X11Utils.cpp 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. // Copyright 2010 Dolphin Emulator Project
  2. // Licensed under GPLv2+
  3. // Refer to the license.txt file included.
  4. #include <spawn.h>
  5. #include <unistd.h>
  6. #include <sys/wait.h>
  7. #include "Common/Logging/Log.h"
  8. #include "Core/ConfigManager.h"
  9. #include "Core/Core.h"
  10. #include "Core/CoreParameter.h"
  11. #include "DolphinWX/X11Utils.h"
  12. extern char **environ;
  13. #if defined(HAVE_WX) && HAVE_WX
  14. #include <algorithm>
  15. #include <string>
  16. #include "DolphinWX/WxUtils.h"
  17. #endif
  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(VIDEO, "Failed to switch fullscreen/windowed mode.");
  35. return false;
  36. }
  37. return true;
  38. }
  39. void InhibitScreensaver(Display *dpy, Window win, bool suspend)
  40. {
  41. char id[11];
  42. snprintf(id, sizeof(id), "0x%lx", win);
  43. // Call xdg-screensaver
  44. char *argv[4] = {
  45. (char *)"xdg-screensaver",
  46. (char *)(suspend ? "suspend" : "resume"),
  47. id,
  48. nullptr};
  49. pid_t pid;
  50. if (!posix_spawnp(&pid, "xdg-screensaver", nullptr, nullptr, argv, environ))
  51. {
  52. int status;
  53. while (waitpid (pid, &status, 0) == -1);
  54. DEBUG_LOG(VIDEO, "Started xdg-screensaver (PID = %d)", (int)pid);
  55. }
  56. }
  57. #if defined(HAVE_XRANDR) && HAVE_XRANDR
  58. XRRConfiguration::XRRConfiguration(Display *_dpy, Window _win)
  59. : dpy(_dpy)
  60. , win(_win)
  61. , screenResources(nullptr), outputInfo(nullptr), crtcInfo(nullptr)
  62. , fullMode(0)
  63. , fs_fb_width(0), fs_fb_height(0), fs_fb_width_mm(0), fs_fb_height_mm(0)
  64. , bValid(true), bIsFullscreen(false)
  65. {
  66. int XRRMajorVersion, XRRMinorVersion;
  67. if (!XRRQueryVersion(dpy, &XRRMajorVersion, &XRRMinorVersion) ||
  68. (XRRMajorVersion < 1 || (XRRMajorVersion == 1 && XRRMinorVersion < 3)))
  69. {
  70. WARN_LOG(VIDEO, "XRRExtension not supported.");
  71. bValid = false;
  72. return;
  73. }
  74. screenResources = XRRGetScreenResourcesCurrent(dpy, win);
  75. screen = DefaultScreen(dpy);
  76. fb_width = DisplayWidth(dpy, screen);
  77. fb_height = DisplayHeight(dpy, screen);
  78. fb_width_mm = DisplayWidthMM(dpy, screen);
  79. fb_height_mm = DisplayHeightMM(dpy, screen);
  80. INFO_LOG(VIDEO, "XRRExtension-Version %d.%d", XRRMajorVersion, XRRMinorVersion);
  81. Update();
  82. }
  83. XRRConfiguration::~XRRConfiguration()
  84. {
  85. if (bValid && bIsFullscreen)
  86. ToggleDisplayMode(False);
  87. if (screenResources)
  88. XRRFreeScreenResources(screenResources);
  89. if (outputInfo)
  90. XRRFreeOutputInfo(outputInfo);
  91. if (crtcInfo)
  92. XRRFreeCrtcInfo(crtcInfo);
  93. }
  94. void XRRConfiguration::Update()
  95. {
  96. if (SConfig::GetInstance().m_LocalCoreStartupParameter.strFullscreenResolution == "Auto")
  97. return;
  98. if (!bValid)
  99. return;
  100. if (outputInfo)
  101. {
  102. XRRFreeOutputInfo(outputInfo);
  103. outputInfo = nullptr;
  104. }
  105. if (crtcInfo)
  106. {
  107. XRRFreeCrtcInfo(crtcInfo);
  108. crtcInfo = nullptr;
  109. }
  110. fullMode = 0;
  111. // Get the resolution setings for fullscreen mode
  112. unsigned int fullWidth, fullHeight;
  113. char *output_name = nullptr;
  114. char auxFlag = '\0';
  115. if (SConfig::GetInstance().m_LocalCoreStartupParameter.strFullscreenResolution.find(':') ==
  116. std::string::npos)
  117. {
  118. fullWidth = fb_width;
  119. fullHeight = fb_height;
  120. }
  121. else
  122. {
  123. sscanf(SConfig::GetInstance().m_LocalCoreStartupParameter.strFullscreenResolution.c_str(),
  124. "%m[^:]: %ux%u%c", &output_name, &fullWidth, &fullHeight, &auxFlag);
  125. }
  126. bool want_interlaced = ('i' == auxFlag);
  127. for (int i = 0; i < screenResources->noutput; i++)
  128. {
  129. XRROutputInfo *output_info = XRRGetOutputInfo(dpy, screenResources, screenResources->outputs[i]);
  130. if (output_info && output_info->crtc && output_info->connection == RR_Connected)
  131. {
  132. XRRCrtcInfo *crtc_info = XRRGetCrtcInfo(dpy, screenResources, output_info->crtc);
  133. if (crtc_info)
  134. {
  135. if (!output_name || !strcmp(output_name, output_info->name))
  136. {
  137. // Use the first output for the default setting.
  138. if (!output_name)
  139. {
  140. output_name = strdup(output_info->name);
  141. SConfig::GetInstance().m_LocalCoreStartupParameter.strFullscreenResolution =
  142. StringFromFormat("%s: %ux%u", output_info->name, fullWidth, fullHeight);
  143. }
  144. outputInfo = output_info;
  145. crtcInfo = crtc_info;
  146. for (int j = 0; j < output_info->nmode && fullMode == 0; j++)
  147. {
  148. for (int k = 0; k < screenResources->nmode && fullMode == 0; k++)
  149. {
  150. if (output_info->modes[j] == screenResources->modes[k].id)
  151. {
  152. if (fullWidth == screenResources->modes[k].width &&
  153. fullHeight == screenResources->modes[k].height &&
  154. want_interlaced == !!(screenResources->modes[k].modeFlags & RR_Interlace))
  155. {
  156. fullMode = screenResources->modes[k].id;
  157. if (crtcInfo->x + (int)screenResources->modes[k].width > fs_fb_width)
  158. fs_fb_width = crtcInfo->x + screenResources->modes[k].width;
  159. if (crtcInfo->y + (int)screenResources->modes[k].height > fs_fb_height)
  160. fs_fb_height = crtcInfo->y + screenResources->modes[k].height;
  161. }
  162. }
  163. }
  164. }
  165. }
  166. else
  167. {
  168. if (crtc_info->x + (int)crtc_info->width > fs_fb_width)
  169. fs_fb_width = crtc_info->x + crtc_info->width;
  170. if (crtc_info->y + (int)crtc_info->height > fs_fb_height)
  171. fs_fb_height = crtc_info->y + crtc_info->height;
  172. }
  173. }
  174. if (crtc_info && crtcInfo != crtc_info)
  175. XRRFreeCrtcInfo(crtc_info);
  176. }
  177. if (output_info && outputInfo != output_info)
  178. XRRFreeOutputInfo(output_info);
  179. }
  180. fs_fb_width_mm = fs_fb_width * DisplayHeightMM(dpy, screen) / DisplayHeight(dpy, screen);
  181. fs_fb_height_mm = fs_fb_height * DisplayHeightMM(dpy, screen) / DisplayHeight(dpy, screen);
  182. if (output_name)
  183. free(output_name);
  184. if (outputInfo && crtcInfo && fullMode)
  185. {
  186. INFO_LOG(VIDEO, "Fullscreen Resolution %dx%d", fullWidth, fullHeight);
  187. }
  188. else
  189. {
  190. ERROR_LOG(VIDEO, "Failed to obtain fullscreen size.\n"
  191. "Using current desktop resolution for fullscreen.");
  192. }
  193. }
  194. void XRRConfiguration::ToggleDisplayMode(bool bFullscreen)
  195. {
  196. if (!bValid || !screenResources || !outputInfo || !crtcInfo || !fullMode)
  197. return;
  198. if (bFullscreen == bIsFullscreen)
  199. return;
  200. XGrabServer(dpy);
  201. if (bFullscreen)
  202. {
  203. XRRSetCrtcConfig(dpy, screenResources, outputInfo->crtc, CurrentTime,
  204. crtcInfo->x, crtcInfo->y, fullMode, crtcInfo->rotation,
  205. crtcInfo->outputs, crtcInfo->noutput);
  206. XRRSetScreenSize(dpy, win, fs_fb_width, fs_fb_height, fs_fb_width_mm, fs_fb_height_mm);
  207. bIsFullscreen = true;
  208. }
  209. else
  210. {
  211. XRRSetCrtcConfig(dpy, screenResources, outputInfo->crtc, CurrentTime,
  212. crtcInfo->x, crtcInfo->y, crtcInfo->mode, crtcInfo->rotation,
  213. crtcInfo->outputs, crtcInfo->noutput);
  214. XRRSetScreenSize(dpy, win, fb_width, fb_height, fb_width_mm, fb_height_mm);
  215. bIsFullscreen = false;
  216. }
  217. XUngrabServer(dpy);
  218. XSync(dpy, false);
  219. }
  220. void XRRConfiguration::AddResolutions(std::vector<std::string>& resos)
  221. {
  222. if (!bValid || !screenResources)
  223. return;
  224. //Get all full screen resolutions for the config dialog
  225. for (int i = 0; i < screenResources->noutput; i++)
  226. {
  227. XRROutputInfo *output_info =
  228. XRRGetOutputInfo(dpy, screenResources, screenResources->outputs[i]);
  229. if (output_info && output_info->crtc && output_info->connection == RR_Connected)
  230. {
  231. for (int j = 0; j < output_info->nmode; j++)
  232. {
  233. for (int k = 0; k < screenResources->nmode; k++)
  234. {
  235. if (output_info->modes[j] == screenResources->modes[k].id)
  236. {
  237. bool interlaced = !!(screenResources->modes[k].modeFlags & RR_Interlace);
  238. const std::string strRes =
  239. std::string(output_info->name) + ": " +
  240. std::string(screenResources->modes[k].name) + (interlaced? "i" : "");
  241. // Only add unique resolutions
  242. if (std::find(resos.begin(), resos.end(), strRes) == resos.end())
  243. {
  244. resos.push_back(strRes);
  245. }
  246. }
  247. }
  248. }
  249. }
  250. if (output_info)
  251. XRRFreeOutputInfo(output_info);
  252. }
  253. }
  254. #endif
  255. }