display-wayland: Wayland backend implementation
[glsdk/omapdrmtest.git] / util / display-wayland.c
1 /*
2  * Copyright (C) 2011 Texas Instruments
3  * Author: Nikhil Devshatwar <nikhil.nd@ti.com>
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 as published by
7  * the Free Software Foundation.
8  *
9  * This program is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
12  * more details.
13  *
14  * You should have received a copy of the GNU General Public License along with
15  * this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
18 #ifdef HAVE_CONFIG_H
19 #include "config.h"
20 #endif
22 #include "util.h"
24 #include <xf86drmMode.h>
25 #include <wayland-client.h>
26 #include <wayland-drm-client-protocol.h>
27 #include <wayland-scaler-client-protocol.h>
29 /* NOTE: healthy dose of recycling from libdrm modetest app.. */
32 #define to_display_wl(x) container_of(x, struct display_wl, base)
33 struct display_wl {
34         struct display base;
36         uint32_t bo_flags;
37         struct wl_display *display;
38         struct wl_compositor *compositor;
39         struct wl_surface *surface;
40         struct wl_shell *shell;
41         struct wl_drm *drm;
42         struct wl_viewport *viewport;
43         struct wl_scaler *scaler;
44 };
46 #define to_buffer_wl(x) container_of(x, struct buffer_wl, base)
47 struct buffer_wl {
48         struct buffer base;
50         struct wl_buffer *wl_buf;
51         uint32_t name;
52 };
54 static int global_fd = 0;
55 static uint32_t used_planes = 0;
56 static int ndisplays = 0;
58 static struct omap_bo *
59 alloc_bo(struct display *disp, uint32_t bpp, uint32_t width, uint32_t height,
60                 uint32_t *bo_handle, uint32_t *pitch)
61 {
62         struct display_wl *disp_wl = to_display_wl(disp);
63         struct omap_bo *bo;
64         uint32_t bo_flags = disp_wl->bo_flags;
66         bo_flags |= OMAP_BO_WC;
67         bo = omap_bo_new(disp->dev, width * height * bpp / 8, bo_flags);
69         if (bo) {
70                 *bo_handle = omap_bo_handle(bo);
71                 *pitch = width * bpp / 8;
72                 if (bo_flags & OMAP_BO_TILED)
73                         *pitch = ALIGN2(*pitch, PAGE_SHIFT);
74         }
76         return bo;
77 }
79 static struct buffer *
80 alloc_buffer(struct display *disp, uint32_t fourcc, uint32_t w, uint32_t h)
81 {
82         struct display_wl *disp_wl = to_display_wl(disp);
83         struct buffer_wl *buf_wl;
84         struct buffer *buf;
85         uint32_t bo_handles[4] = {0};
86         uint32_t wl_fmt = WL_DRM_FORMAT_NV12;
88         buf_wl = calloc(1, sizeof(*buf_wl));
89         if (!buf_wl) {
90                 ERROR("allocation failed");
91                 return NULL;
92         }
93         buf = &buf_wl->base;
95         buf->fourcc = fourcc;
96         buf->width = w;
97         buf->height = h;
98         buf->multiplanar = true;
100         buf->nbo = 1;
102         if (!fourcc)
103                 fourcc = FOURCC('A','R','2','4');
105         switch (fourcc) {
106         case FOURCC('A','R','2','4'):
107                 wl_fmt = WL_DRM_FORMAT_ARGB8888;
108                 buf->nbo = 1;
109                 buf->bo[0] = alloc_bo(disp, 32, buf->width, buf->height,
110                                 &bo_handles[0], &buf->pitches[0]);
111                 omap_bo_get_name(buf->bo[0], &buf_wl->name);
113                 /* ARGB: stride is four times width */
114                 buf_wl->wl_buf = wl_drm_create_buffer(disp_wl->drm,
115                         buf_wl->name, buf->width, buf->height,
116                         buf->width * 4, wl_fmt);
117                 break;
118         case FOURCC('U','Y','V','Y'):
119         case FOURCC('Y','U','Y','V'):
120                 wl_fmt = WL_DRM_FORMAT_YUYV;
121                 buf->nbo = 1;
122                 buf->bo[0] = alloc_bo(disp, 16, buf->width, buf->height,
123                                 &bo_handles[0], &buf->pitches[0]);
125                 omap_bo_get_name(buf->bo[0], &buf_wl->name);
126                 /* YUYV; stride is double width */
127                 buf_wl->wl_buf = wl_drm_create_buffer(disp_wl->drm,
128                         buf_wl->name, buf->width, buf->height,
129                         buf->width * 2, wl_fmt);
130                 break;
131         case FOURCC('N','V','1','2'):
132                 wl_fmt = WL_DRM_FORMAT_NV12;
133                 buf->nbo = 1;
134                 buf->bo[0] = alloc_bo(disp, 8, buf->width, buf->height * 3 / 2,
135                                 &bo_handles[0], &buf->pitches[0]);
137                 omap_bo_get_name(buf->bo[0], &buf_wl->name);
138                 /* NV12: Create a planar buffer */
139                 buf_wl->wl_buf = wl_drm_create_planar_buffer(disp_wl->drm,
140                         buf_wl->name, buf->width, buf->height, wl_fmt,
141                         0, buf->width,
142                         buf->width * buf->height, buf->width,
143                         0, 0);
144                 break;
145         default:
146                 ERROR("invalid format: 0x%08x", fourcc);
147                 goto fail;
148         }
150         return buf;
152 fail:
153         // XXX cleanup
154         return NULL;
157 static void
158 free_buffers(struct display *disp, uint32_t n)
160         uint32_t i;
161         for (i = 0; i < n; i++) {
162                 if (disp->buf[i]) {
163                         close(disp->buf[i]->fd[0]);
164                         omap_bo_del(disp->buf[i]->bo[0]);
165                         if (disp->multiplanar) {
166                                 close(disp->buf[i]->fd[1]);
167                                 omap_bo_del(disp->buf[i]->bo[1]);
168                         }
169                 }
170         }
171         free(disp->buf);
174 static struct buffer **
175 alloc_buffers(struct display *disp, uint32_t n,
176                 uint32_t fourcc, uint32_t w, uint32_t h)
178         struct buffer **bufs;
179         uint32_t i = 0;
181         bufs = calloc(n, sizeof(*bufs));
182         if (!bufs) {
183                 ERROR("allocation failed");
184                 goto fail;
185         }
187         for (i = 0; i < n; i++) {
188                 bufs[i] = alloc_buffer(disp, fourcc, w, h);
189                 if (!bufs[i]) {
190                         ERROR("allocation failed");
191                         goto fail;
192                 }
193         }
194         disp->buf = bufs;
195         return bufs;
197 fail:
198         // XXX cleanup
199         return NULL;
202 static struct buffer **
203 get_buffers(struct display *disp, uint32_t n)
205         return alloc_buffers(disp, n, 0, disp->width, disp->height);
208 static struct buffer **
209 get_vid_buffers(struct display *disp, uint32_t n,
210                 uint32_t fourcc, uint32_t w, uint32_t h)
212         return alloc_buffers(disp, n, fourcc, w, h);
215 static int
216 post_buffer(struct display *disp, struct buffer *buf)
218         ERROR("post_buffer not supported");
220         return -1;
223 static int
224 post_vid_buffer(struct display *disp, struct buffer *buf,
225                 uint32_t x, uint32_t y, uint32_t w, uint32_t h)
227         struct display_wl *disp_wl = to_display_wl(disp);
228         struct buffer_wl *buf_wl = to_buffer_wl(buf);
229         int ret = 0;
231         wl_surface_attach(disp_wl->surface, buf_wl->wl_buf, 0, 0);
232         wl_surface_commit(disp_wl->surface);
233         wl_display_flush(disp_wl->display);
235         return ret;
238 static void
239 close_kms(struct display *disp)
241         omap_device_del(disp->dev);
242         disp->dev = NULL;
243         if (used_planes) {
244                 used_planes >>= 1;
245         }
246         if (--ndisplays == 0) {
247                 close(global_fd);
248         }
251 void
252 disp_wayland_usage(void)
254         MSG("WAYLAND Display Options:");
255         MSG("\t-w <width>x<height>\tset the dimensions of client window");
256         MSG("\t--pos <X coord>x<Y coord>\tset the position of client window");
259 static void
260 registry_handle_global(void *data, struct wl_registry *registry, uint32_t id,
261         const char *interface, uint32_t version)
263         struct display_wl *disp_wl = (struct display_wl *) data;
265         if (strcmp(interface, "wl_compositor") == 0) {
266                 disp_wl->compositor = wl_registry_bind(registry, id,
267                         &wl_compositor_interface, 3);
268         } else if (strcmp(interface, "wl_shell") == 0) {
269                 disp_wl->shell = wl_registry_bind(registry, id,
270                         &wl_shell_interface, 1);
271         } else if (strcmp(interface, "wl_drm") == 0) {
272                 disp_wl->drm = wl_registry_bind(registry, id,
273                         &wl_drm_interface, 1);
274         } else if (strcmp(interface, "wl_scaler") == 0) {
275                 disp_wl->scaler = wl_registry_bind(registry, id,
276                         &wl_scaler_interface, 2);
277         }
280 static void
281 registry_handle_global_remove(void *data, struct wl_registry *registry,
282                 uint32_t name)
285 static const struct wl_registry_listener registry_listener = {
286         registry_handle_global,
287         registry_handle_global_remove
288 };
291 struct display *
292 disp_wayland_open(int argc, char **argv)
294         struct display_wl *disp_wl = NULL;
295         struct display *disp;
296         int i, enabled = 0, width, height, posx, posy;
298         struct wl_registry *registry;
299         struct wl_shell_surface *shell_surface;
300         struct wl_region *region;
301         char title[100];
303         /* note: set args to NULL after we've parsed them so other modules know
304          * that it is already parsed (since the arg parsing is decentralized)
305          */
306         for (i = 1; i < argc; i++) {
307                 if (!argv[i]) {
308                         continue;
309                 }
310                 if (!strcmp("-1", argv[i])) {
311                         disp->multiplanar = false;
312                 } else if (!strcmp("-w", argv[i])) {
313                         argv[i++] = NULL;
314                         if (sscanf(argv[i], "%dx%d", &width, &height) != 2) {
315                                 ERROR("invalid arg: -w %s", argv[i]);
316                                 goto fail;
317                         }
318                         enabled = 1;
319                 } else if (!strcmp("--pos", argv[i])) {
320                         argv[i++] = NULL;
321                         if (sscanf(argv[i], "%dx%d", &posx, &posy) != 2) {
322                                 ERROR("invalid arg: -w %s", argv[i]);
323                                 goto fail;
324                         }
325                 } else {
326                         /* ignore */
327                         continue;
328                 }
329                 argv[i] = NULL;
330         }
331         // If not explicitely enabled from command line, fail.
332         if (!enabled)
333                 goto fail;
335         disp_wl = calloc(1, sizeof(*disp_wl));
336         if (!disp_wl) {
337                 ERROR("allocation failed");
338                 goto fail;
339         }
340         disp = &disp_wl->base;
342         if (!global_fd) {
343                 global_fd = drmOpen("omapdrm", NULL);
344                 if (global_fd < 0) {
345                         ERROR("could not open drm device: %s (%d)",
346                                 strerror(errno), errno);
347                         goto fail;
348                 }
349         }
351         disp->fd = global_fd;
352         ndisplays++;
354         disp->dev = omap_device_new(disp->fd);
355         if (!disp->dev) {
356                 ERROR("couldn't create device");
357                 goto fail;
358         }
360         disp->get_buffers = get_buffers;
361         disp->get_vid_buffers = get_vid_buffers;
362         disp->post_buffer = post_buffer;
363         disp->post_vid_buffer = post_vid_buffer;
364         disp->close = close_kms;
365         disp->disp_free_buf = free_buffers;
367         disp->multiplanar = true;
368         disp->width = width;
369         disp->height = height;
371         disp_wl->bo_flags = OMAP_BO_SCANOUT|OMAP_BO_WC;
373         disp_wl->display = wl_display_connect(NULL);
374         if (disp_wl->display == NULL) {
375                 ERROR("failed to connect to Wayland display: %m\n");
376                 goto fail;
377         } else {
378                 MSG("wayland display opened\n");
379         }
381         /* Find out what interfaces are implemented, and initialize */
382         registry = wl_display_get_registry(disp_wl->display);
383         wl_registry_add_listener(registry, &registry_listener, disp_wl);
384         wl_display_roundtrip(disp_wl->display);
385         MSG("wayland registries obtained\n");
387         disp_wl->surface = wl_compositor_create_surface(disp_wl->compositor);
388         shell_surface = wl_shell_get_shell_surface(disp_wl->shell,
389                                                 disp_wl->surface);
390         wl_shell_surface_set_toplevel(shell_surface);
392         region = wl_compositor_create_region(disp_wl->compositor);
393         wl_region_add(region, 0, 0, disp->width, disp->height);
395         disp_wl->viewport = wl_scaler_get_viewport(disp_wl->scaler,
396                                                 disp_wl->surface);
397         wl_viewport_set_destination(disp_wl->viewport, width, height);
398         sprintf(title, "[@position-req=%dx%d]", posx, posy);
399         wl_shell_surface_set_title(shell_surface, title);
401         return disp;
403 fail:
404         // XXX cleanup
405         return NULL;