FLTK logo

STR #3061

FLTK matrix user chat room
(using Element browser app)   FLTK gitter user chat room   GitHub FLTK Project   FLTK News RSS Feed  
  FLTK Apps      FLTK Library      Forums      Links     Login 
 Home  |  Articles & FAQs  |  Bugs & Features  |  Documentation  |  Download  |  Screenshots  ]
 

Return to Bugs & Features | Roadmap 1.3 | SVN ⇄ GIT ]

STR #3061

Application:FLTK Library
Status:1 - Closed w/Resolution
Priority:3 - Moderate, e.g. unable to compile the software
Scope:2 - Specific to an operating system
Subsystem:WIN32
Summary:Non-resizable window border width is miscalculated in certain conditions on Windows Vista and up
Version:1.3.2
Created By:greentea101
Assigned To:AlbrechtS
Fix Version:1.3.3 (SVN: v10363)
Update Notification:

Receive EMails Don't Receive EMails

Trouble Report Files:


Name/Time/Date Filename/Size  
 
#1 greentea101
15:33 Mar 02, 2014
FLTK_window_issue.png
31k
 
 
#2 AlbrechtS
12:47 Sep 18, 2014
border_padding.patch
1k
 
     

Trouble Report Comments:


Name/Time/Date Text  
 
#1 greentea101
15:33 Mar 02, 2014
Problem symptom:
================

When an executable is built with MajorSubsystemVersion = 6 in the optional header of the image, the initial drawing of non-resizable FLTK windows (via Show()) omits a number of pixels from the client area that is proportional to the Border Padding setting in Windows. Specifically, the right and bottom borders are drawn over the client area, as shown in the image I attached. If the window is moved, the move operation causes the window to be drawn correctly.

In Visual C++ 2012, the subsystem version is controlled by the setting "Configuration Properties->General->Platform Toolset" from the project settings. Alternatively, it can also be changed from "Linker->System->Minimum Required Version". I wouldn't know about other IDEs and compilers.

The MajorSubsystemVersion and MinorSubsystemVersion data can also be modified "after the fact" by editing the PE header with a tool like CFF Explorer, to the same effect.


Problem cause:
==============

The cause of this behavior has to do with the way Windows uses the Border Padding setting, and the fact that FLTK doesn't take that setting into account altogether.
Windows 7 uses padding to draw the border of resizable windows, and doesn't use it for non-resizable windows EXCEPT if MajorSubsystemVersion is 6 in the PE header. This is true if the Windows Classic or Windows Basic themes are being used. If the Aero theme is used, then all windows respect the Border Padding setting without exception, but the behavior I described still happens for whatever reason.

Inside the function Fl_X::fake_X_wm() from Fl_win32.cxx, the following code is found:

if (w->size_range_set && (w->maxw != w->minw || w->maxh != w->minh)) {
        ret = 2;
        bx = GetSystemMetrics(SM_CXSIZEFRAME);
        by = GetSystemMetrics(SM_CYSIZEFRAME);
} else {
        ret = 1;
        bx = GetSystemMetrics(SM_CXFIXEDFRAME);
        by = GetSystemMetrics(SM_CYFIXEDFRAME);
}

That code determines that the border width for non-resizable windows is GetSystemMetrics(SM_CXFIXEDFRAME), which is fine if MajorSubsystemVersion < 6, but is not fine if MajorSubsystemVersion = 6, because in that case the border padding is taken into account as well.

I have fixed it like this:

if (w->size_range_set && (w->maxw != w->minw || w->maxh != w->minh)) {
        ret = 2;
        bx = GetSystemMetrics(SM_CXSIZEFRAME);
        by = GetSystemMetrics(SM_CYSIZEFRAME);
} else {
        ret = 1;
        int padding = GetSystemMetrics(92); // SM_CXPADDEDBORDER
        bx = GetSystemMetrics(SM_CXFIXEDFRAME) + (padding ? padding + GetSystemMetrics(SM_CXBORDER) : 0);
        by = GetSystemMetrics(SM_CYFIXEDFRAME) + (padding ? padding + GetSystemMetrics(SM_CYBORDER) : 0);
}

I couldn't use SM_CXPADDEDBORDER because it was undefined (because _WIN32_WINNT was defined as 0x0500, and SM_CXPADDEDBORDER is not supported on WinXP), so I used its value (92). If MajorSubsystemVersion < 6, then GetSystemMetrics(92) returns 0, and if MajorSubsystemVersion = 6 then it returns 4 or whatever the "BorderPadding" setting is.
 
 
#2 ianmacarthur
08:35 Mar 03, 2014
Well, I guess this looks right - I've spent *some* time staring at MSDN, and all the bits make sense, but I still don't know what MS are doing here...

Why do we need to measure GetSystemMetrics(SM_CXBORDER) *and* the padding, for example? Why can't one parameter do the job?

And why doesn't the WM account for this itself? Why does it have to be done in user code?
I'm sure that window borders always *used* to be handled by the WM without special handling by the user code...?

Anyway, if we do go with this, I'd suggest adding something like...

#ifndef SM_CXPADDEDBORDER
#  define SM_CXPADDEDBORDER 92
#endif /* SM_CXPADDEDBORDER set */

...somewhere near the top of Fl_win32.cxx to work around SM_CXPADDEDBORDER not existing on older Win32 variants. We already define a bunch of "missing" WM_whatever messages there anyway to help out older Windows compilers...
 
 
#3 greentea101
19:28 Mar 03, 2014
I've looked at a bunch of windows in an image editor, and the width of what looks like the padding is actually the sum of the values of the "Border Padding" and "Active Window Border" settings. Unless Aero is being used, in which case it's "Border Padding" + "Active Window Border" - 1. Why? I really couldn't tell you.  
 
#4 greentea101
20:16 Mar 03, 2014
I've just made another unsettling discovery. GetSystemMetrics(SM_CXBORDER) always returns 1, and I have no idea how the get the actual value of "Active Window Border". Also, if I change the color of the "Active Window Border", the padding takes on that color as well, as if the two settings are the two halves of the same horrific entity, or something.

Most people don't bother to change "Active Window Border" from the default of 1, but still, this is not very nice.
 
 
#5 greentea101
12:27 Mar 16, 2014
I am currently using this code:

if (w->size_range_set && (w->maxw != w->minw || w->maxh != w->minh)) {
        ret = 2;
        bx = GetSystemMetrics(SM_CXSIZEFRAME);
        by = GetSystemMetrics(SM_CYSIZEFRAME);
} else {
        ret = 1;
        int padding = GetSystemMetrics(92); // SM_CXPADDEDBORDER
        NONCLIENTMETRICS ncm;
        ncm.cbSize = sizeof(NONCLIENTMETRICS);
        SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &ncm, 0);
        bx = GetSystemMetrics(SM_CXFIXEDFRAME) + (padding ? padding + ncm.iBorderWidth : 0);
        by = GetSystemMetrics(SM_CYFIXEDFRAME) + (padding ? padding + ncm.iBorderWidth : 0);
}

This finally works for all possible cases.
 
 
#6 cristiantm
05:51 May 21, 2014
I just would like to add that
1) the fix works for me on Windows 8, Visual Studio 12 (2013) and my standard and custom dialogs;
2) This is becoming more and more important as more and more fltk based projects switch to VS120. Maybe should be added soon to the dev snapshots at least.
 
 
#7 hectorhon
06:38 Aug 13, 2014
Fix by greentea101 verified working (Visual Studio 2013, Windows 8.1.) (Thank you greentea101!)  
 
#8 roukaour
09:05 Aug 29, 2014
I have also been using greentea101's fix on the 1.3.x weekly snapshots, with both VS 2012 + Windows 7 and VS 2013 + Windows 8.1.  
 
#9 AlbrechtS
12:47 Sep 18, 2014
Raised priority to 3 (moderate). I would really like to have this in the next release (1.3.3).

To get this a little bit forward I uploaded border_padding.patch with the latest code changes found in this STR and Ian's suggestion to define the constant SM_CXPADDEDBORDER, if not defined.

Please review the patch, test, and confirm that this is what to do. It compiles w/o errors, but I didn't test anything.

Ian, you looked into this issue before, maybe you can tell if it's okay to commit (and if so, do it?).
 
 
#10 ianmacarthur
13:35 Oct 05, 2014
Albrecht,

I'm keen to get this one in for 1.3.3, and I *believe* the proposed fix is good.

However, I do not have access to a test environment I can really exercise this with (I have no Win7 or Win8 machine I can stick VS on at the moment.)
 
 
#11 AlbrechtS
16:15 Oct 05, 2014
I agree, and I have Windows 7 and 8 available, but I'm not keen on testing this (changing Windows setup, trying to get VS to do what is expected, and so on). That seems to be a lot of work, and I have many other things I need to do.

Maybe we can just commit the patch and test that it does no harm with a standard setup? I could do that...

And we have a few (4) independent confirmations that the patch works.
 
 
#12 ianmacarthur
14:10 Oct 07, 2014
For what it is worth, the patch has no discernible effect on my regular mingw builds on Win7, so is not causing any regression I can determine.

On that basis I think it is "harmless" and certainly reports suggest it has positive value for folks using recent MS compiler variants.
 
 
#13 AlbrechtS
05:01 Oct 08, 2014
Fixed in Subversion repository.

I can't find any visible regressions either. Committed patch with one small modification.

Thanks to greentea101 for the report and patch, and to others for testing and confirming the resolution.
 
     

Return to Bugs & Features ]

 
 

Comments are owned by the poster. All other content is copyright 1998-2024 by Bill Spitzak and others. This project is hosted by The FLTK Team. Please report site problems to 'erco@seriss.com'.