chiark / gitweb /
a700a7316bb2e3351839909e418d8b4248d512ac
[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) {
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                 /* find suitable back-buffer */
378                 if (!pipe->back) {
379                         if (!pipe->vtable->target)
380                                 continue;
381                         if (!(fb = pipe->vtable->target(pipe)))
382                                 continue;
383
384                         assert(fb == pipe->back);
385                 }
386
387                 target->front = pipe->front;
388                 target->back = pipe->back;
389
390                 return target;
391         }
392
393         return NULL;
394 }
395
396 void grdev_display_flip_target(grdev_display *display, const grdev_display_target *target, uint64_t age) {
397         grdev_display_cache *cache;
398         size_t i;
399
400         assert(display);
401         assert(!display->modified);
402         assert(display->enabled);
403         assert(target);
404         assert(target->back);
405
406         cache = container_of(target, grdev_display_cache, target);
407
408         assert(cache->pipe);
409         assert(cache->pipe->tile->display == display);
410
411         /* reset age of all FB on overflow */
412         if (age < target->back->age)
413                 for (i = 0; i < cache->pipe->max_fbs; ++i)
414                         if (cache->pipe->fbs[i])
415                                 cache->pipe->fbs[i]->age = 0;
416
417         ((grdev_fb*)target->back)->age = age;
418         cache->pipe->flip = true;
419 }
420
421 static void display_cache_apply(grdev_display_cache *c, grdev_tile *l) {
422         uint32_t x, y, width, height;
423         grdev_display_target *t;
424
425         assert(c);
426         assert(l);
427         assert(l->cache_w >= c->target.width + c->target.x);
428         assert(l->cache_h >= c->target.height + c->target.y);
429
430         t = &c->target;
431
432         /* rotate child */
433
434         t->rotate = (t->rotate + l->rotate) & 0x3;
435
436         x = t->x;
437         y = t->y;
438         width = t->width;
439         height = t->height;
440
441         switch (l->rotate) {
442         case GRDEV_ROTATE_0:
443                 break;
444         case GRDEV_ROTATE_90:
445                 t->x = l->cache_h - (height + y);
446                 t->y = x;
447                 t->width = height;
448                 t->height = width;
449                 break;
450         case GRDEV_ROTATE_180:
451                 t->x = l->cache_w - (width + x);
452                 t->y = l->cache_h - (height + y);
453                 break;
454         case GRDEV_ROTATE_270:
455                 t->x = y;
456                 t->y = l->cache_w - (width + x);
457                 t->width = height;
458                 t->height = width;
459                 break;
460         }
461
462         /* flip child */
463
464         t->flip ^= l->flip;
465
466         if (l->flip & GRDEV_FLIP_HORIZONTAL)
467                 t->x = l->cache_w - (t->width + t->x);
468         if (l->flip & GRDEV_FLIP_VERTICAL)
469                 t->y = l->cache_h - (t->height + t->y);
470
471         /* move child */
472
473         t->x += l->x;
474         t->y += l->y;
475 }
476
477 static void display_cache_targets(grdev_display *display) {
478         grdev_display_cache *c;
479         grdev_tile *tile;
480
481         assert(display);
482
483         /* depth-first with children before parent */
484         for (tile = tile_leftmost(display->tile);
485              tile;
486              tile = tile_leftmost(tile->children_by_node_next) ? : tile->parent) {
487                 if (tile->type == GRDEV_TILE_LEAF) {
488                         grdev_pipe *p;
489
490                         /* We're at a leaf and no parent has been cached, yet.
491                          * Copy the pipe information into the target cache and
492                          * update our global pipe-caches if required. */
493
494                         assert(tile->leaf.pipe);
495                         assert(display->n_pipes + 1 <= display->max_pipes);
496
497                         p = tile->leaf.pipe;
498                         c = &display->pipes[display->n_pipes++];
499
500                         zero(*c);
501                         c->pipe = p;
502                         c->pipe->cache = c;
503                         c->target.width = p->width;
504                         c->target.height = p->height;
505                         tile->cache_w = p->width;
506                         tile->cache_h = p->height;
507
508                         /* all new tiles are incomplete due to geometry changes */
509                         c->incomplete = true;
510
511                         display_cache_apply(c, tile);
512                 } else {
513                         grdev_tile *child, *l;
514
515                         /* We're now at a node with all its children already
516                          * computed (depth-first, child before parent). We
517                          * first need to know the size of our tile, then we
518                          * recurse into all leafs and update their cache. */
519
520                         tile->cache_w = 0;
521                         tile->cache_h = 0;
522
523                         LIST_FOREACH(children_by_node, child, tile->node.child_list) {
524                                 if (child->x + child->cache_w > tile->cache_w)
525                                         tile->cache_w = child->x + child->cache_w;
526                                 if (child->y + child->cache_h > tile->cache_h)
527                                         tile->cache_h = child->y + child->cache_h;
528                         }
529
530                         assert(tile->cache_w > 0);
531                         assert(tile->cache_h > 0);
532
533                         TILE_FOREACH(tile, l)
534                                 if (l->type == GRDEV_TILE_LEAF)
535                                         display_cache_apply(l->leaf.pipe->cache, tile);
536                 }
537         }
538 }
539
540 static bool display_cache(grdev_display *display) {
541         grdev_tile *tile;
542         size_t n;
543         void *t;
544         int r;
545
546         assert(display);
547
548         if (!display->modified)
549                 return false;
550
551         display->modified = false;
552         display->framed = false;
553         display->n_pipes = 0;
554         display->width = 0;
555         display->height = 0;
556
557         if (display->n_leafs < 1)
558                 return false;
559
560         TILE_FOREACH(display->tile, tile)
561                 if (tile->type == GRDEV_TILE_LEAF)
562                         tile->leaf.pipe->cache = NULL;
563
564         if (display->n_leafs > display->max_pipes) {
565                 n = ALIGN_POWER2(display->n_leafs);
566                 if (!n) {
567                         r = -ENOMEM;
568                         goto out;
569                 }
570
571                 t = realloc_multiply(display->pipes, sizeof(*display->pipes), n);
572                 if (!t) {
573                         r = -ENOMEM;
574                         goto out;
575                 }
576
577                 display->pipes = t;
578                 display->max_pipes = n;
579         }
580
581         display_cache_targets(display);
582         display->width = display->tile->cache_w;
583         display->height = display->tile->cache_h;
584
585         r = 0;
586
587 out:
588         if (r < 0)
589                 log_debug("grdev: %s/%s: cannot cache pipes: %s",
590                           display->session->name, display->name, strerror(-r));
591         return true;
592 }
593
594 /*
595  * Pipes
596  */
597
598 grdev_pipe *grdev_find_pipe(grdev_card *card, const char *name) {
599         assert_return(card, NULL);
600         assert_return(name, NULL);
601
602         return hashmap_get(card->pipe_map, name);
603 }
604
605 static int pipe_vsync_fn(sd_event_source *src, uint64_t usec, void *userdata) {
606         grdev_pipe *pipe = userdata;
607
608         grdev_pipe_frame(pipe);
609         return 0;
610 }
611
612 int grdev_pipe_add(grdev_pipe *pipe, const char *name, size_t n_fbs) {
613         int r;
614
615         assert_return(pipe, -EINVAL);
616         assert_return(pipe->vtable, -EINVAL);
617         assert_return(pipe->vtable->free, -EINVAL);
618         assert_return(pipe->card, -EINVAL);
619         assert_return(pipe->card->session, -EINVAL);
620         assert_return(!pipe->cache, -EINVAL);
621         assert_return(pipe->width > 0, -EINVAL);
622         assert_return(pipe->height > 0, -EINVAL);
623         assert_return(pipe->vrefresh > 0, -EINVAL);
624         assert_return(!pipe->enabled, -EINVAL);
625         assert_return(!pipe->running, -EINVAL);
626         assert_return(name, -EINVAL);
627
628         pipe->name = strdup(name);
629         if (!pipe->name)
630                 return -ENOMEM;
631
632         if (n_fbs > 0) {
633                 pipe->fbs = new0(grdev_fb*, n_fbs);
634                 if (!pipe->fbs)
635                         return -ENOMEM;
636
637                 pipe->max_fbs = n_fbs;
638         }
639
640         r = grdev_tile_new_leaf(&pipe->tile, pipe);
641         if (r < 0)
642                 return r;
643
644         r = sd_event_add_time(pipe->card->session->context->event,
645                               &pipe->vsync_src,
646                               CLOCK_MONOTONIC,
647                               0,
648                               10 * USEC_PER_MSEC,
649                               pipe_vsync_fn,
650                               pipe);
651         if (r < 0)
652                 return r;
653
654         r = sd_event_source_set_enabled(pipe->vsync_src, SD_EVENT_OFF);
655         if (r < 0)
656                 return r;
657
658         r = hashmap_put(pipe->card->pipe_map, pipe->name, pipe);
659         if (r < 0)
660                 return r;
661
662         card_modified(pipe->card);
663         return 0;
664 }
665
666 grdev_pipe *grdev_pipe_free(grdev_pipe *pipe) {
667         grdev_pipe tmp;
668
669         if (!pipe)
670                 return NULL;
671
672         assert(pipe->card);
673         assert(pipe->vtable);
674         assert(pipe->vtable->free);
675
676         if (pipe->name)
677                 hashmap_remove_value(pipe->card->pipe_map, pipe->name, pipe);
678         if (pipe->tile)
679                 tile_unlink(pipe->tile);
680
681         assert(!pipe->cache);
682
683         tmp = *pipe;
684         pipe->vtable->free(pipe);
685
686         sd_event_source_unref(tmp.vsync_src);
687         grdev_tile_free(tmp.tile);
688         card_modified(tmp.card);
689         free(tmp.fbs);
690         free(tmp.name);
691
692         return NULL;
693 }
694
695 static void pipe_enable(grdev_pipe *pipe) {
696         assert(pipe);
697
698         if (!pipe->enabled) {
699                 pipe->enabled = true;
700                 if (pipe->vtable->enable)
701                         pipe->vtable->enable(pipe);
702         }
703 }
704
705 static void pipe_disable(grdev_pipe *pipe) {
706         assert(pipe);
707
708         if (pipe->enabled) {
709                 pipe->enabled = false;
710                 if (pipe->vtable->disable)
711                         pipe->vtable->disable(pipe);
712         }
713 }
714
715 void grdev_pipe_ready(grdev_pipe *pipe, bool running) {
716         assert(pipe);
717
718         /* grdev_pipe_ready() is used by backends to notify about pipe state
719          * changed. If a pipe is ready, it can be fully used by us (available,
720          * enabled and accessable). Backends can disable pipes at any time
721          * (like for async revocation), but can only enable them from parent
722          * context. Otherwise, we might call user-callbacks recursively. */
723
724         if (pipe->running == running)
725                 return;
726
727         pipe->running = running;
728
729         /* runtime events for unused pipes are not interesting */
730         if (pipe->cache && pipe->enabled) {
731                 grdev_display *display = pipe->tile->display;
732
733                 assert(display);
734
735                 if (running)
736                         session_frame(display->session, display);
737                 else
738                         pipe->cache->incomplete = true;
739         }
740 }
741
742 void grdev_pipe_frame(grdev_pipe *pipe) {
743         grdev_display *display;
744
745         assert(pipe);
746
747         /* if pipe is unused, ignore any frame events */
748         if (!pipe->cache || !pipe->enabled)
749                 return;
750
751         display = pipe->tile->display;
752         assert(display);
753
754         grdev_pipe_schedule(pipe, 0);
755         session_frame(display->session, display);
756 }
757
758 void grdev_pipe_schedule(grdev_pipe *pipe, uint64_t frames) {
759         int r;
760         uint64_t ts;
761
762         if (!frames) {
763                 sd_event_source_set_enabled(pipe->vsync_src, SD_EVENT_OFF);
764                 return;
765         }
766
767         r = sd_event_now(pipe->card->session->context->event, CLOCK_MONOTONIC, &ts);
768         if (r < 0)
769                 goto error;
770
771         ts += frames * USEC_PER_MSEC * 1000ULL / pipe->vrefresh;
772
773         r = sd_event_source_set_time(pipe->vsync_src, ts);
774         if (r < 0)
775                 goto error;
776
777         r = sd_event_source_set_enabled(pipe->vsync_src, SD_EVENT_ONESHOT);
778         if (r < 0)
779                 goto error;
780
781         return;
782
783 error:
784         log_debug("grdev: %s/%s/%s: cannot schedule vsync timer: %s",
785                   pipe->card->session->name, pipe->card->name, pipe->name, strerror(-r));
786 }
787
788 /*
789  * Cards
790  */
791
792 grdev_card *grdev_find_card(grdev_session *session, const char *name) {
793         assert_return(session, NULL);
794         assert_return(name, NULL);
795
796         return hashmap_get(session->card_map, name);
797 }
798
799 int grdev_card_add(grdev_card *card, const char *name) {
800         int r;
801
802         assert_return(card, -EINVAL);
803         assert_return(card->vtable, -EINVAL);
804         assert_return(card->vtable->free, -EINVAL);
805         assert_return(card->session, -EINVAL);
806         assert_return(name, -EINVAL);
807
808         card->name = strdup(name);
809         if (!card->name)
810                 return -ENOMEM;
811
812         card->pipe_map = hashmap_new(&string_hash_ops);
813         if (!card->pipe_map)
814                 return -ENOMEM;
815
816         r = hashmap_put(card->session->card_map, card->name, card);
817         if (r < 0)
818                 return r;
819
820         return 0;
821 }
822
823 grdev_card *grdev_card_free(grdev_card *card) {
824         grdev_card tmp;
825
826         if (!card)
827                 return NULL;
828
829         assert(!card->enabled);
830         assert(card->vtable);
831         assert(card->vtable->free);
832
833         if (card->name)
834                 hashmap_remove_value(card->session->card_map, card->name, card);
835
836         tmp = *card;
837         card->vtable->free(card);
838
839         assert(hashmap_size(tmp.pipe_map) == 0);
840
841         hashmap_free(tmp.pipe_map);
842         free(tmp.name);
843
844         return NULL;
845 }
846
847 static void card_modified(grdev_card *card) {
848         assert(card);
849         assert(card->session->n_pins > 0);
850
851         card->modified = true;
852 }
853
854 static void grdev_card_enable(grdev_card *card) {
855         assert(card);
856
857         if (!card->enabled) {
858                 card->enabled = true;
859                 if (card->vtable->enable)
860                         card->vtable->enable(card);
861         }
862 }
863
864 static void grdev_card_disable(grdev_card *card) {
865         assert(card);
866
867         if (card->enabled) {
868                 card->enabled = false;
869                 if (card->vtable->disable)
870                         card->vtable->disable(card);
871         }
872 }
873
874 /*
875  * Sessions
876  */
877
878 static void session_raise(grdev_session *session, grdev_event *event) {
879         session->event_fn(session, session->userdata, event);
880 }
881
882 static void session_raise_display_add(grdev_session *session, grdev_display *display) {
883         grdev_event event = {
884                 .type = GRDEV_EVENT_DISPLAY_ADD,
885                 .display_add = {
886                         .display = display,
887                 },
888         };
889
890         session_raise(session, &event);
891 }
892
893 static void session_raise_display_remove(grdev_session *session, grdev_display *display) {
894         grdev_event event = {
895                 .type = GRDEV_EVENT_DISPLAY_REMOVE,
896                 .display_remove = {
897                         .display = display,
898                 },
899         };
900
901         session_raise(session, &event);
902 }
903
904 static void session_raise_display_change(grdev_session *session, grdev_display *display) {
905         grdev_event event = {
906                 .type = GRDEV_EVENT_DISPLAY_CHANGE,
907                 .display_change = {
908                         .display = display,
909                 },
910         };
911
912         session_raise(session, &event);
913 }
914
915 static void session_raise_display_frame(grdev_session *session, grdev_display *display) {
916         grdev_event event = {
917                 .type = GRDEV_EVENT_DISPLAY_FRAME,
918                 .display_frame = {
919                         .display = display,
920                 },
921         };
922
923         session_raise(session, &event);
924 }
925
926 static void session_add_card(grdev_session *session, grdev_card *card) {
927         assert(session);
928         assert(card);
929
930         log_debug("grdev: %s: add card '%s'", session->name, card->name);
931
932         /* Cards are not exposed to users, but managed internally. Cards are
933          * enabled if the session is enabled, and will track that state. The
934          * backend can probe the card at any time, but only if enabled. It
935          * will then add pipes according to hardware state.
936          * That is, the card may create pipes as soon as we enable it here. */
937
938         if (session->enabled)
939                 grdev_card_enable(card);
940 }
941
942 static void session_remove_card(grdev_session *session, grdev_card *card) {
943         assert(session);
944         assert(card);
945
946         log_debug("grdev: %s: remove card '%s'", session->name, card->name);
947
948         /* As cards are not exposed, it can never be accessed by outside
949          * users and we can simply remove it. Disabling the card does not
950          * necessarily drop all pipes of the card. This is usually deferred
951          * to card destruction (as pipes are cached as long as FDs remain
952          * open). Therefore, the card destruction might cause pipes, and thus
953          * visible displays, to be removed. */
954
955         grdev_card_disable(card);
956         grdev_card_free(card);
957 }
958
959 static void session_add_display(grdev_session *session, grdev_display *display) {
960         assert(session);
961         assert(display);
962         assert(!display->enabled);
963
964         log_debug("grdev: %s: add display '%s'", session->name, display->name);
965
966         /* Displays are the main entity for public API users. We create them
967          * independent of card backends and they wrap any underlying display
968          * architecture. Displays are public at all times, thus, may be entered
969          * by outside users at any time. */
970
971         display->public = true;
972         session_raise_display_add(session, display);
973 }
974
975 static void session_remove_display(grdev_session *session, grdev_display *display) {
976         assert(session);
977         assert(display);
978
979         log_debug("grdev: %s: remove display '%s'", session->name, display->name);
980
981         /* Displays are public, so we have to be careful when removing them.
982          * We first tell users about their removal, disable them and then drop
983          * them. We now, after the notification, no external access will
984          * happen. Therefore, we can release the tiles afterwards safely. */
985
986         if (display->public) {
987                 display->public = false;
988                 session_raise_display_remove(session, display);
989         }
990
991         grdev_display_disable(display);
992         grdev_display_free(display);
993 }
994
995 static void session_change_display(grdev_session *session, grdev_display *display) {
996         bool changed;
997
998         assert(session);
999         assert(display);
1000
1001         changed = display_cache(display);
1002
1003         if (display->n_leafs == 0) {
1004                 session_remove_display(session, display);
1005         } else if (!display->public) {
1006                 session_add_display(session, display);
1007                 session_frame(session, display);
1008         } else if (changed) {
1009                 session_raise_display_change(session, display);
1010                 session_frame(session, display);
1011         } else if (display->framed) {
1012                 session_frame(session, display);
1013         }
1014 }
1015
1016 static void session_frame(grdev_session *session, grdev_display *display) {
1017         assert(session);
1018         assert(display);
1019
1020         display->framed = false;
1021
1022         if (!display->enabled || !session->enabled)
1023                 return;
1024
1025         if (session->n_pins > 0)
1026                 display->framed = true;
1027         else
1028                 session_raise_display_frame(session, display);
1029 }
1030
1031 int grdev_session_new(grdev_session **out,
1032                       grdev_context *context,
1033                       unsigned int flags,
1034                       const char *name,
1035                       grdev_event_fn event_fn,
1036                       void *userdata) {
1037         _cleanup_(grdev_session_freep) grdev_session *session = NULL;
1038         int r;
1039
1040         assert(out);
1041         assert(context);
1042         assert(name);
1043         assert(event_fn);
1044         assert_return(session_id_valid(name) == !(flags & GRDEV_SESSION_CUSTOM), -EINVAL);
1045         assert_return(!(flags & GRDEV_SESSION_CUSTOM) || !(flags & GRDEV_SESSION_MANAGED), -EINVAL);
1046         assert_return(!(flags & GRDEV_SESSION_MANAGED) || context->sysbus, -EINVAL);
1047
1048         session = new0(grdev_session, 1);
1049         if (!session)
1050                 return -ENOMEM;
1051
1052         session->context = grdev_context_ref(context);
1053         session->custom = flags & GRDEV_SESSION_CUSTOM;
1054         session->managed = flags & GRDEV_SESSION_MANAGED;
1055         session->event_fn = event_fn;
1056         session->userdata = userdata;
1057
1058         session->name = strdup(name);
1059         if (!session->name)
1060                 return -ENOMEM;
1061
1062         if (session->managed) {
1063                 r = sd_bus_path_encode("/org/freedesktop/login1/session",
1064                                        session->name, &session->path);
1065                 if (r < 0)
1066                         return r;
1067         }
1068
1069         session->card_map = hashmap_new(&string_hash_ops);
1070         if (!session->card_map)
1071                 return -ENOMEM;
1072
1073         session->display_map = hashmap_new(&string_hash_ops);
1074         if (!session->display_map)
1075                 return -ENOMEM;
1076
1077         r = hashmap_put(context->session_map, session->name, session);
1078         if (r < 0)
1079                 return r;
1080
1081         *out = session;
1082         session = NULL;
1083         return 0;
1084 }
1085
1086 grdev_session *grdev_session_free(grdev_session *session) {
1087         grdev_card *card;
1088
1089         if (!session)
1090                 return NULL;
1091
1092         grdev_session_disable(session);
1093
1094         while ((card = hashmap_first(session->card_map)))
1095                 session_remove_card(session, card);
1096
1097         assert(hashmap_size(session->display_map) == 0);
1098
1099         if (session->name)
1100                 hashmap_remove_value(session->context->session_map, session->name, session);
1101
1102         hashmap_free(session->display_map);
1103         hashmap_free(session->card_map);
1104         session->context = grdev_context_unref(session->context);
1105         free(session->path);
1106         free(session->name);
1107         free(session);
1108
1109         return NULL;
1110 }
1111
1112 bool grdev_session_is_enabled(grdev_session *session) {
1113         return session && session->enabled;
1114 }
1115
1116 void grdev_session_enable(grdev_session *session) {
1117         grdev_card *card;
1118         Iterator iter;
1119
1120         assert(session);
1121
1122         if (!session->enabled) {
1123                 session->enabled = true;
1124                 HASHMAP_FOREACH(card, session->card_map, iter)
1125                         grdev_card_enable(card);
1126         }
1127 }
1128
1129 void grdev_session_disable(grdev_session *session) {
1130         grdev_card *card;
1131         Iterator iter;
1132
1133         assert(session);
1134
1135         if (session->enabled) {
1136                 session->enabled = false;
1137                 HASHMAP_FOREACH(card, session->card_map, iter)
1138                         grdev_card_disable(card);
1139         }
1140 }
1141
1142 void grdev_session_commit(grdev_session *session) {
1143         grdev_card *card;
1144         Iterator iter;
1145
1146         assert(session);
1147
1148         if (!session->enabled)
1149                 return;
1150
1151         HASHMAP_FOREACH(card, session->card_map, iter)
1152                 if (card->vtable->commit)
1153                         card->vtable->commit(card);
1154 }
1155
1156 void grdev_session_restore(grdev_session *session) {
1157         grdev_card *card;
1158         Iterator iter;
1159
1160         assert(session);
1161
1162         if (!session->enabled)
1163                 return;
1164
1165         HASHMAP_FOREACH(card, session->card_map, iter)
1166                 if (card->vtable->restore)
1167                         card->vtable->restore(card);
1168 }
1169
1170 void grdev_session_add_drm(grdev_session *session, struct udev_device *ud) {
1171         grdev_card *card;
1172         dev_t devnum;
1173         int r;
1174
1175         assert(session);
1176         assert(ud);
1177
1178         devnum = udev_device_get_devnum(ud);
1179         if (devnum == 0)
1180                 return grdev_session_hotplug_drm(session, ud);
1181
1182         card = grdev_find_drm_card(session, devnum);
1183         if (card)
1184                 return;
1185
1186         r = grdev_drm_card_new(&card, session, ud);
1187         if (r < 0) {
1188                 log_debug("grdev: %s: cannot add DRM device for %s: %s",
1189                           session->name, udev_device_get_syspath(ud), strerror(-r));
1190                 return;
1191         }
1192
1193         session_add_card(session, card);
1194 }
1195
1196 void grdev_session_remove_drm(grdev_session *session, struct udev_device *ud) {
1197         grdev_card *card;
1198         dev_t devnum;
1199
1200         assert(session);
1201         assert(ud);
1202
1203         devnum = udev_device_get_devnum(ud);
1204         if (devnum == 0)
1205                 return grdev_session_hotplug_drm(session, ud);
1206
1207         card = grdev_find_drm_card(session, devnum);
1208         if (!card)
1209                 return;
1210
1211         session_remove_card(session, card);
1212 }
1213
1214 void grdev_session_hotplug_drm(grdev_session *session, struct udev_device *ud) {
1215         grdev_card *card = NULL;
1216         struct udev_device *p;
1217         dev_t devnum;
1218
1219         assert(session);
1220         assert(ud);
1221
1222         for (p = ud; p; p = udev_device_get_parent_with_subsystem_devtype(p, "drm", NULL)) {
1223                 devnum = udev_device_get_devnum(ud);
1224                 if (devnum == 0)
1225                         continue;
1226
1227                 card = grdev_find_drm_card(session, devnum);
1228                 if (card)
1229                         break;
1230         }
1231
1232         if (!card)
1233                 return;
1234
1235         grdev_drm_card_hotplug(card, ud);
1236 }
1237
1238 static void session_configure(grdev_session *session) {
1239         grdev_display *display;
1240         grdev_tile *tile;
1241         grdev_card *card;
1242         grdev_pipe *pipe;
1243         Iterator i, j;
1244         int r;
1245
1246         assert(session);
1247
1248         /*
1249          * Whenever backends add or remove pipes, we set session->modified and
1250          * require them to pin the session while modifying it. On release, we
1251          * reconfigure the device and re-assign displays to all modified pipes.
1252          *
1253          * So far, we configure each pipe as a separate display. We do not
1254          * support user-configuration, nor have we gotten any reports from
1255          * users with multi-pipe monitors (4k on DP-1.2 MST and so on). Until
1256          * we get reports, we keep the logic to a minimum.
1257          */
1258
1259         /* create new displays for all unconfigured pipes */
1260         HASHMAP_FOREACH(card, session->card_map, i) {
1261                 if (!card->modified)
1262                         continue;
1263
1264                 card->modified = false;
1265
1266                 HASHMAP_FOREACH(pipe, card->pipe_map, j) {
1267                         tile = pipe->tile;
1268                         if (tile->display)
1269                                 continue;
1270
1271                         assert(!tile->parent);
1272
1273                         display = grdev_find_display(session, pipe->name);
1274                         if (display && display->tile) {
1275                                 log_debug("grdev: %s/%s: occupied display for pipe %s",
1276                                           session->name, card->name, pipe->name);
1277                                 continue;
1278                         } else if (!display) {
1279                                 r = grdev_display_new(&display, session, pipe->name);
1280                                 if (r < 0) {
1281                                         log_debug("grdev: %s/%s: cannot create display for pipe %s: %s",
1282                                                   session->name, card->name, pipe->name, strerror(-r));
1283                                         continue;
1284                                 }
1285                         }
1286
1287                         tile_link(pipe->tile, display->tile);
1288                 }
1289         }
1290
1291         /* update displays */
1292         HASHMAP_FOREACH(display, session->display_map, i)
1293                 session_change_display(session, display);
1294 }
1295
1296 grdev_session *grdev_session_pin(grdev_session *session) {
1297         assert(session);
1298
1299         ++session->n_pins;
1300         return session;
1301 }
1302
1303 grdev_session *grdev_session_unpin(grdev_session *session) {
1304         if (!session)
1305                 return NULL;
1306
1307         assert(session->n_pins > 0);
1308
1309         if (--session->n_pins == 0)
1310                 session_configure(session);
1311
1312         return NULL;
1313 }
1314
1315 /*
1316  * Contexts
1317  */
1318
1319 int grdev_context_new(grdev_context **out, sd_event *event, sd_bus *sysbus) {
1320         _cleanup_(grdev_context_unrefp) grdev_context *context = NULL;
1321
1322         assert_return(out, -EINVAL);
1323         assert_return(event, -EINVAL);
1324
1325         context = new0(grdev_context, 1);
1326         if (!context)
1327                 return -ENOMEM;
1328
1329         context->ref = 1;
1330         context->event = sd_event_ref(event);
1331
1332         if (sysbus)
1333                 context->sysbus = sd_bus_ref(sysbus);
1334
1335         context->session_map = hashmap_new(&string_hash_ops);
1336         if (!context->session_map)
1337                 return -ENOMEM;
1338
1339         *out = context;
1340         context = NULL;
1341         return 0;
1342 }
1343
1344 static void context_cleanup(grdev_context *context) {
1345         assert(hashmap_size(context->session_map) == 0);
1346
1347         hashmap_free(context->session_map);
1348         context->sysbus = sd_bus_unref(context->sysbus);
1349         context->event = sd_event_unref(context->event);
1350         free(context);
1351 }
1352
1353 grdev_context *grdev_context_ref(grdev_context *context) {
1354         assert_return(context, NULL);
1355         assert_return(context->ref > 0, NULL);
1356
1357         ++context->ref;
1358         return context;
1359 }
1360
1361 grdev_context *grdev_context_unref(grdev_context *context) {
1362         if (!context)
1363                 return NULL;
1364
1365         assert_return(context->ref > 0, NULL);
1366
1367         if (--context->ref == 0)
1368                 context_cleanup(context);
1369
1370         return NULL;
1371 }