1 /*
2  * Copyright (C) 2000 Sasha Vasko <sasha at aftercode.net>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program 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  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17  *
18  */
19 
20 #include "../configure.h"
21 #include "asapp.h"
22 #include "../libAfterImage/afterimage.h"
23 #include "afterstep.h"
24 #include "screen.h"
25 #include "event.h"
26 
27 #ifdef XSHMIMAGE
28 #include <sys/ipc.h>
29 #include <sys/shm.h>
30 #include <X11/extensions/XShm.h>
31 #endif
32 
33 static Bool as_xserver_is_local = False;	/* True if we are running on the same host as X Server */
34 
35 /* This is used for error handling : */
36 int last_event_type = 0;
37 Window last_event_window = 0;
38 
39 
40 #define FullStructureNotifyMask (SubstructureNotifyMask|StructureNotifyMask)
41 
42 static ASEventDescription _as_event_types[LASTEvent] = {
43 /*  */ {"nothing", 0},
44 /*  */ {"nothing", 0},
45 /* KeyPress			2  */ {"KeyPress", KeyPressMask, ASE_KeyboardEvent},
46 /* KeyRelease		3  */ {"KeyRelease", KeyReleaseMask, ASE_KeyboardEvent},
47 /* ButtonPress		4  */ {"ButtonPress", ButtonPressMask,
48 											 ASE_MousePressEvent},
49 /* ButtonRelease	5  */ {"ButtonRelease", ButtonReleaseMask,
50 												ASE_MousePressEvent},
51 /* MotionNotify		6  */ {"MotionNotify", PointerMotionMask,
52 												ASE_MouseMotionEvent},
53 /* EnterNotify		7  */ {"EnterNotify", EnterWindowMask,
54 											 ASE_MouseMotionEvent},
55 /* LeaveNotify		8  */ {"LeaveNotify", LeaveWindowMask,
56 											 ASE_MouseMotionEvent},
57 /* FocusIn			9  */ {"FocusIn", FocusChangeMask, 0},
58 /* FocusOut			10 */ {"FocusOut", FocusChangeMask, 0},
59 /* KeymapNotify		11 */ {"KeymapNotify", KeymapStateMask, 0},
60 /* Expose			12 */ {"Expose", ExposureMask, ASE_Expose},
61 /* GraphicsExpose	13 */ {"GraphicsExpose", ExposureMask, ASE_Expose},
62 /* NoExpose			14 */ {"NoExpose", ExposureMask, ASE_Expose},
63 /* VisibilityNotify	15 */ {"VisibilityNotify", VisibilityChangeMask, 0},
64 /* CreateNotify		16 */ {"CreateNotify", SubstructureNotifyMask, 0},
65 /* DestroyNotify	17 */ {"DestroyNotify", FullStructureNotifyMask, 0},
66 /* UnmapNotify		18 */ {"UnmapNotify", FullStructureNotifyMask, 0},
67 /* MapNotify		19 */ {"MapNotify", FullStructureNotifyMask, 0},
68 /* MapRequest		20 */ {"MapRequest", SubstructureRedirectMask, 0},
69 /* ReparentNotify	21 */ {"ReparentNotify", FullStructureNotifyMask, 0},
70 /* ConfigureNotify	22 */ {"ConfigureNotify", FullStructureNotifyMask,
71 													ASE_Config},
72 /* ConfigureRequest	23 */ {"ConfigureRequest", SubstructureRedirectMask,
73 													 0},
74 /* GravityNotify	24 */ {"GravityNotify", FullStructureNotifyMask, 0},
75 /* ResizeRequest	25 */ {"ResizeRequest", ResizeRedirectMask, 0},
76 /* CirculateNotify	26 */ {"CirculateNotify", FullStructureNotifyMask,
77 													0},
78 /* CirculateRequest	27 */ {"CirculateRequest", SubstructureRedirectMask,
79 													 0},
80 /* PropertyNotify	28 */ {"PropertyNotify", PropertyChangeMask, 0},
81 /* SelectionClear	29 */ {"SelectionClear", SelectionMask, 0},
82 /* SelectionRequest	30 */ {"SelectionRequest", SelectionMask, 0},
83 /* SelectionNotify	31 */ {"SelectionNotify", SelectionMask, 0},
84 /* ColormapNotify	32 */ {"ColormapNotify", ColormapChangeMask, 0},
85 /* ClientMessage	33 */ {"ClientMessage", ClientMask, 0},
86 /* MappingNotify	34 */ {"MappingNotify", MappingMask, 0}
87 };
88 
89 static struct ContextDescr {
90 	int context;
91 	char *name;
92 } context_description[] = {
93 #define CONTEXT_DESCR(ctx)  {ctx, #ctx}
94 	CONTEXT_DESCR (C_NO_CONTEXT),
95 			CONTEXT_DESCR (C_FrameN),
96 			CONTEXT_DESCR (C_FrameE),
97 			CONTEXT_DESCR (C_FrameS),
98 			CONTEXT_DESCR (C_FrameW),
99 			CONTEXT_DESCR (C_FrameNW),
100 			CONTEXT_DESCR (C_FrameNE),
101 			CONTEXT_DESCR (C_FrameSW),
102 			CONTEXT_DESCR (C_FrameSE),
103 			CONTEXT_DESCR (C_SIDEBAR),
104 			CONTEXT_DESCR (C_VERTICAL_SIDEBAR),
105 			CONTEXT_DESCR (C_FRAME),
106 			CONTEXT_DESCR (C_FrameStart),
107 			CONTEXT_DESCR (C_FrameEnd),
108 			CONTEXT_DESCR (C_WINDOW),
109 			CONTEXT_DESCR (C_CLIENT),
110 			CONTEXT_DESCR (C_TITLE),
111 			CONTEXT_DESCR (C_IconTitle),
112 			CONTEXT_DESCR (C_IconButton),
113 			CONTEXT_DESCR (C_ICON),
114 			CONTEXT_DESCR (C_ROOT),
115 			CONTEXT_DESCR (C_TButton0),
116 			CONTEXT_DESCR (C_TButton1),
117 			CONTEXT_DESCR (C_TButton2),
118 			CONTEXT_DESCR (C_TButton3),
119 			CONTEXT_DESCR (C_TButton4),
120 			CONTEXT_DESCR (C_TButton5),
121 			CONTEXT_DESCR (C_TButton6),
122 			CONTEXT_DESCR (C_TButton7),
123 			CONTEXT_DESCR (C_TButton8), CONTEXT_DESCR (C_TButton9),
124 			CONTEXT_DESCR (C_TButtonAll), CONTEXT_DESCR (C_ALL), {
125 	-1, NULL}
126 };
127 
128 /***************************************************************************
129  * we need to prepare message handlers :
130  ***************************************************************************/
event_setup(Bool local)131 void event_setup (Bool local)
132 {
133 	register int i;
134 	XEvent event;
135 
136 	as_xserver_is_local = local;
137 	/* adding out handlers here : */
138 	for (i = 0; i < LASTEvent; ++i) {
139 		_as_event_types[i].time_offset = _as_event_types[i].window_offset =
140 				_as_event_types[i].last_time = 0;
141 		if (i >= KeyPress && i <= LeaveNotify) {
142 			_as_event_types[i].time_offset =
143 					(char *)&(event.xkey.time) - (char *)&(event);
144 /*			WE want the actuall window we selected mask on here,
145 			not some lame subwindow !
146 
147 			_as_event_types[i].window_offset =
148 				 (char*)&(event.xkey.subwindow) - (char*)&(event); */
149 
150 		} else if ((i >= CreateNotify && i <= GravityNotify)
151 							 || (i >= CirculateNotify && i <= CirculateRequest))
152 			/*CreateNotify, DestroyNotify, UnmapNotify, MapNotify, MapRequest,
153 			 *ReparentNotify, ConfigureNotify, ConfigureRequest, GravityNotify */
154 		{
155 			_as_event_types[i].window_offset =
156 					(char *)&(event.xcreatewindow.window) - (char *)&(event);
157 		}
158 	}
159 	_as_event_types[PropertyNotify].time_offset =
160 			(char *)&(event.xproperty.time) - (char *)&(event);
161 	_as_event_types[SelectionClear].time_offset =
162 			(char *)&(event.xselectionclear.time) - (char *)&(event);
163 	_as_event_types[SelectionRequest].time_offset =
164 			(char *)&(event.xselectionrequest.time) - (char *)&(event);
165 	_as_event_types[SelectionNotify].time_offset =
166 			(char *)&(event.xselection.time) - (char *)&(event);
167 }
168 
event_type2name(int type)169 const char *event_type2name (int type)
170 {
171 	if (type > 0 && type < LASTEvent)
172 		return _as_event_types[type].name;
173 	return "unknown";
174 }
175 
context2text(int ctx)176 const char *context2text (int ctx)
177 {
178 	register int i = -1;
179 
180 	while (context_description[++i].name)
181 		if (context_description[i].context == ctx)
182 			return context_description[i].name;
183 	return "unknown";
184 }
185 
186 /***********************************************************************
187  * Event Queue management : 										   *
188  ***********************************************************************/
flush_event_queue(Bool check_pending)189 void flush_event_queue (Bool check_pending)
190 {
191 	if (check_pending)
192 		if (XPending (dpy))
193 			return;
194 	XFlush (dpy);
195 }
196 
sync_event_queue(Bool forget)197 void sync_event_queue (Bool forget)
198 {
199 	XSync (dpy, forget);
200 }
201 
202 /****************************************************************************
203  * Records the time of the last processed event. Used in XSetInputFocus
204  ****************************************************************************/
stash_event_time(XEvent * xevent)205 inline Time stash_event_time (XEvent * xevent)
206 {
207 	if (xevent->type < LASTEvent) {
208 		register Time *ptime =
209 				(Time *) ((char *)xevent +
210 									_as_event_types[xevent->type].time_offset);
211 
212 		last_event_type = xevent->type;
213 		last_event_window = xevent->xany.window;
214 
215 		if (ptime != (Time *) xevent) {
216 			register Time NewTimestamp = *ptime;
217 
218 			if (NewTimestamp < ASDefaultScr->last_Timestamp) {
219 				if (as_xserver_is_local) {	/* hack to detect local time change and try to work around it */
220 					static time_t last_system_time = 0;
221 					time_t curr_time;
222 
223 					if (time (&curr_time) < last_system_time) {	/* local time has been changed !!!!!!!! */
224 						ASDefaultScr->last_Timestamp = NewTimestamp;
225 						ASDefaultScr->menu_grab_Timestamp = NewTimestamp;
226 					}
227 					last_system_time = curr_time;
228 				}
229 				if (ASDefaultScr->last_Timestamp - NewTimestamp > 0x7FFFFFFF) {	/* detecting time lapse */
230 					ASDefaultScr->last_Timestamp = NewTimestamp;
231 					if (ASDefaultScr->menu_grab_Timestamp - NewTimestamp >
232 							0x7FFFFFFF)
233 						ASDefaultScr->menu_grab_Timestamp = 0;
234 				}
235 			} else
236 				ASDefaultScr->last_Timestamp = NewTimestamp;
237 			return (_as_event_types[xevent->type].last_time = *ptime);
238 		}
239 	}
240 	return 0;
241 }
242 
243 /* here we will determine what screen event occured on : */
query_event_screen(register XEvent * event)244 inline ScreenInfo *query_event_screen (register XEvent * event)
245 {																/* stub since stable AS does not support multiscreen handling in one process */
246 	return ASDefaultScr;
247 }
248 
get_xevent_window(XEvent * xevt)249 Window get_xevent_window (XEvent * xevt)
250 {
251 	int type = xevt->type;
252 
253 	if (type < LASTEvent) {
254 		register Window *pwin =
255 				(Window *) ((char *)xevt + _as_event_types[type].window_offset);
256 
257 		return (pwin == (Window *) xevt
258 						|| *pwin == None) ? xevt->xany.window : *pwin;
259 	} else
260 		return xevt->xany.window;
261 }
262 
setup_asevent_from_xevent(ASEvent * event)263 void setup_asevent_from_xevent (ASEvent * event)
264 {
265 	XEvent *xevt = &(event->x);
266 	int type = xevt->type;
267 
268 	if (type < LASTEvent) {
269 		register Time *ptime =
270 				(Time *) ((char *)xevt + _as_event_types[type].time_offset);
271 		register Window *pwin =
272 				(Window *) ((char *)xevt + _as_event_types[type].window_offset);
273 
274 		event->w = (pwin == (Window *) xevt
275 								|| *pwin == None) ? xevt->xany.window : *pwin;
276 		event->event_time = (ptime == (Time *) xevt) ? 0 : *ptime;
277 
278 		event->scr = ASEventScreen (xevt);
279 		event->mask = _as_event_types[type].mask;
280 		event->eclass = _as_event_types[type].event_class;
281 		event->last_time = _as_event_types[type].last_time;
282 	} else {
283 		event->w = xevt->xany.window;
284 		event->event_time = 0;
285 
286 		event->scr = ASDefaultScr;
287 		event->mask = 0;
288 		event->eclass = 0;
289 		event->last_time = 0;
290 	}
291 }
292 
293 /**********************************************************************/
294 /* window management specifics - mapping/unmapping with no events :   */
295 /**********************************************************************/
296 
add_window_event_mask(Window w,long event_mask)297 void add_window_event_mask (Window w, long event_mask)
298 {
299 	XWindowAttributes attr;
300 
301 	if (XGetWindowAttributes (dpy, w, &attr))
302 		XSelectInput (dpy, w, attr.your_event_mask | event_mask);
303 }
304 
305 
quietly_unmap_window(Window w,long event_mask)306 void quietly_unmap_window (Window w, long event_mask)
307 {
308 	/* blocking UnmapNotify events since that may bring us into Withdrawn state */
309 	XSelectInput (dpy, w, event_mask & ~StructureNotifyMask);
310 	XUnmapWindow (dpy, w);
311 	XSelectInput (dpy, w, event_mask);
312 }
313 
314 void
quietly_reparent_window(Window w,Window new_parent,int x,int y,long event_mask)315 quietly_reparent_window (Window w, Window new_parent, int x, int y,
316 												 long event_mask)
317 {
318 	/* blocking UnmapNotify events since that may bring us into Withdrawn state */
319 	XSelectInput (dpy, w, event_mask & ~StructureNotifyMask);
320 	XReparentWindow (dpy, w,
321 									 (new_parent != None) ? new_parent : ASDefaultRoot, x,
322 									 y);
323 	XSelectInput (dpy, w, event_mask);
324 }
325 
Empty_XErrorHandler(Display * dpy,XErrorEvent * event)326 int Empty_XErrorHandler (Display * dpy, XErrorEvent * event)
327 {
328 	return 0;
329 }
330 
safely_destroy_window(Window w)331 void safely_destroy_window (Window w)
332 {
333 	int (*old_handler) (Display * dpy, XErrorEvent * event);
334 
335 	old_handler = XSetErrorHandler (Empty_XErrorHandler);
336 	XDestroyWindow (dpy, w);
337 	XSync (dpy, False);
338 	XSetErrorHandler (old_handler);
339 }
340 
341 Bool
query_pointer(Window w,Window * root_return,Window * child_return,int * root_x_return,int * root_y_return,int * win_x_return,int * win_y_return,unsigned int * mask_return)342 query_pointer (Window w,
343 							 Window * root_return, Window * child_return,
344 							 int *root_x_return, int *root_y_return, int *win_x_return,
345 							 int *win_y_return, unsigned int *mask_return)
346 {
347 	Window root, child;
348 	int root_x, root_y;
349 	int win_x, win_y;
350 	unsigned int mask;
351 
352 	if (!XQueryPointer
353 			(dpy, ((w == None) ? ASDefaultRoot : w), &root, &child, &root_x,
354 			 &root_y, &win_x, &win_y, &mask))
355 		return False;
356 
357 	if (root_return)
358 		*root_return = root;
359 	if (child_return)
360 		*child_return = child;
361 	if (root_x_return)
362 		*root_x_return = root_x;
363 	if (root_y_return)
364 		*root_y_return = root_y;
365 	if (win_x_return)
366 		*win_x_return = win_x;
367 	if (win_y_return)
368 		*win_y_return = win_y;
369 	if (mask_return)
370 		*mask_return = mask;
371 
372 	return True;
373 }
374 
375 /**********************************************************************/
376 /* Low level interfaces : 											  */
377 /**********************************************************************/
378 Bool
check_event_masked(register long mask,register XEvent * event_return)379 check_event_masked (register long mask, register XEvent * event_return)
380 {
381 	register int res;
382 
383 	res = XCheckMaskEvent (dpy, mask, event_return);
384 	if (res)
385 		stash_event_time (event_return);
386 	return res;
387 }
388 
389 Bool
check_event_typed(register int event_type,register XEvent * event_return)390 check_event_typed (register int event_type, register XEvent * event_return)
391 {
392 	register int res;
393 
394 	res = XCheckTypedEvent (dpy, event_type, event_return);
395 	if (res)
396 		stash_event_time (event_return);
397 	return res;
398 }
399 
400 Bool
check_event_typed_windowed(Window w,int event_type,register XEvent * event_return)401 check_event_typed_windowed (Window w, int event_type,
402 														register XEvent * event_return)
403 {
404 	register int res;
405 
406 	res = XCheckTypedWindowEvent (dpy, w, event_type, event_return);
407 	if (res)
408 		stash_event_time (event_return);
409 	return res;
410 }
411 
412 Bool
check_event_windowed(Window w,long event_mask,register XEvent * event_return)413 check_event_windowed (Window w, long event_mask,
414 											register XEvent * event_return)
415 {
416 	register int res;
417 
418 	res = XCheckWindowEvent (dpy, w, event_mask, event_return);
419 	if (res)
420 		stash_event_time (event_return);
421 	return res;
422 }
423 
recursively_find_motion_notify(int depth)424 Bool recursively_find_motion_notify (int depth)
425 {
426 	XEvent junk_event;
427 
428 	if (XCheckMaskEvent (dpy, 0xFFFFFFFF, &junk_event)) {
429 		XPutBackEvent (dpy, &junk_event);
430 		if (junk_event.type == MotionNotify)
431 			return True;
432 		if (depth > 0)
433 			return recursively_find_motion_notify (depth - 1);
434 	}
435 	return False;
436 }
437 
next_event(register XEvent * event_return,Bool compress_motion)438 int next_event (register XEvent * event_return, Bool compress_motion)
439 {
440 	register int res;
441 
442 	res = (XNextEvent (dpy, event_return) == 0);
443 	if (res) {
444 		stash_event_time (event_return);
445 #if 0
446 		if (compress_motion && event_return->type == MotionNotify) {
447 			if (recursively_find_motion_notify (5))
448 				return (False);
449 			XFlush (dpy);
450 			if (recursively_find_motion_notify (5))
451 				return (False);
452 			sleep_a_millisec (20);		/* 0.3 sec delay */
453 			if (recursively_find_motion_notify (10))
454 				return False;
455 		}
456 #endif
457 	}
458 	return res;
459 }
460 
peek_event(register XEvent * event_return)461 int peek_event (register XEvent * event_return)
462 {
463 	register int res;
464 
465 	res = XPeekEvent (dpy, event_return);
466 	if (res)
467 		stash_event_time (event_return);
468 	return res;
469 }
470 
mask_event(long event_mask,register XEvent * event_return)471 int mask_event (long event_mask, register XEvent * event_return)
472 {
473 	register int res;
474 
475 	res = XMaskEvent (dpy, event_mask, event_return);
476 	if (res)
477 		stash_event_time (event_return);
478 	return res;
479 }
480 
481 int
window_event(Window w,long event_mask,register XEvent * event_return)482 window_event (Window w, long event_mask, register XEvent * event_return)
483 {
484 	register int res;
485 
486 	res = XWindowEvent (dpy, w, event_mask, event_return);
487 	if (res)
488 		stash_event_time (event_return);
489 	return res;
490 }
491 
wait_event(XEvent * event,Window w,int mask,int max_wait)492 Bool wait_event (XEvent * event, Window w, int mask, int max_wait)
493 {
494 	int tick_count;
495 
496 	start_ticker (1);
497 	/* now we have to wait for our window to become mapped - waiting for PropertyNotify */
498 	for (tick_count = 0;
499 			 !XCheckWindowEvent (dpy, w, mask, event) && tick_count < max_wait;
500 			 tick_count++) {
501 		XSync (dpy, False);
502 		wait_tick ();
503 	}
504 	return (tick_count < max_wait);
505 }
506 
handle_ShmCompletion(ASEvent * event)507 void handle_ShmCompletion (ASEvent * event)
508 {
509 #ifdef XSHMIMAGE
510 	XShmCompletionEvent *sev = (XShmCompletionEvent *) & (event->x);
511 
512 	LOCAL_DEBUG_OUT ("XSHMIMAGE> EVENT : offset   %ld(%lx), shmseg = %lx",
513 									 (long)sev->offset, (unsigned long)(sev->offset),
514 									 sev->shmseg);
515 	destroy_xshm_segment (sev->shmseg);
516 #endif													/* SHAPE */
517 }
518