]> git.ipfire.org Git - thirdparty/newt.git/blame - form.c
remove SIGWINCH handler from whiptail
[thirdparty/newt.git] / form.c
CommitLineData
45f6c4fd 1#include "config.h"
2
4bb1be99 3#include <sys/types.h>
4
139f06bc 5#include <slang.h>
6fb96a3f 6#include <stdarg.h>
7#include <stdlib.h>
4bb1be99 8#ifdef HAVE_SYS_SELECT_H
d02ffb49 9#include <sys/select.h>
4bb1be99 10#endif
e67a6cab 11#include <sys/time.h>
6fb96a3f 12
33b9b6eb 13#ifdef USE_GPM
14#include <ctype.h>
15#include <sys/time.h> /* timeval */
33b9b6eb 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>
45f6c4fd 25#endif
26
6fb96a3f 27#include "newt.h"
28#include "newt_pr.h"
29
33b9b6eb 30#ifdef USE_GPM
31/*....................................... The connection data structure */
32
33typedef 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 */
41typedef struct Gpm_Stst {
42 Gpm_Connect info;
43 struct Gpm_Stst *next;
44} Gpm_Stst;
45
46enum 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
68enum Gpm_Margin {GPM_TOP=1, GPM_BOT=2, GPM_LFT=4, GPM_RGT=8};
69
70typedef 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
79static int Gpm_Open(Gpm_Connect *conn, int flag);
80static int Gpm_Close(void);
81
82static int gpm_fd=-1;
83static int gpm_flag=0;
84static int gpm_tried=0;
85Gpm_Stst *gpm_stack=NULL;
86static struct sigaction gpm_saved_suspend_hook;
87static struct sigaction gpm_saved_winch_hook;
88
89#define GPM_XTERM_ON
90#define GPM_XTERM_OFF
91#define GPM_NODE_DEV "/dev/gpmctl"
92#define GPM_NODE_CTL GPM_NODE_DEV
93
94static inline int putdata(int where, Gpm_Connect *what)
95{
96 if (write(where,what,sizeof(Gpm_Connect))!=sizeof(Gpm_Connect))
97 {
98 return -1;
99 }
100 return 0;
101}
102
103static void gpm_winch_hook (int signum)
104{
105 if (SIG_IGN != gpm_saved_winch_hook.sa_handler &&
106 SIG_DFL != gpm_saved_winch_hook.sa_handler) {
107 gpm_saved_winch_hook.sa_handler(signum);
108 } /*if*/
109}
110
111static void gpm_suspend_hook (int signum)
112{
113 Gpm_Connect gpm_connect;
114 sigset_t old_sigset;
115 sigset_t new_sigset;
116 struct sigaction sa;
117 int success;
118
119 sigemptyset (&new_sigset);
120 sigaddset (&new_sigset, SIGTSTP);
121 sigprocmask (SIG_BLOCK, &new_sigset, &old_sigset);
122
123 /* Open a completely transparent gpm connection */
124 gpm_connect.eventMask = 0;
125 gpm_connect.defaultMask = ~0;
126 gpm_connect.minMod = ~0;
127 gpm_connect.maxMod = 0;
128 /* cannot do this under xterm, tough */
129 success = (Gpm_Open (&gpm_connect, 0) >= 0);
130
131 /* take the default action, whatever it is (probably a stop :) */
132 sigprocmask (SIG_SETMASK, &old_sigset, 0);
133 sigaction (SIGTSTP, &gpm_saved_suspend_hook, 0);
134 kill (getpid (), SIGTSTP);
135
136 /* in bardo here */
137
138 /* Reincarnation. Prepare for another death early. */
139 sigemptyset(&sa.sa_mask);
140 sa.sa_handler = gpm_suspend_hook;
141 sa.sa_flags = SA_NOMASK;
142 sigaction (SIGTSTP, &sa, 0);
143
144 /* Pop the gpm stack by closing the useless connection */
145 /* but do it only when we know we opened one.. */
146 if (success) {
147 Gpm_Close ();
148 } /*if*/
149}
150
151static int Gpm_Open(Gpm_Connect *conn, int flag)
152{
153 char tty[32];
154 char *term;
155 int i;
156 struct sockaddr_un addr;
157 Gpm_Stst *new;
158 char* sock_name = 0;
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 (!(sock_name = tempnam (0, "gpm"))) {
232 goto err;
233 } /*if*/
234 strncpy (addr.sun_path, 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 (sock_name) {
306 unlink(sock_name);
307 free(sock_name);
308 sock_name = 0;
309 } /*if*/
310 gpm_flag=0;
311 return -1;
312}
313
314/*-------------------------------------------------------------------*/
315static int Gpm_Close(void)
316{
317 Gpm_Stst *next;
318
319 gpm_tried=0; /* reset the error flag for next time */
320 if (gpm_fd==-2) /* xterm */
321 GPM_XTERM_OFF;
322 else /* linux */
323 {
324 if (!gpm_flag) return 0;
325 next=gpm_stack->next;
326 free(gpm_stack);
327 gpm_stack=next;
328 if (next)
329 putdata(gpm_fd,&(next->info));
330
331 if (--gpm_flag) return -1;
332 }
333
334 if (gpm_fd>=0) close(gpm_fd);
335 gpm_fd=-1;
336#ifdef SIGTSTP
337 sigaction(SIGTSTP, &gpm_saved_suspend_hook, 0);
338#endif
339#ifdef SIGWINCH
340 sigaction(SIGWINCH, &gpm_saved_winch_hook, 0);
341#endif
342 return 0;
343}
344
345/*-------------------------------------------------------------------*/
346static int Gpm_GetEvent(Gpm_Event *event)
347{
348 int count;
349
350 if (!gpm_flag) return 0;
351
352 if ((count=read(gpm_fd,event,sizeof(Gpm_Event)))!=sizeof(Gpm_Event))
353 {
354 if (count==0)
355 {
356 Gpm_Close();
357 return 0;
358 }
359 return -1;
360 }
361 return 1;
362}
363#endif
364
f5daa14e 365/****************************************************************************
45f6c4fd 366 These forms handle vertical scrolling of components with a height of 1
367
f5daa14e 368 Horizontal scrolling won't work, and scrolling large widgets will fail
369 miserably. It shouldn't be too hard to fix either of those if anyone
370 cares to. I only use scrolling for listboxes and text boxes though so
371 I didn't bother.
372*****************************************************************************/
373
374struct element {
6f481af2 375 int top, left; /* Actual, not virtual. These are translated */
376 newtComponent co; /* into actual through vertOffset */
f5daa14e 377};
378
d02ffb49 379struct fdInfo {
380 int fd;
381 int flags;
382};
383
c4827b34 384struct form {
6fb96a3f 385 int numCompsAlloced;
f5daa14e 386 struct element * elements;
6fb96a3f 387 int numComps;
388 int currComp;
8c4e1047 389 int fixedHeight;
02b180e8 390 int flags;
f5daa14e 391 int vertOffset;
02b180e8 392 newtComponent vertBar, exitComp;
d4109c37 393 const char * help;
f1f3611f 394 int numRows;
da151ca8 395 int * hotKeys;
396 int numHotKeys;
8f4e87fc 397 int background;
3da9c9a2 398 int beenSet;
d02ffb49 399 int numFds;
400 struct fdInfo * fds;
401 int maxFd;
e67a6cab 402 int timer; /* in milliseconds */
403 struct timeval lastTimeout;
72b71fa6 404 void * helpTag;
405 newtCallback helpCb;
6fb96a3f 406};
407
c4827b34 408static void gotoComponent(struct form * form, int newComp);
409static struct eventResult formEvent(newtComponent co, struct event ev);
434c766b 410static struct eventResult sendEvent(newtComponent comp, struct event ev);
8f52cd47 411static void formPlace(newtComponent co, int left, int top);
c4827b34 412
72b71fa6 413/* Global, ick */
414static newtCallback helpCallback;
415
8f52cd47 416/* this isn't static as grid.c tests against it to find forms */
417struct componentOps formOps = {
8b68158d 418 newtDrawForm,
c4827b34 419 formEvent,
420 newtFormDestroy,
8f52cd47 421 formPlace,
422 newtDefaultMappedHandler,
c4827b34 423} ;
424
f5daa14e 425static inline int componentFits(newtComponent co, int compNum) {
426 struct form * form = co->data;
427 struct element * el = form->elements + compNum;
428
429 if ((co->top + form->vertOffset) > el->top) return 0;
430 if ((co->top + form->vertOffset + co->height) <
6f481af2 431 (el->top + el->co->height)) return 0;
f5daa14e 432
433 return 1;
434}
435
72b71fa6 436newtComponent newtForm(newtComponent vertBar, void * help, int flags) {
c4827b34 437 newtComponent co;
438 struct form * form;
439
440 co = malloc(sizeof(*co));
441 form = malloc(sizeof(*form));
442 co->data = form;
443 co->width = 0;
444 co->height = 0;
445 co->top = -1;
446 co->left = -1;
3da9c9a2 447 co->isMapped = 0;
c4827b34 448
6f481af2 449 co->takesFocus = 0; /* we may have 0 components */
c4827b34 450 co->ops = &formOps;
c101e99e 451 co->destroyCallback = NULL;
6fb96a3f 452
02b180e8 453 form->help = help;
454 form->flags = flags;
6fb96a3f 455 form->numCompsAlloced = 5;
456 form->numComps = 0;
457 form->currComp = -1;
f5daa14e 458 form->vertOffset = 0;
8c4e1047 459 form->fixedHeight = 0;
f1f3611f 460 form->numRows = 0;
d02ffb49 461 form->numFds = 0;
462 form->maxFd = 0;
463 form->fds = NULL;
3da9c9a2 464 form->beenSet = 0;
f5daa14e 465 form->elements = malloc(sizeof(*(form->elements)) * form->numCompsAlloced);
6fb96a3f 466
8f4e87fc 467 form->background = COLORSET_WINDOW;
da151ca8 468 form->hotKeys = malloc(sizeof(int));
469 form->numHotKeys = 0;
73390860 470 form->timer = 0;
471 form->lastTimeout.tv_sec = form->lastTimeout.tv_usec = 0;
4a93351d 472 if (!(form->flags & NEWT_FLAG_NOF12)) {
6f481af2 473 newtFormAddHotKey(co, NEWT_KEY_F12);
da151ca8 474 }
475
02b180e8 476 if (vertBar)
6f481af2 477 form->vertBar = vertBar;
f1f3611f 478 else
6f481af2 479 form->vertBar = NULL;
f1f3611f 480
72b71fa6 481 form->helpTag = help;
482 form->helpCb = helpCallback;
483
c4827b34 484 return co;
6fb96a3f 485}
486
2a2998a0 487newtComponent newtFormGetCurrent(newtComponent co) {
488 struct form * form = co->data;
489
d78245ab 490 if (form->currComp == -1) return 0;
2a2998a0 491 return form->elements[form->currComp].co;
492}
493
7ea3dccd 494void newtFormSetCurrent(newtComponent co, newtComponent subco) {
495 struct form * form = co->data;
496 int i, new;
497
498 for (i = 0; i < form->numComps; i++) {
6f481af2 499 if (form->elements[i].co == subco) break;
7ea3dccd 500 }
501
502 if (form->elements[i].co != subco) return;
503 new = i;
504
3da9c9a2 505 if (co->isMapped && !componentFits(co, new)) {
6f481af2 506 gotoComponent(form, -1);
507 form->vertOffset = form->elements[new].top - co->top - 1;
508 if (form->vertOffset > (form->numRows - co->height))
509 form->vertOffset = form->numRows - co->height;
7ea3dccd 510 }
511
512 gotoComponent(form, new);
513}
514
e67a6cab 515void newtFormSetTimer(newtComponent co, int millisecs) {
516 struct form * form = co->data;
517
518 form->timer = millisecs;
73390860 519 form->lastTimeout.tv_usec = 0;
520 form->lastTimeout.tv_sec = 0;
e67a6cab 521}
522
8c4e1047 523void newtFormSetHeight(newtComponent co, int height) {
f5daa14e 524 struct form * form = co->data;
525
8c4e1047 526 form->fixedHeight = 1;
f5daa14e 527 co->height = height;
528}
529
ea947804 530void newtFormSetWidth(newtComponent co, int width) {
531 co->width = width;
532}
533
c4827b34 534void newtFormAddComponent(newtComponent co, newtComponent newco) {
535 struct form * form = co->data;
6fb96a3f 536
8f52cd47 537 co->takesFocus = 1;
538
6fb96a3f 539 if (form->numCompsAlloced == form->numComps) {
6f481af2 540 form->numCompsAlloced += 5;
541 form->elements = realloc(form->elements,
542 sizeof(*(form->elements)) * form->numCompsAlloced);
6fb96a3f 543 }
544
f7540d34 545 /* we grab real values for these a bit later */
546 form->elements[form->numComps].left = -2;
547 form->elements[form->numComps].top = -2;
f5daa14e 548 form->elements[form->numComps].co = newco;
f1f3611f 549
550 if (newco->takesFocus && form->currComp == -1)
6f481af2 551 form->currComp = form->numComps;
f1f3611f 552
f5daa14e 553 form->numComps++;
6fb96a3f 554}
555
c4827b34 556void newtFormAddComponents(newtComponent co, ...) {
6fb96a3f 557 va_list ap;
c4827b34 558 newtComponent subco;
6fb96a3f 559
c4827b34 560 va_start(ap, co);
6fb96a3f 561
c4827b34 562 while ((subco = va_arg(ap, newtComponent)))
6f481af2 563 newtFormAddComponent(co, subco);
45f6c4fd 564
6fb96a3f 565 va_end(ap);
566}
567
8f52cd47 568static void formPlace(newtComponent co, int left, int top) {
569 struct form * form = co->data;
570 int vertDelta, horizDelta;
571 struct element * el;
572 int i;
573
574 newtFormSetSize(co);
575
576 vertDelta = top - co->top;
577 horizDelta = left - co->left;
578 co->top = top;
579 co->left = left;
580
581 for (i = 0, el = form->elements; i < form->numComps; i++, el++) {
6f481af2 582 el->co->top += vertDelta;
583 el->top += vertDelta;
584 el->co->left += horizDelta;
585 el->left += horizDelta;
8f52cd47 586 }
587}
588
8b68158d 589void newtDrawForm(newtComponent co) {
c4827b34 590 struct form * form = co->data;
f5daa14e 591 struct element * el;
6fb96a3f 592 int i;
6fb96a3f 593
3da9c9a2 594 newtFormSetSize(co);
595
8f4e87fc 596 SLsmg_set_color(form->background);
a1331450 597 newtClearBox(co->left, co->top, co->width, co->height);
7ff5fd71 598
f5daa14e 599 for (i = 0, el = form->elements; i < form->numComps; i++, el++) {
6f481af2 600 /* the scrollbar *always* fits somewhere */
601 if (el->co == form->vertBar) {
602 el->co->ops->mapped(el->co, 1);
603 el->co->ops->draw(el->co);
604 } else {
605 /* only draw it if it'll fit on the screen vertically */
606 if (componentFits(co, i)) {
607 el->co->top = el->top - form->vertOffset;
608 el->co->ops->mapped(el->co, 1);
609 el->co->ops->draw(el->co);
610 } else {
611 el->co->ops->mapped(el->co, 0);
612 }
613 }
6fb96a3f 614 }
f1f3611f 615
616 if (form->vertBar)
6f481af2 617 newtScrollbarSet(form->vertBar, form->vertOffset,
618 form->numRows - co->height);
c4827b34 619}
6fb96a3f 620
c4827b34 621static struct eventResult formEvent(newtComponent co, struct event ev) {
622 struct form * form = co->data;
f5daa14e 623 newtComponent subco = form->elements[form->currComp].co;
d1a682d1 624 int new, wrap = 0;
c4827b34 625 struct eventResult er;
d1a682d1 626 int dir = 0, page = 0;
45f6c4fd 627 int i, num, found;
628 struct element * el;
6fb96a3f 629
c4827b34 630 er.result = ER_IGNORED;
8f52cd47 631 if (!form->numComps) return er;
632
d78245ab 633 if (form->currComp == -1) return er;
6fb96a3f 634
434c766b 635 switch (ev.when) {
636 case EV_EARLY:
6f481af2 637 if (ev.event == EV_KEYPRESS) {
638 if (ev.u.key == NEWT_KEY_TAB) {
639 er.result = ER_SWALLOWED;
640 dir = 1;
641 wrap = 1;
642 } else if (ev.u.key == NEWT_KEY_UNTAB) {
643 er.result = ER_SWALLOWED;
644 dir = -1;
645 wrap = 1;
646 }
647 }
648
649 if (form->numComps) {
650 i = form->currComp;
651 num = 0;
652 while (er.result == ER_IGNORED && num != form->numComps ) {
653 er = form->elements[i].co->ops->event(form->elements[i].co, ev);
654
655 num++;
656 i++;
657 if (i == form->numComps) i = 0;
658 }
659 }
660
661 break;
45f6c4fd 662
434c766b 663 case EV_NORMAL:
6f481af2 664 if (ev.event == EV_MOUSE) {
665 found = 0;
666 for (i = 0, el = form->elements; i < form->numComps; i++, el++) {
667 if ((el->co->top <= ev.u.mouse.y) &&
668 (el->co->top + el->co->height > ev.u.mouse.y) &&
669 (el->co->left <= ev.u.mouse.x) &&
670 (el->co->left + el->co->width > ev.u.mouse.x)) {
671 found = 1;
672 if (el->co->takesFocus) {
673 gotoComponent(form, i);
674 subco = form->elements[form->currComp].co;
675 }
676 }
677 /* If we did not find a co to send this event to, we
678 should just swallow the event here. */
679 }
680 if (!found) {
681 er.result = ER_SWALLOWED;
682
683 return er;
684 }
685 }
686 er = subco->ops->event(subco, ev);
687 switch (er.result) {
688 case ER_NEXTCOMP:
689 er.result = ER_SWALLOWED;
690 dir = 1;
691 break;
692
693 case ER_EXITFORM:
694 form->exitComp = subco;
695 break;
696
697 default:
698 break;
699 }
700 break;
434c766b 701
702 case EV_LATE:
6f481af2 703 er = subco->ops->event(subco, ev);
704
705 if (er.result == ER_IGNORED) {
706 switch (ev.u.key) {
707 case NEWT_KEY_UP:
708 case NEWT_KEY_LEFT:
709 case NEWT_KEY_BKSPC:
710 er.result = ER_SWALLOWED;
711 dir = -1;
712 break;
713
714 case NEWT_KEY_DOWN:
715 case NEWT_KEY_RIGHT:
716 er.result = ER_SWALLOWED;
717 dir = 1;
718 break;
719
720 case NEWT_KEY_PGUP:
721 er.result = ER_SWALLOWED;
722 dir = -1;
723 page = 1;
724 break;
725
726 case NEWT_KEY_PGDN:
727 er.result = ER_SWALLOWED;
728 dir = 1;
729 page = 1;
730 break;
731 }
732 }
c4827b34 733 }
6fb96a3f 734
f5daa14e 735 if (dir) {
6f481af2 736 new = form->currComp;
737
738 if (page) {
739 new += dir * co->height;
740 if (new < 0)
741 new = 0;
742 else if (new >= form->numComps)
743 new = (form->numComps - 1);
744
745 while (!form->elements[new].co->takesFocus)
746 new = new - dir;
747 } else {
748 do {
749 new += dir;
750
751 if (wrap) {
752 if (new < 0)
753 new = form->numComps - 1;
754 else if (new >= form->numComps)
755 new = 0;
756 } else if (new < 0 || new >= form->numComps)
757 return er;
758 } while (!form->elements[new].co->takesFocus);
759 }
760
761 /* make sure this component is visible */
762 if (!componentFits(co, new)) {
763 gotoComponent(form, -1);
764
765 if (dir < 0) {
766 /* make the new component the first one */
767 form->vertOffset = form->elements[new].top - co->top;
768 } else {
769 /* make the new component the last one */
770 form->vertOffset = (form->elements[new].top +
771 form->elements[new].co->height) -
772 (co->top + co->height);
773 }
774
775 if (form->vertOffset < 0) form->vertOffset = 0;
776 if (form->vertOffset > (form->numRows - co->height))
777 form->vertOffset = form->numRows - co->height;
778
779 newtDrawForm(co);
780 }
781
782 gotoComponent(form, new);
783 er.result = ER_SWALLOWED;
c4827b34 784 }
6fb96a3f 785
c4827b34 786 return er;
6fb96a3f 787}
788
c101e99e
RJ
789/* Destroy a component. Components which have been added to a form
790 * are destroyed when the form is destroyed; this is just for the
791 * (rare) case of components which for whatever reason weren't added
792 * to a form.
793 */
794void newtComponentDestroy(newtComponent co) {
795 /* If the user registered a destroy callback for this component,
796 * now is a good time to call it.
797 */
798 if (co->destroyCallback)
799 co->destroyCallback(co, co->destroyCallbackData);
800
801 if (co->ops->destroy) {
802 co->ops->destroy(co);
803 } else {
804 if (co->data) free(co->data);
805 free(co);
806 }
807}
808
6fb96a3f 809/* this also destroys all of the components on the form */
c4827b34 810void newtFormDestroy(newtComponent co) {
c4827b34 811 newtComponent subco;
812 struct form * form = co->data;
f1f3611f 813 int i;
6fb96a3f 814
815 /* first, destroy all of the components */
816 for (i = 0; i < form->numComps; i++) {
6f481af2 817 subco = form->elements[i].co;
c101e99e 818 newtComponentDestroy(subco);
6fb96a3f 819 }
820
4e61f5db 821 if (form->hotKeys) free(form->hotKeys);
822
f5daa14e 823 free(form->elements);
6fb96a3f 824 free(form);
c4827b34 825 free(co);
826}
827
828newtComponent newtRunForm(newtComponent co) {
da151ca8 829 struct newtExitStruct es;
830
831 newtFormRun(co, &es);
832 if (es.reason == NEWT_EXIT_HOTKEY) {
6f481af2 833 if (es.u.key == NEWT_KEY_F12) {
834 es.reason = NEWT_EXIT_COMPONENT;
835 es.u.co = co;
836 } else {
837 return NULL;
838 }
da151ca8 839 }
840
841 return es.u.co;
842}
843
844void newtFormAddHotKey(newtComponent co, int key) {
845 struct form * form = co->data;
846
847 form->numHotKeys++;
848 form->hotKeys = realloc(form->hotKeys, sizeof(int) * form->numHotKeys);
849 form->hotKeys[form->numHotKeys - 1] = key;
850}
851
3da9c9a2 852void newtFormSetSize(newtComponent co) {
f7540d34 853 struct form * form = co->data;
854 int delta, i;
855 struct element * el;
856
3da9c9a2 857 if (form->beenSet) return;
858
859 form->beenSet = 1;
860
8f52cd47 861 if (!form->numComps) return;
862
f7540d34 863 co->width = 0;
3da9c9a2 864 if (!form->fixedHeight) co->height = 0;
865
f7540d34 866 co->top = form->elements[0].co->top;
867 co->left = form->elements[0].co->left;
868 for (i = 0, el = form->elements; i < form->numComps; i++, el++) {
6f481af2 869 if (el->co->ops == &formOps)
870 newtFormSetSize(el->co);
871
872 el->left = el->co->left;
873 el->top = el->co->top;
874
875 if (co->left > el->co->left) {
876 delta = co->left - el->co->left;
877 co->left -= delta;
878 co->width += delta;
879 }
880
881 if (co->top > el->co->top) {
882 delta = co->top - el->co->top;
883 co->top -= delta;
884 if (!form->fixedHeight)
885 co->height += delta;
886 }
887
888 if ((co->left + co->width) < (el->co->left + el->co->width))
889 co->width = (el->co->left + el->co->width) - co->left;
890
891 if (!form->fixedHeight) {
892 if ((co->top + co->height) < (el->co->top + el->co->height))
893 co->height = (el->co->top + el->co->height) - co->top;
894 }
895
896 if ((el->co->top + el->co->height - co->top) > form->numRows) {
897 form->numRows = el->co->top + el->co->height - co->top;
898 }
f7540d34 899 }
900}
901
da151ca8 902void newtFormRun(newtComponent co, struct newtExitStruct * es) {
c4827b34 903 struct form * form = co->data;
904 struct event ev;
905 struct eventResult er;
45f6c4fd 906 int key, i, max;
da151ca8 907 int done = 0;
d7e202d6 908 fd_set readSet, writeSet, exceptSet;
e67a6cab 909 struct timeval nextTimeout, now, timeout;
33b9b6eb 910#ifdef USE_GPM
45f6c4fd 911 int x, y;
912 Gpm_Connect conn;
913 Gpm_Event event;
914
915 /* Set up GPM interface */
916 conn.eventMask = ~GPM_MOVE;
917 conn.defaultMask = GPM_MOVE;
918 conn.minMod = 0;
919 conn.maxMod = 0;
920
921 Gpm_Open(&conn, 0);
922#endif
c4827b34 923
f7540d34 924 newtFormSetSize(co);
925 /* draw all of the components */
8b68158d 926 newtDrawForm(co);
c4827b34 927
f1f3611f 928 if (form->currComp == -1) {
6f481af2 929 gotoComponent(form, 0);
f1f3611f 930 } else
6f481af2 931 gotoComponent(form, form->currComp);
45f6c4fd 932
da151ca8 933 while (!done) {
6f481af2 934 newtRefresh();
c4827b34 935
6f481af2 936 FD_ZERO(&readSet);
937 FD_ZERO(&writeSet);
938 FD_ZERO(&exceptSet);
939 FD_SET(0, &readSet);
33b9b6eb 940#ifdef USE_GPM
6f481af2 941 if (gpm_fd > 0) {
942 FD_SET(gpm_fd, &readSet);
943 }
944 max = form->maxFd > gpm_fd ? form->maxFd : gpm_fd;
45f6c4fd 945#else
6f481af2 946 max = form->maxFd;
45f6c4fd 947#endif
948
6f481af2 949 for (i = 0; i < form->numFds; i++) {
950 if (form->fds[i].flags & NEWT_FD_READ)
951 FD_SET(form->fds[i].fd, &readSet);
952 if (form->fds[i].flags & NEWT_FD_WRITE)
953 FD_SET(form->fds[i].fd, &writeSet);
954 if (form->fds[i].flags & NEWT_FD_EXCEPT)
955 FD_SET(form->fds[i].fd, &exceptSet);
956 }
957
958 if (form->timer) {
959 /* Calculate when we next need to return with a timeout. Do
960 this inside the loop in case a callback resets the timer. */
57660927 961 gettimeofday(&now, 0);
962
963 if ((!form->lastTimeout.tv_sec && !form->lastTimeout.tv_usec) ||
964 now.tv_sec < form->lastTimeout.tv_sec ||
965 (now.tv_sec == form->lastTimeout.tv_sec &&
966 now.tv_usec < form->lastTimeout.tv_usec))
967 form->lastTimeout = now;
6f481af2 968
969 nextTimeout.tv_sec = form->lastTimeout.tv_sec +
970 (form->timer / 1000);
971 nextTimeout.tv_usec = form->lastTimeout.tv_usec +
972 (form->timer % 1000) * 1000;
973
6f481af2 974 if (now.tv_sec > nextTimeout.tv_sec) {
975 timeout.tv_sec = timeout.tv_usec = 0;
976 } else if (now.tv_sec == nextTimeout.tv_sec) {
977 timeout.tv_sec = 0;
978 if (now.tv_usec > nextTimeout.tv_usec)
979 timeout.tv_usec = 0;
980 else
981 timeout.tv_usec = nextTimeout.tv_usec - now.tv_usec;
982 } else if (now.tv_sec < nextTimeout.tv_sec) {
983 timeout.tv_sec = nextTimeout.tv_sec - now.tv_sec;
984 if (now.tv_usec > nextTimeout.tv_usec)
985 timeout.tv_sec--,
986 timeout.tv_usec = nextTimeout.tv_usec + 1000000 -
987 now.tv_usec;
988 else
989 timeout.tv_usec = nextTimeout.tv_usec - now.tv_usec;
990 }
991 } else {
992 timeout.tv_sec = timeout.tv_usec = 0;
993 }
994
995 i = select(max + 1, &readSet, &writeSet, &exceptSet,
996 form->timer ? &timeout : NULL);
997 if (i < 0) continue; /* ?? What should we do here? */
998
999 if (i == 0) {
1000 done = 1;
1001 es->reason = NEWT_EXIT_TIMER;
1002 gettimeofday(&form->lastTimeout, NULL);
1003 } else
33b9b6eb 1004#ifdef USE_GPM
6f481af2 1005 if (gpm_fd > 0 && FD_ISSET(gpm_fd, &readSet)) {
1006 Gpm_GetEvent(&event);
d02ffb49 1007
6f481af2 1008 if (event.type & GPM_DOWN) {
1009 /* Transform coordinates to current window */
1010 newtGetWindowPos(&x, &y);
c4827b34 1011
6f481af2 1012 ev.event = EV_MOUSE;
1013 ev.u.mouse.type = MOUSE_BUTTON_DOWN;
1014 ev.u.mouse.x = event.x - x - 1;
1015 ev.u.mouse.y = event.y - y - 1;
d02ffb49 1016
6f481af2 1017 /* Send the form the event */
1018 er = sendEvent(co, ev);
45f6c4fd 1019
6f481af2 1020 if (er.result == ER_EXITFORM) {
1021 done = 1;
1022 es->reason = NEWT_EXIT_COMPONENT;
1023 es->u.co = form->exitComp;
1024 }
45f6c4fd 1025
6f481af2 1026 }
1027 } else
45f6c4fd 1028#endif
6f481af2 1029 {
1030 if (FD_ISSET(0, &readSet)) {
1031
1032 key = newtGetKey();
1033
1034 if (key == NEWT_KEY_RESIZE) {
1035 newtResizeScreen(1);
1036 continue;
1037 }
1038
1039 for (i = 0; i < form->numHotKeys; i++) {
1040 if (form->hotKeys[i] == key) {
1041 es->reason = NEWT_EXIT_HOTKEY;
1042 es->u.key = key;
1043 done = 1;
1044 break;
1045 }
1046 }
1047
9fc6a6b0 1048 if (key == NEWT_KEY_F1 && form->helpTag && form->helpCb) {
1049 if (form->currComp != -1) {
1050 ev.event = EV_UNFOCUS;
1051 sendEvent(form->elements[form->currComp].co, ev);
1052 }
6f481af2 1053 form->helpCb(co, form->helpTag);
9fc6a6b0 1054 if (form->currComp != -1) {
1055 ev.event = EV_FOCUS;
1056 sendEvent(form->elements[form->currComp].co, ev);
1057 }
1058 }
6f481af2 1059
1060 if (!done) {
1061 ev.event = EV_KEYPRESS;
1062 ev.u.key = key;
1063
1064 er = sendEvent(co, ev);
1065
1066 if (er.result == ER_EXITFORM) {
1067 done = 1;
1068 es->reason = NEWT_EXIT_COMPONENT;
1069 es->u.co = form->exitComp;
1070 }
1071 }
1072 } else {
1073 for (i = 0; i < form->numFds; i++) {
1074 if (((form->fds[i].flags & NEWT_FD_READ)
1075 && FD_ISSET(form->fds[i].fd, &readSet))
1076 || ((form->fds[i].flags & NEWT_FD_WRITE)
1077 && FD_ISSET(form->fds[i].fd, &writeSet))
1078 || ((form->fds[i].flags & NEWT_FD_EXCEPT)
1079 && FD_ISSET(form->fds[i].fd, &exceptSet))) break;
1080 }
1081 if(i < form->numFds)
1082 es->u.watch = form->fds[i].fd;
1083 else
1084 es->u.watch = -1;
1085
1086 es->reason = NEWT_EXIT_FDREADY;
1087 done = 1;
1088 }
1089 }
45f6c4fd 1090 }
c4827b34 1091 newtRefresh();
33b9b6eb 1092#ifdef USE_GPM
45f6c4fd 1093 Gpm_Close();
1094#endif
6fb96a3f 1095}
1096
434c766b 1097static struct eventResult sendEvent(newtComponent co, struct event ev) {
1098 struct eventResult er;
1099
1100 ev.when = EV_EARLY;
1101 er = co->ops->event(co, ev);
1102
1103 if (er.result == ER_IGNORED) {
6f481af2 1104 ev.when = EV_NORMAL;
1105 er = co->ops->event(co, ev);
434c766b 1106 }
1107
1108 if (er.result == ER_IGNORED) {
6f481af2 1109 ev.when = EV_LATE;
1110 er = co->ops->event(co, ev);
434c766b 1111 }
1112
1113 return er;
1114}
1115
c4827b34 1116static void gotoComponent(struct form * form, int newComp) {
6fb96a3f 1117 struct event ev;
1118
1119 if (form->currComp != -1) {
6f481af2 1120 ev.event = EV_UNFOCUS;
1121 sendEvent(form->elements[form->currComp].co, ev);
6fb96a3f 1122 }
1123
1124 form->currComp = newComp;
45f6c4fd 1125
f5daa14e 1126 if (form->currComp != -1) {
6f481af2 1127 ev.event = EV_FOCUS;
1128 ev.when = EV_NORMAL;
1129 sendEvent(form->elements[form->currComp].co, ev);
f5daa14e 1130 }
6fb96a3f 1131}
da151ca8 1132
1133void newtComponentAddCallback(newtComponent co, newtCallback f, void * data) {
1134 co->callback = f;
1135 co->callbackData = data;
1136}
1137
c101e99e
RJ
1138/* Add a callback which is called when the component is destroyed. */
1139void newtComponentAddDestroyCallback(newtComponent co,
1140 newtCallback f, void * data) {
1141 co->destroyCallback = f;
1142 co->destroyCallbackData = data;
1143}
1144
41d86a3c 1145void newtComponentTakesFocus(newtComponent co, int val) {
1146 co->takesFocus = val;
1147}
1148
8f4e87fc 1149void newtFormSetBackground(newtComponent co, int color) {
1150 struct form * form = co->data;
1151
1152 form->background = color;
1153}
d02ffb49 1154
1155void newtFormWatchFd(newtComponent co, int fd, int fdFlags) {
1156 struct form * form = co->data;
005d8237 1157 int i;
1158
1159 for (i = 0; i < form->numFds; i++)
1160 if (form->fds[i].fd == fd)
6f481af2 1161 break;
005d8237 1162
1163 if(i >= form->numFds)
1164 form->fds = realloc(form->fds, (++form->numFds) * sizeof(*form->fds));
d02ffb49 1165
005d8237 1166 form->fds[i].fd = fd;
1167 form->fds[i].flags = fdFlags;
d02ffb49 1168 if (form->maxFd < fd) form->maxFd = fd;
1169}
72b71fa6 1170
1171void newtSetHelpCallback(newtCallback cb) {
1172 helpCallback = cb;
1173}