]> Dogcows Code - chaz/openbox/blob - openbox/event.c
699dfdd1a5164f6b762e4f66c0300e74d6eab822
[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) 2006 Mikael Magnusson
5 Copyright (c) 2003-2007 Dana 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 "modkeys.h"
35 #include "propwin.h"
36 #include "mouse.h"
37 #include "mainloop.h"
38 #include "framerender.h"
39 #include "focus.h"
40 #include "moveresize.h"
41 #include "group.h"
42 #include "stacking.h"
43 #include "extensions.h"
44 #include "translate.h"
45
46 #include <X11/Xlib.h>
47 #include <X11/Xatom.h>
48 #include <glib.h>
49
50 #ifdef HAVE_SYS_SELECT_H
51 # include <sys/select.h>
52 #endif
53 #ifdef HAVE_SIGNAL_H
54 # include <signal.h>
55 #endif
56 #ifdef HAVE_UNISTD_H
57 # include <unistd.h> /* for usleep() */
58 #endif
59 #ifdef XKB
60 # include <X11/XKBlib.h>
61 #endif
62
63 #ifdef USE_SM
64 #include <X11/ICE/ICElib.h>
65 #endif
66
67 typedef struct
68 {
69 gboolean ignored;
70 } ObEventData;
71
72 typedef struct
73 {
74 ObClient *client;
75 Time time;
76 } ObFocusDelayData;
77
78 static void event_process(const XEvent *e, gpointer data);
79 static void event_handle_root(XEvent *e);
80 static gboolean event_handle_menu_keyboard(XEvent *e);
81 static gboolean event_handle_menu(XEvent *e);
82 static void event_handle_dock(ObDock *s, XEvent *e);
83 static void event_handle_dockapp(ObDockApp *app, XEvent *e);
84 static void event_handle_client(ObClient *c, XEvent *e);
85 static void event_handle_user_time_window_clients(GSList *l, XEvent *e);
86 static void event_handle_user_input(ObClient *client, XEvent *e);
87
88 static void focus_delay_dest(gpointer data);
89 static gboolean focus_delay_cmp(gconstpointer d1, gconstpointer d2);
90 static gboolean focus_delay_func(gpointer data);
91 static void focus_delay_client_dest(ObClient *client, gpointer data);
92
93 static gboolean menu_hide_delay_func(gpointer data);
94
95 /* The time for the current event being processed */
96 Time event_curtime = CurrentTime;
97
98 static guint ignore_enter_focus = 0;
99 static gboolean menu_can_hide;
100 static gboolean focus_left_screen = FALSE;
101 /*! This variable is used for focus fallback. If we fallback to a window, we
102 set this to the window. And when focus goes somewhere after that, it will
103 be set to NULL. If between falling back to that window and something
104 getting focused, the window gets unmanaged, then if there are no incoming
105 FocusIn events, we fallback again because focus has just gotten itself lost.
106 */
107 static ObClient *focus_tried = NULL;
108
109 #ifdef USE_SM
110 static void ice_handler(gint fd, gpointer conn)
111 {
112 Bool b;
113 IceProcessMessages(conn, NULL, &b);
114 }
115
116 static void ice_watch(IceConn conn, IcePointer data, Bool opening,
117 IcePointer *watch_data)
118 {
119 static gint fd = -1;
120
121 if (opening) {
122 fd = IceConnectionNumber(conn);
123 ob_main_loop_fd_add(ob_main_loop, fd, ice_handler, conn, NULL);
124 } else {
125 ob_main_loop_fd_remove(ob_main_loop, fd);
126 fd = -1;
127 }
128 }
129 #endif
130
131 void event_startup(gboolean reconfig)
132 {
133 if (reconfig) return;
134
135 ob_main_loop_x_add(ob_main_loop, event_process, NULL, NULL);
136
137 #ifdef USE_SM
138 IceAddConnectionWatch(ice_watch, NULL);
139 #endif
140
141 client_add_destructor(focus_delay_client_dest, NULL);
142 }
143
144 void event_shutdown(gboolean reconfig)
145 {
146 if (reconfig) return;
147
148 #ifdef USE_SM
149 IceRemoveConnectionWatch(ice_watch, NULL);
150 #endif
151
152 client_remove_destructor(focus_delay_client_dest);
153 }
154
155 static Window event_get_window(XEvent *e)
156 {
157 Window window;
158
159 /* pick a window */
160 switch (e->type) {
161 case SelectionClear:
162 window = RootWindow(ob_display, ob_screen);
163 break;
164 case MapRequest:
165 window = e->xmap.window;
166 break;
167 case UnmapNotify:
168 window = e->xunmap.window;
169 break;
170 case DestroyNotify:
171 window = e->xdestroywindow.window;
172 break;
173 case ConfigureRequest:
174 window = e->xconfigurerequest.window;
175 break;
176 case ConfigureNotify:
177 window = e->xconfigure.window;
178 break;
179 default:
180 #ifdef XKB
181 if (extensions_xkb && e->type == extensions_xkb_event_basep) {
182 switch (((XkbAnyEvent*)e)->xkb_type) {
183 case XkbBellNotify:
184 window = ((XkbBellNotifyEvent*)e)->window;
185 default:
186 window = None;
187 }
188 } else
189 #endif
190 #ifdef SYNC
191 if (extensions_sync &&
192 e->type == extensions_sync_event_basep + XSyncAlarmNotify)
193 {
194 window = None;
195 } else
196 #endif
197 window = e->xany.window;
198 }
199 return window;
200 }
201
202 static void event_set_curtime(XEvent *e)
203 {
204 Time t = CurrentTime;
205
206 /* grab the lasttime and hack up the state */
207 switch (e->type) {
208 case ButtonPress:
209 case ButtonRelease:
210 t = e->xbutton.time;
211 break;
212 case KeyPress:
213 t = e->xkey.time;
214 break;
215 case KeyRelease:
216 t = e->xkey.time;
217 break;
218 case MotionNotify:
219 t = e->xmotion.time;
220 break;
221 case PropertyNotify:
222 t = e->xproperty.time;
223 break;
224 case EnterNotify:
225 case LeaveNotify:
226 t = e->xcrossing.time;
227 break;
228 default:
229 #ifdef SYNC
230 if (extensions_sync &&
231 e->type == extensions_sync_event_basep + XSyncAlarmNotify)
232 {
233 t = ((XSyncAlarmNotifyEvent*)e)->time;
234 }
235 #endif
236 /* if more event types are anticipated, get their timestamp
237 explicitly */
238 break;
239 }
240
241 event_curtime = t;
242 }
243
244 static void event_hack_mods(XEvent *e)
245 {
246 #ifdef XKB
247 XkbStateRec xkb_state;
248 #endif
249
250 switch (e->type) {
251 case ButtonPress:
252 case ButtonRelease:
253 e->xbutton.state = modkeys_only_modifier_masks(e->xbutton.state);
254 break;
255 case KeyPress:
256 e->xkey.state = modkeys_only_modifier_masks(e->xkey.state);
257 break;
258 case KeyRelease:
259 e->xkey.state = modkeys_only_modifier_masks(e->xkey.state);
260 #ifdef XKB
261 if (XkbGetState(ob_display, XkbUseCoreKbd, &xkb_state) == Success) {
262 e->xkey.state = xkb_state.compat_state;
263 break;
264 }
265 #endif
266 /* remove from the state the mask of the modifier key being released,
267 if it is a modifier key being released that is */
268 e->xkey.state &= ~modkeys_keycode_to_mask(e->xkey.keycode);
269 break;
270 case MotionNotify:
271 e->xmotion.state = modkeys_only_modifier_masks(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 wanted_focusevent(XEvent *e, gboolean in_client_only)
286 {
287 gint mode = e->xfocus.mode;
288 gint detail = e->xfocus.detail;
289 Window win = e->xany.window;
290
291 if (e->type == FocusIn) {
292 /* These are ones we never want.. */
293
294 /* This means focus was given by a keyboard/mouse grab. */
295 if (mode == NotifyGrab)
296 return FALSE;
297 /* This means focus was given back from a keyboard/mouse grab. */
298 if (mode == NotifyUngrab)
299 return FALSE;
300
301 /* These are the ones we want.. */
302
303 if (win == RootWindow(ob_display, ob_screen)) {
304 /* If looking for a focus in on a client, then always return
305 FALSE for focus in's to the root window */
306 if (in_client_only)
307 return FALSE;
308 /* This means focus reverted off of a client */
309 else if (detail == NotifyPointerRoot ||
310 detail == NotifyDetailNone ||
311 detail == NotifyInferior)
312 return TRUE;
313 else
314 return FALSE;
315 }
316
317 /* This means focus moved from the root window to a client */
318 if (detail == NotifyVirtual)
319 return TRUE;
320 /* This means focus moved from one client to another */
321 if (detail == NotifyNonlinearVirtual)
322 return TRUE;
323 /* This means focus moved to the frame window */
324 if (detail == NotifyInferior && !in_client_only)
325 return TRUE;
326
327 /* Otherwise.. */
328 return FALSE;
329 } else {
330 g_assert(e->type == FocusOut);
331
332 /* These are ones we never want.. */
333
334 /* This means focus was taken by a keyboard/mouse grab. */
335 if (mode == NotifyGrab)
336 return FALSE;
337
338 /* Focus left the root window revertedto state */
339 if (win == RootWindow(ob_display, ob_screen))
340 return FALSE;
341
342 /* These are the ones we want.. */
343
344 /* This means focus moved from a client to the root window */
345 if (detail == NotifyVirtual)
346 return TRUE;
347 /* This means focus moved from one client to another */
348 if (detail == NotifyNonlinearVirtual)
349 return TRUE;
350 /* This means focus had moved to our frame window and now moved off */
351 if (detail == NotifyNonlinear)
352 return TRUE;
353
354 /* Otherwise.. */
355 return FALSE;
356 }
357 }
358
359 static Bool look_for_focusin(Display *d, XEvent *e, XPointer arg)
360 {
361 return e->type == FocusIn && wanted_focusevent(e, FALSE);
362 }
363
364 static Bool look_for_focusin_client(Display *d, XEvent *e, XPointer arg)
365 {
366 return e->type == FocusIn && wanted_focusevent(e, TRUE);
367 }
368
369 static void print_focusevent(XEvent *e)
370 {
371 gint mode = e->xfocus.mode;
372 gint detail = e->xfocus.detail;
373 Window win = e->xany.window;
374 const gchar *modestr, *detailstr;
375
376 switch (mode) {
377 case NotifyNormal: modestr="NotifyNormal"; break;
378 case NotifyGrab: modestr="NotifyGrab"; break;
379 case NotifyUngrab: modestr="NotifyUngrab"; break;
380 case NotifyWhileGrabbed: modestr="NotifyWhileGrabbed"; break;
381 }
382 switch (detail) {
383 case NotifyAncestor: detailstr="NotifyAncestor"; break;
384 case NotifyVirtual: detailstr="NotifyVirtual"; break;
385 case NotifyInferior: detailstr="NotifyInferior"; break;
386 case NotifyNonlinear: detailstr="NotifyNonlinear"; break;
387 case NotifyNonlinearVirtual: detailstr="NotifyNonlinearVirtual"; break;
388 case NotifyPointer: detailstr="NotifyPointer"; break;
389 case NotifyPointerRoot: detailstr="NotifyPointerRoot"; break;
390 case NotifyDetailNone: detailstr="NotifyDetailNone"; break;
391 }
392
393 g_assert(modestr);
394 g_assert(detailstr);
395 ob_debug_type(OB_DEBUG_FOCUS, "Focus%s 0x%x mode=%s detail=%s\n",
396 (e->xfocus.type == FocusIn ? "In" : "Out"),
397 win,
398 modestr, detailstr);
399
400 }
401
402 static gboolean event_ignore(XEvent *e, ObClient *client)
403 {
404 switch(e->type) {
405 case FocusIn:
406 print_focusevent(e);
407 if (!wanted_focusevent(e, FALSE))
408 return TRUE;
409 break;
410 case FocusOut:
411 print_focusevent(e);
412 if (!wanted_focusevent(e, FALSE))
413 return TRUE;
414 break;
415 }
416 return FALSE;
417 }
418
419 static void event_process(const XEvent *ec, gpointer data)
420 {
421 Window window;
422 ObClient *client = NULL;
423 ObDock *dock = NULL;
424 ObDockApp *dockapp = NULL;
425 ObWindow *obwin = NULL;
426 GSList *timewinclients = NULL;
427 XEvent ee, *e;
428 ObEventData *ed = data;
429
430 /* make a copy we can mangle */
431 ee = *ec;
432 e = &ee;
433
434 window = event_get_window(e);
435 if (e->type != PropertyNotify ||
436 !(timewinclients = propwin_get_clients(window,
437 OB_PROPWIN_USER_TIME)))
438 if ((obwin = g_hash_table_lookup(window_map, &window))) {
439 switch (obwin->type) {
440 case Window_Dock:
441 dock = WINDOW_AS_DOCK(obwin);
442 break;
443 case Window_DockApp:
444 dockapp = WINDOW_AS_DOCKAPP(obwin);
445 break;
446 case Window_Client:
447 client = WINDOW_AS_CLIENT(obwin);
448 break;
449 case Window_Menu:
450 case Window_Internal:
451 /* not to be used for events */
452 g_assert_not_reached();
453 break;
454 }
455 }
456
457 event_set_curtime(e);
458 event_hack_mods(e);
459 if (event_ignore(e, client)) {
460 if (ed)
461 ed->ignored = TRUE;
462 return;
463 } else if (ed)
464 ed->ignored = FALSE;
465
466 /* deal with it in the kernel */
467
468 if (menu_frame_visible &&
469 (e->type == EnterNotify || e->type == LeaveNotify))
470 {
471 /* crossing events for menu */
472 event_handle_menu(e);
473 } else if (e->type == FocusIn) {
474 if (e->xfocus.detail == NotifyPointerRoot ||
475 e->xfocus.detail == NotifyDetailNone ||
476 e->xfocus.detail == NotifyInferior)
477 {
478 XEvent ce;
479 ob_debug_type(OB_DEBUG_FOCUS,
480 "Focus went to pointer root/none or to our frame "
481 "window\n");
482
483 /* If another FocusIn is in the queue then don't fallback yet. This
484 fixes the fun case of:
485 window map -> send focusin
486 window unmap -> get focusout
487 window map -> send focusin
488 get first focus out -> fall back to something (new window
489 hasn't received focus yet, so something else) -> send focusin
490 which means the "something else" is the last thing to get a
491 focusin sent to it, so the new window doesn't end up with focus.
492
493 But if the other focus in is something like PointerRoot then we
494 still want to fall back.
495 */
496 if (XCheckIfEvent(ob_display, &ce, look_for_focusin_client, NULL)){
497 XPutBackEvent(ob_display, &ce);
498 ob_debug_type(OB_DEBUG_FOCUS,
499 " but another FocusIn is coming\n");
500 } else {
501 /* Focus has been reverted to the root window, nothing, or to
502 our frame window.
503
504 FocusOut events come after UnmapNotify, so we don't need to
505 worry about focusing an invalid window
506 */
507
508 /* In this case we know focus is in our screen */
509 if (e->xfocus.detail == NotifyInferior)
510 focus_left_screen = FALSE;
511
512 if (!focus_left_screen)
513 focus_tried = focus_fallback(TRUE);
514 }
515 } else if (client && client != focus_client) {
516 focus_left_screen = FALSE;
517 frame_adjust_focus(client->frame, TRUE);
518 focus_set_client(client);
519 client_calc_layer(client);
520 client_bring_helper_windows(client);
521
522 focus_tried = NULL; /* focus isn't "trying" to go anywhere now */
523 }
524 } else if (e->type == FocusOut) {
525 gboolean nomove = FALSE;
526 XEvent ce;
527
528 /* Look for the followup FocusIn */
529 if (!XCheckIfEvent(ob_display, &ce, look_for_focusin, NULL)) {
530 /* There is no FocusIn, this means focus went to a window that
531 is not being managed, or a window on another screen. */
532 Window win, root;
533 gint i;
534 guint u;
535 xerror_set_ignore(TRUE);
536 if (XGetInputFocus(ob_display, &win, &i) != 0 &&
537 XGetGeometry(ob_display, win, &root, &i,&i,&u,&u,&u,&u) != 0 &&
538 root != RootWindow(ob_display, ob_screen))
539 {
540 ob_debug_type(OB_DEBUG_FOCUS,
541 "Focus went to another screen !\n");
542 focus_left_screen = TRUE;
543 }
544 else
545 ob_debug_type(OB_DEBUG_FOCUS,
546 "Focus went to a black hole !\n");
547 xerror_set_ignore(FALSE);
548 /* nothing is focused */
549 focus_set_client(NULL);
550 } else if (ce.xany.window == e->xany.window) {
551 ob_debug_type(OB_DEBUG_FOCUS, "Focus didn't go anywhere\n");
552 /* If focus didn't actually move anywhere, there is nothing to do*/
553 nomove = TRUE;
554 } else {
555 /* Focus did move, so process the FocusIn event */
556 ObEventData ed = { .ignored = FALSE };
557 event_process(&ce, &ed);
558 if (ed.ignored) {
559 /* The FocusIn was ignored, this means it was on a window
560 that isn't a client. */
561 ob_debug_type(OB_DEBUG_FOCUS,
562 "Focus went to an unmanaged window 0x%x !\n",
563 ce.xfocus.window);
564 focus_tried = focus_fallback(TRUE);
565 }
566 }
567
568 if (client && !nomove) {
569 frame_adjust_focus(client->frame, FALSE);
570 /* focus_set_client has already been called for sure */
571 client_calc_layer(client);
572 }
573 } else if (timewinclients)
574 event_handle_user_time_window_clients(timewinclients, e);
575 else if (client)
576 event_handle_client(client, e);
577 else if (dockapp)
578 event_handle_dockapp(dockapp, e);
579 else if (dock)
580 event_handle_dock(dock, e);
581 else if (window == RootWindow(ob_display, ob_screen))
582 event_handle_root(e);
583 else if (e->type == MapRequest)
584 client_manage(window);
585 else if (e->type == ClientMessage) {
586 /* This is for _NET_WM_REQUEST_FRAME_EXTENTS messages. They come for
587 windows that are not managed yet. */
588 if (e->xclient.message_type == prop_atoms.net_request_frame_extents) {
589 /* Pretend to manage the client, getting information used to
590 determine its decorations */
591 ObClient *c = client_fake_manage(e->xclient.window);
592 gulong vals[4];
593
594 /* set the frame extents on the window */
595 vals[0] = c->frame->size.left;
596 vals[1] = c->frame->size.right;
597 vals[2] = c->frame->size.top;
598 vals[3] = c->frame->size.bottom;
599 PROP_SETA32(e->xclient.window, net_frame_extents,
600 cardinal, vals, 4);
601
602 /* Free the pretend client */
603 client_fake_unmanage(c);
604 }
605 }
606 else if (e->type == ConfigureRequest) {
607 /* unhandled configure requests must be used to configure the
608 window directly */
609 XWindowChanges xwc;
610
611 xwc.x = e->xconfigurerequest.x;
612 xwc.y = e->xconfigurerequest.y;
613 xwc.width = e->xconfigurerequest.width;
614 xwc.height = e->xconfigurerequest.height;
615 xwc.border_width = e->xconfigurerequest.border_width;
616 xwc.sibling = e->xconfigurerequest.above;
617 xwc.stack_mode = e->xconfigurerequest.detail;
618
619 /* we are not to be held responsible if someone sends us an
620 invalid request! */
621 xerror_set_ignore(TRUE);
622 XConfigureWindow(ob_display, window,
623 e->xconfigurerequest.value_mask, &xwc);
624 xerror_set_ignore(FALSE);
625 }
626 #ifdef SYNC
627 else if (extensions_sync &&
628 e->type == extensions_sync_event_basep + XSyncAlarmNotify)
629 {
630 XSyncAlarmNotifyEvent *se = (XSyncAlarmNotifyEvent*)e;
631 if (se->alarm == moveresize_alarm && moveresize_in_progress)
632 moveresize_event(e);
633 }
634 #endif
635
636 if (e->type == ButtonPress || e->type == ButtonRelease ||
637 e->type == MotionNotify || e->type == KeyPress ||
638 e->type == KeyRelease)
639 {
640 event_handle_user_input(client, e);
641 }
642
643 /* if something happens and it's not from an XEvent, then we don't know
644 the time */
645 event_curtime = CurrentTime;
646 }
647
648 static void event_handle_root(XEvent *e)
649 {
650 Atom msgtype;
651
652 switch(e->type) {
653 case SelectionClear:
654 ob_debug("Another WM has requested to replace us. Exiting.\n");
655 ob_exit_replace();
656 break;
657
658 case ClientMessage:
659 if (e->xclient.format != 32) break;
660
661 msgtype = e->xclient.message_type;
662 if (msgtype == prop_atoms.net_current_desktop) {
663 guint d = e->xclient.data.l[0];
664 if (d < screen_num_desktops) {
665 event_curtime = e->xclient.data.l[1];
666 if (event_curtime == 0)
667 ob_debug_type(OB_DEBUG_APP_BUGS,
668 "_NET_CURRENT_DESKTOP message is missing "
669 "a timestamp\n");
670 screen_set_desktop(d, TRUE);
671 }
672 } else if (msgtype == prop_atoms.net_number_of_desktops) {
673 guint d = e->xclient.data.l[0];
674 if (d > 0)
675 screen_set_num_desktops(d);
676 } else if (msgtype == prop_atoms.net_showing_desktop) {
677 screen_show_desktop(e->xclient.data.l[0] != 0, NULL);
678 } else if (msgtype == prop_atoms.openbox_control) {
679 if (e->xclient.data.l[0] == 1)
680 ob_reconfigure();
681 else if (e->xclient.data.l[0] == 2)
682 ob_restart();
683 }
684 break;
685 case PropertyNotify:
686 if (e->xproperty.atom == prop_atoms.net_desktop_names)
687 screen_update_desktop_names();
688 else if (e->xproperty.atom == prop_atoms.net_desktop_layout)
689 screen_update_layout();
690 break;
691 case ConfigureNotify:
692 #ifdef XRANDR
693 XRRUpdateConfiguration(e);
694 #endif
695 screen_resize();
696 break;
697 default:
698 ;
699 }
700 }
701
702 void event_enter_client(ObClient *client)
703 {
704 g_assert(config_focus_follow);
705
706 if (client_enter_focusable(client) && client_can_focus(client)) {
707 if (config_focus_delay) {
708 ObFocusDelayData *data;
709
710 ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
711
712 data = g_new(ObFocusDelayData, 1);
713 data->client = client;
714 data->time = event_curtime;
715
716 ob_main_loop_timeout_add(ob_main_loop,
717 config_focus_delay,
718 focus_delay_func,
719 data, focus_delay_cmp, focus_delay_dest);
720 } else {
721 ObFocusDelayData data;
722 data.client = client;
723 data.time = event_curtime;
724 focus_delay_func(&data);
725 }
726 }
727 }
728
729 static void event_handle_user_time_window_clients(GSList *l, XEvent *e)
730 {
731 g_assert(e->type == PropertyNotify);
732 if (e->xproperty.atom == prop_atoms.net_wm_user_time) {
733 for (; l; l = g_slist_next(l))
734 client_update_user_time(l->data);
735 }
736 }
737
738 static void event_handle_client(ObClient *client, XEvent *e)
739 {
740 XEvent ce;
741 Atom msgtype;
742 ObFrameContext con;
743 static gint px = -1, py = -1;
744 static guint pb = 0;
745
746 switch (e->type) {
747 case ButtonPress:
748 /* save where the press occured for the first button pressed */
749 if (!pb) {
750 pb = e->xbutton.button;
751 px = e->xbutton.x;
752 py = e->xbutton.y;
753 }
754 case ButtonRelease:
755 /* Wheel buttons don't draw because they are an instant click, so it
756 is a waste of resources to go drawing it.
757 if the user is doing an intereactive thing, or has a menu open then
758 the mouse is grabbed (possibly) and if we get these events we don't
759 want to deal with them
760 */
761 if (!(e->xbutton.button == 4 || e->xbutton.button == 5) &&
762 !keyboard_interactively_grabbed() &&
763 !menu_frame_visible)
764 {
765 /* use where the press occured */
766 con = frame_context(client, e->xbutton.window, px, py);
767 con = mouse_button_frame_context(con, e->xbutton.button);
768
769 if (e->type == ButtonRelease && e->xbutton.button == pb)
770 pb = 0, px = py = -1;
771
772 switch (con) {
773 case OB_FRAME_CONTEXT_MAXIMIZE:
774 client->frame->max_press = (e->type == ButtonPress);
775 framerender_frame(client->frame);
776 break;
777 case OB_FRAME_CONTEXT_CLOSE:
778 client->frame->close_press = (e->type == ButtonPress);
779 framerender_frame(client->frame);
780 break;
781 case OB_FRAME_CONTEXT_ICONIFY:
782 client->frame->iconify_press = (e->type == ButtonPress);
783 framerender_frame(client->frame);
784 break;
785 case OB_FRAME_CONTEXT_ALLDESKTOPS:
786 client->frame->desk_press = (e->type == ButtonPress);
787 framerender_frame(client->frame);
788 break;
789 case OB_FRAME_CONTEXT_SHADE:
790 client->frame->shade_press = (e->type == ButtonPress);
791 framerender_frame(client->frame);
792 break;
793 default:
794 /* nothing changes with clicks for any other contexts */
795 break;
796 }
797 }
798 break;
799 case MotionNotify:
800 con = frame_context(client, e->xmotion.window,
801 e->xmotion.x, e->xmotion.y);
802 switch (con) {
803 case OB_FRAME_CONTEXT_TITLEBAR:
804 /* we've left the button area inside the titlebar */
805 if (client->frame->max_hover || client->frame->desk_hover ||
806 client->frame->shade_hover || client->frame->iconify_hover ||
807 client->frame->close_hover)
808 {
809 client->frame->max_hover = FALSE;
810 client->frame->desk_hover = FALSE;
811 client->frame->shade_hover = FALSE;
812 client->frame->iconify_hover = FALSE;
813 client->frame->close_hover = FALSE;
814 frame_adjust_state(client->frame);
815 }
816 break;
817 case OB_FRAME_CONTEXT_MAXIMIZE:
818 if (!client->frame->max_hover) {
819 client->frame->max_hover = TRUE;
820 frame_adjust_state(client->frame);
821 }
822 break;
823 case OB_FRAME_CONTEXT_ALLDESKTOPS:
824 if (!client->frame->desk_hover) {
825 client->frame->desk_hover = TRUE;
826 frame_adjust_state(client->frame);
827 }
828 break;
829 case OB_FRAME_CONTEXT_SHADE:
830 if (!client->frame->shade_hover) {
831 client->frame->shade_hover = TRUE;
832 frame_adjust_state(client->frame);
833 }
834 break;
835 case OB_FRAME_CONTEXT_ICONIFY:
836 if (!client->frame->iconify_hover) {
837 client->frame->iconify_hover = TRUE;
838 frame_adjust_state(client->frame);
839 }
840 break;
841 case OB_FRAME_CONTEXT_CLOSE:
842 if (!client->frame->close_hover) {
843 client->frame->close_hover = TRUE;
844 frame_adjust_state(client->frame);
845 }
846 break;
847 default:
848 break;
849 }
850 break;
851 case LeaveNotify:
852 con = frame_context(client, e->xcrossing.window,
853 e->xcrossing.x, e->xcrossing.y);
854 switch (con) {
855 case OB_FRAME_CONTEXT_MAXIMIZE:
856 client->frame->max_hover = FALSE;
857 frame_adjust_state(client->frame);
858 break;
859 case OB_FRAME_CONTEXT_ALLDESKTOPS:
860 client->frame->desk_hover = FALSE;
861 frame_adjust_state(client->frame);
862 break;
863 case OB_FRAME_CONTEXT_SHADE:
864 client->frame->shade_hover = FALSE;
865 frame_adjust_state(client->frame);
866 break;
867 case OB_FRAME_CONTEXT_ICONIFY:
868 client->frame->iconify_hover = FALSE;
869 frame_adjust_state(client->frame);
870 break;
871 case OB_FRAME_CONTEXT_CLOSE:
872 client->frame->close_hover = FALSE;
873 frame_adjust_state(client->frame);
874 break;
875 case OB_FRAME_CONTEXT_FRAME:
876 /* When the mouse leaves an animating window, don't use the
877 corresponding enter events. Pretend like the animating window
878 doesn't even exist..! */
879 if (frame_iconify_animating(client->frame))
880 event_ignore_queued_enters();
881
882 ob_debug_type(OB_DEBUG_FOCUS,
883 "%sNotify mode %d detail %d on %lx\n",
884 (e->type == EnterNotify ? "Enter" : "Leave"),
885 e->xcrossing.mode,
886 e->xcrossing.detail, (client?client->window:0));
887 if (keyboard_interactively_grabbed())
888 break;
889 if (config_focus_follow && config_focus_delay &&
890 /* leave inferior events can happen when the mouse goes onto
891 the window's border and then into the window before the
892 delay is up */
893 e->xcrossing.detail != NotifyInferior)
894 {
895 ob_main_loop_timeout_remove_data(ob_main_loop,
896 focus_delay_func,
897 client, FALSE);
898 }
899 break;
900 default:
901 break;
902 }
903 break;
904 case EnterNotify:
905 {
906 gboolean nofocus = FALSE;
907
908 if (ignore_enter_focus) {
909 ignore_enter_focus--;
910 nofocus = TRUE;
911 }
912
913 con = frame_context(client, e->xcrossing.window,
914 e->xcrossing.x, e->xcrossing.y);
915 switch (con) {
916 case OB_FRAME_CONTEXT_MAXIMIZE:
917 client->frame->max_hover = TRUE;
918 frame_adjust_state(client->frame);
919 break;
920 case OB_FRAME_CONTEXT_ALLDESKTOPS:
921 client->frame->desk_hover = TRUE;
922 frame_adjust_state(client->frame);
923 break;
924 case OB_FRAME_CONTEXT_SHADE:
925 client->frame->shade_hover = TRUE;
926 frame_adjust_state(client->frame);
927 break;
928 case OB_FRAME_CONTEXT_ICONIFY:
929 client->frame->iconify_hover = TRUE;
930 frame_adjust_state(client->frame);
931 break;
932 case OB_FRAME_CONTEXT_CLOSE:
933 client->frame->close_hover = TRUE;
934 frame_adjust_state(client->frame);
935 break;
936 case OB_FRAME_CONTEXT_FRAME:
937 if (keyboard_interactively_grabbed())
938 break;
939 if (e->xcrossing.mode == NotifyGrab ||
940 e->xcrossing.mode == NotifyUngrab ||
941 /*ignore enters when we're already in the window */
942 e->xcrossing.detail == NotifyInferior)
943 {
944 ob_debug_type(OB_DEBUG_FOCUS,
945 "%sNotify mode %d detail %d on %lx IGNORED\n",
946 (e->type == EnterNotify ? "Enter" : "Leave"),
947 e->xcrossing.mode,
948 e->xcrossing.detail, client?client->window:0);
949 } else {
950 ob_debug_type(OB_DEBUG_FOCUS,
951 "%sNotify mode %d detail %d on %lx, "
952 "focusing window: %d\n",
953 (e->type == EnterNotify ? "Enter" : "Leave"),
954 e->xcrossing.mode,
955 e->xcrossing.detail, (client?client->window:0),
956 !nofocus);
957 if (!nofocus && config_focus_follow)
958 event_enter_client(client);
959 }
960 break;
961 default:
962 break;
963 }
964 break;
965 }
966 case ConfigureRequest:
967 {
968 /* dont compress these unless you're going to watch for property
969 notifies in between (these can change what the configure would
970 do to the window).
971 also you can't compress stacking events
972 */
973
974 gint x, y, w, h;
975
976 /* if nothing is changed, then a configurenotify is needed */
977 gboolean config = TRUE;
978
979 x = client->area.x;
980 y = client->area.y;
981 w = client->area.width;
982 h = client->area.height;
983
984 ob_debug("ConfigureRequest desktop %d wmstate %d visibile %d\n",
985 screen_desktop, client->wmstate, client->frame->visible);
986
987 if (e->xconfigurerequest.value_mask & CWBorderWidth)
988 if (client->border_width != e->xconfigurerequest.border_width) {
989 client->border_width = e->xconfigurerequest.border_width;
990 /* if only the border width is changing, then it's not needed*/
991 config = FALSE;
992 }
993
994
995 if (e->xconfigurerequest.value_mask & CWStackMode) {
996 ObClient *sibling = NULL;
997
998 /* get the sibling */
999 if (e->xconfigurerequest.value_mask & CWSibling) {
1000 ObWindow *win;
1001 win = g_hash_table_lookup(window_map,
1002 &e->xconfigurerequest.above);
1003 if (WINDOW_IS_CLIENT(win) && WINDOW_AS_CLIENT(win) != client)
1004 sibling = WINDOW_AS_CLIENT(win);
1005 }
1006
1007 /* activate it rather than just focus it */
1008 stacking_restack_request(client, sibling,
1009 e->xconfigurerequest.detail, TRUE);
1010
1011 /* if a stacking change is requested then it is needed */
1012 config = TRUE;
1013 }
1014
1015 /* don't allow clients to move shaded windows (fvwm does this) */
1016 if (client->shaded && (e->xconfigurerequest.value_mask & CWX ||
1017 e->xconfigurerequest.value_mask & CWY))
1018 {
1019 e->xconfigurerequest.value_mask &= ~CWX;
1020 e->xconfigurerequest.value_mask &= ~CWY;
1021
1022 /* if the client tried to move and we aren't letting it then a
1023 synthetic event is needed */
1024 config = TRUE;
1025 }
1026
1027 if (e->xconfigurerequest.value_mask & CWX ||
1028 e->xconfigurerequest.value_mask & CWY ||
1029 e->xconfigurerequest.value_mask & CWWidth ||
1030 e->xconfigurerequest.value_mask & CWHeight)
1031 {
1032 if (e->xconfigurerequest.value_mask & CWX)
1033 x = e->xconfigurerequest.x;
1034 if (e->xconfigurerequest.value_mask & CWY)
1035 y = e->xconfigurerequest.y;
1036 if (e->xconfigurerequest.value_mask & CWWidth)
1037 w = e->xconfigurerequest.width;
1038 if (e->xconfigurerequest.value_mask & CWHeight)
1039 h = e->xconfigurerequest.height;
1040
1041 /* if a new position or size is requested, then a configure is
1042 needed */
1043 config = TRUE;
1044 }
1045
1046 ob_debug("ConfigureRequest x(%d) %d y(%d) %d w(%d) %d h(%d) %d\n",
1047 e->xconfigurerequest.value_mask & CWX, x,
1048 e->xconfigurerequest.value_mask & CWY, y,
1049 e->xconfigurerequest.value_mask & CWWidth, w,
1050 e->xconfigurerequest.value_mask & CWHeight, h);
1051
1052 /* check for broken apps moving to their root position
1053
1054 XXX remove this some day...that would be nice. right now all
1055 kde apps do this when they try activate themselves on another
1056 desktop. eg. open amarok window on desktop 1, switch to desktop
1057 2, click amarok tray icon. it will move by its decoration size.
1058 */
1059 if (x != client->area.x &&
1060 x == (client->frame->area.x + client->frame->size.left -
1061 (gint)client->border_width) &&
1062 y != client->area.y &&
1063 y == (client->frame->area.y + client->frame->size.top -
1064 (gint)client->border_width))
1065 {
1066 ob_debug_type(OB_DEBUG_APP_BUGS,
1067 "Application %s is trying to move via "
1068 "ConfigureRequest to it's root window position "
1069 "but it is not using StaticGravity\n",
1070 client->title);
1071 /* don't move it */
1072 x = client->area.x;
1073 y = client->area.y;
1074 }
1075
1076 if (config) {
1077 client_find_onscreen(client, &x, &y, w, h, FALSE);
1078 client_configure_full(client, x, y, w, h, FALSE, TRUE);
1079 }
1080 break;
1081 }
1082 case UnmapNotify:
1083 if (client->ignore_unmaps) {
1084 client->ignore_unmaps--;
1085 break;
1086 }
1087 ob_debug("UnmapNotify for window 0x%x eventwin 0x%x sendevent %d "
1088 "ignores left %d\n",
1089 client->window, e->xunmap.event, e->xunmap.from_configure,
1090 client->ignore_unmaps);
1091 client_unmanage(client);
1092
1093 /* we were trying to focus this window but it's gone */
1094 if (client == focus_tried) {
1095 ob_debug_type(OB_DEBUG_FOCUS, "Tried to focus window 0x%x and it "
1096 "is being unmanaged:\n");
1097 if (XCheckIfEvent(ob_display, &ce, look_for_focusin_client, NULL)){
1098 XPutBackEvent(ob_display, &ce);
1099 ob_debug_type(OB_DEBUG_FOCUS,
1100 " but another FocusIn is coming\n");
1101 } else {
1102 ob_debug_type(OB_DEBUG_FOCUS,
1103 " so falling back focus again.\n");
1104 focus_tried = focus_fallback(TRUE);
1105 }
1106 }
1107 break;
1108 case DestroyNotify:
1109 ob_debug("DestroyNotify for window 0x%x\n", client->window);
1110 client_unmanage(client);
1111
1112 /* we were trying to focus this window but it's gone */
1113 if (client == focus_tried) {
1114 ob_debug_type(OB_DEBUG_FOCUS, "Tried to focus window 0x%x and it "
1115 "is being unmanaged:\n");
1116 if (XCheckIfEvent(ob_display, &ce, look_for_focusin_client, NULL)){
1117 XPutBackEvent(ob_display, &ce);
1118 ob_debug_type(OB_DEBUG_FOCUS,
1119 " but another FocusIn is coming\n");
1120 } else {
1121 ob_debug_type(OB_DEBUG_FOCUS,
1122 " so falling back focus again.\n");
1123 focus_tried = focus_fallback(TRUE);
1124 }
1125 }
1126 break;
1127 case ReparentNotify:
1128 /* this is when the client is first taken captive in the frame */
1129 if (e->xreparent.parent == client->frame->plate) break;
1130
1131 /*
1132 This event is quite rare and is usually handled in unmapHandler.
1133 However, if the window is unmapped when the reparent event occurs,
1134 the window manager never sees it because an unmap event is not sent
1135 to an already unmapped window.
1136 */
1137
1138 /* we don't want the reparent event, put it back on the stack for the
1139 X server to deal with after we unmanage the window */
1140 XPutBackEvent(ob_display, e);
1141
1142 ob_debug("ReparentNotify for window 0x%x\n", client->window);
1143 client_unmanage(client);
1144
1145 /* we were trying to focus this window but it's gone */
1146 if (client == focus_tried) {
1147 ob_debug_type(OB_DEBUG_FOCUS, "Tried to focus window 0x%x and it "
1148 "is being unmanaged:\n");
1149 if (XCheckIfEvent(ob_display, &ce, look_for_focusin_client, NULL)){
1150 XPutBackEvent(ob_display, &ce);
1151 ob_debug_type(OB_DEBUG_FOCUS,
1152 " but another FocusIn is coming\n");
1153 } else {
1154 ob_debug_type(OB_DEBUG_FOCUS,
1155 " so falling back focus again.\n");
1156 focus_tried = focus_fallback(TRUE);
1157 }
1158 }
1159 break;
1160 case MapRequest:
1161 ob_debug("MapRequest for 0x%lx\n", client->window);
1162 if (!client->iconic) break; /* this normally doesn't happen, but if it
1163 does, we don't want it!
1164 it can happen now when the window is on
1165 another desktop, but we still don't
1166 want it! */
1167 client_activate(client, FALSE, TRUE);
1168 break;
1169 case ClientMessage:
1170 /* validate cuz we query stuff off the client here */
1171 if (!client_validate(client)) break;
1172
1173 if (e->xclient.format != 32) return;
1174
1175 msgtype = e->xclient.message_type;
1176 if (msgtype == prop_atoms.wm_change_state) {
1177 /* compress changes into a single change */
1178 while (XCheckTypedWindowEvent(ob_display, client->window,
1179 e->type, &ce)) {
1180 /* XXX: it would be nice to compress ALL messages of a
1181 type, not just messages in a row without other
1182 message types between. */
1183 if (ce.xclient.message_type != msgtype) {
1184 XPutBackEvent(ob_display, &ce);
1185 break;
1186 }
1187 e->xclient = ce.xclient;
1188 }
1189 client_set_wm_state(client, e->xclient.data.l[0]);
1190 } else if (msgtype == prop_atoms.net_wm_desktop) {
1191 /* compress changes into a single change */
1192 while (XCheckTypedWindowEvent(ob_display, client->window,
1193 e->type, &ce)) {
1194 /* XXX: it would be nice to compress ALL messages of a
1195 type, not just messages in a row without other
1196 message types between. */
1197 if (ce.xclient.message_type != msgtype) {
1198 XPutBackEvent(ob_display, &ce);
1199 break;
1200 }
1201 e->xclient = ce.xclient;
1202 }
1203 if ((unsigned)e->xclient.data.l[0] < screen_num_desktops ||
1204 (unsigned)e->xclient.data.l[0] == DESKTOP_ALL)
1205 client_set_desktop(client, (unsigned)e->xclient.data.l[0],
1206 FALSE);
1207 } else if (msgtype == prop_atoms.net_wm_state) {
1208 /* can't compress these */
1209 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
1210 (e->xclient.data.l[0] == 0 ? "Remove" :
1211 e->xclient.data.l[0] == 1 ? "Add" :
1212 e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
1213 e->xclient.data.l[1], e->xclient.data.l[2],
1214 client->window);
1215 client_set_state(client, e->xclient.data.l[0],
1216 e->xclient.data.l[1], e->xclient.data.l[2]);
1217 } else if (msgtype == prop_atoms.net_close_window) {
1218 ob_debug("net_close_window for 0x%lx\n", client->window);
1219 client_close(client);
1220 } else if (msgtype == prop_atoms.net_active_window) {
1221 ob_debug("net_active_window for 0x%lx source=%s\n",
1222 client->window,
1223 (e->xclient.data.l[0] == 0 ? "unknown" :
1224 (e->xclient.data.l[0] == 1 ? "application" :
1225 (e->xclient.data.l[0] == 2 ? "user" : "INVALID"))));
1226 /* XXX make use of data.l[2] !? */
1227 event_curtime = e->xclient.data.l[1];
1228 if (event_curtime == 0)
1229 ob_debug_type(OB_DEBUG_APP_BUGS,
1230 "_NET_ACTIVE_WINDOW message for window %s is "
1231 "missing a timestamp\n", client->title);
1232 client_activate(client, FALSE,
1233 (e->xclient.data.l[0] == 0 ||
1234 e->xclient.data.l[0] == 2));
1235 } else if (msgtype == prop_atoms.net_wm_moveresize) {
1236 ob_debug("net_wm_moveresize for 0x%lx direction %d\n",
1237 client->window, e->xclient.data.l[2]);
1238 if ((Atom)e->xclient.data.l[2] ==
1239 prop_atoms.net_wm_moveresize_size_topleft ||
1240 (Atom)e->xclient.data.l[2] ==
1241 prop_atoms.net_wm_moveresize_size_top ||
1242 (Atom)e->xclient.data.l[2] ==
1243 prop_atoms.net_wm_moveresize_size_topright ||
1244 (Atom)e->xclient.data.l[2] ==
1245 prop_atoms.net_wm_moveresize_size_right ||
1246 (Atom)e->xclient.data.l[2] ==
1247 prop_atoms.net_wm_moveresize_size_right ||
1248 (Atom)e->xclient.data.l[2] ==
1249 prop_atoms.net_wm_moveresize_size_bottomright ||
1250 (Atom)e->xclient.data.l[2] ==
1251 prop_atoms.net_wm_moveresize_size_bottom ||
1252 (Atom)e->xclient.data.l[2] ==
1253 prop_atoms.net_wm_moveresize_size_bottomleft ||
1254 (Atom)e->xclient.data.l[2] ==
1255 prop_atoms.net_wm_moveresize_size_left ||
1256 (Atom)e->xclient.data.l[2] ==
1257 prop_atoms.net_wm_moveresize_move ||
1258 (Atom)e->xclient.data.l[2] ==
1259 prop_atoms.net_wm_moveresize_size_keyboard ||
1260 (Atom)e->xclient.data.l[2] ==
1261 prop_atoms.net_wm_moveresize_move_keyboard) {
1262
1263 moveresize_start(client, e->xclient.data.l[0],
1264 e->xclient.data.l[1], e->xclient.data.l[3],
1265 e->xclient.data.l[2]);
1266 }
1267 else if ((Atom)e->xclient.data.l[2] ==
1268 prop_atoms.net_wm_moveresize_cancel)
1269 moveresize_end(TRUE);
1270 } else if (msgtype == prop_atoms.net_moveresize_window) {
1271 gint grav, x, y, w, h;
1272
1273 if (e->xclient.data.l[0] & 0xff)
1274 grav = e->xclient.data.l[0] & 0xff;
1275 else
1276 grav = client->gravity;
1277
1278 if (e->xclient.data.l[0] & 1 << 8)
1279 x = e->xclient.data.l[1];
1280 else
1281 x = client->area.x;
1282 if (e->xclient.data.l[0] & 1 << 9)
1283 y = e->xclient.data.l[2];
1284 else
1285 y = client->area.y;
1286 if (e->xclient.data.l[0] & 1 << 10)
1287 w = e->xclient.data.l[3];
1288 else
1289 w = client->area.width;
1290 if (e->xclient.data.l[0] & 1 << 11)
1291 h = e->xclient.data.l[4];
1292 else
1293 h = client->area.height;
1294
1295 ob_debug("MOVERESIZE x %d %d y %d %d\n",
1296 e->xclient.data.l[0] & 1 << 8, x,
1297 e->xclient.data.l[0] & 1 << 9, y);
1298 client_convert_gravity(client, grav, &x, &y, w, h);
1299 client_find_onscreen(client, &x, &y, w, h, FALSE);
1300 client_configure(client, x, y, w, h, FALSE, TRUE);
1301 } else if (msgtype == prop_atoms.net_restack_window) {
1302 if (e->xclient.data.l[0] != 2) {
1303 ob_debug_type(OB_DEBUG_APP_BUGS,
1304 "_NET_RESTACK_WINDOW sent for window %s with "
1305 "invalid source indication %ld\n",
1306 client->title, e->xclient.data.l[0]);
1307 } else {
1308 ObClient *sibling = NULL;
1309 if (e->xclient.data.l[1]) {
1310 ObWindow *win = g_hash_table_lookup(window_map,
1311 &e->xclient.data.l[1]);
1312 if (WINDOW_IS_CLIENT(win) &&
1313 WINDOW_AS_CLIENT(win) != client)
1314 {
1315 sibling = WINDOW_AS_CLIENT(win);
1316 }
1317 if (sibling == NULL)
1318 ob_debug_type(OB_DEBUG_APP_BUGS,
1319 "_NET_RESTACK_WINDOW sent for window %s "
1320 "with invalid sibling 0x%x\n",
1321 client->title, e->xclient.data.l[1]);
1322 }
1323 if (e->xclient.data.l[2] == Below ||
1324 e->xclient.data.l[2] == BottomIf ||
1325 e->xclient.data.l[2] == Above ||
1326 e->xclient.data.l[2] == TopIf ||
1327 e->xclient.data.l[2] == Opposite)
1328 {
1329 /* just raise, don't activate */
1330 stacking_restack_request(client, sibling,
1331 e->xclient.data.l[2], FALSE);
1332 /* send a synthetic ConfigureNotify, cuz this is supposed
1333 to be like a ConfigureRequest. */
1334 client_configure_full(client, client->area.x,
1335 client->area.y,
1336 client->area.width,
1337 client->area.height,
1338 FALSE, TRUE);
1339 } else
1340 ob_debug_type(OB_DEBUG_APP_BUGS,
1341 "_NET_RESTACK_WINDOW sent for window %s "
1342 "with invalid detail %d\n",
1343 client->title, e->xclient.data.l[2]);
1344 }
1345 }
1346 break;
1347 case PropertyNotify:
1348 /* validate cuz we query stuff off the client here */
1349 if (!client_validate(client)) break;
1350
1351 /* compress changes to a single property into a single change */
1352 while (XCheckTypedWindowEvent(ob_display, client->window,
1353 e->type, &ce)) {
1354 Atom a, b;
1355
1356 /* XXX: it would be nice to compress ALL changes to a property,
1357 not just changes in a row without other props between. */
1358
1359 a = ce.xproperty.atom;
1360 b = e->xproperty.atom;
1361
1362 if (a == b)
1363 continue;
1364 if ((a == prop_atoms.net_wm_name ||
1365 a == prop_atoms.wm_name ||
1366 a == prop_atoms.net_wm_icon_name ||
1367 a == prop_atoms.wm_icon_name)
1368 &&
1369 (b == prop_atoms.net_wm_name ||
1370 b == prop_atoms.wm_name ||
1371 b == prop_atoms.net_wm_icon_name ||
1372 b == prop_atoms.wm_icon_name)) {
1373 continue;
1374 }
1375 if (a == prop_atoms.net_wm_icon &&
1376 b == prop_atoms.net_wm_icon)
1377 continue;
1378
1379 XPutBackEvent(ob_display, &ce);
1380 break;
1381 }
1382
1383 msgtype = e->xproperty.atom;
1384 if (msgtype == XA_WM_NORMAL_HINTS) {
1385 client_update_normal_hints(client);
1386 /* normal hints can make a window non-resizable */
1387 client_setup_decor_and_functions(client);
1388 } else if (msgtype == XA_WM_HINTS) {
1389 client_update_wmhints(client);
1390 } else if (msgtype == XA_WM_TRANSIENT_FOR) {
1391 client_update_transient_for(client);
1392 client_get_type_and_transientness(client);
1393 /* type may have changed, so update the layer */
1394 client_calc_layer(client);
1395 client_setup_decor_and_functions(client);
1396 } else if (msgtype == prop_atoms.net_wm_name ||
1397 msgtype == prop_atoms.wm_name ||
1398 msgtype == prop_atoms.net_wm_icon_name ||
1399 msgtype == prop_atoms.wm_icon_name) {
1400 client_update_title(client);
1401 } else if (msgtype == prop_atoms.wm_protocols) {
1402 client_update_protocols(client);
1403 client_setup_decor_and_functions(client);
1404 }
1405 else if (msgtype == prop_atoms.net_wm_strut) {
1406 client_update_strut(client);
1407 }
1408 else if (msgtype == prop_atoms.net_wm_icon) {
1409 client_update_icons(client);
1410 }
1411 else if (msgtype == prop_atoms.net_wm_icon_geometry) {
1412 client_update_icon_geometry(client);
1413 }
1414 else if (msgtype == prop_atoms.net_wm_user_time) {
1415 client_update_user_time(client);
1416 }
1417 else if (msgtype == prop_atoms.net_wm_user_time_window) {
1418 client_update_user_time_window(client);
1419 }
1420 #ifdef SYNC
1421 else if (msgtype == prop_atoms.net_wm_sync_request_counter) {
1422 client_update_sync_request_counter(client);
1423 }
1424 #endif
1425 case ColormapNotify:
1426 client_update_colormap(client, e->xcolormap.colormap);
1427 break;
1428 default:
1429 ;
1430 #ifdef SHAPE
1431 if (extensions_shape && e->type == extensions_shape_event_basep) {
1432 client->shaped = ((XShapeEvent*)e)->shaped;
1433 frame_adjust_shape(client->frame);
1434 }
1435 #endif
1436 }
1437 }
1438
1439 static void event_handle_dock(ObDock *s, XEvent *e)
1440 {
1441 switch (e->type) {
1442 case ButtonPress:
1443 if (e->xbutton.button == 1)
1444 stacking_raise(DOCK_AS_WINDOW(s));
1445 else if (e->xbutton.button == 2)
1446 stacking_lower(DOCK_AS_WINDOW(s));
1447 break;
1448 case EnterNotify:
1449 dock_hide(FALSE);
1450 break;
1451 case LeaveNotify:
1452 dock_hide(TRUE);
1453 break;
1454 }
1455 }
1456
1457 static void event_handle_dockapp(ObDockApp *app, XEvent *e)
1458 {
1459 switch (e->type) {
1460 case MotionNotify:
1461 dock_app_drag(app, &e->xmotion);
1462 break;
1463 case UnmapNotify:
1464 if (app->ignore_unmaps) {
1465 app->ignore_unmaps--;
1466 break;
1467 }
1468 dock_remove(app, TRUE);
1469 break;
1470 case DestroyNotify:
1471 dock_remove(app, FALSE);
1472 break;
1473 case ReparentNotify:
1474 dock_remove(app, FALSE);
1475 break;
1476 case ConfigureNotify:
1477 dock_app_configure(app, e->xconfigure.width, e->xconfigure.height);
1478 break;
1479 }
1480 }
1481
1482 static ObMenuFrame* find_active_menu()
1483 {
1484 GList *it;
1485 ObMenuFrame *ret = NULL;
1486
1487 for (it = menu_frame_visible; it; it = g_list_next(it)) {
1488 ret = it->data;
1489 if (ret->selected)
1490 break;
1491 ret = NULL;
1492 }
1493 return ret;
1494 }
1495
1496 static ObMenuFrame* find_active_or_last_menu()
1497 {
1498 ObMenuFrame *ret = NULL;
1499
1500 ret = find_active_menu();
1501 if (!ret && menu_frame_visible)
1502 ret = menu_frame_visible->data;
1503 return ret;
1504 }
1505
1506 static gboolean event_handle_menu_keyboard(XEvent *ev)
1507 {
1508 guint keycode, state;
1509 gunichar unikey;
1510 ObMenuFrame *frame;
1511 gboolean ret = TRUE;
1512
1513 keycode = ev->xkey.keycode;
1514 state = ev->xkey.state;
1515 unikey = translate_unichar(keycode);
1516
1517 frame = find_active_or_last_menu();
1518 if (frame == NULL)
1519 ret = FALSE;
1520
1521 else if (keycode == ob_keycode(OB_KEY_ESCAPE) && state == 0) {
1522 /* Escape closes the active menu */
1523 menu_frame_hide(frame);
1524 }
1525
1526 else if (keycode == ob_keycode(OB_KEY_RETURN) && (state == 0 ||
1527 state == ControlMask))
1528 {
1529 /* Enter runs the active item or goes into the submenu.
1530 Control-Enter runs it without closing the menu. */
1531 if (frame->child)
1532 menu_frame_select_next(frame->child);
1533 else
1534 menu_entry_frame_execute(frame->selected, state, ev->xkey.time);
1535 }
1536
1537 else if (keycode == ob_keycode(OB_KEY_LEFT) && ev->xkey.state == 0) {
1538 /* Left goes to the parent menu */
1539 menu_frame_select(frame, NULL, TRUE);
1540 }
1541
1542 else if (keycode == ob_keycode(OB_KEY_RIGHT) && ev->xkey.state == 0) {
1543 /* Right goes to the selected submenu */
1544 if (frame->child) menu_frame_select_next(frame->child);
1545 }
1546
1547 else if (keycode == ob_keycode(OB_KEY_UP) && state == 0) {
1548 menu_frame_select_previous(frame);
1549 }
1550
1551 else if (keycode == ob_keycode(OB_KEY_DOWN) && state == 0) {
1552 menu_frame_select_next(frame);
1553 }
1554
1555 /* keyboard accelerator shortcuts. */
1556 else if (ev->xkey.state == 0 &&
1557 /* was it a valid key? */
1558 unikey != 0 &&
1559 /* don't bother if the menu is empty. */
1560 frame->entries)
1561 {
1562 GList *start;
1563 GList *it;
1564 ObMenuEntryFrame *found = NULL;
1565 guint num_found = 0;
1566
1567 /* start after the selected one */
1568 start = frame->entries;
1569 if (frame->selected) {
1570 for (it = start; frame->selected != it->data; it = g_list_next(it))
1571 g_assert(it != NULL); /* nothing was selected? */
1572 /* next with wraparound */
1573 start = g_list_next(it);
1574 if (start == NULL) start = frame->entries;
1575 }
1576
1577 it = start;
1578 do {
1579 ObMenuEntryFrame *e = it->data;
1580 gunichar entrykey = 0;
1581
1582 if (e->entry->type == OB_MENU_ENTRY_TYPE_NORMAL)
1583 entrykey = e->entry->data.normal.shortcut;
1584 else if (e->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU)
1585 entrykey = e->entry->data.submenu.submenu->shortcut;
1586
1587 if (unikey == entrykey) {
1588 if (found == NULL) found = e;
1589 ++num_found;
1590 }
1591
1592 /* next with wraparound */
1593 it = g_list_next(it);
1594 if (it == NULL) it = frame->entries;
1595 } while (it != start);
1596
1597 if (found) {
1598 if (found->entry->type == OB_MENU_ENTRY_TYPE_NORMAL &&
1599 num_found == 1)
1600 {
1601 menu_frame_select(frame, found, TRUE);
1602 usleep(50000); /* highlight the item for a short bit so the
1603 user can see what happened */
1604 menu_entry_frame_execute(found, state, ev->xkey.time);
1605 } else {
1606 menu_frame_select(frame, found, TRUE);
1607 if (num_found == 1)
1608 menu_frame_select_next(frame->child);
1609 }
1610 } else
1611 ret = FALSE;
1612 }
1613 else
1614 ret = FALSE;
1615
1616 return ret;
1617 }
1618
1619 static gboolean event_handle_menu(XEvent *ev)
1620 {
1621 ObMenuFrame *f;
1622 ObMenuEntryFrame *e;
1623 gboolean ret = TRUE;
1624
1625 switch (ev->type) {
1626 case ButtonRelease:
1627 if ((ev->xbutton.button < 4 || ev->xbutton.button > 5)
1628 && menu_can_hide)
1629 {
1630 if ((e = menu_entry_frame_under(ev->xbutton.x_root,
1631 ev->xbutton.y_root)))
1632 menu_entry_frame_execute(e, ev->xbutton.state,
1633 ev->xbutton.time);
1634 else
1635 menu_frame_hide_all();
1636 }
1637 break;
1638 case EnterNotify:
1639 if ((e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.window))) {
1640 if (e->ignore_enters)
1641 --e->ignore_enters;
1642 else
1643 menu_frame_select(e->frame, e, FALSE);
1644 }
1645 break;
1646 case LeaveNotify:
1647 if ((e = g_hash_table_lookup(menu_frame_map, &ev->xcrossing.window)) &&
1648 (f = find_active_menu()) && f->selected == e &&
1649 e->entry->type != OB_MENU_ENTRY_TYPE_SUBMENU)
1650 {
1651 menu_frame_select(e->frame, NULL, FALSE);
1652 }
1653 break;
1654 case MotionNotify:
1655 if ((e = menu_entry_frame_under(ev->xmotion.x_root,
1656 ev->xmotion.y_root)))
1657 menu_frame_select(e->frame, e, FALSE);
1658 break;
1659 case KeyPress:
1660 ret = event_handle_menu_keyboard(ev);
1661 break;
1662 }
1663 return ret;
1664 }
1665
1666 static void event_handle_user_input(ObClient *client, XEvent *e)
1667 {
1668 g_assert(e->type == ButtonPress || e->type == ButtonRelease ||
1669 e->type == MotionNotify || e->type == KeyPress ||
1670 e->type == KeyRelease);
1671
1672 if (menu_frame_visible) {
1673 if (event_handle_menu(e))
1674 /* don't use the event if the menu used it, but if the menu
1675 didn't use it and it's a keypress that is bound, it will
1676 close the menu and be used */
1677 return;
1678 }
1679
1680 /* if the keyboard interactive action uses the event then dont
1681 use it for bindings. likewise is moveresize uses the event. */
1682 if (!keyboard_process_interactive_grab(e, &client) &&
1683 !(moveresize_in_progress && moveresize_event(e)))
1684 {
1685 if (moveresize_in_progress)
1686 /* make further actions work on the client being
1687 moved/resized */
1688 client = moveresize_client;
1689
1690 menu_can_hide = FALSE;
1691 ob_main_loop_timeout_add(ob_main_loop,
1692 config_menu_hide_delay * 1000,
1693 menu_hide_delay_func,
1694 NULL, g_direct_equal, NULL);
1695
1696 if (e->type == ButtonPress ||
1697 e->type == ButtonRelease ||
1698 e->type == MotionNotify)
1699 {
1700 /* the frame may not be "visible" but they can still click on it
1701 in the case where it is animating before disappearing */
1702 if (!client || !frame_iconify_animating(client->frame))
1703 mouse_event(client, e);
1704 } else if (e->type == KeyPress) {
1705 keyboard_event((focus_cycle_target ? focus_cycle_target :
1706 (client ? client : focus_client)), e);
1707 }
1708 }
1709 }
1710
1711 static gboolean menu_hide_delay_func(gpointer data)
1712 {
1713 menu_can_hide = TRUE;
1714 return FALSE; /* no repeat */
1715 }
1716
1717 static void focus_delay_dest(gpointer data)
1718 {
1719 g_free(data);
1720 }
1721
1722 static gboolean focus_delay_cmp(gconstpointer d1, gconstpointer d2)
1723 {
1724 const ObFocusDelayData *f1 = d1;
1725 return f1->client == d2;
1726 }
1727
1728 static gboolean focus_delay_func(gpointer data)
1729 {
1730 ObFocusDelayData *d = data;
1731 Time old = event_curtime;
1732
1733 event_curtime = d->time;
1734 if (focus_client != d->client) {
1735 if (client_focus(d->client) && config_focus_raise)
1736 stacking_raise(CLIENT_AS_WINDOW(d->client));
1737 }
1738 event_curtime = old;
1739 return FALSE; /* no repeat */
1740 }
1741
1742 static void focus_delay_client_dest(ObClient *client, gpointer data)
1743 {
1744 ob_main_loop_timeout_remove_data(ob_main_loop, focus_delay_func,
1745 client, FALSE);
1746 }
1747
1748 void event_halt_focus_delay()
1749 {
1750 ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
1751 }
1752
1753 void event_ignore_queued_enters()
1754 {
1755 GSList *saved = NULL, *it;
1756 XEvent *e;
1757
1758 XSync(ob_display, FALSE);
1759
1760 /* count the events */
1761 while (TRUE) {
1762 e = g_new(XEvent, 1);
1763 if (XCheckTypedEvent(ob_display, EnterNotify, e)) {
1764 ObWindow *win;
1765
1766 win = g_hash_table_lookup(window_map, &e->xany.window);
1767 if (win && WINDOW_IS_CLIENT(win))
1768 ++ignore_enter_focus;
1769
1770 saved = g_slist_append(saved, e);
1771 } else {
1772 g_free(e);
1773 break;
1774 }
1775 }
1776 /* put the events back */
1777 for (it = saved; it; it = g_slist_next(it)) {
1778 XPutBackEvent(ob_display, it->data);
1779 g_free(it->data);
1780 }
1781 g_slist_free(saved);
1782 }
1783
1784 gboolean event_time_after(Time t1, Time t2)
1785 {
1786 g_assert(t1 != CurrentTime);
1787 g_assert(t2 != CurrentTime);
1788
1789 /*
1790 Timestamp values wrap around (after about 49.7 days). The server, given
1791 its current time is represented by timestamp T, always interprets
1792 timestamps from clients by treating half of the timestamp space as being
1793 later in time than T.
1794 - http://tronche.com/gui/x/xlib/input/pointer-grabbing.html
1795 */
1796
1797 /* TIME_HALF is half of the number space of a Time type variable */
1798 #define TIME_HALF (Time)(1 << (sizeof(Time)*8-1))
1799
1800 if (t2 >= TIME_HALF)
1801 /* t2 is in the second half so t1 might wrap around and be smaller than
1802 t2 */
1803 return t1 >= t2 || t1 < (t2 + TIME_HALF);
1804 else
1805 /* t2 is in the first half so t1 has to come after it */
1806 return t1 >= t2 && t1 < (t2 + TIME_HALF);
1807 }
This page took 0.113733 seconds and 3 git commands to generate.