io-console 0.8.2 → 0.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/ext/io/console/console.c +521 -28
- data/ext/io/console/extconf.rb +13 -14
- data/ext/io/console/win32_vk.inc +313 -165
- metadata +2 -2
data/ext/io/console/console.c
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
static const char *const
|
|
7
|
-
IO_CONSOLE_VERSION = "0.
|
|
7
|
+
IO_CONSOLE_VERSION = "0.9.2";
|
|
8
8
|
|
|
9
9
|
#include "ruby.h"
|
|
10
10
|
#include "ruby/io.h"
|
|
@@ -56,6 +56,13 @@ typedef struct sgttyb conmode;
|
|
|
56
56
|
#include <conio.h>
|
|
57
57
|
typedef DWORD conmode;
|
|
58
58
|
|
|
59
|
+
#ifndef ENABLE_WRAP_AT_EOL_OUTPUT
|
|
60
|
+
# define ENABLE_WRAP_AT_EOL_OUTPUT 0x0002
|
|
61
|
+
#endif
|
|
62
|
+
#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
|
|
63
|
+
# define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
|
|
64
|
+
#endif
|
|
65
|
+
|
|
59
66
|
#define LAST_ERROR rb_w32_map_errno(GetLastError())
|
|
60
67
|
#define SET_LAST_ERROR (errno = LAST_ERROR, 0)
|
|
61
68
|
|
|
@@ -81,7 +88,7 @@ getattr(int fd, conmode *t)
|
|
|
81
88
|
|
|
82
89
|
#define CSI "\x1b\x5b"
|
|
83
90
|
|
|
84
|
-
static ID id_getc, id_close;
|
|
91
|
+
static ID id_getc, id_close, id_timeout;
|
|
85
92
|
static ID id_gets, id_flush, id_chomp_bang;
|
|
86
93
|
|
|
87
94
|
#ifndef HAVE_RB_INTERNED_STR_CSTR
|
|
@@ -89,6 +96,10 @@ static ID id_gets, id_flush, id_chomp_bang;
|
|
|
89
96
|
# define rb_interned_str_cstr(str) rb_str_freeze(rb_usascii_str_new_cstr(str))
|
|
90
97
|
#endif
|
|
91
98
|
|
|
99
|
+
#if !defined(HAVE_RB_CATEGORY_WARN) || !defined(HAVE_CONST_RB_WARN_CATEGORY_DEPRECATED)
|
|
100
|
+
# define rb_category_warn(category, ...) rb_warn(__VA_ARGS__)
|
|
101
|
+
#endif
|
|
102
|
+
|
|
92
103
|
#if defined HAVE_RUBY_FIBER_SCHEDULER_H
|
|
93
104
|
# include "ruby/fiber/scheduler.h"
|
|
94
105
|
#elif defined HAVE_RB_SCHEDULER_TIMEOUT
|
|
@@ -118,22 +129,6 @@ io_path_fallback(VALUE io)
|
|
|
118
129
|
#define rb_io_path io_path_fallback
|
|
119
130
|
#endif
|
|
120
131
|
|
|
121
|
-
#ifndef HAVE_RB_IO_GET_WRITE_IO
|
|
122
|
-
static VALUE
|
|
123
|
-
io_get_write_io_fallback(VALUE io)
|
|
124
|
-
{
|
|
125
|
-
rb_io_t *fptr;
|
|
126
|
-
GetOpenFile(io, fptr);
|
|
127
|
-
VALUE wio = fptr->tied_io_for_writing;
|
|
128
|
-
return wio ? wio : io;
|
|
129
|
-
}
|
|
130
|
-
#define rb_io_get_write_io io_get_write_io_fallback
|
|
131
|
-
#endif
|
|
132
|
-
|
|
133
|
-
#ifndef DHAVE_RB_SYSERR_FAIL_STR
|
|
134
|
-
# define rb_syserr_fail_str(e, mesg) rb_exc_raise(rb_syserr_new_str(e, mesg))
|
|
135
|
-
#endif
|
|
136
|
-
|
|
137
132
|
#define sys_fail(io) do { \
|
|
138
133
|
int err = errno; \
|
|
139
134
|
rb_syserr_fail_str(err, rb_io_path(io)); \
|
|
@@ -550,7 +545,7 @@ nogvl_getch(void *p)
|
|
|
550
545
|
|
|
551
546
|
/*
|
|
552
547
|
* call-seq:
|
|
553
|
-
* io.getch(min: nil, time: nil, intr: nil) -> char
|
|
548
|
+
* io.getch(min: nil, time: nil, intr: nil) -> char or nil
|
|
554
549
|
*
|
|
555
550
|
* Reads and returns a character in raw mode.
|
|
556
551
|
*
|
|
@@ -635,6 +630,43 @@ console_getch(int argc, VALUE *argv, VALUE io)
|
|
|
635
630
|
#endif
|
|
636
631
|
}
|
|
637
632
|
|
|
633
|
+
/*
|
|
634
|
+
* call-seq:
|
|
635
|
+
* io.input_pending? -> true or false
|
|
636
|
+
*
|
|
637
|
+
* Returns whether input can be read without blocking.
|
|
638
|
+
*
|
|
639
|
+
* You must require 'io/console' to use this method.
|
|
640
|
+
*/
|
|
641
|
+
static VALUE
|
|
642
|
+
console_input_pending_p(VALUE io)
|
|
643
|
+
{
|
|
644
|
+
rb_io_t *fptr;
|
|
645
|
+
|
|
646
|
+
GetOpenFile(io, fptr);
|
|
647
|
+
if (rb_io_read_pending(fptr)) return Qtrue;
|
|
648
|
+
#ifdef _WIN32
|
|
649
|
+
{
|
|
650
|
+
DWORD mode;
|
|
651
|
+
HANDLE h = (HANDLE)rb_w32_get_osfhandle(GetReadFD(io));
|
|
652
|
+
|
|
653
|
+
if (GetConsoleMode(h, &mode)) return _kbhit() ? Qtrue : Qfalse;
|
|
654
|
+
}
|
|
655
|
+
#endif
|
|
656
|
+
#if defined HAVE_RB_IO_WAIT
|
|
657
|
+
return RTEST(rb_io_wait(io, RB_INT2NUM(RUBY_IO_READABLE), INT2FIX(0))) ? Qtrue : Qfalse;
|
|
658
|
+
#else
|
|
659
|
+
{
|
|
660
|
+
struct timeval timeout = {0, 0};
|
|
661
|
+
int result;
|
|
662
|
+
|
|
663
|
+
result = rb_wait_for_single_fd(fptr->fd, RB_WAITFD_IN, &timeout);
|
|
664
|
+
if (result < 0) sys_fail(io);
|
|
665
|
+
return (result & RB_WAITFD_IN) ? Qtrue : Qfalse;
|
|
666
|
+
}
|
|
667
|
+
#endif
|
|
668
|
+
}
|
|
669
|
+
|
|
638
670
|
/*
|
|
639
671
|
* call-seq:
|
|
640
672
|
* io.noecho {|io| }
|
|
@@ -762,6 +794,70 @@ conmode_raw_new(int argc, VALUE *argv, VALUE obj)
|
|
|
762
794
|
return conmode_new(rb_obj_class(obj), &t);
|
|
763
795
|
}
|
|
764
796
|
|
|
797
|
+
#ifdef _WIN32
|
|
798
|
+
/*
|
|
799
|
+
* call-seq:
|
|
800
|
+
* mode.virtual_terminal_processing? -> true or false
|
|
801
|
+
*
|
|
802
|
+
* Returns whether virtual terminal sequences are processed on output.
|
|
803
|
+
*/
|
|
804
|
+
static VALUE
|
|
805
|
+
conmode_virtual_terminal_processing_p(VALUE obj)
|
|
806
|
+
{
|
|
807
|
+
conmode *t = rb_check_typeddata(obj, &conmode_type);
|
|
808
|
+
return (*t & ENABLE_VIRTUAL_TERMINAL_PROCESSING) ? Qtrue : Qfalse;
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
/*
|
|
812
|
+
* call-seq:
|
|
813
|
+
* mode.virtual_terminal_processing = enabled
|
|
814
|
+
*
|
|
815
|
+
* Enables or disables virtual terminal sequence processing in +mode+.
|
|
816
|
+
* Assign +mode+ to IO#console_mode= to apply the change.
|
|
817
|
+
*/
|
|
818
|
+
static VALUE
|
|
819
|
+
conmode_set_virtual_terminal_processing(VALUE obj, VALUE enabled)
|
|
820
|
+
{
|
|
821
|
+
conmode *t = rb_check_typeddata(obj, &conmode_type);
|
|
822
|
+
if (RTEST(enabled))
|
|
823
|
+
*t |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
|
|
824
|
+
else
|
|
825
|
+
*t &= ~ENABLE_VIRTUAL_TERMINAL_PROCESSING;
|
|
826
|
+
return obj;
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
/*
|
|
830
|
+
* call-seq:
|
|
831
|
+
* mode.wrap_at_eol_output? -> true or false
|
|
832
|
+
*
|
|
833
|
+
* Returns whether output wraps at the end of a line.
|
|
834
|
+
*/
|
|
835
|
+
static VALUE
|
|
836
|
+
conmode_wrap_at_eol_output_p(VALUE obj)
|
|
837
|
+
{
|
|
838
|
+
conmode *t = rb_check_typeddata(obj, &conmode_type);
|
|
839
|
+
return (*t & ENABLE_WRAP_AT_EOL_OUTPUT) ? Qtrue : Qfalse;
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
/*
|
|
843
|
+
* call-seq:
|
|
844
|
+
* mode.wrap_at_eol_output = enabled
|
|
845
|
+
*
|
|
846
|
+
* Enables or disables wrapping at the end of a line in +mode+.
|
|
847
|
+
* Assign +mode+ to IO#console_mode= to apply the change.
|
|
848
|
+
*/
|
|
849
|
+
static VALUE
|
|
850
|
+
conmode_set_wrap_at_eol_output(VALUE obj, VALUE enabled)
|
|
851
|
+
{
|
|
852
|
+
conmode *t = rb_check_typeddata(obj, &conmode_type);
|
|
853
|
+
if (RTEST(enabled))
|
|
854
|
+
*t |= ENABLE_WRAP_AT_EOL_OUTPUT;
|
|
855
|
+
else
|
|
856
|
+
*t &= ~ENABLE_WRAP_AT_EOL_OUTPUT;
|
|
857
|
+
return obj;
|
|
858
|
+
}
|
|
859
|
+
#endif
|
|
860
|
+
|
|
765
861
|
/*
|
|
766
862
|
* call-seq:
|
|
767
863
|
* io.console_mode -> mode
|
|
@@ -861,7 +957,6 @@ console_set_winsize(VALUE io, VALUE size)
|
|
|
861
957
|
int newrow, newcol;
|
|
862
958
|
COORD oldsize;
|
|
863
959
|
SMALL_RECT oldwindow;
|
|
864
|
-
BOOL ret;
|
|
865
960
|
#endif
|
|
866
961
|
VALUE row, col, xpixel, ypixel;
|
|
867
962
|
const VALUE *sz;
|
|
@@ -963,12 +1058,207 @@ console_set_winsize(VALUE io, VALUE size)
|
|
|
963
1058
|
#endif
|
|
964
1059
|
|
|
965
1060
|
#ifdef _WIN32
|
|
1061
|
+
enum console_input_handle_index {
|
|
1062
|
+
console_input_handle,
|
|
1063
|
+
console_input_wakeup,
|
|
1064
|
+
console_input_handle_count
|
|
1065
|
+
};
|
|
1066
|
+
|
|
1067
|
+
typedef struct {
|
|
1068
|
+
HANDLE handles[console_input_handle_count];
|
|
1069
|
+
INPUT_RECORD *records;
|
|
1070
|
+
DWORD length;
|
|
1071
|
+
DWORD count;
|
|
1072
|
+
DWORD timeout;
|
|
1073
|
+
DWORD wait_result;
|
|
1074
|
+
DWORD error;
|
|
1075
|
+
BOOL result;
|
|
1076
|
+
} read_console_input_args_t;
|
|
1077
|
+
|
|
1078
|
+
static void *
|
|
1079
|
+
nogvl_read_console_input(void *ptr)
|
|
1080
|
+
{
|
|
1081
|
+
read_console_input_args_t *args = ptr;
|
|
1082
|
+
|
|
1083
|
+
args->wait_result = WaitForMultipleObjects(console_input_handle_count,
|
|
1084
|
+
args->handles, FALSE, args->timeout);
|
|
1085
|
+
if (args->wait_result == WAIT_OBJECT_0 + console_input_handle) {
|
|
1086
|
+
args->result = ReadConsoleInputW(args->handles[console_input_handle],
|
|
1087
|
+
args->records, args->length, &args->count);
|
|
1088
|
+
if (!args->result) args->error = GetLastError();
|
|
1089
|
+
}
|
|
1090
|
+
else if (args->wait_result == WAIT_FAILED) {
|
|
1091
|
+
args->error = GetLastError();
|
|
1092
|
+
}
|
|
1093
|
+
return 0;
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
static void
|
|
1097
|
+
ubf_console_input(void *ptr)
|
|
1098
|
+
{
|
|
1099
|
+
read_console_input_args_t *args = ptr;
|
|
1100
|
+
SetEvent(args->handles[console_input_wakeup]);
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
static void
|
|
1104
|
+
console_input_event_set(VALUE event, const char *name, VALUE value)
|
|
1105
|
+
{
|
|
1106
|
+
rb_hash_aset(event, ID2SYM(rb_intern(name)), value);
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
static VALUE
|
|
1110
|
+
console_input_event(const INPUT_RECORD *record)
|
|
1111
|
+
{
|
|
1112
|
+
VALUE event = rb_hash_new();
|
|
1113
|
+
|
|
1114
|
+
switch (record->EventType) {
|
|
1115
|
+
case KEY_EVENT:
|
|
1116
|
+
console_input_event_set(event, "type", ID2SYM(rb_intern("key")));
|
|
1117
|
+
console_input_event_set(event, "key_down", record->Event.KeyEvent.bKeyDown ? Qtrue : Qfalse);
|
|
1118
|
+
console_input_event_set(event, "repeat_count", UINT2NUM(record->Event.KeyEvent.wRepeatCount));
|
|
1119
|
+
console_input_event_set(event, "virtual_key_code", UINT2NUM(record->Event.KeyEvent.wVirtualKeyCode));
|
|
1120
|
+
console_input_event_set(event, "virtual_scan_code", UINT2NUM(record->Event.KeyEvent.wVirtualScanCode));
|
|
1121
|
+
console_input_event_set(event, "unicode_char", UINT2NUM(record->Event.KeyEvent.uChar.UnicodeChar));
|
|
1122
|
+
console_input_event_set(event, "control_key_state", UINT2NUM(record->Event.KeyEvent.dwControlKeyState));
|
|
1123
|
+
break;
|
|
1124
|
+
case MOUSE_EVENT:
|
|
1125
|
+
console_input_event_set(event, "type", ID2SYM(rb_intern("mouse")));
|
|
1126
|
+
console_input_event_set(event, "position", rb_assoc_new(
|
|
1127
|
+
INT2NUM(record->Event.MouseEvent.dwMousePosition.Y),
|
|
1128
|
+
INT2NUM(record->Event.MouseEvent.dwMousePosition.X)));
|
|
1129
|
+
console_input_event_set(event, "button_state", UINT2NUM(record->Event.MouseEvent.dwButtonState));
|
|
1130
|
+
console_input_event_set(event, "control_key_state", UINT2NUM(record->Event.MouseEvent.dwControlKeyState));
|
|
1131
|
+
console_input_event_set(event, "event_flags", UINT2NUM(record->Event.MouseEvent.dwEventFlags));
|
|
1132
|
+
break;
|
|
1133
|
+
case WINDOW_BUFFER_SIZE_EVENT:
|
|
1134
|
+
console_input_event_set(event, "type", ID2SYM(rb_intern("window_buffer_size")));
|
|
1135
|
+
console_input_event_set(event, "size", rb_assoc_new(
|
|
1136
|
+
INT2NUM(record->Event.WindowBufferSizeEvent.dwSize.Y),
|
|
1137
|
+
INT2NUM(record->Event.WindowBufferSizeEvent.dwSize.X)));
|
|
1138
|
+
break;
|
|
1139
|
+
case MENU_EVENT:
|
|
1140
|
+
console_input_event_set(event, "type", ID2SYM(rb_intern("menu")));
|
|
1141
|
+
console_input_event_set(event, "command_id", UINT2NUM(record->Event.MenuEvent.dwCommandId));
|
|
1142
|
+
break;
|
|
1143
|
+
case FOCUS_EVENT:
|
|
1144
|
+
console_input_event_set(event, "type", ID2SYM(rb_intern("focus")));
|
|
1145
|
+
console_input_event_set(event, "set_focus", record->Event.FocusEvent.bSetFocus ? Qtrue : Qfalse);
|
|
1146
|
+
break;
|
|
1147
|
+
default:
|
|
1148
|
+
console_input_event_set(event, "type", UINT2NUM(record->EventType));
|
|
1149
|
+
break;
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
return event;
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
static VALUE
|
|
1156
|
+
console_input_events_read(VALUE vargs)
|
|
1157
|
+
{
|
|
1158
|
+
read_console_input_args_t *args = (read_console_input_args_t *)vargs;
|
|
1159
|
+
VALUE events;
|
|
1160
|
+
DWORD i;
|
|
1161
|
+
|
|
1162
|
+
rb_thread_call_without_gvl(nogvl_read_console_input, args,
|
|
1163
|
+
ubf_console_input, args);
|
|
1164
|
+
if (args->wait_result == WAIT_TIMEOUT) return rb_ary_new();
|
|
1165
|
+
if (args->wait_result != WAIT_OBJECT_0 + console_input_handle ||
|
|
1166
|
+
!args->result) {
|
|
1167
|
+
rb_syserr_fail(rb_w32_map_errno(args->error), 0);
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
events = rb_ary_new_capa(args->count);
|
|
1171
|
+
for (i = 0; i < args->count; ++i) {
|
|
1172
|
+
rb_ary_push(events, console_input_event(&args->records[i]));
|
|
1173
|
+
}
|
|
1174
|
+
return events;
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
static VALUE
|
|
1178
|
+
console_input_events_ensure(VALUE vargs)
|
|
1179
|
+
{
|
|
1180
|
+
read_console_input_args_t *args = (read_console_input_args_t *)vargs;
|
|
1181
|
+
|
|
1182
|
+
CloseHandle(args->handles[console_input_wakeup]);
|
|
1183
|
+
xfree(args->records);
|
|
1184
|
+
return Qnil;
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
/*
|
|
1188
|
+
* call-seq:
|
|
1189
|
+
* io.console_input_events([max_events], timeout: nil) -> array
|
|
1190
|
+
*
|
|
1191
|
+
* Reads up to +max_events+ console input events, preserving their order.
|
|
1192
|
+
* The default is one event. Blocks until at least one event is available,
|
|
1193
|
+
* or for +timeout+ seconds if specified. Returns an empty Array on timeout.
|
|
1194
|
+
*
|
|
1195
|
+
* Each event is returned as a Hash. The +:type+ and remaining keys are:
|
|
1196
|
+
*
|
|
1197
|
+
* - +:key+ : +:key_down+, +:repeat_count+, +:virtual_key_code+,
|
|
1198
|
+
* +:virtual_scan_code+, +:unicode_char+, and +:control_key_state+.
|
|
1199
|
+
* - +:mouse+ : +:position+ ([row, column]), +:button_state+,
|
|
1200
|
+
* +:control_key_state+, and +:event_flags+.
|
|
1201
|
+
* - +:window_buffer_size+ : +:size+ ([rows, columns]).
|
|
1202
|
+
* - +:menu+ : +:command_id+.
|
|
1203
|
+
* - +:focus+ : +:set_focus+.
|
|
1204
|
+
*
|
|
1205
|
+
* This method is Windows only.
|
|
1206
|
+
*
|
|
1207
|
+
* You must require 'io/console' to use this method.
|
|
1208
|
+
*/
|
|
1209
|
+
static VALUE
|
|
1210
|
+
console_input_events(int argc, VALUE *argv, VALUE io)
|
|
1211
|
+
{
|
|
1212
|
+
VALUE vmax = Qnil, vopts = Qnil, vtimeout = Qundef;
|
|
1213
|
+
VALUE values[1];
|
|
1214
|
+
ID keywords[1] = {id_timeout};
|
|
1215
|
+
DWORD max_events = 1;
|
|
1216
|
+
read_console_input_args_t args;
|
|
1217
|
+
|
|
1218
|
+
rb_scan_args(argc, argv, "01:", &vmax, &vopts);
|
|
1219
|
+
if (rb_get_kwargs(vopts, keywords, 0, 1, values)) {
|
|
1220
|
+
vtimeout = values[0];
|
|
1221
|
+
}
|
|
1222
|
+
if (!NIL_P(vmax)) {
|
|
1223
|
+
max_events = NUM2UINT(vmax);
|
|
1224
|
+
if (max_events == 0) rb_raise(rb_eArgError, "max_events must be positive");
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
args.timeout = INFINITE;
|
|
1228
|
+
if (!NIL_OR_UNDEF_P(vtimeout)) {
|
|
1229
|
+
struct timeval timeout = rb_time_interval(vtimeout);
|
|
1230
|
+
uint64_t milliseconds = (uint64_t)timeout.tv_sec * 1000;
|
|
1231
|
+
milliseconds += ((uint64_t)timeout.tv_usec + 999) / 1000;
|
|
1232
|
+
args.timeout = milliseconds < INFINITE ? (DWORD)milliseconds : INFINITE - 1;
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
args.handles[console_input_handle] =
|
|
1236
|
+
(HANDLE)rb_w32_get_osfhandle(GetReadFD(io));
|
|
1237
|
+
args.records = ALLOC_N(INPUT_RECORD, max_events);
|
|
1238
|
+
args.handles[console_input_wakeup] = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
1239
|
+
if (!args.handles[console_input_wakeup]) {
|
|
1240
|
+
int error = LAST_ERROR;
|
|
1241
|
+
xfree(args.records);
|
|
1242
|
+
rb_syserr_fail(error, 0);
|
|
1243
|
+
}
|
|
1244
|
+
args.length = max_events;
|
|
1245
|
+
args.count = 0;
|
|
1246
|
+
args.wait_result = WAIT_FAILED;
|
|
1247
|
+
args.error = ERROR_SUCCESS;
|
|
1248
|
+
args.result = FALSE;
|
|
1249
|
+
return rb_ensure(console_input_events_read, (VALUE)&args,
|
|
1250
|
+
console_input_events_ensure, (VALUE)&args);
|
|
1251
|
+
}
|
|
1252
|
+
|
|
966
1253
|
/*
|
|
967
1254
|
* call-seq:
|
|
968
1255
|
* io.check_winsize_changed { ... } -> io
|
|
969
1256
|
*
|
|
970
1257
|
* Yields while console input events are queued.
|
|
971
1258
|
*
|
|
1259
|
+
* Deprecated because it discards queued input events other than window buffer
|
|
1260
|
+
* size changes. Use IO#console_input_events instead to preserve all events.
|
|
1261
|
+
*
|
|
972
1262
|
* This method is Windows only.
|
|
973
1263
|
*
|
|
974
1264
|
* You must require 'io/console' to use this method.
|
|
@@ -979,6 +1269,9 @@ console_check_winsize_changed(VALUE io)
|
|
|
979
1269
|
HANDLE h;
|
|
980
1270
|
DWORD num;
|
|
981
1271
|
|
|
1272
|
+
rb_category_warn(RB_WARN_CATEGORY_DEPRECATED,
|
|
1273
|
+
"IO#check_winsize_changed is deprecated; "
|
|
1274
|
+
"use IO#console_input_events instead");
|
|
982
1275
|
h = (HANDLE)rb_w32_get_osfhandle(GetReadFD(io));
|
|
983
1276
|
while (GetNumberOfConsoleInputEvents(h, &num) && num > 0) {
|
|
984
1277
|
INPUT_RECORD rec;
|
|
@@ -991,6 +1284,7 @@ console_check_winsize_changed(VALUE io)
|
|
|
991
1284
|
return io;
|
|
992
1285
|
}
|
|
993
1286
|
#else
|
|
1287
|
+
#define console_input_events rb_f_notimplement
|
|
994
1288
|
#define console_check_winsize_changed rb_f_notimplement
|
|
995
1289
|
#endif
|
|
996
1290
|
|
|
@@ -1291,6 +1585,54 @@ console_cursor_pos(VALUE io)
|
|
|
1291
1585
|
#endif
|
|
1292
1586
|
}
|
|
1293
1587
|
|
|
1588
|
+
static VALUE
|
|
1589
|
+
console_cursor_visibility(VALUE io, int visible)
|
|
1590
|
+
{
|
|
1591
|
+
#ifdef _WIN32
|
|
1592
|
+
HANDLE h = (HANDLE)rb_w32_get_osfhandle(GetWriteFD(io));
|
|
1593
|
+
CONSOLE_CURSOR_INFO info;
|
|
1594
|
+
|
|
1595
|
+
if (!GetConsoleCursorInfo(h, &info)) {
|
|
1596
|
+
rb_syserr_fail(LAST_ERROR, 0);
|
|
1597
|
+
}
|
|
1598
|
+
info.bVisible = visible;
|
|
1599
|
+
if (!SetConsoleCursorInfo(h, &info)) {
|
|
1600
|
+
rb_syserr_fail(LAST_ERROR, 0);
|
|
1601
|
+
}
|
|
1602
|
+
#else
|
|
1603
|
+
rb_io_write(io, rb_str_new_cstr(visible ? CSI "?25h" : CSI "?25l"));
|
|
1604
|
+
#endif
|
|
1605
|
+
return io;
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
/*
|
|
1609
|
+
* call-seq:
|
|
1610
|
+
* io.hide_cursor -> io
|
|
1611
|
+
*
|
|
1612
|
+
* Hides the cursor.
|
|
1613
|
+
*
|
|
1614
|
+
* You must require 'io/console' to use this method.
|
|
1615
|
+
*/
|
|
1616
|
+
static VALUE
|
|
1617
|
+
console_hide_cursor(VALUE io)
|
|
1618
|
+
{
|
|
1619
|
+
return console_cursor_visibility(io, 0);
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1622
|
+
/*
|
|
1623
|
+
* call-seq:
|
|
1624
|
+
* io.show_cursor -> io
|
|
1625
|
+
*
|
|
1626
|
+
* Shows the cursor.
|
|
1627
|
+
*
|
|
1628
|
+
* You must require 'io/console' to use this method.
|
|
1629
|
+
*/
|
|
1630
|
+
static VALUE
|
|
1631
|
+
console_show_cursor(VALUE io)
|
|
1632
|
+
{
|
|
1633
|
+
return console_cursor_visibility(io, 1);
|
|
1634
|
+
}
|
|
1635
|
+
|
|
1294
1636
|
/*
|
|
1295
1637
|
* call-seq:
|
|
1296
1638
|
* io.goto(line, column) -> io
|
|
@@ -1710,7 +2052,7 @@ console_dev(int argc, VALUE *argv, VALUE klass)
|
|
|
1710
2052
|
Check_Type(sym = argv[0], T_SYMBOL);
|
|
1711
2053
|
}
|
|
1712
2054
|
|
|
1713
|
-
|
|
2055
|
+
/* Force the class to be File. */
|
|
1714
2056
|
if (klass == rb_cIO) klass = rb_cFile;
|
|
1715
2057
|
|
|
1716
2058
|
if (console_dev_get(klass, &con)) {
|
|
@@ -1777,7 +2119,7 @@ console_dev(int argc, VALUE *argv, VALUE klass)
|
|
|
1777
2119
|
|
|
1778
2120
|
/*
|
|
1779
2121
|
* call-seq:
|
|
1780
|
-
* io.getch(min: nil, time: nil, intr: nil) -> char
|
|
2122
|
+
* io.getch(min: nil, time: nil, intr: nil) -> char or nil
|
|
1781
2123
|
*
|
|
1782
2124
|
* See IO#getch.
|
|
1783
2125
|
*/
|
|
@@ -1931,6 +2273,103 @@ console_ttyname(VALUE io)
|
|
|
1931
2273
|
# define console_ttyname rb_f_notimplement
|
|
1932
2274
|
#endif
|
|
1933
2275
|
|
|
2276
|
+
typedef enum {
|
|
2277
|
+
platform_default,
|
|
2278
|
+
#if defined _WIN32
|
|
2279
|
+
platform_cygwin,
|
|
2280
|
+
platform_msys,
|
|
2281
|
+
#endif
|
|
2282
|
+
platform_any,
|
|
2283
|
+
|
|
2284
|
+
platform_default_bit = 1U << platform_default,
|
|
2285
|
+
#if defined _WIN32
|
|
2286
|
+
platform_cygwin_bit = 1U << platform_cygwin,
|
|
2287
|
+
platform_msys_bit = 1U << platform_msys,
|
|
2288
|
+
#endif
|
|
2289
|
+
platform_any_bit = (1U << platform_any) - 1 /* all bits */
|
|
2290
|
+
} console_platform_t;
|
|
2291
|
+
|
|
2292
|
+
/*
|
|
2293
|
+
* call-seq:
|
|
2294
|
+
* io.tty?([mode, ...]) -> true or false
|
|
2295
|
+
*
|
|
2296
|
+
* Returns +true+ if the stream is associated with a terminal device (tty),
|
|
2297
|
+
* +false+ otherwise.
|
|
2298
|
+
*
|
|
2299
|
+
* If one or more +type+s are given, returns +true+ if the stream is
|
|
2300
|
+
* associated with any of the specified tty types.
|
|
2301
|
+
*
|
|
2302
|
+
* - +nil+ : Returns the result of the default tty check, as if no type were
|
|
2303
|
+
* given. It can be combined with other types.
|
|
2304
|
+
* - +:any+ : Returns +true+ for any known kind of tty, including the
|
|
2305
|
+
* default tty.
|
|
2306
|
+
* - +:cygwin+ : Returns +true+ for cygwin tty, on Windows.
|
|
2307
|
+
* - +:msys+ : Returns +true+ for msys2 tty, on Windows.
|
|
2308
|
+
*/
|
|
2309
|
+
static VALUE
|
|
2310
|
+
console_platform_tty_p(int argc, VALUE *argv, VALUE io)
|
|
2311
|
+
{
|
|
2312
|
+
VALUE ret = Qfalse;
|
|
2313
|
+
int mode = 0;
|
|
2314
|
+
|
|
2315
|
+
if (argc > 0) {
|
|
2316
|
+
int i;
|
|
2317
|
+
for (i = 0; i < argc; ++i) {
|
|
2318
|
+
VALUE m = argv[i];
|
|
2319
|
+
if (NIL_P(m)) {
|
|
2320
|
+
mode |= platform_default_bit;
|
|
2321
|
+
continue;
|
|
2322
|
+
}
|
|
2323
|
+
Check_Type(m, T_SYMBOL);
|
|
2324
|
+
if (m == ID2SYM(rb_intern("any"))) {
|
|
2325
|
+
mode |= platform_any_bit;
|
|
2326
|
+
}
|
|
2327
|
+
#if defined _WIN32
|
|
2328
|
+
else if (m == ID2SYM(rb_intern("cygwin"))) {
|
|
2329
|
+
mode |= platform_cygwin_bit;
|
|
2330
|
+
}
|
|
2331
|
+
else if (m == ID2SYM(rb_intern("msys"))) {
|
|
2332
|
+
mode |= platform_msys_bit;
|
|
2333
|
+
}
|
|
2334
|
+
#endif
|
|
2335
|
+
else {
|
|
2336
|
+
rb_raise(rb_eArgError, "unknown tty type: %+" PRIsVALUE, m);
|
|
2337
|
+
}
|
|
2338
|
+
}
|
|
2339
|
+
}
|
|
2340
|
+
if ((mode & platform_default_bit) || (mode == 0)) {
|
|
2341
|
+
ret = rb_call_super(0, 0);
|
|
2342
|
+
}
|
|
2343
|
+
if ((mode & ~platform_default_bit) && !RTEST(ret)) {
|
|
2344
|
+
#if defined _WIN32
|
|
2345
|
+
if (mode & (platform_cygwin_bit | platform_msys_bit)) {
|
|
2346
|
+
struct {
|
|
2347
|
+
FILE_NAME_INFO info;
|
|
2348
|
+
WCHAR rest[MAX_PATH];
|
|
2349
|
+
} buffer;
|
|
2350
|
+
|
|
2351
|
+
HANDLE h = (HANDLE)rb_w32_get_osfhandle(GetReadFD(io));
|
|
2352
|
+
if ((GetFileType(h) == FILE_TYPE_PIPE) &&
|
|
2353
|
+
GetFileInformationByHandleEx(h, FileNameInfo, &buffer, sizeof(buffer))) {
|
|
2354
|
+
WCHAR *const name = buffer.info.FileName;
|
|
2355
|
+
DWORD len = buffer.info.FileNameLength / sizeof(WCHAR);
|
|
2356
|
+
name[len] = L'\0';
|
|
2357
|
+
# define tty_pipe_p(type) \
|
|
2358
|
+
(memcmp(name, L"\\" #type "-", sizeof(L"\\" #type)) == 0 && \
|
|
2359
|
+
wcsstr(&name[rb_strlen_lit("\\" #type "-")], L"-pty") != NULL)
|
|
2360
|
+
if (!ret && (mode & platform_cygwin_bit)) {
|
|
2361
|
+
ret = tty_pipe_p(cygwin);
|
|
2362
|
+
}
|
|
2363
|
+
if (!ret && (mode & platform_msys_bit)) {
|
|
2364
|
+
ret = tty_pipe_p(msys);
|
|
2365
|
+
}
|
|
2366
|
+
}
|
|
2367
|
+
}
|
|
2368
|
+
#endif
|
|
2369
|
+
}
|
|
2370
|
+
return ret;
|
|
2371
|
+
}
|
|
2372
|
+
|
|
1934
2373
|
/*
|
|
1935
2374
|
* IO console methods
|
|
1936
2375
|
*/
|
|
@@ -1952,6 +2391,7 @@ Init_console(void)
|
|
|
1952
2391
|
id_flush = rb_intern("flush");
|
|
1953
2392
|
id_chomp_bang = rb_intern("chomp!");
|
|
1954
2393
|
id_close = rb_intern("close");
|
|
2394
|
+
id_timeout = rb_intern("timeout");
|
|
1955
2395
|
#define init_rawmode_opt_id(name) \
|
|
1956
2396
|
rawmode_opt_ids[kwd_##name] = rb_intern(#name)
|
|
1957
2397
|
init_rawmode_opt_id(min);
|
|
@@ -1966,11 +2406,31 @@ Init_console(void)
|
|
|
1966
2406
|
void
|
|
1967
2407
|
InitVM_console(void)
|
|
1968
2408
|
{
|
|
2409
|
+
/* :nodoc: */
|
|
2410
|
+
VALUE mConsole = rb_define_module_under(rb_cIO, "Console");
|
|
2411
|
+
#ifdef _WIN32
|
|
2412
|
+
/* :nodoc: */
|
|
2413
|
+
VALUE mWindows = rb_define_module_under(mConsole, "Windows");
|
|
2414
|
+
#define define_win32_const(name) rb_define_const(mWindows, #name, UINT2NUM(name))
|
|
2415
|
+
EACH_VK(define_win32_const,;);
|
|
2416
|
+
define_win32_const(RIGHT_ALT_PRESSED);
|
|
2417
|
+
define_win32_const(LEFT_ALT_PRESSED);
|
|
2418
|
+
define_win32_const(RIGHT_CTRL_PRESSED);
|
|
2419
|
+
define_win32_const(LEFT_CTRL_PRESSED);
|
|
2420
|
+
define_win32_const(SHIFT_PRESSED);
|
|
2421
|
+
define_win32_const(NUMLOCK_ON);
|
|
2422
|
+
define_win32_const(SCROLLLOCK_ON);
|
|
2423
|
+
define_win32_const(CAPSLOCK_ON);
|
|
2424
|
+
define_win32_const(ENHANCED_KEY);
|
|
2425
|
+
#undef define_win32_const
|
|
2426
|
+
#endif
|
|
2427
|
+
|
|
1969
2428
|
rb_define_method(rb_cIO, "raw", console_raw, -1);
|
|
1970
2429
|
rb_define_method(rb_cIO, "raw!", console_set_raw, -1);
|
|
1971
2430
|
rb_define_method(rb_cIO, "cooked", console_cooked, 0);
|
|
1972
2431
|
rb_define_method(rb_cIO, "cooked!", console_set_cooked, 0);
|
|
1973
2432
|
rb_define_method(rb_cIO, "getch", console_getch, -1);
|
|
2433
|
+
rb_define_method(rb_cIO, "input_pending?", console_input_pending_p, 0);
|
|
1974
2434
|
rb_define_method(rb_cIO, "echo=", console_set_echo, 1);
|
|
1975
2435
|
rb_define_method(rb_cIO, "echo?", console_echo_p, 0);
|
|
1976
2436
|
rb_define_method(rb_cIO, "console_mode", console_conmode_get, 0);
|
|
@@ -1985,6 +2445,8 @@ InitVM_console(void)
|
|
|
1985
2445
|
rb_define_method(rb_cIO, "goto", console_goto, 2);
|
|
1986
2446
|
rb_define_method(rb_cIO, "cursor", console_cursor_pos, 0);
|
|
1987
2447
|
rb_define_method(rb_cIO, "cursor=", console_cursor_set, 1);
|
|
2448
|
+
rb_define_method(rb_cIO, "hide_cursor", console_hide_cursor, 0);
|
|
2449
|
+
rb_define_method(rb_cIO, "show_cursor", console_show_cursor, 0);
|
|
1988
2450
|
rb_define_method(rb_cIO, "cursor_up", console_cursor_up, 1);
|
|
1989
2451
|
rb_define_method(rb_cIO, "cursor_down", console_cursor_down, 1);
|
|
1990
2452
|
rb_define_method(rb_cIO, "cursor_left", console_cursor_left, 1);
|
|
@@ -1996,27 +2458,58 @@ InitVM_console(void)
|
|
|
1996
2458
|
rb_define_method(rb_cIO, "scroll_backward", console_scroll_backward, 1);
|
|
1997
2459
|
rb_define_method(rb_cIO, "clear_screen", console_clear_screen, 0);
|
|
1998
2460
|
rb_define_method(rb_cIO, "pressed?", console_key_pressed_p, 1);
|
|
2461
|
+
rb_define_method(rb_cIO, "console_input_events", console_input_events, -1);
|
|
1999
2462
|
rb_define_method(rb_cIO, "check_winsize_changed", console_check_winsize_changed, 0);
|
|
2000
2463
|
rb_define_method(rb_cIO, "getpass", console_getpass, -1);
|
|
2001
2464
|
rb_define_method(rb_cIO, "ttyname", console_ttyname, 0);
|
|
2465
|
+
{
|
|
2466
|
+
/* :nodoc: */
|
|
2467
|
+
VALUE platform = rb_define_module_under(rb_cIO, "platform_tty");
|
|
2468
|
+
{
|
|
2469
|
+
VALUE rb_cIO = platform;
|
|
2470
|
+
rb_define_method(rb_cIO, "tty?", console_platform_tty_p, -1);
|
|
2471
|
+
rb_define_method(rb_cIO, "isatty", console_platform_tty_p, -1);
|
|
2472
|
+
}
|
|
2473
|
+
#ifdef HAVE_RB_PREPEND_MODULE
|
|
2474
|
+
rb_prepend_module(rb_cIO, platform);
|
|
2475
|
+
#else
|
|
2476
|
+
rb_funcall(rb_cIO, rb_intern_const("prepend"), 1, platform);
|
|
2477
|
+
#endif
|
|
2478
|
+
}
|
|
2002
2479
|
rb_define_singleton_method(rb_cIO, "console", console_dev, -1);
|
|
2003
2480
|
{
|
|
2004
|
-
/* :
|
|
2481
|
+
/* :nodoc: */
|
|
2005
2482
|
VALUE mReadable = rb_define_module_under(rb_cIO, "generic_readable");
|
|
2006
|
-
/* :startdoc: */
|
|
2007
2483
|
rb_define_method(mReadable, "getch", io_getch, -1);
|
|
2008
2484
|
rb_define_method(mReadable, "getpass", io_getpass, -1);
|
|
2009
2485
|
}
|
|
2010
2486
|
{
|
|
2011
|
-
/* :
|
|
2012
|
-
|
|
2013
|
-
|
|
2487
|
+
/* :nodoc: */
|
|
2488
|
+
VALUE version = rb_obj_freeze(rb_str_new_cstr(IO_CONSOLE_VERSION));
|
|
2489
|
+
ID cid, deprecate_constant = rb_intern_const("deprecate_constant");
|
|
2490
|
+
rb_define_const(mConsole, "VERSION", version);
|
|
2491
|
+
/* :nodoc: */
|
|
2492
|
+
cConmode = rb_define_class_under(mConsole, "Mode", rb_cObject);
|
|
2493
|
+
|
|
2494
|
+
/* old internal names; do not use */
|
|
2495
|
+
cid = rb_intern_const("ConsoleMode");
|
|
2496
|
+
rb_const_set(rb_cIO, cid, cConmode);
|
|
2497
|
+
rb_funcall(rb_cIO, deprecate_constant, 1, ID2SYM(cid));
|
|
2498
|
+
cid = rb_intern_const("VERSION");
|
|
2499
|
+
rb_const_set(cConmode, cid, version);
|
|
2500
|
+
rb_funcall(cConmode, deprecate_constant, 1, ID2SYM(cid));
|
|
2501
|
+
|
|
2014
2502
|
rb_define_alloc_func(cConmode, conmode_alloc);
|
|
2015
2503
|
rb_undef_method(cConmode, "initialize");
|
|
2016
2504
|
rb_define_method(cConmode, "initialize_copy", conmode_init_copy, 1);
|
|
2017
2505
|
rb_define_method(cConmode, "echo=", conmode_set_echo, 1);
|
|
2018
2506
|
rb_define_method(cConmode, "raw!", conmode_set_raw, -1);
|
|
2019
2507
|
rb_define_method(cConmode, "raw", conmode_raw_new, -1);
|
|
2020
|
-
|
|
2508
|
+
#ifdef _WIN32
|
|
2509
|
+
rb_define_method(cConmode, "virtual_terminal_processing?", conmode_virtual_terminal_processing_p, 0);
|
|
2510
|
+
rb_define_method(cConmode, "virtual_terminal_processing=", conmode_set_virtual_terminal_processing, 1);
|
|
2511
|
+
rb_define_method(cConmode, "wrap_at_eol_output?", conmode_wrap_at_eol_output_p, 0);
|
|
2512
|
+
rb_define_method(cConmode, "wrap_at_eol_output=", conmode_set_wrap_at_eol_output, 1);
|
|
2513
|
+
#endif
|
|
2021
2514
|
}
|
|
2022
2515
|
}
|