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