From 5cc4995861ec82f83af71ea20d34fa5cc8cc093f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaakko=20Ker=C3=A4nen?= Date: Sun, 9 Jun 2019 12:21:32 +0300 Subject: [PATCH] Refactor: Replaced DisplayMode with SDL API calls The DisplayMode abstraction is no longer useful, so use SDL directly. This also provides support for multiple displays. --- doomsday/apps/client/src/clientapp.cpp | 1 - doomsday/apps/client/src/dd_main.cpp | 7 +- doomsday/apps/client/src/gl/gl_main.cpp | 118 +++--- doomsday/apps/client/src/ui/clientwindow.cpp | 4 - .../src/ui/dialogs/videosettingsdialog.cpp | 53 +-- doomsday/apps/client/src/unix/dd_uinit.cpp | 7 +- doomsday/apps/gloom/src/gloomapp.cpp | 1 - doomsday/libs/core/include/de/core/vector.h | 3 +- doomsday/libs/core/src/math.cpp | 43 ++ doomsday/libs/gui/CMakeLists.txt | 35 -- doomsday/libs/gui/include/de/DisplayMode | 2 - .../gui/include/de/graphics/displaymode.h | 148 ------- .../include/de/graphics/displaymode_native.h | 55 --- .../libs/gui/include/de/graphics/glwindow.h | 71 +++- .../libs/gui/src/graphics/displaymode.cpp | 394 ------------------ .../gui/src/graphics/displaymode_dummy.cpp | 71 ---- .../libs/gui/src/graphics/displaymode_macx.mm | 254 ----------- .../libs/gui/src/graphics/displaymode_sdl.cpp | 64 --- .../gui/src/graphics/displaymode_windows.cpp | 142 ------- .../libs/gui/src/graphics/displaymode_x11.cpp | 297 ------------- doomsday/libs/gui/src/graphics/glwindow.cpp | 274 ++++++------ doomsday/libs/gui/src/guiapp.cpp | 52 ++- doomsday/libs/gui/src/persistentglwindow.cpp | 95 +++-- 23 files changed, 451 insertions(+), 1740 deletions(-) delete mode 100644 doomsday/libs/gui/include/de/DisplayMode delete mode 100644 doomsday/libs/gui/include/de/graphics/displaymode.h delete mode 100644 doomsday/libs/gui/include/de/graphics/displaymode_native.h delete mode 100644 doomsday/libs/gui/src/graphics/displaymode.cpp delete mode 100644 doomsday/libs/gui/src/graphics/displaymode_dummy.cpp delete mode 100644 doomsday/libs/gui/src/graphics/displaymode_macx.mm delete mode 100644 doomsday/libs/gui/src/graphics/displaymode_sdl.cpp delete mode 100644 doomsday/libs/gui/src/graphics/displaymode_windows.cpp delete mode 100644 doomsday/libs/gui/src/graphics/displaymode_x11.cpp diff --git a/doomsday/apps/client/src/clientapp.cpp b/doomsday/apps/client/src/clientapp.cpp index 7b95d5eb2d..596f558beb 100644 --- a/doomsday/apps/client/src/clientapp.cpp +++ b/doomsday/apps/client/src/clientapp.cpp @@ -85,7 +85,6 @@ #include #include #include -#include #include #include #include diff --git a/doomsday/apps/client/src/dd_main.cpp b/doomsday/apps/client/src/dd_main.cpp index 840fb514eb..8dac4cd2e0 100644 --- a/doomsday/apps/client/src/dd_main.cpp +++ b/doomsday/apps/client/src/dd_main.cpp @@ -55,7 +55,6 @@ #include #ifdef __CLIENT__ # include -# include # include #endif #include @@ -507,10 +506,6 @@ void App_Error(char const *error, ...) // Already in an error? if (errorInProgress) { -#ifdef __CLIENT__ - DisplayMode_Shutdown(); -#endif - va_start(argptr, error); dd_vsnprintf(buff, sizeof(buff), error, argptr); va_end(argptr); @@ -575,7 +570,7 @@ void App_AbnormalShutdown(char const *message) Sys_Shutdown(); #ifdef __CLIENT__ - DisplayMode_Shutdown(); + //DisplayMode_Shutdown(); DE_GUI_APP->loop().pause(); // This is an abnormal shutdown, we cannot continue drawing any of the diff --git a/doomsday/apps/client/src/gl/gl_main.cpp b/doomsday/apps/client/src/gl/gl_main.cpp index 6cd0e356b4..230fa6ef64 100644 --- a/doomsday/apps/client/src/gl/gl_main.cpp +++ b/doomsday/apps/client/src/gl/gl_main.cpp @@ -33,7 +33,6 @@ #include #include #include -#include #include #include #include @@ -72,6 +71,8 @@ #include "ui/ui_main.h" #include "ui/clientwindowsystem.h" +#include + using namespace de; using namespace res; @@ -87,7 +88,7 @@ dfloat glNearClip, glFarClip; static dd_bool initGLOk; static dd_bool initFullGLOk; -static dd_bool gamma_support; +static dd_bool isGammaRampEnabled; static dfloat oldgamma, oldcontrast, oldbright; static dint fogModeDefault; @@ -109,18 +110,34 @@ dd_bool GL_IsFullyInited() return initFullGLOk; } -void GL_GetGammaRamp(DisplayColorTransfer *ramp) +struct GammaRamp { - if(!gamma_support) return; + duint16 red[256]; + duint16 green[256]; + duint16 blue[256]; +}; - DisplayMode_GetColorTransfer(ramp); -} +//void GL_GetGammaRamp(GammaRamp *ramp) +//{ +// if (!isGammaRampEnabled) return; -void GL_SetGammaRamp(DisplayColorTransfer const *ramp) -{ - if(!gamma_support) return; +// DE_ASSERT(GLWindow::mainExists()); - DisplayMode_SetColorTransfer(ramp); +// SDL_GetWindowGammaRamp(reinterpret_cast(GLWindow::getMain().sdlWindow()), +// ramp->red, +// ramp->green, +// ramp->blue); +//} + +void GL_SetGammaRamp(const GammaRamp &ramp) +{ + if (isGammaRampEnabled && GLWindow::mainExists()) + { + SDL_SetWindowGammaRamp(reinterpret_cast(GLWindow::getMain().sdlWindow()), + ramp.red, + ramp.green, + ramp.blue); + } } /** @@ -133,51 +150,48 @@ void GL_SetGammaRamp(DisplayColorTransfer const *ramp) * @param contrast Steepness. * @param bright Brightness, uniform offset. */ -void GL_MakeGammaRamp(ushort *ramp, dfloat gamma, dfloat contrast, dfloat bright) +void GL_MakeGammaRamp(GammaRamp &ramp, dfloat gamma, dfloat contrast, dfloat bright) { - DE_ASSERT(ramp); - - ddouble ideal[256]; // After processing clamped to unsigned short. + double ideal[256]; // After processing clamped to unsigned short. // Don't allow stupid values. - if(contrast < 0.1f) - contrast = 0.1f; + if (contrast < 0.1f) contrast = 0.1f; - if(bright > 0.8f) bright = 0.8f; - if(bright < -0.8f) bright = -0.8f; + if (bright > 0.8f) bright = 0.8f; + if (bright < -0.8f) bright = -0.8f; // Init the ramp as a line with the steepness defined by contrast. - for(dint i = 0; i < 256; ++i) + for (int i = 0; i < 256; ++i) { ideal[i] = i * contrast - (contrast - 1) * 127; } // Apply the gamma curve. - if(gamma != 1) + if (gamma != 1) { - if(gamma <= 0.1f) gamma = 0.1f; + if (gamma <= 0.1f) gamma = 0.1f; - ddouble norm = pow(255, 1 / ddouble( gamma ) - 1); // Normalizing factor. - for(dint i = 0; i < 256; ++i) + double norm = pow(255, 1 / double(gamma) - 1); // Normalizing factor. + for (int i = 0; i < 256; ++i) { - ideal[i] = pow(ideal[i], 1 / ddouble( gamma )) / norm; + ideal[i] = pow(ideal[i], 1 / double(gamma)) / norm; } } // The last step is to add the brightness offset. - for(dint i = 0; i < 256; ++i) + for (int i = 0; i < 256; ++i) { ideal[i] += bright * 128; } // Clamp it and write the ramp table. - for(dint i = 0; i < 256; ++i) + for (int i = 0; i < 256; ++i) { - ideal[i] *= 0x100; // Byte => word - if(ideal[i] < 0) ideal[i] = 0; - if(ideal[i] > 0xffff) ideal[i] = 0xffff; + ideal[i] *= 0x100; // Byte => word + if (ideal[i] < 0) ideal[i] = 0; + if (ideal[i] > 0xffff) ideal[i] = 0xffff; - ramp[i] = ramp[i + 256] = ramp[i + 512] = (dushort) ideal[i]; + ramp.red[i] = ramp.green[i] = ramp.blue[i] = duint16(ideal[i]); } } @@ -186,14 +200,14 @@ void GL_MakeGammaRamp(ushort *ramp, dfloat gamma, dfloat contrast, dfloat bright */ void GL_SetGamma() { - DisplayColorTransfer myramp; + GammaRamp myramp; oldgamma = vid_gamma; oldcontrast = vid_contrast; oldbright = vid_bright; - GL_MakeGammaRamp(myramp.table, vid_gamma, vid_contrast, vid_bright); - GL_SetGammaRamp(&myramp); + GL_MakeGammaRamp(myramp, vid_gamma, vid_contrast, vid_bright); + GL_SetGammaRamp(myramp); } void GL_FinishFrame() @@ -240,7 +254,7 @@ void GL_EarlyInit() ClientApp::renderSystem().glInit(); - gamma_support = !CommandLine_Check("-noramp"); + isGammaRampEnabled = !CommandLine_Check("-noramp"); GL_InitDeferredTask(); @@ -1347,15 +1361,19 @@ D_CMD(DisplayModeInfo) ClientWindow *win = ClientWindowSystem::mainPtr(); if(!win) return false; - DisplayMode const *mode = DisplayMode_Current(); + SDL_DisplayMode disp; + Vec2i ratio = de::ratio({disp.w, disp.h}); + SDL_GetCurrentDisplayMode(win->displayIndex(), &disp); String str = Stringf("Current display mode:%ix%i depth:%i (%i:%i", - mode->width, mode->height, - mode->depth, - mode->ratioX, mode->ratioY); - if(mode->refreshRate > 0) + disp.w, + disp.h, + SDL_BITSPERPIXEL(disp.format), + ratio.x, + ratio.y); + if (disp.refresh_rate > 0) { - str += Stringf(", refresh: %.1f Hz", mode->refreshRate); + str += Stringf(", refresh: %d Hz", disp.refresh_rate); } str += Stringf(")\nMain window:\n current origin:%s size:%s" "\n windowed origin:%s size:%s" @@ -1381,21 +1399,23 @@ D_CMD(ListDisplayModes) { DE_UNUSED(src, argc, argv); - LOG_GL_MSG("There are %i display modes available:") << DisplayMode_Count(); - for(dint i = 0; i < DisplayMode_Count(); ++i) + auto &win = GLWindow::getMain(); + const auto modes = GLWindow::displayModes(win.displayIndex()); + + LOG_GL_MSG("There are %i display modes available:") << modes.size(); + for (const auto &mode : modes) { - DisplayMode const *mode = DisplayMode_ByIndex(i); - if(mode->refreshRate > 0) + if (mode.refreshRate > 0) { - LOG_GL_MSG(" %i x %i x %i " _E(>) "(%i:%i, refresh: %.1f Hz)") - << mode->width << mode->height << mode->depth - << mode->ratioX << mode->ratioY << mode->refreshRate; + LOG_GL_MSG(" %i x %i x %i " _E(>) "(%i:%i, refresh: %d Hz)") + << mode.resolution.x << mode.resolution.y << mode.bitDepth + << mode.ratio().x << mode.ratio().y << mode.refreshRate; } else { LOG_GL_MSG(" %i x %i x %i (%i:%i)") - << mode->width << mode->height << mode->depth - << mode->ratioX << mode->ratioY; + << mode.resolution.x << mode.resolution.y<< mode.bitDepth + << mode.ratio().x << mode.ratio().y; } } return true; diff --git a/doomsday/apps/client/src/ui/clientwindow.cpp b/doomsday/apps/client/src/ui/clientwindow.cpp index 1bf7697522..d831026ba0 100644 --- a/doomsday/apps/client/src/ui/clientwindow.cpp +++ b/doomsday/apps/client/src/ui/clientwindow.cpp @@ -30,13 +30,9 @@ #include "clientapp.h" #include "clientplayer.h" -//#include -//#include -//#include #include #include #include -#include #include #include #include diff --git a/doomsday/apps/client/src/ui/dialogs/videosettingsdialog.cpp b/doomsday/apps/client/src/ui/dialogs/videosettingsdialog.cpp index 1410949e84..e9506f227f 100644 --- a/doomsday/apps/client/src/ui/dialogs/videosettingsdialog.cpp +++ b/doomsday/apps/client/src/ui/dialogs/videosettingsdialog.cpp @@ -32,15 +32,14 @@ #include #include #include -#include using namespace de; using namespace de::ui; -#if !defined (MACOSX) +//#if !defined (MACOSX) # define USE_REFRESH_RATE_CHOICE # define USE_COLOR_DEPTH_CHOICE -#endif +//#endif DE_PIMPL(VideoSettingsDialog) #if !defined (DE_MOBILE) @@ -170,9 +169,9 @@ DE_PIMPL(VideoSettingsDialog) int delta = 0; for (ui::Data::Pos i = 0; i < modes->items().size(); ++i) { - const auto res = modes->items().at(i).data().asText().split(";"); - int dx = res.at(0).toInt() - current.x; - int dy = res.at(1).toInt() - current.y; + const auto res = modes->items().at(i).data().asText().split(";"); + int dx = res.at(0).toInt() - int(current.x); + int dy = res.at(1).toInt() - int(current.y); int d = dx*dx + dy*dy; if (closest == ui::Data::InvalidPos || d < delta) { @@ -225,10 +224,15 @@ DE_PIMPL(VideoSettingsDialog) fpsLimiter->isActive()? int(fpsMax->value()) : 0); } } + + List displayModes() const + { + return GLWindow::displayModes(GLWindow::getMain().displayIndex()); + } bool gotDisplayMode() const { - return DisplayMode_Count() > 0; + return displayModes().size() > 0; } }; @@ -262,34 +266,36 @@ VideoSettingsDialog::VideoSettingsDialog(String const &name) #endif if (d->gotDisplayMode()) { + const auto dispModes = d->displayModes(); // Choice of display modes + 16/32-bit color depth. d->modes->setOpeningDirection(ui::Up); - if (DisplayMode_Count() > 10) + if (dispModes.size() > 10) { d->modes->popup().menu().setGridSize(2, ui::Expand, 0, ui::Expand); } - for (int i = 0; i < DisplayMode_Count(); ++i) + // Resolutions. { - DisplayMode const *m = DisplayMode_ByIndex(i); - const String res = Stringf("%i;%i", m->width, m->height); - if (d->modes->items().findData(TextValue(res)) != ui::Data::InvalidPos) + for (const auto &m : dispModes) { - // Got this already. - continue; + const String res = Stringf("%d;%d", m.resolution.x, m.resolution.y); + if (d->modes->items().findData(TextValue(res)) != ui::Data::InvalidPos) + { + // Got this already. + continue; + } + String desc = Stringf("%d x %d (%d:%d)", m.resolution.x, m.resolution.y, m.ratio().x, m.ratio().y); + d->modes->items() << new ChoiceItem(desc, res); } - String desc = Stringf("%i x %i (%i:%i)", m->width, m->height, m->ratioX, m->ratioY); - d->modes->items() << new ChoiceItem(desc, res); } - #ifdef USE_REFRESH_RATE_CHOICE { refreshLabel = LabelWidget::newWithText("Monitor Refresh:", &area()); Set rates; rates.insert(0); - for (int i = 0; i < DisplayMode_Count(); ++i) + for (const auto &m : dispModes) { - rates.insert(int(DisplayMode_ByIndex(i)->refreshRate * 10)); + rates.insert(int(m.refreshRate * 10)); } for (int rate : rates) { @@ -312,13 +318,11 @@ VideoSettingsDialog::VideoSettingsDialog(String const &name) }); } #endif - #ifdef USE_COLOR_DEPTH_CHOICE { colorLabel = LabelWidget::newWithText("Colors:", &area()); d->depths->items() << new ChoiceItem("32-bit", 32) - << new ChoiceItem("24-bit", 24) << new ChoiceItem("16-bit", 16); } #endif @@ -446,8 +450,8 @@ void VideoSettingsDialog::changeColorDepth(ui::DataPos selected) void VideoSettingsDialog::changeRefreshRate(ui::DataPos selected) { #ifdef USE_REFRESH_RATE_CHOICE - float const rate = d->refreshRates->items().at(selected).data().asNumber() / 10.f; - int const attribs[] = { + const double rate = d->refreshRates->items().at(selected).data().asNumber() / 10.0; + const int attribs[] = { ClientWindow::RefreshRate, int(rate * 1000), // milli-Hz ClientWindow::End }; @@ -480,15 +484,12 @@ void VideoSettingsDialog::showWindowMenu() void VideoSettingsDialog::applyModeToWindow() { -// QPoint const res = d->modes->selectedItem().data().toPoint(); const auto res = d->modes->selectedItem().data().asText().split(";"); - int attribs[] = { ClientWindow::Width, res.at(0).toInt(), ClientWindow::Height, res.at(1).toInt(), ClientWindow::End }; - d->win.changeAttributes(attribs); } diff --git a/doomsday/apps/client/src/unix/dd_uinit.cpp b/doomsday/apps/client/src/unix/dd_uinit.cpp index 49413142b1..22828f42e0 100644 --- a/doomsday/apps/client/src/unix/dd_uinit.cpp +++ b/doomsday/apps/client/src/unix/dd_uinit.cpp @@ -33,7 +33,6 @@ #include "dd_uinit.h" #ifdef __CLIENT__ -# include # include "gl/sys_opengl.h" #endif @@ -81,7 +80,7 @@ void DD_Shutdown(void) // DoomsdayApp::plugins().unloadAll(); // Library_Shutdown(); -#ifdef __CLIENT__ - DisplayMode_Shutdown(); -#endif +//#ifdef __CLIENT__ +// DisplayMode_Shutdown(); +//#endif } diff --git a/doomsday/apps/gloom/src/gloomapp.cpp b/doomsday/apps/gloom/src/gloomapp.cpp index 7d2c95f5a8..a0d9826885 100644 --- a/doomsday/apps/gloom/src/gloomapp.cpp +++ b/doomsday/apps/gloom/src/gloomapp.cpp @@ -29,7 +29,6 @@ #include #include -#include #include #include #include diff --git a/doomsday/libs/core/include/de/core/vector.h b/doomsday/libs/core/include/de/core/vector.h index d8eb45e7db..f51862c6bd 100644 --- a/doomsday/libs/core/include/de/core/vector.h +++ b/doomsday/libs/core/include/de/core/vector.h @@ -821,8 +821,7 @@ typedef Vector4 Vec4f; ///< 4-component vector of floating point value typedef Vector4 Vec4d; ///< 4-component vector of high-precision floating point values. ///@} -// Qt hash functions: -//inline quint32 qHash(Vec2i const &vec) { return vec.x * vec.y + vec.x - vec.y; } +DE_PUBLIC Vec2i ratio(const Vec2i &); } // namespace de diff --git a/doomsday/libs/core/src/math.cpp b/doomsday/libs/core/src/math.cpp index 3f57599f22..aeb94b05a6 100644 --- a/doomsday/libs/core/src/math.cpp +++ b/doomsday/libs/core/src/math.cpp @@ -19,6 +19,7 @@ #include "de/math.h" #include "de/IByteArray" #include "de/Reader" +#include "de/Vector" #include #include @@ -150,4 +151,46 @@ duint32 crc32(IByteArray const &data) return crc32; } +Vec2i ratio(const Vec2i &values) +{ + int ratioX = values.x; + int ratioY = values.y; + + float fx; + float fy; + if (values.x > values.y) + { + fx = float(values.x) / float(values.y); + fy = 1.f; + } + else + { + fx = 1.f; + fy = float(values.y) / float(values.x); + } + + // Multiply until we arrive at a close enough integer ratio. + for (int mul = 2; mul < de::min(values.x, values.y); ++mul) + { + float rx = fx * mul; + float ry = fy * mul; + if (std::abs(rx - roundi(rx)) < .01f && std::abs(ry - roundi(ry)) < .01f) + { + // This seems good. + ratioX = roundi(rx); + ratioY = roundi(ry); + break; + } + } + + if (ratioX == 8 && ratioY == 5) + { + // This is commonly referred to as 16:10. + ratioX *= 2; + ratioY *= 2; + } + + return {ratioX, ratioY}; +} + } // namespace de diff --git a/doomsday/libs/gui/CMakeLists.txt b/doomsday/libs/gui/CMakeLists.txt index 0329f36a33..543cf6ac40 100644 --- a/doomsday/libs/gui/CMakeLists.txt +++ b/doomsday/libs/gui/CMakeLists.txt @@ -6,17 +6,6 @@ project (DE_LIBGUI) # Apply Doomsday's configuration. include (../../cmake/Config.cmake) -if (APPLE) - # Don't change display modes on macOS/iOS. We'll instead resize the - # frame buffer and scale up. - set (DE_ENABLE_DISPLAYMODE_DEFAULT OFF) -else () - set (DE_ENABLE_DISPLAYMODE_DEFAULT ON) -endif () -option (DE_ENABLE_DISPLAYMODE "Enable/disable changing the display mode" - ${DE_ENABLE_DISPLAYMODE_DEFAULT} -) - # OpenGL API selection. set (DE_OPENGL_API "3.3" CACHE STRING "OpenGL API selection (3.3, GLES3, GLES2)") if (IOS) @@ -51,14 +40,6 @@ deng_merge_sources (widgets src/widgets/*.cpp) list (APPEND SOURCES src/dialogs/filedialog_${DE_PLATFORM_SUFFIX}) -# Choose which DisplayMode sources to use. -list_remove_matches (SOURCES ".*/displaymode_.*") -if (NOT DE_ENABLE_DISPLAYMODE) - list (APPEND SOURCES src/graphics/displaymode_dummy.cpp) -else () - list (APPEND SOURCES src/graphics/displaymode_sdl.cpp) -endif () - deng_add_library (libgui ${SOURCES} ${HEADERS}) if (DE_OPENGL_API STREQUAL "3.3") @@ -104,22 +85,6 @@ endif () # target_include_directories (libgui PRIVATE ${X11_INCLUDE_DIR}) # target_link_libraries (libgui PRIVATE ${X11_LIBRARIES}) # deng_target_link_qt (libgui PRIVATE X11Extras) -# if (DE_ENABLE_DISPLAYMODE) -# if (NOT X11_Xrandr_FOUND) -# message (FATAL_ERROR "DisplayMode requires X11 Xrandr extension (enable/disable with DE_ENABLE_DISPLAYMODE)") -# endif () -# if (NOT X11_xf86vmode_FOUND) -# message (FATAL_ERROR "DisplayMode requires X11 xf86vmode extension (enable/disable with DE_ENABLE_DISPLAYMODE)") -# endif () -# target_include_directories (libgui PRIVATE -# ${X11_Xrandr_INCLUDE_PATH} -# ${X11_xf86vmode_INCLUDE_PATH} -# ) -# target_link_libraries (libgui PRIVATE -# ${X11_Xrandr_LIB} -# ${X11_Xxf86vm_LIB} -# ) -# endif () #endif () deng_deploy_library (libgui DengGui) diff --git a/doomsday/libs/gui/include/de/DisplayMode b/doomsday/libs/gui/include/de/DisplayMode deleted file mode 100644 index 97cbf9807c..0000000000 --- a/doomsday/libs/gui/include/de/DisplayMode +++ /dev/null @@ -1,2 +0,0 @@ -#include "graphics/displaymode.h" -#include "graphics/displaymode_native.h" diff --git a/doomsday/libs/gui/include/de/graphics/displaymode.h b/doomsday/libs/gui/include/de/graphics/displaymode.h deleted file mode 100644 index c0595a53db..0000000000 --- a/doomsday/libs/gui/include/de/graphics/displaymode.h +++ /dev/null @@ -1,148 +0,0 @@ -/** - * @file displaymode.h - * Changing and enumerating available display modes. @ingroup gui - * - * High-level logic for enumerating, selecting, and changing display modes. See - * displaymode_native.h for the platform-specific low-level routines. - * - * @todo This is C because it was relocated from the client. It should be - * converted to a C++. - * - * @authors Copyright © 2012-2017 Jaakko Keränen - * - * @par License - * LGPL: http://www.gnu.org/licenses/lgpl.html - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 3 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 Lesser - * General Public License for more details. You should have received a copy of - * the GNU Lesser General Public License along with this program; if not, see: - * http://www.gnu.org/licenses - */ - -#ifndef LIBGUI_DISPLAYMODE_H -#define LIBGUI_DISPLAYMODE_H - -#include "../libgui.h" -#include "de/libcore.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct displaymode_s { - int width; - int height; - float refreshRate; // might be zero - int depth; - - // Calculated automatically: - int ratioX; - int ratioY; -} DisplayMode; - -typedef struct displaycolortransfer_s { - unsigned short table[3 * 256]; // 0-255:red, 256-511:green, 512-767:blue (range: 0..ffff) -} DisplayColorTransfer; - -/** - * Initializes the DisplayMode class. Enumerates all available display modes and - * saves the current display mode. Must be called at engine startup. - * - * @return @c true, if display modes were initialized successfully. - */ -LIBGUI_PUBLIC int DisplayMode_Init(void); - -/** - * Gets the current color transfer function and saves it as the one that will be - * restored at shutdown. - */ -LIBGUI_PUBLIC void DisplayMode_SaveOriginalColorTransfer(void); - -/** - * Shuts down the DisplayMode class. The current display mode is restored to what - * it was at initialization time. - */ -LIBGUI_PUBLIC void DisplayMode_Shutdown(void); - -/** - * Returns the display mode that was in use when DisplayMode_Init() was called. - */ -LIBGUI_PUBLIC DisplayMode const *DisplayMode_OriginalMode(void); - -/** - * Returns the current display mode. - */ -LIBGUI_PUBLIC DisplayMode const *DisplayMode_Current(void); - -/** - * Returns the number of available display modes. - */ -LIBGUI_PUBLIC int DisplayMode_Count(void); - -/** - * Returns one of the available display modes. Use DisplayMode_Count() to - * determine how many modes are available. - * - * @param index Index of the mode, must be between 0 and DisplayMode_Count() - 1. - */ -LIBGUI_PUBLIC DisplayMode const *DisplayMode_ByIndex(int index); - -/** - * Finds the closest available mode to the given criteria. - * - * @param width Width in pixels. - * @param height Height in pixels. - * @param depth Color depth (bits per color component). - * @param freq Refresh rate. If zero, prefers rates closest to the mode at startup time. - * - * @return Mode that most closely matches the criteria. Always returns one of - * the available modes; returns @c NULL only if DisplayMode_Init() has not yet - * been called. - */ -LIBGUI_PUBLIC DisplayMode const *DisplayMode_FindClosest(int width, int height, int depth, float freq); - -/** - * Determines if two display modes are equivalent. - * - * @param a DisplayMode instance. - * @param b DisplayMode instance. - * - * @return @c true or @c false. - */ -LIBGUI_PUBLIC int DisplayMode_IsEqual(DisplayMode const *a, DisplayMode const *b); - -/** - * Changes the display mode. - * - * @param mode Mode to change to. Must be one of the modes returned by the functions in displaymode.h. - * @param shouldCapture @c true, if the mode is intended to capture the entire display. - * - * @return @c true, if a mode change occurred. @c false, otherwise (bad mode or - * when attempting to change to the current mode). - */ -LIBGUI_PUBLIC int DisplayMode_Change(DisplayMode const *mode, int shouldCapture); - -/** - * Gets the current color transfer table. - * - * @param colors Color transfer. - */ -LIBGUI_PUBLIC void DisplayMode_GetColorTransfer(DisplayColorTransfer *colors); - -/** - * Sets the color transfer table. - * - * @param colors Color transfer. - */ -LIBGUI_PUBLIC void DisplayMode_SetColorTransfer(DisplayColorTransfer const *colors); - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif // LIBGUI_DISPLAYMODE_H diff --git a/doomsday/libs/gui/include/de/graphics/displaymode_native.h b/doomsday/libs/gui/include/de/graphics/displaymode_native.h deleted file mode 100644 index e37056ccdc..0000000000 --- a/doomsday/libs/gui/include/de/graphics/displaymode_native.h +++ /dev/null @@ -1,55 +0,0 @@ -/** - * @file displaymode_native.h - * Changing and enumerating available display modes using native APIs. - * @ingroup gui - * - * These routines are only intended to be used privately by the DisplayMode - * class. Never call these directly unless you are sure you want to access - * platform specific low-level functionality. - * - * @authors Copyright © 2012-2017 Jaakko Keränen - * - * @par License - * LGPL: http://www.gnu.org/licenses/lgpl.html - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 3 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 Lesser - * General Public License for more details. You should have received a copy of - * the GNU Lesser General Public License along with this program; if not, see: - * http://www.gnu.org/licenses - */ - -#ifndef DE_DISPLAYMODE_NATIVE_H -#define DE_DISPLAYMODE_NATIVE_H - -#include "displaymode.h" - -#ifdef __cplusplus -extern "C" { -#endif - -void DisplayMode_Native_Init(void); - -void DisplayMode_Native_Shutdown(void); - -int DisplayMode_Native_Count(void); - -void DisplayMode_Native_GetMode(int index, DisplayMode* mode); - -void DisplayMode_Native_GetCurrentMode(DisplayMode* mode); - -int DisplayMode_Native_Change(const DisplayMode* mode, int shouldCapture); - -void DisplayMode_Native_GetColorTransfer(DisplayColorTransfer *colors); - -void DisplayMode_Native_SetColorTransfer(DisplayColorTransfer const *colors); - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif // DE_DISPLAYMODE_NATIVE_H diff --git a/doomsday/libs/gui/include/de/graphics/glwindow.h b/doomsday/libs/gui/include/de/graphics/glwindow.h index 86c9ef748a..17d2988555 100644 --- a/doomsday/libs/gui/include/de/graphics/glwindow.h +++ b/doomsday/libs/gui/include/de/graphics/glwindow.h @@ -27,10 +27,6 @@ #include #include -#if defined (DE_MOBILE) -# error "glwindow.h is for desktop platforms (use glwindow_qml.h instead)" -#endif - #ifdef WIN32 # undef min # undef max @@ -51,6 +47,35 @@ class LIBGUI_PUBLIC GLWindow : public Asset public: using Size = Vec2ui; + struct LIBGUI_PUBLIC DisplayMode // for fullscreen use + { + Vec2i resolution; + unsigned int bitDepth; + int refreshRate; + + bool operator==(const DisplayMode &other) const + { + return resolution == other.resolution && + (!bitDepth || !other.bitDepth || bitDepth == other.bitDepth) && + (!refreshRate || !other.refreshRate || refreshRate == other.refreshRate); + } + + inline bool operator!=(const DisplayMode &other) const + { + return !(*this == other); + } + + inline bool isDefault() const + { + return resolution == Vec2i(); + } + + Vec2i ratio() const + { + return de::ratio(resolution); + } + }; + /** * Notified when the window's GL state needs to be initialized. The OpenGL * context and drawing surface are not ready before this occurs. This gets @@ -64,6 +89,11 @@ class LIBGUI_PUBLIC GLWindow : public Asset */ DE_DEFINE_AUDIENCE2(Resize, void windowResized(GLWindow &)) + /** + * Notified when the window's current display changes. + */ + DE_DEFINE_AUDIENCE2(Display, void windowDisplayChanged(GLWindow &)) + /** * Notified when the window pixel ratio has changed. */ @@ -110,7 +140,7 @@ class LIBGUI_PUBLIC GLWindow : public Asset bool isHidden() const; float frameRate() const; - uint frameCount() const; + uint frameCount() const; inline int x() const { return pos().x; } inline int y() const { return pos().y; } @@ -120,8 +150,8 @@ class LIBGUI_PUBLIC GLWindow : public Asset */ Vec2i pos() const; - Size pointSize() const; - Size pixelSize() const; + Size pointSize() const; + Size pixelSize() const; duint pointWidth() const; duint pointHeight() const; duint pixelWidth() const; @@ -139,6 +169,12 @@ class LIBGUI_PUBLIC GLWindow : public Asset } double pixelRatio() const; + int displayIndex() const; + + void setFullscreenDisplayMode(const DisplayMode &mode); + DisplayMode fullscreenDisplayMode() const; + DisplayMode desktopDisplayMode() const; + bool isNotDesktopDisplayMode() const; inline Rectanglei geometry() const { return {x(), y(), pointSize().x, pointSize().y }; } @@ -215,18 +251,25 @@ class LIBGUI_PUBLIC GLWindow : public Asset */ void glDone(); - /* - * Returns a handle to the native window instance. (Platform-specific.) + /** + * Returns a handle to the SDL window instance. */ -// void *nativeHandle() const; + void *sdlWindow() const; virtual void draw() = 0; public: - static bool mainExists(); - static GLWindow &getMain(); - static void glActivateMain(); - static void setMain(GLWindow *window); + static void setMain(GLWindow *window); + static bool mainExists(); + static GLWindow & getMain(); + static void glActivateMain(); + + /** + * Enumerates the available display modes of a display. + * @param displayIndex Which display. + * @return List of supported modes. + */ + static List displayModes(int displayIndex); protected: virtual void initializeGL(); diff --git a/doomsday/libs/gui/src/graphics/displaymode.cpp b/doomsday/libs/gui/src/graphics/displaymode.cpp deleted file mode 100644 index 109670d539..0000000000 --- a/doomsday/libs/gui/src/graphics/displaymode.cpp +++ /dev/null @@ -1,394 +0,0 @@ -/** @file displaymode.cpp Platform-independent display mode management. - * @ingroup gl - * - * @authors Copyright (c) 2012-2017 Jaakko Keränen - * - * @par License - * LGPL: http://www.gnu.org/licenses/lgpl.html - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 3 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 Lesser - * General Public License for more details. You should have received a copy of - * the GNU Lesser General Public License along with this program; if not, see: - * http://www.gnu.org/licenses - */ - -#include "de/graphics/displaymode.h" -#include "de/graphics/displaymode_native.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -namespace de { - -static bool inited = false; -static Binder * binder = nullptr; -static DisplayColorTransfer originalColorTransfer; - -static float differenceToOriginalHz(float hz); - -namespace internal { - -struct Mode : public DisplayMode -{ - Mode() - { - de::zapPtr(static_cast(this)); - } - - Mode(const DisplayMode& dm) - { - memcpy(static_cast(this), &dm, sizeof(dm)); - } - - Mode(int i) - { - DisplayMode_Native_GetMode(i, this); - updateRatio(); - } - - static Mode fromCurrent() - { - Mode m; - DisplayMode_Native_GetCurrentMode(&m); - m.updateRatio(); - return m; - } - - bool operator == (const Mode& other) const - { - return width == other.width && height == other.height && - depth == other.depth && fequal(refreshRate, other.refreshRate); - } - - bool operator != (const Mode& other) const - { - return !(*this == other); - } - - bool operator < (const Mode& b) const - { - if (width == b.width) - { - if (height == b.height) - { - if (depth == b.depth) - { - // The refresh rate that more closely matches the original is preferable. - return differenceToOriginalHz(refreshRate) < - differenceToOriginalHz(b.refreshRate); - } - return depth < b.depth; - } - return height < b.height; - } - return width < b.width; - } - - void updateRatio() - { - ratioX = width; - ratioY = height; - - float fx; - float fy; - if (width > height) - { - fx = width/float(height); - fy = 1.f; - } - else - { - fx = 1.f; - fy = height/float(width); - } - - // Multiply until we arrive at a close enough integer ratio. - for (int mul = 2; mul < de::min(width, height); ++mul) - { - float rx = fx*mul; - float ry = fy*mul; - if (std::abs(rx - de::roundi(rx)) < .01f && std::abs(ry - de::roundi(ry)) < .01f) - { - // This seems good. - ratioX = de::roundi(rx); - ratioY = de::roundi(ry); - break; - } - } - - if (ratioX == 8 && ratioY == 5) - { - // This is commonly referred to as 16:10. - ratioX *= 2; - ratioY *= 2; - } - } - - void debugPrint() const - { - LOG_GL_VERBOSE("size: %i x %i x %i, rate: %.1f Hz, ratio: %i:%i") - << width << height << depth << refreshRate << ratioX << ratioY; - } -}; - -} // namespace internal - -using namespace internal; - -typedef std::set Modes; // note: no duplicates - -static Modes modes; -static Mode originalMode; -static bool captured; - -static float differenceToOriginalHz(float hz) -{ - return std::abs(hz - originalMode.refreshRate); -} - -static de::Value *Function_DisplayMode_OriginalMode(de::Context &, de::Function::ArgumentValues const &) -{ - using de::NumberValue; - using de::TextValue; - - DisplayMode const *mode = DisplayMode_OriginalMode(); - - de::DictionaryValue *dict = new de::DictionaryValue; - dict->add(new TextValue("width"), new NumberValue(mode->width)); - dict->add(new TextValue("height"), new NumberValue(mode->height)); - dict->add(new TextValue("depth"), new NumberValue(mode->depth)); - dict->add(new TextValue("refreshRate"), new NumberValue(mode->refreshRate)); - - de::ArrayValue *ratio = new de::ArrayValue; - *ratio << NumberValue(mode->ratioX) << NumberValue(mode->ratioY); - dict->add(new TextValue("ratio"), ratio); - - return dict; -} - -} // namespace de - -using namespace de; - -#if defined (DE_DEBUG) -static void assertDisplayModeHasBeenDeinitialized() -{ - DE_ASSERT(!inited); -} -#endif - -int DisplayMode_Init(void) -{ - DE_ASSERT(!inited); // don't redo it - if (inited) return true; - - captured = false; - DisplayMode_Native_Init(); -#if defined(MACOSX) || defined(UNIX) - DisplayMode_SaveOriginalColorTransfer(); -#endif - - // This is used for sorting the mode set (Hz). - originalMode = Mode::fromCurrent(); - - for (int i = 0; i < DisplayMode_Native_Count(); ++i) - { - Mode mode(i); - if (mode.depth < 16 || mode.width < 320 || mode.height < 240) - continue; // This mode is not good. - modes.insert(mode); - } - - LOG_GL_VERBOSE("Current mode is:"); - originalMode.debugPrint(); - - LOG_GL_VERBOSE("All available modes:"); - for (auto i = modes.begin(); i != modes.end(); ++i) - { - i->debugPrint(); - } - - // Script bindings. - { - binder = new Binder; - binder->initNew() << DE_FUNC_NOARG(DisplayMode_OriginalMode, "originalMode"); - de::App::scriptSystem().addNativeModule("DisplayMode", binder->module()); - binder->module().addNumber("PIXEL_RATIO", 1.0); - } - -#if defined (DE_DEBUG) - atexit(assertDisplayModeHasBeenDeinitialized); -#endif - - inited = true; - return true; -} - -void DisplayMode_Shutdown(void) -{ - if (!inited) return; - - delete binder; - binder = nullptr; - - LOG_GL_NOTE("Restoring original display mode due to shutdown"); - - // Back to the original mode. - DisplayMode_Change(&originalMode, false /*release captured*/); - - modes.clear(); - - DisplayMode_Native_Shutdown(); - captured = false; - - DisplayMode_Native_SetColorTransfer(&originalColorTransfer); - - inited = false; -} - -void DisplayMode_SaveOriginalColorTransfer(void) -{ - DisplayMode_Native_GetColorTransfer(&originalColorTransfer); -} - -DisplayMode const *DisplayMode_OriginalMode(void) -{ - return &originalMode; -} - -DisplayMode const *DisplayMode_Current(void) -{ - static Mode currentMode; - // Update it with current mode. - currentMode = Mode::fromCurrent(); - return ¤tMode; -} - -int DisplayMode_Count(void) -{ - return (int) modes.size(); -} - -DisplayMode const *DisplayMode_ByIndex(int index) -{ - DE_ASSERT(index >= 0); - DE_ASSERT(index < (int) modes.size()); - - int pos = 0; - for (Modes::iterator i = modes.begin(); i != modes.end(); ++i, ++pos) - { - if (pos == index) - { - return &*i; - } - } - - DE_ASSERT(false); - return 0; // unreachable -} - -DisplayMode const *DisplayMode_FindClosest(int width, int height, int depth, float freq) -{ - int bestScore = -1; - DisplayMode const *best = 0; - - for (Modes::iterator i = modes.begin(); i != modes.end(); ++i) - { - int score = de::squared(i->width - width) + - de::squared(i->height - height) + - de::squared(i->depth - depth); - if (freq >= 1) - { - score += de::squared(i->refreshRate - freq); - } - - // Note: The first mode to hit the lowest score wins; if there are many modes - // with the same score, the first one will be chosen. Particularly if the - // frequency has not been specified, the sort order of the modes defines which - // one is picked. - if (!best || score < bestScore) - { - bestScore = score; - best = &*i; - } - } - return best; -} - -int DisplayMode_IsEqual(DisplayMode const *a, DisplayMode const *b) -{ - if (!a || !b) return true; // Cannot compare against nothing. - return Mode(*a) == Mode(*b); -} - -int DisplayMode_Change(DisplayMode const *mode, int shouldCapture) -{ - if (Mode::fromCurrent() == *mode && !shouldCapture == !captured) - { - LOG_AS("DisplayMode"); - LOGDEV_GL_XVERBOSE("Requested mode is the same as current, ignoring request", ""); - - // Already in this mode. - return false; - } - captured = shouldCapture; - return DisplayMode_Native_Change(mode, shouldCapture || (originalMode != *mode)); -} - -static inline de::duint16 intensity8To16(de::duint8 b) -{ - return (b << 8) | b; // 0xFF => 0xFFFF -} - -void DisplayMode_GetColorTransfer(DisplayColorTransfer *colors) -{ - DisplayColorTransfer mapped; - DisplayMode_Native_GetColorTransfer(&mapped); - - // Factor out the original color transfer function, which may be set up - // specifically by the user. - for (int i = 0; i < 256; ++i) - { -#define LINEAR_UNMAP(i, c) ( (unsigned short) \ - de::clamp(0.f, float(mapped.table[i]) / float(originalColorTransfer.table[i]) * intensity8To16(c), 65535.f) ) - colors->table[i] = LINEAR_UNMAP(i, i); - colors->table[i + 256] = LINEAR_UNMAP(i + 256, i); - colors->table[i + 512] = LINEAR_UNMAP(i + 512, i); - } -} - -void DisplayMode_SetColorTransfer(DisplayColorTransfer const *colors) -{ - DisplayColorTransfer mapped; - - // Factor in the original color transfer function, which may be set up - // specifically by the user. - for (int i = 0; i < 256; ++i) - { -#define LINEAR_MAP(i, c) ( (unsigned short) \ - de::clamp(0.f, float(colors->table[i]) / float(intensity8To16(c)) * originalColorTransfer.table[i], 65535.f) ) - mapped.table[i] = LINEAR_MAP(i, i); - mapped.table[i + 256] = LINEAR_MAP(i + 256, i); - mapped.table[i + 512] = LINEAR_MAP(i + 512, i); - } - - DisplayMode_Native_SetColorTransfer(&mapped); -} diff --git a/doomsday/libs/gui/src/graphics/displaymode_dummy.cpp b/doomsday/libs/gui/src/graphics/displaymode_dummy.cpp deleted file mode 100644 index b98d20c904..0000000000 --- a/doomsday/libs/gui/src/graphics/displaymode_dummy.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/** @file displaymode_dummy.cpp - * Dummy implementation of the DisplayMode native functionality. - * @ingroup gl - * - * @authors Copyright © 2012-2017 Jaakko Keränen - * - * @par License - * LGPL: http://www.gnu.org/licenses/lgpl.html - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 3 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 Lesser - * General Public License for more details. You should have received a copy of - * the GNU Lesser General Public License along with this program; if not, see: - * http://www.gnu.org/licenses - */ - -#include "de/graphics/displaymode_native.h" -#include - -#include - -void DisplayMode_Native_Init(void) -{ -} - -void DisplayMode_Native_Shutdown(void) -{ -} - -int DisplayMode_Native_Count(void) -{ - return 0; -} - -void DisplayMode_Native_GetMode(int index, DisplayMode *mode) -{ - DE_UNUSED(index); - DE_UNUSED(mode); -} - -void DisplayMode_Native_GetCurrentMode(DisplayMode *mode) -{ - SDL_DisplayMode disp; - SDL_GetCurrentDisplayMode(0, &disp); - - mode->width = disp.w; - mode->height = disp.h; - mode->depth = SDL_BITSPERPIXEL(disp.format); - mode->refreshRate = disp.refresh_rate; -} - -int DisplayMode_Native_Change(DisplayMode const *mode, int shouldCapture) -{ - DE_UNUSED(mode); - DE_UNUSED(shouldCapture); - return true; -} - -void DisplayMode_Native_GetColorTransfer(DisplayColorTransfer *colors) -{ - DE_UNUSED(colors); -} - -void DisplayMode_Native_SetColorTransfer(DisplayColorTransfer const *colors) -{ - DE_UNUSED(colors); -} diff --git a/doomsday/libs/gui/src/graphics/displaymode_macx.mm b/doomsday/libs/gui/src/graphics/displaymode_macx.mm deleted file mode 100644 index ac4d02901a..0000000000 --- a/doomsday/libs/gui/src/graphics/displaymode_macx.mm +++ /dev/null @@ -1,254 +0,0 @@ -/** - * @file displaymode_macx.mm - * A macOS implementation of the DisplayMode class. @ingroup gl - * - * @authors Copyright © 2012-2017 Jaakko Keränen - * - * @par License - * LGPL: http://www.gnu.org/licenses/lgpl.html - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 3 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 Lesser - * General Public License for more details. You should have received a copy of - * the GNU Lesser General Public License along with this program; if not, see: - * http://www.gnu.org/licenses - */ - -#include -#include -#include -#include -#include - -//#include "de/libcore.h" -#include "de/gui/displaymode_native.h" - -/// Returns -1 on error. -static int intFromDict(CFDictionaryRef dict, CFStringRef key) -{ - CFNumberRef ref = (CFNumberRef) CFDictionaryGetValue(dict, key); - if (!ref) return -1; - int value; - if (!CFNumberGetValue(ref, kCFNumberIntType, &value)) return -1; - return value; -} - -/// Returns -1 on error. -static float floatFromDict(CFDictionaryRef dict, CFStringRef key) -{ - CFNumberRef ref = (CFNumberRef) CFDictionaryGetValue(dict, key); - if (!ref) return -1; - float value; - if (!CFNumberGetValue(ref, kCFNumberFloatType, &value)) return -1; - return value; -} - -static DisplayMode modeFromDict(CFDictionaryRef dict) -{ - DisplayMode m; - m.width = intFromDict(dict, kCGDisplayWidth); - m.height = intFromDict(dict, kCGDisplayHeight); - m.refreshRate = floatFromDict(dict, kCGDisplayRefreshRate); - m.depth = intFromDict(dict, kCGDisplayBitsPerPixel); - return m; -} - -static std::vector displayModes; -static std::vector displayDicts; -//static CFDictionaryRef currentDisplayDict; - -static CFDictionaryRef getCurrentDisplayDict(void) -{ - return (CFDictionaryRef) CGDisplayCurrentMode(kCGDirectMainDisplay); -} - -static void updateDisplayDicts(void) -{ - displayDicts.clear(); - - CFArrayRef modes = CGDisplayAvailableModes(kCGDirectMainDisplay); - CFIndex count = CFArrayGetCount(modes); - for (CFIndex i = 0; i < count; ++i) - { - displayDicts.push_back((CFDictionaryRef) CFArrayGetValueAtIndex(modes, i)); - } - - assert(displayModes.size() == displayDicts.size()); -} - -void DisplayMode_Native_Init(void) -{ - // Let's see which modes are available. - CFArrayRef modes = CGDisplayAvailableModes(kCGDirectMainDisplay); - CFIndex count = CFArrayGetCount(modes); - for (CFIndex i = 0; i < count; ++i) - { - CFDictionaryRef dict = (CFDictionaryRef) CFArrayGetValueAtIndex(modes, i); - displayModes.push_back(modeFromDict(dict)); - displayDicts.push_back(dict); - } - //currentDisplayDict = (CFDictionaryRef) CGDisplayCurrentMode(kCGDirectMainDisplay); -} - -static bool captureDisplays(int capture) -{ - if (capture && !CGDisplayIsCaptured(kCGDirectMainDisplay)) - { - return CGCaptureAllDisplays() == kCGErrorSuccess; - } - else if (!capture && CGDisplayIsCaptured(kCGDirectMainDisplay)) - { - CGReleaseAllDisplays(); - return true; - } - return true; -} - -static void releaseDisplays() -{ - captureDisplays(false); -} - -void DisplayMode_Native_Shutdown(void) -{ - displayModes.clear(); - releaseDisplays(); -} - -int DisplayMode_Native_Count(void) -{ - return displayModes.size(); -} - -void DisplayMode_Native_GetMode(int index, DisplayMode *mode) -{ - assert(index >= 0 && index < (int)displayModes.size()); - *mode = displayModes[index]; -} - -void DisplayMode_Native_GetCurrentMode(DisplayMode *mode) -{ - *mode = modeFromDict(getCurrentDisplayDict()); -} - -static int findIndex(DisplayMode const *mode) -{ - for (unsigned int i = 0; i < displayModes.size(); ++i) - { - if (displayModes[i].width == mode->width && - displayModes[i].height == mode->height && - displayModes[i].depth == mode->depth && - displayModes[i].refreshRate == mode->refreshRate) - { - return i; - } - } - return -1; // Invalid mode. -} - -int DisplayMode_Native_Change(DisplayMode const *mode, int shouldCapture) -{ - const CGDisplayFadeInterval fadeTime = .5f; - - updateDisplayDicts(); - - assert(mode); - assert(findIndex(mode) >= 0); // mode must be an enumerated one - - // Fade all displays to black (blocks until faded). - CGDisplayFadeReservationToken token; - CGAcquireDisplayFadeReservation(kCGMaxDisplayReservationInterval, &token); - CGDisplayFade(token, fadeTime, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0, 0, 0, true /* wait */); - - // Capture the displays now if haven't yet done so. - bool wasPreviouslyCaptured = CGDisplayIsCaptured(kCGDirectMainDisplay); - CGDisplayErr result = kCGErrorSuccess; - - CFDictionaryRef newModeDict = displayDicts[findIndex(mode)]; - - // Capture displays if instructed to do so. - if (shouldCapture && !captureDisplays(true)) - { - result = kCGErrorFailure; - } - - if (result == kCGErrorSuccess && getCurrentDisplayDict() != newModeDict) - { - // Try to change. - result = CGDisplaySwitchToMode(kCGDirectMainDisplay, newModeDict); - if (result != kCGErrorSuccess) - { - // Oh no! - //CGDisplaySwitchToMode(kCGDirectMainDisplay, currentDisplayDict); - if (!wasPreviouslyCaptured) releaseDisplays(); - } - /* - else - { - currentDisplayDict = displayDicts[findIndex(mode)]; - } - */ - } - - // Fade back to normal. - CGDisplayFade(token, 2*fadeTime, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0, 0, 0, false); - CGReleaseDisplayFadeReservation(token); - - // Release display capture if instructed to do so. - if (!shouldCapture) - { - captureDisplays(false); - } - - return result == kCGErrorSuccess; -} - -void DisplayMode_Native_Raise(void *nativeHandle) -{ - assert(nativeHandle); - - // Qt uses the Cocoa framework. - NSView* handle = (NSView*) nativeHandle; - NSWindow* wnd = [handle window]; - [wnd setLevel:CGShieldingWindowLevel()]; -} - -void DisplayMode_Native_GetColorTransfer(DisplayColorTransfer *colors) -{ - const uint size = 256; - CGGammaValue red[size]; - CGGammaValue green[size]; - CGGammaValue blue[size]; - uint32_t count = 0; - - CGGetDisplayTransferByTable(kCGDirectMainDisplay, size, red, green, blue, &count); - assert(count == size); - - for (uint i = 0; i < size; ++i) - { - colors->table[i] = (unsigned short) (red[i] * 0xffff); - colors->table[i + size] = (unsigned short) (green[i] * 0xffff); - colors->table[i + 2*size] = (unsigned short) (blue[i] * 0xffff); - } -} - -void DisplayMode_Native_SetColorTransfer(DisplayColorTransfer const *colors) -{ - const uint size = 256; - CGGammaValue red[size]; - CGGammaValue green[size]; - CGGammaValue blue[size]; - - for (uint i = 0; i < size; ++i) - { - red[i] = colors->table[i]/float(0xffff); - green[i] = colors->table[i + size]/float(0xffff); - blue[i] = colors->table[i + 2*size]/float(0xffff); - } - - CGSetDisplayTransferByTable(kCGDirectMainDisplay, size, red, green, blue); -} diff --git a/doomsday/libs/gui/src/graphics/displaymode_sdl.cpp b/doomsday/libs/gui/src/graphics/displaymode_sdl.cpp deleted file mode 100644 index 12f97741b5..0000000000 --- a/doomsday/libs/gui/src/graphics/displaymode_sdl.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/** @file displaymode_sdl.cpp DisplayMode backend based on SDL. - * @ingroup gl - * - * @authors Copyright (c) 2018-2019 Jaakko Keränen - * - * @par License - * LGPL: http://www.gnu.org/licenses/lgpl.html - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 3 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 Lesser - * General Public License for more details. You should have received a copy of - * the GNU Lesser General Public License along with this program; if not, see: - * http://www.gnu.org/licenses - */ - -#include "de/graphics/displaymode_native.h" -#include - -#include - -void DisplayMode_Native_Init(void) -{ -} - -void DisplayMode_Native_Shutdown(void) -{ -} - -int DisplayMode_Native_Count(void) -{ - return 0; -} - -void DisplayMode_Native_GetMode(int index, DisplayMode *mode) -{ - DE_UNUSED(index); - DE_UNUSED(mode); -} - -void DisplayMode_Native_GetCurrentMode(DisplayMode *mode) -{ - DE_UNUSED(mode); -} - -int DisplayMode_Native_Change(DisplayMode const *mode, int shouldCapture) -{ - DE_UNUSED(mode); - DE_UNUSED(shouldCapture); - return true; -} - -void DisplayMode_Native_GetColorTransfer(DisplayColorTransfer *colors) -{ - DE_UNUSED(colors); -} - -void DisplayMode_Native_SetColorTransfer(DisplayColorTransfer const *colors) -{ - DE_UNUSED(colors); -} diff --git a/doomsday/libs/gui/src/graphics/displaymode_windows.cpp b/doomsday/libs/gui/src/graphics/displaymode_windows.cpp deleted file mode 100644 index 25630388d4..0000000000 --- a/doomsday/libs/gui/src/graphics/displaymode_windows.cpp +++ /dev/null @@ -1,142 +0,0 @@ -/** @file displaymode_windows.cpp Windows implementation of the DisplayMode native functionality. - * @ingroup gl - * - * @authors Copyright © 2003-2017 Jaakko Keränen - * @authors Copyright © 2005-2013 Daniel Swanson - * - * @par License - * LGPL: http://www.gnu.org/licenses/lgpl.html - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 3 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 Lesser - * General Public License for more details. You should have received a copy of - * the GNU Lesser General Public License along with this program; if not, see: - * http://www.gnu.org/licenses - */ - -#include - -#define WIN32_LEAN_AND_MEAN -#include -#include -#include -#include - -#include - -#include "de/gui/displaymode_native.h" -#include "de/PersistentGLWindow" - -static std::vector devModes; -static DEVMODE currentDevMode; - -static DisplayMode devToDisplayMode(const DEVMODE& d) -{ - DisplayMode m; - m.width = d.dmPelsWidth; - m.height = d.dmPelsHeight; - m.depth = d.dmBitsPerPel; - m.refreshRate = d.dmDisplayFrequency; - return m; -} - -void DisplayMode_Native_Init(void) -{ - // Let's see which modes are available. - for (int i = 0; ; i++) - { - DEVMODE mode; - de::zap(mode); - mode.dmSize = sizeof(mode); - if (!EnumDisplaySettings(NULL, i, &mode)) - break; // That's all. - - devModes.push_back(mode); - } - - // And which is the current mode? - de::zap(currentDevMode); - currentDevMode.dmSize = sizeof(currentDevMode); - EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, ¤tDevMode); -} - -void DisplayMode_Native_Shutdown(void) -{ - devModes.clear(); -} - -int DisplayMode_Native_Count(void) -{ - return devModes.size(); -} - -void DisplayMode_Native_GetMode(int index, DisplayMode* mode) -{ - DENG2_ASSERT(index >= 0 && index < DisplayMode_Native_Count()); - *mode = devToDisplayMode(devModes[index]); -} - -void DisplayMode_Native_GetCurrentMode(DisplayMode* mode) -{ - *mode = devToDisplayMode(currentDevMode); -} - -static int findMode(const DisplayMode* mode) -{ - for (int i = 0; i < DisplayMode_Native_Count(); ++i) - { - DisplayMode d = devToDisplayMode(devModes[i]); - if (DisplayMode_IsEqual(&d, mode)) - { - return i; - } - } - return -1; -} - -int DisplayMode_Native_Change(const DisplayMode* mode, int shouldCapture) -{ - DENG2_ASSERT(mode); - DENG2_ASSERT(findMode(mode) >= 0); - - DEVMODE m = devModes[findMode(mode)]; - m.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | DM_DISPLAYFREQUENCY; - - if (ChangeDisplaySettings(&m, shouldCapture? CDS_FULLSCREEN : 0) != DISP_CHANGE_SUCCESSFUL) - return false; - - currentDevMode = m; - return true; -} - -void DisplayMode_Native_SetColorTransfer(DisplayColorTransfer const *colors) -{ - if (!de::GLWindow::mainExists()) return; - - HWND hWnd = (HWND) de::GLWindow::main().nativeHandle(); - DENG2_ASSERT(hWnd != 0); - - HDC hDC = GetDC(hWnd); - if (hDC) - { - SetDeviceGammaRamp(hDC, (void*) colors->table); - ReleaseDC(hWnd, hDC); - } -} - -void DisplayMode_Native_GetColorTransfer(DisplayColorTransfer *colors) -{ - HWND hWnd = (HWND) de::GLWindow::main().nativeHandle(); - DENG2_ASSERT(hWnd != 0); - - HDC hDC = GetDC(hWnd); - if (hDC) - { - GetDeviceGammaRamp(hDC, (void *) colors->table); - ReleaseDC(hWnd, hDC); - } -} diff --git a/doomsday/libs/gui/src/graphics/displaymode_x11.cpp b/doomsday/libs/gui/src/graphics/displaymode_x11.cpp deleted file mode 100644 index b4158ca6b5..0000000000 --- a/doomsday/libs/gui/src/graphics/displaymode_x11.cpp +++ /dev/null @@ -1,297 +0,0 @@ -/** @file displaymode_x11.cpp X11 implementation of the DisplayMode native functionality. - * @ingroup gl - * - * Uses the XRandR extension to manipulate the display. - * - * @authors Copyright © 2012-2017 Jaakko Keränen - * - * @par License - * LGPL: http://www.gnu.org/licenses/lgpl.html - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 3 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 Lesser - * General Public License for more details. You should have received a copy of - * the GNU Lesser General Public License along with this program; if not, see: - * http://www.gnu.org/licenses - */ - -#include -#include -#include -#include -#include - -#include -#include -#include -#undef Always -#undef None - -#include "de/gui/displaymode_native.h" - -#include -#include - -namespace de { - -typedef std::vector DisplayModes; - -static int displayDepth; -static Rotation displayRotation; -static DisplayModes availableModes; -static DisplayMode currentMode; - -namespace internal { - -/** - * Wrapper for the Xrandr configuration info. The config is kept in memory only - * for the lifetime of an RRInfo instance. - */ -class RRInfo -{ -public: - /** - * Queries all the available modes in the display configuration. - */ - RRInfo() : _conf(NULL), _numSizes(0) - { - int dummy; - if (!XRRQueryExtension(QX11Info::display(), &dummy, &dummy)) return; // Not available. - - _conf = XRRGetScreenInfo(QX11Info::display(), QX11Info::appRootWindow()); - if (!_conf) return; // Not available. - - // Let's see which modes are available. - _sizes = XRRConfigSizes(_conf, &_numSizes); - for (int i = 0; i < _numSizes; ++i) - { - int numRates = 0; - short* rates = XRRConfigRates(_conf, i, &numRates); - for (int k = 0; k < numRates; k++) - { - DisplayMode mode; - de::zap(mode); - mode.width = _sizes[i].width; - mode.height = _sizes[i].height; - mode.depth = displayDepth; - mode.refreshRate = rates[k]; - _modes.push_back(mode); - } - } - - ::Time prevConfTime; - _confTime = XRRConfigTimes(_conf, &prevConfTime); - } - - ~RRInfo() - { - if (_conf) XRRFreeScreenConfigInfo(_conf); - } - - /** - * Returns the currently active mode as specified in the Xrandr config. - * Also determines the display's current rotation angle. - */ - DisplayMode currentMode() const - { - DisplayMode mode; - de::zap(mode); - - if (!_conf) return mode; - - SizeID currentSize = XRRConfigCurrentConfiguration(_conf, &displayRotation); - - // Update the current mode. - mode.width = _sizes[currentSize].width; - mode.height = _sizes[currentSize].height; - mode.depth = displayDepth; - mode.refreshRate = XRRConfigCurrentRate(_conf); - return mode; - } - - std::vector& modes() { return _modes; } - - static short rateFromMode(const DisplayMode* mode) - { - return short(qRound(mode->refreshRate)); - } - - int find(const DisplayMode* mode) const - { - for (int i = 0; i < _numSizes; ++i) - { - int numRates = 0; - short* rates = XRRConfigRates(_conf, i, &numRates); - for (int k = 0; k < numRates; k++) - { - if (rateFromMode(mode) == rates[k] && - mode->width == _sizes[i].width && - mode->height == _sizes[i].height) - { - // This is the one. - return i; - } - } - } - return -1; - } - - bool apply(const DisplayMode* mode) - { - if (!_conf) return false; - - int sizeIdx = find(mode); - assert(sizeIdx >= 0); - - //qDebug() << "calling XRRSetScreenConfig" << _confTime; - Status result = XRRSetScreenConfigAndRate(QX11Info::display(), _conf, QX11Info::appRootWindow(), - sizeIdx, displayRotation, rateFromMode(mode), _confTime); - //qDebug() << "result" << result; - if (result == BadValue) - { - qDebug() << "Failed to apply screen config and rate with Xrandr"; - return false; - } - - // Update the current mode. - de::currentMode = *mode; - return true; - } - -private: - XRRScreenConfiguration* _conf; - XRRScreenSize* _sizes; - ::Time _confTime; - int _numSizes; - DisplayModes _modes; -}; - -} // namespace internal -} // namespace de - -using namespace de; -using namespace de::internal; - -void DisplayMode_Native_Init(void) -{ - // We will not be changing the depth at runtime. -#ifdef DENG2_QT_5_0_OR_NEWER - displayDepth = qApp->screens().at(0)->depth(); -#else - displayDepth = QX11Info::appDepth(); -#endif - - RRInfo info; - availableModes = info.modes(); - currentMode = info.currentMode(); -} - -void DisplayMode_Native_Shutdown(void) -{ - availableModes.clear(); -} - -int DisplayMode_Native_Count(void) -{ - return availableModes.size(); -} - -void DisplayMode_Native_GetMode(int index, DisplayMode* mode) -{ - assert(index >= 0 && index < DisplayMode_Native_Count()); - *mode = availableModes[index]; -} - -void DisplayMode_Native_GetCurrentMode(DisplayMode* mode) -{ - *mode = currentMode; -} - -#if 0 -static int findMode(const DisplayMode* mode) -{ - for (int i = 0; i < DisplayMode_Native_Count(); ++i) - { - DisplayMode d = devToDisplayMode(devModes[i]); - if (DisplayMode_IsEqual(&d, mode)) - { - return i; - } - } - return -1; -} -#endif - -int DisplayMode_Native_Change(DisplayMode const *mode, int shouldCapture) -{ - DENG2_UNUSED(shouldCapture); - return RRInfo().apply(mode); -} - -void DisplayMode_Native_GetColorTransfer(DisplayColorTransfer *colors) -{ - Display *dpy = QX11Info::display(); - int screen = QX11Info::appScreen(); - int event = 0, error = 0; - - LOG_AS("GetColorTransfer"); - - if (!dpy || !XF86VidModeQueryExtension(dpy, &event, &error)) - { - LOG_GL_WARNING("XFree86-VidModeExtension not available."); - return; - } - LOGDEV_GL_XVERBOSE("event# %i error# %i", event << error); - - // Ramp size. - int rampSize = 0; - XF86VidModeGetGammaRampSize(dpy, screen, &rampSize); - LOGDEV_GL_VERBOSE("Gamma ramp size: %i") << rampSize; - if (!rampSize) return; - - ushort* xRamp = new ushort[3 * rampSize]; - - // Get the current ramps. - XF86VidModeGetGammaRamp(dpy, screen, rampSize, xRamp, - xRamp + rampSize, xRamp + 2*rampSize); - - for (uint i = 0; i < 256; ++i) - { - const uint tx = qMin(uint(rampSize - 1), i * rampSize / 255); - colors->table[i] = xRamp[tx]; - colors->table[i + 256] = xRamp[tx + rampSize]; - colors->table[i + 512] = xRamp[tx + 2*rampSize]; - } - - delete [] xRamp; -} - -void DisplayMode_Native_SetColorTransfer(DisplayColorTransfer const *colors) -{ - Display* dpy = QX11Info::display(); - if (!dpy) return; - - // Ramp size. - int rampSize = 0; - XF86VidModeGetGammaRampSize(dpy, QX11Info::appScreen(), &rampSize); - if (!rampSize) return; - - ushort* xRamp = new ushort[3 * rampSize]; - - for (int i = 0; i < rampSize; ++i) - { - const uint tx = qMin(255, i * 256 / (rampSize - 1)); - xRamp[i] = colors->table[tx]; - xRamp[i + rampSize] = colors->table[tx + 256]; - xRamp[i + 2*rampSize] = colors->table[tx + 512]; - } - - XF86VidModeSetGammaRamp(dpy, QX11Info::appScreen(), rampSize, - xRamp, xRamp + rampSize, xRamp + 2*rampSize); - - delete [] xRamp; -} diff --git a/doomsday/libs/gui/src/graphics/glwindow.cpp b/doomsday/libs/gui/src/graphics/glwindow.cpp index cb935b8451..dbe846dbeb 100644 --- a/doomsday/libs/gui/src/graphics/glwindow.cpp +++ b/doomsday/libs/gui/src/graphics/glwindow.cpp @@ -53,6 +53,7 @@ DE_PIMPL(GLWindow) bool paintPending = false; Size currentSize; double pixelRatio = 0.0; + int displayIndex; uint frameCount = 0; float fps = 0; @@ -80,6 +81,7 @@ DE_PIMPL(GLWindow) SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); glContext = SDL_GL_CreateContext(window); + displayIndex = SDL_GetWindowDisplayIndex(window); } ~Impl() @@ -101,6 +103,23 @@ DE_PIMPL(GLWindow) } } + void updatePixelRatio() + { + int dw, dh, w, h; + SDL_GetWindowSize(window, &w, &h); + SDL_GL_GetDrawableSize(window, &dw, &dh); + const double ratio = double(dw) / double(w); + if (!fequal(ratio, pixelRatio)) + { + pixelRatio = ratio; + debug("[GLWindow] pixel ratio changed: %f", pixelRatio); + DE_FOR_PUBLIC_AUDIENCE2(PixelRatio, i) + { + i->windowPixelRatioChanged(self()); + } + } + } + void glInit() { GLInfo::glInit(); @@ -126,27 +145,9 @@ DE_PIMPL(GLWindow) self().makeCurrent(); DE_ASSERT(SDL_GL_GetCurrentContext() != nullptr); - -#if 0 - // Print some information. - QSurfaceFormat const fmt = self().format(); - -#if defined (DE_OPENGL) - LOG_GL_NOTE("OpenGL %i.%i supported%s") - << fmt.majorVersion() - << fmt.minorVersion() - << (fmt.majorVersion() > 2? - (fmt.profile() == QSurfaceFormat::CompatibilityProfile? " (Compatibility)" - : " (Core)") : ""); -#else - LOG_GL_NOTE("OpenGL ES %i.%i supported") - << fmt.majorVersion() << fmt.minorVersion(); -#endif -#endif - LIBGUI_ASSERT_GL_OK(); - debug("Window pixel size at notifyReady: %s", currentSize.asText().c_str()); + debug("[GLWindow] pixel size at notifyReady: %s", currentSize.asText().c_str()); // Everybody can perform GL init now. DE_FOR_PUBLIC_AUDIENCE2(Init, i) i->windowInit(self()); @@ -264,108 +265,97 @@ DE_PIMPL(GLWindow) } } + void checkWhichDisplay() + { + const int disp = SDL_GetWindowDisplayIndex(window); + if (disp != displayIndex) + { + displayIndex = disp; + debug("[GLWindow] display index changed: %d", displayIndex); + DE_FOR_PUBLIC_AUDIENCE2(Display, i) { i->windowDisplayChanged(self()); } + updatePixelRatio(); + } + } + void handleSDLEvent(const SDL_Event &event) { switch (event.type) { - case SDL_KEYDOWN: - case SDL_KEYUP: - case SDL_TEXTINPUT: - case SDL_MOUSEMOTION: - case SDL_MOUSEBUTTONDOWN: - case SDL_MOUSEBUTTONUP: - case SDL_MOUSEWHEEL: - case SDL_FINGERUP: - case SDL_FINGERDOWN: - case SDL_MULTIGESTURE: - handler->handleSDLEvent(&event); - break; - - case SDL_WINDOWEVENT: - switch (event.window.event) - { - case SDL_WINDOWEVENT_EXPOSED: - if (!initialized) + case SDL_KEYDOWN: + case SDL_KEYUP: + case SDL_TEXTINPUT: + case SDL_MOUSEMOTION: + case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEBUTTONUP: + case SDL_MOUSEWHEEL: + case SDL_FINGERUP: + case SDL_FINGERDOWN: + case SDL_MULTIGESTURE: handler->handleSDLEvent(&event); break; + + case SDL_WINDOWEVENT: + switch (event.window.event) { - self().initializeGL(); - self().update(); + case SDL_WINDOWEVENT_EXPOSED: + if (!initialized) + { + self().initializeGL(); + self().update(); + } + checkWhichDisplay(); + break; + + case SDL_WINDOWEVENT_MOVED: + checkWhichDisplay(); + DE_FOR_PUBLIC_AUDIENCE2(Move, i) + { + i->windowMoved(self(), Vec2i(event.window.data1, event.window.data2)); + } + break; + + case SDL_WINDOWEVENT_RESIZED: + resizeEvent(event.window); + checkWhichDisplay(); + break; + + case SDL_WINDOWEVENT_CLOSE: break; + + case SDL_WINDOWEVENT_FOCUS_GAINED: + case SDL_WINDOWEVENT_FOCUS_LOST: handler->handleSDLEvent(&event); break; + + case SDL_WINDOWEVENT_MAXIMIZED: + case SDL_WINDOWEVENT_MINIMIZED: + case SDL_WINDOWEVENT_RESTORED: + case SDL_WINDOWEVENT_HIDDEN: break; + + case SDL_WINDOWEVENT_SHOWN: + checkWhichDisplay(); + self().update(); + break; + + default: break; } break; - case SDL_WINDOWEVENT_MOVED: - DE_FOR_PUBLIC_AUDIENCE2(Move, i) - { - i->windowMoved(self(), Vec2i(event.window.data1, event.window.data2)); - } - break; - - case SDL_WINDOWEVENT_RESIZED: resizeEvent(event.window); break; - - case SDL_WINDOWEVENT_CLOSE: break; - - case SDL_WINDOWEVENT_FOCUS_GAINED: - case SDL_WINDOWEVENT_FOCUS_LOST: - handler->handleSDLEvent(&event); - break; - - case SDL_WINDOWEVENT_MAXIMIZED: - case SDL_WINDOWEVENT_MINIMIZED: - case SDL_WINDOWEVENT_RESTORED: - case SDL_WINDOWEVENT_HIDDEN: break; - - case SDL_WINDOWEVENT_SHOWN: - self().update(); - break; - default: break; - } - break; - - default: break; } } - DE_PIMPL_AUDIENCES(Init, Resize, PixelRatio, Swap, Move, Visibility) + DE_PIMPL_AUDIENCES(Init, Resize, Display, PixelRatio, Swap, Move, Visibility) }; -DE_AUDIENCE_METHODS(GLWindow, Init, Resize, PixelRatio, Swap, Move, Visibility) +DE_AUDIENCE_METHODS(GLWindow, Init, Resize, Display, PixelRatio, Swap, Move, Visibility) GLWindow::GLWindow() : d(new Impl(this)) { -//#if defined (MACOSX) -// setFlags(flags() | Qt::WindowFullscreenButtonHint); -//#endif - -//#if defined (DE_MOBILE) -// setFocusPolicy(Qt::StrongFocus); -//#endif - d->handler = new WindowEventHandler(this); d->handler->setKeyboardMode(WindowEventHandler::RawKeys); -// d->pixelRatio = devicePixelRatio(); - d->handler->audienceForMouseStateChange() += [this]() { const bool trap = d->handler->isMouseTrapped(); SDL_SetWindowGrab(d->window, trap ? SDL_TRUE : SDL_FALSE); SDL_SetRelativeMouseMode(trap ? SDL_TRUE : SDL_FALSE); }; - -#if 0 - connect(this, &QWindow::screenChanged, [this](QScreen *scr) { - //qDebug() << "window screen changed:" << scr << scr->devicePixelRatio(); - if (!fequal(d->pixelRatio, scr->devicePixelRatio())) - { - d->pixelRatio = scr->devicePixelRatio(); - d->submitResize(pointSize() * d->pixelRatio); - DENG2_FOR_AUDIENCE2(PixelRatio, i) - { - i->windowPixelRatioChanged(*this); - } - } - }); -#endif } void GLWindow::setTitle(const String &title) @@ -421,7 +411,9 @@ void GLWindow::showMaximized() void GLWindow::showFullScreen() { SDL_ShowWindow(d->window); - SDL_SetWindowFullscreen(d->window, SDL_WINDOW_FULLSCREEN_DESKTOP); + const bool isDesktop = (fullscreenDisplayMode() == desktopDisplayMode()); + SDL_SetWindowFullscreen(d->window, + isDesktop ? SDL_WINDOW_FULLSCREEN_DESKTOP : SDL_WINDOW_FULLSCREEN); } void GLWindow::hide() @@ -449,13 +441,6 @@ void GLWindow::setGeometry(const Rectanglei &rect) } } -#if defined (DE_MOBILE) -void GLWindow::setTitle(QString const &title) -{ - setWindowTitle(title); -} -#endif - bool GLWindow::isGLReady() const { return d->readyNotified; @@ -463,20 +448,12 @@ bool GLWindow::isGLReady() const bool GLWindow::isMaximized() const { -#if defined (DE_MOBILE) - return false; -#else return d->winFlags().testFlag(SDL_WINDOW_MAXIMIZED); -#endif } bool GLWindow::isMinimized() const { -#if defined (DE_MOBILE) - return false; -#else return d->winFlags().testFlag(SDL_WINDOW_MINIMIZED); -#endif } bool GLWindow::isVisible() const @@ -486,20 +463,12 @@ bool GLWindow::isVisible() const bool GLWindow::isFullScreen() const { -#if defined (DE_MOBILE) - return true; -#else return (d->winFlags() & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_FULLSCREEN_DESKTOP)) != 0; -#endif } bool GLWindow::isHidden() const { -#if defined (DE_MOBILE) - return false; -#else return d->winFlags().testFlag(SDL_WINDOW_HIDDEN); -#endif } GLFramebuffer &GLWindow::framebuffer() const @@ -546,6 +515,68 @@ double GLWindow::pixelRatio() const return d->pixelRatio; } +int GLWindow::displayIndex() const +{ + DE_ASSERT(d->displayIndex == SDL_GetWindowDisplayIndex(d->window)); + return d->displayIndex; +} + +void GLWindow::setFullscreenDisplayMode(const DisplayMode &mode) +{ + SDL_DisplayMode wanted{}; + wanted.w = mode.resolution.x; + wanted.h = mode.resolution.y; + wanted.format = (mode.bitDepth == 16 ? SDL_PIXELFORMAT_RGB565 : SDL_PIXELFORMAT_RGB888); + wanted.refresh_rate = mode.refreshRate; + + if (mode.isDefault()) + { + SDL_GetDesktopDisplayMode(d->displayIndex, &wanted); + } + + SDL_DisplayMode disp; + if (SDL_GetClosestDisplayMode(d->displayIndex, &wanted, &disp)) + { + SDL_SetWindowDisplayMode(d->window, &disp); + } +} + +static GLWindow::DisplayMode fromSdl(const SDL_DisplayMode &disp) +{ + return {{disp.w, disp.h}, SDL_BITSPERPIXEL(disp.format), disp.refresh_rate}; +} + +GLWindow::DisplayMode GLWindow::fullscreenDisplayMode() const +{ + SDL_DisplayMode disp; + SDL_GetWindowDisplayMode(d->window, &disp); + return fromSdl(disp); +} + +GLWindow::DisplayMode GLWindow::desktopDisplayMode() const +{ + SDL_DisplayMode disp; + SDL_GetDesktopDisplayMode(d->displayIndex, &disp); + return fromSdl(disp); +} + +bool GLWindow::isNotDesktopDisplayMode() const +{ + return fullscreenDisplayMode() != desktopDisplayMode(); +} + +List GLWindow::displayModes(int displayIndex) +{ + List modes; + for (int i = 0; i < SDL_GetNumDisplayModes(displayIndex); ++i) + { + SDL_DisplayMode disp; + SDL_GetDisplayMode(displayIndex, i, &disp); + modes << fromSdl(disp); + } + return modes; +} + duint GLWindow::pointWidth() const { return pointSize().x; @@ -746,4 +777,9 @@ void GLWindow::setMain(GLWindow *window) // static GuiLoop::get().setWindow(window); } +void *GLWindow::sdlWindow() const +{ + return d->window; +} + } // namespace de diff --git a/doomsday/libs/gui/src/guiapp.cpp b/doomsday/libs/gui/src/guiapp.cpp index a42f576807..3941241411 100644 --- a/doomsday/libs/gui/src/guiapp.cpp +++ b/doomsday/libs/gui/src/guiapp.cpp @@ -23,14 +23,16 @@ #include #include -#include +#include +#include #include #include #include #include +#include #include +#include #include -#include #include @@ -39,9 +41,30 @@ namespace de { +static Value *Function_DisplayMode_OriginalMode(Context &, const Function::ArgumentValues &) +{ + SDL_DisplayMode mode; + SDL_GetDesktopDisplayMode(GLWindow::mainExists() ? GLWindow::getMain().displayIndex() : 0, + &mode); + const auto dim = de::ratio({mode.w, mode.h}); + + auto *dict = new DictionaryValue; + dict->add(new TextValue("width"), new NumberValue(mode.w)); + dict->add(new TextValue("height"), new NumberValue(mode.h)); + dict->add(new TextValue("depth"), new NumberValue(SDL_BITSPERPIXEL(mode.format))); + dict->add(new TextValue("refreshRate"), new NumberValue(mode.refresh_rate)); + + auto *ratio = new ArrayValue; + *ratio << NumberValue(dim.x) << NumberValue(dim.y); + dict->add(new TextValue("ratio"), ratio); + + return dict; +} + DE_PIMPL(GuiApp) , DE_OBSERVES(Variable, Change) { + Binder binder; EventLoop eventLoop; GuiLoop loop; thrd_t renderThread{}; @@ -59,6 +82,13 @@ DE_PIMPL(GuiApp) arrowCursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW); vsizeCursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENS); hsizeCursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEWE); + + // Script bindings. + { + binder.initNew() << DE_FUNC_NOARG(DisplayMode_OriginalMode, "originalMode"); + self().scriptSystem().addNativeModule("DisplayMode", binder.module()); + binder.module().addNumber("PIXEL_RATIO", 1.0); + } } ~Impl() override @@ -67,7 +97,6 @@ DE_PIMPL(GuiApp) SDL_FreeCursor(arrowCursor); SDL_FreeCursor(vsizeCursor); SDL_FreeCursor(hsizeCursor); - DisplayMode_Shutdown(); SDL_Quit(); } @@ -122,14 +151,25 @@ GuiApp::GuiApp(const StringList &args) { throw Error("GuiApp::GuiApp", stringf("Failed to initialize SDL: %s", SDL_GetError())); } - if (SDL_GetNumVideoDisplays() == 0) + const int displayCount = SDL_GetNumVideoDisplays(); + if (displayCount == 0) { throw Error("GuiApp::GuiApp", "No video displays available"); } setLocale_Foundation(); - DisplayMode_Init(); - d->determineDevicePixelRatio(); + // List available display modes. + for (int i = 0; i < displayCount; ++i) + { + LOG_GL_MSG("Display %d:") << i; + for (const auto &mode : GLWindow::displayModes(i)) + { + LOG_GL_MSG(" %d x %d (%d bits) @ %d Hz") + << mode.resolution.x << mode.resolution.y << mode.bitDepth << mode.refreshRate; + } + } + + d->determineDevicePixelRatio(); static ImageFile::Interpreter intrpImageFile; fileSystem().addInterpreter(intrpImageFile); diff --git a/doomsday/libs/gui/src/persistentglwindow.cpp b/doomsday/libs/gui/src/persistentglwindow.cpp index 193313837c..ca71327dc7 100644 --- a/doomsday/libs/gui/src/persistentglwindow.cpp +++ b/doomsday/libs/gui/src/persistentglwindow.cpp @@ -26,7 +26,6 @@ #include "de/PersistentGLWindow" #include "de/GuiApp" -#include "de/DisplayMode" #include #include @@ -96,17 +95,17 @@ DE_PIMPL(PersistentGLWindow) }; typedef int Flags; - String winId; - Rectanglei windowRect; ///< Window geometry in windowed mode. - Size fullSize; ///< Dimensions in a fullscreen mode. - int colorDepthBits = 0; - float refreshRate = 0.f; - Flags flags { None }; + String winId; + Rectanglei windowRect; ///< Window geometry in windowed mode. + Size fullSize; ///< Dimensions in a fullscreen mode. + duint colorDepthBits = 0; + float refreshRate = 0.f; + Flags flags{None}; - State(String const &id) : winId(id) + State(const String &id) : winId(id) {} - bool operator == (State const &other) const + bool operator==(const State &other) const { return (winId == other.winId && windowRect == other.windowRect && @@ -223,7 +222,7 @@ DE_PIMPL(PersistentGLWindow) fullSize = Size(fs.at(0).asInt(), fs.at(1).asInt()); } - colorDepthBits = config.geti(configName("colorDepth")); + colorDepthBits = config.getui(configName("colorDepth")); refreshRate = config.getf(configName("refreshRate")); setFlag(Centered, config.getb(configName("center"))); setFlag(Maximized, config.getb(configName("maximize"))); @@ -232,26 +231,26 @@ DE_PIMPL(PersistentGLWindow) setFlag(VSync, config.getb(configName("vsync"), true)); } - /** - * Determines if the window will overtake the entire screen. - */ - bool shouldCaptureScreen() const - { - return isFullscreen() && - !DisplayMode_IsEqual(displayMode(), DisplayMode_OriginalMode()); - } +// /** +// * Determines if the window will overtake the entire screen. +// */ +// bool shouldCaptureScreen() const +// { +// return isFullscreen() && +// !DisplayMode_IsEqual(displayMode(), DisplayMode_OriginalMode()); +// } /** * Determines the display mode that this state will use in * fullscreen mode. */ - DisplayMode const *displayMode() const - { + GLWindow::DisplayMode displayMode() const + { if (isFullscreen()) { - return DisplayMode_FindClosest(fullSize.x, fullSize.y, colorDepthBits, refreshRate); + return {Vec2i(fullSize.x, fullSize.y), colorDepthBits, roundi(refreshRate)}; } - return DisplayMode_OriginalMode(); + return {}; } void applyAttributes(int const *attribs) @@ -567,7 +566,7 @@ DE_PIMPL(PersistentGLWindow) // Update the cached state from the authoritative source: // the widget itself. - state = widgetState(); + state = currentState(); // The new modified state. State mod = state; @@ -605,8 +604,8 @@ DE_PIMPL(PersistentGLWindow) // If the display mode needs to change, we will have to defer the rest // of the state changes so that everything catches up after the change. TimeSpan defer = 0.0; - DisplayMode const *newMode = newState.displayMode(); - bool modeChanged = false; + const DisplayMode newMode = newState.displayMode(); +// bool modeChanged = false; if (!self().isVisible()) { @@ -615,7 +614,7 @@ DE_PIMPL(PersistentGLWindow) } // Change display mode, if necessary. - if (!DisplayMode_IsEqual(DisplayMode_Current(), newMode)) +/* if (!newMode.isDefault() && GLWindow::getMain().fullscreenDisplayMode() != newMode) { LOG_GL_NOTE("Changing display mode to %i x %i x %i (%.1f Hz)") << newMode->width << newMode->height << newMode->depth << newMode->refreshRate; @@ -630,7 +629,11 @@ DE_PIMPL(PersistentGLWindow) #else defer = .01; #endif - } + }*/ + + const auto oldWinMode = self().fullscreenDisplayMode(); + + self().setFullscreenDisplayMode(newMode); if (self().isVisible()) { @@ -655,11 +658,11 @@ DE_PIMPL(PersistentGLWindow) } else { - if (modeChanged) - { - queue << Task(Task::ShowNormal, defer); - defer = 0.01; - } +// if (modeChanged) +// { +// queue << Task(Task::ShowNormal, defer); +// defer = 0.01; +// } if (newState.isMaximized()) { @@ -676,14 +679,14 @@ DE_PIMPL(PersistentGLWindow) defer = 0.0; } - if (modeChanged) + if (oldWinMode != newMode) { -#ifdef MACOSX - if (newState.isFullscreen()) - { - queue << Task(Task::MacRaiseOverShield); - } -#endif +//#ifdef MACOSX +// if (newState.isFullscreen()) +// { +// queue << Task(Task::MacRaiseOverShield); +// } +//#endif queue << Task(Task::NotifyModeChange, .1); } @@ -780,16 +783,16 @@ DE_PIMPL(PersistentGLWindow) } /** - * Gets the current state of the Qt widget. + * Gets the current state of the window. */ - State widgetState() const + State currentState() const { State st(id); st.windowRect = self().windowRect(); st.fullSize = state.fullSize; - st.colorDepthBits = DisplayMode_Current()->depth; - st.refreshRate = DisplayMode_Current()->refreshRate; + st.colorDepthBits = self().fullscreenDisplayMode().bitDepth; + st.refreshRate = self().fullscreenDisplayMode().refreshRate; st.flags = (self().isMaximized()? State::Maximized : State::None) | @@ -808,7 +811,7 @@ DE_PIMPL(PersistentGLWindow) { if (queue.isEmpty()) { - state = widgetState(); + state = currentState(); } DE_FOR_PUBLIC_AUDIENCE2(AttributeChange, i) @@ -868,7 +871,7 @@ void PersistentGLWindow::saveToConfig() { try { - d->widgetState().saveToConfig(); + d->currentState().saveToConfig(); } catch (Error const &er) { @@ -886,7 +889,7 @@ void PersistentGLWindow::restoreFromConfig() void PersistentGLWindow::saveState() { - d->savedState = d->widgetState(); + d->savedState = d->currentState(); } void PersistentGLWindow::restoreState()