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