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 "os/signal"
29)
30
31func adminscreen() {
32 log.SetOutput(ioutil.Discard)
33 stdout := bufio.NewWriter(os.Stdout)
34 esc := "\x1b"
35 smcup := esc + "[?1049h"
36 rmcup := esc + "[?1049l"
37
38 messages := []*struct {
39 name string
40 label string
41 text string
42 }{
43 {
44 name: "servermsg",
45 label: "server",
46 text: string(serverMsg),
47 },
48 {
49 name: "aboutmsg",
50 label: "about",
51 text: string(aboutMsg),
52 },
53 {
54 name: "loginmsg",
55 label: "login",
56 text: string(loginMsg),
57 },
58 }
59 cursel := 0
60
61 hidecursor := func() {
62 stdout.WriteString(esc + "[?25l")
63 }
64 showcursor := func() {
65 stdout.WriteString(esc + "[?12;25h")
66 }
67 movecursor := func(x, y int) {
68 stdout.WriteString(fmt.Sprintf(esc+"[%d;%dH", y, x))
69 }
70 moveleft := func() {
71 stdout.WriteString(esc + "[1D")
72 }
73 clearscreen := func() {
74 stdout.WriteString(esc + "[2J")
75 }
76 clearline := func() {
77 stdout.WriteString(esc + "[2K")
78 }
79 colorfn := func(code int) func(string) string {
80 return func(s string) string {
81 return fmt.Sprintf(esc+"[%dm"+"%s"+esc+"[0m", code, s)
82 }
83 }
84 reverse := colorfn(7)
85 magenta := colorfn(35)
86 readchar := func() byte {
87 var buf [1]byte
88 os.Stdin.Read(buf[:])
89 c := buf[0]
90 return c
91 }
92
93 savedtio := new(C.struct_termios)
94 C.tcgetattr(1, savedtio)
95 restore := func() {
96 stdout.WriteString(rmcup)
97 showcursor()
98 stdout.Flush()
99 C.tcsetattr(1, C.TCSAFLUSH, savedtio)
100 }
101 defer restore()
102 go func() {
103 sig := make(chan os.Signal)
104 signal.Notify(sig, os.Interrupt)
105 <-sig
106 restore()
107 os.Exit(0)
108 }()
109
110 init := func() {
111 tio := new(C.struct_termios)
112 C.tcgetattr(1, tio)
113 tio.c_lflag = tio.c_lflag & ^C.uint(C.ECHO|C.ICANON)
114 C.tcsetattr(1, C.TCSADRAIN, tio)
115
116 hidecursor()
117 stdout.WriteString(smcup)
118 clearscreen()
119 movecursor(1, 1)
120 stdout.Flush()
121 }
122
123 msglineno := func(idx int) int {
124 return 4 + idx*2
125 }
126
127 drawmessage := func(idx int) {
128 line := msglineno(idx)
129 movecursor(4, line)
130 label := messages[idx].label
131 if idx == cursel {
132 label = reverse(label)
133 }
134 stdout.WriteString(fmt.Sprintf("%s %s", label, messages[idx].text))
135 }
136
137 drawscreen := func() {
138 clearscreen()
139 movecursor(4, msglineno(-1))
140 stdout.WriteString(magenta(serverName + " admin panel"))
141 for i := range messages {
142 drawmessage(i)
143 }
144 movecursor(4, msglineno(len(messages)))
145 stdout.WriteString(magenta("j/k to move - q to quit - enter to edit"))
146 stdout.Flush()
147 }
148
149 selectnext := func() {
150 if cursel < len(messages)-1 {
151 cursel++
152 }
153 drawscreen()
154 }
155 selectprev := func() {
156 if cursel > 0 {
157 cursel--
158 }
159 drawscreen()
160 }
161 editsel := func() {
162 movecursor(4, msglineno(cursel))
163 clearline()
164 m := messages[cursel]
165 stdout.WriteString(reverse(magenta(m.label)))
166 text := m.text
167 stdout.WriteString(" ")
168 stdout.WriteString(text)
169 showcursor()
170 stdout.Flush()
171 loop:
172 for {
173 c := readchar()
174 switch c {
175 case '\n':
176 break loop
177 case 127:
178 if len(text) > 0 {
179 moveleft()
180 stdout.WriteString(" ")
181 moveleft()
182 text = text[:len(text)-1]
183 }
184 default:
185 text = text + string(c)
186 stdout.WriteString(string(c))
187 }
188 stdout.Flush()
189 }
190 m.text = text
191 updateconfig(m.name, m.text)
192 hidecursor()
193 drawscreen()
194 }
195
196 init()
197 drawscreen()
198
199 for {
200 c := readchar()
201 switch c {
202 case 'q':
203 return
204 case 'j':
205 selectnext()
206 case 'k':
207 selectprev()
208 case '\n':
209 editsel()
210 default:
211
212 }
213 }
214}