This commit is contained in:
parent
9944db3d2b
commit
3e20807d13
7 changed files with 3075 additions and 1 deletions
1877
Cargo.lock
generated
Normal file
1877
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
13
Cargo.toml
13
Cargo.toml
|
|
@ -4,3 +4,16 @@ 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"
|
||||
|
|
|
|||
22
flake.nix
22
flake.nix
|
|
@ -21,7 +21,29 @@
|
|||
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
|
||||
];
|
||||
|
||||
# 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";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
|||
49
src/egui_app.rs
Normal file
49
src/egui_app.rs
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
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()
|
||||
}
|
||||
}
|
||||
109
src/egui_renderer.rs
Normal file
109
src/egui_renderer.rs
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
// 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
|
||||
}
|
||||
}
|
||||
378
src/input_handler.rs
Normal file
378
src/input_handler.rs
Normal file
|
|
@ -0,0 +1,378 @@
|
|||
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,
|
||||
})
|
||||
}
|
||||
|
||||
628
src/main.rs
628
src/main.rs
|
|
@ -1,3 +1,629 @@
|
|||
// Original here: https://github.com/Smithay/client-toolkit/blob/master/examples/wgpu.rs
|
||||
|
||||
mod egui_renderer;
|
||||
mod egui_app;
|
||||
mod input_handler;
|
||||
|
||||
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},
|
||||
},
|
||||
shell::{
|
||||
wlr_layer::{
|
||||
Anchor, KeyboardInteractivity, Layer, LayerShell, LayerShellHandler,
|
||||
LayerSurface, LayerSurfaceConfigure,
|
||||
},
|
||||
WaylandSurface,
|
||||
},
|
||||
shm::{Shm, ShmHandler},
|
||||
};
|
||||
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;
|
||||
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
env_logger::init();
|
||||
|
||||
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 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"),
|
||||
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();
|
||||
|
||||
if main_state.exit {
|
||||
trace!("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,
|
||||
|
||||
exit: bool,
|
||||
width: u32,
|
||||
height: u32,
|
||||
scale_factor: i32,
|
||||
layer_surface: LayerSurface,
|
||||
|
||||
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>,
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue