Update SCTK to 0.11.0

* Update SCTK to 0.11.0

Updates smithay-client-toolkit to 0.11.0. The major highlight
of that updated, is update of wayland-rs to 0.27.0. Switching
to wayland-cursor, instead of using libwayland-cursor. It
also fixes the following bugs:

  - Disabled repeat rate not being handled.
  - Decoration buttons not working after tty switch.
  - Scaling not being applied on output reenable.
  - Crash when `XCURSOR_SIZE` is `0`.
  - Pointer getting created in some cases without pointer capability.
  - On kwin, fix space between window and decorations on startup.
  - Incorrect size event when entering fullscreen when using
    client side decorations.
  - Client side decorations not being hided properly in fullscreen.
  - Size tracking between fullscreen/tiled state changes.
  - Repeat rate triggering multiple times from slow callback handler.
  - Resizable attribute not being applied properly on startup.
  - Not working IME

Besides those fixes it also adds a bunch of missing virtual key codes,
implements proper cursor grabbing, adds right click on decorations
to open application menu, disabled maximize button for non-resizeable
window, and fall back for cursor icon to similar ones, if the requested
is missing.

It also adds new methods to a `Theme` trait, such as:
  - `title_font(&self) -> Option<(String, f32)>` - The font for a title.
  - `title_color(&self, window_active: bool) -> [u8; 4]` - The color of
  the text in the title.

Fixes #1680.
Fixes #1678.
Fixes #1676.
Fixes #1646.
Fixes #1614.
Fixes #1601.
Fixes #1533.
Fixes #1509.
Fixes #952.
Fixes #947.
This commit is contained in:
Kirill Chibisov 2020-09-29 00:11:43 +03:00 committed by GitHub
parent 471b1e003a
commit 3d85af04be
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
31 changed files with 3791 additions and 2630 deletions

View file

@ -0,0 +1,388 @@
use std::cell::Cell;
use std::sync::{Arc, Mutex};
use sctk::reexports::client::protocol::wl_output::WlOutput;
use sctk::window::{ConceptConfig, ConceptFrame, Decorations, Window};
use crate::dpi::{LogicalPosition, LogicalSize};
use crate::event::WindowEvent;
use crate::platform_impl::wayland::event_loop::WinitState;
use crate::platform_impl::wayland::seat::pointer::WinitPointer;
use crate::platform_impl::wayland::seat::text_input::TextInputHandler;
use crate::platform_impl::wayland::WindowId;
use crate::window::CursorIcon;
/// A request to SCTK window from Winit window.
#[derive(Debug, Clone)]
pub enum WindowRequest {
/// Set fullscreen.
///
/// Passing `None` will set it on the current monitor.
Fullscreen(Option<WlOutput>),
/// Unset fullscreen.
UnsetFullscreen,
/// Show cursor for the certain window or not.
ShowCursor(bool),
/// Change the cursor icon.
NewCursorIcon(CursorIcon),
/// Grab cursor.
GrabCursor(bool),
/// Maximize the window.
Maximize(bool),
/// Minimize the window.
Minimize,
/// Request decorations change.
Decorate(bool),
/// Make the window resizeable.
Resizeable(bool),
/// Set the title for window.
Title(String),
/// Min size.
MinSize(Option<LogicalSize<u32>>),
/// Max size.
MaxSize(Option<LogicalSize<u32>>),
/// New frame size.
FrameSize(LogicalSize<u32>),
/// Set IME window position.
IMEPosition(LogicalPosition<u32>),
/// Redraw was requested.
Redraw,
/// A new theme for a concept frame was requested.
Theme(ConceptConfig),
/// Window should be closed.
Close,
}
/// Pending update to a window from SCTK window.
#[derive(Debug, Clone, Copy)]
pub struct WindowUpdate {
/// New window size.
pub size: Option<LogicalSize<u32>>,
/// New scale factor.
pub scale_factor: Option<i32>,
/// Whether `redraw` was requested.
pub redraw_requested: bool,
/// Wether the frame should be refreshed.
pub refresh_frame: bool,
/// Close the window.
pub close_window: bool,
}
impl WindowUpdate {
pub fn new() -> Self {
Self {
size: None,
scale_factor: None,
redraw_requested: false,
refresh_frame: false,
close_window: false,
}
}
pub fn take(&mut self) -> Self {
let size = self.size.take();
let scale_factor = self.scale_factor.take();
let redraw_requested = self.redraw_requested;
self.redraw_requested = false;
let refresh_frame = self.refresh_frame;
self.refresh_frame = false;
let close_window = self.close_window;
self.close_window = false;
Self {
size,
scale_factor,
redraw_requested,
refresh_frame,
close_window,
}
}
}
/// A handle to perform operations on SCTK window
/// and react to events.
pub struct WindowHandle {
/// An actual window.
pub window: Window<ConceptFrame>,
/// The current size of the window.
pub size: Arc<Mutex<LogicalSize<u32>>>,
/// A pending requests to SCTK window.
pub pending_window_requests: Arc<Mutex<Vec<WindowRequest>>>,
/// Current cursor icon.
pub cursor_icon: Cell<CursorIcon>,
/// Visible cursor or not.
cursor_visible: Cell<bool>,
/// Cursor confined to the surface.
confined: Cell<bool>,
/// Pointers over the current surface.
pointers: Vec<WinitPointer>,
/// Text inputs on the current surface.
text_inputs: Vec<TextInputHandler>,
}
impl WindowHandle {
pub fn new(
window: Window<ConceptFrame>,
size: Arc<Mutex<LogicalSize<u32>>>,
pending_window_requests: Arc<Mutex<Vec<WindowRequest>>>,
) -> Self {
Self {
window,
size,
pending_window_requests,
cursor_icon: Cell::new(CursorIcon::Default),
confined: Cell::new(false),
cursor_visible: Cell::new(true),
pointers: Vec::new(),
text_inputs: Vec::new(),
}
}
pub fn set_cursor_grab(&self, grab: bool) {
// The new requested state matches the current confine status, return.
if self.confined.get() == grab {
return;
}
self.confined.replace(grab);
for pointer in self.pointers.iter() {
if self.confined.get() {
let surface = self.window.surface();
pointer.confine(&surface);
} else {
pointer.unconfine();
}
}
}
/// Pointer appeared over the window.
pub fn pointer_entered(&mut self, pointer: WinitPointer) {
let position = self.pointers.iter().position(|p| *p == pointer);
if position.is_none() {
if self.confined.get() {
let surface = self.window.surface();
pointer.confine(&surface);
}
self.pointers.push(pointer);
}
// Apply the current cursor style.
self.set_cursor_visible(self.cursor_visible.get());
}
/// Pointer left the window.
pub fn pointer_left(&mut self, pointer: WinitPointer) {
let position = self.pointers.iter().position(|p| *p == pointer);
if let Some(position) = position {
let pointer = self.pointers.remove(position);
// Drop the confined pointer.
if self.confined.get() {
pointer.unconfine();
}
}
}
pub fn text_input_entered(&mut self, text_input: TextInputHandler) {
if self
.text_inputs
.iter()
.find(|t| *t == &text_input)
.is_none()
{
self.text_inputs.push(text_input);
}
}
pub fn text_input_left(&mut self, text_input: TextInputHandler) {
if let Some(position) = self.text_inputs.iter().position(|t| *t == text_input) {
self.text_inputs.remove(position);
}
}
pub fn set_ime_position(&self, position: LogicalPosition<u32>) {
// XXX This won't fly unless user will have a way to request IME window per seat, since
// the ime windows will be overlapping, but winit doesn't expose API to specify for
// which seat we're setting IME position.
let (x, y) = (position.x as i32, position.y as i32);
for text_input in self.text_inputs.iter() {
text_input.set_ime_position(x, y);
}
}
pub fn set_cursor_visible(&self, visible: bool) {
self.cursor_visible.replace(visible);
let cursor_icon = match visible {
true => Some(self.cursor_icon.get()),
false => None,
};
for pointer in self.pointers.iter() {
pointer.set_cursor(cursor_icon)
}
}
pub fn set_cursor_icon(&self, cursor_icon: CursorIcon) {
self.cursor_icon.replace(cursor_icon);
if !self.cursor_visible.get() {
return;
}
for pointer in self.pointers.iter() {
pointer.set_cursor(Some(cursor_icon));
}
}
}
#[inline]
pub fn handle_window_requests(winit_state: &mut WinitState) {
let window_map = &mut winit_state.window_map;
let window_updates = &mut winit_state.window_updates;
let mut windows_to_close: Vec<WindowId> = Vec::new();
// Process the rest of the events.
for (window_id, window_handle) in window_map.iter_mut() {
let mut requests = window_handle.pending_window_requests.lock().unwrap();
for request in requests.drain(..) {
match request {
WindowRequest::Fullscreen(fullscreen) => {
window_handle.window.set_fullscreen(fullscreen.as_ref());
}
WindowRequest::UnsetFullscreen => {
window_handle.window.unset_fullscreen();
}
WindowRequest::ShowCursor(show_cursor) => {
window_handle.set_cursor_visible(show_cursor);
}
WindowRequest::NewCursorIcon(cursor_icon) => {
window_handle.set_cursor_icon(cursor_icon);
}
WindowRequest::IMEPosition(position) => {
window_handle.set_ime_position(position);
}
WindowRequest::GrabCursor(grab) => {
window_handle.set_cursor_grab(grab);
}
WindowRequest::Maximize(maximize) => {
if maximize {
window_handle.window.set_maximized();
} else {
window_handle.window.unset_maximized();
}
}
WindowRequest::Minimize => {
window_handle.window.set_minimized();
}
WindowRequest::Decorate(decorate) => {
let decorations = match decorate {
true => Decorations::FollowServer,
false => Decorations::None,
};
window_handle.window.set_decorate(decorations);
// We should refresh the frame to apply decorations change.
let window_update = window_updates.get_mut(&window_id).unwrap();
window_update.refresh_frame = true;
}
WindowRequest::Resizeable(resizeable) => {
window_handle.window.set_resizable(resizeable);
// We should refresh the frame to update button state.
let window_update = window_updates.get_mut(&window_id).unwrap();
window_update.refresh_frame = true;
}
WindowRequest::Title(title) => {
window_handle.window.set_title(title);
// We should refresh the frame to draw new title.
let window_update = window_updates.get_mut(&window_id).unwrap();
window_update.refresh_frame = true;
}
WindowRequest::MinSize(size) => {
let size = size.map(|size| (size.width, size.height));
window_handle.window.set_min_size(size);
let window_update = window_updates.get_mut(&window_id).unwrap();
window_update.redraw_requested = true;
}
WindowRequest::MaxSize(size) => {
let size = size.map(|size| (size.width, size.height));
window_handle.window.set_max_size(size);
let window_update = window_updates.get_mut(&window_id).unwrap();
window_update.redraw_requested = true;
}
WindowRequest::FrameSize(size) => {
// Set new size.
window_handle.window.resize(size.width, size.height);
// We should refresh the frame after resize.
let window_update = window_updates.get_mut(&window_id).unwrap();
window_update.refresh_frame = true;
}
WindowRequest::Redraw => {
let window_update = window_updates.get_mut(&window_id).unwrap();
window_update.redraw_requested = true;
}
WindowRequest::Theme(concept_config) => {
window_handle.window.set_frame_config(concept_config);
// We should refresh the frame to apply new theme.
let window_update = window_updates.get_mut(&window_id).unwrap();
window_update.refresh_frame = true;
}
WindowRequest::Close => {
// The window was requested to be closed.
windows_to_close.push(*window_id);
// Send event that the window was destroyed.
let event_sink = &mut winit_state.event_sink;
event_sink.push_window_event(WindowEvent::Destroyed, *window_id);
}
};
}
}
// Close the windows.
for window in windows_to_close {
let _ = window_map.remove(&window);
let _ = window_updates.remove(&window);
}
}