wlt: fix shl_hook API changes
[platform/upstream/kmscon.git] / src / uterm_video.c
1 /*
2  * uterm - Linux User-Space Terminal
3  *
4  * Copyright (c) 2011-2013 David Herrmann <dh.herrmann@googlemail.com>
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining
7  * a copy of this software and associated documentation files
8  * (the "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sublicense, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included
15  * in all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
21  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24  */
25
26 /*
27  * Video Control
28  * Core Implementation of the uterm_video and uterm_display objects.
29  */
30
31 #include <errno.h>
32 #include <inttypes.h>
33 #include <stdbool.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <unistd.h>
37 #include "eloop.h"
38 #include "log.h"
39 #include "shl_dlist.h"
40 #include "shl_hook.h"
41 #include "uterm_video.h"
42 #include "uterm_video_internal.h"
43
44 #define LOG_SUBSYSTEM "video"
45
46 const char *uterm_dpms_to_name(int dpms)
47 {
48         switch (dpms) {
49         case UTERM_DPMS_ON:
50                 return "ON";
51         case UTERM_DPMS_STANDBY:
52                 return "STANDBY";
53         case UTERM_DPMS_SUSPEND:
54                 return "SUSPEND";
55         case UTERM_DPMS_OFF:
56                 return "OFF";
57         default:
58                 return "UNKNOWN";
59         }
60 }
61
62 bool uterm_video_available(const struct uterm_video_module *mod)
63 {
64         if (!mod)
65                 return false;
66
67         if (mod == UTERM_VIDEO_DRM2D || mod == UTERM_VIDEO_DRM3D)
68                 return video_drm_available();
69
70         return true;
71 }
72
73 int mode_new(struct uterm_mode **out, const struct mode_ops *ops)
74 {
75         struct uterm_mode *mode;
76         int ret;
77
78         if (!out || !ops)
79                 return -EINVAL;
80
81         mode = malloc(sizeof(*mode));
82         if (!mode)
83                 return -ENOMEM;
84         memset(mode, 0, sizeof(*mode));
85         mode->ref = 1;
86         mode->ops = ops;
87
88         ret = VIDEO_CALL(mode->ops->init, 0, mode);
89         if (ret)
90                 goto err_free;
91
92         *out = mode;
93         return 0;
94
95 err_free:
96         free(mode);
97         return ret;
98 }
99
100 void uterm_mode_ref(struct uterm_mode *mode)
101 {
102         if (!mode || !mode->ref)
103                 return;
104
105         ++mode->ref;
106 }
107
108 void uterm_mode_unref(struct uterm_mode *mode)
109 {
110         if (!mode || !mode->ref || --mode->ref)
111                 return;
112
113         VIDEO_CALL(mode->ops->destroy, 0, mode);
114         free(mode);
115 }
116
117 int uterm_mode_bind(struct uterm_mode *mode, struct uterm_display *disp)
118 {
119         if (!mode || !disp || mode->disp)
120                 return -EINVAL;
121
122         mode->disp = disp;
123         shl_dlist_link_tail(&disp->modes, &mode->list);
124         uterm_mode_ref(mode);
125
126         return 0;
127 }
128
129 void uterm_mode_unbind(struct uterm_mode *mode)
130 {
131         if (!mode)
132                 return;
133
134         mode->disp = NULL;
135         shl_dlist_unlink(&mode->list);
136         uterm_mode_unref(mode);
137 }
138
139 struct uterm_mode *uterm_mode_next(struct uterm_mode *mode)
140 {
141         if (!mode || mode->list.next == &mode->disp->modes)
142                 return NULL;
143
144         return shl_dlist_entry(mode->list.next, struct uterm_mode, list);
145 }
146
147 const char *uterm_mode_get_name(const struct uterm_mode *mode)
148 {
149         if (!mode)
150                 return NULL;
151
152         return VIDEO_CALL(mode->ops->get_name, NULL, mode);
153 }
154
155 unsigned int uterm_mode_get_width(const struct uterm_mode *mode)
156 {
157         if (!mode)
158                 return 0;
159
160         return VIDEO_CALL(mode->ops->get_width, 0, mode);
161 }
162
163 unsigned int uterm_mode_get_height(const struct uterm_mode *mode)
164 {
165         if (!mode)
166                 return 0;
167
168         return VIDEO_CALL(mode->ops->get_height, 0, mode);
169 }
170
171 int display_schedule_vblank_timer(struct uterm_display *disp)
172 {
173         int ret;
174
175         if (disp->vblank_scheduled)
176                 return 0;
177
178         ret = ev_timer_update(disp->vblank_timer, &disp->vblank_spec);
179         if (ret)
180                 return ret;
181
182         disp->vblank_scheduled = true;
183         return 0;
184 }
185
186 void display_set_vblank_timer(struct uterm_display *disp,
187                               unsigned int msecs)
188 {
189         if (msecs >= 1000)
190                 msecs = 999;
191         else if (msecs == 0)
192                 msecs = 15;
193
194         disp->vblank_spec.it_value.tv_nsec = msecs * 1000 * 1000;
195 }
196
197 static void display_vblank_timer_event(struct ev_timer *timer,
198                                        uint64_t expirations,
199                                        void *data)
200 {
201         struct uterm_display *disp = data;
202
203         disp->vblank_scheduled = false;
204         DISPLAY_CB(disp, UTERM_PAGE_FLIP);
205 }
206
207 int display_new(struct uterm_display **out, const struct display_ops *ops)
208 {
209         struct uterm_display *disp;
210         int ret;
211
212         if (!out || !ops)
213                 return -EINVAL;
214
215         disp = malloc(sizeof(*disp));
216         if (!disp)
217                 return -ENOMEM;
218         memset(disp, 0, sizeof(*disp));
219         disp->ref = 1;
220         disp->ops = ops;
221         shl_dlist_init(&disp->modes);
222
223         log_info("new display %p", disp);
224
225         ret = shl_hook_new(&disp->hook);
226         if (ret)
227                 goto err_free;
228
229         disp->vblank_spec.it_value.tv_nsec = 15 * 1000 * 1000;
230
231         ret = ev_timer_new(&disp->vblank_timer, NULL,
232                            display_vblank_timer_event, disp, NULL);
233         if (ret)
234                 goto err_hook;
235
236         ret = VIDEO_CALL(disp->ops->init, 0, disp);
237         if (ret)
238                 goto err_timer;
239
240         *out = disp;
241         return 0;
242
243 err_timer:
244         ev_timer_unref(disp->vblank_timer);
245 err_hook:
246         shl_hook_free(disp->hook);
247 err_free:
248         free(disp);
249         return ret;
250 }
251
252 void uterm_display_ref(struct uterm_display *disp)
253 {
254         if (!disp || !disp->ref)
255                 return;
256
257         ++disp->ref;
258 }
259
260 void uterm_display_unref(struct uterm_display *disp)
261 {
262         struct uterm_mode *mode;
263
264         if (!disp || !disp->ref || --disp->ref)
265                 return;
266
267         log_info("free display %p", disp);
268
269         while (!shl_dlist_empty(&disp->modes)) {
270                 mode = shl_dlist_entry(disp->modes.prev, struct uterm_mode,
271                                        list);
272                 uterm_mode_unbind(mode);
273         }
274
275         VIDEO_CALL(disp->ops->destroy, 0, disp);
276         ev_timer_unref(disp->vblank_timer);
277         shl_hook_free(disp->hook);
278         free(disp);
279 }
280
281 int uterm_display_bind(struct uterm_display *disp, struct uterm_video *video)
282 {
283         int ret;
284
285         if (!disp || !video || disp->video)
286                 return -EINVAL;
287
288         ret = ev_eloop_add_timer(video->eloop, disp->vblank_timer);
289         if (ret)
290                 return ret;
291
292         shl_dlist_link_tail(&video->displays, &disp->list);
293         disp->video = video;
294         uterm_display_ref(disp);
295         VIDEO_CB(disp->video, disp, UTERM_NEW);
296
297         return 0;
298 }
299
300 void uterm_display_unbind(struct uterm_display *disp)
301 {
302         if (!disp || !disp->video)
303                 return;
304
305         VIDEO_CB(disp->video, disp, UTERM_GONE);
306         uterm_display_deactivate(disp);
307         disp->video = NULL;
308         shl_dlist_unlink(&disp->list);
309         ev_eloop_rm_timer(disp->vblank_timer);
310         uterm_display_unref(disp);
311 }
312
313 struct uterm_display *uterm_display_next(struct uterm_display *disp)
314 {
315         if (!disp || !disp->video || disp->list.next == &disp->video->displays)
316                 return NULL;
317
318         return shl_dlist_entry(disp->list.next, struct uterm_display, list);
319 }
320
321 int uterm_display_register_cb(struct uterm_display *disp, uterm_display_cb cb,
322                               void *data)
323 {
324         if (!disp)
325                 return -EINVAL;
326
327         return shl_hook_add_cast(disp->hook, cb, data, false);
328 }
329
330 void uterm_display_unregister_cb(struct uterm_display *disp,
331                                  uterm_display_cb cb, void *data)
332 {
333         if (!disp)
334                 return;
335
336         shl_hook_rm_cast(disp->hook, cb, data);
337 }
338
339 struct uterm_mode *uterm_display_get_modes(struct uterm_display *disp)
340 {
341         if (!disp || shl_dlist_empty(&disp->modes))
342                 return NULL;
343
344         return shl_dlist_entry(disp->modes.next, struct uterm_mode, list);
345 }
346
347 struct uterm_mode *uterm_display_get_current(struct uterm_display *disp)
348 {
349         if (!disp)
350                 return NULL;
351
352         return disp->current_mode;
353 }
354
355 struct uterm_mode *uterm_display_get_default(struct uterm_display *disp)
356 {
357         if (!disp)
358                 return NULL;
359
360         return disp->default_mode;
361 }
362
363 int uterm_display_get_state(struct uterm_display *disp)
364 {
365         if (!disp)
366                 return UTERM_DISPLAY_GONE;
367
368         if (disp->video) {
369                 if (disp->flags & DISPLAY_ONLINE) {
370                         if (disp->video->flags & VIDEO_AWAKE)
371                                 return UTERM_DISPLAY_ACTIVE;
372                         else
373                                 return UTERM_DISPLAY_ASLEEP;
374                 } else {
375                         return UTERM_DISPLAY_INACTIVE;
376                 }
377         } else {
378                 return UTERM_DISPLAY_GONE;
379         }
380 }
381
382 int uterm_display_activate(struct uterm_display *disp, struct uterm_mode *mode)
383 {
384         if (!disp || !disp->video || display_is_online(disp) ||
385             !video_is_awake(disp->video))
386                 return -EINVAL;
387
388         if (!mode)
389                 mode = disp->default_mode;
390
391         return VIDEO_CALL(disp->ops->activate, 0, disp, mode);
392 }
393
394 void uterm_display_deactivate(struct uterm_display *disp)
395 {
396         if (!disp || !display_is_online(disp))
397                 return;
398
399         VIDEO_CALL(disp->ops->deactivate, 0, disp);
400 }
401
402 int uterm_display_set_dpms(struct uterm_display *disp, int state)
403 {
404         if (!disp || !display_is_online(disp) || !video_is_awake(disp->video))
405                 return -EINVAL;
406
407         return VIDEO_CALL(disp->ops->set_dpms, 0, disp, state);
408 }
409
410 int uterm_display_get_dpms(const struct uterm_display *disp)
411 {
412         if (!disp || !disp->video)
413                 return UTERM_DPMS_OFF;
414
415         return disp->dpms;
416 }
417
418 int uterm_display_use(struct uterm_display *disp, bool *opengl)
419 {
420         if (!disp || !display_is_online(disp))
421                 return -EINVAL;
422
423         return VIDEO_CALL(disp->ops->use, -EOPNOTSUPP, disp, opengl);
424 }
425
426 int uterm_display_get_buffers(struct uterm_display *disp,
427                               struct uterm_video_buffer *buffer,
428                               unsigned int formats)
429 {
430         if (!disp || !display_is_online(disp) || !buffer)
431                 return -EINVAL;
432
433         return VIDEO_CALL(disp->ops->get_buffers, -EOPNOTSUPP, disp, buffer,
434                           formats);
435 }
436
437 int uterm_display_swap(struct uterm_display *disp, bool immediate)
438 {
439         if (!disp || !display_is_online(disp) || !video_is_awake(disp->video))
440                 return -EINVAL;
441
442         return VIDEO_CALL(disp->ops->swap, 0, disp, immediate);
443 }
444
445 bool uterm_display_is_swapping(struct uterm_display *disp)
446 {
447         if (!disp)
448                 return false;
449
450         return disp->vblank_scheduled || (disp->flags & DISPLAY_VSYNC);
451 }
452
453 int uterm_display_fill(struct uterm_display *disp,
454                        uint8_t r, uint8_t g, uint8_t b,
455                        unsigned int x, unsigned int y,
456                        unsigned int width, unsigned int height)
457 {
458         if (!disp || !display_is_online(disp) || !video_is_awake(disp->video))
459                 return -EINVAL;
460
461         return VIDEO_CALL(disp->ops->fill, -EOPNOTSUPP, disp, r, g, b, x, y,
462                           width, height);
463 }
464
465 int uterm_display_blit(struct uterm_display *disp,
466                        const struct uterm_video_buffer *buf,
467                        unsigned int x, unsigned int y)
468 {
469         if (!disp || !display_is_online(disp) || !video_is_awake(disp->video))
470                 return -EINVAL;
471
472         return VIDEO_CALL(disp->ops->blit, -EOPNOTSUPP, disp, buf, x, y);
473 }
474
475 int uterm_display_fake_blend(struct uterm_display *disp,
476                              const struct uterm_video_buffer *buf,
477                              unsigned int x, unsigned int y,
478                              uint8_t fr, uint8_t fg, uint8_t fb,
479                              uint8_t br, uint8_t bg, uint8_t bb)
480 {
481         struct uterm_video_blend_req req;
482
483         if (!disp || !display_is_online(disp) || !video_is_awake(disp->video))
484                 return -EINVAL;
485
486         memset(&req, 0, sizeof(req));
487         req.buf = buf;
488         req.x = x;
489         req.y = y;
490         req.fr = fr;
491         req.fg = fg;
492         req.fb = fb;
493         req.br = br;
494         req.bg = bg;
495         req.bb = bb;
496
497         return VIDEO_CALL(disp->ops->fake_blendv, -EOPNOTSUPP, disp, &req, 1);
498 }
499
500 int uterm_display_fake_blendv(struct uterm_display *disp,
501                               const struct uterm_video_blend_req *req,
502                               size_t num)
503 {
504         if (!disp || !display_is_online(disp) || !video_is_awake(disp->video))
505                 return -EINVAL;
506
507         return VIDEO_CALL(disp->ops->fake_blendv, -EOPNOTSUPP, disp, req, num);
508 }
509
510 int uterm_video_new(struct uterm_video **out, struct ev_eloop *eloop,
511                     const char *node, const struct uterm_video_module *mod)
512 {
513         struct uterm_video *video;
514         int ret;
515
516         if (!out || !eloop)
517                 return -EINVAL;
518         if (!mod || !mod->ops)
519                 return -EOPNOTSUPP;
520
521         video = malloc(sizeof(*video));
522         if (!video)
523                 return -ENOMEM;
524         memset(video, 0, sizeof(*video));
525         video->ref = 1;
526         video->mod = mod;
527         video->ops = mod->ops;
528         video->eloop = eloop;
529         shl_dlist_init(&video->displays);
530
531         ret = shl_hook_new(&video->hook);
532         if (ret)
533                 goto err_free;
534
535         ret = VIDEO_CALL(video->ops->init, 0, video, node);
536         if (ret)
537                 goto err_hook;
538
539         ev_eloop_ref(video->eloop);
540         log_info("new device %p", video);
541         *out = video;
542         return 0;
543
544 err_hook:
545         shl_hook_free(video->hook);
546 err_free:
547         free(video);
548         return ret;
549 }
550
551 void uterm_video_ref(struct uterm_video *video)
552 {
553         if (!video || !video->ref)
554                 return;
555
556         ++video->ref;
557 }
558
559 void uterm_video_unref(struct uterm_video *video)
560 {
561         struct uterm_display *disp;
562
563         if (!video || !video->ref || --video->ref)
564                 return;
565
566         log_info("free device %p", video);
567
568         while (!shl_dlist_empty(&video->displays)) {
569                 disp = shl_dlist_entry(video->displays.prev,
570                                        struct uterm_display, list);
571                 uterm_display_unbind(disp);
572         }
573
574         VIDEO_CALL(video->ops->destroy, 0, video);
575         shl_hook_free(video->hook);
576         ev_eloop_unref(video->eloop);
577         free(video);
578 }
579
580 void uterm_video_segfault(struct uterm_video *video)
581 {
582         if (!video)
583                 return;
584
585         VIDEO_CALL(video->ops->segfault, 0, video);
586 }
587
588 struct uterm_display *uterm_video_get_displays(struct uterm_video *video)
589 {
590         if (!video || shl_dlist_empty(&video->displays))
591                 return NULL;
592
593         return shl_dlist_entry(video->displays.next, struct uterm_display,
594                                list);
595 }
596
597 int uterm_video_register_cb(struct uterm_video *video, uterm_video_cb cb,
598                                 void *data)
599 {
600         if (!video || !cb)
601                 return -EINVAL;
602
603         return shl_hook_add_cast(video->hook, cb, data, false);
604 }
605
606 void uterm_video_unregister_cb(struct uterm_video *video, uterm_video_cb cb,
607                                 void *data)
608 {
609         if (!video || !cb)
610                 return;
611
612         shl_hook_rm_cast(video->hook, cb, data);
613 }
614
615 void uterm_video_sleep(struct uterm_video *video)
616 {
617         if (!video || !video_is_awake(video))
618                 return;
619
620         log_debug("go asleep");
621
622         VIDEO_CB(video, NULL, UTERM_SLEEP);
623         video->flags &= ~VIDEO_AWAKE;
624         VIDEO_CALL(video->ops->sleep, 0, video);
625 }
626
627 int uterm_video_wake_up(struct uterm_video *video)
628 {
629         int ret;
630
631         if (!video)
632                 return -EINVAL;
633         if (video_is_awake(video))
634                 return 0;
635
636         log_debug("wake up");
637
638         ret = VIDEO_CALL(video->ops->wake_up, 0, video);
639         if (ret) {
640                 video->flags &= ~VIDEO_AWAKE;
641                 return ret;
642         }
643
644         video->flags |= VIDEO_AWAKE;
645         VIDEO_CB(video, NULL, UTERM_WAKE_UP);
646         return 0;
647 }
648
649 bool uterm_video_is_awake(struct uterm_video *video)
650 {
651         return video && video_is_awake(video);
652 }
653
654 void uterm_video_poll(struct uterm_video *video)
655 {
656         if (!video)
657                 return;
658
659         VIDEO_CALL(video->ops->poll, 0, video);
660 }