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