X Tutup
// Copyright (c) 2014 CEF Python, see the Authors file. // All rights reserved. Licensed under BSD 3-clause license. // Project website: https://github.com/cztomczak/cefpython // Windows only #pragma comment(lib, "Gdi32.lib") #include #include "dpi_aware.h" #include "include/wrapper/cef_closure_task.h" #include "include/base/cef_bind.h" #include "include/base/cef_logging.h" const int DEFAULT_DPIX = 96; bool IsProcessDpiAware() { typedef BOOL(WINAPI *IsProcessDPIAwarePtr)(VOID); IsProcessDPIAwarePtr is_process_dpi_aware_func = reinterpret_cast( GetProcAddress(GetModuleHandleA("user32.dll"), "IsProcessDPIAware")); BOOL is_aware = FALSE; if (is_process_dpi_aware_func) { is_aware = is_process_dpi_aware_func(); if (is_aware == TRUE) { return true; } } // GetProcessDpiAwareness is available only on Win8. So it // should be called only as a back-up plan after IsProcessDPIAware // was called. Also if IsProcessDPIAware returned false, // then GetProcessDpiAwareness is called as well. if (GetProcessDpiAwareness() != PROCESS_DPI_UNAWARE) { return true; } return false; } PROCESS_DPI_AWARENESS GetProcessDpiAwareness() { // Win8.1 supports monitor-specific DPI scaling, so it is // recommended to use GetProcessDPIAwareness instead of the // deprecated IsProcessDPIAware. typedef HRESULT(WINAPI *GetProcessDpiAwarenessPtr) (HANDLE,PROCESS_DPI_AWARENESS*); GetProcessDpiAwarenessPtr get_process_dpi_awareness_func = reinterpret_cast( GetProcAddress(GetModuleHandleA("user32.dll"), "GetProcessDpiAwarenessInternal")); if (get_process_dpi_awareness_func) { HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, GetCurrentProcessId()); PROCESS_DPI_AWARENESS result; HRESULT hr = get_process_dpi_awareness_func(hProcess, &result); if (SUCCEEDED(hr)) { return result; } // Possible failures include E_INVALIDARG or E_ACCESSDENIED. LOG(INFO) << "[Browser process] GetProcessDpiAwareness():" " hr=" << hr; } return PROCESS_DPI_UNAWARE; } void SetProcessDpiAware() { // Win8.1 supports monitor-specific DPI scaling, so it is // recommended to use SetProcessDPIAwareness instead of the // deprecated SetProcessDPIAware. SetProcessDpiAwareness is // only available on Win8. So as a back-up plan SetProcessDPIAware // is called. // If DPI aware manifest was embedded in executable, or // "Disable display scaling on high DPI settings" was checked // on the executable properties (Compatibility tab), then // DPI awareness is already set. /* What if GetProcessDpiAwareness returned PROCESS_PER_MONITOR_DPI_AWARE? The code below sets awareness to PROCESS_SYSTEM_DPI_AWARE, so it should be called either way. --OFF: if (IsProcessDpiAware()) { return; } */ typedef BOOL(WINAPI *SetProcessDpiAwarenessPtr)(PROCESS_DPI_AWARENESS); SetProcessDpiAwarenessPtr set_process_dpi_awareness_func = reinterpret_cast( GetProcAddress(GetModuleHandleA("user32.dll"), "SetProcessDpiAwarenessInternal")); if (set_process_dpi_awareness_func) { LOG(INFO) << "[Browser process] SetProcessDpiAware():" " calling user32.dll SetProcessDpiAwareness"; HRESULT hr = set_process_dpi_awareness_func(PROCESS_SYSTEM_DPI_AWARE); if (SUCCEEDED(hr)) { LOG(INFO) << "[Browser process]: SetBrowserDpiAware():" " SetProcessDpiAwareness succeeded"; return; } else if (hr == E_ACCESSDENIED) { LOG(ERROR) << "[Browser process] SetBrowserDpiAware():" " SetProcessDpiAwareness failed:" " The DPI awareness is already set, either by calling" " this API previously or through the application" " (.exe) manifest."; // Do not return here, let's try to call SetProcessDPIAware. } else { LOG(ERROR) << "[Browser process] SetBrowserDpiAware():" " SetProcessDpiAwareness failed"; // Do not return here, let's try to call SetProcessDPIAware. } } // SetProcessDpiAwareness not found in user32.dll or the call failed. typedef BOOL(WINAPI *SetProcessDPIAwarePtr)(VOID); SetProcessDPIAwarePtr set_process_dpi_aware_func = reinterpret_cast( GetProcAddress(GetModuleHandleA("user32.dll"), "SetProcessDPIAware")); if (set_process_dpi_aware_func) { // If cefpython.Initialize() wasn't yet called, then // this log message won't be written, as g_debug is // is set during CEF initialization. LOG(INFO) << "[Browser process] SetProcessDpiAware():" " calling user32.dll SetProcessDPIAware"; set_process_dpi_aware_func(); } } void GetSystemDpi(int* dpix, int* dpiy) { // Win7 DPI (Control Panel > Appearance and Personalization > Display): // text size Larger 150% => dpix/dpiy 144 // text size Medium 125% => dpix/dpiy 120 // text size Smaller 100% => dpix/dpiy 96 // // DPI settings should not be cached. When SetProcessDpiAware // is not yet called, then OS returns 96 DPI, even though it // is set to 144 DPI. After DPI Awareness is enabled for the // running process it will return the correct 144 DPI. HDC hdc = GetDC(HWND_DESKTOP); *dpix = GetDeviceCaps(hdc, LOGPIXELSX); *dpiy = GetDeviceCaps(hdc, LOGPIXELSY); ReleaseDC(HWND_DESKTOP, hdc); } void GetDpiAwareWindowSize(int* width, int* height) { int dpix = 0; int dpiy = 0; GetSystemDpi(&dpix, &dpiy); double newZoomLevel = 0.0; // 1. Using only "dpix" value to calculate zoom level. All // modern displays have equal horizontal and vertical resolution. // 2. Calculation for DPI < 96 is not yet supported (newZoomLevel<0.0). newZoomLevel = (dpix - DEFAULT_DPIX) / 24; if (newZoomLevel > 0.0) { *width = *width + (int)ceil(newZoomLevel * 0.25 * (*width)); *height = *height + (int)ceil(newZoomLevel * 0.25 * (*height)); LOG(INFO) << "[Browser process] GetDpiAwareWindowSize():" " enlarged by " << ceil(newZoomLevel * 0.25 * 100) << "%" " new size = " << *width << "/" << *height; } } void SetBrowserDpiSettings(CefRefPtr cefBrowser, CefString autoZooming) { // Setting zoom level immediately after browser was created // won't work. We need to wait a moment before we can set it. REQUIRE_UI_THREAD(); double oldZoomLevel = cefBrowser->GetHost()->GetZoomLevel(); double newZoomLevel = 0.0; int dpix = 0; int dpiy = 0; GetSystemDpi(&dpix, &dpiy); if (autoZooming.ToString() == "system_dpi") { // Using only "dpix" value to calculate zoom level. All // modern displays have equal horizontal/vertical resolution. // Examples: // dpix=96 zoom=0.0 // dpix=120 zoom=1.0 // dpix=144 zoom=2.0 // dpix=72 zoom=-1.0 newZoomLevel = (dpix - DEFAULT_DPIX) / 24; } else { // When atof() fails converting string to double, then // 0.0 is returned. newZoomLevel = atof(autoZooming.ToString().c_str()); } if (oldZoomLevel != newZoomLevel) { cefBrowser->GetHost()->SetZoomLevel(newZoomLevel); if (cefBrowser->GetHost()->GetZoomLevel() != oldZoomLevel) { // OK succes. LOG(INFO) << "[Browser process] SetBrowserDpiSettings():" " DPI=" << dpix << "" " zoom=" << cefBrowser->GetHost()->GetZoomLevel(); } } else { // This code block running can also be a result of executing // SetZoomLevel(), as GetZoomLevel() didn't return the new // value that was set. Documentation says that if SetZoomLevel // is called on the UI thread, then GetZoomLevel should // immediately return the same value that was set. Unfortunately // this seems not to be true. static bool already_logged = false; if (!already_logged) { already_logged = true; // OK success. LOG(INFO) << "[Browser process] SetBrowserDpiSettings():" " DPI=" << dpix << "" " zoom=" << cefBrowser->GetHost()->GetZoomLevel(); } } // We need to check zooming constantly, during loading of pages. // If we set zooming to 2.0 for localhost/ and then it navigates // to google.com, then the zomming is back at 0.0 and needs to // be set again. CefPostDelayedTask( TID_UI, CefCreateClosureTask( base::Bind(&SetBrowserDpiSettings, cefBrowser, autoZooming) ), 50 ); }
X Tutup