Complete implementation of auto-show/hide an item in OBS on mouse-focus in XServer

This commit is contained in:
Dmitrii Okunev
2024-10-18 17:28:35 +01:00
parent 38bba4bbd1
commit f390abc6e2
25 changed files with 944 additions and 750 deletions

View File

@@ -60,16 +60,20 @@ $(GOPATH)/bin/pkg-config-wrapper:
sh -c 'cd 3rdparty/amd64/windows && wget https://github.com/BtbN/FFmpeg-Builds/releases/download/autobuild-2024-09-29-12-53/ffmpeg-n7.0.2-19-g45ecf80f0e-win64-gpl-shared-7.0.zip && unzip ffmpeg-n7.0.2-19-g45ecf80f0e-win64-gpl-shared-7.0.zip && rm -f ffmpeg-n7.0.2-19-g45ecf80f0e-win64-gpl-shared-7.0.zip' sh -c 'cd 3rdparty/amd64/windows && wget https://github.com/BtbN/FFmpeg-Builds/releases/download/autobuild-2024-09-29-12-53/ffmpeg-n7.0.2-19-g45ecf80f0e-win64-gpl-shared-7.0.zip && unzip ffmpeg-n7.0.2-19-g45ecf80f0e-win64-gpl-shared-7.0.zip && rm -f ffmpeg-n7.0.2-19-g45ecf80f0e-win64-gpl-shared-7.0.zip'
streampanel-linux-amd64: builddir streampanel-linux-amd64: builddir
CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build $(GOBUILD_FLAGS) -o build/streampanel-linux-amd64 ./cmd/streampanel $(eval INSTALL_DEST?=build/streampanel-linux-amd64)
CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build $(GOBUILD_FLAGS) -o "$(INSTALL_DEST)" ./cmd/streampanel
streampanel-linux-arm64: builddir streampanel-linux-arm64: builddir
CGO_ENABLED=1 GOOS=linux GOARCH=arm64 go build $(GOBUILD_FLAGS) -o build/streampanel-linux-arm64 ./cmd/streampanel $(eval INSTALL_DEST?=build/streampanel-linux-arm64)
CGO_ENABLED=1 GOOS=linux GOARCH=arm64 go build $(GOBUILD_FLAGS) -o "$(INSTALL_DEST)" ./cmd/streampanel
streampanel-macos-amd64: builddir streampanel-macos-amd64: builddir
CGO_ENABLED=1 GOOS=darwin GOARCH=amd64 go build $(GOBUILD_FLAGS) -o build/streampanel-macos-amd64 ./cmd/streampanel $(eval INSTALL_DEST?=build/streampanel-macos-amd64)
CGO_ENABLED=1 GOOS=darwin GOARCH=amd64 go build $(GOBUILD_FLAGS) -o "$(INSTALL_DEST)" ./cmd/streampanel
streampanel-macos-arm64: builddir streampanel-macos-arm64: builddir
CGO_ENABLED=1 GOOS=darwin GOARCH=arm64 go build $(GOBUILD_FLAGS) -o build/streampanel-macos-arm64 ./cmd/streampanel $(eval INSTALL_DEST?=build/streampanel-macos-arm64)
CGO_ENABLED=1 GOOS=darwin GOARCH=arm64 go build $(GOBUILD_FLAGS) -o "$(INSTALL_DEST)" ./cmd/streampanel
3rdparty/arm64/termux-packages: 3rdparty/arm64/termux-packages:
mkdir -p 3rdparty/arm64/ mkdir -p 3rdparty/arm64/

View File

@@ -196,6 +196,9 @@ func main() {
if obsGRPCClose != nil { if obsGRPCClose != nil {
defer obsGRPCClose() defer obsGRPCClose()
} }
if err != nil {
log.Fatal(err)
}
obs_grpc.RegisterOBSServer(grpcServer, obsGRPC) obs_grpc.RegisterOBSServer(grpcServer, obsGRPC)
streamd_grpc.RegisterStreamDServer(grpcServer, streamdGRPC) streamd_grpc.RegisterStreamDServer(grpcServer, streamdGRPC)
l.Infof("started server at %s", *listenAddr) l.Infof("started server at %s", *listenAddr)

11
go.mod
View File

@@ -39,7 +39,6 @@ require (
dario.cat/mergo v1.0.0 // indirect dario.cat/mergo v1.0.0 // indirect
fyne.io/systray v1.11.0 // indirect fyne.io/systray v1.11.0 // indirect
github.com/BurntSushi/toml v1.4.0 // indirect github.com/BurntSushi/toml v1.4.0 // indirect
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 // indirect
github.com/MicahParks/jwkset v0.5.20 // indirect github.com/MicahParks/jwkset v0.5.20 // indirect
github.com/MicahParks/keyfunc/v3 v3.3.5 // indirect github.com/MicahParks/keyfunc/v3 v3.3.5 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect
@@ -59,7 +58,6 @@ require (
github.com/cloudwego/iasm v0.2.0 // indirect github.com/cloudwego/iasm v0.2.0 // indirect
github.com/cyphar/filepath-securejoin v0.2.4 // indirect github.com/cyphar/filepath-securejoin v0.2.4 // indirect
github.com/datarhei/gosrt v0.7.0 // indirect github.com/datarhei/gosrt v0.7.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/emirpasic/gods v1.18.1 // indirect github.com/emirpasic/gods v1.18.1 // indirect
github.com/fatih/color v1.10.0 // indirect github.com/fatih/color v1.10.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect
@@ -81,6 +79,7 @@ require (
github.com/go-ng/sort v0.0.0-20220617173827-2cc7cd04f7c7 // indirect github.com/go-ng/sort v0.0.0-20220617173827-2cc7cd04f7c7 // indirect
github.com/go-ng/xatomic v0.0.0-20230519181013-85c0ec87e55f // indirect github.com/go-ng/xatomic v0.0.0-20230519181013-85c0ec87e55f // indirect
github.com/go-ng/xsort v0.0.0-20220617174223-1d146907bccc // indirect github.com/go-ng/xsort v0.0.0-20220617174223-1d146907bccc // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.20.0 // indirect github.com/go-playground/validator/v10 v10.20.0 // indirect
@@ -138,12 +137,15 @@ require (
github.com/skeema/knownhosts v1.2.2 // indirect github.com/skeema/knownhosts v1.2.2 // indirect
github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c // indirect github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c // indirect
github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef // indirect github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef // indirect
github.com/tklauser/go-sysconf v0.3.14 // indirect
github.com/tklauser/numcpus v0.8.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect github.com/ugorji/go/codec v1.2.12 // indirect
github.com/xaionaro-go/spinlock v0.0.0-20200518175509-30e6d1ce68a1 // indirect github.com/xaionaro-go/spinlock v0.0.0-20200518175509-30e6d1ce68a1 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
github.com/yuin/goldmark v1.7.1 // indirect github.com/yuin/goldmark v1.7.1 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
github.com/yutopp/go-amf0 v0.1.0 // indirect github.com/yutopp/go-amf0 v0.1.0 // indirect
go.opencensus.io v0.24.0 // indirect go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
@@ -173,6 +175,7 @@ require (
require ( require (
fyne.io/fyne/v2 v2.5.0 fyne.io/fyne/v2 v2.5.0
github.com/AgustinSRG/go-child-process-manager v1.0.1 github.com/AgustinSRG/go-child-process-manager v1.0.1
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802
github.com/DataDog/gostackparse v0.6.0 github.com/DataDog/gostackparse v0.6.0
github.com/DexterLB/mpvipc v0.0.0-20230829142118-145d6eabdc37 github.com/DexterLB/mpvipc v0.0.0-20230829142118-145d6eabdc37
github.com/adrg/libvlc-go/v3 v3.1.5 github.com/adrg/libvlc-go/v3 v3.1.5
@@ -185,6 +188,7 @@ require (
github.com/blang/mpv v0.0.0-20160810175505-d56d7352e068 github.com/blang/mpv v0.0.0-20160810175505-d56d7352e068
github.com/bluenviron/gortsplib/v4 v4.11.0 github.com/bluenviron/gortsplib/v4 v4.11.0
github.com/chai2010/webp v1.1.1 github.com/chai2010/webp v1.1.1
github.com/davecgh/go-spew v1.1.1
github.com/dustin/go-humanize v1.0.1 github.com/dustin/go-humanize v1.0.1
github.com/getsentry/sentry-go v0.28.1 github.com/getsentry/sentry-go v0.28.1
github.com/go-git/go-git/v5 v5.12.0 github.com/go-git/go-git/v5 v5.12.0
@@ -198,6 +202,7 @@ require (
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1
github.com/prometheus/client_golang v1.18.0 github.com/prometheus/client_golang v1.18.0
github.com/sethvargo/go-password v0.3.1 github.com/sethvargo/go-password v0.3.1
github.com/shirou/gopsutil v3.21.11+incompatible
github.com/sirupsen/logrus v1.9.3 github.com/sirupsen/logrus v1.9.3
github.com/spf13/pflag v1.0.5 github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.9.0 github.com/stretchr/testify v1.9.0
@@ -206,7 +211,7 @@ require (
github.com/xaionaro-go/gorex v0.0.0-20241010205749-bcd59d639c4d github.com/xaionaro-go/gorex v0.0.0-20241010205749-bcd59d639c4d
github.com/xaionaro-go/lockmap v0.0.0-20240901172806-e17aea364748 github.com/xaionaro-go/lockmap v0.0.0-20240901172806-e17aea364748
github.com/xaionaro-go/mediamtx v0.0.0-20241009124606-94c22c603970 github.com/xaionaro-go/mediamtx v0.0.0-20241009124606-94c22c603970
github.com/xaionaro-go/obs-grpc-proxy v0.0.0-20241009130412-03d201da4f74 github.com/xaionaro-go/obs-grpc-proxy v0.0.0-20241018162120-5faf4e7a684a
github.com/xaionaro-go/timeapiio v0.0.0-20240915203246-b907cf699af3 github.com/xaionaro-go/timeapiio v0.0.0-20240915203246-b907cf699af3
github.com/xaionaro-go/typing v0.0.0-20221123235249-2229101d38ba github.com/xaionaro-go/typing v0.0.0-20221123235249-2229101d38ba
github.com/xaionaro-go/unsafetools v0.0.0-20210722164218-75ba48cf7b3c github.com/xaionaro-go/unsafetools v0.0.0-20210722164218-75ba48cf7b3c

15
go.sum
View File

@@ -232,6 +232,8 @@ github.com/go-ng/xmath v0.0.0-20230704233441-028f5ea62335 h1:N17hl+3/Zqxg3SM+33Q
github.com/go-ng/xmath v0.0.0-20230704233441-028f5ea62335/go.mod h1:rmcKNA11zmis1auYtl0UY64vE/4+QKeS07w/htllpSE= github.com/go-ng/xmath v0.0.0-20230704233441-028f5ea62335/go.mod h1:rmcKNA11zmis1auYtl0UY64vE/4+QKeS07w/htllpSE=
github.com/go-ng/xsort v0.0.0-20220617174223-1d146907bccc h1:VNz633GRJx2/hL0SpBNoNlLid4xtyi7LSJP1kHpD2Fo= github.com/go-ng/xsort v0.0.0-20220617174223-1d146907bccc h1:VNz633GRJx2/hL0SpBNoNlLid4xtyi7LSJP1kHpD2Fo=
github.com/go-ng/xsort v0.0.0-20220617174223-1d146907bccc/go.mod h1:Pz/V4pxeXP0hjBlXIrm2ehR0GJ0l4Bon3fsOl6TmoJs= github.com/go-ng/xsort v0.0.0-20220617174223-1d146907bccc/go.mod h1:Pz/V4pxeXP0hjBlXIrm2ehR0GJ0l4Bon3fsOl6TmoJs=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
@@ -565,6 +567,8 @@ github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
github.com/sethvargo/go-password v0.3.1 h1:WqrLTjo7X6AcVYfC6R7GtSyuUQR9hGyAj/f1PYQZCJU= github.com/sethvargo/go-password v0.3.1 h1:WqrLTjo7X6AcVYfC6R7GtSyuUQR9hGyAj/f1PYQZCJU=
github.com/sethvargo/go-password v0.3.1/go.mod h1:rXofC1zT54N7R8K/h1WDUdkf9BOx5OptoxrMBcrXzvs= github.com/sethvargo/go-password v0.3.1/go.mod h1:rXofC1zT54N7R8K/h1WDUdkf9BOx5OptoxrMBcrXzvs=
github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/shurcooL/go v0.0.0-20200502201357-93f07166e636/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= github.com/shurcooL/go v0.0.0-20200502201357-93f07166e636/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
@@ -608,6 +612,10 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/sunfish-shogi/bufseekio v0.0.0-20210207115823-a4185644b365/go.mod h1:dEzdXgvImkQ3WLI+0KQpmEx8T/C/ma9KeS3AfmU899I= github.com/sunfish-shogi/bufseekio v0.0.0-20210207115823-a4185644b365/go.mod h1:dEzdXgvImkQ3WLI+0KQpmEx8T/C/ma9KeS3AfmU899I=
github.com/tklauser/go-sysconf v0.3.14 h1:g5vzr9iPFFz24v2KZXs/pvpvh8/V9Fw6vQK5ZZb78yU=
github.com/tklauser/go-sysconf v0.3.14/go.mod h1:1ym4lWMLUOhuBOPGtRcJm7tEGX4SCYNEEEtghGG/8uY=
github.com/tklauser/numcpus v0.8.0 h1:Mx4Wwe/FjZLeQsK/6kt2EOepwwSl7SmJrK5bV/dXYgY=
github.com/tklauser/numcpus v0.8.0/go.mod h1:ZJZlAY+dmR4eut8epnzf0u/VwodKmryxR8txiloSqBE=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
@@ -630,8 +638,8 @@ github.com/xaionaro-go/logrustash v0.0.0-20240804141650-d48034780a5f h1:mMrVrYtH
github.com/xaionaro-go/logrustash v0.0.0-20240804141650-d48034780a5f/go.mod h1:aszOZHoPPSgKwdbJUgonps3MSODqctkNhwQDDwlw0Eg= github.com/xaionaro-go/logrustash v0.0.0-20240804141650-d48034780a5f/go.mod h1:aszOZHoPPSgKwdbJUgonps3MSODqctkNhwQDDwlw0Eg=
github.com/xaionaro-go/mediamtx v0.0.0-20241009124606-94c22c603970 h1:QmbvVR2Jt+I2TTeGef79xhfmlnvvXl+FYEHoYpe7mUY= github.com/xaionaro-go/mediamtx v0.0.0-20241009124606-94c22c603970 h1:QmbvVR2Jt+I2TTeGef79xhfmlnvvXl+FYEHoYpe7mUY=
github.com/xaionaro-go/mediamtx v0.0.0-20241009124606-94c22c603970/go.mod h1:3J9s+wGt6CV4MDnoXApKEdY3kdc5sd6AYEndLJSAIYI= github.com/xaionaro-go/mediamtx v0.0.0-20241009124606-94c22c603970/go.mod h1:3J9s+wGt6CV4MDnoXApKEdY3kdc5sd6AYEndLJSAIYI=
github.com/xaionaro-go/obs-grpc-proxy v0.0.0-20241009130412-03d201da4f74 h1:W3nQdfHickUpLouh1Gz/0cE9H6y1pW+vX79pnhB3G4w= github.com/xaionaro-go/obs-grpc-proxy v0.0.0-20241018162120-5faf4e7a684a h1:PyX7XpLkj+eAwrPMFMGpvZIG4zBfzAfwNhwTtbORqN0=
github.com/xaionaro-go/obs-grpc-proxy v0.0.0-20241009130412-03d201da4f74/go.mod h1:exSKIlCibB0ww+ABDwH+YG/iNdqVfdzXBBg5LYxkxGw= github.com/xaionaro-go/obs-grpc-proxy v0.0.0-20241018162120-5faf4e7a684a/go.mod h1:exSKIlCibB0ww+ABDwH+YG/iNdqVfdzXBBg5LYxkxGw=
github.com/xaionaro-go/spinlock v0.0.0-20190309154744-55278e21e817/go.mod h1:Nb/15eS0BMty6TMuWgRQM8WCDIUlyPZagcpchHT6c9Y= github.com/xaionaro-go/spinlock v0.0.0-20190309154744-55278e21e817/go.mod h1:Nb/15eS0BMty6TMuWgRQM8WCDIUlyPZagcpchHT6c9Y=
github.com/xaionaro-go/spinlock v0.0.0-20200518175509-30e6d1ce68a1 h1:1Kqw9dv2LnznIhJoMt3dNzc/ctSj6VHjyGh4YZHjpE4= github.com/xaionaro-go/spinlock v0.0.0-20200518175509-30e6d1ce68a1 h1:1Kqw9dv2LnznIhJoMt3dNzc/ctSj6VHjyGh4YZHjpE4=
github.com/xaionaro-go/spinlock v0.0.0-20200518175509-30e6d1ce68a1/go.mod h1:UwmTXX+EpoEYHuy0rSys1Rp5PW+eVTgZSjgMVLJENKg= github.com/xaionaro-go/spinlock v0.0.0-20200518175509-30e6d1ce68a1/go.mod h1:UwmTXX+EpoEYHuy0rSys1Rp5PW+eVTgZSjgMVLJENKg=
@@ -656,6 +664,8 @@ github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark v1.7.1 h1:3bajkSilaCbjdKVsKdZjZCLBNPL9pYzrCakKaf4U49U= github.com/yuin/goldmark v1.7.1 h1:3bajkSilaCbjdKVsKdZjZCLBNPL9pYzrCakKaf4U49U=
github.com/yuin/goldmark v1.7.1/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= github.com/yuin/goldmark v1.7.1/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
github.com/yutopp/go-amf0 v0.1.0 h1:a3UeBZG7nRF0zfvmPn2iAfNo1RGzUpHz1VyJD2oGrik= github.com/yutopp/go-amf0 v0.1.0 h1:a3UeBZG7nRF0zfvmPn2iAfNo1RGzUpHz1VyJD2oGrik=
github.com/yutopp/go-amf0 v0.1.0/go.mod h1:QzDOBr9RV6sQh6E5GFEJROZbU0iQKijORBmprkb3FIk= github.com/yutopp/go-amf0 v0.1.0/go.mod h1:QzDOBr9RV6sQh6E5GFEJROZbU0iQKijORBmprkb3FIk=
github.com/yutopp/go-flv v0.3.1 h1:4ILK6OgCJgUNm2WOjaucWM5lUHE0+sLNPdjq3L0Xtjk= github.com/yutopp/go-flv v0.3.1 h1:4ILK6OgCJgUNm2WOjaucWM5lUHE0+sLNPdjq3L0Xtjk=
@@ -855,6 +865,7 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

View File

@@ -11,7 +11,7 @@ import (
func init() { func init() {
serializable.RegisterType[Noop]() serializable.RegisterType[Noop]()
serializable.RegisterType[OBSElementShowHide]() serializable.RegisterType[OBSItemShowHide]()
serializable.RegisterType[OBSWindowCaptureSetSource]() serializable.RegisterType[OBSWindowCaptureSetSource]()
serializable.RegisterType[StartStream]() serializable.RegisterType[StartStream]()
serializable.RegisterType[EndStream]() serializable.RegisterType[EndStream]()
@@ -24,23 +24,23 @@ type Action interface {
type ValueExpression = expression.Expression type ValueExpression = expression.Expression
type OBSElementShowHide struct { type OBSItemShowHide struct {
ElementName *string `yaml:"element_name,omitempty" json:"element_name,omitempty"` ItemName *string `yaml:"item_name,omitempty" json:"item_name,omitempty"`
ElementUUID *string `yaml:"element_uuid,omitempty" json:"element_uuid,omitempty"` ItemUUID *string `yaml:"item_uuid,omitempty" json:"item_uuid,omitempty"`
ValueExpression ValueExpression `yaml:"value_expression,omitempty" json:"value_expression,omitempty"` ValueExpression ValueExpression `yaml:"value_expression,omitempty" json:"value_expression,omitempty"`
} }
var _ Action = (*OBSElementShowHide)(nil) var _ Action = (*OBSItemShowHide)(nil)
func (OBSElementShowHide) isAction() {} func (OBSItemShowHide) isAction() {}
func (a OBSElementShowHide) String() string { func (a OBSItemShowHide) String() string {
return string(tryJSON(a)) return string(tryJSON(a))
} }
type OBSWindowCaptureSetSource struct { type OBSWindowCaptureSetSource struct {
ElementName *string `yaml:"element_name,omitempty" json:"element_name,omitempty"` ItemName *string `yaml:"item_name,omitempty" json:"item_name,omitempty"`
ElementUUID *string `yaml:"element_uuid,omitempty" json:"element_uuid,omitempty"` ItemUUID *string `yaml:"item_uuid,omitempty" json:"item_uuid,omitempty"`
ValueExpression ValueExpression `yaml:"value_expression,omitempty" json:"value_expression,omitempty"` ValueExpression ValueExpression `yaml:"value_expression,omitempty" json:"value_expression,omitempty"`
} }

View File

@@ -18,13 +18,13 @@ type Event interface {
} }
type WindowFocusChange struct { type WindowFocusChange struct {
WindowID *uint64 `yaml:"window_id,omitempty" json:"window_id,omitempty"` Host *string `yaml:"host,omitempty" json:"host,omitempty"`
WindowTitle *string `yaml:"window_title,omitempty" json:"window_title,omitempty"` WindowID *uint64 `yaml:"window_id,omitempty" json:"window_id,omitempty"`
WindowTitlePartial *string `yaml:"window_title_partial,omitempty" json:"window_title_partial,omitempty"` WindowTitle *string `yaml:"window_title,omitempty" json:"window_title,omitempty"`
UserID *uint64 `yaml:"user_id,omitempty" json:"user_id,omitempty"` UserID *uint64 `yaml:"user_id,omitempty" json:"user_id,omitempty"`
ProcessID *uint64 `yaml:"process_id,omitempty" json:"process_id,omitempty"`
//lint:ignore U1000 this field is used by reflection ProcessName *string `yaml:"process_name,omitempty" json:"process_name,omitempty"`
uiComment struct{} `uicomment:"This action will also add field .IsFocused to the event."` IsFocused *bool `yaml:"is_focused,omitempty" json:"is_focused,omitempty"`
} }
func (ev *WindowFocusChange) Get() Event { return ev } func (ev *WindowFocusChange) Get() Event { return ev }
@@ -35,17 +35,25 @@ func (ev *WindowFocusChange) Match(cmpIface Event) bool {
return false return false
} }
if !fieldMatch(ev.Host, cmp.Host) {
return false
}
if !fieldMatch(ev.WindowID, cmp.WindowID) { if !fieldMatch(ev.WindowID, cmp.WindowID) {
return false return false
} }
if !fieldMatch(ev.WindowTitle, cmp.WindowTitle) { if !fieldMatch(ev.WindowTitle, cmp.WindowTitle) {
return false return false
} }
if !partialFieldMatch(ev.WindowTitle, cmp.WindowTitlePartial) && if !fieldMatch(ev.UserID, cmp.UserID) {
!partialFieldMatch(cmp.WindowTitle, ev.WindowTitlePartial) {
return false return false
} }
if !fieldMatch(ev.WindowID, cmp.WindowID) { if !fieldMatch(ev.ProcessID, cmp.ProcessID) {
return false
}
if !fieldMatch(ev.ProcessName, cmp.ProcessName) {
return false
}
if !fieldMatch(ev.IsFocused, cmp.IsFocused) {
return false return false
} }

View File

@@ -1,7 +1,5 @@
package event package event
import "strings"
func fieldMatch[T comparable](f1 *T, f2 *T) bool { func fieldMatch[T comparable](f1 *T, f2 *T) bool {
if f1 == nil || f2 == nil { if f1 == nil || f2 == nil {
return true return true
@@ -9,11 +7,3 @@ func fieldMatch[T comparable](f1 *T, f2 *T) bool {
return *f1 == *f2 return *f1 == *f2
} }
func partialFieldMatch(full *string, partial *string) bool {
if full == nil || partial == nil {
return true
}
return strings.Contains(*full, *partial)
}

View File

@@ -5,6 +5,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/davecgh/go-spew/spew"
"github.com/facebookincubator/go-belt/tool/logger" "github.com/facebookincubator/go-belt/tool/logger"
"github.com/xaionaro-go/streamctl/pkg/expression" "github.com/xaionaro-go/streamctl/pkg/expression"
"github.com/xaionaro-go/streamctl/pkg/observability" "github.com/xaionaro-go/streamctl/pkg/observability"
@@ -37,13 +38,15 @@ func (d *StreamD) submitEvent(
ctx context.Context, ctx context.Context,
ev event.Event, ev event.Event,
) error { ) error {
logger.Debugf(ctx, "submitEvent(ctx, %s)", spew.Sdump(ev))
defer logger.Debugf(ctx, "/submitEvent(ctx, %#v)", spew.Sdump(ev))
exprCtx := objToMap(ev) exprCtx := objToMap(ev)
for _, rule := range d.Config.TriggerRules { for _, rule := range d.Config.TriggerRules {
if rule.EventQuery.Match(ev) { if rule.EventQuery.Match(ev) {
observability.Go(ctx, func() { observability.Go(ctx, func() {
err := d.doAction(ctx, rule.Action, exprCtx) err := d.doAction(ctx, rule.Action, exprCtx)
if err != nil { if err != nil {
logger.Errorf(ctx, "unable to perform action %#+v: %w", rule.Action, err) logger.Errorf(ctx, "unable to perform action %s: %v", rule.Action, err)
} }
}) })
} }
@@ -63,7 +66,7 @@ func (d *StreamD) doAction(
return d.StartStream(ctx, a.PlatID, a.Title, a.Description, a.Profile, a.CustomArgs...) return d.StartStream(ctx, a.PlatID, a.Title, a.Description, a.Profile, a.CustomArgs...)
case *action.EndStream: case *action.EndStream:
return d.EndStream(ctx, a.PlatID) return d.EndStream(ctx, a.PlatID)
case *action.OBSElementShowHide: case *action.OBSItemShowHide:
value, err := expression.Eval[bool](a.ValueExpression, exprCtx) value, err := expression.Eval[bool](a.ValueExpression, exprCtx)
if err != nil { if err != nil {
return fmt.Errorf("unable to Eval() the expression '%s': %w", a.ValueExpression, err) return fmt.Errorf("unable to Eval() the expression '%s': %w", a.ValueExpression, err)
@@ -71,8 +74,8 @@ func (d *StreamD) doAction(
return d.OBSElementSetShow( return d.OBSElementSetShow(
ctx, ctx,
SceneElementIdentifier{ SceneElementIdentifier{
Name: a.ElementName, Name: a.ItemName,
UUID: a.ElementUUID, UUID: a.ItemUUID,
}, },
value, value,
) )

File diff suppressed because it is too large Load Diff

View File

@@ -35,16 +35,16 @@ func ActionGRPC2Go(
} }
case *streamd_grpc.Action_ObsAction: case *streamd_grpc.Action_ObsAction:
switch o := a.ObsAction.OBSActionOneOf.(type) { switch o := a.ObsAction.OBSActionOneOf.(type) {
case *streamd_grpc.OBSAction_ElementShowHide: case *streamd_grpc.OBSAction_ItemShowHide:
result = &action.OBSElementShowHide{ result = &action.OBSItemShowHide{
ElementName: o.ElementShowHide.ElementName, ItemName: o.ItemShowHide.ItemName,
ElementUUID: o.ElementShowHide.ElementUUID, ItemUUID: o.ItemShowHide.ItemUUID,
ValueExpression: expression.Expression(o.ElementShowHide.ValueExpression), ValueExpression: expression.Expression(o.ItemShowHide.ValueExpression),
} }
case *streamd_grpc.OBSAction_WindowCaptureSetSource: case *streamd_grpc.OBSAction_WindowCaptureSetSource:
result = &action.OBSWindowCaptureSetSource{ result = &action.OBSWindowCaptureSetSource{
ElementName: o.WindowCaptureSetSource.ElementName, ItemName: o.WindowCaptureSetSource.ItemName,
ElementUUID: o.WindowCaptureSetSource.ElementUUID, ItemUUID: o.WindowCaptureSetSource.ItemUUID,
ValueExpression: expression.Expression(o.WindowCaptureSetSource.ValueExpression), ValueExpression: expression.Expression(o.WindowCaptureSetSource.ValueExpression),
} }
default: default:
@@ -82,13 +82,13 @@ func ActionGo2GRPC(
PlatID: string(a.PlatID), PlatID: string(a.PlatID),
}, },
} }
case *action.OBSElementShowHide: case *action.OBSItemShowHide:
result.ActionOneof = &streamd_grpc.Action_ObsAction{ result.ActionOneof = &streamd_grpc.Action_ObsAction{
ObsAction: &streamd_grpc.OBSAction{ ObsAction: &streamd_grpc.OBSAction{
OBSActionOneOf: &streamd_grpc.OBSAction_ElementShowHide{ OBSActionOneOf: &streamd_grpc.OBSAction_ItemShowHide{
ElementShowHide: &streamd_grpc.OBSActionElementShowHide{ ItemShowHide: &streamd_grpc.OBSActionItemShowHide{
ElementName: a.ElementName, ItemName: a.ItemName,
ElementUUID: a.ElementUUID, ItemUUID: a.ItemUUID,
ValueExpression: string(a.ValueExpression), ValueExpression: string(a.ValueExpression),
}, },
}, },
@@ -99,8 +99,8 @@ func ActionGo2GRPC(
ObsAction: &streamd_grpc.OBSAction{ ObsAction: &streamd_grpc.OBSAction{
OBSActionOneOf: &streamd_grpc.OBSAction_WindowCaptureSetSource{ OBSActionOneOf: &streamd_grpc.OBSAction_WindowCaptureSetSource{
WindowCaptureSetSource: &streamd_grpc.OBSActionWindowCaptureSetSource{ WindowCaptureSetSource: &streamd_grpc.OBSActionWindowCaptureSetSource{
ElementName: a.ElementName, ItemName: a.ItemName,
ElementUUID: a.ElementUUID, ItemUUID: a.ItemUUID,
ValueExpression: string(a.ValueExpression), ValueExpression: string(a.ValueExpression),
}, },
}, },

View File

@@ -23,10 +23,13 @@ func EventGo2GRPC(in event.Event) (*streamd_grpc.Event, error) {
func triggerGo2GRPCWindowFocusChange(q *event.WindowFocusChange) *streamd_grpc.EventWindowFocusChange { func triggerGo2GRPCWindowFocusChange(q *event.WindowFocusChange) *streamd_grpc.EventWindowFocusChange {
return &streamd_grpc.EventWindowFocusChange{ return &streamd_grpc.EventWindowFocusChange{
WindowID: q.WindowID, Host: q.Host,
WindowTitle: q.WindowTitle, WindowID: q.WindowID,
WindowTitlePartial: q.WindowTitlePartial, WindowTitle: q.WindowTitle,
UserID: q.UserID, ProcessID: q.ProcessID,
ProcessName: q.ProcessName,
UserID: q.UserID,
IsFocused: q.IsFocused,
} }
} }
@@ -43,9 +46,12 @@ func triggerGRPC2GoWindowFocusChange(
q *streamd_grpc.EventWindowFocusChange, q *streamd_grpc.EventWindowFocusChange,
) config.Event { ) config.Event {
return &event.WindowFocusChange{ return &event.WindowFocusChange{
WindowID: q.WindowID, Host: q.Host,
WindowTitle: q.WindowTitle, WindowID: q.WindowID,
WindowTitlePartial: q.WindowTitlePartial, WindowTitle: q.WindowTitle,
UserID: q.UserID, UserID: q.UserID,
ProcessID: q.ProcessID,
ProcessName: q.ProcessName,
IsFocused: q.IsFocused,
} }
} }

View File

@@ -541,21 +541,21 @@ message StreamPlayersChange {}
message NoopRequest {} message NoopRequest {}
message OBSActionElementShowHide { message OBSActionItemShowHide {
optional string elementName = 1; optional string itemName = 1;
optional string elementUUID = 2; optional string itemUUID = 2;
string valueExpression = 3; string valueExpression = 3;
} }
message OBSActionWindowCaptureSetSource { message OBSActionWindowCaptureSetSource {
optional string elementName = 1; optional string itemName = 1;
optional string elementUUID = 2; optional string itemUUID = 2;
string valueExpression = 3; string valueExpression = 3;
} }
message OBSAction { message OBSAction {
oneof OBSActionOneOf { oneof OBSActionOneOf {
OBSActionElementShowHide elementShowHide = 1; OBSActionItemShowHide itemShowHide = 1;
OBSActionWindowCaptureSetSource windowCaptureSetSource = 2; OBSActionWindowCaptureSetSource windowCaptureSetSource = 2;
} }
} }
@@ -610,10 +610,13 @@ message EventOBSSceneChange {
} }
message EventWindowFocusChange { message EventWindowFocusChange {
optional uint64 windowID = 1; optional string host = 1;
optional string windowTitle = 2; optional uint64 windowID = 2;
optional string windowTitlePartial = 3; optional string windowTitle = 3;
optional uint64 userID = 4; optional uint64 processID = 4;
optional string processName = 5;
optional uint64 userID = 6;
optional bool isFocused = 7;
} }
message EventQuery { message EventQuery {

View File

@@ -170,5 +170,51 @@ func (d *StreamD) OBSElementSetShow(
elID SceneElementIdentifier, elID SceneElementIdentifier,
shouldShow bool, shouldShow bool,
) error { ) error {
return fmt.Errorf("not implemented, yet") if elID.Name == nil && elID.UUID == nil {
return fmt.Errorf("elID.Name == nil && elID.UUID == nil (which is legit, but unexpected, so we fail just in case)")
}
obsServer, obsServerClose, err := d.OBS(ctx)
if obsServerClose != nil {
defer obsServerClose()
}
if err != nil {
return fmt.Errorf("unable to get a client to OBS: %w", err)
}
sceneListResp, err := obsServer.GetSceneList(ctx, &obs_grpc.GetSceneListRequest{})
if err != nil {
return fmt.Errorf("unable to get the scenes list: %w", err)
}
for _, scene := range sceneListResp.Scenes {
itemList, err := obsServer.GetSceneItemList(ctx, &obs_grpc.GetSceneItemListRequest{
SceneUUID: scene.SceneUUID,
})
if err != nil {
return fmt.Errorf("unable to get the list of items of scene %#+v: %w", scene, err)
}
for _, item := range itemList.GetSceneItems() {
if elID.Name != nil && *elID.Name != item.GetSourceName() {
continue
}
if elID.UUID != nil && *elID.UUID != item.GetSourceUUID() {
continue
}
req := &obs_grpc.SetSceneItemEnabledRequest{
SceneName: scene.SceneName,
SceneUUID: scene.SceneUUID,
SceneItemID: item.SceneItemID,
SceneItemEnabled: shouldShow,
}
_, err := obsServer.SetSceneItemEnabled(ctx, req)
if err != nil {
return fmt.Errorf("unable to submit %#+v: %w", shouldShow, item, err)
}
}
}
return nil
} }

115
pkg/streampanel/events.go Normal file
View File

@@ -0,0 +1,115 @@
package streampanel
import (
"context"
"fmt"
"os"
"github.com/davecgh/go-spew/spew"
"github.com/facebookincubator/go-belt/tool/logger"
"github.com/hashicorp/go-multierror"
"github.com/xaionaro-go/streamctl/pkg/observability"
"github.com/xaionaro-go/streamctl/pkg/streamd/config/event"
"github.com/xaionaro-go/streamctl/pkg/windowmanagerhandler"
)
var hostname *string
func init() {
if _hostname, err := os.Hostname(); err == nil {
hostname = &_hostname
}
}
func (p *Panel) initEventSensor(ctx context.Context) {
es, err := newEventSensor()
if err != nil {
p.DisplayError(err)
return
}
observability.Go(ctx, func() {
logger.Debugf(ctx, "eventSensor")
defer logger.Debugf(ctx, "/eventSensor")
es.Loop(ctx, p.StreamD)
})
}
type eventSensor struct {
WMH *windowmanagerhandler.WindowManagerHandler
PreviouslyFocusedWindow *windowmanagerhandler.WindowFocusChange
}
func newEventSensor() (*eventSensor, error) {
wmh, err := windowmanagerhandler.New()
if err != nil {
return nil, fmt.Errorf("unable to init a window manager handler: %w", err)
}
return &eventSensor{
WMH: wmh,
}, nil
}
type submitEventer interface {
SubmitEvent(
ctx context.Context,
event event.Event,
) error
}
func (es *eventSensor) Loop(
ctx context.Context,
eventSubmitter submitEventer,
) {
windowFocusChangeChan := es.WMH.WindowFocusChangeChan(ctx)
for {
select {
case <-ctx.Done():
return
case ev := <-windowFocusChangeChan:
if err := es.submitEventWindowFocusChange(ctx, ev, eventSubmitter); err != nil {
logger.Errorf(ctx, "unable to submit the WindowFocusChange event %#+v: %w", ev, err)
}
}
}
}
func (es *eventSensor) submitEventWindowFocusChange(
ctx context.Context,
ev windowmanagerhandler.WindowFocusChange,
submitEventer submitEventer,
) error {
logger.Debugf(ctx, "submitEventWindowFocusChange(ctx, %s)", spew.Sdump(ev))
defer logger.Debugf(ctx, "/submitEventWindowFocusChange(ctx, %#v)", spew.Sdump(ev))
var err *multierror.Error
if es.PreviouslyFocusedWindow != nil {
ev := es.PreviouslyFocusedWindow
err = multierror.Append(err, submitEventer.SubmitEvent(ctx, &event.WindowFocusChange{
Host: hostname,
WindowID: (*uint64)(ev.WindowID),
WindowTitle: ev.WindowTitle,
UserID: ptr(uint64(*ev.UserID)),
ProcessID: ptr(uint64(*ev.ProcessID)),
ProcessName: ev.ProcessName,
IsFocused: ptr(false),
}))
}
es.PreviouslyFocusedWindow = &ev
err = multierror.Append(err, submitEventer.SubmitEvent(ctx, &event.WindowFocusChange{
Host: hostname,
WindowID: (*uint64)(ev.WindowID),
WindowTitle: ev.WindowTitle,
UserID: ptr(uint64(*ev.UserID)),
ProcessID: ptr(uint64(*ev.ProcessID)),
ProcessName: ev.ProcessName,
IsFocused: ptr(true),
}))
return err.ErrorOrNil()
}

View File

@@ -1,14 +0,0 @@
package streampanel
import (
"time"
"fyne.io/fyne/v2"
)
func hideWindow(w fyne.Window) {
for i := 0; i < 10; i++ {
time.Sleep(time.Millisecond)
w.Hide()
}
}

View File

@@ -231,23 +231,6 @@ func imgFillTo(
return img return img
} }
func imgRotateFillTo(
ctx context.Context,
src image.Image,
size image.Point,
alignX streamdconsts.AlignX,
alignY streamdconsts.AlignY,
) image.Image {
return imgFillTo(
ctx,
src,
size,
size,
image.Point{},
alignX, alignY,
)
}
const ( const (
ScreenshotMaxWidth = 384 ScreenshotMaxWidth = 384
ScreenshotMaxHeight = 216 ScreenshotMaxHeight = 216

View File

@@ -22,7 +22,6 @@ import (
"github.com/anthonynsimon/bild/adjust" "github.com/anthonynsimon/bild/adjust"
"github.com/facebookincubator/go-belt/tool/logger" "github.com/facebookincubator/go-belt/tool/logger"
"github.com/lusingander/colorpicker" "github.com/lusingander/colorpicker"
"github.com/xaionaro-go/obs-grpc-proxy/pkg/obsgrpcproxy"
"github.com/xaionaro-go/obs-grpc-proxy/protobuf/go/obs_grpc" "github.com/xaionaro-go/obs-grpc-proxy/protobuf/go/obs_grpc"
"github.com/xaionaro-go/streamctl/pkg/colorx" "github.com/xaionaro-go/streamctl/pkg/colorx"
"github.com/xaionaro-go/streamctl/pkg/observability" "github.com/xaionaro-go/streamctl/pkg/observability"
@@ -488,50 +487,37 @@ func (p *Panel) editMonitorElementWindow(
var audioSourceNames []string var audioSourceNames []string
videoSourceNameIsSet := map[string]struct{}{} videoSourceNameIsSet := map[string]struct{}{}
audioSourceNameIsSet := map[string]struct{}{} audioSourceNameIsSet := map[string]struct{}{}
for _, _scene := range resp.Scenes { for _, scene := range resp.Scenes {
scene, err := obsgrpcproxy.FromAbstractObject[map[string]any](_scene)
if err != nil {
p.DisplayError(fmt.Errorf("unable to convert scene info: %w", err))
return
}
logger.Debugf(ctx, "scene info: %#+v", scene)
sceneName, _ := scene["sceneName"].(string)
resp, err := obsServer.GetSceneItemList(ctx, &obs_grpc.GetSceneItemListRequest{ resp, err := obsServer.GetSceneItemList(ctx, &obs_grpc.GetSceneItemListRequest{
SceneName: &sceneName, SceneName: scene.SceneName,
}) })
if err != nil { if err != nil {
p.DisplayError( p.DisplayError(
fmt.Errorf("unable to get the list of items of scene '%s': %w", sceneName, err), fmt.Errorf("unable to get the list of items of scene '%s': %w", scene.SceneName, err),
) )
return return
} }
for _, item := range resp.SceneItems { for _, item := range resp.SceneItems {
source, err := obsgrpcproxy.FromAbstractObject[map[string]any](item) logger.Debugf(ctx, "source info: %#+v", item)
if err != nil {
p.DisplayError(fmt.Errorf("unable to convert source info: %w", err))
return
}
logger.Debugf(ctx, "source info: %#+v", source)
sourceName, _ := source["sourceName"].(string)
func() { func() {
if _, ok := videoSourceNameIsSet[sourceName]; ok { if _, ok := videoSourceNameIsSet[item.SourceName]; ok {
return return
} }
sceneItemTransform := source["sceneItemTransform"].(map[string]any) sceneItemTransform := item.SceneItemTransform
sourceWidth, _ := sceneItemTransform["sourceWidth"].(float64) sourceWidth := sceneItemTransform.SourceWidth
if sourceWidth == 0 { if sourceWidth == 0 {
return return
} }
videoSourceNameIsSet[sourceName] = struct{}{} videoSourceNameIsSet[item.SourceName] = struct{}{}
videoSourceNames = append(videoSourceNames, sourceName) videoSourceNames = append(videoSourceNames, item.SourceName)
}() }()
func() { func() {
if _, ok := audioSourceNameIsSet[sourceName]; ok { if _, ok := audioSourceNameIsSet[item.SourceName]; ok {
return return
} }
// TODO: filter only audio sources // TODO: filter only audio sources
audioSourceNameIsSet[sourceName] = struct{}{} audioSourceNameIsSet[item.SourceName] = struct{}{}
audioSourceNames = append(audioSourceNames, sourceName) audioSourceNames = append(audioSourceNames, item.SourceName)
}() }()
} }
} }

View File

@@ -1,21 +0,0 @@
package streampanel
import (
"context"
"fmt"
"github.com/xaionaro-go/streamctl/pkg/streamd/config"
)
func (p *Panel) setStreamDConfig(
ctx context.Context,
cfg *config.Config,
) error {
if err := p.StreamD.SetConfig(ctx, cfg); err != nil {
return fmt.Errorf("unable to set the config: %w", err)
}
if err := p.StreamD.SaveConfig(ctx); err != nil {
return fmt.Errorf("unable to save the config: %w", err)
}
return nil
}

View File

@@ -347,6 +347,7 @@ func (p *Panel) Loop(ctx context.Context, opts ...LoopOption) error {
} }
p.reinitScreenshoter(ctx) p.reinitScreenshoter(ctx)
p.initEventSensor(ctx)
p.initMainWindow(ctx, initCfg.StartingPage) p.initMainWindow(ctx, initCfg.StartingPage)
if streamDRunErr != nil { if streamDRunErr != nil {
@@ -1630,25 +1631,18 @@ func (p *Panel) getUpdatedStatus_backends_noLock(ctx context.Context) {
return return
} }
sceneList, err := obsServer.GetSceneList(ctx, &obs_grpc.GetSceneListRequest{}) sceneListResp, err := obsServer.GetSceneList(ctx, &obs_grpc.GetSceneListRequest{})
if err != nil { if err != nil {
p.ReportError(fmt.Errorf("unable to get the list of scene from OBS: %w", err)) p.ReportError(err)
return return
} }
logger.Tracef(ctx, "OBS SceneList response: %#+v", sceneList)
p.obsSelectScene.Options = p.obsSelectScene.Options[:0] for _, scene := range sceneListResp.Scenes {
for _, scene := range sceneList.Scenes { p.obsSelectScene.Options = append(p.obsSelectScene.Options, *scene.SceneName)
sceneNameAny := scene.GetFields()["sceneName"]
sceneName := string(sceneNameAny.GetString_())
if sceneName == "" {
p.ReportError(fmt.Errorf("unable to parse the scene name from %#+v", scene))
return
}
p.obsSelectScene.Options = append(p.obsSelectScene.Options, sceneName)
} }
if sceneList.CurrentProgramSceneName != p.obsSelectScene.Selected {
p.obsSelectScene.SetSelected(sceneList.CurrentProgramSceneName) if sceneListResp.CurrentProgramSceneName != p.obsSelectScene.Selected {
p.obsSelectScene.SetSelected(sceneListResp.CurrentProgramSceneName)
} }
}) })
} else { } else {

View File

@@ -57,6 +57,10 @@ func reflectMakeFieldsFor(
return []fyne.CanvasObject{newReflectField(v, func(i uint64) { return []fyne.CanvasObject{newReflectField(v, func(i uint64) {
setter(reflect.ValueOf(i).Convert(t)) setter(reflect.ValueOf(i).Convert(t))
}, namePrefix)} }, namePrefix)}
case reflect.Bool:
return []fyne.CanvasObject{newReflectField(v, func(i bool) {
setter(reflect.ValueOf(i).Convert(t))
}, namePrefix)}
case reflect.Ptr: case reflect.Ptr:
return reflectMakeFieldsFor(v.Elem(), t.Elem(), func(newValue reflect.Value) { return reflectMakeFieldsFor(v.Elem(), t.Elem(), func(newValue reflect.Value) {
if newValue.IsZero() && v.CanAddr() { if newValue.IsZero() && v.CanAddr() {

View File

@@ -0,0 +1,5 @@
package windowmanagerhandler
func ptr[T any](in T) *T {
return &in
}

View File

@@ -6,7 +6,7 @@ import (
) )
type WindowManagerHandler struct { type WindowManagerHandler struct {
*PlatformSpecificWindowManagerHandler PlatformSpecificWindowManagerHandler
} }
func New() (*WindowManagerHandler, error) { func New() (*WindowManagerHandler, error) {
@@ -22,6 +22,9 @@ func (wmh *WindowManagerHandler) WindowFocusChangeChan(ctx context.Context) <-ch
} }
type WindowFocusChange struct { type WindowFocusChange struct {
WindowID WindowID WindowID *WindowID
WindowTitle string WindowTitle *string
ProcessID *PID
UserID *UID
ProcessName *string
} }

View File

@@ -9,6 +9,8 @@ import (
) )
type WindowID uint64 type WindowID uint64
type PID int // using the same underlying type as `os` does
type UID int // using the same underlying type as `os` does
type XWMOrWaylandWM interface { type XWMOrWaylandWM interface {
WindowFocusChangeChan(ctx context.Context) <-chan WindowFocusChange WindowFocusChangeChan(ctx context.Context) <-chan WindowFocusChange

View File

@@ -13,6 +13,7 @@ import (
"github.com/BurntSushi/xgbutil" "github.com/BurntSushi/xgbutil"
"github.com/BurntSushi/xgbutil/ewmh" "github.com/BurntSushi/xgbutil/ewmh"
"github.com/facebookincubator/go-belt/tool/logger" "github.com/facebookincubator/go-belt/tool/logger"
"github.com/shirou/gopsutil/process"
"github.com/xaionaro-go/streamctl/pkg/observability" "github.com/xaionaro-go/streamctl/pkg/observability"
) )
@@ -69,9 +70,36 @@ func (wmh *XWindowManagerHandler) WindowFocusChangeChan(ctx context.Context) <-c
continue continue
} }
pid, err := ewmh.WmPidGet(wmh.XUtil, clientID)
if err != nil {
logger.Errorf(ctx, "unable to get the PID of the active window (%d): %w", clientID, err)
continue
}
proc, err := process.NewProcess(int32(pid))
if err != nil {
logger.Errorf(ctx, "unable to get process info of the active window (%d) using PID %d: %w", clientID, pid, err)
continue
}
uids, err := proc.Uids()
if err != nil {
logger.Errorf(ctx, "unable to get the UIDs of the active window (%d) using PID %d", clientID, pid, err)
continue
}
procName, err := proc.Name()
if err != nil {
logger.Errorf(ctx, "unable to get the process name of the active window (%d) using PID %d", clientID, pid, err)
continue
}
ch <- WindowFocusChange{ ch <- WindowFocusChange{
WindowID: WindowID(clientID), WindowID: ptr(WindowID(clientID)),
WindowTitle: name, WindowTitle: ptr(name),
UserID: ptr(UID(uids[0])),
ProcessID: ptr(PID(pid)),
ProcessName: ptr(procName),
} }
} }
}) })

View File

@@ -10,6 +10,8 @@ import (
type PlatformSpecificWindowManagerHandler struct{} type PlatformSpecificWindowManagerHandler struct{}
type WindowID struct{} type WindowID struct{}
type PID struct{}
type UID struct{}
func (wmh *WindowManagerHandler) init(context.Context) error { func (wmh *WindowManagerHandler) init(context.Context) error {
return fmt.Errorf("the support of window manager handler for this platform is not implemented, yet") return fmt.Errorf("the support of window manager handler for this platform is not implemented, yet")