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