forked from NotAShelf/beer
wayland: render at fractional scale via viewporter, integer fallback
Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: I930684c15213a3e3b7de6b74dfb9da076a6a6964
This commit is contained in:
parent
206449a95d
commit
baed9bc98c
3 changed files with 219 additions and 34 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -50,6 +50,7 @@ dependencies = [
|
||||||
"unicode-width",
|
"unicode-width",
|
||||||
"vte",
|
"vte",
|
||||||
"wayland-client",
|
"wayland-client",
|
||||||
|
"wayland-protocols",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,11 @@ tracing-subscriber = { version = "0.3.23", features = ["env-filter"] }
|
||||||
unicode-width = "0.2.2"
|
unicode-width = "0.2.2"
|
||||||
vte = "0.15.0"
|
vte = "0.15.0"
|
||||||
wayland-client = "0.31.14"
|
wayland-client = "0.31.14"
|
||||||
|
wayland-protocols = { version = "0.32.13", features = [
|
||||||
|
"client",
|
||||||
|
"staging",
|
||||||
|
"unstable",
|
||||||
|
] }
|
||||||
|
|
||||||
[lints.rust]
|
[lints.rust]
|
||||||
unsafe_op_in_unsafe_fn = "deny"
|
unsafe_op_in_unsafe_fn = "deny"
|
||||||
|
|
|
||||||
245
src/wayland.rs
245
src/wayland.rs
|
|
@ -70,14 +70,21 @@ use smithay_client_toolkit::{
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use wayland_client::{
|
use wayland_client::{
|
||||||
Connection, QueueHandle,
|
Connection, Dispatch, Proxy, QueueHandle,
|
||||||
globals::registry_queue_init,
|
globals::{GlobalList, registry_queue_init},
|
||||||
protocol::{
|
protocol::{
|
||||||
wl_data_device::WlDataDevice, wl_data_device_manager::DndAction,
|
wl_data_device::WlDataDevice, wl_data_device_manager::DndAction,
|
||||||
wl_data_source::WlDataSource, wl_keyboard, wl_output, wl_pointer, wl_seat, wl_shm,
|
wl_data_source::WlDataSource, wl_keyboard, wl_output, wl_pointer, wl_seat, wl_shm,
|
||||||
wl_surface,
|
wl_surface,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
use wayland_protocols::wp::fractional_scale::v1::client::{
|
||||||
|
wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1,
|
||||||
|
wp_fractional_scale_v1::{self, WpFractionalScaleV1},
|
||||||
|
};
|
||||||
|
use wayland_protocols::wp::viewporter::client::{
|
||||||
|
wp_viewport::WpViewport, wp_viewporter::WpViewporter,
|
||||||
|
};
|
||||||
|
|
||||||
/// MIME types beer offers and accepts for clipboard text.
|
/// MIME types beer offers and accepts for clipboard text.
|
||||||
const TEXT_MIMES: &[&str] = &[
|
const TEXT_MIMES: &[&str] = &[
|
||||||
|
|
@ -195,6 +202,20 @@ pub fn run(config: Config, config_path: Option<std::path::PathBuf>) -> anyhow::R
|
||||||
window.set_title("beer");
|
window.set_title("beer");
|
||||||
window.set_app_id("dev.notashelf.beer");
|
window.set_app_id("dev.notashelf.beer");
|
||||||
window.set_min_size(Some((1, 1)));
|
window.set_min_size(Some((1, 1)));
|
||||||
|
|
||||||
|
// Decorrelate buffer pixels from surface size so we can render at the
|
||||||
|
// compositor's preferred fractional scale (crisp glyphs on a 150% output)
|
||||||
|
// and present at the logical size. Both are optional; without them the
|
||||||
|
// window falls back to integer buffer scaling via `scale_factor_changed`.
|
||||||
|
let viewport = bind_global::<WpViewporter>(&globals, &qh)
|
||||||
|
.map(|vp| vp.get_viewport(window.wl_surface(), &qh, ()));
|
||||||
|
// Fractional scaling needs a viewport to present the scaled buffer back at
|
||||||
|
// the logical size; without one we can only do integer buffer scaling.
|
||||||
|
let fractional_scale = viewport.as_ref().and_then(|_| {
|
||||||
|
bind_global::<WpFractionalScaleManagerV1>(&globals, &qh)
|
||||||
|
.map(|mgr| mgr.get_fractional_scale(window.wl_surface(), &qh, ()))
|
||||||
|
});
|
||||||
|
|
||||||
// First commit with no buffer kicks off the initial configure.
|
// First commit with no buffer kicks off the initial configure.
|
||||||
window.commit();
|
window.commit();
|
||||||
|
|
||||||
|
|
@ -231,6 +252,9 @@ pub fn run(config: Config, config_path: Option<std::path::PathBuf>) -> anyhow::R
|
||||||
primary_manager,
|
primary_manager,
|
||||||
cursor_shape_manager,
|
cursor_shape_manager,
|
||||||
cursor_shape_device: None,
|
cursor_shape_device: None,
|
||||||
|
viewport,
|
||||||
|
fractional_scale,
|
||||||
|
scale120: 120,
|
||||||
data_device: None,
|
data_device: None,
|
||||||
primary_device: None,
|
primary_device: None,
|
||||||
copy_source: None,
|
copy_source: None,
|
||||||
|
|
@ -312,6 +336,22 @@ pub fn run(config: Config, config_path: Option<std::path::PathBuf>) -> anyhow::R
|
||||||
Ok(app.exit_code)
|
Ok(app.exit_code)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Bind a singleton global at version 1 with `()` user-data, or `None` if the
|
||||||
|
/// compositor does not advertise it.
|
||||||
|
fn bind_global<I>(globals: &GlobalList, qh: &QueueHandle<App>) -> Option<I>
|
||||||
|
where
|
||||||
|
I: Proxy + 'static,
|
||||||
|
App: Dispatch<I, ()>,
|
||||||
|
{
|
||||||
|
match globals.bind(qh, 1..=1, ()) {
|
||||||
|
Ok(global) => Some(global),
|
||||||
|
Err(err) => {
|
||||||
|
tracing::debug!("bind {}: {err}", I::interface().name);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Columns and rows that fit a `width`×`height` px window at `metrics`, after
|
/// Columns and rows that fit a `width`×`height` px window at `metrics`, after
|
||||||
/// reserving `2 * pad` pixels of inner padding on each axis.
|
/// reserving `2 * pad` pixels of inner padding on each axis.
|
||||||
fn grid_size(
|
fn grid_size(
|
||||||
|
|
@ -363,6 +403,12 @@ struct App {
|
||||||
/// Sets the pointer to an I-beam over the window (cursor-shape-v1).
|
/// Sets the pointer to an I-beam over the window (cursor-shape-v1).
|
||||||
cursor_shape_manager: Option<CursorShapeManager>,
|
cursor_shape_manager: Option<CursorShapeManager>,
|
||||||
cursor_shape_device: Option<WpCursorShapeDeviceV1>,
|
cursor_shape_device: Option<WpCursorShapeDeviceV1>,
|
||||||
|
/// Presents a scaled buffer at the logical surface size (viewporter).
|
||||||
|
viewport: Option<WpViewport>,
|
||||||
|
/// Per-surface fractional-scale object; kept alive to receive scale events.
|
||||||
|
fractional_scale: Option<WpFractionalScaleV1>,
|
||||||
|
/// Compositor's preferred scale in 120ths (120 = 1.0, 180 = 1.5).
|
||||||
|
scale120: u32,
|
||||||
data_device: Option<DataDevice>,
|
data_device: Option<DataDevice>,
|
||||||
primary_device: Option<PrimarySelectionDevice>,
|
primary_device: Option<PrimarySelectionDevice>,
|
||||||
/// Held while we own the clipboard / primary selection, serving paste reads.
|
/// Held while we own the clipboard / primary selection, serving paste reads.
|
||||||
|
|
@ -432,12 +478,7 @@ struct App {
|
||||||
impl App {
|
impl App {
|
||||||
/// Spawn the shell at the current window size and start reading its output.
|
/// Spawn the shell at the current window size and start reading its output.
|
||||||
fn spawn_session(&mut self) {
|
fn spawn_session(&mut self) {
|
||||||
let (cols, rows) = grid_size(
|
let (cols, rows) = self.grid_dims();
|
||||||
self.renderer.metrics(),
|
|
||||||
self.width,
|
|
||||||
self.height,
|
|
||||||
(self.config.main.pad_x, self.config.main.pad_y),
|
|
||||||
);
|
|
||||||
let pty = match Pty::spawn(cols, rows, &self.config.main.term) {
|
let pty = match Pty::spawn(cols, rows, &self.config.main.term) {
|
||||||
Ok(pty) => pty,
|
Ok(pty) => pty,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
|
|
@ -596,13 +637,17 @@ impl App {
|
||||||
let new = Config::load(self.config_path.as_deref());
|
let new = Config::load(self.config_path.as_deref());
|
||||||
self.bindings =
|
self.bindings =
|
||||||
crate::bindings::Bindings::from_config(&new.key_bindings, &new.text_bindings);
|
crate::bindings::Bindings::from_config(&new.key_bindings, &new.text_bindings);
|
||||||
self.renderer.set_padding(new.main.pad_x, new.main.pad_y);
|
let font_changed = new.main.font != self.config.main.font
|
||||||
if new.main.font != self.config.main.font || new.main.font_size != self.font_size {
|
|| new.main.font_size != self.config.main.font_size;
|
||||||
match self.renderer.set_font(&new.main.font, new.main.font_size) {
|
if font_changed {
|
||||||
Ok(()) => self.font_size = new.main.font_size,
|
self.font_size = new.main.font_size;
|
||||||
Err(err) => tracing::warn!("reload font: {err:#}"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
self.config.main.font = new.main.font.clone();
|
||||||
|
self.config.main.font_size = new.main.font_size;
|
||||||
|
self.config.main.pad_x = new.main.pad_x;
|
||||||
|
self.config.main.pad_y = new.main.pad_y;
|
||||||
|
// Re-rasterize at the active scale and update padding in one place.
|
||||||
|
self.rescale_render();
|
||||||
if let Some(session) = self.session.as_mut() {
|
if let Some(session) = self.session.as_mut() {
|
||||||
session
|
session
|
||||||
.term
|
.term
|
||||||
|
|
@ -628,11 +673,8 @@ impl App {
|
||||||
if new_size == self.font_size {
|
if new_size == self.font_size {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if let Err(err) = self.renderer.set_font(&self.config.main.font, new_size) {
|
|
||||||
tracing::warn!("resize font: {err:#}");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
self.font_size = new_size;
|
self.font_size = new_size;
|
||||||
|
self.rescale_render();
|
||||||
self.frames.clear();
|
self.frames.clear();
|
||||||
self.resize_grid();
|
self.resize_grid();
|
||||||
self.needs_draw = true;
|
self.needs_draw = true;
|
||||||
|
|
@ -687,19 +729,86 @@ impl App {
|
||||||
self.needs_draw = true;
|
self.needs_draw = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Inner padding `(x, y)` in pixels.
|
/// Scale a logical pixel length to physical (buffer) pixels at the current
|
||||||
|
/// fractional scale, rounding to nearest.
|
||||||
|
fn to_phys(&self, v: u32) -> u32 {
|
||||||
|
((u64::from(v) * u64::from(self.scale120) + 60) / 120) as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Physical (buffer) pixel size at the current scale.
|
||||||
|
fn phys_dims(&self) -> (u32, u32) {
|
||||||
|
(
|
||||||
|
self.to_phys(self.width).max(1),
|
||||||
|
self.to_phys(self.height).max(1),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Columns and rows for the current physical size, metrics, and padding.
|
||||||
|
/// Scale-invariant: every term scales together so the cell count is stable.
|
||||||
|
fn grid_dims(&self) -> (u16, u16) {
|
||||||
|
let (pw, ph) = self.phys_dims();
|
||||||
|
grid_size(
|
||||||
|
self.renderer.metrics(),
|
||||||
|
pw,
|
||||||
|
ph,
|
||||||
|
(
|
||||||
|
self.to_phys(self.config.main.pad_x),
|
||||||
|
self.to_phys(self.config.main.pad_y),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Re-rasterize the font and padding at the current scale × the logical
|
||||||
|
/// font size, so glyphs are crisp at fractional scales.
|
||||||
|
fn rescale_render(&mut self) {
|
||||||
|
self.renderer.set_padding(
|
||||||
|
self.to_phys(self.config.main.pad_x),
|
||||||
|
self.to_phys(self.config.main.pad_y),
|
||||||
|
);
|
||||||
|
let px = self.to_phys(self.font_size).max(1);
|
||||||
|
if let Err(err) = self.renderer.set_font(&self.config.main.font, px) {
|
||||||
|
tracing::warn!("rasterize font at scale {}: {err:#}", self.scale120);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adopt a new preferred scale (in 120ths): re-rasterize, re-derive the grid
|
||||||
|
/// geometry, and update the viewport so the logical size stays put.
|
||||||
|
fn set_scale(&mut self, scale120: u32) {
|
||||||
|
let scale120 = scale120.max(1);
|
||||||
|
if scale120 == self.scale120 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.scale120 = scale120;
|
||||||
|
self.rescale_render();
|
||||||
|
self.frames.clear();
|
||||||
|
self.buf_dims = (0, 0);
|
||||||
|
if let Some(vp) = &self.viewport {
|
||||||
|
vp.set_destination(self.width.max(1) as i32, self.height.max(1) as i32);
|
||||||
|
}
|
||||||
|
self.resize_grid();
|
||||||
|
self.needs_draw = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Inner padding `(x, y)` in physical pixels (pointer coords are converted
|
||||||
|
/// to physical before use).
|
||||||
fn padding(&self) -> (f64, f64) {
|
fn padding(&self) -> (f64, f64) {
|
||||||
(
|
(
|
||||||
f64::from(self.config.main.pad_x),
|
f64::from(self.to_phys(self.config.main.pad_x)),
|
||||||
f64::from(self.config.main.pad_y),
|
f64::from(self.to_phys(self.config.main.pad_y)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convert a logical surface coordinate to a physical buffer coordinate.
|
||||||
|
fn to_phys_f(&self, v: f64) -> f64 {
|
||||||
|
v * f64::from(self.scale120) / 120.0
|
||||||
|
}
|
||||||
|
|
||||||
/// Map window pixel coordinates to an absolute `(row, col)` grid point.
|
/// Map window pixel coordinates to an absolute `(row, col)` grid point.
|
||||||
fn cell_at(&self, px: f64, py: f64) -> Option<(usize, usize)> {
|
fn cell_at(&self, px: f64, py: f64) -> Option<(usize, usize)> {
|
||||||
let session = self.session.as_ref()?;
|
let session = self.session.as_ref()?;
|
||||||
let m = self.renderer.metrics();
|
let m = self.renderer.metrics();
|
||||||
let (pad_x, pad_y) = self.padding();
|
let (pad_x, pad_y) = self.padding();
|
||||||
|
let (px, py) = (self.to_phys_f(px), self.to_phys_f(py));
|
||||||
let grid = session.term.grid();
|
let grid = session.term.grid();
|
||||||
let col =
|
let col =
|
||||||
((px - pad_x).max(0.0) as usize / m.width as usize).min(grid.cols().saturating_sub(1));
|
((px - pad_x).max(0.0) as usize / m.width as usize).min(grid.cols().saturating_sub(1));
|
||||||
|
|
@ -825,10 +934,14 @@ impl App {
|
||||||
let session = self.session.as_ref()?;
|
let session = self.session.as_ref()?;
|
||||||
let m = self.renderer.metrics();
|
let m = self.renderer.metrics();
|
||||||
let (pad_x, pad_y) = self.padding();
|
let (pad_x, pad_y) = self.padding();
|
||||||
|
let (ppx, ppy) = (
|
||||||
|
self.to_phys_f(self.pointer_pos.0),
|
||||||
|
self.to_phys_f(self.pointer_pos.1),
|
||||||
|
);
|
||||||
let grid = session.term.grid();
|
let grid = session.term.grid();
|
||||||
let col = ((self.pointer_pos.0 - pad_x).max(0.0) as usize / m.width as usize)
|
let col =
|
||||||
.min(grid.cols().saturating_sub(1));
|
((ppx - pad_x).max(0.0) as usize / m.width as usize).min(grid.cols().saturating_sub(1));
|
||||||
let row = ((self.pointer_pos.1 - pad_y).max(0.0) as usize / m.height as usize)
|
let row = ((ppy - pad_y).max(0.0) as usize / m.height as usize)
|
||||||
.min(grid.rows().saturating_sub(1));
|
.min(grid.rows().saturating_sub(1));
|
||||||
Some((col, row))
|
Some((col, row))
|
||||||
}
|
}
|
||||||
|
|
@ -1135,12 +1248,7 @@ impl App {
|
||||||
/// Recompute the grid size for the current window and tell the grid and the
|
/// Recompute the grid size for the current window and tell the grid and the
|
||||||
/// PTY about it if it changed.
|
/// PTY about it if it changed.
|
||||||
fn resize_grid(&mut self) {
|
fn resize_grid(&mut self) {
|
||||||
let (cols, rows) = grid_size(
|
let (cols, rows) = self.grid_dims();
|
||||||
self.renderer.metrics(),
|
|
||||||
self.width,
|
|
||||||
self.height,
|
|
||||||
(self.config.main.pad_x, self.config.main.pad_y),
|
|
||||||
);
|
|
||||||
let Some(session) = self.session.as_mut() else {
|
let Some(session) = self.session.as_mut() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
@ -1213,7 +1321,9 @@ impl App {
|
||||||
/// them, damage just those rows, and commit with a frame-callback request.
|
/// them, damage just those rows, and commit with a frame-callback request.
|
||||||
fn present(&mut self) {
|
fn present(&mut self) {
|
||||||
self.needs_draw = false;
|
self.needs_draw = false;
|
||||||
let (w, h) = (self.width, self.height);
|
// Render into a buffer sized in physical pixels (logical × scale); the
|
||||||
|
// viewport presents it back at the logical surface size.
|
||||||
|
let (w, h) = self.phys_dims();
|
||||||
let m = self.renderer.metrics();
|
let m = self.renderer.metrics();
|
||||||
let (focused, blink_on) = (self.focused, self.blink_on);
|
let (focused, blink_on) = (self.focused, self.blink_on);
|
||||||
|
|
||||||
|
|
@ -1299,7 +1409,7 @@ impl App {
|
||||||
// A buffer used for the first time has uninitialized margins; paint the
|
// A buffer used for the first time has uninitialized margins; paint the
|
||||||
// whole thing (background + padding) once, then damage it in full below.
|
// whole thing (background + padding) once, then damage it in full below.
|
||||||
let fresh = self.frames[idx].rows.is_empty();
|
let fresh = self.frames[idx].rows.is_empty();
|
||||||
let pad_y = self.config.main.pad_y as i32;
|
let pad_y = self.to_phys(self.config.main.pad_y) as i32;
|
||||||
let Some(canvas) = self.pool.canvas(&self.frames[idx].buffer) else {
|
let Some(canvas) = self.pool.canvas(&self.frames[idx].buffer) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
@ -1329,6 +1439,14 @@ impl App {
|
||||||
tracing::error!("attach buffer: {err}");
|
tracing::error!("attach buffer: {err}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// With a viewport the buffer is presented at the logical destination, so
|
||||||
|
// its own scale stays 1; without one, fall back to integer buffer scale.
|
||||||
|
if let Some(vp) = &self.viewport {
|
||||||
|
surface.set_buffer_scale(1);
|
||||||
|
vp.set_destination(self.width.max(1) as i32, self.height.max(1) as i32);
|
||||||
|
} else {
|
||||||
|
surface.set_buffer_scale((self.scale120 / 120).max(1) as i32);
|
||||||
|
}
|
||||||
if fresh {
|
if fresh {
|
||||||
surface.damage_buffer(0, 0, w as i32, h as i32);
|
surface.damage_buffer(0, 0, w as i32, h as i32);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -1349,8 +1467,13 @@ impl CompositorHandler for App {
|
||||||
_: &Connection,
|
_: &Connection,
|
||||||
_: &QueueHandle<Self>,
|
_: &QueueHandle<Self>,
|
||||||
_: &wl_surface::WlSurface,
|
_: &wl_surface::WlSurface,
|
||||||
_: i32,
|
factor: i32,
|
||||||
) {
|
) {
|
||||||
|
// Integer fallback for compositors without fractional-scale-v1; ignored
|
||||||
|
// when the fractional-scale object drives the scale instead.
|
||||||
|
if self.fractional_scale.is_none() {
|
||||||
|
self.set_scale((factor.max(1) as u32) * 120);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn transform_changed(
|
fn transform_changed(
|
||||||
|
|
@ -1403,6 +1526,9 @@ impl WindowHandler for App {
|
||||||
if let (Some(w), Some(h)) = configure.new_size {
|
if let (Some(w), Some(h)) = configure.new_size {
|
||||||
self.width = w.get();
|
self.width = w.get();
|
||||||
self.height = h.get();
|
self.height = h.get();
|
||||||
|
if let Some(vp) = &self.viewport {
|
||||||
|
vp.set_destination(self.width.max(1) as i32, self.height.max(1) as i32);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
self.focused = configure.is_activated();
|
self.focused = configure.is_activated();
|
||||||
if self.session.is_none() {
|
if self.session.is_none() {
|
||||||
|
|
@ -1854,6 +1980,59 @@ impl PrimarySelectionSourceHandler for App {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fractional-scale and viewporter are not wrapped by sctk, so dispatch them by
|
||||||
|
// hand. Only the fractional-scale object carries an event we act on.
|
||||||
|
impl Dispatch<WpFractionalScaleV1, ()> for App {
|
||||||
|
fn event(
|
||||||
|
state: &mut Self,
|
||||||
|
_: &WpFractionalScaleV1,
|
||||||
|
event: wp_fractional_scale_v1::Event,
|
||||||
|
_: &(),
|
||||||
|
_: &Connection,
|
||||||
|
_: &QueueHandle<Self>,
|
||||||
|
) {
|
||||||
|
if let wp_fractional_scale_v1::Event::PreferredScale { scale } = event {
|
||||||
|
state.set_scale(scale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Dispatch<WpFractionalScaleManagerV1, ()> for App {
|
||||||
|
fn event(
|
||||||
|
_: &mut Self,
|
||||||
|
_: &WpFractionalScaleManagerV1,
|
||||||
|
_: <WpFractionalScaleManagerV1 as Proxy>::Event,
|
||||||
|
_: &(),
|
||||||
|
_: &Connection,
|
||||||
|
_: &QueueHandle<Self>,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Dispatch<WpViewporter, ()> for App {
|
||||||
|
fn event(
|
||||||
|
_: &mut Self,
|
||||||
|
_: &WpViewporter,
|
||||||
|
_: <WpViewporter as Proxy>::Event,
|
||||||
|
_: &(),
|
||||||
|
_: &Connection,
|
||||||
|
_: &QueueHandle<Self>,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Dispatch<WpViewport, ()> for App {
|
||||||
|
fn event(
|
||||||
|
_: &mut Self,
|
||||||
|
_: &WpViewport,
|
||||||
|
_: <WpViewport as Proxy>::Event,
|
||||||
|
_: &(),
|
||||||
|
_: &Connection,
|
||||||
|
_: &QueueHandle<Self>,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
delegate_compositor!(App);
|
delegate_compositor!(App);
|
||||||
delegate_output!(App);
|
delegate_output!(App);
|
||||||
delegate_shm!(App);
|
delegate_shm!(App);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue