@@ -946,6 +946,131 @@ console_set_winsize(VALUE io, VALUE size)
946946#endif
947947
948948#ifdef _WIN32
949+ typedef struct {
950+ HANDLE handle ;
951+ INPUT_RECORD * records ;
952+ DWORD length ;
953+ DWORD count ;
954+ BOOL result ;
955+ } read_console_input_args_t ;
956+
957+ static void *
958+ nogvl_read_console_input (void * ptr )
959+ {
960+ read_console_input_args_t * args = ptr ;
961+ args -> result = ReadConsoleInputW (args -> handle , args -> records , args -> length , & args -> count );
962+ return 0 ;
963+ }
964+
965+ static void
966+ console_input_event_set (VALUE event , const char * name , VALUE value )
967+ {
968+ rb_hash_aset (event , ID2SYM (rb_intern (name )), value );
969+ }
970+
971+ static VALUE
972+ console_input_event (const INPUT_RECORD * record )
973+ {
974+ VALUE event = rb_hash_new ();
975+
976+ switch (record -> EventType ) {
977+ case KEY_EVENT :
978+ console_input_event_set (event , "type" , ID2SYM (rb_intern ("key" )));
979+ console_input_event_set (event , "key_down" , record -> Event .KeyEvent .bKeyDown ? Qtrue : Qfalse );
980+ console_input_event_set (event , "repeat_count" , UINT2NUM (record -> Event .KeyEvent .wRepeatCount ));
981+ console_input_event_set (event , "virtual_key_code" , UINT2NUM (record -> Event .KeyEvent .wVirtualKeyCode ));
982+ console_input_event_set (event , "virtual_scan_code" , UINT2NUM (record -> Event .KeyEvent .wVirtualScanCode ));
983+ console_input_event_set (event , "unicode_char" , UINT2NUM (record -> Event .KeyEvent .uChar .UnicodeChar ));
984+ console_input_event_set (event , "control_key_state" , UINT2NUM (record -> Event .KeyEvent .dwControlKeyState ));
985+ break ;
986+ case MOUSE_EVENT :
987+ console_input_event_set (event , "type" , ID2SYM (rb_intern ("mouse" )));
988+ console_input_event_set (event , "position" , rb_assoc_new (
989+ INT2NUM (record -> Event .MouseEvent .dwMousePosition .Y ),
990+ INT2NUM (record -> Event .MouseEvent .dwMousePosition .X )));
991+ console_input_event_set (event , "button_state" , UINT2NUM (record -> Event .MouseEvent .dwButtonState ));
992+ console_input_event_set (event , "control_key_state" , UINT2NUM (record -> Event .MouseEvent .dwControlKeyState ));
993+ console_input_event_set (event , "event_flags" , UINT2NUM (record -> Event .MouseEvent .dwEventFlags ));
994+ break ;
995+ case WINDOW_BUFFER_SIZE_EVENT :
996+ console_input_event_set (event , "type" , ID2SYM (rb_intern ("window_buffer_size" )));
997+ console_input_event_set (event , "size" , rb_assoc_new (
998+ INT2NUM (record -> Event .WindowBufferSizeEvent .dwSize .Y ),
999+ INT2NUM (record -> Event .WindowBufferSizeEvent .dwSize .X )));
1000+ break ;
1001+ case MENU_EVENT :
1002+ console_input_event_set (event , "type" , ID2SYM (rb_intern ("menu" )));
1003+ console_input_event_set (event , "command_id" , UINT2NUM (record -> Event .MenuEvent .dwCommandId ));
1004+ break ;
1005+ case FOCUS_EVENT :
1006+ console_input_event_set (event , "type" , ID2SYM (rb_intern ("focus" )));
1007+ console_input_event_set (event , "set_focus" , record -> Event .FocusEvent .bSetFocus ? Qtrue : Qfalse );
1008+ break ;
1009+ default :
1010+ console_input_event_set (event , "type" , UINT2NUM (record -> EventType ));
1011+ break ;
1012+ }
1013+
1014+ return event ;
1015+ }
1016+
1017+ /*
1018+ * call-seq:
1019+ * io.console_input_events([max_events]) -> array
1020+ *
1021+ * Reads up to +max_events+ console input events, preserving their order.
1022+ * The default is one event. Blocks until at least one event is available.
1023+ *
1024+ * Each event is returned as a Hash. The +:type+ and remaining keys are:
1025+ *
1026+ * - +:key+ : +:key_down+, +:repeat_count+, +:virtual_key_code+,
1027+ * +:virtual_scan_code+, +:unicode_char+, and +:control_key_state+.
1028+ * - +:mouse+ : +:position+ ([row, column]), +:button_state+,
1029+ * +:control_key_state+, and +:event_flags+.
1030+ * - +:window_buffer_size+ : +:size+ ([rows, columns]).
1031+ * - +:menu+ : +:command_id+.
1032+ * - +:focus+ : +:set_focus+.
1033+ *
1034+ * This method is Windows only.
1035+ *
1036+ * You must require 'io/console' to use this method.
1037+ */
1038+ static VALUE
1039+ console_input_events (int argc , VALUE * argv , VALUE io )
1040+ {
1041+ VALUE vmax ;
1042+ DWORD max_events = 1 ;
1043+ read_console_input_args_t args ;
1044+ VALUE event_buffer = 0 ;
1045+ VALUE events ;
1046+ DWORD i ;
1047+
1048+ rb_scan_args (argc , argv , "01" , & vmax );
1049+ if (!NIL_P (vmax )) {
1050+ max_events = NUM2UINT (vmax );
1051+ if (max_events == 0 ) rb_raise (rb_eArgError , "max_events must be positive" );
1052+ }
1053+
1054+ args .handle = (HANDLE )rb_w32_get_osfhandle (GetReadFD (io ));
1055+ args .records = ALLOCV_N (INPUT_RECORD , event_buffer , max_events );
1056+ args .length = max_events ;
1057+ args .count = 0 ;
1058+ args .result = FALSE;
1059+ rb_thread_call_without_gvl (nogvl_read_console_input , & args , RUBY_UBF_IO , 0 );
1060+ if (!args .result ) {
1061+ int error = LAST_ERROR ;
1062+ ALLOCV_END (event_buffer );
1063+ rb_syserr_fail (error , 0 );
1064+ }
1065+
1066+ events = rb_ary_new_capa (args .count );
1067+ for (i = 0 ; i < args .count ; ++ i ) {
1068+ rb_ary_push (events , console_input_event (& args .records [i ]));
1069+ }
1070+ ALLOCV_END (event_buffer );
1071+ return events ;
1072+ }
1073+
9491074/*
9501075 * call-seq:
9511076 * io.check_winsize_changed { ... } -> io
@@ -974,6 +1099,7 @@ console_check_winsize_changed(VALUE io)
9741099 return io ;
9751100}
9761101#else
1102+ #define console_input_events rb_f_notimplement
9771103#define console_check_winsize_changed rb_f_notimplement
9781104#endif
9791105
@@ -2076,6 +2202,7 @@ InitVM_console(void)
20762202 rb_define_method (rb_cIO , "scroll_backward" , console_scroll_backward , 1 );
20772203 rb_define_method (rb_cIO , "clear_screen" , console_clear_screen , 0 );
20782204 rb_define_method (rb_cIO , "pressed?" , console_key_pressed_p , 1 );
2205+ rb_define_method (rb_cIO , "console_input_events" , console_input_events , -1 );
20792206 rb_define_method (rb_cIO , "check_winsize_changed" , console_check_winsize_changed , 0 );
20802207 rb_define_method (rb_cIO , "getpass" , console_getpass , -1 );
20812208 rb_define_method (rb_cIO , "ttyname" , console_ttyname , 0 );
0 commit comments