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