1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright (C) 2014 David Herrmann <dh.herrmann@gmail.com>
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.
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.
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/>.
24 * The modeset tool attaches to the session of the caller and shows a
25 * test-pattern on all displays of this session. It is meant as debugging tool
26 * for the grdev infrastructure.
29 #include <drm_fourcc.h>
38 #include <sys/ioctl.h>
40 #include <systemd/sd-bus.h>
41 #include <systemd/sd-event.h>
42 #include <systemd/sd-login.h>
47 #include "event-util.h"
49 #include "grdev-internal.h"
54 typedef struct Modeset Modeset;
61 sd_event_source *exit_src;
62 sysview_context *sysview;
64 grdev_session *grdev_session;
67 bool r_up, g_up, b_up;
73 static int modeset_exit_fn(sd_event_source *source, void *userdata) {
74 Modeset *m = userdata;
77 grdev_session_restore(m->grdev_session);
82 static Modeset *modeset_free(Modeset *m) {
86 m->grdev_session = grdev_session_free(m->grdev_session);
87 m->grdev = grdev_context_unref(m->grdev);
88 m->sysview = sysview_context_free(m->sysview);
89 m->exit_src = sd_event_source_unref(m->exit_src);
90 m->bus = sd_bus_unref(m->bus);
91 m->event = sd_event_unref(m->event);
99 DEFINE_TRIVIAL_CLEANUP_FUNC(Modeset*, modeset_free);
101 static bool is_my_tty(const char *session) {
107 /* Using logind's Controller API is highly fragile if there is already
108 * a session controller running. If it is registered as controller
109 * itself, TakeControl will simply fail. But if its a legacy controller
110 * that does not use logind's controller API, we must never register
111 * our own controller. Otherwise, we really mess up the VT. Therefore,
112 * only run in managed mode if there's no-one else. Furthermore, never
113 * try to access graphics devices if there's someone else. Unlike input
114 * devices, graphics devies cannot be shared easily. */
122 r = sd_session_get_vt(session, &vtnr);
123 if (r < 0 || vtnr < 1 || vtnr > 63)
127 r = ioctl(1, KDGETMODE, &mode);
128 if (r < 0 || mode != KD_TEXT)
132 if (r < 0 || minor(st.st_rdev) != vtnr)
138 static int modeset_new(Modeset **out) {
139 _cleanup_(modeset_freep) Modeset *m = NULL;
144 m = new0(Modeset, 1);
148 r = sd_pid_get_session(getpid(), &m->session);
150 log_error("Cannot retrieve logind session: %s", strerror(-r));
154 r = sd_session_get_seat(m->session, &m->seat);
156 log_error("Cannot retrieve seat of logind session: %s", strerror(-r));
160 m->my_tty = is_my_tty(m->session);
161 m->managed = m->my_tty && geteuid() > 0;
163 m->r = rand() % 0xff;
164 m->g = rand() % 0xff;
165 m->b = rand() % 0xff;
166 m->r_up = m->g_up = m->b_up = true;
168 r = sd_event_default(&m->event);
172 r = sd_bus_open_system(&m->bus);
176 r = sd_bus_attach_event(m->bus, m->event, SD_EVENT_PRIORITY_NORMAL);
180 r = sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1);
184 r = sd_event_add_signal(m->event, NULL, SIGTERM, NULL, NULL);
188 r = sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL);
192 r = sd_event_add_exit(m->event, &m->exit_src, modeset_exit_fn, m);
196 /* schedule before sd-bus close */
197 r = sd_event_source_set_priority(m->exit_src, -10);
201 r = sysview_context_new(&m->sysview,
202 SYSVIEW_CONTEXT_SCAN_LOGIND |
203 SYSVIEW_CONTEXT_SCAN_DRM,
210 r = grdev_context_new(&m->grdev, m->event, m->bus);
219 static uint8_t next_color(bool *up, uint8_t cur, unsigned int mod) {
222 /* generate smoothly morphing colors */
224 next = cur + (*up ? 1 : -1) * (rand() % mod);
225 if ((*up && next < cur) || (!*up && next > cur)) {
233 static void modeset_draw(Modeset *m, const grdev_display_target *t) {
237 assert(t->back->format == DRM_FORMAT_XRGB8888 || t->back->format == DRM_FORMAT_ARGB8888);
241 l = t->back->maps[0];
242 for (j = 0; j < t->height; ++j) {
243 for (k = 0; k < t->width; ++k) {
245 b[k] = (0xff << 24) | (m->r << 16) | (m->g << 8) | m->b;
248 l += t->back->strides[0];
252 static void modeset_render(Modeset *m, grdev_display *d) {
253 const grdev_display_target *t;
255 m->r = next_color(&m->r_up, m->r, 4);
256 m->g = next_color(&m->g_up, m->g, 3);
257 m->b = next_color(&m->b_up, m->b, 2);
259 GRDEV_DISPLAY_FOREACH_TARGET(d, t) {
261 grdev_display_flip_target(d, t);
264 grdev_session_commit(m->grdev_session);
267 static void modeset_grdev_fn(grdev_session *session, void *userdata, grdev_event *ev) {
268 Modeset *m = userdata;
271 case GRDEV_EVENT_DISPLAY_ADD:
272 grdev_display_enable(ev->display_add.display);
274 case GRDEV_EVENT_DISPLAY_REMOVE:
276 case GRDEV_EVENT_DISPLAY_CHANGE:
278 case GRDEV_EVENT_DISPLAY_FRAME:
279 modeset_render(m, ev->display_frame.display);
284 static int modeset_sysview_fn(sysview_context *c, void *userdata, sysview_event *ev) {
285 unsigned int flags, type;
286 Modeset *m = userdata;
292 case SYSVIEW_EVENT_SESSION_FILTER:
293 if (streq_ptr(m->session, ev->session_filter.id))
297 case SYSVIEW_EVENT_SESSION_ADD:
298 assert(!m->grdev_session);
300 name = sysview_session_get_name(ev->session_add.session);
304 flags |= GRDEV_SESSION_MANAGED;
306 r = grdev_session_new(&m->grdev_session,
313 log_error("Cannot create grdev session: %s", strerror(-r));
318 r = sysview_session_take_control(ev->session_add.session);
320 log_error("Cannot request session control: %s", strerror(-r));
325 grdev_session_enable(m->grdev_session);
328 case SYSVIEW_EVENT_SESSION_REMOVE:
329 if (!m->grdev_session)
332 grdev_session_restore(m->grdev_session);
333 grdev_session_disable(m->grdev_session);
334 m->grdev_session = grdev_session_free(m->grdev_session);
335 if (sd_event_get_exit_code(m->event, &r) == -ENODATA)
336 sd_event_exit(m->event, 0);
338 case SYSVIEW_EVENT_SESSION_ATTACH:
339 d = ev->session_attach.device;
340 type = sysview_device_get_type(d);
341 if (type == SYSVIEW_DEVICE_DRM)
342 grdev_session_add_drm(m->grdev_session, sysview_device_get_ud(d));
345 case SYSVIEW_EVENT_SESSION_DETACH:
346 d = ev->session_detach.device;
347 type = sysview_device_get_type(d);
348 if (type == SYSVIEW_DEVICE_DRM)
349 grdev_session_remove_drm(m->grdev_session, sysview_device_get_ud(d));
352 case SYSVIEW_EVENT_SESSION_REFRESH:
353 d = ev->session_refresh.device;
354 type = sysview_device_get_type(d);
355 if (type == SYSVIEW_DEVICE_DRM)
356 grdev_session_hotplug_drm(m->grdev_session, ev->session_refresh.ud);
359 case SYSVIEW_EVENT_SESSION_CONTROL:
360 r = ev->session_control.error;
362 log_error("Cannot acquire session control: %s", strerror(-r));
366 r = ioctl(1, KDSKBMODE, K_UNICODE);
368 log_error("Cannot set K_UNICODE on stdout: %m");
378 static int modeset_run(Modeset *m) {
379 struct termios in_attr, saved_attr;
385 log_warning("You need to run this program on a free VT");
389 if (!m->managed && geteuid() > 0)
390 log_warning("You run in unmanaged mode without being root. This is likely to fail..");
392 printf("modeset - Show test pattern on selected graphics devices\n"
393 " Running on seat '%s' in user-session '%s'\n"
394 " Exit by pressing ^C\n\n",
395 m->seat ? : "seat0", m->session ? : "<none>");
397 r = sysview_context_start(m->sysview, modeset_sysview_fn, m);
401 r = tcgetattr(0, &in_attr);
407 saved_attr = in_attr;
408 in_attr.c_lflag &= ~ECHO;
410 r = tcsetattr(0, TCSANOW, &in_attr);
416 r = sd_event_loop(m->event);
417 tcsetattr(0, TCSANOW, &saved_attr);
418 printf("exiting..\n");
421 sysview_context_stop(m->sysview);
425 static int help(void) {
426 printf("%s [OPTIONS...]\n\n"
427 "Show test pattern on all selected graphics devices.\n\n"
428 " -h --help Show this help\n"
429 " --version Show package version\n"
430 , program_invocation_short_name);
435 static int parse_argv(int argc, char *argv[]) {
439 static const struct option options[] = {
440 { "help", no_argument, NULL, 'h' },
441 { "version", no_argument, NULL, ARG_VERSION },
449 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
456 puts(PACKAGE_STRING);
457 puts(SYSTEMD_FEATURES);
464 assert_not_reached("Unhandled option");
468 log_error("Too many arguments");
475 int main(int argc, char *argv[]) {
476 _cleanup_(modeset_freep) Modeset *m = NULL;
479 log_set_target(LOG_TARGET_AUTO);
480 log_parse_environment();
485 r = parse_argv(argc, argv);
496 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;