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