diff --git a/src/event_loop/driver.rs b/src/event_loop/driver.rs index 62ab185f..c60a14bc 100644 --- a/src/event_loop/driver.rs +++ b/src/event_loop/driver.rs @@ -29,7 +29,9 @@ use std::time::Instant; use crate::logging::Warn; +/// Type of the sender that waits for external events type Sender = mpsc::Sender; +/// Type of the sender that waits for internal state changes type UISender = glib::Sender; /// This loop driver spawns a new thread which updates the state in a loop, diff --git a/src/main.h b/src/main.h index c2c62a77..b6ea1cbf 100644 --- a/src/main.h +++ b/src/main.h @@ -21,11 +21,12 @@ struct rsobjects { struct receiver *receiver; struct squeek_state_manager *state_manager; struct submission *submission; + struct squeek_wayland *wayland; }; void register_ui_loop_handler(struct receiver *receiver, ServerContextService *ui, DBusHandler *dbus_handler); -struct rsobjects squeek_rsobjects_new(struct zwp_input_method_v2 *im, struct zwp_virtual_keyboard_v1 *vk); +struct rsobjects squeek_init(void); void squeek_state_send_force_visible(struct squeek_state_manager *state); void squeek_state_send_force_hidden(struct squeek_state_manager *state); diff --git a/src/main.rs b/src/main.rs index 5148e55c..0e9c0538 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -/* Copyright (C) 2020 Purism SPC +/* Copyright (C) 2020,2022 Purism SPC * SPDX-License-Identifier: GPL-3.0+ */ @@ -11,12 +11,14 @@ use glib::{Continue, MainContext, PRIORITY_DEFAULT, Receiver}; mod c { use super::*; use std::os::raw::c_void; + use std::ptr; use std::rc::Rc; use std::time::Instant; use crate::event_loop::driver; use crate::imservice::IMService; use crate::imservice::c::InputMethod; + use crate::outputs::Outputs; use crate::state; use crate::submission::Submission; use crate::util::c::Wrapped; @@ -38,9 +40,40 @@ mod c { receiver: Wrapped>, state_manager: Wrapped, submission: Wrapped, + /// Not wrapped, because C needs to access this. + wayland: *mut Wayland, + } + + /// Corresponds to wayland.h::squeek_wayland. + /// Fields unused by Rust are marked as generic data types. + #[repr(C)] + pub struct Wayland { + layer_shell: *const c_void, + virtual_keyboard_manager: *const c_void, + input_method_manager: *const c_void, + outputs: Wrapped, + seat: *const c_void, + input_method: *mut InputMethod, + virtual_keyboard: ZwpVirtualKeyboardV1, + } + + impl Wayland { + fn new(outputs_manager: Outputs) -> Self { + Wayland { + layer_shell: ptr::null(), + virtual_keyboard_manager: ptr::null(), + input_method_manager: ptr::null(), + outputs: Wrapped::new(outputs_manager), + seat: ptr::null(), + input_method: ptr::null_mut(), + virtual_keyboard: ZwpVirtualKeyboardV1::null(), + } + } } extern "C" { + #[allow(improper_ctypes)] + fn init_wayland(wayland: *mut Wayland); fn server_context_service_real_show_keyboard(service: *const UIManager); fn server_context_service_real_hide_keyboard(service: *const UIManager); fn server_context_service_set_hint_purpose(service: *const UIManager, hint: u32, purpose: u32); @@ -54,19 +87,23 @@ mod c { /// and that leads to suffering. #[no_mangle] pub extern "C" - fn squeek_rsobjects_new( - im: *mut InputMethod, - vk: ZwpVirtualKeyboardV1, - ) -> RsObjects { + fn squeek_init() -> RsObjects { + // Set up channels let (sender, receiver) = MainContext::channel(PRIORITY_DEFAULT); - let now = Instant::now(); let state_manager = driver::Threaded::new(sender, state::Application::new(now)); - let imservice = if im.is_null() { + let outputs = Outputs::new(state_manager.clone()); + let mut wayland = Box::new(Wayland::new(outputs)); + let wayland_raw = &mut *wayland as *mut _; + unsafe { init_wayland(wayland_raw); } + + let vk = wayland.virtual_keyboard; + + let imservice = if wayland.input_method.is_null() { None } else { - Some(IMService::new(im, state_manager.clone())) + Some(IMService::new(wayland.input_method, state_manager.clone())) }; let submission = Submission::new(vk, imservice); @@ -74,6 +111,7 @@ mod c { submission: Wrapped::new(submission), state_manager: Wrapped::new(state_manager), receiver: Wrapped::new(receiver), + wayland: Box::into_raw(wayland), } } diff --git a/src/outputs.rs b/src/outputs.rs index 29f4e78c..78297ec9 100644 --- a/src/outputs.rs +++ b/src/outputs.rs @@ -1,6 +1,11 @@ +/* Copyright (C) 2019-2022 Purism SPC + * SPDX-License-Identifier: GPL-3.0+ + */ + /*! Managing Wayland outputs */ use std::vec::Vec; +use crate::event_loop; use ::logging; // traits @@ -12,7 +17,7 @@ pub mod c { use std::os::raw::{ c_char, c_void }; - use ::util::c::COpaquePtr; + use ::util::c::{COpaquePtr, Wrapped}; // Defined in C @@ -103,7 +108,8 @@ pub mod c { ) -> i32; } - type COutputs = ::util::c::Wrapped; + /// Wrapping Outputs is required for calling its methods from C + type COutputs = Wrapped; /// A stable reference to an output. #[derive(Clone)] @@ -125,6 +131,8 @@ pub mod c { // Defined in Rust + // Callbacks from the output listener follow + extern fn outputs_handle_geometry( outputs: COutputs, wl_output: WlOutput, @@ -221,11 +229,7 @@ pub mod c { }; } - #[no_mangle] - pub extern "C" - fn squeek_outputs_new() -> COutputs { - COutputs::new(Outputs { outputs: Vec::new() }) - } + // End callbacks #[no_mangle] pub extern "C" @@ -363,4 +367,14 @@ pub struct Output { pub struct Outputs { outputs: Vec, + sender: event_loop::driver::Threaded, +} + +impl Outputs { + pub fn new(sender: event_loop::driver::Threaded) -> Outputs { + Outputs { + outputs: Vec::new(), + sender, + } + } } diff --git a/src/server-main.c b/src/server-main.c index 1799ccfa..b2cacdc6 100644 --- a/src/server-main.c +++ b/src/server-main.c @@ -112,34 +112,33 @@ registry_handle_global (void *data, // Even when lower version would be served, it would not be supported, // causing a hard exit (void)version; - struct squeekboard *instance = data; + struct squeek_wayland *wayland = data; if (!strcmp (interface, zwlr_layer_shell_v1_interface.name)) { - instance->wayland.layer_shell = wl_registry_bind (registry, name, + wayland->layer_shell = wl_registry_bind (registry, name, &zwlr_layer_shell_v1_interface, 1); } else if (!strcmp (interface, zwp_virtual_keyboard_manager_v1_interface.name)) { - instance->wayland.virtual_keyboard_manager = wl_registry_bind(registry, name, + wayland->virtual_keyboard_manager = wl_registry_bind(registry, name, &zwp_virtual_keyboard_manager_v1_interface, 1); } else if (!strcmp (interface, zwp_input_method_manager_v2_interface.name)) { - instance->wayland.input_method_manager = wl_registry_bind(registry, name, + wayland->input_method_manager = wl_registry_bind(registry, name, &zwp_input_method_manager_v2_interface, 1); } else if (!strcmp (interface, "wl_output")) { struct wl_output *output = wl_registry_bind (registry, name, &wl_output_interface, 2); - squeek_outputs_register(instance->wayland.outputs, output); + squeek_outputs_register(wayland->outputs, output); } else if (!strcmp(interface, "wl_seat")) { - instance->wayland.seat = wl_registry_bind(registry, name, + wayland->seat = wl_registry_bind(registry, name, &wl_seat_interface, 1); } } - static void registry_handle_global_remove (void *data, struct wl_registry *registry, uint32_t name) { - // TODO + // TODO: outputs } static const struct wl_registry_listener registry_listener = { @@ -147,6 +146,54 @@ static const struct wl_registry_listener registry_listener = { registry_handle_global_remove }; + +void init_wayland(struct squeek_wayland *wayland) { + // Set up Wayland + gdk_set_allowed_backends ("wayland"); + GdkDisplay *gdk_display = gdk_display_get_default (); + struct wl_display *display = gdk_wayland_display_get_wl_display (gdk_display); + + if (display == NULL) { + g_error ("Failed to get display: %m\n"); + exit(1); + } + + struct wl_registry *registry = wl_display_get_registry (display); + wl_registry_add_listener (registry, ®istry_listener, wayland); + wl_display_roundtrip(display); // wait until the registry is actually populated + + if (!wayland->seat) { + g_error("No seat Wayland global available."); + exit(1); + } + if (!wayland->virtual_keyboard_manager) { + g_error("No virtual keyboard manager Wayland global available."); + exit(1); + } + if (!wayland->layer_shell) { + g_error("No layer shell global available."); + exit(1); + } + + if (!wayland->input_method_manager) { + g_warning("Wayland input method interface not available"); + } + + if (wayland->input_method_manager) { + wayland->input_method = zwp_input_method_manager_v2_get_input_method( + wayland->input_method_manager, + wayland->seat); + } + if (wayland->virtual_keyboard_manager) { + wayland->virtual_keyboard = zwp_virtual_keyboard_manager_v1_create_virtual_keyboard( + wayland->virtual_keyboard_manager, + wayland->seat); + } + + // initialize global + squeek_wayland = wayland; +} + #define SESSION_NAME "sm.puri.OSK0" GDBusProxy *_proxy = NULL; @@ -284,22 +331,6 @@ phosh_theme_init (void) g_object_set (G_OBJECT (gtk_settings), "gtk-application-prefer-dark-theme", TRUE, NULL); } -/// Create Rust objects in one go, -/// to avoid crossing the language barrier and losing type information -static struct rsobjects create_rsobjects(struct zwp_input_method_manager_v2 *immanager, - struct zwp_virtual_keyboard_manager_v1 *vkmanager, - struct wl_seat *seat) { - struct zwp_input_method_v2 *im = NULL; - if (immanager) { - im = zwp_input_method_manager_v2_get_input_method(immanager, seat); - } - struct zwp_virtual_keyboard_v1 *vk = NULL; - if (vkmanager) { - vk = zwp_virtual_keyboard_manager_v1_create_virtual_keyboard(vkmanager, seat); - } - return squeek_rsobjects_new(im, vk); -} - static GDebugKey debug_keys[] = { { .key = "force-show", @@ -359,44 +390,10 @@ main (int argc, char **argv) phosh_theme_init (); - // Set up Wayland - gdk_set_allowed_backends ("wayland"); - GdkDisplay *gdk_display = gdk_display_get_default (); - struct wl_display *display = gdk_wayland_display_get_wl_display (gdk_display); - - if (display == NULL) { - g_error ("Failed to get display: %m\n"); - exit(1); - } - - struct squeekboard instance = {0}; - squeek_wayland_init (&instance.wayland); - struct wl_registry *registry = wl_display_get_registry (display); - wl_registry_add_listener (registry, ®istry_listener, &instance); - wl_display_roundtrip(display); // wait until the registry is actually populated - squeek_wayland_set_global(&instance.wayland); - if (!instance.wayland.seat) { - g_error("No seat Wayland global available."); - exit(1); - } - if (!instance.wayland.virtual_keyboard_manager) { - g_error("No virtual keyboard manager Wayland global available."); - exit(1); - } - if (!instance.wayland.layer_shell) { - g_error("No layer shell global available."); - exit(1); - } - - if (!instance.wayland.input_method_manager) { - g_warning("Wayland input method interface not available"); - } - - struct rsobjects rsobjects = create_rsobjects(instance.wayland.input_method_manager, - instance.wayland.virtual_keyboard_manager, - instance.wayland.seat); + // Also initializes wayland + struct rsobjects rsobjects = squeek_init(); instance.ui_manager = squeek_uiman_new(); @@ -477,6 +474,5 @@ main (int argc, char **argv) } g_main_loop_unref (loop); - squeek_wayland_deinit (&instance.wayland); return 0; } diff --git a/src/state.rs b/src/state.rs index 24b89d94..f3db005d 100644 --- a/src/state.rs +++ b/src/state.rs @@ -29,12 +29,14 @@ pub enum InputMethod { InactiveSince(Instant), } -/// Incoming events +/// Incoming events. +/// This contains events that cause a change to the internal state. #[derive(Clone)] pub enum Event { InputMethod(InputMethod), Visibility(visibility::Event), PhysicalKeyboard(Presence), + /// Event triggered because a moment in time passed. /// Use to animate state transitions. /// The value is the ideal arrival time. diff --git a/src/vkeyboard.rs b/src/vkeyboard.rs index aff822a7..4e8fb1a4 100644 --- a/src/vkeyboard.rs +++ b/src/vkeyboard.rs @@ -10,11 +10,18 @@ type KeyCode = u32; pub mod c { use std::ffi::CStr; use std::os::raw::{ c_char, c_void }; + use std::ptr; #[repr(transparent)] #[derive(Clone, Copy)] pub struct ZwpVirtualKeyboardV1(*const c_void); + impl ZwpVirtualKeyboardV1 { + pub fn null() -> Self { + Self(ptr::null()) + } + } + #[repr(C)] pub struct KeyMap { fd: u32, diff --git a/src/wayland.c b/src/wayland.c index 3c9fce93..6e569def 100644 --- a/src/wayland.c +++ b/src/wayland.c @@ -4,6 +4,12 @@ struct squeek_wayland *squeek_wayland = NULL; +void squeek_wayland_init_global(struct squeek_outputs *outputs) { + struct squeek_wayland *wayland = {0}; + wayland->outputs = outputs; + squeek_wayland = wayland; +} + // The following functions only exist // to create linkable symbols out of inline functions, // because those are not directly callable from Rust diff --git a/src/wayland.h b/src/wayland.h index a411188d..08277211 100644 --- a/src/wayland.h +++ b/src/wayland.h @@ -10,27 +10,18 @@ #include "outputs.h" struct squeek_wayland { + // globals struct zwlr_layer_shell_v1 *layer_shell; struct zwp_virtual_keyboard_manager_v1 *virtual_keyboard_manager; struct zwp_input_method_manager_v2 *input_method_manager; struct squeek_outputs *outputs; struct wl_seat *seat; + // objects + struct zwp_input_method_v2 *input_method; + struct zwp_virtual_keyboard_v1 *virtual_keyboard; }; extern struct squeek_wayland *squeek_wayland; - -static inline void squeek_wayland_init(struct squeek_wayland *wayland) { - wayland->outputs = squeek_outputs_new(); -} - -static inline void squeek_wayland_set_global(struct squeek_wayland *wayland) { - squeek_wayland = wayland; -} - -static inline void squeek_wayland_deinit(struct squeek_wayland *wayland) { - squeek_outputs_free(wayland->outputs); -} - #endif // WAYLAND_H