Cthulhu is no longer a fork. Now it is a screen reader written in Golang.
This commit is contained in:
1 parent
a998fe67de
commit
069fc674dc
708 files changed
+112482
-532
No files matched your search
@@ -0,0 +1,100 @@
|
||||
// Copyright (c) 2026 Storm Dragon
|
||||
// Authors: Storm Dragon, Angus Kola
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package clipboard
|
||||
|
||||
/*
|
||||
#include "watch.h"
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"runtime"
|
||||
"time"
|
||||
)
|
||||
|
||||
type WatchSettings struct {
|
||||
Enabled bool
|
||||
Epoch uint64
|
||||
}
|
||||
|
||||
type TextChange struct {
|
||||
Text string
|
||||
Epoch uint64
|
||||
Timestamp uint32
|
||||
Ready bool
|
||||
}
|
||||
|
||||
// WatchText keeps a GTK selection connection warm so successive clipboard
|
||||
// changes are captured asynchronously and emitted in arrival order. Disabled
|
||||
// applications never request clipboard targets or contents.
|
||||
func WatchText(ctx context.Context, input io.Reader, output io.Writer) error {
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
settings := make(chan WatchSettings, 1)
|
||||
inputDone := make(chan error, 1)
|
||||
go func() {
|
||||
scanner := bufio.NewScanner(input)
|
||||
scanner.Buffer(make([]byte, 1024), 4096)
|
||||
for scanner.Scan() {
|
||||
var request WatchSettings
|
||||
if err := json.Unmarshal(scanner.Bytes(), &request); err != nil {
|
||||
inputDone <- err
|
||||
return
|
||||
}
|
||||
select {
|
||||
case settings <- request:
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
inputDone <- scanner.Err()
|
||||
}()
|
||||
if C.clipboard_watch_init() == 0 {
|
||||
return errors.New("cannot watch clipboard on this display")
|
||||
}
|
||||
encoder := json.NewEncoder(output)
|
||||
if err := encoder.Encode(TextChange{Ready: true}); err != nil {
|
||||
return err
|
||||
}
|
||||
ticker := time.NewTicker(time.Millisecond)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case err := <-inputDone:
|
||||
return err
|
||||
case request := <-settings:
|
||||
enabled := C.int(0)
|
||||
if request.Enabled {
|
||||
enabled = 1
|
||||
}
|
||||
C.clipboard_watch_enable(enabled, C.ulonglong(request.Epoch))
|
||||
if err := encoder.Encode(TextChange{Ready: true, Epoch: request.Epoch}); err != nil {
|
||||
return err
|
||||
}
|
||||
case <-ticker.C:
|
||||
C.clipboard_watch_iterate()
|
||||
for {
|
||||
change := C.clipboard_watch_next()
|
||||
if change == nil {
|
||||
break
|
||||
}
|
||||
value := TextChange{Text: C.GoString(change.text), Epoch: uint64(change.epoch), Timestamp: uint32(change.timestamp)}
|
||||
C.clipboard_watch_free(change)
|
||||
if err := encoder.Encode(value); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user