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