Terminal control/Restricted width positional input/No wrapping: Difference between revisions
m (→{{header|Wren}}: Changed to Wren S/H) |
|||
(16 intermediate revisions by 10 users not shown) | |||
Line 17: | Line 17: | ||
==={{header|ncurses}}=== |
==={{header|ncurses}}=== |
||
To interface ncurses from Lisp, the ''[https://www.cliki.net/croatoan croatoan]'' library is used. |
To interface ncurses from Lisp, the ''[https://www.cliki.net/croatoan croatoan]'' library is used. |
||
< |
<syntaxhighlight lang="lisp">;; Load the library from the quicklisp repository |
||
(ql:quickload "croatoan") |
(ql:quickload "croatoan") |
||
(in-package :croatoan) |
(in-package :croatoan) |
||
Line 34: | Line 34: | ||
;; call the routine |
;; call the routine |
||
(field-input-no-wrapping 2 4 8)</ |
(field-input-no-wrapping 2 4 8)</syntaxhighlight> |
||
=={{header|FreeBASIC}}== |
|||
<syntaxhighlight lang="freebasic">Function getInput(fila As Integer, columna As Integer, ancho As Integer) As String |
|||
Locate fila, columna, 0 |
|||
Dim As String KBD, cadena = "" |
|||
Do |
|||
Do: KBD = Inkey: Loop Until KBD <> "" |
|||
If KBD = Chr(8) Then |
|||
cadena = Left(cadena, Len(cadena) - 1) |
|||
Print !"\b "; |
|||
Else |
|||
If Len(cadena) < ancho Then cadena &= KBD |
|||
End If |
|||
Locate fila, columna : Print cadena; |
|||
Loop Until KBD = Chr(13) |
|||
Return cadena |
|||
End Function |
|||
Dim As String s = getInput(3, 5, 8) |
|||
Locate 23,1 : Print "You entered: "; s |
|||
Sleep</syntaxhighlight> |
|||
=={{header|Go}}== |
=={{header|Go}}== |
||
Line 42: | Line 64: | ||
<br> |
<br> |
||
This uses _getch() rather than _getwch() as only ASCII is supported. |
This uses _getch() rather than _getwch() as only ASCII is supported. |
||
< |
<syntaxhighlight lang="go">package main |
||
/* |
/* |
||
Line 105: | Line 127: | ||
setCursor(coord) |
setCursor(coord) |
||
fmt.Printf("You entered '%s'\n", s) |
fmt.Printf("You entered '%s'\n", s) |
||
}</ |
}</syntaxhighlight> |
||
=={{header|Julia}}== |
=={{header|Julia}}== |
||
Requires an ANSI compatible terminal and a system C library implementing _getch() for unbuffered keyboard input. Note the code below is identical to the code in [[ |
Requires an ANSI compatible terminal and a system C library implementing _getch() for unbuffered keyboard input. Note the code below is identical to the code in [[Terminal_control/Restricted_width_positional_input/No_wrapping]] but with no width argument in the input call. |
||
< |
<syntaxhighlight lang="julia">getch() = UInt8(ccall(:_getch, Cint, ())) |
||
cls() = print("\33[2J") |
cls() = print("\33[2J") |
||
reposition(row, col) = print("\u001b[$row;$(col)H") |
reposition(row, col) = print("\u001b[$row;$(col)H") |
||
Line 133: | Line 155: | ||
s = input_y_x_upto(3, 5, 8) |
s = input_y_x_upto(3, 5, 8) |
||
println("\n\n\nResult: You entered <<$s>>") |
println("\n\n\nResult: You entered <<$s>>") |
||
</syntaxhighlight> |
|||
</lang> |
|||
=={{header|Kotlin}}== |
=={{header|Kotlin}}== |
||
Line 142: | Line 164: | ||
It would also be possible to allow for printable Unicode characters (>= 160) to be entered by adding an extra clause to the 'when' statement. However, for this to work properly, you will need to be using a suitable code page (65001, say) and a suitable font (Lucida Console, say). |
It would also be possible to allow for printable Unicode characters (>= 160) to be entered by adding an extra clause to the 'when' statement. However, for this to work properly, you will need to be using a suitable code page (65001, say) and a suitable font (Lucida Console, say). |
||
< |
<syntaxhighlight lang="scala">// Kotlin Native v0.5 |
||
import kotlinx.cinterop.* |
import kotlinx.cinterop.* |
||
Line 183: | Line 205: | ||
} |
} |
||
println("You entered '$s'") |
println("You entered '$s'") |
||
}</ |
}</syntaxhighlight> |
||
=={{header| |
=={{header|Nim}}== |
||
{{trans|Julia}} |
|||
<lang Phix>function getInput(integer row, col, width) |
|||
As in the Julia version we translated, the code is identical to the code in [[Terminal_control/Restricted_width_positional_input/No_wrapping]] but with no width argument in the input call. See this version for some general comments about the code. |
|||
position(row,col) |
|||
string s = "" |
|||
while 1 do |
|||
integer ch = wait_key() |
|||
if ch='\r' then exit end if |
|||
if ch='\b' then |
|||
if length(s)>0 then |
|||
puts(1,"\b \b") |
|||
s = s[1..$-1] |
|||
end if |
|||
elsif ch>=' ' and ch<='~' then |
|||
if length(s)<=width then |
|||
puts(1,ch) |
|||
s &= ch |
|||
end if |
|||
end if |
|||
end while |
|||
return s |
|||
end function |
|||
<syntaxhighlight lang="nim">import strformat, terminal |
|||
clear_screen() -- clear the console |
|||
string s = getInput(3, 5, 8) |
|||
proc eraseLineEnd() = stdout.write("\e[K") |
|||
position(23,1) |
|||
printf(1,"You entered '%s'\n",{s})</lang> |
|||
proc inputXYUpto(row, col, cmax: int; width = cmax): string = |
|||
while result.len < cmax and not ((let c = getch(); c) in ['\xff', '\f', '\r']): |
|||
setCursorPos(row, col) |
|||
eraseLineEnd() |
|||
if c in ['\b', '\x7f'] and result.len > 0: |
|||
result.setLen(result.len - 1) |
|||
else: |
|||
result.add c |
|||
stdout.write result[(if result.len > width: result.len - width else: 0)..result.high] |
|||
eraseScreen() |
|||
setCursorPos(3, 5) |
|||
let s = inputXYUpto(3, 5, 8) |
|||
echo &"\n\n\nResult: You entered <<{s}>>"</syntaxhighlight> |
|||
=={{header|Perl}}== |
|||
Works on ArchLinux in an xterm. |
|||
<syntaxhighlight lang="perl">#!/usr/bin/perl |
|||
use strict; # https://rosettacode.org/wiki/Terminal_control/Restricted_width_positional_input/No_wrapping |
|||
use warnings; |
|||
use List::Util qw( min max ); |
|||
$| = 1; |
|||
sub label { printf "\e[%d;%dH%s", @_; } |
|||
sub getinput |
|||
{ |
|||
my ($row, $column, $length, $prompt) = @_; |
|||
defined $prompt and length $prompt < $column - 1 and |
|||
label( $row, $column - length $prompt, $prompt); |
|||
my $at = "\e[%d;%dH%-$length.${length}s\e[%d;%dH"; |
|||
my $input = ''; |
|||
my $pos = 0; |
|||
use Term::ReadKey; |
|||
eval |
|||
{ |
|||
ReadMode 'cbreak'; |
|||
local $_; |
|||
my $more = 1; |
|||
while( $more ) |
|||
{ |
|||
printf $at, $row, $column, $input, $row, $column + $pos; |
|||
0 < sysread *STDIN, $_, 1024 or last; |
|||
print "\e[10;1H\e[J"; use Data::Dump 'dd'; dd $_; |
|||
for( /\e[O\[][0-9;]*[A-~]|./gs ) |
|||
{ |
|||
/^[\n\r\e]\z/ ? do { $more = 0 } : |
|||
/^[ -~]\z/ ? do { |
|||
substr $input, $pos++, 0, $_; $input &= "\xff" x $length; |
|||
$pos = min $pos, $length } : |
|||
$_ eq "\b" ? do { $pos = max $pos - 1, 0; substr $input, $pos, 1, '' } : |
|||
$_ eq "\e[D" ? do { $pos = max 0, $pos - 1 } : |
|||
$_ eq "\e[C" ? do { $pos = min length $input, $pos + 1 } : |
|||
$_ eq "\e[H" ? do { $pos = 0 } : |
|||
$_ eq "\e[F" ? do { $pos = length $input } : |
|||
$_ eq "\e[3~" ? do { length $input and substr $input, $pos, 1, ''; } : |
|||
0; |
|||
} |
|||
} |
|||
}; |
|||
ReadMode 'restore'; |
|||
return $input; |
|||
} |
|||
print "\e[H\e[J"; |
|||
#label(2, 5, "Enter input below"); |
|||
my $text = getinput( 3, 25, 8, "test string: " ); |
|||
print "\n\nyour input <$text>\n";</syntaxhighlight> |
|||
=={{header|Phix}}== |
|||
<!--<syntaxhighlight lang="phix">(notonline)--> |
|||
<span style="color: #008080;">without</span> <span style="color: #008080;">js</span> <span style="color: #000080;font-style:italic;">-- (position, wait_key, backspace)</span> |
|||
<span style="color: #008080;">function</span> <span style="color: #000000;">getInput</span><span style="color: #0000FF;">(</span><span style="color: #004080;">integer</span> <span style="color: #000000;">row</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">col</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">width</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #7060A8;">position</span><span style="color: #0000FF;">(</span><span style="color: #000000;">row</span><span style="color: #0000FF;">,</span><span style="color: #000000;">col</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #004080;">string</span> <span style="color: #000000;">s</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">""</span> |
|||
<span style="color: #008080;">while</span> <span style="color: #000000;">1</span> <span style="color: #008080;">do</span> |
|||
<span style="color: #004080;">integer</span> <span style="color: #000000;">ch</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">wait_key</span><span style="color: #0000FF;">()</span> |
|||
<span style="color: #008080;">if</span> <span style="color: #000000;">ch</span><span style="color: #0000FF;">=</span><span style="color: #008000;">'\r'</span> <span style="color: #008080;">then</span> <span style="color: #008080;">exit</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
<span style="color: #008080;">if</span> <span style="color: #000000;">ch</span><span style="color: #0000FF;">=</span><span style="color: #008000;">'\b'</span> <span style="color: #008080;">then</span> |
|||
<span style="color: #008080;">if</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">s</span><span style="color: #0000FF;">)></span><span style="color: #000000;">0</span> <span style="color: #008080;">then</span> |
|||
<span style="color: #7060A8;">puts</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"\b \b"</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #000000;">s</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">s</span><span style="color: #0000FF;">[</span><span style="color: #000000;">1</span><span style="color: #0000FF;">..$-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
<span style="color: #008080;">elsif</span> <span style="color: #000000;">ch</span><span style="color: #0000FF;">>=</span><span style="color: #008000;">' '</span> <span style="color: #008080;">and</span> <span style="color: #000000;">ch</span><span style="color: #0000FF;"><=</span><span style="color: #008000;">'~'</span> <span style="color: #008080;">then</span> |
|||
<span style="color: #008080;">if</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">s</span><span style="color: #0000FF;">)<=</span><span style="color: #000000;">width</span> <span style="color: #008080;">then</span> |
|||
<span style="color: #7060A8;">puts</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">ch</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #000000;">s</span> <span style="color: #0000FF;">&=</span> <span style="color: #000000;">ch</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">while</span> |
|||
<span style="color: #008080;">return</span> <span style="color: #000000;">s</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span> |
|||
<span style="color: #7060A8;">clear_screen</span><span style="color: #0000FF;">()</span> <span style="color: #000080;font-style:italic;">-- clear the console</span> |
|||
<span style="color: #004080;">string</span> <span style="color: #000000;">s</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">getInput</span><span style="color: #0000FF;">(</span><span style="color: #000000;">3</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">5</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">8</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #7060A8;">position</span><span style="color: #0000FF;">(</span><span style="color: #000000;">23</span><span style="color: #0000FF;">,</span><span style="color: #000000;">1</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"You entered '%s'\n"</span><span style="color: #0000FF;">,{</span><span style="color: #000000;">s</span><span style="color: #0000FF;">})</span> |
|||
<!--</syntaxhighlight>--> |
|||
=={{header|Raku}}== |
=={{header|Raku}}== |
||
Line 219: | Line 322: | ||
All printable character keys work, as does backspace and enter. Ctrl-c to exit. All other keys / key-combos are ignored. |
All printable character keys work, as does backspace and enter. Ctrl-c to exit. All other keys / key-combos are ignored. |
||
<lang |
<syntaxhighlight lang="raku" line>use Term::termios; |
||
constant $saved = Term::termios.new(fd => 1).getattr; |
constant $saved = Term::termios.new(fd => 1).getattr; |
||
Line 281: | Line 384: | ||
@screen = "\e[41m{' ' x $cols}\e[0m" xx $rows; |
@screen = "\e[41m{' ' x $cols}\e[0m" xx $rows; |
||
print "\e[H\e[J{@screen.join: "\n"}\e[$row;{$column}H$str\e[$row;{$column + $pointer}H"; |
print "\e[H\e[J{@screen.join: "\n"}\e[$row;{$column}H$str\e[$row;{$column + $pointer}H"; |
||
}</ |
}</syntaxhighlight> |
||
=={{header|REXX}}== |
|||
(This REXX program only works with: REXX/Personal) |
|||
<syntaxhighlight lang="rexx">/*REXX pgm reads text from the terminal screen from a certain row, column, and length.*/ |
|||
parse arg row col len . /*obtain optional arguments from the CL*/ |
|||
if row=='' | row=="," then row= 3 /*Not specified? Then use the default.*/ |
|||
if col=='' | col=="," then col= 5 /* " " " " " " */ |
|||
if len=='' | len=="," then len= 8 /* " " " " " " */ |
|||
parse upper version v . /*obtain the version of REXX being used*/ |
|||
if v\=='REXX/PERSONAL' then do; say /*Not correct version? Tell err msg. */ |
|||
say '***error***:' |
|||
say 'This REXX program requires Personal REXX version.' |
|||
say |
|||
exit 13 |
|||
end |
|||
$= scrread(row, col, len) |
|||
say 'data read from terminal row ' row " col " col ' length ' len " is:" |
|||
say $ |
|||
exit 0 /*stick a fork in it, we're all done. */</syntaxhighlight> |
|||
=={{header|Wren}}== |
|||
Due to a bug the ''Stdin.readByte()'' method can currently process only the first byte of a multi-byte character. The others are skipped. |
|||
<syntaxhighlight lang="wren">import "io" for Stdin, Stdout |
|||
var textAtPos = Fn.new { |text, r, c| |
|||
System.write("\e[%(r);%(c)H%(text)") |
|||
Stdout.flush() |
|||
} |
|||
var input = Fn.new { |r, c, maxWidth| |
|||
System.write("\e[2J") |
|||
textAtPos.call("", r, c) |
|||
Stdin.isRaw = true |
|||
var w = 0 |
|||
var res = List.filled(maxWidth, "") |
|||
while (true) { |
|||
var byte = Stdin.readByte() |
|||
if (byte >= 32 && byte < 127) { // All printable ASCII characters |
|||
var char = String.fromByte(byte) |
|||
textAtPos.call(char, r, c+w) |
|||
if (w < maxWidth-1) { |
|||
res[w] = char |
|||
w = w + 1 |
|||
} else { |
|||
System.write("\b") |
|||
Stdout.flush() |
|||
res[w] = char |
|||
} |
|||
} else if (byte == 127 && w > 0) { // Backspace/delete (127 used rather than 8) |
|||
System.write("\b \b") |
|||
Stdout.flush() |
|||
w = w - 1 |
|||
res[w] = "" |
|||
} else if (byte == 13 || byte == 10) { // Carriage return or line feed |
|||
System.print() |
|||
break |
|||
} else if (byte == 3 || byte == 4) { // Ctrl-C or Ctrl-D |
|||
Fiber.abort("\nScript aborted") |
|||
} |
|||
} |
|||
Stdin.isRaw = false |
|||
return res.join().trimEnd(" ") |
|||
} |
|||
var res = input.call(3, 5, 8) |
|||
System.print(res)</syntaxhighlight> |
|||
=={{header|XPL0}}== |
|||
<syntaxhighlight lang="xpl0">proc GetData(Col, Row, Width, Str); |
|||
int Col, Row, Width; char Str; |
|||
int I, C; |
|||
string 0; |
|||
[Cursor(Col, Row); |
|||
I:= 0; |
|||
loop [C:= ChIn(1); \Ctrl+C aborts |
|||
if C = $0D \CR\ then |
|||
[Str(I):= 0; \terminate\ quit]; |
|||
if C = $08 \BS\ then |
|||
[if I > 0 then |
|||
[I:= I-1; |
|||
ChOut(0, C); \echo backspace |
|||
ChOut(0, ^ ); \erase character |
|||
ChOut(0, C); \echo backspace |
|||
]; |
|||
]; |
|||
if I<Width & C>=$20 & C<=$7E then |
|||
[ChOut(0, C); \echo character |
|||
Str(I):= C; |
|||
I:= I+1; |
|||
]; |
|||
]; |
|||
]; |
|||
char Str(8+1); |
|||
[ChOut(0, $0C \FF\); \clear screen |
|||
GetData(5, 3, 8, Str); |
|||
Text(0, " |
|||
You entered: "); |
|||
Text(0, Str); |
|||
]</syntaxhighlight> |
|||
=={{header|Yabasic}}== |
|||
<syntaxhighlight lang="yabasic">// Rosetta Code problem: http://rosettacode.org/wiki/Restricted_width_positional_input/No_wrapping |
|||
// by Galileo, 04/2022 |
|||
clear screen |
|||
sub getInput$(r, c, long) |
|||
local text$, c$ |
|||
c = c - 1 |
|||
r = r - 1 |
|||
print at(c, r); |
|||
do |
|||
c$ = inkey$ |
|||
if c$ = "enter" break |
|||
if c$ = "backspace" then |
|||
text$ = left$(text$, len(text$) - 1) |
|||
print "\b "; |
|||
else |
|||
if len(text$) < long text$ = text$ + c$ |
|||
end if |
|||
print at(c, r) text$; |
|||
loop |
|||
return text$ |
|||
end sub |
|||
text$ = getInput$(3, 5, 8) |
|||
print at(1, 23) "You entered: ", text$</syntaxhighlight> |
Latest revision as of 12:16, 13 February 2024
Create a routine to obtain data entry using a specific place on the terminal screen. Data entry fields should be restricted to a specific length, and it should not be possible for the cursor to move beyond the input length.
The routine should accept parameters for row number, column number and input length, and should obtain a string value entered by the user.
For the purpose of this task, obtain input from the user, showing data entry at row 3, column 5, with input width restricted to a maximum of 8 characters.
Note: In this task input wrapping is not allowed. (See Terminal control/Restricted width positional input/With wrapping) for input wrapping variant.
For a similar task using a graphical user interface, see Graphical User Interface/Restricted width positional input/No wrapping.
Common Lisp
ncurses
To interface ncurses from Lisp, the croatoan library is used.
;; Load the library from the quicklisp repository
(ql:quickload "croatoan")
(in-package :croatoan)
(defun field-input-no-wrapping (row column input-length)
(with-screen (scr :input-echoing nil :cursor-visible t :enable-colors t :enable-function-keys t :input-blocking t)
(let ((field (make-instance 'field :position (list row column) :width input-length :window scr)))
(setf (style field)
(list :background (list :simple-char #\.)))
(bind field #\newline 'accept)
(edit field)
(clear scr)
(refresh scr)
;; return the value of the field as a string
(value field))))
;; call the routine
(field-input-no-wrapping 2 4 8)
FreeBASIC
Function getInput(fila As Integer, columna As Integer, ancho As Integer) As String
Locate fila, columna, 0
Dim As String KBD, cadena = ""
Do
Do: KBD = Inkey: Loop Until KBD <> ""
If KBD = Chr(8) Then
cadena = Left(cadena, Len(cadena) - 1)
Print !"\b ";
Else
If Len(cadena) < ancho Then cadena &= KBD
End If
Locate fila, columna : Print cadena;
Loop Until KBD = Chr(13)
Return cadena
End Function
Dim As String s = getInput(3, 5, 8)
Locate 23,1 : Print "You entered: "; s
Sleep
Go
This uses _getch() rather than _getwch() as only ASCII is supported.
package main
/*
#include <windows.h>
#include <conio.h>
*/
import "C"
import (
"fmt"
"os"
"os/exec"
)
var conOut = C.GetStdHandle(C.STD_OUTPUT_HANDLE)
func setCursor(p C.COORD) {
C.SetConsoleCursorPosition(conOut, p)
}
func cls() {
cmd := exec.Command("cmd", "/c", "cls")
cmd.Stdout = os.Stdout
cmd.Run()
}
func getInput(row, col, width int) string {
if row < 0 || row > 20 || col < 0 || width < 1 || width > 78 || col > (79 - width) {
panic("Invalid parameter(s) to getInput. Terminating program")
}
coord := C.COORD{C.short(col), C.short(row)}
setCursor(coord)
var sb []byte
full := false
loop:
for {
ch := C._getch() // gets next character, no echo
switch c := byte(ch); c {
case 3, 13:
break loop // break on Ctrl-C or enter key
case 8:
if len(sb) > 0 { // mimic backspace
fmt.Print("\b \b")
sb = sb[:len(sb) - 1]
}
case 0, 224:
C._getch() // consume extra character
default:
if c >= 32 && c <= 126 && !full {
C._putch(ch) // echo ascii character, ignore others
sb = append(sb, c)
}
}
full = len(sb) == width // can't exceed width
}
return string(sb)
}
func main() {
cls() // clear the console
s := getInput(2, 4, 8) // Windows console row/col numbering starts at 0
coord := C.COORD{0, 22}
setCursor(coord)
fmt.Printf("You entered '%s'\n", s)
}
Julia
Requires an ANSI compatible terminal and a system C library implementing _getch() for unbuffered keyboard input. Note the code below is identical to the code in Terminal_control/Restricted_width_positional_input/No_wrapping but with no width argument in the input call.
getch() = UInt8(ccall(:_getch, Cint, ()))
cls() = print("\33[2J")
reposition(row, col) = print("\u001b[$row;$(col)H")
clearfromcursor() = print("\u001b[K")
function input_y_x_upto(row, col, cmax, width=cmax)
buf = ""
while (buflen = length(buf)) < cmax && !((c = getch()) in [0xff, 0x0d, 0x0a])
reposition(row, col)
clearfromcursor()
if c == '\b' && buflen > 0
buf = buf[1:end-1]
else
buf = buf * Char(c)
end
print(buf[(buflen > width ? buflen - width + 1 : 1):end])
end
return buf
end
cls()
reposition(3, 5)
s = input_y_x_upto(3, 5, 8)
println("\n\n\nResult: You entered <<$s>>")
Kotlin
This assumes an 80 x 25 Windows console and uses the Win32 function _getwch() to get character input without (immediately) echoing it to the console. Only ASCII characters in the range 32 to 126 are then printed if the maximum width would not be exceeded.
Apart from these, only the backspace key (8), return key (13) and Ctrl-C (3) are handled, all other keys being ignored. Some keys (such as the function and arrow keys) return a two character sequence starting with either 0 or 224 and so, after the first character is trapped, the second character needs to be removed from the buffer.
It would also be possible to allow for printable Unicode characters (>= 160) to be entered by adding an extra clause to the 'when' statement. However, for this to work properly, you will need to be using a suitable code page (65001, say) and a suitable font (Lucida Console, say).
// Kotlin Native v0.5
import kotlinx.cinterop.*
import platform.windows.*
import platform.posix.*
val ascii = 32..126
val conOut = GetStdHandle(STD_OUTPUT_HANDLE)!!
fun setCursor(p: COORD) = SetConsoleCursorPosition(conOut, p.readValue())
fun getInput(row: Short, col: Short, width: Int): String {
require(row in 0..20 && col in 0..(79 - width) && width in 1..78) { "Invalid parameter(s)" }
memScoped {
val coord = alloc<COORD>().apply { X = col; Y = row }
setCursor(coord)
}
val sb = StringBuilder(width)
var full = false
loop@ while (true) {
val ch = _getwch() // gets next character, no echo
when (ch.toInt()) {
3, 13 -> break@loop // break on Ctrl-C or enter key
8 -> if (sb.length > 0) { print("\b \b"); sb.length-- } // mimic backspace
0, 224 -> _getwch() // consume extra character
in ascii -> if (!full) { _putwch(ch); sb.append(ch.toChar()) } // echo ascii character
else -> {} // igore other characters
}
full = sb.length == width // can't exceed width
}
return sb.toString()
}
fun main(args: Array<String>) {
system("cls") // clear the console
val s = getInput(2, 4, 8) // Windows console row/col numbering starts at 0
memScoped {
val coord = alloc<COORD>().apply { X = 0 ; Y = 22 }
setCursor(coord)
}
println("You entered '$s'")
}
Nim
As in the Julia version we translated, the code is identical to the code in Terminal_control/Restricted_width_positional_input/No_wrapping but with no width argument in the input call. See this version for some general comments about the code.
import strformat, terminal
proc eraseLineEnd() = stdout.write("\e[K")
proc inputXYUpto(row, col, cmax: int; width = cmax): string =
while result.len < cmax and not ((let c = getch(); c) in ['\xff', '\f', '\r']):
setCursorPos(row, col)
eraseLineEnd()
if c in ['\b', '\x7f'] and result.len > 0:
result.setLen(result.len - 1)
else:
result.add c
stdout.write result[(if result.len > width: result.len - width else: 0)..result.high]
eraseScreen()
setCursorPos(3, 5)
let s = inputXYUpto(3, 5, 8)
echo &"\n\n\nResult: You entered <<{s}>>"
Perl
Works on ArchLinux in an xterm.
#!/usr/bin/perl
use strict; # https://rosettacode.org/wiki/Terminal_control/Restricted_width_positional_input/No_wrapping
use warnings;
use List::Util qw( min max );
$| = 1;
sub label { printf "\e[%d;%dH%s", @_; }
sub getinput
{
my ($row, $column, $length, $prompt) = @_;
defined $prompt and length $prompt < $column - 1 and
label( $row, $column - length $prompt, $prompt);
my $at = "\e[%d;%dH%-$length.${length}s\e[%d;%dH";
my $input = '';
my $pos = 0;
use Term::ReadKey;
eval
{
ReadMode 'cbreak';
local $_;
my $more = 1;
while( $more )
{
printf $at, $row, $column, $input, $row, $column + $pos;
0 < sysread *STDIN, $_, 1024 or last;
print "\e[10;1H\e[J"; use Data::Dump 'dd'; dd $_;
for( /\e[O\[][0-9;]*[A-~]|./gs )
{
/^[\n\r\e]\z/ ? do { $more = 0 } :
/^[ -~]\z/ ? do {
substr $input, $pos++, 0, $_; $input &= "\xff" x $length;
$pos = min $pos, $length } :
$_ eq "\b" ? do { $pos = max $pos - 1, 0; substr $input, $pos, 1, '' } :
$_ eq "\e[D" ? do { $pos = max 0, $pos - 1 } :
$_ eq "\e[C" ? do { $pos = min length $input, $pos + 1 } :
$_ eq "\e[H" ? do { $pos = 0 } :
$_ eq "\e[F" ? do { $pos = length $input } :
$_ eq "\e[3~" ? do { length $input and substr $input, $pos, 1, ''; } :
0;
}
}
};
ReadMode 'restore';
return $input;
}
print "\e[H\e[J";
#label(2, 5, "Enter input below");
my $text = getinput( 3, 25, 8, "test string: " );
print "\n\nyour input <$text>\n";
Phix
without js -- (position, wait_key, backspace) function getInput(integer row, col, width) position(row,col) string s = "" while 1 do integer ch = wait_key() if ch='\r' then exit end if if ch='\b' then if length(s)>0 then puts(1,"\b \b") s = s[1..$-1] end if elsif ch>=' ' and ch<='~' then if length(s)<=width then puts(1,ch) s &= ch end if end if end while return s end function clear_screen() -- clear the console string s = getInput(3, 5, 8) position(23,1) printf(1,"You entered '%s'\n",{s})
Raku
(formerly Perl 6)
Should work with any termios compatible terminal.
All printable character keys work, as does backspace and enter. Ctrl-c to exit. All other keys / key-combos are ignored.
use Term::termios;
constant $saved = Term::termios.new(fd => 1).getattr;
constant $termios = Term::termios.new(fd => 1).getattr;
# raw mode interferes with carriage returns, so
# set flags needed to emulate it manually
$termios.unset_iflags(<BRKINT ICRNL ISTRIP IXON>);
$termios.unset_lflags(<ECHO ICANON IEXTEN ISIG>);
$termios.setattr(:DRAIN);
END {
$saved.setattr(:NOW); # reset terminal to original settings
print "\e[?25h \e[H\e[J"; # clear and reset screen
}
my $row = 3;
my $column = 5;
my $field = '';
my $spacer = ' ' x 8;
my $pointer = 0;
my ($rows,$cols) = qx/stty size/.words; # get screen size
my @screen = "\e[41m{' ' x $cols}\e[0m" xx $rows;
update($spacer);
loop {
my $key = $*IN.read(4).decode;
given $key {
when ' '..'~' {
if $pointer < 8 {
$field ~= $_;
$spacer = ' ' x 8 - $field.chars;
$pointer +=1;
update($field~$spacer)
}
}
when "\c[127]" { # backspace
if $pointer > 0 {
$field.=substr(0,*-1);
$spacer = ' ' x 8 - $field.chars;
$pointer -= 1;
update($field~$spacer)
}
}
when "\c[13]" {
update(' ');
print "\e[10;6H\e[1;33;41mYou entered: $field\e[0m\e[$row;{$column}H";
$field = '';
$pointer = 0;
}
when "\c[0003]" { exit } # Ctrl-c
default { }
}
}
sub update ($str) {
($rows,$cols) = qx/stty size/.words;
@screen = "\e[41m{' ' x $cols}\e[0m" xx $rows;
print "\e[H\e[J{@screen.join: "\n"}\e[$row;{$column}H$str\e[$row;{$column + $pointer}H";
}
REXX
(This REXX program only works with: REXX/Personal)
/*REXX pgm reads text from the terminal screen from a certain row, column, and length.*/
parse arg row col len . /*obtain optional arguments from the CL*/
if row=='' | row=="," then row= 3 /*Not specified? Then use the default.*/
if col=='' | col=="," then col= 5 /* " " " " " " */
if len=='' | len=="," then len= 8 /* " " " " " " */
parse upper version v . /*obtain the version of REXX being used*/
if v\=='REXX/PERSONAL' then do; say /*Not correct version? Tell err msg. */
say '***error***:'
say 'This REXX program requires Personal REXX version.'
say
exit 13
end
$= scrread(row, col, len)
say 'data read from terminal row ' row " col " col ' length ' len " is:"
say $
exit 0 /*stick a fork in it, we're all done. */
Wren
Due to a bug the Stdin.readByte() method can currently process only the first byte of a multi-byte character. The others are skipped.
import "io" for Stdin, Stdout
var textAtPos = Fn.new { |text, r, c|
System.write("\e[%(r);%(c)H%(text)")
Stdout.flush()
}
var input = Fn.new { |r, c, maxWidth|
System.write("\e[2J")
textAtPos.call("", r, c)
Stdin.isRaw = true
var w = 0
var res = List.filled(maxWidth, "")
while (true) {
var byte = Stdin.readByte()
if (byte >= 32 && byte < 127) { // All printable ASCII characters
var char = String.fromByte(byte)
textAtPos.call(char, r, c+w)
if (w < maxWidth-1) {
res[w] = char
w = w + 1
} else {
System.write("\b")
Stdout.flush()
res[w] = char
}
} else if (byte == 127 && w > 0) { // Backspace/delete (127 used rather than 8)
System.write("\b \b")
Stdout.flush()
w = w - 1
res[w] = ""
} else if (byte == 13 || byte == 10) { // Carriage return or line feed
System.print()
break
} else if (byte == 3 || byte == 4) { // Ctrl-C or Ctrl-D
Fiber.abort("\nScript aborted")
}
}
Stdin.isRaw = false
return res.join().trimEnd(" ")
}
var res = input.call(3, 5, 8)
System.print(res)
XPL0
proc GetData(Col, Row, Width, Str);
int Col, Row, Width; char Str;
int I, C;
string 0;
[Cursor(Col, Row);
I:= 0;
loop [C:= ChIn(1); \Ctrl+C aborts
if C = $0D \CR\ then
[Str(I):= 0; \terminate\ quit];
if C = $08 \BS\ then
[if I > 0 then
[I:= I-1;
ChOut(0, C); \echo backspace
ChOut(0, ^ ); \erase character
ChOut(0, C); \echo backspace
];
];
if I<Width & C>=$20 & C<=$7E then
[ChOut(0, C); \echo character
Str(I):= C;
I:= I+1;
];
];
];
char Str(8+1);
[ChOut(0, $0C \FF\); \clear screen
GetData(5, 3, 8, Str);
Text(0, "
You entered: ");
Text(0, Str);
]
Yabasic
// Rosetta Code problem: http://rosettacode.org/wiki/Restricted_width_positional_input/No_wrapping
// by Galileo, 04/2022
clear screen
sub getInput$(r, c, long)
local text$, c$
c = c - 1
r = r - 1
print at(c, r);
do
c$ = inkey$
if c$ = "enter" break
if c$ = "backspace" then
text$ = left$(text$, len(text$) - 1)
print "\b ";
else
if len(text$) < long text$ = text$ + c$
end if
print at(c, r) text$;
loop
return text$
end sub
text$ = getInput$(3, 5, 8)
print at(1, 23) "You entered: ", text$