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