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