Split into more modules and add bounds checking to widgets

This commit is contained in:
2024-05-05 04:35:28 -05:00
parent b9f1424b54
commit 1acaabb1c2
6 changed files with 824 additions and 906 deletions

View File

@@ -0,0 +1,446 @@
use smithay_client_toolkit::reexports::calloop::{EventLoop, LoopHandle}; use smithay_client_toolkit::reexports::calloop_wayland_source::WaylandSource;
use smithay_client_toolkit::{
activation::{ActivationState, ActivationHandler, RequestData},
compositor::{CompositorState, CompositorHandler},
output::{OutputHandler, OutputState},
registry::{RegistryState, ProvidesRegistryState},
registry_handlers,
seat::{
pointer::{PointerEvent, PointerEventKind, PointerHandler},
SeatState, SeatHandler, Capability
},
shell::{
xdg::{
window::{WindowHandler, Window, WindowConfigure, WindowDecorations},
XdgShell,
},
WaylandSurface,
},
shm::{ slot::{SlotPool, Buffer},
Shm, ShmHandler
},
delegate_registry, delegate_compositor, delegate_seat, delegate_output,
delegate_xdg_shell, delegate_shm, delegate_activation, delegate_xdg_window,
delegate_pointer,
};
use wayland_client::{
Connection, QueueHandle,
globals::registry_queue_init,
protocol::{wl_surface, wl_output, wl_seat, wl_shm, wl_pointer},
};
use std::time::Duration;
use anyhow::Result;
use crate::wayland::{
draw::DrawPath,
builder::{TextAreaBuilder, DrawAreaBuilder, ButtonBuilder},
ui::{self, Widget},
Position,
};
const WINDOW_HEIGHT: u32 = 256;
const WINDOW_WIDTH: u32 = 512;
pub(crate) struct SimpleWindow {
registry_state: RegistryState,
seat_state: SeatState,
output_state: OutputState,
shm: Shm,
xdg_activation: Option<ActivationState>,
buffer: Option<Buffer>,
pool: SlotPool,
window: Window,
ui: Option<ui::Window>,
width: u32,
height: u32,
cursor_down: bool,
exit: bool,
first_configure: bool,
pointer: Option<wl_pointer::WlPointer>,
_loop_handle: LoopHandle<'static, SimpleWindow>,
draw_path: DrawPath,
}
impl SimpleWindow {
pub(crate) fn run() -> Result<()> {
let conn = Connection::connect_to_env()?;
let (globals, event_queue) = registry_queue_init(&conn)?;
let qh = event_queue.handle();
let mut event_loop: EventLoop::<SimpleWindow> =
EventLoop::try_new()?;
let loop_handle = event_loop.handle();
WaylandSource::new(conn.clone(), event_queue).insert(loop_handle)?;
let compositor = CompositorState::bind(&globals, &qh)?;
let xdg_shell = XdgShell::bind(&globals, &qh)?;
let shm = Shm::bind(&globals, &qh)?;
let xdg_activation = ActivationState::bind(&globals, &qh).ok();
let surface = compositor.create_surface(&qh);
let window = xdg_shell.create_window(surface, WindowDecorations::RequestServer, &qh);
window.set_title("A window");
window.set_app_id("simmer.simplewindow");
window.set_min_size(Some((WINDOW_WIDTH, WINDOW_HEIGHT)));
window.set_max_size(Some((WINDOW_WIDTH, WINDOW_HEIGHT)));
window.commit();
if let Some(activation) = xdg_activation.as_ref() {
activation.request_token(
&qh,
RequestData {
seat_and_serial: None,
surface: Some(window.wl_surface().clone()),
app_id: Some(String::from("simmer.waywrite")),
},
)
}
let pool = SlotPool::new((WINDOW_WIDTH as usize) * (WINDOW_HEIGHT as usize) * 4, &shm)?;
let mut simple_window = SimpleWindow {
registry_state: RegistryState::new(&globals),
seat_state: SeatState::new(&globals, &qh),
output_state: OutputState::new(&globals, &qh),
xdg_activation,
shm,
buffer: None,
pool,
window,
ui: None,
width: WINDOW_WIDTH,
height: WINDOW_HEIGHT,
cursor_down: false,
exit: false,
first_configure: true,
pointer: None,
_loop_handle: event_loop.handle(),
draw_path: DrawPath::new(),
};
loop {
event_loop.dispatch(Duration::from_millis(16), &mut simple_window)?;
if simple_window.exit {
println!("exiting");
break;
}
}
Ok(())
}
}
impl CompositorHandler for SimpleWindow {
fn scale_factor_changed(
&mut self,
_: &Connection,
_: &QueueHandle<Self>,
_: &wl_surface::WlSurface,
_: i32) {}
fn transform_changed(
&mut self,
_: &Connection,
_: &QueueHandle<Self>,
_: &wl_surface::WlSurface,
_: wl_output::Transform) {}
fn frame(
&mut self,
conn: &Connection,
qh: &QueueHandle<Self>,
_: &wl_surface::WlSurface,
_: u32
) {
self.draw(conn, qh);
}
}
impl SeatHandler for SimpleWindow {
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,
_: &Connection,
qh: &QueueHandle<Self>,
seat: wl_seat::WlSeat,
capability: Capability,
) {
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,
_: &Connection,
_: &QueueHandle<Self>,
_: wl_seat::WlSeat,
capability: Capability,
) {
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,
) {}
}
impl OutputHandler for SimpleWindow {
fn output_state(&mut self) -> &mut OutputState {
&mut self.output_state
}
fn new_output(
&mut self,
_: &Connection,
_: &QueueHandle<Self>,
_: wl_output::WlOutput,
) {}
fn update_output(
&mut self,
_: &Connection,
_: &QueueHandle<Self>,
_: wl_output::WlOutput,
) {}
fn output_destroyed(
&mut self,
_: &Connection,
_: &QueueHandle<Self>,
_: wl_output::WlOutput,
) {}
}
impl WindowHandler for SimpleWindow {
fn request_close(
&mut self,
_: &Connection,
_: &QueueHandle<Self>,
_: &Window
) {
self.exit = true;
}
fn configure(
&mut self,
conn: &Connection,
qh: &QueueHandle<Self>,
_: &Window,
_: WindowConfigure,
_: u32,
) {
if self.first_configure {
self.first_configure = false;
self.draw(conn, qh);
}
if self.ui.is_none() {
let mut draw_area = DrawAreaBuilder::new();
draw_area.position(0.0, 50.0);
draw_area.width(self.width as f32 - 100.0);
draw_area.height(self.height as f32 - 50.0);
draw_area.path(&mut self.draw_path);
let draw_area = draw_area.finish();
let mut window = ui::Window::new(
self.width.try_into().unwrap(),
self.height.try_into().unwrap(),
draw_area,
);
let mut button = ButtonBuilder::new();
button.position(self.width as f32 - 100.0, 50.0);
button.width(100.0);
button.height(50.0);
let button = button.finish();
let mut text_area = TextAreaBuilder::new();
text_area.position(0.0, 0.0);
text_area.width(self.width as f32);
text_area.height(50.0);
let text_area = text_area.finish();
window.add_widget(button);
window.add_widget(text_area);
self.ui = Some(window);
}
}
}
impl ShmHandler for SimpleWindow {
fn shm_state(&mut self) -> &mut Shm {
&mut self.shm
}
}
impl ActivationHandler for SimpleWindow {
type RequestData = RequestData;
fn new_token(&mut self, token: String, _: &Self::RequestData) {
self.xdg_activation
.as_ref()
.unwrap()
.activate::<SimpleWindow>(self.window.wl_surface(), token);
}
}
impl PointerHandler for SimpleWindow {
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.window.wl_surface() {
continue;
}
match event.kind {
Enter { .. } => {
println!("Pointer entered @{:?}", event.position);
}
Leave { .. } => {
println!("Pointer left");
}
Motion { .. } => {
if self.cursor_down {
if let Some(window) = &mut self.ui {
let pos = Position::new(event.position.0 as f32, event.position.1 as f32);
if window.draw_area.contains_point(pos) {
window.draw_area.path.add_point(pos, false);
}
}
}
}
Press { button, .. } => {
println!("Press {:x} @ {:?}", button, event.position);
self.cursor_down = true;
if let Some(window) = &mut self.ui {
let pos = Position::new(event.position.0 as f32, event.position.1 as f32);
if window.draw_area.contains_point(pos) {
window.draw_area.path.add_point(pos, true);
}
for widget in &window.widgets {
if let Widget::Button(button) = widget {
if button.contains_point(pos) {
window.draw_area.path = DrawPath::default();
}
}
}
}
}
Release { button, .. } => {
println!("Release {:x} @ {:?}", button, event.position);
self.cursor_down = false;
if let Some(window) = &mut self.ui {
let pos = Position::new(event.position.0 as f32, event.position.1 as f32);
if window.draw_area.contains_point(pos) {
window.draw_area.path.add_point(pos, false);
} else {
println!("Does not contain point")
}
}
}
Axis { horizontal, vertical, .. } => {
println!("Scroll H:{horizontal:?}, V:{vertical:?}");
}
}
}
}
}
impl SimpleWindow {
fn draw(&mut self, _: &Connection, qh: &QueueHandle<Self>) {
let buffer = self.buffer.get_or_insert_with(|| {
self.pool
.create_buffer(self.width as i32, self.height as i32, (self.width as i32) * 4, wl_shm::Format::Argb8888)
.expect("create buffer")
.0
});
let canvas = match self.pool.canvas(buffer) {
Some(canvas) => canvas,
None => {
let (second_buffer, canvas) = self
.pool
.create_buffer(
self.width as i32,
self.height as i32,
(self.width as i32) * 4,
wl_shm::Format::Argb8888,
)
.expect("create buffer");
*buffer = second_buffer;
canvas
}
};
if let Some(window) = &mut self.ui {
window.render(canvas);
}
self.window.wl_surface().damage_buffer(0, 0, self.width as i32, self.height as i32);
self.window.wl_surface().frame(qh, self.window.wl_surface().clone());
buffer.attach_to(self.window.wl_surface()).expect("buffer attach");
self.window.commit();
}
}
delegate_compositor!(SimpleWindow);
delegate_output!(SimpleWindow);
delegate_shm!(SimpleWindow);
delegate_seat!(SimpleWindow);
delegate_xdg_shell!(SimpleWindow);
delegate_xdg_window!(SimpleWindow);
delegate_activation!(SimpleWindow);
delegate_pointer!(SimpleWindow);
delegate_registry!(SimpleWindow);
impl ProvidesRegistryState for SimpleWindow {
fn registry(&mut self) -> &mut RegistryState {
&mut self.registry_state
}
registry_handlers![OutputState, SeatState,];
}