xref: /haiku/src/bin/screenmode/screenmode.cpp (revision 6ba31e25d75ed55a749618e0565f42029a8006a8)
1 /*
2  * Copyright 2008-2011, Axel Dörfler, axeld@pinc-software.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include <ctype.h>
8 #include <getopt.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <strings.h>
13 
14 #include <Alert.h>
15 #include <Application.h>
16 #include <Screen.h>
17 
18 #include "ScreenMode.h"
19 
20 
21 static struct option const kLongOptions[] = {
22 	{"fall-back", no_argument, 0, 'f'},
23 	{"dont-confirm", no_argument, 0, 'q'},
24 	{"modeline", no_argument, 0, 'm'},
25 	{"short", no_argument, 0, 's'},
26 	{"list", no_argument, 0, 'l'},
27 	{"help", no_argument, 0, 'h'},
28 	{"brightness", required_argument, 0, 'b'},
29 	{NULL}
30 };
31 
32 extern const char *__progname;
33 static const char *kProgramName = __progname;
34 
35 
36 static color_space
color_space_for_depth(int32 depth)37 color_space_for_depth(int32 depth)
38 {
39 	switch (depth) {
40 		case 8:
41 			return B_CMAP8;
42 		case 15:
43 			return B_RGB15;
44 		case 16:
45 			return B_RGB16;
46 		case 24:
47 			return B_RGB24;
48 		case 32:
49 		default:
50 			return B_RGB32;
51 	}
52 }
53 
54 
55 static void
print_mode(const screen_mode & mode,bool shortOutput)56 print_mode(const screen_mode& mode, bool shortOutput)
57 {
58 	const char* format
59 		= shortOutput ? "%ld %ld %ld %g\n" : "%ld %ld, %ld bits, %g Hz\n";
60 	printf(format, mode.width, mode.height, mode.BitsPerPixel(), mode.refresh);
61 }
62 
63 
64 static void
print_mode(const display_mode & displayMode,const screen_mode & mode)65 print_mode(const display_mode& displayMode, const screen_mode& mode)
66 {
67 	const display_timing& timing = displayMode.timing;
68 
69 	printf("%" B_PRIu32 "  %u %u %u %u  %u %u %u %u ", timing.pixel_clock / 1000,
70 		timing.h_display, timing.h_sync_start, timing.h_sync_end,
71 		timing.h_total, timing.v_display, timing.v_sync_start,
72 		timing.v_sync_end, timing.v_total);
73 
74 	// TODO: more flags?
75 	if ((timing.flags & B_POSITIVE_HSYNC) != 0)
76 		printf(" +HSync");
77 	if ((timing.flags & B_POSITIVE_VSYNC) != 0)
78 		printf(" +VSync");
79 	if ((timing.flags & B_TIMING_INTERLACED) != 0)
80 		printf(" Interlace");
81 	printf(" %" B_PRId32 "\n", mode.BitsPerPixel());
82 }
83 
84 
85 static void
usage(int status)86 usage(int status)
87 {
88 	fprintf(stderr,
89 		"Usage: %s [options] <mode>\n"
90 		"Sets the specified screen mode. When no screen mode has been chosen,\n"
91 		"the current one is printed. <mode> takes the form: <width> <height>\n"
92 		"<depth> <refresh-rate>, or <width>x<height>, etc.\n"
93 		"      --fall-back\tchanges to the standard fallback mode, and "
94 			"displays a\n"
95 		"\t\t\tnotification requester.\n"
96 		"  -s  --short\t\twhen no mode is given the current screen mode is\n"
97 			"\t\t\tprinted in short form.\n"
98 		"  -l  --list\t\tdisplay a list of the available modes.\n"
99 		"  -q  --dont-confirm\tdo not confirm the mode after setting it.\n"
100 		"  -b  --brightness f\tset brightness (range 0 to 1).\n"
101 		"  -b  --brightness +/-f\tchange brightness by given amount.\n"
102 		"  -m  --modeline\taccept and print X-style modeline modes:\n"
103 		"\t\t\t  <pclk> <h-display> <h-sync-start> <h-sync-end> <h-total>\n"
104 		"\t\t\t  <v-disp> <v-sync-start> <v-sync-end> <v-total> [flags] "
105 			"[depth]\n"
106 		"\t\t\t(supported flags are: +/-HSync, +/-VSync, Interlace)\n",
107 		kProgramName);
108 
109 	exit(status);
110 }
111 
112 
113 int
main(int argc,char ** argv)114 main(int argc, char** argv)
115 {
116 	bool fallbackMode = false;
117 	bool setMode = false;
118 	bool shortOutput = false;
119 	bool listModes = false;
120 	bool modeLine = false;
121 	bool confirm = true;
122 	int width = -1;
123 	int height = -1;
124 	int depth = -1;
125 	float refresh = -1;
126 	float brightness = std::nanf("0");
127 	bool relativeBrightness = false;
128 	display_mode mode;
129 
130 	// TODO: add a possibility to set a virtual screen size in addition to
131 	// the display resolution!
132 
133 	int c;
134 	while ((c = getopt_long(argc, argv, "shlfqmb:", kLongOptions, NULL)) != -1) {
135 		switch (c) {
136 			case 0:
137 				break;
138 			case 'f':
139 				fallbackMode = true;
140 				setMode = true;
141 				confirm = false;
142 				break;
143 			case 's':
144 				shortOutput = true;
145 				break;
146 			case 'l':
147 				listModes = true;
148 				break;
149 			case 'm':
150 				modeLine = true;
151 				break;
152 			case 'q':
153 				confirm = false;
154 				break;
155 			case 'b':
156 				if (optarg[0] == '+' || optarg[0] == '-')
157 					relativeBrightness = true;
158 				brightness = atof(optarg);
159 				break;
160 			case 'h':
161 				usage(0);
162 				break;
163 			default:
164 				usage(1);
165 				break;
166 		}
167 	}
168 
169 	if (argc - optind > 0) {
170 		int depthIndex = -1;
171 
172 		// arguments to specify the mode are following
173 
174 		if (!modeLine) {
175 			int parsed = sscanf(argv[optind], "%dx%dx%d", &width, &height,
176 				&depth);
177 			if (parsed == 2)
178 				depthIndex = optind + 1;
179 			else if (parsed == 1) {
180 				if (argc - optind > 1) {
181 					height = strtol(argv[optind + 1], NULL, 0);
182 					depthIndex = optind + 2;
183 				} else
184 					usage(1);
185 			} else if (parsed != 3)
186 				usage(1);
187 
188 			if (depthIndex > 0 && depthIndex < argc)
189 				depth = strtol(argv[depthIndex], NULL, 0);
190 			if (depthIndex + 1 < argc)
191 				refresh = strtod(argv[depthIndex + 1], NULL);
192 		} else {
193 			// parse mode line
194 			if (argc - optind < 9)
195 				usage(1);
196 
197 			mode.timing.pixel_clock = strtol(argv[optind], NULL, 0) * 1000;
198 			mode.timing.h_display = strtol(argv[optind + 1], NULL, 0);
199 			mode.timing.h_sync_start = strtol(argv[optind + 2], NULL, 0);
200 			mode.timing.h_sync_end = strtol(argv[optind + 3], NULL, 0);
201 			mode.timing.h_total = strtol(argv[optind + 4], NULL, 0);
202 			mode.timing.v_display = strtol(argv[optind + 5], NULL, 0);
203 			mode.timing.v_sync_start = strtol(argv[optind + 6], NULL, 0);
204 			mode.timing.v_sync_end = strtol(argv[optind + 7], NULL, 0);
205 			mode.timing.v_total = strtol(argv[optind + 8], NULL, 0);
206 			mode.timing.flags = 0;
207 			mode.space = B_RGB32;
208 
209 			int i = optind + 9;
210 			while (i < argc) {
211 				if (!strcasecmp(argv[i], "+HSync"))
212 					mode.timing.flags |= B_POSITIVE_HSYNC;
213 				else if (!strcasecmp(argv[i], "+VSync"))
214 					mode.timing.flags |= B_POSITIVE_VSYNC;
215 				else if (!strcasecmp(argv[i], "Interlace"))
216 					mode.timing.flags |= B_TIMING_INTERLACED;
217 				else if (!strcasecmp(argv[i], "-VSync")
218 					|| !strcasecmp(argv[i], "-HSync")) {
219 					// okay, but nothing to do
220 				} else if (isdigit(argv[i][0]) && i + 1 == argc) {
221 					// bits per pixel
222 					mode.space
223 						= color_space_for_depth(strtoul(argv[i], NULL, 0));
224 				} else {
225 					fprintf(stderr, "Unknown flag: %s\n", argv[i]);
226 					exit(1);
227 				}
228 
229 				i++;
230 			}
231 
232 			mode.virtual_width = mode.timing.h_display;
233 			mode.virtual_height = mode.timing.v_display;
234 			mode.h_display_start = 0;
235 			mode.v_display_start = 0;
236 		}
237 
238 		setMode = true;
239 	}
240 
241 	BApplication application("application/x-vnd.Haiku-screenmode");
242 
243 	ScreenMode screenMode(NULL);
244 	screen_mode currentMode;
245 	screenMode.Get(currentMode);
246 
247 	if (!isnan(brightness)) {
248 		BScreen screen;
249 		if (relativeBrightness) {
250 			float previousBrightness;
251 			screen.GetBrightness(&previousBrightness);
252 			brightness = previousBrightness + brightness;
253 
254 			// Clamp to min/max values
255 			if (brightness < 0.f)
256 				brightness = 0.f;
257 
258 			if (brightness > 1.f)
259 				brightness = 1.f;
260 		}
261 
262 		if (brightness < 0.f || brightness > 1.f)
263 			printf("Brightness %f is out of range\n", brightness);
264 		screen.SetBrightness(brightness);
265 	}
266 
267 	if (listModes) {
268 		// List all reported modes
269 		if (!shortOutput)
270 			printf("Available screen modes:\n");
271 
272 		for (int index = 0; index < screenMode.CountModes(); index++) {
273 			if (modeLine) {
274 				print_mode(screenMode.DisplayModeAt(index),
275 					screenMode.ModeAt(index));
276 			} else
277 				print_mode(screenMode.ModeAt(index), shortOutput);
278 		}
279 
280 		return 0;
281 	}
282 
283 	if (!setMode) {
284 		// Just print the current mode
285 		if (modeLine) {
286 			display_mode mode;
287 			screenMode.Get(mode);
288 			print_mode(mode, currentMode);
289 		} else {
290 			if (!shortOutput)
291 				printf("Resolution: ");
292 			print_mode(currentMode, shortOutput);
293 		}
294 		return 0;
295 	}
296 
297 	screen_mode newMode = currentMode;
298 
299 	if (fallbackMode) {
300 		if (currentMode.width == 800 && currentMode.height == 600) {
301 			newMode.width = 640;
302 			newMode.height = 480;
303 			newMode.space = B_CMAP8;
304 			newMode.refresh = 60;
305 		} else {
306 			newMode.width = 800;
307 			newMode.height = 600;
308 			newMode.space = B_RGB16;
309 			newMode.refresh = 60;
310 		}
311 	} else if (modeLine) {
312 		display_mode currentDisplayMode;
313 		if (screenMode.Get(currentDisplayMode) == B_OK)
314 			mode.flags = currentDisplayMode.flags;
315 	} else {
316 		newMode.width = width;
317 		newMode.height = height;
318 
319 		if (depth != -1)
320 			newMode.space = color_space_for_depth(depth);
321 		else
322 			newMode.space = B_RGB32;
323 
324 		if (refresh > 0)
325 			newMode.refresh = refresh;
326 		else
327 			newMode.refresh = 60;
328 	}
329 
330 	status_t status;
331 	if (modeLine)
332 		status = screenMode.Set(mode);
333 	else
334 		status = screenMode.Set(newMode);
335 
336 	if (status == B_OK) {
337 		if (confirm) {
338 			printf("Is this mode okay (Y/n - will revert after 10 seconds)? ");
339 			fflush(stdout);
340 
341 			int flags = fcntl(STDIN_FILENO, F_GETFL, 0);
342 			fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK);
343 
344 			bigtime_t end = system_time() + 10000000LL;
345 			int c = 'n';
346 			while (system_time() < end) {
347 				c = getchar();
348 				if (c != -1)
349 					break;
350 
351 				snooze(10000);
352 			}
353 
354 			if (c != '\n' && tolower(c) != 'y')
355 				screenMode.Revert();
356 		}
357 	} else {
358 		fprintf(stderr,
359 			"%s: Could not set screen mode "
360 			"%" B_PRId32 "x%" B_PRId32 "x%" B_PRId32 ": "
361 			"%s\n",
362 				kProgramName,
363 				newMode.width, newMode.height, newMode.BitsPerPixel(),
364 				strerror(status));
365 		return 1;
366 	}
367 
368 	if (fallbackMode) {
369 		// display notification requester
370 		BAlert* alert = new BAlert("screenmode",
371 			"You have used the shortcut <Shift><Command><Ctrl><Escape> to "
372 			"reset the screen mode to a safe fallback.", "Keep", "Revert");
373 		alert->SetShortcut(1, B_ESCAPE);
374 		if (alert->Go() == 1)
375 			screenMode.Revert();
376 	}
377 
378 	return 0;
379 }
380