Browse Source

Fixed input (handles wide unicode).

Steve Thielemann 2 năm trước cách đây
mục cha
commit
ad6ff719c8
7 tập tin đã thay đổi với 284 bổ sung272 xóa
  1. 10 2
      door/door.go
  2. 55 3
      door/extended_string.go
  3. 7 0
      door/go.mod
  4. 6 0
      door/go.sum
  5. 107 225
      door/input.go
  6. 89 42
      door/nomoresecrets.go
  7. 10 0
      door/utilities.go

+ 10 - 2
door/door.go

@@ -30,6 +30,8 @@ import (
 	"strconv"
 	"sync"
 	"time"
+
+	"golang.org/x/term"
 )
 
 const SavePos = "\x1b[s"              // Save Cursor Position
@@ -125,6 +127,7 @@ type Door struct {
 	LastCursor     []CursorPos // Store Cursor pos information
 	mcMutex        sync.Mutex  // Lock for LastMouse, LastCursor
 	wg             sync.WaitGroup
+	tio_default    *term.State // Terminal State to restore
 }
 
 func (d *Door) SafeWriterClose() {
@@ -238,8 +241,10 @@ func (d *Door) ReadDropfile(filename string) {
 	}
 
 	if d.Config.Comm_type == 0 {
-		d.READFD = 1
-		d.WRITEFD = 2
+		d.READFD = 0
+		d.WRITEFD = 1
+		// RAW MODE
+		d.tio_default, err = term.MakeRaw(d.READFD)
 	} else if d.Config.Comm_type == 2 {
 		d.READFD = d.Config.Comm_handle
 		d.WRITEFD = d.Config.Comm_handle
@@ -426,6 +431,9 @@ func (d *Door) Close() {
 	log.Println("wg.Wait()")
 	d.wg.Wait()
 	log.Println("Closed.")
+	if d.Config.Comm_type == 0 {
+		term.Restore(d.READFD, d.tio_default)
+	}
 }
 
 // Goto X, Y - Position the cursor using ANSI Escape Codes

+ 55 - 3
door/extended_string.go

@@ -33,12 +33,64 @@ func _() {
 	_ = x[F12-22]
 	_ = x[MOUSE-23]
 	_ = x[CURSOR-24]
-	_ = x[UNKNOWN-25]
+	_ = x[ALT_a-25]
+	_ = x[ALT_b-26]
+	_ = x[ALT_c-27]
+	_ = x[ALT_d-28]
+	_ = x[ALT_e-29]
+	_ = x[ALT_f-30]
+	_ = x[ALT_g-31]
+	_ = x[ALT_h-32]
+	_ = x[ALT_i-33]
+	_ = x[ALT_j-34]
+	_ = x[ALT_k-35]
+	_ = x[ALT_l-36]
+	_ = x[ALT_m-37]
+	_ = x[ALT_n-38]
+	_ = x[ALT_o-39]
+	_ = x[ALT_p-40]
+	_ = x[ALT_q-41]
+	_ = x[ALT_r-42]
+	_ = x[ALT_s-43]
+	_ = x[ALT_t-44]
+	_ = x[ALT_u-45]
+	_ = x[ALT_v-46]
+	_ = x[ALT_w-47]
+	_ = x[ALT_x-48]
+	_ = x[ALT_y-49]
+	_ = x[ALT_z-50]
+	_ = x[ALT_A-51]
+	_ = x[ALT_B-52]
+	_ = x[ALT_C-53]
+	_ = x[ALT_D-54]
+	_ = x[ALT_E-55]
+	_ = x[ALT_F-56]
+	_ = x[ALT_G-57]
+	_ = x[ALT_H-58]
+	_ = x[ALT_I-59]
+	_ = x[ALT_J-60]
+	_ = x[ALT_K-61]
+	_ = x[ALT_L-62]
+	_ = x[ALT_M-63]
+	_ = x[ALT_N-64]
+	_ = x[ALT_O-65]
+	_ = x[ALT_P-66]
+	_ = x[ALT_Q-67]
+	_ = x[ALT_R-68]
+	_ = x[ALT_S-69]
+	_ = x[ALT_T-70]
+	_ = x[ALT_U-71]
+	_ = x[ALT_V-72]
+	_ = x[ALT_W-73]
+	_ = x[ALT_X-74]
+	_ = x[ALT_Y-75]
+	_ = x[ALT_Z-76]
+	_ = x[UNKNOWN-77]
 }
 
-const _Extended_name = "NOPUP_ARROWDOWN_ARROWRIGHT_ARROWLEFT_ARROWHOMEENDPAGE_UPPAGE_DOWNINSERTDELETEF1F2F3F4F5F6F7F8F9F10F11F12MOUSECURSORUNKNOWN"
+const _Extended_name = "NOPUP_ARROWDOWN_ARROWRIGHT_ARROWLEFT_ARROWHOMEENDPAGE_UPPAGE_DOWNINSERTDELETEF1F2F3F4F5F6F7F8F9F10F11F12MOUSECURSORALT_aALT_bALT_cALT_dALT_eALT_fALT_gALT_hALT_iALT_jALT_kALT_lALT_mALT_nALT_oALT_pALT_qALT_rALT_sALT_tALT_uALT_vALT_wALT_xALT_yALT_zALT_AALT_BALT_CALT_DALT_EALT_FALT_GALT_HALT_IALT_JALT_KALT_LALT_MALT_NALT_OALT_PALT_QALT_RALT_SALT_TALT_UALT_VALT_WALT_XALT_YALT_ZUNKNOWN"
 
-var _Extended_index = [...]uint8{0, 3, 11, 21, 32, 42, 46, 49, 56, 65, 71, 77, 79, 81, 83, 85, 87, 89, 91, 93, 95, 98, 101, 104, 109, 115, 122}
+var _Extended_index = [...]uint16{0, 3, 11, 21, 32, 42, 46, 49, 56, 65, 71, 77, 79, 81, 83, 85, 87, 89, 91, 93, 95, 98, 101, 104, 109, 115, 120, 125, 130, 135, 140, 145, 150, 155, 160, 165, 170, 175, 180, 185, 190, 195, 200, 205, 210, 215, 220, 225, 230, 235, 240, 245, 250, 255, 260, 265, 270, 275, 280, 285, 290, 295, 300, 305, 310, 315, 320, 325, 330, 335, 340, 345, 350, 355, 360, 365, 370, 375, 382}
 
 func (i Extended) String() string {
 	if i < 0 || i >= Extended(len(_Extended_index)-1) {

+ 7 - 0
door/go.mod

@@ -1,3 +1,10 @@
 module red-green/door
 
 go 1.19
+
+require (
+	golang.org/x/term v0.2.0
+	golang.org/x/text v0.4.0
+)
+
+require golang.org/x/sys v0.2.0 // indirect

+ 6 - 0
door/go.sum

@@ -0,0 +1,6 @@
+golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
+golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/term v0.2.0 h1:z85xZCsEl7bi/KwbNADeBYoOP0++7W1ipu+aGnpwzRM=
+golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
+golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
+golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=

+ 107 - 225
door/input.go

@@ -37,6 +37,58 @@ const (
 	F12
 	MOUSE
 	CURSOR
+	ALT_a
+	ALT_b
+	ALT_c
+	ALT_d
+	ALT_e
+	ALT_f
+	ALT_g
+	ALT_h
+	ALT_i
+	ALT_j
+	ALT_k
+	ALT_l
+	ALT_m
+	ALT_n
+	ALT_o
+	ALT_p
+	ALT_q
+	ALT_r
+	ALT_s
+	ALT_t
+	ALT_u
+	ALT_v
+	ALT_w
+	ALT_x
+	ALT_y
+	ALT_z
+	ALT_A
+	ALT_B
+	ALT_C
+	ALT_D
+	ALT_E
+	ALT_F
+	ALT_G
+	ALT_H
+	ALT_I
+	ALT_J
+	ALT_K
+	ALT_L
+	ALT_M
+	ALT_N
+	ALT_O
+	ALT_P
+	ALT_Q
+	ALT_R
+	ALT_S
+	ALT_T
+	ALT_U
+	ALT_V
+	ALT_W
+	ALT_X
+	ALT_Y
+	ALT_Z
 	UNKNOWN
 )
 
@@ -44,6 +96,12 @@ const (
 
 const DEBUG_INPUT = true
 
+func extended_output(buffer []rune) string {
+	var output string = string(buffer)
+	output = strings.Replace(output, "\x1b", "^[", -1)
+	return output
+}
+
 var ErrInactivity error = fmt.Errorf("Inactivity")
 var ErrTimeout error = fmt.Errorf("Timeout")
 var ErrDisconnected error = fmt.Errorf("Disconnected")
@@ -243,8 +301,24 @@ func (d *Door) GetKey() (rune, Extended, error) {
 			return r, ex, nil
 		}
 
-		var extended []rune = make([]rune, 1)
-		extended[0] = r2
+		if unicode.IsLetter(r2) {
+			// ALT-KEY
+			if unicode.IsLower(r2) {
+				// Lower case
+				ex = Extended(int(ALT_a) + int(r2-'a'))
+				return r, ex, nil
+			} else {
+				// Upper case
+				ex = Extended(int(ALT_A) + int(r2-'A'))
+				return r, ex, nil
+			}
+		}
+
+		var extended []rune = make([]rune, 0, 10)
+		extended = append(extended, r2)
+		// extended[0] = r2
+
+		var IsMouse bool = false
 
 		r2, err2 = d.ReadRunePushback()
 		for err2 == nil {
@@ -257,16 +331,30 @@ func (d *Door) GetKey() (rune, Extended, error) {
 
 			ext, has := extendedKeys[string(extended)]
 			if has {
+				// Found it!  Return extended code.
 				return rune(0), ext, nil
 			}
 
-			if unicode.IsLetter(r2) {
+			// Mouse codes can also contain letters.
+			if !IsMouse && unicode.IsLetter(r2) {
 				// The end of the extended code
+				// Unless this is Mouse!
+				if string(extended) == "[M" {
+					IsMouse = true
+				} else {
+					break
+				}
+			}
+
+			if IsMouse && len(extended) == 5 {
 				break
 			}
+
 			r2, err2 = d.ReadRunePushback()
 		}
 
+		log.Printf("Extended Code: [%s]", extended_output(extended))
+
 		var exString string = string(extended)
 		if strings.HasPrefix(exString, "[M") && len(extended) == 5 {
 			// Mouse Extended - input zero based (I add +1 to X, Y, and button)
@@ -338,223 +426,6 @@ func (d *Door) GetKey() (rune, Extended, error) {
 
 // "[1":   XKEY_UNKNOWN, // Syncterm is lost, could be F1..F5?
 
-// Low level read key function.
-// This gets the raw keys from the client, it doesn't handle extended keys,
-// functions, arrows.
-// Return key, or -1 (Timeout/No key available), -2 hangup
-/*
-func (d *Door) getch() int {
-	select {
-	case res, ok := <-d.readerChannel:
-		if ok {
-			return int(res)
-		} else {
-			d.Disconnected = true
-			// atomic.StoreInt32(&d.Disconnected, 1)
-			return -2
-		}
-	case <-time.After(time.Duration(100) * time.Millisecond):
-		return -1
-	}
-}
-
-func (d *Door) getkey_or_pushback() int {
-	if !d.Pushback.Empty() {
-		return d.Pushback.Pop()
-	}
-
-	if false {
-		var key int = d.getch()
-		log.Printf("%d / %X\n", key, key)
-		return key
-	} else {
-		return d.getch()
-	}
-}
-
-// Return key received, or XKEY_* values.
-// -1 timeout/no key
-// -2 hangup
-// -3 out of time
-func (d *Door) GetKey() int {
-	var c, c2 int
-
-	if d.Disconnect() {
-		return -2
-	}
-
-	if d.TimeLeft() < 0 {
-		return -3
-	}
-	c = d.getkey_or_pushback()
-
-	if c < 0 {
-		return c
-	}
-
-	// We get 0x0d 0x00, or 0x0d 0x0a from syncterm.
-	if c == 0x0d {
-		c2 = d.getkey_or_pushback()
-		if c2 > 0 {
-			// wasn't an error
-			if c2 != 0x00 && c2 != 0x0a {
-				// wasn't 0x00 or 0x0a
-				d.Pushback.Push(c2)
-				// log.Printf("Push 0x0d trailer %d / %x\n", c2, c2)
-			}
-		}
-		return c
-	}
-
-	if c == 0 {
-		// possibly doorway mode
-		var tries int = 0
-		c2 = d.getkey_or_pushback()
-		for c2 < 0 {
-			if tries > 7 {
-				return c
-			}
-			c2 = d.getkey_or_pushback()
-			tries++
-		}
-
-		switch c2 {
-		case 0x50:
-			return DOWN_ARROW
-		case 0x48:
-			return UP_ARROW
-		case 0x4b:
-			return LEFT_ARROW
-		case 0x4d:
-			return RIGHT_ARROW
-		case 0x47:
-			return HOME
-		case 0x4f:
-			return END
-		case 0x49:
-			return PAGE_UP
-		case 0x51:
-			return PAGE_DOWN
-		case 0x3b:
-			return F1
-		case 0x3c:
-			return F2
-		case 0x3d:
-			return F3
-		case 0x3e:
-			return F4
-		case 0x3f:
-			return F5
-		case 0x40:
-			return F6
-		case 0x41:
-			return F7
-		case 0x42:
-			return F8
-		case 0x43:
-			return F9
-		case 0x44:
-			return F10
-		case 0x52:
-			return INSERT
-		case 0x53:
-			return DELETE
-		default:
-			log.Printf("ERROR Doorway mode: 0x00 %x\n", c2)
-			return XKEY_UNKNOWN
-		}
-	}
-
-	if c == 0x1b {
-		// Escape key?
-		c2 = d.getkey_or_pushback()
-		if c2 < 0 {
-			// Just escape key
-			return c
-		}
-		var extended []byte = make([]byte, 1)
-		extended[0] = byte(c2) // string = string(byte(c2))
-
-		c2 = d.getkey_or_pushback()
-		for c2 > 0 {
-			if c2 == 0x1b {
-				d.Pushback.Push(c2)
-				break
-			}
-			extended = append(extended, byte(c2)) // += string(byte(c2))
-
-			var has bool
-			c2, has = extendedKeys[string(extended)]
-			if has {
-				// break out here if \x1b[ + letter or @
-				// break out if \x1b[ + digits + ~
-				// break out if \x1bO + letter
-				return c2
-			}
-			c2 = d.getkey_or_pushback()
-		}
-
-		if strings.HasPrefix(string(extended), "[M") && len(extended) == 5 {
-			// log.Printf("MOUSE Extended %#v\n", extended)
-			var mouse Mouse = Mouse{Button: int8(extended[2]) - ' ' + 1,
-				X: int8(extended[3]) - '!' + 1,
-				Y: int8(extended[4]) - '!' + 1}
-			d.AddMouse(mouse)
-			log.Printf("MOUSE %d (%d,%d)\n", mouse.Button, mouse.X, mouse.Y)
-			return XKEY_MOUSE
-		}
-
-		log.Printf("ERROR Extended %#v\n", extended)
-		return XKEY_UNKNOWN
-	}
-
-	return c
-}
-
-func (d *Door) Key() int {
-	return d.WaitKey(Inactivity, 0)
-}
-
-// usecs = Microseconds
-func (d *Door) WaitKey(secs int64, usecs int64) int {
-	if d.Disconnect() {
-		return -2
-	}
-
-	if !d.Pushback.Empty() {
-		return d.GetKey()
-	}
-
-	var timeout time.Duration = time.Duration(secs)*time.Second + time.Duration(usecs)*time.Microsecond
-
-	select {
-	case res, ok := <-d.readerChannel:
-		if ok {
-			d.Pushback.Push(int(res))
-			return d.GetKey()
-		} else {
-			// Reader Closed
-
-			d.Disconnected = true
-
-			// If I wrap this with if !d.WriterClosed .. races ?
-
-			// Why can't I do this?  This isn't a go routine...
-			if !d.WriterClosed {
-				d.writerChannel <- ""
-			}
-
-			// d.closeChannel <- struct{}{}
-
-			// atomic.StoreInt32(&d.Disconnected, 1)
-			return -2
-		}
-	case <-time.After(timeout):
-		return -1
-	}
-}
-*/
-
 // port over WaitKey
 func (d *Door) WaitKey(timeout time.Duration) (rune, Extended, error) {
 	// disconnected test
@@ -612,7 +483,8 @@ func DisplayInput(max int) string {
 // This displays the input area if a bg color was set.
 // This handles timeout, input, backspace, and enter.
 func (d *Door) Input(max int) string {
-	var line string
+	var line []rune = make([]rune, 0, max)
+	var length int
 
 	// draw input area
 	d.Write(DisplayInput(max))
@@ -632,10 +504,13 @@ func (d *Door) Input(max int) string {
 			continue
 		}
 
+		uw := UnicodeWidth(r)
+
 		if strconv.IsPrint(r) {
-			if len(line) < max {
+			if length+uw <= max {
 				d.Write(string(r))
-				line += string(r)
+				line = append(line, r)
+				length += uw
 			} else {
 				d.Write("\x07")
 			}
@@ -645,10 +520,17 @@ func (d *Door) Input(max int) string {
 			case 0x7f, 0x08:
 				if len(line) > 0 {
 					d.Write("\x08 \x08")
-					line = line[:len(line)-1]
+					rlen := len(line)
+					if UnicodeWidth(line[rlen-1]) == 2 {
+						d.Write("\x08 \x08")
+						length -= 2
+					} else {
+						length--
+					}
+					line = line[0 : rlen-1]
 				}
 			case 0x0d:
-				return line
+				return string(line)
 			}
 		}
 	}

+ 89 - 42
door/nomoresecrets.go

@@ -10,8 +10,8 @@ import (
 /*
 No More Secrets - from "Sneakers"
 https://github.com/bartobri/no-more-secrets
-
 */
+const NORANDOM bool = false
 
 type NoMoreSecretsConfig struct {
 	Jumble_Sec        int    // in sec
@@ -46,49 +46,66 @@ func getRandom() byte {
 	return rb
 }
 
+var rb byte
+
+func nonRandom() byte {
+	if rb == 0 {
+		rb = 0x21
+		return rb
+	}
+	rb++
+	if rb == 0x7f {
+		rb++
+	}
+	if rb >= 0xdf {
+		rb = 0x21
+	}
+	return rb
+}
+
 /*
 NoMoreSecrets - render output as random, then fade in the original text.
 
 Example:
 
-func About_Example_NoMoreSecrets(d *door.Door) {
-	W := 60
-	center_x := (door.Width - W) / 2
-	center_y := (door.Height - 16) / 2
-	about := 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())
+	func About_Example_NoMoreSecrets(d *door.Door) {
+		W := 60
+		center_x := (door.Width - W) / 2
+		center_y := (door.Height - 16) / 2
+		about := 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"
+		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)
-	}
+		if door.Unicode {
+			copyright = strings.Replace(copyright, "(C)", "\u00a9", -1)
+		}
 
-	about.Lines = append(about.Lines,
-		door.Line{Text: fmt.Sprintf("%*s", -W, copyright),
-			DefaultColor: door.ColorText("BOLD WHITE ON BLUE")})
-	for _, text := range []string{"",
-		"This door was written by Bugz.",
-		"",
-		"It is written in Go, understands CP437 and unicode, adapts",
-		"to screen sizes, uses door32.sys, supports TheDraw Fonts,",
-		"and runs on Linux and Windows."} {
-		about.Lines = append(about.Lines, door.Line{Text: fmt.Sprintf("%*s", -W, text)})
-	}
+		about.Lines = append(about.Lines,
+			door.Line{Text: fmt.Sprintf("%*s", -W, copyright),
+				DefaultColor: door.ColorText("BOLD WHITE ON BLUE")})
+		for _, text := range []string{"",
+			"This door was written by Bugz.",
+			"",
+			"It is written in Go, understands CP437 and unicode, adapts",
+			"to screen sizes, uses door32.sys, supports TheDraw Fonts,",
+			"and runs on Linux and Windows."} {
+			about.Lines = append(about.Lines, door.Line{Text: fmt.Sprintf("%*s", -W, text)})
+		}
 
-	better := door.NoMoreSecretsDefault
-	better.Color = door.ColorText("BOLD CYAN ON BLUE")
+		better := door.NoMoreSecretsDefault
+		better.Color = door.ColorText("BOLD CYAN ON BLUE")
 
-	door.NoMoreSecrets(about.Output(), d, &better)
-}
+		door.NoMoreSecrets(about.Output(), d, &better)
+	}
 */
 func NoMoreSecrets(output string, Door *Door, config *NoMoreSecretsConfig) {
 	// Find ANSI codes, strip color (We'll handle color)
@@ -225,7 +242,12 @@ func NoMoreSecrets(output string, Door *Door, config *NoMoreSecretsConfig) {
 				if work[pos] != ' ' {
 					// Safe way to handle bytes to unicode
 
-					var rb byte = getRandom()
+					var rb byte
+					if NORANDOM {
+						rb = nonRandom()
+					} else {
+						rb = getRandom()
+					}
 					var safe []byte = []byte{rb}
 					var rndchar string = CP437_to_Unicode(string(safe))
 					work[pos] = []rune(rndchar)[0]
@@ -245,14 +267,27 @@ func NoMoreSecrets(output string, Door *Door, config *NoMoreSecretsConfig) {
 					if chartime[idx] > 0 {
 						if chartime[idx] < 500 {
 							if rand.Intn(3) == 0 {
-								var safe []byte = []byte{getRandom()}
-								var rndchar string = CP437_to_Unicode(string(safe))
+								var safe [1]byte
+
+								if NORANDOM {
+									safe[0] = nonRandom()
+								} else {
+									safe[0] = getRandom()
+								}
+
+								var rndchar string = CP437_to_Unicode(string(safe[:]))
 								work[pos] = []rune(rndchar)[0]
 							}
 						} else {
 							if rand.Intn(10) == 0 {
-								var safe []byte = []byte{getRandom()}
-								var rndchar string = CP437_to_Unicode(string(safe))
+								var safe [1]byte
+								if NORANDOM {
+									safe[0] = nonRandom()
+								} else {
+									safe[0] = getRandom()
+								}
+
+								var rndchar string = CP437_to_Unicode(string(safe[:]))
 								work[pos] = []rune(rndchar)[0]
 							}
 						}
@@ -403,7 +438,11 @@ func NoMoreSecrets(output string, Door *Door, config *NoMoreSecretsConfig) {
 		for i := 0; i < (config.Jumble_Sec*1000)/config.Jumble_Loop_Speed; i++ {
 			for _, pos := range charpos {
 				if work[pos] != ' ' {
-					work[pos] = getRandom()
+					if NORANDOM {
+						work[pos] = nonRandom()
+					} else {
+						work[pos] = getRandom()
+					}
 				}
 			}
 			Door.Write(renderF())
@@ -420,11 +459,19 @@ func NoMoreSecrets(output string, Door *Door, config *NoMoreSecretsConfig) {
 					if chartime[idx] > 0 {
 						if chartime[idx] < 500 {
 							if rand.Intn(3) == 0 {
-								work[pos] = getRandom()
+								if NORANDOM {
+									work[pos] = nonRandom()
+								} else {
+									work[pos] = getRandom()
+								}
 							}
 						} else {
 							if rand.Intn(10) == 0 {
-								work[pos] = getRandom()
+								if NORANDOM {
+									work[pos] = nonRandom()
+								} else {
+									work[pos] = getRandom()
+								}
 							}
 						}
 

+ 10 - 0
door/utilities.go

@@ -3,8 +3,18 @@ package door
 import (
 	"strconv"
 	"strings"
+
+	"golang.org/x/text/width"
 )
 
+func UnicodeWidth(r rune) int {
+	p := width.LookupRune(r)
+	if p.Kind() == width.EastAsianWide {
+		return 2
+	}
+	return 1
+}
+
 func ArrayDelete[T any](stack *[]T, pos int) (T, bool) {
 	var result T
 	/*