[fractal/fractal-next] qr-code-scanner: Use is_camera_present() and allow multiple starts



commit 5bb7e52470d12ca7e87de5bab4791721d8857db4
Author: Julian Sparber <julian sparber net>
Date:   Wed Jan 12 18:41:12 2022 +0100

    qr-code-scanner: Use is_camera_present() and allow multiple starts
    
    This now uses `is_camera_present()` to check whether we have a camera.
    It also makes sure that a pipeline is closed before a new one is
    started.

 Cargo.lock                                      |  5 +-
 Cargo.toml                                      |  2 +-
 src/contrib/qr_code_scanner/camera_paintable.rs | 15 ++---
 src/contrib/qr_code_scanner/mod.rs              | 80 +++++++++++++------------
 4 files changed, 49 insertions(+), 53 deletions(-)
---
diff --git a/Cargo.lock b/Cargo.lock
index 535c7716..e68590ea 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4090,11 +4090,10 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
 
 [[package]]
 name = "tokio"
-version = "1.12.0"
+version = "1.15.0"
 source = "registry+https://github.com/rust-lang/crates.io-index";
-checksum = "c2c2416fdedca8443ae44b4527de1ea633af61d8f7169ffa6e72c5b53d24efcc"
+checksum = "fbbf1c778ec206785635ce8ad57fe52b3009ae9e0c9f574a728f3049d3e55838"
 dependencies = [
- "autocfg",
  "bytes",
  "libc",
  "memchr",
diff --git a/Cargo.toml b/Cargo.toml
index cd10e056..5da11da3 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -21,7 +21,7 @@ gtk-macros = "0.3"
 once_cell = "1.5"
 serde = "1.0.130"
 serde_json = "1.0"
-tokio = { version = "1.2", features = ["rt", "rt-multi-thread"] }
+tokio = { version = "1.15", features = ["rt", "rt-multi-thread", "sync"] }
 url = "2.2"
 secret-service = "2.0"
 html2pango = "0.4"
diff --git a/src/contrib/qr_code_scanner/camera_paintable.rs b/src/contrib/qr_code_scanner/camera_paintable.rs
index e4d7c9b1..4279c68e 100644
--- a/src/contrib/qr_code_scanner/camera_paintable.rs
+++ b/src/contrib/qr_code_scanner/camera_paintable.rs
@@ -212,7 +212,6 @@ mod imp {
 
     pub struct CameraPaintable {
         pub sink: camera_sink::CameraSink,
-        pub detector: QrCodeDetector,
         pub pipeline: RefCell<Option<gst::Pipeline>>,
         pub sender: Sender<Action>,
         pub image: RefCell<Option<gdk::Texture>>,
@@ -227,7 +226,6 @@ mod imp {
             Self {
                 pipeline: RefCell::default(),
                 sink: camera_sink::CameraSink::new(sender.clone()),
-                detector: QrCodeDetector::new(sender.clone()),
                 image: RefCell::new(None),
                 sender,
                 receiver,
@@ -337,6 +335,7 @@ impl Default for CameraPaintable {
 
 impl CameraPaintable {
     pub fn set_pipewire_fd<F: AsRawFd>(&self, fd: F, node_id: u32) {
+        self.close_pipeline();
         let pipewire_element = gst::ElementFactory::make("pipewiresrc", None).unwrap();
         pipewire_element
             .set_property("fd", &fd.as_raw_fd())
@@ -350,6 +349,7 @@ impl CameraPaintable {
     fn init_pipeline(&self, pipewire_src: gst::Element) {
         let self_ = imp::CameraPaintable::from_instance(self);
         let pipeline = gst::Pipeline::new(None);
+        let detector = QrCodeDetector::new(self_.sender.clone()).upcast();
 
         let tee = gst::ElementFactory::make("tee", None).unwrap();
         let queue = gst::ElementFactory::make("queue", None).unwrap();
@@ -377,21 +377,14 @@ impl CameraPaintable {
                 &tee,
                 &queue,
                 &videoconvert1,
-                self_.detector.upcast_ref(),
+                &detector,
                 &queue2,
                 &videoconvert2,
                 self_.sink.upcast_ref(),
             ])
             .unwrap();
 
-        gst::Element::link_many(&[
-            &pipewire_src,
-            &tee,
-            &queue,
-            &videoconvert1,
-            self_.detector.upcast_ref(),
-        ])
-        .unwrap();
+        gst::Element::link_many(&[&pipewire_src, &tee, &queue, &videoconvert1, &detector]).unwrap();
 
         tee.link_pads(None, &queue2, None).unwrap();
         gst::Element::link_many(&[&queue2, &videoconvert2, self_.sink.upcast_ref()]).unwrap();
diff --git a/src/contrib/qr_code_scanner/mod.rs b/src/contrib/qr_code_scanner/mod.rs
index e5a646f3..3e484b5a 100644
--- a/src/contrib/qr_code_scanner/mod.rs
+++ b/src/contrib/qr_code_scanner/mod.rs
@@ -20,6 +20,7 @@ mod imp {
     use gtk::CompositeTemplate;
     use once_cell::sync::Lazy;
     use std::cell::Cell;
+    use tokio::sync::OnceCell;
 
     use super::*;
 
@@ -29,8 +30,8 @@ mod imp {
         pub paintable: CameraPaintable,
         #[template_child]
         pub picture: TemplateChild<gtk::Picture>,
+        pub connection: OnceCell<zbus::Connection>,
         pub has_camera: Cell<bool>,
-        pub is_started: Cell<bool>,
     }
 
     #[glib::object_subclass]
@@ -116,47 +117,51 @@ impl QrCodeScanner {
         glib::Object::new(&[]).expect("Failed to create a QrCodeScanner")
     }
 
+    async fn connection(&self) -> Result<&zbus::Connection, ashpd::Error> {
+        let priv_ = imp::QrCodeScanner::from_instance(self);
+
+        Ok(priv_
+            .connection
+            .get_or_try_init(|| zbus::Connection::session())
+            .await?)
+    }
+
     pub fn stop(&self) {
         let self_ = imp::QrCodeScanner::from_instance(self);
 
         self_.paintable.close_pipeline();
     }
 
-    async fn start_internal(&self) -> bool {
-        let self_ = imp::QrCodeScanner::from_instance(self);
-        if let Ok(Some(stream_fd)) = stream().await {
+    pub async fn start(&self) -> bool {
+        let priv_ = imp::QrCodeScanner::from_instance(self);
+        if let Ok(stream_fd) = self.stream().await {
             if let Ok(node_id) = camera::pipewire_node_id(stream_fd).await {
-                self_.paintable.set_pipewire_fd(stream_fd, node_id);
-                self_.has_camera.set(true);
-                self.notify("has-camera");
+                priv_.paintable.set_pipewire_fd(stream_fd, node_id);
+                self.set_has_camera(true);
                 return true;
             }
         }
-        self_.has_camera.set(false);
-        self.notify("has-camera");
-        false
+
+        self.set_has_camera(false);
+        return false;
     }
 
-    pub async fn start(&self) -> bool {
-        let priv_ = imp::QrCodeScanner::from_instance(self);
-        let is_started = self.start_internal().await;
-        priv_.is_started.set(is_started);
-        is_started
+    async fn has_camera_internal(&self) -> Result<bool, ashpd::Error> {
+        let proxy = camera::CameraProxy::new(self.connection().await?).await?;
+
+        proxy.is_camera_present().await
+    }
+
+    async fn stream(&self) -> Result<RawFd, ashpd::Error> {
+        let proxy = camera::CameraProxy::new(self.connection().await?).await?;
+
+        proxy.access_camera().await?;
+        proxy.open_pipe_wire_remote().await
     }
 
     fn init_has_camera(&self) {
         spawn!(clone!(@weak self as obj => async move {
-            let priv_ = imp::QrCodeScanner::from_instance(&obj);
-            let has_camera = if obj.start_internal().await {
-                if !priv_.is_started.get() {
-                    obj.stop();
-                }
-                true
-            } else {
-                false
-            };
-            priv_.has_camera.set(has_camera);
-            obj.notify("has-camera");
+            obj.set_has_camera(obj.has_camera_internal().await.unwrap_or_default());
         }));
     }
 
@@ -165,6 +170,17 @@ impl QrCodeScanner {
         priv_.has_camera.get()
     }
 
+    fn set_has_camera(&self, has_camera: bool) {
+        let priv_ = imp::QrCodeScanner::from_instance(self);
+
+        if has_camera == self.has_camera() {
+            return;
+        }
+
+        priv_.has_camera.set(has_camera);
+        self.notify("has-camera");
+    }
+
     /// Connects the prepared signals to the function f given in input
     pub fn connect_code_detected<F: Fn(&Self, QrVerificationData) + 'static>(
         &self,
@@ -182,18 +198,6 @@ impl QrCodeScanner {
     }
 }
 
-async fn stream() -> Result<Option<RawFd>, ashpd::Error> {
-    let connection = zbus::Connection::session().await?;
-    let proxy = camera::CameraProxy::new(&connection).await?;
-
-    if proxy.is_camera_present().await? {
-        proxy.access_camera().await?;
-        Ok(Some(proxy.open_pipe_wire_remote().await?))
-    } else {
-        Ok(None)
-    }
-}
-
 #[derive(Clone, Debug, PartialEq, glib::GBoxed)]
 #[gboxed(type_name = "QrVerificationDataBoxed")]
 struct QrVerificationDataBoxed(QrVerificationData);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]