Terminal control/Positional read: Difference between revisions

From Rosetta Code
Content added Content deleted
(→‎{{header|TXR}}: Get the console window info to treat coordinates as physical, translated to virtual.)
Line 169: Line 169:
(typedef WCHAR wchar)
(typedef WCHAR wchar)
(typedef DWORD uint32)
(typedef DWORD uint32)
(typedef WORD uint16)
(typedef SHORT short)
(typedef SHORT short)

(typedef COORD (struct COORD
(typedef COORD
(X SHORT)
(Y SHORT)))
(struct COORD
(X SHORT)
(Y SHORT)))

(typedef SMALL_RECT
(struct SMALL_RECT
(Left SHORT)
(Top SHORT)
(Right SHORT)
(Bottom SHORT)))

(typedef CONSOLE_SCREEN_BUFFER_INFO
(struct CONSOLE_SCREEN_BUFFER_INFO
(dwSize COORD)
(dwCursorPosition COORD)
(wAttributes WORD)
(srWindow SMALL_RECT)
(dwMaximumWindowSize COORD)))


;;; Various constants
;;; Various constants
Line 187: Line 205:
(with-dyn-lib "kernel32.dll"
(with-dyn-lib "kernel32.dll"
(deffi GetStdHandle "GetStdHandle" HANDLE (DWORD))
(deffi GetStdHandle "GetStdHandle" HANDLE (DWORD))
(deffi GetConsoleScreenBufferInfo "GetConsoleScreenBufferInfo"
(deffi ReadConsoleOutputCharacter
"ReadConsoleOutputCharacterW" BOOL (HANDLE
BOOL (HANDLE (ptr-out CONSOLE_SCREEN_BUFFER_INFO)))
(deffi ReadConsoleOutputCharacter "ReadConsoleOutputCharacterW"
(ptr-out (array 1 WCHAR))
BOOL (HANDLE (ptr-out (array 1 WCHAR))
DWORD
COORD
DWORD COORD (ptr-out (array 1 DWORD)))))
(ptr-out (array 1 DWORD)))))


;;; Now the character at <2, 5> -- column 3, row 6.
;;; Now the character at <2, 5> -- column 3, row 6.
Line 199: Line 216:
(when (equal console-handle INVALID_HANDLE_VALUE)
(when (equal console-handle INVALID_HANDLE_VALUE)
(error "couldn't get console handle"))
(error "couldn't get console handle"))

(let* ((chars (vector 5))
(let* ((cinfo (new CONSOLE_SCREEN_BUFFER_INFO))
(getinfo-ok (GetConsoleScreenBufferInfo console-handle cinfo))
(coord (if getinfo-ok
^#S(COORD X ,(+ 2 cinfo.srWindow.Left)
Y ,(+ 5 cinfo.srWindow.Top))
^#S(COORD X 0 Y 0)))
(chars (vector 5))
(nread (vector 1))
(nread (vector 1))
(success-p (ReadConsoleOutputCharacter console-handle
(read-ok (ReadConsoleOutputCharacter console-handle chars
chars
1 coord nread)))
(unless getinfo-ok
1
(error "GetConsoleScreenBufferInfo failed"))
#S(COORD X 2 Y 5)
(prinl cinfo)
nread)))
(unless success-p
(unless read-ok
(error "ReadConsoleOutputCharacter failed"))
(error "ReadConsoleOutputCharacter failed"))
(unless (plusp [nread 0])
(unless (plusp [nread 0])

Revision as of 20:09, 19 June 2017

Task
Terminal control/Positional read
You are encouraged to solve this task according to the task description, using any language you may know.

Determine the character displayed on the screen at column 3, row 6 and store that character in a variable. Note that it is permissible to utilize system or language provided methods or system provided facilities, system maintained records or available buffers or system maintained display records to achieve this task, rather than query the terminal directly, if those methods are more usual for the system type or language.

AutoHotkey

Works with: AutoHotkey_L

AutoHotkey is not built for the command line, so we need call the WinAPI directly.

For fun, this writes random characters to the command window so that it has something to retrieve.

<lang AHK>DllCall( "AllocConsole" ) ; create a console if not launched from one hConsole := DllCall( "GetStdHandle", int, STDOUT := -11 ) Loop 10 { Loop 10 { Random, asc, % asc("A"), % Asc("Z") WriteConsole(hConsole, Chr(asc)) } WriteConsole(hConsole, "`n") }

MsgBox % ReadConsoleOutputCharacter(hConsole, 1, 3, 6)

=== The below simply wraps part of the WinAPI ===

WriteConsole(hConsole, text){ VarSetCapacity(out, 16) If DllCall( "WriteConsole", UPtr, hConsole, Str, text, UInt, StrLen(text) , UPtrP, out, uint, 0 ) return out return 0 } ReadConsoleOutputCharacter(hConsole, length, x, y){ VarSetCapacity(out, length * (1 << !!A_IsUnicode)) VarSetCapacity(n, 16) if DllCall( "ReadConsoleOutputCharacter" , UPtr, hConsole , Str, out , UInt, length , UInt, x | (y << 16) , UPtrP, n )

&& VarSetCapacity(out, -1) return out return 0 }</lang>

BASIC

Applesoft BASIC

<lang ApplesoftBasic> 10 DEF FN C(H) = SCRN( H - 1,(V - 1) * 2) + SCRN( H - 1,(V - 1) * 2 + 1) * 16

20  LET V = 6:C$ =  CHR$ ( FN C(3))</lang>

Locomotive Basic

<lang locobasic>10 LOCATE 3,6 20 a$=COPYCHR$(#0)</lang>

Amstrad CPC screen memory only stores pixels but no character information (as opposed to e.g. the C64), so the firmware routine (TXT_UNWRITE) called by BASIC works by trying to find a match between screen pixels and the shape of a currently defined character. If the character table or screen pixels in the area of the character are changed between writing and reading, COPYCHR$ will therefore fail.

QBasic

The top left corner is (1, 1).

<lang qbasic>c$ = CHR$(SCREEN(6, 3))</lang>

ZX Spectrum Basic

<lang basic> 10 REM The top left corner is at position 0,0

20 REM So we subtract one from the coordinates
30 LET c$ = SCREEN$(5,2)</lang>

BBC BASIC

<lang bbcbasic> PRINT TAB(2,5) "Here"

     char$ = GET$(2,5)
     PRINT "Character at column 3 row 6 was " char$</lang>

C

With the Windows console, call GetConsoleScreenBufferInfo() to find the top-left corner of the display screen. Then add (3, 6) to the top-left corner and call ReadConsoleOutputCharacterW() to read character. This program reckons that the top-left corner is (0, 0).

Library: Win32

<lang c>#include <windows.h>

  1. include <wchar.h>

int main() { CONSOLE_SCREEN_BUFFER_INFO info; COORD pos; HANDLE conout; long len; wchar_t c;

/* Create a handle to the console screen. */ conout = CreateFileW(L"CONOUT$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (conout == INVALID_HANDLE_VALUE) return 1;

/* Where is the display window? */ if (GetConsoleScreenBufferInfo(conout, &info) == 0) return 1;

/* c = character at position. */ pos.X = info.srWindow.Left + 3; /* Column */ pos.Y = info.srWindow.Top + 6; /* Row */ if (ReadConsoleOutputCharacterW(conout, &c, 1, pos, &len) == 0 || len <= 0) return 1;

wprintf(L"Character at (3, 6) had been '%lc'\n", c); return 0; }</lang>

Phix

<lang Phix>-- -- demo\rosetta\Positional_read.exw -- ================================ -- position(6,1) -- line 6 column 1 (1-based) puts(1,"abcdef") integer {ch,attr} = get_screen_char(6,3) printf(1,"\n\n=>%c",ch) {} = wait_key()</lang>

Output:





abcdef

=>c

Racket

Works in a CMD box on Windows: <lang racket>

  1. lang racket

(require ffi/unsafe ffi/unsafe/define) (define-ffi-definer defwin #f) (defwin GetStdHandle (_fun _int -> _pointer)) (defwin ReadConsoleOutputCharacterA

 (_fun _pointer _pointer _uint _uint [len : (_ptr o _uint)] -> _bool))

(define b (make-bytes 1 32)) (and (ReadConsoleOutputCharacterA (GetStdHandle -11) b 1 #x50002)

    (printf "The character at 3x6 is <~a>\n" b))

</lang>

REXX

The REXX doesn't have any cursor or screen management tools, but some REXX interpreters have added the functionality via different methods.

Works with: PC/REXX
Works with: Personal REXX

<lang rexx>/*REXX program demonstrates reading a char at specific screen location.*/ row = 6 /*point to row six. */ col = 3 /*point to column three. */ howMany = 1 /*read one character. */

stuff = scrRead(row, col, howMany) /*this'll do it. */

other = scrRead(40, 6, 1) /*same thing, but for row forty. */

                                      /*stick a fork in it, we're done.*/</lang>

TXR

<lang txrlisp>;;; Type definitions and constants

(typedef BOOL (enum BOOL FALSE TRUE)) (typedef HANDLE cptr) (typedef WCHAR wchar) (typedef DWORD uint32) (typedef WORD uint16) (typedef SHORT short)

(typedef COORD

        (struct COORD
          (X SHORT)
          (Y SHORT)))

(typedef SMALL_RECT

        (struct SMALL_RECT
          (Left SHORT)
          (Top SHORT)
          (Right SHORT)
          (Bottom SHORT)))

(typedef CONSOLE_SCREEN_BUFFER_INFO

        (struct CONSOLE_SCREEN_BUFFER_INFO
          (dwSize COORD)
          (dwCursorPosition COORD)
          (wAttributes WORD)
          (srWindow SMALL_RECT)
          (dwMaximumWindowSize COORD)))
Various constants

(defvarl STD_INPUT_HANDLE (- #x100000000 10)) (defvarl STD_OUTPUT_HANDLE (- #x100000000 11)) (defvarl STD_ERROR_HANDLE (- #x100000000 12))

(defvarl NULL cptr-null) (defvarl INVALID_HANDLE_VALUE (cptr-int -1))

Foreign Function Bindings

(with-dyn-lib "kernel32.dll"

 (deffi GetStdHandle "GetStdHandle" HANDLE (DWORD))
 (deffi GetConsoleScreenBufferInfo "GetConsoleScreenBufferInfo"
        BOOL (HANDLE (ptr-out CONSOLE_SCREEN_BUFFER_INFO)))
 (deffi ReadConsoleOutputCharacter "ReadConsoleOutputCharacterW"
        BOOL (HANDLE (ptr-out (array 1 WCHAR))
                      DWORD COORD (ptr-out (array 1 DWORD)))))
Now the character at <2, 5> -- column 3, row 6.

(let ((console-handle (GetStdHandle STD_OUTPUT_HANDLE)))

 (when (equal console-handle INVALID_HANDLE_VALUE)
   (error "couldn't get console handle"))
 (let* ((cinfo (new CONSOLE_SCREEN_BUFFER_INFO))
        (getinfo-ok (GetConsoleScreenBufferInfo console-handle cinfo))
        (coord (if getinfo-ok
                 ^#S(COORD X ,(+ 2 cinfo.srWindow.Left)
                           Y ,(+ 5 cinfo.srWindow.Top))
                 ^#S(COORD X 0 Y 0)))
        (chars (vector 5))
        (nread (vector 1))
        (read-ok (ReadConsoleOutputCharacter console-handle chars
                                             1 coord nread)))
   (unless getinfo-ok
     (error "GetConsoleScreenBufferInfo failed"))
   (prinl cinfo)
   (unless read-ok
     (error "ReadConsoleOutputCharacter failed"))
   (unless (plusp [nread 0])
     (error "ReadConsoleOutputCharacter read zero characters"))
   (format t "character is ~s\n" [chars 0])))</lang>

XPL0

<lang XPL0>include c:\cxpl\stdlib; int C; [Cursor(3, 6); \move cursor to column 3, row 6 (top left = 0,0) \Call BIOS interrupt routine to read character (& attribute) at cursor position C:= CallInt($10, $0800, 0) & $00FF; \mask off attribute, leaving the character ]</lang>