diff --git a/event.c b/event.c index 3325e668e..2c5ef7460 100644 --- a/event.c +++ b/event.c @@ -25,6 +25,7 @@ #include "objects/tag.h" #include "objects/selection_getter.h" #include "objects/drawin.h" +#include "objects/selection_acquire.h" #include "objects/selection_watcher.h" #include "xwindow.h" #include "ewmh.h" @@ -1013,7 +1014,8 @@ event_handle_selectionclear(xcb_selection_clear_event_t *ev) { warn("Lost WM_Sn selection, exiting..."); g_main_loop_quit(globalconf.loop); - } + } else + selection_handle_selectionclear(ev); } /** \brief awesome xerror function. diff --git a/objects/selection_acquire.c b/objects/selection_acquire.c index 2bc1483d2..3d619cc50 100644 --- a/objects/selection_acquire.c +++ b/objects/selection_acquire.c @@ -28,17 +28,85 @@ typedef struct selection_acquire_t { LUA_OBJECT_HEADER + /** The selection that is being owned. */ + xcb_atom_t selection; /** Window used for owning the selection. */ xcb_window_t window; /** Timestamp used for acquiring the selection. */ xcb_timestamp_t timestamp; - /** Reference in the special table to this object. */ - int ref; } selection_acquire_t; static lua_class_t selection_acquire_class; LUA_OBJECT_FUNCS(selection_acquire_class, selection_acquire_t, selection_acquire) +static void +luaA_pushatom(lua_State *L, xcb_atom_t atom) +{ + lua_pushnumber(L, atom); +} + +static int +selection_acquire_find_by_window(lua_State *L, xcb_window_t window) +{ + /* Iterate over all active selection acquire objects */ + lua_pushliteral(L, REGISTRY_ACQUIRE_TABLE_INDEX); + lua_rawget(L, LUA_REGISTRYINDEX); + lua_pushnil(L); + while (lua_next(L, -2) != 0) { + if (lua_type(L, -1) == LUA_TUSERDATA) { + selection_acquire_t *selection = lua_touserdata(L, -1); + if (selection->window == window) + { + /* Remove table and key */ + lua_remove(L, -2); + lua_remove(L, -2); + return 1; + } + } + /* Remove the value, leaving only the key */ + lua_pop(L, 1); + } + /* Remove the table */ + lua_pop(L, 1); + + return 0; +} + +static void +selection_release(lua_State *L, int ud) +{ + selection_acquire_t *selection = luaA_checkudata(L, ud, &selection_acquire_class); + + luaA_object_emit_signal(L, ud, "release", 0); + + /* Destroy the window, this also releases the selection in X11 */ + xcb_destroy_window(globalconf.connection, selection->window); + selection->window = XCB_NONE; + + /* Unreference the object, it's now dead */ + lua_pushliteral(L, REGISTRY_ACQUIRE_TABLE_INDEX); + lua_rawget(L, LUA_REGISTRYINDEX); + luaA_pushatom(L, selection->selection); + lua_pushnil(L); + lua_rawset(L, -3); + + selection->selection = XCB_NONE; + + lua_pop(L, 1); +} + +void +selection_handle_selectionclear(xcb_selection_clear_event_t *ev) +{ + lua_State *L = globalconf_get_lua_State(); + + if (selection_acquire_find_by_window(L, ev->owner) == 0) + return; + + selection_release(L, -1); + lua_pop(L, 1); +} + static int luaA_selection_acquire_new(lua_State *L) { @@ -51,13 +119,6 @@ luaA_selection_acquire_new(lua_State *L) name = luaL_checklstring(L, 2, &name_length); - /* Create a selection object */ - selection = (void *) selection_acquire_class.allocator(L); - selection->window = xcb_generate_id(globalconf.connection); - xcb_create_window(globalconf.connection, globalconf.screen->root_depth, - selection->window, globalconf.screen->root, -1, -1, 1, 1, 0, - XCB_COPY_FROM_PARENT, globalconf.screen->root_visual, 0, NULL); - /* Get the atom identifying the selection */ reply = xcb_intern_atom_reply(globalconf.connection, xcb_intern_atom_unchecked(globalconf.connection, false, name_length, name), @@ -65,8 +126,16 @@ luaA_selection_acquire_new(lua_State *L) name_atom = reply ? reply->atom : XCB_NONE; p_delete(&reply); - /* Try to acquire the selection */ + /* Create a selection object */ + selection = (void *) selection_acquire_class.allocator(L); + selection->selection = name_atom; selection->timestamp = globalconf.timestamp; + selection->window = xcb_generate_id(globalconf.connection); + xcb_create_window(globalconf.connection, globalconf.screen->root_depth, + selection->window, globalconf.screen->root, -1, -1, 1, 1, 0, + XCB_COPY_FROM_PARENT, globalconf.screen->root_visual, 0, NULL); + + /* Try to acquire the selection */ xcb_set_selection_owner(globalconf.connection, selection->window, name_atom, selection->timestamp); selection_reply = xcb_get_selection_owner_reply(globalconf.connection, xcb_get_selection_owner(globalconf.connection, name_atom), @@ -80,12 +149,24 @@ luaA_selection_acquire_new(lua_State *L) return 0; } - /* Everything worked, register the object in table */ + /* Everything worked, register the object in the table */ lua_pushliteral(L, REGISTRY_ACQUIRE_TABLE_INDEX); lua_rawget(L, LUA_REGISTRYINDEX); - lua_pushvalue(L, -2); - selection->ref = luaL_ref(L, -2); - lua_pop(L, 1); + + luaA_pushatom(L, name_atom); + lua_rawget(L, -2); + if (!lua_isnil(L, -1)) { + /* There is already another selection_acquire object for this selection, + * release it now. X11 does not send us SelectionClear events for our + * own changes to the selection. + */ + selection_release(L, -1); + } + + luaA_pushatom(L, name_atom); + lua_pushvalue(L, -4); + lua_rawset(L, -4); + lua_pop(L, 2); return 1; } @@ -93,18 +174,8 @@ luaA_selection_acquire_new(lua_State *L) static int luaA_selection_acquire_release(lua_State *L) { - selection_acquire_t *selection = luaA_checkudata(L, 1, &selection_acquire_class); - - xcb_destroy_window(globalconf.connection, selection->window); - selection->window = XCB_NONE; - - /* Unreference the object, it's now dead */ - lua_pushliteral(L, REGISTRY_ACQUIRE_TABLE_INDEX); - lua_rawget(L, LUA_REGISTRYINDEX); - luaL_unref(L, -1, selection->ref); - lua_pop(L, 1); - - selection->ref = LUA_NOREF; + luaA_checkudata(L, 1, &selection_acquire_class); + selection_release(L, 1); return 0; } @@ -112,7 +183,7 @@ luaA_selection_acquire_release(lua_State *L) static bool selection_acquire_checker(selection_acquire_t *selection) { - return selection->window != XCB_NONE; + return selection->selection != XCB_NONE; } void diff --git a/objects/selection_acquire.h b/objects/selection_acquire.h index aed23f6ec..1f13bd3b2 100644 --- a/objects/selection_acquire.h +++ b/objects/selection_acquire.h @@ -23,8 +23,10 @@ #define AWESOME_OBJECTS_SELECTION_ACQUIRE_H #include +#include void selection_acquire_class_setup(lua_State*); +void selection_handle_selectionclear(xcb_selection_clear_event_t*); #endif