Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

build fail on Windows: missing ShellScalingAPI.h #9

Closed
Xeverous opened this issue Nov 17, 2019 · 16 comments
Closed

build fail on Windows: missing ShellScalingAPI.h #9

Xeverous opened this issue Nov 17, 2019 · 16 comments

Comments

@Xeverous
Copy link
Contributor

I'm trying to build elements on Windows using MCF GCC 9.1. I downloaded elements, infra and json as the documentation says but there is no information how to handle Cairo dependency other than Mac OS brew install.

CMake does not complain about anything, yet there are missing includes:

> make
Scanning dependencies of target json_test
[  1%] Building CXX object json/test/CMakeFiles/json_test.dir/main.cpp.obj
[  3%] Linking CXX executable json_test.exe
[  3%] Built target json_test
Scanning dependencies of target libelements
[  5%] Building CXX object lib/CMakeFiles/libelements.dir/src/element/button.cpp.obj
[  6%] Building CXX object lib/CMakeFiles/libelements.dir/src/element/composite.cpp.obj
[  8%] Building CXX object lib/CMakeFiles/libelements.dir/src/element/dial.cpp.obj
[ 10%] Building CXX object lib/CMakeFiles/libelements.dir/src/element/element.cpp.obj
[ 12%] Building CXX object lib/CMakeFiles/libelements.dir/src/element/floating.cpp.obj
[ 13%] Building CXX object lib/CMakeFiles/libelements.dir/src/element/flow.cpp.obj
[ 15%] Building CXX object lib/CMakeFiles/libelements.dir/src/element/gallery/button.cpp.obj
[ 17%] Building CXX object lib/CMakeFiles/libelements.dir/src/element/gallery/check_box.cpp.obj
[ 18%] Building CXX object lib/CMakeFiles/libelements.dir/src/element/gallery/icon_button.cpp.obj
[ 20%] Building CXX object lib/CMakeFiles/libelements.dir/src/element/gallery/menu.cpp.obj
[ 22%] Building CXX object lib/CMakeFiles/libelements.dir/src/element/image.cpp.obj
[ 24%] Building CXX object lib/CMakeFiles/libelements.dir/src/element/layer.cpp.obj
[ 25%] Building CXX object lib/CMakeFiles/libelements.dir/src/element/menu.cpp.obj
[ 27%] Building CXX object lib/CMakeFiles/libelements.dir/src/element/misc.cpp.obj
[ 29%] Building CXX object lib/CMakeFiles/libelements.dir/src/element/popup.cpp.obj
[ 31%] Building CXX object lib/CMakeFiles/libelements.dir/src/element/port.cpp.obj
[ 32%] Building CXX object lib/CMakeFiles/libelements.dir/src/element/proxy.cpp.obj
[ 34%] Building CXX object lib/CMakeFiles/libelements.dir/src/element/slider.cpp.obj
[ 36%] Building CXX object lib/CMakeFiles/libelements.dir/src/element/text.cpp.obj
[ 37%] Building CXX object lib/CMakeFiles/libelements.dir/src/element/tile.cpp.obj
[ 39%] Building CXX object lib/CMakeFiles/libelements.dir/src/support/canvas.cpp.obj
[ 41%] Building CXX object lib/CMakeFiles/libelements.dir/src/support/draw_utils.cpp.obj
[ 43%] Building CXX object lib/CMakeFiles/libelements.dir/src/support/glyphs.cpp.obj
[ 44%] Building CXX object lib/CMakeFiles/libelements.dir/src/support/pixmap.cpp.obj
[ 46%] Building CXX object lib/CMakeFiles/libelements.dir/src/support/rect.cpp.obj
[ 48%] Building CXX object lib/CMakeFiles/libelements.dir/src/support/resource_paths.cpp.obj
[ 50%] Building CXX object lib/CMakeFiles/libelements.dir/src/support/text_utils.cpp.obj
[ 51%] Building CXX object lib/CMakeFiles/libelements.dir/src/support/theme.cpp.obj
[ 53%] Building CXX object lib/CMakeFiles/libelements.dir/src/view.cpp.obj
[ 55%] Building CXX object lib/CMakeFiles/libelements.dir/host/windows/app.cpp.obj
[...]\elements\lib\host\windows\app.cpp:12:10: fatal error: ShellScalingAPI.h: No such file or directory
   12 | #include <ShellScalingAPI.h>
      |          ^~~~~~~~~~~~~~~~~~~
compilation terminated.

After brief research, I find out this file is not from Cairo but a part of UWP. Do I miss some Windows SDK?

@djowel
Copy link
Member

djowel commented Nov 17, 2019

After brief research, I find out this file is not from Cairo but a part of UWP. Do I miss some Windows SDK?

Seems like it. Which version of VS Studio? ShellScalingAPI might need a minimum, I am not sure yet.

@Xeverous
Copy link
Contributor Author

I'm using GCC with MCF thread model (https://gcc-mcf.lhmouse.com/)

@djowel
Copy link
Member

djowel commented Nov 17, 2019

Ah, OK, well I can't help you there. Sorry, I can only directly support standard Visual Studio installations. It would be cool if you can get it working though and contribute some how-to docs.

@Xeverous
Copy link
Contributor Author

That looks more like a GCC issue. Official GCC team does not care about Windows and people who release forks have a set of patches to make it build native Windows executables. I just need to figure out how to enable UWP in GCC if it is supported by any fork at all.

@djowel
Copy link
Member

djowel commented Nov 17, 2019

Good luck! I'd appreciate it a lot if you can contribute some how-to docs if you get it working. I'd love to use GCC and CLion (my preferred IDE) if it is doable.

@Xeverous
Copy link
Contributor Author

Ok, another question: Do you actually use any of WinRT or UWP stuff (they have very specific includes like <winrt/Windows.Foundation.Collections.h>)? The chances are Elements uses pure Win32 API and I'm just missing Windows SDK installation which brings the headers.

@Xeverous
Copy link
Contributor Author

After some investigation, the build succeeded, and I have some information for you:

  • GCC ports for Windows do not use original Windows headers, even the ones from SDKs. They ship with their own modified copies, at least due to language conformance and intrusive macro problems. GCC itself documents it does such thing even on some unix systems, as some can't even declare standard-compliant printf so the compiler ships with its own.
  • The only include that does not work with GCC is ShellScalingAPI.h. It is not WinRT or UWPstuff, so there is no trouble. It is simply not yet supported (at least in the 9.1 version I have).
  • ShellScalingAPI and it's functions (Elements uses GetDpiForWindow and SetProcessDpiAwareness) are a part of Windows High DPI technology. This requires Windows 8.1 or Windows Server 2012 R2. I would like my Elements-based applications to work on Windows 7.

There is some minor issue with CMake recipe unless the empty starter was really meant to be that empty:

[  3%] Built target json_test
[...]
[ 62%] Built target libelements
[ 63%] Linking CXX executable EmptyStarter.exe
g++.exe: fatal error: no input files
compilation terminated.

How I made it build:

  • I have commented High DPI API calls and replaced scale with 1.0 so basically build Elements with no DPI awarness support.
  • I also had to change the FOLDERID_AppDataProgramData to something else. My GCC installation supports most of these folder IDs but not this one. At last, one can just copy the GUID from documentation and use it directly instead of using any FOLDERID_ macros.

changes:

diff --git a/lib/host/windows/app.cpp b/lib/host/windows/app.cpp
index 3d26ab1..5846951 100644
--- a/lib/host/windows/app.cpp
+++ b/lib/host/windows/app.cpp
@@ -9,7 +9,7 @@
 #include <infra/assert.hpp>
 #include <boost/filesystem.hpp>
 #include <windows.h>
-#include <ShellScalingAPI.h>
+//#include <ShellScalingAPI.h>
 #include <shlobj.h>
 #include <cstring>
 
@@ -71,7 +71,7 @@ namespace cycfi { namespace elements
    {
       init_app init;
       _app_name = app_config.application_title;
-      SetProcessDpiAwareness(PROCESS_SYSTEM_DPI_AWARE);
+      //SetProcessDpiAwareness(PROCESS_SYSTEM_DPI_AWARE);
    }
 
    app::~app()
@@ -96,7 +96,7 @@ namespace cycfi { namespace elements
    fs::path app_data_path()
    {
       LPWSTR path = nullptr;
-      HRESULT hr = SHGetKnownFolderPath(FOLDERID_AppDataProgramData, KF_FLAG_CREATE, nullptr, &path);
+      HRESULT hr = SHGetKnownFolderPath(FOLDERID_ProgramData, KF_FLAG_CREATE, nullptr, &path);
       return fs::path{ path };
    }
 }}
diff --git a/lib/host/windows/base_view.cpp b/lib/host/windows/base_view.cpp
index 3298f0a..2c3b847 100644
--- a/lib/host/windows/base_view.cpp
+++ b/lib/host/windows/base_view.cpp
@@ -133,7 +133,7 @@ namespace cycfi { namespace elements
             cairo_surface_t* surface = cairo_win32_surface_create(info->offscreen_hdc);
             cairo_t* context = cairo_create(surface);
 
-            auto scale = GetDpiForWindow(hwnd) / 96.0;
+            auto scale = 1.0;
             cairo_scale(context, scale, scale);
 
             view->draw(context,
@@ -189,7 +189,7 @@ namespace cycfi { namespace elements
          float pos_x = GET_X_LPARAM(lparam);
          float pos_y = GET_Y_LPARAM(lparam);
 
-         auto scale = GetDpiForWindow(hwnd) / 96.0;
+         auto scale = 1.0;
          pos_x /= scale;
          pos_y /= scale;
 
@@ -311,7 +311,7 @@ namespace cycfi { namespace elements
          float pos_x = GET_X_LPARAM(lparam);
          float pos_y = GET_Y_LPARAM(lparam);
 
-         auto scale = GetDpiForWindow(hwnd) / 96.0;
+         auto scale = 1.0;
          pos_x /= scale;
          pos_y /= scale;
 
@@ -341,7 +341,7 @@ namespace cycfi { namespace elements
          pos.y = GET_Y_LPARAM(lparam);
          ScreenToClient(hwnd, &pos);
 
-         float scale = GetDpiForWindow(hwnd) / 96.0;
+         float scale = 1.0f;
          info->vptr->scroll(dir, { pos.x / scale, pos.y / scale });
       }
 
@@ -555,13 +555,13 @@ namespace cycfi { namespace elements
       POINT pos;
       GetCursorPos(&pos);
       ScreenToClient(_view, &pos);
-      float scale = GetDpiForWindow(_view) / 96.0;
+      float scale = 1.0f;
       return { float(pos.x) / scale, float(pos.y) / scale };
    }
 
    elements::extent base_view::size() const
    {
-      float scale = GetDpiForWindow(_view) / 96.0;
+      float scale = 1.0f;
       RECT r;
       GetWindowRect(_view, &r);
       return { float(r.right-r.left) / scale, float(r.bottom-r.top) / scale };
@@ -569,7 +569,7 @@ namespace cycfi { namespace elements
 
    void base_view::size(elements::extent p)
    {
-      auto scale = GetDpiForWindow(_view) / 96.0;
+      auto scale = 1.0f;
       auto parent = GetParent(_view);
       RECT bounds;
       GetClientRect(parent, &bounds);
@@ -590,7 +590,7 @@ namespace cycfi { namespace elements
 
    void base_view::refresh(rect area)
    {
-      auto scale = GetDpiForWindow(_view) / 96.0;
+      auto scale = 1.0;
       RECT r;
       r.left = area.left * scale;
       r.right = area.right * scale;
diff --git a/lib/host/windows/window.cpp b/lib/host/windows/window.cpp
index 1bfbb5b..f3903e1 100644
--- a/lib/host/windows/window.cpp
+++ b/lib/host/windows/window.cpp
@@ -100,7 +100,7 @@ namespace cycfi { namespace elements
 
       void constrain_size(HWND hwnd, RECT& r, view_limits limits)
       {
-         auto scale = GetDpiForWindow(hwnd) / 96.0;
+         auto scale = 1.0;
          auto extra = window_frame_size(hwnd);
          auto w = ((r.right - r.left) - extra.x) / scale;
          auto h = ((r.bottom - r.top) - extra.y) / scale;
@@ -161,7 +161,7 @@ namespace cycfi { namespace elements
       static init_window_class init;
 
       std::wstring wname = utf8_decode(name);
-      auto scale = GetDpiForSystem() / 96.0;
+      auto scale = 1.0;
 
       _window = CreateWindowW(
          L"ElementsWindow",
@@ -194,7 +194,7 @@ namespace cycfi { namespace elements
 
    point window::size() const
    {
-      auto scale = GetDpiForWindow(_window) / 96.0;
+      auto scale = 1.0;
       RECT frame;
       GetWindowRect(_window, &frame);
       return {
@@ -205,7 +205,7 @@ namespace cycfi { namespace elements
 
    void window::size(point const& p)
    {
-      auto scale = GetDpiForWindow(_window) / 96.0;
+      auto scale = 1.0;
       RECT frame;
       GetWindowRect(_window, &frame);
       frame.right = frame.left + (p.x * scale);
@@ -239,7 +239,7 @@ namespace cycfi { namespace elements
 
    point window::position() const
    {
-      auto scale = GetDpiForWindow(_window) / 96.0;
+      auto scale = 1.0;
       RECT frame;
       GetWindowRect(_window, &frame);
       return { float(frame.left / scale), float(frame.top / scale) };
@@ -247,7 +247,7 @@ namespace cycfi { namespace elements
 
    void window::position(point const& p)
    {
-      auto scale = GetDpiForWindow(_window) / 96.0;
+      auto scale = 1.0;
       RECT frame;
       GetWindowRect(_window, &frame);

What are your thoughts? It turns out it is possible to build Elements using pure GCC on Windows, just without DPI awarness support.

@djowel
Copy link
Member

djowel commented Nov 18, 2019

Very cool! That is good news. I have mixed thoughts about supporting Windows 7 though. I like Win7. But... The UI is designed with multi-scale resolution in mind and Win7 is at the end-of-life already (January 14, 2020). We can probably conditionally support it by detecting the OS (definitely not hard-coded to scale 1.0!), but there's where my mixed thoughts start --the UI behaviour won't be the same.

@Xeverous
Copy link
Contributor Author

I'm more concerned about compiler support than Windows 7 support.

  • Would you mind a CMake+ifdef switch to support GCC and Windows 7?
  • Why default scale of 1 is not ok? Or do you want to just avoid hardcoding (and eg let users set it)?
  • What about Linking CXX executable EmptyStarter.exe g++.exe: fatal error: no input files? Looks like the examples have missing source files in their CMake recipes.
  • I don't get this - why it has a different meaning on Windows?

# Cairo
if (WIN32)
set(CAIRO_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/external/cairo/include)
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
set(CAIRO_LIBRARIES ${CMAKE_CURRENT_SOURCE_DIR}/external/cairo/lib/x64/cairo.lib)
else()
set(CAIRO_LIBRARIES ${CMAKE_CURRENT_SOURCE_DIR}/external/cairo/lib/x86/cairo.lib)
endif()
else()
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
FIND_PACKAGE(CAIRO REQUIRED cairo)
endif()

@djowel
Copy link
Member

djowel commented Nov 18, 2019

  • Would you mind a CMake+ifdef switch to support GCC and Windows 7?

Perhaps, I'm not yet convinced that supporting Windows 7 is a good idea.

  • Why default scale of 1 is not ok? Or do you want to just avoid hardcoding (and eg let users set it)?

Scale should not be a compile time constant. It should be user configurable. But how do you do that as a special case for non-DPI aware OSes? That is the question.

  • What about Linking CXX executable EmptyStarter.exe g++.exe: fatal error: no input files? Looks like the examples have missing source files in their CMake recipes.
  • I don't get this - why it has a different meaning on Windows?

Sorry, I do not have answers to the last two questions.

@djowel
Copy link
Member

djowel commented Nov 18, 2019

I'm more concerned about compiler support than Windows 7 support.

What's wrong with MSVC compiler support? It is free and even the std lib became open source recently. Is it an ideological issue coming form the evil software vendor :-) ?

@Xeverous
Copy link
Contributor Author

Xeverous commented Nov 18, 2019

Is it an ideological issue coming form the evil software vendor :-) ?

Party yes, but also:

  • I have already all the GCC tooling set up. Don't want to spend time setting up another toolchain for all the dependencies.
  • I use some libraries which require bleeding-edge support (eg C++17 STL, Boost Spirit X3 and maybe soon Boost Outcome / deterministic exceptions or pattern maching (variant and optional really need better monad style programming support)). Spirit X3 (or some other bleeding-edge Boost libraries) don't even document MSVC compilation attempts.

Perhaps, I'm not yet convinced that supporting Windows 7 is a good idea.

I'm more concerned about GCC support. I might check if any new version has support for ShellScalingAPI.h (MinGW-w64 had a significant update recently) - if not, I have no idea how to deal with scale value. Perhaps it could be exposed as some library function (global state though...)?

@djowel
Copy link
Member

djowel commented Nov 19, 2019

I'm more concerned about GCC support. I might check if any new version has support for ShellScalingAPI.h (MinGW-w64 had a significant update recently) - if not, I have no idea how to deal with scale value. Perhaps it could be exposed as some library function (global state though...)?

That might work, at the very least. The thing is, with OSes that support HiDPI, each display can have its own scale value. If the scale is fixed to say 1.0, when you drag a window to a different display with higher resolution, say 4K, it will be very small with very tiny fonts, etc. But if you choose, say 2.0, then it will be very big on low-res displays. I guess there's nothing you can do with such OSes like Win7. And I don't even know if Win8 supports HiDPI. It's a "new" thing and Windows (and Linux!) is playing catch up.

I'm genuinely interested with gcc support. I actually started that path, but chose to move to VS as it is more straightforward. If you can get HiDPI support possible, that would be great!

@Xeverous
Copy link
Contributor Author

Ok, so I will wait few days with this issue (Latest MinGW-w64 release was 10.11., the distro I use builds every 2 weeks and the last one was 06.11 so I can expect next in 4-5 days) to see if ShellScalingAPI.h is supported. Otherwise, I can submit a PR which adds build option to disable HDPI or (if you don't want such thing) just build elements with previosuly mentioned patch.

Regarding Linking CXX executable EmptyStarter.exe g++.exe: fatal error: no input files? error, I have found out the source of the problem but this will be a separate issue.

@Xeverous
Copy link
Contributor Author

Update: I may submit multiple CMake-related issues which also block GCC build on Windows for me and general modern CMake problems/improvements.

You are a modern c++ programmer. You are smart. You know your way around and
you do not need any hand-holding :smiley: So here are the basic requirements

Since the library aims for modern C++, I would expect it also to support modern CMake (which had similar overhaul as old C++ vs modern C++), that is:

  • dedicated options for ifs in the build recipes
  • not toucing very fragile iternal variables (CMAKE_EXE_LINKER_FLAGS, CMAKE_CXX_FLAGS etc)
  • no compiler/toolchain assumptions

Some of these unnecessarily complicate the build or block GCC support on Windows and since the project already requires CMake 3.7.2 it is no problem to fix these.

Refer to https://cliutils.gitlab.io/modern-cmake/ and it's own linked posts.

@Xeverous
Copy link
Contributor Author

Posted CMake PR (#13), unfortunately this issue can not be fixed for MinGW (yet). ShellScalingapi.h is present, but is not actually complete (missing some functions). For

fs::path app_data_path()
{
LPWSTR path = nullptr;
HRESULT hr = SHGetKnownFolderPath(FOLDERID_AppDataProgramData, KF_FLAG_CREATE, nullptr, &path);
return fs::path{ path };
}

the FOLDERID_AppDataProgramData is quite new (Windows 10 update 1709 - see https://docs.microsoft.com/en-us/windows/win32/shell/knownfolderid) which isn't present in MinGW distros (yet). I'm not sure if this is the right folder, since the documentation states its for intrernal .NET application use only.

So as of now, I will just build the library with a patch that comments out scaling API, sets scale to 1.0 and uses FOLDERID_ProgramData instead untill MinGW gets an update to this Windows header.

The incompletness of the Windows scaling API is a compiler support problem, but I think the used Folder ID is wrong.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants