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);
269 if (!vid->seat->app->exiting)
270 kmscon_seat_refresh_display(vid->seat->seat,
276 static bool app_seat_gpu_is_ignored(struct app_seat *seat,
284 case UTERM_MONITOR_FBDEV:
285 if (seat->conf->drm) {
287 log_info("ignoring video device %s on seat %s as it is a DRM-fbdev device",
293 case UTERM_MONITOR_DRM:
294 if (!seat->conf->drm) {
295 log_info("ignoring video device %s on seat %s as it is a DRM device",
301 log_info("ignoring unknown video device %s on seat %s",
306 if (seat->conf->gpus == KMSCON_GPU_PRIMARY && !primary) {
307 log_info("ignoring video device %s on seat %s as it is no primary GPU",
312 if (seat->conf->gpus == KMSCON_GPU_AUX && !primary && !aux) {
313 log_info("ignoring video device %s on seat %s as it is neither a primary nor auxiliary GPU",
321 static int app_seat_add_video(struct app_seat *seat,
325 struct uterm_monitor_dev *udev)
328 const struct uterm_video_module *mode;
329 struct app_video *vid;
331 if (seat->app->exiting)
334 if (app_seat_gpu_is_ignored(seat, type,
335 flags & UTERM_MONITOR_DRM_BACKED,
336 flags & UTERM_MONITOR_PRIMARY,
337 flags & UTERM_MONITOR_AUX,
341 log_debug("new video device %s on seat %s", node, seat->name);
343 vid = malloc(sizeof(*vid));
345 log_error("cannot allocate memory for video device %s on seat %s",
349 memset(vid, 0, sizeof(*vid));
353 vid->node = strdup(node);
355 log_error("cannot copy video device name %s on seat %s",
361 if (type == UTERM_MONITOR_DRM) {
362 if (seat->conf->hwaccel)
363 mode = UTERM_VIDEO_DRM3D;
365 mode = UTERM_VIDEO_DRM2D;
367 mode = UTERM_VIDEO_FBDEV;
370 ret = uterm_video_new(&vid->video, seat->app->eloop, node, mode);
372 if (mode == UTERM_VIDEO_DRM3D) {
373 log_info("cannot create drm3d device %s on seat %s (%d); trying drm2d mode",
374 vid->node, seat->name, ret);
375 ret = uterm_video_new(&vid->video, seat->app->eloop,
376 node, UTERM_VIDEO_DRM2D);
384 ret = uterm_video_register_cb(vid->video, app_seat_video_event, vid);
386 log_error("cannot register video callback for device %s on seat %s: %d",
387 vid->node, seat->name, ret);
392 uterm_video_wake_up(vid->video);
394 uterm_monitor_set_dev_data(vid->udev, vid);
395 shl_dlist_link(&seat->videos, &vid->list);
399 uterm_video_unref(vid->video);
407 static void app_seat_remove_video(struct app_seat *seat, struct app_video *vid)
409 struct uterm_display *disp;
411 log_debug("free video device %s on seat %s", vid->node, seat->name);
413 shl_dlist_unlink(&vid->list);
414 uterm_monitor_set_dev_data(vid->udev, NULL);
415 uterm_video_unregister_cb(vid->video, app_seat_video_event, vid);
417 disp = uterm_video_get_displays(vid->video);
419 kmscon_seat_remove_display(seat->seat, disp);
420 disp = uterm_display_next(disp);
423 uterm_video_unref(vid->video);
428 static void app_monitor_event(struct uterm_monitor *mon,
429 struct uterm_monitor_event *ev,
432 struct kmscon_app *app = data;
433 struct app_seat *seat;
434 struct app_video *vid;
438 case UTERM_MONITOR_NEW_SEAT:
439 ret = app_seat_new(app, ev->seat_name, ev->seat);
443 case UTERM_MONITOR_FREE_SEAT:
445 app_seat_free(ev->seat_data);
447 case UTERM_MONITOR_NEW_DEV:
448 seat = ev->seat_data;
452 switch (ev->dev_type) {
453 case UTERM_MONITOR_DRM:
454 case UTERM_MONITOR_FBDEV:
455 ret = app_seat_add_video(seat, ev->dev_type,
457 ev->dev_node, ev->dev);
461 case UTERM_MONITOR_INPUT:
462 log_debug("new input device %s on seat %s",
463 ev->dev_node, seat->name);
464 kmscon_seat_add_input(seat->seat, ev->dev_node);
468 case UTERM_MONITOR_FREE_DEV:
469 seat = ev->seat_data;
473 switch (ev->dev_type) {
474 case UTERM_MONITOR_DRM:
475 case UTERM_MONITOR_FBDEV:
477 app_seat_remove_video(seat, ev->dev_data);
479 case UTERM_MONITOR_INPUT:
480 log_debug("free input device %s on seat %s",
481 ev->dev_node, seat->name);
482 kmscon_seat_remove_input(seat->seat, ev->dev_node);
486 case UTERM_MONITOR_HOTPLUG_DEV:
487 seat = ev->seat_data;
491 switch (ev->dev_type) {
492 case UTERM_MONITOR_DRM:
493 case UTERM_MONITOR_FBDEV:
498 log_debug("video hotplug event on device %s on seat %s",
499 vid->node, seat->name);
500 uterm_video_poll(vid->video);
507 static void app_sig_generic(struct ev_eloop *eloop,
508 struct signalfd_siginfo *info,
511 struct kmscon_app *app = data;
513 log_info("terminating due to caught signal %d", info->ssi_signo);
514 ev_eloop_exit(app->eloop);
517 static void app_sig_ignore(struct ev_eloop *eloop,
518 struct signalfd_siginfo *info,
523 static void destroy_app(struct kmscon_app *app)
525 uterm_monitor_unref(app->mon);
526 uterm_vt_master_unref(app->vtm);
527 ev_eloop_unregister_signal_cb(app->eloop, SIGPIPE, app_sig_ignore,
529 ev_eloop_unregister_signal_cb(app->eloop, SIGINT, app_sig_generic,
531 ev_eloop_unregister_signal_cb(app->eloop, SIGTERM, app_sig_generic,
533 ev_eloop_unref(app->eloop);
536 static int setup_app(struct kmscon_app *app)
540 shl_dlist_init(&app->seats);
542 ret = ev_eloop_new(&app->eloop, log_llog, NULL);
544 log_error("cannot create eloop object: %d", ret);
548 ret = ev_eloop_register_signal_cb(app->eloop, SIGTERM,
549 app_sig_generic, app);
551 log_error("cannot register SIGTERM signal handler: %d", ret);
555 ret = ev_eloop_register_signal_cb(app->eloop, SIGINT,
556 app_sig_generic, app);
558 log_error("cannot register SIGINT signal handler: %d", ret);
562 ret = ev_eloop_register_signal_cb(app->eloop, SIGPIPE,
563 app_sig_ignore, app);
565 log_error("cannot register SIGPIPE signal handler: %d", ret);
569 ret = uterm_vt_master_new(&app->vtm, app->eloop);
571 log_error("cannot create VT master: %d", ret);
575 ret = uterm_monitor_new(&app->mon, app->eloop, app_monitor_event, app);
577 log_error("cannot create device monitor: %d", ret);
581 log_debug("scanning for devices...");
582 uterm_monitor_scan(app->mon);
591 int main(int argc, char **argv)
594 struct conf_ctx *conf_ctx;
595 struct kmscon_conf_t *conf;
596 struct kmscon_app app;
598 ret = kmscon_conf_new(&conf_ctx);
600 log_error("cannot create configuration: %d", ret);
603 conf = conf_ctx_get_mem(conf_ctx);
605 ret = kmscon_conf_load_main(conf_ctx, argc, argv);
607 log_error("cannot load configuration: %d", ret);
612 kmscon_conf_free(conf_ctx);
616 kmscon_load_modules();
617 kmscon_font_register(&kmscon_font_8x16_ops);
618 kmscon_text_register(&kmscon_text_bblit_ops);
620 memset(&app, 0, sizeof(app));
621 app.conf_ctx = conf_ctx;
624 ret = setup_app(&app);
628 if (!app.conf->listen && !app.running_seats) {
629 log_notice("no running seats; exiting");
631 log_debug("%u running seats after startup", app.running_seats);
632 ev_eloop_run(app.eloop, -1);
637 if (app.conf->switchvt) {
638 /* The VT subsystem needs to acknowledge the VT-leave so if it
639 * returns -EINPROGRESS we need to wait for the VT-leave SIGUSR2
640 * signal to arrive. Therefore, we use a separate eloop object
641 * which is used by the VT system only. Therefore, waiting on
642 * this eloop allows us to safely wait 50ms for the SIGUSR2 to
644 * We use a timeout of 100ms to avoid hanging on exit. */
645 log_debug("deactivating VTs during shutdown");
646 ret = uterm_vt_master_deactivate_all(app.vtm);
648 log_debug("waiting for %d VTs to deactivate", ret);
649 app.vt_exit_count = ret;
650 ev_eloop_run(app.eloop, 50);
658 kmscon_text_unregister(kmscon_text_bblit_ops.name);
659 kmscon_font_unregister(kmscon_font_8x16_ops.name);
660 kmscon_unload_modules();
662 kmscon_conf_free(conf_ctx);
665 log_err("cannot initialize kmscon, errno %d: %s",
666 ret, strerror(-ret));