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