all repos — honk @ 46cb0e3df57390ded1bbf712fe30a38fa03be43e

my fork of honk

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}