a044788c4133e901793051e8d0d4be5094c2a94c
[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"
23 #include <pthread.h>
24 #include <xf86drmMode.h>
25 #include <wayland-client.h>
26 #include <wayland-drm-client-protocol.h>
27 #include <viewporter-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_callback *callback;
43         struct wp_viewport *viewport;
44         struct wp_viewporter *viewporter;
45         pthread_t threadID;
46 };
48 #define to_buffer_wl(x) container_of(x, struct buffer_wl, base)
49 struct buffer_wl {
50         struct buffer base;
52         struct wl_buffer *wl_buf;
53         uint32_t name;
54 };
56 static int global_fd = 0;
57 static uint32_t used_planes = 0;
58 static int ndisplays = 0;
60 int wayland_process_events(struct display_wl *disp_wl)
61 {
63         wl_display_dispatch(disp_wl->display);
65         return 0;
66 }
67 void* wayland_ev_task(void *arg)
68 {
69         int ret;
70         while(1) {
71                 ret = wayland_process_events((struct display_wl *)arg);
72         }
74         return NULL;
75 }
77 static struct omap_bo *
78 alloc_bo(struct display *disp, uint32_t bpp, uint32_t width, uint32_t height,
79                 uint32_t *bo_handle, uint32_t *pitch)
80 {
81         struct display_wl *disp_wl = to_display_wl(disp);
82         struct omap_bo *bo;
83         uint32_t bo_flags = disp_wl->bo_flags;
85         bo_flags |= OMAP_BO_WC;
86         bo = omap_bo_new(disp->dev, width * height * bpp / 8, bo_flags);
88         if (bo) {
89                 *bo_handle = omap_bo_handle(bo);
90                 *pitch = width * bpp / 8;
91                 if (bo_flags & OMAP_BO_TILED)
92                         *pitch = ALIGN2(*pitch, PAGE_SHIFT);
93         }
95         return bo;
96 }
98 static struct buffer *
99 alloc_buffer(struct display *disp, uint32_t fourcc, uint32_t w, uint32_t h)
101         struct display_wl *disp_wl = to_display_wl(disp);
102         struct buffer_wl *buf_wl;
103         struct buffer *buf;
104         uint32_t bo_handles[4] = {0};
105         uint32_t wl_fmt = WL_DRM_FORMAT_NV12;
107         buf_wl = calloc(1, sizeof(*buf_wl));
108         if (!buf_wl) {
109                 ERROR("allocation failed");
110                 return NULL;
111         }
112         buf = &buf_wl->base;
114         buf->fourcc = fourcc;
115         buf->width = w;
116         buf->height = h;
117         buf->multiplanar = false;
119         buf->nbo = 1;
121         if (!fourcc)
122                 fourcc = FOURCC('A','R','2','4');
124         switch (fourcc) {
125         case FOURCC('A','R','2','4'):
126                 wl_fmt = WL_DRM_FORMAT_ARGB8888;
127                 buf->nbo = 1;
128                 buf->bo[0] = alloc_bo(disp, 32, buf->width, buf->height,
129                                 &bo_handles[0], &buf->pitches[0]);
130                 omap_bo_get_name(buf->bo[0], &buf_wl->name);
131                 buf->fd[0] = omap_bo_dmabuf(buf->bo[0]);
133                 /* ARGB: stride is four times width */
134                 buf_wl->wl_buf = wl_drm_create_buffer(disp_wl->drm,
135                         buf_wl->name, buf->width, buf->height,
136                         buf->width * 4, wl_fmt);
137                 break;
138         case FOURCC('U','Y','V','Y'):
139         case FOURCC('Y','U','Y','V'):
140                 wl_fmt = WL_DRM_FORMAT_YUYV;
141                 buf->nbo = 1;
142                 buf->bo[0] = alloc_bo(disp, 16, buf->width, buf->height,
143                                 &bo_handles[0], &buf->pitches[0]);
145                 omap_bo_get_name(buf->bo[0], &buf_wl->name);
146                 buf->fd[0] = omap_bo_dmabuf(buf->bo[0]);
147                 /* YUYV; stride is double width */
148                 buf_wl->wl_buf = wl_drm_create_buffer(disp_wl->drm,
149                         buf_wl->name, buf->width, buf->height,
150                         buf->width * 2, wl_fmt);
151                 break;
152         case FOURCC('N','V','1','2'):
153                 wl_fmt = WL_DRM_FORMAT_NV12;
154                 buf->nbo = 1;
155                 buf->bo[0] = alloc_bo(disp, 8, buf->width, buf->height * 3 / 2,
156                                 &bo_handles[0], &buf->pitches[0]);
158                 omap_bo_get_name(buf->bo[0], &buf_wl->name);
159                 buf->fd[0] = omap_bo_dmabuf(buf->bo[0]);
160                 /* NV12: Create a planar buffer */
161                 buf_wl->wl_buf = wl_drm_create_planar_buffer(disp_wl->drm,
162                         buf_wl->name, buf->width, buf->height, wl_fmt,
163                         0, buf->width,
164                         buf->width * buf->height, buf->width,
165                         0, 0);
166                 break;
167         default:
168                 ERROR("invalid format: 0x%08x", fourcc);
169                 goto fail;
170         }
172         return buf;
174 fail:
175         // XXX cleanup
176         return NULL;
179 static void
180 free_buffers(struct display *disp, uint32_t n)
182         uint32_t i;
183         for (i = 0; i < n; i++) {
184                 if (disp->buf[i]) {
185                         close(disp->buf[i]->fd[0]);
186                         omap_bo_del(disp->buf[i]->bo[0]);
187                         if (disp->multiplanar) {
188                                 close(disp->buf[i]->fd[1]);
189                                 omap_bo_del(disp->buf[i]->bo[1]);
190                         }
191                 }
192         }
193         free(disp->buf);
196 static struct buffer **
197 alloc_buffers(struct display *disp, uint32_t n,
198                 uint32_t fourcc, uint32_t w, uint32_t h)
200         struct buffer **bufs;
201         uint32_t i = 0;
203         bufs = calloc(n, sizeof(*bufs));
204         if (!bufs) {
205                 ERROR("allocation failed");
206                 goto fail;
207         }
209         for (i = 0; i < n; i++) {
210                 bufs[i] = alloc_buffer(disp, fourcc, w, h);
211                 if (!bufs[i]) {
212                         ERROR("allocation failed");
213                         goto fail;
214                 }
215         }
216         disp->buf = bufs;
217         return bufs;
219 fail:
220         // XXX cleanup
221         return NULL;
224 static struct buffer **
225 get_buffers(struct display *disp, uint32_t n)
227         return alloc_buffers(disp, n, 0, disp->width, disp->height);
230 static struct buffer **
231 get_vid_buffers(struct display *disp, uint32_t n,
232                 uint32_t fourcc, uint32_t w, uint32_t h)
234         return alloc_buffers(disp, n, fourcc, w, h);
237 static int
238 post_buffer(struct display *disp, struct buffer *buf)
240         return -1;
242 static void redraw(void *data, struct wl_callback *callback, uint32_t time) {
243        struct display_wl *disp_wl = (struct display_wl *) data;
245        wl_callback_destroy(disp_wl->callback);
246        disp_wl->callback = NULL;
249 static const struct wl_callback_listener frame_listener = { redraw };
251 static int
252 post_vid_buffer(struct display *disp, struct buffer *buf,
253                 uint32_t x, uint32_t y, uint32_t w, uint32_t h)
255         struct display_wl *disp_wl = to_display_wl(disp);
256         struct buffer_wl *buf_wl = to_buffer_wl(buf);
257         int ret = 0;
259         wl_surface_attach(disp_wl->surface, buf_wl->wl_buf, 0, 0);
260         wl_surface_damage(disp_wl->surface,
261                         0, 0, disp->width, disp->height);
262         wp_viewport_set_source(disp_wl->viewport,
263                         wl_fixed_from_int(x), wl_fixed_from_int(y),
264                         wl_fixed_from_int(w), wl_fixed_from_int(h));
266         if (disp_wl->callback)
267                 wl_callback_destroy(disp_wl->callback);
268         disp_wl->callback = wl_surface_frame(disp_wl->surface);
270         wl_callback_add_listener(disp_wl->callback, &frame_listener, disp_wl);
272         wl_surface_commit(disp_wl->surface);
273         wl_display_flush(disp_wl->display);
275         return ret;
278 static void
279 close_kms(struct display *disp)
281         struct display_wl *disp_wl = to_display_wl(disp);
282         pthread_cancel(disp_wl->threadID);
284         omap_device_del(disp->dev);
285         disp->dev = NULL;
286         if (used_planes) {
287                 used_planes >>= 1;
288         }
289         if (--ndisplays == 0) {
290                 close(global_fd);
291         }
294 void
295 disp_wayland_usage(void)
297         MSG("WAYLAND Display Options:");
298         MSG("\t-w <width>x<height>\tset the dimensions of client window");
301 static void
302 registry_handle_global(void *data, struct wl_registry *registry, uint32_t id,
303         const char *interface, uint32_t version)
305         struct display_wl *disp_wl = (struct display_wl *) data;
307         if (strcmp(interface, "wl_compositor") == 0) {
308                 disp_wl->compositor = wl_registry_bind(registry, id,
309                         &wl_compositor_interface, 3);
310         } else if (strcmp(interface, "wl_shell") == 0) {
311                 disp_wl->shell = wl_registry_bind(registry, id,
312                         &wl_shell_interface, 1);
313         } else if (strcmp(interface, "wl_drm") == 0) {
314                 disp_wl->drm = wl_registry_bind(registry, id,
315                         &wl_drm_interface, 1);
316         } else if (strcmp(interface, "wp_viewporter") == 0) {
317                 disp_wl->viewporter = wl_registry_bind(registry, id,
318                         &wp_viewporter_interface, 1);
319         }
322 static void
323 registry_handle_global_remove(void *data, struct wl_registry *registry,
324                 uint32_t name)
327 static const struct wl_registry_listener registry_listener = {
328         registry_handle_global,
329         registry_handle_global_remove
330 };
333 struct display *
334 disp_wayland_open(int argc, char **argv)
336         struct display_wl *disp_wl = NULL;
337         struct display *disp;
338         int i, enabled = 0, width, height;
340         struct wl_registry *registry;
341         struct wl_shell_surface *shell_surface;
342         struct wl_region *region;
343         int ret;
345         /* note: set args to NULL after we've parsed them so other modules know
346          * that it is already parsed (since the arg parsing is decentralized)
347          */
348         for (i = 1; i < argc; i++) {
349                 if (!argv[i]) {
350                         continue;
351                 }
352                 if (!strcmp("-w", argv[i])) {
353                         argv[i++] = NULL;
354                         if (sscanf(argv[i], "%dx%d", &width, &height) != 2) {
355                                 ERROR("invalid arg: -w %s", argv[i]);
356                                 goto fail;
357                         }
358                         enabled = 1;
359                 } else {
360                         /* ignore */
361                         continue;
362                 }
363                 argv[i] = NULL;
364         }
365         // If not explicitely enabled from command line, fail.
366         if (!enabled)
367                 goto fail;
369         disp_wl = calloc(1, sizeof(*disp_wl));
370         if (!disp_wl) {
371                 ERROR("allocation failed");
372                 goto fail;
373         }
374         disp = &disp_wl->base;
376         if (!global_fd) {
377                 global_fd = drmOpen("omapdrm", NULL);
378                 if (global_fd < 0) {
379                         ERROR("could not open drm device: %s (%d)",
380                                 strerror(errno), errno);
381                         goto fail;
382                 }
383         }
385         disp->fd = global_fd;
386         ndisplays++;
388         disp->dev = omap_device_new(disp->fd);
389         if (!disp->dev) {
390                 ERROR("couldn't create device");
391                 goto fail;
392         }
394         disp->get_buffers = get_buffers;
395         disp->get_vid_buffers = get_vid_buffers;
396         disp->post_buffer = post_buffer;
397         disp->post_vid_buffer = post_vid_buffer;
398         disp->close = close_kms;
399         disp->disp_free_buf = free_buffers;
401         disp->multiplanar = false;
402         disp->width = width;
403         disp->height = height;
405         disp_wl->bo_flags = OMAP_BO_SCANOUT|OMAP_BO_WC;
407         disp_wl->display = wl_display_connect(NULL);
408         if (disp_wl->display == NULL) {
409                 ERROR("failed to connect to Wayland display: %m\n");
410                 goto fail;
411         } else {
412                 MSG("wayland display opened\n");
413         }
415         /* Find out what interfaces are implemented, and initialize */
416         registry = wl_display_get_registry(disp_wl->display);
417         wl_registry_add_listener(registry, &registry_listener, disp_wl);
418         wl_display_roundtrip(disp_wl->display);
419         MSG("wayland registries obtained\n");
421         disp_wl->surface = wl_compositor_create_surface(disp_wl->compositor);
422         shell_surface = wl_shell_get_shell_surface(disp_wl->shell,
423                                                 disp_wl->surface);
424         wl_shell_surface_set_toplevel(shell_surface);
426         region = wl_compositor_create_region(disp_wl->compositor);
427         wl_region_add(region, 0, 0, disp->width, disp->height);
429         disp_wl->viewport = wp_viewporter_get_viewport(disp_wl->viewporter,
430                                                 disp_wl->surface);
431         wp_viewport_set_destination(disp_wl->viewport, width, height);
433         ret = pthread_create(&disp_wl->threadID, NULL, wayland_ev_task, disp_wl);
434         if(ret) {
435                 MSG("could not create task for wayland event processing");
436         }
438         return disp;
440 fail:
441         // XXX cleanup
442         return NULL;