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