aboutsummaryrefslogtreecommitdiff
path: root/ui
diff options
context:
space:
mode:
authorTulir Asokan <tulir@maunium.net>2019-04-27 15:02:21 +0300
committerTulir Asokan <tulir@maunium.net>2019-04-27 15:02:21 +0300
commitbc7e2d9a1c871e3fbce932f9695fc24083bc2cc4 (patch)
tree89f104f59ee6aa33ea41140c8472ff5ba95f97a9 /ui
parent60e3fe392711ef233aa68a062a7ee96d145d2cb4 (diff)
Add locks and other sync stuff
Diffstat (limited to 'ui')
-rw-r--r--ui/message-view.go213
-rw-r--r--ui/room-list.go46
-rw-r--r--ui/tag-room-list.go2
-rw-r--r--ui/view-main.go111
4 files changed, 254 insertions, 118 deletions
diff --git a/ui/message-view.go b/ui/message-view.go
index 1d9519e..43c757f 100644
--- a/ui/message-view.go
+++ b/ui/message-view.go
@@ -20,6 +20,8 @@ import (
"fmt"
"math"
"strings"
+ "sync"
+ "sync/atomic"
"github.com/mattn/go-runewidth"
@@ -43,20 +45,24 @@ type MessageView struct {
DateFormat string
TimestampFormat string
TimestampWidth int
- LoadingMessages bool
- widestSender int
- width int
- height int
- prevWidth int
- prevHeight int
- prevMsgCount int
- prevPrefs config.UserPreferences
-
- messageIDs map[string]messages.UIMessage
- messages []messages.UIMessage
-
- msgBuffer []messages.UIMessage
+ // Used for locking
+ loadingMessages int32
+
+ _widestSender uint32
+ _width uint32
+ _height uint32
+ _prevWidth uint32
+ _prevHeight uint32
+ prevMsgCount int
+ prevPrefs config.UserPreferences
+
+ messageIDLock sync.RWMutex
+ messageIDs map[string]messages.UIMessage
+ messagesLock sync.RWMutex
+ messages []messages.UIMessage
+ msgBufferLock sync.RWMutex
+ msgBuffer []messages.UIMessage
}
func NewMessageView(parent *RoomView) *MessageView {
@@ -72,19 +78,20 @@ func NewMessageView(parent *RoomView) *MessageView {
messageIDs: make(map[string]messages.UIMessage),
msgBuffer: make([]messages.UIMessage, 0),
- width: 80,
- widestSender: 5,
- prevWidth: -1,
- prevHeight: -1,
- prevMsgCount: -1,
+ _width: 80,
+ _widestSender: 5,
+ _prevWidth: 0,
+ _prevHeight: 0,
+ prevMsgCount: -1,
}
}
func (view *MessageView) updateWidestSender(sender string) {
- if len(sender) > view.widestSender {
- view.widestSender = len(sender)
- if view.widestSender > view.MaxSenderWidth {
- view.widestSender = view.MaxSenderWidth
+ if len(sender) > int(view._widestSender) {
+ if len(sender) > view.MaxSenderWidth {
+ atomic.StoreUint32(&view._widestSender, uint32(view.MaxSenderWidth))
+ } else {
+ atomic.StoreUint32(&view._widestSender, uint32(len(sender)))
}
}
}
@@ -109,22 +116,21 @@ func (view *MessageView) AddMessage(ifcMessage ifc.Message, direction MessageDir
}
var oldMsg messages.UIMessage
- var messageExists bool
- if oldMsg, messageExists = view.messageIDs[message.ID()]; messageExists {
+ if oldMsg = view.getMessageByID(message.ID()); oldMsg != nil {
view.replaceMessage(oldMsg, message)
direction = IgnoreMessage
- } else if oldMsg, messageExists = view.messageIDs[message.TxnID()]; messageExists {
+ } else if oldMsg = view.getMessageByID(message.TxnID()); oldMsg != nil {
view.replaceMessage(oldMsg, message)
- delete(view.messageIDs, message.TxnID())
+ view.deleteMessageID(message.TxnID())
direction = IgnoreMessage
}
view.updateWidestSender(message.Sender())
- width := view.width
+ width := view.width()
bare := view.config.Preferences.BareMessageView
if !bare {
- width -= view.TimestampWidth + TimestampSenderGap + view.widestSender + SenderMessageGap
+ width -= view.TimestampWidth + TimestampSenderGap + view.widestSender() + SenderMessageGap
}
message.CalculateBuffer(view.config.Preferences, width)
@@ -140,18 +146,22 @@ func (view *MessageView) AddMessage(ifcMessage ifc.Message, direction MessageDir
if view.ScrollOffset > 0 {
view.ScrollOffset += message.Height()
}
+ view.messagesLock.Lock()
if len(view.messages) > 0 && !view.messages[len(view.messages)-1].SameDate(message) {
view.messages = append(view.messages, makeDateChange(), message)
} else {
view.messages = append(view.messages, message)
}
+ view.messagesLock.Unlock()
view.appendBuffer(message)
} else if direction == PrependMessage {
+ view.messagesLock.Lock()
if len(view.messages) > 0 && !view.messages[0].SameDate(message) {
view.messages = append([]messages.UIMessage{message, makeDateChange()}, view.messages...)
} else {
view.messages = append([]messages.UIMessage{message}, view.messages...)
}
+ view.messagesLock.Unlock()
} else if oldMsg != nil {
view.replaceBuffer(oldMsg, message)
} else {
@@ -160,31 +170,62 @@ func (view *MessageView) AddMessage(ifcMessage ifc.Message, direction MessageDir
}
if len(message.ID()) > 0 {
- view.messageIDs[message.ID()] = message
- }
-}
-
-func (view *MessageView) appendBuffer(message messages.UIMessage) {
- for i := 0; i < message.Height(); i++ {
- view.msgBuffer = append(view.msgBuffer, message)
+ view.setMessageID(message)
}
- view.prevMsgCount++
}
func (view *MessageView) replaceMessage(original messages.UIMessage, new messages.UIMessage) {
if len(new.ID()) > 0 {
- view.messageIDs[new.ID()] = new
+ view.setMessageID(new)
}
+ view.messagesLock.Lock()
for index, msg := range view.messages {
if msg == original {
view.messages[index] = new
}
}
+ view.messagesLock.Unlock()
+}
+
+func (view *MessageView) getMessageByID(id string) messages.UIMessage {
+ view.messageIDLock.RLock()
+ defer view.messageIDLock.RUnlock()
+ msg, ok := view.messageIDs[id]
+ if !ok {
+ return nil
+ }
+ return msg
+}
+
+func (view *MessageView) deleteMessageID(id string) {
+ view.messageIDLock.Lock()
+ delete(view.messageIDs, id)
+ view.messageIDLock.Unlock()
+}
+
+func (view *MessageView) setMessageID(message messages.UIMessage) {
+ view.messageIDLock.Lock()
+ view.messageIDs[message.ID()] = message
+ view.messageIDLock.Unlock()
+}
+
+func (view *MessageView) appendBuffer(message messages.UIMessage) {
+ view.msgBufferLock.Lock()
+ view.appendBufferUnlocked(message)
+ view.msgBufferLock.Unlock()
+}
+
+func (view *MessageView) appendBufferUnlocked(message messages.UIMessage) {
+ for i := 0; i < message.Height(); i++ {
+ view.msgBuffer = append(view.msgBuffer, message)
+ }
+ view.prevMsgCount++
}
func (view *MessageView) replaceBuffer(original messages.UIMessage, new messages.UIMessage) {
start := -1
end := -1
+ view.msgBufferLock.RLock()
for index, meta := range view.msgBuffer {
if meta == original {
if start == -1 {
@@ -195,6 +236,7 @@ func (view *MessageView) replaceBuffer(original messages.UIMessage, new messages
break
}
}
+ view.msgBufferLock.RUnlock()
if start == -1 {
debug.Print("Called replaceBuffer() with message that was not in the buffer:", original)
@@ -208,9 +250,10 @@ func (view *MessageView) replaceBuffer(original messages.UIMessage, new messages
}
if new.Height() == 0 {
- new.CalculateBuffer(view.prevPrefs, view.prevWidth)
+ new.CalculateBuffer(view.prevPrefs, view.prevWidth())
}
+ view.msgBufferLock.Lock()
if new.Height() != end-start {
metaBuffer := view.msgBuffer[0:start]
for i := 0; i < new.Height(); i++ {
@@ -222,17 +265,20 @@ func (view *MessageView) replaceBuffer(original messages.UIMessage, new messages
view.msgBuffer[i] = new
}
}
+ view.msgBufferLock.Unlock()
}
func (view *MessageView) recalculateBuffers() {
prefs := view.config.Preferences
- recalculateMessageBuffers := view.width != view.prevWidth ||
+ recalculateMessageBuffers := view.width() != view.prevWidth() ||
view.prevPrefs.BareMessageView != prefs.BareMessageView ||
view.prevPrefs.DisableImages != prefs.DisableImages
+ view.messagesLock.RLock()
+ view.msgBufferLock.Lock()
if recalculateMessageBuffers || len(view.messages) != view.prevMsgCount {
- width := view.width
+ width := view.width()
if !prefs.BareMessageView {
- width -= view.TimestampWidth + TimestampSenderGap + view.widestSender + SenderMessageGap
+ width -= view.TimestampWidth + TimestampSenderGap + view.widestSender() + SenderMessageGap
}
view.msgBuffer = []messages.UIMessage{}
view.prevMsgCount = 0
@@ -244,11 +290,12 @@ func (view *MessageView) recalculateBuffers() {
if recalculateMessageBuffers {
message.CalculateBuffer(prefs, width)
}
- view.appendBuffer(message)
+ view.appendBufferUnlocked(message)
}
}
- view.prevHeight = view.height
- view.prevWidth = view.width
+ view.msgBufferLock.Unlock()
+ view.messagesLock.RUnlock()
+ view.updatePrevSize()
view.prevPrefs = prefs
}
@@ -309,19 +356,21 @@ func (view *MessageView) OnMouseEvent(event mauview.MouseEvent) bool {
return true
case tcell.Button1:
x, y := event.Position()
- line := view.TotalHeight() - view.ScrollOffset - view.height + y
+ line := view.TotalHeight() - view.ScrollOffset - view.Height() + y
if line < 0 || line >= view.TotalHeight() {
return false
}
+ view.msgBufferLock.RLock()
message := view.msgBuffer[line]
var prevMessage messages.UIMessage
if y != 0 && line > 0 {
prevMessage = view.msgBuffer[line-1]
}
+ view.msgBufferLock.RUnlock()
usernameX := view.TimestampWidth + TimestampSenderGap
- messageX := usernameX + view.widestSender + SenderMessageGap
+ messageX := usernameX + view.widestSender() + SenderMessageGap
if x >= messageX {
return view.handleMessageClick(message)
@@ -336,30 +385,59 @@ const PaddingAtTop = 5
func (view *MessageView) AddScrollOffset(diff int) {
totalHeight := view.TotalHeight()
- if diff >= 0 && view.ScrollOffset+diff >= totalHeight-view.height+PaddingAtTop {
- view.ScrollOffset = totalHeight - view.height + PaddingAtTop
+ height := view.Height()
+ if diff >= 0 && view.ScrollOffset+diff >= totalHeight-height+PaddingAtTop {
+ view.ScrollOffset = totalHeight - height + PaddingAtTop
} else {
view.ScrollOffset += diff
}
- if view.ScrollOffset > totalHeight-view.height+PaddingAtTop {
- view.ScrollOffset = totalHeight - view.height + PaddingAtTop
+ if view.ScrollOffset > totalHeight-height+PaddingAtTop {
+ view.ScrollOffset = totalHeight - height + PaddingAtTop
}
if view.ScrollOffset < 0 {
view.ScrollOffset = 0
}
}
+func (view *MessageView) setSize(width, height int) {
+ atomic.StoreUint32(&view._width, uint32(width))
+ atomic.StoreUint32(&view._height, uint32(height))
+}
+
+func (view *MessageView) updatePrevSize() {
+ atomic.StoreUint32(&view._prevWidth, atomic.LoadUint32(&view._width))
+ atomic.StoreUint32(&view._prevHeight, atomic.LoadUint32(&view._height))
+}
+
+func (view *MessageView) prevHeight() int {
+ return int(atomic.LoadUint32(&view._prevHeight))
+}
+
+func (view *MessageView) prevWidth() int {
+ return int(atomic.LoadUint32(&view._prevWidth))
+}
+
+func (view *MessageView) widestSender() int {
+ return int(atomic.LoadUint32(&view._widestSender))
+}
+
func (view *MessageView) Height() int {
- return view.height
+ return int(atomic.LoadUint32(&view._height))
+}
+
+func (view *MessageView) width() int {
+ return int(atomic.LoadUint32(&view._width))
}
func (view *MessageView) TotalHeight() int {
+ view.msgBufferLock.RLock()
+ defer view.msgBufferLock.RUnlock()
return len(view.msgBuffer)
}
func (view *MessageView) IsAtTop() bool {
- return view.ScrollOffset >= len(view.msgBuffer)-view.height+PaddingAtTop
+ return view.ScrollOffset >= view.TotalHeight()-view.Height()+PaddingAtTop
}
const (
@@ -407,7 +485,7 @@ func (view *MessageView) getIndexOffset(screen mauview.Screen, height, messageX
indexOffset = view.TotalHeight() - view.ScrollOffset - height
if indexOffset <= -PaddingAtTop {
message := "Scroll up to load more messages."
- if view.LoadingMessages {
+ if atomic.LoadInt32(&view.loadingMessages) == 1 {
message = "Loading more messages..."
}
widget.WriteLineSimpleColor(screen, message, messageX, 0, tcell.ColorGreen)
@@ -419,6 +497,7 @@ func (view *MessageView) CapturePlaintext(height int) string {
var buf strings.Builder
indexOffset := view.TotalHeight() - view.ScrollOffset - height
var prevMessage messages.UIMessage
+ view.msgBufferLock.RLock()
for line := 0; line < height; line++ {
index := indexOffset + line
if index < 0 {
@@ -438,27 +517,29 @@ func (view *MessageView) CapturePlaintext(height int) string {
prevMessage = message
}
}
+ view.msgBufferLock.RUnlock()
return buf.String()
}
func (view *MessageView) Draw(screen mauview.Screen) {
- view.width, view.height = screen.Size()
+ view.setSize(screen.Size())
view.recalculateBuffers()
+ height := view.Height()
if view.TotalHeight() == 0 {
- widget.WriteLineSimple(screen, "It's quite empty in here.", 0, view.height)
+ widget.WriteLineSimple(screen, "It's quite empty in here.", 0, height)
return
}
usernameX := view.TimestampWidth + TimestampSenderGap
- messageX := usernameX + view.widestSender + SenderMessageGap
+ messageX := usernameX + view.widestSender() + SenderMessageGap
bareMode := view.config.Preferences.BareMessageView
if bareMode {
messageX = 0
}
- indexOffset := view.getIndexOffset(screen, view.height, messageX)
+ indexOffset := view.getIndexOffset(screen, height, messageX)
viewStart := 0
if indexOffset < 0 {
@@ -466,13 +547,13 @@ func (view *MessageView) Draw(screen mauview.Screen) {
}
if !bareMode {
- separatorX := usernameX + view.widestSender + SenderSeparatorGap
- scrollBarHeight, scrollBarPos := view.calculateScrollBar(view.height)
+ separatorX := usernameX + view.widestSender() + SenderSeparatorGap
+ scrollBarHeight, scrollBarPos := view.calculateScrollBar(height)
- for line := viewStart; line < view.height; line++ {
+ for line := viewStart; line < height; line++ {
showScrollbar := line-viewStart >= scrollBarPos-scrollBarHeight && line-viewStart < scrollBarPos
- isTop := line == viewStart && view.ScrollOffset+view.height >= view.TotalHeight()
- isBottom := line == view.height-1 && view.ScrollOffset == 0
+ isTop := line == viewStart && view.ScrollOffset+height >= view.TotalHeight()
+ isBottom := line == height-1 && view.ScrollOffset == 0
borderChar, borderStyle := getScrollbarStyle(showScrollbar, isTop, isBottom)
@@ -481,7 +562,8 @@ func (view *MessageView) Draw(screen mauview.Screen) {
}
var prevMsg messages.UIMessage
- for line := viewStart; line < view.height && indexOffset+line < view.TotalHeight(); line++ {
+ view.msgBufferLock.RLock()
+ for line := viewStart; line < height && indexOffset+line < len(view.msgBuffer); line++ {
index := indexOffset + line
msg := view.msgBuffer[index]
@@ -493,7 +575,7 @@ func (view *MessageView) Draw(screen mauview.Screen) {
//if !bareMode && (prevMsg == nil || meta.Sender() != prevMsg.Sender()) {
widget.WriteLineColor(
screen, mauview.AlignRight, msg.Sender(),
- usernameX, line, view.widestSender,
+ usernameX, line, view.widestSender(),
msg.SenderColor())
//}
prevMsg = msg
@@ -502,7 +584,8 @@ func (view *MessageView) Draw(screen mauview.Screen) {
for i := index - 1; i >= 0 && view.msgBuffer[i] == msg; i-- {
line--
}
- msg.Draw(mauview.NewProxyScreen(screen, messageX, line, view.width-messageX, msg.Height()))
+ msg.Draw(mauview.NewProxyScreen(screen, messageX, line, view.width()-messageX, msg.Height()))
line += msg.Height() - 1
}
+ view.msgBufferLock.RUnlock()
}
diff --git a/ui/room-list.go b/ui/room-list.go
index c8e43a6..47ac182 100644
--- a/ui/room-list.go
+++ b/ui/room-list.go
@@ -20,6 +20,7 @@ import (
"math"
"regexp"
"strings"
+ "sync"
"maunium.net/go/mauview"
"maunium.net/go/tcell"
@@ -29,6 +30,8 @@ import (
)
type RoomList struct {
+ sync.RWMutex
+
parent *MainView
// The list of tags in display order.
@@ -71,6 +74,8 @@ func NewRoomList(parent *MainView) *RoomList {
}
func (list *RoomList) Contains(roomID string) bool {
+ list.RLock()
+ defer list.RUnlock()
for _, trl := range list.items {
for _, room := range trl.All() {
if room.ID == roomID {
@@ -88,8 +93,8 @@ func (list *RoomList) Add(room *rooms.Room) {
}
}
-func (list *RoomList) CheckTag(tag string) {
- index := list.IndexTag(tag)
+func (list *RoomList) checkTag(tag string) {
+ index := list.indexTag(tag)
trl, ok := list.items[tag]
@@ -107,6 +112,8 @@ func (list *RoomList) CheckTag(tag string) {
}
func (list *RoomList) AddToTag(tag rooms.RoomTag, room *rooms.Room) {
+ list.Lock()
+ defer list.Unlock()
trl, ok := list.items[tag.Tag]
if !ok {
list.items[tag.Tag] = NewTagRoomList(list, tag.Tag, NewDefaultOrderedRoom(room))
@@ -114,7 +121,7 @@ func (list *RoomList) AddToTag(tag rooms.RoomTag, room *rooms.Room) {
}
trl.Insert(tag.Order, room)
- list.CheckTag(tag.Tag)
+ list.checkTag(tag.Tag)
}
func (list *RoomList) Remove(room *rooms.Room) {
@@ -124,6 +131,8 @@ func (list *RoomList) Remove(room *rooms.Room) {
}
func (list *RoomList) RemoveFromTag(tag string, room *rooms.Room) {
+ list.Lock()
+ defer list.Unlock()
trl, ok := list.items[tag]
if !ok {
return
@@ -158,10 +167,12 @@ func (list *RoomList) RemoveFromTag(tag string, room *rooms.Room) {
list.selectedTag = ""
}
}
- list.CheckTag(tag)
+ list.checkTag(tag)
}
func (list *RoomList) Bump(room *rooms.Room) {
+ list.RLock()
+ defer list.RUnlock()
for _, tag := range room.Tags() {
trl, ok := list.items[tag.Tag]
if !ok {
@@ -172,6 +183,8 @@ func (list *RoomList) Bump(room *rooms.Room) {
}
func (list *RoomList) Clear() {
+ list.Lock()
+ defer list.Unlock()
list.items = make(map[string]*TagRoomList)
list.tags = []string{"m.favourite", "net.maunium.gomuks.fake.direct", "", "m.lowpriority"}
for _, tag := range list.tags {
@@ -220,6 +233,8 @@ func (list *RoomList) AddScrollOffset(offset int) {
}
func (list *RoomList) First() (string, *rooms.Room) {
+ list.RLock()
+ defer list.RUnlock()
for _, tag := range list.tags {
trl := list.items[tag]
if trl.HasVisibleRooms() {
@@ -230,6 +245,8 @@ func (list *RoomList) First() (string, *rooms.Room) {
}
func (list *RoomList) Last() (string, *rooms.Room) {
+ list.RLock()
+ defer list.RUnlock()
for tagIndex := len(list.tags) - 1; tagIndex >= 0; tagIndex-- {
tag := list.tags[tagIndex]
trl := list.items[tag]
@@ -240,7 +257,7 @@ func (list *RoomList) Last() (string, *rooms.Room) {
return "", nil
}
-func (list *RoomList) IndexTag(tag string) int {
+func (list *RoomList) indexTag(tag string) int {
for index, entry := range list.tags {
if tag == entry {
return index
@@ -250,6 +267,8 @@ func (list *RoomList) IndexTag(tag string) int {
}
func (list *RoomList) Previous() (string, *rooms.Room) {
+ list.RLock()
+ defer list.RUnlock()
if len(list.items) == 0 {
return "", nil
} else if list.selected == nil {
@@ -266,7 +285,7 @@ func (list *RoomList) Previous() (string, *rooms.Room) {
}
if index == trl.Length()-1 {
- tagIndex := list.IndexTag(list.selectedTag)
+ tagIndex := list.indexTag(list.selectedTag)
tagIndex--
for ; tagIndex >= 0; tagIndex-- {
prevTag := list.tags[tagIndex]
@@ -283,6 +302,8 @@ func (list *RoomList) Previous() (string, *rooms.Room) {
}
func (list *RoomList) Next() (string, *rooms.Room) {
+ list.RLock()
+ defer list.RUnlock()
if len(list.items) == 0 {
return "", nil
} else if list.selected == nil {
@@ -299,7 +320,7 @@ func (list *RoomList) Next() (string, *rooms.Room) {
}
if index == 0 {
- tagIndex := list.IndexTag(list.selectedTag)
+ tagIndex := list.indexTag(list.selectedTag)
tagIndex++
for ; tagIndex < len(list.tags); tagIndex++ {
nextTag := list.tags[tagIndex]
@@ -325,6 +346,8 @@ func (list *RoomList) Next() (string, *rooms.Room) {
//
// TODO: Sorting. Now just finds first room with new messages.
func (list *RoomList) NextWithActivity() (string, *rooms.Room) {
+ list.RLock()
+ defer list.RUnlock()
for tag, trl := range list.items {
for _, room := range trl.All() {
if room.HasNewMessages() {
@@ -337,7 +360,7 @@ func (list *RoomList) NextWithActivity() (string, *rooms.Room) {
}
func (list *RoomList) index(tag string, room *rooms.Room) int {
- tagIndex := list.IndexTag(tag)
+ tagIndex := list.indexTag(tag)
if tagIndex == -1 {
return -1
}
@@ -358,6 +381,7 @@ func (list *RoomList) index(tag string, room *rooms.Room) int {
if tagIndex > 0 {
for i := 0; i < tagIndex; i++ {
prevTag := list.tags[i]
+
prevTRL := list.items[prevTag]
localIndex += prevTRL.RenderHeight()
}
@@ -367,9 +391,11 @@ func (list *RoomList) index(tag string, room *rooms.Room) int {
}
func (list *RoomList) ContentHeight() (height int) {
+ list.RLock()
for _, tag := range list.tags {
height += list.items[tag].RenderHeight()
}
+ list.RUnlock()
return
}
@@ -410,6 +436,8 @@ func (list *RoomList) clickRoom(line, column int, mod bool) bool {
if line < 0 {
return false
}
+ list.RLock()
+ defer list.RUnlock()
for _, tag := range list.tags {
trl := list.items[tag]
if line--; line == -1 {
@@ -484,6 +512,7 @@ func (list *RoomList) Draw(screen mauview.Screen) {
y -= list.scrollOffset
// Draw the list items.
+ list.RLock()
for _, tag := range list.tags {
trl := list.items[tag]
tagDisplayName := list.GetTagDisplayName(tag)
@@ -501,4 +530,5 @@ func (list *RoomList) Draw(screen mauview.Screen) {
break
}
}
+ list.RUnlock()
}
diff --git a/ui/tag-room-list.go b/ui/tag-room-list.go
index c965f10..dfc7cb2 100644
--- a/ui/tag-room-list.go
+++ b/ui/tag-room-list.go
@@ -277,7 +277,7 @@ func (trl *TagRoomList) Draw(screen mauview.Screen) {
screen.SetCell(width-1, 0, tcell.StyleDefault, '▼')
y := 1
- for i := trl.Length() - 1; i >= 0; i-- {
+ for i := len(items) - 1; i >= 0; i-- {
if y >= height {
return
}
diff --git a/ui/view-main.go b/ui/view-main.go
index bda53ad..2d2afdf 100644
--- a/ui/view-main.go
+++ b/ui/view-main.go
@@ -20,6 +20,8 @@ import (
"bufio"
"fmt"
"os"
+ "sync"
+ "sync/atomic"
"time"
"unicode"
@@ -42,6 +44,7 @@ type MainView struct {
roomView *mauview.Box
currentRoom *RoomView
rooms map[string]*RoomView
+ roomsLock sync.RWMutex
cmdProcessor *CommandProcessor
focused mauview.Focusable
@@ -245,13 +248,17 @@ func (view *MainView) Blur() {
}
func (view *MainView) SwitchRoom(tag string, room *rooms.Room) {
+ view.switchRoom(tag, room, true)
+}
+
+func (view *MainView) switchRoom(tag string, room *rooms.Room, lock bool) {
if room == nil {
return
}
- roomView := view.rooms[room.ID]
- if roomView == nil {
- debug.Print("Tried to switch to non-nil room with nil roomView!")
+ roomView, ok := view.getRoomView(room.ID, lock)
+ if !ok {
+ debug.Print("Tried to switch to room with nonexistent roomView!")
debug.Print(tag, room)
return
}
@@ -265,7 +272,7 @@ func (view *MainView) SwitchRoom(tag string, room *rooms.Room) {
}
}
-func (view *MainView) addRoomPage(room *rooms.Room) {
+func (view *MainView) addRoomPage(room *rooms.Room) *RoomView {
if _, ok := view.rooms[room.ID]; !ok {
roomView := NewRoomView(view, room).
SetInputChangedFunc(view.InputChanged)
@@ -276,53 +283,73 @@ func (view *MainView) addRoomPage(room *rooms.Room) {
if len(roomView.MessageView().messages) == 0 {
go view.LoadHistory(room.ID)
}
+ return roomView
}
+ return nil
}
func (view *MainView) GetRoom(roomID string) ifc.RoomView {
- room, ok := view.rooms[roomID]
+ room, ok := view.getRoomView(roomID, true)
if !ok {
- view.AddRoom(view.matrix.GetRoom(roomID))
- room, ok := view.rooms[roomID]
- if !ok {
- return nil
- }
- return room
+ return view.addRoom(view.matrix.GetRoom(roomID))
}
return room
}
-func (view *MainView) AddRoom(room *rooms.Room) {
- if view.roomList.Contains(room.ID) {
- debug.Print("Add aborted (room exists)", room.ID, room.GetTitle())
- return
- }
- debug.Print("Adding", room.ID, room.GetTitle())
- view.roomList.Add(room)
- view.addRoomPage(room)
- if !view.roomList.HasSelected() {
- view.SwitchRoom(view.roomList.First())
+func (view *MainView) getRoomView(roomID string, lock bool) (room *RoomView, ok bool) {
+ if lock {
+ view.roomsLock.RLock()
+ room, ok = view.rooms[roomID]
+ view.roomsLock.RUnlock()
+ } else {
+ room, ok = view.rooms[roomID]
}
+ return room, ok
+}
+
+func (view *MainView) AddRoom(room *rooms.Room) {
+ view.addRoom(room)
}
func (view *MainView) RemoveRoom(room *rooms.Room) {
- roomView := view.GetRoom(room.ID)
- if roomView == nil {
+ view.roomsLock.Lock()
+ _, ok := view.getRoomView(room.ID, false)
+ if !ok {
+ view.roomsLock.Unlock()
debug.Print("Remove aborted (not found)", room.ID, room.GetTitle())
return
}
debug.Print("Removing", room.ID, room.GetTitle())
view.roomList.Remove(room)
- view.SwitchRoom(view.roomList.Selected())
-
+ t, r := view.roomList.Selected()
+ view.switchRoom(t, r, false)
delete(view.rooms, room.ID)
+ view.roomsLock.Unlock()
view.parent.Render()
}
+func (view *MainView) addRoom(room *rooms.Room) *RoomView {
+ if view.roomList.Contains(room.ID) {
+ debug.Print("Add aborted (room exists)", room.ID, room.GetTitle())
+ return nil
+ }
+ debug.Print("Adding", room.ID, room.GetTitle())
+ view.roomList.Add(room)
+ view.roomsLock.Lock()
+ roomView := view.addRoomPage(room)
+ if !view.roomList.HasSelected() {
+ t, r := view.roomList.First()
+ view.switchRoom(t, r, false)
+ }
+ view.roomsLock.Unlock()
+ return roomView
+}
+
func (view *MainView) SetRooms(rooms map[string]*rooms.Room) {
view.roomList.Clear()
+ view.roomsLock.Lock()
view.rooms = make(map[string]*RoomView)
for _, room := range rooms {
if room.HasLeft {
@@ -331,7 +358,9 @@ func (view *MainView) SetRooms(rooms map[string]*rooms.Room) {
view.roomList.Add(room)
view.addRoomPage(room)
}
- view.SwitchRoom(view.roomList.First())
+ t, r := view.roomList.First()
+ view.switchRoom(t, r, false)
+ view.roomsLock.Unlock()
}
func (view *MainView) UpdateTags(room *rooms.Room) {
@@ -342,8 +371,8 @@ func (view *MainView) UpdateTags(room *rooms.Room) {
view.roomList.Add(room)
}
-func (view *MainView) SetTyping(room string, users []string) {
- roomView, ok := view.rooms[room]
+func (view *MainView) SetTyping(roomID string, users []string) {
+ roomView, ok := view.getRoomView(roomID, true)
if ok {
roomView.SetTyping(users)
view.parent.Render()
@@ -392,33 +421,27 @@ func (view *MainView) NotifyMessage(room *rooms.Room, message ifc.Message, shoul
func (view *MainView) InitialSyncDone() {
view.roomList.Clear()
+ view.roomsLock.RLock()
for _, room := range view.rooms {
view.roomList.Add(room.Room)
room.UpdateUserList()
}
+ view.roomsLock.RUnlock()
}
-func (view *MainView) LoadHistory(room string) {
+func (view *MainView) LoadHistory(roomID string) {
defer debug.Recover()
- roomView := view.rooms[room]
+ roomView, ok := view.getRoomView(roomID, true)
+ if !ok {
+ return
+ }
msgView := roomView.MessageView()
- batch := roomView.Room.PrevBatch
- lockTime := time.Now().Unix() + 1
-
- roomView.Room.LockHistory()
- msgView.LoadingMessages = true
- defer func() {
- roomView.Room.UnlockHistory()
- msgView.LoadingMessages = false
- }()
-
- // There's no clean way to try to lock a mutex, so we just check if we still
- // want to continue after we get the lock. This function should always be ran
- // in a goroutine, so the blocking doesn't matter.
- if time.Now().Unix() >= lockTime || batch != roomView.Room.PrevBatch {
+ if !atomic.CompareAndSwapInt32(&msgView.loadingMessages, 0, 1) {
+ // Locked
return
}
+ defer atomic.StoreInt32(&msgView.loadingMessages, 0)
history, err := view.matrix.GetHistory(roomView.Room, 50)
if err != nil {