]> www.wagner.pp.ru Git - oss/ck.git/blob - ckEvent.c
Ck console graphics toolkit
[oss/ck.git] / ckEvent.c
1 /* 
2  * ckEvent.c --
3  *
4  *      This file provides basic event-managing facilities,
5  *      whereby procedure callbacks may be attached to
6  *      certain events.
7  *
8  * Copyright (c) 1990-1994 The Regents of the University of California.
9  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
10  * Copyright (c) 1995-1999 Christian Werner
11  *
12  * See the file "license.terms" for information on usage and redistribution
13  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
14  */
15
16 #include "ckPort.h"
17 #include "ck.h"
18
19 #ifdef HAVE_GPM
20 #include "gpm.h"
21 #endif
22
23 /*
24  * There's a potential problem if a handler is deleted while it's
25  * current (i.e. its procedure is executing), since Ck_HandleEvent
26  * will need to read the handler's "nextPtr" field when the procedure
27  * returns.  To handle this problem, structures of the type below
28  * indicate the next handler to be processed for any (recursively
29  * nested) dispatches in progress.  The nextHandler fields get
30  * updated if the handlers pointed to are deleted.  Ck_HandleEvent
31  * also needs to know if the entire window gets deleted;  the winPtr
32  * field is set to zero if that particular window gets deleted.
33  */
34
35 typedef struct InProgress {
36     CkEvent *eventPtr;           /* Event currently being handled. */
37     CkWindow *winPtr;            /* Window for event.  Gets set to NULL if
38                                   * window is deleted while event is being
39                                   * handled. */
40     CkEventHandler *nextHandler; /* Next handler in search. */
41     struct InProgress *nextPtr;  /* Next higher nested search. */
42 } InProgress;
43
44 static InProgress *pendingPtr = NULL;
45                                 /* Topmost search in progress, or
46                                  * NULL if none. */
47
48 /*
49  * For each call to Ck_CreateGenericHandler, an instance of the following
50  * structure will be created.  All of the active handlers are linked into a
51  * list.
52  */
53
54 typedef struct GenericHandler {
55     Ck_GenericProc *proc;       /* Procedure to dispatch on all events. */
56     ClientData clientData;      /* Client data to pass to procedure. */
57     int deleteFlag;             /* Flag to set when this handler is deleted. */
58     struct GenericHandler *nextPtr;
59                                 /* Next handler in list of all generic
60                                  * handlers, or NULL for end of list. */
61 } GenericHandler;
62
63 static GenericHandler *genericList = NULL;
64                                 /* First handler in the list, or NULL. */
65 static GenericHandler *lastGenericPtr = NULL;
66                                 /* Last handler in list. */
67
68 /*
69  * There's a potential problem if Ck_HandleEvent is entered recursively.
70  * A handler cannot be deleted physically until we have returned from
71  * calling it.  Otherwise, we're looking at unallocated memory in advancing to
72  * its `next' entry.  We deal with the problem by using the `delete flag' and
73  * deleting handlers only when it's known that there's no handler active.
74  *
75  * The following variable has a non-zero value when a handler is active.
76  */
77
78 static int genericHandlersActive = 0;
79
80 /*
81  * For barcode readers an instance of the following structure is linked
82  * to mainInfo. The supported DATA LOGIC barcode readers are connected
83  * between PC keyboard and PC keyboard controller and generate a data
84  * packet surrounded by start and end characters. If the start character
85  * is received a timer is started and the following keystrokes are
86  * collected into the buffer until the end character is received or the
87  * timer expires.
88  */
89
90 #define DEFAULT_BARCODE_TIMEOUT 1000
91
92 typedef struct barcodeData {
93     Tk_TimerToken timer;/* Barcode packet timer. */
94     int pkttime;        /* Timeout value. */
95     int startChar;      /* Start of barcode packet character. */
96     int endChar;        /* End of barcode packet character. */
97     int delivered;      /* BarCode event has been delivered. */
98     int index;          /* Current index into buffer. */
99 #if CK_USE_UTF
100     char buffer[256];   /* Here the barcode packet is assembled. */
101 #else
102     char buffer[128];   /* Here the barcode packet is assembled. */
103 #endif
104 } BarcodeData;
105
106 /*
107  * Timeout procedure for reading barcode packet:
108  */
109
110 static void BarcodeTimeout _ANSI_ARGS_((ClientData clientData));
111 \f
112 /*
113  *--------------------------------------------------------------
114  *
115  * Ck_CreateEventHandler --
116  *
117  *      Arrange for a given procedure to be invoked whenever
118  *      events from a given class occur in a given window.
119  *
120  * Results:
121  *      None.
122  *
123  * Side effects:
124  *      From now on, whenever an event of the type given by
125  *      mask occurs for token and is processed by Ck_HandleEvent,
126  *      proc will be called.  See the manual entry for details
127  *      of the calling sequence and return value for proc.
128  *
129  *--------------------------------------------------------------
130  */
131
132 void
133 Ck_CreateEventHandler(winPtr, mask, proc, clientData)
134     CkWindow *winPtr;           /* Window in which to create handler. */
135     long mask;                  /* Events for which proc should be called. */
136     Ck_EventProc *proc;         /* Procedure to call for each
137                                  * selected event */
138     ClientData clientData;      /* Arbitrary data to pass to proc. */
139 {
140     CkEventHandler *handlerPtr;
141     int found;
142
143     /*
144      * Skim through the list of existing handlers to see if there's
145      * already a handler declared with the same callback and clientData
146      * (if so, just change the mask).  If no existing handler matches,
147      * then create a new handler.
148      */
149
150     found = 0;
151     if (winPtr->handlerList == NULL) {
152         handlerPtr = (CkEventHandler *) ckalloc(sizeof (CkEventHandler));
153         winPtr->handlerList = handlerPtr;
154         goto initHandler;
155     } else {
156         for (handlerPtr = winPtr->handlerList; ;
157                 handlerPtr = handlerPtr->nextPtr) {
158             if ((handlerPtr->proc == proc)
159                     && (handlerPtr->clientData == clientData)) {
160                 handlerPtr->mask = mask;
161                 found = 1;
162             }
163             if (handlerPtr->nextPtr == NULL) {
164                 break;
165             }
166         }
167     }
168
169     /*
170      * Create a new handler if no matching old handler was found.
171      */
172
173     if (!found) {
174         handlerPtr->nextPtr = (CkEventHandler *) ckalloc(
175             sizeof (CkEventHandler));
176         handlerPtr = handlerPtr->nextPtr;
177 initHandler:
178         handlerPtr->mask = mask;
179         handlerPtr->proc = proc;
180         handlerPtr->clientData = clientData;
181         handlerPtr->nextPtr = NULL;
182     }
183 }
184 \f
185 /*
186  *--------------------------------------------------------------
187  *
188  * Ck_DeleteEventHandler --
189  *
190  *      Delete a previously-created handler.
191  *
192  * Results:
193  *      None.
194  *
195  * Side effects:
196  *      If there existed a handler as described by the
197  *      parameters, the handler is deleted so that proc
198  *      will not be invoked again.
199  *
200  *--------------------------------------------------------------
201  */
202
203 void
204 Ck_DeleteEventHandler(winPtr, mask, proc, clientData)
205     CkWindow *winPtr;           /* Same as corresponding arguments passed */
206     long mask;                  /* previously to Ck_CreateEventHandler. */
207     Ck_EventProc *proc;
208     ClientData clientData;
209 {
210     CkEventHandler *handlerPtr;
211     InProgress *ipPtr;
212     CkEventHandler *prevPtr;
213
214     /*
215      * Find the event handler to be deleted, or return
216      * immediately if it doesn't exist.
217      */
218
219     for (handlerPtr = winPtr->handlerList, prevPtr = NULL; ;
220             prevPtr = handlerPtr, handlerPtr = handlerPtr->nextPtr) {
221         if (handlerPtr == NULL) {
222             return;
223         }
224         if ((handlerPtr->mask == mask) && (handlerPtr->proc == proc)
225                 && (handlerPtr->clientData == clientData)) {
226             break;
227         }
228     }
229
230     /*
231      * If Ck_HandleEvent is about to process this handler, tell it to
232      * process the next one instead.
233      */
234
235     for (ipPtr = pendingPtr; ipPtr != NULL; ipPtr = ipPtr->nextPtr) {
236         if (ipPtr->nextHandler == handlerPtr) {
237             ipPtr->nextHandler = handlerPtr->nextPtr;
238         }
239     }
240
241     /*
242      * Free resources associated with the handler.
243      */
244
245     if (prevPtr == NULL) {
246         winPtr->handlerList = handlerPtr->nextPtr;
247     } else {
248         prevPtr->nextPtr = handlerPtr->nextPtr;
249     }
250     ckfree((char *) handlerPtr);
251 }
252 \f
253 /*--------------------------------------------------------------
254  *
255  * Ck_CreateGenericHandler --
256  *
257  *      Register a procedure to be called on each event, regardless
258  *      of window.  Generic handlers are useful for capturing
259  *      events that aren't associated with windows, or events for windows
260  *      not managed by Ck.
261  *
262  * Results:
263  *      None.
264  *
265  * Side Effects:
266  *      From now on, whenever an event is given to Ck_HandleEvent,
267  *      invoke proc, giving it clientData and the event as arguments.
268  *
269  *--------------------------------------------------------------
270  */
271
272 void
273 Ck_CreateGenericHandler(proc, clientData)
274      Ck_GenericProc *proc;      /* Procedure to call on every event. */
275      ClientData clientData;     /* One-word value to pass to proc. */
276 {
277     GenericHandler *handlerPtr;
278     
279     handlerPtr = (GenericHandler *) ckalloc (sizeof (GenericHandler));
280     
281     handlerPtr->proc = proc;
282     handlerPtr->clientData = clientData;
283     handlerPtr->deleteFlag = 0;
284     handlerPtr->nextPtr = NULL;
285     if (genericList == NULL) {
286         genericList = handlerPtr;
287     } else {
288         lastGenericPtr->nextPtr = handlerPtr;
289     }
290     lastGenericPtr = handlerPtr;
291 }
292 \f
293 /*
294  *--------------------------------------------------------------
295  *
296  * Ck_DeleteGenericHandler --
297  *
298  *      Delete a previously-created generic handler.
299  *
300  * Results:
301  *      None.
302  *
303  * Side Effects:
304  *      If there existed a handler as described by the parameters,
305  *      that handler is logically deleted so that proc will not be
306  *      invoked again.  The physical deletion happens in the event
307  *      loop in Ck_HandleEvent.
308  *
309  *--------------------------------------------------------------
310  */
311
312 void
313 Ck_DeleteGenericHandler(proc, clientData)
314      Ck_GenericProc *proc;
315      ClientData clientData;
316 {
317     GenericHandler * handler;
318     
319     for (handler = genericList; handler; handler = handler->nextPtr) {
320         if ((handler->proc == proc) && (handler->clientData == clientData)) {
321             handler->deleteFlag = 1;
322         }
323     }
324 }
325 \f
326 /*
327  *--------------------------------------------------------------
328  *
329  * Ck_HandleEvent --
330  *
331  *      Given an event, invoke all the handlers that have
332  *      been registered for the event.
333  *
334  * Results:
335  *      None.
336  *
337  * Side effects:
338  *      Depends on the handlers.
339  *
340  *--------------------------------------------------------------
341  */
342
343 void
344 Ck_HandleEvent(mainPtr, eventPtr)
345     CkMainInfo *mainPtr;
346     CkEvent *eventPtr;          /* Event to dispatch. */
347 {
348     CkEventHandler *handlerPtr;
349     GenericHandler *genericPtr;
350     GenericHandler *genPrevPtr;
351     CkWindow *winPtr;
352     InProgress ip;
353
354     /* 
355      * Invoke all the generic event handlers (those that are
356      * invoked for all events).  If a generic event handler reports that
357      * an event is fully processed, go no further.
358      */
359
360     for (genPrevPtr = NULL, genericPtr = genericList;  genericPtr != NULL; ) {
361         if (genericPtr->deleteFlag) {
362             if (!genericHandlersActive) {
363                 GenericHandler *tmpPtr;
364
365                 /*
366                  * This handler needs to be deleted and there are no
367                  * calls pending through the handler, so now is a safe
368                  * time to delete it.
369                  */
370
371                 tmpPtr = genericPtr->nextPtr;
372                 if (genPrevPtr == NULL) {
373                     genericList = tmpPtr;
374                 } else {
375                     genPrevPtr->nextPtr = tmpPtr;
376                 }
377                 if (tmpPtr == NULL) {
378                     lastGenericPtr = genPrevPtr;
379                 }
380                 (void) ckfree((char *) genericPtr);
381                 genericPtr = tmpPtr;
382                 continue;
383             }
384         } else {
385             int done;
386
387             genericHandlersActive++;
388             done = (*genericPtr->proc)(genericPtr->clientData, eventPtr);
389             genericHandlersActive--;
390             if (done) {
391                 return;
392             }
393         }
394         genPrevPtr = genericPtr;
395         genericPtr = genPrevPtr->nextPtr;
396     }
397
398     if (Tcl_FindHashEntry(&mainPtr->winTable, (char *) eventPtr->any.winPtr)
399         == NULL) {
400         /*
401          * There isn't a CkWindow structure for this window.
402          */
403         return;
404     }
405     winPtr = eventPtr->any.winPtr;
406     ip.eventPtr = eventPtr;
407     ip.winPtr = winPtr;
408     ip.nextHandler = NULL;
409     ip.nextPtr = pendingPtr;
410     pendingPtr = &ip;
411     for (handlerPtr = winPtr->handlerList; handlerPtr != NULL; ) {
412         if ((handlerPtr->mask & eventPtr->type) != 0) {
413             ip.nextHandler = handlerPtr->nextPtr;
414             (*(handlerPtr->proc))(handlerPtr->clientData, eventPtr);
415             handlerPtr = ip.nextHandler;
416         } else {
417             handlerPtr = handlerPtr->nextPtr;
418         }
419     }
420
421     /*
422      * Pass the event to the "bind" command mechanism.
423      */
424
425     CkBindEventProc(winPtr, eventPtr);
426
427     pendingPtr = ip.nextPtr;
428 }
429 \f
430 /*
431  *--------------------------------------------------------------
432  *
433  * CkEventDeadWindow --
434  *
435  *      This procedure is invoked when it is determined that
436  *      a window is dead.  It cleans up event-related information
437  *      about the window.
438  *
439  * Results:
440  *      None.
441  *
442  * Side effects:
443  *      Various things get cleaned up and recycled.
444  *
445  *--------------------------------------------------------------
446  */
447
448 void
449 CkEventDeadWindow(winPtr)
450     CkWindow *winPtr;           /* Information about the window
451                                  * that is being deleted. */
452 {
453     CkEventHandler *handlerPtr;
454     InProgress *ipPtr;
455
456     /*
457      * While deleting all the handlers, be careful to check for
458      * Ck_HandleEvent being about to process one of the deleted
459      * handlers.  If it is, tell it to quit (all of the handlers
460      * are being deleted).
461      */
462
463     while (winPtr->handlerList != NULL) {
464         handlerPtr = winPtr->handlerList;
465         winPtr->handlerList = handlerPtr->nextPtr;
466         for (ipPtr = pendingPtr; ipPtr != NULL; ipPtr = ipPtr->nextPtr) {
467             if (ipPtr->nextHandler == handlerPtr) {
468                 ipPtr->nextHandler = NULL;
469             }
470             if (ipPtr->winPtr == winPtr) {
471                 ipPtr->winPtr = NULL;
472             }
473         }
474         ckfree((char *) handlerPtr);
475     }
476 }
477 \f
478 /*
479  *--------------------------------------------------------------
480  *
481  * CkHandleInput --
482  *
483  *      Process keyboard events from curses.
484  *
485  * Results:
486  *      The return value is TK_FILE_HANDLED if the procedure
487  *      actually found an event to process.  If no event was found
488  *      then TK_READABLE is returned.
489  *
490  * Side effects:
491  *      The handling of the event could cause additional
492  *      side effects.
493  *
494  *--------------------------------------------------------------
495  */
496
497 #if (TCL_MAJOR_VERSION == 7) && (TCL_MINOR_VERSION <= 4)
498 int
499 CkHandleInput(clientData, mask, flags)
500     ClientData clientData;      /* Pointer to main info. */
501     int mask;                   /* OR-ed combination of the bits TK_READABLE,
502                                  * TK_WRITABLE, and TK_EXCEPTION, indicating
503                                  * current state of file. */
504     int flags;                  /* Flag bits passed to Tk_DoOneEvent;
505                                  * contains bits such as TK_DONT_WAIT,
506                                  * TK_X_EVENTS, Tk_FILE_EVENTS, etc. */
507 {
508     CkEvent event;
509     CkMainInfo *mainPtr = (CkMainInfo *) clientData;
510     int code;
511     static int buttonpressed = 0;
512     static int errCount = 0;
513
514     if (!(flags & TK_FILE_EVENTS))
515         return 0;
516
517     if (!(mask & TK_READABLE))
518         return TK_READABLE;
519
520     code = getch();
521     if (code == ERR) {
522         if (++errCount > 100) {
523             Tcl_Eval(mainPtr->interp, "exit 99");
524             exit(99);                   /* just in case */
525         }
526         return TK_READABLE;
527     }
528     errCount = 0;
529
530     /*
531      * Barcode reader handling.
532      */
533
534     if (mainPtr->flags & CK_HAS_BARCODE) {
535         BarcodeData *bd = (BarcodeData *) mainPtr->barcodeData;
536
537         /*
538          * Here, special handling for nested event loops:
539          * If BarCode event has been delivered already, we must
540          * reset the buffer index in order to get normal Key events.
541          */
542         if (bd->delivered && bd->index >= 0) {
543             bd->delivered = 0;
544             bd->index = -1;
545         }
546
547         if (bd->index >= 0 || code == bd->startChar) {
548             if (code == bd->startChar) {
549                 Tk_DeleteTimerHandler(bd->timer);
550                 bd->timer = Tk_CreateTimerHandler(bd->pkttime, BarcodeTimeout,
551                     (ClientData) mainPtr);
552                 bd->index = 0;
553             } else if (code == bd->endChar) {
554                 Tk_DeleteTimerHandler(bd->timer);
555                 bd->timer = (Tk_TimerToken) NULL;
556                 bd->delivered = 1;
557                 event.key.type = CK_EV_BARCODE;
558                 event.key.winPtr = mainPtr->focusPtr;
559                 event.key.keycode = 0;
560                 Ck_HandleEvent(mainPtr, &event);
561                 /*
562                  * Careful, event handler could turn barcode off.
563                  * Only reset buffer index if BarCode event delivered
564                  * flag is set.
565                  */
566                 bd = (BarcodeData *) mainPtr->barcodeData;
567                 if (bd != NULL && bd->delivered) {
568                     bd->delivered = 0;
569                     bd->index = -1;
570                 }
571                 return TK_FILE_HANDLED;
572             } else {
573                 /* Leave space for one NUL byte. */
574                 if (bd->index < sizeof (bd->buffer) - 1)
575                     bd->buffer[bd->index] = code;
576                 bd->index++;
577             }
578             return TK_READABLE;
579         }
580     }
581
582 #ifdef NCURSES_MOUSE_VERSION
583     /*
584      * ncurses-1.9.8a has builtin mouse support for at least xterm.
585      */
586
587     if (code == KEY_MOUSE) {
588         MEVENT mEvent;
589         int i;
590
591         if (mainPtr->flags & CK_MOUSE_XTERM) {
592             goto getMouse;
593         }
594
595         if (getmouse(&mEvent) == ERR)
596             return TK_FILE_HANDLED;
597
598         for (i = 1; i <= 3; i++) {
599             if (BUTTON_PRESS(mEvent.bstate, i)) {
600                 event.mouse.type = CK_EV_MOUSE_DOWN;
601                 goto mouseEventNC;
602             } else if (BUTTON_RELEASE(mEvent.bstate, i)) {
603                 event.mouse.type = CK_EV_MOUSE_UP;
604 mouseEventNC:
605                 event.mouse.button = i;
606                 event.mouse.x = mEvent.x;
607                 event.mouse.y = mEvent.y;
608                 event.mouse.winPtr = Ck_GetWindowXY(mainPtr, &event.mouse.x,
609                     &event.mouse.y, 1);
610                 Ck_HandleEvent(mainPtr, &event);
611                 return TK_FILE_HANDLED;
612             }
613         }
614     }
615 #endif
616
617 #if defined(__WIN32__) || defined(DJGPP)
618     if ((mainPtr->flags & CK_HAS_MOUSE) && code == KEY_MOUSE) {
619         int i;
620
621         request_mouse_pos();
622         for (i = 0; i < 3; i++) {
623             if (Mouse_status.button[i] == BUTTON_PRESSED) {
624                 event.mouse.type = CK_EV_MOUSE_DOWN;
625                 goto mouseEvt;
626             } else if (Mouse_status.button[i] == BUTTON_RELEASED) {
627                 event.mouse.type = CK_EV_MOUSE_UP;
628 mouseEvt:
629                 event.mouse.button = i + 1;
630                 event.mouse.x = Mouse_status.x;
631                 event.mouse.y = Mouse_status.y;
632                 event.mouse.winPtr = Ck_GetWindowXY(mainPtr, &event.mouse.x,
633                     &event.mouse.y, 1);
634                 Ck_HandleEvent(mainPtr, &event);
635                 return TK_FILE_HANDLED;
636             }
637         }
638     }
639 #endif 
640
641     /*
642      * Xterm mouse report handling: Although GPM has an xterm module
643      * this is separately done here, since I want to be as independent
644      * as possible from GPM.
645      * It is assumed that the entire mouse report comes in one piece
646      * ie without any delay between the 6 relevant characters.
647      * Only a single button down/up event is generated.
648      */
649
650 #if !defined(__WIN32__)&& !defined(DJGPP)
651     if ((mainPtr->flags & CK_MOUSE_XTERM) && (code == 0x1b || code == 0x9b)) {
652         int code2;
653
654         if (code == 0x9b)
655             goto getM;
656         code2 = getch();
657         if (code2 != ERR) {
658             if (code2 == '[')
659                 goto getM;
660             ungetch(code2);
661         } else
662             errCount++;
663         goto keyEvent;
664 getM:
665         code2 = getch();
666         if (code2 != ERR) {
667             if (code2 == 'M')
668                 goto getMouse;
669             ungetch(code2);
670         } else
671             errCount++;
672         goto keyEvent;
673 getMouse:
674         code2 = getch();
675         if (code2 == ERR) {
676             errCount++;
677             return TK_READABLE;
678         }
679         event.mouse.button = ((code2 - 0x20) & 0x03) + 1;
680         code2 = getch();
681         if (code2 == ERR) {
682             errCount++;
683             return TK_READABLE;
684         }
685         event.mouse.x = event.mouse.rootx = code2 - 0x20 - 1;
686         code2 = getch();
687         if (code2 == ERR) {
688             errCount++;
689             return TK_READABLE;
690         }
691         event.mouse.y = event.mouse.rooty = code2 - 0x20 - 1;
692         if (event.mouse.button > 3) {
693             event.mouse.button = buttonpressed;
694             buttonpressed = 0;
695             event.mouse.type = CK_EV_MOUSE_UP;
696             goto mouseEvent;
697         } else if (buttonpressed == 0) {
698             buttonpressed = event.mouse.button;
699             event.mouse.type = CK_EV_MOUSE_DOWN;
700 mouseEvent:
701             event.mouse.winPtr = Ck_GetWindowXY(mainPtr, &event.mouse.x,
702                 &event.mouse.y, 1);
703             Ck_HandleEvent(mainPtr, &event);
704             return TK_FILE_HANDLED;
705         }
706         return TK_READABLE;
707     }
708 #endif
709
710 keyEvent:
711     event.key.type = CK_EV_KEYPRESS;
712     event.key.winPtr = mainPtr->focusPtr;
713     event.key.keycode = code;
714     if (event.key.keycode < 0)
715         event.key.keycode &= 0xff;
716     Ck_HandleEvent(mainPtr, &event);
717     return TK_FILE_HANDLED;
718 }
719 #else
720 void
721 CkHandleInput(clientData, mask)
722     ClientData clientData;      /* Pointer to main info. */
723     int mask;                   /* OR-ed combination of the bits TK_READABLE,
724                                  * TK_WRITABLE, and TK_EXCEPTION, indicating
725                                  * current state of file. */
726 {
727     CkEvent event;
728     CkMainInfo *mainPtr = (CkMainInfo *) clientData;
729     int code;
730     static int buttonpressed = 0;
731     static int errCount = 0;
732
733     if (!(mask & TCL_READABLE))
734         return;
735
736     code = getch();
737     if (code == ERR) {
738         if (++errCount > 100) {
739             Tcl_Eval(mainPtr->interp, "exit 99");
740 #if (TCL_MAJOR_VERSION >= 8)
741             Tcl_Exit(99);                       /* just in case */
742 #else
743             exit(99);                           /* just in case */
744 #endif
745         }
746         return;
747     }
748     errCount = 0;
749
750     /*
751      * Barcode reader handling.
752      */
753
754     if (mainPtr->flags & CK_HAS_BARCODE) {
755         BarcodeData *bd = (BarcodeData *) mainPtr->barcodeData;
756
757         /*
758          * Here, special handling for nested event loops:
759          * If BarCode event has been delivered already, we must
760          * reset the buffer index in order to get normal Key events.
761          */
762         if (bd->delivered && bd->index >= 0) {
763             bd->delivered = 0;
764             bd->index = -1;
765         }
766
767         if (bd->index >= 0 || code == bd->startChar) {
768             if (code == bd->startChar) {
769                 Tk_DeleteTimerHandler(bd->timer);
770                 bd->timer = Tk_CreateTimerHandler(bd->pkttime, BarcodeTimeout,
771                     (ClientData) mainPtr);
772                 bd->index = 0;
773             } else if (code == bd->endChar) {
774                 Tk_DeleteTimerHandler(bd->timer);
775                 bd->timer = (Tk_TimerToken) NULL;
776                 bd->delivered = 1;
777                 event.key.type = CK_EV_BARCODE;
778                 event.key.winPtr = mainPtr->focusPtr;
779                 event.key.keycode = 0;
780                 Ck_HandleEvent(mainPtr, &event);
781                 /*
782                  * Careful, event handler could turn barcode off.
783                  * Only reset buffer index if BarCode event delivered
784                  * flag is set.
785                  */
786                 bd = (BarcodeData *) mainPtr->barcodeData;
787                 if (bd != NULL && bd->delivered) {
788                     bd->delivered = 0;
789                     bd->index = -1;
790                 }
791                 return;
792             } else {
793                 /* Leave space for one NUL byte. */
794                 if (bd->index < sizeof (bd->buffer) - 1) {
795 #if CK_USE_UTF
796                     char c, utfb[8];
797                     int numc, i;
798
799                     c = code;
800                     Tcl_ExternalToUtf(NULL, mainPtr->isoEncoding,
801                         &c, 1, 0, NULL, utfb, sizeof (utfb),
802                         NULL, &numc, NULL);
803                     if (bd->index + numc < sizeof (bd->buffer) - 1) {
804                         for (i = 0; i < numc; i++)
805                             bd->buffer[bd->index + i] = utfb[i];
806                     } else
807                         bd->buffer[bd->index] = '\0';
808                     bd->index += numc - 1;
809 #else
810                     bd->buffer[bd->index] = code;
811 #endif
812                 }
813                 bd->index++;
814             }
815             return;
816         }
817     }
818
819 #ifdef NCURSES_MOUSE_VERSION
820     /*
821      * ncurses-1.9.8a has builtin mouse support for at least xterm.
822      */
823
824     if (code == KEY_MOUSE) {
825         MEVENT mEvent;
826         int i;
827
828         if (mainPtr->flags & CK_MOUSE_XTERM) {
829             goto getMouse;
830         }
831
832         if (getmouse(&mEvent) == ERR)
833             return;
834
835         for (i = 1; i <= 3; i++) {
836             if (BUTTON_PRESS(mEvent.bstate, i)) {
837                 event.mouse.type = CK_EV_MOUSE_DOWN;
838                 goto mouseEventNC;
839             } else if (BUTTON_RELEASE(mEvent.bstate, i)) {
840                 event.mouse.type = CK_EV_MOUSE_UP;
841 mouseEventNC:
842                 event.mouse.button = i;
843                 event.mouse.rootx = mEvent.x;
844                 event.mouse.rooty = mEvent.y;
845                 event.mouse.x = mEvent.x;
846                 event.mouse.y = mEvent.y;
847                 event.mouse.winPtr = Ck_GetWindowXY(mainPtr, &event.mouse.x,
848                     &event.mouse.y, 1);
849                 Ck_HandleEvent(mainPtr, &event);
850                 return;
851             }
852         }
853     }
854 #endif
855
856 #if defined(__WIN32__) || defined(DJGPP)
857     if ((mainPtr->flags & CK_HAS_MOUSE) && code == KEY_MOUSE) {
858         int i;
859
860         request_mouse_pos();
861         for (i = 0; i < 3; i++) {
862             if (Mouse_status.button[i] == BUTTON_PRESSED) {
863                 event.mouse.type = CK_EV_MOUSE_DOWN;
864                 goto mouseEvt;
865             } else if (Mouse_status.button[i] == BUTTON_RELEASED) {
866                 event.mouse.type = CK_EV_MOUSE_UP;
867 mouseEvt:
868                 event.mouse.button = i + 1;
869                 event.mouse.x = Mouse_status.x;
870                 event.mouse.y = Mouse_status.y;
871                 event.mouse.winPtr = Ck_GetWindowXY(mainPtr, &event.mouse.x,
872                     &event.mouse.y, 1);
873                 Ck_HandleEvent(mainPtr, &event);
874                 return;
875             }
876         }
877     }
878 #endif
879
880     /*
881      * Xterm mouse report handling: Although GPM has an xterm module
882      * this is separately done here, since I want to be as independent
883      * as possible from GPM.
884      * It is assumed that the entire mouse report comes in one piece
885      * ie without any delay between the 6 relevant characters.
886      * Only a single button down/up event is generated.
887      */
888
889 #if !defined(__WIN32__) && !defined(DJGPP)
890     if ((mainPtr->flags & CK_MOUSE_XTERM) && (code == 0x1b || code == 0x9b)) {
891         int code2;
892
893         if (code == 0x9b)
894             goto getM;
895         code2 = getch();
896         if (code2 != ERR) {
897             if (code2 == '[')
898                 goto getM;
899             ungetch(code2);
900         } else
901             errCount++;
902         goto keyEvent;
903 getM:
904         code2 = getch();
905         if (code2 != ERR) {
906             if (code2 == 'M')
907                 goto getMouse;
908             ungetch(code2);
909         } else
910             errCount++;
911         goto keyEvent;
912 getMouse:
913         code2 = getch();
914         if (code2 == ERR) {
915             errCount++;
916             return;
917         }
918         event.mouse.button = ((code2 - 0x20) & 0x03) + 1;
919         code2 = getch();
920         if (code2 == ERR) {
921             errCount++;
922             return;
923         }
924         event.mouse.x = event.mouse.rootx = code2 - 0x20 - 1;
925         code2 = getch();
926         if (code2 == ERR) {
927             errCount++;
928             return;
929         }
930         event.mouse.y = event.mouse.rooty = code2 - 0x20 - 1;
931         if (event.mouse.button > 3) {
932             event.mouse.button = buttonpressed;
933             buttonpressed = 0;
934             event.mouse.type = CK_EV_MOUSE_UP;
935             goto mouseEvent;
936         } else if (buttonpressed == 0) {
937             buttonpressed = event.mouse.button;
938             event.mouse.type = CK_EV_MOUSE_DOWN;
939 mouseEvent:
940             event.mouse.winPtr = Ck_GetWindowXY(mainPtr, &event.mouse.x,
941                 &event.mouse.y, 1);
942             Ck_HandleEvent(mainPtr, &event);
943             return;
944         }
945         return;
946     }
947 #endif
948
949 keyEvent:
950     event.key.type = CK_EV_KEYPRESS;
951     event.key.winPtr = mainPtr->focusPtr;
952     event.key.keycode = code;
953     if (event.key.keycode < 0)
954         event.key.keycode &= 0xff;
955     Ck_HandleEvent(mainPtr, &event);
956 }
957
958 #endif /* TCL_MAJOR_VERSION == 7 && TCL_MINOR_VERSION <= 4 */
959 \f
960 #ifdef HAVE_GPM
961 /*
962  *--------------------------------------------------------------
963  *
964  * CkHandleGPMInput --
965  *
966  *      Process mouse events from GPM.
967  *
968  * Results:
969  *      The return value is TK_FILE_HANDLED if the procedure
970  *      actually found an event to process.  If no event was found
971  *      then TK_READABLE is returned.
972  *
973  * Side effects:
974  *      The handling of the event could cause additional
975  *      side effects.
976  *
977  *--------------------------------------------------------------
978  */
979
980 #if (TCL_MAJOR_VERSION == 7) && (TCL_MINOR_VERSION <= 4)
981 int
982 CkHandleGPMInput(clientData, mask, flags)
983     ClientData clientData;      /* Pointer to main info. */
984     int mask;                   /* OR-ed combination of the bits TK_READABLE,
985                                  * TK_WRITABLE, and TK_EXCEPTION, indicating
986                                  * current state of file. */
987     int flags;                  /* Flag bits passed to Tk_DoOneEvent;
988                                  * contains bits such as TK_DONT_WAIT,
989                                  * TK_X_EVENTS, Tk_FILE_EVENTS, etc. */
990 {
991     Gpm_Event gpmEvent;
992     CkEvent event;
993     CkMainInfo *mainPtr = (CkMainInfo *) clientData;
994     int ret, type;
995
996     if (!(flags & TK_FILE_EVENTS))
997         return 0;
998
999     if (!(mask & TK_READABLE))
1000         return TK_READABLE;
1001
1002     ret = Gpm_GetEvent(&gpmEvent);
1003     if (ret == 0) {
1004         /*
1005          * GPM connection is closed; delete this file handler.
1006          */
1007
1008         Tk_DeleteFileHandler((int) mainPtr->mouseData);
1009         mainPtr->mouseData = (ClientData) -1;
1010         return 0;
1011     } else if (ret == -1)
1012         return TK_READABLE;
1013
1014     GPM_DRAWPOINTER(&gpmEvent);
1015     type = gpmEvent.type & (GPM_DOWN | GPM_UP);
1016     if (type == GPM_DOWN || type == GPM_UP) {
1017         event.mouse.type = type == GPM_DOWN ? CK_EV_MOUSE_DOWN :
1018             CK_EV_MOUSE_UP;
1019         if (gpmEvent.buttons & GPM_B_LEFT)
1020             event.mouse.button = 1;
1021         else if (gpmEvent.buttons & GPM_B_MIDDLE)
1022             event.mouse.button = 2;
1023         else if (gpmEvent.buttons & GPM_B_RIGHT)
1024             event.mouse.button = 3;
1025         event.mouse.x = event.mouse.rootx = gpmEvent.x - 1;
1026         event.mouse.y = event.mouse.rooty = gpmEvent.y - 1;
1027         event.mouse.winPtr = Ck_GetWindowXY(mainPtr, &event.mouse.x,
1028             &event.mouse.y, 1);
1029         Ck_HandleEvent(mainPtr, &event);
1030         return TK_FILE_HANDLED;
1031     }
1032     return TK_READABLE;
1033 }
1034 #else
1035 void
1036 CkHandleGPMInput(clientData, mask)
1037     ClientData clientData;      /* Pointer to main info. */
1038     int mask;                   /* OR-ed combination of the bits TK_READABLE,
1039                                  * TK_WRITABLE, and TK_EXCEPTION, indicating
1040                                  * current state of file. */
1041 {
1042     Gpm_Event gpmEvent;
1043     CkEvent event;
1044     CkMainInfo *mainPtr = (CkMainInfo *) clientData;
1045     int ret, type;
1046
1047     if (!(mask & TCL_READABLE))
1048         return;
1049
1050     ret = Gpm_GetEvent(&gpmEvent);
1051     if (ret == 0) {
1052         /*
1053          * GPM connection is closed; delete this file handler.
1054          */
1055 #if (TCL_MAJOR_VERSION == 7) 
1056         Tcl_DeleteFileHandler((Tcl_File) mainPtr->mouseData);
1057 #else
1058         Tcl_DeleteFileHandler((int) mainPtr->mouseData);
1059 #endif
1060         mainPtr->mouseData = (ClientData) 0;
1061         return;
1062     } else if (ret == -1)
1063         return;
1064
1065     GPM_DRAWPOINTER(&gpmEvent);
1066     type = gpmEvent.type & (GPM_DOWN | GPM_UP);
1067     if (type == GPM_DOWN || type == GPM_UP) {
1068         event.mouse.type = type == GPM_DOWN ? CK_EV_MOUSE_DOWN :
1069             CK_EV_MOUSE_UP;
1070         if (gpmEvent.buttons & GPM_B_LEFT)
1071             event.mouse.button = 1;
1072         else if (gpmEvent.buttons & GPM_B_MIDDLE)
1073             event.mouse.button = 2;
1074         else if (gpmEvent.buttons & GPM_B_RIGHT)
1075             event.mouse.button = 3;
1076         event.mouse.x = event.mouse.rootx = gpmEvent.x - 1;
1077         event.mouse.y = event.mouse.rooty = gpmEvent.y - 1;
1078         event.mouse.winPtr = Ck_GetWindowXY(mainPtr, &event.mouse.x,
1079             &event.mouse.y, 1);
1080         Ck_HandleEvent(mainPtr, &event);
1081     }
1082 }
1083 #endif /* TCL_MAJOR_VERSION == 7 && TCL_MINOR_VERSION <= 4 */
1084 #endif /* HAVE_GPM */
1085 \f
1086 /*
1087  *--------------------------------------------------------------
1088  *
1089  * Ck_MainLoop --
1090  *
1091  *      Call Ck_DoOneEvent over and over again in an infinite
1092  *      loop as long as there exist any main windows.
1093  *
1094  * Results:
1095  *      None.
1096  *
1097  * Side effects:
1098  *      Arbitrary;  depends on handlers for events.
1099  *
1100  *--------------------------------------------------------------
1101  */
1102
1103 void
1104 Ck_MainLoop()
1105 {
1106     extern CkMainInfo *ckMainInfo;
1107
1108     while (ckMainInfo != NULL) {
1109         Tk_DoOneEvent(0);
1110     }
1111 }
1112 \f
1113 /*
1114  *--------------------------------------------------------------
1115  *
1116  * BarcodeTimeout --
1117  *
1118  *      Handle timeout while reading barcode packet.
1119  *
1120  *--------------------------------------------------------------
1121  */
1122
1123 static void
1124 BarcodeTimeout(clientData)
1125     ClientData clientData;
1126 {
1127     CkMainInfo *mainPtr = (CkMainInfo *) clientData;
1128     BarcodeData *bd = (BarcodeData *) mainPtr->barcodeData;
1129
1130     if (bd != NULL) {
1131         bd->index = -1;
1132         bd->timer = (Tk_TimerToken) NULL;
1133     }
1134 }
1135 \f
1136 /*
1137  *--------------------------------------------------------------
1138  *
1139  * CkGetBarcodeData --
1140  *
1141  *      Return data collected in barcode packet buffer.
1142  *
1143  *--------------------------------------------------------------
1144  */
1145
1146 char *
1147 CkGetBarcodeData(mainPtr)
1148     CkMainInfo *mainPtr;
1149 {
1150     BarcodeData *bd = (BarcodeData *) mainPtr->barcodeData;
1151
1152     if (bd == NULL || bd->index < 0)
1153         return NULL;
1154     if (bd->index >= sizeof (bd->buffer) - 1)
1155         bd->buffer[sizeof (bd->buffer) - 1] = '\0';
1156     else
1157         bd->buffer[bd->index] = '\0';
1158     return bd->buffer;
1159 }
1160 \f
1161 /*
1162  *--------------------------------------------------------------
1163  *
1164  * CkBarcodeCmd --
1165  *
1166  *      Minor command handler to deal with barcode reader.
1167  *      Called by "curses" Tcl command.
1168  *
1169  * Results:
1170  *      TCL_OK or TCL_ERROR.
1171  *
1172  *
1173  *--------------------------------------------------------------
1174  */
1175
1176 int
1177 CkBarcodeCmd(clientData, interp, argc, argv)
1178     ClientData clientData;      /* Main window associated with
1179                                  * interpreter. */
1180     Tcl_Interp *interp;         /* Current interpreter. */
1181     int argc;                   /* Number of arguments. */
1182     char **argv;                /* Argument strings. */
1183 {
1184     CkMainInfo *mainPtr = ((CkWindow *) (clientData))->mainPtr;
1185     BarcodeData *bd = (BarcodeData *) mainPtr->barcodeData;
1186
1187     if (argc == 2) {
1188         if (mainPtr->flags & CK_HAS_BARCODE) {
1189             char buffer[32];
1190
1191             sprintf(buffer, "%d %d %d", bd->startChar, bd->endChar,
1192                 bd->pkttime);
1193             Tcl_AppendResult(interp, buffer, (char *) NULL);
1194         }
1195         return TCL_OK;
1196     } else if (argc == 3) {
1197         if (strcmp(argv[2], "off") != 0)
1198             goto badArgs;
1199         if (mainPtr->flags & CK_HAS_BARCODE) {
1200             Tk_DeleteTimerHandler(bd->timer);
1201             mainPtr->flags &= ~CK_HAS_BARCODE;
1202             mainPtr->barcodeData = NULL;
1203             ckfree((char *) bd);
1204         }
1205         return TCL_OK;
1206     } else if (argc == 4 || argc == 5) {
1207         int start, end, pkttime;
1208
1209         if (Tcl_GetInt(interp, argv[2], &start) != TCL_OK ||
1210             Tcl_GetInt(interp, argv[3], &end) != TCL_OK)
1211             return TCL_ERROR;
1212         if (argc > 4 && Tcl_GetInt(interp, argv[4], &pkttime) != TCL_OK)
1213             return TCL_ERROR;
1214         if (!(mainPtr->flags & CK_HAS_BARCODE)) {
1215             bd = (BarcodeData *) ckalloc(sizeof (BarcodeData));
1216             mainPtr->flags |= CK_HAS_BARCODE;
1217             mainPtr->barcodeData = (ClientData) bd;
1218             bd->pkttime = DEFAULT_BARCODE_TIMEOUT;
1219             bd->timer = (Tk_TimerToken) NULL;
1220             bd->delivered = 0;
1221             bd->index = -1;
1222         }
1223         if (argc > 4 && pkttime > 50)
1224             bd->pkttime = pkttime;
1225         bd->startChar = start;
1226         bd->endChar = end;
1227         return TCL_OK;
1228     } else {
1229 badArgs:
1230         Tcl_AppendResult(interp, "bad or wrong # args: should be \"", argv[0],
1231             " barcode ?off?\" or \"",
1232             argv[0], " barcode startChar endChar ?timeout?\"", (char *) NULL);
1233     }
1234     return TCL_ERROR;
1235 }