Unbreak more things
This commit is contained in:
parent
a55ea42d7f
commit
160b035c4d
1
.gitignore
vendored
1
.gitignore
vendored
@ -7,3 +7,4 @@ gomuks
|
|||||||
coverage.out
|
coverage.out
|
||||||
coverage.html
|
coverage.html
|
||||||
deb/usr
|
deb/usr
|
||||||
|
*.prof
|
||||||
|
@ -58,8 +58,6 @@ func NewGomuks(uiProvider ifc.UIProvider, configDir, cacheDir string) *Gomuks {
|
|||||||
// Save saves the active session and message history.
|
// Save saves the active session and message history.
|
||||||
func (gmx *Gomuks) Save() {
|
func (gmx *Gomuks) Save() {
|
||||||
gmx.config.SaveAll()
|
gmx.config.SaveAll()
|
||||||
//debug.Print("Saving history...")
|
|
||||||
//gmx.ui.MainView().SaveAllHistory()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// StartAutosave calls Save() every minute until it receives a stop signal
|
// StartAutosave calls Save() every minute until it receives a stop signal
|
||||||
@ -70,7 +68,9 @@ func (gmx *Gomuks) StartAutosave() {
|
|||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
|
if gmx.config.AuthCache.InitialSyncDone {
|
||||||
gmx.Save()
|
gmx.Save()
|
||||||
|
}
|
||||||
case val := <-gmx.stop:
|
case val := <-gmx.stop:
|
||||||
if val {
|
if val {
|
||||||
return
|
return
|
||||||
|
@ -221,7 +221,15 @@ func (c *Container) OnLogin() {
|
|||||||
c.syncer.InitDoneCallback = func() {
|
c.syncer.InitDoneCallback = func() {
|
||||||
debug.Print("Initial sync done")
|
debug.Print("Initial sync done")
|
||||||
c.config.AuthCache.InitialSyncDone = true
|
c.config.AuthCache.InitialSyncDone = true
|
||||||
c.config.SaveAuthCache()
|
debug.Print("Updating title caches")
|
||||||
|
for _, room := range c.config.Rooms.Map {
|
||||||
|
room.GetTitle()
|
||||||
|
}
|
||||||
|
debug.Print("Cleaning cached rooms from memory")
|
||||||
|
c.config.Rooms.ForceClean()
|
||||||
|
debug.Print("Saving all data")
|
||||||
|
c.config.SaveAll()
|
||||||
|
debug.Print("Adding rooms to UI")
|
||||||
c.ui.MainView().SetRooms(c.config.Rooms)
|
c.ui.MainView().SetRooms(c.config.Rooms)
|
||||||
c.ui.Render()
|
c.ui.Render()
|
||||||
}
|
}
|
||||||
@ -294,17 +302,21 @@ func (c *Container) SendPreferencesToMatrix() {
|
|||||||
|
|
||||||
// HandleMessage is the event handler for the m.room.message timeline event.
|
// HandleMessage is the event handler for the m.room.message timeline event.
|
||||||
func (c *Container) HandleMessage(source EventSource, evt *mautrix.Event) {
|
func (c *Container) HandleMessage(source EventSource, evt *mautrix.Event) {
|
||||||
if source&EventSourceLeave != 0 || source&EventSourceState != 0 {
|
room := c.GetOrCreateRoom(evt.RoomID)
|
||||||
|
if source&EventSourceLeave != 0 {
|
||||||
|
room.HasLeft = true
|
||||||
|
return
|
||||||
|
} else if source&EventSourceState != 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
room := c.GetOrCreateRoom(evt.RoomID)
|
|
||||||
|
|
||||||
err := c.history.Append(room, []*mautrix.Event{evt})
|
err := c.history.Append(room, []*mautrix.Event{evt})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
debug.Printf("Failed to add event %s to history: %v", evt.ID, err)
|
debug.Printf("Failed to add event %s to history: %v", evt.ID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !c.config.AuthCache.InitialSyncDone {
|
if !c.config.AuthCache.InitialSyncDone || !room.Loaded() {
|
||||||
|
room.LastReceivedMessage = time.Unix(evt.Timestamp/1000, evt.Timestamp%1000*1000)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -327,7 +339,7 @@ func (c *Container) HandleMessage(source EventSource, evt *mautrix.Event) {
|
|||||||
c.ui.Render()
|
c.ui.Render()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
debug.Printf("Parsing event %s type %s %v from %s in %s failed (ParseEvent() returned nil).", evt.ID, evt.Type, evt.Content.Raw, evt.Sender, evt.RoomID)
|
debug.Printf("Parsing event %s type %s %v from %s in %s failed (ParseEvent() returned nil).", evt.ID, evt.Type.String(), evt.Content.Raw, evt.Sender, evt.RoomID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -335,6 +347,9 @@ func (c *Container) HandleMessage(source EventSource, evt *mautrix.Event) {
|
|||||||
func (c *Container) HandleMembership(source EventSource, evt *mautrix.Event) {
|
func (c *Container) HandleMembership(source EventSource, evt *mautrix.Event) {
|
||||||
isLeave := source&EventSourceLeave != 0
|
isLeave := source&EventSourceLeave != 0
|
||||||
isTimeline := source&EventSourceTimeline != 0
|
isTimeline := source&EventSourceTimeline != 0
|
||||||
|
if isLeave {
|
||||||
|
c.GetOrCreateRoom(evt.RoomID).HasLeft = true
|
||||||
|
}
|
||||||
isNonTimelineLeave := isLeave && !isTimeline
|
isNonTimelineLeave := isLeave && !isTimeline
|
||||||
if !c.config.AuthCache.InitialSyncDone && isNonTimelineLeave {
|
if !c.config.AuthCache.InitialSyncDone && isNonTimelineLeave {
|
||||||
return
|
return
|
||||||
@ -437,7 +452,7 @@ func (c *Container) parseDirectChatInfo(evt *mautrix.Event) map[*rooms.Room]bool
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
room := c.GetRoom(roomID)
|
room := c.GetOrCreateRoom(roomID)
|
||||||
if room != nil && !room.HasLeft {
|
if room != nil && !room.HasLeft {
|
||||||
directChats[room] = true
|
directChats[room] = true
|
||||||
}
|
}
|
||||||
@ -473,7 +488,7 @@ func (c *Container) HandlePushRules(source EventSource, evt *mautrix.Event) {
|
|||||||
|
|
||||||
// HandleTag is the event handler for the m.tag account data event.
|
// HandleTag is the event handler for the m.tag account data event.
|
||||||
func (c *Container) HandleTag(source EventSource, evt *mautrix.Event) {
|
func (c *Container) HandleTag(source EventSource, evt *mautrix.Event) {
|
||||||
room := c.config.GetRoom(evt.RoomID)
|
room := c.GetOrCreateRoom(evt.RoomID)
|
||||||
|
|
||||||
newTags := make([]rooms.RoomTag, len(evt.Content.RoomTags))
|
newTags := make([]rooms.RoomTag, len(evt.Content.RoomTags))
|
||||||
index := 0
|
index := 0
|
||||||
|
@ -115,6 +115,12 @@ type Room struct {
|
|||||||
cache *RoomCache
|
cache *RoomCache
|
||||||
// Lock for state and other room stuff.
|
// Lock for state and other room stuff.
|
||||||
lock sync.RWMutex
|
lock sync.RWMutex
|
||||||
|
// Function to call when room state is unloaded.
|
||||||
|
onUnload func() bool
|
||||||
|
// Function to call when room state is loaded.
|
||||||
|
onLoad func() bool
|
||||||
|
// Whether or not the room state has changed
|
||||||
|
changed bool
|
||||||
|
|
||||||
// Room state cache linked list.
|
// Room state cache linked list.
|
||||||
prev *Room
|
prev *Room
|
||||||
@ -133,10 +139,13 @@ func (room *Room) Loaded() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (room *Room) Load() {
|
func (room *Room) Load() {
|
||||||
|
room.cache.TouchNode(room)
|
||||||
if room.Loaded() {
|
if room.Loaded() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
room.cache.TouchNode(room)
|
if room.onLoad != nil && !room.onLoad() {
|
||||||
|
return
|
||||||
|
}
|
||||||
room.lock.Lock()
|
room.lock.Lock()
|
||||||
room.load()
|
room.load()
|
||||||
room.lock.Unlock()
|
room.lock.Unlock()
|
||||||
@ -146,7 +155,7 @@ func (room *Room) load() {
|
|||||||
if room.Loaded() {
|
if room.Loaded() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
debug.Print("Loading state for room", room.ID)
|
debug.Print("Loading state for room", room.ID, "from disk")
|
||||||
room.state = make(map[mautrix.EventType]map[string]*mautrix.Event)
|
room.state = make(map[mautrix.EventType]map[string]*mautrix.Event)
|
||||||
file, err := os.OpenFile(room.path, os.O_RDONLY, 0600)
|
file, err := os.OpenFile(room.path, os.O_RDONLY, 0600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -168,9 +177,17 @@ func (room *Room) load() {
|
|||||||
if err = dec.Decode(&room.state); err != nil {
|
if err = dec.Decode(&room.state); err != nil {
|
||||||
debug.Print("Failed to decode room state:", err)
|
debug.Print("Failed to decode room state:", err)
|
||||||
}
|
}
|
||||||
|
room.changed = false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (room *Room) Unload() {
|
func (room *Room) Touch() {
|
||||||
|
room.cache.TouchNode(room)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (room *Room) Unload() bool {
|
||||||
|
if room.onUnload != nil && !room.onUnload() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
debug.Print("Unloading", room.ID)
|
debug.Print("Unloading", room.ID)
|
||||||
room.Save()
|
room.Save()
|
||||||
room.state = nil
|
room.state = nil
|
||||||
@ -179,14 +196,27 @@ func (room *Room) Unload() {
|
|||||||
room.canonicalAliasCache = ""
|
room.canonicalAliasCache = ""
|
||||||
room.firstMemberCache = nil
|
room.firstMemberCache = nil
|
||||||
room.secondMemberCache = nil
|
room.secondMemberCache = nil
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (room *Room) SetOnUnload(fn func() bool) {
|
||||||
|
room.onUnload = fn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (room *Room) SetOnLoad(fn func() bool) {
|
||||||
|
room.onLoad = fn
|
||||||
}
|
}
|
||||||
|
|
||||||
func (room *Room) Save() {
|
func (room *Room) Save() {
|
||||||
if !room.Loaded() {
|
if !room.Loaded() {
|
||||||
debug.Print("Failed to save room state: room not loaded")
|
debug.Print("Failed to save room", room.ID, "state: room not loaded")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
debug.Print("Saving state for room", room.ID)
|
if !room.changed {
|
||||||
|
debug.Print("Not saving", room.ID, "as state hasn't changed")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
debug.Print("Saving state for room", room.ID, "to disk")
|
||||||
file, err := os.OpenFile(room.path, os.O_WRONLY|os.O_CREATE, 0600)
|
file, err := os.OpenFile(room.path, os.O_WRONLY|os.O_CREATE, 0600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
debug.Print("Failed to open room state file for writing:", err)
|
debug.Print("Failed to open room state file for writing:", err)
|
||||||
@ -298,40 +328,51 @@ func (room *Room) UpdateState(event *mautrix.Event) {
|
|||||||
room.Load()
|
room.Load()
|
||||||
room.lock.Lock()
|
room.lock.Lock()
|
||||||
defer room.lock.Unlock()
|
defer room.lock.Unlock()
|
||||||
|
room.changed = true
|
||||||
_, exists := room.state[event.Type]
|
_, exists := room.state[event.Type]
|
||||||
if !exists {
|
if !exists {
|
||||||
room.state[event.Type] = make(map[string]*mautrix.Event)
|
room.state[event.Type] = make(map[string]*mautrix.Event)
|
||||||
}
|
}
|
||||||
switch event.Type {
|
switch event.Type {
|
||||||
case mautrix.StateRoomName:
|
case mautrix.StateRoomName:
|
||||||
room.NameCache = ""
|
room.NameCache = event.Content.Name
|
||||||
|
room.nameCacheSource = ExplicitRoomName
|
||||||
case mautrix.StateCanonicalAlias:
|
case mautrix.StateCanonicalAlias:
|
||||||
if room.nameCacheSource <= CanonicalAliasRoomName {
|
if room.nameCacheSource <= CanonicalAliasRoomName {
|
||||||
room.NameCache = ""
|
room.NameCache = event.Content.Alias
|
||||||
|
room.nameCacheSource = CanonicalAliasRoomName
|
||||||
}
|
}
|
||||||
room.canonicalAliasCache = ""
|
room.canonicalAliasCache = event.Content.Alias
|
||||||
case mautrix.StateAliases:
|
case mautrix.StateAliases:
|
||||||
if room.nameCacheSource <= AliasRoomName {
|
if room.nameCacheSource <= AliasRoomName {
|
||||||
room.NameCache = ""
|
room.NameCache = ""
|
||||||
}
|
}
|
||||||
room.aliasesCache = nil
|
room.aliasesCache = nil
|
||||||
case mautrix.StateMember:
|
case mautrix.StateMember:
|
||||||
room.memberCache = nil
|
if room.memberCache != nil {
|
||||||
room.firstMemberCache = nil
|
userID := event.GetStateKey()
|
||||||
room.secondMemberCache = nil
|
if event.Content.Membership == mautrix.MembershipLeave || event.Content.Membership == mautrix.MembershipBan {
|
||||||
|
delete(room.memberCache, userID)
|
||||||
|
} else if event.Content.Membership == mautrix.MembershipInvite || event.Content.Membership == mautrix.MembershipJoin {
|
||||||
|
member := room.eventToMember(userID, &event.Content)
|
||||||
|
existingMember, ok := room.memberCache[userID]
|
||||||
|
if ok {
|
||||||
|
*existingMember = *member
|
||||||
|
} else {
|
||||||
|
room.memberCache[userID] = member
|
||||||
|
room.updateNthMemberCache(userID, member)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if room.nameCacheSource <= MemberRoomName {
|
if room.nameCacheSource <= MemberRoomName {
|
||||||
room.NameCache = ""
|
room.NameCache = ""
|
||||||
}
|
}
|
||||||
case mautrix.StateTopic:
|
case mautrix.StateTopic:
|
||||||
room.topicCache = ""
|
room.topicCache = event.Content.Topic
|
||||||
}
|
}
|
||||||
|
|
||||||
stateKey := ""
|
|
||||||
if event.StateKey != nil {
|
|
||||||
stateKey = *event.StateKey
|
|
||||||
}
|
|
||||||
if event.Type != mautrix.StateMember {
|
if event.Type != mautrix.StateMember {
|
||||||
debug.Printf("Updating state %s#%s for %s", event.Type, stateKey, room.ID)
|
debug.Printf("Updating state %s#%s for %s", event.Type.String(), event.GetStateKey(), room.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
if event.StateKey == nil {
|
if event.StateKey == nil {
|
||||||
@ -477,6 +518,25 @@ func (room *Room) GetTitle() string {
|
|||||||
return room.NameCache
|
return room.NameCache
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (room *Room) eventToMember(userID string, content *mautrix.Content) *mautrix.Member {
|
||||||
|
member := &content.Member
|
||||||
|
member.Membership = content.Membership
|
||||||
|
if len(member.Displayname) == 0 {
|
||||||
|
member.Displayname = userID
|
||||||
|
}
|
||||||
|
return member
|
||||||
|
}
|
||||||
|
|
||||||
|
func (room *Room) updateNthMemberCache(userID string, member *mautrix.Member) {
|
||||||
|
if userID != room.SessionUserID {
|
||||||
|
if room.firstMemberCache == nil {
|
||||||
|
room.firstMemberCache = member
|
||||||
|
} else if room.secondMemberCache == nil {
|
||||||
|
room.secondMemberCache = member
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// createMemberCache caches all member events into a easily processable MXID -> *Member map.
|
// createMemberCache caches all member events into a easily processable MXID -> *Member map.
|
||||||
func (room *Room) createMemberCache() map[string]*mautrix.Member {
|
func (room *Room) createMemberCache() map[string]*mautrix.Member {
|
||||||
if len(room.memberCache) > 0 {
|
if len(room.memberCache) > 0 {
|
||||||
@ -489,20 +549,10 @@ func (room *Room) createMemberCache() map[string]*mautrix.Member {
|
|||||||
room.secondMemberCache = nil
|
room.secondMemberCache = nil
|
||||||
if events != nil {
|
if events != nil {
|
||||||
for userID, event := range events {
|
for userID, event := range events {
|
||||||
member := &event.Content.Member
|
member := room.eventToMember(userID, &event.Content)
|
||||||
member.Membership = event.Content.Membership
|
|
||||||
if len(member.Displayname) == 0 {
|
|
||||||
member.Displayname = userID
|
|
||||||
}
|
|
||||||
if userID != room.SessionUserID {
|
|
||||||
if room.firstMemberCache == nil {
|
|
||||||
room.firstMemberCache = member
|
|
||||||
} else if room.secondMemberCache == nil {
|
|
||||||
room.secondMemberCache = member
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if member.Membership == mautrix.MembershipJoin || member.Membership == mautrix.MembershipInvite {
|
if member.Membership == mautrix.MembershipJoin || member.Membership == mautrix.MembershipInvite {
|
||||||
cache[userID] = member
|
cache[userID] = member
|
||||||
|
room.updateNthMemberCache(userID, member)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -106,7 +106,7 @@ func (cache *RoomCache) LoadList() error {
|
|||||||
func (cache *RoomCache) SaveLoadedRooms() {
|
func (cache *RoomCache) SaveLoadedRooms() {
|
||||||
cache.Lock()
|
cache.Lock()
|
||||||
defer cache.Unlock()
|
defer cache.Unlock()
|
||||||
cache.clean()
|
cache.clean(false)
|
||||||
for node := cache.head; node != nil; node = node.prev {
|
for node := cache.head; node != nil; node = node.prev {
|
||||||
node.Save()
|
node.Save()
|
||||||
}
|
}
|
||||||
@ -194,8 +194,7 @@ func (cache *RoomCache) GetOrCreate(roomID string) *Room {
|
|||||||
|
|
||||||
func (cache *RoomCache) get(roomID string) *Room {
|
func (cache *RoomCache) get(roomID string) *Room {
|
||||||
node, ok := cache.Map[roomID]
|
node, ok := cache.Map[roomID]
|
||||||
if ok && node != nil && node.Loaded() {
|
if ok && node != nil {
|
||||||
cache.touch(node)
|
|
||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -273,18 +272,29 @@ func (cache *RoomCache) llPush(node *Room) {
|
|||||||
cache.tail = node
|
cache.tail = node
|
||||||
}
|
}
|
||||||
cache.size++
|
cache.size++
|
||||||
cache.clean()
|
cache.clean(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cache *RoomCache) clean() {
|
func (cache *RoomCache) ForceClean() {
|
||||||
|
cache.Lock()
|
||||||
|
cache.clean(true)
|
||||||
|
cache.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cache *RoomCache) clean(force bool) {
|
||||||
origSize := cache.size
|
origSize := cache.size
|
||||||
maxTS := time.Now().Unix() - cache.maxAge
|
maxTS := time.Now().Unix() - cache.maxAge
|
||||||
for cache.size > cache.maxSize {
|
for cache.size > cache.maxSize {
|
||||||
if cache.tail.touch > maxTS {
|
if cache.tail.touch > maxTS && !force {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
cache.tail.Unload()
|
ok := cache.tail.Unload()
|
||||||
cache.llPop(cache.tail)
|
node := cache.tail
|
||||||
|
cache.llPop(node)
|
||||||
|
if !ok {
|
||||||
|
debug.Print("Unload returned false, pushing node back")
|
||||||
|
cache.llPush(node)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if cleaned := origSize - cache.size; cleaned > 0 {
|
if cleaned := origSize - cache.size; cleaned > 0 {
|
||||||
debug.Print("Cleaned", cleaned, "rooms")
|
debug.Print("Cleaned", cleaned, "rooms")
|
||||||
@ -295,7 +305,11 @@ func (cache *RoomCache) Unload(node *Room) {
|
|||||||
cache.Lock()
|
cache.Lock()
|
||||||
defer cache.Unlock()
|
defer cache.Unlock()
|
||||||
cache.llPop(node)
|
cache.llPop(node)
|
||||||
node.Unload()
|
ok := node.Unload()
|
||||||
|
if !ok {
|
||||||
|
debug.Print("Unload returned false, pushing node back")
|
||||||
|
cache.llPush(node)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cache *RoomCache) newRoom(roomID string) *Room {
|
func (cache *RoomCache) newRoom(roomID string) *Room {
|
||||||
|
@ -108,6 +108,7 @@ func NewCommandProcessor(parent *MainView) *CommandProcessor {
|
|||||||
"rainbow": cmdRainbow,
|
"rainbow": cmdRainbow,
|
||||||
"invite": cmdInvite,
|
"invite": cmdInvite,
|
||||||
"hprof": cmdHeapProfile,
|
"hprof": cmdHeapProfile,
|
||||||
|
"cprof": cmdCPUProfile,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,8 +21,11 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
dbg "runtime/debug"
|
||||||
"runtime/pprof"
|
"runtime/pprof"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
"github.com/lucasb-eyer/go-colorful"
|
"github.com/lucasb-eyer/go-colorful"
|
||||||
@ -71,17 +74,58 @@ var rainbow = GradientTable{
|
|||||||
}
|
}
|
||||||
|
|
||||||
func cmdHeapProfile(cmd *Command) {
|
func cmdHeapProfile(cmd *Command) {
|
||||||
|
dbg.FreeOSMemory()
|
||||||
runtime.GC()
|
runtime.GC()
|
||||||
memProfile, err := os.Create("gomuks.prof")
|
memProfile, err := os.Create("gomuks.heap.prof")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
debug.Print(err)
|
debug.Print("Failed to open gomuks.heap.prof:", err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
defer memProfile.Close()
|
defer func() {
|
||||||
|
err := memProfile.Close()
|
||||||
|
if err != nil {
|
||||||
|
debug.Print("Failed to close gomuks.heap.prof:", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
if err := pprof.WriteHeapProfile(memProfile); err != nil {
|
if err := pprof.WriteHeapProfile(memProfile); err != nil {
|
||||||
debug.Print(err)
|
debug.Print("Heap profile error:", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func cmdCPUProfile(cmd *Command) {
|
||||||
|
if len(cmd.Args) == 0 {
|
||||||
|
cmd.Reply("Usage: /cprof <seconds>")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dur, err := strconv.Atoi(cmd.Args[0])
|
||||||
|
if err != nil || dur < 0 {
|
||||||
|
cmd.Reply("Usage: /cprof <seconds>")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cpuProfile, err := os.Create("gomuks.cpu.prof")
|
||||||
|
if err != nil {
|
||||||
|
debug.Print("Failed to open gomuks.cpu.prof:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = pprof.StartCPUProfile(cpuProfile)
|
||||||
|
if err != nil {
|
||||||
|
_ = cpuProfile.Close()
|
||||||
|
debug.Print("CPU profile error:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cmd.Reply("Started CPU profiling for %d seconds", dur)
|
||||||
|
go func() {
|
||||||
|
time.Sleep(time.Duration(dur) * time.Second)
|
||||||
|
pprof.StopCPUProfile()
|
||||||
|
cmd.Reply("CPU profiling finished.")
|
||||||
|
|
||||||
|
err := cpuProfile.Close()
|
||||||
|
if err != nil {
|
||||||
|
debug.Print("Failed to close gomuks.cpu.prof:", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
// TODO this command definitely belongs in a plugin once we have a plugin system.
|
// TODO this command definitely belongs in a plugin once we have a plugin system.
|
||||||
func cmdRainbow(cmd *Command) {
|
func cmdRainbow(cmd *Command) {
|
||||||
text := strings.Join(cmd.Args, " ")
|
text := strings.Join(cmd.Args, " ")
|
||||||
|
@ -89,6 +89,23 @@ func NewMessageView(parent *RoomView) *MessageView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (view *MessageView) Unload() {
|
||||||
|
debug.Print("Unloading message view", view.parent.Room.ID)
|
||||||
|
view.messagesLock.Lock()
|
||||||
|
view.msgBufferLock.Lock()
|
||||||
|
view.messageIDLock.Lock()
|
||||||
|
view.messageIDs = make(map[string]*messages.UIMessage)
|
||||||
|
view.msgBuffer = make([]*messages.UIMessage, 0)
|
||||||
|
view.messages = make([]*messages.UIMessage, 0)
|
||||||
|
view.initialHistoryLoaded = false
|
||||||
|
view.ScrollOffset = 0
|
||||||
|
view._widestSender = 5
|
||||||
|
view.prevMsgCount = -1
|
||||||
|
view.messagesLock.Unlock()
|
||||||
|
view.msgBufferLock.Unlock()
|
||||||
|
view.messageIDLock.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
func (view *MessageView) updateWidestSender(sender string) {
|
func (view *MessageView) updateWidestSender(sender string) {
|
||||||
if len(sender) > int(view._widestSender) {
|
if len(sender) > int(view._widestSender) {
|
||||||
if len(sender) > view.MaxSenderWidth {
|
if len(sender) > view.MaxSenderWidth {
|
||||||
|
@ -423,8 +423,10 @@ func (list *RoomList) OnMouseEvent(event mauview.MouseEvent) bool {
|
|||||||
switch event.Buttons() {
|
switch event.Buttons() {
|
||||||
case tcell.WheelUp:
|
case tcell.WheelUp:
|
||||||
list.AddScrollOffset(-WheelScrollOffsetDiff)
|
list.AddScrollOffset(-WheelScrollOffsetDiff)
|
||||||
|
return true
|
||||||
case tcell.WheelDown:
|
case tcell.WheelDown:
|
||||||
list.AddScrollOffset(WheelScrollOffsetDiff)
|
list.AddScrollOffset(WheelScrollOffsetDiff)
|
||||||
|
return true
|
||||||
case tcell.Button1:
|
case tcell.Button1:
|
||||||
x, y := event.Position()
|
x, y := event.Position()
|
||||||
return list.clickRoom(y, x, event.Modifiers() == tcell.ModCtrl)
|
return list.clickRoom(y, x, event.Modifiers() == tcell.ModCtrl)
|
||||||
@ -486,7 +488,8 @@ func (list *RoomList) clickRoom(line, column int, mod bool) bool {
|
|||||||
if trl.maxShown < 10 {
|
if trl.maxShown < 10 {
|
||||||
trl.maxShown = 10
|
trl.maxShown = 10
|
||||||
}
|
}
|
||||||
break
|
list.RUnlock()
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Tag footer
|
// Tag footer
|
||||||
|
@ -93,6 +93,17 @@ func NewRoomView(parent *MainView, room *rooms.Room) *RoomView {
|
|||||||
config: parent.config,
|
config: parent.config,
|
||||||
}
|
}
|
||||||
view.content = NewMessageView(view)
|
view.content = NewMessageView(view)
|
||||||
|
view.Room.SetOnUnload(func() bool {
|
||||||
|
if view.parent.currentRoom == view {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
view.content.Unload()
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
view.Room.SetOnLoad(func() bool {
|
||||||
|
view.loadTyping()
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
view.input.
|
view.input.
|
||||||
SetBackgroundColor(tcell.ColorDefault).
|
SetBackgroundColor(tcell.ColorDefault).
|
||||||
@ -270,14 +281,20 @@ func (view *RoomView) SetCompletions(completions []string) {
|
|||||||
view.completions.time = time.Now()
|
view.completions.time = time.Now()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (view *RoomView) SetTyping(users []string) {
|
func (view *RoomView) loadTyping() {
|
||||||
for index, user := range users {
|
for index, user := range view.typing {
|
||||||
member := view.Room.GetMember(user)
|
member := view.Room.GetMember(user)
|
||||||
if member != nil {
|
if member != nil {
|
||||||
users[index] = member.Displayname
|
view.typing[index] = member.Displayname
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (view *RoomView) SetTyping(users []string) {
|
||||||
view.typing = users
|
view.typing = users
|
||||||
|
if view.Room.Loaded() {
|
||||||
|
view.loadTyping()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type completion struct {
|
type completion struct {
|
||||||
|
Loading…
Reference in New Issue
Block a user