Undone the previous change, used and official example and spli the coe to multiple files
This commit is contained in:
parent
3e20807d13
commit
f5c7071d89
17 changed files with 535 additions and 2561 deletions
1394
Cargo.lock
generated
1394
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
11
Cargo.toml
11
Cargo.toml
|
|
@ -4,16 +4,5 @@ version = "0.1.0"
|
|||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
egui = "0.33.2"
|
||||
egui-wgpu = "0.33.2"
|
||||
env_logger = "0.11.8"
|
||||
log = "0.4.28"
|
||||
pollster = "0.4.0"
|
||||
raw-window-handle = "0.6.2"
|
||||
smithay-client-toolkit = "0.20.0"
|
||||
smithay-clipboard = "0.7"
|
||||
wayland-backend = { version = "0.3.11", features = ["client_system"] }
|
||||
wayland-client = "0.31.11"
|
||||
wayland-protocols = "0.32.9"
|
||||
wayland-protocols-wlr = "0.3.9"
|
||||
wgpu = "27.0.1"
|
||||
|
|
|
|||
21
flake.nix
21
flake.nix
|
|
@ -21,29 +21,10 @@
|
|||
gnumake
|
||||
rust-bin.stable.latest.default
|
||||
evcxr
|
||||
pkg-config
|
||||
libxkbcommon
|
||||
wayland
|
||||
wayland-protocols
|
||||
wayland-scanner
|
||||
|
||||
# Graphics libraries for wgpu
|
||||
vulkan-loader
|
||||
vulkan-validation-layers
|
||||
libGL
|
||||
libxkbcommon
|
||||
rusty-man
|
||||
];
|
||||
|
||||
# Critical: Set library paths for runtime
|
||||
LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath [
|
||||
pkgs.wayland
|
||||
pkgs.libxkbcommon
|
||||
pkgs.vulkan-loader
|
||||
pkgs.libGL
|
||||
];
|
||||
|
||||
# Ensure Vulkan ICD is findable
|
||||
VK_LAYER_PATH = "${pkgs.vulkan-validation-layers}/share/vulkan/explicit_layer.d";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
|||
56
src/compositor_handler.rs
Normal file
56
src/compositor_handler.rs
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
use smithay_client_toolkit::compositor::CompositorHandler;
|
||||
use wayland_client::{Connection, QueueHandle, protocol::wl_output, protocol::wl_surface};
|
||||
|
||||
use crate::simple_layer::SimpleLayer;
|
||||
|
||||
impl CompositorHandler for SimpleLayer {
|
||||
fn scale_factor_changed(
|
||||
&mut self,
|
||||
_conn: &Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_surface: &wl_surface::WlSurface,
|
||||
_new_factor: i32,
|
||||
) {
|
||||
// Not needed for this example.
|
||||
}
|
||||
|
||||
fn transform_changed(
|
||||
&mut self,
|
||||
_conn: &Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_surface: &wl_surface::WlSurface,
|
||||
_new_transform: wl_output::Transform,
|
||||
) {
|
||||
// Not needed for this example.
|
||||
}
|
||||
|
||||
fn frame(
|
||||
&mut self,
|
||||
_conn: &Connection,
|
||||
qh: &QueueHandle<Self>,
|
||||
_surface: &wl_surface::WlSurface,
|
||||
_time: u32,
|
||||
) {
|
||||
self.draw(qh);
|
||||
}
|
||||
|
||||
fn surface_enter(
|
||||
&mut self,
|
||||
_conn: &Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_surface: &wl_surface::WlSurface,
|
||||
_output: &wl_output::WlOutput,
|
||||
) {
|
||||
// Not needed for this example.
|
||||
}
|
||||
|
||||
fn surface_leave(
|
||||
&mut self,
|
||||
_conn: &Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_surface: &wl_surface::WlSurface,
|
||||
_output: &wl_output::WlOutput,
|
||||
) {
|
||||
// Not needed for this example.
|
||||
}
|
||||
}
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
use egui::{CentralPanel, Context};
|
||||
pub struct EguiApp {
|
||||
counter: i32,
|
||||
text: String,
|
||||
}
|
||||
|
||||
impl EguiApp {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
counter: 0,
|
||||
text: String::from("Hello from EGUI!"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ui(&mut self, ctx: &Context) {
|
||||
CentralPanel::default().show(ctx, |ui| {
|
||||
ui.heading("Egui WGPU / Smithay example");
|
||||
|
||||
ui.separator();
|
||||
|
||||
ui.label(format!("Counter: {}", self.counter));
|
||||
if ui.button("Increment").clicked() {
|
||||
self.counter += 1;
|
||||
}
|
||||
if ui.button("Decrement").clicked() {
|
||||
self.counter -= 1;
|
||||
}
|
||||
|
||||
ui.separator();
|
||||
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("Text input:");
|
||||
ui.text_edit_singleline(&mut self.text);
|
||||
});
|
||||
|
||||
ui.label(format!("You wrote: {}", self.text));
|
||||
|
||||
ui.separator();
|
||||
|
||||
ui.label("This is a simple EGUI app running on Wayland via Smithay toolkit!");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for EguiApp {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,109 +0,0 @@
|
|||
// Original for Winit:
|
||||
// https://github.com/kaphula/winit-egui-wgpu-template/blob/master/src/egui_tools.rs
|
||||
|
||||
use egui::Context;
|
||||
use egui_wgpu::wgpu::{CommandEncoder, Device, Queue, StoreOp, TextureFormat, TextureView};
|
||||
use egui_wgpu::{Renderer, RendererOptions, ScreenDescriptor, wgpu};
|
||||
|
||||
pub struct EguiRenderer {
|
||||
context: Context,
|
||||
renderer: Renderer,
|
||||
frame_started: bool,
|
||||
}
|
||||
|
||||
impl EguiRenderer {
|
||||
pub fn context(&self) -> &Context {
|
||||
&self.context
|
||||
}
|
||||
|
||||
pub fn context_mut(&mut self) -> &mut Context {
|
||||
&mut self.context
|
||||
}
|
||||
|
||||
pub fn new(
|
||||
device: &Device,
|
||||
output_color_format: TextureFormat,
|
||||
output_depth_format: Option<TextureFormat>,
|
||||
msaa_samples: u32,
|
||||
) -> EguiRenderer {
|
||||
let egui_context = Context::default();
|
||||
|
||||
let egui_renderer = Renderer::new(
|
||||
device,
|
||||
output_color_format,
|
||||
RendererOptions {
|
||||
msaa_samples,
|
||||
depth_stencil_format: output_depth_format,
|
||||
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
|
||||
EguiRenderer {
|
||||
context: egui_context,
|
||||
renderer: egui_renderer,
|
||||
frame_started: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ppp(&mut self, v: f32) {
|
||||
self.context.set_pixels_per_point(v);
|
||||
}
|
||||
|
||||
pub fn begin_frame(&mut self, raw_input: egui::RawInput) {
|
||||
self.context.begin_pass(raw_input);
|
||||
self.frame_started = true;
|
||||
}
|
||||
|
||||
pub fn end_frame_and_draw(
|
||||
&mut self,
|
||||
device: &Device,
|
||||
queue: &Queue,
|
||||
encoder: &mut CommandEncoder,
|
||||
window_surface_view: &TextureView,
|
||||
screen_descriptor: ScreenDescriptor,
|
||||
) -> egui::PlatformOutput {
|
||||
if !self.frame_started {
|
||||
panic!("begin_frame must be called before end_frame_and_draw can be called!");
|
||||
}
|
||||
|
||||
self.ppp(screen_descriptor.pixels_per_point);
|
||||
|
||||
let full_output = self.context.end_pass();
|
||||
|
||||
let tris = self
|
||||
.context
|
||||
.tessellate(full_output.shapes, self.context.pixels_per_point());
|
||||
for (id, image_delta) in &full_output.textures_delta.set {
|
||||
self.renderer
|
||||
.update_texture(device, queue, *id, image_delta);
|
||||
}
|
||||
self.renderer
|
||||
.update_buffers(device, queue, encoder, &tris, &screen_descriptor);
|
||||
let rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||
view: window_surface_view,
|
||||
resolve_target: None,
|
||||
depth_slice: None,
|
||||
ops: egui_wgpu::wgpu::Operations {
|
||||
load: egui_wgpu::wgpu::LoadOp::Load,
|
||||
store: StoreOp::Store,
|
||||
},
|
||||
})],
|
||||
depth_stencil_attachment: None,
|
||||
timestamp_writes: None,
|
||||
label: Some("egui main render pass"),
|
||||
occlusion_query_set: None,
|
||||
});
|
||||
|
||||
self.renderer
|
||||
.render(&mut rpass.forget_lifetime(), &tris, &screen_descriptor);
|
||||
for x in &full_output.textures_delta.free {
|
||||
self.renderer.free_texture(x)
|
||||
}
|
||||
|
||||
self.frame_started = false;
|
||||
|
||||
full_output.platform_output
|
||||
}
|
||||
}
|
||||
|
|
@ -1,378 +0,0 @@
|
|||
use egui::{Event, Key, Modifiers, PointerButton, Pos2, RawInput};
|
||||
use log::trace;
|
||||
use smithay_client_toolkit::seat::keyboard::{KeyEvent, Keysym, Modifiers as WaylandModifiers};
|
||||
use smithay_client_toolkit::seat::pointer::{PointerEvent, PointerEventKind};
|
||||
use smithay_clipboard::Clipboard;
|
||||
use std::time::Instant;
|
||||
|
||||
pub struct InputState {
|
||||
modifiers: Modifiers,
|
||||
pointer_pos: Pos2,
|
||||
events: Vec<Event>,
|
||||
screen_width: u32,
|
||||
screen_height: u32,
|
||||
start_time: Instant,
|
||||
clipboard: Clipboard,
|
||||
last_key_utf8: Option<String>,
|
||||
}
|
||||
|
||||
impl InputState {
|
||||
pub fn new(clipboard: Clipboard) -> Self {
|
||||
Self {
|
||||
modifiers: Modifiers::default(),
|
||||
pointer_pos: Pos2::ZERO,
|
||||
events: Vec::new(),
|
||||
screen_width: 256,
|
||||
screen_height: 256,
|
||||
start_time: Instant::now(),
|
||||
// pressed_keys: std::collections::HashSet::new(),
|
||||
clipboard,
|
||||
last_key_utf8: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_screen_size(&mut self, width: u32, height: u32) {
|
||||
self.screen_width = width;
|
||||
self.screen_height = height;
|
||||
}
|
||||
|
||||
pub fn handle_pointer_event(&mut self, event: &PointerEvent) {
|
||||
trace!("[INPUT] Pointer event: {:?}", event.kind);
|
||||
match &event.kind {
|
||||
PointerEventKind::Enter { .. } => {
|
||||
trace!("[INPUT] Pointer entered surface");
|
||||
// Pointer entered the surface
|
||||
}
|
||||
PointerEventKind::Leave { .. } => {
|
||||
trace!("[INPUT] Pointer left surface");
|
||||
// Pointer left the surface
|
||||
self.events.push(Event::PointerGone);
|
||||
}
|
||||
PointerEventKind::Motion { .. } => {
|
||||
let (x, y) = event.position;
|
||||
self.pointer_pos = Pos2::new(x as f32, y as f32);
|
||||
trace!("[INPUT] Pointer moved to: ({}, {})", x, y);
|
||||
self.events.push(Event::PointerMoved(self.pointer_pos));
|
||||
}
|
||||
PointerEventKind::Press { button, .. } => {
|
||||
trace!("[INPUT] Pointer button pressed: {}", button);
|
||||
if let Some(egui_button) = wayland_button_to_egui(*button) {
|
||||
trace!("[INPUT] Mapped to EGUI button: {:?}", egui_button);
|
||||
self.events.push(Event::PointerButton {
|
||||
pos: self.pointer_pos,
|
||||
button: egui_button,
|
||||
pressed: true,
|
||||
modifiers: self.modifiers,
|
||||
});
|
||||
}
|
||||
}
|
||||
PointerEventKind::Release { button, .. } => {
|
||||
trace!("[INPUT] Pointer button released: {}", button);
|
||||
if let Some(egui_button) = wayland_button_to_egui(*button) {
|
||||
self.events.push(Event::PointerButton {
|
||||
pos: self.pointer_pos,
|
||||
button: egui_button,
|
||||
pressed: false,
|
||||
modifiers: self.modifiers,
|
||||
});
|
||||
}
|
||||
}
|
||||
PointerEventKind::Axis {
|
||||
horizontal,
|
||||
vertical,
|
||||
..
|
||||
} => {
|
||||
// Handle scroll events
|
||||
let scroll_delta = egui::vec2(
|
||||
horizontal.discrete as f32 * 10.0,
|
||||
vertical.discrete as f32 * 10.0,
|
||||
);
|
||||
|
||||
if scroll_delta != egui::Vec2::ZERO {
|
||||
self.events.push(Event::MouseWheel {
|
||||
unit: egui::MouseWheelUnit::Line,
|
||||
delta: scroll_delta,
|
||||
modifiers: self.modifiers,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_keyboard_event(&mut self, event: &KeyEvent, pressed: bool, is_repeat: bool) {
|
||||
trace!(
|
||||
"[INPUT] Keyboard event - keysym: {:?}, raw_code: {}, pressed: {}, repeat: {}, utf8: {:?}",
|
||||
event.keysym.raw(),
|
||||
event.raw_code,
|
||||
pressed,
|
||||
is_repeat,
|
||||
event.utf8
|
||||
);
|
||||
|
||||
// Check for clipboard operations BEFORE general key handling
|
||||
if pressed && !is_repeat && self.modifiers.ctrl {
|
||||
// XKB key constants
|
||||
const XKB_KEY_c: u32 = 0x0063;
|
||||
const XKB_KEY_x: u32 = 0x0078;
|
||||
const XKB_KEY_v: u32 = 0x0076;
|
||||
|
||||
match event.keysym.raw() {
|
||||
XKB_KEY_c => self.events.push(Event::Copy),
|
||||
XKB_KEY_x => self.events.push(Event::Cut),
|
||||
XKB_KEY_v => self
|
||||
.events
|
||||
.push(Event::Paste(self.clipboard.load().unwrap_or_default())),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(key) = keysym_to_egui_key(event.keysym) {
|
||||
trace!(
|
||||
"[INPUT] Mapped to EGUI key: {:?}, repeat: {}",
|
||||
key, is_repeat
|
||||
);
|
||||
// Note: Egui expects repeats to have pressed=true
|
||||
self.events.push(Event::Key {
|
||||
key,
|
||||
physical_key: None,
|
||||
pressed,
|
||||
repeat: is_repeat,
|
||||
modifiers: self.modifiers,
|
||||
});
|
||||
if pressed || is_repeat {
|
||||
let text = event.utf8.clone().or(self.last_key_utf8.clone());
|
||||
if let Some(text) = text {
|
||||
if !text.chars().any(|c| c.is_control()) {
|
||||
trace!("[INPUT] Text input: '{}'", text);
|
||||
self.events.push(Event::Text(text.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
trace!(
|
||||
"[INPUT] No EGUI key mapping for keysym: {:?}",
|
||||
event.keysym.raw()
|
||||
);
|
||||
}
|
||||
|
||||
if event.utf8.is_some() {
|
||||
self.last_key_utf8 = event.utf8.clone();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_modifiers(&mut self, wayland_mods: &WaylandModifiers) {
|
||||
trace!(
|
||||
"[INPUT] Modifiers updated - ctrl: {}, shift: {}, alt: {}",
|
||||
wayland_mods.ctrl, wayland_mods.shift, wayland_mods.alt
|
||||
);
|
||||
self.modifiers = Modifiers {
|
||||
alt: wayland_mods.alt,
|
||||
ctrl: wayland_mods.ctrl,
|
||||
shift: wayland_mods.shift,
|
||||
mac_cmd: false, // Not applicable on Linux/Wayland
|
||||
command: wayland_mods.ctrl, // On non-Mac, command is ctrl
|
||||
};
|
||||
}
|
||||
|
||||
/// Get current modifiers state
|
||||
// pub fn get_modifiers(&self) -> &Modifiers {
|
||||
// &self.modifiers
|
||||
// }
|
||||
|
||||
pub fn take_raw_input(&mut self) -> RawInput {
|
||||
let events = std::mem::take(&mut self.events);
|
||||
trace!("[INPUT] Taking raw input with {} events", events.len());
|
||||
if !events.is_empty() {
|
||||
trace!("[INPUT] Events: {:?}", events);
|
||||
}
|
||||
|
||||
RawInput {
|
||||
screen_rect: Some(egui::Rect::from_min_size(
|
||||
Pos2::ZERO,
|
||||
egui::vec2(self.screen_width as f32, self.screen_height as f32),
|
||||
)),
|
||||
time: Some(self.start_time.elapsed().as_secs_f64()),
|
||||
predicted_dt: 1.0 / 60.0, // Assume 60 FPS
|
||||
modifiers: self.modifiers,
|
||||
events,
|
||||
hovered_files: Vec::new(),
|
||||
dropped_files: Vec::new(),
|
||||
focused: true, // Assume focused when we have the input
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_output_command(&mut self, output: &egui::OutputCommand) {
|
||||
match output {
|
||||
egui::OutputCommand::CopyText(text) => {
|
||||
self.clipboard.store(text.clone());
|
||||
trace!("[INPUT] Copied text to clipboard: {:?}", text);
|
||||
}
|
||||
egui::OutputCommand::CopyImage(_image) => {
|
||||
// Handle image copy if needed
|
||||
trace!("[INPUT] CopyImage command received (not implemented)");
|
||||
// TODO: Implement image copying to clipboard if required
|
||||
}
|
||||
egui::OutputCommand::OpenUrl(url) => {
|
||||
trace!("[INPUT] OpenUrl command received: {}", url.url);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn wayland_button_to_egui(button: u32) -> Option<PointerButton> {
|
||||
// Linux button codes (from linux/input-event-codes.h)
|
||||
match button {
|
||||
0x110 => Some(PointerButton::Primary), // BTN_LEFT
|
||||
0x111 => Some(PointerButton::Secondary), // BTN_RIGHT
|
||||
0x112 => Some(PointerButton::Middle), // BTN_MIDDLE
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn keysym_to_egui_key(keysym: Keysym) -> Option<Key> {
|
||||
// XKB key constants from xkbcommon
|
||||
const XKB_KEY_Escape: u32 = 0xff1b;
|
||||
const XKB_KEY_Return: u32 = 0xff0d;
|
||||
const XKB_KEY_KP_Enter: u32 = 0xff8d;
|
||||
const XKB_KEY_Tab: u32 = 0xff09;
|
||||
const XKB_KEY_BackSpace: u32 = 0xff08;
|
||||
const XKB_KEY_Insert: u32 = 0xff63;
|
||||
const XKB_KEY_Delete: u32 = 0xffff;
|
||||
const XKB_KEY_Home: u32 = 0xff50;
|
||||
const XKB_KEY_End: u32 = 0xff57;
|
||||
const XKB_KEY_Page_Up: u32 = 0xff55;
|
||||
const XKB_KEY_Page_Down: u32 = 0xff56;
|
||||
const XKB_KEY_Left: u32 = 0xff51;
|
||||
const XKB_KEY_Right: u32 = 0xff53;
|
||||
const XKB_KEY_Up: u32 = 0xff52;
|
||||
const XKB_KEY_Down: u32 = 0xff54;
|
||||
const XKB_KEY_space: u32 = 0x0020;
|
||||
|
||||
const XKB_KEY_a: u32 = 0x0061;
|
||||
const XKB_KEY_b: u32 = 0x0062;
|
||||
const XKB_KEY_c: u32 = 0x0063;
|
||||
const XKB_KEY_d: u32 = 0x0064;
|
||||
const XKB_KEY_e: u32 = 0x0065;
|
||||
const XKB_KEY_f: u32 = 0x0066;
|
||||
const XKB_KEY_g: u32 = 0x0067;
|
||||
const XKB_KEY_h: u32 = 0x0068;
|
||||
const XKB_KEY_i: u32 = 0x0069;
|
||||
const XKB_KEY_j: u32 = 0x006a;
|
||||
const XKB_KEY_k: u32 = 0x006b;
|
||||
const XKB_KEY_l: u32 = 0x006c;
|
||||
const XKB_KEY_m: u32 = 0x006d;
|
||||
const XKB_KEY_n: u32 = 0x006e;
|
||||
const XKB_KEY_o: u32 = 0x006f;
|
||||
const XKB_KEY_p: u32 = 0x0070;
|
||||
const XKB_KEY_q: u32 = 0x0071;
|
||||
const XKB_KEY_r: u32 = 0x0072;
|
||||
const XKB_KEY_s: u32 = 0x0073;
|
||||
const XKB_KEY_t: u32 = 0x0074;
|
||||
const XKB_KEY_u: u32 = 0x0075;
|
||||
const XKB_KEY_v: u32 = 0x0076;
|
||||
const XKB_KEY_w: u32 = 0x0077;
|
||||
const XKB_KEY_x: u32 = 0x0078;
|
||||
const XKB_KEY_y: u32 = 0x0079;
|
||||
const XKB_KEY_z: u32 = 0x007a;
|
||||
|
||||
const XKB_KEY_0: u32 = 0x0030;
|
||||
const XKB_KEY_1: u32 = 0x0031;
|
||||
const XKB_KEY_2: u32 = 0x0032;
|
||||
const XKB_KEY_3: u32 = 0x0033;
|
||||
const XKB_KEY_4: u32 = 0x0034;
|
||||
const XKB_KEY_5: u32 = 0x0035;
|
||||
const XKB_KEY_6: u32 = 0x0036;
|
||||
const XKB_KEY_7: u32 = 0x0037;
|
||||
const XKB_KEY_8: u32 = 0x0038;
|
||||
const XKB_KEY_9: u32 = 0x0039;
|
||||
|
||||
const XKB_KEY_F1: u32 = 0xffbe;
|
||||
const XKB_KEY_F2: u32 = 0xffbf;
|
||||
const XKB_KEY_F3: u32 = 0xffc0;
|
||||
const XKB_KEY_F4: u32 = 0xffc1;
|
||||
const XKB_KEY_F5: u32 = 0xffc2;
|
||||
const XKB_KEY_F6: u32 = 0xffc3;
|
||||
const XKB_KEY_F7: u32 = 0xffc4;
|
||||
const XKB_KEY_F8: u32 = 0xffc5;
|
||||
const XKB_KEY_F9: u32 = 0xffc6;
|
||||
const XKB_KEY_F10: u32 = 0xffc7;
|
||||
const XKB_KEY_F11: u32 = 0xffc8;
|
||||
const XKB_KEY_F12: u32 = 0xffc9;
|
||||
|
||||
Some(match keysym.raw() {
|
||||
XKB_KEY_Escape => Key::Escape,
|
||||
XKB_KEY_Return | XKB_KEY_KP_Enter => Key::Enter,
|
||||
XKB_KEY_Tab => Key::Tab,
|
||||
XKB_KEY_BackSpace => Key::Backspace,
|
||||
XKB_KEY_Insert => Key::Insert,
|
||||
XKB_KEY_Delete => Key::Delete,
|
||||
XKB_KEY_Home => Key::Home,
|
||||
XKB_KEY_End => Key::End,
|
||||
XKB_KEY_Page_Up => Key::PageUp,
|
||||
XKB_KEY_Page_Down => Key::PageDown,
|
||||
XKB_KEY_Left => Key::ArrowLeft,
|
||||
XKB_KEY_Right => Key::ArrowRight,
|
||||
XKB_KEY_Up => Key::ArrowUp,
|
||||
XKB_KEY_Down => Key::ArrowDown,
|
||||
|
||||
XKB_KEY_space => Key::Space,
|
||||
|
||||
// Letters (lowercase)
|
||||
XKB_KEY_a => Key::A,
|
||||
XKB_KEY_b => Key::B,
|
||||
XKB_KEY_c => Key::C,
|
||||
XKB_KEY_d => Key::D,
|
||||
XKB_KEY_e => Key::E,
|
||||
XKB_KEY_f => Key::F,
|
||||
XKB_KEY_g => Key::G,
|
||||
XKB_KEY_h => Key::H,
|
||||
XKB_KEY_i => Key::I,
|
||||
XKB_KEY_j => Key::J,
|
||||
XKB_KEY_k => Key::K,
|
||||
XKB_KEY_l => Key::L,
|
||||
XKB_KEY_m => Key::M,
|
||||
XKB_KEY_n => Key::N,
|
||||
XKB_KEY_o => Key::O,
|
||||
XKB_KEY_p => Key::P,
|
||||
XKB_KEY_q => Key::Q,
|
||||
XKB_KEY_r => Key::R,
|
||||
XKB_KEY_s => Key::S,
|
||||
XKB_KEY_t => Key::T,
|
||||
XKB_KEY_u => Key::U,
|
||||
XKB_KEY_v => Key::V,
|
||||
XKB_KEY_w => Key::W,
|
||||
XKB_KEY_x => Key::X,
|
||||
XKB_KEY_y => Key::Y,
|
||||
XKB_KEY_z => Key::Z,
|
||||
|
||||
// Numbers
|
||||
XKB_KEY_0 => Key::Num0,
|
||||
XKB_KEY_1 => Key::Num1,
|
||||
XKB_KEY_2 => Key::Num2,
|
||||
XKB_KEY_3 => Key::Num3,
|
||||
XKB_KEY_4 => Key::Num4,
|
||||
XKB_KEY_5 => Key::Num5,
|
||||
XKB_KEY_6 => Key::Num6,
|
||||
XKB_KEY_7 => Key::Num7,
|
||||
XKB_KEY_8 => Key::Num8,
|
||||
XKB_KEY_9 => Key::Num9,
|
||||
|
||||
// Function keys
|
||||
XKB_KEY_F1 => Key::F1,
|
||||
XKB_KEY_F2 => Key::F2,
|
||||
XKB_KEY_F3 => Key::F3,
|
||||
XKB_KEY_F4 => Key::F4,
|
||||
XKB_KEY_F5 => Key::F5,
|
||||
XKB_KEY_F6 => Key::F6,
|
||||
XKB_KEY_F7 => Key::F7,
|
||||
XKB_KEY_F8 => Key::F8,
|
||||
XKB_KEY_F9 => Key::F9,
|
||||
XKB_KEY_F10 => Key::F10,
|
||||
XKB_KEY_F11 => Key::F11,
|
||||
XKB_KEY_F12 => Key::F12,
|
||||
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
||||
92
src/keyboard_handler.rs
Normal file
92
src/keyboard_handler.rs
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
use smithay_client_toolkit::{
|
||||
seat::keyboard::{KeyEvent, KeyboardHandler, Keysym, Modifiers, RawModifiers},
|
||||
shell::WaylandSurface,
|
||||
};
|
||||
use wayland_client::{
|
||||
Connection, QueueHandle,
|
||||
protocol::{wl_keyboard, wl_surface},
|
||||
};
|
||||
|
||||
use crate::simple_layer::SimpleLayer;
|
||||
|
||||
impl KeyboardHandler for SimpleLayer {
|
||||
fn enter(
|
||||
&mut self,
|
||||
_: &Connection,
|
||||
_: &QueueHandle<Self>,
|
||||
_: &wl_keyboard::WlKeyboard,
|
||||
surface: &wl_surface::WlSurface,
|
||||
_: u32,
|
||||
_: &[u32],
|
||||
keysyms: &[Keysym],
|
||||
) {
|
||||
if self.layer.wl_surface() == surface {
|
||||
println!("Keyboard focus on window with pressed syms: {keysyms:?}");
|
||||
self.keyboard_focus = true;
|
||||
}
|
||||
}
|
||||
|
||||
fn leave(
|
||||
&mut self,
|
||||
_: &Connection,
|
||||
_: &QueueHandle<Self>,
|
||||
_: &wl_keyboard::WlKeyboard,
|
||||
surface: &wl_surface::WlSurface,
|
||||
_: u32,
|
||||
) {
|
||||
if self.layer.wl_surface() == surface {
|
||||
println!("Release keyboard focus on window");
|
||||
self.keyboard_focus = false;
|
||||
}
|
||||
}
|
||||
|
||||
fn press_key(
|
||||
&mut self,
|
||||
_conn: &Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_: &wl_keyboard::WlKeyboard,
|
||||
_: u32,
|
||||
event: KeyEvent,
|
||||
) {
|
||||
println!("Key press: {event:?}");
|
||||
// press 'esc' to exit
|
||||
if event.keysym == Keysym::Escape {
|
||||
self.exit = true;
|
||||
}
|
||||
}
|
||||
|
||||
fn repeat_key(
|
||||
&mut self,
|
||||
_conn: &Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_keyboard: &wl_keyboard::WlKeyboard,
|
||||
_serial: u32,
|
||||
event: KeyEvent,
|
||||
) {
|
||||
println!("Key repeat: {event:?}");
|
||||
}
|
||||
|
||||
fn release_key(
|
||||
&mut self,
|
||||
_: &Connection,
|
||||
_: &QueueHandle<Self>,
|
||||
_: &wl_keyboard::WlKeyboard,
|
||||
_: u32,
|
||||
event: KeyEvent,
|
||||
) {
|
||||
println!("Key release: {event:?}");
|
||||
}
|
||||
|
||||
fn update_modifiers(
|
||||
&mut self,
|
||||
_: &Connection,
|
||||
_: &QueueHandle<Self>,
|
||||
_: &wl_keyboard::WlKeyboard,
|
||||
_serial: u32,
|
||||
modifiers: Modifiers,
|
||||
_raw_modifiers: RawModifiers,
|
||||
_layout: u32,
|
||||
) {
|
||||
println!("Update modifiers: {modifiers:?}");
|
||||
}
|
||||
}
|
||||
30
src/layer_shell_handler.rs
Normal file
30
src/layer_shell_handler.rs
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
use crate::simple_layer::SimpleLayer;
|
||||
use smithay_client_toolkit::shell::wlr_layer::{
|
||||
LayerShellHandler, LayerSurface, LayerSurfaceConfigure,
|
||||
};
|
||||
use std::num::NonZeroU32;
|
||||
use wayland_client::{Connection, QueueHandle};
|
||||
|
||||
impl LayerShellHandler for SimpleLayer {
|
||||
fn closed(&mut self, _conn: &Connection, _qh: &QueueHandle<Self>, _layer: &LayerSurface) {
|
||||
self.exit = true;
|
||||
}
|
||||
|
||||
fn configure(
|
||||
&mut self,
|
||||
_conn: &Connection,
|
||||
qh: &QueueHandle<Self>,
|
||||
_layer: &LayerSurface,
|
||||
configure: LayerSurfaceConfigure,
|
||||
_serial: u32,
|
||||
) {
|
||||
self.width = NonZeroU32::new(configure.new_size.0).map_or(256, NonZeroU32::get);
|
||||
self.height = NonZeroU32::new(configure.new_size.1).map_or(256, NonZeroU32::get);
|
||||
|
||||
// Initiate the first draw.
|
||||
if self.first_configure {
|
||||
self.first_configure = false;
|
||||
self.draw(qh);
|
||||
}
|
||||
}
|
||||
}
|
||||
660
src/main.rs
660
src/main.rs
|
|
@ -1,629 +1,85 @@
|
|||
// Original here: https://github.com/Smithay/client-toolkit/blob/master/examples/wgpu.rs
|
||||
mod compositor_handler;
|
||||
mod keyboard_handler;
|
||||
mod layer_shell_handler;
|
||||
mod mouse_handler;
|
||||
mod output_handler;
|
||||
mod provide_registry_state;
|
||||
mod rendering;
|
||||
mod seat_handling;
|
||||
mod shm_handler;
|
||||
mod simple_layer;
|
||||
|
||||
mod egui_renderer;
|
||||
mod egui_app;
|
||||
mod input_handler;
|
||||
use crate::simple_layer::SimpleLayer;
|
||||
|
||||
use crate::egui_renderer::EguiRenderer;
|
||||
use crate::egui_app::EguiApp;
|
||||
use crate::input_handler::{InputState};
|
||||
use raw_window_handle::{
|
||||
RawDisplayHandle, RawWindowHandle, WaylandDisplayHandle, WaylandWindowHandle,
|
||||
};
|
||||
use smithay_clipboard::Clipboard;
|
||||
use smithay_client_toolkit::{
|
||||
compositor::{CompositorHandler, CompositorState},
|
||||
delegate_compositor, delegate_output, delegate_registry, delegate_seat, delegate_layer,
|
||||
delegate_keyboard, delegate_pointer, delegate_shm,
|
||||
output::{OutputHandler, OutputState},
|
||||
registry::{ProvidesRegistryState, RegistryState},
|
||||
registry_handlers,
|
||||
seat::{
|
||||
Capability, SeatHandler, SeatState,
|
||||
keyboard::{KeyboardHandler, KeyEvent},
|
||||
pointer::{PointerHandler, PointerEvent, ThemedPointer, ThemeSpec, CursorIcon as WaylandCursorIcon},
|
||||
},
|
||||
compositor::CompositorState,
|
||||
delegate_compositor, delegate_keyboard, delegate_layer, delegate_output, delegate_pointer,
|
||||
delegate_registry, delegate_seat, delegate_shm,
|
||||
output::OutputState,
|
||||
registry::RegistryState,
|
||||
seat::SeatState,
|
||||
shell::{
|
||||
wlr_layer::{
|
||||
Anchor, KeyboardInteractivity, Layer, LayerShell, LayerShellHandler,
|
||||
LayerSurface, LayerSurfaceConfigure,
|
||||
},
|
||||
WaylandSurface,
|
||||
wlr_layer::{Anchor, KeyboardInteractivity, Layer, LayerShell},
|
||||
},
|
||||
shm::{Shm, ShmHandler},
|
||||
shm::{Shm, slot::SlotPool},
|
||||
};
|
||||
use wgpu::DeviceDescriptor;
|
||||
use std::ptr::NonNull;
|
||||
use wayland_client::{
|
||||
globals::registry_queue_init,
|
||||
protocol::{wl_output, wl_seat, wl_surface},
|
||||
Connection, Proxy, QueueHandle,
|
||||
};
|
||||
use log::trace;
|
||||
use wayland_client::{Connection, globals::registry_queue_init};
|
||||
|
||||
fn main() {
|
||||
env_logger::init();
|
||||
let conn = Connection::connect_to_env().expect("wayland environment not found");
|
||||
|
||||
let conn = Connection::connect_to_env().unwrap();
|
||||
let (globals, mut event_queue) = registry_queue_init(&conn).unwrap();
|
||||
let qh = event_queue.handle();
|
||||
|
||||
// Initialize layer shell handlers
|
||||
let compositor_state =
|
||||
CompositorState::bind(&globals, &qh).expect("wl_compositor not available");
|
||||
let layer_shell_state = LayerShell::bind(&globals, &qh).expect("layer shell not available");
|
||||
let shm_state = Shm::bind(&globals, &qh).expect("wl_shm not available");
|
||||
let compositor = CompositorState::bind(&globals, &qh).expect("wl_compositor is not available");
|
||||
let layer_shell = LayerShell::bind(&globals, &qh).expect("layer shell is not available");
|
||||
let shm = Shm::bind(&globals, &qh).expect("wl_shm is not available");
|
||||
|
||||
let surface = compositor_state.create_surface(&qh);
|
||||
// Create the layer surface for adapter selection
|
||||
let layer_surface = layer_shell_state.create_layer_surface(
|
||||
&qh,
|
||||
surface,
|
||||
Layer::Top,
|
||||
Some("clock-for-smithay"),
|
||||
let surface = compositor.create_surface(&qh);
|
||||
|
||||
let layer =
|
||||
layer_shell.create_layer_surface(&qh, surface, Layer::Top, Some("simple_layer"), None);
|
||||
layer.set_anchor(Anchor::TOP);
|
||||
layer.set_exclusive_zone(-1);
|
||||
layer.set_keyboard_interactivity(KeyboardInteractivity::OnDemand);
|
||||
layer.set_size(256, 256);
|
||||
|
||||
layer.commit();
|
||||
|
||||
let pool = SlotPool::new(256 * 256 * 4, &shm).expect("Failed to create pool");
|
||||
|
||||
let mut simple_layer = SimpleLayer::new(
|
||||
RegistryState::new(&globals),
|
||||
SeatState::new(&globals, &qh),
|
||||
OutputState::new(&globals, &qh),
|
||||
shm,
|
||||
pool,
|
||||
256,
|
||||
256,
|
||||
layer,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
layer_surface.set_anchor(Anchor::BOTTOM);
|
||||
layer_surface.set_keyboard_interactivity(KeyboardInteractivity::OnDemand);
|
||||
layer_surface.set_size(256, 256);
|
||||
layer_surface.commit();
|
||||
|
||||
// Initialize wgpu
|
||||
let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor {
|
||||
backends: wgpu::Backends::all(),
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
// Create the raw window handle for the surface.
|
||||
let raw_display_handle = RawDisplayHandle::Wayland(WaylandDisplayHandle::new(
|
||||
NonNull::new(conn.backend().display_ptr() as *mut _).unwrap(),
|
||||
));
|
||||
let raw_window_handle = RawWindowHandle::Wayland(WaylandWindowHandle::new(
|
||||
NonNull::new(layer_surface.wl_surface().id().as_ptr() as *mut _).unwrap(),
|
||||
));
|
||||
|
||||
let surface = unsafe {
|
||||
instance
|
||||
.create_surface_unsafe(wgpu::SurfaceTargetUnsafe::RawHandle {
|
||||
raw_display_handle,
|
||||
raw_window_handle,
|
||||
})
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
// Pick a supported adapter
|
||||
let adapter = pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions {
|
||||
compatible_surface: Some(&surface),
|
||||
..Default::default()
|
||||
}))
|
||||
.expect("Failed to find suitable adapter");
|
||||
|
||||
log::info!("Selected backend: {:?}", adapter.get_info().backend);
|
||||
|
||||
let (device, queue) = pollster::block_on(adapter.request_device(&DeviceDescriptor {
|
||||
memory_hints: wgpu::MemoryHints::MemoryUsage,
|
||||
..Default::default()
|
||||
}))
|
||||
.expect("Failed to request device");
|
||||
|
||||
// Initialize clipboard
|
||||
let clipboard = unsafe { Clipboard::new(conn.display().id().as_ptr() as *mut _) };
|
||||
|
||||
let mut main_state = MainState {
|
||||
registry_state: RegistryState::new(&globals),
|
||||
seat_state: SeatState::new(&globals, &qh),
|
||||
output_state: OutputState::new(&globals, &qh),
|
||||
shm_state,
|
||||
|
||||
exit: false,
|
||||
width: 256,
|
||||
height: 256,
|
||||
scale_factor: 1,
|
||||
layer_surface,
|
||||
device,
|
||||
surface,
|
||||
adapter,
|
||||
queue,
|
||||
|
||||
egui_renderer: None,
|
||||
egui_app: EguiApp::new(),
|
||||
input_state: InputState::new(clipboard),
|
||||
themed_pointer: None,
|
||||
};
|
||||
|
||||
// We don't draw immediately, the configure will notify us when to first draw.
|
||||
loop {
|
||||
event_queue.blocking_dispatch(&mut main_state).unwrap();
|
||||
event_queue.blocking_dispatch(&mut simple_layer).unwrap();
|
||||
|
||||
if main_state.exit {
|
||||
trace!("exiting example");
|
||||
if simple_layer.exit {
|
||||
println!("exiting example");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// On exit we must destroy the surface before the layer surface is destroyed.
|
||||
drop(main_state.surface);
|
||||
drop(main_state.layer_surface);
|
||||
}
|
||||
|
||||
struct MainState {
|
||||
registry_state: RegistryState,
|
||||
seat_state: SeatState,
|
||||
output_state: OutputState,
|
||||
shm_state: Shm,
|
||||
delegate_compositor!(SimpleLayer);
|
||||
delegate_output!(SimpleLayer);
|
||||
delegate_shm!(SimpleLayer);
|
||||
|
||||
exit: bool,
|
||||
width: u32,
|
||||
height: u32,
|
||||
scale_factor: i32,
|
||||
layer_surface: LayerSurface,
|
||||
delegate_seat!(SimpleLayer);
|
||||
delegate_keyboard!(SimpleLayer);
|
||||
delegate_pointer!(SimpleLayer);
|
||||
|
||||
adapter: wgpu::Adapter,
|
||||
device: wgpu::Device,
|
||||
queue: wgpu::Queue,
|
||||
surface: wgpu::Surface<'static>,
|
||||
|
||||
egui_renderer: Option<EguiRenderer>,
|
||||
egui_app: EguiApp,
|
||||
input_state: InputState,
|
||||
themed_pointer: Option<ThemedPointer>,
|
||||
}
|
||||
delegate_layer!(SimpleLayer);
|
||||
|
||||
impl MainState {
|
||||
fn render(&mut self, conn: &Connection, qh: &QueueHandle<Self>) {
|
||||
trace!("[MAIN] Render called");
|
||||
|
||||
if self.egui_renderer.is_none() {
|
||||
trace!("[MAIN] Skipping render - EGUI renderer not initialized yet");
|
||||
return;
|
||||
}
|
||||
|
||||
let surface_texture = match self.surface.get_current_texture() {
|
||||
Ok(texture) => texture,
|
||||
Err(e) => {
|
||||
trace!("[MAIN] Failed to acquire swapchain texture: {:?}", e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let texture_view = surface_texture.texture.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
let mut encoder = self.device.create_command_encoder(&Default::default());
|
||||
|
||||
// Clear the surface first
|
||||
{
|
||||
let _renderpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
label: Some("clear pass"),
|
||||
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||
view: &texture_view,
|
||||
depth_slice: None,
|
||||
resolve_target: None,
|
||||
ops: wgpu::Operations {
|
||||
load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
|
||||
store: wgpu::StoreOp::Store,
|
||||
},
|
||||
})],
|
||||
depth_stencil_attachment: None,
|
||||
timestamp_writes: None,
|
||||
occlusion_query_set: None,
|
||||
});
|
||||
}
|
||||
|
||||
// Render EGUI
|
||||
let needs_repaint = if let Some(renderer) = &mut self.egui_renderer {
|
||||
let raw_input = self.input_state.take_raw_input();
|
||||
|
||||
renderer.begin_frame(raw_input);
|
||||
self.egui_app.ui(renderer.context());
|
||||
|
||||
// For Wayland: configure surface at physical resolution, render egui at logical resolution
|
||||
// pixels_per_point tells egui how many physical pixels per logical point
|
||||
let screen_descriptor = egui_wgpu::ScreenDescriptor {
|
||||
size_in_pixels: [self.width * self.scale_factor as u32, self.height * self.scale_factor as u32],
|
||||
pixels_per_point: self.scale_factor as f32,
|
||||
};
|
||||
|
||||
let platform_output = renderer.end_frame_and_draw(
|
||||
&self.device,
|
||||
&self.queue,
|
||||
&mut encoder,
|
||||
&texture_view,
|
||||
screen_descriptor,
|
||||
);
|
||||
|
||||
// Handle clipboard commands from egui
|
||||
for command in &platform_output.commands {
|
||||
self.input_state.handle_output_command(command);
|
||||
}
|
||||
|
||||
// Handle cursor icon changes from EGUI
|
||||
if let Some(themed_pointer) = &self.themed_pointer {
|
||||
let cursor_icon = egui_to_wayland_cursor(platform_output.cursor_icon);
|
||||
let _ = themed_pointer.set_cursor(conn, cursor_icon);
|
||||
}
|
||||
|
||||
// For now, just check if there are any platform commands (indicates interaction)
|
||||
!platform_output.events.is_empty()
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
// Submit the command in the queue to execute
|
||||
self.queue.submit(Some(encoder.finish()));
|
||||
surface_texture.present();
|
||||
|
||||
// Only request next frame if EGUI needs repaint (animations, etc.)
|
||||
if needs_repaint {
|
||||
trace!("[MAIN] EGUI has events, scheduling next frame");
|
||||
self.layer_surface.wl_surface().frame(qh, self.layer_surface.wl_surface().clone());
|
||||
self.layer_surface.wl_surface().commit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CompositorHandler for MainState {
|
||||
fn scale_factor_changed(
|
||||
&mut self,
|
||||
_conn: &Connection,
|
||||
qh: &QueueHandle<Self>,
|
||||
_surface: &wl_surface::WlSurface,
|
||||
new_factor: i32,
|
||||
) {
|
||||
trace!("[MAIN] Scale factor changed to {}", new_factor);
|
||||
self.scale_factor = new_factor;
|
||||
// Request a redraw with the new scale factor
|
||||
self.layer_surface.wl_surface().frame(qh, self.layer_surface.wl_surface().clone());
|
||||
self.layer_surface.wl_surface().commit();
|
||||
}
|
||||
|
||||
fn transform_changed(
|
||||
&mut self,
|
||||
_conn: &Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_surface: &wl_surface::WlSurface,
|
||||
_new_transform: wl_output::Transform,
|
||||
) {
|
||||
// Not needed for this example.
|
||||
}
|
||||
|
||||
fn frame(
|
||||
&mut self,
|
||||
conn: &Connection,
|
||||
qh: &QueueHandle<Self>,
|
||||
_surface: &wl_surface::WlSurface,
|
||||
_time: u32,
|
||||
) {
|
||||
trace!("[MAIN] Frame callback");
|
||||
self.render(conn, qh);
|
||||
}
|
||||
|
||||
fn surface_enter(
|
||||
&mut self,
|
||||
_conn: &Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_surface: &wl_surface::WlSurface,
|
||||
_output: &wl_output::WlOutput,
|
||||
) {
|
||||
// Not needed for this example.
|
||||
}
|
||||
|
||||
fn surface_leave(
|
||||
&mut self,
|
||||
_conn: &Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_surface: &wl_surface::WlSurface,
|
||||
_output: &wl_output::WlOutput,
|
||||
) {
|
||||
// Not needed for this example.
|
||||
}
|
||||
}
|
||||
|
||||
impl OutputHandler for MainState {
|
||||
fn output_state(&mut self) -> &mut OutputState {
|
||||
&mut self.output_state
|
||||
}
|
||||
|
||||
fn new_output(
|
||||
&mut self,
|
||||
_conn: &Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_output: wl_output::WlOutput,
|
||||
) {
|
||||
}
|
||||
|
||||
fn update_output(
|
||||
&mut self,
|
||||
_conn: &Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_output: wl_output::WlOutput,
|
||||
) {
|
||||
}
|
||||
|
||||
fn output_destroyed(
|
||||
&mut self,
|
||||
_conn: &Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_output: wl_output::WlOutput,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
impl LayerShellHandler for MainState {
|
||||
fn closed(&mut self, _conn: &Connection, _qh: &QueueHandle<Self>, _layer: &LayerSurface) {
|
||||
self.exit = true;
|
||||
}
|
||||
|
||||
fn configure(
|
||||
&mut self,
|
||||
conn: &Connection,
|
||||
qh: &QueueHandle<Self>,
|
||||
_layer: &LayerSurface,
|
||||
configure: LayerSurfaceConfigure,
|
||||
_serial: u32,
|
||||
) {
|
||||
trace!("[MAIN] Configure called");
|
||||
let (new_width, new_height) = configure.new_size;
|
||||
self.width = new_width.max(1);
|
||||
self.height = new_height.max(1);
|
||||
self.input_state.set_screen_size(self.width, self.height);
|
||||
trace!("[MAIN] Layer surface size: {}x{}", self.width, self.height);
|
||||
|
||||
let adapter = &self.adapter;
|
||||
let surface = &self.surface;
|
||||
let device = &self.device;
|
||||
|
||||
let cap = surface.get_capabilities(&adapter);
|
||||
let surface_config = wgpu::SurfaceConfiguration {
|
||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
||||
format: cap.formats[0],
|
||||
view_formats: vec![cap.formats[0]],
|
||||
alpha_mode: wgpu::CompositeAlphaMode::Auto,
|
||||
width: self.width * self.scale_factor as u32,
|
||||
height: self.height * self.scale_factor as u32,
|
||||
desired_maximum_frame_latency: 2,
|
||||
// Wayland is inherently a mailbox system.
|
||||
present_mode: wgpu::PresentMode::Mailbox,
|
||||
};
|
||||
|
||||
surface.configure(&self.device, &surface_config);
|
||||
|
||||
// Tell Wayland we're providing a buffer at scale_factor resolution
|
||||
self.layer_surface.wl_surface().set_buffer_scale(self.scale_factor);
|
||||
|
||||
// Initialize EGUI renderer if not already done
|
||||
if self.egui_renderer.is_none() {
|
||||
self.egui_renderer = Some(EguiRenderer::new(
|
||||
device,
|
||||
surface_config.format,
|
||||
None,
|
||||
1,
|
||||
));
|
||||
}
|
||||
|
||||
// Render the frame
|
||||
self.render(conn, qh);
|
||||
}
|
||||
}
|
||||
|
||||
impl PointerHandler for MainState {
|
||||
fn pointer_frame(
|
||||
&mut self,
|
||||
_conn: &Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_pointer: &wayland_client::protocol::wl_pointer::WlPointer,
|
||||
events: &[PointerEvent],
|
||||
) {
|
||||
trace!("[MAIN] Pointer frame with {} events", events.len());
|
||||
for event in events {
|
||||
self.input_state.handle_pointer_event(event);
|
||||
}
|
||||
// Request a redraw after input
|
||||
trace!("[MAIN] Requesting frame after pointer input");
|
||||
self.layer_surface.wl_surface().frame(&_qh, self.layer_surface.wl_surface().clone());
|
||||
self.layer_surface.wl_surface().commit();
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyboardHandler for MainState {
|
||||
fn enter(
|
||||
&mut self,
|
||||
_conn: &Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_keyboard: &wayland_client::protocol::wl_keyboard::WlKeyboard,
|
||||
_surface: &wl_surface::WlSurface,
|
||||
_serial: u32,
|
||||
_raw: &[u32],
|
||||
_keysyms: &[smithay_client_toolkit::seat::keyboard::Keysym],
|
||||
) {
|
||||
trace!("[MAIN] Keyboard focus gained");
|
||||
// Keyboard focus gained
|
||||
}
|
||||
|
||||
fn leave(
|
||||
&mut self,
|
||||
_conn: &Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_keyboard: &wayland_client::protocol::wl_keyboard::WlKeyboard,
|
||||
_surface: &wl_surface::WlSurface,
|
||||
_serial: u32,
|
||||
) {
|
||||
trace!("[MAIN] Keyboard focus lost");
|
||||
// Keyboard focus lost
|
||||
}
|
||||
|
||||
fn press_key(
|
||||
&mut self,
|
||||
_conn: &Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_keyboard: &wayland_client::protocol::wl_keyboard::WlKeyboard,
|
||||
_serial: u32,
|
||||
event: KeyEvent,
|
||||
) {
|
||||
trace!("[MAIN] Key pressed");
|
||||
|
||||
|
||||
self.input_state.handle_keyboard_event(&event, true, false);
|
||||
|
||||
// Request a redraw after input
|
||||
trace!("[MAIN] Requesting frame after key press");
|
||||
self.layer_surface.wl_surface().frame(&_qh, self.layer_surface.wl_surface().clone());
|
||||
self.layer_surface.wl_surface().commit();
|
||||
}
|
||||
|
||||
fn release_key(
|
||||
&mut self,
|
||||
_conn: &Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_keyboard: &wayland_client::protocol::wl_keyboard::WlKeyboard,
|
||||
_serial: u32,
|
||||
event: KeyEvent,
|
||||
) {
|
||||
self.input_state.handle_keyboard_event(&event, false, false);
|
||||
}
|
||||
|
||||
fn update_modifiers(
|
||||
&mut self,
|
||||
_conn: &Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_keyboard: &wayland_client::protocol::wl_keyboard::WlKeyboard,
|
||||
_serial: u32,
|
||||
modifiers: smithay_client_toolkit::seat::keyboard::Modifiers,
|
||||
_raw_modifiers: smithay_client_toolkit::seat::keyboard::RawModifiers,
|
||||
_layout: u32,
|
||||
) {
|
||||
self.input_state.update_modifiers(&modifiers);
|
||||
}
|
||||
|
||||
fn repeat_key(
|
||||
&mut self,
|
||||
_conn: &Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_keyboard: &wayland_client::protocol::wl_keyboard::WlKeyboard,
|
||||
_serial: u32,
|
||||
event: KeyEvent,
|
||||
) {
|
||||
self.input_state.handle_keyboard_event(&event, true, true);
|
||||
// Request a redraw after input
|
||||
self.layer_surface.wl_surface().frame(&_qh, self.layer_surface.wl_surface().clone());
|
||||
self.layer_surface.wl_surface().commit();
|
||||
}
|
||||
}
|
||||
|
||||
impl SeatHandler for MainState {
|
||||
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,
|
||||
) {
|
||||
trace!("[MAIN] New seat capability: {:?}", capability);
|
||||
if capability == Capability::Keyboard && self.seat_state.get_keyboard(qh, &seat, None).is_err() {
|
||||
trace!("[MAIN] Failed to get keyboard");
|
||||
}
|
||||
if capability == Capability::Pointer && self.themed_pointer.is_none() {
|
||||
trace!("[MAIN] Creating themed pointer");
|
||||
let surface = self.layer_surface.wl_surface().clone();
|
||||
match self.seat_state.get_pointer_with_theme(
|
||||
qh,
|
||||
&seat,
|
||||
self.shm_state.wl_shm(),
|
||||
surface,
|
||||
ThemeSpec::default(),
|
||||
) {
|
||||
Ok(themed_pointer) => {
|
||||
self.themed_pointer = Some(themed_pointer);
|
||||
trace!("[MAIN] Themed pointer created successfully");
|
||||
}
|
||||
Err(e) => {
|
||||
trace!("[MAIN] Failed to create themed pointer: {:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_capability(
|
||||
&mut self,
|
||||
_conn: &Connection,
|
||||
_: &QueueHandle<Self>,
|
||||
_: wl_seat::WlSeat,
|
||||
_capability: Capability,
|
||||
) {
|
||||
}
|
||||
|
||||
fn remove_seat(&mut self, _: &Connection, _: &QueueHandle<Self>, _: wl_seat::WlSeat) {}
|
||||
}
|
||||
|
||||
impl ShmHandler for MainState {
|
||||
fn shm_state(&mut self) -> &mut Shm {
|
||||
&mut self.shm_state
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert EGUI cursor icon to Wayland cursor icon
|
||||
fn egui_to_wayland_cursor(cursor: egui::CursorIcon) -> WaylandCursorIcon {
|
||||
use egui::CursorIcon::*;
|
||||
use smithay_client_toolkit::seat::pointer::CursorIcon as WCI;
|
||||
|
||||
match cursor {
|
||||
Default => WCI::Default,
|
||||
None => WCI::Default,
|
||||
ContextMenu => WCI::ContextMenu,
|
||||
Help => WCI::Help,
|
||||
PointingHand => WCI::Pointer,
|
||||
Progress => WCI::Progress,
|
||||
Wait => WCI::Wait,
|
||||
Cell => WCI::Cell,
|
||||
Crosshair => WCI::Crosshair,
|
||||
Text => WCI::Text,
|
||||
VerticalText => WCI::VerticalText,
|
||||
Alias => WCI::Alias,
|
||||
Copy => WCI::Copy,
|
||||
Move => WCI::Move,
|
||||
NoDrop => WCI::NoDrop,
|
||||
NotAllowed => WCI::NotAllowed,
|
||||
Grab => WCI::Grab,
|
||||
Grabbing => WCI::Grabbing,
|
||||
AllScroll => WCI::AllScroll,
|
||||
ResizeHorizontal => WCI::EwResize,
|
||||
ResizeNeSw => WCI::NeswResize,
|
||||
ResizeNwSe => WCI::NwseResize,
|
||||
ResizeVertical => WCI::NsResize,
|
||||
ResizeEast => WCI::EResize,
|
||||
ResizeSouthEast => WCI::SeResize,
|
||||
ResizeSouth => WCI::SResize,
|
||||
ResizeSouthWest => WCI::SwResize,
|
||||
ResizeWest => WCI::WResize,
|
||||
ResizeNorthWest => WCI::NwResize,
|
||||
ResizeNorth => WCI::NResize,
|
||||
ResizeNorthEast => WCI::NeResize,
|
||||
ResizeColumn => WCI::ColResize,
|
||||
ResizeRow => WCI::RowResize,
|
||||
ZoomIn => WCI::ZoomIn,
|
||||
ZoomOut => WCI::ZoomOut,
|
||||
}
|
||||
}
|
||||
|
||||
delegate_compositor!(MainState);
|
||||
delegate_output!(MainState);
|
||||
delegate_shm!(MainState);
|
||||
|
||||
delegate_seat!(MainState);
|
||||
delegate_keyboard!(MainState);
|
||||
delegate_pointer!(MainState);
|
||||
|
||||
delegate_layer!(MainState);
|
||||
|
||||
delegate_registry!(MainState);
|
||||
|
||||
impl ProvidesRegistryState for MainState {
|
||||
fn registry(&mut self) -> &mut RegistryState {
|
||||
&mut self.registry_state
|
||||
}
|
||||
registry_handlers![OutputState];
|
||||
}
|
||||
delegate_registry!(SimpleLayer);
|
||||
|
|
|
|||
46
src/mouse_handler.rs
Normal file
46
src/mouse_handler.rs
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
use smithay_client_toolkit::seat::pointer::{PointerEvent, PointerEventKind, PointerHandler};
|
||||
use smithay_client_toolkit::shell::WaylandSurface;
|
||||
use wayland_client::{Connection, QueueHandle, protocol::wl_pointer};
|
||||
|
||||
use crate::simple_layer::SimpleLayer;
|
||||
|
||||
impl PointerHandler for SimpleLayer {
|
||||
fn pointer_frame(
|
||||
&mut self,
|
||||
_conn: &Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_pointer: &wl_pointer::WlPointer,
|
||||
events: &[PointerEvent],
|
||||
) {
|
||||
use PointerEventKind::*;
|
||||
for event in events {
|
||||
// Ignore events for other surfaces
|
||||
if &event.surface != self.layer.wl_surface() {
|
||||
continue;
|
||||
}
|
||||
match event.kind {
|
||||
Enter { .. } => {
|
||||
println!("Pointer entered @{:?}", event.position);
|
||||
}
|
||||
Leave { .. } => {
|
||||
println!("Pointer left");
|
||||
}
|
||||
Motion { .. } => {}
|
||||
Press { button, .. } => {
|
||||
println!("Press {:x} @ {:?}", button, event.position);
|
||||
self.shift = self.shift.xor(Some(0));
|
||||
}
|
||||
Release { button, .. } => {
|
||||
println!("Release {:x} @ {:?}", button, event.position);
|
||||
}
|
||||
Axis {
|
||||
horizontal,
|
||||
vertical,
|
||||
..
|
||||
} => {
|
||||
println!("Scroll H:{horizontal:?}, V:{vertical:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
45
src/output_handler.rs
Normal file
45
src/output_handler.rs
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
use smithay_client_toolkit::output::{OutputHandler, OutputState};
|
||||
use wayland_client::{Connection, QueueHandle, protocol::wl_output};
|
||||
|
||||
use crate::simple_layer::SimpleLayer;
|
||||
|
||||
impl OutputHandler for SimpleLayer {
|
||||
fn output_state(&mut self) -> &mut OutputState {
|
||||
&mut self.output_state
|
||||
}
|
||||
|
||||
fn new_output(
|
||||
&mut self,
|
||||
_conn: &Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
output: wl_output::WlOutput,
|
||||
) {
|
||||
if let Some(info) = self.output_state.info(&output) {
|
||||
if let Some((width, height)) = info.logical_size {
|
||||
self.layer
|
||||
.set_margin((height - (self.height as i32)) / 2, 0, 0, 0);
|
||||
println!("Monitor logical size: {}x{}", width, height);
|
||||
} else {
|
||||
println!("Logical size not yet known");
|
||||
}
|
||||
} else {
|
||||
println!("OutputInfo not found for this output");
|
||||
}
|
||||
}
|
||||
|
||||
fn update_output(
|
||||
&mut self,
|
||||
_conn: &Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_output: wl_output::WlOutput,
|
||||
) {
|
||||
}
|
||||
|
||||
fn output_destroyed(
|
||||
&mut self,
|
||||
_conn: &Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_output: wl_output::WlOutput,
|
||||
) {
|
||||
}
|
||||
}
|
||||
15
src/provide_registry_state.rs
Normal file
15
src/provide_registry_state.rs
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
use smithay_client_toolkit::{
|
||||
output::OutputState,
|
||||
registry::{ProvidesRegistryState, RegistryState},
|
||||
registry_handlers,
|
||||
seat::SeatState,
|
||||
};
|
||||
|
||||
use crate::simple_layer::SimpleLayer;
|
||||
|
||||
impl ProvidesRegistryState for SimpleLayer {
|
||||
fn registry(&mut self) -> &mut RegistryState {
|
||||
&mut self.registry_state
|
||||
}
|
||||
registry_handlers![OutputState, SeatState];
|
||||
}
|
||||
67
src/rendering.rs
Normal file
67
src/rendering.rs
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
use smithay_client_toolkit::shell::WaylandSurface;
|
||||
use wayland_client::{QueueHandle, protocol::wl_shm};
|
||||
|
||||
use crate::simple_layer::SimpleLayer;
|
||||
|
||||
impl SimpleLayer {
|
||||
pub fn draw(&mut self, qh: &QueueHandle<Self>) {
|
||||
let width = self.width;
|
||||
let height = self.height;
|
||||
let stride = self.width as i32 * 4;
|
||||
|
||||
let (buffer, canvas) = self
|
||||
.pool
|
||||
.create_buffer(
|
||||
width as i32,
|
||||
height as i32,
|
||||
stride,
|
||||
wl_shm::Format::Argb8888,
|
||||
)
|
||||
.expect("create buffer");
|
||||
|
||||
// Draw to the window:
|
||||
{
|
||||
let shift = self.shift.unwrap_or(0);
|
||||
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();
|
||||
});
|
||||
|
||||
if let Some(shift) = &mut self.shift {
|
||||
*shift = (*shift + 1) % width;
|
||||
}
|
||||
}
|
||||
|
||||
// Damage the entire window
|
||||
self.layer
|
||||
.wl_surface()
|
||||
.damage_buffer(0, 0, width as i32, height as i32);
|
||||
|
||||
// Request our next frame
|
||||
self.layer
|
||||
.wl_surface()
|
||||
.frame(qh, self.layer.wl_surface().clone());
|
||||
|
||||
// Attach and commit to present.
|
||||
buffer
|
||||
.attach_to(self.layer.wl_surface())
|
||||
.expect("buffer attach");
|
||||
self.layer.commit();
|
||||
|
||||
// TODO save and reuse buffer when the window size is unchanged. This is especially
|
||||
// useful if you do damage tracking, since you don't need to redraw the undamaged parts
|
||||
// of the canvas.
|
||||
}
|
||||
}
|
||||
58
src/seat_handling.rs
Normal file
58
src/seat_handling.rs
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
use smithay_client_toolkit::seat::{Capability, SeatHandler, SeatState};
|
||||
use wayland_client::{Connection, QueueHandle, protocol::wl_seat};
|
||||
|
||||
use crate::simple_layer::SimpleLayer;
|
||||
|
||||
impl SeatHandler for SimpleLayer {
|
||||
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,
|
||||
) {
|
||||
if capability == Capability::Keyboard && self.keyboard.is_none() {
|
||||
println!("Set keyboard capability");
|
||||
let keyboard = self
|
||||
.seat_state
|
||||
.get_keyboard(qh, &seat, None)
|
||||
.expect("Failed to create keyboard");
|
||||
self.keyboard = Some(keyboard);
|
||||
}
|
||||
|
||||
if capability == Capability::Pointer && self.pointer.is_none() {
|
||||
println!("Set pointer capability");
|
||||
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,
|
||||
) {
|
||||
if capability == Capability::Keyboard && self.keyboard.is_some() {
|
||||
println!("Unset keyboard capability");
|
||||
self.keyboard.take().unwrap().release();
|
||||
}
|
||||
|
||||
if capability == Capability::Pointer && self.pointer.is_some() {
|
||||
println!("Unset pointer capability");
|
||||
self.pointer.take().unwrap().release();
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_seat(&mut self, _: &Connection, _: &QueueHandle<Self>, _: wl_seat::WlSeat) {}
|
||||
}
|
||||
9
src/shm_handler.rs
Normal file
9
src/shm_handler.rs
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
use smithay_client_toolkit::shm::{Shm, ShmHandler};
|
||||
|
||||
use crate::simple_layer::SimpleLayer;
|
||||
|
||||
impl ShmHandler for SimpleLayer {
|
||||
fn shm_state(&mut self) -> &mut Shm {
|
||||
&mut self.shm
|
||||
}
|
||||
}
|
||||
56
src/simple_layer.rs
Normal file
56
src/simple_layer.rs
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
use smithay_client_toolkit::{
|
||||
output::OutputState,
|
||||
registry::RegistryState,
|
||||
seat::SeatState,
|
||||
shell::wlr_layer::LayerSurface,
|
||||
shm::{Shm, slot::SlotPool},
|
||||
};
|
||||
use wayland_client::protocol::{wl_keyboard, wl_pointer};
|
||||
|
||||
pub struct SimpleLayer {
|
||||
pub registry_state: RegistryState,
|
||||
pub seat_state: SeatState,
|
||||
pub output_state: OutputState,
|
||||
pub shm: Shm,
|
||||
|
||||
pub exit: bool,
|
||||
pub first_configure: bool,
|
||||
pub pool: SlotPool,
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
pub layer: LayerSurface,
|
||||
pub keyboard: Option<wl_keyboard::WlKeyboard>,
|
||||
pub keyboard_focus: bool,
|
||||
pub pointer: Option<wl_pointer::WlPointer>,
|
||||
}
|
||||
|
||||
impl SimpleLayer {
|
||||
pub fn new(
|
||||
registry_state: RegistryState,
|
||||
seat_state: SeatState,
|
||||
output_state: OutputState,
|
||||
shm: Shm,
|
||||
pool: SlotPool,
|
||||
width: u32,
|
||||
height: u32,
|
||||
layer: LayerSurface,
|
||||
keyboard: Option<wl_keyboard::WlKeyboard>,
|
||||
pointer: Option<wl_pointer::WlPointer>,
|
||||
) -> Self {
|
||||
Self {
|
||||
registry_state,
|
||||
seat_state,
|
||||
output_state,
|
||||
shm,
|
||||
exit: false,
|
||||
first_configure: true,
|
||||
pool,
|
||||
width,
|
||||
height,
|
||||
layer,
|
||||
keyboard,
|
||||
keyboard_focus: false,
|
||||
pointer,
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue