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

Event loop freezes when the primary window is minimised whilst a second window contains a convas with mouse events #2772

Open
4 tasks done
mfreeborn opened this issue Jan 31, 2025 · 1 comment
Labels
bug Something isn't working

Comments

@mfreeborn
Copy link

Is your issue REALLY a bug?

  • My issue is indeed a bug!
  • I am not crazy! I will not fill out this form just to ask a question or request a feature. Pinky promise.

Is there an existing issue for this?

  • I have searched the existing issues.

Is this issue related to iced?

  • My hardware is compatible and my graphics drivers are up-to-date.

What happened?

In this example, I am using an iced::daemon to create a multi-window application. The main window contains a button which opens a secondary window. The secondary window contains a canvas which makes use of the update method on the canvas::Program trait to capture mouse events. When the main window is minimised leaving just the secondary window is visible, the secondary window freezes - as if the event loop was paused just because the main window is minimised.

If I remove the use of the update method, the secondary window no longer freezes.

Here's a little gif showing the steps (excuse the visual artifacts from the conversion of video -> gif):

  1. Start the application; the main window is displayed
  2. Click the button to open a secondary window - observe the mouse position being logged to the console
  3. Minimise the main window, leaving just the secondary window displayed
  4. Event loop freezes and the secondary window stops responding

Image

Code:
use iced::{
    daemon, mouse,
    widget::{button, canvas, center},
    window, Element, Length, Rectangle, Renderer, Task, Theme, Vector,
};

#[derive(Debug, Clone)]
pub enum Message {
    OpenWindowButtonClicked,
    WindowOpened(window::Id),
    CanvasMouseEvent(Vector),
}

pub struct State {
    main_window_id: window::Id,
    secondary_window_id: Option<window::Id>,
}

impl State {
    fn new() -> (Self, Task<Message>) {
        let (main_window_id, main_window_task) = window::open(window::Settings::default());
        let main_window_task = main_window_task.discard();

        (
            Self {
                main_window_id,
                secondary_window_id: None,
            },
            main_window_task,
        )
    }
    fn update(&mut self, message: Message) -> Task<Message> {
        match message {
            Message::OpenWindowButtonClicked => {
                let (id, task) = window::open(window::Settings::default());
                self.secondary_window_id = Some(id);
                return task.map(Message::WindowOpened);
            }
            Message::WindowOpened(_id) => (),
            Message::CanvasMouseEvent(position) => {
                dbg!(position);
            }
        }
        Task::none()
    }

    fn view(&self, window_id: window::Id) -> Element<Message> {
        if window_id == self.main_window_id {
            center(button("Open Second Window").on_press_maybe(
                if self.secondary_window_id.is_none() {
                    Some(Message::OpenWindowButtonClicked)
                } else {
                    None
                },
            ))
            .into()
        } else {
            center(
                canvas(CanvasWidget)
                    .width(Length::Fill)
                    .height(Length::Fill),
            )
            .into()
        }
    }
}

struct CanvasWidget;

impl canvas::Program<Message> for CanvasWidget {
    type State = ();

    fn draw(
        &self,
        _state: &Self::State,
        renderer: &Renderer,
        _theme: &Theme,
        bounds: Rectangle,
        _cursor: mouse::Cursor,
    ) -> Vec<canvas::Geometry<Renderer>> {
        let mut frame = canvas::Frame::new(renderer, bounds.size());
        frame.fill_text("Ahoy");

        vec![frame.into_geometry()]
    }

    fn update(
        &self,
        _state: &mut Self::State,
        _event: canvas::Event,
        bounds: Rectangle,
        cursor: mouse::Cursor,
    ) -> (canvas::event::Status, Option<Message>) {
        if let iced::mouse::Cursor::Available(point) = cursor {
            if bounds.contains(point) {
                // Point is relative to the window
                let p_origin = bounds.position();

                // Pos is now in relative to the canvas
                let pos = point - p_origin;
                return (
                    canvas::event::Status::Captured,
                    Some(Message::CanvasMouseEvent(pos)),
                );
            }
        };

        return (canvas::event::Status::Ignored, None);
    }
}

fn main() {
    daemon(">1 windows freezes", State::update, State::view)
        .run_with(State::new)
        .unwrap();
}

What is the expected behavior?

No freezes to occur.

Version

crates.io release

Operating System

Linux

Do you have any log output?

@mfreeborn mfreeborn added the bug Something isn't working label Jan 31, 2025
@mfreeborn
Copy link
Author

In my full application, I have switched from using a canvas to using a custom widget, and the problem is much less frequent/not as obvious how to reproduce it. However, trying to resize the secondary window whilst the primary window is minimised is very likely to cause the secondary window to freeze.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

1 participant