]> Dogcows Code - chaz/openbox/blob - openbox/event.c
save session state for old clients that dont use XSMP. more session improvements...
[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 "client.h"
26 #include "xerror.h"
27 #include "prop.h"
28 #include "config.h"
29 #include "screen.h"
30 #include "frame.h"
31 #include "menu.h"
32 #include "menuframe.h"
33 #include "keyboard.h"
34 #include "modkeys.h"
35 #include "mouse.h"
36 #include "mainloop.h"
37 #include "framerender.h"
38 #include "focus.h"
39 #include "moveresize.h"
40 #include "group.h"
41 #include "stacking.h"
42 #include "extensions.h"
43 #include "translate.h"
44
45 #include <X11/Xlib.h>
46 #include <X11/keysym.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 } ObFocusDelayData;
77
78 static void event_process(const XEvent *e, gpointer data);
79 static void event_handle_root(XEvent *e);
80 static gboolean event_handle_menu_keyboard(XEvent *e);
81 static gboolean event_handle_menu(XEvent *e);
82 static void event_handle_dock(ObDock *s, XEvent *e);
83 static void event_handle_dockapp(ObDockApp *app, XEvent *e);
84 static void event_handle_client(ObClient *c, XEvent *e);
85 static void event_handle_group(ObGroup *g, XEvent *e);
86 static void event_handle_user_input(ObClient *client, XEvent *e);
87
88 static void focus_delay_dest(gpointer data);
89 static gboolean focus_delay_cmp(gconstpointer d1, gconstpointer d2);
90 static gboolean focus_delay_func(gpointer data);
91 static void focus_delay_client_dest(ObClient *client, gpointer data);
92
93 static gboolean menu_hide_delay_func(gpointer data);
94
95 /* The time for the current event being processed */
96 Time event_curtime = CurrentTime;
97
98 static guint ignore_enter_focus = 0;
99 static gboolean menu_can_hide;
100 static gboolean focus_left_screen = FALSE;
101
102 #ifdef USE_SM
103 static void ice_handler(gint fd, gpointer conn)
104 {
105 Bool b;
106 IceProcessMessages(conn, NULL, &b);
107 }
108
109 static void ice_watch(IceConn conn, IcePointer data, Bool opening,
110 IcePointer *watch_data)
111 {
112 static gint fd = -1;
113
114 if (opening) {
115 fd = IceConnectionNumber(conn);
116 ob_main_loop_fd_add(ob_main_loop, fd, ice_handler, conn, NULL);
117 } else {
118 ob_main_loop_fd_remove(ob_main_loop, fd);
119 fd = -1;
120 }
121 }
122 #endif
123
124 void event_startup(gboolean reconfig)
125 {
126 if (reconfig) return;
127
128 ob_main_loop_x_add(ob_main_loop, event_process, NULL, NULL);
129
130 #ifdef USE_SM
131 IceAddConnectionWatch(ice_watch, NULL);
132 #endif
133
134 client_add_destructor(focus_delay_client_dest, NULL);
135 }
136
137 void event_shutdown(gboolean reconfig)
138 {
139 if (reconfig) return;
140
141 #ifdef USE_SM
142 IceRemoveConnectionWatch(ice_watch, NULL);
143 #endif
144
145 client_remove_destructor(focus_delay_client_dest);
146 }
147
148 static Window event_get_window(XEvent *e)
149 {
150 Window window;
151
152 /* pick a window */
153 switch (e->type) {
154 case SelectionClear:
155 window = RootWindow(ob_display, ob_screen);
156 break;
157 case MapRequest:
158 window = e->xmap.window;
159 break;
160 case UnmapNotify:
161 window = e->xunmap.window;
162 break;
163 case DestroyNotify:
164 window = e->xdestroywindow.window;
165 break;
166 case ConfigureRequest:
167 window = e->xconfigurerequest.window;
168 break;
169 case ConfigureNotify:
170 window = e->xconfigure.window;
171 break;
172 default:
173 #ifdef XKB
174 if (extensions_xkb && e->type == extensions_xkb_event_basep) {
175 switch (((XkbAnyEvent*)e)->xkb_type) {
176 case XkbBellNotify:
177 window = ((XkbBellNotifyEvent*)e)->window;
178 default:
179 window = None;
180 }
181 } else
182 #endif
183 #ifdef SYNC
184 if (extensions_sync &&
185 e->type == extensions_sync_event_basep + XSyncAlarmNotify)
186 {
187 window = None;
188 } else
189 #endif
190 window = e->xany.window;
191 }
192 return window;
193 }
194
195 static void event_set_curtime(XEvent *e)
196 {
197 Time t = CurrentTime;
198
199 /* grab the lasttime and hack up the state */
200 switch (e->type) {
201 case ButtonPress:
202 case ButtonRelease:
203 t = e->xbutton.time;
204 break;
205 case KeyPress:
206 t = e->xkey.time;
207 break;
208 case KeyRelease:
209 t = e->xkey.time;
210 break;
211 case MotionNotify:
212 t = e->xmotion.time;
213 break;
214 case PropertyNotify:
215 t = e->xproperty.time;
216 break;
217 case EnterNotify:
218 case LeaveNotify:
219 t = e->xcrossing.time;
220 break;
221 default:
222 #ifdef SYNC
223 if (extensions_sync &&
224 e->type == extensions_sync_event_basep + XSyncAlarmNotify)
225 {
226 t = ((XSyncAlarmNotifyEvent*)e)->time;
227 }
228 #endif
229 /* if more event types are anticipated, get their timestamp
230 explicitly */
231 break;
232 }
233
234 event_curtime = t;
235 }
236
237 static void event_hack_mods(XEvent *e)
238 {
239 #ifdef XKB
240 XkbStateRec xkb_state;
241 #endif
242
243 switch (e->type) {
244 case ButtonPress:
245 case ButtonRelease:
246 e->xbutton.state = modkeys_only_modifier_masks(e->xbutton.state);
247 break;
248 case KeyPress:
249 e->xkey.state = modkeys_only_modifier_masks(e->xkey.state);
250 break;
251 case KeyRelease:
252 e->xkey.state = modkeys_only_modifier_masks(e->xkey.state);
253 #ifdef XKB
254 if (XkbGetState(ob_display, XkbUseCoreKbd, &xkb_state) == Success) {
255 e->xkey.state = xkb_state.compat_state;
256 break;
257 }
258 #endif
259 /* remove from the state the mask of the modifier key being released,
260 if it is a modifier key being released that is */
261 e->xkey.state &= ~modkeys_keycode_to_mask(e->xkey.keycode);
262 break;
263 case MotionNotify:
264 e->xmotion.state = modkeys_only_modifier_masks(e->xmotion.state);
265 /* compress events */
266 {
267 XEvent ce;
268 while (XCheckTypedWindowEvent(ob_display, e->xmotion.window,
269 e->type, &ce)) {
270 e->xmotion.x_root = ce.xmotion.x_root;
271 e->xmotion.y_root = ce.xmotion.y_root;
272 }
273 }
274 break;
275 }
276 }
277
278 static gboolean wanted_focusevent(XEvent *e)
279 {
280 gint mode = e->xfocus.mode;
281 gint detail = e->xfocus.detail;
282 Window win = e->xany.window;
283
284 if (e->type == FocusIn) {
285
286 /* These are ones we never want.. */
287
288 /* This means focus was given by a keyboard/mouse grab. */
289 if (mode == NotifyGrab)
290 return FALSE;
291 /* This means focus was given back from a keyboard/mouse grab. */
292 if (mode == NotifyUngrab)
293 return FALSE;
294
295 /* These are the ones we want.. */
296
297 if (win == RootWindow(ob_display, ob_screen)) {
298 /* This means focus reverted off of a client */
299 if (detail == NotifyPointerRoot || detail == NotifyDetailNone ||
300 detail == NotifyInferior)
301 return TRUE;
302 else
303 return FALSE;
304 }
305
306 /* This means focus moved from the root window to a client */
307 if (detail == NotifyVirtual)
308 return TRUE;
309 /* This means focus moved from one client to another */
310 if (detail == NotifyNonlinearVirtual)
311 return TRUE;
312 /* This means focus moved to the frame window */
313 if (detail == NotifyInferior)
314 return TRUE;
315
316 /* Otherwise.. */
317 return FALSE;
318 } else {
319 g_assert(e->type == FocusOut);
320
321
322 /* These are ones we never want.. */
323
324 /* This means focus was taken by a keyboard/mouse grab. */
325 if (mode == NotifyGrab)
326 return FALSE;
327
328 /* Focus left the root window revertedto state */
329 if (win == RootWindow(ob_display, ob_screen))
330 return FALSE;
331
332 /* These are the ones we want.. */
333
334 /* This means focus moved from a client to the root window */
335 if (detail == NotifyVirtual)
336 return TRUE;
337 /* This means focus moved from one client to another */
338 if (detail == NotifyNonlinearVirtual)
339 return TRUE;
340 /* This means focus had moved to our frame window and now moved off */
341 if (detail == NotifyNonlinear)
342 return TRUE;
343
344 /* Otherwise.. */
345 return FALSE;
346 }
347 }
348
349 static Bool look_for_focusin(Display *d, XEvent *e, XPointer arg)
350 {
351 return e->type == FocusIn && wanted_focusevent(e);
352 }
353
354 static gboolean event_ignore(XEvent *e, ObClient *client)
355 {
356 switch(e->type) {
357 case FocusIn:
358 if (!wanted_focusevent(e))
359 return TRUE;
360 break;
361 case FocusOut:
362 if (!wanted_focusevent(e))
363 return TRUE;
364 break;
365 }
366 return FALSE;
367 }
368
369 static void event_process(const XEvent *ec, gpointer data)
370 {
371 Window window;
372 ObGroup *group = NULL;
373 ObClient *client = NULL;
374 ObDock *dock = NULL;
375 ObDockApp *dockapp = NULL;
376 ObWindow *obwin = NULL;
377 XEvent ee, *e;
378 ObEventData *ed = data;
379
380 /* make a copy we can mangle */
381 ee = *ec;
382 e = &ee;
383
384 window = event_get_window(e);
385 if (!(e->type == PropertyNotify &&
386 (group = g_hash_table_lookup(group_map, &window))))
387 if ((obwin = g_hash_table_lookup(window_map, &window))) {
388 switch (obwin->type) {
389 case Window_Dock:
390 dock = WINDOW_AS_DOCK(obwin);
391 break;
392 case Window_DockApp:
393 dockapp = WINDOW_AS_DOCKAPP(obwin);
394 break;
395 case Window_Client:
396 client = WINDOW_AS_CLIENT(obwin);
397 break;
398 case Window_Menu:
399 case Window_Internal:
400 /* not to be used for events */
401 g_assert_not_reached();
402 break;
403 }
404 }
405
406 event_set_curtime(e);
407 event_hack_mods(e);
408 if (event_ignore(e, client)) {
409 if (ed)
410 ed->ignored = TRUE;
411 return;
412 } else if (ed)
413 ed->ignored = FALSE;
414
415 /* deal with it in the kernel */
416
417 if (menu_frame_visible &&
418 (e->type == EnterNotify || e->type == LeaveNotify))
419 {
420 /* crossing events for menu */
421 event_handle_menu(e);
422 } else if (e->type == FocusIn) {
423 if (e->xfocus.detail == NotifyPointerRoot ||
424 e->xfocus.detail == NotifyDetailNone ||
425 e->xfocus.detail == NotifyInferior)
426 {
427 XEvent ce;
428 ob_debug_type(OB_DEBUG_FOCUS,
429 "Focus went to pointer root/none or to our frame "
430 "window\n");
431
432 /* If another FocusIn is in the queue then don't fallback yet. This
433 fixes the fun case of:
434 window map -> send focusin
435 window unmap -> get focusout
436 window map -> send focusin
437 get first focus out -> fall back to something (new window
438 hasn't received focus yet, so something else) -> send focusin
439 which means the "something else" is the last thing to get a
440 focusin sent to it, so the new window doesn't end up with focus.
441 */
442 if (XCheckIfEvent(ob_display, &ce, look_for_focusin, NULL)) {
443 XPutBackEvent(ob_display, &ce);
444 ob_debug_type(OB_DEBUG_FOCUS,
445 " but another FocusIn is coming\n");
446 } else {
447 /* Focus has been reverted to the root window, nothing, or to
448 our frame window.
449
450 FocusOut events come after UnmapNotify, so we don't need to
451 worry about focusing an invalid window
452 */
453
454 /* In this case we know focus is in our screen */
455 if (e->xfocus.detail == NotifyInferior)
456 focus_left_screen = FALSE;
457
458 if (!focus_left_screen)
459 focus_fallback(TRUE);
460 }
461 } else if (client && client != focus_client) {
462 focus_left_screen = FALSE;
463 frame_adjust_focus(client->frame, TRUE);
464 focus_set_client(client);
465 client_calc_layer(client);
466 }
467 } else if (e->type == FocusOut) {
468 gboolean nomove = FALSE;
469 XEvent ce;
470
471 ob_debug_type(OB_DEBUG_FOCUS, "FocusOut Event\n");
472
473 /* Look for the followup FocusIn */
474 if (!XCheckIfEvent(ob_display, &ce, look_for_focusin, NULL)) {
475 /* There is no FocusIn, this means focus went to a window that
476 is not being managed, or a window on another screen. */
477 Window win, root;
478 gint i;
479 guint u;
480 xerror_set_ignore(TRUE);
481 if (XGetInputFocus(ob_display, &win, &i) != 0 &&
482 XGetGeometry(ob_display, win, &root, &i,&i,&u,&u,&u,&u) != 0 &&
483 root != RootWindow(ob_display, ob_screen))
484 {
485 ob_debug_type(OB_DEBUG_FOCUS,
486 "Focus went to another screen !\n");
487 focus_left_screen = TRUE;
488 }
489 else
490 ob_debug_type(OB_DEBUG_FOCUS,
491 "Focus went to a black hole !\n");
492 xerror_set_ignore(FALSE);
493 /* nothing is focused */
494 focus_set_client(NULL);
495 } else if (ce.xany.window == e->xany.window) {
496 ob_debug_type(OB_DEBUG_FOCUS, "Focus didn't go anywhere\n");
497 /* If focus didn't actually move anywhere, there is nothing to do*/
498 nomove = TRUE;
499 } else {
500 /* Focus did move, so process the FocusIn event */
501 ObEventData ed = { .ignored = FALSE };
502 event_process(&ce, &ed);
503 if (ed.ignored) {
504 /* The FocusIn was ignored, this means it was on a window
505 that isn't a client. */
506 ob_debug_type(OB_DEBUG_FOCUS,
507 "Focus went to an unmanaged window 0x%x !\n",
508 ce.xfocus.window);
509 focus_fallback(TRUE);
510 }
511 }
512
513 if (client && !nomove) {
514 frame_adjust_focus(client->frame, FALSE);
515 /* focus_set_client has already been called for sure */
516 client_calc_layer(client);
517 }
518 } else if (group)
519 event_handle_group(group, e);
520 else if (client)
521 event_handle_client(client, e);
522 else if (dockapp)
523 event_handle_dockapp(dockapp, e);
524 else if (dock)
525 event_handle_dock(dock, e);
526 else if (window == RootWindow(ob_display, ob_screen))
527 event_handle_root(e);
528 else if (e->type == MapRequest)
529 client_manage(window);
530 else if (e->type == ConfigureRequest) {
531 /* unhandled configure requests must be used to configure the
532 window directly */
533 XWindowChanges xwc;
534
535 xwc.x = e->xconfigurerequest.x;
536 xwc.y = e->xconfigurerequest.y;
537 xwc.width = e->xconfigurerequest.width;
538 xwc.height = e->xconfigurerequest.height;
539 xwc.border_width = e->xconfigurerequest.border_width;
540 xwc.sibling = e->xconfigurerequest.above;
541 xwc.stack_mode = e->xconfigurerequest.detail;
542
543 /* we are not to be held responsible if someone sends us an
544 invalid request! */
545 xerror_set_ignore(TRUE);
546 XConfigureWindow(ob_display, window,
547 e->xconfigurerequest.value_mask, &xwc);
548 xerror_set_ignore(FALSE);
549 }
550 #ifdef SYNC
551 else if (extensions_sync &&
552 e->type == extensions_sync_event_basep + XSyncAlarmNotify)
553 {
554 XSyncAlarmNotifyEvent *se = (XSyncAlarmNotifyEvent*)e;
555 if (se->alarm == moveresize_alarm && moveresize_in_progress)
556 moveresize_event(e);
557 }
558 #endif
559
560 if (e->type == ButtonPress || e->type == ButtonRelease ||
561 e->type == MotionNotify || e->type == KeyPress ||
562 e->type == KeyRelease)
563 {
564 event_handle_user_input(client, e);
565 }
566
567 /* if something happens and it's not from an XEvent, then we don't know
568 the time */
569 event_curtime = CurrentTime;
570 }
571
572 static void event_handle_root(XEvent *e)
573 {
574 Atom msgtype;
575
576 switch(e->type) {
577 case SelectionClear:
578 ob_debug("Another WM has requested to replace us. Exiting.\n");
579 ob_exit_replace();
580 break;
581
582 case ClientMessage:
583 if (e->xclient.format != 32) break;
584
585 msgtype = e->xclient.message_type;
586 if (msgtype == prop_atoms.net_current_desktop) {
587 guint d = e->xclient.data.l[0];
588 if (d < screen_num_desktops) {
589 event_curtime = e->xclient.data.l[1];
590 if (event_curtime == 0)
591 ob_debug_type(OB_DEBUG_APP_BUGS,
592 "_NET_CURRENT_DESKTOP message is missing "
593 "a timestamp\n");
594 screen_set_desktop(d, TRUE);
595 }
596 } else if (msgtype == prop_atoms.net_number_of_desktops) {
597 guint d = e->xclient.data.l[0];
598 if (d > 0)
599 screen_set_num_desktops(d);
600 } else if (msgtype == prop_atoms.net_showing_desktop) {
601 screen_show_desktop(e->xclient.data.l[0] != 0, TRUE);
602 } else if (msgtype == prop_atoms.openbox_control) {
603 if (e->xclient.data.l[0] == 1)
604 ob_reconfigure();
605 else if (e->xclient.data.l[0] == 2)
606 ob_restart();
607 }
608 break;
609 case PropertyNotify:
610 if (e->xproperty.atom == prop_atoms.net_desktop_names)
611 screen_update_desktop_names();
612 else if (e->xproperty.atom == prop_atoms.net_desktop_layout)
613 screen_update_layout();
614 break;
615 case ConfigureNotify:
616 #ifdef XRANDR
617 XRRUpdateConfiguration(e);
618 #endif
619 screen_resize();
620 break;
621 default:
622 ;
623 }
624 }
625
626 static void event_handle_group(ObGroup *group, XEvent *e)
627 {
628 GSList *it;
629
630 g_assert(e->type == PropertyNotify);
631
632 for (it = group->members; it; it = g_slist_next(it))
633 event_handle_client(it->data, e);
634 }
635
636 void event_enter_client(ObClient *client)
637 {
638 g_assert(config_focus_follow);
639
640 if (client_normal(client) && client_can_focus(client)) {
641 if (config_focus_delay) {
642 ObFocusDelayData *data;
643
644 ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
645
646 data = g_new(ObFocusDelayData, 1);
647 data->client = client;
648 data->time = event_curtime;
649
650 ob_main_loop_timeout_add(ob_main_loop,
651 config_focus_delay,
652 focus_delay_func,
653 data, focus_delay_cmp, focus_delay_dest);
654 } else {
655 ObFocusDelayData data;
656 data.client = client;
657 data.time = event_curtime;
658 focus_delay_func(&data);
659 }
660 }
661 }
662
663 static void event_handle_client(ObClient *client, XEvent *e)
664 {
665 XEvent ce;
666 Atom msgtype;
667 ObFrameContext con;
668
669 switch (e->type) {
670 case ButtonPress:
671 case ButtonRelease:
672 /* Wheel buttons don't draw because they are an instant click, so it
673 is a waste of resources to go drawing it. */
674 if (!(e->xbutton.button == 4 || e->xbutton.button == 5)) {
675 con = frame_context(client, e->xbutton.window);
676 con = mouse_button_frame_context(con, e->xbutton.button);
677 switch (con) {
678 case OB_FRAME_CONTEXT_MAXIMIZE:
679 client->frame->max_press = (e->type == ButtonPress);
680 framerender_frame(client->frame);
681 break;
682 case OB_FRAME_CONTEXT_CLOSE:
683 client->frame->close_press = (e->type == ButtonPress);
684 framerender_frame(client->frame);
685 break;
686 case OB_FRAME_CONTEXT_ICONIFY:
687 client->frame->iconify_press = (e->type == ButtonPress);
688 framerender_frame(client->frame);
689 break;
690 case OB_FRAME_CONTEXT_ALLDESKTOPS:
691 client->frame->desk_press = (e->type == ButtonPress);
692 framerender_frame(client->frame);
693 break;
694 case OB_FRAME_CONTEXT_SHADE:
695 client->frame->shade_press = (e->type == ButtonPress);
696 framerender_frame(client->frame);
697 break;
698 default:
699 /* nothing changes with clicks for any other contexts */
700 break;
701 }
702 }
703 break;
704 case LeaveNotify:
705 con = frame_context(client, e->xcrossing.window);
706 switch (con) {
707 case OB_FRAME_CONTEXT_MAXIMIZE:
708 client->frame->max_hover = FALSE;
709 frame_adjust_state(client->frame);
710 break;
711 case OB_FRAME_CONTEXT_ALLDESKTOPS:
712 client->frame->desk_hover = FALSE;
713 frame_adjust_state(client->frame);
714 break;
715 case OB_FRAME_CONTEXT_SHADE:
716 client->frame->shade_hover = FALSE;
717 frame_adjust_state(client->frame);
718 break;
719 case OB_FRAME_CONTEXT_ICONIFY:
720 client->frame->iconify_hover = FALSE;
721 frame_adjust_state(client->frame);
722 break;
723 case OB_FRAME_CONTEXT_CLOSE:
724 client->frame->close_hover = FALSE;
725 frame_adjust_state(client->frame);
726 break;
727 case OB_FRAME_CONTEXT_FRAME:
728 /* When the mouse leaves an animating window, don't use the
729 corresponding enter events. Pretend like the animating window
730 doesn't even exist..! */
731 if (frame_iconify_animating(client->frame))
732 event_ignore_queued_enters();
733
734 ob_debug_type(OB_DEBUG_FOCUS,
735 "%sNotify mode %d detail %d on %lx\n",
736 (e->type == EnterNotify ? "Enter" : "Leave"),
737 e->xcrossing.mode,
738 e->xcrossing.detail, (client?client->window:0));
739 if (keyboard_interactively_grabbed())
740 break;
741 if (config_focus_follow && config_focus_delay &&
742 /* leave inferior events can happen when the mouse goes onto
743 the window's border and then into the window before the
744 delay is up */
745 e->xcrossing.detail != NotifyInferior)
746 {
747 ob_main_loop_timeout_remove_data(ob_main_loop,
748 focus_delay_func,
749 client, FALSE);
750 }
751 break;
752 default:
753 break;
754 }
755 break;
756 case EnterNotify:
757 {
758 gboolean nofocus = FALSE;
759
760 if (ignore_enter_focus) {
761 ignore_enter_focus--;
762 nofocus = TRUE;
763 }
764
765 con = frame_context(client, e->xcrossing.window);
766 switch (con) {
767 case OB_FRAME_CONTEXT_MAXIMIZE:
768 client->frame->max_hover = TRUE;
769 frame_adjust_state(client->frame);
770 break;
771 case OB_FRAME_CONTEXT_ALLDESKTOPS:
772 client->frame->desk_hover = TRUE;
773 frame_adjust_state(client->frame);
774 break;
775 case OB_FRAME_CONTEXT_SHADE:
776 client->frame->shade_hover = TRUE;
777 frame_adjust_state(client->frame);
778 break;
779 case OB_FRAME_CONTEXT_ICONIFY:
780 client->frame->iconify_hover = TRUE;
781 frame_adjust_state(client->frame);
782 break;
783 case OB_FRAME_CONTEXT_CLOSE:
784 client->frame->close_hover = TRUE;
785 frame_adjust_state(client->frame);
786 break;
787 case OB_FRAME_CONTEXT_FRAME:
788 if (keyboard_interactively_grabbed())
789 break;
790 if (e->xcrossing.mode == NotifyGrab ||
791 e->xcrossing.mode == NotifyUngrab ||
792 /*ignore enters when we're already in the window */
793 e->xcrossing.detail == NotifyInferior)
794 {
795 ob_debug_type(OB_DEBUG_FOCUS,
796 "%sNotify mode %d detail %d on %lx IGNORED\n",
797 (e->type == EnterNotify ? "Enter" : "Leave"),
798 e->xcrossing.mode,
799 e->xcrossing.detail, client?client->window:0);
800 } else {
801 ob_debug_type(OB_DEBUG_FOCUS,
802 "%sNotify mode %d detail %d on %lx, "
803 "focusing window: %d\n",
804 (e->type == EnterNotify ? "Enter" : "Leave"),
805 e->xcrossing.mode,
806 e->xcrossing.detail, (client?client->window:0),
807 !nofocus);
808 if (!nofocus && config_focus_follow)
809 event_enter_client(client);
810 }
811 break;
812 default:
813 break;
814 }
815 break;
816 }
817 case ConfigureRequest:
818 /* dont compress these unless you're going to watch for property
819 notifies in between (these can change what the configure would
820 do to the window).
821 also you can't compress stacking events
822 */
823
824 ob_debug("ConfigureRequest desktop %d wmstate %d vis %d\n",
825 screen_desktop, client->wmstate, client->frame->visible);
826
827 /* don't allow clients to move shaded windows (fvwm does this) */
828 if (client->shaded) {
829 e->xconfigurerequest.value_mask &= ~CWX;
830 e->xconfigurerequest.value_mask &= ~CWY;
831 }
832
833 /* resize, then move, as specified in the EWMH section 7.7 */
834 if (e->xconfigurerequest.value_mask & (CWWidth | CWHeight |
835 CWX | CWY |
836 CWBorderWidth)) {
837 gint x, y, w, h;
838
839 if (e->xconfigurerequest.value_mask & CWBorderWidth)
840 client->border_width = e->xconfigurerequest.border_width;
841
842 x = (e->xconfigurerequest.value_mask & CWX) ?
843 e->xconfigurerequest.x : client->area.x;
844 y = (e->xconfigurerequest.value_mask & CWY) ?
845 e->xconfigurerequest.y : client->area.y;
846 w = (e->xconfigurerequest.value_mask & CWWidth) ?
847 e->xconfigurerequest.width : client->area.width;
848 h = (e->xconfigurerequest.value_mask & CWHeight) ?
849 e->xconfigurerequest.height : client->area.height;
850
851 ob_debug("ConfigureRequest x %d %d y %d %d\n",
852 e->xconfigurerequest.value_mask & CWX, x,
853 e->xconfigurerequest.value_mask & CWY, y);
854
855 /* check for broken apps moving to their root position
856
857 XXX remove this some day...that would be nice. right now all
858 kde apps do this when they try activate themselves on another
859 desktop. eg. open amarok window on desktop 1, switch to desktop
860 2, click amarok tray icon. it will move by its decoration size.
861 */
862 if (x != client->area.x &&
863 x == (client->frame->area.x + client->frame->size.left -
864 (gint)client->border_width) &&
865 y != client->area.y &&
866 y == (client->frame->area.y + client->frame->size.top -
867 (gint)client->border_width))
868 {
869 ob_debug_type(OB_DEBUG_APP_BUGS,
870 "Application %s is trying to move via "
871 "ConfigureRequest to it's root window position "
872 "but it is not using StaticGravity\n",
873 client->title);
874 /* don't move it */
875 x = client->area.x;
876 y = client->area.y;
877 }
878
879 client_find_onscreen(client, &x, &y, w, h, FALSE);
880 client_configure_full(client, x, y, w, h, FALSE, TRUE, TRUE);
881 }
882
883 if (e->xconfigurerequest.value_mask & CWStackMode) {
884 switch (e->xconfigurerequest.detail) {
885 case Below:
886 case BottomIf:
887 /* Apps are so rude. And this is totally disconnected from
888 activation/focus. Bleh. */
889 /*client_lower(client);*/
890 break;
891
892 case Above:
893 case TopIf:
894 default:
895 /* Apps are so rude. And this is totally disconnected from
896 activation/focus. Bleh. */
897 /*client_raise(client);*/
898 break;
899 }
900 }
901 break;
902 case UnmapNotify:
903 if (client->ignore_unmaps) {
904 client->ignore_unmaps--;
905 break;
906 }
907 ob_debug("UnmapNotify for window 0x%x eventwin 0x%x sendevent %d "
908 "ignores left %d\n",
909 client->window, e->xunmap.event, e->xunmap.from_configure,
910 client->ignore_unmaps);
911 client_unmanage(client);
912 break;
913 case DestroyNotify:
914 ob_debug("DestroyNotify for window 0x%x\n", client->window);
915 client_unmanage(client);
916 break;
917 case ReparentNotify:
918 /* this is when the client is first taken captive in the frame */
919 if (e->xreparent.parent == client->frame->plate) break;
920
921 /*
922 This event is quite rare and is usually handled in unmapHandler.
923 However, if the window is unmapped when the reparent event occurs,
924 the window manager never sees it because an unmap event is not sent
925 to an already unmapped window.
926 */
927
928 /* we don't want the reparent event, put it back on the stack for the
929 X server to deal with after we unmanage the window */
930 XPutBackEvent(ob_display, e);
931
932 ob_debug("ReparentNotify for window 0x%x\n", client->window);
933 client_unmanage(client);
934 break;
935 case MapRequest:
936 ob_debug("MapRequest for 0x%lx\n", client->window);
937 if (!client->iconic) break; /* this normally doesn't happen, but if it
938 does, we don't want it!
939 it can happen now when the window is on
940 another desktop, but we still don't
941 want it! */
942 client_activate(client, FALSE, TRUE);
943 break;
944 case ClientMessage:
945 /* validate cuz we query stuff off the client here */
946 if (!client_validate(client)) break;
947
948 if (e->xclient.format != 32) return;
949
950 msgtype = e->xclient.message_type;
951 if (msgtype == prop_atoms.wm_change_state) {
952 /* compress changes into a single change */
953 while (XCheckTypedWindowEvent(ob_display, client->window,
954 e->type, &ce)) {
955 /* XXX: it would be nice to compress ALL messages of a
956 type, not just messages in a row without other
957 message types between. */
958 if (ce.xclient.message_type != msgtype) {
959 XPutBackEvent(ob_display, &ce);
960 break;
961 }
962 e->xclient = ce.xclient;
963 }
964 client_set_wm_state(client, e->xclient.data.l[0]);
965 } else if (msgtype == prop_atoms.net_wm_desktop) {
966 /* compress changes into a single change */
967 while (XCheckTypedWindowEvent(ob_display, client->window,
968 e->type, &ce)) {
969 /* XXX: it would be nice to compress ALL messages of a
970 type, not just messages in a row without other
971 message types between. */
972 if (ce.xclient.message_type != msgtype) {
973 XPutBackEvent(ob_display, &ce);
974 break;
975 }
976 e->xclient = ce.xclient;
977 }
978 if ((unsigned)e->xclient.data.l[0] < screen_num_desktops ||
979 (unsigned)e->xclient.data.l[0] == DESKTOP_ALL)
980 client_set_desktop(client, (unsigned)e->xclient.data.l[0],
981 FALSE);
982 } else if (msgtype == prop_atoms.net_wm_state) {
983 /* can't compress these */
984 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
985 (e->xclient.data.l[0] == 0 ? "Remove" :
986 e->xclient.data.l[0] == 1 ? "Add" :
987 e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
988 e->xclient.data.l[1], e->xclient.data.l[2],
989 client->window);
990 client_set_state(client, e->xclient.data.l[0],
991 e->xclient.data.l[1], e->xclient.data.l[2]);
992 } else if (msgtype == prop_atoms.net_close_window) {
993 ob_debug("net_close_window for 0x%lx\n", client->window);
994 client_close(client);
995 } else if (msgtype == prop_atoms.net_active_window) {
996 ob_debug("net_active_window for 0x%lx source=%s\n",
997 client->window,
998 (e->xclient.data.l[0] == 0 ? "unknown" :
999 (e->xclient.data.l[0] == 1 ? "application" :
1000 (e->xclient.data.l[0] == 2 ? "user" : "INVALID"))));
1001 /* XXX make use of data.l[2] !? */
1002 event_curtime = e->xclient.data.l[1];
1003 ob_debug_type(OB_DEBUG_APP_BUGS,
1004 "_NET_ACTIVE_WINDOW message for window %s is "
1005 "missing a timestamp\n", client->title);
1006 client_activate(client, FALSE,
1007 (e->xclient.data.l[0] == 0 ||
1008 e->xclient.data.l[0] == 2));
1009 } else if (msgtype == prop_atoms.net_wm_moveresize) {
1010 ob_debug("net_wm_moveresize for 0x%lx direction %d\n",
1011 client->window, e->xclient.data.l[2]);
1012 if ((Atom)e->xclient.data.l[2] ==
1013 prop_atoms.net_wm_moveresize_size_topleft ||
1014 (Atom)e->xclient.data.l[2] ==
1015 prop_atoms.net_wm_moveresize_size_top ||
1016 (Atom)e->xclient.data.l[2] ==
1017 prop_atoms.net_wm_moveresize_size_topright ||
1018 (Atom)e->xclient.data.l[2] ==
1019 prop_atoms.net_wm_moveresize_size_right ||
1020 (Atom)e->xclient.data.l[2] ==
1021 prop_atoms.net_wm_moveresize_size_right ||
1022 (Atom)e->xclient.data.l[2] ==
1023 prop_atoms.net_wm_moveresize_size_bottomright ||
1024 (Atom)e->xclient.data.l[2] ==
1025 prop_atoms.net_wm_moveresize_size_bottom ||
1026 (Atom)e->xclient.data.l[2] ==
1027 prop_atoms.net_wm_moveresize_size_bottomleft ||
1028 (Atom)e->xclient.data.l[2] ==
1029 prop_atoms.net_wm_moveresize_size_left ||
1030 (Atom)e->xclient.data.l[2] ==
1031 prop_atoms.net_wm_moveresize_move ||
1032 (Atom)e->xclient.data.l[2] ==
1033 prop_atoms.net_wm_moveresize_size_keyboard ||
1034 (Atom)e->xclient.data.l[2] ==
1035 prop_atoms.net_wm_moveresize_move_keyboard) {
1036
1037 moveresize_start(client, e->xclient.data.l[0],
1038 e->xclient.data.l[1], e->xclient.data.l[3],
1039 e->xclient.data.l[2]);
1040 }
1041 else if ((Atom)e->xclient.data.l[2] ==
1042 prop_atoms.net_wm_moveresize_cancel)
1043 moveresize_end(TRUE);
1044 } else if (msgtype == prop_atoms.net_moveresize_window) {
1045 gint grav, x, y, w, h;
1046
1047 if (e->xclient.data.l[0] & 0xff)
1048 grav = e->xclient.data.l[0] & 0xff;
1049 else
1050 grav = client->gravity;
1051
1052 if (e->xclient.data.l[0] & 1 << 8)
1053 x = e->xclient.data.l[1];
1054 else
1055 x = client->area.x;
1056 if (e->xclient.data.l[0] & 1 << 9)
1057 y = e->xclient.data.l[2];
1058 else
1059 y = client->area.y;
1060 if (e->xclient.data.l[0] & 1 << 10)
1061 w = e->xclient.data.l[3];
1062 else
1063 w = client->area.width;
1064 if (e->xclient.data.l[0] & 1 << 11)
1065 h = e->xclient.data.l[4];
1066 else
1067 h = client->area.height;
1068
1069 ob_debug("MOVERESIZE x %d %d y %d %d\n",
1070 e->xclient.data.l[0] & 1 << 8, x,
1071 e->xclient.data.l[0] & 1 << 9, y);
1072 client_convert_gravity(client, grav, &x, &y, w, h);
1073 client_find_onscreen(client, &x, &y, w, h, FALSE);
1074 client_configure(client, x, y, w, h, FALSE, TRUE);
1075 }
1076 break;
1077 case PropertyNotify:
1078 /* validate cuz we query stuff off the client here */
1079 if (!client_validate(client)) break;
1080
1081 /* compress changes to a single property into a single change */
1082 while (XCheckTypedWindowEvent(ob_display, client->window,
1083 e->type, &ce)) {
1084 Atom a, b;
1085
1086 /* XXX: it would be nice to compress ALL changes to a property,
1087 not just changes in a row without other props between. */
1088
1089 a = ce.xproperty.atom;
1090 b = e->xproperty.atom;
1091
1092 if (a == b)
1093 continue;
1094 if ((a == prop_atoms.net_wm_name ||
1095 a == prop_atoms.wm_name ||
1096 a == prop_atoms.net_wm_icon_name ||
1097 a == prop_atoms.wm_icon_name)
1098 &&
1099 (b == prop_atoms.net_wm_name ||
1100 b == prop_atoms.wm_name ||
1101 b == prop_atoms.net_wm_icon_name ||
1102 b == prop_atoms.wm_icon_name)) {
1103 continue;
1104 }
1105 if (a == prop_atoms.net_wm_icon &&
1106 b == prop_atoms.net_wm_icon)
1107 continue;
1108
1109 XPutBackEvent(ob_display, &ce);
1110 break;
1111 }
1112
1113 msgtype = e->xproperty.atom;
1114 if (msgtype == XA_WM_NORMAL_HINTS) {
1115 client_update_normal_hints(client);
1116 /* normal hints can make a window non-resizable */
1117 client_setup_decor_and_functions(client);
1118 } else if (msgtype == XA_WM_HINTS) {
1119 client_update_wmhints(client);
1120 } else if (msgtype == XA_WM_TRANSIENT_FOR) {
1121 client_update_transient_for(client);
1122 client_get_type(client);
1123 /* type may have changed, so update the layer */
1124 client_calc_layer(client);
1125 client_setup_decor_and_functions(client);
1126 } else if (msgtype == prop_atoms.net_wm_name ||
1127 msgtype == prop_atoms.wm_name ||
1128 msgtype == prop_atoms.net_wm_icon_name ||
1129 msgtype == prop_atoms.wm_icon_name) {
1130 client_update_title(client);
1131 } else if (msgtype == prop_atoms.wm_command) {
1132 client_update_command(client);
1133 } else if (msgtype == prop_atoms.wm_class) {
1134 client_update_class(client);
1135 } else if (msgtype == prop_atoms.wm_protocols) {
1136 client_update_protocols(client);
1137 client_setup_decor_and_functions(client);
1138 }
1139 else if (msgtype == prop_atoms.net_wm_strut) {
1140 client_update_strut(client);
1141 }
1142 else if (msgtype == prop_atoms.net_wm_icon) {
1143 client_update_icons(client);
1144 }
1145 else if (msgtype == prop_atoms.net_wm_icon_geometry) {
1146 client_update_icon_geometry(client);
1147 }
1148 else if (msgtype == prop_atoms.net_wm_user_time) {
1149 client_update_user_time(client);
1150 }
1151 #ifdef SYNC
1152 else if (msgtype == prop_atoms.net_wm_sync_request_counter) {
1153 client_update_sync_request_counter(client);
1154 }
1155 #endif
1156 else if (msgtype == prop_atoms.sm_client_id) {
1157 client_update_sm_client_id(client);
1158 }
1159 case ColormapNotify:
1160 client_update_colormap(client, e->xcolormap.colormap);
1161 break;
1162 default:
1163 ;
1164 #ifdef SHAPE
1165 if (extensions_shape && e->type == extensions_shape_event_basep) {
1166 client->shaped = ((XShapeEvent*)e)->shaped;
1167 frame_adjust_shape(client->frame);
1168 }
1169 #endif
1170 }
1171 }
1172
1173 static void event_handle_dock(ObDock *s, XEvent *e)
1174 {
1175 switch (e->type) {
1176 case ButtonPress:
1177 if (e->xbutton.button == 1)
1178 stacking_raise(DOCK_AS_WINDOW(s));
1179 else if (e->xbutton.button == 2)
1180 stacking_lower(DOCK_AS_WINDOW(s));
1181 break;
1182 case EnterNotify:
1183 dock_hide(FALSE);
1184 break;
1185 case LeaveNotify:
1186 dock_hide(TRUE);
1187 break;
1188 }
1189 }
1190
1191 static void event_handle_dockapp(ObDockApp *app, XEvent *e)
1192 {
1193 switch (e->type) {
1194 case MotionNotify:
1195 dock_app_drag(app, &e->xmotion);
1196 break;
1197 case UnmapNotify:
1198 if (app->ignore_unmaps) {
1199 app->ignore_unmaps--;
1200 break;
1201 }
1202 dock_remove(app, TRUE);
1203 break;
1204 case DestroyNotify:
1205 dock_remove(app, FALSE);
1206 break;
1207 case ReparentNotify:
1208 dock_remove(app, FALSE);
1209 break;
1210 case ConfigureNotify:
1211 dock_app_configure(app, e->xconfigure.width, e->xconfigure.height);
1212 break;
1213 }
1214 }
1215
1216 static ObMenuFrame* find_active_menu()
1217 {
1218 GList *it;
1219 ObMenuFrame *ret = NULL;
1220
1221 for (it = menu_frame_visible; it; it = g_list_next(it)) {
1222 ret = it->data;
1223 if (ret->selected)
1224 break;
1225 ret = NULL;
1226 }
1227 return ret;
1228 }
1229
1230 static ObMenuFrame* find_active_or_last_menu()
1231 {
1232 ObMenuFrame *ret = NULL;
1233
1234 ret = find_active_menu();
1235 if (!ret && menu_frame_visible)
1236 ret = menu_frame_visible->data;
1237 return ret;
1238 }
1239
1240 static gboolean event_handle_menu_keyboard(XEvent *ev)
1241 {
1242 guint keycode, state;
1243 gunichar unikey;
1244 ObMenuFrame *frame;
1245 gboolean ret = TRUE;
1246
1247 keycode = ev->xkey.keycode;
1248 state = ev->xkey.state;
1249 unikey = translate_unichar(keycode);
1250
1251 frame = find_active_or_last_menu();
1252 if (frame == NULL)
1253 ret = FALSE;
1254
1255 else if (keycode == ob_keycode(OB_KEY_ESCAPE) && state == 0) {
1256 /* Escape closes the active menu */
1257 menu_frame_hide(frame);
1258 }
1259
1260 else if (keycode == ob_keycode(OB_KEY_RETURN) && (state == 0 ||
1261 state == ControlMask))
1262 {
1263 /* Enter runs the active item or goes into the submenu.
1264 Control-Enter runs it without closing the menu. */
1265 if (frame->child)
1266 menu_frame_select_next(frame->child);
1267 else
1268 menu_entry_frame_execute(frame->selected, state, ev->xkey.time);
1269 }
1270
1271 else if (keycode == ob_keycode(OB_KEY_LEFT) && ev->xkey.state == 0) {
1272 /* Left goes to the parent menu */
1273 menu_frame_select(frame, NULL, TRUE);
1274 }
1275
1276 else if (keycode == ob_keycode(OB_KEY_RIGHT) && ev->xkey.state == 0) {
1277 /* Right goes to the selected submenu */
1278 if (frame->child) menu_frame_select_next(frame->child);
1279 }
1280
1281 else if (keycode == ob_keycode(OB_KEY_UP) && state == 0) {
1282 menu_frame_select_previous(frame);
1283 }
1284
1285 else if (keycode == ob_keycode(OB_KEY_DOWN) && state == 0) {
1286 menu_frame_select_next(frame);
1287 }
1288
1289 /* keyboard accelerator shortcuts. */
1290 else if (ev->xkey.state == 0 &&
1291 /* was it a valid key? */
1292 unikey != 0 &&
1293 /* don't bother if the menu is empty. */
1294 frame->entries)
1295 {
1296 GList *start;
1297 GList *it;
1298 ObMenuEntryFrame *found = NULL;
1299 guint num_found = 0;
1300
1301 /* start after the selected one */
1302 start = frame->entries;
1303 if (frame->selected) {
1304 for (it = start; frame->selected != it->data; it = g_list_next(it))
1305 g_assert(it != NULL); /* nothing was selected? */
1306 /* next with wraparound */
1307 start = g_list_next(it);
1308 if (start == NULL) start = frame->entries;
1309 }
1310
1311 it = start;
1312 do {
1313 ObMenuEntryFrame *e = it->data;
1314 gunichar entrykey = 0;
1315
1316 if (e->entry->type == OB_MENU_ENTRY_TYPE_NORMAL)
1317 entrykey = e->entry->data.normal.shortcut;
1318 else if (e->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU)
1319 entrykey = e->entry->data.submenu.submenu->shortcut;
1320
1321 if (unikey == entrykey) {
1322 if (found == NULL) found = e;
1323 ++num_found;
1324 }
1325
1326 /* next with wraparound */
1327 it = g_list_next(it);
1328 if (it == NULL) it = frame->entries;
1329 } while (it != start);
1330
1331 if (found) {
1332 if (found->entry->type == OB_MENU_ENTRY_TYPE_NORMAL &&
1333 num_found == 1)
1334 {
1335 menu_frame_select(frame, found, TRUE);
1336 usleep(50000); /* highlight the item for a short bit so the
1337 user can see what happened */
1338 menu_entry_frame_execute(found, state, ev->xkey.time);
1339 } else {
1340 menu_frame_select(frame, found, TRUE);
1341 if (num_found == 1)
1342 menu_frame_select_next(frame->child);
1343 }
1344 } else
1345 ret = FALSE;
1346 }
1347 else
1348 ret = FALSE;
1349
1350 return ret;
1351 }
1352
1353 static gboolean event_handle_menu(XEvent *ev)
1354 {
1355 ObMenuFrame *f;
1356 ObMenuEntryFrame *e;
1357 gboolean ret = TRUE;
1358
1359 switch (ev->type) {
1360 case ButtonRelease:
1361 if ((ev->xbutton.button < 4 || ev->xbutton.button > 5)
1362 && menu_can_hide)
1363 {
1364 if ((e = menu_entry_frame_under(ev->xbutton.x_root,
1365 ev->xbutton.y_root)))
1366 menu_entry_frame_execute(e, ev->xbutton.state,
1367 ev->xbutton.time);
1368 else
1369 menu_frame_hide_all();
1370 }
1371 break;
1372 case EnterNotify:
1373 if ((e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.window))) {
1374 if (e->ignore_enters)
1375 --e->ignore_enters;
1376 else
1377 menu_frame_select(e->frame, e, FALSE);
1378 }
1379 break;
1380 case LeaveNotify:
1381 if ((e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.window)) &&
1382 (f = find_active_menu()) && f->selected == e &&
1383 e->entry->type != OB_MENU_ENTRY_TYPE_SUBMENU)
1384 {
1385 menu_frame_select(e->frame, NULL, FALSE);
1386 }
1387 case MotionNotify:
1388 if ((e = menu_entry_frame_under(ev->xmotion.x_root,
1389 ev->xmotion.y_root)))
1390 menu_frame_select(e->frame, e, FALSE);
1391 break;
1392 case KeyPress:
1393 ret = event_handle_menu_keyboard(ev);
1394 break;
1395 }
1396 return ret;
1397 }
1398
1399 static void event_handle_user_input(ObClient *client, XEvent *e)
1400 {
1401 g_assert(e->type == ButtonPress || e->type == ButtonRelease ||
1402 e->type == MotionNotify || e->type == KeyPress ||
1403 e->type == KeyRelease);
1404
1405 if (menu_frame_visible) {
1406 if (event_handle_menu(e))
1407 /* don't use the event if the menu used it, but if the menu
1408 didn't use it and it's a keypress that is bound, it will
1409 close the menu and be used */
1410 return;
1411 }
1412
1413 /* if the keyboard interactive action uses the event then dont
1414 use it for bindings. likewise is moveresize uses the event. */
1415 if (!keyboard_process_interactive_grab(e, &client) &&
1416 !(moveresize_in_progress && moveresize_event(e)))
1417 {
1418 if (moveresize_in_progress)
1419 /* make further actions work on the client being
1420 moved/resized */
1421 client = moveresize_client;
1422
1423 menu_can_hide = FALSE;
1424 ob_main_loop_timeout_add(ob_main_loop,
1425 config_menu_hide_delay * 1000,
1426 menu_hide_delay_func,
1427 NULL, g_direct_equal, NULL);
1428
1429 if (e->type == ButtonPress ||
1430 e->type == ButtonRelease ||
1431 e->type == MotionNotify)
1432 {
1433 /* the frame may not be "visible" but they can still click on it
1434 in the case where it is animating before disappearing */
1435 if (!client || !frame_iconify_animating(client->frame))
1436 mouse_event(client, e);
1437 } else if (e->type == KeyPress) {
1438 keyboard_event((focus_cycle_target ? focus_cycle_target :
1439 (client ? client : focus_client)), e);
1440 }
1441 }
1442 }
1443
1444 static gboolean menu_hide_delay_func(gpointer data)
1445 {
1446 menu_can_hide = TRUE;
1447 return FALSE; /* no repeat */
1448 }
1449
1450 static void focus_delay_dest(gpointer data)
1451 {
1452 g_free(data);
1453 }
1454
1455 static gboolean focus_delay_cmp(gconstpointer d1, gconstpointer d2)
1456 {
1457 const ObFocusDelayData *f1 = d1;
1458 return f1->client == d2;
1459 }
1460
1461 static gboolean focus_delay_func(gpointer data)
1462 {
1463 ObFocusDelayData *d = data;
1464 Time old = event_curtime;
1465
1466 event_curtime = d->time;
1467 if (focus_client != d->client) {
1468 if (client_focus(d->client) && config_focus_raise)
1469 client_raise(d->client);
1470 }
1471 event_curtime = old;
1472 return FALSE; /* no repeat */
1473 }
1474
1475 static void focus_delay_client_dest(ObClient *client, gpointer data)
1476 {
1477 ob_main_loop_timeout_remove_data(ob_main_loop, focus_delay_func,
1478 client, FALSE);
1479 }
1480
1481 void event_halt_focus_delay()
1482 {
1483 ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
1484 }
1485
1486 void event_ignore_queued_enters()
1487 {
1488 GSList *saved = NULL, *it;
1489 XEvent *e;
1490
1491 XSync(ob_display, FALSE);
1492
1493 /* count the events */
1494 while (TRUE) {
1495 e = g_new(XEvent, 1);
1496 if (XCheckTypedEvent(ob_display, EnterNotify, e)) {
1497 ObWindow *win;
1498
1499 win = g_hash_table_lookup(window_map, &e->xany.window);
1500 if (win && WINDOW_IS_CLIENT(win))
1501 ++ignore_enter_focus;
1502
1503 saved = g_slist_append(saved, e);
1504 } else {
1505 g_free(e);
1506 break;
1507 }
1508 }
1509 /* put the events back */
1510 for (it = saved; it; it = g_slist_next(it)) {
1511 XPutBackEvent(ob_display, it->data);
1512 g_free(it->data);
1513 }
1514 g_slist_free(saved);
1515 }
1516
1517 gboolean event_time_after(Time t1, Time t2)
1518 {
1519 g_assert(t1 != CurrentTime);
1520 g_assert(t2 != CurrentTime);
1521
1522 /*
1523 Timestamp values wrap around (after about 49.7 days). The server, given
1524 its current time is represented by timestamp T, always interprets
1525 timestamps from clients by treating half of the timestamp space as being
1526 later in time than T.
1527 - http://tronche.com/gui/x/xlib/input/pointer-grabbing.html
1528 */
1529
1530 /* TIME_HALF is half of the number space of a Time type variable */
1531 #define TIME_HALF (Time)(1 << (sizeof(Time)*8-1))
1532
1533 if (t2 >= TIME_HALF)
1534 /* t2 is in the second half so t1 might wrap around and be smaller than
1535 t2 */
1536 return t1 >= t2 || t1 < (t2 + TIME_HALF);
1537 else
1538 /* t2 is in the first half so t1 has to come after it */
1539 return t1 >= t2 && t1 < (t2 + TIME_HALF);
1540 }
This page took 0.101268 seconds and 4 git commands to generate.