io-console 0.4.8 → 0.5.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e47d9d1d48607091ca031404108b70d572c2007e313deaf5264d310159563dbb
4
- data.tar.gz: 7e7c494fe8011cd0a5e26741c7529eb3e574d7326c65cc9e3c1410170c07291e
3
+ metadata.gz: 9474fd3d0f8b7760a83e50da0e69ba4e018e96935018a22b933a3f9eec648a5b
4
+ data.tar.gz: dc4f448e975155e39360f95f4442f1c54e067821183e6214e5fd2a5475504879
5
5
  SHA512:
6
- metadata.gz: 43f74f32f96daa78be45a1a8584fe11ed71ab8576c8602b70f603e5803f97c43b13e0abc20adbd7fffce4337907595fbe2121637b612874a07454af1d9c310ed
7
- data.tar.gz: e4abad7c152fae66c4cecfba8d0a473bb7af25149ed22e2a459a319111295bf3e48be1894489ae8f6d73e21b2c64eccde8a44ffab4d58cf335c28a2f53e749e3
6
+ metadata.gz: 7734614638324ebd6fdf96518d897ebb3e497fae56f5be84d2fbf5098a14923c19445166ccacfa4fcfcdc0a4ea9bdef5c16e627416f44b0d1a7ac93e06b8c073
7
+ data.tar.gz: 8bd122615986429eba3668a56e420a5a242c7d80617b8960566426a99f85c56b80fa5089eb5078016aec36cc5b548a49d7ec44e52699555847e06380359dd36d
@@ -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|IXON;
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.3
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.1
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"