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