@@ -79,13 +79,6 @@ set(GUI_SRCS
|
||||
WxUtils.cpp
|
||||
)
|
||||
|
||||
set(NOGUI_SRCS MainNoGUI.cpp)
|
||||
|
||||
if(USE_X11)
|
||||
set(GUI_SRCS ${GUI_SRCS} X11Utils.cpp)
|
||||
set(NOGUI_SRCS ${NOGUI_SRCS} X11Utils.cpp)
|
||||
endif()
|
||||
|
||||
set(WXLIBS ${wxWidgets_LIBRARIES})
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
@@ -200,11 +193,3 @@ if(wxWidgets_FOUND)
|
||||
|
||||
set(CPACK_PACKAGE_EXECUTABLES ${CPACK_PACKAGE_EXECUTABLES} ${DOLPHIN_EXE})
|
||||
endif()
|
||||
|
||||
if(USE_X11 OR ENABLE_HEADLESS)
|
||||
set(DOLPHIN_NOGUI_EXE ${DOLPHIN_EXE_BASE}-nogui)
|
||||
add_executable(${DOLPHIN_NOGUI_EXE} ${SRCS} ${NOGUI_SRCS})
|
||||
target_link_libraries(${DOLPHIN_NOGUI_EXE} ${LIBS})
|
||||
set(CPACK_PACKAGE_EXECUTABLES ${CPACK_PACKAGE_EXECUTABLES} ${DOLPHIN_NOGUI_EXE})
|
||||
install(TARGETS ${DOLPHIN_NOGUI_EXE} RUNTIME DESTINATION ${bindir})
|
||||
endif()
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
#include "DolphinWX/WxUtils.h"
|
||||
|
||||
#if defined(HAVE_XRANDR) && HAVE_XRANDR
|
||||
#include "DolphinWX/X11Utils.h"
|
||||
#include "UICommon/X11Utils.h"
|
||||
#endif
|
||||
|
||||
static const std::array<std::string, 29> language_ids{{
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
#include "InputCommon/GCAdapter.h"
|
||||
|
||||
#if defined(HAVE_XRANDR) && HAVE_XRANDR
|
||||
#include "DolphinWX/X11Utils.h"
|
||||
#include "UICommon/X11Utils.h"
|
||||
#endif
|
||||
|
||||
ControllerConfigDiag::ControllerConfigDiag(wxWindow* const parent)
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
#include "InputCommon/GCPadStatus.h"
|
||||
|
||||
#if defined(HAVE_X11) && HAVE_X11
|
||||
#include "DolphinWX/X11Utils.h"
|
||||
#include "UICommon/X11Utils.h"
|
||||
#endif
|
||||
|
||||
// Class declarations
|
||||
|
||||
@@ -1,455 +0,0 @@
|
||||
// Copyright 2008 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <OptionParser.h>
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <signal.h>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/Event.h"
|
||||
#include "Common/Flag.h"
|
||||
#include "Common/Logging/LogManager.h"
|
||||
#include "Common/MsgHandler.h"
|
||||
|
||||
#include "Core/Analytics.h"
|
||||
#include "Core/BootManager.h"
|
||||
#include "Core/ConfigManager.h"
|
||||
#include "Core/Core.h"
|
||||
#include "Core/HW/Wiimote.h"
|
||||
#include "Core/Host.h"
|
||||
#include "Core/IOS/IPC.h"
|
||||
#include "Core/IOS/STM/STM.h"
|
||||
#include "Core/IOS/USB/Bluetooth/BTEmu.h"
|
||||
#include "Core/IOS/USB/Bluetooth/WiimoteDevice.h"
|
||||
#include "Core/State.h"
|
||||
|
||||
#include "UICommon/CommandLineParse.h"
|
||||
#include "UICommon/UICommon.h"
|
||||
|
||||
#include "VideoCommon/RenderBase.h"
|
||||
#include "VideoCommon/VideoBackendBase.h"
|
||||
|
||||
static bool rendererHasFocus = true;
|
||||
static bool rendererIsFullscreen = false;
|
||||
static Common::Flag s_running{true};
|
||||
static Common::Flag s_shutdown_requested{false};
|
||||
static Common::Flag s_tried_graceful_shutdown{false};
|
||||
|
||||
static void signal_handler(int)
|
||||
{
|
||||
const char message[] = "A signal was received. A second signal will force Dolphin to stop.\n";
|
||||
if (write(STDERR_FILENO, message, sizeof(message)) < 0)
|
||||
{
|
||||
}
|
||||
s_shutdown_requested.Set();
|
||||
}
|
||||
|
||||
namespace ProcessorInterface
|
||||
{
|
||||
void PowerButton_Tap();
|
||||
}
|
||||
|
||||
class Platform
|
||||
{
|
||||
public:
|
||||
virtual void Init() {}
|
||||
virtual void SetTitle(const std::string& title) {}
|
||||
virtual void MainLoop()
|
||||
{
|
||||
while (s_running.IsSet())
|
||||
{
|
||||
Core::HostDispatchJobs();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
}
|
||||
}
|
||||
virtual void Shutdown() {}
|
||||
virtual ~Platform() {}
|
||||
};
|
||||
|
||||
static Platform* platform;
|
||||
|
||||
void Host_NotifyMapLoaded()
|
||||
{
|
||||
}
|
||||
void Host_RefreshDSPDebuggerWindow()
|
||||
{
|
||||
}
|
||||
|
||||
static Common::Event updateMainFrameEvent;
|
||||
void Host_Message(int Id)
|
||||
{
|
||||
if (Id == WM_USER_STOP)
|
||||
{
|
||||
s_running.Clear();
|
||||
updateMainFrameEvent.Set();
|
||||
}
|
||||
}
|
||||
|
||||
static void* s_window_handle = nullptr;
|
||||
void* Host_GetRenderHandle()
|
||||
{
|
||||
return s_window_handle;
|
||||
}
|
||||
|
||||
void Host_UpdateTitle(const std::string& title)
|
||||
{
|
||||
platform->SetTitle(title);
|
||||
}
|
||||
|
||||
void Host_UpdateDisasmDialog()
|
||||
{
|
||||
}
|
||||
|
||||
void Host_UpdateMainFrame()
|
||||
{
|
||||
updateMainFrameEvent.Set();
|
||||
}
|
||||
|
||||
void Host_RequestRenderWindowSize(int width, int height)
|
||||
{
|
||||
}
|
||||
|
||||
void Host_SetStartupDebuggingParameters()
|
||||
{
|
||||
SConfig& StartUp = SConfig::GetInstance();
|
||||
StartUp.bEnableDebugging = false;
|
||||
StartUp.bBootToPause = false;
|
||||
}
|
||||
|
||||
bool Host_UIHasFocus()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Host_RendererHasFocus()
|
||||
{
|
||||
return rendererHasFocus;
|
||||
}
|
||||
|
||||
bool Host_RendererIsFullscreen()
|
||||
{
|
||||
return rendererIsFullscreen;
|
||||
}
|
||||
|
||||
void Host_ConnectWiimote(int wm_idx, bool connect)
|
||||
{
|
||||
if (Core::IsRunning() && SConfig::GetInstance().bWii &&
|
||||
!SConfig::GetInstance().m_bt_passthrough_enabled)
|
||||
{
|
||||
Core::QueueHostJob([=] {
|
||||
bool was_unpaused = Core::PauseAndLock(true);
|
||||
const auto bt = std::static_pointer_cast<IOS::HLE::Device::BluetoothEmu>(
|
||||
IOS::HLE::GetDeviceByName("/dev/usb/oh1/57e/305"));
|
||||
if (bt)
|
||||
bt->AccessWiiMote(wm_idx | 0x100)->Activate(connect);
|
||||
Host_UpdateMainFrame();
|
||||
Core::PauseAndLock(false, was_unpaused);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void Host_SetWiiMoteConnectionState(int _State)
|
||||
{
|
||||
}
|
||||
|
||||
void Host_ShowVideoConfig(void*, const std::string&)
|
||||
{
|
||||
}
|
||||
|
||||
void Host_YieldToUI()
|
||||
{
|
||||
}
|
||||
|
||||
#if HAVE_X11
|
||||
#include <X11/keysym.h>
|
||||
#include "DolphinWX/X11Utils.h"
|
||||
|
||||
class PlatformX11 : public Platform
|
||||
{
|
||||
Display* dpy;
|
||||
Window win;
|
||||
Cursor blankCursor = None;
|
||||
#if defined(HAVE_XRANDR) && HAVE_XRANDR
|
||||
X11Utils::XRRConfiguration* XRRConfig;
|
||||
#endif
|
||||
|
||||
void Init() override
|
||||
{
|
||||
XInitThreads();
|
||||
dpy = XOpenDisplay(nullptr);
|
||||
if (!dpy)
|
||||
{
|
||||
PanicAlert("No X11 display found");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
win = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy), SConfig::GetInstance().iRenderWindowXPos,
|
||||
SConfig::GetInstance().iRenderWindowYPos,
|
||||
SConfig::GetInstance().iRenderWindowWidth,
|
||||
SConfig::GetInstance().iRenderWindowHeight, 0, 0, BlackPixel(dpy, 0));
|
||||
XSelectInput(dpy, win, StructureNotifyMask | KeyPressMask | FocusChangeMask);
|
||||
Atom wmProtocols[1];
|
||||
wmProtocols[0] = XInternAtom(dpy, "WM_DELETE_WINDOW", True);
|
||||
XSetWMProtocols(dpy, win, wmProtocols, 1);
|
||||
XMapRaised(dpy, win);
|
||||
XFlush(dpy);
|
||||
s_window_handle = (void*)win;
|
||||
|
||||
if (SConfig::GetInstance().bDisableScreenSaver)
|
||||
X11Utils::InhibitScreensaver(dpy, win, true);
|
||||
|
||||
#if defined(HAVE_XRANDR) && HAVE_XRANDR
|
||||
XRRConfig = new X11Utils::XRRConfiguration(dpy, win);
|
||||
#endif
|
||||
|
||||
if (SConfig::GetInstance().bHideCursor)
|
||||
{
|
||||
// make a blank cursor
|
||||
Pixmap Blank;
|
||||
XColor DummyColor;
|
||||
char ZeroData[1] = {0};
|
||||
Blank = XCreateBitmapFromData(dpy, win, ZeroData, 1, 1);
|
||||
blankCursor = XCreatePixmapCursor(dpy, Blank, Blank, &DummyColor, &DummyColor, 0, 0);
|
||||
XFreePixmap(dpy, Blank);
|
||||
XDefineCursor(dpy, win, blankCursor);
|
||||
}
|
||||
}
|
||||
|
||||
void SetTitle(const std::string& string) override { XStoreName(dpy, win, string.c_str()); }
|
||||
void MainLoop() override
|
||||
{
|
||||
bool fullscreen = SConfig::GetInstance().bFullscreen;
|
||||
int last_window_width = SConfig::GetInstance().iRenderWindowWidth;
|
||||
int last_window_height = SConfig::GetInstance().iRenderWindowHeight;
|
||||
|
||||
if (fullscreen)
|
||||
{
|
||||
rendererIsFullscreen = X11Utils::ToggleFullscreen(dpy, win);
|
||||
#if defined(HAVE_XRANDR) && HAVE_XRANDR
|
||||
XRRConfig->ToggleDisplayMode(True);
|
||||
#endif
|
||||
}
|
||||
|
||||
// The actual loop
|
||||
while (s_running.IsSet())
|
||||
{
|
||||
if (s_shutdown_requested.TestAndClear())
|
||||
{
|
||||
const auto stm = IOS::HLE::GetDeviceByName("/dev/stm/eventhook");
|
||||
if (!s_tried_graceful_shutdown.IsSet() && stm &&
|
||||
std::static_pointer_cast<IOS::HLE::Device::STMEventHook>(stm)->HasHookInstalled())
|
||||
{
|
||||
ProcessorInterface::PowerButton_Tap();
|
||||
s_tried_graceful_shutdown.Set();
|
||||
}
|
||||
else
|
||||
{
|
||||
s_running.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
XEvent event;
|
||||
KeySym key;
|
||||
for (int num_events = XPending(dpy); num_events > 0; num_events--)
|
||||
{
|
||||
XNextEvent(dpy, &event);
|
||||
switch (event.type)
|
||||
{
|
||||
case KeyPress:
|
||||
key = XLookupKeysym((XKeyEvent*)&event, 0);
|
||||
if (key == XK_Escape)
|
||||
{
|
||||
if (Core::GetState() == Core::State::Running)
|
||||
{
|
||||
if (SConfig::GetInstance().bHideCursor)
|
||||
XUndefineCursor(dpy, win);
|
||||
Core::SetState(Core::State::Paused);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (SConfig::GetInstance().bHideCursor)
|
||||
XDefineCursor(dpy, win, blankCursor);
|
||||
Core::SetState(Core::State::Running);
|
||||
}
|
||||
}
|
||||
else if ((key == XK_Return) && (event.xkey.state & Mod1Mask))
|
||||
{
|
||||
fullscreen = !fullscreen;
|
||||
X11Utils::ToggleFullscreen(dpy, win);
|
||||
#if defined(HAVE_XRANDR) && HAVE_XRANDR
|
||||
XRRConfig->ToggleDisplayMode(fullscreen);
|
||||
#endif
|
||||
}
|
||||
else if (key >= XK_F1 && key <= XK_F8)
|
||||
{
|
||||
int slot_number = key - XK_F1 + 1;
|
||||
if (event.xkey.state & ShiftMask)
|
||||
State::Save(slot_number);
|
||||
else
|
||||
State::Load(slot_number);
|
||||
}
|
||||
else if (key == XK_F9)
|
||||
Core::SaveScreenShot();
|
||||
else if (key == XK_F11)
|
||||
State::LoadLastSaved();
|
||||
else if (key == XK_F12)
|
||||
{
|
||||
if (event.xkey.state & ShiftMask)
|
||||
State::UndoLoadState();
|
||||
else
|
||||
State::UndoSaveState();
|
||||
}
|
||||
break;
|
||||
case FocusIn:
|
||||
rendererHasFocus = true;
|
||||
if (SConfig::GetInstance().bHideCursor && Core::GetState() != Core::State::Paused)
|
||||
XDefineCursor(dpy, win, blankCursor);
|
||||
break;
|
||||
case FocusOut:
|
||||
rendererHasFocus = false;
|
||||
if (SConfig::GetInstance().bHideCursor)
|
||||
XUndefineCursor(dpy, win);
|
||||
break;
|
||||
case ClientMessage:
|
||||
if ((unsigned long)event.xclient.data.l[0] == XInternAtom(dpy, "WM_DELETE_WINDOW", False))
|
||||
s_shutdown_requested.Set();
|
||||
break;
|
||||
case ConfigureNotify:
|
||||
{
|
||||
if (last_window_width != event.xconfigure.width ||
|
||||
last_window_height != event.xconfigure.height)
|
||||
{
|
||||
last_window_width = event.xconfigure.width;
|
||||
last_window_height = event.xconfigure.height;
|
||||
|
||||
// We call Renderer::ChangeSurface here to indicate the size has changed,
|
||||
// but pass the same window handle. This is needed for the Vulkan backend,
|
||||
// otherwise it cannot tell that the window has been resized on some drivers.
|
||||
if (g_renderer)
|
||||
g_renderer->ChangeSurface(s_window_handle);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!fullscreen)
|
||||
{
|
||||
Window winDummy;
|
||||
unsigned int borderDummy, depthDummy;
|
||||
XGetGeometry(dpy, win, &winDummy, &SConfig::GetInstance().iRenderWindowXPos,
|
||||
&SConfig::GetInstance().iRenderWindowYPos,
|
||||
(unsigned int*)&SConfig::GetInstance().iRenderWindowWidth,
|
||||
(unsigned int*)&SConfig::GetInstance().iRenderWindowHeight, &borderDummy,
|
||||
&depthDummy);
|
||||
rendererIsFullscreen = false;
|
||||
}
|
||||
Core::HostDispatchJobs();
|
||||
usleep(100000);
|
||||
}
|
||||
}
|
||||
|
||||
void Shutdown() override
|
||||
{
|
||||
#if defined(HAVE_XRANDR) && HAVE_XRANDR
|
||||
delete XRRConfig;
|
||||
#endif
|
||||
|
||||
if (SConfig::GetInstance().bHideCursor)
|
||||
XFreeCursor(dpy, blankCursor);
|
||||
|
||||
XCloseDisplay(dpy);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
static Platform* GetPlatform()
|
||||
{
|
||||
#if defined(USE_EGL) && defined(USE_HEADLESS)
|
||||
return new Platform();
|
||||
#elif HAVE_X11
|
||||
return new PlatformX11();
|
||||
#endif
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
auto parser = CommandLineParse::CreateParser(CommandLineParse::ParserOptions::OmitGUIOptions);
|
||||
optparse::Values& options = CommandLineParse::ParseArguments(parser.get(), argc, argv);
|
||||
std::vector<std::string> args = parser->args();
|
||||
|
||||
std::string boot_filename;
|
||||
if (options.is_set("exec"))
|
||||
{
|
||||
boot_filename = static_cast<const char*>(options.get("exec"));
|
||||
}
|
||||
else if (args.size())
|
||||
{
|
||||
boot_filename = args.front();
|
||||
args.erase(args.begin());
|
||||
}
|
||||
else
|
||||
{
|
||||
parser->print_help();
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string user_directory;
|
||||
if (options.is_set("user"))
|
||||
{
|
||||
user_directory = static_cast<const char*>(options.get("user"));
|
||||
}
|
||||
|
||||
platform = GetPlatform();
|
||||
if (!platform)
|
||||
{
|
||||
fprintf(stderr, "No platform found\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
UICommon::SetUserDirectory(user_directory);
|
||||
UICommon::Init();
|
||||
|
||||
Core::SetOnStoppedCallback([]() { s_running.Clear(); });
|
||||
platform->Init();
|
||||
|
||||
// Shut down cleanly on SIGINT and SIGTERM
|
||||
struct sigaction sa;
|
||||
sa.sa_handler = signal_handler;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = SA_RESETHAND;
|
||||
sigaction(SIGINT, &sa, nullptr);
|
||||
sigaction(SIGTERM, &sa, nullptr);
|
||||
|
||||
DolphinAnalytics::Instance()->ReportDolphinStart("nogui");
|
||||
|
||||
if (!BootManager::BootCore(boot_filename))
|
||||
{
|
||||
fprintf(stderr, "Could not boot %s\n", boot_filename.c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
while (!Core::IsRunning() && s_running.IsSet())
|
||||
{
|
||||
Core::HostDispatchJobs();
|
||||
updateMainFrameEvent.Wait();
|
||||
}
|
||||
|
||||
if (s_running.IsSet())
|
||||
platform->MainLoop();
|
||||
Core::Stop();
|
||||
|
||||
Core::Shutdown();
|
||||
platform->Shutdown();
|
||||
UICommon::Shutdown();
|
||||
|
||||
delete platform;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,275 +0,0 @@
|
||||
// Copyright 2010 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "DolphinWX/X11Utils.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <spawn.h>
|
||||
#include <string>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/StringUtil.h"
|
||||
#include "Core/ConfigManager.h"
|
||||
#include "Core/Core.h"
|
||||
|
||||
extern char** environ;
|
||||
|
||||
namespace X11Utils
|
||||
{
|
||||
bool ToggleFullscreen(Display* dpy, Window win)
|
||||
{
|
||||
// Init X event structure for _NET_WM_STATE_FULLSCREEN client message
|
||||
XEvent event;
|
||||
event.xclient.type = ClientMessage;
|
||||
event.xclient.message_type = XInternAtom(dpy, "_NET_WM_STATE", False);
|
||||
event.xclient.window = win;
|
||||
event.xclient.format = 32;
|
||||
event.xclient.data.l[0] = _NET_WM_STATE_TOGGLE;
|
||||
event.xclient.data.l[1] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
|
||||
|
||||
// Send the event
|
||||
if (!XSendEvent(dpy, DefaultRootWindow(dpy), False,
|
||||
SubstructureRedirectMask | SubstructureNotifyMask, &event))
|
||||
{
|
||||
ERROR_LOG(VIDEO, "Failed to switch fullscreen/windowed mode.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void InhibitScreensaver(Display* dpy, Window win, bool suspend)
|
||||
{
|
||||
char id[11];
|
||||
snprintf(id, sizeof(id), "0x%lx", win);
|
||||
|
||||
// Call xdg-screensaver
|
||||
char* argv[4] = {(char*)"xdg-screensaver", (char*)(suspend ? "suspend" : "resume"), id, nullptr};
|
||||
pid_t pid;
|
||||
if (!posix_spawnp(&pid, "xdg-screensaver", nullptr, nullptr, argv, environ))
|
||||
{
|
||||
int status;
|
||||
while (waitpid(pid, &status, 0) == -1)
|
||||
;
|
||||
|
||||
INFO_LOG(VIDEO, "Started xdg-screensaver (PID = %d)", (int)pid);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(HAVE_XRANDR) && HAVE_XRANDR
|
||||
XRRConfiguration::XRRConfiguration(Display* _dpy, Window _win)
|
||||
: dpy(_dpy), win(_win), screenResources(nullptr), outputInfo(nullptr), crtcInfo(nullptr),
|
||||
fullMode(0), fs_fb_width(0), fs_fb_height(0), fs_fb_width_mm(0), fs_fb_height_mm(0),
|
||||
bValid(true), bIsFullscreen(false)
|
||||
{
|
||||
int XRRMajorVersion, XRRMinorVersion;
|
||||
|
||||
if (!XRRQueryVersion(dpy, &XRRMajorVersion, &XRRMinorVersion) ||
|
||||
(XRRMajorVersion < 1 || (XRRMajorVersion == 1 && XRRMinorVersion < 3)))
|
||||
{
|
||||
WARN_LOG(VIDEO, "XRRExtension not supported.");
|
||||
bValid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
screenResources = XRRGetScreenResourcesCurrent(dpy, win);
|
||||
|
||||
screen = DefaultScreen(dpy);
|
||||
fb_width = DisplayWidth(dpy, screen);
|
||||
fb_height = DisplayHeight(dpy, screen);
|
||||
fb_width_mm = DisplayWidthMM(dpy, screen);
|
||||
fb_height_mm = DisplayHeightMM(dpy, screen);
|
||||
|
||||
INFO_LOG(VIDEO, "XRRExtension-Version %d.%d", XRRMajorVersion, XRRMinorVersion);
|
||||
Update();
|
||||
}
|
||||
|
||||
XRRConfiguration::~XRRConfiguration()
|
||||
{
|
||||
if (bValid && bIsFullscreen)
|
||||
ToggleDisplayMode(False);
|
||||
if (screenResources)
|
||||
XRRFreeScreenResources(screenResources);
|
||||
if (outputInfo)
|
||||
XRRFreeOutputInfo(outputInfo);
|
||||
if (crtcInfo)
|
||||
XRRFreeCrtcInfo(crtcInfo);
|
||||
}
|
||||
|
||||
void XRRConfiguration::Update()
|
||||
{
|
||||
if (SConfig::GetInstance().strFullscreenResolution == "Auto")
|
||||
return;
|
||||
|
||||
if (!bValid)
|
||||
return;
|
||||
|
||||
if (outputInfo)
|
||||
{
|
||||
XRRFreeOutputInfo(outputInfo);
|
||||
outputInfo = nullptr;
|
||||
}
|
||||
if (crtcInfo)
|
||||
{
|
||||
XRRFreeCrtcInfo(crtcInfo);
|
||||
crtcInfo = nullptr;
|
||||
}
|
||||
fullMode = 0;
|
||||
|
||||
// Get the resolution setings for fullscreen mode
|
||||
unsigned int fullWidth, fullHeight;
|
||||
char* output_name = nullptr;
|
||||
char auxFlag = '\0';
|
||||
if (SConfig::GetInstance().strFullscreenResolution.find(':') == std::string::npos)
|
||||
{
|
||||
fullWidth = fb_width;
|
||||
fullHeight = fb_height;
|
||||
}
|
||||
else
|
||||
{
|
||||
sscanf(SConfig::GetInstance().strFullscreenResolution.c_str(), "%m[^:]: %ux%u%c", &output_name,
|
||||
&fullWidth, &fullHeight, &auxFlag);
|
||||
}
|
||||
bool want_interlaced = ('i' == auxFlag);
|
||||
|
||||
for (int i = 0; i < screenResources->noutput; i++)
|
||||
{
|
||||
XRROutputInfo* output_info =
|
||||
XRRGetOutputInfo(dpy, screenResources, screenResources->outputs[i]);
|
||||
if (output_info && output_info->crtc && output_info->connection == RR_Connected)
|
||||
{
|
||||
XRRCrtcInfo* crtc_info = XRRGetCrtcInfo(dpy, screenResources, output_info->crtc);
|
||||
if (crtc_info)
|
||||
{
|
||||
if (!output_name || !strcmp(output_name, output_info->name))
|
||||
{
|
||||
// Use the first output for the default setting.
|
||||
if (!output_name)
|
||||
{
|
||||
output_name = strdup(output_info->name);
|
||||
SConfig::GetInstance().strFullscreenResolution =
|
||||
StringFromFormat("%s: %ux%u", output_info->name, fullWidth, fullHeight);
|
||||
}
|
||||
outputInfo = output_info;
|
||||
crtcInfo = crtc_info;
|
||||
for (int j = 0; j < output_info->nmode && fullMode == 0; j++)
|
||||
{
|
||||
for (int k = 0; k < screenResources->nmode && fullMode == 0; k++)
|
||||
{
|
||||
if (output_info->modes[j] == screenResources->modes[k].id)
|
||||
{
|
||||
if (fullWidth == screenResources->modes[k].width &&
|
||||
fullHeight == screenResources->modes[k].height &&
|
||||
want_interlaced == !!(screenResources->modes[k].modeFlags & RR_Interlace))
|
||||
{
|
||||
fullMode = screenResources->modes[k].id;
|
||||
if (crtcInfo->x + (int)screenResources->modes[k].width > fs_fb_width)
|
||||
fs_fb_width = crtcInfo->x + screenResources->modes[k].width;
|
||||
if (crtcInfo->y + (int)screenResources->modes[k].height > fs_fb_height)
|
||||
fs_fb_height = crtcInfo->y + screenResources->modes[k].height;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (crtc_info->x + (int)crtc_info->width > fs_fb_width)
|
||||
fs_fb_width = crtc_info->x + crtc_info->width;
|
||||
if (crtc_info->y + (int)crtc_info->height > fs_fb_height)
|
||||
fs_fb_height = crtc_info->y + crtc_info->height;
|
||||
}
|
||||
}
|
||||
if (crtc_info && crtcInfo != crtc_info)
|
||||
XRRFreeCrtcInfo(crtc_info);
|
||||
}
|
||||
if (output_info && outputInfo != output_info)
|
||||
XRRFreeOutputInfo(output_info);
|
||||
}
|
||||
fs_fb_width_mm = fs_fb_width * DisplayHeightMM(dpy, screen) / DisplayHeight(dpy, screen);
|
||||
fs_fb_height_mm = fs_fb_height * DisplayHeightMM(dpy, screen) / DisplayHeight(dpy, screen);
|
||||
|
||||
if (output_name)
|
||||
free(output_name);
|
||||
|
||||
if (outputInfo && crtcInfo && fullMode)
|
||||
{
|
||||
INFO_LOG(VIDEO, "Fullscreen Resolution %dx%d", fullWidth, fullHeight);
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR_LOG(VIDEO, "Failed to obtain fullscreen size.\n"
|
||||
"Using current desktop resolution for fullscreen.");
|
||||
}
|
||||
}
|
||||
|
||||
void XRRConfiguration::ToggleDisplayMode(bool bFullscreen)
|
||||
{
|
||||
if (!bValid || !screenResources || !outputInfo || !crtcInfo || !fullMode)
|
||||
return;
|
||||
if (bFullscreen == bIsFullscreen)
|
||||
return;
|
||||
|
||||
XGrabServer(dpy);
|
||||
if (bFullscreen)
|
||||
{
|
||||
XRRSetCrtcConfig(dpy, screenResources, outputInfo->crtc, CurrentTime, crtcInfo->x, crtcInfo->y,
|
||||
fullMode, crtcInfo->rotation, crtcInfo->outputs, crtcInfo->noutput);
|
||||
XRRSetScreenSize(dpy, win, fs_fb_width, fs_fb_height, fs_fb_width_mm, fs_fb_height_mm);
|
||||
bIsFullscreen = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
XRRSetCrtcConfig(dpy, screenResources, outputInfo->crtc, CurrentTime, crtcInfo->x, crtcInfo->y,
|
||||
crtcInfo->mode, crtcInfo->rotation, crtcInfo->outputs, crtcInfo->noutput);
|
||||
XRRSetScreenSize(dpy, win, fb_width, fb_height, fb_width_mm, fb_height_mm);
|
||||
bIsFullscreen = false;
|
||||
}
|
||||
XUngrabServer(dpy);
|
||||
XSync(dpy, false);
|
||||
}
|
||||
|
||||
void XRRConfiguration::AddResolutions(std::vector<std::string>& resos)
|
||||
{
|
||||
if (!bValid || !screenResources)
|
||||
return;
|
||||
|
||||
// Get all full screen resolutions for the config dialog
|
||||
for (int i = 0; i < screenResources->noutput; i++)
|
||||
{
|
||||
XRROutputInfo* output_info =
|
||||
XRRGetOutputInfo(dpy, screenResources, screenResources->outputs[i]);
|
||||
|
||||
if (output_info && output_info->crtc && output_info->connection == RR_Connected)
|
||||
{
|
||||
for (int j = 0; j < output_info->nmode; j++)
|
||||
{
|
||||
for (int k = 0; k < screenResources->nmode; k++)
|
||||
{
|
||||
if (output_info->modes[j] == screenResources->modes[k].id)
|
||||
{
|
||||
bool interlaced = !!(screenResources->modes[k].modeFlags & RR_Interlace);
|
||||
const std::string strRes = std::string(output_info->name) + ": " +
|
||||
std::string(screenResources->modes[k].name) +
|
||||
(interlaced ? "i" : "");
|
||||
// Only add unique resolutions
|
||||
if (std::find(resos.begin(), resos.end(), strRes) == resos.end())
|
||||
{
|
||||
resos.push_back(strRes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (output_info)
|
||||
XRRFreeOutputInfo(output_info);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
// Copyright 2010 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#if defined(HAVE_XRANDR) && HAVE_XRANDR
|
||||
#include <X11/extensions/Xrandr.h>
|
||||
#endif
|
||||
#include <X11/X.h>
|
||||
|
||||
// EWMH state actions, see
|
||||
// http://freedesktop.org/wiki/Specifications/wm-spec?action=show&redirect=Standards%2Fwm-spec
|
||||
#define _NET_WM_STATE_REMOVE 0 // remove/unset property
|
||||
#define _NET_WM_STATE_ADD 1 // add/set property
|
||||
#define _NET_WM_STATE_TOGGLE 2 // toggle property
|
||||
|
||||
namespace X11Utils
|
||||
{
|
||||
bool ToggleFullscreen(Display* dpy, Window win);
|
||||
Window XWindowFromHandle(void* Handle);
|
||||
Display* XDisplayFromHandle(void* Handle);
|
||||
|
||||
void InhibitScreensaver(Display* dpy, Window win, bool suspend);
|
||||
|
||||
#if defined(HAVE_XRANDR) && HAVE_XRANDR
|
||||
class XRRConfiguration
|
||||
{
|
||||
public:
|
||||
XRRConfiguration(Display* _dpy, Window _win);
|
||||
~XRRConfiguration();
|
||||
|
||||
void Update();
|
||||
void ToggleDisplayMode(bool bFullscreen);
|
||||
void AddResolutions(std::vector<std::string>& resos);
|
||||
|
||||
private:
|
||||
Display* dpy;
|
||||
Window win;
|
||||
int screen;
|
||||
XRRScreenResources* screenResources;
|
||||
XRROutputInfo* outputInfo;
|
||||
XRRCrtcInfo* crtcInfo;
|
||||
RRMode fullMode;
|
||||
int fb_width, fb_height, fb_width_mm, fb_height_mm;
|
||||
int fs_fb_width, fs_fb_height, fs_fb_width_mm, fs_fb_height_mm;
|
||||
bool bValid;
|
||||
bool bIsFullscreen;
|
||||
};
|
||||
#endif
|
||||
}
|
||||
Reference in New Issue
Block a user