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