xwayland-output.c 15.2 KB
Newer Older
Kristian Høgsberg's avatar
Kristian Høgsberg committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/*
 * Copyright © 2011-2014 Intel Corporation
 *
 * Permission to use, copy, modify, distribute, and sell this software
 * and its documentation for any purpose is hereby granted without
 * fee, provided that the above copyright notice appear in all copies
 * and that both that copyright notice and this permission notice
 * appear in supporting documentation, and that the name of the
 * copyright holders not be used in advertising or publicity
 * pertaining to distribution of the software without specific,
 * written prior permission.  The copyright holders make no
 * representations about the suitability of this software for any
 * purpose.  It is provided "as is" without express or implied
 * warranty.
 *
 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 * SOFTWARE.
 */

#ifdef HAVE_DIX_CONFIG_H
#include <dix-config.h>
#endif

#include "xwayland.h"
#include <randrstr.h>

33
#define DEFAULT_DPI 96
34
35
36
37
38
39
#define ALL_ROTATIONS (RR_Rotate_0   | \
                       RR_Rotate_90  | \
                       RR_Rotate_180 | \
                       RR_Rotate_270 | \
                       RR_Reflect_X  | \
                       RR_Reflect_Y)
40

41
42
static void xwl_output_get_xdg_output(struct xwl_output *xwl_output);

Kristian Høgsberg's avatar
Kristian Høgsberg committed
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
static Rotation
wl_transform_to_xrandr(enum wl_output_transform transform)
{
    switch (transform) {
    default:
    case WL_OUTPUT_TRANSFORM_NORMAL:
        return RR_Rotate_0;
    case WL_OUTPUT_TRANSFORM_90:
        return RR_Rotate_90;
    case WL_OUTPUT_TRANSFORM_180:
        return RR_Rotate_180;
    case WL_OUTPUT_TRANSFORM_270:
        return RR_Rotate_270;
    case WL_OUTPUT_TRANSFORM_FLIPPED:
        return RR_Reflect_X | RR_Rotate_0;
    case WL_OUTPUT_TRANSFORM_FLIPPED_90:
        return RR_Reflect_X | RR_Rotate_90;
    case WL_OUTPUT_TRANSFORM_FLIPPED_180:
        return RR_Reflect_X | RR_Rotate_180;
    case WL_OUTPUT_TRANSFORM_FLIPPED_270:
        return RR_Reflect_X | RR_Rotate_270;
    }
}

static int
wl_subpixel_to_xrandr(int subpixel)
{
    switch (subpixel) {
    default:
    case WL_OUTPUT_SUBPIXEL_UNKNOWN:
        return SubPixelUnknown;
    case WL_OUTPUT_SUBPIXEL_NONE:
        return SubPixelNone;
    case WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB:
        return SubPixelHorizontalRGB;
    case WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR:
        return SubPixelHorizontalBGR;
    case WL_OUTPUT_SUBPIXEL_VERTICAL_RGB:
        return SubPixelVerticalRGB;
    case WL_OUTPUT_SUBPIXEL_VERTICAL_BGR:
        return SubPixelVerticalBGR;
    }
}

static void
output_handle_geometry(void *data, struct wl_output *wl_output, int x, int y,
                       int physical_width, int physical_height, int subpixel,
                       const char *make, const char *model, int transform)
{
    struct xwl_output *xwl_output = data;

    RROutputSetPhysicalSize(xwl_output->randr_output,
                            physical_width, physical_height);
    RROutputSetSubpixelOrder(xwl_output->randr_output,
                             wl_subpixel_to_xrandr(subpixel));

99
100
101
102
103
    /* Apply the change from wl_output only if xdg-output is not supported */
    if (!xwl_output->xdg_output) {
        xwl_output->x = x;
        xwl_output->y = y;
    }
Kristian Høgsberg's avatar
Kristian Høgsberg committed
104
105
106
107
108
109
110
111
112
113
114
115
    xwl_output->rotation = wl_transform_to_xrandr(transform);
}

static void
output_handle_mode(void *data, struct wl_output *wl_output, uint32_t flags,
                   int width, int height, int refresh)
{
    struct xwl_output *xwl_output = data;

    if (!(flags & WL_OUTPUT_MODE_CURRENT))
        return;

116
117
118
119
120
    /* Apply the change from wl_output only if xdg-output is not supported */
    if (!xwl_output->xdg_output) {
        xwl_output->width = width;
        xwl_output->height = height;
    }
121
    xwl_output->refresh = refresh;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
122
123
}

124
125
static inline void
output_get_new_size(struct xwl_output *xwl_output,
126
                    Bool need_rotate,
127
                    int *height, int *width)
128
{
129
130
    int output_width, output_height;

131
    if (!need_rotate || (xwl_output->rotation & (RR_Rotate_0 | RR_Rotate_180))) {
132
133
134
135
136
137
138
139
140
        output_width = xwl_output->width;
        output_height = xwl_output->height;
    } else {
        output_width = xwl_output->height;
        output_height = xwl_output->width;
    }

    if (*width < xwl_output->x + output_width)
        *width = xwl_output->x + output_width;
141

142
143
    if (*height < xwl_output->y + output_height)
        *height = xwl_output->y + output_height;
144
145
}

146
147
148
/* Approximate some kind of mmpd (m.m. per dot) of the screen given the outputs
 * associated with it.
 *
Roman Gilg's avatar
Roman Gilg committed
149
150
151
 * It either calculates the mean mmpd of all the outputs or, if no reasonable
 * value could be calculated, defaults to the mmpd of a screen with a DPI value
 * of DEFAULT_DPI.
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
 */
static double
approximate_mmpd(struct xwl_screen *xwl_screen)
{
    struct xwl_output *it;
    int total_width_mm = 0;
    int total_width = 0;

    xorg_list_for_each_entry(it, &xwl_screen->output_list, link) {
        if (it->randr_output->mmWidth == 0)
            continue;

        total_width_mm += it->randr_output->mmWidth;
        total_width += it->width;
    }

    if (total_width_mm != 0)
        return (double)total_width_mm / total_width;
    else
        return 25.4 / DEFAULT_DPI;
}

Kristian Høgsberg's avatar
Kristian Høgsberg committed
174
static void
175
update_screen_size(struct xwl_output *xwl_output, int width, int height)
Kristian Høgsberg's avatar
Kristian Høgsberg committed
176
177
{
    struct xwl_screen *xwl_screen = xwl_output->xwl_screen;
178
    double mmpd;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
179

180
181
182
    width = width * xwl_screen->global_output_scale;
    height = height * xwl_screen->global_output_scale;

183
184
    if (xwl_screen->root_clip_mode == ROOT_CLIP_FULL)
        SetRootClip(xwl_screen->screen, ROOT_CLIP_NONE);
185

Kristian Høgsberg's avatar
Kristian Høgsberg committed
186
187
    xwl_screen->width = width;
    xwl_screen->height = height;
188
189
190
    xwl_screen->screen->width = width;
    xwl_screen->screen->height = height;

191
192
193
194
195
196
197
198
199
    if (xwl_output->width == width && xwl_output->height == height) {
        xwl_screen->screen->mmWidth = xwl_output->randr_output->mmWidth;
        xwl_screen->screen->mmHeight = xwl_output->randr_output->mmHeight;
    } else {
        mmpd = approximate_mmpd(xwl_screen);
        xwl_screen->screen->mmWidth = width * mmpd;
        xwl_screen->screen->mmHeight = height * mmpd;
    }

200
201
    SetRootClip(xwl_screen->screen, xwl_screen->root_clip_mode);

202
    if (xwl_screen->screen->root) {
203
204
        BoxRec box = { 0, 0, width, height };

205
206
        xwl_screen->screen->root->drawable.width = width;
        xwl_screen->screen->root->drawable.height = height;
207
        RegionReset(&xwl_screen->screen->root->winSize, &box);
208
209
210
211
        RRScreenSizeNotify(xwl_screen->screen);
    }

    update_desktop_dimensions();
Kristian Høgsberg's avatar
Kristian Høgsberg committed
212
213
}

214
static void
215
apply_output_change(struct xwl_output *xwl_output)
216
217
{
    struct xwl_screen *xwl_screen = xwl_output->xwl_screen;
218
    struct xwl_output *it;
219
    int mode_width, mode_height;
220
    int width = 0, height = 0, has_this_output = 0;
221
    RRModePtr randr_mode;
222
    Bool need_rotate;
223
    int32_t scale = xwl_screen->global_output_scale;
224
225
226
227
228
229

    /* Clear out the "done" received flags */
    xwl_output->wl_output_done = FALSE;
    xwl_output->xdg_output_done = FALSE;

    /* xdg-output sends output size in compositor space. so already rotated */
230
    need_rotate = (xwl_output->xdg_output == NULL);
231

232
233
    /* We need to rotate back the logical size for the mode */
    if (need_rotate || xwl_output->rotation & (RR_Rotate_0 | RR_Rotate_180)) {
234
235
        mode_width = xwl_output->width * scale;
        mode_height = xwl_output->height * scale;
236
237
238
239
240
241
    } else {
        mode_width = xwl_output->height;
        mode_height = xwl_output->width;
    }

    randr_mode = xwayland_cvt(mode_width, mode_height,
242
243
244
                              xwl_output->refresh / 1000.0, 0, 0);
    RROutputSetModes(xwl_output->randr_output, &randr_mode, 1, 1);
    RRCrtcNotify(xwl_output->randr_crtc, randr_mode,
245
                 xwl_output->x * scale, xwl_output->y * scale,
246
                 xwl_output->rotation, NULL, 1, &xwl_output->randr_output);
247
248
249
250
251
252
253
254
255

    xorg_list_for_each_entry(it, &xwl_screen->output_list, link) {
        /* output done event is sent even when some property
         * of output is changed. That means that we may already
         * have this output. If it is true, we must not add it
         * into the output_list otherwise we'll corrupt it */
        if (it == xwl_output)
            has_this_output = 1;

256
        output_get_new_size(it, need_rotate, &height, &width);
257
258
259
260
261
262
    }

    if (!has_this_output) {
        xorg_list_append(&xwl_output->link, &xwl_screen->output_list);

        /* we did not check this output for new screen size, do it now */
263
        output_get_new_size(xwl_output, need_rotate, &height, &width);
264
265
266
267
268
269
270

	--xwl_screen->expecting_event;
    }

    update_screen_size(xwl_output, width, height);
}

271
272
273
274
275
276
277
278
279
280
281
282
283
static void
output_handle_done(void *data, struct wl_output *wl_output)
{
    struct xwl_output *xwl_output = data;

    xwl_output->wl_output_done = TRUE;
    /* Apply the changes from wl_output only if both "done" events are received,
     * or if xdg-output is not supported.
     */
    if (xwl_output->xdg_output_done || !xwl_output->xdg_output)
        apply_output_change(xwl_output);
}

Roman Gilg's avatar
Roman Gilg committed
284
285
286
287
288
289
static void
output_calc_global_scale( struct xwl_screen *xwl_screen)
{
    struct xwl_output *it;
    int32_t scale = 1;

290
291
    if (!xwl_screen->multidpi)
        return;
Roman Gilg's avatar
Roman Gilg committed
292
    if (!xwl_screen->xdg_output_manager)
293
        // multi dpi scaling is only possible when logical sizes are available
Roman Gilg's avatar
Roman Gilg committed
294
295
296
297
298
299
300
301
302
        return;

    xorg_list_for_each_entry(it, &xwl_screen->output_list, link) {
        if (it->scale > scale) {
            scale = it->scale;
        }
    }
    xwl_screen->global_output_scale = scale;

303
304
305
306
    // change randr resolutions and positions
    xorg_list_for_each_entry(it, &xwl_screen->output_list, link) {
        apply_output_change(it);
    }
Roman Gilg's avatar
Roman Gilg committed
307
308
}

Kristian Høgsberg's avatar
Kristian Høgsberg committed
309
310
311
static void
output_handle_scale(void *data, struct wl_output *wl_output, int32_t factor)
{
Roman Gilg's avatar
Roman Gilg committed
312
313
314
315
316
317
    struct xwl_output *xwl_output = data;

    xwl_output->scale = factor;

    // recalculate global scale
    output_calc_global_scale(xwl_output->xwl_screen);
Kristian Høgsberg's avatar
Kristian Høgsberg committed
318
319
320
321
322
323
324
325
326
}

static const struct wl_output_listener output_listener = {
    output_handle_geometry,
    output_handle_mode,
    output_handle_done,
    output_handle_scale
};

327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
static void
xdg_output_handle_logical_position(void *data, struct zxdg_output_v1 *xdg_output,
                                   int32_t x, int32_t y)
{
    struct xwl_output *xwl_output = data;

    xwl_output->x = x;
    xwl_output->y = y;
}

static void
xdg_output_handle_logical_size(void *data, struct zxdg_output_v1 *xdg_output,
                               int32_t width, int32_t height)
{
    struct xwl_output *xwl_output = data;

    xwl_output->width = width;
    xwl_output->height = height;
}

static void
xdg_output_handle_done(void *data, struct zxdg_output_v1 *xdg_output)
{
    struct xwl_output *xwl_output = data;

    xwl_output->xdg_output_done = TRUE;
    if (xwl_output->wl_output_done)
        apply_output_change(xwl_output);
}

static const struct zxdg_output_v1_listener xdg_output_listener = {
    xdg_output_handle_logical_position,
    xdg_output_handle_logical_size,
    xdg_output_handle_done,
};

Kristian Høgsberg's avatar
Kristian Høgsberg committed
363
364
365
366
367
368
369
struct xwl_output *
xwl_output_create(struct xwl_screen *xwl_screen, uint32_t id)
{
    struct xwl_output *xwl_output;
    static int serial;
    char name[256];

370
    xwl_output = calloc(1, sizeof *xwl_output);
Kristian Høgsberg's avatar
Kristian Høgsberg committed
371
    if (xwl_output == NULL) {
372
        ErrorF("%s ENOMEM\n", __func__);
Kristian Høgsberg's avatar
Kristian Høgsberg committed
373
374
375
376
377
        return NULL;
    }

    xwl_output->output = wl_registry_bind(xwl_screen->registry, id,
                                          &wl_output_interface, 2);
378
379
380
381
382
    if (!xwl_output->output) {
        ErrorF("Failed binding wl_output\n");
        goto err;
    }

383
    xwl_output->server_output_id = id;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
384
385
    wl_output_add_listener(xwl_output->output, &output_listener, xwl_output);

386
    snprintf(name, sizeof name, "XWAYLAND%d", serial++);
Kristian Høgsberg's avatar
Kristian Høgsberg committed
387
388
389

    xwl_output->xwl_screen = xwl_screen;
    xwl_output->randr_crtc = RRCrtcCreate(xwl_screen->screen, xwl_output);
390
391
392
393
    if (!xwl_output->randr_crtc) {
        ErrorF("Failed creating RandR CRTC\n");
        goto err;
    }
394
    RRCrtcSetRotations (xwl_output->randr_crtc, ALL_ROTATIONS);
395

Kristian Høgsberg's avatar
Kristian Høgsberg committed
396
397
    xwl_output->randr_output = RROutputCreate(xwl_screen->screen, name,
                                              strlen(name), xwl_output);
398
399
400
401
402
    if (!xwl_output->randr_output) {
        ErrorF("Failed creating RandR Output\n");
        goto err;
    }

Kristian Høgsberg's avatar
Kristian Høgsberg committed
403
404
405
406
    RRCrtcGammaSetSize(xwl_output->randr_crtc, 256);
    RROutputSetCrtcs(xwl_output->randr_output, &xwl_output->randr_crtc, 1);
    RROutputSetConnection(xwl_output->randr_output, RR_Connected);

Roman Gilg's avatar
Roman Gilg committed
407
408
    xwl_output->scale = 1;

409
410
411
412
413
414
415
416
417
    /* We want the output to be in the list as soon as created so we can
     * use it when binding to the xdg-output protocol...
     */
    xorg_list_append(&xwl_output->link, &xwl_screen->output_list);
    --xwl_screen->expecting_event;

    if (xwl_screen->xdg_output_manager)
        xwl_output_get_xdg_output(xwl_output);

Kristian Høgsberg's avatar
Kristian Høgsberg committed
418
    return xwl_output;
419
420
421
422
423
424
425
426

err:
    if (xwl_output->randr_crtc)
        RRCrtcDestroy(xwl_output->randr_crtc);
    if (xwl_output->output)
        wl_output_destroy(xwl_output->output);
    free(xwl_output);
    return NULL;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
427
428
429
430
}

void
xwl_output_destroy(struct xwl_output *xwl_output)
431
432
433
434
435
436
437
{
    wl_output_destroy(xwl_output->output);
    free(xwl_output);
}

void
xwl_output_remove(struct xwl_output *xwl_output)
Kristian Høgsberg's avatar
Kristian Høgsberg committed
438
{
439
440
441
    struct xwl_output *it;
    struct xwl_screen *xwl_screen = xwl_output->xwl_screen;
    int width = 0, height = 0;
442
    Bool need_rotate = (xwl_output->xdg_output == NULL);
443

444
    xorg_list_del(&xwl_output->link);
445
446

    xorg_list_for_each_entry(it, &xwl_screen->output_list, link)
447
        output_get_new_size(it, need_rotate, &height, &width);
448
449
    update_screen_size(xwl_output, width, height);

450
451
452
    RRCrtcDestroy(xwl_output->randr_crtc);
    RROutputDestroy(xwl_output->randr_output);

453
    xwl_output_destroy(xwl_output);
Kristian Høgsberg's avatar
Kristian Høgsberg committed
454
455
456
457
458
}

static Bool
xwl_randr_get_info(ScreenPtr pScreen, Rotation * rotations)
{
459
    *rotations = ALL_ROTATIONS;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486

    return TRUE;
}

static Bool
xwl_randr_set_config(ScreenPtr pScreen,
                     Rotation rotation, int rate, RRScreenSizePtr pSize)
{
    return FALSE;
}

Bool
xwl_screen_init_output(struct xwl_screen *xwl_screen)
{
    rrScrPrivPtr rp;

    if (!RRScreenInit(xwl_screen->screen))
        return FALSE;

    RRScreenSetSizeRange(xwl_screen->screen, 320, 200, 8192, 8192);

    rp = rrGetScrPriv(xwl_screen->screen);
    rp->rrGetInfo = xwl_randr_get_info;
    rp->rrSetConfig = xwl_randr_set_config;

    return TRUE;
}
487

488
static void
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
xwl_output_get_xdg_output(struct xwl_output *xwl_output)
{
    struct xwl_screen *xwl_screen = xwl_output->xwl_screen;

    xwl_output->xdg_output =
        zxdg_output_manager_v1_get_xdg_output (xwl_screen->xdg_output_manager,
                                               xwl_output->output);

    zxdg_output_v1_add_listener(xwl_output->xdg_output,
                                &xdg_output_listener,
                                xwl_output);
}

void
xwl_screen_init_xdg_output(struct xwl_screen *xwl_screen)
{
    struct xwl_output *it;

    assert(xwl_screen->xdg_output_manager);

    xorg_list_for_each_entry(it, &xwl_screen->output_list, link)
        xwl_output_get_xdg_output(it);
}