]> Dogcows Code - chaz/openbox/blob - openbox/event.c
sloppy focus fixes, ignore a few more enter events
[chaz/openbox] / openbox / event.c
1 #include "debug.h"
2 #include "openbox.h"
3 #include "dock.h"
4 #include "client.h"
5 #include "xerror.h"
6 #include "prop.h"
7 #include "config.h"
8 #include "screen.h"
9 #include "frame.h"
10 #include "menu.h"
11 #include "menuframe.h"
12 #include "keyboard.h"
13 #include "mouse.h"
14 #include "mainloop.h"
15 #include "framerender.h"
16 #include "focus.h"
17 #include "moveresize.h"
18 #include "stacking.h"
19 #include "extensions.h"
20 #include "event.h"
21
22 #include <X11/Xlib.h>
23 #include <X11/keysym.h>
24 #include <X11/Xatom.h>
25 #include <glib.h>
26
27 #ifdef HAVE_SYS_SELECT_H
28 # include <sys/select.h>
29 #endif
30 #ifdef HAVE_SIGNAL_H
31 # include <signal.h>
32 #endif
33
34 #ifdef USE_SM
35 #include <X11/ICE/ICElib.h>
36 #endif
37
38 static void event_process(const XEvent *e, gpointer data);
39 static void event_handle_root(XEvent *e);
40 static void event_handle_menu(XEvent *e);
41 static void event_handle_dock(ObDock *s, XEvent *e);
42 static void event_handle_dockapp(ObDockApp *app, XEvent *e);
43 static void event_handle_client(ObClient *c, XEvent *e);
44
45 static gboolean focus_delay_func(gpointer data);
46 static void focus_delay_client_dest(gpointer data);
47
48 static gboolean menu_hide_delay_func(gpointer data);
49
50 #define INVALID_FOCUSIN(e) ((e)->xfocus.detail == NotifyInferior || \
51 (e)->xfocus.detail == NotifyAncestor || \
52 (e)->xfocus.detail > NotifyNonlinearVirtual)
53 #define INVALID_FOCUSOUT(e) ((e)->xfocus.mode == NotifyGrab || \
54 (e)->xfocus.detail == NotifyInferior || \
55 (e)->xfocus.detail == NotifyAncestor || \
56 (e)->xfocus.detail > NotifyNonlinearVirtual)
57
58 Time event_lasttime = 0;
59
60 /*! The value of the mask for the NumLock modifier */
61 unsigned int NumLockMask;
62 /*! The value of the mask for the ScrollLock modifier */
63 unsigned int ScrollLockMask;
64 /*! The key codes for the modifier keys */
65 static XModifierKeymap *modmap;
66 /*! Table of the constant modifier masks */
67 static const int mask_table[] = {
68 ShiftMask, LockMask, ControlMask, Mod1Mask,
69 Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask
70 };
71 static int mask_table_size;
72
73 static ObClient *focus_delay_client;
74
75 static gboolean menu_can_hide;
76
77 #ifdef USE_SM
78 static void ice_handler(int fd, gpointer conn)
79 {
80 Bool b;
81 IceProcessMessages(conn, NULL, &b);
82 }
83
84 static void ice_watch(IceConn conn, IcePointer data, Bool opening,
85 IcePointer *watch_data)
86 {
87 static gint fd = -1;
88
89 if (opening) {
90 fd = IceConnectionNumber(conn);
91 ob_main_loop_fd_add(ob_main_loop, fd, ice_handler, conn, NULL);
92 } else {
93 ob_main_loop_fd_remove(ob_main_loop, fd);
94 fd = -1;
95 }
96 }
97 #endif
98
99 void event_startup(gboolean reconfig)
100 {
101 if (reconfig) return;
102
103 mask_table_size = sizeof(mask_table) / sizeof(mask_table[0]);
104
105 /* get lock masks that are defined by the display (not constant) */
106 modmap = XGetModifierMapping(ob_display);
107 g_assert(modmap);
108 if (modmap && modmap->max_keypermod > 0) {
109 size_t cnt;
110 const size_t size = mask_table_size * modmap->max_keypermod;
111 /* get the values of the keyboard lock modifiers
112 Note: Caps lock is not retrieved the same way as Scroll and Num
113 lock since it doesn't need to be. */
114 const KeyCode num_lock = XKeysymToKeycode(ob_display, XK_Num_Lock);
115 const KeyCode scroll_lock = XKeysymToKeycode(ob_display,
116 XK_Scroll_Lock);
117
118 for (cnt = 0; cnt < size; ++cnt) {
119 if (! modmap->modifiermap[cnt]) continue;
120
121 if (num_lock == modmap->modifiermap[cnt])
122 NumLockMask = mask_table[cnt / modmap->max_keypermod];
123 if (scroll_lock == modmap->modifiermap[cnt])
124 ScrollLockMask = mask_table[cnt / modmap->max_keypermod];
125 }
126 }
127
128 ob_main_loop_x_add(ob_main_loop, event_process, NULL, NULL);
129
130 #ifdef USE_SM
131 IceAddConnectionWatch(ice_watch, NULL);
132 #endif
133
134 client_add_destructor(focus_delay_client_dest);
135 }
136
137 void event_shutdown(gboolean reconfig)
138 {
139 if (reconfig) return;
140
141 #ifdef USE_SM
142 IceRemoveConnectionWatch(ice_watch, NULL);
143 #endif
144
145 client_remove_destructor(focus_delay_client_dest);
146 XFreeModifiermap(modmap);
147 }
148
149 static Window event_get_window(XEvent *e)
150 {
151 Window window;
152
153 /* pick a window */
154 switch (e->type) {
155 case SelectionClear:
156 window = RootWindow(ob_display, ob_screen);
157 break;
158 case MapRequest:
159 window = e->xmap.window;
160 break;
161 case UnmapNotify:
162 window = e->xunmap.window;
163 break;
164 case DestroyNotify:
165 window = e->xdestroywindow.window;
166 break;
167 case ConfigureRequest:
168 window = e->xconfigurerequest.window;
169 break;
170 case ConfigureNotify:
171 window = e->xconfigure.window;
172 break;
173 default:
174 #ifdef XKB
175 if (extensions_xkb && e->type == extensions_xkb_event_basep) {
176 switch (((XkbAnyEvent*)e)->xkb_type) {
177 case XkbBellNotify:
178 window = ((XkbBellNotifyEvent*)e)->window;
179 default:
180 window = None;
181 }
182 } else
183 #endif
184 window = e->xany.window;
185 }
186 return window;
187 }
188
189 static void event_set_lasttime(XEvent *e)
190 {
191 Time t = 0;
192
193 /* grab the lasttime and hack up the state */
194 switch (e->type) {
195 case ButtonPress:
196 case ButtonRelease:
197 t = e->xbutton.time;
198 break;
199 case KeyPress:
200 t = e->xkey.time;
201 break;
202 case KeyRelease:
203 t = e->xkey.time;
204 break;
205 case MotionNotify:
206 t = e->xmotion.time;
207 break;
208 case PropertyNotify:
209 t = e->xproperty.time;
210 break;
211 case EnterNotify:
212 case LeaveNotify:
213 t = e->xcrossing.time;
214 break;
215 default:
216 /* if more event types are anticipated, get their timestamp
217 explicitly */
218 break;
219 }
220
221 if (t > event_lasttime)
222 event_lasttime = t;
223 }
224
225 #define STRIP_MODS(s) \
226 s &= ~(LockMask | NumLockMask | ScrollLockMask), \
227 /* kill off the Button1Mask etc, only want the modifiers */ \
228 s &= (ControlMask | ShiftMask | Mod1Mask | \
229 Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask) \
230
231 static void event_hack_mods(XEvent *e)
232 {
233 KeyCode *kp;
234 int i, k;
235
236 switch (e->type) {
237 case ButtonPress:
238 case ButtonRelease:
239 STRIP_MODS(e->xbutton.state);
240 break;
241 case KeyPress:
242 STRIP_MODS(e->xkey.state);
243 break;
244 case KeyRelease:
245 STRIP_MODS(e->xkey.state);
246 /* remove from the state the mask of the modifier being released, if
247 it is a modifier key being released (this is a little ugly..) */
248 kp = modmap->modifiermap;
249 for (i = 0; i < mask_table_size; ++i) {
250 for (k = 0; k < modmap->max_keypermod; ++k) {
251 if (*kp == e->xkey.keycode) { /* found the keycode */
252 /* remove the mask for it */
253 e->xkey.state &= ~mask_table[i];
254 /* cause the first loop to break; */
255 i = mask_table_size;
256 break; /* get outta here! */
257 }
258 ++kp;
259 }
260 }
261 break;
262 case MotionNotify:
263 STRIP_MODS(e->xmotion.state);
264 /* compress events */
265 {
266 XEvent ce;
267 while (XCheckTypedWindowEvent(ob_display, e->xmotion.window,
268 e->type, &ce)) {
269 e->xmotion.x_root = ce.xmotion.x_root;
270 e->xmotion.y_root = ce.xmotion.y_root;
271 }
272 }
273 break;
274 }
275 }
276
277 static gboolean event_ignore(XEvent *e, ObClient *client)
278 {
279 switch(e->type) {
280 case FocusIn:
281 /* NotifyAncestor is not ignored in FocusIn like it is in FocusOut
282 because of RevertToPointerRoot. If the focus ends up reverting to
283 pointer root on a workspace change, then the FocusIn event that we
284 want will be of type NotifyAncestor. This situation does not occur
285 for FocusOut, so it is safely ignored there.
286 */
287 if (INVALID_FOCUSIN(e) ||
288 client == NULL) {
289 #ifdef DEBUG_FOCUS
290 ob_debug("FocusIn on %lx mode %d detail %d IGNORED\n",
291 e->xfocus.window, e->xfocus.mode, e->xfocus.detail);
292 #endif
293 /* says a client was not found for the event (or a valid FocusIn
294 event was not found.
295 */
296 e->xfocus.window = None;
297 return TRUE;
298 }
299
300 #ifdef DEBUG_FOCUS
301 ob_debug("FocusIn on %lx mode %d detail %d\n", e->xfocus.window,
302 e->xfocus.mode, e->xfocus.detail);
303 #endif
304 break;
305 case FocusOut:
306 if (INVALID_FOCUSOUT(e)) {
307 #ifdef DEBUG_FOCUS
308 ob_debug("FocusOut on %lx mode %d detail %d IGNORED\n",
309 e->xfocus.window, e->xfocus.mode, e->xfocus.detail);
310 #endif
311 return TRUE;
312 }
313
314 #ifdef DEBUG_FOCUS
315 ob_debug("FocusOut on %lx mode %d detail %d\n",
316 e->xfocus.window, e->xfocus.mode, e->xfocus.detail);
317 #endif
318
319 {
320 XEvent fe;
321 gboolean fallback = TRUE;
322
323 while (TRUE) {
324 if (!XCheckTypedWindowEvent(ob_display, e->xfocus.window,
325 FocusOut, &fe))
326 if (!XCheckTypedEvent(ob_display, FocusIn, &fe))
327 break;
328 if (fe.type == FocusOut) {
329 #ifdef DEBUG_FOCUS
330 ob_debug("found pending FocusOut\n");
331 #endif
332 if (!INVALID_FOCUSOUT(&fe)) {
333 /* if there is a VALID FocusOut still coming, don't
334 fallback focus yet, we'll deal with it then */
335 XPutBackEvent(ob_display, &fe);
336 fallback = FALSE;
337 break;
338 }
339 } else {
340 #ifdef DEBUG_FOCUS
341 ob_debug("found pending FocusIn\n");
342 #endif
343 /* is the focused window getting a FocusOut/In back to
344 itself?
345 */
346 if (fe.xfocus.window == e->xfocus.window &&
347 !event_ignore(&fe, client)) {
348 /*
349 if focus_client is not set, then we can't do
350 this. we need the FocusIn. This happens in the
351 case when the set_focus_client(NULL) in the
352 focus_fallback function fires and then
353 focus_fallback picks the currently focused
354 window (such as on a SendToDesktop-esque action.
355 */
356 if (focus_client) {
357 #ifdef DEBUG_FOCUS
358 ob_debug("focused window got an Out/In back to "
359 "itself IGNORED both\n");
360 #endif
361 return TRUE;
362 } else {
363 event_process(&fe, NULL);
364 #ifdef DEBUG_FOCUS
365 ob_debug("focused window got an Out/In back to "
366 "itself but focus_client was null "
367 "IGNORED just the Out\n");
368 #endif
369 return TRUE;
370 }
371 }
372
373 /* once all the FocusOut's have been dealt with, if there
374 is a FocusIn still left and it is valid, then use it */
375 event_process(&fe, NULL);
376 /* secret magic way of event_process telling us that no
377 client was found for the FocusIn event. ^_^ */
378 if (fe.xfocus.window != None) {
379 fallback = FALSE;
380 break;
381 }
382 }
383 }
384 if (fallback) {
385 #ifdef DEBUG_FOCUS
386 ob_debug("no valid FocusIn and no FocusOut events found, "
387 "falling back\n");
388 #endif
389 focus_fallback(OB_FOCUS_FALLBACK_NOFOCUS);
390 }
391 }
392 break;
393 case EnterNotify:
394 case LeaveNotify:
395 /* NotifyUngrab occurs when a mouse button is released and the event is
396 caused, like when lowering a window */
397 /* NotifyVirtual and NotifyAncestor occurs when ungrabbing the
398 pointer (Ancestor happens when the pointer is on a window border) */
399 if (e->xcrossing.mode == NotifyGrab ||
400 e->xcrossing.detail == NotifyInferior ||
401 (e->xcrossing.mode == NotifyUngrab &&
402 (e->xcrossing.detail == NotifyAncestor ||
403 e->xcrossing.detail == NotifyNonlinearVirtual ||
404 e->xcrossing.detail == NotifyVirtual))) {
405 #ifdef DEBUG_FOCUS
406 ob_debug("%sNotify mode %d detail %d on %lx IGNORED\n",
407 (e->type == EnterNotify ? "Enter" : "Leave"),
408 e->xcrossing.mode,
409 e->xcrossing.detail, client?client->window:0);
410 #endif
411 return TRUE;
412 }
413 #ifdef DEBUG_FOCUS
414 ob_debug("%sNotify mode %d detail %d on %lx\n",
415 (e->type == EnterNotify ? "Enter" : "Leave"),
416 e->xcrossing.mode,
417 e->xcrossing.detail, client?client->window:0);
418 #endif
419 break;
420 }
421 return FALSE;
422 }
423
424 static void event_process(const XEvent *ec, gpointer data)
425 {
426 Window window;
427 ObClient *client = NULL;
428 ObDock *dock = NULL;
429 ObDockApp *dockapp = NULL;
430 ObWindow *obwin = NULL;
431 XEvent ee, *e;
432
433 /* make a copy we can mangle */
434 ee = *ec;
435 e = &ee;
436
437 window = event_get_window(e);
438 if ((obwin = g_hash_table_lookup(window_map, &window))) {
439 switch (obwin->type) {
440 case Window_Dock:
441 dock = WINDOW_AS_DOCK(obwin);
442 break;
443 case Window_DockApp:
444 dockapp = WINDOW_AS_DOCKAPP(obwin);
445 break;
446 case Window_Client:
447 client = WINDOW_AS_CLIENT(obwin);
448 break;
449 case Window_Menu:
450 case Window_Internal:
451 /* not to be used for events */
452 g_assert_not_reached();
453 break;
454 }
455 }
456
457 event_set_lasttime(e);
458 event_hack_mods(e);
459 if (event_ignore(e, client))
460 return;
461
462 /* deal with it in the kernel */
463 if (client)
464 event_handle_client(client, e);
465 else if (dockapp)
466 event_handle_dockapp(dockapp, e);
467 else if (dock)
468 event_handle_dock(dock, e);
469 else if (window == RootWindow(ob_display, ob_screen))
470 event_handle_root(e);
471 else if (e->type == MapRequest)
472 client_manage(window);
473 else if (e->type == ConfigureRequest) {
474 /* unhandled configure requests must be used to configure the
475 window directly */
476 XWindowChanges xwc;
477
478 xwc.x = e->xconfigurerequest.x;
479 xwc.y = e->xconfigurerequest.y;
480 xwc.width = e->xconfigurerequest.width;
481 xwc.height = e->xconfigurerequest.height;
482 xwc.border_width = e->xconfigurerequest.border_width;
483 xwc.sibling = e->xconfigurerequest.above;
484 xwc.stack_mode = e->xconfigurerequest.detail;
485
486 /* we are not to be held responsible if someone sends us an
487 invalid request! */
488 xerror_set_ignore(TRUE);
489 XConfigureWindow(ob_display, window,
490 e->xconfigurerequest.value_mask, &xwc);
491 xerror_set_ignore(FALSE);
492 }
493
494 /* user input (action-bound) events */
495 if (e->type == ButtonPress || e->type == ButtonRelease ||
496 e->type == MotionNotify || e->type == KeyPress ||
497 e->type == KeyRelease)
498 {
499 if (menu_frame_visible)
500 event_handle_menu(e);
501 else {
502 if (!keyboard_process_interactive_grab(e, &client)) {
503 if (moveresize_in_progress)
504 moveresize_event(e);
505
506 menu_can_hide = FALSE;
507 ob_main_loop_timeout_add(ob_main_loop,
508 G_USEC_PER_SEC / 4,
509 menu_hide_delay_func,
510 NULL, NULL);
511
512 if (e->type == ButtonPress || e->type == ButtonRelease ||
513 e->type == MotionNotify)
514 mouse_event(client, e);
515 else if (e->type == KeyPress)
516 /* when in the middle of a focus cycling action, this
517 causes the window which appears to be focused to be
518 the one on which the actions will be executed */
519 keyboard_event((focus_cycle_target ?
520 focus_cycle_target :
521 (client ? client : focus_client)), e);
522 }
523 }
524 }
525 }
526
527 static void event_handle_root(XEvent *e)
528 {
529 Atom msgtype;
530
531 switch(e->type) {
532 case SelectionClear:
533 ob_debug("Another WM has requested to replace us. Exiting.\n");
534 ob_exit();
535 break;
536
537 case ClientMessage:
538 if (e->xclient.format != 32) break;
539
540 msgtype = e->xclient.message_type;
541 if (msgtype == prop_atoms.net_current_desktop) {
542 unsigned int d = e->xclient.data.l[0];
543 if (d < screen_num_desktops)
544 screen_set_desktop(d);
545 } else if (msgtype == prop_atoms.net_number_of_desktops) {
546 unsigned int d = e->xclient.data.l[0];
547 if (d > 0)
548 screen_set_num_desktops(d);
549 } else if (msgtype == prop_atoms.net_showing_desktop) {
550 screen_show_desktop(e->xclient.data.l[0] != 0);
551 }
552 break;
553 case PropertyNotify:
554 if (e->xproperty.atom == prop_atoms.net_desktop_names)
555 screen_update_desktop_names();
556 else if (e->xproperty.atom == prop_atoms.net_desktop_layout)
557 screen_update_layout();
558 break;
559 case ConfigureNotify:
560 #ifdef XRANDR
561 XRRUpdateConfiguration(e);
562 #endif
563 screen_resize();
564 break;
565 default:
566 ;
567 #ifdef VIDMODE
568 if (extensions_vidmode && e->type == extensions_vidmode_event_basep) {
569 ob_debug("VIDMODE EVENT\n");
570 }
571 #endif
572 }
573 }
574
575 static void event_handle_client(ObClient *client, XEvent *e)
576 {
577 XEvent ce;
578 Atom msgtype;
579 int i=0;
580 ObFrameContext con;
581
582 switch (e->type) {
583 case VisibilityNotify:
584 client->frame->obscured = e->xvisibility.state != VisibilityUnobscured;
585 break;
586 case ButtonPress:
587 case ButtonRelease:
588 /* Wheel buttons don't draw because they are an instant click, so it
589 is a waste of resources to go drawing it. */
590 if (!(e->xbutton.button == 4 || e->xbutton.button == 5)) {
591 con = frame_context(client, e->xbutton.window);
592 con = mouse_button_frame_context(con, e->xbutton.button);
593 switch (con) {
594 case OB_FRAME_CONTEXT_MAXIMIZE:
595 client->frame->max_press = (e->type == ButtonPress);
596 framerender_frame(client->frame);
597 break;
598 case OB_FRAME_CONTEXT_CLOSE:
599 client->frame->close_press = (e->type == ButtonPress);
600 framerender_frame(client->frame);
601 break;
602 case OB_FRAME_CONTEXT_ICONIFY:
603 client->frame->iconify_press = (e->type == ButtonPress);
604 framerender_frame(client->frame);
605 break;
606 case OB_FRAME_CONTEXT_ALLDESKTOPS:
607 client->frame->desk_press = (e->type == ButtonPress);
608 framerender_frame(client->frame);
609 break;
610 case OB_FRAME_CONTEXT_SHADE:
611 client->frame->shade_press = (e->type == ButtonPress);
612 framerender_frame(client->frame);
613 break;
614 default:
615 /* nothing changes with clicks for any other contexts */
616 break;
617 }
618 }
619 break;
620 case FocusIn:
621 #ifdef DEBUG_FOCUS
622 ob_debug("FocusIn on client for %lx\n", client->window);
623 #endif
624 if (client != focus_client) {
625 focus_set_client(client);
626 frame_adjust_focus(client->frame, TRUE);
627 }
628 break;
629 case FocusOut:
630 #ifdef DEBUG_FOCUS
631 ob_debug("FocusOut on client for %lx\n", client->window);
632 #endif
633 /* are we a fullscreen window or a transient of one? (checks layer)
634 if we are then we need to be iconified since we are losing focus
635 */
636 if (client->layer == OB_STACKING_LAYER_FULLSCREEN && !client->iconic &&
637 !client_search_focus_tree_full(client))
638 /* iconify fullscreen windows when they and their transients
639 aren't focused */
640 client_iconify(client, TRUE, TRUE);
641 frame_adjust_focus(client->frame, FALSE);
642 break;
643 case LeaveNotify:
644 con = frame_context(client, e->xcrossing.window);
645 switch (con) {
646 case OB_FRAME_CONTEXT_MAXIMIZE:
647 client->frame->max_hover = FALSE;
648 frame_adjust_state(client->frame);
649 break;
650 case OB_FRAME_CONTEXT_ALLDESKTOPS:
651 client->frame->desk_hover = FALSE;
652 frame_adjust_state(client->frame);
653 break;
654 case OB_FRAME_CONTEXT_SHADE:
655 client->frame->shade_hover = FALSE;
656 frame_adjust_state(client->frame);
657 break;
658 case OB_FRAME_CONTEXT_ICONIFY:
659 client->frame->iconify_hover = FALSE;
660 frame_adjust_state(client->frame);
661 break;
662 case OB_FRAME_CONTEXT_CLOSE:
663 client->frame->close_hover = FALSE;
664 frame_adjust_state(client->frame);
665 break;
666 case OB_FRAME_CONTEXT_FRAME:
667 /* XXX if doing a 'reconfigure' make sure you kill this timer,
668 maybe all timers.. */
669 if (config_focus_delay && client == focus_delay_client) {
670 ob_main_loop_timeout_remove_data(ob_main_loop,
671 focus_delay_func,
672 focus_delay_client);
673 focus_delay_client = NULL;
674 }
675 default:
676 break;
677 }
678 break;
679 case EnterNotify:
680 con = frame_context(client, e->xcrossing.window);
681 switch (con) {
682 case OB_FRAME_CONTEXT_MAXIMIZE:
683 client->frame->max_hover = TRUE;
684 frame_adjust_state(client->frame);
685 break;
686 case OB_FRAME_CONTEXT_ALLDESKTOPS:
687 client->frame->desk_hover = TRUE;
688 frame_adjust_state(client->frame);
689 break;
690 case OB_FRAME_CONTEXT_SHADE:
691 client->frame->shade_hover = TRUE;
692 frame_adjust_state(client->frame);
693 break;
694 case OB_FRAME_CONTEXT_ICONIFY:
695 client->frame->iconify_hover = TRUE;
696 frame_adjust_state(client->frame);
697 break;
698 case OB_FRAME_CONTEXT_CLOSE:
699 client->frame->close_hover = TRUE;
700 frame_adjust_state(client->frame);
701 break;
702 case OB_FRAME_CONTEXT_FRAME:
703 if (client_normal(client)) {
704 if (config_focus_follow) {
705 #ifdef DEBUG_FOCUS
706 ob_debug("EnterNotify on %lx, focusing window\n",
707 client->window);
708 #endif
709 if (config_focus_delay) {
710 ob_main_loop_timeout_add(ob_main_loop,
711 config_focus_delay,
712 focus_delay_func,
713 client, NULL);
714 focus_delay_client = client;
715 } else
716 client_focus(client);
717 }
718 }
719 break;
720 default:
721 break;
722 }
723 break;
724 case ConfigureRequest:
725 /* compress these */
726 while (XCheckTypedWindowEvent(ob_display, client->window,
727 ConfigureRequest, &ce)) {
728 ++i;
729 /* XXX if this causes bad things.. we can compress config req's
730 with the same mask. */
731 e->xconfigurerequest.value_mask |=
732 ce.xconfigurerequest.value_mask;
733 if (ce.xconfigurerequest.value_mask & CWX)
734 e->xconfigurerequest.x = ce.xconfigurerequest.x;
735 if (ce.xconfigurerequest.value_mask & CWY)
736 e->xconfigurerequest.y = ce.xconfigurerequest.y;
737 if (ce.xconfigurerequest.value_mask & CWWidth)
738 e->xconfigurerequest.width = ce.xconfigurerequest.width;
739 if (ce.xconfigurerequest.value_mask & CWHeight)
740 e->xconfigurerequest.height = ce.xconfigurerequest.height;
741 if (ce.xconfigurerequest.value_mask & CWBorderWidth)
742 e->xconfigurerequest.border_width =
743 ce.xconfigurerequest.border_width;
744 if (ce.xconfigurerequest.value_mask & CWStackMode)
745 e->xconfigurerequest.detail = ce.xconfigurerequest.detail;
746 }
747
748 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
749 if (client->iconic || client->shaded) return;
750
751 /* resize, then move, as specified in the EWMH section 7.7 */
752 if (e->xconfigurerequest.value_mask & (CWWidth | CWHeight |
753 CWX | CWY |
754 CWBorderWidth)) {
755 int x, y, w, h;
756 ObCorner corner;
757
758 if (e->xconfigurerequest.value_mask & CWBorderWidth)
759 client->border_width = e->xconfigurerequest.border_width;
760
761 x = (e->xconfigurerequest.value_mask & CWX) ?
762 e->xconfigurerequest.x : client->area.x;
763 y = (e->xconfigurerequest.value_mask & CWY) ?
764 e->xconfigurerequest.y : client->area.y;
765 w = (e->xconfigurerequest.value_mask & CWWidth) ?
766 e->xconfigurerequest.width : client->area.width;
767 h = (e->xconfigurerequest.value_mask & CWHeight) ?
768 e->xconfigurerequest.height : client->area.height;
769
770 {
771 int newx = x;
772 int newy = y;
773 int fw = w +
774 client->frame->size.left + client->frame->size.right;
775 int fh = h +
776 client->frame->size.top + client->frame->size.bottom;
777 client_find_onscreen(client, &newx, &newy, fw, fh,
778 client_normal(client));
779 if (e->xconfigurerequest.value_mask & CWX)
780 x = newx;
781 if (e->xconfigurerequest.value_mask & CWY)
782 y = newy;
783 }
784
785 switch (client->gravity) {
786 case NorthEastGravity:
787 case EastGravity:
788 corner = OB_CORNER_TOPRIGHT;
789 break;
790 case SouthWestGravity:
791 case SouthGravity:
792 corner = OB_CORNER_BOTTOMLEFT;
793 break;
794 case SouthEastGravity:
795 corner = OB_CORNER_BOTTOMRIGHT;
796 break;
797 default: /* NorthWest, Static, etc */
798 corner = OB_CORNER_TOPLEFT;
799 }
800
801 client_configure_full(client, corner, x, y, w, h, FALSE, TRUE,
802 TRUE);
803 }
804
805 if (e->xconfigurerequest.value_mask & CWStackMode) {
806 switch (e->xconfigurerequest.detail) {
807 case Below:
808 case BottomIf:
809 stacking_lower(CLIENT_AS_WINDOW(client));
810 break;
811
812 case Above:
813 case TopIf:
814 default:
815 stacking_raise(CLIENT_AS_WINDOW(client));
816 break;
817 }
818 }
819 break;
820 case UnmapNotify:
821 if (client->ignore_unmaps) {
822 client->ignore_unmaps--;
823 break;
824 }
825 client_unmanage(client);
826 break;
827 case DestroyNotify:
828 client_unmanage(client);
829 break;
830 case ReparentNotify:
831 /* this is when the client is first taken captive in the frame */
832 if (e->xreparent.parent == client->frame->plate) break;
833
834 /*
835 This event is quite rare and is usually handled in unmapHandler.
836 However, if the window is unmapped when the reparent event occurs,
837 the window manager never sees it because an unmap event is not sent
838 to an already unmapped window.
839 */
840
841 /* we don't want the reparent event, put it back on the stack for the
842 X server to deal with after we unmanage the window */
843 XPutBackEvent(ob_display, e);
844
845 client_unmanage(client);
846 break;
847 case MapRequest:
848 ob_debug("MapRequest for 0x%lx\n", client->window);
849 if (!client->iconic) break; /* this normally doesn't happen, but if it
850 does, we don't want it! */
851 if (screen_showing_desktop)
852 screen_show_desktop(FALSE);
853 client_iconify(client, FALSE, TRUE);
854 if (!client->frame->visible)
855 /* if its not visible still, then don't mess with it */
856 break;
857 if (client->shaded)
858 client_shade(client, FALSE);
859 client_focus(client);
860 stacking_raise(CLIENT_AS_WINDOW(client));
861 break;
862 case ClientMessage:
863 /* validate cuz we query stuff off the client here */
864 if (!client_validate(client)) break;
865
866 if (e->xclient.format != 32) return;
867
868 msgtype = e->xclient.message_type;
869 if (msgtype == prop_atoms.wm_change_state) {
870 /* compress changes into a single change */
871 while (XCheckTypedWindowEvent(ob_display, client->window,
872 e->type, &ce)) {
873 /* XXX: it would be nice to compress ALL messages of a
874 type, not just messages in a row without other
875 message types between. */
876 if (ce.xclient.message_type != msgtype) {
877 XPutBackEvent(ob_display, &ce);
878 break;
879 }
880 e->xclient = ce.xclient;
881 }
882 client_set_wm_state(client, e->xclient.data.l[0]);
883 } else if (msgtype == prop_atoms.net_wm_desktop) {
884 /* compress changes into a single change */
885 while (XCheckTypedWindowEvent(ob_display, client->window,
886 e->type, &ce)) {
887 /* XXX: it would be nice to compress ALL messages of a
888 type, not just messages in a row without other
889 message types between. */
890 if (ce.xclient.message_type != msgtype) {
891 XPutBackEvent(ob_display, &ce);
892 break;
893 }
894 e->xclient = ce.xclient;
895 }
896 if ((unsigned)e->xclient.data.l[0] < screen_num_desktops ||
897 (unsigned)e->xclient.data.l[0] == DESKTOP_ALL)
898 client_set_desktop(client, (unsigned)e->xclient.data.l[0],
899 FALSE);
900 } else if (msgtype == prop_atoms.net_wm_state) {
901 /* can't compress these */
902 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
903 (e->xclient.data.l[0] == 0 ? "Remove" :
904 e->xclient.data.l[0] == 1 ? "Add" :
905 e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
906 e->xclient.data.l[1], e->xclient.data.l[2],
907 client->window);
908 client_set_state(client, e->xclient.data.l[0],
909 e->xclient.data.l[1], e->xclient.data.l[2]);
910 } else if (msgtype == prop_atoms.net_close_window) {
911 ob_debug("net_close_window for 0x%lx\n", client->window);
912 client_close(client);
913 } else if (msgtype == prop_atoms.net_active_window) {
914 ob_debug("net_active_window for 0x%lx\n", client->window);
915 client_activate(client, FALSE);
916 } else if (msgtype == prop_atoms.net_wm_moveresize) {
917 ob_debug("net_wm_moveresize for 0x%lx\n", client->window);
918 if ((Atom)e->xclient.data.l[2] ==
919 prop_atoms.net_wm_moveresize_size_topleft ||
920 (Atom)e->xclient.data.l[2] ==
921 prop_atoms.net_wm_moveresize_size_top ||
922 (Atom)e->xclient.data.l[2] ==
923 prop_atoms.net_wm_moveresize_size_topright ||
924 (Atom)e->xclient.data.l[2] ==
925 prop_atoms.net_wm_moveresize_size_right ||
926 (Atom)e->xclient.data.l[2] ==
927 prop_atoms.net_wm_moveresize_size_right ||
928 (Atom)e->xclient.data.l[2] ==
929 prop_atoms.net_wm_moveresize_size_bottomright ||
930 (Atom)e->xclient.data.l[2] ==
931 prop_atoms.net_wm_moveresize_size_bottom ||
932 (Atom)e->xclient.data.l[2] ==
933 prop_atoms.net_wm_moveresize_size_bottomleft ||
934 (Atom)e->xclient.data.l[2] ==
935 prop_atoms.net_wm_moveresize_size_left ||
936 (Atom)e->xclient.data.l[2] ==
937 prop_atoms.net_wm_moveresize_move ||
938 (Atom)e->xclient.data.l[2] ==
939 prop_atoms.net_wm_moveresize_size_keyboard ||
940 (Atom)e->xclient.data.l[2] ==
941 prop_atoms.net_wm_moveresize_move_keyboard) {
942
943 moveresize_start(client, e->xclient.data.l[0],
944 e->xclient.data.l[1], e->xclient.data.l[3],
945 e->xclient.data.l[2]);
946 }
947 } else if (msgtype == prop_atoms.net_moveresize_window) {
948 int oldg = client->gravity;
949 int tmpg, x, y, w, h;
950
951 if (e->xclient.data.l[0] & 0xff)
952 tmpg = e->xclient.data.l[0] & 0xff;
953 else
954 tmpg = oldg;
955
956 if (e->xclient.data.l[0] & 1 << 8)
957 x = e->xclient.data.l[1];
958 else
959 x = client->area.x;
960 if (e->xclient.data.l[0] & 1 << 9)
961 y = e->xclient.data.l[2];
962 else
963 y = client->area.y;
964 if (e->xclient.data.l[0] & 1 << 10)
965 w = e->xclient.data.l[3];
966 else
967 w = client->area.width;
968 if (e->xclient.data.l[0] & 1 << 11)
969 h = e->xclient.data.l[4];
970 else
971 h = client->area.height;
972 client->gravity = tmpg;
973
974 {
975 int newx = x;
976 int newy = y;
977 int fw = w +
978 client->frame->size.left + client->frame->size.right;
979 int fh = h +
980 client->frame->size.top + client->frame->size.bottom;
981 client_find_onscreen(client, &newx, &newy, fw, fh,
982 client_normal(client));
983 if (e->xclient.data.l[0] & 1 << 8)
984 x = newx;
985 if (e->xclient.data.l[0] & 1 << 9)
986 y = newy;
987 }
988
989 client_configure(client, OB_CORNER_TOPLEFT,
990 x, y, w, h, FALSE, TRUE);
991
992 client->gravity = oldg;
993 }
994 break;
995 case PropertyNotify:
996 /* validate cuz we query stuff off the client here */
997 if (!client_validate(client)) break;
998
999 /* compress changes to a single property into a single change */
1000 while (XCheckTypedWindowEvent(ob_display, client->window,
1001 e->type, &ce)) {
1002 Atom a, b;
1003
1004 /* XXX: it would be nice to compress ALL changes to a property,
1005 not just changes in a row without other props between. */
1006
1007 a = ce.xproperty.atom;
1008 b = e->xproperty.atom;
1009
1010 if (a == b)
1011 continue;
1012 if ((a == prop_atoms.net_wm_name ||
1013 a == prop_atoms.wm_name ||
1014 a == prop_atoms.net_wm_icon_name ||
1015 a == prop_atoms.wm_icon_name)
1016 &&
1017 (b == prop_atoms.net_wm_name ||
1018 b == prop_atoms.wm_name ||
1019 b == prop_atoms.net_wm_icon_name ||
1020 b == prop_atoms.wm_icon_name)) {
1021 continue;
1022 }
1023 if ((a == prop_atoms.net_wm_icon ||
1024 a == prop_atoms.kwm_win_icon)
1025 &&
1026 (b == prop_atoms.net_wm_icon ||
1027 b == prop_atoms.kwm_win_icon))
1028 continue;
1029
1030 XPutBackEvent(ob_display, &ce);
1031 break;
1032 }
1033
1034 msgtype = e->xproperty.atom;
1035 if (msgtype == XA_WM_NORMAL_HINTS) {
1036 client_update_normal_hints(client);
1037 /* normal hints can make a window non-resizable */
1038 client_setup_decor_and_functions(client);
1039 } else if (msgtype == XA_WM_HINTS) {
1040 client_update_wmhints(client);
1041 } else if (msgtype == XA_WM_TRANSIENT_FOR) {
1042 client_update_transient_for(client);
1043 client_get_type(client);
1044 /* type may have changed, so update the layer */
1045 client_calc_layer(client);
1046 client_setup_decor_and_functions(client);
1047 } else if (msgtype == prop_atoms.net_wm_name ||
1048 msgtype == prop_atoms.wm_name ||
1049 msgtype == prop_atoms.net_wm_icon_name ||
1050 msgtype == prop_atoms.wm_icon_name) {
1051 client_update_title(client);
1052 } else if (msgtype == prop_atoms.wm_class) {
1053 client_update_class(client);
1054 } else if (msgtype == prop_atoms.wm_protocols) {
1055 client_update_protocols(client);
1056 client_setup_decor_and_functions(client);
1057 }
1058 else if (msgtype == prop_atoms.net_wm_strut) {
1059 client_update_strut(client);
1060 }
1061 else if (msgtype == prop_atoms.net_wm_icon ||
1062 msgtype == prop_atoms.kwm_win_icon) {
1063 client_update_icons(client);
1064 }
1065 default:
1066 ;
1067 #ifdef SHAPE
1068 if (extensions_shape && e->type == extensions_shape_event_basep) {
1069 client->shaped = ((XShapeEvent*)e)->shaped;
1070 frame_adjust_shape(client->frame);
1071 }
1072 #endif
1073 }
1074 }
1075
1076 static void event_handle_dock(ObDock *s, XEvent *e)
1077 {
1078 switch (e->type) {
1079 case ButtonPress:
1080 stacking_raise(DOCK_AS_WINDOW(s));
1081 break;
1082 case EnterNotify:
1083 dock_hide(FALSE);
1084 break;
1085 case LeaveNotify:
1086 dock_hide(TRUE);
1087 break;
1088 }
1089 }
1090
1091 static void event_handle_dockapp(ObDockApp *app, XEvent *e)
1092 {
1093 switch (e->type) {
1094 case MotionNotify:
1095 dock_app_drag(app, &e->xmotion);
1096 break;
1097 case UnmapNotify:
1098 if (app->ignore_unmaps) {
1099 app->ignore_unmaps--;
1100 break;
1101 }
1102 dock_remove(app, TRUE);
1103 break;
1104 case DestroyNotify:
1105 dock_remove(app, FALSE);
1106 break;
1107 case ReparentNotify:
1108 dock_remove(app, FALSE);
1109 break;
1110 case ConfigureNotify:
1111 dock_app_configure(app, e->xconfigure.width, e->xconfigure.height);
1112 break;
1113 }
1114 }
1115
1116 ObMenuFrame* find_active_menu()
1117 {
1118 GList *it;
1119 ObMenuFrame *f;
1120
1121 for (it = menu_frame_visible; it; it = g_list_next(it)) {
1122 f = it->data;
1123 if (f->selected)
1124 break;
1125 }
1126 return it ? it->data : NULL;
1127 }
1128
1129 static void event_handle_menu(XEvent *ev)
1130 {
1131 ObMenuFrame *f;
1132 ObMenuEntryFrame *e;
1133
1134 switch (ev->type) {
1135 case ButtonRelease:
1136 if ((e = menu_entry_frame_under(ev->xbutton.x_root,
1137 ev->xbutton.y_root)))
1138 menu_entry_frame_execute(e, ev->xbutton.state);
1139 else if (menu_can_hide)
1140 menu_frame_hide_all();
1141 break;
1142 case MotionNotify:
1143 if ((f = menu_frame_under(ev->xmotion.x_root,
1144 ev->xmotion.y_root))) {
1145 menu_frame_move_on_screen(f);
1146 if ((e = menu_entry_frame_under(ev->xmotion.x_root,
1147 ev->xmotion.y_root)))
1148 menu_frame_select(f, e);
1149 }
1150 break;
1151 case KeyPress:
1152 if (ev->xkey.keycode == ob_keycode(OB_KEY_ESCAPE))
1153 menu_frame_hide_all();
1154 else if (ev->xkey.keycode == ob_keycode(OB_KEY_RETURN)) {
1155 ObMenuFrame *f;
1156 if ((f = find_active_menu()))
1157 menu_entry_frame_execute(f->selected, ev->xkey.state);
1158 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_LEFT)) {
1159 ObMenuFrame *f;
1160 if ((f = find_active_menu()) && f->parent)
1161 menu_frame_select(f, NULL);
1162 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_RIGHT)) {
1163 ObMenuFrame *f;
1164 if ((f = find_active_menu()) && f->child)
1165 menu_frame_select_next(f->child);
1166 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_UP)) {
1167 ObMenuFrame *f;
1168 if ((f = find_active_menu()))
1169 menu_frame_select_previous(f);
1170 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_DOWN)) {
1171 ObMenuFrame *f;
1172 if ((f = find_active_menu()))
1173 menu_frame_select_next(f);
1174 }
1175 break;
1176 }
1177 }
1178
1179 static gboolean menu_hide_delay_func(gpointer data)
1180 {
1181 menu_can_hide = TRUE;
1182 return FALSE; /* no repeat */
1183 }
1184
1185 static gboolean focus_delay_func(gpointer data)
1186 {
1187 client_focus(focus_delay_client);
1188 return FALSE; /* no repeat */
1189 }
1190
1191 static void focus_delay_client_dest(gpointer data)
1192 {
1193 ObClient *c = data;
1194 if (c == focus_delay_client) {
1195 ob_main_loop_timeout_remove_data(ob_main_loop, focus_delay_func,
1196 focus_delay_client);
1197 focus_delay_client = NULL;
1198 }
1199 }
This page took 0.085947 seconds and 5 git commands to generate.