io-console 0.4.9 → 0.5.0

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.
Files changed (3) hide show
  1. checksums.yaml +4 -4
  2. data/ext/io/console/console.c +671 -26
  3. metadata +3 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 86e6802f946ec7698be85ff97876f91278df790bdd49f81dcd2873108f5d47ab
4
- data.tar.gz: cc0fa0defc0f00af084672b923ba8610e6e9534a30660d993704e4fb25e95aa6
3
+ metadata.gz: cfa5eeace30a7b2e59d986f4eaa5ed2bea9f5c2e04aa6b029d7ce59ef28e2c6f
4
+ data.tar.gz: c719d7a8e91b4ae909602bacdab03267a341a11430068ba737cc43a79392c63f
5
5
  SHA512:
6
- metadata.gz: 65be87bb471f734897bc3e8664194b65aafb7e6879dc99a0225b65872f0ab65446c85932f3dfa17c04df2e202ff52fe49abaa13cc2e9c537c314df0b5a353140
7
- data.tar.gz: 75a29ced15a2cd880017266ef68014c352581ef72adede0cf44dcff42303b9da033abcbfa73c61b2edf116b0a35671e9e3986f12729fd3ebb34678fa56e3a6ef
6
+ metadata.gz: bd166f563c0c8cda6ee94eacd6292068e4d681539a6f3ce42872896031634ca944a3b538f6e36232b8227c6d8cf1b95d8a94637c06c83d83a801d0fc347a5a0a
7
+ data.tar.gz: 84677613df9260493c5e9ed33e490cf520a21eb50daace16e42503cc7a70885674baa662c015c3f020ba1b076cccba01f75721539d9fec0439751613b8936a0f
@@ -48,6 +48,7 @@ typedef struct sgttyb conmode;
48
48
  # endif
49
49
  #elif defined _WIN32
50
50
  #include <winioctl.h>
51
+ #include <conio.h>
51
52
  typedef DWORD conmode;
52
53
 
53
54
  #define LAST_ERROR rb_w32_map_errno(GetLastError())
@@ -73,7 +74,7 @@ getattr(int fd, conmode *t)
73
74
  #define SET_LAST_ERROR (0)
74
75
  #endif
75
76
 
76
- static ID id_getc, id_console, id_close, id_min, id_time;
77
+ static ID id_getc, id_console, id_close, id_min, id_time, id_intr;
77
78
  #if ENABLE_IO_GETPASS
78
79
  static ID id_gets;
79
80
  #endif
@@ -100,20 +101,33 @@ rb_f_send(int argc, VALUE *argv, VALUE recv)
100
101
  typedef struct {
101
102
  int vmin;
102
103
  int vtime;
104
+ int intr;
103
105
  } rawmode_arg_t;
104
106
 
105
107
  static rawmode_arg_t *
106
- rawmode_opt(int argc, VALUE *argv, rawmode_arg_t *opts)
108
+ rawmode_opt(int *argcp, VALUE *argv, int min_argc, int max_argc, rawmode_arg_t *opts)
107
109
  {
110
+ int argc = *argcp;
108
111
  rawmode_arg_t *optp = NULL;
109
- VALUE vopts;
110
- rb_scan_args(argc, argv, "0:", &vopts);
112
+ VALUE vopts = Qnil;
113
+ if (argc > min_argc) {
114
+ vopts = rb_check_hash_type(argv[argc-1]);
115
+ if (!NIL_P(vopts)) {
116
+ argv[argc-1] = vopts;
117
+ vopts = rb_extract_keywords(&argv[argc-1]);
118
+ if (!argv[argc-1]) *argcp = --argc;
119
+ if (!vopts) vopts = Qnil;
120
+ }
121
+ }
122
+ rb_check_arity(argc, min_argc, max_argc);
111
123
  if (!NIL_P(vopts)) {
112
124
  VALUE vmin = rb_hash_aref(vopts, ID2SYM(id_min));
113
125
  VALUE vtime = rb_hash_aref(vopts, ID2SYM(id_time));
126
+ VALUE intr = rb_hash_aref(vopts, ID2SYM(id_intr));
114
127
  /* default values by `stty raw` */
115
128
  opts->vmin = 1;
116
129
  opts->vtime = 0;
130
+ opts->intr = 0;
117
131
  if (!NIL_P(vmin)) {
118
132
  opts->vmin = NUM2INT(vmin);
119
133
  optp = opts;
@@ -124,6 +138,21 @@ rawmode_opt(int argc, VALUE *argv, rawmode_arg_t *opts)
124
138
  opts->vtime = NUM2INT(vtime);
125
139
  optp = opts;
126
140
  }
141
+ switch (intr) {
142
+ case Qtrue:
143
+ opts->intr = 1;
144
+ optp = opts;
145
+ break;
146
+ case Qfalse:
147
+ opts->intr = 0;
148
+ optp = opts;
149
+ break;
150
+ case Qnil:
151
+ break;
152
+ default:
153
+ rb_raise(rb_eArgError, "true or false expected as intr: %"PRIsVALUE,
154
+ intr);
155
+ }
127
156
  }
128
157
  return optp;
129
158
  }
@@ -146,13 +175,21 @@ set_rawmode(conmode *t, void *arg)
146
175
  #elif defined _WIN32
147
176
  *t = 0;
148
177
  #endif
149
- #if defined HAVE_TERMIOS_H || defined HAVE_TERMIO_H
150
178
  if (arg) {
151
179
  const rawmode_arg_t *r = arg;
180
+ #ifdef VMIN
152
181
  if (r->vmin >= 0) t->c_cc[VMIN] = r->vmin;
182
+ #endif
183
+ #ifdef VTIME
153
184
  if (r->vtime >= 0) t->c_cc[VTIME] = r->vtime;
154
- }
155
185
  #endif
186
+ #ifdef ISIG
187
+ if (r->intr) {
188
+ t->c_iflag |= BRKINT|IXON;
189
+ t->c_lflag |= ISIG|IEXTEN;
190
+ }
191
+ #endif
192
+ }
156
193
  }
157
194
 
158
195
  static void
@@ -232,7 +269,7 @@ get_write_fd(const rb_io_t *fptr)
232
269
  #define FD_PER_IO 2
233
270
 
234
271
  static VALUE
235
- ttymode(VALUE io, VALUE (*func)(VALUE), void (*setter)(conmode *, void *), void *arg)
272
+ ttymode(VALUE io, VALUE (*func)(VALUE), VALUE farg, void (*setter)(conmode *, void *), void *arg)
236
273
  {
237
274
  rb_io_t *fptr;
238
275
  int status = -1;
@@ -263,7 +300,7 @@ ttymode(VALUE io, VALUE (*func)(VALUE), void (*setter)(conmode *, void *), void
263
300
  }
264
301
  }
265
302
  if (status == 0) {
266
- result = rb_protect(func, io, &status);
303
+ result = rb_protect(func, farg, &status);
267
304
  }
268
305
  GetOpenFile(io, fptr);
269
306
  if (fd[0] != -1 && fd[0] == GetReadFD(fptr)) {
@@ -287,6 +324,31 @@ ttymode(VALUE io, VALUE (*func)(VALUE), void (*setter)(conmode *, void *), void
287
324
  return result;
288
325
  }
289
326
 
327
+ #if !defined _WIN32
328
+ struct ttymode_callback_args {
329
+ VALUE (*func)(VALUE, VALUE);
330
+ VALUE io;
331
+ VALUE farg;
332
+ };
333
+
334
+ static VALUE
335
+ ttymode_callback(VALUE args)
336
+ {
337
+ struct ttymode_callback_args *argp = (struct ttymode_callback_args *)args;
338
+ return argp->func(argp->io, argp->farg);
339
+ }
340
+
341
+ static VALUE
342
+ ttymode_with_io(VALUE io, VALUE (*func)(VALUE, VALUE), VALUE farg, void (*setter)(conmode *, void *), void *arg)
343
+ {
344
+ struct ttymode_callback_args cargs;
345
+ cargs.func = func;
346
+ cargs.io = io;
347
+ cargs.farg = farg;
348
+ return ttymode(io, ttymode_callback, (VALUE)&cargs, setter, arg);
349
+ }
350
+ #endif
351
+
290
352
  /*
291
353
  * call-seq:
292
354
  * io.raw(min: nil, time: nil) {|io| }
@@ -310,8 +372,8 @@ ttymode(VALUE io, VALUE (*func)(VALUE), void (*setter)(conmode *, void *), void
310
372
  static VALUE
311
373
  console_raw(int argc, VALUE *argv, VALUE io)
312
374
  {
313
- rawmode_arg_t opts, *optp = rawmode_opt(argc, argv, &opts);
314
- return ttymode(io, rb_yield, set_rawmode, optp);
375
+ rawmode_arg_t opts, *optp = rawmode_opt(&argc, argv, 0, 0, &opts);
376
+ return ttymode(io, rb_yield, io, set_rawmode, optp);
315
377
  }
316
378
 
317
379
  /*
@@ -332,7 +394,7 @@ console_set_raw(int argc, VALUE *argv, VALUE io)
332
394
  conmode t;
333
395
  rb_io_t *fptr;
334
396
  int fd;
335
- rawmode_arg_t opts, *optp = rawmode_opt(argc, argv, &opts);
397
+ rawmode_arg_t opts, *optp = rawmode_opt(&argc, argv, 0, 0, &opts);
336
398
 
337
399
  GetOpenFile(io, fptr);
338
400
  fd = GetReadFD(fptr);
@@ -357,7 +419,7 @@ console_set_raw(int argc, VALUE *argv, VALUE io)
357
419
  static VALUE
358
420
  console_cooked(VALUE io)
359
421
  {
360
- return ttymode(io, rb_yield, set_cookedmode, NULL);
422
+ return ttymode(io, rb_yield, io, set_cookedmode, NULL);
361
423
  }
362
424
 
363
425
  /*
@@ -385,11 +447,35 @@ console_set_cooked(VALUE io)
385
447
  return io;
386
448
  }
387
449
 
450
+ #ifndef _WIN32
388
451
  static VALUE
389
452
  getc_call(VALUE io)
390
453
  {
391
454
  return rb_funcallv(io, id_getc, 0, 0);
392
455
  }
456
+ #else
457
+ static VALUE
458
+ nogvl_getch(void *p)
459
+ {
460
+ int len = 0;
461
+ wint_t *buf = p, c = _getwch();
462
+
463
+ switch (c) {
464
+ case WEOF:
465
+ break;
466
+ return (VALUE)0;
467
+ case 0x00:
468
+ case 0xe0:
469
+ buf[len++] = c;
470
+ c = _getwch();
471
+ /* fall through */
472
+ default:
473
+ buf[len++] = c;
474
+ break;
475
+ }
476
+ return (VALUE)len;
477
+ }
478
+ #endif
393
479
 
394
480
  /*
395
481
  * call-seq:
@@ -404,8 +490,53 @@ getc_call(VALUE io)
404
490
  static VALUE
405
491
  console_getch(int argc, VALUE *argv, VALUE io)
406
492
  {
407
- rawmode_arg_t opts, *optp = rawmode_opt(argc, argv, &opts);
408
- return ttymode(io, getc_call, set_rawmode, optp);
493
+ rawmode_arg_t opts, *optp = rawmode_opt(&argc, argv, 0, 0, &opts);
494
+ #ifndef _WIN32
495
+ return ttymode(io, getc_call, io, set_rawmode, optp);
496
+ #else
497
+ rb_io_t *fptr;
498
+ VALUE str;
499
+ wint_t c;
500
+ int w, len;
501
+ char buf[8];
502
+ wint_t wbuf[2];
503
+ struct timeval *to = NULL, tv;
504
+
505
+ GetOpenFile(io, fptr);
506
+ if (optp) {
507
+ if (optp->vtime) {
508
+ to = &tv;
509
+ tv.tv_sec = optp->vtime / 10;
510
+ tv.tv_usec = (optp->vtime % 10) * 100000;
511
+ }
512
+ if (optp->vmin != 1) {
513
+ rb_warning("min option ignored");
514
+ }
515
+ if (optp->intr) {
516
+ w = rb_wait_for_single_fd(fptr->fd, RB_WAITFD_IN, to);
517
+ if (w < 0) rb_eof_error();
518
+ if (!(w & RB_WAITFD_IN)) return Qnil;
519
+ }
520
+ }
521
+ len = (int)rb_thread_io_blocking_region(nogvl_getch, wbuf, fptr->fd);
522
+ switch (len) {
523
+ case 0:
524
+ return Qnil;
525
+ case 2:
526
+ buf[0] = (char)wbuf[0];
527
+ c = wbuf[1];
528
+ len = 1;
529
+ do {
530
+ buf[len++] = (unsigned char)c;
531
+ } while ((c >>= CHAR_BIT) && len < (int)sizeof(buf));
532
+ return rb_str_new(buf, len);
533
+ default:
534
+ c = wbuf[0];
535
+ len = rb_uv_to_utf8(buf, c);
536
+ str = rb_utf8_str_new(buf, len);
537
+ return rb_str_conv_enc(str, NULL, rb_default_external_encoding());
538
+ }
539
+ #endif
409
540
  }
410
541
 
411
542
  /*
@@ -423,7 +554,7 @@ console_getch(int argc, VALUE *argv, VALUE io)
423
554
  static VALUE
424
555
  console_noecho(VALUE io)
425
556
  {
426
- return ttymode(io, rb_yield, set_noecho, NULL);
557
+ return ttymode(io, rb_yield, io, set_noecho, NULL);
427
558
  }
428
559
 
429
560
  /*
@@ -475,6 +606,115 @@ console_echo_p(VALUE io)
475
606
  return echo_p(&t) ? Qtrue : Qfalse;
476
607
  }
477
608
 
609
+ static const rb_data_type_t conmode_type = {
610
+ "console-mode",
611
+ {0, RUBY_TYPED_DEFAULT_FREE,},
612
+ 0, 0,
613
+ RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
614
+ };
615
+ static VALUE cConmode;
616
+
617
+ static VALUE
618
+ conmode_alloc(VALUE klass)
619
+ {
620
+ return rb_data_typed_object_zalloc(klass, sizeof(conmode), &conmode_type);
621
+ }
622
+
623
+ static VALUE
624
+ conmode_new(VALUE klass, const conmode *t)
625
+ {
626
+ VALUE obj = conmode_alloc(klass);
627
+ *(conmode *)DATA_PTR(obj) = *t;
628
+ return obj;
629
+ }
630
+
631
+ static VALUE
632
+ conmode_init_copy(VALUE obj, VALUE obj2)
633
+ {
634
+ conmode *t = rb_check_typeddata(obj, &conmode_type);
635
+ conmode *t2 = rb_check_typeddata(obj2, &conmode_type);
636
+ *t = *t2;
637
+ return obj;
638
+ }
639
+
640
+ static VALUE
641
+ conmode_set_echo(VALUE obj, VALUE f)
642
+ {
643
+ conmode *t = rb_check_typeddata(obj, &conmode_type);
644
+ if (RTEST(f))
645
+ set_echo(t, NULL);
646
+ else
647
+ set_noecho(t, NULL);
648
+ return obj;
649
+ }
650
+
651
+ static VALUE
652
+ conmode_set_raw(int argc, VALUE *argv, VALUE obj)
653
+ {
654
+ conmode *t = rb_check_typeddata(obj, &conmode_type);
655
+ rawmode_arg_t opts, *optp = rawmode_opt(&argc, argv, 0, 0, &opts);
656
+
657
+ set_rawmode(t, optp);
658
+ return obj;
659
+ }
660
+
661
+ static VALUE
662
+ conmode_raw_new(int argc, VALUE *argv, VALUE obj)
663
+ {
664
+ conmode *r = rb_check_typeddata(obj, &conmode_type);
665
+ conmode t = *r;
666
+ rawmode_arg_t opts, *optp = rawmode_opt(&argc, argv, 0, 0, &opts);
667
+
668
+ set_rawmode(&t, optp);
669
+ return conmode_new(rb_obj_class(obj), &t);
670
+ }
671
+
672
+ /*
673
+ * call-seq:
674
+ * io.console_mode -> mode
675
+ *
676
+ * Returns a data represents the current console mode.
677
+ *
678
+ * You must require 'io/console' to use this method.
679
+ */
680
+ static VALUE
681
+ console_conmode_get(VALUE io)
682
+ {
683
+ conmode t;
684
+ rb_io_t *fptr;
685
+ int fd;
686
+
687
+ GetOpenFile(io, fptr);
688
+ fd = GetReadFD(fptr);
689
+ if (!getattr(fd, &t)) rb_sys_fail(0);
690
+
691
+ return conmode_new(cConmode, &t);
692
+ }
693
+
694
+ /*
695
+ * call-seq:
696
+ * io.console_mode = mode
697
+ *
698
+ * Sets the console mode to +mode+.
699
+ *
700
+ * You must require 'io/console' to use this method.
701
+ */
702
+ static VALUE
703
+ console_conmode_set(VALUE io, VALUE mode)
704
+ {
705
+ conmode *t, r;
706
+ rb_io_t *fptr;
707
+ int fd;
708
+
709
+ TypedData_Get_Struct(mode, conmode, &conmode_type, t);
710
+ r = *t;
711
+ GetOpenFile(io, fptr);
712
+ fd = GetReadFD(fptr);
713
+ if (!setattr(fd, &r)) rb_sys_fail(0);
714
+
715
+ return mode;
716
+ }
717
+
478
718
  #if defined TIOCGWINSZ
479
719
  typedef struct winsize rb_console_size_t;
480
720
  #define getwinsize(fd, buf) (ioctl((fd), TIOCGWINSZ, (buf)) == 0)
@@ -593,6 +833,30 @@ console_set_winsize(VALUE io, VALUE size)
593
833
  }
594
834
  #endif
595
835
 
836
+ #ifdef _WIN32
837
+ static VALUE
838
+ console_check_winsize_changed(VALUE io)
839
+ {
840
+ rb_io_t *fptr;
841
+ HANDLE h;
842
+ DWORD num;
843
+
844
+ GetOpenFile(io, fptr);
845
+ h = (HANDLE)rb_w32_get_osfhandle(GetReadFD(fptr));
846
+ while (GetNumberOfConsoleInputEvents(h, &num) && num > 0) {
847
+ INPUT_RECORD rec;
848
+ if (ReadConsoleInput(h, &rec, 1, &num)) {
849
+ if (rec.EventType == WINDOW_BUFFER_SIZE_EVENT) {
850
+ rb_yield(Qnil);
851
+ }
852
+ }
853
+ }
854
+ return io;
855
+ }
856
+ #else
857
+ #define console_check_winsize_changed rb_f_notimplement
858
+ #endif
859
+
596
860
  /*
597
861
  * call-seq:
598
862
  * io.iflush
@@ -688,9 +952,24 @@ console_beep(VALUE io)
688
952
  return io;
689
953
  }
690
954
 
955
+ static int
956
+ mode_in_range(VALUE val, int high, const char *modename)
957
+ {
958
+ int mode;
959
+ if (NIL_P(val)) return 0;
960
+ if (!RB_INTEGER_TYPE_P(val)) {
961
+ wrong_value:
962
+ rb_raise(rb_eArgError, "wrong %s mode: %"PRIsVALUE, modename, val);
963
+ }
964
+ if ((mode = NUM2INT(val)) < 0 || mode > high) {
965
+ goto wrong_value;
966
+ }
967
+ return mode;
968
+ }
969
+
691
970
  #if defined _WIN32
692
971
  static VALUE
693
- console_goto(VALUE io, VALUE x, VALUE y)
972
+ console_goto(VALUE io, VALUE y, VALUE x)
694
973
  {
695
974
  rb_io_t *fptr;
696
975
  int fd;
@@ -718,15 +997,159 @@ console_cursor_pos(VALUE io)
718
997
  if (!GetConsoleScreenBufferInfo((HANDLE)rb_w32_get_osfhandle(fd), &ws)) {
719
998
  rb_syserr_fail(LAST_ERROR, 0);
720
999
  }
721
- return rb_assoc_new(UINT2NUM(ws.dwCursorPosition.X), UINT2NUM(ws.dwCursorPosition.Y));
1000
+ return rb_assoc_new(UINT2NUM(ws.dwCursorPosition.Y), UINT2NUM(ws.dwCursorPosition.X));
722
1001
  }
723
1002
 
724
1003
  static VALUE
725
- console_cursor_set(VALUE io, VALUE cpos)
1004
+ console_move(VALUE io, int y, int x)
726
1005
  {
727
- cpos = rb_convert_type(cpos, T_ARRAY, "Array", "to_ary");
728
- if (RARRAY_LEN(cpos) != 2) rb_raise(rb_eArgError, "expected 2D coordinate");
729
- return console_goto(io, RARRAY_AREF(cpos, 0), RARRAY_AREF(cpos, 1));
1006
+ rb_io_t *fptr;
1007
+ HANDLE h;
1008
+ rb_console_size_t ws;
1009
+ COORD *pos = &ws.dwCursorPosition;
1010
+
1011
+ GetOpenFile(io, fptr);
1012
+ h = (HANDLE)rb_w32_get_osfhandle(GetWriteFD(fptr));
1013
+ if (!GetConsoleScreenBufferInfo(h, &ws)) {
1014
+ rb_syserr_fail(LAST_ERROR, 0);
1015
+ }
1016
+ pos->X += x;
1017
+ pos->Y += y;
1018
+ if (!SetConsoleCursorPosition(h, *pos)) {
1019
+ rb_syserr_fail(LAST_ERROR, 0);
1020
+ }
1021
+ return io;
1022
+ }
1023
+
1024
+ static VALUE
1025
+ console_goto_column(VALUE io, VALUE val)
1026
+ {
1027
+ rb_io_t *fptr;
1028
+ HANDLE h;
1029
+ rb_console_size_t ws;
1030
+ COORD *pos = &ws.dwCursorPosition;
1031
+
1032
+ GetOpenFile(io, fptr);
1033
+ h = (HANDLE)rb_w32_get_osfhandle(GetWriteFD(fptr));
1034
+ if (!GetConsoleScreenBufferInfo(h, &ws)) {
1035
+ rb_syserr_fail(LAST_ERROR, 0);
1036
+ }
1037
+ pos->X = NUM2INT(val);
1038
+ if (!SetConsoleCursorPosition(h, *pos)) {
1039
+ rb_syserr_fail(LAST_ERROR, 0);
1040
+ }
1041
+ return io;
1042
+ }
1043
+
1044
+ static void
1045
+ constat_clear(HANDLE handle, WORD attr, DWORD len, COORD pos)
1046
+ {
1047
+ DWORD written;
1048
+
1049
+ FillConsoleOutputAttribute(handle, attr, len, pos, &written);
1050
+ FillConsoleOutputCharacterW(handle, L' ', len, pos, &written);
1051
+ }
1052
+
1053
+ static VALUE
1054
+ console_erase_line(VALUE io, VALUE val)
1055
+ {
1056
+ rb_io_t *fptr;
1057
+ HANDLE h;
1058
+ rb_console_size_t ws;
1059
+ COORD *pos = &ws.dwCursorPosition;
1060
+ DWORD w;
1061
+ int mode = mode_in_range(val, 2, "line erase");
1062
+
1063
+ GetOpenFile(io, fptr);
1064
+ h = (HANDLE)rb_w32_get_osfhandle(GetWriteFD(fptr));
1065
+ if (!GetConsoleScreenBufferInfo(h, &ws)) {
1066
+ rb_syserr_fail(LAST_ERROR, 0);
1067
+ }
1068
+ w = winsize_col(&ws);
1069
+ switch (mode) {
1070
+ case 0: /* after cursor */
1071
+ w -= pos->X;
1072
+ break;
1073
+ case 1: /* before *and* cursor */
1074
+ w = pos->X + 1;
1075
+ pos->X = 0;
1076
+ break;
1077
+ case 2: /* entire line */
1078
+ pos->X = 0;
1079
+ break;
1080
+ }
1081
+ constat_clear(h, ws.wAttributes, w, *pos);
1082
+ return io;
1083
+ }
1084
+
1085
+ static VALUE
1086
+ console_erase_screen(VALUE io, VALUE val)
1087
+ {
1088
+ rb_io_t *fptr;
1089
+ HANDLE h;
1090
+ rb_console_size_t ws;
1091
+ COORD *pos = &ws.dwCursorPosition;
1092
+ DWORD w;
1093
+ int mode = mode_in_range(val, 3, "screen erase");
1094
+
1095
+ GetOpenFile(io, fptr);
1096
+ h = (HANDLE)rb_w32_get_osfhandle(GetWriteFD(fptr));
1097
+ if (!GetConsoleScreenBufferInfo(h, &ws)) {
1098
+ rb_syserr_fail(LAST_ERROR, 0);
1099
+ }
1100
+ w = winsize_col(&ws);
1101
+ switch (mode) {
1102
+ case 0: /* erase after cursor */
1103
+ w = (w * (ws.srWindow.Bottom - pos->Y + 1) - pos->X);
1104
+ break;
1105
+ case 1: /* erase before *and* cursor */
1106
+ w = (w * (pos->Y - ws.srWindow.Top) + pos->X + 1);
1107
+ pos->X = 0;
1108
+ pos->Y = ws.srWindow.Top;
1109
+ break;
1110
+ case 2: /* erase entire screen */
1111
+ w = (w * winsize_row(&ws));
1112
+ pos->X = 0;
1113
+ pos->Y = ws.srWindow.Top;
1114
+ break;
1115
+ case 3: /* erase entire screen */
1116
+ w = (w * ws.dwSize.Y);
1117
+ pos->X = 0;
1118
+ pos->Y = 0;
1119
+ break;
1120
+ }
1121
+ constat_clear(h, ws.wAttributes, w, *pos);
1122
+ return io;
1123
+ }
1124
+
1125
+ static VALUE
1126
+ console_scroll(VALUE io, int line)
1127
+ {
1128
+ rb_io_t *fptr;
1129
+ HANDLE h;
1130
+ rb_console_size_t ws;
1131
+
1132
+ GetOpenFile(io, fptr);
1133
+ h = (HANDLE)rb_w32_get_osfhandle(GetWriteFD(fptr));
1134
+ if (!GetConsoleScreenBufferInfo(h, &ws)) {
1135
+ rb_syserr_fail(LAST_ERROR, 0);
1136
+ }
1137
+ if (line) {
1138
+ SMALL_RECT scroll;
1139
+ COORD destination;
1140
+ CHAR_INFO fill;
1141
+ scroll.Left = 0;
1142
+ scroll.Top = line > 0 ? line : 0;
1143
+ scroll.Right = winsize_col(&ws) - 1;
1144
+ scroll.Bottom = winsize_row(&ws) - 1 + (line < 0 ? line : 0);
1145
+ destination.X = 0;
1146
+ destination.Y = line < 0 ? -line : 0;
1147
+ fill.Char.UnicodeChar = L' ';
1148
+ fill.Attributes = ws.wAttributes;
1149
+
1150
+ ScrollConsoleScreenBuffer(h, &scroll, NULL, destination, &fill);
1151
+ }
1152
+ return io;
730
1153
  }
731
1154
 
732
1155
  #include "win32_vk.inc"
@@ -757,12 +1180,210 @@ console_key_pressed_p(VALUE io, VALUE k)
757
1180
  return GetKeyState(vk) & 0x80 ? Qtrue : Qfalse;
758
1181
  }
759
1182
  #else
760
- # define console_goto rb_f_notimplement
761
- # define console_cursor_pos rb_f_notimplement
762
- # define console_cursor_set rb_f_notimplement
1183
+ struct query_args {
1184
+ const char *qstr;
1185
+ int opt;
1186
+ };
1187
+
1188
+ static int
1189
+ direct_query(VALUE io, const struct query_args *query)
1190
+ {
1191
+ if (RB_TYPE_P(io, T_FILE)) {
1192
+ rb_io_t *fptr;
1193
+ VALUE wio;
1194
+ GetOpenFile(io, fptr);
1195
+ wio = fptr->tied_io_for_writing;
1196
+ if (wio) {
1197
+ VALUE s = rb_str_new_cstr(query->qstr);
1198
+ rb_io_write(wio, s);
1199
+ rb_io_flush(wio);
1200
+ return 1;
1201
+ }
1202
+ if (write(fptr->fd, query->qstr, strlen(query->qstr)) != -1) {
1203
+ return 1;
1204
+ }
1205
+ if (fptr->fd == 0 &&
1206
+ write(1, query->qstr, strlen(query->qstr)) != -1) {
1207
+ return 1;
1208
+ }
1209
+ }
1210
+ return 0;
1211
+ }
1212
+
1213
+ static VALUE
1214
+ read_vt_response(VALUE io, VALUE query)
1215
+ {
1216
+ struct query_args *qargs = (struct query_args *)query;
1217
+ VALUE result, b;
1218
+ int opt = 0;
1219
+ int num = 0;
1220
+ if (qargs) {
1221
+ opt = qargs->opt;
1222
+ if (!direct_query(io, qargs)) return Qnil;
1223
+ }
1224
+ if (rb_io_getbyte(io) != INT2FIX(0x1b)) return Qnil;
1225
+ if (rb_io_getbyte(io) != INT2FIX('[')) return Qnil;
1226
+ result = rb_ary_new();
1227
+ while (!NIL_P(b = rb_io_getbyte(io))) {
1228
+ int c = NUM2UINT(b);
1229
+ if (c == ';') {
1230
+ rb_ary_push(result, INT2NUM(num));
1231
+ num = 0;
1232
+ }
1233
+ else if (ISDIGIT(c)) {
1234
+ num = num * 10 + c - '0';
1235
+ }
1236
+ else if (opt && c == opt) {
1237
+ opt = 0;
1238
+ }
1239
+ else {
1240
+ char last = (char)c;
1241
+ rb_ary_push(result, INT2NUM(num));
1242
+ b = rb_str_new(&last, 1);
1243
+ break;
1244
+ }
1245
+ }
1246
+ return rb_ary_push(result, b);
1247
+ }
1248
+
1249
+ static VALUE
1250
+ console_vt_response(int argc, VALUE *argv, VALUE io, const struct query_args *qargs)
1251
+ {
1252
+ rawmode_arg_t opts, *optp = rawmode_opt(&argc, argv, 0, 1, &opts);
1253
+ VALUE query = (VALUE)qargs;
1254
+ VALUE ret = ttymode_with_io(io, read_vt_response, query, set_rawmode, optp);
1255
+ return ret;
1256
+ }
1257
+
1258
+ static VALUE
1259
+ console_cursor_pos(VALUE io)
1260
+ {
1261
+ static const struct query_args query = {"\033[6n", 0};
1262
+ VALUE resp = console_vt_response(0, 0, io, &query);
1263
+ VALUE row, column, term;
1264
+ unsigned int r, c;
1265
+ if (!RB_TYPE_P(resp, T_ARRAY) || RARRAY_LEN(resp) != 3) return Qnil;
1266
+ term = RARRAY_AREF(resp, 2);
1267
+ if (!RB_TYPE_P(term, T_STRING) || RSTRING_LEN(term) != 1) return Qnil;
1268
+ if (RSTRING_PTR(term)[0] != 'R') return Qnil;
1269
+ row = RARRAY_AREF(resp, 0);
1270
+ column = RARRAY_AREF(resp, 1);
1271
+ rb_ary_resize(resp, 2);
1272
+ r = NUM2UINT(row) - 1;
1273
+ c = NUM2UINT(column) - 1;
1274
+ RARRAY_ASET(resp, 0, INT2NUM(r));
1275
+ RARRAY_ASET(resp, 1, INT2NUM(c));
1276
+ return resp;
1277
+ }
1278
+
1279
+ static VALUE
1280
+ console_goto(VALUE io, VALUE y, VALUE x)
1281
+ {
1282
+ rb_io_write(io, rb_sprintf("\x1b[%d;%dH", NUM2UINT(y)+1, NUM2UINT(x)+1));
1283
+ return io;
1284
+ }
1285
+
1286
+ static VALUE
1287
+ console_move(VALUE io, int y, int x)
1288
+ {
1289
+ if (x || y) {
1290
+ VALUE s = rb_str_new_cstr("");
1291
+ if (y) rb_str_catf(s, "\x1b[%d%c", y < 0 ? -y : y, y < 0 ? 'A' : 'B');
1292
+ if (x) rb_str_catf(s, "\x1b[%d%c", x < 0 ? -x : x, x < 0 ? 'D' : 'C');
1293
+ rb_io_write(io, s);
1294
+ rb_io_flush(io);
1295
+ }
1296
+ return io;
1297
+ }
1298
+
1299
+ static VALUE
1300
+ console_goto_column(VALUE io, VALUE val)
1301
+ {
1302
+ rb_io_write(io, rb_sprintf("\x1b[%dG", NUM2UINT(val)+1));
1303
+ return io;
1304
+ }
1305
+
1306
+ static VALUE
1307
+ console_erase_line(VALUE io, VALUE val)
1308
+ {
1309
+ int mode = mode_in_range(val, 2, "line erase");
1310
+ rb_io_write(io, rb_sprintf("\x1b[%dK", mode));
1311
+ return io;
1312
+ }
1313
+
1314
+ static VALUE
1315
+ console_erase_screen(VALUE io, VALUE val)
1316
+ {
1317
+ int mode = mode_in_range(val, 3, "screen erase");
1318
+ rb_io_write(io, rb_sprintf("\x1b[%dJ", mode));
1319
+ return io;
1320
+ }
1321
+
1322
+ static VALUE
1323
+ console_scroll(VALUE io, int line)
1324
+ {
1325
+ if (line) {
1326
+ VALUE s = rb_sprintf("\x1b[%d%c", line < 0 ? -line : line,
1327
+ line < 0 ? 'T' : 'S');
1328
+ rb_io_write(io, s);
1329
+ }
1330
+ return io;
1331
+ }
763
1332
  # define console_key_pressed_p rb_f_notimplement
764
1333
  #endif
765
1334
 
1335
+ static VALUE
1336
+ console_cursor_set(VALUE io, VALUE cpos)
1337
+ {
1338
+ cpos = rb_convert_type(cpos, T_ARRAY, "Array", "to_ary");
1339
+ if (RARRAY_LEN(cpos) != 2) rb_raise(rb_eArgError, "expected 2D coordinate");
1340
+ return console_goto(io, RARRAY_AREF(cpos, 0), RARRAY_AREF(cpos, 1));
1341
+ }
1342
+
1343
+ static VALUE
1344
+ console_cursor_up(VALUE io, VALUE val)
1345
+ {
1346
+ return console_move(io, -NUM2INT(val), 0);
1347
+ }
1348
+
1349
+ static VALUE
1350
+ console_cursor_down(VALUE io, VALUE val)
1351
+ {
1352
+ return console_move(io, +NUM2INT(val), 0);
1353
+ }
1354
+
1355
+ static VALUE
1356
+ console_cursor_left(VALUE io, VALUE val)
1357
+ {
1358
+ return console_move(io, 0, -NUM2INT(val));
1359
+ }
1360
+
1361
+ static VALUE
1362
+ console_cursor_right(VALUE io, VALUE val)
1363
+ {
1364
+ return console_move(io, 0, +NUM2INT(val));
1365
+ }
1366
+
1367
+ static VALUE
1368
+ console_scroll_forward(VALUE io, VALUE val)
1369
+ {
1370
+ return console_scroll(io, +NUM2INT(val));
1371
+ }
1372
+
1373
+ static VALUE
1374
+ console_scroll_backward(VALUE io, VALUE val)
1375
+ {
1376
+ return console_scroll(io, -NUM2INT(val));
1377
+ }
1378
+
1379
+ static VALUE
1380
+ console_clear_screen(VALUE io)
1381
+ {
1382
+ console_erase_screen(io, INT2FIX(2));
1383
+ console_goto(io, INT2FIX(0), INT2FIX(0));
1384
+ return io;
1385
+ }
1386
+
766
1387
  /*
767
1388
  * call-seq:
768
1389
  * IO.console -> #<File:/dev/tty>
@@ -882,7 +1503,7 @@ puts_call(VALUE io)
882
1503
  static VALUE
883
1504
  getpass_call(VALUE io)
884
1505
  {
885
- return ttymode(io, rb_io_gets, set_noecho, NULL);
1506
+ return ttymode(io, rb_io_gets, io, set_noecho, NULL);
886
1507
  }
887
1508
 
888
1509
  static void
@@ -891,7 +1512,6 @@ prompt(int argc, VALUE *argv, VALUE io)
891
1512
  if (argc > 0 && !NIL_P(argv[0])) {
892
1513
  VALUE str = argv[0];
893
1514
  StringValueCStr(str);
894
- rb_check_safe_obj(str);
895
1515
  rb_io_write(io, str);
896
1516
  }
897
1517
  }
@@ -961,6 +1581,7 @@ Init_console(void)
961
1581
  id_close = rb_intern("close");
962
1582
  id_min = rb_intern("min");
963
1583
  id_time = rb_intern("time");
1584
+ id_intr = rb_intern("intr");
964
1585
  #ifndef HAVE_RB_F_SEND
965
1586
  id___send__ = rb_intern("__send__");
966
1587
  #endif
@@ -977,6 +1598,8 @@ InitVM_console(void)
977
1598
  rb_define_method(rb_cIO, "getch", console_getch, -1);
978
1599
  rb_define_method(rb_cIO, "echo=", console_set_echo, 1);
979
1600
  rb_define_method(rb_cIO, "echo?", console_echo_p, 0);
1601
+ rb_define_method(rb_cIO, "console_mode", console_conmode_get, 0);
1602
+ rb_define_method(rb_cIO, "console_mode=", console_conmode_set, 1);
980
1603
  rb_define_method(rb_cIO, "noecho", console_noecho, 0);
981
1604
  rb_define_method(rb_cIO, "winsize", console_winsize, 0);
982
1605
  rb_define_method(rb_cIO, "winsize=", console_set_winsize, 1);
@@ -987,7 +1610,18 @@ InitVM_console(void)
987
1610
  rb_define_method(rb_cIO, "goto", console_goto, 2);
988
1611
  rb_define_method(rb_cIO, "cursor", console_cursor_pos, 0);
989
1612
  rb_define_method(rb_cIO, "cursor=", console_cursor_set, 1);
1613
+ rb_define_method(rb_cIO, "cursor_up", console_cursor_up, 1);
1614
+ rb_define_method(rb_cIO, "cursor_down", console_cursor_down, 1);
1615
+ rb_define_method(rb_cIO, "cursor_left", console_cursor_left, 1);
1616
+ rb_define_method(rb_cIO, "cursor_right", console_cursor_right, 1);
1617
+ rb_define_method(rb_cIO, "goto_column", console_goto_column, 1);
1618
+ rb_define_method(rb_cIO, "erase_line", console_erase_line, 1);
1619
+ rb_define_method(rb_cIO, "erase_screen", console_erase_screen, 1);
1620
+ rb_define_method(rb_cIO, "scroll_forward", console_scroll_forward, 1);
1621
+ rb_define_method(rb_cIO, "scroll_backward", console_scroll_backward, 1);
1622
+ rb_define_method(rb_cIO, "clear_screen", console_clear_screen, 0);
990
1623
  rb_define_method(rb_cIO, "pressed?", console_key_pressed_p, 1);
1624
+ rb_define_method(rb_cIO, "check_winsize_changed", console_check_winsize_changed, 0);
991
1625
  #if ENABLE_IO_GETPASS
992
1626
  rb_define_method(rb_cIO, "getpass", console_getpass, -1);
993
1627
  #endif
@@ -999,4 +1633,15 @@ InitVM_console(void)
999
1633
  rb_define_method(mReadable, "getpass", io_getpass, -1);
1000
1634
  #endif
1001
1635
  }
1636
+ {
1637
+ /* :stopdoc: */
1638
+ cConmode = rb_define_class_under(rb_cIO, "ConsoleMode", rb_cObject);
1639
+ rb_define_alloc_func(cConmode, conmode_alloc);
1640
+ rb_undef_method(cConmode, "initialize");
1641
+ rb_define_method(cConmode, "initialize_copy", conmode_init_copy, 1);
1642
+ rb_define_method(cConmode, "echo=", conmode_set_echo, 1);
1643
+ rb_define_method(cConmode, "raw!", conmode_set_raw, -1);
1644
+ rb_define_method(cConmode, "raw", conmode_raw_new, -1);
1645
+ /* :startdoc: */
1646
+ }
1002
1647
  }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: io-console
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.9
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nobu Nakada
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-12-14 00:00:00.000000000 Z
11
+ date: 2019-06-05 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: add console capabilities to IO instances.
14
14
  email: nobu@ruby-lang.org
@@ -43,7 +43,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
43
43
  - !ruby/object:Gem::Version
44
44
  version: '0'
45
45
  requirements: []
46
- rubygems_version: 3.1.0.pre1
46
+ rubygems_version: 3.1.0.pre3
47
47
  signing_key:
48
48
  specification_version: 4
49
49
  summary: Console interface