4 * Copyright (c) 2012 David Herrmann <dh.herrmann@googlemail.com>
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files
8 * (the "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sublicense, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
21 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 #include <sys/signalfd.h>
36 #include "kmscon_conf.h"
37 #include "kmscon_module.h"
38 #include "kmscon_seat.h"
39 #include "shl_dlist.h"
43 #include "uterm_input.h"
44 #include "uterm_monitor.h"
45 #include "uterm_video.h"
49 struct shl_dlist list;
50 struct app_seat *seat;
51 struct uterm_monitor_dev *udev;
54 struct uterm_video *video;
58 struct shl_dlist list;
59 struct kmscon_app *app;
60 struct uterm_monitor_seat *useat;
64 struct kmscon_seat *seat;
65 struct conf_ctx *conf_ctx;
66 struct kmscon_conf_t *conf;
67 struct shl_dlist videos;
71 struct conf_ctx *conf_ctx;
72 struct kmscon_conf_t *conf;
75 struct ev_eloop *eloop;
76 unsigned int vt_exit_count;
78 struct uterm_vt_master *vtm;
79 struct uterm_monitor *mon;
80 struct shl_dlist seats;
81 unsigned int running_seats;
84 static int app_seat_event(struct kmscon_seat *s, unsigned int event,
87 struct app_seat *seat = data;
88 struct kmscon_app *app = seat->app;
89 struct shl_dlist *iter;
90 struct app_video *vid;
93 case KMSCON_SEAT_FOREGROUND:
96 shl_dlist_for_each(iter, &seat->videos) {
97 vid = shl_dlist_entry(iter, struct app_video, list);
98 uterm_video_wake_up(vid->video);
101 case KMSCON_SEAT_BACKGROUND:
102 shl_dlist_for_each(iter, &seat->videos) {
103 vid = shl_dlist_entry(iter, struct app_video, list);
104 uterm_video_sleep(vid->video);
109 case KMSCON_SEAT_SLEEP:
110 if (app->vt_exit_count > 0) {
111 log_debug("deactivating VT on exit, %d to go",
112 app->vt_exit_count - 1);
113 if (!--app->vt_exit_count)
114 ev_eloop_exit(app->eloop);
117 case KMSCON_SEAT_WAKE_UP:
121 case KMSCON_SEAT_HUP:
122 kmscon_seat_free(seat->seat);
125 if (!app->conf->listen) {
126 --app->running_seats;
127 if (!app->running_seats) {
128 log_debug("seat HUP on %s in default-mode; exiting...",
130 ev_eloop_exit(app->eloop);
132 log_debug("seat HUP on %s in default-mode; %u more running seats",
133 seat->name, app->running_seats);
136 /* Seat HUP here means that we are running in
137 * listen-mode on a modular-VT like kmscon-fake-VTs. But
138 * this is an invalid setup. In listen-mode we
139 * exclusively run as seat-VT-master without a
140 * controlling VT and we effectively prevent other
141 * setups during startup. Hence, we can safely drop the
142 * seat here and ignore it.
143 * You can destroy and recreate the seat to make kmscon
144 * pick it up again in listen-mode. */
145 log_warning("seat HUP on %s in listen-mode; dropping seat...",
155 static int app_seat_new(struct kmscon_app *app, const char *sname,
156 struct uterm_monitor_seat *useat)
158 struct app_seat *seat;
160 unsigned int i, types;
168 if (kmscon_conf_is_all_seats(app->conf)) {
170 } else if (kmscon_conf_is_current_seat(app->conf)) {
171 cseat = getenv("XDG_SEAT");
174 if (!strcmp(cseat, sname))
177 for (i = 0; app->conf->seats[i]; ++i) {
178 if (!strcmp(app->conf->seats[i], sname)) {
186 log_info("ignoring new seat %s as not specified in seat-list",
191 log_debug("new seat %s", sname);
193 seat = malloc(sizeof(*seat));
195 log_error("cannot allocate memory for seat %s", sname);
198 memset(seat, 0, sizeof(*seat));
201 shl_dlist_init(&seat->videos);
203 seat->name = strdup(sname);
205 log_error("cannot copy seat name on seat %s", sname);
210 types = UTERM_VT_FAKE;
211 if (!app->conf->listen)
212 types |= UTERM_VT_REAL;
214 ret = kmscon_seat_new(&seat->seat, app->conf_ctx, app->eloop, app->vtm,
215 types, sname, app_seat_event, seat);
218 log_debug("ignoring seat %s as it already has a seat manager",
221 log_error("cannot create seat object on seat %s: %d",
225 seat->conf_ctx = kmscon_seat_get_conf(seat->seat);
226 seat->conf = conf_ctx_get_mem(seat->conf_ctx);
228 uterm_monitor_set_seat_data(seat->useat, seat);
229 shl_dlist_link(&app->seats, &seat->list);
230 ++app->running_seats;
232 kmscon_seat_startup(seat->seat);
243 static void app_seat_free(struct app_seat *seat)
245 log_debug("free seat %s", seat->name);
247 shl_dlist_unlink(&seat->list);
248 uterm_monitor_set_seat_data(seat->useat, NULL);
249 kmscon_seat_free(seat->seat);
254 static void app_seat_video_event(struct uterm_video *video,
255 struct uterm_video_hotplug *ev,
258 struct app_video *vid = data;
260 switch (ev->action) {
262 if (!vid->seat->app->exiting)
263 kmscon_seat_add_display(vid->seat->seat, ev->display);
266 kmscon_seat_remove_display(vid->seat->seat, ev->display);
271 static bool app_seat_gpu_is_ignored(struct app_seat *seat,
279 case UTERM_MONITOR_FBDEV:
280 if (seat->conf->drm) {
282 log_info("ignoring video device %s on seat %s as it is a DRM-fbdev device",
288 case UTERM_MONITOR_DRM:
289 if (!seat->conf->drm) {
290 log_info("ignoring video device %s on seat %s as it is a DRM device",
296 log_info("ignoring unknown video device %s on seat %s",
301 if (seat->conf->gpus == KMSCON_GPU_PRIMARY && !primary) {
302 log_info("ignoring video device %s on seat %s as it is no primary GPU",
307 if (seat->conf->gpus == KMSCON_GPU_AUX && !primary && !aux) {
308 log_info("ignoring video device %s on seat %s as it is neither a primary nor auxiliary GPU",
316 static int app_seat_add_video(struct app_seat *seat,
320 struct uterm_monitor_dev *udev)
323 const struct uterm_video_module *mode;
324 struct app_video *vid;
326 if (seat->app->exiting)
329 if (app_seat_gpu_is_ignored(seat, type,
330 flags & UTERM_MONITOR_DRM_BACKED,
331 flags & UTERM_MONITOR_PRIMARY,
332 flags & UTERM_MONITOR_AUX,
336 log_debug("new video device %s on seat %s", node, seat->name);
338 vid = malloc(sizeof(*vid));
340 log_error("cannot allocate memory for video device %s on seat %s",
344 memset(vid, 0, sizeof(*vid));
348 vid->node = strdup(node);
350 log_error("cannot copy video device name %s on seat %s",
356 if (type == UTERM_MONITOR_DRM) {
357 if (seat->conf->hwaccel)
358 mode = UTERM_VIDEO_DRM3D;
360 mode = UTERM_VIDEO_DRM2D;
362 mode = UTERM_VIDEO_FBDEV;
365 ret = uterm_video_new(&vid->video, seat->app->eloop, node, mode);
367 if (mode == UTERM_VIDEO_DRM3D) {
368 log_info("cannot create drm3d device %s on seat %s (%d); trying drm2d mode",
369 vid->node, seat->name, ret);
370 ret = uterm_video_new(&vid->video, seat->app->eloop,
371 node, UTERM_VIDEO_DRM2D);
379 ret = uterm_video_register_cb(vid->video, app_seat_video_event, vid);
381 log_error("cannot register video callback for device %s on seat %s: %d",
382 vid->node, seat->name, ret);
387 uterm_video_wake_up(vid->video);
389 uterm_monitor_set_dev_data(vid->udev, vid);
390 shl_dlist_link(&seat->videos, &vid->list);
394 uterm_video_unref(vid->video);
402 static void app_seat_remove_video(struct app_seat *seat, struct app_video *vid)
404 struct uterm_display *disp;
406 log_debug("free video device %s on seat %s", vid->node, seat->name);
408 shl_dlist_unlink(&vid->list);
409 uterm_monitor_set_dev_data(vid->udev, NULL);
410 uterm_video_unregister_cb(vid->video, app_seat_video_event, vid);
412 disp = uterm_video_get_displays(vid->video);
414 kmscon_seat_remove_display(seat->seat, disp);
415 disp = uterm_display_next(disp);
418 uterm_video_unref(vid->video);
423 static void app_monitor_event(struct uterm_monitor *mon,
424 struct uterm_monitor_event *ev,
427 struct kmscon_app *app = data;
428 struct app_seat *seat;
429 struct app_video *vid;
433 case UTERM_MONITOR_NEW_SEAT:
434 ret = app_seat_new(app, ev->seat_name, ev->seat);
438 case UTERM_MONITOR_FREE_SEAT:
440 app_seat_free(ev->seat_data);
442 case UTERM_MONITOR_NEW_DEV:
443 seat = ev->seat_data;
447 switch (ev->dev_type) {
448 case UTERM_MONITOR_DRM:
449 case UTERM_MONITOR_FBDEV:
450 ret = app_seat_add_video(seat, ev->dev_type,
452 ev->dev_node, ev->dev);
456 case UTERM_MONITOR_INPUT:
457 log_debug("new input device %s on seat %s",
458 ev->dev_node, seat->name);
459 kmscon_seat_add_input(seat->seat, ev->dev_node);
463 case UTERM_MONITOR_FREE_DEV:
464 seat = ev->seat_data;
468 switch (ev->dev_type) {
469 case UTERM_MONITOR_DRM:
470 case UTERM_MONITOR_FBDEV:
472 app_seat_remove_video(seat, ev->dev_data);
474 case UTERM_MONITOR_INPUT:
475 log_debug("free input device %s on seat %s",
476 ev->dev_node, seat->name);
477 kmscon_seat_remove_input(seat->seat, ev->dev_node);
481 case UTERM_MONITOR_HOTPLUG_DEV:
482 seat = ev->seat_data;
486 switch (ev->dev_type) {
487 case UTERM_MONITOR_DRM:
488 case UTERM_MONITOR_FBDEV:
493 log_debug("video hotplug event on device %s on seat %s",
494 vid->node, seat->name);
495 uterm_video_poll(vid->video);
502 static void app_sig_generic(struct ev_eloop *eloop,
503 struct signalfd_siginfo *info,
506 struct kmscon_app *app = data;
508 log_info("terminating due to caught signal %d", info->ssi_signo);
509 ev_eloop_exit(app->eloop);
512 static void app_sig_ignore(struct ev_eloop *eloop,
513 struct signalfd_siginfo *info,
518 static void destroy_app(struct kmscon_app *app)
520 uterm_monitor_unref(app->mon);
521 uterm_vt_master_unref(app->vtm);
522 ev_eloop_unregister_signal_cb(app->eloop, SIGPIPE, app_sig_ignore,
524 ev_eloop_unregister_signal_cb(app->eloop, SIGINT, app_sig_generic,
526 ev_eloop_unregister_signal_cb(app->eloop, SIGTERM, app_sig_generic,
528 ev_eloop_unref(app->eloop);
531 static int setup_app(struct kmscon_app *app)
535 shl_dlist_init(&app->seats);
537 ret = ev_eloop_new(&app->eloop, log_llog, NULL);
539 log_error("cannot create eloop object: %d", ret);
543 ret = ev_eloop_register_signal_cb(app->eloop, SIGTERM,
544 app_sig_generic, app);
546 log_error("cannot register SIGTERM signal handler: %d", ret);
550 ret = ev_eloop_register_signal_cb(app->eloop, SIGINT,
551 app_sig_generic, app);
553 log_error("cannot register SIGINT signal handler: %d", ret);
557 ret = ev_eloop_register_signal_cb(app->eloop, SIGPIPE,
558 app_sig_ignore, app);
560 log_error("cannot register SIGPIPE signal handler: %d", ret);
564 ret = uterm_vt_master_new(&app->vtm, app->eloop);
566 log_error("cannot create VT master: %d", ret);
570 ret = uterm_monitor_new(&app->mon, app->eloop, app_monitor_event, app);
572 log_error("cannot create device monitor: %d", ret);
576 log_debug("scanning for devices...");
577 uterm_monitor_scan(app->mon);
586 int main(int argc, char **argv)
589 struct conf_ctx *conf_ctx;
590 struct kmscon_conf_t *conf;
591 struct kmscon_app app;
593 ret = kmscon_conf_new(&conf_ctx);
595 log_error("cannot create configuration: %d", ret);
598 conf = conf_ctx_get_mem(conf_ctx);
600 ret = kmscon_conf_load_main(conf_ctx, argc, argv);
602 log_error("cannot load configuration: %d", ret);
607 kmscon_conf_free(conf_ctx);
611 kmscon_load_modules();
612 kmscon_font_register(&kmscon_font_8x16_ops);
613 kmscon_text_register(&kmscon_text_bblit_ops);
615 memset(&app, 0, sizeof(app));
616 app.conf_ctx = conf_ctx;
619 ret = setup_app(&app);
623 if (!app.conf->listen && !app.running_seats) {
624 log_notice("no running seats; exiting");
626 log_debug("%u running seats after startup", app.running_seats);
627 ev_eloop_run(app.eloop, -1);
632 if (app.conf->switchvt) {
633 /* The VT subsystem needs to acknowledge the VT-leave so if it
634 * returns -EINPROGRESS we need to wait for the VT-leave SIGUSR2
635 * signal to arrive. Therefore, we use a separate eloop object
636 * which is used by the VT system only. Therefore, waiting on
637 * this eloop allows us to safely wait 50ms for the SIGUSR2 to
639 * We use a timeout of 100ms to avoid hanging on exit. */
640 log_debug("deactivating VTs during shutdown");
641 ret = uterm_vt_master_deactivate_all(app.vtm);
643 log_debug("waiting for %d VTs to deactivate", ret);
644 app.vt_exit_count = ret;
645 ev_eloop_run(app.eloop, 50);
653 kmscon_text_unregister(kmscon_text_bblit_ops.name);
654 kmscon_font_unregister(kmscon_font_8x16_ops.name);
655 kmscon_unload_modules();
657 kmscon_conf_free(conf_ctx);
660 log_err("cannot initialize kmscon, errno %d: %s",
661 ret, strerror(-ret));