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