Re: About get_key() alternative
- Posted by petelomax Oct 17, 2020
- 892 views
Sure. There should be no real need for this to be part of the core interpreter, or wait for the next release.
I started cobbling the following together from some existing phix innards, reworked to run on Eu, before realising
(a) I was quite, quite stumped as to how RDS Eu might invoke kernel calls on linux, and
(b) there is a far easier way (see below), but I got the windows (32-)bit working so here it is:
integer c_init = 0 atom stdin, knl32, xAllocConsole, xGetStdHandle, xSetConsoleMode, stdin_redirected=0, xPeekConsoleInputA, xReadConsoleInputA, pBuffer, pBytes constant STD_INPUT_HANDLE = -10, ENABLE_PROCESSED_INPUT = 1, P = C_POINTER, I = C_INT procedure init_console() c_init = 1 if platform()=WINDOWS then knl32 = open_dll("kernel32.dll") xAllocConsole = define_c_proc(knl32,"AllocConsole",{}) xGetStdHandle = define_c_func(knl32,"GetStdHandle",{P},P) xSetConsoleMode = define_c_func(knl32,"SetConsoleMode",{P,I},I) xPeekConsoleInputA = define_c_proc(knl32,"PeekConsoleInputA",{P,P,I,P}) xReadConsoleInputA = define_c_proc(knl32,"ReadConsoleInputA",{P,P,I,P}) pBuffer = allocate(20) -- sizeof(INPUT_RECORD[/KEY_EVENT_RECORD]) pBytes = allocate(8) -- 4 or 8? (DWORD/) c_proc(xAllocConsole,{}) stdin = c_func(xGetStdHandle,{STD_INPUT_HANDLE}) stdin_redirected = c_func(xSetConsoleMode,{stdin,ENABLE_PROCESSED_INPUT}) else pBuffer = allocate(40) -- termios struct (+ 4 bytes padding) stdin = 0 end if end procedure global function getKey() -- -- drop-in replacement for the get_key() builtin -- integer keyChar = -1 if not c_init then init_console() end if if platform()=WINDOWS then while true do c_proc(xPeekConsoleInputA,{stdin,pBuffer,1,pBytes}) if peek4s(pBytes)=0 then exit end if c_proc(xReadConsoleInputA,{stdin,pBuffer,1,pBytes}) if peek4s(pBuffer)=1 -- KEY_EVENT and peek4s(pBuffer+4)=1 then -- keyDown(ignore key up events) keyChar = peek(pBuffer+14) if keyChar!=0 then exit end if keyChar = #100+peek(pBuffer+12) -- keyScan if keyChar!=#12A -- shift key and keyChar!=#11D -- ctrl key and keyChar!=#138 then -- alt key exit end if end if end while else -- LINUX -- sys_ioctl(54/pBuffer/stdin/TCGETS(0x5401))... oh dear -- poke4(pBuffer+12,and_bits(peek4s(pBuffer+12),#FFFFFFF5)) -- not(ICANON or ECHO) ;turn off echo -- poke(pBuffer+22,0) -- VMIN ;turn off canonical mode -- poke(pBuffer+23,0) -- VTIME ;we dont want to wait for keystrokes -- sys_ioctl(54/pBuffer/stdin/TCSETS(0x5402))... oh dear -- keyChar = sys_read(3/stdin/pBuffer/1).. oh dear -- if keyChar=0 then keyChar=-1 end if -- poke4(pBuffer+12,or_bits(peek4s(pBuffer+12),#0000000A)) -- (ICANON or ECHO) ;turn on echo -- poke(pBuffer+22,1) -- VMIN ;turn on canonical mode --? poke(pBuffer+23,1) -- VTIME ;wait for keystrokes -- sys_ioctl(54/pBuffer/stdin/TCSETS(0x5402))... oh dear end if return keyChar end function
As promised, a far (far) easier way... (DOH)
global function getKey() -- -- drop-in replacement for the get_key() builtin -- integer ch = get_key() if platform()=WINDOWS then if ch=1015936 then ch=9 end if else -- linux -- if ch=??????? then ch=? end if end if return ch end function
test either of the above with say
while true do integer ch = getKey() ?ch if ch=#1B then exit end if -- escape sleep(0.1) end while