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