@@ -56,13 +56,42 @@ def _update_max_yx(self):
5656 self .maxy = maxy - 1
5757 self .maxx = maxx - 1
5858
59+ def _decode (self , ch ):
60+ # The text of a chtype cell or input byte, decoded with the window's
61+ # encoding. A_CHARTEXT keeps the character byte, dropping the attributes.
62+ return bytes ([ch & curses .A_CHARTEXT ]).decode (self .win .encoding , 'replace' )
63+
64+ def _char_at (self , * yx ):
65+ # The text of the cell at the given position (default: the cursor).
66+ # instr() re-encodes it to the window's encoding; inch() cannot
67+ # represent a non-ASCII 8-bit-locale character on a wide build.
68+ return self .win .instr (* yx , 1 ).decode (self .win .encoding , 'replace' )
69+
70+ def _cell_at (self , * yx ):
71+ # The cell at the given position (default: the cursor) as a chtype
72+ # addch() can write back with its rendition. inch() mangles a non-ASCII
73+ # character on a wide build, so take the byte from instr() and the
74+ # attributes from inch().
75+ return self .win .instr (* yx , 1 )[0 ] | self .win .inch (* yx ) & curses .A_ATTRIBUTES
76+
77+ def _isprint (self , cell ):
78+ # Whether a chtype cell holds a printable character; _decode() drops the
79+ # attribute bits.
80+ return self ._decode (cell ).isprintable ()
81+
82+ def _printable_key (self , ch ):
83+ # Whether the integer keystroke is a printable character, not a key
84+ # code. 0..255 are character bytes (decoded with the window's encoding);
85+ # larger values are function and navigation keys.
86+ return ch <= 0xff and self ._decode (ch ).isprintable ()
87+
5988 def _end_of_line (self , y ):
6089 """Go to the location of the first blank on the given line,
6190 returning the index of the last non-blank character."""
6291 self ._update_max_yx ()
6392 last = self .maxx
6493 while True :
65- if curses . ascii . ascii ( self .win . inch (y , last )) != curses . ascii . SP :
94+ if self ._char_at (y , last ) != ' ' :
6695 last = min (self .maxx , last + 1 )
6796 break
6897 elif last == 0 :
@@ -76,15 +105,16 @@ def _insert_printable_char(self, ch):
76105 backyx = None
77106 while True :
78107 if self .insert_mode :
79- oldch = self .win . inch ()
108+ oldch = self ._cell_at ()
80109 if y >= self .maxy and x >= self .maxx :
81110 # Use insch() in the lower-right cell: addch() there would move
82111 # the cursor out of the window, raising an error and scrolling
83- # a scrollable window.
84- self .win .insch (ch )
112+ # a scrollable window. Pass it as text: insch() does not decode
113+ # an int byte through the locale on a wide build.
114+ self .win .insch (self ._decode (ch ), ch & curses .A_ATTRIBUTES )
85115 break
86116 self .win .addch (ch )
87- if not self .insert_mode or not curses . ascii . isprint (oldch ):
117+ if not self .insert_mode or not self . _isprint (oldch ):
88118 break
89119 ch = oldch
90120 (y , x ) = self .win .getyx ()
@@ -100,7 +130,7 @@ def do_command(self, ch):
100130 self ._update_max_yx ()
101131 (y , x ) = self .win .getyx ()
102132 self .lastcmd = ch
103- if curses . ascii . isprint (ch ):
133+ if self . _printable_key (ch ):
104134 self ._insert_printable_char (ch )
105135 elif ch == curses .ascii .SOH : # ^a
106136 self .win .move (y , 0 )
@@ -174,7 +204,7 @@ def gather(self):
174204 for x in range (self .maxx + 1 ):
175205 if self .stripspaces and x > stop :
176206 break
177- result = result + chr ( curses . ascii . ascii ( self .win . inch (y , x )) )
207+ result = result + self ._char_at (y , x )
178208 if self .maxy > 0 :
179209 result = result + "\n "
180210 return result
0 commit comments