Integrate native Rime and stabilize input, output, and rendering
- Add optional modifier-only Super binding whose tap is cancelled by other input, focus changes, grabs, locks, and session teardown. - Ship the native ferese-rime user service, Rime defaults, installer inventory, package dependencies, session environment, and docs. - Vendor Smithay 8a27023 with the SHM texture-cache format fix and add GPU regressions for format changes and resize snapshots. - Release reused CRTC owners before staging output modes, then restore changed outputs before former owners when a transaction fails. - Wait for resize-capture GPU completion, expand filtered surface damage, and preserve IME popup origin under fractional scaling. - Mark weekends like holidays in both calendar layouts.
This commit is contained in:
1 parent
a2be421974
commit
993bc0f88f
391 files changed
+130908
-76
No files matched your search
+10
@@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "test_clients"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
license = "MIT"
|
||||
|
||||
[dependencies]
|
||||
smithay-client-toolkit = "0.19.2"
|
||||
tracing = { version = "0.1.37" }
|
||||
tracing-subscriber = { version = "0.3.16", features = ["env-filter"] }
|
||||
@@ -0,0 +1,355 @@
|
||||
//! Constrain the pointer to surface region
|
||||
|
||||
use smithay_client_toolkit::{
|
||||
compositor::{CompositorHandler, CompositorState},
|
||||
delegate_compositor, delegate_output, delegate_pointer, delegate_pointer_constraints, delegate_registry,
|
||||
delegate_seat, delegate_shm, delegate_xdg_shell, delegate_xdg_window,
|
||||
output::{OutputHandler, OutputState},
|
||||
reexports::{
|
||||
calloop, client as wayland_client,
|
||||
protocols::wp::pointer_constraints::zv1::client::{
|
||||
zwp_confined_pointer_v1::ZwpConfinedPointerV1, zwp_locked_pointer_v1::ZwpLockedPointerV1,
|
||||
zwp_pointer_constraints_v1,
|
||||
},
|
||||
},
|
||||
registry::{ProvidesRegistryState, RegistryState},
|
||||
registry_handlers,
|
||||
seat::{
|
||||
Capability, SeatHandler, SeatState,
|
||||
pointer::{PointerEvent, PointerEventKind, PointerHandler},
|
||||
pointer_constraints::{PointerConstraintsHandler, PointerConstraintsState},
|
||||
},
|
||||
shell::{
|
||||
WaylandSurface,
|
||||
xdg::{
|
||||
XdgShell,
|
||||
window::{Window, WindowConfigure, WindowDecorations, WindowHandler},
|
||||
},
|
||||
},
|
||||
shm::{
|
||||
Shm, ShmHandler,
|
||||
slot::{Buffer, SlotPool},
|
||||
},
|
||||
};
|
||||
|
||||
use wayland_client::{
|
||||
Connection, QueueHandle, delegate_noop,
|
||||
protocol::{
|
||||
wl_output::{self, WlOutput},
|
||||
wl_pointer::WlPointer,
|
||||
wl_region::WlRegion,
|
||||
wl_seat,
|
||||
wl_surface::{self, WlSurface},
|
||||
},
|
||||
};
|
||||
|
||||
use tracing::{info, warn};
|
||||
|
||||
fn main() {
|
||||
test_clients::init_logging();
|
||||
|
||||
let (mut event_loop, globals, qh) = test_clients::init_connection::<App>();
|
||||
|
||||
let compositor_state = CompositorState::bind(&globals, &qh).unwrap();
|
||||
let xdg_shell = XdgShell::bind(&globals, &qh).unwrap();
|
||||
|
||||
let surface = compositor_state.create_surface(&qh);
|
||||
let window = xdg_shell.create_window(surface, WindowDecorations::RequestServer, &qh);
|
||||
|
||||
let shm = Shm::bind(&globals, &qh).unwrap();
|
||||
let pool = SlotPool::new(256 * 256 * 4, &shm).unwrap();
|
||||
|
||||
let mut simple_window = App {
|
||||
registry_state: RegistryState::new(&globals),
|
||||
output_state: OutputState::new(&globals, &qh),
|
||||
compositor_state,
|
||||
seat_state: SeatState::new(&globals, &qh),
|
||||
shm,
|
||||
pointer: None,
|
||||
confined_pointer: None,
|
||||
pointer_constraint_state: PointerConstraintsState::bind(&globals, &qh),
|
||||
|
||||
should_be_mapped: false,
|
||||
|
||||
first_configure: true,
|
||||
pool,
|
||||
width: 256,
|
||||
height: 256,
|
||||
shift: 0,
|
||||
buffer: None,
|
||||
window,
|
||||
loop_signal: event_loop.get_signal(),
|
||||
};
|
||||
|
||||
simple_window.map();
|
||||
|
||||
event_loop.run(None, &mut simple_window, |_| {}).unwrap();
|
||||
}
|
||||
|
||||
struct App {
|
||||
registry_state: RegistryState,
|
||||
output_state: OutputState,
|
||||
compositor_state: CompositorState,
|
||||
seat_state: SeatState,
|
||||
shm: Shm,
|
||||
|
||||
pointer: Option<WlPointer>,
|
||||
confined_pointer: Option<ZwpConfinedPointerV1>,
|
||||
pointer_constraint_state: PointerConstraintsState,
|
||||
|
||||
should_be_mapped: bool,
|
||||
|
||||
first_configure: bool,
|
||||
pool: SlotPool,
|
||||
width: u32,
|
||||
height: u32,
|
||||
shift: u32,
|
||||
buffer: Option<Buffer>,
|
||||
window: Window,
|
||||
loop_signal: calloop::LoopSignal,
|
||||
}
|
||||
|
||||
impl CompositorHandler for App {
|
||||
fn frame(
|
||||
&mut self,
|
||||
conn: &Connection,
|
||||
qh: &QueueHandle<Self>,
|
||||
_surface: &wl_surface::WlSurface,
|
||||
_time: u32,
|
||||
) {
|
||||
self.draw(conn, qh);
|
||||
}
|
||||
fn surface_enter(&mut self, _: &Connection, _: &QueueHandle<Self>, _: &WlSurface, _: &WlOutput) {}
|
||||
fn surface_leave(&mut self, _: &Connection, _: &QueueHandle<Self>, _: &WlSurface, _: &WlOutput) {}
|
||||
fn scale_factor_changed(&mut self, _: &Connection, _: &QueueHandle<Self>, _: &WlSurface, _: i32) {}
|
||||
fn transform_changed(
|
||||
&mut self,
|
||||
_: &Connection,
|
||||
_: &QueueHandle<Self>,
|
||||
_: &WlSurface,
|
||||
_: wl_output::Transform,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
impl WindowHandler for App {
|
||||
fn request_close(&mut self, _: &Connection, _: &QueueHandle<Self>, _: &Window) {
|
||||
self.loop_signal.stop();
|
||||
}
|
||||
|
||||
fn configure(
|
||||
&mut self,
|
||||
conn: &Connection,
|
||||
qh: &QueueHandle<Self>,
|
||||
_window: &Window,
|
||||
configure: WindowConfigure,
|
||||
_serial: u32,
|
||||
) {
|
||||
self.buffer = None;
|
||||
self.width = configure.new_size.0.map(|v| v.get()).unwrap_or(256);
|
||||
self.height = configure.new_size.1.map(|v| v.get()).unwrap_or(256);
|
||||
|
||||
if self.first_configure {
|
||||
self.first_configure = false;
|
||||
self.draw(conn, qh);
|
||||
}
|
||||
|
||||
if let Some(confined_pointer) = &self.confined_pointer {
|
||||
confined_pointer.destroy();
|
||||
self.confined_pointer = None;
|
||||
}
|
||||
|
||||
if let Some(pointer) = &self.pointer {
|
||||
let region = self.compositor_state.wl_compositor().create_region(qh, ());
|
||||
let w = self.width as i32;
|
||||
let h = self.height as i32;
|
||||
|
||||
region.add(0, 0, w, h);
|
||||
region.subtract(w / 2, h / 2, w / 2, h / 2);
|
||||
region.subtract(0, 0, w / 4, h / 4);
|
||||
|
||||
let confined_pointer = self
|
||||
.pointer_constraint_state
|
||||
.confine_pointer(
|
||||
self.window.wl_surface(),
|
||||
&pointer,
|
||||
Some(®ion),
|
||||
zwp_pointer_constraints_v1::Lifetime::Persistent,
|
||||
qh,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
self.confined_pointer = Some(confined_pointer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl App {
|
||||
fn map(&mut self) {
|
||||
self.first_configure = true;
|
||||
self.should_be_mapped = true;
|
||||
self.window.commit();
|
||||
}
|
||||
|
||||
fn draw(&mut self, _conn: &Connection, qh: &QueueHandle<Self>) {
|
||||
if !self.should_be_mapped {
|
||||
return;
|
||||
}
|
||||
|
||||
test_clients::draw(
|
||||
qh,
|
||||
self.window.wl_surface(),
|
||||
&mut self.pool,
|
||||
&mut self.buffer,
|
||||
self.width,
|
||||
self.height,
|
||||
&mut self.shift,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
delegate_compositor!(App);
|
||||
delegate_output!(App);
|
||||
delegate_shm!(App);
|
||||
|
||||
delegate_xdg_shell!(App);
|
||||
delegate_xdg_window!(App);
|
||||
|
||||
delegate_registry!(App);
|
||||
delegate_seat!(App);
|
||||
delegate_pointer!(App);
|
||||
delegate_pointer_constraints!(App);
|
||||
delegate_noop!(App: WlRegion);
|
||||
|
||||
impl SeatHandler for App {
|
||||
fn seat_state(&mut self) -> &mut SeatState {
|
||||
&mut self.seat_state
|
||||
}
|
||||
|
||||
fn new_seat(&mut self, _: &Connection, _: &QueueHandle<Self>, _: wl_seat::WlSeat) {}
|
||||
|
||||
fn new_capability(
|
||||
&mut self,
|
||||
_conn: &Connection,
|
||||
qh: &QueueHandle<Self>,
|
||||
seat: wl_seat::WlSeat,
|
||||
capability: Capability,
|
||||
) {
|
||||
info!("add capability: {capability}");
|
||||
if capability == Capability::Pointer && self.pointer.is_none() {
|
||||
let pointer = self
|
||||
.seat_state
|
||||
.get_pointer(qh, &seat)
|
||||
.expect("Failed to create pointer");
|
||||
|
||||
self.pointer = Some(pointer);
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_capability(
|
||||
&mut self,
|
||||
_conn: &Connection,
|
||||
_: &QueueHandle<Self>,
|
||||
_: wl_seat::WlSeat,
|
||||
capability: Capability,
|
||||
) {
|
||||
warn!("remove capability: {capability}");
|
||||
if capability == Capability::Pointer && self.pointer.is_some() {
|
||||
self.confined_pointer.take().unwrap().destroy();
|
||||
self.pointer.take().unwrap().release();
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_seat(&mut self, _: &Connection, _: &QueueHandle<Self>, _: wl_seat::WlSeat) {}
|
||||
}
|
||||
|
||||
impl PointerHandler for App {
|
||||
fn pointer_frame(
|
||||
&mut self,
|
||||
_conn: &Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_pointer: &WlPointer,
|
||||
events: &[PointerEvent],
|
||||
) {
|
||||
for event in events {
|
||||
match event.kind {
|
||||
PointerEventKind::Enter { .. } => {
|
||||
info!("Pointer entered @{:?}", event.position);
|
||||
}
|
||||
PointerEventKind::Leave { .. } => {
|
||||
info!("Pointer left");
|
||||
}
|
||||
PointerEventKind::Motion { .. } => {
|
||||
info!("Pointer motion: {:?}", event.position);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PointerConstraintsHandler for App {
|
||||
fn confined(
|
||||
&mut self,
|
||||
_conn: &Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_confined_pointer: &ZwpConfinedPointerV1,
|
||||
_surface: &WlSurface,
|
||||
_pointer: &WlPointer,
|
||||
) {
|
||||
info!("Pointer confined");
|
||||
}
|
||||
|
||||
fn unconfined(
|
||||
&mut self,
|
||||
_conn: &Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_confined_pointer: &ZwpConfinedPointerV1,
|
||||
_surface: &WlSurface,
|
||||
_pointer: &WlPointer,
|
||||
) {
|
||||
warn!("Pointer unconfined");
|
||||
}
|
||||
|
||||
fn locked(
|
||||
&mut self,
|
||||
_conn: &Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_locked_pointer: &ZwpLockedPointerV1,
|
||||
_surface: &WlSurface,
|
||||
_pointer: &WlPointer,
|
||||
) {
|
||||
}
|
||||
|
||||
fn unlocked(
|
||||
&mut self,
|
||||
_conn: &Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_locked_pointer: &ZwpLockedPointerV1,
|
||||
_surface: &WlSurface,
|
||||
_pointer: &WlPointer,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
impl OutputHandler for App {
|
||||
fn output_state(&mut self) -> &mut OutputState {
|
||||
&mut self.output_state
|
||||
}
|
||||
fn new_output(&mut self, _: &Connection, _: &QueueHandle<Self>, _: WlOutput) {}
|
||||
fn update_output(&mut self, _: &Connection, _: &QueueHandle<Self>, _: WlOutput) {}
|
||||
fn output_destroyed(&mut self, _: &Connection, _: &QueueHandle<Self>, _: WlOutput) {}
|
||||
}
|
||||
|
||||
impl ShmHandler for App {
|
||||
fn shm_state(&mut self) -> &mut Shm {
|
||||
&mut self.shm
|
||||
}
|
||||
}
|
||||
|
||||
impl ProvidesRegistryState for App {
|
||||
fn registry(&mut self) -> &mut RegistryState {
|
||||
&mut self.registry_state
|
||||
}
|
||||
registry_handlers![OutputState,];
|
||||
}
|
||||
@@ -0,0 +1,236 @@
|
||||
//! Attempt to reproduce https://github.com/Smithay/smithay/issues/1894
|
||||
|
||||
use smithay_client_toolkit::delegate_subcompositor;
|
||||
use smithay_client_toolkit::reexports::client::protocol::wl_subsurface::WlSubsurface;
|
||||
use smithay_client_toolkit::reexports::{calloop, client as wayland_client};
|
||||
|
||||
use smithay_client_toolkit::subcompositor::SubcompositorState;
|
||||
use smithay_client_toolkit::{
|
||||
compositor::{CompositorHandler, CompositorState},
|
||||
delegate_compositor, delegate_output, delegate_registry, delegate_shm, delegate_xdg_shell,
|
||||
delegate_xdg_window,
|
||||
output::{OutputHandler, OutputState},
|
||||
registry::{ProvidesRegistryState, RegistryState},
|
||||
registry_handlers,
|
||||
shell::{
|
||||
WaylandSurface,
|
||||
xdg::{
|
||||
XdgShell,
|
||||
window::{Window, WindowConfigure, WindowDecorations, WindowHandler},
|
||||
},
|
||||
},
|
||||
shm::{
|
||||
Shm, ShmHandler,
|
||||
slot::{Buffer, SlotPool},
|
||||
},
|
||||
};
|
||||
use tracing::info;
|
||||
use wayland_client::{
|
||||
Connection, QueueHandle,
|
||||
protocol::{
|
||||
wl_output::{self, WlOutput},
|
||||
wl_surface::{self, WlSurface},
|
||||
},
|
||||
};
|
||||
|
||||
fn main() {
|
||||
test_clients::init_logging();
|
||||
|
||||
let (mut event_loop, globals, qh) = test_clients::init_connection::<App>();
|
||||
|
||||
let compositor = CompositorState::bind(&globals, &qh).unwrap();
|
||||
let subcompositor = SubcompositorState::bind(compositor.wl_compositor().clone(), &globals, &qh).unwrap();
|
||||
let xdg_shell = XdgShell::bind(&globals, &qh).unwrap();
|
||||
|
||||
let surface = compositor.create_surface(&qh);
|
||||
let subsurface = subcompositor.create_subsurface(surface.clone(), &qh);
|
||||
|
||||
let window = xdg_shell.create_window(surface, WindowDecorations::RequestServer, &qh);
|
||||
|
||||
let shm = Shm::bind(&globals, &qh).unwrap();
|
||||
let pool = SlotPool::new(256 * 256 * 4, &shm).unwrap();
|
||||
|
||||
let mut app = App {
|
||||
registry_state: RegistryState::new(&globals),
|
||||
output_state: OutputState::new(&globals, &qh),
|
||||
shm,
|
||||
|
||||
should_be_mapped: false,
|
||||
|
||||
first_configure: true,
|
||||
pool,
|
||||
width: 256,
|
||||
height: 256,
|
||||
shift: 0,
|
||||
buffer: None,
|
||||
window,
|
||||
subsurface,
|
||||
commit_only_once: true,
|
||||
loop_signal: event_loop.get_signal(),
|
||||
};
|
||||
|
||||
app.map_toggle();
|
||||
|
||||
event_loop.run(None, &mut app, |_| {}).unwrap();
|
||||
}
|
||||
|
||||
struct App {
|
||||
registry_state: RegistryState,
|
||||
output_state: OutputState,
|
||||
shm: Shm,
|
||||
|
||||
should_be_mapped: bool,
|
||||
|
||||
first_configure: bool,
|
||||
pool: SlotPool,
|
||||
width: u32,
|
||||
height: u32,
|
||||
shift: u32,
|
||||
buffer: Option<Buffer>,
|
||||
window: Window,
|
||||
subsurface: (WlSubsurface, WlSurface),
|
||||
commit_only_once: bool,
|
||||
|
||||
loop_signal: calloop::LoopSignal,
|
||||
}
|
||||
|
||||
impl CompositorHandler for App {
|
||||
fn frame(
|
||||
&mut self,
|
||||
conn: &Connection,
|
||||
qh: &QueueHandle<Self>,
|
||||
_surface: &wl_surface::WlSurface,
|
||||
_time: u32,
|
||||
) {
|
||||
self.draw(conn, qh);
|
||||
}
|
||||
fn surface_enter(&mut self, _: &Connection, _: &QueueHandle<Self>, _: &WlSurface, _: &WlOutput) {}
|
||||
fn surface_leave(&mut self, _: &Connection, _: &QueueHandle<Self>, _: &WlSurface, _: &WlOutput) {}
|
||||
fn scale_factor_changed(&mut self, _: &Connection, _: &QueueHandle<Self>, _: &WlSurface, _: i32) {}
|
||||
fn transform_changed(
|
||||
&mut self,
|
||||
_: &Connection,
|
||||
_: &QueueHandle<Self>,
|
||||
_: &WlSurface,
|
||||
_: wl_output::Transform,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
impl WindowHandler for App {
|
||||
fn request_close(&mut self, _: &Connection, _: &QueueHandle<Self>, _: &Window) {
|
||||
self.loop_signal.stop();
|
||||
}
|
||||
|
||||
fn configure(
|
||||
&mut self,
|
||||
conn: &Connection,
|
||||
qh: &QueueHandle<Self>,
|
||||
_window: &Window,
|
||||
configure: WindowConfigure,
|
||||
_serial: u32,
|
||||
) {
|
||||
if self.first_configure {
|
||||
info!("Window initial configure");
|
||||
} else {
|
||||
info!("Window configure");
|
||||
}
|
||||
|
||||
self.buffer = None;
|
||||
self.width = configure.new_size.0.map(|v| v.get()).unwrap_or(256);
|
||||
self.height = configure.new_size.1.map(|v| v.get()).unwrap_or(256);
|
||||
|
||||
if self.first_configure {
|
||||
self.first_configure = false;
|
||||
self.draw(conn, qh);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl App {
|
||||
fn map_toggle(&mut self) {
|
||||
if self.should_be_mapped {
|
||||
self.unmap()
|
||||
} else {
|
||||
self.map()
|
||||
}
|
||||
}
|
||||
|
||||
fn map(&mut self) {
|
||||
self.first_configure = true;
|
||||
self.should_be_mapped = true;
|
||||
self.window.commit();
|
||||
}
|
||||
|
||||
fn unmap(&mut self) {
|
||||
self.buffer = None;
|
||||
self.first_configure = false;
|
||||
self.should_be_mapped = false;
|
||||
self.window.attach(None, 0, 0);
|
||||
self.window.commit();
|
||||
}
|
||||
|
||||
fn draw(&mut self, _conn: &Connection, qh: &QueueHandle<Self>) {
|
||||
if !self.should_be_mapped {
|
||||
return;
|
||||
}
|
||||
|
||||
if std::mem::take(&mut self.commit_only_once) {
|
||||
test_clients::draw(
|
||||
qh,
|
||||
&self.subsurface.1,
|
||||
&mut self.pool,
|
||||
&mut self.buffer,
|
||||
self.width,
|
||||
self.height,
|
||||
&mut self.shift,
|
||||
);
|
||||
}
|
||||
|
||||
// Test if setting the pos and never calling commit on the subsurface still applies the pos on partent commit
|
||||
self.subsurface.0.set_position(self.shift as i32, 20);
|
||||
|
||||
test_clients::draw(
|
||||
qh,
|
||||
self.window.wl_surface(),
|
||||
&mut self.pool,
|
||||
&mut self.buffer,
|
||||
self.width,
|
||||
self.height,
|
||||
&mut self.shift,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
delegate_compositor!(App);
|
||||
delegate_subcompositor!(App);
|
||||
|
||||
delegate_output!(App);
|
||||
delegate_shm!(App);
|
||||
|
||||
delegate_xdg_shell!(App);
|
||||
delegate_xdg_window!(App);
|
||||
|
||||
delegate_registry!(App);
|
||||
|
||||
impl OutputHandler for App {
|
||||
fn output_state(&mut self) -> &mut OutputState {
|
||||
&mut self.output_state
|
||||
}
|
||||
fn new_output(&mut self, _: &Connection, _: &QueueHandle<Self>, _: WlOutput) {}
|
||||
fn update_output(&mut self, _: &Connection, _: &QueueHandle<Self>, _: WlOutput) {}
|
||||
fn output_destroyed(&mut self, _: &Connection, _: &QueueHandle<Self>, _: WlOutput) {}
|
||||
}
|
||||
|
||||
impl ShmHandler for App {
|
||||
fn shm_state(&mut self) -> &mut Shm {
|
||||
&mut self.shm
|
||||
}
|
||||
}
|
||||
|
||||
impl ProvidesRegistryState for App {
|
||||
fn registry(&mut self) -> &mut RegistryState {
|
||||
&mut self.registry_state
|
||||
}
|
||||
registry_handlers![OutputState,];
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
//! Cycles through initial commit-configure sequence, over and over again (every 1s)
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
use smithay_client_toolkit::reexports::{calloop, client as wayland_client};
|
||||
|
||||
use smithay_client_toolkit::{
|
||||
compositor::{CompositorHandler, CompositorState},
|
||||
delegate_compositor, delegate_output, delegate_registry, delegate_shm, delegate_xdg_shell,
|
||||
delegate_xdg_window,
|
||||
output::{OutputHandler, OutputState},
|
||||
registry::{ProvidesRegistryState, RegistryState},
|
||||
registry_handlers,
|
||||
shell::{
|
||||
WaylandSurface,
|
||||
xdg::{
|
||||
XdgShell,
|
||||
window::{Window, WindowConfigure, WindowDecorations, WindowHandler},
|
||||
},
|
||||
},
|
||||
shm::{
|
||||
Shm, ShmHandler,
|
||||
slot::{Buffer, SlotPool},
|
||||
},
|
||||
};
|
||||
use tracing::info;
|
||||
use wayland_client::{
|
||||
Connection, QueueHandle,
|
||||
protocol::{
|
||||
wl_output::{self, WlOutput},
|
||||
wl_surface::{self, WlSurface},
|
||||
},
|
||||
};
|
||||
|
||||
fn main() {
|
||||
test_clients::init_logging();
|
||||
|
||||
let (mut event_loop, globals, qh) = test_clients::init_connection::<App>();
|
||||
|
||||
let compositor = CompositorState::bind(&globals, &qh).unwrap();
|
||||
let xdg_shell = XdgShell::bind(&globals, &qh).unwrap();
|
||||
|
||||
let surface = compositor.create_surface(&qh);
|
||||
let window = xdg_shell.create_window(surface, WindowDecorations::RequestServer, &qh);
|
||||
|
||||
let shm = Shm::bind(&globals, &qh).unwrap();
|
||||
let pool = SlotPool::new(256 * 256 * 4, &shm).unwrap();
|
||||
|
||||
let mut simple_window = App {
|
||||
registry_state: RegistryState::new(&globals),
|
||||
output_state: OutputState::new(&globals, &qh),
|
||||
shm,
|
||||
|
||||
should_be_mapped: false,
|
||||
|
||||
first_configure: true,
|
||||
pool,
|
||||
width: 256,
|
||||
height: 256,
|
||||
shift: 0,
|
||||
buffer: None,
|
||||
window,
|
||||
loop_signal: event_loop.get_signal(),
|
||||
};
|
||||
|
||||
event_loop
|
||||
.handle()
|
||||
.insert_source(calloop::timer::Timer::immediate(), |_, _, app| {
|
||||
app.map_toggle();
|
||||
calloop::timer::TimeoutAction::ToDuration(Duration::from_secs(1))
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
event_loop.run(None, &mut simple_window, |_| {}).unwrap();
|
||||
}
|
||||
|
||||
struct App {
|
||||
registry_state: RegistryState,
|
||||
output_state: OutputState,
|
||||
shm: Shm,
|
||||
|
||||
should_be_mapped: bool,
|
||||
|
||||
first_configure: bool,
|
||||
pool: SlotPool,
|
||||
width: u32,
|
||||
height: u32,
|
||||
shift: u32,
|
||||
buffer: Option<Buffer>,
|
||||
window: Window,
|
||||
loop_signal: calloop::LoopSignal,
|
||||
}
|
||||
|
||||
impl CompositorHandler for App {
|
||||
fn frame(
|
||||
&mut self,
|
||||
conn: &Connection,
|
||||
qh: &QueueHandle<Self>,
|
||||
_surface: &wl_surface::WlSurface,
|
||||
_time: u32,
|
||||
) {
|
||||
self.draw(conn, qh);
|
||||
}
|
||||
fn surface_enter(&mut self, _: &Connection, _: &QueueHandle<Self>, _: &WlSurface, _: &WlOutput) {}
|
||||
fn surface_leave(&mut self, _: &Connection, _: &QueueHandle<Self>, _: &WlSurface, _: &WlOutput) {}
|
||||
fn scale_factor_changed(&mut self, _: &Connection, _: &QueueHandle<Self>, _: &WlSurface, _: i32) {}
|
||||
fn transform_changed(
|
||||
&mut self,
|
||||
_: &Connection,
|
||||
_: &QueueHandle<Self>,
|
||||
_: &WlSurface,
|
||||
_: wl_output::Transform,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
impl WindowHandler for App {
|
||||
fn request_close(&mut self, _: &Connection, _: &QueueHandle<Self>, _: &Window) {
|
||||
self.loop_signal.stop();
|
||||
}
|
||||
|
||||
fn configure(
|
||||
&mut self,
|
||||
conn: &Connection,
|
||||
qh: &QueueHandle<Self>,
|
||||
_window: &Window,
|
||||
configure: WindowConfigure,
|
||||
_serial: u32,
|
||||
) {
|
||||
if self.first_configure {
|
||||
info!("Window initial configure");
|
||||
} else {
|
||||
info!("Window configure");
|
||||
}
|
||||
|
||||
self.buffer = None;
|
||||
self.width = configure.new_size.0.map(|v| v.get()).unwrap_or(256);
|
||||
self.height = configure.new_size.1.map(|v| v.get()).unwrap_or(256);
|
||||
|
||||
if self.first_configure {
|
||||
self.first_configure = false;
|
||||
self.draw(conn, qh);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl App {
|
||||
fn map_toggle(&mut self) {
|
||||
if self.should_be_mapped {
|
||||
self.unmap()
|
||||
} else {
|
||||
self.map()
|
||||
}
|
||||
}
|
||||
|
||||
fn map(&mut self) {
|
||||
self.first_configure = true;
|
||||
self.should_be_mapped = true;
|
||||
self.window.commit();
|
||||
}
|
||||
|
||||
fn unmap(&mut self) {
|
||||
self.buffer = None;
|
||||
self.first_configure = false;
|
||||
self.should_be_mapped = false;
|
||||
self.window.attach(None, 0, 0);
|
||||
self.window.commit();
|
||||
}
|
||||
|
||||
fn draw(&mut self, _conn: &Connection, qh: &QueueHandle<Self>) {
|
||||
if !self.should_be_mapped {
|
||||
return;
|
||||
}
|
||||
|
||||
test_clients::draw(
|
||||
qh,
|
||||
self.window.wl_surface(),
|
||||
&mut self.pool,
|
||||
&mut self.buffer,
|
||||
self.width,
|
||||
self.height,
|
||||
&mut self.shift,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
delegate_compositor!(App);
|
||||
delegate_output!(App);
|
||||
delegate_shm!(App);
|
||||
|
||||
delegate_xdg_shell!(App);
|
||||
delegate_xdg_window!(App);
|
||||
|
||||
delegate_registry!(App);
|
||||
|
||||
impl OutputHandler for App {
|
||||
fn output_state(&mut self) -> &mut OutputState {
|
||||
&mut self.output_state
|
||||
}
|
||||
fn new_output(&mut self, _: &Connection, _: &QueueHandle<Self>, _: WlOutput) {}
|
||||
fn update_output(&mut self, _: &Connection, _: &QueueHandle<Self>, _: WlOutput) {}
|
||||
fn output_destroyed(&mut self, _: &Connection, _: &QueueHandle<Self>, _: WlOutput) {}
|
||||
}
|
||||
|
||||
impl ShmHandler for App {
|
||||
fn shm_state(&mut self) -> &mut Shm {
|
||||
&mut self.shm
|
||||
}
|
||||
}
|
||||
|
||||
impl ProvidesRegistryState for App {
|
||||
fn registry(&mut self) -> &mut RegistryState {
|
||||
&mut self.registry_state
|
||||
}
|
||||
registry_handlers![OutputState,];
|
||||
}
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
use smithay_client_toolkit::{
|
||||
reexports::{
|
||||
calloop,
|
||||
calloop_wayland_source::WaylandSource,
|
||||
client::{
|
||||
self as wayland_client,
|
||||
globals::GlobalList,
|
||||
protocol::{wl_callback::WlCallback, wl_shm, wl_surface::WlSurface},
|
||||
},
|
||||
},
|
||||
shm::slot::{Buffer, SlotPool},
|
||||
};
|
||||
|
||||
use calloop::EventLoop;
|
||||
use wayland_client::{
|
||||
Connection, Dispatch, QueueHandle, globals::GlobalListContents, globals::registry_queue_init,
|
||||
protocol::wl_registry::WlRegistry,
|
||||
};
|
||||
|
||||
pub fn init_logging() {
|
||||
if let Ok(env_filter) = tracing_subscriber::EnvFilter::try_from_default_env() {
|
||||
tracing_subscriber::fmt()
|
||||
.compact()
|
||||
.with_env_filter(env_filter)
|
||||
.init();
|
||||
} else {
|
||||
tracing_subscriber::fmt().compact().init();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init_connection<APP>() -> (EventLoop<'static, APP>, GlobalList, QueueHandle<APP>)
|
||||
where
|
||||
APP: Dispatch<WlRegistry, GlobalListContents> + 'static,
|
||||
{
|
||||
let conn = Connection::connect_to_env().unwrap();
|
||||
|
||||
let (globals, event_queue) = registry_queue_init(&conn).unwrap();
|
||||
let qh = event_queue.handle();
|
||||
let event_loop: EventLoop<APP> = EventLoop::try_new().unwrap();
|
||||
let loop_handle = event_loop.handle();
|
||||
WaylandSource::new(conn.clone(), event_queue)
|
||||
.insert(loop_handle)
|
||||
.unwrap();
|
||||
|
||||
(event_loop, globals, qh)
|
||||
}
|
||||
|
||||
pub fn fill_with_gradient_bytes(canvas: &mut [u8], shift: u32, width: u32, height: u32) {
|
||||
canvas.chunks_exact_mut(4).enumerate().for_each(|(index, chunk)| {
|
||||
let x = ((index + shift as usize) % width as usize) as u32;
|
||||
let y = (index / width as usize) as u32;
|
||||
|
||||
let a = 0xFF;
|
||||
let r = u32::min(((width - x) * 0xFF) / width, ((height - y) * 0xFF) / height);
|
||||
let g = u32::min((x * 0xFF) / width, ((height - y) * 0xFF) / height);
|
||||
let b = u32::min(((width - x) * 0xFF) / width, (y * 0xFF) / height);
|
||||
let color = (a << 24) + (r << 16) + (g << 8) + b;
|
||||
|
||||
let array: &mut [u8; 4] = chunk.try_into().unwrap();
|
||||
*array = color.to_le_bytes();
|
||||
});
|
||||
}
|
||||
|
||||
pub fn draw<D>(
|
||||
qh: &QueueHandle<D>,
|
||||
wl_surface: &WlSurface,
|
||||
pool: &mut SlotPool,
|
||||
buffer: &mut Option<Buffer>,
|
||||
width: u32,
|
||||
height: u32,
|
||||
shift: &mut u32,
|
||||
) where
|
||||
D: 'static,
|
||||
D: Dispatch<WlCallback, WlSurface>,
|
||||
{
|
||||
let stride = width as i32 * 4;
|
||||
|
||||
let buffer = buffer.get_or_insert_with(|| {
|
||||
pool.create_buffer(width as i32, height as i32, stride, wl_shm::Format::Argb8888)
|
||||
.unwrap()
|
||||
.0
|
||||
});
|
||||
|
||||
let canvas = match pool.canvas(buffer) {
|
||||
Some(canvas) => canvas,
|
||||
None => {
|
||||
// This should be rare, but if the compositor has not released the previous
|
||||
// buffer, we need double-buffering.
|
||||
let (second_buffer, canvas) = pool
|
||||
.create_buffer(width as i32, height as i32, stride, wl_shm::Format::Argb8888)
|
||||
.unwrap();
|
||||
*buffer = second_buffer;
|
||||
canvas
|
||||
}
|
||||
};
|
||||
|
||||
// Draw to the window:
|
||||
fill_with_gradient_bytes(canvas, *shift, width, height);
|
||||
*shift = (*shift + 1) % width;
|
||||
|
||||
wl_surface.damage_buffer(0, 0, width as i32, height as i32);
|
||||
|
||||
wl_surface.frame(qh, wl_surface.clone());
|
||||
|
||||
buffer.attach_to(wl_surface).expect("buffer attach");
|
||||
wl_surface.commit();
|
||||
}
|
||||
Reference in new issue
Block a user