]> Dogcows Code - chaz/openbox/blob - openbox/event.c
ifdef DEBUG_FOCUS focus output
[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 #ifdef DEBUG_FOCUS
389 ob_debug("FocusIn was OK, so don't fallback\n");
390 #endif
391 fallback = FALSE;
392 break;
393 }
394 }
395 }
396 }
397 if (fallback) {
398 #ifdef DEBUG_FOCUS
399 ob_debug("no valid FocusIn and no FocusOut events found, "
400 "falling back\n");
401 #endif
402 focus_fallback(OB_FOCUS_FALLBACK_NOFOCUS);
403 }
404 }
405 break;
406 case EnterNotify:
407 case LeaveNotify:
408 /* NotifyUngrab occurs when a mouse button is released and the event is
409 caused, like when lowering a window */
410 /* NotifyVirtual and NotifyAncestor occurs when ungrabbing the
411 pointer (Ancestor happens when the pointer is on a window border) */
412 if (e->xcrossing.mode == NotifyGrab ||
413 e->xcrossing.detail == NotifyInferior ||
414 (e->xcrossing.mode == NotifyUngrab &&
415 (e->xcrossing.detail == NotifyAncestor ||
416 e->xcrossing.detail == NotifyNonlinearVirtual ||
417 e->xcrossing.detail == NotifyVirtual))) {
418 #ifdef DEBUG_FOCUS
419 ob_debug("%sNotify mode %d detail %d on %lx IGNORED\n",
420 (e->type == EnterNotify ? "Enter" : "Leave"),
421 e->xcrossing.mode,
422 e->xcrossing.detail, client?client->window:0);
423 #endif
424 return TRUE;
425 }
426 #ifdef DEBUG_FOCUS
427 ob_debug("%sNotify mode %d detail %d on %lx\n",
428 (e->type == EnterNotify ? "Enter" : "Leave"),
429 e->xcrossing.mode,
430 e->xcrossing.detail, client?client->window:0);
431 #endif
432 break;
433 }
434 return FALSE;
435 }
436
437 static void event_process(const XEvent *ec, gpointer data)
438 {
439 Window window;
440 ObGroup *group = NULL;
441 ObClient *client = NULL;
442 ObDock *dock = NULL;
443 ObDockApp *dockapp = NULL;
444 ObWindow *obwin = NULL;
445 XEvent ee, *e;
446 ObEventData *ed = data;
447
448 /* make a copy we can mangle */
449 ee = *ec;
450 e = &ee;
451
452 window = event_get_window(e);
453 if (!(e->type == PropertyNotify &&
454 (group = g_hash_table_lookup(group_map, &window))))
455 if ((obwin = g_hash_table_lookup(window_map, &window))) {
456 switch (obwin->type) {
457 case Window_Dock:
458 dock = WINDOW_AS_DOCK(obwin);
459 break;
460 case Window_DockApp:
461 dockapp = WINDOW_AS_DOCKAPP(obwin);
462 break;
463 case Window_Client:
464 client = WINDOW_AS_CLIENT(obwin);
465 break;
466 case Window_Menu:
467 case Window_Internal:
468 /* not to be used for events */
469 g_assert_not_reached();
470 break;
471 }
472 }
473
474 event_set_lasttime(e);
475 event_hack_mods(e);
476 if (event_ignore(e, client)) {
477 if (ed)
478 ed->ignored = TRUE;
479 return;
480 } else if (ed)
481 ed->ignored = FALSE;
482
483 /* deal with it in the kernel */
484 if (group)
485 event_handle_group(group, e);
486 else if (client)
487 event_handle_client(client, e);
488 else if (dockapp)
489 event_handle_dockapp(dockapp, e);
490 else if (dock)
491 event_handle_dock(dock, e);
492 else if (window == RootWindow(ob_display, ob_screen))
493 event_handle_root(e);
494 else if (e->type == MapRequest)
495 client_manage(window);
496 else if (e->type == ConfigureRequest) {
497 /* unhandled configure requests must be used to configure the
498 window directly */
499 XWindowChanges xwc;
500
501 xwc.x = e->xconfigurerequest.x;
502 xwc.y = e->xconfigurerequest.y;
503 xwc.width = e->xconfigurerequest.width;
504 xwc.height = e->xconfigurerequest.height;
505 xwc.border_width = e->xconfigurerequest.border_width;
506 xwc.sibling = e->xconfigurerequest.above;
507 xwc.stack_mode = e->xconfigurerequest.detail;
508
509 /* we are not to be held responsible if someone sends us an
510 invalid request! */
511 xerror_set_ignore(TRUE);
512 XConfigureWindow(ob_display, window,
513 e->xconfigurerequest.value_mask, &xwc);
514 xerror_set_ignore(FALSE);
515 }
516
517 /* user input (action-bound) events */
518 if (e->type == ButtonPress || e->type == ButtonRelease ||
519 e->type == MotionNotify || e->type == KeyPress ||
520 e->type == KeyRelease)
521 {
522 if (menu_frame_visible)
523 event_handle_menu(e);
524 else {
525 if (!keyboard_process_interactive_grab(e, &client)) {
526 if (moveresize_in_progress)
527 moveresize_event(e);
528
529 menu_can_hide = FALSE;
530 ob_main_loop_timeout_add(ob_main_loop,
531 G_USEC_PER_SEC / 4,
532 menu_hide_delay_func,
533 NULL, NULL);
534
535 if (e->type == ButtonPress || e->type == ButtonRelease ||
536 e->type == MotionNotify)
537 mouse_event(client, e);
538 else if (e->type == KeyPress)
539 /* when in the middle of a focus cycling action, this
540 causes the window which appears to be focused to be
541 the one on which the actions will be executed */
542 keyboard_event((focus_cycle_target ?
543 focus_cycle_target :
544 (client ? client : focus_client)), e);
545 }
546 }
547 }
548 }
549
550 static void event_handle_root(XEvent *e)
551 {
552 Atom msgtype;
553
554 switch(e->type) {
555 case SelectionClear:
556 ob_debug("Another WM has requested to replace us. Exiting.\n");
557 ob_exit();
558 break;
559
560 case ClientMessage:
561 if (e->xclient.format != 32) break;
562
563 msgtype = e->xclient.message_type;
564 if (msgtype == prop_atoms.net_current_desktop) {
565 unsigned int d = e->xclient.data.l[0];
566 if (d < screen_num_desktops)
567 screen_set_desktop(d);
568 } else if (msgtype == prop_atoms.net_number_of_desktops) {
569 unsigned int d = e->xclient.data.l[0];
570 if (d > 0)
571 screen_set_num_desktops(d);
572 } else if (msgtype == prop_atoms.net_showing_desktop) {
573 screen_show_desktop(e->xclient.data.l[0] != 0);
574 }
575 break;
576 case PropertyNotify:
577 if (e->xproperty.atom == prop_atoms.net_desktop_names)
578 screen_update_desktop_names();
579 else if (e->xproperty.atom == prop_atoms.net_desktop_layout)
580 screen_update_layout();
581 break;
582 case ConfigureNotify:
583 #ifdef XRANDR
584 XRRUpdateConfiguration(e);
585 #endif
586 screen_resize();
587 break;
588 default:
589 ;
590 #ifdef VIDMODE
591 if (extensions_vidmode && e->type == extensions_vidmode_event_basep) {
592 ob_debug("VIDMODE EVENT\n");
593 }
594 #endif
595 }
596 }
597
598 static void event_handle_group(ObGroup *group, XEvent *e)
599 {
600 GSList *it;
601
602 g_assert(e->type == PropertyNotify);
603
604 for (it = group->members; it; it = g_slist_next(it))
605 event_handle_client(it->data, e);
606 }
607
608 static void event_handle_client(ObClient *client, XEvent *e)
609 {
610 XEvent ce;
611 Atom msgtype;
612 int i=0;
613 ObFrameContext con;
614
615 switch (e->type) {
616 case VisibilityNotify:
617 client->frame->obscured = e->xvisibility.state != VisibilityUnobscured;
618 break;
619 case ButtonPress:
620 case ButtonRelease:
621 /* Wheel buttons don't draw because they are an instant click, so it
622 is a waste of resources to go drawing it. */
623 if (!(e->xbutton.button == 4 || e->xbutton.button == 5)) {
624 con = frame_context(client, e->xbutton.window);
625 con = mouse_button_frame_context(con, e->xbutton.button);
626 switch (con) {
627 case OB_FRAME_CONTEXT_MAXIMIZE:
628 client->frame->max_press = (e->type == ButtonPress);
629 framerender_frame(client->frame);
630 break;
631 case OB_FRAME_CONTEXT_CLOSE:
632 client->frame->close_press = (e->type == ButtonPress);
633 framerender_frame(client->frame);
634 break;
635 case OB_FRAME_CONTEXT_ICONIFY:
636 client->frame->iconify_press = (e->type == ButtonPress);
637 framerender_frame(client->frame);
638 break;
639 case OB_FRAME_CONTEXT_ALLDESKTOPS:
640 client->frame->desk_press = (e->type == ButtonPress);
641 framerender_frame(client->frame);
642 break;
643 case OB_FRAME_CONTEXT_SHADE:
644 client->frame->shade_press = (e->type == ButtonPress);
645 framerender_frame(client->frame);
646 break;
647 default:
648 /* nothing changes with clicks for any other contexts */
649 break;
650 }
651 }
652 break;
653 case FocusIn:
654 #ifdef DEBUG_FOCUS
655 ob_debug("FocusIn on client for %lx\n", client->window);
656 #endif
657 if (client != focus_client) {
658 focus_set_client(client);
659 frame_adjust_focus(client->frame, TRUE);
660 }
661 break;
662 case FocusOut:
663 #ifdef DEBUG_FOCUS
664 ob_debug("FocusOut on client for %lx\n", client->window);
665 #endif
666 /* are we a fullscreen window or a transient of one? (checks layer)
667 if we are then we need to be iconified since we are losing focus
668 */
669 if (client->layer == OB_STACKING_LAYER_FULLSCREEN && !client->iconic &&
670 !client_search_focus_tree_full(client))
671 /* iconify fullscreen windows when they and their transients
672 aren't focused */
673 client_iconify(client, TRUE, TRUE);
674 frame_adjust_focus(client->frame, FALSE);
675 break;
676 case LeaveNotify:
677 con = frame_context(client, e->xcrossing.window);
678 switch (con) {
679 case OB_FRAME_CONTEXT_MAXIMIZE:
680 client->frame->max_hover = FALSE;
681 frame_adjust_state(client->frame);
682 break;
683 case OB_FRAME_CONTEXT_ALLDESKTOPS:
684 client->frame->desk_hover = FALSE;
685 frame_adjust_state(client->frame);
686 break;
687 case OB_FRAME_CONTEXT_SHADE:
688 client->frame->shade_hover = FALSE;
689 frame_adjust_state(client->frame);
690 break;
691 case OB_FRAME_CONTEXT_ICONIFY:
692 client->frame->iconify_hover = FALSE;
693 frame_adjust_state(client->frame);
694 break;
695 case OB_FRAME_CONTEXT_CLOSE:
696 client->frame->close_hover = FALSE;
697 frame_adjust_state(client->frame);
698 break;
699 case OB_FRAME_CONTEXT_FRAME:
700 /* XXX if doing a 'reconfigure' make sure you kill this timer,
701 maybe all timers.. */
702 if (config_focus_delay && client == focus_delay_client) {
703 ob_main_loop_timeout_remove_data(ob_main_loop,
704 focus_delay_func,
705 focus_delay_client);
706 focus_delay_client = NULL;
707 }
708 default:
709 break;
710 }
711 break;
712 case EnterNotify:
713 con = frame_context(client, e->xcrossing.window);
714 switch (con) {
715 case OB_FRAME_CONTEXT_MAXIMIZE:
716 client->frame->max_hover = TRUE;
717 frame_adjust_state(client->frame);
718 break;
719 case OB_FRAME_CONTEXT_ALLDESKTOPS:
720 client->frame->desk_hover = TRUE;
721 frame_adjust_state(client->frame);
722 break;
723 case OB_FRAME_CONTEXT_SHADE:
724 client->frame->shade_hover = TRUE;
725 frame_adjust_state(client->frame);
726 break;
727 case OB_FRAME_CONTEXT_ICONIFY:
728 client->frame->iconify_hover = TRUE;
729 frame_adjust_state(client->frame);
730 break;
731 case OB_FRAME_CONTEXT_CLOSE:
732 client->frame->close_hover = TRUE;
733 frame_adjust_state(client->frame);
734 break;
735 case OB_FRAME_CONTEXT_FRAME:
736 if (client_normal(client)) {
737 if (config_focus_follow) {
738 #ifdef DEBUG_FOCUS
739 ob_debug("EnterNotify on %lx, focusing window\n",
740 client->window);
741 #endif
742 if (config_focus_delay) {
743 ob_main_loop_timeout_add(ob_main_loop,
744 config_focus_delay,
745 focus_delay_func,
746 client, NULL);
747 focus_delay_client = client;
748 } else
749 client_focus(client);
750 }
751 }
752 break;
753 default:
754 break;
755 }
756 break;
757 case ConfigureRequest:
758 /* compress these */
759 while (XCheckTypedWindowEvent(ob_display, client->window,
760 ConfigureRequest, &ce)) {
761 ++i;
762 /* XXX if this causes bad things.. we can compress config req's
763 with the same mask. */
764 e->xconfigurerequest.value_mask |=
765 ce.xconfigurerequest.value_mask;
766 if (ce.xconfigurerequest.value_mask & CWX)
767 e->xconfigurerequest.x = ce.xconfigurerequest.x;
768 if (ce.xconfigurerequest.value_mask & CWY)
769 e->xconfigurerequest.y = ce.xconfigurerequest.y;
770 if (ce.xconfigurerequest.value_mask & CWWidth)
771 e->xconfigurerequest.width = ce.xconfigurerequest.width;
772 if (ce.xconfigurerequest.value_mask & CWHeight)
773 e->xconfigurerequest.height = ce.xconfigurerequest.height;
774 if (ce.xconfigurerequest.value_mask & CWBorderWidth)
775 e->xconfigurerequest.border_width =
776 ce.xconfigurerequest.border_width;
777 if (ce.xconfigurerequest.value_mask & CWStackMode)
778 e->xconfigurerequest.detail = ce.xconfigurerequest.detail;
779 }
780
781 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
782 if (client->iconic || client->shaded) return;
783
784 /* resize, then move, as specified in the EWMH section 7.7 */
785 if (e->xconfigurerequest.value_mask & (CWWidth | CWHeight |
786 CWX | CWY |
787 CWBorderWidth)) {
788 int x, y, w, h;
789 ObCorner corner;
790
791 if (e->xconfigurerequest.value_mask & CWBorderWidth)
792 client->border_width = e->xconfigurerequest.border_width;
793
794 x = (e->xconfigurerequest.value_mask & CWX) ?
795 e->xconfigurerequest.x : client->area.x;
796 y = (e->xconfigurerequest.value_mask & CWY) ?
797 e->xconfigurerequest.y : client->area.y;
798 w = (e->xconfigurerequest.value_mask & CWWidth) ?
799 e->xconfigurerequest.width : client->area.width;
800 h = (e->xconfigurerequest.value_mask & CWHeight) ?
801 e->xconfigurerequest.height : client->area.height;
802
803 {
804 int newx = x;
805 int newy = y;
806 int fw = w +
807 client->frame->size.left + client->frame->size.right;
808 int fh = h +
809 client->frame->size.top + client->frame->size.bottom;
810 client_find_onscreen(client, &newx, &newy, fw, fh,
811 client_normal(client));
812 if (e->xconfigurerequest.value_mask & CWX)
813 x = newx;
814 if (e->xconfigurerequest.value_mask & CWY)
815 y = newy;
816 }
817
818 switch (client->gravity) {
819 case NorthEastGravity:
820 case EastGravity:
821 corner = OB_CORNER_TOPRIGHT;
822 break;
823 case SouthWestGravity:
824 case SouthGravity:
825 corner = OB_CORNER_BOTTOMLEFT;
826 break;
827 case SouthEastGravity:
828 corner = OB_CORNER_BOTTOMRIGHT;
829 break;
830 default: /* NorthWest, Static, etc */
831 corner = OB_CORNER_TOPLEFT;
832 }
833
834 client_configure_full(client, corner, x, y, w, h, FALSE, TRUE,
835 TRUE);
836 }
837
838 if (e->xconfigurerequest.value_mask & CWStackMode) {
839 switch (e->xconfigurerequest.detail) {
840 case Below:
841 case BottomIf:
842 stacking_lower(CLIENT_AS_WINDOW(client));
843 break;
844
845 case Above:
846 case TopIf:
847 default:
848 stacking_raise(CLIENT_AS_WINDOW(client));
849 break;
850 }
851 }
852 break;
853 case UnmapNotify:
854 if (client->ignore_unmaps) {
855 client->ignore_unmaps--;
856 break;
857 }
858 client_unmanage(client);
859 break;
860 case DestroyNotify:
861 client_unmanage(client);
862 break;
863 case ReparentNotify:
864 /* this is when the client is first taken captive in the frame */
865 if (e->xreparent.parent == client->frame->plate) break;
866
867 /*
868 This event is quite rare and is usually handled in unmapHandler.
869 However, if the window is unmapped when the reparent event occurs,
870 the window manager never sees it because an unmap event is not sent
871 to an already unmapped window.
872 */
873
874 /* we don't want the reparent event, put it back on the stack for the
875 X server to deal with after we unmanage the window */
876 XPutBackEvent(ob_display, e);
877
878 client_unmanage(client);
879 break;
880 case MapRequest:
881 ob_debug("MapRequest for 0x%lx\n", client->window);
882 if (!client->iconic) break; /* this normally doesn't happen, but if it
883 does, we don't want it! */
884 if (screen_showing_desktop)
885 screen_show_desktop(FALSE);
886 client_iconify(client, FALSE, TRUE);
887 if (!client->frame->visible)
888 /* if its not visible still, then don't mess with it */
889 break;
890 if (client->shaded)
891 client_shade(client, FALSE);
892 client_focus(client);
893 stacking_raise(CLIENT_AS_WINDOW(client));
894 break;
895 case ClientMessage:
896 /* validate cuz we query stuff off the client here */
897 if (!client_validate(client)) break;
898
899 if (e->xclient.format != 32) return;
900
901 msgtype = e->xclient.message_type;
902 if (msgtype == prop_atoms.wm_change_state) {
903 /* compress changes into a single change */
904 while (XCheckTypedWindowEvent(ob_display, client->window,
905 e->type, &ce)) {
906 /* XXX: it would be nice to compress ALL messages of a
907 type, not just messages in a row without other
908 message types between. */
909 if (ce.xclient.message_type != msgtype) {
910 XPutBackEvent(ob_display, &ce);
911 break;
912 }
913 e->xclient = ce.xclient;
914 }
915 client_set_wm_state(client, e->xclient.data.l[0]);
916 } else if (msgtype == prop_atoms.net_wm_desktop) {
917 /* compress changes into a single change */
918 while (XCheckTypedWindowEvent(ob_display, client->window,
919 e->type, &ce)) {
920 /* XXX: it would be nice to compress ALL messages of a
921 type, not just messages in a row without other
922 message types between. */
923 if (ce.xclient.message_type != msgtype) {
924 XPutBackEvent(ob_display, &ce);
925 break;
926 }
927 e->xclient = ce.xclient;
928 }
929 if ((unsigned)e->xclient.data.l[0] < screen_num_desktops ||
930 (unsigned)e->xclient.data.l[0] == DESKTOP_ALL)
931 client_set_desktop(client, (unsigned)e->xclient.data.l[0],
932 FALSE);
933 } else if (msgtype == prop_atoms.net_wm_state) {
934 /* can't compress these */
935 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
936 (e->xclient.data.l[0] == 0 ? "Remove" :
937 e->xclient.data.l[0] == 1 ? "Add" :
938 e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
939 e->xclient.data.l[1], e->xclient.data.l[2],
940 client->window);
941 client_set_state(client, e->xclient.data.l[0],
942 e->xclient.data.l[1], e->xclient.data.l[2]);
943 } else if (msgtype == prop_atoms.net_close_window) {
944 ob_debug("net_close_window for 0x%lx\n", client->window);
945 client_close(client);
946 } else if (msgtype == prop_atoms.net_active_window) {
947 ob_debug("net_active_window for 0x%lx\n", client->window);
948 client_activate(client, FALSE);
949 } else if (msgtype == prop_atoms.net_wm_moveresize) {
950 ob_debug("net_wm_moveresize for 0x%lx\n", client->window);
951 if ((Atom)e->xclient.data.l[2] ==
952 prop_atoms.net_wm_moveresize_size_topleft ||
953 (Atom)e->xclient.data.l[2] ==
954 prop_atoms.net_wm_moveresize_size_top ||
955 (Atom)e->xclient.data.l[2] ==
956 prop_atoms.net_wm_moveresize_size_topright ||
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_right ||
961 (Atom)e->xclient.data.l[2] ==
962 prop_atoms.net_wm_moveresize_size_bottomright ||
963 (Atom)e->xclient.data.l[2] ==
964 prop_atoms.net_wm_moveresize_size_bottom ||
965 (Atom)e->xclient.data.l[2] ==
966 prop_atoms.net_wm_moveresize_size_bottomleft ||
967 (Atom)e->xclient.data.l[2] ==
968 prop_atoms.net_wm_moveresize_size_left ||
969 (Atom)e->xclient.data.l[2] ==
970 prop_atoms.net_wm_moveresize_move ||
971 (Atom)e->xclient.data.l[2] ==
972 prop_atoms.net_wm_moveresize_size_keyboard ||
973 (Atom)e->xclient.data.l[2] ==
974 prop_atoms.net_wm_moveresize_move_keyboard) {
975
976 moveresize_start(client, e->xclient.data.l[0],
977 e->xclient.data.l[1], e->xclient.data.l[3],
978 e->xclient.data.l[2]);
979 }
980 } else if (msgtype == prop_atoms.net_moveresize_window) {
981 int oldg = client->gravity;
982 int tmpg, x, y, w, h;
983
984 if (e->xclient.data.l[0] & 0xff)
985 tmpg = e->xclient.data.l[0] & 0xff;
986 else
987 tmpg = oldg;
988
989 if (e->xclient.data.l[0] & 1 << 8)
990 x = e->xclient.data.l[1];
991 else
992 x = client->area.x;
993 if (e->xclient.data.l[0] & 1 << 9)
994 y = e->xclient.data.l[2];
995 else
996 y = client->area.y;
997 if (e->xclient.data.l[0] & 1 << 10)
998 w = e->xclient.data.l[3];
999 else
1000 w = client->area.width;
1001 if (e->xclient.data.l[0] & 1 << 11)
1002 h = e->xclient.data.l[4];
1003 else
1004 h = client->area.height;
1005 client->gravity = tmpg;
1006
1007 {
1008 int newx = x;
1009 int newy = y;
1010 int fw = w +
1011 client->frame->size.left + client->frame->size.right;
1012 int fh = h +
1013 client->frame->size.top + client->frame->size.bottom;
1014 client_find_onscreen(client, &newx, &newy, fw, fh,
1015 client_normal(client));
1016 if (e->xclient.data.l[0] & 1 << 8)
1017 x = newx;
1018 if (e->xclient.data.l[0] & 1 << 9)
1019 y = newy;
1020 }
1021
1022 client_configure(client, OB_CORNER_TOPLEFT,
1023 x, y, w, h, FALSE, TRUE);
1024
1025 client->gravity = oldg;
1026 }
1027 break;
1028 case PropertyNotify:
1029 /* validate cuz we query stuff off the client here */
1030 if (!client_validate(client)) break;
1031
1032 /* compress changes to a single property into a single change */
1033 while (XCheckTypedWindowEvent(ob_display, client->window,
1034 e->type, &ce)) {
1035 Atom a, b;
1036
1037 /* XXX: it would be nice to compress ALL changes to a property,
1038 not just changes in a row without other props between. */
1039
1040 a = ce.xproperty.atom;
1041 b = e->xproperty.atom;
1042
1043 if (a == b)
1044 continue;
1045 if ((a == prop_atoms.net_wm_name ||
1046 a == prop_atoms.wm_name ||
1047 a == prop_atoms.net_wm_icon_name ||
1048 a == prop_atoms.wm_icon_name)
1049 &&
1050 (b == prop_atoms.net_wm_name ||
1051 b == prop_atoms.wm_name ||
1052 b == prop_atoms.net_wm_icon_name ||
1053 b == prop_atoms.wm_icon_name)) {
1054 continue;
1055 }
1056 if ((a == prop_atoms.net_wm_icon ||
1057 a == prop_atoms.kwm_win_icon)
1058 &&
1059 (b == prop_atoms.net_wm_icon ||
1060 b == prop_atoms.kwm_win_icon))
1061 continue;
1062
1063 XPutBackEvent(ob_display, &ce);
1064 break;
1065 }
1066
1067 msgtype = e->xproperty.atom;
1068 if (msgtype == XA_WM_NORMAL_HINTS) {
1069 client_update_normal_hints(client);
1070 /* normal hints can make a window non-resizable */
1071 client_setup_decor_and_functions(client);
1072 } else if (msgtype == XA_WM_HINTS) {
1073 client_update_wmhints(client);
1074 } else if (msgtype == XA_WM_TRANSIENT_FOR) {
1075 client_update_transient_for(client);
1076 client_get_type(client);
1077 /* type may have changed, so update the layer */
1078 client_calc_layer(client);
1079 client_setup_decor_and_functions(client);
1080 } else if (msgtype == prop_atoms.net_wm_name ||
1081 msgtype == prop_atoms.wm_name ||
1082 msgtype == prop_atoms.net_wm_icon_name ||
1083 msgtype == prop_atoms.wm_icon_name) {
1084 client_update_title(client);
1085 } else if (msgtype == prop_atoms.wm_class) {
1086 client_update_class(client);
1087 } else if (msgtype == prop_atoms.wm_protocols) {
1088 client_update_protocols(client);
1089 client_setup_decor_and_functions(client);
1090 }
1091 else if (msgtype == prop_atoms.net_wm_strut) {
1092 client_update_strut(client);
1093 }
1094 else if (msgtype == prop_atoms.net_wm_icon ||
1095 msgtype == prop_atoms.kwm_win_icon) {
1096 client_update_icons(client);
1097 }
1098 else if (msgtype == prop_atoms.sm_client_id) {
1099 client_update_sm_client_id(client);
1100 }
1101 default:
1102 ;
1103 #ifdef SHAPE
1104 if (extensions_shape && e->type == extensions_shape_event_basep) {
1105 client->shaped = ((XShapeEvent*)e)->shaped;
1106 frame_adjust_shape(client->frame);
1107 }
1108 #endif
1109 }
1110 }
1111
1112 static void event_handle_dock(ObDock *s, XEvent *e)
1113 {
1114 switch (e->type) {
1115 case ButtonPress:
1116 stacking_raise(DOCK_AS_WINDOW(s));
1117 break;
1118 case EnterNotify:
1119 dock_hide(FALSE);
1120 break;
1121 case LeaveNotify:
1122 dock_hide(TRUE);
1123 break;
1124 }
1125 }
1126
1127 static void event_handle_dockapp(ObDockApp *app, XEvent *e)
1128 {
1129 switch (e->type) {
1130 case MotionNotify:
1131 dock_app_drag(app, &e->xmotion);
1132 break;
1133 case UnmapNotify:
1134 if (app->ignore_unmaps) {
1135 app->ignore_unmaps--;
1136 break;
1137 }
1138 dock_remove(app, TRUE);
1139 break;
1140 case DestroyNotify:
1141 dock_remove(app, FALSE);
1142 break;
1143 case ReparentNotify:
1144 dock_remove(app, FALSE);
1145 break;
1146 case ConfigureNotify:
1147 dock_app_configure(app, e->xconfigure.width, e->xconfigure.height);
1148 break;
1149 }
1150 }
1151
1152 ObMenuFrame* find_active_menu()
1153 {
1154 GList *it;
1155 ObMenuFrame *f;
1156
1157 for (it = menu_frame_visible; it; it = g_list_next(it)) {
1158 f = it->data;
1159 if (f->selected)
1160 break;
1161 }
1162 return it ? it->data : NULL;
1163 }
1164
1165 static void event_handle_menu(XEvent *ev)
1166 {
1167 ObMenuFrame *f;
1168 ObMenuEntryFrame *e;
1169
1170 switch (ev->type) {
1171 case ButtonRelease:
1172 if ((e = menu_entry_frame_under(ev->xbutton.x_root,
1173 ev->xbutton.y_root)))
1174 menu_entry_frame_execute(e, ev->xbutton.state);
1175 else if (menu_can_hide)
1176 menu_frame_hide_all();
1177 break;
1178 case MotionNotify:
1179 if ((f = menu_frame_under(ev->xmotion.x_root,
1180 ev->xmotion.y_root))) {
1181 menu_frame_move_on_screen(f);
1182 if ((e = menu_entry_frame_under(ev->xmotion.x_root,
1183 ev->xmotion.y_root)))
1184 menu_frame_select(f, e);
1185 }
1186 {
1187 ObMenuFrame *a;
1188
1189 a = find_active_menu();
1190 if (a && a != f &&
1191 a->selected->entry->type != OB_MENU_ENTRY_TYPE_SUBMENU)
1192 {
1193 menu_frame_select(a, NULL);
1194 }
1195 }
1196 break;
1197 case KeyPress:
1198 if (ev->xkey.keycode == ob_keycode(OB_KEY_ESCAPE))
1199 menu_frame_hide_all();
1200 else if (ev->xkey.keycode == ob_keycode(OB_KEY_RETURN)) {
1201 ObMenuFrame *f;
1202 if ((f = find_active_menu()))
1203 menu_entry_frame_execute(f->selected, ev->xkey.state);
1204 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_LEFT)) {
1205 ObMenuFrame *f;
1206 if ((f = find_active_menu()) && f->parent)
1207 menu_frame_select(f, NULL);
1208 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_RIGHT)) {
1209 ObMenuFrame *f;
1210 if ((f = find_active_menu()) && f->child)
1211 menu_frame_select_next(f->child);
1212 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_UP)) {
1213 ObMenuFrame *f;
1214 if ((f = find_active_menu()))
1215 menu_frame_select_previous(f);
1216 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_DOWN)) {
1217 ObMenuFrame *f;
1218 if ((f = find_active_menu()))
1219 menu_frame_select_next(f);
1220 }
1221 break;
1222 }
1223 }
1224
1225 static gboolean menu_hide_delay_func(gpointer data)
1226 {
1227 menu_can_hide = TRUE;
1228 return FALSE; /* no repeat */
1229 }
1230
1231 static gboolean focus_delay_func(gpointer data)
1232 {
1233 client_focus(focus_delay_client);
1234 return FALSE; /* no repeat */
1235 }
1236
1237 static void focus_delay_client_dest(gpointer data)
1238 {
1239 ObClient *c = data;
1240 if (c == focus_delay_client) {
1241 ob_main_loop_timeout_remove_data(ob_main_loop, focus_delay_func,
1242 focus_delay_client);
1243 focus_delay_client = NULL;
1244 }
1245 }
This page took 0.087634 seconds and 5 git commands to generate.