blob: ff12902fdd8ee7373c16558c4ce53ca976dbd12b [file] [log] [blame]
Peter Åstrand (astrand)242c5b22018-03-07 13:00:47 +01001/* 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
19#ifdef HAVE_XRANDR
20#include <X11/Xlib.h>
21#include <X11/extensions/Xrandr.h>
22#include <stdlib.h>
23#include <string.h>
24
25#include "RandrGlue.h"
26
27typedef struct _vncGlueContext {
28 Display *dpy;
29 XRRScreenResources *res;
30} vncGlueContext;
31
32static vncGlueContext randrGlueContext;
33
34void vncSetGlueContext(Display *dpy, void *res)
35{
36 randrGlueContext.dpy = dpy;
37 randrGlueContext.res = (XRRScreenResources *)res;
38}
39
40static RRMode vncRandRGetMatchingMode(XRROutputInfo *output,
41 unsigned int width, unsigned int height)
42{
43 vncGlueContext *ctx = &randrGlueContext;
44
45 /*
46 * We're not going to change which modes are preferred, but let's
47 * see if we can at least find a mode with matching dimensions.
48 */
49
50 if (output->crtc) {
51 XRRCrtcInfo *crtc;
52 unsigned int swap;
53
54 crtc = XRRGetCrtcInfo(ctx->dpy, ctx->res, output->crtc);
55 if (!crtc)
56 return None;
57
58 switch (crtc->rotation) {
59 case RR_Rotate_90:
60 case RR_Rotate_270:
61 swap = width;
62 width = height;
63 height = swap;
64 break;
65 }
66
67 XRRFreeCrtcInfo(crtc);
68 }
69
70 for (int i = 0; i < ctx->res->nmode; i++) {
71 for (int j = 0; j < output->nmode; j++) {
72 if ((output->modes[j] == ctx->res->modes[i].id) &&
73 (ctx->res->modes[i].width == width) &&
74 (ctx->res->modes[i].height == height)) {
75 return ctx->res->modes[i].id;
76 }
77 }
78 }
79
80 return None;
81}
82
83int vncGetScreenWidth(void)
84{
85 vncGlueContext *ctx = &randrGlueContext;
86 return DisplayWidth(ctx->dpy, DefaultScreen(ctx->dpy));
87}
88
89int vncGetScreenHeight(void)
90{
91 vncGlueContext *ctx = &randrGlueContext;
92 return DisplayHeight(ctx->dpy, DefaultScreen(ctx->dpy));
93}
94
95int vncRandRIsValidScreenSize(int width, int height)
96{
97 vncGlueContext *ctx = &randrGlueContext;
98 /* Assert size ranges */
99 int minwidth, minheight, maxwidth, maxheight;
100 int ret = XRRGetScreenSizeRange(ctx->dpy, DefaultRootWindow(ctx->dpy),
101 &minwidth, &minheight,
102 &maxwidth, &maxheight);
103 if (!ret) {
104 return 0;
105 }
106 if (width < minwidth || maxwidth < width) {
107 return 0;
108 }
109 if (height < minheight || maxheight < height) {
110 return 0;
111 }
112
113 return 1;
114}
115
116int vncRandRResizeScreen(int width, int height)
117{
118 vncGlueContext *ctx = &randrGlueContext;
119
120 int xwidth = DisplayWidth(ctx->dpy, DefaultScreen(ctx->dpy));
121 int xheight = DisplayHeight(ctx->dpy, DefaultScreen(ctx->dpy));
122 int xwidthmm = DisplayWidthMM(ctx->dpy, DefaultScreen(ctx->dpy));
123 int xheightmm = DisplayHeightMM(ctx->dpy, DefaultScreen(ctx->dpy));
124
125 /* Try to retain DPI when we resize */
126 XRRSetScreenSize(ctx->dpy, DefaultRootWindow(ctx->dpy), width, height,
127 xwidthmm * width / xwidth,
128 xheightmm * height / xheight);
129
130 return 1;
131}
132
133void vncRandRUpdateSetTime(void)
134{
135
136}
137
138int vncRandRHasOutputClones(void)
139{
140 vncGlueContext *ctx = &randrGlueContext;
141 for (int i = 0; i < ctx->res->ncrtc; i++) {
142 XRRCrtcInfo *crtc = XRRGetCrtcInfo(ctx->dpy, ctx->res, ctx->res->crtcs[i]);
143 if (!crtc) {
144 return 0;
145 }
146 if (crtc->noutput > 1) {
147 XRRFreeCrtcInfo (crtc);
148 return 1;
149 }
150 XRRFreeCrtcInfo (crtc);
151 }
152 return 0;
153}
154
155int vncRandRGetOutputCount(void)
156{
157 vncGlueContext *ctx = &randrGlueContext;
158 return ctx->res->noutput;
159}
160
161int vncRandRGetAvailableOutputs(void)
162{
163 vncGlueContext *ctx = &randrGlueContext;
164
165 int availableOutputs;
166 RRCrtc *usedCrtcs;
167 int numUsed;
168
169 int i, j, k;
170
171 usedCrtcs = (RRCrtc*)malloc(sizeof(RRCrtc) * ctx->res->ncrtc);
172 if (usedCrtcs == NULL)
173 return 0;
174
175 /*
176 * This gets slightly complicated because we might need to hook a CRTC
177 * up to the output, but also check that we don't try to use the same
178 * CRTC for multiple outputs.
179 */
180 availableOutputs = 0;
181 numUsed = 0;
182 for (i = 0;i < ctx->res->noutput; i++) {
183 XRROutputInfo *output;
184
185 output = XRRGetOutputInfo(ctx->dpy, ctx->res, ctx->res->outputs[i]);
186 if (!output) {
187 continue;
188 }
189
190 if (output->crtc != None)
191 availableOutputs++;
192 else {
193 for (j = 0;j < output->ncrtc;j++) {
194 XRRCrtcInfo *crtc = XRRGetCrtcInfo(ctx->dpy, ctx->res, output->crtcs[j]);
195 if (!crtc) {
196 continue;
197 }
198 if (crtc->noutput != 0) {
199 XRRFreeCrtcInfo(crtc);
200 continue;
201 }
202 XRRFreeCrtcInfo(crtc);
203
204 for (k = 0;k < numUsed;k++) {
205 if (usedCrtcs[k] == output->crtcs[j])
206 break;
207 }
208 if (k != numUsed)
209 continue;
210
211 availableOutputs++;
212
213 usedCrtcs[numUsed] = output->crtcs[j];
214 numUsed++;
215
216 break;
217 }
218 }
219 XRRFreeOutputInfo(output);
220 }
221
222 free(usedCrtcs);
223
224 return availableOutputs;
225}
226
227char *vncRandRGetOutputName(int outputIdx)
228{
229 vncGlueContext *ctx = &randrGlueContext;
230 XRROutputInfo *output = XRRGetOutputInfo(ctx->dpy, ctx->res, ctx->res->outputs[outputIdx]);
231 if (!output) {
232 return strdup("");
233 }
234 char *ret = strdup(output->name);
235 XRRFreeOutputInfo(output);
236 return ret;
237}
238
239int vncRandRIsOutputEnabled(int outputIdx)
240{
241 vncGlueContext *ctx = &randrGlueContext;
242 XRROutputInfo *output = XRRGetOutputInfo(ctx->dpy, ctx->res, ctx->res->outputs[outputIdx]);
243 if (!output) {
244 return 0;
245 }
246
247 if (output->crtc == None) {
248 XRRFreeOutputInfo(output);
249 return 0;
250 }
251 XRRCrtcInfo *crtc = XRRGetCrtcInfo(ctx->dpy, ctx->res, output->crtc);
252 XRRFreeOutputInfo(output);
253 if (!crtc) {
254 return 0;
255 }
256 if (crtc->mode == None) {
257 XRRFreeCrtcInfo(crtc);
258 return 0;
259 }
260 XRRFreeCrtcInfo(crtc);
261 return 1;
262}
263
264int vncRandRIsOutputUsable(int outputIdx)
265{
266 vncGlueContext *ctx = &randrGlueContext;
267
268 XRROutputInfo *output;
269 int i;
270
271 output = XRRGetOutputInfo(ctx->dpy, ctx->res, ctx->res->outputs[outputIdx]);
272 if (!output) {
273 return 0;
274 }
275
276 if (output->crtc != None) {
277 XRRFreeOutputInfo(output);
278 return 1;
279 }
280
281 /* Any unused CRTCs? */
282 for (i = 0;i < output->ncrtc;i++) {
283 XRRCrtcInfo *crtc = XRRGetCrtcInfo(ctx->dpy, ctx->res, output->crtcs[i]);
284 if (crtc->noutput == 0) {
285 XRRFreeOutputInfo(output);
286 XRRFreeCrtcInfo(crtc);
287 return 1;
288 }
289 XRRFreeCrtcInfo(crtc);
290 }
291
292 XRRFreeOutputInfo(output);
293 return 0;
294}
295
296int vncRandRIsOutputConnected(int outputIdx)
297{
298 vncGlueContext *ctx = &randrGlueContext;
299 XRROutputInfo *output;
300
301 output = XRRGetOutputInfo(ctx->dpy, ctx->res, ctx->res->outputs[outputIdx]);
302 if (!output) {
303 return 0;
304 }
305
306 int ret = (output->connection == RR_Connected);
307 XRRFreeOutputInfo(output);
308 return ret;
309}
310
311int vncRandRCheckOutputMode(int outputIdx, int width, int height)
312{
313 vncGlueContext *ctx = &randrGlueContext;
314 XRROutputInfo *output;
315 RRMode mode;
316
317 output = XRRGetOutputInfo(ctx->dpy, ctx->res, ctx->res->outputs[outputIdx]);
318 if (!output)
319 return 0;
320
321 /* Make sure we have the mode we want */
322 mode = vncRandRGetMatchingMode(output, width, height);
323 XRRFreeOutputInfo(output);
324
325 if (mode == None)
326 return 0;
327
328 return 1;
329}
330
331int vncRandRDisableOutput(int outputIdx)
332{
333 vncGlueContext *ctx = &randrGlueContext;
334 RRCrtc crtcid;
335 int i;
336 int move = 0;
337
338 XRROutputInfo *output = XRRGetOutputInfo(ctx->dpy, ctx->res, ctx->res->outputs[outputIdx]);
339 if (!output) {
340 return 0;
341 }
342
343 crtcid = output->crtc;
344 if (crtcid == 0) {
345 XRRFreeOutputInfo(output);
346 return 1;
347 }
348
349 XRRCrtcInfo *crtc = XRRGetCrtcInfo(ctx->dpy, ctx->res, output->crtc);
350 XRRFreeOutputInfo(output);
351 if (!crtc) {
352 return 0;
353 }
354
355 /* Remove this output from the CRTC configuration */
356 for (i = 0; i < crtc->noutput; i++) {
357 if (ctx->res->outputs[outputIdx] == crtc->outputs[i]) {
358 crtc->noutput -= 1;
359 move = 1;
360 }
361 if (move && i < crtc->noutput) {
362 crtc->outputs[i] = crtc->outputs[i+1];
363 }
364 }
365 if (crtc->noutput == 0) {
366 crtc->mode = None;
367 crtc->outputs = NULL;
368 }
369
370 int ret = XRRSetCrtcConfig(ctx->dpy,
371 ctx->res,
372 crtcid,
373 CurrentTime,
374 crtc->x, crtc->y,
375 crtc->mode, crtc->rotation,
376 crtc->outputs, crtc->noutput);
377
378 XRRFreeCrtcInfo(crtc);
379
380 return (ret == RRSetConfigSuccess);
381}
382
383unsigned int vncRandRGetOutputId(int outputIdx)
384{
385 vncGlueContext *ctx = &randrGlueContext;
386 return ctx->res->outputs[outputIdx];
387}
388
389int vncRandRGetOutputDimensions(int outputIdx,
390 int *x, int *y, int *width, int *height)
391{
392 vncGlueContext *ctx = &randrGlueContext;
393 int swap;
394 *x = *y = *width = *height = 0;
395
396 XRROutputInfo *output = XRRGetOutputInfo(ctx->dpy, ctx->res, ctx->res->outputs[outputIdx]);
397 if (!output) {
398 return 1;
399 }
400
401 if (!output->crtc) {
402 XRRFreeOutputInfo(output);
403 return 1;
404 }
405
406 XRRCrtcInfo *crtc = XRRGetCrtcInfo(ctx->dpy, ctx->res, output->crtc);
407 XRRFreeOutputInfo(output);
408 if (!crtc) {
409 return 1;
410 }
411 if (crtc->mode == None) {
412 XRRFreeCrtcInfo(crtc);
413 return 1;
414 }
415
416 *x = crtc->x;
417 *y = crtc->y;
418 for (int m = 0; m < ctx->res->nmode; m++) {
419 if (crtc->mode == ctx->res->modes[m].id) {
420 *width = ctx->res->modes[m].width;
421 *height = ctx->res->modes[m].height;
422 }
423 }
424
425 switch (crtc->rotation) {
426 case RR_Rotate_90:
427 case RR_Rotate_270:
428 swap = *width;
429 *width = *height;
430 *height = swap;
431 break;
432 }
433
434 XRRFreeCrtcInfo(crtc);
435 return 0;
436}
437
438int vncRandRReconfigureOutput(int outputIdx, int x, int y,
439 int width, int height)
440{
441 vncGlueContext *ctx = &randrGlueContext;
442
443 XRROutputInfo *output;
444 RRCrtc crtcid;
445 RRMode mode;
446 XRRCrtcInfo *crtc = NULL;
447
448 int i, ret;
449
450 output = XRRGetOutputInfo(ctx->dpy, ctx->res, ctx->res->outputs[outputIdx]);
451 if (!output) {
452 return 0;
453 }
454
455 crtcid = output->crtc;
456
457 /* Need a CRTC? */
458 if (crtcid == None) {
459 for (i = 0;i < output->ncrtc;i++) {
460 crtc = XRRGetCrtcInfo(ctx->dpy, ctx->res, output->crtcs[i]);
461 if (!crtc) {
462 continue;
463 }
464
465 if (crtc->noutput != 0) {
466 XRRFreeCrtcInfo(crtc);
467 continue;
468 }
469
470 crtcid = output->crtcs[i];
Peter Åstrand (astrand)86e4eb62018-05-08 13:54:22 +0200471 crtc->rotation = RR_Rotate_0;
Peter Åstrand (astrand)242c5b22018-03-07 13:00:47 +0100472 break;
473 }
474 } else {
475 crtc = XRRGetCrtcInfo(ctx->dpy, ctx->res, crtcid);
476 }
477
478 /* Couldn't find one... */
479 if (crtc == NULL) {
480 XRRFreeOutputInfo(output);
481 return 0;
482 }
483
484 /* Make sure we have the mode we want */
485 mode = vncRandRGetMatchingMode(output, width, height);
486 if (mode == None) {
487 XRRFreeCrtcInfo(crtc);
488 XRRFreeOutputInfo(output);
489 return 0;
490 }
491
492 /* Reconfigure new mode and position */
493 ret = XRRSetCrtcConfig (ctx->dpy, ctx->res, crtcid, CurrentTime, x, y,
494 mode, crtc->rotation, ctx->res->outputs+outputIdx, 1);
495
496 XRRFreeCrtcInfo(crtc);
497 XRRFreeOutputInfo(output);
498
499 return (ret == RRSetConfigSuccess);
500}
501
502int vncRandRCanCreateOutputs(int extraOutputs)
503{
504 return 0;
505}
506
507int vncRandRCreateOutputs(int extraOutputs)
508{
509 return 0;
510}
511
512#endif /* HAVE_XRANDR */