diff --git a/utils/chvideomode.cpp b/utils/chvideomode.cpp index 28bd2c1c..6779af7b 100644 --- a/utils/chvideomode.cpp +++ b/utils/chvideomode.cpp @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2012, 2013. + Copyright(C) Jonas 'Sortie' Termansen 2012, 2013, 2014. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free @@ -27,113 +27,22 @@ #include #include #include -#include -#include + +#include +#include +#include #include #include -#include #include -#include -#include +#include +#include #include +#include #if !defined(VERSIONSTR) #define VERSIONSTR "unknown version" #endif -// TODO: Provide this using asprintf or somewhere sane in user-space. -char* Combine(size_t NumParameters, ...) -{ - va_list param_pt; - - va_start(param_pt, NumParameters); - - // First calculate the string length. - size_t ResultLength = 0; - const char* TMP = 0; - - for ( size_t I = 0; I < NumParameters; I++ ) - { - TMP = va_arg(param_pt, const char*); - - if ( TMP != NULL ) { ResultLength += strlen(TMP); } - } - - // Allocate a string with the desired length. - char* Result = new char[ResultLength+1]; - if ( !Result ) { return NULL; } - - Result[0] = 0; - - va_end(param_pt); - va_start(param_pt, NumParameters); - - size_t ResultOffset = 0; - - for ( size_t I = 0; I < NumParameters; I++ ) - { - TMP = va_arg(param_pt, const char*); - - if ( TMP ) - { - size_t TMPLength = strlen(TMP); - strcpy(Result + ResultOffset, TMP); - ResultOffset += TMPLength; - } - } - - return Result; -} - -char* ActualGetDriverName(uint64_t driver_index) -{ - struct dispmsg_get_driver_name msg; - msg.msgid = DISPMSG_GET_DRIVER_NAME; - msg.device = 0; - msg.driver_index = driver_index; - size_t guess = 32; - while ( true ) - { - char* ret = new char[guess]; - if ( !ret ) - return NULL; - msg.name.byte_size = guess; - msg.name.str = ret; - if ( dispmsg_issue(&msg, sizeof(msg)) == 0 ) - return ret; - delete[] ret; - if ( errno == ERANGE && guess < msg.name.byte_size ) - { - guess = msg.name.byte_size; - continue; - } - return NULL; - } -} - -static char* StringOfCrtcMode(struct dispmsg_crtc_mode mode) -{ - char bppstr[sizeof(mode.fb_format) * 3]; - char xresstr[sizeof(mode.view_xres) * 3]; - char yresstr[sizeof(mode.view_yres) * 3]; - char magicstr[sizeof(mode.magic) * 3]; - snprintf(bppstr, sizeof(bppstr), "%ju", (uintmax_t) mode.fb_format); - snprintf(xresstr, sizeof(xresstr), "%ju", (uintmax_t) mode.view_xres); - snprintf(yresstr, sizeof(yresstr), "%ju", (uintmax_t) mode.view_yres); - snprintf(magicstr, sizeof(magicstr), "%ju", (uintmax_t) mode.magic); - char* drivername = ActualGetDriverName(mode.driver_index); - if ( !drivername ) - return NULL; - char* ret = Combine(10, - "driver=", drivername, "," - "bpp=", bppstr, "," - "width=", xresstr, "," - "height=", yresstr, "," - "modeid=", magicstr); - delete[] drivername; - return ret; -} - bool SetCurrentMode(struct dispmsg_crtc_mode mode) { struct dispmsg_set_crtc_mode msg; @@ -144,17 +53,7 @@ bool SetCurrentMode(struct dispmsg_crtc_mode mode) return dispmsg_issue(&msg, sizeof(msg)) == 0; } -struct dispmsg_crtc_mode GetCurrentMode() -{ - struct dispmsg_set_crtc_mode msg; - msg.msgid = DISPMSG_GET_CRTC_MODE; - msg.device = 0; - msg.connector = 0; - dispmsg_issue(&msg, sizeof(msg)); - return msg.mode; -} - -struct dispmsg_crtc_mode* GetAvailableModes(size_t* nummodesptr) +struct dispmsg_crtc_mode* GetAvailableModes(size_t* num_modes_ptr) { struct dispmsg_get_crtc_modes msg; msg.msgid = DISPMSG_GET_CRTC_MODES; @@ -170,7 +69,7 @@ struct dispmsg_crtc_mode* GetAvailableModes(size_t* nummodesptr) msg.modes = ret; if ( dispmsg_issue(&msg, sizeof(msg)) == 0 ) { - *nummodesptr = guess; + *num_modes_ptr = guess; return ret; } delete[] ret; @@ -183,62 +82,13 @@ struct dispmsg_crtc_mode* GetAvailableModes(size_t* nummodesptr) } } -void DrawMenu(size_t selection, struct dispmsg_crtc_mode* modes, size_t nummodes) +struct filter { - printf("\e[m\e[H\e[2K"); // Move to (0,0), clear screen. - printf("Please select one of these video modes or press ESC to abort.\n"); - size_t off = 1; // Above line - struct winsize ws; - if ( tcgetwinsize(1, &ws) != 0 ) - ws.ws_row = 25; - size_t amount = ws.ws_row-off; - size_t from = (selection / amount) * amount; - size_t howmanyavailable = nummodes - from; - size_t howmany = howmanyavailable < amount ? howmanyavailable : amount; - size_t to = from + howmany - 1; - for ( size_t i = from; i <= to; i++ ) - { - size_t screenline = 1 + off + i - from; - const char* color = i == selection ? "\e[31m" : "\e[m"; - char* desc = StringOfCrtcMode(modes[i]); - const char* print_desc = desc ? desc : ""; - printf("\e[%zuH%s\e[2K%zu\t%s", screenline, color, i, print_desc); - delete[] desc; - } - printf("\e[m\e[J"); - fflush(stdout); -} - -int GetKey(int fd) -{ - unsigned int oldtermmode; - gettermmode(fd, &oldtermmode); - // Read the keyboard input from the user. - const unsigned termmode = TERMMODE_KBKEY - | TERMMODE_UNICODE - | TERMMODE_SIGNAL; - if ( settermmode(fd, termmode) ) { error(1, errno, "settermmode"); } - uint32_t codepoint; - ssize_t numbytes; - while ( 0 < (numbytes = read(0, &codepoint, sizeof(codepoint))) ) - { - int kbkey = KBKEY_DECODE(codepoint); - if ( !kbkey ) { continue; } - return kbkey; - } - settermmode(fd, oldtermmode); - return 0; -} - -int GetKey(FILE* fp) { return GetKey(fileno(fp)); } - -struct Filter -{ - bool includeall; - bool includesupported; - bool includeunsupported; - bool includetext; - bool includegraphics; + bool include_all; + bool include_supported; + bool include_unsupported; + bool include_text; + bool include_graphics; size_t minbpp; size_t maxbpp; size_t minxres; @@ -251,18 +101,62 @@ struct Filter size_t maxychars; }; -size_t ParseSizeT(const char* str, size_t def = 0) +bool mode_passes_filter(struct dispmsg_crtc_mode mode, struct filter* filter) +{ + if ( filter->include_all ) + return true; + size_t width = mode.view_xres; + size_t height = mode.view_yres; + size_t bpp = mode.fb_format; + bool supported = (mode.control & DISPMSG_CONTROL_VALID) || + (mode.control & DISPMSG_CONTROL_OTHER_RESOLUTIONS); + bool unsupported = !supported; + bool text = mode.control & DISPMSG_CONTROL_VGA; + bool graphics = !text; + if ( mode.control & DISPMSG_CONTROL_OTHER_RESOLUTIONS ) + return true; + if ( unsupported && !filter->include_unsupported ) + return false; + if ( supported && !filter->include_supported ) + return false; + if ( text && !filter->include_text ) + return false; + if ( graphics && !filter->include_graphics ) + return false; + if ( graphics && (bpp < filter->minbpp || filter->maxbpp < bpp) ) + return false; + if ( graphics && (width < filter->minxres || filter->maxxres < width) ) + return false; + if ( graphics && (height < filter->minyres || filter->maxyres < height) ) + return false; + // TODO: Support filtering text modes according to columns/rows. + return true; +} + +void filter_modes(struct dispmsg_crtc_mode* modes, size_t* num_modes_ptr, struct filter* filter) +{ + size_t in_num = *num_modes_ptr; + size_t out_num = 0; + for ( size_t i = 0; i < in_num; i++ ) + { + if ( mode_passes_filter(modes[i], filter) ) + modes[out_num++] = modes[i]; + } + *num_modes_ptr = out_num; +} + +size_t parse_size_t(const char* str, size_t def = 0) { if ( !str || !*str ) return def; char* endptr; - size_t ret = strtoul(str, &endptr, 10); + size_t ret = (size_t) strtoumax(str, &endptr, 10); if ( *endptr ) return def; return ret; } -bool ParseBool(const char* str, bool def = false) +bool parse_bool(const char* str, bool def = false) { if ( !str || !*str ) return def; @@ -270,49 +164,22 @@ bool ParseBool(const char* str, bool def = false) return !isfalse; } -bool PassesFilter(struct dispmsg_crtc_mode mode, Filter* filt) +static void compact_arguments(int* argc, char*** argv) { - if ( filt->includeall ) { return true; } - size_t width = mode.view_xres; - size_t height = mode.view_yres; - size_t bpp = mode.fb_format; - bool unsupported = (mode.control & 1) == 0; - bool supported = !unsupported; - bool text = false; // TODO: How does tell this? - bool graphics = !text; - if ( unsupported && !filt->includeunsupported ) - return false; - if ( supported && !filt->includesupported ) - return false; - if ( text && !filt->includetext ) - return false; - if ( graphics && !filt->includegraphics ) - return false; - if ( graphics && (bpp < filt->minbpp || filt->maxbpp < bpp) ) - return false; - if ( graphics && (width < filt->minxres || filt->maxxres < width) ) - return false; - if ( graphics && (height < filt->minyres || filt->maxyres < height) ) - return false; - // TODO: Support filtering text modes according to columns/rows. - return true; -} - -void FilterModes(struct dispmsg_crtc_mode* modes, size_t* nummodesptr, Filter* filt) -{ - size_t innum = *nummodesptr; - size_t outnum = 0; - for ( size_t i = 0; i < innum; i++ ) + for ( int i = 0; i < *argc; i++ ) { - if ( PassesFilter(modes[i], filt) ) - modes[outnum++] = modes[i]; + while ( i < *argc && !(*argv)[i] ) + { + for ( int n = i; n < *argc; n++ ) + (*argv)[n] = (*argv)[n+1]; + (*argc)--; + } } - *nummodesptr = outnum; } -void Help(FILE* fp, const char* argv0) +void help(FILE* fp, const char* argv0) { - fprintf(fp, "Usage: %s [OPTION ...] [PROGRAM-TO-RUN [ARG ...]]\n", argv0); + fprintf(fp, "Usage: %s [OPTION ...] [-- PROGRAM-TO-RUN [ARG ...]]\n", argv0); fprintf(fp, "Changes the video mode and optionally runs a program\n"); fprintf(fp, "\n"); fprintf(fp, "Options supported by %s:\n", argv0); @@ -320,16 +187,16 @@ void Help(FILE* fp, const char* argv0) fprintf(fp, " --version Output version information and exit\n"); fprintf(fp, "\n"); fprintf(fp, "Options for filtering modes:\n"); - fprintf(fp, " --show-all BOOL\n"); - fprintf(fp, " --show-supported BOOL, --show-unsupported BOOL\n"); - fprintf(fp, " --show-text BOOL\n"); - fprintf(fp, " --show-graphics BOOL\n"); - fprintf(fp, " --bpp BPP, --min-bpp BPP, --max-bpp BPP\n"); - fprintf(fp, " --width NUM, --min-width NUM, --max-width NUM\n"); - fprintf(fp, " --height NUM, --min-height NUM, --max-height NUM\n"); + fprintf(fp, " --show-all=BOOL\n"); + fprintf(fp, " --show-supported=BOOL, --show-unsupported=BOOL\n"); + fprintf(fp, " --show-text=BOOL\n"); + fprintf(fp, " --show-graphics=BOOL\n"); + fprintf(fp, " --bpp BPP, --min-bpp=BPP, --max-bpp=BPP\n"); + fprintf(fp, " --width=NUM, --min-width=NUM, --max-width=NUM\n"); + fprintf(fp, " --height=NUM, --min-heigh= NUM, --max-height=NUM\n"); } -void Version(FILE* fp, const char* argv0) +void version(FILE* fp, const char* argv0) { fprintf(fp, "%s (Sortix) %s\n", argv0, VERSIONSTR); fprintf(fp, "License GPLv3+: GNU GPL version 3 or later .\n"); @@ -337,146 +204,326 @@ void Version(FILE* fp, const char* argv0) fprintf(fp, "There is NO WARRANTY, to the extent permitted by law.\n"); } -bool SizeTParam(const char* name, const char* option, - const char* param, size_t* minvar, size_t* maxvar) +bool string_parameter(const char* option, + const char* arg, + int argc, + char** argv, + int* ip, + const char* argv0, + const char** result) { - char opt[64]; stpcpy(stpcpy(opt, "--"), name); - char minopt[64]; stpcpy(stpcpy(minopt, "--min-"), name); - char maxopt[64]; stpcpy(stpcpy(maxopt, "--max-"), name); - if ( !strcmp(option, opt) ) - *minvar = *maxvar = ParseSizeT(param); - else if ( !strcmp(option, minopt) ) - *minvar = ParseBool(param); - else if ( !strcmp(option, maxopt) ) - *maxvar = ParseBool(param); - else + size_t option_len = strlen(option); + if ( strncmp(option, arg, option_len) != 0 ) return false; + if ( arg[option_len] == '=' ) + return arg + option_len + 1; + if ( arg[option_len] != '\0' ) + return false; + if ( *ip + 1 == argc ) + { + fprintf(stderr, "%s: expected operand after `%s'\n", argv0, option); + exit(1); + } + *result = argv[++*ip]; + argv[*ip] = NULL; return true; } -bool BoolParam(const char* name, const char* option, - const char* param, bool* var) +bool minmax_parameter(const char* option, + const char* min_option, + const char* max_option, + const char* arg, + int argc, + char** argv, + int* ip, + const char* argv0, + size_t* min_result, + size_t* max_result) { - char opt[64]; stpcpy(stpcpy(opt, "--"), name); - if ( !strcmp(option, opt) ) - *var = ParseBool(param); - else - return false; - return true; + const char* parameter; + if ( string_parameter(option, arg, argc, argv, ip, argv0, ¶meter) ) + return *min_result = *max_result = parse_size_t(parameter), true; + if ( string_parameter(min_option, arg, argc, argv, ip, argv0, ¶meter) ) + return *min_result = parse_size_t(parameter), true; + if ( string_parameter(max_option, arg, argc, argv, ip, argv0, ¶meter) ) + return *max_result = parse_size_t(parameter), true; + return false; } +#define MINMAX_PARAMETER(option, min_result, max_result) \ + minmax_parameter("--" option, "--min-" option, "--max" option, arg, \ + argc, argv, &i, argv0, min_result, max_result) + +bool bool_parameter(const char* option, + const char* arg, + int argc, + char** argv, + int* ip, + const char* argv0, + bool* result) +{ + const char* parameter; + if ( string_parameter(option, arg, argc, argv, ip, argv0, ¶meter) ) + return *result = parse_bool(parameter), true; + return false; +} + +#define BOOL_PARAMETER(option, result) \ + bool_parameter("--" option, arg, argc, argv, &i, argv0, result) + int main(int argc, char* argv[]) { - const char* argv0 = argv[0]; + struct filter filter; - Filter filt; - filt.includeall = false; - filt.includesupported = true; - filt.includeunsupported = false; - filt.includetext = true; - filt.includegraphics = true; + filter.include_all = false; + filter.include_supported = true; + filter.include_unsupported = false; + filter.include_text = true; + filter.include_graphics = true; // TODO: HACK: The kernel log printing requires either text mode or 32-bit // graphics. For now, just filter away anything but 32-bit graphics. - filt.minbpp = 32; - filt.maxbpp = 32; - filt.minxres = 0; - filt.maxxres = SIZE_MAX; - filt.minyres = 0; - filt.maxyres = SIZE_MAX; - filt.minxchars = 0; - filt.maxxchars = SIZE_MAX; - filt.minychars = 0; - filt.maxychars = SIZE_MAX; + filter.minbpp = 32; + filter.maxbpp = 32; + filter.minxres = 0; + filter.maxxres = SIZE_MAX; + filter.minyres = 0; + filter.maxyres = SIZE_MAX; + filter.minxchars = 0; + filter.maxxchars = SIZE_MAX; + filter.minychars = 0; + filter.maxychars = SIZE_MAX; + const char* argv0 = argv[0]; for ( int i = 1; i < argc; i++ ) { const char* arg = argv[i]; - if ( arg[0] != '-' ) { break; } - if ( !strcmp(arg, "--") ) { break; } - if ( !strcmp(arg, "--help") ) { Help(stdout, argv0); exit(0); } - if ( !strcmp(arg, "--version") ) { Version(stdout, argv0); exit(0); } - if ( i == argc-1 ) + if ( arg[0] != '-' ) + break; + argv[i] = NULL; + if ( !strcmp(arg, "--") ) + break; + if ( arg[1] != '-' ) { - fprintf(stderr, "%s: missing parameter to %s\n", argv0, arg); - Help(stderr, argv0); - exit(1); + while ( char c = *++arg ) switch ( c ) + { + default: + fprintf(stderr, "%s: unknown option -- '%c'\n", argv0, c); + help(stderr, argv0); + exit(1); + } } - const char* p = argv[++i]; argv[i] = NULL; - bool handled = - BoolParam("show-all", arg, p, &filt.includeall) || - BoolParam("show-supported", arg, p, &filt.includesupported) || - BoolParam("show-unsupported", arg, p, &filt.includeunsupported) || - BoolParam("show-text", arg, p, &filt.includetext) || - BoolParam("show-graphics", arg, p, &filt.includegraphics) || - SizeTParam("bpp", arg, p, &filt.minbpp, &filt.maxbpp) || - SizeTParam("width", arg, p, &filt.minxres, &filt.maxxres) || - SizeTParam("height", arg, p, &filt.minyres, &filt.maxyres) || - false; - if ( !handled ) + else if ( !strcmp(arg, "--help") ) + help(stdout, argv0), exit(0); + else if ( !strcmp(arg, "--version") ) + version(stdout, argv0), exit(0); + else if ( BOOL_PARAMETER("show-all", &filter.include_all) ) { } + else if ( BOOL_PARAMETER("show-supported", &filter.include_supported) ) { } + else if ( BOOL_PARAMETER("show-unsupported", &filter.include_unsupported) ) { } + else if ( BOOL_PARAMETER("show-text", &filter.include_text) ) { } + else if ( BOOL_PARAMETER("show-graphics", &filter.include_graphics) ) { } + else if ( MINMAX_PARAMETER("bpp", &filter.minbpp, &filter.maxbpp) ) { } + else if ( MINMAX_PARAMETER("width", &filter.minxres, &filter.maxxres) ) { } + else if ( MINMAX_PARAMETER("height", &filter.minyres, &filter.maxyres) ) { } + else { - fprintf(stderr, "%s: no such option: %s\n", argv0, arg); - Help(stderr, argv0); + fprintf(stderr, "%s: unknown option: %s\n", argv0, arg); + help(stderr, argv0); exit(1); } } - struct dispmsg_crtc_mode prevmode = GetCurrentMode(); -#if 1 - size_t nummodes = 0; - struct dispmsg_crtc_mode* modes = GetAvailableModes(&nummodes); - if ( !modes ) { perror("Unable to detect available video modes"); exit(1); } + compact_arguments(&argc, &argv); - if ( !nummodes ) + size_t num_modes = 0; + struct dispmsg_crtc_mode* modes = GetAvailableModes(&num_modes); + if ( !modes ) + error(1, errno, "Unable to detect available video modes"); + + if ( !num_modes ) { fprintf(stderr, "No video modes are currently available.\n"); - fprintf(stderr, "Try make sure a driver exists and is activated.\n"); - exit(1); + fprintf(stderr, "Try make sure a device driver exists and is activated.\n"); + exit(11); } - FilterModes(modes, &nummodes, &filt); - if ( !nummodes ) + filter_modes(modes, &num_modes, &filter); + if ( !num_modes ) { - fprintf(stderr, "\ -No video mode remains after filtering away unwanted modes.\n"); - fprintf(stderr, "\ -Try make sure the desired driver is loaded and is configured correctly.\n"); - exit(1); + fprintf(stderr, "No video mode remains after filtering away unwanted modes.\n"); + fprintf(stderr, "Try make sure the desired device driver is loaded and is configured correctly.\n"); + exit(12); } + int num_modes_display_length = 1; + for ( size_t i = num_modes; 10 <= i; i /= 10 ) + num_modes_display_length++; + + int mode_set_error = 0; +retry_pick_mode: size_t selection = 0; bool decided = false; + bool first_render = true; + struct wincurpos render_at; + memset(&render_at, 0, sizeof(render_at)); while ( !decided ) { - DrawMenu(selection, modes, nummodes); - switch ( GetKey(stdin) ) + fflush(stdout); + + struct winsize ws; + if ( tcgetwinsize(1, &ws) != 0 ) { - case KBKEY_ESC: printf("\n"); exit(10); break; - case KBKEY_UP: selection = (nummodes+selection-1) % nummodes; break; - case KBKEY_DOWN: selection = (selection+1) % nummodes; break; - case KBKEY_ENTER: decided = true; break; + ws.ws_col = 80; + ws.ws_row = 25; + } + + struct wincurpos wcp; + if ( tcgetwincurpos(1, &wcp) != 0 ) + { + wcp.wcp_col = 1; + wcp.wcp_row = 1; + } + + size_t off = 1; // The "Please select ..." line at the top. + if ( mode_set_error ) + off++; + + size_t entries_per_page = ws.ws_row - off; + size_t page = selection / entries_per_page; + size_t from = page * entries_per_page; + size_t how_many_available = num_modes - from; + size_t how_many = entries_per_page; + if ( how_many_available < how_many ) + how_many = how_many_available; + size_t lines_on_screen = off + how_many; + + if ( first_render ) + { + while ( wcp.wcp_row && ws.ws_row - (wcp.wcp_row + 1) < lines_on_screen ) + { + printf("\e[S"); + printf("\e[%juH", 1 + (uintmax_t) wcp.wcp_row); + wcp.wcp_row--; + wcp.wcp_col = 1; + } + render_at = wcp; + first_render = false; + } + + printf("\e[m"); + printf("\e[%juH", 1 + (uintmax_t) render_at.wcp_row); + printf("\e[2K"); + + if ( mode_set_error ) + printf("Error: Could not set desired mode: %s\n", strerror(mode_set_error)); + printf("Please select one of these video modes or press ESC to abort.\n"); + + for ( size_t i = 0; i < how_many; i++ ) + { + size_t index = from + i; + size_t screenline = off + index - from; + const char* color = index == selection ? "\e[31m" : "\e[m"; + printf("\e[%zuH", 1 + render_at.wcp_row + screenline); + printf("%s", color); + printf("\e[2K"); + printf(" [%-*zu] ", num_modes_display_length, index); + if ( modes[i].control & DISPMSG_CONTROL_VALID ) + printf("%u x %u x %u", + modes[i].fb_format, + modes[i].view_xres, + modes[i].view_yres); + else if ( modes[i].control & DISPMSG_CONTROL_OTHER_RESOLUTIONS ) + printf("(enter a custom resolution)"); + else + printf("(unknown video device feature)"); + printf("\e[m"); + } + + printf("\e[J"); + fflush(stdout); + + unsigned int oldtermmode; + if ( gettermmode(0, &oldtermmode) < 0 ) + error(1, errno, "gettermmode"); + + if ( settermmode(0, TERMMODE_KBKEY | TERMMODE_UNICODE | TERMMODE_SIGNAL) < 0 ) + error(1, errno, "settermmode"); + + uint32_t codepoint; + ssize_t numbytes; + int kbkey = 0; + while ( 0 < (numbytes = read(0, &codepoint, sizeof(codepoint))) && + (kbkey = KBKEY_DECODE(codepoint)) == 0 ) + continue; + + if ( settermmode(0, oldtermmode) < 0 ) + error(1, errno, "settermmode"); + + if ( numbytes < 0 ) + error(1, errno, "read"); + + switch ( kbkey ) + { + case KBKEY_ESC: + printf("\n"); + exit(10); + break; + case KBKEY_UP: + if ( selection ) + selection--; + else + selection = num_modes -1; + break; + case KBKEY_DOWN: + if ( selection + 1 == num_modes ) + selection = 0; + else + selection++; + break; + case KBKEY_ENTER: + fgetc(stdin); + printf("\n"); + decided = true; + break; } } struct dispmsg_crtc_mode mode = modes[selection]; + if ( mode.control & DISPMSG_CONTROL_OTHER_RESOLUTIONS ) + { + uintmax_t req_bpp; + uintmax_t req_width; + uintmax_t req_height; + while ( true ) + { + printf("Enter video mode [BPP x WIDTH x HEIGHT]: "); + fflush(stdout); + if ( scanf("%ju x %ju x %ju", &req_bpp, &req_width, &req_height) != 3 ) + { + fgetc(stdin); + fflush(stdin); + continue; + } + fgetc(stdin); + break; + } + mode.fb_format = req_bpp; + mode.view_xres = req_width; + mode.view_yres = req_height; + mode.control &= ~DISPMSG_CONTROL_OTHER_RESOLUTIONS; + mode.control |= DISPMSG_CONTROL_VALID; + } + if ( !SetCurrentMode(mode) ) { - error(1, errno, "Unable to set video mode: %s", StringOfCrtcMode(mode)); + error(0, mode_set_error = errno, "Unable to set video mode %ju x %ju x %ju", + (uintmax_t) mode.fb_format, + (uintmax_t) mode.view_xres, + (uintmax_t) mode.view_yres); + goto retry_pick_mode; } -#endif + if ( 1 < argc ) { - pid_t childpid = fork(); - if ( childpid < 0 ) { perror("fork"); exit(1); } - if ( childpid ) - { - int status; - waitpid(childpid, &status, 0); - if ( !SetCurrentMode(prevmode) ) - { - error(1, errno, "Unable to restore video mode: %s", StringOfCrtcMode(prevmode)); - } - exit(WEXITSTATUS(status)); - } execvp(argv[1], argv + 1); error(127, errno, "`%s'", argv[1]); }