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