diff --git a/Cargo.lock b/Cargo.lock index f164bbd..88f8309 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2258,11 +2258,15 @@ name = "weft-compositor" version = "0.1.0" dependencies = [ "anyhow", + "bitflags 2.11.0", "sd-notify", "smithay", "smithay-drm-extras", "tracing", "tracing-subscriber", + "wayland-backend", + "wayland-scanner", + "wayland-server", ] [[package]] diff --git a/crates/weft-compositor/Cargo.toml b/crates/weft-compositor/Cargo.toml index 4be15c6..5f1406b 100644 --- a/crates/weft-compositor/Cargo.toml +++ b/crates/weft-compositor/Cargo.toml @@ -21,6 +21,10 @@ smithay = { version = "0.7", default-features = false, features = [ tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] } anyhow = "1" +wayland-scanner = "0.31" +wayland-server = "0.31" +wayland-backend = "0.3" +bitflags = "2" # DRM/KMS and hardware input depend on Linux kernel interfaces; compile only on Linux. [target.'cfg(target_os = "linux")'.dependencies] diff --git a/crates/weft-compositor/src/main.rs b/crates/weft-compositor/src/main.rs index 90130c5..6f4412a 100644 --- a/crates/weft-compositor/src/main.rs +++ b/crates/weft-compositor/src/main.rs @@ -2,6 +2,7 @@ use tracing_subscriber::EnvFilter; mod backend; mod input; +mod protocols; mod state; fn main() -> anyhow::Result<()> { diff --git a/crates/weft-compositor/src/protocols/mod.rs b/crates/weft-compositor/src/protocols/mod.rs new file mode 100644 index 0000000..4394037 --- /dev/null +++ b/crates/weft-compositor/src/protocols/mod.rs @@ -0,0 +1,42 @@ +#[allow(dead_code, non_camel_case_types, unused_unsafe, unused_variables)] +#[allow(non_upper_case_globals, non_snake_case, unused_imports)] +#[allow(missing_docs, clippy::all)] +pub mod server { + use wayland_server; + use wayland_server::protocol::*; + + pub mod __interfaces { + use wayland_server::protocol::__interfaces::*; + wayland_scanner::generate_interfaces!("../../protocol/weft-shell-unstable-v1.xml"); + } + use self::__interfaces::*; + + wayland_scanner::generate_server_code!("../../protocol/weft-shell-unstable-v1.xml"); +} + +pub use server::zweft_shell_manager_v1::ZweftShellManagerV1; +pub use server::zweft_shell_window_v1::ZweftShellWindowV1; + +use wayland_server::{DisplayHandle, GlobalDispatch, backend::GlobalId}; + +pub struct WeftShellState { + _global: GlobalId, +} + +#[allow(dead_code)] +pub struct WeftShellWindowData { + pub app_id: String, + pub title: String, + pub role: String, +} + +impl WeftShellState { + pub fn new(display: &DisplayHandle) -> Self + where + D: GlobalDispatch, + D: 'static, + { + let global = display.create_global::(1, ()); + Self { _global: global } + } +} diff --git a/crates/weft-compositor/src/state.rs b/crates/weft-compositor/src/state.rs index e5c17cb..2a735e8 100644 --- a/crates/weft-compositor/src/state.rs +++ b/crates/weft-compositor/src/state.rs @@ -1,5 +1,9 @@ #[cfg(target_os = "linux")] use crate::backend::drm_device::WeftDrmData; +use crate::protocols::{ + WeftShellState, WeftShellWindowData, ZweftShellManagerV1, ZweftShellWindowV1, + server::{zweft_shell_manager_v1, zweft_shell_window_v1}, +}; use smithay::{ backend::{input::TabletToolDescriptor, renderer::utils::on_commit_buffer_handler}, @@ -15,7 +19,7 @@ use smithay::{ reexports::{ calloop::{LoopHandle, LoopSignal}, wayland_server::{ - Client, DisplayHandle, + Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, backend::{ClientData, ClientId, DisconnectReason}, protocol::{wl_buffer::WlBuffer, wl_output::WlOutput, wl_surface::WlSurface}, }, @@ -89,6 +93,9 @@ pub struct WeftCompositorState { // Set to false when the compositor should exit the event loop. pub running: bool, + // WEFT compositor–shell protocol global. + pub weft_shell_state: WeftShellState, + #[cfg(target_os = "linux")] pub drm: Option, } @@ -113,6 +120,7 @@ impl WeftCompositorState { InputMethodManagerState::new::(&display_handle, |_client| true); let pointer_constraints_state = PointerConstraintsState::new::(&display_handle); let cursor_shape_state = CursorShapeManagerState::new::(&display_handle); + let weft_shell_state = WeftShellState::new::(&display_handle); let mut seat_state = SeatState::new(); let mut seat = seat_state.new_wl_seat(&display_handle, seat_name); @@ -136,6 +144,7 @@ impl WeftCompositorState { input_method_state, pointer_constraints_state, cursor_shape_state, + weft_shell_state, space: Space::default(), popups: PopupManager::default(), seat_state, @@ -404,3 +413,81 @@ impl TabletSeatHandler for WeftCompositorState { // CursorShapeManagerState has no handler trait; it calls SeatHandler::cursor_image directly. delegate_cursor_shape!(WeftCompositorState); + +// --- weft-shell-protocol --- + +impl GlobalDispatch for WeftCompositorState { + fn bind( + _state: &mut Self, + _handle: &DisplayHandle, + _client: &Client, + resource: New, + _global_data: &(), + data_init: &mut DataInit<'_, Self>, + ) { + data_init.init(resource, ()); + } +} + +impl Dispatch for WeftCompositorState { + fn request( + _state: &mut Self, + _client: &Client, + _resource: &ZweftShellManagerV1, + request: zweft_shell_manager_v1::Request, + _data: &(), + _dh: &DisplayHandle, + data_init: &mut DataInit<'_, Self>, + ) { + match request { + zweft_shell_manager_v1::Request::Destroy => {} + zweft_shell_manager_v1::Request::CreateWindow { + id, + app_id, + title, + role, + x, + y, + width, + height, + } => { + let window = data_init.init( + id, + WeftShellWindowData { + app_id, + title, + role, + }, + ); + window.configure(x, y, width, height, 0); + } + } + } +} + +impl Dispatch for WeftCompositorState { + fn request( + _state: &mut Self, + _client: &Client, + resource: &ZweftShellWindowV1, + request: zweft_shell_window_v1::Request, + _data: &WeftShellWindowData, + _dh: &DisplayHandle, + _data_init: &mut DataInit<'_, Self>, + ) { + match request { + zweft_shell_window_v1::Request::Destroy => {} + zweft_shell_window_v1::Request::UpdateMetadata { title, role } => { + let _ = (title, role); + } + zweft_shell_window_v1::Request::SetGeometry { + x, + y, + width, + height, + } => { + resource.configure(x, y, width, height, 0); + } + } + } +} diff --git a/protocol/weft-shell-unstable-v1.xml b/protocol/weft-shell-unstable-v1.xml new file mode 100644 index 0000000..e7e6150 --- /dev/null +++ b/protocol/weft-shell-unstable-v1.xml @@ -0,0 +1,196 @@ + + + + + Copyright (C) 2026 WEFT OS contributors + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice (including the + next paragraph) shall be included in all copies or substantial portions + of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + + + This protocol defines the boundary between weft-compositor and + weft-servo-shell. It allows the shell to manage window slots on behalf + of application surfaces that the compositor owns. + + The shell binds the weft_shell_manager_v1 global once per connection. + The manager creates weft_shell_window_v1 objects, each representing one + visual window slot. The compositor is authoritative for geometry, + focus, and stacking. The shell is authoritative for window metadata, + chrome layout, and the decision to create or destroy a window slot. + + Warning: This protocol is unstable and subject to change before version + 1 is finalized. Clients must check the version advertised by the + compositor and handle unknown events gracefully. + + + + + + + + Bound once by weft-servo-shell. The manager owns the shell session. + If the binding is destroyed the compositor invalidates all window + objects that were created through it. + + + + + Destroys the manager. All weft_shell_window_v1 objects created + through this manager become inert. The compositor will emit + window_closed on each before processing the destructor. + + + + + + Creates a new window slot. The compositor assigns the protocol + object identity; no separate window_id is exchanged. The + compositor will emit configure on the new object to communicate the + effective initial geometry and state. + + + + + + + + + + + + + + + + + + + Represents one visual window. The compositor controls the effective + geometry, stacking, and focus state. The shell controls metadata and + may request geometry changes; the compositor may reject or adjust + such requests. + + A window object becomes inert after window_closed is received. The + shell must call destroy on an inert object as soon as possible. + + + + + + + + + + + + + + + + + + Destroys the window object. If the window is still alive the + compositor will remove the corresponding window slot from the + display. The shell must not send requests on this object after + calling destroy. + + + + + + Updates advisory metadata. The compositor may use these values in + window decorations or accessibility trees. Updates are not + authoritative for policy decisions. + + + + + + + + Asks the compositor to change the window geometry. The compositor + validates the request against output bounds and current policy. The + compositor is not required to honor the exact values. A configure + event will be sent with the effective geometry that results. + + + + + + + + + + Sent by the compositor when the effective geometry or state + changes. The shell must treat this as authoritative and update its + DOM layout to match. The shell must ack each configure event by + sending an update_metadata request or by doing nothing if no + metadata changed; no explicit ack request is defined in version 1. + + + + + + + + + + + Sent when surface-level focus changes for this window slot. The + shell updates visual focus state in its DOM but does not override + compositor focus policy. + + + + + + + Sent by the compositor when the window object is being invalidated. + After this event the shell must call destroy on the object. Any + request sent after window_closed is a protocol error. + + + + + + Sent after a frame containing this window's content is presented + to the output. Allows the shell to align animations and resize + flows with compositor timing. tv_sec and tv_nsec are wall-clock + values from the compositor's monotonic clock. refresh is the + output refresh interval in nanoseconds; 0 if unknown. + + + + + + + + +