admin.go (view raw)
1//
2// Copyright (c) 2019 Ted Unangst <tedu@tedunangst.com>
3//
4// Permission to use, copy, modify, and distribute this software for any
5// purpose with or without fee is hereby granted, provided that the above
6// copyright notice and this permission notice appear in all copies.
7//
8// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15
16package main
17
18/*
19#include <termios.h>
20*/
21import "C"
22import (
23 "bufio"
24 "fmt"
25 "io/ioutil"
26 "log"
27 "os"
28)
29
30func adminscreen() {
31 log.SetOutput(ioutil.Discard)
32 stdout := bufio.NewWriter(os.Stdout)
33 esc := "\x1b"
34 smcup := esc + "[?1049h"
35 rmcup := esc + "[?1049l"
36
37 messages := []*struct {
38 name string
39 label string
40 text string
41 }{
42 {
43 name: "servermsg",
44 label: "server",
45 text: string(serverMsg),
46 },
47 {
48 name: "aboutmsg",
49 label: "about",
50 text: string(aboutMsg),
51 },
52 {
53 name: "loginmsg",
54 label: "login",
55 text: string(loginMsg),
56 },
57 }
58 cursel := 0
59
60 hidecursor := func() {
61 stdout.WriteString(esc + "[?25l")
62 }
63 showcursor := func() {
64 stdout.WriteString(esc + "[?12;25h")
65 }
66 movecursor := func(x, y int) {
67 stdout.WriteString(fmt.Sprintf(esc+"[%d;%dH", y, x))
68 }
69 moveleft := func() {
70 stdout.WriteString(esc + "[1D")
71 }
72 clearscreen := func() {
73 stdout.WriteString(esc + "[2J")
74 }
75 clearline := func() {
76 stdout.WriteString(esc + "[2K")
77 }
78 colorfn := func(code int) func(string) string {
79 return func(s string) string {
80 return fmt.Sprintf(esc+"[%dm"+"%s"+esc+"[0m", code, s)
81 }
82 }
83 reverse := colorfn(7)
84 magenta := colorfn(35)
85 readchar := func() byte {
86 var buf [1]byte
87 os.Stdin.Read(buf[:])
88 c := buf[0]
89 return c
90 }
91
92 savedtio := new(C.struct_termios)
93 C.tcgetattr(1, savedtio)
94 restore := func() {
95 stdout.WriteString(rmcup)
96 showcursor()
97 stdout.Flush()
98 C.tcsetattr(1, C.TCSAFLUSH, savedtio)
99 }
100 defer restore()
101
102 init := func() {
103 tio := new(C.struct_termios)
104 C.tcgetattr(1, tio)
105 tio.c_lflag = tio.c_lflag & ^C.uint(C.ECHO|C.ICANON)
106 C.tcsetattr(1, C.TCSADRAIN, tio)
107
108 hidecursor()
109 stdout.WriteString(smcup)
110 clearscreen()
111 movecursor(1, 1)
112 stdout.Flush()
113 }
114
115 msglineno := func(idx int) int {
116 return 4 + idx*2
117 }
118
119 drawmessage := func(idx int) {
120 line := msglineno(idx)
121 movecursor(4, line)
122 label := messages[idx].label
123 if idx == cursel {
124 label = reverse(label)
125 }
126 stdout.WriteString(fmt.Sprintf("%s %s", label, messages[idx].text))
127 }
128
129 drawscreen := func() {
130 clearscreen()
131 movecursor(4, msglineno(-1))
132 stdout.WriteString(magenta(serverName + " admin panel"))
133 for i := range messages {
134 drawmessage(i)
135 }
136 movecursor(4, msglineno(len(messages)))
137 stdout.WriteString(magenta("j/k to move - q to quit - enter to edit"))
138 stdout.Flush()
139 }
140
141 selectnext := func() {
142 if cursel < len(messages)-1 {
143 cursel++
144 }
145 drawscreen()
146 }
147 selectprev := func() {
148 if cursel > 0 {
149 cursel--
150 }
151 drawscreen()
152 }
153 editsel := func() {
154 movecursor(4, msglineno(cursel))
155 clearline()
156 m := messages[cursel]
157 stdout.WriteString(reverse(magenta(m.label)))
158 text := m.text
159 stdout.WriteString(" ")
160 stdout.WriteString(text)
161 showcursor()
162 stdout.Flush()
163 loop:
164 for {
165 c := readchar()
166 switch c {
167 case '\n':
168 break loop
169 case 127:
170 if len(text) > 0 {
171 moveleft()
172 stdout.WriteString(" ")
173 moveleft()
174 text = text[:len(text)-1]
175 }
176 default:
177 text = text + string(c)
178 stdout.WriteString(string(c))
179 }
180 stdout.Flush()
181 }
182 m.text = text
183 updateconfig(m.name, m.text)
184 hidecursor()
185 drawscreen()
186 }
187
188 init()
189 drawscreen()
190
191 for {
192 c := readchar()
193 switch c {
194 case 'q':
195 return
196 case 'j':
197 selectnext()
198 case 'k':
199 selectprev()
200 case '\n':
201 editsel()
202 default:
203
204 }
205 }
206}