Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 83 additions & 4 deletions ext/io/console/console.c
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,22 @@ typedef struct {
# define NIL_OR_UNDEF_P(obj) (NIL_P(obj) || UNDEF_P(obj))
#endif

static int
to_vtime(VALUE vtime)
{
VALUE v10 = INT2FIX(10);
vtime = rb_funcall3(vtime, '*', 1, &v10);
return NUM2INT(vtime);
}

static unsigned char
clamp_uchar(int n)
{
if ((unsigned int)n >= UCHAR_MAX) n = UCHAR_MAX;
else if (n < 0) n = 0;
return (unsigned char)n;
}

static rawmode_arg_t *
rawmode_opt(int *argcp, VALUE *argv, int min_argc, int max_argc, rawmode_arg_t *opts)
{
Expand Down Expand Up @@ -209,13 +225,11 @@ rawmode_opt(int *argcp, VALUE *argv, int min_argc, int max_argc, rawmode_arg_t *
opts->vtime = 0;
opts->intr = 0;
if (!NIL_OR_UNDEF_P(vmin)) {
opts->vmin = NUM2INT(vmin);
opts->vmin = clamp_uchar(NUM2INT(vmin));
optp = opts;
}
if (!NIL_OR_UNDEF_P(vtime)) {
VALUE v10 = INT2FIX(10);
vtime = rb_funcall3(vtime, '*', 1, &v10);
opts->vtime = NUM2INT(vtime);
opts->vtime = clamp_uchar(to_vtime(vtime));
optp = opts;
}
switch (intr) {
Expand Down Expand Up @@ -762,6 +776,13 @@ conmode_init_copy(VALUE obj, VALUE obj2)
return obj;
}

static VALUE
conmode_get_echo(VALUE obj)
{
conmode *t = rb_check_typeddata(obj, &conmode_type);
return echo_p(t) ? Qtrue : Qfalse;
}

static VALUE
conmode_set_echo(VALUE obj, VALUE f)
{
Expand Down Expand Up @@ -794,6 +815,58 @@ conmode_raw_new(int argc, VALUE *argv, VALUE obj)
return conmode_new(rb_obj_class(obj), &t);
}

static VALUE
conmode_get_min(VALUE obj)
{
conmode *t = rb_check_typeddata(obj, &conmode_type);
#ifdef VMIN
return INT2FIX(t->c_cc[VMIN]);
#else
(void)t;
return Qnil;
#endif
}

static VALUE
conmode_set_min(VALUE obj, VALUE min)
{
conmode *t = rb_check_typeddata(obj, &conmode_type);
int vmin = NIL_P(min) ? 1 : clamp_uchar(NUM2INT(min));
#ifdef VMIN
t->c_cc[VMIN] = vmin;
#else
(void)t;
(void)vmin;
#endif
return min;
}

static VALUE
conmode_get_time(VALUE obj)
{
conmode *t = rb_check_typeddata(obj, &conmode_type);
#ifdef VTIME
return rb_rational_new(INT2FIX(t->c_cc[VTIME]), INT2FIX(10));
#else
(void)t;
return Qnil;
#endif
}

static VALUE
conmode_set_time(VALUE obj, VALUE time)
{
conmode *t = rb_check_typeddata(obj, &conmode_type);
int vtime = NIL_P(time) ? 0 : clamp_uchar(to_vtime(time));
#ifdef VTIME
t->c_cc[VTIME] = vtime;
#else
(void)t;
(void)vtime;
#endif
return time;
}

#ifdef _WIN32
/*
* call-seq:
Expand Down Expand Up @@ -2508,9 +2581,15 @@ InitVM_console(void)
rb_define_alloc_func(cConmode, conmode_alloc);
rb_undef_method(cConmode, "initialize");
rb_define_method(cConmode, "initialize_copy", conmode_init_copy, 1);
rb_define_method(cConmode, "echo?", conmode_get_echo, 0);
rb_define_method(cConmode, "echo", conmode_get_echo, 0);
rb_define_method(cConmode, "echo=", conmode_set_echo, 1);
rb_define_method(cConmode, "raw!", conmode_set_raw, -1);
rb_define_method(cConmode, "raw", conmode_raw_new, -1);
rb_define_method(cConmode, "min", conmode_get_min, 0);
rb_define_method(cConmode, "min=", conmode_set_min, 1);
rb_define_method(cConmode, "time", conmode_get_time, 0);
rb_define_method(cConmode, "time=", conmode_set_time, 1);
#ifdef _WIN32
rb_define_method(cConmode, "virtual_terminal_processing?", conmode_virtual_terminal_processing_p, 0);
rb_define_method(cConmode, "virtual_terminal_processing=", conmode_set_virtual_terminal_processing, 1);
Expand Down
43 changes: 36 additions & 7 deletions jruby/lib/io/console/backend/ffi/termios.rb
Original file line number Diff line number Diff line change
Expand Up @@ -102,21 +102,27 @@ def ttymode_yield(block, **opts, &setup)
end
private :ttymode_yield

TTY_RAW = Proc.new do |t, min: 1, time: nil, intr: nil|
TTY_SET_MIN = Proc.new do |t, min|
t[:c_cc][LibC::VMIN] = min ? min.to_i.clamp(0, 255) : 1
end

TTY_SET_TIME = Proc.new do |t, time|
t[:c_cc][LibC::VTIME] = time ? (time * 10).to_i.clamp(0, 255) : 0
end

TTY_RAW = Proc.new do |t, min: nil, time: nil, intr: nil|
LibC.cfmakeraw(t.pointer)
t[:c_lflag] &= ~(LibC::ECHOE|LibC::ECHOK)
if min && min >= 0
t[:c_cc][LibC::VMIN] = min
end
t[:c_cc][LibC::VTIME] = ((time || 0) * 10).to_i
TTY_SET_MIN[t, min]
TTY_SET_TIME[t, time]
if intr
t[:c_iflag] |= LibC::BRKINT
t[:c_lflag] |= LibC::ISIG
t[:c_oflag] |= LibC::OPOST
end
end

def raw(*, min: 1, time: nil, intr: nil, &block)
def raw(*, min: nil, time: nil, intr: nil, &block)
ttymode_yield(block, min:, time:, intr:, &TTY_RAW)
end

Expand All @@ -128,7 +134,7 @@ def raw!(*, min: 1, time: nil, intr: nil)
TTY_COOKED = Proc.new do |t|
t[:c_iflag] |= (LibC::BRKINT|LibC::ISTRIP|LibC::ICRNL|LibC::IXON)
t[:c_oflag] |= LibC::OPOST
t[:c_lflag] |= (LibC::ECHO|LibC::ECHOE|LibC::ECHOK|LibC::ECHONL|LibC::ICANON|LibC::ISIG|LibC::IEXTEN)
t[:c_lflag] |= (TTY_ECHO|LibC::ICANON|LibC::ISIG|LibC::IEXTEN)
end

def cooked(*, &block)
Expand Down Expand Up @@ -158,6 +164,8 @@ def noecho(&block)
ttymode_yield(block) { |t| t[:c_lflag] &= ~(TTY_ECHO) }
end

private_constant :TTY_SET_TIME, :TTY_SET_MIN, :TTY_RAW, :TTY_COOKED, :TTY_ECHO

class Mode
attr_reader :termios

Expand All @@ -169,6 +177,11 @@ def initialize_copy(m)
@termios = m.termios.dup
end

def echo?
!(@termios[:c_lflag] & TTY_ECHO).zero?
end
alias echo echo?

def echo=(echo)
if echo
@termios[:c_lflag] |= TTY_ECHO
Expand All @@ -187,6 +200,22 @@ def raw(min: 1, time: nil, intr: nil)
TTY_RAW[new_mode.termios, min:, time:, intr:]
new_mode
end

def min
@termios[:c_cc][LibC::VMIN]
end

def min=(min)
TTY_SET_MIN[@termios, min]
end

def time
@termios[:c_cc][LibC::VTIME].quo(10)
end

def time=(time)
TTY_SET_TIME[@termios, time]
end
end

def console_mode
Expand Down
23 changes: 21 additions & 2 deletions jruby/lib/io/console/backend/ffi/windows.rb
Original file line number Diff line number Diff line change
Expand Up @@ -118,11 +118,30 @@ def tty?(*types)

IO.prepend(IO::Console::Windows::TTY)

class IO::ConsoleMode
class IO::Console::Mode
def initialize(mode)
@mode = mode
end

def echo?
end
alias echo echo?

def echo=(echo)
end

def min
end

def min=(min)
end

def time
end

def time=(time)
end

def virtual_terminal_processing?
@mode & IO::Console::Windows::ENABLE_VIRTUAL_TERMINAL_PROCESSING != 0
end
Expand Down Expand Up @@ -151,7 +170,7 @@ def wrap_at_eol_output=(enabled)

class IO
def console_mode
IO::ConsoleMode.new(IO::Console::Windows.console_mode(self))
IO::Console::Mode.new(IO::Console::Windows.console_mode(self))
end

def console_mode=(mode)
Expand Down
Loading