PistonDevelopers / glfw-rs

GLFW3 bindings and idiomatic wrapper for Rust.
Apache License 2.0
648 stars 123 forks source link

There doesn't seem to be a way to make a `Window` fullscreen after creating it if you don't happen to have a spare `&Glfw` outside of your `Window` #490

Open PatchMixolydic opened 2 years ago

PatchMixolydic commented 2 years ago

The recommended way to set a window to fullscreen after creating it seems to be glfwSetWindowMonitor. The wrapper for this, Glfw::set_monitor, takes a WindowMode<'_>. In order to use WindowMode::Fullscreen, you need a reference to a Monitor. The only ways to get such a reference seem to be Glfw::with_primary_monitor or Glfw::with_connected_monitors, both of which require a closure so that the &Monitor they yield can only live within that closure.

I'm designing a backend-agnostic graphics framework for a game engine. To that end, I have a trait like this:

// need to be able to use this as a trait object with no generics necessary
// (ie. no associated types allowed, otherwise you'd have to specify them)
trait Window {
    // various functions
    fn set_fullscreen(&mut self, fullscreen: bool);
}

When trying to implement this for GLFW, however, it quickly becomes apparent that the borrow checker isn't pleased with my harebrained scheme:

struct GlfwWindow {
    window: glfw::Window,
    // ...
}

impl Window for GlfwWindow {
    fn set_fullscreen(&mut self, fullscreen: bool) {
        if self.fullscreen == fullscreen {
            return;
        }

        self.fullscreen = fullscreen;

        if !fullscreen {
            self.window.set_monitor(WindowMode::Windowed, ...);
            return;
        }

        // Can't have a `glfw` parameter (there's no way to specify its type),
        // have to use the field on `self.window`
        self.window.glfw.with_primary_monitor(|glfw, maybe_monitor| {
            let monitor = maybe_monitor.unwrap();
            let video_mode = monitor.get_video_mode().unwrap();

            // ERROR self.window is borrowed as mutable for
            // the duration of the call to `with_primary_monitor`
            self.window.set_monitor(
                WindowMode::Fullscreen(monitor),
                0,
                0,
                video_mode.width,
                video_mode.height,
                None
            );
        });
    }
}

Due to this, I seem to be at an impasse. I can't recreate the window on the fly since my trait for glfw::Glfw and similar (Backend) requires associated types and therefore would require generics to use it in a trait object.

If the real issue is a severe design flaw on my end or if providing an API for this is intractable/impossible, please feel free to close this issue.

PatchMixolydic commented 2 years ago

For the sake of posterity, one can use Window::set_decorated and Window::set_size to attain borderless fullscreen, but this might not be usable for games that need more resources or low input latency.

PatchMixolydic commented 2 years ago

I seem to be terrible at remembering and worse at reading. There is, in fact, a way to use ffi::glfwSetWindowMonitor (the original issue used to claim that there wasn't) thanks to <Window as Context>::window_ptr. However, doing so seems to give me an error from Xorg (which may be because the code I threw together to test this is unsound):

X Error of failed request:  BadRRCrtc (invalid Crtc parameter)
  Major opcode of failed request:  140 (RANDR)
  Minor opcode of failed request:  20 (RRGetCrtcInfo)
  Crtc id in failed request: 0xef600101
  Serial number of failed request:  990
  Current serial number in output stream:  990