]> Dogcows Code - chaz/openbox/blob - openbox/event.c
show an indicator similar to metacity's for focus cycling windows
[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 }
325
326 static void event_done(gpointer data)
327 {
328 static ObClient *last = NULL;
329
330 /* sometimes focus_hilite can be on an unfocused window, this make sure
331 it loses its focus hilite when focus moves */
332 if (focus_hilite &&
333 (focus_in && focus_hilite != focus_in) &&
334 (focus_out && focus_hilite != focus_out))
335 {
336 frame_adjust_focus(focus_hilite->frame, FALSE);
337 }
338
339 if (focus_in) {
340 if (focus_in != focus_client) {
341 focus_set_client(focus_in);
342 frame_adjust_focus(focus_in->frame, TRUE);
343 client_calc_layer(focus_in);
344 }
345 }
346 if (focus_out) {
347 if (focus_out == focus_client)
348 focus_set_client(NULL);
349 frame_adjust_focus(focus_out->frame, FALSE);
350 client_calc_layer(focus_out);
351 }
352
353 focus_hilite = focus_in;
354
355 if (focus_client != last) {
356 if (!focus_client)
357 focus_fallback(OB_FOCUS_FALLBACK_NOFOCUS);
358 last = focus_client;
359 }
360
361 focus_in = focus_out = NULL;
362 }
363
364 static void event_process(const XEvent *ec, gpointer data)
365 {
366 Window window;
367 ObGroup *group = NULL;
368 ObClient *client = NULL;
369 ObDock *dock = NULL;
370 ObDockApp *dockapp = NULL;
371 ObWindow *obwin = NULL;
372 XEvent ee, *e;
373
374 /* make a copy we can mangle */
375 ee = *ec;
376 e = &ee;
377
378 window = event_get_window(e);
379 if (!(e->type == PropertyNotify &&
380 (group = g_hash_table_lookup(group_map, &window))))
381 if ((obwin = g_hash_table_lookup(window_map, &window))) {
382 switch (obwin->type) {
383 case Window_Dock:
384 dock = WINDOW_AS_DOCK(obwin);
385 break;
386 case Window_DockApp:
387 dockapp = WINDOW_AS_DOCKAPP(obwin);
388 break;
389 case Window_Client:
390 client = WINDOW_AS_CLIENT(obwin);
391 break;
392 case Window_Menu:
393 case Window_Internal:
394 /* not to be used for events */
395 g_assert_not_reached();
396 break;
397 }
398 }
399
400 event_set_lasttime(e);
401 event_hack_mods(e);
402 if (event_ignore(e, client))
403 return;
404
405 /* deal with it in the kernel */
406 if (group)
407 event_handle_group(group, e);
408 else if (client)
409 event_handle_client(client, e);
410 else if (dockapp)
411 event_handle_dockapp(dockapp, e);
412 else if (dock)
413 event_handle_dock(dock, e);
414 else if (window == RootWindow(ob_display, ob_screen))
415 event_handle_root(e);
416 else if (e->type == MapRequest)
417 client_manage(window);
418 else if (e->type == ConfigureRequest) {
419 /* unhandled configure requests must be used to configure the
420 window directly */
421 XWindowChanges xwc;
422
423 xwc.x = e->xconfigurerequest.x;
424 xwc.y = e->xconfigurerequest.y;
425 xwc.width = e->xconfigurerequest.width;
426 xwc.height = e->xconfigurerequest.height;
427 xwc.border_width = e->xconfigurerequest.border_width;
428 xwc.sibling = e->xconfigurerequest.above;
429 xwc.stack_mode = e->xconfigurerequest.detail;
430
431 /* we are not to be held responsible if someone sends us an
432 invalid request! */
433 xerror_set_ignore(TRUE);
434 XConfigureWindow(ob_display, window,
435 e->xconfigurerequest.value_mask, &xwc);
436 xerror_set_ignore(FALSE);
437 }
438
439 /* user input (action-bound) events */
440 if (e->type == ButtonPress || e->type == ButtonRelease ||
441 e->type == MotionNotify || e->type == KeyPress ||
442 e->type == KeyRelease)
443 {
444 if (menu_frame_visible)
445 event_handle_menu(e);
446 else {
447 if (!keyboard_process_interactive_grab(e, &client)) {
448 if (moveresize_in_progress) {
449 moveresize_event(e);
450
451 /* make further actions work on the client being
452 moved/resized */
453 client = moveresize_client;
454 }
455
456 menu_can_hide = FALSE;
457 ob_main_loop_timeout_add(ob_main_loop,
458 G_USEC_PER_SEC / 4,
459 menu_hide_delay_func,
460 NULL, NULL);
461
462 if (e->type == ButtonPress || e->type == ButtonRelease ||
463 e->type == MotionNotify)
464 mouse_event(client, e);
465 else if (e->type == KeyPress)
466 /* when in the middle of a focus cycling action, this
467 causes the window which appears to be focused to be
468 the one on which the actions will be executed */
469 keyboard_event((focus_cycle_target ?
470 focus_cycle_target : client), e);
471 }
472 }
473 }
474 }
475
476 static void event_handle_root(XEvent *e)
477 {
478 Atom msgtype;
479
480 switch(e->type) {
481 case SelectionClear:
482 ob_debug("Another WM has requested to replace us. Exiting.\n");
483 ob_exit(0);
484 break;
485
486 case ClientMessage:
487 if (e->xclient.format != 32) break;
488
489 msgtype = e->xclient.message_type;
490 if (msgtype == prop_atoms.net_current_desktop) {
491 unsigned int d = e->xclient.data.l[0];
492 if (d < screen_num_desktops)
493 screen_set_desktop(d);
494 } else if (msgtype == prop_atoms.net_number_of_desktops) {
495 unsigned int d = e->xclient.data.l[0];
496 if (d > 0)
497 screen_set_num_desktops(d);
498 } else if (msgtype == prop_atoms.net_showing_desktop) {
499 screen_show_desktop(e->xclient.data.l[0] != 0);
500 }
501 break;
502 case PropertyNotify:
503 if (e->xproperty.atom == prop_atoms.net_desktop_names)
504 screen_update_desktop_names();
505 else if (e->xproperty.atom == prop_atoms.net_desktop_layout)
506 screen_update_layout();
507 break;
508 case ConfigureNotify:
509 #ifdef XRANDR
510 XRRUpdateConfiguration(e);
511 #endif
512 screen_resize();
513 break;
514 default:
515 ;
516 #ifdef VIDMODE
517 if (extensions_vidmode && e->type == extensions_vidmode_event_basep) {
518 ob_debug("VIDMODE EVENT\n");
519 }
520 #endif
521 }
522 }
523
524 static void event_handle_group(ObGroup *group, XEvent *e)
525 {
526 GSList *it;
527
528 g_assert(e->type == PropertyNotify);
529
530 for (it = group->members; it; it = g_slist_next(it))
531 event_handle_client(it->data, e);
532 }
533
534 void event_enter_client(ObClient *client)
535 {
536 g_assert(config_focus_follow);
537
538 if (client_normal(client) && client_can_focus(client)) {
539 if (config_focus_delay) {
540 ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
541 ob_main_loop_timeout_add(ob_main_loop,
542 config_focus_delay,
543 focus_delay_func,
544 client, NULL);
545 } else
546 focus_delay_func(client);
547 }
548 }
549
550 static void event_handle_client(ObClient *client, XEvent *e)
551 {
552 XEvent ce;
553 Atom msgtype;
554 int i=0;
555 ObFrameContext con;
556
557 switch (e->type) {
558 case VisibilityNotify:
559 client->frame->obscured = e->xvisibility.state != VisibilityUnobscured;
560 break;
561 case ButtonPress:
562 case ButtonRelease:
563 /* Wheel buttons don't draw because they are an instant click, so it
564 is a waste of resources to go drawing it. */
565 if (!(e->xbutton.button == 4 || e->xbutton.button == 5)) {
566 con = frame_context(client, e->xbutton.window);
567 con = mouse_button_frame_context(con, e->xbutton.button);
568 switch (con) {
569 case OB_FRAME_CONTEXT_MAXIMIZE:
570 client->frame->max_press = (e->type == ButtonPress);
571 framerender_frame(client->frame);
572 break;
573 case OB_FRAME_CONTEXT_CLOSE:
574 client->frame->close_press = (e->type == ButtonPress);
575 framerender_frame(client->frame);
576 break;
577 case OB_FRAME_CONTEXT_ICONIFY:
578 client->frame->iconify_press = (e->type == ButtonPress);
579 framerender_frame(client->frame);
580 break;
581 case OB_FRAME_CONTEXT_ALLDESKTOPS:
582 client->frame->desk_press = (e->type == ButtonPress);
583 framerender_frame(client->frame);
584 break;
585 case OB_FRAME_CONTEXT_SHADE:
586 client->frame->shade_press = (e->type == ButtonPress);
587 framerender_frame(client->frame);
588 break;
589 default:
590 /* nothing changes with clicks for any other contexts */
591 break;
592 }
593 }
594 break;
595 case FocusIn:
596 #ifdef DEBUG_FOCUS
597 ob_debug("FocusIn on client for %lx (client %lx) mode %d detail %d\n",
598 e->xfocus.window, client->window,
599 e->xfocus.mode, e->xfocus.detail);
600 #endif
601 focus_in = client;
602 if (focus_out == client)
603 focus_out = NULL;
604 break;
605 case FocusOut:
606 #ifdef DEBUG_FOCUS
607 ob_debug("FocusOut on client for %lx (client %lx) mode %d detail %d\n",
608 e->xfocus.window, client->window,
609 e->xfocus.mode, e->xfocus.detail);
610 #endif
611 focus_out = client;
612 if (focus_in == client)
613 focus_in = NULL;
614 break;
615 case LeaveNotify:
616 con = frame_context(client, e->xcrossing.window);
617 switch (con) {
618 case OB_FRAME_CONTEXT_MAXIMIZE:
619 client->frame->max_hover = FALSE;
620 frame_adjust_state(client->frame);
621 break;
622 case OB_FRAME_CONTEXT_ALLDESKTOPS:
623 client->frame->desk_hover = FALSE;
624 frame_adjust_state(client->frame);
625 break;
626 case OB_FRAME_CONTEXT_SHADE:
627 client->frame->shade_hover = FALSE;
628 frame_adjust_state(client->frame);
629 break;
630 case OB_FRAME_CONTEXT_ICONIFY:
631 client->frame->iconify_hover = FALSE;
632 frame_adjust_state(client->frame);
633 break;
634 case OB_FRAME_CONTEXT_CLOSE:
635 client->frame->close_hover = FALSE;
636 frame_adjust_state(client->frame);
637 break;
638 case OB_FRAME_CONTEXT_FRAME:
639 if (config_focus_follow && config_focus_delay)
640 ob_main_loop_timeout_remove_data(ob_main_loop,
641 focus_delay_func,
642 client);
643 break;
644 default:
645 break;
646 }
647 break;
648 case EnterNotify:
649 {
650 gboolean nofocus = FALSE;
651
652 if (ignore_enter_focus) {
653 ignore_enter_focus--;
654 nofocus = TRUE;
655 }
656
657 con = frame_context(client, e->xcrossing.window);
658 switch (con) {
659 case OB_FRAME_CONTEXT_MAXIMIZE:
660 client->frame->max_hover = TRUE;
661 frame_adjust_state(client->frame);
662 break;
663 case OB_FRAME_CONTEXT_ALLDESKTOPS:
664 client->frame->desk_hover = TRUE;
665 frame_adjust_state(client->frame);
666 break;
667 case OB_FRAME_CONTEXT_SHADE:
668 client->frame->shade_hover = TRUE;
669 frame_adjust_state(client->frame);
670 break;
671 case OB_FRAME_CONTEXT_ICONIFY:
672 client->frame->iconify_hover = TRUE;
673 frame_adjust_state(client->frame);
674 break;
675 case OB_FRAME_CONTEXT_CLOSE:
676 client->frame->close_hover = TRUE;
677 frame_adjust_state(client->frame);
678 break;
679 case OB_FRAME_CONTEXT_FRAME:
680 if (e->xcrossing.mode == NotifyGrab ||
681 e->xcrossing.mode == NotifyUngrab)
682 {
683 #ifdef DEBUG_FOCUS
684 ob_debug("%sNotify mode %d detail %d on %lx IGNORED\n",
685 (e->type == EnterNotify ? "Enter" : "Leave"),
686 e->xcrossing.mode,
687 e->xcrossing.detail, client?client->window:0);
688 #endif
689 } else {
690 #ifdef DEBUG_FOCUS
691 ob_debug("%sNotify mode %d detail %d on %lx, "
692 "focusing window: %d\n",
693 (e->type == EnterNotify ? "Enter" : "Leave"),
694 e->xcrossing.mode,
695 e->xcrossing.detail, (client?client->window:0),
696 !nofocus);
697 #endif
698 if (!nofocus && config_focus_follow)
699 event_enter_client(client);
700 }
701 break;
702 default:
703 break;
704 }
705 break;
706 }
707 case ConfigureRequest:
708 /* compress these */
709 while (XCheckTypedWindowEvent(ob_display, client->window,
710 ConfigureRequest, &ce)) {
711 ++i;
712 /* XXX if this causes bad things.. we can compress config req's
713 with the same mask. */
714 e->xconfigurerequest.value_mask |=
715 ce.xconfigurerequest.value_mask;
716 if (ce.xconfigurerequest.value_mask & CWX)
717 e->xconfigurerequest.x = ce.xconfigurerequest.x;
718 if (ce.xconfigurerequest.value_mask & CWY)
719 e->xconfigurerequest.y = ce.xconfigurerequest.y;
720 if (ce.xconfigurerequest.value_mask & CWWidth)
721 e->xconfigurerequest.width = ce.xconfigurerequest.width;
722 if (ce.xconfigurerequest.value_mask & CWHeight)
723 e->xconfigurerequest.height = ce.xconfigurerequest.height;
724 if (ce.xconfigurerequest.value_mask & CWBorderWidth)
725 e->xconfigurerequest.border_width =
726 ce.xconfigurerequest.border_width;
727 if (ce.xconfigurerequest.value_mask & CWStackMode)
728 e->xconfigurerequest.detail = ce.xconfigurerequest.detail;
729 }
730
731 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
732 if (client->iconic || client->shaded) return;
733
734 /* resize, then move, as specified in the EWMH section 7.7 */
735 if (e->xconfigurerequest.value_mask & (CWWidth | CWHeight |
736 CWX | CWY |
737 CWBorderWidth)) {
738 int x, y, w, h;
739 ObCorner corner;
740
741 if (e->xconfigurerequest.value_mask & CWBorderWidth)
742 client->border_width = e->xconfigurerequest.border_width;
743
744 x = (e->xconfigurerequest.value_mask & CWX) ?
745 e->xconfigurerequest.x : client->area.x;
746 y = (e->xconfigurerequest.value_mask & CWY) ?
747 e->xconfigurerequest.y : client->area.y;
748 w = (e->xconfigurerequest.value_mask & CWWidth) ?
749 e->xconfigurerequest.width : client->area.width;
750 h = (e->xconfigurerequest.value_mask & CWHeight) ?
751 e->xconfigurerequest.height : client->area.height;
752
753 {
754 int newx = x;
755 int newy = y;
756 int fw = w +
757 client->frame->size.left + client->frame->size.right;
758 int fh = h +
759 client->frame->size.top + client->frame->size.bottom;
760 client_find_onscreen(client, &newx, &newy, fw, fh,
761 client_normal(client));
762 if (e->xconfigurerequest.value_mask & CWX)
763 x = newx;
764 if (e->xconfigurerequest.value_mask & CWY)
765 y = newy;
766 }
767
768 switch (client->gravity) {
769 case NorthEastGravity:
770 case EastGravity:
771 corner = OB_CORNER_TOPRIGHT;
772 break;
773 case SouthWestGravity:
774 case SouthGravity:
775 corner = OB_CORNER_BOTTOMLEFT;
776 break;
777 case SouthEastGravity:
778 corner = OB_CORNER_BOTTOMRIGHT;
779 break;
780 default: /* NorthWest, Static, etc */
781 corner = OB_CORNER_TOPLEFT;
782 }
783
784 client_configure_full(client, corner, x, y, w, h, FALSE, TRUE,
785 TRUE);
786 }
787
788 if (e->xconfigurerequest.value_mask & CWStackMode) {
789 switch (e->xconfigurerequest.detail) {
790 case Below:
791 case BottomIf:
792 client_lower(client);
793 break;
794
795 case Above:
796 case TopIf:
797 default:
798 client_raise(client);
799 break;
800 }
801 }
802 break;
803 case UnmapNotify:
804 if (client->ignore_unmaps) {
805 client->ignore_unmaps--;
806 break;
807 }
808 client_unmanage(client);
809 break;
810 case DestroyNotify:
811 client_unmanage(client);
812 break;
813 case ReparentNotify:
814 /* this is when the client is first taken captive in the frame */
815 if (e->xreparent.parent == client->frame->plate) break;
816
817 /*
818 This event is quite rare and is usually handled in unmapHandler.
819 However, if the window is unmapped when the reparent event occurs,
820 the window manager never sees it because an unmap event is not sent
821 to an already unmapped window.
822 */
823
824 /* we don't want the reparent event, put it back on the stack for the
825 X server to deal with after we unmanage the window */
826 XPutBackEvent(ob_display, e);
827
828 client_unmanage(client);
829 break;
830 case MapRequest:
831 ob_debug("MapRequest for 0x%lx\n", client->window);
832 if (!client->iconic) break; /* this normally doesn't happen, but if it
833 does, we don't want it! */
834 client_activate(client, FALSE);
835 break;
836 case ClientMessage:
837 /* validate cuz we query stuff off the client here */
838 if (!client_validate(client)) break;
839
840 if (e->xclient.format != 32) return;
841
842 msgtype = e->xclient.message_type;
843 if (msgtype == prop_atoms.wm_change_state) {
844 /* compress changes into a single change */
845 while (XCheckTypedWindowEvent(ob_display, client->window,
846 e->type, &ce)) {
847 /* XXX: it would be nice to compress ALL messages of a
848 type, not just messages in a row without other
849 message types between. */
850 if (ce.xclient.message_type != msgtype) {
851 XPutBackEvent(ob_display, &ce);
852 break;
853 }
854 e->xclient = ce.xclient;
855 }
856 client_set_wm_state(client, e->xclient.data.l[0]);
857 } else if (msgtype == prop_atoms.net_wm_desktop) {
858 /* compress changes into a single change */
859 while (XCheckTypedWindowEvent(ob_display, client->window,
860 e->type, &ce)) {
861 /* XXX: it would be nice to compress ALL messages of a
862 type, not just messages in a row without other
863 message types between. */
864 if (ce.xclient.message_type != msgtype) {
865 XPutBackEvent(ob_display, &ce);
866 break;
867 }
868 e->xclient = ce.xclient;
869 }
870 if ((unsigned)e->xclient.data.l[0] < screen_num_desktops ||
871 (unsigned)e->xclient.data.l[0] == DESKTOP_ALL)
872 client_set_desktop(client, (unsigned)e->xclient.data.l[0],
873 FALSE);
874 } else if (msgtype == prop_atoms.net_wm_state) {
875 /* can't compress these */
876 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
877 (e->xclient.data.l[0] == 0 ? "Remove" :
878 e->xclient.data.l[0] == 1 ? "Add" :
879 e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
880 e->xclient.data.l[1], e->xclient.data.l[2],
881 client->window);
882 client_set_state(client, e->xclient.data.l[0],
883 e->xclient.data.l[1], e->xclient.data.l[2]);
884 } else if (msgtype == prop_atoms.net_close_window) {
885 ob_debug("net_close_window for 0x%lx\n", client->window);
886 client_close(client);
887 } else if (msgtype == prop_atoms.net_active_window) {
888 ob_debug("net_active_window for 0x%lx\n", client->window);
889 client_activate(client, FALSE);
890 } else if (msgtype == prop_atoms.net_wm_moveresize) {
891 ob_debug("net_wm_moveresize for 0x%lx\n", client->window);
892 if ((Atom)e->xclient.data.l[2] ==
893 prop_atoms.net_wm_moveresize_size_topleft ||
894 (Atom)e->xclient.data.l[2] ==
895 prop_atoms.net_wm_moveresize_size_top ||
896 (Atom)e->xclient.data.l[2] ==
897 prop_atoms.net_wm_moveresize_size_topright ||
898 (Atom)e->xclient.data.l[2] ==
899 prop_atoms.net_wm_moveresize_size_right ||
900 (Atom)e->xclient.data.l[2] ==
901 prop_atoms.net_wm_moveresize_size_right ||
902 (Atom)e->xclient.data.l[2] ==
903 prop_atoms.net_wm_moveresize_size_bottomright ||
904 (Atom)e->xclient.data.l[2] ==
905 prop_atoms.net_wm_moveresize_size_bottom ||
906 (Atom)e->xclient.data.l[2] ==
907 prop_atoms.net_wm_moveresize_size_bottomleft ||
908 (Atom)e->xclient.data.l[2] ==
909 prop_atoms.net_wm_moveresize_size_left ||
910 (Atom)e->xclient.data.l[2] ==
911 prop_atoms.net_wm_moveresize_move ||
912 (Atom)e->xclient.data.l[2] ==
913 prop_atoms.net_wm_moveresize_size_keyboard ||
914 (Atom)e->xclient.data.l[2] ==
915 prop_atoms.net_wm_moveresize_move_keyboard) {
916
917 moveresize_start(client, e->xclient.data.l[0],
918 e->xclient.data.l[1], e->xclient.data.l[3],
919 e->xclient.data.l[2]);
920 }
921 } else if (msgtype == prop_atoms.net_moveresize_window) {
922 int oldg = client->gravity;
923 int tmpg, x, y, w, h;
924
925 if (e->xclient.data.l[0] & 0xff)
926 tmpg = e->xclient.data.l[0] & 0xff;
927 else
928 tmpg = oldg;
929
930 if (e->xclient.data.l[0] & 1 << 8)
931 x = e->xclient.data.l[1];
932 else
933 x = client->area.x;
934 if (e->xclient.data.l[0] & 1 << 9)
935 y = e->xclient.data.l[2];
936 else
937 y = client->area.y;
938 if (e->xclient.data.l[0] & 1 << 10)
939 w = e->xclient.data.l[3];
940 else
941 w = client->area.width;
942 if (e->xclient.data.l[0] & 1 << 11)
943 h = e->xclient.data.l[4];
944 else
945 h = client->area.height;
946 client->gravity = tmpg;
947
948 {
949 int newx = x;
950 int newy = y;
951 int fw = w +
952 client->frame->size.left + client->frame->size.right;
953 int fh = h +
954 client->frame->size.top + client->frame->size.bottom;
955 client_find_onscreen(client, &newx, &newy, fw, fh,
956 client_normal(client));
957 if (e->xclient.data.l[0] & 1 << 8)
958 x = newx;
959 if (e->xclient.data.l[0] & 1 << 9)
960 y = newy;
961 }
962
963 client_configure(client, OB_CORNER_TOPLEFT,
964 x, y, w, h, FALSE, TRUE);
965
966 client->gravity = oldg;
967 }
968 break;
969 case PropertyNotify:
970 /* validate cuz we query stuff off the client here */
971 if (!client_validate(client)) break;
972
973 /* compress changes to a single property into a single change */
974 while (XCheckTypedWindowEvent(ob_display, client->window,
975 e->type, &ce)) {
976 Atom a, b;
977
978 /* XXX: it would be nice to compress ALL changes to a property,
979 not just changes in a row without other props between. */
980
981 a = ce.xproperty.atom;
982 b = e->xproperty.atom;
983
984 if (a == b)
985 continue;
986 if ((a == prop_atoms.net_wm_name ||
987 a == prop_atoms.wm_name ||
988 a == prop_atoms.net_wm_icon_name ||
989 a == prop_atoms.wm_icon_name)
990 &&
991 (b == prop_atoms.net_wm_name ||
992 b == prop_atoms.wm_name ||
993 b == prop_atoms.net_wm_icon_name ||
994 b == prop_atoms.wm_icon_name)) {
995 continue;
996 }
997 if ((a == prop_atoms.net_wm_icon ||
998 a == prop_atoms.kwm_win_icon)
999 &&
1000 (b == prop_atoms.net_wm_icon ||
1001 b == prop_atoms.kwm_win_icon))
1002 continue;
1003
1004 XPutBackEvent(ob_display, &ce);
1005 break;
1006 }
1007
1008 msgtype = e->xproperty.atom;
1009 if (msgtype == XA_WM_NORMAL_HINTS) {
1010 client_update_normal_hints(client);
1011 /* normal hints can make a window non-resizable */
1012 client_setup_decor_and_functions(client);
1013 } else if (msgtype == XA_WM_HINTS) {
1014 client_update_wmhints(client);
1015 } else if (msgtype == XA_WM_TRANSIENT_FOR) {
1016 client_update_transient_for(client);
1017 client_get_type(client);
1018 /* type may have changed, so update the layer */
1019 client_calc_layer(client);
1020 client_setup_decor_and_functions(client);
1021 } else if (msgtype == prop_atoms.net_wm_name ||
1022 msgtype == prop_atoms.wm_name ||
1023 msgtype == prop_atoms.net_wm_icon_name ||
1024 msgtype == prop_atoms.wm_icon_name) {
1025 client_update_title(client);
1026 } else if (msgtype == prop_atoms.wm_class) {
1027 client_update_class(client);
1028 } else if (msgtype == prop_atoms.wm_protocols) {
1029 client_update_protocols(client);
1030 client_setup_decor_and_functions(client);
1031 }
1032 else if (msgtype == prop_atoms.net_wm_strut) {
1033 client_update_strut(client);
1034 }
1035 else if (msgtype == prop_atoms.net_wm_icon ||
1036 msgtype == prop_atoms.kwm_win_icon) {
1037 client_update_icons(client);
1038 }
1039 else if (msgtype == prop_atoms.sm_client_id) {
1040 client_update_sm_client_id(client);
1041 }
1042 default:
1043 ;
1044 #ifdef SHAPE
1045 if (extensions_shape && e->type == extensions_shape_event_basep) {
1046 client->shaped = ((XShapeEvent*)e)->shaped;
1047 frame_adjust_shape(client->frame);
1048 }
1049 #endif
1050 }
1051 }
1052
1053 static void event_handle_dock(ObDock *s, XEvent *e)
1054 {
1055 switch (e->type) {
1056 case ButtonPress:
1057 stacking_raise(DOCK_AS_WINDOW(s));
1058 break;
1059 case EnterNotify:
1060 dock_hide(FALSE);
1061 break;
1062 case LeaveNotify:
1063 dock_hide(TRUE);
1064 break;
1065 }
1066 }
1067
1068 static void event_handle_dockapp(ObDockApp *app, XEvent *e)
1069 {
1070 switch (e->type) {
1071 case MotionNotify:
1072 dock_app_drag(app, &e->xmotion);
1073 break;
1074 case UnmapNotify:
1075 if (app->ignore_unmaps) {
1076 app->ignore_unmaps--;
1077 break;
1078 }
1079 dock_remove(app, TRUE);
1080 break;
1081 case DestroyNotify:
1082 dock_remove(app, FALSE);
1083 break;
1084 case ReparentNotify:
1085 dock_remove(app, FALSE);
1086 break;
1087 case ConfigureNotify:
1088 dock_app_configure(app, e->xconfigure.width, e->xconfigure.height);
1089 break;
1090 }
1091 }
1092
1093 ObMenuFrame* find_active_menu()
1094 {
1095 GList *it;
1096 ObMenuFrame *ret = NULL;
1097
1098 for (it = menu_frame_visible; it; it = g_list_next(it)) {
1099 ret = it->data;
1100 if (ret->selected)
1101 break;
1102 ret = NULL;
1103 }
1104 return ret;
1105 }
1106
1107 ObMenuFrame* find_active_or_last_menu()
1108 {
1109 ObMenuFrame *ret = NULL;
1110
1111 ret = find_active_menu();
1112 if (!ret && menu_frame_visible)
1113 ret = menu_frame_visible->data;
1114 return ret;
1115 }
1116
1117 static void event_handle_menu(XEvent *ev)
1118 {
1119 ObMenuFrame *f;
1120 ObMenuEntryFrame *e;
1121
1122 switch (ev->type) {
1123 case ButtonRelease:
1124 if (menu_can_hide) {
1125 if ((e = menu_entry_frame_under(ev->xbutton.x_root,
1126 ev->xbutton.y_root)))
1127 menu_entry_frame_execute(e, ev->xbutton.state);
1128 else
1129 menu_frame_hide_all();
1130 }
1131 break;
1132 case MotionNotify:
1133 if ((f = menu_frame_under(ev->xmotion.x_root,
1134 ev->xmotion.y_root))) {
1135 menu_frame_move_on_screen(f);
1136 if ((e = menu_entry_frame_under(ev->xmotion.x_root,
1137 ev->xmotion.y_root)))
1138 menu_frame_select(f, e);
1139 }
1140 {
1141 ObMenuFrame *a;
1142
1143 a = find_active_menu();
1144 if (a && a != f &&
1145 a->selected->entry->type != OB_MENU_ENTRY_TYPE_SUBMENU)
1146 {
1147 menu_frame_select(a, NULL);
1148 }
1149 }
1150 break;
1151 case KeyPress:
1152 if (ev->xkey.keycode == ob_keycode(OB_KEY_ESCAPE))
1153 menu_frame_hide_all();
1154 else if (ev->xkey.keycode == ob_keycode(OB_KEY_RETURN)) {
1155 ObMenuFrame *f;
1156 if ((f = find_active_menu()))
1157 menu_entry_frame_execute(f->selected, ev->xkey.state);
1158 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_LEFT)) {
1159 ObMenuFrame *f;
1160 if ((f = find_active_or_last_menu()) && f->parent)
1161 menu_frame_select(f, NULL);
1162 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_RIGHT)) {
1163 ObMenuFrame *f;
1164 if ((f = find_active_or_last_menu()) && f->child)
1165 menu_frame_select_next(f->child);
1166 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_UP)) {
1167 ObMenuFrame *f;
1168 if ((f = find_active_or_last_menu()))
1169 menu_frame_select_previous(f);
1170 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_DOWN)) {
1171 ObMenuFrame *f;
1172 if ((f = find_active_or_last_menu()))
1173 menu_frame_select_next(f);
1174 }
1175 break;
1176 }
1177 }
1178
1179 static gboolean menu_hide_delay_func(gpointer data)
1180 {
1181 menu_can_hide = TRUE;
1182 return FALSE; /* no repeat */
1183 }
1184
1185 static gboolean focus_delay_func(gpointer data)
1186 {
1187 ObClient *c = data;
1188
1189 if (focus_client != c) {
1190 client_focus(c);
1191 if (config_focus_raise)
1192 client_raise(c);
1193 }
1194 return FALSE; /* no repeat */
1195 }
1196
1197 static void focus_delay_client_dest(ObClient *client, gpointer data)
1198 {
1199 ob_main_loop_timeout_remove_data(ob_main_loop, focus_delay_func, client);
1200 }
1201
1202 void event_halt_focus_delay()
1203 {
1204 ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
1205 }
1206
1207 void event_ignore_queued_enters()
1208 {
1209 GSList *saved = NULL, *it;
1210 XEvent *e;
1211
1212 XSync(ob_display, FALSE);
1213
1214 /* count the events */
1215 while (TRUE) {
1216 e = g_new(XEvent, 1);
1217 if (XCheckTypedEvent(ob_display, EnterNotify, e)) {
1218 ObWindow *win;
1219
1220 win = g_hash_table_lookup(window_map, &e->xany.window);
1221 if (win && WINDOW_IS_CLIENT(win))
1222 ++ignore_enter_focus;
1223
1224 saved = g_slist_append(saved, e);
1225 } else {
1226 g_free(e);
1227 break;
1228 }
1229 }
1230 /* put the events back */
1231 for (it = saved; it; it = g_slist_next(it)) {
1232 XPutBackEvent(ob_display, it->data);
1233 g_free(it->data);
1234 }
1235 g_slist_free(saved);
1236 }
This page took 0.090549 seconds and 4 git commands to generate.