]> Dogcows Code - chaz/openbox/blob - openbox/event.c
61aea4415958c9c06960e2b0e3eb2fc03b59d497
[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 {
426 ob_debug_type(OB_DEBUG_FOCUS, "Focus went to pointer root/none\n");
427 /* Focus has been reverted to the root window or nothing.
428 FocusOut events come after UnmapNotify, so we don't need to
429 worry about focusing an invalid window
430 */
431 if (!focus_left_screen)
432 focus_fallback(TRUE);
433 } else if (e->xfocus.detail == NotifyInferior) {
434 ob_debug_type(OB_DEBUG_FOCUS,
435 "Focus went to root or our frame window");
436 /* Focus has been given to the root window. */
437 focus_fallback(TRUE);
438 } else if (client && client != focus_client) {
439 focus_left_screen = FALSE;
440 frame_adjust_focus(client->frame, TRUE);
441 focus_set_client(client);
442 client_calc_layer(client);
443 }
444 } else if (e->type == FocusOut) {
445 gboolean nomove = FALSE;
446 XEvent ce;
447
448 ob_debug_type(OB_DEBUG_FOCUS, "FocusOut Event\n");
449
450 /* Look for the followup FocusIn */
451 if (!XCheckIfEvent(ob_display, &ce, look_for_focusin, NULL)) {
452 /* There is no FocusIn, this means focus went to a window that
453 is not being managed, or a window on another screen. */
454 Window win, root;
455 gint i;
456 guint u;
457 xerror_set_ignore(TRUE);
458 if (XGetInputFocus(ob_display, &win, &i) != 0 &&
459 XGetGeometry(ob_display, win, &root, &i,&i,&u,&u,&u,&u) != 0 &&
460 root != RootWindow(ob_display, ob_screen))
461 {
462 ob_debug_type(OB_DEBUG_FOCUS,
463 "Focus went to another screen !\n");
464 focus_left_screen = TRUE;
465 }
466 else
467 ob_debug_type(OB_DEBUG_FOCUS,
468 "Focus went to a black hole !\n");
469 xerror_set_ignore(FALSE);
470 /* nothing is focused */
471 focus_set_client(NULL);
472 } else if (ce.xany.window == e->xany.window) {
473 ob_debug_type(OB_DEBUG_FOCUS, "Focus didn't go anywhere\n");
474 /* If focus didn't actually move anywhere, there is nothing to do*/
475 nomove = TRUE;
476 } else {
477 /* Focus did move, so process the FocusIn event */
478 ObEventData ed = { .ignored = FALSE };
479 event_process(&ce, &ed);
480 if (ed.ignored) {
481 /* The FocusIn was ignored, this means it was on a window
482 that isn't a client. */
483 ob_debug_type(OB_DEBUG_FOCUS,
484 "Focus went to an unmanaged window 0x%x !\n",
485 ce.xfocus.window);
486 focus_fallback(TRUE);
487 }
488 }
489
490 if (client && !nomove) {
491 frame_adjust_focus(client->frame, FALSE);
492 /* focus_set_client has already been called for sure */
493 client_calc_layer(client);
494 }
495 } else if (group)
496 event_handle_group(group, e);
497 else if (client)
498 event_handle_client(client, e);
499 else if (dockapp)
500 event_handle_dockapp(dockapp, e);
501 else if (dock)
502 event_handle_dock(dock, e);
503 else if (window == RootWindow(ob_display, ob_screen))
504 event_handle_root(e);
505 else if (e->type == MapRequest)
506 client_manage(window);
507 else if (e->type == ConfigureRequest) {
508 /* unhandled configure requests must be used to configure the
509 window directly */
510 XWindowChanges xwc;
511
512 xwc.x = e->xconfigurerequest.x;
513 xwc.y = e->xconfigurerequest.y;
514 xwc.width = e->xconfigurerequest.width;
515 xwc.height = e->xconfigurerequest.height;
516 xwc.border_width = e->xconfigurerequest.border_width;
517 xwc.sibling = e->xconfigurerequest.above;
518 xwc.stack_mode = e->xconfigurerequest.detail;
519
520 /* we are not to be held responsible if someone sends us an
521 invalid request! */
522 xerror_set_ignore(TRUE);
523 XConfigureWindow(ob_display, window,
524 e->xconfigurerequest.value_mask, &xwc);
525 xerror_set_ignore(FALSE);
526 }
527 #ifdef SYNC
528 else if (extensions_sync &&
529 e->type == extensions_sync_event_basep + XSyncAlarmNotify)
530 {
531 XSyncAlarmNotifyEvent *se = (XSyncAlarmNotifyEvent*)e;
532 if (se->alarm == moveresize_alarm && moveresize_in_progress)
533 moveresize_event(e);
534 }
535 #endif
536
537 if (e->type == ButtonPress || e->type == ButtonRelease ||
538 e->type == MotionNotify || e->type == KeyPress ||
539 e->type == KeyRelease)
540 {
541 event_handle_user_input(client, e);
542 }
543
544 /* if something happens and it's not from an XEvent, then we don't know
545 the time */
546 event_curtime = CurrentTime;
547 }
548
549 static void event_handle_root(XEvent *e)
550 {
551 Atom msgtype;
552
553 switch(e->type) {
554 case SelectionClear:
555 ob_debug("Another WM has requested to replace us. Exiting.\n");
556 ob_exit_replace();
557 break;
558
559 case ClientMessage:
560 if (e->xclient.format != 32) break;
561
562 msgtype = e->xclient.message_type;
563 if (msgtype == prop_atoms.net_current_desktop) {
564 guint d = e->xclient.data.l[0];
565 if (d < screen_num_desktops) {
566 event_curtime = e->xclient.data.l[1];
567 if (event_curtime == 0)
568 ob_debug_type(OB_DEBUG_APP_BUGS,
569 "_NET_CURRENT_DESKTOP message is missing "
570 "a timestamp\n");
571 screen_set_desktop(d);
572 }
573 } else if (msgtype == prop_atoms.net_number_of_desktops) {
574 guint d = e->xclient.data.l[0];
575 if (d > 0)
576 screen_set_num_desktops(d);
577 } else if (msgtype == prop_atoms.net_showing_desktop) {
578 screen_show_desktop(e->xclient.data.l[0] != 0, TRUE);
579 } else if (msgtype == prop_atoms.openbox_control) {
580 if (e->xclient.data.l[0] == 1)
581 ob_reconfigure();
582 else if (e->xclient.data.l[0] == 2)
583 ob_restart();
584 }
585 break;
586 case PropertyNotify:
587 if (e->xproperty.atom == prop_atoms.net_desktop_names)
588 screen_update_desktop_names();
589 else if (e->xproperty.atom == prop_atoms.net_desktop_layout)
590 screen_update_layout();
591 break;
592 case ConfigureNotify:
593 #ifdef XRANDR
594 XRRUpdateConfiguration(e);
595 #endif
596 screen_resize();
597 break;
598 default:
599 ;
600 }
601 }
602
603 static void event_handle_group(ObGroup *group, XEvent *e)
604 {
605 GSList *it;
606
607 g_assert(e->type == PropertyNotify);
608
609 for (it = group->members; it; it = g_slist_next(it))
610 event_handle_client(it->data, e);
611 }
612
613 void event_enter_client(ObClient *client)
614 {
615 g_assert(config_focus_follow);
616
617 if (client_normal(client) && client_can_focus(client)) {
618 if (config_focus_delay) {
619 ObFocusDelayData *data;
620
621 ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
622
623 data = g_new(ObFocusDelayData, 1);
624 data->client = client;
625 data->time = event_curtime;
626
627 ob_main_loop_timeout_add(ob_main_loop,
628 config_focus_delay,
629 focus_delay_func,
630 data, focus_delay_cmp, focus_delay_dest);
631 } else {
632 ObFocusDelayData data;
633 data.client = client;
634 data.time = event_curtime;
635 focus_delay_func(&data);
636 }
637 }
638 }
639
640 static void event_handle_client(ObClient *client, XEvent *e)
641 {
642 XEvent ce;
643 Atom msgtype;
644 ObFrameContext con;
645
646 switch (e->type) {
647 case ButtonPress:
648 case ButtonRelease:
649 /* Wheel buttons don't draw because they are an instant click, so it
650 is a waste of resources to go drawing it. */
651 if (!(e->xbutton.button == 4 || e->xbutton.button == 5)) {
652 con = frame_context(client, e->xbutton.window);
653 con = mouse_button_frame_context(con, e->xbutton.button);
654 switch (con) {
655 case OB_FRAME_CONTEXT_MAXIMIZE:
656 client->frame->max_press = (e->type == ButtonPress);
657 framerender_frame(client->frame);
658 break;
659 case OB_FRAME_CONTEXT_CLOSE:
660 client->frame->close_press = (e->type == ButtonPress);
661 framerender_frame(client->frame);
662 break;
663 case OB_FRAME_CONTEXT_ICONIFY:
664 client->frame->iconify_press = (e->type == ButtonPress);
665 framerender_frame(client->frame);
666 break;
667 case OB_FRAME_CONTEXT_ALLDESKTOPS:
668 client->frame->desk_press = (e->type == ButtonPress);
669 framerender_frame(client->frame);
670 break;
671 case OB_FRAME_CONTEXT_SHADE:
672 client->frame->shade_press = (e->type == ButtonPress);
673 framerender_frame(client->frame);
674 break;
675 default:
676 /* nothing changes with clicks for any other contexts */
677 break;
678 }
679 }
680 break;
681 case LeaveNotify:
682 con = frame_context(client, e->xcrossing.window);
683 switch (con) {
684 case OB_FRAME_CONTEXT_MAXIMIZE:
685 client->frame->max_hover = FALSE;
686 frame_adjust_state(client->frame);
687 break;
688 case OB_FRAME_CONTEXT_ALLDESKTOPS:
689 client->frame->desk_hover = FALSE;
690 frame_adjust_state(client->frame);
691 break;
692 case OB_FRAME_CONTEXT_SHADE:
693 client->frame->shade_hover = FALSE;
694 frame_adjust_state(client->frame);
695 break;
696 case OB_FRAME_CONTEXT_ICONIFY:
697 client->frame->iconify_hover = FALSE;
698 frame_adjust_state(client->frame);
699 break;
700 case OB_FRAME_CONTEXT_CLOSE:
701 client->frame->close_hover = FALSE;
702 frame_adjust_state(client->frame);
703 break;
704 case OB_FRAME_CONTEXT_FRAME:
705 /* When the mouse leaves an animating window, don't use the
706 corresponding enter events. Pretend like the animating window
707 doesn't even exist..! */
708 if (frame_iconify_animating(client->frame))
709 event_ignore_queued_enters();
710
711 ob_debug_type(OB_DEBUG_FOCUS,
712 "%sNotify mode %d detail %d on %lx\n",
713 (e->type == EnterNotify ? "Enter" : "Leave"),
714 e->xcrossing.mode,
715 e->xcrossing.detail, (client?client->window:0));
716 if (keyboard_interactively_grabbed())
717 break;
718 if (config_focus_follow && config_focus_delay &&
719 /* leave inferior events can happen when the mouse goes onto
720 the window's border and then into the window before the
721 delay is up */
722 e->xcrossing.detail != NotifyInferior)
723 {
724 ob_main_loop_timeout_remove_data(ob_main_loop,
725 focus_delay_func,
726 client, FALSE);
727 }
728 break;
729 default:
730 break;
731 }
732 break;
733 case EnterNotify:
734 {
735 gboolean nofocus = FALSE;
736
737 if (ignore_enter_focus) {
738 ignore_enter_focus--;
739 nofocus = TRUE;
740 }
741
742 con = frame_context(client, e->xcrossing.window);
743 switch (con) {
744 case OB_FRAME_CONTEXT_MAXIMIZE:
745 client->frame->max_hover = TRUE;
746 frame_adjust_state(client->frame);
747 break;
748 case OB_FRAME_CONTEXT_ALLDESKTOPS:
749 client->frame->desk_hover = TRUE;
750 frame_adjust_state(client->frame);
751 break;
752 case OB_FRAME_CONTEXT_SHADE:
753 client->frame->shade_hover = TRUE;
754 frame_adjust_state(client->frame);
755 break;
756 case OB_FRAME_CONTEXT_ICONIFY:
757 client->frame->iconify_hover = TRUE;
758 frame_adjust_state(client->frame);
759 break;
760 case OB_FRAME_CONTEXT_CLOSE:
761 client->frame->close_hover = TRUE;
762 frame_adjust_state(client->frame);
763 break;
764 case OB_FRAME_CONTEXT_FRAME:
765 if (keyboard_interactively_grabbed())
766 break;
767 if (e->xcrossing.mode == NotifyGrab ||
768 e->xcrossing.mode == NotifyUngrab ||
769 /*ignore enters when we're already in the window */
770 e->xcrossing.detail == NotifyInferior)
771 {
772 ob_debug_type(OB_DEBUG_FOCUS,
773 "%sNotify mode %d detail %d on %lx IGNORED\n",
774 (e->type == EnterNotify ? "Enter" : "Leave"),
775 e->xcrossing.mode,
776 e->xcrossing.detail, client?client->window:0);
777 } else {
778 ob_debug_type(OB_DEBUG_FOCUS,
779 "%sNotify mode %d detail %d on %lx, "
780 "focusing window: %d\n",
781 (e->type == EnterNotify ? "Enter" : "Leave"),
782 e->xcrossing.mode,
783 e->xcrossing.detail, (client?client->window:0),
784 !nofocus);
785 if (!nofocus && config_focus_follow)
786 event_enter_client(client);
787 }
788 break;
789 default:
790 break;
791 }
792 break;
793 }
794 case ConfigureRequest:
795 /* dont compress these unless you're going to watch for property
796 notifies in between (these can change what the configure would
797 do to the window).
798 also you can't compress stacking events
799 */
800
801 ob_debug("ConfigureRequest desktop %d wmstate %d vis %d\n",
802 screen_desktop, client->wmstate, client->frame->visible);
803
804 /* don't allow clients to move shaded windows (fvwm does this) */
805 if (client->shaded) {
806 e->xconfigurerequest.value_mask &= ~CWX;
807 e->xconfigurerequest.value_mask &= ~CWY;
808 }
809
810 /* resize, then move, as specified in the EWMH section 7.7 */
811 if (e->xconfigurerequest.value_mask & (CWWidth | CWHeight |
812 CWX | CWY |
813 CWBorderWidth)) {
814 gint x, y, w, h;
815
816 if (e->xconfigurerequest.value_mask & CWBorderWidth)
817 client->border_width = e->xconfigurerequest.border_width;
818
819 x = (e->xconfigurerequest.value_mask & CWX) ?
820 e->xconfigurerequest.x : client->area.x;
821 y = (e->xconfigurerequest.value_mask & CWY) ?
822 e->xconfigurerequest.y : client->area.y;
823 w = (e->xconfigurerequest.value_mask & CWWidth) ?
824 e->xconfigurerequest.width : client->area.width;
825 h = (e->xconfigurerequest.value_mask & CWHeight) ?
826 e->xconfigurerequest.height : client->area.height;
827
828 ob_debug("ConfigureRequest x %d %d y %d %d\n",
829 e->xconfigurerequest.value_mask & CWX, x,
830 e->xconfigurerequest.value_mask & CWY, y);
831
832 /* check for broken apps moving to their root position
833
834 XXX remove this some day...that would be nice. right now all
835 kde apps do this when they try activate themselves on another
836 desktop. eg. open amarok window on desktop 1, switch to desktop
837 2, click amarok tray icon. it will move by its decoration size.
838 */
839 if (x != client->area.x &&
840 x == (client->frame->area.x + client->frame->size.left -
841 (gint)client->border_width) &&
842 y != client->area.y &&
843 y == (client->frame->area.y + client->frame->size.top -
844 (gint)client->border_width))
845 {
846 ob_debug_type(OB_DEBUG_APP_BUGS,
847 "Application %s is trying to move via "
848 "ConfigureRequest to it's root window position "
849 "but it is not using StaticGravity\n",
850 client->title);
851 /* don't move it */
852 x = client->area.x;
853 y = client->area.y;
854 }
855
856 client_find_onscreen(client, &x, &y, w, h, FALSE);
857 client_configure_full(client, x, y, w, h, FALSE, TRUE, TRUE);
858 }
859
860 if (e->xconfigurerequest.value_mask & CWStackMode) {
861 switch (e->xconfigurerequest.detail) {
862 case Below:
863 case BottomIf:
864 /* Apps are so rude. And this is totally disconnected from
865 activation/focus. Bleh. */
866 /*client_lower(client);*/
867 break;
868
869 case Above:
870 case TopIf:
871 default:
872 /* Apps are so rude. And this is totally disconnected from
873 activation/focus. Bleh. */
874 /*client_raise(client);*/
875 break;
876 }
877 }
878 break;
879 case UnmapNotify:
880 if (client->ignore_unmaps) {
881 client->ignore_unmaps--;
882 break;
883 }
884 ob_debug("UnmapNotify for window 0x%x eventwin 0x%x sendevent %d "
885 "ignores left %d\n",
886 client->window, e->xunmap.event, e->xunmap.from_configure,
887 client->ignore_unmaps);
888 client_unmanage(client);
889 break;
890 case DestroyNotify:
891 ob_debug("DestroyNotify for window 0x%x\n", client->window);
892 client_unmanage(client);
893 break;
894 case ReparentNotify:
895 /* this is when the client is first taken captive in the frame */
896 if (e->xreparent.parent == client->frame->plate) break;
897
898 /*
899 This event is quite rare and is usually handled in unmapHandler.
900 However, if the window is unmapped when the reparent event occurs,
901 the window manager never sees it because an unmap event is not sent
902 to an already unmapped window.
903 */
904
905 /* we don't want the reparent event, put it back on the stack for the
906 X server to deal with after we unmanage the window */
907 XPutBackEvent(ob_display, e);
908
909 ob_debug("ReparentNotify for window 0x%x\n", client->window);
910 client_unmanage(client);
911 break;
912 case MapRequest:
913 ob_debug("MapRequest for 0x%lx\n", client->window);
914 if (!client->iconic) break; /* this normally doesn't happen, but if it
915 does, we don't want it!
916 it can happen now when the window is on
917 another desktop, but we still don't
918 want it! */
919 client_activate(client, FALSE, TRUE);
920 break;
921 case ClientMessage:
922 /* validate cuz we query stuff off the client here */
923 if (!client_validate(client)) break;
924
925 if (e->xclient.format != 32) return;
926
927 msgtype = e->xclient.message_type;
928 if (msgtype == prop_atoms.wm_change_state) {
929 /* compress changes into a single change */
930 while (XCheckTypedWindowEvent(ob_display, client->window,
931 e->type, &ce)) {
932 /* XXX: it would be nice to compress ALL messages of a
933 type, not just messages in a row without other
934 message types between. */
935 if (ce.xclient.message_type != msgtype) {
936 XPutBackEvent(ob_display, &ce);
937 break;
938 }
939 e->xclient = ce.xclient;
940 }
941 client_set_wm_state(client, e->xclient.data.l[0]);
942 } else if (msgtype == prop_atoms.net_wm_desktop) {
943 /* compress changes into a single change */
944 while (XCheckTypedWindowEvent(ob_display, client->window,
945 e->type, &ce)) {
946 /* XXX: it would be nice to compress ALL messages of a
947 type, not just messages in a row without other
948 message types between. */
949 if (ce.xclient.message_type != msgtype) {
950 XPutBackEvent(ob_display, &ce);
951 break;
952 }
953 e->xclient = ce.xclient;
954 }
955 if ((unsigned)e->xclient.data.l[0] < screen_num_desktops ||
956 (unsigned)e->xclient.data.l[0] == DESKTOP_ALL)
957 client_set_desktop(client, (unsigned)e->xclient.data.l[0],
958 FALSE);
959 } else if (msgtype == prop_atoms.net_wm_state) {
960 /* can't compress these */
961 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
962 (e->xclient.data.l[0] == 0 ? "Remove" :
963 e->xclient.data.l[0] == 1 ? "Add" :
964 e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
965 e->xclient.data.l[1], e->xclient.data.l[2],
966 client->window);
967 client_set_state(client, e->xclient.data.l[0],
968 e->xclient.data.l[1], e->xclient.data.l[2]);
969 } else if (msgtype == prop_atoms.net_close_window) {
970 ob_debug("net_close_window for 0x%lx\n", client->window);
971 client_close(client);
972 } else if (msgtype == prop_atoms.net_active_window) {
973 ob_debug("net_active_window for 0x%lx source=%s\n",
974 client->window,
975 (e->xclient.data.l[0] == 0 ? "unknown" :
976 (e->xclient.data.l[0] == 1 ? "application" :
977 (e->xclient.data.l[0] == 2 ? "user" : "INVALID"))));
978 /* XXX make use of data.l[2] !? */
979 event_curtime = e->xclient.data.l[1];
980 ob_debug_type(OB_DEBUG_APP_BUGS,
981 "_NET_ACTIVE_WINDOW message for window %s is "
982 "missing a timestamp\n", client->title);
983 client_activate(client, FALSE,
984 (e->xclient.data.l[0] == 0 ||
985 e->xclient.data.l[0] == 2));
986 } else if (msgtype == prop_atoms.net_wm_moveresize) {
987 ob_debug("net_wm_moveresize for 0x%lx direction %d\n",
988 client->window, e->xclient.data.l[2]);
989 if ((Atom)e->xclient.data.l[2] ==
990 prop_atoms.net_wm_moveresize_size_topleft ||
991 (Atom)e->xclient.data.l[2] ==
992 prop_atoms.net_wm_moveresize_size_top ||
993 (Atom)e->xclient.data.l[2] ==
994 prop_atoms.net_wm_moveresize_size_topright ||
995 (Atom)e->xclient.data.l[2] ==
996 prop_atoms.net_wm_moveresize_size_right ||
997 (Atom)e->xclient.data.l[2] ==
998 prop_atoms.net_wm_moveresize_size_right ||
999 (Atom)e->xclient.data.l[2] ==
1000 prop_atoms.net_wm_moveresize_size_bottomright ||
1001 (Atom)e->xclient.data.l[2] ==
1002 prop_atoms.net_wm_moveresize_size_bottom ||
1003 (Atom)e->xclient.data.l[2] ==
1004 prop_atoms.net_wm_moveresize_size_bottomleft ||
1005 (Atom)e->xclient.data.l[2] ==
1006 prop_atoms.net_wm_moveresize_size_left ||
1007 (Atom)e->xclient.data.l[2] ==
1008 prop_atoms.net_wm_moveresize_move ||
1009 (Atom)e->xclient.data.l[2] ==
1010 prop_atoms.net_wm_moveresize_size_keyboard ||
1011 (Atom)e->xclient.data.l[2] ==
1012 prop_atoms.net_wm_moveresize_move_keyboard) {
1013
1014 moveresize_start(client, e->xclient.data.l[0],
1015 e->xclient.data.l[1], e->xclient.data.l[3],
1016 e->xclient.data.l[2]);
1017 }
1018 else if ((Atom)e->xclient.data.l[2] ==
1019 prop_atoms.net_wm_moveresize_cancel)
1020 moveresize_end(TRUE);
1021 } else if (msgtype == prop_atoms.net_moveresize_window) {
1022 gint grav, x, y, w, h;
1023
1024 if (e->xclient.data.l[0] & 0xff)
1025 grav = e->xclient.data.l[0] & 0xff;
1026 else
1027 grav = client->gravity;
1028
1029 if (e->xclient.data.l[0] & 1 << 8)
1030 x = e->xclient.data.l[1];
1031 else
1032 x = client->area.x;
1033 if (e->xclient.data.l[0] & 1 << 9)
1034 y = e->xclient.data.l[2];
1035 else
1036 y = client->area.y;
1037 if (e->xclient.data.l[0] & 1 << 10)
1038 w = e->xclient.data.l[3];
1039 else
1040 w = client->area.width;
1041 if (e->xclient.data.l[0] & 1 << 11)
1042 h = e->xclient.data.l[4];
1043 else
1044 h = client->area.height;
1045
1046 ob_debug("MOVERESIZE x %d %d y %d %d\n",
1047 e->xclient.data.l[0] & 1 << 8, x,
1048 e->xclient.data.l[0] & 1 << 9, y);
1049 client_convert_gravity(client, grav, &x, &y, w, h);
1050 client_find_onscreen(client, &x, &y, w, h, FALSE);
1051 client_configure(client, x, y, w, h, FALSE, TRUE);
1052 }
1053 break;
1054 case PropertyNotify:
1055 /* validate cuz we query stuff off the client here */
1056 if (!client_validate(client)) break;
1057
1058 /* compress changes to a single property into a single change */
1059 while (XCheckTypedWindowEvent(ob_display, client->window,
1060 e->type, &ce)) {
1061 Atom a, b;
1062
1063 /* XXX: it would be nice to compress ALL changes to a property,
1064 not just changes in a row without other props between. */
1065
1066 a = ce.xproperty.atom;
1067 b = e->xproperty.atom;
1068
1069 if (a == b)
1070 continue;
1071 if ((a == prop_atoms.net_wm_name ||
1072 a == prop_atoms.wm_name ||
1073 a == prop_atoms.net_wm_icon_name ||
1074 a == prop_atoms.wm_icon_name)
1075 &&
1076 (b == prop_atoms.net_wm_name ||
1077 b == prop_atoms.wm_name ||
1078 b == prop_atoms.net_wm_icon_name ||
1079 b == prop_atoms.wm_icon_name)) {
1080 continue;
1081 }
1082 if (a == prop_atoms.net_wm_icon &&
1083 b == prop_atoms.net_wm_icon)
1084 continue;
1085
1086 XPutBackEvent(ob_display, &ce);
1087 break;
1088 }
1089
1090 msgtype = e->xproperty.atom;
1091 if (msgtype == XA_WM_NORMAL_HINTS) {
1092 client_update_normal_hints(client);
1093 /* normal hints can make a window non-resizable */
1094 client_setup_decor_and_functions(client);
1095 } else if (msgtype == XA_WM_HINTS) {
1096 client_update_wmhints(client);
1097 } else if (msgtype == XA_WM_TRANSIENT_FOR) {
1098 client_update_transient_for(client);
1099 client_get_type(client);
1100 /* type may have changed, so update the layer */
1101 client_calc_layer(client);
1102 client_setup_decor_and_functions(client);
1103 } else if (msgtype == prop_atoms.net_wm_name ||
1104 msgtype == prop_atoms.wm_name ||
1105 msgtype == prop_atoms.net_wm_icon_name ||
1106 msgtype == prop_atoms.wm_icon_name) {
1107 client_update_title(client);
1108 } else if (msgtype == prop_atoms.wm_class) {
1109 client_update_class(client);
1110 } else if (msgtype == prop_atoms.wm_protocols) {
1111 client_update_protocols(client);
1112 client_setup_decor_and_functions(client);
1113 }
1114 else if (msgtype == prop_atoms.net_wm_strut) {
1115 client_update_strut(client);
1116 }
1117 else if (msgtype == prop_atoms.net_wm_icon) {
1118 client_update_icons(client);
1119 }
1120 else if (msgtype == prop_atoms.net_wm_icon_geometry) {
1121 client_update_icon_geometry(client);
1122 }
1123 else if (msgtype == prop_atoms.net_wm_user_time) {
1124 client_update_user_time(client);
1125 }
1126 #ifdef SYNC
1127 else if (msgtype == prop_atoms.net_wm_sync_request_counter) {
1128 client_update_sync_request_counter(client);
1129 }
1130 #endif
1131 else if (msgtype == prop_atoms.sm_client_id) {
1132 client_update_sm_client_id(client);
1133 }
1134 case ColormapNotify:
1135 client_update_colormap(client, e->xcolormap.colormap);
1136 break;
1137 default:
1138 ;
1139 #ifdef SHAPE
1140 if (extensions_shape && e->type == extensions_shape_event_basep) {
1141 client->shaped = ((XShapeEvent*)e)->shaped;
1142 frame_adjust_shape(client->frame);
1143 }
1144 #endif
1145 }
1146 }
1147
1148 static void event_handle_dock(ObDock *s, XEvent *e)
1149 {
1150 switch (e->type) {
1151 case ButtonPress:
1152 if (e->xbutton.button == 1)
1153 stacking_raise(DOCK_AS_WINDOW(s));
1154 else if (e->xbutton.button == 2)
1155 stacking_lower(DOCK_AS_WINDOW(s));
1156 break;
1157 case EnterNotify:
1158 dock_hide(FALSE);
1159 break;
1160 case LeaveNotify:
1161 dock_hide(TRUE);
1162 break;
1163 }
1164 }
1165
1166 static void event_handle_dockapp(ObDockApp *app, XEvent *e)
1167 {
1168 switch (e->type) {
1169 case MotionNotify:
1170 dock_app_drag(app, &e->xmotion);
1171 break;
1172 case UnmapNotify:
1173 if (app->ignore_unmaps) {
1174 app->ignore_unmaps--;
1175 break;
1176 }
1177 dock_remove(app, TRUE);
1178 break;
1179 case DestroyNotify:
1180 dock_remove(app, FALSE);
1181 break;
1182 case ReparentNotify:
1183 dock_remove(app, FALSE);
1184 break;
1185 case ConfigureNotify:
1186 dock_app_configure(app, e->xconfigure.width, e->xconfigure.height);
1187 break;
1188 }
1189 }
1190
1191 static ObMenuFrame* find_active_menu()
1192 {
1193 GList *it;
1194 ObMenuFrame *ret = NULL;
1195
1196 for (it = menu_frame_visible; it; it = g_list_next(it)) {
1197 ret = it->data;
1198 if (ret->selected)
1199 break;
1200 ret = NULL;
1201 }
1202 return ret;
1203 }
1204
1205 static ObMenuFrame* find_active_or_last_menu()
1206 {
1207 ObMenuFrame *ret = NULL;
1208
1209 ret = find_active_menu();
1210 if (!ret && menu_frame_visible)
1211 ret = menu_frame_visible->data;
1212 return ret;
1213 }
1214
1215 static gboolean event_handle_menu_keyboard(XEvent *ev)
1216 {
1217 guint keycode, state;
1218 gunichar unikey;
1219 ObMenuFrame *frame;
1220 gboolean ret = TRUE;
1221
1222 keycode = ev->xkey.keycode;
1223 state = ev->xkey.state;
1224 unikey = translate_unichar(keycode);
1225
1226 frame = find_active_or_last_menu();
1227 if (frame == NULL)
1228 ret = FALSE;
1229
1230 else if (keycode == ob_keycode(OB_KEY_ESCAPE) && state == 0) {
1231 /* Escape closes the active menu */
1232 menu_frame_hide(frame);
1233 }
1234
1235 else if (keycode == ob_keycode(OB_KEY_RETURN) && (state == 0 ||
1236 state == ControlMask))
1237 {
1238 /* Enter runs the active item or goes into the submenu.
1239 Control-Enter runs it without closing the menu. */
1240 if (frame->child)
1241 menu_frame_select_next(frame->child);
1242 else
1243 menu_entry_frame_execute(frame->selected, state, ev->xkey.time);
1244 }
1245
1246 else if (keycode == ob_keycode(OB_KEY_LEFT) && ev->xkey.state == 0) {
1247 /* Left goes to the parent menu */
1248 menu_frame_select(frame, NULL, TRUE);
1249 }
1250
1251 else if (keycode == ob_keycode(OB_KEY_RIGHT) && ev->xkey.state == 0) {
1252 /* Right goes to the selected submenu */
1253 if (frame->child) menu_frame_select_next(frame->child);
1254 }
1255
1256 else if (keycode == ob_keycode(OB_KEY_UP) && state == 0) {
1257 menu_frame_select_previous(frame);
1258 }
1259
1260 else if (keycode == ob_keycode(OB_KEY_DOWN) && state == 0) {
1261 menu_frame_select_next(frame);
1262 }
1263
1264 /* keyboard accelerator shortcuts. */
1265 else if (ev->xkey.state == 0 &&
1266 /* was it a valid key? */
1267 unikey != 0 &&
1268 /* don't bother if the menu is empty. */
1269 frame->entries)
1270 {
1271 GList *start;
1272 GList *it;
1273 ObMenuEntryFrame *found = NULL;
1274 guint num_found = 0;
1275
1276 /* start after the selected one */
1277 start = frame->entries;
1278 if (frame->selected) {
1279 for (it = start; frame->selected != it->data; it = g_list_next(it))
1280 g_assert(it != NULL); /* nothing was selected? */
1281 /* next with wraparound */
1282 start = g_list_next(it);
1283 if (start == NULL) start = frame->entries;
1284 }
1285
1286 it = start;
1287 do {
1288 ObMenuEntryFrame *e = it->data;
1289 gunichar entrykey = 0;
1290
1291 if (e->entry->type == OB_MENU_ENTRY_TYPE_NORMAL &&
1292 e->entry->data.normal.enabled)
1293 entrykey = e->entry->data.normal.shortcut;
1294 else if (e->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU)
1295 entrykey = e->entry->data.submenu.submenu->shortcut;
1296
1297 if (unikey == entrykey) {
1298 if (found == NULL) found = e;
1299 ++num_found;
1300 }
1301
1302 /* next with wraparound */
1303 it = g_list_next(it);
1304 if (it == NULL) it = frame->entries;
1305 } while (it != start);
1306
1307 if (found) {
1308 if (found->entry->type == OB_MENU_ENTRY_TYPE_NORMAL &&
1309 num_found == 1)
1310 {
1311 menu_frame_select(frame, found, TRUE);
1312 usleep(50000);
1313 menu_entry_frame_execute(found, state, ev->xkey.time);
1314 } else {
1315 menu_frame_select(frame, found, TRUE);
1316 if (num_found == 1)
1317 menu_frame_select_next(frame->child);
1318 }
1319 } else
1320 ret = FALSE;
1321 }
1322 else
1323 ret = FALSE;
1324
1325 return ret;
1326 }
1327
1328 static gboolean event_handle_menu(XEvent *ev)
1329 {
1330 ObMenuFrame *f;
1331 ObMenuEntryFrame *e;
1332 gboolean ret = TRUE;
1333
1334 switch (ev->type) {
1335 case ButtonRelease:
1336 if ((ev->xbutton.button < 4 || ev->xbutton.button > 5)
1337 && menu_can_hide)
1338 {
1339 if ((e = menu_entry_frame_under(ev->xbutton.x_root,
1340 ev->xbutton.y_root)))
1341 menu_entry_frame_execute(e, ev->xbutton.state,
1342 ev->xbutton.time);
1343 else
1344 menu_frame_hide_all();
1345 }
1346 break;
1347 case EnterNotify:
1348 if ((e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.window))) {
1349 if (e->ignore_enters)
1350 --e->ignore_enters;
1351 else
1352 menu_frame_select(e->frame, e, FALSE);
1353 }
1354 break;
1355 case LeaveNotify:
1356 if ((e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.window)) &&
1357 (f = find_active_menu()) && f->selected == e &&
1358 e->entry->type != OB_MENU_ENTRY_TYPE_SUBMENU)
1359 {
1360 menu_frame_select(e->frame, NULL, FALSE);
1361 }
1362 case MotionNotify:
1363 if ((e = menu_entry_frame_under(ev->xmotion.x_root,
1364 ev->xmotion.y_root)))
1365 menu_frame_select(e->frame, e, FALSE);
1366 break;
1367 case KeyPress:
1368 ret = event_handle_menu_keyboard(ev);
1369 break;
1370 }
1371 return ret;
1372 }
1373
1374 static void event_handle_user_input(ObClient *client, XEvent *e)
1375 {
1376 g_assert(e->type == ButtonPress || e->type == ButtonRelease ||
1377 e->type == MotionNotify || e->type == KeyPress ||
1378 e->type == KeyRelease);
1379
1380 if (menu_frame_visible) {
1381 if (event_handle_menu(e))
1382 /* don't use the event if the menu used it, but if the menu
1383 didn't use it and it's a keypress that is bound, it will
1384 close the menu and be used */
1385 return;
1386 }
1387
1388 /* if the keyboard interactive action uses the event then dont
1389 use it for bindings. likewise is moveresize uses the event. */
1390 if (!keyboard_process_interactive_grab(e, &client) &&
1391 !(moveresize_in_progress && moveresize_event(e)))
1392 {
1393 if (moveresize_in_progress)
1394 /* make further actions work on the client being
1395 moved/resized */
1396 client = moveresize_client;
1397
1398 menu_can_hide = FALSE;
1399 ob_main_loop_timeout_add(ob_main_loop,
1400 config_menu_hide_delay * 1000,
1401 menu_hide_delay_func,
1402 NULL, g_direct_equal, NULL);
1403
1404 if (e->type == ButtonPress ||
1405 e->type == ButtonRelease ||
1406 e->type == MotionNotify)
1407 {
1408 /* the frame may not be "visible" but they can still click on it
1409 in the case where it is animating before disappearing */
1410 if (!client || !frame_iconify_animating(client->frame))
1411 mouse_event(client, e);
1412 } else if (e->type == KeyPress) {
1413 keyboard_event((focus_cycle_target ? focus_cycle_target :
1414 (client ? client : focus_client)), e);
1415 }
1416 }
1417 }
1418
1419 static gboolean menu_hide_delay_func(gpointer data)
1420 {
1421 menu_can_hide = TRUE;
1422 return FALSE; /* no repeat */
1423 }
1424
1425 static void focus_delay_dest(gpointer data)
1426 {
1427 g_free(data);
1428 }
1429
1430 static gboolean focus_delay_cmp(gconstpointer d1, gconstpointer d2)
1431 {
1432 const ObFocusDelayData *f1 = d1;
1433 return f1->client == d2;
1434 }
1435
1436 static gboolean focus_delay_func(gpointer data)
1437 {
1438 ObFocusDelayData *d = data;
1439 Time old = event_curtime;
1440
1441 event_curtime = d->time;
1442 if (focus_client != d->client) {
1443 if (client_focus(d->client) && config_focus_raise)
1444 client_raise(d->client);
1445 }
1446 event_curtime = old;
1447 return FALSE; /* no repeat */
1448 }
1449
1450 static void focus_delay_client_dest(ObClient *client, gpointer data)
1451 {
1452 ob_main_loop_timeout_remove_data(ob_main_loop, focus_delay_func,
1453 client, FALSE);
1454 }
1455
1456 void event_halt_focus_delay()
1457 {
1458 ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
1459 }
1460
1461 void event_ignore_queued_enters()
1462 {
1463 GSList *saved = NULL, *it;
1464 XEvent *e;
1465
1466 XSync(ob_display, FALSE);
1467
1468 /* count the events */
1469 while (TRUE) {
1470 e = g_new(XEvent, 1);
1471 if (XCheckTypedEvent(ob_display, EnterNotify, e)) {
1472 ObWindow *win;
1473
1474 win = g_hash_table_lookup(window_map, &e->xany.window);
1475 if (win && WINDOW_IS_CLIENT(win))
1476 ++ignore_enter_focus;
1477
1478 saved = g_slist_append(saved, e);
1479 } else {
1480 g_free(e);
1481 break;
1482 }
1483 }
1484 /* put the events back */
1485 for (it = saved; it; it = g_slist_next(it)) {
1486 XPutBackEvent(ob_display, it->data);
1487 g_free(it->data);
1488 }
1489 g_slist_free(saved);
1490 }
1491
1492 gboolean event_time_after(Time t1, Time t2)
1493 {
1494 g_assert(t1 != CurrentTime);
1495 g_assert(t2 != CurrentTime);
1496
1497 /*
1498 Timestamp values wrap around (after about 49.7 days). The server, given
1499 its current time is represented by timestamp T, always interprets
1500 timestamps from clients by treating half of the timestamp space as being
1501 later in time than T.
1502 - http://tronche.com/gui/x/xlib/input/pointer-grabbing.html
1503 */
1504
1505 /* TIME_HALF is half of the number space of a Time type variable */
1506 #define TIME_HALF (Time)(1 << (sizeof(Time)*8-1))
1507
1508 if (t2 >= TIME_HALF)
1509 /* t2 is in the second half so t1 might wrap around and be smaller than
1510 t2 */
1511 return t1 >= t2 || t1 < (t2 + TIME_HALF);
1512 else
1513 /* t2 is in the first half so t1 has to come after it */
1514 return t1 >= t2 && t1 < (t2 + TIME_HALF);
1515 }
This page took 0.097707 seconds and 3 git commands to generate.