From 1acaabb1c23a454df100de227d9bdf7362950e1a Mon Sep 17 00:00:00 2001 From: Ethan Simmons Date: Sun, 5 May 2024 04:35:28 -0500 Subject: [PATCH] Split into more modules and add bounds checking to widgets --- src/main.rs | 158 +-------------- src/wayland.rs | 411 +------------------------------------ src/wayland/app.rs | 446 +++++++++++++++++++++++++++++++++++++++++ src/wayland/builder.rs | 187 +++++++++++++++++ src/wayland/draw.rs | 132 ++++++++++++ src/wayland/ui.rs | 396 +++++------------------------------- 6 files changed, 824 insertions(+), 906 deletions(-) create mode 100644 src/wayland/builder.rs create mode 100644 src/wayland/draw.rs diff --git a/src/main.rs b/src/main.rs index c51f95e..3ecbb42 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,162 +1,12 @@ -use relm4::prelude::{gtk, ComponentParts, ComponentSender, RelmApp, RelmWidgetExt, SimpleComponent}; -use gtk::prelude::{BoxExt, ButtonExt, GtkWindowExt, OrientableExt, WidgetExt, GestureSingleExt}; -use gtk::cairo::{Context, Operator}; -use relm4::drawing::DrawHandler; - -use std::error::Error; - -use anyhow::Result; - +use anyhow; mod wayland; mod process_point; -use process_point::{Point, print_words}; +use wayland::app::SimpleWindow; -#[derive(Debug)] -enum AppInput { - Input, - NewLine((f64, f64)), - AddPoint((f64, f64)), - Reset, -} +fn main() -> anyhow::Result<()> { - -#[derive(Debug)] -struct AppModel { - counter: u8, - points: Vec, - handler: DrawHandler, -} - -#[derive(Debug)] -struct UpdatePoints {} - -#[relm4::component] -impl SimpleComponent for AppModel { - type Init = u8; - type Input = AppInput; - type Output = UpdatePoints; - - view! { - gtk::Window { - set_title: Some("Waywrite"), - set_default_width: 600, - set_default_height: 200, - - gtk::Box{ - set_orientation: gtk::Orientation::Horizontal, - set_spacing: 5, - set_margin_all: 2, - set_hexpand: true, - - gtk::Box { - set_orientation: gtk::Orientation::Vertical, - set_spacing: 5, - set_margin_all: 5, - set_vexpand: true, - - #[local_ref] - area -> gtk::DrawingArea { - set_vexpand: true, - set_hexpand: true, - - add_controller = gtk::GestureStylus { - set_button: 0, - connect_motion[sender] => move |_, x, y| { - sender.input(AppInput::AddPoint((x, y))); - }, - connect_down[sender] => move |_, x, y| { - sender.input(AppInput::NewLine((x, y))); - } - } - }, - }, - - gtk::Box { - set_orientation: gtk::Orientation::Vertical, - set_spacing: 4, - set_margin_all: 2, - set_halign: gtk::Align::End, - - gtk::Button { - set_label: "Enter", - connect_clicked[sender] => move |_| { - sender.input(AppInput::Input); - } - }, - - gtk::Button { - set_label: "Erase", - connect_clicked[sender] => move |_| { - sender.input(AppInput::Reset); - } - }, - } - } - } - } - - fn init( - _: Self::Init, - root: &Self::Root, - sender: ComponentSender, - ) ->relm4::ComponentParts { - let model = AppModel { - counter: 0, - points: Vec::new(), - handler: DrawHandler::new(), - }; - - let area = model.handler.drawing_area(); - let widgets = view_output!(); - - ComponentParts { model, widgets } - - } - - fn update(&mut self, message: Self::Input, _sender: ComponentSender) { - let cx = self.handler.get_context(); - - match message { - AppInput::Input => { - print_words(&self.points).unwrap(); - } - AppInput::AddPoint((x, y)) => { - self.points.push(Point { x, y, new_line: false }) - } - AppInput::NewLine((x, y)) => { - self.points.push(Point { x, y, new_line: true }) - } - AppInput::Reset => { - cx.set_operator(Operator::Clear); - cx.set_source_rgba(0.0, 0.0, 0.0, 0.0); - cx.paint().expect("Could not fill context"); - self.points = Vec::new(); - } - } - - draw(&cx, &self.points); - } -} - -fn draw(cx: &Context, points: &[Point]) { - for (i, point) in points.into_iter().enumerate().filter(|(i, _)| *i != 0) { - - if !point.new_line { - let last_point = &points[i - 1]; - - cx.move_to(last_point.x as f64, last_point.y as f64); - cx.line_to(point.x as f64, point.y as f64); - cx.set_source_rgb(200.0, 200.0, 200.0); - cx.set_line_width(2.0); - cx.stroke().expect("Failed to draw line"); - } - } -} - -fn main() -> Result<(), Box> { - - wayland::run()?; + SimpleWindow::run()?; Ok(()) } diff --git a/src/wayland.rs b/src/wayland.rs index 3b7fa2e..697722a 100644 --- a/src/wayland.rs +++ b/src/wayland.rs @@ -1,409 +1,18 @@ -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; - mod ui; -mod app; +pub mod app; +mod draw; +mod builder; -use ui::{ButtonBuilder, DrawAreaBuilder, TextAreaBuilder, DrawPath, Position}; - - -const WINDOW_HEIGHT: u32 = 256; -const WINDOW_WIDTH: u32 = 512; - - -pub(super) 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:: = - 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, - 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(()) +#[derive(Clone, Copy, Debug)] +pub(super) struct Position { + pub(super) x: f32, + pub(super) y: f32, } -struct SimpleWindow { - registry_state: RegistryState, - seat_state: SeatState, - output_state: OutputState, - shm: Shm, - xdg_activation: Option, - - buffer: Option, - pool: SlotPool, - window: Window, - width: u32, - height: u32, - cursor_down: bool, - exit: bool, - first_configure: bool, - pointer: Option, - _loop_handle: LoopHandle<'static, SimpleWindow>, - - draw_path: DrawPath, -} - -impl CompositorHandler for SimpleWindow { - fn scale_factor_changed( - &mut self, - _: &Connection, - _: &QueueHandle, - _: &wl_surface::WlSurface, - _: i32) {} - fn transform_changed( - &mut self, - _: &Connection, - _: &QueueHandle, - _: &wl_surface::WlSurface, - _: wl_output::Transform) {} - fn frame( - &mut self, - conn: &Connection, - qh: &QueueHandle, - _: &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, - _: wl_seat::WlSeat - ) {} - fn new_capability( - &mut self, - _: &Connection, - qh: &QueueHandle, - 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, - _: 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, - _: 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, - _: wl_output::WlOutput, - ) {} - fn update_output( - &mut self, - _: &Connection, - _: &QueueHandle, - _: wl_output::WlOutput, - ) {} - fn output_destroyed( - &mut self, - _: &Connection, - _: &QueueHandle, - _: wl_output::WlOutput, - ) {} -} - -impl WindowHandler for SimpleWindow { - fn request_close( - &mut self, - _: &Connection, - _: &QueueHandle, - _: &Window - ) { - self.exit = true; - } - - fn configure( - &mut self, - conn: &Connection, - qh: &QueueHandle, - _: &Window, - _: WindowConfigure, - _: u32, - ) { - if self.first_configure { - self.first_configure = false; - self.draw(conn, qh); - } +impl Position { + pub(super) fn new(x: f32, y: f32) -> Self { + Position { x, y } } } -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::(self.window.wl_surface(), token); - } -} - -impl PointerHandler for SimpleWindow { - fn pointer_frame( - &mut self, - _conn: &Connection, - _qh: &QueueHandle, - _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 { - self.draw_path.add_point(Position::new(event.position.0 as f32, event.position.1 as f32), false); - } - } - Press { button, .. } => { - println!("Press {:x} @ {:?}", button, event.position); - self.cursor_down = true; - self.draw_path.add_point(Position::new(event.position.0 as f32, event.position.1 as f32), true) - } - Release { button, .. } => { - println!("Release {:x} @ {:?}", button, event.position); - self.cursor_down = false; - self.draw_path.add_point(Position::new(event.position.0 as f32, event.position.1 as f32), false) - } - Axis { horizontal, vertical, .. } => { - println!("Scroll H:{horizontal:?}, V:{vertical:?}"); - } - } - } - } -} - -impl SimpleWindow { - fn draw(&mut self, _: &Connection, qh: &QueueHandle) { - 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 - } - }; - - let draw_buffer: &mut [u32] = unsafe {canvas.align_to_mut::().1}; - - - let mut window = ui::Window::new( - draw_buffer, - self.width.try_into().unwrap(), - self.height.try_into().unwrap() - ); - - let mut button = ButtonBuilder::new(); - let mut draw_area = DrawAreaBuilder::new(); - - button.position(self.width as f32 - 100.0, 50.0); - button.width(100.0); - button.height(50.0); - let button = button.finish(); - - 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(&self.draw_path); - let draw_area = draw_area.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(draw_area); - window.add_widget(text_area); - - window.render(); - - - 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,]; -} diff --git a/src/wayland/app.rs b/src/wayland/app.rs index e69de29..2652739 100644 --- a/src/wayland/app.rs +++ b/src/wayland/app.rs @@ -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, + + buffer: Option, + pool: SlotPool, + window: Window, + ui: Option, + width: u32, + height: u32, + cursor_down: bool, + exit: bool, + first_configure: bool, + pointer: Option, + _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:: = + 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, + _: &wl_surface::WlSurface, + _: i32) {} + fn transform_changed( + &mut self, + _: &Connection, + _: &QueueHandle, + _: &wl_surface::WlSurface, + _: wl_output::Transform) {} + fn frame( + &mut self, + conn: &Connection, + qh: &QueueHandle, + _: &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, + _: wl_seat::WlSeat + ) {} + fn new_capability( + &mut self, + _: &Connection, + qh: &QueueHandle, + 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, + _: 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, + _: 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, + _: wl_output::WlOutput, + ) {} + fn update_output( + &mut self, + _: &Connection, + _: &QueueHandle, + _: wl_output::WlOutput, + ) {} + fn output_destroyed( + &mut self, + _: &Connection, + _: &QueueHandle, + _: wl_output::WlOutput, + ) {} +} + +impl WindowHandler for SimpleWindow { + fn request_close( + &mut self, + _: &Connection, + _: &QueueHandle, + _: &Window + ) { + self.exit = true; + } + + fn configure( + &mut self, + conn: &Connection, + qh: &QueueHandle, + _: &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::(self.window.wl_surface(), token); + } +} + +impl PointerHandler for SimpleWindow { + fn pointer_frame( + &mut self, + _conn: &Connection, + _qh: &QueueHandle, + _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) { + 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,]; +} diff --git a/src/wayland/builder.rs b/src/wayland/builder.rs new file mode 100644 index 0000000..6f622de --- /dev/null +++ b/src/wayland/builder.rs @@ -0,0 +1,187 @@ +use crate::wayland::{ + ui::{TextArea, TextAreaColors, Button, ButtonColors, Widget}, + draw::{DrawAreaColors, DrawArea, DrawPath}, + Position +}; + +use raqote::{DrawOptions, PathBuilder}; + +pub(super) struct TextAreaBuilder { + pub(super) width: Option, + pub(super) height: Option, + pub(super) position: Option, + pub(super) colors: Option, + pub(super) options: Option, +} + +impl<'a> TextAreaBuilder { + pub(super) fn new() -> Self { + Self { + width: None, + height: None, + position: None, + colors: None, + options: None, + } + } + + pub(super) fn width(&mut self, width: f32) { + self.width = Some(width) + } + + pub(super) fn height(&mut self, height: f32) { + self.height = Some(height) + } + + pub(super) fn position(&mut self, x: f32, y: f32) { + self.position = Some(Position::new(x, y)) + } + + + pub(super) fn colors(&mut self, colors: TextAreaColors) { + self.colors = Some(colors); + } + + pub(super) fn finish(self) -> Widget { + Widget::TextArea( + TextArea { + width: self.width.expect("Button must have width"), + height: self.height.expect("Button must have height"), + position: self.position.expect("Button must have a position"), + colors: self.colors.unwrap_or_default(), + options: self.options.unwrap_or(DrawOptions::new()), + } + ) + } +} + +pub(super) struct ButtonBuilder { + pub(super) width: Option, + pub(super) height: Option, + pub(super) position: Option, + pub(super) colors: Option, + pub(super) options: Option, +} + +impl ButtonBuilder { + pub(super) fn new() -> Self { + Self { + width: None, + height: None, + position: None, + colors: None, + options: None, + } + } + + pub(super) fn width(&mut self, width: f32) { + self.width = Some(width) + } + + pub(super) fn height(&mut self, height: f32) { + self.height = Some(height) + } + + pub(super) fn position(&mut self, x: f32, y: f32) { + self.position = Some(Position::new(x, y)) + } + + pub(super) fn colors(&mut self, colors: ButtonColors) { + self.colors = Some(colors) + } + + pub(super) fn finish(self) -> Widget { + let width = self.width.expect("Button must have a width"); + let height = self.height.expect("Button must have a height"); + let position = self.position.expect("Button must have a position"); + + let mut button_path = PathBuilder::new(); + button_path.move_to(position.x, position.y); + button_path.line_to(position.x + width, position.y); + button_path.line_to(position.x + width, position.y + height); + button_path.line_to(position.x, position.y + height); + button_path.line_to(position.x, position.y); + let button_path = button_path.finish(); + + Widget::Button( + Button { + width: self.width.expect("Button must have width"), + height: self.height.expect("Button must have height"), + position: self.position.expect("Button must have a position"), + shape: button_path, + colors: self.colors.unwrap_or_default(), + options: self.options.unwrap_or(DrawOptions::new()), + } + ) + } +} + +pub(super) struct DrawAreaBuilder { + pub(super) width: Option, + pub(super) height: Option, + pub(super) position: Option, + pub(super) path: Option, + pub(super) colors: Option, + pub(super) options: Option, +} + +impl DrawAreaBuilder { + pub(super) fn new() -> Self { + Self { + width: None, + height: None, + position: None, + path: None, + colors: None, + options: None, + } + } + + pub(super) fn width(&mut self, width: f32) { + self.width = Some(width) + } + + pub(super) fn height(&mut self, height: f32) { + self.height = Some(height) + } + + pub(super) fn position(&mut self, x: f32, y: f32) { + self.position = Some(Position::new(x, y)) + } + + pub(super) fn colors(&mut self, colors: DrawAreaColors) { + self.colors = Some(colors) + } + + pub(super) fn path(&mut self, path: &mut DrawPath) { + self.path = Some(DrawPath { + paths: std::mem::take(&mut path.paths), + newline: path.newline, + }); + } + + pub(super) fn finish(self) -> DrawArea { + + let width = self.width.expect("Button must have a width"); + let height = self.height.expect("Button must have a height"); + let position = self.position.expect("Button must have a position"); + + let mut draw_rect_path = PathBuilder::new(); + draw_rect_path.move_to(position.x, position.y); + draw_rect_path.line_to(position.x + width, position.y); + draw_rect_path.line_to(position.x + width, position.y + height); + draw_rect_path.line_to(position.x, position.y + height); + draw_rect_path.line_to(position.x, position.y); + let draw_rect_path = draw_rect_path.finish(); + + DrawArea { + width: self.width.expect("Draw area must have width"), + height: self.height.expect("Draw area must have height"), + position: self.position.expect("Draw area must have a position"), + path: self.path.expect("Draw must have a path"), + shape: draw_rect_path, + colors: self.colors.unwrap_or_default(), + options: self.options.unwrap_or(DrawOptions::new()), + } + } +} diff --git a/src/wayland/draw.rs b/src/wayland/draw.rs new file mode 100644 index 0000000..05092e2 --- /dev/null +++ b/src/wayland/draw.rs @@ -0,0 +1,132 @@ +use raqote::{DrawTarget, Color, StrokeStyle, PathBuilder, Source, DrawOptions}; + +use crate::wayland::Position; + +pub(super) trait Draw { + fn render(&self, area: &mut DrawTarget<&mut [u32]>); +} + +type Path = Vec; + +#[derive(Default)] +pub(super) struct DrawPath { + pub(super) paths: Vec, + pub(super) newline: bool, +} + +impl DrawPath { + pub(super) fn new() -> Self { + Self { + paths: Vec::new(), + newline: false, + } + } + + pub(super) fn add_point(&mut self, point: Position, newline: bool) { + if newline { + self.paths.push(vec![point]) + } else if self.paths.len() > 0 { + self.paths + .last_mut() + .unwrap() + .push(point); + } + } + + pub(super) fn into_iter(self) -> std::vec::IntoIter { + self.paths.into_iter() + } + + pub(super) fn iter(&self) -> std::slice::Iter { + self.paths.iter() + } + + pub(super) fn iter_mut(&mut self) -> std::slice::IterMut { + self.paths.iter_mut() + } +} + +pub(super) struct DrawAreaColors { + background_color: Color, + drawing_color: Color, + border_color: Color, +} + +impl Default for DrawAreaColors { + fn default() -> Self { + Self { + background_color: Color::new(0xff, 0x4a, 0x4e, 0x64), + drawing_color: Color::new(0xff, 0xea, 0xea, 0xe6), + border_color: Color::new(0xff, 0x77, 0x7f, 0x97), + } + } +} + +pub(super) struct DrawArea { + pub(super) width: f32, + pub(super) height: f32, + pub(super) position: Position, + pub(super) path: DrawPath, + pub(super) shape: raqote::Path, + pub(super) colors: DrawAreaColors, + pub(super) options: DrawOptions, +} + +impl DrawArea { + fn background_source(&self) -> Source { + Source::Solid(self.colors.background_color.into()) + } + + fn drawing_source(&self) -> Source { + Source::Solid(self.colors.drawing_color.into()) + } + + fn border_source(&self) -> Source { + Source::Solid(self.colors.border_color.into()) + } + + pub(crate) fn contains_point(&self, point: Position) -> bool { + self.shape.contains_point(0.1, point.x, point.y) + } + +} + +impl Draw for DrawArea { + fn render(&self, area: &mut DrawTarget<&mut [u32]>) { + let mut draw_path = PathBuilder::new(); + + for path in self.path.iter() { + if path.len() > 1 { + let mut path = path.into_iter(); + + let first_point = path + .by_ref() + .next(); + + draw_path.move_to(first_point.unwrap().x, first_point.unwrap().y); + + for point in path { + draw_path.line_to(point.x, point.y); + } + + } + + } + + + let path = draw_path.finish(); + + area.fill( + &self.shape, + &self.background_source(), + &self.options, + ); + + area.stroke( + &path, + &self.drawing_source(), + &StrokeStyle {width: 3.0, ..StrokeStyle::default()}, + &self.options, + ); + } +} diff --git a/src/wayland/ui.rs b/src/wayland/ui.rs index 3d9a79e..900656d 100644 --- a/src/wayland/ui.rs +++ b/src/wayland/ui.rs @@ -1,6 +1,11 @@ -use raqote::{DrawTarget, PathBuilder, Source, StrokeStyle, DrawOptions, SolidSource, Color}; +use raqote::{DrawTarget, Source, DrawOptions, Color}; -struct UIColor { +use crate::wayland::{ + draw::{Draw, DrawArea}, + Position, +}; + +pub(super) struct UIColors { draw_area: Color, button: Color, button_accent: Color, @@ -10,7 +15,7 @@ struct UIColor { _text: Color, } -impl Default for UIColor { +impl Default for UIColors { fn default() -> Self { Self { draw_area: Color::new(0xff, 0x15, 0x15, 0x15), @@ -24,139 +29,63 @@ impl Default for UIColor { } } -pub(super) struct Window<'a, 'b> { - pub(super) draw: DrawTarget<&'a mut [u32]>, - pub(super) options: DrawOptions, - pub(super) colors: UIColor, - pub(super) widgets: Vec>, - pub(super) width: usize, - pub(super) height: usize, - +pub(super) enum Widget { + Button(Button), + TextArea(TextArea) } -impl<'a, 'b> Window<'a, 'b> { - pub(super) fn new(buffer: &'a mut [u32], width: usize, height: usize) -> Self { +pub(super) struct Window { + pub(super) width: usize, + pub(super) height: usize, + pub(super) widgets: Vec, + pub(super) draw_area: DrawArea, + pub(super) colors: UIColors, + pub(super) options: DrawOptions, +} + +impl Window { + pub(super) fn new(width: usize, height: usize, draw_area: DrawArea) -> Self { Self { - draw: DrawTarget::from_backing(width.try_into().unwrap(), height.try_into().unwrap(), buffer), - options: DrawOptions::new(), - colors: UIColor::default(), - widgets: Vec::new(), width, height, + widgets: Vec::new(), + draw_area, + colors: UIColors::default(), + options: DrawOptions::new(), } } - pub(super) fn add_widget(&mut self, widget: Widget<'b>) { + pub(super) fn add_widget(&mut self, widget: Widget) { self.widgets.push(widget); } - pub(super) fn render(&mut self) { + pub(super) fn render(&self, canvas: &mut [u8]) { - self.draw.fill_rect( + // TODO: Make sure this is actually safe + let draw_buffer: &mut [u32] = unsafe {canvas.align_to_mut::().1}; + + let mut target = DrawTarget::from_backing(self.width as i32, self.height as i32, draw_buffer); + + target.fill_rect( 0.0, 0.0, self.width as f32, self.height as f32, &Source::Solid(self.colors.background.into()), &self.options - ); + ); + + self.draw_area.render(&mut target); for widget in self.widgets.iter() { match widget { - Widget::Button(button) => button.render(&mut self.draw), - Widget::DrawArea(draw_area) => draw_area.render(&mut self.draw), - Widget::TextArea(text_area) => text_area.render(&mut self.draw), + Widget::Button(button) => button.render(&mut target), + Widget::TextArea(text_area) => text_area.render(&mut target), } } } } -#[derive(Clone, Copy, Debug)] -pub(super) struct Position { - pub(super) x: f32, - pub(super) y: f32, -} - -pub(super) struct Path(Vec); - -impl IntoIterator for Path { - type Item = Position; - type IntoIter = std::vec::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() - } -} - -impl<'a> IntoIterator for &'a Path { - type Item = &'a Position; - type IntoIter = std::slice::Iter<'a, Position>; - - fn into_iter(self) -> Self::IntoIter { - self.0.iter() - } -} - -pub(super) struct DrawPath { - pub(super) paths: Vec, - pub(super) newline: bool, -} - -impl DrawPath { - pub(super) fn new() -> Self { - Self { - paths: Vec::new(), - newline: false, - } - } - - pub(super) fn add_point(&mut self, point: Position, newline: bool) { - if newline { - self.paths.push(Path(vec![point])) - } else if self.paths.len() > 0 { - self.paths - .last_mut() - .unwrap() - .0 - .push(point); - } - } -} - -impl IntoIterator for DrawPath { - type Item = Path; - type IntoIter = std::vec::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.paths.into_iter() - } -} - -impl<'a> IntoIterator for &'a DrawPath { - type Item = &'a Path; - type IntoIter = std::slice::Iter<'a, Path>; - - fn into_iter(self) -> Self::IntoIter { - self.paths.iter() - } -} - - -impl Position { - pub(super) fn new(x: f32, y: f32) -> Self { - Position { x, y } - } -} - -pub(super) enum Widget<'a> { - Button(Button), - DrawArea(DrawArea<'a>), - TextArea(TextArea) -} - -pub(super) trait Draw { - fn render(&self, area: &mut DrawTarget<&mut [u32]>); -} pub(super) struct ButtonColors { background: Color, @@ -178,6 +107,7 @@ pub(super) struct Button { pub(super) width: f32, pub(super) height: f32, pub(super) position: Position, + pub(super) shape: raqote::Path, pub(super) colors: ButtonColors, pub(super) options: DrawOptions, } @@ -186,208 +116,24 @@ impl Button { fn background_source(&self) -> Source { Source::Solid(self.colors.background.into()) } -} -pub(super) struct ButtonBuilder { - pub(super) width: Option, - pub(super) height: Option, - pub(super) position: Option, - pub(super) colors: Option, - pub(super) options: Option, -} - -impl<'a> ButtonBuilder { - pub(super) fn new() -> Self { - Self { - width: None, - height: None, - position: None, - colors: None, - options: None, - } - } - - pub(super) fn width(&mut self, width: f32) { - self.width = Some(width) - } - - pub(super) fn height(&mut self, height: f32) { - self.height = Some(height) - } - - pub(super) fn position(&mut self, x: f32, y: f32) { - self.position = Some(Position::new(x, y)) - } - - pub(super) fn colors(&mut self, colors: ButtonColors) { - self.colors = Some(colors) - } - - pub(super) fn finish(self) -> Widget<'a> { - Widget::Button( - Button { - width: self.width.expect("Button must have width"), - height: self.height.expect("Button must have height"), - position: self.position.expect("Button must have a position"), - colors: self.colors.unwrap_or_default(), - options: self.options.unwrap_or(DrawOptions::new()), - } - ) + pub(crate) fn contains_point(&self, point: Position) -> bool { + self.shape.contains_point(0.1, point.x, point.y) } } + impl Draw for Button { fn render(&self, area: &mut DrawTarget<&mut [u32]>) { - (*area).fill_rect( - self.position.x, - self.position.y, - self.width, - self.height, - &self.background_source(), - &self.options, - ) - } -} - -pub(super) struct DrawAreaColors { - background_color: Color, - drawing_color: Color, - border_color: Color, -} - -impl Default for DrawAreaColors { - fn default() -> Self { - Self { - background_color: Color::new(0xff, 0x4a, 0x4e, 0x64), - drawing_color: Color::new(0xff, 0xea, 0xea, 0xe6), - border_color: Color::new(0xff, 0x77, 0x7f, 0x97), - } - } -} - -pub(super) struct DrawArea<'a> { - pub(super) width: f32, - pub(super) height: f32, - pub(super) position: Position, - pub(super) path: &'a DrawPath, - pub(super) colors: DrawAreaColors, - pub(super) options: DrawOptions, -} - -impl DrawArea<'_> { - fn background_source(&self) -> Source { - Source::Solid(self.colors.background_color.into()) - } - - fn drawing_source(&self) -> Source { - Source::Solid(self.colors.drawing_color.into()) - } - - fn border_source(&self) -> Source { - Source::Solid(self.colors.border_color.into()) - } -} - - -pub(super) struct DrawAreaBuilder<'a> { - pub(super) width: Option, - pub(super) height: Option, - pub(super) position: Option, - pub(super) path: Option<&'a DrawPath>, - pub(super) colors: Option, - pub(super) options: Option, -} - -impl<'a> DrawAreaBuilder<'a> { - pub(super) fn new() -> Self { - Self { - width: None, - height: None, - position: None, - path: None, - colors: None, - options: None, - } - } - - pub(super) fn width(&mut self, width: f32) { - self.width = Some(width) - } - - pub(super) fn height(&mut self, height: f32) { - self.height = Some(height) - } - - pub(super) fn position(&mut self, x: f32, y: f32) { - self.position = Some(Position::new(x, y)) - } - - pub(super) fn colors(&mut self, colors: DrawAreaColors) { - self.colors = Some(colors) - } - - pub(super) fn path(&mut self, path: &'a DrawPath) { - self.path = Some(path); - } - - pub(super) fn finish(self) -> Widget<'a> { - Widget::DrawArea( - DrawArea { - width: self.width.expect("Draw area must have width"), - height: self.height.expect("Draw area must have height"), - position: self.position.expect("Draw area must have a position"), - path: self.path.expect("Draw must have a path"), - colors: self.colors.unwrap_or_default(), - options: self.options.unwrap_or(DrawOptions::new()), - } - ) - } -} - -impl Draw for DrawArea<'_> { - fn render(&self, area: &mut DrawTarget<&mut [u32]>) { - let mut draw_path = PathBuilder::new(); - - for path in self.path.into_iter() { - if path.0.len() > 1 { - let mut path = path.into_iter(); - - let first_point = path - .by_ref() - .next(); - - draw_path.move_to(first_point.unwrap().x, first_point.unwrap().y); - - for point in path { - draw_path.line_to(point.x, point.y); - } - - } - - } - - - let path = draw_path.finish(); - area.fill_rect( - self.position.x, - self.position.y, - self.width, - self.height, + area.fill( + &self.shape, &self.background_source(), &self.options, ); - - area.stroke( - &path, - &self.drawing_source(), - &StrokeStyle {width: 3.0, ..StrokeStyle::default()}, - &self.options, - ); } } - pub(super) struct TextAreaColors { pub(super) background: Color, pub(super) text: Color, @@ -420,60 +166,9 @@ impl TextArea { } } -pub(super) struct TextAreaBuilder { - pub(super) width: Option, - pub(super) height: Option, - pub(super) position: Option, - pub(super) colors: Option, - pub(super) options: Option, -} - -impl<'a> TextAreaBuilder { - pub(super) fn new() -> Self { - Self { - width: None, - height: None, - position: None, - colors: None, - options: None, - } - } - - pub(super) fn width(&mut self, width: f32) { - self.width = Some(width) - } - - pub(super) fn height(&mut self, height: f32) { - self.height = Some(height) - } - - pub(super) fn position(&mut self, x: f32, y: f32) { - self.position = Some(Position::new(x, y)) - } - - - pub(super) fn colors(&mut self, colors: TextAreaColors) { - self.colors = Some(colors); - } - - pub(super) fn finish(self) -> Widget<'a> { - Widget::TextArea( - TextArea { - width: self.width.expect("Button must have width"), - height: self.height.expect("Button must have height"), - position: self.position.expect("Button must have a position"), - colors: self.colors.unwrap_or_default(), - options: self.options.unwrap_or(DrawOptions::new()), - } - ) - } -} - impl Draw for TextArea { fn render(&self, area: &mut DrawTarget<&mut [u32]>) { - - area.fill_rect( self.position.x, self.position.y, @@ -482,7 +177,6 @@ impl Draw for TextArea { &self.background_source(), &self.options, ); - } }