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