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