From 8c4a3b46cbeaf0efbb8a7775903e9648e26637d0 Mon Sep 17 00:00:00 2001
From: wheaney <42350981+wheaney@users.noreply.github.com>
Date: Wed, 18 Sep 2024 13:58:26 -0700
Subject: [PATCH] WIP
---
ui/src/connecteddevice.py | 12 ++++++
ui/src/gtk/connected-device.ui | 13 +++++++
ui/src/meson.build | 1 +
ui/src/virtualdisplay.py | 71 ++++++++++++++++++++++++++++++++++
4 files changed, 97 insertions(+)
create mode 100644 ui/src/virtualdisplay.py
diff --git a/ui/src/connecteddevice.py b/ui/src/connecteddevice.py
index a1883ba..3693da2 100644
--- a/ui/src/connecteddevice.py
+++ b/ui/src/connecteddevice.py
@@ -4,10 +4,13 @@ from .license import BREEZY_GNOME_FEATURES
from .settingsmanager import SettingsManager
from .shortcutdialog import bind_shortcut_settings
from .statemanager import StateManager
+from .virtualdisplay import VirtualMonitor
from .xrdriveripc import XRDriverIPC
import gettext
+import logging
_ = gettext.gettext
+logger = logging.getLogger('breezy_ui')
@Gtk.Template(resource_path='/com/xronlinux/BreezyDesktop/gtk/connected-device.ui')
class ConnectedDevice(Gtk.Box):
@@ -28,6 +31,7 @@ class ConnectedDevice(Gtk.Box):
widescreen_mode_switch = Gtk.Template.Child()
widescreen_mode_row = Gtk.Template.Child()
curved_display_switch = Gtk.Template.Child()
+ add_virtual_display_button = Gtk.Template.Child()
set_toggle_display_distance_start_button = Gtk.Template.Child()
set_toggle_display_distance_end_button = Gtk.Template.Child()
reassign_recenter_display_shortcut_button = Gtk.Template.Child()
@@ -55,6 +59,7 @@ class ConnectedDevice(Gtk.Box):
self.follow_mode_switch,
self.follow_threshold_scale,
self.curved_display_switch,
+ # self.add_virtual_display_button,
self.set_toggle_display_distance_start_button,
self.set_toggle_display_distance_end_button,
self.movement_look_ahead_scale
@@ -87,6 +92,7 @@ class ConnectedDevice(Gtk.Box):
self.set_toggle_display_distance_start_button,
self.set_toggle_display_distance_end_button
])
+ self.add_virtual_display_button.connect('clicked', self.on_add_virtual_display)
self.state_manager = StateManager.get_instance()
self.state_manager.bind_property('follow-mode', self.follow_mode_switch, 'active', GObject.BindingFlags.DEFAULT)
@@ -166,6 +172,12 @@ class ConnectedDevice(Gtk.Box):
for widget in widgets:
widget.connect('clicked', lambda *args, widget=widget: on_set_display_distance_toggle(widget))
reload_display_distance_toggle_button(widget)
+
+ def on_add_virtual_display(self, widget):
+ VirtualMonitor(1920, 1080, self.on_virtual_display_ready).create()
+
+ def on_virtual_display_ready(self):
+ logger.info("Virtual display ready")
def _on_widget_destroy(self, widget):
self.state_manager.unbind_property('follow-mode', self.follow_mode_switch, 'active')
diff --git a/ui/src/gtk/connected-device.ui b/ui/src/gtk/connected-device.ui
index 3a73559..2cec2c6 100644
--- a/ui/src/gtk/connected-device.ui
+++ b/ui/src/gtk/connected-device.ui
@@ -84,6 +84,19 @@
+
+
+
diff --git a/ui/src/meson.build b/ui/src/meson.build
index cb1ecbc..8272bfa 100644
--- a/ui/src/meson.build
+++ b/ui/src/meson.build
@@ -46,6 +46,7 @@ breezydesktop_sources = [
'shortcutdialog.py',
'statemanager.py',
'time.py',
+ 'virtualdisplay.py',
'verify.py',
'window.py'
]
diff --git a/ui/src/virtualdisplay.py b/ui/src/virtualdisplay.py
new file mode 100644
index 0000000..b57f473
--- /dev/null
+++ b/ui/src/virtualdisplay.py
@@ -0,0 +1,71 @@
+#!/usr/bin/python3
+
+import logging
+import sys
+import signal
+import pydbus
+import gi
+gi.require_version('Gst', '1.0')
+from gi.repository import GLib, GObject, Gst
+
+logger = logging.getLogger('breezy_ui')
+
+screen_cast_iface = 'org.gnome.Mutter.ScreenCast'
+screen_cast_session_iface = 'org.gnome.Mutter.ScreenCast.Session'
+screen_cast_stream_iface = 'org.gnome.Mutter.ScreenCast.Session'
+gst_pipeline_format = "pipewiresrc path=%u ! video/x-raw,max-framerate=120/1,width=%d,height=%d ! videoconvert ! fakesink sync=false"
+
+
+def _screen_cast_session():
+ bus = pydbus.SessionBus()
+ screen_cast = bus.get(screen_cast_iface, '/org/gnome/Mutter/ScreenCast')
+ session_path = screen_cast.CreateSession([])
+ logger.info("session path: %s" % session_path)
+ screen_cast_session = bus.get(screen_cast_iface, session_path)
+
+ return screen_cast_session
+
+class VirtualMonitor:
+ def __init__(self, width, height, on_ready_cb):
+ self.width = width
+ self.height = height
+ self.on_ready_cb = on_ready_cb
+
+ Gst.init(None)
+
+ def create(self):
+ session = _screen_cast_session()
+ stream_path = session.RecordVirtual({
+ 'is-platform': GLib.Variant.new_boolean(True),
+ })
+ logger.info("stream path: %s" % stream_path)
+ bus = pydbus.SessionBus()
+ self.stream = bus.get(screen_cast_iface, stream_path)
+
+ self.stream.onPipeWireStreamAdded = self._on_pipewire_stream_added
+
+ session.Start()
+
+ def terminate(self):
+ if self.stream is not None:
+ self.stream.Stop()
+
+ if self.pipeline is not None:
+ self.pipeline.send_event(Gst.Event.new_eos())
+ self.pipeline.set_state(Gst.State.NULL)
+
+ def _on_message(self, bus, message):
+ type = message.type
+ logger.info("message type: %s" % type)
+ if type == Gst.MessageType.EOS or type == Gst.MessageType.ERROR:
+ self.terminate()
+
+ def _on_pipewire_stream_added(self, node_id):
+ logger.info("pipe wire stream added: %u" % node_id)
+
+ self.pipeline = Gst.parse_launch(gst_pipeline_format % (node_id, self.width, self.height))
+ self.pipeline.set_state(Gst.State.PLAYING)
+ self.pipeline.get_bus().connect('message', self._on_message)
+ self.pipeline.set_state(Gst.State.PAUSED)
+
+ self.on_ready_cb()
\ No newline at end of file