From d96674186e555fcef0b18207e3546429f4b2e33c Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 20 Oct 2022 01:49:33 -0500 Subject: [PATCH] GUI: new scaling factor detection technique --- CMakeLists.txt | 1 + src/engine/engine.h | 4 +- src/gui/gui.cpp | 122 ++++++++++++++++---------- src/gui/gui.h | 2 +- src/gui/scaling.cpp | 207 ++++++++++++++++++++++++++++++++++++++++++++ src/gui/scaling.h | 20 +++++ 6 files changed, 306 insertions(+), 50 deletions(-) create mode 100644 src/gui/scaling.cpp create mode 100644 src/gui/scaling.h diff --git a/CMakeLists.txt b/CMakeLists.txt index fc2d6dc7..8628379f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -593,6 +593,7 @@ src/gui/piano.cpp src/gui/presets.cpp src/gui/regView.cpp src/gui/sampleEdit.cpp +src/gui/scaling.cpp src/gui/settings.cpp src/gui/songInfo.cpp src/gui/songNotes.cpp diff --git a/src/engine/engine.h b/src/engine/engine.h index f4ed7cf5..b30f52b1 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -47,8 +47,8 @@ #define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock(); #define BUSY_END isBusy.unlock(); softLocked=false; -#define DIV_VERSION "dev121" -#define DIV_ENGINE_VERSION 121 +#define DIV_VERSION "dev122" +#define DIV_ENGINE_VERSION 122 // for imports #define DIV_VERSION_MOD 0xff01 #define DIV_VERSION_FC 0xff02 diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index f3cc2918..ec5e25cb 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -1,3 +1,5 @@ +#define _USE_MATH_DEFINES +// OK, sorry for inserting the define here but I'm so tired of this extension /** * Furnace Tracker - multi-system chiptune tracker * Copyright (C) 2021-2022 tildearrow and contributors @@ -20,7 +22,7 @@ // I hate you clangd extension! // how about you DON'T insert random headers before this freaking important // define!!!!!! -#define _USE_MATH_DEFINES + #include "gui.h" #include "util.h" #include "icon.h" @@ -36,17 +38,12 @@ #include "plot_nolerp.h" #include "guiConst.h" #include "intConst.h" +#include "scaling.h" #include #include #include #include -#ifdef __APPLE__ -extern "C" { -#include "macstuff.h" -} -#endif - #ifdef _WIN32 #include #include @@ -4898,9 +4895,7 @@ bool FurnaceGUI::loop() { } bool FurnaceGUI::init() { -#ifndef __APPLE__ - float dpiScaleF; -#endif + logI("initializing GUI."); String homeDir=getHomeDir(); workingDir=e->getConfString("lastDir",homeDir); @@ -5004,10 +4999,6 @@ bool FurnaceGUI::init() { } } - if (settings.dpiScale>=0.5f) { - dpiScale=settings.dpiScale; - } - initSystemPresets(); e->setAutoNotePoly(noteInputPoly); @@ -5017,6 +5008,7 @@ bool FurnaceGUI::init() { SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS,"0"); // don't disable compositing on KWin #if SDL_VERSION_ATLEAST(2,0,22) + logV("setting window type to NORMAL."); SDL_SetHint(SDL_HINT_X11_WINDOW_TYPE,"_NET_WM_WINDOW_TYPE_NORMAL"); #endif @@ -5025,14 +5017,28 @@ bool FurnaceGUI::init() { const char* videoBackend=SDL_GetCurrentVideoDriver(); if (videoBackend!=NULL) { + logV("video backend: %s",videoBackend); if (strcmp(videoBackend,"wayland")==0 || strcmp(videoBackend,"cocoa")==0 || strcmp(videoBackend,"uikit")==0) { sysManagedScale=true; + logV("scaling managed by system."); + } else { + logV("scaling managed by application."); } + } else { + logV("could not get video backend name!"); } - // TODO: get scaling factor + // get scale factor + if (settings.dpiScale>=0.5f) { + logD("setting UI scale factor from config (%f).",settings.dpiScale); + dpiScale=settings.dpiScale; + } else { + logD("auto-detecting UI scale factor."); + dpiScale=getScaleFactor(videoBackend); + logD("scale factor: %f",dpiScale); + } #if !(defined(__APPLE__) || defined(_WIN32)) // get the icon (on macOS and Windows the icon is bundled with the app) @@ -5046,10 +5052,8 @@ bool FurnaceGUI::init() { scrX=0; scrY=0; #else - double defaultW=1280*(sysManagedScale?1.0:dpiScale); - double defaultH=800*(sysManagedScale?1.0:dpiScale); - scrW=scrConfW=e->getConfInt("lastWindowWidth",(int)defaultW); - scrH=scrConfH=e->getConfInt("lastWindowHeight",(int)defaultH); + scrW=scrConfW=e->getConfInt("lastWindowWidth",1280); + scrH=scrConfH=e->getConfInt("lastWindowHeight",800); scrX=scrConfX=e->getConfInt("lastWindowX",SDL_WINDOWPOS_CENTERED); scrY=scrConfY=e->getConfInt("lastWindowY",SDL_WINDOWPOS_CENTERED); scrMax=e->getConfBool("lastWindowMax",false); @@ -5057,6 +5061,22 @@ bool FurnaceGUI::init() { portrait=(scrWgetConfInt("configVersion",0)<122 && !sysManagedScale) { + logD("scaling window size to scale factor because configVersion is not present."); + scrW*=dpiScale; + scrH*=dpiScale; + } + + // predict the canvas size + if (sysManagedScale) { + canvasW=scrW*dpiScale; + canvasH=scrH*dpiScale; + } else { + canvasW=scrW; + canvasH=scrH; + } + #if !defined(__APPLE__) && !defined(IS_MOBILE) SDL_Rect displaySize; #endif @@ -5070,47 +5090,41 @@ bool FurnaceGUI::init() { } #endif + logV("window size: %dx%d",scrW,scrH); + sdlWin=SDL_CreateWindow("Furnace",scrX,scrY,scrW,scrH,SDL_WINDOW_RESIZABLE|SDL_WINDOW_ALLOW_HIGHDPI|(scrMax?SDL_WINDOW_MAXIMIZED:0)|(fullScreen?SDL_WINDOW_FULLSCREEN_DESKTOP:0)); if (sdlWin==NULL) { lastError=fmt::sprintf("could not open window! %s",SDL_GetError()); return false; } - // TODO: remove this -#ifndef __APPLE__ - if (settings.dpiScale<0.5f) { - // TODO: replace with a function to actually detect the display scaling factor as it's unreliable. - SDL_GetDisplayDPI(SDL_GetWindowDisplayIndex(sdlWin),&dpiScaleF,NULL,NULL); - dpiScale=round(dpiScaleF/96.0f); - if (dpiScale<1) dpiScale=1; -#ifndef IS_MOBILE - if (dpiScale!=1) { - if (!fullScreen) { - SDL_SetWindowSize(sdlWin,scrW*dpiScale,scrH*dpiScale); - } + if (SDL_GetDisplayUsableBounds(SDL_GetWindowDisplayIndex(sdlWin),&displaySize)==0) { + bool mustChange=false; + if (scrW>((displaySize.w)-48) && scrH>((displaySize.h)-64)) { + // maximize + SDL_MaximizeWindow(sdlWin); + logD("maximizing as it doesn't fit (%dx%d+%d+%d).",displaySize.w,displaySize.h,displaySize.x,displaySize.y); } - - if (SDL_GetDisplayUsableBounds(SDL_GetWindowDisplayIndex(sdlWin),&displaySize)==0) { - if (scrW>((displaySize.w/dpiScale)-48) && scrH>((displaySize.h/dpiScale)-64)) { - // maximize - SDL_MaximizeWindow(sdlWin); - } - if (scrW>displaySize.w/dpiScale) scrW=(displaySize.w/dpiScale)-32; - if (scrH>displaySize.h/dpiScale) scrH=(displaySize.h/dpiScale)-32; + if (scrW>displaySize.w) { + scrW=(displaySize.w)-32; + mustChange=true; + } + if (scrH>displaySize.h) { + scrH=(displaySize.h)-32; + mustChange=true; + } + if (mustChange) { portrait=(scrWsetConf("configVersion",(int)DIV_ENGINE_VERSION); + e->setConf("lastDir",workingDir); e->setConf("lastDirSong",workingDirSong); e->setConf("lastDirIns",workingDirIns); @@ -5369,6 +5395,8 @@ FurnaceGUI::FurnaceGUI(): scrH(800), scrConfW(1280), scrConfH(800), + canvasW(1280), + canvasH(800), scrX(SDL_WINDOWPOS_CENTERED), scrY(SDL_WINDOWPOS_CENTERED), scrConfX(SDL_WINDOWPOS_CENTERED), diff --git a/src/gui/gui.h b/src/gui/gui.h index 5f5064e5..dd3a02af 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -1062,7 +1062,7 @@ class FurnaceGUI { FurnaceGUIFileDialog* fileDialog; - int scrW, scrH, scrConfW, scrConfH; + int scrW, scrH, scrConfW, scrConfH, canvasW, canvasH; int scrX, scrY, scrConfX, scrConfY; bool scrMax, sysManagedScale; diff --git a/src/gui/scaling.cpp b/src/gui/scaling.cpp new file mode 100644 index 00000000..c1fd5bcc --- /dev/null +++ b/src/gui/scaling.cpp @@ -0,0 +1,207 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2022 tildearrow and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include "scaling.h" +#include "../ta-log.h" +#include + +#ifdef _WIN32 +#include +typedef HRESULT (*GDFM)(HMONITOR,int,UINT*,UINT*); +#endif + +#ifdef __APPLE__ +extern "C" { +#include "macstuff.h" +} +#endif + +#if defined(__unix__) || defined(ANDROID) +#include +typedef void* (*XOD)(const char*); +typedef int (*XCD)(void*); +typedef int (*XDS)(void*); +typedef int (*XDW)(void*,int); +#endif + +double getScaleFactor(const char* driverHint) { + double ret=1.0; + + // Windows +#ifdef _WIN32 + POINT nullPoint; + nullPoint.x=-1; + nullPoint.y=-1; + HMONITOR disp=MonitorFromPoint(nullPoint,MONITOR_DEFAULTTOPRIMARY); + + if (disp==NULL) { + logW("could not find a monitor - no scaling detection available!"); + return 1.0; + } + + HMODULE shcore=LoadLibraryW(L"shcore.dll"); + if (shcore==NULL) { + logW("could not find shcore.dll (%.8x) - no scaling detection available!",GetLastError()); + return 1.0; + } + GDFM ta_GetDpiForMonitor=(GDFM)GetProcAddress(shcore,"GetDpiForMonitor"); + if (ta_GetDpiForMonitor==NULL) { + logW("GetDpiForMonitor not found (%.8x) - no scaling detection available!",GetLastError()); + + if (!FreeLibrary(shcore)) { + logE("could not free shcore.dll (%.8x)!",GetLastError()); + } + return 1.0; + } + + unsigned int dpiX=96; + unsigned int dpiY=96; + HRESULT result=ta_GetDpiForMonitor(disp,0,&dpiX,&dpiY); + if (result!=S_OK) { + logW("GetDpiForMonitor failure (%.8x) - no scaling detection available!",result); + + if (!FreeLibrary(shcore)) { + logE("could not free shcore.dll (%.8x)!",GetLastError()); + } + return 1.0; + } + + ret=(double)(dpiX+dpiY)/192.0; + + if (!FreeLibrary(shcore)) { + logE("could not free shcore.dll (%.8x)!",GetLastError()); + } + + return ret; +#endif + + // macOS - backingScaleFactor +#ifdef __APPLE__ + if (driverHint==NULL) { + return getMacDPIScale(); + } else if (strcmp(driverHint,"cocoa")==0 || strcmp(driverHint,"uikit")==0) { + return getMacDPIScale(); + } +#endif + +#if defined(__unix__) || defined(ANDROID) + if (driverHint==NULL) { + return ret; + } + + // X11 + if (strcmp(driverHint,"x11")==0) { + void* libX11=dlopen("libX11.so",RTLD_LAZY|RTLD_LOCAL); + if (libX11==NULL) { + logW("could not load libX11.so (%s) - no scaling detection available!",dlerror()); + return 1.0; + } + + XOD ta_XOpenDisplay=(XOD)dlsym(libX11,"XOpenDisplay"); + if (ta_XOpenDisplay==NULL) { + logW("XOpenDisplay not found (%s) - no scaling detection available!",dlerror()); + if (dlclose(libX11)!=0) { + logE("could not free libX11.so (%s)!",dlerror()); + } + return 1.0; + } + + XCD ta_XCloseDisplay=(XCD)dlsym(libX11,"XCloseDisplay"); + if (ta_XCloseDisplay==NULL) { + logW("XCloseDisplay not found (%s) - no scaling detection available!",dlerror()); + if (dlclose(libX11)!=0) { + logE("could not free libX11.so (%s)!",dlerror()); + } + return 1.0; + } + + XDS ta_XDefaultScreen=(XDS)dlsym(libX11,"XDefaultScreen"); + if (ta_XDefaultScreen==NULL) { + logW("XDefaultScreen not found (%s) - no scaling detection available!",dlerror()); + if (dlclose(libX11)!=0) { + logE("could not free libX11.so (%s)!",dlerror()); + } + return 1.0; + } + + XDW ta_XDisplayWidth=(XDW)dlsym(libX11,"XDisplayWidth"); + if (ta_XDisplayWidth==NULL) { + logW("XDisplayWidth not found (%s) - no scaling detection available!",dlerror()); + if (dlclose(libX11)!=0) { + logE("could not free libX11.so (%s)!",dlerror()); + } + return 1.0; + } + + XDW ta_XDisplayWidthMM=(XDW)dlsym(libX11,"XDisplayWidthMM"); + if (ta_XDisplayWidthMM==NULL) { + logW("XDisplayWidthMM not found (%s) - no scaling detection available!",dlerror()); + if (dlclose(libX11)!=0) { + logE("could not free libX11.so (%s)!",dlerror()); + } + return 1.0; + } + + // dl mess + void* disp=NULL; + int screen=0; + int dpi=96; + + disp=ta_XOpenDisplay(NULL); + if (disp==NULL) { + logW("couldn't open X display - no scaling detection available!",dlerror()); + if (dlclose(libX11)!=0) { + logE("could not free libX11.so (%s)!",dlerror()); + } + return 1.0; + } + + screen=ta_XDefaultScreen(disp); + + dpi=(int)(0.5+(25.4*(double)ta_XDisplayWidth(disp,screen)/(double)ta_XDisplayWidthMM(disp,screen))); + + ta_XCloseDisplay(disp); + + ret=round(dpi/96.0); + + if (dlclose(libX11)!=0) { + logE("could not free libX11.so (%s)!",dlerror()); + } + + return ret; + } + + // Wayland + if (strcmp(driverHint,"wayland")==0) { + // give up (we handle scaling factor detection after window creation) + return 1.0; + } +#endif + + // SDL fallback + float dpiScaleF=96.0f; + if (SDL_GetDisplayDPI(0,&dpiScaleF,NULL,NULL)==0) { + ret=round(dpiScaleF/96.0f); + if (ret<1) ret=1; + } + + // couldn't detect scaling factor :< + return ret; +} diff --git a/src/gui/scaling.h b/src/gui/scaling.h new file mode 100644 index 00000000..ce1a364a --- /dev/null +++ b/src/gui/scaling.h @@ -0,0 +1,20 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2022 tildearrow and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +double getScaleFactor(const char* driverHint); \ No newline at end of file