1 /*
2  * Copyright 2005-2006 Luc Verhaegen.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20  * OTHER DEALINGS IN THE SOFTWARE.
21  *
22  */
23 
24 /* Standalone VESA CVT standard timing modelines generator. */
25 
26 #include <string.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <math.h>
30 
31 #include <libxcvt/libxcvt.h>
32 
33 bool
cvt_is_standard(int hdisplay,int vdisplay,float vrefresh,bool reduced,bool verbose)34 cvt_is_standard(int hdisplay, int vdisplay, float vrefresh, bool reduced, bool verbose)
35 {
36     bool is_cvt = true;
37 
38     if ((!(vdisplay % 3) && ((vdisplay * 4 / 3) == hdisplay)) ||
39         (!(vdisplay % 9) && ((vdisplay * 16 / 9) == hdisplay)) ||
40         (!(vdisplay % 10) && ((vdisplay * 16 / 10) == hdisplay)) ||
41         (!(vdisplay % 4) && ((vdisplay * 5 / 4) == hdisplay)) ||
42         (!(vdisplay % 9) && ((vdisplay * 15 / 9) == hdisplay)));
43     else {
44         if (verbose)
45             fprintf(stderr, "Warning: Aspect Ratio is not CVT standard.\n");
46         is_cvt = false;
47     }
48 
49     if ((vrefresh != 50.0) && (vrefresh != 60.0) &&
50         (vrefresh != 75.0) && (vrefresh != 85.0)) {
51         if (verbose)
52             fprintf(stderr, "Warning: Refresh Rate %.2f is not CVT standard "
53                     "(50, 60, 75 or 85Hz).\n", vrefresh);
54         is_cvt = false;
55     }
56 
57     return is_cvt;
58 }
59 /*
60  * I'm not documenting --interlaced for obvious reasons, even though I did
61  * implement it. I also can't deny having looked at gtf here.
62  */
63 static void
print_usage(char * Name)64 print_usage(char *Name)
65 {
66     fprintf(stderr, "\n");
67     fprintf(stderr, "usage: %s [-v|--verbose] [-r|--reduced] X Y [refresh]\n",
68             Name);
69     fprintf(stderr, "\n");
70     fprintf(stderr, " -v|--verbose : Warn about CVT standard adherence.\n");
71     fprintf(stderr, " -r|--reduced : Create a mode with reduced blanking "
72             "(default: normal blanking).\n");
73     fprintf(stderr, "            X : Desired horizontal resolution "
74             "(multiple of 8, required).\n");
75     fprintf(stderr,
76             "            Y : Desired vertical resolution (required).\n");
77     fprintf(stderr,
78             "      refresh : Desired refresh rate (default: 60.0Hz).\n");
79     fprintf(stderr, "\n");
80 
81     fprintf(stderr, "Calculates VESA CVT (Coordinated Video Timing) modelines"
82             " for use with X.\n");
83 }
84 
85 /*
86  *
87  */
88 static void
print_comment(struct libxcvt_mode_info * mode_info,bool is_cvt,bool reduced)89 print_comment(struct libxcvt_mode_info *mode_info, bool is_cvt, bool reduced)
90 {
91     printf("# %dx%d %.2f Hz ", mode_info->hdisplay, mode_info->vdisplay, mode_info->vrefresh);
92 
93     if (is_cvt) {
94         printf("(CVT %.2fM",
95                ((float) mode_info->hdisplay * mode_info->vdisplay) / 1000000.0);
96 
97         if (!(mode_info->vdisplay % 3) &&
98             ((mode_info->vdisplay * 4 / 3) == mode_info->hdisplay))
99             printf("3");
100         else if (!(mode_info->vdisplay % 9) &&
101                  ((mode_info->vdisplay * 16 / 9) == mode_info->hdisplay))
102             printf("9");
103         else if (!(mode_info->vdisplay % 10) &&
104                  ((mode_info->vdisplay * 16 / 10) == mode_info->hdisplay))
105             printf("A");
106         else if (!(mode_info->vdisplay % 4) &&
107                  ((mode_info->vdisplay * 5 / 4) == mode_info->hdisplay))
108             printf("4");
109         else if (!(mode_info->vdisplay % 9) &&
110                  ((mode_info->vdisplay * 15 / 9) == mode_info->hdisplay))
111             printf("9");
112 
113         if (reduced)
114             printf("-R");
115 
116         printf(") ");
117     }
118     else
119         printf("(CVT) ");
120 
121     printf("hsync: %.2f kHz; ", mode_info->hsync);
122     printf("pclk: %.2f MHz", ((float) mode_info->dot_clock) / 1000.0);
123 
124     printf("\n");
125 }
126 
127 /*
128  * Originally grabbed from xf86Mode.c.
129  *
130  * Ignoring the actual mode_info->name, as the user will want something solid
131  * to grab hold of.
132  */
133 static void
print_mode_line(struct libxcvt_mode_info * mode_info,int hdisplay,int vdisplay,float vrefresh,bool reduced)134 print_mode_line(struct libxcvt_mode_info *mode_info, int hdisplay, int vdisplay, float vrefresh,
135               bool reduced)
136 {
137     if (reduced)
138         printf("Modeline \"%dx%dR\"  ", hdisplay, vdisplay);
139     else
140         printf("Modeline \"%dx%d_%.2f\"  ", hdisplay, vdisplay, vrefresh);
141 
142     printf("%6.2f  %i %i %i %i  %i %i %i %i", mode_info->dot_clock / 1000.,
143            mode_info->hdisplay, mode_info->hsync_start, mode_info->hsync_end, mode_info->htotal,
144            mode_info->vdisplay, mode_info->vsync_start, mode_info->vsync_end, mode_info->vtotal);
145 
146     if (mode_info->mode_flags & LIBXCVT_MODE_FLAG_INTERLACE)
147         printf(" interlace");
148     if (mode_info->mode_flags & LIBXCVT_MODE_FLAG_HSYNC_POSITIVE)
149         printf(" +hsync");
150     if (mode_info->mode_flags & LIBXCVT_MODE_FLAG_HSYNC_NEGATIVE)
151         printf(" -hsync");
152     if (mode_info->mode_flags & LIBXCVT_MODE_FLAG_VSYNC_POSITIVE)
153         printf(" +vsync");
154     if (mode_info->mode_flags & LIBXCVT_MODE_FLAG_VSYNC_NEGATIVE)
155         printf(" -vsync");
156 
157     printf("\n");
158 }
159 
160 /*
161  *
162  */
163 int
main(int argc,char * argv[])164 main(int argc, char *argv[])
165 {
166     struct libxcvt_mode_info *mode_info;
167     int hdisplay = 0, vdisplay = 0;
168     float vrefresh = 0.0;
169     bool reduced = false, verbose = false, is_cvt;
170     bool interlaced = false;
171     int n;
172 
173     if ((argc < 3) || (argc > 7)) {
174         print_usage(argv[0]);
175         return 1;
176     }
177 
178     /* This doesn't filter out bad flags properly. Bad flags get passed down
179      * to atoi/atof, which then return 0, so that these variables can get
180      * filled next time round. So this is just a cosmetic problem.
181      */
182     for (n = 1; n < argc; n++) {
183         if (!strcmp(argv[n], "-r") || !strcmp(argv[n], "--reduced"))
184             reduced = true;
185         else if (!strcmp(argv[n], "-i") || !strcmp(argv[n], "--interlaced"))
186             interlaced = true;
187         else if (!strcmp(argv[n], "-v") || !strcmp(argv[n], "--verbose"))
188             verbose = true;
189         else if (!strcmp(argv[n], "-h") || !strcmp(argv[n], "--help")) {
190             print_usage(argv[0]);
191             return 0;
192         }
193         else if (!hdisplay) {
194             hdisplay = atoi(argv[n]);
195             if (!hdisplay) {
196                 print_usage(argv[0]);
197                 return 1;
198             }
199         }
200         else if (!vdisplay) {
201             vdisplay = atoi(argv[n]);
202             if (!vdisplay) {
203                 print_usage(argv[0]);
204                 return 1;
205             }
206         }
207         else if (!vrefresh) {
208             vrefresh = atof(argv[n]);
209             if (!vrefresh) {
210                 print_usage(argv[0]);
211                 return 1;
212             }
213         }
214         else {
215             print_usage(argv[0]);
216             return 1;
217         }
218     }
219 
220     if (!hdisplay || !vdisplay) {
221         print_usage(argv[0]);
222         return 0;
223     }
224 
225     /* Default to 60.0Hz */
226     if (!vrefresh)
227         vrefresh = 60.0;
228 
229     /* Horizontal timing is always a multiple of 8: round up. */
230     if (hdisplay & 0x07) {
231         hdisplay &= ~0x07;
232         hdisplay += 8;
233     }
234 
235     if (reduced) {
236         if ((vrefresh / 60.0) != floor(vrefresh / 60.0)) {
237             fprintf(stderr,
238                     "\nERROR: Multiple of 60Hz refresh rate required for "
239                     " reduced blanking.\n");
240             print_usage(argv[0]);
241             return 0;
242         }
243     }
244 
245     mode_info = libxcvt_gen_mode_info(hdisplay, vdisplay, vrefresh, reduced, interlaced);
246     if (!mode_info) {
247         fprintf(stderr, "Out of memory!\n");
248             return 0;
249     }
250 
251     is_cvt = cvt_is_standard(hdisplay, vdisplay, vrefresh, reduced, verbose);
252     print_comment(mode_info, is_cvt, reduced);
253     print_mode_line(mode_info, hdisplay, vdisplay, vrefresh, reduced);
254     free(mode_info);
255 
256     return 0;
257 }
258