mirror of
https://github.com/stenzek/duckstation.git
synced 2024-11-22 21:39:40 +00:00
OpenGLDevice: Support both XCB and Xlib
Some checks failed
Create rolling release / Windows x64 Build (push) Has been cancelled
Create rolling release / Windows x64 SSE2 Build (push) Has been cancelled
Create rolling release / Windows ARM64 Build (push) Has been cancelled
Create rolling release / Linux x64 AppImage (push) Has been cancelled
Create rolling release / Linux x64 SSE2 AppImage (push) Has been cancelled
Create rolling release / Linux Flatpak Build (push) Has been cancelled
Create rolling release / MacOS Universal Build (push) Has been cancelled
Create rolling release / Create Release (push) Has been cancelled
Some checks failed
Create rolling release / Windows x64 Build (push) Has been cancelled
Create rolling release / Windows x64 SSE2 Build (push) Has been cancelled
Create rolling release / Windows ARM64 Build (push) Has been cancelled
Create rolling release / Linux x64 AppImage (push) Has been cancelled
Create rolling release / Linux x64 SSE2 AppImage (push) Has been cancelled
Create rolling release / Linux Flatpak Build (push) Has been cancelled
Create rolling release / MacOS Universal Build (push) Has been cancelled
Create rolling release / Create Release (push) Has been cancelled
Required for NVIDIA+XWayland.
This commit is contained in:
parent
816ef45199
commit
e69f0d3cce
@ -28,8 +28,8 @@ endif()
|
|||||||
|
|
||||||
if(ENABLE_X11)
|
if(ENABLE_X11)
|
||||||
find_package(X11 REQUIRED)
|
find_package(X11 REQUIRED)
|
||||||
if (NOT X11_Xrandr_FOUND)
|
if (NOT X11_xcb_FOUND OR NOT X11_xcb_randr_FOUND OR NOT X11_X11_xcb_FOUND)
|
||||||
message(FATAL_ERROR "XRandR extension is required")
|
message(FATAL_ERROR "XCB, XCB-randr and X11-xcb are required")
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
@ -58,9 +58,9 @@ int DisplayWidget::scaledWindowHeight() const
|
|||||||
static_cast<int>(std::ceil(static_cast<qreal>(height()) * QtUtils::GetDevicePixelRatioForWidget(this))), 1);
|
static_cast<int>(std::ceil(static_cast<qreal>(height()) * QtUtils::GetDevicePixelRatioForWidget(this))), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<WindowInfo> DisplayWidget::getWindowInfo(Error* error)
|
std::optional<WindowInfo> DisplayWidget::getWindowInfo(RenderAPI render_api, Error* error)
|
||||||
{
|
{
|
||||||
std::optional<WindowInfo> ret(QtUtils::GetWindowInfoForWidget(this, error));
|
std::optional<WindowInfo> ret = QtUtils::GetWindowInfoForWidget(this, render_api, error);
|
||||||
if (ret.has_value())
|
if (ret.has_value())
|
||||||
{
|
{
|
||||||
m_last_window_width = ret->surface_width;
|
m_last_window_width = ret->surface_width;
|
||||||
|
@ -13,6 +13,8 @@
|
|||||||
|
|
||||||
class Error;
|
class Error;
|
||||||
|
|
||||||
|
enum class RenderAPI : u8;
|
||||||
|
|
||||||
class QCloseEvent;
|
class QCloseEvent;
|
||||||
|
|
||||||
class DisplayWidget final : public QWidget
|
class DisplayWidget final : public QWidget
|
||||||
@ -28,7 +30,7 @@ public:
|
|||||||
int scaledWindowWidth() const;
|
int scaledWindowWidth() const;
|
||||||
int scaledWindowHeight() const;
|
int scaledWindowHeight() const;
|
||||||
|
|
||||||
std::optional<WindowInfo> getWindowInfo(Error* error);
|
std::optional<WindowInfo> getWindowInfo(RenderAPI render_api, Error* error);
|
||||||
|
|
||||||
void updateRelativeMode(bool enabled);
|
void updateRelativeMode(bool enabled);
|
||||||
void updateCursor(bool hidden);
|
void updateCursor(bool hidden);
|
||||||
|
@ -221,8 +221,8 @@ bool MainWindow::nativeEvent(const QByteArray& eventType, void* message, qintptr
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
std::optional<WindowInfo> MainWindow::acquireRenderWindow(bool fullscreen, bool render_to_main, bool surfaceless,
|
std::optional<WindowInfo> MainWindow::acquireRenderWindow(RenderAPI render_api, bool fullscreen, bool render_to_main,
|
||||||
bool use_main_window_pos, Error* error)
|
bool surfaceless, bool use_main_window_pos, Error* error)
|
||||||
{
|
{
|
||||||
DEV_LOG("acquireRenderWindow() fullscreen={} render_to_main={} surfaceless={} use_main_window_pos={}",
|
DEV_LOG("acquireRenderWindow() fullscreen={} render_to_main={} surfaceless={} use_main_window_pos={}",
|
||||||
fullscreen ? "true" : "false", render_to_main ? "true" : "false", surfaceless ? "true" : "false",
|
fullscreen ? "true" : "false", render_to_main ? "true" : "false", surfaceless ? "true" : "false",
|
||||||
@ -265,7 +265,7 @@ std::optional<WindowInfo> MainWindow::acquireRenderWindow(bool fullscreen, bool
|
|||||||
updateWindowState();
|
updateWindowState();
|
||||||
|
|
||||||
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
|
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
|
||||||
return m_display_widget->getWindowInfo(error);
|
return m_display_widget->getWindowInfo(render_api, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
destroyDisplayWidget(surfaceless);
|
destroyDisplayWidget(surfaceless);
|
||||||
@ -277,7 +277,7 @@ std::optional<WindowInfo> MainWindow::acquireRenderWindow(bool fullscreen, bool
|
|||||||
|
|
||||||
createDisplayWidget(fullscreen, render_to_main, use_main_window_pos);
|
createDisplayWidget(fullscreen, render_to_main, use_main_window_pos);
|
||||||
|
|
||||||
std::optional<WindowInfo> wi = m_display_widget->getWindowInfo(error);
|
std::optional<WindowInfo> wi = m_display_widget->getWindowInfo(render_api, error);
|
||||||
if (!wi.has_value())
|
if (!wi.has_value())
|
||||||
{
|
{
|
||||||
QMessageBox::critical(this, tr("Error"), tr("Failed to get window info from widget"));
|
QMessageBox::critical(this, tr("Error"), tr("Failed to get window info from widget"));
|
||||||
@ -2483,9 +2483,9 @@ void MainWindow::checkForSettingChanges()
|
|||||||
std::optional<WindowInfo> MainWindow::getWindowInfo()
|
std::optional<WindowInfo> MainWindow::getWindowInfo()
|
||||||
{
|
{
|
||||||
if (!m_display_widget || isRenderingToMain())
|
if (!m_display_widget || isRenderingToMain())
|
||||||
return QtUtils::GetWindowInfoForWidget(this);
|
return QtUtils::GetWindowInfoForWidget(this, RenderAPI::None);
|
||||||
else if (QWidget* widget = getDisplayContainer())
|
else if (QWidget* widget = getDisplayContainer())
|
||||||
return QtUtils::GetWindowInfoForWidget(widget);
|
return QtUtils::GetWindowInfoForWidget(widget, RenderAPI::None);
|
||||||
else
|
else
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
@ -2565,7 +2565,7 @@ void MainWindow::onAchievementsChallengeModeChanged(bool enabled)
|
|||||||
updateEmulationActions(false, System::IsValid(), enabled);
|
updateEmulationActions(false, System::IsValid(), enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MainWindow::onCreateAuxiliaryRenderWindow(qint32 x, qint32 y, quint32 width, quint32 height, const QString& title,
|
bool MainWindow::onCreateAuxiliaryRenderWindow(RenderAPI render_api, qint32 x, qint32 y, quint32 width, quint32 height, const QString& title,
|
||||||
const QString& icon_name, Host::AuxiliaryRenderWindowUserData userdata,
|
const QString& icon_name, Host::AuxiliaryRenderWindowUserData userdata,
|
||||||
Host::AuxiliaryRenderWindowHandle* handle, WindowInfo* wi, Error* error)
|
Host::AuxiliaryRenderWindowHandle* handle, WindowInfo* wi, Error* error)
|
||||||
{
|
{
|
||||||
@ -2573,7 +2573,7 @@ bool MainWindow::onCreateAuxiliaryRenderWindow(qint32 x, qint32 y, quint32 width
|
|||||||
if (!widget)
|
if (!widget)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const std::optional<WindowInfo> owi = QtUtils::GetWindowInfoForWidget(widget, error);
|
const std::optional<WindowInfo> owi = QtUtils::GetWindowInfoForWidget(widget, render_api, error);
|
||||||
if (!owi.has_value())
|
if (!owi.has_value())
|
||||||
{
|
{
|
||||||
widget->destroy();
|
widget->destroy();
|
||||||
|
@ -35,6 +35,7 @@ class MemoryScannerWindow;
|
|||||||
|
|
||||||
struct SystemBootParameters;
|
struct SystemBootParameters;
|
||||||
|
|
||||||
|
enum class RenderAPI : u8;
|
||||||
class GPUDevice;
|
class GPUDevice;
|
||||||
namespace Achievements {
|
namespace Achievements {
|
||||||
enum class LoginRequestReason;
|
enum class LoginRequestReason;
|
||||||
@ -127,8 +128,8 @@ private Q_SLOTS:
|
|||||||
bool confirmMessage(const QString& title, const QString& message);
|
bool confirmMessage(const QString& title, const QString& message);
|
||||||
void onStatusMessage(const QString& message);
|
void onStatusMessage(const QString& message);
|
||||||
|
|
||||||
std::optional<WindowInfo> acquireRenderWindow(bool fullscreen, bool render_to_main, bool surfaceless,
|
std::optional<WindowInfo> acquireRenderWindow(RenderAPI render_api, bool fullscreen, bool render_to_main,
|
||||||
bool use_main_window_pos, Error* error);
|
bool surfaceless, bool use_main_window_pos, Error* error);
|
||||||
void displayResizeRequested(qint32 width, qint32 height);
|
void displayResizeRequested(qint32 width, qint32 height);
|
||||||
void releaseRenderWindow();
|
void releaseRenderWindow();
|
||||||
void focusDisplayWidget();
|
void focusDisplayWidget();
|
||||||
@ -145,8 +146,9 @@ private Q_SLOTS:
|
|||||||
void onMediaCaptureStopped();
|
void onMediaCaptureStopped();
|
||||||
void onAchievementsLoginRequested(Achievements::LoginRequestReason reason);
|
void onAchievementsLoginRequested(Achievements::LoginRequestReason reason);
|
||||||
void onAchievementsChallengeModeChanged(bool enabled);
|
void onAchievementsChallengeModeChanged(bool enabled);
|
||||||
bool onCreateAuxiliaryRenderWindow(qint32 x, qint32 y, quint32 width, quint32 height, const QString& title,
|
bool onCreateAuxiliaryRenderWindow(RenderAPI render_api, qint32 x, qint32 y, quint32 width, quint32 height,
|
||||||
const QString& icon_name, Host::AuxiliaryRenderWindowUserData userdata,
|
const QString& title, const QString& icon_name,
|
||||||
|
Host::AuxiliaryRenderWindowUserData userdata,
|
||||||
Host::AuxiliaryRenderWindowHandle* handle, WindowInfo* wi, Error* error);
|
Host::AuxiliaryRenderWindowHandle* handle, WindowInfo* wi, Error* error);
|
||||||
void onDestroyAuxiliaryRenderWindow(Host::AuxiliaryRenderWindowHandle handle, QPoint* pos, QSize* size);
|
void onDestroyAuxiliaryRenderWindow(Host::AuxiliaryRenderWindowHandle handle, QPoint* pos, QSize* size);
|
||||||
|
|
||||||
|
@ -133,6 +133,7 @@ void QtHost::RegisterTypes()
|
|||||||
qRegisterMetaType<std::function<void()>>("std::function<void()>");
|
qRegisterMetaType<std::function<void()>>("std::function<void()>");
|
||||||
qRegisterMetaType<std::shared_ptr<SystemBootParameters>>();
|
qRegisterMetaType<std::shared_ptr<SystemBootParameters>>();
|
||||||
qRegisterMetaType<const GameList::Entry*>();
|
qRegisterMetaType<const GameList::Entry*>();
|
||||||
|
qRegisterMetaType<RenderAPI>("RenderAPI");
|
||||||
qRegisterMetaType<GPURenderer>("GPURenderer");
|
qRegisterMetaType<GPURenderer>("GPURenderer");
|
||||||
qRegisterMetaType<InputBindingKey>("InputBindingKey");
|
qRegisterMetaType<InputBindingKey>("InputBindingKey");
|
||||||
qRegisterMetaType<std::string>("std::string");
|
qRegisterMetaType<std::string>("std::string");
|
||||||
@ -954,7 +955,8 @@ void EmuThread::requestDisplaySize(float scale)
|
|||||||
System::RequestDisplaySize(scale);
|
System::RequestDisplaySize(scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<WindowInfo> EmuThread::acquireRenderWindow(bool fullscreen, bool exclusive_fullscreen, Error* error)
|
std::optional<WindowInfo> EmuThread::acquireRenderWindow(RenderAPI render_api, bool fullscreen,
|
||||||
|
bool exclusive_fullscreen, Error* error)
|
||||||
{
|
{
|
||||||
DebugAssert(g_gpu_device);
|
DebugAssert(g_gpu_device);
|
||||||
|
|
||||||
@ -964,8 +966,8 @@ std::optional<WindowInfo> EmuThread::acquireRenderWindow(bool fullscreen, bool e
|
|||||||
const bool render_to_main = !fullscreen && m_is_rendering_to_main;
|
const bool render_to_main = !fullscreen && m_is_rendering_to_main;
|
||||||
const bool use_main_window_pos = shouldRenderToMain();
|
const bool use_main_window_pos = shouldRenderToMain();
|
||||||
|
|
||||||
return emit onAcquireRenderWindowRequested(window_fullscreen, render_to_main, m_is_surfaceless, use_main_window_pos,
|
return emit onAcquireRenderWindowRequested(render_api, window_fullscreen, render_to_main, m_is_surfaceless,
|
||||||
error);
|
use_main_window_pos, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuThread::releaseRenderWindow()
|
void EmuThread::releaseRenderWindow()
|
||||||
@ -1655,9 +1657,9 @@ bool Host::CreateAuxiliaryRenderWindow(s32 x, s32 y, u32 width, u32 height, std:
|
|||||||
std::string_view icon_name, AuxiliaryRenderWindowUserData userdata,
|
std::string_view icon_name, AuxiliaryRenderWindowUserData userdata,
|
||||||
AuxiliaryRenderWindowHandle* handle, WindowInfo* wi, Error* error)
|
AuxiliaryRenderWindowHandle* handle, WindowInfo* wi, Error* error)
|
||||||
{
|
{
|
||||||
return emit g_emu_thread->onCreateAuxiliaryRenderWindow(x, y, width, height, QtUtils::StringViewToQString(title),
|
return emit g_emu_thread->onCreateAuxiliaryRenderWindow(
|
||||||
QtUtils::StringViewToQString(icon_name), userdata, handle, wi,
|
g_gpu_device->GetRenderAPI(), x, y, width, height, QtUtils::StringViewToQString(title),
|
||||||
error);
|
QtUtils::StringViewToQString(icon_name), userdata, handle, wi, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Host::DestroyAuxiliaryRenderWindow(AuxiliaryRenderWindowHandle handle, s32* pos_x, s32* pos_y, u32* width,
|
void Host::DestroyAuxiliaryRenderWindow(AuxiliaryRenderWindowHandle handle, s32* pos_x, s32* pos_y, u32* width,
|
||||||
@ -2002,7 +2004,7 @@ void Host::CommitBaseSettingChanges()
|
|||||||
std::optional<WindowInfo> Host::AcquireRenderWindow(RenderAPI render_api, bool fullscreen, bool exclusive_fullscreen,
|
std::optional<WindowInfo> Host::AcquireRenderWindow(RenderAPI render_api, bool fullscreen, bool exclusive_fullscreen,
|
||||||
Error* error)
|
Error* error)
|
||||||
{
|
{
|
||||||
return g_emu_thread->acquireRenderWindow(fullscreen, exclusive_fullscreen, error);
|
return g_emu_thread->acquireRenderWindow(render_api, fullscreen, exclusive_fullscreen, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Host::ReleaseRenderWindow()
|
void Host::ReleaseRenderWindow()
|
||||||
|
@ -41,6 +41,7 @@ class QTranslator;
|
|||||||
|
|
||||||
class INISettingsInterface;
|
class INISettingsInterface;
|
||||||
|
|
||||||
|
enum class RenderAPI : u8;
|
||||||
class GPUDevice;
|
class GPUDevice;
|
||||||
|
|
||||||
class MainWindow;
|
class MainWindow;
|
||||||
@ -94,7 +95,8 @@ public:
|
|||||||
ALWAYS_INLINE bool isSurfaceless() const { return m_is_surfaceless; }
|
ALWAYS_INLINE bool isSurfaceless() const { return m_is_surfaceless; }
|
||||||
ALWAYS_INLINE bool isRunningFullscreenUI() const { return m_run_fullscreen_ui; }
|
ALWAYS_INLINE bool isRunningFullscreenUI() const { return m_run_fullscreen_ui; }
|
||||||
|
|
||||||
std::optional<WindowInfo> acquireRenderWindow(bool fullscreen, bool exclusive_fullscreen, Error* error);
|
std::optional<WindowInfo> acquireRenderWindow(RenderAPI render_api, bool fullscreen, bool exclusive_fullscreen,
|
||||||
|
Error* error);
|
||||||
void connectDisplaySignals(DisplayWidget* widget);
|
void connectDisplaySignals(DisplayWidget* widget);
|
||||||
void releaseRenderWindow();
|
void releaseRenderWindow();
|
||||||
|
|
||||||
@ -137,8 +139,8 @@ Q_SIGNALS:
|
|||||||
void systemPaused();
|
void systemPaused();
|
||||||
void systemResumed();
|
void systemResumed();
|
||||||
void gameListRefreshed();
|
void gameListRefreshed();
|
||||||
std::optional<WindowInfo> onAcquireRenderWindowRequested(bool fullscreen, bool render_to_main, bool surfaceless,
|
std::optional<WindowInfo> onAcquireRenderWindowRequested(RenderAPI render_api, bool fullscreen, bool render_to_main,
|
||||||
bool use_main_window_pos, Error* error);
|
bool surfaceless, bool use_main_window_pos, Error* error);
|
||||||
void onResizeRenderWindowRequested(qint32 width, qint32 height);
|
void onResizeRenderWindowRequested(qint32 width, qint32 height);
|
||||||
void onReleaseRenderWindowRequested();
|
void onReleaseRenderWindowRequested();
|
||||||
void focusDisplayWidgetRequested();
|
void focusDisplayWidgetRequested();
|
||||||
@ -153,8 +155,9 @@ Q_SIGNALS:
|
|||||||
void mediaCaptureStarted();
|
void mediaCaptureStarted();
|
||||||
void mediaCaptureStopped();
|
void mediaCaptureStopped();
|
||||||
|
|
||||||
bool onCreateAuxiliaryRenderWindow(qint32 x, qint32 y, quint32 width, quint32 height, const QString& title,
|
bool onCreateAuxiliaryRenderWindow(RenderAPI render_api, qint32 x, qint32 y, quint32 width, quint32 height,
|
||||||
const QString& icon_name, Host::AuxiliaryRenderWindowUserData userdata,
|
const QString& title, const QString& icon_name,
|
||||||
|
Host::AuxiliaryRenderWindowUserData userdata,
|
||||||
Host::AuxiliaryRenderWindowHandle* handle, WindowInfo* wi, Error* error);
|
Host::AuxiliaryRenderWindowHandle* handle, WindowInfo* wi, Error* error);
|
||||||
void onDestroyAuxiliaryRenderWindow(Host::AuxiliaryRenderWindowHandle handle, QPoint* pos, QSize* size);
|
void onDestroyAuxiliaryRenderWindow(Host::AuxiliaryRenderWindowHandle handle, QPoint* pos, QSize* size);
|
||||||
|
|
||||||
|
@ -7,6 +7,8 @@
|
|||||||
#include "core/game_list.h"
|
#include "core/game_list.h"
|
||||||
#include "core/system.h"
|
#include "core/system.h"
|
||||||
|
|
||||||
|
#include "util/gpu_device.h"
|
||||||
|
|
||||||
#include "common/error.h"
|
#include "common/error.h"
|
||||||
#include "common/log.h"
|
#include "common/log.h"
|
||||||
|
|
||||||
@ -30,6 +32,8 @@
|
|||||||
#include <QtWidgets/QTreeView>
|
#include <QtWidgets/QTreeView>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
#if !defined(_WIN32) && !defined(APPLE)
|
#if !defined(_WIN32) && !defined(APPLE)
|
||||||
@ -317,7 +321,7 @@ qreal QtUtils::GetDevicePixelRatioForWidget(const QWidget* widget)
|
|||||||
return screen_for_ratio ? screen_for_ratio->devicePixelRatio() : static_cast<qreal>(1);
|
return screen_for_ratio ? screen_for_ratio->devicePixelRatio() : static_cast<qreal>(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<WindowInfo> QtUtils::GetWindowInfoForWidget(QWidget* widget, Error* error)
|
std::optional<WindowInfo> QtUtils::GetWindowInfoForWidget(QWidget* widget, RenderAPI render_api, Error* error)
|
||||||
{
|
{
|
||||||
WindowInfo wi;
|
WindowInfo wi;
|
||||||
|
|
||||||
@ -333,8 +337,20 @@ std::optional<WindowInfo> QtUtils::GetWindowInfoForWidget(QWidget* widget, Error
|
|||||||
const QString platform_name = QGuiApplication::platformName();
|
const QString platform_name = QGuiApplication::platformName();
|
||||||
if (platform_name == QStringLiteral("xcb"))
|
if (platform_name == QStringLiteral("xcb"))
|
||||||
{
|
{
|
||||||
wi.type = WindowInfo::Type::X11;
|
// This is fucking ridiculous. NVIDIA+XWayland doesn't support Xlib, and NVIDIA+Xorg doesn't support XCB.
|
||||||
wi.display_connection = pni->nativeResourceForWindow("display", widget->windowHandle());
|
// Use Xlib if we're not running under Wayland, or we're not requesting OpenGL. Vulkan+XCB seems fine.
|
||||||
|
const char* xdg_session_type = std::getenv("XDG_SESSION_TYPE");
|
||||||
|
const bool is_running_on_xwayland = (xdg_session_type && std::strstr(xdg_session_type, "wayland"));
|
||||||
|
if (is_running_on_xwayland || render_api == RenderAPI::Vulkan)
|
||||||
|
{
|
||||||
|
wi.type = WindowInfo::Type::XCB;
|
||||||
|
wi.display_connection = pni->nativeResourceForWindow("connection", widget->windowHandle());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
wi.type = WindowInfo::Type::Xlib;
|
||||||
|
wi.display_connection = pni->nativeResourceForWindow("display", widget->windowHandle());
|
||||||
|
}
|
||||||
wi.window_handle = reinterpret_cast<void*>(widget->winId());
|
wi.window_handle = reinterpret_cast<void*>(widget->winId());
|
||||||
}
|
}
|
||||||
else if (platform_name == QStringLiteral("wayland"))
|
else if (platform_name == QStringLiteral("wayland"))
|
||||||
@ -356,9 +372,12 @@ std::optional<WindowInfo> QtUtils::GetWindowInfoForWidget(QWidget* widget, Error
|
|||||||
wi.surface_scale = static_cast<float>(dpr);
|
wi.surface_scale = static_cast<float>(dpr);
|
||||||
|
|
||||||
// Query refresh rate, we need it for sync.
|
// Query refresh rate, we need it for sync.
|
||||||
std::optional<float> surface_refresh_rate = WindowInfo::QueryRefreshRateForWindow(wi);
|
Error refresh_rate_error;
|
||||||
|
std::optional<float> surface_refresh_rate = WindowInfo::QueryRefreshRateForWindow(wi, &refresh_rate_error);
|
||||||
if (!surface_refresh_rate.has_value())
|
if (!surface_refresh_rate.has_value())
|
||||||
{
|
{
|
||||||
|
WARNING_LOG("Failed to get refresh rate for window, falling back to Qt: {}", refresh_rate_error.GetDescription());
|
||||||
|
|
||||||
// Fallback to using the screen, getting the rate for Wayland is an utter mess otherwise.
|
// Fallback to using the screen, getting the rate for Wayland is an utter mess otherwise.
|
||||||
const QScreen* widget_screen = widget->screen();
|
const QScreen* widget_screen = widget->screen();
|
||||||
if (!widget_screen)
|
if (!widget_screen)
|
||||||
|
@ -32,6 +32,8 @@ class QVariant;
|
|||||||
class QWidget;
|
class QWidget;
|
||||||
class QUrl;
|
class QUrl;
|
||||||
|
|
||||||
|
enum class RenderAPI : u8;
|
||||||
|
|
||||||
enum class ConsoleRegion : u8;
|
enum class ConsoleRegion : u8;
|
||||||
enum class DiscRegion : u8;
|
enum class DiscRegion : u8;
|
||||||
namespace GameDatabase {
|
namespace GameDatabase {
|
||||||
@ -116,7 +118,7 @@ QIcon GetIconForCompatibility(GameDatabase::CompatibilityRating rating);
|
|||||||
qreal GetDevicePixelRatioForWidget(const QWidget* widget);
|
qreal GetDevicePixelRatioForWidget(const QWidget* widget);
|
||||||
|
|
||||||
/// Returns the common window info structure for a Qt widget.
|
/// Returns the common window info structure for a Qt widget.
|
||||||
std::optional<WindowInfo> GetWindowInfoForWidget(QWidget* widget, Error* error = nullptr);
|
std::optional<WindowInfo> GetWindowInfoForWidget(QWidget* widget, RenderAPI render_api, Error* error = nullptr);
|
||||||
|
|
||||||
/// Saves a window's geometry to configuration. Returns false if the configuration was changed.
|
/// Saves a window's geometry to configuration. Returns false if the configuration was changed.
|
||||||
bool SaveWindowGeometry(std::string_view window_name, QWidget* widget, bool auto_commit_changes = true);
|
bool SaveWindowGeometry(std::string_view window_name, QWidget* widget, bool auto_commit_changes = true);
|
||||||
|
@ -80,8 +80,12 @@ target_link_libraries(util PUBLIC common simpleini imgui)
|
|||||||
target_link_libraries(util PRIVATE libchdr lzma JPEG::JPEG PNG::PNG WebP::libwebp lunasvg::lunasvg ZLIB::ZLIB SoundTouch::SoundTouchDLL xxhash Zstd::Zstd reshadefx)
|
target_link_libraries(util PRIVATE libchdr lzma JPEG::JPEG PNG::PNG WebP::libwebp lunasvg::lunasvg ZLIB::ZLIB SoundTouch::SoundTouchDLL xxhash Zstd::Zstd reshadefx)
|
||||||
|
|
||||||
if(ENABLE_X11)
|
if(ENABLE_X11)
|
||||||
|
target_sources(util PRIVATE
|
||||||
|
x11_tools.cpp
|
||||||
|
x11_tools.h
|
||||||
|
)
|
||||||
target_compile_definitions(util PRIVATE "-DENABLE_X11=1")
|
target_compile_definitions(util PRIVATE "-DENABLE_X11=1")
|
||||||
target_link_libraries(util PRIVATE X11::X11 X11::Xrandr)
|
target_link_libraries(util PRIVATE X11::xcb X11::xcb_randr X11::X11_xcb)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(ENABLE_WAYLAND)
|
if(ENABLE_WAYLAND)
|
||||||
@ -121,8 +125,10 @@ if(ENABLE_OPENGL)
|
|||||||
|
|
||||||
if(ENABLE_X11)
|
if(ENABLE_X11)
|
||||||
target_sources(util PRIVATE
|
target_sources(util PRIVATE
|
||||||
opengl_context_egl_x11.cpp
|
opengl_context_egl_xcb.cpp
|
||||||
opengl_context_egl_x11.h
|
opengl_context_egl_xcb.h
|
||||||
|
opengl_context_egl_xlib.cpp
|
||||||
|
opengl_context_egl_xlib.h
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
if(ENABLE_WAYLAND)
|
if(ENABLE_WAYLAND)
|
||||||
|
@ -27,7 +27,8 @@
|
|||||||
#include "opengl_context_egl_wayland.h"
|
#include "opengl_context_egl_wayland.h"
|
||||||
#endif
|
#endif
|
||||||
#ifdef ENABLE_X11
|
#ifdef ENABLE_X11
|
||||||
#include "opengl_context_egl_x11.h"
|
#include "opengl_context_egl_xcb.h"
|
||||||
|
#include "opengl_context_egl_xlib.h"
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
@ -154,8 +155,10 @@ std::unique_ptr<OpenGLContext> OpenGLContext::Create(WindowInfo& wi, SurfaceHand
|
|||||||
context = OpenGLContextEGLAndroid::Create(wi, surface, versions_to_try, error);
|
context = OpenGLContextEGLAndroid::Create(wi, surface, versions_to_try, error);
|
||||||
#else
|
#else
|
||||||
#if defined(ENABLE_X11)
|
#if defined(ENABLE_X11)
|
||||||
if (wi.type == WindowInfo::Type::X11)
|
if (wi.type == WindowInfo::Type::Xlib)
|
||||||
context = OpenGLContextEGLX11::Create(wi, surface, versions_to_try, error);
|
context = OpenGLContextEGLXlib::Create(wi, surface, versions_to_try, error);
|
||||||
|
else if (wi.type == WindowInfo::Type::XCB)
|
||||||
|
context = OpenGLContextEGLXCB::Create(wi, surface, versions_to_try, error);
|
||||||
#endif
|
#endif
|
||||||
#if defined(ENABLE_WAYLAND)
|
#if defined(ENABLE_WAYLAND)
|
||||||
if (wi.type == WindowInfo::Type::Wayland)
|
if (wi.type == WindowInfo::Type::Wayland)
|
||||||
|
105
src/util/opengl_context_egl_xcb.cpp
Normal file
105
src/util/opengl_context_egl_xcb.cpp
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
|
||||||
|
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
|
||||||
|
|
||||||
|
#include "opengl_context_egl_xcb.h"
|
||||||
|
|
||||||
|
#include "common/error.h"
|
||||||
|
#include "common/log.h"
|
||||||
|
|
||||||
|
LOG_CHANNEL(GPUDevice);
|
||||||
|
|
||||||
|
OpenGLContextEGLXCB::OpenGLContextEGLXCB() = default;
|
||||||
|
|
||||||
|
OpenGLContextEGLXCB::~OpenGLContextEGLXCB() = default;
|
||||||
|
|
||||||
|
std::unique_ptr<OpenGLContext> OpenGLContextEGLXCB::Create(WindowInfo& wi, SurfaceHandle* surface,
|
||||||
|
std::span<const Version> versions_to_try, Error* error)
|
||||||
|
{
|
||||||
|
std::unique_ptr<OpenGLContextEGLXCB> context = std::make_unique<OpenGLContextEGLXCB>();
|
||||||
|
if (!context->Initialize(wi, surface, versions_to_try, error))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<OpenGLContext> OpenGLContextEGLXCB::CreateSharedContext(WindowInfo& wi, SurfaceHandle* surface,
|
||||||
|
Error* error)
|
||||||
|
{
|
||||||
|
std::unique_ptr<OpenGLContextEGLXCB> context = std::make_unique<OpenGLContextEGLXCB>();
|
||||||
|
context->m_display = m_display;
|
||||||
|
|
||||||
|
if (!context->CreateContextAndSurface(wi, surface, m_version, m_context, false, error))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLDisplay OpenGLContextEGLXCB::GetPlatformDisplay(const WindowInfo& wi, Error* error)
|
||||||
|
{
|
||||||
|
EGLDisplay dpy = TryGetPlatformDisplay(wi.display_connection, EGL_PLATFORM_XCB_EXT, "EGL_EXT_platform_xcb");
|
||||||
|
m_using_platform_display = (dpy != EGL_NO_DISPLAY);
|
||||||
|
if (!m_using_platform_display)
|
||||||
|
dpy = GetFallbackDisplay(wi.display_connection, error);
|
||||||
|
|
||||||
|
return dpy;
|
||||||
|
}
|
||||||
|
|
||||||
|
EGLSurface OpenGLContextEGLXCB::CreatePlatformSurface(EGLConfig config, const WindowInfo& wi, Error* error)
|
||||||
|
{
|
||||||
|
// Try YOLO'ing it, if the depth/visual is compatible we don't need to create a subwindow.
|
||||||
|
// Seems to be the case with Mesa, but not on NVIDIA.
|
||||||
|
xcb_window_t xcb_window = static_cast<xcb_window_t>(reinterpret_cast<uintptr_t>(wi.window_handle));
|
||||||
|
EGLSurface surface = m_using_platform_display ? TryCreatePlatformSurface(config, &xcb_window, error) :
|
||||||
|
CreateFallbackSurface(config, wi.window_handle, error);
|
||||||
|
if (surface != EGL_NO_SURFACE)
|
||||||
|
{
|
||||||
|
// Yay, no subwindow.
|
||||||
|
return surface;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Why do we need this shit? XWayland on NVIDIA....
|
||||||
|
EGLint native_visual_id = 0;
|
||||||
|
if (!eglGetConfigAttrib(m_display, m_config, EGL_NATIVE_VISUAL_ID, &native_visual_id))
|
||||||
|
{
|
||||||
|
Error::SetStringView(error, "Failed to get XCB visual ID");
|
||||||
|
return EGL_NO_SURFACE;
|
||||||
|
}
|
||||||
|
|
||||||
|
X11Window subwindow;
|
||||||
|
if (!subwindow.Create(static_cast<xcb_connection_t*>(wi.display_connection), xcb_window,
|
||||||
|
static_cast<xcb_visualid_t>(native_visual_id), error))
|
||||||
|
{
|
||||||
|
Error::AddPrefix(error, "Failed to create subwindow");
|
||||||
|
return EGL_NO_SURFACE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is hideous.. the EXT version requires a pointer to the window, whereas the base
|
||||||
|
// version requires the window itself, casted to void*...
|
||||||
|
surface = TryCreatePlatformSurface(config, subwindow.GetWindowPtr(), error);
|
||||||
|
if (surface == EGL_NO_SURFACE)
|
||||||
|
surface = CreateFallbackSurface(config, wi.window_handle, error);
|
||||||
|
|
||||||
|
if (surface != EGL_NO_SURFACE)
|
||||||
|
{
|
||||||
|
DEV_LOG("Created {}x{} subwindow with visual ID {}", subwindow.GetWidth(), subwindow.GetHeight(), native_visual_id);
|
||||||
|
m_x11_windows.emplace(surface, std::move(subwindow));
|
||||||
|
}
|
||||||
|
|
||||||
|
return surface;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLContextEGLXCB::DestroyPlatformSurface(EGLSurface surface)
|
||||||
|
{
|
||||||
|
OpenGLContextEGL::DestroyPlatformSurface(surface);
|
||||||
|
|
||||||
|
auto it = m_x11_windows.find((EGLSurface)surface);
|
||||||
|
if (it != m_x11_windows.end())
|
||||||
|
m_x11_windows.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenGLContextEGLXCB::ResizeSurface(WindowInfo& wi, SurfaceHandle handle)
|
||||||
|
{
|
||||||
|
const auto it = m_x11_windows.find((EGLSurface)handle);
|
||||||
|
if (it != m_x11_windows.end())
|
||||||
|
it->second.Resize(wi.surface_width, wi.surface_height);
|
||||||
|
}
|
34
src/util/opengl_context_egl_xcb.h
Normal file
34
src/util/opengl_context_egl_xcb.h
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
|
||||||
|
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "opengl_context_egl.h"
|
||||||
|
#include "x11_tools.h"
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
class OpenGLContextEGLXCB final : public OpenGLContextEGL
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
OpenGLContextEGLXCB();
|
||||||
|
~OpenGLContextEGLXCB() override;
|
||||||
|
|
||||||
|
static std::unique_ptr<OpenGLContext> Create(WindowInfo& wi, SurfaceHandle* surface,
|
||||||
|
std::span<const Version> versions_to_try, Error* error);
|
||||||
|
|
||||||
|
std::unique_ptr<OpenGLContext> CreateSharedContext(WindowInfo& wi, SurfaceHandle* surface, Error* error) override;
|
||||||
|
|
||||||
|
void ResizeSurface(WindowInfo& wi, SurfaceHandle handle) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
EGLDisplay GetPlatformDisplay(const WindowInfo& wi, Error* error) override;
|
||||||
|
EGLSurface CreatePlatformSurface(EGLConfig config, const WindowInfo& wi, Error* error) override;
|
||||||
|
void DestroyPlatformSurface(EGLSurface surface) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
using X11WindowMap = std::unordered_map<EGLSurface, X11Window>;
|
||||||
|
|
||||||
|
X11WindowMap m_x11_windows;
|
||||||
|
bool m_using_platform_display = false;
|
||||||
|
};
|
@ -1,28 +1,28 @@
|
|||||||
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
|
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
|
||||||
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
|
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
|
||||||
|
|
||||||
#include "opengl_context_egl_x11.h"
|
#include "opengl_context_egl_xlib.h"
|
||||||
|
|
||||||
#include "common/error.h"
|
#include "common/error.h"
|
||||||
|
|
||||||
OpenGLContextEGLX11::OpenGLContextEGLX11() = default;
|
OpenGLContextEGLXlib::OpenGLContextEGLXlib() = default;
|
||||||
|
|
||||||
OpenGLContextEGLX11::~OpenGLContextEGLX11() = default;
|
OpenGLContextEGLXlib::~OpenGLContextEGLXlib() = default;
|
||||||
|
|
||||||
std::unique_ptr<OpenGLContext> OpenGLContextEGLX11::Create(WindowInfo& wi, SurfaceHandle* surface,
|
std::unique_ptr<OpenGLContext> OpenGLContextEGLXlib::Create(WindowInfo& wi, SurfaceHandle* surface,
|
||||||
std::span<const Version> versions_to_try, Error* error)
|
std::span<const Version> versions_to_try, Error* error)
|
||||||
{
|
{
|
||||||
std::unique_ptr<OpenGLContextEGLX11> context = std::make_unique<OpenGLContextEGLX11>();
|
std::unique_ptr<OpenGLContextEGLXlib> context = std::make_unique<OpenGLContextEGLXlib>();
|
||||||
if (!context->Initialize(wi, surface, versions_to_try, error))
|
if (!context->Initialize(wi, surface, versions_to_try, error))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<OpenGLContext> OpenGLContextEGLX11::CreateSharedContext(WindowInfo& wi, SurfaceHandle* surface,
|
std::unique_ptr<OpenGLContext> OpenGLContextEGLXlib::CreateSharedContext(WindowInfo& wi, SurfaceHandle* surface,
|
||||||
Error* error)
|
Error* error)
|
||||||
{
|
{
|
||||||
std::unique_ptr<OpenGLContextEGLX11> context = std::make_unique<OpenGLContextEGLX11>();
|
std::unique_ptr<OpenGLContextEGLXlib> context = std::make_unique<OpenGLContextEGLXlib>();
|
||||||
context->m_display = m_display;
|
context->m_display = m_display;
|
||||||
|
|
||||||
if (!context->CreateContextAndSurface(wi, surface, m_version, m_context, false, error))
|
if (!context->CreateContextAndSurface(wi, surface, m_version, m_context, false, error))
|
||||||
@ -31,7 +31,7 @@ std::unique_ptr<OpenGLContext> OpenGLContextEGLX11::CreateSharedContext(WindowIn
|
|||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
EGLDisplay OpenGLContextEGLX11::GetPlatformDisplay(const WindowInfo& wi, Error* error)
|
EGLDisplay OpenGLContextEGLXlib::GetPlatformDisplay(const WindowInfo& wi, Error* error)
|
||||||
{
|
{
|
||||||
EGLDisplay dpy = TryGetPlatformDisplay(wi.display_connection, EGL_PLATFORM_X11_KHR, "EGL_EXT_platform_x11");
|
EGLDisplay dpy = TryGetPlatformDisplay(wi.display_connection, EGL_PLATFORM_X11_KHR, "EGL_EXT_platform_x11");
|
||||||
if (dpy == EGL_NO_DISPLAY)
|
if (dpy == EGL_NO_DISPLAY)
|
||||||
@ -40,7 +40,7 @@ EGLDisplay OpenGLContextEGLX11::GetPlatformDisplay(const WindowInfo& wi, Error*
|
|||||||
return dpy;
|
return dpy;
|
||||||
}
|
}
|
||||||
|
|
||||||
EGLSurface OpenGLContextEGLX11::CreatePlatformSurface(EGLConfig config, const WindowInfo& wi, Error* error)
|
EGLSurface OpenGLContextEGLXlib::CreatePlatformSurface(EGLConfig config, const WindowInfo& wi, Error* error)
|
||||||
{
|
{
|
||||||
// This is hideous.. the EXT version requires a pointer to the window, whereas the base
|
// This is hideous.. the EXT version requires a pointer to the window, whereas the base
|
||||||
// version requires the window itself, casted to void*...
|
// version requires the window itself, casted to void*...
|
@ -5,11 +5,11 @@
|
|||||||
|
|
||||||
#include "opengl_context_egl.h"
|
#include "opengl_context_egl.h"
|
||||||
|
|
||||||
class OpenGLContextEGLX11 final : public OpenGLContextEGL
|
class OpenGLContextEGLXlib final : public OpenGLContextEGL
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
OpenGLContextEGLX11();
|
OpenGLContextEGLXlib();
|
||||||
~OpenGLContextEGLX11() override;
|
~OpenGLContextEGLXlib() override;
|
||||||
|
|
||||||
static std::unique_ptr<OpenGLContext> Create(WindowInfo& wi, SurfaceHandle* surface,
|
static std::unique_ptr<OpenGLContext> Create(WindowInfo& wi, SurfaceHandle* surface,
|
||||||
std::span<const Version> versions_to_try, Error* error);
|
std::span<const Version> versions_to_try, Error* error);
|
@ -25,5 +25,5 @@ std::optional<WindowInfo> GetTopLevelWindowInfo();
|
|||||||
// TODO: Move all the other Cocoa stuff in here.
|
// TODO: Move all the other Cocoa stuff in here.
|
||||||
namespace CocoaTools {
|
namespace CocoaTools {
|
||||||
/// Returns the refresh rate of the display the window is placed on.
|
/// Returns the refresh rate of the display the window is placed on.
|
||||||
std::optional<float> GetViewRefreshRate(const WindowInfo& wi);
|
std::optional<float> GetViewRefreshRate(const WindowInfo& wi, Error* error);
|
||||||
}
|
} // namespace CocoaTools
|
||||||
|
@ -129,12 +129,12 @@ void CocoaTools::DestroyMetalLayer(const WindowInfo& wi, void* layer)
|
|||||||
[clayer release];
|
[clayer release];
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<float> CocoaTools::GetViewRefreshRate(const WindowInfo& wi)
|
std::optional<float> CocoaTools::GetViewRefreshRate(const WindowInfo& wi, Error* error)
|
||||||
{
|
{
|
||||||
if (![NSThread isMainThread])
|
if (![NSThread isMainThread])
|
||||||
{
|
{
|
||||||
std::optional<float> ret;
|
std::optional<float> ret;
|
||||||
dispatch_sync(dispatch_get_main_queue(), [&ret, wi] { ret = GetViewRefreshRate(wi); });
|
dispatch_sync(dispatch_get_main_queue(), [&ret, wi, error] { ret = GetViewRefreshRate(wi, error); });
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,6 +146,10 @@ std::optional<float> CocoaTools::GetViewRefreshRate(const WindowInfo& wi)
|
|||||||
ret = CGDisplayModeGetRefreshRate(mode);
|
ret = CGDisplayModeGetRefreshRate(mode);
|
||||||
CGDisplayModeRelease(mode);
|
CGDisplayModeRelease(mode);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Error::SetStringView(error, "CGDisplayCopyDisplayMode() failed");
|
||||||
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,10 @@
|
|||||||
<ClInclude Include="opengl_context_egl_wayland.h">
|
<ClInclude Include="opengl_context_egl_wayland.h">
|
||||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="opengl_context_egl_x11.h">
|
<ClInclude Include="opengl_context_egl_xcb.h">
|
||||||
|
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="opengl_context_egl_xlib.h">
|
||||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="opengl_context_wgl.h">
|
<ClInclude Include="opengl_context_wgl.h">
|
||||||
@ -101,6 +104,9 @@
|
|||||||
<ClInclude Include="wav_reader_writer.h" />
|
<ClInclude Include="wav_reader_writer.h" />
|
||||||
<ClInclude Include="win32_raw_input_source.h" />
|
<ClInclude Include="win32_raw_input_source.h" />
|
||||||
<ClInclude Include="window_info.h" />
|
<ClInclude Include="window_info.h" />
|
||||||
|
<ClInclude Include="x11_tools.h">
|
||||||
|
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||||
|
</ClInclude>
|
||||||
<ClInclude Include="xinput_source.h" />
|
<ClInclude Include="xinput_source.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@ -159,7 +165,10 @@
|
|||||||
<ClCompile Include="opengl_context_egl_wayland.cpp">
|
<ClCompile Include="opengl_context_egl_wayland.cpp">
|
||||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="opengl_context_egl_x11.cpp">
|
<ClCompile Include="opengl_context_egl_xcb.cpp">
|
||||||
|
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="opengl_context_egl_xlib.cpp">
|
||||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="opengl_context_wgl.cpp">
|
<ClCompile Include="opengl_context_wgl.cpp">
|
||||||
@ -202,6 +211,9 @@
|
|||||||
<ClCompile Include="wav_reader_writer.cpp" />
|
<ClCompile Include="wav_reader_writer.cpp" />
|
||||||
<ClCompile Include="win32_raw_input_source.cpp" />
|
<ClCompile Include="win32_raw_input_source.cpp" />
|
||||||
<ClCompile Include="window_info.cpp" />
|
<ClCompile Include="window_info.cpp" />
|
||||||
|
<ClCompile Include="x11_tools.cpp">
|
||||||
|
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="xinput_source.cpp" />
|
<ClCompile Include="xinput_source.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -66,13 +66,15 @@
|
|||||||
<ClInclude Include="opengl_context_agl.h" />
|
<ClInclude Include="opengl_context_agl.h" />
|
||||||
<ClInclude Include="opengl_context_egl.h" />
|
<ClInclude Include="opengl_context_egl.h" />
|
||||||
<ClInclude Include="opengl_context_egl_wayland.h" />
|
<ClInclude Include="opengl_context_egl_wayland.h" />
|
||||||
<ClInclude Include="opengl_context_egl_x11.h" />
|
<ClInclude Include="opengl_context_egl_xcb.h" />
|
||||||
<ClInclude Include="opengl_context_wgl.h" />
|
<ClInclude Include="opengl_context_wgl.h" />
|
||||||
<ClInclude Include="image.h" />
|
<ClInclude Include="image.h" />
|
||||||
<ClInclude Include="sockets.h" />
|
<ClInclude Include="sockets.h" />
|
||||||
<ClInclude Include="media_capture.h" />
|
<ClInclude Include="media_capture.h" />
|
||||||
<ClInclude Include="compress_helpers.h" />
|
<ClInclude Include="compress_helpers.h" />
|
||||||
<ClInclude Include="elf_file.h" />
|
<ClInclude Include="elf_file.h" />
|
||||||
|
<ClInclude Include="x11_tools.h" />
|
||||||
|
<ClInclude Include="opengl_context_egl_xlib.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="state_wrapper.cpp" />
|
<ClCompile Include="state_wrapper.cpp" />
|
||||||
@ -146,7 +148,7 @@
|
|||||||
<ClCompile Include="opengl_context.cpp" />
|
<ClCompile Include="opengl_context.cpp" />
|
||||||
<ClCompile Include="opengl_context_egl.cpp" />
|
<ClCompile Include="opengl_context_egl.cpp" />
|
||||||
<ClCompile Include="opengl_context_egl_wayland.cpp" />
|
<ClCompile Include="opengl_context_egl_wayland.cpp" />
|
||||||
<ClCompile Include="opengl_context_egl_x11.cpp" />
|
<ClCompile Include="opengl_context_egl_xcb.cpp" />
|
||||||
<ClCompile Include="opengl_context_wgl.cpp" />
|
<ClCompile Include="opengl_context_wgl.cpp" />
|
||||||
<ClCompile Include="image.cpp" />
|
<ClCompile Include="image.cpp" />
|
||||||
<ClCompile Include="sdl_audio_stream.cpp" />
|
<ClCompile Include="sdl_audio_stream.cpp" />
|
||||||
@ -154,6 +156,8 @@
|
|||||||
<ClCompile Include="media_capture.cpp" />
|
<ClCompile Include="media_capture.cpp" />
|
||||||
<ClCompile Include="compress_helpers.cpp" />
|
<ClCompile Include="compress_helpers.cpp" />
|
||||||
<ClCompile Include="elf_file.cpp" />
|
<ClCompile Include="elf_file.cpp" />
|
||||||
|
<ClCompile Include="x11_tools.cpp" />
|
||||||
|
<ClCompile Include="opengl_context_egl_xlib.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="metal_shaders.metal" />
|
<None Include="metal_shaders.metal" />
|
||||||
|
@ -248,8 +248,8 @@ bool VulkanDevice::SelectInstanceExtensions(ExtensionList* extension_list, const
|
|||||||
if (wi.type == WindowInfo::Type::Win32 && !SupportsExtension(VK_KHR_WIN32_SURFACE_EXTENSION_NAME, true))
|
if (wi.type == WindowInfo::Type::Win32 && !SupportsExtension(VK_KHR_WIN32_SURFACE_EXTENSION_NAME, true))
|
||||||
return false;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
#if defined(VK_USE_PLATFORM_XLIB_KHR)
|
#if defined(VK_USE_PLATFORM_XCB_KHR)
|
||||||
if (wi.type == WindowInfo::Type::X11 && !SupportsExtension(VK_KHR_XLIB_SURFACE_EXTENSION_NAME, true))
|
if (wi.type == WindowInfo::Type::XCB && !SupportsExtension(VK_KHR_XCB_SURFACE_EXTENSION_NAME, true))
|
||||||
return false;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
#if defined(VK_USE_PLATFORM_WAYLAND_KHR)
|
#if defined(VK_USE_PLATFORM_WAYLAND_KHR)
|
||||||
|
@ -40,12 +40,10 @@ VULKAN_INSTANCE_ENTRY_POINT(vkGetPhysicalDeviceSurfacePresentModesKHR, false)
|
|||||||
|
|
||||||
#if defined(VK_USE_PLATFORM_WIN32_KHR)
|
#if defined(VK_USE_PLATFORM_WIN32_KHR)
|
||||||
VULKAN_INSTANCE_ENTRY_POINT(vkCreateWin32SurfaceKHR, false)
|
VULKAN_INSTANCE_ENTRY_POINT(vkCreateWin32SurfaceKHR, false)
|
||||||
VULKAN_INSTANCE_ENTRY_POINT(vkGetPhysicalDeviceWin32PresentationSupportKHR, false)
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(VK_USE_PLATFORM_XLIB_KHR)
|
#if defined(VK_USE_PLATFORM_XCB_KHR)
|
||||||
VULKAN_INSTANCE_ENTRY_POINT(vkCreateXlibSurfaceKHR, false)
|
VULKAN_INSTANCE_ENTRY_POINT(vkCreateXcbSurfaceKHR, false)
|
||||||
VULKAN_INSTANCE_ENTRY_POINT(vkGetPhysicalDeviceXlibPresentationSupportKHR, false)
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(VK_USE_PLATFORM_WAYLAND_KHR)
|
#if defined(VK_USE_PLATFORM_WAYLAND_KHR)
|
||||||
|
@ -18,7 +18,7 @@ class Error;
|
|||||||
#define VK_USE_PLATFORM_ANDROID_KHR
|
#define VK_USE_PLATFORM_ANDROID_KHR
|
||||||
#else
|
#else
|
||||||
#ifdef ENABLE_X11
|
#ifdef ENABLE_X11
|
||||||
#define VK_USE_PLATFORM_XLIB_KHR
|
#define VK_USE_PLATFORM_XCB_KHR
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef ENABLE_WAYLAND
|
#ifdef ENABLE_WAYLAND
|
||||||
@ -28,48 +28,6 @@ class Error;
|
|||||||
|
|
||||||
#include "vulkan/vulkan.h"
|
#include "vulkan/vulkan.h"
|
||||||
|
|
||||||
#if defined(ENABLE_X11)
|
|
||||||
|
|
||||||
// This breaks a bunch of our code. They shouldn't be #defines in the first place.
|
|
||||||
#ifdef None
|
|
||||||
#undef None
|
|
||||||
#endif
|
|
||||||
#ifdef Always
|
|
||||||
#undef Always
|
|
||||||
#endif
|
|
||||||
#ifdef Status
|
|
||||||
#undef Status
|
|
||||||
#endif
|
|
||||||
#ifdef CursorShape
|
|
||||||
#undef CursorShape
|
|
||||||
#endif
|
|
||||||
#ifdef KeyPress
|
|
||||||
#undef KeyPress
|
|
||||||
#endif
|
|
||||||
#ifdef KeyRelease
|
|
||||||
#undef KeyRelease
|
|
||||||
#endif
|
|
||||||
#ifdef FocusIn
|
|
||||||
#undef FocusIn
|
|
||||||
#endif
|
|
||||||
#ifdef FocusOut
|
|
||||||
#undef FocusOut
|
|
||||||
#endif
|
|
||||||
#ifdef FontChange
|
|
||||||
#undef FontChange
|
|
||||||
#endif
|
|
||||||
#ifdef Expose
|
|
||||||
#undef Expose
|
|
||||||
#endif
|
|
||||||
#ifdef Unsorted
|
|
||||||
#undef Unsorted
|
|
||||||
#endif
|
|
||||||
#ifdef Bool
|
|
||||||
#undef Bool
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "vulkan_entry_points.h"
|
#include "vulkan_entry_points.h"
|
||||||
|
|
||||||
// We include vk_mem_alloc globally, so we don't accidentally include it before the vulkan header somewhere.
|
// We include vk_mem_alloc globally, so we don't accidentally include it before the vulkan header somewhere.
|
||||||
|
@ -138,17 +138,17 @@ bool VulkanSwapChain::CreateSurface(VkInstance instance, VkPhysicalDevice physic
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(VK_USE_PLATFORM_XLIB_KHR)
|
#if defined(VK_USE_PLATFORM_XCB_KHR)
|
||||||
if (m_window_info.type == WindowInfo::Type::X11)
|
if (m_window_info.type == WindowInfo::Type::XCB)
|
||||||
{
|
{
|
||||||
const VkXlibSurfaceCreateInfoKHR surface_create_info = {
|
const VkXcbSurfaceCreateInfoKHR surface_create_info = {
|
||||||
.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR,
|
.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR,
|
||||||
.dpy = static_cast<Display*>(m_window_info.display_connection),
|
.connection = static_cast<xcb_connection_t*>(m_window_info.display_connection),
|
||||||
.window = reinterpret_cast<Window>(m_window_info.window_handle)};
|
.window = static_cast<xcb_window_t>(reinterpret_cast<uintptr_t>(m_window_info.window_handle))};
|
||||||
const VkResult res = vkCreateXlibSurfaceKHR(instance, &surface_create_info, nullptr, &m_surface);
|
const VkResult res = vkCreateXcbSurfaceKHR(instance, &surface_create_info, nullptr, &m_surface);
|
||||||
if (res != VK_SUCCESS)
|
if (res != VK_SUCCESS)
|
||||||
{
|
{
|
||||||
Vulkan::SetErrorObject(error, "vkCreateXlibSurfaceKHR failed: ", res);
|
Vulkan::SetErrorObject(error, "vkCreateXcbSurfaceKHR failed: ", res);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,13 +16,13 @@ LOG_CHANNEL(WindowInfo);
|
|||||||
#include "common/windows_headers.h"
|
#include "common/windows_headers.h"
|
||||||
#include <dwmapi.h>
|
#include <dwmapi.h>
|
||||||
|
|
||||||
static std::optional<float> GetRefreshRateFromDisplayConfig(HWND hwnd)
|
static std::optional<float> GetRefreshRateFromDisplayConfig(HWND hwnd, Error* error)
|
||||||
{
|
{
|
||||||
// Partially based on Chromium ui/display/win/display_config_helper.cc.
|
// Partially based on Chromium ui/display/win/display_config_helper.cc.
|
||||||
const HMONITOR monitor = MonitorFromWindow(hwnd, 0);
|
const HMONITOR monitor = MonitorFromWindow(hwnd, 0);
|
||||||
if (!monitor) [[unlikely]]
|
if (!monitor) [[unlikely]]
|
||||||
{
|
{
|
||||||
ERROR_LOG("{}() failed: {}", "MonitorFromWindow", Error::CreateWin32(GetLastError()).GetDescription());
|
Error::SetWin32(error, "MonitorFromWindow() failed: ", GetLastError());
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -30,7 +30,7 @@ static std::optional<float> GetRefreshRateFromDisplayConfig(HWND hwnd)
|
|||||||
mi.cbSize = sizeof(mi);
|
mi.cbSize = sizeof(mi);
|
||||||
if (!GetMonitorInfoW(monitor, &mi))
|
if (!GetMonitorInfoW(monitor, &mi))
|
||||||
{
|
{
|
||||||
ERROR_LOG("{}() failed: {}", "GetMonitorInfoW", Error::CreateWin32(GetLastError()).GetDescription());
|
Error::SetWin32(error, "GetMonitorInfoW() failed: ", GetLastError());
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,7 +44,7 @@ static std::optional<float> GetRefreshRateFromDisplayConfig(HWND hwnd)
|
|||||||
LONG res = GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &path_size, &mode_size);
|
LONG res = GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &path_size, &mode_size);
|
||||||
if (res != ERROR_SUCCESS)
|
if (res != ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
ERROR_LOG("{}() failed: {}", "GetDisplayConfigBufferSizes", Error::CreateWin32(res).GetDescription());
|
Error::SetWin32(error, "GetDisplayConfigBufferSizes() failed: ", res);
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,7 +56,7 @@ static std::optional<float> GetRefreshRateFromDisplayConfig(HWND hwnd)
|
|||||||
break;
|
break;
|
||||||
if (res != ERROR_INSUFFICIENT_BUFFER)
|
if (res != ERROR_INSUFFICIENT_BUFFER)
|
||||||
{
|
{
|
||||||
ERROR_LOG("{}() failed: {}", "QueryDisplayConfig", Error::CreateWin32(res).GetDescription());
|
Error::SetWin32(error, "QueryDisplayConfig() failed: ", res);
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -70,7 +70,7 @@ static std::optional<float> GetRefreshRateFromDisplayConfig(HWND hwnd)
|
|||||||
LONG res = DisplayConfigGetDeviceInfo(&sdn.header);
|
LONG res = DisplayConfigGetDeviceInfo(&sdn.header);
|
||||||
if (res != ERROR_SUCCESS)
|
if (res != ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
ERROR_LOG("{}() failed: {}", "DisplayConfigGetDeviceInfo", Error::CreateWin32(res).GetDescription());
|
Error::SetWin32(error, "DisplayConfigGetDeviceInfo() failed: ", res);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,15 +85,19 @@ static std::optional<float> GetRefreshRateFromDisplayConfig(HWND hwnd)
|
|||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::optional<float> GetRefreshRateFromDWM(HWND hwnd)
|
static std::optional<float> GetRefreshRateFromDWM(HWND hwnd, Error* error)
|
||||||
{
|
{
|
||||||
BOOL composition_enabled;
|
BOOL composition_enabled;
|
||||||
if (FAILED(DwmIsCompositionEnabled(&composition_enabled)))
|
HRESULT hr = DwmIsCompositionEnabled(&composition_enabled);
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
Error::SetHResult(error, "DwmIsCompositionEnabled() failed: ", hr);
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
DWM_TIMING_INFO ti = {};
|
DWM_TIMING_INFO ti = {};
|
||||||
ti.cbSize = sizeof(ti);
|
ti.cbSize = sizeof(ti);
|
||||||
HRESULT hr = DwmGetCompositionTimingInfo(nullptr, &ti);
|
hr = DwmGetCompositionTimingInfo(nullptr, &ti);
|
||||||
if (SUCCEEDED(hr))
|
if (SUCCEEDED(hr))
|
||||||
{
|
{
|
||||||
if (ti.rateRefresh.uiNumerator == 0 || ti.rateRefresh.uiDenominator == 0)
|
if (ti.rateRefresh.uiNumerator == 0 || ti.rateRefresh.uiDenominator == 0)
|
||||||
@ -101,15 +105,21 @@ static std::optional<float> GetRefreshRateFromDWM(HWND hwnd)
|
|||||||
|
|
||||||
return static_cast<float>(ti.rateRefresh.uiNumerator) / static_cast<float>(ti.rateRefresh.uiDenominator);
|
return static_cast<float>(ti.rateRefresh.uiNumerator) / static_cast<float>(ti.rateRefresh.uiDenominator);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
return std::nullopt;
|
{
|
||||||
|
Error::SetHResult(error, "DwmGetCompositionTimingInfo() failed: ", hr);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::optional<float> GetRefreshRateFromMonitor(HWND hwnd)
|
static std::optional<float> GetRefreshRateFromMonitor(HWND hwnd, Error* error)
|
||||||
{
|
{
|
||||||
HMONITOR mon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
|
HMONITOR mon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
|
||||||
if (!mon)
|
if (!mon)
|
||||||
|
{
|
||||||
|
Error::SetWin32(error, "MonitorFromWindow() failed: ", GetLastError());
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
MONITORINFOEXW mi = {};
|
MONITORINFOEXW mi = {};
|
||||||
mi.cbSize = sizeof(mi);
|
mi.cbSize = sizeof(mi);
|
||||||
@ -120,26 +130,46 @@ static std::optional<float> GetRefreshRateFromMonitor(HWND hwnd)
|
|||||||
|
|
||||||
// 0/1 are reserved for "defaults".
|
// 0/1 are reserved for "defaults".
|
||||||
if (EnumDisplaySettingsW(mi.szDevice, ENUM_CURRENT_SETTINGS, &dm) && dm.dmDisplayFrequency > 1)
|
if (EnumDisplaySettingsW(mi.szDevice, ENUM_CURRENT_SETTINGS, &dm) && dm.dmDisplayFrequency > 1)
|
||||||
|
{
|
||||||
return static_cast<float>(dm.dmDisplayFrequency);
|
return static_cast<float>(dm.dmDisplayFrequency);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Error::SetWin32(error, "EnumDisplaySettingsW() failed: ", GetLastError());
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Error::SetWin32(error, "GetMonitorInfoW() failed: ", GetLastError());
|
||||||
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<float> WindowInfo::QueryRefreshRateForWindow(const WindowInfo& wi)
|
std::optional<float> WindowInfo::QueryRefreshRateForWindow(const WindowInfo& wi, Error* error)
|
||||||
{
|
{
|
||||||
std::optional<float> ret;
|
std::optional<float> ret;
|
||||||
if (wi.type != Type::Win32 || !wi.window_handle)
|
if (wi.type != Type::Win32 || !wi.window_handle)
|
||||||
|
{
|
||||||
|
Error::SetStringView(error, "Invalid window type.");
|
||||||
return ret;
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
// Try DWM first, then fall back to integer values.
|
// Try DWM first, then fall back to integer values.
|
||||||
const HWND hwnd = static_cast<HWND>(wi.window_handle);
|
const HWND hwnd = static_cast<HWND>(wi.window_handle);
|
||||||
ret = GetRefreshRateFromDisplayConfig(hwnd);
|
Error local_error;
|
||||||
|
ret = GetRefreshRateFromDisplayConfig(hwnd, &local_error);
|
||||||
if (!ret.has_value())
|
if (!ret.has_value())
|
||||||
{
|
{
|
||||||
ret = GetRefreshRateFromDWM(hwnd);
|
WARNING_LOG("GetRefreshRateFromDisplayConfig() failed: {}", local_error.GetDescription());
|
||||||
|
|
||||||
|
ret = GetRefreshRateFromDWM(hwnd, &local_error);
|
||||||
if (!ret.has_value())
|
if (!ret.has_value())
|
||||||
ret = GetRefreshRateFromMonitor(hwnd);
|
{
|
||||||
|
WARNING_LOG("GetRefreshRateFromDWM() failed: {}", local_error.GetDescription());
|
||||||
|
|
||||||
|
ret = GetRefreshRateFromMonitor(hwnd, error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@ -149,157 +179,29 @@ std::optional<float> WindowInfo::QueryRefreshRateForWindow(const WindowInfo& wi)
|
|||||||
|
|
||||||
#include "util/platform_misc.h"
|
#include "util/platform_misc.h"
|
||||||
|
|
||||||
std::optional<float> WindowInfo::QueryRefreshRateForWindow(const WindowInfo& wi)
|
std::optional<float> WindowInfo::QueryRefreshRateForWindow(const WindowInfo& wi, Error* error)
|
||||||
{
|
{
|
||||||
if (wi.type == WindowInfo::Type::MacOS)
|
if (wi.type == WindowInfo::Type::MacOS)
|
||||||
return CocoaTools::GetViewRefreshRate(wi);
|
return CocoaTools::GetViewRefreshRate(wi, error);
|
||||||
|
|
||||||
|
Error::SetStringView(error, "Invalid window type.");
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
#ifdef ENABLE_X11
|
#ifdef ENABLE_X11
|
||||||
|
#include "x11_tools.h"
|
||||||
#include <X11/Xlib.h>
|
|
||||||
#include <X11/Xutil.h>
|
|
||||||
#include <X11/extensions/Xrandr.h>
|
|
||||||
|
|
||||||
// Helper class for managing X errors
|
|
||||||
namespace {
|
|
||||||
class X11InhibitErrors;
|
|
||||||
|
|
||||||
static X11InhibitErrors* s_current_error_inhibiter;
|
|
||||||
|
|
||||||
class X11InhibitErrors
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
X11InhibitErrors()
|
|
||||||
{
|
|
||||||
Assert(!s_current_error_inhibiter);
|
|
||||||
m_old_handler = XSetErrorHandler(ErrorHandler);
|
|
||||||
s_current_error_inhibiter = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
~X11InhibitErrors()
|
|
||||||
{
|
|
||||||
Assert(s_current_error_inhibiter == this);
|
|
||||||
s_current_error_inhibiter = nullptr;
|
|
||||||
XSetErrorHandler(m_old_handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
ALWAYS_INLINE bool HadError() const { return m_had_error; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
static int ErrorHandler(Display* display, XErrorEvent* ee)
|
|
||||||
{
|
|
||||||
char error_string[256] = {};
|
|
||||||
XGetErrorText(display, ee->error_code, error_string, sizeof(error_string));
|
|
||||||
WARNING_LOG("X11 Error: {} (Error {} Minor {} Request {})", error_string, ee->error_code, ee->minor_code,
|
|
||||||
ee->request_code);
|
|
||||||
|
|
||||||
s_current_error_inhibiter->m_had_error = true;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
XErrorHandler m_old_handler = {};
|
|
||||||
bool m_had_error = false;
|
|
||||||
};
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
static std::optional<float> GetRefreshRateFromXRandR(const WindowInfo& wi)
|
|
||||||
{
|
|
||||||
Display* display = static_cast<Display*>(wi.display_connection);
|
|
||||||
Window window = static_cast<Window>(reinterpret_cast<uintptr_t>(wi.window_handle));
|
|
||||||
if (!display || !window)
|
|
||||||
return std::nullopt;
|
|
||||||
|
|
||||||
X11InhibitErrors inhibiter;
|
|
||||||
|
|
||||||
XRRScreenResources* res = XRRGetScreenResources(display, window);
|
|
||||||
if (!res)
|
|
||||||
{
|
|
||||||
ERROR_LOG("XRRGetScreenResources() failed");
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
ScopedGuard res_guard([res]() { XRRFreeScreenResources(res); });
|
|
||||||
|
|
||||||
int num_monitors;
|
|
||||||
XRRMonitorInfo* mi = XRRGetMonitors(display, window, True, &num_monitors);
|
|
||||||
if (num_monitors < 0)
|
|
||||||
{
|
|
||||||
ERROR_LOG("XRRGetMonitors() failed");
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
else if (num_monitors > 1)
|
|
||||||
{
|
|
||||||
WARNING_LOG("XRRGetMonitors() returned {} monitors, using first", num_monitors);
|
|
||||||
}
|
|
||||||
|
|
||||||
ScopedGuard mi_guard([mi]() { XRRFreeMonitors(mi); });
|
|
||||||
if (mi->noutput <= 0)
|
|
||||||
{
|
|
||||||
ERROR_LOG("Monitor has no outputs");
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
else if (mi->noutput > 1)
|
|
||||||
{
|
|
||||||
WARNING_LOG("Monitor has {} outputs, using first", mi->noutput);
|
|
||||||
}
|
|
||||||
|
|
||||||
XRROutputInfo* oi = XRRGetOutputInfo(display, res, mi->outputs[0]);
|
|
||||||
if (!oi)
|
|
||||||
{
|
|
||||||
ERROR_LOG("XRRGetOutputInfo() failed");
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
ScopedGuard oi_guard([oi]() { XRRFreeOutputInfo(oi); });
|
|
||||||
|
|
||||||
XRRCrtcInfo* ci = XRRGetCrtcInfo(display, res, oi->crtc);
|
|
||||||
if (!ci)
|
|
||||||
{
|
|
||||||
ERROR_LOG("XRRGetCrtcInfo() failed");
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
ScopedGuard ci_guard([ci]() { XRRFreeCrtcInfo(ci); });
|
|
||||||
|
|
||||||
XRRModeInfo* mode = nullptr;
|
|
||||||
for (int i = 0; i < res->nmode; i++)
|
|
||||||
{
|
|
||||||
if (res->modes[i].id == ci->mode)
|
|
||||||
{
|
|
||||||
mode = &res->modes[i];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!mode)
|
|
||||||
{
|
|
||||||
ERROR_LOG("Failed to look up mode {} (of {})", static_cast<int>(ci->mode), res->nmode);
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mode->dotClock == 0 || mode->hTotal == 0 || mode->vTotal == 0)
|
|
||||||
{
|
|
||||||
ERROR_LOG("Modeline is invalid: {}/{}/{}", mode->dotClock, mode->hTotal, mode->vTotal);
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
return static_cast<float>(static_cast<double>(mode->dotClock) /
|
|
||||||
(static_cast<double>(mode->hTotal) * static_cast<double>(mode->vTotal)));
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // ENABLE_X11
|
|
||||||
|
|
||||||
std::optional<float> WindowInfo::QueryRefreshRateForWindow(const WindowInfo& wi)
|
|
||||||
{
|
|
||||||
#if defined(ENABLE_X11)
|
|
||||||
if (wi.type == WindowInfo::Type::X11)
|
|
||||||
return GetRefreshRateFromXRandR(wi);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
std::optional<float> WindowInfo::QueryRefreshRateForWindow(const WindowInfo& wi, Error* error)
|
||||||
|
{
|
||||||
|
#if defined(ENABLE_X11)
|
||||||
|
if (wi.type == WindowInfo::Type::Xlib || wi.type == WindowInfo::Type::XCB)
|
||||||
|
return GetRefreshRateFromXRandR(wi, error);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Error::SetStringView(error, "Invalid window type.");
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,6 +8,8 @@
|
|||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
|
class Error;
|
||||||
|
|
||||||
// Contains the information required to create a graphics context in a window.
|
// Contains the information required to create a graphics context in a window.
|
||||||
struct WindowInfo
|
struct WindowInfo
|
||||||
{
|
{
|
||||||
@ -15,7 +17,8 @@ struct WindowInfo
|
|||||||
{
|
{
|
||||||
Surfaceless,
|
Surfaceless,
|
||||||
Win32,
|
Win32,
|
||||||
X11,
|
Xlib,
|
||||||
|
XCB,
|
||||||
Wayland,
|
Wayland,
|
||||||
MacOS,
|
MacOS,
|
||||||
Android,
|
Android,
|
||||||
@ -32,5 +35,5 @@ struct WindowInfo
|
|||||||
|
|
||||||
ALWAYS_INLINE bool IsSurfaceless() const { return type == Type::Surfaceless; }
|
ALWAYS_INLINE bool IsSurfaceless() const { return type == Type::Surfaceless; }
|
||||||
|
|
||||||
static std::optional<float> QueryRefreshRateForWindow(const WindowInfo& wi);
|
static std::optional<float> QueryRefreshRateForWindow(const WindowInfo& wi, Error* error = nullptr);
|
||||||
};
|
};
|
||||||
|
325
src/util/x11_tools.cpp
Normal file
325
src/util/x11_tools.cpp
Normal file
@ -0,0 +1,325 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
|
||||||
|
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
|
||||||
|
|
||||||
|
#include "x11_tools.h"
|
||||||
|
#include "window_info.h"
|
||||||
|
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "common/error.h"
|
||||||
|
#include "common/log.h"
|
||||||
|
#include "common/scoped_guard.h"
|
||||||
|
|
||||||
|
#include <X11/Xlib-xcb.h>
|
||||||
|
#include <xcb/randr.h>
|
||||||
|
#include <xcb/xcb.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
LOG_CHANNEL(WindowInfo);
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
template<typename T>
|
||||||
|
struct XCBPointerDeleter
|
||||||
|
{
|
||||||
|
void operator()(T* ptr) { free(ptr); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
using XCBPointer = std::unique_ptr<T, XCBPointerDeleter<T>>;
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
X11Window::X11Window() = default;
|
||||||
|
|
||||||
|
X11Window::X11Window(X11Window&& move)
|
||||||
|
{
|
||||||
|
m_connection = move.m_connection;
|
||||||
|
m_parent_window = move.m_parent_window;
|
||||||
|
m_window = move.m_window;
|
||||||
|
m_colormap = move.m_colormap;
|
||||||
|
m_width = move.m_width;
|
||||||
|
m_height = move.m_height;
|
||||||
|
|
||||||
|
move.m_connection = nullptr;
|
||||||
|
move.m_parent_window = {};
|
||||||
|
move.m_window = {};
|
||||||
|
move.m_colormap = {};
|
||||||
|
move.m_width = 0;
|
||||||
|
move.m_height = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
X11Window::~X11Window()
|
||||||
|
{
|
||||||
|
Destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
X11Window& X11Window::operator=(X11Window&& move)
|
||||||
|
{
|
||||||
|
m_connection = move.m_connection;
|
||||||
|
m_parent_window = move.m_parent_window;
|
||||||
|
m_window = move.m_window;
|
||||||
|
m_colormap = move.m_colormap;
|
||||||
|
m_width = move.m_width;
|
||||||
|
m_height = move.m_height;
|
||||||
|
|
||||||
|
move.m_connection = nullptr;
|
||||||
|
move.m_parent_window = {};
|
||||||
|
move.m_window = {};
|
||||||
|
move.m_colormap = {};
|
||||||
|
move.m_width = 0;
|
||||||
|
move.m_height = 0;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void SetErrorObject(Error* error, const char* prefix, const xcb_generic_error_t* xerror)
|
||||||
|
{
|
||||||
|
Error::SetStringFmt(error, "{} failed: EC={} Major={} Minor={} Resource={:X}", xerror->error_code,
|
||||||
|
xerror->response_type, xerror->major_code, xerror->minor_code, xerror->resource_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool X11Window::Create(xcb_connection_t* connection, xcb_window_t parent_window, xcb_visualid_t vi, Error* error)
|
||||||
|
{
|
||||||
|
xcb_generic_error_t* xerror;
|
||||||
|
|
||||||
|
m_connection = connection;
|
||||||
|
m_parent_window = parent_window;
|
||||||
|
|
||||||
|
XCBPointer<xcb_get_geometry_reply_t> gwa(
|
||||||
|
xcb_get_geometry_reply(connection, xcb_get_geometry(connection, parent_window), &xerror));
|
||||||
|
if (!gwa)
|
||||||
|
{
|
||||||
|
SetErrorObject(error, "xcb_get_geometry_reply() failed: ", xerror);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_width = gwa->width;
|
||||||
|
m_height = gwa->height;
|
||||||
|
|
||||||
|
// Need to find the root window to get an appropriate depth. Needed for NVIDIA+XWayland.
|
||||||
|
int visual_depth = XCB_COPY_FROM_PARENT;
|
||||||
|
for (xcb_screen_iterator_t it = xcb_setup_roots_iterator(xcb_get_setup(connection)); it.rem != 0;
|
||||||
|
xcb_screen_next(&it))
|
||||||
|
{
|
||||||
|
if (it.data->root == gwa->root)
|
||||||
|
{
|
||||||
|
for (xcb_depth_iterator_t dit = xcb_screen_allowed_depths_iterator(it.data); dit.rem != 0; xcb_depth_next(&dit))
|
||||||
|
{
|
||||||
|
const int len = xcb_depth_visuals_length(dit.data);
|
||||||
|
const xcb_visualtype_t* visuals = xcb_depth_visuals(dit.data);
|
||||||
|
int idx = 0;
|
||||||
|
for (; idx < len; idx++)
|
||||||
|
{
|
||||||
|
if (vi == visuals[idx].visual_id)
|
||||||
|
{
|
||||||
|
visual_depth = dit.data->depth;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (visual_depth == XCB_COPY_FROM_PARENT)
|
||||||
|
WARNING_LOG("Could not find visual's depth.");
|
||||||
|
|
||||||
|
// ID isn't "used" until the call succeeds.
|
||||||
|
m_colormap = xcb_generate_id(connection);
|
||||||
|
if ((xerror = xcb_request_check(
|
||||||
|
connection, xcb_create_colormap_checked(connection, XCB_COLORMAP_ALLOC_NONE, m_colormap, parent_window, vi))))
|
||||||
|
{
|
||||||
|
SetErrorObject(error, "xcb_create_colormap_checked() failed: ", xerror);
|
||||||
|
m_colormap = {};
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_window = xcb_generate_id(connection);
|
||||||
|
|
||||||
|
const u32 window_values[] = {XCB_PIXMAP_NONE, 0u, m_colormap};
|
||||||
|
xerror = xcb_request_check(
|
||||||
|
connection, xcb_create_window_checked(connection, visual_depth, m_window, parent_window, 0, 0, m_width, m_height, 0,
|
||||||
|
XCB_WINDOW_CLASS_INPUT_OUTPUT, vi,
|
||||||
|
XCB_CW_BACK_PIXMAP | XCB_CW_BORDER_PIXEL | XCB_CW_COLORMAP, window_values));
|
||||||
|
if (xerror)
|
||||||
|
{
|
||||||
|
SetErrorObject(error, "xcb_create_window_checked() failed: ", xerror);
|
||||||
|
m_window = {};
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
xerror = xcb_request_check(connection, xcb_map_window_checked(connection, m_window));
|
||||||
|
if (xerror)
|
||||||
|
{
|
||||||
|
SetErrorObject(error, "xcb_map_window_checked() failed: ", xerror);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void X11Window::Destroy()
|
||||||
|
{
|
||||||
|
xcb_generic_error_t* xerror;
|
||||||
|
Error error;
|
||||||
|
|
||||||
|
if (m_window)
|
||||||
|
{
|
||||||
|
if ((xerror = xcb_request_check(m_connection, xcb_unmap_window_checked(m_connection, m_window))))
|
||||||
|
{
|
||||||
|
SetErrorObject(&error, "xcb_unmap_window_checked() failed: ", xerror);
|
||||||
|
ERROR_LOG(error.GetDescription());
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((xerror = xcb_request_check(m_connection, xcb_destroy_window_checked(m_connection, m_window))))
|
||||||
|
{
|
||||||
|
SetErrorObject(&error, "xcb_destroy_window_checked() failed: ", xerror);
|
||||||
|
ERROR_LOG(error.GetDescription());
|
||||||
|
}
|
||||||
|
|
||||||
|
m_window = {};
|
||||||
|
m_parent_window = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_colormap)
|
||||||
|
{
|
||||||
|
if ((xerror = xcb_request_check(m_connection, xcb_free_colormap_checked(m_connection, m_colormap))))
|
||||||
|
{
|
||||||
|
SetErrorObject(&error, "xcb_free_colormap_checked() failed: ", xerror);
|
||||||
|
ERROR_LOG(error.GetDescription());
|
||||||
|
}
|
||||||
|
|
||||||
|
m_colormap = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void X11Window::Resize(u16 width, u16 height)
|
||||||
|
{
|
||||||
|
xcb_generic_error_t* xerror;
|
||||||
|
Error error;
|
||||||
|
|
||||||
|
if (width != 0 && height != 0)
|
||||||
|
{
|
||||||
|
m_width = width;
|
||||||
|
m_height = height;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
XCBPointer<xcb_get_geometry_reply_t> gwa(
|
||||||
|
xcb_get_geometry_reply(m_connection, xcb_get_geometry(m_connection, m_parent_window), &xerror));
|
||||||
|
if (!gwa)
|
||||||
|
{
|
||||||
|
SetErrorObject(&error, "xcb_get_geometry() failed: ", xerror);
|
||||||
|
ERROR_LOG(error.GetDescription());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_width = gwa->width;
|
||||||
|
m_height = gwa->height;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 values[] = {width, height};
|
||||||
|
if ((xerror = xcb_request_check(
|
||||||
|
m_connection, xcb_configure_window_checked(m_connection, m_window,
|
||||||
|
XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, values))))
|
||||||
|
{
|
||||||
|
SetErrorObject(&error, "xcb_configure_window_checked() failed: ", xerror);
|
||||||
|
ERROR_LOG(error.GetDescription());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<float> GetRefreshRateFromXRandR(const WindowInfo& wi, Error* error)
|
||||||
|
{
|
||||||
|
xcb_connection_t* connection = nullptr;
|
||||||
|
if (wi.type == WindowInfo::Type::Xlib)
|
||||||
|
{
|
||||||
|
connection = XGetXCBConnection(static_cast<Display*>(wi.display_connection));
|
||||||
|
}
|
||||||
|
else if (wi.type == WindowInfo::Type::XCB)
|
||||||
|
{
|
||||||
|
connection = static_cast<xcb_connection_t*>(wi.display_connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
xcb_window_t window = static_cast<xcb_window_t>(reinterpret_cast<uintptr_t>(wi.window_handle));
|
||||||
|
if (wi.type != WindowInfo::Type::XCB || !connection || window == XCB_NONE)
|
||||||
|
{
|
||||||
|
Error::SetStringView(error, "Invalid window handle.");
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
xcb_generic_error_t* xerror;
|
||||||
|
XCBPointer<xcb_randr_get_screen_resources_reply_t> gsr(
|
||||||
|
xcb_randr_get_screen_resources_reply(connection, xcb_randr_get_screen_resources(connection, window), &xerror));
|
||||||
|
if (xerror)
|
||||||
|
{
|
||||||
|
SetErrorObject(error, "xcb_randr_get_screen_resources() failed: ", xerror);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
XCBPointer<xcb_randr_get_monitors_reply_t> gm(
|
||||||
|
xcb_randr_get_monitors_reply(connection, xcb_randr_get_monitors(connection, window, true), &xerror));
|
||||||
|
if (xerror || gm->nMonitors < 0)
|
||||||
|
{
|
||||||
|
SetErrorObject(error, "xcb_randr_get_screen_resources() failed: ", xerror);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gm->nMonitors > 1)
|
||||||
|
WARNING_LOG("xcb_randr_get_monitors() returned {} monitors, using first", gm->nMonitors);
|
||||||
|
|
||||||
|
if (gm->nOutputs <= 0)
|
||||||
|
{
|
||||||
|
Error::SetStringView(error, "Monitor has no outputs");
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
else if (gm->nOutputs > 1)
|
||||||
|
{
|
||||||
|
WARNING_LOG("Monitor has {} outputs, using first", gm->nOutputs);
|
||||||
|
}
|
||||||
|
|
||||||
|
xcb_randr_monitor_info_t* monitor_info = xcb_randr_get_monitors_monitors_iterator(gm.get()).data;
|
||||||
|
DebugAssert(monitor_info);
|
||||||
|
|
||||||
|
xcb_randr_output_t* monitor_outputs = xcb_randr_monitor_info_outputs(monitor_info);
|
||||||
|
DebugAssert(monitor_outputs);
|
||||||
|
|
||||||
|
XCBPointer<xcb_randr_get_output_info_reply_t> goi(
|
||||||
|
xcb_randr_get_output_info_reply(connection, xcb_randr_get_output_info(connection, monitor_outputs[0], 0), &xerror));
|
||||||
|
if (xerror)
|
||||||
|
{
|
||||||
|
SetErrorObject(error, "xcb_randr_get_output_info() failed: ", xerror);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
XCBPointer<xcb_randr_get_crtc_info_reply_t> gci(
|
||||||
|
xcb_randr_get_crtc_info_reply(connection, xcb_randr_get_crtc_info(connection, goi->crtc, 0), &xerror));
|
||||||
|
if (xerror)
|
||||||
|
{
|
||||||
|
SetErrorObject(error, "xcb_randr_get_crtc_info_reply() failed: ", xerror);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
xcb_randr_mode_info_t* mode = nullptr;
|
||||||
|
for (xcb_randr_mode_info_iterator_t it = xcb_randr_get_screen_resources_modes_iterator(gsr.get()); it.rem != 0;
|
||||||
|
xcb_randr_mode_info_next(&it))
|
||||||
|
{
|
||||||
|
if (it.data->id == gci->mode)
|
||||||
|
{
|
||||||
|
mode = it.data;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!mode)
|
||||||
|
{
|
||||||
|
Error::SetStringFmt(error, "Failed to look up mode ID {}", static_cast<int>(gci->mode));
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode->dot_clock == 0 || mode->htotal == 0 || mode->vtotal == 0)
|
||||||
|
{
|
||||||
|
ERROR_LOG("Modeline is invalid: {}/{}/{}", mode->dot_clock, mode->htotal, mode->vtotal);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return static_cast<float>(static_cast<double>(mode->dot_clock) /
|
||||||
|
(static_cast<double>(mode->htotal) * static_cast<double>(mode->vtotal)));
|
||||||
|
}
|
45
src/util/x11_tools.h
Normal file
45
src/util/x11_tools.h
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
|
||||||
|
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common/types.h"
|
||||||
|
|
||||||
|
#include <xcb/xcb.h>
|
||||||
|
#include <xcb/xproto.h>
|
||||||
|
|
||||||
|
class Error;
|
||||||
|
struct WindowInfo;
|
||||||
|
|
||||||
|
class X11Window
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
X11Window();
|
||||||
|
X11Window(const X11Window&) = delete;
|
||||||
|
X11Window(X11Window&& move);
|
||||||
|
~X11Window();
|
||||||
|
|
||||||
|
X11Window& operator=(const X11Window&) = delete;
|
||||||
|
X11Window& operator=(X11Window&& move);
|
||||||
|
|
||||||
|
ALWAYS_INLINE xcb_window_t GetWindow() const { return m_window; }
|
||||||
|
ALWAYS_INLINE xcb_window_t* GetWindowPtr() { return &m_window; }
|
||||||
|
ALWAYS_INLINE u32 GetWidth() const { return m_width; }
|
||||||
|
ALWAYS_INLINE u32 GetHeight() const { return m_height; }
|
||||||
|
|
||||||
|
bool Create(xcb_connection_t* connection, xcb_window_t parent_window, xcb_visualid_t vi, Error* error = nullptr);
|
||||||
|
void Destroy();
|
||||||
|
|
||||||
|
// Setting a width/height of 0 will use parent dimensions.
|
||||||
|
void Resize(u16 width = 0, u16 height = 0);
|
||||||
|
|
||||||
|
private:
|
||||||
|
xcb_connection_t* m_connection = nullptr;
|
||||||
|
xcb_window_t m_parent_window = {};
|
||||||
|
xcb_window_t m_window = {};
|
||||||
|
xcb_colormap_t m_colormap = {};
|
||||||
|
u16 m_width = 0;
|
||||||
|
u16 m_height = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::optional<float> GetRefreshRateFromXRandR(const WindowInfo& wi, Error* error);
|
Loading…
Reference in New Issue
Block a user