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