From 61c7415071479c20297612b0598253a186b453ae Mon Sep 17 00:00:00 2001 From: Alfred Reynolds Date: Wed, 29 Jul 2015 17:18:56 -0700 Subject: [PATCH] Add SDL_GetDisplayDPI routine and implement for Windows. --- include/SDL_video.h | 12 ++++ src/dynapi/SDL_dynapi_overrides.h | 1 + src/dynapi/SDL_dynapi_procs.h | 1 + src/video/SDL_sysvideo.h | 7 ++ src/video/SDL_video.c | 29 ++++++++ src/video/windows/SDL_windowsmodes.c | 100 +++++++++++++++++++++++++-- src/video/windows/SDL_windowsmodes.h | 4 ++ src/video/windows/SDL_windowsvideo.c | 9 +++ src/video/windows/SDL_windowsvideo.h | 17 +++++ 9 files changed, 173 insertions(+), 7 deletions(-) diff --git a/include/SDL_video.h b/include/SDL_video.h index a2abfb83c..f466165b4 100644 --- a/include/SDL_video.h +++ b/include/SDL_video.h @@ -296,6 +296,18 @@ extern DECLSPEC const char * SDLCALL SDL_GetDisplayName(int displayIndex); */ extern DECLSPEC int SDLCALL SDL_GetDisplayBounds(int displayIndex, SDL_Rect * rect); +/** + * \brief Get the dots/pixels-per-inch for a display + * + * \note Diagonal, horizontal and vertical DPI can all be optionally + * returned if the parameter is non-NULL. + * + * \return 0 on success, or -1 if no DPI information is available or the index is out of range. + * + * \sa SDL_GetNumVideoDisplays() + */ +extern DECLSPEC int SDLCALL SDL_GetDisplayDPI(int displayIndex, float * ddpi, float * hdpi, float * vdpi); + /** * \brief Returns the number of available display modes. * diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h index 083638c66..3ae3ed89e 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/src/dynapi/SDL_dynapi_overrides.h @@ -505,6 +505,7 @@ #define SDL_GetNumVideoDisplays SDL_GetNumVideoDisplays_REAL #define SDL_GetDisplayName SDL_GetDisplayName_REAL #define SDL_GetDisplayBounds SDL_GetDisplayBounds_REAL +#define SDL_GetDisplayDPI SDL_GetDisplayDPI_REAL #define SDL_GetNumDisplayModes SDL_GetNumDisplayModes_REAL #define SDL_GetDisplayMode SDL_GetDisplayMode_REAL #define SDL_GetDesktopDisplayMode SDL_GetDesktopDisplayMode_REAL diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index a5045509c..6b9e83929 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -534,6 +534,7 @@ SDL_DYNAPI_PROC(const char*,SDL_GetCurrentVideoDriver,(void),(),return) SDL_DYNAPI_PROC(int,SDL_GetNumVideoDisplays,(void),(),return) SDL_DYNAPI_PROC(const char*,SDL_GetDisplayName,(int a),(a),return) SDL_DYNAPI_PROC(int,SDL_GetDisplayBounds,(int a, SDL_Rect *b),(a,b),return) +SDL_DYNAPI_PROC(int,SDL_GetDisplayDPI,(int a, float *b, float *c, float *d),(a,b,c,d),return) SDL_DYNAPI_PROC(int,SDL_GetNumDisplayModes,(int a),(a),return) SDL_DYNAPI_PROC(int,SDL_GetDisplayMode,(int a, int b, SDL_DisplayMode *c),(a,b,c),return) SDL_DYNAPI_PROC(int,SDL_GetDesktopDisplayMode,(int a, SDL_DisplayMode *b),(a,b),return) diff --git a/src/video/SDL_sysvideo.h b/src/video/SDL_sysvideo.h index b86019f8b..5e3e1526d 100644 --- a/src/video/SDL_sysvideo.h +++ b/src/video/SDL_sysvideo.h @@ -170,6 +170,11 @@ struct SDL_VideoDevice */ int (*GetDisplayBounds) (_THIS, SDL_VideoDisplay * display, SDL_Rect * rect); + /* + * Get the dots/pixels-per-inch of a display + */ + int (*GetDisplayDPI) (_THIS, SDL_VideoDisplay * display, float * ddpi, float * hdpi, float * vdpi); + /* * Get a list of the available display modes for a display. */ @@ -423,6 +428,8 @@ extern SDL_Window * SDL_GetFocusWindow(void); extern SDL_bool SDL_ShouldAllowTopmost(void); +extern float SDL_ComputeDiagonalDPI(int hpix, int vpix, float hinches, float vinches); + #endif /* _SDL_sysvideo_h */ /* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c index f464f07ad..944c07d3d 100644 --- a/src/video/SDL_video.c +++ b/src/video/SDL_video.c @@ -687,6 +687,24 @@ SDL_GetDisplayBounds(int displayIndex, SDL_Rect * rect) return 0; } +int +SDL_GetDisplayDPI(int displayIndex, float * ddpi, float * hdpi, float * vdpi) +{ + SDL_VideoDisplay *display; + + CHECK_DISPLAY_INDEX(displayIndex, -1); + + display = &_this->displays[displayIndex]; + + if (_this->GetDisplayDPI) { + if (_this->GetDisplayDPI(_this, display, ddpi, hdpi, vdpi) == 0) { + return 0; + } + } + + return -1; +} + SDL_bool SDL_AddDisplayMode(SDL_VideoDisplay * display, const SDL_DisplayMode * mode) { @@ -3538,4 +3556,15 @@ SDL_SetWindowHitTest(SDL_Window * window, SDL_HitTest callback, void *userdata) return 0; } +float SDL_ComputeDiagonalDPI(int hpix, int vpix, float hinches, float vinches) +{ + float den2 = hinches * hinches + vinches * vinches; + if ( den2 <= 0.0f ) { + return 0.0f; + } + + return (float)(SDL_sqrt((double)hpix * (double)hpix + (double)vpix * (double)vpix) / + SDL_sqrt((double)den2)); +} + /* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/video/windows/SDL_windowsmodes.c b/src/video/windows/SDL_windowsmodes.c index 7ddf18b78..9f1f3d18c 100644 --- a/src/video/windows/SDL_windowsmodes.c +++ b/src/video/windows/SDL_windowsmodes.c @@ -29,9 +29,50 @@ #define CDS_FULLSCREEN 0 #endif -static SDL_bool -WIN_GetDisplayMode(LPCTSTR deviceName, DWORD index, SDL_DisplayMode * mode) +typedef struct _WIN_GetMonitorDPIData { + SDL_VideoData *vid_data; + SDL_DisplayMode *mode; + SDL_DisplayModeData *mode_data; +} WIN_GetMonitorDPIData; + +static BOOL CALLBACK +WIN_GetMonitorDPI(HMONITOR hMonitor, + HDC hdcMonitor, + LPRECT lprcMonitor, + LPARAM dwData) { + WIN_GetMonitorDPIData *data = (WIN_GetMonitorDPIData*) dwData; + UINT hdpi, vdpi; + + if (data->vid_data->GetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &hdpi, &vdpi) == S_OK && + hdpi > 0 && + vdpi > 0) { + float hsize, vsize; + + data->mode_data->HorzDPI = (float)hdpi; + data->mode_data->VertDPI = (float)vdpi; + + // Figure out the monitor size and compute the diagonal DPI. + hsize = data->mode->w / data->mode_data->HorzDPI; + vsize = data->mode->h / data->mode_data->VertDPI; + + data->mode_data->DiagDPI = SDL_ComputeDiagonalDPI( data->mode->w, + data->mode->h, + hsize, + vsize ); + + // We can only handle one DPI per display mode so end the enumeration. + return FALSE; + } + + // We didn't get DPI information so keep going. + return TRUE; +} + +static SDL_bool +WIN_GetDisplayMode(_THIS, LPCTSTR deviceName, DWORD index, SDL_DisplayMode * mode) +{ + SDL_VideoData *vid_data = (SDL_VideoData *) _this->driverdata; SDL_DisplayModeData *data; DEVMODE devmode; HDC hdc; @@ -52,6 +93,9 @@ WIN_GetDisplayMode(LPCTSTR deviceName, DWORD index, SDL_DisplayMode * mode) DM_DISPLAYFLAGS); data->ScaleX = 1.0f; data->ScaleY = 1.0f; + data->DiagDPI = 0.0f; + data->HorzDPI = 0.0f; + data->VertDPI = 0.0f; /* Fill in the mode information */ mode->format = SDL_PIXELFORMAT_UNKNOWN; @@ -73,6 +117,30 @@ WIN_GetDisplayMode(LPCTSTR deviceName, DWORD index, SDL_DisplayMode * mode) mode->w = logical_width; mode->h = logical_height; + // WIN_GetMonitorDPI needs mode->w and mode->h + // so only call after those are set. + if (vid_data->GetDpiForMonitor) { + WIN_GetMonitorDPIData dpi_data; + + dpi_data.vid_data = vid_data; + dpi_data.mode = mode; + dpi_data.mode_data = data; + EnumDisplayMonitors(hdc, NULL, WIN_GetMonitorDPI, (LPARAM)&dpi_data); + } else { + // We don't have the Windows 8.1 routine so just + // get system DPI. + data->HorzDPI = (float)GetDeviceCaps( hdc, LOGPIXELSX ); + data->VertDPI = (float)GetDeviceCaps( hdc, LOGPIXELSY ); + if (data->HorzDPI == data->VertDPI) { + data->DiagDPI = data->HorzDPI; + } else { + data->DiagDPI = SDL_ComputeDiagonalDPI( mode->w, + mode->h, + (float)GetDeviceCaps( hdc, HORZSIZE ) / 25.4f, + (float)GetDeviceCaps( hdc, VERTSIZE ) / 25.4f ); + } + } + SDL_zero(bmi_data); bmi = (LPBITMAPINFO) bmi_data; bmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); @@ -131,7 +199,7 @@ WIN_GetDisplayMode(LPCTSTR deviceName, DWORD index, SDL_DisplayMode * mode) } static SDL_bool -WIN_AddDisplay(LPTSTR DeviceName) +WIN_AddDisplay(_THIS, LPTSTR DeviceName) { SDL_VideoDisplay display; SDL_DisplayData *displaydata; @@ -141,7 +209,7 @@ WIN_AddDisplay(LPTSTR DeviceName) #ifdef DEBUG_MODES printf("Display: %s\n", WIN_StringToUTF8(DeviceName)); #endif - if (!WIN_GetDisplayMode(DeviceName, ENUM_CURRENT_SETTINGS, &mode)) { + if (!WIN_GetDisplayMode(_this, DeviceName, ENUM_CURRENT_SETTINGS, &mode)) { return SDL_FALSE; } @@ -215,10 +283,10 @@ WIN_InitModes(_THIS) continue; } } - count += WIN_AddDisplay(device.DeviceName); + count += WIN_AddDisplay(_this, device.DeviceName); } if (count == 0) { - WIN_AddDisplay(DeviceName); + WIN_AddDisplay(_this, DeviceName); } } } @@ -241,6 +309,24 @@ WIN_GetDisplayBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect) return 0; } +int +WIN_GetDisplayDPI(_THIS, SDL_VideoDisplay * display, float * ddpi, float * hdpi, float * vdpi) +{ + SDL_DisplayModeData *data = (SDL_DisplayModeData *) display->current_mode.driverdata; + + if (ddpi) { + *ddpi = data->DiagDPI; + } + if (hdpi) { + *hdpi = data->HorzDPI; + } + if (vdpi) { + *vdpi = data->VertDPI; + } + + return data->DiagDPI != 0.0f ? 0 : -1; +} + void WIN_GetDisplayModes(_THIS, SDL_VideoDisplay * display) { @@ -249,7 +335,7 @@ WIN_GetDisplayModes(_THIS, SDL_VideoDisplay * display) SDL_DisplayMode mode; for (i = 0;; ++i) { - if (!WIN_GetDisplayMode(data->DeviceName, i, &mode)) { + if (!WIN_GetDisplayMode(_this, data->DeviceName, i, &mode)) { break; } if (SDL_ISPIXELFORMAT_INDEXED(mode.format)) { diff --git a/src/video/windows/SDL_windowsmodes.h b/src/video/windows/SDL_windowsmodes.h index 2b4e532f0..ba0964a3b 100644 --- a/src/video/windows/SDL_windowsmodes.h +++ b/src/video/windows/SDL_windowsmodes.h @@ -33,10 +33,14 @@ typedef struct DEVMODE DeviceMode; float ScaleX; float ScaleY; + float DiagDPI; + float HorzDPI; + float VertDPI; } SDL_DisplayModeData; extern int WIN_InitModes(_THIS); extern int WIN_GetDisplayBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect); +extern int WIN_GetDisplayDPI(_THIS, SDL_VideoDisplay * display, float * ddpi, float * hdpi, float * vdpi); extern void WIN_GetDisplayModes(_THIS, SDL_VideoDisplay * display); extern int WIN_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode); extern void WIN_QuitModes(_THIS); diff --git a/src/video/windows/SDL_windowsvideo.c b/src/video/windows/SDL_windowsvideo.c index 839a8af73..86c3437c4 100644 --- a/src/video/windows/SDL_windowsvideo.c +++ b/src/video/windows/SDL_windowsvideo.c @@ -78,6 +78,9 @@ WIN_DeleteDevice(SDL_VideoDevice * device) if (data->userDLL) { SDL_UnloadObject(data->userDLL); } + if (data->shcoreDLL) { + SDL_UnloadObject(data->shcoreDLL); + } SDL_free(device->driverdata); SDL_free(device); @@ -112,10 +115,16 @@ WIN_CreateDevice(int devindex) data->RegisterTouchWindow = (BOOL (WINAPI *)(HWND, ULONG)) SDL_LoadFunction(data->userDLL, "RegisterTouchWindow"); } + data->shcoreDLL = SDL_LoadObject("SHCORE.DLL"); + if (data->shcoreDLL) { + data->GetDpiForMonitor = (HRESULT (WINAPI *)(HMONITOR, MONITOR_DPI_TYPE, UINT *, UINT *)) SDL_LoadFunction(data->shcoreDLL, "GetDpiForMonitor"); + } + /* Set the function pointers */ device->VideoInit = WIN_VideoInit; device->VideoQuit = WIN_VideoQuit; device->GetDisplayBounds = WIN_GetDisplayBounds; + device->GetDisplayDPI = WIN_GetDisplayDPI; device->GetDisplayModes = WIN_GetDisplayModes; device->SetDisplayMode = WIN_SetDisplayMode; device->PumpEvents = WIN_PumpEvents; diff --git a/src/video/windows/SDL_windowsvideo.h b/src/video/windows/SDL_windowsvideo.h index e8ba37e06..85327a7ab 100644 --- a/src/video/windows/SDL_windowsvideo.h +++ b/src/video/windows/SDL_windowsvideo.h @@ -76,6 +76,17 @@ typedef struct _TOUCHINPUT { #endif /* WINVER < 0x0601 */ +#if WINVER < 0x0603 + +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; + +#endif /* WINVER < 0x0603 */ + typedef BOOL (*PFNSHFullScreen)(HWND, DWORD); typedef void (*PFCoordTransform)(SDL_Window*, POINT*); @@ -124,6 +135,12 @@ typedef struct SDL_VideoData BOOL (WINAPI *GetTouchInputInfo)( HTOUCHINPUT, UINT, PTOUCHINPUT, int ); BOOL (WINAPI *RegisterTouchWindow)( HWND, ULONG ); + void* shcoreDLL; + HRESULT (WINAPI *GetDpiForMonitor)( HMONITOR hmonitor, + MONITOR_DPI_TYPE dpiType, + UINT *dpiX, + UINT *dpiY ); + SDL_bool ime_com_initialized; struct ITfThreadMgr *ime_threadmgr; SDL_bool ime_initialized;