]> git.ipfire.org Git - thirdparty/newt.git/blame - form.c
improve handling of malloc failures
[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;
36ccb508 86static char *gpm_sock_name=NULL;
33b9b6eb 87static struct sigaction gpm_saved_suspend_hook;
88static 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
95static 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
104static 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
112static 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
152static 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;
33b9b6eb 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;
36ccb508 231 if (!(gpm_sock_name = tempnam (0, "gpm"))) {
33b9b6eb 232 goto err;
233 } /*if*/
36ccb508 234 strncpy (addr.sun_path, gpm_sock_name, sizeof (addr.sun_path));
33b9b6eb 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);
36ccb508
ML
305 if (gpm_sock_name) {
306 unlink(gpm_sock_name);
307 free(gpm_sock_name);
308 gpm_sock_name = NULL;
33b9b6eb 309 } /*if*/
310 gpm_flag=0;
9a3aad92 311 gpm_fd=-1;
33b9b6eb 312 return -1;
313}
314
315/*-------------------------------------------------------------------*/
316static 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;
36ccb508
ML
337 if (gpm_sock_name) {
338 unlink(gpm_sock_name);
339 free(gpm_sock_name);
340 gpm_sock_name = NULL;
341 }
33b9b6eb 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/*-------------------------------------------------------------------*/
352static 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
f5daa14e 371/****************************************************************************
45f6c4fd 372 These forms handle vertical scrolling of components with a height of 1
373
f5daa14e 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
380struct element {
5c72fb03 381 newtComponent co;
f5daa14e 382};
383
d02ffb49 384struct fdInfo {
385 int fd;
386 int flags;
387};
388
c4827b34 389struct form {
6fb96a3f 390 int numCompsAlloced;
f5daa14e 391 struct element * elements;
6fb96a3f 392 int numComps;
393 int currComp;
8c4e1047 394 int fixedHeight;
02b180e8 395 int flags;
f5daa14e 396 int vertOffset;
02b180e8 397 newtComponent vertBar, exitComp;
d4109c37 398 const char * help;
f1f3611f 399 int numRows;
da151ca8 400 int * hotKeys;
401 int numHotKeys;
8f4e87fc 402 int background;
d02ffb49 403 int numFds;
404 struct fdInfo * fds;
405 int maxFd;
e67a6cab 406 int timer; /* in milliseconds */
407 struct timeval lastTimeout;
72b71fa6 408 void * helpTag;
409 newtCallback helpCb;
6fb96a3f 410};
411
ad2dca95 412static void gotoComponent(newtComponent co, int newComp);
c4827b34 413static struct eventResult formEvent(newtComponent co, struct event ev);
434c766b 414static struct eventResult sendEvent(newtComponent comp, struct event ev);
8f52cd47 415static void formPlace(newtComponent co, int left, int top);
c4827b34 416
72b71fa6 417/* Global, ick */
418static newtCallback helpCallback;
419
8f52cd47 420/* this isn't static as grid.c tests against it to find forms */
421struct componentOps formOps = {
8b68158d 422 newtDrawForm,
c4827b34 423 formEvent,
424 newtFormDestroy,
8f52cd47 425 formPlace,
426 newtDefaultMappedHandler,
c4827b34 427} ;
428
0593cb33
ML
429int needResize = 0;
430
f5daa14e 431static inline int componentFits(newtComponent co, int compNum) {
432 struct form * form = co->data;
433 struct element * el = form->elements + compNum;
434
5c72fb03
ML
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;
f5daa14e 439
440 return 1;
441}
442
72b71fa6 443newtComponent newtForm(newtComponent vertBar, void * help, int flags) {
c4827b34 444 newtComponent co;
445 struct form * form;
446
447 co = malloc(sizeof(*co));
d92e05d0
RG
448 if (co == NULL)
449 return NULL;
450
c4827b34 451 form = malloc(sizeof(*form));
d92e05d0
RG
452 if (form == NULL) {
453 free(co);
454 return NULL;
455 }
456
c4827b34 457 co->data = form;
458 co->width = 0;
459 co->height = 0;
460 co->top = -1;
461 co->left = -1;
3da9c9a2 462 co->isMapped = 0;
c4827b34 463
6f481af2 464 co->takesFocus = 0; /* we may have 0 components */
c4827b34 465 co->ops = &formOps;
ad2dca95 466 co->callback = NULL;
c101e99e 467 co->destroyCallback = NULL;
6fb96a3f 468
d92e05d0
RG
469 form->elements = NULL;
470 form->hotKeys = NULL;
471
02b180e8 472 form->help = help;
473 form->flags = flags;
6fb96a3f 474 form->numCompsAlloced = 5;
475 form->numComps = 0;
476 form->currComp = -1;
f5daa14e 477 form->vertOffset = 0;
8c4e1047 478 form->fixedHeight = 0;
f1f3611f 479 form->numRows = 0;
d02ffb49 480 form->numFds = 0;
481 form->maxFd = 0;
482 form->fds = NULL;
f5daa14e 483 form->elements = malloc(sizeof(*(form->elements)) * form->numCompsAlloced);
d92e05d0 484 if (form->elements == NULL) goto error;
6fb96a3f 485
8f4e87fc 486 form->background = COLORSET_WINDOW;
da151ca8 487 form->hotKeys = malloc(sizeof(int));
d92e05d0
RG
488 if (form->hotKeys == NULL) goto error;
489
da151ca8 490 form->numHotKeys = 0;
73390860 491 form->timer = 0;
492 form->lastTimeout.tv_sec = form->lastTimeout.tv_usec = 0;
4a93351d 493 if (!(form->flags & NEWT_FLAG_NOF12)) {
6f481af2 494 newtFormAddHotKey(co, NEWT_KEY_F12);
da151ca8 495 }
496
02b180e8 497 if (vertBar)
6f481af2 498 form->vertBar = vertBar;
f1f3611f 499 else
6f481af2 500 form->vertBar = NULL;
f1f3611f 501
72b71fa6 502 form->helpTag = help;
503 form->helpCb = helpCallback;
504
c4827b34 505 return co;
d92e05d0
RG
506
507error:
508 free(form->hotKeys);
509 free(form->elements);
510 free(form);
511 free(co);
512
513 return NULL;
6fb96a3f 514}
515
2a2998a0 516newtComponent newtFormGetCurrent(newtComponent co) {
517 struct form * form = co->data;
518
d78245ab 519 if (form->currComp == -1) return 0;
2a2998a0 520 return form->elements[form->currComp].co;
521}
522
5c72fb03
ML
523static void formScroll(newtComponent co, int delta) {
524 struct form * form = co->data;
525 struct element * el;
526 int i, newVertOffset = form->vertOffset + delta;
527
528 if (newVertOffset < 0)
529 newVertOffset = 0;
530 if (newVertOffset > form->numRows - co->height)
531 newVertOffset = form->numRows - co->height;
532
533 delta = newVertOffset - form->vertOffset;
534 form->vertOffset = newVertOffset;
535
536 for (i = 0, el = form->elements; i < form->numComps; i++, el++) {
537 if (el->co == form->vertBar)
538 continue;
539 el->co->ops->place(el->co, el->co->left, el->co->top - delta);
540 }
541}
542
7f72e4ef
DW
543int newtFormGetScrollPosition(newtComponent co) {
544 struct form * form = co->data;
545
546 return form->vertOffset;
547}
548
549void newtFormSetScrollPosition(newtComponent co, int position) {
550 struct form * form = co->data;
551
552 if (form->numRows == 0)
553 newtFormSetSize(co);
554 formScroll(co, position - form->vertOffset);
555}
556
7ea3dccd 557void newtFormSetCurrent(newtComponent co, newtComponent subco) {
558 struct form * form = co->data;
559 int i, new;
560
561 for (i = 0; i < form->numComps; i++) {
6f481af2 562 if (form->elements[i].co == subco) break;
7ea3dccd 563 }
564
565 if (form->elements[i].co != subco) return;
566 new = i;
567
3da9c9a2 568 if (co->isMapped && !componentFits(co, new)) {
ad2dca95 569 gotoComponent(co, -1);
5c72fb03 570 formScroll(co, form->elements[new].co->top - co->top - 1);
7ea3dccd 571 }
572
ad2dca95 573 gotoComponent(co, new);
7ea3dccd 574}
575
e67a6cab 576void newtFormSetTimer(newtComponent co, int millisecs) {
577 struct form * form = co->data;
578
579 form->timer = millisecs;
73390860 580 form->lastTimeout.tv_usec = 0;
581 form->lastTimeout.tv_sec = 0;
e67a6cab 582}
583
8c4e1047 584void newtFormSetHeight(newtComponent co, int height) {
f5daa14e 585 struct form * form = co->data;
586
8c4e1047 587 form->fixedHeight = 1;
f5daa14e 588 co->height = height;
589}
590
ea947804 591void newtFormSetWidth(newtComponent co, int width) {
592 co->width = width;
593}
594
c4827b34 595void newtFormAddComponent(newtComponent co, newtComponent newco) {
596 struct form * form = co->data;
6fb96a3f 597
8f52cd47 598 co->takesFocus = 1;
599
6fb96a3f 600 if (form->numCompsAlloced == form->numComps) {
6f481af2 601 form->numCompsAlloced += 5;
602 form->elements = realloc(form->elements,
603 sizeof(*(form->elements)) * form->numCompsAlloced);
6fb96a3f 604 }
605
f5daa14e 606 form->elements[form->numComps].co = newco;
f1f3611f 607
608 if (newco->takesFocus && form->currComp == -1)
6f481af2 609 form->currComp = form->numComps;
f1f3611f 610
f5daa14e 611 form->numComps++;
6fb96a3f 612}
613
c4827b34 614void newtFormAddComponents(newtComponent co, ...) {
6fb96a3f 615 va_list ap;
c4827b34 616 newtComponent subco;
6fb96a3f 617
c4827b34 618 va_start(ap, co);
6fb96a3f 619
c4827b34 620 while ((subco = va_arg(ap, newtComponent)))
6f481af2 621 newtFormAddComponent(co, subco);
45f6c4fd 622
6fb96a3f 623 va_end(ap);
624}
625
8f52cd47 626static void formPlace(newtComponent co, int left, int top) {
627 struct form * form = co->data;
628 int vertDelta, horizDelta;
629 struct element * el;
630 int i;
631
8f52cd47 632 vertDelta = top - co->top;
633 horizDelta = left - co->left;
634 co->top = top;
635 co->left = left;
636
637 for (i = 0, el = form->elements; i < form->numComps; i++, el++) {
5c72fb03
ML
638 el->co->ops->place(el->co, el->co->left + horizDelta,
639 el->co->top + vertDelta);
8f52cd47 640 }
641}
642
8b68158d 643void newtDrawForm(newtComponent co) {
c4827b34 644 struct form * form = co->data;
f5daa14e 645 struct element * el;
6fb96a3f 646 int i;
6fb96a3f 647
3da9c9a2 648 newtFormSetSize(co);
649
8f4e87fc 650 SLsmg_set_color(form->background);
a1331450 651 newtClearBox(co->left, co->top, co->width, co->height);
7ff5fd71 652
f5daa14e 653 for (i = 0, el = form->elements; i < form->numComps; i++, el++) {
5c72fb03
ML
654 /* only draw it if it'll fit on the screen vertically
655 (the scrollbar *always* fits somewhere) */
656 if (el->co == form->vertBar || componentFits(co, i)) {
6f481af2 657 el->co->ops->mapped(el->co, 1);
658 el->co->ops->draw(el->co);
659 } else {
5c72fb03 660 el->co->ops->mapped(el->co, 0);
6f481af2 661 }
6fb96a3f 662 }
f1f3611f 663
664 if (form->vertBar)
6f481af2 665 newtScrollbarSet(form->vertBar, form->vertOffset,
666 form->numRows - co->height);
c4827b34 667}
6fb96a3f 668
c4827b34 669static struct eventResult formEvent(newtComponent co, struct event ev) {
670 struct form * form = co->data;
f5daa14e 671 newtComponent subco = form->elements[form->currComp].co;
d1a682d1 672 int new, wrap = 0;
c4827b34 673 struct eventResult er;
d1a682d1 674 int dir = 0, page = 0;
45f6c4fd 675 int i, num, found;
676 struct element * el;
6fb96a3f 677
c4827b34 678 er.result = ER_IGNORED;
8f52cd47 679 if (!form->numComps) return er;
680
d78245ab 681 if (form->currComp == -1) return er;
6fb96a3f 682
434c766b 683 switch (ev.when) {
684 case EV_EARLY:
6f481af2 685 if (ev.event == EV_KEYPRESS) {
686 if (ev.u.key == NEWT_KEY_TAB) {
687 er.result = ER_SWALLOWED;
688 dir = 1;
689 wrap = 1;
690 } else if (ev.u.key == NEWT_KEY_UNTAB) {
691 er.result = ER_SWALLOWED;
692 dir = -1;
693 wrap = 1;
694 }
695 }
696
697 if (form->numComps) {
698 i = form->currComp;
699 num = 0;
700 while (er.result == ER_IGNORED && num != form->numComps ) {
701 er = form->elements[i].co->ops->event(form->elements[i].co, ev);
702
703 num++;
704 i++;
705 if (i == form->numComps) i = 0;
706 }
707 }
708
709 break;
45f6c4fd 710
434c766b 711 case EV_NORMAL:
6f481af2 712 if (ev.event == EV_MOUSE) {
713 found = 0;
714 for (i = 0, el = form->elements; i < form->numComps; i++, el++) {
715 if ((el->co->top <= ev.u.mouse.y) &&
716 (el->co->top + el->co->height > ev.u.mouse.y) &&
717 (el->co->left <= ev.u.mouse.x) &&
718 (el->co->left + el->co->width > ev.u.mouse.x)) {
719 found = 1;
720 if (el->co->takesFocus) {
ad2dca95 721 gotoComponent(co, i);
6f481af2 722 subco = form->elements[form->currComp].co;
723 }
724 }
725 /* If we did not find a co to send this event to, we
726 should just swallow the event here. */
727 }
728 if (!found) {
729 er.result = ER_SWALLOWED;
730
731 return er;
732 }
733 }
734 er = subco->ops->event(subco, ev);
735 switch (er.result) {
736 case ER_NEXTCOMP:
737 er.result = ER_SWALLOWED;
738 dir = 1;
739 break;
740
741 case ER_EXITFORM:
742 form->exitComp = subco;
743 break;
744
745 default:
746 break;
747 }
748 break;
434c766b 749
750 case EV_LATE:
6f481af2 751 er = subco->ops->event(subco, ev);
752
753 if (er.result == ER_IGNORED) {
754 switch (ev.u.key) {
755 case NEWT_KEY_UP:
756 case NEWT_KEY_LEFT:
757 case NEWT_KEY_BKSPC:
758 er.result = ER_SWALLOWED;
759 dir = -1;
760 break;
761
762 case NEWT_KEY_DOWN:
763 case NEWT_KEY_RIGHT:
764 er.result = ER_SWALLOWED;
765 dir = 1;
766 break;
767
768 case NEWT_KEY_PGUP:
769 er.result = ER_SWALLOWED;
770 dir = -1;
771 page = 1;
772 break;
773
774 case NEWT_KEY_PGDN:
775 er.result = ER_SWALLOWED;
776 dir = 1;
777 page = 1;
778 break;
779 }
780 }
c4827b34 781 }
6fb96a3f 782
f5daa14e 783 if (dir) {
6f481af2 784 new = form->currComp;
785
786 if (page) {
787 new += dir * co->height;
788 if (new < 0)
789 new = 0;
790 else if (new >= form->numComps)
791 new = (form->numComps - 1);
792
cf69773e
ML
793 while (!form->elements[new].co->takesFocus &&
794 new - dir >= 0 && new - dir < form->numComps)
795 new -= dir;
6f481af2 796 } else {
797 do {
798 new += dir;
799
800 if (wrap) {
801 if (new < 0)
802 new = form->numComps - 1;
803 else if (new >= form->numComps)
804 new = 0;
cf69773e
ML
805 if (new == form->currComp)
806 /* back where we started */
807 return er;
6f481af2 808 } else if (new < 0 || new >= form->numComps)
809 return er;
810 } while (!form->elements[new].co->takesFocus);
811 }
812
813 /* make sure this component is visible */
814 if (!componentFits(co, new)) {
5c72fb03
ML
815 int vertDelta;
816
ad2dca95 817 gotoComponent(co, -1);
6f481af2 818
819 if (dir < 0) {
820 /* make the new component the first one */
5c72fb03 821 vertDelta = form->elements[new].co->top - co->top;
6f481af2 822 } else {
823 /* make the new component the last one */
5c72fb03 824 vertDelta = (form->elements[new].co->top +
6f481af2 825 form->elements[new].co->height) -
826 (co->top + co->height);
827 }
828
5c72fb03 829 formScroll(co, vertDelta);
6f481af2 830 newtDrawForm(co);
831 }
832
ad2dca95 833 gotoComponent(co, new);
6f481af2 834 er.result = ER_SWALLOWED;
c4827b34 835 }
6fb96a3f 836
c4827b34 837 return er;
6fb96a3f 838}
839
c101e99e
RJ
840/* Destroy a component. Components which have been added to a form
841 * are destroyed when the form is destroyed; this is just for the
842 * (rare) case of components which for whatever reason weren't added
843 * to a form.
844 */
845void newtComponentDestroy(newtComponent co) {
846 /* If the user registered a destroy callback for this component,
847 * now is a good time to call it.
848 */
849 if (co->destroyCallback)
850 co->destroyCallback(co, co->destroyCallbackData);
851
852 if (co->ops->destroy) {
853 co->ops->destroy(co);
854 } else {
855 if (co->data) free(co->data);
856 free(co);
857 }
858}
859
6fb96a3f 860/* this also destroys all of the components on the form */
c4827b34 861void newtFormDestroy(newtComponent co) {
c4827b34 862 newtComponent subco;
863 struct form * form = co->data;
f1f3611f 864 int i;
6fb96a3f 865
866 /* first, destroy all of the components */
867 for (i = 0; i < form->numComps; i++) {
6f481af2 868 subco = form->elements[i].co;
c101e99e 869 newtComponentDestroy(subco);
6fb96a3f 870 }
871
4e61f5db 872 if (form->hotKeys) free(form->hotKeys);
873
f5daa14e 874 free(form->elements);
6fb96a3f 875 free(form);
c4827b34 876 free(co);
877}
878
879newtComponent newtRunForm(newtComponent co) {
da151ca8 880 struct newtExitStruct es;
881
882 newtFormRun(co, &es);
883 if (es.reason == NEWT_EXIT_HOTKEY) {
6f481af2 884 if (es.u.key == NEWT_KEY_F12) {
885 es.reason = NEWT_EXIT_COMPONENT;
886 es.u.co = co;
887 } else {
888 return NULL;
889 }
b8b8a86f
ML
890 } else if (es.reason == NEWT_EXIT_ERROR)
891 return NULL;
da151ca8 892
893 return es.u.co;
894}
895
896void newtFormAddHotKey(newtComponent co, int key) {
897 struct form * form = co->data;
898
899 form->numHotKeys++;
900 form->hotKeys = realloc(form->hotKeys, sizeof(int) * form->numHotKeys);
901 form->hotKeys[form->numHotKeys - 1] = key;
902}
903
3da9c9a2 904void newtFormSetSize(newtComponent co) {
f7540d34 905 struct form * form = co->data;
21150df5 906 int delta, i, first;
f7540d34 907 struct element * el;
908
21150df5 909 form->numRows = 0;
8f52cd47 910
f7540d34 911 co->width = 0;
3da9c9a2 912 if (!form->fixedHeight) co->height = 0;
913
35d6a6ab
ML
914 co->top = -1;
915 co->left = -1;
21150df5 916 first = 1;
35d6a6ab 917
f7540d34 918 for (i = 0, el = form->elements; i < form->numComps; i++, el++) {
6f481af2 919 if (el->co->ops == &formOps)
920 newtFormSetSize(el->co);
35d6a6ab
ML
921 else if (el->co == form->vertBar)
922 continue;
923
21150df5 924 if (first) {
35d6a6ab
ML
925 co->top = el->co->top;
926 co->left = el->co->left;
21150df5 927 first = 0;
35d6a6ab 928 }
6f481af2 929
6f481af2 930 if (co->left > el->co->left) {
931 delta = co->left - el->co->left;
932 co->left -= delta;
933 co->width += delta;
934 }
935
936 if (co->top > el->co->top) {
937 delta = co->top - el->co->top;
938 co->top -= delta;
35d6a6ab 939 form->numRows += delta;
6f481af2 940 if (!form->fixedHeight)
941 co->height += delta;
942 }
943
944 if ((co->left + co->width) < (el->co->left + el->co->width))
945 co->width = (el->co->left + el->co->width) - co->left;
946
947 if (!form->fixedHeight) {
948 if ((co->top + co->height) < (el->co->top + el->co->height))
949 co->height = (el->co->top + el->co->height) - co->top;
950 }
951
952 if ((el->co->top + el->co->height - co->top) > form->numRows) {
953 form->numRows = el->co->top + el->co->height - co->top;
954 }
f7540d34 955 }
5c72fb03
ML
956
957 co->top += form->vertOffset;
f7540d34 958}
959
da151ca8 960void newtFormRun(newtComponent co, struct newtExitStruct * es) {
c4827b34 961 struct form * form = co->data;
962 struct event ev;
963 struct eventResult er;
45f6c4fd 964 int key, i, max;
da151ca8 965 int done = 0;
d7e202d6 966 fd_set readSet, writeSet, exceptSet;
e67a6cab 967 struct timeval nextTimeout, now, timeout;
33b9b6eb 968#ifdef USE_GPM
45f6c4fd 969 int x, y;
970 Gpm_Connect conn;
971 Gpm_Event event;
972
973 /* Set up GPM interface */
974 conn.eventMask = ~GPM_MOVE;
975 conn.defaultMask = GPM_MOVE;
976 conn.minMod = 0;
977 conn.maxMod = 0;
978
979 Gpm_Open(&conn, 0);
980#endif
c4827b34 981
f7540d34 982 /* draw all of the components */
8b68158d 983 newtDrawForm(co);
c4827b34 984
f1f3611f 985 if (form->currComp == -1) {
d3cfe899 986 if (form->numComps)
ad2dca95 987 gotoComponent(co, 0);
f1f3611f 988 } else
ad2dca95 989 gotoComponent(co, form->currComp);
45f6c4fd 990
da151ca8 991 while (!done) {
6f481af2 992 newtRefresh();
c4827b34 993
6f481af2 994 FD_ZERO(&readSet);
995 FD_ZERO(&writeSet);
996 FD_ZERO(&exceptSet);
997 FD_SET(0, &readSet);
33b9b6eb 998#ifdef USE_GPM
6f481af2 999 if (gpm_fd > 0) {
1000 FD_SET(gpm_fd, &readSet);
1001 }
1002 max = form->maxFd > gpm_fd ? form->maxFd : gpm_fd;
45f6c4fd 1003#else
6f481af2 1004 max = form->maxFd;
45f6c4fd 1005#endif
1006
6f481af2 1007 for (i = 0; i < form->numFds; i++) {
1008 if (form->fds[i].flags & NEWT_FD_READ)
1009 FD_SET(form->fds[i].fd, &readSet);
1010 if (form->fds[i].flags & NEWT_FD_WRITE)
1011 FD_SET(form->fds[i].fd, &writeSet);
1012 if (form->fds[i].flags & NEWT_FD_EXCEPT)
1013 FD_SET(form->fds[i].fd, &exceptSet);
1014 }
1015
1016 if (form->timer) {
1017 /* Calculate when we next need to return with a timeout. Do
1018 this inside the loop in case a callback resets the timer. */
57660927 1019 gettimeofday(&now, 0);
1020
1021 if ((!form->lastTimeout.tv_sec && !form->lastTimeout.tv_usec) ||
1022 now.tv_sec < form->lastTimeout.tv_sec ||
1023 (now.tv_sec == form->lastTimeout.tv_sec &&
1024 now.tv_usec < form->lastTimeout.tv_usec))
1025 form->lastTimeout = now;
6f481af2 1026
1027 nextTimeout.tv_sec = form->lastTimeout.tv_sec +
1028 (form->timer / 1000);
1029 nextTimeout.tv_usec = form->lastTimeout.tv_usec +
1030 (form->timer % 1000) * 1000;
1031
6f481af2 1032 if (now.tv_sec > nextTimeout.tv_sec) {
1033 timeout.tv_sec = timeout.tv_usec = 0;
1034 } else if (now.tv_sec == nextTimeout.tv_sec) {
1035 timeout.tv_sec = 0;
1036 if (now.tv_usec > nextTimeout.tv_usec)
1037 timeout.tv_usec = 0;
1038 else
1039 timeout.tv_usec = nextTimeout.tv_usec - now.tv_usec;
1040 } else if (now.tv_sec < nextTimeout.tv_sec) {
1041 timeout.tv_sec = nextTimeout.tv_sec - now.tv_sec;
1042 if (now.tv_usec > nextTimeout.tv_usec)
1043 timeout.tv_sec--,
1044 timeout.tv_usec = nextTimeout.tv_usec + 1000000 -
1045 now.tv_usec;
1046 else
1047 timeout.tv_usec = nextTimeout.tv_usec - now.tv_usec;
1048 }
1049 } else {
1050 timeout.tv_sec = timeout.tv_usec = 0;
1051 }
1052
0593cb33
ML
1053 if (needResize) {
1054 needResize = 0;
1055 newtResizeScreen(1);
d861e991
ML
1056
1057 /* The application may want to handle the resize */
1058 for (i = 0; i < form->numHotKeys; i++) {
1059 if (form->hotKeys[i] == NEWT_KEY_RESIZE) {
1060 es->reason = NEWT_EXIT_HOTKEY;
1061 es->u.key = NEWT_KEY_RESIZE;
1062 done = 1;
1063 break;
1064 }
1065 }
1066 if (done)
1067 break;
0593cb33
ML
1068 }
1069
6f481af2 1070 i = select(max + 1, &readSet, &writeSet, &exceptSet,
1071 form->timer ? &timeout : NULL);
1072 if (i < 0) continue; /* ?? What should we do here? */
1073
1074 if (i == 0) {
1075 done = 1;
1076 es->reason = NEWT_EXIT_TIMER;
1077 gettimeofday(&form->lastTimeout, NULL);
1078 } else
33b9b6eb 1079#ifdef USE_GPM
6f481af2 1080 if (gpm_fd > 0 && FD_ISSET(gpm_fd, &readSet)) {
1081 Gpm_GetEvent(&event);
d02ffb49 1082
6f481af2 1083 if (event.type & GPM_DOWN) {
1084 /* Transform coordinates to current window */
1085 newtGetWindowPos(&x, &y);
c4827b34 1086
6f481af2 1087 ev.event = EV_MOUSE;
1088 ev.u.mouse.type = MOUSE_BUTTON_DOWN;
1089 ev.u.mouse.x = event.x - x - 1;
1090 ev.u.mouse.y = event.y - y - 1;
d02ffb49 1091
6f481af2 1092 /* Send the form the event */
1093 er = sendEvent(co, ev);
45f6c4fd 1094
6f481af2 1095 if (er.result == ER_EXITFORM) {
1096 done = 1;
1097 es->reason = NEWT_EXIT_COMPONENT;
1098 es->u.co = form->exitComp;
1099 }
45f6c4fd 1100
6f481af2 1101 }
1102 } else
45f6c4fd 1103#endif
6f481af2 1104 {
1105 if (FD_ISSET(0, &readSet)) {
1106
1107 key = newtGetKey();
1108
6f481af2 1109 for (i = 0; i < form->numHotKeys; i++) {
1110 if (form->hotKeys[i] == key) {
1111 es->reason = NEWT_EXIT_HOTKEY;
1112 es->u.key = key;
1113 done = 1;
1114 break;
1115 }
1116 }
1117
9fc6a6b0 1118 if (key == NEWT_KEY_F1 && form->helpTag && form->helpCb) {
1119 if (form->currComp != -1) {
1120 ev.event = EV_UNFOCUS;
1121 sendEvent(form->elements[form->currComp].co, ev);
1122 }
6f481af2 1123 form->helpCb(co, form->helpTag);
9fc6a6b0 1124 if (form->currComp != -1) {
1125 ev.event = EV_FOCUS;
1126 sendEvent(form->elements[form->currComp].co, ev);
1127 }
1128 }
6f481af2 1129
b8b8a86f
ML
1130 if (key == NEWT_KEY_ERROR) {
1131 es->u.watch = -1;
1132 es->reason = NEWT_EXIT_ERROR;
1133 done = 1;
1134 }
1135
6f481af2 1136 if (!done) {
1137 ev.event = EV_KEYPRESS;
1138 ev.u.key = key;
1139
1140 er = sendEvent(co, ev);
1141
1142 if (er.result == ER_EXITFORM) {
1143 done = 1;
1144 es->reason = NEWT_EXIT_COMPONENT;
1145 es->u.co = form->exitComp;
1146 }
1147 }
1148 } else {
1149 for (i = 0; i < form->numFds; i++) {
1150 if (((form->fds[i].flags & NEWT_FD_READ)
1151 && FD_ISSET(form->fds[i].fd, &readSet))
1152 || ((form->fds[i].flags & NEWT_FD_WRITE)
1153 && FD_ISSET(form->fds[i].fd, &writeSet))
1154 || ((form->fds[i].flags & NEWT_FD_EXCEPT)
1155 && FD_ISSET(form->fds[i].fd, &exceptSet))) break;
1156 }
1157 if(i < form->numFds)
1158 es->u.watch = form->fds[i].fd;
1159 else
1160 es->u.watch = -1;
1161
1162 es->reason = NEWT_EXIT_FDREADY;
1163 done = 1;
1164 }
1165 }
45f6c4fd 1166 }
c4827b34 1167 newtRefresh();
33b9b6eb 1168#ifdef USE_GPM
45f6c4fd 1169 Gpm_Close();
1170#endif
6fb96a3f 1171}
1172
434c766b 1173static struct eventResult sendEvent(newtComponent co, struct event ev) {
1174 struct eventResult er;
1175
1176 ev.when = EV_EARLY;
1177 er = co->ops->event(co, ev);
1178
1179 if (er.result == ER_IGNORED) {
6f481af2 1180 ev.when = EV_NORMAL;
1181 er = co->ops->event(co, ev);
434c766b 1182 }
1183
1184 if (er.result == ER_IGNORED) {
6f481af2 1185 ev.when = EV_LATE;
1186 er = co->ops->event(co, ev);
434c766b 1187 }
1188
1189 return er;
1190}
1191
ad2dca95
DW
1192static void gotoComponent(newtComponent co, int newComp) {
1193 struct form * form = co->data;
6fb96a3f 1194 struct event ev;
1195
1196 if (form->currComp != -1) {
6f481af2 1197 ev.event = EV_UNFOCUS;
1198 sendEvent(form->elements[form->currComp].co, ev);
6fb96a3f 1199 }
1200
1201 form->currComp = newComp;
45f6c4fd 1202
f5daa14e 1203 if (form->currComp != -1) {
6f481af2 1204 ev.event = EV_FOCUS;
1205 ev.when = EV_NORMAL;
1206 sendEvent(form->elements[form->currComp].co, ev);
f5daa14e 1207 }
ad2dca95
DW
1208
1209 if (co->callback)
1210 co->callback(co, co->callbackData);
6fb96a3f 1211}
da151ca8 1212
1213void newtComponentAddCallback(newtComponent co, newtCallback f, void * data) {
1214 co->callback = f;
1215 co->callbackData = data;
1216}
1217
c101e99e
RJ
1218/* Add a callback which is called when the component is destroyed. */
1219void newtComponentAddDestroyCallback(newtComponent co,
1220 newtCallback f, void * data) {
1221 co->destroyCallback = f;
1222 co->destroyCallbackData = data;
1223}
1224
41d86a3c 1225void newtComponentTakesFocus(newtComponent co, int val) {
1226 co->takesFocus = val;
1227}
1228
8f4e87fc 1229void newtFormSetBackground(newtComponent co, int color) {
1230 struct form * form = co->data;
1231
1232 form->background = color;
1233}
d02ffb49 1234
1235void newtFormWatchFd(newtComponent co, int fd, int fdFlags) {
1236 struct form * form = co->data;
005d8237 1237 int i;
1238
1239 for (i = 0; i < form->numFds; i++)
1240 if (form->fds[i].fd == fd)
6f481af2 1241 break;
005d8237 1242
1243 if(i >= form->numFds)
1244 form->fds = realloc(form->fds, (++form->numFds) * sizeof(*form->fds));
d02ffb49 1245
005d8237 1246 form->fds[i].fd = fd;
1247 form->fds[i].flags = fdFlags;
d02ffb49 1248 if (form->maxFd < fd) form->maxFd = fd;
1249}
72b71fa6 1250
1251void newtSetHelpCallback(newtCallback cb) {
1252 helpCallback = cb;
1253}