diff options
-rw-r--r-- | gui.go | 76 | ||||
-rw-r--r-- | limox.go | 99 | ||||
-rw-r--r-- | main.go | 228 | ||||
-rw-r--r-- | xmpp.go | 73 |
4 files changed, 248 insertions, 228 deletions
@@ -0,0 +1,76 @@ +package main + +import ( + "image/color" + "log" + + "gioui.org/io/system" + "gioui.org/layout" + "gioui.org/text" + "gioui.org/widget/material" +) + +type GuiEvent uint8 + +const ( + Disconnect GuiEvent = iota +) + +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 +} diff --git a/limox.go b/limox.go new file mode 100644 index 0000000..3ca21b3 --- /dev/null +++ b/limox.go @@ -0,0 +1,99 @@ +package main + +import ( + "log" + "reflect" + + "gioui.org/app" + "gioui.org/font/gofont" + "gioui.org/io/system" + "gioui.org/op" + "gioui.org/unit" + "gioui.org/widget" + "gioui.org/widget/material" +) + +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 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)) + } + l.Window.Invalidate() + } + } +} + +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 + } +} @@ -1,56 +1,12 @@ 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() @@ -64,187 +20,3 @@ func main() { 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)) - } - l.Window.Invalidate() - } - } -} - -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 -} @@ -0,0 +1,73 @@ +package main + +import ( + "log" + "net" +) + +type XmppEvent uint8 + +const ( + XmppDisconnect XmppEvent = iota + XmppConnect +) + +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) +} |