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