1157 lines · 38.2 KB · Rust
RawDownload
use std::{    cell::{Ref, RefCell, RefMut},    ffi::c_void,    ptr::NonNull,    rc::Rc,    sync::Arc,}; use blade_graphics as gpu;use collections::HashMap;use futures::channel::oneshot::Receiver; use raw_window_handle as rwh;use wayland_backend::client::ObjectId;use wayland_client::WEnum;use wayland_client::{protocol::wl_surface, Proxy};use wayland_protocols::wp::fractional_scale::v1::client::wp_fractional_scale_v1;use wayland_protocols::wp::viewporter::client::wp_viewport;use wayland_protocols::xdg::decoration::zv1::client::zxdg_toplevel_decoration_v1;use wayland_protocols::xdg::shell::client::xdg_surface;use wayland_protocols::xdg::shell::client::xdg_toplevel::{self};use wayland_protocols_plasma::blur::client::org_kde_kwin_blur; use crate::platform::{    blade::{BladeContext, BladeRenderer, BladeSurfaceConfig},    linux::wayland::{display::WaylandDisplay, serial::SerialKind},    PlatformAtlas, PlatformInputHandler, PlatformWindow,};use crate::scene::Scene;use crate::{    px, size, AnyWindowHandle, Bounds, Decorations, Globals, GpuSpecs, Modifiers, Output, Pixels,    PlatformDisplay, PlatformInput, Point, PromptLevel, RequestFrameOptions, ResizeEdge,    ScaledPixels, Size, Tiling, WaylandClientStatePtr, WindowAppearance,    WindowBackgroundAppearance, WindowBounds, WindowControls, WindowDecorations, WindowParams,}; #[derive(Default)]pub(crate) struct Callbacks {    request_frame: Option<Box<dyn FnMut(RequestFrameOptions)>>,    input: Option<Box<dyn FnMut(crate::PlatformInput) -> crate::DispatchEventResult>>,    active_status_change: Option<Box<dyn FnMut(bool)>>,    hover_status_change: Option<Box<dyn FnMut(bool)>>,    resize: Option<Box<dyn FnMut(Size<Pixels>, f32)>>,    moved: Option<Box<dyn FnMut()>>,    should_close: Option<Box<dyn FnMut() -> bool>>,    close: Option<Box<dyn FnOnce()>>,    appearance_changed: Option<Box<dyn FnMut()>>,} struct RawWindow {    window: *mut c_void,    display: *mut c_void,} impl rwh::HasWindowHandle for RawWindow {    fn window_handle(&self) -> Result<rwh::WindowHandle<'_>, rwh::HandleError> {        let window = NonNull::new(self.window).unwrap();        let handle = rwh::WaylandWindowHandle::new(window);        Ok(unsafe { rwh::WindowHandle::borrow_raw(handle.into()) })    }}impl rwh::HasDisplayHandle for RawWindow {    fn display_handle(&self) -> Result<rwh::DisplayHandle<'_>, rwh::HandleError> {        let display = NonNull::new(self.display).unwrap();        let handle = rwh::WaylandDisplayHandle::new(display);        Ok(unsafe { rwh::DisplayHandle::borrow_raw(handle.into()) })    }} #[derive(Debug)]struct InProgressConfigure {    size: Option<Size<Pixels>>,    fullscreen: bool,    maximized: bool,    tiling: Tiling,} pub struct WaylandWindowState {    xdg_surface: xdg_surface::XdgSurface,    acknowledged_first_configure: bool,    pub surface: wl_surface::WlSurface,    decoration: Option<zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1>,    app_id: Option<String>,    appearance: WindowAppearance,    blur: Option<org_kde_kwin_blur::OrgKdeKwinBlur>,    toplevel: xdg_toplevel::XdgToplevel,    viewport: Option<wp_viewport::WpViewport>,    outputs: HashMap<ObjectId, Output>,    display: Option<(ObjectId, Output)>,    globals: Globals,    renderer: BladeRenderer,    bounds: Bounds<Pixels>,    scale: f32,    input_handler: Option<PlatformInputHandler>,    decorations: WindowDecorations,    background_appearance: WindowBackgroundAppearance,    fullscreen: bool,    maximized: bool,    tiling: Tiling,    window_bounds: Bounds<Pixels>,    client: WaylandClientStatePtr,    handle: AnyWindowHandle,    active: bool,    hovered: bool,    in_progress_configure: Option<InProgressConfigure>,    in_progress_window_controls: Option<WindowControls>,    window_controls: WindowControls,    inset: Option<Pixels>,} #[derive(Clone)]pub struct WaylandWindowStatePtr {    state: Rc<RefCell<WaylandWindowState>>,    callbacks: Rc<RefCell<Callbacks>>,} impl WaylandWindowState {    #[allow(clippy::too_many_arguments)]    pub(crate) fn new(        handle: AnyWindowHandle,        surface: wl_surface::WlSurface,        xdg_surface: xdg_surface::XdgSurface,        toplevel: xdg_toplevel::XdgToplevel,        decoration: Option<zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1>,        appearance: WindowAppearance,        viewport: Option<wp_viewport::WpViewport>,        client: WaylandClientStatePtr,        globals: Globals,        gpu_context: &BladeContext,        options: WindowParams,    ) -> anyhow::Result<Self> {        let renderer = {            let raw_window = RawWindow {                window: surface.id().as_ptr().cast::<c_void>(),                display: surface                    .backend()                    .upgrade()                    .unwrap()                    .display_ptr()                    .cast::<c_void>(),            };            let config = BladeSurfaceConfig {                size: gpu::Extent {                    width: options.bounds.size.width.0 as u32,                    height: options.bounds.size.height.0 as u32,                    depth: 1,                },                transparent: true,            };            BladeRenderer::new(gpu_context, &raw_window, config)?        };         Ok(Self {            xdg_surface,            acknowledged_first_configure: false,            surface,            decoration,            app_id: None,            blur: None,            toplevel,            viewport,            globals,            outputs: HashMap::default(),            display: None,            renderer,            bounds: options.bounds,            scale: 1.0,            input_handler: None,            decorations: WindowDecorations::Client,            background_appearance: WindowBackgroundAppearance::Opaque,            fullscreen: false,            maximized: false,            tiling: Tiling::default(),            window_bounds: options.bounds,            in_progress_configure: None,            client,            appearance,            handle,            active: false,            hovered: false,            in_progress_window_controls: None,            window_controls: WindowControls::default(),            inset: None,        })    }     pub fn is_transparent(&self) -> bool {        self.decorations == WindowDecorations::Client            || self.background_appearance != WindowBackgroundAppearance::Opaque    }     pub fn primary_output_scale(&mut self) -> i32 {        let mut scale = 1;        let mut current_output = self.display.take();        for (id, output) in self.outputs.iter() {            if let Some((_, output_data)) = &current_output {                if output.scale > output_data.scale {                    current_output = Some((id.clone(), output.clone()));                }            } else {                current_output = Some((id.clone(), output.clone()));            }            scale = scale.max(output.scale);        }        self.display = current_output;        scale    }} pub(crate) struct WaylandWindow(pub WaylandWindowStatePtr);pub enum ImeInput {    InsertText(String),    SetMarkedText(String),    UnmarkText,    DeleteText,} impl Drop for WaylandWindow {    fn drop(&mut self) {        let mut state = self.0.state.borrow_mut();        let surface_id = state.surface.id();        let client = state.client.clone();         state.renderer.destroy();        if let Some(decoration) = &state.decoration {            decoration.destroy();        }        if let Some(blur) = &state.blur {            blur.release();        }        state.toplevel.destroy();        if let Some(viewport) = &state.viewport {            viewport.destroy();        }        state.xdg_surface.destroy();        state.surface.destroy();         let state_ptr = self.0.clone();        state            .globals            .executor            .spawn(async move {                state_ptr.close();                client.drop_window(&surface_id)            })            .detach();        drop(state);    }} impl WaylandWindow {    fn borrow(&self) -> Ref<WaylandWindowState> {        self.0.state.borrow()    }     fn borrow_mut(&self) -> RefMut<WaylandWindowState> {        self.0.state.borrow_mut()    }     pub fn new(        handle: AnyWindowHandle,        globals: Globals,        gpu_context: &BladeContext,        client: WaylandClientStatePtr,        params: WindowParams,        appearance: WindowAppearance,    ) -> anyhow::Result<(Self, ObjectId)> {        let surface = globals.compositor.create_surface(&globals.qh, ());        let xdg_surface = globals            .wm_base            .get_xdg_surface(&surface, &globals.qh, surface.id());        let toplevel = xdg_surface.get_toplevel(&globals.qh, surface.id());         if let Some(size) = params.window_min_size {            toplevel.set_min_size(size.width.0 as i32, size.height.0 as i32);        }         if let Some(fractional_scale_manager) = globals.fractional_scale_manager.as_ref() {            fractional_scale_manager.get_fractional_scale(&surface, &globals.qh, surface.id());        }         // Attempt to set up window decorations based on the requested configuration        let decoration = globals            .decoration_manager            .as_ref()            .map(|decoration_manager| {                decoration_manager.get_toplevel_decoration(&toplevel, &globals.qh, surface.id())            });         let viewport = globals            .viewporter            .as_ref()            .map(|viewporter| viewporter.get_viewport(&surface, &globals.qh, ()));         let this = Self(WaylandWindowStatePtr {            state: Rc::new(RefCell::new(WaylandWindowState::new(                handle,                surface.clone(),                xdg_surface,                toplevel,                decoration,                appearance,                viewport,                client,                globals,                gpu_context,                params,            )?)),            callbacks: Rc::new(RefCell::new(Callbacks::default())),        });         // Kick things off        surface.commit();         Ok((this, surface.id()))    }} impl WaylandWindowStatePtr {    pub fn handle(&self) -> AnyWindowHandle {        self.state.borrow().handle    }     pub fn surface(&self) -> wl_surface::WlSurface {        self.state.borrow().surface.clone()    }     pub fn ptr_eq(&self, other: &Self) -> bool {        Rc::ptr_eq(&self.state, &other.state)    }     pub fn frame(&self) {        let mut state = self.state.borrow_mut();        state.surface.frame(&state.globals.qh, state.surface.id());        drop(state);         let mut cb = self.callbacks.borrow_mut();        if let Some(fun) = cb.request_frame.as_mut() {            fun(Default::default());        }    }     pub fn handle_xdg_surface_event(&self, event: xdg_surface::Event) {        match event {            xdg_surface::Event::Configure { serial } => {                {                    let mut state = self.state.borrow_mut();                    if let Some(window_controls) = state.in_progress_window_controls.take() {                        state.window_controls = window_controls;                         drop(state);                        let mut callbacks = self.callbacks.borrow_mut();                        if let Some(appearance_changed) = callbacks.appearance_changed.as_mut() {                            appearance_changed();                        }                    }                }                {                    let mut state = self.state.borrow_mut();                     if let Some(mut configure) = state.in_progress_configure.take() {                        let got_unmaximized = state.maximized && !configure.maximized;                        state.fullscreen = configure.fullscreen;                        state.maximized = configure.maximized;                        state.tiling = configure.tiling;                        if !configure.fullscreen && !configure.maximized {                            configure.size = if got_unmaximized {                                Some(state.window_bounds.size)                            } else {                                compute_outer_size(state.inset, configure.size, state.tiling)                            };                            if let Some(size) = configure.size {                                state.window_bounds = Bounds {                                    origin: Point::default(),                                    size,                                };                            }                        }                        drop(state);                        if let Some(size) = configure.size {                            self.resize(size);                        }                    }                }                let mut state = self.state.borrow_mut();                state.xdg_surface.ack_configure(serial);                 let window_geometry = inset_by_tiling(                    state.bounds.map_origin(|_| px(0.0)),                    state.inset.unwrap_or(px(0.0)),                    state.tiling,                )                .map(|v| v.0 as i32)                .map_size(|v| if v <= 0 { 1 } else { v });                 state.xdg_surface.set_window_geometry(                    window_geometry.origin.x,                    window_geometry.origin.y,                    window_geometry.size.width,                    window_geometry.size.height,                );                 let request_frame_callback = !state.acknowledged_first_configure;                if request_frame_callback {                    state.acknowledged_first_configure = true;                    drop(state);                    self.frame();                }            }            _ => {}        }    }     pub fn handle_toplevel_decoration_event(&self, event: zxdg_toplevel_decoration_v1::Event) {        match event {            zxdg_toplevel_decoration_v1::Event::Configure { mode } => match mode {                WEnum::Value(zxdg_toplevel_decoration_v1::Mode::ServerSide) => {                    self.state.borrow_mut().decorations = WindowDecorations::Server;                    if let Some(mut appearance_changed) =                        self.callbacks.borrow_mut().appearance_changed.as_mut()                    {                        appearance_changed();                    }                }                WEnum::Value(zxdg_toplevel_decoration_v1::Mode::ClientSide) => {                    self.state.borrow_mut().decorations = WindowDecorations::Client;                    // Update background to be transparent                    if let Some(mut appearance_changed) =                        self.callbacks.borrow_mut().appearance_changed.as_mut()                    {                        appearance_changed();                    }                }                WEnum::Value(_) => {                    log::warn!("Unknown decoration mode");                }                WEnum::Unknown(v) => {                    log::warn!("Unknown decoration mode: {}", v);                }            },            _ => {}        }    }     pub fn handle_fractional_scale_event(&self, event: wp_fractional_scale_v1::Event) {        match event {            wp_fractional_scale_v1::Event::PreferredScale { scale } => {                self.rescale(scale as f32 / 120.0);            }            _ => {}        }    }     pub fn handle_toplevel_event(&self, event: xdg_toplevel::Event) -> bool {        match event {            xdg_toplevel::Event::Configure {                width,                height,                states,            } => {                let mut size = if width == 0 || height == 0 {                    None                } else {                    Some(size(px(width as f32), px(height as f32)))                };                 let states = extract_states::<xdg_toplevel::State>(&states);                 let mut tiling = Tiling::default();                let mut fullscreen = false;                let mut maximized = false;                 for state in states {                    match state {                        xdg_toplevel::State::Maximized => {                            maximized = true;                        }                        xdg_toplevel::State::Fullscreen => {                            fullscreen = true;                        }                        xdg_toplevel::State::TiledTop => {                            tiling.top = true;                        }                        xdg_toplevel::State::TiledLeft => {                            tiling.left = true;                        }                        xdg_toplevel::State::TiledRight => {                            tiling.right = true;                        }                        xdg_toplevel::State::TiledBottom => {                            tiling.bottom = true;                        }                        _ => {                            // noop                        }                    }                }                 if fullscreen || maximized {                    tiling = Tiling::tiled();

Lines 1–500 of 1157.