summary refs log tree commit diff stats
path: root/lib/pure
diff options
context:
space:
mode:
Diffstat (limited to 'lib/pure')
-rw-r--r--lib/pure/terminal.nim158
1 files changed, 73 insertions, 85 deletions
diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim
index f2be5cf18..904274ade 100644
--- a/lib/pure/terminal.nim
+++ b/lib/pure/terminal.nim
@@ -19,22 +19,30 @@
 import macros
 import strformat
 from strutils import toLowerAscii
-import colors
+import colors, tables
 
-const
-  hasThreadSupport = compileOption("threads")
+when defined(windows):
+  import winlean
 
-when not hasThreadSupport:
-  import tables
-  var
-    colorsFGCache = initTable[Color, string]()
-    colorsBGCache = initTable[Color, string]()
-    styleCache = initTable[int, string]()
+type
+  PTerminal = ref object
+    trueColorIsSupported: bool
+    trueColorIsEnabled: bool
+    fgSetColor: bool
+    when defined(windows):
+      hStdout: Handle
+      hStderr: Handle
+      oldStdoutAttr: int16
+      oldStderrAttr: int16
 
-var
-  trueColorIsSupported: bool
-  trueColorIsEnabled: bool
-  fgSetColor: bool
+var gTerm {.threadvar.}: PTerminal
+
+proc newTerminal(): PTerminal
+
+proc getTerminal(): PTerminal {.inline.} =
+  if isNil(gTerm):
+    gTerm = newTerminal()
+  result = gTerm
 
 const
   fgPrefix = "\x1b[38;2;"
@@ -156,23 +164,6 @@ when defined(windows):
   proc setConsoleMode(hConsoleHandle: Handle, dwMode: DWORD): WINBOOL{.
       stdcall, dynlib: "kernel32", importc: "SetConsoleMode".}
 
-  var
-    hStdout: Handle # = createFile("CONOUT$", GENERIC_WRITE, 0, nil,
-                    #              OPEN_ALWAYS, 0, 0)
-    hStderr: Handle
-
-  block:
-    var hStdoutTemp = getStdHandle(STD_OUTPUT_HANDLE)
-    if duplicateHandle(getCurrentProcess(), hStdoutTemp, getCurrentProcess(),
-                       addr(hStdout), 0, 1, DUPLICATE_SAME_ACCESS) == 0:
-      when defined(consoleapp):
-        raiseOSError(osLastError())
-    var hStderrTemp = getStdHandle(STD_ERROR_HANDLE)
-    if duplicateHandle(getCurrentProcess(), hStderrTemp, getCurrentProcess(),
-                       addr(hStderr), 0, 1, DUPLICATE_SAME_ACCESS) == 0:
-      when defined(consoleapp):
-        raiseOSError(osLastError())
-
   proc getCursorPos(h: Handle): tuple [x,y: int] =
     var c: CONSOLESCREENBUFFERINFO
     if getConsoleScreenBufferInfo(h, addr(c)) == 0:
@@ -193,12 +184,23 @@ when defined(windows):
       return c.wAttributes
     return 0x70'i16 # ERROR: return white background, black text
 
-  var
-    oldStdoutAttr = getAttributes(hStdout)
-    oldStderrAttr = getAttributes(hStderr)
+  proc initTerminal(term: PTerminal) =
+    var hStdoutTemp = getStdHandle(STD_OUTPUT_HANDLE)
+    if duplicateHandle(getCurrentProcess(), hStdoutTemp, getCurrentProcess(),
+                       addr(term.hStdout), 0, 1, DUPLICATE_SAME_ACCESS) == 0:
+      when defined(consoleapp):
+        raiseOSError(osLastError())
+    var hStderrTemp = getStdHandle(STD_ERROR_HANDLE)
+    if duplicateHandle(getCurrentProcess(), hStderrTemp, getCurrentProcess(),
+                       addr(term.hStderr), 0, 1, DUPLICATE_SAME_ACCESS) == 0:
+      when defined(consoleapp):
+        raiseOSError(osLastError())
+    term.oldStdoutAttr = getAttributes(term.hStdout)
+    term.oldStderrAttr = getAttributes(term.hStderr)
 
   template conHandle(f: File): Handle =
-    if f == stderr: hStderr else: hStdout
+    let term = getTerminal()
+    if f == stderr: term.hStderr else: term.hStdout
 
 else:
   import termios, posix, os, parseutils
@@ -459,10 +461,11 @@ proc eraseScreen*(f: File) =
 proc resetAttributes*(f: File) =
   ## Resets all attributes.
   when defined(windows):
+    let term = getTerminal()
     if f == stderr:
-      discard setConsoleTextAttribute(hStderr, oldStderrAttr)
+      discard setConsoleTextAttribute(term.hStderr, term.oldStderrAttr)
     else:
-      discard setConsoleTextAttribute(hStdout, oldStdoutAttr)
+      discard setConsoleTextAttribute(term.hStdout, term.oldStdoutAttr)
   else:
     f.write(ansiResetCode)
 
@@ -487,14 +490,7 @@ when not defined(windows):
     gBG {.threadvar.}: int
 
 proc ansiStyleCode*(style: int): string =
-  when hasThreadSupport:
-    result = fmt"{stylePrefix}{style}m"
-  else:
-    if styleCache.hasKey(style):
-      result = styleCache[style]
-    else:
-      result = fmt"{stylePrefix}{style}m"
-      styleCache[style] = result
+  result = fmt"{stylePrefix}{style}m"
 
 template ansiStyleCode*(style: Style): string =
   ansiStyleCode(style.int)
@@ -521,10 +517,11 @@ proc setStyle*(f: File, style: set[Style]) =
 proc writeStyled*(txt: string, style: set[Style] = {styleBright}) =
   ## Writes the text `txt` in a given `style` to stdout.
   when defined(windows):
-    var old = getAttributes(hStdout)
+    let term = getTerminal()
+    var old = getAttributes(term.hStdout)
     stdout.setStyle(style)
     stdout.write(txt)
-    discard setConsoleTextAttribute(hStdout, old)
+    discard setConsoleTextAttribute(term.hStdout, old)
   else:
     stdout.setStyle(style)
     stdout.write(txt)
@@ -633,32 +630,16 @@ template ansiForegroundColorCode*(fg: static[ForegroundColor],
   ansiStyleCode(fg.int + bright.int * 60)
 
 proc ansiForegroundColorCode*(color: Color): string =
-  when hasThreadSupport:
-    let rgb = extractRGB(color)
-    result = fmt"{fgPrefix}{rgb.r};{rgb.g};{rgb.b}m"
-  else:
-    if colorsFGCache.hasKey(color):
-      result = colorsFGCache[color]
-    else:
-      let rgb = extractRGB(color)
-      result = fmt"{fgPrefix}{rgb.r};{rgb.g};{rgb.b}m"
-      colorsFGCache[color] = result
+  let rgb = extractRGB(color)
+  result = fmt"{fgPrefix}{rgb.r};{rgb.g};{rgb.b}m"
 
 template ansiForegroundColorCode*(color: static[Color]): string =
   const rgb = extractRGB(color)
   (static(fmt"{fgPrefix}{rgb.r};{rgb.g};{rgb.b}m"))
 
 proc ansiBackgroundColorCode*(color: Color): string =
-  when hasThreadSupport:
-    let rgb = extractRGB(color)
-    result = fmt"{bgPrefix}{rgb.r};{rgb.g};{rgb.b}m"
-  else:
-    if colorsBGCache.hasKey(color):
-      result = colorsBGCache[color]
-    else:
-      let rgb = extractRGB(color)
-      result = fmt"{bgPrefix}{rgb.r};{rgb.g};{rgb.b}m"
-      colorsFGCache[color] = result
+  let rgb = extractRGB(color)
+  result = fmt"{bgPrefix}{rgb.r};{rgb.g};{rgb.b}m"
 
 template ansiBackgroundColorCode*(color: static[Color]): string =
   const rgb = extractRGB(color)
@@ -666,16 +647,17 @@ template ansiBackgroundColorCode*(color: static[Color]): string =
 
 proc setForegroundColor*(f: File, color: Color) =
   ## Sets the terminal's foreground true color.
-  if trueColorIsEnabled:
+  if getTerminal().trueColorIsEnabled:
     f.write(ansiForegroundColorCode(color))
 
 proc setBackgroundColor*(f: File, color: Color) =
   ## Sets the terminal's background true color.
-  if trueColorIsEnabled:
+  if getTerminal().trueColorIsEnabled:
     f.write(ansiBackgroundColorCode(color))
 
 proc setTrueColor(f: File, color: Color) =
-  if fgSetColor:
+  let term = getTerminal()
+  if term.fgSetColor:
     setForegroundColor(f, color)
   else:
     setBackgroundColor(f, color)
@@ -727,11 +709,10 @@ macro styledWrite*(f: File, m: varargs[typed]): untyped =
   ##   stdout.styledWrite(fgRed, "red text ")
   ##   stdout.styledWrite(fgGreen, "green text")
   ##
-  let m = callsite()
   var reset = false
   result = newNimNode(nnkStmtList)
 
-  for i in countup(2, m.len - 1):
+  for i in countup(0, m.len - 1):
     let item = m[i]
     case item.kind
     of nnkStrLit..nnkTripleStrLit:
@@ -872,54 +853,61 @@ proc resetAttributes*() {.noconv.} =
 
 proc isTrueColorSupported*(): bool =
   ## Returns true if a terminal supports true color.
-  return trueColorIsSupported
+  return getTerminal().trueColorIsSupported
 
 when defined(windows):
   import os
 
 proc enableTrueColors*() =
   ## Enable true color.
+  var term = getTerminal()
   when defined(windows):
     var
       ver: OSVERSIONINFO
     ver.dwOSVersionInfoSize = sizeof(ver).DWORD
     let res = getVersionExW(addr ver)
     if res == 0:
-      trueColorIsSupported = false
+      term.trueColorIsSupported = false
     else:
-      trueColorIsSupported = ver.dwMajorVersion > 10 or
+      term.trueColorIsSupported = ver.dwMajorVersion > 10 or
         (ver.dwMajorVersion == 10 and (ver.dwMinorVersion > 0 or
         (ver.dwMinorVersion == 0 and ver.dwBuildNumber >= 10586)))
-    if not trueColorIsSupported:
-      trueColorIsSupported = getEnv("ANSICON_DEF").len > 0
+    if not term.trueColorIsSupported:
+      term.trueColorIsSupported = getEnv("ANSICON_DEF").len > 0
 
-    if trueColorIsSupported:
+    if term.trueColorIsSupported:
       if getEnv("ANSICON_DEF").len == 0:
         var mode: DWORD = 0
         if getConsoleMode(getStdHandle(STD_OUTPUT_HANDLE), addr(mode)) != 0:
           mode = mode or ENABLE_VIRTUAL_TERMINAL_PROCESSING
           if setConsoleMode(getStdHandle(STD_OUTPUT_HANDLE), mode) != 0:
-            trueColorIsEnabled = true
+            term.trueColorIsEnabled = true
           else:
-            trueColorIsEnabled = false
+            term.trueColorIsEnabled = false
       else:
-        trueColorIsEnabled = true
+        term.trueColorIsEnabled = true
   else:
-    trueColorIsSupported = string(getEnv("COLORTERM")).toLowerAscii() in ["truecolor", "24bit"]
-    trueColorIsEnabled = trueColorIsSupported
+    term.trueColorIsSupported = string(getEnv("COLORTERM")).toLowerAscii() in ["truecolor", "24bit"]
+    term.trueColorIsEnabled = term.trueColorIsSupported
 
 proc disableTrueColors*() =
   ## Disable true color.
+  var term = getTerminal()
   when defined(windows):
-    if trueColorIsSupported:
+    if term.trueColorIsSupported:
       if getEnv("ANSICON_DEF").len == 0:
         var mode: DWORD = 0
         if getConsoleMode(getStdHandle(STD_OUTPUT_HANDLE), addr(mode)) != 0:
           mode = mode and not ENABLE_VIRTUAL_TERMINAL_PROCESSING
           discard setConsoleMode(getStdHandle(STD_OUTPUT_HANDLE), mode)
-      trueColorIsEnabled = false
+      term.trueColorIsEnabled = false
   else:
-    trueColorIsEnabled = false
+    term.trueColorIsEnabled = false
+
+proc newTerminal(): PTerminal =
+  new result
+  when defined(windows):
+    initTerminal(result)
 
 when not defined(testing) and isMainModule:
   assert ansiStyleCode(styleBright) == "\e[1m"