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