WIN32: computing the width and height of window borders

1,163 views
Skip to first unread message

Manolo

unread,
Jan 5, 2016, 9:46:51 AM1/5/16
to fltk.coredev
For the WIN32 platform, the width and height of window borders and title bar
is presently computed in two ways:

1)  by the fake_X_wm_style() function that itself uses 2 computations depending on
whether AdjustWindowRectEx() runs OK or not. This is called, among other cases,
at window creation by Fl_X::make().

2)  by border_width_title_bar_height()  called by Fl_Window::decorated_w() and
Fl_Window::decorated_h() that also uses 2 methods depending on whether
dwmapi.dll is available and the DwmGetWindowAttribute() call runs OK
or not.

I have found that the values returned by 1) are correct under MSWindows XP and 7
and incorrect under Win10 (see attached table: bt is the title bar height,
bx and by are the border widths). In all tested cases, procedure 2) gives
the same values as those observed on the display.

At this point, I did not see erroneous behaviours of the FLTK library.
But it would be useful to
- determine what happens with Win 8
- determine whether it is possible to compute these values in one procedure
only and consistently across OS versions. Procedure 2) would not apply
because it requires a mapped window. Procedure 1) is wrong with Win 10.
MSWindowDecorations.png

Albrecht Schlosser

unread,
Jan 5, 2016, 10:48:43 AM1/5/16
to fltkc...@googlegroups.com
I did a quick test under Win8 by adding a printf statement at the end of
fake_X_wm(), and it appears to show a similar behavior as under Win10. I
got:

$ ./ask
fake_X_wm() returns: bx=8, by=8, bt=23
fake_X_wm() returns: bx=3, by=3, bt=23
fake_X_wm() returns: bx=3, by=3, bt=23
fake_X_wm() returns: bx=3, by=3, bt=23
$ ./tabs
fake_X_wm() returns: bx=8, by=8, bt=23
$ ./valuators
fake_X_wm() returns: bx=3, by=3, bt=23

Most of the values look correct, if bt is only the size of the window
title area and by is the additional frame height between the title area
and the window's client area - I can see a total height of the area on
top of the client area of 31 pixels.

The border width (bx, by) always seems to be 8. I don't see a difference
whether the window is resizable nor if it is modal on the display (i.e.
the /observed/ border size is always 8), but the calculated size is 3
for non-resizable windows ('valuators') and modal windows (lines 2-4 of
'ask'), which are not resizable as well.

I don't know how to test the other values, and my time doesn't permit
further tests now, but I'd be glad to assist and test more under win7
and win8 if you can provide a test program and/or patch like the one
used by me (see attachment). Maybe you have one readily available...

window_borders.diff

Manolo Gouy

unread,
Jan 5, 2016, 11:34:04 AM1/5/16
to fltkc...@googlegroups.com
Thanks for testing.



$ ./ask
fake_X_wm() returns: bx=8, by=8, bt=23
fake_X_wm() returns: bx=3, by=3, bt=23
fake_X_wm() returns: bx=3, by=3, bt=23
fake_X_wm() returns: bx=3, by=3, bt=23
$ ./tabs
fake_X_wm() returns: bx=8, by=8, bt=23
$ ./valuators
fake_X_wm() returns: bx=3, by=3, bt=23

Most of the values look correct, if bt is only the size of the window title area and by is the additional frame height between the title area and the window's client area - I can see a total height of the area on top of the client area of 31 pixels.

The border width (bx, by) always seems to be 8. I don't see a difference whether the window is resizable nor if it is modal on the display (i.e. the /observed/ border size is always 8), but the calculated size is 3 for non-resizable windows ('valuators') and modal windows (lines 2-4 of 'ask'), which are not resizable as well.

I don’t have Win8 available, so cannot test. Under XP and WIn7 the border width differences 
between resizable/non-resizable windows computed by fake_X_wm() are actually visible 
on screen (I had never noticed that before today).
Under Win10, the actual, visible difference is in the title bar height, as reported by the new 
border_width_title_bar_height() function.

So, apparently there is no visible difference under Win8. Is it a transitional stage between
border differences (Win7) and title-bar differences (Win10)?

I attach the modified widths table, with your WIn8-related values.

The issue here is whether bx, by, and bt values computed by fake_X_wm() are correct.
If there is no actual border difference according to window resizability, then the answer is INCORRECT,
because your test shows that returned values do differ. 

I don't know how to test the other values, and my time doesn't permit further tests now, but I'd be glad to assist and test more under win7 and win8 if you can provide a test program and/or patch like the one used by me (see attachment). Maybe you have one readily available…

It is possible to test the second computation as follows:
- compile Fl.cxx with -DUSE_PRINT_BUTTON
- add a print bx,by,bt statement at the end of function border_width_title_bar_height() in Fl_win32.cxx
- use the "Print front window" button with diverse windows and test apps.



Albrecht Schlosser

unread,
Jan 5, 2016, 12:27:35 PM1/5/16
to fltkc...@googlegroups.com
On 05.01.2016 17:34 Manolo Gouy wrote:
>
>> Le 5 janv. 2016 à 16:48, Albrecht wrote :
>> ...
>> I did a quick test under Win8 by adding a printf statement at the end
>> of fake_X_wm(), and it appears to show a similar behavior as under
>> Win10. I got:
>> ...
>> The border width (bx, by) always seems to be 8. I don't see a
>> difference whether the window is resizable nor if it is modal on the
>> display (i.e. the /observed/ border size is always 8), but the
>> calculated size is 3 for non-resizable windows ('valuators') and modal
>> windows (lines 2-4 of 'ask'), which are not resizable as well.
>
> I don’t have Win8 available, so cannot test. Under XP and WIn7 the
> border width differences
> between resizable/non-resizable windows computed by fake_X_wm() are
> actually visible
> on screen (I had never noticed that before today).

On my Win8 the border width is constant, AFAICT. I measured the width
with Greg's fl_ruler app, and it confirms it's really 8.

> Under Win10, the actual, visible difference is in the title bar height,
> as reported by the new
> border_width_title_bar_height() function.
>
> So, apparently there is no visible difference under Win8. Is it a
> transitional stage between
> border differences (Win7) and title-bar differences (Win10)?
>
> I attach the modified widths table, with your WIn8-related values.

I believe you can remove the question mark ("8 ?") from the table at
Win8 not-resizable.

> The issue here is whether bx, by, and bt values computed by fake_X_wm()
> are correct.
> If there is no actual border difference according to window
> resizability, then the answer is INCORRECT,
> because your test shows that returned values do differ.

Yes, they do, but the window borders have all the same width. So yes,
the computation appears to be wrong.

>> I don't know how to test the other values, and my time doesn't permit
>> further tests now, but I'd be glad to assist and test more under win7
>> and win8 if you can provide a test program and/or patch like the one
>> used by me (see attachment). Maybe you have one readily available…
>
> It is possible to test the second computation as follows:
> - compile Fl.cxx with -DUSE_PRINT_BUTTON
> - add a print bx,by,bt statement at the end of function
> border_width_title_bar_height() in Fl_win32.cxx
> - use the "Print front window" button with diverse windows and test apps.

Okay, I tried another patch and your proposed test. Here are the results:

$ bin/examples/ask.exe
fake_X_wm_style() returns: bx=8, by=8, bt=23
fake_X_wm_style() returns: bx=3, by=3, bt=23
fake_X_wm_style() returns: bx=3, by=3, bt=23
border_width_title_bar_height() returns: bx=8, by=8, bt=23
border_width_title_bar_height() returns: bx=8, by=8, bt=23
border_width_title_bar_height() returns: bx=8, by=8, bt=23
fake_X_wm_style() returns: bx=3, by=3, bt=23
fake_X_wm_style() returns: bx=3, by=3, bt=23
fake_X_wm_style() returns: bx=3, by=3, bt=23
border_width_title_bar_height() returns: bx=8, by=8, bt=23
border_width_title_bar_height() returns: bx=8, by=8, bt=23
border_width_title_bar_height() returns: bx=8, by=8, bt=23
fake_X_wm_style() returns: bx=3, by=3, bt=23
fake_X_wm_style() returns: bx=3, by=3, bt=23

Note: all window borders are 8 px wide on screen, despite the different
numbers.

Attachments:

window_borders.diff: my new test (patch) with printf()'s
window_borders.png: screen shot of test (ask.exe)
window_borders.pdf: print output to a PDF printer

I hope these attachments make it to the mailing list...

I encourage everybody to test on different Windows versions with my
supplied test patch (and -DUSE_PRINT_BUTTON as Manolo suggested).
window_borders.diff
window_borders.png
window_borders.pdf

MacArthur, Ian (Selex ES, UK)

unread,
Jan 5, 2016, 12:36:46 PM1/5/16
to fltkc...@googlegroups.com
I'm not sure if this is pertinent, but I seem to recall that when we looked at the SM_CXPADDEDBORDER stuff (was that STR #3061 or something? I think so...) that there were some really quite weird values coming out of the Win7/Win8 tests, particularly if the user had set some custom values for the window borders and so forth, rather than using the MS defaults.

I think there was weird stuff with the border and the borderpadding values, and the deltas didn't seem to end up where I'd have imagined... Like why do you need both a border value and a border-padding value anyway?

Presumably the SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &ncm, 0); stuff to measure the window is only useful once a window is mapped? Or does it yield something useful even before we create the window (though how would it know?)


I have no idea...



Selex ES Ltd
Registered Office: Sigma House, Christopher Martin Road, Basildon, Essex SS14 3EL
A company registered in England & Wales. Company no. 02426132
********************************************************************
This email and any attachments are confidential to the intended
recipient and may also be privileged. If you are not the intended
recipient please delete it from your system and notify the sender.
You should not copy it or use it for any purpose nor disclose or
distribute its contents to any other person.
********************************************************************

Manolo Gouy

unread,
Jan 5, 2016, 1:18:57 PM1/5/16
to fltkc...@googlegroups.com
Updated table :

Ian MacArthur

unread,
Jan 5, 2016, 6:08:46 PM1/5/16
to fltkc...@googlegroups.com

Harking back to my earlier post, do we know what:-

SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &ncm, 0);

returns for Win8 and Win10 systems? I have access to neither...



Also, since I have been reading the MSDN docs, it rather appears we *might* be using that wrong anyway.

The docs say that:

<quote>

Retrieves the metrics associated with the nonclient area of nonminimized windows. The pvParamparameter must point to a NONCLIENTMETRICS structure that receives the information. Set the cbSize member of this structure and the uiParam parameter to sizeof(NONCLIENTMETRICS).

</quote>



Now, we do correctly set the

ncm.cbSize = sizeof(NONCLIENTMETRICS);

But we are not setting the uiParam value in the call, so this probably ought to be:


ncm.cbSize = sizeof(NONCLIENTMETRICS);
SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &ncm, 0);

(and potentially we should zero the ncm struct before the call? Dunno... but examples I have seen often do...)


Also, it seems there are some weirdnesses with NONCLIENTMETRICS and border padding too:-


<quote>

When querying NONCLIENTMETRICS you need to adjust the size based on the WINVER setting and the OS.
If you run an application on pre-vista OS then the size set in cbSize needs to be adjusted.

NONCLIENTMETRICS ncm;
memset(&ncm, 0, sizeof(NONCLIENTMETRICS));
ncm.cbSize = sizeof(NONCLIENTMETRICS);

#if(WINVER >= 0x0600) // if compiling on a later WinXX variant
OSVERSIONINFO osvi;
memset(&osvi,0,sizeof(osvi));
osvi.dwOSVersionInfoSize = sizeof(osvi);
GetVersionEx(&osvi);
if (osvi.dwMajorVersion < 6)
ncm.cbSize -= sizeof(ncm.iPaddedBorderWidth);
#endif


Remarks

If the iPaddedBorderWidth member of the NONCLIENTMETRICS structure is present, this structure is 4 bytes larger than for an application that is compiled with _WIN32_WINNT less than or equal to 0x0502.
For more information about conditional compilation, see Using the Windows Headers.

Windows Server 2003 and Windows XP/2000: If an application that is compiled for Windows Server 2008 or Windows Vista must also run on Windows Server 2003 or Windows XP/2000, use the GetVersionEx function to check the operating system version at run time and, if the application is running on Windows Server 2003 or Windows XP/2000, subtract the size of the iPaddedBorderWidth member from the cbSize member of the NONCLIENTMETRICS structure before calling the SystemParametersInfo function.

</quote>



Which makes me wonder if that accounts for why the mingw builds do not seem to trigger these issues: The mingw version of gcc sets WINVER to a relatively low number by default, and the highest we ever explicitly raise it is maybe 0x500 or so I think...(?)

Now, the VS tools set a value for WINVER which I think tracks the host system, so likely to be bigger than 0x500, indeed bigger than or equal to 0x600, so taking us into the realms of weird padded border values and such... (0x600 is Vista, 0x500 is XP I think; Win8 is 0x602)

Is it feasible that this accounts for the observed behaviours, and the differences between the VS and mingw tests?

And is using SystemParametersInfo(SPI_GETNONCLIENTMETRICS,...) “properly” a possible way out?







Manolo Gouy

unread,
Jan 6, 2016, 3:01:57 AM1/6/16
to fltkc...@googlegroups.com
The SystemParametersInfo() call is no longer used by the fake_X_wm_style() function.
The size info is now computed with AdjustWindowRectEx():
input: client RECT and window style
output: full RECT for a decorated window having the input RECT as client area.
> --
> You received this message because you are subscribed to the Google Groups "fltk.coredev" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to fltkcoredev...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

Manolo Gouy

unread,
Jan 6, 2016, 4:05:10 AM1/6/16
to fltkc...@googlegroups.com
It seems that Win10 uses two distinct window measurement systems in parallel :

1) the "old" API with GetWindowRect() and AdjustWindowRectEx().
FLTK uses it when creating a window and asking:
    What full window size should I use to get the client area I want?
FLTK also uses it when it programmatically resizes a window.

2) the "new" API with DwmGetWindowAttribute() of dwmapi.dll 
FLTK uses it in Fl_Window::decorated_w() and Fl_Window::decorated_h()  
and when printing, capturing, or copying a decorated window.

On Win10, the new API gives the exact window dimensions seen on the display.
This is what is needed for the uses FLTK makes of these dimensions.

On Win10, the old API gives wrong dimensions, but the sequence
   AdjustWindowRectEx()
   CreateWindowExW()
does what FLTK wants, a decorated window with a client area of known size.

On Win XP and Win7 the new API is not available, or not functional, so FLTK uses the old one.

On Win8: investigation needed.

FLTK is also correct when putting windows fullscreen, because there is no window decoration,
so no API difference.

A possible minor problem might be as follows: ask for a decorated window 
that fully covers the screen work area. The user will employ the new API to compute the desired client area,
and FLTK will use the old API (which gives larger dimensions than real) to construct the window. 
Needs investigation on Win10.

I added this at the end of the Fl_X::make() function (just before "return x;")

  WINDOWINFO info;
  GetWindowInfo(x->xid, &info);
  RECT fullrect;
  GetWindowRect(x->xid, &fullrect);
  fprintf(LOG, "client area according to FLTK %dx%d | to OS %dx%d\n"
          "full win size according to GetWindowRect() %dx%d | to DwmGetWindowAttribute() %dx%d\n\n\n",
          w->w(), w->h(), info.rcClient.right - info.rcClient.left, info.rcClient.bottom - info.rcClient.top, 
          fullrect.right - fullrect.left, fullrect.bottom - fullrect.top, w->decorated_w(), w->decorated_h());

and run the resize demo program and got:

On XP

On Win10


Overall, FLTK seems to be doing OK with the evolving MSWindows APIs !


MacArthur, Ian (Selex ES, UK)

unread,
Jan 6, 2016, 4:55:58 AM1/6/16
to fltkc...@googlegroups.com
> The SystemParametersInfo() call is no longer used by the
> fake_X_wm_style() function.
> The size info is now computed with AdjustWindowRectEx():
> input: client RECT and window style
> output: full RECT for a decorated window having the input RECT as
> client area.


Well, OK; it is still *maybe* used, but I think only in the (presumably rare and unusual) case that AdjustWindowRectEx() fails, so we are using the fallback path, *and* the window is *not* resizable.
Well, I think that's the condition!

Anyway... In any case, it looks like we might be calling SystemParametersInfo() wrong, in the rare (?) cases that we do call it at all.

Really, I just wondered if it told us anything interesting; but your approach seems to be getting sensible answers now?

manol...@univ-lyon1.fr

unread,
Jan 6, 2016, 7:13:40 AM1/6/16
to fltkc...@googlegroups.com

> Le 6 janv. 2016 à 10:05, Manolo wrote :
>
> It seems that Win10 uses two distinct window measurement systems in parallel :
>
> 1) the "old" API with GetWindowRect() and AdjustWindowRectEx().
> FLTK uses it when creating a window and asking:
> What full window size should I use to get the client area I want?
> FLTK also uses it when it programmatically resizes a window.
>
> 2) the "new" API with DwmGetWindowAttribute() of dwmapi.dll
> FLTK uses it in Fl_Window::decorated_w() and Fl_Window::decorated_h()
> and when printing, capturing, or copying a decorated window.
>
> On Win10, the new API gives the exact window dimensions seen on the display.
> This is what is needed for the uses FLTK makes of these dimensions.
>
> On Win10, the old API gives wrong dimensions, but the sequence
> AdjustWindowRectEx()
> CreateWindowExW()
> does what FLTK wants, a decorated window with a client area of known size.
>
> On Win XP and Win7 the new API is not available, or not functional, so FLTK uses the old one.
>
> On Win8: investigation needed.
>
> FLTK is also correct when putting windows fullscreen, because there is no window decoration,
> so no API difference.
>
> A possible minor problem might be as follows: ask for a decorated window
> that fully covers the screen work area. The user will employ the new API to compute the desired client area,
> and FLTK will use the old API (which gives larger dimensions than real) to construct the window.
> Needs investigation on Win10.

This potential problem does not occur on WIn10. Tested with:

#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Button.H>

void cb(Fl_Widget *wid, void *data)
{
Fl_Window *win = wid->window();
int bx = (win->decorated_w() - win->w())/2;
int bt = win->decorated_h() - win->h() - 2*bx;
int xwa, ywa, wwa, hwa;
Fl::screen_work_area(xwa, ywa, wwa, hwa, 0);
win->resize(xwa+bx, ywa+bx+bt, wwa - 2*bx, hwa - (2*bx+bt));
win->redraw();
}

int main()
{
Fl_Window* win = new Fl_Window(500, 500, "Title") ;
Fl_Button *button = new Fl_Button(50,50,100,30,"resize");
button->callback(cb);
win->end() ;
win->resizable(win);
win->show() ;

return Fl::run() ;
}

============

Reply all
Reply to author
Forward
0 new messages