Procházet zdrojové kódy

Restructured testdoor.

Breaking up testdoor.go.
Bugz před 1 rokem
rodič
revize
651f4c0954
6 změnil soubory, kde provedl 866 přidání a 743 odebrání
  1. 73 0
      testdoor/about.go
  2. 232 0
      testdoor/display.go
  3. 276 0
      testdoor/fontdemo.go
  4. 98 0
      testdoor/mainmenu.go
  5. 184 0
      testdoor/panels.go
  6. 3 743
      testdoor/testdoor.go

+ 73 - 0
testdoor/about.go

@@ -0,0 +1,73 @@
+package main
+
+import (
+	"fmt"
+	"red-green/door"
+	"strings"
+
+	"github.com/mitchellh/go-wordwrap"
+)
+
+func about_test_door(d *door.Door) {
+	var W int = 58
+	// We're colliding with MemStats window.
+
+	// (80 - 60) / 2 = 10
+	var center_x int = (door.Width - W) / 2
+
+	// MaxX = 80 - 20 = 60
+
+	if MaxX < center_x+W {
+		// Problem.
+		center_x = (MaxX - W) / 2
+		if center_x < 0 {
+			//Problem #2
+			W = 48
+			center_x = (MaxX - W) / 2
+		}
+	}
+
+	var center_y int = (door.Height - 16) / 2
+	var about door.Panel = door.Panel{X: center_x,
+		Y:           center_y,
+		Width:       W,
+		Style:       door.SINGLE_DOUBLE,
+		BorderColor: door.ColorText("BOLD YELLOW ON BLUE"),
+	}
+	about.Lines = append(about.Lines, door.Line{Text: fmt.Sprintf("%*s", -W, "About This Door"),
+		DefaultColor: door.ColorText("BOLD CYAN ON BLUE")})
+	about.Lines = append(about.Lines, about.Spacer())
+
+	about.Lines = append(about.Lines, door.Line{Text: fmt.Sprintf("%*s", -W, "Test Door written in go, using go door.")})
+	var copyright string = "(C) 2022 Bugz, Red Green Software"
+
+	if door.Unicode {
+		copyright = strings.Replace(copyright, "(C)", "\u00a9", -1)
+	}
+
+	about.Lines = append(about.Lines,
+		door.Line{Text: copyright, Width: W,
+			DefaultColor: door.ColorText("BOLD WHITE ON BLUE")})
+	for _, text := range []string{"",
+		"This door was written by Bugz.",
+		""} {
+		about.Lines = append(about.Lines, door.Line{Text: text, Width: W})
+	}
+
+	var mainText string = "It is written in Go, detects CP437/unicode, detects screen " +
+		"size, supports door.sys & door32.sys, supports TheDraw Fonts " +
+		"(the fonts are compiled into the door), has NoMoreSecrets " +
+		"and SpinRite effects, and runs on Linux " +
+		"and sometimes even Windows..."
+	var wrapped string = wordwrap.WrapString(mainText, uint(W))
+	for _, text := range strings.Split(wrapped, "\n") {
+		about.Lines = append(about.Lines, door.Line{Text: text, Width: W})
+	}
+
+	var better door.NoMoreSecretsConfig = door.NoMoreSecretsDefault
+	better.Jumble_Loop_Speed = 75  // 35
+	better.Reveal_Loop_Speed = 100 // 50
+	better.Color = door.ColorText("BRI CYAN ON BLUE")
+
+	door.NoMoreSecrets(about.Output(), d, &better)
+}

+ 232 - 0
testdoor/display.go

@@ -0,0 +1,232 @@
+package main
+
+import (
+	"fmt"
+	"red-green/door"
+	"strconv"
+	"strings"
+	"time"
+)
+
+func display_information(d *door.Door) {
+	d.Write(door.Clrscr)
+
+	var headerColor string = door.ColorText("BRI CYAN")
+	var keyColor string = door.ColorText("BRI GREEN")
+	var sepColor string = door.ColorText("BRI YEL")
+	var valColor string = door.ColorText("CYAN")
+
+	var nice_format func(string, string) string = func(key string, value string) string {
+		return fmt.Sprintf("%s%-20s %s: %s%s", keyColor, key, sepColor, valColor, value) + door.CRNL
+	}
+
+	var offset string
+	var header string = "DropFile: "
+	offset = strings.Repeat(" ", len(header))
+
+	d.Write(headerColor + header)
+	d.Write(nice_format("Comm Type", strconv.Itoa(d.Config.Comm_type)))
+
+	if d.Config.BBSID != "" {
+		d.Write(offset + nice_format("BBS Software", d.Config.BBSID))
+	}
+	d.Write(offset + nice_format("Time Left", strconv.Itoa(d.Config.Time_left)))
+	d.Write(offset + nice_format("Real Name", d.Config.Real_name))
+	// d.Write(nice_format("Comm Handle", strconv.Itoa(d.Config.Comm_handle)))
+	d.Write(offset + nice_format("Handle", d.Config.Handle))
+	d.Write(offset + nice_format("User #", strconv.Itoa(d.Config.User_number)))
+	d.Write(offset + nice_format("Security Level", strconv.Itoa(d.Config.Security_level)))
+	d.Write(offset + nice_format("Node #", strconv.Itoa(d.Config.Node)))
+
+	header = "Detected: "
+	offset = strings.Repeat(" ", len(header))
+	d.Write(door.CRNL + headerColor + header)
+	d.Write(nice_format("Unicode", strconv.FormatBool(door.Unicode)))
+	d.Write(offset + nice_format("CP437", strconv.FormatBool(door.CP437)))
+	d.Write(offset + nice_format("Full CP437", strconv.FormatBool(door.Full_CP437)))
+	d.Write(offset + nice_format("Screen Size", fmt.Sprintf("%d X %d", door.Width, door.Height)))
+	var time time.Duration = d.TimeLeft()
+	d.Write(offset + nice_format("Door Time Left", fmt.Sprintf("%d Hours, %d Minutes, %d Seconds", int(time.Hours()), int(time.Minutes())%60, int(time.Seconds())%60)))
+	time = d.TimeUsed()
+	d.Write(offset + nice_format("Door Time Used", fmt.Sprintf("%d Minutes, %d Seconds", int(time.Minutes()), int(time.Seconds())%60)))
+	press_a_key(d)
+
+	d.Write(door.Clrscr + door.CRNL + door.CRNL + door.CRNL)
+	modules := GetModules()
+	header = "Build:    "
+	offset = strings.Repeat(" ", len(header))
+	d.Write(headerColor + header)
+	gover, gitver, goarch, goos := GetVersion()
+	d.Write(nice_format("go version", gover))
+	d.Write(offset + nice_format("git commit", gitver))
+	d.Write(offset + nice_format("Arch", goarch))
+	d.Write(offset + nice_format("OS", goos))
+	for mod, version := range modules {
+		d.Write(offset + nice_format(mod, version))
+	}
+}
+
+func display_ansi(d *door.Door) {
+	var art []string = ANSIGrowl()
+	d.Write(door.Clrscr)
+	for _, line := range art {
+		d.Write(line + door.CRNL)
+	}
+}
+
+func input_demo(d *door.Door) {
+	var ticker *time.Ticker = time.NewTicker(time.Second)
+	var StopIt = make(chan bool)
+
+	go func() {
+		for {
+			select {
+			case <-StopIt:
+				return
+
+			case t := <-ticker.C:
+				const tf = "January 2, 2006 03:04:05 PM MST"
+				output := door.SavePos + door.Goto(5, 2) + door.ColorText("BRI WHI ON CYAN") + t.Format(tf) + door.RestorePos
+				d.Write(output)
+			}
+		}
+	}()
+
+	var inputColor string = door.ColorText("BRI WHI ON BLUE")
+	var inputColor2 string = door.ColorText("BRI WHI ON GREEN")
+	var prompt door.Line = door.Line{Text: "What is YOUR Name: "}
+	prompt.RenderF = door.RenderBlueYellow
+	d.Write(prompt.Output() + inputColor)
+	var name string = d.Input(25)
+	d.Write(door.Reset + door.CRNL)
+	prompt.Text = "What is Your Quest: "
+	d.Write(prompt.Output() + inputColor2)
+	var quest string = d.Input(35)
+	d.Write(door.Reset + door.CRNL)
+	prompt.Text = "What is your Favorite CoLoR: "
+	d.Write(prompt.Output() + inputColor)
+	var color string = d.Input(15)
+	d.Write(door.Reset + door.CRNL)
+	ticker.Stop()
+	StopIt <- true
+	d.Write(fmt.Sprintf("You're %s on the %s quest, and fond of %s."+door.CRNL, name, quest, color))
+}
+
+func pctUpdate(pct *int64) func() int64 {
+	return func() int64 {
+		return *pct
+	}
+}
+
+func progress_bars(d *door.Door) {
+	d.Write(door.Clrscr)
+
+	var barHalf door.BarLine = door.BarLine{Line: door.Line{DefaultColor: door.ColorText("BOLD YELLOW")}, Width: 20, Style: door.HALF_STEP}
+	var barPercent door.BarLine = door.BarLine{Width: 30, Style: door.SOLID, PercentStyle: door.PERCENT_SPACE}
+
+	barPercent.ColorRange = []door.BarRange{
+		{Percent: 2500, Color: door.ColorText("RED")},
+		{Percent: 5000, Color: door.ColorText("BROWN")},
+		{Percent: 7500, Color: door.ColorText("BOLD YEL")},
+		{Percent: 9500, Color: door.ColorText("GREEN")},
+		{Percent: 10100, Color: door.ColorText("BRI GRE")}}
+
+	var barGradient door.BarLine = door.BarLine{Width: 15, Style: door.GRADIENT, Line: door.Line{DefaultColor: door.ColorText("CYAN")}}
+
+	var percentage int64
+	barHalf.UpdateP = pctUpdate(&percentage)
+	barPercent.UpdateP = pctUpdate(&percentage)
+	barGradient.UpdateP = pctUpdate(&percentage)
+
+	update_bars := func() {
+		barHalf.Update()
+		barPercent.Update()
+		barGradient.Update()
+	}
+
+	d.Write(door.Goto(3, 12) + "Half-Step")
+	d.Write(door.Goto(25, 12) + "% with space and Color Range")
+	d.Write(door.Goto(57, 12) + "Gradient")
+	d.Write(door.HideCursor)
+
+	bar_start := door.Goto(3, 15)
+
+	for f := 0; f <= 100; f++ {
+
+		d.Write(door.Goto(3, 10) + door.Reset + fmt.Sprintf("Value: %d", f))
+		percentage = int64(f * 100)
+
+		update_bars()
+		d.Write(bar_start + barHalf.Output() + "  " + door.Reset + barPercent.Output() + door.Reset + "  " + barGradient.Output())
+
+		if d.Disconnect() {
+			// don't continue to sleep if we're disconnected
+			break
+		}
+
+		time.Sleep(time.Millisecond * 100)
+	}
+	d.Write(door.ShowCursor)
+}
+
+func width_demo(d *door.Door) {
+	var w int = door.Width
+	var panel door.Panel = door.Panel{X: 1, Y: 1, Width: w}
+	var lineColor string = door.ColorText("WHI")
+	var line string
+
+	for y := 1; y <= door.Height; y++ {
+		if y%10 == 0 {
+			line = strings.Repeat("1234567890", w/10)
+			for x := len(line); x < w; x++ {
+				line += strconv.Itoa((x + 1) % 10)
+			}
+		} else {
+			line = ""
+			for x := 1; x < w; x++ {
+				if x%10 == 0 {
+					line += strconv.Itoa(y % 10)
+				} else {
+					line += " "
+				}
+			}
+		}
+		var l door.Line = door.Line{Text: line, DefaultColor: lineColor}
+		panel.Lines = append(panel.Lines, l)
+	}
+	var message string = fmt.Sprintf("Screen Size: %d X %d", door.Width, door.Height)
+	d.Write(panel.Output())
+
+	// Output alert on top of panel
+	var cx, cy int
+	cx = (door.Width - len(message) + 2) / 2
+	cy = (door.Height - 3) / 2
+	var alert []string = door.AlertBox(message, 1)
+	d.Write(door.ColorText("BRI YEL ON BLUE"))
+	for idx, ab := range alert {
+		d.Write(door.Goto(cx, cy+idx) + ab)
+	}
+	d.Write(door.Reset + panel.GotoEnd())
+
+	// Pause for key
+	d.WaitKey(door.Inactivity)
+
+	panel.Lines = make([]door.Line, 0)
+	var background string = "BUGZ Test Door in GO "
+	var bl int = len(background)
+	for y := 1; y <= door.Height; y++ {
+		offset := (y - 1) % bl
+		line = background[offset:]
+		for len(line) < w {
+			if len(line)+bl <= w {
+				line += background
+			} else {
+				line += background[0 : w-len(line)]
+			}
+		}
+		var l door.Line = door.Line{Text: line, RenderF: door.RenderBlueYellow}
+		panel.Lines = append(panel.Lines, l)
+	}
+	d.Write(panel.Output())
+	d.WaitKey(door.Inactivity)
+}

+ 276 - 0
testdoor/fontdemo.go

@@ -0,0 +1,276 @@
+package main
+
+import (
+	"fmt"
+	"red-green/door"
+	"strings"
+	"time"
+)
+
+//go:generate sh -c "font-util extract -f 'Amazon Cyan,Serpent,Unchained,Asylum,ArmageonRed,BrainDmgBlu,Boner,Descent,Remorse,Dungeon' ../TDFONTS.TDF ../TDFONTS2.TDF ../TDFONTS9.TDF > fonts.go"
+
+//go:generate sh -c "font-util extract -f Armageddon -c 7,1 -c 4,2 ../TDFONTS2.TDF > rgfont.go; sed -i 's/Armageddon/RedGreen/g' rgfont.go"
+
+func font_demo(d *door.Door) {
+	var output []string
+	var l int
+	var centering string
+	var center bool = false
+	var now time.Time = time.Now()
+
+	// I have checks here (but not all are complete), that verify the font
+	// output doesn't exceed the size of the screen.
+
+	d.Write(door.Clrscr + door.CRNL) //  + door.CRNL + door.CRNL)
+	var fac door.ColorFont = FontAmazonCyan()
+	output, l = fac.Output(now.Format("Jan Mon"))
+
+	// Size check:  Is this too big for the screen?
+	/*
+		if l > door.Width {
+			output, l = fac.Output("Jan")
+		}
+	*/
+	if l < door.Width {
+		if center {
+			centering = strings.Repeat(" ", (door.Width-l)/2)
+		} else {
+			centering = ""
+		}
+
+		for _, o := range output {
+			d.Write(fmt.Sprintf("%s%s%s", centering, o, door.Reset) + door.CRNL)
+		}
+		d.Write(door.CRNL)
+	}
+
+	var patch door.ColorMap = fac.Scan(6)
+	// log.Printf("Patch: %#v\n", patch)
+
+	fac.Modify(4, patch)
+
+	output, l = fac.Output(now.Format("Monday"))
+
+	if center {
+		centering = strings.Repeat(" ", (door.Width-l)/2)
+	} else {
+		centering = ""
+	}
+
+	for _, o := range output {
+		d.Write(fmt.Sprintf("%s%s%s", centering, o, door.Reset) + door.CRNL)
+	}
+	d.Write(door.CRNL)
+
+	fac.Modify(1, patch)
+	output, l = fac.Output(now.Format("January")) // 3:04:05 PM"))
+
+	if center {
+		centering = strings.Repeat(" ", (door.Width-l)/2)
+	} else {
+		centering = ""
+	}
+
+	for _, o := range output {
+		d.Write(fmt.Sprintf("%s%s%s", centering, o, door.Reset) + door.CRNL)
+	}
+	d.Write(door.CRNL)
+	press_a_key(d)
+
+	// Anarchy Blue - no digits
+	var fab door.ColorFont = FontSerpent()
+	output, l = fab.Output("Date/Time:")
+
+	if l < door.Width {
+		if center {
+			centering = strings.Repeat(" ", (door.Width-l)/2)
+		} else {
+			centering = ""
+		}
+
+		for _, o := range output {
+			d.Write(centering + o + door.Reset + door.CRNL)
+		}
+		d.Write(door.CRNL)
+	}
+
+	var unchain door.ColorFont = FontUnchained()
+	now = time.Now()
+	output, l = unchain.Output(now.Format("01/02/2006"))
+	if l < door.Width {
+		if center {
+			centering = strings.Repeat(" ", (door.Width-l)/2)
+		} else {
+			centering = ""
+		}
+
+		for _, o := range output {
+			d.Write(centering + o + door.Reset + door.CRNL)
+		}
+		d.Write(door.CRNL)
+	}
+
+	output, l = unchain.Output(now.Format("3:04:05 PM"))
+	if l > door.Width {
+		output, l = unchain.Output("Meow")
+	}
+
+	center = true
+	if l < door.Width {
+		if center {
+			centering = strings.Repeat(" ", (door.Width-l)/2)
+		} else {
+			centering = ""
+		}
+		for _, o := range output {
+			d.Write(centering + o + door.Reset + door.CRNL)
+		}
+		d.Write(door.CRNL)
+	}
+	press_a_key(d)
+
+	var asylum door.ColorFont = FontAsylum()
+	output, l = asylum.Output("Bugz ROCKS")
+	if l > door.Width {
+		output, l = asylum.Output("Aslym")
+	}
+
+	if l < door.Width {
+		if center {
+			centering = strings.Repeat(" ", (door.Width-l)/2)
+		} else {
+			centering = ""
+		}
+		for _, o := range output {
+			d.Write(centering + o + door.Reset + door.CRNL)
+		}
+		d.Write(door.CRNL)
+	}
+
+	var brain door.ColorFont = FontBrainDmgBlu()
+	output, l = brain.Output("I'm so BLUE")
+	if l > door.Width {
+		output, l = brain.Output("Blue")
+	}
+
+	if l < door.Width {
+		if center {
+			centering = strings.Repeat(" ", (door.Width-l)/2)
+		} else {
+			centering = ""
+		}
+		for _, o := range output {
+			d.Write(centering + o + door.Reset + door.CRNL)
+		}
+		d.Write(door.CRNL)
+	}
+
+	var boner door.ColorFont = FontBoner()
+	output, l = boner.Output("Welcome!")
+	if l < door.Width {
+		if center {
+			centering = strings.Repeat(" ", (door.Width-l)/2)
+		} else {
+			centering = ""
+		}
+		for _, o := range output {
+			d.Write(centering + o + door.Reset + door.CRNL)
+		}
+		d.Write(door.CRNL)
+	}
+	press_a_key(d)
+
+	var descent door.ColorFont = FontDescent()
+	output, l = descent.Output("Meanwhile...")
+	if l > door.Width {
+		output, l = descent.Output("BUGZ")
+	}
+
+	if l < door.Width {
+		if center {
+			centering = strings.Repeat(" ", (door.Width-l)/2)
+		} else {
+			centering = ""
+		}
+		for _, o := range output {
+			d.Write(centering + o + door.Reset + door.CRNL)
+		}
+		d.Write(door.CRNL)
+	}
+
+	var remorse door.ColorFont = FontRemorse()
+	output, l = remorse.Output("Enjoy the fonts")
+	if l > door.Width {
+		output, l = remorse.Output("Amazing")
+	}
+	if l < door.Width {
+		if center {
+			centering = strings.Repeat(" ", (door.Width-l)/2)
+		} else {
+			centering = ""
+		}
+
+		for _, o := range output {
+			d.Write(centering + o + door.Reset + door.CRNL)
+		}
+		d.Write(door.CRNL)
+	}
+
+	var dungeon door.ColorFont = FontDungeon()
+	output, l = dungeon.Output("Until NEXT time")
+	if l > door.Width {
+		output, l = dungeon.Output("Beware")
+	}
+	if l < door.Width {
+		if center {
+			centering = strings.Repeat(" ", (door.Width-l)/2)
+		} else {
+			centering = ""
+		}
+
+		for _, o := range output {
+			d.Write(centering + o + door.Reset + door.CRNL)
+		}
+		d.Write(door.CRNL)
+	}
+
+	/*
+		redgreen := FontArmageddon()
+		white := redgreen.Scan(7)
+		blue := redgreen.Scan(4)
+		redgreen.Modify(1, white)
+		redgreen.Modify(2, blue)
+	*/
+	redgreen := FontRedGreen()
+	output, l = redgreen.Output("Red-Green")
+	center = false
+
+	if l < door.Width {
+		press_a_key(d)
+
+		// Don't center this
+		if center {
+			centering = strings.Repeat(" ", (door.Width-l)/2)
+		} else {
+			centering = ""
+		}
+
+		for _, o := range output {
+			d.Write(centering + o + door.Reset + door.CRNL)
+		}
+		d.Write(door.CRNL)
+
+		// Center this part (below the MemStat panel)
+		output, l = redgreen.Output("Software")
+		center = true
+		if center {
+			centering = strings.Repeat(" ", (door.Width-l)/2)
+		} else {
+			centering = ""
+		}
+		for _, o := range output {
+			d.Write(centering + o + door.Reset + door.CRNL)
+		}
+		d.Write(door.CRNL)
+	}
+}

+ 98 - 0
testdoor/mainmenu.go

@@ -0,0 +1,98 @@
+package main
+
+import (
+	"red-green/door"
+	"strings"
+
+	"github.com/mitchellh/go-wordwrap"
+)
+
+// Build door.Menu (Main menu)
+func MainMenu() door.Menu {
+	// Make the main menu
+	// Width was 45
+	var menu door.Menu = door.Menu{Panel: door.Panel{Width: 35,
+		X:           2,
+		Y:           2,
+		Style:       door.DOUBLE,
+		Title:       "[ Main Menu: ]",
+		TitleOffset: 3,
+		BorderColor: door.ColorText("BRI CYAN ON BLA")}}
+	menu.SelectedR = door.MakeMenuRender(door.ColorText("BOLD CYAN"),
+		door.ColorText("BOLD BLUE"),
+		door.ColorText("BOLD WHITE"),
+		door.ColorText("BOLD CYAN"))
+	menu.UnselectedR = door.MakeMenuRender(door.ColorText("BOLD YEL ON BLUE"),
+		door.ColorText("BOLD WHI ON BLUE"),
+		door.ColorText("BOLD YEL ON BLUE"),
+		door.ColorText("BOLD CYAN ON BLUE"))
+
+	menu.AddSelection("A", "ANSI Display")
+	// m.AddSelection("C", "Crash")
+	menu.AddSelection("D", "Display Information")
+	menu.AddSelection("F", "Font Demo")
+	menu.AddSelection("I", "Input Demo")
+	menu.AddSelection("M", "Menu Demo")
+	menu.AddSelection("P", "Progress Bars Demo")
+	menu.AddSelection("S", "Show Panel")
+	menu.AddSelection("T", "Test Door About")
+	menu.AddSelection("W", "Screen Width")
+	menu.AddSelection("Q", "Quit")
+
+	var descriptions []string = []string{
+		// 12345678901234567890123456789012345678901234567890
+		"Display an ANSI file. It is compiled into the door itself.",
+		// "Crash go, see a handled error.",  // The error shows up in the logs.
+		"Display dropfile information.",
+		"Display TheDraw Fonts. Font information is compiled into the door.",
+		"Input some values, while updating the time.",
+		"Isn't this is a menu?",
+		"Display progress bar styles: half step, display percentage, and gradient.",
+		"Show multiple panels. Panels can be mouse drag/drop around.",
+		"Show about the door, using NoMoreSecrets effect.",
+		"Examples using the full width of the screen.",
+		"Exit this door.",
+	}
+
+	var widthLeft int = door.Width - (menu.Width + menu.X + 2)
+	var panelWidth int = widthLeft - (2 + 2)
+	var maxLines int = 1
+	var maxLineLength int
+	// Calculate the max lines needed for each line.
+	for _, line := range descriptions {
+		var wrapped string = wordwrap.WrapString(line, uint(panelWidth))
+		var lines int = len(strings.Split(wrapped, "\n"))
+		if lines > maxLines {
+			maxLines = lines
+		}
+		if len(line) > maxLineLength {
+			maxLineLength = len(line)
+		}
+	}
+
+	if maxLines == 1 {
+		// Ok! Everything fits into one line, SO use max line length as width of panel.
+		panelWidth = maxLineLength
+	}
+	// Position the description panel beside the menu. m.Width + m.X + 2 (1 one to give it a space)
+	// var descPanel door.Panel = door.Panel{X: menu.Width + menu.X + 2 + 1, Y: menu.Y, Width: panelWidth, Style: door.SINGLE, BorderColor: door.ColorText("WHI ON BLU")}
+
+	var descPanel door.Panel = door.Panel{X: 2, Y: menu.Y + len(menu.Lines) + 3, Width: panelWidth, Style: door.SINGLE, BorderColor: door.ColorText("WHI ON BLU")}
+	for x := 0; x < maxLines; x++ {
+		descPanel.Lines = append(descPanel.Lines, door.Line{Text: "", Width: panelWidth})
+	}
+
+	menu.Activated = func(item int, d *door.Door) {
+		var line string = descriptions[item]
+		var lines []string = strings.Split(wordwrap.WrapString(line, uint(panelWidth)), "\n")
+		for idx := range descPanel.Lines {
+			if idx >= len(lines) {
+				descPanel.Lines[idx].Text = ""
+			} else {
+				descPanel.Lines[idx].Text = lines[idx]
+			}
+		}
+		d.Write(door.SavePos + descPanel.Output() + door.RestorePos)
+	}
+	return menu
+}

+ 184 - 0
testdoor/panels.go

@@ -0,0 +1,184 @@
+package main
+
+import (
+	"log"
+	"red-green/door"
+	"strings"
+)
+
+type TrackPanels struct {
+	Panel  *door.Panel
+	XPos   int
+	YPos   int
+	BColor string
+}
+
+// Find the Panel that was mouse clicked.
+func FindPanel(m door.Mouse, panels []TrackPanels) int {
+	for idx, p := range panels {
+		hit, _, _ := p.Panel.Within(int(m.X), int(m.Y))
+		if hit {
+			return idx
+		}
+	}
+	return -1
+}
+
+func panel_demo(d *door.Door) {
+	var width int = 55
+	var panel door.Panel = door.Panel{X: 5, Y: 5, Width: width, Style: door.DOUBLE, BorderColor: door.ColorText("CYAN ON BLUE"), Title: "[ Panel Demo ]"}
+	var moveColor string = door.ColorText("CYAN ON BLACK")
+	var lineColor string = door.ColorText("BRIGHT WHI ON BLUE")
+	// Add lines to the panel
+	for _, line := range []string{"The BBS Door Panel Demo", "(C) 2021 Red Green Software, https://red-green.com"} {
+		if door.Unicode {
+			line = strings.Replace(line, "(C)", "\u00a9", -1)
+		}
+		var l door.Line = door.Line{Text: line, Width: width, DefaultColor: lineColor}
+		panel.Lines = append(panel.Lines, l)
+	}
+	panel.Lines = append(panel.Lines, panel.Spacer())
+	panel.Lines = append(panel.Lines, door.Line{Text: "Welcome to golang!", Width: width, DefaultColor: lineColor})
+
+	width = 10
+	var single door.Panel = door.Panel{X: 6, Y: 12, Width: width, Style: door.SINGLE, BorderColor: door.ColorText("WHITE ON BLUE"), Title: "< Single >"}
+	single.Lines = append(single.Lines, door.Line{Text: "Example", Width: width, DefaultColor: door.ColorText("WHI ON BLACK")})
+	single.Lines = append(single.Lines, single.Spacer())
+	single.Lines = append(single.Lines, door.Line{Text: "More Text", Width: width, DefaultColor: door.ColorText("BRI GREEN ON BLACK")})
+
+	width = 15
+	var double_single door.Panel = door.Panel{X: 26, Y: 12, Width: width, Style: door.DOUBLE_SINGLE, BorderColor: door.ColorText("BRI CYAN ON GREEN"), Title: "Double", TitleOffset: 3}
+	double_single.Lines = append(double_single.Lines, door.Line{Text: "Double / Single", Width: width, DefaultColor: door.ColorText("BRI WHI ON GREEN")})
+	double_single.Lines = append(double_single.Lines, double_single.Spacer())
+	double_single.Lines = append(double_single.Lines, door.Line{Text: "Some Other Text", Width: width, DefaultColor: door.ColorText("BRI CYAN ON GREEN")})
+
+	var single_double door.Panel = door.Panel{X: 46, Y: 12, Width: width, Style: door.SINGLE_DOUBLE, BorderColor: door.ColorText("BRI YELL ON RED")}
+	single_double.Lines = append(single_double.Lines, door.Line{Text: "Single / Double", Width: width, DefaultColor: door.ColorText("BRI WHI ON RED")})
+	single_double.Lines = append(single_double.Lines, single_double.Spacer())
+	single_double.Lines = append(single_double.Lines, door.Line{Text: "Text Goes Here ", Width: width, DefaultColor: door.ColorText("BRI GREEN ON RED")})
+
+	d.Write(door.Clrscr)
+	d.Write(panel.Output())
+	d.Write(single.Output())
+	d.Write(double_single.Output())
+	d.Write(single_double.Output())
+	d.Write(door.Goto(1, 20) + door.Reset + "Use MOUSE to click/drag panels, Right-Click Exits, R to Reset, Q to quit...")
+
+	var panels []TrackPanels = []TrackPanels{
+		{&panel, panel.X, panel.Y, panel.BorderColor},
+		{&single, single.X, single.Y, single.BorderColor},
+		{&double_single, double_single.X, double_single.Y, single.BorderColor},
+		{&single_double, single_double.X, single_double.Y, single_double.BorderColor}}
+
+	var movingPanel *door.Panel
+	var moveX, moveY int
+	var panelColor string
+
+	for {
+		key, ex, err := d.WaitKey(door.Inactivity)
+		if err != nil {
+			return
+		}
+		if ex == door.MOUSE {
+			m, ok := d.GetMouse()
+			if ok {
+				// Process Mouse Event
+				if m.Button == 3 {
+					// Exit on right click
+					return
+				}
+				if m.Button == 1 {
+					idx := FindPanel(m, panels)
+					if idx != -1 {
+						movingPanel = panels[idx].Panel
+						panelColor = movingPanel.BorderColor
+						moveX = int(m.X)
+						moveY = int(m.Y)
+					} else {
+						continue
+					}
+					// Should we do something to the panel?  Yes!
+					movingPanel.BorderColor = moveColor
+					d.Update(movingPanel.Output())
+				} else if m.Button == 4 {
+					if movingPanel != nil {
+						PR, PB := movingPanel.RightBottomPos()
+						log.Printf("Panel (%d,%d) End(%d,%d) W %d, L %d\n", movingPanel.X, movingPanel.Y, PR, PB, movingPanel.Width, movingPanel.Length())
+						// Ok, panel is move!
+						d.Update(movingPanel.Clear())
+
+						// Restore panel border
+						movingPanel.BorderColor = panelColor
+
+						// move panel
+						movingPanel.X -= (moveX - int(m.X))
+						movingPanel.Y -= (moveY - int(m.Y))
+
+						// sanity checks
+						if movingPanel.X < 1 {
+							movingPanel.X = 1
+						}
+
+						// var edgeX bool
+						if movingPanel.X+movingPanel.Width >= door.Width {
+							movingPanel.X = door.Width - movingPanel.Width - 1
+							// edgeX = true
+						}
+						if movingPanel.Y < 1 {
+							movingPanel.Y = 1
+						}
+
+						// var edgeY bool
+						if movingPanel.Y+movingPanel.Length() >= door.Height {
+							movingPanel.Y = door.Height - movingPanel.Length() - 1
+							// edgeY = true
+						}
+
+						PR, PB = movingPanel.RightBottomPos()
+						log.Printf("Panel Now (%d,%d) End (%d,%d) %d, %d\n", movingPanel.X, movingPanel.Y, PR, PB, movingPanel.Width, movingPanel.Length()) //, edgeX, edgeY)
+						// If panel is at the end of the screen -- it scrolls (syncterm)
+						// This "fixes" it.  (Maybe a better way would be to not use last
+						// line on the screen?)
+
+						// Or another way:
+						if PR == door.Width && PB == door.Height {
+							movingPanel.X--
+						}
+						/*
+							if edgeX && edgeY {
+								movingPanel.X--
+								log.Printf("Panel adjust X\n")
+							}
+						*/
+
+						d.Update(movingPanel.Output())
+					}
+					movingPanel = nil
+				}
+			}
+		} else {
+			if (key == 'Q') || (key == 'q') || (key == '\x1b') {
+				return
+			}
+
+			if (key == 'R') || (key == 'r') {
+				for _, p := range panels {
+					d.Update(p.Panel.Clear())
+				}
+				for _, p := range panels {
+					p.Panel.X = p.XPos
+					p.Panel.Y = p.YPos
+					p.Panel.BorderColor = p.BColor
+					d.Update(p.Panel.Output())
+				}
+			}
+			if key == '0' {
+				d.Write(panels[0].Panel.GotoEnd())
+			}
+			if key == '1' {
+				d.Write(panels[1].Panel.GotoEnd())
+			}
+
+		}
+	}
+}

+ 3 - 743
testdoor/testdoor.go

@@ -5,25 +5,13 @@ import (
 	"log"
 	"red-green/door"
 	"runtime/debug"
-	"strconv"
-	"strings"
 	"time"
-
 	// "net/http"
 	// _ "net/http/pprof"
-
-	"github.com/mitchellh/go-wordwrap"
 )
 
-//go:generate sh -c "font-util extract -f 'Amazon Cyan,Serpent,Unchained,Asylum,ArmageonRed,BrainDmgBlu,Boner,Descent,Remorse,Dungeon' ../TDFONTS.TDF ../TDFONTS2.TDF ../TDFONTS9.TDF > fonts.go"
-
-//go:generate sh -c "font-util extract -f Armageddon -c 7,1 -c 4,2 ../TDFONTS2.TDF > rgfont.go; sed -i 's/Armageddon/RedGreen/g' rgfont.go"
-
-func pctUpdate(pct *int64) func() int64 {
-	return func() int64 {
-		return *pct
-	}
-}
+// Max X value we can use before hitting MemStats panel.
+var MaxX int = 0
 
 func press_keys(d *door.Door) {
 	d.Write(door.Reset + door.CRNL + "Press some keys... <ENTER> to exit.")
@@ -72,735 +60,6 @@ func press_a_key(d *door.Door) error {
 	return err
 }
 
-func about_test_door(d *door.Door) {
-	var W int = 60
-	var center_x int = (door.Width - W) / 2
-	var center_y int = (door.Height - 16) / 2
-	var about door.Panel = door.Panel{X: center_x,
-		Y:           center_y,
-		Width:       W,
-		Style:       door.SINGLE_DOUBLE,
-		BorderColor: door.ColorText("BOLD YELLOW ON BLUE"),
-	}
-	about.Lines = append(about.Lines, door.Line{Text: fmt.Sprintf("%*s", -W, "About This Door"),
-		DefaultColor: door.ColorText("BOLD CYAN ON BLUE")})
-	about.Lines = append(about.Lines, about.Spacer())
-
-	about.Lines = append(about.Lines, door.Line{Text: fmt.Sprintf("%*s", -W, "Test Door written in go, using go door.")})
-	var copyright string = "(C) 2022 Bugz, Red Green Software"
-
-	if door.Unicode {
-		copyright = strings.Replace(copyright, "(C)", "\u00a9", -1)
-	}
-
-	about.Lines = append(about.Lines,
-		door.Line{Text: copyright, Width: W,
-			DefaultColor: door.ColorText("BOLD WHITE ON BLUE")})
-	for _, text := range []string{"",
-		"This door was written by Bugz.",
-		"",
-		"It is written in Go, detects CP437/unicode, detects screen",
-		"size, supports door.sys & door32.sys, supports TheDraw Fonts",
-		"(the fonts are compiled into the door), has NoMoreSecrets",
-		"and SpinRite effects, and runs on Linux",
-		"and sometimes even Windows..."} {
-		about.Lines = append(about.Lines, door.Line{Text: text, Width: W})
-	}
-
-	var better door.NoMoreSecretsConfig = door.NoMoreSecretsDefault
-	better.Jumble_Loop_Speed = 75  // 35
-	better.Reveal_Loop_Speed = 100 // 50
-	better.Color = door.ColorText("BRI CYAN ON BLUE")
-
-	door.NoMoreSecrets(about.Output(), d, &better)
-}
-
-// Build door.Menu (Main menu)
-func MainMenu() door.Menu {
-	// Make the main menu
-	// Width was 45
-	var menu door.Menu = door.Menu{Panel: door.Panel{Width: 35,
-		X:           2,
-		Y:           5,
-		Style:       door.DOUBLE,
-		Title:       "[ Main Menu: ]",
-		TitleOffset: 3,
-		BorderColor: door.ColorText("BRI CYAN ON BLA")}}
-	menu.SelectedR = door.MakeMenuRender(door.ColorText("BOLD CYAN"),
-		door.ColorText("BOLD BLUE"),
-		door.ColorText("BOLD WHITE"),
-		door.ColorText("BOLD CYAN"))
-	menu.UnselectedR = door.MakeMenuRender(door.ColorText("BOLD YEL ON BLUE"),
-		door.ColorText("BOLD WHI ON BLUE"),
-		door.ColorText("BOLD YEL ON BLUE"),
-		door.ColorText("BOLD CYAN ON BLUE"))
-
-	menu.AddSelection("A", "ANSI Display")
-	// m.AddSelection("C", "Crash")
-	menu.AddSelection("D", "Display Information")
-	menu.AddSelection("F", "Font Demo")
-	menu.AddSelection("I", "Input Demo")
-	menu.AddSelection("M", "Menu Demo")
-	menu.AddSelection("P", "Progress Bars Demo")
-	menu.AddSelection("S", "Show Panel")
-	menu.AddSelection("T", "Test Door About")
-	menu.AddSelection("W", "Screen Width")
-	menu.AddSelection("Q", "Quit")
-
-	var descriptions []string = []string{
-		// 12345678901234567890123456789012345678901234567890
-		"Display an ANSI file. It is compiled into the door itself.",
-		// "Crash go, see a handled error.",  // The error shows up in the logs.
-		"Display dropfile information.",
-		"Display TheDraw Fonts. Font information is compiled into the door.",
-		"Input some values, while updating the time.",
-		"Isn't this is a menu?",
-		"Display progress bar styles: half step, display percentage, and gradient.",
-		"Show multiple panels. Panels can be mouse drag/drop around.",
-		"Show about the door, using NoMoreSecrets effect.",
-		"Examples using the full width of the screen.",
-		"Exit this door.",
-	}
-
-	var widthLeft int = door.Width - (menu.Width + menu.X + 2)
-	var panelWidth int = widthLeft - (2 + 2)
-	var maxLines int = 1
-	var maxLineLength int
-	// Calculate the max lines needed for each line.
-	for _, line := range descriptions {
-		var wrapped string = wordwrap.WrapString(line, uint(panelWidth))
-		var lines int = len(strings.Split(wrapped, "\n"))
-		if lines > maxLines {
-			maxLines = lines
-		}
-		if len(line) > maxLineLength {
-			maxLineLength = len(line)
-		}
-	}
-
-	if maxLines == 1 {
-		// Ok! Everything fits into one line, SO use max line length as width of panel.
-		panelWidth = maxLineLength
-	}
-	// Position the description panel beside the menu. m.Width + m.X + 2 (1 one to give it a space)
-	var descPanel door.Panel = door.Panel{X: menu.Width + menu.X + 2 + 1, Y: menu.Y, Width: panelWidth, Style: door.SINGLE, BorderColor: door.ColorText("WHI ON BLU")}
-	for x := 0; x < maxLines; x++ {
-		descPanel.Lines = append(descPanel.Lines, door.Line{Text: "", Width: panelWidth})
-	}
-
-	menu.Activated = func(item int, d *door.Door) {
-		var line string = descriptions[item]
-		var lines []string = strings.Split(wordwrap.WrapString(line, uint(panelWidth)), "\n")
-		for idx := range descPanel.Lines {
-			if idx >= len(lines) {
-				descPanel.Lines[idx].Text = ""
-			} else {
-				descPanel.Lines[idx].Text = lines[idx]
-			}
-		}
-		d.Write(door.SavePos + descPanel.Output() + door.RestorePos)
-	}
-	return menu
-}
-
-func display_information(d *door.Door) {
-	d.Write(door.Clrscr)
-
-	var headerColor string = door.ColorText("BRI CYAN")
-	var keyColor string = door.ColorText("BRI GREEN")
-	var sepColor string = door.ColorText("BRI YEL")
-	var valColor string = door.ColorText("CYAN")
-
-	var nice_format func(string, string) string = func(key string, value string) string {
-		return fmt.Sprintf("%s%-20s %s: %s%s", keyColor, key, sepColor, valColor, value) + door.CRNL
-	}
-
-	var offset string
-	var header string = "DropFile: "
-	offset = strings.Repeat(" ", len(header))
-
-	d.Write(headerColor + header)
-	d.Write(nice_format("Comm Type", strconv.Itoa(d.Config.Comm_type)))
-
-	if d.Config.BBSID != "" {
-		d.Write(offset + nice_format("BBS Software", d.Config.BBSID))
-	}
-	d.Write(offset + nice_format("Time Left", strconv.Itoa(d.Config.Time_left)))
-	d.Write(offset + nice_format("Real Name", d.Config.Real_name))
-	// d.Write(nice_format("Comm Handle", strconv.Itoa(d.Config.Comm_handle)))
-	d.Write(offset + nice_format("Handle", d.Config.Handle))
-	d.Write(offset + nice_format("User #", strconv.Itoa(d.Config.User_number)))
-	d.Write(offset + nice_format("Security Level", strconv.Itoa(d.Config.Security_level)))
-	d.Write(offset + nice_format("Node #", strconv.Itoa(d.Config.Node)))
-
-	header = "Detected: "
-	offset = strings.Repeat(" ", len(header))
-	d.Write(door.CRNL + headerColor + header)
-	d.Write(nice_format("Unicode", strconv.FormatBool(door.Unicode)))
-	d.Write(offset + nice_format("CP437", strconv.FormatBool(door.CP437)))
-	d.Write(offset + nice_format("Full CP437", strconv.FormatBool(door.Full_CP437)))
-	d.Write(offset + nice_format("Screen Size", fmt.Sprintf("%d X %d", door.Width, door.Height)))
-	var time time.Duration = d.TimeLeft()
-	d.Write(offset + nice_format("Door Time Left", fmt.Sprintf("%d Hours, %d Minutes, %d Seconds", int(time.Hours()), int(time.Minutes())%60, int(time.Seconds())%60)))
-	time = d.TimeUsed()
-	d.Write(offset + nice_format("Door Time Used", fmt.Sprintf("%d Minutes, %d Seconds", int(time.Minutes()), int(time.Seconds())%60)))
-	press_a_key(d)
-
-	d.Write(door.Clrscr + door.CRNL + door.CRNL + door.CRNL)
-	modules := GetModules()
-	header = "Build:    "
-	offset = strings.Repeat(" ", len(header))
-	d.Write(headerColor + header)
-	gover, gitver, goarch, goos := GetVersion()
-	d.Write(nice_format("go version", gover))
-	d.Write(offset + nice_format("git commit", gitver))
-	d.Write(offset + nice_format("Arch", goarch))
-	d.Write(offset + nice_format("OS", goos))
-	for mod, version := range modules {
-		d.Write(offset + nice_format(mod, version))
-	}
-}
-
-func display_ansi(d *door.Door) {
-	var art []string = ANSIGrowl()
-	d.Write(door.Clrscr)
-	for _, line := range art {
-		d.Write(line + door.CRNL)
-	}
-}
-
-func font_demo(d *door.Door) {
-	var output []string
-	var l int
-	var centering string
-	var now time.Time = time.Now()
-
-	// I have checks here (but not all are complete), that verify the font
-	// output doesn't exceed the size of the screen.
-
-	d.Write(door.Clrscr + door.CRNL) //  + door.CRNL + door.CRNL)
-	var fac door.ColorFont = FontAmazonCyan()
-	output, l = fac.Output(now.Format("Jan Mon"))
-
-	// Size check:  Is this too big for the screen?
-	/*
-		if l > door.Width {
-			output, l = fac.Output("Jan")
-		}
-	*/
-	if l < door.Width {
-		centering = ""
-		// centering = strings.Repeat(" ", (door.Width-l)/2)
-
-		for _, o := range output {
-			d.Write(fmt.Sprintf("%s%s%s", centering, o, door.Reset) + door.CRNL)
-		}
-		d.Write(door.CRNL)
-	}
-
-	var patch door.ColorMap = fac.Scan(6)
-	// log.Printf("Patch: %#v\n", patch)
-
-	fac.Modify(4, patch)
-
-	output, l = fac.Output(now.Format("Monday"))
-	centering = strings.Repeat(" ", (door.Width-l)/2)
-	for _, o := range output {
-		d.Write(fmt.Sprintf("%s%s%s", centering, o, door.Reset) + door.CRNL)
-	}
-	d.Write(door.CRNL)
-
-	fac.Modify(1, patch)
-	output, l = fac.Output(now.Format("January")) // 3:04:05 PM"))
-	centering = strings.Repeat(" ", (door.Width-l)/2)
-	for _, o := range output {
-		d.Write(fmt.Sprintf("%s%s%s", centering, o, door.Reset) + door.CRNL)
-	}
-	d.Write(door.CRNL)
-	press_a_key(d)
-
-	// Anarchy Blue - no digits
-	var fab door.ColorFont = FontSerpent()
-	output, l = fab.Output("Date/Time:")
-
-	if l < door.Width {
-		centering = strings.Repeat(" ", (door.Width-l)/2)
-
-		for _, o := range output {
-			d.Write(centering + o + door.Reset + door.CRNL)
-		}
-		d.Write(door.CRNL)
-	}
-
-	var unchain door.ColorFont = FontUnchained()
-	now = time.Now()
-	output, l = unchain.Output(now.Format("01/02/2006"))
-	if l < door.Width {
-		centering = strings.Repeat(" ", (door.Width-l)/2)
-
-		for _, o := range output {
-			d.Write(centering + o + door.Reset + door.CRNL)
-		}
-		d.Write(door.CRNL)
-	}
-
-	output, l = unchain.Output(now.Format("3:04:05 PM"))
-	if l > door.Width {
-		output, l = unchain.Output("Meow")
-	}
-
-	if l < door.Width {
-		centering = strings.Repeat(" ", (door.Width-l)/2)
-
-		for _, o := range output {
-			d.Write(centering + o + door.Reset + door.CRNL)
-		}
-		d.Write(door.CRNL)
-	}
-	press_a_key(d)
-
-	var asylum door.ColorFont = FontAsylum()
-	output, l = asylum.Output("Bugz ROCKS")
-	if l > door.Width {
-		output, l = asylum.Output("Aslym")
-	}
-
-	if l < door.Width {
-		centering = strings.Repeat(" ", (door.Width-l)/2)
-
-		for _, o := range output {
-			d.Write(centering + o + door.Reset + door.CRNL)
-		}
-		d.Write(door.CRNL)
-	}
-
-	var brain door.ColorFont = FontBrainDmgBlu()
-	output, l = brain.Output("I'm so BLUE")
-	if l > door.Width {
-		output, l = brain.Output("Blue")
-	}
-
-	if l < door.Width {
-		centering = strings.Repeat(" ", (door.Width-l)/2)
-
-		for _, o := range output {
-			d.Write(centering + o + door.Reset + door.CRNL)
-		}
-		d.Write(door.CRNL)
-	}
-
-	var boner door.ColorFont = FontBoner()
-	output, l = boner.Output("Welcome!")
-	if l < door.Width {
-		centering = strings.Repeat(" ", (door.Width-l)/2)
-
-		for _, o := range output {
-			d.Write(centering + o + door.Reset + door.CRNL)
-		}
-		d.Write(door.CRNL)
-	}
-	press_a_key(d)
-
-	var descent door.ColorFont = FontDescent()
-	output, l = descent.Output("Meanwhile...")
-	if l > door.Width {
-		output, l = descent.Output("BUGZ")
-	}
-
-	if l < door.Width {
-		centering = strings.Repeat(" ", (door.Width-l)/2)
-
-		for _, o := range output {
-			d.Write(centering + o + door.Reset + door.CRNL)
-		}
-		d.Write(door.CRNL)
-	}
-
-	var remorse door.ColorFont = FontRemorse()
-	output, l = remorse.Output("Enjoy the fonts")
-	if l > door.Width {
-		output, l = remorse.Output("Amazing")
-	}
-	if l < door.Width {
-		centering = strings.Repeat(" ", (door.Width-l)/2)
-
-		for _, o := range output {
-			d.Write(centering + o + door.Reset + door.CRNL)
-		}
-		d.Write(door.CRNL)
-	}
-
-	var dungeon door.ColorFont = FontDungeon()
-	output, l = dungeon.Output("Until NEXT time")
-	if l > door.Width {
-		output, l = dungeon.Output("Beware")
-	}
-	if l < door.Width {
-		centering = strings.Repeat(" ", (door.Width-l)/2)
-
-		for _, o := range output {
-			d.Write(centering + o + door.Reset + door.CRNL)
-		}
-		d.Write(door.CRNL)
-	}
-
-	/*
-		redgreen := FontArmageddon()
-		white := redgreen.Scan(7)
-		blue := redgreen.Scan(4)
-		redgreen.Modify(1, white)
-		redgreen.Modify(2, blue)
-	*/
-	redgreen := FontRedGreen()
-	output, l = redgreen.Output("Red-Green")
-
-	if l < door.Width {
-		press_a_key(d)
-		centering = strings.Repeat(" ", (door.Width-l)/2)
-
-		for _, o := range output {
-			d.Write(centering + o + door.Reset + door.CRNL)
-		}
-		d.Write(door.CRNL)
-		output, l = redgreen.Output("Software")
-		centering = strings.Repeat(" ", (door.Width-l)/2)
-
-		for _, o := range output {
-			d.Write(centering + o + door.Reset + door.CRNL)
-		}
-		d.Write(door.CRNL)
-	}
-
-}
-
-func input_demo(d *door.Door) {
-	var ticker *time.Ticker = time.NewTicker(time.Second)
-	var StopIt = make(chan bool)
-
-	go func() {
-		for {
-			select {
-			case <-StopIt:
-				return
-
-			case t := <-ticker.C:
-				const tf = "January 2, 2006 03:04:05 PM MST"
-				output := door.SavePos + door.Goto(5, 2) + door.ColorText("BRI WHI ON CYAN") + t.Format(tf) + door.RestorePos
-				d.Write(output)
-			}
-		}
-	}()
-
-	var inputColor string = door.ColorText("BRI WHI ON BLUE")
-	var inputColor2 string = door.ColorText("BRI WHI ON GREEN")
-	var prompt door.Line = door.Line{Text: "What is YOUR Name: "}
-	prompt.RenderF = door.RenderBlueYellow
-	d.Write(prompt.Output() + inputColor)
-	var name string = d.Input(25)
-	d.Write(door.Reset + door.CRNL)
-	prompt.Text = "What is Your Quest: "
-	d.Write(prompt.Output() + inputColor2)
-	var quest string = d.Input(35)
-	d.Write(door.Reset + door.CRNL)
-	prompt.Text = "What is your Favorite CoLoR: "
-	d.Write(prompt.Output() + inputColor)
-	var color string = d.Input(15)
-	d.Write(door.Reset + door.CRNL)
-	ticker.Stop()
-	StopIt <- true
-	d.Write(fmt.Sprintf("You're %s on the %s quest, and fond of %s."+door.CRNL, name, quest, color))
-}
-
-func progress_bars(d *door.Door) {
-	d.Write(door.Clrscr)
-
-	var bar door.BarLine = door.BarLine{Line: door.Line{DefaultColor: door.ColorText("BOLD YELLOW")}, Width: 20, Style: door.HALF_STEP}
-	var bar2 door.BarLine = door.BarLine{Width: 30, Style: door.SOLID, PercentStyle: door.PERCENT_SPACE}
-
-	bar2.ColorRange = []door.BarRange{
-		{Percent: 2500, Color: door.ColorText("RED")},
-		{Percent: 5000, Color: door.ColorText("BROWN")},
-		{Percent: 7500, Color: door.ColorText("BOLD YEL")},
-		{Percent: 9500, Color: door.ColorText("GREEN")},
-		{Percent: 10100, Color: door.ColorText("BRI GRE")}}
-
-	var bar3 door.BarLine = door.BarLine{Width: 15, Style: door.GRADIENT, Line: door.Line{DefaultColor: door.ColorText("CYAN")}}
-
-	var percentage int64
-	bar.UpdateP = pctUpdate(&percentage)
-	bar2.UpdateP = pctUpdate(&percentage)
-	bar3.UpdateP = pctUpdate(&percentage)
-
-	update_bars := func() {
-		bar.Update()
-		bar2.Update()
-		bar3.Update()
-	}
-
-	d.Write(door.Goto(3, 12) + "Half-Step")
-	d.Write(door.Goto(25, 12) + "% with space and Color Range")
-	d.Write(door.Goto(57, 12) + "Gradient")
-	d.Write(door.HideCursor)
-
-	bar_start := door.Goto(3, 15)
-
-	for f := 0; f <= 100; f++ {
-
-		d.Write(door.Goto(3, 10) + door.Reset + fmt.Sprintf("Value: %d", f))
-		percentage = int64(f * 100)
-
-		update_bars()
-		d.Write(bar_start + bar.Output() + "  " + door.Reset + bar2.Output() + door.Reset + "  " + bar3.Output())
-
-		if d.Disconnect() {
-			// don't continue to sleep if we're disconnected
-			break
-		}
-
-		time.Sleep(time.Millisecond * 100)
-	}
-	d.Write(door.ShowCursor)
-}
-
-func width_demo(d *door.Door) {
-	var w int = door.Width
-	var panel door.Panel = door.Panel{X: 1, Y: 1, Width: w}
-	var lineColor string = door.ColorText("WHI")
-	var line string
-
-	for y := 1; y <= door.Height; y++ {
-		if y%10 == 0 {
-			line = strings.Repeat("1234567890", w/10)
-			for x := len(line); x < w; x++ {
-				line += strconv.Itoa((x + 1) % 10)
-			}
-		} else {
-			line = ""
-			for x := 1; x < w; x++ {
-				if x%10 == 0 {
-					line += strconv.Itoa(y % 10)
-				} else {
-					line += " "
-				}
-			}
-		}
-		var l door.Line = door.Line{Text: line, DefaultColor: lineColor}
-		panel.Lines = append(panel.Lines, l)
-	}
-	var message string = fmt.Sprintf("Screen Size: %d X %d", door.Width, door.Height)
-	d.Write(panel.Output())
-
-	// Output alert on top of panel
-	var cx, cy int
-	cx = (door.Width - len(message) + 2) / 2
-	cy = (door.Height - 3) / 2
-	var alert []string = door.AlertBox(message, 1)
-	d.Write(door.ColorText("BRI YEL ON BLUE"))
-	for idx, ab := range alert {
-		d.Write(door.Goto(cx, cy+idx) + ab)
-	}
-	d.Write(door.Reset + panel.GotoEnd())
-
-	// Pause for key
-	d.WaitKey(door.Inactivity)
-
-	panel.Lines = make([]door.Line, 0)
-	var background string = "BUGZ Test Door in GO "
-	var bl int = len(background)
-	for y := 1; y <= door.Height; y++ {
-		offset := (y - 1) % bl
-		line = background[offset:]
-		for len(line) < w {
-			if len(line)+bl <= w {
-				line += background
-			} else {
-				line += background[0 : w-len(line)]
-			}
-		}
-		var l door.Line = door.Line{Text: line, RenderF: door.RenderBlueYellow}
-		panel.Lines = append(panel.Lines, l)
-	}
-	d.Write(panel.Output())
-	d.WaitKey(door.Inactivity)
-}
-
-type TrackPanels struct {
-	Panel  *door.Panel
-	XPos   int
-	YPos   int
-	BColor string
-}
-
-// Find the Panel that was mouse clicked.
-func FindPanel(m door.Mouse, panels []TrackPanels) int {
-	for idx, p := range panels {
-		hit, _, _ := p.Panel.Within(int(m.X), int(m.Y))
-		if hit {
-			return idx
-		}
-	}
-	return -1
-}
-
-func panel_demo(d *door.Door) {
-	var width int = 55
-	var panel door.Panel = door.Panel{X: 5, Y: 5, Width: width, Style: door.DOUBLE, BorderColor: door.ColorText("CYAN ON BLUE"), Title: "[ Panel Demo ]"}
-	var moveColor string = door.ColorText("CYAN ON BLACK")
-	var lineColor string = door.ColorText("BRIGHT WHI ON BLUE")
-	// Add lines to the panel
-	for _, line := range []string{"The BBS Door Panel Demo", "(C) 2021 Red Green Software, https://red-green.com"} {
-		if door.Unicode {
-			line = strings.Replace(line, "(C)", "\u00a9", -1)
-		}
-		var l door.Line = door.Line{Text: line, Width: width, DefaultColor: lineColor}
-		panel.Lines = append(panel.Lines, l)
-	}
-	panel.Lines = append(panel.Lines, panel.Spacer())
-	panel.Lines = append(panel.Lines, door.Line{Text: "Welcome to golang!", Width: width, DefaultColor: lineColor})
-
-	width = 10
-	var single door.Panel = door.Panel{X: 6, Y: 12, Width: width, Style: door.SINGLE, BorderColor: door.ColorText("WHITE ON BLUE"), Title: "< Single >"}
-	single.Lines = append(single.Lines, door.Line{Text: "Example", Width: width, DefaultColor: door.ColorText("WHI ON BLACK")})
-	single.Lines = append(single.Lines, single.Spacer())
-	single.Lines = append(single.Lines, door.Line{Text: "More Text", Width: width, DefaultColor: door.ColorText("BRI GREEN ON BLACK")})
-
-	width = 15
-	var double_single door.Panel = door.Panel{X: 26, Y: 12, Width: width, Style: door.DOUBLE_SINGLE, BorderColor: door.ColorText("BRI CYAN ON GREEN"), Title: "Double", TitleOffset: 3}
-	double_single.Lines = append(double_single.Lines, door.Line{Text: "Double / Single", Width: width, DefaultColor: door.ColorText("BRI WHI ON GREEN")})
-	double_single.Lines = append(double_single.Lines, double_single.Spacer())
-	double_single.Lines = append(double_single.Lines, door.Line{Text: "Some Other Text", Width: width, DefaultColor: door.ColorText("BRI CYAN ON GREEN")})
-
-	var single_double door.Panel = door.Panel{X: 46, Y: 12, Width: width, Style: door.SINGLE_DOUBLE, BorderColor: door.ColorText("BRI YELL ON RED")}
-	single_double.Lines = append(single_double.Lines, door.Line{Text: "Single / Double", Width: width, DefaultColor: door.ColorText("BRI WHI ON RED")})
-	single_double.Lines = append(single_double.Lines, single_double.Spacer())
-	single_double.Lines = append(single_double.Lines, door.Line{Text: "Text Goes Here ", Width: width, DefaultColor: door.ColorText("BRI GREEN ON RED")})
-
-	d.Write(door.Clrscr)
-	d.Write(panel.Output())
-	d.Write(single.Output())
-	d.Write(double_single.Output())
-	d.Write(single_double.Output())
-	d.Write(door.Goto(1, 20) + door.Reset + "Use MOUSE to click/drag panels, Right-Click Exits, R to Reset, Q to quit...")
-
-	var panels []TrackPanels = []TrackPanels{
-		{&panel, panel.X, panel.Y, panel.BorderColor},
-		{&single, single.X, single.Y, single.BorderColor},
-		{&double_single, double_single.X, double_single.Y, single.BorderColor},
-		{&single_double, single_double.X, single_double.Y, single_double.BorderColor}}
-
-	var movingPanel *door.Panel
-	var moveX, moveY int
-	var panelColor string
-
-	for {
-		key, ex, err := d.WaitKey(door.Inactivity)
-		if err != nil {
-			return
-		}
-		if ex == door.MOUSE {
-			m, ok := d.GetMouse()
-			if ok {
-				// Process Mouse Event
-				if m.Button == 3 {
-					// Exit on right click
-					return
-				}
-				if m.Button == 1 {
-					idx := FindPanel(m, panels)
-					if idx != -1 {
-						movingPanel = panels[idx].Panel
-						panelColor = movingPanel.BorderColor
-						moveX = int(m.X)
-						moveY = int(m.Y)
-					} else {
-						continue
-					}
-					// Should we do something to the panel?  Yes!
-					movingPanel.BorderColor = moveColor
-					d.Update(movingPanel.Output())
-				} else if m.Button == 4 {
-					if movingPanel != nil {
-						PR, PB := movingPanel.RightBottomPos()
-						log.Printf("Panel (%d,%d) End(%d,%d) W %d, L %d\n", movingPanel.X, movingPanel.Y, PR, PB, movingPanel.Width, movingPanel.Length())
-						// Ok, panel is move!
-						d.Update(movingPanel.Clear())
-
-						// Restore panel border
-						movingPanel.BorderColor = panelColor
-
-						// move panel
-						movingPanel.X -= (moveX - int(m.X))
-						movingPanel.Y -= (moveY - int(m.Y))
-
-						// sanity checks
-						if movingPanel.X < 1 {
-							movingPanel.X = 1
-						}
-
-						// var edgeX bool
-						if movingPanel.X+movingPanel.Width >= door.Width {
-							movingPanel.X = door.Width - movingPanel.Width - 1
-							// edgeX = true
-						}
-						if movingPanel.Y < 1 {
-							movingPanel.Y = 1
-						}
-
-						// var edgeY bool
-						if movingPanel.Y+movingPanel.Length() >= door.Height {
-							movingPanel.Y = door.Height - movingPanel.Length() - 1
-							// edgeY = true
-						}
-
-						PR, PB = movingPanel.RightBottomPos()
-						log.Printf("Panel Now (%d,%d) End (%d,%d) %d, %d\n", movingPanel.X, movingPanel.Y, PR, PB, movingPanel.Width, movingPanel.Length()) //, edgeX, edgeY)
-						// If panel is at the end of the screen -- it scrolls (syncterm)
-						// This "fixes" it.  (Maybe a better way would be to not use last
-						// line on the screen?)
-
-						// Or another way:
-						if PR == door.Width && PB == door.Height {
-							movingPanel.X--
-						}
-						/*
-							if edgeX && edgeY {
-								movingPanel.X--
-								log.Printf("Panel adjust X\n")
-							}
-						*/
-
-						d.Update(movingPanel.Output())
-					}
-					movingPanel = nil
-				}
-			}
-		} else {
-			if (key == 'Q') || (key == 'q') || (key == '\x1b') {
-				return
-			}
-
-			if (key == 'R') || (key == 'r') {
-				for _, p := range panels {
-					d.Update(p.Panel.Clear())
-				}
-				for _, p := range panels {
-					p.Panel.X = p.XPos
-					p.Panel.Y = p.YPos
-					p.Panel.BorderColor = p.BColor
-					d.Update(p.Panel.Output())
-				}
-			}
-			if key == '0' {
-				d.Write(panels[0].Panel.GotoEnd())
-			}
-			if key == '1' {
-				d.Write(panels[1].Panel.GotoEnd())
-			}
-
-		}
-	}
-}
-
 func main() {
 	var message string
 
@@ -845,6 +104,7 @@ func main() {
 	*/
 	// Add in the GoRoutine Status panel
 
+	MaxX = door.Width - 20
 	var goPanel door.Panel = door.Panel{X: door.Width - 19,
 		Y:           3,
 		Width:       16,