]> Dogcows Code - chaz/openbox/blob - openbox/event.c
not passing timestamps sometimes and not others. set event_curtime when client messag...
[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 Ben 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 "mouse.h"
35 #include "mainloop.h"
36 #include "framerender.h"
37 #include "focus.h"
38 #include "moveresize.h"
39 #include "group.h"
40 #include "stacking.h"
41 #include "extensions.h"
42
43 #include <X11/Xlib.h>
44 #include <X11/keysym.h>
45 #include <X11/Xatom.h>
46 #include <glib.h>
47
48 #ifdef HAVE_SYS_SELECT_H
49 # include <sys/select.h>
50 #endif
51 #ifdef HAVE_SIGNAL_H
52 # include <signal.h>
53 #endif
54 #ifdef XKB
55 # include <X11/XKBlib.h>
56 #endif
57
58 #ifdef USE_SM
59 #include <X11/ICE/ICElib.h>
60 #endif
61
62 typedef struct
63 {
64 gboolean ignored;
65 } ObEventData;
66
67 static void event_process(const XEvent *e, gpointer data);
68 static void event_client_dest(ObClient *client, gpointer data);
69 static void event_handle_root(XEvent *e);
70 static void event_handle_menu(XEvent *e);
71 static void event_handle_dock(ObDock *s, XEvent *e);
72 static void event_handle_dockapp(ObDockApp *app, XEvent *e);
73 static void event_handle_client(ObClient *c, XEvent *e);
74 static void event_handle_group(ObGroup *g, XEvent *e);
75
76 static gboolean focus_delay_func(gpointer data);
77 static void focus_delay_client_dest(ObClient *client, gpointer data);
78
79 static gboolean menu_hide_delay_func(gpointer data);
80
81 /* The time for the current event being processed */
82 Time event_curtime = CurrentTime;
83
84 /*! The value of the mask for the NumLock modifier */
85 guint NumLockMask;
86 /*! The value of the mask for the ScrollLock modifier */
87 guint ScrollLockMask;
88 /*! The key codes for the modifier keys */
89 static XModifierKeymap *modmap;
90 /*! Table of the constant modifier masks */
91 static const gint mask_table[] = {
92 ShiftMask, LockMask, ControlMask, Mod1Mask,
93 Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask
94 };
95 static gint mask_table_size;
96
97 static guint ignore_enter_focus = 0;
98
99 static gboolean menu_can_hide;
100
101 #ifdef USE_SM
102 static void ice_handler(gint fd, gpointer conn)
103 {
104 Bool b;
105 IceProcessMessages(conn, NULL, &b);
106 }
107
108 static void ice_watch(IceConn conn, IcePointer data, Bool opening,
109 IcePointer *watch_data)
110 {
111 static gint fd = -1;
112
113 if (opening) {
114 fd = IceConnectionNumber(conn);
115 ob_main_loop_fd_add(ob_main_loop, fd, ice_handler, conn, NULL);
116 } else {
117 ob_main_loop_fd_remove(ob_main_loop, fd);
118 fd = -1;
119 }
120 }
121 #endif
122
123 void event_startup(gboolean reconfig)
124 {
125 if (reconfig) return;
126
127 mask_table_size = sizeof(mask_table) / sizeof(mask_table[0]);
128
129 /* get lock masks that are defined by the display (not constant) */
130 modmap = XGetModifierMapping(ob_display);
131 g_assert(modmap);
132 if (modmap && modmap->max_keypermod > 0) {
133 size_t cnt;
134 const size_t size = mask_table_size * modmap->max_keypermod;
135 /* get the values of the keyboard lock modifiers
136 Note: Caps lock is not retrieved the same way as Scroll and Num
137 lock since it doesn't need to be. */
138 const KeyCode num_lock = XKeysymToKeycode(ob_display, XK_Num_Lock);
139 const KeyCode scroll_lock = XKeysymToKeycode(ob_display,
140 XK_Scroll_Lock);
141
142 for (cnt = 0; cnt < size; ++cnt) {
143 if (! modmap->modifiermap[cnt]) continue;
144
145 if (num_lock == modmap->modifiermap[cnt])
146 NumLockMask = mask_table[cnt / modmap->max_keypermod];
147 if (scroll_lock == modmap->modifiermap[cnt])
148 ScrollLockMask = mask_table[cnt / modmap->max_keypermod];
149 }
150 }
151
152 ob_main_loop_x_add(ob_main_loop, event_process, NULL, NULL);
153
154 #ifdef USE_SM
155 IceAddConnectionWatch(ice_watch, NULL);
156 #endif
157
158 client_add_destructor(focus_delay_client_dest, NULL);
159 client_add_destructor(event_client_dest, NULL);
160 }
161
162 void event_shutdown(gboolean reconfig)
163 {
164 if (reconfig) return;
165
166 #ifdef USE_SM
167 IceRemoveConnectionWatch(ice_watch, NULL);
168 #endif
169
170 client_remove_destructor(focus_delay_client_dest);
171 client_remove_destructor(event_client_dest);
172 XFreeModifiermap(modmap);
173 }
174
175 static Window event_get_window(XEvent *e)
176 {
177 Window window;
178
179 /* pick a window */
180 switch (e->type) {
181 case SelectionClear:
182 window = RootWindow(ob_display, ob_screen);
183 break;
184 case MapRequest:
185 window = e->xmap.window;
186 break;
187 case UnmapNotify:
188 window = e->xunmap.window;
189 break;
190 case DestroyNotify:
191 window = e->xdestroywindow.window;
192 break;
193 case ConfigureRequest:
194 window = e->xconfigurerequest.window;
195 break;
196 case ConfigureNotify:
197 window = e->xconfigure.window;
198 break;
199 default:
200 #ifdef XKB
201 if (extensions_xkb && e->type == extensions_xkb_event_basep) {
202 switch (((XkbAnyEvent*)e)->xkb_type) {
203 case XkbBellNotify:
204 window = ((XkbBellNotifyEvent*)e)->window;
205 default:
206 window = None;
207 }
208 } else
209 #endif
210 window = e->xany.window;
211 }
212 return window;
213 }
214
215 static void event_set_curtime(XEvent *e)
216 {
217 Time t = CurrentTime;
218
219 /* grab the lasttime and hack up the state */
220 switch (e->type) {
221 case ButtonPress:
222 case ButtonRelease:
223 t = e->xbutton.time;
224 break;
225 case KeyPress:
226 t = e->xkey.time;
227 break;
228 case KeyRelease:
229 t = e->xkey.time;
230 break;
231 case MotionNotify:
232 t = e->xmotion.time;
233 break;
234 case PropertyNotify:
235 t = e->xproperty.time;
236 break;
237 case EnterNotify:
238 case LeaveNotify:
239 t = e->xcrossing.time;
240 break;
241 default:
242 /* if more event types are anticipated, get their timestamp
243 explicitly */
244 break;
245 }
246
247 event_curtime = t;
248 }
249
250 #define STRIP_MODS(s) \
251 s &= ~(LockMask | NumLockMask | ScrollLockMask), \
252 /* kill off the Button1Mask etc, only want the modifiers */ \
253 s &= (ControlMask | ShiftMask | Mod1Mask | \
254 Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask) \
255
256 static void event_hack_mods(XEvent *e)
257 {
258 #ifdef XKB
259 XkbStateRec xkb_state;
260 #endif
261 KeyCode *kp;
262 gint i, k;
263
264 switch (e->type) {
265 case ButtonPress:
266 case ButtonRelease:
267 STRIP_MODS(e->xbutton.state);
268 break;
269 case KeyPress:
270 STRIP_MODS(e->xkey.state);
271 break;
272 case KeyRelease:
273 STRIP_MODS(e->xkey.state);
274 /* remove from the state the mask of the modifier being released, if
275 it is a modifier key being released (this is a little ugly..) */
276 #ifdef XKB
277 if (XkbGetState(ob_display, XkbUseCoreKbd, &xkb_state) == Success) {
278 e->xkey.state = xkb_state.compat_state;
279 break;
280 }
281 #endif
282 kp = modmap->modifiermap;
283 for (i = 0; i < mask_table_size; ++i) {
284 for (k = 0; k < modmap->max_keypermod; ++k) {
285 if (*kp == e->xkey.keycode) { /* found the keycode */
286 /* remove the mask for it */
287 e->xkey.state &= ~mask_table[i];
288 /* cause the first loop to break; */
289 i = mask_table_size;
290 break; /* get outta here! */
291 }
292 ++kp;
293 }
294 }
295 break;
296 case MotionNotify:
297 STRIP_MODS(e->xmotion.state);
298 /* compress events */
299 {
300 XEvent ce;
301 while (XCheckTypedWindowEvent(ob_display, e->xmotion.window,
302 e->type, &ce)) {
303 e->xmotion.x_root = ce.xmotion.x_root;
304 e->xmotion.y_root = ce.xmotion.y_root;
305 }
306 }
307 break;
308 }
309 }
310
311 static gboolean wanted_focusevent(XEvent *e)
312 {
313 gint mode = e->xfocus.mode;
314 gint detail = e->xfocus.detail;
315
316 if (e->type == FocusIn) {
317
318 /* These are ones we never want.. */
319
320 /* This means focus was given by a keyboard/mouse grab. */
321 if (mode == NotifyGrab)
322 return FALSE;
323 /* This means focus was given back from a keyboard/mouse grab. */
324 if (mode == NotifyUngrab)
325 return FALSE;
326
327 /* These are the ones we want.. */
328
329 /* This means focus moved from the root window to a client */
330 if (detail == NotifyVirtual)
331 return TRUE;
332 /* This means focus moved from one client to another */
333 if (detail == NotifyNonlinearVirtual)
334 return TRUE;
335
336 /* Otherwise.. */
337 return FALSE;
338 } else {
339 g_assert(e->type == FocusOut);
340
341
342 /* These are ones we never want.. */
343
344 /* This means focus was taken by a keyboard/mouse grab. */
345 if (mode == NotifyGrab)
346 return FALSE;
347
348 /* These are the ones we want.. */
349
350 /* This means focus moved from a client to the root window */
351 if (detail == NotifyVirtual)
352 return TRUE;
353 /* This means focus moved from one client to another */
354 if (detail == NotifyNonlinearVirtual)
355 return TRUE;
356
357 /* Otherwise.. */
358 return FALSE;
359 }
360 }
361
362 static Bool look_for_focusin(Display *d, XEvent *e, XPointer arg)
363 {
364 return e->type == FocusIn && wanted_focusevent(e);
365 }
366
367 static gboolean event_ignore(XEvent *e, ObClient *client)
368 {
369 switch(e->type) {
370 case EnterNotify:
371 case LeaveNotify:
372 if (e->xcrossing.detail == NotifyInferior)
373 return TRUE;
374 break;
375 case FocusIn:
376 case FocusOut:
377 /* I don't think this should ever happen with our event masks, but
378 if it does, we don't want it. */
379 if (client == NULL)
380 return TRUE;
381 if (!wanted_focusevent(e))
382 return TRUE;
383 break;
384 }
385 return FALSE;
386 }
387
388 static void event_process(const XEvent *ec, gpointer data)
389 {
390 Window window;
391 ObGroup *group = NULL;
392 ObClient *client = NULL;
393 ObDock *dock = NULL;
394 ObDockApp *dockapp = NULL;
395 ObWindow *obwin = NULL;
396 XEvent ee, *e;
397 ObEventData *ed = data;
398
399 /* make a copy we can mangle */
400 ee = *ec;
401 e = &ee;
402
403 window = event_get_window(e);
404 if (!(e->type == PropertyNotify &&
405 (group = g_hash_table_lookup(group_map, &window))))
406 if ((obwin = g_hash_table_lookup(window_map, &window))) {
407 switch (obwin->type) {
408 case Window_Dock:
409 dock = WINDOW_AS_DOCK(obwin);
410 break;
411 case Window_DockApp:
412 dockapp = WINDOW_AS_DOCKAPP(obwin);
413 break;
414 case Window_Client:
415 client = WINDOW_AS_CLIENT(obwin);
416 break;
417 case Window_Menu:
418 case Window_Internal:
419 /* not to be used for events */
420 g_assert_not_reached();
421 break;
422 }
423 }
424
425 #if 1 /* focus debugging stuff */
426 if (e->type == FocusIn || e->type == FocusOut) {
427 gint mode = e->xfocus.mode;
428 gint detail = e->xfocus.detail;
429 Window window = e->xfocus.window;
430 if (detail == NotifyVirtual) {
431 ob_debug("FOCUS %s NOTIFY VIRTUAL window 0x%x\n",
432 (e->type == FocusIn ? "IN" : "OUT"), window);
433 }
434
435 else if (detail == NotifyNonlinearVirtual) {
436 ob_debug("FOCUS %s NOTIFY NONLINVIRTUAL window 0x%x\n",
437 (e->type == FocusIn ? "IN" : "OUT"), window);
438 }
439
440 else
441 ob_debug("UNKNOWN FOCUS %s (d %d, m %d) window 0x%x\n",
442 (e->type == FocusIn ? "IN" : "OUT"),
443 detail, mode, window);
444 }
445 #endif
446
447 event_set_curtime(e);
448 event_hack_mods(e);
449 if (event_ignore(e, client)) {
450 if (ed)
451 ed->ignored = TRUE;
452 return;
453 } else if (ed)
454 ed->ignored = FALSE;
455
456 /* deal with it in the kernel */
457 if (group)
458 event_handle_group(group, e);
459 else if (client)
460 event_handle_client(client, e);
461 else if (dockapp)
462 event_handle_dockapp(dockapp, e);
463 else if (dock)
464 event_handle_dock(dock, e);
465 else if (window == RootWindow(ob_display, ob_screen))
466 event_handle_root(e);
467 else if (e->type == MapRequest)
468 client_manage(window);
469 else if (e->type == ConfigureRequest) {
470 /* unhandled configure requests must be used to configure the
471 window directly */
472 XWindowChanges xwc;
473
474 xwc.x = e->xconfigurerequest.x;
475 xwc.y = e->xconfigurerequest.y;
476 xwc.width = e->xconfigurerequest.width;
477 xwc.height = e->xconfigurerequest.height;
478 xwc.border_width = e->xconfigurerequest.border_width;
479 xwc.sibling = e->xconfigurerequest.above;
480 xwc.stack_mode = e->xconfigurerequest.detail;
481
482 /* we are not to be held responsible if someone sends us an
483 invalid request! */
484 xerror_set_ignore(TRUE);
485 XConfigureWindow(ob_display, window,
486 e->xconfigurerequest.value_mask, &xwc);
487 xerror_set_ignore(FALSE);
488 }
489
490 /* user input (action-bound) events */
491 if (e->type == ButtonPress || e->type == ButtonRelease ||
492 e->type == MotionNotify || e->type == KeyPress ||
493 e->type == KeyRelease)
494 {
495 if (menu_frame_visible)
496 event_handle_menu(e);
497 else {
498 if (!keyboard_process_interactive_grab(e, &client)) {
499 if (moveresize_in_progress) {
500 moveresize_event(e);
501
502 /* make further actions work on the client being
503 moved/resized */
504 client = moveresize_client;
505 }
506
507 menu_can_hide = FALSE;
508 ob_main_loop_timeout_add(ob_main_loop,
509 config_menu_hide_delay * 1000,
510 menu_hide_delay_func,
511 NULL, NULL);
512
513 if (e->type == ButtonPress || e->type == ButtonRelease ||
514 e->type == MotionNotify)
515 mouse_event(client, e);
516 else if (e->type == KeyPress) {
517 keyboard_event((focus_cycle_target ? focus_cycle_target :
518 (focus_hilite ? focus_hilite : client)),
519 e);
520 }
521 }
522 }
523 }
524 /* if something happens and it's not from an XEvent, then we don't know
525 the time */
526 event_curtime = CurrentTime;
527 }
528
529 static void event_handle_root(XEvent *e)
530 {
531 Atom msgtype;
532
533 switch(e->type) {
534 case SelectionClear:
535 ob_debug("Another WM has requested to replace us. Exiting.\n");
536 ob_exit_replace();
537 break;
538
539 case ClientMessage:
540 if (e->xclient.format != 32) break;
541
542 msgtype = e->xclient.message_type;
543 if (msgtype == prop_atoms.net_current_desktop) {
544 guint d = e->xclient.data.l[0];
545 event_curtime = e->xclient.data.l[1];
546 if (d < screen_num_desktops)
547 screen_set_desktop(d);
548 } else if (msgtype == prop_atoms.net_number_of_desktops) {
549 guint d = e->xclient.data.l[0];
550 if (d > 0)
551 screen_set_num_desktops(d);
552 } else if (msgtype == prop_atoms.net_showing_desktop) {
553 screen_show_desktop(e->xclient.data.l[0] != 0);
554 } else if (msgtype == prop_atoms.ob_control) {
555 if (e->xclient.data.l[0] == 1)
556 ob_reconfigure();
557 else if (e->xclient.data.l[0] == 2)
558 ob_restart();
559 }
560 break;
561 case PropertyNotify:
562 if (e->xproperty.atom == prop_atoms.net_desktop_names)
563 screen_update_desktop_names();
564 else if (e->xproperty.atom == prop_atoms.net_desktop_layout)
565 screen_update_layout();
566 break;
567 case ConfigureNotify:
568 #ifdef XRANDR
569 XRRUpdateConfiguration(e);
570 #endif
571 screen_resize();
572 break;
573 default:
574 ;
575 }
576 }
577
578 static void event_handle_group(ObGroup *group, XEvent *e)
579 {
580 GSList *it;
581
582 g_assert(e->type == PropertyNotify);
583
584 for (it = group->members; it; it = g_slist_next(it))
585 event_handle_client(it->data, e);
586 }
587
588 void event_enter_client(ObClient *client)
589 {
590 g_assert(config_focus_follow);
591
592 if (client_normal(client) && client_can_focus(client)) {
593 if (config_focus_delay) {
594 ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
595 ob_main_loop_timeout_add(ob_main_loop,
596 config_focus_delay,
597 focus_delay_func,
598 client, NULL);
599 } else
600 focus_delay_func(client);
601 }
602 }
603
604 static void event_handle_client(ObClient *client, XEvent *e)
605 {
606 XEvent ce;
607 Atom msgtype;
608 gint i=0;
609 ObFrameContext con;
610
611 switch (e->type) {
612 case VisibilityNotify:
613 client->frame->obscured = e->xvisibility.state != VisibilityUnobscured;
614 break;
615 case ButtonPress:
616 case ButtonRelease:
617 /* Wheel buttons don't draw because they are an instant click, so it
618 is a waste of resources to go drawing it. */
619 if (!(e->xbutton.button == 4 || e->xbutton.button == 5)) {
620 con = frame_context(client, e->xbutton.window);
621 con = mouse_button_frame_context(con, e->xbutton.button);
622 switch (con) {
623 case OB_FRAME_CONTEXT_MAXIMIZE:
624 client->frame->max_press = (e->type == ButtonPress);
625 framerender_frame(client->frame);
626 break;
627 case OB_FRAME_CONTEXT_CLOSE:
628 client->frame->close_press = (e->type == ButtonPress);
629 framerender_frame(client->frame);
630 break;
631 case OB_FRAME_CONTEXT_ICONIFY:
632 client->frame->iconify_press = (e->type == ButtonPress);
633 framerender_frame(client->frame);
634 break;
635 case OB_FRAME_CONTEXT_ALLDESKTOPS:
636 client->frame->desk_press = (e->type == ButtonPress);
637 framerender_frame(client->frame);
638 break;
639 case OB_FRAME_CONTEXT_SHADE:
640 client->frame->shade_press = (e->type == ButtonPress);
641 framerender_frame(client->frame);
642 break;
643 default:
644 /* nothing changes with clicks for any other contexts */
645 break;
646 }
647 }
648 break;
649 case FocusIn:
650 if (client != focus_client) {
651 focus_set_client(client);
652 frame_adjust_focus(client->frame, TRUE);
653 client_calc_layer(client);
654 }
655 break;
656 case FocusOut:
657 /* Look for the followup FocusIn */
658 if (!XCheckIfEvent(ob_display, &ce, look_for_focusin, NULL)) {
659 /* There is no FocusIn, this means focus went to a window that
660 is not being managed. most likely, this went to PointerRoot
661 or None, meaning the window is no longer around so fallback
662 focus, but not to that window */
663 ob_debug("Focus went to a black hole !\n");
664 focus_fallback(FALSE);
665 } else if (ce.xany.window == e->xany.window) {
666 /* If focus didn't actually move anywhere, there is nothing to do*/
667 break;
668 } else {
669 /* Focus did move, so process the FocusIn event */
670 ObEventData ed;
671 event_process(&ce, &ed);
672 if (ed.ignored) {
673 /* The FocusIn was ignored, this means it was on a window
674 that isn't a client. */
675 ob_debug("Focus went to an unmanaged window 0x%x !\n",
676 ce.xfocus.window);
677 focus_fallback(TRUE);
678 }
679 }
680
681 /* This client is no longer focused, so show that */
682 focus_hilite = NULL;
683 frame_adjust_focus(client->frame, FALSE);
684 client_calc_layer(client);
685 break;
686 case LeaveNotify:
687 con = frame_context(client, e->xcrossing.window);
688 switch (con) {
689 case OB_FRAME_CONTEXT_MAXIMIZE:
690 client->frame->max_hover = FALSE;
691 frame_adjust_state(client->frame);
692 break;
693 case OB_FRAME_CONTEXT_ALLDESKTOPS:
694 client->frame->desk_hover = FALSE;
695 frame_adjust_state(client->frame);
696 break;
697 case OB_FRAME_CONTEXT_SHADE:
698 client->frame->shade_hover = FALSE;
699 frame_adjust_state(client->frame);
700 break;
701 case OB_FRAME_CONTEXT_ICONIFY:
702 client->frame->iconify_hover = FALSE;
703 frame_adjust_state(client->frame);
704 break;
705 case OB_FRAME_CONTEXT_CLOSE:
706 client->frame->close_hover = FALSE;
707 frame_adjust_state(client->frame);
708 break;
709 case OB_FRAME_CONTEXT_FRAME:
710 if (config_focus_follow && config_focus_delay)
711 ob_main_loop_timeout_remove_data(ob_main_loop,
712 focus_delay_func,
713 client, TRUE);
714 break;
715 default:
716 break;
717 }
718 break;
719 case EnterNotify:
720 {
721 gboolean nofocus = FALSE;
722
723 if (ignore_enter_focus) {
724 ignore_enter_focus--;
725 nofocus = TRUE;
726 }
727
728 con = frame_context(client, e->xcrossing.window);
729 switch (con) {
730 case OB_FRAME_CONTEXT_MAXIMIZE:
731 client->frame->max_hover = TRUE;
732 frame_adjust_state(client->frame);
733 break;
734 case OB_FRAME_CONTEXT_ALLDESKTOPS:
735 client->frame->desk_hover = TRUE;
736 frame_adjust_state(client->frame);
737 break;
738 case OB_FRAME_CONTEXT_SHADE:
739 client->frame->shade_hover = TRUE;
740 frame_adjust_state(client->frame);
741 break;
742 case OB_FRAME_CONTEXT_ICONIFY:
743 client->frame->iconify_hover = TRUE;
744 frame_adjust_state(client->frame);
745 break;
746 case OB_FRAME_CONTEXT_CLOSE:
747 client->frame->close_hover = TRUE;
748 frame_adjust_state(client->frame);
749 break;
750 case OB_FRAME_CONTEXT_FRAME:
751 if (e->xcrossing.mode == NotifyGrab ||
752 e->xcrossing.mode == NotifyUngrab)
753 {
754 #ifdef DEBUG_FOCUS
755 ob_debug("%sNotify mode %d detail %d on %lx IGNORED\n",
756 (e->type == EnterNotify ? "Enter" : "Leave"),
757 e->xcrossing.mode,
758 e->xcrossing.detail, client?client->window:0);
759 #endif
760 } else {
761 #ifdef DEBUG_FOCUS
762 ob_debug("%sNotify mode %d detail %d on %lx, "
763 "focusing window: %d\n",
764 (e->type == EnterNotify ? "Enter" : "Leave"),
765 e->xcrossing.mode,
766 e->xcrossing.detail, (client?client->window:0),
767 !nofocus);
768 #endif
769 if (!nofocus && config_focus_follow)
770 event_enter_client(client);
771 }
772 break;
773 default:
774 break;
775 }
776 break;
777 }
778 case ConfigureRequest:
779 /* compress these */
780 while (XCheckTypedWindowEvent(ob_display, client->window,
781 ConfigureRequest, &ce)) {
782 ++i;
783 /* XXX if this causes bad things.. we can compress config req's
784 with the same mask. */
785 e->xconfigurerequest.value_mask |=
786 ce.xconfigurerequest.value_mask;
787 if (ce.xconfigurerequest.value_mask & CWX)
788 e->xconfigurerequest.x = ce.xconfigurerequest.x;
789 if (ce.xconfigurerequest.value_mask & CWY)
790 e->xconfigurerequest.y = ce.xconfigurerequest.y;
791 if (ce.xconfigurerequest.value_mask & CWWidth)
792 e->xconfigurerequest.width = ce.xconfigurerequest.width;
793 if (ce.xconfigurerequest.value_mask & CWHeight)
794 e->xconfigurerequest.height = ce.xconfigurerequest.height;
795 if (ce.xconfigurerequest.value_mask & CWBorderWidth)
796 e->xconfigurerequest.border_width =
797 ce.xconfigurerequest.border_width;
798 if (ce.xconfigurerequest.value_mask & CWStackMode)
799 e->xconfigurerequest.detail = ce.xconfigurerequest.detail;
800 }
801
802 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
803 if (client->iconic || client->shaded) return;
804
805 /* resize, then move, as specified in the EWMH section 7.7 */
806 if (e->xconfigurerequest.value_mask & (CWWidth | CWHeight |
807 CWX | CWY |
808 CWBorderWidth)) {
809 gint x, y, w, h;
810 ObCorner corner;
811
812 if (e->xconfigurerequest.value_mask & CWBorderWidth)
813 client->border_width = e->xconfigurerequest.border_width;
814
815 x = (e->xconfigurerequest.value_mask & CWX) ?
816 e->xconfigurerequest.x : client->area.x;
817 y = (e->xconfigurerequest.value_mask & CWY) ?
818 e->xconfigurerequest.y : client->area.y;
819 w = (e->xconfigurerequest.value_mask & CWWidth) ?
820 e->xconfigurerequest.width : client->area.width;
821 h = (e->xconfigurerequest.value_mask & CWHeight) ?
822 e->xconfigurerequest.height : client->area.height;
823
824 {
825 gint newx = x;
826 gint newy = y;
827 gint fw = w +
828 client->frame->size.left + client->frame->size.right;
829 gint fh = h +
830 client->frame->size.top + client->frame->size.bottom;
831 /* make this rude for size-only changes but not for position
832 changes.. */
833 gboolean moving = ((e->xconfigurerequest.value_mask & CWX) ||
834 (e->xconfigurerequest.value_mask & CWY));
835
836 client_find_onscreen(client, &newx, &newy, fw, fh,
837 !moving);
838 if (e->xconfigurerequest.value_mask & CWX)
839 x = newx;
840 if (e->xconfigurerequest.value_mask & CWY)
841 y = newy;
842 }
843
844 switch (client->gravity) {
845 case NorthEastGravity:
846 case EastGravity:
847 corner = OB_CORNER_TOPRIGHT;
848 break;
849 case SouthWestGravity:
850 case SouthGravity:
851 corner = OB_CORNER_BOTTOMLEFT;
852 break;
853 case SouthEastGravity:
854 corner = OB_CORNER_BOTTOMRIGHT;
855 break;
856 default: /* NorthWest, Static, etc */
857 corner = OB_CORNER_TOPLEFT;
858 }
859
860 client_configure_full(client, corner, x, y, w, h, FALSE, TRUE,
861 TRUE);
862 }
863
864 if (e->xconfigurerequest.value_mask & CWStackMode) {
865 switch (e->xconfigurerequest.detail) {
866 case Below:
867 case BottomIf:
868 /* Apps are so rude. And this is totally disconnected from
869 activation/focus. Bleh. */
870 /*client_lower(client);*/
871 break;
872
873 case Above:
874 case TopIf:
875 default:
876 /* Apps are so rude. And this is totally disconnected from
877 activation/focus. Bleh. */
878 /*client_raise(client);*/
879 break;
880 }
881 }
882 break;
883 case UnmapNotify:
884 ob_debug("UnmapNotify for window 0x%x\n", client->window);
885 if (client->ignore_unmaps) {
886 client->ignore_unmaps--;
887 break;
888 }
889 client_unmanage(client);
890 break;
891 case DestroyNotify:
892 ob_debug("DestroyNotify for window 0x%x\n", client->window);
893 client_unmanage(client);
894 break;
895 case ReparentNotify:
896 /* this is when the client is first taken captive in the frame */
897 if (e->xreparent.parent == client->frame->plate) break;
898
899 /*
900 This event is quite rare and is usually handled in unmapHandler.
901 However, if the window is unmapped when the reparent event occurs,
902 the window manager never sees it because an unmap event is not sent
903 to an already unmapped window.
904 */
905
906 /* we don't want the reparent event, put it back on the stack for the
907 X server to deal with after we unmanage the window */
908 XPutBackEvent(ob_display, e);
909
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 client_activate(client, FALSE,
981 (e->xclient.data.l[0] == 0 ||
982 e->xclient.data.l[0] == 2));
983 } else if (msgtype == prop_atoms.net_wm_moveresize) {
984 ob_debug("net_wm_moveresize for 0x%lx direction %d\n",
985 client->window, e->xclient.data.l[2]);
986 if ((Atom)e->xclient.data.l[2] ==
987 prop_atoms.net_wm_moveresize_size_topleft ||
988 (Atom)e->xclient.data.l[2] ==
989 prop_atoms.net_wm_moveresize_size_top ||
990 (Atom)e->xclient.data.l[2] ==
991 prop_atoms.net_wm_moveresize_size_topright ||
992 (Atom)e->xclient.data.l[2] ==
993 prop_atoms.net_wm_moveresize_size_right ||
994 (Atom)e->xclient.data.l[2] ==
995 prop_atoms.net_wm_moveresize_size_right ||
996 (Atom)e->xclient.data.l[2] ==
997 prop_atoms.net_wm_moveresize_size_bottomright ||
998 (Atom)e->xclient.data.l[2] ==
999 prop_atoms.net_wm_moveresize_size_bottom ||
1000 (Atom)e->xclient.data.l[2] ==
1001 prop_atoms.net_wm_moveresize_size_bottomleft ||
1002 (Atom)e->xclient.data.l[2] ==
1003 prop_atoms.net_wm_moveresize_size_left ||
1004 (Atom)e->xclient.data.l[2] ==
1005 prop_atoms.net_wm_moveresize_move ||
1006 (Atom)e->xclient.data.l[2] ==
1007 prop_atoms.net_wm_moveresize_size_keyboard ||
1008 (Atom)e->xclient.data.l[2] ==
1009 prop_atoms.net_wm_moveresize_move_keyboard) {
1010
1011 moveresize_start(client, e->xclient.data.l[0],
1012 e->xclient.data.l[1], e->xclient.data.l[3],
1013 e->xclient.data.l[2]);
1014 }
1015 else if ((Atom)e->xclient.data.l[2] ==
1016 prop_atoms.net_wm_moveresize_cancel)
1017 moveresize_end(TRUE);
1018 } else if (msgtype == prop_atoms.net_moveresize_window) {
1019 gint oldg = client->gravity;
1020 gint tmpg, x, y, w, h;
1021
1022 if (e->xclient.data.l[0] & 0xff)
1023 tmpg = e->xclient.data.l[0] & 0xff;
1024 else
1025 tmpg = oldg;
1026
1027 if (e->xclient.data.l[0] & 1 << 8)
1028 x = e->xclient.data.l[1];
1029 else
1030 x = client->area.x;
1031 if (e->xclient.data.l[0] & 1 << 9)
1032 y = e->xclient.data.l[2];
1033 else
1034 y = client->area.y;
1035 if (e->xclient.data.l[0] & 1 << 10)
1036 w = e->xclient.data.l[3];
1037 else
1038 w = client->area.width;
1039 if (e->xclient.data.l[0] & 1 << 11)
1040 h = e->xclient.data.l[4];
1041 else
1042 h = client->area.height;
1043 client->gravity = tmpg;
1044
1045 {
1046 gint newx = x;
1047 gint newy = y;
1048 gint fw = w +
1049 client->frame->size.left + client->frame->size.right;
1050 gint fh = h +
1051 client->frame->size.top + client->frame->size.bottom;
1052 client_find_onscreen(client, &newx, &newy, fw, fh,
1053 client_normal(client));
1054 if (e->xclient.data.l[0] & 1 << 8)
1055 x = newx;
1056 if (e->xclient.data.l[0] & 1 << 9)
1057 y = newy;
1058 }
1059
1060 client_configure(client, OB_CORNER_TOPLEFT,
1061 x, y, w, h, FALSE, TRUE);
1062
1063 client->gravity = oldg;
1064 }
1065 break;
1066 case PropertyNotify:
1067 /* validate cuz we query stuff off the client here */
1068 if (!client_validate(client)) break;
1069
1070 /* compress changes to a single property into a single change */
1071 while (XCheckTypedWindowEvent(ob_display, client->window,
1072 e->type, &ce)) {
1073 Atom a, b;
1074
1075 /* XXX: it would be nice to compress ALL changes to a property,
1076 not just changes in a row without other props between. */
1077
1078 a = ce.xproperty.atom;
1079 b = e->xproperty.atom;
1080
1081 if (a == b)
1082 continue;
1083 if ((a == prop_atoms.net_wm_name ||
1084 a == prop_atoms.wm_name ||
1085 a == prop_atoms.net_wm_icon_name ||
1086 a == prop_atoms.wm_icon_name)
1087 &&
1088 (b == prop_atoms.net_wm_name ||
1089 b == prop_atoms.wm_name ||
1090 b == prop_atoms.net_wm_icon_name ||
1091 b == prop_atoms.wm_icon_name)) {
1092 continue;
1093 }
1094 if (a == prop_atoms.net_wm_icon &&
1095 b == prop_atoms.net_wm_icon)
1096 continue;
1097
1098 XPutBackEvent(ob_display, &ce);
1099 break;
1100 }
1101
1102 msgtype = e->xproperty.atom;
1103 if (msgtype == XA_WM_NORMAL_HINTS) {
1104 client_update_normal_hints(client);
1105 /* normal hints can make a window non-resizable */
1106 client_setup_decor_and_functions(client);
1107 } else if (msgtype == XA_WM_HINTS) {
1108 client_update_wmhints(client);
1109 } else if (msgtype == XA_WM_TRANSIENT_FOR) {
1110 client_update_transient_for(client);
1111 client_get_type(client);
1112 /* type may have changed, so update the layer */
1113 client_calc_layer(client);
1114 client_setup_decor_and_functions(client);
1115 } else if (msgtype == prop_atoms.net_wm_name ||
1116 msgtype == prop_atoms.wm_name ||
1117 msgtype == prop_atoms.net_wm_icon_name ||
1118 msgtype == prop_atoms.wm_icon_name) {
1119 client_update_title(client);
1120 } else if (msgtype == prop_atoms.wm_class) {
1121 client_update_class(client);
1122 } else if (msgtype == prop_atoms.wm_protocols) {
1123 client_update_protocols(client);
1124 client_setup_decor_and_functions(client);
1125 }
1126 else if (msgtype == prop_atoms.net_wm_strut) {
1127 client_update_strut(client);
1128 }
1129 else if (msgtype == prop_atoms.net_wm_icon) {
1130 client_update_icons(client);
1131 }
1132 else if (msgtype == prop_atoms.net_wm_user_time) {
1133 client_update_user_time(client, TRUE);
1134 }
1135 else if (msgtype == prop_atoms.sm_client_id) {
1136 client_update_sm_client_id(client);
1137 }
1138 default:
1139 ;
1140 #ifdef SHAPE
1141 if (extensions_shape && e->type == extensions_shape_event_basep) {
1142 client->shaped = ((XShapeEvent*)e)->shaped;
1143 frame_adjust_shape(client->frame);
1144 }
1145 #endif
1146 }
1147 }
1148
1149 static void event_handle_dock(ObDock *s, XEvent *e)
1150 {
1151 switch (e->type) {
1152 case ButtonPress:
1153 if (e->xbutton.button == 1)
1154 stacking_raise(DOCK_AS_WINDOW(s));
1155 else if (e->xbutton.button == 2)
1156 stacking_lower(DOCK_AS_WINDOW(s));
1157 break;
1158 case EnterNotify:
1159 dock_hide(FALSE);
1160 break;
1161 case LeaveNotify:
1162 dock_hide(TRUE);
1163 break;
1164 }
1165 }
1166
1167 static void event_handle_dockapp(ObDockApp *app, XEvent *e)
1168 {
1169 switch (e->type) {
1170 case MotionNotify:
1171 dock_app_drag(app, &e->xmotion);
1172 break;
1173 case UnmapNotify:
1174 if (app->ignore_unmaps) {
1175 app->ignore_unmaps--;
1176 break;
1177 }
1178 dock_remove(app, TRUE);
1179 break;
1180 case DestroyNotify:
1181 dock_remove(app, FALSE);
1182 break;
1183 case ReparentNotify:
1184 dock_remove(app, FALSE);
1185 break;
1186 case ConfigureNotify:
1187 dock_app_configure(app, e->xconfigure.width, e->xconfigure.height);
1188 break;
1189 }
1190 }
1191
1192 ObMenuFrame* find_active_menu()
1193 {
1194 GList *it;
1195 ObMenuFrame *ret = NULL;
1196
1197 for (it = menu_frame_visible; it; it = g_list_next(it)) {
1198 ret = it->data;
1199 if (ret->selected)
1200 break;
1201 ret = NULL;
1202 }
1203 return ret;
1204 }
1205
1206 ObMenuFrame* find_active_or_last_menu()
1207 {
1208 ObMenuFrame *ret = NULL;
1209
1210 ret = find_active_menu();
1211 if (!ret && menu_frame_visible)
1212 ret = menu_frame_visible->data;
1213 return ret;
1214 }
1215
1216 static void event_handle_menu(XEvent *ev)
1217 {
1218 ObMenuFrame *f;
1219 ObMenuEntryFrame *e;
1220
1221 switch (ev->type) {
1222 case ButtonRelease:
1223 if (menu_can_hide) {
1224 if ((e = menu_entry_frame_under(ev->xbutton.x_root,
1225 ev->xbutton.y_root)))
1226 menu_entry_frame_execute(e, ev->xbutton.state,
1227 ev->xbutton.time);
1228 else
1229 menu_frame_hide_all();
1230 }
1231 break;
1232 case MotionNotify:
1233 if ((f = menu_frame_under(ev->xmotion.x_root,
1234 ev->xmotion.y_root))) {
1235 menu_frame_move_on_screen(f);
1236 if ((e = menu_entry_frame_under(ev->xmotion.x_root,
1237 ev->xmotion.y_root)))
1238 menu_frame_select(f, e);
1239 }
1240 {
1241 ObMenuFrame *a;
1242
1243 a = find_active_menu();
1244 if (a && a != f &&
1245 a->selected->entry->type != OB_MENU_ENTRY_TYPE_SUBMENU)
1246 {
1247 menu_frame_select(a, NULL);
1248 }
1249 }
1250 break;
1251 case KeyPress:
1252 if (ev->xkey.keycode == ob_keycode(OB_KEY_ESCAPE))
1253 menu_frame_hide_all();
1254 else if (ev->xkey.keycode == ob_keycode(OB_KEY_RETURN)) {
1255 ObMenuFrame *f;
1256 if ((f = find_active_menu()))
1257 menu_entry_frame_execute(f->selected, ev->xkey.state,
1258 ev->xkey.time);
1259 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_LEFT)) {
1260 ObMenuFrame *f;
1261 if ((f = find_active_or_last_menu()) && f->parent)
1262 menu_frame_select(f, NULL);
1263 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_RIGHT)) {
1264 ObMenuFrame *f;
1265 if ((f = find_active_or_last_menu()) && f->child)
1266 menu_frame_select_next(f->child);
1267 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_UP)) {
1268 ObMenuFrame *f;
1269 if ((f = find_active_or_last_menu()))
1270 menu_frame_select_previous(f);
1271 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_DOWN)) {
1272 ObMenuFrame *f;
1273 if ((f = find_active_or_last_menu()))
1274 menu_frame_select_next(f);
1275 }
1276 break;
1277 }
1278 }
1279
1280 static gboolean menu_hide_delay_func(gpointer data)
1281 {
1282 menu_can_hide = TRUE;
1283 return FALSE; /* no repeat */
1284 }
1285
1286 static gboolean focus_delay_func(gpointer data)
1287 {
1288 ObClient *c = data;
1289
1290 if (focus_client != c) {
1291 if (client_validate(c)) {
1292 client_focus(c);
1293 if (config_focus_raise)
1294 client_raise(c);
1295 }
1296 }
1297 return FALSE; /* no repeat */
1298 }
1299
1300 static void focus_delay_client_dest(ObClient *client, gpointer data)
1301 {
1302 ob_main_loop_timeout_remove_data(ob_main_loop, focus_delay_func,
1303 client, TRUE);
1304 }
1305
1306 static void event_client_dest(ObClient *client, gpointer data)
1307 {
1308 if (client == focus_hilite)
1309 focus_hilite = NULL;
1310 }
1311
1312 void event_halt_focus_delay()
1313 {
1314 ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
1315 }
1316
1317 void event_ignore_queued_enters()
1318 {
1319 GSList *saved = NULL, *it;
1320 XEvent *e;
1321
1322 XSync(ob_display, FALSE);
1323
1324 /* count the events */
1325 while (TRUE) {
1326 e = g_new(XEvent, 1);
1327 if (XCheckTypedEvent(ob_display, EnterNotify, e)) {
1328 ObWindow *win;
1329
1330 win = g_hash_table_lookup(window_map, &e->xany.window);
1331 if (win && WINDOW_IS_CLIENT(win))
1332 ++ignore_enter_focus;
1333
1334 saved = g_slist_append(saved, e);
1335 } else {
1336 g_free(e);
1337 break;
1338 }
1339 }
1340 /* put the events back */
1341 for (it = saved; it; it = g_slist_next(it)) {
1342 XPutBackEvent(ob_display, it->data);
1343 g_free(it->data);
1344 }
1345 g_slist_free(saved);
1346 }
This page took 0.092857 seconds and 4 git commands to generate.