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