wingy 0.31.1

Cross-platform window creation library.
Documentation
use std::error::Error;

use softbuffer::{Context, Surface};
use tracing::{error, info};
use wingy::application::ApplicationHandler;
use wingy::data_transfer::{DataTransferSendBuilder, TypeHint};
use wingy::event::{ButtonSource, MouseButton, WindowEvent};
use wingy::event_loop::{ActiveEventLoop, AsyncRequestSerial, EventLoop, OwnedDisplayHandle};
use wingy::keyboard::Key;
use wingy::window::{Window, WindowAttributes, WindowId};

#[path = "util/fill.rs"]
mod fill;
#[path = "util/tracing.rs"]
mod tracing;

fn main() -> Result<(), Box<dyn Error>> {
    tracing::init();

    info!("Press 'C' to copy to the clipboard.");
    info!("Press 'V' to paste from the clipboard.");
    info!("Press 'P' to copy to the primary selection (Wayland and X11).");
    info!("Middle-click to paste from the primary selection (Wayland and X11).");

    let event_loop = EventLoop::new()?;

    let app = Application::new();
    Ok(event_loop.run_app(app)?)
}

#[derive(Debug)]
struct Application {
    surface: Option<Surface<OwnedDisplayHandle, Box<dyn Window>>>,
    last_clipboard_fetch: Option<AsyncRequestSerial>,
}

impl Application {
    fn new() -> Self {
        Self { surface: None, last_clipboard_fetch: None }
    }
}

impl ApplicationHandler for Application {
    fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) {
        let window_attributes = WindowAttributes::default().with_title("Wingy clipboard example");
        let window = event_loop.create_window(window_attributes).unwrap();
        let context = Context::new(event_loop.owned_display_handle()).unwrap();
        let surface = Surface::new(&context, window).unwrap();
        self.surface = Some(surface);
    }

    fn window_event(
        &mut self,
        event_loop: &dyn ActiveEventLoop,
        _window_id: WindowId,
        event: WindowEvent,
    ) {
        match event {
            WindowEvent::KeyboardInput { event, .. } if event.state.is_pressed() => {
                match event.logical_key {
                    Key::Character(ref c) if c == "c" => {
                        let send_data = DataTransferSendBuilder::new("Wingy example".to_string())
                            .with_type(TypeHint::Plaintext, |text: &String, _| Some(text.clone()))
                            .with_type(TypeHint::Html, |text, _| {
                                Some(format!("<strong>{text}</strong>"))
                            })
                            .build();

                        match event_loop.set_clipboard(send_data) {
                            Ok(()) => info!("Copied"),
                            Err(err) => error!("Failed to set clipboard: {err}"),
                        }
                    },
                    Key::Character(ref c) if c == "v" => {
                        let result = event_loop.clipboard().and_then(|id| {
                            let Some(id) = id else {
                                info!("Clipboard is empty");
                                return Ok(None);
                            };

                            let data_transfer = event_loop.data_transfer(id)?;

                            info!("Types: {:#?}", data_transfer.available_types());

                            event_loop.fetch_data_transfer(id, &TypeHint::Plaintext).map(Some)
                        });

                        match result {
                            Ok(serial) => self.last_clipboard_fetch = serial,
                            Err(err) => error!("Failed to read clipboard: {err}"),
                        }
                    },
                    Key::Character(ref c) if c == "p" => {
                        let Some(ext) = event_loop.primary_selection_ext() else {
                            info!("Primary selection is not supported");
                            return;
                        };

                        let send_data = DataTransferSendBuilder::new(
                            "Wingy primary selection example".to_string(),
                        )
                        .with_type(TypeHint::Plaintext, |text: &String, _| Some(text.clone()))
                        .with_type(TypeHint::Html, |text, _| {
                            Some(format!("<strong>{text}</strong>"))
                        })
                        .build();

                        match ext.set_primary_selection(send_data) {
                            Ok(()) => info!("Copied to primary selection"),
                            Err(err) => error!("Failed to set primary selection: {err}"),
                        }
                    },
                    _ => {},
                }
            },
            WindowEvent::PointerButton {
                button: ButtonSource::Mouse(MouseButton::Middle),
                state,
                ..
            } if state.is_pressed() => {
                let Some(ext) = event_loop.primary_selection_ext() else {
                    info!("Primary selection is not supported");
                    return;
                };

                let result = ext.primary_selection().and_then(|id| {
                    let Some(id) = id else {
                        info!("Primary selection is empty");
                        return Ok(None);
                    };

                    let data_transfer = event_loop.data_transfer(id)?;

                    info!("Types: {:#?}", data_transfer.available_types());

                    event_loop.fetch_data_transfer(id, &TypeHint::Plaintext).map(Some)
                });

                match result {
                    Ok(serial) => self.last_clipboard_fetch = serial,
                    Err(err) => error!("Failed to read primary selection: {err}"),
                }
            },
            WindowEvent::DataTransferReceived { ref value, serial, .. } => {
                assert_eq!(self.last_clipboard_fetch, Some(serial));

                match value.type_().hint() {
                    Some(TypeHint::Plaintext) => {
                        let Ok(text) = value.try_as_string() else {
                            return;
                        };
                        info!("Pasted {text:?}");
                    },
                    _ => {
                        unreachable!("Received a type we didn't ask for!");
                    },
                }
            },
            WindowEvent::RedrawRequested => {
                let surface = self.surface.as_mut().unwrap();
                surface.window().pre_present_notify();
                fill::fill(surface);
            },
            WindowEvent::CloseRequested => {
                event_loop.exit();
            },
            _ => {},
        }
    }
}