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>
36 #include <sys/ioctl.h>
45 #include "random-util.h"
46 #include "signal-util.h"
51 typedef struct Modeset Modeset
;
58 sd_event_source
*exit_src
;
59 sysview_context
*sysview
;
61 grdev_session
*grdev_session
;
64 bool r_up
, g_up
, b_up
;
70 static int modeset_exit_fn(sd_event_source
*source
, void *userdata
) {
71 Modeset
*m
= userdata
;
74 grdev_session_restore(m
->grdev_session
);
79 static Modeset
*modeset_free(Modeset
*m
) {
83 m
->grdev_session
= grdev_session_free(m
->grdev_session
);
84 m
->grdev
= grdev_context_unref(m
->grdev
);
85 m
->sysview
= sysview_context_free(m
->sysview
);
86 m
->exit_src
= sd_event_source_unref(m
->exit_src
);
87 m
->bus
= sd_bus_unref(m
->bus
);
88 m
->event
= sd_event_unref(m
->event
);
96 DEFINE_TRIVIAL_CLEANUP_FUNC(Modeset
*, modeset_free
);
98 static bool is_my_tty(const char *session
) {
104 /* Using logind's Controller API is highly fragile if there is already
105 * a session controller running. If it is registered as controller
106 * itself, TakeControl will simply fail. But if its a legacy controller
107 * that does not use logind's controller API, we must never register
108 * our own controller. Otherwise, we really mess up the VT. Therefore,
109 * only run in managed mode if there's no-one else. Furthermore, never
110 * try to access graphics devices if there's someone else. Unlike input
111 * devices, graphics devies cannot be shared easily. */
119 r
= sd_session_get_vt(session
, &vtnr
);
120 if (r
< 0 || vtnr
< 1 || vtnr
> 63)
124 r
= ioctl(1, KDGETMODE
, &mode
);
125 if (r
< 0 || mode
!= KD_TEXT
)
129 if (r
< 0 || minor(st
.st_rdev
) != vtnr
)
135 static int modeset_new(Modeset
**out
) {
136 _cleanup_(modeset_freep
) Modeset
*m
= NULL
;
141 m
= new0(Modeset
, 1);
145 r
= sd_pid_get_session(getpid(), &m
->session
);
147 return log_error_errno(r
, "Cannot retrieve logind session: %m");
149 r
= sd_session_get_seat(m
->session
, &m
->seat
);
151 return log_error_errno(r
, "Cannot retrieve seat of logind session: %m");
153 m
->my_tty
= is_my_tty(m
->session
);
154 m
->managed
= m
->my_tty
&& geteuid() > 0;
156 m
->r
= rand() % 0xff;
157 m
->g
= rand() % 0xff;
158 m
->b
= rand() % 0xff;
159 m
->r_up
= m
->g_up
= m
->b_up
= true;
161 r
= sd_event_default(&m
->event
);
165 r
= sd_bus_open_system(&m
->bus
);
169 r
= sd_bus_attach_event(m
->bus
, m
->event
, SD_EVENT_PRIORITY_NORMAL
);
173 r
= sigprocmask_many(SIG_BLOCK
, NULL
, SIGTERM
, SIGINT
, -1);
177 r
= sd_event_add_signal(m
->event
, NULL
, SIGTERM
, NULL
, NULL
);
181 r
= sd_event_add_signal(m
->event
, NULL
, SIGINT
, NULL
, NULL
);
185 r
= sd_event_add_exit(m
->event
, &m
->exit_src
, modeset_exit_fn
, m
);
189 /* schedule before sd-bus close */
190 r
= sd_event_source_set_priority(m
->exit_src
, -10);
194 r
= sysview_context_new(&m
->sysview
,
195 SYSVIEW_CONTEXT_SCAN_LOGIND
|
196 SYSVIEW_CONTEXT_SCAN_DRM
,
203 r
= grdev_context_new(&m
->grdev
, m
->event
, m
->bus
);
212 static uint8_t next_color(bool *up
, uint8_t cur
, unsigned int mod
) {
215 /* generate smoothly morphing colors */
217 next
= cur
+ (*up
? 1 : -1) * (rand() % mod
);
218 if ((*up
&& next
< cur
) || (!*up
&& next
> cur
)) {
226 static void modeset_draw(Modeset
*m
, const grdev_display_target
*t
) {
230 assert(t
->back
->format
== DRM_FORMAT_XRGB8888
|| t
->back
->format
== DRM_FORMAT_ARGB8888
);
234 l
= t
->back
->maps
[0];
235 for (j
= 0; j
< t
->height
; ++j
) {
236 for (k
= 0; k
< t
->width
; ++k
) {
238 b
[k
] = (0xff << 24) | (m
->r
<< 16) | (m
->g
<< 8) | m
->b
;
241 l
+= t
->back
->strides
[0];
245 static void modeset_render(Modeset
*m
, grdev_display
*d
) {
246 const grdev_display_target
*t
;
248 m
->r
= next_color(&m
->r_up
, m
->r
, 4);
249 m
->g
= next_color(&m
->g_up
, m
->g
, 3);
250 m
->b
= next_color(&m
->b_up
, m
->b
, 2);
252 GRDEV_DISPLAY_FOREACH_TARGET(d
, t
) {
254 grdev_display_flip_target(d
, t
);
257 grdev_session_commit(m
->grdev_session
);
260 static void modeset_grdev_fn(grdev_session
*session
, void *userdata
, grdev_event
*ev
) {
261 Modeset
*m
= userdata
;
264 case GRDEV_EVENT_DISPLAY_ADD
:
265 grdev_display_enable(ev
->display_add
.display
);
267 case GRDEV_EVENT_DISPLAY_REMOVE
:
269 case GRDEV_EVENT_DISPLAY_CHANGE
:
271 case GRDEV_EVENT_DISPLAY_FRAME
:
272 modeset_render(m
, ev
->display_frame
.display
);
277 static int modeset_sysview_fn(sysview_context
*c
, void *userdata
, sysview_event
*ev
) {
278 unsigned int flags
, type
;
279 Modeset
*m
= userdata
;
285 case SYSVIEW_EVENT_SESSION_FILTER
:
286 if (streq_ptr(m
->session
, ev
->session_filter
.id
))
290 case SYSVIEW_EVENT_SESSION_ADD
:
291 assert(!m
->grdev_session
);
293 name
= sysview_session_get_name(ev
->session_add
.session
);
297 flags
|= GRDEV_SESSION_MANAGED
;
299 r
= grdev_session_new(&m
->grdev_session
,
306 return log_error_errno(r
, "Cannot create grdev session: %m");
309 r
= sysview_session_take_control(ev
->session_add
.session
);
311 return log_error_errno(r
, "Cannot request session control: %m");
314 grdev_session_enable(m
->grdev_session
);
317 case SYSVIEW_EVENT_SESSION_REMOVE
:
318 if (!m
->grdev_session
)
321 grdev_session_restore(m
->grdev_session
);
322 grdev_session_disable(m
->grdev_session
);
323 m
->grdev_session
= grdev_session_free(m
->grdev_session
);
324 if (sd_event_get_exit_code(m
->event
, &r
) == -ENODATA
)
325 sd_event_exit(m
->event
, 0);
327 case SYSVIEW_EVENT_SESSION_ATTACH
:
328 d
= ev
->session_attach
.device
;
329 type
= sysview_device_get_type(d
);
330 if (type
== SYSVIEW_DEVICE_DRM
)
331 grdev_session_add_drm(m
->grdev_session
, sysview_device_get_ud(d
));
334 case SYSVIEW_EVENT_SESSION_DETACH
:
335 d
= ev
->session_detach
.device
;
336 type
= sysview_device_get_type(d
);
337 if (type
== SYSVIEW_DEVICE_DRM
)
338 grdev_session_remove_drm(m
->grdev_session
, sysview_device_get_ud(d
));
341 case SYSVIEW_EVENT_SESSION_REFRESH
:
342 d
= ev
->session_refresh
.device
;
343 type
= sysview_device_get_type(d
);
344 if (type
== SYSVIEW_DEVICE_DRM
)
345 grdev_session_hotplug_drm(m
->grdev_session
, ev
->session_refresh
.ud
);
348 case SYSVIEW_EVENT_SESSION_CONTROL
:
349 r
= ev
->session_control
.error
;
351 return log_error_errno(r
, "Cannot acquire session control: %m");
353 r
= ioctl(1, KDSKBMODE
, K_UNICODE
);
355 return log_error_errno(errno
, "Cannot set K_UNICODE on stdout: %m");
363 static int modeset_run(Modeset
*m
) {
364 struct termios in_attr
, saved_attr
;
370 log_warning("You need to run this program on a free VT");
374 if (!m
->managed
&& geteuid() > 0)
375 log_warning("You run in unmanaged mode without being root. This is likely to fail..");
377 printf("modeset - Show test pattern on selected graphics devices\n"
378 " Running on seat '%s' in user-session '%s'\n"
379 " Exit by pressing ^C\n\n",
380 m
->seat
? : "seat0", m
->session
? : "<none>");
382 r
= sysview_context_start(m
->sysview
, modeset_sysview_fn
, m
);
386 r
= tcgetattr(0, &in_attr
);
392 saved_attr
= in_attr
;
393 in_attr
.c_lflag
&= ~ECHO
;
395 r
= tcsetattr(0, TCSANOW
, &in_attr
);
401 r
= sd_event_loop(m
->event
);
402 tcsetattr(0, TCSANOW
, &saved_attr
);
403 printf("exiting..\n");
406 sysview_context_stop(m
->sysview
);
410 static int help(void) {
411 printf("%s [OPTIONS...]\n\n"
412 "Show test pattern on all selected graphics devices.\n\n"
413 " -h --help Show this help\n"
414 " --version Show package version\n"
415 , program_invocation_short_name
);
420 static int parse_argv(int argc
, char *argv
[]) {
424 static const struct option options
[] = {
425 { "help", no_argument
, NULL
, 'h' },
426 { "version", no_argument
, NULL
, ARG_VERSION
},
434 while ((c
= getopt_long(argc
, argv
, "h", options
, NULL
)) >= 0)
441 puts(PACKAGE_STRING
);
442 puts(SYSTEMD_FEATURES
);
449 assert_not_reached("Unhandled option");
453 log_error("Too many arguments");
460 int main(int argc
, char *argv
[]) {
461 _cleanup_(modeset_freep
) Modeset
*m
= NULL
;
464 log_set_target(LOG_TARGET_AUTO
);
465 log_parse_environment();
470 r
= parse_argv(argc
, argv
);
481 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;