diff --git a/CMakeLists.txt b/CMakeLists.txt index 56c1f386..239a8bfc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -286,9 +286,7 @@ if (_GLFW_WAYLAND) list(APPEND glfw_LIBRARIES "${Wayland_LIBRARIES}" "${CMAKE_THREAD_LIBS_INIT}") find_package(XKBCommon REQUIRED) - list(APPEND glfw_PKG_DEPS "xkbcommon") list(APPEND glfw_INCLUDE_DIRS "${XKBCOMMON_INCLUDE_DIRS}") - list(APPEND glfw_LIBRARIES "${XKBCOMMON_LIBRARY}") endif() #-------------------------------------------------------------------- diff --git a/README.md b/README.md index 01b159c9..4048baf1 100644 --- a/README.md +++ b/README.md @@ -136,6 +136,8 @@ information on what to include when reporting a bug. gamepad mapping (#900) - Added `glfwGetGamepadState` function, `GLFW_GAMEPAD_*` and `GLFWgamepadstate` for retrieving gamepad input state (#900) +- Added `glfwGetWindowContentScale` and `glfwGetMonitorContentScale` for + DPI-aware rendering (#235,#439,#677,#845,#898) - Added `glfwRequestWindowAttention` function for requesting attention from the user (#732,#988) - Added `glfwGetKeyScancode` function that allows retrieving platform dependent @@ -198,6 +200,7 @@ information on what to include when reporting a bug. - [Win32] Bugfix: Disabled cursor mode prevented use of caption buttons (#650,#1071) - [Win32] Bugfix: Returned key names did not match other platforms (#943) +- [Win32] Bugfix: Undecorated windows did not maximize to workarea (#899) - [X11] Moved to XI2 `XI_RawMotion` for disable cursor mode motion input (#125) - [X11] Replaced `_GLFW_HAS_XF86VM` compile-time option with dynamic loading - [X11] Bugfix: `glfwGetVideoMode` would segfault on Cygwin/X diff --git a/docs/build.dox b/docs/build.dox index e428594e..18ce13ce 100644 --- a/docs/build.dox +++ b/docs/build.dox @@ -183,8 +183,8 @@ This section is about using CMake to compile and link GLFW along with your application. If you want to use an installed binary instead, see @ref build_link_cmake_package. -With just a few changes to your `CMakeLists.txt` you can have the GLFW source -tree built along with your application. +With a few changes to your `CMakeLists.txt` you can have the GLFW source tree +built along with your application. When including GLFW as part of your build, you probably don't want to build the GLFW tests, examples and documentation. To disable these, set the corresponding @@ -249,7 +249,7 @@ This section is about using CMake to link GLFW after it has been built and installed. If you want to build it along with your application instead, see @ref build_link_cmake_source. -With just a few changes to your `CMakeLists.txt`, you can locate the package and +With a few changes to your `CMakeLists.txt` you can locate the package and target files generated when GLFW is installed. @code{.cmake} @@ -312,8 +312,8 @@ GLFW library may look like this: cc `pkg-config --cflags glfw3` -o myprog myprog.c `pkg-config --static --libs glfw3` @endcode -If you are using the shared version of the GLFW library, simply omit the -`--static` flag. +If you are using the shared version of the GLFW library, omit the `--static` +flag. @code{.sh} cc `pkg-config --cflags glfw3` -o myprog myprog.c `pkg-config --libs glfw3` @@ -350,8 +350,8 @@ cc `pkg-config --cflags glfw3 glu` -o myprog myprog.c `pkg-config --static --lib @subsection build_link_xcode With Xcode on macOS -If you are using the dynamic library version of GLFW, simply add it to the -project dependencies. +If you are using the dynamic library version of GLFW, add it to the project +dependencies. If you are using the static library version of GLFW, add it and the Cocoa, OpenGL, IOKit and CoreVideo frameworks to the project as dependencies. They can diff --git a/docs/compile.dox b/docs/compile.dox index 35d65732..4486156e 100644 --- a/docs/compile.dox +++ b/docs/compile.dox @@ -13,7 +13,7 @@ build applications that use GLFW, see @ref build_guide. GLFW uses [CMake](http://www.cmake.org/) to generate project files or makefiles for a particular development environment. If you are on a Unix-like system such as Linux or FreeBSD or have a package system like Fink, MacPorts, Cygwin or -Homebrew, you can simply install its CMake package. If not, you can download +Homebrew, you can install its CMake package. If not, you can download installers for Windows and macOS from the [CMake website](http://www.cmake.org/). @@ -33,9 +33,9 @@ below. @subsubsection compile_deps_msvc Dependencies for Visual C++ on Windows -The Microsoft Platform SDK that is installed along with Visual C++ already -contains all the necessary headers, link libraries and tools except for CMake. -Move on to @ref compile_generate. +The Windows SDK bundled with Visual C++ already contains all the necessary +headers, link libraries and tools except for CMake. Move on to @ref +compile_generate. @subsubsection compile_deps_mingw Dependencies for MinGW or MinGW-w64 on Windows diff --git a/docs/context.dox b/docs/context.dox index 9aa72a5c..61292849 100644 --- a/docs/context.dox +++ b/docs/context.dox @@ -61,7 +61,7 @@ information. The name and number of this chapter unfortunately varies between versions and APIs, but has at times been named _Shared Objects and Multiple Contexts_. -GLFW comes with a simple object sharing test program called `sharing`. +GLFW comes with a barebones object sharing test program called `sharing`. @subsection context_offscreen Offscreen contexts diff --git a/docs/extra.css b/docs/extra.css index df978c0c..42091cd1 100644 --- a/docs/extra.css +++ b/docs/extra.css @@ -1 +1 @@ -.sm-dox,.sm-dox a,.sm-dox a:focus,.sm-dox a:active,.sm-dox a:hover,.sm-dox a.highlighted,.sm-dox ul a:hover{background:none;text-shadow:none}.sm-dox ul a:hover{background:#666;text-shadow:none}#main-nav,#navrow1,#navrow2,#navrow3,#navrow4,.tablist a,.tablist a:visited,.tablist a:hover,.tablist li,.tablist li.current a,.memdoc,dl.reflist dd,div.toc li,.ah,span.lineno,span.lineno a,span.lineno a:hover,.note code,.pre code,.post code,.invariant code,.warning code,.attention code,.deprecated code,.bug code,.todo code,.test code,.doxtable code{background:none}#titlearea,.footer,.contents,div.header,.memdoc,table.doxtable td,table.doxtable th,hr,.memSeparator{border:none}.tablist a,.tablist a:visited,.tablist a:hover,.tablist li,.tablist li.current a,.reflist dt a.el,.levels span,.directory .levels span{text-shadow:none}.memdoc,dl.reflist dd{box-shadow:none}div.headertitle,.note code,.pre code,.post code,.invariant code,.warning code,.attention code,.deprecated code,.bug code,.todo code,.test code,table.doxtable code{padding:0}#nav-path,.directory .levels,span.lineno{display:none}html,#titlearea,.footer,tr.even,.directory tr.even,.doxtable tr:nth-child(even),.mdescLeft,.mdescRight,.memItemLeft,.memItemRight,code{background:#f2f2f2}body{color:#4d4d4d}h1,h2,h2.groupheader,h3,div.toc h3,h4,h5,h6,strong,em{color:#1a1a1a;border-bottom:none}h1{padding-top:0.5em;font-size:180%}h2{padding-top:0.5em;margin-bottom:0;font-size:140%}h3{padding-top:0.5em;margin-bottom:0;font-size:110%}.glfwheader{font-size:16px;height:64px;max-width:920px;min-width:800px;padding:0 32px;margin:0 auto}#glfwhome{line-height:64px;padding-right:48px;color:#666;font-size:2.5em;background:url("http://www.glfw.org/css/arrow.png") no-repeat right}.glfwnavbar{list-style-type:none;margin:0 auto;float:right}#glfwhome,.glfwnavbar li{float:left}.glfwnavbar a,.glfwnavbar a:visited{line-height:64px;margin-left:2em;display:block;color:#666}#glfwhome,.glfwnavbar a,.glfwnavbar a:visited{transition:.35s ease}#titlearea,.footer{color:#666}address.footer{text-align:center;padding:2em;margin-top:3em}#top{background:#666}#main-nav{max-width:960px;min-width:800px;margin:0 auto;font-size:13px}#navrow1,#navrow2,#navrow3,#navrow4{max-width:920px;min-width:800px;margin:0 auto;font-size:13px}.memtitle{display:none}.memproto,.memname{font-weight:bold;text-shadow:none}.tablist{height:36px;display:block;position:relative}.tablist a,.tablist a:visited,.tablist a:hover,.tablist li,.tablist li.current a{color:#f2f2f2}.tablist li.current a{background:linear-gradient(to bottom, #ffa733 0, #f60 100%);box-shadow:inset 0 0 32px #f60;text-shadow:0 -1px 1px #b34700;color:#fff}.contents{min-height:590px}div.contents,div.header{max-width:920px;margin:0 auto;padding:0 32px;background:#fff none}table.doxtable th,dl.reflist dt{background:linear-gradient(to bottom, #ffa733 0, #f60 100%);box-shadow:inset 0 0 32px #f60;text-shadow:0 -1px 1px #b34700;text-align:left;color:#fff}dl.reflist dt a.el{color:#f60;padding:.2em;border-radius:4px;background-color:#ffe0cc}div.toc{float:none;width:auto}div.toc h3{font-size:1.17em}div.toc ul{padding-left:1.5em}div.toc li{font-size:1em;padding-left:0;list-style-type:disc}div.toc,.memproto,div.qindex,div.ah{background:linear-gradient(to bottom, #f2f2f2 0, #e6e6e6 100%);box-shadow:inset 0 0 32px #e6e6e6;text-shadow:0 1px 1px #fff;color:#1a1a1a;border:2px solid #e6e6e6;border-radius:4px}.paramname{color:#803300}dl.reflist dt{border:2px solid #f60;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom:none}dl.reflist dd{border:2px solid #f60;border-bottom-right-radius:4px;border-bottom-left-radius:4px;border-top:none}table.doxtable{border-collapse:inherit;border-spacing:0;border:2px solid #f60;border-radius:4px}a,a:hover,a:visited,a:visited:hover,.contents a:visited,.el,a.el:visited,#glfwhome:hover,.tablist a:hover,span.lineno a:hover{color:#f60;text-decoration:none}div.directory{border-collapse:inherit;border-spacing:0;border:2px solid #f60;border-radius:4px}hr,.memSeparator{height:2px;background:linear-gradient(to right, #f2f2f2 0, #d9d9d9 50%, #f2f2f2 100%)}dl.note,dl.pre,dl.post,dl.invariant{background:linear-gradient(to bottom, #ddfad1 0, #cbf7ba 100%);box-shadow:inset 0 0 32px #baf5a3;color:#1e5309;border:2px solid #afe599}dl.warning,dl.attention{background:linear-gradient(to bottom, #fae8d1 0, #f7ddba 100%);box-shadow:inset 0 0 32px #f5d1a3;color:#533309;border:2px solid #e5c499}dl.deprecated,dl.bug{background:linear-gradient(to bottom, #fad1e3 0, #f7bad6 100%);box-shadow:inset 0 0 32px #f5a3c8;color:#53092a;border:2px solid #e599bb}dl.todo,dl.test{background:linear-gradient(to bottom, #d1ecfa 0, #bae3f7 100%);box-shadow:inset 0 0 32px #a3daf5;color:#093a53;border:2px solid #99cce5}dl.note,dl.pre,dl.post,dl.invariant,dl.warning,dl.attention,dl.deprecated,dl.bug,dl.todo,dl.test{border-radius:4px;padding:1em;text-shadow:0 1px 1px #fff;margin:1em 0}.note a,.pre a,.post a,.invariant a,.warning a,.attention a,.deprecated a,.bug a,.todo a,.test a,.note a:visited,.pre a:visited,.post a:visited,.invariant a:visited,.warning a:visited,.attention a:visited,.deprecated a:visited,.bug a:visited,.todo a:visited,.test a:visited{color:inherit}div.line{line-height:inherit}div.fragment,pre.fragment{background:#f2f2f2;border-radius:4px;border:none;padding:1em;overflow:auto;border-left:4px solid #ccc;margin:1em 0}.lineno a,.lineno a:visited,.line,pre.fragment{color:#4d4d4d}span.preprocessor,span.comment{color:#007899}a.code,a.code:visited{color:#e64500}span.keyword,span.keywordtype,span.keywordflow{color:#404040;font-weight:bold}span.stringliteral{color:#360099}code{padding:.1em;border-radius:4px} +.sm-dox,.sm-dox a,.sm-dox a:focus,.sm-dox a:active,.sm-dox a:hover,.sm-dox a.highlighted,.sm-dox ul a:hover{background:none;text-shadow:none}.sm-dox a span.sub-arrow{border-color:#f2f2f2 transparent transparent transparent}.sm-dox a span.sub-arrow:active,.sm-dox a span.sub-arrow:focus,.sm-dox a span.sub-arrow:hover,.sm-dox a:hover span.sub-arrow{border-color:#f60 transparent transparent transparent}.sm-dox ul a span.sub-arrow:active,.sm-dox ul a span.sub-arrow:focus,.sm-dox ul a span.sub-arrow:hover,.sm-dox ul a:hover span.sub-arrow{border-color:transparent transparent transparent #f60}.sm-dox ul a:hover{background:#666;text-shadow:none}.sm-dox ul.sm-nowrap a{color:#4d4d4d;text-shadow:none}#main-nav,#main-menu,#main-menu a,#main-menu a:visited,#main-menu a:hover,#main-menu li,.memdoc,dl.reflist dd,div.toc li,.ah,span.lineno,span.lineno a,span.lineno a:hover,.note code,.pre code,.post code,.invariant code,.warning code,.attention code,.deprecated code,.bug code,.todo code,.test code,.doxtable code{background:none}#titlearea,.footer,.contents,div.header,.memdoc,table.doxtable td,table.doxtable th,hr,.memSeparator{border:none}#main-menu a,#main-menu a:visited,#main-menu a:hover,#main-menu li,.reflist dt a.el,.levels span,.directory .levels span{text-shadow:none}.memdoc,dl.reflist dd{box-shadow:none}div.headertitle,.note code,.pre code,.post code,.invariant code,.warning code,.attention code,.deprecated code,.bug code,.todo code,.test code,table.doxtable code{padding:0}#nav-path,.directory .levels,span.lineno{display:none}html,#titlearea,.footer,tr.even,.directory tr.even,.doxtable tr:nth-child(even),.mdescLeft,.mdescRight,.memItemLeft,.memItemRight,code{background:#f2f2f2}body{color:#4d4d4d}h1,h2,h2.groupheader,h3,div.toc h3,h4,h5,h6,strong,em{color:#1a1a1a;border-bottom:none}h1{padding-top:.5em;font-size:180%}h2{padding-top:.5em;margin-bottom:0;font-size:140%}h3{padding-top:.5em;margin-bottom:0;font-size:110%}.glfwheader{font-size:16px;height:64px;max-width:920px;min-width:800px;padding:0 32px;margin:0 auto}#glfwhome{line-height:64px;padding-right:48px;color:#666;font-size:2.5em;background:url("http://www.glfw.org/css/arrow.png") no-repeat right}.glfwnavbar{list-style-type:none;margin:0 auto;float:right}#glfwhome,.glfwnavbar li{float:left}.glfwnavbar a,.glfwnavbar a:visited{line-height:64px;margin-left:2em;display:block;color:#666}#glfwhome,.glfwnavbar a,.glfwnavbar a:visited{transition:.35s ease}#titlearea,.footer{color:#666}address.footer{text-align:center;padding:2em;margin-top:3em}#top{background:#666}#main-nav{max-width:960px;min-width:800px;margin:0 auto;font-size:13px}#main-menu{max-width:920px;min-width:800px;margin:0 auto;font-size:13px}.memtitle{display:none}.memproto,.memname{font-weight:bold;text-shadow:none}#main-menu{height:36px;display:block;position:relative}#main-menu a,#main-menu a:visited,#main-menu a:hover,#main-menu li{color:#f2f2f2}#main-menu li ul.sm-nowrap li a{color:#4d4d4d}#main-menu li ul.sm-nowrap li a:hover{color:#f60}.contents{min-height:590px}div.contents,div.header{max-width:920px;margin:0 auto;padding:0 32px;background:#fff none}table.doxtable th,dl.reflist dt{background:linear-gradient(to bottom, #ffa733 0, #f60 100%);box-shadow:inset 0 0 32px #f60;text-shadow:0 -1px 1px #b34700;text-align:left;color:#fff}dl.reflist dt a.el{color:#f60;padding:.2em;border-radius:4px;background-color:#ffe0cc}div.toc{float:none;width:auto}div.toc h3{font-size:1.17em}div.toc ul{padding-left:1.5em}div.toc li{font-size:1em;padding-left:0;list-style-type:disc}div.toc,.memproto,div.qindex,div.ah{background:linear-gradient(to bottom, #f2f2f2 0, #e6e6e6 100%);box-shadow:inset 0 0 32px #e6e6e6;text-shadow:0 1px 1px #fff;color:#1a1a1a;border:2px solid #e6e6e6;border-radius:4px}.paramname{color:#803300}dl.reflist dt{border:2px solid #f60;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom:none}dl.reflist dd{border:2px solid #f60;border-bottom-right-radius:4px;border-bottom-left-radius:4px;border-top:none}table.doxtable{border-collapse:inherit;border-spacing:0;border:2px solid #f60;border-radius:4px}a,a:hover,a:visited,a:visited:hover,.contents a:visited,.el,a.el:visited,#glfwhome:hover,#main-menu a:hover,span.lineno a:hover{color:#f60;text-decoration:none}div.directory{border-collapse:inherit;border-spacing:0;border:2px solid #f60;border-radius:4px}hr,.memSeparator{height:2px;background:linear-gradient(to right, #f2f2f2 0, #d9d9d9 50%, #f2f2f2 100%)}dl.note,dl.pre,dl.post,dl.invariant{background:linear-gradient(to bottom, #ddfad1 0, #cbf7ba 100%);box-shadow:inset 0 0 32px #baf5a3;color:#1e5309;border:2px solid #afe599}dl.warning,dl.attention{background:linear-gradient(to bottom, #fae8d1 0, #f7ddba 100%);box-shadow:inset 0 0 32px #f5d1a3;color:#533309;border:2px solid #e5c499}dl.deprecated,dl.bug{background:linear-gradient(to bottom, #fad1e3 0, #f7bad6 100%);box-shadow:inset 0 0 32px #f5a3c8;color:#53092a;border:2px solid #e599bb}dl.todo,dl.test{background:linear-gradient(to bottom, #d1ecfa 0, #bae3f7 100%);box-shadow:inset 0 0 32px #a3daf5;color:#093a53;border:2px solid #99cce5}dl.note,dl.pre,dl.post,dl.invariant,dl.warning,dl.attention,dl.deprecated,dl.bug,dl.todo,dl.test{border-radius:4px;padding:1em;text-shadow:0 1px 1px #fff;margin:1em 0}.note a,.pre a,.post a,.invariant a,.warning a,.attention a,.deprecated a,.bug a,.todo a,.test a,.note a:visited,.pre a:visited,.post a:visited,.invariant a:visited,.warning a:visited,.attention a:visited,.deprecated a:visited,.bug a:visited,.todo a:visited,.test a:visited{color:inherit}div.line{line-height:inherit}div.fragment,pre.fragment{background:#f2f2f2;border-radius:4px;border:none;padding:1em;overflow:auto;border-left:4px solid #ccc;margin:1em 0}.lineno a,.lineno a:visited,.line,pre.fragment{color:#4d4d4d}span.preprocessor,span.comment{color:#007899}a.code,a.code:visited{color:#e64500}span.keyword,span.keywordtype,span.keywordflow{color:#404040;font-weight:bold}span.stringliteral{color:#360099}code{padding:.1em;border-radius:4px} diff --git a/docs/extra.less b/docs/extra.less index 9221459a..53e94f75 100644 --- a/docs/extra.less +++ b/docs/extra.less @@ -81,12 +81,29 @@ text-shadow:none; } +.sm-dox a span.sub-arrow { + border-color:@navbar-link-color transparent transparent transparent; +} + +.sm-dox a span.sub-arrow:active,.sm-dox a span.sub-arrow:focus,.sm-dox a span.sub-arrow:hover,.sm-dox a:hover span.sub-arrow { + border-color:@default-link-color transparent transparent transparent; +} + +.sm-dox ul a span.sub-arrow:active,.sm-dox ul a span.sub-arrow:focus,.sm-dox ul a span.sub-arrow:hover,.sm-dox ul a:hover span.sub-arrow { + border-color:transparent transparent transparent @default-link-color; +} + .sm-dox ul a:hover { background:@header-footer-link-color; text-shadow:none; } -#main-nav,#navrow1,#navrow2,#navrow3,#navrow4,.tablist a,.tablist a:visited,.tablist a:hover,.tablist li,.tablist li.current a,.memdoc,dl.reflist dd,div.toc li,.ah,span.lineno,span.lineno a,span.lineno a:hover,.note code,.pre code,.post code,.invariant code,.warning code,.attention code,.deprecated code,.bug code,.todo code,.test code,.doxtable code { +.sm-dox ul.sm-nowrap a { + color:@default-text-color; + text-shadow:none; +} + +#main-nav,#main-menu,#main-menu a,#main-menu a:visited,#main-menu a:hover,#main-menu li,.memdoc,dl.reflist dd,div.toc li,.ah,span.lineno,span.lineno a,span.lineno a:hover,.note code,.pre code,.post code,.invariant code,.warning code,.attention code,.deprecated code,.bug code,.todo code,.test code,.doxtable code { background:none; } @@ -94,7 +111,7 @@ border:none; } -.tablist a,.tablist a:visited,.tablist a:hover,.tablist li,.tablist li.current a,.reflist dt a.el,.levels span,.directory .levels span { +#main-menu a,#main-menu a:visited,#main-menu a:hover,#main-menu li,.reflist dt a.el,.levels span,.directory .levels span { text-shadow:none; } @@ -199,7 +216,7 @@ address.footer { font-size:13px; } -#navrow1,#navrow2,#navrow3,#navrow4 { +#main-menu { max-width:920px; min-width:800px; margin:0 auto; @@ -215,21 +232,22 @@ address.footer { text-shadow:none; } -.tablist { +#main-menu { height:36px; display:block; position:relative; } -.tablist a,.tablist a:visited,.tablist a:hover,.tablist li,.tablist li.current a { +#main-menu a,#main-menu a:visited,#main-menu a:hover,#main-menu li { color:@navbar-link-color; } -.tablist li.current a { - background:linear-gradient(to bottom,@tab-background-color2 0%,@tab-background-color1 100%); - box-shadow:inset 0 0 32px @tab-background-color1; - text-shadow:0 -1px 1px darken(@tab-background-color1, 15%); - color:@tab-text-color; +#main-menu li ul.sm-nowrap li a { + color:@default-text-color; +} + +#main-menu li ul.sm-nowrap li a:hover { + color:@default-link-color; } .contents { @@ -311,7 +329,7 @@ table.doxtable { border-radius:4px; } -a,a:hover,a:visited,a:visited:hover,.contents a:visited,.el,a.el:visited,#glfwhome:hover,.tablist a:hover,span.lineno a:hover { +a,a:hover,a:visited,a:visited:hover,.contents a:visited,.el,a.el:visited,#glfwhome:hover,#main-menu a:hover,span.lineno a:hover { color:@default-link-color; text-decoration:none; } diff --git a/docs/input.dox b/docs/input.dox index 6d444656..318d48d5 100644 --- a/docs/input.dox +++ b/docs/input.dox @@ -203,9 +203,9 @@ void character_callback(GLFWwindow* window, unsigned int codepoint) } @endcode -If you wish to receive even those Unicode code points generated with modifier -key combinations that a plain text field would ignore, or just want to know -exactly what modifier keys were used, set a character with modifiers callback. +If you also wish to receive those Unicode code points generated with modifier +key combinations that a plain text field would ignore, or want to know exactly +what modifier keys were used, set a character with modifiers callback. @code glfwSetCharModsCallback(window, charmods_callback); @@ -297,8 +297,8 @@ is provided normally via both the cursor position callback and through polling. other features of GLFW. It is not supported and will not work as robustly as `GLFW_CURSOR_DISABLED`. -If you just wish the cursor to become hidden when it is over a window, set -the cursor mode to `GLFW_CURSOR_HIDDEN`. +If you only wish the cursor to become hidden when it is over a window but still +want it to behave normally, set the cursor mode to `GLFW_CURSOR_HIDDEN`. @code glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); @@ -501,7 +501,7 @@ void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) } @endcode -A simple mouse wheel, being vertical, provides offsets along the Y-axis. +A normal mouse wheel, being vertical, provides offsets along the Y-axis. @section joystick Joystick input @@ -786,7 +786,6 @@ functions. There is also the special `platform` field that specifies which platform the mapping is valid for. Possible values are `Windows`, `Mac OS X` and `Linux`. -Mappings without this field will always be considered valid. Below is an example of what a gamepad mapping might look like. It is the one built into GLFW for Xbox controllers accessed via the XInput API on Windows. diff --git a/docs/intro.dox b/docs/intro.dox index 65aeef68..46a445aa 100644 --- a/docs/intro.dox +++ b/docs/intro.dox @@ -55,13 +55,13 @@ if (!glfwInit()) If any part of initialization fails, any parts that succeeded are terminated as if @ref glfwTerminate had been called. The library only needs to be initialized -once and additional calls to an already initialized library will simply return +once and additional calls to an already initialized library will return `GLFW_TRUE` immediately. Once the library has been successfully initialized, it should be terminated before the application exits. Modern systems are very good at freeing resources -allocated by programs that simply exit, but GLFW sometimes has to change global -system settings and these might not be restored without termination. +allocated by programs that exit, but GLFW sometimes has to change global system +settings and these might not be restored without termination. @subsection init_hints Initialization hints @@ -79,8 +79,8 @@ during initialization. Once GLFW has been initialized, any values you set will be ignored until the library is terminated and initialized again. Some hints are platform specific. These may be set on any platform but they -will only affect their specific platform. Other platforms will simply ignore -them. Setting these hints requires no platform specific headers or functions. +will only affect their specific platform. Other platforms will ignore them. +Setting these hints requires no platform specific headers or functions. @subsubsection init_hints_shared Shared init hints diff --git a/docs/monitor.dox b/docs/monitor.dox index 708461f5..620646d5 100644 --- a/docs/monitor.dox +++ b/docs/monitor.dox @@ -85,8 +85,8 @@ void monitor_callback(GLFWmonitor* monitor, int event) } @endcode -If a monitor is disconnected, any windows that are full screen on it get forced -into windowed mode. +If a monitor is disconnected, all windows that are full screen on it will be +switched to windowed mode. @section monitor_properties Monitor properties @@ -131,17 +131,34 @@ current _resolution_, i.e. the width and height of its current [video mode](@ref monitor_modes). @code -int widthMM, heightMM; -glfwGetMonitorPhysicalSize(monitor, &widthMM, &heightMM); +int width_mm, height_mm; +glfwGetMonitorPhysicalSize(monitor, &width_mm, &height_mm); @endcode -This can, for example, be used together with the current video mode to calculate -the DPI of a monitor. +While this can be used to calculate the raw DPI of a monitor, this is often not +useful. Instead use the [monitor content scale](@ref monitor_scale) and +[window content scale](@ref window_scale) to scale your content. + + +@subsection monitor_scale Content scale + +The content scale for a monitor can be retrieved with @ref +glfwGetMonitorContentScale. @code -const double dpi = mode->width / (widthMM / 25.4); +float xscale, yscale; +glfwGetMonitorContentScale(monitor, &xscale, &yscale); @endcode +The content scale is the ratio between the current DPI and the platform's +default DPI. If you scale all pixel dimensions by this scale then your content +should appear at an appropriate size. This is especially important for text and +any UI elements. + +The content scale may depend on both the monitor resolution and pixel density +and on user settings. It may be very different from the raw DPI calculated from +the physical size and current resolution. + @subsection monitor_pos Virtual position diff --git a/docs/moving.dox b/docs/moving.dox index af3da61c..0f33a7e5 100644 --- a/docs/moving.dox +++ b/docs/moving.dox @@ -221,7 +221,7 @@ GLFW 2, windows and contexts created with GLFW 3 will never be destroyed unless you choose them to be. Each window now has a close flag that is set to `GLFW_TRUE` when the user attempts to close that window. By default, nothing else happens and the window stays visible. It is then up to you to either destroy -the window, take some other action or simply ignore the request. +the window, take some other action or ignore the request. You can query the close flag at any time with @ref glfwWindowShouldClose and set it at any time with @ref glfwSetWindowShouldClose. diff --git a/docs/news.dox b/docs/news.dox index 86b68c41..03a9c8dc 100644 --- a/docs/news.dox +++ b/docs/news.dox @@ -58,6 +58,15 @@ windows with @ref glfwSetWindowAttrib. @see @ref window_attribs +@subsection news_33_contentscale Content scale queries for DPI-aware rendering + +GLFW now supports querying the window and monitor content scale, i.e. the ratio +between the current DPI and the platform's default DPI, with @ref +glfwGetWindowContentScale and @ref glfwGetMonitorContentScale. + +@see @ref window_scale + + @subsection news_33_inithint Support for initialization hints GLFW now supports setting library initialization hints with @ref glfwInitHint diff --git a/docs/quick.dox b/docs/quick.dox index dc1a2f8d..ff2c6f06 100644 --- a/docs/quick.dox +++ b/docs/quick.dox @@ -69,7 +69,7 @@ if (!glfwInit()) } @endcode -Note that `GLFW_TRUE` and `GLFW_FALSE` are and will always be just one and zero. +Note that `GLFW_TRUE` and `GLFW_FALSE` are and will always be one and zero. When you are done using GLFW, typically just before the application exits, you need to terminate GLFW. @@ -86,14 +86,12 @@ functions that require it. @subsection quick_capture_error Setting an error callback Most events are reported through callbacks, whether it's a key being pressed, -a GLFW window being moved, or an error occurring. Callbacks are simply -C functions (or C++ static methods) that are called by GLFW with arguments -describing the event. +a GLFW window being moved, or an error occurring. Callbacks are C functions (or +C++ static methods) that are called by GLFW with arguments describing the event. In case a GLFW function fails, an error is reported to the GLFW error callback. You can receive these reports with an error callback. This function must have -the signature below. This simple error callback just prints the error -description to `stderr`. +the signature below but may do anything permitted in other callbacks. @code void error_callback(int error, const char* description) @@ -249,7 +247,7 @@ You can also set a framebuffer size callback using @ref glfwSetFramebufferSizeCallback and be notified when the size changes. Actual rendering with OpenGL is outside the scope of this tutorial, but there -are [many](https://open.gl/) [excellent](http://learnopengl.com/) +are [many](https://open.gl/) [excellent](https://learnopengl.com/) [tutorial](http://openglbook.com/) [sites](http://ogldev.atspace.co.uk/) that teach modern OpenGL. Some of them use GLFW to create the context and window while others use GLUT or SDL, but remember that OpenGL itself always works the diff --git a/docs/window.dox b/docs/window.dox index 5dbf15bf..3e8c748f 100644 --- a/docs/window.dox +++ b/docs/window.dox @@ -77,7 +77,7 @@ GLFWvidmode.blueBits | @ref GLFW_BLUE_BITS hint GLFWvidmode.refreshRate | @ref GLFW_REFRESH_RATE hint Once you have a full screen window, you can change its resolution, refresh rate -and monitor with @ref glfwSetWindowMonitor. If you just need change its +and monitor with @ref glfwSetWindowMonitor. If you only need change its resolution you can also call @ref glfwSetWindowSize. In all cases, the new video mode will be selected the same way as the video mode chosen by @ref glfwCreateWindow. If the window has an OpenGL or OpenGL ES context, it will be @@ -87,10 +87,10 @@ By default, the original video mode of the monitor will be restored and the window iconified if it loses input focus, to allow the user to switch back to the desktop. This behavior can be disabled with the [GLFW_AUTO_ICONIFY](@ref GLFW_AUTO_ICONIFY_hint) window hint, for example if you -wish to simultaneously cover multiple windows with full screen windows. +wish to simultaneously cover multiple monitors with full screen windows. -If a monitor is disconnected, any window that is full screen on that monitor -will be forced into windowed mode. See @ref monitor_event for more information. +If a monitor is disconnected, all windows that are full screen on that monitor +will be switched to windowed mode. See @ref monitor_event for more information. @subsubsection window_windowed_full_screen "Windowed full screen" windows @@ -99,7 +99,7 @@ If the closest match for the desired video mode is the current one, the video mode will not be changed, making window creation faster and application switching much smoother. This is sometimes called _windowed full screen_ or _borderless full screen_ window and counts as a full screen window. To create -such a window, simply request the current video mode. +such a window, request the current video mode. @code const GLFWvidmode* mode = glfwGetVideoMode(monitor); @@ -151,8 +151,8 @@ and reset all at once to their defaults with @ref glfwDefaultWindowHints. Some hints are platform specific. These are always valid to set on any platform but they will only affect their specific platform. Other platforms -will simply ignore them. Setting these hints requires no platform specific -headers or calls. +will ignore them. Setting these hints requires no platform specific headers or +calls. Note that hints need to be set _before_ the creation of the window and context you wish to have the specified attributes. @@ -582,15 +582,15 @@ window's desired video mode. The video mode most closely matching the new desired video mode is set immediately. The window is resized to fit the resolution of the set video mode. -If you wish to be notified when a window is resized, whether by the user or -the system, set a size callback. +If you wish to be notified when a window is resized, whether by the user, the +system or your own code, set a size callback. @code glfwSetWindowSizeCallback(window, window_size_callback); @endcode The callback function receives the new size, in screen coordinates, of the -client area of the window when it is resized. +client area of the window when the window is resized. @code void window_size_callback(GLFWwindow* window, int width, int height) @@ -663,6 +663,26 @@ The size of a framebuffer may change independently of the size of a window, for example if the window is dragged between a regular monitor and a high-DPI one. +@subsection window_scale Window content scale + +The content scale for a window can be retrieved with @ref +glfwGetWindowContentScale. + +@code +float xscale, yscale; +glfwGetWindowContentScale(window, &xscale, &yscale); +@endcode + +The content scale of a window is the ratio between the current DPI and the +platform's default DPI. If you scale all pixel dimensions by this scale then +your content should appear at an appropriate size. This is especially important +for text and any UI elements. + +On systems where each monitors can have its own content scale, the window +content scale will depend on which monitor the system considers the window to be +on. + + @subsection window_sizelimits Window size limits The minimum and maximum size of the client area of a windowed mode window can be @@ -694,7 +714,7 @@ glfwSetWindowAspectRatio(window, 16, 9); The aspect ratio is specified as a numerator and denominator, corresponding to the width and height, respectively. If you want a window to maintain its -current aspect ratio, simply use its current size as the ratio. +current aspect ratio, use its current size as the ratio. @code int width, height; @@ -720,15 +740,15 @@ The window system may put limitations on window placement. glfwSetWindowPos(window, 100, 100); @endcode -If you wish to be notified when a window is moved, whether by the user, system -or your own code, set a position callback. +If you wish to be notified when a window is moved, whether by the user, the +system or your own code, set a position callback. @code glfwSetWindowPosCallback(window, window_pos_callback); @endcode -The callback function receives the new position of the upper-left corner of the -client area when the window is moved. +The callback function receives the new position, in screen coordinates, of the +upper-left corner of the client area when the window is moved. @code void window_pos_callback(GLFWwindow* window, int xpos, int ypos) diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index 673884ff..30ac82dc 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -274,14 +274,14 @@ extern "C" { /*! @brief One. * * One. Seriously. You don't _need_ to use this symbol in your code. It's - * just semantic sugar for the number 1. You can use `1` or `true` or `_True` + * semantic sugar for the number 1. You can also use `1` or `true` or `_True` * or `GL_TRUE` or whatever you want. */ #define GLFW_TRUE 1 /*! @brief Zero. * * Zero. Seriously. You don't _need_ to use this symbol in your code. It's - * just just semantic sugar for the number 0. You can use `0` or `false` or + * semantic sugar for the number 0. You can also use `0` or `false` or * `_False` or `GL_FALSE` or whatever you want. */ #define GLFW_FALSE 0 @@ -1640,9 +1640,8 @@ GLFWAPI void glfwTerminate(void); * again. * * Some hints are platform specific. These may be set on any platform but they - * will only affect their specific platform. Other platforms will simply - * ignore them. Setting these hints requires no platform specific headers or - * functions. + * will only affect their specific platform. Other platforms will ignore them. + * Setting these hints requires no platform specific headers or functions. * * @param[in] hint The [init hint](@ref init_hints) to set. * @param[in] value The new value of the init hint. @@ -1675,9 +1674,8 @@ GLFWAPI void glfwInitHint(int hint, int value); * again. * * Some hints are platform specific. These may be set on any platform but they - * will only affect their specific platform. Other platforms will simply - * ignore them. Setting these hints requires no platform specific headers or - * functions. + * will only affect their specific platform. Other platforms will ignore them. + * Setting these hints requires no platform specific headers or functions. * * @param[in] hint The [init hint](@ref init_hints) to set. * @param[in] value The new value of the init hint. @@ -1939,6 +1937,36 @@ GLFWAPI void glfwGetMonitorPos(GLFWmonitor* monitor, int* xpos, int* ypos); */ GLFWAPI void glfwGetMonitorPhysicalSize(GLFWmonitor* monitor, int* widthMM, int* heightMM); +/*! @brief Retrieves the content scale for the specified monitor. + * + * This function retrieves the content scale for the specified monitor. The + * content scale is the ratio between the current DPI and the platform's + * default DPI. If you scale all pixel dimensions by this scale then your + * content should appear at an appropriate size. This is especially important + * for text and any UI elements. + * + * The content scale may depend on both the monitor resolution and pixel + * density and on user settings. It may be very different from the raw DPI + * calculated from the physical size and current resolution. + * + * @param[in] monitor The monitor to query. + * @param[out] xscale Where to store the x-axis content scale, or `NULL`. + * @param[out] yscale Where to store the y-axis content scale, or `NULL`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref monitor_scale + * @sa @ref glfwGetWindowContentScale + * + * @since Added in version 3.3. + * + * @ingroup monitor + */ +GLFWAPI void glfwGetMonitorContentScale(GLFWmonitor* monitor, float* xscale, float* yscale); + /*! @brief Returns the name of the specified monitor. * * This function returns a human-readable name, encoded as UTF-8, of the @@ -2813,6 +2841,36 @@ GLFWAPI void glfwGetFramebufferSize(GLFWwindow* window, int* width, int* height) */ GLFWAPI void glfwGetWindowFrameSize(GLFWwindow* window, int* left, int* top, int* right, int* bottom); +/*! @brief Retrieves the content scale for the specified window. + * + * This function retrieves the content scale for the specified window. The + * content scale is the ratio between the current DPI and the platform's + * default DPI. If you scale all pixel dimensions by this scale then your + * content should appear at an appropriate size. This is especially important + * for text and any UI elements. + * + * On systems where each monitors can have its own content scale, the window + * content scale will depend on which monitor the system considers the window + * to be on. + * + * @param[in] window The window to query. + * @param[out] xscale Where to store the x-axis content scale, or `NULL`. + * @param[out] yscale Where to store the y-axis content scale, or `NULL`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_scale + * @sa @ref glfwGetMonitorContentScale + * + * @since Added in version 3.3. + * + * @ingroup window + */ +GLFWAPI void glfwGetWindowContentScale(GLFWwindow* window, float* xscale, float* yscale); + /*! @brief Iconifies the specified window. * * This function iconifies (minimizes) the specified window if it was @@ -3199,8 +3257,9 @@ GLFWAPI void* glfwGetWindowUserPointer(GLFWwindow* window); /*! @brief Sets the position callback for the specified window. * * This function sets the position callback of the specified window, which is - * called when the window is moved. The callback is provided with the screen - * position of the upper-left corner of the client area of the window. + * called when the window is moved. The callback is provided with the + * position, in screen coordinates, of the upper-left corner of the client area + * of the window. * * @param[in] window The window whose callback to set. * @param[in] cbfun The new callback, or `NULL` to remove the currently set diff --git a/src/cocoa_monitor.m b/src/cocoa_monitor.m index 6020599f..6108342c 100644 --- a/src/cocoa_monitor.m +++ b/src/cocoa_monitor.m @@ -229,6 +229,9 @@ void _glfwPollMonitorsNS(void) displays = calloc(displayCount, sizeof(CGDirectDisplayID)); CGGetOnlineDisplayList(displayCount, displays, &displayCount); + for (i = 0; i < _glfw.monitorCount; i++) + _glfw.monitors[i]->ns.screen = nil; + disconnectedCount = _glfw.monitorCount; if (disconnectedCount) { @@ -371,6 +374,48 @@ void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) *ypos = (int) bounds.origin.y; } +void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, + float* xscale, float* yscale) +{ + if (!monitor->ns.screen) + { + NSUInteger i; + NSArray* screens = [NSScreen screens]; + + for (i = 0; i < [screens count]; i++) + { + NSScreen* screen = [screens objectAtIndex:i]; + NSNumber* displayID = + [[screen deviceDescription] objectForKey:@"NSScreenNumber"]; + + // HACK: Compare unit numbers instead of display IDs to work around + // display replacement on machines with automatic graphics + // switching + if (monitor->ns.unitNumber == + CGDisplayUnitNumber([displayID unsignedIntValue])) + { + monitor->ns.screen = screen; + break; + } + } + + if (i == [screens count]) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Cocoa: Failed to find a screen for monitor"); + return; + } + } + + const NSRect points = [monitor->ns.screen frame]; + const NSRect pixels = [monitor->ns.screen convertRectToBacking:points]; + + if (xscale) + *xscale = (float) (pixels.size.width / points.size.width); + if (yscale) + *yscale = (float) (pixels.size.height / points.size.height); +} + GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count) { CFArrayRef modes; diff --git a/src/cocoa_platform.h b/src/cocoa_platform.h index f0ba4e85..61d0ee91 100644 --- a/src/cocoa_platform.h +++ b/src/cocoa_platform.h @@ -135,6 +135,7 @@ typedef struct _GLFWmonitorNS CGDirectDisplayID displayID; CGDisplayModeRef previousMode; uint32_t unitNumber; + id screen; } _GLFWmonitorNS; diff --git a/src/cocoa_window.m b/src/cocoa_window.m index 0a3bcd54..1ee85bc6 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -1288,6 +1288,18 @@ void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, *bottom = contentRect.origin.y - frameRect.origin.y; } +void _glfwPlatformGetWindowContentScale(_GLFWwindow* window, + float* xscale, float* yscale) +{ + const NSRect points = [window->ns.view frame]; + const NSRect pixels = [window->ns.view convertRectToBacking:points]; + + if (xscale) + *xscale = (float) (pixels.size.width / points.size.width); + if (yscale) + *yscale = (float) (pixels.size.height / points.size.height); +} + void _glfwPlatformIconifyWindow(_GLFWwindow* window) { [window->ns.object miniaturize:nil]; @@ -1363,7 +1375,7 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, if (window->monitor) releaseMonitor(window); - _glfwInputWindowMonitorChange(window, monitor); + _glfwInputWindowMonitor(window, monitor); // HACK: Allow the state cached in Cocoa to catch up to reality // TODO: Solve this in a less terrible way diff --git a/src/internal.h b/src/internal.h index d83bf5ae..1320539d 100644 --- a/src/internal.h +++ b/src/internal.h @@ -642,6 +642,7 @@ const char* _glfwPlatformGetScancodeName(int scancode); int _glfwPlatformGetKeyScancode(int key); void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos); +void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, float* xscale, float* yscale); GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count); void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode); void _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp); @@ -671,6 +672,7 @@ void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, int minwidth, int min void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom); void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height); void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, int* left, int* top, int* right, int* bottom); +void _glfwPlatformGetWindowContentScale(_GLFWwindow* window, float* xscale, float* yscale); void _glfwPlatformIconifyWindow(_GLFWwindow* window); void _glfwPlatformRestoreWindow(_GLFWwindow* window); void _glfwPlatformMaximizeWindow(_GLFWwindow* window); @@ -778,7 +780,7 @@ void _glfwInputWindowCloseRequest(_GLFWwindow* window); * @param[in] monitor The new desired monitor, or `NULL`. * @ingroup event */ -void _glfwInputWindowMonitorChange(_GLFWwindow* window, _GLFWmonitor* monitor); +void _glfwInputWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor); /*! @brief Notifies shared code of a physical key event. * @param[in] window The window that received the event. diff --git a/src/mir_monitor.c b/src/mir_monitor.c index 26848506..b300cac0 100644 --- a/src/mir_monitor.c +++ b/src/mir_monitor.c @@ -88,6 +88,15 @@ void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) *ypos = monitor->mir.y; } +void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, + float* xscale, float* yscale) +{ + if (xscale) + *xscale = 1.f; + if (yscale) + *yscale = 1.f; +} + static void FillInRGBBitsFromPixelFormat(GLFWvidmode* mode, const MirPixelFormat pf) { switch (pf) diff --git a/src/mir_window.c b/src/mir_window.c index 8b763de1..c752cdb4 100644 --- a/src/mir_window.c +++ b/src/mir_window.c @@ -515,6 +515,15 @@ void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height) *height = window->mir.height; } +void _glfwPlatformGetWindowContentScale(_GLFWwindow* window, + float* xscale, float* yscale) +{ + if (xscale) + *xscale = 1.f; + if (yscale) + *yscale = 1.f; +} + void _glfwPlatformIconifyWindow(_GLFWwindow* window) { MirWindowSpec* spec; diff --git a/src/monitor.c b/src/monitor.c index 4acecd58..0dfb1958 100644 --- a/src/monitor.c +++ b/src/monitor.c @@ -107,6 +107,17 @@ void _glfwInputMonitor(_GLFWmonitor* monitor, int action, int placement) else if (action == GLFW_DISCONNECTED) { int i; + _GLFWwindow* window; + + for (window = _glfw.windowListHead; window; window = window->next) + { + if (window->monitor == monitor) + { + int width, height; + _glfwPlatformGetWindowSize(window, &width, &height); + _glfwPlatformSetWindowMonitor(window, NULL, 0, 0, width, height, 0); + } + } for (i = 0; i < _glfw.monitorCount; i++) { @@ -314,6 +325,21 @@ GLFWAPI void glfwGetMonitorPhysicalSize(GLFWmonitor* handle, int* widthMM, int* *heightMM = monitor->heightMM; } +GLFWAPI void glfwGetMonitorContentScale(GLFWmonitor* handle, + float* xscale, float* yscale) +{ + _GLFWmonitor* monitor = (_GLFWmonitor*) handle; + assert(monitor != NULL); + + if (xscale) + *xscale = 0.f; + if (yscale) + *yscale = 0.f; + + _GLFW_REQUIRE_INIT(); + _glfwPlatformGetMonitorContentScale(monitor, xscale, yscale); +} + GLFWAPI const char* glfwGetMonitorName(GLFWmonitor* handle) { _GLFWmonitor* monitor = (_GLFWmonitor*) handle; diff --git a/src/null_monitor.c b/src/null_monitor.c index f5678bbc..007dd1aa 100644 --- a/src/null_monitor.c +++ b/src/null_monitor.c @@ -36,6 +36,15 @@ void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) { } +void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, + float* xscale, float* yscale) +{ + if (xscale) + *xscale = 1.f; + if (yscale) + *yscale = 1.f; +} + GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* found) { return NULL; diff --git a/src/null_window.c b/src/null_window.c index 33ff6c33..3cc3905d 100644 --- a/src/null_window.c +++ b/src/null_window.c @@ -139,6 +139,15 @@ void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, { } +void _glfwPlatformGetWindowContentScale(_GLFWwindow* window, + float* xscale, float* yscale) +{ + if (xscale) + *xscale = 1.f; + if (yscale) + *yscale = 1.f; +} + void _glfwPlatformIconifyWindow(_GLFWwindow* window) { } diff --git a/src/win32_init.c b/src/win32_init.c index 0531ff13..ee7ccfda 100644 --- a/src/win32_init.c +++ b/src/win32_init.c @@ -151,6 +151,8 @@ static GLFWbool loadLibraries(void) { _glfw.win32.shcore.SetProcessDpiAwareness_ = (PFN_SetProcessDpiAwareness) GetProcAddress(_glfw.win32.shcore.instance, "SetProcessDpiAwareness"); + _glfw.win32.shcore.GetDpiForMonitor_ = (PFN_GetDpiForMonitor) + GetProcAddress(_glfw.win32.shcore.instance, "GetDpiForMonitor"); } return GLFW_TRUE; diff --git a/src/win32_monitor.c b/src/win32_monitor.c index 76fbc0d8..74dd8252 100644 --- a/src/win32_monitor.c +++ b/src/win32_monitor.c @@ -60,6 +60,7 @@ static _GLFWmonitor* createMonitor(DISPLAY_DEVICEW* adapter, DISPLAY_DEVICEW* display) { _GLFWmonitor* monitor; + int widthMM, heightMM; char* name; HDC dc; DEVMODEW dm; @@ -72,13 +73,26 @@ static _GLFWmonitor* createMonitor(DISPLAY_DEVICEW* adapter, if (!name) return NULL; + ZeroMemory(&dm, sizeof(dm)); + dm.dmSize = sizeof(dm); + EnumDisplaySettingsW(adapter->DeviceName, ENUM_CURRENT_SETTINGS, &dm); + dc = CreateDCW(L"DISPLAY", adapter->DeviceName, NULL, NULL); - monitor = _glfwAllocMonitor(name, - GetDeviceCaps(dc, HORZSIZE), - GetDeviceCaps(dc, VERTSIZE)); + if (IsWindows8Point1OrGreater()) + { + widthMM = GetDeviceCaps(dc, HORZSIZE); + heightMM = GetDeviceCaps(dc, VERTSIZE); + } + else + { + widthMM = (int) (dm.dmPelsWidth * 25.4f / GetDeviceCaps(dc, LOGPIXELSX)); + heightMM = (int) (dm.dmPelsHeight * 25.4f / GetDeviceCaps(dc, LOGPIXELSY)); + } DeleteDC(dc); + + monitor = _glfwAllocMonitor(name, widthMM, heightMM); free(name); if (adapter->StateFlags & DISPLAY_DEVICE_MODESPRUNED) @@ -101,10 +115,6 @@ static _GLFWmonitor* createMonitor(DISPLAY_DEVICEW* adapter, NULL, NULL); } - ZeroMemory(&dm, sizeof(dm)); - dm.dmSize = sizeof(dm); - EnumDisplaySettingsW(adapter->DeviceName, ENUM_CURRENT_SETTINGS, &dm); - rect.left = dm.dmPosition.x; rect.top = dm.dmPosition.y; rect.right = dm.dmPosition.x + dm.dmPelsWidth; @@ -302,6 +312,26 @@ void _glfwRestoreVideoModeWin32(_GLFWmonitor* monitor) } } +void _glfwGetMonitorContentScaleWin32(HMONITOR handle, float* xscale, float* yscale) +{ + UINT xdpi, ydpi; + + if (IsWindows8Point1OrGreater()) + GetDpiForMonitor(handle, MDT_EFFECTIVE_DPI, &xdpi, &ydpi); + else + { + const HDC dc = GetDC(NULL); + xdpi = GetDeviceCaps(dc, LOGPIXELSX); + ydpi = GetDeviceCaps(dc, LOGPIXELSY); + ReleaseDC(NULL, dc); + } + + if (xscale) + *xscale = xdpi / 96.f; + if (yscale) + *yscale = ydpi / 96.f; +} + ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// @@ -324,6 +354,12 @@ void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) *ypos = dm.dmPosition.y; } +void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, + float* xscale, float* yscale) +{ + _glfwGetMonitorContentScaleWin32(monitor->win32.handle, xscale, yscale); +} + GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count) { int modeIndex = 0, size = 0; diff --git a/src/win32_platform.h b/src/win32_platform.h index acbb5ca2..1bfc638f 100644 --- a/src/win32_platform.h +++ b/src/win32_platform.h @@ -135,6 +135,13 @@ typedef enum PROCESS_DPI_AWARENESS PROCESS_SYSTEM_DPI_AWARE = 1, PROCESS_PER_MONITOR_DPI_AWARE = 2 } PROCESS_DPI_AWARENESS; +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 /*DPI_ENUMS_DECLARED*/ // HACK: Define versionhelpers.h functions manually as MinGW lacks the header @@ -216,7 +223,9 @@ typedef HRESULT(WINAPI * PFN_DwmEnableBlurBehindWindow)(HWND,const DWM_BLURBEHIN // shcore.dll function pointer typedefs typedef HRESULT (WINAPI * PFN_SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS); +typedef HRESULT (WINAPI * PFN_GetDpiForMonitor)(HMONITOR,MONITOR_DPI_TYPE,UINT*,UINT*); #define SetProcessDpiAwareness _glfw.win32.shcore.SetProcessDpiAwareness_ +#define GetDpiForMonitor _glfw.win32.shcore.GetDpiForMonitor_ typedef VkFlags VkWin32SurfaceCreateFlagsKHR; @@ -326,6 +335,7 @@ typedef struct _GLFWlibraryWin32 struct { HINSTANCE instance; PFN_SetProcessDpiAwareness SetProcessDpiAwareness_; + PFN_GetDpiForMonitor GetDpiForMonitor_; } shcore; } _GLFWlibraryWin32; @@ -395,4 +405,5 @@ void _glfwInitTimerWin32(void); void _glfwPollMonitorsWin32(void); GLFWbool _glfwSetVideoModeWin32(_GLFWmonitor* monitor, const GLFWvidmode* desired); void _glfwRestoreVideoModeWin32(_GLFWmonitor* monitor); +void _glfwGetMonitorContentScaleWin32(HMONITOR handle, float* xscale, float* yscale); diff --git a/src/win32_window.c b/src/win32_window.c index 7de46e28..e30a3341 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -951,6 +951,22 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, mmi->ptMaxTrackSize.y = window->maxheight + yoff; } + if (!window->decorated) + { + MONITORINFO mi; + const HMONITOR mh = MonitorFromWindow(window->win32.handle, + MONITOR_DEFAULTTONEAREST); + + ZeroMemory(&mi, sizeof(mi)); + mi.cbSize = sizeof(mi); + GetMonitorInfo(mh, &mi); + + mmi->ptMaxPosition.x = mi.rcWork.left - mi.rcMonitor.left; + mmi->ptMaxPosition.y = mi.rcWork.top - mi.rcMonitor.top; + mmi->ptMaxSize.x = mi.rcWork.right - mi.rcWork.left; + mmi->ptMaxSize.y = mi.rcWork.bottom - mi.rcWork.top; + } + return 0; } @@ -983,19 +999,6 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, break; } - case WM_DPICHANGED: - { - RECT* rect = (RECT*) lParam; - SetWindowPos(window->win32.handle, - HWND_TOP, - rect->left, - rect->top, - rect->right - rect->left, - rect->bottom - rect->top, - SWP_NOACTIVATE | SWP_NOZORDER); - break; - } - case WM_DROPFILES: { HDROP drop = (HDROP) wParam; @@ -1415,6 +1418,14 @@ void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, *bottom = rect.bottom - height; } +void _glfwPlatformGetWindowContentScale(_GLFWwindow* window, + float* xscale, float* yscale) +{ + const HANDLE handle = MonitorFromWindow(window->win32.handle, + MONITOR_DEFAULTTONEAREST); + _glfwGetMonitorContentScaleWin32(handle, xscale, yscale); +} + void _glfwPlatformIconifyWindow(_GLFWwindow* window) { ShowWindow(window->win32.handle, SW_MINIMIZE); @@ -1482,7 +1493,7 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, if (window->monitor) releaseMonitor(window); - _glfwInputWindowMonitorChange(window, monitor); + _glfwInputWindowMonitor(window, monitor); if (monitor) { diff --git a/src/window.c b/src/window.c index 201e9a8a..391d02b2 100644 --- a/src/window.c +++ b/src/window.c @@ -108,7 +108,7 @@ void _glfwInputWindowCloseRequest(_GLFWwindow* window) window->callbacks.close((GLFWwindow*) window); } -void _glfwInputWindowMonitorChange(_GLFWwindow* window, _GLFWmonitor* monitor) +void _glfwInputWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor) { window->monitor = monitor; } @@ -245,12 +245,13 @@ void glfwDefaultWindowHints(void) // The default is a focused, visible, resizable window with decorations memset(&_glfw.hints.window, 0, sizeof(_glfw.hints.window)); - _glfw.hints.window.resizable = GLFW_TRUE; - _glfw.hints.window.visible = GLFW_TRUE; - _glfw.hints.window.decorated = GLFW_TRUE; - _glfw.hints.window.focused = GLFW_TRUE; - _glfw.hints.window.autoIconify = GLFW_TRUE; - _glfw.hints.window.parent = NULL; + _glfw.hints.window.resizable = GLFW_TRUE; + _glfw.hints.window.visible = GLFW_TRUE; + _glfw.hints.window.decorated = GLFW_TRUE; + _glfw.hints.window.focused = GLFW_TRUE; + _glfw.hints.window.autoIconify = GLFW_TRUE; + _glfw.hints.window.centerCursor = GLFW_TRUE; + _glfw.hints.window.parent = NULL; // The default is 24 bits of color, 24 bits of depth and 8 bits of stencil, // double buffered @@ -650,6 +651,21 @@ GLFWAPI void glfwGetWindowFrameSize(GLFWwindow* handle, _glfwPlatformGetWindowFrameSize(window, left, top, right, bottom); } +GLFWAPI void glfwGetWindowContentScale(GLFWwindow* handle, + float* xscale, float* yscale) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + if (xscale) + *xscale = 0.f; + if (yscale) + *yscale = 0.f; + + _GLFW_REQUIRE_INIT(); + _glfwPlatformGetWindowContentScale(window, xscale, yscale); +} + GLFWAPI void glfwIconifyWindow(GLFWwindow* handle) { _GLFWwindow* window = (_GLFWwindow*) handle; diff --git a/src/wl_init.c b/src/wl_init.c index 8e42ce6e..3841636f 100644 --- a/src/wl_init.c +++ b/src/wl_init.c @@ -642,6 +642,49 @@ static void createKeyTables(void) int _glfwPlatformInit(void) { + _glfw.wl.xkb.handle = dlopen("libxkbcommon.so.0", RTLD_LAZY | RTLD_GLOBAL); + if (!_glfw.wl.xkb.handle) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Failed to open libxkbcommon."); + return GLFW_FALSE; + } + + _glfw.wl.xkb.context_new = (PFN_xkb_context_new) + dlsym(_glfw.wl.xkb.handle, "xkb_context_new"); + _glfw.wl.xkb.context_unref = (PFN_xkb_context_unref) + dlsym(_glfw.wl.xkb.handle, "xkb_context_unref"); + _glfw.wl.xkb.keymap_new_from_string = (PFN_xkb_keymap_new_from_string) + dlsym(_glfw.wl.xkb.handle, "xkb_keymap_new_from_string"); + _glfw.wl.xkb.keymap_unref = (PFN_xkb_keymap_unref) + dlsym(_glfw.wl.xkb.handle, "xkb_keymap_unref"); + _glfw.wl.xkb.keymap_mod_get_index = (PFN_xkb_keymap_mod_get_index) + dlsym(_glfw.wl.xkb.handle, "xkb_keymap_mod_get_index"); + _glfw.wl.xkb.state_new = (PFN_xkb_state_new) + dlsym(_glfw.wl.xkb.handle, "xkb_state_new"); + _glfw.wl.xkb.state_unref = (PFN_xkb_state_unref) + dlsym(_glfw.wl.xkb.handle, "xkb_state_unref"); + _glfw.wl.xkb.state_key_get_syms = (PFN_xkb_state_key_get_syms) + dlsym(_glfw.wl.xkb.handle, "xkb_state_key_get_syms"); + _glfw.wl.xkb.state_update_mask = (PFN_xkb_state_update_mask) + dlsym(_glfw.wl.xkb.handle, "xkb_state_update_mask"); + _glfw.wl.xkb.state_serialize_mods = (PFN_xkb_state_serialize_mods) + dlsym(_glfw.wl.xkb.handle, "xkb_state_serialize_mods"); + _glfw.wl.xkb.compose_table_new_from_locale = (PFN_xkb_compose_table_new_from_locale) + dlsym(_glfw.wl.xkb.handle, "xkb_compose_table_new_from_locale"); + _glfw.wl.xkb.compose_table_unref = (PFN_xkb_compose_table_unref) + dlsym(_glfw.wl.xkb.handle, "xkb_compose_table_unref"); + _glfw.wl.xkb.compose_state_new = (PFN_xkb_compose_state_new) + dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_new"); + _glfw.wl.xkb.compose_state_unref = (PFN_xkb_compose_state_unref) + dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_unref"); + _glfw.wl.xkb.compose_state_feed = (PFN_xkb_compose_state_feed) + dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_feed"); + _glfw.wl.xkb.compose_state_get_status = (PFN_xkb_compose_state_get_status) + dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_get_status"); + _glfw.wl.xkb.compose_state_get_one_sym = (PFN_xkb_compose_state_get_one_sym) + dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_get_one_sym"); + _glfw.wl.display = wl_display_connect(NULL); if (!_glfw.wl.display) { @@ -700,6 +743,9 @@ void _glfwPlatformTerminate(void) xkb_state_unref(_glfw.wl.xkb.state); xkb_context_unref(_glfw.wl.xkb.context); + dlclose(_glfw.wl.xkb.handle); + _glfw.wl.xkb.handle = NULL; + if (_glfw.wl.cursorTheme) wl_cursor_theme_destroy(_glfw.wl.cursorTheme); if (_glfw.wl.cursorSurface) diff --git a/src/wl_monitor.c b/src/wl_monitor.c index 24b0b39a..10ef0e12 100644 --- a/src/wl_monitor.c +++ b/src/wl_monitor.c @@ -153,6 +153,15 @@ void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) *ypos = monitor->wl.y; } +void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, + float* xscale, float* yscale) +{ + if (xscale) + *xscale = (float) monitor->wl.scale; + if (yscale) + *yscale = (float) monitor->wl.scale; +} + GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* found) { *found = monitor->modeCount; diff --git a/src/wl_platform.h b/src/wl_platform.h index 656f0ef1..059ea2ee 100644 --- a/src/wl_platform.h +++ b/src/wl_platform.h @@ -68,6 +68,41 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR #define _GLFW_PLATFORM_CONTEXT_STATE #define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE +typedef struct xkb_context* (* PFN_xkb_context_new)(enum xkb_context_flags); +typedef void (* PFN_xkb_context_unref)(struct xkb_context*); +typedef struct xkb_keymap* (* PFN_xkb_keymap_new_from_string)(struct xkb_context*, const char*, enum xkb_keymap_format, enum xkb_keymap_compile_flags); +typedef void (* PFN_xkb_keymap_unref)(struct xkb_keymap*); +typedef xkb_mod_index_t (* PFN_xkb_keymap_mod_get_index)(struct xkb_keymap*, const char*); +typedef struct xkb_state* (* PFN_xkb_state_new)(struct xkb_keymap*); +typedef void (* PFN_xkb_state_unref)(struct xkb_state*); +typedef int (* PFN_xkb_state_key_get_syms)(struct xkb_state*, xkb_keycode_t, const xkb_keysym_t**); +typedef enum xkb_state_component (* PFN_xkb_state_update_mask)(struct xkb_state*, xkb_mod_mask_t, xkb_mod_mask_t, xkb_mod_mask_t, xkb_layout_index_t, xkb_layout_index_t, xkb_layout_index_t); +typedef xkb_mod_mask_t (* PFN_xkb_state_serialize_mods)(struct xkb_state*, enum xkb_state_component); +typedef struct xkb_compose_table* (* PFN_xkb_compose_table_new_from_locale)(struct xkb_context*, const char*, enum xkb_compose_compile_flags); +typedef void (* PFN_xkb_compose_table_unref)(struct xkb_compose_table*); +typedef struct xkb_compose_state* (* PFN_xkb_compose_state_new)(struct xkb_compose_table*, enum xkb_compose_state_flags); +typedef void (* PFN_xkb_compose_state_unref)(struct xkb_compose_state*); +typedef enum xkb_compose_feed_result (* PFN_xkb_compose_state_feed)(struct xkb_compose_state*, xkb_keysym_t); +typedef enum xkb_compose_status (* PFN_xkb_compose_state_get_status)(struct xkb_compose_state*); +typedef xkb_keysym_t (* PFN_xkb_compose_state_get_one_sym)(struct xkb_compose_state*); +#define xkb_context_new _glfw.wl.xkb.context_new +#define xkb_context_unref _glfw.wl.xkb.context_unref +#define xkb_keymap_new_from_string _glfw.wl.xkb.keymap_new_from_string +#define xkb_keymap_unref _glfw.wl.xkb.keymap_unref +#define xkb_keymap_mod_get_index _glfw.wl.xkb.keymap_mod_get_index +#define xkb_state_new _glfw.wl.xkb.state_new +#define xkb_state_unref _glfw.wl.xkb.state_unref +#define xkb_state_key_get_syms _glfw.wl.xkb.state_key_get_syms +#define xkb_state_update_mask _glfw.wl.xkb.state_update_mask +#define xkb_state_serialize_mods _glfw.wl.xkb.state_serialize_mods +#define xkb_compose_table_new_from_locale _glfw.wl.xkb.compose_table_new_from_locale +#define xkb_compose_table_unref _glfw.wl.xkb.compose_table_unref +#define xkb_compose_state_new _glfw.wl.xkb.compose_state_new +#define xkb_compose_state_unref _glfw.wl.xkb.compose_state_unref +#define xkb_compose_state_feed _glfw.wl.xkb.compose_state_feed +#define xkb_compose_state_get_status _glfw.wl.xkb.compose_state_get_status +#define xkb_compose_state_get_one_sym _glfw.wl.xkb.compose_state_get_one_sym + // Wayland-specific per-window data // @@ -125,6 +160,7 @@ typedef struct _GLFWlibraryWayland short int scancodes[GLFW_KEY_LAST + 1]; struct { + void* handle; struct xkb_context* context; struct xkb_keymap* keymap; struct xkb_state* state; @@ -134,6 +170,24 @@ typedef struct _GLFWlibraryWayland xkb_mod_mask_t shiftMask; xkb_mod_mask_t superMask; unsigned int modifiers; + + PFN_xkb_context_new context_new; + PFN_xkb_context_unref context_unref; + PFN_xkb_keymap_new_from_string keymap_new_from_string; + PFN_xkb_keymap_unref keymap_unref; + PFN_xkb_keymap_mod_get_index keymap_mod_get_index; + PFN_xkb_state_new state_new; + PFN_xkb_state_unref state_unref; + PFN_xkb_state_key_get_syms state_key_get_syms; + PFN_xkb_state_update_mask state_update_mask; + PFN_xkb_state_serialize_mods state_serialize_mods; + PFN_xkb_compose_table_new_from_locale compose_table_new_from_locale; + PFN_xkb_compose_table_unref compose_table_unref; + PFN_xkb_compose_state_new compose_state_new; + PFN_xkb_compose_state_unref compose_state_unref; + PFN_xkb_compose_state_feed compose_state_feed; + PFN_xkb_compose_state_get_status compose_state_get_status; + PFN_xkb_compose_state_get_one_sym compose_state_get_one_sym; } xkb; _GLFWwindow* pointerFocus; diff --git a/src/wl_window.c b/src/wl_window.c index 32d5f404..f0f2637e 100644 --- a/src/wl_window.c +++ b/src/wl_window.c @@ -550,6 +550,15 @@ void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, // implemented, but for now just leave everything as 0. } +void _glfwPlatformGetWindowContentScale(_GLFWwindow* window, + float* xscale, float* yscale) +{ + if (xscale) + *xscale = (float) window->wl.scale; + if (yscale) + *yscale = (float) window->wl.scale; +} + void _glfwPlatformIconifyWindow(_GLFWwindow* window) { // TODO: move to xdg_shell instead of wl_shell. @@ -633,7 +642,7 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, { wl_shell_surface_set_toplevel(window->wl.shellSurface); } - _glfwInputWindowMonitorChange(window, monitor); + _glfwInputWindowMonitor(window, monitor); } int _glfwPlatformWindowFocused(_GLFWwindow* window) diff --git a/src/x11_init.c b/src/x11_init.c index 526d9ca2..b603a15f 100644 --- a/src/x11_init.c +++ b/src/x11_init.c @@ -743,6 +743,43 @@ static GLFWbool initExtensions(void) return GLFW_TRUE; } +// Retrieve system content scale via folklore heuristics +// +static void getSystemContentScale(float* xscale, float* yscale) +{ + // NOTE: Default to the display-wide DPI as we don't currently have a policy + // for which monitor a window is considered to be on + float xdpi = DisplayWidth(_glfw.x11.display, _glfw.x11.screen) * + 25.4f / DisplayWidthMM(_glfw.x11.display, _glfw.x11.screen); + float ydpi = DisplayHeight(_glfw.x11.display, _glfw.x11.screen) * + 25.4f / DisplayHeightMM(_glfw.x11.display, _glfw.x11.screen); + + // NOTE: Basing the scale on Xft.dpi where available should provide the most + // consistent user experience (matches Qt, Gtk, etc), although not + // always the most accurate one + char* rms = XResourceManagerString(_glfw.x11.display); + if (rms) + { + XrmDatabase db = XrmGetStringDatabase(rms); + if (db) + { + XrmValue value; + char* type = NULL; + + if (XrmGetResource(db, "Xft.dpi", "Xft.Dpi", &type, &value)) + { + if (type && strcmp(type, "String") == 0) + xdpi = ydpi = atof(value.addr); + } + + XrmDestroyDatabase(db); + } + } + + *xscale = xdpi / 96.f; + *yscale = ydpi / 96.f; +} + // Create a blank cursor for hidden and disabled cursor modes // static Cursor createHiddenCursor(void) @@ -861,6 +898,7 @@ int _glfwPlatformInit(void) #endif XInitThreads(); + XrmInitialize(); _glfw.x11.display = XOpenDisplay(NULL); if (!_glfw.x11.display) @@ -884,6 +922,8 @@ int _glfwPlatformInit(void) _glfw.x11.root = RootWindow(_glfw.x11.display, _glfw.x11.screen); _glfw.x11.context = XUniqueContext(); + getSystemContentScale(&_glfw.x11.contentScaleX, &_glfw.x11.contentScaleY); + if (!initExtensions()) return GLFW_FALSE; diff --git a/src/x11_monitor.c b/src/x11_monitor.c index 5c0516e6..d68c5888 100644 --- a/src/x11_monitor.c +++ b/src/x11_monitor.c @@ -338,6 +338,15 @@ void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) } } +void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, + float* xscale, float* yscale) +{ + if (xscale) + *xscale = _glfw.x11.contentScaleX; + if (yscale) + *yscale = _glfw.x11.contentScaleY; +} + GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count) { GLFWvidmode* result; diff --git a/src/x11_platform.h b/src/x11_platform.h index 2b1c0c62..7eba441d 100644 --- a/src/x11_platform.h +++ b/src/x11_platform.h @@ -211,6 +211,8 @@ typedef struct _GLFWlibraryX11 int screen; Window root; + // System content scale + float contentScaleX, contentScaleY; // Helper window for IPC Window helperWindowHandle; // Invisible cursor for hidden cursor mode diff --git a/src/x11_window.c b/src/x11_window.c index cf5483b0..c89d2ec5 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -2240,6 +2240,15 @@ void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, XFree(extents); } +void _glfwPlatformGetWindowContentScale(_GLFWwindow* window, + float* xscale, float* yscale) +{ + if (xscale) + *xscale = _glfw.x11.contentScaleX; + if (yscale) + *yscale = _glfw.x11.contentScaleY; +} + void _glfwPlatformIconifyWindow(_GLFWwindow* window) { if (window->x11.overrideRedirect) @@ -2368,7 +2377,7 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, if (window->monitor) releaseMonitor(window); - _glfwInputWindowMonitorChange(window, monitor); + _glfwInputWindowMonitor(window, monitor); updateNormalHints(window, width, height); updateWindowMode(window); diff --git a/tests/monitors.c b/tests/monitors.c index fc54c319..c7fd5c5c 100644 --- a/tests/monitors.c +++ b/tests/monitors.c @@ -92,21 +92,24 @@ static void key_callback(GLFWwindow* window, int key, int scancode, int action, static void list_modes(GLFWmonitor* monitor) { - int count, x, y, widthMM, heightMM, i; + int count, x, y, width_mm, height_mm, i; + float xscale, yscale; const GLFWvidmode* mode = glfwGetVideoMode(monitor); const GLFWvidmode* modes = glfwGetVideoModes(monitor, &count); glfwGetMonitorPos(monitor, &x, &y); - glfwGetMonitorPhysicalSize(monitor, &widthMM, &heightMM); + glfwGetMonitorPhysicalSize(monitor, &width_mm, &height_mm); + glfwGetMonitorContentScale(monitor, &xscale, &yscale); printf("Name: %s (%s)\n", glfwGetMonitorName(monitor), glfwGetPrimaryMonitor() == monitor ? "primary" : "secondary"); printf("Current mode: %s\n", format_mode(mode)); printf("Virtual position: %i %i\n", x, y); + printf("Content scale: %f %f\n", xscale, yscale); printf("Physical size: %i x %i mm (%0.2f dpi)\n", - widthMM, heightMM, mode->width * 25.4f / widthMM); + width_mm, height_mm, mode->width * 25.4f / width_mm); printf("Modes:\n");