wayland: Listen to output changes

This commit is contained in:
Dorota Czaplejewicz
2019-10-10 11:22:36 +00:00
parent 869a0af67f
commit 24126ad4f3
8 changed files with 266 additions and 7 deletions

View File

@ -12,6 +12,7 @@ pub mod float_ord;
pub mod imservice;
mod keyboard;
mod layout;
mod outputs;
mod resources;
mod submission;
mod util;

13
src/outputs.h Normal file
View File

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

227
src/outputs.rs Normal file
View File

@ -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<T: COpaquePtr> {
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<COutputs>,
data: COutputs,
) -> i32;
}
type COutputs = ::util::c::Wrapped<Outputs>;
// 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<COutputs>,
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<Mode>,
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<Output>,
}

View File

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

View File

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

View File

@ -63,6 +63,10 @@ pub mod c {
}
}
/// 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,
/// RefCell will enforce them dynamically (only 1 writer/many readers)
@ -79,6 +83,9 @@ pub mod c {
// which is a bit too complex for now.
impl<T> Wrapped<T> {
pub fn new(value: T) -> Wrapped<T> {
Wrapped::wrap(Rc::new(RefCell::new(value)))
}
pub fn wrap(state: Rc<RefCell<T>>) -> Wrapped<T> {
Wrapped(Rc::into_raw(state))
}
@ -116,6 +123,8 @@ pub mod c {
r.to_owned()
}
}
impl<T> COpaquePtr for Wrapped<T> {}
}
pub trait CloneOwned {

View File

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

View File

@ -1,18 +1,19 @@
#ifndef WAYLAND_H
#define WAYLAND_H
#include <gmodule.h>
#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 <gmodule.h>
#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