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