From dd7a24d8ccae7cf943fcb66133c51591fb2e719d Mon Sep 17 00:00:00 2001 From: miere43 Date: Thu, 23 Jun 2016 23:22:38 +0300 Subject: [PATCH 1/4] Implemented terminal.getch() for Windows --- lib/pure/terminal.nim | 31 ++++++++++++++++++++----------- lib/windows/winlean.nim | 24 +++++++++++++++++++++++- 2 files changed, 43 insertions(+), 12 deletions(-) diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim index 60f064e7c..60fce04c9 100644 --- a/lib/pure/terminal.nim +++ b/lib/pure/terminal.nim @@ -493,17 +493,26 @@ template styledEcho*(args: varargs[expr]): expr = ## Echoes styles arguments to stdout using ``styledWriteLine``. callStyledEcho(args) -when defined(nimdoc): - proc getch*(): char = - ## Read a single character from the terminal, blocking until it is entered. - ## The character is not printed to the terminal. This is not available for - ## Windows. - discard -elif not defined(windows): - proc getch*(): char = - ## Read a single character from the terminal, blocking until it is entered. - ## The character is not printed to the terminal. This is not available for - ## Windows. +proc getch*(): char = + ## Read a single character from the terminal, blocking until it is entered. + ## The character is not printed to the terminal. + when defined(windows): + let fd = getStdHandle(STD_INPUT_HANDLE) + # Block until character is entered + discard waitForSingleObject(fd, INFINITE) + var record = INPUT_RECORD() + var recordPtr: ptr INPUT_RECORD = addr(record) + var numRead: cint + while true: + discard readConsoleInput(fd, recordPtr, 1, addr(numRead)) + if numRead == 0 or record.eventType != 1: + continue + let keyEvent = cast[ptr KEY_EVENT_RECORD](recordPtr) + # skip key release events + if keyEvent.bKeyDown == 0: + continue + return char(keyEvent.UnicodeChar) + else: let fd = getFileHandle(stdin) var oldMode: Termios discard fd.tcgetattr(addr oldMode) diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim index 04edeb2cb..9d44621b9 100644 --- a/lib/windows/winlean.nim +++ b/lib/windows/winlean.nim @@ -1016,4 +1016,26 @@ proc wsaCloseEvent*(hEvent: Handle): bool {.stdcall, importc: "WSACloseEvent", dynlib: "ws2_32.dll".} proc wsaResetEvent*(hEvent: Handle): bool - {.stdcall, importc: "WSAResetEvent", dynlib: "ws2_32.dll".} \ No newline at end of file + {.stdcall, importc: "WSAResetEvent", dynlib: "ws2_32.dll".} + +type + INPUT_RECORD* {.final, pure.} = object + eventType*: int16 + padding: array[18, byte] + KEY_EVENT_RECORD* {.final, pure.} = object + eventType*: int16 + bKeyDown*: WINBOOL + wRepeatCount*: int16 + wVirtualKeyCode*: int16 + wVirtualScanCode*: int16 + UnicodeChar*: int16 + dwControlKeyState*: DWORD + +when defined(useWinAnsi): + proc readConsoleInput*(hConsoleInput: Handle, lpBuffer: pointer, nLength: cint, + lpNumberOfEventsRead: ptr cint): cint + {.header: "", importc: "ReadConsoleInputA".} +else: + proc readConsoleInput*(hConsoleInput: Handle, lpBuffer: pointer, nLength: cint, + lpNumberOfEventsRead: ptr cint): cint + {.header: "", importc: "ReadConsoleInputW".} \ No newline at end of file From 57bd50a9166fa4171071f88bc29f37f6d8959c11 Mon Sep 17 00:00:00 2001 From: miere43 Date: Sun, 26 Jun 2016 16:01:37 +0300 Subject: [PATCH 2/4] Removed INPUT_KEY_RECORD --- lib/pure/terminal.nim | 17 ++++++----------- lib/windows/winlean.nim | 18 ++++++++---------- 2 files changed, 14 insertions(+), 21 deletions(-) diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim index 60fce04c9..62cee0ee8 100644 --- a/lib/pure/terminal.nim +++ b/lib/pure/terminal.nim @@ -498,20 +498,15 @@ proc getch*(): char = ## The character is not printed to the terminal. when defined(windows): let fd = getStdHandle(STD_INPUT_HANDLE) - # Block until character is entered - discard waitForSingleObject(fd, INFINITE) - var record = INPUT_RECORD() - var recordPtr: ptr INPUT_RECORD = addr(record) + var keyEvent = KEY_EVENT_RECORD() var numRead: cint while true: - discard readConsoleInput(fd, recordPtr, 1, addr(numRead)) - if numRead == 0 or record.eventType != 1: + # Block until character is entered + assert(waitForSingleObject(fd, INFINITE) == WAIT_OBJECT_0) + assert(readConsoleInput(fd, addr(keyEvent), 1, addr(numRead)) != 0) + if numRead == 0 or keyEvent.eventType != 1 or keyEvent.bKeyDown == 0: continue - let keyEvent = cast[ptr KEY_EVENT_RECORD](recordPtr) - # skip key release events - if keyEvent.bKeyDown == 0: - continue - return char(keyEvent.UnicodeChar) + return char(keyEvent.uChar) else: let fd = getFileHandle(stdin) var oldMode: Termios diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim index 9d44621b9..b24a62b33 100644 --- a/lib/windows/winlean.nim +++ b/lib/windows/winlean.nim @@ -1019,23 +1019,21 @@ proc wsaResetEvent*(hEvent: Handle): bool {.stdcall, importc: "WSAResetEvent", dynlib: "ws2_32.dll".} type - INPUT_RECORD* {.final, pure.} = object - eventType*: int16 - padding: array[18, byte] KEY_EVENT_RECORD* {.final, pure.} = object - eventType*: int16 + eventType*: uint16 + # padding: array[2, byte] bKeyDown*: WINBOOL - wRepeatCount*: int16 - wVirtualKeyCode*: int16 - wVirtualScanCode*: int16 - UnicodeChar*: int16 + wRepeatCount*: uint16 + wVirtualKeyCode*: uint16 + wVirtualScanCode*: uint16 + uChar*: uint16 dwControlKeyState*: DWORD when defined(useWinAnsi): proc readConsoleInput*(hConsoleInput: Handle, lpBuffer: pointer, nLength: cint, lpNumberOfEventsRead: ptr cint): cint - {.header: "", importc: "ReadConsoleInputA".} + {.dynlib: "kernel32", importc: "ReadConsoleInputA".} else: proc readConsoleInput*(hConsoleInput: Handle, lpBuffer: pointer, nLength: cint, lpNumberOfEventsRead: ptr cint): cint - {.header: "", importc: "ReadConsoleInputW".} \ No newline at end of file + {.dynlib: "kernel32", importc: "ReadConsoleInputW".} \ No newline at end of file From 7fc9585f7b77965f171dba5a866f06c2cc7b459a Mon Sep 17 00:00:00 2001 From: miere43 Date: Sun, 26 Jun 2016 17:46:43 +0300 Subject: [PATCH 3/4] getch() asserts to doAsserts --- lib/pure/terminal.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim index 62cee0ee8..c135f6a00 100644 --- a/lib/pure/terminal.nim +++ b/lib/pure/terminal.nim @@ -502,8 +502,8 @@ proc getch*(): char = var numRead: cint while true: # Block until character is entered - assert(waitForSingleObject(fd, INFINITE) == WAIT_OBJECT_0) - assert(readConsoleInput(fd, addr(keyEvent), 1, addr(numRead)) != 0) + doAssert(waitForSingleObject(fd, INFINITE) == WAIT_OBJECT_0) + doAssert(readConsoleInput(fd, addr(keyEvent), 1, addr(numRead)) != 0) if numRead == 0 or keyEvent.eventType != 1 or keyEvent.bKeyDown == 0: continue return char(keyEvent.uChar) From 8314412b99fbb101d44cee62a091c0a8e5f84089 Mon Sep 17 00:00:00 2001 From: miere43 Date: Sun, 26 Jun 2016 22:18:14 +0300 Subject: [PATCH 4/4] Added stdcall pragma to WINAPI methods. --- lib/windows/winlean.nim | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim index b24a62b33..c9b4b610c 100644 --- a/lib/windows/winlean.nim +++ b/lib/windows/winlean.nim @@ -1020,20 +1020,19 @@ proc wsaResetEvent*(hEvent: Handle): bool type KEY_EVENT_RECORD* {.final, pure.} = object - eventType*: uint16 - # padding: array[2, byte] + eventType*: int16 bKeyDown*: WINBOOL - wRepeatCount*: uint16 - wVirtualKeyCode*: uint16 - wVirtualScanCode*: uint16 - uChar*: uint16 + wRepeatCount*: int16 + wVirtualKeyCode*: int16 + wVirtualScanCode*: int16 + uChar*: int16 dwControlKeyState*: DWORD when defined(useWinAnsi): proc readConsoleInput*(hConsoleInput: Handle, lpBuffer: pointer, nLength: cint, lpNumberOfEventsRead: ptr cint): cint - {.dynlib: "kernel32", importc: "ReadConsoleInputA".} + {.stdcall, dynlib: "kernel32", importc: "ReadConsoleInputA".} else: proc readConsoleInput*(hConsoleInput: Handle, lpBuffer: pointer, nLength: cint, lpNumberOfEventsRead: ptr cint): cint - {.dynlib: "kernel32", importc: "ReadConsoleInputW".} \ No newline at end of file + {.stdcall, dynlib: "kernel32", importc: "ReadConsoleInputW".} \ No newline at end of file