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