event_core.c revision 0bbfda8a
1/*
2 * Core event handling
3 *
4 *       Copyright 1988 by Evans & Sutherland Computer Corporation,
5 *                          Salt Lake City, Utah
6 *  Portions Copyright 1989 by the Massachusetts Institute of Technology
7 *                        Cambridge, Massachusetts
8 *
9 * Copyright 1992 Claude Lecommandeur.
10 */
11
12/***********************************************************************
13 *
14 * $XConsortium: events.c,v 1.182 91/07/17 13:59:14 dave Exp $
15 *
16 * twm event handling
17 *
18 * 17-Nov-87 Thomas E. LaStrange                File created
19 *
20 * Do the necessary modification to be integrated in ctwm.
21 * Can no longer be used for the standard twm.
22 *
23 * 22-April-92 Claude Lecommandeur.
24 *
25 *
26 ***********************************************************************/
27
28#include "ctwm.h"
29
30#include <stdio.h>
31#include <stdlib.h>
32#include <errno.h>
33#include <sys/time.h>
34
35#include <X11/extensions/shape.h>
36
37#include "animate.h"
38#include "captive.h"
39#include "colormaps.h"
40#include "events.h"
41#include "event_handlers.h"
42#include "event_internal.h"
43#include "event_names.h"
44#include "functions.h"
45#include "iconmgr.h"
46#include "image.h"
47#include "screen.h"
48#include "util.h"
49#include "version.h"
50#include "win_utils.h"
51#ifdef SOUNDS
52#include "sound.h"
53#endif
54
55
56static void CtwmNextEvent(Display *display, XEvent  *event);
57static bool StashEventTime(XEvent *ev);
58static void dumpevent(const XEvent *e);
59
60#define MAX_X_EVENT 256
61event_proc EventHandler[MAX_X_EVENT]; /* event handler jump table */
62int Context = C_NO_CONTEXT;     /* current button press context */
63XEvent Event;                   /* the current event */
64
65Window DragWindow;              /* variables used in moving windows */
66int origDragX;
67int origDragY;
68int DragX;
69int DragY;
70unsigned int DragWidth;
71unsigned int DragHeight;
72unsigned int DragBW;
73int CurrentDragX;
74int CurrentDragY;
75
76Time EventTime = CurrentTime;       /* until Xlib does this for us */
77
78/* Maybe more staticizable later? */
79bool enter_flag;
80bool leave_flag;
81TwmWindow *enter_win, *raise_win, *leave_win, *lower_win;
82
83/*
84 * Not static because shared with colormaps.c and other events files, but
85 * not listed in events.h since nowhere else needs it.
86 */
87bool ColortableThrashing;
88TwmWindow *Tmp_win; // the current twm window; shared with other event code
89
90int ButtonPressed = -1;
91bool Cancel = false;
92
93
94
95/*
96 * Initialize the event handling bits.  Mainly the jump table for event
97 * handling, plus various event vars.  Called during startup.
98 */
99void
100InitEvents(void)
101{
102	/* Clear out vars */
103	ResizeWindow = (Window) 0;
104	DragWindow = (Window) 0;
105	enter_flag = false;
106	enter_win = raise_win = NULL;
107	leave_flag = false;
108	leave_win = lower_win = NULL;
109
110	/* Set everything to unknown to start */
111	for(int i = 0; i < MAX_X_EVENT; i++) {
112		EventHandler[i] = HandleUnknown;
113	}
114
115#define STDH(evt) EventHandler[evt] = Handle##evt
116
117	/* Init the standard events */
118	STDH(Expose);
119	STDH(CreateNotify);
120	STDH(DestroyNotify);
121	STDH(MapRequest);
122	STDH(MapNotify);
123	STDH(UnmapNotify);
124	STDH(MotionNotify);
125	STDH(ButtonRelease);
126	STDH(ButtonPress);
127	STDH(EnterNotify);
128	STDH(LeaveNotify);
129	STDH(ConfigureRequest);
130	STDH(ClientMessage);
131	STDH(PropertyNotify);
132	STDH(KeyPress);
133	STDH(KeyRelease);
134	STDH(ColormapNotify);
135	STDH(VisibilityNotify);
136	STDH(CirculateNotify);
137
138	/* Focus handlers are special; see comment on HandleFocusChange() */
139	EventHandler[FocusIn] = HandleFocusChange;
140	EventHandler[FocusOut] = HandleFocusChange;
141
142	/* Some more conditional bits */
143#ifdef EWMH
144	STDH(SelectionClear);
145#endif
146
147	/*
148	 * Optional extensions are registered dynamically, so their events
149	 * are put by the X server at some offset into the event number tree
150	 * when it starts.
151	 */
152	if(HasShape) {
153		EventHandler[ShapeEventBase + ShapeNotify] = HandleShapeNotify;
154	}
155
156#undef STDH
157
158	/* And done */
159	return;
160}
161
162
163
164/*
165 * Main event loop.
166 *
167 * This is the last thing called during ctwm startup, and never returns.
168 * So in essence, this is everything ctwm does except starting up and
169 * shutting down (and that latter winds up called through us as well).
170 */
171void
172HandleEvents(void)
173{
174	while(1) {
175		if(enter_flag && !QLength(dpy)) {
176			if(enter_win && enter_win != raise_win) {
177				AutoRaiseWindow(enter_win);   /* sets enter_flag T */
178			}
179			else {
180				enter_flag = false;
181			}
182		}
183		if(leave_flag && !QLength(dpy)) {
184			if(leave_win && leave_win != lower_win) {
185				AutoLowerWindow(leave_win);  /* sets leave_flag T */
186			}
187			else {
188				leave_flag = false;
189			}
190		}
191		if(ColortableThrashing && !QLength(dpy) && Scr) {
192			InstallColormaps(ColormapNotify, NULL);
193		}
194		WindowMoved = false;
195
196		CtwmNextEvent(dpy, &Event);
197
198		if(Event.type < 0 || Event.type >= MAX_X_EVENT) {
199			XtDispatchEvent(&Event);
200		}
201		else {
202			DispatchEvent();
203		}
204	}
205
206	/* NOTREACHED */
207	fprintf(stderr, "Error: Should never reach the end of HandleEvents()\n");
208	exit(1);
209}
210
211
212/*
213 * Grab the next event in the queue to process.
214 */
215static void
216CtwmNextEvent(Display *display, XEvent *event)
217{
218	int         fd;
219	struct timeval timeout, *tout = NULL;
220	const bool animate = (AnimationActive && MaybeAnimate);
221
222#define NEXTEVENT XtAppNextEvent(appContext, event)
223
224	if(RestartFlag) {
225		DoRestart(CurrentTime);
226	}
227	if(XEventsQueued(display, QueuedAfterFlush) != 0) {
228		NEXTEVENT;
229		return;
230	}
231	fd = ConnectionNumber(display);
232
233	if(animate) {
234		TryToAnimate();
235	}
236	if(RestartFlag) {
237		DoRestart(CurrentTime);
238	}
239	if(! MaybeAnimate) {
240		NEXTEVENT;
241		return;
242	}
243	if(animate) {
244		tout = (AnimationSpeed > 0) ? &timeout : NULL;
245	}
246	while(1) {
247		fd_set mask;
248		int found;
249
250		FD_ZERO(&mask);
251		FD_SET(fd, &mask);
252		if(animate) {
253			timeout = AnimateTimeout;
254		}
255		found = select(fd + 1, &mask, NULL, NULL, tout);
256		if(RestartFlag) {
257			DoRestart(CurrentTime);
258		}
259		if(found < 0) {
260			if(errno != EINTR) {
261				perror("select");
262			}
263			continue;
264		}
265		if(FD_ISSET(fd, &mask)) {
266			NEXTEVENT;
267			return;
268		}
269		if(found == 0) {
270			if(animate) {
271				TryToAnimate();
272			}
273			if(RestartFlag) {
274				DoRestart(CurrentTime);
275			}
276			if(! MaybeAnimate) {
277				NEXTEVENT;
278				return;
279			}
280			continue;
281		}
282	}
283
284#undef NEXTEVENT
285
286	/* NOTREACHED */
287}
288
289
290
291/*
292 * And dispatchers.  These look at the global Event and run with it from
293 * there.
294 *
295 * There are slight differences between the two, and it's not at all
296 * clear to what extent there should be or why they're different.  At
297 * least some of the difference seem gratuitous; this requires further
298 * research.  They probably can and should be collapsed together.
299 */
300/* Main dispatcher, from the loop above and a few other places */
301bool
302DispatchEvent(void)
303{
304	Window w = Event.xany.window;
305	ScreenInfo *thisScr;
306
307	StashEventTime(&Event);
308	Tmp_win = GetTwmWindow(w);
309	thisScr = GetTwmScreen(&Event);
310
311	dumpevent(&Event);
312
313	if(!thisScr) {
314		return false;
315	}
316	Scr = thisScr;
317
318	if(CLarg.is_captive) {
319		if((Event.type == ConfigureNotify)
320		                && (Event.xconfigure.window == Scr->CaptiveRoot)) {
321			ConfigureCaptiveRootWindow(&Event);
322			return false;
323		}
324	}
325	FixRootEvent(&Event);
326	if(Event.type >= 0 && Event.type < MAX_X_EVENT) {
327#ifdef SOUNDS
328		play_sound(Event.type);
329#endif
330		(*EventHandler[Event.type])();
331	}
332	return true;
333}
334
335
336/* Alternate; called from various function and menu code */
337bool
338DispatchEvent2(void)
339{
340	Window w = Event.xany.window;
341	ScreenInfo *thisScr;
342
343	StashEventTime(&Event);
344	Tmp_win = GetTwmWindow(w);
345	thisScr = GetTwmScreen(&Event);
346
347	dumpevent(&Event);
348
349	if(!thisScr) {
350		return false;
351	}
352	Scr = thisScr;
353
354	FixRootEvent(&Event);
355
356#ifdef SOUNDS
357	play_sound(Event.type);
358#endif
359
360	if(menuFromFrameOrWindowOrTitlebar) {
361		if(Event.type == Expose) {
362			HandleExpose();
363		}
364	}
365	else {
366		if(Event.type >= 0 && Event.type < MAX_X_EVENT) {
367			(*EventHandler[Event.type])();
368		}
369	}
370
371	return true;
372}
373
374
375
376
377/*
378 * Stash the time of the given event in our EventTime global.  Called
379 * during dispatching the event.
380 */
381static bool
382StashEventTime(XEvent *ev)
383{
384	switch(ev->type) {
385		case KeyPress:
386		case KeyRelease:
387			EventTime = ev->xkey.time;
388			return true;
389		case ButtonPress:
390		case ButtonRelease:
391			EventTime = ev->xbutton.time;
392			return true;
393		case MotionNotify:
394			EventTime = ev->xmotion.time;
395			return true;
396		case EnterNotify:
397		case LeaveNotify:
398			EventTime = ev->xcrossing.time;
399			return true;
400		case PropertyNotify:
401			EventTime = ev->xproperty.time;
402			return true;
403		case SelectionClear:
404			EventTime = ev->xselectionclear.time;
405			return true;
406		case SelectionRequest:
407			EventTime = ev->xselectionrequest.time;
408			return true;
409		case SelectionNotify:
410			EventTime = ev->xselection.time;
411			return true;
412	}
413	return false;
414}
415
416
417/*
418 * Debugging: output details of an event.  Or at least, details of a few
419 * events, presumably those which somebody doing debugging needed at the
420 * time.
421 */
422static void
423dumpevent(const XEvent *e)
424{
425	const char *name;
426
427	if(! tracefile) {
428		return;
429	}
430
431	/* Whatsit? */
432	name = event_name_by_num(e->type);
433	if(!name) {
434		name = "Unknown event";
435	}
436
437	/* Tell about it */
438	fprintf(tracefile, "event:  %s in window 0x%x\n", name,
439	        (unsigned int)e->xany.window);
440	switch(e->type) {
441		case KeyPress:
442		case KeyRelease:
443			fprintf(tracefile, "     :  +%d,+%d (+%d,+%d)  state=%d, keycode=%d\n",
444			        e->xkey.x, e->xkey.y,
445			        e->xkey.x_root, e->xkey.y_root,
446			        e->xkey.state, e->xkey.keycode);
447			break;
448		case ButtonPress:
449		case ButtonRelease:
450			fprintf(tracefile, "     :  +%d,+%d (+%d,+%d)  state=%d, button=%d\n",
451			        e->xbutton.x, e->xbutton.y,
452			        e->xbutton.x_root, e->xbutton.y_root,
453			        e->xbutton.state, e->xbutton.button);
454			break;
455	}
456}
457