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