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