GPU-accelerated UI toolkit (Vulkan)
git clone https://git.lucas.co/cce-ui.git
Update system configuration and interface modules
src/main.rs | 64 +++++++++++++++++++++++++++++++++++++---------------------
src/wayland.rs | 23 +++++++++++++++++++++
src/widget.rs | 3 ++-
3 files changed, 66 insertions(+), 24 deletions(-)
diff --git a/src/main.rs b/src/main.rs
index 9f6433a..a898a69 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -128,8 +128,7 @@ struct State {
}
impl State {
- async fn new(wayland_handle: &'static clear_ui::wayland::WaylandSurfaceHandle, pw: u32, ph: u32) -> Self {
- let scale = 1.0f64;
+ async fn new(wayland_handle: &'static clear_ui::wayland::WaylandSurfaceHandle, pw: u32, ph: u32, scale: f64) -> Self {
let lw = pw as f32 / scale as f32;
let lh = ph as f32 / scale as f32;
let sw = lw;
@@ -534,8 +533,8 @@ struct AppState {
pointer: Option<wl_pointer::WlPointer>,
keyboard: Option<wl_keyboard::WlKeyboard>,
- window: XdgWindow,
- surface: wl_surface::WlSurface,
+ window: Option<XdgWindow>,
+ surface: Option<wl_surface::WlSurface>,
state: Option<State>,
exit: bool,
@@ -550,9 +549,12 @@ impl CompositorHandler for AppState {
_surface: &wl_surface::WlSurface,
scale_factor: i32,
) {
+ _surface.set_buffer_scale(scale_factor);
if let Some(state) = &mut self.state {
state.scale = scale_factor as f64;
- state.resize(state.physical_width, state.physical_height);
+ let pw = (state.width as f64 * state.scale) as u32;
+ let ph = (state.height as f64 * state.scale) as u32;
+ state.resize(pw, ph);
}
self.redraw = true;
}
@@ -908,7 +910,9 @@ impl WindowHandler for AppState {
let width = w.get();
let height = h.get();
if let Some(state) = &mut self.state {
- state.resize(width, height);
+ let pw = (width as f64 * state.scale) as u32;
+ let ph = (height as f64 * state.scale) as u32;
+ state.resize(pw, ph);
}
}
self.redraw = true;
@@ -963,20 +967,6 @@ fn main() {
let seat_state = SeatState::new(&globals, &qh);
let output_state = OutputState::new(&globals, &qh);
- let surface = compositor_state.create_surface(&qh);
- let window = xdg_shell_state.create_window(surface.clone(), WindowDecorations::None, &qh);
- window.set_title("Clear UI - Test Window");
- window.set_app_id("clear-ui");
- window.set_min_size(Some((1024, 768)));
- window.commit();
-
- let wayland_handle = Box::leak(Box::new(clear_ui::wayland::WaylandSurfaceHandle {
- display_ptr: conn.backend().display_id().as_ptr() as *mut std::ffi::c_void,
- surface_ptr: surface.id().as_ptr() as *mut std::ffi::c_void,
- }));
-
- let state = pollster::block_on(State::new(wayland_handle, 1024, 768));
-
let mut app = AppState {
registry_state: RegistryState::new(&globals),
compositor_state,
@@ -987,13 +977,41 @@ fn main() {
seats: Vec::new(),
pointer: None,
keyboard: None,
- window,
- surface,
- state: Some(state),
+ window: None,
+ surface: None,
+ state: None,
exit: false,
redraw: true,
};
+ // Perform a roundtrip to populate output_state with active output scales
+ event_queue.roundtrip(&mut app).unwrap();
+
+ let scale = clear_ui::wayland::detect_scale_factor(&app.output_state);
+
+ let surface = app.compositor_state.create_surface(&qh);
+ surface.set_buffer_scale(scale as i32);
+
+ let pw = (1024.0 * scale) as u32;
+ let ph = (768.0 * scale) as u32;
+
+ let window = app.xdg_shell_state.create_window(surface.clone(), WindowDecorations::None, &qh);
+ window.set_title("Clear UI - Test Window");
+ window.set_app_id("clear-ui");
+ window.set_min_size(Some((pw, ph)));
+ window.commit();
+
+ let wayland_handle = Box::leak(Box::new(clear_ui::wayland::WaylandSurfaceHandle {
+ display_ptr: conn.backend().display_id().as_ptr() as *mut std::ffi::c_void,
+ surface_ptr: surface.id().as_ptr() as *mut std::ffi::c_void,
+ }));
+
+ let state = pollster::block_on(State::new(wayland_handle, pw, ph, scale));
+
+ app.window = Some(window);
+ app.surface = Some(surface);
+ app.state = Some(state);
+
let mut event_loop = EventLoop::try_new().unwrap();
let loop_handle = event_loop.handle();
WaylandSource::new(conn, event_queue).insert(loop_handle).unwrap();
diff --git a/src/wayland.rs b/src/wayland.rs
index d853205..c2aa580 100644
--- a/src/wayland.rs
+++ b/src/wayland.rs
@@ -2,6 +2,7 @@ use raw_window_handle::{
DisplayHandle, HandleError, HasDisplayHandle, HasWindowHandle, RawDisplayHandle,
RawWindowHandle, WaylandDisplayHandle, WaylandWindowHandle, WindowHandle,
};
+use smithay_client_toolkit::output::OutputState;
#[derive(Debug, Clone, Copy)]
pub struct WaylandSurfaceHandle {
@@ -29,3 +30,25 @@ impl HasWindowHandle for WaylandSurfaceHandle {
unsafe { Ok(WindowHandle::borrow_raw(raw)) }
}
}
+
+/// Helper function to detect the initial display scale factor from Wayland output state.
+/// Iterates over all active outputs and returns the maximum scale factor found (defaulting to 1.0).
+pub fn detect_scale_factor(output_state: &OutputState) -> f64 {
+ let mut max_scale = 1.0;
+ for output in output_state.outputs() {
+ if let Some(info) = output_state.info(&output) {
+ let scale = info.scale_factor as f64;
+ if scale > max_scale {
+ max_scale = scale;
+ }
+ }
+ }
+ max_scale
+}
+
+/// Helper to convert a logical pointer position (from Wayland/SCTK events)
+/// to physical pixel coordinates based on the display scale factor.
+pub fn scale_pointer_pos(pos: (f64, f64), scale: f64) -> (f32, f32) {
+ ((pos.0 * scale) as f32, (pos.1 * scale) as f32)
+}
+
diff --git a/src/widget.rs b/src/widget.rs
index eac70ff..0de965e 100644
--- a/src/widget.rs
+++ b/src/widget.rs
@@ -60,6 +60,7 @@ pub struct KeyEvent {
use crate::colors;
+#[derive(Debug, Clone)]
pub struct TextLabel {
pub text: String,
pub x: f32,
@@ -2987,7 +2988,7 @@ impl ScrollBox {
pub fn get_item_draw_y(&self, virtual_y: f32, item_h: f32) -> Option<f32> {
let draw_y = self.viewport_y + virtual_y - self.scroll_y;
- if draw_y >= self.viewport_y + 2.0 && draw_y + item_h <= self.viewport_y + self.viewport_h - 2.0 {
+ if draw_y >= self.viewport_y - 1.0 && draw_y + item_h <= self.viewport_y + self.viewport_h + 1.0 {
Some(draw_y)
} else {
None