refactor: standardize constant names and improve goroutine handling across multiple files.

This commit is contained in:
Miroslav Šedivý
2026-04-08 15:16:47 +02:00
parent 79e498090d
commit a4f1cfbe0d
14 changed files with 85 additions and 88 deletions
+1 -1
View File
@@ -61,7 +61,7 @@ func (h *RoomHandler) clipboardGetImage(w http.ResponseWriter, r *http.Request)
/* TODO: Unused now.
func (h *RoomHandler) clipboardSetImage(w http.ResponseWriter, r *http.Request) error {
err := r.ParseMultipartForm(MAX_UPLOAD_SIZE)
err := r.ParseMultipartForm(multipartFormMaxMemory)
if err != nil {
return utils.HttpBadRequest("failed to parse multipart form").WithInternalErr(err)
}
+3 -3
View File
@@ -13,14 +13,14 @@ import (
// TODO: Extract file uploading to custom utility.
// maximum upload size of 32 MB
const maxUploadSize = 32 << 20
const multipartFormMaxMemory = 32 << 20
func (h *RoomHandler) uploadDrop(w http.ResponseWriter, r *http.Request) error {
if !h.desktop.IsUploadDropEnabled() {
return utils.HttpBadRequest("upload drop is disabled")
}
err := r.ParseMultipartForm(maxUploadSize)
err := r.ParseMultipartForm(multipartFormMaxMemory)
if err != nil {
return utils.HttpBadRequest("failed to parse multipart form").WithInternalErr(err)
}
@@ -95,7 +95,7 @@ func (h *RoomHandler) uploadDialogPost(w http.ResponseWriter, r *http.Request) e
return utils.HttpBadRequest("file chooser dialog is disabled")
}
err := r.ParseMultipartForm(maxUploadSize)
err := r.ParseMultipartForm(multipartFormMaxMemory)
if err != nil {
return utils.HttpBadRequest("failed to parse multipart form").WithInternalErr(err)
}
+4 -11
View File
@@ -87,11 +87,7 @@ func screencastNew(enabled bool, pipelineStr string) *ScreencastManagerCtx {
}),
}
manager.wg.Add(1)
go func() {
defer manager.wg.Done()
manager.wg.Go(func() {
ticker := time.NewTicker(screencastTimeout)
defer ticker.Stop()
@@ -105,7 +101,7 @@ func screencastNew(enabled bool, pipelineStr string) *ScreencastManagerCtx {
}
}
}
}()
})
return manager
}
@@ -212,12 +208,9 @@ func (manager *ScreencastManagerCtx) createPipeline() error {
return errors.New("timeouted while waiting for first image")
}
manager.wg.Add(1)
pipeline := manager.pipeline
go func() {
manager.wg.Go(func() {
manager.logger.Debug().Msg("started receiving images")
defer manager.wg.Done()
for {
image, ok := <-pipeline.Sample()
@@ -228,7 +221,7 @@ func (manager *ScreencastManagerCtx) createPipeline() error {
manager.setImage(image)
}
}()
})
return nil
}
+2 -5
View File
@@ -325,12 +325,9 @@ func (manager *StreamSinkManagerCtx) CreatePipeline() error {
manager.pipeline.AttachAppsink("appsink")
manager.pipeline.Play()
manager.wg.Add(1)
pipeline := manager.pipeline
go func() {
manager.wg.Go(func() {
manager.logger.Debug().Msg("started emitting samples")
defer manager.wg.Done()
for {
sample, ok := <-pipeline.Sample()
@@ -341,7 +338,7 @@ func (manager *StreamSinkManagerCtx) CreatePipeline() error {
manager.onSample(sample)
}
}()
})
manager.pipelinesCounter.Inc()
manager.pipelinesActive.Set(1)
+4 -3
View File
@@ -150,19 +150,20 @@ func NewVideoPipeline(rtpCodec codec.RTPCodec, display string, pipelineSrc strin
vbvbuf = bitrate
}
if hwenc == HwEncVAAPI {
switch hwenc {
case HwEncVAAPI:
if err := gst.CheckPlugins([]string{"vaapi"}); err != nil {
return "", err
}
pipelineStr = fmt.Sprintf(videoSrc+"video/x-raw,format=NV12 ! vaapih264enc rate-control=vbr bitrate=%d keyframe-period=180 quality-level=7 ! video/x-h264,stream-format=byte-stream,profile=constrained-baseline"+pipelineStr, display, fps, bitrate)
} else if hwenc == HwEncNVENC {
case HwEncNVENC:
if err := gst.CheckPlugins([]string{"nvcodec"}); err != nil {
return "", err
}
pipelineStr = fmt.Sprintf(videoSrc+"video/x-raw,format=NV12 ! nvh264enc name=encoder preset=2 gop-size=25 spatial-aq=true temporal-aq=true bitrate=%d vbv-buffer-size=%d rc-mode=6 ! h264parse config-interval=-1 ! video/x-h264,stream-format=byte-stream,profile=constrained-baseline"+pipelineStr, display, fps, bitrate, vbvbuf)
} else {
default:
// https://gstreamer.freedesktop.org/documentation/openh264/openh264enc.html?gi-language=c#openh264enc
// gstreamer1.0-plugins-bad
// openh264enc multi-thread=4 complexity=high bitrate=3072000 max-bitrate=4096000
+11 -11
View File
@@ -11,18 +11,21 @@ import (
)
const (
ClipboardTextPlainTarget = "UTF8_STRING"
ClipboardTextHtmlTarget = "text/html"
// MIME type for plain text in clipboard
clipboardTextPlainTarget = "UTF8_STRING"
// MIME type for HTML in clipboard
clipboardTextHtmlTarget = "text/html"
)
func (manager *DesktopManagerCtx) ClipboardGetText() (*types.ClipboardText, error) {
text, err := manager.ClipboardGetBinary(ClipboardTextPlainTarget)
text, err := manager.ClipboardGetBinary(clipboardTextPlainTarget)
if err != nil {
return nil, err
}
// Rich text must not always be available, can fail silently.
html, _ := manager.ClipboardGetBinary(ClipboardTextHtmlTarget)
html, _ := manager.ClipboardGetBinary(clipboardTextHtmlTarget)
return &types.ClipboardText{
Text: string(text),
@@ -36,10 +39,10 @@ func (manager *DesktopManagerCtx) ClipboardSetText(data types.ClipboardText) err
// is set, if available. Otherwise plain text.
if data.HTML != "" {
return manager.ClipboardSetBinary(ClipboardTextHtmlTarget, []byte(data.HTML))
return manager.ClipboardSetBinary(clipboardTextHtmlTarget, []byte(data.HTML))
}
return manager.ClipboardSetBinary(ClipboardTextPlainTarget, []byte(data.Text))
return manager.ClipboardSetBinary(clipboardTextPlainTarget, []byte(data.Text))
}
func (manager *DesktopManagerCtx) ClipboardGetBinary(mime string) ([]byte, error) {
@@ -114,17 +117,14 @@ func (manager *DesktopManagerCtx) ClipboardSetBinary(mime string, data []byte) e
case <-wait:
}
manager.wg.Add(1)
go func() {
defer manager.wg.Done()
manager.wg.Go(func() {
if err := cmd.Wait(); err != nil {
msg := strings.TrimSpace(stderr.String())
manager.logger.Err(err).Msgf("clipboard command finished with error: %s", msg)
} else {
manager.logger.Debug().Msg("clipboard command finished successfully")
}
}()
})
return nil
}
+11 -6
View File
@@ -6,11 +6,16 @@ import (
"github.com/m1k1o/neko/server/pkg/drop"
)
// repeat move event multiple times
const dropMoveRepeat = 4
const (
// repeat move event multiple times
dropMoveRepeat = 4
// wait after each repeated move event
const dropMoveDelay = 100 * time.Millisecond
// wait after each repeated move event
dropMoveDelay = 100 * time.Millisecond
// wait for drop to finish before giving up
dropFinishTimeout = 1 * time.Second
)
func (manager *DesktopManagerCtx) DropFiles(x int, y int, files []string) bool {
mu.Lock()
@@ -32,7 +37,7 @@ func (manager *DesktopManagerCtx) DropFiles(x int, y int, files []string) bool {
})
drop.Emmiter.Once("begin", func(payload ...any) {
for i := 0; i < dropMoveRepeat; i++ {
for range dropMoveRepeat {
manager.Move(x, y)
time.Sleep(dropMoveDelay)
}
@@ -57,7 +62,7 @@ func (manager *DesktopManagerCtx) DropFiles(x int, y int, files []string) bool {
select {
case succeeded := <-finished:
return succeeded
case <-time.After(1 * time.Second):
case <-time.After(dropFinishTimeout):
drop.CloseWindow()
return false
}
+12 -7
View File
@@ -7,14 +7,19 @@ import (
"github.com/m1k1o/neko/server/pkg/xorg"
)
// name of the window that is being controlled
const fileChooserDialogName = "Open File"
const (
// name of the window that is being controlled
fileChooserDialogName = "Open File"
// short sleep value between fake user interactions
const fileChooserDialogShortSleep = "0.2"
// short sleep value between fake user interactions
fileChooserDialogShortSleep = "0.2"
// long sleep value between fake user interactions
const fileChooserDialogLongSleep = "0.4"
// long sleep value between fake user interactions
fileChooserDialogLongSleep = "0.4"
// number of attempts to close the dialog
fileChooserDialogCloseAttempts = 5
)
func (manager *DesktopManagerCtx) HandleFileChooserDialog(uri string) error {
mu.Lock()
@@ -57,7 +62,7 @@ func (manager *DesktopManagerCtx) HandleFileChooserDialog(uri string) error {
}
func (manager *DesktopManagerCtx) CloseFileChooserDialog() {
for i := 0; i < 5; i++ {
for range fileChooserDialogCloseAttempts {
mu.Lock()
manager.logger.Debug().Msg("attempting to close file chooser dialog")
+11 -9
View File
@@ -17,6 +17,14 @@ import (
"github.com/m1k1o/neko/server/pkg/xorg"
)
const (
// debounce frequency for key events
debounceFrequency = 1 * time.Second
// debounce duration for key events
debounceDuration = 10 * time.Second
)
var mu = sync.Mutex{}
type DesktopManagerCtx struct {
@@ -100,16 +108,10 @@ func (manager *DesktopManagerCtx) Start() {
Msg("X event error occured")
})
manager.wg.Add(1)
go func() {
defer manager.wg.Done()
ticker := time.NewTicker(1 * time.Second)
manager.wg.Go(func() {
ticker := time.NewTicker(debounceFrequency)
defer ticker.Stop()
const debounceDuration = 10 * time.Second
for {
select {
case <-manager.shutdown:
@@ -119,7 +121,7 @@ func (manager *DesktopManagerCtx) Start() {
manager.input.Debounce(debounceDuration)
}
}
}()
})
}
func (manager *DesktopManagerCtx) OnBeforeScreenSizeChange(listener func()) {
@@ -20,7 +20,7 @@ import (
"github.com/rs/zerolog/log"
)
const MULTIPART_FORM_MAX_MEMORY = 32 << 20
const multipartFormMaxMemory = 32 << 20
func NewManager(
sessions types.SessionManager,
@@ -285,7 +285,7 @@ func (m *Manager) uploadFileHandler(w http.ResponseWriter, r *http.Request) erro
return utils.HttpForbidden("file transfer is disabled")
}
err = r.ParseMultipartForm(MULTIPART_FORM_MAX_MEMORY)
err = r.ParseMultipartForm(multipartFormMaxMemory)
if err != nil || r.MultipartForm == nil {
return utils.HttpBadRequest().
WithInternalErr(err).
+2 -2
View File
@@ -12,7 +12,7 @@ import (
// client is expected to reconnect within 5 second
// if some unexpected websocket disconnect happens
const WS_DELAYED_DURATION = 5 * time.Second
const wsDelayedDuration = 5 * time.Second
type SessionCtx struct {
id string
@@ -165,7 +165,7 @@ func (session *SessionCtx) DisconnectWebSocketPeer(websocketPeer types.WebSocket
var wsDelayedTimer *time.Timer
if delayed {
wsDelayedTimer = time.AfterFunc(WS_DELAYED_DURATION, func() {
wsDelayedTimer = time.AfterFunc(wsDelayedDuration, func() {
session.DisconnectWebSocketPeer(websocketPeer, false)
})
}
+6 -4
View File
@@ -284,9 +284,10 @@ func (met *metrics) NewICECandidate(candidate webrtc.ICECandidateStats) {
}
met.iceCandidates[candidate.ID] = struct{}{}
if candidate.Protocol == "udp" {
switch candidate.Protocol {
case "udp":
met.iceCandidatesUdpCount.Add(1)
} else if candidate.Protocol == "tcp" {
case "tcp":
met.iceCandidatesTcpCount.Add(1)
}
}
@@ -294,9 +295,10 @@ func (met *metrics) NewICECandidate(candidate webrtc.ICECandidateStats) {
func (met *metrics) SetICECandidatesUsed(candidates []webrtc.ICECandidateStats) {
udp, tcp := 0, 0
for _, candidate := range candidates {
if candidate.Protocol == "udp" {
switch candidate.Protocol {
case "udp":
udp++
} else if candidate.Protocol == "tcp" {
case "tcp":
tcp++
}
}
+4 -8
View File
@@ -401,10 +401,8 @@ func (peer *WebRTCPeerCtx) SetVideo(r types.PeerVideoRequest) error {
// send video signal if modified
if modified {
go func() {
// in goroutine because of mutex and we don't want to block
peer.session.Send(event.SIGNAL_VIDEO, peer.Video())
}()
// in goroutine because of mutex and we don't want to block
go peer.session.Send(event.SIGNAL_VIDEO, peer.Video())
}
return nil
@@ -455,10 +453,8 @@ func (peer *WebRTCPeerCtx) SetAudio(r types.PeerAudioRequest) error {
// send video signal if modified
if modified {
go func() {
// in goroutine because of mutex and we don't want to block
peer.session.Send(event.SIGNAL_AUDIO, peer.Audio())
}()
// in goroutine because of mutex and we don't want to block
go peer.session.Send(event.SIGNAL_AUDIO, peer.Audio())
}
return nil
+12 -16
View File
@@ -18,14 +18,16 @@ import (
"github.com/m1k1o/neko/server/pkg/utils"
)
// send pings to peer with this period - must be less than pongWait
const pingPeriod = 10 * time.Second
const (
// send pings to peer with this period - must be less than pongWait
pingPeriod = 10 * time.Second
// period for sending inactive cursor messages
const inactiveCursorsPeriod = 750 * time.Millisecond
// period for sending inactive cursor messages
inactiveCursorsPeriod = 750 * time.Millisecond
// maximum payload length for logging
const maxPayloadLogLength = 10_000
// maximum payload length for logging
maxPayloadLogLength = 10_000
)
// events that are not logged in debug mode
var nologEvents = []string{
@@ -309,10 +311,7 @@ func (manager *WebSocketManagerCtx) handle(connection *websocket.Conn, peer type
ticker := time.NewTicker(pingPeriod)
defer ticker.Stop()
manager.wg.Add(1)
go func() {
defer manager.wg.Done()
manager.wg.Go(func() {
for {
_, raw, err := connection.ReadMessage()
if err != nil {
@@ -322,7 +321,7 @@ func (manager *WebSocketManagerCtx) handle(connection *websocket.Conn, peer type
bytes <- raw
}
}()
})
for {
select {
@@ -381,10 +380,7 @@ func (manager *WebSocketManagerCtx) startInactiveCursors() {
manager.logger.Info().Msg("starting inactive cursors handler")
manager.shutdownInactiveCursors = make(chan struct{})
manager.wg.Add(1)
go func() {
defer manager.wg.Done()
manager.wg.Go(func() {
ticker := time.NewTicker(inactiveCursorsPeriod)
defer ticker.Stop()
@@ -424,7 +420,7 @@ func (manager *WebSocketManagerCtx) startInactiveCursors() {
manager.sessions.InactiveCursorsBroadcast(event.SESSION_CURSORS, sessionCursors)
}
}
}()
})
}
func (manager *WebSocketManagerCtx) stopInactiveCursors() {