Skip to content

Commit 9c7cb6d

Browse files
committed
Add nonblocking input status
Add `IO#input_pending?` so console users can detect queued input without consuming it or relying on platform-specific APIs.
1 parent dbfb2b0 commit 9c7cb6d

2 files changed

Lines changed: 56 additions & 0 deletions

File tree

ext/io/console/console.c

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -630,6 +630,43 @@ console_getch(int argc, VALUE *argv, VALUE io)
630630
#endif
631631
}
632632

633+
/*
634+
* call-seq:
635+
* io.input_pending? -> true or false
636+
*
637+
* Returns whether input can be read without blocking.
638+
*
639+
* You must require 'io/console' to use this method.
640+
*/
641+
static VALUE
642+
console_input_pending_p(VALUE io)
643+
{
644+
rb_io_t *fptr;
645+
646+
GetOpenFile(io, fptr);
647+
if (rb_io_read_pending(fptr)) return Qtrue;
648+
#ifdef _WIN32
649+
{
650+
DWORD mode;
651+
HANDLE h = (HANDLE)rb_w32_get_osfhandle(GetReadFD(io));
652+
653+
if (GetConsoleMode(h, &mode)) return _kbhit() ? Qtrue : Qfalse;
654+
}
655+
#endif
656+
#if defined HAVE_RB_IO_WAIT
657+
return RTEST(rb_io_wait(io, RB_INT2NUM(RUBY_IO_READABLE), INT2FIX(0))) ? Qtrue : Qfalse;
658+
#else
659+
{
660+
struct timeval timeout = {0, 0};
661+
int result;
662+
663+
result = rb_wait_for_single_fd(fptr->fd, RB_WAITFD_IN, &timeout);
664+
if (result < 0) sys_fail(io);
665+
return (result & RB_WAITFD_IN) ? Qtrue : Qfalse;
666+
}
667+
#endif
668+
}
669+
633670
/*
634671
* call-seq:
635672
* io.noecho {|io| }
@@ -2306,6 +2343,7 @@ InitVM_console(void)
23062343
rb_define_method(rb_cIO, "cooked", console_cooked, 0);
23072344
rb_define_method(rb_cIO, "cooked!", console_set_cooked, 0);
23082345
rb_define_method(rb_cIO, "getch", console_getch, -1);
2346+
rb_define_method(rb_cIO, "input_pending?", console_input_pending_p, 0);
23092347
rb_define_method(rb_cIO, "echo=", console_set_echo, 1);
23102348
rb_define_method(rb_cIO, "echo?", console_echo_p, 0);
23112349
rb_define_method(rb_cIO, "console_mode", console_conmode_get, 0);

test/io/console/test_io_console.rb

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,18 @@ def test_cursor_visibility
454454
end
455455
end unless RbConfig::CONFIG["host_os"] =~ /mswin|mingw/
456456

457+
def test_input_pending
458+
IO.pipe do |read, write|
459+
assert_false(read.input_pending?)
460+
write.write("ab")
461+
assert_true(read.input_pending?)
462+
assert_equal("a", read.getc)
463+
assert_true(read.input_pending?)
464+
assert_equal("b", read.getc)
465+
assert_false(read.input_pending?)
466+
end
467+
end unless RbConfig::CONFIG["host_os"] =~ /mswin|mingw/ || RUBY_ENGINE == "jruby"
468+
457469
def assert_ctrl(expect, cc, r, w)
458470
sleep 0.1
459471
w.print cc
@@ -809,6 +821,12 @@ def test_console_input_events
809821
records = key + resize + mouse + menu + focus
810822
assert_not_equal(0, write_console_input.call(handle, records, 5, written))
811823
assert_equal(5, written.unpack1("L<"))
824+
assert_true(IO.console.input_pending?)
825+
IO.pipe do |read, write|
826+
assert_false(read.input_pending?)
827+
write.write("a")
828+
assert_true(read.input_pending?)
829+
end
812830
ensure
813831
close_handle.call(handle)
814832
end

0 commit comments

Comments
 (0)