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