@@ -434,8 +434,11 @@ def call_raw(self, msg):
434434
435435 # Screenshot capture disabled in call_raw (captures idle screens, adds latency).
436436 # Real confirmation screenshots are captured in callback_ButtonRequest instead.
437+ # Exception: capture on Failure (rejection screens like invalid BIP-39 word).
437438
438439 resp = super (DebugLinkMixin , self ).call_raw (msg )
440+ if isinstance (resp , proto .Failure ):
441+ self ._capture_oled ()
439442 self ._check_request (resp )
440443 return resp
441444
@@ -457,37 +460,42 @@ def _check_request(self, msg):
457460 raise CallException (types .Failure_Other ,
458461 "Expected %s, got %s" % (pprint (expected ), pprint (msg )))
459462
463+ def _capture_oled (self ):
464+ """Capture current OLED layout to screenshot directory."""
465+ if not SCREENSHOT or not self .debug :
466+ return
467+ try :
468+ layout = self .debug .read_layout ()
469+ if layout and len (layout ) >= 1024 :
470+ layout_bytes = len (layout )
471+ height = 64 if layout_bytes >= 2048 else 32
472+ rows = []
473+ for y in range (height ):
474+ row = bytearray (256 )
475+ for x in range (256 ):
476+ byte_idx = x + (y // 8 ) * 256
477+ if byte_idx < layout_bytes :
478+ b = layout [byte_idx ] if isinstance (layout [byte_idx ], int ) else ord (layout [byte_idx ])
479+ if (b >> (y % 8 )) & 1 :
480+ row [x ] = 255
481+ rows .append (bytes (row ))
482+ while len (rows ) < 64 :
483+ rows .append (bytes (256 ))
484+ screenshot_dir = getattr (self , 'screenshot_dir' , os .environ .get ('SCREENSHOT_DIR' , '.' ))
485+ os .makedirs (screenshot_dir , exist_ok = True )
486+ png_path = os .path .join (screenshot_dir , 'btn%05d.png' % self .screenshot_id )
487+ with open (png_path , 'wb' ) as f :
488+ f .write (_write_png (png_path , 256 , 64 , rows ))
489+ self .screenshot_id += 1
490+ except Exception :
491+ pass
492+
460493 def callback_ButtonRequest (self , msg ):
461494 if self .verbose :
462495 log ("ButtonRequest code: " + get_buttonrequest_value (msg .code ))
463496
464497 # Capture OLED screenshot BEFORE pressing button (confirmation screen)
465- if SCREENSHOT and self .debug :
466- try :
467- layout = self .debug .read_layout ()
468- if layout and len (layout ) >= 1024 :
469- layout_bytes = len (layout )
470- height = 64 if layout_bytes >= 2048 else 32
471- rows = []
472- for y in range (height ):
473- row = bytearray (256 )
474- for x in range (256 ):
475- byte_idx = x + (y // 8 ) * 256
476- if byte_idx < layout_bytes :
477- b = layout [byte_idx ] if isinstance (layout [byte_idx ], int ) else ord (layout [byte_idx ])
478- if (b >> (y % 8 )) & 1 :
479- row [x ] = 255
480- rows .append (bytes (row ))
481- while len (rows ) < 64 :
482- rows .append (bytes (256 ))
483- screenshot_dir = getattr (self , 'screenshot_dir' , os .environ .get ('SCREENSHOT_DIR' , '.' ))
484- os .makedirs (screenshot_dir , exist_ok = True )
485- png_path = os .path .join (screenshot_dir , 'btn%05d.png' % self .screenshot_id )
486- with open (png_path , 'wb' ) as f :
487- f .write (_write_png (png_path , 256 , 64 , rows ))
488- self .screenshot_id += 1
489- except Exception :
490- pass
498+ self ._capture_oled ()
491499
492500 if self .auto_button :
493501 if self .verbose :
0 commit comments