]> Dogcows Code - chaz/openbox/blobdiff - c/event.c
merge the C branch into HEAD
[chaz/openbox] / c / event.c
diff --git a/c/event.c b/c/event.c
new file mode 100644 (file)
index 0000000..32d8734
--- /dev/null
+++ b/c/event.c
@@ -0,0 +1,593 @@
+#include "openbox.h"
+#include "client.h"
+#include "xerror.h"
+#include "prop.h"
+#include "screen.h"
+#include "frame.h"
+#include "focus.h"
+#include "hooks.h"
+#include "stacking.h"
+#include "kbind.h"
+#include "mbind.h"
+
+#include <X11/Xlib.h>
+#include <X11/keysym.h>
+#include <X11/Xatom.h>
+
+static void event_process(XEvent *e);
+static void event_handle_root(XEvent *e);
+static void event_handle_client(Client *c, XEvent *e);
+
+Time event_lasttime = 0;
+
+/*! A list of all possible combinations of keyboard lock masks */
+static unsigned int mask_list[8];
+/*! The value of the mask for the NumLock modifier */
+static unsigned int NumLockMask;
+/*! The value of the mask for the ScrollLock modifier */
+static unsigned int ScrollLockMask;
+/*! The key codes for the modifier keys */
+static XModifierKeymap *modmap;
+/*! Table of the constant modifier masks */
+static const int mask_table[] = {
+    ShiftMask, LockMask, ControlMask, Mod1Mask,
+    Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask
+};
+static int mask_table_size;
+
+void event_startup()
+{
+    mask_table_size = sizeof(mask_table) / sizeof(mask_table[0]);
+     
+    /* get lock masks that are defined by the display (not constant) */
+    modmap = XGetModifierMapping(ob_display);
+    g_assert(modmap);
+    if (modmap && modmap->max_keypermod > 0) {
+       size_t cnt;
+       const size_t size = mask_table_size * modmap->max_keypermod;
+       /* get the values of the keyboard lock modifiers
+          Note: Caps lock is not retrieved the same way as Scroll and Num
+          lock since it doesn't need to be. */
+       const KeyCode num_lock = XKeysymToKeycode(ob_display, XK_Num_Lock);
+       const KeyCode scroll_lock = XKeysymToKeycode(ob_display,
+                                                    XK_Scroll_Lock);
+         
+       for (cnt = 0; cnt < size; ++cnt) {
+           if (! modmap->modifiermap[cnt]) continue;
+              
+           if (num_lock == modmap->modifiermap[cnt])
+               NumLockMask = mask_table[cnt / modmap->max_keypermod];
+           if (scroll_lock == modmap->modifiermap[cnt])
+               ScrollLockMask = mask_table[cnt / modmap->max_keypermod];
+       }
+    }
+
+    mask_list[0] = 0;
+    mask_list[1] = LockMask;
+    mask_list[2] = NumLockMask;
+    mask_list[3] = LockMask | NumLockMask;
+    mask_list[4] = ScrollLockMask;
+    mask_list[5] = ScrollLockMask | LockMask;
+    mask_list[6] = ScrollLockMask | NumLockMask;
+    mask_list[7] = ScrollLockMask | LockMask | NumLockMask;
+}
+
+void event_shutdown()
+{
+    XFreeModifiermap(modmap);
+}
+
+void event_loop()
+{
+    fd_set selset;
+    XEvent e;
+    int x_fd;
+
+    while (TRUE) {
+       /*
+         There are slightly different event retrieval semantics here for
+         local (or high bandwidth) versus remote (or low bandwidth)
+         connections to the display/Xserver.
+       */
+       if (ob_remote) {
+           if (!XPending(ob_display))
+               break;
+       } else {
+           /*
+             This XSync allows for far more compression of events, which
+             makes things like Motion events perform far far better. Since
+             it also means network traffic for every event instead of every
+             X events (where X is the number retrieved at a time), it
+             probably should not be used for setups where Openbox is
+             running on a remote/low bandwidth display/Xserver.
+           */
+           XSync(ob_display, FALSE);
+           if (!XEventsQueued(ob_display, QueuedAlready))
+               break;
+       }
+       XNextEvent(ob_display, &e);
+
+       event_process(&e);
+    }
+     
+    x_fd = ConnectionNumber(ob_display);
+    FD_ZERO(&selset);
+    FD_SET(x_fd, &selset);
+    select(x_fd + 1, &selset, NULL, NULL, NULL);
+}
+
+void event_process(XEvent *e)
+{
+    XEvent ce;
+    KeyCode *kp;
+    Window window;
+    int i, k;
+    Client *client;
+    GQuark context;
+    static guint motion_button = 0;
+
+    /* pick a window */
+    switch (e->type) {
+    case UnmapNotify:
+       window = e->xunmap.window;
+       break;
+    case DestroyNotify:
+       window = e->xdestroywindow.window;
+       break;
+    case ConfigureRequest:
+       window = e->xconfigurerequest.window;
+       break;
+    default:
+       window = e->xany.window;
+    }
+     
+    /* grab the lasttime and hack up the state */
+    switch (e->type) {
+    case ButtonPress:
+    case ButtonRelease:
+       event_lasttime = e->xbutton.time;
+       e->xbutton.state &= ~(LockMask | NumLockMask | ScrollLockMask);
+       /* kill off the Button1Mask etc, only want the modifiers */
+       e->xbutton.state &= (ControlMask | ShiftMask | Mod1Mask |
+                            Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask);
+       break;
+    case KeyPress:
+       event_lasttime = e->xkey.time;
+       e->xkey.state &= ~(LockMask | NumLockMask | ScrollLockMask);
+       /* kill off the Button1Mask etc, only want the modifiers */
+       e->xkey.state &= (ControlMask | ShiftMask | Mod1Mask |
+                         Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask);
+       /* add to the state the mask of the modifier being pressed, if it is
+          a modifier key being pressed (this is a little ugly..) */
+/* I'm commenting this out cuz i don't want "C-Control_L" being returned. */
+/*     kp = modmap->modifiermap;*/
+/*     for (i = 0; i < mask_table_size; ++i) {*/
+/*         for (k = 0; k < modmap->max_keypermod; ++k) {*/
+/*             if (*kp == e->xkey.keycode) {*/ /* found the keycode */
+                   /* add the mask for it */
+/*                 e->xkey.state |= mask_table[i];*/
+                   /* cause the first loop to break; */
+/*                 i = mask_table_size;*/
+/*                 break;*/ /* get outta here! */
+/*             }*/
+/*             ++kp;*/
+/*         }*/
+/*     }*/
+
+       break;
+    case KeyRelease:
+       event_lasttime = e->xkey.time;
+       e->xkey.state &= ~(LockMask | NumLockMask | ScrollLockMask);
+       /* kill off the Button1Mask etc, only want the modifiers */
+       e->xkey.state &= (ControlMask | ShiftMask | Mod1Mask |
+                         Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask);
+       /* remove from the state the mask of the modifier being released, if
+          it is a modifier key being released (this is a little ugly..) */
+       kp = modmap->modifiermap;
+       for (i = 0; i < mask_table_size; ++i) {
+           for (k = 0; k < modmap->max_keypermod; ++k) {
+               if (*kp == e->xkey.keycode) { /* found the keycode */
+                   /* remove the mask for it */
+                   e->xkey.state &= ~mask_table[i];
+                   /* cause the first loop to break; */
+                   i = mask_table_size;
+                   break; /* get outta here! */
+               }
+               ++kp;
+           }
+       }
+       break;
+    case MotionNotify:
+       event_lasttime = e->xmotion.time;
+       e->xmotion.state &= ~(LockMask | NumLockMask | ScrollLockMask);
+       /* kill off the Button1Mask etc, only want the modifiers */
+       e->xmotion.state &= (ControlMask | ShiftMask | Mod1Mask |
+                            Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask);
+       /* compress events */
+       while (XCheckTypedWindowEvent(ob_display, window, e->type, &ce)) {
+           e->xmotion.x_root = ce.xmotion.x_root;
+           e->xmotion.y_root = ce.xmotion.y_root;
+       }
+       break;
+    case PropertyNotify:
+       event_lasttime = e->xproperty.time;
+       break;
+    case FocusIn:
+    case FocusOut:
+       if (e->xfocus.mode == NotifyGrab)
+           /*|| e.xfocus.mode == NotifyUngrab ||*/
+              
+           /* From Metacity, from WindowMaker, ignore all funky pointer
+              root events. Its commented out cuz I don't think we need this
+              at all. If problems arise we can look into it */
+           /*e.xfocus.detail > NotifyNonlinearVirtual) */
+           return; /* skip me! */
+       if (e->type == FocusOut) {
+           /* FocusOut events just make us look for FocusIn events. They
+              are mostly ignored otherwise. */
+           XEvent fi;
+           if (XCheckTypedEvent(ob_display, FocusIn, &fi)) {
+               event_process(&fi);
+               /* dont unfocus the window we just focused! */
+               if (fi.xfocus.window == e->xfocus.window)
+                   return;
+           }
+       }
+       break;
+    case EnterNotify:
+    case LeaveNotify:
+       event_lasttime = e->xcrossing.time;
+       if (e->xcrossing.mode != NotifyNormal)
+           return; /* skip me! */
+       break;
+    }
+
+    client = g_hash_table_lookup(client_map, (gpointer)window);
+    if (client) {
+       event_handle_client(client, e);
+    } else if (window == ob_root)
+       event_handle_root(e);
+    else if (e->type == ConfigureRequest) {
+       /* unhandled configure requests must be used to configure the
+          window directly */
+       XWindowChanges xwc;
+              
+       xwc.x = e->xconfigurerequest.x;
+       xwc.y = e->xconfigurerequest.y;
+       xwc.width = e->xconfigurerequest.width;
+       xwc.height = e->xconfigurerequest.height;
+       xwc.border_width = e->xconfigurerequest.border_width;
+       xwc.sibling = e->xconfigurerequest.above;
+       xwc.stack_mode = e->xconfigurerequest.detail;
+       
+       g_message("Proxying configure event for 0x%lx\n", window);
+       
+       /* we are not to be held responsible if someone sends us an
+          invalid request! */
+       xerror_set_ignore(TRUE);
+       XConfigureWindow(ob_display, window,
+                        e->xconfigurerequest.value_mask, &xwc);
+       xerror_set_ignore(FALSE);
+    }
+
+    /* dispatch Crossing, Pointer and Key events to the hooks */
+    switch(e->type) {
+    case EnterNotify:
+       context = frame_get_context(client, window);
+       LOGICALHOOK(EnterWindow, context, client);
+       break;
+    case LeaveNotify:
+       context = frame_get_context(client, window);
+       LOGICALHOOK(LeaveWindow, context, client);
+       break;
+    case ButtonPress:
+       if (!motion_button) motion_button = e->xbutton.button;
+       context = frame_get_context(client, window);
+       mbind_fire(e->xbutton.state, e->xbutton.button, context,
+                  Pointer_Press, client, e->xbutton.x_root,
+                  e->xbutton.y_root);
+       break;
+    case ButtonRelease:
+       if (motion_button == e->xbutton.button) motion_button = 0;
+       context = frame_get_context(client, window);
+       mbind_fire(e->xbutton.state, e->xbutton.button, context,
+                  Pointer_Release, client, e->xbutton.x_root,
+                  e->xbutton.y_root);
+       break;
+    case MotionNotify:
+       context = frame_get_context(client, window);
+       mbind_fire(e->xkey.state, motion_button, context, Pointer_Motion,
+                  client, e->xmotion.x_root, e->xmotion.y_root);
+       break;
+    case KeyPress:
+       kbind_fire(e->xkey.state, e->xkey.keycode, TRUE);
+       break;
+    case KeyRelease:
+       kbind_fire(e->xkey.state, e->xkey.keycode, FALSE);
+       break;
+    }
+}
+
+static void event_handle_root(XEvent *e)
+{
+    Atom msgtype;
+     
+    switch(e->type) {
+    case MapRequest:
+       g_message("MapRequest on root");
+       client_manage(e->xmap.window);
+       break;
+    case ClientMessage:
+       if (e->xclient.format != 32) break;
+
+       msgtype = e->xclient.message_type;
+       if (msgtype == prop_atoms.net_current_desktop) {
+           unsigned int d = e->xclient.data.l[0];
+           if (d <= screen_num_desktops)
+               screen_set_desktop(d);
+       } else if (msgtype == prop_atoms.net_number_of_desktops) {
+           unsigned int d = e->xclient.data.l[0];
+           if (d > 0)
+               screen_set_num_desktops(d);
+       } else if (msgtype == prop_atoms.net_showing_desktop) {
+           screen_show_desktop(e->xclient.data.l[0] != 0);
+       }
+       break;
+    case PropertyNotify:
+       if (e->xproperty.atom == prop_atoms.net_desktop_names)
+           screen_update_desktop_names();
+       else if (e->xproperty.atom == prop_atoms.net_desktop_layout)
+           screen_update_layout();
+       break;
+    }
+}
+
+static void event_handle_client(Client *client, XEvent *e)
+{
+    XEvent ce;
+    Atom msgtype;
+     
+    switch (e->type) {
+    case FocusIn:
+       client->focused = TRUE;
+       frame_adjust_focus(client->frame);
+
+       /* focus state can affect the stacking layer */
+       client_calc_layer(client);
+
+       focus_set_client(client);
+       break;
+    case FocusOut:
+       client->focused = FALSE;
+       frame_adjust_focus(client->frame);
+
+       /* focus state can affect the stacking layer */
+       client_calc_layer(client);
+
+       if (focus_client == client)
+           focus_set_client(NULL);
+       break;
+    case ConfigureRequest:
+       g_message("ConfigureRequest for window %lx", client->window);
+       /* compress these */
+       while (XCheckTypedWindowEvent(ob_display, client->window,
+                                     ConfigureRequest, &ce)) {
+           /* XXX if this causes bad things.. we can compress config req's
+              with the same mask. */
+           e->xconfigurerequest.value_mask |=
+               ce.xconfigurerequest.value_mask;
+           if (ce.xconfigurerequest.value_mask & CWX)
+               e->xconfigurerequest.x = ce.xconfigurerequest.x;
+           if (ce.xconfigurerequest.value_mask & CWY)
+               e->xconfigurerequest.y = ce.xconfigurerequest.y;
+           if (ce.xconfigurerequest.value_mask & CWWidth)
+               e->xconfigurerequest.width = ce.xconfigurerequest.width;
+           if (ce.xconfigurerequest.value_mask & CWHeight)
+               e->xconfigurerequest.height = ce.xconfigurerequest.height;
+           if (ce.xconfigurerequest.value_mask & CWBorderWidth)
+               e->xconfigurerequest.border_width =
+                   ce.xconfigurerequest.border_width;
+           if (ce.xconfigurerequest.value_mask & CWStackMode)
+               e->xconfigurerequest.detail = ce.xconfigurerequest.detail;
+       }
+
+       /* if we are iconic (or shaded (fvwm does this)) ignore the event */
+       if (client->iconic || client->shaded) return;
+
+       if (e->xconfigurerequest.value_mask & CWBorderWidth)
+           client->border_width = e->xconfigurerequest.border_width;
+
+       /* resize, then move, as specified in the EWMH section 7.7 */
+       if (e->xconfigurerequest.value_mask & (CWWidth | CWHeight |
+                                              CWX | CWY)) {
+           int x, y, w, h;
+           Corner corner;
+              
+           x = (e->xconfigurerequest.value_mask & CWX) ?
+               e->xconfigurerequest.x : client->area.x;
+           y = (e->xconfigurerequest.value_mask & CWY) ?
+               e->xconfigurerequest.y : client->area.y;
+           w = (e->xconfigurerequest.value_mask & CWWidth) ?
+               e->xconfigurerequest.width : client->area.width;
+           h = (e->xconfigurerequest.value_mask & CWHeight) ?
+               e->xconfigurerequest.height : client->area.height;
+              
+           switch (client->gravity) {
+           case NorthEastGravity:
+           case EastGravity:
+               corner = Corner_TopRight;
+               break;
+           case SouthWestGravity:
+           case SouthGravity:
+               corner = Corner_BottomLeft;
+               break;
+           case SouthEastGravity:
+               corner = Corner_BottomRight;
+               break;
+           default:     /* NorthWest, Static, etc */
+               corner = Corner_TopLeft;
+           }
+
+           client_configure(client, corner, x, y, w, h, FALSE, FALSE);
+       }
+
+       if (e->xconfigurerequest.value_mask & CWStackMode) {
+           switch (e->xconfigurerequest.detail) {
+           case Below:
+           case BottomIf:
+               stacking_lower(client);
+               break;
+
+           case Above:
+           case TopIf:
+           default:
+               stacking_raise(client);
+               break;
+           }
+       }
+       break;
+    case UnmapNotify:
+       if (client->ignore_unmaps) {
+           client->ignore_unmaps--;
+           break;
+       }
+       g_message("UnmapNotify for %lx", client->window);
+       client_unmanage(client);
+       break;
+    case DestroyNotify:
+       g_message("DestroyNotify for %lx", client->window);
+       client_unmanage(client);
+       break;
+    case ReparentNotify:
+       /* this is when the client is first taken captive in the frame */
+       if (e->xreparent.parent == client->frame->plate) break;
+
+       /*
+         This event is quite rare and is usually handled in unmapHandler.
+         However, if the window is unmapped when the reparent event occurs,
+         the window manager never sees it because an unmap event is not sent
+         to an already unmapped window.
+       */
+
+       /* we don't want the reparent event, put it back on the stack for the
+          X server to deal with after we unmanage the window */
+       XPutBackEvent(ob_display, e);
+     
+       client_unmanage(client);
+       break;
+    case MapRequest:
+       /* we shouldn't be able to get this unless we're iconic */
+       g_assert(client->iconic);
+
+       LOGICALHOOK(RequestActivate, g_quark_try_string("client"), client);
+       break;
+    case ClientMessage:
+       /* validate cuz we query stuff off the client here */
+       if (!client_validate(client)) break;
+  
+       if (e->xclient.format != 32) return;
+
+       msgtype = e->xclient.message_type;
+       if (msgtype == prop_atoms.wm_change_state) {
+           /* compress changes into a single change */
+           while (XCheckTypedWindowEvent(ob_display, e->type,
+                                         client->window, &ce)) {
+               /* XXX: it would be nice to compress ALL messages of a
+                  type, not just messages in a row without other
+                  message types between. */
+               if (ce.xclient.message_type != msgtype) {
+                   XPutBackEvent(ob_display, &ce);
+                   break;
+               }
+               e->xclient = ce.xclient;
+           }
+           client_set_wm_state(client, e->xclient.data.l[0]);
+       } else if (msgtype == prop_atoms.net_wm_desktop) {
+           /* compress changes into a single change */
+           while (XCheckTypedWindowEvent(ob_display, e->type,
+                                         client->window, &ce)) {
+               /* XXX: it would be nice to compress ALL messages of a
+                  type, not just messages in a row without other
+                  message types between. */
+               if (ce.xclient.message_type != msgtype) {
+                   XPutBackEvent(ob_display, &ce);
+                   break;
+               }
+               e->xclient = ce.xclient;
+           }
+           client_set_desktop(client, e->xclient.data.l[0]);
+       } else if (msgtype == prop_atoms.net_wm_state) {
+           /* can't compress these */
+           g_message("net_wm_state %s %ld %ld for 0x%lx\n",
+                     (e->xclient.data.l[0] == 0 ? "Remove" :
+                      e->xclient.data.l[0] == 1 ? "Add" :
+                      e->xclient.data.l[0] == 2 ? "Toggle" : "INVALID"),
+                     e->xclient.data.l[1], e->xclient.data.l[2],
+                     client->window);
+           client_set_state(client, e->xclient.data.l[0],
+                            e->xclient.data.l[1], e->xclient.data.l[2]);
+       } else if (msgtype == prop_atoms.net_close_window) {
+           g_message("net_close_window for 0x%lx\n", client->window);
+           client_close(client);
+       } else if (msgtype == prop_atoms.net_active_window) {
+           g_message("net_active_window for 0x%lx\n", client->window);
+           if (screen_showing_desktop)
+               screen_show_desktop(FALSE);
+           if (client->iconic)
+               client_iconify(client, FALSE, TRUE);
+           else if (!client->frame->visible)
+               /* if its not visible for other reasons, then don't mess
+                  with it */
+               return;
+           LOGICALHOOK(RequestActivate, g_quark_try_string("client"), client);
+       }
+       break;
+    case PropertyNotify:
+       /* validate cuz we query stuff off the client here */
+       if (!client_validate(client)) break;
+  
+       /* compress changes to a single property into a single change */
+       while (XCheckTypedWindowEvent(ob_display, e->type,
+                                     client->window, &ce)) {
+           /* XXX: it would be nice to compress ALL changes to a property,
+              not just changes in a row without other props between. */
+           if (ce.xproperty.atom != e->xproperty.atom) {
+               XPutBackEvent(ob_display, &ce);
+               break;
+           }
+       }
+
+       msgtype = e->xproperty.atom;
+       if (msgtype == XA_WM_NORMAL_HINTS) {
+           client_update_normal_hints(client);
+           /* normal hints can make a window non-resizable */
+           client_setup_decor_and_functions(client);
+       } else if (msgtype == XA_WM_HINTS)
+           client_update_wmhints(client);
+       else if (msgtype == XA_WM_TRANSIENT_FOR) {
+           client_update_transient_for(client);
+           client_get_type(client);
+           /* type may have changed, so update the layer */
+           client_calc_layer(client);
+           client_setup_decor_and_functions(client);
+       }
+       else if (msgtype == prop_atoms.net_wm_name ||
+                msgtype == prop_atoms.wm_name)
+           client_update_title(client);
+       else if (msgtype == prop_atoms.net_wm_icon_name ||
+                msgtype == prop_atoms.wm_icon_name)
+           client_update_icon_title(client);
+       else if (msgtype == prop_atoms.wm_class)
+           client_update_class(client);
+       else if (msgtype == prop_atoms.wm_protocols) {
+           client_update_protocols(client);
+           client_setup_decor_and_functions(client);
+       }
+       else if (msgtype == prop_atoms.net_wm_strut)
+           client_update_strut(client);
+       else if (msgtype == prop_atoms.net_wm_icon)
+           client_update_icons(client);
+       else if (msgtype == prop_atoms.kwm_win_icon)
+           client_update_kwm_icon(client);
+    }
+}
This page took 0.032765 seconds and 4 git commands to generate.