Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: update in the wild #1128

Closed
wants to merge 93 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
93 commits
Select commit Hold shift + click to select a range
13ffcad
refactor: reimplement the event loop with a sequence parser
aymanbagabas Aug 12, 2024
448eb82
fix(ci): skip CI for examples/tutorials running go1.18
aymanbagabas Aug 12, 2024
c629efb
feat: enable focus reporting
caarlos0 Aug 13, 2024
cb37f88
fix: rename events to messages
aymanbagabas Aug 13, 2024
7a49b33
refactor: define renderer execute to write queries to the terminal
aymanbagabas Aug 13, 2024
ea54605
feat: introduce ExecuteSequence to send the terminal arbitrary sequence
aymanbagabas Aug 13, 2024
5cf8c29
feat(example): add example to query the terminal
aymanbagabas Aug 13, 2024
76fc19d
feat: detect key input grapheme runes
aymanbagabas Aug 13, 2024
7782a60
Merge remote-tracking branch 'origin/input' into input-focus-blur
caarlos0 Aug 13, 2024
88a5cd2
fix: parse invalid utf8 sequences
aymanbagabas Aug 13, 2024
7567352
fix: mouse sequence enable/disable order
aymanbagabas Aug 13, 2024
3981b80
refactor: remove tea.ExecuteSequence
aymanbagabas Aug 13, 2024
0dd6210
fix: initialize the terminal before the renderer
aymanbagabas Aug 13, 2024
c2c195c
fix: special case modified f3 key and cursor pos report
aymanbagabas Aug 13, 2024
4247aac
refactor: flatten csi sequence parsing
aymanbagabas Aug 13, 2024
3075646
refactor: bracketed-paste active state
aymanbagabas Aug 13, 2024
a4cd456
Merge remote-tracking branch 'origin/input' into input-focus-blur
caarlos0 Aug 14, 2024
3e87bf8
feat: add Enable/Disable focus report commands
caarlos0 Aug 14, 2024
f2bdd36
fix: use KeyRunes to indicate text input
aymanbagabas Aug 14, 2024
eb2eee4
refactor: rename KeySym to KeyType and make KeyRunes the default
aymanbagabas Aug 15, 2024
0126caf
feat: add kitty keyboard options and settings
aymanbagabas Aug 13, 2024
bdb3237
fix: kitty: request protocol flags and rename flag methods
aymanbagabas Aug 14, 2024
03df14c
feat: kitty: support associated text keyboard enhancement
aymanbagabas Aug 15, 2024
4b7fef7
chore: add kitty/fixterms test cases
aymanbagabas Aug 15, 2024
a37d731
chore: add kitty keyboard command test case
aymanbagabas Aug 15, 2024
ec5b362
refactor: rename query kitty keyboard command
aymanbagabas Aug 15, 2024
4d2072b
fix: kitty: request protocol flags and rename flag methods
aymanbagabas Aug 14, 2024
e16559d
feat(examples): add print-key example
aymanbagabas Aug 14, 2024
d36b259
chore: add modifyOtherKeys test cases
aymanbagabas Aug 15, 2024
44d4973
feat: query and set terminal background, foreground, and cursor colors
aymanbagabas Aug 14, 2024
cdc2503
feat(example): add example to set-terminal-color's
aymanbagabas Aug 14, 2024
e6ad78c
chore: add bg/fg/cur terminal color command test cases
aymanbagabas Aug 15, 2024
8f6ac01
chore: color: add min/max todo note
aymanbagabas Aug 15, 2024
04f843d
feat: support setting and querying the terminal clipboard using OSC52
aymanbagabas Aug 15, 2024
26bc2cb
feat: query the terminal version string and primary device attrs
aymanbagabas Aug 15, 2024
e0865cf
refactor: simplify key modifier matching
aymanbagabas Aug 16, 2024
3f5fb9a
fix(lint): reorder key struct to fix fieldalignment
aymanbagabas Aug 19, 2024
c630d5e
fix: simplify kitty keyboard msg flags
aymanbagabas Aug 19, 2024
0c1a6a4
refactor: reimplement the event loop with a sequence parser (#1080)
aymanbagabas Aug 19, 2024
8e406ff
feat: add kitty keyboard options and settings (#1083)
aymanbagabas Aug 19, 2024
828ff70
feat: support xterm modifyOtherKeys keyboard protocol (#1084)
aymanbagabas Aug 19, 2024
19f916b
Merge branch 'master' into input-background-foreground
aymanbagabas Aug 19, 2024
c657bba
feat: query the terminal version string and primary device attrs (#1088)
aymanbagabas Aug 19, 2024
485e52e
feat: query and set terminal background, foreground, and cursor color…
aymanbagabas Aug 19, 2024
e25b4b5
Merge branch 'master' into input-clipboard
aymanbagabas Aug 19, 2024
7cebfb8
Merge branch 'master' into input-focus-blur
aymanbagabas Aug 19, 2024
6062461
feat: support setting and querying the terminal clipboard using OSC52…
aymanbagabas Aug 19, 2024
2158d73
Merge branch 'master' into input-focus-blur
aymanbagabas Aug 19, 2024
05e8ecb
feat: enable focus reporting (#1081)
aymanbagabas Aug 19, 2024
70853cb
feat: drop erikgeiser/coninput in favor of x/windows
aymanbagabas Aug 15, 2024
da83499
fix: windows: correctly parse upper/lower key
aymanbagabas Aug 16, 2024
ed7dc3d
feat: add support for win32-input-mode
aymanbagabas Aug 15, 2024
0aa699c
chore: bump github.com/charmbracelet/x/windows
aymanbagabas Aug 19, 2024
ffe0133
refactor: change kitty keyboard flag name for clarity
aymanbagabas Aug 19, 2024
8480d19
chore: bump charmbracelet/x
aymanbagabas Aug 19, 2024
3ef72f2
Fix Windows API & Add support for win32-input-mode (#1087)
aymanbagabas Aug 19, 2024
c0f9975
fix: lint errors
aymanbagabas Aug 19, 2024
2df9b54
chore: mark deprecated
aymanbagabas Aug 19, 2024
2635e2e
chore(examples): go mod tidy
aymanbagabas Aug 19, 2024
210358d
docs: add v2 todo code reminders
aymanbagabas Aug 20, 2024
370d248
refactor: remove kitty protocol flags
aymanbagabas Aug 20, 2024
39ea34c
refactor: unexport kitty keyboard settings and options
aymanbagabas Aug 20, 2024
ad68fc1
feat: query modify other keys
aymanbagabas Aug 20, 2024
5f7700e
refactor: unexport modify other keys
aymanbagabas Aug 20, 2024
c47c2b9
refactor: unexport win32 input mode
aymanbagabas Aug 20, 2024
e206b36
fix: use explicit names for kitty keyboard option
aymanbagabas Aug 20, 2024
6f7f9e6
feat: expose the renderer interface
aymanbagabas Aug 19, 2024
fa03b59
feat: add custom renderer option
aymanbagabas Aug 19, 2024
c385aba
feat: add renderer Resize and InsertAbove
aymanbagabas Aug 19, 2024
ae0c273
refactor: check the initial size during Run
aymanbagabas Aug 19, 2024
53d72b4
feat: add the ability to change the renderer output
aymanbagabas Aug 20, 2024
544a715
fix: simplify instantiating a new standard renderer
aymanbagabas Aug 20, 2024
0825f61
feat: export nil renderer
aymanbagabas Aug 20, 2024
fe54df7
docs(examples): add help view to table example
meowgorithm Aug 20, 2024
a6b9970
chore(deps): go mod tidy
meowgorithm Aug 20, 2024
1f88b9e
refactor: simplify renderer interface
aymanbagabas Aug 20, 2024
9660d7d
fix: keep track of terminal capabilities and gracefully turn them off
aymanbagabas Aug 20, 2024
c9f2a56
fix: screen test
aymanbagabas Aug 21, 2024
05f8399
feat: dry setting and retrieving terminal modes
aymanbagabas Aug 21, 2024
f31a5f3
refactor: we don't care about renderer render errors
aymanbagabas Aug 21, 2024
356c649
fix(examples): update testdata
aymanbagabas Aug 21, 2024
5693515
chore(docs): edit some comments in the renderer interface
meowgorithm Aug 21, 2024
1f5a28f
feat: expose the renderer interface (#1094)
aymanbagabas Aug 21, 2024
8635fb3
fix: hide cursor if needed after restore
aymanbagabas Aug 21, 2024
fe88dd5
fix: show the cursor on exit
aymanbagabas Aug 21, 2024
f757c10
chore(deps): bump github.com/charmbracelet/x/ansi from 0.2.1 to 0.2.2…
dependabot[bot] Aug 22, 2024
55fb63f
chore(deps): go mod tidy
meowgorithm Aug 22, 2024
5df8f28
fix(examples): keys shouldn't be routed to viewport in chat example
meowgorithm Aug 22, 2024
fa69e03
fix: lint issues (#1109)
aymanbagabas Aug 22, 2024
508be2e
fix: force query the terminal bg before running any programs
aymanbagabas Aug 22, 2024
833cc78
chore: go mod tidy
aymanbagabas Aug 22, 2024
b2e983a
feat: add mode 2027 grapheme clustering stubs (#1105)
aymanbagabas Aug 23, 2024
ca5f214
docs(featured): clean up in the wild list
bashbunni Sep 3, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,19 @@ jobs:
run: go test ./...

- name: Build examples
if: ${{ matrix.go-version != '~1.18' }}
run: |
go mod tidy
go build -v ./...
working-directory: ./examples

- name: Test examples
if: ${{ matrix.go-version != '~1.18' }}
run: go test -v ./...
working-directory: ./examples

- name: Build tutorials
if: ${{ matrix.go-version != '~1.18' }}
run: |
go mod tidy
go build -v ./...
Expand Down
98 changes: 12 additions & 86 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -323,102 +323,27 @@ your program in another window.

## Bubble Tea in the Wild

For some Bubble Tea programs in production, see:
### Staff favourites

* [ASCII Movie](https://github.com/gabe565/ascii-movie): a Star Wars ASCII art movie player
* [AT CLI](https://github.com/daskycodes/at_cli): execute AT Commands via serial port connections
* [Aztify](https://github.com/Azure/aztfy): bring Microsoft Azure resources under Terraform
* [brows](https://github.com/rubysolo/brows): a GitHub release browser
* [Canard](https://github.com/mrusme/canard): an RSS client
* [charm](https://github.com/charmbracelet/charm): the official Charm user account manager
* [chatgpt-cli](https://github.com/j178/chatgpt): a CLI for ChatGPT
* [chatgpt-tui](https://github.com/tearingItUp786/chatgpt-tui): a TUI for ChatGPT with SQLite sessions
* [ChatGPTUI](https://github.com/dwisiswant0/chatgptui): a TUI for ChatGPT
* [chezmoi](https://github.com/twpayne/chezmoi): securely manage your dotfiles across multiple machines
* [chip-8](https://github.com/braheezy/chip-8): a CHIP-8 interpreter
* [chtop](https://github.com/chhetripradeep/chtop): monitor your ClickHouse node without leaving the terminal
* [circumflex](https://github.com/bensadeh/circumflex): read Hacker News in the terminal
* [clidle](https://github.com/ajeetdsouza/clidle): a Wordle clone
* [cLive](https://github.com/koki-develop/clive): automate terminal operations and view them live in a browser
* [container-canary](https://github.com/NVIDIA/container-canary): a container validator
* [countdown](https://github.com/aldernero/countdown): a multi-event countdown timer
* [CRT](https://github.com/BigJk/crt): a simple terminal emulator for running Bubble Tea in a dedicated window, with optional shaders
* [cueitup](https://github.com/dhth/cueitup): inspect messages in an AWS SQS queue in a simple and deliberate manner
* [Daytona](https://github.com/daytonaio/daytona): an development environment manager
* [dns53](https://github.com/purpleclay/dns53): dynamic DNS with Amazon Route53; expose your EC2 quickly, securely and privately
* [eks-node-viewer](https://github.com/awslabs/eks-node-viewer): a tool for visualizing dynamic node usage within an EKS cluster
* [End Of Eden](https://github.com/BigJk/end_of_eden): a "Slay the Spire"-like, roguelike deck-builder game
* [enola](https://github.com/sherlock-project/enola): find social media accounts by username across social networks
* [flapioca](https://github.com/kbrgl/flapioca): Flappy Bird on the CLI!
* [fm](https://github.com/knipferrc/fm): a terminal-based file manager
* [fork-cleaner](https://github.com/caarlos0/fork-cleaner): clean up old and inactive forks in your GitHub account
* [fractals-cli](https://github.com/MicheleFiladelfia/fractals-cli): a multiplatform terminal fractal explorer
* [fztea](https://github.com/jon4hz/fztea): a Flipper Zero TUI
* [gama](https://github.com/termkit/gama): manage GitHub Actions from the terminal
* [gambit](https://github.com/maaslalani/gambit): chess in the terminal
* [gembro](https://git.sr.ht/~rafael/gembro): a mouse-driven Gemini browser
* [gh-b](https://github.com/joaom00/gh-b): a GitHub CLI extension for managing branches
* [gh-dash](https://www.github.com/dlvhdr/gh-dash): a GitHub CLI extension for PRs and issues
* [gitflow-toolkit](https://github.com/mritd/gitflow-toolkit): a GitFlow submission tool

### Built by companies

* [Aztify](https://github.com/Azure/aztfy): bring Microsoft Azure resources under Terraform
* [Daytona](https://github.com/daytonaio/daytona): an development environment manager
* [Glow](https://github.com/charmbracelet/glow): a markdown reader, browser, and online markdown stash
* [go-sweep](https://github.com/maxpaulus43/go-sweep): Minesweeper in the terminal
* [gocovsh](https://github.com/orlangure/gocovsh): explore Go coverage reports from the CLI
* [got](https://github.com/fedeztk/got): a simple translator and text-to-speech app built on simplytranslate's APIs
* [gum](https://github.com/charmbracelet/gum): interactivity and styling for shells and shell scripts
* [hiSHtory](https://github.com/ddworken/hishtory): your shell history in context: synced, and queryable
* [httpit](https://github.com/gonetx/httpit): a rapid http(s) benchmark tool
* [Huh?](https://github.com/charmbracelet/huh): an interactive prompt and form toolkit
* [IDNT](https://github.com/r-darwish/idnt): a batch software uninstaller
* [json-log-viewer](https://github.com/hedhyw/json-log-viewer): an interactive JSON log viewer
* [kboard](https://github.com/CamiloGarciaLaRotta/kboard): a typing game
* [kplay](https://github.com/dhth/kplay): inspect messages in a Kafka topic
* [laboon](https://github.com/arisnacg/laboon): a Docker-desktop-style container manager
* [Trufflehog](https://github.com/trufflesecurity/trufflehog): find leaked credentials
* [container-canary](https://github.com/NVIDIA/container-canary): a container validator
* [eks-node-viewer](https://github.com/awslabs/eks-node-viewer): a tool for visualizing dynamic node usage within an EKS cluster
* [mc](https://github.com/minio/mc): the official [MinIO](https://min.io) client
* [mergestat](https://github.com/mergestat/mergestat): run SQL queries on git repositories
* [meteor](https://github.com/stefanlogue/meteor): a highly customizable conventional commit message tool
* [mods](https://github.com/charmbracelet/mods): AI on the CLI, built for pipelines
* [nachrichten](https://github.com/zMoooooritz/nachrichten): access up-to-date news in German provided by the [Tagesschau](https://www.tagesschau.de/)
* [Neon Modem Overdrive](https://github.com/mrusme/neonmodem): a BBS-style TUI client for Discourse, Lemmy, Lobste.rs and Hacker News
* [nom](https://github.com/guyfedwards/nom): an RSS reader and manager
* [Noted](https://github.com/torbratsberg/noted): a note viewer and manager
* [outtasync](https://github.com/dhth/outtasync): identify CloudFormation stacks that are out of sync with their template files
* [pathos](https://github.com/chip/pathos): a PATH environment variable editor
* [Plandex](https://github.com/plandex-ai/plandex): a terminal-based AI coding engine for complex tasks
* [portal](https://github.com/ZinoKader/portal): secure transfers between computers
* [prs](https://github.com/dhth/prs): stay up to date with your PRs
* [puffin](https://github.com/siddhantac/puffin): a TUI for hledger to manage your finances
* [pug](https://github.com/leg100/pug): terraform task manager
* [punchout](https://github.com/dhth/punchout): takes the suck out of logging time on JIRA
* [redis-viewer](https://github.com/SaltFishPr/redis-viewer): a Redis database browser
* [redis_tui](https://github.com/mat2cc/redis_tui): a Redis database browser
* [schemas](https://github.com/dhth/schemas): lets you inspect postgres schemas in the terminal
* [scrabbler](https://github.com/wI2L/scrabbler): an automatic draw tool for your duplicate Scrabble games
* [sku](https://github.com/fedeztk/sku): Sudoku on the CLI
* [Slides](https://github.com/maaslalani/slides): a markdown-based presentation tool
* [SlurmCommander](https://github.com/CLIP-HPC/SlurmCommander): a Slurm workload manager
* [Soft Serve](https://github.com/charmbracelet/soft-serve): a command-line-first Git server that runs a TUI over SSH
* [solitaire-tui](https://github.com/brianstrauch/solitaire-tui): Klondike Solitaire for the terminal
* [StormForge Optimize Controller](https://github.com/thestormforge/optimize-controller): a tool for experimenting with application configurations in Kubernetes
* [Storydb](https://github.com/grrlopes/storydb): an improved bash/zsh-style ctrl+r command history finder
* [STTG](https://github.com/wille1101/sttg): a teletext client for SVT, Sweden’s national public television station
* [sttr](https://github.com/abhimanyu003/sttr): a general-purpose text transformer
* [superfile](https://github.com/MHNightCat/superfile) a fancy, modern terminal-based file manager
* [tasktimer](https://github.com/caarlos0/tasktimer): a dead-simple task timer
* [termdbms](https://github.com/mathaou/termdbms): a keyboard and mouse driven database browser
* [tgpt](https://github.com/aandrew-me/tgpt): conversational AI for the CLI; no API keys necessary
* [ticker](https://github.com/achannarasappa/ticker): a terminal stock viewer and stock position tracker
* [trainer](https://github.com/rusinikita/trainer): a Go concurrency coding interview simulator with learning materials
* [tran](https://github.com/abdfnx/tran): securely transfer stuff between computers (based on [portal](https://github.com/ZinoKader/portal))
* [Trufflehog](https://github.com/trufflesecurity/trufflehog): find leaked credentials
* [Typer](https://github.com/maaslalani/typer): a typing test
* [typioca](https://github.com/bloznelis/typioca): a typing test
* [tz](https://github.com/oz/tz): a scheduling aid for people in multiple time zones
* [ugm](https://github.com/ariasmn/ugm): a unix user and group browser
* [walk](https://github.com/antonmedv/walk): a terminal navigator
* [wander](https://github.com/robinovitch61/wander): a HashiCorp Nomad terminal client
* [WG Commander](https://github.com/AndrianBdn/wg-cmd): a TUI for a simple WireGuard VPN setup
* [wishlist](https://github.com/charmbracelet/wishlist): an SSH directory

See [more][community] projects built by the community...

## Feedback

We'd love to hear your thoughts on this project. Feel free to drop us a note!
Expand All @@ -437,6 +362,7 @@ of days past.
[elm]: https://guide.elm-lang.org/architecture/
[gotea]: https://github.com/tj/go-tea
[zb]: https://de.wikipedia.org/wiki/Zeichenorientierte_Benutzerschnittstelle
[community]: https://github.com/charm-and-friends/charm-in-the-wild

## License

Expand Down
2 changes: 1 addition & 1 deletion inputreader_other.go → cancelreader_other.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ import (
"github.com/muesli/cancelreader"
)

func newInputReader(r io.Reader) (cancelreader.CancelReader, error) {
func newCancelreader(r io.Reader) (cancelreader.CancelReader, error) {
return cancelreader.NewReader(r)
}
219 changes: 219 additions & 0 deletions cancelreader_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
//go:build windows
// +build windows

package tea

import (
"fmt"
"io"
"os"
"sync"
"time"

"github.com/muesli/cancelreader"
"golang.org/x/sys/windows"
)

type conInputReader struct {
cancelMixin

conin windows.Handle
cancelEvent windows.Handle

originalMode uint32

// blockingReadSignal is used to signal that a blocking read is in progress.
blockingReadSignal chan struct{}
}

var _ cancelreader.CancelReader = &conInputReader{}

func newCancelreader(r io.Reader) (cancelreader.CancelReader, error) {
fallback := func(io.Reader) (cancelreader.CancelReader, error) {
return cancelreader.NewReader(r)
}

var dummy uint32
if f, ok := r.(cancelreader.File); !ok || f.Fd() != os.Stdin.Fd() ||
// If data was piped to the standard input, it does not emit events
// anymore. We can detect this if the console mode cannot be set anymore,
// in this case, we fallback to the default cancelreader implementation.
windows.GetConsoleMode(windows.Handle(f.Fd()), &dummy) != nil {
return fallback(r)
}

conin, err := windows.GetStdHandle(windows.STD_INPUT_HANDLE)
if err != nil {
return fallback(r)
}

originalMode, err := prepareConsole(conin,
windows.ENABLE_MOUSE_INPUT,
windows.ENABLE_WINDOW_INPUT,
windows.ENABLE_EXTENDED_FLAGS,
)
if err != nil {
return nil, fmt.Errorf("failed to prepare console input: %w", err)
}

cancelEvent, err := windows.CreateEvent(nil, 0, 0, nil)
if err != nil {
return nil, fmt.Errorf("create stop event: %w", err)
}

return &conInputReader{
conin: conin,
cancelEvent: cancelEvent,
originalMode: originalMode,
blockingReadSignal: make(chan struct{}, 1),
}, nil
}

// Cancel implements cancelreader.CancelReader.
func (r *conInputReader) Cancel() bool {
r.setCanceled()

select {
case r.blockingReadSignal <- struct{}{}:
err := windows.SetEvent(r.cancelEvent)
if err != nil {
return false
}
<-r.blockingReadSignal
case <-time.After(100 * time.Millisecond):
// Read() hangs in a GetOverlappedResult which is likely due to
// WaitForMultipleObjects returning without input being available
// so we cannot cancel this ongoing read.
return false
}

return true
}

// Close implements cancelreader.CancelReader.
func (r *conInputReader) Close() error {
err := windows.CloseHandle(r.cancelEvent)
if err != nil {
return fmt.Errorf("closing cancel event handle: %w", err)
}

if r.originalMode != 0 {
err := windows.SetConsoleMode(r.conin, r.originalMode)
if err != nil {
return fmt.Errorf("reset console mode: %w", err)
}
}

return nil
}

// Read implements cancelreader.CancelReader.
func (r *conInputReader) Read(data []byte) (n int, err error) {
if r.isCanceled() {
return 0, cancelreader.ErrCanceled
}

err = waitForInput(r.conin, r.cancelEvent)
if err != nil {
return 0, err
}

if r.isCanceled() {
return 0, cancelreader.ErrCanceled
}

r.blockingReadSignal <- struct{}{}
n, err = overlappedReader(r.conin).Read(data)
<-r.blockingReadSignal

return
}

func prepareConsole(input windows.Handle, modes ...uint32) (originalMode uint32, err error) {
err = windows.GetConsoleMode(input, &originalMode)
if err != nil {
return 0, fmt.Errorf("get console mode: %w", err)
}

var newMode uint32
for _, mode := range modes {
newMode |= mode
}

err = windows.SetConsoleMode(input, newMode)
if err != nil {
return 0, fmt.Errorf("set console mode: %w", err)
}

return originalMode, nil
}

func waitForInput(conin, cancel windows.Handle) error {
event, err := windows.WaitForMultipleObjects([]windows.Handle{conin, cancel}, false, windows.INFINITE)
switch {
case windows.WAIT_OBJECT_0 <= event && event < windows.WAIT_OBJECT_0+2:
if event == windows.WAIT_OBJECT_0+1 {
return cancelreader.ErrCanceled
}

if event == windows.WAIT_OBJECT_0 {
return nil
}

return fmt.Errorf("unexpected wait object is ready: %d", event-windows.WAIT_OBJECT_0)
case windows.WAIT_ABANDONED <= event && event < windows.WAIT_ABANDONED+2:
return fmt.Errorf("abandoned")
case event == uint32(windows.WAIT_TIMEOUT):
return fmt.Errorf("timeout")
case event == windows.WAIT_FAILED:
return fmt.Errorf("failed")
default:
return fmt.Errorf("unexpected error: %w", err)
}
}

// cancelMixin represents a goroutine-safe cancelation status.
type cancelMixin struct {
unsafeCanceled bool
lock sync.Mutex
}

func (c *cancelMixin) setCanceled() {
c.lock.Lock()
defer c.lock.Unlock()

c.unsafeCanceled = true
}

func (c *cancelMixin) isCanceled() bool {
c.lock.Lock()
defer c.lock.Unlock()

return c.unsafeCanceled
}

type overlappedReader windows.Handle

// Read performs an overlapping read fom a windows.Handle.
func (r overlappedReader) Read(data []byte) (int, error) {
hevent, err := windows.CreateEvent(nil, 0, 0, nil)
if err != nil {
return 0, fmt.Errorf("create event: %w", err)
}

overlapped := windows.Overlapped{HEvent: hevent}

var n uint32

err = windows.ReadFile(windows.Handle(r), data, &n, &overlapped)
if err != nil && err != windows.ERROR_IO_PENDING {
return int(n), err
}

err = windows.GetOverlappedResult(windows.Handle(r), &overlapped, &n, true)
if err != nil {
return int(n), nil
}

return int(n), nil
}
Loading
Loading