always_on_top on macOS and Wayland and RFC

82 views
Skip to first unread message

Gonzalo Garramuño

unread,
May 1, 2022, 4:56:58 AM5/1/22
to fltkg...@googlegroups.com
I have the following function to keep the window as the topmost window
for X11 and Windows:

void MainWindow::always_on_top( bool t )
{
#if defined(_WIN32) || defined(_WIN64)
    HWND action;
    if ( t ) action = HWND_TOPMOST;
    else     action = HWND_NOTOPMOST;
    // Microsoft (R) Windows(TM)
    SetWindowPos(fl_xid(this), action, 0, 0, 0, 0, SWP_NOMOVE |
SWP_NOSIZE );
#elif defined(LINUX) && !defined(FLTK_USE_WAYLAND)
    // XOrg / XWindows(TM)
    XEvent ev;
    static const char* const names[2] = { "_NET_WM_STATE",
                                          "_NET_WM_STATE_ABOVE"
                                        };
    Atom atoms[ 2 ];
    fl_open_display();
    XInternAtoms(fl_display, (char**)names, 2, False, atoms );
    Atom net_wm_state = atoms[ 0 ];
    Atom net_wm_state_above = atoms[ 1 ];
    ev.type = ClientMessage;
    ev.xclient.window = fl_xid(this);
    ev.xclient.message_type = net_wm_state;
    ev.xclient.format = 32;
    ev.xclient.data.l[ 0 ] = (int)t;
    ev.xclient.data.l[ 1 ] = net_wm_state_above;
    ev.xclient.data.l[ 2 ] = 0;
    XSendEvent(fl_display,
               DefaultRootWindow(fl_display),  False,
               SubstructureNotifyMask|SubstructureRedirectMask, &ev);
#endif
}

I am missing how to do the same on macOS (and Wayland, too, but I don't
care too much about Wayland yet).    Also, I RFC about adding such a
function to Fl_Window in fltk1.4?

--
Gonzalo Garramuño

Ian MacArthur

unread,
May 2, 2022, 4:01:24 AM5/2/22
to fltk.general
On Sunday, 1 May 2022 at 09:56:58 UTC+1  Gonzalo   wrote:
I have the following function to keep the window as the topmost window
for X11 and Windows:

By "topmost" you mean a window, like a splash screen, that stays on top of every other window, from any application that has windows on the screen?

If you only mean a window that stays on top of windows from your own app. (but not necessarily on top of other apps) then you only need to set the window "non_modal" to get that behaviour.
TBH, I expect you already know that, but I thought I should state it here for clarity, and in case others were seeing this thread who may not know. 
There, are of course, 3 modality states for windows, normal, non_modal and modal. It may be that non_modal is "good enough" for most purposes here...


 
void MainWindow::always_on_top( bool t )
{
#if defined(_WIN32) || defined(_WIN64)
    HWND action;
    if ( t ) action = HWND_TOPMOST;
    else     action = HWND_NOTOPMOST;
    // Microsoft (R) Windows(TM)
    SetWindowPos(fl_xid(this), action, 0, 0, 0, 0, SWP_NOMOVE |
SWP_NOSIZE );

Juts out of an abundance of caution - but is this valid? I have a vague (therefore almost certainly wrong) feeling that the "action" had to be ORd with any existing action states... or something... Dunno...

 

#elif defined(LINUX) && !defined(FLTK_USE_WAYLAND)
    // XOrg / XWindows(TM)
    XEvent ev;
    static const char* const names[2] = { "_NET_WM_STATE",
                                          "_NET_WM_STATE_ABOVE"
                                        };
    Atom atoms[ 2 ];
    fl_open_display();
    XInternAtoms(fl_display, (char**)names, 2, False, atoms );
    Atom net_wm_state = atoms[ 0 ];
    Atom net_wm_state_above = atoms[ 1 ];
    ev.type = ClientMessage;
    ev.xclient.window = fl_xid(this);
    ev.xclient.message_type = net_wm_state;
    ev.xclient.format = 32;
    ev.xclient.data.l[ 0 ] = (int)t;
    ev.xclient.data.l[ 1 ] = net_wm_state_above;
    ev.xclient.data.l[ 2 ] = 0;
    XSendEvent(fl_display,
               DefaultRootWindow(fl_display),  False,
               SubstructureNotifyMask|SubstructureRedirectMask, &ev);
#endif
}

I am missing how to do the same on macOS

Don't know - but non-modal is supported on macOS, so if that is "good enough" then all is well already.
If not... need to ask Manolo or Matt I suspect!

 

Gonzalo Garramuno

unread,
May 2, 2022, 5:43:30 AM5/2/22
to fltkg...@googlegroups.com


> El 2 may. 2022, a las 05:01, Ian MacArthur <imaca...@gmail.com> escribió:
>
> On Sunday, 1 May 2022 at 09:56:58 UTC+1 Gonzalo wrote:
> I have the following function to keep the window as the topmost window
> for X11 and Windows:
>
> By "topmost" you mean a window, like a splash screen, that stays on top of every other window, from any application that has windows on the screen?

Yes. That’s exactly what I mean. The window should remain on top of all other application windows.

>
> void MainWindow::always_on_top( bool t )
> {
> #if defined(_WIN32) || defined(_WIN64)
> HWND action;
> if ( t ) action = HWND_TOPMOST;
> else action = HWND_NOTOPMOST;
> // Microsoft (R) Windows(TM)
> SetWindowPos(fl_xid(this), action, 0, 0, 0, 0, SWP_NOMOVE |
> SWP_NOSIZE );
>
> Juts out of an abundance of caution - but is this valid? I have a vague (therefore almost certainly wrong) feeling that the "action" had to be ORd with any existing action states... or something... Dunno...

Yes, it is valid (and it works). “Action” most definitively does not support OR.

>
> If not... need to ask Manolo or Matt I suspect!
>

Yes, I’ll need their help. I believe I need to write in Obj-C:

[window setlevel:NSFloatingWindowLevel]

However, I am unsure how do you hook that code into C++, without modifying Fl_cocoa.mm itself.


Gonzalo Garramuno
ggar...@gmail.com

Gonzalo Garramuno

unread,
May 2, 2022, 6:34:02 AM5/2/22
to fltkg...@googlegroups.com


> El 2 may. 2022, a las 06:43, Gonzalo Garramuno <ggar...@gmail.com> escribió:
> Yes, I’ll need their help. I believe I need to write in Obj-C:
>
> [window setlevel:NSFloatingWindowLevel]
>
> However, I am unsure how do you hook that code into C++, without modifying Fl_cocoa.mm itself.
>

Okay. I got it to work. I added guards for __APPLE__ in the C++ file so always_on_top was never used and then created a mrvMainWindow.mm file and wrote the following Obj-C code:

#import <Cocoa/Cocoa.h>

#include <FL/platform.H>
#include <FL/Fl.H>
#include "mrvMainWindow.h"

namespace mrv {

void MainWindow::always_on_top( int t )
{
FLWindow* nswin = fl_xid(this);
if ( t )
[nswin setLevel:NSFloatingWindowLevel];
else
[nswin setLevel:NSNormalWindowLevel];
} // above_all function


} // namespace mrv



Gonzalo Garramuno
ggar...@gmail.com




Manolo

unread,
May 2, 2022, 6:36:25 AM5/2/22
to fltk.general
here is what you can do for macOS, in a .mm file of your own:

#include <FL/platform.H>
#if __APPLE__
  #import <Cocoa/Cocoa.h>
  Fl_Window *mywindow = .... // the toplevel window of interest
  NSWindow *nswin = (NSWindow*)fl_xid(mywindow);
  [nswin setlevel:NSFloatingWindowLevel];
#endif

Reply all
Reply to author
Forward
0 new messages