]> Dogcows Code - chaz/openbox/blob - openbox/event.c
b32736ecac14969ce3001ed9f8a68b08ac44693d
[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 "keyboard.h"
12 #include "mouse.h"
13 #include "framerender.h"
14 #include "focus.h"
15 #include "moveresize.h"
16 #include "stacking.h"
17 #include "extensions.h"
18 #include "timer.h"
19 #include "dispatch.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_dock(ObDock *s, XEvent *e);
45 static void event_handle_dockapp(ObDockApp *app, XEvent *e);
46 static void event_handle_client(ObClient *c, XEvent *e);
47 static void event_handle_menu(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 /* compress events */
302 {
303 XEvent ce;
304 while (XCheckTypedWindowEvent(ob_display, e->xmotion.window,
305 e->type, &ce)) {
306 e->xmotion.x_root = ce.xmotion.x_root;
307 e->xmotion.y_root = ce.xmotion.y_root;
308 }
309 }
310 break;
311 }
312 }
313
314 static gboolean event_ignore(XEvent *e, ObClient *client)
315 {
316 switch(e->type) {
317 case FocusIn:
318 /* NotifyAncestor is not ignored in FocusIn like it is in FocusOut
319 because of RevertToPointerRoot. If the focus ends up reverting to
320 pointer root on a workspace change, then the FocusIn event that we
321 want will be of type NotifyAncestor. This situation does not occur
322 for FocusOut, so it is safely ignored there.
323 */
324 if (INVALID_FOCUSIN(e) ||
325 client == NULL) {
326 #ifdef DEBUG_FOCUS
327 ob_debug("FocusIn on %lx mode %d detail %d IGNORED\n",
328 e->xfocus.window, e->xfocus.mode, e->xfocus.detail);
329 #endif
330 /* says a client was not found for the event (or a valid FocusIn
331 event was not found.
332 */
333 e->xfocus.window = None;
334 return TRUE;
335 }
336
337 #ifdef DEBUG_FOCUS
338 ob_debug("FocusIn on %lx mode %d detail %d\n", e->xfocus.window,
339 e->xfocus.mode, e->xfocus.detail);
340 #endif
341 break;
342 case FocusOut:
343 if (INVALID_FOCUSOUT(e)) {
344 #ifdef DEBUG_FOCUS
345 ob_debug("FocusOut on %lx mode %d detail %d IGNORED\n",
346 e->xfocus.window, e->xfocus.mode, e->xfocus.detail);
347 #endif
348 return TRUE;
349 }
350
351 #ifdef DEBUG_FOCUS
352 ob_debug("FocusOut on %lx mode %d detail %d\n",
353 e->xfocus.window, e->xfocus.mode, e->xfocus.detail);
354 #endif
355
356 {
357 XEvent fe;
358 gboolean fallback = TRUE;
359
360 while (TRUE) {
361 if (!XCheckTypedWindowEvent(ob_display, FocusOut,
362 e->xfocus.window,&fe))
363 if (!XCheckTypedEvent(ob_display, FocusIn, &fe))
364 break;
365 if (fe.type == FocusOut) {
366 #ifdef DEBUG_FOCUS
367 ob_debug("found pending FocusOut");
368 #endif
369 if (!INVALID_FOCUSOUT(&fe)) {
370 /* if there is a VALID FocusOut still coming, don't
371 fallback focus yet, we'll deal with it then */
372 XPutBackEvent(ob_display, &fe);
373 fallback = FALSE;
374 break;
375 }
376 } else {
377 #ifdef DEBUG_FOCUS
378 ob_debug("found pending FocusIn");
379 #endif
380 /* is the focused window getting a FocusOut/In back to
381 itself?
382 */
383 if (fe.xfocus.window == e->xfocus.window &&
384 !event_ignore(&fe, client)) {
385 /*
386 if focus_client is not set, then we can't do
387 this. we need the FocusIn. This happens in the
388 case when the set_focus_client(NULL) in the
389 focus_fallback function fires and then
390 focus_fallback picks the currently focused
391 window (such as on a SendToDesktop-esque action.
392 */
393 if (focus_client) {
394 #ifdef DEBUG_FOCUS
395 ob_debug("focused window got an Out/In back to "
396 "itself IGNORED both");
397 #endif
398 return TRUE;
399 } else {
400 event_process(&fe);
401 #ifdef DEBUG_FOCUS
402 ob_debug("focused window got an Out/In back to "
403 "itself but focus_client was null "
404 "IGNORED just the Out");
405 #endif
406 return TRUE;
407 }
408 }
409
410 /* once all the FocusOut's have been dealt with, if there
411 is a FocusIn still left and it is valid, then use it */
412 event_process(&fe);
413 /* secret magic way of event_process telling us that no
414 client was found for the FocusIn event. ^_^ */
415 if (fe.xfocus.window != None) {
416 fallback = FALSE;
417 break;
418 }
419 }
420 }
421 if (fallback) {
422 #ifdef DEBUG_FOCUS
423 ob_debug("no valid FocusIn and no FocusOut events found, "
424 "falling back");
425 #endif
426 focus_fallback(OB_FOCUS_FALLBACK_NOFOCUS);
427 }
428 }
429 break;
430 case EnterNotify:
431 case LeaveNotify:
432 /* NotifyUngrab occurs when a mouse button is released and the event is
433 caused, like when lowering a window */
434 /* NotifyVirtual occurs when ungrabbing the pointer */
435 if (e->xcrossing.mode == NotifyGrab ||
436 e->xcrossing.detail == NotifyInferior ||
437 (e->xcrossing.mode == NotifyUngrab &&
438 e->xcrossing.detail == NotifyVirtual)) {
439 #ifdef DEBUG_FOCUS
440 ob_debug("%sNotify mode %d detail %d on %lx IGNORED",
441 (e->type == EnterNotify ? "Enter" : "Leave"),
442 e->xcrossing.mode,
443 e->xcrossing.detail, client?client->window:0);
444 #endif
445 return TRUE;
446 }
447 #ifdef DEBUG_FOCUS
448 ob_debug("%sNotify mode %d detail %d on %lx",
449 (e->type == EnterNotify ? "Enter" : "Leave"),
450 e->xcrossing.mode,
451 e->xcrossing.detail, client?client->window:0);
452 #endif
453 break;
454 }
455 return FALSE;
456 }
457
458 static void event_process(XEvent *e)
459 {
460 Window window;
461 ObClient *client = NULL;
462 ObDock *dock = NULL;
463 ObDockApp *dockapp = NULL;
464 ObMenu *menu = NULL;
465 ObWindow *obwin = NULL;
466
467 window = event_get_window(e);
468 if ((obwin = g_hash_table_lookup(window_map, &window))) {
469 switch (obwin->type) {
470 case Window_Dock:
471 dock = WINDOW_AS_DOCK(obwin);
472 break;
473 case Window_DockApp:
474 dockapp = WINDOW_AS_DOCKAPP(obwin);
475 break;
476 case Window_Menu:
477 menu = WINDOW_AS_MENU(obwin);
478 break;
479 case Window_Client:
480 client = WINDOW_AS_CLIENT(obwin);
481 break;
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_visible)
527 if (e->type == MotionNotify || e->type == ButtonRelease ||
528 e->type == ButtonPress ||
529 e->type == KeyPress || e->type == KeyRelease) {
530 event_handle_menu(client, 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
545 /* user input (action-bound) events */
546 if (e->type == ButtonPress || e->type == ButtonRelease ||
547 e->type == MotionNotify || e->type == KeyPress ||
548 e->type == KeyRelease)
549 {
550 ObFrameContext context;
551
552 context = frame_context(client, e->xany.window);
553
554 if (!keyboard_process_interactive_grab(e, &client, &context)) {
555
556 if (e->type == ButtonPress || e->type == ButtonRelease ||
557 e->type == MotionNotify)
558 mouse_event(client, context, e);
559 else if (e->type == KeyPress)
560 keyboard_event(client, e);
561 }
562 }
563
564 /* dispatch the event to registered handlers */
565 dispatch_x(e, client);
566 }
567
568 static void event_handle_root(XEvent *e)
569 {
570 Atom msgtype;
571
572 switch(e->type) {
573 case SelectionClear:
574 ob_debug("Another WM has requested to replace us. Exiting.\n");
575 ob_exit();
576 break;
577
578 case ClientMessage:
579 if (e->xclient.format != 32) break;
580
581 msgtype = e->xclient.message_type;
582 if (msgtype == prop_atoms.net_current_desktop) {
583 unsigned int d = e->xclient.data.l[0];
584 if (d < screen_num_desktops)
585 screen_set_desktop(d);
586 } else if (msgtype == prop_atoms.net_number_of_desktops) {
587 unsigned int d = e->xclient.data.l[0];
588 if (d > 0)
589 screen_set_num_desktops(d);
590 } else if (msgtype == prop_atoms.net_showing_desktop) {
591 screen_show_desktop(e->xclient.data.l[0] != 0);
592 }
593 break;
594 case PropertyNotify:
595 if (e->xproperty.atom == prop_atoms.net_desktop_names)
596 screen_update_desktop_names();
597 else if (e->xproperty.atom == prop_atoms.net_desktop_layout)
598 screen_update_layout();
599 break;
600 case ConfigureNotify:
601 #ifdef XRANDR
602 XRRUpdateConfiguration(e);
603 #endif
604 screen_resize();
605 break;
606 default:
607 ;
608 #ifdef VIDMODE
609 if (extensions_vidmode && e->type == extensions_vidmode_event_basep) {
610 ob_debug("VIDMODE EVENT\n");
611 }
612 #endif
613 }
614 }
615
616 static void event_handle_client(ObClient *client, XEvent *e)
617 {
618 XEvent ce;
619 Atom msgtype;
620 int i=0;
621 ObFrameContext con;
622
623 switch (e->type) {
624 case VisibilityNotify:
625 client->frame->obscured = e->xvisibility.state != VisibilityUnobscured;
626 break;
627 case ButtonPress:
628 case ButtonRelease:
629 /* Wheel buttons don't draw because they are an instant click, so it
630 is a waste of resources to go drawing it. */
631 if (!(e->xbutton.button == 4 || e->xbutton.button == 5)) {
632 switch (frame_context(client, e->xbutton.window)) {
633 case OB_FRAME_CONTEXT_MAXIMIZE:
634 client->frame->max_press = (e->type == ButtonPress);
635 framerender_frame(client->frame);
636 break;
637 case OB_FRAME_CONTEXT_CLOSE:
638 client->frame->close_press = (e->type == ButtonPress);
639 framerender_frame(client->frame);
640 break;
641 case OB_FRAME_CONTEXT_ICONIFY:
642 client->frame->iconify_press = (e->type == ButtonPress);
643 framerender_frame(client->frame);
644 break;
645 case OB_FRAME_CONTEXT_ALLDESKTOPS:
646 client->frame->desk_press = (e->type == ButtonPress);
647 framerender_frame(client->frame);
648 break;
649 case OB_FRAME_CONTEXT_SHADE:
650 client->frame->shade_press = (e->type == ButtonPress);
651 framerender_frame(client->frame);
652 break;
653 default:
654 /* nothing changes with clicks for any other contexts */
655 break;
656 }
657 }
658 break;
659 case FocusIn:
660 #ifdef DEBUG_FOCUS
661 ob_debug("FocusIn on client for %lx\n", client->window);
662 #endif
663 if (client != focus_client) {
664 focus_set_client(client);
665 frame_adjust_focus(client->frame, TRUE);
666 }
667 break;
668 case FocusOut:
669 #ifdef DEBUG_FOCUS
670 ob_debug("FocusOut on client for %lx\n", client->window);
671 #endif
672 /* are we a fullscreen window or a transient of one? (checks layer)
673 if we are then we need to be iconified since we are losing focus
674 */
675 if (client->layer == OB_STACKING_LAYER_FULLSCREEN && !client->iconic &&
676 !client_search_focus_tree_full(client))
677 /* iconify fullscreen windows when they and their transients
678 aren't focused */
679 client_iconify(client, TRUE, TRUE);
680 frame_adjust_focus(client->frame, FALSE);
681 break;
682 case LeaveNotify:
683 con = frame_context(client, e->xcrossing.window);
684 switch (con) {
685 case OB_FRAME_CONTEXT_MAXIMIZE:
686 client->frame->max_hover = FALSE;
687 frame_adjust_state(client->frame);
688 break;
689 case OB_FRAME_CONTEXT_ALLDESKTOPS:
690 client->frame->desk_hover = FALSE;
691 frame_adjust_state(client->frame);
692 break;
693 case OB_FRAME_CONTEXT_SHADE:
694 client->frame->shade_hover = FALSE;
695 frame_adjust_state(client->frame);
696 break;
697 case OB_FRAME_CONTEXT_ICONIFY:
698 client->frame->iconify_hover = FALSE;
699 frame_adjust_state(client->frame);
700 break;
701 case OB_FRAME_CONTEXT_CLOSE:
702 client->frame->close_hover = FALSE;
703 frame_adjust_state(client->frame);
704 break;
705 default:
706 break;
707 }
708 break;
709 case EnterNotify:
710 con = frame_context(client, e->xcrossing.window);
711 switch (con) {
712 case OB_FRAME_CONTEXT_MAXIMIZE:
713 client->frame->max_hover = TRUE;
714 frame_adjust_state(client->frame);
715 break;
716 case OB_FRAME_CONTEXT_ALLDESKTOPS:
717 client->frame->desk_hover = TRUE;
718 frame_adjust_state(client->frame);
719 break;
720 case OB_FRAME_CONTEXT_SHADE:
721 client->frame->shade_hover = TRUE;
722 frame_adjust_state(client->frame);
723 break;
724 case OB_FRAME_CONTEXT_ICONIFY:
725 client->frame->iconify_hover = TRUE;
726 frame_adjust_state(client->frame);
727 break;
728 case OB_FRAME_CONTEXT_CLOSE:
729 client->frame->close_hover = TRUE;
730 frame_adjust_state(client->frame);
731 break;
732 case OB_FRAME_CONTEXT_FRAME:
733 if (client_normal(client)) {
734 if (ob_state() == OB_STATE_STARTING) {
735 /* move it to the top of the focus order */
736 guint desktop = client->desktop;
737 if (desktop == DESKTOP_ALL) desktop = screen_desktop;
738 focus_order[desktop] = g_list_remove(focus_order[desktop],
739 client);
740 focus_order[desktop] = g_list_prepend(focus_order[desktop],
741 client);
742 } else if (config_focus_follow) {
743 #ifdef DEBUG_FOCUS
744 ob_debug("EnterNotify on %lx, focusing window\n",
745 client->window);
746 #endif
747 client_focus(client);
748 }
749 }
750 break;
751 default:
752 break;
753 }
754 break;
755 case ConfigureRequest:
756 /* compress these */
757 while (XCheckTypedWindowEvent(ob_display, client->window,
758 ConfigureRequest, &ce)) {
759 ++i;
760 /* XXX if this causes bad things.. we can compress config req's
761 with the same mask. */
762 e->xconfigurerequest.value_mask |=
763 ce.xconfigurerequest.value_mask;
764 if (ce.xconfigurerequest.value_mask & CWX)
765 e->xconfigurerequest.x = ce.xconfigurerequest.x;
766 if (ce.xconfigurerequest.value_mask & CWY)
767 e->xconfigurerequest.y = ce.xconfigurerequest.y;
768 if (ce.xconfigurerequest.value_mask & CWWidth)
769 e->xconfigurerequest.width = ce.xconfigurerequest.width;
770 if (ce.xconfigurerequest.value_mask & CWHeight)
771 e->xconfigurerequest.height = ce.xconfigurerequest.height;
772 if (ce.xconfigurerequest.value_mask & CWBorderWidth)
773 e->xconfigurerequest.border_width =
774 ce.xconfigurerequest.border_width;
775 if (ce.xconfigurerequest.value_mask & CWStackMode)
776 e->xconfigurerequest.detail = ce.xconfigurerequest.detail;
777 }
778
779 /* if we are iconic (or shaded (fvwm does this)) ignore the event */
780 if (client->iconic || client->shaded) return;
781
782 /* resize, then move, as specified in the EWMH section 7.7 */
783 if (e->xconfigurerequest.value_mask & (CWWidth | CWHeight |
784 CWX | CWY |
785 CWBorderWidth)) {
786 int x, y, w, h;
787 ObCorner corner;
788
789 if (e->xconfigurerequest.value_mask & CWBorderWidth)
790 client->border_width = e->xconfigurerequest.border_width;
791
792 x = (e->xconfigurerequest.value_mask & CWX) ?
793 e->xconfigurerequest.x : client->area.x;
794 y = (e->xconfigurerequest.value_mask & CWY) ?
795 e->xconfigurerequest.y : client->area.y;
796 w = (e->xconfigurerequest.value_mask & CWWidth) ?
797 e->xconfigurerequest.width : client->area.width;
798 h = (e->xconfigurerequest.value_mask & CWHeight) ?
799 e->xconfigurerequest.height : client->area.height;
800
801 {
802 int newx = x;
803 int newy = y;
804 int fw = w +
805 client->frame->size.left + client->frame->size.right;
806 int fh = h +
807 client->frame->size.top + client->frame->size.bottom;
808 client_find_onscreen(client, &newx, &newy, fw, fh,
809 client_normal(client));
810 if (e->xconfigurerequest.value_mask & CWX)
811 x = newx;
812 if (e->xconfigurerequest.value_mask & CWY)
813 y = newy;
814 }
815
816 switch (client->gravity) {
817 case NorthEastGravity:
818 case EastGravity:
819 corner = OB_CORNER_TOPRIGHT;
820 break;
821 case SouthWestGravity:
822 case SouthGravity:
823 corner = OB_CORNER_BOTTOMLEFT;
824 break;
825 case SouthEastGravity:
826 corner = OB_CORNER_BOTTOMRIGHT;
827 break;
828 default: /* NorthWest, Static, etc */
829 corner = OB_CORNER_TOPLEFT;
830 }
831
832 client_configure_full(client, corner, x, y, w, h, FALSE, TRUE,
833 TRUE);
834 }
835
836 if (e->xconfigurerequest.value_mask & CWStackMode) {
837 switch (e->xconfigurerequest.detail) {
838 case Below:
839 case BottomIf:
840 stacking_lower(CLIENT_AS_WINDOW(client));
841 break;
842
843 case Above:
844 case TopIf:
845 default:
846 stacking_raise(CLIENT_AS_WINDOW(client));
847 break;
848 }
849 }
850 break;
851 case UnmapNotify:
852 if (client->ignore_unmaps) {
853 client->ignore_unmaps--;
854 break;
855 }
856 client_unmanage(client);
857 break;
858 case DestroyNotify:
859 client_unmanage(client);
860 break;
861 case ReparentNotify:
862 /* this is when the client is first taken captive in the frame */
863 if (e->xreparent.parent == client->frame->plate) break;
864
865 /*
866 This event is quite rare and is usually handled in unmapHandler.
867 However, if the window is unmapped when the reparent event occurs,
868 the window manager never sees it because an unmap event is not sent
869 to an already unmapped window.
870 */
871
872 /* we don't want the reparent event, put it back on the stack for the
873 X server to deal with after we unmanage the window */
874 XPutBackEvent(ob_display, e);
875
876 client_unmanage(client);
877 break;
878 case MapRequest:
879 ob_debug("MapRequest for 0x%lx\n", client->window);
880 if (!client->iconic) break; /* this normally doesn't happen, but if it
881 does, we don't want it! */
882 if (screen_showing_desktop)
883 screen_show_desktop(FALSE);
884 client_iconify(client, FALSE, TRUE);
885 if (!client->frame->visible)
886 /* if its not visible still, then don't mess with it */
887 break;
888 if (client->shaded)
889 client_shade(client, FALSE);
890 client_focus(client);
891 stacking_raise(CLIENT_AS_WINDOW(client));
892 break;
893 case ClientMessage:
894 /* validate cuz we query stuff off the client here */
895 if (!client_validate(client)) break;
896
897 if (e->xclient.format != 32) return;
898
899 msgtype = e->xclient.message_type;
900 if (msgtype == prop_atoms.wm_change_state) {
901 /* compress changes into a single change */
902 while (XCheckTypedWindowEvent(ob_display, e->type,
903 client->window, &ce)) {
904 /* XXX: it would be nice to compress ALL messages of a
905 type, not just messages in a row without other
906 message types between. */
907 if (ce.xclient.message_type != msgtype) {
908 XPutBackEvent(ob_display, &ce);
909 break;
910 }
911 e->xclient = ce.xclient;
912 }
913 client_set_wm_state(client, e->xclient.data.l[0]);
914 } else if (msgtype == prop_atoms.net_wm_desktop) {
915 /* compress changes into a single change */
916 while (XCheckTypedWindowEvent(ob_display, e->type,
917 client->window, &ce)) {
918 /* XXX: it would be nice to compress ALL messages of a
919 type, not just messages in a row without other
920 message types between. */
921 if (ce.xclient.message_type != msgtype) {
922 XPutBackEvent(ob_display, &ce);
923 break;
924 }
925 e->xclient = ce.xclient;
926 }
927 if ((unsigned)e->xclient.data.l[0] < screen_num_desktops ||
928 (unsigned)e->xclient.data.l[0] == DESKTOP_ALL)
929 client_set_desktop(client, (unsigned)e->xclient.data.l[0],
930 FALSE);
931 } else if (msgtype == prop_atoms.net_wm_state) {
932 /* can't compress these */
933 ob_debug("net_wm_state %s %ld %ld for 0x%lx\n",
934 (e->xclient.data.l[0] == 0 ? "Remove" :
935 e->xclient.data.l[0] == 1 ? "Add" :
936 e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
937 e->xclient.data.l[1], e->xclient.data.l[2],
938 client->window);
939 client_set_state(client, e->xclient.data.l[0],
940 e->xclient.data.l[1], e->xclient.data.l[2]);
941 } else if (msgtype == prop_atoms.net_close_window) {
942 ob_debug("net_close_window for 0x%lx\n", client->window);
943 client_close(client);
944 } else if (msgtype == prop_atoms.net_active_window) {
945 ob_debug("net_active_window for 0x%lx\n", client->window);
946 client_activate(client, FALSE);
947 } else if (msgtype == prop_atoms.net_wm_moveresize) {
948 ob_debug("net_wm_moveresize for 0x%lx\n", client->window);
949 if ((Atom)e->xclient.data.l[2] ==
950 prop_atoms.net_wm_moveresize_size_topleft ||
951 (Atom)e->xclient.data.l[2] ==
952 prop_atoms.net_wm_moveresize_size_top ||
953 (Atom)e->xclient.data.l[2] ==
954 prop_atoms.net_wm_moveresize_size_topright ||
955 (Atom)e->xclient.data.l[2] ==
956 prop_atoms.net_wm_moveresize_size_right ||
957 (Atom)e->xclient.data.l[2] ==
958 prop_atoms.net_wm_moveresize_size_right ||
959 (Atom)e->xclient.data.l[2] ==
960 prop_atoms.net_wm_moveresize_size_bottomright ||
961 (Atom)e->xclient.data.l[2] ==
962 prop_atoms.net_wm_moveresize_size_bottom ||
963 (Atom)e->xclient.data.l[2] ==
964 prop_atoms.net_wm_moveresize_size_bottomleft ||
965 (Atom)e->xclient.data.l[2] ==
966 prop_atoms.net_wm_moveresize_size_left ||
967 (Atom)e->xclient.data.l[2] ==
968 prop_atoms.net_wm_moveresize_move ||
969 (Atom)e->xclient.data.l[2] ==
970 prop_atoms.net_wm_moveresize_size_keyboard ||
971 (Atom)e->xclient.data.l[2] ==
972 prop_atoms.net_wm_moveresize_move_keyboard) {
973
974 moveresize_start(client, e->xclient.data.l[0],
975 e->xclient.data.l[1], e->xclient.data.l[3],
976 e->xclient.data.l[2]);
977 }
978 } else if (msgtype == prop_atoms.net_moveresize_window) {
979 int oldg = client->gravity;
980 int tmpg, x, y, w, h;
981
982 if (e->xclient.data.l[0] & 0xff)
983 tmpg = e->xclient.data.l[0] & 0xff;
984 else
985 tmpg = oldg;
986
987 if (e->xclient.data.l[0] & 1 << 8)
988 x = e->xclient.data.l[1];
989 else
990 x = client->area.x;
991 if (e->xclient.data.l[0] & 1 << 9)
992 y = e->xclient.data.l[2];
993 else
994 y = client->area.y;
995 if (e->xclient.data.l[0] & 1 << 10)
996 w = e->xclient.data.l[3];
997 else
998 w = client->area.width;
999 if (e->xclient.data.l[0] & 1 << 11)
1000 h = e->xclient.data.l[4];
1001 else
1002 h = client->area.height;
1003 client->gravity = tmpg;
1004
1005 {
1006 int newx = x;
1007 int newy = y;
1008 int fw = w +
1009 client->frame->size.left + client->frame->size.right;
1010 int fh = h +
1011 client->frame->size.top + client->frame->size.bottom;
1012 client_find_onscreen(client, &newx, &newy, fw, fh,
1013 client_normal(client));
1014 if (e->xclient.data.l[0] & 1 << 8)
1015 x = newx;
1016 if (e->xclient.data.l[0] & 1 << 9)
1017 y = newy;
1018 }
1019
1020 client_configure(client, OB_CORNER_TOPLEFT,
1021 x, y, w, h, FALSE, TRUE);
1022
1023 client->gravity = oldg;
1024 }
1025 break;
1026 case PropertyNotify:
1027 /* validate cuz we query stuff off the client here */
1028 if (!client_validate(client)) break;
1029
1030 /* compress changes to a single property into a single change */
1031 while (XCheckTypedWindowEvent(ob_display, e->type,
1032 client->window, &ce)) {
1033 /* XXX: it would be nice to compress ALL changes to a property,
1034 not just changes in a row without other props between. */
1035 if (ce.xproperty.atom != e->xproperty.atom) {
1036 XPutBackEvent(ob_display, &ce);
1037 break;
1038 }
1039 }
1040
1041 msgtype = e->xproperty.atom;
1042 if (msgtype == XA_WM_NORMAL_HINTS) {
1043 client_update_normal_hints(client);
1044 /* normal hints can make a window non-resizable */
1045 client_setup_decor_and_functions(client);
1046 }
1047 else if (msgtype == XA_WM_HINTS)
1048 client_update_wmhints(client);
1049 else if (msgtype == XA_WM_TRANSIENT_FOR) {
1050 client_update_transient_for(client);
1051 client_get_type(client);
1052 /* type may have changed, so update the layer */
1053 client_calc_layer(client);
1054 client_setup_decor_and_functions(client);
1055 }
1056 else if (msgtype == prop_atoms.net_wm_name ||
1057 msgtype == prop_atoms.wm_name ||
1058 msgtype == prop_atoms.net_wm_icon_name ||
1059 msgtype == prop_atoms.wm_icon_name)
1060 client_update_title(client);
1061 else if (msgtype == prop_atoms.wm_class)
1062 client_update_class(client);
1063 else if (msgtype == prop_atoms.wm_protocols) {
1064 client_update_protocols(client);
1065 client_setup_decor_and_functions(client);
1066 }
1067 else if (msgtype == prop_atoms.net_wm_strut) {
1068 client_update_strut(client);
1069 }
1070 else if (msgtype == prop_atoms.net_wm_icon ||
1071 msgtype == prop_atoms.kwm_win_icon)
1072 client_update_icons(client);
1073 default:
1074 ;
1075 #ifdef SHAPE
1076 if (extensions_shape && e->type == extensions_shape_event_basep) {
1077 client->shaped = ((XShapeEvent*)e)->shaped;
1078 frame_adjust_shape(client->frame);
1079 }
1080 #endif
1081 }
1082 }
1083
1084 static void event_handle_menu(ObClient *client, XEvent *e)
1085 {
1086 ObMenuEntry *entry;
1087 ObMenu *top;
1088 GList *it = NULL;
1089
1090 top = g_list_nth_data(menu_visible, 0);
1091
1092 ob_debug("EVENT %d\n", e->type);
1093 switch (e->type) {
1094 case KeyPress:
1095 menu_control_keyboard_nav(e->xkey.keycode);
1096 break;
1097 case ButtonPress:
1098 ob_debug("BUTTON PRESS\n");
1099
1100 break;
1101 case ButtonRelease:
1102 ob_debug("BUTTON RELEASED\n");
1103
1104 for (it = menu_visible; it; it = g_list_next(it)) {
1105 ObMenu *m = it->data;
1106 if (e->xbutton.x_root >= m->location.x - ob_rr_theme->bwidth &&
1107 e->xbutton.y_root >= m->location.y - ob_rr_theme->bwidth &&
1108 e->xbutton.x_root < m->location.x + m->size.width +
1109 ob_rr_theme->bwidth &&
1110 e->xbutton.y_root < m->location.y + m->size.height +
1111 ob_rr_theme->bwidth) {
1112 if ((entry = menu_find_entry_by_pos(it->data,
1113 e->xbutton.x_root -
1114 m->location.x,
1115 e->xbutton.y_root -
1116 m->location.y))) {
1117 m->selected(entry, e->xbutton.button,
1118 e->xbutton.x_root,
1119 e->xbutton.y_root);
1120 break;
1121 }
1122 break;
1123 }
1124 }
1125
1126 /* will call the menu_hide() for each submenu as well */
1127 if (!it)
1128 menu_control_keyboard_nav(ob_keycode(OB_KEY_ESCAPE));
1129
1130 break;
1131 case MotionNotify:
1132 ob_debug("motion\n");
1133 for (it = menu_visible; it; it = g_list_next(it)) {
1134 ObMenu *m = it->data;
1135 if ((entry = menu_find_entry_by_pos(it->data,
1136 e->xmotion.x_root -
1137 m->location.x,
1138 e->xmotion.y_root -
1139 m->location.y))) {
1140 if (m->over && m->over->data != entry)
1141 m->mouseover(m->over->data, FALSE);
1142
1143 m->mouseover(entry, TRUE);
1144 break;
1145 }
1146 }
1147
1148 break;
1149 }
1150 }
1151
1152 void event_add_fd_handler(event_fd_handler *h) {
1153 g_datalist_id_set_data(&fd_handler_list, h->fd, h);
1154 FD_SET(h->fd, &allset);
1155 max_fd = MAX(max_fd, h->fd);
1156 }
1157
1158 static void find_max_fd_foreach(GQuark n, gpointer data, gpointer max)
1159 {
1160 *((unsigned int *)max) = MAX(*((unsigned int *)max), n);
1161 }
1162
1163 static void find_max_fd()
1164 {
1165 int tmpmax = -1;
1166 g_datalist_foreach(&fd_handler_list, find_max_fd_foreach,
1167 (gpointer)&tmpmax);
1168 max_fd = MAX(x_fd, tmpmax);
1169 #ifdef USE_SM
1170 max_fd = MAX(ice_fd, max_fd);
1171 #endif
1172 }
1173
1174 void event_remove_fd(int n)
1175 {
1176 FD_CLR(n, &allset);
1177 g_datalist_id_remove_data(&fd_handler_list, (GQuark)n);
1178 find_max_fd();
1179 }
1180
1181 static void fd_event_handle_foreach(GQuark n, gpointer data, gpointer user_data)
1182 {
1183 if (FD_ISSET( (int)n, &selset)) {
1184 event_fd_handler *h = (event_fd_handler *)data;
1185 g_assert(h->fd == (int)n);
1186 h->handler(h->fd, h->data);
1187 }
1188 }
1189
1190 static void fd_event_handle()
1191 {
1192 g_datalist_foreach(&fd_handler_list, fd_event_handle_foreach, NULL);
1193 }
1194
1195 static void event_handle_dock(ObDock *s, XEvent *e)
1196 {
1197 switch (e->type) {
1198 case ButtonPress:
1199 stacking_raise(DOCK_AS_WINDOW(s));
1200 break;
1201 case EnterNotify:
1202 dock_hide(FALSE);
1203 break;
1204 case LeaveNotify:
1205 dock_hide(TRUE);
1206 break;
1207 }
1208 }
1209
1210 static void event_handle_dockapp(ObDockApp *app, XEvent *e)
1211 {
1212 switch (e->type) {
1213 case MotionNotify:
1214 dock_app_drag(app, &e->xmotion);
1215 break;
1216 case UnmapNotify:
1217 if (app->ignore_unmaps) {
1218 app->ignore_unmaps--;
1219 break;
1220 }
1221 dock_remove(app, TRUE);
1222 break;
1223 case DestroyNotify:
1224 dock_remove(app, FALSE);
1225 break;
1226 case ReparentNotify:
1227 dock_remove(app, FALSE);
1228 break;
1229 case ConfigureNotify:
1230 dock_app_configure(app, e->xconfigure.width, e->xconfigure.height);
1231 break;
1232 }
1233 }
This page took 0.082285 seconds and 3 git commands to generate.