]> Dogcows Code - chaz/openbox/blob - openbox/event.c
use focus_hilite to pick which window gets keyboard events
[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) 2003 Ben Jansens
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 See the COPYING file for a copy of the GNU General Public License.
17 */
18
19 #include "event.h"
20 #include "debug.h"
21 #include "window.h"
22 #include "openbox.h"
23 #include "dock.h"
24 #include "client.h"
25 #include "xerror.h"
26 #include "prop.h"
27 #include "config.h"
28 #include "screen.h"
29 #include "frame.h"
30 #include "menu.h"
31 #include "menuframe.h"
32 #include "keyboard.h"
33 #include "mouse.h"
34 #include "mainloop.h"
35 #include "framerender.h"
36 #include "focus.h"
37 #include "moveresize.h"
38 #include "group.h"
39 #include "stacking.h"
40 #include "extensions.h"
41
42 #include <X11/Xlib.h>
43 #include <X11/keysym.h>
44 #include <X11/Xatom.h>
45 #include <glib.h>
46
47 #ifdef HAVE_SYS_SELECT_H
48 # include <sys/select.h>
49 #endif
50 #ifdef HAVE_SIGNAL_H
51 # include <signal.h>
52 #endif
53
54 #ifdef USE_SM
55 #include <X11/ICE/ICElib.h>
56 #endif
57
58 static void event_process(const XEvent *e, gpointer data);
59 static void event_done(gpointer data);
60 static void event_client_dest(ObClient *client, gpointer data);
61 static void event_handle_root(XEvent *e);
62 static void event_handle_menu(XEvent *e);
63 static void event_handle_dock(ObDock *s, XEvent *e);
64 static void event_handle_dockapp(ObDockApp *app, XEvent *e);
65 static void event_handle_client(ObClient *c, XEvent *e);
66 static void event_handle_group(ObGroup *g, XEvent *e);
67
68 static gboolean focus_delay_func(gpointer data);
69 static void focus_delay_client_dest(ObClient *client, gpointer data);
70
71 static gboolean menu_hide_delay_func(gpointer data);
72
73 Time event_lasttime = 0;
74
75 /*! The value of the mask for the NumLock modifier */
76 unsigned int NumLockMask;
77 /*! The value of the mask for the ScrollLock modifier */
78 unsigned int ScrollLockMask;
79 /*! The key codes for the modifier keys */
80 static XModifierKeymap *modmap;
81 /*! Table of the constant modifier masks */
82 static const int mask_table[] = {
83 ShiftMask, LockMask, ControlMask, Mod1Mask,
84 Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask
85 };
86 static int mask_table_size;
87
88 static guint ignore_enter_focus = 0;
89
90 static gboolean menu_can_hide;
91
92 static ObClient *focus_in, *focus_out;
93
94 #ifdef USE_SM
95 static void ice_handler(int fd, gpointer conn)
96 {
97 Bool b;
98 IceProcessMessages(conn, NULL, &b);
99 }
100
101 static void ice_watch(IceConn conn, IcePointer data, Bool opening,
102 IcePointer *watch_data)
103 {
104 static gint fd = -1;
105
106 if (opening) {
107 fd = IceConnectionNumber(conn);
108 ob_main_loop_fd_add(ob_main_loop, fd, ice_handler, conn, NULL);
109 } else {
110 ob_main_loop_fd_remove(ob_main_loop, fd);
111 fd = -1;
112 }
113 }
114 #endif
115
116 void event_startup(gboolean reconfig)
117 {
118 if (reconfig) return;
119
120 mask_table_size = sizeof(mask_table) / sizeof(mask_table[0]);
121
122 /* get lock masks that are defined by the display (not constant) */
123 modmap = XGetModifierMapping(ob_display);
124 g_assert(modmap);
125 if (modmap && modmap->max_keypermod > 0) {
126 size_t cnt;
127 const size_t size = mask_table_size * modmap->max_keypermod;
128 /* get the values of the keyboard lock modifiers
129 Note: Caps lock is not retrieved the same way as Scroll and Num
130 lock since it doesn't need to be. */
131 const KeyCode num_lock = XKeysymToKeycode(ob_display, XK_Num_Lock);
132 const KeyCode scroll_lock = XKeysymToKeycode(ob_display,
133 XK_Scroll_Lock);
134
135 for (cnt = 0; cnt < size; ++cnt) {
136 if (! modmap->modifiermap[cnt]) continue;
137
138 if (num_lock == modmap->modifiermap[cnt])
139 NumLockMask = mask_table[cnt / modmap->max_keypermod];
140 if (scroll_lock == modmap->modifiermap[cnt])
141 ScrollLockMask = mask_table[cnt / modmap->max_keypermod];
142 }
143 }
144
145 ob_main_loop_x_add(ob_main_loop, event_process, event_done, NULL, NULL);
146
147 #ifdef USE_SM
148 IceAddConnectionWatch(ice_watch, NULL);
149 #endif
150
151 client_add_destructor(focus_delay_client_dest, NULL);
152 client_add_destructor(event_client_dest, NULL);
153 }
154
155 void event_shutdown(gboolean reconfig)
156 {
157 if (reconfig) return;
158
159 #ifdef USE_SM
160 IceRemoveConnectionWatch(ice_watch, NULL);
161 #endif
162
163 client_remove_destructor(focus_delay_client_dest);
164 XFreeModifiermap(modmap);
165 }
166
167 static Window event_get_window(XEvent *e)
168 {
169 Window window;
170
171 /* pick a window */
172 switch (e->type) {
173 case SelectionClear:
174 window = RootWindow(ob_display, ob_screen);
175 break;
176 case MapRequest:
177 window = e->xmap.window;
178 break;
179 case UnmapNotify:
180 window = e->xunmap.window;
181 break;
182 case DestroyNotify:
183 window = e->xdestroywindow.window;
184 break;
185 case ConfigureRequest:
186 window = e->xconfigurerequest.window;
187 break;
188 case ConfigureNotify:
189 window = e->xconfigure.window;
190 break;
191 default:
192 #ifdef XKB
193 if (extensions_xkb && e->type == extensions_xkb_event_basep) {
194 switch (((XkbAnyEvent*)e)->xkb_type) {
195 case XkbBellNotify:
196 window = ((XkbBellNotifyEvent*)e)->window;
197 default:
198 window = None;
199 }
200 } else
201 #endif
202 window = e->xany.window;
203 }
204 return window;
205 }
206
207 static void event_set_lasttime(XEvent *e)
208 {
209 Time t = 0;
210
211 /* grab the lasttime and hack up the state */
212 switch (e->type) {
213 case ButtonPress:
214 case ButtonRelease:
215 t = e->xbutton.time;
216 break;
217 case KeyPress:
218 t = e->xkey.time;
219 break;
220 case KeyRelease:
221 t = e->xkey.time;
222 break;
223 case MotionNotify:
224 t = e->xmotion.time;
225 break;
226 case PropertyNotify:
227 t = e->xproperty.time;
228 break;
229 case EnterNotify:
230 case LeaveNotify:
231 t = e->xcrossing.time;
232 break;
233 default:
234 /* if more event types are anticipated, get their timestamp
235 explicitly */
236 break;
237 }
238
239 if (t > event_lasttime)
240 event_lasttime = t;
241 }
242
243 #define STRIP_MODS(s) \
244 s &= ~(LockMask | NumLockMask | ScrollLockMask), \
245 /* kill off the Button1Mask etc, only want the modifiers */ \
246 s &= (ControlMask | ShiftMask | Mod1Mask | \
247 Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask) \
248
249 static void event_hack_mods(XEvent *e)
250 {
251 KeyCode *kp;
252 int i, k;
253
254 switch (e->type) {
255 case ButtonPress:
256 case ButtonRelease:
257 STRIP_MODS(e->xbutton.state);
258 break;
259 case KeyPress:
260 STRIP_MODS(e->xkey.state);
261 break;
262 case KeyRelease:
263 STRIP_MODS(e->xkey.state);
264 /* remove from the state the mask of the modifier being released, if
265 it is a modifier key being released (this is a little ugly..) */
266 kp = modmap->modifiermap;
267 for (i = 0; i < mask_table_size; ++i) {
268 for (k = 0; k < modmap->max_keypermod; ++k) {
269 if (*kp == e->xkey.keycode) { /* found the keycode */
270 /* remove the mask for it */
271 e->xkey.state &= ~mask_table[i];
272 /* cause the first loop to break; */
273 i = mask_table_size;
274 break; /* get outta here! */
275 }
276 ++kp;
277 }
278 }
279 break;
280 case MotionNotify:
281 STRIP_MODS(e->xmotion.state);
282 /* compress events */
283 {
284 XEvent ce;
285 while (XCheckTypedWindowEvent(ob_display, e->xmotion.window,
286 e->type, &ce)) {
287 e->xmotion.x_root = ce.xmotion.x_root;
288 e->xmotion.y_root = ce.xmotion.y_root;
289 }
290 }
291 break;
292 }
293 }
294
295 static gboolean event_ignore(XEvent *e, ObClient *client)
296 {
297 switch(e->type) {
298 case EnterNotify:
299 case LeaveNotify:
300 if (e->xcrossing.detail == NotifyInferior)
301 return TRUE;
302 break;
303 case FocusIn:
304 if (e->xfocus.detail > NotifyNonlinearVirtual)
305 return TRUE;
306 break;
307 case FocusOut:
308 if (e->xfocus.detail > NotifyNonlinearVirtual)
309 return TRUE;
310 if (e->xfocus.detail == NotifyInferior ||
311 e->xfocus.mode == NotifyGrab)
312 return TRUE;
313 break;
314 }
315 return FALSE;
316 }
317
318 static void event_client_dest(ObClient *client, gpointer data)
319 {
320 if (client == focus_in)
321 focus_in = NULL;
322 if (client == focus_out)
323 focus_out = NULL;
324 }
325
326 static void event_done(gpointer data)
327 {
328 static ObClient *last = NULL;
329
330 /* sometimes focus_hilite can be on an unfocused window, this make sure
331 it loses its focus hilite when focus moves */
332 if (focus_hilite &&
333 (focus_in && focus_hilite != focus_in) &&
334 (focus_out && focus_hilite != focus_out))
335 {
336 frame_adjust_focus(focus_hilite->frame, FALSE);
337 }
338
339 if (focus_in) {
340 if (focus_in != focus_client) {
341 focus_set_client(focus_in);
342 frame_adjust_focus(focus_in->frame, TRUE);
343 client_calc_layer(focus_in);
344 }
345 }
346 if (focus_out) {
347 if (focus_out == focus_client)
348 focus_set_client(NULL);
349 frame_adjust_focus(focus_out->frame, FALSE);
350 client_calc_layer(focus_out);
351 }
352
353 focus_hilite = focus_in;
354
355 if (focus_client != last) {
356 if (!focus_client)
357 focus_fallback(OB_FOCUS_FALLBACK_NOFOCUS);
358 last = focus_client;
359 }
360
361 focus_in = focus_out = NULL;
362 }
363
364 static void event_process(const XEvent *ec, gpointer data)
365 {
366 Window window;
367 ObGroup *group = NULL;
368 ObClient *client = NULL;
369 ObDock *dock = NULL;
370 ObDockApp *dockapp = NULL;
371 ObWindow *obwin = NULL;
372 XEvent ee, *e;
373
374 /* make a copy we can mangle */
375 ee = *ec;
376 e = &ee;
377
378 window = event_get_window(e);
379 if (!(e->type == PropertyNotify &&
380 (group = g_hash_table_lookup(group_map, &window))))
381 if ((obwin = g_hash_table_lookup(window_map, &window))) {
382 switch (obwin->type) {
383 case Window_Dock:
384 dock = WINDOW_AS_DOCK(obwin);
385 break;
386 case Window_DockApp:
387 dockapp = WINDOW_AS_DOCKAPP(obwin);
388 break;
389 case Window_Client:
390 client = WINDOW_AS_CLIENT(obwin);
391 break;
392 case Window_Menu:
393 case Window_Internal:
394 /* not to be used for events */
395 g_assert_not_reached();
396 break;
397 }
398 }
399
400 event_set_lasttime(e);
401 event_hack_mods(e);
402 if (event_ignore(e, client))
403 return;
404
405 /* deal with it in the kernel */
406 if (group)
407 event_handle_group(group, e);
408 else if (client)
409 event_handle_client(client, e);
410 else if (dockapp)
411 event_handle_dockapp(dockapp, e);
412 else if (dock)
413 event_handle_dock(dock, e);
414 else if (window == RootWindow(ob_display, ob_screen))
415 event_handle_root(e);
416 else if (e->type == MapRequest)
417 client_manage(window);
418 else if (e->type == ConfigureRequest) {
419 /* unhandled configure requests must be used to configure the
420 window directly */
421 XWindowChanges xwc;
422
423 xwc.x = e->xconfigurerequest.x;
424 xwc.y = e->xconfigurerequest.y;
425 xwc.width = e->xconfigurerequest.width;
426 xwc.height = e->xconfigurerequest.height;
427 xwc.border_width = e->xconfigurerequest.border_width;
428 xwc.sibling = e->xconfigurerequest.above;
429 xwc.stack_mode = e->xconfigurerequest.detail;
430
431 /* we are not to be held responsible if someone sends us an
432 invalid request! */
433 xerror_set_ignore(TRUE);
434 XConfigureWindow(ob_display, window,
435 e->xconfigurerequest.value_mask, &xwc);
436 xerror_set_ignore(FALSE);
437 }
438
439 /* user input (action-bound) events */
440 if (e->type == ButtonPress || e->type == ButtonRelease ||
441 e->type == MotionNotify || e->type == KeyPress ||
442 e->type == KeyRelease)
443 {
444 if (menu_frame_visible)
445 event_handle_menu(e);
446 else {
447 if (!keyboard_process_interactive_grab(e, &client)) {
448 if (moveresize_in_progress) {
449 moveresize_event(e);
450
451 /* make further actions work on the client being
452 moved/resized */
453 client = moveresize_client;
454 }
455
456 menu_can_hide = FALSE;
457 ob_main_loop_timeout_add(ob_main_loop,
458 G_USEC_PER_SEC / 4,
459 menu_hide_delay_func,
460 NULL, NULL);
461
462 if (e->type == ButtonPress || e->type == ButtonRelease ||
463 e->type == MotionNotify)
464 mouse_event(client, e);
465 else if (e->type == KeyPress)
466 keyboard_event((focus_hilite ?
467 focus_hilite : client), e);
468 }
469 }
470 }
471 }
472
473 static void event_handle_root(XEvent *e)
474 {
475 Atom msgtype;
476
477 switch(e->type) {
478 case SelectionClear:
479 ob_debug("Another WM has requested to replace us. Exiting.\n");
480 ob_exit(0);
481 break;
482
483 case ClientMessage:
484 if (e->xclient.format != 32) break;
485
486 msgtype = e->xclient.message_type;
487 if (msgtype == prop_atoms.net_current_desktop) {
488 unsigned int d = e->xclient.data.l[0];
489 if (d < screen_num_desktops)
490 screen_set_desktop(d);
491 } else if (msgtype == prop_atoms.net_number_of_desktops) {
492 unsigned int d = e->xclient.data.l[0];
493 if (d > 0)
494 screen_set_num_desktops(d);
495 } else if (msgtype == prop_atoms.net_showing_desktop) {
496 screen_show_desktop(e->xclient.data.l[0] != 0);
497 }
498 break;
499 case PropertyNotify:
500 if (e->xproperty.atom == prop_atoms.net_desktop_names)
501 screen_update_desktop_names();
502 else if (e->xproperty.atom == prop_atoms.net_desktop_layout)
503 screen_update_layout();
504 break;
505 case ConfigureNotify:
506 #ifdef XRANDR
507 XRRUpdateConfiguration(e);
508 #endif
509 screen_resize();
510 break;
511 default:
512 ;
513 #ifdef VIDMODE
514 if (extensions_vidmode && e->type == extensions_vidmode_event_basep) {
515 ob_debug("VIDMODE EVENT\n");
516 }
517 #endif
518 }
519 }
520
521 static void event_handle_group(ObGroup *group, XEvent *e)
522 {
523 GSList *it;
524
525 g_assert(e->type == PropertyNotify);
526
527 for (it = group->members; it; it = g_slist_next(it))
528 event_handle_client(it->data, e);
529 }
530
531 void event_enter_client(ObClient *client)
532 {
533 g_assert(config_focus_follow);
534
535 if (client_normal(client) && client_can_focus(client)) {
536 if (config_focus_delay) {
537 ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
538 ob_main_loop_timeout_add(ob_main_loop,
539 config_focus_delay,
540 focus_delay_func,
541 client, NULL);
542 } else
543 focus_delay_func(client);
544 }
545 }
546
547 static void event_handle_client(ObClient *client, XEvent *e)
548 {
549 XEvent ce;
550 Atom msgtype;
551 int i=0;
552 ObFrameContext con;
553
554 switch (e->type) {
555 case VisibilityNotify:
556 client->frame->obscured = e->xvisibility.state != VisibilityUnobscured;
557 break;
558 case ButtonPress:
559 case ButtonRelease:
560 /* Wheel buttons don't draw because they are an instant click, so it
561 is a waste of resources to go drawing it. */
562 if (!(e->xbutton.button == 4 || e->xbutton.button == 5)) {
563 con = frame_context(client, e->xbutton.window);
564 con = mouse_button_frame_context(con, e->xbutton.button);
565 switch (con) {
566 case OB_FRAME_CONTEXT_MAXIMIZE:
567 client->frame->max_press = (e->type == ButtonPress);
568 framerender_frame(client->frame);
569 break;
570 case OB_FRAME_CONTEXT_CLOSE:
571 client->frame->close_press = (e->type == ButtonPress);
572 framerender_frame(client->frame);
573 break;
574 case OB_FRAME_CONTEXT_ICONIFY:
575 client->frame->iconify_press = (e->type == ButtonPress);
576 framerender_frame(client->frame);
577 break;
578 case OB_FRAME_CONTEXT_ALLDESKTOPS:
579 client->frame->desk_press = (e->type == ButtonPress);
580 framerender_frame(client->frame);
581 break;
582 case OB_FRAME_CONTEXT_SHADE:
583 client->frame->shade_press = (e->type == ButtonPress);
584 framerender_frame(client->frame);
585 break;
586 default:
587 /* nothing changes with clicks for any other contexts */
588 break;
589 }
590 }
591 break;
592 case FocusIn:
593 #ifdef DEBUG_FOCUS
594 ob_debug("FocusIn on client for %lx (client %lx) mode %d detail %d\n",
595 e->xfocus.window, client->window,
596 e->xfocus.mode, e->xfocus.detail);
597 #endif
598 focus_in = client;
599 if (focus_out == client)
600 focus_out = NULL;
601 break;
602 case FocusOut:
603 #ifdef DEBUG_FOCUS
604 ob_debug("FocusOut on client for %lx (client %lx) mode %d detail %d\n",
605 e->xfocus.window, client->window,
606 e->xfocus.mode, e->xfocus.detail);
607 #endif
608 focus_out = client;
609 if (focus_in == client)
610 focus_in = NULL;
611 break;
612 case LeaveNotify:
613 con = frame_context(client, e->xcrossing.window);
614 switch (con) {
615 case OB_FRAME_CONTEXT_MAXIMIZE:
616 client->frame->max_hover = FALSE;
617 frame_adjust_state(client->frame);
618 break;
619 case OB_FRAME_CONTEXT_ALLDESKTOPS:
620 client->frame->desk_hover = FALSE;
621 frame_adjust_state(client->frame);
622 break;
623 case OB_FRAME_CONTEXT_SHADE:
624 client->frame->shade_hover = FALSE;
625 frame_adjust_state(client->frame);
626 break;
627 case OB_FRAME_CONTEXT_ICONIFY:
628 client->frame->iconify_hover = FALSE;
629 frame_adjust_state(client->frame);
630 break;
631 case OB_FRAME_CONTEXT_CLOSE:
632 client->frame->close_hover = FALSE;
633 frame_adjust_state(client->frame);
634 break;
635 case OB_FRAME_CONTEXT_FRAME:
636 if (config_focus_follow && config_focus_delay)
637 ob_main_loop_timeout_remove_data(ob_main_loop,
638 focus_delay_func,
639 client);
640 break;
641 default:
642 break;
643 }
644 break;
645 case EnterNotify:
646 {
647 gboolean nofocus = FALSE;
648
649 if (ignore_enter_focus) {
650 ignore_enter_focus--;
651 nofocus = TRUE;
652 }
653
654 con = frame_context(client, e->xcrossing.window);
655 switch (con) {
656 case OB_FRAME_CONTEXT_MAXIMIZE:
657 client->frame->max_hover = TRUE;
658 frame_adjust_state(client->frame);
659 break;
660 case OB_FRAME_CONTEXT_ALLDESKTOPS:
661 client->frame->desk_hover = TRUE;
662 frame_adjust_state(client->frame);
663 break;
664 case OB_FRAME_CONTEXT_SHADE:
665 client->frame->shade_hover = TRUE;
666 frame_adjust_state(client->frame);
667 break;
668 case OB_FRAME_CONTEXT_ICONIFY:
669 client->frame->iconify_hover = TRUE;
670 frame_adjust_state(client->frame);
671 break;
672 case OB_FRAME_CONTEXT_CLOSE:
673 client->frame->close_hover = TRUE;
674 frame_adjust_state(client->frame);
675 break;
676 case OB_FRAME_CONTEXT_FRAME:
677 if (e->xcrossing.mode == NotifyGrab ||
678 e->xcrossing.mode == NotifyUngrab)
679 {
680 #ifdef DEBUG_FOCUS
681 ob_debug("%sNotify mode %d detail %d on %lx IGNORED\n",
682 (e->type == EnterNotify ? "Enter" : "Leave"),
683 e->xcrossing.mode,
684 e->xcrossing.detail, client?client->window:0);
685 #endif
686 } else {
687 #ifdef DEBUG_FOCUS
688 ob_debug("%sNotify mode %d detail %d on %lx, "
689 "focusing window: %d\n",
690 (e->type == EnterNotify ? "Enter" : "Leave"),
691 e->xcrossing.mode,
692 e->xcrossing.detail, (client?client->window:0),
693 !nofocus);
694 #endif
695 if (!nofocus && config_focus_follow)
696 event_enter_client(client);
697 }
698 break;
699 default:
700 break;
701 }
702 break;
703 }
704 case ConfigureRequest:
705 /* compress these */
706 while (XCheckTypedWindowEvent(ob_display, client->window,
707 ConfigureRequest, &ce)) {
708 ++i;
709 /* XXX if this causes bad things.. we can compress config req's
710 with the same mask. */
711 e->xconfigurerequest.value_mask |=
712 ce.xconfigurerequest.value_mask;
713 if (ce.xconfigurerequest.value_mask & CWX)
714 e->xconfigurerequest.x = ce.xconfigurerequest.x;
715 if (ce.xconfigurerequest.value_mask & CWY)
716 e->xconfigurerequest.y = ce.xconfigurerequest.y;
717 if (ce.xconfigurerequest.value_mask & CWWidth)
718 e->xconfigurerequest.width = ce.xconfigurerequest.width;
719 if (ce.xconfigurerequest.value_mask & CWHeight)
720 e->xconfigurerequest.height = ce.xconfigurerequest.height;
721 if (ce.xconfigurerequest.value_mask & CWBorderWidth)
722 e->xconfigurerequest.border_width =
723 ce.xconfigurerequest.border_width;
724 if (ce.xconfigurerequest.value_mask & CWStackMode)
725 e->xconfigurerequest.detail = ce.xconfigurerequest.detail;
726 }
727
728 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
729 if (client->iconic || client->shaded) return;
730
731 /* resize, then move, as specified in the EWMH section 7.7 */
732 if (e->xconfigurerequest.value_mask & (CWWidth | CWHeight |
733 CWX | CWY |
734 CWBorderWidth)) {
735 int x, y, w, h;
736 ObCorner corner;
737
738 if (e->xconfigurerequest.value_mask & CWBorderWidth)
739 client->border_width = e->xconfigurerequest.border_width;
740
741 x = (e->xconfigurerequest.value_mask & CWX) ?
742 e->xconfigurerequest.x : client->area.x;
743 y = (e->xconfigurerequest.value_mask & CWY) ?
744 e->xconfigurerequest.y : client->area.y;
745 w = (e->xconfigurerequest.value_mask & CWWidth) ?
746 e->xconfigurerequest.width : client->area.width;
747 h = (e->xconfigurerequest.value_mask & CWHeight) ?
748 e->xconfigurerequest.height : client->area.height;
749
750 {
751 int newx = x;
752 int newy = y;
753 int fw = w +
754 client->frame->size.left + client->frame->size.right;
755 int fh = h +
756 client->frame->size.top + client->frame->size.bottom;
757 client_find_onscreen(client, &newx, &newy, fw, fh,
758 client_normal(client));
759 if (e->xconfigurerequest.value_mask & CWX)
760 x = newx;
761 if (e->xconfigurerequest.value_mask & CWY)
762 y = newy;
763 }
764
765 switch (client->gravity) {
766 case NorthEastGravity:
767 case EastGravity:
768 corner = OB_CORNER_TOPRIGHT;
769 break;
770 case SouthWestGravity:
771 case SouthGravity:
772 corner = OB_CORNER_BOTTOMLEFT;
773 break;
774 case SouthEastGravity:
775 corner = OB_CORNER_BOTTOMRIGHT;
776 break;
777 default: /* NorthWest, Static, etc */
778 corner = OB_CORNER_TOPLEFT;
779 }
780
781 client_configure_full(client, corner, x, y, w, h, FALSE, TRUE,
782 TRUE);
783 }
784
785 if (e->xconfigurerequest.value_mask & CWStackMode) {
786 switch (e->xconfigurerequest.detail) {
787 case Below:
788 case BottomIf:
789 client_lower(client);
790 break;
791
792 case Above:
793 case TopIf:
794 default:
795 client_raise(client);
796 break;
797 }
798 }
799 break;
800 case UnmapNotify:
801 if (client->ignore_unmaps) {
802 client->ignore_unmaps--;
803 break;
804 }
805 client_unmanage(client);
806 break;
807 case DestroyNotify:
808 client_unmanage(client);
809 break;
810 case ReparentNotify:
811 /* this is when the client is first taken captive in the frame */
812 if (e->xreparent.parent == client->frame->plate) break;
813
814 /*
815 This event is quite rare and is usually handled in unmapHandler.
816 However, if the window is unmapped when the reparent event occurs,
817 the window manager never sees it because an unmap event is not sent
818 to an already unmapped window.
819 */
820
821 /* we don't want the reparent event, put it back on the stack for the
822 X server to deal with after we unmanage the window */
823 XPutBackEvent(ob_display, e);
824
825 client_unmanage(client);
826 break;
827 case MapRequest:
828 ob_debug("MapRequest for 0x%lx\n", client->window);
829 if (!client->iconic) break; /* this normally doesn't happen, but if it
830 does, we don't want it! */
831 client_activate(client, FALSE);
832 break;
833 case ClientMessage:
834 /* validate cuz we query stuff off the client here */
835 if (!client_validate(client)) break;
836
837 if (e->xclient.format != 32) return;
838
839 msgtype = e->xclient.message_type;
840 if (msgtype == prop_atoms.wm_change_state) {
841 /* compress changes into a single change */
842 while (XCheckTypedWindowEvent(ob_display, client->window,
843 e->type, &ce)) {
844 /* XXX: it would be nice to compress ALL messages of a
845 type, not just messages in a row without other
846 message types between. */
847 if (ce.xclient.message_type != msgtype) {
848 XPutBackEvent(ob_display, &ce);
849 break;
850 }
851 e->xclient = ce.xclient;
852 }
853 client_set_wm_state(client, e->xclient.data.l[0]);
854 } else if (msgtype == prop_atoms.net_wm_desktop) {
855 /* compress changes into a single change */
856 while (XCheckTypedWindowEvent(ob_display, client->window,
857 e->type, &ce)) {
858 /* XXX: it would be nice to compress ALL messages of a
859 type, not just messages in a row without other
860 message types between. */
861 if (ce.xclient.message_type != msgtype) {
862 XPutBackEvent(ob_display, &ce);
863 break;
864 }
865 e->xclient = ce.xclient;
866 }
867 if ((unsigned)e->xclient.data.l[0] < screen_num_desktops ||
868 (unsigned)e->xclient.data.l[0] == DESKTOP_ALL)
869 client_set_desktop(client, (unsigned)e->xclient.data.l[0],
870 FALSE);
871 } else if (msgtype == prop_atoms.net_wm_state) {
872 /* can't compress these */
873 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
874 (e->xclient.data.l[0] == 0 ? "Remove" :
875 e->xclient.data.l[0] == 1 ? "Add" :
876 e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
877 e->xclient.data.l[1], e->xclient.data.l[2],
878 client->window);
879 client_set_state(client, e->xclient.data.l[0],
880 e->xclient.data.l[1], e->xclient.data.l[2]);
881 } else if (msgtype == prop_atoms.net_close_window) {
882 ob_debug("net_close_window for 0x%lx\n", client->window);
883 client_close(client);
884 } else if (msgtype == prop_atoms.net_active_window) {
885 ob_debug("net_active_window for 0x%lx\n", client->window);
886 client_activate(client, FALSE);
887 } else if (msgtype == prop_atoms.net_wm_moveresize) {
888 ob_debug("net_wm_moveresize for 0x%lx\n", client->window);
889 if ((Atom)e->xclient.data.l[2] ==
890 prop_atoms.net_wm_moveresize_size_topleft ||
891 (Atom)e->xclient.data.l[2] ==
892 prop_atoms.net_wm_moveresize_size_top ||
893 (Atom)e->xclient.data.l[2] ==
894 prop_atoms.net_wm_moveresize_size_topright ||
895 (Atom)e->xclient.data.l[2] ==
896 prop_atoms.net_wm_moveresize_size_right ||
897 (Atom)e->xclient.data.l[2] ==
898 prop_atoms.net_wm_moveresize_size_right ||
899 (Atom)e->xclient.data.l[2] ==
900 prop_atoms.net_wm_moveresize_size_bottomright ||
901 (Atom)e->xclient.data.l[2] ==
902 prop_atoms.net_wm_moveresize_size_bottom ||
903 (Atom)e->xclient.data.l[2] ==
904 prop_atoms.net_wm_moveresize_size_bottomleft ||
905 (Atom)e->xclient.data.l[2] ==
906 prop_atoms.net_wm_moveresize_size_left ||
907 (Atom)e->xclient.data.l[2] ==
908 prop_atoms.net_wm_moveresize_move ||
909 (Atom)e->xclient.data.l[2] ==
910 prop_atoms.net_wm_moveresize_size_keyboard ||
911 (Atom)e->xclient.data.l[2] ==
912 prop_atoms.net_wm_moveresize_move_keyboard) {
913
914 moveresize_start(client, e->xclient.data.l[0],
915 e->xclient.data.l[1], e->xclient.data.l[3],
916 e->xclient.data.l[2]);
917 }
918 } else if (msgtype == prop_atoms.net_moveresize_window) {
919 int oldg = client->gravity;
920 int tmpg, x, y, w, h;
921
922 if (e->xclient.data.l[0] & 0xff)
923 tmpg = e->xclient.data.l[0] & 0xff;
924 else
925 tmpg = oldg;
926
927 if (e->xclient.data.l[0] & 1 << 8)
928 x = e->xclient.data.l[1];
929 else
930 x = client->area.x;
931 if (e->xclient.data.l[0] & 1 << 9)
932 y = e->xclient.data.l[2];
933 else
934 y = client->area.y;
935 if (e->xclient.data.l[0] & 1 << 10)
936 w = e->xclient.data.l[3];
937 else
938 w = client->area.width;
939 if (e->xclient.data.l[0] & 1 << 11)
940 h = e->xclient.data.l[4];
941 else
942 h = client->area.height;
943 client->gravity = tmpg;
944
945 {
946 int newx = x;
947 int newy = y;
948 int fw = w +
949 client->frame->size.left + client->frame->size.right;
950 int fh = h +
951 client->frame->size.top + client->frame->size.bottom;
952 client_find_onscreen(client, &newx, &newy, fw, fh,
953 client_normal(client));
954 if (e->xclient.data.l[0] & 1 << 8)
955 x = newx;
956 if (e->xclient.data.l[0] & 1 << 9)
957 y = newy;
958 }
959
960 client_configure(client, OB_CORNER_TOPLEFT,
961 x, y, w, h, FALSE, TRUE);
962
963 client->gravity = oldg;
964 }
965 break;
966 case PropertyNotify:
967 /* validate cuz we query stuff off the client here */
968 if (!client_validate(client)) break;
969
970 /* compress changes to a single property into a single change */
971 while (XCheckTypedWindowEvent(ob_display, client->window,
972 e->type, &ce)) {
973 Atom a, b;
974
975 /* XXX: it would be nice to compress ALL changes to a property,
976 not just changes in a row without other props between. */
977
978 a = ce.xproperty.atom;
979 b = e->xproperty.atom;
980
981 if (a == b)
982 continue;
983 if ((a == prop_atoms.net_wm_name ||
984 a == prop_atoms.wm_name ||
985 a == prop_atoms.net_wm_icon_name ||
986 a == prop_atoms.wm_icon_name)
987 &&
988 (b == prop_atoms.net_wm_name ||
989 b == prop_atoms.wm_name ||
990 b == prop_atoms.net_wm_icon_name ||
991 b == prop_atoms.wm_icon_name)) {
992 continue;
993 }
994 if ((a == prop_atoms.net_wm_icon ||
995 a == prop_atoms.kwm_win_icon)
996 &&
997 (b == prop_atoms.net_wm_icon ||
998 b == prop_atoms.kwm_win_icon))
999 continue;
1000
1001 XPutBackEvent(ob_display, &ce);
1002 break;
1003 }
1004
1005 msgtype = e->xproperty.atom;
1006 if (msgtype == XA_WM_NORMAL_HINTS) {
1007 client_update_normal_hints(client);
1008 /* normal hints can make a window non-resizable */
1009 client_setup_decor_and_functions(client);
1010 } else if (msgtype == XA_WM_HINTS) {
1011 client_update_wmhints(client);
1012 } else if (msgtype == XA_WM_TRANSIENT_FOR) {
1013 client_update_transient_for(client);
1014 client_get_type(client);
1015 /* type may have changed, so update the layer */
1016 client_calc_layer(client);
1017 client_setup_decor_and_functions(client);
1018 } else if (msgtype == prop_atoms.net_wm_name ||
1019 msgtype == prop_atoms.wm_name ||
1020 msgtype == prop_atoms.net_wm_icon_name ||
1021 msgtype == prop_atoms.wm_icon_name) {
1022 client_update_title(client);
1023 } else if (msgtype == prop_atoms.wm_class) {
1024 client_update_class(client);
1025 } else if (msgtype == prop_atoms.wm_protocols) {
1026 client_update_protocols(client);
1027 client_setup_decor_and_functions(client);
1028 }
1029 else if (msgtype == prop_atoms.net_wm_strut) {
1030 client_update_strut(client);
1031 }
1032 else if (msgtype == prop_atoms.net_wm_icon ||
1033 msgtype == prop_atoms.kwm_win_icon) {
1034 client_update_icons(client);
1035 }
1036 else if (msgtype == prop_atoms.sm_client_id) {
1037 client_update_sm_client_id(client);
1038 }
1039 default:
1040 ;
1041 #ifdef SHAPE
1042 if (extensions_shape && e->type == extensions_shape_event_basep) {
1043 client->shaped = ((XShapeEvent*)e)->shaped;
1044 frame_adjust_shape(client->frame);
1045 }
1046 #endif
1047 }
1048 }
1049
1050 static void event_handle_dock(ObDock *s, XEvent *e)
1051 {
1052 switch (e->type) {
1053 case ButtonPress:
1054 stacking_raise(DOCK_AS_WINDOW(s));
1055 break;
1056 case EnterNotify:
1057 dock_hide(FALSE);
1058 break;
1059 case LeaveNotify:
1060 dock_hide(TRUE);
1061 break;
1062 }
1063 }
1064
1065 static void event_handle_dockapp(ObDockApp *app, XEvent *e)
1066 {
1067 switch (e->type) {
1068 case MotionNotify:
1069 dock_app_drag(app, &e->xmotion);
1070 break;
1071 case UnmapNotify:
1072 if (app->ignore_unmaps) {
1073 app->ignore_unmaps--;
1074 break;
1075 }
1076 dock_remove(app, TRUE);
1077 break;
1078 case DestroyNotify:
1079 dock_remove(app, FALSE);
1080 break;
1081 case ReparentNotify:
1082 dock_remove(app, FALSE);
1083 break;
1084 case ConfigureNotify:
1085 dock_app_configure(app, e->xconfigure.width, e->xconfigure.height);
1086 break;
1087 }
1088 }
1089
1090 ObMenuFrame* find_active_menu()
1091 {
1092 GList *it;
1093 ObMenuFrame *ret = NULL;
1094
1095 for (it = menu_frame_visible; it; it = g_list_next(it)) {
1096 ret = it->data;
1097 if (ret->selected)
1098 break;
1099 ret = NULL;
1100 }
1101 return ret;
1102 }
1103
1104 ObMenuFrame* find_active_or_last_menu()
1105 {
1106 ObMenuFrame *ret = NULL;
1107
1108 ret = find_active_menu();
1109 if (!ret && menu_frame_visible)
1110 ret = menu_frame_visible->data;
1111 return ret;
1112 }
1113
1114 static void event_handle_menu(XEvent *ev)
1115 {
1116 ObMenuFrame *f;
1117 ObMenuEntryFrame *e;
1118
1119 switch (ev->type) {
1120 case ButtonRelease:
1121 if (menu_can_hide) {
1122 if ((e = menu_entry_frame_under(ev->xbutton.x_root,
1123 ev->xbutton.y_root)))
1124 menu_entry_frame_execute(e, ev->xbutton.state);
1125 else
1126 menu_frame_hide_all();
1127 }
1128 break;
1129 case MotionNotify:
1130 if ((f = menu_frame_under(ev->xmotion.x_root,
1131 ev->xmotion.y_root))) {
1132 menu_frame_move_on_screen(f);
1133 if ((e = menu_entry_frame_under(ev->xmotion.x_root,
1134 ev->xmotion.y_root)))
1135 menu_frame_select(f, e);
1136 }
1137 {
1138 ObMenuFrame *a;
1139
1140 a = find_active_menu();
1141 if (a && a != f &&
1142 a->selected->entry->type != OB_MENU_ENTRY_TYPE_SUBMENU)
1143 {
1144 menu_frame_select(a, NULL);
1145 }
1146 }
1147 break;
1148 case KeyPress:
1149 if (ev->xkey.keycode == ob_keycode(OB_KEY_ESCAPE))
1150 menu_frame_hide_all();
1151 else if (ev->xkey.keycode == ob_keycode(OB_KEY_RETURN)) {
1152 ObMenuFrame *f;
1153 if ((f = find_active_menu()))
1154 menu_entry_frame_execute(f->selected, ev->xkey.state);
1155 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_LEFT)) {
1156 ObMenuFrame *f;
1157 if ((f = find_active_or_last_menu()) && f->parent)
1158 menu_frame_select(f, NULL);
1159 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_RIGHT)) {
1160 ObMenuFrame *f;
1161 if ((f = find_active_or_last_menu()) && f->child)
1162 menu_frame_select_next(f->child);
1163 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_UP)) {
1164 ObMenuFrame *f;
1165 if ((f = find_active_or_last_menu()))
1166 menu_frame_select_previous(f);
1167 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_DOWN)) {
1168 ObMenuFrame *f;
1169 if ((f = find_active_or_last_menu()))
1170 menu_frame_select_next(f);
1171 }
1172 break;
1173 }
1174 }
1175
1176 static gboolean menu_hide_delay_func(gpointer data)
1177 {
1178 menu_can_hide = TRUE;
1179 return FALSE; /* no repeat */
1180 }
1181
1182 static gboolean focus_delay_func(gpointer data)
1183 {
1184 ObClient *c = data;
1185
1186 if (focus_client != c) {
1187 client_focus(c);
1188 if (config_focus_raise)
1189 client_raise(c);
1190 }
1191 return FALSE; /* no repeat */
1192 }
1193
1194 static void focus_delay_client_dest(ObClient *client, gpointer data)
1195 {
1196 ob_main_loop_timeout_remove_data(ob_main_loop, focus_delay_func, client);
1197 }
1198
1199 void event_halt_focus_delay()
1200 {
1201 ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
1202 }
1203
1204 void event_ignore_queued_enters()
1205 {
1206 GSList *saved = NULL, *it;
1207 XEvent *e;
1208
1209 XSync(ob_display, FALSE);
1210
1211 /* count the events */
1212 while (TRUE) {
1213 e = g_new(XEvent, 1);
1214 if (XCheckTypedEvent(ob_display, EnterNotify, e)) {
1215 ObWindow *win;
1216
1217 win = g_hash_table_lookup(window_map, &e->xany.window);
1218 if (win && WINDOW_IS_CLIENT(win))
1219 ++ignore_enter_focus;
1220
1221 saved = g_slist_append(saved, e);
1222 } else {
1223 g_free(e);
1224 break;
1225 }
1226 }
1227 /* put the events back */
1228 for (it = saved; it; it = g_slist_next(it)) {
1229 XPutBackEvent(ob_display, it->data);
1230 g_free(it->data);
1231 }
1232 g_slist_free(saved);
1233 }
This page took 0.087849 seconds and 5 git commands to generate.