From d4942858baf5cd36240320a63f767ad0e665ca07 Mon Sep 17 00:00:00 2001 From: "Jasper St. Pierre" Date: Wed, 17 Jul 2013 15:26:06 -0400 Subject: [PATCH] Add a screencast indicator for when we're recording This will replace the indicator painted on the stage right now. This unfortunately does not work for the recorder triggered by the keybinding -- we'll simply replace the in-shell code with a keybinding powered by gnome-settings-daemon. --- data/theme/gnome-shell.css | 4 ++ js/Makefile.am | 1 + js/ui/main.js | 3 + js/ui/panel.js | 2 + js/ui/screencast.js | 9 +++ js/ui/shellDBus.js | 2 - js/ui/status/screencast.js | 26 +++++++ src/shell-recorder.c | 140 +------------------------------------ 8 files changed, 47 insertions(+), 140 deletions(-) create mode 100644 js/ui/status/screencast.js diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css index e81e882bd..4dae82c0c 100644 --- a/data/theme/gnome-shell.css +++ b/data/theme/gnome-shell.css @@ -660,6 +660,10 @@ StScrollBar StButton#vhandle:active { icon-size: 32px; } +.screencast-indicator { + color: #ff0000; +} + /* Overview */ #overview { diff --git a/js/Makefile.am b/js/Makefile.am index 11a1d5f4b..5eaa6a8f7 100644 --- a/js/Makefile.am +++ b/js/Makefile.am @@ -96,6 +96,7 @@ nobase_dist_js_DATA = \ ui/status/rfkill.js \ ui/status/volume.js \ ui/status/bluetooth.js \ + ui/status/screencast.js \ ui/status/system.js \ ui/switcherPopup.js \ ui/tweener.js \ diff --git a/js/ui/main.js b/js/ui/main.js index 17f05e17e..a2175f5e0 100644 --- a/js/ui/main.js +++ b/js/ui/main.js @@ -28,6 +28,7 @@ const LoginManager = imports.misc.loginManager; const LookingGlass = imports.ui.lookingGlass; const NotificationDaemon = imports.ui.notificationDaemon; const WindowAttentionHandler = imports.ui.windowAttentionHandler; +const Screencast = imports.ui.screencast; const ScreenShield = imports.ui.screenShield; const Scripting = imports.ui.scripting; const SessionMode = imports.ui.sessionMode; @@ -59,6 +60,7 @@ let sessionMode = null; let shellDBusService = null; let shellMountOpDBusService = null; let screenSaverDBus = null; +let screencastService = null; let modalCount = 0; let keybindingMode = Shell.KeyBindingMode.NONE; let modalActorFocusStack = []; @@ -151,6 +153,7 @@ function _initializeUI() { // working until it's updated. uiGroup = layoutManager.uiGroup; + screencastService = new Screencast.ScreencastService(); xdndHandler = new XdndHandler.XdndHandler(); ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager(); osdWindow = new OsdWindow.OsdWindow(); diff --git a/js/ui/panel.js b/js/ui/panel.js index 3f207819f..aaabad1a9 100644 --- a/js/ui/panel.js +++ b/js/ui/panel.js @@ -815,7 +815,9 @@ const AggregateMenu = new Lang.Class({ this._volume = new imports.ui.status.volume.Indicator(); this._brightness = new imports.ui.status.brightness.Indicator(); this._system = new imports.ui.status.system.Indicator(); + this._screencast = new imports.ui.status.screencast.Indicator(); + this._indicators.add_child(this._screencast.indicators); this._indicators.add_child(this._network.indicators); this._indicators.add_child(this._bluetooth.indicators); this._indicators.add_child(this._rfkill.indicators); diff --git a/js/ui/screencast.js b/js/ui/screencast.js index 307cab085..61dc692ef 100644 --- a/js/ui/screencast.js +++ b/js/ui/screencast.js @@ -4,6 +4,7 @@ const Gio = imports.gi.Gio; const GLib = imports.gi.GLib; const Lang = imports.lang; const Shell = imports.gi.Shell; +const Signals = imports.signals; const Hash = imports.misc.hash; const Main = imports.ui.main; @@ -44,6 +45,10 @@ const ScreencastService = new Lang.Class({ Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated)); }, + get isRecording() { + return this._recorders.size() > 0; + }, + _ensureRecorderForSender: function(sender) { let recorder = this._recorders.get(sender); if (!recorder) { @@ -52,6 +57,7 @@ const ScreencastService = new Lang.Class({ Gio.bus_watch_name(Gio.BusType.SESSION, sender, 0, null, Lang.bind(this, this._onNameVanished)); this._recorders.set(sender, recorder); + this.emit('updated'); } return recorder; }, @@ -62,6 +68,7 @@ const ScreencastService = new Lang.Class({ for (let sender in this._recorders.keys()) this._recorders.delete(sender); + this.emit('updated'); }, _onNameVanished: function(connection, name) { @@ -76,6 +83,7 @@ const ScreencastService = new Lang.Class({ Gio.bus_unwatch_name(recorder._watchNameId); recorder.close(); this._recorders.delete(sender); + this.emit('updated'); return true; }, @@ -137,3 +145,4 @@ const ScreencastService = new Lang.Class({ invocation.return_value(GLib.Variant.new('(b)', [success])); } }); +Signals.addSignalMethods(ScreencastService.prototype); diff --git a/js/ui/shellDBus.js b/js/ui/shellDBus.js index 0099978d6..78a9c5b64 100644 --- a/js/ui/shellDBus.js +++ b/js/ui/shellDBus.js @@ -12,7 +12,6 @@ const ExtensionDownloader = imports.ui.extensionDownloader; const ExtensionUtils = imports.misc.extensionUtils; const Hash = imports.misc.hash; const Main = imports.ui.main; -const Screencast = imports.ui.screencast; const Screenshot = imports.ui.screenshot; const GnomeShellIface = @@ -73,7 +72,6 @@ const GnomeShell = new Lang.Class({ this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell'); this._extensionsService = new GnomeShellExtensions(); - this._screencastService = new Screencast.ScreencastService(); this._screenshotService = new Screenshot.ScreenshotService(); this._grabbedAccelerators = new Hash.Map(); diff --git a/js/ui/status/screencast.js b/js/ui/status/screencast.js new file mode 100644 index 000000000..7a35ff0fb --- /dev/null +++ b/js/ui/status/screencast.js @@ -0,0 +1,26 @@ +// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- + +const Lang = imports.lang; + +const Main = imports.ui.main; +const PanelMenu = imports.ui.panelMenu; + +const Indicator = new Lang.Class({ + Name: 'ScreencastIndicator', + Extends: PanelMenu.SystemIndicator, + + _init: function() { + this.parent(); + + this._indicator = this._addIndicator(); + this._indicator.icon_name = 'media-record-symbolic'; + this._indicator.add_style_class_name('screencast-indicator'); + this._sync(); + + Main.screencastService.connect('updated', Lang.bind(this, this._sync)); + }, + + _sync: function() { + this._indicator.visible = Main.screencastService.isRecording; + }, +}); diff --git a/src/shell-recorder.c b/src/shell-recorder.c index eb0d44748..6b2bdfa64 100644 --- a/src/shell-recorder.c +++ b/src/shell-recorder.c @@ -69,16 +69,12 @@ struct _ShellRecorder { int xinput_opcode; - CoglHandle recording_icon; /* icon shown while playing */ - GSettings *a11y_settings; gboolean draw_cursor; cairo_surface_t *cursor_image; int cursor_hot_x; int cursor_hot_y; - gboolean only_paint; /* Used to temporarily suppress recording */ - int framerate; char *pipeline_description; char *file_template; @@ -170,57 +166,6 @@ G_DEFINE_TYPE(ShellRecorder, shell_recorder, G_TYPE_OBJECT); */ #define DEFAULT_MEMORY_TARGET (512*1024) -/* Create an emblem to show at the lower-left corner of the stage while - * recording. The emblem is drawn *after* we record the frame so doesn't - * show up in the frame. - */ -static CoglHandle -create_recording_icon (void) -{ - cairo_surface_t *surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 32, 32); - cairo_t *cr; - cairo_pattern_t *pat; - CoglHandle texture; - - cr = cairo_create (surface); - - /* clear to transparent */ - cairo_save (cr); - cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR); - cairo_paint (cr); - cairo_restore (cr); - - /* radial "glow" */ - pat = cairo_pattern_create_radial (16, 16, 6, - 16, 16, 14); - cairo_pattern_add_color_stop_rgba (pat, 0.0, - 1, 0, 0, 1); /* opaque red */ - cairo_pattern_add_color_stop_rgba (pat, 1.0, - 1, 0, 0, 0); /* transparent red */ - - cairo_set_source (cr, pat); - cairo_paint (cr); - cairo_pattern_destroy (pat); - - /* red circle */ - cairo_arc (cr, 16, 16, 8, - 0, 2 * M_PI); - cairo_set_source_rgb (cr, 1, 0, 0); - cairo_fill (cr); - - cairo_destroy (cr); - - texture = cogl_texture_new_from_data (32, 32, - COGL_TEXTURE_NONE, - CLUTTER_CAIRO_FORMAT_ARGB32, - COGL_PIXEL_FORMAT_ANY, - cairo_image_surface_get_stride (surface), - cairo_image_surface_get_data (surface)); - cairo_surface_destroy (surface); - - return texture; -} - static guint get_memory_target (void) { @@ -278,7 +223,6 @@ shell_recorder_init (ShellRecorder *recorder) recorder->gdk_screen = gdk_screen_get_default (); - recorder->recording_icon = create_recording_icon (); recorder->memory_target = get_memory_target(); recorder->a11y_settings = g_settings_new (A11Y_APPS_SCHEMA); @@ -303,8 +247,6 @@ shell_recorder_finalize (GObject *object) recorder_set_pipeline (recorder, NULL); recorder_set_file_template (recorder, NULL); - cogl_handle_unref (recorder->recording_icon); - g_clear_object (&recorder->a11y_settings); G_OBJECT_CLASS (shell_recorder_parent_class)->finalize (object); @@ -340,20 +282,7 @@ recorder_update_memory_used (ShellRecorder *recorder, } if (memory_used != recorder->memory_used) - { - recorder->memory_used = memory_used; - if (repaint) - { - /* In other cases we just queue a redraw even if we only need - * to repaint and not redraw a frame, but having changes in - * memory usage cause frames to be painted and memory used - * seems like a bad idea. - */ - recorder->only_paint = TRUE; - clutter_stage_ensure_redraw (recorder->stage); - recorder->only_paint = FALSE; - } - } + recorder->memory_used = memory_used; } /* Timeout used to avoid not drawing for more than MAXIMUM_PAUSE_TIME @@ -475,56 +404,6 @@ recorder_draw_cursor (ShellRecorder *recorder, gst_buffer_unmap (buffer, &info); } -/* Draw an overlay indicating how much of the target memory is used - * for buffering frames. - */ -static void -recorder_draw_buffer_meter (ShellRecorder *recorder) -{ - int fill_level; - GdkRectangle primary_monitor; - float rects[16]; - - gdk_screen_get_monitor_workarea (recorder->gdk_screen, - gdk_screen_get_primary_monitor (recorder->gdk_screen), - &primary_monitor); - - recorder_update_memory_used (recorder, FALSE); - - /* As the buffer gets more full, we go from green, to yellow, to red */ - if (recorder->memory_used > (recorder->memory_target * 3) / 4) - cogl_set_source_color4f (1, 0, 0, 1); - else if (recorder->memory_used > recorder->memory_target / 2) - cogl_set_source_color4f (1, 1, 0, 1); - else - cogl_set_source_color4f (0, 1, 0, 1); - - fill_level = MIN (60, (recorder->memory_used * 60) / recorder->memory_target); - - /* A hollow rectangle filled from the left to fill_level */ - rects[0] = primary_monitor.x + primary_monitor.width - 64; - rects[1] = primary_monitor.y + primary_monitor.height - 10; - rects[2] = primary_monitor.x + primary_monitor.width - 2; - rects[3] = primary_monitor.y + primary_monitor.height - 9; - - rects[4] = primary_monitor.x + primary_monitor.width - 64; - rects[5] = primary_monitor.y + primary_monitor.height - 9; - rects[6] = primary_monitor.x + primary_monitor.width - (63 - fill_level); - rects[7] = primary_monitor.y + primary_monitor.height - 3; - - rects[8] = primary_monitor.x + primary_monitor.width - 3; - rects[9] = primary_monitor.y + primary_monitor.height - 9; - rects[10] = primary_monitor.x + primary_monitor.width - 2; - rects[11] = primary_monitor.y + primary_monitor.height - 3; - - rects[12] = primary_monitor.x + primary_monitor.width - 64; - rects[13] = primary_monitor.y + primary_monitor.height - 3; - rects[14] = primary_monitor.x + primary_monitor.width - 2; - rects[15] = primary_monitor.y + primary_monitor.height - 2; - - cogl_rectangles (rects, 4); -} - /* We want to time-stamp each frame based on the actual time it was * recorded. We probably should use the pipeline clock rather than * gettimeofday(): that would be needed to get sync'ed audio correct. @@ -608,22 +487,7 @@ recorder_on_stage_paint (ClutterActor *actor, ShellRecorder *recorder) { if (recorder->state == RECORDER_STATE_RECORDING) - { - GdkRectangle primary_monitor; - - gdk_screen_get_monitor_workarea (recorder->gdk_screen, - gdk_screen_get_primary_monitor (recorder->gdk_screen), - &primary_monitor); - if (!recorder->only_paint) - recorder_record_frame (recorder); - - cogl_set_source_texture (recorder->recording_icon); - cogl_rectangle (primary_monitor.x + primary_monitor.width - 32, primary_monitor.y + primary_monitor.height - 42, - primary_monitor.x + primary_monitor.width, primary_monitor.y + primary_monitor.height - 10); - } - - if (recorder->state == RECORDER_STATE_RECORDING || recorder->memory_used != 0) - recorder_draw_buffer_meter (recorder); + recorder_record_frame (recorder); } static void