1 // Low level event handling
2 // Copyright (C) 2000 Core Technologies.
3
4 // This file is part of e93.
5 //
6 // e93 is free software; you can redistribute it and/or modify
7 // it under the terms of the e93 LICENSE AGREEMENT.
8 //
9 // e93 is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // e93 LICENSE AGREEMENT for more details.
13 //
14 // You should have received a copy of the e93 LICENSE AGREEMENT
15 // along with e93; see the file "LICENSE.TXT".
16
17 #include "includes.h"
18
19 #define BLINK_TIMEOUT_START 40 // when restarting the timeout value, this is used
20 #define BLINK_TIMEOUT_CONTINUE 30 // when continuing timeouts, this value is used
21 #define ABORT_CHECK_INTERVAL 30 // check for abort every 1/2 second
22
23 static volatile UINT32
24 waitTimer; // used by the WaitTicks routine, this counts up at the alarm rate, but can be reset
25 static volatile UINT32
26 blinkTimeout; // when this gets to 0, it is time to flash the cursor
27 static KeyCode
28 abortKeyCode; // keycode which matches the "abort" key (currently "escape")
29 static UINT32
30 nextAbortCheckTime; // when timer is >= to this value, it is ok to check for an abort
31
XErrorEventHandler(Display * display,XErrorEvent * event)32 int XErrorEventHandler(Display *display,XErrorEvent *event)
33 // Handle protocol errors (generated by strange X implementations, or window managers)
34 {
35 // char
36 // errorMessage[256];
37
38 // XGetErrorText(display,event->error_code,errorMessage,256);
39 // fprintf(stderr,"X Error: %s\n",errorMessage);
40
41 xFail=true;
42 return(0);
43 }
44
UpdateEventsPredicate(Display * display,XEvent * event,char * arg)45 static Bool UpdateEventsPredicate(Display *display,XEvent *event,char *arg)
46 // Match all update events
47 {
48 if((event->type==Expose)||(event->type==GraphicsExpose)||(event->type==NoExpose)||(event->type==ConfigureNotify))
49 {
50 return(True);
51 }
52 return(False);
53 }
54
AllButUpdateEventsPredicate(Display * display,XEvent * event,char * arg)55 static Bool AllButUpdateEventsPredicate(Display *display,XEvent *event,char *arg)
56 // Match all events (used in XCheckIfEvent below)
57 {
58 return(!UpdateEventsPredicate(display,event,arg));
59 }
60
KeyEventsPredicate(Display * display,XEvent * event,char * arg)61 static Bool KeyEventsPredicate(Display *display,XEvent *event,char *arg)
62 // Match all keyboard press events
63 {
64 if(event->type==KeyPress)
65 {
66 return(True);
67 }
68 return(False);
69 }
70
71 // **** The following comment is brought to you by: emacs 19.22 ****
72 // This holds the state XLookupString needs to implement dead keys
73 // and other tricks known as "compose processing". _X Window System_
74 // says that a portable program can't use this, but Stephen Gildea assures
75 // me (emacs person) that letting the compiler initialize it to zeros will
76 // work okay.
77
78 // If it is good enough for emacs, it is good enough for e93!
79
80 static XComposeStatus
81 composeStatus; // keep compose status around, and pass it in to all calls to XLookupString
82
ComposeXLookupString(XKeyEvent * event_struct,char * buffer_return,int bytes_buffer,KeySym * keysym_return)83 int ComposeXLookupString(XKeyEvent *event_struct,char *buffer_return,int bytes_buffer,KeySym *keysym_return)
84 // Call this instead of XLookupString, it does the same thing, but handles the state of
85 // compose processing properly
86 // This should allow people to enter umlaut characters more easily
87 {
88 return(XLookupString(event_struct,buffer_return,bytes_buffer,keysym_return,&composeStatus));
89 }
90
KeyEventToEditorKey(XEvent * event,EDITOR_KEY * keyData)91 bool KeyEventToEditorKey(XEvent *event,EDITOR_KEY *keyData)
92 // Take a keyboard event, and fill in an editor key record
93 // If event does not yield a editor key, then
94 // return false
95 {
96 KeySym
97 keySym;
98 char
99 ascCode;
100 bool
101 virtualKey;
102 UINT32
103 key;
104 int
105 numAsc;
106 XKeyEvent
107 ourEvent;
108
109 key=0;
110 ourEvent=*((XKeyEvent *)event); // copy the event, so we can play with it a bit
111 ourEvent.state&=(~Mod1Mask); // remove this from consideration
112 numAsc=ComposeXLookupString(&ourEvent,&ascCode,1,&keySym);
113 if(!IsModifierKey(keySym)) // make sure it is not a modifier key (these do not get passed back)
114 {
115 virtualKey=true; // assume virtual key
116 switch(keySym)
117 {
118 case XK_Left:
119 key=VVK_LEFTARROW;
120 break;
121 case XK_Right:
122 key=VVK_RIGHTARROW;
123 break;
124 case XK_Up:
125 key=VVK_UPARROW;
126 break;
127 case XK_Down:
128 key=VVK_DOWNARROW;
129 break;
130 case XK_Prior:
131 key=VVK_PAGEUP;
132 break;
133 case XK_Next:
134 key=VVK_PAGEDOWN;
135 break;
136 case XK_Home:
137 key=VVK_HOME;
138 break;
139 case XK_End:
140 key=VVK_END;
141 break;
142 case XK_Insert:
143 key=VVK_INSERT;
144 break;
145 case XK_Delete:
146 key=VVK_DELETE;
147 break;
148 case XK_BackSpace:
149 key=VVK_BACKSPACE;
150 break;
151 case XK_Help:
152 key=VVK_HELP;
153 break;
154 case XK_Undo:
155 case XK_F14:
156 key=VVK_UNDO;
157 break;
158 case XK_Redo:
159 case XK_F12:
160 key=VVK_REDO;
161 break;
162 case XK_Return:
163 key=VVK_RETURN;
164 break;
165 case XK_KP_Enter:
166 key=VVK_RETURN;
167 break;
168 case XK_Tab:
169 key=VVK_TAB;
170 break;
171 case XK_F20:
172 key=VVK_CUT;
173 break;
174 case XK_F16:
175 key=VVK_COPY;
176 break;
177 case XK_F18:
178 key=VVK_PASTE;
179 break;
180 case XK_F1:
181 key=VVK_F1;
182 break;
183 case XK_F2:
184 key=VVK_F2;
185 break;
186 case XK_F3:
187 key=VVK_F3;
188 break;
189 case XK_F4:
190 key=VVK_F4;
191 break;
192 case XK_F5:
193 key=VVK_F5;
194 break;
195 case XK_F6:
196 key=VVK_F6;
197 break;
198 case XK_F7:
199 key=VVK_F7;
200 break;
201 case XK_F8:
202 key=VVK_F8;
203 break;
204 case XK_F9:
205 key=VVK_F9;
206 break;
207 case XK_F10:
208 key=VVK_F10;
209 break;
210 default:
211 virtualKey=false;
212 break;
213 }
214 if((keyData->isVirtual=virtualKey))
215 {
216 keyData->keyCode=key;
217 XStateToEditorModifiers(event->xkey.state,0,&keyData->modifiers);
218 return(true);
219 }
220 else
221 {
222 if(numAsc)
223 {
224 keyData->keyCode=ascCode;
225 XStateToEditorModifiers(event->xkey.state,0,&keyData->modifiers);
226 return(true);
227 }
228 }
229 }
230 return(false);
231 }
232
EditorKeyNameToKeyCode(const char * name,UINT32 * code)233 bool EditorKeyNameToKeyCode(const char *name,UINT32 *code)
234 // Given a string which is the name of an unmodified key on the keyboard,
235 // convert it into a keycode (in the case of X, we use KeySyms)
236 // if the name is not the name of any key on our keyboard, then return false
237 {
238 KeySym
239 keySym;
240
241 if((keySym=XStringToKeysym(name))!=NoSymbol)
242 {
243 *code=(UINT32)keySym;
244 return(true);
245 }
246 return(false);
247 }
248
EditorKeyCodeToKeyName(UINT32 code)249 char *EditorKeyCodeToKeyName(UINT32 code)
250 // Given a keycode, get its name
251 // if the code is invalid, return NULL
252 // NOTE: the returned string is static, and should not be modified or deleted
253 {
254 return(XKeysymToString((KeySym)code));
255 }
256
XStateToEditorModifiers(unsigned int state,UINT32 numClicks,UINT32 * editorModifiers)257 void XStateToEditorModifiers(unsigned int state,UINT32 numClicks,UINT32 *editorModifiers)
258 // Convert modifiers and numClicks into modifier bits for the editor
259 {
260 (*editorModifiers)=0;
261
262 if(state&LockMask)
263 {
264 (*editorModifiers)|=EEM_CAPSLOCK;
265 }
266 if(state&ShiftMask)
267 {
268 (*editorModifiers)|=EEM_SHIFT;
269 }
270 if(state&ControlMask)
271 {
272 (*editorModifiers)|=EEM_CTL;
273 }
274 if(state&Mod1Mask) // Alt key
275 {
276 (*editorModifiers)|=EEM_MOD0;
277 }
278 if(state&Mod4Mask) // Meta L key
279 {
280 (*editorModifiers)|=EEM_MOD1;
281 }
282
283 numClicks<<=EES_STATE0;
284 numClicks&=EEM_STATE0;
285 (*editorModifiers)|=numClicks;
286 }
287
EditorGetKeyPress(UINT32 * keyCode,UINT32 * editorModifiers,bool wait,bool clearBuffered)288 bool EditorGetKeyPress(UINT32 *keyCode,UINT32 *editorModifiers,bool wait,bool clearBuffered)
289 // Used by editor when it wants to get a key press
290 // If a code is found, it is returned in keyCode/editorModifiers
291 // If none is ready, return false
292 // If wait is true, wait here until one is ready (true is always returned)
293 // If clearBuffered is true, then clear any keypresses that may have been in the
294 // buffer before proceeding.
295 {
296 XEvent
297 event;
298 bool
299 gotOne;
300 KeySym
301 keySym;
302
303 gotOne=false;
304 if(clearBuffered)
305 {
306 while(XCheckIfEvent(xDisplay,&event,KeyEventsPredicate,NULL)) // inhale all keypresses
307 ;
308 }
309 if(wait)
310 {
311 do
312 {
313 XIfEvent(xDisplay,&event,KeyEventsPredicate,NULL);
314 ComposeXLookupString((XKeyEvent *)&event,NULL,0,&keySym); // get KeySym to see if modifiers or real press
315 }
316 while(IsModifierKey(keySym));
317 gotOne=true;
318 }
319 else
320 {
321 while(!gotOne&&XCheckIfEvent(xDisplay,&event,KeyEventsPredicate,NULL))
322 {
323 ComposeXLookupString((XKeyEvent *)&event,NULL,0,&keySym); // get KeySym to see if modifiers or real press
324 if(!IsModifierKey(keySym))
325 {
326 gotOne=true;
327 }
328 }
329 }
330 if(gotOne)
331 {
332 XStateToEditorModifiers(event.xkey.state,0,editorModifiers); // make modifiers
333 event.xkey.state=0; // get rid of all modifiers, so we end up with un-modified keysym
334 ComposeXLookupString((XKeyEvent *)&event,NULL,0,&keySym); // get back unmodified keysym
335 *keyCode=(UINT32)keySym;
336 }
337 return(gotOne);
338 }
339
340 // pseudo globals needed to handle multiple clicks
341
342 static UINT32
343 lastClickTime;
344 static Window
345 lastClickWindow;
346 static INT32
347 lastClickX,
348 lastClickY;
349 static UINT32
350 numClicks;
351
352 #define CLICKSLOP 5 // number of pixels of slop in either direction
353 #define CLICKTIME 300 // number of milliseconds between clicks considered multiple
354
ResetMultipleClicks()355 void ResetMultipleClicks()
356 // Clears the double click information, so that next click starts from scratch
357 {
358 lastClickTime=0; // initialize multiple click variables
359 lastClickWindow=(Window)0;
360 lastClickX=lastClickY=0;
361 numClicks=0;
362 }
363
HandleMultipleClicks(XEvent * event)364 UINT32 HandleMultipleClicks(XEvent *event)
365 // Updates and returns numClicks which tells how many clicks were in roughly the same area
366 // of the same window.
367 // where each click followed the previous by no more than CLICKTIME milliseconds
368 // This is how double, triple, etc clicks are handled
369 // NOTE: due to wrap around in the time field, this routine could possibly
370 // fail to work correctly once every 49.7 days
371 {
372 if((lastClickTime+CLICKTIME)>event->xbutton.time&&
373 (lastClickX>=(event->xbutton.x_root-CLICKSLOP))&&(lastClickX<=(event->xbutton.x_root+CLICKSLOP))&&
374 (lastClickY>=(event->xbutton.y_root-CLICKSLOP))&&(lastClickY<=(event->xbutton.y_root+CLICKSLOP))&&
375 event->xbutton.window==lastClickWindow)
376 {
377 numClicks++;
378 }
379 else
380 {
381 numClicks=0;
382 }
383 lastClickTime=event->xbutton.time;
384 lastClickWindow=event->xbutton.window;
385 lastClickX=event->xbutton.x_root;
386 lastClickY=event->xbutton.y_root;
387 return(numClicks);
388 }
389
ButtonEventPredicate(Display * display,XEvent * event,char * arg)390 static Bool ButtonEventPredicate(Display *display,XEvent *event,char *arg)
391 // Return True if the event is a button press, or a button release
392 // All button events are looked at while in StillDown
393 {
394 if((event->type==ButtonPress)||(event->type==ButtonRelease))
395 {
396 return(True);
397 }
398 return(False);
399 }
400
StillDown(unsigned int button,UINT32 numTicks)401 bool StillDown(unsigned int button,UINT32 numTicks)
402 // Call this after receiving a Button Pressed event for button, and
403 // it will return true if the button is still held down
404 // numTicks can be used to give some time back to the system while
405 // waiting, it can also be used to time scrolls and such so that they
406 // do not run too fast on fast machines
407 {
408 XEvent
409 event;
410 bool
411 locatedUp;
412
413 locatedUp=false;
414 WaitTicks(numTicks);
415 while(!locatedUp&&XCheckIfEvent(xDisplay,&event,ButtonEventPredicate,NULL))
416 {
417 if(event.type==ButtonRelease&&event.xbutton.button==button)
418 {
419 locatedUp=true;
420 }
421 }
422 return(!locatedUp);
423 }
424
CollectInvalidations(XEvent * event)425 static void CollectInvalidations(XEvent *event)
426 // Given an Expose, or GraphicsExpose event, find the window
427 // it belongs to, and add the event's invalid area to the window's
428 // invalid region
429 {
430 WINDOW_LIST_ELEMENT
431 *windowElement;
432 EDITOR_RECT
433 invalidRect;
434
435 if((windowElement=LocateWindowElement(event->xany.window)))
436 {
437 invalidRect.x=event->xexpose.x;
438 invalidRect.y=event->xexpose.y;
439 invalidRect.w=event->xexpose.width;
440 invalidRect.h=event->xexpose.height;
441 InvalidateWindowRect(windowElement->editorWindow,&invalidRect);
442 }
443 }
444
ConfigureWindow(XEvent * event)445 static void ConfigureWindow(XEvent *event)
446 // Given a ConfigureNotify event, find the window
447 // it belongs to, and tell it to adjust to its size
448 {
449 WINDOW_LIST_ELEMENT
450 *windowElement;
451
452 if((windowElement=LocateWindowElement(event->xany.window)))
453 {
454 switch(windowElement->editorWindow->windowType)
455 {
456 case EWT_DOCUMENT:
457 AdjustDocumentWindowForNewSize(windowElement->editorWindow);
458 break;
459 case EWT_DIALOG:
460 AdjustDialogWindowForNewSize(windowElement->editorWindow);
461 break;
462 case EWT_MENU:
463 // these do not configure at the moment, so ignore the events
464 break;
465 default:
466 break;
467 }
468 }
469 }
470
AttemptBoundKeyPress(XEvent * event)471 static bool AttemptBoundKeyPress(XEvent *event)
472 // See if the keypress event can be handled by one of the bound keys
473 // if so, handle it, and return true
474 // if the key is not bound, return false
475 {
476 KeySym
477 keySym;
478 XKeyEvent
479 ourEvent;
480 UINT32
481 editorModifiers;
482
483 XStateToEditorModifiers(event->xkey.state,0,&editorModifiers); // make modifiers
484 ourEvent=*((XKeyEvent *)event);
485 ourEvent.state=0; // get rid of all modifiers, so we end up with un-modified keysym
486 ComposeXLookupString(&ourEvent,NULL,0,&keySym); // get back unmodified keysym
487 if(!IsModifierKey(keySym)) // do not pass lone modifiers
488 {
489 return(HandleBoundKeyEvent((UINT32)keySym,editorModifiers)); // attempt to handle it
490 }
491 return(false);
492 }
493
DoEvent(XEvent * event)494 static void DoEvent(XEvent *event)
495 // Passed an event, do what is needed to take care of it
496 {
497 WINDOW_LIST_ELEMENT
498 *windowElement;
499 Window
500 xWindow;
501 EDITOR_WINDOW
502 *window;
503 int
504 focusRevert;
505
506 switch(event->type)
507 {
508 case KeyPress:
509 if(!AttemptMenuKeyPress(event)) // look for menu key first
510 {
511 if(!AttemptBoundKeyPress(event)) // then try for bound key
512 {
513 XGetInputFocus(xDisplay,&xWindow,&focusRevert); // find out who has the focus
514 if((windowElement=LocateWindowElement(xWindow))) // see if it is one of our windows
515 {
516 event->xany.window=xWindow; // fudge top window into event
517 switch(windowElement->editorWindow->windowType)
518 {
519 case EWT_MENU: // keys in menu window which are not menu keys get passed to top document window
520 if((window=GetTopEditorWindowType(EWT_DOCUMENT)))
521 {
522 event->xany.window=((WINDOW_LIST_ELEMENT *)window->userData)->xWindow;
523 DocumentWindowEvent(window,event);
524 }
525 break;
526 case EWT_DOCUMENT:
527 DocumentWindowEvent(windowElement->editorWindow,event);
528 break;
529 }
530 }
531 }
532 }
533 break;
534
535 default:
536 if((windowElement=LocateWindowElement(event->xany.window)))
537 {
538 switch(windowElement->editorWindow->windowType)
539 {
540 case EWT_DOCUMENT:
541 DocumentWindowEvent(windowElement->editorWindow,event);
542 break;
543 case EWT_DIALOG:
544 DialogWindowEvent(windowElement->editorWindow,event);
545 break;
546 case EWT_MENU:
547 MenuWindowEvent(windowElement->editorWindow,event);
548 break;
549 default:
550 break;
551 }
552 }
553 break;
554 }
555 }
556
DoDialogEvent(EDITOR_WINDOW * dialog,XEvent * event)557 static void DoDialogEvent(EDITOR_WINDOW *dialog,XEvent *event)
558 // Passed an event, do what is needed to take care of it
559 // this is called only when a dialog is up
560 {
561 WINDOW_LIST_ELEMENT
562 *windowElement;
563 Window
564 xWindow;
565 int
566 focusRevert;
567
568 switch(event->type)
569 {
570 case KeyPress:
571 XGetInputFocus(xDisplay,&xWindow,&focusRevert); // find out who has the focus
572 if((windowElement=LocateWindowElement(xWindow))&&(windowElement->editorWindow==dialog)) // see if it is the active dialog
573 {
574 event->xany.window=xWindow; // fudge top window into event
575 DialogWindowEvent(windowElement->editorWindow,event);
576 }
577 else
578 {
579 EditorBeep(); // ugly kludge caused by X not allowing me to force the dialog to the top of the application
580 SetTopEditorWindow(dialog);
581 }
582 break;
583
584 default:
585 if((windowElement=LocateWindowElement(event->xany.window)))
586 {
587 if((windowElement->editorWindow==dialog)||(event->type!=ButtonPress&&event->type!=ClientMessage)) // dont let the user do things to windows other than the dialog we are handling
588 {
589 switch(windowElement->editorWindow->windowType)
590 {
591 case EWT_DOCUMENT:
592 DocumentWindowEvent(windowElement->editorWindow,event);
593 break;
594 case EWT_DIALOG:
595 DialogWindowEvent(windowElement->editorWindow,event);
596 break;
597 case EWT_MENU:
598 MenuWindowEvent(windowElement->editorWindow,event);
599 break;
600 default:
601 break;
602 }
603 }
604 else
605 {
606 SetTopEditorWindow(dialog);
607 }
608 }
609 break;
610 }
611 }
612
UpdateEditorWindows()613 void UpdateEditorWindows()
614 // Attend to changes brought about in our windows.
615 // Redraw all editor windows that have been invalidated by previous expose or
616 // graphics expose events.
617 {
618 XEvent
619 event;
620
621 while(XCheckIfEvent(xDisplay,&event,UpdateEventsPredicate,NULL)) // get pending update messages
622 {
623 switch(event.type)
624 {
625 case Expose:
626 case GraphicsExpose:
627 CollectInvalidations(&event); // collect any rectangles from expose events
628 break;
629 case ConfigureNotify:
630 ConfigureWindow(&event); // go configure the window
631 break;
632 default:
633 break;
634 }
635 }
636 RedrawInvalidWindows();
637 }
638
ResetCursorBlinkTime()639 void ResetCursorBlinkTime()
640 // Reset the timeout until the next cursor blink arrives
641 {
642 blinkTimeout=BLINK_TIMEOUT_START; // number of ticks of system timer before flashing the cursor
643 }
644
WaitTicks(UINT32 numTicks)645 void WaitTicks(UINT32 numTicks)
646 // Wait for numTicks of the system timer before continuing
647 // NOTE: the delay is measured from the last time this
648 // was called, so if it has not been called in a while, the
649 // first call usually returns immediately, it is useful
650 // for slowing down scrolls on fast machines
651 {
652 while(waitTimer<numTicks)
653 {
654 pause(); // give some time back to the machine, while we wait
655 }
656 waitTimer=0; // reset the wait timeout
657 }
658
AlarmIntervalProc(int unused)659 void AlarmIntervalProc(int unused)
660 // When the alarm goes off, this executes (then main wakes up)
661 {
662 if(blinkTimeout)
663 {
664 blinkTimeout--;
665 }
666 waitTimer++;
667 timer++;
668 }
669
StartAlarmTimer()670 void StartAlarmTimer()
671 // Called at the start of execution, this sets up the time base
672 // used during the rest of the program
673 // it also installs the signal handler
674 {
675 struct sigaction
676 *vector; // used to set the alarm vector
677 struct itimerval
678 timerStruct;
679
680 vector=(struct sigaction *)calloc(sizeof(struct sigaction),1); // cheap way to clear most fields of sigaction that we do not care about (because fields are named differently on each machine)
681 #ifdef SOLARIS
682 vector->_funcptr._handler=AlarmIntervalProc; // handle the signal with this routine
683 #else
684 vector->sa_handler=AlarmIntervalProc; // handle the signal with this routine
685 #endif
686 vector->sa_flags=SA_RESTART; // do not let system call stop when interrupted
687 sigaction(SIGALRM,vector,NULL); // when the alarm signal goes off, call here
688 free(vector);
689
690 // timerStruct.it_interval.tv_sec=0; // 1/60 of a second intervals (fast enough to avoid sluggish feeling, not so fast as to bog machine)
691 // timerStruct.it_interval.tv_usec=16667;
692 // timerStruct.it_value.tv_sec=0;
693 // timerStruct.it_value.tv_usec=16667;
694
695 timerStruct.it_interval.tv_sec=0; // 1/100 of a second intervals (machines are faster, and more resolution is better)
696 timerStruct.it_interval.tv_usec=10000;
697 timerStruct.it_value.tv_sec=0;
698 timerStruct.it_value.tv_usec=10000;
699
700 setitimer(ITIMER_REAL,&timerStruct,NULL);
701 }
702
DoPeriodicProc()703 static void DoPeriodicProc()
704 // When it is time to flash the cursor (or whatever), attempt to locate the window with input
705 // focus, and call it to do its thing
706 {
707 Window
708 xWindow;
709 int
710 focusRevert;
711 WINDOW_LIST_ELEMENT
712 *element;
713
714 XGetInputFocus(xDisplay,&xWindow,&focusRevert); // find out who has the focus
715 if((element=LocateWindowElement(xWindow))) // see if it is one of our windows
716 {
717 switch(element->editorWindow->windowType)
718 {
719 case EWT_DOCUMENT:
720 DocumentPeriodicProc(element->editorWindow);
721 break;
722 case EWT_DIALOG:
723 DialogPeriodicProc(element->editorWindow);
724 break;
725 }
726 }
727 }
728
EditorQuit()729 void EditorQuit()
730 // This is called from the editor when it wants to quit
731 // it needs to inform the event loop that it should stop looping
732 {
733 timeToQuit=true; // set the quit request flag
734 }
735
EditorDoBackground()736 void EditorDoBackground()
737 // do the background tasks of updating windows, tasks and timers
738 {
739 ShellDoBackground(); // call the shell to do anything it wants
740 UpdateEditorWindows(); // update anything that is invalid now
741 if(!UpdateBufferTasks()) // see if any buffers have tasks that need to be serviced/updated
742 {
743 // when no tasks needed service, give time back to the machine
744 pause(); // wait for alarm signal to wake us up again
745 }
746 if(blinkTimeout==0) // time to flash cursor
747 {
748 blinkTimeout=BLINK_TIMEOUT_CONTINUE; // reset the timer
749 DoPeriodicProc();
750 }
751 }
752
EditorEventLoop(int argc,char * argv[])753 void EditorEventLoop(int argc,char *argv[])
754 // Get the next event that the editor needs to know about and return it
755 // if other events occur, this routine should stay here handling them until
756 // one specifically related to the editor arrives, it should then pass that
757 // event up to the editor for processing
758 // if no events arrive, it is the responsibility of this routine to
759 // give time back to the OS if necessary.
760 {
761 XEvent
762 event;
763
764 HandleShellCommand("initialargs",argc-1,&(argv[1])); // pass off initial arguments to shell for processing
765
766 while(!timeToQuit)
767 {
768 if(XCheckIfEvent(xDisplay,&event,AllButUpdateEventsPredicate,NULL))
769 {
770 DoEvent(&event);
771 }
772 else
773 {
774 EditorDoBackground(); // handle background stuff
775 }
776 }
777 }
778
DialogEventLoop(EDITOR_WINDOW * dialog,bool * dialogComplete)779 void DialogEventLoop(EDITOR_WINDOW *dialog,bool *dialogComplete)
780 // When a dialog is up, this handles getting events, it does not send any
781 // events other than update to document windows, or the menus
782 // ALL events are either discarded, or given to the current dialog window
783 // dialogComplete must be cleared when calling this, and it
784 // must be set for this to return
785 // NOTE: it is possible for this loop to be called BEFORE EditorEventLoop
786 // has ever been executed, since dialogs can be brought up during the
787 // startup script (which executes before the main event loop is called for the
788 // first time)
789 {
790 XEvent
791 event;
792
793 while(!(*dialogComplete))
794 {
795 if(XCheckIfEvent(xDisplay,&event,AllButUpdateEventsPredicate,NULL))
796 {
797 DoDialogEvent(dialog,&event);
798 }
799 else
800 {
801 EditorDoBackground(); // handle background stuff
802 }
803 }
804 }
805
AbortEventPredicate(Display * display,XEvent * event,char * arg)806 static Bool AbortEventPredicate(Display *display,XEvent *event,char *arg)
807 // Check the Event to see if it is an abort event
808 // if it is, return True
809 // NOTE: since this is an event predicate, it is not allowed to
810 // call back into XLib (See XCheckIfEvent man page).
811 // Because of this, we cannot call XLookupString, and must instead
812 // compare keycodes directly.
813 {
814 if((event->type==KeyPress))
815 {
816 if(((XKeyEvent *)event)->keycode==abortKeyCode)
817 {
818 return(True);
819 }
820 }
821 return(False);
822 }
823
ClearAbort()824 void ClearAbort()
825 // call this to clear any pending abort status
826 {
827 XEvent
828 event;
829
830 while(XCheckIfEvent(xDisplay,&event,AbortEventPredicate,NULL)) // get all abort events out of the queue
831 ;
832 nextAbortCheckTime=timer+ABORT_CHECK_INTERVAL; // set next time it will be ok to check for abort
833 }
834
CheckAbort()835 bool CheckAbort()
836 // This will return true if the user is requesting an abortion of the
837 // current job
838 // This should be made to execute as efficiently as possible, since it will
839 // be called often during certain loops
840 // To make it more efficient, this version checks the time, and if enough time
841 // has not passed, returns instantly
842 {
843 XEvent
844 event;
845
846 if(timer>=nextAbortCheckTime)
847 {
848 nextAbortCheckTime=timer+ABORT_CHECK_INTERVAL; // reset the interval
849 if(XCheckIfEvent(xDisplay,&event,AbortEventPredicate,NULL)) // see if there is an abort event floating around in the queue
850 {
851 return(true);
852 }
853 }
854 return(false);
855 }
856
UnInitEvents()857 void UnInitEvents()
858 // Uninitialize event stuff
859 {
860 }
861
InitEvents()862 bool InitEvents()
863 // Initialize event stuff
864 // if there is a problem, SetError, return false
865 {
866 waitTimer=0; // init the wait timer
867 abortKeyCode=XKeysymToKeycode(xDisplay,XK_Escape); // work out the escape keycode (since we cannot call XLib during an event predicate to work out the keysym from the keycode)
868 ResetMultipleClicks();
869 return(true);
870 }
871