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
Show file tree
Hide file tree
Changes from all 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
Expand Up @@ -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");
Expand All @@ -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;
Expand Down Expand Up @@ -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());
}
Expand Down
60 changes: 32 additions & 28 deletions src/platform_impl/linux/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)| {
Expand Down Expand Up @@ -292,9 +293,13 @@ 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();
}
Expand Down Expand Up @@ -462,35 +467,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())
}
}
}
Expand Down
1 change: 1 addition & 0 deletions src/platform_impl/linux/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down
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> {
Expand Down Expand Up @@ -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;
28 changes: 20 additions & 8 deletions src/platform_impl/linux/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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)
Expand All @@ -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
Expand All @@ -104,10 +110,15 @@ 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
Expand Down Expand Up @@ -578,10 +589,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);
}
}
Expand Down Expand Up @@ -609,7 +622,6 @@ impl Window {
pub fn is_maximizable(&self) -> bool {
true
}

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