mirror of
https://github.com/xaionaro-go/streamctl.git
synced 2025-10-05 15:37:00 +08:00
Complete implementation of auto-show/hide an item in OBS on mouse-focus in XServer
This commit is contained in:
12
Makefile
12
Makefile
@@ -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/
|
||||||
|
@@ -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
11
go.mod
@@ -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
15
go.sum
@@ -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=
|
||||||
|
@@ -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"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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)
|
|
||||||
}
|
|
||||||
|
@@ -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
@@ -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),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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 {
|
||||||
|
@@ -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
115
pkg/streampanel/events.go
Normal 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()
|
||||||
|
}
|
@@ -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()
|
|
||||||
}
|
|
||||||
}
|
|
@@ -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
|
||||||
|
@@ -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)
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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
|
|
||||||
}
|
|
@@ -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 {
|
||||||
|
@@ -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() {
|
||||||
|
5
pkg/windowmanagerhandler/utils.go
Normal file
5
pkg/windowmanagerhandler/utils.go
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package windowmanagerhandler
|
||||||
|
|
||||||
|
func ptr[T any](in T) *T {
|
||||||
|
return &in
|
||||||
|
}
|
@@ -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
|
||||||
}
|
}
|
||||||
|
@@ -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
|
||||||
|
@@ -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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@@ -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")
|
||||||
|
Reference in New Issue
Block a user