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