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