Skip to content
Permalink
Browse files

[[ HiDPI ]] Implement refactored HiDPI support on windows

[[ HiDPI ]] Update MCScreenDC::platform_getdisplays to include display pixel scale
[[ HiDPI ]] Refactor display info updating on desktop change
[[ HiDPI ]] Remove extraneous "}" from libgraphics utils.cpp
  • Loading branch information...
livecodeian committed Jan 28, 2014
1 parent 1373bfd commit 27f65afb96de93873a435525fb684ff704abc575
@@ -241,27 +241,14 @@ static pascal OSStatus WinEvtHndlr(EventHandlerCallRef ehcf, EventRef event, voi
{
if (sptr == MCdispatcher -> gethome())
{
const MCDisplay *t_monitors = NULL;
MCDisplay *t_old_monitors = NULL;

uint4 t_monitor_count, t_old_monitor_count;
// IM-2014-01-28: [[ HiDPI ]] Use updatedisplayinfo() method to update & compare display details
bool t_changed;
t_old_monitor_count = ((MCScreenDC *)MCscreen) -> getdisplays(t_monitors, false);
/* UNCHECKED */ MCMemoryAllocateCopy(t_monitors, sizeof(MCDisplay) * t_old_monitor_count, t_old_monitors);
if (t_old_monitors != NULL)
{
// IM-2014-01-24: [[ HiDPI ]] Use refactored method to update display info
MCscreen->cleardisplayinfocache();
t_monitor_count = ((MCScreenDC *)MCscreen) -> getdisplays(t_monitors, false);
t_changed = t_monitor_count != t_old_monitor_count || memcmp(t_old_monitors, t_monitors, sizeof(MCDisplay) * t_monitor_count) != 0;
delete t_old_monitors;
}
else
t_changed = true;
t_changed = false;

MCscreen->updatedisplayinfo(t_changed);

if (t_changed)
{
MCscreen -> delaymessage(MCdefaultstackptr -> getcurcard(), MCM_desktop_changed);
}
}
}
else if (GetEventKind(event) == kEventWindowCollapsed && sptr != NULL)
@@ -383,6 +383,24 @@ uint4 MCUIDC::getdisplays(const MCDisplay *&r_displays, bool p_effective)
return s_display_count;
}

void MCUIDC::updatedisplayinfo(bool &r_changed)
{
MCDisplay *t_displays;
t_displays = nil;

uint32_t t_display_count;
t_display_count = 0;

/* UNCHECKED */ platform_getdisplays(s_display_info_effective, t_displays, t_display_count);

r_changed = t_display_count != s_display_count ||
(MCMemoryCompare(t_displays, s_displays, sizeof(MCDisplay) * s_display_count) != 0);

MCMemoryDeleteArray(s_displays);
s_displays = t_displays;
s_display_count = t_display_count;
}

void MCUIDC::cleardisplayinfocache(void)
{
MCMemoryDeleteArray(s_displays);
@@ -318,6 +318,9 @@ class MCUIDC

uint4 getdisplays(MCDisplay const *& p_displays, bool effective);

// IM-2014-01-28: [[ HiDPI ]] Update the currently held display info, returning whether or not an changes have occured
void updatedisplayinfo(bool &r_changed);

// IM-2014-01-24: [[ HiDPI ]] Clear the currently held display information. Should be called
// when the display info needs to be refreshed, for example when a screen is (dis)connected
// or screen resolution settings are changed.
@@ -41,6 +41,19 @@ along with LiveCode. If not see <http://www.gnu.org/licenses/>. */
#include "graphicscontext.h"
#include "graphics_util.h"

////////////////////////////////////////////////////////////////////////////////

static inline POINT MCPointToWin32POINT(const MCPoint &p_point)
{
POINT t_point;
t_point.x = p_point.x;
t_point.y = p_point.y;

return t_point;
}

////////////////////////////////////////////////////////////////////////////////

MCScreenDC::MCScreenDC()
{
f_src_dc = NULL;
@@ -241,11 +254,16 @@ void MCScreenDC::listprinters(MCExecPoint& ep)

///////////////////////////////////////////////////////////////////////////////

MCStack *MCScreenDC::device_getstackatpoint(int32_t x, int32_t y)
MCStack *MCScreenDC::platform_getstackatpoint(int32_t x, int32_t y)
{
MCPoint t_loc;
t_loc = MCPointMake(x, y);

// IM-2014-01-28: [[ HiDPI ]] Convert logical to screen coordinates
t_loc = logicaltoscreenpoint(t_loc);

POINT t_location;
t_location . x = x;
t_location . y = y;
t_location = MCPointToWin32POINT(t_loc);

HWND t_window;
t_window = WindowFromPoint(t_location);
@@ -262,32 +280,202 @@ MCStack *MCScreenDC::device_getstackatpoint(int32_t x, int32_t y)

///////////////////////////////////////////////////////////////////////////////

bool MCWin32GetScreenDPI(uint32_t &r_dpi)
// IM-2014-01-28: [[ HiDPI ]] Weak-linked IsProcessDPIAware function
typedef BOOL (WINAPI *IsProcessDPIAwarePtr)(VOID);
bool MCWin32IsProcessDPIAware(bool &r_aware)
{
static IsProcessDPIAwarePtr s_IsProcessDPIAware = NULL;
static bool s_init = true;

if (s_init)
{
s_IsProcessDPIAware = (IsProcessDPIAwarePtr)GetProcAddress(GetModuleHandleA("user32.dll"), "IsProcessDPIAware");
s_init = false;
}

if (s_IsProcessDPIAware == nil)
return false;

r_aware = s_IsProcessDPIAware();

return true;
}

//////////

typedef enum __MCW32ProcessDPIAwareness
{
kMCW32ProcessDPIUnaware,
kMCW32ProcessSystemDPIAware,
kMCW32ProcessPerMonitorDPIAware,
} MCW32ProcessDPIAwareness;

// IM-2014-01-28: [[ HiDPI ]] Weak-linked GetProcessDPIAwareness function
typedef HRESULT (WINAPI *GetProcessDPIAwarenessPTR)(HANDLE hprocess, MCW32ProcessDPIAwareness *value);
bool MCWin32GetProcessDPIAwareness(MCW32ProcessDPIAwareness &r_awareness)
{
static GetProcessDPIAwarenessPTR s_GetProcessDPIAwareness = NULL;
static bool s_init = true;

if (s_init)
{
s_GetProcessDPIAwareness = (GetProcessDPIAwarenessPTR)GetProcAddress(GetModuleHandleA("shcore.dll"), "GetProcessDPIAwareness");
s_init = false;
}

if (s_GetProcessDPIAwareness == nil)
return false;

HRESULT t_result;
t_result = s_GetProcessDPIAwareness(NULL, &r_awareness);

return t_result == S_OK;
}

//////////

typedef enum __MCW32MonitorDPIType
{
kMCW32MDTEffectiveDPI,
kMCW32MDTAngularDPI,
kMCW32MDTRawDPI,
kMCW32MDTDefault = kMCW32MDTEffectiveDPI,
} MCW32MonitorDPIType;

// IM-2014-01-28: [[ HiDPI ]] Weak-linked GetDPIForMonitor function
typedef HRESULT (WINAPI *GetDPIForMonitorPTR)(HMONITOR hmonitor, MCW32MonitorDPIType dpiType, UINT *dpiX, UINT *dpiY);
bool MCWin32GetDPIForMonitor(HMONITOR p_monitor, uint32_t &r_xdpi, uint32_t &r_ydpi)
{
static GetDPIForMonitorPTR s_GetDPIForMonitor = NULL;
static bool s_init = true;

if (s_init)
{
s_GetDPIForMonitor = (GetDPIForMonitorPTR)GetProcAddress(GetModuleHandleA("shcore.dll"), "GetDPIForMonitor");
s_init = false;
}

if (s_GetDPIForMonitor == nil)
return false;

HRESULT t_result;
UINT t_x, t_y;
t_result = s_GetDPIForMonitor(p_monitor, kMCW32MDTEffectiveDPI, &t_x, &t_y);

if (t_result != S_OK)
return false;

r_xdpi = t_x;
r_ydpi = t_y;

return true;
}

////////////////////////////////////////////////////////////////////////////////

// IM-2014-01-28: [[ HiDPI ]] Return the x & y dpi of the main screen
bool MCWin32GetScreenDPI(uint32_t &r_xdpi, uint32_t &r_ydpi)
{
HDC t_dc;
t_dc = GetDC(NULL);

if (t_dc == NULL)
return false;

r_dpi = GetDeviceCaps(t_dc, LOGPIXELSX);
r_xdpi = GetDeviceCaps(t_dc, LOGPIXELSX);
r_ydpi = GetDeviceCaps(t_dc, LOGPIXELSY);

ReleaseDC(NULL, t_dc);

return true;
}

///////////////////////////////////////////////////////////////////////////////

#define NORMAL_DENSITY (96.0)

// IM-2013-12-11: [[ HiDPI ]] Calculate system scale based on normal dpi of 96
MCGFloat MCResGetSystemScale(void)
// IM-2014-01-28: [[ HiDPI ]] Return the DPI scale factor of the main screen.
// This gives us the pixelscale for system-DPI-aware applications.
MCGFloat MCWin32GetLogicalToScreenScale(void)
{
// TODO - determine the correct value on Win8.1 - this may depend on the display in question

uint32_t t_x, t_y;
/* UNCHECKED */ MCWin32GetScreenDPI(t_x, t_y);

return (MCGFloat) MCMax(t_x, t_y) / NORMAL_DENSITY;
}

////////////////////////////////////////////////////////////////////////////////

// IM-2014-01-28: [[ HiDPI ]] Return the DPI scale factor of the given monitor.
// For system-DPI-aware applications this will be the global system DPI value.
// For Per-Monitor-DPI-aware applications, this will be the effective DPI scale of the given monitor
bool MCWin32GetMonitorPixelScale(HMONITOR p_monitor, MCGFloat &r_pixel_scale)
{
uint32_t t_xdpi, t_ydpi;

// try to get per-monitor DPI setting
if (!MCWin32GetDPIForMonitor(p_monitor, t_xdpi, t_ydpi) &&
// fallback to the global system DPI setting
!MCWin32GetScreenDPI(t_xdpi, t_ydpi))
return false;

r_pixel_scale = (MCGFloat)MCMax(t_xdpi, t_ydpi) / NORMAL_DENSITY;

return true;
}

///////////////////////////////////////////////////////////////////////////////

// IM-2014-01-27: [[ HiDPI ]] Return whether the application is DPI-aware
bool MCResPlatformSupportsPixelScaling(void)
{
bool t_aware;
if (MCWin32IsProcessDPIAware(t_aware))
return t_aware;

return false;
}

// IM-2014-01-27: [[ HiDPI ]] On Windows, DPI-awareness can only be set at app startup or in the app manifest
bool MCResPlatformCanChangePixelScaling(void)
{
return false;
}

// IM-2014-01-27: [[ HiDPI ]] update system settings after change to pixel scaling
void MCResPlatformSetUsePixelScaling(bool p_use_scaling)
{
// No-op as this cannot be modified at runtime on Windows
}

///////////////////////////////////////////////////////////////////////////////

extern MCGFloat MCWin32GetLogicalToScreenScale(void);

MCPoint MCScreenDC::logicaltoscreenpoint(const MCPoint &p_point)
{
uint32_t t_dpi;
if (!MCWin32GetScreenDPI(t_dpi))
// Default to 1.0 scale on error
return 1.0;
MCGFloat t_scale;
t_scale = MCWin32GetLogicalToScreenScale();
return MCPointTransform(p_point, MCGAffineTransformMakeScale(t_scale, t_scale));
}

return (MCGFloat)t_dpi / NORMAL_DENSITY;
MCPoint MCScreenDC::screentologicalpoint(const MCPoint &p_point)
{
MCGFloat t_scale;
t_scale = 1 / MCWin32GetLogicalToScreenScale();
return MCPointTransform(p_point, MCGAffineTransformMakeScale(t_scale, t_scale));
}

MCRectangle MCScreenDC::logicaltoscreenrect(const MCRectangle &p_rect)
{
return MCRectangleGetScaledInterior(p_rect, MCWin32GetLogicalToScreenScale());
}

MCRectangle MCScreenDC::screentologicalrect(const MCRectangle &p_rect)
{
return MCRectangleGetScaledBounds(p_rect, 1 / MCWin32GetLogicalToScreenScale());
}

///////////////////////////////////////////////////////////////////////////////
@@ -174,9 +174,6 @@ class MCScreenDC : public MCUIDC
HMENU f_icon_menu;
#endif

static MCDisplay *s_monitor_displays;
static uint4 s_monitor_count;

int4 system_codepage;
int4 input_codepage;
HKL input_default_keyboard;
@@ -206,15 +203,22 @@ class MCScreenDC : public MCUIDC
virtual uint2 getvclass();
virtual uint2 getdepth();

virtual uint16_t device_getwidth(void);
virtual uint16_t device_getheight(void);
virtual bool device_getdisplays(bool p_effective, MCDisplay *&r_displays, uint32_t &r_count);
virtual bool device_getwindowgeometry(Window w, MCRectangle &drect);
virtual void device_boundrect(MCRectangle &rect, Boolean title, Window_mode m);
virtual void device_querymouse(int16_t &r_x, int16_t &r_y);
virtual void device_setmouse(int16_t p_x, int16_t p_y);
virtual MCStack *device_getstackatpoint(int32_t x, int32_t y);

// IM-2014-01-28: [[ HiDPI ]] Update device_* methods to platform-specific logical coord based methods
virtual uint16_t platform_getwidth(void);
virtual uint16_t platform_getheight(void);
virtual bool platform_getdisplays(bool p_effective, MCDisplay *&r_displays, uint32_t &r_count);
virtual bool platform_getwindowgeometry(Window w, MCRectangle &drect);
virtual void platform_boundrect(MCRectangle &rect, Boolean title, Window_mode m);
virtual void platform_querymouse(int16_t &r_x, int16_t &r_y);
virtual void platform_setmouse(int16_t p_x, int16_t p_y);
virtual MCStack *platform_getstackatpoint(int32_t x, int32_t y);

// IM-2014-01-28: [[ HiDPI ]] Convenience methods to convert logical to screen coords and back
MCPoint logicaltoscreenpoint(const MCPoint &p_point);
MCPoint screentologicalpoint(const MCPoint &p_point);
MCRectangle logicaltoscreenrect(const MCRectangle &p_rect);
MCRectangle screentologicalrect(const MCRectangle &p_rect);

virtual void openwindow(Window w, Boolean override);
virtual void closewindow(Window window);
virtual void destroywindow(Window &window);

0 comments on commit 27f65af

Please sign in to comment.
You can’t perform that action at this time.