neko/internal/webrtc/cursor/image.go

169 lines
3.6 KiB
Go
Raw Normal View History

package cursor
import (
"reflect"
"sync"
"github.com/rs/zerolog"
"github.com/demodesk/neko/pkg/types"
"github.com/demodesk/neko/pkg/utils"
)
2023-04-17 10:42:29 +12:00
type ImageListener interface {
SendCursorImage(cur *types.CursorImage, img []byte) error
}
type Image interface {
Start()
Shutdown()
GetCurrent() (cur *types.CursorImage, img []byte, err error)
AddListener(listener ImageListener)
RemoveListener(listener ImageListener)
}
type imageEntry struct {
*types.CursorImage
ImagePNG []byte
}
2023-04-17 10:42:29 +12:00
type image struct {
2022-02-15 06:41:47 +13:00
logger zerolog.Logger
desktop types.DesktopManager
2023-04-17 10:42:29 +12:00
listeners map[uintptr]ImageListener
listenersMu sync.RWMutex
2022-02-15 06:41:47 +13:00
2023-04-17 10:42:29 +12:00
cache map[uint64]*imageEntry
cacheMu sync.RWMutex
current *imageEntry
maxSerial uint64
}
2023-04-17 10:42:29 +12:00
func NewImage(logger zerolog.Logger, desktop types.DesktopManager) *image {
return &image{
logger: logger.With().Str("submodule", "cursor-image").Logger(),
desktop: desktop,
listeners: map[uintptr]ImageListener{},
cache: map[uint64]*imageEntry{},
maxSerial: 300, // TODO: Cleanup?
}
}
2023-04-17 10:42:29 +12:00
func (manager *image) Start() {
manager.desktop.OnCursorChanged(func(serial uint64) {
2023-04-17 10:42:29 +12:00
entry, err := manager.getCached(serial)
if err != nil {
2021-09-02 09:10:06 +12:00
manager.logger.Err(err).Msg("failed to get cursor image")
return
}
manager.current = entry
2022-02-15 06:41:47 +13:00
2023-04-17 10:42:29 +12:00
manager.listenersMu.RLock()
for _, l := range manager.listeners {
if err := l.SendCursorImage(entry.CursorImage, entry.ImagePNG); err != nil {
manager.logger.Err(err).Msg("failed to set cursor image")
}
}
2023-04-17 10:42:29 +12:00
manager.listenersMu.RUnlock()
})
2021-08-30 04:59:46 +12:00
manager.logger.Info().Msg("starting")
}
2023-04-17 10:42:29 +12:00
func (manager *image) Shutdown() {
2021-08-30 04:59:46 +12:00
manager.logger.Info().Msg("shutdown")
2022-02-15 06:41:47 +13:00
manager.listenersMu.Lock()
for key := range manager.listeners {
delete(manager.listeners, key)
}
2022-02-15 06:41:47 +13:00
manager.listenersMu.Unlock()
}
2023-04-17 10:42:29 +12:00
func (manager *image) getCached(serial uint64) (*imageEntry, error) {
2021-03-01 06:52:37 +13:00
// zero means no serial available
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()
}
2023-04-17 10:42:29 +12:00
manager.cacheMu.RLock()
entry, ok := manager.cache[serial]
2023-04-17 10:42:29 +12:00
manager.cacheMu.RUnlock()
if ok {
return entry, nil
}
2023-04-17 10:42:29 +12:00
manager.logger.Debug().Uint64("serial", serial).Msg("cache miss")
entry, err := manager.fetchEntry()
if err != nil {
return nil, err
}
manager.cacheMu.Lock()
2023-04-17 10:42:29 +12:00
manager.cache[entry.Serial] = entry
manager.cacheMu.Unlock()
2023-04-17 10:42:29 +12:00
if entry.Serial != serial {
manager.logger.Warn().
Uint64("requested_serial", serial).
Uint64("received_serial", entry.Serial).
Msg("serial mismatch")
}
return entry, nil
}
2023-04-17 10:42:29 +12:00
func (manager *image) GetCurrent() (cur *types.CursorImage, img []byte, err error) {
if manager.current != nil {
2023-04-17 10:42:29 +12:00
return manager.current.CursorImage, manager.current.ImagePNG, nil
}
entry, err := manager.fetchEntry()
if err != nil {
return nil, nil, err
}
2023-04-17 10:42:29 +12:00
manager.current = entry
return entry.CursorImage, entry.ImagePNG, nil
}
2023-04-17 10:42:29 +12:00
func (manager *image) AddListener(listener ImageListener) {
2022-02-15 06:41:47 +13:00
manager.listenersMu.Lock()
defer manager.listenersMu.Unlock()
if listener != nil {
ptr := reflect.ValueOf(listener).Pointer()
manager.listeners[ptr] = listener
}
}
2023-04-17 10:42:29 +12:00
func (manager *image) RemoveListener(listener ImageListener) {
2022-02-15 06:41:47 +13:00
manager.listenersMu.Lock()
defer manager.listenersMu.Unlock()
if listener != nil {
ptr := reflect.ValueOf(listener).Pointer()
delete(manager.listeners, ptr)
}
}
2023-04-17 10:42:29 +12:00
func (manager *image) fetchEntry() (*imageEntry, error) {
cur := manager.desktop.GetCursorImage()
img, err := utils.CreatePNGImage(cur.Image)
if err != nil {
return nil, err
}
2023-04-17 10:42:29 +12:00
cur.Image = nil // free memory
2023-04-17 10:42:29 +12:00
return &imageEntry{
CursorImage: cur,
ImagePNG: img,
}, nil
}