all repos — honk @ 9bddacc82837e639716398a48037dceea5e4e074

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	"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}