seperate remote desktop from webrtc

This commit is contained in:
Craig 2020-04-05 22:34:51 +00:00
parent 6de731b9bb
commit 26c6cfbe1e
18 changed files with 625 additions and 459 deletions

View File

@ -19,6 +19,7 @@ func init() {
configs := []config.Config{ configs := []config.Config{
neko.Service.Server, neko.Service.Server,
neko.Service.WebRTC, neko.Service.WebRTC,
neko.Service.Remote,
neko.Service.WebSocket, neko.Service.WebSocket,
} }

View File

@ -0,0 +1,208 @@
package remote
import (
"fmt"
"time"
"github.com/kataras/go-events"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"n.eko.moe/neko/internal/gst"
"n.eko.moe/neko/internal/types"
"n.eko.moe/neko/internal/types/config"
"n.eko.moe/neko/internal/xorg"
)
type RemoteManager struct {
logger zerolog.Logger
video *gst.Pipeline
audio *gst.Pipeline
config *config.Remote
cleanup *time.Ticker
shutdown chan bool
emmiter events.EventEmmiter
}
func New(config *config.Remote) *RemoteManager {
return &RemoteManager{
logger: log.With().Str("module", "remote").Logger(),
cleanup: time.NewTicker(1 * time.Second),
shutdown: make(chan bool),
emmiter: events.New(),
config: config,
}
}
func (manager *RemoteManager) VideoCodec() string {
return manager.config.VideoCodec
}
func (manager *RemoteManager) AudioCodec() string {
return manager.config.AudioCodec
}
func (manager *RemoteManager) Start() {
var err error
manager.video, err = gst.CreatePipeline(
manager.config.VideoCodec,
manager.config.Display,
manager.config.VideoParams,
)
if err != nil {
manager.logger.Panic().Err(err).Msg("unable to create video pipeline")
}
manager.audio, err = gst.CreatePipeline(
manager.config.AudioCodec,
manager.config.Device,
manager.config.AudioParams,
)
if err != nil {
manager.logger.Panic().Err(err).Msg("unable to screate audio pipeline")
}
manager.StartStream()
go func() {
defer func() {
manager.logger.Info().Msg("shutdown")
}()
for {
select {
case <-manager.shutdown:
return
case sample := <-manager.video.Sample:
manager.emmiter.Emit("video", sample)
case sample := <-manager.audio.Sample:
manager.emmiter.Emit("audio", sample)
case <-manager.cleanup.C:
xorg.CheckKeys(time.Second * 10)
}
}
}()
}
func (manager *RemoteManager) Shutdown() error {
manager.logger.Info().Msgf("remote shutting down")
manager.video.Stop()
manager.audio.Stop()
manager.cleanup.Stop()
manager.shutdown <- true
return nil
}
func (manager *RemoteManager) OnVideoFrame(listener func(sample types.Sample)) {
manager.emmiter.On("video", func(payload ...interface{}) {
listener(payload[0].(types.Sample))
})
}
func (manager *RemoteManager) OnAudioFrame(listener func(sample types.Sample)) {
manager.emmiter.On("audio", func(payload ...interface{}) {
listener(payload[0].(types.Sample))
})
}
func (manager *RemoteManager) StartStream() {
manager.logger.Info().
Str("video_display", manager.config.Display).
Str("video_codec", manager.config.VideoCodec).
Str("audio_device", manager.config.Device).
Str("audio_codec", manager.config.AudioCodec).
Str("audio_pipeline_src", manager.audio.Src).
Str("video_pipeline_src", manager.video.Src).
Str("screen_resolution", fmt.Sprintf("%dx%d@%d", manager.config.ScreenWidth, manager.config.ScreenHeight, manager.config.ScreenRate)).
Msgf("pipelines starting...")
xorg.Display(manager.config.Display)
if !xorg.ValidScreenSize(manager.config.ScreenWidth, manager.config.ScreenHeight, manager.config.ScreenRate) {
manager.logger.Warn().Msgf("invalid screen option %dx%d@%d", manager.config.ScreenWidth, manager.config.ScreenHeight, manager.config.ScreenRate)
} else {
if err := xorg.ChangeScreenSize(manager.config.ScreenWidth, manager.config.ScreenHeight, manager.config.ScreenRate); err != nil {
manager.logger.Warn().Err(err).Msg("unable to change screen size")
}
}
manager.video.Start()
manager.audio.Start()
}
func (manager *RemoteManager) StopStream() {
manager.video.Stop()
manager.audio.Stop()
}
func (manager *RemoteManager) ChangeResolution(width int, height int, rate int) error {
if !xorg.ValidScreenSize(width, height, rate) {
return fmt.Errorf("unknown configuration")
}
manager.video.Stop()
defer func() {
manager.video.Start()
manager.logger.Info().Msg("starting video pipeline...")
}()
if err := xorg.ChangeScreenSize(width, height, rate); err != nil {
return err
}
video, err := gst.CreatePipeline(
manager.config.VideoCodec,
manager.config.Display,
manager.config.VideoParams,
)
if err != nil {
manager.logger.Panic().Err(err).Msg("unable to create new video pipeline")
}
manager.video = video
return nil
}
func (manager *RemoteManager) Move(x, y int) {
xorg.Move(x, y)
}
func (manager *RemoteManager) Scroll(x, y int) {
xorg.Scroll(x, y)
}
func (manager *RemoteManager) ButtonDown(code int) (*types.Button, error) {
return xorg.ButtonDown(code)
}
func (manager *RemoteManager) KeyDown(code int) (*types.Key, error) {
return xorg.KeyDown(code)
}
func (manager *RemoteManager) ButtonUp(code int) (*types.Button, error) {
return xorg.ButtonUp(code)
}
func (manager *RemoteManager) KeyUp(code int) (*types.Key, error) {
return xorg.KeyUp(code)
}
func (manager *RemoteManager) ReadClipboard() string {
return xorg.ReadClipboard()
}
func (manager *RemoteManager) WriteClipboard(data string) {
xorg.WriteClipboard(data)
}
func (manager *RemoteManager) ResetKeys() {
xorg.ResetKeys()
}
func (manager *RemoteManager) ScreenConfigurations() map[int]types.ScreenConfiguration {
return xorg.ScreenConfigurations
}
func (manager *RemoteManager) GetScreenSize() *types.ScreenSize {
return xorg.GetScreenSize()
}

View File

@ -0,0 +1,136 @@
package config
import (
"regexp"
"strconv"
"github.com/pion/webrtc/v2"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
type Remote struct {
Display string
Device string
AudioCodec string
AudioParams string
VideoCodec string
VideoParams string
ScreenWidth int
ScreenHeight int
ScreenRate int
}
func (Remote) Init(cmd *cobra.Command) error {
cmd.PersistentFlags().String("display", ":99.0", "XDisplay to capture")
if err := viper.BindPFlag("display", cmd.PersistentFlags().Lookup("display")); err != nil {
return err
}
cmd.PersistentFlags().String("device", "auto_null.monitor", "audio device to capture")
if err := viper.BindPFlag("device", cmd.PersistentFlags().Lookup("device")); err != nil {
return err
}
cmd.PersistentFlags().String("audio", "", "audio codec parameters to use for streaming (unused)")
if err := viper.BindPFlag("aparams", cmd.PersistentFlags().Lookup("audio")); err != nil {
return err
}
cmd.PersistentFlags().String("video", "", "video codec parameters to use for streaming (unused)")
if err := viper.BindPFlag("vparams", cmd.PersistentFlags().Lookup("video")); err != nil {
return err
}
cmd.PersistentFlags().String("screen", "1280x720@30", "default screen resolution and framerate")
if err := viper.BindPFlag("vparams", cmd.PersistentFlags().Lookup("video")); err != nil {
return err
}
// video codecs
cmd.PersistentFlags().Bool("vp8", false, "use VP8 video codec")
if err := viper.BindPFlag("vp8", cmd.PersistentFlags().Lookup("vp8")); err != nil {
return err
}
cmd.PersistentFlags().Bool("vp9", false, "use VP9 video codec")
if err := viper.BindPFlag("vp9", cmd.PersistentFlags().Lookup("vp9")); err != nil {
return err
}
cmd.PersistentFlags().Bool("h264", false, "use H264 video codec")
if err := viper.BindPFlag("h264", cmd.PersistentFlags().Lookup("h264")); err != nil {
return err
}
// audio codecs
cmd.PersistentFlags().Bool("opus", false, "use Opus audio codec")
if err := viper.BindPFlag("opus", cmd.PersistentFlags().Lookup("opus")); err != nil {
return err
}
cmd.PersistentFlags().Bool("g722", false, "use G722 audio codec")
if err := viper.BindPFlag("g722", cmd.PersistentFlags().Lookup("g722")); err != nil {
return err
}
cmd.PersistentFlags().Bool("pcmu", false, "use PCMU audio codec")
if err := viper.BindPFlag("pcmu", cmd.PersistentFlags().Lookup("pcmu")); err != nil {
return err
}
cmd.PersistentFlags().Bool("pcma", false, "use PCMA audio codec")
if err := viper.BindPFlag("pcma", cmd.PersistentFlags().Lookup("pcma")); err != nil {
return err
}
return nil
}
func (s *Remote) Set() {
videoCodec := webrtc.VP8
if viper.GetBool("vp8") {
videoCodec = webrtc.VP8
} else if viper.GetBool("vp9") {
videoCodec = webrtc.VP9
} else if viper.GetBool("h264") {
videoCodec = webrtc.H264
}
audioCodec := webrtc.Opus
if viper.GetBool("opus") {
audioCodec = webrtc.Opus
} else if viper.GetBool("g722") {
audioCodec = webrtc.G722
} else if viper.GetBool("pcmu") {
audioCodec = webrtc.PCMU
} else if viper.GetBool("pcma") {
audioCodec = webrtc.PCMA
}
s.Device = viper.GetString("device")
s.AudioCodec = audioCodec
s.AudioParams = viper.GetString("aparams")
s.Display = viper.GetString("display")
s.VideoCodec = videoCodec
s.VideoParams = viper.GetString("vparams")
s.ScreenWidth = 1280
s.ScreenHeight = 720
s.ScreenRate = 30
r := regexp.MustCompile(`([0-9]{1,4})x([0-9]{1,4})@([0-9]{1,3})`)
res := r.FindStringSubmatch(viper.GetString("screen"))
if len(res) > 0 {
width, err1 := strconv.ParseInt(res[1], 10, 64)
height, err2 := strconv.ParseInt(res[1], 10, 64)
rate, err3 := strconv.ParseInt(res[1], 10, 64)
if err1 == nil && err2 == nil && err3 == nil {
s.ScreenWidth = int(width)
s.ScreenHeight = int(height)
s.ScreenRate = int(rate)
}
}
}

View File

@ -1,101 +1,28 @@
package config package config
import ( import (
"regexp"
"strconv" "strconv"
"strings" "strings"
"github.com/pion/webrtc/v2"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/viper" "github.com/spf13/viper"
"n.eko.moe/neko/internal/utils" "n.eko.moe/neko/internal/utils"
) )
type WebRTC struct { type WebRTC struct {
Device string
AudioCodec string
AudioParams string
Display string
ICELite bool ICELite bool
ICEServers []string ICEServers []string
VideoCodec string
VideoParams string
EphemeralMin uint16 EphemeralMin uint16
EphemeralMax uint16 EphemeralMax uint16
NAT1To1IPs []string NAT1To1IPs []string
ScreenWidth int
ScreenHeight int
ScreenRate int
} }
func (WebRTC) Init(cmd *cobra.Command) error { func (WebRTC) Init(cmd *cobra.Command) error {
cmd.PersistentFlags().String("device", "auto_null.monitor", "audio device to capture")
if err := viper.BindPFlag("device", cmd.PersistentFlags().Lookup("device")); err != nil {
return err
}
cmd.PersistentFlags().String("audio", "", "audio codec parameters to use for streaming (unused)")
if err := viper.BindPFlag("aparams", cmd.PersistentFlags().Lookup("audio")); err != nil {
return err
}
cmd.PersistentFlags().String("display", ":99.0", "XDisplay to capture")
if err := viper.BindPFlag("display", cmd.PersistentFlags().Lookup("display")); err != nil {
return err
}
cmd.PersistentFlags().String("video", "", "video codec parameters to use for streaming (unused)")
if err := viper.BindPFlag("vparams", cmd.PersistentFlags().Lookup("video")); err != nil {
return err
}
cmd.PersistentFlags().String("screen", "1280x720@30", "default screen resolution and framerate")
if err := viper.BindPFlag("vparams", cmd.PersistentFlags().Lookup("video")); err != nil {
return err
}
cmd.PersistentFlags().String("epr", "59000-59100", "limits the pool of ephemeral ports that ICE UDP connections can allocate from") cmd.PersistentFlags().String("epr", "59000-59100", "limits the pool of ephemeral ports that ICE UDP connections can allocate from")
if err := viper.BindPFlag("epr", cmd.PersistentFlags().Lookup("epr")); err != nil { if err := viper.BindPFlag("epr", cmd.PersistentFlags().Lookup("epr")); err != nil {
return err return err
} }
// video codecs
cmd.PersistentFlags().Bool("vp8", false, "use VP8 video codec")
if err := viper.BindPFlag("vp8", cmd.PersistentFlags().Lookup("vp8")); err != nil {
return err
}
cmd.PersistentFlags().Bool("vp9", false, "use VP9 video codec")
if err := viper.BindPFlag("vp9", cmd.PersistentFlags().Lookup("vp9")); err != nil {
return err
}
cmd.PersistentFlags().Bool("h264", false, "use H264 video codec")
if err := viper.BindPFlag("h264", cmd.PersistentFlags().Lookup("h264")); err != nil {
return err
}
// audio codecs
cmd.PersistentFlags().Bool("opus", false, "use Opus audio codec")
if err := viper.BindPFlag("opus", cmd.PersistentFlags().Lookup("opus")); err != nil {
return err
}
cmd.PersistentFlags().Bool("g722", false, "use G722 audio codec")
if err := viper.BindPFlag("g722", cmd.PersistentFlags().Lookup("g722")); err != nil {
return err
}
cmd.PersistentFlags().Bool("pcmu", false, "use PCMU audio codec")
if err := viper.BindPFlag("pcmu", cmd.PersistentFlags().Lookup("pcmu")); err != nil {
return err
}
cmd.PersistentFlags().Bool("pcma", false, "use PCMA audio codec")
if err := viper.BindPFlag("pcma", cmd.PersistentFlags().Lookup("pcma")); err != nil {
return err
}
cmd.PersistentFlags().StringSlice("nat1to1", []string{}, "sets a list of external IP addresses of 1:1 (D)NAT and a candidate type for which the external IP address is used") cmd.PersistentFlags().StringSlice("nat1to1", []string{}, "sets a list of external IP addresses of 1:1 (D)NAT and a candidate type for which the external IP address is used")
if err := viper.BindPFlag("nat1to1", cmd.PersistentFlags().Lookup("nat1to1")); err != nil { if err := viper.BindPFlag("nat1to1", cmd.PersistentFlags().Lookup("nat1to1")); err != nil {
return err return err
@ -115,35 +42,8 @@ func (WebRTC) Init(cmd *cobra.Command) error {
} }
func (s *WebRTC) Set() { func (s *WebRTC) Set() {
videoCodec := webrtc.VP8
if viper.GetBool("vp8") {
videoCodec = webrtc.VP8
} else if viper.GetBool("vp9") {
videoCodec = webrtc.VP9
} else if viper.GetBool("h264") {
videoCodec = webrtc.H264
}
audioCodec := webrtc.Opus
if viper.GetBool("opus") {
audioCodec = webrtc.Opus
} else if viper.GetBool("g722") {
audioCodec = webrtc.G722
} else if viper.GetBool("pcmu") {
audioCodec = webrtc.PCMU
} else if viper.GetBool("pcma") {
audioCodec = webrtc.PCMA
}
s.ICELite = viper.GetBool("icelite") s.ICELite = viper.GetBool("icelite")
s.ICEServers = viper.GetStringSlice("iceserver") s.ICEServers = viper.GetStringSlice("iceserver")
s.Device = viper.GetString("device")
s.AudioCodec = audioCodec
s.AudioParams = viper.GetString("aparams")
s.Display = viper.GetString("display")
s.VideoCodec = videoCodec
s.VideoParams = viper.GetString("vparams")
s.NAT1To1IPs = viper.GetStringSlice("nat1to1") s.NAT1To1IPs = viper.GetStringSlice("nat1to1")
if len(s.NAT1To1IPs) == 0 { if len(s.NAT1To1IPs) == 0 {
@ -176,23 +76,4 @@ func (s *WebRTC) Set() {
s.EphemeralMin = min s.EphemeralMin = min
s.EphemeralMax = max s.EphemeralMax = max
} }
s.ScreenWidth = 1280
s.ScreenHeight = 720
s.ScreenRate = 30
r := regexp.MustCompile(`([0-9]{1,4})x([0-9]{1,4})@([0-9]{1,3})`)
res := r.FindStringSubmatch(viper.GetString("screen"))
if len(res) > 0 {
width, err1 := strconv.ParseInt(res[1], 10, 64)
height, err2 := strconv.ParseInt(res[1], 10, 64)
rate, err3 := strconv.ParseInt(res[1], 10, 64)
if err1 == nil && err2 == nil && err3 == nil {
s.ScreenWidth = int(width)
s.ScreenHeight = int(height)
s.ScreenRate = int(rate)
}
}
} }

View File

@ -0,0 +1,14 @@
package types
type Button struct {
Name string
Code int
Keysym int
}
type Key struct {
Name string
Value string
Code int
Keysym int
}

View File

@ -0,0 +1,24 @@
package types
type RemoteManager interface {
VideoCodec() string
AudioCodec() string
Start()
Shutdown() error
OnVideoFrame(listener func(sample Sample))
OnAudioFrame(listener func(sample Sample))
StartStream()
StopStream()
ChangeResolution(width int, height int, rate int) error
GetScreenSize() *ScreenSize
ScreenConfigurations() map[int]ScreenConfiguration
Move(x, y int)
Scroll(x, y int)
ButtonDown(code int) (*Button, error)
KeyDown(code int) (*Key, error)
ButtonUp(code int) (*Button, error)
KeyUp(code int) (*Key, error)
ReadClipboard() string
WriteClipboard(data string)
ResetKeys()
}

View File

@ -9,7 +9,6 @@ type WebRTCManager interface {
Start() Start()
Shutdown() error Shutdown() error
CreatePeer(id string, session Session) (string, bool, []string, error) CreatePeer(id string, session Session) (string, bool, []string, error)
ChangeScreenSize(width int, height int, rate int) error
} }
type Peer interface { type Peer interface {

View File

@ -6,7 +6,6 @@ import (
"strconv" "strconv"
"github.com/pion/webrtc/v2" "github.com/pion/webrtc/v2"
"n.eko.moe/neko/internal/xorg"
) )
const OP_MOVE = 0x01 const OP_MOVE = 0x01
@ -37,8 +36,8 @@ type PayloadKey struct {
Key uint16 Key uint16
} }
func (m *WebRTCManager) handle(id string, msg webrtc.DataChannelMessage) error { func (manager *WebRTCManager) handle(id string, msg webrtc.DataChannelMessage) error {
if !m.sessions.IsHost(id) { if !manager.sessions.IsHost(id) {
return nil return nil
} }
@ -63,7 +62,7 @@ func (m *WebRTCManager) handle(id string, msg webrtc.DataChannelMessage) error {
return err return err
} }
xorg.Move(int(payload.X), int(payload.Y)) manager.remote.Move(int(payload.X), int(payload.Y))
break break
case OP_SCROLL: case OP_SCROLL:
payload := &PayloadScroll{} payload := &PayloadScroll{}
@ -71,13 +70,13 @@ func (m *WebRTCManager) handle(id string, msg webrtc.DataChannelMessage) error {
return err return err
} }
m.logger. manager.logger.
Debug(). Debug().
Str("x", strconv.Itoa(int(payload.X))). Str("x", strconv.Itoa(int(payload.X))).
Str("y", strconv.Itoa(int(payload.Y))). Str("y", strconv.Itoa(int(payload.Y))).
Msg("scroll") Msg("scroll")
xorg.Scroll(int(payload.X), int(payload.Y)) manager.remote.Scroll(int(payload.X), int(payload.Y))
break break
case OP_KEY_DOWN: case OP_KEY_DOWN:
payload := &PayloadKey{} payload := &PayloadKey{}
@ -86,21 +85,21 @@ func (m *WebRTCManager) handle(id string, msg webrtc.DataChannelMessage) error {
} }
if payload.Key < 8 { if payload.Key < 8 {
button, err := xorg.ButtonDown(int(payload.Key)) button, err := manager.remote.ButtonDown(int(payload.Key))
if err != nil { if err != nil {
m.logger.Warn().Err(err).Msg("key down failed") manager.logger.Warn().Err(err).Msg("key down failed")
return nil return nil
} }
m.logger.Debug().Msgf("button down %s(%d)", button.Name, payload.Key) manager.logger.Debug().Msgf("button down %s(%d)", button.Name, payload.Key)
} else { } else {
key, err := xorg.KeyDown(int(payload.Key)) key, err := manager.remote.KeyDown(int(payload.Key))
if err != nil { if err != nil {
m.logger.Warn().Err(err).Msg("key down failed") manager.logger.Warn().Err(err).Msg("key down failed")
return nil return nil
} }
m.logger.Debug().Msgf("key down %s(%d)", key.Name, payload.Key) manager.logger.Debug().Msgf("key down %s(%d)", key.Name, payload.Key)
} }
break break
@ -112,21 +111,21 @@ func (m *WebRTCManager) handle(id string, msg webrtc.DataChannelMessage) error {
} }
if payload.Key < 8 { if payload.Key < 8 {
button, err := xorg.ButtonUp(int(payload.Key)) button, err := manager.remote.ButtonUp(int(payload.Key))
if err != nil { if err != nil {
m.logger.Warn().Err(err).Msg("button up failed") manager.logger.Warn().Err(err).Msg("button up failed")
return nil return nil
} }
m.logger.Debug().Msgf("button up %s(%d)", button.Name, payload.Key) manager.logger.Debug().Msgf("button up %s(%d)", button.Name, payload.Key)
} else { } else {
key, err := xorg.KeyUp(int(payload.Key)) key, err := manager.remote.KeyUp(int(payload.Key))
if err != nil { if err != nil {
m.logger.Warn().Err(err).Msg("keyup failed") manager.logger.Warn().Err(err).Msg("keyup failed")
return nil return nil
} }
m.logger.Debug().Msgf("key up %s(%d)", key.Name, payload.Key) manager.logger.Debug().Msgf("key up %s(%d)", key.Name, payload.Key)
} }
break break
case OP_KEY_CLK: case OP_KEY_CLK:

View File

@ -1,48 +0,0 @@
package webrtc
import (
"fmt"
"math/rand"
"github.com/pion/webrtc/v2"
"n.eko.moe/neko/internal/gst"
)
func (m *WebRTCManager) createTrack(codecName string, pipelineDevice string, pipelineSrc string) (*gst.Pipeline, *webrtc.Track, *webrtc.RTPCodec, error) {
pipeline, err := gst.CreatePipeline(
codecName,
pipelineDevice,
pipelineSrc,
)
if err != nil {
return nil, nil, nil, err
}
var codec *webrtc.RTPCodec
switch codecName {
case webrtc.VP8:
codec = webrtc.NewRTPVP8Codec(webrtc.DefaultPayloadTypeVP8, 90000)
case webrtc.VP9:
codec = webrtc.NewRTPVP9Codec(webrtc.DefaultPayloadTypeVP9, 90000)
case webrtc.H264:
codec = webrtc.NewRTPH264Codec(webrtc.DefaultPayloadTypeH264, 90000)
case webrtc.Opus:
codec = webrtc.NewRTPOpusCodec(webrtc.DefaultPayloadTypeOpus, 48000)
case webrtc.G722:
codec = webrtc.NewRTPG722Codec(webrtc.DefaultPayloadTypeG722, 8000)
case webrtc.PCMU:
codec = webrtc.NewRTPPCMUCodec(webrtc.DefaultPayloadTypePCMU, 8000)
case webrtc.PCMA:
codec = webrtc.NewRTPPCMACodec(webrtc.DefaultPayloadTypePCMA, 8000)
default:
return nil, nil, nil, fmt.Errorf("unknown codec %s", codecName)
}
track, err := webrtc.NewTrack(codec.PayloadType, rand.Uint32(), "stream", "stream", codec)
if err != nil {
return nil, nil, nil, err
}
return pipeline, track, codec, nil
}

View File

@ -3,25 +3,22 @@ package webrtc
import ( import (
"fmt" "fmt"
"io" "io"
"math/rand"
"strings" "strings"
"time"
"github.com/pion/webrtc/v2" "github.com/pion/webrtc/v2"
"github.com/pion/webrtc/v2/pkg/media" "github.com/pion/webrtc/v2/pkg/media"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"n.eko.moe/neko/internal/gst"
"n.eko.moe/neko/internal/types" "n.eko.moe/neko/internal/types"
"n.eko.moe/neko/internal/types/config" "n.eko.moe/neko/internal/types/config"
"n.eko.moe/neko/internal/xorg"
) )
func New(sessions types.SessionManager, config *config.WebRTC) *WebRTCManager { func New(sessions types.SessionManager, remote types.RemoteManager, config *config.WebRTC) *WebRTCManager {
return &WebRTCManager{ return &WebRTCManager{
logger: log.With().Str("module", "webrtc").Logger(), logger: log.With().Str("module", "webrtc").Logger(),
cleanup: time.NewTicker(1 * time.Second), remote: remote,
shutdown: make(chan bool),
sessions: sessions, sessions: sessions,
config: config, config: config,
} }
@ -31,105 +28,55 @@ type WebRTCManager struct {
logger zerolog.Logger logger zerolog.Logger
videoTrack *webrtc.Track videoTrack *webrtc.Track
audioTrack *webrtc.Track audioTrack *webrtc.Track
videoPipeline *gst.Pipeline
audioPipeline *gst.Pipeline
videoCodec *webrtc.RTPCodec videoCodec *webrtc.RTPCodec
audioCodec *webrtc.RTPCodec audioCodec *webrtc.RTPCodec
sessions types.SessionManager sessions types.SessionManager
cleanup *time.Ticker remote types.RemoteManager
config *config.WebRTC config *config.WebRTC
shutdown chan bool
}
func (m *WebRTCManager) Start() {
// Set display and change to default resolution
xorg.Display(m.config.Display)
if !xorg.ValidScreenSize(m.config.ScreenWidth, m.config.ScreenHeight, m.config.ScreenRate) {
m.logger.Warn().Msgf("invalid screen option %dx%d@%d", m.config.ScreenWidth, m.config.ScreenHeight, m.config.ScreenRate)
} else {
if err := xorg.ChangeScreenSize(m.config.ScreenWidth, m.config.ScreenHeight, m.config.ScreenRate); err != nil {
m.logger.Warn().Err(err).Msg("unable to change screen size")
}
} }
func (manager *WebRTCManager) Start() {
var err error var err error
m.videoPipeline, m.videoTrack, m.videoCodec, err = m.createTrack(m.config.VideoCodec, m.config.Display, m.config.VideoParams) manager.audioTrack, manager.audioCodec, err = manager.createTrack(manager.remote.AudioCodec())
if err != nil { if err != nil {
m.logger.Panic().Err(err).Msg("unable to start webrtc manager") manager.logger.Panic().Err(err).Msg("unable to create audio track")
} }
m.audioPipeline, m.audioTrack, m.audioCodec, err = m.createTrack(m.config.AudioCodec, m.config.Device, m.config.AudioParams) manager.remote.OnAudioFrame(func(sample types.Sample) {
if err := manager.audioTrack.WriteSample(media.Sample(sample)); err != nil && err != io.ErrClosedPipe {
manager.logger.Warn().Err(err).Msg("audio pipeline failed to write")
}
})
manager.videoTrack, manager.videoCodec, err = manager.createTrack(manager.remote.VideoCodec())
if err != nil { if err != nil {
m.logger.Panic().Err(err).Msg("unable to start webrtc manager") manager.logger.Panic().Err(err).Msg("unable to create video track")
} }
go func() { manager.remote.OnVideoFrame(func(sample types.Sample) {
defer func() { if err := manager.videoTrack.WriteSample(media.Sample(sample)); err != nil && err != io.ErrClosedPipe {
m.logger.Info().Msg("shutdown") manager.logger.Warn().Err(err).Msg("video pipeline failed to write")
}()
for {
select {
case <-m.shutdown:
return
case sample := <-m.videoPipeline.Sample:
if err := m.videoTrack.WriteSample(media.Sample(sample)); err != nil && err != io.ErrClosedPipe {
m.logger.Warn().Err(err).Msg("video pipeline failed to write")
} }
case sample := <-m.audioPipeline.Sample:
if err := m.audioTrack.WriteSample(media.Sample(sample)); err != nil && err != io.ErrClosedPipe {
m.logger.Warn().Err(err).Msg("audio pipeline failed to write")
}
case <-m.cleanup.C:
xorg.CheckKeys(time.Second * 10)
}
}
}()
m.videoPipeline.Start()
m.audioPipeline.Start()
m.sessions.OnHostCleared(func(id string) {
xorg.ResetKeys()
}) })
m.sessions.OnCreated(func(id string, session types.Session) { manager.logger.Info().
m.logger.Debug().Str("id", id).Msg("session created") Str("ice_lite", fmt.Sprintf("%t", manager.config.ICELite)).
}) Str("ice_servers", strings.Join(manager.config.ICEServers, ",")).
Str("ephemeral_port_range", fmt.Sprintf("%d-%d", manager.config.EphemeralMin, manager.config.EphemeralMax)).
m.sessions.OnDestroy(func(id string) { Str("nat_ips", strings.Join(manager.config.NAT1To1IPs, ",")).
m.logger.Debug().Str("id", id).Msg("session destroyed") Msgf("webrtc starting")
})
// TODO: log resolution, bit rate and codec parameters
m.logger.Info().
Str("video_display", m.config.Display).
Str("video_codec", m.config.VideoCodec).
Str("audio_device", m.config.Device).
Str("audio_codec", m.config.AudioCodec).
Str("audio_pipeline_src", m.audioPipeline.Src).
Str("video_pipeline_src", m.videoPipeline.Src).
Str("ice_lite", fmt.Sprintf("%t", m.config.ICELite)).
Str("ice_servers", strings.Join(m.config.ICEServers, ",")).
Str("ephemeral_port_range", fmt.Sprintf("%d-%d", m.config.EphemeralMin, m.config.EphemeralMax)).
Str("nat_ips", strings.Join(m.config.NAT1To1IPs, ",")).
Msgf("webrtc streaming")
} }
func (m *WebRTCManager) Shutdown() error { func (manager *WebRTCManager) Shutdown() error {
m.logger.Info().Msgf("webrtc shutting down") manager.logger.Info().Msgf("webrtc shutting down")
m.videoPipeline.Stop()
m.audioPipeline.Stop()
m.cleanup.Stop()
m.shutdown <- true
return nil return nil
} }
func (m *WebRTCManager) CreatePeer(id string, session types.Session) (string, bool, []string, error) { func (manager *WebRTCManager) CreatePeer(id string, session types.Session) (string, bool, []string, error) {
configuration := &webrtc.Configuration{ configuration := &webrtc.Configuration{
ICEServers: []webrtc.ICEServer{ ICEServers: []webrtc.ICEServer{
{ {
URLs: m.config.ICEServers, URLs: manager.config.ICEServers,
}, },
}, },
SDPSemantics: webrtc.SDPSemanticsUnifiedPlanWithFallback, SDPSemantics: webrtc.SDPSemanticsUnifiedPlanWithFallback,
@ -137,25 +84,25 @@ func (m *WebRTCManager) CreatePeer(id string, session types.Session) (string, bo
settings := webrtc.SettingEngine{ settings := webrtc.SettingEngine{
LoggerFactory: loggerFactory{ LoggerFactory: loggerFactory{
logger: m.logger, logger: manager.logger,
}, },
} }
if m.config.ICELite { if manager.config.ICELite {
configuration = &webrtc.Configuration{ configuration = &webrtc.Configuration{
SDPSemantics: webrtc.SDPSemanticsUnifiedPlanWithFallback, SDPSemantics: webrtc.SDPSemanticsUnifiedPlanWithFallback,
} }
settings.SetLite(true) settings.SetLite(true)
} }
settings.SetEphemeralUDPPortRange(m.config.EphemeralMin, m.config.EphemeralMax) settings.SetEphemeralUDPPortRange(manager.config.EphemeralMin, manager.config.EphemeralMax)
settings.SetNAT1To1IPs(m.config.NAT1To1IPs, webrtc.ICECandidateTypeHost) settings.SetNAT1To1IPs(manager.config.NAT1To1IPs, webrtc.ICECandidateTypeHost)
// Create MediaEngine based off sdp // Create MediaEngine based off sdp
engine := webrtc.MediaEngine{} engine := webrtc.MediaEngine{}
// engine.RegisterDefaultCodecs()
engine.RegisterCodec(m.audioCodec) engine.RegisterCodec(manager.audioCodec)
engine.RegisterCodec(m.videoCodec) engine.RegisterCodec(manager.videoCodec)
// Create API with MediaEngine and SettingEngine // Create API with MediaEngine and SettingEngine
api := webrtc.NewAPI(webrtc.WithMediaEngine(engine), webrtc.WithSettingEngine(settings)) api := webrtc.NewAPI(webrtc.WithMediaEngine(engine), webrtc.WithSettingEngine(settings))
@ -163,30 +110,30 @@ func (m *WebRTCManager) CreatePeer(id string, session types.Session) (string, bo
// Create new peer connection // Create new peer connection
connection, err := api.NewPeerConnection(*configuration) connection, err := api.NewPeerConnection(*configuration)
if err != nil { if err != nil {
return "", m.config.ICELite, m.config.ICEServers, err return "", manager.config.ICELite, manager.config.ICEServers, err
} }
if _, err = connection.AddTransceiverFromTrack(m.videoTrack, webrtc.RtpTransceiverInit{ if _, err = connection.AddTransceiverFromTrack(manager.videoTrack, webrtc.RtpTransceiverInit{
Direction: webrtc.RTPTransceiverDirectionSendonly, Direction: webrtc.RTPTransceiverDirectionSendonly,
}); err != nil { }); err != nil {
return "", m.config.ICELite, m.config.ICEServers, err return "", manager.config.ICELite, manager.config.ICEServers, err
} }
if _, err = connection.AddTransceiverFromTrack(m.audioTrack, webrtc.RtpTransceiverInit{ if _, err = connection.AddTransceiverFromTrack(manager.audioTrack, webrtc.RtpTransceiverInit{
Direction: webrtc.RTPTransceiverDirectionSendonly, Direction: webrtc.RTPTransceiverDirectionSendonly,
}); err != nil { }); err != nil {
return "", m.config.ICELite, m.config.ICEServers, err return "", manager.config.ICELite, manager.config.ICEServers, err
} }
description, err := connection.CreateOffer(nil) description, err := connection.CreateOffer(nil)
if err != nil { if err != nil {
return "", m.config.ICELite, m.config.ICEServers, err return "", manager.config.ICELite, manager.config.ICEServers, err
} }
connection.OnDataChannel(func(d *webrtc.DataChannel) { connection.OnDataChannel(func(d *webrtc.DataChannel) {
d.OnMessage(func(msg webrtc.DataChannelMessage) { d.OnMessage(func(msg webrtc.DataChannelMessage) {
if err = m.handle(id, msg); err != nil { if err = manager.handle(id, msg); err != nil {
m.logger.Warn().Err(err).Msg("data handle failed") manager.logger.Warn().Err(err).Msg("data handle failed")
} }
}) })
}) })
@ -196,14 +143,14 @@ func (m *WebRTCManager) CreatePeer(id string, session types.Session) (string, bo
switch state { switch state {
case webrtc.PeerConnectionStateDisconnected: case webrtc.PeerConnectionStateDisconnected:
case webrtc.PeerConnectionStateFailed: case webrtc.PeerConnectionStateFailed:
m.logger.Info().Str("id", id).Msg("peer disconnected") manager.logger.Info().Str("id", id).Msg("peer disconnected")
m.sessions.Destroy(id) manager.sessions.Destroy(id)
break break
case webrtc.PeerConnectionStateConnected: case webrtc.PeerConnectionStateConnected:
m.logger.Info().Str("id", id).Msg("peer connected") manager.logger.Info().Str("id", id).Msg("peer connected")
if err = session.SetConnected(true); err != nil { if err = session.SetConnected(true); err != nil {
m.logger.Warn().Err(err).Msg("unable to set connected on peer") manager.logger.Warn().Err(err).Msg("unable to set connected on peer")
m.sessions.Destroy(id) manager.sessions.Destroy(id)
} }
break break
} }
@ -213,43 +160,42 @@ func (m *WebRTCManager) CreatePeer(id string, session types.Session) (string, bo
id: id, id: id,
api: api, api: api,
engine: &engine, engine: &engine,
manager: m, manager: manager,
settings: &settings, settings: &settings,
connection: connection, connection: connection,
configuration: configuration, configuration: configuration,
}); err != nil { }); err != nil {
return "", m.config.ICELite, m.config.ICEServers, err return "", manager.config.ICELite, manager.config.ICEServers, err
} }
return description.SDP, m.config.ICELite, m.config.ICEServers, nil return description.SDP, manager.config.ICELite, manager.config.ICEServers, nil
} }
func (m *WebRTCManager) ChangeScreenSize(width int, height int, rate int) error { func (m *WebRTCManager) createTrack(codecName string) (*webrtc.Track, *webrtc.RTPCodec, error) {
if !xorg.ValidScreenSize(width, height, rate) { var codec *webrtc.RTPCodec
return fmt.Errorf("unknown configuration") switch codecName {
case webrtc.VP8:
codec = webrtc.NewRTPVP8Codec(webrtc.DefaultPayloadTypeVP8, 90000)
case webrtc.VP9:
codec = webrtc.NewRTPVP9Codec(webrtc.DefaultPayloadTypeVP9, 90000)
case webrtc.H264:
codec = webrtc.NewRTPH264Codec(webrtc.DefaultPayloadTypeH264, 90000)
case webrtc.Opus:
codec = webrtc.NewRTPOpusCodec(webrtc.DefaultPayloadTypeOpus, 48000)
case webrtc.G722:
codec = webrtc.NewRTPG722Codec(webrtc.DefaultPayloadTypeG722, 8000)
case webrtc.PCMU:
codec = webrtc.NewRTPPCMUCodec(webrtc.DefaultPayloadTypePCMU, 8000)
case webrtc.PCMA:
codec = webrtc.NewRTPPCMACodec(webrtc.DefaultPayloadTypePCMA, 8000)
default:
return nil, nil, fmt.Errorf("unknown codec %s", codecName)
} }
m.videoPipeline.Stop() track, err := webrtc.NewTrack(codec.PayloadType, rand.Uint32(), "stream", "stream", codec)
defer func() {
m.videoPipeline.Start()
m.logger.Info().Msg("starting pipeline")
}()
if err := xorg.ChangeScreenSize(width, height, rate); err != nil {
return err
}
videoPipeline, err := gst.CreatePipeline(
m.config.VideoCodec,
m.config.Display,
m.config.VideoParams,
)
if err != nil { if err != nil {
m.logger.Panic().Err(err).Msg("unable to create new video pipeline") return nil, nil, err
} }
m.videoPipeline = videoPipeline return track, codec, nil
return nil
} }

View File

@ -4,7 +4,6 @@ import (
"n.eko.moe/neko/internal/types" "n.eko.moe/neko/internal/types"
"n.eko.moe/neko/internal/types/event" "n.eko.moe/neko/internal/types/event"
"n.eko.moe/neko/internal/types/message" "n.eko.moe/neko/internal/types/message"
"n.eko.moe/neko/internal/xorg"
) )
func (h *MessageHandler) controlRelease(id string, session types.Session) error { func (h *MessageHandler) controlRelease(id string, session types.Session) error {
@ -113,6 +112,6 @@ func (h *MessageHandler) controlClipboard(id string, session types.Session, payl
return nil return nil
} }
xorg.WriteClipboard(payload.Text) h.remote.WriteClipboard(payload.Text)
return nil return nil
} }

View File

@ -16,6 +16,7 @@ type MessageHandler struct {
logger zerolog.Logger logger zerolog.Logger
sessions types.SessionManager sessions types.SessionManager
webrtc types.WebRTCManager webrtc types.WebRTCManager
remote types.RemoteManager
banned map[string]bool banned map[string]bool
locked bool locked bool
} }

View File

@ -4,7 +4,6 @@ import (
"n.eko.moe/neko/internal/types" "n.eko.moe/neko/internal/types"
"n.eko.moe/neko/internal/types/event" "n.eko.moe/neko/internal/types/event"
"n.eko.moe/neko/internal/types/message" "n.eko.moe/neko/internal/types/message"
"n.eko.moe/neko/internal/xorg"
) )
func (h *MessageHandler) screenSet(id string, session types.Session, payload *message.ScreenResolution) error { func (h *MessageHandler) screenSet(id string, session types.Session, payload *message.ScreenResolution) error {
@ -13,7 +12,7 @@ func (h *MessageHandler) screenSet(id string, session types.Session, payload *me
return nil return nil
} }
if err := h.webrtc.ChangeScreenSize(payload.Width, payload.Height, payload.Rate); err != nil { if err := h.remote.ChangeResolution(payload.Width, payload.Height, payload.Rate); err != nil {
h.logger.Warn().Err(err).Msgf("unable to change screen size") h.logger.Warn().Err(err).Msgf("unable to change screen size")
return err return err
} }
@ -34,7 +33,7 @@ func (h *MessageHandler) screenSet(id string, session types.Session, payload *me
} }
func (h *MessageHandler) screenResolution(id string, session types.Session) error { func (h *MessageHandler) screenResolution(id string, session types.Session) error {
if size := xorg.GetScreenSize(); size != nil { if size := h.remote.GetScreenSize(); size != nil {
if err := session.Send(message.ScreenResolution{ if err := session.Send(message.ScreenResolution{
Event: event.SCREEN_RESOLUTION, Event: event.SCREEN_RESOLUTION,
Width: size.Width, Width: size.Width,
@ -57,7 +56,7 @@ func (h *MessageHandler) screenConfigurations(id string, session types.Session)
if err := session.Send(message.ScreenConfigurations{ if err := session.Send(message.ScreenConfigurations{
Event: event.SCREEN_CONFIGURATIONS, Event: event.SCREEN_CONFIGURATIONS,
Configurations: xorg.ScreenConfigurations, Configurations: h.remote.ScreenConfigurations(),
}); err != nil { }); err != nil {
h.logger.Warn().Err(err).Msgf("sending event %s has failed", event.SCREEN_CONFIGURATIONS) h.logger.Warn().Err(err).Msgf("sending event %s has failed", event.SCREEN_CONFIGURATIONS)
return err return err

View File

@ -14,16 +14,16 @@ import (
"n.eko.moe/neko/internal/types/event" "n.eko.moe/neko/internal/types/event"
"n.eko.moe/neko/internal/types/message" "n.eko.moe/neko/internal/types/message"
"n.eko.moe/neko/internal/utils" "n.eko.moe/neko/internal/utils"
"n.eko.moe/neko/internal/xorg"
) )
func New(sessions types.SessionManager, webrtc types.WebRTCManager, conf *config.WebSocket) *WebSocketHandler { func New(sessions types.SessionManager, remote types.RemoteManager, webrtc types.WebRTCManager, conf *config.WebSocket) *WebSocketHandler {
logger := log.With().Str("module", "websocket").Logger() logger := log.With().Str("module", "websocket").Logger()
return &WebSocketHandler{ return &WebSocketHandler{
logger: logger, logger: logger,
conf: conf, conf: conf,
sessions: sessions, sessions: sessions,
remote: remote,
upgrader: websocket.Upgrader{ upgrader: websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool { CheckOrigin: func(r *http.Request) bool {
return true return true
@ -31,6 +31,7 @@ func New(sessions types.SessionManager, webrtc types.WebRTCManager, conf *config
}, },
handler: &MessageHandler{ handler: &MessageHandler{
logger: logger.With().Str("subsystem", "handler").Logger(), logger: logger.With().Str("subsystem", "handler").Logger(),
remote: remote,
sessions: sessions, sessions: sessions,
webrtc: webrtc, webrtc: webrtc,
banned: make(map[string]bool), banned: make(map[string]bool),
@ -46,6 +47,7 @@ type WebSocketHandler struct {
logger zerolog.Logger logger zerolog.Logger
upgrader websocket.Upgrader upgrader websocket.Upgrader
sessions types.SessionManager sessions types.SessionManager
remote types.RemoteManager
conf *config.WebSocket conf *config.WebSocket
handler *MessageHandler handler *MessageHandler
shutdown chan bool shutdown chan bool
@ -81,7 +83,7 @@ func (ws *WebSocketHandler) Start() error {
ws.logger.Info().Msg("shutdown") ws.logger.Info().Msg("shutdown")
}() }()
current := xorg.ReadClipboard() current := ws.remote.ReadClipboard()
for { for {
select { select {
@ -89,7 +91,7 @@ func (ws *WebSocketHandler) Start() error {
return return
default: default:
if ws.sessions.HasHost() { if ws.sessions.HasHost() {
text := xorg.ReadClipboard() text := ws.remote.ReadClipboard()
if text != current { if text != current {
session, ok := ws.sessions.GetHost() session, ok := ws.sessions.GetHost()
if ok { if ok {

View File

@ -1,48 +1,44 @@
package keycode package keycode
type Button struct { import "n.eko.moe/neko/internal/types"
Name string
Code int
Keysym int
}
var LEFT_BUTTON = Button{ var LEFT_BUTTON = types.Button{
Name: "LEFT", Name: "LEFT",
Code: 0, Code: 0,
Keysym: 1, Keysym: 1,
} }
var CENTER_BUTTON = Button{ var CENTER_BUTTON = types.Button{
Name: "CENTER", Name: "CENTER",
Code: 1, Code: 1,
Keysym: 2, Keysym: 2,
} }
var RIGHT_BUTTON = Button{ var RIGHT_BUTTON = types.Button{
Name: "RIGHT", Name: "RIGHT",
Code: 2, Code: 2,
Keysym: 3, Keysym: 3,
} }
var SCROLL_UP_BUTTON = Button{ var SCROLL_UP_BUTTON = types.Button{
Name: "SCROLL_UP", Name: "SCROLL_UP",
Code: 3, Code: 3,
Keysym: 4, Keysym: 4,
} }
var SCROLL_DOWN_BUTTON = Button{ var SCROLL_DOWN_BUTTON = types.Button{
Name: "SCROLL_DOWN", Name: "SCROLL_DOWN",
Code: 4, Code: 4,
Keysym: 5, Keysym: 5,
} }
var SCROLL_LEFT_BUTTON = Button{ var SCROLL_LEFT_BUTTON = types.Button{
Name: "SCROLL_LEFT", Name: "SCROLL_LEFT",
Code: 5, Code: 5,
Keysym: 6, Keysym: 6,
} }
var SCROLL_RIGHT_BUTTON = Button{ var SCROLL_RIGHT_BUTTON = types.Button{
Name: "SCROLL_RIGHT", Name: "SCROLL_RIGHT",
Code: 6, Code: 6,
Keysym: 7, Keysym: 7,

View File

@ -1,699 +1,694 @@
package keycode package keycode
type Key struct { import "n.eko.moe/neko/internal/types"
Name string
Value string
Code int
Keysym int
}
var BACKSPACE = Key{ var BACKSPACE = types.Key{
Name: "BACKSPACE", Name: "BACKSPACE",
Value: "BackSpace", Value: "BackSpace",
Code: 8, Code: 8,
Keysym: int(0xff08), Keysym: int(0xff08),
} }
var TAB = Key{ var TAB = types.Key{
Name: "TAB", Name: "TAB",
Value: "Tab", Value: "Tab",
Code: 9, Code: 9,
Keysym: int(0xFF09), Keysym: int(0xFF09),
} }
var CLEAR = Key{ var CLEAR = types.Key{
Name: "CLEAR", Name: "CLEAR",
Value: "Clear", Value: "Clear",
Code: 12, Code: 12,
Keysym: int(0xFF0B), Keysym: int(0xFF0B),
} }
var ENTER = Key{ var ENTER = types.Key{
Name: "ENTER", Name: "ENTER",
Value: "Enter", Value: "Enter",
Code: 13, Code: 13,
Keysym: int(0xFF0D), Keysym: int(0xFF0D),
} }
var SHIFT = Key{ var SHIFT = types.Key{
Name: "SHIFT", Name: "SHIFT",
Value: "Shift", Value: "Shift",
Code: 16, Code: 16,
Keysym: int(0xFFE1), Keysym: int(0xFFE1),
} }
var CTRL = Key{ var CTRL = types.Key{
Name: "CTRL", Name: "CTRL",
Value: "Ctrl", Value: "Ctrl",
Code: 17, Code: 17,
Keysym: int(0xFFE3), Keysym: int(0xFFE3),
} }
var ALT = Key{ var ALT = types.Key{
Name: "ALT", Name: "ALT",
Value: "Alt", Value: "Alt",
Code: 18, Code: 18,
Keysym: int(0xFFE9), Keysym: int(0xFFE9),
} }
var PAUSE = Key{ var PAUSE = types.Key{
Name: "PAUSE", Name: "PAUSE",
Value: "Pause", Value: "Pause",
Code: 19, Code: 19,
Keysym: int(0xFF13), Keysym: int(0xFF13),
} }
var CAPS_LOCK = Key{ var CAPS_LOCK = types.Key{
Name: "CAPS_LOCK", Name: "CAPS_LOCK",
Value: "Caps Lock", Value: "Caps Lock",
Code: 20, Code: 20,
Keysym: int(0xFFE5), Keysym: int(0xFFE5),
} }
var ESCAPE = Key{ var ESCAPE = types.Key{
Name: "ESCAPE", Name: "ESCAPE",
Value: "Escape", Value: "Escape",
Code: 27, Code: 27,
Keysym: int(0xFF1B), Keysym: int(0xFF1B),
} }
var SPACE = Key{ var SPACE = types.Key{
Name: "SPACE", Name: "SPACE",
Value: " ", Value: " ",
Code: 32, Code: 32,
Keysym: int(0x0020), Keysym: int(0x0020),
} }
var PAGE_UP = Key{ var PAGE_UP = types.Key{
Name: "PAGE_UP", Name: "PAGE_UP",
Value: "Page Up", Value: "Page Up",
Code: 33, Code: 33,
Keysym: int(0xFF55), Keysym: int(0xFF55),
} }
var PAGE_DOWN = Key{ var PAGE_DOWN = types.Key{
Name: "PAGE_DOWN", Name: "PAGE_DOWN",
Value: "Page Down", Value: "Page Down",
Code: 34, Code: 34,
Keysym: int(0xFF56), Keysym: int(0xFF56),
} }
var END = Key{ var END = types.Key{
Name: "END", Name: "END",
Value: "End", Value: "End",
Code: 35, Code: 35,
Keysym: int(0xFF57), Keysym: int(0xFF57),
} }
var HOME = Key{ var HOME = types.Key{
Name: "HOME", Name: "HOME",
Value: "Home", Value: "Home",
Code: 36, Code: 36,
Keysym: int(0xFF50), Keysym: int(0xFF50),
} }
var LEFT_ARROW = Key{ var LEFT_ARROW = types.Key{
Name: "LEFT_ARROW", Name: "LEFT_ARROW",
Value: "Left Arrow", Value: "Left Arrow",
Code: 37, Code: 37,
Keysym: int(0xFF51), Keysym: int(0xFF51),
} }
var UP_ARROW = Key{ var UP_ARROW = types.Key{
Name: "UP_ARROW", Name: "UP_ARROW",
Value: "Up Arrow", Value: "Up Arrow",
Code: 38, Code: 38,
Keysym: int(0xFF52), Keysym: int(0xFF52),
} }
var RIGHT_ARROW = Key{ var RIGHT_ARROW = types.Key{
Name: "RIGHT_ARROW", Name: "RIGHT_ARROW",
Value: "Right Arrow", Value: "Right Arrow",
Code: 39, Code: 39,
Keysym: int(0xFF53), Keysym: int(0xFF53),
} }
var DOWN_ARROW = Key{ var DOWN_ARROW = types.Key{
Name: "DOWN_ARROW", Name: "DOWN_ARROW",
Value: "Down Arrow", Value: "Down Arrow",
Code: 40, Code: 40,
Keysym: int(0xFF54), Keysym: int(0xFF54),
} }
var INSERT = Key{ var INSERT = types.Key{
Name: "INSERT", Name: "INSERT",
Value: "Insert", Value: "Insert",
Code: 45, Code: 45,
Keysym: int(0xFF63), Keysym: int(0xFF63),
} }
var DELETE = Key{ var DELETE = types.Key{
Name: "DELETE", Name: "DELETE",
Value: "Delete", Value: "Delete",
Code: 46, Code: 46,
Keysym: int(0xFFFF), Keysym: int(0xFFFF),
} }
var KEY_0 = Key{ var KEY_0 = types.Key{
Name: "KEY_0", Name: "KEY_0",
Value: "0", Value: "0",
Code: 48, Code: 48,
Keysym: int(0x0030), Keysym: int(0x0030),
} }
var KEY_1 = Key{ var KEY_1 = types.Key{
Name: "KEY_1", Name: "KEY_1",
Value: "1", Value: "1",
Code: 49, Code: 49,
Keysym: int(0x0031), Keysym: int(0x0031),
} }
var KEY_2 = Key{ var KEY_2 = types.Key{
Name: "KEY_2", Name: "KEY_2",
Value: "2", Value: "2",
Code: 50, Code: 50,
Keysym: int(0x0032), Keysym: int(0x0032),
} }
var KEY_3 = Key{ var KEY_3 = types.Key{
Name: "KEY_3", Name: "KEY_3",
Value: "3", Value: "3",
Code: 51, Code: 51,
Keysym: int(0x0033), Keysym: int(0x0033),
} }
var KEY_4 = Key{ var KEY_4 = types.Key{
Name: "KEY_4", Name: "KEY_4",
Value: "4", Value: "4",
Code: 52, Code: 52,
Keysym: int(0x0034), Keysym: int(0x0034),
} }
var KEY_5 = Key{ var KEY_5 = types.Key{
Name: "KEY_5", Name: "KEY_5",
Value: "5", Value: "5",
Code: 53, Code: 53,
Keysym: int(0x0035), Keysym: int(0x0035),
} }
var KEY_6 = Key{ var KEY_6 = types.Key{
Name: "KEY_6", Name: "KEY_6",
Value: "6", Value: "6",
Code: 54, Code: 54,
Keysym: int(0x0036), Keysym: int(0x0036),
} }
var KEY_7 = Key{ var KEY_7 = types.Key{
Name: "KEY_7", Name: "KEY_7",
Value: "7", Value: "7",
Code: 55, Code: 55,
Keysym: int(0x0037), Keysym: int(0x0037),
} }
var KEY_8 = Key{ var KEY_8 = types.Key{
Name: "KEY_8", Name: "KEY_8",
Value: "8", Value: "8",
Code: 56, Code: 56,
Keysym: int(0x0038), Keysym: int(0x0038),
} }
var KEY_9 = Key{ var KEY_9 = types.Key{
Name: "KEY_9", Name: "KEY_9",
Value: "9", Value: "9",
Code: 57, Code: 57,
Keysym: int(0x0039), Keysym: int(0x0039),
} }
var KEY_A = Key{ var KEY_A = types.Key{
Name: "KEY_A", Name: "KEY_A",
Value: "a", Value: "a",
Code: 65, Code: 65,
Keysym: int(0x0061), Keysym: int(0x0061),
} }
var KEY_B = Key{ var KEY_B = types.Key{
Name: "KEY_B", Name: "KEY_B",
Value: "b", Value: "b",
Code: 66, Code: 66,
Keysym: int(0x0062), Keysym: int(0x0062),
} }
var KEY_C = Key{ var KEY_C = types.Key{
Name: "KEY_C", Name: "KEY_C",
Value: "c", Value: "c",
Code: 67, Code: 67,
Keysym: int(0x0063), Keysym: int(0x0063),
} }
var KEY_D = Key{ var KEY_D = types.Key{
Name: "KEY_D", Name: "KEY_D",
Value: "d", Value: "d",
Code: 68, Code: 68,
Keysym: int(0x0064), Keysym: int(0x0064),
} }
var KEY_E = Key{ var KEY_E = types.Key{
Name: "KEY_E", Name: "KEY_E",
Value: "e", Value: "e",
Code: 69, Code: 69,
Keysym: int(0x0065), Keysym: int(0x0065),
} }
var KEY_F = Key{ var KEY_F = types.Key{
Name: "KEY_F", Name: "KEY_F",
Value: "f", Value: "f",
Code: 70, Code: 70,
Keysym: int(0x0066), Keysym: int(0x0066),
} }
var KEY_G = Key{ var KEY_G = types.Key{
Name: "KEY_G", Name: "KEY_G",
Value: "g", Value: "g",
Code: 71, Code: 71,
Keysym: int(0x0067), Keysym: int(0x0067),
} }
var KEY_H = Key{ var KEY_H = types.Key{
Name: "KEY_H", Name: "KEY_H",
Value: "h", Value: "h",
Code: 72, Code: 72,
Keysym: int(0x0068), Keysym: int(0x0068),
} }
var KEY_I = Key{ var KEY_I = types.Key{
Name: "KEY_I", Name: "KEY_I",
Value: "i", Value: "i",
Code: 73, Code: 73,
Keysym: int(0x0069), Keysym: int(0x0069),
} }
var KEY_J = Key{ var KEY_J = types.Key{
Name: "KEY_J", Name: "KEY_J",
Value: "j", Value: "j",
Code: 74, Code: 74,
Keysym: int(0x006a), Keysym: int(0x006a),
} }
var KEY_K = Key{ var KEY_K = types.Key{
Name: "KEY_K", Name: "KEY_K",
Value: "k", Value: "k",
Code: 75, Code: 75,
Keysym: int(0x006b), Keysym: int(0x006b),
} }
var KEY_L = Key{ var KEY_L = types.Key{
Name: "KEY_L", Name: "KEY_L",
Value: "l", Value: "l",
Code: 76, Code: 76,
Keysym: int(0x006c), Keysym: int(0x006c),
} }
var KEY_M = Key{ var KEY_M = types.Key{
Name: "KEY_M", Name: "KEY_M",
Value: "m", Value: "m",
Code: 77, Code: 77,
Keysym: int(0x006d), Keysym: int(0x006d),
} }
var KEY_N = Key{ var KEY_N = types.Key{
Name: "KEY_N", Name: "KEY_N",
Value: "n", Value: "n",
Code: 78, Code: 78,
Keysym: int(0x006e), Keysym: int(0x006e),
} }
var KEY_O = Key{ var KEY_O = types.Key{
Name: "KEY_O", Name: "KEY_O",
Value: "o", Value: "o",
Code: 79, Code: 79,
Keysym: int(0x006f), Keysym: int(0x006f),
} }
var KEY_P = Key{ var KEY_P = types.Key{
Name: "KEY_P", Name: "KEY_P",
Value: "p", Value: "p",
Code: 80, Code: 80,
Keysym: int(0x0070), Keysym: int(0x0070),
} }
var KEY_Q = Key{ var KEY_Q = types.Key{
Name: "KEY_Q", Name: "KEY_Q",
Value: "q", Value: "q",
Code: 81, Code: 81,
Keysym: int(0x0071), Keysym: int(0x0071),
} }
var KEY_R = Key{ var KEY_R = types.Key{
Name: "KEY_R", Name: "KEY_R",
Value: "r", Value: "r",
Code: 82, Code: 82,
Keysym: int(0x0072), Keysym: int(0x0072),
} }
var KEY_S = Key{ var KEY_S = types.Key{
Name: "KEY_S", Name: "KEY_S",
Value: "s", Value: "s",
Code: 83, Code: 83,
Keysym: int(0x0073), Keysym: int(0x0073),
} }
var KEY_T = Key{ var KEY_T = types.Key{
Name: "KEY_T", Name: "KEY_T",
Value: "t", Value: "t",
Code: 84, Code: 84,
Keysym: int(0x0074), Keysym: int(0x0074),
} }
var KEY_U = Key{ var KEY_U = types.Key{
Name: "KEY_U", Name: "KEY_U",
Value: "u", Value: "u",
Code: 85, Code: 85,
Keysym: int(0x0075), Keysym: int(0x0075),
} }
var KEY_V = Key{ var KEY_V = types.Key{
Name: "KEY_V", Name: "KEY_V",
Value: "v", Value: "v",
Code: 86, Code: 86,
Keysym: int(0x0076), Keysym: int(0x0076),
} }
var KEY_W = Key{ var KEY_W = types.Key{
Name: "KEY_W", Name: "KEY_W",
Value: "w", Value: "w",
Code: 87, Code: 87,
Keysym: int(0x0077), Keysym: int(0x0077),
} }
var KEY_X = Key{ var KEY_X = types.Key{
Name: "KEY_X", Name: "KEY_X",
Value: "x", Value: "x",
Code: 88, Code: 88,
Keysym: int(0x0078), Keysym: int(0x0078),
} }
var KEY_Y = Key{ var KEY_Y = types.Key{
Name: "KEY_Y", Name: "KEY_Y",
Value: "y", Value: "y",
Code: 89, Code: 89,
Keysym: int(0x0079), Keysym: int(0x0079),
} }
var KEY_Z = Key{ var KEY_Z = types.Key{
Name: "KEY_Z", Name: "KEY_Z",
Value: "z", Value: "z",
Code: 90, Code: 90,
Keysym: int(0x007a), Keysym: int(0x007a),
} }
var WIN_LEFT = Key{ var WIN_LEFT = types.Key{
Name: "WIN_LEFT", Name: "WIN_LEFT",
Value: "Win Left", Value: "Win Left",
Code: 91, Code: 91,
Keysym: int(0xFFEB), Keysym: int(0xFFEB),
} }
var WIN_RIGHT = Key{ var WIN_RIGHT = types.Key{
Name: "WIN_RIGHT", Name: "WIN_RIGHT",
Value: "Win Right", Value: "Win Right",
Code: 92, Code: 92,
Keysym: int(0xFF67), Keysym: int(0xFF67),
} }
var PAD_0 = Key{ var PAD_0 = types.Key{
Name: "PAD_0", Name: "PAD_0",
Value: "Num Pad 0", Value: "Num Pad 0",
Code: 96, Code: 96,
Keysym: int(0xFFB0), Keysym: int(0xFFB0),
} }
var PAD_1 = Key{ var PAD_1 = types.Key{
Name: "PAD_1", Name: "PAD_1",
Value: "Num Pad 1", Value: "Num Pad 1",
Code: 97, Code: 97,
Keysym: int(0xFFB1), Keysym: int(0xFFB1),
} }
var PAD_2 = Key{ var PAD_2 = types.Key{
Name: "PAD_2", Name: "PAD_2",
Value: "Num Pad 2", Value: "Num Pad 2",
Code: 98, Code: 98,
Keysym: int(0xFFB2), Keysym: int(0xFFB2),
} }
var PAD_3 = Key{ var PAD_3 = types.Key{
Name: "PAD_3", Name: "PAD_3",
Value: "Num Pad 3", Value: "Num Pad 3",
Code: 99, Code: 99,
Keysym: int(0xFFB3), Keysym: int(0xFFB3),
} }
var PAD_4 = Key{ var PAD_4 = types.Key{
Name: "PAD_4", Name: "PAD_4",
Value: "Num Pad 4", Value: "Num Pad 4",
Code: 100, Code: 100,
Keysym: int(0xFFB4), Keysym: int(0xFFB4),
} }
var PAD_5 = Key{ var PAD_5 = types.Key{
Name: "PAD_5", Name: "PAD_5",
Value: "Num Pad 5", Value: "Num Pad 5",
Code: 101, Code: 101,
Keysym: int(0xFFB5), Keysym: int(0xFFB5),
} }
var PAD_6 = Key{ var PAD_6 = types.Key{
Name: "PAD_6", Name: "PAD_6",
Value: "Num Pad 6", Value: "Num Pad 6",
Code: 102, Code: 102,
Keysym: int(0xFFB6), Keysym: int(0xFFB6),
} }
var PAD_7 = Key{ var PAD_7 = types.Key{
Name: "PAD_7", Name: "PAD_7",
Value: "Num Pad 7", Value: "Num Pad 7",
Code: 103, Code: 103,
Keysym: int(0xFFB7), Keysym: int(0xFFB7),
} }
var PAD_8 = Key{ var PAD_8 = types.Key{
Name: "PAD_8", Name: "PAD_8",
Value: "Num Pad 8", Value: "Num Pad 8",
Code: 104, Code: 104,
Keysym: int(0xFFB8), Keysym: int(0xFFB8),
} }
var PAD_9 = Key{ var PAD_9 = types.Key{
Name: "PAD_9", Name: "PAD_9",
Value: "Num Pad 9", Value: "Num Pad 9",
Code: 105, Code: 105,
Keysym: int(0xFFB9), Keysym: int(0xFFB9),
} }
var MULTIPLY = Key{ var MULTIPLY = types.Key{
Name: "MULTIPLY", Name: "MULTIPLY",
Value: "*", Value: "*",
Code: 106, Code: 106,
Keysym: int(0xFFAA), Keysym: int(0xFFAA),
} }
var ADD = Key{ var ADD = types.Key{
Name: "ADD", Name: "ADD",
Value: "+", Value: "+",
Code: 107, Code: 107,
Keysym: int(0xFFAB), Keysym: int(0xFFAB),
} }
var SUBTRACT = Key{ var SUBTRACT = types.Key{
Name: "SUBTRACT", Name: "SUBTRACT",
Value: "-", Value: "-",
Code: 109, Code: 109,
Keysym: int(0xFFAD), Keysym: int(0xFFAD),
} }
var DECIMAL = Key{ var DECIMAL = types.Key{
Name: "DECIMAL", Name: "DECIMAL",
Value: ".", Value: ".",
Code: 110, Code: 110,
Keysym: int(0xFFAE), Keysym: int(0xFFAE),
} }
var DIVIDE = Key{ var DIVIDE = types.Key{
Name: "DIVIDE", Name: "DIVIDE",
Value: "/", Value: "/",
Code: 111, Code: 111,
Keysym: int(0xFFAF), Keysym: int(0xFFAF),
} }
var KEY_F1 = Key{ var KEY_F1 = types.Key{
Name: "KEY_F1", Name: "KEY_F1",
Value: "f1", Value: "f1",
Code: 112, Code: 112,
Keysym: int(0xFFBE), Keysym: int(0xFFBE),
} }
var KEY_F2 = Key{ var KEY_F2 = types.Key{
Name: "KEY_F2", Name: "KEY_F2",
Value: "f2", Value: "f2",
Code: 113, Code: 113,
Keysym: int(0xFFBF), Keysym: int(0xFFBF),
} }
var KEY_F3 = Key{ var KEY_F3 = types.Key{
Name: "KEY_F3", Name: "KEY_F3",
Value: "f3", Value: "f3",
Code: 114, Code: 114,
Keysym: int(0xFFC0), Keysym: int(0xFFC0),
} }
var KEY_F4 = Key{ var KEY_F4 = types.Key{
Name: "KEY_F4", Name: "KEY_F4",
Value: "f4", Value: "f4",
Code: 115, Code: 115,
Keysym: int(0xFFC1), Keysym: int(0xFFC1),
} }
var KEY_F5 = Key{ var KEY_F5 = types.Key{
Name: "KEY_F5", Name: "KEY_F5",
Value: "f5", Value: "f5",
Code: 116, Code: 116,
Keysym: int(0xFFC2), Keysym: int(0xFFC2),
} }
var KEY_F6 = Key{ var KEY_F6 = types.Key{
Name: "KEY_F6", Name: "KEY_F6",
Value: "f6", Value: "f6",
Code: 117, Code: 117,
Keysym: int(0xFFC3), Keysym: int(0xFFC3),
} }
var KEY_F7 = Key{ var KEY_F7 = types.Key{
Name: "KEY_F7", Name: "KEY_F7",
Value: "f7", Value: "f7",
Code: 118, Code: 118,
Keysym: int(0xFFC4), Keysym: int(0xFFC4),
} }
var KEY_F8 = Key{ var KEY_F8 = types.Key{
Name: "KEY_F8", Name: "KEY_F8",
Value: "f8", Value: "f8",
Code: 119, Code: 119,
Keysym: int(0xFFC5), Keysym: int(0xFFC5),
} }
var KEY_F9 = Key{ var KEY_F9 = types.Key{
Name: "KEY_F9", Name: "KEY_F9",
Value: "f9", Value: "f9",
Code: 120, Code: 120,
Keysym: int(0xFFC6), Keysym: int(0xFFC6),
} }
var KEY_F10 = Key{ var KEY_F10 = types.Key{
Name: "KEY_F10", Name: "KEY_F10",
Value: "f10", Value: "f10",
Code: 121, Code: 121,
Keysym: int(0xFFC7), Keysym: int(0xFFC7),
} }
var KEY_F11 = Key{ var KEY_F11 = types.Key{
Name: "KEY_F11", Name: "KEY_F11",
Value: "f11", Value: "f11",
Code: 122, Code: 122,
Keysym: int(0xFFC8), Keysym: int(0xFFC8),
} }
var KEY_F12 = Key{ var KEY_F12 = types.Key{
Name: "KEY_F12", Name: "KEY_F12",
Value: "f12", Value: "f12",
Code: 123, Code: 123,
Keysym: int(0xFFC9), Keysym: int(0xFFC9),
} }
var NUM_LOCK = Key{ var NUM_LOCK = types.Key{
Name: "NUM_LOCK", Name: "NUM_LOCK",
Value: "Num Lock", Value: "Num Lock",
Code: 144, Code: 144,
Keysym: int(0xFF7F), Keysym: int(0xFF7F),
} }
var SCROLL_LOCK = Key{ var SCROLL_LOCK = types.Key{
Name: "SCROLL_LOCK", Name: "SCROLL_LOCK",
Value: "Scroll Lock", Value: "Scroll Lock",
Code: 145, Code: 145,
Keysym: int(0xFF14), Keysym: int(0xFF14),
} }
var SEMI_COLON = Key{ var SEMI_COLON = types.Key{
Name: "SEMI_COLON", Name: "SEMI_COLON",
Value: ";", Value: ";",
Code: 186, Code: 186,
Keysym: int(0x003b), Keysym: int(0x003b),
} }
var EQUAL = Key{ var EQUAL = types.Key{
Name: "EQUAL", Name: "EQUAL",
Value: "=", Value: "=",
Code: 187, Code: 187,
Keysym: int(0x003d), Keysym: int(0x003d),
} }
var COMMA = Key{ var COMMA = types.Key{
Name: "COMMA", Name: "COMMA",
Value: ",", Value: ",",
Code: 188, Code: 188,
Keysym: int(0x002c), Keysym: int(0x002c),
} }
var DASH = Key{ var DASH = types.Key{
Name: "DASH", Name: "DASH",
Value: "-", Value: "-",
Code: 189, Code: 189,
Keysym: int(0x002d), Keysym: int(0x002d),
} }
var PERIOD = Key{ var PERIOD = types.Key{
Name: "PERIOD", Name: "PERIOD",
Value: ".", Value: ".",
Code: 190, Code: 190,
Keysym: int(0x002e), Keysym: int(0x002e),
} }
var FORWARD_SLASH = Key{ var FORWARD_SLASH = types.Key{
Name: "FORWARD_SLASH", Name: "FORWARD_SLASH",
Value: "/", Value: "/",
Code: 191, Code: 191,
Keysym: int(0x002f), Keysym: int(0x002f),
} }
var GRAVE = Key{ var GRAVE = types.Key{
Name: "GRAVE", Name: "GRAVE",
Value: "`", Value: "`",
Code: 192, Code: 192,
Keysym: int(0x0060), Keysym: int(0x0060),
} }
var OPEN_BRACKET = Key{ var OPEN_BRACKET = types.Key{
Name: "OPEN_BRACKET", Name: "OPEN_BRACKET",
Value: "[", Value: "[",
Code: 219, Code: 219,
Keysym: int(0x005b), Keysym: int(0x005b),
} }
var BACK_SLASH = Key{ var BACK_SLASH = types.Key{
Name: "BACK_SLASH", Name: "BACK_SLASH",
Value: "\\", Value: "\\",
Code: 220, Code: 220,
Keysym: int(0x005c), Keysym: int(0x005c),
} }
var CLOSE_BRAKET = Key{ var CLOSE_BRAKET = types.Key{
Name: "CLOSE_BRAKET", Name: "CLOSE_BRAKET",
Value: "]", Value: "]",
Code: 221, Code: 221,
Keysym: int(0x005d), Keysym: int(0x005d),
} }
var SINGLE_QUOTE = Key{ var SINGLE_QUOTE = types.Key{
Name: "SINGLE_QUOTE", Name: "SINGLE_QUOTE",
Value: "'", Value: "'",
Code: 222, Code: 222,

View File

@ -27,8 +27,8 @@ import (
var ScreenConfigurations = make(map[int]types.ScreenConfiguration) var ScreenConfigurations = make(map[int]types.ScreenConfiguration)
var debounce = make(map[int]time.Time) var debounce = make(map[int]time.Time)
var buttons = make(map[int]keycode.Button) var buttons = make(map[int]types.Button)
var keys = make(map[int]keycode.Key) var keys = make(map[int]types.Key)
var mu = sync.Mutex{} var mu = sync.Mutex{}
func init() { func init() {
@ -167,7 +167,7 @@ func Scroll(x, y int) {
C.XScroll(C.int(x), C.int(y)) C.XScroll(C.int(x), C.int(y))
} }
func ButtonDown(code int) (*keycode.Button, error) { func ButtonDown(code int) (*types.Button, error) {
mu.Lock() mu.Lock()
defer mu.Unlock() defer mu.Unlock()
@ -186,7 +186,7 @@ func ButtonDown(code int) (*keycode.Button, error) {
return &button, nil return &button, nil
} }
func KeyDown(code int) (*keycode.Key, error) { func KeyDown(code int) (*types.Key, error) {
mu.Lock() mu.Lock()
defer mu.Unlock() defer mu.Unlock()
@ -205,7 +205,7 @@ func KeyDown(code int) (*keycode.Key, error) {
return &key, nil return &key, nil
} }
func ButtonUp(code int) (*keycode.Button, error) { func ButtonUp(code int) (*types.Button, error) {
mu.Lock() mu.Lock()
defer mu.Unlock() defer mu.Unlock()
@ -224,7 +224,7 @@ func ButtonUp(code int) (*keycode.Button, error) {
return &button, nil return &button, nil
} }
func KeyUp(code int) (*keycode.Key, error) { func KeyUp(code int) (*types.Key, error) {
mu.Lock() mu.Lock()
defer mu.Unlock() defer mu.Unlock()

View File

@ -7,6 +7,7 @@ import (
"runtime" "runtime"
"n.eko.moe/neko/internal/http" "n.eko.moe/neko/internal/http"
"n.eko.moe/neko/internal/remote"
"n.eko.moe/neko/internal/session" "n.eko.moe/neko/internal/session"
"n.eko.moe/neko/internal/types/config" "n.eko.moe/neko/internal/types/config"
"n.eko.moe/neko/internal/webrtc" "n.eko.moe/neko/internal/webrtc"
@ -59,6 +60,7 @@ func init() {
}, },
Root: &config.Root{}, Root: &config.Root{},
Server: &config.Server{}, Server: &config.Server{},
Remote: &config.Remote{},
WebRTC: &config.WebRTC{}, WebRTC: &config.WebRTC{},
WebSocket: &config.WebSocket{}, WebSocket: &config.WebSocket{},
} }
@ -96,13 +98,15 @@ func (i *Version) Details() string {
type Neko struct { type Neko struct {
Version *Version Version *Version
Root *config.Root Root *config.Root
Remote *config.Remote
Server *config.Server Server *config.Server
WebRTC *config.WebRTC WebRTC *config.WebRTC
WebSocket *config.WebSocket WebSocket *config.WebSocket
logger zerolog.Logger logger zerolog.Logger
server *http.Server server *http.Server
sessions *session.SessionManager sessionManager *session.SessionManager
remoteManager *remote.RemoteManager
webRTCManager *webrtc.WebRTCManager webRTCManager *webrtc.WebRTCManager
webSocketHandler *websocket.WebSocketHandler webSocketHandler *websocket.WebSocketHandler
} }
@ -112,24 +116,34 @@ func (neko *Neko) Preflight() {
} }
func (neko *Neko) Start() { func (neko *Neko) Start() {
sessions := session.New() sessionManager := session.New()
webRTCManager := webrtc.New(sessions, neko.WebRTC) remoteManager := remote.New(neko.Remote)
remoteManager.Start()
webRTCManager := webrtc.New(sessionManager, remoteManager, neko.WebRTC)
webRTCManager.Start() webRTCManager.Start()
webSocketHandler := websocket.New(sessions, webRTCManager, neko.WebSocket) webSocketHandler := websocket.New(sessionManager, remoteManager, webRTCManager, neko.WebSocket)
webSocketHandler.Start() webSocketHandler.Start()
server := http.New(neko.Server, webSocketHandler) server := http.New(neko.Server, webSocketHandler)
server.Start() server.Start()
neko.sessions = sessions neko.sessionManager = sessionManager
neko.remoteManager = remoteManager
neko.webRTCManager = webRTCManager neko.webRTCManager = webRTCManager
neko.webSocketHandler = webSocketHandler neko.webSocketHandler = webSocketHandler
neko.server = server neko.server = server
} }
func (neko *Neko) Shutdown() { func (neko *Neko) Shutdown() {
if err := neko.remoteManager.Shutdown(); err != nil {
neko.logger.Err(err).Msg("remote manager shutdown with an error")
} else {
neko.logger.Debug().Msg("remote manager shutdown")
}
if err := neko.webRTCManager.Shutdown(); err != nil { if err := neko.webRTCManager.Shutdown(); err != nil {
neko.logger.Err(err).Msg("webrtc manager shutdown with an error") neko.logger.Err(err).Msg("webrtc manager shutdown with an error")
} else { } else {