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