diff --git a/src/lib.rs b/src/lib.rs index e5aded47..d4652349 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,6 +12,7 @@ pub mod float_ord; pub mod imservice; mod keyboard; mod layout; +mod outputs; mod resources; mod submission; mod util; diff --git a/src/outputs.h b/src/outputs.h new file mode 100644 index 00000000..9e88ab92 --- /dev/null +++ b/src/outputs.h @@ -0,0 +1,13 @@ +#ifndef __OUTPUTS_H +#define __OUTPUTS_H + +#include "wayland-client-protocol.h" + +struct squeek_outputs; + +struct squeek_outputs *squeek_outputs_new(); +void squeek_outputs_free(struct squeek_outputs*); +void squeek_outputs_register(struct squeek_outputs*, struct wl_output *output); +struct wl_output *squeek_outputs_get_current(struct squeek_outputs*); + +#endif diff --git a/src/outputs.rs b/src/outputs.rs new file mode 100644 index 00000000..0ce48476 --- /dev/null +++ b/src/outputs.rs @@ -0,0 +1,227 @@ +/*! Managing Wayland outputs */ + +use std::vec::Vec; + + +/// Gathers stuff defined in C or called by C +pub mod c { + use super::*; + + use std::os::raw::{ c_char, c_void }; + + use ::util::c::COpaquePtr; + + // Defined in C + + #[repr(transparent)] + #[derive(Clone, PartialEq)] + pub struct WlOutput(*const c_void); + + #[repr(C)] + struct WlOutputListener { + geometry: extern fn( + T, // data + WlOutput, + i32, // x + i32, // y + i32, // physical_width + i32, // physical_height + i32, // subpixel + *const c_char, // make + *const c_char, // model + i32, // transform + ), + mode: extern fn( + T, // data + WlOutput, + u32, // flags + i32, // width + i32, // height + i32, // refresh + ), + done: extern fn( + T, // data + WlOutput, + ), + scale: extern fn( + T, // data + WlOutput, + i32, // factor + ), + } + + bitflags!{ + /// Map to `wl_output.mode` values + pub struct Mode: u32 { + const NONE = 0x0; + const CURRENT = 0x1; + const PREFERRED = 0x2; + } + } + + extern "C" { + // Rustc wrongly assumes + // that COutputs allows C direct access to the underlying RefCell + #[allow(improper_ctypes)] + fn squeek_output_add_listener( + wl_output: WlOutput, + listener: *const WlOutputListener, + data: COutputs, + ) -> i32; + } + + type COutputs = ::util::c::Wrapped; + + // Defined in Rust + + extern fn outputs_handle_geometry( + _outputs: COutputs, + _wl_output: WlOutput, + _x: i32, _y: i32, + _phys_width: i32, _phys_height: i32, + _subpixel: i32, + _make: *const c_char, _model: *const c_char, + _transform: i32, + ) { + //println!("geometry handled {},{}", x, y); + // Totally unused + } + + extern fn outputs_handle_mode( + outputs: COutputs, + wl_output: WlOutput, + flags: u32, + width: i32, + height: i32, + _refresh: i32, + ) { + let flags = Mode::from_bits(flags).unwrap_or_else(|| { + eprintln!("Warning: received invalid wl_output.mode flags"); + Mode::NONE + }); + let outputs = outputs.clone_ref(); + let mut outputs = outputs.borrow_mut(); + let mut output_state: Option<&mut OutputState> = outputs.outputs + .iter_mut() + .find_map(|o| + if o.output == wl_output { Some(&mut o.pending) } else { None } + ); + match output_state { + Some(ref mut state) => { + if flags.contains(Mode::CURRENT) { + state.current_mode = Some(super::Mode { width, height}); + } + }, + None => eprintln!("Wayland error: Got mode on unknown output"), + }; + } + + extern fn outputs_handle_done( + outputs: COutputs, + wl_output: WlOutput, + ) { + let outputs = outputs.clone_ref(); + let mut outputs = outputs.borrow_mut(); + let mut output = outputs.outputs + .iter_mut() + .find(|o| o.output == wl_output); + match output { + Some(ref mut output) => { output.current = output.pending.clone(); } + None => eprintln!("Wayland error: Got done on unknown output"), + }; + } + + extern fn outputs_handle_scale( + outputs: COutputs, + wl_output: WlOutput, + factor: i32, + ) { + let outputs = outputs.clone_ref(); + let mut outputs = outputs.borrow_mut(); + let output_state = outputs.outputs + .iter_mut() + .find_map(|o| + if o.output == wl_output { Some(&mut o.pending) } else { None } + ); + match output_state { + Some(state) => { state.scale = factor; } + None => eprintln!("Wayland error: Got done on unknown output"), + }; + } + + #[no_mangle] + pub extern "C" + fn squeek_outputs_new() -> COutputs { + COutputs::new(Outputs { outputs: Vec::new() }) + } + + #[no_mangle] + pub extern "C" + fn squeek_outputs_free(outputs: COutputs) { + unsafe { outputs.unwrap() }; // gets dropped + } + + #[no_mangle] + pub extern "C" + fn squeek_outputs_register(raw_collection: COutputs, output: WlOutput) { + let collection = raw_collection.clone_ref(); + let mut collection = collection.borrow_mut(); + collection.outputs.push(Output { + output: output.clone(), + pending: OutputState::uninitialized(), + current: OutputState::uninitialized(), + }); + + unsafe { squeek_output_add_listener( + output, + &WlOutputListener { + geometry: outputs_handle_geometry, + mode: outputs_handle_mode, + done: outputs_handle_done, + scale: outputs_handle_scale, + } as *const WlOutputListener, + raw_collection, + )}; + } + + #[no_mangle] + pub extern "C" + fn squeek_outputs_get_current(raw_collection: COutputs) -> WlOutput { + let collection = raw_collection.clone_ref(); + let collection = collection.borrow(); + collection.outputs[0].output.clone() + } + + // TODO: handle unregistration +} + +#[derive(Clone)] +struct Mode { + width: i32, + height: i32, +} + +#[derive(Clone)] +pub struct OutputState { + current_mode: Option, + scale: i32, +} + +impl OutputState { + fn uninitialized() -> OutputState { + OutputState { + current_mode: None, + scale: 1, + } + } +} + +pub struct Output { + output: c::WlOutput, + pending: OutputState, + current: OutputState, +} + +pub struct Outputs { + outputs: Vec, +} diff --git a/src/server-context-service.c b/src/server-context-service.c index becd7477..a3ab820f 100644 --- a/src/server-context-service.c +++ b/src/server-context-service.c @@ -120,10 +120,12 @@ make_window (ServerContextService *context) if (context->window) g_error("Window already present"); + struct wl_output *output = squeek_outputs_get_current(squeek_wayland->outputs); + context->window = g_object_new ( PHOSH_TYPE_LAYER_SURFACE, "layer-shell", squeek_wayland->layer_shell, - "wl-output", g_ptr_array_index(squeek_wayland->outputs, 0), // TODO: select output as needed, + "wl-output", output, "height", KEYBOARD_HEIGHT, "anchor", ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT diff --git a/src/server-main.c b/src/server-main.c index 96b37acd..56a2fd1f 100644 --- a/src/server-main.c +++ b/src/server-main.c @@ -28,6 +28,7 @@ #include "eekboard/eekboard-service.h" #include "eek/eek.h" #include "imservice.h" +#include "outputs.h" #include "server-context-service.h" #include "wayland.h" @@ -116,7 +117,7 @@ registry_handle_global (void *data, } else if (!strcmp (interface, "wl_output")) { struct wl_output *output = wl_registry_bind (registry, name, &wl_output_interface, 2); - g_ptr_array_add (instance->wayland.outputs, output); + squeek_outputs_register(instance->wayland.outputs, output); } else if (!strcmp(interface, "wl_seat")) { instance->wayland.seat = wl_registry_bind(registry, name, &wl_seat_interface, 1); diff --git a/src/util.rs b/src/util.rs index 3a1b6020..baec594e 100644 --- a/src/util.rs +++ b/src/util.rs @@ -62,6 +62,10 @@ pub mod c { assert_eq!(as_str(&ptr::null()), Ok(None)) } } + + /// Marker trait for values that can be transferred to/received from C. + /// They must be either *const or *mut or repr(transparent). + pub trait COpaquePtr {} /// Wraps structures to pass them safely to/from C /// Since C doesn't respect borrowing rules, @@ -79,6 +83,9 @@ pub mod c { // which is a bit too complex for now. impl Wrapped { + pub fn new(value: T) -> Wrapped { + Wrapped::wrap(Rc::new(RefCell::new(value))) + } pub fn wrap(state: Rc>) -> Wrapped { Wrapped(Rc::into_raw(state)) } @@ -116,6 +123,8 @@ pub mod c { r.to_owned() } } + + impl COpaquePtr for Wrapped {} } pub trait CloneOwned { diff --git a/src/wayland.c b/src/wayland.c index cd10609e..96b037f4 100644 --- a/src/wayland.c +++ b/src/wayland.c @@ -10,3 +10,8 @@ void eek_virtual_keyboard_v1_key(struct zwp_virtual_keyboard_v1 *zwp_virtual_keyboard_v1, uint32_t time, uint32_t key, uint32_t state) { zwp_virtual_keyboard_v1_key(zwp_virtual_keyboard_v1, time, key, state); } + +int squeek_output_add_listener(struct wl_output *wl_output, + const struct wl_output_listener *listener, void *data) { + return wl_output_add_listener(wl_output, listener, data); +} diff --git a/src/wayland.h b/src/wayland.h index 3907cc35..a411188d 100644 --- a/src/wayland.h +++ b/src/wayland.h @@ -1,18 +1,19 @@ #ifndef WAYLAND_H #define WAYLAND_H +#include + #include "wlr-layer-shell-unstable-v1-client-protocol.h" #include "virtual-keyboard-unstable-v1-client-protocol.h" #include "input-method-unstable-v2-client-protocol.h" -#include - +#include "outputs.h" struct squeek_wayland { 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; - GPtrArray *outputs; // *wl_output + struct squeek_outputs *outputs; struct wl_seat *seat; }; @@ -21,7 +22,7 @@ extern struct squeek_wayland *squeek_wayland; static inline void squeek_wayland_init(struct squeek_wayland *wayland) { - wayland->outputs = g_ptr_array_new(); + wayland->outputs = squeek_outputs_new(); } static inline void squeek_wayland_set_global(struct squeek_wayland *wayland) { @@ -29,7 +30,7 @@ static inline void squeek_wayland_set_global(struct squeek_wayland *wayland) { } static inline void squeek_wayland_deinit(struct squeek_wayland *wayland) { - g_ptr_array_free(wayland->outputs, TRUE); + squeek_outputs_free(wayland->outputs); } #endif // WAYLAND_H