aboutsummaryrefslogtreecommitdiff
path: root/xA/xA.go
diff options
context:
space:
mode:
Diffstat (limited to 'xA/xA.go')
-rw-r--r--xA/xA.go137
1 files changed, 137 insertions, 0 deletions
diff --git a/xA/xA.go b/xA/xA.go
new file mode 100644
index 0000000..192265f
--- /dev/null
+++ b/xA/xA.go
@@ -0,0 +1,137 @@
+// Copyright (c) 2024, Přemysl Eric Janouch <p@janouch.name>
+// SPDX-License-Identifier: 0BSD
+
+package main
+
+import (
+ "bufio"
+ "context"
+ "encoding/binary"
+ "flag"
+ "fmt"
+ "io"
+ "log"
+ "net"
+ "net/url"
+ "os"
+
+ "fyne.io/fyne/v2"
+ "fyne.io/fyne/v2/app"
+ "fyne.io/fyne/v2/container"
+ "fyne.io/fyne/v2/widget"
+)
+
+var (
+ debug = flag.Bool("debug", false, "enable debug output")
+ projectName = "xA"
+ projectVersion = "?"
+ addressConnect string
+)
+
+// -----------------------------------------------------------------------------
+
+func relayReadFrame(r io.Reader) []byte {
+ var length uint32
+ if err := binary.Read(r, binary.BigEndian, &length); err != nil {
+ log.Println("Event receive failed: " + err.Error())
+ return nil
+ }
+ b := make([]byte, length)
+ if _, err := io.ReadFull(r, b); err != nil {
+ log.Println("Event receive failed: " + err.Error())
+ return nil
+ }
+
+ if *debug {
+ log.Printf("<? %v\n", b)
+
+ var m RelayEventMessage
+ if after, ok := m.ConsumeFrom(b); !ok {
+ log.Println("Event deserialization failed")
+ return nil
+ } else if len(after) != 0 {
+ log.Println("Event deserialization failed: trailing data")
+ return nil
+ }
+
+ j, err := m.MarshalJSON()
+ if err != nil {
+ log.Println("Event marshalling failed: " + err.Error())
+ return nil
+ }
+
+ log.Printf("<- %s\n", j)
+ }
+ return b
+}
+
+func relayMakeReceiver(ctx context.Context, conn net.Conn) <-chan []byte {
+ // The usual event message rarely gets above 1 kilobyte,
+ // thus this is set to buffer up at most 1 megabyte or so.
+ p := make(chan []byte, 1000)
+ r := bufio.NewReaderSize(conn, 65536)
+ go func() {
+ defer close(p)
+ for {
+ j := relayReadFrame(r)
+ if j == nil {
+ return
+ }
+ select {
+ case p <- j:
+ case <-ctx.Done():
+ return
+ }
+ }
+ }()
+ return p
+}
+
+func main() {
+ flag.Usage = func() {
+ fmt.Fprintf(flag.CommandLine.Output(),
+ "Usage: %s [OPTION...] CONNECT\n\n", os.Args[0])
+ flag.PrintDefaults()
+ }
+
+ flag.Parse()
+ if flag.NArg() < 1 || flag.NArg() > 1 {
+ flag.Usage()
+ os.Exit(1)
+ }
+
+ addressConnect = flag.Arg(0)
+
+ // TODO(p): First, test Fyne's GUI capabilities.
+
+ a := app.New()
+ w := a.NewWindow(projectName)
+
+ // TODO(p): There should also be a widget.NewLabel() next to the entry.
+ // - Probably another Border, even though this seems odd.
+ var (
+ richtext = widget.NewRichText()
+ richscroll = container.NewVScroll(richtext)
+ entry = widget.NewMultiLineEntry()
+ )
+ w.SetContent(container.NewBorder(nil, entry, nil, nil, richscroll))
+
+ testURL, _ := url.Parse("https://x.com")
+ richtext.Segments = []widget.RichTextSegment{
+ &widget.ParagraphSegment{Texts: []widget.RichTextSegment{
+ &widget.TextSegment{Text: "Test"},
+ &widget.HyperlinkSegment{Text: "X", URL: testURL},
+ &widget.TextSegment{
+ Text: " is a website, certainly",
+ Style: widget.RichTextStyleInline,
+ },
+ }},
+ &widget.TextSegment{Style: widget.RichTextStyleParagraph},
+ &widget.SeparatorSegment{},
+ &widget.TextSegment{Text: "Paragraph"},
+ }
+ richtext.Wrapping = fyne.TextWrapWord
+ richtext.Refresh()
+
+ w.ShowAndRun()
+}