#include "config.h"
#include <sys/types.h>
#include <slang.h>
#include <stdarg.h>
#include <stdlib.h>
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#include <sys/time.h>
#ifdef USE_GPM
#include <ctype.h>
#include <sys/time.h> /* timeval */
#include <sys/socket.h> /* socket() */
#include <sys/un.h> /* struct sockaddr_un */
#include <sys/fcntl.h> /* O_RDONLY */
#include <sys/stat.h> /* stat() */
#include <termios.h> /* winsize */
#include <unistd.h>
#include <sys/kd.h> /* KDGETMODE */
#include <signal.h>
#include <stdio.h>
#endif
#include "newt.h"
#include "newt_pr.h"
#ifdef USE_GPM
/*....................................... The connection data structure */
typedef struct Gpm_Connect {
unsigned short eventMask, defaultMask;
unsigned short minMod, maxMod;
int pid;
int vc;
} Gpm_Connect;
/*....................................... Stack struct */
typedef struct Gpm_Stst {
Gpm_Connect info;
struct Gpm_Stst *next;
} Gpm_Stst;
enum Gpm_Etype {
GPM_MOVE=1,
GPM_DRAG=2, /* exactly one of the bare ones is active at a time */
GPM_DOWN=4,
GPM_UP= 8,
#define GPM_BARE_EVENTS(type) ((type)&(0x0f|GPM_ENTER|GPM_LEAVE))
GPM_SINGLE=16, /* at most one in three is set */
GPM_DOUBLE=32,
GPM_TRIPLE=64, /* WARNING: I depend on the values */
GPM_MFLAG=128, /* motion during click? */
GPM_HARD=256, /* if set in the defaultMask, force an already
used event to pass over to another handler */
GPM_ENTER=512, /* enter event, user in Roi's */
GPM_LEAVE=1024 /* leave event, used in Roi's */
};
/*....................................... The reported event */
enum Gpm_Margin {GPM_TOP=1, GPM_BOT=2, GPM_LFT=4, GPM_RGT=8};
typedef struct Gpm_Event {
unsigned char buttons, modifiers; /* try to be a multiple of 4 */
unsigned short vc;
short dx, dy, x, y;
enum Gpm_Etype type;
int clicks;
enum Gpm_Margin margin;
} Gpm_Event;
static int Gpm_Open(Gpm_Connect *conn, int flag);
static int Gpm_Close(void);
static int gpm_fd=-1;
static int gpm_flag=0;
static int gpm_tried=0;
Gpm_Stst *gpm_stack=NULL;
static char *gpm_sock_name=NULL;
static struct sigaction gpm_saved_suspend_hook;
static struct sigaction gpm_saved_winch_hook;
#define GPM_XTERM_ON
#define GPM_XTERM_OFF
#define GPM_NODE_DEV "/dev/gpmctl"
#define GPM_NODE_CTL GPM_NODE_DEV
static inline int putdata(int where, Gpm_Connect *what)
{
if (write(where,what,sizeof(Gpm_Connect))!=sizeof(Gpm_Connect))
{
return -1;
}
return 0;
}
static void gpm_winch_hook (int signum)
{
if (SIG_IGN != gpm_saved_winch_hook.sa_handler &&
SIG_DFL != gpm_saved_winch_hook.sa_handler) {
gpm_saved_winch_hook.sa_handler(signum);
} /*if*/
}
static void gpm_suspend_hook (int signum)
{
Gpm_Connect gpm_connect;
sigset_t old_sigset;
sigset_t new_sigset;
struct sigaction sa;
int success;
sigemptyset (&new_sigset);
sigaddset (&new_sigset, SIGTSTP);
sigprocmask (SIG_BLOCK, &new_sigset, &old_sigset);
/* Open a completely transparent gpm connection */
gpm_connect.eventMask = 0;
gpm_connect.defaultMask = ~0;
gpm_connect.minMod = ~0;
gpm_connect.maxMod = 0;
/* cannot do this under xterm, tough */
success = (Gpm_Open (&gpm_connect, 0) >= 0);
/* take the default action, whatever it is (probably a stop :) */
sigprocmask (SIG_SETMASK, &old_sigset, 0);
sigaction (SIGTSTP, &gpm_saved_suspend_hook, 0);
kill (getpid (), SIGTSTP);
/* in bardo here */
/* Reincarnation. Prepare for another death early. */
sigemptyset(&sa.sa_mask);
sa.sa_handler = gpm_suspend_hook;
sa.sa_flags = SA_NOMASK;
sigaction (SIGTSTP, &sa, 0);
/* Pop the gpm stack by closing the useless connection */
/* but do it only when we know we opened one.. */
if (success) {
Gpm_Close ();
} /*if*/
}
static int Gpm_Open(Gpm_Connect *conn, int flag)
{
char tty[32];
char *term;
int i;
struct sockaddr_un addr;
Gpm_Stst *new;
/*....................................... First of all, check xterm */
if ((term=(char *)getenv("TERM")) && !strncmp(term,"xterm",5))
{
if (gpm_tried) return gpm_fd; /* no stack */
gpm_fd=-2;
GPM_XTERM_ON;
gpm_flag=1;
return gpm_fd;
}
/*....................................... No xterm, go on */
/*
* So I chose to use the current tty, instead of /dev/console, which
* has permission problems. (I am fool, and my console is
* readable/writeable by everybody.
*
* However, making this piece of code work has been a real hassle.
*/
if (!gpm_flag && gpm_tried) return -1;
gpm_tried=1; /* do or die */
new=malloc(sizeof(Gpm_Stst));
if (!new) return -1;
new->next=gpm_stack;
gpm_stack=new;
conn->pid=getpid(); /* fill obvious values */
if (new->next)
conn->vc=new->next->info.vc; /* inherit */
else
{
conn->vc=0; /* default handler */
if (flag>0)
{ /* forced vc number */
conn->vc=flag;
sprintf(tty,"/dev/tty%i",flag);
}
else if (flag==0) /* use your current vc */
{
char *t = ttyname(0); /* stdin */
if (!t) t = ttyname(1); /* stdout */
if (!t) goto err;
strcpy(tty,t);
if (strncmp(tty,"/dev/tty",8) || !isdigit(tty[8]))
goto err;
conn->vc=atoi(tty+8);
}
else /* a default handler -- use console */
sprintf(tty,"/dev/tty0");
}
new->info=*conn;
/*....................................... Connect to the control socket */
if (!(gpm_flag++))
{
if ( (gpm_fd=socket(AF_UNIX,SOCK_STREAM,0))<0 )
{
goto err;
}
bzero((char *)&addr,sizeof(addr));
addr.sun_family=AF_UNIX;
if (!(gpm_sock_name = tempnam (0, "gpm"))) {
goto err;
} /*if*/
strncpy (addr.sun_path, gpm_sock_name, sizeof (addr.sun_path));
if (bind (gpm_fd, (struct sockaddr*)&addr,
sizeof (addr.sun_family) + strlen (addr.sun_path))==-1) {
goto err;
} /*if*/
bzero((char *)&addr,sizeof(addr));
addr.sun_family=AF_UNIX;
strcpy(addr.sun_path, GPM_NODE_CTL);
i=sizeof(addr.sun_family)+strlen(GPM_NODE_CTL);
if ( connect(gpm_fd,(struct sockaddr *)(&addr),i)<0 )
{
struct stat stbuf;
/*
* Well, try to open a chr device called /dev/gpmctl. This should
* be forward-compatible with a kernel server
*/
close(gpm_fd); /* the socket */
if ((gpm_fd=open(GPM_NODE_DEV,O_RDWR))==-1) {
goto err;
} /*if*/
if (fstat(gpm_fd,&stbuf)==-1 || (stbuf.st_mode&S_IFMT)!=S_IFCHR)
goto err;
}
}
/*....................................... Put your data */
if (putdata(gpm_fd,conn)!=-1)
{
/* itz Wed Dec 16 23:22:16 PST 1998 use sigaction, the old
code caused a signal loop under XEmacs */
struct sigaction sa;
sigemptyset(&sa.sa_mask);
#if (defined(SIGWINCH))
/* And the winch hook .. */
sa.sa_handler = gpm_winch_hook;
sa.sa_flags = 0;
sigaction(SIGWINCH, &sa, &gpm_saved_winch_hook);
#endif
#if (defined(SIGTSTP))
if (gpm_flag == 1) {
/* Install suspend hook */
sa.sa_handler = SIG_IGN;
sigaction(SIGTSTP, &sa, &gpm_saved_suspend_hook);
/* if signal was originally ignored, job control is not supported */
if (gpm_saved_suspend_hook.sa_handler != SIG_IGN) {
sa.sa_flags = SA_NOMASK;
sa.sa_handler = gpm_suspend_hook;
sigaction(SIGTSTP, &sa, 0);
} /*if*/
} /*if*/
#endif
} /*if*/
return gpm_fd;
/*....................................... Error: free all memory */
err:
do
{
new=gpm_stack->next;
free(gpm_stack);
gpm_stack=new;
}
while(gpm_stack);
if (gpm_fd>=0) close(gpm_fd);
if (gpm_sock_name) {
unlink(gpm_sock_name);
free(gpm_sock_name);
gpm_sock_name = NULL;
} /*if*/
gpm_flag=0;
gpm_fd=-1;
return -1;
}
/*-------------------------------------------------------------------*/
static int Gpm_Close(void)
{
Gpm_Stst *next;
gpm_tried=0; /* reset the error flag for next time */
if (gpm_fd==-2) /* xterm */
GPM_XTERM_OFF;
else /* linux */
{
if (!gpm_flag) return 0;
next=gpm_stack->next;
free(gpm_stack);
gpm_stack=next;
if (next)
putdata(gpm_fd,&(next->info));
if (--gpm_flag) return -1;
}
if (gpm_fd>=0) close(gpm_fd);
gpm_fd=-1;
if (gpm_sock_name) {
unlink(gpm_sock_name);
free(gpm_sock_name);
gpm_sock_name = NULL;
}
#ifdef SIGTSTP
sigaction(SIGTSTP, &gpm_saved_suspend_hook, 0);
#endif
#ifdef SIGWINCH
sigaction(SIGWINCH, &gpm_saved_winch_hook, 0);
#endif
return 0;
}
/*-------------------------------------------------------------------*/
static int Gpm_GetEvent(Gpm_Event *event)
{
int count;
if (!gpm_flag) return 0;
if ((count=read(gpm_fd,event,sizeof(Gpm_Event)))!=sizeof(Gpm_Event))
{
if (count==0)
{
Gpm_Close();
return 0;
}
return -1;
}
return 1;
}
#endif
/****************************************************************************
These forms handle vertical scrolling of components with a height of 1
Horizontal scrolling won't work, and scrolling large widgets will fail
miserably. It shouldn't be too hard to fix either of those if anyone
cares to. I only use scrolling for listboxes and text boxes though so
I didn't bother.
*****************************************************************************/
struct element {
newtComponent co;
};
struct fdInfo {
int fd;
int flags;
};
struct form {
int numCompsAlloced;
struct element * elements;
int numComps;
int currComp;
int fixedHeight;
int flags;
int vertOffset;
newtComponent vertBar, exitComp;
const char * help;
int numRows;
int * hotKeys;
int numHotKeys;
int background;
int numFds;
struct fdInfo * fds;
int maxFd;
int timer; /* in milliseconds */
struct timeval lastTimeout;
void * helpTag;
newtCallback helpCb;
};
static void gotoComponent(newtComponent co, int newComp);
static struct eventResult formEvent(newtComponent co, struct event ev);
static struct eventResult sendEvent(newtComponent comp, struct event ev);
static void formPlace(newtComponent co, int left, int top);
/* Global, ick */
static newtCallback helpCallback;
/* this isn't static as grid.c tests against it to find forms */
struct componentOps formOps = {
newtDrawForm,
formEvent,
newtFormDestroy,
formPlace,
newtDefaultMappedHandler,
} ;
int needResize = 0;
static inline int componentFits(newtComponent co, int compNum) {
struct form * form = co->data;
struct element * el = form->elements + compNum;
if (co->top > el->co->top)
return 0;
if (co->top + co->height < el->co->top + el->co->height)
return 0;
return 1;
}
newtComponent newtForm(newtComponent vertBar, void * help, int flags) {
newtComponent co;
struct form * form;
co = malloc(sizeof(*co));
form = malloc(sizeof(*form));
co->data = form;
co->width = 0;
co->height = 0;
co->top = -1;
co->left = -1;
co->isMapped = 0;
co->takesFocus = 0; /* we may have 0 components */
co->ops = &formOps;
co->callback = NULL;
co->destroyCallback = NULL;
form->help = help;
form->flags = flags;
form->numCompsAlloced = 5;
form->numComps = 0;
form->currComp = -1;
form->vertOffset = 0;
form->fixedHeight = 0;
form->numRows = 0;
form->numFds = 0;
form->maxFd = 0;
form->fds = NULL;
form->elements = malloc(sizeof(*(form->elements)) * form->numCompsAlloced);
form->background = COLORSET_WINDOW;
form->hotKeys = malloc(sizeof(int));
form->numHotKeys = 0;
form->timer = 0;
form->lastTimeout.tv_sec = form->lastTimeout.tv_usec = 0;
if (!(form->flags & NEWT_FLAG_NOF12)) {
newtFormAddHotKey(co, NEWT_KEY_F12);
}
if (vertBar)
form->vertBar = vertBar;
else
form->vertBar = NULL;
form->helpTag = help;
form->helpCb = helpCallback;
return co;
}
newtComponent newtFormGetCurrent(newtComponent co) {
struct form * form = co->data;
if (form->currComp == -1) return 0;
return form->elements[form->currComp].co;
}
static void formScroll(newtComponent co, int delta) {
struct form * form = co->data;
struct element * el;
int i, newVertOffset = form->vertOffset + delta;
if (newVertOffset < 0)
newVertOffset = 0;
if (newVertOffset > form->numRows - co->height)
newVertOffset = form->numRows - co->height;
delta = newVertOffset - form->vertOffset;
form->vertOffset = newVertOffset;
for (i = 0, el = form->elements; i < form->numComps; i++, el++) {
if (el->co == form->vertBar)
continue;
el->co->ops->place(el->co, el->co->left, el->co->top - delta);
}
}
int newtFormGetScrollPosition(newtComponent co) {
struct form * form = co->data;
return form->vertOffset;
}
void newtFormSetScrollPosition(newtComponent co, int position) {
struct form * form = co->data;
if (form->numRows == 0)
newtFormSetSize(co);
formScroll(co, position - form->vertOffset);
}
void newtFormSetCurrent(newtComponent co, newtComponent subco) {
struct form * form = co->data;
int i, new;
for (i = 0; i < form->numComps; i++) {
if (form->elements[i].co == subco) break;
}
if (form->elements[i].co != subco) return;
new = i;
if (co->isMapped && !componentFits(co, new)) {
gotoComponent(co, -1);
formScroll(co, form->elements[new].co->top - co->top - 1);
}
gotoComponent(co, new);
}
void newtFormSetTimer(newtComponent co, int millisecs) {
struct form * form = co->data;
form->timer = millisecs;
form->lastTimeout.tv_usec = 0;
form->lastTimeout.tv_sec = 0;
}
void newtFormSetHeight(newtComponent co, int height) {
struct form * form = co->data;
form->fixedHeight = 1;
co->height = height;
}
void newtFormSetWidth(newtComponent co, int width) {
co->width = width;
}
void newtFormAddComponent(newtComponent co, newtComponent newco) {
struct form * form = co->data;
co->takesFocus = 1;
if (form->numCompsAlloced == form->numComps) {
form->numCompsAlloced += 5;
form->elements = realloc(form->elements,
sizeof(*(form->elements)) * form->numCompsAlloced);
}
form->elements[form->numComps].co = newco;
if (newco->takesFocus && form->currComp == -1)
form->currComp = form->numComps;
form->numComps++;
}
void newtFormAddComponents(newtComponent co, ...) {
va_list ap;
newtComponent subco;
va_start(ap, co);
while ((subco = va_arg(ap, newtComponent)))
newtFormAddComponent(co, subco);
va_end(ap);
}
static void formPlace(newtComponent co, int left, int top) {
struct form * form = co->data;
int vertDelta, horizDelta;
struct element * el;
int i;
vertDelta = top - co->top;
horizDelta = left - co->left;
co->top = top;
co->left = left;
for (i = 0, el = form->elements; i < form->numComps; i++, el++) {
el->co->ops->place(el->co, el->co->left + horizDelta,
el->co->top + vertDelta);
}
}
void newtDrawForm(newtComponent co) {
struct form * form = co->data;
struct element * el;
int i;
newtFormSetSize(co);
SLsmg_set_color(form->background);
newtClearBox(co->left, co->top, co->width, co->height);
for (i = 0, el = form->elements; i < form->numComps; i++, el++) {
/* only draw it if it'll fit on the screen vertically
(the scrollbar *always* fits somewhere) */
if (el->co == form->vertBar || componentFits(co, i)) {
el->co->ops->mapped(el->co, 1);
el->co->ops->draw(el->co);
} else {
el->co->ops->mapped(el->co, 0);
}
}
if (form->vertBar)
newtScrollbarSet(form->vertBar, form->vertOffset,
form->numRows - co->height);
}
static struct eventResult formEvent(newtComponent co, struct event ev) {
struct form * form = co->data;
newtComponent subco = form->elements[form->currComp].co;
int new, wrap = 0;
struct eventResult er;
int dir = 0, page = 0;
int i, num, found;
struct element * el;
er.result = ER_IGNORED;
if (!form->numComps) return er;
if (form->currComp == -1) return er;
switch (ev.when) {
case EV_EARLY:
if (ev.event == EV_KEYPRESS) {
if (ev.u.key == NEWT_KEY_TAB) {
er.result = ER_SWALLOWED;
dir = 1;
wrap = 1;
} else if (ev.u.key == NEWT_KEY_UNTAB) {
er.result = ER_SWALLOWED;
dir = -1;
wrap = 1;
}
}
if (form->numComps) {
i = form->currComp;
num = 0;
while (er.result == ER_IGNORED && num != form->numComps ) {
er = form->elements[i].co->ops->event(form->elements[i].co, ev);
num++;
i++;
if (i == form->numComps) i = 0;
}
}
break;
case EV_NORMAL:
if (ev.event == EV_MOUSE) {
found = 0;
for (i = 0, el = form->elements; i < form->numComps; i++, el++) {
if ((el->co->top <= ev.u.mouse.y) &&
(el->co->top + el->co->height > ev.u.mouse.y) &&
(el->co->left <= ev.u.mouse.x) &&
(el->co->left + el->co->width > ev.u.mouse.x)) {
found = 1;
if (el->co->takesFocus) {
gotoComponent(co, i);
subco = form->elements[form->currComp].co;
}
}
/* If we did not find a co to send this event to, we
should just swallow the event here. */
}
if (!found) {
er.result = ER_SWALLOWED;
return er;
}
}
er = subco->ops->event(subco, ev);
switch (er.result) {
case ER_NEXTCOMP:
er.result = ER_SWALLOWED;
dir = 1;
break;
case ER_EXITFORM:
form->exitComp = subco;
break;
default:
break;
}
break;
case EV_LATE:
er = subco->ops->event(subco, ev);
if (er.result == ER_IGNORED) {
switch (ev.u.key) {
case NEWT_KEY_UP:
case NEWT_KEY_LEFT:
case NEWT_KEY_BKSPC:
er.result = ER_SWALLOWED;
dir = -1;
break;
case NEWT_KEY_DOWN:
case NEWT_KEY_RIGHT:
er.result = ER_SWALLOWED;
dir = 1;
break;
case NEWT_KEY_PGUP:
er.result = ER_SWALLOWED;
dir = -1;
page = 1;
break;
case NEWT_KEY_PGDN:
er.result = ER_SWALLOWED;
dir = 1;
page = 1;
break;
}
}
}
if (dir) {
new = form->currComp;
if (page) {
new += dir * co->height;
if (new < 0)
new = 0;
else if (new >= form->numComps)
new = (form->numComps - 1);
while (!form->elements[new].co->takesFocus &&
new - dir >= 0 && new - dir < form->numComps)
new -= dir;
} else {
do {
new += dir;
if (wrap) {
if (new < 0)
new = form->numComps - 1;
else if (new >= form->numComps)
new = 0;
if (new == form->currComp)
/* back where we started */
return er;
} else if (new < 0 || new >= form->numComps)
return er;
} while (!form->elements[new].co->takesFocus);
}
/* make sure this component is visible */
if (!componentFits(co, new)) {
int vertDelta;
gotoComponent(co, -1);
if (dir < 0) {
/* make the new component the first one */
vertDelta = form->elements[new].co->top - co->top;
} else {
/* make the new component the last one */
vertDelta = (form->elements[new].co->top +
form->elements[new].co->height) -
(co->top + co->height);
}
formScroll(co, vertDelta);
newtDrawForm(co);
}
gotoComponent(co, new);
er.result = ER_SWALLOWED;
}
return er;
}
/* Destroy a component. Components which have been added to a form
* are destroyed when the form is destroyed; this is just for the
* (rare) case of components which for whatever reason weren't added
* to a form.
*/
void newtComponentDestroy(newtComponent co) {
/* If the user registered a destroy callback for this component,
* now is a good time to call it.
*/
if (co->destroyCallback)
co->destroyCallback(co, co->destroyCallbackData);
if (co->ops->destroy) {
co->ops->destroy(co);
} else {
if (co->data) free(co->data);
free(co);
}
}
/* this also destroys all of the components on the form */
void newtFormDestroy(newtComponent co) {
newtComponent subco;
struct form * form = co->data;
int i;
/* first, destroy all of the components */
for (i = 0; i < form->numComps; i++) {
subco = form->elements[i].co;
newtComponentDestroy(subco);
}
if (form->hotKeys) free(form->hotKeys);
free(form->elements);
free(form);
free(co);
}
newtComponent newtRunForm(newtComponent co) {
struct newtExitStruct es;
newtFormRun(co, &es);
if (es.reason == NEWT_EXIT_HOTKEY) {
if (es.u.key == NEWT_KEY_F12) {
es.reason = NEWT_EXIT_COMPONENT;
es.u.co = co;
} else {
return NULL;
}
} else if (es.reason == NEWT_EXIT_ERROR)
return NULL;
return es.u.co;
}
void newtFormAddHotKey(newtComponent co, int key) {
struct form * form = co->data;
form->numHotKeys++;
form->hotKeys = realloc(form->hotKeys, sizeof(int) * form->numHotKeys);
form->hotKeys[form->numHotKeys - 1] = key;
}
void newtFormSetSize(newtComponent co) {
struct form * form = co->data;
int delta, i, first;
struct element * el;
form->numRows = 0;
co->width = 0;
if (!form->fixedHeight) co->height = 0;
co->top = -1;
co->left = -1;
first = 1;
for (i = 0, el = form->elements; i < form->numComps; i++, el++) {
if (el->co->ops == &formOps)
newtFormSetSize(el->co);
else if (el->co == form->vertBar)
continue;
if (first) {
co->top = el->co->top;
co->left = el->co->left;
first = 0;
}
if (co->left > el->co->left) {
delta = co->left - el->co->left;
co->left -= delta;
co->width += delta;
}
if (co->top > el->co->top) {
delta = co->top - el->co->top;
co->top -= delta;
form->numRows += delta;
if (!form->fixedHeight)
co->height += delta;
}
if ((co->left + co->width) < (el->co->left + el->co->width))
co->width = (el->co->left + el->co->width) - co->left;
if (!form->fixedHeight) {
if ((co->top + co->height) < (el->co->top + el->co->height))
co->height = (el->co->top + el->co->height) - co->top;
}
if ((el->co->top + el->co->height - co->top) > form->numRows) {
form->numRows = el->co->top + el->co->height - co->top;
}
}
co->top += form->vertOffset;
}
void newtFormRun(newtComponent co, struct newtExitStruct * es) {
struct form * form = co->data;
struct event ev;
struct eventResult er;
int key, i, max;
int done = 0;
fd_set readSet, writeSet, exceptSet;
struct timeval nextTimeout, now, timeout;
#ifdef USE_GPM
int x, y;
Gpm_Connect conn;
Gpm_Event event;
/* Set up GPM interface */
conn.eventMask = ~GPM_MOVE;
conn.defaultMask = GPM_MOVE;
conn.minMod = 0;
conn.maxMod = 0;
Gpm_Open(&conn, 0);
#endif
/* draw all of the components */
newtDrawForm(co);
if (form->currComp == -1) {
if (form->numComps)
gotoComponent(co, 0);
} else
gotoComponent(co, form->currComp);
while (!done) {
newtRefresh();
FD_ZERO(&readSet);
FD_ZERO(&writeSet);
FD_ZERO(&exceptSet);
FD_SET(0, &readSet);
#ifdef USE_GPM
if (gpm_fd > 0) {
FD_SET(gpm_fd, &readSet);
}
max = form->maxFd > gpm_fd ? form->maxFd : gpm_fd;
#else
max = form->maxFd;
#endif
for (i = 0; i < form->numFds; i++) {
if (form->fds[i].flags & NEWT_FD_READ)
FD_SET(form->fds[i].fd, &readSet);
if (form->fds[i].flags & NEWT_FD_WRITE)
FD_SET(form->fds[i].fd, &writeSet);
if (form->fds[i].flags & NEWT_FD_EXCEPT)
FD_SET(form->fds[i].fd, &exceptSet);
}
if (form->timer) {
/* Calculate when we next need to return with a timeout. Do
this inside the loop in case a callback resets the timer. */
gettimeofday(&now, 0);
if ((!form->lastTimeout.tv_sec && !form->lastTimeout.tv_usec) ||
now.tv_sec < form->lastTimeout.tv_sec ||
(now.tv_sec == form->lastTimeout.tv_sec &&
now.tv_usec < form->lastTimeout.tv_usec))
form->lastTimeout = now;
nextTimeout.tv_sec = form->lastTimeout.tv_sec +
(form->timer / 1000);
nextTimeout.tv_usec = form->lastTimeout.tv_usec +
(form->timer % 1000) * 1000;
if (now.tv_sec > nextTimeout.tv_sec) {
timeout.tv_sec = timeout.tv_usec = 0;
} else if (now.tv_sec == nextTimeout.tv_sec) {
timeout.tv_sec = 0;
if (now.tv_usec > nextTimeout.tv_usec)
timeout.tv_usec = 0;
else
timeout.tv_usec = nextTimeout.tv_usec - now.tv_usec;
} else if (now.tv_sec < nextTimeout.tv_sec) {
timeout.tv_sec = nextTimeout.tv_sec - now.tv_sec;
if (now.tv_usec > nextTimeout.tv_usec)
timeout.tv_sec--,
timeout.tv_usec = nextTimeout.tv_usec + 1000000 -
now.tv_usec;
else
timeout.tv_usec = nextTimeout.tv_usec - now.tv_usec;
}
} else {
timeout.tv_sec = timeout.tv_usec = 0;
}
if (needResize) {
needResize = 0;
newtResizeScreen(1);
/* The application may want to handle the resize */
for (i = 0; i < form->numHotKeys; i++) {
if (form->hotKeys[i] == NEWT_KEY_RESIZE) {
es->reason = NEWT_EXIT_HOTKEY;
es->u.key = NEWT_KEY_RESIZE;
done = 1;
break;
}
}
if (done)
break;
}
i = select(max + 1, &readSet, &writeSet, &exceptSet,
form->timer ? &timeout : NULL);
if (i < 0) continue; /* ?? What should we do here? */
if (i == 0) {
done = 1;
es->reason = NEWT_EXIT_TIMER;
gettimeofday(&form->lastTimeout, NULL);
} else
#ifdef USE_GPM
if (gpm_fd > 0 && FD_ISSET(gpm_fd, &readSet)) {
Gpm_GetEvent(&event);
if (event.type & GPM_DOWN) {
/* Transform coordinates to current window */
newtGetWindowPos(&x, &y);
ev.event = EV_MOUSE;
ev.u.mouse.type = MOUSE_BUTTON_DOWN;
ev.u.mouse.x = event.x - x - 1;
ev.u.mouse.y = event.y - y - 1;
/* Send the form the event */
er = sendEvent(co, ev);
if (er.result == ER_EXITFORM) {
done = 1;
es->reason = NEWT_EXIT_COMPONENT;
es->u.co = form->exitComp;
}
}
} else
#endif
{
if (FD_ISSET(0, &readSet)) {
key = newtGetKey();
for (i = 0; i < form->numHotKeys; i++) {
if (form->hotKeys[i] == key) {
es->reason = NEWT_EXIT_HOTKEY;
es->u.key = key;
done = 1;
break;
}
}
if (key == NEWT_KEY_F1 && form->helpTag && form->helpCb) {
if (form->currComp != -1) {
ev.event = EV_UNFOCUS;
sendEvent(form->elements[form->currComp].co, ev);
}
form->helpCb(co, form->helpTag);
if (form->currComp != -1) {
ev.event = EV_FOCUS;
sendEvent(form->elements[form->currComp].co, ev);
}
}
if (key == NEWT_KEY_ERROR) {
es->u.watch = -1;
es->reason = NEWT_EXIT_ERROR;
done = 1;
}
if (!done) {
ev.event = EV_KEYPRESS;
ev.u.key = key;
er = sendEvent(co, ev);
if (er.result == ER_EXITFORM) {
done = 1;
es->reason = NEWT_EXIT_COMPONENT;
es->u.co = form->exitComp;
}
}
} else {
for (i = 0; i < form->numFds; i++) {
if (((form->fds[i].flags & NEWT_FD_READ)
&& FD_ISSET(form->fds[i].fd, &readSet))
|| ((form->fds[i].flags & NEWT_FD_WRITE)
&& FD_ISSET(form->fds[i].fd, &writeSet))
|| ((form->fds[i].flags & NEWT_FD_EXCEPT)
&& FD_ISSET(form->fds[i].fd, &exceptSet))) break;
}
if(i < form->numFds)
es->u.watch = form->fds[i].fd;
else
es->u.watch = -1;
es->reason = NEWT_EXIT_FDREADY;
done = 1;
}
}
}
newtRefresh();
#ifdef USE_GPM
Gpm_Close();
#endif
}
static struct eventResult sendEvent(newtComponent co, struct event ev) {
struct eventResult er;
ev.when = EV_EARLY;
er = co->ops->event(co, ev);
if (er.result == ER_IGNORED) {
ev.when = EV_NORMAL;
er = co->ops->event(co, ev);
}
if (er.result == ER_IGNORED) {
ev.when = EV_LATE;
er = co->ops->event(co, ev);
}
return er;
}
static void gotoComponent(newtComponent co, int newComp) {
struct form * form = co->data;
struct event ev;
if (form->currComp != -1) {
ev.event = EV_UNFOCUS;
sendEvent(form->elements[form->currComp].co, ev);
}
form->currComp = newComp;
if (form->currComp != -1) {
ev.event = EV_FOCUS;
ev.when = EV_NORMAL;
sendEvent(form->elements[form->currComp].co, ev);
}
if (co->callback)
co->callback(co, co->callbackData);
}
void newtComponentAddCallback(newtComponent co, newtCallback f, void * data) {
co->callback = f;
co->callbackData = data;
}
/* Add a callback which is called when the component is destroyed. */
void newtComponentAddDestroyCallback(newtComponent co,
newtCallback f, void * data) {
co->destroyCallback = f;
co->destroyCallbackData = data;
}
void newtComponentTakesFocus(newtComponent co, int val) {
co->takesFocus = val;
}
void newtFormSetBackground(newtComponent co, int color) {
struct form * form = co->data;
form->background = color;
}
void newtFormWatchFd(newtComponent co, int fd, int fdFlags) {
struct form * form = co->data;
int i;
for (i = 0; i < form->numFds; i++)
if (form->fds[i].fd == fd)
break;
if(i >= form->numFds)
form->fds = realloc(form->fds, (++form->numFds) * sizeof(*form->fds));
form->fds[i].fd = fd;
form->fds[i].flags = fdFlags;
if (form->maxFd < fd) form->maxFd = fd;
}
void newtSetHelpCallback(newtCallback cb) {
helpCallback = cb;
}