f572b60810d4d7c33b0cc97a80845fc7e651c363
[platform/core/uifw/libds-tizen.git] / src / clients / simple-tbm.c
1 /*
2  * Copyright © 2011 Benjamin Franzke
3  * Copyright © 2010 Intel Corporation
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice (including the next
13  * paragraph) shall be included in all copies or substantial portions of the
14  * Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22  * DEALINGS IN THE SOFTWARE.
23  */
24
25 #include <stdint.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <stdbool.h>
30 #include <assert.h>
31 #include <unistd.h>
32 #include <sys/mman.h>
33 #include <signal.h>
34 #include <errno.h>
35
36 #include <wayland-client.h>
37 #include <wayland-tbm-client.h>
38 #include <tbm_surface.h>
39 #include <tbm_surface_internal.h>
40 #include "xdg-shell-client-protocol.h"
41
42 static uint64_t buffer_info_key;
43 #define BUFFER_INFO_KEY (unsigned long)(&buffer_info_key)
44
45 struct display {
46         struct wl_display *display;
47         struct wl_registry *registry;
48         struct wl_compositor *compositor;
49         struct xdg_wm_base *wm_base;
50         struct wl_shm *shm;
51     struct wayland_tbm_client *wl_tbm;
52         bool has_xrgb;
53 };
54
55 struct window {
56         struct display *display;
57         int width, height;
58         struct wl_surface *surface;
59         struct xdg_surface *xdg_surface;
60         struct xdg_toplevel *xdg_toplevel;
61         struct wl_callback *callback;
62     tbm_surface_queue_h surface_queue;
63         bool wait_for_configure;
64 };
65
66 struct buffer_info {
67     struct window *window;
68     struct wl_buffer *wl_buffer;
69 };
70
71 static int running = 1;
72
73 static void
74 redraw(void *data, struct wl_callback *callback, uint32_t time);
75
76 static void
77 handle_xdg_surface_configure(void *data, struct xdg_surface *surface,
78                              uint32_t serial)
79 {
80         struct window *window = data;
81
82         xdg_surface_ack_configure(surface, serial);
83
84         if (window->wait_for_configure) {
85                 redraw(window, NULL, 0);
86                 window->wait_for_configure = false;
87         }
88 }
89
90 static const struct xdg_surface_listener xdg_surface_listener = {
91         handle_xdg_surface_configure,
92 };
93
94 static void
95 handle_xdg_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel,
96                               int32_t width, int32_t height,
97                               struct wl_array *state)
98 {
99 }
100
101 static void
102 handle_xdg_toplevel_close(void *data, struct xdg_toplevel *xdg_toplevel)
103 {
104         running = 0;
105 }
106
107 static const struct xdg_toplevel_listener xdg_toplevel_listener = {
108         handle_xdg_toplevel_configure,
109         handle_xdg_toplevel_close,
110 };
111
112 static struct window *
113 create_window(struct display *display, int width, int height)
114 {
115         struct window *window;
116
117         window = calloc(1, sizeof *window);
118         if (!window)
119                 return NULL;
120
121         window->callback = NULL;
122         window->display = display;
123         window->width = width;
124         window->height = height;
125         window->surface = wl_compositor_create_surface(display->compositor);
126
127         if (display->wm_base) {
128                 window->xdg_surface =
129                         xdg_wm_base_get_xdg_surface(display->wm_base,
130                                                     window->surface);
131                 assert(window->xdg_surface);
132                 xdg_surface_add_listener(window->xdg_surface,
133                                          &xdg_surface_listener, window);
134
135                 window->xdg_toplevel =
136                         xdg_surface_get_toplevel(window->xdg_surface);
137                 assert(window->xdg_toplevel);
138                 xdg_toplevel_add_listener(window->xdg_toplevel,
139                                           &xdg_toplevel_listener, window);
140
141                 xdg_toplevel_set_title(window->xdg_toplevel, "simple-tbm");
142                 wl_surface_commit(window->surface);
143                 window->wait_for_configure = true;
144         } else {
145                 assert(0);
146         }
147
148     window->surface_queue =
149         wayland_tbm_client_create_surface_queue(display->wl_tbm,
150                 window->surface,
151                 3,
152                 width,
153                 height,
154                 TBM_FORMAT_XRGB8888);
155     assert(window->surface_queue);
156
157         return window;
158 }
159
160 static void
161 destroy_window(struct window *window)
162 {
163     tbm_surface_queue_destroy(window->surface_queue);
164
165         if (window->callback)
166                 wl_callback_destroy(window->callback);
167
168         if (window->xdg_toplevel)
169                 xdg_toplevel_destroy(window->xdg_toplevel);
170         if (window->xdg_surface)
171                 xdg_surface_destroy(window->xdg_surface);
172         wl_surface_destroy(window->surface);
173         free(window);
174 }
175
176 static void
177 paint_pixels(void *image, int padding, int width, int height, uint32_t time)
178 {
179         const int halfh = padding + (height - padding * 2) / 2;
180         const int halfw = padding + (width  - padding * 2) / 2;
181         int ir, or;
182         uint32_t *pixel = image;
183         int y;
184
185         /* squared radii thresholds */
186         or = (halfw < halfh ? halfw : halfh) - 8;
187         ir = or - 32;
188         or *= or;
189         ir *= ir;
190
191         pixel += padding * width;
192         for (y = padding; y < height - padding; y++) {
193                 int x;
194                 int y2 = (y - halfh) * (y - halfh);
195
196                 pixel += padding;
197                 for (x = padding; x < width - padding; x++) {
198                         uint32_t v;
199
200                         /* squared distance from center */
201                         int r2 = (x - halfw) * (x - halfw) + y2;
202
203                         if (r2 < ir)
204                                 v = (r2 / 32 + time / 64) * 0x0080401;
205                         else if (r2 < or)
206                                 v = (y + time / 32) * 0x0080401;
207                         else
208                                 v = (x + time / 16) * 0x0080401;
209                         v &= 0x00ffffff;
210
211                         /* cross if compositor uses X from XRGB as alpha */
212                         if (abs(x - y) > 6 && abs(x + y - height) > 6)
213                                 v |= 0xff000000;
214
215                         *pixel++ = v;
216                 }
217
218                 pixel += padding;
219         }
220 }
221
222 static void
223 buffer_info_free_cb(void *data)
224 {
225     struct buffer_info *buffer_info = data;
226
227     if (!buffer_info)
228         return;
229
230     wayland_tbm_client_destroy_buffer(buffer_info->window->display->wl_tbm,
231             buffer_info->wl_buffer);
232     free(buffer_info);
233 }
234
235 static void
236 buffer_handle_release(void *data, struct wl_buffer *wl_buffer)
237 {
238     tbm_surface_h surface = data;
239     struct buffer_info *buffer_info;
240
241     tbm_surface_internal_get_user_data(surface, BUFFER_INFO_KEY,
242             (void **)&buffer_info);
243     if (buffer_info)
244         tbm_surface_queue_release(buffer_info->window->surface_queue, surface);
245 }
246
247 static const struct wl_buffer_listener buffer_listener = {
248     .release = buffer_handle_release,
249 };
250
251 static const struct wl_callback_listener frame_listener;
252
253 static void
254 redraw(void *data, struct wl_callback *callback, uint32_t time)
255 {
256         struct window *window = data;
257     struct buffer_info *buffer_info = NULL;
258     tbm_surface_h surface = NULL;
259     tbm_surface_info_s surface_info;
260
261     if (!tbm_surface_queue_can_dequeue(window->surface_queue, 0))
262         return;
263
264     tbm_surface_queue_dequeue(window->surface_queue, &surface);
265     assert(surface);
266
267     tbm_surface_internal_get_user_data(surface, BUFFER_INFO_KEY,
268             (void **)&buffer_info);
269     if (!buffer_info) {
270         buffer_info = calloc(1, sizeof *buffer_info);
271         assert(buffer_info);
272
273         tbm_surface_internal_add_user_data(surface, BUFFER_INFO_KEY, buffer_info_free_cb);
274         tbm_surface_internal_set_user_data(surface, BUFFER_INFO_KEY, buffer_info);
275
276         buffer_info->wl_buffer =
277             wayland_tbm_client_create_buffer(window->display->wl_tbm, surface);
278         assert(buffer_info->wl_buffer);
279
280         wl_buffer_add_listener(buffer_info->wl_buffer, &buffer_listener,
281                 surface);
282
283         buffer_info->window = window;
284     }
285
286     tbm_surface_map(surface, TBM_SURF_OPTION_WRITE, &surface_info);
287
288         paint_pixels(surface_info.planes[0].ptr, 20,
289             (surface_info.planes[0].stride/4), surface_info.height, time);
290
291     tbm_surface_unmap(surface);
292
293         wl_surface_attach(window->surface, buffer_info->wl_buffer, 0, 0);
294         wl_surface_damage(window->surface,
295                           20, 20, window->width - 40, window->height - 40);
296
297         if (callback)
298                 wl_callback_destroy(callback);
299
300         window->callback = wl_surface_frame(window->surface);
301         wl_callback_add_listener(window->callback, &frame_listener, window);
302         wl_surface_commit(window->surface);
303 }
304
305 static const struct wl_callback_listener frame_listener = {
306         redraw
307 };
308
309 static void
310 shm_format(void *data, struct wl_shm *wl_shm, uint32_t format)
311 {
312         struct display *d = data;
313
314         if (format == WL_SHM_FORMAT_XRGB8888)
315                 d->has_xrgb = true;
316 }
317
318 struct wl_shm_listener shm_listener = {
319         shm_format
320 };
321
322 static void
323 xdg_wm_base_ping(void *data, struct xdg_wm_base *shell, uint32_t serial)
324 {
325         xdg_wm_base_pong(shell, serial);
326 }
327
328 static const struct xdg_wm_base_listener xdg_wm_base_listener = {
329         xdg_wm_base_ping,
330 };
331
332 static void
333 registry_handle_global(void *data, struct wl_registry *registry,
334                        uint32_t id, const char *interface, uint32_t version)
335 {
336         struct display *d = data;
337
338         if (strcmp(interface, "wl_compositor") == 0) {
339                 d->compositor =
340                         wl_registry_bind(registry,
341                                          id, &wl_compositor_interface, 1);
342         } else if (strcmp(interface, "xdg_wm_base") == 0) {
343                 d->wm_base = wl_registry_bind(registry,
344                                               id, &xdg_wm_base_interface, 1);
345                 xdg_wm_base_add_listener(d->wm_base, &xdg_wm_base_listener, d);
346         } else if (strcmp(interface, "wl_shm") == 0) {
347                 d->shm = wl_registry_bind(registry,
348                                           id, &wl_shm_interface, 1);
349                 wl_shm_add_listener(d->shm, &shm_listener, d);
350         }
351 }
352
353 static void
354 registry_handle_global_remove(void *data, struct wl_registry *registry,
355                               uint32_t name)
356 {
357 }
358
359 static const struct wl_registry_listener registry_listener = {
360         registry_handle_global,
361         registry_handle_global_remove
362 };
363
364 static struct display *
365 create_display(void)
366 {
367         struct display *display;
368
369         display = calloc(1, sizeof *display);
370         if (display == NULL) {
371                 fprintf(stderr, "out of memory\n");
372                 exit(1);
373         }
374         display->display = wl_display_connect(NULL);
375         assert(display->display);
376
377         display->has_xrgb = false;
378         display->registry = wl_display_get_registry(display->display);
379         wl_registry_add_listener(display->registry,
380                                  &registry_listener, display);
381         wl_display_roundtrip(display->display);
382         if (display->shm == NULL) {
383                 fprintf(stderr, "No wl_shm global\n");
384                 exit(1);
385         }
386
387         wl_display_roundtrip(display->display);
388
389         /*
390          * Why do we need two roundtrips here?
391          *
392          * wl_display_get_registry() sends a request to the server, to which
393          * the server replies by emitting the wl_registry.global events.
394          * The first wl_display_roundtrip() sends wl_display.sync. The server
395          * first processes the wl_display.get_registry which includes sending
396          * the global events, and then processes the sync. Therefore when the
397          * sync (roundtrip) returns, we are guaranteed to have received and
398          * processed all the global events.
399          *
400          * While we are inside the first wl_display_roundtrip(), incoming
401          * events are dispatched, which causes registry_handle_global() to
402          * be called for each global. One of these globals is wl_shm.
403          * registry_handle_global() sends wl_registry.bind request for the
404          * wl_shm global. However, wl_registry.bind request is sent after
405          * the first wl_display.sync, so the reply to the sync comes before
406          * the initial events of the wl_shm object.
407          *
408          * The initial events that get sent as a reply to binding to wl_shm
409          * include wl_shm.format. These tell us which pixel formats are
410          * supported, and we need them before we can create buffers. They
411          * don't change at runtime, so we receive them as part of init.
412          *
413          * When the reply to the first sync comes, the server may or may not
414          * have sent the initial wl_shm events. Therefore we need the second
415          * wl_display_roundtrip() call here.
416          *
417          * The server processes the wl_registry.bind for wl_shm first, and
418          * the second wl_display.sync next. During our second call to
419          * wl_display_roundtrip() the initial wl_shm events are received and
420          * processed. Finally, when the reply to the second wl_display.sync
421          * arrives, it guarantees we have processed all wl_shm initial events.
422          *
423          * This sequence contains two examples on how wl_display_roundtrip()
424          * can be used to guarantee, that all reply events to a request
425          * have been received and processed. This is a general Wayland
426          * technique.
427          */
428
429         if (!display->has_xrgb) {
430                 fprintf(stderr, "WL_SHM_FORMAT_XRGB32 not available\n");
431                 exit(1);
432         }
433
434     display->wl_tbm = wayland_tbm_client_init(display->display);
435     if (!display->wl_tbm) {
436         fprintf(stderr, "failed wayland_tbm_client_init()\n");
437         exit(1);
438     }
439
440         return display;
441 }
442
443 static void
444 destroy_display(struct display *display)
445 {
446         if (display->shm)
447                 wl_shm_destroy(display->shm);
448
449         if (display->wm_base)
450                 xdg_wm_base_destroy(display->wm_base);
451
452         if (display->compositor)
453                 wl_compositor_destroy(display->compositor);
454
455     wayland_tbm_client_deinit(display->wl_tbm);
456         wl_registry_destroy(display->registry);
457         wl_display_flush(display->display);
458         wl_display_disconnect(display->display);
459         free(display);
460 }
461
462 static void
463 signal_int(int signum)
464 {
465         running = 0;
466 }
467
468 int
469 main(int argc, char **argv)
470 {
471         struct sigaction sigint;
472         struct display *display;
473         struct window *window;
474         int ret = 0;
475
476         display = create_display();
477         window = create_window(display, 250, 250);
478         if (!window)
479                 return 1;
480
481         sigint.sa_handler = signal_int;
482         sigemptyset(&sigint.sa_mask);
483         sigint.sa_flags = SA_RESETHAND;
484         sigaction(SIGINT, &sigint, NULL);
485
486         /* Initialise damage to full surface, so the padding gets painted */
487         wl_surface_damage(window->surface, 0, 0,
488                           window->width, window->height);
489
490         if (!window->wait_for_configure)
491                 redraw(window, NULL, 0);
492
493         while (running && ret != -1)
494                 ret = wl_display_dispatch(display->display);
495
496         fprintf(stderr, "simple-shm exiting\n");
497
498         destroy_window(window);
499         destroy_display(display);
500
501         return 0;
502 }