Zbigniew Jędrzejewski-Szmek 62fe94
From 810626a80de8361dfbe27110d585023b3d6167a6 Mon Sep 17 00:00:00 2001
Zbigniew Jędrzejewski-Szmek 62fe94
From: David Herrmann <dh.herrmann@gmail.com>
Zbigniew Jędrzejewski-Szmek 62fe94
Date: Fri, 19 Sep 2014 14:48:54 +0200
Zbigniew Jędrzejewski-Szmek 62fe94
Subject: [PATCH] terminal: add systemd-modeset debugging tool
Zbigniew Jędrzejewski-Szmek 62fe94
Zbigniew Jędrzejewski-Szmek 62fe94
The systemd-modeset tool is meant to debug grdev issues. It simply
Zbigniew Jędrzejewski-Szmek 62fe94
displays morphing colors on any found display. This is pretty handy to
Zbigniew Jędrzejewski-Szmek 62fe94
look for tearing in the backends and debug hotplug issues.
Zbigniew Jędrzejewski-Szmek 62fe94
Zbigniew Jędrzejewski-Szmek 62fe94
Note that this tool requires systemd-logind to be compiled from git
Zbigniew Jędrzejewski-Szmek 62fe94
(there're important fixes that haven't been released, yet).
Zbigniew Jędrzejewski-Szmek 62fe94
---
Zbigniew Jędrzejewski-Szmek 62fe94
 .gitignore                        |   1 +
Zbigniew Jędrzejewski-Szmek 62fe94
 Makefile.am                       |  14 ++
Zbigniew Jędrzejewski-Szmek 62fe94
 src/libsystemd-terminal/modeset.c | 491 ++++++++++++++++++++++++++++++++++++++
Zbigniew Jędrzejewski-Szmek 62fe94
 3 files changed, 506 insertions(+)
Zbigniew Jędrzejewski-Szmek 62fe94
 create mode 100644 src/libsystemd-terminal/modeset.c
Zbigniew Jędrzejewski-Szmek 62fe94
Zbigniew Jędrzejewski-Szmek 62fe94
diff --git a/.gitignore b/.gitignore
Zbigniew Jędrzejewski-Szmek 62fe94
index f8650870a3..288946029b 100644
Zbigniew Jędrzejewski-Szmek 62fe94
--- a/.gitignore
Zbigniew Jędrzejewski-Szmek 62fe94
+++ b/.gitignore
Zbigniew Jędrzejewski-Szmek 62fe94
@@ -90,6 +90,7 @@
Zbigniew Jędrzejewski-Szmek 62fe94
 /systemd-logind
Zbigniew Jędrzejewski-Szmek 62fe94
 /systemd-machine-id-setup
Zbigniew Jędrzejewski-Szmek 62fe94
 /systemd-machined
Zbigniew Jędrzejewski-Szmek 62fe94
+/systemd-modeset
Zbigniew Jędrzejewski-Szmek 62fe94
 /systemd-modules-load
Zbigniew Jędrzejewski-Szmek 62fe94
 /systemd-multi-seat-x
Zbigniew Jędrzejewski-Szmek 62fe94
 /systemd-networkd
Zbigniew Jędrzejewski-Szmek 62fe94
diff --git a/Makefile.am b/Makefile.am
Zbigniew Jędrzejewski-Szmek 62fe94
index be25023c75..f80ffc6749 100644
Zbigniew Jędrzejewski-Szmek 62fe94
--- a/Makefile.am
Zbigniew Jędrzejewski-Szmek 62fe94
+++ b/Makefile.am
Zbigniew Jędrzejewski-Szmek 62fe94
@@ -2987,6 +2987,7 @@ noinst_LTLIBRARIES += \
Zbigniew Jędrzejewski-Szmek 62fe94
 
Zbigniew Jędrzejewski-Szmek 62fe94
 noinst_PROGRAMS += \
Zbigniew Jędrzejewski-Szmek 62fe94
 	systemd-evcat \
Zbigniew Jędrzejewski-Szmek 62fe94
+	systemd-modeset \
Zbigniew Jędrzejewski-Szmek 62fe94
 	systemd-subterm
Zbigniew Jędrzejewski-Szmek 62fe94
 
Zbigniew Jędrzejewski-Szmek 62fe94
 unifontdatadir=$(datadir)/unifont
Zbigniew Jędrzejewski-Szmek 62fe94
@@ -3045,6 +3046,19 @@ systemd_evcat_LDADD = \
Zbigniew Jędrzejewski-Szmek 62fe94
 	libsystemd-shared.la \
Zbigniew Jędrzejewski-Szmek 62fe94
 	$(TERMINAL_LIBS)
Zbigniew Jędrzejewski-Szmek 62fe94
 
Zbigniew Jędrzejewski-Szmek 62fe94
+systemd_modeset_CFLAGS = \
Zbigniew Jędrzejewski-Szmek 62fe94
+	$(AM_CFLAGS) \
Zbigniew Jędrzejewski-Szmek 62fe94
+	$(TERMINAL_CFLAGS)
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+systemd_modeset_SOURCES = \
Zbigniew Jędrzejewski-Szmek 62fe94
+	src/libsystemd-terminal/modeset.c
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+systemd_modeset_LDADD = \
Zbigniew Jędrzejewski-Szmek 62fe94
+	libsystemd-terminal.la \
Zbigniew Jędrzejewski-Szmek 62fe94
+	libsystemd-internal.la \
Zbigniew Jędrzejewski-Szmek 62fe94
+	libsystemd-shared.la \
Zbigniew Jędrzejewski-Szmek 62fe94
+	$(TERMINAL_LIBS)
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
 systemd_subterm_SOURCES = \
Zbigniew Jędrzejewski-Szmek 62fe94
 	src/libsystemd-terminal/subterm.c
Zbigniew Jędrzejewski-Szmek 62fe94
 
Zbigniew Jędrzejewski-Szmek 62fe94
diff --git a/src/libsystemd-terminal/modeset.c b/src/libsystemd-terminal/modeset.c
Zbigniew Jędrzejewski-Szmek 62fe94
new file mode 100644
Zbigniew Jędrzejewski-Szmek 62fe94
index 0000000000..02ed1a8987
Zbigniew Jędrzejewski-Szmek 62fe94
--- /dev/null
Zbigniew Jędrzejewski-Szmek 62fe94
+++ b/src/libsystemd-terminal/modeset.c
Zbigniew Jędrzejewski-Szmek 62fe94
@@ -0,0 +1,491 @@
Zbigniew Jędrzejewski-Szmek 62fe94
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+/***
Zbigniew Jędrzejewski-Szmek 62fe94
+  This file is part of systemd.
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+  Copyright (C) 2014 David Herrmann <dh.herrmann@gmail.com>
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+  systemd is free software; you can redistribute it and/or modify it
Zbigniew Jędrzejewski-Szmek 62fe94
+  under the terms of the GNU Lesser General Public License as published by
Zbigniew Jędrzejewski-Szmek 62fe94
+  the Free Software Foundation; either version 2.1 of the License, or
Zbigniew Jędrzejewski-Szmek 62fe94
+  (at your option) any later version.
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+  systemd is distributed in the hope that it will be useful, but
Zbigniew Jędrzejewski-Szmek 62fe94
+  WITHOUT ANY WARRANTY; without even the implied warranty of
Zbigniew Jędrzejewski-Szmek 62fe94
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Zbigniew Jędrzejewski-Szmek 62fe94
+  Lesser General Public License for more details.
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+  You should have received a copy of the GNU Lesser General Public License
Zbigniew Jędrzejewski-Szmek 62fe94
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
Zbigniew Jędrzejewski-Szmek 62fe94
+***/
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+/*
Zbigniew Jędrzejewski-Szmek 62fe94
+ * Modeset Testing
Zbigniew Jędrzejewski-Szmek 62fe94
+ * The modeset tool attaches to the session of the caller and shows a
Zbigniew Jędrzejewski-Szmek 62fe94
+ * test-pattern on all displays of this session. It is meant as debugging tool
Zbigniew Jędrzejewski-Szmek 62fe94
+ * for the grdev infrastructure.
Zbigniew Jędrzejewski-Szmek 62fe94
+ */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <drm_fourcc.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <errno.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <getopt.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <linux/kd.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <linux/vt.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <stdbool.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <stdio.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <stdlib.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <string.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <sys/ioctl.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <sys/stat.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <systemd/sd-bus.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <systemd/sd-event.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <systemd/sd-login.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <termios.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <unistd.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include "build.h"
Zbigniew Jędrzejewski-Szmek 62fe94
+#include "bus-util.h"
Zbigniew Jędrzejewski-Szmek 62fe94
+#include "event-util.h"
Zbigniew Jędrzejewski-Szmek 62fe94
+#include "grdev.h"
Zbigniew Jędrzejewski-Szmek 62fe94
+#include "grdev-internal.h"
Zbigniew Jędrzejewski-Szmek 62fe94
+#include "macro.h"
Zbigniew Jędrzejewski-Szmek 62fe94
+#include "sysview.h"
Zbigniew Jędrzejewski-Szmek 62fe94
+#include "util.h"
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+typedef struct Modeset Modeset;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+struct Modeset {
Zbigniew Jędrzejewski-Szmek 62fe94
+        char *session;
Zbigniew Jędrzejewski-Szmek 62fe94
+        char *seat;
Zbigniew Jędrzejewski-Szmek 62fe94
+        sd_event *event;
Zbigniew Jędrzejewski-Szmek 62fe94
+        sd_bus *bus;
Zbigniew Jędrzejewski-Szmek 62fe94
+        sd_event_source *exit_src;
Zbigniew Jędrzejewski-Szmek 62fe94
+        sysview_context *sysview;
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_context *grdev;
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_session *grdev_session;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        uint8_t r, g, b;
Zbigniew Jędrzejewski-Szmek 62fe94
+        bool r_up, g_up, b_up;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        bool my_tty : 1;
Zbigniew Jędrzejewski-Szmek 62fe94
+        bool managed : 1;
Zbigniew Jędrzejewski-Szmek 62fe94
+};
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static int modeset_exit_fn(sd_event_source *source, void *userdata) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        Modeset *m = userdata;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (m->grdev_session)
Zbigniew Jędrzejewski-Szmek 62fe94
+                grdev_session_restore(m->grdev_session);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static Modeset *modeset_free(Modeset *m) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!m)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        m->grdev_session = grdev_session_free(m->grdev_session);
Zbigniew Jędrzejewski-Szmek 62fe94
+        m->grdev = grdev_context_unref(m->grdev);
Zbigniew Jędrzejewski-Szmek 62fe94
+        m->sysview = sysview_context_free(m->sysview);
Zbigniew Jędrzejewski-Szmek 62fe94
+        m->exit_src = sd_event_source_unref(m->exit_src);
Zbigniew Jędrzejewski-Szmek 62fe94
+        m->bus = sd_bus_unref(m->bus);
Zbigniew Jędrzejewski-Szmek 62fe94
+        m->event = sd_event_unref(m->event);
Zbigniew Jędrzejewski-Szmek 62fe94
+        free(m->seat);
Zbigniew Jędrzejewski-Szmek 62fe94
+        free(m->session);
Zbigniew Jędrzejewski-Szmek 62fe94
+        free(m);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        return NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+DEFINE_TRIVIAL_CLEANUP_FUNC(Modeset*, modeset_free);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static bool is_my_tty(const char *session) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        unsigned int vtnr;
Zbigniew Jędrzejewski-Szmek 62fe94
+        struct stat st;
Zbigniew Jędrzejewski-Szmek 62fe94
+        long mode;
Zbigniew Jędrzejewski-Szmek 62fe94
+        int r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        /* Using logind's Controller API is highly fragile if there is already
Zbigniew Jędrzejewski-Szmek 62fe94
+         * a session controller running. If it is registered as controller
Zbigniew Jędrzejewski-Szmek 62fe94
+         * itself, TakeControl will simply fail. But if its a legacy controller
Zbigniew Jędrzejewski-Szmek 62fe94
+         * that does not use logind's controller API, we must never register
Zbigniew Jędrzejewski-Szmek 62fe94
+         * our own controller. Otherwise, we really mess up the VT. Therefore,
Zbigniew Jędrzejewski-Szmek 62fe94
+         * only run in managed mode if there's no-one else.  Furthermore, never
Zbigniew Jędrzejewski-Szmek 62fe94
+         * try to access graphics devices if there's someone else. Unlike input
Zbigniew Jędrzejewski-Szmek 62fe94
+         * devices, graphics devies cannot be shared easily. */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!isatty(1))
Zbigniew Jędrzejewski-Szmek 62fe94
+                return false;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!session)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return false;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = sd_session_get_vt(session, &vtnr);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0 || vtnr < 1 || vtnr > 63)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return false;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        mode = 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = ioctl(1, KDGETMODE, &mode);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0 || mode != KD_TEXT)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return false;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = fstat(1, &st);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0 || minor(st.st_rdev) != vtnr)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return false;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        return true;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static int modeset_new(Modeset **out) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        _cleanup_(modeset_freep) Modeset *m = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+        int r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(out);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        m = new0(Modeset, 1);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!m)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return log_oom();
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = sd_pid_get_session(getpid(), &m->session);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                log_error("Cannot retrieve logind session: %s", strerror(-r));
Zbigniew Jędrzejewski-Szmek 62fe94
+                return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = sd_session_get_seat(m->session, &m->seat);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                log_error("Cannot retrieve seat of logind session: %s", strerror(-r));
Zbigniew Jędrzejewski-Szmek 62fe94
+                return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        m->my_tty = is_my_tty(m->session);
Zbigniew Jędrzejewski-Szmek 62fe94
+        m->managed = m->my_tty && geteuid() > 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        m->r = rand() % 0xff;
Zbigniew Jędrzejewski-Szmek 62fe94
+        m->g = rand() % 0xff;
Zbigniew Jędrzejewski-Szmek 62fe94
+        m->b = rand() % 0xff;
Zbigniew Jędrzejewski-Szmek 62fe94
+        m->r_up = m->g_up = m->b_up = true;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = sd_event_default(&m->event);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = sd_bus_open_system(&m->bus);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = sd_bus_attach_event(m->bus, m->event, SD_EVENT_PRIORITY_NORMAL);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = sd_event_add_signal(m->event, NULL, SIGTERM, NULL, NULL);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = sd_event_add_exit(m->event, &m->exit_src, modeset_exit_fn, m);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        /* schedule before sd-bus close */
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = sd_event_source_set_priority(m->exit_src, -10);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = sysview_context_new(&m->sysview,
Zbigniew Jędrzejewski-Szmek 62fe94
+                                SYSVIEW_CONTEXT_SCAN_LOGIND |
Zbigniew Jędrzejewski-Szmek 62fe94
+                                SYSVIEW_CONTEXT_SCAN_DRM,
Zbigniew Jędrzejewski-Szmek 62fe94
+                                m->event,
Zbigniew Jędrzejewski-Szmek 62fe94
+                                m->bus,
Zbigniew Jędrzejewski-Szmek 62fe94
+                                NULL);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = grdev_context_new(&m->grdev, m->event, m->bus);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        *out = m;
Zbigniew Jędrzejewski-Szmek 62fe94
+        m = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+        return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static uint8_t next_color(bool *up, uint8_t cur, unsigned int mod) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        uint8_t next;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        /* generate smoothly morphing colors */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        next = cur + (*up ? 1 : -1) * (rand() % mod);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if ((*up && next < cur) || (!*up && next > cur)) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                *up = !*up;
Zbigniew Jędrzejewski-Szmek 62fe94
+                next = cur;
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        return next;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void modeset_draw(Modeset *m, const grdev_display_target *t) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        uint32_t j, k, *b;
Zbigniew Jędrzejewski-Szmek 62fe94
+        uint8_t *l;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(t->fb->format == DRM_FORMAT_XRGB8888 || t->fb->format == DRM_FORMAT_ARGB8888);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(!t->rotate);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(!t->flip);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        l = t->fb->maps[0];
Zbigniew Jędrzejewski-Szmek 62fe94
+        for (j = 0; j < t->height; ++j) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                for (k = 0; k < t->width; ++k) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        b = (uint32_t*)l;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        b[k] = (0xff << 24) | (m->r << 16) | (m->g << 8) | m->b;
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                l += t->fb->strides[0];
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void modeset_render(Modeset *m, grdev_display *d) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        const grdev_display_target *t;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        m->r = next_color(&m->r_up, m->r, 20);
Zbigniew Jędrzejewski-Szmek 62fe94
+        m->g = next_color(&m->g_up, m->g, 10);
Zbigniew Jędrzejewski-Szmek 62fe94
+        m->b = next_color(&m->b_up, m->b, 5);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        GRDEV_DISPLAY_FOREACH_TARGET(d, t, 0) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                modeset_draw(m, t);
Zbigniew Jędrzejewski-Szmek 62fe94
+                grdev_display_flip_target(d, t, 1);
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        grdev_session_commit(m->grdev_session);
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void modeset_grdev_fn(grdev_session *session, void *userdata, grdev_event *ev) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        Modeset *m = userdata;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        switch (ev->type) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        case GRDEV_EVENT_DISPLAY_ADD:
Zbigniew Jędrzejewski-Szmek 62fe94
+                grdev_display_enable(ev->display_add.display);
Zbigniew Jędrzejewski-Szmek 62fe94
+                modeset_render(m, ev->display_add.display);
Zbigniew Jędrzejewski-Szmek 62fe94
+                break;
Zbigniew Jędrzejewski-Szmek 62fe94
+        case GRDEV_EVENT_DISPLAY_REMOVE:
Zbigniew Jędrzejewski-Szmek 62fe94
+                break;
Zbigniew Jędrzejewski-Szmek 62fe94
+        case GRDEV_EVENT_DISPLAY_CHANGE:
Zbigniew Jędrzejewski-Szmek 62fe94
+                modeset_render(m, ev->display_change.display);
Zbigniew Jędrzejewski-Szmek 62fe94
+                break;
Zbigniew Jędrzejewski-Szmek 62fe94
+        case GRDEV_EVENT_DISPLAY_FRAME:
Zbigniew Jędrzejewski-Szmek 62fe94
+                modeset_render(m, ev->display_frame.display);
Zbigniew Jędrzejewski-Szmek 62fe94
+                break;
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static int modeset_sysview_fn(sysview_context *c, void *userdata, sysview_event *ev) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        unsigned int flags, type;
Zbigniew Jędrzejewski-Szmek 62fe94
+        Modeset *m = userdata;
Zbigniew Jędrzejewski-Szmek 62fe94
+        sysview_device *d;
Zbigniew Jędrzejewski-Szmek 62fe94
+        const char *name;
Zbigniew Jędrzejewski-Szmek 62fe94
+        int r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        switch (ev->type) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        case SYSVIEW_EVENT_SESSION_FILTER:
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (streq_ptr(m->session, ev->session_filter.id))
Zbigniew Jędrzejewski-Szmek 62fe94
+                        return 1;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                break;
Zbigniew Jędrzejewski-Szmek 62fe94
+        case SYSVIEW_EVENT_SESSION_ADD:
Zbigniew Jędrzejewski-Szmek 62fe94
+                assert(!m->grdev_session);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                name = sysview_session_get_name(ev->session_add.session);
Zbigniew Jędrzejewski-Szmek 62fe94
+                flags = 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (m->managed)
Zbigniew Jędrzejewski-Szmek 62fe94
+                        flags |= GRDEV_SESSION_MANAGED;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                r = grdev_session_new(&m->grdev_session,
Zbigniew Jędrzejewski-Szmek 62fe94
+                                      m->grdev,
Zbigniew Jędrzejewski-Szmek 62fe94
+                                      flags,
Zbigniew Jędrzejewski-Szmek 62fe94
+                                      name,
Zbigniew Jędrzejewski-Szmek 62fe94
+                                      modeset_grdev_fn,
Zbigniew Jędrzejewski-Szmek 62fe94
+                                      m);
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (r < 0) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        log_error("Cannot create grdev session: %s", strerror(-r));
Zbigniew Jędrzejewski-Szmek 62fe94
+                        return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (m->managed) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        r = sysview_session_take_control(ev->session_add.session);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (r < 0) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                                log_error("Cannot request session control: %s", strerror(-r));
Zbigniew Jędrzejewski-Szmek 62fe94
+                                return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        }
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                grdev_session_enable(m->grdev_session);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                break;
Zbigniew Jędrzejewski-Szmek 62fe94
+        case SYSVIEW_EVENT_SESSION_REMOVE:
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (!m->grdev_session)
Zbigniew Jędrzejewski-Szmek 62fe94
+                        return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                grdev_session_restore(m->grdev_session);
Zbigniew Jędrzejewski-Szmek 62fe94
+                grdev_session_disable(m->grdev_session);
Zbigniew Jędrzejewski-Szmek 62fe94
+                m->grdev_session = grdev_session_free(m->grdev_session);
Zbigniew Jędrzejewski-Szmek 62fe94
+                sd_event_exit(m->event, 0);
Zbigniew Jędrzejewski-Szmek 62fe94
+                break;
Zbigniew Jędrzejewski-Szmek 62fe94
+        case SYSVIEW_EVENT_SESSION_ATTACH:
Zbigniew Jędrzejewski-Szmek 62fe94
+                d = ev->session_attach.device;
Zbigniew Jędrzejewski-Szmek 62fe94
+                type = sysview_device_get_type(d);
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (type == SYSVIEW_DEVICE_DRM)
Zbigniew Jędrzejewski-Szmek 62fe94
+                        grdev_session_add_drm(m->grdev_session, sysview_device_get_ud(d));
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                break;
Zbigniew Jędrzejewski-Szmek 62fe94
+        case SYSVIEW_EVENT_SESSION_DETACH:
Zbigniew Jędrzejewski-Szmek 62fe94
+                d = ev->session_detach.device;
Zbigniew Jędrzejewski-Szmek 62fe94
+                type = sysview_device_get_type(d);
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (type == SYSVIEW_DEVICE_DRM)
Zbigniew Jędrzejewski-Szmek 62fe94
+                        grdev_session_remove_drm(m->grdev_session, sysview_device_get_ud(d));
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                break;
Zbigniew Jędrzejewski-Szmek 62fe94
+        case SYSVIEW_EVENT_SESSION_CONTROL:
Zbigniew Jędrzejewski-Szmek 62fe94
+                r = ev->session_control.error;
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (r < 0) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        log_error("Cannot acquire session control: %s", strerror(-r));
Zbigniew Jędrzejewski-Szmek 62fe94
+                        return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                r = ioctl(1, KDSKBMODE, K_UNICODE);
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (r < 0) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        log_error("Cannot set K_UNICODE on stdout: %m");
Zbigniew Jędrzejewski-Szmek 62fe94
+                        return -errno;
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                break;
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static int modeset_run(Modeset *m) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        struct termios in_attr, saved_attr;
Zbigniew Jędrzejewski-Szmek 62fe94
+        int r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(m);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!m->my_tty) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                log_warning("You need to run this program on a free VT");
Zbigniew Jędrzejewski-Szmek 62fe94
+                return -EACCES;
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!m->managed && geteuid() > 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                log_warning("You run in unmanaged mode without being root. This is likely to fail..");
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        printf("modeset - Show test pattern on selected graphics devices\n"
Zbigniew Jędrzejewski-Szmek 62fe94
+               "          Running on seat '%s' in user-session '%s'\n"
Zbigniew Jędrzejewski-Szmek 62fe94
+               "          Exit by pressing ^C\n\n",
Zbigniew Jędrzejewski-Szmek 62fe94
+               m->seat ? : "seat0", m->session ? : "<none>");
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = sysview_context_start(m->sysview, modeset_sysview_fn, m);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                goto out;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = tcgetattr(0, &in_attr);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                r = -errno;
Zbigniew Jędrzejewski-Szmek 62fe94
+                goto out;
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        saved_attr = in_attr;
Zbigniew Jędrzejewski-Szmek 62fe94
+        in_attr.c_lflag &= ~ECHO;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = tcsetattr(0, TCSANOW, &in_attr);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                r = -errno;
Zbigniew Jędrzejewski-Szmek 62fe94
+                goto out;
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = sd_event_loop(m->event);
Zbigniew Jędrzejewski-Szmek 62fe94
+        tcsetattr(0, TCSANOW, &saved_attr);
Zbigniew Jędrzejewski-Szmek 62fe94
+        printf("exiting..\n");
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+out:
Zbigniew Jędrzejewski-Szmek 62fe94
+        sysview_context_stop(m->sysview);
Zbigniew Jędrzejewski-Szmek 62fe94
+        return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static int help(void) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        printf("%s [OPTIONS...]\n\n"
Zbigniew Jędrzejewski-Szmek 62fe94
+               "Show test pattern on all selected graphics devices.\n\n"
Zbigniew Jędrzejewski-Szmek 62fe94
+               "  -h --help               Show this help\n"
Zbigniew Jędrzejewski-Szmek 62fe94
+               "     --version            Show package version\n"
Zbigniew Jędrzejewski-Szmek 62fe94
+               , program_invocation_short_name);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static int parse_argv(int argc, char *argv[]) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        enum {
Zbigniew Jędrzejewski-Szmek 62fe94
+                ARG_VERSION = 0x100,
Zbigniew Jędrzejewski-Szmek 62fe94
+        };
Zbigniew Jędrzejewski-Szmek 62fe94
+        static const struct option options[] = {
Zbigniew Jędrzejewski-Szmek 62fe94
+                { "help",       no_argument,    NULL, 'h'               },
Zbigniew Jędrzejewski-Szmek 62fe94
+                { "version",    no_argument,    NULL, ARG_VERSION       },
Zbigniew Jędrzejewski-Szmek 62fe94
+                {},
Zbigniew Jędrzejewski-Szmek 62fe94
+        };
Zbigniew Jędrzejewski-Szmek 62fe94
+        int c;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(argc >= 0);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(argv);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                switch (c) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                case 'h':
Zbigniew Jędrzejewski-Szmek 62fe94
+                        help();
Zbigniew Jędrzejewski-Szmek 62fe94
+                        return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                case ARG_VERSION:
Zbigniew Jędrzejewski-Szmek 62fe94
+                        puts(PACKAGE_STRING);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        puts(SYSTEMD_FEATURES);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                case '?':
Zbigniew Jędrzejewski-Szmek 62fe94
+                        return -EINVAL;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                default:
Zbigniew Jędrzejewski-Szmek 62fe94
+                        assert_not_reached("Unhandled option");
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (argc > optind) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                log_error("Too many arguments");
Zbigniew Jędrzejewski-Szmek 62fe94
+                return -EINVAL;
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        return 1;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+int main(int argc, char *argv[]) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        _cleanup_(modeset_freep) Modeset *m = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+        int r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        log_set_target(LOG_TARGET_AUTO);
Zbigniew Jędrzejewski-Szmek 62fe94
+        log_parse_environment();
Zbigniew Jędrzejewski-Szmek 62fe94
+        log_open();
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        srand(time(NULL));
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = parse_argv(argc, argv);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r <= 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                goto finish;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = modeset_new(&m);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                goto finish;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = modeset_run(m);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+finish:
Zbigniew Jędrzejewski-Szmek 62fe94
+        return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
Zbigniew Jędrzejewski-Szmek 62fe94
+}