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