From 6ef5a5cee3c5bc9b15a53b2ba2a06d369ba41704 Mon Sep 17 00:00:00 2001 From: Jonas 'Sortie' Termansen Date: Sat, 19 Nov 2016 16:44:35 +0100 Subject: [PATCH] Detect whether the terminal has a display and a keyboard layout. A new ioctl TIOCGDISPLAYS allow detecting which displays the terminal has associated. The ability to set a keyboard layout can be detected with tcgetblob kblayout. Improve the user-space multi-monitor support while here. The kernel now sets TERM rather than init(8). This is a compatible ABI change riding on the previous commit's bump. --- dispd/client/framebuffer.h | 3 ++- dispd/client/session.c | 25 ++++++++++++++++------- dispd/client/window.c | 7 ++++--- init/init.c | 36 ++++++++++++++++++++++++++------- kernel/include/sortix/display.h | 12 +++++++++++ kernel/include/sortix/ioctl.h | 1 + kernel/kernel.cpp | 4 ++-- kernel/logterminal.cpp | 22 ++++++++++++++++++++ login/graphical.c | 36 ++++++++++++++++++++++++--------- sysinstall/sysinstall.c | 36 +++++++++++++++++++++++---------- sysinstall/sysupgrade.c | 20 ++++++++++++++---- utils/chvideomode.c | 23 ++++++++++++++++++++- 12 files changed, 179 insertions(+), 46 deletions(-) diff --git a/dispd/client/framebuffer.h b/dispd/client/framebuffer.h index 90efd1d8..4bdf6ed8 100644 --- a/dispd/client/framebuffer.h +++ b/dispd/client/framebuffer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012 Jonas 'Sortie' Termansen. + * Copyright (c) 2012, 2016 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -29,6 +29,7 @@ struct dispd_framebuffer int bpp; int width; int height; + uint64_t fb_location; }; #endif diff --git a/dispd/client/session.c b/dispd/client/session.c index 3ba09470..37b3d33d 100644 --- a/dispd/client/session.c +++ b/dispd/client/session.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012 Jonas 'Sortie' Termansen. + * Copyright (c) 2012, 2013, 2014, 2016 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -17,9 +17,10 @@ * Handles session management. */ -#include +#include #include #include +#include #include #include @@ -34,9 +35,6 @@ #include "session.h" -static const uint64_t ONE_AND_ONLY_DEVICE = 0; -static const uint64_t ONE_AND_ONLY_CONNECTOR = 0; - struct dispd_session* global_session = NULL; bool dispd__session_initialize(int* argc, char*** argv) @@ -48,8 +46,21 @@ bool dispd__session_initialize(int* argc, char*** argv) if ( !global_session ) return false; memset(global_session, 0, sizeof(*global_session)); - global_session->device = ONE_AND_ONLY_DEVICE; - global_session->connector = ONE_AND_ONLY_CONNECTOR; + int tty_fd = open("/dev/tty", O_RDWR); + if ( tty_fd < 0 ) + return free(global_session), false; + struct tiocgdisplay display; + struct tiocgdisplays gdisplays; + memset(&gdisplays, 0, sizeof(gdisplays)); + gdisplays.count = 1; + gdisplays.displays = &display; + bool fail = ioctl(tty_fd, TIOCGDISPLAYS, &gdisplays) < 0 || + gdisplays.count == 0; + close(tty_fd); + if ( fail ) + return free(global_session), false; + global_session->device = display.device; + global_session->connector = display.connector; return true; } diff --git a/dispd/client/window.c b/dispd/client/window.c index 8b8c5d0b..2a7dfb94 100644 --- a/dispd/client/window.c +++ b/dispd/client/window.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012 Jonas 'Sortie' Termansen. + * Copyright (c) 2012, 2013, 2014, 2016 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -17,8 +17,8 @@ * Handles windows. */ -#include #include +#include #include #include @@ -102,6 +102,7 @@ struct dispd_framebuffer* dispd_begin_render(struct dispd_window* window) fb->pitch = fb->width * fb->bpp / 8; fb->datasize = fb->pitch * fb->height; fb->data = (uint8_t*) request_buffer(window, fb->datasize); + fb->fb_location = msg.mode.fb_location; if ( !fb->data ) { free(fb); return NULL; } return fb; } @@ -113,7 +114,7 @@ bool dispd_finish_render(struct dispd_framebuffer* fb) struct dispmsg_write_memory msg; msg.msgid = DISPMSG_WRITE_MEMORY; msg.device = window->session->device; - msg.offset = 0; + msg.offset = fb->fb_location; msg.size = fb->datasize; msg.src = fb->data; if ( dispmsg_issue(&msg, sizeof(msg)) == 0 ) diff --git a/init/init.c b/init/init.c index a62831a7..5de5ba8b 100644 --- a/init/init.c +++ b/init/init.c @@ -18,6 +18,7 @@ */ #include +#include #include #include #include @@ -429,6 +430,14 @@ static void set_hostname(void) static void set_kblayout(void) { + int tty_fd = open("/dev/tty", O_RDWR); + if ( !tty_fd ) + return warning("unable to set keyboard layout: /dev/tty: %m"); + bool unsupported = tcgetblob(tty_fd, "kblayout", NULL, 0) < 0 && + (errno == ENOTTY || errno == ENOENT); + close(tty_fd); + if ( unsupported ) + return; FILE* fp = fopen("/etc/kblayout", "r"); if ( !fp && errno == ENOENT ) return; @@ -440,7 +449,11 @@ static void set_kblayout(void) return warning("unable to set keyboard layout: /etc/kblayout: %m"); pid_t child_pid = fork(); if ( child_pid < 0 ) - return warning("unable to set keyboard layout: fork: %m"); + { + free(kblayout); + warning("unable to set keyboard layout: fork: %m"); + return; + } if ( !child_pid ) { execlp("chkblayout", "chkblayout", "--", kblayout, (const char*) NULL); @@ -454,6 +467,19 @@ static void set_kblayout(void) static void set_videomode(void) { + int tty_fd = open("/dev/tty", O_RDWR); + if ( !tty_fd ) + return warning("unable to set video mode: /dev/tty: %m"); + struct tiocgdisplay display; + struct tiocgdisplays gdisplays; + memset(&gdisplays, 0, sizeof(gdisplays)); + gdisplays.count = 1; + gdisplays.displays = &display; + bool unsupported = ioctl(tty_fd, TIOCGDISPLAYS, &gdisplays) < 0 || + gdisplays.count == 0; + close(tty_fd); + if ( unsupported ) + return; FILE* fp = fopen("/etc/videomode", "r"); if ( !fp && errno == ENOENT ) return; @@ -476,8 +502,8 @@ static void set_videomode(void) struct dispmsg_get_crtc_mode get_mode; memset(&get_mode, 0, sizeof(get_mode)); get_mode.msgid = DISPMSG_GET_CRTC_MODE; - get_mode.device = 0; - get_mode.connector = 0; + get_mode.device = display.device; + get_mode.connector = display.connector; // Don't set the resolution if it's already correct. if ( dispmsg_issue(&get_mode, sizeof(get_mode)) == 0 ) { @@ -530,10 +556,6 @@ static void init_early(void) // Set up the PATH variable. if ( setenv("PATH", "/bin:/sbin", 1) < 0 ) fatal("setenv: %m"); - - // Set the terminal type. - if ( setenv("TERM", "sortix", 1) < 0 ) - fatal("setenv: %m"); } static bool is_chain_init_mountpoint(const struct mountpoint* mountpoint) diff --git a/kernel/include/sortix/display.h b/kernel/include/sortix/display.h index 1ee80e37..67a856d1 100644 --- a/kernel/include/sortix/display.h +++ b/kernel/include/sortix/display.h @@ -161,6 +161,18 @@ struct dispmsg_read_memory uint8_t* dst; // in, *out }; +struct tiocgdisplay +{ + uint64_t device; + uint64_t connector; +}; + +struct tiocgdisplays +{ + size_t count; // in, out + struct tiocgdisplay* displays; // in, *out +}; + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/kernel/include/sortix/ioctl.h b/kernel/include/sortix/ioctl.h index 0a7243cc..6d0ee889 100644 --- a/kernel/include/sortix/ioctl.h +++ b/kernel/include/sortix/ioctl.h @@ -38,5 +38,6 @@ #define TIOCGPTLCK __IOCTL(5, __IOCTL_TYPE_PTR) #define TIOCGNAME __IOCTL(6, __IOCTL_TYPE_PTR) #define TIOCGPTN __IOCTL(7, __IOCTL_TYPE_PTR) +#define TIOCGDISPLAYS __IOCTL(8, __IOCTL_TYPE_PTR) #endif diff --git a/kernel/kernel.cpp b/kernel/kernel.cpp index 6cb0d7c3..bc188560 100644 --- a/kernel/kernel.cpp +++ b/kernel/kernel.cpp @@ -719,8 +719,8 @@ static void InitThread(void* /*user*/) Log::PrintF("\r\e[m\e[J"); - int envc = 0; - const char* envp[] = { NULL }; + int envc = 1; + const char* envp[] = { "TERM=sortix", NULL }; struct thread_registers regs; assert((((uintptr_t) ®s) & (alignof(regs)-1)) == 0); diff --git a/kernel/logterminal.cpp b/kernel/logterminal.cpp index 227e8e35..089fd857 100644 --- a/kernel/logterminal.cpp +++ b/kernel/logterminal.cpp @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -332,6 +333,27 @@ int LogTerminal::ioctl(ioctx_t* ctx, int cmd, uintptr_t arg) return -1; return 0; } + else if ( cmd == TIOCGDISPLAYS ) + { + struct tiocgdisplays* input = (struct tiocgdisplays*) arg; + struct tiocgdisplays gdisplays; + if ( !ctx->copy_from_src(&gdisplays, input, sizeof(gdisplays)) ) + return -1; + if ( 0 < gdisplays.count ) + { + struct tiocgdisplay display; + memset(&display, 0, sizeof(display)); + display.device = 0; + display.connector = 0; + if ( !ctx->copy_to_dest(gdisplays.displays, &display, + sizeof(display)) ) + return -1; + } + gdisplays.count = 1; + if ( !ctx->copy_to_dest(input, &gdisplays, sizeof(gdisplays)) ) + return -1; + return 0; + } lock.Reset(); return TTY::ioctl(ctx, cmd, arg); } diff --git a/login/graphical.c b/login/graphical.c index 069032c0..f7d6f23e 100644 --- a/login/graphical.c +++ b/login/graphical.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -106,17 +107,21 @@ struct glogin bool pointer_working; struct termios old_tio; bool has_old_tio; + uint64_t device; + uint64_t connector; }; static struct glogin state; -static bool get_graphical_mode(struct dispmsg_crtc_mode* mode) +static bool get_graphical_mode(uint64_t device, + uint64_t connector, + struct dispmsg_crtc_mode* mode) { struct dispmsg_get_crtc_mode msg; memset(&msg, 0, sizeof(msg)); msg.msgid = DISPMSG_GET_CRTC_MODE; - msg.device = 0; // TODO: Multi-screen support! - msg.connector = 0; // TODO: Multi-screen support! + msg.device = device; + msg.connector = connector; if ( dispmsg_issue(&msg, sizeof(msg)) != 0 ) { warn("dispmsg_issue: DISPMSG_GET_CRTC_MODE"); @@ -496,8 +501,8 @@ static bool screen_capture(struct glogin* state, struct framebuffer* fb) struct dispmsg_write_memory msg; memset(&msg, 0, sizeof(msg)); msg.msgid = DISPMSG_READ_MEMORY; - msg.device = 0; // TODO: Multi-screen support! - msg.offset = 0; // TODO: mode.fb_location! + msg.device = state->device; + msg.offset = state->mode.fb_location; msg.size = fb->xres * fb->yres * sizeof(fb->buffer[0]); msg.src = (uint8_t*) fb->buffer; if ( dispmsg_issue(&msg, sizeof(msg)) != 0 ) @@ -510,7 +515,7 @@ static bool screen_capture(struct glogin* state, struct framebuffer* fb) static bool begin_render(struct glogin* state, struct framebuffer* fb) { - if ( !get_graphical_mode(&state->mode) ) + if ( !get_graphical_mode(state->device, state->connector, &state->mode) ) return false; fb->xres = state->mode.view_xres; fb->yres = state->mode.view_yres; @@ -530,8 +535,8 @@ static bool finish_render(struct glogin* state, struct framebuffer* fb) struct dispmsg_write_memory msg; memset(&msg, 0, sizeof(msg)); msg.msgid = DISPMSG_WRITE_MEMORY; - msg.device = 0; // TODO: Multi-screen support! - msg.offset = 0; // TODO: mode.fb_location! + msg.device = state->device; + msg.offset = state->mode.fb_location; msg.size = sizeof(uint32_t) * fb->xres * fb->yres; msg.src = (uint8_t*) fb->buffer; if ( dispmsg_issue(&msg, sizeof(msg)) != 0 ) @@ -730,8 +735,19 @@ bool glogin_init(struct glogin* state) { memset(state, 0, sizeof(*state)); state->fd_mouse = -1; - - if ( !get_graphical_mode(&state->mode) ) + struct tiocgdisplay display; + struct tiocgdisplays gdisplays; + memset(&gdisplays, 0, sizeof(gdisplays)); + gdisplays.count = 1; + gdisplays.displays = &display; + if ( ioctl(1, TIOCGDISPLAYS, &gdisplays) < 0 || gdisplays.count == 0 ) + { + glogin_destroy(state); + return false; + } + state->device = display.device; + state->connector = display.connector; + if ( !get_graphical_mode(state->device, state->connector, &state->mode) ) { warn("dispmsg_issue"); glogin_destroy(state); diff --git a/sysinstall/sysinstall.c b/sysinstall/sysinstall.c index f7b0d68f..9c0c8b02 100644 --- a/sysinstall/sysinstall.c +++ b/sysinstall/sysinstall.c @@ -18,6 +18,7 @@ */ #include +#include #include #include #include @@ -37,6 +38,7 @@ #include #include #include +#include #include // Sortix libc doesn't have its own proper at this time. @@ -472,7 +474,8 @@ int main(void) install_configurationf("upgrade.conf", "a", "src = yes\n"); - while ( true ) + bool kblayout_setable = 0 <= tcgetblob(0, "kblayout", NULL, 0); + while ( kblayout_setable ) { // TODO: Detect the name of the current keyboard layout. prompt(input, sizeof(input), @@ -510,25 +513,36 @@ int main(void) if ( execute(argv, "f") == 0 ) break; } - if ( !input[0] || !strcmp(input, "default") ) - text("/etc/kblayout will not be created (default).\n"); - else + if ( kblayout_setable ) { - textf("/etc/kblayout will be set to \"%s\".\n", input); - mode_t old_umask = getumask(); - umask(022); - install_configurationf("kblayout", "w", "%s\n", input); - umask(old_umask); + if ( !input[0] || !strcmp(input, "default") ) + text("/etc/kblayout will not be created (default).\n"); + else + { + textf("/etc/kblayout will be set to \"%s\".\n", input); + mode_t old_umask = getumask(); + umask(022); + install_configurationf("kblayout", "w", "%s\n", input); + umask(old_umask); + } + text("\n"); } - text("\n"); + struct tiocgdisplay display; + struct tiocgdisplays gdisplays; + memset(&gdisplays, 0, sizeof(gdisplays)); + gdisplays.count = 1; + gdisplays.displays = &display; struct dispmsg_get_driver_name dgdn = { 0 }; dgdn.msgid = DISPMSG_GET_DRIVER_NAME; dgdn.device = 0; dgdn.driver_index = 0; dgdn.name.byte_size = 0; dgdn.name.str = NULL; - if ( dispmsg_issue(&dgdn, sizeof(dgdn)) == 0 || errno != ENODEV ) + if ( ioctl(1, TIOCGDISPLAYS, &gdisplays) == 0 && + 1 < gdisplays.count && + (dgdn.device = display.device, true) && + (dispmsg_issue(&dgdn, sizeof(dgdn)) == 0 || errno != ENODEV) ) { while ( true ) { diff --git a/sysinstall/sysupgrade.c b/sysinstall/sysupgrade.c index 252df712..e1f73382 100644 --- a/sysinstall/sysupgrade.c +++ b/sysinstall/sysupgrade.c @@ -18,6 +18,7 @@ */ #include +#include #include #include #include @@ -32,6 +33,7 @@ #include #include #include +#include #include #include @@ -379,7 +381,8 @@ int main(void) prompt(input, sizeof(input), "Ready?", ready); text("\n"); - while ( true ) + bool kblayout_setable = 0 <= tcgetblob(0, "kblayout", NULL, 0); + while ( kblayout_setable ) { // TODO: Detect the name of the current keyboard layout. prompt(input, sizeof(input), @@ -417,15 +420,24 @@ int main(void) if ( execute(argv, "f") == 0 ) break; } - text("\n"); + if ( kblayout_setable ) + text("\n"); + struct tiocgdisplay display; + struct tiocgdisplays gdisplays; + memset(&gdisplays, 0, sizeof(gdisplays)); + gdisplays.count = 1; + gdisplays.displays = &display; struct dispmsg_get_driver_name dgdn = { 0 }; dgdn.msgid = DISPMSG_GET_DRIVER_NAME; dgdn.device = 0; dgdn.driver_index = 0; dgdn.name.byte_size = 0; dgdn.name.str = NULL; - if ( dispmsg_issue(&dgdn, sizeof(dgdn)) == 0 || errno != ENODEV ) + if ( ioctl(1, TIOCGDISPLAYS, &gdisplays) == 0 && + 1 < gdisplays.count && + (dgdn.device = display.device, true) && + (dispmsg_issue(&dgdn, sizeof(dgdn)) == 0 || errno != ENODEV) ) { while ( true ) { @@ -439,8 +451,8 @@ int main(void) continue; break; } + text("\n"); } - text("\n"); struct release new_release; if ( !os_release_load(&new_release, "/etc/sortix-release", diff --git a/utils/chvideomode.c b/utils/chvideomode.c index 70253c74..616c559d 100644 --- a/utils/chvideomode.c +++ b/utils/chvideomode.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, 2014 Jonas 'Sortie' Termansen. + * Copyright (c) 2012, 2013, 2014, 2015, 2016 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -18,12 +18,14 @@ */ #include +#include #include #include #include #include #include +#include #include #include #include @@ -34,6 +36,9 @@ #include #include +static uint64_t device; +static uint64_t connector; + bool SetCurrentMode(struct dispmsg_crtc_mode mode) { struct dispmsg_set_crtc_mode msg; @@ -324,6 +329,22 @@ int main(int argc, char* argv[]) compact_arguments(&argc, &argv); + int tty_fd = open("/dev/tty", O_RDWR); + if ( tty_fd < 0 ) + error(1, errno, "/dev/tty"); + struct tiocgdisplay display; + struct tiocgdisplays gdisplays; + memset(&gdisplays, 0, sizeof(gdisplays)); + gdisplays.count = 1; + gdisplays.displays = &display; + if ( ioctl(1, TIOCGDISPLAYS, &gdisplays) < 0 || gdisplays.count == 0 ) + { + fprintf(stderr, "No video devices are associated with this terminal.\n"); + exit(13); + } + device = display.device; + connector = display.connector; + size_t num_modes = 0; struct dispmsg_crtc_mode* modes = GetAvailableModes(&num_modes); if ( !modes )