2021-03-01 06:37:35 +13:00
|
|
|
package cursor
|
|
|
|
|
|
|
|
import (
|
|
|
|
"reflect"
|
|
|
|
"sync"
|
|
|
|
|
|
|
|
"github.com/rs/zerolog"
|
|
|
|
"github.com/rs/zerolog/log"
|
|
|
|
|
|
|
|
"demodesk/neko/internal/types"
|
|
|
|
"demodesk/neko/internal/utils"
|
|
|
|
)
|
|
|
|
|
|
|
|
func NewImage(desktop types.DesktopManager) *ImageCtx {
|
|
|
|
return &ImageCtx{
|
|
|
|
logger: log.With().Str("module", "cursor-image").Logger(),
|
|
|
|
desktop: desktop,
|
|
|
|
listeners: map[uintptr]*func(entry *ImageEntry){},
|
|
|
|
cache: map[uint64]*ImageEntry{},
|
2021-03-19 01:38:27 +13:00
|
|
|
maxSerial: 200, // TODO: Cleanup?
|
2021-03-01 06:37:35 +13:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type ImageCtx struct {
|
|
|
|
logger zerolog.Logger
|
|
|
|
desktop types.DesktopManager
|
|
|
|
emitMu sync.Mutex
|
|
|
|
listeners map[uintptr]*func(entry *ImageEntry)
|
|
|
|
cacheMu sync.Mutex
|
|
|
|
cache map[uint64]*ImageEntry
|
|
|
|
current *ImageEntry
|
2021-03-19 01:38:27 +13:00
|
|
|
maxSerial uint64
|
2021-03-01 06:37:35 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
type ImageEntry struct {
|
|
|
|
Cursor *types.CursorImage
|
|
|
|
Image []byte
|
|
|
|
}
|
|
|
|
|
|
|
|
func (manager *ImageCtx) Start() {
|
|
|
|
manager.desktop.OnCursorChanged(func(serial uint64) {
|
|
|
|
entry, err := manager.GetCached(serial)
|
|
|
|
if err != nil {
|
|
|
|
manager.logger.Warn().Err(err).Msg("failed to get cursor image")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-03-19 01:38:27 +13:00
|
|
|
manager.current = entry
|
2021-03-01 06:37:35 +13:00
|
|
|
for _, emit := range manager.listeners {
|
|
|
|
(*emit)(entry)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func (manager *ImageCtx) Shutdown() {
|
|
|
|
manager.logger.Info().Msgf("shutting down")
|
|
|
|
|
|
|
|
manager.emitMu.Lock()
|
|
|
|
for key := range manager.listeners {
|
|
|
|
delete(manager.listeners, key)
|
|
|
|
}
|
|
|
|
manager.emitMu.Unlock()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (manager *ImageCtx) GetCached(serial uint64) (*ImageEntry, error) {
|
2021-03-01 06:52:37 +13:00
|
|
|
// zero means no serial available
|
2021-03-19 01:38:27 +13:00
|
|
|
if serial == 0 || serial > manager.maxSerial {
|
2021-03-26 00:20:30 +13:00
|
|
|
manager.logger.Debug().Uint64("serial", serial).Msg("cache bypass")
|
2021-03-01 06:52:37 +13:00
|
|
|
return manager.fetchEntry()
|
|
|
|
}
|
|
|
|
|
2021-03-01 06:37:35 +13:00
|
|
|
manager.cacheMu.Lock()
|
|
|
|
entry, ok := manager.cache[serial]
|
|
|
|
manager.cacheMu.Unlock()
|
|
|
|
|
|
|
|
if ok {
|
|
|
|
return entry, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
entry, err := manager.fetchEntry()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
manager.cacheMu.Lock()
|
|
|
|
manager.cache[serial] = entry
|
|
|
|
manager.cacheMu.Unlock()
|
|
|
|
|
2021-03-26 00:20:30 +13:00
|
|
|
manager.logger.Debug().Uint64("serial", serial).Msg("cache miss")
|
2021-03-01 06:37:35 +13:00
|
|
|
return entry, nil
|
|
|
|
}
|
|
|
|
|
2021-03-01 06:50:32 +13:00
|
|
|
func (manager *ImageCtx) Get() (*ImageEntry, error) {
|
2021-03-01 06:37:35 +13:00
|
|
|
if manager.current != nil {
|
|
|
|
return manager.current, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return manager.fetchEntry()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (manager *ImageCtx) AddListener(listener *func(entry *ImageEntry)) {
|
|
|
|
manager.emitMu.Lock()
|
|
|
|
defer manager.emitMu.Unlock()
|
|
|
|
|
|
|
|
if listener != nil {
|
|
|
|
ptr := reflect.ValueOf(listener).Pointer()
|
|
|
|
manager.listeners[ptr] = listener
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (manager *ImageCtx) RemoveListener(listener *func(entry *ImageEntry)) {
|
|
|
|
manager.emitMu.Lock()
|
|
|
|
defer manager.emitMu.Unlock()
|
|
|
|
|
|
|
|
if listener != nil {
|
|
|
|
ptr := reflect.ValueOf(listener).Pointer()
|
|
|
|
delete(manager.listeners, ptr)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (manager *ImageCtx) fetchEntry() (*ImageEntry, error) {
|
|
|
|
cur := manager.desktop.GetCursorImage()
|
|
|
|
|
|
|
|
img, err := utils.CreatePNGImage(cur.Image)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
entry := &ImageEntry{
|
|
|
|
Cursor: cur,
|
|
|
|
Image: img,
|
|
|
|
}
|
|
|
|
|
|
|
|
return entry, nil
|
|
|
|
}
|