#wayland #minimalist #resize #borders #surface #top #built

wayland-window

A minimalistic window-decorations library built on top of wayland-client

26 releases (12 breaking)

Uses old Rust 2015

0.13.3 Mar 26, 2018
0.13.2 Nov 5, 2017
0.12.0 Oct 29, 2017
0.7.0 Jul 5, 2017
0.2.0 Nov 30, 2015

#1091 in GUI

Download history 2044/week @ 2023-12-12 1972/week @ 2023-12-19 1277/week @ 2023-12-26 856/week @ 2024-01-02 2209/week @ 2024-01-09 1938/week @ 2024-01-16 1309/week @ 2024-01-23 1026/week @ 2024-01-30 1815/week @ 2024-02-06 2252/week @ 2024-02-13 1638/week @ 2024-02-20 2393/week @ 2024-02-27 2244/week @ 2024-03-05 1969/week @ 2024-03-12 2868/week @ 2024-03-19 2413/week @ 2024-03-26

9,960 downloads per month

MIT license

52KB
1K SLoC

wayland-window

A simple window-decorations library built on top of wayland-client.

It draws simplistic decorations around a given wayland surface, and registers callbacks to allow resizing and moving the surface useing the borders.

It is currently more aiming at usability than design quality, so the drawn borders are plain grey and not customizable. I'll possible improve in future releases.

Usage

Instructions of use are on the main page of the documentation, readable on docs.rs.

Docs for the master branch are also available online: https://smithay.github.io/wayland-window


lib.rs:

Wayland Window, a minimalistic decoration-drawing library for wayland applications.

This crate is only usable in conjuction of the wayland-client crate.

Creating a window with decorations

Creating a decorated frame for your window is simply done using the provided init function:

use wayland_window::create_frame;
// if using the legacy wl_shell global
let shell = Shell::Wl(my_wl_shell);
// if using the new not-yet-stable xdg_shell
let shell = Shell::Xdg(my_xdh_shell);
let frame = create_frame(
       &mut event_queue, my_implementation, my_implementation_data,
       &my_surface, width, height, &compositor, &subcompositor, &shm, &shell, Some(seat)
).unwrap(); // creation can fail

As you can see, you need to pass several references to global objects as well as a WlSeat. It is required for the library to be able to create the surfaces to draw the borders, react to user input in the borders, for resizing and move. It will use the events provided on the seat you passed as argument. (So if you are on a setup with more than one pointer, only the one associated with this seat will be able to resize the window).

See next section for example use of the my_implementation and my_implementation_data arguments.

Configure events

The Frame object will not resize your window itself, as it cannot do it.

When the user clicks on a border and starts a resize, the server will start to generate a number of configure events on the shell surface. You'll need to process the events generated by the surface to handle them.

The wayland server can (and will) generate a ton of configure events during a single WlDisplay::dispatch() if the user is currently resizing the window. You are only required to process the last one, and if you try to handle them all your aplication will be very laggy.

The proper way is to accumulate them in your subhandler, overwriting the the previous one each time, and manually checking if one has been received in the main loop of your program. For example like this

use wayland_window::{Frame, create_frame, FrameImplementation};

// define a state to accumulate sizes
struct ConfigureState {
    new_size: Option<(i32,i32)>
}

// insert it in your event queue state
let configure_token = event_queue.state().insert(ConfigureState { new_size: None });

// use it in your implementation:
let my_implementation = FrameImplementation {
    configure: |evqh, token, _, newsize| {
        let configure_state: &mut ConfigureState = evqh.state().get_mut(token);
        configure_state.new_size = newsize;
    },
    close: |_, _| { /* ... */ },
    refresh: |_, _| { /* ... */ }
};

// create the decorated surface:
let frame = create_frame(
    &mut event_queue,          // the event queue
    my_implementation,         // our implementation
    configure_token.clone(),   // the implementation data
    &my_surface, width, height, &compositor, &subcompositor, &shm, &shell, Some(seat)
).unwrap();

// then, while running your event loop
loop {
    display.flush().unwrap();
    event_queue.dispatch().unwrap();

    // check if a resize is needed
    let mut configure_state = event_queue.state().get_mut(&configure_token);
    if let Some((w, h)) = configure_state.new_size.take() {
        // The compositor suggests we take a new size of (w, h)
        // Handle it as needed (see next section)
    }
}

Resizing the surface

When resizing your main surface, you need to tell the Frame that it must update its dimensions. This is very simple:

// update your contents size (here by attaching a new buffer)
surface.attach(Some(&new_buffer));
surface.commit();
// update the borders size
frame.resize(width, height);
// refresh the frame so that it actually draws the new size
frame.refresh();

If you do this as a response of a configure event, note the following points:

  • You do not have to respect the exact sizes provided by the compositor, it is just a hint. You can even ignore it if you don't want the window to be resized.
  • In case you chose to ignore the resize, it can be appropiate to still resize your window to its current size (update the buffer to the compositor), as the compositer might have resized your window without telling you.
  • The size hint provided to your implementation is a size hint for the interior of the window: the dimensions of the border has been subtracted from the hint the compositor gave. If you need to compute dimensions taking into account the sizes of the borders, you can use the add_borders and subtract_borders functions.

Dependencies

~2–12MB
~122K SLoC