6
#include <sys/select.h>
11
#include <sys/time.h> /* timeval */
12
#include <sys/types.h> /* socket() */
13
#include <sys/socket.h> /* socket() */
14
#include <sys/un.h> /* struct sockaddr_un */
15
#include <sys/fcntl.h> /* O_RDONLY */
16
#include <sys/stat.h> /* stat() */
17
#include <termios.h> /* winsize */
19
#include <sys/kd.h> /* KDGETMODE */
28
/*....................................... The connection data structure */
30
typedef struct Gpm_Connect {
31
unsigned short eventMask, defaultMask;
32
unsigned short minMod, maxMod;
37
/*....................................... Stack struct */
38
typedef struct Gpm_Stst {
40
struct Gpm_Stst *next;
45
GPM_DRAG=2, /* exactly one of the bare ones is active at a time */
49
#define GPM_BARE_EVENTS(type) ((type)&(0x0f|GPM_ENTER|GPM_LEAVE))
51
GPM_SINGLE=16, /* at most one in three is set */
53
GPM_TRIPLE=64, /* WARNING: I depend on the values */
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 */
59
GPM_ENTER=512, /* enter event, user in Roi's */
60
GPM_LEAVE=1024 /* leave event, used in Roi's */
63
/*....................................... The reported event */
65
enum Gpm_Margin {GPM_TOP=1, GPM_BOT=2, GPM_LFT=4, GPM_RGT=8};
67
typedef struct Gpm_Event {
68
unsigned char buttons, modifiers; /* try to be a multiple of 4 */
73
enum Gpm_Margin margin;
76
static int Gpm_Open(Gpm_Connect *conn, int flag);
77
static int Gpm_Close(void);
80
static int gpm_flag=0;
81
static int gpm_tried=0;
82
Gpm_Stst *gpm_stack=NULL;
83
static struct sigaction gpm_saved_suspend_hook;
84
static struct sigaction gpm_saved_winch_hook;
88
#define GPM_NODE_DEV "/dev/gpmctl"
89
#define GPM_NODE_CTL GPM_NODE_DEV
91
static inline int putdata(int where, Gpm_Connect *what)
93
if (write(where,what,sizeof(Gpm_Connect))!=sizeof(Gpm_Connect))
100
static void gpm_winch_hook (int signum)
102
if (SIG_IGN != gpm_saved_winch_hook.sa_handler &&
103
SIG_DFL != gpm_saved_winch_hook.sa_handler) {
104
gpm_saved_winch_hook.sa_handler(signum);
108
static void gpm_suspend_hook (int signum)
110
Gpm_Connect gpm_connect;
116
sigemptyset (&new_sigset);
117
sigaddset (&new_sigset, SIGTSTP);
118
sigprocmask (SIG_BLOCK, &new_sigset, &old_sigset);
120
/* Open a completely transparent gpm connection */
121
gpm_connect.eventMask = 0;
122
gpm_connect.defaultMask = ~0;
123
gpm_connect.minMod = ~0;
124
gpm_connect.maxMod = 0;
125
/* cannot do this under xterm, tough */
126
success = (Gpm_Open (&gpm_connect, 0) >= 0);
128
/* take the default action, whatever it is (probably a stop :) */
129
sigprocmask (SIG_SETMASK, &old_sigset, 0);
130
sigaction (SIGTSTP, &gpm_saved_suspend_hook, 0);
131
kill (getpid (), SIGTSTP);
135
/* Reincarnation. Prepare for another death early. */
136
sigemptyset(&sa.sa_mask);
137
sa.sa_handler = gpm_suspend_hook;
138
sa.sa_flags = SA_NOMASK;
139
sigaction (SIGTSTP, &sa, 0);
141
/* Pop the gpm stack by closing the useless connection */
142
/* but do it only when we know we opened one.. */
148
static int Gpm_Open(Gpm_Connect *conn, int flag)
153
struct sockaddr_un addr;
157
/*....................................... First of all, check xterm */
159
if ((term=(char *)getenv("TERM")) && !strncmp(term,"xterm",5))
161
if (gpm_tried) return gpm_fd; /* no stack */
167
/*....................................... No xterm, go on */
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.
175
* However, making this piece of code work has been a real hassle.
178
if (!gpm_flag && gpm_tried) return -1;
179
gpm_tried=1; /* do or die */
181
new=malloc(sizeof(Gpm_Stst));
187
conn->pid=getpid(); /* fill obvious values */
190
conn->vc=new->next->info.vc; /* inherit */
193
conn->vc=0; /* default handler */
195
{ /* forced vc number */
197
sprintf(tty,"/dev/tty%i",flag);
199
else if (flag==0) /* use your current vc */
201
char *t = ttyname(0); /* stdin */
202
if (!t) t = ttyname(1); /* stdout */
205
if (strncmp(tty,"/dev/tty",8) || !isdigit(tty[8]))
207
conn->vc=atoi(tty+8);
209
else /* a default handler -- use console */
210
sprintf(tty,"/dev/tty0");
216
/*....................................... Connect to the control socket */
221
if ( (gpm_fd=socket(AF_UNIX,SOCK_STREAM,0))<0 )
226
bzero((char *)&addr,sizeof(addr));
227
addr.sun_family=AF_UNIX;
228
if (!(sock_name = tempnam (0, "gpm"))) {
231
strncpy (addr.sun_path, sock_name, sizeof (addr.sun_path));
232
if (bind (gpm_fd, (struct sockaddr*)&addr,
233
sizeof (addr.sun_family) + strlen (addr.sun_path))==-1) {
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);
242
if ( connect(gpm_fd,(struct sockaddr *)(&addr),i)<0 )
247
* Well, try to open a chr device called /dev/gpmctl. This should
248
* be forward-compatible with a kernel server
250
close(gpm_fd); /* the socket */
251
if ((gpm_fd=open(GPM_NODE_DEV,O_RDWR))==-1) {
254
if (fstat(gpm_fd,&stbuf)==-1 || (stbuf.st_mode&S_IFMT)!=S_IFCHR)
258
/*....................................... Put your data */
260
if (putdata(gpm_fd,conn)!=-1)
262
/* itz Wed Dec 16 23:22:16 PST 1998 use sigaction, the old
263
code caused a signal loop under XEmacs */
265
sigemptyset(&sa.sa_mask);
267
#if (defined(SIGWINCH))
268
/* And the winch hook .. */
269
sa.sa_handler = gpm_winch_hook;
271
sigaction(SIGWINCH, &sa, &gpm_saved_winch_hook);
274
#if (defined(SIGTSTP))
276
/* Install suspend hook */
277
sa.sa_handler = SIG_IGN;
278
sigaction(SIGTSTP, &sa, &gpm_saved_suspend_hook);
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);
292
/*....................................... Error: free all memory */
301
if (gpm_fd>=0) close(gpm_fd);
311
/*-------------------------------------------------------------------*/
312
static int Gpm_Close(void)
316
gpm_tried=0; /* reset the error flag for next time */
317
if (gpm_fd==-2) /* xterm */
321
if (!gpm_flag) return 0;
322
next=gpm_stack->next;
326
putdata(gpm_fd,&(next->info));
328
if (--gpm_flag) return -1;
331
if (gpm_fd>=0) close(gpm_fd);
334
sigaction(SIGTSTP, &gpm_saved_suspend_hook, 0);
337
sigaction(SIGWINCH, &gpm_saved_winch_hook, 0);
342
/*-------------------------------------------------------------------*/
343
static int Gpm_GetEvent(Gpm_Event *event)
347
if (!gpm_flag) return 0;
349
if ((count=read(gpm_fd,event,sizeof(Gpm_Event)))!=sizeof(Gpm_Event))
362
/****************************************************************************
363
These forms handle vertical scrolling of components with a height of 1
365
Horizontal scrolling won't work, and scrolling large widgets will fail
366
miserably. It shouldn't be too hard to fix either of those if anyone
367
cares to. I only use scrolling for listboxes and text boxes though so
369
*****************************************************************************/
372
int top, left; /* Actual, not virtual. These are translated */
373
newtComponent co; /* into actual through vertOffset */
383
struct element * elements;
389
newtComponent vertBar, exitComp;
399
int timer; /* in milliseconds */
400
struct timeval lastTimeout;
405
static void gotoComponent(struct form * form, int newComp);
406
static struct eventResult formEvent(newtComponent co, struct event ev);
407
static struct eventResult sendEvent(newtComponent comp, struct event ev);
408
static void formPlace(newtComponent co, int left, int top);
411
static newtCallback helpCallback;
413
/* this isn't static as grid.c tests against it to find forms */
414
struct componentOps formOps = {
419
newtDefaultMappedHandler,
422
static inline int componentFits(newtComponent co, int compNum) {
423
struct form * form = co->data;
424
struct element * el = form->elements + compNum;
426
if ((co->top + form->vertOffset) > el->top) return 0;
427
if ((co->top + form->vertOffset + co->height) <
428
(el->top + el->co->height)) return 0;
433
newtComponent newtForm(newtComponent vertBar, void * help, int flags) {
437
co = malloc(sizeof(*co));
438
form = malloc(sizeof(*form));
446
co->takesFocus = 0; /* we may have 0 components */
451
form->numCompsAlloced = 5;
454
form->vertOffset = 0;
455
form->fixedHeight = 0;
461
form->elements = malloc(sizeof(*(form->elements)) * form->numCompsAlloced);
463
form->background = COLORSET_WINDOW;
464
form->hotKeys = malloc(sizeof(int));
465
form->numHotKeys = 0;
467
form->lastTimeout.tv_sec = form->lastTimeout.tv_usec = 0;
468
if (!(form->flags & NEWT_FLAG_NOF12)) {
469
newtFormAddHotKey(co, NEWT_KEY_F12);
473
form->vertBar = vertBar;
475
form->vertBar = NULL;
477
form->helpTag = help;
478
form->helpCb = helpCallback;
483
newtComponent newtFormGetCurrent(newtComponent co) {
484
struct form * form = co->data;
486
return form->elements[form->currComp].co;
489
void newtFormSetCurrent(newtComponent co, newtComponent subco) {
490
struct form * form = co->data;
493
for (i = 0; i < form->numComps; i++) {
494
if (form->elements[i].co == subco) break;
497
if (form->elements[i].co != subco) return;
500
if (co->isMapped && !componentFits(co, new)) {
501
gotoComponent(form, -1);
502
form->vertOffset = form->elements[new].top - co->top - 1;
503
if (form->vertOffset > (form->numRows - co->height))
504
form->vertOffset = form->numRows - co->height;
507
gotoComponent(form, new);
510
void newtFormSetTimer(newtComponent co, int millisecs) {
511
struct form * form = co->data;
513
form->timer = millisecs;
514
form->lastTimeout.tv_usec = 0;
515
form->lastTimeout.tv_sec = 0;
518
void newtFormSetHeight(newtComponent co, int height) {
519
struct form * form = co->data;
521
form->fixedHeight = 1;
525
void newtFormSetWidth(newtComponent co, int width) {
529
void newtFormAddComponent(newtComponent co, newtComponent newco) {
530
struct form * form = co->data;
534
if (form->numCompsAlloced == form->numComps) {
535
form->numCompsAlloced += 5;
536
form->elements = realloc(form->elements,
537
sizeof(*(form->elements)) * form->numCompsAlloced);
540
/* we grab real values for these a bit later */
541
form->elements[form->numComps].left = -2;
542
form->elements[form->numComps].top = -2;
543
form->elements[form->numComps].co = newco;
545
if (newco->takesFocus && form->currComp == -1)
546
form->currComp = form->numComps;
551
void newtFormAddComponents(newtComponent co, ...) {
557
while ((subco = va_arg(ap, newtComponent)))
558
newtFormAddComponent(co, subco);
563
static void formPlace(newtComponent co, int left, int top) {
564
struct form * form = co->data;
565
int vertDelta, horizDelta;
571
vertDelta = top - co->top;
572
horizDelta = left - co->left;
576
for (i = 0, el = form->elements; i < form->numComps; i++, el++) {
577
el->co->top += vertDelta;
578
el->top += vertDelta;
579
el->co->left += horizDelta;
580
el->left += horizDelta;
584
void newtDrawForm(newtComponent co) {
585
struct form * form = co->data;
591
SLsmg_set_color(form->background);
592
newtClearBox(co->left, co->top, co->width, co->height);
593
for (i = 0, el = form->elements; i < form->numComps; i++, el++) {
594
/* the scrollbar *always* fits somewhere */
595
if (el->co == form->vertBar) {
596
el->co->ops->mapped(el->co, 1);
597
el->co->ops->draw(el->co);
599
/* only draw it if it'll fit on the screen vertically */
600
if (componentFits(co, i)) {
601
el->co->top = el->top - form->vertOffset;
602
el->co->ops->mapped(el->co, 1);
603
el->co->ops->draw(el->co);
605
el->co->ops->mapped(el->co, 0);
611
newtScrollbarSet(form->vertBar, form->vertOffset,
612
form->numRows - co->height);
615
static struct eventResult formEvent(newtComponent co, struct event ev) {
616
struct form * form = co->data;
617
newtComponent subco = form->elements[form->currComp].co;
619
struct eventResult er;
620
int dir = 0, page = 0;
624
er.result = ER_IGNORED;
625
if (!form->numComps) return er;
627
subco = form->elements[form->currComp].co;
631
if (ev.event == EV_KEYPRESS) {
632
if (ev.u.key == NEWT_KEY_TAB) {
633
er.result = ER_SWALLOWED;
636
} else if (ev.u.key == NEWT_KEY_UNTAB) {
637
er.result = ER_SWALLOWED;
643
if (form->numComps) {
646
while (er.result == ER_IGNORED && num != form->numComps ) {
647
er = form->elements[i].co->ops->event(form->elements[i].co, ev);
651
if (i == form->numComps) i = 0;
658
if (ev.event == EV_MOUSE) {
660
for (i = 0, el = form->elements; i < form->numComps; i++, el++) {
661
if ((el->co->top <= ev.u.mouse.y) &&
662
(el->co->top + el->co->height > ev.u.mouse.y) &&
663
(el->co->left <= ev.u.mouse.x) &&
664
(el->co->left + el->co->width > ev.u.mouse.x)) {
666
if (el->co->takesFocus) {
667
gotoComponent(form, i);
668
subco = form->elements[form->currComp].co;
671
/* If we did not find a co to send this event to, we
672
should just swallow the event here. */
675
er.result = ER_SWALLOWED;
680
er = subco->ops->event(subco, ev);
683
er.result = ER_SWALLOWED;
688
form->exitComp = subco;
697
er = subco->ops->event(subco, ev);
699
if (er.result == ER_IGNORED) {
704
er.result = ER_SWALLOWED;
710
er.result = ER_SWALLOWED;
715
er.result = ER_SWALLOWED;
721
er.result = ER_SWALLOWED;
730
new = form->currComp;
733
new += dir * co->height;
736
else if (new >= form->numComps)
737
new = (form->numComps - 1);
739
while (!form->elements[new].co->takesFocus)
747
new = form->numComps - 1;
748
else if (new >= form->numComps)
750
} else if (new < 0 || new >= form->numComps)
752
} while (!form->elements[new].co->takesFocus);
755
/* make sure this component is visible */
756
if (!componentFits(co, new)) {
757
gotoComponent(form, -1);
760
/* make the new component the first one */
761
form->vertOffset = form->elements[new].top - co->top;
763
/* make the new component the last one */
764
form->vertOffset = (form->elements[new].top +
765
form->elements[new].co->height) -
766
(co->top + co->height);
769
if (form->vertOffset < 0) form->vertOffset = 0;
770
if (form->vertOffset > (form->numRows - co->height))
771
form->vertOffset = form->numRows - co->height;
776
gotoComponent(form, new);
777
er.result = ER_SWALLOWED;
783
/* this also destroys all of the components on the form */
784
void newtFormDestroy(newtComponent co) {
786
struct form * form = co->data;
789
/* first, destroy all of the components */
790
for (i = 0; i < form->numComps; i++) {
791
subco = form->elements[i].co;
792
if (subco->ops->destroy) {
793
subco->ops->destroy(subco);
795
if (subco->data) free(subco->data);
800
if (form->hotKeys) free(form->hotKeys);
802
free(form->elements);
807
newtComponent newtRunForm(newtComponent co) {
808
struct newtExitStruct es;
810
newtFormRun(co, &es);
811
if (es.reason == NEWT_EXIT_HOTKEY) {
812
if (es.u.key == NEWT_KEY_F12) {
813
es.reason = NEWT_EXIT_COMPONENT;
823
void newtFormAddHotKey(newtComponent co, int key) {
824
struct form * form = co->data;
827
form->hotKeys = realloc(form->hotKeys, sizeof(int) * form->numHotKeys);
828
form->hotKeys[form->numHotKeys - 1] = key;
831
void newtFormSetSize(newtComponent co) {
832
struct form * form = co->data;
836
if (form->beenSet) return;
840
if (!form->numComps) return;
843
if (!form->fixedHeight) co->height = 0;
845
co->top = form->elements[0].co->top;
846
co->left = form->elements[0].co->left;
847
for (i = 0, el = form->elements; i < form->numComps; i++, el++) {
848
if (el->co->ops == &formOps)
849
newtFormSetSize(el->co);
851
el->left = el->co->left;
852
el->top = el->co->top;
854
if (co->left > el->co->left) {
855
delta = co->left - el->co->left;
860
if (co->top > el->co->top) {
861
delta = co->top - el->co->top;
863
if (!form->fixedHeight)
867
if ((co->left + co->width) < (el->co->left + el->co->width))
868
co->width = (el->co->left + el->co->width) - co->left;
870
if (!form->fixedHeight) {
871
if ((co->top + co->height) < (el->co->top + el->co->height))
872
co->height = (el->co->top + el->co->height) - co->top;
875
if ((el->co->top + el->co->height - co->top) > form->numRows) {
876
form->numRows = el->co->top + el->co->height - co->top;
881
void newtFormRun(newtComponent co, struct newtExitStruct * es) {
882
struct form * form = co->data;
884
struct eventResult er;
887
fd_set readSet, writeSet;
888
struct timeval nextTimeout, now, timeout;
894
/* Set up GPM interface */
895
conn.eventMask = ~GPM_MOVE;
896
conn.defaultMask = GPM_MOVE;
904
/* draw all of the components */
907
if (form->currComp == -1) {
908
gotoComponent(form, 0);
910
gotoComponent(form, form->currComp);
920
FD_SET(gpm_fd, &readSet);
922
max = form->maxFd > gpm_fd ? form->maxFd : gpm_fd;
927
for (i = 0; i < form->numFds; i++) {
928
if (form->fds[i].flags & NEWT_FD_READ)
929
FD_SET(form->fds[i].fd, &readSet);
930
if (form->fds[i].flags & NEWT_FD_WRITE)
931
FD_SET(form->fds[i].fd, &writeSet);
935
/* Calculate when we next need to return with a timeout. Do
936
this inside the loop in case a callback resets the timer. */
937
if (!form->lastTimeout.tv_sec && !form->lastTimeout.tv_usec)
938
gettimeofday(&form->lastTimeout, NULL);
940
nextTimeout.tv_sec = form->lastTimeout.tv_sec +
941
(form->timer / 1000);
942
nextTimeout.tv_usec = form->lastTimeout.tv_usec +
943
(form->timer % 1000) * 1000;
945
gettimeofday(&now, 0);
947
if (now.tv_sec > nextTimeout.tv_sec) {
948
timeout.tv_sec = timeout.tv_usec = 0;
949
} else if (now.tv_sec == nextTimeout.tv_sec) {
951
if (now.tv_usec > nextTimeout.tv_usec)
954
timeout.tv_usec = nextTimeout.tv_usec - now.tv_usec;
955
} else if (now.tv_sec < nextTimeout.tv_sec) {
956
timeout.tv_sec = nextTimeout.tv_sec - now.tv_sec;
957
if (now.tv_usec > nextTimeout.tv_usec)
959
timeout.tv_usec = nextTimeout.tv_usec + 1000000 -
962
timeout.tv_usec = nextTimeout.tv_usec - now.tv_usec;
965
timeout.tv_sec = timeout.tv_usec = 0;
968
i = select(max + 1, &readSet, &writeSet, NULL,
969
form->timer ? &timeout : NULL);
970
if (i < 0) continue; /* ?? What should we do here? */
974
es->reason = NEWT_EXIT_TIMER;
975
gettimeofday(&form->lastTimeout, NULL);
978
if (gpm_fd > 0 && FD_ISSET(gpm_fd, &readSet)) {
979
Gpm_GetEvent(&event);
981
if (event.type & GPM_DOWN) {
982
/* Transform coordinates to current window */
983
newtGetWindowPos(&x, &y);
986
ev.u.mouse.type = MOUSE_BUTTON_DOWN;
987
ev.u.mouse.x = event.x - x - 1;
988
ev.u.mouse.y = event.y - y - 1;
990
/* Send the form the event */
991
er = sendEvent(co, ev);
993
if (er.result == ER_EXITFORM) {
995
es->reason = NEWT_EXIT_COMPONENT;
996
es->u.co = form->exitComp;
1003
if (FD_ISSET(0, &readSet)) {
1007
if (key == NEWT_KEY_RESIZE) {
1008
/* newtResizeScreen(1); */
1012
for (i = 0; i < form->numHotKeys; i++) {
1013
if (form->hotKeys[i] == key) {
1014
es->reason = NEWT_EXIT_HOTKEY;
1021
if (key == NEWT_KEY_F1 && form->helpTag && form->helpCb)
1022
form->helpCb(co, form->helpTag);
1025
ev.event = EV_KEYPRESS;
1028
er = sendEvent(co, ev);
1030
if (er.result == ER_EXITFORM) {
1032
es->reason = NEWT_EXIT_COMPONENT;
1033
es->u.co = form->exitComp;
1037
es->reason = NEWT_EXIT_FDREADY;
1048
static struct eventResult sendEvent(newtComponent co, struct event ev) {
1049
struct eventResult er;
1052
er = co->ops->event(co, ev);
1054
if (er.result == ER_IGNORED) {
1055
ev.when = EV_NORMAL;
1056
er = co->ops->event(co, ev);
1059
if (er.result == ER_IGNORED) {
1061
er = co->ops->event(co, ev);
1067
static void gotoComponent(struct form * form, int newComp) {
1070
if (form->currComp != -1) {
1071
ev.event = EV_UNFOCUS;
1072
sendEvent(form->elements[form->currComp].co, ev);
1075
form->currComp = newComp;
1077
if (form->currComp != -1) {
1078
ev.event = EV_FOCUS;
1079
ev.when = EV_NORMAL;
1080
sendEvent(form->elements[form->currComp].co, ev);
1084
void newtComponentAddCallback(newtComponent co, newtCallback f, void * data) {
1086
co->callbackData = data;
1089
void newtComponentTakesFocus(newtComponent co, int val) {
1090
co->takesFocus = val;
1093
void newtFormSetBackground(newtComponent co, int color) {
1094
struct form * form = co->data;
1096
form->background = color;
1099
void newtFormWatchFd(newtComponent co, int fd, int fdFlags) {
1100
struct form * form = co->data;
1102
form->fds = realloc(form->fds, (form->numFds + 1) * sizeof(*form->fds));
1103
form->fds[form->numFds].fd = fd;
1104
form->fds[form->numFds++].flags = fdFlags;
1105
if (form->maxFd < fd) form->maxFd = fd;
1108
void newtSetHelpCallback(newtCallback cb) {