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