You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

RandrGlue.c 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518
  1. /* Copyright 2018 Peter Astrand <astrand@cendio.se> for Cendio AB
  2. *
  3. * This is free software; you can redistribute it and/or modify
  4. * it under the terms of the GNU General Public License as published by
  5. * the Free Software Foundation; either version 2 of the License, or
  6. * (at your option) any later version.
  7. *
  8. * This software is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU General Public License
  14. * along with this software; if not, write to the Free Software
  15. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
  16. * USA.
  17. */
  18. #ifdef HAVE_CONFIG_H
  19. #include <config.h>
  20. #endif
  21. #ifdef HAVE_XRANDR
  22. #include <X11/Xlib.h>
  23. #include <X11/extensions/Xrandr.h>
  24. #include <stdlib.h>
  25. #include <string.h>
  26. #include "RandrGlue.h"
  27. typedef struct _vncGlueContext {
  28. Display *dpy;
  29. XRRScreenResources *res;
  30. } vncGlueContext;
  31. static vncGlueContext randrGlueContext;
  32. void vncSetGlueContext(Display *dpy, void *res)
  33. {
  34. randrGlueContext.dpy = dpy;
  35. randrGlueContext.res = (XRRScreenResources *)res;
  36. }
  37. static RRMode vncRandRGetMatchingMode(XRROutputInfo *output,
  38. unsigned int width, unsigned int height)
  39. {
  40. vncGlueContext *ctx = &randrGlueContext;
  41. /*
  42. * We're not going to change which modes are preferred, but let's
  43. * see if we can at least find a mode with matching dimensions.
  44. */
  45. if (output->crtc) {
  46. XRRCrtcInfo *crtc;
  47. unsigned int swap;
  48. crtc = XRRGetCrtcInfo(ctx->dpy, ctx->res, output->crtc);
  49. if (!crtc)
  50. return None;
  51. switch (crtc->rotation) {
  52. case RR_Rotate_90:
  53. case RR_Rotate_270:
  54. swap = width;
  55. width = height;
  56. height = swap;
  57. break;
  58. }
  59. XRRFreeCrtcInfo(crtc);
  60. }
  61. for (int i = 0; i < ctx->res->nmode; i++) {
  62. for (int j = 0; j < output->nmode; j++) {
  63. if ((output->modes[j] == ctx->res->modes[i].id) &&
  64. (ctx->res->modes[i].width == width) &&
  65. (ctx->res->modes[i].height == height)) {
  66. return ctx->res->modes[i].id;
  67. }
  68. }
  69. }
  70. return None;
  71. }
  72. int vncGetScreenWidth(void)
  73. {
  74. vncGlueContext *ctx = &randrGlueContext;
  75. return DisplayWidth(ctx->dpy, DefaultScreen(ctx->dpy));
  76. }
  77. int vncGetScreenHeight(void)
  78. {
  79. vncGlueContext *ctx = &randrGlueContext;
  80. return DisplayHeight(ctx->dpy, DefaultScreen(ctx->dpy));
  81. }
  82. int vncRandRIsValidScreenSize(int width, int height)
  83. {
  84. vncGlueContext *ctx = &randrGlueContext;
  85. /* Assert size ranges */
  86. int minwidth, minheight, maxwidth, maxheight;
  87. int ret = XRRGetScreenSizeRange(ctx->dpy, DefaultRootWindow(ctx->dpy),
  88. &minwidth, &minheight,
  89. &maxwidth, &maxheight);
  90. if (!ret) {
  91. return 0;
  92. }
  93. if (width < minwidth || maxwidth < width) {
  94. return 0;
  95. }
  96. if (height < minheight || maxheight < height) {
  97. return 0;
  98. }
  99. return 1;
  100. }
  101. int vncRandRResizeScreen(int width, int height)
  102. {
  103. vncGlueContext *ctx = &randrGlueContext;
  104. int mwidth, mheight;
  105. // Always calculate a DPI of 96.
  106. // It's what mutter does, so good enough for us.
  107. mwidth = width * 254 / 96 / 10;
  108. mheight = height * 254 / 96 / 10;
  109. XRRSetScreenSize(ctx->dpy, DefaultRootWindow(ctx->dpy),
  110. width, height, mwidth, mheight);
  111. return 1;
  112. }
  113. void vncRandRUpdateSetTime(void)
  114. {
  115. }
  116. int vncRandRHasOutputClones(void)
  117. {
  118. vncGlueContext *ctx = &randrGlueContext;
  119. for (int i = 0; i < ctx->res->ncrtc; i++) {
  120. XRRCrtcInfo *crtc = XRRGetCrtcInfo(ctx->dpy, ctx->res, ctx->res->crtcs[i]);
  121. if (!crtc) {
  122. return 0;
  123. }
  124. if (crtc->noutput > 1) {
  125. XRRFreeCrtcInfo (crtc);
  126. return 1;
  127. }
  128. XRRFreeCrtcInfo (crtc);
  129. }
  130. return 0;
  131. }
  132. int vncRandRGetOutputCount(void)
  133. {
  134. vncGlueContext *ctx = &randrGlueContext;
  135. return ctx->res->noutput;
  136. }
  137. int vncRandRGetAvailableOutputs(void)
  138. {
  139. vncGlueContext *ctx = &randrGlueContext;
  140. int availableOutputs;
  141. RRCrtc *usedCrtcs;
  142. int numUsed;
  143. int i, j, k;
  144. usedCrtcs = (RRCrtc*)malloc(sizeof(RRCrtc) * ctx->res->ncrtc);
  145. if (usedCrtcs == NULL)
  146. return 0;
  147. /*
  148. * This gets slightly complicated because we might need to hook a CRTC
  149. * up to the output, but also check that we don't try to use the same
  150. * CRTC for multiple outputs.
  151. */
  152. availableOutputs = 0;
  153. numUsed = 0;
  154. for (i = 0;i < ctx->res->noutput; i++) {
  155. XRROutputInfo *output;
  156. output = XRRGetOutputInfo(ctx->dpy, ctx->res, ctx->res->outputs[i]);
  157. if (!output) {
  158. continue;
  159. }
  160. if (output->crtc != None)
  161. availableOutputs++;
  162. else {
  163. for (j = 0;j < output->ncrtc;j++) {
  164. XRRCrtcInfo *crtc = XRRGetCrtcInfo(ctx->dpy, ctx->res, output->crtcs[j]);
  165. if (!crtc) {
  166. continue;
  167. }
  168. if (crtc->noutput != 0) {
  169. XRRFreeCrtcInfo(crtc);
  170. continue;
  171. }
  172. XRRFreeCrtcInfo(crtc);
  173. for (k = 0;k < numUsed;k++) {
  174. if (usedCrtcs[k] == output->crtcs[j])
  175. break;
  176. }
  177. if (k != numUsed)
  178. continue;
  179. availableOutputs++;
  180. usedCrtcs[numUsed] = output->crtcs[j];
  181. numUsed++;
  182. break;
  183. }
  184. }
  185. XRRFreeOutputInfo(output);
  186. }
  187. free(usedCrtcs);
  188. return availableOutputs;
  189. }
  190. char *vncRandRGetOutputName(int outputIdx)
  191. {
  192. vncGlueContext *ctx = &randrGlueContext;
  193. XRROutputInfo *output = XRRGetOutputInfo(ctx->dpy, ctx->res, ctx->res->outputs[outputIdx]);
  194. if (!output) {
  195. return strdup("");
  196. }
  197. char *ret = strdup(output->name);
  198. XRRFreeOutputInfo(output);
  199. return ret;
  200. }
  201. int vncRandRIsOutputEnabled(int outputIdx)
  202. {
  203. vncGlueContext *ctx = &randrGlueContext;
  204. XRROutputInfo *output = XRRGetOutputInfo(ctx->dpy, ctx->res, ctx->res->outputs[outputIdx]);
  205. if (!output) {
  206. return 0;
  207. }
  208. if (output->crtc == None) {
  209. XRRFreeOutputInfo(output);
  210. return 0;
  211. }
  212. XRRCrtcInfo *crtc = XRRGetCrtcInfo(ctx->dpy, ctx->res, output->crtc);
  213. XRRFreeOutputInfo(output);
  214. if (!crtc) {
  215. return 0;
  216. }
  217. if (crtc->mode == None) {
  218. XRRFreeCrtcInfo(crtc);
  219. return 0;
  220. }
  221. XRRFreeCrtcInfo(crtc);
  222. return 1;
  223. }
  224. int vncRandRIsOutputUsable(int outputIdx)
  225. {
  226. vncGlueContext *ctx = &randrGlueContext;
  227. XRROutputInfo *output;
  228. int i;
  229. output = XRRGetOutputInfo(ctx->dpy, ctx->res, ctx->res->outputs[outputIdx]);
  230. if (!output) {
  231. return 0;
  232. }
  233. if (output->crtc != None) {
  234. XRRFreeOutputInfo(output);
  235. return 1;
  236. }
  237. /* Any unused CRTCs? */
  238. for (i = 0;i < output->ncrtc;i++) {
  239. XRRCrtcInfo *crtc = XRRGetCrtcInfo(ctx->dpy, ctx->res, output->crtcs[i]);
  240. if (crtc->noutput == 0) {
  241. XRRFreeOutputInfo(output);
  242. XRRFreeCrtcInfo(crtc);
  243. return 1;
  244. }
  245. XRRFreeCrtcInfo(crtc);
  246. }
  247. XRRFreeOutputInfo(output);
  248. return 0;
  249. }
  250. int vncRandRIsOutputConnected(int outputIdx)
  251. {
  252. vncGlueContext *ctx = &randrGlueContext;
  253. XRROutputInfo *output;
  254. output = XRRGetOutputInfo(ctx->dpy, ctx->res, ctx->res->outputs[outputIdx]);
  255. if (!output) {
  256. return 0;
  257. }
  258. int ret = (output->connection == RR_Connected);
  259. XRRFreeOutputInfo(output);
  260. return ret;
  261. }
  262. int vncRandRCheckOutputMode(int outputIdx, int width, int height)
  263. {
  264. vncGlueContext *ctx = &randrGlueContext;
  265. XRROutputInfo *output;
  266. RRMode mode;
  267. output = XRRGetOutputInfo(ctx->dpy, ctx->res, ctx->res->outputs[outputIdx]);
  268. if (!output)
  269. return 0;
  270. /* Make sure we have the mode we want */
  271. mode = vncRandRGetMatchingMode(output, width, height);
  272. XRRFreeOutputInfo(output);
  273. if (mode == None)
  274. return 0;
  275. return 1;
  276. }
  277. int vncRandRDisableOutput(int outputIdx)
  278. {
  279. vncGlueContext *ctx = &randrGlueContext;
  280. RRCrtc crtcid;
  281. int i;
  282. int move = 0;
  283. XRROutputInfo *output = XRRGetOutputInfo(ctx->dpy, ctx->res, ctx->res->outputs[outputIdx]);
  284. if (!output) {
  285. return 0;
  286. }
  287. crtcid = output->crtc;
  288. if (crtcid == 0) {
  289. XRRFreeOutputInfo(output);
  290. return 1;
  291. }
  292. XRRCrtcInfo *crtc = XRRGetCrtcInfo(ctx->dpy, ctx->res, output->crtc);
  293. XRRFreeOutputInfo(output);
  294. if (!crtc) {
  295. return 0;
  296. }
  297. /* Remove this output from the CRTC configuration */
  298. for (i = 0; i < crtc->noutput; i++) {
  299. if (ctx->res->outputs[outputIdx] == crtc->outputs[i]) {
  300. crtc->noutput -= 1;
  301. move = 1;
  302. }
  303. if (move && i < crtc->noutput) {
  304. crtc->outputs[i] = crtc->outputs[i+1];
  305. }
  306. }
  307. if (crtc->noutput == 0) {
  308. crtc->mode = None;
  309. crtc->outputs = NULL;
  310. }
  311. int ret = XRRSetCrtcConfig(ctx->dpy,
  312. ctx->res,
  313. crtcid,
  314. CurrentTime,
  315. crtc->x, crtc->y,
  316. crtc->mode, crtc->rotation,
  317. crtc->outputs, crtc->noutput);
  318. XRRFreeCrtcInfo(crtc);
  319. return (ret == RRSetConfigSuccess);
  320. }
  321. unsigned int vncRandRGetOutputId(int outputIdx)
  322. {
  323. vncGlueContext *ctx = &randrGlueContext;
  324. return ctx->res->outputs[outputIdx];
  325. }
  326. int vncRandRGetOutputDimensions(int outputIdx,
  327. int *x, int *y, int *width, int *height)
  328. {
  329. vncGlueContext *ctx = &randrGlueContext;
  330. int swap;
  331. *x = *y = *width = *height = 0;
  332. XRROutputInfo *output = XRRGetOutputInfo(ctx->dpy, ctx->res, ctx->res->outputs[outputIdx]);
  333. if (!output) {
  334. return 1;
  335. }
  336. if (!output->crtc) {
  337. XRRFreeOutputInfo(output);
  338. return 1;
  339. }
  340. XRRCrtcInfo *crtc = XRRGetCrtcInfo(ctx->dpy, ctx->res, output->crtc);
  341. XRRFreeOutputInfo(output);
  342. if (!crtc) {
  343. return 1;
  344. }
  345. if (crtc->mode == None) {
  346. XRRFreeCrtcInfo(crtc);
  347. return 1;
  348. }
  349. *x = crtc->x;
  350. *y = crtc->y;
  351. for (int m = 0; m < ctx->res->nmode; m++) {
  352. if (crtc->mode == ctx->res->modes[m].id) {
  353. *width = ctx->res->modes[m].width;
  354. *height = ctx->res->modes[m].height;
  355. }
  356. }
  357. switch (crtc->rotation) {
  358. case RR_Rotate_90:
  359. case RR_Rotate_270:
  360. swap = *width;
  361. *width = *height;
  362. *height = swap;
  363. break;
  364. }
  365. XRRFreeCrtcInfo(crtc);
  366. return 0;
  367. }
  368. int vncRandRReconfigureOutput(int outputIdx, int x, int y,
  369. int width, int height)
  370. {
  371. vncGlueContext *ctx = &randrGlueContext;
  372. XRROutputInfo *output;
  373. RRCrtc crtcid;
  374. RRMode mode;
  375. XRRCrtcInfo *crtc = NULL;
  376. int i, ret;
  377. output = XRRGetOutputInfo(ctx->dpy, ctx->res, ctx->res->outputs[outputIdx]);
  378. if (!output) {
  379. return 0;
  380. }
  381. crtcid = output->crtc;
  382. /* Need a CRTC? */
  383. if (crtcid == None) {
  384. for (i = 0;i < output->ncrtc;i++) {
  385. crtc = XRRGetCrtcInfo(ctx->dpy, ctx->res, output->crtcs[i]);
  386. if (!crtc) {
  387. continue;
  388. }
  389. if (crtc->noutput != 0) {
  390. XRRFreeCrtcInfo(crtc);
  391. continue;
  392. }
  393. crtcid = output->crtcs[i];
  394. crtc->rotation = RR_Rotate_0;
  395. break;
  396. }
  397. } else {
  398. crtc = XRRGetCrtcInfo(ctx->dpy, ctx->res, crtcid);
  399. }
  400. /* Couldn't find one... */
  401. if (crtc == NULL) {
  402. XRRFreeOutputInfo(output);
  403. return 0;
  404. }
  405. /* Make sure we have the mode we want */
  406. mode = vncRandRGetMatchingMode(output, width, height);
  407. if (mode == None) {
  408. XRRFreeCrtcInfo(crtc);
  409. XRRFreeOutputInfo(output);
  410. return 0;
  411. }
  412. /* Reconfigure new mode and position */
  413. ret = XRRSetCrtcConfig (ctx->dpy, ctx->res, crtcid, CurrentTime, x, y,
  414. mode, crtc->rotation, ctx->res->outputs+outputIdx, 1);
  415. XRRFreeCrtcInfo(crtc);
  416. XRRFreeOutputInfo(output);
  417. return (ret == RRSetConfigSuccess);
  418. }
  419. int vncRandRCanCreateOutputs(int extraOutputs)
  420. {
  421. (void)extraOutputs;
  422. return 0;
  423. }
  424. int vncRandRCreateOutputs(int extraOutputs)
  425. {
  426. (void)extraOutputs;
  427. return 0;
  428. }
  429. #endif /* HAVE_XRANDR */