Blame SOURCES/0001-screencast-Stop-recording-when-screen-size-or-resour.patch

0df5f8
From 67a4506d4d8a0cbbaca5df4adfc309e54e557aee Mon Sep 17 00:00:00 2001
0df5f8
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
0df5f8
Date: Tue, 5 Jan 2021 12:04:23 +0100
0df5f8
Subject: [PATCH] screencast: Stop recording when screen size or resource scale
0df5f8
 change
0df5f8
0df5f8
Video encoders don't really handle changing the size of the video, and if
0df5f8
we'd e.g. change resolution while recording, we would end up with a corrupt
0df5f8
video file. Handle this more gracefully by stopping the recording if the
0df5f8
conditions change.
0df5f8
---
0df5f8
 js/ui/screencast.js  | 92 +++++++++++++++++++++++++++++++++++++++++---
0df5f8
 src/shell-recorder.c | 50 ++++++++++++------------
0df5f8
 src/shell-recorder.h |  1 +
0df5f8
 3 files changed, 114 insertions(+), 29 deletions(-)
0df5f8
0df5f8
diff --git a/js/ui/screencast.js b/js/ui/screencast.js
0df5f8
index 0b0b14a8e..54f8fb5ae 100644
0df5f8
--- a/js/ui/screencast.js
0df5f8
+++ b/js/ui/screencast.js
0df5f8
@@ -1,6 +1,6 @@
0df5f8
 // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
0df5f8
 
0df5f8
-const { Gio, GLib, Shell } = imports.gi;
0df5f8
+const { Gio, GLib, Meta, Shell } = imports.gi;
0df5f8
 const Signals = imports.signals;
0df5f8
 
0df5f8
 const Main = imports.ui.main;
0df5f8
@@ -53,16 +53,27 @@ var ScreencastService = class {
0df5f8
         this._stopRecordingForSender(name);
0df5f8
     }
0df5f8
 
0df5f8
-    _stopRecordingForSender(sender) {
0df5f8
+    _stopRecordingForSender(sender, closeNow=false) {
0df5f8
         let recorder = this._recorders.get(sender);
0df5f8
         if (!recorder)
0df5f8
             return false;
0df5f8
 
0df5f8
         Gio.bus_unwatch_name(recorder._watchNameId);
0df5f8
-        recorder.close();
0df5f8
+        if (closeNow)
0df5f8
+            recorder.close_now();
0df5f8
+        else
0df5f8
+            recorder.close();
0df5f8
         this._recorders.delete(sender);
0df5f8
         this.emit('updated');
0df5f8
 
0df5f8
+        let connection = this._dbusImpl.get_connection();
0df5f8
+        let info = this._dbusImpl.get_info();
0df5f8
+        connection.emit_signal(sender,
0df5f8
+            this._dbusImpl.get_object_path(),
0df5f8
+            info ? info.name : null,
0df5f8
+            'Stopped',
0df5f8
+            null);
0df5f8
+
0df5f8
         return true;
0df5f8
     }
0df5f8
 
0df5f8
@@ -78,6 +89,53 @@ var ScreencastService = class {
0df5f8
             recorder.set_draw_cursor(options['draw-cursor']);
0df5f8
     }
0df5f8
 
0df5f8
+    _ensureResourceScaleChangedHandler() {
0df5f8
+        if (this._resourceScaleChangedHandlerId)
0df5f8
+            return;
0df5f8
+
0df5f8
+        this._resourceScaleChangedHandlerId =
0df5f8
+            global.stage.connect('notify::resource-scale',
0df5f8
+                () => {
0df5f8
+                    for (let sender of this._recorders.keys()) {
0df5f8
+                        let recorder = this._recorders.get(sender);
0df5f8
+
0df5f8
+                        if (!recorder.is_recording())
0df5f8
+                            continue;
0df5f8
+
0df5f8
+                        this._stopRecordingForSender(sender, true);
0df5f8
+                    }
0df5f8
+                });
0df5f8
+    }
0df5f8
+
0df5f8
+    _ensureMonitorsChangedHandler() {
0df5f8
+        if (this._monitorsChangedHandlerId)
0df5f8
+            return;
0df5f8
+
0df5f8
+        this._monitorsChangedHandlerId = Main.layoutManager.connect('monitors-changed',
0df5f8
+            () => {
0df5f8
+                for (let sender of this._recorders.keys()) {
0df5f8
+                    let recorder = this._recorders.get(sender);
0df5f8
+
0df5f8
+                    if (!recorder.is_recording())
0df5f8
+                        continue;
0df5f8
+
0df5f8
+                    let geometry = recorder._geometry;
0df5f8
+                    let screenWidth = global.screen_width;
0df5f8
+                    let screenHeight = global.screen_height;
0df5f8
+
0df5f8
+                    if (recorder._isAreaScreecast) {
0df5f8
+                        if (geometry.x + geometry.width > screenWidth ||
0df5f8
+                            geometry.y + geometry.height > screenHeight)
0df5f8
+                            this._stopRecordingForSender(sender, true);
0df5f8
+                    } else {
0df5f8
+                        if (geometry.width != screenWidth ||
0df5f8
+                            geometry.height != screenHeight)
0df5f8
+                            this._stopRecordingForSender(sender, true);
0df5f8
+                    }
0df5f8
+                }
0df5f8
+            });
0df5f8
+    }
0df5f8
+
0df5f8
     ScreencastAsync(params, invocation) {
0df5f8
         let returnValue = [false, ''];
0df5f8
         if (!Main.sessionMode.allowScreencast ||
0df5f8
@@ -95,8 +153,20 @@ var ScreencastService = class {
0df5f8
             this._applyOptionalParameters(recorder, options);
0df5f8
             let [success, fileName] = recorder.record();
0df5f8
             returnValue = [success, fileName ? fileName : ''];
0df5f8
-            if (!success)
0df5f8
+            if (success) {
0df5f8
+                recorder._isAreaScreecast = false;
0df5f8
+                recorder._geometry =
0df5f8
+                    new Meta.Rectangle({
0df5f8
+                        x: 0,
0df5f8
+                        y: 0,
0df5f8
+                        width: global.screen_width,
0df5f8
+                        height: global.screen_height
0df5f8
+                    });
0df5f8
+                this._ensureResourceScaleChangedHandler();
0df5f8
+                this._ensureMonitorsChangedHandler();
0df5f8
+            } else {
0df5f8
                 this._stopRecordingForSender(sender);
0df5f8
+            }
0df5f8
         }
0df5f8
 
0df5f8
         invocation.return_value(GLib.Variant.new('(bs)', returnValue));
0df5f8
@@ -131,8 +201,20 @@ var ScreencastService = class {
0df5f8
             this._applyOptionalParameters(recorder, options);
0df5f8
             let [success, fileName] = recorder.record();
0df5f8
             returnValue = [success, fileName ? fileName : ''];
0df5f8
-            if (!success)
0df5f8
+            if (success) {
0df5f8
+                recorder._isAreaScreecast = true;
0df5f8
+                recorder._geometry =
0df5f8
+                    new Meta.Rectangle({
0df5f8
+                        x: x,
0df5f8
+                        y: y,
0df5f8
+                        width: width,
0df5f8
+                        height: height
0df5f8
+                    });
0df5f8
+                this._ensureResourceScaleChangedHandler();
0df5f8
+                this._ensureMonitorsChangedHandler();
0df5f8
+            } else {
0df5f8
                 this._stopRecordingForSender(sender);
0df5f8
+            }
0df5f8
         }
0df5f8
 
0df5f8
         invocation.return_value(GLib.Variant.new('(bs)', returnValue));
0df5f8
diff --git a/src/shell-recorder.c b/src/shell-recorder.c
0df5f8
index 0203ecf1c..e561a0152 100644
0df5f8
--- a/src/shell-recorder.c
0df5f8
+++ b/src/shell-recorder.c
0df5f8
@@ -511,21 +511,6 @@ recorder_update_size (ShellRecorder *recorder)
0df5f8
     }
0df5f8
 }
0df5f8
 
0df5f8
-static void
0df5f8
-recorder_on_stage_notify_size (GObject          *object,
0df5f8
-                               GParamSpec       *pspec,
0df5f8
-                               ShellRecorder    *recorder)
0df5f8
-{
0df5f8
-  recorder_update_size (recorder);
0df5f8
-
0df5f8
-  /* This breaks the recording but tweaking the GStreamer pipeline a bit
0df5f8
-   * might make it work, at least if the codec can handle a stream where
0df5f8
-   * the frame size changes in the middle.
0df5f8
-   */
0df5f8
-  if (recorder->current_pipeline)
0df5f8
-    recorder_pipeline_set_caps (recorder->current_pipeline);
0df5f8
-}
0df5f8
-
0df5f8
 static gboolean
0df5f8
 recorder_idle_redraw (gpointer data)
0df5f8
 {
0df5f8
@@ -622,12 +607,6 @@ recorder_connect_stage_callbacks (ShellRecorder *recorder)
0df5f8
                     G_CALLBACK (recorder_on_stage_destroy), recorder);
0df5f8
   g_signal_connect_after (recorder->stage, "paint",
0df5f8
                           G_CALLBACK (recorder_on_stage_paint), recorder);
0df5f8
-  g_signal_connect (recorder->stage, "notify::width",
0df5f8
-                    G_CALLBACK (recorder_on_stage_notify_size), recorder);
0df5f8
-  g_signal_connect (recorder->stage, "notify::height",
0df5f8
-                    G_CALLBACK (recorder_on_stage_notify_size), recorder);
0df5f8
-  g_signal_connect (recorder->stage, "notify::resource-scale",
0df5f8
-                    G_CALLBACK (recorder_on_stage_notify_size), recorder);
0df5f8
 }
0df5f8
 
0df5f8
 static void
0df5f8
@@ -639,9 +618,6 @@ recorder_disconnect_stage_callbacks (ShellRecorder *recorder)
0df5f8
   g_signal_handlers_disconnect_by_func (recorder->stage,
0df5f8
                                         (void *)recorder_on_stage_paint,
0df5f8
                                         recorder);
0df5f8
-  g_signal_handlers_disconnect_by_func (recorder->stage,
0df5f8
-                                        (void *)recorder_on_stage_notify_size,
0df5f8
-                                        recorder);
0df5f8
 
0df5f8
   /* We don't don't deselect for cursor changes in case someone else just
0df5f8
    * happened to be selecting for cursor events on the same window; sending
0df5f8
@@ -1578,6 +1554,32 @@ shell_recorder_record (ShellRecorder  *recorder,
0df5f8
   return TRUE;
0df5f8
 }
0df5f8
 
0df5f8
+/**
0df5f8
+ * shell_recorder_close_now:
0df5f8
+ * @recorder: the #ShellRecorder
0df5f8
+ *
0df5f8
+ * Stops recording immediately. It's possible to call shell_recorder_record()
0df5f8
+ * again to reopen a new recording stream, but unless change the recording
0df5f8
+ * filename, this may result in the old recording being overwritten.
0df5f8
+ */
0df5f8
+void
0df5f8
+shell_recorder_close_now (ShellRecorder *recorder)
0df5f8
+{
0df5f8
+  g_return_if_fail (SHELL_IS_RECORDER (recorder));
0df5f8
+  g_return_if_fail (recorder->state != RECORDER_STATE_CLOSED);
0df5f8
+
0df5f8
+  recorder_remove_update_pointer_timeout (recorder);
0df5f8
+  recorder_close_pipeline (recorder);
0df5f8
+
0df5f8
+  recorder->state = RECORDER_STATE_CLOSED;
0df5f8
+
0df5f8
+  /* Reenable after the recording */
0df5f8
+  meta_enable_unredirect_for_display (shell_global_get_display (shell_global_get ()));
0df5f8
+
0df5f8
+  /* Release the refcount we took when we started recording */
0df5f8
+  g_object_unref (recorder);
0df5f8
+}
0df5f8
+
0df5f8
 /**
0df5f8
  * shell_recorder_close:
0df5f8
  * @recorder: the #ShellRecorder
0df5f8
diff --git a/src/shell-recorder.h b/src/shell-recorder.h
0df5f8
index c1e0e6368..1c3e6aab4 100644
0df5f8
--- a/src/shell-recorder.h
0df5f8
+++ b/src/shell-recorder.h
0df5f8
@@ -37,6 +37,7 @@ void               shell_recorder_set_area     (ShellRecorder *recorder,
0df5f8
 gboolean           shell_recorder_record       (ShellRecorder  *recorder,
0df5f8
                                                 char          **filename_used);
0df5f8
 void               shell_recorder_close        (ShellRecorder *recorder);
0df5f8
+void               shell_recorder_close_now    (ShellRecorder *recorder);
0df5f8
 void               shell_recorder_pause        (ShellRecorder *recorder);
0df5f8
 gboolean           shell_recorder_is_recording (ShellRecorder *recorder);
0df5f8
 
0df5f8
-- 
0df5f8
2.27.0
0df5f8