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