lv2 / pugl

A minimal portable API for embeddable GUIs
https://gitlab.com/lv2/pugl/
ISC License
179 stars 34 forks source link

Start non-child windows centered on screen or to its transient parent #69

Closed falkTX closed 1 year ago

falkTX commented 2 years ago

Currently on Windows, starting a pugl window without a parent makes it appear at the 0,0 position. This does not include the title bar/decoration, making it very difficult to manage the window (there is no Alt+Drag on Windows afaik) Similar situation happens on macOS, windows just start at top-left. On Linux/X11 this seems a bit random and WM-dependent.

This is specially painful with multiple screens, as the new window can appear far away from the transient parent or the expected place.

I have code for this in DPF, feel free to adapt and reuse. macOS:

    if (view->transientParent != 0)
    {
        NSWindow* const transientWindow = [(NSView*)view->transientParent window];
        DISTRHO_SAFE_ASSERT_RETURN(transientWindow != nullptr,);

        const NSRect ourFrame       = [view->impl->window frame];
        const NSRect transientFrame = [transientWindow frame];

        const int x = transientFrame.origin.x + transientFrame.size.width / 2 - ourFrame.size.width / 2;
        const int y = transientFrame.origin.y + transientFrame.size.height / 2  + ourFrame.size.height / 2;

        [view->impl->window setFrameTopLeftPoint:NSMakePoint(x, y)];
    }
    else
    {
        [view->impl->window center];
    }

Wndows:

    RECT rectChild, rectParent;
    if (view->transientParent != 0 &&
        GetWindowRect(impl->hwnd, &rectChild) &&
        GetWindowRect((HWND)view->transientParent, &rectParent))
    {
        SetWindowPos(impl->hwnd, (HWND)view->transientParent,
                     rectParent.left + (rectChild.right-rectChild.left)/2,
                     rectParent.top + (rectChild.bottom-rectChild.top)/2,
                     0, 0, SWP_SHOWWINDOW|SWP_NOSIZE);
    }
    else
    {
        // TODO this part, didnt find a proper API to start window in the correct position
        ShowWindow(impl->hwnd, SW_SHOWNORMAL);
    }

I didn't bother with X11 since it has never been a problem there.

drobilla commented 2 years ago

Is it really necessary to manually position explicitly transient windows on their parents on MacOS? This feels incredibly unlikely to me.

falkTX commented 2 years ago

I am pretty sure that, if I added that code, was because at some point things were not working. One of those "oh this is not working actually, let me go fix it" things that you then just forget exactly why you did it, but was necessary before..

drobilla commented 2 years ago

That much is obvious ;)

I don't know the guidelines for each platform off the top of my head, but I do know that explicitly positioning windows when you shouldn't is terrible application behaviour that tends to break things. The correct thing to do needs figuring out.

e.g. on Windows, for top-level windows, the real bug may be that pugl is explicitly specifying a position (because the frame is set to something or other) rather than using CW_USEDEFAULT. I'm guessing that there's an analogous case on MacOS as well, since everything tends to be automatic there, where defaults adhere to the HiG.

On X11, IIRC, it's the window manager's job, so although the X11 API has positions in e.g. XCreateWindow, there's no guarantee that you'll actually get that one (and this happens in practice).

Transient windows tend to be handled specially in general.

etc. etc.

drobilla commented 2 years ago

Additional fun facts: as far as I can tell, Wayland doesn't support clients doing this sort of thing at all.

drobilla commented 1 year ago

Seems my intuition that kludging this is a bad idea was right. Things work properly if you set no position at all, as far as I can tell anyway.

As of 14b35ef at least, transient children will be automatically centered on their parent if no position is set. You can test this by pressing space in the new pugl_management_demo. It works for me on several WMs, MacOS, and Windows.

drobilla commented 1 year ago

Scratch that, not on Windows... uh.... sometimes.

drobilla commented 1 year ago

Implemented in 16739fe

Some further improvements to position follow that one. Pretty thorough overhaul, so regressions to position stuff in general are possible, but I've tested it pretty well and everything seems to work on MacOS, Windows, GNOME 3, KDE 5, and i3. Awesome and bspwm have some minor issues. Things are a lot more direct now, though (I killed all of the redundant frame information), so if there are some kinks to iron out, things should be much more solid than before.

Consistency across window managers for top-level window positioning is an absolute nightmare...

Russell-Jones-OxPhys commented 8 months ago

Hi @drobilla What do you think of the arguments made in this post? https://lists.freedesktop.org/archives/wayland-devel/2021-May/041844.html It is about how window positioning under Wayland is the prerogative of compositors, and how the information that needs to be provided is that such that good decisions can be made by the compositor (e.g. which windows own which other windows). I'm asking about this as I was struck by the fact that the README at https://gitlab.com/lv2/pugl/ does not list Wayland as a supported platform.

drobilla commented 8 months ago

I don't have any particular opinion on it. The compositor being responsible for such things seems, broadly speaking, reasonable. I have zero clue if or how how embedding plugin UIs can work there, though.

The trouble, of course, is that this requires hosts to be a full-on Wayland compositor, as far as I can tell, which is a pretty big ask for little real benefit (typical Wayland, really).

I'm asking about this as I was struck by the fact that the README at https://gitlab.com/lv2/pugl/ does not list Wayland as a supported platform.

That's because of the above, and the fact that Wayland is absolutely brutal to implement. I have, however, gotten things basically working for top-level programs. I don't have the time or energy to drive the thankless topic of embedding plugin UIs on Wayland, though. Pragmatically, we might just have to abandon the idea entirely.