diff --git a/.gitignore b/.gitignore index c5846cb..812fc77 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ examples/editor examples/interactive_basic examples/interactive_history -getkey +examples/getkey # Ignore all tests/* diff --git a/NOTICE.md b/NOTICE.md new file mode 100644 index 0000000..10ed51f --- /dev/null +++ b/NOTICE.md @@ -0,0 +1,25 @@ +# Sources of inspiration + +https://github.com/h3rald/nimline + +MIT License + +Copyright (c) 2017 Fabio Cevasco + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/examples/getkey.nim b/examples/getkey.nim new file mode 100644 index 0000000..87d744f --- /dev/null +++ b/examples/getkey.nim @@ -0,0 +1,28 @@ +# handy tool that prints out char codes when pressed + +import jale/uniterm +import jale/keycodes +import strutils +import terminal +import os +import tables + +echo "Press 'ctrl+c' to quit" +echo "Press 'ctrl+a' to print a horizontal bar" + +var escape = false +if paramCount() > 0 and paramStr(1) == "esc": + escape = true + +while true: + + let key = if escape: getKey() else: int(uniGetchr()) + if key == 3: + break + if key == 1: + echo "=".repeat(terminalWidth()) + else: + if keysById.hasKey(key): + echo keysById[key] + else: + echo key diff --git a/getkey.nim b/getkey.nim deleted file mode 100644 index 926f207..0000000 --- a/getkey.nim +++ /dev/null @@ -1,15 +0,0 @@ -# handy tool that prints out char codes when pressed - -import terminal -import strutils - -echo "Press 'ctrl+c' to quit" -echo "Press 'ctrl+a' to print a horizontal bar" -while true: - let key = int(getch()) - if key == 3: - break - if key == 1: - echo "=".repeat(terminalWidth()) - else: - echo key diff --git a/src/jale/keycodes.nim b/src/jale/keycodes.nim index cbe109e..f93556a 100644 --- a/src/jale/keycodes.nim +++ b/src/jale/keycodes.nim @@ -2,18 +2,19 @@ import tables import strutils -import terminal +import uniterm type JaleKeycode* = enum jkStart = 255 # jale keycodes exported start one above jkStart + # arrow keys jkLeft = 256, jkRight, jkUp, jkDown, - jkHome, jkEnd, jkPageUp, jkPageDown, - jkShiftLeft, jkShiftRight, jkShiftUp, jkShiftDown, jkCtrlLeft, jkCtrlRight, jkCtrlUp, jkCtrlDown, + # other 4 move keys + jkHome, jkEnd, jkPageUp, jkPageDown, jkCtrlHome, jkCtrlEnd, jkCtrlPageUp, jkCtrlPageDown, - jkDelete, jkBackspace, - jkInsert, jkEnter + # special keys + jkDelete, jkBackspace, jkInsert, jkEnter, jkFinish, # jale keycodes exported end one below jkFinish # non-exported jale keycodes come here: @@ -28,6 +29,12 @@ block: keysById[i] = "ctrl+" & $char(i + 96) keysById[9] = "tab" + # keysById[8] will never get triggered because it's an escape seq + + keysById[28] = r"ctrl+\" + keysById[29] = "ctrl+]" + keysByName[r"ctrl+\"] = 28 + keysByName["ctrl+]"] = 29 for i in countup(1, 26): keysByName[keysById[i]] = i @@ -51,15 +58,33 @@ proc defEscSeq(keys: seq[int], id: JaleKeycode) = block: when defined(windows): defEscSeq(@[224], jkContinue) + + # arrow keys defEscSeq(@[224, 72], jkUp) defEscSeq(@[224, 80], jkDown) defEscSeq(@[224, 77], jkRight) defEscSeq(@[224, 75], jkLeft) + # ctrl+arrow keys + defEscSeq(@[224, 141], jkCtrlUp) + defEscSeq(@[224, 145], jkCtrlDown) + defEscSeq(@[224, 116], jkCtrlRight) + defEscSeq(@[224, 115], jkCtrlLeft) + # moves defEscSeq(@[224, 71], jkHome) defEscSeq(@[224, 79], jkEnd) + defEscSeq(@[224, 73], jkPageUp) + defEscSeq(@[224, 81], jkPageDown) + # ctrl+moves + defEscSeq(@[224, 134], jkCtrlPageUp) + defEscSeq(@[224, 118], jkCtrlPageDown) + defEscSeq(@[224, 115], jkCtrlHome) + defEscSeq(@[224, 116], jkCtrlEnd) + + # special keys + defEscSeq(@[8], jkBackspace) + defEscSeq(@[13], jkEnter) defEscSeq(@[224, 82], jkInsert) defEscSeq(@[224, 83], jkDelete) - # TODO: finish defining escape sequences else: # arrow keys defEscSeq(@[27], jkContinue) @@ -73,33 +98,19 @@ block: defEscSeq(@[27, 91, 49], jkContinue) defEscSeq(@[27, 91, 49, 59], jkContinue) defEscSeq(@[27, 91, 49, 59, 53], jkContinue) # ctrl - defEscSeq(@[27, 91, 49, 59, 50], jkContinue) # shift - defEscSeq(@[27, 91, 49, 59, 50, 65], jkShiftUp) - defEscSeq(@[27, 91, 49, 59, 50, 66], jkShiftDown) - defEscSeq(@[27, 91, 49, 59, 50, 67], jkShiftRight) - defEscSeq(@[27, 91, 49, 59, 50, 68], jkShiftLeft) + defEscSeq(@[27, 91, 49, 59, 50], jkContinue) # shift defEscSeq(@[27, 91, 49, 59, 53, 65], jkCtrlUp) # ctrl defEscSeq(@[27, 91, 49, 59, 53, 66], jkCtrlDown) # ctrl defEscSeq(@[27, 91, 49, 59, 53, 67], jkCtrlRight) # ctrl defEscSeq(@[27, 91, 49, 59, 53, 68], jkCtrlLeft) # ctrl - # home, end + # other 4 move keys defEscSeq(@[27, 91, 72], jkHome) defEscSeq(@[27, 91, 70], jkEnd) - - # ctrl+ home, end - defEscSeq(@[27, 91, 49, 59, 53, 72], jkCtrlHome) - defEscSeq(@[27, 91, 49, 59, 53, 70], jkCtrlEnd) - - # fancy keys like delete, insert, pgup, pgdown - defEscSeq(@[27, 91, 51], jkContinue) - defEscSeq(@[27, 91, 50], jkContinue) defEscSeq(@[27, 91, 54], jkContinue) defEscSeq(@[27, 91, 53], jkContinue) - defEscSeq(@[27, 91, 51, 126], jkDelete) - defEscSeq(@[27, 91, 50, 126], jkInsert) defEscSeq(@[27, 91, 53, 126], jkPageUp) defEscSeq(@[27, 91, 54, 126], jkPageDown) @@ -111,16 +122,24 @@ block: defEscSeq(@[27, 91, 54, 59, 53], jkContinue) defEscSeq(@[27, 91, 54, 59, 53, 126], jkCtrlPageDown) - # other keys + # ctrl+ home, end + defEscSeq(@[27, 91, 49, 59, 53, 72], jkCtrlHome) + defEscSeq(@[27, 91, 49, 59, 53, 70], jkCtrlEnd) + + # other keys + defEscSeq(@[27, 91, 51], jkContinue) + defEscSeq(@[27, 91, 50], jkContinue) + defEscSeq(@[27, 91, 51, 126], jkDelete) + defEscSeq(@[27, 91, 50, 126], jkInsert) defEscSeq(@[13], jkEnter) defEscSeq(@[127], jkBackspace) - # TODO ctrl+h as backspace? test more terminals + defEscSeq(@[8], jkBackspace) proc getKey*: int = var key: int = 0 while true: key *= 256 - key += int(getch()) + key += int(uniGetChr()) if escapeSeqs.hasKey(key): if escapeSeqs[key] != jkContinue: key = int(escapeSeqs[key]) diff --git a/src/jale/uniterm.nim b/src/jale/uniterm.nim new file mode 100644 index 0000000..56f3ac6 --- /dev/null +++ b/src/jale/uniterm.nim @@ -0,0 +1,26 @@ +## Universal (cross platform) terminal abstractions + +# source: https://github.com/h3rald/nimline/blob/master/nimline.nim +# lines 42-56 (modified) + +when defined(windows): + proc putchr(c: cint): cint {.discardable, header: "", importc: "_putch".} + proc getchr(): cint {.header: "", importc: "_getch".} + + proc uniPutChr*(c: char) = + ## Prints an ASCII character to stdout. + putchr(c.cint) + proc uniGetChr*: int = + ## Retrieves an ASCII character from stdin. + getchr().int + +else: + import terminal + proc uniPutChr*(c: char) = + ## Prints an ASCII character to stdout. + stdout.write(c) + + proc uniGetChr*: int = + ## Retrieves an ASCII character from stdin. + return getch().int +