Reset master to testing content
This commit is contained in:
1 parent
0f2587b719
commit
42afa2a56c
1483 files changed
+145308
-938069
No files matched your search
@@ -0,0 +1,299 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
"git.stormux.org/storm/cthulhu/internal/braille"
|
||||
"git.stormux.org/storm/cthulhu/internal/config"
|
||||
)
|
||||
|
||||
func (worker *brailleWorker) Enqueue(text string) {
|
||||
worker.EnqueueFrame(braille.Frame{Text: text, Cursor: -1})
|
||||
}
|
||||
|
||||
func (worker *brailleWorker) EnqueueMessage(text string) bool {
|
||||
if text == "" || len(text) > 10000 {
|
||||
return false
|
||||
}
|
||||
worker.mu.Lock()
|
||||
if !worker.connected {
|
||||
worker.mu.Unlock()
|
||||
return false
|
||||
}
|
||||
queued := worker.enqueueFrameLocked(braille.Frame{Text: text, Cursor: -1, WordWrap: worker.wordWrap}, true)
|
||||
worker.mu.Unlock()
|
||||
if queued {
|
||||
worker.wakeDisplay()
|
||||
}
|
||||
return queued
|
||||
}
|
||||
|
||||
func (worker *brailleWorker) EnqueueFrame(frame braille.Frame) {
|
||||
worker.enqueueFrame(frame, true)
|
||||
}
|
||||
|
||||
// SetInternalUIFrame temporarily owns presentation while normal focus frames
|
||||
// continue updating the base. Closing restores the newest application frame.
|
||||
func (worker *brailleWorker) SetInternalUIFrame(frame *braille.Frame) {
|
||||
worker.mu.Lock()
|
||||
if worker.flashTimer != nil {
|
||||
worker.flashTimer.Stop()
|
||||
worker.flashTimer = nil
|
||||
}
|
||||
worker.flashGeneration++
|
||||
worker.flashContext = nil
|
||||
worker.protectedFlash = false
|
||||
worker.internalUIFrame = nil
|
||||
next := worker.baseFrame
|
||||
if frame != nil {
|
||||
next = *frame
|
||||
next.WordWrap = worker.wordWrap
|
||||
if len(next.Text) > 10000 {
|
||||
next.Text = next.Text[:10000]
|
||||
for !utf8.ValidString(next.Text) {
|
||||
next.Text = next.Text[:len(next.Text)-1]
|
||||
}
|
||||
if next.Cursor >= utf8.RuneCountInString(next.Text) {
|
||||
next.Cursor = -1
|
||||
}
|
||||
}
|
||||
}
|
||||
queued := worker.enqueueFrameLocked(next, false)
|
||||
if frame != nil {
|
||||
copy := *frame
|
||||
worker.internalUIFrame = ©
|
||||
}
|
||||
worker.mu.Unlock()
|
||||
if queued {
|
||||
worker.wakeDisplay()
|
||||
}
|
||||
}
|
||||
|
||||
func (worker *brailleWorker) enqueueFrame(frame braille.Frame, base bool) {
|
||||
// JSON escaping can expand a frame; keep it below the helper's 64 KiB
|
||||
// scanner limit even when every byte needs escaping.
|
||||
if len(frame.Text) > 10000 {
|
||||
frame.Text = frame.Text[:10000]
|
||||
for !utf8.ValidString(frame.Text) {
|
||||
frame.Text = frame.Text[:len(frame.Text)-1]
|
||||
}
|
||||
if frame.Cursor > utf8.RuneCountInString(frame.Text) {
|
||||
frame.Cursor = -1
|
||||
}
|
||||
if len(frame.Dots) > utf8.RuneCountInString(frame.Text) {
|
||||
frame.Dots = frame.Dots[:utf8.RuneCountInString(frame.Text)]
|
||||
}
|
||||
}
|
||||
worker.mu.Lock()
|
||||
queued := worker.enqueueFrameLocked(frame, base)
|
||||
worker.mu.Unlock()
|
||||
if queued {
|
||||
worker.wakeDisplay()
|
||||
}
|
||||
}
|
||||
|
||||
// enqueueFrameLocked must run under mu so flash, focus and timer restoration
|
||||
// cannot reorder their frames.
|
||||
func (worker *brailleWorker) enqueueFrameLocked(frame braille.Frame, base bool) bool {
|
||||
if worker.secure {
|
||||
return false
|
||||
}
|
||||
return worker.enqueueSecureFrameLocked(frame, base)
|
||||
}
|
||||
|
||||
func (worker *brailleWorker) enqueueSecureFrameLocked(frame braille.Frame, base bool) bool {
|
||||
if worker.internalUIFrame != nil {
|
||||
if base {
|
||||
worker.baseFrame = frame
|
||||
}
|
||||
return false
|
||||
}
|
||||
if worker.cancel == nil || worker.stopping {
|
||||
return false
|
||||
}
|
||||
if base {
|
||||
worker.flashContext = nil
|
||||
if worker.protectedFlash {
|
||||
worker.baseFrame = frame
|
||||
return false
|
||||
}
|
||||
worker.flashGeneration++
|
||||
if worker.flashTimer != nil {
|
||||
worker.flashTimer.Stop()
|
||||
worker.flashTimer = nil
|
||||
}
|
||||
worker.baseFrame = frame
|
||||
}
|
||||
frame.Contract = worker.contracted
|
||||
frame.Table = worker.table
|
||||
frame.ExpandAtCursor = frame.Cursor >= 0 && len(frame.Routes) > 0
|
||||
worker.sequence++
|
||||
frame.ID = worker.sequence
|
||||
frame.Routes = append([]braille.TextRoute(nil), frame.Routes...)
|
||||
frame.Dots = append([]byte(nil), frame.Dots...)
|
||||
if worker.routeCancel != nil {
|
||||
worker.routeCancel()
|
||||
}
|
||||
worker.frame = frame
|
||||
return true
|
||||
}
|
||||
|
||||
func (worker *brailleWorker) wakeDisplay() {
|
||||
select {
|
||||
case worker.wake <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
func (worker *brailleWorker) Flash(text string, options config.BrailleOptions) bool {
|
||||
return worker.flash_message(text, options, false)
|
||||
}
|
||||
|
||||
// Confirmation flashes survive the automatic focus change caused by Apply/OK.
|
||||
// Normal flashes keep their existing focus-replacement behavior.
|
||||
func (worker *brailleWorker) FlashConfirmation(text string, options config.BrailleOptions) bool {
|
||||
return worker.flash_message(text, options, true)
|
||||
}
|
||||
|
||||
func (worker *brailleWorker) flash_message(text string, options config.BrailleOptions, confirmation bool) bool {
|
||||
return worker.flash_message_guarded(text, options, confirmation, nil, 0)
|
||||
}
|
||||
|
||||
func (worker *brailleWorker) flash_message_guarded(text string, options config.BrailleOptions, confirmation bool, owner context.Context, sequence uint64) bool {
|
||||
if text == "" || !options.FlashMessages {
|
||||
return false
|
||||
}
|
||||
if len(text) > 10000 {
|
||||
text = text[:10000]
|
||||
for !utf8.ValidString(text) {
|
||||
text = text[:len(text)-1]
|
||||
}
|
||||
}
|
||||
worker.mu.Lock()
|
||||
if worker.internalUIFrame != nil {
|
||||
worker.mu.Unlock()
|
||||
return false
|
||||
}
|
||||
if owner != nil && (owner.Err() != nil || worker.sequence != sequence) {
|
||||
worker.mu.Unlock()
|
||||
return false
|
||||
}
|
||||
if worker.cancel == nil || worker.stopping || !worker.connected || worker.secure {
|
||||
worker.mu.Unlock()
|
||||
return false
|
||||
}
|
||||
worker.flashGeneration++
|
||||
worker.flashContext = owner
|
||||
worker.protectedFlash = confirmation
|
||||
generation := worker.flashGeneration
|
||||
if worker.flashTimer != nil {
|
||||
worker.flashTimer.Stop()
|
||||
}
|
||||
queued := worker.enqueueFrameLocked(braille.Frame{Text: text, Cursor: -1, WordWrap: options.WordWrap}, false)
|
||||
if !options.FlashPersistent && options.FlashTime > 0 {
|
||||
worker.flashTimer = time.AfterFunc(time.Duration(options.FlashTime)*time.Millisecond, func() {
|
||||
worker.mu.Lock()
|
||||
if worker.flashGeneration != generation {
|
||||
worker.mu.Unlock()
|
||||
return
|
||||
}
|
||||
worker.flashTimer = nil
|
||||
worker.flashContext = nil
|
||||
worker.protectedFlash = false
|
||||
queued := worker.enqueueFrameLocked(worker.baseFrame, false)
|
||||
worker.mu.Unlock()
|
||||
if queued {
|
||||
worker.wakeDisplay()
|
||||
}
|
||||
})
|
||||
}
|
||||
worker.mu.Unlock()
|
||||
if queued {
|
||||
worker.wakeDisplay()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (worker *brailleWorker) DismissConfirmation() {
|
||||
worker.mu.Lock()
|
||||
if !worker.protectedFlash {
|
||||
worker.mu.Unlock()
|
||||
return
|
||||
}
|
||||
worker.protectedFlash = false
|
||||
worker.flashGeneration++
|
||||
if worker.flashTimer != nil {
|
||||
worker.flashTimer.Stop()
|
||||
worker.flashTimer = nil
|
||||
}
|
||||
queued := worker.enqueueFrameLocked(worker.baseFrame, false)
|
||||
worker.mu.Unlock()
|
||||
if queued {
|
||||
worker.wakeDisplay()
|
||||
}
|
||||
}
|
||||
|
||||
func (worker *brailleWorker) SetContraction(enabled bool, table string) {
|
||||
worker.mu.Lock()
|
||||
worker.contracted, worker.table = enabled, table
|
||||
frame := worker.frame
|
||||
running := worker.cancel != nil && !worker.stopping
|
||||
queued := false
|
||||
if running {
|
||||
queued = worker.enqueueFrameLocked(frame, false)
|
||||
}
|
||||
worker.mu.Unlock()
|
||||
if queued {
|
||||
worker.wakeDisplay()
|
||||
}
|
||||
}
|
||||
|
||||
func (worker *brailleWorker) SetWordWrap(enabled bool) {
|
||||
worker.mu.Lock()
|
||||
if worker.wordWrap == enabled {
|
||||
worker.mu.Unlock()
|
||||
return
|
||||
}
|
||||
worker.wordWrap = enabled
|
||||
worker.baseFrame.WordWrap = enabled
|
||||
frame := worker.frame
|
||||
frame.WordWrap = enabled
|
||||
queued := worker.enqueueFrameLocked(frame, false)
|
||||
worker.mu.Unlock()
|
||||
if queued {
|
||||
worker.wakeDisplay()
|
||||
}
|
||||
}
|
||||
|
||||
func (worker *brailleWorker) current() braille.Frame {
|
||||
worker.mu.Lock()
|
||||
defer worker.mu.Unlock()
|
||||
if worker.flashContext != nil && worker.flashContext.Err() != nil {
|
||||
worker.retirePresentationLocked(worker.flashContext)
|
||||
}
|
||||
return worker.frame
|
||||
}
|
||||
|
||||
func (worker *brailleWorker) retirePresentationLocked(owner context.Context) bool {
|
||||
if worker.flashContext != owner {
|
||||
return false
|
||||
}
|
||||
worker.flashContext = nil
|
||||
worker.flashGeneration++
|
||||
if worker.flashTimer != nil {
|
||||
worker.flashTimer.Stop()
|
||||
worker.flashTimer = nil
|
||||
}
|
||||
return worker.enqueueFrameLocked(worker.baseFrame, false)
|
||||
}
|
||||
|
||||
func (worker *brailleWorker) retirePresentation(owner context.Context) {
|
||||
worker.mu.Lock()
|
||||
queued := worker.retirePresentationLocked(owner)
|
||||
worker.mu.Unlock()
|
||||
if queued {
|
||||
worker.wakeDisplay()
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user