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.
@@ -4,7 +4,7 @@
4
4
  */
5
5
 
6
6
  static const char *const
7
- IO_CONSOLE_VERSION = "0.8.2";
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
- // Force the class to be File.
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
- /* :stopdoc: */
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
- /* :stopdoc: */
2012
- cConmode = rb_define_class_under(rb_cIO, "ConsoleMode", rb_cObject);
2013
- rb_define_const(cConmode, "VERSION", rb_obj_freeze(rb_str_new_cstr(IO_CONSOLE_VERSION)));
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
- /* :startdoc: */
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
  }