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