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