]> git.ipfire.org Git - thirdparty/newt.git/blob - form.c
define env NEWT_NOFLOWCTRL to disable flow control
[thirdparty/newt.git] / form.c
1 #include "config.h"
2
3 #include <sys/types.h>
4
5 #include <slang.h>
6 #include <stdarg.h>
7 #include <stdlib.h>
8 #ifdef HAVE_SYS_SELECT_H
9 #include <sys/select.h>
10 #endif
11 #include <sys/time.h>
12
13 #ifdef USE_GPM
14 #include <ctype.h>
15 #include <sys/time.h> /* timeval */
16 #include <sys/socket.h> /* socket() */
17 #include <sys/un.h> /* struct sockaddr_un */
18 #include <sys/fcntl.h> /* O_RDONLY */
19 #include <sys/stat.h> /* stat() */
20 #include <termios.h> /* winsize */
21 #include <unistd.h>
22 #include <sys/kd.h> /* KDGETMODE */
23 #include <signal.h>
24 #include <stdio.h>
25 #endif
26
27 #include "newt.h"
28 #include "newt_pr.h"
29
30 #ifdef USE_GPM
31 /*....................................... The connection data structure */
32
33 typedef struct Gpm_Connect {
34 unsigned short eventMask, defaultMask;
35 unsigned short minMod, maxMod;
36 int pid;
37 int vc;
38 } Gpm_Connect;
39
40 /*....................................... Stack struct */
41 typedef struct Gpm_Stst {
42 Gpm_Connect info;
43 struct Gpm_Stst *next;
44 } Gpm_Stst;
45
46 enum Gpm_Etype {
47 GPM_MOVE=1,
48 GPM_DRAG=2, /* exactly one of the bare ones is active at a time */
49 GPM_DOWN=4,
50 GPM_UP= 8,
51
52 #define GPM_BARE_EVENTS(type) ((type)&(0x0f|GPM_ENTER|GPM_LEAVE))
53
54 GPM_SINGLE=16, /* at most one in three is set */
55 GPM_DOUBLE=32,
56 GPM_TRIPLE=64, /* WARNING: I depend on the values */
57
58 GPM_MFLAG=128, /* motion during click? */
59 GPM_HARD=256, /* if set in the defaultMask, force an already
60 used event to pass over to another handler */
61
62 GPM_ENTER=512, /* enter event, user in Roi's */
63 GPM_LEAVE=1024 /* leave event, used in Roi's */
64 };
65
66 /*....................................... The reported event */
67
68 enum Gpm_Margin {GPM_TOP=1, GPM_BOT=2, GPM_LFT=4, GPM_RGT=8};
69
70 typedef struct Gpm_Event {
71 unsigned char buttons, modifiers; /* try to be a multiple of 4 */
72 unsigned short vc;
73 short dx, dy, x, y;
74 enum Gpm_Etype type;
75 int clicks;
76 enum Gpm_Margin margin;
77 } Gpm_Event;
78
79 static int Gpm_Open(Gpm_Connect *conn, int flag);
80 static int Gpm_Close(void);
81
82 static int gpm_fd=-1;
83 static int gpm_flag=0;
84 static int gpm_tried=0;
85 Gpm_Stst *gpm_stack=NULL;
86 static char *gpm_sock_name=NULL;
87 static struct sigaction gpm_saved_suspend_hook;
88 static struct sigaction gpm_saved_winch_hook;
89
90 #define GPM_XTERM_ON
91 #define GPM_XTERM_OFF
92 #define GPM_NODE_DEV "/dev/gpmctl"
93 #define GPM_NODE_CTL GPM_NODE_DEV
94
95 static inline int putdata(int where, Gpm_Connect *what)
96 {
97 if (write(where,what,sizeof(Gpm_Connect))!=sizeof(Gpm_Connect))
98 {
99 return -1;
100 }
101 return 0;
102 }
103
104 static void gpm_winch_hook (int signum)
105 {
106 if (SIG_IGN != gpm_saved_winch_hook.sa_handler &&
107 SIG_DFL != gpm_saved_winch_hook.sa_handler) {
108 gpm_saved_winch_hook.sa_handler(signum);
109 } /*if*/
110 }
111
112 static void gpm_suspend_hook (int signum)
113 {
114 Gpm_Connect gpm_connect;
115 sigset_t old_sigset;
116 sigset_t new_sigset;
117 struct sigaction sa;
118 int success;
119
120 sigemptyset (&new_sigset);
121 sigaddset (&new_sigset, SIGTSTP);
122 sigprocmask (SIG_BLOCK, &new_sigset, &old_sigset);
123
124 /* Open a completely transparent gpm connection */
125 gpm_connect.eventMask = 0;
126 gpm_connect.defaultMask = ~0;
127 gpm_connect.minMod = ~0;
128 gpm_connect.maxMod = 0;
129 /* cannot do this under xterm, tough */
130 success = (Gpm_Open (&gpm_connect, 0) >= 0);
131
132 /* take the default action, whatever it is (probably a stop :) */
133 sigprocmask (SIG_SETMASK, &old_sigset, 0);
134 sigaction (SIGTSTP, &gpm_saved_suspend_hook, 0);
135 kill (getpid (), SIGTSTP);
136
137 /* in bardo here */
138
139 /* Reincarnation. Prepare for another death early. */
140 sigemptyset(&sa.sa_mask);
141 sa.sa_handler = gpm_suspend_hook;
142 sa.sa_flags = SA_NOMASK;
143 sigaction (SIGTSTP, &sa, 0);
144
145 /* Pop the gpm stack by closing the useless connection */
146 /* but do it only when we know we opened one.. */
147 if (success) {
148 Gpm_Close ();
149 } /*if*/
150 }
151
152 static int Gpm_Open(Gpm_Connect *conn, int flag)
153 {
154 char tty[32];
155 char *term;
156 int i;
157 struct sockaddr_un addr;
158 Gpm_Stst *new;
159
160 /*....................................... First of all, check xterm */
161
162 if ((term=(char *)getenv("TERM")) && !strncmp(term,"xterm",5))
163 {
164 if (gpm_tried) return gpm_fd; /* no stack */
165 gpm_fd=-2;
166 GPM_XTERM_ON;
167 gpm_flag=1;
168 return gpm_fd;
169 }
170 /*....................................... No xterm, go on */
171
172
173 /*
174 * So I chose to use the current tty, instead of /dev/console, which
175 * has permission problems. (I am fool, and my console is
176 * readable/writeable by everybody.
177 *
178 * However, making this piece of code work has been a real hassle.
179 */
180
181 if (!gpm_flag && gpm_tried) return -1;
182 gpm_tried=1; /* do or die */
183
184 new=malloc(sizeof(Gpm_Stst));
185 if (!new) return -1;
186
187 new->next=gpm_stack;
188 gpm_stack=new;
189
190 conn->pid=getpid(); /* fill obvious values */
191
192 if (new->next)
193 conn->vc=new->next->info.vc; /* inherit */
194 else
195 {
196 conn->vc=0; /* default handler */
197 if (flag>0)
198 { /* forced vc number */
199 conn->vc=flag;
200 sprintf(tty,"/dev/tty%i",flag);
201 }
202 else if (flag==0) /* use your current vc */
203 {
204 char *t = ttyname(0); /* stdin */
205 if (!t) t = ttyname(1); /* stdout */
206 if (!t) goto err;
207 strcpy(tty,t);
208 if (strncmp(tty,"/dev/tty",8) || !isdigit(tty[8]))
209 goto err;
210 conn->vc=atoi(tty+8);
211 }
212 else /* a default handler -- use console */
213 sprintf(tty,"/dev/tty0");
214
215 }
216
217 new->info=*conn;
218
219 /*....................................... Connect to the control socket */
220
221 if (!(gpm_flag++))
222 {
223
224 if ( (gpm_fd=socket(AF_UNIX,SOCK_STREAM,0))<0 )
225 {
226 goto err;
227 }
228
229 bzero((char *)&addr,sizeof(addr));
230 addr.sun_family=AF_UNIX;
231 if (!(gpm_sock_name = tempnam (0, "gpm"))) {
232 goto err;
233 } /*if*/
234 strncpy (addr.sun_path, gpm_sock_name, sizeof (addr.sun_path));
235 if (bind (gpm_fd, (struct sockaddr*)&addr,
236 sizeof (addr.sun_family) + strlen (addr.sun_path))==-1) {
237 goto err;
238 } /*if*/
239
240 bzero((char *)&addr,sizeof(addr));
241 addr.sun_family=AF_UNIX;
242 strcpy(addr.sun_path, GPM_NODE_CTL);
243 i=sizeof(addr.sun_family)+strlen(GPM_NODE_CTL);
244
245 if ( connect(gpm_fd,(struct sockaddr *)(&addr),i)<0 )
246 {
247 struct stat stbuf;
248
249 /*
250 * Well, try to open a chr device called /dev/gpmctl. This should
251 * be forward-compatible with a kernel server
252 */
253 close(gpm_fd); /* the socket */
254 if ((gpm_fd=open(GPM_NODE_DEV,O_RDWR))==-1) {
255 goto err;
256 } /*if*/
257 if (fstat(gpm_fd,&stbuf)==-1 || (stbuf.st_mode&S_IFMT)!=S_IFCHR)
258 goto err;
259 }
260 }
261 /*....................................... Put your data */
262
263 if (putdata(gpm_fd,conn)!=-1)
264 {
265 /* itz Wed Dec 16 23:22:16 PST 1998 use sigaction, the old
266 code caused a signal loop under XEmacs */
267 struct sigaction sa;
268 sigemptyset(&sa.sa_mask);
269
270 #if (defined(SIGWINCH))
271 /* And the winch hook .. */
272 sa.sa_handler = gpm_winch_hook;
273 sa.sa_flags = 0;
274 sigaction(SIGWINCH, &sa, &gpm_saved_winch_hook);
275 #endif
276
277 #if (defined(SIGTSTP))
278 if (gpm_flag == 1) {
279 /* Install suspend hook */
280 sa.sa_handler = SIG_IGN;
281 sigaction(SIGTSTP, &sa, &gpm_saved_suspend_hook);
282
283 /* if signal was originally ignored, job control is not supported */
284 if (gpm_saved_suspend_hook.sa_handler != SIG_IGN) {
285 sa.sa_flags = SA_NOMASK;
286 sa.sa_handler = gpm_suspend_hook;
287 sigaction(SIGTSTP, &sa, 0);
288 } /*if*/
289 } /*if*/
290 #endif
291
292 } /*if*/
293 return gpm_fd;
294
295 /*....................................... Error: free all memory */
296 err:
297 do
298 {
299 new=gpm_stack->next;
300 free(gpm_stack);
301 gpm_stack=new;
302 }
303 while(gpm_stack);
304 if (gpm_fd>=0) close(gpm_fd);
305 if (gpm_sock_name) {
306 unlink(gpm_sock_name);
307 free(gpm_sock_name);
308 gpm_sock_name = NULL;
309 } /*if*/
310 gpm_flag=0;
311 gpm_fd=-1;
312 return -1;
313 }
314
315 /*-------------------------------------------------------------------*/
316 static int Gpm_Close(void)
317 {
318 Gpm_Stst *next;
319
320 gpm_tried=0; /* reset the error flag for next time */
321 if (gpm_fd==-2) /* xterm */
322 GPM_XTERM_OFF;
323 else /* linux */
324 {
325 if (!gpm_flag) return 0;
326 next=gpm_stack->next;
327 free(gpm_stack);
328 gpm_stack=next;
329 if (next)
330 putdata(gpm_fd,&(next->info));
331
332 if (--gpm_flag) return -1;
333 }
334
335 if (gpm_fd>=0) close(gpm_fd);
336 gpm_fd=-1;
337 if (gpm_sock_name) {
338 unlink(gpm_sock_name);
339 free(gpm_sock_name);
340 gpm_sock_name = NULL;
341 }
342 #ifdef SIGTSTP
343 sigaction(SIGTSTP, &gpm_saved_suspend_hook, 0);
344 #endif
345 #ifdef SIGWINCH
346 sigaction(SIGWINCH, &gpm_saved_winch_hook, 0);
347 #endif
348 return 0;
349 }
350
351 /*-------------------------------------------------------------------*/
352 static int Gpm_GetEvent(Gpm_Event *event)
353 {
354 int count;
355
356 if (!gpm_flag) return 0;
357
358 if ((count=read(gpm_fd,event,sizeof(Gpm_Event)))!=sizeof(Gpm_Event))
359 {
360 if (count==0)
361 {
362 Gpm_Close();
363 return 0;
364 }
365 return -1;
366 }
367 return 1;
368 }
369 #endif
370
371 /****************************************************************************
372 These forms handle vertical scrolling of components with a height of 1
373
374 Horizontal scrolling won't work, and scrolling large widgets will fail
375 miserably. It shouldn't be too hard to fix either of those if anyone
376 cares to. I only use scrolling for listboxes and text boxes though so
377 I didn't bother.
378 *****************************************************************************/
379
380 struct element {
381 newtComponent co;
382 };
383
384 struct fdInfo {
385 int fd;
386 int flags;
387 };
388
389 struct form {
390 int numCompsAlloced;
391 struct element * elements;
392 int numComps;
393 int currComp;
394 int fixedHeight;
395 int flags;
396 int vertOffset;
397 newtComponent vertBar, exitComp;
398 const char * help;
399 int numRows;
400 int * hotKeys;
401 int numHotKeys;
402 int background;
403 int numFds;
404 struct fdInfo * fds;
405 int maxFd;
406 int timer; /* in milliseconds */
407 struct timeval lastTimeout;
408 void * helpTag;
409 newtCallback helpCb;
410 };
411
412 static void gotoComponent(newtComponent co, int newComp);
413 static struct eventResult formEvent(newtComponent co, struct event ev);
414 static struct eventResult sendEvent(newtComponent comp, struct event ev);
415 static void formPlace(newtComponent co, int left, int top);
416
417 /* Global, ick */
418 static newtCallback helpCallback;
419
420 /* this isn't static as grid.c tests against it to find forms */
421 struct componentOps formOps = {
422 newtDrawForm,
423 formEvent,
424 newtFormDestroy,
425 formPlace,
426 newtDefaultMappedHandler,
427 } ;
428
429 int needResize = 0;
430
431 static inline int componentFits(newtComponent co, int compNum) {
432 struct form * form = co->data;
433 struct element * el = form->elements + compNum;
434
435 if (co->top > el->co->top)
436 return 0;
437 if (co->top + co->height < el->co->top + el->co->height)
438 return 0;
439
440 return 1;
441 }
442
443 newtComponent newtForm(newtComponent vertBar, void * help, int flags) {
444 newtComponent co;
445 struct form * form;
446
447 co = malloc(sizeof(*co));
448 form = malloc(sizeof(*form));
449 co->data = form;
450 co->width = 0;
451 co->height = 0;
452 co->top = -1;
453 co->left = -1;
454 co->isMapped = 0;
455
456 co->takesFocus = 0; /* we may have 0 components */
457 co->ops = &formOps;
458 co->callback = NULL;
459 co->destroyCallback = NULL;
460
461 form->help = help;
462 form->flags = flags;
463 form->numCompsAlloced = 5;
464 form->numComps = 0;
465 form->currComp = -1;
466 form->vertOffset = 0;
467 form->fixedHeight = 0;
468 form->numRows = 0;
469 form->numFds = 0;
470 form->maxFd = 0;
471 form->fds = NULL;
472 form->elements = malloc(sizeof(*(form->elements)) * form->numCompsAlloced);
473
474 form->background = COLORSET_WINDOW;
475 form->hotKeys = malloc(sizeof(int));
476 form->numHotKeys = 0;
477 form->timer = 0;
478 form->lastTimeout.tv_sec = form->lastTimeout.tv_usec = 0;
479 if (!(form->flags & NEWT_FLAG_NOF12)) {
480 newtFormAddHotKey(co, NEWT_KEY_F12);
481 }
482
483 if (vertBar)
484 form->vertBar = vertBar;
485 else
486 form->vertBar = NULL;
487
488 form->helpTag = help;
489 form->helpCb = helpCallback;
490
491 return co;
492 }
493
494 newtComponent newtFormGetCurrent(newtComponent co) {
495 struct form * form = co->data;
496
497 if (form->currComp == -1) return 0;
498 return form->elements[form->currComp].co;
499 }
500
501 static void formScroll(newtComponent co, int delta) {
502 struct form * form = co->data;
503 struct element * el;
504 int i, newVertOffset = form->vertOffset + delta;
505
506 if (newVertOffset < 0)
507 newVertOffset = 0;
508 if (newVertOffset > form->numRows - co->height)
509 newVertOffset = form->numRows - co->height;
510
511 delta = newVertOffset - form->vertOffset;
512 form->vertOffset = newVertOffset;
513
514 for (i = 0, el = form->elements; i < form->numComps; i++, el++) {
515 if (el->co == form->vertBar)
516 continue;
517 el->co->ops->place(el->co, el->co->left, el->co->top - delta);
518 }
519 }
520
521 int newtFormGetScrollPosition(newtComponent co) {
522 struct form * form = co->data;
523
524 return form->vertOffset;
525 }
526
527 void newtFormSetScrollPosition(newtComponent co, int position) {
528 struct form * form = co->data;
529
530 if (form->numRows == 0)
531 newtFormSetSize(co);
532 formScroll(co, position - form->vertOffset);
533 }
534
535 void newtFormSetCurrent(newtComponent co, newtComponent subco) {
536 struct form * form = co->data;
537 int i, new;
538
539 for (i = 0; i < form->numComps; i++) {
540 if (form->elements[i].co == subco) break;
541 }
542
543 if (form->elements[i].co != subco) return;
544 new = i;
545
546 if (co->isMapped && !componentFits(co, new)) {
547 gotoComponent(co, -1);
548 formScroll(co, form->elements[new].co->top - co->top - 1);
549 }
550
551 gotoComponent(co, new);
552 }
553
554 void newtFormSetTimer(newtComponent co, int millisecs) {
555 struct form * form = co->data;
556
557 form->timer = millisecs;
558 form->lastTimeout.tv_usec = 0;
559 form->lastTimeout.tv_sec = 0;
560 }
561
562 void newtFormSetHeight(newtComponent co, int height) {
563 struct form * form = co->data;
564
565 form->fixedHeight = 1;
566 co->height = height;
567 }
568
569 void newtFormSetWidth(newtComponent co, int width) {
570 co->width = width;
571 }
572
573 void newtFormAddComponent(newtComponent co, newtComponent newco) {
574 struct form * form = co->data;
575
576 co->takesFocus = 1;
577
578 if (form->numCompsAlloced == form->numComps) {
579 form->numCompsAlloced += 5;
580 form->elements = realloc(form->elements,
581 sizeof(*(form->elements)) * form->numCompsAlloced);
582 }
583
584 form->elements[form->numComps].co = newco;
585
586 if (newco->takesFocus && form->currComp == -1)
587 form->currComp = form->numComps;
588
589 form->numComps++;
590 }
591
592 void newtFormAddComponents(newtComponent co, ...) {
593 va_list ap;
594 newtComponent subco;
595
596 va_start(ap, co);
597
598 while ((subco = va_arg(ap, newtComponent)))
599 newtFormAddComponent(co, subco);
600
601 va_end(ap);
602 }
603
604 static void formPlace(newtComponent co, int left, int top) {
605 struct form * form = co->data;
606 int vertDelta, horizDelta;
607 struct element * el;
608 int i;
609
610 vertDelta = top - co->top;
611 horizDelta = left - co->left;
612 co->top = top;
613 co->left = left;
614
615 for (i = 0, el = form->elements; i < form->numComps; i++, el++) {
616 el->co->ops->place(el->co, el->co->left + horizDelta,
617 el->co->top + vertDelta);
618 }
619 }
620
621 void newtDrawForm(newtComponent co) {
622 struct form * form = co->data;
623 struct element * el;
624 int i;
625
626 newtFormSetSize(co);
627
628 SLsmg_set_color(form->background);
629 newtClearBox(co->left, co->top, co->width, co->height);
630
631 for (i = 0, el = form->elements; i < form->numComps; i++, el++) {
632 /* only draw it if it'll fit on the screen vertically
633 (the scrollbar *always* fits somewhere) */
634 if (el->co == form->vertBar || componentFits(co, i)) {
635 el->co->ops->mapped(el->co, 1);
636 el->co->ops->draw(el->co);
637 } else {
638 el->co->ops->mapped(el->co, 0);
639 }
640 }
641
642 if (form->vertBar)
643 newtScrollbarSet(form->vertBar, form->vertOffset,
644 form->numRows - co->height);
645 }
646
647 static struct eventResult formEvent(newtComponent co, struct event ev) {
648 struct form * form = co->data;
649 newtComponent subco = form->elements[form->currComp].co;
650 int new, wrap = 0;
651 struct eventResult er;
652 int dir = 0, page = 0;
653 int i, num, found;
654 struct element * el;
655
656 er.result = ER_IGNORED;
657 if (!form->numComps) return er;
658
659 if (form->currComp == -1) return er;
660
661 switch (ev.when) {
662 case EV_EARLY:
663 if (ev.event == EV_KEYPRESS) {
664 if (ev.u.key == NEWT_KEY_TAB) {
665 er.result = ER_SWALLOWED;
666 dir = 1;
667 wrap = 1;
668 } else if (ev.u.key == NEWT_KEY_UNTAB) {
669 er.result = ER_SWALLOWED;
670 dir = -1;
671 wrap = 1;
672 }
673 }
674
675 if (form->numComps) {
676 i = form->currComp;
677 num = 0;
678 while (er.result == ER_IGNORED && num != form->numComps ) {
679 er = form->elements[i].co->ops->event(form->elements[i].co, ev);
680
681 num++;
682 i++;
683 if (i == form->numComps) i = 0;
684 }
685 }
686
687 break;
688
689 case EV_NORMAL:
690 if (ev.event == EV_MOUSE) {
691 found = 0;
692 for (i = 0, el = form->elements; i < form->numComps; i++, el++) {
693 if ((el->co->top <= ev.u.mouse.y) &&
694 (el->co->top + el->co->height > ev.u.mouse.y) &&
695 (el->co->left <= ev.u.mouse.x) &&
696 (el->co->left + el->co->width > ev.u.mouse.x)) {
697 found = 1;
698 if (el->co->takesFocus) {
699 gotoComponent(co, i);
700 subco = form->elements[form->currComp].co;
701 }
702 }
703 /* If we did not find a co to send this event to, we
704 should just swallow the event here. */
705 }
706 if (!found) {
707 er.result = ER_SWALLOWED;
708
709 return er;
710 }
711 }
712 er = subco->ops->event(subco, ev);
713 switch (er.result) {
714 case ER_NEXTCOMP:
715 er.result = ER_SWALLOWED;
716 dir = 1;
717 break;
718
719 case ER_EXITFORM:
720 form->exitComp = subco;
721 break;
722
723 default:
724 break;
725 }
726 break;
727
728 case EV_LATE:
729 er = subco->ops->event(subco, ev);
730
731 if (er.result == ER_IGNORED) {
732 switch (ev.u.key) {
733 case NEWT_KEY_UP:
734 case NEWT_KEY_LEFT:
735 case NEWT_KEY_BKSPC:
736 er.result = ER_SWALLOWED;
737 dir = -1;
738 break;
739
740 case NEWT_KEY_DOWN:
741 case NEWT_KEY_RIGHT:
742 er.result = ER_SWALLOWED;
743 dir = 1;
744 break;
745
746 case NEWT_KEY_PGUP:
747 er.result = ER_SWALLOWED;
748 dir = -1;
749 page = 1;
750 break;
751
752 case NEWT_KEY_PGDN:
753 er.result = ER_SWALLOWED;
754 dir = 1;
755 page = 1;
756 break;
757 }
758 }
759 }
760
761 if (dir) {
762 new = form->currComp;
763
764 if (page) {
765 new += dir * co->height;
766 if (new < 0)
767 new = 0;
768 else if (new >= form->numComps)
769 new = (form->numComps - 1);
770
771 while (!form->elements[new].co->takesFocus &&
772 new - dir >= 0 && new - dir < form->numComps)
773 new -= dir;
774 } else {
775 do {
776 new += dir;
777
778 if (wrap) {
779 if (new < 0)
780 new = form->numComps - 1;
781 else if (new >= form->numComps)
782 new = 0;
783 if (new == form->currComp)
784 /* back where we started */
785 return er;
786 } else if (new < 0 || new >= form->numComps)
787 return er;
788 } while (!form->elements[new].co->takesFocus);
789 }
790
791 /* make sure this component is visible */
792 if (!componentFits(co, new)) {
793 int vertDelta;
794
795 gotoComponent(co, -1);
796
797 if (dir < 0) {
798 /* make the new component the first one */
799 vertDelta = form->elements[new].co->top - co->top;
800 } else {
801 /* make the new component the last one */
802 vertDelta = (form->elements[new].co->top +
803 form->elements[new].co->height) -
804 (co->top + co->height);
805 }
806
807 formScroll(co, vertDelta);
808 newtDrawForm(co);
809 }
810
811 gotoComponent(co, new);
812 er.result = ER_SWALLOWED;
813 }
814
815 return er;
816 }
817
818 /* Destroy a component. Components which have been added to a form
819 * are destroyed when the form is destroyed; this is just for the
820 * (rare) case of components which for whatever reason weren't added
821 * to a form.
822 */
823 void newtComponentDestroy(newtComponent co) {
824 /* If the user registered a destroy callback for this component,
825 * now is a good time to call it.
826 */
827 if (co->destroyCallback)
828 co->destroyCallback(co, co->destroyCallbackData);
829
830 if (co->ops->destroy) {
831 co->ops->destroy(co);
832 } else {
833 if (co->data) free(co->data);
834 free(co);
835 }
836 }
837
838 /* this also destroys all of the components on the form */
839 void newtFormDestroy(newtComponent co) {
840 newtComponent subco;
841 struct form * form = co->data;
842 int i;
843
844 /* first, destroy all of the components */
845 for (i = 0; i < form->numComps; i++) {
846 subco = form->elements[i].co;
847 newtComponentDestroy(subco);
848 }
849
850 if (form->hotKeys) free(form->hotKeys);
851
852 free(form->elements);
853 free(form);
854 free(co);
855 }
856
857 newtComponent newtRunForm(newtComponent co) {
858 struct newtExitStruct es;
859
860 newtFormRun(co, &es);
861 if (es.reason == NEWT_EXIT_HOTKEY) {
862 if (es.u.key == NEWT_KEY_F12) {
863 es.reason = NEWT_EXIT_COMPONENT;
864 es.u.co = co;
865 } else {
866 return NULL;
867 }
868 } else if (es.reason == NEWT_EXIT_ERROR)
869 return NULL;
870
871 return es.u.co;
872 }
873
874 void newtFormAddHotKey(newtComponent co, int key) {
875 struct form * form = co->data;
876
877 form->numHotKeys++;
878 form->hotKeys = realloc(form->hotKeys, sizeof(int) * form->numHotKeys);
879 form->hotKeys[form->numHotKeys - 1] = key;
880 }
881
882 void newtFormSetSize(newtComponent co) {
883 struct form * form = co->data;
884 int delta, i, first;
885 struct element * el;
886
887 form->numRows = 0;
888
889 co->width = 0;
890 if (!form->fixedHeight) co->height = 0;
891
892 co->top = -1;
893 co->left = -1;
894 first = 1;
895
896 for (i = 0, el = form->elements; i < form->numComps; i++, el++) {
897 if (el->co->ops == &formOps)
898 newtFormSetSize(el->co);
899 else if (el->co == form->vertBar)
900 continue;
901
902 if (first) {
903 co->top = el->co->top;
904 co->left = el->co->left;
905 first = 0;
906 }
907
908 if (co->left > el->co->left) {
909 delta = co->left - el->co->left;
910 co->left -= delta;
911 co->width += delta;
912 }
913
914 if (co->top > el->co->top) {
915 delta = co->top - el->co->top;
916 co->top -= delta;
917 form->numRows += delta;
918 if (!form->fixedHeight)
919 co->height += delta;
920 }
921
922 if ((co->left + co->width) < (el->co->left + el->co->width))
923 co->width = (el->co->left + el->co->width) - co->left;
924
925 if (!form->fixedHeight) {
926 if ((co->top + co->height) < (el->co->top + el->co->height))
927 co->height = (el->co->top + el->co->height) - co->top;
928 }
929
930 if ((el->co->top + el->co->height - co->top) > form->numRows) {
931 form->numRows = el->co->top + el->co->height - co->top;
932 }
933 }
934
935 co->top += form->vertOffset;
936 }
937
938 void newtFormRun(newtComponent co, struct newtExitStruct * es) {
939 struct form * form = co->data;
940 struct event ev;
941 struct eventResult er;
942 int key, i, max;
943 int done = 0;
944 fd_set readSet, writeSet, exceptSet;
945 struct timeval nextTimeout, now, timeout;
946 #ifdef USE_GPM
947 int x, y;
948 Gpm_Connect conn;
949 Gpm_Event event;
950
951 /* Set up GPM interface */
952 conn.eventMask = ~GPM_MOVE;
953 conn.defaultMask = GPM_MOVE;
954 conn.minMod = 0;
955 conn.maxMod = 0;
956
957 Gpm_Open(&conn, 0);
958 #endif
959
960 /* draw all of the components */
961 newtDrawForm(co);
962
963 if (form->currComp == -1) {
964 if (form->numComps)
965 gotoComponent(co, 0);
966 } else
967 gotoComponent(co, form->currComp);
968
969 while (!done) {
970 newtRefresh();
971
972 FD_ZERO(&readSet);
973 FD_ZERO(&writeSet);
974 FD_ZERO(&exceptSet);
975 FD_SET(0, &readSet);
976 #ifdef USE_GPM
977 if (gpm_fd > 0) {
978 FD_SET(gpm_fd, &readSet);
979 }
980 max = form->maxFd > gpm_fd ? form->maxFd : gpm_fd;
981 #else
982 max = form->maxFd;
983 #endif
984
985 for (i = 0; i < form->numFds; i++) {
986 if (form->fds[i].flags & NEWT_FD_READ)
987 FD_SET(form->fds[i].fd, &readSet);
988 if (form->fds[i].flags & NEWT_FD_WRITE)
989 FD_SET(form->fds[i].fd, &writeSet);
990 if (form->fds[i].flags & NEWT_FD_EXCEPT)
991 FD_SET(form->fds[i].fd, &exceptSet);
992 }
993
994 if (form->timer) {
995 /* Calculate when we next need to return with a timeout. Do
996 this inside the loop in case a callback resets the timer. */
997 gettimeofday(&now, 0);
998
999 if ((!form->lastTimeout.tv_sec && !form->lastTimeout.tv_usec) ||
1000 now.tv_sec < form->lastTimeout.tv_sec ||
1001 (now.tv_sec == form->lastTimeout.tv_sec &&
1002 now.tv_usec < form->lastTimeout.tv_usec))
1003 form->lastTimeout = now;
1004
1005 nextTimeout.tv_sec = form->lastTimeout.tv_sec +
1006 (form->timer / 1000);
1007 nextTimeout.tv_usec = form->lastTimeout.tv_usec +
1008 (form->timer % 1000) * 1000;
1009
1010 if (now.tv_sec > nextTimeout.tv_sec) {
1011 timeout.tv_sec = timeout.tv_usec = 0;
1012 } else if (now.tv_sec == nextTimeout.tv_sec) {
1013 timeout.tv_sec = 0;
1014 if (now.tv_usec > nextTimeout.tv_usec)
1015 timeout.tv_usec = 0;
1016 else
1017 timeout.tv_usec = nextTimeout.tv_usec - now.tv_usec;
1018 } else if (now.tv_sec < nextTimeout.tv_sec) {
1019 timeout.tv_sec = nextTimeout.tv_sec - now.tv_sec;
1020 if (now.tv_usec > nextTimeout.tv_usec)
1021 timeout.tv_sec--,
1022 timeout.tv_usec = nextTimeout.tv_usec + 1000000 -
1023 now.tv_usec;
1024 else
1025 timeout.tv_usec = nextTimeout.tv_usec - now.tv_usec;
1026 }
1027 } else {
1028 timeout.tv_sec = timeout.tv_usec = 0;
1029 }
1030
1031 if (needResize) {
1032 needResize = 0;
1033 newtResizeScreen(1);
1034
1035 /* The application may want to handle the resize */
1036 for (i = 0; i < form->numHotKeys; i++) {
1037 if (form->hotKeys[i] == NEWT_KEY_RESIZE) {
1038 es->reason = NEWT_EXIT_HOTKEY;
1039 es->u.key = NEWT_KEY_RESIZE;
1040 done = 1;
1041 break;
1042 }
1043 }
1044 if (done)
1045 break;
1046 }
1047
1048 i = select(max + 1, &readSet, &writeSet, &exceptSet,
1049 form->timer ? &timeout : NULL);
1050 if (i < 0) continue; /* ?? What should we do here? */
1051
1052 if (i == 0) {
1053 done = 1;
1054 es->reason = NEWT_EXIT_TIMER;
1055 gettimeofday(&form->lastTimeout, NULL);
1056 } else
1057 #ifdef USE_GPM
1058 if (gpm_fd > 0 && FD_ISSET(gpm_fd, &readSet)) {
1059 Gpm_GetEvent(&event);
1060
1061 if (event.type & GPM_DOWN) {
1062 /* Transform coordinates to current window */
1063 newtGetWindowPos(&x, &y);
1064
1065 ev.event = EV_MOUSE;
1066 ev.u.mouse.type = MOUSE_BUTTON_DOWN;
1067 ev.u.mouse.x = event.x - x - 1;
1068 ev.u.mouse.y = event.y - y - 1;
1069
1070 /* Send the form the event */
1071 er = sendEvent(co, ev);
1072
1073 if (er.result == ER_EXITFORM) {
1074 done = 1;
1075 es->reason = NEWT_EXIT_COMPONENT;
1076 es->u.co = form->exitComp;
1077 }
1078
1079 }
1080 } else
1081 #endif
1082 {
1083 if (FD_ISSET(0, &readSet)) {
1084
1085 key = newtGetKey();
1086
1087 for (i = 0; i < form->numHotKeys; i++) {
1088 if (form->hotKeys[i] == key) {
1089 es->reason = NEWT_EXIT_HOTKEY;
1090 es->u.key = key;
1091 done = 1;
1092 break;
1093 }
1094 }
1095
1096 if (key == NEWT_KEY_F1 && form->helpTag && form->helpCb) {
1097 if (form->currComp != -1) {
1098 ev.event = EV_UNFOCUS;
1099 sendEvent(form->elements[form->currComp].co, ev);
1100 }
1101 form->helpCb(co, form->helpTag);
1102 if (form->currComp != -1) {
1103 ev.event = EV_FOCUS;
1104 sendEvent(form->elements[form->currComp].co, ev);
1105 }
1106 }
1107
1108 if (key == NEWT_KEY_ERROR) {
1109 es->u.watch = -1;
1110 es->reason = NEWT_EXIT_ERROR;
1111 done = 1;
1112 }
1113
1114 if (!done) {
1115 ev.event = EV_KEYPRESS;
1116 ev.u.key = key;
1117
1118 er = sendEvent(co, ev);
1119
1120 if (er.result == ER_EXITFORM) {
1121 done = 1;
1122 es->reason = NEWT_EXIT_COMPONENT;
1123 es->u.co = form->exitComp;
1124 }
1125 }
1126 } else {
1127 for (i = 0; i < form->numFds; i++) {
1128 if (((form->fds[i].flags & NEWT_FD_READ)
1129 && FD_ISSET(form->fds[i].fd, &readSet))
1130 || ((form->fds[i].flags & NEWT_FD_WRITE)
1131 && FD_ISSET(form->fds[i].fd, &writeSet))
1132 || ((form->fds[i].flags & NEWT_FD_EXCEPT)
1133 && FD_ISSET(form->fds[i].fd, &exceptSet))) break;
1134 }
1135 if(i < form->numFds)
1136 es->u.watch = form->fds[i].fd;
1137 else
1138 es->u.watch = -1;
1139
1140 es->reason = NEWT_EXIT_FDREADY;
1141 done = 1;
1142 }
1143 }
1144 }
1145 newtRefresh();
1146 #ifdef USE_GPM
1147 Gpm_Close();
1148 #endif
1149 }
1150
1151 static struct eventResult sendEvent(newtComponent co, struct event ev) {
1152 struct eventResult er;
1153
1154 ev.when = EV_EARLY;
1155 er = co->ops->event(co, ev);
1156
1157 if (er.result == ER_IGNORED) {
1158 ev.when = EV_NORMAL;
1159 er = co->ops->event(co, ev);
1160 }
1161
1162 if (er.result == ER_IGNORED) {
1163 ev.when = EV_LATE;
1164 er = co->ops->event(co, ev);
1165 }
1166
1167 return er;
1168 }
1169
1170 static void gotoComponent(newtComponent co, int newComp) {
1171 struct form * form = co->data;
1172 struct event ev;
1173
1174 if (form->currComp != -1) {
1175 ev.event = EV_UNFOCUS;
1176 sendEvent(form->elements[form->currComp].co, ev);
1177 }
1178
1179 form->currComp = newComp;
1180
1181 if (form->currComp != -1) {
1182 ev.event = EV_FOCUS;
1183 ev.when = EV_NORMAL;
1184 sendEvent(form->elements[form->currComp].co, ev);
1185 }
1186
1187 if (co->callback)
1188 co->callback(co, co->callbackData);
1189 }
1190
1191 void newtComponentAddCallback(newtComponent co, newtCallback f, void * data) {
1192 co->callback = f;
1193 co->callbackData = data;
1194 }
1195
1196 /* Add a callback which is called when the component is destroyed. */
1197 void newtComponentAddDestroyCallback(newtComponent co,
1198 newtCallback f, void * data) {
1199 co->destroyCallback = f;
1200 co->destroyCallbackData = data;
1201 }
1202
1203 void newtComponentTakesFocus(newtComponent co, int val) {
1204 co->takesFocus = val;
1205 }
1206
1207 void newtFormSetBackground(newtComponent co, int color) {
1208 struct form * form = co->data;
1209
1210 form->background = color;
1211 }
1212
1213 void newtFormWatchFd(newtComponent co, int fd, int fdFlags) {
1214 struct form * form = co->data;
1215 int i;
1216
1217 for (i = 0; i < form->numFds; i++)
1218 if (form->fds[i].fd == fd)
1219 break;
1220
1221 if(i >= form->numFds)
1222 form->fds = realloc(form->fds, (++form->numFds) * sizeof(*form->fds));
1223
1224 form->fds[i].fd = fd;
1225 form->fds[i].flags = fdFlags;
1226 if (form->maxFd < fd) form->maxFd = fd;
1227 }
1228
1229 void newtSetHelpCallback(newtCallback cb) {
1230 helpCallback = cb;
1231 }