]> Dogcows Code - chaz/openbox/blob - openbox/event.c
2dcb6af2f220e1811294ae1533273ad0e3bc3935
[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_class) {
1132 client_update_class(client);
1133 } else if (msgtype == prop_atoms.wm_protocols) {
1134 client_update_protocols(client);
1135 client_setup_decor_and_functions(client);
1136 }
1137 else if (msgtype == prop_atoms.net_wm_strut) {
1138 client_update_strut(client);
1139 }
1140 else if (msgtype == prop_atoms.net_wm_icon) {
1141 client_update_icons(client);
1142 }
1143 else if (msgtype == prop_atoms.net_wm_icon_geometry) {
1144 client_update_icon_geometry(client);
1145 }
1146 else if (msgtype == prop_atoms.net_wm_user_time) {
1147 client_update_user_time(client);
1148 }
1149 #ifdef SYNC
1150 else if (msgtype == prop_atoms.net_wm_sync_request_counter) {
1151 client_update_sync_request_counter(client);
1152 }
1153 #endif
1154 else if (msgtype == prop_atoms.sm_client_id) {
1155 client_update_sm_client_id(client);
1156 }
1157 case ColormapNotify:
1158 client_update_colormap(client, e->xcolormap.colormap);
1159 break;
1160 default:
1161 ;
1162 #ifdef SHAPE
1163 if (extensions_shape && e->type == extensions_shape_event_basep) {
1164 client->shaped = ((XShapeEvent*)e)->shaped;
1165 frame_adjust_shape(client->frame);
1166 }
1167 #endif
1168 }
1169 }
1170
1171 static void event_handle_dock(ObDock *s, XEvent *e)
1172 {
1173 switch (e->type) {
1174 case ButtonPress:
1175 if (e->xbutton.button == 1)
1176 stacking_raise(DOCK_AS_WINDOW(s));
1177 else if (e->xbutton.button == 2)
1178 stacking_lower(DOCK_AS_WINDOW(s));
1179 break;
1180 case EnterNotify:
1181 dock_hide(FALSE);
1182 break;
1183 case LeaveNotify:
1184 dock_hide(TRUE);
1185 break;
1186 }
1187 }
1188
1189 static void event_handle_dockapp(ObDockApp *app, XEvent *e)
1190 {
1191 switch (e->type) {
1192 case MotionNotify:
1193 dock_app_drag(app, &e->xmotion);
1194 break;
1195 case UnmapNotify:
1196 if (app->ignore_unmaps) {
1197 app->ignore_unmaps--;
1198 break;
1199 }
1200 dock_remove(app, TRUE);
1201 break;
1202 case DestroyNotify:
1203 dock_remove(app, FALSE);
1204 break;
1205 case ReparentNotify:
1206 dock_remove(app, FALSE);
1207 break;
1208 case ConfigureNotify:
1209 dock_app_configure(app, e->xconfigure.width, e->xconfigure.height);
1210 break;
1211 }
1212 }
1213
1214 static ObMenuFrame* find_active_menu()
1215 {
1216 GList *it;
1217 ObMenuFrame *ret = NULL;
1218
1219 for (it = menu_frame_visible; it; it = g_list_next(it)) {
1220 ret = it->data;
1221 if (ret->selected)
1222 break;
1223 ret = NULL;
1224 }
1225 return ret;
1226 }
1227
1228 static ObMenuFrame* find_active_or_last_menu()
1229 {
1230 ObMenuFrame *ret = NULL;
1231
1232 ret = find_active_menu();
1233 if (!ret && menu_frame_visible)
1234 ret = menu_frame_visible->data;
1235 return ret;
1236 }
1237
1238 static gboolean event_handle_menu_keyboard(XEvent *ev)
1239 {
1240 guint keycode, state;
1241 gunichar unikey;
1242 ObMenuFrame *frame;
1243 gboolean ret = TRUE;
1244
1245 keycode = ev->xkey.keycode;
1246 state = ev->xkey.state;
1247 unikey = translate_unichar(keycode);
1248
1249 frame = find_active_or_last_menu();
1250 if (frame == NULL)
1251 ret = FALSE;
1252
1253 else if (keycode == ob_keycode(OB_KEY_ESCAPE) && state == 0) {
1254 /* Escape closes the active menu */
1255 menu_frame_hide(frame);
1256 }
1257
1258 else if (keycode == ob_keycode(OB_KEY_RETURN) && (state == 0 ||
1259 state == ControlMask))
1260 {
1261 /* Enter runs the active item or goes into the submenu.
1262 Control-Enter runs it without closing the menu. */
1263 if (frame->child)
1264 menu_frame_select_next(frame->child);
1265 else
1266 menu_entry_frame_execute(frame->selected, state, ev->xkey.time);
1267 }
1268
1269 else if (keycode == ob_keycode(OB_KEY_LEFT) && ev->xkey.state == 0) {
1270 /* Left goes to the parent menu */
1271 menu_frame_select(frame, NULL, TRUE);
1272 }
1273
1274 else if (keycode == ob_keycode(OB_KEY_RIGHT) && ev->xkey.state == 0) {
1275 /* Right goes to the selected submenu */
1276 if (frame->child) menu_frame_select_next(frame->child);
1277 }
1278
1279 else if (keycode == ob_keycode(OB_KEY_UP) && state == 0) {
1280 menu_frame_select_previous(frame);
1281 }
1282
1283 else if (keycode == ob_keycode(OB_KEY_DOWN) && state == 0) {
1284 menu_frame_select_next(frame);
1285 }
1286
1287 /* keyboard accelerator shortcuts. */
1288 else if (ev->xkey.state == 0 &&
1289 /* was it a valid key? */
1290 unikey != 0 &&
1291 /* don't bother if the menu is empty. */
1292 frame->entries)
1293 {
1294 GList *start;
1295 GList *it;
1296 ObMenuEntryFrame *found = NULL;
1297 guint num_found = 0;
1298
1299 /* start after the selected one */
1300 start = frame->entries;
1301 if (frame->selected) {
1302 for (it = start; frame->selected != it->data; it = g_list_next(it))
1303 g_assert(it != NULL); /* nothing was selected? */
1304 /* next with wraparound */
1305 start = g_list_next(it);
1306 if (start == NULL) start = frame->entries;
1307 }
1308
1309 it = start;
1310 do {
1311 ObMenuEntryFrame *e = it->data;
1312 gunichar entrykey = 0;
1313
1314 if (e->entry->type == OB_MENU_ENTRY_TYPE_NORMAL)
1315 entrykey = e->entry->data.normal.shortcut;
1316 else if (e->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU)
1317 entrykey = e->entry->data.submenu.submenu->shortcut;
1318
1319 if (unikey == entrykey) {
1320 if (found == NULL) found = e;
1321 ++num_found;
1322 }
1323
1324 /* next with wraparound */
1325 it = g_list_next(it);
1326 if (it == NULL) it = frame->entries;
1327 } while (it != start);
1328
1329 if (found) {
1330 if (found->entry->type == OB_MENU_ENTRY_TYPE_NORMAL &&
1331 num_found == 1)
1332 {
1333 menu_frame_select(frame, found, TRUE);
1334 usleep(50000); /* highlight the item for a short bit so the
1335 user can see what happened */
1336 menu_entry_frame_execute(found, state, ev->xkey.time);
1337 } else {
1338 menu_frame_select(frame, found, TRUE);
1339 if (num_found == 1)
1340 menu_frame_select_next(frame->child);
1341 }
1342 } else
1343 ret = FALSE;
1344 }
1345 else
1346 ret = FALSE;
1347
1348 return ret;
1349 }
1350
1351 static gboolean event_handle_menu(XEvent *ev)
1352 {
1353 ObMenuFrame *f;
1354 ObMenuEntryFrame *e;
1355 gboolean ret = TRUE;
1356
1357 switch (ev->type) {
1358 case ButtonRelease:
1359 if ((ev->xbutton.button < 4 || ev->xbutton.button > 5)
1360 && menu_can_hide)
1361 {
1362 if ((e = menu_entry_frame_under(ev->xbutton.x_root,
1363 ev->xbutton.y_root)))
1364 menu_entry_frame_execute(e, ev->xbutton.state,
1365 ev->xbutton.time);
1366 else
1367 menu_frame_hide_all();
1368 }
1369 break;
1370 case EnterNotify:
1371 if ((e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.window))) {
1372 if (e->ignore_enters)
1373 --e->ignore_enters;
1374 else
1375 menu_frame_select(e->frame, e, FALSE);
1376 }
1377 break;
1378 case LeaveNotify:
1379 if ((e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.window)) &&
1380 (f = find_active_menu()) && f->selected == e &&
1381 e->entry->type != OB_MENU_ENTRY_TYPE_SUBMENU)
1382 {
1383 menu_frame_select(e->frame, NULL, FALSE);
1384 }
1385 case MotionNotify:
1386 if ((e = menu_entry_frame_under(ev->xmotion.x_root,
1387 ev->xmotion.y_root)))
1388 menu_frame_select(e->frame, e, FALSE);
1389 break;
1390 case KeyPress:
1391 ret = event_handle_menu_keyboard(ev);
1392 break;
1393 }
1394 return ret;
1395 }
1396
1397 static void event_handle_user_input(ObClient *client, XEvent *e)
1398 {
1399 g_assert(e->type == ButtonPress || e->type == ButtonRelease ||
1400 e->type == MotionNotify || e->type == KeyPress ||
1401 e->type == KeyRelease);
1402
1403 if (menu_frame_visible) {
1404 if (event_handle_menu(e))
1405 /* don't use the event if the menu used it, but if the menu
1406 didn't use it and it's a keypress that is bound, it will
1407 close the menu and be used */
1408 return;
1409 }
1410
1411 /* if the keyboard interactive action uses the event then dont
1412 use it for bindings. likewise is moveresize uses the event. */
1413 if (!keyboard_process_interactive_grab(e, &client) &&
1414 !(moveresize_in_progress && moveresize_event(e)))
1415 {
1416 if (moveresize_in_progress)
1417 /* make further actions work on the client being
1418 moved/resized */
1419 client = moveresize_client;
1420
1421 menu_can_hide = FALSE;
1422 ob_main_loop_timeout_add(ob_main_loop,
1423 config_menu_hide_delay * 1000,
1424 menu_hide_delay_func,
1425 NULL, g_direct_equal, NULL);
1426
1427 if (e->type == ButtonPress ||
1428 e->type == ButtonRelease ||
1429 e->type == MotionNotify)
1430 {
1431 /* the frame may not be "visible" but they can still click on it
1432 in the case where it is animating before disappearing */
1433 if (!client || !frame_iconify_animating(client->frame))
1434 mouse_event(client, e);
1435 } else if (e->type == KeyPress) {
1436 keyboard_event((focus_cycle_target ? focus_cycle_target :
1437 (client ? client : focus_client)), e);
1438 }
1439 }
1440 }
1441
1442 static gboolean menu_hide_delay_func(gpointer data)
1443 {
1444 menu_can_hide = TRUE;
1445 return FALSE; /* no repeat */
1446 }
1447
1448 static void focus_delay_dest(gpointer data)
1449 {
1450 g_free(data);
1451 }
1452
1453 static gboolean focus_delay_cmp(gconstpointer d1, gconstpointer d2)
1454 {
1455 const ObFocusDelayData *f1 = d1;
1456 return f1->client == d2;
1457 }
1458
1459 static gboolean focus_delay_func(gpointer data)
1460 {
1461 ObFocusDelayData *d = data;
1462 Time old = event_curtime;
1463
1464 event_curtime = d->time;
1465 if (focus_client != d->client) {
1466 if (client_focus(d->client) && config_focus_raise)
1467 client_raise(d->client);
1468 }
1469 event_curtime = old;
1470 return FALSE; /* no repeat */
1471 }
1472
1473 static void focus_delay_client_dest(ObClient *client, gpointer data)
1474 {
1475 ob_main_loop_timeout_remove_data(ob_main_loop, focus_delay_func,
1476 client, FALSE);
1477 }
1478
1479 void event_halt_focus_delay()
1480 {
1481 ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
1482 }
1483
1484 void event_ignore_queued_enters()
1485 {
1486 GSList *saved = NULL, *it;
1487 XEvent *e;
1488
1489 XSync(ob_display, FALSE);
1490
1491 /* count the events */
1492 while (TRUE) {
1493 e = g_new(XEvent, 1);
1494 if (XCheckTypedEvent(ob_display, EnterNotify, e)) {
1495 ObWindow *win;
1496
1497 win = g_hash_table_lookup(window_map, &e->xany.window);
1498 if (win && WINDOW_IS_CLIENT(win))
1499 ++ignore_enter_focus;
1500
1501 saved = g_slist_append(saved, e);
1502 } else {
1503 g_free(e);
1504 break;
1505 }
1506 }
1507 /* put the events back */
1508 for (it = saved; it; it = g_slist_next(it)) {
1509 XPutBackEvent(ob_display, it->data);
1510 g_free(it->data);
1511 }
1512 g_slist_free(saved);
1513 }
1514
1515 gboolean event_time_after(Time t1, Time t2)
1516 {
1517 g_assert(t1 != CurrentTime);
1518 g_assert(t2 != CurrentTime);
1519
1520 /*
1521 Timestamp values wrap around (after about 49.7 days). The server, given
1522 its current time is represented by timestamp T, always interprets
1523 timestamps from clients by treating half of the timestamp space as being
1524 later in time than T.
1525 - http://tronche.com/gui/x/xlib/input/pointer-grabbing.html
1526 */
1527
1528 /* TIME_HALF is half of the number space of a Time type variable */
1529 #define TIME_HALF (Time)(1 << (sizeof(Time)*8-1))
1530
1531 if (t2 >= TIME_HALF)
1532 /* t2 is in the second half so t1 might wrap around and be smaller than
1533 t2 */
1534 return t1 >= t2 || t1 < (t2 + TIME_HALF);
1535 else
1536 /* t2 is in the first half so t1 has to come after it */
1537 return t1 >= t2 && t1 < (t2 + TIME_HALF);
1538 }
This page took 0.102808 seconds and 3 git commands to generate.