モノトーンの伝説日記

Apex Legends, Splatoon, Programming, and so on...

高解像度厨が Windows 8.1 のスケーリングについてのお話を書いてみる。(2)

 こんばんはー、モノトーンです。

 (ネタバレ含む余談)世界樹やってて、フレドリカちゃんと仲良くなりまして、彼女のことをリッキィって呼ぶようになりました。あー、リッキィかっこかわいいなぁ、っていう。あ、新世界樹やってない人わかりませんねww いや、かわゆすよ。あ、本題行きましょうか。

概要

  1. Windows 8.1 のスケーリングをいじってて発見したこと。
  2. [おまけ] スケーリングの関数群

1. Windows 8.1 のスケーリングをいじってて発見したこと。

 Windows 8.1 のスケーリング用に追加された API で Raw Dpi (デバイスの実際の dots per inch) が取れるようになりました。

STDAPI GetDpiForMonitor( _In_ HMONITOR hmonitor, _In_ MONITOR_DPI_TYPE dpiType, _Out_ UINT *dpiX, _Out_ UINT *dpiY );

 こちらの API です。これにモニターハンドル HMONITOR、MDT_RAW_DPI を指定し、DPI を取得すると、ほぼ物理デバイスに一致する DPI を取得することが可能です。ちなみに、MDT_EFFECTIVE_DPI を指定すると DWM (Desktop Window Manager) が使う DPI を取得することができます。

2. [おまけ] スケーリングの関数群

 スケーリングの関数について取り出してまとめておきます。XP 時代からの名残りの API もあるので、過去の OS に対応するならそちらの API も併用しないといけないかも。(このあたりは要検証)

/* sdkddkver.h ll. 38-40 */
#define _WIN32_WINNT_WIN7    0x0601
#define _WIN32_WINNT_WIN8    0x0602
#define _WIN32_WINNT_WINBLUE 0x0603

/* WinUser.h ll. 2355-2357 */
#if( WINVER >= 0x0601 )
#define WM_DPICHANGED 0x02E0
#endif

/* WinUser.h ll. 13709-13725 */
#if( _WIN32_WINNT >= 0x0600 )
#define USER_DEFAULT_SCREEN_DPI 96
WINUSERAPI BOOL WINAPI SetProcessDPIAware( VOID );
WINUSERAPI BOOL WINAPI IsProcessDPIAware( VOID );
#endif

/* WinUser.h ll. 8961-8976 */
#if( WINVER >= 0x0600 )
WINUSERAPI BOOL WINAPI LogicalToPhysicalPoint( _In_ HWND hWnd, _Inout_ LPPOINT lpPoint );
WINUSERAPI BOOL WINAPI PhysicalToLogicalPoint( _In_ HWND hWnd, _Inout_ LPPOINT lpPoint );
#endif

/* WinUser.h ll. 8978-8993 */
#if( WINVER >= 0x0603 )
WINUSERAPI BOOL WINAPI LogicalToPhysicalPointForPerMonitorDPI( _In_opt_ HWND hWnd, _Inout_ LPPOINT lpPoint );
WINUSERAPI BOOL WINAPI PhysicalToLogicalPointForPerMonitorDPI( _In_opt_ HWND hWnd, _Inout_ LPPOINT lpPoint );
#endif

/* ShellScalingApi.h ll. 50-90 */
#if (NTDDI_VERSION >= NTDDI_BLUE)
STDAPI GetScaleFactorForMonitor(_In_ HMONITOR hMon, _Out_ DEVICE_SCALE_FACTOR *pScale);
STDAPI RegisterScaleChangeEvent(_In_ HANDLE hEvent, _Out_ DWORD_PTR *pdwCookie);
STDAPI UnregisterScaleChangeEvent(_In_ DWORD_PTR dwCookie);

#ifndef DPI_ENUMS_DECLARED
typedef enum PROCESS_DPI_AWARENESS
{ PROCESS_DPI_UNAWARE = 0, PROCESS_SYSTEM_DPI_AWARE = 1, PROCESS_PER_MONITOR_DPI_AWARE = 2 } PROCESS_DPI_AWARENESS;
typedef enum MONITOR_DPI_TYPE
{ MDT_EFFECTIVE_DPI = 0, MDT_ANGULAR_DPI = 1, MDT_RAW_DPI = 2, MDT_DEFAULT = MDT_EFFECTIVE_DPI } MONITOR_DPI_TYPE;
#define DPI_ENUMS_DECLARED
#endif

STDAPI SetProcessDpiAwareness( _In_ PROCESS_DPI_AWARENESS value );
STDAPI GetProcessDpiAwareness( _In_opt_ HANDLE hprocess, _Out_ PROCESS_DPI_AWARENESS *value );
STDAPI GetDpiForMonitor( _In_ HMONITOR hmonitor, _In_ MONITOR_DPI_TYPE dpiType, _Out_ UINT *dpiX, _Out_ UINT *dpiY );
#endif

 Windows 8.1 で新しく追加された API は「ShellScalingApi.h」に存在するので注意。これ、探すのに WinUser.h になかったので、「あれ? まだ使えないの?」って思ってしまったw

総括

 実際、この API 呼び出すのは簡単なんですよね。アプリ側のレンダリング問題のほうがやばい。今、High Dpi 向けの native アプリケーションのクラス整備している段階なんですけど、やっぱりクラス整備するのは難しいですね。Dpi 絡みもうまく解決できるような設計にしないといけないので。まあがんばります。

 以上。

こちらの記事もご覧ください