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