summaryrefslogtreecommitdiff
path: root/main.go
diff options
context:
space:
mode:
authorxengineering <me@xengineering.eu>2023-04-16 11:44:28 +0200
committerxengineering <me@xengineering.eu>2023-04-16 11:44:28 +0200
commit59c7f02ad05af0223a08f47e3193c331a86445d5 (patch)
tree0079097fd9eea7c09067c2acf7cc978322db3fc2 /main.go
parent70456216ebbcc7e5006b6a005c571457e60be6e7 (diff)
downloadlimox-59c7f02ad05af0223a08f47e3193c331a86445d5.tar
limox-59c7f02ad05af0223a08f47e3193c331a86445d5.tar.zst
limox-59c7f02ad05af0223a08f47e3193c331a86445d5.zip
Switch completely to Go and Gio UI
The properties of the Go language, standard library and the Gio UI library are well suited for this project. The existing experimental Go code exceeds the GTK4 and SDL attempts by far with respect to quality and code simplicity.
Diffstat (limited to 'main.go')
-rw-r--r--main.go249
1 files changed, 249 insertions, 0 deletions
diff --git a/main.go b/main.go
new file mode 100644
index 0000000..2803e68
--- /dev/null
+++ b/main.go
@@ -0,0 +1,249 @@
+package main
+
+import (
+ "image/color"
+ "log"
+ "net"
+ "os"
+ "reflect"
+
+ "gioui.org/app"
+ "gioui.org/font/gofont"
+ "gioui.org/io/system"
+ "gioui.org/layout"
+ "gioui.org/op"
+ "gioui.org/text"
+ "gioui.org/unit"
+ "gioui.org/widget"
+ "gioui.org/widget/material"
+)
+
+type XmppEvent uint8
+
+const (
+ XmppDisconnect XmppEvent = iota
+ XmppConnect
+)
+
+type GuiEvent uint8
+
+const (
+ Disconnect GuiEvent = iota
+)
+
+type LimoxState uint8
+
+const (
+ Disconnected LimoxState = iota
+ Connecting
+ Connected
+)
+
+type Limox struct {
+ JidEditor widget.Editor
+ PwdEditor widget.Editor
+ MainButton widget.Clickable
+ XmppEvents chan any
+ GuiEvents chan GuiEvent
+ State LimoxState
+ Window *app.Window
+ Operations op.Ops
+ Theme *material.Theme
+}
+
+func main() {
+ limox := NewLimox()
+
+ go func() {
+ err := limox.run()
+ if err != nil {
+ log.Fatal(err)
+ }
+ os.Exit(0)
+ }()
+
+ app.Main()
+}
+
+func NewLimox() Limox {
+ return Limox{
+ Window: app.NewWindow(
+ app.Title("LimoX"),
+ app.Size(unit.Dp(400), unit.Dp(600)),
+ ),
+ Operations: op.Ops{},
+ Theme: material.NewTheme(gofont.Collection()),
+ XmppEvents: make(chan any),
+ GuiEvents: make(chan GuiEvent),
+ State: Disconnected,
+ }
+}
+
+func (l *Limox) run() error {
+ for {
+ select {
+ case e := <-l.Window.Events():
+ switch e := e.(type) {
+ case system.DestroyEvent:
+ return e.Err
+ case system.FrameEvent:
+ if l.MainButton.Clicked() {
+ l.buttonCallback()
+ }
+ l.draw(e)
+ }
+ case ev := <-l.XmppEvents:
+ switch ev.(type) {
+ case error:
+ log.Print(ev)
+ l.State = Disconnected
+ case XmppEvent:
+ switch ev {
+ case XmppDisconnect:
+ l.State = Disconnected
+ case XmppConnect:
+ l.State = Connected
+ default:
+ log.Fatalf("Unknown XmppEvent '%d'\n", ev)
+ }
+ default:
+ log.Fatalf("Unknown event type '%s'.\n", reflect.TypeOf(ev))
+ }
+ }
+ }
+}
+
+func (l *Limox) buttonCallback() {
+ switch l.State {
+ case Disconnected:
+ log.Println("Starting connection establishment")
+ go l.xmpp(l.JidEditor.Text(), l.PwdEditor.Text())
+ l.State = Connecting
+ case Connecting:
+ log.Println("Aborted connection establishment")
+ l.State = Disconnected
+ case Connected:
+ log.Println("Disconnected")
+ l.GuiEvents <- Disconnect
+ l.State = Disconnected
+ }
+}
+
+func (l *Limox) xmpp(jid string, pwd string) {
+ log.Printf("JID: '%s' PWD: '%s'\n", jid, pwd)
+
+ domain := domainpart(jid)
+ log.Printf("Domain: '%s'\n", domain)
+
+ tcpServer, err := net.ResolveTCPAddr("tcp", domain+":"+"5222")
+ if err != nil {
+ l.XmppEvents <- err
+ return
+ }
+ log.Printf("Server: %s\n", tcpServer)
+
+ conn, err := net.DialTCP("tcp", nil, tcpServer)
+ if err != nil {
+ l.XmppEvents <- err
+ return
+ }
+ l.XmppEvents <- XmppConnect
+
+ var closing bool = false
+ for {
+ ev := <-l.GuiEvents
+ switch ev {
+ case Disconnect:
+ closing = true
+ default:
+ log.Fatalf("Unknown GuiEvent '%d'!\n", ev)
+ }
+ if closing {
+ break
+ }
+ }
+
+ conn.Close()
+ l.XmppEvents <- XmppDisconnect
+}
+
+// domainpart extracts the domain name from a JID / XMPP address. See
+// https://datatracker.ietf.org/doc/html/rfc7622#section-3.2 for details.
+func domainpart(jid string) string {
+ list := []rune(jid)
+
+ for i, v := range list {
+ if v == '/' {
+ list = list[:i]
+ break
+ }
+ }
+
+ for i, v := range list {
+ if v == '@' {
+ list = list[i+1:]
+ break
+ }
+ }
+
+ return string(list)
+}
+
+func (l *Limox) draw(e system.FrameEvent) {
+ gtx := layout.NewContext(&l.Operations, e)
+
+ flex := layout.Flex{
+ Axis: layout.Vertical,
+ Spacing: layout.SpaceBetween,
+ }
+
+ flex.Layout(gtx,
+ layout.Rigid(
+ func(gtx layout.Context) layout.Dimensions {
+ h2 := material.H2(l.Theme, "LimoX")
+ h2.Color = color.NRGBA{R: 20, G: 20, B: 20, A: 255}
+ h2.Alignment = text.Middle
+ return h2.Layout(gtx)
+ },
+ ),
+ layout.Rigid(
+ func(gtx layout.Context) layout.Dimensions {
+ jid := material.Editor(l.Theme, &l.JidEditor, "user@example.com")
+ jid.Editor.Alignment = text.Middle
+ return jid.Layout(gtx)
+ },
+ ),
+ layout.Rigid(
+ func(gtx layout.Context) layout.Dimensions {
+ pwd := material.Editor(l.Theme, &l.PwdEditor, "mySafePassword")
+ pwd.Editor.Alignment = text.Middle
+ pwd.Editor.Mask = '*'
+ return pwd.Layout(gtx)
+ },
+ ),
+ layout.Rigid(
+ func(gtx layout.Context) layout.Dimensions {
+ btn := material.Button(l.Theme, &l.MainButton,
+ l.buttonLabel())
+ return btn.Layout(gtx)
+ },
+ ),
+ )
+
+ e.Frame(gtx.Ops)
+}
+
+func (l *Limox) buttonLabel() string {
+ var label string
+ switch l.State {
+ case Disconnected:
+ label = "Connect"
+ case Connecting:
+ label = "Abort"
+ case Connected:
+ label = "Disconnect"
+ default:
+ log.Fatalf("Unknown Limox state '%d'\n", l.State)
+ }
+ return label
+}