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