1 package edid
2
3 import (
4 "bytes"
5 "encoding/binary"
6 "fmt"
7 "os"
8 "strconv"
9 "strings"
10 "text/tabwriter"
11 )
12
13 var TimingBitMap1 = []string{
14 "720×400 @ 70 Hz",
15 "720×400 @ 88 Hz",
16 "640×480 @ 60 Hz",
17 "640×480 @ 67 Hz",
18 "640×480 @ 72 Hz",
19 "640×480 @ 75 Hz",
20 "800×600 @ 56 Hz",
21 "800×600 @ 60 Hz"}
22
23 var TimingBitMap2 = []string{
24 "800×600 @ 72 Hz",
25 "800×600 @ 75 Hz",
26 "832×624 @ 75 Hz",
27 "1024×768 @ 87 Hz, interlaced (1024×768i)",
28 "1024×768 @ 60 Hz",
29 "1024×768 @ 72 Hz",
30 "1024×768 @ 75 Hz",
31 "1280×1024 @ 75 Hz"}
32
33 type MonitorRangeLimitDescriptor struct {
34 MinimumVerticalFieldRate byte
35 MaximumVerticalFieldRate byte
36 MinimumHorizontalLineRate byte
37 MaximumHorizontalLineRate byte
38 MaximumPixelClockRate byte
39 }
40
41 type DetailedTimingDescriptor struct {
42 PixelClock uint32
43 HorizontalActive uint16
44 HorizontalBlanking uint16
45 VerticalActive uint16
46 VerticalBlanking uint16
47 HorizontalSyncOffset uint16
48 HorizontalSyncPulseWidth uint16
49 VerticalSyncOffset uint16
50 VerticalSyncPulseWidth uint16
51 HorizontalImageSize uint16
52 VerticalImageSize uint16
53 HorizontalBorder byte
54 VerticalBorder byte
55 Interlaced bool
56 Stereo string
57 SyncType byte
58 HorizontalSyncPolarity bool
59 VerticalSyncPolarity bool
60 }
61
62 type Edid struct {
63 CheckSum bool
64 Header [8]byte
65 ManufacturerId string
66 ProductCode uint16
67 SerialNumber uint32
68 WeekOfManufacture byte
69 YearOfManufacture uint
70 EdidVersion byte
71 EdidRevision byte
72
73 DigitalInput bool
74 VESADFPCompatible bool
75
76 CompositeSyncSupported bool
77 SyncOnGreenSupported bool
78
79 MaximumHorizontalImageSize byte
80 MaximumVerticalImageSize byte
81 DisplayGamma float32
82
83 DPMSStandbySupported bool
84 DPMSSuspendSupported bool
85 DPMSActiveOffSupported bool
86 DisplayType string
87
88 RedX float64
89 RedY float64
90 GreenX float64
91 GreenY float64
92 BlueX float64
93 BlueY float64
94 WhiteX float64
95 WhiteY float64
96
97 TimingBitMap1 byte
98 TimingBitMap2 byte
99 TimingBitMap3 byte
100
101 StandardTimingInformation []string
102
103 DetailedTimingDescriptors []DetailedTimingDescriptor
104
105 MonitorName string
106 MonitorSerialNumber string
107
108 MonitorRangeLimitDescriptors []MonitorRangeLimitDescriptor
109
110 NumberOfExtensions byte
111 }
112
113 func NewEdid(edidBytes []byte) (*Edid, error) {
114 edid := new(Edid)
115 for i := 0; i < 8; i++ {
116 edid.Header[i] = edidBytes[i]
117 }
118
119
120 var checkSum byte
121 checkSum = 0
122 for i := 0; i < 128; i++ {
123 checkSum += edidBytes[i]
124 }
125 if checkSum == 0 {
126 edid.CheckSum = true
127 } else {
128 edid.CheckSum = false
129 }
130
131 manufacturerId := edidBytes[8:10]
132 edid.ManufacturerId = fmt.Sprintf("%c%c%c", (manufacturerId[0]>>2&0x1f)+'A'-1, (((manufacturerId[0]&0x3)<<3)|((manufacturerId[1]&0xe0)>>5))+'A'-1, (manufacturerId[1]&0x1f)+'A'-1)
133
134 p := bytes.NewBuffer(edidBytes)
135
136 p.Next(10)
137
138 var productCode uint16
139 binary.Read(p, binary.LittleEndian, &productCode)
140 edid.ProductCode = productCode
141
142 var serialNumber uint32
143 binary.Read(p, binary.LittleEndian, &serialNumber)
144 edid.SerialNumber = serialNumber
145
146 var weekOfManufacture byte
147 binary.Read(p, binary.LittleEndian, &weekOfManufacture)
148 edid.WeekOfManufacture = weekOfManufacture
149
150 var yearOfManufacture byte
151 binary.Read(p, binary.LittleEndian, &yearOfManufacture)
152 edid.YearOfManufacture = uint(yearOfManufacture) + 1990
153
154 var edidVersion byte
155 binary.Read(p, binary.LittleEndian, &edidVersion)
156 edid.EdidVersion = edidVersion
157
158 var edidRevision byte
159 binary.Read(p, binary.LittleEndian, &edidRevision)
160 edid.EdidRevision = edidRevision
161
162 var displayParam byte
163 binary.Read(p, binary.LittleEndian, &displayParam)
164 if displayParam&0x80 > 0 {
165 edid.DigitalInput = true
166 if displayParam&0x1 > 0 {
167 edid.VESADFPCompatible = true
168 }
169 } else {
170 edid.DigitalInput = false
171 if displayParam&0x4 > 0 {
172 edid.CompositeSyncSupported = true
173 }
174 if displayParam&0x2 > 0 {
175 edid.SyncOnGreenSupported = true
176 }
177
178 }
179
180 var horizontalDisplaySize byte
181 binary.Read(p, binary.LittleEndian, &horizontalDisplaySize)
182 edid.MaximumHorizontalImageSize = horizontalDisplaySize
183
184 var verticalDisplaySize byte
185 binary.Read(p, binary.LittleEndian, &verticalDisplaySize)
186 edid.MaximumVerticalImageSize = verticalDisplaySize
187
188 var displayGamma byte
189 binary.Read(p, binary.LittleEndian, &displayGamma)
190 edid.DisplayGamma = (float32(displayGamma) / 100) + 1
191
192 var featureBitmap byte
193 binary.Read(p, binary.LittleEndian, &featureBitmap)
194 if featureBitmap&0x80 > 0 {
195 edid.DPMSStandbySupported = true
196 }
197 if featureBitmap&0x40 > 0 {
198 edid.DPMSSuspendSupported = true
199 }
200 if featureBitmap&0x20 > 0 {
201 edid.DPMSActiveOffSupported = true
202 }
203
204 dispType := (featureBitmap & 0x18) >> 3
205
206 if edid.DigitalInput {
207 switch dispType {
208 case 0:
209 edid.DisplayType = "RGB 4:4:4"
210 case 1:
211 edid.DisplayType = "RGB 4:4:4 + YCrCb 4:4:4"
212 case 2:
213 edid.DisplayType = "RGB 4:4:4 + YCrCb 4:2:2"
214 case 3:
215 edid.DisplayType = "RGB 4:4:4 + YCrCb 4:4:4 + YCrCb 4:2:2"
216 }
217 } else {
218 switch dispType {
219 case 0:
220 edid.DisplayType = "Monochrome/Grayscale"
221 case 1:
222 edid.DisplayType = "RGB color"
223 case 2:
224 edid.DisplayType = "Non-RGB color"
225 case 3:
226 edid.DisplayType = "Undefined"
227 }
228 }
229
230
231 var redGreenLSB byte
232 binary.Read(p, binary.LittleEndian, &redGreenLSB)
233
234
235 var blueWhiteLSB byte
236 binary.Read(p, binary.LittleEndian, &blueWhiteLSB)
237
238
239 var redXMSB byte
240 binary.Read(p, binary.LittleEndian, &redXMSB)
241
242
243 var redYMSB byte
244 binary.Read(p, binary.LittleEndian, &redYMSB)
245
246
247 var greenXMSB byte
248 binary.Read(p, binary.LittleEndian, &greenXMSB)
249
250
251 var greenYMSB byte
252 binary.Read(p, binary.LittleEndian, &greenYMSB)
253
254
255 var blueXMSB byte
256 binary.Read(p, binary.LittleEndian, &blueXMSB)
257
258
259 var blueYMSB byte
260 binary.Read(p, binary.LittleEndian, &blueYMSB)
261
262
263 var whiteXMSB byte
264 binary.Read(p, binary.LittleEndian, &whiteXMSB)
265
266
267 var whiteYMSB byte
268 binary.Read(p, binary.LittleEndian, &whiteYMSB)
269
270 edid.RedX = float64((uint16(redXMSB)<<2)|((uint16(redGreenLSB)>>6)&0x3)) / 1024
271 edid.RedY = float64((uint16(redYMSB)<<2)|((uint16(redGreenLSB)>>4)&0x3)) / 1024
272 edid.GreenX = float64((uint16(greenXMSB)<<2)|((uint16(redGreenLSB)>>2)&0x3)) / 1024
273 edid.GreenY = float64((uint16(greenYMSB)<<2)|(uint16(redGreenLSB)&0x3)) / 1024
274 edid.BlueX = float64((uint16(blueXMSB)<<2)|((uint16(blueWhiteLSB)>>6)&0x3)) / 1024
275 edid.BlueY = float64((uint16(blueYMSB)<<2)|((uint16(blueWhiteLSB)>>4)&0x3)) / 1024
276 edid.WhiteX = float64((uint16(whiteXMSB)<<2)|((uint16(blueWhiteLSB)>>2)&0x3)) / 1024
277 edid.WhiteY = float64((uint16(whiteYMSB)<<2)|(uint16(blueWhiteLSB)&0x3)) / 1024
278
279 binary.Read(p, binary.LittleEndian, &edid.TimingBitMap1)
280 binary.Read(p, binary.LittleEndian, &edid.TimingBitMap2)
281 binary.Read(p, binary.LittleEndian, &edid.TimingBitMap3)
282
283 for i := 0; i < 8; i++ {
284 var temp byte
285 binary.Read(p, binary.LittleEndian, &temp)
286 xResolution := (uint(temp) + 31) * 8
287 binary.Read(p, binary.LittleEndian, &temp)
288 pixelRatio := temp >> 6
289 verticalFrequency := (uint(temp) & 63) + 60
290 var yResolution uint
291 var pixelRatioStr string
292
293 switch pixelRatio {
294 case 0:
295
296 yResolution = xResolution * 10 / 16
297 pixelRatioStr = "16:10"
298 case 1:
299
300 pixelRatioStr = "4:3"
301 yResolution = xResolution * 3 / 4
302 case 2:
303
304 pixelRatioStr = "5:4"
305 yResolution = xResolution * 4 / 5
306 case 3:
307
308 pixelRatioStr = "16:9"
309 yResolution = xResolution * 9 / 16
310
311 }
312 edid.StandardTimingInformation = append(edid.StandardTimingInformation, fmt.Sprintf("%dx%d %s @%dHz", xResolution, yResolution, pixelRatioStr, verticalFrequency))
313
314 }
315
316
317
318 for i := 0; i < 4; i++ {
319 var temp [18]byte
320 var descriptor DetailedTimingDescriptor
321 binary.Read(p, binary.LittleEndian, &temp)
322 descriptor.PixelClock = ((uint32(temp[1]) << 8) | uint32(temp[0])) * 10
323 descriptor.HorizontalActive = (uint16(temp[4]&240) << 4) | uint16(temp[2])
324 descriptor.HorizontalBlanking = (uint16(temp[4]&15) << 8) | uint16(temp[3])
325 descriptor.VerticalActive = (uint16(temp[7]&240) << 4) | uint16(temp[5])
326 descriptor.VerticalBlanking = (uint16(temp[7]&15) << 8) | uint16(temp[6])
327 descriptor.HorizontalSyncOffset = ((uint16(temp[11]) & 192) << 2) | uint16(temp[8])
328 descriptor.HorizontalSyncPulseWidth = ((uint16(temp[11]) & 48) << 4) | uint16(temp[9])
329 descriptor.VerticalSyncOffset = ((uint16(temp[11]) & 12) << 2) | ((uint16(temp[10]) & 240) >> 4)
330 descriptor.VerticalSyncPulseWidth = ((uint16(temp[11]) & 3) << 4) | (uint16(temp[10]) & 15)
331 descriptor.HorizontalImageSize = ((uint16(temp[14]) & 240) << 4) | uint16(temp[12])
332 descriptor.VerticalImageSize = ((uint16(temp[14]) & 15) << 8) | uint16(temp[13])
333 descriptor.HorizontalBorder = temp[15]
334 descriptor.VerticalBorder = temp[16]
335 descriptor.Interlaced = (temp[17] & 128) > 0
336 zeroBit := temp[17] & 1
337 stereoMode := (temp[17] & 96) >> 5
338 if stereoMode == 0 {
339 descriptor.Stereo = "No Stereo"
340 } else {
341 if zeroBit == 1 {
342 switch stereoMode {
343 case 1:
344 descriptor.Stereo = "2-way interleaved stereo - Right image on even lines"
345 case 2:
346 descriptor.Stereo = "2-way interleaved stereo - Left image on even lines"
347 case 3:
348 descriptor.Stereo = "2-way interleaved stereo - side-by-side"
349 }
350
351 } else {
352 switch stereoMode {
353 case 1:
354 descriptor.Stereo = "Field sequential, sync=1 during right"
355 case 2:
356 descriptor.Stereo = "similar, sync=1 during left"
357 case 3:
358 descriptor.Stereo = "4-way interleaved stereo"
359 }
360 }
361 }
362
363 descriptor.SyncType = (temp[17] & 24) >> 3
364 descriptor.VerticalSyncPolarity = ((temp[17] & 4) >> 2) > 0
365 descriptor.HorizontalSyncPolarity = ((temp[17] & 2) >> 1) > 0
366
367 if descriptor.PixelClock == 0 {
368 descriptorType := temp[3]
369 switch descriptorType {
370 case 0xFF:
371 edid.MonitorSerialNumber = string(temp[5:])
372 case 0xFC:
373 mon := string(temp[5:])
374 mon = strings.Replace(mon, "\n", "", -1)
375 mon = strings.TrimSpace(mon)
376 edid.MonitorName = mon
377 case 0xFD:
378
379 var monitorRangeDescriptor MonitorRangeLimitDescriptor
380 monitorRangeDescriptor.MinimumVerticalFieldRate = temp[5]
381 monitorRangeDescriptor.MaximumVerticalFieldRate = temp[6]
382 monitorRangeDescriptor.MinimumHorizontalLineRate = temp[7]
383 monitorRangeDescriptor.MaximumHorizontalLineRate = temp[8]
384 monitorRangeDescriptor.MaximumPixelClockRate = temp[9] * 10
385 edid.MonitorRangeLimitDescriptors = append(edid.MonitorRangeLimitDescriptors, monitorRangeDescriptor)
386 }
387 } else {
388 edid.DetailedTimingDescriptors = append(edid.DetailedTimingDescriptors, descriptor)
389 }
390 }
391
392 binary.Read(p, binary.LittleEndian, &edid.NumberOfExtensions)
393 return edid, nil
394 }
395
396 func (edid *Edid) PrintableHeader() string {
397 var buffer bytes.Buffer
398 buffer.WriteString("0x")
399 for i := 0; i < len(edid.Header); i++ {
400 s := fmt.Sprintf("%02X", edid.Header[i])
401 buffer.WriteString(s)
402 }
403 return buffer.String()
404 }
405
406 func (e *Edid) PrettyPrint() {
407 w := new(tabwriter.Writer)
408 w.Init(os.Stdout, 30, 20, 4, '-', 0)
409 fmt.Fprintln(w, "Valid Checksum: \t ", e.CheckSum)
410 fmt.Fprintln(w, "Header: \t ", e.PrintableHeader())
411 fmt.Fprintln(w, "Monitor Name: \t ", e.MonitorName)
412 fmt.Fprintln(w, "Monitor Serial Number: \t ", e.MonitorSerialNumber)
413 fmt.Fprintln(w, "Manufacturer Name: \t ", e.ManufacturerId)
414 fmt.Fprintln(w, "Product Code: \t ", e.ProductCode)
415 fmt.Fprintln(w, "Serial Number: \t ", e.SerialNumber)
416 fmt.Fprintln(w, "Week of Manufacture: \t ", e.WeekOfManufacture)
417 fmt.Fprintln(w, "Year of Manufacture: \t ", e.YearOfManufacture)
418 fmt.Fprintln(w, "EDID Version: \t ", e.EdidVersion)
419 fmt.Fprintln(w, "EDID Revision: \t ", e.EdidRevision)
420 fmt.Fprintln(w, "\nBasic display parameters:\n")
421
422 if e.DigitalInput {
423 fmt.Fprintln(w, " Video Input Definition: \t ", "Digital")
424 fmt.Fprintln(w, " VESA DFP Compatibility: \t ", e.VESADFPCompatible)
425 } else {
426 fmt.Fprintln(w, " Video Input Definition: \t ", "Analog")
427 fmt.Fprintln(w, " Composite Sync Supported: \t ", e.CompositeSyncSupported)
428 fmt.Fprintln(w, " Sync on Green Supported: \t ", e.SyncOnGreenSupported)
429 }
430 fmt.Fprintln(w, " Max Horizontal Image Size: \t ", e.MaximumHorizontalImageSize, "cm")
431 fmt.Fprintln(w, " Max Vertical Image Size: \t ", e.MaximumVerticalImageSize, "cm")
432 fmt.Fprintln(w, " Display Gamma: \t ", e.DisplayGamma)
433
434 fmt.Fprintln(w, "\nPower Management:")
435 fmt.Fprintln(w, " DPMS Standby Supported: \t ", e.DPMSStandbySupported)
436 fmt.Fprintln(w, " DPMS Suspend Supported: \t ", e.DPMSSuspendSupported)
437 fmt.Fprintln(w, " DPMS Active Off Supported: \t ", e.DPMSActiveOffSupported)
438 fmt.Fprintln(w, " Display Type: \t ", e.DisplayType)
439
440 fmt.Fprintln(w, "\nChroma Information:")
441 fmt.Fprintln(w, " Red X: \t ", strconv.FormatFloat(e.RedX, 'f', 3, 64))
442 fmt.Fprintln(w, " Red Y: \t ", strconv.FormatFloat(e.RedY, 'f', 3, 64))
443 fmt.Fprintln(w, " Green X: \t ", strconv.FormatFloat(e.GreenX, 'f', 3, 64))
444 fmt.Fprintln(w, " Green Y: \t ", strconv.FormatFloat(e.GreenY, 'f', 3, 64))
445 fmt.Fprintln(w, " Blue X: \t ", strconv.FormatFloat(e.BlueX, 'f', 3, 64))
446 fmt.Fprintln(w, " Blue Y: \t ", strconv.FormatFloat(e.BlueY, 'f', 3, 64))
447 fmt.Fprintln(w, " White X: \t ", strconv.FormatFloat(e.WhiteX, 'f', 3, 64))
448 fmt.Fprintln(w, " White Y: \t ", strconv.FormatFloat(e.WhiteY, 'f', 3, 64))
449
450 fmt.Fprintln(w, "\nTimings Bitmaps:")
451
452 counter := 0
453 var mask byte
454 for mask = 0x80; mask != 0; mask >>= 1 {
455 if e.TimingBitMap1&mask > 0 {
456 fmt.Fprintln(w, " "+TimingBitMap1[counter])
457 }
458 counter++
459 }
460 counter = 0
461 for mask = 0x80; mask != 0; mask >>= 1 {
462 if e.TimingBitMap2&mask > 0 {
463 fmt.Fprintln(w, " "+TimingBitMap2[counter])
464 }
465 counter++
466 }
467
468 if e.TimingBitMap3&0x80 > 0 {
469 fmt.Fprintln(w, " 1152x870 @ 75 Hz (Apple Macintosh II)")
470 }
471 fmt.Fprintln(w, "\nStandard Timing Identification:")
472 for _, element := range e.StandardTimingInformation {
473 fmt.Fprintln(w, " ", element)
474 }
475
476 for index, element := range e.DetailedTimingDescriptors {
477 fmt.Fprintln(w, "\nDetailed Timing/Descriptor block", (index + 1))
478 fmt.Fprintln(w, " Pixel Clock: \t ", element.PixelClock, "kHz")
479 fmt.Fprintln(w, " Horizontal Active: \t ", element.HorizontalActive, "pixels")
480 fmt.Fprintln(w, " Horizontal Blanking: \t ", element.HorizontalBlanking, "pixels")
481 fmt.Fprintln(w, " Vertical Active: \t ", element.VerticalActive, "pixels")
482 fmt.Fprintln(w, " Vertical Blanking: \t ", element.VerticalBlanking, "pixels")
483 fmt.Fprintln(w, " Horizontal Sync Offset: \t ", element.HorizontalSyncOffset, "pixels")
484 fmt.Fprintln(w, " Horizontal Sync Pulse Width: \t ", element.HorizontalSyncPulseWidth, "pixels")
485 fmt.Fprintln(w, " Vertical Sync Offset: \t ", element.VerticalSyncOffset, "lines")
486 fmt.Fprintln(w, " Vertical Sync Pulse Width: \t ", element.VerticalSyncPulseWidth, "lines")
487 fmt.Fprintln(w, " Horizontal Image Size: \t ", element.HorizontalImageSize, "mm")
488 fmt.Fprintln(w, " Vertical Image Size: \t ", element.VerticalImageSize, "mm")
489 fmt.Fprintln(w, " Horizontal Border: \t ", element.HorizontalBorder, "px each side")
490 fmt.Fprintln(w, " Vertical Border: \t ", element.VerticalBorder, "px each side")
491 fmt.Fprintln(w, " Interlaced: \t ", element.Interlaced)
492 fmt.Fprintln(w, " Stereo Mode: \t ", element.Stereo)
493 switch element.SyncType {
494 case 0:
495 fmt.Fprintln(w, " Sync Type: \t ", "Analog composite")
496 case 1:
497 fmt.Fprintln(w, " Sync Type: \t ", "Bipolar analog composite")
498 case 2:
499 fmt.Fprintln(w, " Sync Type: \t ", "Digital composite (on HSync)")
500 case 3:
501 fmt.Fprintln(w, " Sync Type: \t ", "Digital separate")
502 fmt.Fprintln(w, " Vertical Sync Polarity: \t ", element.VerticalSyncPolarity)
503 fmt.Fprintln(w, " Horizontal Sync Polarity: \t ", element.HorizontalSyncPolarity)
504 }
505
506 }
507
508 for index, element := range e.MonitorRangeLimitDescriptors {
509 fmt.Fprintln(w, "\nMonitor range limits descriptor block", (index + 1))
510 fmt.Fprintln(w, " Minimum Vertical Field Rate: \t ", element.MinimumVerticalFieldRate, "Hz")
511 fmt.Fprintln(w, " Maximum Vertical Field Rate: \t ", element.MaximumVerticalFieldRate, "Hz")
512 fmt.Fprintln(w, " Minimum Horizontal Line Rate: \t ", element.MinimumHorizontalLineRate, "kHz")
513 fmt.Fprintln(w, " Maximum Horizontal Line Rate: \t ", element.MaximumHorizontalLineRate, "kHz")
514 fmt.Fprintln(w, " Maximum Pixel Clock Rate: \t ", element.MaximumPixelClockRate, "MHz")
515 }
516 fmt.Fprintln(w, "\nTotal Number of Extensions: \t ", e.NumberOfExtensions, "(not parsed)")
517
518 fmt.Fprintln(w, "\n**************************************************************************")
519 fmt.Fprintln(w, "* Bugs? Contact anoopengineer@gmail.com *")
520 fmt.Fprintln(w, "**************************************************************************\n")
521 w.Flush()
522 }
523
View as plain text