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