diff --git a/data/meson.build b/data/meson.build
index e35455ef..bc26276a 100644
--- a/data/meson.build
+++ b/data/meson.build
@@ -15,4 +15,6 @@ install_data(join_paths('icons', 'scalable', 'wayfire.svg'), install_dir: join_p
install_data('wf-locker-password', install_dir:'/etc/pam.d/')
+install_data('xdpw/wayfire', install_dir: '/etc/xdg/xdg-desktop-portal-wlr/')
+
subdir('css')
\ No newline at end of file
diff --git a/data/xdpw/wayfire b/data/xdpw/wayfire
new file mode 100644
index 00000000..8b0ac616
--- /dev/null
+++ b/data/xdpw/wayfire
@@ -0,0 +1,3 @@
+[screencast]
+chooser_cmd=wf-stream-chooser
+chooser_type=simple
\ No newline at end of file
diff --git a/proto/ext-foreign-toplevel-list-v1.xml b/proto/ext-foreign-toplevel-list-v1.xml
new file mode 100644
index 00000000..11b0113a
--- /dev/null
+++ b/proto/ext-foreign-toplevel-list-v1.xml
@@ -0,0 +1,219 @@
+
+
+
+ Copyright © 2018 Ilia Bozhinov
+ Copyright © 2020 Isaac Freund
+ Copyright © 2022 wb9688
+ Copyright © 2023 i509VCB
+
+ Permission to use, copy, modify, distribute, and sell this
+ software and its documentation for any purpose is hereby granted
+ without fee, provided that the above copyright notice appear in
+ all copies and that both that copyright notice and this permission
+ notice appear in supporting documentation, and that the name of
+ the copyright holders not be used in advertising or publicity
+ pertaining to distribution of the software without specific,
+ written prior permission. The copyright holders make no
+ representations about the suitability of this software for any
+ purpose. It is provided "as is" without express or implied
+ warranty.
+
+ THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+ THIS SOFTWARE.
+
+
+
+ The purpose of this protocol is to provide protocol object handles for
+ toplevels, possibly originating from another client.
+
+ This protocol is intentionally minimalistic and expects additional
+ functionality (e.g. creating a screencopy source from a toplevel handle,
+ getting information about the state of the toplevel) to be implemented
+ in extension protocols.
+
+ The compositor may choose to restrict this protocol to a special client
+ launched by the compositor itself or expose it to all clients,
+ this is compositor policy.
+
+ The key words "must", "must not", "required", "shall", "shall not",
+ "should", "should not", "recommended", "may", and "optional" in this
+ document are to be interpreted as described in IETF RFC 2119.
+
+ Warning! The protocol described in this file is currently in the testing
+ phase. Backward compatible changes may be added together with the
+ corresponding interface version bump. Backward incompatible changes can
+ only be done by creating a new major version of the extension.
+
+
+
+
+ A toplevel is defined as a surface with a role similar to xdg_toplevel.
+ XWayland surfaces may be treated like toplevels in this protocol.
+
+ After a client binds the ext_foreign_toplevel_list_v1, each mapped
+ toplevel window will be sent using the ext_foreign_toplevel_list_v1.toplevel
+ event.
+
+ Clients which only care about the current state can perform a roundtrip after
+ binding this global.
+
+ For each instance of ext_foreign_toplevel_list_v1, the compositor must
+ create a new ext_foreign_toplevel_handle_v1 object for each mapped toplevel.
+
+ If a compositor implementation sends the ext_foreign_toplevel_list_v1.finished
+ event after the global is bound, the compositor must not send any
+ ext_foreign_toplevel_list_v1.toplevel events.
+
+
+
+
+ This event is emitted whenever a new toplevel window is created. It is
+ emitted for all toplevels, regardless of the app that has created them.
+
+ All initial properties of the toplevel (identifier, title, app_id) will be sent
+ immediately after this event using the corresponding events for
+ ext_foreign_toplevel_handle_v1. The compositor will use the
+ ext_foreign_toplevel_handle_v1.done event to indicate when all data has
+ been sent.
+
+
+
+
+
+
+ This event indicates that the compositor is done sending events
+ to this object. The client should destroy the object.
+ See ext_foreign_toplevel_list_v1.destroy for more information.
+
+ The compositor must not send any more toplevel events after this event.
+
+
+
+
+
+ This request indicates that the client no longer wishes to receive
+ events for new toplevels.
+
+ The Wayland protocol is asynchronous, meaning the compositor may send
+ further toplevel events until the stop request is processed.
+ The client should wait for a ext_foreign_toplevel_list_v1.finished
+ event before destroying this object.
+
+
+
+
+
+ This request should be called either when the client will no longer
+ use the ext_foreign_toplevel_list_v1 or after the finished event
+ has been received to allow destruction of the object.
+
+ If a client wishes to destroy this object it should send a
+ ext_foreign_toplevel_list_v1.stop request and wait for a ext_foreign_toplevel_list_v1.finished
+ event, then destroy the handles and then this object.
+
+
+
+
+
+
+ A ext_foreign_toplevel_handle_v1 object represents a mapped toplevel
+ window. A single app may have multiple mapped toplevels.
+
+
+
+
+ This request should be used when the client will no longer use the handle
+ or after the closed event has been received to allow destruction of the
+ object.
+
+ When a handle is destroyed, a new handle may not be created by the server
+ until the toplevel is unmapped and then remapped. Destroying a toplevel handle
+ is not recommended unless the client is cleaning up child objects
+ before destroying the ext_foreign_toplevel_list_v1 object, the toplevel
+ was closed or the toplevel handle will not be used in the future.
+
+ Other protocols which extend the ext_foreign_toplevel_handle_v1
+ interface should require destructors for extension interfaces be
+ called before allowing the toplevel handle to be destroyed.
+
+
+
+
+
+ The server will emit no further events on the ext_foreign_toplevel_handle_v1
+ after this event. Any requests received aside from the destroy request must
+ be ignored. Upon receiving this event, the client should destroy the handle.
+
+ Other protocols which extend the ext_foreign_toplevel_handle_v1
+ interface must also ignore requests other than destructors.
+
+
+
+
+
+ This event is sent after all changes in the toplevel state have
+ been sent.
+
+ This allows changes to the ext_foreign_toplevel_handle_v1 properties
+ to be atomically applied. Other protocols which extend the
+ ext_foreign_toplevel_handle_v1 interface may use this event to also
+ atomically apply any pending state.
+
+ This event must not be sent after the ext_foreign_toplevel_handle_v1.closed
+ event.
+
+
+
+
+
+ The title of the toplevel has changed.
+
+ The configured state must not be applied immediately. See
+ ext_foreign_toplevel_handle_v1.done for details.
+
+
+
+
+
+
+ The app id of the toplevel has changed.
+
+ The configured state must not be applied immediately. See
+ ext_foreign_toplevel_handle_v1.done for details.
+
+
+
+
+
+
+ This identifier is used to check if two or more toplevel handles belong
+ to the same toplevel.
+
+ The identifier is useful for command line tools or privileged clients
+ which may need to reference an exact toplevel across processes or
+ instances of the ext_foreign_toplevel_list_v1 global.
+
+ The compositor must only send this event when the handle is created.
+
+ The identifier must be unique per toplevel and it's handles. Two different
+ toplevels must not have the same identifier. The identifier is only valid
+ as long as the toplevel is mapped. If the toplevel is unmapped the identifier
+ must not be reused. An identifier must not be reused by the compositor to
+ ensure there are no races when sharing identifiers between processes.
+
+ An identifier is a string that contains up to 32 printable ASCII bytes.
+ An identifier must not be an empty string. It is recommended that a
+ compositor includes an opaque generation value in identifiers. How the
+ generation value is used when generating the identifier is implementation
+ dependent.
+
+
+
+
+
diff --git a/proto/meson.build b/proto/meson.build
index 831308a0..ebcaebfa 100644
--- a/proto/meson.build
+++ b/proto/meson.build
@@ -15,9 +15,10 @@ wayland_scanner_client = generator(
)
client_protocols = [
- 'wlr-foreign-toplevel-management-unstable-v1.xml',
- 'wlr-screencopy.xml',
- wayfire.get_pkgconfig_variable('pkgdatadir') / 'unstable' / 'wayfire-shell-unstable-v2.xml',
+ 'ext-foreign-toplevel-list-v1.xml',
+ 'wlr-foreign-toplevel-management-unstable-v1.xml',
+ 'wlr-screencopy.xml',
+ wayfire.get_pkgconfig_variable('pkgdatadir') / 'unstable' / 'wayfire-shell-unstable-v2.xml',
]
if gbm.found() and drm.found() and not get_option('live-previews-dmabuf').disabled()
diff --git a/src/meson.build b/src/meson.build
index a2fe8f19..78774b7c 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -4,6 +4,7 @@ subdir('background')
subdir('dock')
subdir('locker')
subdir('locker-pin')
+subdir('stream-chooser')
pkgconfig = import('pkgconfig')
pkgconfig.generate(
diff --git a/src/stream-chooser/meson.build b/src/stream-chooser/meson.build
new file mode 100644
index 00000000..90343525
--- /dev/null
+++ b/src/stream-chooser/meson.build
@@ -0,0 +1,13 @@
+deps = [
+ gtkmm,
+ wf_protos,
+ libutil,
+ gtklayershell,
+]
+
+executable(
+ 'wf-stream-chooser',
+ ['stream-chooser.cpp', 'toplevelwidget.cpp', 'outputwidget.cpp'],
+ dependencies: deps,
+ install: true,
+)
\ No newline at end of file
diff --git a/src/stream-chooser/outputwidget.cpp b/src/stream-chooser/outputwidget.cpp
new file mode 100644
index 00000000..6220b28d
--- /dev/null
+++ b/src/stream-chooser/outputwidget.cpp
@@ -0,0 +1,29 @@
+#include
+
+#include "outputwidget.hpp"
+#include "stream-chooser.hpp"
+
+WayfireChooserOutput::WayfireChooserOutput(std::shared_ptr output) : output(output)
+{
+ append(contents);
+ append(model);
+ append(connector);
+
+ /* TODO Contents. We should probably grab screenshots of each output and display them */
+
+ model.set_label(output->get_model());
+ connector.set_label(output->get_connector());
+
+ set_orientation(Gtk::Orientation::VERTICAL);
+
+ output->signal_invalidate().connect([=]
+ {
+ WayfireStreamChooserApp::getInstance().remove_output(output->get_connector());
+ });
+}
+
+void WayfireChooserOutput::print()
+{
+ std::cout << "Monitor: " << output->get_connector() << std::endl;
+ exit(0);
+}
diff --git a/src/stream-chooser/outputwidget.hpp b/src/stream-chooser/outputwidget.hpp
new file mode 100644
index 00000000..e96b6386
--- /dev/null
+++ b/src/stream-chooser/outputwidget.hpp
@@ -0,0 +1,15 @@
+#pragma once
+#include
+#include
+
+class WayfireChooserOutput : public Gtk::Box
+{
+ Gtk::Label connector, model;
+ Gtk::Image contents;
+
+ std::shared_ptr output;
+
+ public:
+ void print();
+ WayfireChooserOutput(std::shared_ptr output);
+};
diff --git a/src/stream-chooser/stream-chooser.cpp b/src/stream-chooser/stream-chooser.cpp
new file mode 100644
index 00000000..a0a1965c
--- /dev/null
+++ b/src/stream-chooser/stream-chooser.cpp
@@ -0,0 +1,228 @@
+#include
+#include
+#include
+#include
+
+#include "stream-chooser.hpp"
+#include "outputwidget.hpp"
+#include "toplevelwidget.hpp"
+
+#define EXT_IMAGE_COPY_CAPTURE_V1 "ext-image-copy-capture-v1"
+
+/* Static callbacks for toplevel list object */
+static void handle_toplevel(void *data,
+ struct ext_foreign_toplevel_list_v1 *list,
+ struct ext_foreign_toplevel_handle_v1 *toplevel)
+{
+ WayfireStreamChooserApp::getInstance().add_toplevel(toplevel);
+}
+
+static void handle_finished(void *data,
+ struct ext_foreign_toplevel_list_v1 *list)
+{
+ ext_foreign_toplevel_list_v1_stop(list);
+ ext_foreign_toplevel_list_v1_destroy(list);
+}
+
+ext_foreign_toplevel_list_v1_listener toplevel_list_v1_impl = {
+ .toplevel = handle_toplevel,
+ .finished = handle_finished,
+};
+
+/* Static callbacks for wayland registry */
+static void registry_add_object(void *data, wl_registry *registry, uint32_t name,
+ const char *interface, uint32_t version)
+{
+ if (strcmp(interface, ext_foreign_toplevel_list_v1_interface.name) == 0)
+ {
+ auto list = (ext_foreign_toplevel_list_v1*)
+ wl_registry_bind(registry, name,
+ &ext_foreign_toplevel_list_v1_interface,
+ version);
+
+ WayfireStreamChooserApp::getInstance().set_toplevel_list(list);
+ ext_foreign_toplevel_list_v1_add_listener(list,
+ &toplevel_list_v1_impl, NULL);
+ } else if (strcmp(interface, EXT_IMAGE_COPY_CAPTURE_V1) == 0)
+ {
+ /* We need this to exist, but we're not using it directly */
+ WayfireStreamChooserApp::getInstance().has_image_copy_capture = true;
+ }
+}
+
+static void registry_remove_object(void *data, struct wl_registry *registry, uint32_t name)
+{}
+
+static struct wl_registry_listener registry_listener =
+{
+ ®istry_add_object,
+ ®istry_remove_object
+};
+
+WayfireStreamChooserApp::WayfireStreamChooserApp() : Gtk::Application("org.wayfire.screen-chooser",
+ Gio::Application::Flags::NONE)
+{
+ signal_activate().connect(sigc::mem_fun(*this, &WayfireStreamChooserApp::activate));
+}
+
+void WayfireStreamChooserApp::activate()
+{
+ window.set_size_request(300, 300);
+ add_window(window);
+ window.set_child(main);
+ main.append(header);
+ main.append(notebook);
+ main.append(buttons);
+
+ notebook.set_expand(true);
+ notebook.append_page(window_list, window_label);
+ notebook.append_page(screen_list, screen_label);
+
+ main.set_orientation(Gtk::Orientation::VERTICAL);
+
+ buttons.set_hexpand(true);
+ buttons.append(cancel);
+ cancel.set_halign(Gtk::Align::START);
+ buttons.append(done);
+ done.set_halign(Gtk::Align::END);
+
+ window_label.set_label("Window");
+ screen_label.set_label("Screen");
+ header.set_label("Choose a view to share");
+
+ cancel.set_label("Cancel");
+ done.set_label("Done");
+ buttons.set_homogeneous(true);
+
+ done.signal_clicked().connect([this] ()
+ {
+ if (notebook.get_current_page() == 0)
+ {
+ auto children = window_list.get_selected_children();
+ if (children.size() == 1)
+ {
+ WayfireChooserTopLevel *cast_child = (WayfireChooserTopLevel*)(children[0]->get_child());
+ cast_child->print();
+ }
+
+ /* TODO Consider an error to let user know the selection was invalid */
+ exit(0);
+ } else
+ {
+ auto children = screen_list.get_selected_children();
+ if (children.size() == 1)
+ {
+ WayfireChooserOutput *cast_child = (WayfireChooserOutput*)(children[0]->get_child());
+ cast_child->print();
+ }
+
+ /* TODO Consider an error to let user know the selection was invalid */
+ exit(0);
+ }
+ });
+
+ cancel.signal_clicked().connect([] ()
+ {
+ exit(0);
+ });
+
+ /* Attempt to get Window list */
+ auto gdk_display = gdk_display_get_default();
+ auto display = gdk_wayland_display_get_wl_display(gdk_display);
+
+ this->display = display;
+
+ wl_registry *registry = wl_display_get_registry(display);
+ wl_registry_add_listener(registry, ®istry_listener, this);
+ this->registry = registry;
+ wl_display_roundtrip(display);
+
+ if (this->list && has_image_copy_capture)
+ {} else
+ {
+ std::cerr << "Compositor doesn't support" <<
+ " ext-foreign-toplevel-list and/or ext-image-copy-capture-v1." <<
+ " Only screens can be cast currently." << std::endl;
+ window_label.set_sensitive(false);
+ window_label.set_tooltip_text("This compositor does not currently support sharing individual windows");
+ notebook.set_current_page(1);
+ }
+
+ /* Get output list */
+ auto gtkdisplay = Gdk::Display::get_default();
+ auto monitors = gtkdisplay->get_monitors();
+ monitors->signal_items_changed().connect(
+ [this] (const int pos, const int rem, const int add)
+ {
+ auto display = Gdk::Display::get_default();
+ auto monitors = display->get_monitors();
+ int num_monitors = monitors->get_n_items();
+ for (int i = 0; i < num_monitors; i++)
+ {
+ auto obj = std::dynamic_pointer_cast(monitors->get_object(i));
+ add_output(obj);
+ }
+ });
+
+ // Initial monitors
+ int num_monitors = monitors->get_n_items();
+ for (int i = 0; i < num_monitors; i++)
+ {
+ auto obj = std::dynamic_pointer_cast(monitors->get_object(i));
+ add_output(obj);
+ }
+
+ gtk_layer_init_for_window(window.gobj());
+ gtk_layer_set_namespace(window.gobj(), "chooser");
+ gtk_layer_set_anchor(window.gobj(), GTK_LAYER_SHELL_EDGE_TOP, true);
+ gtk_layer_set_keyboard_mode(window.gobj(), GTK_LAYER_SHELL_KEYBOARD_MODE_ON_DEMAND);
+ gtk_layer_set_exclusive_zone(window.gobj(), 0);
+ window.present();
+}
+
+void WayfireStreamChooserApp::add_toplevel(ext_foreign_toplevel_handle_v1 *handle)
+{
+ toplevels.emplace(handle, new WayfireChooserTopLevel(handle));
+ window_list.append(*toplevels[handle]);
+ if (window_list.get_selected_children().size() == 0)
+ {
+ auto child = window_list.get_child_at_index(0);
+ window_list.select_child(*child);
+ }
+}
+
+void WayfireStreamChooserApp::remove_toplevel(WayfireChooserTopLevel *toplevel)
+{
+ window_list.remove(*toplevel);
+ toplevels.erase(toplevel->handle);
+}
+
+void WayfireStreamChooserApp::add_output(std::shared_ptr monitor)
+{
+ std::string connector = monitor->get_connector();
+ outputs.emplace(connector, new WayfireChooserOutput(monitor));
+ screen_list.append(*outputs[connector]);
+ if (screen_list.get_selected_children().size() == 0)
+ {
+ auto child = screen_list.get_child_at_index(0);
+ screen_list.select_child(*child);
+ }
+}
+
+void WayfireStreamChooserApp::remove_output(std::string connector)
+{
+ screen_list.remove(*outputs[connector]);
+ outputs.erase(connector);
+}
+
+void WayfireStreamChooserApp::set_toplevel_list(ext_foreign_toplevel_list_v1 *list)
+{
+ this->list = list;
+}
+
+/* Starting point */
+int main(int argc, char **argv)
+{
+ WayfireStreamChooserApp::getInstance().run();
+ exit(0);
+}
diff --git a/src/stream-chooser/stream-chooser.hpp b/src/stream-chooser/stream-chooser.hpp
new file mode 100644
index 00000000..6c400e0c
--- /dev/null
+++ b/src/stream-chooser/stream-chooser.hpp
@@ -0,0 +1,49 @@
+#pragma once
+#include
+#include
+#include
+
+#include "outputwidget.hpp"
+#include "toplevelwidget.hpp"
+
+
+class WayfireStreamChooserApp : public Gtk::Application
+{
+ private:
+ Gtk::Window window;
+ Gtk::Notebook notebook;
+
+ Gtk::Box main, buttons;
+
+ Gtk::Label window_label, screen_label, header;
+
+ Gtk::FlowBox window_list, screen_list;
+ Gtk::Button done, cancel;
+ WayfireStreamChooserApp();
+
+ wl_display *display;
+ wl_registry *registry;
+ ext_foreign_toplevel_list_v1 *list;
+
+ public:
+ bool has_image_copy_capture = false;
+
+ std::map> toplevels;
+ std::map> outputs;
+
+ static WayfireStreamChooserApp& getInstance()
+ {
+ static WayfireStreamChooserApp instance;
+ return instance;
+ }
+
+ void set_toplevel_list(ext_foreign_toplevel_list_v1 *list);
+ void add_toplevel(ext_foreign_toplevel_handle_v1 *handle);
+ void remove_toplevel(WayfireChooserTopLevel *widget);
+
+ void add_output(std::shared_ptr monitor);
+ void remove_output(std::string connector);
+ void activate();
+ WayfireStreamChooserApp(WayfireStreamChooserApp const&) = delete;
+ void operator =(WayfireStreamChooserApp const&) = delete;
+};
diff --git a/src/stream-chooser/toplevelwidget.cpp b/src/stream-chooser/toplevelwidget.cpp
new file mode 100644
index 00000000..26dc182d
--- /dev/null
+++ b/src/stream-chooser/toplevelwidget.cpp
@@ -0,0 +1,115 @@
+#include
+
+#include "stream-chooser.hpp"
+#include "toplevelwidget.hpp"
+
+static void handle_closed(void *data,
+ struct ext_foreign_toplevel_handle_v1 *handle)
+{
+ WayfireChooserTopLevel *toplevel = (WayfireChooserTopLevel*)data;
+ WayfireStreamChooserApp::getInstance().remove_toplevel(toplevel);
+
+ /* TODO Clean up */
+}
+
+static void handle_done(void *data,
+ struct ext_foreign_toplevel_handle_v1 *handle)
+{
+ WayfireChooserTopLevel *toplevel = (WayfireChooserTopLevel*)data;
+ toplevel->commit();
+}
+
+static void handle_title(void *data,
+ struct ext_foreign_toplevel_handle_v1 *ext_foreign_toplevel_handle_v1,
+ const char *title)
+{
+ WayfireChooserTopLevel *toplevel = (WayfireChooserTopLevel*)data;
+ toplevel->set_title(title);
+}
+
+static void handle_app_id(void *data,
+ struct ext_foreign_toplevel_handle_v1 *handle1,
+ const char *app_id)
+{
+ WayfireChooserTopLevel *toplevel = (WayfireChooserTopLevel*)data;
+ toplevel->set_app_id(app_id);
+}
+
+static void handle_identifier(void *data,
+ struct ext_foreign_toplevel_handle_v1 *ext_foreign_toplevel_handle_v1,
+ const char *identifier)
+{
+ WayfireChooserTopLevel *toplevel = (WayfireChooserTopLevel*)data;
+ toplevel->set_identifier(identifier);
+}
+
+ext_foreign_toplevel_handle_v1_listener listener =
+{
+ .closed = handle_closed,
+ .done = handle_done,
+ .title = handle_title,
+ .app_id = handle_app_id,
+ .identifier = handle_identifier,
+};
+
+/* Gtk Overlay showing information about a window */
+WayfireChooserTopLevel::WayfireChooserTopLevel(ext_foreign_toplevel_handle_v1 *handle)
+{
+ append(overlay);
+ append(label);
+ overlay.set_child(screenshot);
+ overlay.add_overlay(icon);
+ icon.set_halign(Gtk::Align::START);
+ icon.set_valign(Gtk::Align::END);
+ label.set_ellipsize(Pango::EllipsizeMode::MIDDLE);
+ label.set_max_width_chars(40);
+
+ ext_foreign_toplevel_handle_v1_add_listener(handle, &listener, this);
+}
+
+void WayfireChooserTopLevel::set_title(std::string title)
+{
+ buffered_title = title;
+}
+
+void WayfireChooserTopLevel::set_app_id(std::string app_id)
+{
+ buffered_app_id = app_id;
+}
+
+void WayfireChooserTopLevel::set_identifier(std::string identifier)
+{
+ buffered_identifier = identifier;
+}
+
+void WayfireChooserTopLevel::commit()
+{
+ if (buffered_app_id != "")
+ {
+ app_id = buffered_app_id;
+ icon.set_from_icon_name(app_id);
+ buffered_app_id = "";
+ }
+
+ if (buffered_title != "")
+ {
+ title = buffered_title;
+ label.set_label(title);
+ buffered_title = "";
+ }
+
+ if (buffered_identifier != "")
+ {
+ identifier = buffered_identifier;
+ buffered_identifier = "";
+ }
+}
+
+WayfireChooserTopLevel::~WayfireChooserTopLevel()
+{}
+
+void WayfireChooserTopLevel::print()
+{
+ std::cout << "Window: " << identifier << std::endl;
+ exit(0);
+}
diff --git a/src/stream-chooser/toplevelwidget.hpp b/src/stream-chooser/toplevelwidget.hpp
new file mode 100644
index 00000000..ef0bd1b3
--- /dev/null
+++ b/src/stream-chooser/toplevelwidget.hpp
@@ -0,0 +1,26 @@
+#pragma once
+#include
+#include "ext-foreign-toplevel-list-v1-client-protocol.h"
+
+class WayfireChooserTopLevel : public Gtk::Box
+{
+ private:
+ Gtk::Overlay overlay;
+ Gtk::Image icon;
+ Gtk::Image screenshot;
+ Gtk::Label label;
+
+ std::string buffered_title = "", title = "";
+ std::string buffered_app_id = "", app_id = "";
+ std::string buffered_identifier = "", identifier = "";
+
+ public:
+ ext_foreign_toplevel_handle_v1 *handle;
+ WayfireChooserTopLevel(ext_foreign_toplevel_handle_v1 *handle);
+ ~WayfireChooserTopLevel();
+ void commit();
+ void set_app_id(std::string app_id);
+ void set_title(std::string title);
+ void set_identifier(std::string identifier);
+ void print();
+};