io-console 0.4.9 → 0.5.5

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