// Another Modeline Calculator GNU GPL // This version is too early code, lacks too many features and is too buggy, // so please do not redistribute it. // http://delenn.tky.hut.fi/amlc/ // This program was written because the other modeline calculators don't want to // generate proper modes. // Use any modern C++ compiler (such as GCC 3+) to build. GCC 2.95 won't work // because it lacks too much C++ support. Just upgrade... // # g++ amlc.cpp -o amlc // # ./amlc // It prints some modelines that you can then copy&paste into X server config. #include #include #include #include using namespace std; // SETTINGS ASKED AT RUNTIME --------------------------------------------- // The default values defined here are never used. // Monitor aspect ratio double aspect = 1.333; // All frequencies are in Hz // Monitor or GFX card bandwidth (maximum dotclock) - usually 400 MHz for gfx chips // If the picture is too blurry for your taste, reduce this value. // Other than that, it doesn't normally make much difference. double maxPixelClock = 260e+6; // Monitor hsync range // 30-109 kHz, the default, is good for Samsung 959NF or other high quality 45 cm (19") monitors double minHSync = 30e+3; double maxHSync = 109e+3; // <-- THIS SETTING IS THE MOST IMPORTANT ONE // Monitor vsync range (refresh) - set min & max to exact same value to get fixed sync double minVSync = 50.0; double maxVSync = 160.0; // The program will never spit out modelines that go beyond the min/max values, // but often you'd like to have something lower than the hardware could do. // Set this to desired refresh and the program will try to match that... double idealVSync = 120.0; // Important: if your monitor can do 160 Hz or whatever, but you want lower, fiddle with // idealVSync instead of maxVSync. This way you give AMLC more freedom to choose modes // for you, even if the parameters don't match exactly (otherwise it would disable the mode) // RESOLUTIONS ----------------------------------------------------------- // Will use these to construct default modes. Add or remove values freely, if you wish const unsigned int xDimensions[] = { 320, 360, 400, 416, 512, 576, 640, 720, 768, 800, 832, 896, 928, 960, 1024, 1152, 1280, 1360, 1400, 1536, 1600, 1792, 1856, 1920, 2048 }; const unsigned int yDimensions[] = { 175, 200, 240, 288, 300, 312, 350, 360, 384, 400, 432, 480, 512, 525, 540, 576, 600, 624, 672, 696, 720, 768, 864, 960, 1008, 1020, 1024, 1050, 1080, 1088, 1200, 1344, 1392, 1440, 1536 }; // All common aspect ratios: // const double aspects[] = { 1.25, 4.0/3.0, 1.6, 16.0/9.0 }; // If y-dimension is smaller than this, the mode will be forced doublescan // This prevents the black lines, often (incorrectly) called interlace, // seen in small modes. const unsigned int doublescanBoundary = 400; // Postfix for add mode names, like 800x600@test, to keep 'em from overriding // the default modes of X (or something, I don't really have a clue). const string modenamePostfix = ""; // "@test"; // FINE-TUNING PICTURE POSITIONING ---------------------------------------- /* A line consists of 1. x visible pixels 2. xFront pixels on the right edge of the screen, before sync 3. xSync pixels wide sync signal (there is certain minimum width, but apparently only the rising edge matters) 4. xBack pixels on the left edge of the screen, after sync We want to have xFront just big enough so that the right edge of the picture doesn't b0rk. Additionally, xSync + xBack should be just big enough for the left edge to be OK. If these are too small, the image quality degrades slightly and there may be large black borders. */ // The commented out values are something that has been used successfully with some monitor. Use them to get some // idea of the range, then adjust to suit your monitor... // Time used for different phases of sync. // Horizontal: 1.0 = time used for painting visible part of the line const double timeHFront = 0.07; // 0.074219; // 0.075; // Width of the black border on right edge of the screen const double timeHSync = 0.1; // 0.107422; // 0.1125; // Sync pulse duration const double timeHBack = 0.15; // 0.183594; // 0.1875; // Width of the black border on left edge of the screen // Vertical: 1.0 = time used for painting visible lines of the picture // VFront is always one line // VSync is always three lines const double timeVBack = 0.06; // 0.031901; // 0.055664; // Adjust this to move picture up/down // FIXME: timeVBack is not sophisticated enough. The value should be different for different refresh rates and resolutions // I don't have the GTF specs, so the only thing I can do is reverse-engineer. // How exactly the mode aspect ratio must match the requested aspect ratio const double ASPECT_EPSILON = 0.0005; // Round a dimension to the nearest MOD 8 (as required by some video drivers) unsigned int resMod(double res) { return ((unsigned int)res > 8) ? ((unsigned int)res + 4) / 8 * 8 : 8; } void printMode(ostringstream& conf, unsigned int x, unsigned int y); string makeMode(unsigned int x, unsigned int y, bool doublescan = false, bool interlace = false, double *syncErrorFromIdeal = NULL); int main (int argc, char *args[]) { // TODO: add checks for input correctness int tmp; cerr << "Another Modeline Calculator 0.5.1 (beta) " << endl << "-----------------------------------------------------" << endl << endl; cerr << "This program will generate XFree86/Xorg X11 monitor definition and proper" << endl << "modelines. The config file section will be printed on stdout, not written" << endl << "to your config file. Redirect (>>) it there if you don't like copy&paste." << endl << endl; if (argc < 2) { cerr << "Usage: amlc [-c] [-d] [-l]" << endl << "-c make modelines for custom modes (will ask for dimensions)" << endl << "-d make modelines for default modes" << endl << "-l make modelines for legacy VGA modes 320x200 and 640x400" << endl << endl; return 1; } bool vgaModes = false; bool defaultModes = false; bool customModes = false; for (int i = 1; i < argc; i++) { if (string(args[i]) == "-l") vgaModes = true; if (string(args[i]) == "-d") defaultModes = true; if (string(args[i]) == "-c") customModes = true; } cerr << "Monitor identifier [Monitor0]: "; string name; getline(cin, name); if (!name.size()) name = "Monitor0"; cerr << endl << "Monitor aspect ratio" << endl << " 1. 1.333:1, 4:3 normal displays" << endl << " 2. 1.778:1, 16:9 wide displays" << endl << " 3. 1.600:1, \"wide\" computer TFTs" << endl << " 4. 1.250:1, 1280x1024 \"tall\" TFTs" << endl << " 0. other" << endl << ":"; cin >> tmp; switch (tmp) { case 1: aspect = 4.0/3.0; break; case 2: aspect = 16.0/9.0; break; case 3: aspect = 1.600; break; case 4: aspect = 1.250; break; default: cerr << "Aspect ratio, with four decimals (x:1.0000): "; cin >> aspect; } double diag = 0.0; if (fabs(aspect - 4.0/3.0) < ASPECT_EPSILON) { cerr << endl << "Monitor physical size (diagonal, corner to corner, visible area)" << endl << " 1. 35 cm (15\" CRTs, 13\" TFTs)" << endl << " 2. 40 cm (17\" CRTs, 15\" TFTs)" << endl << " 3. 45 cm (19\" CRTs, 17\" TFTs)" << endl << " 4. 50 cm (21\" CRTs, 19\" TFTs)" << endl << " 0. other" << endl << ":"; cin >> tmp; switch (tmp) { case 1: diag = 35.0; break; case 2: diag = 40.0; break; case 3: diag = 45.0; break; case 4: diag = 50.0; break; } } if (diag == 0.0) { cerr << "Diagonal size, in cm: "; cin >> diag; } cerr << endl << "You need to decide the refresh rate you prefer, and where using even higher" << endl << "frequencies doesn't do any good. For example, if 120 Hz is enough for you," << endl << "enter 120. If you want the hardware maximum, just hit enter." << endl << endl; cerr << "Ideal refresh rate, in Hz: "; cin.ignore(); if (cin.peek() != '\n') { cin >> idealVSync; } else { idealVSync = 0; } cerr << endl << "Next we need the monitor sync ranges. Using incorrect values may break your" << endl << "monitor if it is very old or special. With any normal monitor you'll just" << endl << "get no picture." << endl << endl << "Check out the monitor specifications (manual) for the correct horizontal and" << endl << "vertical sync values. You can also find these by Googling the model number." << endl << endl << "Note: some monitors slightly misdetect the frequencies and say \"out of sync\"" << endl << "even when it really isn't. If this happens, try a bit more conservative" << endl << "sync ranges than the manual says (maybe one kHz less for maximum, etc)" << endl << endl; cerr << "Minimum HSync, in kHz: "; cin >> minHSync; minHSync *= 1e3; cerr << "Maximum HSync, in kHz: "; cin >> maxHSync; maxHSync *= 1e3; cerr << "Minimum VSync, in Hz: "; cin >> minVSync; cerr << "Maximum VSync, in Hz: "; cin >> maxVSync; if (idealVSync == 0.0) idealVSync = maxVSync; cerr << endl << "The next setting is the maximum pixel clock. The graphics card and the monitor" << endl << "both have their pixel clock limits. With gfx card this is called the maximum" << endl << "pixel clock and you can find it in X server logs (grep MHz )." << endl << "For monitors, it is called bandwidth. However, the monitor maximum bandwidth," << endl << "for analog monitor such as CRT, is completely irrelevant and you should" << endl << "ignore whatever it says in the manual." << endl << endl << "Frequencies higher than the gfx card can support will cause X to not to use" << endl << "the Modeline at all." << endl << endl << "If you are getting too blurry image on the higher display modes, this is what" << endl << "you need to adjust. Use lower values to sharpen the image (and to force lower" << endl << "refresh rates on higher resolution modes)." << endl << endl << " - use at most the maximum pixel clock of the graphics card" << endl << " - ignore the value shown in your monitor specifications" << endl << " - use lower values if the refresh rate is too high and makes the image blurry" << endl << endl; cerr << "Maximum pixel clock, in MHz: "; cin >> maxPixelClock; maxPixelClock *= 1e6; cerr << endl; ostringstream conf; conf << "Section \"Monitor\"" << endl << " Identifier \"" << name << "\"" << endl << " ModelName \"Generated by Another Modeline Calculator\"" << endl << " HorizSync " << minHSync * 1e-3 << "-" << maxHSync * 1e-3 << endl << " VertRefresh " << minVSync << "-" << maxVSync << endl; if ((diag > 0.0) && (aspect > 0.0)) { conf << " DisplaySize " << (int)round(sqrt(1.0/(1.0 + 1.0/(aspect*aspect)))*diag*10) << " " << (int)round(sqrt(1.0/(1.0 + aspect*aspect))*diag*10) << " # Aspect ratio " << fixed << setprecision(3) << aspect << ":1" << endl; } if (vgaModes && ((aspect != 1.600) || !defaultModes)) { conf << " # Legacy VGA modes" << endl; cerr << endl << "Legacy VGA modes:" << endl; printMode(conf, 320, 200); printMode(conf, 640, 400); } if (defaultModes) { conf << " # Default modes" << endl; cerr << endl << "Default modes:" << endl; // TODO: improve this algorithm for (unsigned int j = 0; j < sizeof(yDimensions)/sizeof(int); j++) { for (unsigned int i = 0; i < sizeof(xDimensions)/sizeof(int); i++) { unsigned int x = xDimensions[i], y = yDimensions[j]; if (fabs((double)x/y - aspect) > ASPECT_EPSILON) continue; // Create a modeline, if possible printMode(conf, x, y); } } } if (customModes) { conf << " # Custom modes" << endl; cerr << endl << "Custom modes:" << endl; cerr << "Input an empty line to stop. The program calculates the Y dimension if omitted." << endl; cin.ignore(); while (1) { string tmp; cerr << "Enter dimensions (xxx yyy): "; getline(cin, tmp); if (tmp.size() == 0) break; stringstream input(tmp); unsigned int x = 0, y = 0; input >> x >> y; if (!x) { cerr << "Invalid input!" << endl; continue; } // Calculate y dimension if not given if (!y) { y = ((unsigned int)(x / aspect) + 1) / 2 * 2; } if (x % 8) cerr << "Warning: the X dimension is not multiple of 8 - this may cause trouble." << endl; if (y % 2) cerr << "Warning: the Y dimension is odd (not even) - this may cause trouble." << endl; printMode(conf, x, y); } } conf << "EndSection # " << name << endl; cerr << ">>> Printing XFree86/Xorg X11 config section" << endl; cout << conf.str(); cerr << "--- Done" << endl; return 0; } void printMode(ostringstream& conf, unsigned int x, unsigned int y) { // There is nothing wrong with your monitor // Do not attempt to adjust the picture // We are now controlling the transmission string mode; try { double diff = 0.0; // Try with doublescan first mode = makeMode(x, y, true, false, &diff); // If we didn't get exactly ideal with doublescan and are not forced to do DS if ((diff != 0.0) && (y >= doublescanBoundary)) { string mode2; double diff2; // Try without doublescan too mode2 = makeMode(x, y, false, false, &diff2); // Use whichever has smaller error if (fabs(diff2) < fabs(diff)) mode = mode2; } } catch (string) { // Failed while trying with doublescan? if (mode.size() == 0) { // Try without doublescan try { mode = makeMode(x, y, false, false, NULL); } catch (string) { // As the last resort, try with interlace try { mode = makeMode(x, y, false, true, NULL); } catch (string) {} } } } // Print info line on screen cerr << "Mode "; if (x < 1000) cerr << " "; cerr << "\"" << x << "x" << y << modenamePostfix << "\" "; if (y < 1000) cerr << " "; // Got any mode anyway? if (mode.size()) { size_t pos = mode.find('#'); cerr << mode.substr(pos + 1); if (mode[pos - 12] != ' ') cerr << ", " << mode.substr(pos - 12, 10); cerr << endl; conf << " " << mode << endl; } else { cerr << "Not possible (no suitable clocks found)" << endl; } // We can deluge you with a thousand modelines // Or expand one single image to crystal clarity... and beyond } string makeMode(unsigned int x, unsigned int y, bool doublescan, bool interlace, double *syncErrorFromIdeal) { if (interlace && doublescan) throw string("Mode cannot be both doublescan AND interlace"); // A multiplier for y dimension, used in various places double yFactor = (interlace ? 0.5 : 1.0) * (doublescan ? 2.0 : 1.0); // We control the horizontal ... unsigned int xFront, xSync, xBack; xFront = resMod(x * timeHFront); xSync = resMod(x * timeHSync); xBack = resMod(x * timeHBack); int xTotal = x + xFront + xSync + xBack; // ... and the vertical unsigned int yFront, ySync, yBack; // yFront = (unsigned int)round(y * timeVFront); yFront = 1; // In GTF it is always one // ySync = (unsigned int)round(y * timeVSync); ySync = 3; // In GTF it usually is 3, but there are known cases where it may be 2 (rounding error?) yBack = (unsigned int)round(y * timeVBack); int yTotal = y + yFront + ySync + yBack; // Now we know the pixel timings, and we only need to find out the best pixel clock, // or to find out that there is no suitable clock. // Must have a temporary variable, right? double tmp; // Find out the maximum clock we can use // Begin with maximum pixel clock double modeMaxClock = maxPixelClock; // Mode HSync too high? => reduce mode maximum pixel clock tmp = maxHSync * xTotal; if (modeMaxClock > tmp) modeMaxClock = tmp; // Mode VSync too high? => reduce mode maximum pixel clock tmp = maxVSync * xTotal * yTotal * yFactor; if (modeMaxClock > tmp) modeMaxClock = tmp; // Find out the minimum clock we can use // Begin with the monitor minimum HSync double modeMinClock = minHSync * xTotal; // Monitor minVSync too low? => increase mode minimum pixel clock tmp = minVSync * xTotal * yTotal * yFactor; if (modeMinClock < tmp) modeMinClock = tmp; // If minimum clock > maximum clock, the mode is impossible... if (modeMinClock > modeMaxClock) { throw string("No suitable clocks could be found"); } // Get the nearest clock to ideal speed, within the margins double idealClock = idealVSync * xTotal * yTotal * yFactor; double clock = idealClock; if (clock < modeMinClock) clock = modeMinClock; else if (clock > modeMaxClock) clock = modeMaxClock; // Write out how far away we are from ideal freq (if that parameter was not NULL) if (syncErrorFromIdeal) *syncErrorFromIdeal = idealClock - clock; // Time to write the modeline ostringstream conf; conf << "Modeline "; // Mode name if (x < 1000) conf << " "; conf << "\"" << x << "x" << y << modenamePostfix << "\" "; if (y < 1000) conf << " "; // Pixel clock conf << fixed << setprecision(2) << setw(6) << clock * 1e-6 // Horizontal and vertical pixel counts/timings << " " << setw(4) << x << " " << setw(4) << x + xFront << " " << setw(4) << x + xFront + xSync << " " << setw(4) << xTotal << " " << setw(4) << y << " " << setw(4) << y + yFront << " " << setw(4) << y + yFront + ySync << " " << setw(4) << yTotal; if (doublescan) conf << " doublescan"; else if (interlace) conf << " interlace "; else conf << " "; // The comment at end of line conf << " # " << setw(6) << 1e-6 * clock << " MHz, " << setw(6) << 1e-3 * clock / xTotal << " kHz, " << setw(6) << clock / (xTotal * yTotal * yFactor) << " Hz"; if (interlace) conf << " field rate"; double modeAspect = (double)x/y; if (fabs(modeAspect - aspect) > ASPECT_EPSILON) { conf << ", AR " << fixed << setprecision(3) << modeAspect << ":1"; } return conf.str(); // For the record: The Outer Limits stinks. Its intro just fits this program all too well ;) }