raysan5 / raylib

A simple and easy-to-use library to enjoy videogames programming
http://www.raylib.com
zlib License
21.81k stars 2.21k forks source link

[core] TOPMOST flag does not work with multiple monitors #3198

Closed Vessov closed 1 year ago

Vessov commented 1 year ago

Issue description

I use Python API for Raylib - Raylib 4.5.0.0. When setting up a window, if different than default monitor is selected via set_window_monitor(), the FLAG_WINDOW_TOPMOST is not working as intended - even when flag is set, window will minimize when clicked anywhere on any other monitor - regardless of if the flag was set before setting the window monitor, or after. This occurs always when using the set_window_monitor(), even when the default monitor is selected.

Environment

Code Example

import pyray as pr

pr.set_config_flags(pr.ConfigFlags.FLAG_WINDOW_TOPMOST)
pr.set_config_flags(pr.ConfigFlags.FLAG_WINDOW_UNDECORATED)
pr.init_window(500, 500, "FooBar")
pr.set_window_position(0, 0)
pr.set_target_fps(60)

monitor = 0
pr.set_window_size(pr.get_monitor_width(monitor), pr.get_monitor_height(monitor))
pr.set_window_monitor(monitor)

while not pr.window_should_close():
    pr.begin_drawing()
    pr.clear_background(pr.RAYWHITE)
    pr.end_drawing()

pr.close_window()
raysan5 commented 1 year ago

@Vessov Please, try using SetWindowState() after InitWindow(), some flags are only functional after the window has been created. Here it is an usage example: https://github.com/raysan5/raylib/blob/master/examples/core/core_window_flags.c

Vessov commented 1 year ago

@raysan5 Still, error persists - even after adding SetWindowState() after InitWindow(), or even inside the event loop

ghost commented 1 year ago

@Vessov @raysan5 I was able to replicate the issue.

The window gets minimized because SetWindowMonitor (set_window_monitor) forces fullscreen mode:

  1. Cheatsheet: // Set monitor for the current window (fullscreen mode);
  2. rcore.c#L1664-L1665;
  3. "This function sets the monitor that the window uses for full screen mode or, if the monitor is NULL, makes it windowed mode." https://www.glfw.org/docs/3.3/group__window.html#ga81c76c418af80a1cce7055bccb0ae0a7

AFAIK, minimizing the fullscreen window when it loses focus is, more or less, the standard behavior on most OS. Same happened on Linux here. So, I guess this expected behavior.

Vessov commented 1 year ago

@ubkp @raysan5 I see. I wonder if there may be any workaround - since when window monitor isn't set via SetWindowMonitor (so, if I understand correctly, it defaults to NULL?), and window starts on the defaulft monitor, the issue doesn't occur - no matter if in fullscreen mode or otherwise, the window stays topmost.

After testing, I noticed another problem - it's impossible to make window drop the fullscreen mode while using SetWindowMonitor; changing the window size via SetWindowSize changes the resolution of the screen, but window stays fullscreen. I tried clearing the FLAG_FULLSCREEN_MODE with the ClearWindowState (both right after setting window monitor, as well as inside event loop), but it doesn't seem to have any affect.

Is it possible to design any workaround to keep window topsize when using SetWindowMonitor with the current library code, or add that functionality to the code, or would it be impossible due to GLWF constraints?

ghost commented 1 year ago

@Vessov Unfortunately, I believe these are mostly GLFW issues. Check the discussion on https://github.com/raysan5/raylib/issues/2764 and https://github.com/raysan5/raylib/issues/3099.

Aside from patching GLFW, I guess it could be possible to work around it by managing the window positioning yourself and "simulating" fullscreen with some "fullscreen" borderless window. I could be wrong tho, I haven't tried this yet.

Edit: Something like this:

#include "raylib.h"

int main(void) {

    InitWindow(500, 500, "Test");
    SetTargetFPS(60);

    while (!WindowShouldClose()) {

        if (IsKeyPressed(KEY_F1)) {
            // Set "fullscreen" borderless window on monitor 0:
            SetWindowState(FLAG_WINDOW_TOPMOST);
            SetWindowState(FLAG_WINDOW_UNDECORATED);
            SetWindowSize(GetMonitorWidth(0), GetMonitorHeight(0));
            SetWindowPosition(0, 0);
        }

        if (IsKeyPressed(KEY_F2)) {
            // Set "fullscreen" borderless window on monitor 1 if it exists:
            if (GetMonitorCount() == 2) {
                SetWindowState(FLAG_WINDOW_TOPMOST);
                SetWindowState(FLAG_WINDOW_UNDECORATED);
                SetWindowSize(GetMonitorWidth(1), GetMonitorHeight(1));
                SetWindowPosition(GetMonitorWidth(0), 0);
            }
        }

        BeginDrawing();
        ClearBackground(RAYWHITE);
        EndDrawing();
    }

    CloseWindow();
    return 0;
}
Vessov commented 1 year ago

@ubkp Thank you, your example works. I see you assumed that the second monitor is at the same level and to the right of the first one; I'm trying to implement it in a way that a monitor may be wherever. However, I'm not sure how to get a monitor position that could be correctly passed to SetWindowPosition; since GetMonitorPosition returns Vector2, which can't be unpacked into it's respective values, and I'm not sure if I understand how to correctly transform it to get the X and Y position to pass to SetWindowPosition. However, I'm not sure if that question is relevant to the issue at hand, so if I should go with it somwhere else (like Discord) and close this issue, let me know

ghost commented 1 year ago

@Vessov I don't know how it's done in Python, but in C/C++ you can do:

int monitor = 0;
float monitorX = GetMonitorPosition(monitor).x;
float monitorY = GetMonitorPosition(monitor).y;

Or:

int monitor = 0;
Vector2 monitorXY = GetMonitorPosition(monitor);
float monitorX = monitorXY.x;
float monitorY = monitorXY.y;
Vessov commented 1 year ago

@ubkp Done it, it works, thank you very much. Since, as you pointed, the main primary issue was something beyond the scope of the library itself, and relates to the way GLFW handles it, I'll be closing the issue now

raysan5 commented 1 year ago

@ubkp

...managing the window positioning yourself and "simulating" fullscreen with some "fullscreen" borderless window.

Actually, a BORDERLESS_WINDOWED_MODE (aka non-exclusive fullscreen mode) is on the TODO list, many games support it.

It could be accomplished with current raylib implementation but several users asked for it in the past, it could require a virtual and a logic screen size to be scaled to a full monitor borderless window.

ghost commented 1 year ago

@raysan5 I can take a look at that.

While I'm at it, should I also try to review SetWindowMonitor() so windows can be positioned without forcing fullscreen?

raysan5 commented 1 year ago

I can take a look at that.

@ubkp Sure! I'm opening a separate issue for further discussion #3207

should I also try to review SetWindowMonitor() so windows can be positioned without forcing fullscreen?

Of course.