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