uvtd: seat: implement session IDs
[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 "shl_dlist.h"
39 #include "shl_hook.h"
40 #include "shl_log.h"
41 #include "shl_misc.h"
42 #include "uterm_video.h"
43 #include "uterm_video_internal.h"
44
45 #define LOG_SUBSYSTEM "video"
46
47 SHL_EXPORT
48 const char *uterm_dpms_to_name(int dpms)
49 {
50         switch (dpms) {
51         case UTERM_DPMS_ON:
52                 return "ON";
53         case UTERM_DPMS_STANDBY:
54                 return "STANDBY";
55         case UTERM_DPMS_SUSPEND:
56                 return "SUSPEND";
57         case UTERM_DPMS_OFF:
58                 return "OFF";
59         default:
60                 return "UNKNOWN";
61         }
62 }
63
64 SHL_EXPORT
65 bool uterm_video_available(const struct uterm_video_module *mod)
66 {
67         if (!mod)
68                 return false;
69
70         if (mod == UTERM_VIDEO_DRM2D || mod == UTERM_VIDEO_DRM3D)
71                 return video_drm_available();
72
73         return true;
74 }
75
76 SHL_EXPORT
77 int mode_new(struct uterm_mode **out, const struct mode_ops *ops)
78 {
79         struct uterm_mode *mode;
80         int ret;
81
82         if (!out || !ops)
83                 return -EINVAL;
84
85         mode = malloc(sizeof(*mode));
86         if (!mode)
87                 return -ENOMEM;
88         memset(mode, 0, sizeof(*mode));
89         mode->ref = 1;
90         mode->ops = ops;
91
92         ret = VIDEO_CALL(mode->ops->init, 0, mode);
93         if (ret)
94                 goto err_free;
95
96         *out = mode;
97         return 0;
98
99 err_free:
100         free(mode);
101         return ret;
102 }
103
104 SHL_EXPORT
105 void uterm_mode_ref(struct uterm_mode *mode)
106 {
107         if (!mode || !mode->ref)
108                 return;
109
110         ++mode->ref;
111 }
112
113 SHL_EXPORT
114 void uterm_mode_unref(struct uterm_mode *mode)
115 {
116         if (!mode || !mode->ref || --mode->ref)
117                 return;
118
119         VIDEO_CALL(mode->ops->destroy, 0, mode);
120         free(mode);
121 }
122
123 SHL_EXPORT
124 int uterm_mode_bind(struct uterm_mode *mode, struct uterm_display *disp)
125 {
126         if (!mode || !disp || mode->disp)
127                 return -EINVAL;
128
129         mode->disp = disp;
130         shl_dlist_link_tail(&disp->modes, &mode->list);
131         uterm_mode_ref(mode);
132
133         return 0;
134 }
135
136 SHL_EXPORT
137 void uterm_mode_unbind(struct uterm_mode *mode)
138 {
139         if (!mode)
140                 return;
141
142         mode->disp = NULL;
143         shl_dlist_unlink(&mode->list);
144         uterm_mode_unref(mode);
145 }
146
147 SHL_EXPORT
148 struct uterm_mode *uterm_mode_next(struct uterm_mode *mode)
149 {
150         if (!mode || mode->list.next == &mode->disp->modes)
151                 return NULL;
152
153         return shl_dlist_entry(mode->list.next, struct uterm_mode, list);
154 }
155
156 SHL_EXPORT
157 const char *uterm_mode_get_name(const struct uterm_mode *mode)
158 {
159         if (!mode)
160                 return NULL;
161
162         return VIDEO_CALL(mode->ops->get_name, NULL, mode);
163 }
164
165 SHL_EXPORT
166 unsigned int uterm_mode_get_width(const struct uterm_mode *mode)
167 {
168         if (!mode)
169                 return 0;
170
171         return VIDEO_CALL(mode->ops->get_width, 0, mode);
172 }
173
174 SHL_EXPORT
175 unsigned int uterm_mode_get_height(const struct uterm_mode *mode)
176 {
177         if (!mode)
178                 return 0;
179
180         return VIDEO_CALL(mode->ops->get_height, 0, mode);
181 }
182
183 int display_schedule_vblank_timer(struct uterm_display *disp)
184 {
185         int ret;
186
187         if (disp->vblank_scheduled)
188                 return 0;
189
190         ret = ev_timer_update(disp->vblank_timer, &disp->vblank_spec);
191         if (ret)
192                 return ret;
193
194         disp->vblank_scheduled = true;
195         return 0;
196 }
197
198 void display_set_vblank_timer(struct uterm_display *disp,
199                               unsigned int msecs)
200 {
201         if (msecs >= 1000)
202                 msecs = 999;
203         else if (msecs == 0)
204                 msecs = 15;
205
206         disp->vblank_spec.it_value.tv_nsec = msecs * 1000 * 1000;
207 }
208
209 static void display_vblank_timer_event(struct ev_timer *timer,
210                                        uint64_t expirations,
211                                        void *data)
212 {
213         struct uterm_display *disp = data;
214
215         disp->vblank_scheduled = false;
216         DISPLAY_CB(disp, UTERM_PAGE_FLIP);
217 }
218
219 int display_new(struct uterm_display **out, const struct display_ops *ops)
220 {
221         struct uterm_display *disp;
222         int ret;
223
224         if (!out || !ops)
225                 return -EINVAL;
226
227         disp = malloc(sizeof(*disp));
228         if (!disp)
229                 return -ENOMEM;
230         memset(disp, 0, sizeof(*disp));
231         disp->ref = 1;
232         disp->ops = ops;
233         shl_dlist_init(&disp->modes);
234
235         log_info("new display %p", disp);
236
237         ret = shl_hook_new(&disp->hook);
238         if (ret)
239                 goto err_free;
240
241         disp->vblank_spec.it_value.tv_nsec = 15 * 1000 * 1000;
242
243         ret = ev_timer_new(&disp->vblank_timer, NULL,
244                            display_vblank_timer_event, disp, NULL, NULL);
245         if (ret)
246                 goto err_hook;
247
248         ret = VIDEO_CALL(disp->ops->init, 0, disp);
249         if (ret)
250                 goto err_timer;
251
252         *out = disp;
253         return 0;
254
255 err_timer:
256         ev_timer_unref(disp->vblank_timer);
257 err_hook:
258         shl_hook_free(disp->hook);
259 err_free:
260         free(disp);
261         return ret;
262 }
263
264 SHL_EXPORT
265 void uterm_display_ref(struct uterm_display *disp)
266 {
267         if (!disp || !disp->ref)
268                 return;
269
270         ++disp->ref;
271 }
272
273 SHL_EXPORT
274 void uterm_display_unref(struct uterm_display *disp)
275 {
276         struct uterm_mode *mode;
277
278         if (!disp || !disp->ref || --disp->ref)
279                 return;
280
281         log_info("free display %p", disp);
282
283         while (!shl_dlist_empty(&disp->modes)) {
284                 mode = shl_dlist_entry(disp->modes.prev, struct uterm_mode,
285                                        list);
286                 uterm_mode_unbind(mode);
287         }
288
289         VIDEO_CALL(disp->ops->destroy, 0, disp);
290         ev_timer_unref(disp->vblank_timer);
291         shl_hook_free(disp->hook);
292         free(disp);
293 }
294
295 SHL_EXPORT
296 int uterm_display_bind(struct uterm_display *disp, struct uterm_video *video)
297 {
298         int ret;
299
300         if (!disp || !video || disp->video)
301                 return -EINVAL;
302
303         ret = ev_eloop_add_timer(video->eloop, disp->vblank_timer);
304         if (ret)
305                 return ret;
306
307         shl_dlist_link_tail(&video->displays, &disp->list);
308         disp->video = video;
309         uterm_display_ref(disp);
310         VIDEO_CB(disp->video, disp, UTERM_NEW);
311
312         return 0;
313 }
314
315 SHL_EXPORT
316 void uterm_display_unbind(struct uterm_display *disp)
317 {
318         if (!disp || !disp->video)
319                 return;
320
321         VIDEO_CB(disp->video, disp, UTERM_GONE);
322         uterm_display_deactivate(disp);
323         disp->video = NULL;
324         shl_dlist_unlink(&disp->list);
325         ev_eloop_rm_timer(disp->vblank_timer);
326         uterm_display_unref(disp);
327 }
328
329 SHL_EXPORT
330 struct uterm_display *uterm_display_next(struct uterm_display *disp)
331 {
332         if (!disp || !disp->video || disp->list.next == &disp->video->displays)
333                 return NULL;
334
335         return shl_dlist_entry(disp->list.next, struct uterm_display, list);
336 }
337
338 SHL_EXPORT
339 int uterm_display_register_cb(struct uterm_display *disp, uterm_display_cb cb,
340                               void *data)
341 {
342         if (!disp)
343                 return -EINVAL;
344
345         return shl_hook_add_cast(disp->hook, cb, data, false);
346 }
347
348 SHL_EXPORT
349 void uterm_display_unregister_cb(struct uterm_display *disp,
350                                  uterm_display_cb cb, void *data)
351 {
352         if (!disp)
353                 return;
354
355         shl_hook_rm_cast(disp->hook, cb, data);
356 }
357
358 SHL_EXPORT
359 struct uterm_mode *uterm_display_get_modes(struct uterm_display *disp)
360 {
361         if (!disp || shl_dlist_empty(&disp->modes))
362                 return NULL;
363
364         return shl_dlist_entry(disp->modes.next, struct uterm_mode, list);
365 }
366
367 SHL_EXPORT
368 struct uterm_mode *uterm_display_get_current(struct uterm_display *disp)
369 {
370         if (!disp)
371                 return NULL;
372
373         return disp->current_mode;
374 }
375
376 SHL_EXPORT
377 struct uterm_mode *uterm_display_get_default(struct uterm_display *disp)
378 {
379         if (!disp)
380                 return NULL;
381
382         return disp->default_mode;
383 }
384
385 SHL_EXPORT
386 int uterm_display_get_state(struct uterm_display *disp)
387 {
388         if (!disp)
389                 return UTERM_DISPLAY_GONE;
390
391         if (disp->video) {
392                 if (disp->flags & DISPLAY_ONLINE) {
393                         if (disp->video->flags & VIDEO_AWAKE)
394                                 return UTERM_DISPLAY_ACTIVE;
395                         else
396                                 return UTERM_DISPLAY_ASLEEP;
397                 } else {
398                         return UTERM_DISPLAY_INACTIVE;
399                 }
400         } else {
401                 return UTERM_DISPLAY_GONE;
402         }
403 }
404
405 SHL_EXPORT
406 int uterm_display_activate(struct uterm_display *disp, struct uterm_mode *mode)
407 {
408         if (!disp || !disp->video || display_is_online(disp) ||
409             !video_is_awake(disp->video))
410                 return -EINVAL;
411
412         if (!mode)
413                 mode = disp->default_mode;
414
415         return VIDEO_CALL(disp->ops->activate, 0, disp, mode);
416 }
417
418 SHL_EXPORT
419 void uterm_display_deactivate(struct uterm_display *disp)
420 {
421         if (!disp || !display_is_online(disp))
422                 return;
423
424         VIDEO_CALL(disp->ops->deactivate, 0, disp);
425 }
426
427 SHL_EXPORT
428 int uterm_display_set_dpms(struct uterm_display *disp, int state)
429 {
430         if (!disp || !display_is_online(disp) || !video_is_awake(disp->video))
431                 return -EINVAL;
432
433         return VIDEO_CALL(disp->ops->set_dpms, 0, disp, state);
434 }
435
436 SHL_EXPORT
437 int uterm_display_get_dpms(const struct uterm_display *disp)
438 {
439         if (!disp || !disp->video)
440                 return UTERM_DPMS_OFF;
441
442         return disp->dpms;
443 }
444
445 SHL_EXPORT
446 int uterm_display_use(struct uterm_display *disp, bool *opengl)
447 {
448         if (!disp || !display_is_online(disp))
449                 return -EINVAL;
450
451         return VIDEO_CALL(disp->ops->use, -EOPNOTSUPP, disp, opengl);
452 }
453
454 SHL_EXPORT
455 int uterm_display_get_buffers(struct uterm_display *disp,
456                               struct uterm_video_buffer *buffer,
457                               unsigned int formats)
458 {
459         if (!disp || !display_is_online(disp) || !buffer)
460                 return -EINVAL;
461
462         return VIDEO_CALL(disp->ops->get_buffers, -EOPNOTSUPP, disp, buffer,
463                           formats);
464 }
465
466 SHL_EXPORT
467 int uterm_display_swap(struct uterm_display *disp, bool immediate)
468 {
469         if (!disp || !display_is_online(disp) || !video_is_awake(disp->video))
470                 return -EINVAL;
471
472         return VIDEO_CALL(disp->ops->swap, 0, disp, immediate);
473 }
474
475 SHL_EXPORT
476 bool uterm_display_is_swapping(struct uterm_display *disp)
477 {
478         if (!disp)
479                 return false;
480
481         return disp->vblank_scheduled || (disp->flags & DISPLAY_VSYNC);
482 }
483
484 SHL_EXPORT
485 int uterm_display_fill(struct uterm_display *disp,
486                        uint8_t r, uint8_t g, uint8_t b,
487                        unsigned int x, unsigned int y,
488                        unsigned int width, unsigned int height)
489 {
490         if (!disp || !display_is_online(disp) || !video_is_awake(disp->video))
491                 return -EINVAL;
492
493         return VIDEO_CALL(disp->ops->fill, -EOPNOTSUPP, disp, r, g, b, x, y,
494                           width, height);
495 }
496
497 SHL_EXPORT
498 int uterm_display_blit(struct uterm_display *disp,
499                        const struct uterm_video_buffer *buf,
500                        unsigned int x, unsigned int y)
501 {
502         if (!disp || !display_is_online(disp) || !video_is_awake(disp->video))
503                 return -EINVAL;
504
505         return VIDEO_CALL(disp->ops->blit, -EOPNOTSUPP, disp, buf, x, y);
506 }
507
508 SHL_EXPORT
509 int uterm_display_fake_blend(struct uterm_display *disp,
510                              const struct uterm_video_buffer *buf,
511                              unsigned int x, unsigned int y,
512                              uint8_t fr, uint8_t fg, uint8_t fb,
513                              uint8_t br, uint8_t bg, uint8_t bb)
514 {
515         struct uterm_video_blend_req req;
516
517         if (!disp || !display_is_online(disp) || !video_is_awake(disp->video))
518                 return -EINVAL;
519
520         memset(&req, 0, sizeof(req));
521         req.buf = buf;
522         req.x = x;
523         req.y = y;
524         req.fr = fr;
525         req.fg = fg;
526         req.fb = fb;
527         req.br = br;
528         req.bg = bg;
529         req.bb = bb;
530
531         return VIDEO_CALL(disp->ops->fake_blendv, -EOPNOTSUPP, disp, &req, 1);
532 }
533
534 SHL_EXPORT
535 int uterm_display_fake_blendv(struct uterm_display *disp,
536                               const struct uterm_video_blend_req *req,
537                               size_t num)
538 {
539         if (!disp || !display_is_online(disp) || !video_is_awake(disp->video))
540                 return -EINVAL;
541
542         return VIDEO_CALL(disp->ops->fake_blendv, -EOPNOTSUPP, disp, req, num);
543 }
544
545 SHL_EXPORT
546 int uterm_video_new(struct uterm_video **out, struct ev_eloop *eloop,
547                     const char *node, const struct uterm_video_module *mod)
548 {
549         struct uterm_video *video;
550         int ret;
551
552         if (!out || !eloop)
553                 return -EINVAL;
554         if (!mod || !mod->ops)
555                 return -EOPNOTSUPP;
556
557         video = malloc(sizeof(*video));
558         if (!video)
559                 return -ENOMEM;
560         memset(video, 0, sizeof(*video));
561         video->ref = 1;
562         video->mod = mod;
563         video->ops = mod->ops;
564         video->eloop = eloop;
565         shl_dlist_init(&video->displays);
566
567         ret = shl_hook_new(&video->hook);
568         if (ret)
569                 goto err_free;
570
571         ret = VIDEO_CALL(video->ops->init, 0, video, node);
572         if (ret)
573                 goto err_hook;
574
575         ev_eloop_ref(video->eloop);
576         log_info("new device %p", video);
577         *out = video;
578         return 0;
579
580 err_hook:
581         shl_hook_free(video->hook);
582 err_free:
583         free(video);
584         return ret;
585 }
586
587 SHL_EXPORT
588 void uterm_video_ref(struct uterm_video *video)
589 {
590         if (!video || !video->ref)
591                 return;
592
593         ++video->ref;
594 }
595
596 SHL_EXPORT
597 void uterm_video_unref(struct uterm_video *video)
598 {
599         struct uterm_display *disp;
600
601         if (!video || !video->ref || --video->ref)
602                 return;
603
604         log_info("free device %p", video);
605
606         while (!shl_dlist_empty(&video->displays)) {
607                 disp = shl_dlist_entry(video->displays.prev,
608                                        struct uterm_display, list);
609                 uterm_display_unbind(disp);
610         }
611
612         VIDEO_CALL(video->ops->destroy, 0, video);
613         shl_hook_free(video->hook);
614         ev_eloop_unref(video->eloop);
615         free(video);
616 }
617
618 SHL_EXPORT
619 void uterm_video_segfault(struct uterm_video *video)
620 {
621         if (!video)
622                 return;
623
624         VIDEO_CALL(video->ops->segfault, 0, video);
625 }
626
627 SHL_EXPORT
628 struct uterm_display *uterm_video_get_displays(struct uterm_video *video)
629 {
630         if (!video || shl_dlist_empty(&video->displays))
631                 return NULL;
632
633         return shl_dlist_entry(video->displays.next, struct uterm_display,
634                                list);
635 }
636
637 SHL_EXPORT
638 int uterm_video_register_cb(struct uterm_video *video, uterm_video_cb cb,
639                                 void *data)
640 {
641         if (!video || !cb)
642                 return -EINVAL;
643
644         return shl_hook_add_cast(video->hook, cb, data, false);
645 }
646
647 SHL_EXPORT
648 void uterm_video_unregister_cb(struct uterm_video *video, uterm_video_cb cb,
649                                 void *data)
650 {
651         if (!video || !cb)
652                 return;
653
654         shl_hook_rm_cast(video->hook, cb, data);
655 }
656
657 SHL_EXPORT
658 void uterm_video_sleep(struct uterm_video *video)
659 {
660         if (!video || !video_is_awake(video))
661                 return;
662
663         log_debug("go asleep");
664
665         VIDEO_CB(video, NULL, UTERM_SLEEP);
666         video->flags &= ~VIDEO_AWAKE;
667         VIDEO_CALL(video->ops->sleep, 0, video);
668 }
669
670 SHL_EXPORT
671 int uterm_video_wake_up(struct uterm_video *video)
672 {
673         int ret;
674
675         if (!video)
676                 return -EINVAL;
677         if (video_is_awake(video))
678                 return 0;
679
680         log_debug("wake up");
681
682         ret = VIDEO_CALL(video->ops->wake_up, 0, video);
683         if (ret) {
684                 video->flags &= ~VIDEO_AWAKE;
685                 return ret;
686         }
687
688         video->flags |= VIDEO_AWAKE;
689         VIDEO_CB(video, NULL, UTERM_WAKE_UP);
690         return 0;
691 }
692
693 SHL_EXPORT
694 bool uterm_video_is_awake(struct uterm_video *video)
695 {
696         return video && video_is_awake(video);
697 }
698
699 SHL_EXPORT
700 void uterm_video_poll(struct uterm_video *video)
701 {
702         if (!video)
703                 return;
704
705         VIDEO_CALL(video->ops->poll, 0, video);
706 }