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