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