From e17912ec0c30cada5ca2cab749b1f5224c5a5524 Mon Sep 17 00:00:00 2001 From: Uli Schlachter Date: Thu, 7 Feb 2019 15:16:48 +0100 Subject: [PATCH 1/8] Prepare a selection getter interface This prepares a new class for getting selection contents. No run-time behaviour changes yet. Signed-off-by: Uli Schlachter --- CMakeLists.txt | 1 + luaa.c | 4 ++++ objects/selection_getter.c | 29 +++++++++++++++++++++++++++++ objects/selection_getter.h | 30 ++++++++++++++++++++++++++++++ 4 files changed, 64 insertions(+) create mode 100644 objects/selection_getter.c create mode 100644 objects/selection_getter.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 9d3d3b4da..a3bb366bb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -89,6 +89,7 @@ set(AWE_SRCS ${BUILD_DIR}/objects/key.c ${BUILD_DIR}/objects/screen.c ${BUILD_DIR}/objects/tag.c + ${BUILD_DIR}/objects/selection_getter.c ${BUILD_DIR}/objects/window.c) set(AWE_MAN_SRCS diff --git a/luaa.c b/luaa.c index bfe8eee39..7a1a44866 100644 --- a/luaa.c +++ b/luaa.c @@ -48,6 +48,7 @@ #include "objects/client.h" #include "objects/drawable.h" #include "objects/drawin.h" +#include "objects/selection_getter.h" #include "objects/screen.h" #include "objects/tag.h" #include "property.h" @@ -1032,6 +1033,9 @@ luaA_init(xdgHandle* xdg, string_array_t *searchpath) /* Export client */ client_class_setup(L); + /* Export selection getter */ + selection_getter_class_setup(L); + /* Export keys */ key_class_setup(L); diff --git a/objects/selection_getter.c b/objects/selection_getter.c new file mode 100644 index 000000000..2fc4edb30 --- /dev/null +++ b/objects/selection_getter.c @@ -0,0 +1,29 @@ +/* + * selection_getter.c - selection content getter + * + * Copyright © 2019 Uli Schlachter + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include "objects/selection_getter.h" + +void +selection_getter_class_setup(lua_State *L) +{ +} + +// vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/objects/selection_getter.h b/objects/selection_getter.h new file mode 100644 index 000000000..7d25012cd --- /dev/null +++ b/objects/selection_getter.h @@ -0,0 +1,30 @@ +/* + * selection_getter.h - selection content getter header + * + * Copyright © 2019 Uli Schlachter + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#ifndef AWESOME_OBJECTS_SELECTION_GETTER_H +#define AWESOME_OBJECTS_SELECTION_GETTER_H + +#include + +void selection_getter_class_setup(lua_State*); + +#endif +// vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 From 010c51aa83e91cbcdd402b2f68836f7961057fec Mon Sep 17 00:00:00 2001 From: Uli Schlachter Date: Thu, 7 Feb 2019 15:47:06 +0100 Subject: [PATCH 2/8] Implement constructing selection getter objects So far they do not do match. The selection and target are specified and a window is created for a transfer, but no transfer is actually started yet. Signed-off-by: Uli Schlachter --- objects/selection_getter.c | 91 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/objects/selection_getter.c b/objects/selection_getter.c index 2fc4edb30..aabf3c07e 100644 --- a/objects/selection_getter.c +++ b/objects/selection_getter.c @@ -20,10 +20,101 @@ */ #include "objects/selection_getter.h" +#include "common/luaobject.h" +#include "globalconf.h" + +#define REGISTRY_GETTER_TABLE_INDEX "awesome_selection_getters" + +typedef struct selection_getter_t +{ + LUA_OBJECT_HEADER + /** Reference in the special table to this object */ + int ref; + /** Window used for the transfer */ + xcb_window_t window; +} selection_getter_t; + +static lua_class_t selection_getter_class; +LUA_OBJECT_FUNCS(selection_getter_class, selection_getter_t, selection_getter) + +static void +selection_getter_wipe(selection_getter_t *selection) +{ + xcb_destroy_window(globalconf.connection, selection->window); +} + +static int +luaA_selection_getter_new(lua_State *L) +{ + size_t name_length, target_length; + const char *name, *target; + xcb_intern_atom_cookie_t cookies[2]; + xcb_intern_atom_reply_t *reply; + selection_getter_t *selection; + xcb_atom_t name_atom, target_atom; + + name = luaL_checklstring(L, 2, &name_length); + target = luaL_checklstring(L, 3, &target_length); + + /* Create a selection object */ + selection = (void *) selection_getter_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); + + /* Save it in the registry */ + lua_pushliteral(L, REGISTRY_GETTER_TABLE_INDEX); + lua_rawget(L, LUA_REGISTRYINDEX); + lua_pushvalue(L, -2); + selection->ref = luaL_ref(L, -2); + lua_pop(L, 1); + + /* Get the atoms identifying the request */ + cookies[0] = xcb_intern_atom_unchecked(globalconf.connection, false, name_length, name); + cookies[1] = xcb_intern_atom_unchecked(globalconf.connection, false, target_length, target); + + reply = xcb_intern_atom_reply(globalconf.connection, cookies[0], NULL); + name_atom = reply ? reply->atom : XCB_NONE; + p_delete(&reply); + + reply = xcb_intern_atom_reply(globalconf.connection, cookies[1], NULL); + target_atom = reply ? reply->atom : XCB_NONE; + p_delete(&reply); + + (void) name_atom; + (void) target_atom; + + return 1; +} void selection_getter_class_setup(lua_State *L) { + static const struct luaL_Reg selection_getter_methods[] = + { + { "__call", luaA_selection_getter_new }, + { NULL, NULL } + }; + + static const struct luaL_Reg selection_getter_metha[] = { + LUA_OBJECT_META(selection_getter) + LUA_CLASS_META + { NULL, NULL } + }; + + /* Store a table in the registry that tracks active getters. This code does + * debug.getregistry(){REGISTRY_GETTER_TABLE_INDEX] = {} + */ + lua_pushliteral(L, REGISTRY_GETTER_TABLE_INDEX); + lua_newtable(L); + lua_rawset(L, LUA_REGISTRYINDEX); + + luaA_class_setup(L, &selection_getter_class, "selection_getter", NULL, + (lua_class_allocator_t) selection_getter_new, + (lua_class_collector_t) selection_getter_wipe, NULL, + luaA_class_index_miss_property, luaA_class_newindex_miss_property, + selection_getter_methods, selection_getter_metha); } // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 From 8ad29950f5f87979b76fca49969a0ed0f631b602 Mon Sep 17 00:00:00 2001 From: Uli Schlachter Date: Thu, 7 Feb 2019 16:20:02 +0100 Subject: [PATCH 3/8] selection_getter: Implement simple data transfers This commit adds support for simple selection transfers. INCR support is still missing. The API is that a selection getter object emit the "data" signal when some data becomes available and "data_end" when all data was received. Signed-off-by: Uli Schlachter --- common/atoms.list | 1 + event.c | 2 ++ objects/selection_getter.c | 64 ++++++++++++++++++++++++++++++++++++-- objects/selection_getter.h | 2 ++ 4 files changed, 67 insertions(+), 2 deletions(-) diff --git a/common/atoms.list b/common/atoms.list index 4dd304bab..5c1205b6f 100644 --- a/common/atoms.list +++ b/common/atoms.list @@ -63,5 +63,6 @@ WM_CLIENT_LEADER XSEL_DATA WM_TAKE_FOCUS AWESOME_CLIENT_ORDER +AWESOME_SELECTION_ATOM _XKB_RULES_NAMES _MOTIF_WM_HINTS diff --git a/event.c b/event.c index ddda3a6d4..eb3c20eca 100644 --- a/event.c +++ b/event.c @@ -23,6 +23,7 @@ #include "awesome.h" #include "property.h" #include "objects/tag.h" +#include "objects/selection_getter.h" #include "objects/drawin.h" #include "xwindow.h" #include "ewmh.h" @@ -1105,6 +1106,7 @@ void event_handle(xcb_generic_event_t *event) EVENT(XCB_REPARENT_NOTIFY, event_handle_reparentnotify); EVENT(XCB_UNMAP_NOTIFY, event_handle_unmapnotify); EVENT(XCB_SELECTION_CLEAR, event_handle_selectionclear); + EVENT(XCB_SELECTION_NOTIFY, event_handle_selectionnotify); #undef EVENT } diff --git a/objects/selection_getter.c b/objects/selection_getter.c index aabf3c07e..8d4314910 100644 --- a/objects/selection_getter.c +++ b/objects/selection_getter.c @@ -21,6 +21,7 @@ #include "objects/selection_getter.h" #include "common/luaobject.h" +#include "common/atoms.h" #include "globalconf.h" #define REGISTRY_GETTER_TABLE_INDEX "awesome_selection_getters" @@ -82,12 +83,71 @@ luaA_selection_getter_new(lua_State *L) target_atom = reply ? reply->atom : XCB_NONE; p_delete(&reply); - (void) name_atom; - (void) target_atom; + xcb_convert_selection(globalconf.connection, selection->window, name_atom, + target_atom, AWESOME_SELECTION_ATOM, globalconf.timestamp); return 1; } +static void +selection_handle_selectionnotify(lua_State *L, int ud, xcb_atom_t property) +{ + selection_getter_t *selection; + + ud = luaA_absindex(L, ud); + selection = lua_touserdata(L, ud); + + if (property != XCB_NONE) + { + xcb_get_property_reply_t *property_r = xcb_get_property_reply(globalconf.connection, + xcb_get_property(globalconf.connection, true, selection->window, AWESOME_SELECTION_ATOM, + XCB_GET_PROPERTY_TYPE_ANY, 0, 0xffffffff), NULL); + + if (property_r) + { + lua_pushlstring(L, xcb_get_property_value(property_r), xcb_get_property_value_length(property_r)); + luaA_object_emit_signal(L, ud, "data", 1); + p_delete(&property_r); + } + } + + luaA_object_emit_signal(L, ud, "data_end", 0); + + /* Now unreference the selection object; it's dead */ + lua_pushliteral(L, REGISTRY_GETTER_TABLE_INDEX); + lua_rawget(L, LUA_REGISTRYINDEX); + luaL_unref(L, -1, selection->ref); + lua_pop(L, 1); + + selection->ref = LUA_NOREF; +} + +void +event_handle_selectionnotify(xcb_selection_notify_event_t *ev) +{ + lua_State *L = globalconf_get_lua_State(); + + /* Iterate over all active selection getters */ + lua_pushliteral(L, REGISTRY_GETTER_TABLE_INDEX); + lua_rawget(L, LUA_REGISTRYINDEX); + lua_pushnil(L); + while (lua_next(L, -2) != 0) { + if (lua_type(L, -1) == LUA_TUSERDATA) { + selection_getter_t *selection = lua_touserdata(L, -1); + if (ev->requestor == selection->window) { + /* Found the right selection */ + selection_handle_selectionnotify(L, -1, ev->property); + lua_pop(L, 2); + break; + } + } + /* Remove the value, leaving only the key */ + lua_pop(L, 1); + } + /* Remove the getter table */ + lua_pop(L, 1); +} + void selection_getter_class_setup(lua_State *L) { diff --git a/objects/selection_getter.h b/objects/selection_getter.h index 7d25012cd..374020996 100644 --- a/objects/selection_getter.h +++ b/objects/selection_getter.h @@ -23,8 +23,10 @@ #define AWESOME_OBJECTS_SELECTION_GETTER_H #include +#include void selection_getter_class_setup(lua_State*); +void event_handle_selectionnotify(xcb_selection_notify_event_t*); #endif // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 From 0d2c6fc5d1b3407e5f54e2c124ca12b9a4ce981d Mon Sep 17 00:00:00 2001 From: Uli Schlachter Date: Thu, 7 Feb 2019 16:37:37 +0100 Subject: [PATCH 4/8] Implement INCR transfers Incremental transfers are required to be supported. This commit adds that necessary support to awesomeWM. Signed-off-by: Uli Schlachter --- common/atoms.list | 1 + objects/selection_getter.c | 106 ++++++++++++++++++++++++++++++------- objects/selection_getter.h | 1 + property.c | 4 ++ 4 files changed, 94 insertions(+), 18 deletions(-) diff --git a/common/atoms.list b/common/atoms.list index 5c1205b6f..9cf41336e 100644 --- a/common/atoms.list +++ b/common/atoms.list @@ -64,5 +64,6 @@ XSEL_DATA WM_TAKE_FOCUS AWESOME_CLIENT_ORDER AWESOME_SELECTION_ATOM +INCR _XKB_RULES_NAMES _MOTIF_WM_HINTS diff --git a/objects/selection_getter.c b/objects/selection_getter.c index 8d4314910..5d2372306 100644 --- a/objects/selection_getter.c +++ b/objects/selection_getter.c @@ -89,6 +89,22 @@ luaA_selection_getter_new(lua_State *L) return 1; } +static void +selection_transfer_finished(lua_State *L, int ud) +{ + selection_getter_t *selection = lua_touserdata(L, ud); + + /* Unreference the selection object; it's dead */ + lua_pushliteral(L, REGISTRY_GETTER_TABLE_INDEX); + lua_rawget(L, LUA_REGISTRYINDEX); + luaL_unref(L, -1, selection->ref); + lua_pop(L, 1); + + selection->ref = LUA_NOREF; + + luaA_object_emit_signal(L, ud, "data_end", 0); +} + static void selection_handle_selectionnotify(lua_State *L, int ud, xcb_atom_t property) { @@ -99,34 +115,37 @@ selection_handle_selectionnotify(lua_State *L, int ud, xcb_atom_t property) if (property != XCB_NONE) { + xcb_change_window_attributes(globalconf.connection, selection->window, + XCB_CW_EVENT_MASK, (uint32_t[]) { XCB_EVENT_MASK_PROPERTY_CHANGE }); + xcb_get_property_reply_t *property_r = xcb_get_property_reply(globalconf.connection, xcb_get_property(globalconf.connection, true, selection->window, AWESOME_SELECTION_ATOM, XCB_GET_PROPERTY_TYPE_ANY, 0, 0xffffffff), NULL); if (property_r) { + if (property_r->type == INCR) + { + /* This is an incremental transfer. The above GetProperty had + * delete=true. This indicates to the other end that the + * transfer should start now. Right now we only get an estimate + * of the size of the data to be transferred, which we ignore. + */ + p_delete(&property_r); + return; + } lua_pushlstring(L, xcb_get_property_value(property_r), xcb_get_property_value_length(property_r)); luaA_object_emit_signal(L, ud, "data", 1); p_delete(&property_r); } } - luaA_object_emit_signal(L, ud, "data_end", 0); - - /* Now unreference the selection object; it's dead */ - lua_pushliteral(L, REGISTRY_GETTER_TABLE_INDEX); - lua_rawget(L, LUA_REGISTRYINDEX); - luaL_unref(L, -1, selection->ref); - lua_pop(L, 1); - - selection->ref = LUA_NOREF; + selection_transfer_finished(L, ud); } -void -event_handle_selectionnotify(xcb_selection_notify_event_t *ev) +static int +selection_getter_find_by_window(lua_State *L, xcb_window_t window) { - lua_State *L = globalconf_get_lua_State(); - /* Iterate over all active selection getters */ lua_pushliteral(L, REGISTRY_GETTER_TABLE_INDEX); lua_rawget(L, LUA_REGISTRYINDEX); @@ -134,11 +153,11 @@ event_handle_selectionnotify(xcb_selection_notify_event_t *ev) while (lua_next(L, -2) != 0) { if (lua_type(L, -1) == LUA_TUSERDATA) { selection_getter_t *selection = lua_touserdata(L, -1); - if (ev->requestor == selection->window) { - /* Found the right selection */ - selection_handle_selectionnotify(L, -1, ev->property); - lua_pop(L, 2); - break; + if (selection->window == window) { + /* Found the right selection, remove table and key */ + lua_remove(L, -2); + lua_remove(L, -2); + return 1; } } /* Remove the value, leaving only the key */ @@ -146,6 +165,57 @@ event_handle_selectionnotify(xcb_selection_notify_event_t *ev) } /* Remove the getter table */ lua_pop(L, 1); + + return 0; +} + +int +property_handle_awesome_selection_atom(uint8_t state, xcb_window_t window) +{ + lua_State *L = globalconf_get_lua_State(); + + if (state != XCB_PROPERTY_NEW_VALUE) + return 0; + + if (selection_getter_find_by_window(L, window) == 0) + return 0; + + selection_getter_t *selection = lua_touserdata(L, -1); + + xcb_get_property_reply_t *property_r = xcb_get_property_reply(globalconf.connection, + xcb_get_property(globalconf.connection, true, selection->window, AWESOME_SELECTION_ATOM, + XCB_GET_PROPERTY_TYPE_ANY, 0, 0xffffffff), NULL); + + if (property_r) + { + if (property_r->value_len > 0) + { + lua_pushlstring(L, xcb_get_property_value(property_r), xcb_get_property_value_length(property_r)); + luaA_object_emit_signal(L, -2, "data", 1); + } + else + { + /* Transfer finished */ + selection_transfer_finished(L, -1); + } + + p_delete(&property_r); + } + + lua_pop(L, 1); + return 0; +} + +void +event_handle_selectionnotify(xcb_selection_notify_event_t *ev) +{ + lua_State *L = globalconf_get_lua_State(); + + if (selection_getter_find_by_window(L, ev->requestor) == 0) + return; + + selection_handle_selectionnotify(L, -1, ev->property); + lua_pop(L, 1); } void diff --git a/objects/selection_getter.h b/objects/selection_getter.h index 374020996..aa676be71 100644 --- a/objects/selection_getter.h +++ b/objects/selection_getter.h @@ -27,6 +27,7 @@ void selection_getter_class_setup(lua_State*); void event_handle_selectionnotify(xcb_selection_notify_event_t*); +int property_handle_awesome_selection_atom(uint8_t, xcb_window_t); #endif // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/property.c b/property.c index 5449b1810..f84a6b529 100644 --- a/property.c +++ b/property.c @@ -25,6 +25,7 @@ #include "ewmh.h" #include "objects/client.h" #include "objects/drawin.h" +#include "objects/selection_getter.h" #include "xwindow.h" #include @@ -524,6 +525,9 @@ property_handle_propertynotify(xcb_property_notify_event_t *ev) /* background change */ HANDLE(_XROOTPMAP_ID, property_handle_xrootpmap_id) + /* selection transfers */ + HANDLE(AWESOME_SELECTION_ATOM, property_handle_awesome_selection_atom) + /* If nothing was found, return */ END; From 9b3f3e35d9cc140f511504c0ca295451b91cf1c0 Mon Sep 17 00:00:00 2001 From: Uli Schlachter Date: Thu, 7 Feb 2019 16:46:48 +0100 Subject: [PATCH 5/8] selection_getter: Add support for ATOMs The list of supported formats of the selection is queried by requesting the target TARGETS. This target is a list of ATOMs and needs special handling which is what this commit adds. Signed-off-by: Uli Schlachter --- objects/selection_getter.c | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/objects/selection_getter.c b/objects/selection_getter.c index 5d2372306..f932be951 100644 --- a/objects/selection_getter.c +++ b/objects/selection_getter.c @@ -105,6 +105,33 @@ selection_transfer_finished(lua_State *L, int ud) luaA_object_emit_signal(L, ud, "data_end", 0); } +static void +selection_push_data(lua_State *L, xcb_get_property_reply_t *property) +{ + if (property->type == XCB_ATOM_ATOM && property->format == 32) { + size_t num_atoms = xcb_get_property_value_length(property) / 4; + xcb_atom_t *atoms = xcb_get_property_value(property); + xcb_get_atom_name_cookie_t cookies[num_atoms]; + + for (size_t i = 0; i < num_atoms; i++) + cookies[i] = xcb_get_atom_name_unchecked(globalconf.connection, atoms[i]); + + lua_newtable(L); + for (size_t i = 0; i < num_atoms; i++) { + xcb_get_atom_name_reply_t *reply = xcb_get_atom_name_reply( + globalconf.connection, cookies[i], NULL); + if (reply) + { + lua_pushlstring(L, xcb_get_atom_name_name(reply), xcb_get_atom_name_name_length(reply)); + lua_rawseti(L, -2, i+1); + } + p_delete(&reply); + } + } else { + lua_pushlstring(L, xcb_get_property_value(property), xcb_get_property_value_length(property)); + } +} + static void selection_handle_selectionnotify(lua_State *L, int ud, xcb_atom_t property) { @@ -134,7 +161,7 @@ selection_handle_selectionnotify(lua_State *L, int ud, xcb_atom_t property) p_delete(&property_r); return; } - lua_pushlstring(L, xcb_get_property_value(property_r), xcb_get_property_value_length(property_r)); + selection_push_data(L, property_r); luaA_object_emit_signal(L, ud, "data", 1); p_delete(&property_r); } @@ -190,7 +217,7 @@ property_handle_awesome_selection_atom(uint8_t state, xcb_window_t window) { if (property_r->value_len > 0) { - lua_pushlstring(L, xcb_get_property_value(property_r), xcb_get_property_value_length(property_r)); + selection_push_data(L, property_r); luaA_object_emit_signal(L, -2, "data", 1); } else From cc73c39a6055291404d94fec667537379456acdd Mon Sep 17 00:00:00 2001 From: Uli Schlachter Date: Thu, 7 Feb 2019 17:59:36 +0100 Subject: [PATCH 6/8] Add a test case for the selection getter Signed-off-by: Uli Schlachter --- tests/test-selection-getter.lua | 196 ++++++++++++++++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100644 tests/test-selection-getter.lua diff --git a/tests/test-selection-getter.lua b/tests/test-selection-getter.lua new file mode 100644 index 000000000..a11296ba3 --- /dev/null +++ b/tests/test-selection-getter.lua @@ -0,0 +1,196 @@ +-- Test the selection getter API + +local runner = require("_runner") +local spawn = require("awful.spawn") +local dump_return = require("gears.debug").dump_return +local gtable = require("gears.table") +local lgi = require("lgi") +local Gio = lgi.Gio +local GdkPixbuf = lgi.GdkPixbuf + +local header = [[ +local lgi = require("lgi") +local Gdk = lgi.Gdk +local Gtk = lgi.Gtk +local GLib = lgi.GLib +local clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD) +]] + +local set_text = [[ +clipboard:set_text("This is an experiment", -1) +]] + +local set_pixbuf = [[ +local GdkPixbuf = lgi.GdkPixbuf +local buf = GdkPixbuf.Pixbuf.new(GdkPixbuf.Colorspace.RGB, true, 8, 1900, 1600) +clipboard:set_image(buf) +]] + +local common_tail = [[ +GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 60, Gtk.main_quit) +GLib.idle_add(GLib.PRIORITY_DEFAULT, function() + print("initialisation done") + io.stdout:flush() +end) +Gtk.main() +]] + +local acquire_and_clear_clipboard = header .. set_text .. [[ +GLib.idle_add(GLib.PRIORITY_DEFAULT, Gtk.main_quit) +Gtk.main() +]] + +local acquire_clipboard_text = header .. set_text .. common_tail +local acquire_clipboard_pixbuf = header .. set_pixbuf .. common_tail + +local continue = false +runner.run_steps{ + + -- Clear the clipboard to get to a known state + function() + spawn.with_line_callback({ "lua", "-e", acquire_and_clear_clipboard }, + { exit = function() continue = true end }) + return true + end, + + function() + -- Wait for the clipboard to be cleared + if not continue then + return + end + + -- Now query the state of the clipboard (should be empty) + continue = false + local s = selection_getter("CLIPBOARD", "TARGETS") + s:connect_signal("data", function(...) error("Got unexpected data: " .. dump_return{...}) end) + s:connect_signal("data_end", function() + assert(not continue) + continue = true + end) + + return true + end, + + function() + -- Wait for the above check to be done + if not continue then + return + end + + -- Now set the clipboard to some text + continue = false + spawn.with_line_callback({ "lua", "-e", acquire_clipboard_text }, + { stdout = function(line) + assert(line == "initialisation done", + "Unexpected line: " .. line) + continue = true + end }) + + return true + end, + + function() + -- Wait for the clipboard to be set + if not continue then + return + end + + -- Query whether the clipboard contains a text (UTF8_STRING) + continue = false + local s = selection_getter("CLIPBOARD", "TARGETS") + local data = nil + s:connect_signal("data", function(_, d) + assert(not data) + data = d + end) + s:connect_signal("data_end", function() + assert(gtable.hasitem(data, "UTF8_STRING")) + assert(not continue) + continue = true + end) + + return true + end, + + function() + -- Wait for the above check to be done + if not continue then + return + end + + -- Query the text in the clipboard + continue = false + local s = selection_getter("CLIPBOARD", "UTF8_STRING") + local data = nil + s:connect_signal("data", function(_, d) + assert(data == nil) + data = d + end) + s:connect_signal("data_end", function() + assert(data == "This is an experiment") + assert(not continue) + continue = true + end) + + return true + end, + + function() + -- Wait for the above check to be done + if not continue then + return + end + + -- Now set the clipboard to an image + continue = false + spawn.with_line_callback({ "lua", "-e", acquire_clipboard_pixbuf }, + { stdout = function(line) + assert(line == "initialisation done", + "Unexpected line: " .. line) + continue = true + end }) + + return true + end, + + function() + -- Wait for the above check to be done + if not continue then + return + end + + -- Query the image in the clipboard + continue = false + local s = selection_getter("CLIPBOARD", "image/bmp") + local data = {} + s:connect_signal("data", function(_, d) + table.insert(data, d) + end) + s:connect_signal("data_end", function() + local image = table.concat(data) + local stream = Gio.MemoryInputStream.new_from_data(image) + local pixbuf, err = GdkPixbuf.Pixbuf.new_from_stream(stream) + assert(not err, tostring(err)) + assert(pixbuf.width == 1900) + assert(pixbuf.height == 1600) + assert(pixbuf.colorspace == "RGB") + assert(pixbuf["has-alpha"] == false) + + assert(not continue) + continue = true + end) + + return true + end, + + function() + -- Wait for the above check to be done + if not continue then + return + end + + return true + end, +} + +-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 From 3dac3fd1d8a3188c40763a32df52699dbf35f071 Mon Sep 17 00:00:00 2001 From: Uli Schlachter Date: Thu, 7 Feb 2019 18:22:14 +0100 Subject: [PATCH 7/8] Luacheck: Allow selection_getter global Signed-off-by: Uli Schlachter --- .luacheckrc | 1 + 1 file changed, 1 insertion(+) diff --git a/.luacheckrc b/.luacheckrc index 127213173..7af03441d 100644 --- a/.luacheckrc +++ b/.luacheckrc @@ -27,6 +27,7 @@ read_globals = { "keygrabber", "mousegrabber", "root", + "selection_getter", "selection", "tag", "window", From 4c82199d1969eaf5e91eab703639f7b0eab62025 Mon Sep 17 00:00:00 2001 From: Uli Schlachter Date: Sun, 10 Feb 2019 09:49:11 +0100 Subject: [PATCH 8/8] selection_getter: Convert to use a table as argument Signed-off-by: Uli Schlachter --- objects/selection_getter.c | 10 ++++++++-- tests/test-selection-getter.lua | 8 ++++---- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/objects/selection_getter.c b/objects/selection_getter.c index f932be951..9b8f4160e 100644 --- a/objects/selection_getter.c +++ b/objects/selection_getter.c @@ -54,8 +54,14 @@ luaA_selection_getter_new(lua_State *L) selection_getter_t *selection; xcb_atom_t name_atom, target_atom; - name = luaL_checklstring(L, 2, &name_length); - target = luaL_checklstring(L, 3, &target_length); + luaA_checktable(L, 2); + lua_pushliteral(L, "selection"); + lua_gettable(L, 2); + lua_pushliteral(L, "target"); + lua_gettable(L, 2); + + name = luaL_checklstring(L, -2, &name_length); + target = luaL_checklstring(L, -1, &target_length); /* Create a selection object */ selection = (void *) selection_getter_class.allocator(L); diff --git a/tests/test-selection-getter.lua b/tests/test-selection-getter.lua index a11296ba3..a2ae4d71c 100644 --- a/tests/test-selection-getter.lua +++ b/tests/test-selection-getter.lua @@ -61,7 +61,7 @@ runner.run_steps{ -- Now query the state of the clipboard (should be empty) continue = false - local s = selection_getter("CLIPBOARD", "TARGETS") + local s = selection_getter{ selection = "CLIPBOARD", target = "TARGETS" } s:connect_signal("data", function(...) error("Got unexpected data: " .. dump_return{...}) end) s:connect_signal("data_end", function() assert(not continue) @@ -97,7 +97,7 @@ runner.run_steps{ -- Query whether the clipboard contains a text (UTF8_STRING) continue = false - local s = selection_getter("CLIPBOARD", "TARGETS") + local s = selection_getter{ selection = "CLIPBOARD", target = "TARGETS" } local data = nil s:connect_signal("data", function(_, d) assert(not data) @@ -120,7 +120,7 @@ runner.run_steps{ -- Query the text in the clipboard continue = false - local s = selection_getter("CLIPBOARD", "UTF8_STRING") + local s = selection_getter{ selection = "CLIPBOARD", target = "UTF8_STRING" } local data = nil s:connect_signal("data", function(_, d) assert(data == nil) @@ -161,7 +161,7 @@ runner.run_steps{ -- Query the image in the clipboard continue = false - local s = selection_getter("CLIPBOARD", "image/bmp") + local s = selection_getter{ selection = "CLIPBOARD", target = "image/bmp" } local data = {} s:connect_signal("data", function(_, d) table.insert(data, d)