1 | /* |
2 | * Copyright 2007 Red Hat, Inc. |
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 | * on the rights to use, copy, modify, merge, publish, distribute, sub |
8 | * license, and/or sell copies of the Software, and to permit persons to whom |
9 | * the Software is furnished to do so, subject to the following conditions: |
10 | * |
11 | * The above copyright notice and this permission notice (including the next |
12 | * paragraph) shall be included in all copies or substantial portions of the |
13 | * Software. |
14 | * |
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL |
18 | * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER |
19 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
20 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
21 | */ |
22 | |
23 | /* Author: Soren Sandmann <sandmann@redhat.com> */ |
24 | #include "SDL_internal.h" |
25 | |
26 | #include "edid.h" |
27 | #include <stdlib.h> |
28 | #include <string.h> |
29 | #include <math.h> |
30 | #include <stdio.h> |
31 | |
32 | #define TRUE 1 |
33 | #define FALSE 0 |
34 | |
35 | static int |
36 | get_bit (int in, int bit) |
37 | { |
38 | return (in & (1 << bit)) >> bit; |
39 | } |
40 | |
41 | static int |
42 | get_bits (int in, int begin, int end) |
43 | { |
44 | int mask = (1 << (end - begin + 1)) - 1; |
45 | |
46 | return (in >> begin) & mask; |
47 | } |
48 | |
49 | static int |
50 | (const uchar *edid) |
51 | { |
52 | if (SDL_memcmp (edid, "\x00\xff\xff\xff\xff\xff\xff\x00" , 8) == 0) |
53 | return TRUE; |
54 | return FALSE; |
55 | } |
56 | |
57 | static int |
58 | decode_vendor_and_product_identification (const uchar *edid, MonitorInfo *info) |
59 | { |
60 | int is_model_year; |
61 | |
62 | /* Manufacturer Code */ |
63 | info->manufacturer_code[0] = get_bits (edid[0x08], 2, 6); |
64 | info->manufacturer_code[1] = get_bits (edid[0x08], 0, 1) << 3; |
65 | info->manufacturer_code[1] |= get_bits (edid[0x09], 5, 7); |
66 | info->manufacturer_code[2] = get_bits (edid[0x09], 0, 4); |
67 | info->manufacturer_code[3] = '\0'; |
68 | |
69 | info->manufacturer_code[0] += 'A' - 1; |
70 | info->manufacturer_code[1] += 'A' - 1; |
71 | info->manufacturer_code[2] += 'A' - 1; |
72 | |
73 | /* Product Code */ |
74 | info->product_code = edid[0x0b] << 8 | edid[0x0a]; |
75 | |
76 | /* Serial Number */ |
77 | info->serial_number = |
78 | edid[0x0c] | edid[0x0d] << 8 | edid[0x0e] << 16 | (Uint32)edid[0x0f] << 24; |
79 | |
80 | /* Week and Year */ |
81 | is_model_year = FALSE; |
82 | switch (edid[0x10]) |
83 | { |
84 | case 0x00: |
85 | info->production_week = -1; |
86 | break; |
87 | |
88 | case 0xff: |
89 | info->production_week = -1; |
90 | is_model_year = TRUE; |
91 | break; |
92 | |
93 | default: |
94 | info->production_week = edid[0x10]; |
95 | break; |
96 | } |
97 | |
98 | if (is_model_year) |
99 | { |
100 | info->production_year = -1; |
101 | info->model_year = 1990 + edid[0x11]; |
102 | } |
103 | else |
104 | { |
105 | info->production_year = 1990 + edid[0x11]; |
106 | info->model_year = -1; |
107 | } |
108 | |
109 | return TRUE; |
110 | } |
111 | |
112 | static int |
113 | decode_edid_version (const uchar *edid, MonitorInfo *info) |
114 | { |
115 | info->major_version = edid[0x12]; |
116 | info->minor_version = edid[0x13]; |
117 | |
118 | return TRUE; |
119 | } |
120 | |
121 | static int |
122 | decode_display_parameters (const uchar *edid, MonitorInfo *info) |
123 | { |
124 | /* Digital vs Analog */ |
125 | info->is_digital = get_bit (edid[0x14], 7); |
126 | |
127 | if (info->is_digital) |
128 | { |
129 | int bits; |
130 | |
131 | static const int bit_depth[8] = |
132 | { |
133 | -1, 6, 8, 10, 12, 14, 16, -1 |
134 | }; |
135 | |
136 | static const Interface interfaces[6] = |
137 | { |
138 | UNDEFINED, DVI, HDMI_A, HDMI_B, MDDI, DISPLAY_PORT |
139 | }; |
140 | |
141 | bits = get_bits (edid[0x14], 4, 6); |
142 | info->ad.digital.bits_per_primary = bit_depth[bits]; |
143 | |
144 | bits = get_bits (edid[0x14], 0, 3); |
145 | |
146 | if (bits <= 5) |
147 | info->ad.digital.interface = interfaces[bits]; |
148 | else |
149 | info->ad.digital.interface = UNDEFINED; |
150 | } |
151 | else |
152 | { |
153 | int bits = get_bits (edid[0x14], 5, 6); |
154 | |
155 | static const double levels[][3] = |
156 | { |
157 | { 0.7, 0.3, 1.0 }, |
158 | { 0.714, 0.286, 1.0 }, |
159 | { 1.0, 0.4, 1.4 }, |
160 | { 0.7, 0.0, 0.7 }, |
161 | }; |
162 | |
163 | info->ad.analog.video_signal_level = levels[bits][0]; |
164 | info->ad.analog.sync_signal_level = levels[bits][1]; |
165 | info->ad.analog.total_signal_level = levels[bits][2]; |
166 | |
167 | info->ad.analog.blank_to_black = get_bit (edid[0x14], 4); |
168 | |
169 | info->ad.analog.separate_hv_sync = get_bit (edid[0x14], 3); |
170 | info->ad.analog.composite_sync_on_h = get_bit (edid[0x14], 2); |
171 | info->ad.analog.composite_sync_on_green = get_bit (edid[0x14], 1); |
172 | |
173 | info->ad.analog.serration_on_vsync = get_bit (edid[0x14], 0); |
174 | } |
175 | |
176 | /* Screen Size / Aspect Ratio */ |
177 | if (edid[0x15] == 0 && edid[0x16] == 0) |
178 | { |
179 | info->width_mm = -1; |
180 | info->height_mm = -1; |
181 | info->aspect_ratio = -1.0; |
182 | } |
183 | else if (edid[0x16] == 0) |
184 | { |
185 | info->width_mm = -1; |
186 | info->height_mm = -1; |
187 | info->aspect_ratio = 100.0 / (edid[0x15] + 99); |
188 | } |
189 | else if (edid[0x15] == 0) |
190 | { |
191 | info->width_mm = -1; |
192 | info->height_mm = -1; |
193 | info->aspect_ratio = 100.0 / (edid[0x16] + 99); |
194 | info->aspect_ratio = 1/info->aspect_ratio; /* portrait */ |
195 | } |
196 | else |
197 | { |
198 | info->width_mm = 10 * edid[0x15]; |
199 | info->height_mm = 10 * edid[0x16]; |
200 | } |
201 | |
202 | /* Gamma */ |
203 | if (edid[0x17] == 0xFF) |
204 | info->gamma = -1.0; |
205 | else |
206 | info->gamma = (edid[0x17] + 100.0) / 100.0; |
207 | |
208 | /* Features */ |
209 | info->standby = get_bit (edid[0x18], 7); |
210 | info->suspend = get_bit (edid[0x18], 6); |
211 | info->active_off = get_bit (edid[0x18], 5); |
212 | |
213 | if (info->is_digital) |
214 | { |
215 | info->ad.digital.rgb444 = TRUE; |
216 | if (get_bit (edid[0x18], 3)) |
217 | info->ad.digital.ycrcb444 = 1; |
218 | if (get_bit (edid[0x18], 4)) |
219 | info->ad.digital.ycrcb422 = 1; |
220 | } |
221 | else |
222 | { |
223 | int bits = get_bits (edid[0x18], 3, 4); |
224 | ColorType color_type[4] = |
225 | { |
226 | MONOCHROME, RGB, OTHER_COLOR, UNDEFINED_COLOR |
227 | }; |
228 | |
229 | info->ad.analog.color_type = color_type[bits]; |
230 | } |
231 | |
232 | info->srgb_is_standard = get_bit (edid[0x18], 2); |
233 | |
234 | /* In 1.3 this is called "has preferred timing" */ |
235 | info->preferred_timing_includes_native = get_bit (edid[0x18], 1); |
236 | |
237 | /* FIXME: In 1.3 this indicates whether the monitor accepts GTF */ |
238 | info->continuous_frequency = get_bit (edid[0x18], 0); |
239 | return TRUE; |
240 | } |
241 | |
242 | static double |
243 | decode_fraction (int high, int low) |
244 | { |
245 | double result = 0.0; |
246 | int i; |
247 | |
248 | high = (high << 2) | low; |
249 | |
250 | for (i = 0; i < 10; ++i) |
251 | result += get_bit (high, i) * SDL_pow (2, i - 10); |
252 | |
253 | return result; |
254 | } |
255 | |
256 | static int |
257 | decode_color_characteristics (const uchar *edid, MonitorInfo *info) |
258 | { |
259 | info->red_x = decode_fraction (edid[0x1b], get_bits (edid[0x19], 6, 7)); |
260 | info->red_y = decode_fraction (edid[0x1c], get_bits (edid[0x19], 5, 4)); |
261 | info->green_x = decode_fraction (edid[0x1d], get_bits (edid[0x19], 2, 3)); |
262 | info->green_y = decode_fraction (edid[0x1e], get_bits (edid[0x19], 0, 1)); |
263 | info->blue_x = decode_fraction (edid[0x1f], get_bits (edid[0x1a], 6, 7)); |
264 | info->blue_y = decode_fraction (edid[0x20], get_bits (edid[0x1a], 4, 5)); |
265 | info->white_x = decode_fraction (edid[0x21], get_bits (edid[0x1a], 2, 3)); |
266 | info->white_y = decode_fraction (edid[0x22], get_bits (edid[0x1a], 0, 1)); |
267 | |
268 | return TRUE; |
269 | } |
270 | |
271 | static int |
272 | decode_established_timings (const uchar *edid, MonitorInfo *info) |
273 | { |
274 | static const Timing established[][8] = |
275 | { |
276 | { |
277 | { 800, 600, 60 }, |
278 | { 800, 600, 56 }, |
279 | { 640, 480, 75 }, |
280 | { 640, 480, 72 }, |
281 | { 640, 480, 67 }, |
282 | { 640, 480, 60 }, |
283 | { 720, 400, 88 }, |
284 | { 720, 400, 70 } |
285 | }, |
286 | { |
287 | { 1280, 1024, 75 }, |
288 | { 1024, 768, 75 }, |
289 | { 1024, 768, 70 }, |
290 | { 1024, 768, 60 }, |
291 | { 1024, 768, 87 }, |
292 | { 832, 624, 75 }, |
293 | { 800, 600, 75 }, |
294 | { 800, 600, 72 } |
295 | }, |
296 | { |
297 | { 0, 0, 0 }, |
298 | { 0, 0, 0 }, |
299 | { 0, 0, 0 }, |
300 | { 0, 0, 0 }, |
301 | { 0, 0, 0 }, |
302 | { 0, 0, 0 }, |
303 | { 0, 0, 0 }, |
304 | { 1152, 870, 75 } |
305 | }, |
306 | }; |
307 | |
308 | int i, j, idx; |
309 | |
310 | idx = 0; |
311 | for (i = 0; i < 3; ++i) |
312 | { |
313 | for (j = 0; j < 8; ++j) |
314 | { |
315 | int byte = edid[0x23 + i]; |
316 | |
317 | if (get_bit (byte, j) && established[i][j].frequency != 0) |
318 | info->established[idx++] = established[i][j]; |
319 | } |
320 | } |
321 | return TRUE; |
322 | } |
323 | |
324 | static int |
325 | decode_standard_timings (const uchar *edid, MonitorInfo *info) |
326 | { |
327 | int i; |
328 | |
329 | for (i = 0; i < 8; i++) |
330 | { |
331 | int first = edid[0x26 + 2 * i]; |
332 | int second = edid[0x27 + 2 * i]; |
333 | |
334 | if (first != 0x01 && second != 0x01) |
335 | { |
336 | int w = 8 * (first + 31); |
337 | int h = 0; |
338 | |
339 | switch (get_bits (second, 6, 7)) |
340 | { |
341 | case 0x00: h = (w / 16) * 10; break; |
342 | case 0x01: h = (w / 4) * 3; break; |
343 | case 0x02: h = (w / 5) * 4; break; |
344 | case 0x03: h = (w / 16) * 9; break; |
345 | } |
346 | |
347 | info->standard[i].width = w; |
348 | info->standard[i].height = h; |
349 | info->standard[i].frequency = get_bits (second, 0, 5) + 60; |
350 | } |
351 | } |
352 | |
353 | return TRUE; |
354 | } |
355 | |
356 | static void |
357 | decode_lf_string (const uchar *s, int n_chars, char *result) |
358 | { |
359 | int i; |
360 | for (i = 0; i < n_chars; ++i) |
361 | { |
362 | if (s[i] == 0x0a) |
363 | { |
364 | *result++ = '\0'; |
365 | break; |
366 | } |
367 | else if (s[i] == 0x00) |
368 | { |
369 | /* Convert embedded 0's to spaces */ |
370 | *result++ = ' '; |
371 | } |
372 | else |
373 | { |
374 | *result++ = s[i]; |
375 | } |
376 | } |
377 | } |
378 | |
379 | static void |
380 | decode_display_descriptor (const uchar *desc, |
381 | MonitorInfo *info) |
382 | { |
383 | switch (desc[0x03]) |
384 | { |
385 | case 0xFC: |
386 | decode_lf_string (desc + 5, 13, info->dsc_product_name); |
387 | break; |
388 | case 0xFF: |
389 | decode_lf_string (desc + 5, 13, info->dsc_serial_number); |
390 | break; |
391 | case 0xFE: |
392 | decode_lf_string (desc + 5, 13, info->dsc_string); |
393 | break; |
394 | case 0xFD: |
395 | /* Range Limits */ |
396 | break; |
397 | case 0xFB: |
398 | /* Color Point */ |
399 | break; |
400 | case 0xFA: |
401 | /* Timing Identifications */ |
402 | break; |
403 | case 0xF9: |
404 | /* Color Management */ |
405 | break; |
406 | case 0xF8: |
407 | /* Timing Codes */ |
408 | break; |
409 | case 0xF7: |
410 | /* Established Timings */ |
411 | break; |
412 | case 0x10: |
413 | break; |
414 | } |
415 | } |
416 | |
417 | static void |
418 | decode_detailed_timing (const uchar *timing, |
419 | DetailedTiming *detailed) |
420 | { |
421 | int bits; |
422 | StereoType stereo[] = |
423 | { |
424 | NO_STEREO, NO_STEREO, FIELD_RIGHT, FIELD_LEFT, |
425 | TWO_WAY_RIGHT_ON_EVEN, TWO_WAY_LEFT_ON_EVEN, |
426 | FOUR_WAY_INTERLEAVED, SIDE_BY_SIDE |
427 | }; |
428 | |
429 | detailed->pixel_clock = (timing[0x00] | timing[0x01] << 8) * 10000; |
430 | detailed->h_addr = timing[0x02] | ((timing[0x04] & 0xf0) << 4); |
431 | detailed->h_blank = timing[0x03] | ((timing[0x04] & 0x0f) << 8); |
432 | detailed->v_addr = timing[0x05] | ((timing[0x07] & 0xf0) << 4); |
433 | detailed->v_blank = timing[0x06] | ((timing[0x07] & 0x0f) << 8); |
434 | detailed->h_front_porch = timing[0x08] | get_bits (timing[0x0b], 6, 7) << 8; |
435 | detailed->h_sync = timing[0x09] | get_bits (timing[0x0b], 4, 5) << 8; |
436 | detailed->v_front_porch = |
437 | get_bits (timing[0x0a], 4, 7) | get_bits (timing[0x0b], 2, 3) << 4; |
438 | detailed->v_sync = |
439 | get_bits (timing[0x0a], 0, 3) | get_bits (timing[0x0b], 0, 1) << 4; |
440 | detailed->width_mm = timing[0x0c] | get_bits (timing[0x0e], 4, 7) << 8; |
441 | detailed->height_mm = timing[0x0d] | get_bits (timing[0x0e], 0, 3) << 8; |
442 | detailed->right_border = timing[0x0f]; |
443 | detailed->top_border = timing[0x10]; |
444 | |
445 | detailed->interlaced = get_bit (timing[0x11], 7); |
446 | |
447 | /* Stereo */ |
448 | bits = get_bits (timing[0x11], 5, 6) << 1 | get_bit (timing[0x11], 0); |
449 | detailed->stereo = stereo[bits]; |
450 | |
451 | /* Sync */ |
452 | bits = timing[0x11]; |
453 | |
454 | detailed->digital_sync = get_bit (bits, 4); |
455 | if (detailed->digital_sync) |
456 | { |
457 | detailed->ad.digital.composite = !get_bit (bits, 3); |
458 | |
459 | if (detailed->ad.digital.composite) |
460 | { |
461 | detailed->ad.digital.serrations = get_bit (bits, 2); |
462 | detailed->ad.digital.negative_vsync = FALSE; |
463 | } |
464 | else |
465 | { |
466 | detailed->ad.digital.serrations = FALSE; |
467 | detailed->ad.digital.negative_vsync = !get_bit (bits, 2); |
468 | } |
469 | |
470 | detailed->ad.digital.negative_hsync = !get_bit (bits, 0); |
471 | } |
472 | else |
473 | { |
474 | detailed->ad.analog.bipolar = get_bit (bits, 3); |
475 | detailed->ad.analog.serrations = get_bit (bits, 2); |
476 | detailed->ad.analog.sync_on_green = !get_bit (bits, 1); |
477 | } |
478 | } |
479 | |
480 | static int |
481 | decode_descriptors (const uchar *edid, MonitorInfo *info) |
482 | { |
483 | int i; |
484 | int timing_idx; |
485 | |
486 | timing_idx = 0; |
487 | |
488 | for (i = 0; i < 4; ++i) |
489 | { |
490 | int index = 0x36 + i * 18; |
491 | |
492 | if (edid[index + 0] == 0x00 && edid[index + 1] == 0x00) |
493 | { |
494 | decode_display_descriptor (edid + index, info); |
495 | } |
496 | else |
497 | { |
498 | decode_detailed_timing ( |
499 | edid + index, &(info->detailed_timings[timing_idx++])); |
500 | } |
501 | } |
502 | |
503 | info->n_detailed_timings = timing_idx; |
504 | |
505 | return TRUE; |
506 | } |
507 | |
508 | static void |
509 | decode_check_sum (const uchar *edid, |
510 | MonitorInfo *info) |
511 | { |
512 | int i; |
513 | uchar check = 0; |
514 | |
515 | for (i = 0; i < 128; ++i) |
516 | check += edid[i]; |
517 | |
518 | info->checksum = check; |
519 | } |
520 | |
521 | MonitorInfo * |
522 | decode_edid (const uchar *edid) |
523 | { |
524 | MonitorInfo *info = SDL_calloc (1, sizeof (MonitorInfo)); |
525 | |
526 | decode_check_sum (edid, info); |
527 | |
528 | if (!decode_header (edid) || |
529 | !decode_vendor_and_product_identification (edid, info) || |
530 | !decode_edid_version (edid, info) || |
531 | !decode_display_parameters (edid, info) || |
532 | !decode_color_characteristics (edid, info) || |
533 | !decode_established_timings (edid, info) || |
534 | !decode_standard_timings (edid, info) || |
535 | !decode_descriptors (edid, info)) { |
536 | SDL_free(info); |
537 | return NULL; |
538 | } |
539 | |
540 | return info; |
541 | } |
542 | |
543 | static const char * |
544 | yesno (int v) |
545 | { |
546 | return v? "yes" : "no" ; |
547 | } |
548 | |
549 | void |
550 | dump_monitor_info (MonitorInfo *info) |
551 | { |
552 | int i; |
553 | |
554 | printf ("Checksum: %d (%s)\n" , |
555 | info->checksum, info->checksum? "incorrect" : "correct" ); |
556 | printf ("Manufacturer Code: %s\n" , info->manufacturer_code); |
557 | printf ("Product Code: 0x%x\n" , info->product_code); |
558 | printf ("Serial Number: %u\n" , info->serial_number); |
559 | |
560 | if (info->production_week != -1) |
561 | printf ("Production Week: %d\n" , info->production_week); |
562 | else |
563 | printf ("Production Week: unspecified\n" ); |
564 | |
565 | if (info->production_year != -1) |
566 | printf ("Production Year: %d\n" , info->production_year); |
567 | else |
568 | printf ("Production Year: unspecified\n" ); |
569 | |
570 | if (info->model_year != -1) |
571 | printf ("Model Year: %d\n" , info->model_year); |
572 | else |
573 | printf ("Model Year: unspecified\n" ); |
574 | |
575 | printf ("EDID revision: %d.%d\n" , info->major_version, info->minor_version); |
576 | |
577 | printf ("Display is %s\n" , info->is_digital? "digital" : "analog" ); |
578 | if (info->is_digital) |
579 | { |
580 | const char *interface; |
581 | if (info->ad.digital.bits_per_primary != -1) |
582 | printf ("Bits Per Primary: %d\n" , info->ad.digital.bits_per_primary); |
583 | else |
584 | printf ("Bits Per Primary: undefined\n" ); |
585 | |
586 | switch (info->ad.digital.interface) |
587 | { |
588 | case DVI: interface = "DVI" ; break; |
589 | case HDMI_A: interface = "HDMI-a" ; break; |
590 | case HDMI_B: interface = "HDMI-b" ; break; |
591 | case MDDI: interface = "MDDI" ; break; |
592 | case DISPLAY_PORT: interface = "DisplayPort" ; break; |
593 | case UNDEFINED: interface = "undefined" ; break; |
594 | default: interface = "unknown" ; break; |
595 | } |
596 | printf ("Interface: %s\n" , interface); |
597 | |
598 | printf ("RGB 4:4:4: %s\n" , yesno (info->ad.digital.rgb444)); |
599 | printf ("YCrCb 4:4:4: %s\n" , yesno (info->ad.digital.ycrcb444)); |
600 | printf ("YCrCb 4:2:2: %s\n" , yesno (info->ad.digital.ycrcb422)); |
601 | } |
602 | else |
603 | { |
604 | const char *s; |
605 | printf ("Video Signal Level: %f\n" , info->ad.analog.video_signal_level); |
606 | printf ("Sync Signal Level: %f\n" , info->ad.analog.sync_signal_level); |
607 | printf ("Total Signal Level: %f\n" , info->ad.analog.total_signal_level); |
608 | |
609 | printf ("Blank to Black: %s\n" , |
610 | yesno (info->ad.analog.blank_to_black)); |
611 | printf ("Separate HV Sync: %s\n" , |
612 | yesno (info->ad.analog.separate_hv_sync)); |
613 | printf ("Composite Sync on H: %s\n" , |
614 | yesno (info->ad.analog.composite_sync_on_h)); |
615 | printf ("Serration on VSync: %s\n" , |
616 | yesno (info->ad.analog.serration_on_vsync)); |
617 | |
618 | switch (info->ad.analog.color_type) |
619 | { |
620 | case UNDEFINED_COLOR: s = "undefined" ; break; |
621 | case MONOCHROME: s = "monochrome" ; break; |
622 | case RGB: s = "rgb" ; break; |
623 | case OTHER_COLOR: s = "other color" ; break; |
624 | default: s = "unknown" ; break; |
625 | } |
626 | |
627 | printf ("Color: %s\n" , s); |
628 | } |
629 | |
630 | if (info->width_mm == -1) |
631 | printf ("Width: undefined\n" ); |
632 | else |
633 | printf ("Width: %d mm\n" , info->width_mm); |
634 | |
635 | if (info->height_mm == -1) |
636 | printf ("Height: undefined\n" ); |
637 | else |
638 | printf ("Height: %d mm\n" , info->height_mm); |
639 | |
640 | if (info->aspect_ratio > 0) |
641 | printf ("Aspect Ratio: %f\n" , info->aspect_ratio); |
642 | else |
643 | printf ("Aspect Ratio: undefined\n" ); |
644 | |
645 | if (info->gamma >= 0) |
646 | printf ("Gamma: %f\n" , info->gamma); |
647 | else |
648 | printf ("Gamma: undefined\n" ); |
649 | |
650 | printf ("Standby: %s\n" , yesno (info->standby)); |
651 | printf ("Suspend: %s\n" , yesno (info->suspend)); |
652 | printf ("Active Off: %s\n" , yesno (info->active_off)); |
653 | |
654 | printf ("SRGB is Standard: %s\n" , yesno (info->srgb_is_standard)); |
655 | printf ("Preferred Timing Includes Native: %s\n" , |
656 | yesno (info->preferred_timing_includes_native)); |
657 | printf ("Continuous Frequency: %s\n" , yesno (info->continuous_frequency)); |
658 | |
659 | printf ("Red X: %f\n" , info->red_x); |
660 | printf ("Red Y: %f\n" , info->red_y); |
661 | printf ("Green X: %f\n" , info->green_x); |
662 | printf ("Green Y: %f\n" , info->green_y); |
663 | printf ("Blue X: %f\n" , info->blue_x); |
664 | printf ("Blue Y: %f\n" , info->blue_y); |
665 | printf ("White X: %f\n" , info->white_x); |
666 | printf ("White Y: %f\n" , info->white_y); |
667 | |
668 | printf ("Established Timings:\n" ); |
669 | |
670 | for (i = 0; i < 24; ++i) |
671 | { |
672 | Timing *timing = &(info->established[i]); |
673 | |
674 | if (timing->frequency == 0) |
675 | break; |
676 | |
677 | printf (" %d x %d @ %d Hz\n" , |
678 | timing->width, timing->height, timing->frequency); |
679 | |
680 | } |
681 | |
682 | printf ("Standard Timings:\n" ); |
683 | for (i = 0; i < 8; ++i) |
684 | { |
685 | Timing *timing = &(info->standard[i]); |
686 | |
687 | if (timing->frequency == 0) |
688 | break; |
689 | |
690 | printf (" %d x %d @ %d Hz\n" , |
691 | timing->width, timing->height, timing->frequency); |
692 | } |
693 | |
694 | for (i = 0; i < info->n_detailed_timings; ++i) |
695 | { |
696 | DetailedTiming *timing = &(info->detailed_timings[i]); |
697 | const char *s; |
698 | |
699 | printf ("Timing%s: \n" , |
700 | (i == 0 && info->preferred_timing_includes_native)? |
701 | " (Preferred)" : "" ); |
702 | printf (" Pixel Clock: %d\n" , timing->pixel_clock); |
703 | printf (" H Addressable: %d\n" , timing->h_addr); |
704 | printf (" H Blank: %d\n" , timing->h_blank); |
705 | printf (" H Front Porch: %d\n" , timing->h_front_porch); |
706 | printf (" H Sync: %d\n" , timing->h_sync); |
707 | printf (" V Addressable: %d\n" , timing->v_addr); |
708 | printf (" V Blank: %d\n" , timing->v_blank); |
709 | printf (" V Front Porch: %d\n" , timing->v_front_porch); |
710 | printf (" V Sync: %d\n" , timing->v_sync); |
711 | printf (" Width: %d mm\n" , timing->width_mm); |
712 | printf (" Height: %d mm\n" , timing->height_mm); |
713 | printf (" Right Border: %d\n" , timing->right_border); |
714 | printf (" Top Border: %d\n" , timing->top_border); |
715 | switch (timing->stereo) |
716 | { |
717 | default: |
718 | case NO_STEREO: s = "No Stereo" ; break; |
719 | case FIELD_RIGHT: s = "Field Sequential, Right on Sync" ; break; |
720 | case FIELD_LEFT: s = "Field Sequential, Left on Sync" ; break; |
721 | case TWO_WAY_RIGHT_ON_EVEN: s = "Two-way, Right on Even" ; break; |
722 | case TWO_WAY_LEFT_ON_EVEN: s = "Two-way, Left on Even" ; break; |
723 | case FOUR_WAY_INTERLEAVED: s = "Four-way Interleaved" ; break; |
724 | case SIDE_BY_SIDE: s = "Side-by-Side" ; break; |
725 | } |
726 | printf (" Stereo: %s\n" , s); |
727 | |
728 | if (timing->digital_sync) |
729 | { |
730 | printf (" Digital Sync:\n" ); |
731 | printf (" composite: %s\n" , yesno (timing->ad.digital.composite)); |
732 | printf (" serrations: %s\n" , yesno (timing->ad.digital.serrations)); |
733 | printf (" negative vsync: %s\n" , |
734 | yesno (timing->ad.digital.negative_vsync)); |
735 | printf (" negative hsync: %s\n" , |
736 | yesno (timing->ad.digital.negative_hsync)); |
737 | } |
738 | else |
739 | { |
740 | printf (" Analog Sync:\n" ); |
741 | printf (" bipolar: %s\n" , yesno (timing->ad.analog.bipolar)); |
742 | printf (" serrations: %s\n" , yesno (timing->ad.analog.serrations)); |
743 | printf (" sync on green: %s\n" , yesno ( |
744 | timing->ad.analog.sync_on_green)); |
745 | } |
746 | } |
747 | |
748 | printf ("Detailed Product information:\n" ); |
749 | printf (" Product Name: %s\n" , info->dsc_product_name); |
750 | printf (" Serial Number: %s\n" , info->dsc_serial_number); |
751 | printf (" Unspecified String: %s\n" , info->dsc_string); |
752 | } |
753 | |
754 | |