events.src Copyright (c) Kapteyn Laboratorium Groningen 1999 All Rights Reserved. Name: events.src Creator: terlouw Host: magellan Date: Aug 4, 1999 Contents: Makeevents AttachXt.dc2 DescheduleAll.dc3 ScheduleTimer.dc3 ConnectTk.dc3 MainLoop.dc2 ScheduleWrite.dc3 Deschedule.dc3 ScheduleRead.dc3 ScheduleX.dc2 events.dc3 events.h eventsxt.h eventcom.H events.c eventsxt.c BreakMainLoop.dc2 #> Makeevents # # Makeevents # # Utility makefile for generating events.src : # make -f Makeevents pack SHELL = /bin/sh pack:: $$gip_sys/pack.csh events.src Makeevents \ AttachXt.dc2 DescheduleAll.dc3 ScheduleTimer.dc3 \ ConnectTk.dc3 MainLoop.dc2 ScheduleWrite.dc3 \ Deschedule.dc3 ScheduleRead.dc3 ScheduleX.dc2 \ events.dc3 events.h eventsxt.h \ eventcom.H events.c eventsxt.c \ BreakMainLoop.dc2 #< #> AttachXt.dc2 Function: AttachXt Purpose: Delegate event handling to X Toolkit. Category: SYSTEM File: events.src Author: J.P. Terlouw Use: #include "eventsxt.h" AttachXt(app_context); XtAppContext *app_context - an X Toolkit application context. Description: AttachXt delegates event handling and registration of functions to the corresponding Xt routines. It should be called before any of the other routines in this source. (See list of related documents) When AttachXt has been called, MainLoop can be called instead of XtAppMainLoop. Related Docs: events.dc3, ScheduleX.dc2, ScheduleRead.dc3, ScheduleWrite.dc3, ScheduleTimer.dc3, Deschedule.dc3, DescheduleAll.dc3, MainLoop.dc2. Updates: Apr 8, 1997: JPT Document created. #< #> DescheduleAll.dc3 Function: DescheduleAll Purpose: De-register all functions registered previously by one of the functions ScheduleRead, ScheduleWrite or ScheduleTimer. Category: SYSTEM File: events.src Author: J.P. Terlouw Use: #include "events.h" DescheduleAll(); Description: DescheduleAll removes all registrations of functions which have been registered by one of the routines ScheduleRead(), ScheduleWrite() or ScheduleTimer(). This normally causes MainLoop to return, except in the case when event handling has been delegated to the X Toolkit by calling AttachXt. Related Docs: events.dc3, ScheduleRead.dc3, ScheduleWrite.dc3, ScheduleTimer.dc3, Deschedule.dc3, DescheduleAll.dc3, MainLoop.dc2, AttachXt.dc2. Updates: Apr 8, 1997: JPT Document created. #< #> ScheduleTimer.dc3 Function: ScheduleTimer Purpose: Register a function to be called whenever a specified time interval has elapsed. Category: SYSTEM File: events.src Author: J.P. Terlouw Use: #include "events.h" ident id; id = ScheduleTimer(proc, interval, arg); void(*proc)() - pointer to function. int interval - time interval in milliseconds. void *arg - 'client' data Description: ScheduleTimer registers a function to be called by MainLoop whenever a specified interval in milliseconds has expired. Until it is descheduled, 'proc' will be called every 'interval' milliseconds. In the call 'arg' is passed as an argument. The return value is a unique identification code corresponding with the registration, which will also be passed as an argument to 'proc'. The prototype of 'proc' is: void proc(ident id, void arg); The header file contains a number of convenient definitions for units of time, e.g., "5 MINUTES" is equivalent to 300000 milliseconds. See events.h. Warning: this software resembles the old Patriot missile control software: 24 days, 20 hours, 31 minutes and 23.647 seconds after starting MainLoop, the internal timer will overflow. Related Docs: events.dc3, ScheduleWrite.dc3, ScheduleRead.dc3, Deschedule.dc3, DescheduleAll.dc3, MainLoop.dc2, AttachXt.dc2. Updates: Apr 8, 1997: JPT Document created. #< #> ConnectTk.dc3 Function: ConnectTk Purpose: Connect toolkit to event handling. Category: SYSTEM File: events.src Author: J.P. Terlouw Use: Private. Not to be called from outside events.src. Updates: Apr 8, 1997: JPT Document created. #< #> MainLoop.dc2 Function: MainLoop Purpose: Dispatcher for event-driven programs. Category: SYSTEM File: events.src Author: J.P. Terlouw Use: #include "events.h" MainLoop(); Description: MainLoop serves as the nucleus of event-driven programs. Whenever necessary it calls the functions which have been registered before by ScheduleRead(), ScheduleWrite() or ScheduleTimer(). Normally it returns when all registered functions have been descheduled. This is not the case when event handling has been delegated to the X Toolkit by calling AttachXt. In this case MainLoop() can be forced to return when BreakMainLoop() is called. This is only useful when MainLoop() was called recursively. Related Docs: events.dc3, ScheduleRead.dc3, ScheduleWrite.dc3, ScheduleTimer.dc3, Deschedule.dc3, DescheduleAll.dc3, AttachXt.dc2, BreakMainLoop.dc2. Updates: Apr 8, 1997: JPT Document created. #< #> ScheduleWrite.dc3 Function: ScheduleWrite Purpose: Register a function to be called whenever data can be written to a specified file descriptor. Category: SYSTEM File: events.src Author: J.P. Terlouw Use: #include "events.h" ident id; id = ScheduleWrite(proc, fd, arg); void(*proc)() - pointer to function. int fd - file descriptor. void *arg - 'client' data Description: ScheduleWrite registers a function to be called by MainLoop whenever data can be written to file descriptor 'fd'. In this call, 'arg' will be passed to 'proc'. The return value is a unique identification code corresponding with the registration, which will also be passed as an argument to 'proc'. The prototype of 'proc' is: void proc(ident id, int fd, void *arg); This function can be used when the calling program should not "block" when data cannot immediately be written, e.g., when writing a large amount of data to a socket when it is not sure that the receiving process immediately can accept it. Related Docs: events.dc3, ScheduleRead.dc3, ScheduleTimer.dc3, Deschedule.dc3, DescheduleAll.dc3, MainLoop.dc2, AttachXt.dc2. Updates: Apr 8, 1997: JPT Document created. #< #> Deschedule.dc3 Function: Deschedule Purpose: De-register a function registered previously by one of the functions ScheduleRead, ScheduleWrite or ScheduleTimer. Category: SYSTEM File: events.src Author: J.P. Terlouw Use: #include "events.h" ident id; Deschedule(&id); Description: The function associated with 'id' is descheduled and 'id' is set to zero. (Note that the argument is a pointer to 'id'.) Related Docs: events.h ScheduleRead.dc3, ScheduleWrite.dc3, ScheduleTimer.dc3, DescheduleAll.dc3, MainLoop.dc2, AttachXt.dc2. Updates: Apr 8, 1997: JPT Document created. #< #> ScheduleRead.dc3 Function: ScheduleRead Purpose: Register a function to be called whenever data is available on a specified file descriptor. Category: SYSTEM File: events.src Author: J.P. Terlouw Use: #include "events.h" ident id; id = ScheduleRead(proc, fd, arg); void(*proc)() - pointer to function. int fd - file descriptor. void *arg - 'client' data Description: ScheduleRead registers a function to be called by MainLoop whenever data can be read from file descriptor 'fd'. In this call, 'arg' will be passed to 'proc'. The return value is a unique identification code corresponding with the registration, which will also be passed as an argument to 'proc'. The prototype of 'proc' is: void proc(ident id, int fd, void *arg); Related Docs: events.dc3, ScheduleWrite.dc3, ScheduleTimer.dc3, Deschedule.dc3, DescheduleAll.dc3, MainLoop.dc2, AttachXt.dc2. Updates: Apr 8, 1997: JPT Document created. #< #> ScheduleX.dc2 Function: ScheduleX Purpose: Register or de-register function receiving X events. Category: SYSTEM File: events.src Author: J.P. Terlouw Use: #include "eventsxt.h" ScheduleX(handler); handler - the function to receive the events, or NULL if a function is to be de-registered. Prototype: void handler(XEvent *event); Description: This function enables the programmer to receive X11 events which are not dispatched by the X Toolkit, e.g. events from a separate window not managed by Xt. Related Docs: events.dc3, AttachXt.dc2, ScheduleRead.dc3, ScheduleWrite.dc3, ScheduleTimer.dc3, Deschedule.dc3, DescheduleAll.dc3, MainLoop.dc2. Updates: Apr 22, 1997: JPT Document created. #< #> events.dc3 Document: events Purpose: Describes routines for event-driven programs. Category: SYSTEM File: events.src Author: J.P. Terlouw Description: The eventhandler routines allow a program to specify ("schedule") one or more functions to be called when a specified event occurs. An event can be one of the following: - data can be read from a specified file descriptor; - data can be written to a specified file descriptor; - a specified period of time has elapsed. The following elementary routines are available: ScheduleRead - Schedule function for reading. ScheduleWrite - Schedule function for writing. ScheduleTimer - Schedule function to be called periodically. Deschedule - Deschedule function. DescheduleAll - Deschedule all scheduled functions. MainLoop - Wait for events and call scheduled functions. BreakMainLoop - Terminate current MainLoop invocation. The following function is related to the X toolkit: AttachXt - Required when program uses the X toolkit. Each routine is described in the appropriate document. Example: The following program writes every second a dash to the screen. It does this 10 times. During the same time it also reads characters from the keyboard and writes these to the screen. When a dot has been typed, it stops looking for more characters. #include "stddef.h" #include "stdio.h" #include #include #include "events.h" void timefun(ident id, void *arg) { static int count=10; write(1,(char*)arg,1); count--; if (!count) Deschedule(&id); /* deschedule after 10 dashes */ } void readfun(ident id, int fd, void *arg) { char c; read(0,&c,1); if (c=='.') Deschedule(&id); /* deschedule when dot seen */ write(1,&c,1); /* echo character read */ } void main(void) { struct sgttyb tt; ioctl(0,TIOCGETP,&tt); tt.sg_flags |= RAW; tt.sg_flags &= ~ECHO; ioctl(0,TIOCSETP,&tt); /* set modes for keyboard input */ (void)ScheduleRead(readfun, 0, NULL); /* read from stdin */ (void)ScheduleTimer(timefun, 1000, "-"); /* every 1000 ms */ MainLoop(); /* handle events */ write(1,"\n",1); /* all functions descheduled - MainLoop returns */ } Related Docs: ScheduleRead.dc3, ScheduleWrite.dc3, ScheduleTimer.dc3, Deschedule.dc3, DescheduleAll.dc3, MainLoop.dc2 BreakMainLoop.dc2, AttachXt.dc2, ScheduleX.dc2 Still to do: This software would also be a logical place to handle signals and exceptional conditions on file descriptors. Updates: Apr 8, 1997: JPT, Document created. Apr 22, 1997: JPT, ScheduleX added. Aug 4, 1999: JPT, BreakMainLoop added. #< #> events.h #if !defined(_events_h_) #define _events_h_ #define MS *1 #define SECONDS *1000 MS #define MINUTES *60 SECONDS #define HOURS *60 MINUTES #define DAYS *24 HOURS #define WEEKS *7 DAYS #define SECOND SECONDS #define MINUTE MINUTES #define HOUR HOURS #define DAY DAYS #define WEEK WEEKS typedef void *ident; extern void MainLoop(void); extern ident ScheduleRead(void(*)(),int,void*); extern ident ScheduleWrite(void(*)(),int,void*); extern ident ScheduleTimer(void(*)(),int,void*); extern void Deschedule(ident*); extern void DescheduleAll(void); extern void BreakMainLoop(void); #endif #< #> eventsxt.h #if !defined(_eventsxt_h_) #define _eventsxt_h_ #include "events.h" #include extern void AttachXt(XtAppContext *app_context); extern void ScheduleX(void(*)()); #endif #< #> eventcom.H #define INVALID 0 #define READ 1 #define WRITE 2 #define TIMER 3 #define New(type) ((type *)calloc(1,sizeof(type))) #define NNew(n,type) ((type *)calloc(n,sizeof(type))) #define Delete(x) {free(x); x=NULL;} #define Milliseconds(x) (x.tv_sec*1000+x.tv_usec/1000) typedef struct _Callback { unsigned long tkident; /* (X-) toolkit-supplied ident */ int type; /* service type */ int fd; /* file descriptor */ int interval; /* interval for periodic tasks (ms) */ int calltime; /* 'absolute' time for next call (ms) */ void (*proc)(); /* procedure pointer */ void *arg; /* argument to procedure */ struct _Callback *next; /* link to next entry */ } *Callback, _Callback; typedef struct { void (*schedule)(Callback cb); void (*deschedule)(Callback cb); void (*mainloop)(void); } *TkConnection, _TkConnection; extern void ConnectTk(TkConnection); #< #> events.c /* events.c COPYRIGHT (c) 1997 Kapteyn Astronomical Institute University of Groningen - 9700 AV Groningen, The Netherlands Author: J.P. Terlouw. */ #include "stddef.h" #include #if defined(__aix__) #include #endif #include #include "events.h" #include "eventcom.H" #define PERIOD (1 WEEK) /* default delay */ static Callback cb_active=NULL; static fd_set readfds, writefds; static int maxfds=0; static int n_active=0; static int delay=PERIOD; static int secstart=0; static TkConnection connection=NULL; /* connection to (X-) toolkit */ static ident Schedule(int type, void(*proc)(), int fd, int interval, void *arg); /* ========================================================================== */ /* ConnectTk */ /* -------------------------------------------------------------------------- */ /* * ConnectTk registers a toolkit-provided struct containing pointers to * functions which take over the responsibility of scheduling timer actions * and actions on file descriptors. It is currently only used to connect * the X-toolkit to Mainloop. */ extern void ConnectTk(TkConnection c) { connection = c; } /* ========================================================================== */ /* MainLoop */ /* -------------------------------------------------------------------------- */ /* * MainLoop serves as the nucleus of event-driven programs. * Whenever necessary it calls the functions which have been registered * before by ScheduleRead(), ScheduleWrite() or ScheduleTimer(). */ extern void MainLoop(void) { Callback cb, *cbp, *next; struct timeval timeout, now; int msnow; if (connection) { connection->mainloop(); return; } while (n_active) { timeout.tv_sec = delay/1000; timeout.tv_usec = delay%1000*1000; while (select(maxfds+1,(void*)&readfds, (void*)&writefds,NULL,&timeout)==-1) {;} delay = PERIOD; /* set next timeout to maximum */ for (cbp=&cb_active; *cbp; cbp=next) { cb = *cbp; next = &cb->next; if (cb->type==INVALID) { next = cbp; /* invalidated block; delete */ *cbp = cb->next; Delete(cb); } else if (cb->type==READ) { if (FD_ISSET(cb->fd,&readfds)) { cb->proc(cb,cb->fd,cb->arg); /* file descriptor callback */ } if (cb->type!=INVALID) FD_SET(cb->fd,&readfds); /* re-set if still valid */ } else if (cb->type==WRITE) { if (FD_ISSET(cb->fd,&writefds)) { cb->proc(cb,cb->fd,cb->arg); /* file descriptor callback */ } if (cb->type!=INVALID) FD_SET(cb->fd,&writefds); /* re-set if still valid */ } else { gettimeofday(&now,NULL); now.tv_sec -= secstart; /* avoid overflow in ms int's */ msnow = Milliseconds(now); if (msnow >= cb->calltime) { cb->proc(cb,cb->arg); /* timer callback */ if (cb->type!=INVALID) { cb->calltime = msnow + cb->interval; if (delay > cb->interval) delay = cb->interval; /* trim */ } } else if (delay > (cb->calltime-msnow)) { delay = cb->calltime - msnow; /* trim */ } } } } } /* ========================================================================== */ /* ScheduleRead */ /* -------------------------------------------------------------------------- */ /* * ScheduleRead registers a function to be called by MainLoop whenever * data can be read from file descriptor 'fd'. In this call, 'arg' will be * passed to 'proc'. The return value is a unique identification code * corresponding with the registration, which will also be passed as an * argument to 'proc'. * * The prototype of 'proc' is: * void proc(ident id, int fd, void *arg); */ extern ident ScheduleRead(void(*proc)(), int fd, void *arg) { return Schedule(READ,proc,fd,0,arg); } /* ========================================================================== */ /* ScheduleWrite */ /* -------------------------------------------------------------------------- */ /* * ScheduleWrite registers a function to be called by MainLoop whenever * data can be written to file descriptor 'fd'. In this call, 'arg' will be * passed to 'proc'. The return value is a unique identification code * corresponding with the registration, which will also be passed as an * argument to 'proc'. * * The prototype of 'proc' is: * void proc(ident id, int fd, void *arg); */ extern ident ScheduleWrite(void(*proc)(), int fd, void *arg) { return Schedule(WRITE,proc,fd,0,arg); } /* ========================================================================== */ /* ScheduleTimer */ /* -------------------------------------------------------------------------- */ /* ScheduleTimer registers a function to be called by MainLoop whenever * a specified interval in milliseconds has expired. Until it is descheduled, * 'proc' will be called every 'interval' milliseconds. In the call 'arg' is * passed as an argument. The return value is a unique identification code * corresponding with the registration, which will also be passed as an * argument to 'proc'. * * The prototype of 'proc' is: * void proc(ident id, void *arg); * * WARNING: this software resembles the old Patriot missile control software: * 24 days, 20 hours, 31 minutes and 23.647 seconds after starting MainLoop, * the internal timer will overflow. */ extern ident ScheduleTimer(void(*proc)(), int interval, void *arg) { return Schedule(TIMER,proc,0,interval,arg); } /* ========================================================================== */ /* Deschedule */ /* -------------------------------------------------------------------------- */ /* Deschedule removes the registration of a function which has been * registered by one of the routines ScheduleRead(), ScheduleWrite() or * ScheduleTimer(). The ident to which the argument points is set to NULL. */ extern void Deschedule(ident *id) { Callback cb=(Callback)*id; /* convert id to callback pointer*/ if (!*id) return; /* not scheduled: no-op */ *id = NULL; if (cb->type==READ) { FD_CLR(cb->fd,&readfds); /* clear fd flag */ }else if (cb->type==WRITE) { FD_CLR(cb->fd,&writefds); /* clear fd flag */ } if (connection) { connection->deschedule(cb); Delete(cb); /* The callback block is deleted immediately. This is possible because * there is no linked list traversal when a toolkit is connected. */ } else { cb->type = INVALID; /* invalidate for MainLoop */ } n_active--; } /* ========================================================================== */ /* DescheduleAll */ /* -------------------------------------------------------------------------- */ /* DescheduleAll removes all registrations of functions which have been * registered by one of the routines ScheduleRead(), ScheduleWrite() or * ScheduleTimer(). This causes MainLoop to return. */ extern void DescheduleAll(void) { Callback cb; if (connection) return; /* no-op when Xt is used */ for (cb=cb_active; cb; cb=cb->next) { if (cb->type != INVALID) { if (cb->type==READ) { FD_CLR(cb->fd,&readfds); }else if (cb->type==WRITE) { FD_CLR(cb->fd,&writefds); } cb->type = INVALID; n_active--; } } } /* -------------------------------------------------------------------------- */ /* Schedule */ /* -------------------------------------------------------------------------- */ /* Schedule() registers any kind of function. Its first argument is the * kind of registration that is wanted. */ static ident Schedule(int type, void(*proc)(), int fd, int interval, void *arg) { Callback cb; cb = New(_Callback); /* allocate a new entry */ cb->next= cb_active; cb_active = cb; cb->type = type; cb->fd = fd; /* store parameters */ cb->interval = interval; cb->proc = proc; cb->arg = arg; if (fd>maxfds) maxfds=fd; /* update maximum file descriptor value */ if (type==READ) { FD_SET(fd,&readfds); /* set flag in select() structure */ } else if (type==WRITE) { FD_SET(fd,&writefds); } else { struct timeval now; gettimeofday(&now,NULL); if (!secstart) { secstart = now.tv_sec; } now.tv_sec -= secstart; cb->calltime = Milliseconds(now) + interval; if (delay>interval) delay = interval; } n_active++; if (connection) connection->schedule(cb); return (ident)cb; /* use pointer to callback entry as ID */ } #< #> eventsxt.c /* eventsxt.c -X COPYRIGHT (c) 1997 Kapteyn Astronomical Institute University of Groningen - 9700 AV Groningen, The Netherlands */ #include #include "eventcom.H" #include "eventsxt.h" static XtAppContext *context; static void (*XHandler)()=NULL; /* handler for non-Xt events */ static int depth=0; /* mainloop recursion depth */ /* -------------------------------------------------------------------------- */ /* icbproc */ /* -------------------------------------------------------------------------- */ /* X Toolkit input callback function. */ static void icbproc(XtPointer arg, int *fd, XtInputId *id) { Callback cb=(Callback)arg; cb->proc(cb,cb->fd,cb->arg); } /* -------------------------------------------------------------------------- */ /* tcbproc */ /* -------------------------------------------------------------------------- */ /* X Toolkit timeout callback function. */ static void tcbproc(XtPointer arg, XtIntervalId *id) { Callback cb=(Callback)arg; cb->tkident = XtAppAddTimeOut(*context, cb->interval, tcbproc, arg); cb->proc(cb, cb->arg); } /* -------------------------------------------------------------------------- */ /* schedule */ /* -------------------------------------------------------------------------- */ /* X Toolkit compatible replacement for mainloop schedule function. */ static void schedule(Callback cb) { if (cb->type==READ) { cb->tkident = XtAppAddInput(*context, cb->fd, (XtPointer)XtInputReadMask, icbproc, (XtPointer)cb); } else if (cb->type==WRITE) { cb->tkident = XtAppAddInput(*context, cb->fd, (XtPointer)XtInputWriteMask, icbproc, (XtPointer)cb); } else { cb->tkident = XtAppAddTimeOut(*context, cb->interval, tcbproc, (XtPointer)cb); } } /* -------------------------------------------------------------------------- */ /* deschedule */ /* -------------------------------------------------------------------------- */ /* X Toolkit compatible replacement for mainloop deschedule function. */ static void deschedule(Callback cb) { if (cb->type==TIMER) { XtRemoveTimeOut(cb->tkident); } else { XtRemoveInput(cb->tkident); } } /* -------------------------------------------------------------------------- */ /* mainloop */ /* -------------------------------------------------------------------------- */ /* X Toolkit compatible replacement for mainloop. */ static void mainloop(void) { XEvent event; int curdep; curdep = ++depth; while (curdep==depth) { XtAppNextEvent(*context, &event); if (!XtDispatchEvent(&event) && XHandler) { XHandler(&event); } } } /* ========================================================================== */ /* AttachXt */ /* -------------------------------------------------------------------------- */ /* Attach X Toolkit to event handler. */ extern void AttachXt(XtAppContext *app_context) { static TkConnection connection=NULL; if (connection) return; /* redundant attach: no-op */ context = app_context; connection = New(_TkConnection); connection->schedule = schedule; connection->deschedule = deschedule; connection->mainloop = mainloop; ConnectTk(connection); } /* ========================================================================== */ /* ScheduleX */ /* -------------------------------------------------------------------------- */ /* Register function to receive events not dispatched by the X Toolkit. */ extern void ScheduleX(void(*handler)()) { XHandler = handler; } /* ========================================================================== */ /* BreakMainLoop */ /* -------------------------------------------------------------------------- */ /* Decrement recursion depth, eventually causing mainloop() to return. */ extern void BreakMainLoop(void) { depth--; } #< #> BreakMainLoop.dc2 Function: BreakMainLoop Purpose: Terminate invocation of MainLoop() Category: SYSTEM File: events.src Author: J.P. Terlouw Use: #include "events.h" BreakMainLoop(); Description: BreakMainLoop causes the current, possibly recursive, invocation of MaiLoop() to be terminated. This only works when event handling has been delegated to the X Toolkit, which is always the case for programs using GIPSY's graphical user interface (Ggi). Related Doc: MainLoop.dc2 Updates: Aug 4, 1999: JPT Document created. #<