diff options
author | xengineering <me@xengineering.eu> | 2023-04-16 11:44:28 +0200 |
---|---|---|
committer | xengineering <me@xengineering.eu> | 2023-04-16 11:44:28 +0200 |
commit | 59c7f02ad05af0223a08f47e3193c331a86445d5 (patch) | |
tree | 0079097fd9eea7c09067c2acf7cc978322db3fc2 /main.go | |
parent | 70456216ebbcc7e5006b6a005c571457e60be6e7 (diff) | |
download | limox-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.go | 249 |
1 files changed, 249 insertions, 0 deletions
@@ -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 +} |