ds_output: Add ds_output_set_custom_mode
[platform/core/uifw/libds-tizen.git] / src / examples / tinyds.c
1 #include "pixman-helper.h"
2
3 #include <assert.h>
4 #include <stdbool.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <signal.h>
8 #include <time.h>
9
10 #include <drm_fourcc.h>
11 #include <pixman.h>
12 #include <wayland-server.h>
13 #include <libds/log.h>
14 #include <libds/backend.h>
15 #include <libds/allocator/shm.h>
16 #include <libds/backend/wayland.h>
17 #include <libds/swapchain.h>
18 #include <libds/compositor.h>
19 #include <libds/xdg_shell.h>
20 #include <libds/input_device.h>
21 #include <libds/keyboard.h>
22 #include <libds/touch.h>
23 #include <libds/pointer.h>
24
25 #define TINYDS_UNUSED   __attribute__((unused))
26
27 #define OUTPUT_WIDTH   1280
28 #define OUTPUT_HEIGHT  720
29
30 struct tinyds_server;
31
32 struct tinyds_pointer
33 {
34     struct ds_input_device *dev;
35     struct tinyds_server *server;
36
37     struct wl_listener destroy;
38     struct wl_listener motion;
39     struct wl_listener motion_absolute;
40     struct wl_listener button;
41     struct wl_listener frame;
42 };
43
44 struct tinyds_keyboard
45 {
46     struct ds_input_device *dev;
47     struct tinyds_server *server;
48
49     struct wl_listener destroy;
50     struct wl_listener key;
51 };
52
53 struct tinyds_touch
54 {
55     struct ds_input_device *dev;
56     struct tinyds_server *server;
57
58     struct wl_listener destroy;
59     struct wl_listener down;
60     struct wl_listener up;
61     struct wl_listener motion;
62 };
63
64 struct tinyds_output
65 {
66     struct tinyds_server *server;
67     struct ds_output *ds_output;
68     struct ds_allocator *allocator;
69     struct ds_swapchain *swapchain;
70
71     struct wl_listener output_destroy;
72     struct wl_listener output_frame;
73
74     int width, height;
75
76     bool drawable;
77     bool damaged;
78 };
79
80 struct tinyds_server
81 {
82     struct wl_display *display;
83
84     struct ds_backend *backend;
85     struct ds_compositor *compositor;
86     struct ds_xdg_shell *xdg_shell;
87
88     struct tinyds_output output;
89
90     struct wl_list views;
91
92     struct wl_listener new_output;
93     struct wl_listener new_input;
94     struct wl_listener new_xdg_surface;
95 };
96
97 struct tinyds_view
98 {
99     struct tinyds_server *server;
100
101     struct ds_xdg_surface *xdg_surface;
102
103     struct wl_listener xdg_surface_map;
104     struct wl_listener xdg_surface_unmap;
105     struct wl_listener xdg_surface_destroy;
106     struct wl_listener surface_commit;
107     struct wl_list link; // tinyds_server::views
108
109     int x, y;
110     bool mapped;
111 };
112
113 static bool server_init(struct tinyds_server *server,
114         struct wl_display *display);
115 static void server_fini(struct tinyds_server *server);
116 static void server_add_view(struct tinyds_server *server,
117         struct ds_xdg_surface *xdg_surface);
118 static bool output_init(struct tinyds_output *output,
119         struct tinyds_server *server, struct ds_output *ds_output,
120         int width, int height);
121 static void output_fini(struct tinyds_output *output);
122 static void output_damage(struct tinyds_output *output);
123 static void output_redraw(struct tinyds_output *output);
124 static void view_destroy(struct tinyds_view *view);
125 static void view_composite(struct tinyds_view *view,
126         pixman_image_t *dst_image);
127
128 int
129 main(void)
130 {
131     struct tinyds_server server;
132     struct wl_display *display;
133     const char *socket;
134
135     ds_log_init(DS_DBG, NULL);
136
137     display = wl_display_create();
138     assert(display);
139
140     assert(server_init(&server, display) == true);
141
142     socket = wl_display_add_socket_auto(display);
143     assert(socket);
144
145     ds_backend_start(server.backend);
146
147     output_damage(&server.output);
148     output_redraw(&server.output);
149
150     setenv("WAYLAND_DISPLAY", socket, true);
151
152     ds_inf("Running Wayland compositor on WAYLAND_DISPLAY=%s", socket);
153
154     wl_display_run(server.display);
155
156     server_fini(&server);
157     wl_display_destroy(display);
158
159     return 0;
160 }
161
162 static struct ds_backend *
163 create_wl_backend(struct wl_display *display)
164 {
165     if (!getenv("WAYLAND_DISPLAY") && !getenv("WAYLAND_SOCKET"))
166         return NULL;
167
168     return ds_wl_backend_create(display, NULL);
169 }
170
171 static void
172 server_handle_new_output(struct wl_listener *listener, void *data)
173 {
174     struct tinyds_server *server;
175     struct ds_output *ds_output = data;
176
177     server = wl_container_of(listener, server, new_output);
178
179     assert(output_init(&server->output, server, ds_output,
180             OUTPUT_WIDTH, OUTPUT_HEIGHT) == true);
181 }
182
183 static void
184 keyboard_handle_device_destroy(struct wl_listener *listener, void *data)
185 {
186     struct tinyds_keyboard *kbd;
187
188     kbd = wl_container_of(listener, kbd, destroy);
189
190     ds_inf("Keyboard(%p) destroyed", kbd);
191
192     wl_list_remove(&kbd->destroy.link);
193     wl_list_remove(&kbd->key.link);
194
195     free(kbd);
196 }
197
198 static bool
199 server_handle_keybinding(struct tinyds_server *server, xkb_keysym_t sym)
200 {
201     switch (sym) {
202         case XKB_KEY_BackSpace:
203             wl_display_terminate(server->display);
204             break;
205         default:
206             return false;
207     }
208
209     return true;
210 }
211
212 static void
213 keyboard_handle_key(struct wl_listener *listener, void *data)
214 {
215     struct tinyds_keyboard *kbd;
216     struct ds_event_keyboard_key *event = data;
217     struct ds_keyboard *ds_keyboard;
218     struct xkb_state *xkb_state;
219     const xkb_keysym_t *syms;
220     uint32_t modifiers;
221     int nsyms;
222
223     kbd = wl_container_of(listener, kbd, key);
224
225     ds_keyboard = ds_input_device_get_keyboard(kbd->dev);
226
227     modifiers = ds_keyboard_get_modifiers(ds_keyboard);
228     if ((modifiers & DS_MODIFIER_CTRL) &&
229             (modifiers & DS_MODIFIER_ALT) &&
230             (modifiers & DS_MODIFIER_SHIFT) &&
231             event->state == WL_KEYBOARD_KEY_STATE_PRESSED) {
232         xkb_state = ds_keyboard_get_xkb_state(ds_keyboard);
233         if (xkb_state) {
234             nsyms = xkb_state_key_get_syms(xkb_state, event->keycode + 8,
235                     &syms);
236             for (int i = 0; i < nsyms; i++) {
237                 server_handle_keybinding(kbd->server, syms[i]);
238             }
239         }
240     }
241 }
242
243 static void
244 server_add_keyboard(struct tinyds_server *server, struct ds_input_device *dev)
245 {
246     struct tinyds_keyboard *kbd;
247
248     kbd = calloc(1, sizeof *kbd);
249     assert(kbd);
250
251     kbd->dev = dev;
252     kbd->server = server;
253
254     kbd->destroy.notify = keyboard_handle_device_destroy;
255     ds_input_device_add_destroy_listener(dev, &kbd->destroy);
256
257     kbd->key.notify = keyboard_handle_key;
258     ds_keyboard_add_key_listener(ds_input_device_get_keyboard(dev), &kbd->key);
259
260     ds_inf("Keyboard(%p) added", kbd);
261 }
262
263 static void
264 touch_handle_device_destroy(struct wl_listener *listener, void *data)
265 {
266     struct tinyds_touch *touch;
267
268     touch = wl_container_of(listener, touch, destroy);
269
270     ds_inf("Touch(%p) destroyed", touch);
271
272     wl_list_remove(&touch->destroy.link);
273     wl_list_remove(&touch->down.link);
274     wl_list_remove(&touch->up.link);
275     wl_list_remove(&touch->motion.link);
276
277     free(touch);
278 }
279
280 static void
281 touch_handle_down(struct wl_listener *listener, void *data)
282 {
283     ds_inf("Touch device(%p): down", data);
284 }
285
286 static void
287 touch_handle_up(struct wl_listener *listener, void *data)
288 {
289     ds_inf("Touch device(%p): up", data);
290 }
291
292 static void
293 touch_handle_motion(struct wl_listener *listener, void *data)
294 {
295     ds_inf("Touch device(%p): motion", data);
296 }
297
298 static void
299 server_add_touch(struct tinyds_server *server, struct ds_input_device *dev)
300 {
301     struct tinyds_touch *touch;
302
303     touch = calloc(1, sizeof *touch);
304     assert(touch);
305
306     touch->dev = dev;
307     touch->server = server;
308
309     touch->destroy.notify = touch_handle_device_destroy;
310     ds_input_device_add_destroy_listener(dev, &touch->destroy);
311
312     touch->down.notify = touch_handle_down;
313     ds_touch_add_down_listener(ds_input_device_get_touch(dev), &touch->down);
314
315     touch->up.notify = touch_handle_up;
316     ds_touch_add_up_listener(ds_input_device_get_touch(dev), &touch->up);
317
318     touch->motion.notify = touch_handle_motion;
319     ds_touch_add_motion_listener(ds_input_device_get_touch(dev), &touch->motion);
320
321     ds_inf("Touch(%p) added", touch);
322 }
323
324 static void
325 pointer_handle_device_destroy(struct wl_listener *listener, void *data)
326 {
327     struct tinyds_pointer *pointer;
328
329     pointer = wl_container_of(listener, pointer, destroy);
330
331     ds_inf("Pointer(%p) destroyed", pointer);
332
333     wl_list_remove(&pointer->destroy.link);
334     wl_list_remove(&pointer->motion.link);
335     wl_list_remove(&pointer->motion_absolute.link);
336     wl_list_remove(&pointer->button.link);
337     wl_list_remove(&pointer->frame.link);
338
339     free(pointer);
340 }
341
342 static void
343 pointer_handle_motion(struct wl_listener *listener, void *data)
344 {
345     struct tinyds_pointer *pointer;
346
347     pointer = wl_container_of(listener, pointer, motion);
348
349     ds_inf("Pointer(%p) motion", pointer);
350 }
351
352 static void
353 pointer_handle_motion_absolute(struct wl_listener *listener, void *data)
354 {
355     struct tinyds_pointer *pointer;
356     struct ds_event_pointer_motion_absolute *event = data;
357
358     pointer = wl_container_of(listener, pointer, motion_absolute);
359
360     ds_inf("Pointer(%p) motion absolute: (x %f y %f) time(%d)",
361             pointer, event->x, event->y, event->time_msec);
362 }
363
364 static void
365 pointer_handle_button(struct wl_listener *listener, void *data)
366 {
367     struct tinyds_pointer *pointer;
368     struct ds_event_pointer_button *event = data;
369
370     pointer = wl_container_of(listener, pointer, button);
371
372     ds_inf("Pointer(%p) button(%d): state(%s) time(%d)",
373             pointer, event->button,
374             (event->state == DS_BUTTON_PRESSED) ? "Pressed" : "Released",
375             event->time_msec);
376 }
377
378 static void
379 pointer_handle_frame(struct wl_listener *listener, void *data)
380 {
381     struct tinyds_pointer *pointer;
382
383     pointer = wl_container_of(listener, pointer, frame);
384
385     ds_inf("Pointer(%p) frame", pointer);
386 }
387
388 static void
389 server_add_pointer(struct tinyds_server *server, struct ds_input_device *dev)
390 {
391     struct tinyds_pointer *pointer;
392
393     pointer = calloc(1, sizeof *pointer);
394     assert(pointer);
395
396     pointer->dev = dev;
397     pointer->server = server;
398
399     pointer->destroy.notify = pointer_handle_device_destroy;
400     ds_input_device_add_destroy_listener(dev, &pointer->destroy);
401
402     pointer->motion.notify = pointer_handle_motion;
403     ds_pointer_add_motion_listener(ds_input_device_get_pointer(dev),
404             &pointer->motion);
405
406     pointer->motion_absolute.notify = pointer_handle_motion_absolute;
407     ds_pointer_add_motion_absolute_listener(ds_input_device_get_pointer(dev),
408             &pointer->motion_absolute);
409
410     pointer->button.notify = pointer_handle_button;
411     ds_pointer_add_button_listener(ds_input_device_get_pointer(dev),
412             &pointer->button);
413
414     pointer->frame.notify = pointer_handle_frame;
415     ds_pointer_add_frame_listener(ds_input_device_get_pointer(dev),
416             &pointer->frame);
417
418     ds_inf("Pointer(%p) added", pointer);
419 }
420
421 static void
422 server_handle_new_input(struct wl_listener *listener, void *data)
423 {
424     struct tinyds_server *server;
425     struct ds_input_device *dev = data;
426     enum ds_input_device_type dev_type;
427
428     server = wl_container_of(listener, server, new_input);
429
430     dev_type = ds_input_device_get_type(dev);
431     switch (dev_type) {
432         case DS_INPUT_DEVICE_KEYBOARD:
433             server_add_keyboard(server, dev);
434             break;
435         case DS_INPUT_DEVICE_TOUCH:
436             server_add_touch(server, dev);
437             break;
438         case DS_INPUT_DEVICE_POINTER:
439             server_add_pointer(server, dev);
440             break;
441         default:
442             ds_err("Unknown type(%d) of ds_input_device", dev_type);
443             break;
444     }
445 }
446
447 static void
448 view_handle_xdg_surface_map(struct wl_listener *listener,
449         void *data TINYDS_UNUSED)
450 {
451     struct tinyds_view *view;
452
453     view = wl_container_of(listener, view, xdg_surface_map);
454     view->mapped = true;
455 }
456
457 static void
458 view_handle_xdg_surface_unmap(struct wl_listener *listener,
459         void *data TINYDS_UNUSED)
460 {
461     struct tinyds_view *view;
462
463     view = wl_container_of(listener, view, xdg_surface_unmap);
464     view->mapped = false;
465 }
466
467 static void
468 view_handle_xdg_surface_destroy(struct wl_listener *listener,
469         void *data TINYDS_UNUSED) 
470 {
471     struct tinyds_view *view;
472
473     view = wl_container_of(listener, view, xdg_surface_destroy);
474
475     output_damage(&view->server->output);
476     output_redraw(&view->server->output);
477
478     view_destroy(view);
479 }
480
481 static void
482 view_handle_surface_commit(struct wl_listener *listener,
483         void *data TINYDS_UNUSED)
484 {
485     struct tinyds_view *view;
486
487     view = wl_container_of(listener, view, surface_commit);
488
489     output_damage(&view->server->output);
490     output_redraw(&view->server->output);
491 }
492
493 static void
494 server_new_xdg_surface(struct wl_listener *listener, void *data)
495 {
496     struct tinyds_server *server;
497
498     server = wl_container_of(listener, server, new_xdg_surface);
499
500     server_add_view(server, (struct ds_xdg_surface *)data);
501 }
502
503 static bool
504 server_init(struct tinyds_server *server, struct wl_display *display)
505 {
506     server->display = display;
507
508     wl_list_init(&server->views);
509
510     if (wl_display_init_shm(display) != 0)
511         return false;
512
513     server->backend = create_wl_backend(display);
514     if (!server->backend)
515         return false;
516
517     ds_wl_backend_create_output(server->backend);
518
519     server->new_input.notify = server_handle_new_input;
520     ds_backend_add_new_input_listener(server->backend, &server->new_input);
521
522     server->new_output.notify = server_handle_new_output;
523     ds_backend_add_new_output_listener(server->backend, &server->new_output);
524
525     server->compositor = ds_compositor_create(display);
526     if (!server->compositor)
527         goto err;
528
529     server->xdg_shell = ds_xdg_shell_create(display);
530     if (!server->xdg_shell)
531         goto err;
532
533     server->new_xdg_surface.notify = server_new_xdg_surface;
534     ds_xdg_shell_add_new_surface_listener(server->xdg_shell,
535             &server->new_xdg_surface);
536
537     return true;
538
539 err:
540     ds_backend_destroy(server->backend);
541
542     return false;
543 }
544
545 static void
546 server_fini(struct tinyds_server *server)
547 {
548     struct tinyds_view *view, *tmp;
549
550     wl_list_for_each_safe(view, tmp, &server->views, link)
551         view_destroy(view);
552
553     output_fini(&server->output);
554
555     wl_list_remove(&server->new_xdg_surface.link);
556 }
557
558 static void
559 output_handle_destroy(struct wl_listener *listener, void *data TINYDS_UNUSED)
560 {
561     struct tinyds_output *output =
562         wl_container_of(listener, output, output_destroy);
563
564     wl_list_remove(&output->output_destroy.link);
565     wl_list_remove(&output->output_frame.link);
566     output->ds_output = NULL;
567
568     wl_display_terminate(output->server->display);
569 }
570
571 static void
572 output_handle_frame(struct wl_listener *listener, void *data TINYDS_UNUSED)
573 {
574     struct tinyds_output *output =
575         wl_container_of(listener, output, output_frame);
576
577     output->drawable = true;
578     output_redraw(output);
579 }
580
581 static bool
582 output_init(struct tinyds_output *output, struct tinyds_server *server,
583         struct ds_output *ds_output, int width, int height)
584 {
585     output->server = server;
586     output->ds_output = ds_output;
587     output->width = width;
588     output->height = height;
589     output->drawable = true;
590
591     ds_output_set_custom_mode(ds_output, OUTPUT_WIDTH, OUTPUT_HEIGHT, 0);
592
593     output->allocator = ds_shm_allocator_create();
594     if (!output->allocator)
595         return false;
596
597     output->swapchain = ds_swapchain_create(output->allocator,
598             width, height, DRM_FORMAT_XRGB8888);
599     if (!output->swapchain)
600         goto err_swapchain;
601
602     output->output_destroy.notify = output_handle_destroy;
603     ds_output_add_destroy_listener(output->ds_output, &output->output_destroy);
604
605     output->output_frame.notify = output_handle_frame;
606     ds_output_add_frame_listener(output->ds_output, &output->output_frame);
607
608     return true;
609
610 err_swapchain:
611     ds_allocator_destroy(output->allocator);
612
613     return false;
614 }
615
616 static void
617 output_fini(struct tinyds_output *output)
618 {
619     ds_output_destroy(output->ds_output);
620     ds_swapchain_destroy(output->swapchain);
621     ds_allocator_destroy(output->allocator);
622 }
623
624 static void
625 output_damage(struct tinyds_output *output)
626 {
627     output->damaged = true;
628 }
629
630 static void
631 output_redraw(struct tinyds_output *output)
632 {
633     struct ds_buffer *output_buffer;
634     pixman_image_t *output_image;
635     struct tinyds_view *view;
636
637     if (!output->drawable || !output->damaged)
638         return;
639
640     output_buffer = ds_swapchain_acquire(output->swapchain, NULL);
641     if (!output_buffer)
642         return;
643
644     output_image = pixman_image_from_buffer(output_buffer,
645             DS_BUFFER_DATA_PTR_ACCESS_WRITE);
646     if (!output_image)
647         goto out;
648
649     pixman_image_fill_color(output_image, 80, 80, 80);
650
651     wl_list_for_each(view, &output->server->views, link) {
652         if (!view->mapped)
653             continue;
654         view_composite(view, output_image);
655     }
656     pixman_image_unref(output_image);
657
658     ds_output_attach_buffer(output->ds_output, output_buffer);
659     ds_output_commit(output->ds_output);
660
661     output->drawable = false;
662     output->damaged = false;
663
664 out:
665     ds_buffer_unlock(output_buffer);
666 }
667
668 static void
669 server_add_view(struct tinyds_server *server, struct ds_xdg_surface *xdg_surface)
670 {
671     struct tinyds_view *view;
672
673     view = calloc(1, sizeof *view);
674     view->server = server;
675     view->xdg_surface = xdg_surface;
676
677     view->xdg_surface_map.notify = view_handle_xdg_surface_map;
678     ds_xdg_surface_add_map_listener(xdg_surface,
679             &view->xdg_surface_map);
680
681     view->xdg_surface_unmap.notify = view_handle_xdg_surface_unmap;
682     ds_xdg_surface_add_unmap_listener(xdg_surface,
683             &view->xdg_surface_unmap);
684
685     view->xdg_surface_destroy.notify = view_handle_xdg_surface_destroy;
686     ds_xdg_surface_add_destroy_listener(xdg_surface,
687             &view->xdg_surface_destroy);
688
689     view->surface_commit.notify = view_handle_surface_commit;
690     ds_surface_add_commit_listener(
691             ds_xdg_surface_get_surface(xdg_surface),
692             &view->surface_commit);
693
694     wl_list_insert(server->views.prev, &view->link);
695
696     ds_inf("View(%p) added", view);
697 }
698
699 static void
700 view_destroy(struct tinyds_view *view)
701 {
702     ds_inf("View(%p) destroyed", view);
703
704     wl_list_remove(&view->xdg_surface_destroy.link);
705     wl_list_remove(&view->xdg_surface_map.link);
706     wl_list_remove(&view->xdg_surface_unmap.link);
707     wl_list_remove(&view->surface_commit.link);
708     wl_list_remove(&view->link);
709     free(view);
710 }
711
712 static void
713 view_send_frame_done(struct tinyds_view *view)
714 {
715     struct timespec now;
716
717     clock_gettime(CLOCK_MONOTONIC, &now);
718     ds_surface_send_frame_done(ds_xdg_surface_get_surface(view->xdg_surface),
719             &now);
720 }
721
722 static void
723 view_composite(struct tinyds_view *view, pixman_image_t *dst_image)
724 {
725     struct ds_buffer *buffer;
726     pixman_image_t *src_image;
727
728     buffer = ds_surface_get_buffer(
729             ds_xdg_surface_get_surface(view->xdg_surface));
730     if (!buffer)
731         return;
732
733     src_image = pixman_image_from_buffer(buffer,
734             DS_BUFFER_DATA_PTR_ACCESS_READ);
735     pixman_image_composite32(PIXMAN_OP_OVER,
736             src_image,
737             NULL,
738             dst_image,
739             0, 0, 0, 0, 0, 0,
740             pixman_image_get_width(src_image),
741             pixman_image_get_height(src_image));
742     pixman_image_unref(src_image);
743
744     view_send_frame_done(view);
745 }