]> Dogcows Code - chaz/openbox/blob - openbox/event.c
make keyboard events work on the focus_cycle_target again
[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_cycle_target ? focus_cycle_target :
467 (focus_hilite ? focus_hilite : client)),
468 e);
469 }
470 }
471 }
472 }
473
474 static void event_handle_root(XEvent *e)
475 {
476 Atom msgtype;
477
478 switch(e->type) {
479 case SelectionClear:
480 ob_debug("Another WM has requested to replace us. Exiting.\n");
481 ob_exit(0);
482 break;
483
484 case ClientMessage:
485 if (e->xclient.format != 32) break;
486
487 msgtype = e->xclient.message_type;
488 if (msgtype == prop_atoms.net_current_desktop) {
489 unsigned int d = e->xclient.data.l[0];
490 if (d < screen_num_desktops)
491 screen_set_desktop(d);
492 } else if (msgtype == prop_atoms.net_number_of_desktops) {
493 unsigned int d = e->xclient.data.l[0];
494 if (d > 0)
495 screen_set_num_desktops(d);
496 } else if (msgtype == prop_atoms.net_showing_desktop) {
497 screen_show_desktop(e->xclient.data.l[0] != 0);
498 }
499 break;
500 case PropertyNotify:
501 if (e->xproperty.atom == prop_atoms.net_desktop_names)
502 screen_update_desktop_names();
503 else if (e->xproperty.atom == prop_atoms.net_desktop_layout)
504 screen_update_layout();
505 break;
506 case ConfigureNotify:
507 #ifdef XRANDR
508 XRRUpdateConfiguration(e);
509 #endif
510 screen_resize();
511 break;
512 default:
513 ;
514 #ifdef VIDMODE
515 if (extensions_vidmode && e->type == extensions_vidmode_event_basep) {
516 ob_debug("VIDMODE EVENT\n");
517 }
518 #endif
519 }
520 }
521
522 static void event_handle_group(ObGroup *group, XEvent *e)
523 {
524 GSList *it;
525
526 g_assert(e->type == PropertyNotify);
527
528 for (it = group->members; it; it = g_slist_next(it))
529 event_handle_client(it->data, e);
530 }
531
532 void event_enter_client(ObClient *client)
533 {
534 g_assert(config_focus_follow);
535
536 if (client_normal(client) && client_can_focus(client)) {
537 if (config_focus_delay) {
538 ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
539 ob_main_loop_timeout_add(ob_main_loop,
540 config_focus_delay,
541 focus_delay_func,
542 client, NULL);
543 } else
544 focus_delay_func(client);
545 }
546 }
547
548 static void event_handle_client(ObClient *client, XEvent *e)
549 {
550 XEvent ce;
551 Atom msgtype;
552 int i=0;
553 ObFrameContext con;
554
555 switch (e->type) {
556 case VisibilityNotify:
557 client->frame->obscured = e->xvisibility.state != VisibilityUnobscured;
558 break;
559 case ButtonPress:
560 case ButtonRelease:
561 /* Wheel buttons don't draw because they are an instant click, so it
562 is a waste of resources to go drawing it. */
563 if (!(e->xbutton.button == 4 || e->xbutton.button == 5)) {
564 con = frame_context(client, e->xbutton.window);
565 con = mouse_button_frame_context(con, e->xbutton.button);
566 switch (con) {
567 case OB_FRAME_CONTEXT_MAXIMIZE:
568 client->frame->max_press = (e->type == ButtonPress);
569 framerender_frame(client->frame);
570 break;
571 case OB_FRAME_CONTEXT_CLOSE:
572 client->frame->close_press = (e->type == ButtonPress);
573 framerender_frame(client->frame);
574 break;
575 case OB_FRAME_CONTEXT_ICONIFY:
576 client->frame->iconify_press = (e->type == ButtonPress);
577 framerender_frame(client->frame);
578 break;
579 case OB_FRAME_CONTEXT_ALLDESKTOPS:
580 client->frame->desk_press = (e->type == ButtonPress);
581 framerender_frame(client->frame);
582 break;
583 case OB_FRAME_CONTEXT_SHADE:
584 client->frame->shade_press = (e->type == ButtonPress);
585 framerender_frame(client->frame);
586 break;
587 default:
588 /* nothing changes with clicks for any other contexts */
589 break;
590 }
591 }
592 break;
593 case FocusIn:
594 #ifdef DEBUG_FOCUS
595 ob_debug("FocusIn on client for %lx (client %lx) mode %d detail %d\n",
596 e->xfocus.window, client->window,
597 e->xfocus.mode, e->xfocus.detail);
598 #endif
599 focus_in = client;
600 if (focus_out == client)
601 focus_out = NULL;
602 break;
603 case FocusOut:
604 #ifdef DEBUG_FOCUS
605 ob_debug("FocusOut on client for %lx (client %lx) mode %d detail %d\n",
606 e->xfocus.window, client->window,
607 e->xfocus.mode, e->xfocus.detail);
608 #endif
609 focus_out = client;
610 if (focus_in == client)
611 focus_in = NULL;
612 break;
613 case LeaveNotify:
614 con = frame_context(client, e->xcrossing.window);
615 switch (con) {
616 case OB_FRAME_CONTEXT_MAXIMIZE:
617 client->frame->max_hover = FALSE;
618 frame_adjust_state(client->frame);
619 break;
620 case OB_FRAME_CONTEXT_ALLDESKTOPS:
621 client->frame->desk_hover = FALSE;
622 frame_adjust_state(client->frame);
623 break;
624 case OB_FRAME_CONTEXT_SHADE:
625 client->frame->shade_hover = FALSE;
626 frame_adjust_state(client->frame);
627 break;
628 case OB_FRAME_CONTEXT_ICONIFY:
629 client->frame->iconify_hover = FALSE;
630 frame_adjust_state(client->frame);
631 break;
632 case OB_FRAME_CONTEXT_CLOSE:
633 client->frame->close_hover = FALSE;
634 frame_adjust_state(client->frame);
635 break;
636 case OB_FRAME_CONTEXT_FRAME:
637 if (config_focus_follow && config_focus_delay)
638 ob_main_loop_timeout_remove_data(ob_main_loop,
639 focus_delay_func,
640 client);
641 break;
642 default:
643 break;
644 }
645 break;
646 case EnterNotify:
647 {
648 gboolean nofocus = FALSE;
649
650 if (ignore_enter_focus) {
651 ignore_enter_focus--;
652 nofocus = TRUE;
653 }
654
655 con = frame_context(client, e->xcrossing.window);
656 switch (con) {
657 case OB_FRAME_CONTEXT_MAXIMIZE:
658 client->frame->max_hover = TRUE;
659 frame_adjust_state(client->frame);
660 break;
661 case OB_FRAME_CONTEXT_ALLDESKTOPS:
662 client->frame->desk_hover = TRUE;
663 frame_adjust_state(client->frame);
664 break;
665 case OB_FRAME_CONTEXT_SHADE:
666 client->frame->shade_hover = TRUE;
667 frame_adjust_state(client->frame);
668 break;
669 case OB_FRAME_CONTEXT_ICONIFY:
670 client->frame->iconify_hover = TRUE;
671 frame_adjust_state(client->frame);
672 break;
673 case OB_FRAME_CONTEXT_CLOSE:
674 client->frame->close_hover = TRUE;
675 frame_adjust_state(client->frame);
676 break;
677 case OB_FRAME_CONTEXT_FRAME:
678 if (e->xcrossing.mode == NotifyGrab ||
679 e->xcrossing.mode == NotifyUngrab)
680 {
681 #ifdef DEBUG_FOCUS
682 ob_debug("%sNotify mode %d detail %d on %lx IGNORED\n",
683 (e->type == EnterNotify ? "Enter" : "Leave"),
684 e->xcrossing.mode,
685 e->xcrossing.detail, client?client->window:0);
686 #endif
687 } else {
688 #ifdef DEBUG_FOCUS
689 ob_debug("%sNotify mode %d detail %d on %lx, "
690 "focusing window: %d\n",
691 (e->type == EnterNotify ? "Enter" : "Leave"),
692 e->xcrossing.mode,
693 e->xcrossing.detail, (client?client->window:0),
694 !nofocus);
695 #endif
696 if (!nofocus && config_focus_follow)
697 event_enter_client(client);
698 }
699 break;
700 default:
701 break;
702 }
703 break;
704 }
705 case ConfigureRequest:
706 /* compress these */
707 while (XCheckTypedWindowEvent(ob_display, client->window,
708 ConfigureRequest, &ce)) {
709 ++i;
710 /* XXX if this causes bad things.. we can compress config req's
711 with the same mask. */
712 e->xconfigurerequest.value_mask |=
713 ce.xconfigurerequest.value_mask;
714 if (ce.xconfigurerequest.value_mask & CWX)
715 e->xconfigurerequest.x = ce.xconfigurerequest.x;
716 if (ce.xconfigurerequest.value_mask & CWY)
717 e->xconfigurerequest.y = ce.xconfigurerequest.y;
718 if (ce.xconfigurerequest.value_mask & CWWidth)
719 e->xconfigurerequest.width = ce.xconfigurerequest.width;
720 if (ce.xconfigurerequest.value_mask & CWHeight)
721 e->xconfigurerequest.height = ce.xconfigurerequest.height;
722 if (ce.xconfigurerequest.value_mask & CWBorderWidth)
723 e->xconfigurerequest.border_width =
724 ce.xconfigurerequest.border_width;
725 if (ce.xconfigurerequest.value_mask & CWStackMode)
726 e->xconfigurerequest.detail = ce.xconfigurerequest.detail;
727 }
728
729 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
730 if (client->iconic || client->shaded) return;
731
732 /* resize, then move, as specified in the EWMH section 7.7 */
733 if (e->xconfigurerequest.value_mask & (CWWidth | CWHeight |
734 CWX | CWY |
735 CWBorderWidth)) {
736 int x, y, w, h;
737 ObCorner corner;
738
739 if (e->xconfigurerequest.value_mask & CWBorderWidth)
740 client->border_width = e->xconfigurerequest.border_width;
741
742 x = (e->xconfigurerequest.value_mask & CWX) ?
743 e->xconfigurerequest.x : client->area.x;
744 y = (e->xconfigurerequest.value_mask & CWY) ?
745 e->xconfigurerequest.y : client->area.y;
746 w = (e->xconfigurerequest.value_mask & CWWidth) ?
747 e->xconfigurerequest.width : client->area.width;
748 h = (e->xconfigurerequest.value_mask & CWHeight) ?
749 e->xconfigurerequest.height : client->area.height;
750
751 {
752 int newx = x;
753 int newy = y;
754 int fw = w +
755 client->frame->size.left + client->frame->size.right;
756 int fh = h +
757 client->frame->size.top + client->frame->size.bottom;
758 client_find_onscreen(client, &newx, &newy, fw, fh,
759 client_normal(client));
760 if (e->xconfigurerequest.value_mask & CWX)
761 x = newx;
762 if (e->xconfigurerequest.value_mask & CWY)
763 y = newy;
764 }
765
766 switch (client->gravity) {
767 case NorthEastGravity:
768 case EastGravity:
769 corner = OB_CORNER_TOPRIGHT;
770 break;
771 case SouthWestGravity:
772 case SouthGravity:
773 corner = OB_CORNER_BOTTOMLEFT;
774 break;
775 case SouthEastGravity:
776 corner = OB_CORNER_BOTTOMRIGHT;
777 break;
778 default: /* NorthWest, Static, etc */
779 corner = OB_CORNER_TOPLEFT;
780 }
781
782 client_configure_full(client, corner, x, y, w, h, FALSE, TRUE,
783 TRUE);
784 }
785
786 if (e->xconfigurerequest.value_mask & CWStackMode) {
787 switch (e->xconfigurerequest.detail) {
788 case Below:
789 case BottomIf:
790 client_lower(client);
791 break;
792
793 case Above:
794 case TopIf:
795 default:
796 client_raise(client);
797 break;
798 }
799 }
800 break;
801 case UnmapNotify:
802 if (client->ignore_unmaps) {
803 client->ignore_unmaps--;
804 break;
805 }
806 client_unmanage(client);
807 break;
808 case DestroyNotify:
809 client_unmanage(client);
810 break;
811 case ReparentNotify:
812 /* this is when the client is first taken captive in the frame */
813 if (e->xreparent.parent == client->frame->plate) break;
814
815 /*
816 This event is quite rare and is usually handled in unmapHandler.
817 However, if the window is unmapped when the reparent event occurs,
818 the window manager never sees it because an unmap event is not sent
819 to an already unmapped window.
820 */
821
822 /* we don't want the reparent event, put it back on the stack for the
823 X server to deal with after we unmanage the window */
824 XPutBackEvent(ob_display, e);
825
826 client_unmanage(client);
827 break;
828 case MapRequest:
829 ob_debug("MapRequest for 0x%lx\n", client->window);
830 if (!client->iconic) break; /* this normally doesn't happen, but if it
831 does, we don't want it! */
832 client_activate(client, FALSE);
833 break;
834 case ClientMessage:
835 /* validate cuz we query stuff off the client here */
836 if (!client_validate(client)) break;
837
838 if (e->xclient.format != 32) return;
839
840 msgtype = e->xclient.message_type;
841 if (msgtype == prop_atoms.wm_change_state) {
842 /* compress changes into a single change */
843 while (XCheckTypedWindowEvent(ob_display, client->window,
844 e->type, &ce)) {
845 /* XXX: it would be nice to compress ALL messages of a
846 type, not just messages in a row without other
847 message types between. */
848 if (ce.xclient.message_type != msgtype) {
849 XPutBackEvent(ob_display, &ce);
850 break;
851 }
852 e->xclient = ce.xclient;
853 }
854 client_set_wm_state(client, e->xclient.data.l[0]);
855 } else if (msgtype == prop_atoms.net_wm_desktop) {
856 /* compress changes into a single change */
857 while (XCheckTypedWindowEvent(ob_display, client->window,
858 e->type, &ce)) {
859 /* XXX: it would be nice to compress ALL messages of a
860 type, not just messages in a row without other
861 message types between. */
862 if (ce.xclient.message_type != msgtype) {
863 XPutBackEvent(ob_display, &ce);
864 break;
865 }
866 e->xclient = ce.xclient;
867 }
868 if ((unsigned)e->xclient.data.l[0] < screen_num_desktops ||
869 (unsigned)e->xclient.data.l[0] == DESKTOP_ALL)
870 client_set_desktop(client, (unsigned)e->xclient.data.l[0],
871 FALSE);
872 } else if (msgtype == prop_atoms.net_wm_state) {
873 /* can't compress these */
874 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
875 (e->xclient.data.l[0] == 0 ? "Remove" :
876 e->xclient.data.l[0] == 1 ? "Add" :
877 e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
878 e->xclient.data.l[1], e->xclient.data.l[2],
879 client->window);
880 client_set_state(client, e->xclient.data.l[0],
881 e->xclient.data.l[1], e->xclient.data.l[2]);
882 } else if (msgtype == prop_atoms.net_close_window) {
883 ob_debug("net_close_window for 0x%lx\n", client->window);
884 client_close(client);
885 } else if (msgtype == prop_atoms.net_active_window) {
886 ob_debug("net_active_window for 0x%lx\n", client->window);
887 client_activate(client, FALSE);
888 } else if (msgtype == prop_atoms.net_wm_moveresize) {
889 ob_debug("net_wm_moveresize for 0x%lx\n", client->window);
890 if ((Atom)e->xclient.data.l[2] ==
891 prop_atoms.net_wm_moveresize_size_topleft ||
892 (Atom)e->xclient.data.l[2] ==
893 prop_atoms.net_wm_moveresize_size_top ||
894 (Atom)e->xclient.data.l[2] ==
895 prop_atoms.net_wm_moveresize_size_topright ||
896 (Atom)e->xclient.data.l[2] ==
897 prop_atoms.net_wm_moveresize_size_right ||
898 (Atom)e->xclient.data.l[2] ==
899 prop_atoms.net_wm_moveresize_size_right ||
900 (Atom)e->xclient.data.l[2] ==
901 prop_atoms.net_wm_moveresize_size_bottomright ||
902 (Atom)e->xclient.data.l[2] ==
903 prop_atoms.net_wm_moveresize_size_bottom ||
904 (Atom)e->xclient.data.l[2] ==
905 prop_atoms.net_wm_moveresize_size_bottomleft ||
906 (Atom)e->xclient.data.l[2] ==
907 prop_atoms.net_wm_moveresize_size_left ||
908 (Atom)e->xclient.data.l[2] ==
909 prop_atoms.net_wm_moveresize_move ||
910 (Atom)e->xclient.data.l[2] ==
911 prop_atoms.net_wm_moveresize_size_keyboard ||
912 (Atom)e->xclient.data.l[2] ==
913 prop_atoms.net_wm_moveresize_move_keyboard) {
914
915 moveresize_start(client, e->xclient.data.l[0],
916 e->xclient.data.l[1], e->xclient.data.l[3],
917 e->xclient.data.l[2]);
918 }
919 } else if (msgtype == prop_atoms.net_moveresize_window) {
920 int oldg = client->gravity;
921 int tmpg, x, y, w, h;
922
923 if (e->xclient.data.l[0] & 0xff)
924 tmpg = e->xclient.data.l[0] & 0xff;
925 else
926 tmpg = oldg;
927
928 if (e->xclient.data.l[0] & 1 << 8)
929 x = e->xclient.data.l[1];
930 else
931 x = client->area.x;
932 if (e->xclient.data.l[0] & 1 << 9)
933 y = e->xclient.data.l[2];
934 else
935 y = client->area.y;
936 if (e->xclient.data.l[0] & 1 << 10)
937 w = e->xclient.data.l[3];
938 else
939 w = client->area.width;
940 if (e->xclient.data.l[0] & 1 << 11)
941 h = e->xclient.data.l[4];
942 else
943 h = client->area.height;
944 client->gravity = tmpg;
945
946 {
947 int newx = x;
948 int newy = y;
949 int fw = w +
950 client->frame->size.left + client->frame->size.right;
951 int fh = h +
952 client->frame->size.top + client->frame->size.bottom;
953 client_find_onscreen(client, &newx, &newy, fw, fh,
954 client_normal(client));
955 if (e->xclient.data.l[0] & 1 << 8)
956 x = newx;
957 if (e->xclient.data.l[0] & 1 << 9)
958 y = newy;
959 }
960
961 client_configure(client, OB_CORNER_TOPLEFT,
962 x, y, w, h, FALSE, TRUE);
963
964 client->gravity = oldg;
965 }
966 break;
967 case PropertyNotify:
968 /* validate cuz we query stuff off the client here */
969 if (!client_validate(client)) break;
970
971 /* compress changes to a single property into a single change */
972 while (XCheckTypedWindowEvent(ob_display, client->window,
973 e->type, &ce)) {
974 Atom a, b;
975
976 /* XXX: it would be nice to compress ALL changes to a property,
977 not just changes in a row without other props between. */
978
979 a = ce.xproperty.atom;
980 b = e->xproperty.atom;
981
982 if (a == b)
983 continue;
984 if ((a == prop_atoms.net_wm_name ||
985 a == prop_atoms.wm_name ||
986 a == prop_atoms.net_wm_icon_name ||
987 a == prop_atoms.wm_icon_name)
988 &&
989 (b == prop_atoms.net_wm_name ||
990 b == prop_atoms.wm_name ||
991 b == prop_atoms.net_wm_icon_name ||
992 b == prop_atoms.wm_icon_name)) {
993 continue;
994 }
995 if ((a == prop_atoms.net_wm_icon ||
996 a == prop_atoms.kwm_win_icon)
997 &&
998 (b == prop_atoms.net_wm_icon ||
999 b == prop_atoms.kwm_win_icon))
1000 continue;
1001
1002 XPutBackEvent(ob_display, &ce);
1003 break;
1004 }
1005
1006 msgtype = e->xproperty.atom;
1007 if (msgtype == XA_WM_NORMAL_HINTS) {
1008 client_update_normal_hints(client);
1009 /* normal hints can make a window non-resizable */
1010 client_setup_decor_and_functions(client);
1011 } else if (msgtype == XA_WM_HINTS) {
1012 client_update_wmhints(client);
1013 } else if (msgtype == XA_WM_TRANSIENT_FOR) {
1014 client_update_transient_for(client);
1015 client_get_type(client);
1016 /* type may have changed, so update the layer */
1017 client_calc_layer(client);
1018 client_setup_decor_and_functions(client);
1019 } else if (msgtype == prop_atoms.net_wm_name ||
1020 msgtype == prop_atoms.wm_name ||
1021 msgtype == prop_atoms.net_wm_icon_name ||
1022 msgtype == prop_atoms.wm_icon_name) {
1023 client_update_title(client);
1024 } else if (msgtype == prop_atoms.wm_class) {
1025 client_update_class(client);
1026 } else if (msgtype == prop_atoms.wm_protocols) {
1027 client_update_protocols(client);
1028 client_setup_decor_and_functions(client);
1029 }
1030 else if (msgtype == prop_atoms.net_wm_strut) {
1031 client_update_strut(client);
1032 }
1033 else if (msgtype == prop_atoms.net_wm_icon ||
1034 msgtype == prop_atoms.kwm_win_icon) {
1035 client_update_icons(client);
1036 }
1037 else if (msgtype == prop_atoms.sm_client_id) {
1038 client_update_sm_client_id(client);
1039 }
1040 default:
1041 ;
1042 #ifdef SHAPE
1043 if (extensions_shape && e->type == extensions_shape_event_basep) {
1044 client->shaped = ((XShapeEvent*)e)->shaped;
1045 frame_adjust_shape(client->frame);
1046 }
1047 #endif
1048 }
1049 }
1050
1051 static void event_handle_dock(ObDock *s, XEvent *e)
1052 {
1053 switch (e->type) {
1054 case ButtonPress:
1055 stacking_raise(DOCK_AS_WINDOW(s));
1056 break;
1057 case EnterNotify:
1058 dock_hide(FALSE);
1059 break;
1060 case LeaveNotify:
1061 dock_hide(TRUE);
1062 break;
1063 }
1064 }
1065
1066 static void event_handle_dockapp(ObDockApp *app, XEvent *e)
1067 {
1068 switch (e->type) {
1069 case MotionNotify:
1070 dock_app_drag(app, &e->xmotion);
1071 break;
1072 case UnmapNotify:
1073 if (app->ignore_unmaps) {
1074 app->ignore_unmaps--;
1075 break;
1076 }
1077 dock_remove(app, TRUE);
1078 break;
1079 case DestroyNotify:
1080 dock_remove(app, FALSE);
1081 break;
1082 case ReparentNotify:
1083 dock_remove(app, FALSE);
1084 break;
1085 case ConfigureNotify:
1086 dock_app_configure(app, e->xconfigure.width, e->xconfigure.height);
1087 break;
1088 }
1089 }
1090
1091 ObMenuFrame* find_active_menu()
1092 {
1093 GList *it;
1094 ObMenuFrame *ret = NULL;
1095
1096 for (it = menu_frame_visible; it; it = g_list_next(it)) {
1097 ret = it->data;
1098 if (ret->selected)
1099 break;
1100 ret = NULL;
1101 }
1102 return ret;
1103 }
1104
1105 ObMenuFrame* find_active_or_last_menu()
1106 {
1107 ObMenuFrame *ret = NULL;
1108
1109 ret = find_active_menu();
1110 if (!ret && menu_frame_visible)
1111 ret = menu_frame_visible->data;
1112 return ret;
1113 }
1114
1115 static void event_handle_menu(XEvent *ev)
1116 {
1117 ObMenuFrame *f;
1118 ObMenuEntryFrame *e;
1119
1120 switch (ev->type) {
1121 case ButtonRelease:
1122 if (menu_can_hide) {
1123 if ((e = menu_entry_frame_under(ev->xbutton.x_root,
1124 ev->xbutton.y_root)))
1125 menu_entry_frame_execute(e, ev->xbutton.state);
1126 else
1127 menu_frame_hide_all();
1128 }
1129 break;
1130 case MotionNotify:
1131 if ((f = menu_frame_under(ev->xmotion.x_root,
1132 ev->xmotion.y_root))) {
1133 menu_frame_move_on_screen(f);
1134 if ((e = menu_entry_frame_under(ev->xmotion.x_root,
1135 ev->xmotion.y_root)))
1136 menu_frame_select(f, e);
1137 }
1138 {
1139 ObMenuFrame *a;
1140
1141 a = find_active_menu();
1142 if (a && a != f &&
1143 a->selected->entry->type != OB_MENU_ENTRY_TYPE_SUBMENU)
1144 {
1145 menu_frame_select(a, NULL);
1146 }
1147 }
1148 break;
1149 case KeyPress:
1150 if (ev->xkey.keycode == ob_keycode(OB_KEY_ESCAPE))
1151 menu_frame_hide_all();
1152 else if (ev->xkey.keycode == ob_keycode(OB_KEY_RETURN)) {
1153 ObMenuFrame *f;
1154 if ((f = find_active_menu()))
1155 menu_entry_frame_execute(f->selected, ev->xkey.state);
1156 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_LEFT)) {
1157 ObMenuFrame *f;
1158 if ((f = find_active_or_last_menu()) && f->parent)
1159 menu_frame_select(f, NULL);
1160 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_RIGHT)) {
1161 ObMenuFrame *f;
1162 if ((f = find_active_or_last_menu()) && f->child)
1163 menu_frame_select_next(f->child);
1164 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_UP)) {
1165 ObMenuFrame *f;
1166 if ((f = find_active_or_last_menu()))
1167 menu_frame_select_previous(f);
1168 } else if (ev->xkey.keycode == ob_keycode(OB_KEY_DOWN)) {
1169 ObMenuFrame *f;
1170 if ((f = find_active_or_last_menu()))
1171 menu_frame_select_next(f);
1172 }
1173 break;
1174 }
1175 }
1176
1177 static gboolean menu_hide_delay_func(gpointer data)
1178 {
1179 menu_can_hide = TRUE;
1180 return FALSE; /* no repeat */
1181 }
1182
1183 static gboolean focus_delay_func(gpointer data)
1184 {
1185 ObClient *c = data;
1186
1187 if (focus_client != c) {
1188 client_focus(c);
1189 if (config_focus_raise)
1190 client_raise(c);
1191 }
1192 return FALSE; /* no repeat */
1193 }
1194
1195 static void focus_delay_client_dest(ObClient *client, gpointer data)
1196 {
1197 ob_main_loop_timeout_remove_data(ob_main_loop, focus_delay_func, client);
1198 }
1199
1200 void event_halt_focus_delay()
1201 {
1202 ob_main_loop_timeout_remove(ob_main_loop, focus_delay_func);
1203 }
1204
1205 void event_ignore_queued_enters()
1206 {
1207 GSList *saved = NULL, *it;
1208 XEvent *e;
1209
1210 XSync(ob_display, FALSE);
1211
1212 /* count the events */
1213 while (TRUE) {
1214 e = g_new(XEvent, 1);
1215 if (XCheckTypedEvent(ob_display, EnterNotify, e)) {
1216 ObWindow *win;
1217
1218 win = g_hash_table_lookup(window_map, &e->xany.window);
1219 if (win && WINDOW_IS_CLIENT(win))
1220 ++ignore_enter_focus;
1221
1222 saved = g_slist_append(saved, e);
1223 } else {
1224 g_free(e);
1225 break;
1226 }
1227 }
1228 /* put the events back */
1229 for (it = saved; it; it = g_slist_next(it)) {
1230 XPutBackEvent(ob_display, it->data);
1231 g_free(it->data);
1232 }
1233 g_slist_free(saved);
1234 }
This page took 0.089238 seconds and 5 git commands to generate.