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