Move cursor to screen home with TIMEOUT command!

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Post Reply
Message
Author
Aacini
Expert
Posts: 1885
Joined: 06 Dec 2011 22:15
Location: México City, México
Contact:

Move cursor to screen home with TIMEOUT command!

#1 Post by Aacini » 30 Oct 2015 22:40

Some time ago I realized that "timeout" command moves the cursor in order to show the seconds countdown in the same place. I tried to make good use of this point, with no luck.

Today I read this SO question entitled "ask set /p variable on a different line" that request to get user input with this format:

Code: Select all

enter your name
name:_(input would be here)
enter name above

I remembered timeout behavior and after several tests I discovered that when timeout is started in a separate cmd.exe session (i.e. at any side of a pipeline or in a FOR /F command), then the "previous" cursor position is lost and timeout show the seconds countdown at screen home (position 0,0)! I used this behavior to develop the program below:

Code: Select all

@echo off
setlocal

cls
echo/
echo Enter your name
echo First:
echo Enter first name above
echo/
echo Last:
echo Enter last name above

rem Move cursor to one line below screen size,
rem you must adjust this value to fit your screen.
for /L %%i in (1,1,30) do echo/

rem The line below do this:
rem 1. Show "Waiting for  1 seconds, press a key..." at current line.
rem 2. Move cursor to screen home (position 0,0) and show a "0" there;
rem    this scrolls screen contents and previous "Waiting" line disappear.
rem 3. Timeout command terminate; the cursor remains at second screen line.

for /F %%a in ('timeout /T 1 ^> CON') do rem

echo/
set /P "first=First: "
echo/
echo/
set /P "last=Last:  "
echo/
echo/
echo Name read: "%first% %last%"

The only disadvantage of this method is that a "0" will always appear at screen home. Tested on Windows 8.1.

Antonio

jeb
Expert
Posts: 1041
Joined: 30 Aug 2007 08:05
Location: Germany, Bochum

Re: Move cursor to screen home with TIMEOUT command!

#2 Post by jeb » 31 Oct 2015 05:49

:o :D Impressive, I lost to hope that it's possible with "normal" commands.

Btw. You don't need the for loop, this works also

Code: Select all

timeout /T 1 > CON

But how it works, why there is the difference with COn and without :?:

And redirecting to a file shows that it seems only be a backspace character.

aGerman
Expert
Posts: 4654
Joined: 22 Jan 2010 18:01
Location: Germany

Re: Move cursor to screen home with TIMEOUT command!

#3 Post by aGerman » 31 Oct 2015 07:18

jeb wrote:But how it works, why there is the difference with COn and without :?:

And redirecting to a file shows that it seems only be a backspace character.

I can't explain that behavior but I had a look into timeout.exe. Two API functions can be found that come in handy: WriteConsoleW and SetConsoleCursorPosition. I wasn't able to reproduce this effect with WriteConsoleW and backspace only. Thus, most likely SetConsoleCursorPosition is involved as well even if I still don't understand how this influences the redirection to CON.

Regards
aGerman

penpen
Expert
Posts: 1991
Joined: 23 Jun 2013 06:15
Location: Germany

Re: Move cursor to screen home with TIMEOUT command!

#4 Post by penpen » 31 Oct 2015 08:59

With mode to ensure a specific amount of lines:

Code: Select all

@echo off
setlocal
set "oldModeLines="
for /F "skip=3 tokens=2 delims=:" %%a in ('mode con /status') do if not defined oldModeLines set "oldModeLines=%%~a"
set "oldModeLines=%oldModeLines: =%"
set "modelines=30"

mode con lines=%modelines%

set /A "lines=%modelines%-7-2"

:: ...

rem Move cursor to one line below screen size,
rem you must adjust this value to fit your screen.
:: for /L %%i in (1,1,30) do echo/
for /L %%i in (1,1,%lines%) do echo/

:: ...

rem restore mode
mode con lines=%oldModeLines%


penpen

Edit: I've overseen the important note: "Move cursor to one line below screen size", so i've replaced my post.

penpen
Expert
Posts: 1991
Joined: 23 Jun 2013 06:15
Location: Germany

Re: Move cursor to screen home with TIMEOUT command!

#5 Post by penpen » 31 Oct 2015 09:57

I guess it's a doublebuffer issue:
All echoes print to a buffer, not changing the curor position.
When timeout is executed it reads the cursor position (0, 0).
The timeout.exe flushes the buffer restores the cursor position (0,0) and writes "0" at the cursor position.

But i'm unsure, because of that:
If you don't wait and press a key (for example a) before the timeout expires the cursor position is set after the text.
(Why should the command line interpreter flush the outputstream to the console when input arrives?)
(Edit: I really hope cmd.exe doesn't use the same buffer for both: Input and output... .)


penpen

jeb
Expert
Posts: 1041
Joined: 30 Aug 2007 08:05
Location: Germany, Bochum

Re: Move cursor to screen home with TIMEOUT command!

#6 Post by jeb » 05 Nov 2015 04:13

I played a bit with the timeout trick and it's possible to delete the first output of the timout command.
This line can be replaced with any content, in the expamle dots are used.
Gewartet wird 1 Sekunden. Weiter mit beliebiger Taste...
.........................................................


And it's also possible to access the top line of the screen, but currently I can't remove the 0 at the very end

Code: Select all

@echo off
setlocal EnableDelayedExpansion
call :backspace
call :CR
(SET LF=^
%=EMPTY=%
)
if "%1" == "out" (
  (echo(!CR!..................................................................)
  ping -n 2 localhost > NUL
  <nul set /p ".=.!CR!XXXXXXXXXXXXXXXXXX"
  set /p v=
  echo **** 2-
  exit /b
)
cls
for /L %%n in ( 1 1 30) do echo .... %%n

timeout /t 4 > con | "%~f0" out
echo **** 3-
echo !LF!!LF!**** 6-
for /L %%n in ( 1 1 30) do echo(
exit /b

:backspace
for /F %%A in ('"prompt $H & echo on & FOR %%B in (1) do REM"') do (
    set "BS=%%A"
)
exit /b

:CR
for /F "usebackq delims= " %%C in (`copy /z "%~f0" nul`) do set "cr=%%C"
exit /b

penpen
Expert
Posts: 1991
Joined: 23 Jun 2013 06:15
Location: Germany

Re: Move cursor to screen home with TIMEOUT command!

#7 Post by penpen » 05 Nov 2015 05:19

To avoid the unwanted behaviour when pressing a key immediately after the timeout is executed, just use the "/nobreak" parameter.

penpen

Ed Dyreen
Expert
Posts: 1569
Joined: 16 May 2011 08:21
Location: Flanders(Belgium)
Contact:

Re: Move cursor to screen home with TIMEOUT command!

#8 Post by Ed Dyreen » 16 Nov 2015 06:42

aGerman wrote:I can't explain that behavior but I had a look into timeout.exe. Two API functions can be found that come in handy: WriteConsoleW and SetConsoleCursorPosition. I wasn't able to reproduce this effect with WriteConsoleW and backspace only. Thus, most likely SetConsoleCursorPosition is involved as well even if I still don't understand how this influences the redirection to CON.
How did you figure out timeout.exe uses WriteConsoleW and SetConsoleCursorPosition ? Did you decompile it ? how ? :shock:

Aacini
Expert
Posts: 1885
Joined: 06 Dec 2011 22:15
Location: México City, México
Contact:

Re: Move cursor to screen home with TIMEOUT command!

#9 Post by Aacini » 16 Nov 2015 09:17

The .exe file format include a table of the functions that the program uses; this table is used by the program loader when the program start execution. The good news is that function names are stored in plain Ascii, so you only need to open the .exe file with any text editor (like Notepad) in order to see this table.

For example, this is the table of timeout.exe:

Code: Select all

x  «w  ♠w  ­v  µv  ▄v  ╬v  ∟y  tw      és  ls      Þ♣VerSetConditionMask e♣RtlVe
rifyVersionInfo  ntdll.dll 0☻LoadStringW < CharUpperW  USER32.dll  WS2_32.dll  ←
☺StrChrIW  ▲☺StrChrW O☺StrStrIW  R☺StrStrW SHLWAPI.dll ♠ GetFileVersionInfoSizeE
xW ♥ GetFileVersionInfoExW ► VerQueryValueW  VERSION.dll Q☺ExitProcess ä☻GetNumb
erOfConsoleInputEvents æ☺FlushConsoleInputBuffer 9♣SetThreadUILanguage ¯☺GetCons
oleMode  R♣Sleep Ë♦SetConsoleMode  Ã♦SetConsoleCursorPosition  └☻GetStdHandle  ♂
♣SetLastError  ☻♦PeekConsoleInputW ¶☺GetConsoleScreenBufferInfo  >☻GetFileType ├
♦SetConsoleCtrlHandler 7♥HeapSetInformation  H♦ReadConsoleInputW P☻GetLastError
 Ü☺FormatMessageW  ▓♥LocalFree ═♣WideCharToMultiByte ♂♠lstrlenW  ô CompareString
W  Ù☻GetThreadLocale É CompareStringA  Ó♣WriteConsoleW ‗☺GetConsoleOutputCP  ó☻G
etProcessHeap  3♥HeapFree  ;♥HeapValidate  /♥HeapAlloc 6♥HeapReAlloc 8♥HeapSize
 c☻GetModuleFileNameW  KERNEL32.dll  Ñ __iob_func  i☺_errno  ë♣wcstol  ¯♥_vsnwpr
intf U♣time  ■☻_memicmp  å♣wcstod  î♣wcstoul é☺_fileno ┐☺_get_osfhandle  └♦fprin
tf Â♦fflush  q _XcptFilter ╦ __p__commode  ‼☺_amsg_exit  ¾ __wgetmainargs  õ __s
et_app_type  ░♦exit  u☺_exit &☺_cexit  ð __p__fmode  µ __setusermatherr  Û☺_init
term msvcrt.dll  7 ?terminate@@YAXXZ 9☺_controlfp  l☺_except_handler4_common C♣S
etUnhandledExceptionFilter d☻GetModuleHandleA  -♦QueryPerformanceCounter
☻GetCurrentProcessId ♫☻GetCurrentThreadId  Í☻GetSystemTimeAsFileTime ‗☻GetTickCo
unt  é♣UnhandledExceptionFilter         ☻GetCurrentProcess a♣TerminateProcess  ☼
♣memset

This method also allows to discover undocumented switches in a program! 8)

Antonio

Ed Dyreen
Expert
Posts: 1569
Joined: 16 May 2011 08:21
Location: Flanders(Belgium)
Contact:

Re: Move cursor to screen home with TIMEOUT command!

#10 Post by Ed Dyreen » 16 Nov 2015 09:52

Aacini wrote:This method also allows to discover undocumented switches in a program! 8)
Excellent :mrgreen:

Squashman
Expert
Posts: 4465
Joined: 23 Dec 2011 13:59

Re: Move cursor to screen home with TIMEOUT command!

#11 Post by Squashman » 16 Nov 2015 10:47

We discussed some of that in this thread as well.
viewtopic.php?f=3&t=6137

einstein1969
Expert
Posts: 941
Joined: 15 Jun 2012 13:16
Location: Italy, Rome

Re: Move cursor to screen home with TIMEOUT command!

#12 Post by einstein1969 » 06 Dec 2015 12:44

jeb wrote:I played a bit with the timeout trick and it's possible to delete the first output of the timout command.
This line can be replaced with any content, in the expamle dots are used.
Gewartet wird 1 Sekunden. Weiter mit beliebiger Taste...
.........................................................


And it's also possible to access the top line of the screen, but currently I can't remove the 0 at the very end

Code: Select all

@echo off
setlocal EnableDelayedExpansion
call :backspace
call :CR
(SET LF=^
%=EMPTY=%
)
if "%1" == "out" (
  (echo(!CR!..................................................................)
  ping -n 2 localhost > NUL
  <nul set /p ".=.!CR!XXXXXXXXXXXXXXXXXX"
  set /p v=
  echo **** 2-
  exit /b
)
cls
for /L %%n in ( 1 1 30) do echo .... %%n

timeout /t 4 > con | "%~f0" out
echo **** 3-
echo !LF!!LF!**** 6-
for /L %%n in ( 1 1 30) do echo(
exit /b

:backspace
for /F %%A in ('"prompt $H & echo on & FOR %%B in (1) do REM"') do (
    set "BS=%%A"
)
exit /b

:CR
for /F "usebackq delims= " %%C in (`copy /z "%~f0" nul`) do set "cr=%%C"
exit /b


Hi jeb,

why there is a "set /P v=" ?

Code: Select all

  <nul set /p ".=.!CR!XXXXXXXXXXXXXXXXXX"
  set /p v=


Einstein1969

einstein1969
Expert
Posts: 941
Joined: 15 Jun 2012 13:16
Location: Italy, Rome

Re: Move cursor to screen home with TIMEOUT command!

#13 Post by einstein1969 » 06 Dec 2015 13:33

It's safe this?

Code: Select all

@echo off
mode 80,34

cls
echo(

For /L %%k in ( 31 -1 1) do (
    for /L %%n in ( 2 1 %%k) do echo(
    echo %%k
    for /L %%n in (%%k 1 30) do echo(
    call :goto_home
)
for /L %%n in (1 1 31) do echo(
pause>nul

exit /b

:goto_home
 timeout /t 1 > con | cmd /Q /C"for /F "usebackq delims= " %%C in (`copy /z "%~f0" nul`) do set/p".=.%%C                                                                  "
exit /b


Einstein1969

Aacini
Expert
Posts: 1885
Joined: 06 Dec 2011 22:15
Location: México City, México
Contact:

Re: Move cursor to screen home with TIMEOUT command!

#14 Post by Aacini » 25 Mar 2018 18:32

I was really bored, so I take the boat scheme posted at this SO question and used it to complete this program:

Code: Select all

@echo off
setlocal EnableDelayedExpansion

set "n=0"
for %%a in ("                                     |__                                       "
            "                                     |\/                                       "
            "                                     ---                                       "
            "                                     / | [                                     "
            "                              |      | |||                                     "
            "                            _/|     _/|-++'                                    "
            "                        +  +--|    |--|--|_ |-                                 "
            "                     { /|__|  |/\__|  |--- |||__/                              "
            "                    +---------------___[}-_===_.'____                 /\       "
            "                ____`-' ||___-{]_| _[}-  |     |_[___\==--            \/   _   "
            " __..._____--==/___]_|__|_____________________________[___\==--____,------' 7  "
            "|                                                                     BB-61/   "
            "\_________________________________________________________________________/    "
           ) do (
   set /A n+=1
   set "line[!n!]=%%~a"
)
set "spaces="
for /L %%i in (1,1,79) do set "spaces=!spaces! "
for /F %%a in ('echo prompt $H ^| cmd') do set "BS=%%a"
for /F "skip=4 delims=pR tokens=2" %%a in ('reg query hkcu\environment /v temp' ) do set "TAB=%%a"
for /F "tokens=2 delims=0" %%a in ('shutdown /? ^| findstr /BC:E') do if not defined TAB set "TAB=%%a"

mode 80
color 1F

for /L %%j in (78,-1,0) do (
   for /L %%i in (1,1,%n%) do echo/!line[%%i]:~%%j!
   call :MoveCursorHome
)
for /L %%j in (78,-1,0) do (
   for /L %%i in (1,1,%n%) do echo/!spaces:~%%j!!line[%%i]:~0^,%%j!
   call :MoveCursorHome
)
goto :EOF


:MoveCursorHome
timeout /T 1 > CON | cmd /Q /C for /F %%C in ('copy /Z "%~F0" NUL') do set /P "=.%%C%TAB%%TAB%%TAB%%TAB%%TAB%%TAB%%TAB%%TAB%" ^& set /P "=.%%C%TAB%%BS%%BS%%%C %%C"
exit /B
Antonio

Post Reply