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
->fb
->format
== DRM_FORMAT_XRGB8888
|| t
->fb
->format
== DRM_FORMAT_ARGB8888
);
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
->fb
->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
, 0) {
261 grdev_display_flip_target(d
, t
, 1);
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
);
273 modeset_render(m
, ev
->display_add
.display
);
275 case GRDEV_EVENT_DISPLAY_REMOVE
:
277 case GRDEV_EVENT_DISPLAY_CHANGE
:
278 modeset_render(m
, ev
->display_change
.display
);
280 case GRDEV_EVENT_DISPLAY_FRAME
:
281 modeset_render(m
, ev
->display_frame
.display
);
286 static int modeset_sysview_fn(sysview_context
*c
, void *userdata
, sysview_event
*ev
) {
287 unsigned int flags
, type
;
288 Modeset
*m
= userdata
;
294 case SYSVIEW_EVENT_SESSION_FILTER
:
295 if (streq_ptr(m
->session
, ev
->session_filter
.id
))
299 case SYSVIEW_EVENT_SESSION_ADD
:
300 assert(!m
->grdev_session
);
302 name
= sysview_session_get_name(ev
->session_add
.session
);
306 flags
|= GRDEV_SESSION_MANAGED
;
308 r
= grdev_session_new(&m
->grdev_session
,
315 log_error("Cannot create grdev session: %s", strerror(-r
));
320 r
= sysview_session_take_control(ev
->session_add
.session
);
322 log_error("Cannot request session control: %s", strerror(-r
));
327 grdev_session_enable(m
->grdev_session
);
330 case SYSVIEW_EVENT_SESSION_REMOVE
:
331 if (!m
->grdev_session
)
334 grdev_session_restore(m
->grdev_session
);
335 grdev_session_disable(m
->grdev_session
);
336 m
->grdev_session
= grdev_session_free(m
->grdev_session
);
337 sd_event_exit(m
->event
, 0);
339 case SYSVIEW_EVENT_SESSION_ATTACH
:
340 d
= ev
->session_attach
.device
;
341 type
= sysview_device_get_type(d
);
342 if (type
== SYSVIEW_DEVICE_DRM
)
343 grdev_session_add_drm(m
->grdev_session
, sysview_device_get_ud(d
));
346 case SYSVIEW_EVENT_SESSION_DETACH
:
347 d
= ev
->session_detach
.device
;
348 type
= sysview_device_get_type(d
);
349 if (type
== SYSVIEW_DEVICE_DRM
)
350 grdev_session_remove_drm(m
->grdev_session
, sysview_device_get_ud(d
));
353 case SYSVIEW_EVENT_SESSION_CONTROL
:
354 r
= ev
->session_control
.error
;
356 log_error("Cannot acquire session control: %s", strerror(-r
));
360 r
= ioctl(1, KDSKBMODE
, K_UNICODE
);
362 log_error("Cannot set K_UNICODE on stdout: %m");
372 static int modeset_run(Modeset
*m
) {
373 struct termios in_attr
, saved_attr
;
379 log_warning("You need to run this program on a free VT");
383 if (!m
->managed
&& geteuid() > 0)
384 log_warning("You run in unmanaged mode without being root. This is likely to fail..");
386 printf("modeset - Show test pattern on selected graphics devices\n"
387 " Running on seat '%s' in user-session '%s'\n"
388 " Exit by pressing ^C\n\n",
389 m
->seat
? : "seat0", m
->session
? : "<none>");
391 r
= sysview_context_start(m
->sysview
, modeset_sysview_fn
, m
);
395 r
= tcgetattr(0, &in_attr
);
401 saved_attr
= in_attr
;
402 in_attr
.c_lflag
&= ~ECHO
;
404 r
= tcsetattr(0, TCSANOW
, &in_attr
);
410 r
= sd_event_loop(m
->event
);
411 tcsetattr(0, TCSANOW
, &saved_attr
);
412 printf("exiting..\n");
415 sysview_context_stop(m
->sysview
);
419 static int help(void) {
420 printf("%s [OPTIONS...]\n\n"
421 "Show test pattern on all selected graphics devices.\n\n"
422 " -h --help Show this help\n"
423 " --version Show package version\n"
424 , program_invocation_short_name
);
429 static int parse_argv(int argc
, char *argv
[]) {
433 static const struct option options
[] = {
434 { "help", no_argument
, NULL
, 'h' },
435 { "version", no_argument
, NULL
, ARG_VERSION
},
443 while ((c
= getopt_long(argc
, argv
, "h", options
, NULL
)) >= 0)
450 puts(PACKAGE_STRING
);
451 puts(SYSTEMD_FEATURES
);
458 assert_not_reached("Unhandled option");
462 log_error("Too many arguments");
469 int main(int argc
, char *argv
[]) {
470 _cleanup_(modeset_freep
) Modeset
*m
= NULL
;
473 log_set_target(LOG_TARGET_AUTO
);
474 log_parse_environment();
479 r
= parse_argv(argc
, argv
);
490 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;