From 7e32f2df711e524cac803aacaee5040509205bb8 Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Tue, 2 Apr 2019 12:00:50 +0200 Subject: [PATCH] Implement per-screen DPI on Windows, DPI change event, wxDialog & wxFrame mixin base classes --- src/slic3r/GUI/GUI_App.cpp | 20 +++++++++++ src/slic3r/GUI/GUI_Utils.cpp | 63 ++++++++++++++++++++++++++++++++++ src/slic3r/GUI/GUI_Utils.hpp | 65 ++++++++++++++++++++++++++++++++++++ src/slic3r/GUI/MainFrame.cpp | 7 +++- src/slic3r/GUI/MainFrame.hpp | 6 +++- 5 files changed, 159 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index f1a7d9263..8c34337dc 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -76,6 +76,24 @@ wxString file_wildcards(FileType file_type, const std::string &custom_extension) static std::string libslic3r_translate_callback(const char *s) { return wxGetTranslation(wxString(s, wxConvUTF8)).utf8_str().data(); } +static void register_dpi_event() +{ +#ifdef WIN32 + enum { WM_DPICHANGED_ = 0x02e0 }; + + wxWindow::MSWRegisterMessageHandler(WM_DPICHANGED_, [](wxWindow *win, WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) { + const int dpi = wParam & 0xffff; + const auto rect = reinterpret_cast(lParam); + const wxRect wxrect(wxPoint(rect->top, rect->left), wxPoint(rect->bottom, rect->right)); + + DpiChangedEvent evt(EVT_DPI_CHANGED, dpi, wxrect); + win->GetEventHandler()->AddPendingEvent(evt); + + return true; + }); +#endif +} + IMPLEMENT_APP(GUI_App) GUI_App::GUI_App() @@ -149,6 +167,8 @@ bool GUI_App::OnInit() show_error(nullptr, ex.what()); } + register_dpi_event(); + // Let the libslic3r know the callback, which will translate messages on demand. Slic3r::I18N::set_translate_callback(libslic3r_translate_callback); diff --git a/src/slic3r/GUI/GUI_Utils.cpp b/src/slic3r/GUI/GUI_Utils.cpp index 56d6eaeb5..6dc5c5b4c 100644 --- a/src/slic3r/GUI/GUI_Utils.cpp +++ b/src/slic3r/GUI/GUI_Utils.cpp @@ -4,9 +4,14 @@ #include #include +#ifdef _WIN32 +#include +#endif + #include #include #include +#include #include "libslic3r/Config.hpp" @@ -48,6 +53,64 @@ void on_window_geometry(wxTopLevelWindow *tlw, std::function callback) #endif } +wxDEFINE_EVENT(EVT_DPI_CHANGED, DpiChangedEvent); + +#ifdef _WIN32 +template typename F::FN winapi_get_function(const wchar_t *dll, const char *fn_name) { + static HINSTANCE dll_handle = LoadLibraryExW(dll, nullptr, 0); + + if (dll_handle == nullptr) { return nullptr; } + return (F::FN)GetProcAddress(dll_handle, fn_name); +} +#endif + +int get_dpi_for_window(wxWindow *window) +{ +#ifdef _WIN32 + enum MONITOR_DPI_TYPE_ { + // This enum is inlined here to avoid build-time dependency + MDT_EFFECTIVE_DPI_ = 0, + MDT_ANGULAR_DPI_ = 1, + MDT_RAW_DPI_ = 2, + MDT_DEFAULT_ = MDT_EFFECTIVE_DPI_, + }; + + // Need strong types for winapi_get_function() to work + struct GetDpiForWindow_t { typedef HRESULT (WINAPI *FN)(HWND hwnd); }; + struct GetDpiForMonitor_t { typedef HRESULT (WINAPI *FN)(HMONITOR hmonitor, MONITOR_DPI_TYPE_ dpiType, UINT *dpiX, UINT *dpiY); }; + + static auto GetDpiForWindow_fn = winapi_get_function(L"User32.dll", "GetDpiForWindow"); + static auto GetDpiForMonitor_fn = winapi_get_function(L"Shcore.dll", "GetDpiForMonitor"); + + const HWND hwnd = window->GetHandle(); + + if (GetDpiForWindow_fn != nullptr) { + // We're on Windows 10, we have per-screen DPI settings + return GetDpiForWindow_fn(hwnd); + } else if (GetDpiForMonitor_fn != nullptr) { + // We're on Windows 8.1, we have per-system DPI + // Note: MonitorFromWindow() is available on all Windows. + + const HMONITOR monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST); + UINT dpiX; + UINT dpiY; + return GetDpiForMonitor_fn(monitor, MDT_EFFECTIVE_DPI_, &dpiX, &dpiY) == S_OK ? dpiX : DPI_DEFAULT; + } else { + // We're on Windows earlier than 8.1, use DC + + const HDC hdc = GetDC(hwnd); + if (hdc == NULL) { return DPI_DEFAULT; } + return GetDeviceCaps(hdc, LOGPIXELSX); + } +#elif defined __linux__ + // TODO + return DPI_DEFAULT; +#elif defined __APPLE__ + // TODO + return DPI_DEFAULT; +#endif +} + CheckboxFileDialog::ExtraPanel::ExtraPanel(wxWindow *parent) : wxPanel(parent, wxID_ANY) diff --git a/src/slic3r/GUI/GUI_Utils.hpp b/src/slic3r/GUI/GUI_Utils.hpp index 8d942dcf8..e12153625 100644 --- a/src/slic3r/GUI/GUI_Utils.hpp +++ b/src/slic3r/GUI/GUI_Utils.hpp @@ -8,10 +8,13 @@ #include +#include +#include #include #include #include #include +#include #include class wxCheckBox; @@ -27,6 +30,68 @@ wxTopLevelWindow* find_toplevel_parent(wxWindow *window); void on_window_geometry(wxTopLevelWindow *tlw, std::function callback); +enum { DPI_DEFAULT = 96 }; + +int get_dpi_for_window(wxWindow *window); + +struct DpiChangedEvent : public wxEvent { + int dpi; + wxRect rect; + + DpiChangedEvent(wxEventType eventType, int dpi, wxRect rect) + : wxEvent(0, eventType), dpi(dpi), rect(rect) + {} + + virtual wxEvent *Clone() const + { + return new DpiChangedEvent(*this); + } +}; + +wxDECLARE_EVENT(EVT_DPI_CHANGED, DpiChangedEvent); + +template class DPIAware : public P +{ +public: + DPIAware(wxWindow *parent, wxWindowID id, const wxString &title, const wxPoint &pos=wxDefaultPosition, + const wxSize &size=wxDefaultSize, long style=wxDEFAULT_FRAME_STYLE, const wxString &name=wxFrameNameStr) + : P(parent, id, title, pos, size, style, name) + { + m_scale_factor = (float)get_dpi_for_window(this) / (float)DPI_DEFAULT; + recalc_font(); + + this->Bind(EVT_DPI_CHANGED, [this](const DpiChangedEvent &evt) { + m_scale_factor = (float)evt.dpi / (float)DPI_DEFAULT; + on_dpi_changed(evt.rect); + }); + } + + virtual ~DPIAware() {} + + float scale_factor() const { return m_scale_factor; } + int em_unit() const { return m_em_unit; } + int font_size() const { return m_font_size; } + +protected: + virtual void on_dpi_changed(const wxRect &suggested_rect) = 0; + +private: + int m_scale_factor; + int m_em_unit; + int m_font_size; + + void recalc_font() + { + wxClientDC dc(this); + const auto metrics = dc.GetFontMetrics(); + m_font_size = metrics.height; + m_em_unit = metrics.averageWidth; + } +}; + +typedef DPIAware DPIFrame; +typedef DPIAware DPIDialog; + class EventGuard { diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 0c3cf316d..059d04f85 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -32,7 +32,7 @@ namespace Slic3r { namespace GUI { MainFrame::MainFrame() : -wxFrame(NULL, wxID_ANY, SLIC3R_BUILD, wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE, "mainframe"), +DPIFrame(NULL, wxID_ANY, SLIC3R_BUILD, wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE, "mainframe"), m_printhost_queue_dlg(new PrintHostQueueDialog(this)) { // Load the icon either from the exe, or from the ico file. @@ -256,6 +256,11 @@ bool MainFrame::can_delete_all() const return (m_plater != nullptr) ? !m_plater->model().objects.empty() : false; } +void MainFrame::on_dpi_changed(const wxRect &suggested_rect) +{ + // TODO +} + void MainFrame::init_menubar() { #ifdef __APPLE__ diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp index b44c73f91..625e70b83 100644 --- a/src/slic3r/GUI/MainFrame.hpp +++ b/src/slic3r/GUI/MainFrame.hpp @@ -9,6 +9,7 @@ #include #include +#include "GUI_Utils.hpp" #include "Plater.hpp" #include "Event.hpp" @@ -40,7 +41,7 @@ struct PresetTab { PrinterTechnology technology; }; -class MainFrame : public wxFrame +class MainFrame : public DPIFrame { bool m_loaded {false}; @@ -68,6 +69,9 @@ class MainFrame : public wxFrame bool can_delete() const; bool can_delete_all() const; +protected: + virtual void on_dpi_changed(const wxRect &suggested_rect); + public: MainFrame(); ~MainFrame() {}