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