wayland: Move initialization to the Rust side

This will help make the init procedure safer, by limiting the number of Rust objects that need to be carried to the C side and may be mangled on the way there.

The second benefit is that it allows outputs to become part of new state management.
This commit is contained in:
Dorota Czaplejewicz
2022-01-23 17:12:33 +00:00
parent 1b72cbdfaa
commit f4f44a49ae
9 changed files with 148 additions and 91 deletions

View File

@ -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<Event>;
/// Type of the sender that waits for internal state changes
type UISender = glib::Sender<Commands>;
/// This loop driver spawns a new thread which updates the state in a loop,

View File

@ -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);

View File

@ -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<Receiver<Commands>>,
state_manager: Wrapped<driver::Threaded>,
submission: Wrapped<Submission>,
/// 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<Outputs>,
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),
}
}

View File

@ -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<Outputs>;
/// Wrapping Outputs is required for calling its methods from C
type COutputs = Wrapped<Outputs>;
/// 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<Output>,
sender: event_loop::driver::Threaded,
}
impl Outputs {
pub fn new(sender: event_loop::driver::Threaded) -> Outputs {
Outputs {
outputs: Vec::new(),
sender,
}
}
}

View File

@ -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, &registry_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, &registry_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;
}

View File

@ -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.

View File

@ -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,

View File

@ -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

View File

@ -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