]> Dogcows Code - chaz/openbox/blob - openbox/event.c
handle multiple focusin/out in one batch
[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 unsigned int NumLockMask;
77 /*! The value of the mask for the ScrollLock modifier */
78 unsigned int ScrollLockMask;
79 /*! The key codes for the modifier keys */
80 static XModifierKeymap *modmap;
81 /*! Table of the constant modifier masks */
82 static const int mask_table[] = {
83 ShiftMask, LockMask, ControlMask, Mod1Mask,
84 Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask
85 };
86 static int 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(int 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 int 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 int r;
364
365 /* is focus anywhere valid? */
366 XGetInputFocus(ob_display, &w, &r);
367 if (!w || w == RootWindow(ob_display, ob_screen))
368 focus_fallback(OB_FOCUS_FALLBACK_NOFOCUS);
369 }
370 last = focus_client;
371 }
372
373 focus_in = focus_out = NULL;
374 }
375
376 static void event_process(const XEvent *ec, gpointer data)
377 {
378 Window window;
379 ObGroup *group = NULL;
380 ObClient *client = NULL;
381 ObDock *dock = NULL;
382 ObDockApp *dockapp = NULL;
383 ObWindow *obwin = NULL;
384 XEvent ee, *e;
385
386 /* make a copy we can mangle */
387 ee = *ec;
388 e = &ee;
389
390 window = event_get_window(e);
391 if (!(e->type == PropertyNotify &&
392 (group = g_hash_table_lookup(group_map, &window))))
393 if ((obwin = g_hash_table_lookup(window_map, &window))) {
394 switch (obwin->type) {
395 case Window_Dock:
396 dock = WINDOW_AS_DOCK(obwin);
397 break;
398 case Window_DockApp:
399 dockapp = WINDOW_AS_DOCKAPP(obwin);
400 break;
401 case Window_Client:
402 client = WINDOW_AS_CLIENT(obwin);
403 break;
404 case Window_Menu:
405 case Window_Internal:
406 /* not to be used for events */
407 g_assert_not_reached();
408 break;
409 }
410 }
411
412 event_set_lasttime(e);
413 event_hack_mods(e);
414 if (event_ignore(e, client))
415 return;
416
417 /* deal with it in the kernel */
418 if (group)
419 event_handle_group(group, e);
420 else if (client)
421 event_handle_client(client, e);
422 else if (dockapp)
423 event_handle_dockapp(dockapp, e);
424 else if (dock)
425 event_handle_dock(dock, e);
426 else if (window == RootWindow(ob_display, ob_screen))
427 event_handle_root(e);
428 else if (e->type == MapRequest)
429 client_manage(window);
430 else if (e->type == ConfigureRequest) {
431 /* unhandled configure requests must be used to configure the
432 window directly */
433 XWindowChanges xwc;
434
435 xwc.x = e->xconfigurerequest.x;
436 xwc.y = e->xconfigurerequest.y;
437 xwc.width = e->xconfigurerequest.width;
438 xwc.height = e->xconfigurerequest.height;
439 xwc.border_width = e->xconfigurerequest.border_width;
440 xwc.sibling = e->xconfigurerequest.above;
441 xwc.stack_mode = e->xconfigurerequest.detail;
442
443 /* we are not to be held responsible if someone sends us an
444 invalid request! */
445 xerror_set_ignore(TRUE);
446 XConfigureWindow(ob_display, window,
447 e->xconfigurerequest.value_mask, &xwc);
448 xerror_set_ignore(FALSE);
449 }
450
451 /* user input (action-bound) events */
452 if (e->type == ButtonPress || e->type == ButtonRelease ||
453 e->type == MotionNotify || e->type == KeyPress ||
454 e->type == KeyRelease)
455 {
456 if (menu_frame_visible)
457 event_handle_menu(e);
458 else {
459 if (!keyboard_process_interactive_grab(e, &client)) {
460 if (moveresize_in_progress) {
461 moveresize_event(e);
462
463 /* make further actions work on the client being
464 moved/resized */
465 client = moveresize_client;
466 }
467
468 menu_can_hide = FALSE;
469 ob_main_loop_timeout_add(ob_main_loop,
470 G_USEC_PER_SEC / 4,
471 menu_hide_delay_func,
472 NULL, NULL);
473
474 if (e->type == ButtonPress || e->type == ButtonRelease ||
475 e->type == MotionNotify)
476 mouse_event(client, e);
477 else if (e->type == KeyPress)
478 keyboard_event((focus_cycle_target ? focus_cycle_target :
479 (focus_hilite ? focus_hilite : client)),
480 e);
481 }
482 }
483 }
484 }
485
486 static void event_handle_root(XEvent *e)
487 {
488 Atom msgtype;
489
490 switch(e->type) {
491 case SelectionClear:
492 ob_debug("Another WM has requested to replace us. Exiting.\n");
493 ob_exit(0);
494 break;
495
496 case ClientMessage:
497 if (e->xclient.format != 32) break;
498
499 msgtype = e->xclient.message_type;
500 if (msgtype == prop_atoms.net_current_desktop) {
501 unsigned int d = e->xclient.data.l[0];
502 if (d < screen_num_desktops)
503 screen_set_desktop(d);
504 } else if (msgtype == prop_atoms.net_number_of_desktops) {
505 unsigned int d = e->xclient.data.l[0];
506 if (d > 0)
507 screen_set_num_desktops(d);
508 } else if (msgtype == prop_atoms.net_showing_desktop) {
509 screen_show_desktop(e->xclient.data.l[0] != 0);
510 }
511 break;
512 case PropertyNotify:
513 if (e->xproperty.atom == prop_atoms.net_desktop_names)
514 screen_update_desktop_names();
515 else if (e->xproperty.atom == prop_atoms.net_desktop_layout)
516 screen_update_layout();
517 break;
518 case ConfigureNotify:
519 #ifdef XRANDR
520 XRRUpdateConfiguration(e);
521 #endif
522 screen_resize();
523 break;
524 default:
525 ;
526 #ifdef VIDMODE
527 if (extensions_vidmode && e->type == extensions_vidmode_event_basep) {
528 ob_debug("VIDMODE EVENT\n");
529 }
530 #endif
531 }
532 }
533
534 static void event_handle_group(ObGroup *group, XEvent *e)
535 {
536 GSList *it;
537
538 g_assert(e->type == PropertyNotify);
539
540 for (it = group->members; it; it = g_slist_next(it))
541 event_handle_client(it->data, e);
542 }
543
544 void event_enter_client(ObClient *client)
545 {
546 g_assert(config_focus_follow);
547
548 if (client_normal(client) && client_can_focus(client)) {
549 if (config_focus_delay) {
550 ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
551 ob_main_loop_timeout_add(ob_main_loop,
552 config_focus_delay,
553 focus_delay_func,
554 client, NULL);
555 } else
556 focus_delay_func(client);
557 }
558 }
559
560 static void event_handle_client(ObClient *client, XEvent *e)
561 {
562 XEvent ce;
563 Atom msgtype;
564 int i=0;
565 ObFrameContext con;
566
567 switch (e->type) {
568 case VisibilityNotify:
569 client->frame->obscured = e->xvisibility.state != VisibilityUnobscured;
570 break;
571 case ButtonPress:
572 case ButtonRelease:
573 /* Wheel buttons don't draw because they are an instant click, so it
574 is a waste of resources to go drawing it. */
575 if (!(e->xbutton.button == 4 || e->xbutton.button == 5)) {
576 con = frame_context(client, e->xbutton.window);
577 con = mouse_button_frame_context(con, e->xbutton.button);
578 switch (con) {
579 case OB_FRAME_CONTEXT_MAXIMIZE:
580 client->frame->max_press = (e->type == ButtonPress);
581 framerender_frame(client->frame);
582 break;
583 case OB_FRAME_CONTEXT_CLOSE:
584 client->frame->close_press = (e->type == ButtonPress);
585 framerender_frame(client->frame);
586 break;
587 case OB_FRAME_CONTEXT_ICONIFY:
588 client->frame->iconify_press = (e->type == ButtonPress);
589 framerender_frame(client->frame);
590 break;
591 case OB_FRAME_CONTEXT_ALLDESKTOPS:
592 client->frame->desk_press = (e->type == ButtonPress);
593 framerender_frame(client->frame);
594 break;
595 case OB_FRAME_CONTEXT_SHADE:
596 client->frame->shade_press = (e->type == ButtonPress);
597 framerender_frame(client->frame);
598 break;
599 default:
600 /* nothing changes with clicks for any other contexts */
601 break;
602 }
603 }
604 break;
605 case FocusIn:
606 #ifdef DEBUG_FOCUS
607 ob_debug("FocusIn on client for %lx (client %lx) mode %d detail %d\n",
608 e->xfocus.window, client->window,
609 e->xfocus.mode, e->xfocus.detail);
610 #endif
611 focus_in = client;
612 if (focus_out == client)
613 focus_out = NULL;
614 break;
615 case FocusOut:
616 #ifdef DEBUG_FOCUS
617 ob_debug("FocusOut on client for %lx (client %lx) mode %d detail %d\n",
618 e->xfocus.window, client->window,
619 e->xfocus.mode, e->xfocus.detail);
620 #endif
621 g_message("hilite %x client %x", focus_hilite, client);
622 if (focus_hilite == client)
623 focus_out = client;
624 if (focus_in == client)
625 focus_in = NULL;
626 break;
627 case LeaveNotify:
628 con = frame_context(client, e->xcrossing.window);
629 switch (con) {
630 case OB_FRAME_CONTEXT_MAXIMIZE:
631 client->frame->max_hover = FALSE;
632 frame_adjust_state(client->frame);
633 break;
634 case OB_FRAME_CONTEXT_ALLDESKTOPS:
635 client->frame->desk_hover = FALSE;
636 frame_adjust_state(client->frame);
637 break;
638 case OB_FRAME_CONTEXT_SHADE:
639 client->frame->shade_hover = FALSE;
640 frame_adjust_state(client->frame);
641 break;
642 case OB_FRAME_CONTEXT_ICONIFY:
643 client->frame->iconify_hover = FALSE;
644 frame_adjust_state(client->frame);
645 break;
646 case OB_FRAME_CONTEXT_CLOSE:
647 client->frame->close_hover = FALSE;
648 frame_adjust_state(client->frame);
649 break;
650 case OB_FRAME_CONTEXT_FRAME:
651 if (config_focus_follow && config_focus_delay)
652 ob_main_loop_timeout_remove_data(ob_main_loop,
653 focus_delay_func,
654 client);
655 break;
656 default:
657 break;
658 }
659 break;
660 case EnterNotify:
661 {
662 gboolean nofocus = FALSE;
663
664 if (ignore_enter_focus) {
665 ignore_enter_focus--;
666 nofocus = TRUE;
667 }
668
669 con = frame_context(client, e->xcrossing.window);
670 switch (con) {
671 case OB_FRAME_CONTEXT_MAXIMIZE:
672 client->frame->max_hover = TRUE;
673 frame_adjust_state(client->frame);
674 break;
675 case OB_FRAME_CONTEXT_ALLDESKTOPS:
676 client->frame->desk_hover = TRUE;
677 frame_adjust_state(client->frame);
678 break;
679 case OB_FRAME_CONTEXT_SHADE:
680 client->frame->shade_hover = TRUE;
681 frame_adjust_state(client->frame);
682 break;
683 case OB_FRAME_CONTEXT_ICONIFY:
684 client->frame->iconify_hover = TRUE;
685 frame_adjust_state(client->frame);
686 break;
687 case OB_FRAME_CONTEXT_CLOSE:
688 client->frame->close_hover = TRUE;
689 frame_adjust_state(client->frame);
690 break;
691 case OB_FRAME_CONTEXT_FRAME:
692 if (e->xcrossing.mode == NotifyGrab ||
693 e->xcrossing.mode == NotifyUngrab)
694 {
695 #ifdef DEBUG_FOCUS
696 ob_debug("%sNotify mode %d detail %d on %lx IGNORED\n",
697 (e->type == EnterNotify ? "Enter" : "Leave"),
698 e->xcrossing.mode,
699 e->xcrossing.detail, client?client->window:0);
700 #endif
701 } else {
702 #ifdef DEBUG_FOCUS
703 ob_debug("%sNotify mode %d detail %d on %lx, "
704 "focusing window: %d\n",
705 (e->type == EnterNotify ? "Enter" : "Leave"),
706 e->xcrossing.mode,
707 e->xcrossing.detail, (client?client->window:0),
708 !nofocus);
709 #endif
710 if (!nofocus && config_focus_follow)
711 event_enter_client(client);
712 }
713 break;
714 default:
715 break;
716 }
717 break;
718 }
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 client_lower(client);
805 break;
806
807 case Above:
808 case TopIf:
809 default:
810 client_raise(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 client_activate(client, FALSE);
847 break;
848 case ClientMessage:
849 /* validate cuz we query stuff off the client here */
850 if (!client_validate(client)) break;
851
852 if (e->xclient.format != 32) return;
853
854 msgtype = e->xclient.message_type;
855 if (msgtype == prop_atoms.wm_change_state) {
856 /* compress changes into a single change */
857 while (XCheckTypedWindowEvent(ob_display, client->window,
858 e->type, &ce)) {
859 /* XXX: it would be nice to compress ALL messages of a
860 type, not just messages in a row without other
861 message types between. */
862 if (ce.xclient.message_type != msgtype) {
863 XPutBackEvent(ob_display, &ce);
864 break;
865 }
866 e->xclient = ce.xclient;
867 }
868 client_set_wm_state(client, e->xclient.data.l[0]);
869 } else if (msgtype == prop_atoms.net_wm_desktop) {
870 /* compress changes into a single change */
871 while (XCheckTypedWindowEvent(ob_display, client->window,
872 e->type, &ce)) {
873 /* XXX: it would be nice to compress ALL messages of a
874 type, not just messages in a row without other
875 message types between. */
876 if (ce.xclient.message_type != msgtype) {
877 XPutBackEvent(ob_display, &ce);
878 break;
879 }
880 e->xclient = ce.xclient;
881 }
882 if ((unsigned)e->xclient.data.l[0] < screen_num_desktops ||
883 (unsigned)e->xclient.data.l[0] == DESKTOP_ALL)
884 client_set_desktop(client, (unsigned)e->xclient.data.l[0],
885 FALSE);
886 } else if (msgtype == prop_atoms.net_wm_state) {
887 /* can't compress these */
888 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
889 (e->xclient.data.l[0] == 0 ? "Remove" :
890 e->xclient.data.l[0] == 1 ? "Add" :
891 e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
892 e->xclient.data.l[1], e->xclient.data.l[2],
893 client->window);
894 client_set_state(client, e->xclient.data.l[0],
895 e->xclient.data.l[1], e->xclient.data.l[2]);
896 } else if (msgtype == prop_atoms.net_close_window) {
897 ob_debug("net_close_window for 0x%lx\n", client->window);
898 client_close(client);
899 } else if (msgtype == prop_atoms.net_active_window) {
900 ob_debug("net_active_window for 0x%lx\n", client->window);
901 client_activate(client, FALSE);
902 } else if (msgtype == prop_atoms.net_wm_moveresize) {
903 ob_debug("net_wm_moveresize for 0x%lx\n", client->window);
904 if ((Atom)e->xclient.data.l[2] ==
905 prop_atoms.net_wm_moveresize_size_topleft ||
906 (Atom)e->xclient.data.l[2] ==
907 prop_atoms.net_wm_moveresize_size_top ||
908 (Atom)e->xclient.data.l[2] ==
909 prop_atoms.net_wm_moveresize_size_topright ||
910 (Atom)e->xclient.data.l[2] ==
911 prop_atoms.net_wm_moveresize_size_right ||
912 (Atom)e->xclient.data.l[2] ==
913 prop_atoms.net_wm_moveresize_size_right ||
914 (Atom)e->xclient.data.l[2] ==
915 prop_atoms.net_wm_moveresize_size_bottomright ||
916 (Atom)e->xclient.data.l[2] ==
917 prop_atoms.net_wm_moveresize_size_bottom ||
918 (Atom)e->xclient.data.l[2] ==
919 prop_atoms.net_wm_moveresize_size_bottomleft ||
920 (Atom)e->xclient.data.l[2] ==
921 prop_atoms.net_wm_moveresize_size_left ||
922 (Atom)e->xclient.data.l[2] ==
923 prop_atoms.net_wm_moveresize_move ||
924 (Atom)e->xclient.data.l[2] ==
925 prop_atoms.net_wm_moveresize_size_keyboard ||
926 (Atom)e->xclient.data.l[2] ==
927 prop_atoms.net_wm_moveresize_move_keyboard) {
928
929 moveresize_start(client, e->xclient.data.l[0],
930 e->xclient.data.l[1], e->xclient.data.l[3],
931 e->xclient.data.l[2]);
932 }
933 } else if (msgtype == prop_atoms.net_moveresize_window) {
934 int oldg = client->gravity;
935 int tmpg, x, y, w, h;
936
937 if (e->xclient.data.l[0] & 0xff)
938 tmpg = e->xclient.data.l[0] & 0xff;
939 else
940 tmpg = oldg;
941
942 if (e->xclient.data.l[0] & 1 << 8)
943 x = e->xclient.data.l[1];
944 else
945 x = client->area.x;
946 if (e->xclient.data.l[0] & 1 << 9)
947 y = e->xclient.data.l[2];
948 else
949 y = client->area.y;
950 if (e->xclient.data.l[0] & 1 << 10)
951 w = e->xclient.data.l[3];
952 else
953 w = client->area.width;
954 if (e->xclient.data.l[0] & 1 << 11)
955 h = e->xclient.data.l[4];
956 else
957 h = client->area.height;
958 client->gravity = tmpg;
959
960 {
961 int newx = x;
962 int newy = y;
963 int fw = w +
964 client->frame->size.left + client->frame->size.right;
965 int fh = h +
966 client->frame->size.top + client->frame->size.bottom;
967 client_find_onscreen(client, &newx, &newy, fw, fh,
968 client_normal(client));
969 if (e->xclient.data.l[0] & 1 << 8)
970 x = newx;
971 if (e->xclient.data.l[0] & 1 << 9)
972 y = newy;
973 }
974
975 client_configure(client, OB_CORNER_TOPLEFT,
976 x, y, w, h, FALSE, TRUE);
977
978 client->gravity = oldg;
979 }
980 break;
981 case PropertyNotify:
982 /* validate cuz we query stuff off the client here */
983 if (!client_validate(client)) break;
984
985 /* compress changes to a single property into a single change */
986 while (XCheckTypedWindowEvent(ob_display, client->window,
987 e->type, &ce)) {
988 Atom a, b;
989
990 /* XXX: it would be nice to compress ALL changes to a property,
991 not just changes in a row without other props between. */
992
993 a = ce.xproperty.atom;
994 b = e->xproperty.atom;
995
996 if (a == b)
997 continue;
998 if ((a == prop_atoms.net_wm_name ||
999 a == prop_atoms.wm_name ||
1000 a == prop_atoms.net_wm_icon_name ||
1001 a == prop_atoms.wm_icon_name)
1002 &&
1003 (b == prop_atoms.net_wm_name ||
1004 b == prop_atoms.wm_name ||
1005 b == prop_atoms.net_wm_icon_name ||
1006 b == prop_atoms.wm_icon_name)) {
1007 continue;
1008 }
1009 if ((a == prop_atoms.net_wm_icon ||
1010 a == prop_atoms.kwm_win_icon)
1011 &&
1012 (b == prop_atoms.net_wm_icon ||
1013 b == prop_atoms.kwm_win_icon))
1014 continue;
1015
1016 XPutBackEvent(ob_display, &ce);
1017 break;
1018 }
1019
1020 msgtype = e->xproperty.atom;
1021 if (msgtype == XA_WM_NORMAL_HINTS) {
1022 client_update_normal_hints(client);
1023 /* normal hints can make a window non-resizable */
1024 client_setup_decor_and_functions(client);
1025 } else if (msgtype == XA_WM_HINTS) {
1026 client_update_wmhints(client);
1027 } else if (msgtype == XA_WM_TRANSIENT_FOR) {
1028 client_update_transient_for(client);
1029 client_get_type(client);
1030 /* type may have changed, so update the layer */
1031 client_calc_layer(client);
1032 client_setup_decor_and_functions(client);
1033 } else if (msgtype == prop_atoms.net_wm_name ||
1034 msgtype == prop_atoms.wm_name ||
1035 msgtype == prop_atoms.net_wm_icon_name ||
1036 msgtype == prop_atoms.wm_icon_name) {
1037 client_update_title(client);
1038 } else if (msgtype == prop_atoms.wm_class) {
1039 client_update_class(client);
1040 } else if (msgtype == prop_atoms.wm_protocols) {
1041 client_update_protocols(client);
1042 client_setup_decor_and_functions(client);
1043 }
1044 else if (msgtype == prop_atoms.net_wm_strut) {
1045 client_update_strut(client);
1046 }
1047 else if (msgtype == prop_atoms.net_wm_icon ||
1048 msgtype == prop_atoms.kwm_win_icon) {
1049 client_update_icons(client);
1050 }
1051 else if (msgtype == prop_atoms.sm_client_id) {
1052 client_update_sm_client_id(client);
1053 }
1054 default:
1055 ;
1056 #ifdef SHAPE
1057 if (extensions_shape && e->type == extensions_shape_event_basep) {
1058 client->shaped = ((XShapeEvent*)e)->shaped;
1059 frame_adjust_shape(client->frame);
1060 }
1061 #endif
1062 }
1063 }
1064
1065 static void event_handle_dock(ObDock *s, XEvent *e)
1066 {
1067 switch (e->type) {
1068 case ButtonPress:
1069 stacking_raise(DOCK_AS_WINDOW(s));
1070 break;
1071 case EnterNotify:
1072 dock_hide(FALSE);
1073 break;
1074 case LeaveNotify:
1075 dock_hide(TRUE);
1076 break;
1077 }
1078 }
1079
1080 static void event_handle_dockapp(ObDockApp *app, XEvent *e)
1081 {
1082 switch (e->type) {
1083 case MotionNotify:
1084 dock_app_drag(app, &e->xmotion);
1085 break;
1086 case UnmapNotify:
1087 if (app->ignore_unmaps) {
1088 app->ignore_unmaps--;
1089 break;
1090 }
1091 dock_remove(app, TRUE);
1092 break;
1093 case DestroyNotify:
1094 dock_remove(app, FALSE);
1095 break;
1096 case ReparentNotify:
1097 dock_remove(app, FALSE);
1098 break;
1099 case ConfigureNotify:
1100 dock_app_configure(app, e->xconfigure.width, e->xconfigure.height);
1101 break;
1102 }
1103 }
1104
1105 ObMenuFrame* find_active_menu()
1106 {
1107 GList *it;
1108 ObMenuFrame *ret = NULL;
1109
1110 for (it = menu_frame_visible; it; it = g_list_next(it)) {
1111 ret = it->data;
1112 if (ret->selected)
1113 break;
1114 ret = NULL;
1115 }
1116 return ret;
1117 }
1118
1119 ObMenuFrame* find_active_or_last_menu()
1120 {
1121 ObMenuFrame *ret = NULL;
1122
1123 ret = find_active_menu();
1124 if (!ret && menu_frame_visible)
1125 ret = menu_frame_visible->data;
1126 return ret;
1127 }
1128
1129 static void event_handle_menu(XEvent *ev)
1130 {
1131 ObMenuFrame *f;
1132 ObMenuEntryFrame *e;
1133
1134 switch (ev->type) {
1135 case ButtonRelease:
1136 if (menu_can_hide) {
1137 if ((e = menu_entry_frame_under(ev->xbutton.x_root,
1138 ev->xbutton.y_root)))
1139 menu_entry_frame_execute(e, ev->xbutton.state);
1140 else
1141 menu_frame_hide_all();
1142 }
1143 break;
1144 case MotionNotify:
1145 if ((f = menu_frame_under(ev->xmotion.x_root,
1146 ev->xmotion.y_root))) {
1147 menu_frame_move_on_screen(f);
1148 if ((e = menu_entry_frame_under(ev->xmotion.x_root,
1149 ev->xmotion.y_root)))
1150 menu_frame_select(f, e);
1151 }
1152 {
1153 ObMenuFrame *a;
1154
1155 a = find_active_menu();
1156 if (a && a != f &&
1157 a->selected->entry->type != OB_MENU_ENTRY_TYPE_SUBMENU)
1158 {
1159 menu_frame_select(a, NULL);
1160 }
1161 }
1162 break;
1163 case KeyPress:
1164 if (ev->xkey.keycode == ob_keycode(OB_KEY_ESCAPE))
1165 menu_frame_hide_all();
1166 else if (ev->xkey.keycode == ob_keycode(OB_KEY_RETURN)) {
1167 ObMenuFrame *f;
1168 if ((f = find_active_menu()))
1169 menu_entry_frame_execute(f->selected, ev->xkey.state);
1170 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_LEFT)) {
1171 ObMenuFrame *f;
1172 if ((f = find_active_or_last_menu()) && f->parent)
1173 menu_frame_select(f, NULL);
1174 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_RIGHT)) {
1175 ObMenuFrame *f;
1176 if ((f = find_active_or_last_menu()) && f->child)
1177 menu_frame_select_next(f->child);
1178 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_UP)) {
1179 ObMenuFrame *f;
1180 if ((f = find_active_or_last_menu()))
1181 menu_frame_select_previous(f);
1182 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_DOWN)) {
1183 ObMenuFrame *f;
1184 if ((f = find_active_or_last_menu()))
1185 menu_frame_select_next(f);
1186 }
1187 break;
1188 }
1189 }
1190
1191 static gboolean menu_hide_delay_func(gpointer data)
1192 {
1193 menu_can_hide = TRUE;
1194 return FALSE; /* no repeat */
1195 }
1196
1197 static gboolean focus_delay_func(gpointer data)
1198 {
1199 ObClient *c = data;
1200
1201 if (focus_client != c) {
1202 client_focus(c);
1203 if (config_focus_raise)
1204 client_raise(c);
1205 }
1206 return FALSE; /* no repeat */
1207 }
1208
1209 static void focus_delay_client_dest(ObClient *client, gpointer data)
1210 {
1211 ob_main_loop_timeout_remove_data(ob_main_loop, focus_delay_func, client);
1212 }
1213
1214 void event_halt_focus_delay()
1215 {
1216 ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
1217 }
1218
1219 void event_ignore_queued_enters()
1220 {
1221 GSList *saved = NULL, *it;
1222 XEvent *e;
1223
1224 XSync(ob_display, FALSE);
1225
1226 /* count the events */
1227 while (TRUE) {
1228 e = g_new(XEvent, 1);
1229 if (XCheckTypedEvent(ob_display, EnterNotify, e)) {
1230 ObWindow *win;
1231
1232 win = g_hash_table_lookup(window_map, &e->xany.window);
1233 if (win && WINDOW_IS_CLIENT(win))
1234 ++ignore_enter_focus;
1235
1236 saved = g_slist_append(saved, e);
1237 } else {
1238 g_free(e);
1239 break;
1240 }
1241 }
1242 /* put the events back */
1243 for (it = saved; it; it = g_slist_next(it)) {
1244 XPutBackEvent(ob_display, it->data);
1245 g_free(it->data);
1246 }
1247 g_slist_free(saved);
1248 }
This page took 0.090081 seconds and 5 git commands to generate.