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