OS2 World Community Forum

OS/2, eCS & ArcaOS - Technical => Programming => Topic started by: Martin Vieregg on April 02, 2020, 09:15:25 pm

Title: how to stay a window on top
Post by: Martin Vieregg on April 02, 2020, 09:15:25 pm
OS/2 distinghishes between a standard "window" and a "dialog window". A visible difference is that a standard window has got an arbitrary icon on the system menu button. A dialog window has only a drop down array icon. Both windows can run in modal and non-modal state. The standard "window" is created with the API call "WinCreateWindow" or "WinCreateStdWindow". A dialog window is created with WinCreateDlg or with WinLoadDlg (from a resource) and a parameter is the address of a message processing function.

An interesting difference in the behaviour of both types of window is that a dialog window continously stays on top, even another window of the program gets the focus, also in non-modal state.

My question: Is there a flag where a window created with WinCreateWindow gets the same behaviour of staying on top like a "dialog window" does?

The reason is that I want to add a specific property to the WDsibyl component library. In Freepascal Lazarus, such a property exists (tForm.Formstyle = fsStayOnTop). The WDsibyl tForm component is completely based on WinCreateWindow, also all standard controls do so.
Title: Re: how to stay a window on top
Post by: Laurence Pithie on April 03, 2020, 09:32:13 am
The FCF_SYSMODAL creation flag creates a system modal window. You can also use the WinSetSysModalWindow call to change a window into being the system modal window or from being the system modal window.
Title: Re: how to stay a window on top
Post by: Martin Vieregg on April 03, 2020, 11:18:20 am
Quote
The FCF_SYSMODAL creation flag creates a system modal window

But I want that the child window floats only over the main window of my appliction, not system wide. And it should be non-modal.
Title: Re: how to stay a window on top
Post by: Alex Taylor on April 03, 2020, 06:43:16 pm
For a PM frame window, try this:
Code: [Select]
#define WS_TOPMOST      0x00200000L
WinSetWindowBits( hwndFrame, QWL_STYLE, WS_TOPMOST, WS_TOPMOST );
Title: Re: how to stay a window on top
Post by: Martin Vieregg on April 03, 2020, 11:21:38 pm
I'm sorry, but the WinSetWindowBits code seems not to work.

A system wide Float to top would be helpful, too, because it is also part of the Freepascal Lazarus code. (FormStyle = fsSystemStayOnTop) But all variants are non-modal.
Title: Re: how to stay a window on top
Post by: Martin Vieregg on April 03, 2020, 11:29:10 pm
I'm sorry, but the WinSetWindowBits code seems not to work.

A system wide Float to top would be helpful, too, because it is also part of the Freepascal Lazarus code. (FormStyle = fsSystemStayOnTop) But all variants are non-modal.
Title: Re: how to stay a window on top
Post by: Laurence Pithie on April 04, 2020, 12:51:15 am
One option would be to place the Main window behind the client window using WiinSetWindowPos when the client program is active. One way to  achieve that is by placing a check for the clinet program being active and calling WinSetWindowPos at the end of processing the WM_PAINT message in the main window procedure. Essentially what you woulld be doing is swapping the Z order of the two windows if the user brings the main window to the front while the client program is active.
Title: Re: how to stay a window on top
Post by: Lars on April 04, 2020, 09:35:59 pm
If your child window is a sibling of your main window (whatever "child" and "main" window means in your context), you can use WinSetWindowPos with SWP_ZORDER to place your child window on top of any other sibling windows.

Sibling windows have a common parent window. If you can show us the relationship between your "child" window and "main" window (using PMTree, for example) then it should be possible to come up with a solution.
Title: Re: how to stay a window on top
Post by: Lars on April 04, 2020, 09:45:20 pm
Or try WinSetActiveWindow. See the help for this function. The active window will be placed above all other top-level windows on the screen (see "Window activation" in the help).
Title: Re: how to stay a window on top
Post by: Martin Vieregg on April 07, 2020, 09:36:27 pm
Both "WinSetWindowPos with SWP_ZORDER" and "WinSetActiveWindow" let the window appear on top, but only for the moment until another window gets the focus. I am searching for a permanent way that another window cannot float above my window.
Title: Re: how to stay a window on top
Post by: Dave Yeo on April 08, 2020, 01:29:23 am
The screensaver seems to use this,
Code: [Select]
      {
        SWP *pSWP;

        // The following is required so that this window will be on
        // top of the xCenter window, evenif that is set to be always on top!

        // Real screensaving, here we should stay on top!
        // Set WS_TOPMOST flag again!
        WinSetWindowBits(hwnd, QWL_STYLE, WS_TOPMOST, WS_TOPMOST);

        pSWP = (SWP *) mp1;
        pSWP->hwndInsertBehind = HWND_TOP;
        pSWP->fl |= SWP_ZORDER;
      }

Not an expert though and not sure about the SWP stuff.
Title: Re: how to stay a window on top
Post by: Martin Vieregg on April 08, 2020, 12:22:36 pm
I think that in this code the last API call where the new pSWP values are sent to the system is missing.
Title: Re: how to stay a window on top
Post by: Doodle on April 09, 2020, 03:55:45 pm
Once you set your window to be styled WS_TOPMOST, it gets above all other windows, even previous topmost ones.
Fortunately, when the Z-Order of the windows changes, the affected windows get a window message WM_ADJUSTWINDOWPOS, in which you can realize that some other app has gone above your window, and you can set your window to be topmost again then.

It's a bit risky, as when this meets other similar code, they will "fight" being topmost, and probably cause flicker and eat up CPU.

Still, this seems to work in DSSaver, as Dave has pointed out.

For an example, please download the source code of DSSaver, and check one of the modules (preferable the Blank module, as that has the least additional code) on how it's done:
ftp://ftp.netlabs.org/pub/dssaver/dssaver_v20_srcbin.zip
Once unzipped, check the file Modules\Blank\Blank.c, search for lines containing "topmost", and you'll see how it's done in there.

Good luck!
Title: Re: how to stay a window on top
Post by: Dave Yeo on April 09, 2020, 04:56:01 pm
It's a bit risky, as when this meets other similar code, they will "fight" being topmost, and probably cause flicker and eat up CPU.

Which will happen when the screensaver kicks in. https://hobbes.nmsu.edu/download/pub/os2/apps/scrnsave/dss_rmt_v13.zip (https://hobbes.nmsu.edu/download/pub/os2/apps/scrnsave/dss_rmt_v13.zip) is an example of a REXX script to control the screensaver, including a watchdog to disable the screensaver if certain processes are running.
Perhaps the always on top should be an option?
Title: Re: how to stay a window on top
Post by: Martin Vieregg on April 11, 2020, 10:57:12 am
Quote
WS_TOPMOST

I have no such constant in Borland C 2.0 for OS/2 and a in WDsibyl a constant WS_EX_TOPMOST 0x00000008

Is 8 the right value for WS_TOPMOST ? In pmwindow.h I have got a value
#define WS_THICKFRAME       0x00000008L
Title: Re: how to stay a window on top
Post by: Doodle on April 11, 2020, 12:19:29 pm
Quote
Is 8 the right value for WS_TOPMOST ?

In my previous reply, I wrote:

Quote
For an example, please download the source code of DSSaver, and check one of the modules (preferable the Blank module, as that has the least additional code) on how it's done:
ftp://ftp.netlabs.org/pub/dssaver/dssaver_v20_srcbin.zip
Once unzipped, check the file Modules\Blank\Blank.c, search for lines containing "topmost", and you'll see how it's done in there.

Searching for lines containing "topmost" in that Blank.c file results these:

Code: [Select]
// Undocumented flag to make a window topmost:
#define WS_TOPMOST  0x00200000L

...

// Set WS_TOPMOST flag again!
WinSetWindowBits(hwnd, QWL_STYLE, WS_TOPMOST, WS_TOPMOST);

...

// Make window 'Always on top' because we'll be in real screensaver mode!
ulStyle = WS_VISIBLE | WS_TOPMOST;
hwndSaverWindow = WinCreateWindow(HWND_DESKTOP, SAVERWINDOW_CLASS, "Screen saver",
                                      ulStyle,
                                      0, 0,
                                      (int) WinQuerySysValue(HWND_DESKTOP, SV_CXSCREEN),
                                      (int) WinQuerySysValue(HWND_DESKTOP, SV_CYSCREEN),
                                      HWND_DESKTOP,
                                      HWND_TOP,
                                      0x9fff, // Some ID....
                                      NULL, NULL);


Hope this helps!
Title: Re: how to stay a window on top
Post by: Martin Vieregg on April 29, 2020, 11:00:01 pm
Code: [Select]
#define WS_TOPMOST  0x00200000L

...
ulStyle = WS_VISIBLE | WS_TOPMOST;
hwndSaverWindow = WinCreateWindow(...ulStyle...)

Great, thank you Doodle. This code works. But it only works if I set the flag when creating the window, later with WinSetWindowBits it does not has an effect.

But now the window floats system wide. This is a very interesting and useful setting. I implement this lazarus-compatible in WDsibyl:
tForm.FormStyle = fsSystemStayOnTop

But I also want to implement the same, but only for a single application, so windows of other applications can hide my  "topmost" window:
tForm.FormStyle = fsStayOnTop

Is there also a flag or a way where I can achieve this?
Title: Re: how to stay a window on top
Post by: Martin Vieregg on May 07, 2020, 11:38:01 pm
Doodle,

I'm sorry for the false alarm, but all the code you have posted is working, even the code

Code: [Select]
WinSetWindowBits(hwnd, QWL_STYLE, WS_TOPMOST, WS_TOPMOST);
It seems that I did not use the correct frame handle. And I have to put the window on top again after this call, and then it stays on top system wide.

But I did not manage to turn it off again.

And I still did not manage to float on top only between my program windows and windows of other programs can hide my top window. WS_TOPMOST is system wide. I want to implement both options in the WDsibyl library.
Title: Re: how to stay a window on top
Post by: Digi on May 09, 2020, 05:21:58 pm
And I still did not manage to float on top only between my program windows and windows of other programs can hide my top window. WS_TOPMOST is system wide.

As far as I remember, WXP / XPager uses a hook for these purposes ("Stay on top" option). Probably, standard methods for this are not provided.
Title: Re: how to stay a window on top
Post by: Doodle on May 09, 2020, 05:39:11 pm
Hi Martin,

I afraid that OS/2 does not have such a built-in windows style that you're seeking. The WS_TOPMOST (as you've already found out) has a system-wide effect. It will be on top of all other windows (until another gets WS_TOPMOST, or another WS_TOPMOST window gets moved on top, etc...).

If you want to have your window to be above all your windows, but not foreign windows, I think that you have to implement your own logic in all of your window procedures to handle the case when Z-order changes (WM_ADJUSTWINDOWPOS message, IIRC), and every time your window would get on top, set your "app-topmost" window to be on top.

Doodle
Title: Re: how to stay a window on top
Post by: Martin Vieregg on May 09, 2020, 06:33:57 pm
Thank you Doodle for your post. But I wonder why a Dialog Window has the behaviour I would like to have. Dialog Windows always stay on top above the main program (standard) window. I thaught that all windows are internally the same for the operating system and only with different properties. So it seems that you cannot write a Dialog Window call with WinCreateWindow or WinCreateStandardWindow.

In WDsibyl, I have got a list of existing forms of an application in the main form. A form which changes the ZOrder to TOP should send a message to the main form of the application and the main form should send a message back to all other forms. If the other form has got the "StayOnTop" property set, it should get the ZOrder TOP again. This conecpt works without timer messages. I will report if I managed it.

Do you have an idea how to turn off the system wide WS_TOPMOST flag after it has been set?



Title: Re: how to stay a window on top
Post by: Doodle on May 09, 2020, 11:40:02 pm
Quote
I wonder why a Dialog Window has the behaviour I would like to have. Dialog Windows always stay on top above the main program (standard) window.

The Dialog Windows are created with a different API (WinCreateDialog), and their window procedure has to follow different rules (return if a message was processed or not). If it's not processed, a default dialog window procedure of the OS/2 system will handle the message, and that already has this logic implemented, that when the dialog window gets activated and shown, it disables its owner window for the time the dialog window is active. If a window is disabled (see WinEnableWindow API), the user cannot interact with it, so you cannot click on the main window if you have a child dialog window active.

It's quite nicely described in the Presentation Manager Programming Guide:
ftp://bitsavers.informatik.uni-stuttgart.de/pdf/ibm/pc/os2/warp_ver_3/G25H-7103-00_OS2_WARP_V3_Presentation_Manager_Programming_Guide_The_Basics_Oct94.pdf

Focus on chapter 15-1 (Dialog Windows), most importantly on "modal" dialogs.


Quote
Do you have an idea how to turn off the system wide WS_TOPMOST flag after it has been set?

Sorry, I don't know. I never had to do that, but I always thought that removing the flag would be enough..
Title: Re: how to stay a window on top
Post by: Martin Vieregg on May 10, 2020, 08:11:52 pm
Now I managed to implement all the functionality I wanted for the WDsibyl library:
tForm.Formstyle = fsSystemStayOnTop
tForm.Formstyle = fsStayOnTop

For turning on the TOPMOST functionality, I used
WinSetWindowBits(hwnd, QWL_STYLE, WS_TOPMOST, WS_TOPMOST);
Turning off:
WinSetWindowBits(hwnd, QWL_STYLE, 0, WS_TOPMOST);

If the window is not created while setting the property, the window is created with the WS_TOPMOST flag.

For "fsStayOnTop", I have fetched all events where other window go to HWND_TOP of  and in these cases, all the forms of the program which have got the fsStayOnTop property set are going to top after the window which has gone to top by the user. It works already fine.

I send now the changes to Rolf which collects the libary changes. I will send a message here if the new WDsibyl library is available.
Title: Re: how to stay a window on top
Post by: Rich Walsh on May 11, 2020, 03:15:01 pm
I wonder why a Dialog Window has the behaviour I would like to have. Dialog Windows always stay on top above the main program (standard) window.

This is really sad. Hasn't anyone RTFM?

You're about to abuse a powerful - and possibly very annoying - feature (WS_TOPMOST) simply because neither you nor any of the other respondents knows one of the basics of PM programming: an owned window is always higher in the z-order than the frame window which owns it.

If a dialog is always on top of the window which created it, it's because the dialog is owned by that window. Change its owner to HWND_DESKTOP and the dialog will be free to go anywhere in the z-order. If you need a specific z-order for your windows, establish an ownership chain - don't use WS_TOPMOST.

BTW... there are times when WS_TOPMOST is called for. but it should always be at the user's discretion (i.e. he can turn it off).
Title: Re: how to stay a window on top
Post by: Lars on May 12, 2020, 11:52:32 am
I think you meant to say "parent-child relationship" instead of "ownership" but the explanation in the "Presentation Manager Programming Guide" is a bit blurry about z-ordering.
Title: Re: how to stay a window on top
Post by: Martin Vieregg on May 12, 2020, 03:01:47 pm
Rich,

the WS_TOPMOST setting is for the system wide float on top. This can be useful for very specific programs like a virtual desktop manager, a tool bar or a clock. In all these cases, the window which is allowed to float to system top should be small. It does not get the focus, of course. Because of the lack of knowing the existance of this hidden flag, for example the PMVdesk virtual desktop tool and others do floating via timer.

It was indeed not clear for me that the relationship of the child window defines the behaviour of the ZOrder. But in a lot of cases, a float or "rest" on top is not useful. For example, my WSedit editor has got a content tree window which should be placed on the desktop with the same rights as the main window.

So in the WDsibyl library, all windows are started without ownership and are listed in the Screen.Forms[] property. (A created tForm does not mean automatically that the window has been created on the OS-handle level. This is done in SetupShow.) One window is the Application.Mainform, but this only means that this window is started first when starting the application. I had problems with cleaning up the windows when creating a window with an owner. The current state is pehaps not "holy PM programming", but it works fine. I am not sure if I can turn off the relationship after creating a window with an owner. With the new FormStyle = wsStayOnTop property I can turn on and off this setting while the window exists.