]> Dogcows Code - chaz/openbox/blob - openbox/event.c
using the ObMainLoop, which rulz the planet
[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 #if 0
263 /* compress events */
264 {
265 XEvent ce;
266 while (XCheckTypedWindowEvent(ob_display, e->xmotion.window,
267 e->type, &ce)) {
268 e->xmotion.x_root = ce.xmotion.x_root;
269 e->xmotion.y_root = ce.xmotion.y_root;
270 }
271 }
272 #endif
273 break;
274 }
275 }
276
277 static gboolean event_ignore(XEvent *e, ObClient *client)
278 {
279 switch(e->type) {
280 case FocusIn:
281 /* NotifyAncestor is not ignored in FocusIn like it is in FocusOut
282 because of RevertToPointerRoot. If the focus ends up reverting to
283 pointer root on a workspace change, then the FocusIn event that we
284 want will be of type NotifyAncestor. This situation does not occur
285 for FocusOut, so it is safely ignored there.
286 */
287 if (INVALID_FOCUSIN(e) ||
288 client == NULL) {
289 #ifdef DEBUG_FOCUS
290 ob_debug("FocusIn on %lx mode %d detail %d IGNORED\n",
291 e->xfocus.window, e->xfocus.mode, e->xfocus.detail);
292 #endif
293 /* says a client was not found for the event (or a valid FocusIn
294 event was not found.
295 */
296 e->xfocus.window = None;
297 return TRUE;
298 }
299
300 #ifdef DEBUG_FOCUS
301 ob_debug("FocusIn on %lx mode %d detail %d\n", e->xfocus.window,
302 e->xfocus.mode, e->xfocus.detail);
303 #endif
304 break;
305 case FocusOut:
306 if (INVALID_FOCUSOUT(e)) {
307 #ifdef DEBUG_FOCUS
308 ob_debug("FocusOut on %lx mode %d detail %d IGNORED\n",
309 e->xfocus.window, e->xfocus.mode, e->xfocus.detail);
310 #endif
311 return TRUE;
312 }
313
314 #ifdef DEBUG_FOCUS
315 ob_debug("FocusOut on %lx mode %d detail %d\n",
316 e->xfocus.window, e->xfocus.mode, e->xfocus.detail);
317 #endif
318
319 {
320 XEvent fe;
321 gboolean fallback = TRUE;
322
323 while (TRUE) {
324 if (!XCheckTypedWindowEvent(ob_display, FocusOut,
325 e->xfocus.window,&fe))
326 if (!XCheckTypedEvent(ob_display, FocusIn, &fe))
327 break;
328 if (fe.type == FocusOut) {
329 #ifdef DEBUG_FOCUS
330 ob_debug("found pending FocusOut");
331 #endif
332 if (!INVALID_FOCUSOUT(&fe)) {
333 /* if there is a VALID FocusOut still coming, don't
334 fallback focus yet, we'll deal with it then */
335 XPutBackEvent(ob_display, &fe);
336 fallback = FALSE;
337 break;
338 }
339 } else {
340 #ifdef DEBUG_FOCUS
341 ob_debug("found pending FocusIn");
342 #endif
343 /* is the focused window getting a FocusOut/In back to
344 itself?
345 */
346 if (fe.xfocus.window == e->xfocus.window &&
347 !event_ignore(&fe, client)) {
348 /*
349 if focus_client is not set, then we can't do
350 this. we need the FocusIn. This happens in the
351 case when the set_focus_client(NULL) in the
352 focus_fallback function fires and then
353 focus_fallback picks the currently focused
354 window (such as on a SendToDesktop-esque action.
355 */
356 if (focus_client) {
357 #ifdef DEBUG_FOCUS
358 ob_debug("focused window got an Out/In back to "
359 "itself IGNORED both");
360 #endif
361 return TRUE;
362 } else {
363 event_process(&fe, NULL);
364 #ifdef DEBUG_FOCUS
365 ob_debug("focused window got an Out/In back to "
366 "itself but focus_client was null "
367 "IGNORED just the Out");
368 #endif
369 return TRUE;
370 }
371 }
372
373 /* once all the FocusOut's have been dealt with, if there
374 is a FocusIn still left and it is valid, then use it */
375 event_process(&fe, NULL);
376 /* secret magic way of event_process telling us that no
377 client was found for the FocusIn event. ^_^ */
378 if (fe.xfocus.window != None) {
379 fallback = FALSE;
380 break;
381 }
382 }
383 }
384 if (fallback) {
385 #ifdef DEBUG_FOCUS
386 ob_debug("no valid FocusIn and no FocusOut events found, "
387 "falling back");
388 #endif
389 focus_fallback(OB_FOCUS_FALLBACK_NOFOCUS);
390 }
391 }
392 break;
393 case EnterNotify:
394 case LeaveNotify:
395 /* NotifyUngrab occurs when a mouse button is released and the event is
396 caused, like when lowering a window */
397 /* NotifyVirtual occurs when ungrabbing the pointer */
398 if (e->xcrossing.mode == NotifyGrab ||
399 e->xcrossing.detail == NotifyInferior ||
400 (e->xcrossing.mode == NotifyUngrab &&
401 e->xcrossing.detail == NotifyVirtual)) {
402 #ifdef DEBUG_FOCUS
403 ob_debug("%sNotify mode %d detail %d on %lx IGNORED",
404 (e->type == EnterNotify ? "Enter" : "Leave"),
405 e->xcrossing.mode,
406 e->xcrossing.detail, client?client->window:0);
407 #endif
408 return TRUE;
409 }
410 #ifdef DEBUG_FOCUS
411 ob_debug("%sNotify mode %d detail %d on %lx",
412 (e->type == EnterNotify ? "Enter" : "Leave"),
413 e->xcrossing.mode,
414 e->xcrossing.detail, client?client->window:0);
415 #endif
416 break;
417 }
418 return FALSE;
419 }
420
421 static void event_process(const XEvent *ec, gpointer data)
422 {
423 Window window;
424 ObClient *client = NULL;
425 ObDock *dock = NULL;
426 ObDockApp *dockapp = NULL;
427 ObWindow *obwin = NULL;
428 XEvent ee, *e;
429
430 /* make a copy we can mangle */
431 ee = *ec;
432 e = &ee;
433
434 window = event_get_window(e);
435 if ((obwin = g_hash_table_lookup(window_map, &window))) {
436 switch (obwin->type) {
437 case Window_Dock:
438 dock = WINDOW_AS_DOCK(obwin);
439 break;
440 case Window_DockApp:
441 dockapp = WINDOW_AS_DOCKAPP(obwin);
442 break;
443 case Window_Client:
444 client = WINDOW_AS_CLIENT(obwin);
445 break;
446 case Window_Menu:
447 case Window_Internal:
448 /* not to be used for events */
449 g_assert_not_reached();
450 break;
451 }
452 }
453
454 event_set_lasttime(e);
455 event_hack_mods(e);
456 if (event_ignore(e, client))
457 return;
458
459 /* deal with it in the kernel */
460 if (client)
461 event_handle_client(client, e);
462 else if (dockapp)
463 event_handle_dockapp(dockapp, e);
464 else if (dock)
465 event_handle_dock(dock, e);
466 else if (window == RootWindow(ob_display, ob_screen))
467 event_handle_root(e);
468 else if (e->type == MapRequest)
469 client_manage(window);
470 else if (e->type == ConfigureRequest) {
471 /* unhandled configure requests must be used to configure the
472 window directly */
473 XWindowChanges xwc;
474
475 xwc.x = e->xconfigurerequest.x;
476 xwc.y = e->xconfigurerequest.y;
477 xwc.width = e->xconfigurerequest.width;
478 xwc.height = e->xconfigurerequest.height;
479 xwc.border_width = e->xconfigurerequest.border_width;
480 xwc.sibling = e->xconfigurerequest.above;
481 xwc.stack_mode = e->xconfigurerequest.detail;
482
483 /* we are not to be held responsible if someone sends us an
484 invalid request! */
485 xerror_set_ignore(TRUE);
486 XConfigureWindow(ob_display, window,
487 e->xconfigurerequest.value_mask, &xwc);
488 xerror_set_ignore(FALSE);
489 }
490
491 /* user input (action-bound) events */
492 if (e->type == ButtonPress || e->type == ButtonRelease ||
493 e->type == MotionNotify || e->type == KeyPress ||
494 e->type == KeyRelease)
495 {
496 if (menu_frame_visible)
497 event_handle_menu(e);
498 else 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, e->type,
851 client->window, &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, e->type,
865 client->window, &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, e->type,
980 client->window, &ce)) {
981 /* XXX: it would be nice to compress ALL changes to a property,
982 not just changes in a row without other props between. */
983 if (ce.xproperty.atom != e->xproperty.atom) {
984 XPutBackEvent(ob_display, &ce);
985 break;
986 }
987 }
988
989 msgtype = e->xproperty.atom;
990 if (msgtype == XA_WM_NORMAL_HINTS) {
991 client_update_normal_hints(client);
992 /* normal hints can make a window non-resizable */
993 client_setup_decor_and_functions(client);
994 }
995 else if (msgtype == XA_WM_HINTS)
996 client_update_wmhints(client);
997 else if (msgtype == XA_WM_TRANSIENT_FOR) {
998 client_update_transient_for(client);
999 client_get_type(client);
1000 /* type may have changed, so update the layer */
1001 client_calc_layer(client);
1002 client_setup_decor_and_functions(client);
1003 }
1004 else if (msgtype == prop_atoms.net_wm_name ||
1005 msgtype == prop_atoms.wm_name ||
1006 msgtype == prop_atoms.net_wm_icon_name ||
1007 msgtype == prop_atoms.wm_icon_name)
1008 client_update_title(client);
1009 else if (msgtype == prop_atoms.wm_class)
1010 client_update_class(client);
1011 else if (msgtype == prop_atoms.wm_protocols) {
1012 client_update_protocols(client);
1013 client_setup_decor_and_functions(client);
1014 }
1015 else if (msgtype == prop_atoms.net_wm_strut) {
1016 client_update_strut(client);
1017 }
1018 else if (msgtype == prop_atoms.net_wm_icon ||
1019 msgtype == prop_atoms.kwm_win_icon)
1020 client_update_icons(client);
1021 default:
1022 ;
1023 #ifdef SHAPE
1024 if (extensions_shape && e->type == extensions_shape_event_basep) {
1025 client->shaped = ((XShapeEvent*)e)->shaped;
1026 frame_adjust_shape(client->frame);
1027 }
1028 #endif
1029 }
1030 }
1031
1032 static void event_handle_dock(ObDock *s, XEvent *e)
1033 {
1034 switch (e->type) {
1035 case ButtonPress:
1036 stacking_raise(DOCK_AS_WINDOW(s));
1037 break;
1038 case EnterNotify:
1039 dock_hide(FALSE);
1040 break;
1041 case LeaveNotify:
1042 dock_hide(TRUE);
1043 break;
1044 }
1045 }
1046
1047 static void event_handle_dockapp(ObDockApp *app, XEvent *e)
1048 {
1049 switch (e->type) {
1050 case MotionNotify:
1051 dock_app_drag(app, &e->xmotion);
1052 break;
1053 case UnmapNotify:
1054 if (app->ignore_unmaps) {
1055 app->ignore_unmaps--;
1056 break;
1057 }
1058 dock_remove(app, TRUE);
1059 break;
1060 case DestroyNotify:
1061 dock_remove(app, FALSE);
1062 break;
1063 case ReparentNotify:
1064 dock_remove(app, FALSE);
1065 break;
1066 case ConfigureNotify:
1067 dock_app_configure(app, e->xconfigure.width, e->xconfigure.height);
1068 break;
1069 }
1070 }
1071
1072 ObMenuFrame* find_active_menu()
1073 {
1074 GList *it;
1075 ObMenuFrame *f;
1076
1077 for (it = menu_frame_visible; it; it = g_list_next(it)) {
1078 f = it->data;
1079 if (f->selected)
1080 break;
1081 }
1082 return it ? it->data : NULL;
1083 }
1084
1085 static void event_handle_menu(XEvent *ev)
1086 {
1087 ObMenuFrame *f;
1088 ObMenuEntryFrame *e;
1089
1090 switch (ev->type) {
1091 case ButtonRelease:
1092 if (!(f = menu_frame_under(ev->xbutton.x_root,
1093 ev->xbutton.y_root)))
1094 menu_frame_hide_all();
1095 else {
1096 if ((e = menu_entry_frame_under(ev->xbutton.x_root,
1097 ev->xbutton.y_root)))
1098 menu_entry_frame_execute(e,
1099 !(ev->xbutton.state & ControlMask));
1100 }
1101 break;
1102 case MotionNotify:
1103 if ((f = menu_frame_under(ev->xmotion.x_root,
1104 ev->xmotion.y_root))) {
1105 menu_frame_move_on_screen(f);
1106 if ((e = menu_entry_frame_under(ev->xmotion.x_root,
1107 ev->xmotion.y_root)))
1108 menu_frame_select(f, e);
1109 }
1110 break;
1111 case KeyPress:
1112 if (ev->xkey.keycode == ob_keycode(OB_KEY_ESCAPE))
1113 menu_frame_hide_all();
1114 else if (ev->xkey.keycode == ob_keycode(OB_KEY_RETURN)) {
1115 ObMenuFrame *f;
1116 if ((f = find_active_menu()))
1117 menu_entry_frame_execute(f->selected,
1118 !(ev->xkey.state & ControlMask));
1119 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_LEFT)) {
1120 ObMenuFrame *f;
1121 if ((f = find_active_menu()) && f->parent)
1122 menu_frame_select(f, NULL);
1123 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_RIGHT)) {
1124 ObMenuFrame *f;
1125 if ((f = find_active_menu()) && f->child)
1126 menu_frame_select_next(f->child);
1127 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_UP)) {
1128 ObMenuFrame *f;
1129 if ((f = find_active_menu()))
1130 menu_frame_select_previous(f);
1131 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_DOWN)) {
1132 ObMenuFrame *f;
1133 if ((f = find_active_menu()))
1134 menu_frame_select_next(f);
1135 }
1136 break;
1137 }
1138 }
This page took 0.09032 seconds and 4 git commands to generate.