aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--debug/debug.go18
-rw-r--r--gomuks.go69
-rw-r--r--interface/gomuks.go3
-rw-r--r--interface/ui.go12
-rw-r--r--main.go44
-rw-r--r--matrix/matrix.go8
-rw-r--r--ui/message-view.go4
-rw-r--r--ui/messages/base.go2
-rw-r--r--ui/messages/imagemessage.go18
-rw-r--r--ui/messages/message.go2
-rw-r--r--ui/messages/parser/parser.go10
-rw-r--r--ui/room-view.go4
-rw-r--r--ui/ui.go26
-rw-r--r--ui/view-main.go12
-rw-r--r--ui/widget/color.go2
15 files changed, 137 insertions, 97 deletions
diff --git a/debug/debug.go b/debug/debug.go
index c6f367e..3e32125 100644
--- a/debug/debug.go
+++ b/debug/debug.go
@@ -29,6 +29,8 @@ import (
)
var writer io.Writer
+var RecoverPrettyPanic bool
+var OnRecover func()
func init() {
var err error
@@ -59,6 +61,22 @@ func PrintStack() {
}
}
+// Recover recovers a panic, runs the OnRecover handler and either re-panics or
+// shows an user-friendly message about the panic depending on whether or not
+// the pretty panic mode is enabled.
+func Recover() {
+ if p := recover(); p != nil {
+ if OnRecover != nil {
+ OnRecover()
+ }
+ if RecoverPrettyPanic {
+ PrettyPanic(p)
+ } else {
+ panic(p)
+ }
+ }
+}
+
const Oops = ` __________
< Oh noes! >
‾‾‾\‾‾‾‾‾‾
diff --git a/gomuks.go b/gomuks.go
index 494f182..ffdfad3 100644
--- a/gomuks.go
+++ b/gomuks.go
@@ -17,7 +17,6 @@
package main
import (
- "fmt"
"os"
"path/filepath"
"time"
@@ -26,44 +25,36 @@ import (
"maunium.net/go/gomuks/debug"
"maunium.net/go/gomuks/interface"
"maunium.net/go/gomuks/matrix"
- "maunium.net/go/gomuks/ui"
- "maunium.net/go/tview"
)
// Gomuks is the wrapper for everything.
type Gomuks struct {
- app *tview.Application
- ui *ui.GomuksUI
- matrix *matrix.Container
- debugMode bool
- config *config.Config
- stop chan bool
+ ui ifc.GomuksUI
+ matrix *matrix.Container
+ config *config.Config
+ stop chan bool
}
// NewGomuks creates a new Gomuks instance with everything initialized,
// but does not start it.
-func NewGomuks(enableDebug bool) *Gomuks {
+func NewGomuks(uiProvider ifc.UIProvider) *Gomuks {
configDir := filepath.Join(os.Getenv("HOME"), ".config/gomuks")
gmx := &Gomuks{
- app: tview.NewApplication(),
- stop: make(chan bool, 1),
- debugMode: enableDebug,
+ stop: make(chan bool, 1),
}
gmx.config = config.NewConfig(configDir)
- gmx.ui = ui.NewGomuksUI(gmx)
+ gmx.ui = uiProvider(gmx)
gmx.matrix = matrix.NewContainer(gmx)
+ gmx.ui.Init()
+
+ debug.OnRecover = gmx.ui.Finish
gmx.config.Load()
if len(gmx.config.UserID) > 0 {
_ = gmx.config.LoadSession(gmx.config.UserID)
}
- _ = gmx.matrix.InitClient()
-
- main := gmx.ui.InitViews()
- gmx.app.SetRoot(main, true)
-
return gmx
}
@@ -80,7 +71,7 @@ func (gmx *Gomuks) Save() {
// StartAutosave calls Save() every minute until it receives a stop signal
// on the Gomuks.stop channel.
func (gmx *Gomuks) StartAutosave() {
- defer gmx.Recover()
+ defer debug.Recover()
ticker := time.NewTicker(time.Minute)
for {
select {
@@ -100,36 +91,21 @@ func (gmx *Gomuks) Stop() {
debug.Print("Disconnecting from Matrix...")
gmx.matrix.Stop()
debug.Print("Cleaning up UI...")
- gmx.app.Stop()
+ gmx.ui.Stop()
gmx.stop <- true
gmx.Save()
os.Exit(0)
}
-// Recover recovers a panic, closes the tcell screen and either re-panics or
-// shows an user-friendly message about the panic depending on whether or not
-// the debug mode is enabled.
-func (gmx *Gomuks) Recover() {
- if p := recover(); p != nil {
- if gmx.App().GetScreen() != nil {
- gmx.App().GetScreen().Fini()
- }
- if gmx.debugMode {
- panic(p)
- } else {
- debug.PrettyPanic(p)
- }
- }
-}
-
// Start opens a goroutine for the autosave loop and starts the tview app.
//
// If the tview app returns an error, it will be passed into panic(), which
// will be recovered as specified in Recover().
func (gmx *Gomuks) Start() {
- defer gmx.Recover()
+ _ = gmx.matrix.InitClient()
+
go gmx.StartAutosave()
- if err := gmx.app.Run(); err != nil {
+ if err := gmx.ui.Start(); err != nil {
panic(err)
}
}
@@ -139,11 +115,6 @@ func (gmx *Gomuks) Matrix() ifc.MatrixContainer {
return gmx.matrix
}
-// App returns the tview Application instance.
-func (gmx *Gomuks) App() *tview.Application {
- return gmx.app
-}
-
// Config returns the Gomuks config instance.
func (gmx *Gomuks) Config() *config.Config {
return gmx.config
@@ -153,13 +124,3 @@ func (gmx *Gomuks) Config() *config.Config {
func (gmx *Gomuks) UI() ifc.GomuksUI {
return gmx.ui
}
-
-func main() {
- enableDebug := len(os.Getenv("DEBUG")) > 0
- NewGomuks(enableDebug).Start()
-
- // We use os.Exit() everywhere, so exiting by returning from Start() shouldn't happen.
- time.Sleep(5 * time.Second)
- fmt.Println("Unexpected exit by return from Gomuks#Start().")
- os.Exit(2)
-}
diff --git a/interface/gomuks.go b/interface/gomuks.go
index fbe05e1..90a5c4c 100644
--- a/interface/gomuks.go
+++ b/interface/gomuks.go
@@ -18,17 +18,14 @@ package ifc
import (
"maunium.net/go/gomuks/config"
- "maunium.net/go/tview"
)
// Gomuks is the wrapper for everything.
type Gomuks interface {
Matrix() MatrixContainer
- App() *tview.Application
UI() GomuksUI
Config() *config.Config
Start()
Stop()
- Recover()
}
diff --git a/interface/ui.go b/interface/ui.go
index 271dbd2..731e321 100644
--- a/interface/ui.go
+++ b/interface/ui.go
@@ -33,11 +33,18 @@ const (
ViewMain View = "main"
)
+type UIProvider func(gmx Gomuks) GomuksUI
+
type GomuksUI interface {
Render()
SetView(name View)
MainView() MainView
LoginView() LoginView
+
+ Init()
+ Start() error
+ Stop()
+ Finish()
}
type MainView interface {
@@ -50,8 +57,7 @@ type MainView interface {
SetTyping(roomID string, users []string)
ParseEvent(roomView RoomView, evt *gomatrix.Event) Message
- //ProcessMessageEvent(roomView RoomView, evt *gomatrix.Event) Message
- //ProcessMembershipEvent(roomView RoomView, evt *gomatrix.Event) Message
+
NotifyMessage(room *rooms.Room, message Message, should pushrules.PushActionArrayShould)
}
@@ -69,7 +75,7 @@ const (
type RoomView interface {
MxRoom() *rooms.Room
SaveHistory(dir string) error
- LoadHistory(gmx Gomuks, dir string) (int, error)
+ LoadHistory(matrix MatrixContainer, dir string) (int, error)
SetStatus(status string)
SetTyping(users []string)
diff --git a/main.go b/main.go
new file mode 100644
index 0000000..f9955a1
--- /dev/null
+++ b/main.go
@@ -0,0 +1,44 @@
+// gomuks - A terminal Matrix client written in Go.
+// Copyright (C) 2018 Tulir Asokan
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+package main
+
+import (
+ "fmt"
+ "os"
+ "time"
+
+ "maunium.net/go/gomuks/debug"
+ "maunium.net/go/gomuks/interface"
+ "maunium.net/go/gomuks/ui"
+)
+
+var MainUIProvider ifc.UIProvider = ui.NewGomuksUI
+
+func main() {
+ defer debug.Recover()
+
+ enableDebug := len(os.Getenv("DEBUG")) > 0
+ debug.RecoverPrettyPanic = !enableDebug
+
+ gmx := NewGomuks(MainUIProvider)
+ gmx.Start()
+
+ // We use os.Exit() everywhere, so exiting by returning from Start() shouldn't happen.
+ time.Sleep(5 * time.Second)
+ fmt.Println("Unexpected exit by return from gmx.Start().")
+ os.Exit(2)
+}
diff --git a/matrix/matrix.go b/matrix/matrix.go
index a2e3767..a38b439 100644
--- a/matrix/matrix.go
+++ b/matrix/matrix.go
@@ -196,7 +196,7 @@ func (c *Container) OnLogin() {
// Start moves the UI to the main view, calls OnLogin() and runs the syncer forever until stopped with Stop()
func (c *Container) Start() {
- defer c.gmx.Recover()
+ defer debug.Recover()
c.ui.SetView(ifc.ViewMain)
c.OnLogin()
@@ -317,7 +317,7 @@ func (c *Container) HandleTyping(evt *gomatrix.Event) {
// SendMessage sends a message with the given text to the given room.
func (c *Container) SendMessage(roomID, msgtype, text string) (string, error) {
- defer c.gmx.Recover()
+ defer debug.Recover()
c.SendTyping(roomID, false)
resp, err := c.client.SendMessageEvent(roomID, "m.room.message",
gomatrix.TextMessage{MsgType: msgtype, Body: text})
@@ -328,7 +328,7 @@ func (c *Container) SendMessage(roomID, msgtype, text string) (string, error) {
}
func (c *Container) SendMarkdownMessage(roomID, msgtype, text string) (string, error) {
- defer c.gmx.Recover()
+ defer debug.Recover()
html := string(blackfriday.Run([]byte(text)))
if html == text {
@@ -351,7 +351,7 @@ func (c *Container) SendMarkdownMessage(roomID, msgtype, text string) (string, e
// SendTyping sets whether or not the user is typing in the given room.
func (c *Container) SendTyping(roomID string, typing bool) {
- defer c.gmx.Recover()
+ defer debug.Recover()
ts := time.Now().Unix()
if c.typing > ts && typing {
return
diff --git a/ui/message-view.go b/ui/message-view.go
index 4b67c6a..71d1cba 100644
--- a/ui/message-view.go
+++ b/ui/message-view.go
@@ -95,7 +95,7 @@ func (view *MessageView) SaveHistory(path string) error {
return nil
}
-func (view *MessageView) LoadHistory(gmx ifc.Gomuks, path string) (int, error) {
+func (view *MessageView) LoadHistory(matrix ifc.MatrixContainer, path string) (int, error) {
file, err := os.OpenFile(path, os.O_RDONLY, 0600)
if err != nil {
if os.IsNotExist(err) {
@@ -119,7 +119,7 @@ func (view *MessageView) LoadHistory(gmx ifc.Gomuks, path string) (int, error) {
if message != nil {
view.messages[index-indexOffset] = message
view.updateWidestSender(message.Sender())
- message.RegisterGomuks(gmx)
+ message.RegisterMatrix(matrix)
} else {
indexOffset++
}
diff --git a/ui/messages/base.go b/ui/messages/base.go
index 5322b34..cf698db 100644
--- a/ui/messages/base.go
+++ b/ui/messages/base.go
@@ -59,7 +59,7 @@ func newBaseMessage(id, sender, displayname, msgtype string, timestamp time.Time
}
}
-func (msg *BaseMessage) RegisterGomuks(gmx ifc.Gomuks) {}
+func (msg *BaseMessage) RegisterMatrix(matrix ifc.MatrixContainer) {}
// Sender gets the string that should be displayed as the sender of this message.
//
diff --git a/ui/messages/imagemessage.go b/ui/messages/imagemessage.go
index cd8c2fe..c9301c5 100644
--- a/ui/messages/imagemessage.go
+++ b/ui/messages/imagemessage.go
@@ -41,27 +41,26 @@ type ImageMessage struct {
FileID string
data []byte
- gmx ifc.Gomuks
+ matrix ifc.MatrixContainer
}
// NewImageMessage creates a new ImageMessage object with the provided values and the default state.
-func NewImageMessage(gmx ifc.Gomuks, id, sender, displayname, msgtype, homeserver, fileID string, data []byte, timestamp time.Time) UIMessage {
+func NewImageMessage(matrix ifc.MatrixContainer, id, sender, displayname, msgtype, homeserver, fileID string, data []byte, timestamp time.Time) UIMessage {
return &ImageMessage{
newBaseMessage(id, sender, displayname, msgtype, timestamp),
homeserver,
fileID,
data,
- gmx,
+ matrix,
}
}
-func (msg *ImageMessage) RegisterGomuks(gmx ifc.Gomuks) {
- msg.gmx = gmx
+func (msg *ImageMessage) RegisterMatrix(matrix ifc.MatrixContainer) {
+ msg.matrix = matrix
- debug.Print(len(msg.data), msg.data)
if len(msg.data) == 0 {
go func() {
- defer gmx.Recover()
+ defer debug.Recover()
msg.updateData()
}()
}
@@ -73,7 +72,7 @@ func (msg *ImageMessage) NotificationContent() string {
func (msg *ImageMessage) updateData() {
debug.Print("Loading image:", msg.Homeserver, msg.FileID)
- data, _, _, err := msg.gmx.Matrix().Download(fmt.Sprintf("mxc://%s/%s", msg.Homeserver, msg.FileID))
+ data, _, _, err := msg.matrix.Download(fmt.Sprintf("mxc://%s/%s", msg.Homeserver, msg.FileID))
if err != nil {
debug.Print("Failed to download image %s/%s: %v", msg.Homeserver, msg.FileID, err)
return
@@ -82,7 +81,7 @@ func (msg *ImageMessage) updateData() {
}
func (msg *ImageMessage) Path() string {
- return msg.gmx.Matrix().GetCachePath(msg.Homeserver, msg.FileID)
+ return msg.matrix.GetCachePath(msg.Homeserver, msg.FileID)
}
// CalculateBuffer generates the internal buffer for this message that consists
@@ -108,4 +107,3 @@ func (msg *ImageMessage) CalculateBuffer(width int) {
func (msg *ImageMessage) RecalculateBuffer() {
msg.CalculateBuffer(msg.prevBufferWidth)
}
-
diff --git a/ui/messages/message.go b/ui/messages/message.go
index 0d5c9e4..d3f2db4 100644
--- a/ui/messages/message.go
+++ b/ui/messages/message.go
@@ -32,7 +32,7 @@ type UIMessage interface {
SenderID() string
RealSender() string
- RegisterGomuks(gmx ifc.Gomuks)
+ RegisterMatrix(matrix ifc.MatrixContainer)
}
const DateFormat = "January _2, 2006"
diff --git a/ui/messages/parser/parser.go b/ui/messages/parser/parser.go
index fbb2bf5..9c833f5 100644
--- a/ui/messages/parser/parser.go
+++ b/ui/messages/parser/parser.go
@@ -30,10 +30,10 @@ import (
"maunium.net/go/tcell"
)
-func ParseEvent(gmx ifc.Gomuks, room *rooms.Room, evt *gomatrix.Event) messages.UIMessage {
+func ParseEvent(matrix ifc.MatrixContainer, room *rooms.Room, evt *gomatrix.Event) messages.UIMessage {
switch evt.Type {
case "m.room.message":
- return ParseMessage(gmx, room, evt)
+ return ParseMessage(matrix, room, evt)
case "m.room.member":
return ParseMembershipEvent(room, evt)
}
@@ -48,7 +48,7 @@ func unixToTime(unix int64) time.Time {
return timestamp
}
-func ParseMessage(gmx ifc.Gomuks, room *rooms.Room, evt *gomatrix.Event) messages.UIMessage {
+func ParseMessage(matrix ifc.MatrixContainer, room *rooms.Room, evt *gomatrix.Event) messages.UIMessage {
displayname := evt.Sender
member := room.GetMember(evt.Sender)
if member != nil {
@@ -68,11 +68,11 @@ func ParseMessage(gmx ifc.Gomuks, room *rooms.Room, evt *gomatrix.Event) message
}
case "m.image":
url, _ := evt.Content["url"].(string)
- data, hs, id, err := gmx.Matrix().Download(url)
+ data, hs, id, err := matrix.Download(url)
if err != nil {
debug.Printf("Failed to download %s: %v", url, err)
}
- return messages.NewImageMessage(gmx, evt.ID, evt.Sender, displayname, msgtype, hs, id, data, ts)
+ return messages.NewImageMessage(matrix, evt.ID, evt.Sender, displayname, msgtype, hs, id, data, ts)
}
return nil
}
diff --git a/ui/room-view.go b/ui/room-view.go
index d1e5a16..5268682 100644
--- a/ui/room-view.go
+++ b/ui/room-view.go
@@ -81,8 +81,8 @@ func (view *RoomView) SaveHistory(dir string) error {
return view.MessageView().SaveHistory(view.logPath(dir))
}
-func (view *RoomView) LoadHistory(gmx ifc.Gomuks, dir string) (int, error) {
- return view.MessageView().LoadHistory(gmx, view.logPath(dir))
+func (view *RoomView) LoadHistory(matrix ifc.MatrixContainer, dir string) (int, error) {
+ return view.MessageView().LoadHistory(matrix, view.logPath(dir))
}
func (view *RoomView) SetTabCompleteFunc(fn func(room *RoomView, text string, cursorOffset int) string) *RoomView {
diff --git a/ui/ui.go b/ui/ui.go
index b5f847d..75fd8bf 100644
--- a/ui/ui.go
+++ b/ui/ui.go
@@ -36,14 +36,32 @@ func init() {
tview.Styles.ContrastBackgroundColor = tcell.ColorDarkGreen
}
-func NewGomuksUI(gmx ifc.Gomuks) (ui *GomuksUI) {
- ui = &GomuksUI{
+func NewGomuksUI(gmx ifc.Gomuks) ifc.GomuksUI {
+ ui := &GomuksUI{
gmx: gmx,
- app: gmx.App(),
+ app: tview.NewApplication(),
views: tview.NewPages(),
}
ui.views.SetChangedFunc(ui.Render)
- return
+ return ui
+}
+
+func (ui *GomuksUI) Init() {
+ ui.app.SetRoot(ui.InitViews(), true)
+}
+
+func (ui *GomuksUI) Start() error {
+ return ui.app.Run()
+}
+
+func (ui *GomuksUI) Stop() {
+ ui.app.Stop()
+}
+
+func (ui *GomuksUI) Finish() {
+ if ui.app.GetScreen() != nil {
+ ui.app.GetScreen().Fini()
+ }
}
func (ui *GomuksUI) Render() {
diff --git a/ui/view-main.go b/ui/view-main.go
index f54654a..c3e94a5 100644
--- a/ui/view-main.go
+++ b/ui/view-main.go
@@ -143,7 +143,7 @@ func (view *MainView) SendMessage(roomView *RoomView, text string) {
}
func (view *MainView) sendTempMessage(roomView *RoomView, tempMessage ifc.Message, text string) {
- defer view.gmx.Recover()
+ defer debug.Recover()
eventID, err := view.matrix.SendMarkdownMessage(roomView.Room.ID, tempMessage.Type(), text)
if err != nil {
tempMessage.SetState(ifc.MessageStateFailed)
@@ -154,7 +154,7 @@ func (view *MainView) sendTempMessage(roomView *RoomView, tempMessage ifc.Messag
}
func (view *MainView) HandleCommand(roomView *RoomView, command string, args []string) {
- defer view.gmx.Recover()
+ defer debug.Recover()
debug.Print("Handling command", command, args)
switch command {
case "/me":
@@ -286,7 +286,7 @@ func (view *MainView) SwitchRoom(roomIndex int) {
roomView.Room.MarkRead()
}
view.roomList.SetSelected(roomView.Room)
- view.gmx.App().SetFocus(view)
+ view.parent.app.SetFocus(view)
view.parent.Render()
}
@@ -321,7 +321,7 @@ func (view *MainView) addRoom(index int, room string) {
view.roomView.AddPage(room, roomView, true, false)
roomView.UpdateUserList()
- count, err := roomView.LoadHistory(view.gmx, view.config.HistoryDir)
+ count, err := roomView.LoadHistory(view.matrix, view.config.HistoryDir)
if err != nil {
debug.Printf("Failed to load history of %s: %v", roomView.Room.GetTitle(), err)
} else if count <= 0 {
@@ -424,7 +424,7 @@ func (view *MainView) NotifyMessage(room *rooms.Room, message ifc.Message, shoul
}
func (view *MainView) LoadHistory(room string, initial bool) {
- defer view.gmx.Recover()
+ defer debug.Recover()
roomView := view.rooms[room]
batch := roomView.Room.PrevBatch
@@ -472,5 +472,5 @@ func (view *MainView) LoadHistory(room string, initial bool) {
}
func (view *MainView) ParseEvent(roomView ifc.RoomView, evt *gomatrix.Event) ifc.Message {
- return parser.ParseEvent(view.gmx, roomView.MxRoom(), evt)
+ return parser.ParseEvent(view.matrix, roomView.MxRoom(), evt)
}
diff --git a/ui/widget/color.go b/ui/widget/color.go
index c46377a..e2c2b68 100644
--- a/ui/widget/color.go
+++ b/ui/widget/color.go
@@ -21,7 +21,6 @@ import (
"hash/fnv"
"sort"
- "maunium.net/go/gomuks/debug"
"maunium.net/go/tcell"
)
@@ -52,7 +51,6 @@ func init() {
// <-- = red
// --- = yellow
func GetHashColorName(s string) string {
- debug.Print("Getting color for", s)
switch s {
case "-->":
return "green"