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