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

Window property always-on-top does not work on linux #6691

Open
michaelrampl opened this issue Oct 31, 2024 · 10 comments
Open

Window property always-on-top does not work on linux #6691

michaelrampl opened this issue Oct 31, 2024 · 10 comments
Labels
a:platform-linux issue specific to Linux, X11 or wayland (mO,bT) bug Something isn't working

Comments

@michaelrampl
Copy link

michaelrampl commented Oct 31, 2024

On Linux (nixos) both under x11 and wayland (tested using KDE), the always-on-top property doesn't seem to work

Cargo.toml

[package]
name = "example"
version = "0.1.0"
edition = "2021"

[dependencies]
slint = "1.8.0"

[build-dependencies]
slint-build = "1.8.0"

main.slint

import { Button } from "std-widgets.slint";

export component MainWindow inherits Window {
    always-on-top: true;
}

main.rs

slint::include_modules!();
fn main() {
    MainWindow::new().unwrap().run().unwrap();
}

It looks like SLINT uses the winit crate which should support the feature tough https://docs.rs/winit/latest/winit/window/enum.WindowLevel.html#variant.AlwaysOnTop

@Vadoola
Copy link

Vadoola commented Oct 31, 2024

If I recall correctly the always on top works(ed) on X11 but didn't on Wayland. My project that uses this features has taken a back sear the past couple of months, but I thought that's what I remember from using it.

When you tested it using X11 was it a full X11 instance, or running under XWayland?

@ogoffart ogoffart added bug Something isn't working a:platform-linux issue specific to Linux, X11 or wayland (mO,bT) need triaging Issue that the owner of the area still need to triage labels Nov 1, 2024
@michaelrampl
Copy link
Author

I've had full X11 and XWayland sessions running individually.
When starting the X11 sessions, loginctl show-session 2 -p Type also shows x11 correctly.
If wayland is not supported (yet), It should be mentioned in the documentation.

@Vadoola
Copy link

Vadoola commented Nov 4, 2024

If wayland is not supported (yet), It should be mentioned in the documentation.

The documentation does says on Window Managers that support it. I don't know enough about Wayland to know if that's a Wayland issue or lack of Support for a Wayland protocol. One of the actual Slint developers might have more to offer here.

I was testing it on an Arch install under Wayland with Gnome and it didn't work. I thought I remember testing on X11 as well and it did work, but it's been a while.

I'll see if I can load up my system, as well as get a KDE install and maybe some other to try my program on. Might get some more info out of it.

@michaelrampl
Copy link
Author

Having a plain simple winit example featuring with_window_level(winit::window::WindowLevel::AlwaysOnTop) seems to work on X11

use winit::application::ApplicationHandler;
use winit::event::WindowEvent;
use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop};
use winit::window::{Window, WindowId};

#[derive(Default)]
struct App {
    window: Option<Window>,
}

impl ApplicationHandler for App {
    fn resumed(&mut self, event_loop: &ActiveEventLoop) {
        println!("resumed");
        self.window = Some(event_loop.create_window(Window::default_attributes().with_window_level(winit::window::WindowLevel::AlwaysOnTop)).unwrap());
    }

    fn window_event(&mut self, event_loop: &ActiveEventLoop, _: WindowId, event: WindowEvent) {
        match event {
            WindowEvent::CloseRequested => {
                println!("The close button was pressed; stopping");
                event_loop.exit();
            },
            WindowEvent::RedrawRequested => {
                self.window.as_ref().unwrap().request_redraw();
            }
            _ => (),
        }
    }
}

fn main() {
    let event_loop = EventLoop::new().unwrap();
    event_loop.set_control_flow(ControlFlow::Poll);
    event_loop.set_control_flow(ControlFlow::Wait);
    let mut app = App::default();
    let _ = event_loop.run_app(&mut app);
}

@michaelrampl
Copy link
Author

Also https://rust-windowing.github.io/winit/winit/window/enum.WindowLevel.html says it's currently unsupported on wayland

@Vadoola
Copy link

Vadoola commented Nov 4, 2024

Having a plain simple winit example featuring with_window_level(winit::window::WindowLevel::AlwaysOnTop) seems to work on X11

Certainly sounds like a Slint issue then. I'll try and test mine and report back. My project is still back on Slint 1.6...although I want to say the last version I tested on X11 was back on v1.2 or 1.4. I wonder if this problem cropped up I'm one of the newer versions.

@ogoffart
Copy link
Member

ogoffart commented Nov 4, 2024

I try to reproduce this and there is indeed a bug.
With winit, backend, I verified that we do indeed call winit::Window::set_window_level with the right level.
I also noticed that if you have something like CheckBox { toggled => { root.always-on-top = self.checked; } } it works. But for some reason, it doesn't work at startup.

With Qt, disabling the always-on-top actually hide the window.

To me, it looks like what we pass to the underlying library (Qt, winit) is correct, and something wrong happens in these library or in the window manager.

@ogoffart ogoffart removed the need triaging Issue that the owner of the area still need to triage label Nov 4, 2024
@Vadoola
Copy link

Vadoola commented Nov 4, 2024

I appear to be having some instability issues with X11 on my setup at the moment, but doing some quick testing I am experiencing this bug in the current version of Tomotroid which is using Slint 1.7.2. I checked out an older version compiling with Slint 1.6, and the always on top seemed to be working (as well as I could test with the instability issues).

So it looks like the bug cropped up somewhere between Slint 1.6 and 1.7.2. Hopefully that helps narrow down where the issue might lie.

@ogoffart
Copy link
Member

ogoffart commented Nov 5, 2024

Maybe this is caused by the update of winit to winit 0.30

@michaelrampl
Copy link
Author

michaelrampl commented Nov 8, 2024

I can confirm that the set_window_level function is also being called correctly in internal/backends/winit/winitwindowadapter.rs:update_window_properties (at least the first time)

I also can confirm that the checkbox hack works.

As a dirty workaround one could use a single-shot timer like this:

Timer {
  interval: 100ms;
  running: true;
  triggered => {
    root.always-on-top = true;
    self.running = false;
  }
}

Without the hack the following happens:
Upon start, the code

if self.window_level.replace(new_window_level) != new_window_level {
  winit_window_or_none.set_window_level(new_window_level);
}

is being called 3 times. The first time the level will be set to the winit::window::Window object. The second and third time it will not be set because the value stays the same for WinitWindowAdapter. So i would assume that somewhere after the first call, the level of the underlying winit::window::Window gets changed without WinitWindowAdapter noticing it.

Removing the condition, thus always setting the level seems to solve the issue

set_window_level(new_window_level);

This most likely will not be the fix we want as it might affect #4225. But for me this confirms the theory that something is happening to the state underneath after the first call.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
a:platform-linux issue specific to Linux, X11 or wayland (mO,bT) bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants