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