chiark / gitweb /
terminal/drm: provide pipe->target() callback
[elogind.git] / src / libsystemd-terminal / grdev.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright (C) 2014 David Herrmann <dh.herrmann@gmail.com>
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <inttypes.h>
23 #include <libudev.h>
24 #include <stdbool.h>
25 #include <stdlib.h>
26 #include <systemd/sd-bus.h>
27 #include <systemd/sd-event.h>
28 #include <systemd/sd-login.h>
29 #include "grdev.h"
30 #include "grdev-internal.h"
31 #include "hashmap.h"
32 #include "login-shared.h"
33 #include "macro.h"
34 #include "udev-util.h"
35 #include "util.h"
36
37 static void pipe_enable(grdev_pipe *pipe);
38 static void pipe_disable(grdev_pipe *pipe);
39 static void card_modified(grdev_card *card);
40 static void session_frame(grdev_session *session, grdev_display *display);
41
42 /*
43  * Displays
44  */
45
46 static inline grdev_tile *tile_leftmost(grdev_tile *tile) {
47         if (!tile)
48                 return NULL;
49
50         while (tile->type == GRDEV_TILE_NODE && tile->node.child_list)
51                 tile = tile->node.child_list;
52
53         return tile;
54 }
55
56 #define TILE_FOREACH(_root, _i) \
57         for (_i = tile_leftmost(_root); _i; _i = tile_leftmost(_i->children_by_node_next) ? : _i->parent)
58
59 #define TILE_FOREACH_SAFE(_root, _i, _next) \
60         for (_i = tile_leftmost(_root); _i && ((_next = tile_leftmost(_i->children_by_node_next) ? : _i->parent), true); _i = _next)
61
62 static void tile_link(grdev_tile *tile, grdev_tile *parent) {
63         grdev_display *display;
64         grdev_tile *t;
65
66         assert(tile);
67         assert(!tile->parent);
68         assert(!tile->display);
69         assert(parent);
70         assert(parent->type == GRDEV_TILE_NODE);
71
72         display = parent->display;
73
74         assert(!display || !display->enabled);
75
76         ++parent->node.n_children;
77         LIST_PREPEND(children_by_node, parent->node.child_list, tile);
78         tile->parent = parent;
79
80         if (display) {
81                 display->modified = true;
82                 TILE_FOREACH(tile, t) {
83                         t->display = display;
84                         if (t->type == GRDEV_TILE_LEAF) {
85                                 ++display->n_leafs;
86                                 if (display->enabled)
87                                         pipe_enable(t->leaf.pipe);
88                         }
89                 }
90         }
91 }
92
93 static void tile_unlink(grdev_tile *tile) {
94         grdev_tile *parent, *t;
95         grdev_display *display;
96
97         assert(tile);
98
99         display = tile->display;
100         parent = tile->parent;
101         if (!parent) {
102                 assert(!display);
103                 return;
104         }
105
106         assert(parent->type == GRDEV_TILE_NODE);
107         assert(parent->display == display);
108         assert(parent->node.n_children > 0);
109
110         --parent->node.n_children;
111         LIST_REMOVE(children_by_node, parent->node.child_list, tile);
112         tile->parent = NULL;
113
114         if (display) {
115                 display->modified = true;
116                 TILE_FOREACH(tile, t) {
117                         t->display = NULL;
118                         if (t->type == GRDEV_TILE_LEAF) {
119                                 --display->n_leafs;
120                                 t->leaf.pipe->cache = NULL;
121                                 pipe_disable(t->leaf.pipe);
122                         }
123                 }
124         }
125
126         /* Tile trees are driven by leafs. Internal nodes have no owner, thus,
127          * we must take care to not leave them around. Therefore, whenever we
128          * unlink any part of a tree, we also destroy the parent, in case it's
129          * now stale.
130          * Parents are stale if they have no children and either have no display
131          * or if they are intermediate nodes (i.e, they have a parent).
132          * This means, you can easily create trees, but you can never partially
133          * move or destruct them so far. They're always reduced to minimal form
134          * if you cut them. This might change later, but so far we didn't need
135          * partial destruction or the ability to move whole trees. */
136
137         if (parent->node.n_children < 1 && (parent->parent || !parent->display))
138                 grdev_tile_free(parent);
139 }
140
141 static int tile_new(grdev_tile **out) {
142         _cleanup_(grdev_tile_freep) grdev_tile *tile = NULL;
143
144         assert(out);
145
146         tile = new0(grdev_tile, 1);
147         if (!tile)
148                 return -ENOMEM;
149
150         tile->type = (unsigned)-1;
151
152         *out = tile;
153         tile = NULL;
154         return 0;
155 }
156
157 int grdev_tile_new_leaf(grdev_tile **out, grdev_pipe *pipe) {
158         _cleanup_(grdev_tile_freep) grdev_tile *tile = NULL;
159         int r;
160
161         assert_return(out, -EINVAL);
162         assert_return(pipe, -EINVAL);
163         assert_return(!pipe->tile, -EINVAL);
164
165         r = tile_new(&tile);
166         if (r < 0)
167                 return r;
168
169         tile->type = GRDEV_TILE_LEAF;
170         tile->leaf.pipe = pipe;
171
172         if (out)
173                 *out = tile;
174         tile = NULL;
175         return 0;
176 }
177
178 int grdev_tile_new_node(grdev_tile **out) {
179         _cleanup_(grdev_tile_freep) grdev_tile *tile = NULL;
180         int r;
181
182         assert_return(out, -EINVAL);
183
184         r = tile_new(&tile);
185         if (r < 0)
186                 return r;
187
188         tile->type = GRDEV_TILE_NODE;
189
190         *out = tile;
191         tile = NULL;
192         return 0;
193 }
194
195 grdev_tile *grdev_tile_free(grdev_tile *tile) {
196         if (!tile)
197                 return NULL;
198
199         tile_unlink(tile);
200
201         switch (tile->type) {
202         case GRDEV_TILE_LEAF:
203                 assert(!tile->parent);
204                 assert(!tile->display);
205                 assert(tile->leaf.pipe);
206
207                 break;
208         case GRDEV_TILE_NODE:
209                 assert(!tile->parent);
210                 assert(!tile->display);
211                 assert(tile->node.n_children == 0);
212
213                 break;
214         }
215
216         free(tile);
217
218         return NULL;
219 }
220
221 grdev_display *grdev_find_display(grdev_session *session, const char *name) {
222         assert_return(session, NULL);
223         assert_return(name, NULL);
224
225         return hashmap_get(session->display_map, name);
226 }
227
228 int grdev_display_new(grdev_display **out, grdev_session *session, const char *name) {
229         _cleanup_(grdev_display_freep) grdev_display *display = NULL;
230         int r;
231
232         assert(session);
233         assert(name);
234
235         display = new0(grdev_display, 1);
236         if (!display)
237                 return -ENOMEM;
238
239         display->session = session;
240
241         display->name = strdup(name);
242         if (!display->name)
243                 return -ENOMEM;
244
245         r = grdev_tile_new_node(&display->tile);
246         if (r < 0)
247                 return r;
248
249         display->tile->display = display;
250
251         r = hashmap_put(session->display_map, display->name, display);
252         if (r < 0)
253                 return r;
254
255         if (out)
256                 *out = display;
257         display = NULL;
258         return 0;
259 }
260
261 grdev_display *grdev_display_free(grdev_display *display) {
262         if (!display)
263                 return NULL;
264
265         assert(!display->public);
266         assert(!display->enabled);
267         assert(!display->modified);
268         assert(display->n_leafs == 0);
269         assert(display->n_pipes == 0);
270
271         if (display->name)
272                 hashmap_remove_value(display->session->display_map, display->name, display);
273
274         if (display->tile) {
275                 display->tile->display = NULL;
276                 grdev_tile_free(display->tile);
277         }
278
279         free(display->pipes);
280         free(display->name);
281         free(display);
282
283         return NULL;
284 }
285
286 void grdev_display_set_userdata(grdev_display *display, void *userdata) {
287         assert(display);
288
289         display->userdata = userdata;
290 }
291
292 void *grdev_display_get_userdata(grdev_display *display) {
293         assert_return(display, NULL);
294
295         return display->userdata;
296 }
297
298 const char *grdev_display_get_name(grdev_display *display) {
299         assert_return(display, NULL);
300
301         return display->name;
302 }
303
304 uint32_t grdev_display_get_width(grdev_display *display) {
305         assert_return(display, 0);
306
307         return display->width;
308 }
309
310 uint32_t grdev_display_get_height(grdev_display *display) {
311         assert_return(display, 0);
312
313         return display->height;
314 }
315
316 bool grdev_display_is_enabled(grdev_display *display) {
317         return display && display->enabled;
318 }
319
320 void grdev_display_enable(grdev_display *display) {
321         grdev_tile *t;
322
323         assert(display);
324
325         if (!display->enabled) {
326                 display->enabled = true;
327                 TILE_FOREACH(display->tile, t)
328                         if (t->type == GRDEV_TILE_LEAF)
329                                 pipe_enable(t->leaf.pipe);
330         }
331 }
332
333 void grdev_display_disable(grdev_display *display) {
334         grdev_tile *t;
335
336         assert(display);
337
338         if (display->enabled) {
339                 display->enabled = false;
340                 TILE_FOREACH(display->tile, t)
341                         if (t->type == GRDEV_TILE_LEAF)
342                                 pipe_disable(t->leaf.pipe);
343         }
344 }
345
346 const grdev_display_target *grdev_display_next_target(grdev_display *display, const grdev_display_target *prev, uint64_t minage) {
347         grdev_display_cache *cache;
348         size_t idx;
349
350         assert_return(display, NULL);
351         assert_return(!display->modified, NULL);
352         assert_return(display->enabled, NULL);
353
354         if (prev) {
355                 cache = container_of(prev, grdev_display_cache, target);
356
357                 assert(cache->pipe);
358                 assert(cache->pipe->tile->display == display);
359                 assert(display->pipes >= cache);
360
361                 idx = cache - display->pipes + 1;
362         } else {
363                 idx = 0;
364         }
365
366         for (cache = display->pipes + idx; idx < display->n_pipes; ++idx, ++cache) {
367                 grdev_display_target *target;
368                 grdev_pipe *pipe;
369                 grdev_fb *fb;
370
371                 pipe = cache->pipe;
372                 target = &cache->target;
373
374                 if (!pipe->running || !pipe->enabled)
375                         continue;
376
377                 /* if front-buffer is up-to-date, there's nothing to do */
378                 if (minage > 0 && pipe->front && pipe->front->age >= minage)
379                         continue;
380
381                 /* find suitable back-buffer */
382                 if (!(fb = pipe->back)) {
383                         if (!pipe->vtable->target || !(fb = pipe->vtable->target(pipe)))
384                                 continue;
385
386                         assert(fb == pipe->back);
387                 }
388
389                 /* if back-buffer is up-to-date, schedule flip */
390                 if (minage > 0 && fb->age >= minage) {
391                         grdev_display_flip_target(display, target, fb->age);
392                         continue;
393                 }
394
395                 /* we have an out-of-date back-buffer; return for redraw */
396                 target->fb = fb;
397                 return target;
398         }
399
400         return NULL;
401 }
402
403 void grdev_display_flip_target(grdev_display *display, const grdev_display_target *target, uint64_t age) {
404         grdev_display_cache *cache;
405         size_t i;
406
407         assert(display);
408         assert(!display->modified);
409         assert(display->enabled);
410         assert(target);
411         assert(target->fb);
412
413         cache = container_of(target, grdev_display_cache, target);
414
415         assert(cache->pipe);
416         assert(cache->pipe->tile->display == display);
417
418         /* reset age of all FB on overflow */
419         if (age < target->fb->age)
420                 for (i = 0; i < cache->pipe->max_fbs; ++i)
421                         if (cache->pipe->fbs[i])
422                                 cache->pipe->fbs[i]->age = 0;
423
424         ((grdev_fb*)target->fb)->age = age;
425         cache->pipe->flip = true;
426 }
427
428 static void display_cache_apply(grdev_display_cache *c, grdev_tile *l) {
429         uint32_t x, y, width, height;
430         grdev_display_target *t;
431
432         assert(c);
433         assert(l);
434         assert(l->cache_w >= c->target.width + c->target.x);
435         assert(l->cache_h >= c->target.height + c->target.y);
436
437         t = &c->target;
438
439         /* rotate child */
440
441         t->rotate = (t->rotate + l->rotate) & 0x3;
442
443         x = t->x;
444         y = t->y;
445         width = t->width;
446         height = t->height;
447
448         switch (l->rotate) {
449         case GRDEV_ROTATE_0:
450                 break;
451         case GRDEV_ROTATE_90:
452                 t->x = l->cache_h - (height + y);
453                 t->y = x;
454                 t->width = height;
455                 t->height = width;
456                 break;
457         case GRDEV_ROTATE_180:
458                 t->x = l->cache_w - (width + x);
459                 t->y = l->cache_h - (height + y);
460                 break;
461         case GRDEV_ROTATE_270:
462                 t->x = y;
463                 t->y = l->cache_w - (width + x);
464                 t->width = height;
465                 t->height = width;
466                 break;
467         }
468
469         /* flip child */
470
471         t->flip ^= l->flip;
472
473         if (l->flip & GRDEV_FLIP_HORIZONTAL)
474                 t->x = l->cache_w - (t->width + t->x);
475         if (l->flip & GRDEV_FLIP_VERTICAL)
476                 t->y = l->cache_h - (t->height + t->y);
477
478         /* move child */
479
480         t->x += l->x;
481         t->y += l->y;
482 }
483
484 static void display_cache_targets(grdev_display *display) {
485         grdev_display_cache *c;
486         grdev_tile *tile;
487
488         assert(display);
489
490         /* depth-first with children before parent */
491         for (tile = tile_leftmost(display->tile);
492              tile;
493              tile = tile_leftmost(tile->children_by_node_next) ? : tile->parent) {
494                 if (tile->type == GRDEV_TILE_LEAF) {
495                         grdev_pipe *p;
496
497                         /* We're at a leaf and no parent has been cached, yet.
498                          * Copy the pipe information into the target cache and
499                          * update our global pipe-caches if required. */
500
501                         assert(tile->leaf.pipe);
502                         assert(display->n_pipes + 1 <= display->max_pipes);
503
504                         p = tile->leaf.pipe;
505                         c = &display->pipes[display->n_pipes++];
506
507                         zero(*c);
508                         c->pipe = p;
509                         c->pipe->cache = c;
510                         c->target.width = p->width;
511                         c->target.height = p->height;
512                         tile->cache_w = p->width;
513                         tile->cache_h = p->height;
514
515                         /* all new tiles are incomplete due to geometry changes */
516                         c->incomplete = true;
517
518                         display_cache_apply(c, tile);
519                 } else {
520                         grdev_tile *child, *l;
521
522                         /* We're now at a node with all its children already
523                          * computed (depth-first, child before parent). We
524                          * first need to know the size of our tile, then we
525                          * recurse into all leafs and update their cache. */
526
527                         tile->cache_w = 0;
528                         tile->cache_h = 0;
529
530                         LIST_FOREACH(children_by_node, child, tile->node.child_list) {
531                                 if (child->x + child->cache_w > tile->cache_w)
532                                         tile->cache_w = child->x + child->cache_w;
533                                 if (child->y + child->cache_h > tile->cache_h)
534                                         tile->cache_h = child->y + child->cache_h;
535                         }
536
537                         assert(tile->cache_w > 0);
538                         assert(tile->cache_h > 0);
539
540                         TILE_FOREACH(tile, l)
541                                 if (l->type == GRDEV_TILE_LEAF)
542                                         display_cache_apply(l->leaf.pipe->cache, tile);
543                 }
544         }
545 }
546
547 static bool display_cache(grdev_display *display) {
548         grdev_tile *tile;
549         size_t n;
550         void *t;
551         int r;
552
553         assert(display);
554
555         if (!display->modified)
556                 return false;
557
558         display->modified = false;
559         display->framed = false;
560         display->n_pipes = 0;
561         display->width = 0;
562         display->height = 0;
563
564         if (display->n_leafs < 1)
565                 return false;
566
567         TILE_FOREACH(display->tile, tile)
568                 if (tile->type == GRDEV_TILE_LEAF)
569                         tile->leaf.pipe->cache = NULL;
570
571         if (display->n_leafs > display->max_pipes) {
572                 n = ALIGN_POWER2(display->n_leafs);
573                 if (!n) {
574                         r = -ENOMEM;
575                         goto out;
576                 }
577
578                 t = realloc_multiply(display->pipes, sizeof(*display->pipes), n);
579                 if (!t) {
580                         r = -ENOMEM;
581                         goto out;
582                 }
583
584                 display->pipes = t;
585                 display->max_pipes = n;
586         }
587
588         display_cache_targets(display);
589         display->width = display->tile->cache_w;
590         display->height = display->tile->cache_h;
591
592         r = 0;
593
594 out:
595         if (r < 0)
596                 log_debug("grdev: %s/%s: cannot cache pipes: %s",
597                           display->session->name, display->name, strerror(-r));
598         return true;
599 }
600
601 /*
602  * Pipes
603  */
604
605 grdev_pipe *grdev_find_pipe(grdev_card *card, const char *name) {
606         assert_return(card, NULL);
607         assert_return(name, NULL);
608
609         return hashmap_get(card->pipe_map, name);
610 }
611
612 static int pipe_vsync_fn(sd_event_source *src, uint64_t usec, void *userdata) {
613         grdev_pipe *pipe = userdata;
614
615         grdev_pipe_frame(pipe);
616         return 0;
617 }
618
619 int grdev_pipe_add(grdev_pipe *pipe, const char *name, size_t n_fbs) {
620         int r;
621
622         assert_return(pipe, -EINVAL);
623         assert_return(pipe->vtable, -EINVAL);
624         assert_return(pipe->vtable->free, -EINVAL);
625         assert_return(pipe->card, -EINVAL);
626         assert_return(pipe->card->session, -EINVAL);
627         assert_return(!pipe->cache, -EINVAL);
628         assert_return(pipe->width > 0, -EINVAL);
629         assert_return(pipe->height > 0, -EINVAL);
630         assert_return(pipe->vrefresh > 0, -EINVAL);
631         assert_return(!pipe->enabled, -EINVAL);
632         assert_return(!pipe->running, -EINVAL);
633         assert_return(name, -EINVAL);
634
635         pipe->name = strdup(name);
636         if (!pipe->name)
637                 return -ENOMEM;
638
639         if (n_fbs > 0) {
640                 pipe->fbs = new0(grdev_fb*, n_fbs);
641                 if (!pipe->fbs)
642                         return -ENOMEM;
643
644                 pipe->max_fbs = n_fbs;
645         }
646
647         r = grdev_tile_new_leaf(&pipe->tile, pipe);
648         if (r < 0)
649                 return r;
650
651         r = sd_event_add_time(pipe->card->session->context->event,
652                               &pipe->vsync_src,
653                               CLOCK_MONOTONIC,
654                               0,
655                               10 * USEC_PER_MSEC,
656                               pipe_vsync_fn,
657                               pipe);
658         if (r < 0)
659                 return r;
660
661         r = sd_event_source_set_enabled(pipe->vsync_src, SD_EVENT_OFF);
662         if (r < 0)
663                 return r;
664
665         r = hashmap_put(pipe->card->pipe_map, pipe->name, pipe);
666         if (r < 0)
667                 return r;
668
669         card_modified(pipe->card);
670         return 0;
671 }
672
673 grdev_pipe *grdev_pipe_free(grdev_pipe *pipe) {
674         grdev_pipe tmp;
675
676         if (!pipe)
677                 return NULL;
678
679         assert(pipe->card);
680         assert(pipe->vtable);
681         assert(pipe->vtable->free);
682
683         if (pipe->name)
684                 hashmap_remove_value(pipe->card->pipe_map, pipe->name, pipe);
685         if (pipe->tile)
686                 tile_unlink(pipe->tile);
687
688         assert(!pipe->cache);
689
690         tmp = *pipe;
691         pipe->vtable->free(pipe);
692
693         sd_event_source_unref(tmp.vsync_src);
694         grdev_tile_free(tmp.tile);
695         card_modified(tmp.card);
696         free(tmp.fbs);
697         free(tmp.name);
698
699         return NULL;
700 }
701
702 static void pipe_enable(grdev_pipe *pipe) {
703         assert(pipe);
704
705         if (!pipe->enabled) {
706                 pipe->enabled = true;
707                 if (pipe->vtable->enable)
708                         pipe->vtable->enable(pipe);
709         }
710 }
711
712 static void pipe_disable(grdev_pipe *pipe) {
713         assert(pipe);
714
715         if (pipe->enabled) {
716                 pipe->enabled = false;
717                 if (pipe->vtable->disable)
718                         pipe->vtable->disable(pipe);
719         }
720 }
721
722 void grdev_pipe_ready(grdev_pipe *pipe, bool running) {
723         assert(pipe);
724
725         /* grdev_pipe_ready() is used by backends to notify about pipe state
726          * changed. If a pipe is ready, it can be fully used by us (available,
727          * enabled and accessable). Backends can disable pipes at any time
728          * (like for async revocation), but can only enable them from parent
729          * context. Otherwise, we might call user-callbacks recursively. */
730
731         if (pipe->running == running)
732                 return;
733
734         pipe->running = running;
735
736         /* runtime events for unused pipes are not interesting */
737         if (pipe->cache && pipe->enabled) {
738                 grdev_display *display = pipe->tile->display;
739
740                 assert(display);
741
742                 if (running)
743                         session_frame(display->session, display);
744                 else
745                         pipe->cache->incomplete = true;
746         }
747 }
748
749 void grdev_pipe_frame(grdev_pipe *pipe) {
750         grdev_display *display;
751
752         assert(pipe);
753
754         /* if pipe is unused, ignore any frame events */
755         if (!pipe->cache || !pipe->enabled)
756                 return;
757
758         display = pipe->tile->display;
759         assert(display);
760
761         grdev_pipe_schedule(pipe, 0);
762         session_frame(display->session, display);
763 }
764
765 void grdev_pipe_schedule(grdev_pipe *pipe, uint64_t frames) {
766         int r;
767         uint64_t ts;
768
769         if (!frames) {
770                 sd_event_source_set_enabled(pipe->vsync_src, SD_EVENT_OFF);
771                 return;
772         }
773
774         r = sd_event_now(pipe->card->session->context->event, CLOCK_MONOTONIC, &ts);
775         if (r < 0)
776                 goto error;
777
778         ts += frames * USEC_PER_MSEC * 1000ULL / pipe->vrefresh;
779
780         r = sd_event_source_set_time(pipe->vsync_src, ts);
781         if (r < 0)
782                 goto error;
783
784         r = sd_event_source_set_enabled(pipe->vsync_src, SD_EVENT_ONESHOT);
785         if (r < 0)
786                 goto error;
787
788         return;
789
790 error:
791         log_debug("grdev: %s/%s/%s: cannot schedule vsync timer: %s",
792                   pipe->card->session->name, pipe->card->name, pipe->name, strerror(-r));
793 }
794
795 /*
796  * Cards
797  */
798
799 grdev_card *grdev_find_card(grdev_session *session, const char *name) {
800         assert_return(session, NULL);
801         assert_return(name, NULL);
802
803         return hashmap_get(session->card_map, name);
804 }
805
806 int grdev_card_add(grdev_card *card, const char *name) {
807         int r;
808
809         assert_return(card, -EINVAL);
810         assert_return(card->vtable, -EINVAL);
811         assert_return(card->vtable->free, -EINVAL);
812         assert_return(card->session, -EINVAL);
813         assert_return(name, -EINVAL);
814
815         card->name = strdup(name);
816         if (!card->name)
817                 return -ENOMEM;
818
819         card->pipe_map = hashmap_new(&string_hash_ops);
820         if (!card->pipe_map)
821                 return -ENOMEM;
822
823         r = hashmap_put(card->session->card_map, card->name, card);
824         if (r < 0)
825                 return r;
826
827         return 0;
828 }
829
830 grdev_card *grdev_card_free(grdev_card *card) {
831         grdev_card tmp;
832
833         if (!card)
834                 return NULL;
835
836         assert(!card->enabled);
837         assert(card->vtable);
838         assert(card->vtable->free);
839
840         if (card->name)
841                 hashmap_remove_value(card->session->card_map, card->name, card);
842
843         tmp = *card;
844         card->vtable->free(card);
845
846         assert(hashmap_size(tmp.pipe_map) == 0);
847
848         hashmap_free(tmp.pipe_map);
849         free(tmp.name);
850
851         return NULL;
852 }
853
854 static void card_modified(grdev_card *card) {
855         assert(card);
856         assert(card->session->n_pins > 0);
857
858         card->modified = true;
859 }
860
861 static void grdev_card_enable(grdev_card *card) {
862         assert(card);
863
864         if (!card->enabled) {
865                 card->enabled = true;
866                 if (card->vtable->enable)
867                         card->vtable->enable(card);
868         }
869 }
870
871 static void grdev_card_disable(grdev_card *card) {
872         assert(card);
873
874         if (card->enabled) {
875                 card->enabled = false;
876                 if (card->vtable->disable)
877                         card->vtable->disable(card);
878         }
879 }
880
881 /*
882  * Sessions
883  */
884
885 static void session_raise(grdev_session *session, grdev_event *event) {
886         session->event_fn(session, session->userdata, event);
887 }
888
889 static void session_raise_display_add(grdev_session *session, grdev_display *display) {
890         grdev_event event = {
891                 .type = GRDEV_EVENT_DISPLAY_ADD,
892                 .display_add = {
893                         .display = display,
894                 },
895         };
896
897         session_raise(session, &event);
898 }
899
900 static void session_raise_display_remove(grdev_session *session, grdev_display *display) {
901         grdev_event event = {
902                 .type = GRDEV_EVENT_DISPLAY_REMOVE,
903                 .display_remove = {
904                         .display = display,
905                 },
906         };
907
908         session_raise(session, &event);
909 }
910
911 static void session_raise_display_change(grdev_session *session, grdev_display *display) {
912         grdev_event event = {
913                 .type = GRDEV_EVENT_DISPLAY_CHANGE,
914                 .display_change = {
915                         .display = display,
916                 },
917         };
918
919         session_raise(session, &event);
920 }
921
922 static void session_raise_display_frame(grdev_session *session, grdev_display *display) {
923         grdev_event event = {
924                 .type = GRDEV_EVENT_DISPLAY_FRAME,
925                 .display_frame = {
926                         .display = display,
927                 },
928         };
929
930         session_raise(session, &event);
931 }
932
933 static void session_add_card(grdev_session *session, grdev_card *card) {
934         assert(session);
935         assert(card);
936
937         log_debug("grdev: %s: add card '%s'", session->name, card->name);
938
939         /* Cards are not exposed to users, but managed internally. Cards are
940          * enabled if the session is enabled, and will track that state. The
941          * backend can probe the card at any time, but only if enabled. It
942          * will then add pipes according to hardware state.
943          * That is, the card may create pipes as soon as we enable it here. */
944
945         if (session->enabled)
946                 grdev_card_enable(card);
947 }
948
949 static void session_remove_card(grdev_session *session, grdev_card *card) {
950         assert(session);
951         assert(card);
952
953         log_debug("grdev: %s: remove card '%s'", session->name, card->name);
954
955         /* As cards are not exposed, it can never be accessed by outside
956          * users and we can simply remove it. Disabling the card does not
957          * necessarily drop all pipes of the card. This is usually deferred
958          * to card destruction (as pipes are cached as long as FDs remain
959          * open). Therefore, the card destruction might cause pipes, and thus
960          * visible displays, to be removed. */
961
962         grdev_card_disable(card);
963         grdev_card_free(card);
964 }
965
966 static void session_add_display(grdev_session *session, grdev_display *display) {
967         assert(session);
968         assert(display);
969         assert(!display->enabled);
970
971         log_debug("grdev: %s: add display '%s'", session->name, display->name);
972
973         /* Displays are the main entity for public API users. We create them
974          * independent of card backends and they wrap any underlying display
975          * architecture. Displays are public at all times, thus, may be entered
976          * by outside users at any time. */
977
978         display->public = true;
979         session_raise_display_add(session, display);
980 }
981
982 static void session_remove_display(grdev_session *session, grdev_display *display) {
983         assert(session);
984         assert(display);
985
986         log_debug("grdev: %s: remove display '%s'", session->name, display->name);
987
988         /* Displays are public, so we have to be careful when removing them.
989          * We first tell users about their removal, disable them and then drop
990          * them. We now, after the notification, no external access will
991          * happen. Therefore, we can release the tiles afterwards safely. */
992
993         if (display->public) {
994                 display->public = false;
995                 session_raise_display_remove(session, display);
996         }
997
998         grdev_display_disable(display);
999         grdev_display_free(display);
1000 }
1001
1002 static void session_change_display(grdev_session *session, grdev_display *display) {
1003         bool changed;
1004
1005         assert(session);
1006         assert(display);
1007
1008         changed = display_cache(display);
1009
1010         if (display->n_leafs == 0) {
1011                 session_remove_display(session, display);
1012         } else if (!display->public) {
1013                 session_add_display(session, display);
1014                 session_frame(session, display);
1015         } else if (changed) {
1016                 session_raise_display_change(session, display);
1017                 session_frame(session, display);
1018         } else if (display->framed) {
1019                 session_frame(session, display);
1020         }
1021 }
1022
1023 static void session_frame(grdev_session *session, grdev_display *display) {
1024         assert(session);
1025         assert(display);
1026
1027         display->framed = false;
1028
1029         if (!display->enabled || !session->enabled)
1030                 return;
1031
1032         if (session->n_pins > 0)
1033                 display->framed = true;
1034         else
1035                 session_raise_display_frame(session, display);
1036 }
1037
1038 int grdev_session_new(grdev_session **out,
1039                       grdev_context *context,
1040                       unsigned int flags,
1041                       const char *name,
1042                       grdev_event_fn event_fn,
1043                       void *userdata) {
1044         _cleanup_(grdev_session_freep) grdev_session *session = NULL;
1045         int r;
1046
1047         assert(out);
1048         assert(context);
1049         assert(name);
1050         assert(event_fn);
1051         assert_return(session_id_valid(name) == !(flags & GRDEV_SESSION_CUSTOM), -EINVAL);
1052         assert_return(!(flags & GRDEV_SESSION_CUSTOM) || !(flags & GRDEV_SESSION_MANAGED), -EINVAL);
1053         assert_return(!(flags & GRDEV_SESSION_MANAGED) || context->sysbus, -EINVAL);
1054
1055         session = new0(grdev_session, 1);
1056         if (!session)
1057                 return -ENOMEM;
1058
1059         session->context = grdev_context_ref(context);
1060         session->custom = flags & GRDEV_SESSION_CUSTOM;
1061         session->managed = flags & GRDEV_SESSION_MANAGED;
1062         session->event_fn = event_fn;
1063         session->userdata = userdata;
1064
1065         session->name = strdup(name);
1066         if (!session->name)
1067                 return -ENOMEM;
1068
1069         if (session->managed) {
1070                 r = sd_bus_path_encode("/org/freedesktop/login1/session",
1071                                        session->name, &session->path);
1072                 if (r < 0)
1073                         return r;
1074         }
1075
1076         session->card_map = hashmap_new(&string_hash_ops);
1077         if (!session->card_map)
1078                 return -ENOMEM;
1079
1080         session->display_map = hashmap_new(&string_hash_ops);
1081         if (!session->display_map)
1082                 return -ENOMEM;
1083
1084         r = hashmap_put(context->session_map, session->name, session);
1085         if (r < 0)
1086                 return r;
1087
1088         *out = session;
1089         session = NULL;
1090         return 0;
1091 }
1092
1093 grdev_session *grdev_session_free(grdev_session *session) {
1094         grdev_card *card;
1095
1096         if (!session)
1097                 return NULL;
1098
1099         grdev_session_disable(session);
1100
1101         while ((card = hashmap_first(session->card_map)))
1102                 session_remove_card(session, card);
1103
1104         assert(hashmap_size(session->display_map) == 0);
1105
1106         if (session->name)
1107                 hashmap_remove_value(session->context->session_map, session->name, session);
1108
1109         hashmap_free(session->display_map);
1110         hashmap_free(session->card_map);
1111         session->context = grdev_context_unref(session->context);
1112         free(session->path);
1113         free(session->name);
1114         free(session);
1115
1116         return NULL;
1117 }
1118
1119 bool grdev_session_is_enabled(grdev_session *session) {
1120         return session && session->enabled;
1121 }
1122
1123 void grdev_session_enable(grdev_session *session) {
1124         grdev_card *card;
1125         Iterator iter;
1126
1127         assert(session);
1128
1129         if (!session->enabled) {
1130                 session->enabled = true;
1131                 HASHMAP_FOREACH(card, session->card_map, iter)
1132                         grdev_card_enable(card);
1133         }
1134 }
1135
1136 void grdev_session_disable(grdev_session *session) {
1137         grdev_card *card;
1138         Iterator iter;
1139
1140         assert(session);
1141
1142         if (session->enabled) {
1143                 session->enabled = false;
1144                 HASHMAP_FOREACH(card, session->card_map, iter)
1145                         grdev_card_disable(card);
1146         }
1147 }
1148
1149 void grdev_session_commit(grdev_session *session) {
1150         grdev_card *card;
1151         Iterator iter;
1152
1153         assert(session);
1154
1155         if (!session->enabled)
1156                 return;
1157
1158         HASHMAP_FOREACH(card, session->card_map, iter)
1159                 if (card->vtable->commit)
1160                         card->vtable->commit(card);
1161 }
1162
1163 void grdev_session_restore(grdev_session *session) {
1164         grdev_card *card;
1165         Iterator iter;
1166
1167         assert(session);
1168
1169         if (!session->enabled)
1170                 return;
1171
1172         HASHMAP_FOREACH(card, session->card_map, iter)
1173                 if (card->vtable->restore)
1174                         card->vtable->restore(card);
1175 }
1176
1177 void grdev_session_add_drm(grdev_session *session, struct udev_device *ud) {
1178         grdev_card *card;
1179         dev_t devnum;
1180         int r;
1181
1182         assert(session);
1183         assert(ud);
1184
1185         devnum = udev_device_get_devnum(ud);
1186         if (devnum == 0)
1187                 return grdev_session_hotplug_drm(session, ud);
1188
1189         card = grdev_find_drm_card(session, devnum);
1190         if (card)
1191                 return;
1192
1193         r = grdev_drm_card_new(&card, session, ud);
1194         if (r < 0) {
1195                 log_debug("grdev: %s: cannot add DRM device for %s: %s",
1196                           session->name, udev_device_get_syspath(ud), strerror(-r));
1197                 return;
1198         }
1199
1200         session_add_card(session, card);
1201 }
1202
1203 void grdev_session_remove_drm(grdev_session *session, struct udev_device *ud) {
1204         grdev_card *card;
1205         dev_t devnum;
1206
1207         assert(session);
1208         assert(ud);
1209
1210         devnum = udev_device_get_devnum(ud);
1211         if (devnum == 0)
1212                 return grdev_session_hotplug_drm(session, ud);
1213
1214         card = grdev_find_drm_card(session, devnum);
1215         if (!card)
1216                 return;
1217
1218         session_remove_card(session, card);
1219 }
1220
1221 void grdev_session_hotplug_drm(grdev_session *session, struct udev_device *ud) {
1222         grdev_card *card = NULL;
1223         struct udev_device *p;
1224         dev_t devnum;
1225
1226         assert(session);
1227         assert(ud);
1228
1229         for (p = ud; p; p = udev_device_get_parent_with_subsystem_devtype(p, "drm", NULL)) {
1230                 devnum = udev_device_get_devnum(ud);
1231                 if (devnum == 0)
1232                         continue;
1233
1234                 card = grdev_find_drm_card(session, devnum);
1235                 if (card)
1236                         break;
1237         }
1238
1239         if (!card)
1240                 return;
1241
1242         grdev_drm_card_hotplug(card, ud);
1243 }
1244
1245 static void session_configure(grdev_session *session) {
1246         grdev_display *display;
1247         grdev_tile *tile;
1248         grdev_card *card;
1249         grdev_pipe *pipe;
1250         Iterator i, j;
1251         int r;
1252
1253         assert(session);
1254
1255         /*
1256          * Whenever backends add or remove pipes, we set session->modified and
1257          * require them to pin the session while modifying it. On release, we
1258          * reconfigure the device and re-assign displays to all modified pipes.
1259          *
1260          * So far, we configure each pipe as a separate display. We do not
1261          * support user-configuration, nor have we gotten any reports from
1262          * users with multi-pipe monitors (4k on DP-1.2 MST and so on). Until
1263          * we get reports, we keep the logic to a minimum.
1264          */
1265
1266         /* create new displays for all unconfigured pipes */
1267         HASHMAP_FOREACH(card, session->card_map, i) {
1268                 if (!card->modified)
1269                         continue;
1270
1271                 card->modified = false;
1272
1273                 HASHMAP_FOREACH(pipe, card->pipe_map, j) {
1274                         tile = pipe->tile;
1275                         if (tile->display)
1276                                 continue;
1277
1278                         assert(!tile->parent);
1279
1280                         display = grdev_find_display(session, pipe->name);
1281                         if (display && display->tile) {
1282                                 log_debug("grdev: %s/%s: occupied display for pipe %s",
1283                                           session->name, card->name, pipe->name);
1284                                 continue;
1285                         } else if (!display) {
1286                                 r = grdev_display_new(&display, session, pipe->name);
1287                                 if (r < 0) {
1288                                         log_debug("grdev: %s/%s: cannot create display for pipe %s: %s",
1289                                                   session->name, card->name, pipe->name, strerror(-r));
1290                                         continue;
1291                                 }
1292                         }
1293
1294                         tile_link(pipe->tile, display->tile);
1295                 }
1296         }
1297
1298         /* update displays */
1299         HASHMAP_FOREACH(display, session->display_map, i)
1300                 session_change_display(session, display);
1301 }
1302
1303 grdev_session *grdev_session_pin(grdev_session *session) {
1304         assert(session);
1305
1306         ++session->n_pins;
1307         return session;
1308 }
1309
1310 grdev_session *grdev_session_unpin(grdev_session *session) {
1311         if (!session)
1312                 return NULL;
1313
1314         assert(session->n_pins > 0);
1315
1316         if (--session->n_pins == 0)
1317                 session_configure(session);
1318
1319         return NULL;
1320 }
1321
1322 /*
1323  * Contexts
1324  */
1325
1326 int grdev_context_new(grdev_context **out, sd_event *event, sd_bus *sysbus) {
1327         _cleanup_(grdev_context_unrefp) grdev_context *context = NULL;
1328
1329         assert_return(out, -EINVAL);
1330         assert_return(event, -EINVAL);
1331
1332         context = new0(grdev_context, 1);
1333         if (!context)
1334                 return -ENOMEM;
1335
1336         context->ref = 1;
1337         context->event = sd_event_ref(event);
1338
1339         if (sysbus)
1340                 context->sysbus = sd_bus_ref(sysbus);
1341
1342         context->session_map = hashmap_new(&string_hash_ops);
1343         if (!context->session_map)
1344                 return -ENOMEM;
1345
1346         *out = context;
1347         context = NULL;
1348         return 0;
1349 }
1350
1351 static void context_cleanup(grdev_context *context) {
1352         assert(hashmap_size(context->session_map) == 0);
1353
1354         hashmap_free(context->session_map);
1355         context->sysbus = sd_bus_unref(context->sysbus);
1356         context->event = sd_event_unref(context->event);
1357         free(context);
1358 }
1359
1360 grdev_context *grdev_context_ref(grdev_context *context) {
1361         assert_return(context, NULL);
1362         assert_return(context->ref > 0, NULL);
1363
1364         ++context->ref;
1365         return context;
1366 }
1367
1368 grdev_context *grdev_context_unref(grdev_context *context) {
1369         if (!context)
1370                 return NULL;
1371
1372         assert_return(context->ref > 0, NULL);
1373
1374         if (--context->ref == 0)
1375                 context_cleanup(context);
1376
1377         return NULL;
1378 }