* Copyright 2008-2011, Axel Dörfler, axeld@pinc-software.de.
* Distributed under the terms of the MIT License.
*/
#include <ctype.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <Alert.h>
#include <Application.h>
#include <Screen.h>
#include "ScreenMode.h"
static struct option const kLongOptions[] = {
{"fall-back", no_argument, 0, 'f'},
{"dont-confirm", no_argument, 0, 'q'},
{"modeline", no_argument, 0, 'm'},
{"short", no_argument, 0, 's'},
{"list", no_argument, 0, 'l'},
{"help", no_argument, 0, 'h'},
{"brightness", required_argument, 0, 'b'},
{"get-brightness", no_argument, 0, 'B'},
{NULL}
};
extern const char *__progname;
static const char *kProgramName = __progname;
static color_space
color_space_for_depth(int32 depth)
{
switch (depth) {
case 8:
return B_CMAP8;
case 15:
return B_RGB15;
case 16:
return B_RGB16;
case 24:
return B_RGB24;
case 32:
default:
return B_RGB32;
}
}
static void
print_mode(const screen_mode& mode, bool shortOutput)
{
const char* format
= shortOutput ? "%ld %ld %ld %g\n" : "%ld %ld, %ld bits, %g Hz\n";
printf(format, mode.width, mode.height, mode.BitsPerPixel(), mode.refresh);
}
static void
print_mode(const display_mode& displayMode, const screen_mode& mode)
{
const display_timing& timing = displayMode.timing;
printf("%" B_PRIu32 " %u %u %u %u %u %u %u %u ", timing.pixel_clock / 1000,
timing.h_display, timing.h_sync_start, timing.h_sync_end,
timing.h_total, timing.v_display, timing.v_sync_start,
timing.v_sync_end, timing.v_total);
if ((timing.flags & B_POSITIVE_HSYNC) != 0)
printf(" +HSync");
if ((timing.flags & B_POSITIVE_VSYNC) != 0)
printf(" +VSync");
if ((timing.flags & B_TIMING_INTERLACED) != 0)
printf(" Interlace");
printf(" %" B_PRId32 "\n", mode.BitsPerPixel());
}
static void
usage(int status)
{
fprintf(stderr,
"Usage: %s [options] <mode>\n"
"Sets the specified screen mode. When no screen mode has been chosen,\n"
"the current one is printed. <mode> takes the form: <width> <height>\n"
"<depth> <refresh-rate>, or <width>x<height>, etc.\n"
" --fall-back\tchanges to the standard fallback mode, and "
"displays a\n"
"\t\t\tnotification requester.\n"
" -s --short\t\twhen no mode is given the current screen mode or\n\n"
"\t\t\tthe screen brightness is printed in short form.\n"
" -l --list\t\tdisplay a list of the available modes.\n"
" -q --dont-confirm\tdo not confirm the mode after setting it.\n"
" -b --brightness f\tset brightness (range 0 to 1).\n"
" -b --brightness +/-f\tchange brightness by given amount.\n"
" -B --get-brightness\tprint the current brightness to stdout.\n"
"\t\t\tinstead of the screen mode\n"
" -m --modeline\taccept and print X-style modeline modes:\n"
"\t\t\t <pclk> <h-display> <h-sync-start> <h-sync-end> <h-total>\n"
"\t\t\t <v-disp> <v-sync-start> <v-sync-end> <v-total> [flags] "
"[depth]\n"
"\t\t\t(supported flags are: +/-HSync, +/-VSync, Interlace)\n",
kProgramName);
exit(status);
}
int
main(int argc, char** argv)
{
bool fallbackMode = false;
bool setMode = false;
bool shortOutput = false;
bool listModes = false;
bool modeLine = false;
bool getBrightness = false;
bool confirm = true;
int width = -1;
int height = -1;
int depth = -1;
float refresh = -1;
float brightness = std::nanf("0");
bool relativeBrightness = false;
display_mode mode;
int c;
while ((c = getopt_long(argc, argv, "shlfqmb:B", kLongOptions, NULL)) != -1) {
switch (c) {
case 0:
break;
case 'f':
fallbackMode = true;
setMode = true;
confirm = false;
break;
case 's':
shortOutput = true;
break;
case 'l':
listModes = true;
break;
case 'm':
modeLine = true;
break;
case 'q':
confirm = false;
break;
case 'b':
if (optarg[0] == '+' || optarg[0] == '-')
relativeBrightness = true;
brightness = atof(optarg);
break;
case 'B':
getBrightness = true;
break;
case 'h':
usage(0);
break;
default:
usage(1);
break;
}
}
if (argc - optind > 0) {
int depthIndex = -1;
if (!modeLine) {
int parsed = sscanf(argv[optind], "%dx%dx%d", &width, &height,
&depth);
if (parsed == 2)
depthIndex = optind + 1;
else if (parsed == 1) {
if (argc - optind > 1) {
height = strtol(argv[optind + 1], NULL, 0);
depthIndex = optind + 2;
} else
usage(1);
} else if (parsed != 3)
usage(1);
if (depthIndex > 0 && depthIndex < argc)
depth = strtol(argv[depthIndex], NULL, 0);
if (depthIndex + 1 < argc)
refresh = strtod(argv[depthIndex + 1], NULL);
} else {
if (argc - optind < 9)
usage(1);
mode.timing.pixel_clock = strtol(argv[optind], NULL, 0) * 1000;
mode.timing.h_display = strtol(argv[optind + 1], NULL, 0);
mode.timing.h_sync_start = strtol(argv[optind + 2], NULL, 0);
mode.timing.h_sync_end = strtol(argv[optind + 3], NULL, 0);
mode.timing.h_total = strtol(argv[optind + 4], NULL, 0);
mode.timing.v_display = strtol(argv[optind + 5], NULL, 0);
mode.timing.v_sync_start = strtol(argv[optind + 6], NULL, 0);
mode.timing.v_sync_end = strtol(argv[optind + 7], NULL, 0);
mode.timing.v_total = strtol(argv[optind + 8], NULL, 0);
mode.timing.flags = 0;
mode.space = B_RGB32;
int i = optind + 9;
while (i < argc) {
if (!strcasecmp(argv[i], "+HSync"))
mode.timing.flags |= B_POSITIVE_HSYNC;
else if (!strcasecmp(argv[i], "+VSync"))
mode.timing.flags |= B_POSITIVE_VSYNC;
else if (!strcasecmp(argv[i], "Interlace"))
mode.timing.flags |= B_TIMING_INTERLACED;
else if (!strcasecmp(argv[i], "-VSync")
|| !strcasecmp(argv[i], "-HSync")) {
} else if (isdigit(argv[i][0]) && i + 1 == argc) {
mode.space
= color_space_for_depth(strtoul(argv[i], NULL, 0));
} else {
fprintf(stderr, "Unknown flag: %s\n", argv[i]);
exit(1);
}
i++;
}
mode.virtual_width = mode.timing.h_display;
mode.virtual_height = mode.timing.v_display;
mode.h_display_start = 0;
mode.v_display_start = 0;
}
setMode = true;
}
BApplication application("application/x-vnd.Haiku-screenmode");
ScreenMode screenMode(NULL);
screen_mode currentMode;
screenMode.Get(currentMode);
if (!isnan(brightness)) {
BScreen screen;
if (relativeBrightness) {
float previousBrightness;
screen.GetBrightness(&previousBrightness);
brightness = previousBrightness + brightness;
if (brightness < 0.f)
brightness = 0.f;
if (brightness > 1.f)
brightness = 1.f;
}
if (brightness < 0.f || brightness > 1.f)
printf("Brightness %f is out of range\n", brightness);
screen.SetBrightness(brightness);
}
if (listModes) {
if (!shortOutput)
printf("Available screen modes:\n");
for (int index = 0; index < screenMode.CountModes(); index++) {
if (modeLine) {
print_mode(screenMode.DisplayModeAt(index),
screenMode.ModeAt(index));
} else
print_mode(screenMode.ModeAt(index), shortOutput);
}
return 0;
}
if (!setMode && !getBrightness) {
if (modeLine) {
display_mode mode;
screenMode.Get(mode);
print_mode(mode, currentMode);
} else {
if (!shortOutput)
printf("Resolution: ");
print_mode(currentMode, shortOutput);
}
return 0;
}
status_t status;
if (getBrightness) {
float brightnessToPrint = std::nanf("0");
BScreen screen;
status = screen.GetBrightness(&brightnessToPrint);
if (status != B_OK) {
fprintf(stderr, "Error retrieving brightness: %s\n", strerror(status));
return 1;
}
if (!shortOutput)
printf("Brightness: ");
printf("%f\n", brightnessToPrint);
return 0;
}
screen_mode newMode = currentMode;
if (fallbackMode) {
if (currentMode.width == 800 && currentMode.height == 600) {
newMode.width = 640;
newMode.height = 480;
newMode.space = B_CMAP8;
newMode.refresh = 60;
} else {
newMode.width = 800;
newMode.height = 600;
newMode.space = B_RGB16;
newMode.refresh = 60;
}
} else if (modeLine) {
display_mode currentDisplayMode;
if (screenMode.Get(currentDisplayMode) == B_OK)
mode.flags = currentDisplayMode.flags;
} else {
newMode.width = width;
newMode.height = height;
if (depth != -1)
newMode.space = color_space_for_depth(depth);
else
newMode.space = B_RGB32;
if (refresh > 0)
newMode.refresh = refresh;
else
newMode.refresh = 60;
}
if (modeLine)
status = screenMode.Set(mode);
else
status = screenMode.Set(newMode);
if (status == B_OK) {
if (confirm) {
printf("Is this mode okay (Y/n - will revert after 10 seconds)? ");
fflush(stdout);
int flags = fcntl(STDIN_FILENO, F_GETFL, 0);
fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK);
bigtime_t end = system_time() + 10000000LL;
int c = 'n';
while (system_time() < end) {
c = getchar();
if (c != -1)
break;
snooze(10000);
}
if (c != '\n' && tolower(c) != 'y')
screenMode.Revert();
}
} else {
fprintf(stderr,
"%s: Could not set screen mode "
"%" B_PRId32 "x%" B_PRId32 "x%" B_PRId32 ": "
"%s\n",
kProgramName,
newMode.width, newMode.height, newMode.BitsPerPixel(),
strerror(status));
return 1;
}
if (fallbackMode) {
BAlert* alert = new BAlert("screenmode",
"You have used the shortcut <Shift><Command><Ctrl><Escape> to "
"reset the screen mode to a safe fallback.", "Keep", "Revert");
alert->SetShortcut(1, B_ESCAPE);
if (alert->Go() == 1)
screenMode.Revert();
}
return 0;
}