]> Dogcows Code - chaz/openbox/blob - openbox/event.c
d3e5ad72d4d1ea44b8ffd0d44a29b5bfd3cdea30
[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 "debug.h"
20 #include "openbox.h"
21 #include "dock.h"
22 #include "client.h"
23 #include "xerror.h"
24 #include "prop.h"
25 #include "config.h"
26 #include "screen.h"
27 #include "frame.h"
28 #include "menu.h"
29 #include "menuframe.h"
30 #include "keyboard.h"
31 #include "mouse.h"
32 #include "mainloop.h"
33 #include "framerender.h"
34 #include "focus.h"
35 #include "moveresize.h"
36 #include "group.h"
37 #include "stacking.h"
38 #include "extensions.h"
39 #include "event.h"
40
41 #include <X11/Xlib.h>
42 #include <X11/keysym.h>
43 #include <X11/Xatom.h>
44 #include <glib.h>
45
46 #ifdef HAVE_SYS_SELECT_H
47 # include <sys/select.h>
48 #endif
49 #ifdef HAVE_SIGNAL_H
50 # include <signal.h>
51 #endif
52
53 #ifdef USE_SM
54 #include <X11/ICE/ICElib.h>
55 #endif
56
57 typedef struct
58 {
59 gboolean ignored;
60 } ObEventData;
61
62 static void event_process(const XEvent *e, gpointer data);
63 static void event_handle_root(XEvent *e);
64 static void event_handle_menu(XEvent *e);
65 static void event_handle_dock(ObDock *s, XEvent *e);
66 static void event_handle_dockapp(ObDockApp *app, XEvent *e);
67 static void event_handle_client(ObClient *c, XEvent *e);
68 static void event_handle_group(ObGroup *g, XEvent *e);
69
70 static gboolean focus_delay_func(gpointer data);
71 static void focus_delay_client_dest(gpointer data);
72
73 static gboolean menu_hide_delay_func(gpointer data);
74
75 #define INVALID_FOCUSIN(e) ((e)->xfocus.detail == NotifyInferior || \
76 (e)->xfocus.detail == NotifyAncestor || \
77 (e)->xfocus.detail > NotifyNonlinearVirtual)
78 #define INVALID_FOCUSOUT(e) ((e)->xfocus.mode == NotifyGrab || \
79 (e)->xfocus.detail == NotifyInferior || \
80 (e)->xfocus.detail == NotifyAncestor || \
81 (e)->xfocus.detail > NotifyNonlinearVirtual)
82
83 Time event_lasttime = 0;
84
85 /*! The value of the mask for the NumLock modifier */
86 unsigned int NumLockMask;
87 /*! The value of the mask for the ScrollLock modifier */
88 unsigned int ScrollLockMask;
89 /*! The key codes for the modifier keys */
90 static XModifierKeymap *modmap;
91 /*! Table of the constant modifier masks */
92 static const int mask_table[] = {
93 ShiftMask, LockMask, ControlMask, Mod1Mask,
94 Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask
95 };
96 static int mask_table_size;
97
98 static ObClient *focus_delay_client;
99
100 static gboolean menu_can_hide;
101
102 #ifdef USE_SM
103 static void ice_handler(int fd, gpointer conn)
104 {
105 Bool b;
106 IceProcessMessages(conn, NULL, &b);
107 }
108
109 static void ice_watch(IceConn conn, IcePointer data, Bool opening,
110 IcePointer *watch_data)
111 {
112 static gint fd = -1;
113
114 if (opening) {
115 fd = IceConnectionNumber(conn);
116 ob_main_loop_fd_add(ob_main_loop, fd, ice_handler, conn, NULL);
117 } else {
118 ob_main_loop_fd_remove(ob_main_loop, fd);
119 fd = -1;
120 }
121 }
122 #endif
123
124 void event_startup(gboolean reconfig)
125 {
126 if (reconfig) return;
127
128 mask_table_size = sizeof(mask_table) / sizeof(mask_table[0]);
129
130 /* get lock masks that are defined by the display (not constant) */
131 modmap = XGetModifierMapping(ob_display);
132 g_assert(modmap);
133 if (modmap && modmap->max_keypermod > 0) {
134 size_t cnt;
135 const size_t size = mask_table_size * modmap->max_keypermod;
136 /* get the values of the keyboard lock modifiers
137 Note: Caps lock is not retrieved the same way as Scroll and Num
138 lock since it doesn't need to be. */
139 const KeyCode num_lock = XKeysymToKeycode(ob_display, XK_Num_Lock);
140 const KeyCode scroll_lock = XKeysymToKeycode(ob_display,
141 XK_Scroll_Lock);
142
143 for (cnt = 0; cnt < size; ++cnt) {
144 if (! modmap->modifiermap[cnt]) continue;
145
146 if (num_lock == modmap->modifiermap[cnt])
147 NumLockMask = mask_table[cnt / modmap->max_keypermod];
148 if (scroll_lock == modmap->modifiermap[cnt])
149 ScrollLockMask = mask_table[cnt / modmap->max_keypermod];
150 }
151 }
152
153 ob_main_loop_x_add(ob_main_loop, event_process, NULL, NULL);
154
155 #ifdef USE_SM
156 IceAddConnectionWatch(ice_watch, NULL);
157 #endif
158
159 client_add_destructor(focus_delay_client_dest);
160 }
161
162 void event_shutdown(gboolean reconfig)
163 {
164 if (reconfig) return;
165
166 #ifdef USE_SM
167 IceRemoveConnectionWatch(ice_watch, NULL);
168 #endif
169
170 client_remove_destructor(focus_delay_client_dest);
171 XFreeModifiermap(modmap);
172 }
173
174 static Window event_get_window(XEvent *e)
175 {
176 Window window;
177
178 /* pick a window */
179 switch (e->type) {
180 case SelectionClear:
181 window = RootWindow(ob_display, ob_screen);
182 break;
183 case MapRequest:
184 window = e->xmap.window;
185 break;
186 case UnmapNotify:
187 window = e->xunmap.window;
188 break;
189 case DestroyNotify:
190 window = e->xdestroywindow.window;
191 break;
192 case ConfigureRequest:
193 window = e->xconfigurerequest.window;
194 break;
195 case ConfigureNotify:
196 window = e->xconfigure.window;
197 break;
198 default:
199 #ifdef XKB
200 if (extensions_xkb && e->type == extensions_xkb_event_basep) {
201 switch (((XkbAnyEvent*)e)->xkb_type) {
202 case XkbBellNotify:
203 window = ((XkbBellNotifyEvent*)e)->window;
204 default:
205 window = None;
206 }
207 } else
208 #endif
209 window = e->xany.window;
210 }
211 return window;
212 }
213
214 static void event_set_lasttime(XEvent *e)
215 {
216 Time t = 0;
217
218 /* grab the lasttime and hack up the state */
219 switch (e->type) {
220 case ButtonPress:
221 case ButtonRelease:
222 t = e->xbutton.time;
223 break;
224 case KeyPress:
225 t = e->xkey.time;
226 break;
227 case KeyRelease:
228 t = e->xkey.time;
229 break;
230 case MotionNotify:
231 t = e->xmotion.time;
232 break;
233 case PropertyNotify:
234 t = e->xproperty.time;
235 break;
236 case EnterNotify:
237 case LeaveNotify:
238 t = e->xcrossing.time;
239 break;
240 default:
241 /* if more event types are anticipated, get their timestamp
242 explicitly */
243 break;
244 }
245
246 if (t > event_lasttime)
247 event_lasttime = t;
248 }
249
250 #define STRIP_MODS(s) \
251 s &= ~(LockMask | NumLockMask | ScrollLockMask), \
252 /* kill off the Button1Mask etc, only want the modifiers */ \
253 s &= (ControlMask | ShiftMask | Mod1Mask | \
254 Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask) \
255
256 static void event_hack_mods(XEvent *e)
257 {
258 KeyCode *kp;
259 int i, k;
260
261 switch (e->type) {
262 case ButtonPress:
263 case ButtonRelease:
264 STRIP_MODS(e->xbutton.state);
265 break;
266 case KeyPress:
267 STRIP_MODS(e->xkey.state);
268 break;
269 case KeyRelease:
270 STRIP_MODS(e->xkey.state);
271 /* remove from the state the mask of the modifier being released, if
272 it is a modifier key being released (this is a little ugly..) */
273 kp = modmap->modifiermap;
274 for (i = 0; i < mask_table_size; ++i) {
275 for (k = 0; k < modmap->max_keypermod; ++k) {
276 if (*kp == e->xkey.keycode) { /* found the keycode */
277 /* remove the mask for it */
278 e->xkey.state &= ~mask_table[i];
279 /* cause the first loop to break; */
280 i = mask_table_size;
281 break; /* get outta here! */
282 }
283 ++kp;
284 }
285 }
286 break;
287 case MotionNotify:
288 STRIP_MODS(e->xmotion.state);
289 /* compress events */
290 {
291 XEvent ce;
292 while (XCheckTypedWindowEvent(ob_display, e->xmotion.window,
293 e->type, &ce)) {
294 e->xmotion.x_root = ce.xmotion.x_root;
295 e->xmotion.y_root = ce.xmotion.y_root;
296 }
297 }
298 break;
299 }
300 }
301
302 static gboolean event_ignore(XEvent *e, ObClient *client)
303 {
304 switch(e->type) {
305 case FocusIn:
306 /* NotifyAncestor is not ignored in FocusIn like it is in FocusOut
307 because of RevertToPointerRoot. If the focus ends up reverting to
308 pointer root on a workspace change, then the FocusIn event that we
309 want will be of type NotifyAncestor. This situation does not occur
310 for FocusOut, so it is safely ignored there.
311 */
312 if (INVALID_FOCUSIN(e) ||
313 client == NULL) {
314 #ifdef DEBUG_FOCUS
315 ob_debug("FocusIn on %lx mode %d detail %d IGNORED\n",
316 e->xfocus.window, e->xfocus.mode, e->xfocus.detail);
317 #endif
318 /* says a client was not found for the event (or a valid FocusIn
319 event was not found.
320 */
321 e->xfocus.window = None;
322 return TRUE;
323 }
324
325 #ifdef DEBUG_FOCUS
326 ob_debug("FocusIn on %lx mode %d detail %d\n", e->xfocus.window,
327 e->xfocus.mode, e->xfocus.detail);
328 #endif
329 break;
330 case FocusOut:
331 if (INVALID_FOCUSOUT(e)) {
332 #ifdef DEBUG_FOCUS
333 ob_debug("FocusOut on %lx mode %d detail %d IGNORED\n",
334 e->xfocus.window, e->xfocus.mode, e->xfocus.detail);
335 #endif
336 return TRUE;
337 }
338
339 #ifdef DEBUG_FOCUS
340 ob_debug("FocusOut on %lx mode %d detail %d\n",
341 e->xfocus.window, e->xfocus.mode, e->xfocus.detail);
342 #endif
343
344 {
345 XEvent fe;
346 gboolean fallback = TRUE;
347
348 while (TRUE) {
349 if (!XCheckTypedWindowEvent(ob_display, e->xfocus.window,
350 FocusOut, &fe))
351 if (!XCheckTypedEvent(ob_display, FocusIn, &fe))
352 break;
353 if (fe.type == FocusOut) {
354 #ifdef DEBUG_FOCUS
355 ob_debug("found pending FocusOut\n");
356 #endif
357 if (!INVALID_FOCUSOUT(&fe)) {
358 /* if there is a VALID FocusOut still coming, don't
359 fallback focus yet, we'll deal with it then */
360 XPutBackEvent(ob_display, &fe);
361 fallback = FALSE;
362 break;
363 }
364 } else {
365 #ifdef DEBUG_FOCUS
366 ob_debug("found pending FocusIn\n");
367 #endif
368 /* is the focused window getting a FocusOut/In back to
369 itself?
370 */
371 if (fe.xfocus.window == e->xfocus.window &&
372 !event_ignore(&fe, client)) {
373 /*
374 if focus_client is not set, then we can't do
375 this. we need the FocusIn. This happens in the
376 case when the set_focus_client(NULL) in the
377 focus_fallback function fires and then
378 focus_fallback picks the currently focused
379 window (such as on a SendToDesktop-esque action.
380 */
381 if (focus_client) {
382 #ifdef DEBUG_FOCUS
383 ob_debug("focused window got an Out/In back to "
384 "itself IGNORED both\n");
385 #endif
386 return TRUE;
387 } else {
388 event_process(&fe, NULL);
389 #ifdef DEBUG_FOCUS
390 ob_debug("focused window got an Out/In back to "
391 "itself but focus_client was null "
392 "IGNORED just the Out\n");
393 #endif
394 return TRUE;
395 }
396 }
397
398 {
399 ObEventData d;
400
401 /* once all the FocusOut's have been dealt with, if
402 there is a FocusIn still left and it is valid, then
403 use it */
404 event_process(&fe, &d);
405 if (!d.ignored) {
406 #ifdef DEBUG_FOCUS
407 ob_debug("FocusIn was OK, so don't fallback\n");
408 #endif
409 fallback = FALSE;
410 break;
411 }
412 }
413 }
414 }
415 if (fallback) {
416 #ifdef DEBUG_FOCUS
417 ob_debug("no valid FocusIn and no FocusOut events found, "
418 "falling back\n");
419 #endif
420 focus_fallback(OB_FOCUS_FALLBACK_NOFOCUS);
421 }
422 }
423 break;
424 case EnterNotify:
425 case LeaveNotify:
426 /* NotifyUngrab occurs when a mouse button is released and the event is
427 caused, like when lowering a window */
428 /* NotifyVirtual and NotifyAncestor occurs when ungrabbing the
429 pointer (Ancestor happens when the pointer is on a window border) */
430 if (e->xcrossing.mode == NotifyGrab ||
431 e->xcrossing.detail == NotifyInferior ||
432 (e->xcrossing.mode == NotifyUngrab &&
433 (e->xcrossing.detail == NotifyAncestor ||
434 e->xcrossing.detail == NotifyNonlinearVirtual ||
435 e->xcrossing.detail == NotifyVirtual))) {
436 #ifdef DEBUG_FOCUS
437 ob_debug("%sNotify mode %d detail %d on %lx IGNORED\n",
438 (e->type == EnterNotify ? "Enter" : "Leave"),
439 e->xcrossing.mode,
440 e->xcrossing.detail, client?client->window:0);
441 #endif
442 return TRUE;
443 }
444 #ifdef DEBUG_FOCUS
445 ob_debug("%sNotify mode %d detail %d on %lx\n",
446 (e->type == EnterNotify ? "Enter" : "Leave"),
447 e->xcrossing.mode,
448 e->xcrossing.detail, client?client->window:0);
449 #endif
450 break;
451 }
452 return FALSE;
453 }
454
455 static void event_process(const XEvent *ec, gpointer data)
456 {
457 Window window;
458 ObGroup *group = NULL;
459 ObClient *client = NULL;
460 ObDock *dock = NULL;
461 ObDockApp *dockapp = NULL;
462 ObWindow *obwin = NULL;
463 XEvent ee, *e;
464 ObEventData *ed = data;
465
466 /* make a copy we can mangle */
467 ee = *ec;
468 e = &ee;
469
470 window = event_get_window(e);
471 if (!(e->type == PropertyNotify &&
472 (group = g_hash_table_lookup(group_map, &window))))
473 if ((obwin = g_hash_table_lookup(window_map, &window))) {
474 switch (obwin->type) {
475 case Window_Dock:
476 dock = WINDOW_AS_DOCK(obwin);
477 break;
478 case Window_DockApp:
479 dockapp = WINDOW_AS_DOCKAPP(obwin);
480 break;
481 case Window_Client:
482 client = WINDOW_AS_CLIENT(obwin);
483 break;
484 case Window_Menu:
485 case Window_Internal:
486 /* not to be used for events */
487 g_assert_not_reached();
488 break;
489 }
490 }
491
492 event_set_lasttime(e);
493 event_hack_mods(e);
494 if (event_ignore(e, client)) {
495 if (ed)
496 ed->ignored = TRUE;
497 return;
498 } else if (ed)
499 ed->ignored = FALSE;
500
501 /* deal with it in the kernel */
502 if (group)
503 event_handle_group(group, e);
504 else if (client)
505 event_handle_client(client, e);
506 else if (dockapp)
507 event_handle_dockapp(dockapp, e);
508 else if (dock)
509 event_handle_dock(dock, e);
510 else if (window == RootWindow(ob_display, ob_screen))
511 event_handle_root(e);
512 else if (e->type == MapRequest)
513 client_manage(window);
514 else if (e->type == ConfigureRequest) {
515 /* unhandled configure requests must be used to configure the
516 window directly */
517 XWindowChanges xwc;
518
519 xwc.x = e->xconfigurerequest.x;
520 xwc.y = e->xconfigurerequest.y;
521 xwc.width = e->xconfigurerequest.width;
522 xwc.height = e->xconfigurerequest.height;
523 xwc.border_width = e->xconfigurerequest.border_width;
524 xwc.sibling = e->xconfigurerequest.above;
525 xwc.stack_mode = e->xconfigurerequest.detail;
526
527 /* we are not to be held responsible if someone sends us an
528 invalid request! */
529 xerror_set_ignore(TRUE);
530 XConfigureWindow(ob_display, window,
531 e->xconfigurerequest.value_mask, &xwc);
532 xerror_set_ignore(FALSE);
533 }
534
535 /* user input (action-bound) events */
536 if (e->type == ButtonPress || e->type == ButtonRelease ||
537 e->type == MotionNotify || e->type == KeyPress ||
538 e->type == KeyRelease)
539 {
540 if (menu_frame_visible)
541 event_handle_menu(e);
542 else {
543 if (!keyboard_process_interactive_grab(e, &client)) {
544 if (moveresize_in_progress) {
545 moveresize_event(e);
546
547 /* make further actions work on the client being
548 moved/resized */
549 client = moveresize_client;
550 }
551
552 menu_can_hide = FALSE;
553 ob_main_loop_timeout_add(ob_main_loop,
554 G_USEC_PER_SEC / 4,
555 menu_hide_delay_func,
556 NULL, NULL);
557
558 if (e->type == ButtonPress || e->type == ButtonRelease ||
559 e->type == MotionNotify)
560 mouse_event(client, e);
561 else if (e->type == KeyPress)
562 /* when in the middle of a focus cycling action, this
563 causes the window which appears to be focused to be
564 the one on which the actions will be executed */
565 keyboard_event((focus_cycle_target ?
566 focus_cycle_target : client), e);
567 }
568 }
569 }
570 }
571
572 static void event_handle_root(XEvent *e)
573 {
574 Atom msgtype;
575
576 switch(e->type) {
577 case SelectionClear:
578 ob_debug("Another WM has requested to replace us. Exiting.\n");
579 ob_exit(0);
580 break;
581
582 case ClientMessage:
583 if (e->xclient.format != 32) break;
584
585 msgtype = e->xclient.message_type;
586 if (msgtype == prop_atoms.net_current_desktop) {
587 unsigned int d = e->xclient.data.l[0];
588 if (d < screen_num_desktops)
589 screen_set_desktop(d);
590 } else if (msgtype == prop_atoms.net_number_of_desktops) {
591 unsigned int d = e->xclient.data.l[0];
592 if (d > 0)
593 screen_set_num_desktops(d);
594 } else if (msgtype == prop_atoms.net_showing_desktop) {
595 screen_show_desktop(e->xclient.data.l[0] != 0);
596 }
597 break;
598 case PropertyNotify:
599 if (e->xproperty.atom == prop_atoms.net_desktop_names)
600 screen_update_desktop_names();
601 else if (e->xproperty.atom == prop_atoms.net_desktop_layout)
602 screen_update_layout();
603 break;
604 case ConfigureNotify:
605 #ifdef XRANDR
606 XRRUpdateConfiguration(e);
607 #endif
608 screen_resize();
609 break;
610 default:
611 ;
612 #ifdef VIDMODE
613 if (extensions_vidmode && e->type == extensions_vidmode_event_basep) {
614 ob_debug("VIDMODE EVENT\n");
615 }
616 #endif
617 }
618 }
619
620 static void event_handle_group(ObGroup *group, XEvent *e)
621 {
622 GSList *it;
623
624 g_assert(e->type == PropertyNotify);
625
626 for (it = group->members; it; it = g_slist_next(it))
627 event_handle_client(it->data, e);
628 }
629
630 static void event_handle_client(ObClient *client, XEvent *e)
631 {
632 XEvent ce;
633 Atom msgtype;
634 int i=0;
635 ObFrameContext con;
636
637 switch (e->type) {
638 case VisibilityNotify:
639 client->frame->obscured = e->xvisibility.state != VisibilityUnobscured;
640 break;
641 case ButtonPress:
642 case ButtonRelease:
643 /* Wheel buttons don't draw because they are an instant click, so it
644 is a waste of resources to go drawing it. */
645 if (!(e->xbutton.button == 4 || e->xbutton.button == 5)) {
646 con = frame_context(client, e->xbutton.window);
647 con = mouse_button_frame_context(con, e->xbutton.button);
648 switch (con) {
649 case OB_FRAME_CONTEXT_MAXIMIZE:
650 client->frame->max_press = (e->type == ButtonPress);
651 framerender_frame(client->frame);
652 break;
653 case OB_FRAME_CONTEXT_CLOSE:
654 client->frame->close_press = (e->type == ButtonPress);
655 framerender_frame(client->frame);
656 break;
657 case OB_FRAME_CONTEXT_ICONIFY:
658 client->frame->iconify_press = (e->type == ButtonPress);
659 framerender_frame(client->frame);
660 break;
661 case OB_FRAME_CONTEXT_ALLDESKTOPS:
662 client->frame->desk_press = (e->type == ButtonPress);
663 framerender_frame(client->frame);
664 break;
665 case OB_FRAME_CONTEXT_SHADE:
666 client->frame->shade_press = (e->type == ButtonPress);
667 framerender_frame(client->frame);
668 break;
669 default:
670 /* nothing changes with clicks for any other contexts */
671 break;
672 }
673 }
674 break;
675 case FocusIn:
676 #ifdef DEBUG_FOCUS
677 ob_debug("FocusIn on client for %lx\n", client->window);
678 #endif
679 if (client != focus_client) {
680 focus_set_client(client);
681 frame_adjust_focus(client->frame, TRUE);
682 }
683 break;
684 case FocusOut:
685 #ifdef DEBUG_FOCUS
686 ob_debug("FocusOut on client for %lx\n", client->window);
687 #endif
688 /* are we a fullscreen window or a transient of one? (checks layer)
689 if we are then we need to be iconified since we are losing focus
690 */
691 if (client->layer == OB_STACKING_LAYER_FULLSCREEN && !client->iconic &&
692 !client_search_focus_tree_full(client))
693 /* iconify fullscreen windows when they and their transients
694 aren't focused */
695 client_iconify(client, TRUE, TRUE);
696 frame_adjust_focus(client->frame, FALSE);
697 break;
698 case LeaveNotify:
699 con = frame_context(client, e->xcrossing.window);
700 switch (con) {
701 case OB_FRAME_CONTEXT_MAXIMIZE:
702 client->frame->max_hover = FALSE;
703 frame_adjust_state(client->frame);
704 break;
705 case OB_FRAME_CONTEXT_ALLDESKTOPS:
706 client->frame->desk_hover = FALSE;
707 frame_adjust_state(client->frame);
708 break;
709 case OB_FRAME_CONTEXT_SHADE:
710 client->frame->shade_hover = FALSE;
711 frame_adjust_state(client->frame);
712 break;
713 case OB_FRAME_CONTEXT_ICONIFY:
714 client->frame->iconify_hover = FALSE;
715 frame_adjust_state(client->frame);
716 break;
717 case OB_FRAME_CONTEXT_CLOSE:
718 client->frame->close_hover = FALSE;
719 frame_adjust_state(client->frame);
720 break;
721 case OB_FRAME_CONTEXT_FRAME:
722 /* XXX if doing a 'reconfigure' make sure you kill this timer,
723 maybe all timers.. */
724 if (config_focus_delay && client == focus_delay_client) {
725 ob_main_loop_timeout_remove_data(ob_main_loop,
726 focus_delay_func,
727 focus_delay_client);
728 focus_delay_client = NULL;
729 }
730 default:
731 break;
732 }
733 break;
734 case EnterNotify:
735 con = frame_context(client, e->xcrossing.window);
736 switch (con) {
737 case OB_FRAME_CONTEXT_MAXIMIZE:
738 client->frame->max_hover = TRUE;
739 frame_adjust_state(client->frame);
740 break;
741 case OB_FRAME_CONTEXT_ALLDESKTOPS:
742 client->frame->desk_hover = TRUE;
743 frame_adjust_state(client->frame);
744 break;
745 case OB_FRAME_CONTEXT_SHADE:
746 client->frame->shade_hover = TRUE;
747 frame_adjust_state(client->frame);
748 break;
749 case OB_FRAME_CONTEXT_ICONIFY:
750 client->frame->iconify_hover = TRUE;
751 frame_adjust_state(client->frame);
752 break;
753 case OB_FRAME_CONTEXT_CLOSE:
754 client->frame->close_hover = TRUE;
755 frame_adjust_state(client->frame);
756 break;
757 case OB_FRAME_CONTEXT_FRAME:
758 if (client_normal(client)) {
759 if (config_focus_follow) {
760 #ifdef DEBUG_FOCUS
761 ob_debug("EnterNotify on %lx, focusing window\n",
762 client->window);
763 #endif
764 if (config_focus_delay) {
765 ob_main_loop_timeout_add(ob_main_loop,
766 config_focus_delay,
767 focus_delay_func,
768 client, NULL);
769 focus_delay_client = client;
770 } else
771 client_focus(client);
772 }
773 }
774 break;
775 default:
776 break;
777 }
778 break;
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 client_focus(focus_delay_client);
1256 return FALSE; /* no repeat */
1257 }
1258
1259 static void focus_delay_client_dest(gpointer data)
1260 {
1261 ObClient *c = data;
1262 if (c == focus_delay_client) {
1263 ob_main_loop_timeout_remove_data(ob_main_loop, focus_delay_func,
1264 focus_delay_client);
1265 focus_delay_client = NULL;
1266 }
1267 }
This page took 0.084138 seconds and 3 git commands to generate.