1 /*
2  * Copyright © 2011-2014 Intel Corporation
3  *
4  * Permission to use, copy, modify, distribute, and sell this software
5  * and its documentation for any purpose is hereby granted without
6  * fee, provided that the above copyright notice appear in all copies
7  * and that both that copyright notice and this permission notice
8  * appear in supporting documentation, and that the name of the
9  * copyright holders not be used in advertising or publicity
10  * pertaining to distribution of the software without specific,
11  * written prior permission.  The copyright holders make no
12  * representations about the suitability of this software for any
13  * purpose.  It is provided "as is" without express or implied
14  * warranty.
15  *
16  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
17  * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
18  * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
19  * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
21  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
22  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
23  * SOFTWARE.
24  */
25 
26 #ifdef HAVE_DIX_CONFIG_H
27 #include <dix-config.h>
28 #endif
29 
30 #include "xwayland.h"
31 #include <randrstr.h>
32 
33 #define ALL_ROTATIONS (RR_Rotate_0   | \
34                        RR_Rotate_90  | \
35                        RR_Rotate_180 | \
36                        RR_Rotate_270 | \
37                        RR_Reflect_X  | \
38                        RR_Reflect_Y)
39 
40 static void xwl_output_get_xdg_output(struct xwl_output *xwl_output);
41 
42 static Rotation
wl_transform_to_xrandr(enum wl_output_transform transform)43 wl_transform_to_xrandr(enum wl_output_transform transform)
44 {
45     switch (transform) {
46     default:
47     case WL_OUTPUT_TRANSFORM_NORMAL:
48         return RR_Rotate_0;
49     case WL_OUTPUT_TRANSFORM_90:
50         return RR_Rotate_90;
51     case WL_OUTPUT_TRANSFORM_180:
52         return RR_Rotate_180;
53     case WL_OUTPUT_TRANSFORM_270:
54         return RR_Rotate_270;
55     case WL_OUTPUT_TRANSFORM_FLIPPED:
56         return RR_Reflect_X | RR_Rotate_0;
57     case WL_OUTPUT_TRANSFORM_FLIPPED_90:
58         return RR_Reflect_X | RR_Rotate_90;
59     case WL_OUTPUT_TRANSFORM_FLIPPED_180:
60         return RR_Reflect_X | RR_Rotate_180;
61     case WL_OUTPUT_TRANSFORM_FLIPPED_270:
62         return RR_Reflect_X | RR_Rotate_270;
63     }
64 }
65 
66 static int
wl_subpixel_to_xrandr(int subpixel)67 wl_subpixel_to_xrandr(int subpixel)
68 {
69     switch (subpixel) {
70     default:
71     case WL_OUTPUT_SUBPIXEL_UNKNOWN:
72         return SubPixelUnknown;
73     case WL_OUTPUT_SUBPIXEL_NONE:
74         return SubPixelNone;
75     case WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB:
76         return SubPixelHorizontalRGB;
77     case WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR:
78         return SubPixelHorizontalBGR;
79     case WL_OUTPUT_SUBPIXEL_VERTICAL_RGB:
80         return SubPixelVerticalRGB;
81     case WL_OUTPUT_SUBPIXEL_VERTICAL_BGR:
82         return SubPixelVerticalBGR;
83     }
84 }
85 
86 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)87 output_handle_geometry(void *data, struct wl_output *wl_output, int x, int y,
88                        int physical_width, int physical_height, int subpixel,
89                        const char *make, const char *model, int transform)
90 {
91     struct xwl_output *xwl_output = data;
92 
93     RROutputSetPhysicalSize(xwl_output->randr_output,
94                             physical_width, physical_height);
95     RROutputSetSubpixelOrder(xwl_output->randr_output,
96                              wl_subpixel_to_xrandr(subpixel));
97 
98     /* Apply the change from wl_output only if xdg-output is not supported */
99     if (!xwl_output->xdg_output) {
100         xwl_output->x = x;
101         xwl_output->y = y;
102     }
103     xwl_output->rotation = wl_transform_to_xrandr(transform);
104 }
105 
106 static void
output_handle_mode(void * data,struct wl_output * wl_output,uint32_t flags,int width,int height,int refresh)107 output_handle_mode(void *data, struct wl_output *wl_output, uint32_t flags,
108                    int width, int height, int refresh)
109 {
110     struct xwl_output *xwl_output = data;
111 
112     if (!(flags & WL_OUTPUT_MODE_CURRENT))
113         return;
114 
115     /* Apply the change from wl_output only if xdg-output is not supported */
116     if (!xwl_output->xdg_output) {
117         xwl_output->width = width;
118         xwl_output->height = height;
119     }
120     xwl_output->refresh = refresh;
121 }
122 
123 static inline void
output_get_new_size(struct xwl_output * xwl_output,Bool need_rotate,int * height,int * width)124 output_get_new_size(struct xwl_output *xwl_output,
125                     Bool need_rotate,
126                     int *height, int *width)
127 {
128     int output_width, output_height;
129 
130     if (!need_rotate || (xwl_output->rotation & (RR_Rotate_0 | RR_Rotate_180))) {
131         output_width = xwl_output->width;
132         output_height = xwl_output->height;
133     } else {
134         output_width = xwl_output->height;
135         output_height = xwl_output->width;
136     }
137 
138     if (*width < xwl_output->x + output_width)
139         *width = xwl_output->x + output_width;
140 
141     if (*height < xwl_output->y + output_height)
142         *height = xwl_output->y + output_height;
143 }
144 
145 static int
xwl_set_pixmap_visit_window(WindowPtr window,void * data)146 xwl_set_pixmap_visit_window(WindowPtr window, void *data)
147 {
148     ScreenPtr screen = window->drawable.pScreen;
149 
150     if (screen->GetWindowPixmap(window) == data) {
151         screen->SetWindowPixmap(window, screen->GetScreenPixmap(screen));
152         return WT_WALKCHILDREN;
153     }
154 
155     return WT_DONTWALKCHILDREN;
156 }
157 
158 static void
update_backing_pixmaps(struct xwl_screen * xwl_screen,int width,int height)159 update_backing_pixmaps(struct xwl_screen *xwl_screen, int width, int height)
160 {
161     ScreenPtr pScreen = xwl_screen->screen;
162     WindowPtr pRoot = pScreen->root;
163     PixmapPtr old_pixmap, new_pixmap;
164 
165     old_pixmap = pScreen->GetScreenPixmap(pScreen);
166     new_pixmap = pScreen->CreatePixmap(pScreen, width, height,
167                                        pScreen->rootDepth,
168                                        CREATE_PIXMAP_USAGE_BACKING_PIXMAP);
169     pScreen->SetScreenPixmap(new_pixmap);
170 
171     if (old_pixmap) {
172         TraverseTree(pRoot, xwl_set_pixmap_visit_window, old_pixmap);
173         pScreen->DestroyPixmap(old_pixmap);
174     }
175 
176     pScreen->ResizeWindow(pRoot, 0, 0, width, height, NULL);
177 }
178 
179 static void
update_screen_size(struct xwl_output * xwl_output,int width,int height)180 update_screen_size(struct xwl_output *xwl_output, int width, int height)
181 {
182     struct xwl_screen *xwl_screen = xwl_output->xwl_screen;
183 
184     if (xwl_screen->root_clip_mode == ROOT_CLIP_FULL)
185         SetRootClip(xwl_screen->screen, ROOT_CLIP_NONE);
186 
187     if (!xwl_screen->rootless && xwl_screen->screen->root)
188         update_backing_pixmaps (xwl_screen, width, height);
189 
190     xwl_screen->width = width;
191     xwl_screen->height = height;
192     xwl_screen->screen->width = width;
193     xwl_screen->screen->height = height;
194     xwl_screen->screen->mmWidth = (width * 25.4) / monitorResolution;
195     xwl_screen->screen->mmHeight = (height * 25.4) / monitorResolution;
196 
197     SetRootClip(xwl_screen->screen, xwl_screen->root_clip_mode);
198 
199     if (xwl_screen->screen->root) {
200         BoxRec box = { 0, 0, width, height };
201 
202         xwl_screen->screen->root->drawable.width = width;
203         xwl_screen->screen->root->drawable.height = height;
204         RegionReset(&xwl_screen->screen->root->winSize, &box);
205         RRScreenSizeNotify(xwl_screen->screen);
206     }
207 
208     update_desktop_dimensions();
209 }
210 
211 static void
apply_output_change(struct xwl_output * xwl_output)212 apply_output_change(struct xwl_output *xwl_output)
213 {
214     struct xwl_screen *xwl_screen = xwl_output->xwl_screen;
215     struct xwl_output *it;
216     int mode_width, mode_height;
217     int width = 0, height = 0, has_this_output = 0;
218     RRModePtr randr_mode;
219     Bool need_rotate;
220 
221     /* Clear out the "done" received flags */
222     xwl_output->wl_output_done = FALSE;
223     xwl_output->xdg_output_done = FALSE;
224 
225     /* xdg-output sends output size in compositor space. so already rotated */
226     need_rotate = (xwl_output->xdg_output == NULL);
227 
228     /* We need to rotate back the logical size for the mode */
229     if (need_rotate || xwl_output->rotation & (RR_Rotate_0 | RR_Rotate_180)) {
230         mode_width = xwl_output->width;
231         mode_height = xwl_output->height;
232     } else {
233         mode_width = xwl_output->height;
234         mode_height = xwl_output->width;
235     }
236 
237     randr_mode = xwayland_cvt(mode_width, mode_height,
238                               xwl_output->refresh / 1000.0, 0, 0);
239     RROutputSetModes(xwl_output->randr_output, &randr_mode, 1, 1);
240     RRCrtcNotify(xwl_output->randr_crtc, randr_mode,
241                  xwl_output->x, xwl_output->y,
242                  xwl_output->rotation, NULL, 1, &xwl_output->randr_output);
243 
244     xorg_list_for_each_entry(it, &xwl_screen->output_list, link) {
245         /* output done event is sent even when some property
246          * of output is changed. That means that we may already
247          * have this output. If it is true, we must not add it
248          * into the output_list otherwise we'll corrupt it */
249         if (it == xwl_output)
250             has_this_output = 1;
251 
252         output_get_new_size(it, need_rotate, &height, &width);
253     }
254 
255     if (!has_this_output) {
256         xorg_list_append(&xwl_output->link, &xwl_screen->output_list);
257 
258         /* we did not check this output for new screen size, do it now */
259         output_get_new_size(xwl_output, need_rotate, &height, &width);
260 
261 	--xwl_screen->expecting_event;
262     }
263 
264     update_screen_size(xwl_output, width, height);
265 }
266 
267 static void
output_handle_done(void * data,struct wl_output * wl_output)268 output_handle_done(void *data, struct wl_output *wl_output)
269 {
270     struct xwl_output *xwl_output = data;
271 
272     xwl_output->wl_output_done = TRUE;
273     /* Apply the changes from wl_output only if both "done" events are received,
274      * or if xdg-output is not supported.
275      */
276     if (xwl_output->xdg_output_done || !xwl_output->xdg_output)
277         apply_output_change(xwl_output);
278 }
279 
280 static void
output_handle_scale(void * data,struct wl_output * wl_output,int32_t factor)281 output_handle_scale(void *data, struct wl_output *wl_output, int32_t factor)
282 {
283 }
284 
285 static const struct wl_output_listener output_listener = {
286     output_handle_geometry,
287     output_handle_mode,
288     output_handle_done,
289     output_handle_scale
290 };
291 
292 static void
xdg_output_handle_logical_position(void * data,struct zxdg_output_v1 * xdg_output,int32_t x,int32_t y)293 xdg_output_handle_logical_position(void *data, struct zxdg_output_v1 *xdg_output,
294                                    int32_t x, int32_t y)
295 {
296     struct xwl_output *xwl_output = data;
297 
298     xwl_output->x = x;
299     xwl_output->y = y;
300 }
301 
302 static void
xdg_output_handle_logical_size(void * data,struct zxdg_output_v1 * xdg_output,int32_t width,int32_t height)303 xdg_output_handle_logical_size(void *data, struct zxdg_output_v1 *xdg_output,
304                                int32_t width, int32_t height)
305 {
306     struct xwl_output *xwl_output = data;
307 
308     xwl_output->width = width;
309     xwl_output->height = height;
310 }
311 
312 static void
xdg_output_handle_done(void * data,struct zxdg_output_v1 * xdg_output)313 xdg_output_handle_done(void *data, struct zxdg_output_v1 *xdg_output)
314 {
315     struct xwl_output *xwl_output = data;
316 
317     xwl_output->xdg_output_done = TRUE;
318     if (xwl_output->wl_output_done)
319         apply_output_change(xwl_output);
320 }
321 
322 static const struct zxdg_output_v1_listener xdg_output_listener = {
323     xdg_output_handle_logical_position,
324     xdg_output_handle_logical_size,
325     xdg_output_handle_done,
326 };
327 
328 struct xwl_output *
xwl_output_create(struct xwl_screen * xwl_screen,uint32_t id)329 xwl_output_create(struct xwl_screen *xwl_screen, uint32_t id)
330 {
331     struct xwl_output *xwl_output;
332     static int serial;
333     char name[256];
334 
335     xwl_output = calloc(1, sizeof *xwl_output);
336     if (xwl_output == NULL) {
337         ErrorF("%s ENOMEM\n", __func__);
338         return NULL;
339     }
340 
341     xwl_output->output = wl_registry_bind(xwl_screen->registry, id,
342                                           &wl_output_interface, 2);
343     if (!xwl_output->output) {
344         ErrorF("Failed binding wl_output\n");
345         goto err;
346     }
347 
348     xwl_output->server_output_id = id;
349     wl_output_add_listener(xwl_output->output, &output_listener, xwl_output);
350 
351     snprintf(name, sizeof name, "XWAYLAND%d", serial++);
352 
353     xwl_output->xwl_screen = xwl_screen;
354     xwl_output->randr_crtc = RRCrtcCreate(xwl_screen->screen, xwl_output);
355     if (!xwl_output->randr_crtc) {
356         ErrorF("Failed creating RandR CRTC\n");
357         goto err;
358     }
359     RRCrtcSetRotations (xwl_output->randr_crtc, ALL_ROTATIONS);
360 
361     xwl_output->randr_output = RROutputCreate(xwl_screen->screen, name,
362                                               strlen(name), xwl_output);
363     if (!xwl_output->randr_output) {
364         ErrorF("Failed creating RandR Output\n");
365         goto err;
366     }
367 
368     RRCrtcGammaSetSize(xwl_output->randr_crtc, 256);
369     RROutputSetCrtcs(xwl_output->randr_output, &xwl_output->randr_crtc, 1);
370     RROutputSetConnection(xwl_output->randr_output, RR_Connected);
371 
372     /* We want the output to be in the list as soon as created so we can
373      * use it when binding to the xdg-output protocol...
374      */
375     xorg_list_append(&xwl_output->link, &xwl_screen->output_list);
376     --xwl_screen->expecting_event;
377 
378     if (xwl_screen->xdg_output_manager)
379         xwl_output_get_xdg_output(xwl_output);
380 
381     return xwl_output;
382 
383 err:
384     if (xwl_output->randr_crtc)
385         RRCrtcDestroy(xwl_output->randr_crtc);
386     if (xwl_output->output)
387         wl_output_destroy(xwl_output->output);
388     free(xwl_output);
389     return NULL;
390 }
391 
392 void
xwl_output_destroy(struct xwl_output * xwl_output)393 xwl_output_destroy(struct xwl_output *xwl_output)
394 {
395     wl_output_destroy(xwl_output->output);
396     free(xwl_output);
397 }
398 
399 void
xwl_output_remove(struct xwl_output * xwl_output)400 xwl_output_remove(struct xwl_output *xwl_output)
401 {
402     struct xwl_output *it;
403     struct xwl_screen *xwl_screen = xwl_output->xwl_screen;
404     int width = 0, height = 0;
405     Bool need_rotate = (xwl_output->xdg_output == NULL);
406 
407     xorg_list_del(&xwl_output->link);
408 
409     xorg_list_for_each_entry(it, &xwl_screen->output_list, link)
410         output_get_new_size(it, need_rotate, &height, &width);
411     update_screen_size(xwl_output, width, height);
412 
413     RRCrtcDestroy(xwl_output->randr_crtc);
414     RROutputDestroy(xwl_output->randr_output);
415 
416     xwl_output_destroy(xwl_output);
417 }
418 
419 static Bool
xwl_randr_get_info(ScreenPtr pScreen,Rotation * rotations)420 xwl_randr_get_info(ScreenPtr pScreen, Rotation * rotations)
421 {
422     *rotations = ALL_ROTATIONS;
423 
424     return TRUE;
425 }
426 
427 static Bool
xwl_randr_set_config(ScreenPtr pScreen,Rotation rotation,int rate,RRScreenSizePtr pSize)428 xwl_randr_set_config(ScreenPtr pScreen,
429                      Rotation rotation, int rate, RRScreenSizePtr pSize)
430 {
431     return FALSE;
432 }
433 
434 Bool
xwl_screen_init_output(struct xwl_screen * xwl_screen)435 xwl_screen_init_output(struct xwl_screen *xwl_screen)
436 {
437     rrScrPrivPtr rp;
438 
439     if (!RRScreenInit(xwl_screen->screen))
440         return FALSE;
441 
442     RRScreenSetSizeRange(xwl_screen->screen, 16, 16, 32767, 32767);
443 
444     rp = rrGetScrPriv(xwl_screen->screen);
445     rp->rrGetInfo = xwl_randr_get_info;
446     rp->rrSetConfig = xwl_randr_set_config;
447 
448     return TRUE;
449 }
450 
451 static void
xwl_output_get_xdg_output(struct xwl_output * xwl_output)452 xwl_output_get_xdg_output(struct xwl_output *xwl_output)
453 {
454     struct xwl_screen *xwl_screen = xwl_output->xwl_screen;
455 
456     xwl_output->xdg_output =
457         zxdg_output_manager_v1_get_xdg_output (xwl_screen->xdg_output_manager,
458                                                xwl_output->output);
459 
460     zxdg_output_v1_add_listener(xwl_output->xdg_output,
461                                 &xdg_output_listener,
462                                 xwl_output);
463 }
464 
465 void
xwl_screen_init_xdg_output(struct xwl_screen * xwl_screen)466 xwl_screen_init_xdg_output(struct xwl_screen *xwl_screen)
467 {
468     struct xwl_output *it;
469 
470     assert(xwl_screen->xdg_output_manager);
471 
472     xorg_list_for_each_entry(it, &xwl_screen->output_list, link)
473         xwl_output_get_xdg_output(it);
474 }
475