Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions metadata/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ install_data('cube.xml', install_dir: conf_data.get('PLUGIN_XML_DIR'))
install_data('decoration.xml', install_dir: conf_data.get('PLUGIN_XML_DIR'))
install_data('expo.xml', install_dir: conf_data.get('PLUGIN_XML_DIR'))
install_data('extra-gestures.xml', install_dir: conf_data.get('PLUGIN_XML_DIR'))
install_data('ext-toplevel.xml', install_dir: conf_data.get('PLUGIN_XML_DIR'))
install_data('fast-switcher.xml', install_dir: conf_data.get('PLUGIN_XML_DIR'))
install_data('fisheye.xml', install_dir: conf_data.get('PLUGIN_XML_DIR'))
install_data('foreign-toplevel.xml', install_dir: conf_data.get('PLUGIN_XML_DIR'))
Expand Down
243 changes: 228 additions & 15 deletions plugins/protocols/ext-toplevel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@
#include <wayfire/toplevel-view.hpp>
#include <wayfire/window-manager.hpp>
#include "gtk-shell.hpp"
#include "../wm-actions/wm-actions-signals.hpp"
#include "config.h"

#include "toplevel-common.hpp"

class wayfire_foreign_toplevel;
using foreign_toplevel_map_type = std::map<wayfire_toplevel_view, std::unique_ptr<wayfire_foreign_toplevel>>;
class wayfire_ext_foreign_toplevel;
using foreign_toplevel_map_type = std::map<wayfire_toplevel_view, std::unique_ptr<wayfire_ext_foreign_toplevel>>;

void get_state(wayfire_view view, struct wlr_ext_foreign_toplevel_handle_v1_state *state)
{
Expand All @@ -31,11 +32,16 @@ class wayfire_ext_foreign_toplevel
{
wayfire_toplevel_view view;
wlr_ext_foreign_toplevel_handle_v1 *handle;
wlr_ext_foreign_toplevel_state_handle_v1 *state_handle;
foreign_toplevel_map_type *view_to_toplevel;

public:
wayfire_ext_foreign_toplevel(wayfire_toplevel_view view, wlr_ext_foreign_toplevel_handle_v1 *hndl) :
wayfire_ext_foreign_toplevel(wayfire_toplevel_view view, wlr_ext_foreign_toplevel_handle_v1 *hndl,
wlr_ext_foreign_toplevel_state_handle_v1 *st, foreign_toplevel_map_type *view_map) :
view(view),
handle(hndl)
handle(hndl),
state_handle(st),
view_to_toplevel(view_map)
{
/**
* This is future-proofing.
Expand Down Expand Up @@ -65,35 +71,122 @@ class wayfire_ext_foreign_toplevel
protected:
virtual void init_request_handlers()
{
// No request handlers at the present moment.
// We may want to deal with them later on while implementing
// ext-foreign-toplevel-management protocol.
toplevel_handle_v1_maximize_request.set_callback([&] (void *data)
{
auto ev = static_cast<wlr_ext_foreign_toplevel_state_handle_v1_maximized_event*>(data);
wf::get_core().default_wm->tile_request(view, ev->maximized ? wf::TILED_EDGES_ALL : 0);
});

toplevel_handle_v1_minimize_request.set_callback([&] (void *data)
{
auto ev = static_cast<wlr_ext_foreign_toplevel_state_handle_v1_minimized_event*>(data);
wf::get_core().default_wm->minimize_request(view, ev->minimized);
});

toplevel_handle_v1_activate_request.set_callback([&] (auto)
{
wf::get_core().default_wm->focus_request(view);
});

toplevel_handle_v1_close_request.set_callback([&] (auto)
{
view->close();
});

toplevel_handle_v1_set_rectangle_request.set_callback([&] (void *data)
{
auto ev = static_cast<wlr_ext_foreign_toplevel_state_handle_v1_set_rectangle_event*>(data);
auto surface = wf::wl_surface_to_wayfire_view(ev->surface->resource);
if (!surface)
{
LOGE("Setting minimize hint to unknown surface. Wayfire currently"
"supports only setting hints relative to views.");
return;
}

handle_minimize_hint(view.get(), surface.get(), {ev->x, ev->y, ev->width, ev->height});
});

toplevel_handle_v1_fullscreen_request.set_callback([&] (
void *data)
{
auto ev = static_cast<wlr_ext_foreign_toplevel_state_handle_v1_fullscreen_event*>(data);
auto wo = wf::get_core().output_layout->find_output(ev->output);
wf::get_core().default_wm->fullscreen_request(view, wo, ev->fullscreen);
});

toplevel_handle_v1_above_request.set_callback([&] (void *data)
{
auto ev = static_cast<wlr_ext_foreign_toplevel_state_handle_v1_always_on_top_event*>(data);
wf::wm_actions_set_above_state_signal sig;

auto output = view->get_output();
if (!output)
{
return;
}

sig.view = view;
sig.above = ev->always_on_top;
output->emit(&sig);
});

toplevel_handle_v1_sticky_request.set_callback([&] (void *data)
{
auto ev = static_cast<wlr_ext_foreign_toplevel_state_handle_v1_sticky_event*>(data);
view->set_sticky(ev->sticky);
});

toplevel_handle_v1_close_request.connect(&state_handle->events.request_close);
toplevel_handle_v1_maximize_request.connect(&state_handle->events.request_maximize);
toplevel_handle_v1_minimize_request.connect(&state_handle->events.request_minimize);
toplevel_handle_v1_activate_request.connect(&state_handle->events.request_activate);
toplevel_handle_v1_fullscreen_request.connect(&state_handle->events.request_fullscreen);
toplevel_handle_v1_set_rectangle_request.connect(&state_handle->events.set_rectangle);
toplevel_handle_v1_above_request.connect(&state_handle->events.request_always_on_top);
toplevel_handle_v1_sticky_request.connect(&state_handle->events.request_sticky);
}

virtual void send_initial_state()
{
toplevel_send_state();
toplevel_send_title_appid();
toplevel_update_output(view->get_output(), true);
toplevel_send_additional_state();
}

virtual void init_connections()
{
view->connect(&on_title_changed);
view->connect(&on_app_id_changed);
view->connect(&on_set_output);
view->connect(&on_tiled);
view->connect(&on_minimized);
view->connect(&on_fullscreen);
view->connect(&on_activated);
view->connect(&on_parent_changed);
view->connect(&on_above_changed);
view->connect(&on_sticky_changed);
}

virtual void disconnect_request_handlers()
{
// No request handlers at the present moment.
// We may want to deal with them later on while implementing
// ext-foreign-toplevel-management protocol.
toplevel_handle_v1_close_request.disconnect();
toplevel_handle_v1_maximize_request.disconnect();
toplevel_handle_v1_minimize_request.disconnect();
toplevel_handle_v1_activate_request.disconnect();
toplevel_handle_v1_fullscreen_request.disconnect();
toplevel_handle_v1_set_rectangle_request.disconnect();
toplevel_handle_v1_above_request.disconnect();
toplevel_handle_v1_sticky_request.disconnect();
}

virtual void destroy_handle()
{
wlr_ext_foreign_toplevel_state_handle_v1_destroy(state_handle);
wlr_ext_foreign_toplevel_handle_v1_destroy(handle);
}

virtual void toplevel_send_state()
virtual void toplevel_send_title_appid()
{
// Prepare the state
struct wlr_ext_foreign_toplevel_handle_v1_state new_state;
Expand All @@ -103,16 +196,116 @@ class wayfire_ext_foreign_toplevel
wlr_ext_foreign_toplevel_handle_v1_update_state(handle,
&new_state);
}

virtual void toplevel_send_additional_state()
{
/** Additional state (note: these are no-ops if there is not change from the current state */
wlr_ext_foreign_toplevel_state_handle_v1_set_maximized(state_handle, view->pending_tiled_edges() == wf::TILED_EDGES_ALL);
wlr_ext_foreign_toplevel_state_handle_v1_set_activated(state_handle, view->activated);
wlr_ext_foreign_toplevel_state_handle_v1_set_minimized(state_handle, view->minimized);
wlr_ext_foreign_toplevel_state_handle_v1_set_fullscreen(state_handle, view->pending_fullscreen());
bool is_above = view->has_data("wm-actions-above"); //!! TODO: is there a better way to get this?
wlr_ext_foreign_toplevel_state_handle_v1_set_always_on_top(state_handle, is_above);
wlr_ext_foreign_toplevel_state_handle_v1_set_sticky(state_handle, view->sticky);

/* update parent as well */
auto it = view_to_toplevel->find(view->parent);
if (it == view_to_toplevel->end())
{
wlr_ext_foreign_toplevel_state_handle_v1_set_parent(state_handle, nullptr);
} else
{
wlr_ext_foreign_toplevel_state_handle_v1_set_parent(state_handle, it->second->state_handle);
}
}

void toplevel_update_output(wf::output_t *output, bool enter)
{
if (output && enter)
{
wlr_ext_foreign_toplevel_state_handle_v1_output_enter(state_handle, output->handle);
}

if (output && !enter)
{
wlr_ext_foreign_toplevel_state_handle_v1_output_leave(state_handle, output->handle);
}
}

wf::signal::connection_t<wf::view_title_changed_signal> on_title_changed = [=] (auto)
{
toplevel_send_state();
toplevel_send_title_appid();
};

wf::signal::connection_t<wf::view_app_id_changed_signal> on_app_id_changed = [=] (auto)
{
toplevel_send_state();
toplevel_send_title_appid();
};

wf::signal::connection_t<wf::view_set_output_signal> on_set_output = [=] (wf::view_set_output_signal *ev)
{
toplevel_update_output(ev->output, false);
toplevel_update_output(view->get_output(), true);
};

wf::signal::connection_t<wf::view_minimized_signal> on_minimized = [=] (auto)
{
toplevel_send_additional_state();
};

wf::signal::connection_t<wf::view_fullscreen_signal> on_fullscreen = [=] (auto)
{
toplevel_send_additional_state();
};

wf::signal::connection_t<wf::view_tiled_signal> on_tiled = [=] (auto)
{
toplevel_send_additional_state();
};

wf::signal::connection_t<wf::view_activated_state_signal> on_activated = [=] (auto)
{
toplevel_send_additional_state();
};

wf::signal::connection_t<wf::view_parent_changed_signal> on_parent_changed = [=] (auto)
{
toplevel_send_additional_state();
};

wf::signal::connection_t<wf::wm_actions_above_changed_signal> on_above_changed = [=] (auto)
{
toplevel_send_additional_state();
};

wf::signal::connection_t<wf::view_set_sticky_signal> on_sticky_changed = [=] (auto)
{
toplevel_send_additional_state();
};

wf::wl_listener_wrapper toplevel_handle_v1_maximize_request;
wf::wl_listener_wrapper toplevel_handle_v1_activate_request;
wf::wl_listener_wrapper toplevel_handle_v1_minimize_request;
wf::wl_listener_wrapper toplevel_handle_v1_set_rectangle_request;
wf::wl_listener_wrapper toplevel_handle_v1_fullscreen_request;
wf::wl_listener_wrapper toplevel_handle_v1_close_request;
wf::wl_listener_wrapper toplevel_handle_v1_above_request;
wf::wl_listener_wrapper toplevel_handle_v1_sticky_request;

void handle_minimize_hint(wf::toplevel_view_interface_t *view, wf::view_interface_t *relative_to,
wlr_box hint)
{
if (relative_to->get_output() != view->get_output())
{
LOGE("Minimize hint set to surface on a different output, " "problems might arise");
/* TODO: translate coordinates in case minimize hint is on another output */
}

wf::pointf_t relative = relative_to->get_surface_root_node()->to_global({0, 0});
hint.x += relative.x;
hint.y += relative.y;
view->set_minimize_hint(hint);
}
};

class wayfire_ext_foreign_toplevel_protocol_impl : public wf::plugin_interface_t
Expand All @@ -127,6 +320,17 @@ class wayfire_ext_foreign_toplevel_protocol_impl : public wf::plugin_interface_t
return;
}

state_manager = wlr_ext_foreign_toplevel_state_manager_v1_create(wf::get_core().display,
63, // possible notifications: maximize, minimize, activated, fullscreen, always_on_top, sticky -- TODO: use #defines from protocol header !!
127 // possible actions: close, maximize, minimize, activate, fullscreen, always_on_top, sticky
);
if (!state_manager)
{
LOGE("Failed to create foreign toplevel state manager");
//!! TODO: is there no way to destroy toplevel_manager ??
return;
}

wf::get_core().connect(&on_view_mapped);
wf::get_core().connect(&on_view_unmapped);
}
Expand Down Expand Up @@ -159,7 +363,15 @@ class wayfire_ext_foreign_toplevel_protocol_impl : public wf::plugin_interface_t
return;
}

handle_for_view[toplevel] = std::make_unique<wayfire_ext_foreign_toplevel>(toplevel, handle);
auto state = wlr_ext_foreign_toplevel_state_handle_v1_create (state_manager, handle);
if (!state)
{
LOGE("Failed to create foreign toplevel state handle for view");
wlr_ext_foreign_toplevel_handle_v1_destroy (handle);
return;
}

handle_for_view[toplevel] = std::make_unique<wayfire_ext_foreign_toplevel>(toplevel, handle, state, &handle_for_view);
}
};

Expand All @@ -169,6 +381,7 @@ class wayfire_ext_foreign_toplevel_protocol_impl : public wf::plugin_interface_t
};

wlr_ext_foreign_toplevel_list_v1 *toplevel_manager;
wlr_ext_foreign_toplevel_state_manager_v1 *state_manager;
std::map<wayfire_toplevel_view, std::unique_ptr<wayfire_ext_foreign_toplevel>> handle_for_view;
};

Expand Down
1 change: 1 addition & 0 deletions src/api/wayfire/nonstd/wlroots-full.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ extern "C"
#include <wlr/types/wlr_screencopy_v1.h>
#include <wlr/types/wlr_export_dmabuf_v1.h>
#include <wlr/types/wlr_ext_foreign_toplevel_list_v1.h>
#include <wlr/types/wlr_ext_foreign_toplevel_state_v1.h>
#if __has_include(<ext-image-copy-capture-v1-protocol.h>)
#include <wlr/types/wlr_ext_image_copy_capture_v1.h>
#include <wlr/types/wlr_ext_image_capture_source_v1.h>
Expand Down
Loading