X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=c%2Fevent.c;fp=c%2Fevent.c;h=32d8734825dd5c87af2f616490767acfee3c472e;hb=f8a47de5ec444c452093371e3db16857eb39a490;hp=0000000000000000000000000000000000000000;hpb=8ba0586bcbdc7fe9648f1063812126d71a041670;p=chaz%2Fopenbox diff --git a/c/event.c b/c/event.c new file mode 100644 index 00000000..32d87348 --- /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 +#include +#include + +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); + } +}