Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(wayland): add client side decorations & fix error protocol 71 #979

Merged
merged 19 commits into from
Sep 26, 2024
Merged
Changes from 18 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changes/improve-wayland-display.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"tao": patch
---

On Linux Wayland, changed the event handling for maximizing to process events sequentially to avoid "Error 71(Protocol error): dispatching to Wayland display".
5 changes: 5 additions & 0 deletions .changes/wayland-dragging-header.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"tao": patch
---

On Linux Wayland, fixed an issue where the window was not moving when dragging the header bar area.
5 changes: 5 additions & 0 deletions .changes/wayland-resize-window.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"tao": patch
---

On Linux Wayland, fixed an issue where the window was not resizing when dragging the window borders.
5 changes: 5 additions & 0 deletions .changes/wayland-titlebar.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"tao": patch
---

On Linux Wayland, added buttons for maximize and minimize in the title bar.
7 changes: 7 additions & 0 deletions examples/window_debug.rs
Original file line number Diff line number Diff line change
@@ -33,6 +33,7 @@ fn main() {
eprintln!(" (T) Toggle always on top");
eprintln!(" (B) Toggle always on bottom");
eprintln!(" (C) Toggle content protection");
eprintln!(" (R) Toggle resizable");
eprintln!(" (M) Toggle minimized");
eprintln!(" (X) Toggle maximized");
eprintln!(" (Q) Quit event loop");
@@ -44,6 +45,7 @@ fn main() {
let mut always_on_top = false;
let mut visible = true;
let mut content_protection = false;
let mut resizable = false;

event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait;
@@ -119,6 +121,11 @@ fn main() {
window.set_fullscreen(Some(Fullscreen::Borderless(None)));
}
}
"r" => {
resizable = !resizable;
window.set_resizable(resizable);
println!("Resizable: {}", resizable);
}
"m" => {
window.set_minimized(!window.is_minimized());
}
61 changes: 33 additions & 28 deletions src/platform_impl/linux/event_loop.rs
Original file line number Diff line number Diff line change
@@ -262,6 +262,7 @@ impl<T: 'static> EventLoop<T> {
};

let mut taskbar = TaskbarIndicator::new();
let is_wayland = window_target.is_wayland();

// Window Request
window_requests_rx.attach(Some(&context), move |(id, request)| {
@@ -292,9 +293,14 @@ impl<T: 'static> EventLoop<T> {
window.deiconify();
}
}
WindowRequest::Maximized(maximized) => {
WindowRequest::Maximized(maximized, resizable) => {
if maximized {
window.maximize();
let maximize_process =
util::WindowMaximizeProcess::new(window.clone(), resizable);
glib::idle_add_local_full(glib::Priority::DEFAULT_IDLE, move || {
let mut maximize_process = maximize_process.borrow_mut();
maximize_process.next_step()
});
} else {
window.unmaximize();
}
@@ -462,35 +468,34 @@ impl<T: 'static> EventLoop<T> {
glib::Propagation::Proceed
});
window.connect_button_press_event(move |window, event| {
if !window.is_decorated()
const LMB: u32 = 1;
if (is_wayland || !window.is_decorated())
&& window.is_resizable()
&& !window.is_maximized()
&& event.button() == 1
&& event.button() == LMB
{
if let Some(window) = window.window() {
let (cx, cy) = event.root();
let (left, top) = window.position();
let (w, h) = (window.width(), window.height());
let (right, bottom) = (left + w, top + h);
let border = window.scale_factor() * 5;
let edge = crate::window::hit_test(
(left, top, right, bottom),
cx as _,
cy as _,
border,
border,
)
.map(|d| d.to_gtk_edge())
// we return `WindowEdge::__Unknown` to be ignored later.
// we must return 8 or bigger, otherwise it will be the same as one of the other 7 variants of `WindowEdge` enum.
.unwrap_or(WindowEdge::__Unknown(8));
// Ignore the `__Unknown` variant so the window receives the click correctly if it is not on the edges.
match edge {
WindowEdge::__Unknown(_) => (),
_ => {
// FIXME: calling `window.begin_resize_drag` uses the default cursor, it should show a resizing cursor instead
window.begin_resize_drag(edge, 1, cx as i32, cy as i32, event.time())
}
let (cx, cy) = event.root();
let (left, top) = window.position();
let (w, h) = window.size();
let (right, bottom) = (left + w, top + h);
let border = window.scale_factor() * 5;
let edge = crate::window::hit_test(
(left, top, right, bottom),
cx as _,
cy as _,
border,
border,
)
.map(|d| d.to_gtk_edge())
// we return `WindowEdge::__Unknown` to be ignored later.
// we must return 8 or bigger, otherwise it will be the same as one of the other 7 variants of `WindowEdge` enum.
.unwrap_or(WindowEdge::__Unknown(8));
// Ignore the `__Unknown` variant so the window receives the click correctly if it is not on the edges.
match edge {
WindowEdge::__Unknown(_) => (),
_ => {
// FIXME: calling `window.begin_resize_drag` uses the default cursor, it should show a resizing cursor instead
window.begin_resize_drag(edge, LMB as i32, cx as i32, cy as i32, event.time())
}
}
}
1 change: 1 addition & 0 deletions src/platform_impl/linux/mod.rs
Original file line number Diff line number Diff line change
@@ -20,6 +20,7 @@ mod util;
mod window;

pub mod taskbar;
pub mod wayland;
pub mod x11;

pub use self::keycode::{keycode_from_scancode, keycode_to_scancode};
52 changes: 46 additions & 6 deletions src/platform_impl/linux/util.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
use crate::{
dpi::{LogicalPosition, LogicalSize, PhysicalPosition},
error::ExternalError,
window::WindowSizeConstraints,
};
use gtk::gdk::{
self,
prelude::{DeviceExt, SeatExt},
Display,
};
use gtk::traits::{GtkWindowExt, WidgetExt};

use crate::{
dpi::{LogicalPosition, LogicalSize, PhysicalPosition},
error::ExternalError,
window::WindowSizeConstraints,
use gtk::{
glib::{self},
traits::{GtkWindowExt, WidgetExt},
};
use std::cell::RefCell;
use std::rc::Rc;

#[inline]
pub fn cursor_position(is_wayland: bool) -> Result<PhysicalPosition<f64>, ExternalError> {
@@ -70,3 +74,39 @@ pub fn set_size_constraints<W: GtkWindowExt + WidgetExt>(
geom_mask,
)
}

pub struct WindowMaximizeProcess<W: GtkWindowExt + WidgetExt> {
window: W,
resizable: bool,
step: u8,
}

impl<W: GtkWindowExt + WidgetExt> WindowMaximizeProcess<W> {
pub fn new(window: W, resizable: bool) -> Rc<RefCell<Self>> {
Rc::new(RefCell::new(Self {
window,
resizable,
step: 0,
}))
}

pub fn next_step(&mut self) -> glib::ControlFlow {
match self.step {
0 => {
self.window.set_resizable(true);
self.step += 1;
glib::ControlFlow::Continue
}
1 => {
self.window.maximize();
self.step += 1;
glib::ControlFlow::Continue
}
2 => {
self.window.set_resizable(self.resizable);
glib::ControlFlow::Break
}
_ => glib::ControlFlow::Break,
}
}
}
36 changes: 36 additions & 0 deletions src/platform_impl/linux/wayland/header.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
use gtk::{prelude::*, ApplicationWindow, EventBox, HeaderBar};

pub struct WlHeader;

impl WlHeader {
pub fn setup(window: &ApplicationWindow, title: &str) {
let header = HeaderBar::builder()
.show_close_button(true)
.decoration_layout("menu:minimize,maximize,close")
.title(title)
.build();

let event_box = EventBox::new();
amrbashir marked this conversation as resolved.
Show resolved Hide resolved
event_box.set_above_child(true);
event_box.set_visible(true);
event_box.set_can_focus(false);
event_box.add(&header);

window.set_titlebar(Some(&event_box));
Self::connect_resize_window(&header, window);
}

fn connect_resize_window(header: &HeaderBar, window: &ApplicationWindow) {
let header_weak = header.downgrade();
window.connect_resizable_notify(move |window| {
if let Some(header) = header_weak.upgrade() {
let is_resizable = window.is_resizable();
header.set_decoration_layout(if !is_resizable {
Some("menu:minimize,close")
} else {
Some("menu:minimize,maximize,close")
});
}
});
}
}
5 changes: 5 additions & 0 deletions src/platform_impl/linux/wayland/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Copyright 2014-2021 The winit contributors
// Copyright 2021-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0

pub mod header;
31 changes: 23 additions & 8 deletions src/platform_impl/linux/window.rs
Original file line number Diff line number Diff line change
@@ -23,6 +23,7 @@ use crate::{
error::{ExternalError, NotSupportedError, OsError as RootOsError},
icon::Icon,
monitor::MonitorHandle as RootMonitorHandle,
platform_impl::wayland::header::WlHeader,
window::{
CursorIcon, Fullscreen, ProgressBarState, ResizeDirection, Theme, UserAttentionType,
WindowAttributes, WindowSizeConstraints,
@@ -78,6 +79,7 @@ impl Window {
let app = &event_loop_window_target.app;
let window_requests_tx = event_loop_window_target.window_requests_tx.clone();
let draw_tx = event_loop_window_target.draw_tx.clone();
let is_wayland = event_loop_window_target.is_wayland();

let mut window_builder = gtk::ApplicationWindow::builder()
.application(app)
@@ -88,6 +90,10 @@ impl Window {

let window = window_builder.build();

if is_wayland {
WlHeader::setup(&window, &attributes.title);
}

let window_id = WindowId(window.id());
event_loop_window_target
.windows
@@ -104,10 +110,18 @@ impl Window {
window.resize(width, height);

if attributes.maximized {
window.maximize();
let maximize_process = util::WindowMaximizeProcess::new(
window.clone(),
attributes.resizable,
);
glib::idle_add_local_full(glib::Priority::HIGH_IDLE, move || {
let mut maximize_process = maximize_process.borrow_mut();
maximize_process.next_step()
});
} else {
window.set_resizable(attributes.resizable);
}

window.set_resizable(attributes.resizable);
window.set_deletable(attributes.closable);

// Set Min/Max Size
@@ -578,10 +592,12 @@ impl Window {
}

pub fn set_maximized(&self, maximized: bool) {
if let Err(e) = self
.window_requests_tx
.send((self.window_id, WindowRequest::Maximized(maximized)))
{
let resizable = self.is_resizable();
amrbashir marked this conversation as resolved.
Show resolved Hide resolved

if let Err(e) = self.window_requests_tx.send((
self.window_id,
WindowRequest::Maximized(maximized, resizable),
)) {
log::warn!("Fail to send maximized request: {}", e);
}
}
@@ -609,7 +625,6 @@ impl Window {
pub fn is_maximizable(&self) -> bool {
true
}

pub fn is_closable(&self) -> bool {
self.window.is_deletable()
}
@@ -995,7 +1010,7 @@ pub enum WindowRequest {
Resizable(bool),
Closable(bool),
Minimized(bool),
Maximized(bool),
Maximized(bool, bool),
DragWindow,
DragResizeWindow(ResizeDirection),
Fullscreen(Option<Fullscreen>),