X11Utils.cpp 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. // Copyright 2010 Dolphin Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include "UICommon/X11Utils.h"
  4. #include <algorithm>
  5. #include <cstdio>
  6. #include <cstdlib>
  7. #include <cstring>
  8. #include <spawn.h>
  9. #include <string>
  10. #include <sys/wait.h>
  11. #include <unistd.h>
  12. #include <fmt/format.h>
  13. #include "Common/Logging/Log.h"
  14. #include "Common/StringUtil.h"
  15. #include "Core/Config/MainSettings.h"
  16. #include "Core/Core.h"
  17. extern char** environ;
  18. namespace X11Utils
  19. {
  20. bool ToggleFullscreen(Display* dpy, Window win)
  21. {
  22. // Init X event structure for _NET_WM_STATE_FULLSCREEN client message
  23. XEvent event;
  24. event.xclient.type = ClientMessage;
  25. event.xclient.message_type = XInternAtom(dpy, "_NET_WM_STATE", False);
  26. event.xclient.window = win;
  27. event.xclient.format = 32;
  28. event.xclient.data.l[0] = _NET_WM_STATE_TOGGLE;
  29. event.xclient.data.l[1] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
  30. // Send the event
  31. if (!XSendEvent(dpy, DefaultRootWindow(dpy), False,
  32. SubstructureRedirectMask | SubstructureNotifyMask, &event))
  33. {
  34. ERROR_LOG_FMT(VIDEO, "Failed to switch fullscreen/windowed mode.");
  35. return false;
  36. }
  37. return true;
  38. }
  39. #ifdef HAVE_XRANDR
  40. XRRConfiguration::XRRConfiguration(Display* _dpy, Window _win)
  41. : dpy(_dpy), win(_win), screenResources(nullptr), outputInfo(nullptr), crtcInfo(nullptr),
  42. fullMode(0), fs_fb_width(0), fs_fb_height(0), fs_fb_width_mm(0), fs_fb_height_mm(0),
  43. bValid(true), bIsFullscreen(false)
  44. {
  45. int XRRMajorVersion, XRRMinorVersion;
  46. if (!XRRQueryVersion(dpy, &XRRMajorVersion, &XRRMinorVersion) ||
  47. (XRRMajorVersion < 1 || (XRRMajorVersion == 1 && XRRMinorVersion < 3)))
  48. {
  49. WARN_LOG_FMT(VIDEO, "XRRExtension not supported.");
  50. bValid = false;
  51. return;
  52. }
  53. screenResources = XRRGetScreenResourcesCurrent(dpy, win);
  54. screen = DefaultScreen(dpy);
  55. fb_width = DisplayWidth(dpy, screen);
  56. fb_height = DisplayHeight(dpy, screen);
  57. fb_width_mm = DisplayWidthMM(dpy, screen);
  58. fb_height_mm = DisplayHeightMM(dpy, screen);
  59. INFO_LOG_FMT(VIDEO, "XRRExtension-Version {}.{}", XRRMajorVersion, XRRMinorVersion);
  60. Update();
  61. }
  62. XRRConfiguration::~XRRConfiguration()
  63. {
  64. if (bValid && bIsFullscreen)
  65. ToggleDisplayMode(False);
  66. if (screenResources)
  67. XRRFreeScreenResources(screenResources);
  68. if (outputInfo)
  69. XRRFreeOutputInfo(outputInfo);
  70. if (crtcInfo)
  71. XRRFreeCrtcInfo(crtcInfo);
  72. }
  73. void XRRConfiguration::Update()
  74. {
  75. const std::string fullscreen_display_res = Config::Get(Config::MAIN_FULLSCREEN_DISPLAY_RES);
  76. if (fullscreen_display_res == "Auto")
  77. return;
  78. if (!bValid)
  79. return;
  80. if (outputInfo)
  81. {
  82. XRRFreeOutputInfo(outputInfo);
  83. outputInfo = nullptr;
  84. }
  85. if (crtcInfo)
  86. {
  87. XRRFreeCrtcInfo(crtcInfo);
  88. crtcInfo = nullptr;
  89. }
  90. fullMode = 0;
  91. // Get the resolution setings for fullscreen mode
  92. unsigned int fullWidth, fullHeight;
  93. char* output_name = nullptr;
  94. char auxFlag = '\0';
  95. if (fullscreen_display_res.find(':') == std::string::npos)
  96. {
  97. fullWidth = fb_width;
  98. fullHeight = fb_height;
  99. }
  100. else
  101. {
  102. sscanf(fullscreen_display_res.c_str(), "%m[^:]: %ux%u%c", &output_name, &fullWidth, &fullHeight,
  103. &auxFlag);
  104. }
  105. bool want_interlaced = ('i' == auxFlag);
  106. for (int i = 0; i < screenResources->noutput; i++)
  107. {
  108. XRROutputInfo* output_info =
  109. XRRGetOutputInfo(dpy, screenResources, screenResources->outputs[i]);
  110. if (output_info && output_info->crtc && output_info->connection == RR_Connected)
  111. {
  112. XRRCrtcInfo* crtc_info = XRRGetCrtcInfo(dpy, screenResources, output_info->crtc);
  113. if (crtc_info)
  114. {
  115. if (!output_name || !strcmp(output_name, output_info->name))
  116. {
  117. // Use the first output for the default setting.
  118. if (!output_name)
  119. {
  120. output_name = strdup(output_info->name);
  121. Config::SetBaseOrCurrent(
  122. Config::MAIN_FULLSCREEN_DISPLAY_RES,
  123. fmt::format("{}: {}x{}", output_info->name, fullWidth, fullHeight));
  124. }
  125. outputInfo = output_info;
  126. crtcInfo = crtc_info;
  127. for (int j = 0; j < output_info->nmode && fullMode == 0; j++)
  128. {
  129. for (int k = 0; k < screenResources->nmode && fullMode == 0; k++)
  130. {
  131. if (output_info->modes[j] == screenResources->modes[k].id)
  132. {
  133. if (fullWidth == screenResources->modes[k].width &&
  134. fullHeight == screenResources->modes[k].height &&
  135. want_interlaced == !!(screenResources->modes[k].modeFlags & RR_Interlace))
  136. {
  137. fullMode = screenResources->modes[k].id;
  138. if (crtcInfo->x + (int)screenResources->modes[k].width > fs_fb_width)
  139. fs_fb_width = crtcInfo->x + screenResources->modes[k].width;
  140. if (crtcInfo->y + (int)screenResources->modes[k].height > fs_fb_height)
  141. fs_fb_height = crtcInfo->y + screenResources->modes[k].height;
  142. }
  143. }
  144. }
  145. }
  146. }
  147. else
  148. {
  149. if (crtc_info->x + (int)crtc_info->width > fs_fb_width)
  150. fs_fb_width = crtc_info->x + crtc_info->width;
  151. if (crtc_info->y + (int)crtc_info->height > fs_fb_height)
  152. fs_fb_height = crtc_info->y + crtc_info->height;
  153. }
  154. }
  155. if (crtc_info && crtcInfo != crtc_info)
  156. XRRFreeCrtcInfo(crtc_info);
  157. }
  158. if (output_info && outputInfo != output_info)
  159. XRRFreeOutputInfo(output_info);
  160. }
  161. fs_fb_width_mm = fs_fb_width * DisplayHeightMM(dpy, screen) / DisplayHeight(dpy, screen);
  162. fs_fb_height_mm = fs_fb_height * DisplayHeightMM(dpy, screen) / DisplayHeight(dpy, screen);
  163. if (output_name)
  164. free(output_name);
  165. if (outputInfo && crtcInfo && fullMode)
  166. {
  167. INFO_LOG_FMT(VIDEO, "Fullscreen Resolution {}x{}", fullWidth, fullHeight);
  168. }
  169. else
  170. {
  171. ERROR_LOG_FMT(VIDEO, "Failed to obtain fullscreen size.\n"
  172. "Using current desktop resolution for fullscreen.");
  173. }
  174. }
  175. void XRRConfiguration::ToggleDisplayMode(bool bFullscreen)
  176. {
  177. if (!bValid || !screenResources || !outputInfo || !crtcInfo || !fullMode)
  178. return;
  179. if (bFullscreen == bIsFullscreen)
  180. return;
  181. XGrabServer(dpy);
  182. if (bFullscreen)
  183. {
  184. XRRSetCrtcConfig(dpy, screenResources, outputInfo->crtc, CurrentTime, crtcInfo->x, crtcInfo->y,
  185. fullMode, crtcInfo->rotation, crtcInfo->outputs, crtcInfo->noutput);
  186. XRRSetScreenSize(dpy, win, fs_fb_width, fs_fb_height, fs_fb_width_mm, fs_fb_height_mm);
  187. bIsFullscreen = true;
  188. }
  189. else
  190. {
  191. XRRSetCrtcConfig(dpy, screenResources, outputInfo->crtc, CurrentTime, crtcInfo->x, crtcInfo->y,
  192. crtcInfo->mode, crtcInfo->rotation, crtcInfo->outputs, crtcInfo->noutput);
  193. XRRSetScreenSize(dpy, win, fb_width, fb_height, fb_width_mm, fb_height_mm);
  194. bIsFullscreen = false;
  195. }
  196. XUngrabServer(dpy);
  197. XSync(dpy, false);
  198. }
  199. void XRRConfiguration::AddResolutions(std::vector<std::string>& resos)
  200. {
  201. if (!bValid || !screenResources)
  202. return;
  203. // Get all full screen resolutions for the config dialog
  204. for (int i = 0; i < screenResources->noutput; i++)
  205. {
  206. XRROutputInfo* output_info =
  207. XRRGetOutputInfo(dpy, screenResources, screenResources->outputs[i]);
  208. if (output_info && output_info->crtc && output_info->connection == RR_Connected)
  209. {
  210. for (int j = 0; j < output_info->nmode; j++)
  211. {
  212. for (int k = 0; k < screenResources->nmode; k++)
  213. {
  214. if (output_info->modes[j] == screenResources->modes[k].id)
  215. {
  216. bool interlaced = !!(screenResources->modes[k].modeFlags & RR_Interlace);
  217. const std::string strRes = std::string(output_info->name) + ": " +
  218. std::string(screenResources->modes[k].name) +
  219. (interlaced ? "i" : "");
  220. // Only add unique resolutions
  221. if (std::find(resos.begin(), resos.end(), strRes) == resos.end())
  222. {
  223. resos.push_back(strRes);
  224. }
  225. }
  226. }
  227. }
  228. }
  229. if (output_info)
  230. XRRFreeOutputInfo(output_info);
  231. }
  232. }
  233. #endif
  234. } // namespace X11Utils