From c5cc932b106a1c5239d0ccc9e418944f0ba016de Mon Sep 17 00:00:00 2001 From: Stenzek Date: Thu, 2 May 2019 23:40:37 +1000 Subject: [PATCH 1/8] GLContext: Support passing surface dimensions from frontend --- Source/Core/Common/WindowSystemInfo.h | 4 ++++ Source/Core/DolphinNoGUI/PlatformX11.cpp | 2 ++ Source/Core/DolphinQt/MainWindow.cpp | 4 ++++ 3 files changed, 10 insertions(+) diff --git a/Source/Core/Common/WindowSystemInfo.h b/Source/Core/Common/WindowSystemInfo.h index 8936ad1a02e7..1f2c78ab57c2 100644 --- a/Source/Core/Common/WindowSystemInfo.h +++ b/Source/Core/Common/WindowSystemInfo.h @@ -41,6 +41,10 @@ struct WindowSystemInfo // during video backend startup the surface pointer may change (MoltenVK). void* render_surface = nullptr; + // Dimensions of the render surface, if this is determined by the frontend. + int render_surface_width = 0; + int render_surface_height = 0; + // Scale of the render surface. For hidpi systems, this will be >1. float render_surface_scale = 1.0f; }; diff --git a/Source/Core/DolphinNoGUI/PlatformX11.cpp b/Source/Core/DolphinNoGUI/PlatformX11.cpp index 8dcd93bf5231..795689ae7a9a 100644 --- a/Source/Core/DolphinNoGUI/PlatformX11.cpp +++ b/Source/Core/DolphinNoGUI/PlatformX11.cpp @@ -166,6 +166,8 @@ WindowSystemInfo PlatformX11::GetWindowSystemInfo() const wsi.display_connection = static_cast(m_display); wsi.render_window = reinterpret_cast(m_window); wsi.render_surface = reinterpret_cast(m_window); + wsi.render_surface_width = m_window_width; + wsi.render_surface_height = m_window_height; return wsi; } diff --git a/Source/Core/DolphinQt/MainWindow.cpp b/Source/Core/DolphinQt/MainWindow.cpp index be9f8f6db156..d60ea09d05a2 100644 --- a/Source/Core/DolphinQt/MainWindow.cpp +++ b/Source/Core/DolphinQt/MainWindow.cpp @@ -191,6 +191,10 @@ static WindowSystemInfo GetWindowSystemInfo(QWindow* window) wsi.render_surface = wsi.render_window; #endif wsi.render_surface_scale = window ? static_cast(window->devicePixelRatio()) : 1.0f; + wsi.render_surface_width = + window ? static_cast(window->size().width() * window->devicePixelRatio()) : 0; + wsi.render_surface_height = + window ? static_cast(window->size().height() * window->devicePixelRatio()) : 0; return wsi; } From 948d6c18dd1116cd27ccb2a8efb8316a7561afc1 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Thu, 2 May 2019 23:43:10 +1000 Subject: [PATCH 2/8] VideoBackends: Pass window size from frontend when resizing --- Source/Android/jni/MainAndroid.cpp | 7 +++- Source/Core/Common/GL/GLContext.cpp | 4 +- Source/Core/Common/GL/GLContext.h | 4 +- Source/Core/Common/GL/GLInterface/AGL.h | 2 +- Source/Core/Common/GL/GLInterface/AGL.mm | 2 +- Source/Core/Common/GL/GLInterface/EGL.cpp | 32 +++++++++++----- Source/Core/Common/GL/GLInterface/EGL.h | 4 +- Source/Core/Common/GL/GLInterface/EGLX11.cpp | 2 +- Source/Core/Common/GL/GLInterface/EGLX11.h | 2 +- Source/Core/Common/GL/GLInterface/GLX.cpp | 2 +- Source/Core/Common/GL/GLInterface/GLX.h | 2 +- Source/Core/Common/GL/GLInterface/WGL.cpp | 2 +- Source/Core/Common/GL/GLInterface/WGL.h | 2 +- Source/Core/DolphinNoGUI/PlatformX11.cpp | 5 ++- Source/Core/DolphinQt/Host.cpp | 6 +-- Source/Core/DolphinQt/Host.h | 2 +- Source/Core/DolphinQt/MainWindow.cpp | 38 ++++++++++++------- Source/Core/DolphinQt/RenderWidget.cpp | 24 +++++++----- Source/Core/DolphinQt/RenderWidget.h | 4 +- Source/Core/VideoBackends/OGL/OGLRender.cpp | 4 +- .../VideoBackends/Software/SWOGLWindow.cpp | 17 ++++++++- .../Core/VideoBackends/Software/SWOGLWindow.h | 4 ++ .../VideoBackends/Software/SWRenderer.cpp | 27 +++++++------ .../Core/VideoBackends/Software/SWRenderer.h | 2 + .../Core/VideoBackends/Vulkan/VKRenderer.cpp | 10 +++-- .../Core/VideoBackends/Vulkan/VKSwapChain.cpp | 13 ++++--- .../Core/VideoBackends/Vulkan/VKSwapChain.h | 4 +- Source/Core/VideoCommon/RenderBase.cpp | 8 +++- Source/Core/VideoCommon/RenderBase.h | 6 ++- 29 files changed, 158 insertions(+), 83 deletions(-) diff --git a/Source/Android/jni/MainAndroid.cpp b/Source/Android/jni/MainAndroid.cpp index 1e80675d8d17..7bafb9e18157 100644 --- a/Source/Android/jni/MainAndroid.cpp +++ b/Source/Android/jni/MainAndroid.cpp @@ -443,7 +443,10 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SurfaceChang __android_log_print(ANDROID_LOG_ERROR, DOLPHIN_TAG, "Error: Surface is null."); if (g_renderer) - g_renderer->ChangeSurface(s_surf); + { + g_renderer->ChangeSurface(s_surf, ANativeWindow_getWidth(s_surf), + ANativeWindow_getHeight(s_surf)); + } } JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SurfaceDestroyed(JNIEnv*, @@ -470,7 +473,7 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SurfaceDestr std::lock_guard surface_guard(s_surface_lock); if (g_renderer) - g_renderer->ChangeSurface(nullptr); + g_renderer->ChangeSurface(nullptr, 0, 0); if (s_surf) { diff --git a/Source/Core/Common/GL/GLContext.cpp b/Source/Core/Common/GL/GLContext.cpp index f7b6ca8af89f..04ade079f7f5 100644 --- a/Source/Core/Common/GL/GLContext.cpp +++ b/Source/Core/Common/GL/GLContext.cpp @@ -57,11 +57,11 @@ bool GLContext::ClearCurrent() return false; } -void GLContext::Update() +void GLContext::UpdateDimensions(int window_width, int window_height) { } -void GLContext::UpdateSurface(void* window_handle) +void GLContext::UpdateSurface(void* window_handle, int window_width, int window_height) { } diff --git a/Source/Core/Common/GL/GLContext.h b/Source/Core/Common/GL/GLContext.h index a1d0a931bfe5..7ccd68e15613 100644 --- a/Source/Core/Common/GL/GLContext.h +++ b/Source/Core/Common/GL/GLContext.h @@ -36,8 +36,8 @@ class GLContext virtual bool MakeCurrent(); virtual bool ClearCurrent(); - virtual void Update(); - virtual void UpdateSurface(void* window_handle); + virtual void UpdateDimensions(int window_width, int window_height); + virtual void UpdateSurface(void* window_handle, int window_width, int window_height); virtual void Swap(); virtual void SwapInterval(int interval); diff --git a/Source/Core/Common/GL/GLInterface/AGL.h b/Source/Core/Common/GL/GLInterface/AGL.h index dda9fee0e31f..f76733420f30 100644 --- a/Source/Core/Common/GL/GLInterface/AGL.h +++ b/Source/Core/Common/GL/GLInterface/AGL.h @@ -28,7 +28,7 @@ class GLContextAGL final : public GLContext bool MakeCurrent() override; bool ClearCurrent() override; - void Update() override; + void UpdateDimensions(int window_width, int window_height) override; void Swap() override; void SwapInterval(int interval) override; diff --git a/Source/Core/Common/GL/GLInterface/AGL.mm b/Source/Core/Common/GL/GLInterface/AGL.mm index a6fc7f0c0e8b..b0358a17ddd3 100644 --- a/Source/Core/Common/GL/GLInterface/AGL.mm +++ b/Source/Core/Common/GL/GLInterface/AGL.mm @@ -144,7 +144,7 @@ static bool AttachContextToView(NSOpenGLContext* context, NSView* view, u32* wid return true; } -void GLContextAGL::Update() +void GLContextAGL::UpdateDimensions(int window_width, int window_height) { if (!m_view) return; diff --git a/Source/Core/Common/GL/GLInterface/EGL.cpp b/Source/Core/Common/GL/GLInterface/EGL.cpp index 30230e7d082e..9a3b0461ac88 100644 --- a/Source/Core/Common/GL/GLInterface/EGL.cpp +++ b/Source/Core/Common/GL/GLInterface/EGL.cpp @@ -301,15 +301,7 @@ bool GLContextEGL::CreateWindowSurface() } // Get dimensions from the surface. - EGLint surface_width = 1, surface_height = 1; - if (!eglQuerySurface(m_egl_display, m_egl_surface, EGL_WIDTH, &surface_width) || - !eglQuerySurface(m_egl_display, m_egl_surface, EGL_HEIGHT, &surface_height)) - { - WARN_LOG_FMT(VIDEO, - "Failed to get surface dimensions via eglQuerySurface. Size may be incorrect."); - } - m_backbuffer_width = static_cast(surface_width); - m_backbuffer_height = static_cast(surface_height); + QueryDimensions(); } else if (!m_supports_surfaceless) { @@ -347,9 +339,16 @@ bool GLContextEGL::MakeCurrent() return eglMakeCurrent(m_egl_display, m_egl_surface, m_egl_surface, m_egl_context); } -void GLContextEGL::UpdateSurface(void* window_handle) +void GLContextEGL::UpdateDimensions(int window_width, int window_height) +{ + QueryDimensions(); +} + +void GLContextEGL::UpdateSurface(void* window_handle, int window_width, int window_height) { m_wsi.render_surface = window_handle; + m_wsi.render_surface_width = window_width; + m_wsi.render_surface_height = window_height; ClearCurrent(); DestroyWindowSurface(); CreateWindowSurface(); @@ -376,3 +375,16 @@ void GLContextEGL::DestroyContext() m_egl_context = EGL_NO_CONTEXT; m_egl_display = EGL_NO_DISPLAY; } + +void GLContextEGL::QueryDimensions() +{ + EGLint surface_width = 1, surface_height = 1; + if (!eglQuerySurface(m_egl_display, m_egl_surface, EGL_WIDTH, &surface_width) || + !eglQuerySurface(m_egl_display, m_egl_surface, EGL_HEIGHT, &surface_height)) + { + WARN_LOG_FMT(VIDEO, + "Failed to get surface dimensions via eglQuerySurface. Size may be incorrect."); + } + m_backbuffer_width = static_cast(surface_width); + m_backbuffer_height = static_cast(surface_height); +} diff --git a/Source/Core/Common/GL/GLInterface/EGL.h b/Source/Core/Common/GL/GLInterface/EGL.h index bed1b31ecc8b..6eeb35e96d59 100644 --- a/Source/Core/Common/GL/GLInterface/EGL.h +++ b/Source/Core/Common/GL/GLInterface/EGL.h @@ -22,7 +22,8 @@ class GLContextEGL : public GLContext bool MakeCurrent() override; bool ClearCurrent() override; - void UpdateSurface(void* window_handle) override; + void UpdateDimensions(int window_width, int window_height); + void UpdateSurface(void* window_handle, int window_width, int window_height) override; void Swap() override; void SwapInterval(int interval) override; @@ -39,6 +40,7 @@ class GLContextEGL : public GLContext void DestroyWindowSurface(); void DetectMode(); void DestroyContext(); + void QueryDimensions(); WindowSystemInfo m_wsi = {}; diff --git a/Source/Core/Common/GL/GLInterface/EGLX11.cpp b/Source/Core/Common/GL/GLInterface/EGLX11.cpp index f4308983615d..d2496c2cf4ba 100644 --- a/Source/Core/Common/GL/GLInterface/EGLX11.cpp +++ b/Source/Core/Common/GL/GLInterface/EGLX11.cpp @@ -11,7 +11,7 @@ GLContextEGLX11::~GLContextEGLX11() m_render_window.reset(); } -void GLContextEGLX11::Update() +void GLContextEGLX11::UpdateDimensions(int window_width, int window_height) { m_render_window->UpdateDimensions(); m_backbuffer_width = m_render_window->GetWidth(); diff --git a/Source/Core/Common/GL/GLInterface/EGLX11.h b/Source/Core/Common/GL/GLInterface/EGLX11.h index 6f3033334156..b199881116fa 100644 --- a/Source/Core/Common/GL/GLInterface/EGLX11.h +++ b/Source/Core/Common/GL/GLInterface/EGLX11.h @@ -13,7 +13,7 @@ class GLContextEGLX11 final : public GLContextEGL public: ~GLContextEGLX11() override; - void Update() override; + void UpdateDimensions(int window_width, int window_height) override; protected: EGLDisplay OpenEGLDisplay() override; diff --git a/Source/Core/Common/GL/GLInterface/GLX.cpp b/Source/Core/Common/GL/GLInterface/GLX.cpp index b04296d0968f..e6116bc9999f 100644 --- a/Source/Core/Common/GL/GLInterface/GLX.cpp +++ b/Source/Core/Common/GL/GLInterface/GLX.cpp @@ -310,7 +310,7 @@ bool GLContextGLX::ClearCurrent() return glXMakeCurrent(m_display, None, nullptr); } -void GLContextGLX::Update() +void GLContextGLX::UpdateDimensions(int window_width, int window_height) { m_render_window->UpdateDimensions(); m_backbuffer_width = m_render_window->GetWidth(); diff --git a/Source/Core/Common/GL/GLInterface/GLX.h b/Source/Core/Common/GL/GLInterface/GLX.h index ec4bd29cb995..cf8b81a42d2a 100644 --- a/Source/Core/Common/GL/GLInterface/GLX.h +++ b/Source/Core/Common/GL/GLInterface/GLX.h @@ -24,7 +24,7 @@ class GLContextGLX final : public GLContext bool MakeCurrent() override; bool ClearCurrent() override; - void Update() override; + void UpdateDimensions(int window_width, int window_height) override; void SwapInterval(int Interval) override; void Swap() override; diff --git a/Source/Core/Common/GL/GLInterface/WGL.cpp b/Source/Core/Common/GL/GLInterface/WGL.cpp index 286859daf661..cf2dbe06179c 100644 --- a/Source/Core/Common/GL/GLInterface/WGL.cpp +++ b/Source/Core/Common/GL/GLInterface/WGL.cpp @@ -480,7 +480,7 @@ bool GLContextWGL::ClearCurrent() } // Update window width, size and etc. Called from Render.cpp -void GLContextWGL::Update() +void GLContextWGL::UpdateDimensions(int window_width, int window_height) { RECT rcWindow; GetClientRect(m_window_handle, &rcWindow); diff --git a/Source/Core/Common/GL/GLInterface/WGL.h b/Source/Core/Common/GL/GLInterface/WGL.h index ad2b66502837..4ee867eb03d9 100644 --- a/Source/Core/Common/GL/GLInterface/WGL.h +++ b/Source/Core/Common/GL/GLInterface/WGL.h @@ -19,7 +19,7 @@ class GLContextWGL final : public GLContext bool MakeCurrent() override; bool ClearCurrent() override; - void Update() override; + void UpdateDimensions(int window_width, int window_height) override; void Swap() override; void SwapInterval(int interval) override; diff --git a/Source/Core/DolphinNoGUI/PlatformX11.cpp b/Source/Core/DolphinNoGUI/PlatformX11.cpp index 795689ae7a9a..0bee07d7d140 100644 --- a/Source/Core/DolphinNoGUI/PlatformX11.cpp +++ b/Source/Core/DolphinNoGUI/PlatformX11.cpp @@ -266,7 +266,10 @@ void PlatformX11::ProcessEvents() case ConfigureNotify: { if (g_renderer) - g_renderer->ResizeSurface(); + { + UpdateWindowPosition(); + g_renderer->ResizeSurface(m_window_width, m_window_height); + } } break; } diff --git a/Source/Core/DolphinQt/Host.cpp b/Source/Core/DolphinQt/Host.cpp index 5ccd10189c23..4ee94361733a 100644 --- a/Source/Core/DolphinQt/Host.cpp +++ b/Source/Core/DolphinQt/Host.cpp @@ -68,7 +68,7 @@ bool Host::IsHostThread() return tls_is_host_thread; } -void Host::SetRenderHandle(void* handle) +void Host::SetRenderHandle(void* handle, int width, int height) { m_render_to_main = Config::Get(Config::MAIN_RENDER_TO_MAIN); @@ -78,7 +78,7 @@ void Host::SetRenderHandle(void* handle) m_render_handle = handle; if (g_renderer) { - g_renderer->ChangeSurface(handle); + g_renderer->ChangeSurface(handle, width, height); g_controller_interface.ChangeWindow(handle); } } @@ -189,7 +189,7 @@ void Host::SetRenderFullscreen(bool fullscreen) void Host::ResizeSurface(int new_width, int new_height) { if (g_renderer) - g_renderer->ResizeSurface(); + g_renderer->ResizeSurface(new_width, new_height); } std::vector Host_GetPreferredLocales() diff --git a/Source/Core/DolphinQt/Host.h b/Source/Core/DolphinQt/Host.h index 6ddc4cb3e470..40e24aa8a69b 100644 --- a/Source/Core/DolphinQt/Host.h +++ b/Source/Core/DolphinQt/Host.h @@ -30,7 +30,7 @@ class Host final : public QObject bool GetGBAFocus(); void SetMainWindowHandle(void* handle); - void SetRenderHandle(void* handle); + void SetRenderHandle(void* handle, int width, int height); void SetRenderFocus(bool focus); void SetRenderFullFocus(bool focus); void SetRenderFullscreen(bool fullscreen); diff --git a/Source/Core/DolphinQt/MainWindow.cpp b/Source/Core/DolphinQt/MainWindow.cpp index d60ea09d05a2..2a2aca618fe4 100644 --- a/Source/Core/DolphinQt/MainWindow.cpp +++ b/Source/Core/DolphinQt/MainWindow.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -172,30 +173,40 @@ static WindowSystemType GetWindowSystemType() return WindowSystemType::Headless; } -static WindowSystemInfo GetWindowSystemInfo(QWindow* window) +static WindowSystemInfo GetWindowSystemInfo(QWidget* widget) { WindowSystemInfo wsi; wsi.type = GetWindowSystemType(); + QWindow* window = widget ? widget->windowHandle() : nullptr; + if (!widget || !window) + { + wsi.display_connection = nullptr; + wsi.render_window = nullptr; + wsi.render_surface = nullptr; + wsi.render_surface_scale = 1.0f; + wsi.render_surface_width = 0; + wsi.render_surface_height = 0; + return wsi; + } + // Our Win32 Qt external doesn't have the private API. #if defined(WIN32) || defined(__APPLE__) || defined(__HAIKU__) - wsi.render_window = window ? reinterpret_cast(window->winId()) : nullptr; + wsi.render_window = reinterpret_cast(window->winId()); wsi.render_surface = wsi.render_window; #else QPlatformNativeInterface* pni = QGuiApplication::platformNativeInterface(); wsi.display_connection = pni->nativeResourceForWindow("display", window); if (wsi.type == WindowSystemType::Wayland) - wsi.render_window = window ? pni->nativeResourceForWindow("surface", window) : nullptr; + wsi.render_window = pni->nativeResourceForWindow("surface", window); else - wsi.render_window = window ? reinterpret_cast(window->winId()) : nullptr; + wsi.render_window = reinterpret_cast(window->winId()); + wsi.render_surface = wsi.render_window; #endif - wsi.render_surface_scale = window ? static_cast(window->devicePixelRatio()) : 1.0f; - wsi.render_surface_width = - window ? static_cast(window->size().width() * window->devicePixelRatio()) : 0; - wsi.render_surface_height = - window ? static_cast(window->size().height() * window->devicePixelRatio()) : 0; - + wsi.render_surface_scale = static_cast(widget->screen()->devicePixelRatio()); + wsi.render_surface_width = static_cast(widget->size().width() * wsi.render_surface_scale); + wsi.render_surface_height = static_cast(widget->size().height() * wsi.render_surface_scale); return wsi; } @@ -327,7 +338,7 @@ void MainWindow::InitControllers() if (g_controller_interface.IsInit()) return; - UICommon::InitControllers(GetWindowSystemInfo(windowHandle())); + UICommon::InitControllers(GetWindowSystemInfo(this)); m_hotkey_scheduler = new HotkeyScheduler(); m_hotkey_scheduler->Start(); @@ -1053,8 +1064,7 @@ void MainWindow::StartGame(std::unique_ptr&& parameters) ShowRenderWidget(); // Boot up, show an error if it fails to load the game. - if (!BootManager::BootCore(std::move(parameters), - GetWindowSystemInfo(m_render_widget->windowHandle()))) + if (!BootManager::BootCore(std::move(parameters), GetWindowSystemInfo(m_render_widget))) { ModalMessageBox::critical(this, tr("Error"), tr("Failed to init core"), QMessageBox::Ok); HideRenderWidget(); @@ -1162,7 +1172,7 @@ void MainWindow::HideRenderWidget(bool reinit, bool is_exit) // The controller interface will still be registered to the old render widget, if the core // has booted. Therefore, we should re-bind it to the main window for now. When the core // is next started, it will be swapped back to the new render widget. - g_controller_interface.ChangeWindow(GetWindowSystemInfo(windowHandle()).render_window, + g_controller_interface.ChangeWindow(GetWindowSystemInfo(this).render_surface, is_exit ? ControllerInterface::WindowChangeReason::Exit : ControllerInterface::WindowChangeReason::Other); } diff --git a/Source/Core/DolphinQt/RenderWidget.cpp b/Source/Core/DolphinQt/RenderWidget.cpp index cb55877ace98..0ae2a9f60516 100644 --- a/Source/Core/DolphinQt/RenderWidget.cpp +++ b/Source/Core/DolphinQt/RenderWidget.cpp @@ -204,6 +204,11 @@ void RenderWidget::showFullScreen() emit SizeChanged(width() * dpr, height() * dpr); } +float RenderWidget::GetDevicePixelRatio() const +{ + return screen()->devicePixelRatio(); +} + // Lock the cursor within the window/widget internal borders, including the aspect ratio if wanted void RenderWidget::SetCursorLocked(bool locked, bool follow_aspect_ratio) { @@ -379,8 +384,12 @@ bool RenderWidget::event(QEvent* event) } break; case QEvent::WinIdChange: - emit HandleChanged(reinterpret_cast(winId())); - break; + { + const float dpr = GetDevicePixelRatio(); + emit HandleChanged(reinterpret_cast(winId()), static_cast(width() * dpr), + static_cast(height() * dpr)); + } + break; case QEvent::Show: // Don't do if "stay on top" changed (or was true) if (Settings::Instance().GetLockCursor() && @@ -443,13 +452,10 @@ bool RenderWidget::event(QEvent* event) SetCursorLocked(m_cursor_locked); const QResizeEvent* se = static_cast(event); - QSize new_size = se->size(); - - QScreen* screen = window()->windowHandle()->screen(); - - const auto dpr = screen->devicePixelRatio(); - - emit SizeChanged(new_size.width() * dpr, new_size.height() * dpr); + const QSize new_size = se->size(); + const float dpr = GetDevicePixelRatio(); + emit SizeChanged(static_cast(new_size.width() * dpr), + static_cast(new_size.height() * dpr)); break; } // Happens when we add/remove the widget from the main window instead of the dedicated one diff --git a/Source/Core/DolphinQt/RenderWidget.h b/Source/Core/DolphinQt/RenderWidget.h index c8ea1e3a8486..942a7c1d1000 100644 --- a/Source/Core/DolphinQt/RenderWidget.h +++ b/Source/Core/DolphinQt/RenderWidget.h @@ -24,10 +24,12 @@ class RenderWidget final : public QWidget void SetWaitingForMessageBox(bool waiting_for_message_box); void SetCursorLocked(bool locked, bool follow_aspect_ratio = true); + float GetDevicePixelRatio() const; + signals: void EscapePressed(); void Closed(); - void HandleChanged(void* handle); + void HandleChanged(void* handle, int new_width, int new_height); void StateChanged(bool fullscreen); void SizeChanged(int new_width, int new_height); void FocusChanged(bool focus); diff --git a/Source/Core/VideoBackends/OGL/OGLRender.cpp b/Source/Core/VideoBackends/OGL/OGLRender.cpp index 49400d73ff45..dd812a7c87bd 100644 --- a/Source/Core/VideoBackends/OGL/OGLRender.cpp +++ b/Source/Core/VideoBackends/OGL/OGLRender.cpp @@ -1079,7 +1079,7 @@ void Renderer::CheckForSurfaceChange() if (!m_surface_changed.TestAndClear()) return; - m_main_gl_context->UpdateSurface(m_new_surface_handle); + m_main_gl_context->UpdateSurface(m_new_surface_handle, m_new_surface_width, m_new_surface_height); m_new_surface_handle = nullptr; // With a surface change, the window likely has new dimensions. @@ -1093,7 +1093,7 @@ void Renderer::CheckForSurfaceResize() if (!m_surface_resized.TestAndClear()) return; - m_main_gl_context->Update(); + m_main_gl_context->UpdateDimensions(m_new_surface_width, m_new_surface_height); m_backbuffer_width = m_main_gl_context->GetBackBufferWidth(); m_backbuffer_height = m_main_gl_context->GetBackBufferHeight(); m_system_framebuffer->UpdateDimensions(m_backbuffer_width, m_backbuffer_height); diff --git a/Source/Core/VideoBackends/Software/SWOGLWindow.cpp b/Source/Core/VideoBackends/Software/SWOGLWindow.cpp index a002e3e51980..120a8b3dbdd7 100644 --- a/Source/Core/VideoBackends/Software/SWOGLWindow.cpp +++ b/Source/Core/VideoBackends/Software/SWOGLWindow.cpp @@ -32,6 +32,16 @@ bool SWOGLWindow::IsHeadless() const return m_gl_context->IsHeadless(); } +u32 SWOGLWindow::GetWidth() const +{ + return m_gl_context->GetBackBufferWidth(); +} + +u32 SWOGLWindow::GetHeight() const +{ + return m_gl_context->GetBackBufferHeight(); +} + bool SWOGLWindow::Initialize(const WindowSystemInfo& wsi) { m_gl_context = GLContext::Create(wsi); @@ -84,11 +94,16 @@ bool SWOGLWindow::Initialize(const WindowSystemInfo& wsi) return true; } +void SWOGLWindow::UpdateDimensions(int window_width, int window_height) +{ + // just updates the render window position and the backbuffer size + m_gl_context->UpdateDimensions(window_width, window_height); +} + void SWOGLWindow::ShowImage(const AbstractTexture* image, const MathUtil::Rectangle& xfb_region) { const SW::SWTexture* sw_image = static_cast(image); - m_gl_context->Update(); // just updates the render window position and the backbuffer size GLsizei glWidth = (GLsizei)m_gl_context->GetBackBufferWidth(); GLsizei glHeight = (GLsizei)m_gl_context->GetBackBufferHeight(); diff --git a/Source/Core/VideoBackends/Software/SWOGLWindow.h b/Source/Core/VideoBackends/Software/SWOGLWindow.h index 965f7f4e4318..2a4b522096db 100644 --- a/Source/Core/VideoBackends/Software/SWOGLWindow.h +++ b/Source/Core/VideoBackends/Software/SWOGLWindow.h @@ -20,6 +20,10 @@ class SWOGLWindow GLContext* GetContext() const { return m_gl_context.get(); } bool IsHeadless() const; + u32 GetWidth() const; + u32 GetHeight() const; + void UpdateDimensions(int window_width, int window_height); + // Image to show, will be swapped immediately void ShowImage(const AbstractTexture* image, const MathUtil::Rectangle& xfb_region); diff --git a/Source/Core/VideoBackends/Software/SWRenderer.cpp b/Source/Core/VideoBackends/Software/SWRenderer.cpp index 76eea930689c..b4fa852a32ba 100644 --- a/Source/Core/VideoBackends/Software/SWRenderer.cpp +++ b/Source/Core/VideoBackends/Software/SWRenderer.cpp @@ -59,18 +59,6 @@ SWRenderer::CreateFramebuffer(AbstractTexture* color_attachment, AbstractTexture static_cast(depth_attachment)); } -void SWRenderer::BindBackbuffer(const ClearColor& clear_color) -{ - // Look for framebuffer resizes - if (!m_surface_resized.TestAndClear()) - return; - - GLContext* context = m_window->GetContext(); - context->Update(); - m_backbuffer_width = context->GetBackBufferWidth(); - m_backbuffer_height = context->GetBackBufferHeight(); -} - class SWShader final : public AbstractShader { public: @@ -108,6 +96,11 @@ std::unique_ptr SWRenderer::CreatePipeline(const AbstractPipel return std::make_unique(); } +void SWRenderer::BindBackbuffer(const ClearColor& clear_color) +{ + CheckForSurfaceResize(); +} + // Called on the GPU thread void SWRenderer::RenderXFBToScreen(const MathUtil::Rectangle& target_rc, const AbstractTexture* source_texture, @@ -117,6 +110,16 @@ void SWRenderer::RenderXFBToScreen(const MathUtil::Rectangle& target_rc, m_window->ShowImage(source_texture, source_rc); } +void SWRenderer::CheckForSurfaceResize() +{ + if (!m_surface_resized.TestAndClear()) + return; + + m_window->UpdateDimensions(m_new_surface_width, m_new_surface_height); + m_backbuffer_width = static_cast(m_window->GetWidth()); + m_backbuffer_height = static_cast(m_window->GetHeight()); +} + u32 SWRenderer::AccessEFB(EFBAccessType type, u32 x, u32 y, u32 InputData) { u32 value = 0; diff --git a/Source/Core/VideoBackends/Software/SWRenderer.h b/Source/Core/VideoBackends/Software/SWRenderer.h index 8aa9aa4af5d1..034b1aa4e8bd 100644 --- a/Source/Core/VideoBackends/Software/SWRenderer.h +++ b/Source/Core/VideoBackends/Software/SWRenderer.h @@ -64,6 +64,8 @@ class SWRenderer final : public Renderer std::unique_ptr CreateBoundingBox() const override; private: + void CheckForSurfaceResize(); + std::unique_ptr m_window; }; } // namespace SW diff --git a/Source/Core/VideoBackends/Vulkan/VKRenderer.cpp b/Source/Core/VideoBackends/Vulkan/VKRenderer.cpp index 23dba4613bcd..19d6c4c67b23 100644 --- a/Source/Core/VideoBackends/Vulkan/VKRenderer.cpp +++ b/Source/Core/VideoBackends/Vulkan/VKRenderer.cpp @@ -306,7 +306,7 @@ void Renderer::BindBackbuffer(const ClearColor& clear_color) else if (res == VK_SUBOPTIMAL_KHR || res == VK_ERROR_OUT_OF_DATE_KHR) { INFO_LOG_FMT(VIDEO, "Resizing swap chain due to suboptimal/out-of-date"); - m_swap_chain->ResizeSwapChain(); + m_swap_chain->ResizeSwapChain(m_backbuffer_width, m_backbuffer_height); } else { @@ -314,6 +314,7 @@ void Renderer::BindBackbuffer(const ClearColor& clear_color) m_swap_chain->RecreateSwapChain(); } + OnSwapChainResized(); res = m_swap_chain->AcquireNextImage(); if (res != VK_SUCCESS) PanicAlertFmt("Failed to grab image from swap chain: {:#010X}", res); @@ -384,8 +385,11 @@ void Renderer::CheckForSurfaceChange() g_command_buffer_mgr->CheckLastPresentFail(); // Recreate the surface. If this fails we're in trouble. - if (!m_swap_chain->RecreateSurface(m_new_surface_handle)) + if (!m_swap_chain->RecreateSurface(m_new_surface_handle, m_new_surface_width, + m_new_surface_height)) + { PanicAlertFmt("Failed to recreate Vulkan surface. Cannot continue."); + } m_new_surface_handle = nullptr; // Handle case where the dimensions are now different. @@ -412,7 +416,7 @@ void Renderer::CheckForSurfaceResize() g_command_buffer_mgr->CheckLastPresentFail(); // Resize the swap chain. - m_swap_chain->RecreateSwapChain(); + m_swap_chain->ResizeSwapChain(m_new_surface_width, m_new_surface_height); OnSwapChainResized(); } diff --git a/Source/Core/VideoBackends/Vulkan/VKSwapChain.cpp b/Source/Core/VideoBackends/Vulkan/VKSwapChain.cpp index a7f5bb929dad..e5a6b8c71a44 100644 --- a/Source/Core/VideoBackends/Vulkan/VKSwapChain.cpp +++ b/Source/Core/VideoBackends/Vulkan/VKSwapChain.cpp @@ -15,7 +15,6 @@ #include "VideoBackends/Vulkan/ObjectCache.h" #include "VideoBackends/Vulkan/VKTexture.h" #include "VideoBackends/Vulkan/VulkanContext.h" -#include "VideoCommon/RenderBase.h" #if defined(VK_USE_PLATFORM_XLIB_KHR) #include @@ -265,8 +264,8 @@ bool SwapChain::CreateSwapChain() VkExtent2D size = surface_capabilities.currentExtent; if (size.width == UINT32_MAX) { - size.width = std::max(g_renderer->GetBackbufferWidth(), 1); - size.height = std::max(g_renderer->GetBackbufferHeight(), 1); + size.width = static_cast(m_wsi.render_surface_width); + size.height = static_cast(m_wsi.render_surface_height); } size.width = std::clamp(size.width, surface_capabilities.minImageExtent.width, surface_capabilities.maxImageExtent.width); @@ -463,9 +462,11 @@ VkResult SwapChain::AcquireNextImage() return res; } -bool SwapChain::ResizeSwapChain() +bool SwapChain::ResizeSwapChain(int window_width, int window_height) { DestroySwapChainImages(); + m_wsi.render_surface_width = window_width; + m_wsi.render_surface_height = window_height; if (!CreateSwapChain() || !SetupSwapChainImages()) { PanicAlertFmt("Failed to re-configure swap chain images, this is fatal (for now)"); @@ -531,7 +532,7 @@ bool SwapChain::SetFullscreenState(bool state) #endif } -bool SwapChain::RecreateSurface(void* native_handle) +bool SwapChain::RecreateSurface(void* native_handle, int window_width, int window_height) { // Destroy the old swap chain, images, and surface. DestroySwapChainImages(); @@ -540,6 +541,8 @@ bool SwapChain::RecreateSurface(void* native_handle) // Re-create the surface with the new native handle m_wsi.render_surface = native_handle; + m_wsi.render_surface_width = window_width; + m_wsi.render_surface_height = window_height; m_surface = CreateVulkanSurface(g_vulkan_context->GetVulkanInstance(), m_wsi); if (m_surface == VK_NULL_HANDLE) return false; diff --git a/Source/Core/VideoBackends/Vulkan/VKSwapChain.h b/Source/Core/VideoBackends/Vulkan/VKSwapChain.h index 5e67217f2d7a..7fb8c21f530a 100644 --- a/Source/Core/VideoBackends/Vulkan/VKSwapChain.h +++ b/Source/Core/VideoBackends/Vulkan/VKSwapChain.h @@ -52,8 +52,8 @@ class SwapChain } VkResult AcquireNextImage(); - bool RecreateSurface(void* native_handle); - bool ResizeSwapChain(); + bool RecreateSurface(void* native_handle, int window_width, int window_height); + bool ResizeSwapChain(int window_width, int window_height); bool RecreateSwapChain(); // Change vsync enabled state. This may fail as it causes a swapchain recreation. diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp index f787ab7dabb7..abd50cdcca00 100644 --- a/Source/Core/VideoCommon/RenderBase.cpp +++ b/Source/Core/VideoCommon/RenderBase.cpp @@ -726,16 +726,20 @@ bool Renderer::IsHeadless() const return true; } -void Renderer::ChangeSurface(void* new_surface_handle) +void Renderer::ChangeSurface(void* new_surface_handle, int new_width, int new_height) { std::lock_guard lock(m_swap_mutex); m_new_surface_handle = new_surface_handle; + m_new_surface_width = new_width; + m_new_surface_height = new_height; m_surface_changed.Set(); } -void Renderer::ResizeSurface() +void Renderer::ResizeSurface(int new_width, int new_height) { std::lock_guard lock(m_swap_mutex); + m_new_surface_width = new_width; + m_new_surface_height = new_height; m_surface_resized.Set(); } diff --git a/Source/Core/VideoCommon/RenderBase.h b/Source/Core/VideoCommon/RenderBase.h index 8824e6ff77f0..4692036a9660 100644 --- a/Source/Core/VideoCommon/RenderBase.h +++ b/Source/Core/VideoCommon/RenderBase.h @@ -245,8 +245,8 @@ class Renderer VideoCommon::PostProcessing* GetPostProcessor() const { return m_post_processor.get(); } // Final surface changing // This is called when the surface is resized (WX) or the window changes (Android). - void ChangeSurface(void* new_surface_handle); - void ResizeSurface(); + void ChangeSurface(void* new_surface_handle, int new_width, int new_height); + void ResizeSurface(int new_width, int new_height); bool UseVertexDepthRange() const; void DoState(PointerWrap& p); @@ -340,6 +340,8 @@ class Renderer std::unique_ptr m_post_processor; void* m_new_surface_handle = nullptr; + int m_new_surface_width = 0; + int m_new_surface_height = 0; Common::Flag m_surface_changed; Common::Flag m_surface_resized; std::mutex m_swap_mutex; From 84d0089e8aa0b0780155be551a770547b6c47f49 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Thu, 2 May 2019 23:43:14 +1000 Subject: [PATCH 3/8] GLContext: Add support for Wayland --- CMakeLists.txt | 9 +++++ Source/Core/Common/CMakeLists.txt | 19 +++++++--- Source/Core/Common/GL/GLContext.cpp | 7 ++++ Source/Core/Common/GL/GLInterface/EGL.cpp | 4 +-- Source/Core/Common/GL/GLInterface/EGL.h | 2 ++ .../Core/Common/GL/GLInterface/EGLWayland.cpp | 36 +++++++++++++++++++ .../Core/Common/GL/GLInterface/EGLWayland.h | 19 ++++++++++ 7 files changed, 89 insertions(+), 7 deletions(-) create mode 100644 Source/Core/Common/GL/GLInterface/EGLWayland.cpp create mode 100644 Source/Core/Common/GL/GLInterface/EGLWayland.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 04bcdde4c2f0..7972d831b41c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,6 +43,7 @@ set(DOLPHIN_DEFAULT_UPDATE_TRACK "" CACHE STRING "Name of the default update tra if(UNIX AND NOT APPLE AND NOT ANDROID) option(ENABLE_X11 "Enables X11 Support" ON) + option(ENABLE_WAYLAND "Enables Wayland Support" OFF) endif() if(NOT WIN32 AND NOT APPLE AND NOT HAIKU) option(ENABLE_EGL "Enables EGL OpenGL Interface" ON) @@ -559,6 +560,14 @@ if(ENABLE_EGL) endif() endif() +if(ENABLE_WAYLAND) + find_package(ECM REQUIRED NO_MODULE) + list(APPEND CMAKE_MODULE_PATH "${ECM_MODULE_PATH}") + find_package(Wayland REQUIRED Client Egl) + add_definitions(-DHAVE_WAYLAND=1) + message(STATUS "Wayland support enabled") +endif() + if(ENCODE_FRAMEDUMPS) if(WIN32) if(_M_X86_64) diff --git a/Source/Core/Common/CMakeLists.txt b/Source/Core/Common/CMakeLists.txt index 9f2e66c982d2..01367960c620 100644 --- a/Source/Core/Common/CMakeLists.txt +++ b/Source/Core/Common/CMakeLists.txt @@ -263,11 +263,20 @@ if(ENABLE_EGL AND EGL_FOUND) GL/GLInterface/EGLAndroid.cpp GL/GLInterface/EGLAndroid.h ) - elseif(ENABLE_X11 AND X11_FOUND) - target_sources(common PRIVATE - GL/GLInterface/EGLX11.cpp - GL/GLInterface/EGLX11.h - ) + else() + if(ENABLE_X11 AND X11_FOUND) + target_sources(common PRIVATE + GL/GLInterface/EGLX11.cpp + GL/GLInterface/EGLX11.h + ) + endif() + if(ENABLE_WAYLAND AND WAYLAND_FOUND) + target_sources(common PRIVATE + GL/GLInterface/EGLWayland.cpp + GL/GLInterface/EGLWayland.h + ) + target_link_libraries(common PRIVATE Wayland::Egl) + endif() endif() target_include_directories(common PRIVATE ${EGL_INCLUDE_DIRS}) target_link_libraries(common PUBLIC ${EGL_LIBRARIES}) diff --git a/Source/Core/Common/GL/GLContext.cpp b/Source/Core/Common/GL/GLContext.cpp index 04ade079f7f5..7d07303617a4 100644 --- a/Source/Core/Common/GL/GLContext.cpp +++ b/Source/Core/Common/GL/GLContext.cpp @@ -25,6 +25,9 @@ #if defined(ANDROID) #include "Common/GL/GLInterface/EGLAndroid.h" #endif +#if HAVE_WAYLAND +#include "Common/GL/GLInterface/EGLWayland.h" +#endif #endif const std::array, 9> GLContext::s_desktop_opengl_versions = { @@ -113,6 +116,10 @@ std::unique_ptr GLContext::Create(const WindowSystemInfo& wsi, bool s #endif } #endif +#if HAVE_WAYLAND + if (wsi.type == WindowSystemType::Wayland) + context = std::make_unique(); +#endif #if HAVE_EGL if (wsi.type == WindowSystemType::Headless || wsi.type == WindowSystemType::FBDev) context = std::make_unique(); diff --git a/Source/Core/Common/GL/GLInterface/EGL.cpp b/Source/Core/Common/GL/GLInterface/EGL.cpp index 9a3b0461ac88..9e644830e96d 100644 --- a/Source/Core/Common/GL/GLInterface/EGL.cpp +++ b/Source/Core/Common/GL/GLInterface/EGL.cpp @@ -292,8 +292,8 @@ bool GLContextEGL::CreateWindowSurface() { if (!IsHeadless()) { - EGLNativeWindowType native_window = GetEGLNativeWindow(m_config); - m_egl_surface = eglCreateWindowSurface(m_egl_display, m_config, native_window, nullptr); + m_native_window = GetEGLNativeWindow(m_config); + m_egl_surface = eglCreateWindowSurface(m_egl_display, m_config, m_native_window, nullptr); if (!m_egl_surface) { INFO_LOG_FMT(VIDEO, "Error: eglCreateWindowSurface failed"); diff --git a/Source/Core/Common/GL/GLInterface/EGL.h b/Source/Core/Common/GL/GLInterface/EGL.h index 6eeb35e96d59..5029765265e4 100644 --- a/Source/Core/Common/GL/GLInterface/EGL.h +++ b/Source/Core/Common/GL/GLInterface/EGL.h @@ -44,6 +44,8 @@ class GLContextEGL : public GLContext WindowSystemInfo m_wsi = {}; + EGLNativeWindowType m_native_window = {}; + EGLConfig m_config; bool m_supports_surfaceless = false; std::vector m_attribs; diff --git a/Source/Core/Common/GL/GLInterface/EGLWayland.cpp b/Source/Core/Common/GL/GLInterface/EGLWayland.cpp new file mode 100644 index 000000000000..422c167a2e99 --- /dev/null +++ b/Source/Core/Common/GL/GLInterface/EGLWayland.cpp @@ -0,0 +1,36 @@ +// Copyright 2019 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "Common/GL/GLInterface/EGLWayland.h" +#include + +GLContextEGLWayland::~GLContextEGLWayland() +{ + if (m_native_window) + wl_egl_window_destroy(reinterpret_cast(m_native_window)); +} + +EGLDisplay GLContextEGLWayland::OpenEGLDisplay() +{ + return eglGetDisplay(reinterpret_cast(m_wsi.display_connection)); +} + +void GLContextEGLWayland::UpdateDimensions(int window_width, int window_height) +{ + wl_egl_window_resize(reinterpret_cast(m_native_window), window_width, + window_height, 0, 0); + m_backbuffer_width = window_width; + m_backbuffer_height = window_height; +} + +EGLNativeWindowType GLContextEGLWayland::GetEGLNativeWindow(EGLConfig config) +{ + wl_egl_window* window = + wl_egl_window_create(static_cast(m_wsi.render_surface), + m_wsi.render_surface_width, m_wsi.render_surface_height); + if (!window) + return {}; + + return reinterpret_cast(window); +} diff --git a/Source/Core/Common/GL/GLInterface/EGLWayland.h b/Source/Core/Common/GL/GLInterface/EGLWayland.h new file mode 100644 index 000000000000..83f403a34f06 --- /dev/null +++ b/Source/Core/Common/GL/GLInterface/EGLWayland.h @@ -0,0 +1,19 @@ +// Copyright 2019 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include "Common/GL/GLInterface/EGL.h" + +class GLContextEGLWayland : public GLContextEGL +{ +public: + ~GLContextEGLWayland(); + + void UpdateDimensions(int window_width, int window_height) override; + +protected: + EGLDisplay OpenEGLDisplay() override; + EGLNativeWindowType GetEGLNativeWindow(EGLConfig config) override; +}; From de3e336b4b9ee8c364164113d108abeb0a4d1cd4 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Thu, 2 May 2019 23:43:16 +1000 Subject: [PATCH 4/8] Vulkan: Support for Wayland --- .../Core/VideoBackends/Vulkan/VKSwapChain.cpp | 26 ++++++++++++++++++- .../VideoBackends/Vulkan/VulkanContext.cpp | 7 +++++ .../Vulkan/VulkanEntryPoints.inl | 5 ++++ .../Core/VideoBackends/Vulkan/VulkanLoader.h | 4 +++ 4 files changed, 41 insertions(+), 1 deletion(-) diff --git a/Source/Core/VideoBackends/Vulkan/VKSwapChain.cpp b/Source/Core/VideoBackends/Vulkan/VKSwapChain.cpp index e5a6b8c71a44..bc515a6d34ee 100644 --- a/Source/Core/VideoBackends/Vulkan/VKSwapChain.cpp +++ b/Source/Core/VideoBackends/Vulkan/VKSwapChain.cpp @@ -24,7 +24,8 @@ namespace Vulkan { SwapChain::SwapChain(const WindowSystemInfo& wsi, VkSurfaceKHR surface, bool vsync) : m_wsi(wsi), m_surface(surface), m_vsync_enabled(vsync), - m_fullscreen_supported(g_vulkan_context->SupportsExclusiveFullscreen(wsi, surface)) + m_fullscreen_supported(g_vulkan_context->SupportsExclusiveFullscreen(wsi, surface)), + m_width(wsi.render_surface_width), m_height(wsi.render_surface_height) { } @@ -83,6 +84,29 @@ VkSurfaceKHR SwapChain::CreateVulkanSurface(VkInstance instance, const WindowSys } #endif +#if defined(VK_USE_PLATFORM_WAYLAND_KHR) + if (wsi.type == WindowSystemType::Wayland) + { + VkWaylandSurfaceCreateInfoKHR surface_create_info = { + VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR, // VkStructureType sType + nullptr, // const void* pNext + 0, // VkWaylandSurfaceCreateFlagsKHR flags + static_cast(wsi.display_connection), // struct wl_display* display + static_cast(wsi.render_surface) // struct wl_surface* surface + }; + + VkSurfaceKHR surface; + VkResult res = vkCreateWaylandSurfaceKHR(instance, &surface_create_info, nullptr, &surface); + if (res != VK_SUCCESS) + { + LOG_VULKAN_ERROR(res, "vkCreateWaylandSurfaceKHR failed: "); + return VK_NULL_HANDLE; + } + + return surface; + } +#endif + #if defined(VK_USE_PLATFORM_ANDROID_KHR) if (wsi.type == WindowSystemType::Android) { diff --git a/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp b/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp index 3275cb941735..639069b0cb0f 100644 --- a/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp +++ b/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp @@ -209,6 +209,13 @@ bool VulkanContext::SelectInstanceExtensions(std::vector* extension return false; } #endif +#if defined(VK_USE_PLATFORM_WAYLAND_KHR) + if (wstype == WindowSystemType::Wayland && + !AddExtension(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME, true)) + { + return false; + } +#endif #if defined(VK_USE_PLATFORM_ANDROID_KHR) if (wstype == WindowSystemType::Android && !AddExtension(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME, true)) diff --git a/Source/Core/VideoBackends/Vulkan/VulkanEntryPoints.inl b/Source/Core/VideoBackends/Vulkan/VulkanEntryPoints.inl index 3bb21c41e360..bebbcbf23961 100644 --- a/Source/Core/VideoBackends/Vulkan/VulkanEntryPoints.inl +++ b/Source/Core/VideoBackends/Vulkan/VulkanEntryPoints.inl @@ -49,6 +49,11 @@ VULKAN_INSTANCE_ENTRY_POINT(vkCreateXlibSurfaceKHR, false) VULKAN_INSTANCE_ENTRY_POINT(vkGetPhysicalDeviceXlibPresentationSupportKHR, false) #endif +#if defined(VK_USE_PLATFORM_WAYLAND_KHR) +VULKAN_INSTANCE_ENTRY_POINT(vkCreateWaylandSurfaceKHR, false) +VULKAN_INSTANCE_ENTRY_POINT(vkGetPhysicalDeviceWaylandPresentationSupportKHR, false) +#endif + #if defined(VK_USE_PLATFORM_ANDROID_KHR) VULKAN_INSTANCE_ENTRY_POINT(vkCreateAndroidSurfaceKHR, false) #endif diff --git a/Source/Core/VideoBackends/Vulkan/VulkanLoader.h b/Source/Core/VideoBackends/Vulkan/VulkanLoader.h index c9b92f5ac8e0..a728d25186a0 100644 --- a/Source/Core/VideoBackends/Vulkan/VulkanLoader.h +++ b/Source/Core/VideoBackends/Vulkan/VulkanLoader.h @@ -13,6 +13,10 @@ #define VK_USE_PLATFORM_XLIB_KHR #endif +#if defined(HAVE_WAYLAND) +#define VK_USE_PLATFORM_WAYLAND_KHR +#endif + #if defined(ANDROID) #define VK_USE_PLATFORM_ANDROID_KHR #endif From 54a54852031ac0b2c7ec5a7dc62bc0ad8d74b5e2 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Thu, 2 May 2019 23:43:20 +1000 Subject: [PATCH 5/8] ControllerInterface: Add a Wayland backend --- CMake/FindXKBCommon.cmake | 34 ++ CMakeLists.txt | 1 + Source/Core/InputCommon/CMakeLists.txt | 30 +- .../ControllerInterface.cpp | 13 + .../ControllerInterface/ControllerInterface.h | 3 + .../ControllerInterface/Wayland/Wayland.cpp | 389 ++++++++++++++++++ .../ControllerInterface/Wayland/Wayland.h | 150 +++++++ 7 files changed, 611 insertions(+), 9 deletions(-) create mode 100644 CMake/FindXKBCommon.cmake create mode 100644 Source/Core/InputCommon/ControllerInterface/Wayland/Wayland.cpp create mode 100644 Source/Core/InputCommon/ControllerInterface/Wayland/Wayland.h diff --git a/CMake/FindXKBCommon.cmake b/CMake/FindXKBCommon.cmake new file mode 100644 index 000000000000..0f571eeacb5c --- /dev/null +++ b/CMake/FindXKBCommon.cmake @@ -0,0 +1,34 @@ +# - Try to find XKBCommon +# Once done, this will define +# +# XKBCOMMON_FOUND - System has XKBCommon +# XKBCOMMON_INCLUDE_DIRS - The XKBCommon include directories +# XKBCOMMON_LIBRARIES - The libraries needed to use XKBCommon +# XKBCOMMON_DEFINITIONS - Compiler switches required for using XKBCommon + +find_package(PkgConfig) +pkg_check_modules(PC_XKBCOMMON QUIET xkbcommon) +set(XKBCOMMON_DEFINITIONS ${PC_XKBCOMMON_CFLAGS_OTHER}) + +find_path(XKBCOMMON_INCLUDE_DIR + NAMES xkbcommon/xkbcommon.h + HINTS ${PC_XKBCOMMON_INCLUDE_DIR} ${PC_XKBCOMMON_INCLUDE_DIRS} +) + +find_library(XKBCOMMON_LIBRARY + NAMES xkbcommon + HINTS ${PC_XKBCOMMON_LIBRARY} ${PC_XKBCOMMON_LIBRARY_DIRS} +) + +set(XKBCOMMON_LIBRARIES ${XKBCOMMON_LIBRARY}) +set(XKBCOMMON_LIBRARY_DIRS ${XKBCOMMON_LIBRARY_DIRS}) +set(XKBCOMMON_INCLUDE_DIRS ${XKBCOMMON_INCLUDE_DIR}) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(XKBCommon DEFAULT_MSG + XKBCOMMON_LIBRARY + XKBCOMMON_INCLUDE_DIR +) + +mark_as_advanced(XKBCOMMON_LIBRARY XKBCOMMON_INCLUDE_DIR) + diff --git a/CMakeLists.txt b/CMakeLists.txt index 7972d831b41c..4fc15587908b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -564,6 +564,7 @@ if(ENABLE_WAYLAND) find_package(ECM REQUIRED NO_MODULE) list(APPEND CMAKE_MODULE_PATH "${ECM_MODULE_PATH}") find_package(Wayland REQUIRED Client Egl) + find_package(XKBCommon REQUIRED) add_definitions(-DHAVE_WAYLAND=1) message(STATUS "Wayland support enabled") endif() diff --git a/Source/Core/InputCommon/CMakeLists.txt b/Source/Core/InputCommon/CMakeLists.txt index 20ae0f3f173d..95b35b14ade1 100644 --- a/Source/Core/InputCommon/CMakeLists.txt +++ b/Source/Core/InputCommon/CMakeLists.txt @@ -129,15 +129,6 @@ elseif(APPLE) target_compile_options(inputcommon PRIVATE -fobjc-arc ) -elseif(X11_FOUND) - target_sources(inputcommon PRIVATE - ControllerInterface/Xlib/XInput2.cpp - ControllerInterface/Xlib/XInput2.h - ) - target_link_libraries(inputcommon PUBLIC - ${X11_LIBRARIES} - ${X11_INPUT_LIBRARIES} - ) elseif(ANDROID) target_compile_definitions(inputcommon PRIVATE -DCIFACE_USE_ANDROID) target_sources(inputcommon PRIVATE @@ -150,6 +141,27 @@ elseif(ANDROID) ControllerInterface/Touch/Touchscreen.cpp ControllerInterface/Touch/Touchscreen.h ) +else() + if(ENABLE_X11 AND X11_FOUND) + target_sources(inputcommon PRIVATE + ControllerInterface/Xlib/XInput2.cpp + ControllerInterface/Xlib/XInput2.h + ) + target_link_libraries(inputcommon PUBLIC + ${X11_LIBRARIES} + ${X11_INPUT_LIBRARIES} + ) + endif() + if(ENABLE_WAYLAND AND WAYLAND_FOUND) + target_sources(inputcommon PRIVATE + ControllerInterface/Wayland/Wayland.cpp + ControllerInterface/Wayland/Wayland.h + ) + target_include_directories(inputcommon PRIVATE + ${WAYLAND_INCLUDE_DIR} ${XKBCOMMON_INCLUDE_DIR}) + target_link_libraries(inputcommon PRIVATE + ${WAYLAND_LIBRARIES} ${XKBCOMMON_LIBRARIES}) + endif() endif() if(NOT ANDROID) diff --git a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp index 6a6affd478c4..1b98d39d475e 100644 --- a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp +++ b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp @@ -15,6 +15,9 @@ #ifdef CIFACE_USE_XLIB #include "InputCommon/ControllerInterface/Xlib/XInput2.h" #endif +#ifdef CIFACE_USE_WAYLAND +#include "InputCommon/ControllerInterface/Wayland/Wayland.h" +#endif #ifdef CIFACE_USE_OSX #include "InputCommon/ControllerInterface/OSX/OSX.h" #include "InputCommon/ControllerInterface/Quartz/Quartz.h" @@ -60,6 +63,9 @@ void ControllerInterface::Initialize(const WindowSystemInfo& wsi) #ifdef CIFACE_USE_XLIB // nothing needed #endif +#ifdef CIFACE_USE_WAYLAND +// nothing needed +#endif #ifdef CIFACE_USE_OSX // nothing needed for OSX and Quartz #endif @@ -171,6 +177,10 @@ void ControllerInterface::RefreshDevices(RefreshReason reason) if (m_wsi.type == WindowSystemType::X11) ciface::XInput2::PopulateDevices(m_wsi.render_window); #endif +#ifdef CIFACE_USE_WAYLAND + if (m_wsi.type == WindowSystemType::Wayland) + ciface::Wayland::PopulateDevices(m_wsi); +#endif #ifdef CIFACE_USE_OSX if (m_wsi.type == WindowSystemType::MacOS) { @@ -232,6 +242,9 @@ void ControllerInterface::Shutdown() #ifdef CIFACE_USE_XLIB // nothing needed #endif +#ifdef CIFACE_USE_WAYLAND +// nothing needed +#endif #ifdef CIFACE_USE_OSX ciface::OSX::DeInit(); ciface::Quartz::DeInit(); diff --git a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h index 59909aeec83d..cc3cb54f5075 100644 --- a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h +++ b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h @@ -30,6 +30,9 @@ #if defined(USE_PIPES) #define CIFACE_USE_PIPES #endif +#if defined(HAVE_WAYLAND) +#define CIFACE_USE_WAYLAND +#endif #define CIFACE_USE_DUALSHOCKUDPCLIENT #if defined(HAVE_SDL2) #define CIFACE_USE_SDL diff --git a/Source/Core/InputCommon/ControllerInterface/Wayland/Wayland.cpp b/Source/Core/InputCommon/ControllerInterface/Wayland/Wayland.cpp new file mode 100644 index 000000000000..599ffcfe3d25 --- /dev/null +++ b/Source/Core/InputCommon/ControllerInterface/Wayland/Wayland.cpp @@ -0,0 +1,389 @@ +// Copyright 2018 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include +#include +#include +#include +#include +#include + +#include "InputCommon/ControllerInterface/Wayland/Wayland.h" + +#include "Common/Assert.h" +#include "Common/StringUtil.h" + +// Mouse axis control output is simply divided by this number. In practice, +// that just means you can use a smaller "dead zone" if you bind axis controls +// to a joystick. No real need to make this customizable. +#define MOUSE_AXIS_SENSITIVITY 8.0f + +// The mouse axis controls use a weighted running average. Each frame, the new +// value is the average of the old value and the amount of relative mouse +// motion during that frame. The old value is weighted by a ratio of +// MOUSE_AXIS_SMOOTHING:1 compared to the new value. Increasing +// MOUSE_AXIS_SMOOTHING makes the controls smoother, decreasing it makes them +// more responsive. This might be useful as a user-customizable option. +#define MOUSE_AXIS_SMOOTHING 1.5f + +namespace ciface +{ +namespace Wayland +{ +void PopulateDevices(const WindowSystemInfo& wsi) +{ + std::shared_ptr dev = std::make_shared(); + if (!dev->Initialize(wsi)) + return; + + g_controller_interface.AddDevice(dev); +} + +KeyboardMouse::KeyboardMouse() = default; + +KeyboardMouse::~KeyboardMouse() +{ + if (m_xkb_state) + xkb_state_unref(m_xkb_state); + if (m_xkb_keymap) + xkb_keymap_unref(m_xkb_keymap); + if (m_wl_keyboard) + wl_keyboard_destroy(m_wl_keyboard); + if (m_wl_pointer) + wl_pointer_destroy(m_wl_pointer); + if (m_wl_seat) + wl_seat_destroy(m_wl_seat); + if (m_wl_registry) + wl_registry_destroy(m_wl_registry); + if (m_xkb_context) + xkb_context_unref(m_xkb_context); + if (m_display_proxy) + wl_proxy_wrapper_destroy(m_display_proxy); + if (m_event_queue) + wl_event_queue_destroy(m_event_queue); +} + +bool KeyboardMouse::Initialize(const WindowSystemInfo& wsi) +{ + m_display = static_cast(wsi.display_connection); + m_window_width = wsi.render_surface_width; + m_window_height = wsi.render_surface_height; + m_event_queue = wl_display_create_queue(m_display); + if (!m_event_queue) + return false; + + // As UpdateInput() can be called from the CPU, we don't want to clash with the main (UI) thread. + // Therefore, we create a second event queue for retrieving keyboard/mouse events. + m_display_proxy = static_cast(wl_proxy_create_wrapper(m_display)); + if (!m_display_proxy) + return false; + + wl_proxy_set_queue(reinterpret_cast(m_display_proxy), m_event_queue); + m_xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + if (!m_xkb_context) + return false; + + static const wl_registry_listener registry_listener = {GlobalRegistryHandler, + GlobalRegistryRemover}; + m_wl_registry = wl_display_get_registry(m_display_proxy); + wl_proxy_set_queue(reinterpret_cast(m_display_proxy), m_event_queue); + wl_registry_add_listener(m_wl_registry, ®istry_listener, this); + + // Call back to registry listener to get compositor/shell. + wl_display_dispatch_queue(m_display, m_event_queue); + wl_display_roundtrip_queue(m_display, m_event_queue); + // We need to do this twice so the seat listener is called. + wl_display_dispatch_queue(m_display, m_event_queue); + + // If we have a keyboard, keep dispatching until we get the keymap. + if (m_wl_keyboard) + { + while (!m_xkb_keymap) + { + if (wl_display_dispatch_queue(m_display, m_event_queue) == -1) + break; + } + } + + // Keyboard Keys + if (m_xkb_keymap) + AddKeyInputs(); + + // Mouse Buttons + if (m_wl_pointer) + { + for (int i = 0; i < 32; i++) + AddInput(new Button(i, &m_state.buttons)); + + // Mouse Cursor, X-/+ and Y-/+ + for (int i = 0; i != 4; ++i) + AddInput(new Cursor(!!(i & 2), !!(i & 1), (i & 2) ? &m_state.cursor.y : &m_state.cursor.x)); + + // Mouse Axis, X-/+ and Y-/+ + for (int i = 0; i != 4; ++i) + AddInput(new Axis(!!(i & 2), !!(i & 1), (i & 2) ? &m_state.axis.y : &m_state.axis.x)); + } + + return true; +} + +void KeyboardMouse::UpdateInput() +{ + while (wl_display_prepare_read_queue(m_display, m_event_queue) != 0) + wl_display_dispatch_queue_pending(m_display, m_event_queue); + + wl_display_read_events(m_display); + wl_display_dispatch_queue_pending(m_display, m_event_queue); +} + +void KeyboardMouse::PointerEnter(void* data, wl_pointer* pointer, uint32_t serial, + wl_surface* surface, wl_fixed_t surface_x, wl_fixed_t surface_y) +{ +} + +void KeyboardMouse::PointerLeave(void* data, wl_pointer* pointer, uint32_t serial, + wl_surface* surface) +{ +} + +void KeyboardMouse::PointerMotion(void* data, wl_pointer* pointer, uint32_t time, wl_fixed_t x, + wl_fixed_t y) +{ + KeyboardMouse* kbm = static_cast(data); + const float pos_x = static_cast(wl_fixed_to_double(x)); + const float pos_y = static_cast(wl_fixed_to_double(y)); + const float delta_x = pos_x - kbm->m_state.cursor.x; + const float delta_y = pos_y - kbm->m_state.cursor.y; + + // apply axis smoothing + kbm->m_state.axis.x *= MOUSE_AXIS_SMOOTHING; + kbm->m_state.axis.x += delta_x; + kbm->m_state.axis.x /= MOUSE_AXIS_SMOOTHING + 1.0f; + kbm->m_state.axis.y *= MOUSE_AXIS_SMOOTHING; + kbm->m_state.axis.y += delta_y; + kbm->m_state.axis.y /= MOUSE_AXIS_SMOOTHING + 1.0f; + + kbm->m_state.cursor.x = (pos_x / static_cast(kbm->m_window_width) * 2.0f) - 1.0f; + kbm->m_state.cursor.y = (pos_y / static_cast(kbm->m_window_height) * 2.0f) - 1.0f; +} + +void KeyboardMouse::PointerButton(void* data, wl_pointer* pointer, uint32_t serial, uint32_t time, + uint32_t button, uint32_t state) +{ + KeyboardMouse* kbm = static_cast(data); + if (button < BTN_MOUSE || (button - BTN_MOUSE) >= 32) + return; + + const u32 mask = 1u << (button - BTN_MOUSE); + if (state == WL_POINTER_BUTTON_STATE_PRESSED) + kbm->m_state.buttons |= mask; + else if (state == WL_POINTER_BUTTON_STATE_RELEASED) + kbm->m_state.buttons &= ~mask; +} + +void KeyboardMouse::PointerAxis(void* data, wl_pointer* pointer, uint32_t time, uint32_t axis, + wl_fixed_t value) +{ + // TODO: Wheel support. + // KeyboardMouse* kbm = static_cast(data); + // printf("pointer axis %u %f\n", axis, wl_fixed_to_double(value)); +} + +void KeyboardMouse::KeyboardKeymap(void* data, wl_keyboard* keyboard, uint32_t format, int32_t fd, + uint32_t size) +{ + KeyboardMouse* kbm = static_cast(data); + char* keymap_string = static_cast(mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0)); + if (kbm->m_xkb_keymap) + xkb_keymap_unref(kbm->m_xkb_keymap); + kbm->m_xkb_keymap = xkb_keymap_new_from_string( + kbm->m_xkb_context, keymap_string, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS); + munmap(keymap_string, size); + close(fd); + if (kbm->m_xkb_state) + xkb_state_unref(kbm->m_xkb_state); + kbm->m_xkb_state = xkb_state_new(kbm->m_xkb_keymap); +} + +void KeyboardMouse::KeyboardEnter(void* data, wl_keyboard* keyboard, uint32_t serial, + wl_surface* surface, wl_array* keys) +{ +} + +void KeyboardMouse::KeyboardLeave(void* data, wl_keyboard* keyboard, uint32_t serial, + wl_surface* surface) +{ +} + +void KeyboardMouse::KeyboardKey(void* data, wl_keyboard* keyboard, uint32_t serial, uint32_t time, + uint32_t key, uint32_t state) +{ + KeyboardMouse* kbm = static_cast(data); + + const xkb_keycode_t keycode = static_cast(key + 8); + if (keycode < kbm->m_min_keycode && keycode > kbm->m_max_keycode) + return; + + const u32 bit_index = static_cast(keycode - kbm->m_min_keycode); + const u32 array_index = bit_index / 32; + const u32 mask = 1u << (bit_index % 32); + if (state == WL_KEYBOARD_KEY_STATE_PRESSED) + kbm->m_state.keyboard[array_index] |= mask; + else if (state == WL_KEYBOARD_KEY_STATE_RELEASED) + kbm->m_state.keyboard[array_index] &= ~mask; +} + +void KeyboardMouse::KeyboardModifiers(void* data, wl_keyboard* keyboard, uint32_t serial, + uint32_t mods_depressed, uint32_t mods_latched, + uint32_t mods_locked, uint32_t group) +{ + KeyboardMouse* kbm = static_cast(data); + xkb_state_update_mask(kbm->m_xkb_state, mods_depressed, mods_latched, mods_locked, 0, 0, group); +} + +void KeyboardMouse::AddKeyInputs() +{ + m_min_keycode = xkb_keymap_min_keycode(m_xkb_keymap); + m_max_keycode = xkb_keymap_max_keycode(m_xkb_keymap); + ASSERT(m_max_keycode >= m_min_keycode); + + // Allocate bitmask for key state. + const u32 num_keycodes = static_cast(m_max_keycode - m_min_keycode) + 1; + m_state.keyboard.resize((num_keycodes + 31) / 32); + + for (xkb_keycode_t keycode = m_min_keycode; keycode <= m_max_keycode; keycode++) + { + const xkb_layout_index_t num_layouts = xkb_keymap_num_layouts_for_key(m_xkb_keymap, keycode); + if (num_layouts == 0) + continue; + + // Take the first layout which we find a valid keysym for. + Key* key = nullptr; + for (xkb_layout_index_t layout = 0; layout < num_layouts && !key; layout++) + { + const xkb_level_index_t num_levels = + xkb_keymap_num_levels_for_key(m_xkb_keymap, keycode, layout); + if (num_levels == 0) + continue; + + // Take the first level which we find a valid keysym for. + for (xkb_level_index_t level = 0; level < num_levels; level++) + { + const xkb_keysym_t* keysyms; + int num_syms = + xkb_keymap_key_get_syms_by_level(m_xkb_keymap, keycode, layout, level, &keysyms); + if (num_syms == 0) + continue; + + // Just take the first. Should only be one in most cases anyway. + const xkb_keysym_t keysym = xkb_keysym_to_upper(keysyms[0]); + + char keysym_name_buf[64]; + if (xkb_keysym_get_name(keysym, keysym_name_buf, sizeof(keysym_name_buf)) <= 0) + continue; + + const u32 bit_index = static_cast(keycode - m_min_keycode); + const u32 array_index = bit_index / 32; + const u32 mask = 1u << (bit_index % 32); + key = new Key(keysym_name_buf, keycode, m_state, array_index, mask); + AddInput(key); + break; + } + } + } +} + +void KeyboardMouse::SeatCapabilities(void* data, wl_seat* seat, uint32_t capabilities) +{ + KeyboardMouse* kbm = static_cast(data); + if (capabilities & WL_SEAT_CAPABILITY_KEYBOARD) + { + static const wl_keyboard_listener keyboard_listener = { + &KeyboardMouse::KeyboardKeymap, &KeyboardMouse::KeyboardEnter, + &KeyboardMouse::KeyboardLeave, &KeyboardMouse::KeyboardKey, + &KeyboardMouse::KeyboardModifiers}; + kbm->m_wl_keyboard = wl_seat_get_keyboard(seat); + wl_keyboard_add_listener(kbm->m_wl_keyboard, &keyboard_listener, kbm); + } + if (capabilities & WL_SEAT_CAPABILITY_POINTER) + { + static const wl_pointer_listener pointer_listener = { + &KeyboardMouse::PointerEnter, &KeyboardMouse::PointerLeave, &KeyboardMouse::PointerMotion, + &KeyboardMouse::PointerButton, &KeyboardMouse::PointerAxis}; + kbm->m_wl_pointer = wl_seat_get_pointer(seat); + wl_pointer_add_listener(kbm->m_wl_pointer, &pointer_listener, kbm); + } +} + +void KeyboardMouse::GlobalRegistryHandler(void* data, wl_registry* registry, uint32_t id, + const char* interface, uint32_t version) +{ + KeyboardMouse* kbm = static_cast(data); + if (std::strcmp(interface, "wl_seat") == 0) + { + static const wl_seat_listener seat_listener = {&KeyboardMouse::SeatCapabilities}; + kbm->m_wl_seat = static_cast(wl_registry_bind(registry, id, &wl_seat_interface, 1)); + wl_seat_add_listener(kbm->m_wl_seat, &seat_listener, kbm); + } +} + +void KeyboardMouse::GlobalRegistryRemover(void* data, wl_registry* registry, uint32_t id) +{ +} + +std::string KeyboardMouse::GetName() const +{ + return "Keyboard and Mouse"; +} + +std::string KeyboardMouse::GetSource() const +{ + return "Wayland"; +} + +KeyboardMouse::Key::Key(const std::string& name, const xkb_keycode_t keycode, const State& state, + const u32 index, const u32 mask) + : m_keyname(name), m_keycode(keycode), m_state(state), m_index(index), m_mask(mask) +{ +} + +ControlState KeyboardMouse::Key::GetState() const +{ + return (m_state.keyboard[m_index] & m_mask) != 0; +} + +KeyboardMouse::Button::Button(unsigned int index, unsigned int* buttons) + : m_buttons(buttons), m_index(index) +{ + m_name = StringFromFormat("Click %d", m_index + 1); +} + +ControlState KeyboardMouse::Button::GetState() const +{ + return ((*m_buttons & (1 << m_index)) != 0); +} + +KeyboardMouse::Cursor::Cursor(u8 index, bool positive, const float* cursor) + : m_cursor(cursor), m_index(index), m_positive(positive) +{ + m_name = std::string("Cursor ") + (char)('X' + m_index) + (m_positive ? '+' : '-'); +} + +ControlState KeyboardMouse::Cursor::GetState() const +{ + return std::max(0.0f, *m_cursor / (m_positive ? 1.0f : -1.0f)); +} + +KeyboardMouse::Axis::Axis(u8 index, bool positive, const float* axis) + : m_axis(axis), m_index(index), m_positive(positive) +{ + m_name = std::string("Axis ") + (char)('X' + m_index) + (m_positive ? '+' : '-'); +} + +ControlState KeyboardMouse::Axis::GetState() const +{ + return std::max(0.0f, *m_axis / (m_positive ? MOUSE_AXIS_SENSITIVITY : -MOUSE_AXIS_SENSITIVITY)); +} +} // namespace Wayland +} // namespace ciface diff --git a/Source/Core/InputCommon/ControllerInterface/Wayland/Wayland.h b/Source/Core/InputCommon/ControllerInterface/Wayland/Wayland.h new file mode 100644 index 000000000000..706eeb17b06c --- /dev/null +++ b/Source/Core/InputCommon/ControllerInterface/Wayland/Wayland.h @@ -0,0 +1,150 @@ +// Copyright 2018 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include + +#include "Common/CommonTypes.h" +#include "Common/WindowSystemInfo.h" +#include "InputCommon/ControllerInterface/ControllerInterface.h" + +namespace ciface +{ +namespace Wayland +{ +void PopulateDevices(const WindowSystemInfo& wsi); + +class KeyboardMouse : public Core::Device +{ +private: + struct State + { + std::vector keyboard; + unsigned int buttons; + struct + { + float x, y; + } cursor, axis; + }; + + class Key : public Input + { + friend class KeyboardMouse; + + public: + std::string GetName() const override { return m_keyname; } + Key(const std::string& name, const xkb_keycode_t keycode, const State& state, const u32 index, + const u32 mask); + ControlState GetState() const override; + + private: + const std::string m_keyname; + const xkb_keycode_t m_keycode; + const State& m_state; + const u32 m_index; + const u32 m_mask; + }; + + class Button : public Input + { + public: + std::string GetName() const override { return m_name; } + Button(unsigned int index, unsigned int* buttons); + ControlState GetState() const override; + + private: + const unsigned int* m_buttons; + const unsigned int m_index; + std::string m_name; + }; + + class Cursor : public Input + { + public: + std::string GetName() const override { return m_name; } + bool IsDetectable() const override { return false; } + Cursor(u8 index, bool positive, const float* cursor); + ControlState GetState() const override; + + private: + const float* m_cursor; + const u8 m_index; + const bool m_positive; + std::string m_name; + }; + + class Axis : public Input + { + public: + std::string GetName() const override { return m_name; } + bool IsDetectable() const override { return false; } + Axis(u8 index, bool positive, const float* axis); + ControlState GetState() const override; + + private: + const float* m_axis; + const u8 m_index; + const bool m_positive; + std::string m_name; + }; + +public: + KeyboardMouse(); + ~KeyboardMouse(); + + bool Initialize(const WindowSystemInfo& wsi); + + std::string GetName() const override; + std::string GetSource() const override; + void UpdateInput() override; + +private: + void AddKeyInputs(); + + static void PointerEnter(void* data, wl_pointer* pointer, uint32_t serial, wl_surface* surface, + wl_fixed_t surface_x, wl_fixed_t surface_y); + static void PointerLeave(void* data, wl_pointer* pointer, uint32_t serial, wl_surface* surface); + static void PointerMotion(void* data, wl_pointer* pointer, uint32_t time, wl_fixed_t x, + wl_fixed_t y); + static void PointerButton(void* data, wl_pointer* pointer, uint32_t serial, uint32_t time, + uint32_t button, uint32_t state); + static void PointerAxis(void* data, wl_pointer* pointer, uint32_t time, uint32_t axis, + wl_fixed_t value); + static void KeyboardKeymap(void* data, wl_keyboard* keyboard, uint32_t format, int32_t fd, + uint32_t size); + static void KeyboardEnter(void* data, wl_keyboard* keyboard, uint32_t serial, wl_surface* surface, + wl_array* keys); + static void KeyboardLeave(void* data, wl_keyboard* keyboard, uint32_t serial, + wl_surface* surface); + static void KeyboardKey(void* data, wl_keyboard* keyboard, uint32_t serial, uint32_t time, + uint32_t key, uint32_t state); + static void KeyboardModifiers(void* data, wl_keyboard* keyboard, uint32_t serial, + uint32_t mods_depressed, uint32_t mods_latched, + uint32_t mods_locked, uint32_t group); + static void SeatCapabilities(void* data, wl_seat* seat, uint32_t capabilities); + static void GlobalRegistryHandler(void* data, wl_registry* registry, uint32_t id, + const char* interface, uint32_t version); + static void GlobalRegistryRemover(void* data, wl_registry* registry, uint32_t id); + + wl_display* m_display = nullptr; + wl_display* m_display_proxy = nullptr; + wl_event_queue* m_event_queue = nullptr; + wl_registry* m_wl_registry = nullptr; + wl_seat* m_wl_seat = nullptr; + wl_keyboard* m_wl_keyboard = nullptr; + wl_pointer* m_wl_pointer = nullptr; + xkb_context* m_xkb_context = nullptr; + xkb_keymap* m_xkb_keymap = nullptr; + xkb_state* m_xkb_state = nullptr; + State m_state = {}; + xkb_keycode_t m_min_keycode = {}; + xkb_keycode_t m_max_keycode = {}; + int m_window_width = 1; + int m_window_height = 1; +}; +} // namespace Wayland +} // namespace ciface From b4386326f2c08f3327cdb9a1db902f3e3ce42a08 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Thu, 2 May 2019 23:43:24 +1000 Subject: [PATCH 6/8] DolphinNoGUI: Add a basic Wayland platform --- CMake/FindWaylandProtocols.cmake | 29 +++ CMakeLists.txt | 2 + Source/Core/DolphinNoGUI/CMakeLists.txt | 16 ++ Source/Core/DolphinNoGUI/MainNoGUI.cpp | 9 + Source/Core/DolphinNoGUI/Platform.h | 3 + Source/Core/DolphinNoGUI/PlatformWayland.cpp | 248 +++++++++++++++++++ 6 files changed, 307 insertions(+) create mode 100644 CMake/FindWaylandProtocols.cmake create mode 100644 Source/Core/DolphinNoGUI/PlatformWayland.cpp diff --git a/CMake/FindWaylandProtocols.cmake b/CMake/FindWaylandProtocols.cmake new file mode 100644 index 000000000000..bfe7c9a21e18 --- /dev/null +++ b/CMake/FindWaylandProtocols.cmake @@ -0,0 +1,29 @@ +# from https://github.com/glfw/glfw/blob/master/CMake/modules/FindWaylandProtocols.cmake + +find_package(PkgConfig) + +pkg_check_modules(WaylandProtocols QUIET wayland-protocols>=${WaylandProtocols_FIND_VERSION}) + +execute_process(COMMAND ${PKG_CONFIG_EXECUTABLE} --variable=pkgdatadir wayland-protocols + OUTPUT_VARIABLE WaylandProtocols_PKGDATADIR + RESULT_VARIABLE _pkgconfig_failed) +if (_pkgconfig_failed) + message(FATAL_ERROR "Missing wayland-protocols pkgdatadir") +endif() + +string(REGEX REPLACE "[\r\n]" "" WaylandProtocols_PKGDATADIR "${WaylandProtocols_PKGDATADIR}") + +find_package_handle_standard_args(WaylandProtocols + FOUND_VAR + WaylandProtocols_FOUND + REQUIRED_VARS + WaylandProtocols_PKGDATADIR + VERSION_VAR + WaylandProtocols_VERSION + HANDLE_COMPONENTS +) + +set(WAYLAND_PROTOCOLS_FOUND ${WaylandProtocols_FOUND}) +set(WAYLAND_PROTOCOLS_PKGDATADIR ${WaylandProtocols_PKGDATADIR}) +set(WAYLAND_PROTOCOLS_VERSION ${WaylandProtocols_VERSION}) + diff --git a/CMakeLists.txt b/CMakeLists.txt index 4fc15587908b..568cad81d118 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -564,6 +564,8 @@ if(ENABLE_WAYLAND) find_package(ECM REQUIRED NO_MODULE) list(APPEND CMAKE_MODULE_PATH "${ECM_MODULE_PATH}") find_package(Wayland REQUIRED Client Egl) + find_package(WaylandScanner REQUIRED) + find_package(WaylandProtocols 1.15 REQUIRED) find_package(XKBCommon REQUIRED) add_definitions(-DHAVE_WAYLAND=1) message(STATUS "Wayland support enabled") diff --git a/Source/Core/DolphinNoGUI/CMakeLists.txt b/Source/Core/DolphinNoGUI/CMakeLists.txt index f21955d80996..c4de5e64bd10 100644 --- a/Source/Core/DolphinNoGUI/CMakeLists.txt +++ b/Source/Core/DolphinNoGUI/CMakeLists.txt @@ -17,6 +17,22 @@ if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") target_sources(dolphin-nogui PRIVATE PlatformFBDev.cpp) endif() +if(ENABLE_WAYLAND AND WAYLAND_FOUND) + set(WAYLAND_PLATFORM_SRCS PlatformWayland.cpp) + + # Generate the xdg-shell and xdg-decoration protocols at build-time. + ecm_add_wayland_client_protocol(WAYLAND_PLATFORM_SRCS + PROTOCOL "${WAYLAND_PROTOCOLS_PKGDATADIR}/stable/xdg-shell/xdg-shell.xml" + BASENAME xdg-shell) + ecm_add_wayland_client_protocol(WAYLAND_PLATFORM_SRCS + PROTOCOL "${WAYLAND_PROTOCOLS_PKGDATADIR}/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml" + BASENAME xdg-decoration) + + target_include_directories(dolphin-nogui PRIVATE "${CMAKE_CURRENT_BINARY_DIR}") + target_sources(dolphin-nogui PRIVATE "${WAYLAND_PLATFORM_SRCS}") + target_link_libraries(dolphin-nogui PRIVATE Wayland::Client) +endif() + set_target_properties(dolphin-nogui PROPERTIES OUTPUT_NAME dolphin-emu-nogui) target_link_libraries(dolphin-nogui diff --git a/Source/Core/DolphinNoGUI/MainNoGUI.cpp b/Source/Core/DolphinNoGUI/MainNoGUI.cpp index 11bbf55da101..023b2917a3f4 100644 --- a/Source/Core/DolphinNoGUI/MainNoGUI.cpp +++ b/Source/Core/DolphinNoGUI/MainNoGUI.cpp @@ -155,6 +155,11 @@ static std::unique_ptr GetPlatform(const optparse::Values& options) { std::string platform_name = static_cast(options.get("platform")); +#if HAVE_WAYLAND + if (platform_name == "wayland") + return Platform::CreateWaylandPlatform(); +#endif + #if HAVE_X11 if (platform_name == "x11" || platform_name.empty()) return Platform::CreateX11Platform(); @@ -199,6 +204,10 @@ int main(int argc, char* argv[]) #ifdef _WIN32 , "win32" +#endif +#ifdef HAVE_WAYLAND + , + "wayland" #endif }); diff --git a/Source/Core/DolphinNoGUI/Platform.h b/Source/Core/DolphinNoGUI/Platform.h index 24ec06e307fc..8a5a4c30f26f 100644 --- a/Source/Core/DolphinNoGUI/Platform.h +++ b/Source/Core/DolphinNoGUI/Platform.h @@ -34,6 +34,9 @@ class Platform #ifdef HAVE_X11 static std::unique_ptr CreateX11Platform(); #endif +#ifdef HAVE_WAYLAND + static std::unique_ptr CreateWaylandPlatform(); +#endif #ifdef __linux__ static std::unique_ptr CreateFBDevPlatform(); diff --git a/Source/Core/DolphinNoGUI/PlatformWayland.cpp b/Source/Core/DolphinNoGUI/PlatformWayland.cpp new file mode 100644 index 000000000000..5f1a18a8cf60 --- /dev/null +++ b/Source/Core/DolphinNoGUI/PlatformWayland.cpp @@ -0,0 +1,248 @@ +// Copyright 2018 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include + +#include "DolphinNoGUI/Platform.h" + +#include "Common/MsgHandler.h" +#include "Core/Config/MainSettings.h" +#include "Core/Core.h" +#include "Core/State.h" + +#include +#include +#include + +#include +#include "wayland-xdg-decoration-client-protocol.h" +#include "wayland-xdg-shell-client-protocol.h" + +#include "UICommon/X11Utils.h" +#include "VideoCommon/RenderBase.h" + +namespace +{ +class PlatformWayland : public Platform +{ +public: + ~PlatformWayland() override; + + bool Init() override; + void SetTitle(const std::string& string) override; + void MainLoop() override; + + WindowSystemInfo GetWindowSystemInfo() const; + +private: + void ProcessEvents(); + + static void GlobalRegistryHandler(void* data, wl_registry* registry, uint32_t id, + const char* interface, uint32_t version); + static void GlobalRegistryRemover(void* data, wl_registry* registry, uint32_t id); + static void XDGWMBasePing(void* data, struct xdg_wm_base* xdg_wm_base, uint32_t serial); + static void XDGSurfaceConfigure(void* data, struct xdg_surface* xdg_surface, uint32_t serial); + static void TopLevelConfigure(void* data, struct xdg_toplevel* xdg_toplevel, int32_t width, + int32_t height, struct wl_array* states); + static void TopLevelClose(void* data, struct xdg_toplevel* xdg_toplevel); + + wl_display* m_display = nullptr; + wl_registry* m_registry = nullptr; + wl_compositor* m_compositor = nullptr; + xdg_wm_base* m_xdg_wm_base = nullptr; + wl_surface* m_surface = nullptr; + wl_region* m_region = nullptr; + xdg_surface* m_xdg_surface = nullptr; + xdg_toplevel* m_xdg_toplevel = nullptr; + zxdg_decoration_manager_v1* m_decoration_manager = nullptr; + zxdg_toplevel_decoration_v1* m_toplevel_decoration = nullptr; + + int m_surface_width = 0; + int m_surface_height = 0; +}; + +PlatformWayland::~PlatformWayland() +{ + if (m_xdg_toplevel) + xdg_toplevel_destroy(m_xdg_toplevel); + if (m_xdg_surface) + xdg_surface_destroy(m_xdg_surface); + if (m_surface) + wl_surface_destroy(m_surface); + if (m_region) + wl_region_destroy(m_region); + if (m_xdg_wm_base) + xdg_wm_base_destroy(m_xdg_wm_base); + if (m_compositor) + wl_compositor_destroy(m_compositor); + if (m_registry) + wl_registry_destroy(m_registry); + if (m_display) + wl_display_disconnect(m_display); +} + +void PlatformWayland::GlobalRegistryHandler(void* data, wl_registry* registry, uint32_t id, + const char* interface, uint32_t version) +{ + PlatformWayland* platform = static_cast(data); + if (std::strcmp(interface, wl_compositor_interface.name) == 0) + { + platform->m_compositor = static_cast( + wl_registry_bind(platform->m_registry, id, &wl_compositor_interface, 1)); + } + else if (std::strcmp(interface, xdg_wm_base_interface.name) == 0) + { + platform->m_xdg_wm_base = static_cast( + wl_registry_bind(platform->m_registry, id, &xdg_wm_base_interface, 1)); + } + else if (std::strcmp(interface, zxdg_decoration_manager_v1_interface.name) == 0) + { + platform->m_decoration_manager = static_cast( + wl_registry_bind(platform->m_registry, id, &zxdg_decoration_manager_v1_interface, 1)); + } +} + +void PlatformWayland::GlobalRegistryRemover(void* data, wl_registry* registry, uint32_t id) +{ +} + +void PlatformWayland::XDGWMBasePing(void* data, struct xdg_wm_base* xdg_wm_base, uint32_t serial) +{ + xdg_wm_base_pong(xdg_wm_base, serial); +} + +void PlatformWayland::XDGSurfaceConfigure(void* data, struct xdg_surface* xdg_surface, + uint32_t serial) +{ + xdg_surface_ack_configure(xdg_surface, serial); +} + +void PlatformWayland::TopLevelConfigure(void* data, struct xdg_toplevel* xdg_toplevel, + int32_t width, int32_t height, struct wl_array* states) +{ + // If this is zero, it's asking us to set the size. + if (width == 0 || height == 0) + return; + + PlatformWayland* platform = static_cast(data); + platform->m_surface_width = width; + platform->m_surface_height = height; + if (g_renderer) + g_renderer->ResizeSurface(width, height); +} + +void PlatformWayland::TopLevelClose(void* data, struct xdg_toplevel* xdg_toplevel) +{ + PlatformWayland* platform = static_cast(data); + platform->Stop(); +} + +bool PlatformWayland::Init() +{ + m_display = wl_display_connect(nullptr); + if (!m_display) + { + PanicAlertFmt("Failed to connect to Wayland display."); + return false; + } + + static const wl_registry_listener registry_listener = {GlobalRegistryHandler, + GlobalRegistryRemover}; + m_registry = wl_display_get_registry(m_display); + wl_registry_add_listener(m_registry, ®istry_listener, this); + + // Call back to registry listener to get compositor/shell. + wl_display_dispatch(m_display); + wl_display_roundtrip(m_display); + + // We need a shell/compositor, or at least one we understand. + if (!m_compositor || !m_display || !m_xdg_wm_base) + { + std::fprintf(stderr, "Missing Wayland shell/compositor\n"); + return false; + } + + // Create the compositor and shell surface. + if (!(m_surface = wl_compositor_create_surface(m_compositor)) || + !(m_xdg_surface = xdg_wm_base_get_xdg_surface(m_xdg_wm_base, m_surface)) || + !(m_xdg_toplevel = xdg_surface_get_toplevel(m_xdg_surface))) + { + std::fprintf(stderr, "Failed to create compositor/shell surfaces\n"); + return false; + } + + static const xdg_wm_base_listener xdg_wm_base_listener = {XDGWMBasePing}; + xdg_wm_base_add_listener(m_xdg_wm_base, &xdg_wm_base_listener, this); + + static const xdg_surface_listener shell_surface_listener = {XDGSurfaceConfigure}; + xdg_surface_add_listener(m_xdg_surface, &shell_surface_listener, this); + + static const xdg_toplevel_listener toplevel_listener = {TopLevelConfigure, TopLevelClose}; + xdg_toplevel_add_listener(m_xdg_toplevel, &toplevel_listener, this); + + // Create region in the surface to draw into. + m_surface_width = Config::Get(Config::MAIN_RENDER_WINDOW_WIDTH); + m_surface_height = Config::Get(Config::MAIN_RENDER_WINDOW_HEIGHT); + m_region = wl_compositor_create_region(m_compositor); + wl_region_add(m_region, 0, 0, m_surface_width, m_surface_height); + wl_surface_set_opaque_region(m_surface, m_region); + wl_surface_commit(m_surface); + + // This doesn't seem to have any effect on kwin... + xdg_surface_set_window_geometry(m_xdg_surface, Config::Get(Config::MAIN_RENDER_WINDOW_XPOS), + Config::Get(Config::MAIN_RENDER_WINDOW_YPOS), + Config::Get(Config::MAIN_RENDER_WINDOW_WIDTH), + Config::Get(Config::MAIN_RENDER_WINDOW_HEIGHT)); + + if (m_decoration_manager) + { + m_toplevel_decoration = + zxdg_decoration_manager_v1_get_toplevel_decoration(m_decoration_manager, m_xdg_toplevel); + if (m_toplevel_decoration) + zxdg_toplevel_decoration_v1_set_mode(m_toplevel_decoration, + ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); + } + + return true; +} + +void PlatformWayland::SetTitle(const std::string& string) +{ + xdg_toplevel_set_title(m_xdg_toplevel, string.c_str()); +} + +void PlatformWayland::MainLoop() +{ + while (IsRunning()) + { + UpdateRunningFlag(); + Core::HostDispatchJobs(); + ProcessEvents(); + + // TODO: Is this sleep appropriate? + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } +} + +WindowSystemInfo PlatformWayland::GetWindowSystemInfo() const +{ + WindowSystemInfo wsi; + wsi.type = WindowSystemType::Wayland; + wsi.display_connection = static_cast(m_display); + wsi.render_surface = reinterpret_cast(m_surface); + wsi.render_surface_width = m_surface_width; + wsi.render_surface_height = m_surface_height; + return wsi; +} + +void PlatformWayland::ProcessEvents() +{ + wl_display_dispatch_pending(m_display); +} +} // namespace + +std::unique_ptr Platform::CreateWaylandPlatform() +{ + return std::make_unique(); +} From 255d63d677ab7f4acf356d7ef1809cd679b3a62a Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sat, 4 May 2019 15:17:02 +1000 Subject: [PATCH 7/8] ControllerInterface: Get window size from frontend --- Source/Core/Core/Core.cpp | 3 ++- Source/Core/DolphinNoGUI/PlatformWayland.cpp | 5 +++++ Source/Core/DolphinNoGUI/PlatformX11.cpp | 12 ++++++++---- Source/Core/DolphinQt/Host.cpp | 7 ++++++- Source/Core/DolphinQt/MainWindow.cpp | 6 +++--- .../ControllerInterface.cpp | 18 ++++++++++++++++-- .../ControllerInterface/ControllerInterface.h | 4 +++- .../ControllerInterface/CoreDevice.h | 3 +++ .../ControllerInterface/Wayland/Wayland.cpp | 6 ++++++ .../ControllerInterface/Wayland/Wayland.h | 1 + .../ControllerInterface/Xlib/XInput2.cpp | 17 +++++++++++++---- .../ControllerInterface/Xlib/XInput2.h | 9 +++++++-- 12 files changed, 73 insertions(+), 18 deletions(-) diff --git a/Source/Core/Core/Core.cpp b/Source/Core/Core/Core.cpp index 8a02534c5719..b1291647676d 100644 --- a/Source/Core/Core/Core.cpp +++ b/Source/Core/Core/Core.cpp @@ -473,7 +473,8 @@ static void EmuThread(std::unique_ptr boot, WindowSystemInfo wsi // Switch the window used for inputs to the render window. This way, the cursor position // is relative to the render window, instead of the main window. ASSERT(g_controller_interface.IsInit()); - g_controller_interface.ChangeWindow(wsi.render_window); + g_controller_interface.ChangeWindow(wsi.render_surface, wsi.render_surface_width, + wsi.render_surface_height); Pad::LoadConfig(); Pad::LoadGBAConfig(); diff --git a/Source/Core/DolphinNoGUI/PlatformWayland.cpp b/Source/Core/DolphinNoGUI/PlatformWayland.cpp index 5f1a18a8cf60..94d54b70572c 100644 --- a/Source/Core/DolphinNoGUI/PlatformWayland.cpp +++ b/Source/Core/DolphinNoGUI/PlatformWayland.cpp @@ -10,6 +10,7 @@ #include "Core/Config/MainSettings.h" #include "Core/Core.h" #include "Core/State.h" +#include "InputCommon/ControllerInterface/ControllerInterface.h" #include #include @@ -129,7 +130,11 @@ void PlatformWayland::TopLevelConfigure(void* data, struct xdg_toplevel* xdg_top platform->m_surface_width = width; platform->m_surface_height = height; if (g_renderer) + { g_renderer->ResizeSurface(width, height); + if (g_controller_interface.IsInit()) + g_controller_interface.OnWindowResized(width, height); + } } void PlatformWayland::TopLevelClose(void* data, struct xdg_toplevel* xdg_toplevel) diff --git a/Source/Core/DolphinNoGUI/PlatformX11.cpp b/Source/Core/DolphinNoGUI/PlatformX11.cpp index 0bee07d7d140..1f0c7f72550d 100644 --- a/Source/Core/DolphinNoGUI/PlatformX11.cpp +++ b/Source/Core/DolphinNoGUI/PlatformX11.cpp @@ -15,6 +15,7 @@ static constexpr auto X_None = None; #include "Core/Config/MainSettings.h" #include "Core/Core.h" #include "Core/State.h" +#include "InputCommon/ControllerInterface/ControllerInterface.h" #include #include @@ -57,8 +58,8 @@ class PlatformX11 : public Platform #endif int m_window_x = Config::Get(Config::MAIN_RENDER_WINDOW_XPOS); int m_window_y = Config::Get(Config::MAIN_RENDER_WINDOW_YPOS); - unsigned int m_window_width = Config::Get(Config::MAIN_RENDER_WINDOW_WIDTH); - unsigned int m_window_height = Config::Get(Config::MAIN_RENDER_WINDOW_HEIGHT); + int m_window_width = Config::Get(Config::MAIN_RENDER_WINDOW_WIDTH); + int m_window_height = Config::Get(Config::MAIN_RENDER_WINDOW_HEIGHT); }; PlatformX11::~PlatformX11() @@ -178,8 +179,9 @@ void PlatformX11::UpdateWindowPosition() Window winDummy; unsigned int borderDummy, depthDummy; - XGetGeometry(m_display, m_window, &winDummy, &m_window_x, &m_window_y, &m_window_width, - &m_window_height, &borderDummy, &depthDummy); + XGetGeometry(m_display, m_window, &winDummy, &m_window_x, &m_window_y, + reinterpret_cast(&m_window_width), + reinterpret_cast(&m_window_height), &borderDummy, &depthDummy); } void PlatformX11::ProcessEvents() @@ -270,6 +272,8 @@ void PlatformX11::ProcessEvents() UpdateWindowPosition(); g_renderer->ResizeSurface(m_window_width, m_window_height); } + if (g_controller_interface.IsInit()) + g_controller_interface.OnWindowResized(m_window_width, m_window_height); } break; } diff --git a/Source/Core/DolphinQt/Host.cpp b/Source/Core/DolphinQt/Host.cpp index 4ee94361733a..b6a9f5ca0e29 100644 --- a/Source/Core/DolphinQt/Host.cpp +++ b/Source/Core/DolphinQt/Host.cpp @@ -79,7 +79,8 @@ void Host::SetRenderHandle(void* handle, int width, int height) if (g_renderer) { g_renderer->ChangeSurface(handle, width, height); - g_controller_interface.ChangeWindow(handle); + if (g_controller_interface.IsInit()) + g_controller_interface.ChangeWindow(handle, width, height); } } @@ -189,7 +190,11 @@ void Host::SetRenderFullscreen(bool fullscreen) void Host::ResizeSurface(int new_width, int new_height) { if (g_renderer) + { g_renderer->ResizeSurface(new_width, new_height); + if (g_controller_interface.IsInit()) + g_controller_interface.OnWindowResized(new_width, new_height); + } } std::vector Host_GetPreferredLocales() diff --git a/Source/Core/DolphinQt/MainWindow.cpp b/Source/Core/DolphinQt/MainWindow.cpp index 2a2aca618fe4..ddd95a8939f4 100644 --- a/Source/Core/DolphinQt/MainWindow.cpp +++ b/Source/Core/DolphinQt/MainWindow.cpp @@ -1172,9 +1172,9 @@ void MainWindow::HideRenderWidget(bool reinit, bool is_exit) // The controller interface will still be registered to the old render widget, if the core // has booted. Therefore, we should re-bind it to the main window for now. When the core // is next started, it will be swapped back to the new render widget. - g_controller_interface.ChangeWindow(GetWindowSystemInfo(this).render_surface, - is_exit ? ControllerInterface::WindowChangeReason::Exit : - ControllerInterface::WindowChangeReason::Other); + const WindowSystemInfo wsi = GetWindowSystemInfo(this); + g_controller_interface.ChangeWindow(wsi.render_surface, wsi.render_surface_width, + wsi.render_surface_height); } } diff --git a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp index 1b98d39d475e..51eab886068b 100644 --- a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp +++ b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp @@ -53,7 +53,10 @@ void ControllerInterface::Initialize(const WindowSystemInfo& wsi) std::lock_guard lk_population(m_devices_population_mutex); + // Prevent divide by zero if we somehow end up with a 0x0 window. m_wsi = wsi; + m_wsi.render_surface_width = std::max(m_wsi.render_surface_width, 1); + m_wsi.render_surface_height = std::max(m_wsi.render_surface_height, 1); m_populating_devices_counter = 1; @@ -100,13 +103,15 @@ void ControllerInterface::Initialize(const WindowSystemInfo& wsi) InvokeDevicesChangedCallbacks(); } -void ControllerInterface::ChangeWindow(void* hwnd, WindowChangeReason reason) +void ControllerInterface::ChangeWindow(void* hwnd, int width, int height, WindowChangeReason reason) { if (!m_is_init) return; // This shouldn't use render_surface so no need to update it. m_wsi.render_window = hwnd; + m_wsi.render_surface_width = std::max(width, 1); + m_wsi.render_surface_height = std::max(height, 1); // No need to re-add devices if this is an application exit request if (reason == WindowChangeReason::Exit) @@ -115,6 +120,15 @@ void ControllerInterface::ChangeWindow(void* hwnd, WindowChangeReason reason) RefreshDevices(RefreshReason::WindowChangeOnly); } +void ControllerInterface::OnWindowResized(int width, int height) +{ + m_wsi.render_surface_width = std::max(width, 1); + m_wsi.render_surface_height = std::max(height, 1); + std::lock_guard lk(m_devices_mutex); + for (const auto& d : m_devices) + d->OnWindowResized(width, height); +} + void ControllerInterface::RefreshDevices(RefreshReason reason) { if (!m_is_init) @@ -175,7 +189,7 @@ void ControllerInterface::RefreshDevices(RefreshReason reason) #endif #ifdef CIFACE_USE_XLIB if (m_wsi.type == WindowSystemType::X11) - ciface::XInput2::PopulateDevices(m_wsi.render_window); + ciface::XInput2::PopulateDevices(m_wsi); #endif #ifdef CIFACE_USE_WAYLAND if (m_wsi.type == WindowSystemType::Wayland) diff --git a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h index cc3cb54f5075..9bce26789b83 100644 --- a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h +++ b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.h @@ -85,7 +85,9 @@ class ControllerInterface : public ciface::Core::DeviceContainer ControllerInterface() : m_is_init(false) {} void Initialize(const WindowSystemInfo& wsi); // Only call from one thread at a time. - void ChangeWindow(void* hwnd, WindowChangeReason reason = WindowChangeReason::Other); + void ChangeWindow(void* hwnd, int width, int height, + WindowChangeReason reason = WindowChangeReason::Other); + void OnWindowResized(int width, int height); // Can be called by any thread at any time (when initialized). void RefreshDevices(RefreshReason reason = RefreshReason::Other); void Shutdown(); diff --git a/Source/Core/InputCommon/ControllerInterface/CoreDevice.h b/Source/Core/InputCommon/ControllerInterface/CoreDevice.h index 2280ddac3bd8..c9f2c95752b7 100644 --- a/Source/Core/InputCommon/ControllerInterface/CoreDevice.h +++ b/Source/Core/InputCommon/ControllerInterface/CoreDevice.h @@ -139,6 +139,9 @@ class Device // Every platform should have at least one device with priority >= 0. virtual int GetSortPriority() const { return 0; } + // Called when the window the device is bound to is resized. + virtual void OnWindowResized(int width, int height) {} + const std::vector& Inputs() const { return m_inputs; } const std::vector& Outputs() const { return m_outputs; } diff --git a/Source/Core/InputCommon/ControllerInterface/Wayland/Wayland.cpp b/Source/Core/InputCommon/ControllerInterface/Wayland/Wayland.cpp index 599ffcfe3d25..68db95e5a2d9 100644 --- a/Source/Core/InputCommon/ControllerInterface/Wayland/Wayland.cpp +++ b/Source/Core/InputCommon/ControllerInterface/Wayland/Wayland.cpp @@ -137,6 +137,12 @@ void KeyboardMouse::UpdateInput() wl_display_dispatch_queue_pending(m_display, m_event_queue); } +void KeyboardMouse::OnWindowResized(int width, int height) +{ + m_window_width = width; + m_window_height = height; +} + void KeyboardMouse::PointerEnter(void* data, wl_pointer* pointer, uint32_t serial, wl_surface* surface, wl_fixed_t surface_x, wl_fixed_t surface_y) { diff --git a/Source/Core/InputCommon/ControllerInterface/Wayland/Wayland.h b/Source/Core/InputCommon/ControllerInterface/Wayland/Wayland.h index 706eeb17b06c..22639a63f2ed 100644 --- a/Source/Core/InputCommon/ControllerInterface/Wayland/Wayland.h +++ b/Source/Core/InputCommon/ControllerInterface/Wayland/Wayland.h @@ -101,6 +101,7 @@ class KeyboardMouse : public Core::Device std::string GetName() const override; std::string GetSource() const override; void UpdateInput() override; + void OnWindowResized(int width, int height) override; private: void AddKeyInputs(); diff --git a/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.cpp b/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.cpp index 0c5f5ffb01c5..224ac5081ec6 100644 --- a/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.cpp +++ b/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.cpp @@ -52,7 +52,7 @@ namespace ciface::XInput2 { // This function will add zero or more KeyboardMouse objects to devices. -void PopulateDevices(void* const hwnd) +void PopulateDevices(const WindowSystemInfo& wsi) { Display* dpy = XOpenDisplay(nullptr); @@ -86,7 +86,7 @@ void PopulateDevices(void* const hwnd) // Since current_master is a master pointer, its attachment must // be a master keyboard. g_controller_interface.AddDevice(std::make_shared( - (Window)hwnd, xi_opcode, current_master->deviceid, current_master->attachment)); + wsi, xi_opcode, current_master->deviceid, current_master->attachment)); } } @@ -128,8 +128,9 @@ void KeyboardMouse::SelectEventsForDevice(XIEventMask* mask, int deviceid) XIFreeDeviceInfo(all_slaves); } -KeyboardMouse::KeyboardMouse(Window window, int opcode, int pointer, int keyboard) - : m_window(window), xi_opcode(opcode), pointer_deviceid(pointer), keyboard_deviceid(keyboard) +KeyboardMouse::KeyboardMouse(const WindowSystemInfo& wsi, int opcode, int pointer, int keyboard) + : m_window(reinterpret_cast(wsi.render_window)), xi_opcode(opcode), + pointer_deviceid(pointer), keyboard_deviceid(keyboard) { // The cool thing about each KeyboardMouse object having its own Display // is that each one gets its own separate copy of the X11 event stream, @@ -137,6 +138,8 @@ KeyboardMouse::KeyboardMouse(Window window, int opcode, int pointer, int keyboar // in. So be aware that each KeyboardMouse object actually has its own X11 // "context." m_display = XOpenDisplay(nullptr); + m_window_width = wsi.render_surface_width; + m_window_height = wsi.render_surface_height; // should always be 1 int unused; @@ -348,6 +351,12 @@ void KeyboardMouse::UpdateInput() m_state.keyboard[i] &= keyboard[i]; } +void KeyboardMouse::OnWindowResized(int width, int height) +{ + m_window_width = width; + m_window_height = height; +} + std::string KeyboardMouse::GetName() const { // This is the name string we got from the X server for this master diff --git a/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.h b/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.h index b07f6325c7ce..7b9883457597 100644 --- a/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.h +++ b/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.h @@ -14,11 +14,12 @@ extern "C" { } #include "Common/Matrix.h" +#include "Common/WindowSystemInfo.h" #include "InputCommon/ControllerInterface/ControllerInterface.h" namespace ciface::XInput2 { -void PopulateDevices(void* const hwnd); +void PopulateDevices(const WindowSystemInfo& wsi); class KeyboardMouse : public Core::Device { @@ -112,8 +113,10 @@ class KeyboardMouse : public Core::Device public: void UpdateInput() override; + void OnWindowResized(int width, int height) override; - KeyboardMouse(Window window, int opcode, int pointer_deviceid, int keyboard_deviceid); + KeyboardMouse(const WindowSystemInfo& wsi, int opcode, int pointer_deviceid, + int keyboard_deviceid); ~KeyboardMouse(); std::string GetName() const override; @@ -127,5 +130,7 @@ class KeyboardMouse : public Core::Device const int pointer_deviceid; const int keyboard_deviceid; std::string name; + int m_window_width; + int m_window_height; }; } // namespace ciface::XInput2 From cf188707ddf1af3495a17171f79bb08d947354a4 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Wed, 24 Jul 2019 13:12:22 +1000 Subject: [PATCH 8/8] DolphinQt: Work around surface-already-created error in Wayland when stopping --- Source/Core/DolphinQt/MainWindow.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Source/Core/DolphinQt/MainWindow.cpp b/Source/Core/DolphinQt/MainWindow.cpp index ddd95a8939f4..4cbbafe4fc30 100644 --- a/Source/Core/DolphinQt/MainWindow.cpp +++ b/Source/Core/DolphinQt/MainWindow.cpp @@ -1142,7 +1142,11 @@ void MainWindow::HideRenderWidget(bool reinit, bool is_exit) // Remove the widget from the stack and reparent it to nullptr, so that it can draw // itself in a new window if it wants. Disconnect the title updates. m_stack->removeWidget(m_render_widget); - m_render_widget->setParent(nullptr); + + // Don't bother unparenting it if we're going to destroy the window anyway. + if (!reinit) + m_render_widget->setParent(nullptr); + m_rendering_to_main = false; m_stack->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); disconnect(Host::GetInstance(), &Host::RequestTitle, this, &MainWindow::setWindowTitle); @@ -1154,9 +1158,7 @@ void MainWindow::HideRenderWidget(bool reinit, bool is_exit) // recreated if (reinit) { - m_render_widget->hide(); disconnect(m_render_widget, &RenderWidget::Closed, this, &MainWindow::ForceStop); - m_render_widget->removeEventFilter(this); m_render_widget->deleteLater();