Merge pull request #8343 from stenzek/fbdev
DolphinNoGUI: Add a FBDev platform
This commit is contained in:
commit
a89fdb628c
|
@ -30,7 +30,7 @@ const std::array<std::pair<int, int>, 9> GLContext::s_desktop_opengl_versions =
|
|||
|
||||
GLContext::~GLContext() = default;
|
||||
|
||||
bool GLContext::Initialize(void* display_handle, void* window_handle, bool stereo, bool core)
|
||||
bool GLContext::Initialize(const WindowSystemInfo& wsi, bool stereo, bool core)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -108,7 +108,7 @@ std::unique_ptr<GLContext> GLContext::Create(const WindowSystemInfo& wsi, bool s
|
|||
}
|
||||
#endif
|
||||
#if HAVE_EGL
|
||||
if (wsi.type == WindowSystemType::Headless)
|
||||
if (wsi.type == WindowSystemType::Headless || wsi.type == WindowSystemType::FBDev)
|
||||
context = std::make_unique<GLContextEGL>();
|
||||
#endif
|
||||
|
||||
|
@ -119,7 +119,7 @@ std::unique_ptr<GLContext> GLContext::Create(const WindowSystemInfo& wsi, bool s
|
|||
if (prefer_gles)
|
||||
context->m_opengl_mode = Mode::OpenGLES;
|
||||
|
||||
if (!context->Initialize(wsi.display_connection, wsi.render_surface, stereo, core))
|
||||
if (!context->Initialize(wsi, stereo, core))
|
||||
return nullptr;
|
||||
|
||||
return context;
|
||||
|
|
|
@ -52,7 +52,7 @@ public:
|
|||
bool prefer_gles = false);
|
||||
|
||||
protected:
|
||||
virtual bool Initialize(void* display_handle, void* window_handle, bool stereo, bool core);
|
||||
virtual bool Initialize(const WindowSystemInfo& wsi, bool stereo, bool core);
|
||||
|
||||
Mode m_opengl_mode = Mode::Detect;
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ public:
|
|||
void SwapInterval(int interval) override;
|
||||
|
||||
protected:
|
||||
bool Initialize(void* display_handle, void* window_handle, bool stereo, bool core) override;
|
||||
bool Initialize(const WindowSystemInfo& wsi, bool stereo, bool core) override;
|
||||
|
||||
NSView* m_view = nullptr;
|
||||
NSOpenGLContext* m_context = nullptr;
|
||||
|
|
|
@ -69,7 +69,7 @@ void GLContextAGL::Swap()
|
|||
|
||||
// Create rendering window.
|
||||
// Call browser: Core.cpp:EmuThread() > main.cpp:Video_Initialize()
|
||||
bool GLContextAGL::Initialize(void* display_handle, void* window_handle, bool stereo, bool core)
|
||||
bool GLContextAGL::Initialize(const WindowSystemInfo& wsi, bool stereo, bool core)
|
||||
{
|
||||
NSOpenGLPixelFormatAttribute attr[] = {
|
||||
NSOpenGLPFADoubleBuffer,
|
||||
|
@ -92,10 +92,10 @@ bool GLContextAGL::Initialize(void* display_handle, void* window_handle, bool st
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!window_handle)
|
||||
if (!wsi.render_surface)
|
||||
return true;
|
||||
|
||||
m_view = static_cast<NSView*>(window_handle);
|
||||
m_view = static_cast<NSView*>(wsi.render_surface);
|
||||
m_opengl_mode = Mode::OpenGL;
|
||||
if (!AttachContextToView(m_context, m_view, &m_backbuffer_width, &m_backbuffer_height))
|
||||
return false;
|
||||
|
|
|
@ -35,7 +35,7 @@ GLContextEGL::~GLContextEGL()
|
|||
|
||||
bool GLContextEGL::IsHeadless() const
|
||||
{
|
||||
return m_host_window == nullptr;
|
||||
return m_wsi.type == WindowSystemType::Headless;
|
||||
}
|
||||
|
||||
void GLContextEGL::Swap()
|
||||
|
@ -53,7 +53,7 @@ void* GLContextEGL::GetFuncAddress(const std::string& name)
|
|||
return (void*)eglGetProcAddress(name.c_str());
|
||||
}
|
||||
|
||||
void GLContextEGL::DetectMode(bool has_handle)
|
||||
void GLContextEGL::DetectMode()
|
||||
{
|
||||
EGLint num_configs;
|
||||
bool supportsGL = false, supportsGLES3 = false;
|
||||
|
@ -72,7 +72,7 @@ void GLContextEGL::DetectMode(bool has_handle)
|
|||
EGL_RENDERABLE_TYPE,
|
||||
renderable_type,
|
||||
EGL_SURFACE_TYPE,
|
||||
has_handle ? EGL_WINDOW_BIT : 0,
|
||||
IsHeadless() ? 0 : EGL_WINDOW_BIT,
|
||||
EGL_NONE};
|
||||
|
||||
// Get how many configs there are
|
||||
|
@ -130,27 +130,23 @@ void GLContextEGL::DetectMode(bool has_handle)
|
|||
|
||||
EGLDisplay GLContextEGL::OpenEGLDisplay()
|
||||
{
|
||||
return eglGetDisplay(EGL_DEFAULT_DISPLAY);
|
||||
return eglGetDisplay(static_cast<EGLNativeDisplayType>(m_wsi.render_surface));
|
||||
}
|
||||
|
||||
EGLNativeWindowType GLContextEGL::GetEGLNativeWindow(EGLConfig config)
|
||||
{
|
||||
return reinterpret_cast<EGLNativeWindowType>(EGL_DEFAULT_DISPLAY);
|
||||
return reinterpret_cast<EGLNativeWindowType>(m_wsi.display_connection);
|
||||
}
|
||||
|
||||
// Create rendering window.
|
||||
// Call browser: Core.cpp:EmuThread() > main.cpp:Video_Initialize()
|
||||
bool GLContextEGL::Initialize(void* display_handle, void* window_handle, bool stereo, bool core)
|
||||
bool GLContextEGL::Initialize(const WindowSystemInfo& wsi, bool stereo, bool core)
|
||||
{
|
||||
const bool has_handle = !!window_handle;
|
||||
|
||||
EGLint egl_major, egl_minor;
|
||||
bool supports_core_profile = false;
|
||||
|
||||
m_host_display = display_handle;
|
||||
m_host_window = window_handle;
|
||||
m_wsi = wsi;
|
||||
m_egl_display = OpenEGLDisplay();
|
||||
|
||||
if (!m_egl_display)
|
||||
{
|
||||
INFO_LOG(VIDEO, "Error: eglGetDisplay() failed");
|
||||
|
@ -167,7 +163,7 @@ bool GLContextEGL::Initialize(void* display_handle, void* window_handle, bool st
|
|||
EGLint num_configs;
|
||||
|
||||
if (m_opengl_mode == Mode::Detect)
|
||||
DetectMode(has_handle);
|
||||
DetectMode();
|
||||
|
||||
// attributes for a visual in RGBA format with at least
|
||||
// 8 bits per color
|
||||
|
@ -180,7 +176,7 @@ bool GLContextEGL::Initialize(void* display_handle, void* window_handle, bool st
|
|||
EGL_BLUE_SIZE,
|
||||
8,
|
||||
EGL_SURFACE_TYPE,
|
||||
has_handle ? EGL_WINDOW_BIT : 0,
|
||||
IsHeadless() ? 0 : EGL_WINDOW_BIT,
|
||||
EGL_NONE};
|
||||
|
||||
std::vector<EGLint> ctx_attribs;
|
||||
|
@ -278,7 +274,7 @@ std::unique_ptr<GLContext> GLContextEGL::CreateSharedContext()
|
|||
std::unique_ptr<GLContextEGL> new_context = std::make_unique<GLContextEGL>();
|
||||
new_context->m_opengl_mode = m_opengl_mode;
|
||||
new_context->m_egl_context = new_egl_context;
|
||||
new_context->m_host_display = m_host_display;
|
||||
new_context->m_wsi.display_connection = m_wsi.display_connection;
|
||||
new_context->m_egl_display = m_egl_display;
|
||||
new_context->m_config = m_config;
|
||||
new_context->m_supports_surfaceless = m_supports_surfaceless;
|
||||
|
@ -294,7 +290,7 @@ std::unique_ptr<GLContext> GLContextEGL::CreateSharedContext()
|
|||
|
||||
bool GLContextEGL::CreateWindowSurface()
|
||||
{
|
||||
if (m_host_window)
|
||||
if (!IsHeadless())
|
||||
{
|
||||
EGLNativeWindowType native_window = GetEGLNativeWindow(m_config);
|
||||
m_egl_surface = eglCreateWindowSurface(m_egl_display, m_config, native_window, nullptr);
|
||||
|
@ -303,6 +299,17 @@ bool GLContextEGL::CreateWindowSurface()
|
|||
INFO_LOG(VIDEO, "Error: eglCreateWindowSurface failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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(VIDEO,
|
||||
"Failed to get surface dimensions via eglQuerySurface. Size may be incorrect.");
|
||||
}
|
||||
m_backbuffer_width = static_cast<int>(surface_width);
|
||||
m_backbuffer_height = static_cast<int>(surface_height);
|
||||
}
|
||||
else if (!m_supports_surfaceless)
|
||||
{
|
||||
|
@ -342,7 +349,7 @@ bool GLContextEGL::MakeCurrent()
|
|||
|
||||
void GLContextEGL::UpdateSurface(void* window_handle)
|
||||
{
|
||||
m_host_window = window_handle;
|
||||
m_wsi.render_surface = window_handle;
|
||||
ClearCurrent();
|
||||
DestroyWindowSurface();
|
||||
CreateWindowSurface();
|
||||
|
|
|
@ -34,15 +34,14 @@ protected:
|
|||
virtual EGLDisplay OpenEGLDisplay();
|
||||
virtual EGLNativeWindowType GetEGLNativeWindow(EGLConfig config);
|
||||
|
||||
bool Initialize(void* display_handle, void* window_handle, bool stereo, bool core) override;
|
||||
bool Initialize(const WindowSystemInfo& wsi, bool stereo, bool core) override;
|
||||
|
||||
bool CreateWindowSurface();
|
||||
void DestroyWindowSurface();
|
||||
void DetectMode(bool has_handle);
|
||||
void DetectMode();
|
||||
void DestroyContext();
|
||||
|
||||
void* m_host_display = nullptr;
|
||||
void* m_host_window = nullptr;
|
||||
WindowSystemInfo m_wsi = {};
|
||||
|
||||
EGLConfig m_config;
|
||||
bool m_supports_surfaceless = false;
|
||||
|
|
|
@ -14,8 +14,8 @@ EGLNativeWindowType GLContextEGLAndroid::GetEGLNativeWindow(EGLConfig config)
|
|||
{
|
||||
EGLint format;
|
||||
eglGetConfigAttrib(m_egl_display, config, EGL_NATIVE_VISUAL_ID, &format);
|
||||
ANativeWindow_setBuffersGeometry(static_cast<ANativeWindow*>(m_host_window), 0, 0, format);
|
||||
m_backbuffer_width = ANativeWindow_getWidth(static_cast<ANativeWindow*>(m_host_window));
|
||||
m_backbuffer_height = ANativeWindow_getHeight(static_cast<ANativeWindow*>(m_host_window));
|
||||
return static_cast<EGLNativeWindowType>(m_host_window);
|
||||
ANativeWindow_setBuffersGeometry(static_cast<ANativeWindow*>(m_wsi.render_surface), 0, 0, format);
|
||||
m_backbuffer_width = ANativeWindow_getWidth(static_cast<ANativeWindow*>(m_wsi.render_surface));
|
||||
m_backbuffer_height = ANativeWindow_getHeight(static_cast<ANativeWindow*>(m_wsi.render_surface));
|
||||
return static_cast<EGLNativeWindowType>(m_wsi.render_surface);
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ void GLContextEGLX11::Update()
|
|||
|
||||
EGLDisplay GLContextEGLX11::OpenEGLDisplay()
|
||||
{
|
||||
return eglGetDisplay(static_cast<Display*>(m_host_display));
|
||||
return eglGetDisplay(static_cast<Display*>(m_wsi.display_connection));
|
||||
}
|
||||
|
||||
EGLNativeWindowType GLContextEGLX11::GetEGLNativeWindow(EGLConfig config)
|
||||
|
@ -33,14 +33,14 @@ EGLNativeWindowType GLContextEGLX11::GetEGLNativeWindow(EGLConfig config)
|
|||
visTemplate.visualid = vid;
|
||||
|
||||
int nVisuals;
|
||||
XVisualInfo* vi =
|
||||
XGetVisualInfo(static_cast<Display*>(m_host_display), VisualIDMask, &visTemplate, &nVisuals);
|
||||
XVisualInfo* vi = XGetVisualInfo(static_cast<Display*>(m_wsi.display_connection), VisualIDMask,
|
||||
&visTemplate, &nVisuals);
|
||||
|
||||
if (m_render_window)
|
||||
m_render_window.reset();
|
||||
|
||||
m_render_window = GLX11Window::Create(static_cast<Display*>(m_host_display),
|
||||
reinterpret_cast<Window>(m_host_window), vi);
|
||||
m_render_window = GLX11Window::Create(static_cast<Display*>(m_wsi.display_connection),
|
||||
reinterpret_cast<Window>(m_wsi.render_surface), vi);
|
||||
m_backbuffer_width = m_render_window->GetWidth();
|
||||
m_backbuffer_height = m_render_window->GetHeight();
|
||||
|
||||
|
|
|
@ -73,9 +73,9 @@ void GLContextGLX::Swap()
|
|||
|
||||
// Create rendering window.
|
||||
// Call browser: Core.cpp:EmuThread() > main.cpp:Video_Initialize()
|
||||
bool GLContextGLX::Initialize(void* display_handle, void* window_handle, bool stereo, bool core)
|
||||
bool GLContextGLX::Initialize(const WindowSystemInfo& wsi, bool stereo, bool core)
|
||||
{
|
||||
m_display = static_cast<Display*>(display_handle);
|
||||
m_display = static_cast<Display*>(wsi.display_connection);
|
||||
int screen = DefaultScreen(m_display);
|
||||
|
||||
// checking glx version
|
||||
|
@ -204,7 +204,7 @@ bool GLContextGLX::Initialize(void* display_handle, void* window_handle, bool st
|
|||
}
|
||||
}
|
||||
|
||||
if (!CreateWindowSurface(reinterpret_cast<Window>(window_handle)))
|
||||
if (!CreateWindowSurface(reinterpret_cast<Window>(wsi.render_surface)))
|
||||
{
|
||||
ERROR_LOG(VIDEO, "Error: CreateWindowSurface failed\n");
|
||||
XSetErrorHandler(oldHandler);
|
||||
|
|
|
@ -33,7 +33,7 @@ public:
|
|||
void* GetFuncAddress(const std::string& name) override;
|
||||
|
||||
protected:
|
||||
bool Initialize(void* display_handle, void* window_handle, bool stereo, bool core) override;
|
||||
bool Initialize(const WindowSystemInfo& wsi, bool stereo, bool core) override;
|
||||
|
||||
Display* m_display = nullptr;
|
||||
std::unique_ptr<GLX11Window> m_render_window;
|
||||
|
|
|
@ -223,13 +223,13 @@ void* GLContextWGL::GetFuncAddress(const std::string& name)
|
|||
|
||||
// Create rendering window.
|
||||
// Call browser: Core.cpp:EmuThread() > main.cpp:Video_Initialize()
|
||||
bool GLContextWGL::Initialize(void* display_handle, void* window_handle, bool stereo, bool core)
|
||||
bool GLContextWGL::Initialize(const WindowSystemInfo& wsi, bool stereo, bool core)
|
||||
{
|
||||
if (!window_handle)
|
||||
if (!wsi.render_surface)
|
||||
return false;
|
||||
|
||||
RECT window_rect = {};
|
||||
m_window_handle = reinterpret_cast<HWND>(window_handle);
|
||||
m_window_handle = reinterpret_cast<HWND>(wsi.render_surface);
|
||||
if (!GetClientRect(m_window_handle, &window_rect))
|
||||
return false;
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ public:
|
|||
void* GetFuncAddress(const std::string& name) override;
|
||||
|
||||
protected:
|
||||
bool Initialize(void* display_handle, void* window_handle, bool stereo, bool core) override;
|
||||
bool Initialize(const WindowSystemInfo& wsi, bool stereo, bool core) override;
|
||||
|
||||
static HGLRC CreateCoreContext(HDC dc, HGLRC share_context);
|
||||
static bool CreatePBuffer(HDC onscreen_dc, int width, int height, HANDLE* pbuffer_handle,
|
||||
|
|
|
@ -11,7 +11,8 @@ enum class WindowSystemType
|
|||
MacOS,
|
||||
Android,
|
||||
X11,
|
||||
Wayland
|
||||
Wayland,
|
||||
FBDev,
|
||||
};
|
||||
|
||||
struct WindowSystemInfo
|
||||
|
|
|
@ -9,6 +9,10 @@ if(ENABLE_X11 AND X11_FOUND)
|
|||
target_sources(dolphin-nogui PRIVATE PlatformX11.cpp)
|
||||
endif()
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
target_sources(dolphin-nogui PRIVATE PlatformFBDev.cpp)
|
||||
endif()
|
||||
|
||||
set_target_properties(dolphin-nogui PROPERTIES OUTPUT_NAME dolphin-emu-nogui)
|
||||
|
||||
target_link_libraries(dolphin-nogui
|
||||
|
|
|
@ -121,6 +121,11 @@ static std::unique_ptr<Platform> GetPlatform(const optparse::Values& options)
|
|||
return Platform::CreateX11Platform();
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
if (platform_name == "fbdev" || platform_name.empty())
|
||||
return Platform::CreateFBDevPlatform();
|
||||
#endif
|
||||
|
||||
if (platform_name == "headless" || platform_name.empty())
|
||||
return Platform::CreateHeadlessPlatform();
|
||||
|
||||
|
@ -135,6 +140,10 @@ int main(int argc, char* argv[])
|
|||
.help("Window platform to use [%choices]")
|
||||
.choices({
|
||||
"headless"
|
||||
#ifdef __linux__
|
||||
,
|
||||
"fbdev"
|
||||
#endif
|
||||
#if HAVE_X11
|
||||
,
|
||||
"x11"
|
||||
|
|
|
@ -35,6 +35,9 @@ public:
|
|||
#ifdef HAVE_X11
|
||||
static std::unique_ptr<Platform> CreateX11Platform();
|
||||
#endif
|
||||
#ifdef __linux__
|
||||
static std::unique_ptr<Platform> CreateFBDevPlatform();
|
||||
#endif
|
||||
|
||||
protected:
|
||||
void UpdateRunningFlag();
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
// Copyright 2018 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "DolphinNoGUI/Platform.h"
|
||||
|
||||
#include "Common/MsgHandler.h"
|
||||
#include "Core/ConfigManager.h"
|
||||
#include "Core/Core.h"
|
||||
#include "Core/State.h"
|
||||
|
||||
#include <climits>
|
||||
#include <cstdio>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <linux/fb.h>
|
||||
#include <linux/kd.h>
|
||||
#include <linux/vt.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
#include "VideoCommon/RenderBase.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
class PlatformFBDev : public Platform
|
||||
{
|
||||
public:
|
||||
~PlatformFBDev() override;
|
||||
|
||||
bool Init() override;
|
||||
void SetTitle(const std::string& string) override;
|
||||
void MainLoop() override;
|
||||
|
||||
WindowSystemInfo GetWindowSystemInfo() const;
|
||||
|
||||
private:
|
||||
bool OpenFramebuffer();
|
||||
|
||||
int m_fb_fd = -1;
|
||||
};
|
||||
|
||||
PlatformFBDev::~PlatformFBDev()
|
||||
{
|
||||
if (m_fb_fd >= 0)
|
||||
close(m_fb_fd);
|
||||
}
|
||||
|
||||
bool PlatformFBDev::Init()
|
||||
{
|
||||
if (!OpenFramebuffer())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PlatformFBDev::OpenFramebuffer()
|
||||
{
|
||||
m_fb_fd = open("/dev/fb0", O_RDWR);
|
||||
if (m_fb_fd < 0)
|
||||
{
|
||||
std::fprintf(stderr, "open(/dev/fb0) failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PlatformFBDev::SetTitle(const std::string& string)
|
||||
{
|
||||
std::fprintf(stdout, "%s\n", string.c_str());
|
||||
}
|
||||
|
||||
void PlatformFBDev::MainLoop()
|
||||
{
|
||||
while (IsRunning())
|
||||
{
|
||||
UpdateRunningFlag();
|
||||
Core::HostDispatchJobs();
|
||||
|
||||
// TODO: Is this sleep appropriate?
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
}
|
||||
|
||||
WindowSystemInfo PlatformFBDev::GetWindowSystemInfo() const
|
||||
{
|
||||
WindowSystemInfo wsi;
|
||||
wsi.type = WindowSystemType::FBDev;
|
||||
wsi.display_connection = nullptr; // EGL_DEFAULT_DISPLAY
|
||||
wsi.render_surface = nullptr;
|
||||
return wsi;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::unique_ptr<Platform> Platform::CreateFBDevPlatform()
|
||||
{
|
||||
return std::make_unique<PlatformFBDev>();
|
||||
}
|
Loading…
Reference in New Issue