io-console 0.4.8 → 0.5.4

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