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