diff --git a/src/ScreenCast/Dialog.vala b/src/ScreenCast/Dialog.vala new file mode 100644 index 00000000..a4d46828 --- /dev/null +++ b/src/ScreenCast/Dialog.vala @@ -0,0 +1,83 @@ +public class ScreenCast.Dialog : Granite.Dialog { + public SourceType source_types { get; construct; } + public bool allow_multiple { get; construct; } + + public Dialog (SourceType source_types, bool allow_multiple) { + Object (source_types: source_types, allow_multiple: allow_multiple); + } + + private List window_rows; + private List monitor_rows; + + construct { + window_rows = new List (); + monitor_rows = new List (); + + var list_box = new Gtk.ListBox () { + selection_mode = MULTIPLE, + vexpand = true + }; + list_box.add_css_class ("boxed-list"); + list_box.add_css_class (Granite.STYLE_CLASS_RICH_LIST); + list_box.set_header_func (header_func); + + Gtk.CheckButton? group = null; + + if (WINDOW in source_types) { + //TODO: populate windows + } + + if (MONITOR in source_types) { + var monitor_tracker = new MonitorTracker (); + + foreach (var monitor in monitor_tracker.monitors) { + var row = new SelectionRow (MONITOR, monitor.connector, + monitor.display_name, null, allow_multiple ? null : group); + + monitor_rows.append (row); + + group = row.check_button; + + list_box.append (row); + } + } + + get_content_area ().append (list_box); + + add_button (_("Cancel"), Gtk.ResponseType.CANCEL); + add_button (_("Share"), Gtk.ResponseType.ACCEPT).add_css_class (Granite.STYLE_CLASS_SUGGESTED_ACTION); + } + + private void header_func (Gtk.ListBoxRow row, Gtk.ListBoxRow? prev) { + if (!(row is SelectionRow) && prev != null && !(prev is SelectionRow)) { + return; + } + + var selection_row = (SelectionRow) row; + + if (prev == null || ((SelectionRow) prev).source_type != selection_row.source_type) { + var label = selection_row.source_type == WINDOW ? _("Windows") : _("Monitors"); + selection_row.set_header (new Granite.HeaderLabel (label)); + } + } + + public uint64[] get_selected_windows () { + uint64[] result = {}; + foreach (var row in window_rows) { + if (row.selected) { + result += (uint64) row.id; + } + } + return result; + } + + public string[] get_selected_monitors () { + string[] result = {}; + foreach (var row in monitor_rows) { + if (row.selected) { + result += (string) row.id; + } + } + return result; + } +} \ No newline at end of file diff --git a/src/ScreenCast/Portal.vala b/src/ScreenCast/Portal.vala index 7d52c66e..0635dca8 100644 --- a/src/ScreenCast/Portal.vala +++ b/src/ScreenCast/Portal.vala @@ -1,7 +1,7 @@ [Flags] public enum ScreenCast.SourceType { MONITOR = 1, - WINODW = 2, + WINDOW = 2, VIRTUAL = 4, } @@ -14,7 +14,7 @@ public enum ScreenCast.CursorMode { [DBus (name = "org.freedesktop.impl.portal.ScreenCast")] public class ScreenCast.Portal : Object { - public SourceType available_source_types { get; default = VIRTUAL; } + public SourceType available_source_types { get; default = MONITOR | WINDOW | VIRTUAL; } public CursorMode available_cursor_modes { get; default = HIDDEN; } // TODO: What is GNOMEs cursor mode public uint version { get; default = 3; } diff --git a/src/ScreenCast/SelectionRow.vala b/src/ScreenCast/SelectionRow.vala new file mode 100644 index 00000000..38d3775d --- /dev/null +++ b/src/ScreenCast/SelectionRow.vala @@ -0,0 +1,39 @@ +public class ScreenCast.SelectionRow : Gtk.ListBoxRow { + public SourceType source_type { get; construct; } + public Variant id { get; construct; } + public string label { get; construct; } + public Icon? icon { get; construct; } + public Gtk.CheckButton? group { get; construct; } + + public Gtk.CheckButton check_button { get; construct; } + + public bool selected { get; set; default = false; } + + public SelectionRow (SourceType source_type, Variant id, string label, Icon? icon, Gtk.CheckButton? group) { + Object ( + source_type: source_type, + id: id, + label: label, + icon: icon, + group: group + ); + } + + construct { + var box = new Gtk.Box (HORIZONTAL, 6); + + check_button = new Gtk.CheckButton (); + box.append (check_button); + check_button.set_group (group); + + if (icon != null) { + box.append (new Gtk.Image.from_gicon (icon)); + } + + box.append (new Gtk.Label (label)); + + child = box; + + check_button.bind_property ("active", this, "selected", DEFAULT); + } +} \ No newline at end of file diff --git a/src/ScreenCast/Session.vala b/src/ScreenCast/Session.vala index b9497b98..13fbe357 100644 --- a/src/ScreenCast/Session.vala +++ b/src/ScreenCast/Session.vala @@ -78,19 +78,42 @@ public class ScreenCast.Session : Object { } internal async PipeWireStream[]? start () { - //TODO: All user interaction. I.e. permission stuff, allow multiple, which monitor, which window, etc. + bool allow = false; + + var dialog = new Dialog (source_types, allow_multiple); + dialog.response.connect ((response) => Idle.add (() => { + dialog.destroy (); + allow = response == Gtk.ResponseType.ACCEPT; + start.callback (); + return Source.REMOVE; + })); + dialog.present (); + + yield; + + if (!allow) { + return null; + } //Should we fail if one fails or if all fail? Currently it's all - if (VIRTUAL in source_types && yield record_virtual ()) { - required_streams++; + foreach (var window in dialog.get_selected_windows ()) { + if (yield record_window (window)) { + required_streams++; + } } - if (MONITOR in source_types && yield select_monitor ()) { + foreach (var connector in dialog.get_selected_monitors ()) { + if (yield record_monitor (connector)) { + required_streams++; + } + } + + if (VIRTUAL in source_types && yield record_virtual ()) { required_streams++; } if (required_streams == 0) { - warning ("At least one source type has to be successfully setup."); + warning ("At least one stream has to be successfully setup."); return null; } @@ -110,6 +133,18 @@ public class ScreenCast.Session : Object { return streams; } + private async bool record_window (uint64 id) { + ObjectPath path; + try { + path = yield session.record_virtual (new HashTable (str_hash, str_equal)); + } catch (Error e) { + warning ("Failed to record window: %s", e.message); + return false; + } + + return yield setup_mutter_stream (path, VIRTUAL); + } + private async bool record_virtual () { ObjectPath path; try { diff --git a/src/meson.build b/src/meson.build index b52e4215..90d99bc0 100644 --- a/src/meson.build +++ b/src/meson.build @@ -11,7 +11,9 @@ executable( 'ScreenCast/MonitorTracker/Interface.vala', 'ScreenCast/MonitorTracker/Monitor.vala', 'ScreenCast/MonitorTracker/MonitorTracker.vala', + 'ScreenCast/Dialog.vala', 'ScreenCast/Portal.vala', + 'ScreenCast/SelectionRow.vala', 'ScreenCast/Session.vala', 'Wallpaper/Portal.vala', configure_file(input: 'Config.vala.in', output: '@BASENAME@', configuration: conf_data),