X-Git-Url: https://git.brokenzipper.com/gitweb?a=blobdiff_plain;f=openbox%2Fevent.c;h=eac312df66f09d4e3eb989c90e41c6c7fa42af6e;hb=d59f0d67b0a6f3a7eed2753875691e78c08c632c;hp=ff21918be9c162d049d1a437b93e39f7eb38c791;hpb=f289adf653955bc647ac32ad7945509b2a1acea9;p=chaz%2Fopenbox diff --git a/openbox/event.c b/openbox/event.c index ff21918b..eac312df 100644 --- a/openbox/event.c +++ b/openbox/event.c @@ -32,6 +32,7 @@ #include "menuframe.h" #include "keyboard.h" #include "modkeys.h" +#include "propwin.h" #include "mouse.h" #include "mainloop.h" #include "framerender.h" @@ -43,7 +44,6 @@ #include "translate.h" #include -#include #include #include @@ -82,7 +82,7 @@ static gboolean event_handle_menu(XEvent *e); static void event_handle_dock(ObDock *s, XEvent *e); static void event_handle_dockapp(ObDockApp *app, XEvent *e); static void event_handle_client(ObClient *c, XEvent *e); -static void event_handle_group(ObGroup *g, XEvent *e); +static void event_handle_user_time_window_clients(GSList *l, XEvent *e); static void event_handle_user_input(ObClient *client, XEvent *e); static void focus_delay_dest(gpointer data); @@ -98,6 +98,13 @@ Time event_curtime = CurrentTime; static guint ignore_enter_focus = 0; static gboolean menu_can_hide; static gboolean focus_left_screen = FALSE; +/*! This variable is used for focus fallback. If we fallback to a window, we + set this to the window. And when focus goes somewhere after that, it will + be set to NULL. If between falling back to that window and something + getting focused, the window gets unmanaged, then if there are no incoming + FocusIn events, we fallback again because focus has just gotten itself lost. + */ +static ObClient *focus_tried = NULL; #ifdef USE_SM static void ice_handler(gint fd, gpointer conn) @@ -407,11 +414,11 @@ static gboolean event_ignore(XEvent *e, ObClient *client) static void event_process(const XEvent *ec, gpointer data) { Window window; - ObGroup *group = NULL; ObClient *client = NULL; ObDock *dock = NULL; ObDockApp *dockapp = NULL; ObWindow *obwin = NULL; + GSList *timewinclients = NULL; XEvent ee, *e; ObEventData *ed = data; @@ -420,8 +427,9 @@ static void event_process(const XEvent *ec, gpointer data) e = ⅇ window = event_get_window(e); - if (!(e->type == PropertyNotify && - (group = g_hash_table_lookup(group_map, &window)))) + if (e->type != PropertyNotify || + !(timewinclients = propwin_get_clients(window, + OB_PROPWIN_USER_TIME))) if ((obwin = g_hash_table_lookup(window_map, &window))) { switch (obwin->type) { case Window_Dock: @@ -497,13 +505,16 @@ static void event_process(const XEvent *ec, gpointer data) focus_left_screen = FALSE; if (!focus_left_screen) - focus_fallback(TRUE); + focus_tried = focus_fallback(TRUE); } } else if (client && client != focus_client) { focus_left_screen = FALSE; frame_adjust_focus(client->frame, TRUE); focus_set_client(client); client_calc_layer(client); + client_bring_helper_windows(client); + + focus_tried = NULL; /* focus isn't "trying" to go anywhere now */ } } else if (e->type == FocusOut) { gboolean nomove = FALSE; @@ -545,7 +556,7 @@ static void event_process(const XEvent *ec, gpointer data) ob_debug_type(OB_DEBUG_FOCUS, "Focus went to an unmanaged window 0x%x !\n", ce.xfocus.window); - focus_fallback(TRUE); + focus_tried = focus_fallback(TRUE); } } @@ -554,8 +565,8 @@ static void event_process(const XEvent *ec, gpointer data) /* focus_set_client has already been called for sure */ client_calc_layer(client); } - } else if (group) - event_handle_group(group, e); + } else if (timewinclients) + event_handle_user_time_window_clients(timewinclients, e); else if (client) event_handle_client(client, e); else if (dockapp) @@ -566,6 +577,27 @@ static void event_process(const XEvent *ec, gpointer data) event_handle_root(e); else if (e->type == MapRequest) client_manage(window); + else if (e->type == ClientMessage) { + /* This is for _NET_WM_REQUEST_FRAME_EXTENTS messages. They come for + windows that are not managed yet. */ + if (e->xclient.message_type == prop_atoms.net_request_frame_extents) { + /* Pretend to manage the client, getting information used to + determine its decorations */ + ObClient *c = client_fake_manage(e->xclient.window); + gulong vals[4]; + + /* set the frame extents on the window */ + vals[0] = c->frame->size.left; + vals[1] = c->frame->size.right; + vals[2] = c->frame->size.top; + vals[3] = c->frame->size.bottom; + PROP_SETA32(e->xclient.window, net_frame_extents, + cardinal, vals, 4); + + /* Free the pretend client */ + client_fake_unmanage(c); + } + } else if (e->type == ConfigureRequest) { /* unhandled configure requests must be used to configure the window directly */ @@ -637,7 +669,7 @@ static void event_handle_root(XEvent *e) 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, TRUE); + screen_show_desktop(e->xclient.data.l[0] != 0, NULL); } else if (msgtype == prop_atoms.openbox_control) { if (e->xclient.data.l[0] == 1) ob_reconfigure(); @@ -662,21 +694,11 @@ static void event_handle_root(XEvent *e) } } -static void event_handle_group(ObGroup *group, XEvent *e) -{ - GSList *it; - - g_assert(e->type == PropertyNotify); - - for (it = group->members; it; it = g_slist_next(it)) - event_handle_client(it->data, e); -} - void event_enter_client(ObClient *client) { g_assert(config_focus_follow); - if (client_normal(client) && client_can_focus(client)) { + if (client_enter_focusable(client) && client_can_focus(client)) { if (config_focus_delay) { ObFocusDelayData *data; @@ -699,20 +721,49 @@ void event_enter_client(ObClient *client) } } +static void event_handle_user_time_window_clients(GSList *l, XEvent *e) +{ + g_assert(e->type == PropertyNotify); + if (e->xproperty.atom == prop_atoms.net_wm_user_time) { + for (; l; l = g_slist_next(l)) + client_update_user_time(l->data); + } +} + static void event_handle_client(ObClient *client, XEvent *e) { XEvent ce; Atom msgtype; ObFrameContext con; + static gint px = -1, py = -1; + static guint pb = 0; switch (e->type) { case ButtonPress: + /* save where the press occured for the first button pressed */ + if (!pb) { + pb = e->xbutton.button; + px = e->xbutton.x; + py = e->xbutton.y; + } case ButtonRelease: /* Wheel buttons don't draw because they are an instant click, so it - is a waste of resources to go drawing it. */ - if (!(e->xbutton.button == 4 || e->xbutton.button == 5)) { - con = frame_context(client, e->xbutton.window); + is a waste of resources to go drawing it. + if the user is doing an intereactive thing, or has a menu open then + the mouse is grabbed (possibly) and if we get these events we don't + want to deal with them + */ + if (!(e->xbutton.button == 4 || e->xbutton.button == 5) && + !keyboard_interactively_grabbed() && + !menu_frame_visible) + { + /* use where the press occured */ + con = frame_context(client, e->xbutton.window, px, py); con = mouse_button_frame_context(con, e->xbutton.button); + + if (e->type == ButtonRelease && e->xbutton.button == pb) + pb = 0, px = py = -1; + switch (con) { case OB_FRAME_CONTEXT_MAXIMIZE: client->frame->max_press = (e->type == ButtonPress); @@ -740,8 +791,61 @@ static void event_handle_client(ObClient *client, XEvent *e) } } break; + case MotionNotify: + con = frame_context(client, e->xmotion.window, + e->xmotion.x, e->xmotion.y); + switch (con) { + case OB_FRAME_CONTEXT_TITLEBAR: + /* we've left the button area inside the titlebar */ + if (client->frame->max_hover || client->frame->desk_hover || + client->frame->shade_hover || client->frame->iconify_hover || + client->frame->close_hover) + { + client->frame->max_hover = FALSE; + client->frame->desk_hover = FALSE; + client->frame->shade_hover = FALSE; + client->frame->iconify_hover = FALSE; + client->frame->close_hover = FALSE; + frame_adjust_state(client->frame); + } + break; + case OB_FRAME_CONTEXT_MAXIMIZE: + if (!client->frame->max_hover) { + client->frame->max_hover = TRUE; + frame_adjust_state(client->frame); + } + break; + case OB_FRAME_CONTEXT_ALLDESKTOPS: + if (!client->frame->desk_hover) { + client->frame->desk_hover = TRUE; + frame_adjust_state(client->frame); + } + break; + case OB_FRAME_CONTEXT_SHADE: + if (!client->frame->shade_hover) { + client->frame->shade_hover = TRUE; + frame_adjust_state(client->frame); + } + break; + case OB_FRAME_CONTEXT_ICONIFY: + if (!client->frame->iconify_hover) { + client->frame->iconify_hover = TRUE; + frame_adjust_state(client->frame); + } + break; + case OB_FRAME_CONTEXT_CLOSE: + if (!client->frame->close_hover) { + client->frame->close_hover = TRUE; + frame_adjust_state(client->frame); + } + break; + default: + break; + } + break; case LeaveNotify: - con = frame_context(client, e->xcrossing.window); + con = frame_context(client, e->xcrossing.window, + e->xcrossing.x, e->xcrossing.y); switch (con) { case OB_FRAME_CONTEXT_MAXIMIZE: client->frame->max_hover = FALSE; @@ -801,7 +905,8 @@ static void event_handle_client(ObClient *client, XEvent *e) nofocus = TRUE; } - con = frame_context(client, e->xcrossing.window); + con = frame_context(client, e->xcrossing.window, + e->xcrossing.x, e->xcrossing.y); switch (con) { case OB_FRAME_CONTEXT_MAXIMIZE: client->frame->max_hover = TRUE; @@ -854,90 +959,121 @@ static void event_handle_client(ObClient *client, XEvent *e) break; } case ConfigureRequest: + { /* dont compress these unless you're going to watch for property notifies in between (these can change what the configure would do to the window). also you can't compress stacking events */ - ob_debug("ConfigureRequest desktop %d wmstate %d vis %d\n", + gint x, y, w, h; + + /* if nothing is changed, then a configurenotify is needed */ + gboolean config = TRUE; + + x = client->area.x; + y = client->area.y; + w = client->area.width; + h = client->area.height; + + ob_debug("ConfigureRequest desktop %d wmstate %d visibile %d\n", screen_desktop, client->wmstate, client->frame->visible); + if (e->xconfigurerequest.value_mask & CWBorderWidth) + if (client->border_width != e->xconfigurerequest.border_width) { + client->border_width = e->xconfigurerequest.border_width; + /* if only the border width is changing, then it's not needed*/ + config = FALSE; + } + + + if (e->xconfigurerequest.value_mask & CWStackMode) { + ObClient *sibling = NULL; + + /* get the sibling */ + if (e->xconfigurerequest.value_mask & CWSibling) { + ObWindow *win; + win = g_hash_table_lookup(window_map, + &e->xconfigurerequest.above); + if (WINDOW_IS_CLIENT(win) && WINDOW_AS_CLIENT(win) != client) + sibling = WINDOW_AS_CLIENT(win); + } + + /* activate it rather than just focus it */ + stacking_restack_request(client, sibling, + e->xconfigurerequest.detail, TRUE); + + /* if a stacking change is requested then it is needed */ + config = TRUE; + } + /* don't allow clients to move shaded windows (fvwm does this) */ - if (client->shaded) { + if (client->shaded && (e->xconfigurerequest.value_mask & CWX || + e->xconfigurerequest.value_mask & CWY)) + { e->xconfigurerequest.value_mask &= ~CWX; e->xconfigurerequest.value_mask &= ~CWY; + + /* if the client tried to move and we aren't letting it then a + synthetic event is needed */ + config = TRUE; } - /* resize, then move, as specified in the EWMH section 7.7 */ - if (e->xconfigurerequest.value_mask & (CWWidth | CWHeight | - CWX | CWY | - CWBorderWidth)) { - gint x, y, w, h; + if (e->xconfigurerequest.value_mask & CWX || + e->xconfigurerequest.value_mask & CWY || + e->xconfigurerequest.value_mask & CWWidth || + e->xconfigurerequest.value_mask & CWHeight) + { + if (e->xconfigurerequest.value_mask & CWX) + x = e->xconfigurerequest.x; + if (e->xconfigurerequest.value_mask & CWY) + y = e->xconfigurerequest.y; + if (e->xconfigurerequest.value_mask & CWWidth) + w = e->xconfigurerequest.width; + if (e->xconfigurerequest.value_mask & CWHeight) + h = e->xconfigurerequest.height; + + /* if a new position or size is requested, then a configure is + needed */ + config = TRUE; + } - if (e->xconfigurerequest.value_mask & CWBorderWidth) - client->border_width = e->xconfigurerequest.border_width; + ob_debug("ConfigureRequest x(%d) %d y(%d) %d w(%d) %d h(%d) %d\n", + e->xconfigurerequest.value_mask & CWX, x, + e->xconfigurerequest.value_mask & CWY, y, + e->xconfigurerequest.value_mask & CWWidth, w, + e->xconfigurerequest.value_mask & CWHeight, h); - 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; - - ob_debug("ConfigureRequest x %d %d y %d %d\n", - e->xconfigurerequest.value_mask & CWX, x, - e->xconfigurerequest.value_mask & CWY, y); - - /* check for broken apps moving to their root position - - XXX remove this some day...that would be nice. right now all - kde apps do this when they try activate themselves on another - desktop. eg. open amarok window on desktop 1, switch to desktop - 2, click amarok tray icon. it will move by its decoration size. - */ - if (x != client->area.x && - x == (client->frame->area.x + client->frame->size.left - - (gint)client->border_width) && - y != client->area.y && - y == (client->frame->area.y + client->frame->size.top - - (gint)client->border_width)) - { - ob_debug_type(OB_DEBUG_APP_BUGS, - "Application %s is trying to move via " - "ConfigureRequest to it's root window position " - "but it is not using StaticGravity\n", - client->title); - /* don't move it */ - x = client->area.x; - y = client->area.y; - } + /* check for broken apps moving to their root position - client_find_onscreen(client, &x, &y, w, h, FALSE); - client_configure_full(client, x, y, w, h, FALSE, TRUE, TRUE); + XXX remove this some day...that would be nice. right now all + kde apps do this when they try activate themselves on another + desktop. eg. open amarok window on desktop 1, switch to desktop + 2, click amarok tray icon. it will move by its decoration size. + */ + if (x != client->area.x && + x == (client->frame->area.x + client->frame->size.left - + (gint)client->border_width) && + y != client->area.y && + y == (client->frame->area.y + client->frame->size.top - + (gint)client->border_width)) + { + ob_debug_type(OB_DEBUG_APP_BUGS, + "Application %s is trying to move via " + "ConfigureRequest to it's root window position " + "but it is not using StaticGravity\n", + client->title); + /* don't move it */ + x = client->area.x; + y = client->area.y; } - if (e->xconfigurerequest.value_mask & CWStackMode) { - switch (e->xconfigurerequest.detail) { - case Below: - case BottomIf: - /* Apps are so rude. And this is totally disconnected from - activation/focus. Bleh. */ - /*client_lower(client);*/ - break; - - case Above: - case TopIf: - default: - /* Apps are so rude. And this is totally disconnected from - activation/focus. Bleh. */ - /*client_raise(client);*/ - break; - } + if (config) { + client_find_onscreen(client, &x, &y, w, h, FALSE); + client_configure_full(client, x, y, w, h, FALSE, TRUE); } break; + } case UnmapNotify: if (client->ignore_unmaps) { client->ignore_unmaps--; @@ -948,10 +1084,40 @@ static void event_handle_client(ObClient *client, XEvent *e) client->window, e->xunmap.event, e->xunmap.from_configure, client->ignore_unmaps); client_unmanage(client); + + /* we were trying to focus this window but it's gone */ + if (client == focus_tried) { + ob_debug_type(OB_DEBUG_FOCUS, "Tried to focus window 0x%x and it " + "is being unmanaged:\n"); + if (XCheckIfEvent(ob_display, &ce, look_for_focusin_client, NULL)){ + XPutBackEvent(ob_display, &ce); + ob_debug_type(OB_DEBUG_FOCUS, + " but another FocusIn is coming\n"); + } else { + ob_debug_type(OB_DEBUG_FOCUS, + " so falling back focus again.\n"); + focus_tried = focus_fallback(TRUE); + } + } break; case DestroyNotify: ob_debug("DestroyNotify for window 0x%x\n", client->window); client_unmanage(client); + + /* we were trying to focus this window but it's gone */ + if (client == focus_tried) { + ob_debug_type(OB_DEBUG_FOCUS, "Tried to focus window 0x%x and it " + "is being unmanaged:\n"); + if (XCheckIfEvent(ob_display, &ce, look_for_focusin_client, NULL)){ + XPutBackEvent(ob_display, &ce); + ob_debug_type(OB_DEBUG_FOCUS, + " but another FocusIn is coming\n"); + } else { + ob_debug_type(OB_DEBUG_FOCUS, + " so falling back focus again.\n"); + focus_tried = focus_fallback(TRUE); + } + } break; case ReparentNotify: /* this is when the client is first taken captive in the frame */ @@ -970,6 +1136,21 @@ static void event_handle_client(ObClient *client, XEvent *e) ob_debug("ReparentNotify for window 0x%x\n", client->window); client_unmanage(client); + + /* we were trying to focus this window but it's gone */ + if (client == focus_tried) { + ob_debug_type(OB_DEBUG_FOCUS, "Tried to focus window 0x%x and it " + "is being unmanaged:\n"); + if (XCheckIfEvent(ob_display, &ce, look_for_focusin_client, NULL)){ + XPutBackEvent(ob_display, &ce); + ob_debug_type(OB_DEBUG_FOCUS, + " but another FocusIn is coming\n"); + } else { + ob_debug_type(OB_DEBUG_FOCUS, + " so falling back focus again.\n"); + focus_tried = focus_fallback(TRUE); + } + } break; case MapRequest: ob_debug("MapRequest for 0x%lx\n", client->window); @@ -1039,9 +1220,10 @@ static void event_handle_client(ObClient *client, XEvent *e) (e->xclient.data.l[0] == 2 ? "user" : "INVALID")))); /* XXX make use of data.l[2] !? */ event_curtime = e->xclient.data.l[1]; - ob_debug_type(OB_DEBUG_APP_BUGS, - "_NET_ACTIVE_WINDOW message for window %s is " - "missing a timestamp\n", client->title); + if (event_curtime == 0) + ob_debug_type(OB_DEBUG_APP_BUGS, + "_NET_ACTIVE_WINDOW message for window %s is " + "missing a timestamp\n", client->title); client_activate(client, FALSE, (e->xclient.data.l[0] == 0 || e->xclient.data.l[0] == 2)); @@ -1111,6 +1293,50 @@ static void event_handle_client(ObClient *client, XEvent *e) client_convert_gravity(client, grav, &x, &y, w, h); client_find_onscreen(client, &x, &y, w, h, FALSE); client_configure(client, x, y, w, h, FALSE, TRUE); + } else if (msgtype == prop_atoms.net_restack_window) { + if (e->xclient.data.l[0] != 2) { + ob_debug_type(OB_DEBUG_APP_BUGS, + "_NET_RESTACK_WINDOW sent for window %s with " + "invalid source indication %ld\n", + client->title, e->xclient.data.l[0]); + } else { + ObClient *sibling = NULL; + if (e->xclient.data.l[1]) { + ObWindow *win = g_hash_table_lookup(window_map, + &e->xclient.data.l[1]); + if (WINDOW_IS_CLIENT(win) && + WINDOW_AS_CLIENT(win) != client) + { + sibling = WINDOW_AS_CLIENT(win); + } + if (sibling == NULL) + ob_debug_type(OB_DEBUG_APP_BUGS, + "_NET_RESTACK_WINDOW sent for window %s " + "with invalid sibling 0x%x\n", + client->title, e->xclient.data.l[1]); + } + if (e->xclient.data.l[2] == Below || + e->xclient.data.l[2] == BottomIf || + e->xclient.data.l[2] == Above || + e->xclient.data.l[2] == TopIf || + e->xclient.data.l[2] == Opposite) + { + /* just raise, don't activate */ + stacking_restack_request(client, sibling, + e->xclient.data.l[2], FALSE); + /* send a synthetic ConfigureNotify, cuz this is supposed + to be like a ConfigureRequest. */ + client_configure_full(client, client->area.x, + client->area.y, + client->area.width, + client->area.height, + FALSE, TRUE); + } else + ob_debug_type(OB_DEBUG_APP_BUGS, + "_NET_RESTACK_WINDOW sent for window %s " + "with invalid detail %d\n", + client->title, e->xclient.data.l[2]); + } } break; case PropertyNotify: @@ -1158,7 +1384,7 @@ static void event_handle_client(ObClient *client, XEvent *e) client_update_wmhints(client); } else if (msgtype == XA_WM_TRANSIENT_FOR) { client_update_transient_for(client); - client_get_type(client); + client_get_type_and_transientness(client); /* type may have changed, so update the layer */ client_calc_layer(client); client_setup_decor_and_functions(client); @@ -1167,10 +1393,6 @@ static void event_handle_client(ObClient *client, XEvent *e) msgtype == prop_atoms.net_wm_icon_name || msgtype == prop_atoms.wm_icon_name) { client_update_title(client); - } else if (msgtype == prop_atoms.wm_command) { - client_update_command(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); @@ -1187,14 +1409,14 @@ static void event_handle_client(ObClient *client, XEvent *e) else if (msgtype == prop_atoms.net_wm_user_time) { client_update_user_time(client); } + else if (msgtype == prop_atoms.net_wm_user_time_window) { + client_update_user_time_window(client); + } #ifdef SYNC else if (msgtype == prop_atoms.net_wm_sync_request_counter) { client_update_sync_request_counter(client); } #endif - else if (msgtype == prop_atoms.sm_client_id) { - client_update_sm_client_id(client); - } case ColormapNotify: client_update_colormap(client, e->xcolormap.colormap); break; @@ -1423,6 +1645,7 @@ static gboolean event_handle_menu(XEvent *ev) { menu_frame_select(e->frame, NULL, FALSE); } + break; case MotionNotify: if ((e = menu_entry_frame_under(ev->xmotion.x_root, ev->xmotion.y_root))) @@ -1505,7 +1728,7 @@ static gboolean focus_delay_func(gpointer data) event_curtime = d->time; if (focus_client != d->client) { if (client_focus(d->client) && config_focus_raise) - client_raise(d->client); + stacking_raise(CLIENT_AS_WINDOW(d->client)); } event_curtime = old; return FALSE; /* no repeat */