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