@@ -159,6 +159,174 @@ def is_closed_lid(output):
159
159
return False
160
160
161
161
162
+ def _read_resolution_from_18_byte_data_block (edid , offset ):
163
+ """Read resolution from edid bytearray beginning at offset. Return a tuple
164
+ of (horizontal, vertical) pixels if a valid resolution is defined, else
165
+ return None"""
166
+
167
+ horz = edid [offset + 2 ] + ((edid [offset + 4 ] & 0xf0 ) << 4 )
168
+ if horz == 0 :
169
+ return None
170
+
171
+ vert = edid [offset + 5 ] + ((edid [offset + 7 ] & 0xf0 ) << 4 )
172
+
173
+ return (horz , vert )
174
+
175
+
176
+ def _read_resolutions_from_base_block (edid , offset ):
177
+ resolutions = []
178
+
179
+ detail = 0x36
180
+ for _ in range (4 ):
181
+ res = _read_resolution_from_18_byte_data_block (edid , offset + detail )
182
+ if res :
183
+ resolutions .append (res )
184
+
185
+ detail += 18
186
+
187
+ return resolutions
188
+
189
+
190
+ def _read_resolutions_from_cta_block (edid , offset ):
191
+ version = edid [offset + 1 ]
192
+ if version < 1 :
193
+ return []
194
+
195
+ detail = edid [offset + 2 ]
196
+ if detail < 4 :
197
+ return []
198
+
199
+ resolutions = []
200
+ while detail < 127 :
201
+ res = _read_resolution_from_18_byte_data_block (edid , offset + detail )
202
+ if res :
203
+ resolutions .append (res )
204
+
205
+ detail += 18
206
+
207
+ return resolutions
208
+
209
+
210
+ def _read_resolution_from_displayid_block_0x01 (edid , offset ):
211
+ payload_length = edid [offset + 2 ]
212
+
213
+ if payload_length != 12 :
214
+ return None
215
+
216
+ horz = (edid [offset + 8 ] << 8 ) + edid [offset + 7 ]
217
+ if horz == 0 :
218
+ return None
219
+
220
+ vert = (edid [offset + 10 ] << 8 ) + edid [offset + 9 ]
221
+
222
+ return (horz , vert )
223
+
224
+
225
+ def _read_resolution_from_displayid_block_0x0c (edid , offset ):
226
+ payload_length = edid [offset + 2 ]
227
+
228
+ if payload_length != 13 :
229
+ return None
230
+
231
+ horz = (edid [offset + 6 ] << 8 ) + edid [offset + 5 ] + 1
232
+ if horz == 0 :
233
+ return None
234
+
235
+ vert = (edid [offset + 8 ] << 8 ) + edid [offset + 7 ] + 1
236
+
237
+ return (horz , vert )
238
+
239
+
240
+ def _read_resolution_from_displayid_block_0x21 (edid , offset ):
241
+ payload_length = edid [offset + 2 ]
242
+
243
+ if payload_length != 29 :
244
+ return None
245
+
246
+ horz = (edid [offset + 8 ] << 8 ) + edid [offset + 7 ]
247
+ if horz == 0 :
248
+ return None
249
+
250
+ vert = (edid [offset + 10 ] << 8 ) + edid [offset + 9 ]
251
+
252
+ return (horz , vert )
253
+
254
+
255
+ def _read_resolution_from_displayid_block (edid , offset ):
256
+ tag = edid [offset ]
257
+ payload_length = edid [offset + 2 ]
258
+
259
+ if tag == 0x01 :
260
+ res = _read_resolution_from_displayid_block_0x01 (edid , offset )
261
+ elif tag == 0x0c :
262
+ res = _read_resolution_from_displayid_block_0x0c (edid , offset )
263
+ elif tag == 0x21 :
264
+ res = _read_resolution_from_displayid_block_0x21 (edid , offset )
265
+ else :
266
+ res = None
267
+
268
+ # Add header length of 3
269
+ return payload_length + 3 , res
270
+
271
+
272
+ def _read_resolutions_from_displayid_block (edid , offset ):
273
+ # DisplayID length has a maximum of 121
274
+ total_length = min (edid [offset + 2 ], 121 )
275
+ base = offset + 5
276
+
277
+ resolutions = []
278
+ detail = 0
279
+ while detail < total_length :
280
+ block_length , resolution = \
281
+ _read_resolution_from_displayid_block (edid , base + detail )
282
+ if resolution :
283
+ resolutions .append (resolution )
284
+
285
+ detail += block_length
286
+
287
+ return resolutions
288
+
289
+
290
+ def _read_resolutions_from_block (edid , offset ):
291
+ block_type = edid [offset ]
292
+
293
+ if block_type == 0x00 :
294
+ return _read_resolutions_from_base_block (edid , offset )
295
+ if block_type == 0x02 :
296
+ return _read_resolutions_from_cta_block (edid , offset )
297
+ if block_type == 0x70 :
298
+ return _read_resolutions_from_displayid_block (edid , offset )
299
+
300
+ return []
301
+
302
+
303
+ def read_native_resolutions (edid ):
304
+ """Given an edid hex string, return a set of all the native resolutions
305
+ (horizontal, vertical) defined in the EDID"""
306
+
307
+ edid_page_size = 128
308
+
309
+ edid_bytearray = bytearray .fromhex (edid )
310
+
311
+ native_resolutions = set ()
312
+ for offset in range (0 , len (edid_bytearray ), edid_page_size ):
313
+ native_resolutions .update (_read_resolutions_from_block (edid_bytearray , offset ))
314
+
315
+ return native_resolutions
316
+
317
+
318
+ def _parse_edid_pattern (pattern ):
319
+ """Parse (horizontal, vertical) pixels from a pattern defined like """
320
+
321
+ # Only supports patterns like "native_resolution=3840x2160" for now
322
+ if "native_resolution=" not in pattern :
323
+ return None
324
+
325
+ m = re .match (r"native_resolution=(\d+)x(\d+)" , pattern )
326
+ if m :
327
+ return (int (m .group (1 )), int (m .group (2 )))
328
+
329
+
162
330
class AutorandrException (Exception ):
163
331
def __init__ (self , message , original_exception = None , report_bug = False ):
164
332
self .message = message
@@ -361,6 +529,8 @@ def parse_serial_from_edid(self):
361
529
return
362
530
if "*" in self .edid :
363
531
return
532
+ if _parse_edid_pattern (self .edid ):
533
+ return
364
534
# Thx to pyedid project, the following code was
365
535
# copied (and modified) from pyedid/__init__py:21 [parse_edid()]
366
536
raw = bytes .fromhex (self .edid )
@@ -551,6 +721,10 @@ def edid_equals(self, other):
551
721
return match_asterisk (self .edid , other .edid ) > 0
552
722
elif "*" in other .edid :
553
723
return match_asterisk (other .edid , self .edid ) > 0
724
+ if _parse_edid_pattern (self .edid ):
725
+ return match_native_resolution (self .edid , other .edid )
726
+ elif _parse_edid_pattern (other .edid ):
727
+ return match_native_resolution (other .edid , self .edid )
554
728
return self .edid == other .edid
555
729
556
730
def __ne__ (self , other ):
@@ -721,6 +895,14 @@ def match_asterisk(pattern, data):
721
895
return matched * 1. / total
722
896
723
897
898
+ def match_native_resolution (edid_pattern , edid_string ):
899
+ horz_x_vert = _parse_edid_pattern (edid_pattern )
900
+ if not horz_x_vert :
901
+ return False
902
+
903
+ return horz_x_vert in read_native_resolutions (edid_string )
904
+
905
+
724
906
def update_profiles_edid (profiles , config ):
725
907
fp_map = {}
726
908
for c in config :
@@ -766,8 +948,14 @@ def find_profiles(current_config, profiles):
766
948
if not matches or any ((name not in config .keys () for name in current_config .keys () if current_config [name ].fingerprint )):
767
949
continue
768
950
if matches :
769
- closeness = max (match_asterisk (output .edid , current_config [name ].edid ), match_asterisk (
770
- current_config [name ].edid , output .edid ))
951
+ config_edid = current_config [name ].edid
952
+ parsed = _parse_edid_pattern (output .edid ) or _parse_edid_pattern (config_edid )
953
+ if parsed :
954
+ closeness = int (match_native_resolution (config_edid , output .edid ) or
955
+ match_native_resolution (output .edid , config_edid ))
956
+ else :
957
+ closeness = max (match_asterisk (output .edid , config_edid ),
958
+ match_asterisk (config_edid , output .edid ))
771
959
detected_profiles .append ((closeness , profile_name ))
772
960
detected_profiles = [o [1 ] for o in sorted (detected_profiles , key = lambda x : - x [0 ])]
773
961
return detected_profiles
0 commit comments