~ehmry/nim_genode

nim_genode/src/genode/terminals.nim -rw-r--r-- 3.3 KiB
db729733Emery Hemingway Version 20.11.1 4 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
#
# Copyright (C) 2018 Genode Labs GmbH
#
# This file is part of the Genode OS framework, which is distributed
# under the terms of the GNU Affero General Public License version 3.
#

##
## This module provides a client of Genode
## **Terminal** services.
##
## For more information on the **Terminal** service please
## refer to section 4.5.3 of the `Genode Foundations
## <http://genode.org/documentation/genode-foundations-18-05.pdf>`_
## manual.
##

import std/unicode
import ../genode, ./signals

const terminalH = "<terminal_session/connection.h>"

type
  ConnectionBase {.
    importcpp: "Terminal::Connection", header: terminalH.} = object
  Connection = Constructible[ConnectionBase]

  TerminalClient* = ref TerminalClientObj
  TerminalClientObj = object
    conn: Connection
    readAvailSigh, sizeChangedSigh: SignalDispatcher

  CppSize {.
    importcpp: "Terminal::Session::Size", header: terminalH.} = object

  TerminalSize* = object
    columns*, lines*: int

proc columns(ts: CppSize): int {.importcpp.}
proc lines(ts: CppSize): int {.importcpp.}

proc construct(c: Connection; env: GenodeEnv; label: cstring) {.
    importcpp: "#.construct(*#, @)", tags: [RpcEffect].}

proc size(c: Connection): CppSize {.tags: [RpcEffect], importcpp: "#->size()".}

proc read(c: Connection; buf: pointer; bufLen: int): int {.tags: [RpcEffect],
    importcpp: "#->read(@)".}

proc write(c: Connection; buf: cstring|pointer; bufLen: int): int {.tags: [
    RpcEffect], importcpp: "#->write(@)".}

proc newTerminalClient*(env: GenodeEnv; label = ""): TerminalClient =
  ## Open a new **Terminal** session.
  new result
  result.conn.construct(env, label)

proc size*(tc: TerminalClient): TerminalSize =
  ## Return the number of character available for reading.
  let size = tc.conn.size()
  TerminalSize(columns: size.columns(), lines: size.lines())

proc avail*(tc: TerminalClient): bool =
  ## Check if character are available for reading.
  proc avail(c: Connection): bool {.tags: [RpcEffect], importcpp: "#->avail()".}
  tc.conn.avail()

proc read*(tc: TerminalClient; buffer: pointer; bufLen: int): int =
  ## Read any available data from the terminal.
  tc.conn.read(buffer, bufLen)

proc read*(tc: TerminalClient): string =
  ## Read any available data from the terminal.
  var buf: array[4096, char]
  let n = tc.read(buf[0].addr, buf.len)
  result = newString(n)
  copyMem(result[0].addr, buf[0].addr, n)

proc write*(tc: TerminalClient; buffer: pointer; bufLen: int): int =
  ## Write data from the terminal. This procedure must be expected
  ## to perform a short write, or to be blocked arbitrarily by the server.
  tc.conn.write(buffer, bufLen)

proc write*(tc: TerminalClient; s: string): int =
  ## Write a string to the terminal.
  tc.conn.write(s.cstring, s.len)

proc readAvailSigh*(tc: TerminalClient; cap: SignalContextCapability) =
  ## Install a signal handler to be informed
  ## about ready-to-read characters.
  proc read_avail_sigh(c: Connection; cap: SignalContextCapability) {.
      tags: [RpcEffect], importcpp: "#->read_avail_sigh(#)".}
  tc.conn.read_avail_sigh(cap)

proc sizeChangedSigh*(tc: TerminalClient; cap: SignalContextCapability) =
  ## Install a signal handler to be notified on terminal-size changes.
  proc size_changed_sigh(c: Connection; cap: SignalContextCapability) {.
      tags: [RpcEffect], importcpp: "#->size_changed_sigh(#)".}
  tc.conn.size_changed_sigh(cap)