diff --git a/meson.build b/meson.build
index cd69159..79d3eef 100644
--- a/meson.build
+++ b/meson.build
@@ -15,6 +15,8 @@ project(
wayfire = dependency('wayfire')
giomm = dependency('giomm-2.4', required: false)
+wayland_protos = dependency('wayland-protocols', version: '>=1.12')
+wayland_server = dependency('wayland-server')
if get_option('enable_windecor') == true
windecor = subproject('windecor')
diff --git a/meson_options.txt b/meson_options.txt
index df68f1a..d2a9439 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -1,4 +1,3 @@
-option('enable_nk', type: 'boolean', value: false, description: 'Enable network-keyboard demo')
option('enable_windecor', type: 'boolean', value: 'false', description: 'Install windecor plugin')
option('enable_wayfire_shadows', type: 'boolean', value: 'false', description: 'Install wayfire shadows plugin')
option('enable_focus_request', type: 'boolean', value: 'false', description: 'Install wayfire focus-request plugin')
diff --git a/proto/meson.build b/proto/meson.build
index 996fc8a..a306d25 100644
--- a/proto/meson.build
+++ b/proto/meson.build
@@ -1,9 +1,6 @@
-wayland_protos = dependency('wayland-protocols')
-wayland_client = dependency('wayland-client')
+wl_protocol_dir = wayland_protos.get_variable(pkgconfig: 'pkgdatadir')
-wl_protocol_dir = wayland_protos.get_pkgconfig_variable('pkgdatadir')
-
-wayland_scanner = find_program('wayland-scanner')
+wayland_scanner = find_program('wayland-scanner', native: true)
wayland_scanner_server = generator(
wayland_scanner,
@@ -17,28 +14,21 @@ wayland_scanner_code = generator(
arguments: ['private-code', '@INPUT@', '@OUTPUT@'],
)
-wayland_scanner_client = generator(
- wayland_scanner,
- output: '@BASENAME@-client-protocol.h',
- arguments: ['client-header', '@INPUT@', '@OUTPUT@'],
-)
-
-client_protocols = [
- './virtual-keyboard-unstable-v1.xml',
- './wlr-input-inhibitor-unstable-v1.xml'
+server_protocols = [
+ [wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'],
]
-wl_protos_client_src = []
+wl_protos_src = []
wl_protos_headers = []
-foreach p : client_protocols
+foreach p : server_protocols
xml = join_paths(p)
- wl_protos_headers += wayland_scanner_client.process(xml)
- wl_protos_client_src += wayland_scanner_code.process(xml)
+ wl_protos_src += wayland_scanner_code.process(xml)
+ wl_protos_headers += wayland_scanner_server.process(xml)
endforeach
-lib_wl_protos = static_library('wl_protos', wl_protos_client_src + wl_protos_headers,
- dependencies: [wayland_client]) # for the include directory
+lib_wl_protos = static_library('wl_protos', wl_protos_src + wl_protos_headers,
+ dependencies: [wayland_server]) # for the include directory
wf_protos = declare_dependency(
link_with: lib_wl_protos,
diff --git a/proto/virtual-keyboard-unstable-v1.xml b/proto/virtual-keyboard-unstable-v1.xml
deleted file mode 100644
index 5095c91..0000000
--- a/proto/virtual-keyboard-unstable-v1.xml
+++ /dev/null
@@ -1,113 +0,0 @@
-
-
-
- Copyright © 2008-2011 Kristian Høgsberg
- Copyright © 2010-2013 Intel Corporation
- Copyright © 2012-2013 Collabora, Ltd.
- Copyright © 2018 Purism SPC
-
- Permission is hereby granted, free of charge, to any person obtaining a
- copy of this software and associated documentation files (the "Software"),
- to deal in the Software without restriction, including without limitation
- the rights to use, copy, modify, merge, publish, distribute, sublicense,
- and/or sell copies of the Software, and to permit persons to whom the
- Software is furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice (including the next
- paragraph) shall be included in all copies or substantial portions of the
- Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- DEALINGS IN THE SOFTWARE.
-
-
-
-
- The virtual keyboard provides an application with requests which emulate
- the behaviour of a physical keyboard.
-
- This interface can be used by clients on its own to provide raw input
- events, or it can accompany the input method protocol.
-
-
-
-
- Provide a file descriptor to the compositor which can be
- memory-mapped to provide a keyboard mapping description.
-
- Format carries a value from the keymap_format enumeration.
-
-
-
-
-
-
-
-
-
-
-
-
- A key was pressed or released.
- The time argument is a timestamp with millisecond granularity, with an
- undefined base. All requests regarding a single object must share the
- same clock.
-
- Keymap must be set before issuing this request.
-
- State carries a value from the key_state enumeration.
-
-
-
-
-
-
-
-
- Notifies the compositor that the modifier and/or group state has
- changed, and it should update state.
-
- The client should use wl_keyboard.modifiers event to synchronize its
- internal state with seat state.
-
- Keymap must be set before issuing this request.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- A virtual keyboard manager allows an application to provide keyboard
- input events as if they came from a physical keyboard.
-
-
-
-
-
-
-
-
- Creates a new virtual keyboard associated to a seat.
-
- If the compositor enables a keyboard to perform arbitrary actions, it
- should present an error when an untrusted client requests a new
- keyboard.
-
-
-
-
-
-
diff --git a/proto/wlr-input-inhibitor-unstable-v1.xml b/proto/wlr-input-inhibitor-unstable-v1.xml
deleted file mode 100644
index b62d1bb..0000000
--- a/proto/wlr-input-inhibitor-unstable-v1.xml
+++ /dev/null
@@ -1,67 +0,0 @@
-
-
-
- Copyright © 2018 Drew DeVault
-
- Permission to use, copy, modify, distribute, and sell this
- software and its documentation for any purpose is hereby granted
- without fee, provided that the above copyright notice appear in
- all copies and that both that copyright notice and this permission
- notice appear in supporting documentation, and that the name of
- the copyright holders not be used in advertising or publicity
- pertaining to distribution of the software without specific,
- written prior permission. The copyright holders make no
- representations about the suitability of this software for any
- purpose. It is provided "as is" without express or implied
- warranty.
-
- THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
- SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
- FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
- SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
- AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
- ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
- THIS SOFTWARE.
-
-
-
-
- Clients can use this interface to prevent input events from being sent to
- any surfaces but its own, which is useful for example in lock screen
- software. It is assumed that access to this interface will be locked down
- to whitelisted clients by the compositor.
-
-
-
-
- Activates the input inhibitor. As long as the inhibitor is active, the
- compositor will not send input events to other clients.
-
-
-
-
-
-
-
-
-
-
-
- While this resource exists, input to clients other than the owner of the
- inhibitor resource will not receive input events. The client that owns
- this resource will receive all input events normally. The compositor will
- also disable all of its own input processing (such as keyboard shortcuts)
- while the inhibitor is active.
-
- The compositor may continue to send input events to selected clients,
- such as an on-screen keyboard (via the input-method protocol).
-
-
-
-
- Destroy the inhibitor and allow other clients to receive input.
-
-
-
-
diff --git a/src/background-view.cpp b/src/background-view.cpp
index 81f8e6e..1c703f6 100644
--- a/src/background-view.cpp
+++ b/src/background-view.cpp
@@ -23,10 +23,17 @@
*/
#include
+#include
+#include
#include
+#include
+#include
#include
#include
+#include
+#include
#include
+#include
#include
#include
#include
@@ -34,34 +41,170 @@
#include
#include
#include
-#include
-
+#include
+#include
+#include
+#include
+#include
+#include
#include
-#if WF_HAS_XWAYLAND
-extern "C"
+
+class wayfire_bgview_set_pointer_interaction : public wf::pointer_interaction_t
{
- #define class class_t
- #define static
- #include
- #undef static
- #undef class
-}
-#endif
+ public:
+ void handle_pointer_enter(wf::pointf_t position) override
+ {
+ wf::get_core().set_cursor("default");
+ }
+};
+
+// A main node for the view. It allows or denies keyboard and pointer focus according to inhibit_output.
+class wayfire_background_view_root_node : public wf::scene::translation_node_t
+{
+ std::weak_ptr _view;
+ wf::option_wrapper_t inhibit_input{"background-view/inhibit_input"};
+ std::unique_ptr wlr_kb_interaction;
+
+ public:
+ wayfire_background_view_root_node(wf::view_interface_t *_view) : translation_node_t(false)
+ {
+ this->_view = _view->weak_from_this();
+ this->wlr_kb_interaction = std::make_unique(_view);
+ }
+
+ std::optional find_node_at(const wf::pointf_t& point) override
+ {
+ if (inhibit_input)
+ {
+ // Prohibit pointer/touch input, but need to set the pointer on enter => we grab the input to this
+ // node in those cases, and the pointer_interaction.enter() sets the cursor to default
+ return wf::scene::input_node_t{
+ .node = {this},
+ .local_coords = point,
+ };
+ }
+
+ // Propagate event to the actual view node
+ return floating_inner_node_t::find_node_at(point);
+ }
+
+ wf::keyboard_focus_node_t keyboard_refocus(wf::output_t *output) override
+ {
+ auto view = _view.lock();
+ if (inhibit_input || !view)
+ {
+ // Prohibit keyboard input
+ return wf::keyboard_focus_node_t{};
+ }
+
+ // Take input only if the user explicitly clicks on the view => in this case, the last focus timestamp
+ // on the seat will be equal to our focus timestamp.
+ if (output != view->get_output())
+ {
+ return wf::keyboard_focus_node_t{};
+ }
+
+ const uint64_t last_ts = wf::get_core().seat->get_last_focus_timestamp();
+ const uint64_t our_ts = keyboard_interaction().last_focus_timestamp;
+ if (our_ts == last_ts)
+ {
+ return wf::keyboard_focus_node_t{this, wf::focus_importance::REGULAR};
+ }
+
+ return wf::keyboard_focus_node_t{};
+ }
+
+ wf::pointer_interaction_t& pointer_interaction() override
+ {
+ static wayfire_bgview_set_pointer_interaction itr;
+ return itr;
+ }
+
+ wf::keyboard_interaction_t& keyboard_interaction() override
+ {
+ return *wlr_kb_interaction;
+ }
-struct parent_view
+ std::string stringify() const override
+ {
+ return "background-view node " + stringify_flags();
+ }
+};
+
+// The view type which background-views use.
+// It derives from wf::view_interface_t directly instead of deriving from wf::toplevel_view_interface_t.
+// This makes it so that the view appears similarly to a layer-shell view in the eyes of other plugins.
+class unmappable_view_t : public virtual wf::view_interface_t
{
- nonstd::observer_ptr view;
+ public:
+ virtual void bg_view_unmap() = 0;
+
+ wf::wl_listener_wrapper on_unmap;
+ std::shared_ptr root_node;
+
+ template
+ static std::shared_ptr create(
+ WlrObject *toplevel, wf::output_t *output)
+ {
+ auto new_view = wf::view_interface_t::create(toplevel);
+
+ new_view->role = wf::VIEW_ROLE_DESKTOP_ENVIRONMENT;
+ new_view->root_node = std::make_shared(new_view.get());
+
+ // Pin the view to (0, 0) on its output: backgrounds always span the whole output
+ new_view->root_node->set_offset({0, 0});
+ new_view->set_surface_root_node(new_view->root_node);
+
+ new_view->set_output(output);
+ wf::scene::add_front(output->node_for_layer(wf::scene::layer::BACKGROUND), new_view->get_root_node());
+
+ // Directly map the view, we know that the old_view comes from a pre_map signal, so it is already
+ // mapped on the wlroots side
+ new_view->map();
+ wf::view_implementation::emit_view_map_signal({new_view.get()}, true);
+ return new_view;
+ }
};
-static std::map views;
+// An adapter so that xdg-shell views can be used as background views
+class wayfire_background_view_xdg : public wf::xdg_toplevel_view_base_t, public unmappable_view_t
+{
+ public:
+ wayfire_background_view_xdg(wlr_xdg_toplevel *toplevel) : xdg_toplevel_view_base_t(toplevel, true)
+ {}
+
+ void bg_view_unmap() override
+ {
+ wf::xdg_toplevel_view_base_t::unmap();
+ }
+};
-class wayfire_background_view : public wf::per_output_plugin_instance_t,
- public wf::keyboard_interaction_t,
- public wf::pointer_interaction_t,
- public wf::touch_interaction_t
+#if WF_HAS_XWAYLAND
+// An adapter so that xwayland views can be used as background views
+class wayfire_background_view_xwl : public wf::xwayland_view_base_t, public unmappable_view_t
{
- const std::string transformer_name = "background-view";
+ wf::option_wrapper_t inhibit_input{"background-view/inhibit_input"};
+
+ public:
+ wayfire_background_view_xwl(wlr_xwayland_surface *xw) : xwayland_view_base_t(xw)
+ {
+ this->kb_focus_enabled = !inhibit_input;
+ }
+
+ void map()
+ {
+ do_map(xw->surface, true);
+ }
+
+ void bg_view_unmap() override
+ {
+ do_unmap();
+ }
+};
+#endif
+class wayfire_background_view : public wf::plugin_interface_t
+{
/* The command option should be set to a client like mpv, projectM or
* a single xscreensaver.
*/
@@ -78,125 +221,109 @@ class wayfire_background_view : public wf::per_output_plugin_instance_t,
*/
wf::option_wrapper_t app_id{"background-view/app_id"};
- /* When this option is true, keyboard, pointer and touch input will
- * be inhibited on the background layer.
- */
- wf::option_wrapper_t inhibit_input{"background-view/inhibit_input"};
+ // List of all background views assigned to an output
+ std::map> views;
- /* To grab input on the background layer */
- std::unique_ptr grab;
+ wf::wl_listener_wrapper on_new_inhibitor;
+ wf::wl_idle_call idle_cleanup_inhibitors;
public:
void init() override
{
- grab = std::make_unique(
- "background-view", output, this, this, this);
-
- inhibit_input.set_callback(inhibit_input_changed);
command.set_callback(option_changed);
file.set_callback(option_changed);
-
- output->connect(&view_mapped);
- output->connect(&view_detached);
-
- inhibit_input_changed();
+ wf::get_core().connect(&on_view_pre_map);
option_changed();
- }
- void handle_pointer_enter(wf::pointf_t position) override
- {
- wf::get_core().set_cursor("default");
+ on_new_inhibitor.set_callback([=] (auto) { remove_idle_inhibitors(); });
+ on_new_inhibitor.connect(&wf::get_core().protocols.idle_inhibit->events.new_inhibitor);
}
- wf::config::option_base_t::updated_callback_t inhibit_input_changed = [=] ()
+ void close_all_views()
{
- if ((bool)inhibit_input)
- {
- grab->grab_input(wf::scene::layer::BACKGROUND);
- } else
+ for (auto& [output, view] : views)
{
- grab->ungrab_input();
+ view->close();
+ view->on_unmap.disconnect();
+ view->bg_view_unmap();
}
- };
+
+ views.clear();
+ }
wf::config::option_base_t::updated_callback_t option_changed = [=] ()
{
- if (views[output].view)
- {
- views[output].view->close();
- }
-
+ close_all_views();
if (std::string(command).empty())
{
return;
}
- views[output].view = nullptr;
- wf::get_core().run(std::string(
- command) + add_arg_if_not_empty(file));
+ for (size_t i = 0; i < wf::get_core().output_layout->get_outputs().size(); i++)
+ {
+ wf::get_core().run(std::string(command) + add_arg_if_not_empty(file));
+ }
};
- void set_view_for_output(wayfire_toplevel_view view, wf::output_t *o)
+ void set_view_for_output(wayfire_toplevel_view view, wlr_surface *surface, wf::output_t *o)
{
- /* Move to the respective output */
- wf::move_view_to_output(view, o, false);
-
- /* A client should be used that can be resized to any size.
- * If we set it fullscreen, the screensaver would be inhibited
- * so we send a resize request that is the size of the output
- */
- view->set_geometry(o->get_relative_geometry());
+ std::shared_ptr new_view;
+ if (wlr_surface_is_xdg_surface(surface))
+ {
+ auto toplevel = wlr_xdg_surface_from_wlr_surface(surface)->toplevel;
+ wlr_xdg_toplevel_set_size(toplevel, o->get_screen_size().width, o->get_screen_size().height);
+ new_view = unmappable_view_t::create(toplevel, o);
+ new_view->on_unmap.connect(&toplevel->base->events.unmap);
+ }
- /* Set it as the background */
- view->get_wset()->remove_view(view);
- wf::scene::readd_front(o->node_for_layer(wf::scene::layer::BACKGROUND), view->get_root_node());
+#if WF_HAS_XWAYLAND
+ else if (wlr_surface_is_xwayland_surface(surface))
+ {
+ auto xw = wlr_xwayland_surface_from_wlr_surface(surface);
+ wlr_xwayland_surface_configure(xw, o->get_layout_geometry().x, o->get_layout_geometry().y,
+ o->get_layout_geometry().width, o->get_layout_geometry().height);
+ new_view = unmappable_view_t::create(xw, o);
+ new_view->on_unmap.connect(&xw->events.unmap);
+ }
+#endif
+ else
+ {
+ LOGE("Failed to set background view: the view is neither xdg-toplevel nor a xwayland surface!");
+ return;
+ }
- /* Make it show on all workspaces in Expo, Cube, etc. */
- view->role = wf::VIEW_ROLE_DESKTOP_ENVIRONMENT;
+ new_view->on_unmap.set_callback([this, o] (auto)
+ {
+ views[o]->bg_view_unmap();
+ views.erase(o);
+ });
+ views[o] = new_view;
- /* Remember to close it later */
- views[o].view = view;
+ // Remove any idle inhibitors which were already set
+ remove_idle_inhibitors();
}
- wf::signal::connection_t view_detached{[this] (wf::view_disappeared_signal*
- ev)
+ wf::signal::connection_t on_view_pre_map = [=] (wf::view_pre_map_signal *ev)
+ {
+ auto view = wf::toplevel_cast(ev->view);
+ if (!view)
{
- auto view = ev->view;
-
- if (!view)
- {
- return;
- }
-
- if (view == views[output].view)
- {
- views[output].view = nullptr;
- }
+ return;
}
- };
- wf::signal::connection_t view_mapped{[this] (wf::view_mapped_signal *ev)
+ for (auto& o : wf::get_core().output_layout->get_outputs())
{
- auto view = wf::toplevel_cast(ev->view);
-
- if (!view)
+ if (views.count(o))
{
- return;
+ continue;
}
- for (auto& o : wf::get_core().output_layout->get_outputs())
+ /* Try to match application identifier */
+ if (std::string(app_id) == view->get_app_id())
{
- if (views[o].view)
- {
- continue;
- }
-
- /* Try to match application identifier */
- if (std::string(app_id) == view->get_app_id())
- {
- set_view_for_output(view, o);
- break;
- }
+ set_view_for_output(view, ev->surface, o);
+ ev->override_implementation = true;
+ break;
}
}
};
@@ -212,14 +339,33 @@ class wayfire_background_view : public wf::per_output_plugin_instance_t,
}
}
- void fini() override
+ void remove_idle_inhibitors()
{
- grab->ungrab_input();
- if (views[output].view)
+ idle_cleanup_inhibitors.run_once([=] ()
{
- views[output].view->close();
- }
+ auto& mgr = wf::get_core().protocols.idle_inhibit;
+ wlr_idle_inhibitor_v1 *inhibitor;
+
+ wl_list_for_each(inhibitor, &mgr->inhibitors, link)
+ {
+ for (auto& [_, view] : views)
+ {
+ if (inhibitor->surface == view->get_wlr_surface())
+ {
+ // Tell core that the inhibitor was destroyed. It was not really destroyed, but core
+ // will adjust its internal state as if the inhibitor wasn't there.
+ wl_signal_emit(&inhibitor->events.destroy, inhibitor->surface);
+ break;
+ }
+ }
+ }
+ });
+ }
+
+ void fini() override
+ {
+ close_all_views();
}
};
-DECLARE_WAYFIRE_PLUGIN(wf::per_output_plugin_t);
+DECLARE_WAYFIRE_PLUGIN(wayfire_background_view);
diff --git a/src/meson.build b/src/meson.build
index 6177a49..1f74066 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -9,7 +9,7 @@ autorotate = shared_module('autorotate-iio', 'autorotate-iio.cpp',
endif
background_view = shared_module('background-view', 'background-view.cpp',
- dependencies: [wayfire],
+ dependencies: [wayfire, wf_protos],
install: true, install_dir: join_paths(get_option('libdir'), 'wayfire'))
benchmark = shared_module('bench', 'bench.cpp',
@@ -77,8 +77,3 @@ workspace_names = shared_module('workspace-names', 'workspace-names.cpp',
hinge = shared_module('hinge', 'hinge.cpp',
dependencies: [wayfire],
install: true, install_dir: join_paths(get_option('libdir'), 'wayfire'))
-
-if get_option('enable_nk')
- subdir('network-keyboard')
-endif
-
diff --git a/src/network-keyboard/client.cpp b/src/network-keyboard/client.cpp
deleted file mode 100644
index 58d0295..0000000
--- a/src/network-keyboard/client.cpp
+++ /dev/null
@@ -1,306 +0,0 @@
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include
-#include
-#include
-#include
-
-#include
-#include "shared/os-compatibility.h"
-
-#include
-
-struct WaylandDisplay
-{
- wl_display *display = nullptr;
- zwp_virtual_keyboard_manager_v1 *vk_manager = nullptr;
- wl_seat *seat = nullptr;
-};
-
-struct Modifiers
-{
- uint32_t depressed;
- uint32_t latched;
- uint32_t locked;
- uint32_t group;
-
- bool operator ==(const Modifiers& other)
- {
- return depressed == other.depressed && latched == other.latched &&
- locked == other.locked && group == other.group;
- }
-
- bool operator !=(const Modifiers& other)
- {
- return !(*this == other);
- }
-};
-
-class VirtualKeyboardDevice
-{
- zwp_virtual_keyboard_v1 *vk;
- void send_keymap()
- {
- /* The keymap string is defined in keymap.tpp, it is keymap_normal */
-#include "keymap.tpp"
-
- size_t keymap_size = strlen(keymap) + 1;
- int keymap_fd = os_create_anonymous_file(keymap_size);
- void *ptr = mmap(NULL, keymap_size, PROT_READ | PROT_WRITE, MAP_SHARED,
- keymap_fd, 0);
-
- std::strcpy((char*)ptr, keymap);
- zwp_virtual_keyboard_v1_keymap(vk, WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1,
- keymap_fd, keymap_size);
- }
-
- public:
- VirtualKeyboardDevice(WaylandDisplay *display)
- {
- vk = zwp_virtual_keyboard_manager_v1_create_virtual_keyboard(
- display->vk_manager, display->seat);
-
- this->send_keymap();
- }
-
- void send_key(uint32_t time, uint32_t key, uint32_t state) const
- {
- zwp_virtual_keyboard_v1_key(vk, time, key, state);
- }
-
- void send_modifiers(Modifiers mod)
- {
- zwp_virtual_keyboard_v1_modifiers(vk, mod.depressed,
- mod.latched, mod.locked, mod.group);
- }
-};
-
-// listeners
-static void registry_add_object(void *data, struct wl_registry *registry,
- uint32_t name, const char *interface, uint32_t version)
-{
- auto display = static_cast(data);
- if (strcmp(interface, zwp_virtual_keyboard_manager_v1_interface.name) == 0)
- {
- display->vk_manager = (zwp_virtual_keyboard_manager_v1*)
- wl_registry_bind(registry, name,
- &zwp_virtual_keyboard_manager_v1_interface, 1u);
- }
-
- if (!display->seat && (strcmp(interface, wl_seat_interface.name) == 0))
- {
- display->seat = (wl_seat*)
- wl_registry_bind(registry, name, &wl_seat_interface, 1u);
- }
-}
-
-static void registry_remove_object(void *data, struct wl_registry *registry,
- uint32_t name)
-{
- /* no-op */
-}
-
-static struct wl_registry_listener registry_listener =
-{
- ®istry_add_object,
- ®istry_remove_object
-};
-
-class NetworkKeyboardClient
-{
- WaylandDisplay disp = {nullptr};
- std::unique_ptr device;
- std::stringstream stream;
-
- /* Do not allow long press, double press or whatever */
- std::set pressed_keys;
- Modifiers last_modifiers = {0, 0, 0, 0};
- uint32_t last_timestamp = 0;
-
- void process_event(uint32_t time, uint32_t key, uint32_t state)
- {
- last_timestamp = time;
- device->send_key(time, key, state);
-
- /* Update modifiers */
- xkb_state_update_key(xkb.state, key + 8,
- state ? XKB_KEY_DOWN : XKB_KEY_UP);
-
- Modifiers mods;
- mods.depressed = xkb_state_serialize_mods(xkb.state,
- XKB_STATE_MODS_DEPRESSED);
- mods.latched = xkb_state_serialize_mods(xkb.state,
- XKB_STATE_MODS_LATCHED);
- mods.locked = xkb_state_serialize_mods(xkb.state,
- XKB_STATE_MODS_LOCKED);
- mods.group = xkb_state_serialize_layout(xkb.state,
- XKB_STATE_LAYOUT_EFFECTIVE);
-
- if (mods != last_modifiers)
- {
- last_modifiers = mods;
- device->send_modifiers(mods);
- }
- }
-
- /**
- * Read a combination of time, key and state from the stream,
- * and send it
- */
- void read_single_event()
- {
- uint32_t time, keycode, state;
- if (stream >> time >> keycode >> state)
- {
- bool current_state = pressed_keys.count(keycode) > 0;
- if (current_state != state)
- {
- std::cout << "Received " << time << " " << keycode <<
- " " << state << std::endl;
- process_event(time, keycode, state);
-
- if (state)
- {
- pressed_keys.insert(keycode);
- } else
- {
- pressed_keys.erase(keycode);
- }
- }
- }
- }
-
- struct
- {
- xkb_context *ctx;
- xkb_keymap *keymap;
- xkb_state *state;
- } xkb;
-
- void setup_xkb_state()
- {
-#include "keymap.tpp"
- xkb.ctx = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
- xkb.keymap = xkb_keymap_new_from_string(xkb.ctx, keymap,
- XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
- xkb.state = xkb_state_new(xkb.keymap);
- }
-
- public:
-
- NetworkKeyboardClient()
- {
- /* Init wayland connection & protocol */
- disp.display = wl_display_connect(NULL);
- assert(disp.display);
- auto registry = wl_display_get_registry(disp.display);
-
- wl_registry_add_listener(registry, ®istry_listener, &disp);
- wl_display_dispatch(disp.display);
- wl_display_roundtrip(disp.display);
-
- if (!disp.vk_manager)
- {
- std::cout << "Compositor does not support" <<
- " virtual-keyboard-v1 protocol!" << std::endl;
- std::exit(-1);
- }
-
- device = std::make_unique(&disp);
-
- wl_display_flush(disp.display);
- wl_display_roundtrip(disp.display);
-
- setup_xkb_state();
- }
-
- void release_all()
- {
- device->send_modifiers({0, 0, 0, 0});
- for (auto& key : pressed_keys)
- {
- device->send_key(last_timestamp, key, 0);
- }
-
- wl_display_flush(disp.display);
- }
-
- /**
- * Handle input data from the sever
- */
- void process_input(std::string input)
- {
- for (auto& c : input)
- {
- if (c == '$')
- {
- read_single_event();
- continue;
- }
-
- /* Read other characters and wait for the next event end */
- stream.clear();
- stream << c;
- }
- }
-
- /**
- * Run the wayland event loop for some time
- */
- void spin_some()
- {
- wl_display_flush(disp.display);
- wl_display_roundtrip(disp.display);
- }
-};
-
-
-using boost::asio::ip::tcp;
-
-int main(int argc, char **argv)
-{
- if (argc < 3)
- {
- std::cout << "Usage: wf-nk-client " << std::endl;
-
- return -1;
- }
-
- unsigned short port = std::atoi(argv[2]);
- std::cout << "Using server " << argv[1] << ":" << port << std::endl;
-
- boost::asio::io_context ctx;
- tcp::resolver resolver(ctx);
- tcp::endpoint endpoint{boost::asio::ip::make_address_v4(argv[1]), port};
-
- tcp::socket socket(ctx);
- socket.connect(endpoint);
-
- NetworkKeyboardClient client;
- while (true)
- {
- boost::array buf;
- boost::system::error_code error;
-
- size_t len = socket.read_some(boost::asio::buffer(buf), error);
- client.process_input(std::string{buf.data(), len});
-
- if (error == boost::asio::error::eof)
- {
- break; // Connection closed cleanly by peer.
- } else if (error)
- {
- break; // Some other error
- }
-
- client.spin_some();
- }
-
- client.release_all();
- std::cout << "Server shut down, shutting down client" << std::endl;
-}
diff --git a/src/network-keyboard/keymap.tpp b/src/network-keyboard/keymap.tpp
deleted file mode 100644
index d97c6ca..0000000
--- a/src/network-keyboard/keymap.tpp
+++ /dev/null
@@ -1,1462 +0,0 @@
-static const char keymap[] = "xkb_keymap {\
-xkb_keycodes \"(unnamed)\" {\
- minimum = 8;\
- maximum = 255;\
- = 9;\
- = 10;\
- = 11;\
- = 12;\
- = 13;\
- = 14;\
- = 15;\
- = 16;\
- = 17;\
- = 18;\
- = 19;\
- = 20;\
- = 21;\
- = 22;\
- = 23;\
- = 24;\
- = 25;\
- = 26;\
- = 27;\
- = 28;\
- = 29;\
- = 30;\
- = 31;\
- = 32;\
- = 33;\
- = 34;\
- = 35;\
- = 36;\
- = 37;\
- = 38;\
- = 39;\
- = 40;\
- = 41;\
- = 42;\
- = 43;\
- = 44;\
- = 45;\
- = 46;\
- = 47;\
- = 48;\
- = 49;\
- = 50;\
- = 51;\
- = 52;\
- = 53;\
- = 54;\
- = 55;\
- = 56;\
- = 57;\
- = 58;\
- = 59;\
- = 60;\
- = 61;\
- = 62;\
- = 63;\
- = 64;\
- = 65;\
- = 66;\
- = 67;\
- = 68;\
- = 69;\
- = 70;\
- = 71;\
- = 72;\
- = 73;\
- = 74;\
- = 75;\
- = 76;\
- = 77;\
- = 78;\
- = 79;\
- = 80;\
- = 81;\
- = 82;\
- = 83;\
- = 84;\
- = 85;\
- = 86;\
- = 87;\
- = 88;\
- = 89;\
- = 90;\
- = 91;\
- = 92;\
- = 94;\
- = 95;\
- = 96;\
- = 97;\
- = 98;\
- = 99;\
- = 100;\
- = 101;\
- = 102;\
- = 103;\
- = 104;\
- = 105;\
- = 106;\
- = 107;\
- = 108;\
- = 109;\
- = 110;\
- = 111;\
- = 112;\
- = 113;\
- = 114;\
- = 115;\
- = 116;\
- = 117;\
- = 118;\
- = 119;\
- = 120;\
- = 121;\
- = 122;\
- = 123;\
- = 124;\
- = 125;\
- = 126;\
- = 127;\
- = 128;\
- = 129;\
- = 130;\
- = 131;\
- = 132;\
- = 133;\
- = 134;\
- = 135;\
- = 136;\
- = 137;\
- = 138;\
- = 139;\
- = 140;\
- = 141;\
- = 142;\
- = 143;\
- = 144;\
- = 145;\
- = 146;\
- = 147;\
- = 148;\
- = 149;\
- = 150;\
- = 151;\
- = 152;\
- = 153;\
- = 154;\
- = 155;\
- = 156;\
- = 157;\
- = 158;\
- = 159;\
- = 160;\
- = 161;\
- = 162;\
- = 163;\
- = 164;\
- = 165;\
- = 166;\
- = 167;\
- = 168;\
- = 169;\
- = 170;\
- = 171;\
- = 172;\
- = 173;\
- = 174;\
- = 175;\
- = 176;\
- = 177;\
- = 178;\
- = 179;\
- = 180;\
- = 181;\
- = 182;\
- = 183;\
- = 184;\
- = 185;\
- = 186;\
- = 187;\
- = 188;\
- = 189;\
- = 190;\
- = 191;\
- = 192;\
-