]> Dogcows Code - chaz/openbox/blob - openbox/event.c
Merge branch 'backport' into work
[chaz/openbox] / openbox / event.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3 event.c for the Openbox window manager
4 Copyright (c) 2006 Mikael Magnusson
5 Copyright (c) 2003-2007 Dana Jansens
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 See the COPYING file for a copy of the GNU General Public License.
18 */
19
20 #include "event.h"
21 #include "debug.h"
22 #include "window.h"
23 #include "openbox.h"
24 #include "dock.h"
25 #include "actions.h"
26 #include "client.h"
27 #include "config.h"
28 #include "screen.h"
29 #include "frame.h"
30 #include "grab.h"
31 #include "menu.h"
32 #include "prompt.h"
33 #include "menuframe.h"
34 #include "keyboard.h"
35 #include "mouse.h"
36 #include "focus.h"
37 #include "focus_cycle.h"
38 #include "moveresize.h"
39 #include "group.h"
40 #include "stacking.h"
41 #include "ping.h"
42 #include "obt/display.h"
43 #include "obt/prop.h"
44 #include "obt/keyboard.h"
45
46 #include <X11/Xlib.h>
47 #include <X11/Xatom.h>
48 #include <glib.h>
49
50 #ifdef HAVE_SYS_SELECT_H
51 # include <sys/select.h>
52 #endif
53 #ifdef HAVE_SIGNAL_H
54 # include <signal.h>
55 #endif
56 #ifdef HAVE_UNISTD_H
57 # include <unistd.h> /* for usleep() */
58 #endif
59 #ifdef XKB
60 # include <X11/XKBlib.h>
61 #endif
62
63 #ifdef USE_SM
64 #include <X11/ICE/ICElib.h>
65 #endif
66
67 typedef struct
68 {
69 gboolean ignored;
70 } ObEventData;
71
72 typedef struct
73 {
74 ObClient *client;
75 Time time;
76 gulong serial;
77 } ObFocusDelayData;
78
79 typedef struct
80 {
81 gulong start; /* inclusive */
82 gulong end; /* inclusive */
83 } ObSerialRange;
84
85 static void event_process(const XEvent *e, gpointer data);
86 static void event_handle_root(XEvent *e);
87 static gboolean event_handle_menu_input(XEvent *e);
88 static void event_handle_menu(ObMenuFrame *frame, XEvent *e);
89 static gboolean event_handle_prompt(ObPrompt *p, XEvent *e);
90 static void event_handle_dock(ObDock *s, XEvent *e);
91 static void event_handle_dockapp(ObDockApp *app, XEvent *e);
92 static void event_handle_client(ObClient *c, XEvent *e);
93 static void event_handle_user_input(ObClient *client, XEvent *e);
94 static gboolean is_enter_focus_event_ignored(gulong serial);
95 static void event_ignore_enter_range(gulong start, gulong end);
96
97 static void focus_delay_dest(gpointer data);
98 static gboolean focus_delay_cmp(gconstpointer d1, gconstpointer d2);
99 static gboolean focus_delay_func(gpointer data);
100 static gboolean unfocus_delay_func(gpointer data);
101 static void focus_delay_client_dest(ObClient *client, gpointer data);
102
103 Time event_curtime = CurrentTime;
104 Time event_last_user_time = CurrentTime;
105 /*! The serial of the current X event */
106
107 static gulong event_curserial;
108 static gboolean focus_left_screen = FALSE;
109 /*! A list of ObSerialRanges which are to be ignored for mouse enter events */
110 static GSList *ignore_serials = NULL;
111
112 #ifdef USE_SM
113 static void ice_handler(gint fd, gpointer conn)
114 {
115 Bool b;
116 IceProcessMessages(conn, NULL, &b);
117 }
118
119 static void ice_watch(IceConn conn, IcePointer data, Bool opening,
120 IcePointer *watch_data)
121 {
122 static gint fd = -1;
123
124 if (opening) {
125 fd = IceConnectionNumber(conn);
126 obt_main_loop_fd_add(ob_main_loop, fd, ice_handler, conn, NULL);
127 } else {
128 obt_main_loop_fd_remove(ob_main_loop, fd);
129 fd = -1;
130 }
131 }
132 #endif
133
134 void event_startup(gboolean reconfig)
135 {
136 if (reconfig) return;
137
138 obt_main_loop_x_add(ob_main_loop, event_process, NULL, NULL);
139
140 #ifdef USE_SM
141 IceAddConnectionWatch(ice_watch, NULL);
142 #endif
143
144 client_add_destroy_notify(focus_delay_client_dest, NULL);
145 }
146
147 void event_shutdown(gboolean reconfig)
148 {
149 if (reconfig) return;
150
151 #ifdef USE_SM
152 IceRemoveConnectionWatch(ice_watch, NULL);
153 #endif
154
155 client_remove_destroy_notify(focus_delay_client_dest);
156 }
157
158 static Window event_get_window(XEvent *e)
159 {
160 Window window;
161
162 /* pick a window */
163 switch (e->type) {
164 case SelectionClear:
165 window = obt_root(ob_screen);
166 break;
167 case CreateNotify:
168 window = e->xcreatewindow.window;
169 break;
170 case MapRequest:
171 window = e->xmaprequest.window;
172 break;
173 case MapNotify:
174 window = e->xmap.window;
175 break;
176 case UnmapNotify:
177 window = e->xunmap.window;
178 break;
179 case DestroyNotify:
180 window = e->xdestroywindow.window;
181 break;
182 case ConfigureRequest:
183 window = e->xconfigurerequest.window;
184 break;
185 case ConfigureNotify:
186 window = e->xconfigure.window;
187 break;
188 default:
189 #ifdef XKB
190 if (obt_display_extension_xkb &&
191 e->type == obt_display_extension_xkb_basep)
192 {
193 switch (((XkbAnyEvent*)e)->xkb_type) {
194 case XkbBellNotify:
195 window = ((XkbBellNotifyEvent*)e)->window;
196 default:
197 window = None;
198 }
199 } else
200 #endif
201 #ifdef SYNC
202 if (obt_display_extension_sync &&
203 e->type == obt_display_extension_sync_basep + XSyncAlarmNotify)
204 {
205 window = None;
206 } else
207 #endif
208 window = e->xany.window;
209 }
210 return window;
211 }
212
213 static void event_set_curtime(XEvent *e)
214 {
215 Time t = CurrentTime;
216
217 /* grab the lasttime and hack up the state */
218 switch (e->type) {
219 case ButtonPress:
220 case ButtonRelease:
221 t = e->xbutton.time;
222 break;
223 case KeyPress:
224 t = e->xkey.time;
225 break;
226 case KeyRelease:
227 t = e->xkey.time;
228 break;
229 case MotionNotify:
230 t = e->xmotion.time;
231 break;
232 case PropertyNotify:
233 t = e->xproperty.time;
234 break;
235 case EnterNotify:
236 case LeaveNotify:
237 t = e->xcrossing.time;
238 break;
239 default:
240 #ifdef SYNC
241 if (obt_display_extension_sync &&
242 e->type == obt_display_extension_sync_basep + XSyncAlarmNotify)
243 {
244 t = ((XSyncAlarmNotifyEvent*)e)->time;
245 }
246 #endif
247 /* if more event types are anticipated, get their timestamp
248 explicitly */
249 break;
250 }
251
252 /* watch that if we get an event earlier than the last specified user_time,
253 which can happen if the clock goes backwards, we erase the last
254 specified user_time */
255 if (t && event_last_user_time && event_time_after(event_last_user_time, t))
256 event_last_user_time = CurrentTime;
257
258 event_curtime = t;
259 }
260
261 static void event_hack_mods(XEvent *e)
262 {
263 #ifdef XKB
264 XkbStateRec xkb_state;
265 #endif
266
267 switch (e->type) {
268 case ButtonPress:
269 case ButtonRelease:
270 e->xbutton.state = obt_keyboard_only_modmasks(e->xbutton.state);
271 break;
272 case KeyPress:
273 e->xkey.state = obt_keyboard_only_modmasks(e->xkey.state);
274 break;
275 case KeyRelease:
276 #ifdef XKB
277 /* If XKB is present, then the modifiers are all strange from its
278 magic. Our X core protocol stuff won't work, so we use this to
279 find what the modifier state is instead. */
280 if (XkbGetState(obt_display, XkbUseCoreKbd, &xkb_state) == Success)
281 e->xkey.state =
282 obt_keyboard_only_modmasks(xkb_state.compat_state);
283 else
284 #endif
285 {
286 e->xkey.state = obt_keyboard_only_modmasks(e->xkey.state);
287 /* remove from the state the mask of the modifier key being
288 released, if it is a modifier key being released that is */
289 e->xkey.state &= ~obt_keyboard_keycode_to_modmask(e->xkey.keycode);
290 }
291 break;
292 case MotionNotify:
293 e->xmotion.state = obt_keyboard_only_modmasks(e->xmotion.state);
294 /* compress events */
295 {
296 XEvent ce;
297 while (XCheckTypedWindowEvent(obt_display, e->xmotion.window,
298 e->type, &ce)) {
299 e->xmotion.x = ce.xmotion.x;
300 e->xmotion.y = ce.xmotion.y;
301 e->xmotion.x_root = ce.xmotion.x_root;
302 e->xmotion.y_root = ce.xmotion.y_root;
303 }
304 }
305 break;
306 }
307 }
308
309 static gboolean wanted_focusevent(XEvent *e, gboolean in_client_only)
310 {
311 gint mode = e->xfocus.mode;
312 gint detail = e->xfocus.detail;
313 Window win = e->xany.window;
314
315 if (e->type == FocusIn) {
316 /* These are ones we never want.. */
317
318 /* This means focus was given by a keyboard/mouse grab. */
319 if (mode == NotifyGrab)
320 return FALSE;
321 /* This means focus was given back from a keyboard/mouse grab. */
322 if (mode == NotifyUngrab)
323 return FALSE;
324
325 /* These are the ones we want.. */
326
327 if (win == obt_root(ob_screen)) {
328 /* If looking for a focus in on a client, then always return
329 FALSE for focus in's to the root window */
330 if (in_client_only)
331 return FALSE;
332 /* This means focus reverted off of a client */
333 else if (detail == NotifyPointerRoot ||
334 detail == NotifyDetailNone ||
335 detail == NotifyInferior ||
336 /* This means focus got here from another screen */
337 detail == NotifyNonlinear)
338 return TRUE;
339 else
340 return FALSE;
341 }
342
343 /* It was on a client, was it a valid one?
344 It's possible to get a FocusIn event for a client that was managed
345 but has disappeared.
346 */
347 if (in_client_only) {
348 ObWindow *w = window_find(e->xfocus.window);
349 if (!w || !WINDOW_IS_CLIENT(w))
350 return FALSE;
351 }
352 else {
353 /* This means focus reverted to parent from the client (this
354 happens often during iconify animation) */
355 if (detail == NotifyInferior)
356 return TRUE;
357 }
358
359 /* This means focus moved from the root window to a client */
360 if (detail == NotifyVirtual)
361 return TRUE;
362 /* This means focus moved from one client to another */
363 if (detail == NotifyNonlinearVirtual)
364 return TRUE;
365
366 /* Otherwise.. */
367 return FALSE;
368 } else {
369 g_assert(e->type == FocusOut);
370
371 /* These are ones we never want.. */
372
373 /* This means focus was taken by a keyboard/mouse grab. */
374 if (mode == NotifyGrab)
375 return FALSE;
376 /* This means focus was grabbed on a window and it was released. */
377 if (mode == NotifyUngrab)
378 return FALSE;
379
380 /* Focus left the root window revertedto state */
381 if (win == obt_root(ob_screen))
382 return FALSE;
383
384 /* These are the ones we want.. */
385
386 /* This means focus moved from a client to the root window */
387 if (detail == NotifyVirtual)
388 return TRUE;
389 /* This means focus moved from one client to another */
390 if (detail == NotifyNonlinearVirtual)
391 return TRUE;
392
393 /* Otherwise.. */
394 return FALSE;
395 }
396 }
397
398 static Bool event_look_for_focusin(Display *d, XEvent *e, XPointer arg)
399 {
400 return e->type == FocusIn && wanted_focusevent(e, FALSE);
401 }
402
403 static Bool event_look_for_focusin_client(Display *d, XEvent *e, XPointer arg)
404 {
405 return e->type == FocusIn && wanted_focusevent(e, TRUE);
406 }
407
408 static void print_focusevent(XEvent *e)
409 {
410 gint mode = e->xfocus.mode;
411 gint detail = e->xfocus.detail;
412 Window win = e->xany.window;
413 const gchar *modestr, *detailstr;
414
415 switch (mode) {
416 case NotifyNormal: modestr="NotifyNormal"; break;
417 case NotifyGrab: modestr="NotifyGrab"; break;
418 case NotifyUngrab: modestr="NotifyUngrab"; break;
419 case NotifyWhileGrabbed: modestr="NotifyWhileGrabbed"; break;
420 default: g_assert_not_reached();
421 }
422 switch (detail) {
423 case NotifyAncestor: detailstr="NotifyAncestor"; break;
424 case NotifyVirtual: detailstr="NotifyVirtual"; break;
425 case NotifyInferior: detailstr="NotifyInferior"; break;
426 case NotifyNonlinear: detailstr="NotifyNonlinear"; break;
427 case NotifyNonlinearVirtual: detailstr="NotifyNonlinearVirtual"; break;
428 case NotifyPointer: detailstr="NotifyPointer"; break;
429 case NotifyPointerRoot: detailstr="NotifyPointerRoot"; break;
430 case NotifyDetailNone: detailstr="NotifyDetailNone"; break;
431 default: g_assert_not_reached();
432 }
433
434 if (mode == NotifyGrab || mode == NotifyUngrab)
435 return;
436
437 g_assert(modestr);
438 g_assert(detailstr);
439 ob_debug_type(OB_DEBUG_FOCUS, "Focus%s 0x%x mode=%s detail=%s",
440 (e->xfocus.type == FocusIn ? "In" : "Out"),
441 win,
442 modestr, detailstr);
443
444 }
445
446 static gboolean event_ignore(XEvent *e, ObClient *client)
447 {
448 switch(e->type) {
449 case FocusIn:
450 print_focusevent(e);
451 if (!wanted_focusevent(e, FALSE))
452 return TRUE;
453 break;
454 case FocusOut:
455 print_focusevent(e);
456 if (!wanted_focusevent(e, FALSE))
457 return TRUE;
458 break;
459 }
460 return FALSE;
461 }
462
463 static void event_process(const XEvent *ec, gpointer data)
464 {
465 XEvent ee, *e;
466 ObEventData *ed = data;
467
468 Window window;
469 ObClient *client = NULL;
470 ObDock *dock = NULL;
471 ObDockApp *dockapp = NULL;
472 ObWindow *obwin = NULL;
473 ObMenuFrame *menu = NULL;
474 ObPrompt *prompt = NULL;
475
476 /* make a copy we can mangle */
477 ee = *ec;
478 e = &ee;
479
480 window = event_get_window(e);
481 if (window == obt_root(ob_screen))
482 /* don't do any lookups, waste of cpu */;
483 else if ((obwin = window_find(window))) {
484 switch (obwin->type) {
485 case OB_WINDOW_CLASS_DOCK:
486 dock = WINDOW_AS_DOCK(obwin);
487 break;
488 case OB_WINDOW_CLASS_CLIENT:
489 client = WINDOW_AS_CLIENT(obwin);
490 /* events on clients can be events on prompt windows too */
491 prompt = client->prompt;
492 break;
493 case OB_WINDOW_CLASS_MENUFRAME:
494 menu = WINDOW_AS_MENUFRAME(obwin);
495 break;
496 case OB_WINDOW_CLASS_INTERNAL:
497 /* we don't do anything with events directly on these windows */
498 break;
499 case OB_WINDOW_CLASS_PROMPT:
500 prompt = WINDOW_AS_PROMPT(obwin);
501 break;
502 }
503 }
504 else
505 dockapp = dock_find_dockapp(window);
506
507 event_set_curtime(e);
508 event_curserial = e->xany.serial;
509 event_hack_mods(e);
510 if (event_ignore(e, client)) {
511 if (ed)
512 ed->ignored = TRUE;
513 return;
514 } else if (ed)
515 ed->ignored = FALSE;
516
517 /* deal with it in the kernel */
518
519 if (e->type == FocusIn) {
520 if (client &&
521 e->xfocus.detail == NotifyInferior)
522 {
523 ob_debug_type(OB_DEBUG_FOCUS,
524 "Focus went to the frame window");
525
526 focus_left_screen = FALSE;
527
528 focus_fallback(FALSE, config_focus_under_mouse, TRUE, TRUE);
529
530 /* We don't get a FocusOut for this case, because it's just moving
531 from our Inferior up to us. This happens when iconifying a
532 window with RevertToParent focus */
533 frame_adjust_focus(client->frame, FALSE);
534 /* focus_set_client(NULL) has already been called */
535 }
536 else if (e->xfocus.detail == NotifyPointerRoot ||
537 e->xfocus.detail == NotifyDetailNone ||
538 e->xfocus.detail == NotifyInferior ||
539 e->xfocus.detail == NotifyNonlinear)
540 {
541 XEvent ce;
542
543 ob_debug_type(OB_DEBUG_FOCUS,
544 "Focus went to root or pointer root/none");
545
546 if (e->xfocus.detail == NotifyInferior ||
547 e->xfocus.detail == NotifyNonlinear)
548 {
549 focus_left_screen = FALSE;
550 }
551
552 /* If another FocusIn is in the queue then don't fallback yet. This
553 fixes the fun case of:
554 window map -> send focusin
555 window unmap -> get focusout
556 window map -> send focusin
557 get first focus out -> fall back to something (new window
558 hasn't received focus yet, so something else) -> send focusin
559 which means the "something else" is the last thing to get a
560 focusin sent to it, so the new window doesn't end up with focus.
561
562 But if the other focus in is something like PointerRoot then we
563 still want to fall back.
564 */
565 if (XCheckIfEvent(obt_display, &ce, event_look_for_focusin_client,
566 NULL))
567 {
568 XPutBackEvent(obt_display, &ce);
569 ob_debug_type(OB_DEBUG_FOCUS,
570 " but another FocusIn is coming");
571 } else {
572 /* Focus has been reverted.
573
574 FocusOut events come after UnmapNotify, so we don't need to
575 worry about focusing an invalid window
576 */
577
578 if (!focus_left_screen)
579 focus_fallback(FALSE, config_focus_under_mouse,
580 TRUE, TRUE);
581 }
582 }
583 else if (!client)
584 {
585 ob_debug_type(OB_DEBUG_FOCUS,
586 "Focus went to a window that is already gone");
587
588 /* If you send focus to a window and then it disappears, you can
589 get the FocusIn for it, after it is unmanaged.
590 Just wait for the next FocusOut/FocusIn pair, but make note that
591 the window that was focused no longer is. */
592 focus_set_client(NULL);
593 }
594 else if (client != focus_client) {
595 focus_left_screen = FALSE;
596 frame_adjust_focus(client->frame, TRUE);
597 focus_set_client(client);
598 client_calc_layer(client);
599 client_bring_helper_windows(client);
600 }
601 } else if (e->type == FocusOut) {
602 XEvent ce;
603
604 /* Look for the followup FocusIn */
605 if (!XCheckIfEvent(obt_display, &ce, event_look_for_focusin, NULL)) {
606 /* There is no FocusIn, this means focus went to a window that
607 is not being managed, or a window on another screen. */
608 Window win, root;
609 gint i;
610 guint u;
611 obt_display_ignore_errors(TRUE);
612 if (XGetInputFocus(obt_display, &win, &i) &&
613 XGetGeometry(obt_display, win, &root, &i,&i,&u,&u,&u,&u) &&
614 root != obt_root(ob_screen))
615 {
616 ob_debug_type(OB_DEBUG_FOCUS,
617 "Focus went to another screen !");
618 focus_left_screen = TRUE;
619 }
620 else
621 ob_debug_type(OB_DEBUG_FOCUS,
622 "Focus went to a black hole !");
623 obt_display_ignore_errors(FALSE);
624 /* nothing is focused */
625 focus_set_client(NULL);
626 } else {
627 /* Focus moved, so process the FocusIn event */
628 ObEventData ed = { .ignored = FALSE };
629 event_process(&ce, &ed);
630 if (ed.ignored) {
631 /* The FocusIn was ignored, this means it was on a window
632 that isn't a client. */
633 ob_debug_type(OB_DEBUG_FOCUS,
634 "Focus went to an unmanaged window 0x%x !",
635 ce.xfocus.window);
636 focus_fallback(TRUE, config_focus_under_mouse, TRUE, TRUE);
637 }
638 }
639
640 if (client && client != focus_client) {
641 frame_adjust_focus(client->frame, FALSE);
642 /* focus_set_client(NULL) has already been called in this
643 section or by focus_fallback */
644 }
645 }
646 else if (client)
647 event_handle_client(client, e);
648 else if (dockapp)
649 event_handle_dockapp(dockapp, e);
650 else if (dock)
651 event_handle_dock(dock, e);
652 else if (menu)
653 event_handle_menu(menu, e);
654 else if (window == obt_root(ob_screen))
655 event_handle_root(e);
656 else if (e->type == MapRequest)
657 window_manage(window);
658 else if (e->type == MappingNotify) {
659 /* keyboard layout changes for modifier mapping changes. reload the
660 modifier map, and rebind all the key bindings as appropriate */
661 ob_debug("Keyboard map changed. Reloading keyboard bindings.");
662 ob_set_state(OB_STATE_RECONFIGURING);
663 obt_keyboard_reload();
664 keyboard_rebind();
665 ob_set_state(OB_STATE_RUNNING);
666 }
667 else if (e->type == ClientMessage) {
668 /* This is for _NET_WM_REQUEST_FRAME_EXTENTS messages. They come for
669 windows that are not managed yet. */
670 if (e->xclient.message_type ==
671 OBT_PROP_ATOM(NET_REQUEST_FRAME_EXTENTS))
672 {
673 /* Pretend to manage the client, getting information used to
674 determine its decorations */
675 ObClient *c = client_fake_manage(e->xclient.window);
676 gulong vals[4];
677
678 /* set the frame extents on the window */
679 vals[0] = c->frame->size.left;
680 vals[1] = c->frame->size.right;
681 vals[2] = c->frame->size.top;
682 vals[3] = c->frame->size.bottom;
683 OBT_PROP_SETA32(e->xclient.window, NET_FRAME_EXTENTS,
684 CARDINAL, vals, 4);
685
686 /* Free the pretend client */
687 client_fake_unmanage(c);
688 }
689 }
690 else if (e->type == ConfigureRequest) {
691 /* unhandled configure requests must be used to configure the
692 window directly */
693 XWindowChanges xwc;
694
695 xwc.x = e->xconfigurerequest.x;
696 xwc.y = e->xconfigurerequest.y;
697 xwc.width = e->xconfigurerequest.width;
698 xwc.height = e->xconfigurerequest.height;
699 xwc.border_width = e->xconfigurerequest.border_width;
700 xwc.sibling = e->xconfigurerequest.above;
701 xwc.stack_mode = e->xconfigurerequest.detail;
702
703 /* we are not to be held responsible if someone sends us an
704 invalid request! */
705 obt_display_ignore_errors(TRUE);
706 XConfigureWindow(obt_display, window,
707 e->xconfigurerequest.value_mask, &xwc);
708 obt_display_ignore_errors(FALSE);
709 }
710 #ifdef SYNC
711 else if (obt_display_extension_sync &&
712 e->type == obt_display_extension_sync_basep + XSyncAlarmNotify)
713 {
714 XSyncAlarmNotifyEvent *se = (XSyncAlarmNotifyEvent*)e;
715 if (se->alarm == moveresize_alarm && moveresize_in_progress)
716 moveresize_event(e);
717 }
718 #endif
719
720 if (prompt && event_handle_prompt(prompt, e))
721 ;
722 else if (e->type == ButtonPress || e->type == ButtonRelease) {
723 /* If the button press was on some non-root window, or was physically
724 on the root window, then process it */
725 if (window != obt_root(ob_screen) ||
726 e->xbutton.subwindow == None)
727 {
728 event_handle_user_input(client, e);
729 }
730 /* Otherwise only process it if it was physically on an openbox
731 internal window */
732 else {
733 ObWindow *w;
734
735 if ((w = window_find(e->xbutton.subwindow)) &&
736 WINDOW_IS_INTERNAL(w))
737 {
738 event_handle_user_input(client, e);
739 }
740 }
741 }
742 else if (e->type == KeyPress || e->type == KeyRelease ||
743 e->type == MotionNotify)
744 event_handle_user_input(client, e);
745
746 /* if something happens and it's not from an XEvent, then we don't know
747 the time */
748 event_curtime = CurrentTime;
749 event_curserial = 0;
750 }
751
752 static void event_handle_root(XEvent *e)
753 {
754 Atom msgtype;
755
756 switch(e->type) {
757 case SelectionClear:
758 ob_debug("Another WM has requested to replace us. Exiting.");
759 ob_exit_replace();
760 break;
761
762 case ClientMessage:
763 if (e->xclient.format != 32) break;
764
765 msgtype = e->xclient.message_type;
766 if (msgtype == OBT_PROP_ATOM(NET_CURRENT_DESKTOP)) {
767 guint d = e->xclient.data.l[0];
768 if (d < screen_num_desktops) {
769 event_curtime = e->xclient.data.l[1];
770 if (event_curtime == 0)
771 ob_debug_type(OB_DEBUG_APP_BUGS,
772 "_NET_CURRENT_DESKTOP message is missing "
773 "a timestamp");
774 screen_set_desktop(d, TRUE);
775 }
776 } else if (msgtype == OBT_PROP_ATOM(NET_NUMBER_OF_DESKTOPS)) {
777 guint d = e->xclient.data.l[0];
778 if (d > 0 && d <= 1000)
779 screen_set_num_desktops(d);
780 } else if (msgtype == OBT_PROP_ATOM(NET_SHOWING_DESKTOP)) {
781 screen_show_desktop(e->xclient.data.l[0] != 0, NULL);
782 } else if (msgtype == OBT_PROP_ATOM(OB_CONTROL)) {
783 ob_debug("OB_CONTROL: %d", e->xclient.data.l[0]);
784 if (e->xclient.data.l[0] == 1)
785 ob_reconfigure();
786 else if (e->xclient.data.l[0] == 2)
787 ob_restart();
788 else if (e->xclient.data.l[0] == 3)
789 ob_exit(0);
790 } else if (msgtype == OBT_PROP_ATOM(WM_PROTOCOLS)) {
791 if ((Atom)e->xclient.data.l[0] == OBT_PROP_ATOM(NET_WM_PING))
792 ping_got_pong(e->xclient.data.l[1]);
793 }
794 break;
795 case PropertyNotify:
796 if (e->xproperty.atom == OBT_PROP_ATOM(NET_DESKTOP_NAMES)) {
797 ob_debug("UPDATE DESKTOP NAMES");
798 screen_update_desktop_names();
799 }
800 else if (e->xproperty.atom == OBT_PROP_ATOM(NET_DESKTOP_LAYOUT))
801 screen_update_layout();
802 break;
803 case ConfigureNotify:
804 #ifdef XRANDR
805 XRRUpdateConfiguration(e);
806 #endif
807 screen_resize();
808 break;
809 default:
810 ;
811 }
812 }
813
814 void event_enter_client(ObClient *client)
815 {
816 g_assert(config_focus_follow);
817
818 if (is_enter_focus_event_ignored(event_curserial)) {
819 ob_debug_type(OB_DEBUG_FOCUS, "Ignoring enter event with serial %lu\n"
820 "on client 0x%x", event_curserial, client->window);
821 return;
822 }
823
824 if (client_enter_focusable(client) && client_can_focus(client)) {
825 if (config_focus_delay) {
826 ObFocusDelayData *data;
827
828 obt_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
829
830 data = g_new(ObFocusDelayData, 1);
831 data->client = client;
832 data->time = event_curtime;
833 data->serial = event_curserial;
834
835 obt_main_loop_timeout_add(ob_main_loop,
836 config_focus_delay * 1000,
837 focus_delay_func,
838 data, focus_delay_cmp, focus_delay_dest);
839 } else {
840 ObFocusDelayData data;
841 data.client = client;
842 data.time = event_curtime;
843 data.serial = event_curserial;
844 focus_delay_func(&data);
845 }
846 }
847 }
848
849 void event_leave_client(ObClient *client)
850 {
851 g_assert(config_focus_follow);
852
853 if (is_enter_focus_event_ignored(event_curserial)) {
854 ob_debug_type(OB_DEBUG_FOCUS, "Ignoring leave event with serial %lu\n"
855 "on client 0x%x", event_curserial, client->window);
856 return;
857 }
858
859 if (client == focus_client) {
860 if (config_focus_delay) {
861 ObFocusDelayData *data;
862
863 obt_main_loop_timeout_remove(ob_main_loop, unfocus_delay_func);
864
865 data = g_new(ObFocusDelayData, 1);
866 data->client = client;
867 data->time = event_curtime;
868 data->serial = event_curserial;
869
870 obt_main_loop_timeout_add(ob_main_loop,
871 config_focus_delay * 1000,
872 unfocus_delay_func,
873 data, focus_delay_cmp, focus_delay_dest);
874 } else {
875 ObFocusDelayData data;
876 data.client = client;
877 data.time = event_curtime;
878 data.serial = event_curserial;
879 unfocus_delay_func(&data);
880 }
881 }
882 }
883
884 static gboolean *context_to_button(ObFrame *f, ObFrameContext con, gboolean press)
885 {
886 if (press) {
887 switch (con) {
888 case OB_FRAME_CONTEXT_MAXIMIZE:
889 return &f->max_press;
890 case OB_FRAME_CONTEXT_CLOSE:
891 return &f->close_press;
892 case OB_FRAME_CONTEXT_ICONIFY:
893 return &f->iconify_press;
894 case OB_FRAME_CONTEXT_ALLDESKTOPS:
895 return &f->desk_press;
896 case OB_FRAME_CONTEXT_SHADE:
897 return &f->shade_press;
898 default:
899 return NULL;
900 }
901 } else {
902 switch (con) {
903 case OB_FRAME_CONTEXT_MAXIMIZE:
904 return &f->max_hover;
905 case OB_FRAME_CONTEXT_CLOSE:
906 return &f->close_hover;
907 case OB_FRAME_CONTEXT_ICONIFY:
908 return &f->iconify_hover;
909 case OB_FRAME_CONTEXT_ALLDESKTOPS:
910 return &f->desk_hover;
911 case OB_FRAME_CONTEXT_SHADE:
912 return &f->shade_hover;
913 default:
914 return NULL;
915 }
916 }
917 }
918
919 static void compress_client_message_event(XEvent *e, XEvent *ce, Window window,
920 Atom msgtype)
921 {
922 /* compress changes into a single change */
923 while (XCheckTypedWindowEvent(obt_display, window, e->type, ce)) {
924 /* XXX: it would be nice to compress ALL messages of a
925 type, not just messages in a row without other
926 message types between. */
927 if (ce->xclient.message_type != msgtype) {
928 XPutBackEvent(obt_display, ce);
929 break;
930 }
931 e->xclient = ce->xclient;
932 }
933 }
934
935 static void event_handle_client(ObClient *client, XEvent *e)
936 {
937 XEvent ce;
938 Atom msgtype;
939 ObFrameContext con;
940 gboolean *but;
941 static gint px = -1, py = -1;
942 static guint pb = 0;
943 static ObFrameContext pcon = OB_FRAME_CONTEXT_NONE;
944
945 switch (e->type) {
946 case ButtonPress:
947 /* save where the press occured for the first button pressed */
948 if (!pb) {
949 pb = e->xbutton.button;
950 px = e->xbutton.x;
951 py = e->xbutton.y;
952
953 pcon = frame_context(client, e->xbutton.window, px, py);
954 pcon = mouse_button_frame_context(pcon, e->xbutton.button,
955 e->xbutton.state);
956 }
957 case ButtonRelease:
958 /* Wheel buttons don't draw because they are an instant click, so it
959 is a waste of resources to go drawing it.
960 if the user is doing an interactive thing, or has a menu open then
961 the mouse is grabbed (possibly) and if we get these events we don't
962 want to deal with them
963 */
964 if (!(e->xbutton.button == 4 || e->xbutton.button == 5) &&
965 !grab_on_keyboard())
966 {
967 /* use where the press occured */
968 con = frame_context(client, e->xbutton.window, px, py);
969 con = mouse_button_frame_context(con, e->xbutton.button,
970 e->xbutton.state);
971
972 /* button presses on CLIENT_CONTEXTs are not accompanied by a
973 release because they are Replayed to the client */
974 if ((e->type == ButtonRelease || CLIENT_CONTEXT(con, client)) &&
975 e->xbutton.button == pb)
976 pb = 0, px = py = -1, pcon = OB_FRAME_CONTEXT_NONE;
977
978 but = context_to_button(client->frame, con, TRUE);
979 if (but) {
980 *but = (e->type == ButtonPress);
981 frame_adjust_state(client->frame);
982 }
983 }
984 break;
985 case MotionNotify:
986 /* when there is a grab on the pointer, we won't get enter/leave
987 notifies, but we still get motion events */
988 if (grab_on_pointer()) break;
989
990 con = frame_context(client, e->xmotion.window,
991 e->xmotion.x, e->xmotion.y);
992 switch (con) {
993 case OB_FRAME_CONTEXT_TITLEBAR:
994 case OB_FRAME_CONTEXT_TLCORNER:
995 case OB_FRAME_CONTEXT_TRCORNER:
996 /* we've left the button area inside the titlebar */
997 if (client->frame->max_hover || client->frame->desk_hover ||
998 client->frame->shade_hover || client->frame->iconify_hover ||
999 client->frame->close_hover)
1000 {
1001 client->frame->max_hover =
1002 client->frame->desk_hover =
1003 client->frame->shade_hover =
1004 client->frame->iconify_hover =
1005 client->frame->close_hover = FALSE;
1006 frame_adjust_state(client->frame);
1007 }
1008 break;
1009 default:
1010 but = context_to_button(client->frame, con, FALSE);
1011 if (but && !*but && !pb) {
1012 *but = TRUE;
1013 frame_adjust_state(client->frame);
1014 }
1015 break;
1016 }
1017 break;
1018 case LeaveNotify:
1019 con = frame_context(client, e->xcrossing.window,
1020 e->xcrossing.x, e->xcrossing.y);
1021 switch (con) {
1022 case OB_FRAME_CONTEXT_TITLEBAR:
1023 case OB_FRAME_CONTEXT_TLCORNER:
1024 case OB_FRAME_CONTEXT_TRCORNER:
1025 /* we've left the button area inside the titlebar */
1026 client->frame->max_hover =
1027 client->frame->desk_hover =
1028 client->frame->shade_hover =
1029 client->frame->iconify_hover =
1030 client->frame->close_hover = FALSE;
1031 if (e->xcrossing.mode == NotifyGrab) {
1032 client->frame->max_press =
1033 client->frame->desk_press =
1034 client->frame->shade_press =
1035 client->frame->iconify_press =
1036 client->frame->close_press = FALSE;
1037 }
1038 break;
1039 case OB_FRAME_CONTEXT_FRAME:
1040 /* When the mouse leaves an animating window, don't use the
1041 corresponding enter events. Pretend like the animating window
1042 doesn't even exist..! */
1043 if (frame_iconify_animating(client->frame))
1044 event_end_ignore_all_enters(event_start_ignore_all_enters());
1045
1046 ob_debug_type(OB_DEBUG_FOCUS,
1047 "%sNotify mode %d detail %d on %lx",
1048 (e->type == EnterNotify ? "Enter" : "Leave"),
1049 e->xcrossing.mode,
1050 e->xcrossing.detail, (client?client->window:0));
1051 if (grab_on_keyboard())
1052 break;
1053 if (config_focus_follow &&
1054 /* leave inferior events can happen when the mouse goes onto
1055 the window's border and then into the window before the
1056 delay is up */
1057 e->xcrossing.detail != NotifyInferior)
1058 {
1059 if (config_focus_delay)
1060 obt_main_loop_timeout_remove_data(ob_main_loop,
1061 focus_delay_func,
1062 client, FALSE);
1063 if (config_unfocus_leave)
1064 event_leave_client(client);
1065 }
1066 break;
1067 default:
1068 but = context_to_button(client->frame, con, FALSE);
1069 if (but) {
1070 *but = FALSE;
1071 if (e->xcrossing.mode == NotifyGrab) {
1072 but = context_to_button(client->frame, con, TRUE);
1073 *but = FALSE;
1074 }
1075 frame_adjust_state(client->frame);
1076 }
1077 break;
1078 }
1079 break;
1080 case EnterNotify:
1081 {
1082 con = frame_context(client, e->xcrossing.window,
1083 e->xcrossing.x, e->xcrossing.y);
1084 switch (con) {
1085 case OB_FRAME_CONTEXT_FRAME:
1086 if (grab_on_keyboard())
1087 break;
1088 if (e->xcrossing.mode == NotifyGrab ||
1089 e->xcrossing.mode == NotifyUngrab ||
1090 /*ignore enters when we're already in the window */
1091 e->xcrossing.detail == NotifyInferior)
1092 {
1093 ob_debug_type(OB_DEBUG_FOCUS,
1094 "%sNotify mode %d detail %d serial %lu on %lx "
1095 "IGNORED",
1096 (e->type == EnterNotify ? "Enter" : "Leave"),
1097 e->xcrossing.mode,
1098 e->xcrossing.detail,
1099 e->xcrossing.serial,
1100 client?client->window:0);
1101 }
1102 else {
1103 ob_debug_type(OB_DEBUG_FOCUS,
1104 "%sNotify mode %d detail %d serial %lu on %lx, "
1105 "focusing window",
1106 (e->type == EnterNotify ? "Enter" : "Leave"),
1107 e->xcrossing.mode,
1108 e->xcrossing.detail,
1109 e->xcrossing.serial,
1110 (client?client->window:0));
1111 if (config_focus_follow) {
1112 if (config_focus_delay)
1113 obt_main_loop_timeout_remove_data(ob_main_loop,
1114 unfocus_delay_func,
1115 client, FALSE);
1116 event_enter_client(client);
1117 }
1118 }
1119 break;
1120 default:
1121 but = context_to_button(client->frame, con, FALSE);
1122 if (but) {
1123 *but = TRUE;
1124 if (e->xcrossing.mode == NotifyUngrab) {
1125 but = context_to_button(client->frame, con, TRUE);
1126 *but = (con == pcon);
1127 }
1128 frame_adjust_state(client->frame);
1129 }
1130 break;
1131 }
1132 break;
1133 }
1134 case ConfigureRequest:
1135 {
1136 /* dont compress these unless you're going to watch for property
1137 notifies in between (these can change what the configure would
1138 do to the window).
1139 also you can't compress stacking events
1140 */
1141
1142 gint x, y, w, h;
1143 gboolean move = FALSE;
1144 gboolean resize = FALSE;
1145
1146 /* get the current area */
1147 RECT_TO_DIMS(client->area, x, y, w, h);
1148
1149 ob_debug("ConfigureRequest for \"%s\" desktop %d wmstate %d "
1150 "visible %d",
1151 client->title,
1152 screen_desktop, client->wmstate, client->frame->visible);
1153 ob_debug(" x %d y %d w %d h %d b %d",
1154 x, y, w, h, client->border_width);
1155
1156 if (e->xconfigurerequest.value_mask & CWBorderWidth)
1157 if (client->border_width != e->xconfigurerequest.border_width) {
1158 client->border_width = e->xconfigurerequest.border_width;
1159
1160 /* if the border width is changing then that is the same
1161 as requesting a resize, but we don't actually change
1162 the client's border, so it will change their root
1163 coordinates (since they include the border width) and
1164 we need to a notify then */
1165 move = TRUE;
1166 }
1167
1168 if (e->xconfigurerequest.value_mask & CWStackMode) {
1169 ObClient *sibling = NULL;
1170 gulong ignore_start;
1171 gboolean ok = TRUE;
1172
1173 /* get the sibling */
1174 if (e->xconfigurerequest.value_mask & CWSibling) {
1175 ObWindow *win;
1176 win = window_find(e->xconfigurerequest.above);
1177 if (win && WINDOW_IS_CLIENT(win) &&
1178 WINDOW_AS_CLIENT(win) != client)
1179 {
1180 sibling = WINDOW_AS_CLIENT(win);
1181 }
1182 else
1183 /* an invalid sibling was specified so don't restack at
1184 all, it won't make sense no matter what we do */
1185 ok = FALSE;
1186 }
1187
1188 if (ok) {
1189 if (!config_focus_under_mouse)
1190 ignore_start = event_start_ignore_all_enters();
1191 stacking_restack_request(client, sibling,
1192 e->xconfigurerequest.detail);
1193 if (!config_focus_under_mouse)
1194 event_end_ignore_all_enters(ignore_start);
1195 }
1196
1197 /* a stacking change moves the window without resizing */
1198 move = TRUE;
1199 }
1200
1201 if ((e->xconfigurerequest.value_mask & CWX) ||
1202 (e->xconfigurerequest.value_mask & CWY) ||
1203 (e->xconfigurerequest.value_mask & CWWidth) ||
1204 (e->xconfigurerequest.value_mask & CWHeight))
1205 {
1206 /* don't allow clients to move shaded windows (fvwm does this)
1207 */
1208 if (e->xconfigurerequest.value_mask & CWX) {
1209 if (!client->shaded)
1210 x = e->xconfigurerequest.x;
1211 move = TRUE;
1212 }
1213 if (e->xconfigurerequest.value_mask & CWY) {
1214 if (!client->shaded)
1215 y = e->xconfigurerequest.y;
1216 move = TRUE;
1217 }
1218
1219 if (e->xconfigurerequest.value_mask & CWWidth) {
1220 w = e->xconfigurerequest.width;
1221 resize = TRUE;
1222 }
1223 if (e->xconfigurerequest.value_mask & CWHeight) {
1224 h = e->xconfigurerequest.height;
1225 resize = TRUE;
1226 }
1227 }
1228
1229 ob_debug("ConfigureRequest x(%d) %d y(%d) %d w(%d) %d h(%d) %d "
1230 "move %d resize %d",
1231 e->xconfigurerequest.value_mask & CWX, x,
1232 e->xconfigurerequest.value_mask & CWY, y,
1233 e->xconfigurerequest.value_mask & CWWidth, w,
1234 e->xconfigurerequest.value_mask & CWHeight, h,
1235 move, resize);
1236
1237 /* check for broken apps moving to their root position
1238
1239 XXX remove this some day...that would be nice. right now all
1240 kde apps do this when they try activate themselves on another
1241 desktop. eg. open amarok window on desktop 1, switch to desktop
1242 2, click amarok tray icon. it will move by its decoration size.
1243 */
1244 if (x != client->area.x &&
1245 x == (client->frame->area.x + client->frame->size.left -
1246 (gint)client->border_width) &&
1247 y != client->area.y &&
1248 y == (client->frame->area.y + client->frame->size.top -
1249 (gint)client->border_width) &&
1250 w == client->area.width &&
1251 h == client->area.height)
1252 {
1253 ob_debug_type(OB_DEBUG_APP_BUGS,
1254 "Application %s is trying to move via "
1255 "ConfigureRequest to it's root window position "
1256 "but it is not using StaticGravity",
1257 client->title);
1258 /* don't move it */
1259 x = client->area.x;
1260 y = client->area.y;
1261
1262 /* they still requested a move, so don't change whether a
1263 notify is sent or not */
1264 }
1265
1266 {
1267 gint lw, lh;
1268
1269 client_try_configure(client, &x, &y, &w, &h, &lw, &lh, FALSE);
1270
1271 /* if x was not given, then use gravity to figure out the new
1272 x. the reference point should not be moved */
1273 if ((e->xconfigurerequest.value_mask & CWWidth &&
1274 !(e->xconfigurerequest.value_mask & CWX)))
1275 client_gravity_resize_w(client, &x, client->area.width, w);
1276 /* same for y */
1277 if ((e->xconfigurerequest.value_mask & CWHeight &&
1278 !(e->xconfigurerequest.value_mask & CWY)))
1279 client_gravity_resize_h(client, &y, client->area.height,h);
1280
1281 client_find_onscreen(client, &x, &y, w, h, FALSE);
1282
1283 ob_debug("Granting ConfigureRequest x %d y %d w %d h %d",
1284 x, y, w, h);
1285 client_configure(client, x, y, w, h, FALSE, TRUE, TRUE);
1286 }
1287 break;
1288 }
1289 case UnmapNotify:
1290 ob_debug("UnmapNotify for window 0x%x eventwin 0x%x sendevent %d "
1291 "ignores left %d",
1292 client->window, e->xunmap.event, e->xunmap.from_configure,
1293 client->ignore_unmaps);
1294 if (client->ignore_unmaps) {
1295 client->ignore_unmaps--;
1296 break;
1297 }
1298 client_unmanage(client);
1299 break;
1300 case DestroyNotify:
1301 ob_debug("DestroyNotify for window 0x%x", client->window);
1302 client_unmanage(client);
1303 break;
1304 case ReparentNotify:
1305 /* this is when the client is first taken captive in the frame */
1306 if (e->xreparent.parent == client->frame->window) break;
1307
1308 /*
1309 This event is quite rare and is usually handled in unmapHandler.
1310 However, if the window is unmapped when the reparent event occurs,
1311 the window manager never sees it because an unmap event is not sent
1312 to an already unmapped window.
1313 */
1314
1315 /* we don't want the reparent event, put it back on the stack for the
1316 X server to deal with after we unmanage the window */
1317 XPutBackEvent(obt_display, e);
1318
1319 ob_debug("ReparentNotify for window 0x%x", client->window);
1320 client_unmanage(client);
1321 break;
1322 case MapRequest:
1323 ob_debug("MapRequest for 0x%lx", client->window);
1324 if (!client->iconic) break; /* this normally doesn't happen, but if it
1325 does, we don't want it!
1326 it can happen now when the window is on
1327 another desktop, but we still don't
1328 want it! */
1329 client_activate(client, FALSE, FALSE, TRUE, TRUE, TRUE);
1330 break;
1331 case ClientMessage:
1332 /* validate cuz we query stuff off the client here */
1333 if (!client_validate(client)) break;
1334
1335 if (e->xclient.format != 32) return;
1336
1337 msgtype = e->xclient.message_type;
1338 if (msgtype == OBT_PROP_ATOM(WM_CHANGE_STATE)) {
1339 compress_client_message_event(e, &ce, client->window, msgtype);
1340 client_set_wm_state(client, e->xclient.data.l[0]);
1341 } else if (msgtype == OBT_PROP_ATOM(NET_WM_DESKTOP)) {
1342 compress_client_message_event(e, &ce, client->window, msgtype);
1343 if ((unsigned)e->xclient.data.l[0] < screen_num_desktops ||
1344 (unsigned)e->xclient.data.l[0] == DESKTOP_ALL)
1345 client_set_desktop(client, (unsigned)e->xclient.data.l[0],
1346 FALSE, FALSE);
1347 } else if (msgtype == OBT_PROP_ATOM(NET_WM_STATE)) {
1348 gulong ignore_start;
1349
1350 /* can't compress these */
1351 ob_debug("net_wm_state %s %ld %ld for 0x%lx",
1352 (e->xclient.data.l[0] == 0 ? "Remove" :
1353 e->xclient.data.l[0] == 1 ? "Add" :
1354 e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
1355 e->xclient.data.l[1], e->xclient.data.l[2],
1356 client->window);
1357
1358 /* ignore enter events caused by these like ob actions do */
1359 if (!config_focus_under_mouse)
1360 ignore_start = event_start_ignore_all_enters();
1361 client_set_state(client, e->xclient.data.l[0],
1362 e->xclient.data.l[1], e->xclient.data.l[2]);
1363 if (!config_focus_under_mouse)
1364 event_end_ignore_all_enters(ignore_start);
1365 } else if (msgtype == OBT_PROP_ATOM(NET_CLOSE_WINDOW)) {
1366 ob_debug("net_close_window for 0x%lx", client->window);
1367 client_close(client);
1368 } else if (msgtype == OBT_PROP_ATOM(NET_ACTIVE_WINDOW)) {
1369 ob_debug("net_active_window for 0x%lx source=%s",
1370 client->window,
1371 (e->xclient.data.l[0] == 0 ? "unknown" :
1372 (e->xclient.data.l[0] == 1 ? "application" :
1373 (e->xclient.data.l[0] == 2 ? "user" : "INVALID"))));
1374 /* XXX make use of data.l[2] !? */
1375 if (e->xclient.data.l[0] == 1 || e->xclient.data.l[0] == 2) {
1376 event_curtime = e->xclient.data.l[1];
1377 if (e->xclient.data.l[1] == 0)
1378 ob_debug_type(OB_DEBUG_APP_BUGS,
1379 "_NET_ACTIVE_WINDOW message for window %s is"
1380 " missing a timestamp", client->title);
1381 } else
1382 ob_debug_type(OB_DEBUG_APP_BUGS,
1383 "_NET_ACTIVE_WINDOW message for window %s is "
1384 "missing source indication", client->title);
1385 client_activate(client, FALSE, FALSE, TRUE, TRUE,
1386 (e->xclient.data.l[0] == 0 ||
1387 e->xclient.data.l[0] == 2));
1388 } else if (msgtype == OBT_PROP_ATOM(NET_WM_MOVERESIZE)) {
1389 ob_debug("net_wm_moveresize for 0x%lx direction %d",
1390 client->window, e->xclient.data.l[2]);
1391 if ((Atom)e->xclient.data.l[2] ==
1392 OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPLEFT) ||
1393 (Atom)e->xclient.data.l[2] ==
1394 OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOP) ||
1395 (Atom)e->xclient.data.l[2] ==
1396 OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_TOPRIGHT) ||
1397 (Atom)e->xclient.data.l[2] ==
1398 OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_RIGHT) ||
1399 (Atom)e->xclient.data.l[2] ==
1400 OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_RIGHT) ||
1401 (Atom)e->xclient.data.l[2] ==
1402 OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT) ||
1403 (Atom)e->xclient.data.l[2] ==
1404 OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOM) ||
1405 (Atom)e->xclient.data.l[2] ==
1406 OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT) ||
1407 (Atom)e->xclient.data.l[2] ==
1408 OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_LEFT) ||
1409 (Atom)e->xclient.data.l[2] ==
1410 OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE) ||
1411 (Atom)e->xclient.data.l[2] ==
1412 OBT_PROP_ATOM(NET_WM_MOVERESIZE_SIZE_KEYBOARD) ||
1413 (Atom)e->xclient.data.l[2] ==
1414 OBT_PROP_ATOM(NET_WM_MOVERESIZE_MOVE_KEYBOARD))
1415 {
1416 moveresize_start(client, e->xclient.data.l[0],
1417 e->xclient.data.l[1], e->xclient.data.l[3],
1418 e->xclient.data.l[2]);
1419 }
1420 else if ((Atom)e->xclient.data.l[2] ==
1421 OBT_PROP_ATOM(NET_WM_MOVERESIZE_CANCEL))
1422 moveresize_end(TRUE);
1423 } else if (msgtype == OBT_PROP_ATOM(NET_MOVERESIZE_WINDOW)) {
1424 gint ograv, x, y, w, h;
1425
1426 ograv = client->gravity;
1427
1428 if (e->xclient.data.l[0] & 0xff)
1429 client->gravity = e->xclient.data.l[0] & 0xff;
1430
1431 if (e->xclient.data.l[0] & 1 << 8)
1432 x = e->xclient.data.l[1];
1433 else
1434 x = client->area.x;
1435 if (e->xclient.data.l[0] & 1 << 9)
1436 y = e->xclient.data.l[2];
1437 else
1438 y = client->area.y;
1439
1440 if (e->xclient.data.l[0] & 1 << 10) {
1441 w = e->xclient.data.l[3];
1442
1443 /* if x was not given, then use gravity to figure out the new
1444 x. the reference point should not be moved */
1445 if (!(e->xclient.data.l[0] & 1 << 8))
1446 client_gravity_resize_w(client, &x, client->area.width, w);
1447 }
1448 else
1449 w = client->area.width;
1450
1451 if (e->xclient.data.l[0] & 1 << 11) {
1452 h = e->xclient.data.l[4];
1453
1454 /* same for y */
1455 if (!(e->xclient.data.l[0] & 1 << 9))
1456 client_gravity_resize_h(client, &y, client->area.height,h);
1457 }
1458 else
1459 h = client->area.height;
1460
1461 ob_debug("MOVERESIZE x %d %d y %d %d (gravity %d)",
1462 e->xclient.data.l[0] & 1 << 8, x,
1463 e->xclient.data.l[0] & 1 << 9, y,
1464 client->gravity);
1465
1466 client_find_onscreen(client, &x, &y, w, h, FALSE);
1467
1468 client_configure(client, x, y, w, h, FALSE, TRUE, FALSE);
1469
1470 client->gravity = ograv;
1471 } else if (msgtype == OBT_PROP_ATOM(NET_RESTACK_WINDOW)) {
1472 if (e->xclient.data.l[0] != 2) {
1473 ob_debug_type(OB_DEBUG_APP_BUGS,
1474 "_NET_RESTACK_WINDOW sent for window %s with "
1475 "invalid source indication %ld",
1476 client->title, e->xclient.data.l[0]);
1477 } else {
1478 ObClient *sibling = NULL;
1479 if (e->xclient.data.l[1]) {
1480 ObWindow *win = window_find(e->xclient.data.l[1]);
1481 if (WINDOW_IS_CLIENT(win) &&
1482 WINDOW_AS_CLIENT(win) != client)
1483 {
1484 sibling = WINDOW_AS_CLIENT(win);
1485 }
1486 if (sibling == NULL)
1487 ob_debug_type(OB_DEBUG_APP_BUGS,
1488 "_NET_RESTACK_WINDOW sent for window %s "
1489 "with invalid sibling 0x%x",
1490 client->title, e->xclient.data.l[1]);
1491 }
1492 if (e->xclient.data.l[2] == Below ||
1493 e->xclient.data.l[2] == BottomIf ||
1494 e->xclient.data.l[2] == Above ||
1495 e->xclient.data.l[2] == TopIf ||
1496 e->xclient.data.l[2] == Opposite)
1497 {
1498 gulong ignore_start;
1499
1500 if (!config_focus_under_mouse)
1501 ignore_start = event_start_ignore_all_enters();
1502 /* just raise, don't activate */
1503 stacking_restack_request(client, sibling,
1504 e->xclient.data.l[2]);
1505 if (!config_focus_under_mouse)
1506 event_end_ignore_all_enters(ignore_start);
1507
1508 /* send a synthetic ConfigureNotify, cuz this is supposed
1509 to be like a ConfigureRequest. */
1510 client_reconfigure(client, TRUE);
1511 } else
1512 ob_debug_type(OB_DEBUG_APP_BUGS,
1513 "_NET_RESTACK_WINDOW sent for window %s "
1514 "with invalid detail %d",
1515 client->title, e->xclient.data.l[2]);
1516 }
1517 }
1518 break;
1519 case PropertyNotify:
1520 /* validate cuz we query stuff off the client here */
1521 if (!client_validate(client)) break;
1522
1523 /* compress changes to a single property into a single change */
1524 while (XCheckTypedWindowEvent(obt_display, client->window,
1525 e->type, &ce)) {
1526 Atom a, b;
1527
1528 /* XXX: it would be nice to compress ALL changes to a property,
1529 not just changes in a row without other props between. */
1530
1531 a = ce.xproperty.atom;
1532 b = e->xproperty.atom;
1533
1534 if (a == b)
1535 continue;
1536 if ((a == OBT_PROP_ATOM(NET_WM_NAME) ||
1537 a == OBT_PROP_ATOM(WM_NAME) ||
1538 a == OBT_PROP_ATOM(NET_WM_ICON_NAME) ||
1539 a == OBT_PROP_ATOM(WM_ICON_NAME))
1540 &&
1541 (b == OBT_PROP_ATOM(NET_WM_NAME) ||
1542 b == OBT_PROP_ATOM(WM_NAME) ||
1543 b == OBT_PROP_ATOM(NET_WM_ICON_NAME) ||
1544 b == OBT_PROP_ATOM(WM_ICON_NAME))) {
1545 continue;
1546 }
1547 if (a == OBT_PROP_ATOM(NET_WM_ICON) &&
1548 b == OBT_PROP_ATOM(NET_WM_ICON))
1549 continue;
1550
1551 XPutBackEvent(obt_display, &ce);
1552 break;
1553 }
1554
1555 msgtype = e->xproperty.atom;
1556 if (msgtype == XA_WM_NORMAL_HINTS) {
1557 int x, y, w, h, lw, lh;
1558
1559 ob_debug("Update NORMAL hints");
1560 client_update_normal_hints(client);
1561 /* normal hints can make a window non-resizable */
1562 client_setup_decor_and_functions(client, FALSE);
1563
1564 x = client->area.x;
1565 y = client->area.y;
1566 w = client->area.width;
1567 h = client->area.height;
1568
1569 /* apply the new normal hints */
1570 client_try_configure(client, &x, &y, &w, &h, &lw, &lh, FALSE);
1571 /* make sure the window is visible, and if the window is resized
1572 off-screen due to the normal hints changing then this will push
1573 it back onto the screen. */
1574 client_find_onscreen(client, &x, &y, w, h, FALSE);
1575
1576 /* make sure the client's sizes are within its bounds, but don't
1577 make it reply with a configurenotify unless something changed.
1578 emacs will update its normal hints every time it receives a
1579 configurenotify */
1580 client_configure(client, x, y, w, h, FALSE, TRUE, FALSE);
1581 } else if (msgtype == OBT_PROP_ATOM(MOTIF_WM_HINTS)) {
1582 client_get_mwm_hints(client);
1583 /* This can override some mwm hints */
1584 client_get_type_and_transientness(client);
1585
1586 /* Apply the changes to the window */
1587 client_setup_decor_and_functions(client, TRUE);
1588 } else if (msgtype == XA_WM_HINTS) {
1589 client_update_wmhints(client);
1590 } else if (msgtype == XA_WM_TRANSIENT_FOR) {
1591 client_update_transient_for(client);
1592 client_get_type_and_transientness(client);
1593 /* type may have changed, so update the layer */
1594 client_calc_layer(client);
1595 client_setup_decor_and_functions(client, TRUE);
1596 } else if (msgtype == OBT_PROP_ATOM(NET_WM_NAME) ||
1597 msgtype == OBT_PROP_ATOM(WM_NAME) ||
1598 msgtype == OBT_PROP_ATOM(NET_WM_ICON_NAME) ||
1599 msgtype == OBT_PROP_ATOM(WM_ICON_NAME)) {
1600 client_update_title(client);
1601 } else if (msgtype == OBT_PROP_ATOM(WM_PROTOCOLS)) {
1602 client_update_protocols(client);
1603 client_setup_decor_and_functions(client, TRUE);
1604 }
1605 else if (msgtype == OBT_PROP_ATOM(NET_WM_STRUT) ||
1606 msgtype == OBT_PROP_ATOM(NET_WM_STRUT_PARTIAL)) {
1607 client_update_strut(client);
1608 }
1609 else if (msgtype == OBT_PROP_ATOM(NET_WM_ICON)) {
1610 client_update_icons(client);
1611 }
1612 else if (msgtype == OBT_PROP_ATOM(NET_WM_ICON_GEOMETRY)) {
1613 client_update_icon_geometry(client);
1614 }
1615 else if (msgtype == OBT_PROP_ATOM(NET_WM_USER_TIME)) {
1616 guint32 t;
1617 if (client == focus_client &&
1618 OBT_PROP_GET32(client->window, NET_WM_USER_TIME, CARDINAL, &t)
1619 && t && !event_time_after(t, e->xproperty.time) &&
1620 (!event_last_user_time ||
1621 event_time_after(t, event_last_user_time)))
1622 {
1623 event_last_user_time = t;
1624 }
1625 }
1626 #ifdef SYNC
1627 else if (msgtype == OBT_PROP_ATOM(NET_WM_SYNC_REQUEST_COUNTER)) {
1628 client_update_sync_request_counter(client);
1629 }
1630 #endif
1631 break;
1632 case ColormapNotify:
1633 client_update_colormap(client, e->xcolormap.colormap);
1634 break;
1635 default:
1636 ;
1637 #ifdef SHAPE
1638 {
1639 int kind;
1640 if (obt_display_extension_shape &&
1641 e->type == obt_display_extension_shape_basep)
1642 {
1643 switch (((XShapeEvent*)e)->kind) {
1644 case ShapeBounding:
1645 case ShapeClip:
1646 client->shaped = ((XShapeEvent*)e)->shaped;
1647 kind = ShapeBounding;
1648 break;
1649 case ShapeInput:
1650 client->shaped_input = ((XShapeEvent*)e)->shaped;
1651 kind = ShapeInput;
1652 break;
1653 default:
1654 g_assert_not_reached();
1655 }
1656 frame_adjust_shape_kind(client->frame, kind);
1657 }
1658 }
1659 #endif
1660 }
1661 }
1662
1663 static void event_handle_dock(ObDock *s, XEvent *e)
1664 {
1665 switch (e->type) {
1666 case ButtonPress:
1667 if (e->xbutton.button == 1)
1668 stacking_raise(DOCK_AS_WINDOW(s));
1669 else if (e->xbutton.button == 2)
1670 stacking_lower(DOCK_AS_WINDOW(s));
1671 break;
1672 case EnterNotify:
1673 dock_hide(FALSE);
1674 break;
1675 case LeaveNotify:
1676 /* don't hide when moving into a dock app */
1677 if (e->xcrossing.detail != NotifyInferior)
1678 dock_hide(TRUE);
1679 break;
1680 }
1681 }
1682
1683 static void event_handle_dockapp(ObDockApp *app, XEvent *e)
1684 {
1685 switch (e->type) {
1686 case MotionNotify:
1687 dock_app_drag(app, &e->xmotion);
1688 break;
1689 case UnmapNotify:
1690 if (app->ignore_unmaps) {
1691 app->ignore_unmaps--;
1692 break;
1693 }
1694 dock_unmanage(app, TRUE);
1695 break;
1696 case DestroyNotify:
1697 case ReparentNotify:
1698 dock_unmanage(app, FALSE);
1699 break;
1700 case ConfigureNotify:
1701 dock_app_configure(app, e->xconfigure.width, e->xconfigure.height);
1702 break;
1703 }
1704 }
1705
1706 static ObMenuFrame* find_active_menu(void)
1707 {
1708 GList *it;
1709 ObMenuFrame *ret = NULL;
1710
1711 for (it = menu_frame_visible; it; it = g_list_next(it)) {
1712 ret = it->data;
1713 if (ret->selected)
1714 break;
1715 ret = NULL;
1716 }
1717 return ret;
1718 }
1719
1720 static ObMenuFrame* find_active_or_last_menu(void)
1721 {
1722 ObMenuFrame *ret = NULL;
1723
1724 ret = find_active_menu();
1725 if (!ret && menu_frame_visible)
1726 ret = menu_frame_visible->data;
1727 return ret;
1728 }
1729
1730 static gboolean event_handle_prompt(ObPrompt *p, XEvent *e)
1731 {
1732 switch (e->type) {
1733 case ButtonPress:
1734 case ButtonRelease:
1735 case MotionNotify:
1736 return prompt_mouse_event(p, e);
1737 break;
1738 case KeyPress:
1739 return prompt_key_event(p, e);
1740 break;
1741 }
1742 return FALSE;
1743 }
1744
1745 static gboolean event_handle_menu_input(XEvent *ev)
1746 {
1747 gboolean ret = FALSE;
1748
1749 if (ev->type == ButtonRelease || ev->type == ButtonPress) {
1750 ObMenuEntryFrame *e;
1751
1752 if (menu_hide_delay_reached() &&
1753 (ev->xbutton.button < 4 || ev->xbutton.button > 5))
1754 {
1755 if ((e = menu_entry_frame_under(ev->xbutton.x_root,
1756 ev->xbutton.y_root)))
1757 {
1758 if (ev->type == ButtonPress && e->frame->child)
1759 menu_frame_select(e->frame->child, NULL, TRUE);
1760 menu_frame_select(e->frame, e, TRUE);
1761 if (ev->type == ButtonRelease)
1762 menu_entry_frame_execute(e, ev->xbutton.state);
1763 }
1764 else if (ev->type == ButtonRelease)
1765 menu_frame_hide_all();
1766 }
1767 ret = TRUE;
1768 }
1769 else if (ev->type == MotionNotify) {
1770 ObMenuFrame *f;
1771 ObMenuEntryFrame *e;
1772
1773 if ((e = menu_entry_frame_under(ev->xmotion.x_root,
1774 ev->xmotion.y_root)))
1775 if (!(f = find_active_menu()) ||
1776 f == e->frame ||
1777 f->parent == e->frame ||
1778 f->child == e->frame)
1779 menu_frame_select(e->frame, e, FALSE);
1780 }
1781 else if (ev->type == KeyPress || ev->type == KeyRelease) {
1782 guint keycode, state;
1783 gunichar unikey;
1784 ObMenuFrame *frame;
1785
1786 keycode = ev->xkey.keycode;
1787 state = ev->xkey.state;
1788 unikey = obt_keyboard_keycode_to_unichar(keycode);
1789
1790 frame = find_active_or_last_menu();
1791 if (frame == NULL)
1792 g_assert_not_reached(); /* there is no active menu */
1793
1794 /* Allow control while going thru the menu */
1795 else if (ev->type == KeyPress && (state & ~ControlMask) == 0) {
1796 frame->got_press = TRUE;
1797
1798 if (ob_keycode_match(keycode, OB_KEY_ESCAPE)) {
1799 menu_frame_hide_all();
1800 ret = TRUE;
1801 }
1802
1803 else if (ob_keycode_match(keycode, OB_KEY_LEFT)) {
1804 /* Left goes to the parent menu */
1805 if (frame->parent) {
1806 /* remove focus from the child */
1807 menu_frame_select(frame, NULL, TRUE);
1808 /* and put it in the parent */
1809 menu_frame_select(frame->parent, frame->parent->selected,
1810 TRUE);
1811 }
1812 ret = TRUE;
1813 }
1814
1815 else if (ob_keycode_match(keycode, OB_KEY_RIGHT)) {
1816 /* Right goes to the selected submenu */
1817 if (frame->child) menu_frame_select_next(frame->child);
1818 ret = TRUE;
1819 }
1820
1821 else if (ob_keycode_match(keycode, OB_KEY_UP)) {
1822 menu_frame_select_previous(frame);
1823 ret = TRUE;
1824 }
1825
1826 else if (ob_keycode_match(keycode, OB_KEY_DOWN)) {
1827 menu_frame_select_next(frame);
1828 ret = TRUE;
1829 }
1830 }
1831
1832 /* Use KeyRelease events for running things so that the key release
1833 doesn't get sent to the focused application.
1834
1835 Allow ControlMask only, and don't bother if the menu is empty */
1836 else if (ev->type == KeyRelease && (state & ~ControlMask) == 0 &&
1837 frame->entries && frame->got_press)
1838 {
1839 if (ob_keycode_match(keycode, OB_KEY_RETURN)) {
1840 /* Enter runs the active item or goes into the submenu.
1841 Control-Enter runs it without closing the menu. */
1842 if (frame->child)
1843 menu_frame_select_next(frame->child);
1844 else if (frame->selected)
1845 menu_entry_frame_execute(frame->selected, state);
1846
1847 ret = TRUE;
1848 }
1849
1850 /* keyboard accelerator shortcuts. (if it was a valid key) */
1851 else if (unikey != 0) {
1852 GList *start;
1853 GList *it;
1854 ObMenuEntryFrame *found = NULL;
1855 guint num_found = 0;
1856
1857 /* start after the selected one */
1858 start = frame->entries;
1859 if (frame->selected) {
1860 for (it = start; frame->selected != it->data;
1861 it = g_list_next(it))
1862 g_assert(it != NULL); /* nothing was selected? */
1863 /* next with wraparound */
1864 start = g_list_next(it);
1865 if (start == NULL) start = frame->entries;
1866 }
1867
1868 it = start;
1869 do {
1870 ObMenuEntryFrame *e = it->data;
1871 gunichar entrykey = 0;
1872
1873 if (e->entry->type == OB_MENU_ENTRY_TYPE_NORMAL)
1874 entrykey = e->entry->data.normal.shortcut;
1875 else if (e->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU)
1876 entrykey = e->entry->data.submenu.submenu->shortcut;
1877
1878 if (unikey == entrykey) {
1879 if (found == NULL) found = e;
1880 ++num_found;
1881 }
1882
1883 /* next with wraparound */
1884 it = g_list_next(it);
1885 if (it == NULL) it = frame->entries;
1886 } while (it != start);
1887
1888 if (found) {
1889 if (found->entry->type == OB_MENU_ENTRY_TYPE_NORMAL &&
1890 num_found == 1)
1891 {
1892 menu_frame_select(frame, found, TRUE);
1893 usleep(50000); /* highlight the item for a short bit so
1894 the user can see what happened */
1895 menu_entry_frame_execute(found, state);
1896 } else {
1897 menu_frame_select(frame, found, TRUE);
1898 if (num_found == 1)
1899 menu_frame_select_next(frame->child);
1900 }
1901
1902 ret = TRUE;
1903 }
1904 }
1905 }
1906 }
1907
1908 return ret;
1909 }
1910
1911 static Bool event_look_for_menu_enter(Display *d, XEvent *ev, XPointer arg)
1912 {
1913 ObMenuFrame *f = (ObMenuFrame*)arg;
1914 ObMenuEntryFrame *e;
1915 return ev->type == EnterNotify &&
1916 (e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.window)) &&
1917 !e->ignore_enters && e->frame == f;
1918 }
1919
1920 static void event_handle_menu(ObMenuFrame *frame, XEvent *ev)
1921 {
1922 ObMenuFrame *f;
1923 ObMenuEntryFrame *e;
1924
1925 switch (ev->type) {
1926 case EnterNotify:
1927 if ((e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.window))) {
1928 if (e->ignore_enters)
1929 --e->ignore_enters;
1930 else if (!(f = find_active_menu()) ||
1931 f == e->frame ||
1932 f->parent == e->frame ||
1933 f->child == e->frame)
1934 menu_frame_select(e->frame, e, FALSE);
1935 }
1936 break;
1937 case LeaveNotify:
1938 /*ignore leaves when we're already in the window */
1939 if (ev->xcrossing.detail == NotifyInferior)
1940 break;
1941
1942 if ((e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.window)))
1943 {
1944 XEvent ce;
1945
1946 /* check if an EnterNotify event is coming, and if not, then select
1947 nothing in the menu */
1948 if (XCheckIfEvent(obt_display, &ce, event_look_for_menu_enter,
1949 (XPointer)e->frame))
1950 XPutBackEvent(obt_display, &ce);
1951 else
1952 menu_frame_select(e->frame, NULL, FALSE);
1953 }
1954 break;
1955 }
1956 }
1957
1958 static void event_handle_user_input(ObClient *client, XEvent *e)
1959 {
1960 g_assert(e->type == ButtonPress || e->type == ButtonRelease ||
1961 e->type == MotionNotify || e->type == KeyPress ||
1962 e->type == KeyRelease);
1963
1964 if (menu_frame_visible) {
1965 if (event_handle_menu_input(e))
1966 /* don't use the event if the menu used it, but if the menu
1967 didn't use it and it's a keypress that is bound, it will
1968 close the menu and be used */
1969 return;
1970 }
1971
1972 /* if the keyboard interactive action uses the event then dont
1973 use it for bindings. likewise is moveresize uses the event. */
1974 if (!actions_interactive_input_event(e) && !moveresize_event(e)) {
1975 if (moveresize_in_progress)
1976 /* make further actions work on the client being
1977 moved/resized */
1978 client = moveresize_client;
1979
1980 if (e->type == ButtonPress ||
1981 e->type == ButtonRelease ||
1982 e->type == MotionNotify)
1983 {
1984 /* the frame may not be "visible" but they can still click on it
1985 in the case where it is animating before disappearing */
1986 if (!client || !frame_iconify_animating(client->frame))
1987 mouse_event(client, e);
1988 } else
1989 keyboard_event((focus_cycle_target ? focus_cycle_target :
1990 (client ? client : focus_client)), e);
1991 }
1992 }
1993
1994 static void focus_delay_dest(gpointer data)
1995 {
1996 g_free(data);
1997 }
1998
1999 static gboolean focus_delay_cmp(gconstpointer d1, gconstpointer d2)
2000 {
2001 const ObFocusDelayData *f1 = d1;
2002 return f1->client == d2;
2003 }
2004
2005 static gboolean focus_delay_func(gpointer data)
2006 {
2007 ObFocusDelayData *d = data;
2008 Time old = event_curtime;
2009
2010 event_curtime = d->time;
2011 event_curserial = d->serial;
2012 if (client_focus(d->client) && config_focus_raise)
2013 stacking_raise(CLIENT_AS_WINDOW(d->client));
2014 event_curtime = old;
2015 return FALSE; /* no repeat */
2016 }
2017
2018 static gboolean unfocus_delay_func(gpointer data)
2019 {
2020 ObFocusDelayData *d = data;
2021 Time old = event_curtime;
2022
2023 event_curtime = d->time;
2024 event_curserial = d->serial;
2025 focus_nothing();
2026 event_curtime = old;
2027 return FALSE; /* no repeat */
2028 }
2029
2030 static void focus_delay_client_dest(ObClient *client, gpointer data)
2031 {
2032 obt_main_loop_timeout_remove_data(ob_main_loop, focus_delay_func,
2033 client, FALSE);
2034 obt_main_loop_timeout_remove_data(ob_main_loop, unfocus_delay_func,
2035 client, FALSE);
2036 }
2037
2038 void event_halt_focus_delay(void)
2039 {
2040 /* ignore all enter events up till the event which caused this to occur */
2041 if (event_curserial) event_ignore_enter_range(1, event_curserial);
2042 obt_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
2043 obt_main_loop_timeout_remove(ob_main_loop, unfocus_delay_func);
2044 }
2045
2046 gulong event_start_ignore_all_enters(void)
2047 {
2048 return NextRequest(obt_display);
2049 }
2050
2051 static void event_ignore_enter_range(gulong start, gulong end)
2052 {
2053 ObSerialRange *r;
2054
2055 g_assert(start != 0);
2056 g_assert(end != 0);
2057
2058 r = g_new(ObSerialRange, 1);
2059 r->start = start;
2060 r->end = end;
2061 ignore_serials = g_slist_prepend(ignore_serials, r);
2062
2063 ob_debug_type(OB_DEBUG_FOCUS, "ignoring enters from %lu until %lu",
2064 r->start, r->end);
2065
2066 /* increment the serial so we don't ignore events we weren't meant to */
2067 OBT_PROP_ERASE(screen_support_win, MOTIF_WM_HINTS);
2068 }
2069
2070 void event_end_ignore_all_enters(gulong start)
2071 {
2072 /* Use (NextRequest-1) so that we ignore up to the current serial only.
2073 Inside event_ignore_enter_range, we increment the serial by one, but if
2074 we ignore that serial too, then any enter events generated by mouse
2075 movement will be ignored until we create some further network traffic.
2076 Instead ignore up to NextRequest-1, then when we increment the serial,
2077 we will be *past* the range of ignored serials */
2078 event_ignore_enter_range(start, NextRequest(obt_display)-1);
2079 }
2080
2081 static gboolean is_enter_focus_event_ignored(gulong serial)
2082 {
2083 GSList *it, *next;
2084
2085 for (it = ignore_serials; it; it = next) {
2086 ObSerialRange *r = it->data;
2087
2088 next = g_slist_next(it);
2089
2090 if ((glong)(serial - r->end) > 0) {
2091 /* past the end */
2092 ignore_serials = g_slist_delete_link(ignore_serials, it);
2093 g_free(r);
2094 }
2095 else if ((glong)(serial - r->start) >= 0)
2096 return TRUE;
2097 }
2098 return FALSE;
2099 }
2100
2101 void event_cancel_all_key_grabs(void)
2102 {
2103 if (actions_interactive_act_running()) {
2104 actions_interactive_cancel_act();
2105 ob_debug("KILLED interactive action");
2106 }
2107 else if (menu_frame_visible) {
2108 menu_frame_hide_all();
2109 ob_debug("KILLED open menus");
2110 }
2111 else if (moveresize_in_progress) {
2112 moveresize_end(TRUE);
2113 ob_debug("KILLED interactive moveresize");
2114 }
2115 else if (grab_on_keyboard()) {
2116 ungrab_keyboard();
2117 ob_debug("KILLED active grab on keyboard");
2118 }
2119 else
2120 ungrab_passive_key();
2121
2122 XSync(obt_display, FALSE);
2123 }
2124
2125 gboolean event_time_after(guint32 t1, guint32 t2)
2126 {
2127 g_assert(t1 != CurrentTime);
2128 g_assert(t2 != CurrentTime);
2129
2130 /*
2131 Timestamp values wrap around (after about 49.7 days). The server, given
2132 its current time is represented by timestamp T, always interprets
2133 timestamps from clients by treating half of the timestamp space as being
2134 later in time than T.
2135 - http://tronche.com/gui/x/xlib/input/pointer-grabbing.html
2136 */
2137
2138 /* TIME_HALF is not half of the number space of a Time type variable.
2139 * Rather, it is half the number space of a timestamp value, which is
2140 * always 32 bits. */
2141 #define TIME_HALF (guint32)(1 << 31)
2142
2143 if (t2 >= TIME_HALF)
2144 /* t2 is in the second half so t1 might wrap around and be smaller than
2145 t2 */
2146 return t1 >= t2 || t1 < (t2 + TIME_HALF);
2147 else
2148 /* t2 is in the first half so t1 has to come after it */
2149 return t1 >= t2 && t1 < (t2 + TIME_HALF);
2150 }
2151
2152 Time event_get_server_time(void)
2153 {
2154 /* Generate a timestamp */
2155 XEvent event;
2156
2157 XChangeProperty(obt_display, screen_support_win,
2158 OBT_PROP_ATOM(WM_CLASS), OBT_PROP_ATOM(STRING),
2159 8, PropModeAppend, NULL, 0);
2160 XWindowEvent(obt_display, screen_support_win, PropertyChangeMask, &event);
2161 return event.xproperty.time;
2162 }
This page took 0.127743 seconds and 4 git commands to generate.