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