@@ -10,7 +10,7 @@ use anyhow::{Result, bail};
1010use tokio:: sync:: Mutex ;
1111use tokio:: sync:: mpsc:: { Sender , UnboundedReceiver } ;
1212use warpgate_common:: auth:: AuthStateUserInfo ;
13- use warpgate_core:: { DesktopInput , Services } ;
13+ use warpgate_core:: { DesktopInput , Scancode , Services } ;
1414use warpgate_desktop_auth:: {
1515 Deadline , HoldEvent , HoldFrame , HoldInputSource , HoldPainter as HoldPainterExt ,
1616 InteractiveAuth , OtpAction , run_hold_screen as run_hold_screen_driver,
@@ -82,7 +82,7 @@ impl HoldInputSource for RdpHoldInput<'_> {
8282 scancode,
8383 down : true ,
8484 } ) ) => scancode
85- . and_then ( |s| scancode_otp_action ( s . code ) )
85+ . and_then ( scancode_otp_action)
8686 . or_else ( || keysym. and_then ( key_otp_action) )
8787 . map_or ( HoldEvent :: Other , HoldEvent :: Otp ) ,
8888 Some ( ServerEvent :: Size { width, height } ) => {
@@ -218,7 +218,14 @@ impl HoldPainter {
218218}
219219
220220/// Map a PC/AT set-1 scancode (what mstsc/FreeRDP send) to an OTP action.
221- fn scancode_otp_action ( code : u8 ) -> Option < OtpAction > {
221+ fn scancode_otp_action ( scancode : Scancode ) -> Option < OtpAction > {
222+ let Scancode { code, extended } = scancode;
223+ // The nav cluster shares its make codes with the keypad and is told apart only by the
224+ // E0 prefix, so without this an arrow key would type a digit. Numpad Enter is the one
225+ // extended key that still means something here.
226+ if extended && code != 0x1c {
227+ return None ;
228+ }
222229 Some ( match code {
223230 0x02 ..=0x0a => OtpAction :: Digit ( char:: from ( b'1' + ( code - 0x02 ) ) ) , // top row 1..9
224231 0x0b | 0x52 => OtpAction :: Digit ( '0' ) , // keypad 0
@@ -249,7 +256,7 @@ fn key_otp_action(keysym: u32) -> Option<OtpAction> {
249256
250257#[ cfg( test) ]
251258mod otp_input_tests {
252- use super :: { OtpAction , key_otp_action, scancode_otp_action} ;
259+ use super :: { OtpAction , Scancode , key_otp_action, scancode_otp_action} ;
253260
254261 fn digit ( action : Option < OtpAction > ) -> Option < char > {
255262 match action {
@@ -258,12 +265,26 @@ mod otp_input_tests {
258265 }
259266 }
260267
268+ fn plain ( code : u8 ) -> Option < OtpAction > {
269+ scancode_otp_action ( Scancode {
270+ code,
271+ extended : false ,
272+ } )
273+ }
274+
275+ fn e0 ( code : u8 ) -> Option < OtpAction > {
276+ scancode_otp_action ( Scancode {
277+ code,
278+ extended : true ,
279+ } )
280+ }
281+
261282 #[ test]
262283 fn scancode_number_row ( ) {
263284 // 0x02..=0x0a is the '1'..'9' row (computed, so guard the ends), 0x0b is '0'.
264- assert_eq ! ( digit( scancode_otp_action ( 0x02 ) ) , Some ( '1' ) ) ;
265- assert_eq ! ( digit( scancode_otp_action ( 0x0a ) ) , Some ( '9' ) ) ;
266- assert_eq ! ( digit( scancode_otp_action ( 0x0b ) ) , Some ( '0' ) ) ;
285+ assert_eq ! ( digit( plain ( 0x02 ) ) , Some ( '1' ) ) ;
286+ assert_eq ! ( digit( plain ( 0x0a ) ) , Some ( '9' ) ) ;
287+ assert_eq ! ( digit( plain ( 0x0b ) ) , Some ( '0' ) ) ;
267288 }
268289
269290 #[ test]
@@ -280,23 +301,26 @@ mod otp_input_tests {
280301 ( 0x48 , '8' ) ,
281302 ( 0x49 , '9' ) ,
282303 ] {
283- assert_eq ! (
284- digit( scancode_otp_action( code) ) ,
285- Some ( expected) ,
286- "scancode {code:#x}"
287- ) ;
304+ assert_eq ! ( digit( plain( code) ) , Some ( expected) , "scancode {code:#x}" ) ;
305+ }
306+ }
307+
308+ /// The nav cluster repeats the keypad's make codes under an E0 prefix; pressing an
309+ /// arrow must not enter a digit.
310+ #[ test]
311+ fn scancode_nav_cluster_is_not_a_digit ( ) {
312+ for code in [ 0x47u8 , 0x48 , 0x49 , 0x4b , 0x4d , 0x4f , 0x50 , 0x51 , 0x52 ] {
313+ assert ! ( e0( code) . is_none( ) , "extended scancode {code:#x}" ) ;
288314 }
315+ assert ! ( matches!( e0( 0x1c ) , Some ( OtpAction :: Submit ) ) ) ; // numpad Enter
289316 }
290317
291318 #[ test]
292319 fn scancode_control_and_unmapped ( ) {
293- assert ! ( matches!(
294- scancode_otp_action( 0x0e ) ,
295- Some ( OtpAction :: Backspace )
296- ) ) ;
297- assert ! ( matches!( scancode_otp_action( 0x1c ) , Some ( OtpAction :: Submit ) ) ) ;
298- assert ! ( scancode_otp_action( 0x3b ) . is_none( ) ) ; // F1 — not an OTP key
299- assert ! ( scancode_otp_action( 0x00 ) . is_none( ) ) ;
320+ assert ! ( matches!( plain( 0x0e ) , Some ( OtpAction :: Backspace ) ) ) ;
321+ assert ! ( matches!( plain( 0x1c ) , Some ( OtpAction :: Submit ) ) ) ;
322+ assert ! ( plain( 0x3b ) . is_none( ) ) ; // F1 — not an OTP key
323+ assert ! ( plain( 0x00 ) . is_none( ) ) ;
300324 }
301325
302326 #[ test]
0 commit comments