]> Dogcows Code - chaz/openbox/blob - openbox/event.c
d4d921efd229cb34d94af6b23cfcaaf9db8a4513
[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 if (d < screen_num_desktops)
546 screen_set_desktop(d);
547 } else if (msgtype == prop_atoms.net_number_of_desktops) {
548 guint d = e->xclient.data.l[0];
549 if (d > 0)
550 screen_set_num_desktops(d);
551 } else if (msgtype == prop_atoms.net_showing_desktop) {
552 screen_show_desktop(e->xclient.data.l[0] != 0);
553 } else if (msgtype == prop_atoms.ob_control) {
554 if (e->xclient.data.l[0] == 1)
555 ob_reconfigure();
556 else if (e->xclient.data.l[0] == 2)
557 ob_restart();
558 }
559 break;
560 case PropertyNotify:
561 if (e->xproperty.atom == prop_atoms.net_desktop_names)
562 screen_update_desktop_names();
563 else if (e->xproperty.atom == prop_atoms.net_desktop_layout)
564 screen_update_layout();
565 break;
566 case ConfigureNotify:
567 #ifdef XRANDR
568 XRRUpdateConfiguration(e);
569 #endif
570 screen_resize();
571 break;
572 default:
573 ;
574 }
575 }
576
577 static void event_handle_group(ObGroup *group, XEvent *e)
578 {
579 GSList *it;
580
581 g_assert(e->type == PropertyNotify);
582
583 for (it = group->members; it; it = g_slist_next(it))
584 event_handle_client(it->data, e);
585 }
586
587 void event_enter_client(ObClient *client)
588 {
589 g_assert(config_focus_follow);
590
591 if (client_normal(client) && client_can_focus(client)) {
592 if (config_focus_delay) {
593 ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
594 ob_main_loop_timeout_add(ob_main_loop,
595 config_focus_delay,
596 focus_delay_func,
597 client, NULL);
598 } else
599 focus_delay_func(client);
600 }
601 }
602
603 static void event_handle_client(ObClient *client, XEvent *e)
604 {
605 XEvent ce;
606 Atom msgtype;
607 gint i=0;
608 ObFrameContext con;
609
610 switch (e->type) {
611 case VisibilityNotify:
612 client->frame->obscured = e->xvisibility.state != VisibilityUnobscured;
613 break;
614 case ButtonPress:
615 case ButtonRelease:
616 /* Wheel buttons don't draw because they are an instant click, so it
617 is a waste of resources to go drawing it. */
618 if (!(e->xbutton.button == 4 || e->xbutton.button == 5)) {
619 con = frame_context(client, e->xbutton.window);
620 con = mouse_button_frame_context(con, e->xbutton.button);
621 switch (con) {
622 case OB_FRAME_CONTEXT_MAXIMIZE:
623 client->frame->max_press = (e->type == ButtonPress);
624 framerender_frame(client->frame);
625 break;
626 case OB_FRAME_CONTEXT_CLOSE:
627 client->frame->close_press = (e->type == ButtonPress);
628 framerender_frame(client->frame);
629 break;
630 case OB_FRAME_CONTEXT_ICONIFY:
631 client->frame->iconify_press = (e->type == ButtonPress);
632 framerender_frame(client->frame);
633 break;
634 case OB_FRAME_CONTEXT_ALLDESKTOPS:
635 client->frame->desk_press = (e->type == ButtonPress);
636 framerender_frame(client->frame);
637 break;
638 case OB_FRAME_CONTEXT_SHADE:
639 client->frame->shade_press = (e->type == ButtonPress);
640 framerender_frame(client->frame);
641 break;
642 default:
643 /* nothing changes with clicks for any other contexts */
644 break;
645 }
646 }
647 break;
648 case FocusIn:
649 if (client != focus_client) {
650 focus_set_client(client);
651 frame_adjust_focus(client->frame, TRUE);
652 client_calc_layer(client);
653 }
654 break;
655 case FocusOut:
656 /* Look for the followup FocusIn */
657 if (!XCheckIfEvent(ob_display, &ce, look_for_focusin, NULL)) {
658 /* There is no FocusIn, this means focus went to a window that
659 is not being managed. most likely, this went to PointerRoot
660 or None, meaning the window is no longer around so fallback
661 focus, but not to that window */
662 ob_debug("Focus went to a black hole !\n");
663 focus_fallback(FALSE);
664 } else if (ce.xany.window == e->xany.window) {
665 /* If focus didn't actually move anywhere, there is nothing to do*/
666 break;
667 } else {
668 /* Focus did move, so process the FocusIn event */
669 ObEventData ed;
670 event_process(&ce, &ed);
671 if (ed.ignored) {
672 /* The FocusIn was ignored, this means it was on a window
673 that isn't a client. */
674 ob_debug("Focus went to an unmanaged window 0x%x !\n",
675 ce.xfocus.window);
676 focus_fallback(TRUE);
677 }
678 }
679
680 /* This client is no longer focused, so show that */
681 focus_hilite = NULL;
682 frame_adjust_focus(client->frame, FALSE);
683 client_calc_layer(client);
684 break;
685 case LeaveNotify:
686 con = frame_context(client, e->xcrossing.window);
687 switch (con) {
688 case OB_FRAME_CONTEXT_MAXIMIZE:
689 client->frame->max_hover = FALSE;
690 frame_adjust_state(client->frame);
691 break;
692 case OB_FRAME_CONTEXT_ALLDESKTOPS:
693 client->frame->desk_hover = FALSE;
694 frame_adjust_state(client->frame);
695 break;
696 case OB_FRAME_CONTEXT_SHADE:
697 client->frame->shade_hover = FALSE;
698 frame_adjust_state(client->frame);
699 break;
700 case OB_FRAME_CONTEXT_ICONIFY:
701 client->frame->iconify_hover = FALSE;
702 frame_adjust_state(client->frame);
703 break;
704 case OB_FRAME_CONTEXT_CLOSE:
705 client->frame->close_hover = FALSE;
706 frame_adjust_state(client->frame);
707 break;
708 case OB_FRAME_CONTEXT_FRAME:
709 if (config_focus_follow && config_focus_delay)
710 ob_main_loop_timeout_remove_data(ob_main_loop,
711 focus_delay_func,
712 client, TRUE);
713 break;
714 default:
715 break;
716 }
717 break;
718 case EnterNotify:
719 {
720 gboolean nofocus = FALSE;
721
722 if (ignore_enter_focus) {
723 ignore_enter_focus--;
724 nofocus = TRUE;
725 }
726
727 con = frame_context(client, e->xcrossing.window);
728 switch (con) {
729 case OB_FRAME_CONTEXT_MAXIMIZE:
730 client->frame->max_hover = TRUE;
731 frame_adjust_state(client->frame);
732 break;
733 case OB_FRAME_CONTEXT_ALLDESKTOPS:
734 client->frame->desk_hover = TRUE;
735 frame_adjust_state(client->frame);
736 break;
737 case OB_FRAME_CONTEXT_SHADE:
738 client->frame->shade_hover = TRUE;
739 frame_adjust_state(client->frame);
740 break;
741 case OB_FRAME_CONTEXT_ICONIFY:
742 client->frame->iconify_hover = TRUE;
743 frame_adjust_state(client->frame);
744 break;
745 case OB_FRAME_CONTEXT_CLOSE:
746 client->frame->close_hover = TRUE;
747 frame_adjust_state(client->frame);
748 break;
749 case OB_FRAME_CONTEXT_FRAME:
750 if (e->xcrossing.mode == NotifyGrab ||
751 e->xcrossing.mode == NotifyUngrab)
752 {
753 #ifdef DEBUG_FOCUS
754 ob_debug("%sNotify mode %d detail %d on %lx IGNORED\n",
755 (e->type == EnterNotify ? "Enter" : "Leave"),
756 e->xcrossing.mode,
757 e->xcrossing.detail, client?client->window:0);
758 #endif
759 } else {
760 #ifdef DEBUG_FOCUS
761 ob_debug("%sNotify mode %d detail %d on %lx, "
762 "focusing window: %d\n",
763 (e->type == EnterNotify ? "Enter" : "Leave"),
764 e->xcrossing.mode,
765 e->xcrossing.detail, (client?client->window:0),
766 !nofocus);
767 #endif
768 if (!nofocus && config_focus_follow)
769 event_enter_client(client);
770 }
771 break;
772 default:
773 break;
774 }
775 break;
776 }
777 case ConfigureRequest:
778 /* compress these */
779 while (XCheckTypedWindowEvent(ob_display, client->window,
780 ConfigureRequest, &ce)) {
781 ++i;
782 /* XXX if this causes bad things.. we can compress config req's
783 with the same mask. */
784 e->xconfigurerequest.value_mask |=
785 ce.xconfigurerequest.value_mask;
786 if (ce.xconfigurerequest.value_mask & CWX)
787 e->xconfigurerequest.x = ce.xconfigurerequest.x;
788 if (ce.xconfigurerequest.value_mask & CWY)
789 e->xconfigurerequest.y = ce.xconfigurerequest.y;
790 if (ce.xconfigurerequest.value_mask & CWWidth)
791 e->xconfigurerequest.width = ce.xconfigurerequest.width;
792 if (ce.xconfigurerequest.value_mask & CWHeight)
793 e->xconfigurerequest.height = ce.xconfigurerequest.height;
794 if (ce.xconfigurerequest.value_mask & CWBorderWidth)
795 e->xconfigurerequest.border_width =
796 ce.xconfigurerequest.border_width;
797 if (ce.xconfigurerequest.value_mask & CWStackMode)
798 e->xconfigurerequest.detail = ce.xconfigurerequest.detail;
799 }
800
801 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
802 if (client->iconic || client->shaded) return;
803
804 /* resize, then move, as specified in the EWMH section 7.7 */
805 if (e->xconfigurerequest.value_mask & (CWWidth | CWHeight |
806 CWX | CWY |
807 CWBorderWidth)) {
808 gint x, y, w, h;
809 ObCorner corner;
810
811 if (e->xconfigurerequest.value_mask & CWBorderWidth)
812 client->border_width = e->xconfigurerequest.border_width;
813
814 x = (e->xconfigurerequest.value_mask & CWX) ?
815 e->xconfigurerequest.x : client->area.x;
816 y = (e->xconfigurerequest.value_mask & CWY) ?
817 e->xconfigurerequest.y : client->area.y;
818 w = (e->xconfigurerequest.value_mask & CWWidth) ?
819 e->xconfigurerequest.width : client->area.width;
820 h = (e->xconfigurerequest.value_mask & CWHeight) ?
821 e->xconfigurerequest.height : client->area.height;
822
823 {
824 gint newx = x;
825 gint newy = y;
826 gint fw = w +
827 client->frame->size.left + client->frame->size.right;
828 gint fh = h +
829 client->frame->size.top + client->frame->size.bottom;
830 /* make this rude for size-only changes but not for position
831 changes.. */
832 gboolean moving = ((e->xconfigurerequest.value_mask & CWX) ||
833 (e->xconfigurerequest.value_mask & CWY));
834
835 client_find_onscreen(client, &newx, &newy, fw, fh,
836 !moving);
837 if (e->xconfigurerequest.value_mask & CWX)
838 x = newx;
839 if (e->xconfigurerequest.value_mask & CWY)
840 y = newy;
841 }
842
843 switch (client->gravity) {
844 case NorthEastGravity:
845 case EastGravity:
846 corner = OB_CORNER_TOPRIGHT;
847 break;
848 case SouthWestGravity:
849 case SouthGravity:
850 corner = OB_CORNER_BOTTOMLEFT;
851 break;
852 case SouthEastGravity:
853 corner = OB_CORNER_BOTTOMRIGHT;
854 break;
855 default: /* NorthWest, Static, etc */
856 corner = OB_CORNER_TOPLEFT;
857 }
858
859 client_configure_full(client, corner, x, y, w, h, FALSE, TRUE,
860 TRUE);
861 }
862
863 if (e->xconfigurerequest.value_mask & CWStackMode) {
864 switch (e->xconfigurerequest.detail) {
865 case Below:
866 case BottomIf:
867 /* Apps are so rude. And this is totally disconnected from
868 activation/focus. Bleh. */
869 /*client_lower(client);*/
870 break;
871
872 case Above:
873 case TopIf:
874 default:
875 /* Apps are so rude. And this is totally disconnected from
876 activation/focus. Bleh. */
877 /*client_raise(client);*/
878 break;
879 }
880 }
881 break;
882 case UnmapNotify:
883 if (client->ignore_unmaps) {
884 client->ignore_unmaps--;
885 break;
886 }
887 client_unmanage(client);
888 break;
889 case DestroyNotify:
890 client_unmanage(client);
891 break;
892 case ReparentNotify:
893 /* this is when the client is first taken captive in the frame */
894 if (e->xreparent.parent == client->frame->plate) break;
895
896 /*
897 This event is quite rare and is usually handled in unmapHandler.
898 However, if the window is unmapped when the reparent event occurs,
899 the window manager never sees it because an unmap event is not sent
900 to an already unmapped window.
901 */
902
903 /* we don't want the reparent event, put it back on the stack for the
904 X server to deal with after we unmanage the window */
905 XPutBackEvent(ob_display, e);
906
907 client_unmanage(client);
908 break;
909 case MapRequest:
910 ob_debug("MapRequest for 0x%lx\n", client->window);
911 if (!client->iconic) break; /* this normally doesn't happen, but if it
912 does, we don't want it!
913 it can happen now when the window is on
914 another desktop, but we still don't
915 want it! */
916 client_activate(client, FALSE, TRUE, CurrentTime);
917 break;
918 case ClientMessage:
919 /* validate cuz we query stuff off the client here */
920 if (!client_validate(client)) break;
921
922 if (e->xclient.format != 32) return;
923
924 msgtype = e->xclient.message_type;
925 if (msgtype == prop_atoms.wm_change_state) {
926 /* compress changes into a single change */
927 while (XCheckTypedWindowEvent(ob_display, client->window,
928 e->type, &ce)) {
929 /* XXX: it would be nice to compress ALL messages of a
930 type, not just messages in a row without other
931 message types between. */
932 if (ce.xclient.message_type != msgtype) {
933 XPutBackEvent(ob_display, &ce);
934 break;
935 }
936 e->xclient = ce.xclient;
937 }
938 client_set_wm_state(client, e->xclient.data.l[0]);
939 } else if (msgtype == prop_atoms.net_wm_desktop) {
940 /* compress changes into a single change */
941 while (XCheckTypedWindowEvent(ob_display, client->window,
942 e->type, &ce)) {
943 /* XXX: it would be nice to compress ALL messages of a
944 type, not just messages in a row without other
945 message types between. */
946 if (ce.xclient.message_type != msgtype) {
947 XPutBackEvent(ob_display, &ce);
948 break;
949 }
950 e->xclient = ce.xclient;
951 }
952 if ((unsigned)e->xclient.data.l[0] < screen_num_desktops ||
953 (unsigned)e->xclient.data.l[0] == DESKTOP_ALL)
954 client_set_desktop(client, (unsigned)e->xclient.data.l[0],
955 FALSE);
956 } else if (msgtype == prop_atoms.net_wm_state) {
957 /* can't compress these */
958 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
959 (e->xclient.data.l[0] == 0 ? "Remove" :
960 e->xclient.data.l[0] == 1 ? "Add" :
961 e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
962 e->xclient.data.l[1], e->xclient.data.l[2],
963 client->window);
964 client_set_state(client, e->xclient.data.l[0],
965 e->xclient.data.l[1], e->xclient.data.l[2]);
966 } else if (msgtype == prop_atoms.net_close_window) {
967 ob_debug("net_close_window for 0x%lx\n", client->window);
968 client_close(client);
969 } else if (msgtype == prop_atoms.net_active_window) {
970 ob_debug("net_active_window for 0x%lx source=%s\n",
971 client->window,
972 (e->xclient.data.l[0] == 0 ? "unknown" :
973 (e->xclient.data.l[0] == 1 ? "application" :
974 (e->xclient.data.l[0] == 2 ? "user" : "INVALID"))));
975 /* XXX make use of data.l[1] and [2] ! */
976 client_activate(client, FALSE,
977 (e->xclient.data.l[0] == 0 ||
978 e->xclient.data.l[0] == 2),
979 e->xclient.data.l[1]);
980 } else if (msgtype == prop_atoms.net_wm_moveresize) {
981 ob_debug("net_wm_moveresize for 0x%lx direction %d\n",
982 client->window, e->xclient.data.l[2]);
983 if ((Atom)e->xclient.data.l[2] ==
984 prop_atoms.net_wm_moveresize_size_topleft ||
985 (Atom)e->xclient.data.l[2] ==
986 prop_atoms.net_wm_moveresize_size_top ||
987 (Atom)e->xclient.data.l[2] ==
988 prop_atoms.net_wm_moveresize_size_topright ||
989 (Atom)e->xclient.data.l[2] ==
990 prop_atoms.net_wm_moveresize_size_right ||
991 (Atom)e->xclient.data.l[2] ==
992 prop_atoms.net_wm_moveresize_size_right ||
993 (Atom)e->xclient.data.l[2] ==
994 prop_atoms.net_wm_moveresize_size_bottomright ||
995 (Atom)e->xclient.data.l[2] ==
996 prop_atoms.net_wm_moveresize_size_bottom ||
997 (Atom)e->xclient.data.l[2] ==
998 prop_atoms.net_wm_moveresize_size_bottomleft ||
999 (Atom)e->xclient.data.l[2] ==
1000 prop_atoms.net_wm_moveresize_size_left ||
1001 (Atom)e->xclient.data.l[2] ==
1002 prop_atoms.net_wm_moveresize_move ||
1003 (Atom)e->xclient.data.l[2] ==
1004 prop_atoms.net_wm_moveresize_size_keyboard ||
1005 (Atom)e->xclient.data.l[2] ==
1006 prop_atoms.net_wm_moveresize_move_keyboard) {
1007
1008 moveresize_start(client, e->xclient.data.l[0],
1009 e->xclient.data.l[1], e->xclient.data.l[3],
1010 e->xclient.data.l[2]);
1011 }
1012 else if ((Atom)e->xclient.data.l[2] ==
1013 prop_atoms.net_wm_moveresize_cancel)
1014 moveresize_end(TRUE);
1015 } else if (msgtype == prop_atoms.net_moveresize_window) {
1016 gint oldg = client->gravity;
1017 gint tmpg, x, y, w, h;
1018
1019 if (e->xclient.data.l[0] & 0xff)
1020 tmpg = e->xclient.data.l[0] & 0xff;
1021 else
1022 tmpg = oldg;
1023
1024 if (e->xclient.data.l[0] & 1 << 8)
1025 x = e->xclient.data.l[1];
1026 else
1027 x = client->area.x;
1028 if (e->xclient.data.l[0] & 1 << 9)
1029 y = e->xclient.data.l[2];
1030 else
1031 y = client->area.y;
1032 if (e->xclient.data.l[0] & 1 << 10)
1033 w = e->xclient.data.l[3];
1034 else
1035 w = client->area.width;
1036 if (e->xclient.data.l[0] & 1 << 11)
1037 h = e->xclient.data.l[4];
1038 else
1039 h = client->area.height;
1040 client->gravity = tmpg;
1041
1042 {
1043 gint newx = x;
1044 gint newy = y;
1045 gint fw = w +
1046 client->frame->size.left + client->frame->size.right;
1047 gint fh = h +
1048 client->frame->size.top + client->frame->size.bottom;
1049 client_find_onscreen(client, &newx, &newy, fw, fh,
1050 client_normal(client));
1051 if (e->xclient.data.l[0] & 1 << 8)
1052 x = newx;
1053 if (e->xclient.data.l[0] & 1 << 9)
1054 y = newy;
1055 }
1056
1057 client_configure(client, OB_CORNER_TOPLEFT,
1058 x, y, w, h, FALSE, TRUE);
1059
1060 client->gravity = oldg;
1061 }
1062 break;
1063 case PropertyNotify:
1064 /* validate cuz we query stuff off the client here */
1065 if (!client_validate(client)) break;
1066
1067 /* compress changes to a single property into a single change */
1068 while (XCheckTypedWindowEvent(ob_display, client->window,
1069 e->type, &ce)) {
1070 Atom a, b;
1071
1072 /* XXX: it would be nice to compress ALL changes to a property,
1073 not just changes in a row without other props between. */
1074
1075 a = ce.xproperty.atom;
1076 b = e->xproperty.atom;
1077
1078 if (a == b)
1079 continue;
1080 if ((a == prop_atoms.net_wm_name ||
1081 a == prop_atoms.wm_name ||
1082 a == prop_atoms.net_wm_icon_name ||
1083 a == prop_atoms.wm_icon_name)
1084 &&
1085 (b == prop_atoms.net_wm_name ||
1086 b == prop_atoms.wm_name ||
1087 b == prop_atoms.net_wm_icon_name ||
1088 b == prop_atoms.wm_icon_name)) {
1089 continue;
1090 }
1091 if (a == prop_atoms.net_wm_icon &&
1092 b == prop_atoms.net_wm_icon)
1093 continue;
1094
1095 XPutBackEvent(ob_display, &ce);
1096 break;
1097 }
1098
1099 msgtype = e->xproperty.atom;
1100 if (msgtype == XA_WM_NORMAL_HINTS) {
1101 client_update_normal_hints(client);
1102 /* normal hints can make a window non-resizable */
1103 client_setup_decor_and_functions(client);
1104 } else if (msgtype == XA_WM_HINTS) {
1105 client_update_wmhints(client);
1106 } else if (msgtype == XA_WM_TRANSIENT_FOR) {
1107 client_update_transient_for(client);
1108 client_get_type(client);
1109 /* type may have changed, so update the layer */
1110 client_calc_layer(client);
1111 client_setup_decor_and_functions(client);
1112 } else if (msgtype == prop_atoms.net_wm_name ||
1113 msgtype == prop_atoms.wm_name ||
1114 msgtype == prop_atoms.net_wm_icon_name ||
1115 msgtype == prop_atoms.wm_icon_name) {
1116 client_update_title(client);
1117 } else if (msgtype == prop_atoms.wm_class) {
1118 client_update_class(client);
1119 } else if (msgtype == prop_atoms.wm_protocols) {
1120 client_update_protocols(client);
1121 client_setup_decor_and_functions(client);
1122 }
1123 else if (msgtype == prop_atoms.net_wm_strut) {
1124 client_update_strut(client);
1125 }
1126 else if (msgtype == prop_atoms.net_wm_icon) {
1127 client_update_icons(client);
1128 }
1129 else if (msgtype == prop_atoms.net_wm_user_time) {
1130 client_update_user_time(client, TRUE);
1131 }
1132 else if (msgtype == prop_atoms.sm_client_id) {
1133 client_update_sm_client_id(client);
1134 }
1135 default:
1136 ;
1137 #ifdef SHAPE
1138 if (extensions_shape && e->type == extensions_shape_event_basep) {
1139 client->shaped = ((XShapeEvent*)e)->shaped;
1140 frame_adjust_shape(client->frame);
1141 }
1142 #endif
1143 }
1144 }
1145
1146 static void event_handle_dock(ObDock *s, XEvent *e)
1147 {
1148 switch (e->type) {
1149 case ButtonPress:
1150 if (e->xbutton.button == 1)
1151 stacking_raise(DOCK_AS_WINDOW(s));
1152 else if (e->xbutton.button == 2)
1153 stacking_lower(DOCK_AS_WINDOW(s));
1154 break;
1155 case EnterNotify:
1156 dock_hide(FALSE);
1157 break;
1158 case LeaveNotify:
1159 dock_hide(TRUE);
1160 break;
1161 }
1162 }
1163
1164 static void event_handle_dockapp(ObDockApp *app, XEvent *e)
1165 {
1166 switch (e->type) {
1167 case MotionNotify:
1168 dock_app_drag(app, &e->xmotion);
1169 break;
1170 case UnmapNotify:
1171 if (app->ignore_unmaps) {
1172 app->ignore_unmaps--;
1173 break;
1174 }
1175 dock_remove(app, TRUE);
1176 break;
1177 case DestroyNotify:
1178 dock_remove(app, FALSE);
1179 break;
1180 case ReparentNotify:
1181 dock_remove(app, FALSE);
1182 break;
1183 case ConfigureNotify:
1184 dock_app_configure(app, e->xconfigure.width, e->xconfigure.height);
1185 break;
1186 }
1187 }
1188
1189 ObMenuFrame* find_active_menu()
1190 {
1191 GList *it;
1192 ObMenuFrame *ret = NULL;
1193
1194 for (it = menu_frame_visible; it; it = g_list_next(it)) {
1195 ret = it->data;
1196 if (ret->selected)
1197 break;
1198 ret = NULL;
1199 }
1200 return ret;
1201 }
1202
1203 ObMenuFrame* find_active_or_last_menu()
1204 {
1205 ObMenuFrame *ret = NULL;
1206
1207 ret = find_active_menu();
1208 if (!ret && menu_frame_visible)
1209 ret = menu_frame_visible->data;
1210 return ret;
1211 }
1212
1213 static void event_handle_menu(XEvent *ev)
1214 {
1215 ObMenuFrame *f;
1216 ObMenuEntryFrame *e;
1217
1218 switch (ev->type) {
1219 case ButtonRelease:
1220 if (menu_can_hide) {
1221 if ((e = menu_entry_frame_under(ev->xbutton.x_root,
1222 ev->xbutton.y_root)))
1223 menu_entry_frame_execute(e, ev->xbutton.state,
1224 ev->xbutton.time);
1225 else
1226 menu_frame_hide_all();
1227 }
1228 break;
1229 case MotionNotify:
1230 if ((f = menu_frame_under(ev->xmotion.x_root,
1231 ev->xmotion.y_root))) {
1232 menu_frame_move_on_screen(f);
1233 if ((e = menu_entry_frame_under(ev->xmotion.x_root,
1234 ev->xmotion.y_root)))
1235 menu_frame_select(f, e);
1236 }
1237 {
1238 ObMenuFrame *a;
1239
1240 a = find_active_menu();
1241 if (a && a != f &&
1242 a->selected->entry->type != OB_MENU_ENTRY_TYPE_SUBMENU)
1243 {
1244 menu_frame_select(a, NULL);
1245 }
1246 }
1247 break;
1248 case KeyPress:
1249 if (ev->xkey.keycode == ob_keycode(OB_KEY_ESCAPE))
1250 menu_frame_hide_all();
1251 else if (ev->xkey.keycode == ob_keycode(OB_KEY_RETURN)) {
1252 ObMenuFrame *f;
1253 if ((f = find_active_menu()))
1254 menu_entry_frame_execute(f->selected, ev->xkey.state,
1255 ev->xkey.time);
1256 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_LEFT)) {
1257 ObMenuFrame *f;
1258 if ((f = find_active_or_last_menu()) && f->parent)
1259 menu_frame_select(f, NULL);
1260 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_RIGHT)) {
1261 ObMenuFrame *f;
1262 if ((f = find_active_or_last_menu()) && f->child)
1263 menu_frame_select_next(f->child);
1264 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_UP)) {
1265 ObMenuFrame *f;
1266 if ((f = find_active_or_last_menu()))
1267 menu_frame_select_previous(f);
1268 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_DOWN)) {
1269 ObMenuFrame *f;
1270 if ((f = find_active_or_last_menu()))
1271 menu_frame_select_next(f);
1272 }
1273 break;
1274 }
1275 }
1276
1277 static gboolean menu_hide_delay_func(gpointer data)
1278 {
1279 menu_can_hide = TRUE;
1280 return FALSE; /* no repeat */
1281 }
1282
1283 static gboolean focus_delay_func(gpointer data)
1284 {
1285 ObClient *c = data;
1286
1287 if (focus_client != c) {
1288 if (client_validate(c)) {
1289 client_focus(c);
1290 if (config_focus_raise)
1291 client_raise(c);
1292 }
1293 }
1294 return FALSE; /* no repeat */
1295 }
1296
1297 static void focus_delay_client_dest(ObClient *client, gpointer data)
1298 {
1299 ob_main_loop_timeout_remove_data(ob_main_loop, focus_delay_func,
1300 client, TRUE);
1301 }
1302
1303 static void event_client_dest(ObClient *client, gpointer data)
1304 {
1305 if (client == focus_hilite)
1306 focus_hilite = NULL;
1307 }
1308
1309 void event_halt_focus_delay()
1310 {
1311 ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
1312 }
1313
1314 void event_ignore_queued_enters()
1315 {
1316 GSList *saved = NULL, *it;
1317 XEvent *e;
1318
1319 XSync(ob_display, FALSE);
1320
1321 /* count the events */
1322 while (TRUE) {
1323 e = g_new(XEvent, 1);
1324 if (XCheckTypedEvent(ob_display, EnterNotify, e)) {
1325 ObWindow *win;
1326
1327 win = g_hash_table_lookup(window_map, &e->xany.window);
1328 if (win && WINDOW_IS_CLIENT(win))
1329 ++ignore_enter_focus;
1330
1331 saved = g_slist_append(saved, e);
1332 } else {
1333 g_free(e);
1334 break;
1335 }
1336 }
1337 /* put the events back */
1338 for (it = saved; it; it = g_slist_next(it)) {
1339 XPutBackEvent(ob_display, it->data);
1340 g_free(it->data);
1341 }
1342 g_slist_free(saved);
1343 }
This page took 0.10137 seconds and 3 git commands to generate.