Skip to content

Commit

Permalink
Add pin-view plugin (#245)
Browse files Browse the repository at this point in the history
* Add pin-view plugin

* Add ipc scripts for pin-view plugin

* Lots of fixes make it work better

* Add boolean to control whether or not the view is resized to the output geometry
  • Loading branch information
soreau authored Aug 14, 2024
1 parent f60d50b commit 035fd0e
Show file tree
Hide file tree
Showing 7 changed files with 306 additions and 0 deletions.
24 changes: 24 additions & 0 deletions ipc-scripts/pin-view.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/usr/bin/python3

import os
import sys
from wayfire import WayfireSocket
from wayfire.extra.wpe import WPE

if len(sys.argv) < 4:
print(f"Usage: {sys.argv[0]} <view_id: int> <layer: str> <resize: bool> [workspace_x: int], [workspace_y: int]")
exit(1)

addr = os.getenv("WAYFIRE_SOCKET")

sock = WayfireSocket(addr)
wpe = WPE(sock)

for view in sock.list_views():
if view["id"] == int(sys.argv[1]):
if len(sys.argv) == 4:
wpe.pin_view(int(sys.argv[1]), sys.argv[2], sys.argv[3].lower() == "true", None, None)
if len(sys.argv) == 5:
wpe.pin_view(int(sys.argv[1]), sys.argv[2], sys.argv[3].lower() == "true", int(sys.argv[4]), None)
elif len(sys.argv) == 6:
wpe.pin_view(int(sys.argv[1]), sys.argv[2], sys.argv[3].lower() == "true", int(sys.argv[4]), int(sys.argv[5]))
17 changes: 17 additions & 0 deletions ipc-scripts/unpin-view.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/usr/bin/python3

import os
import sys
from wayfire_socket import *

if len(sys.argv) < 2:
print(f"Usage: {sys.argv[0]} <view_id>")
exit(1)

addr = os.getenv('WAYFIRE_SOCKET')

commands_sock = WayfireSocket(addr)

for view in commands_sock.list_views():
if view["id"] == int(sys.argv[1]):
commands_sock.unpin_view(int(sys.argv[1]))
16 changes: 16 additions & 0 deletions ipc-scripts/wayfire_socket.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,19 @@ def ghost_view_toggle(self, view_id: int):
message = get_msg_template("ghost/ghost_toggle")
message["data"]["view-id"] = view_id
return self.send_json(message)

def pin_view(self, view_id: int, layer: str, ws_x: int, ws_y: int):
message = get_msg_template("pin-view/pin")
message["data"]["view-id"] = view_id
message["data"]["layer"] = layer
if ws_x != None:
message["data"]["x"] = ws_x
message["data"]["y"] = 0
if ws_y != None:
message["data"]["y"] = ws_y
return self.send_json(message)

def unpin_view(self, view_id: int):
message = get_msg_template("pin-view/unpin")
message["data"]["view-id"] = view_id
return self.send_json(message)
1 change: 1 addition & 0 deletions metadata/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ install_data('join-views.xml', install_dir: wayfire.get_variable(pkgconfig: 'met
install_data('keycolor.xml', install_dir: wayfire.get_variable(pkgconfig: 'metadatadir'))
install_data('mag.xml', install_dir: wayfire.get_variable(pkgconfig: 'metadatadir'))
install_data('obs.xml', install_dir: wayfire.get_variable(pkgconfig: 'metadatadir'))
install_data('pin-view.xml', install_dir: wayfire.get_variable(pkgconfig: 'metadatadir'))
install_data('showrepaint.xml', install_dir: wayfire.get_variable(pkgconfig: 'metadatadir'))
install_data('view-shot.xml', install_dir: wayfire.get_variable(pkgconfig: 'metadatadir'))
install_data('water.xml', install_dir: wayfire.get_variable(pkgconfig: 'metadatadir'))
Expand Down
8 changes: 8 additions & 0 deletions metadata/pin-view.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0"?>
<wayfire>
<plugin name="pin-view">
<_short>Pin View</_short>
<_long>Set a view layer, role and workspace</_long>
<category>Window Management</category>
</plugin>
</wayfire>
3 changes: 3 additions & 0 deletions src/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ if json.found()
obs = shared_module('obs', 'obs.cpp',
dependencies: [wayfire, json],
install: true, install_dir: join_paths(get_option('libdir'), 'wayfire'))
pin_view = shared_module('pin-view', 'pin-view.cpp',
dependencies: [wayfire, json],
install: true, install_dir: join_paths(get_option('libdir'), 'wayfire'))
endif

showrepaint = shared_module('showrepaint', 'showrepaint.cpp',
Expand Down
237 changes: 237 additions & 0 deletions src/pin-view.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2024 Scott Moreau <oreaus@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

#include <wayfire/core.hpp>
#include <wayfire/view.hpp>
#include <wayfire/plugin.hpp>
#include <wayfire/output.hpp>
#include <wayfire/view-helpers.hpp>
#include <wayfire/util/duration.hpp>
#include <wayfire/render-manager.hpp>
#include <wayfire/view-transform.hpp>
#include <wayfire/scene-operations.hpp>
#include <wayfire/per-output-plugin.hpp>
#include <wayfire/signal-definitions.hpp>
#include <wayfire/plugins/ipc/ipc-helpers.hpp>
#include <wayfire/plugins/common/shared-core-data.hpp>
#include <wayfire/plugins/ipc/ipc-method-repository.hpp>

namespace wf
{
namespace pin_view
{
class pin_view_data : public wf::custom_data_t
{
public:
wf::geometry_t geometry;
wf::point_t workspace;
wf::view_role_t role;
};
class wayfire_pin_view : public wf::plugin_interface_t
{
wf::shared_data::ref_ptr_t<wf::ipc::method_repository_t> ipc_repo;

public:
void init() override
{
ipc_repo->register_method("pin-view/pin", ipc_pin_view);
ipc_repo->register_method("pin-view/unpin", ipc_unpin_view);
}

wf::ipc::method_callback ipc_pin_view = [=] (nlohmann::json data) -> nlohmann::json
{
WFJSON_EXPECT_FIELD(data, "view-id", number_unsigned);
WFJSON_EXPECT_FIELD(data, "layer", string);
WFJSON_EXPECT_FIELD(data, "resize", boolean);
/* workspace x,y */
WFJSON_OPTIONAL_FIELD(data, "x", number_unsigned);
WFJSON_OPTIONAL_FIELD(data, "y", number_unsigned);

auto view = wf::ipc::find_view_by_id(data["view-id"]);
if (view)
{
auto output = view->get_output();
output->connect(&on_workspace_changed);
if (!view->get_data<pin_view_data>())
{
pin_view_data pv_data;
pv_data.role = view->role;

if (auto toplevel = toplevel_cast(view))
{
pv_data.workspace = output->wset()->get_view_main_workspace(toplevel);
pv_data.geometry = toplevel->get_geometry();
}

view->store_data(std::make_unique<pin_view_data>(pv_data));
}

wf::scene::layer layer;
if (data["layer"] == "background")
{
layer = wf::scene::layer::BACKGROUND;
} else if (data["layer"] == "bottom")
{
layer = wf::scene::layer::BOTTOM;
} else if (data["layer"] == "workspace")
{
layer = wf::scene::layer::WORKSPACE;
} else if (data["layer"] == "top")
{
layer = wf::scene::layer::TOP;
} else if (data["layer"] == "unmanaged")
{
layer = wf::scene::layer::UNMANAGED;
} else if (data["layer"] == "overlay")
{
layer = wf::scene::layer::OVERLAY;
} else if (data["layer"] == "lock")
{
layer = wf::scene::layer::LOCK;
} else
{
layer = wf::scene::layer::TOP;
}

bool resize = data["resize"];
auto og = output->get_relative_geometry();
int x = 0, y = 0;
if (data.contains("x"))
{
x = data["x"].get<int>();
if (data.contains("y"))
{
y = data["y"].get<int>();
}

wf::point_t nws{x, y};
if (auto toplevel = toplevel_cast(view))
{
auto vg = toplevel->get_geometry();
auto cws = output->wset()->get_view_main_workspace(toplevel);
toplevel->set_geometry(wf::geometry_t{(nws.x - cws.x) * og.width,
(nws.y - cws.y) * og.height, resize ? og.width : vg.width,
resize ? og.height : vg.height});
}
} else
{
if (auto toplevel = toplevel_cast(view))
{
auto vg = toplevel->get_geometry();
wf::point_t nws = output->wset()->get_current_workspace();
auto cws = output->wset()->get_view_main_workspace(toplevel);
toplevel->move(vg.x + (nws.x - cws.x) * og.width, vg.y + (nws.y - cws.y) * og.height);
if (resize)
{
toplevel->set_geometry(og);
}
}

view->role = wf::VIEW_ROLE_DESKTOP_ENVIRONMENT;
wf::scene::readd_front(view->get_output()->node_for_layer(layer), view->get_root_node());
return wf::ipc::json_ok();
}

wf::scene::readd_front(output->node_for_layer(layer), view->get_root_node());
} else
{
return wf::ipc::json_error("Failed to find view with given id.");
}

return wf::ipc::json_ok();
};

wf::ipc::method_callback ipc_unpin_view = [=] (nlohmann::json data) -> nlohmann::json
{
WFJSON_EXPECT_FIELD(data, "view-id", number_unsigned);

auto view = wf::ipc::find_view_by_id(data["view-id"]);
if (view && view->get_data<pin_view_data>())
{
auto pvd = view->get_data<pin_view_data>();
view->role = pvd->role;
wf::scene::readd_front(view->get_output()->wset()->get_node(), view->get_root_node());
if (auto toplevel = toplevel_cast(view))
{
auto output = view->get_output();
auto og = output->get_relative_geometry();
auto cws = output->wset()->get_view_main_workspace(toplevel);
auto vg = toplevel->get_geometry();
toplevel->move(vg.x + (pvd->workspace.x - cws.x) * og.width,
vg.y + (pvd->workspace.y - cws.y) * og.height);
toplevel->set_geometry(pvd->geometry);
}

view->release_data<pin_view_data>();
} else
{
LOGE("Failed to find view with given id. Perhaps it is not pinned.");
return wf::ipc::json_error("Failed to find view with given id. Perhaps it is not pinned.");
}

return wf::ipc::json_ok();
};

wf::signal::connection_t<wf::workspace_changed_signal> on_workspace_changed =
[=] (wf::workspace_changed_signal *ev)
{
auto nws = ev->new_viewport;
auto output = ev->output;
auto og = output->get_relative_geometry();
for (auto & view : wf::get_core().get_all_views())
{
auto pvd = view->get_data<pin_view_data>();
if (!pvd || (view->role != wf::VIEW_ROLE_DESKTOP_ENVIRONMENT))
{
continue;
}

if (auto toplevel = toplevel_cast(view))
{
auto cws = output->wset()->get_view_main_workspace(toplevel);
auto vg = toplevel->get_geometry();
toplevel->move(vg.x + (nws.x - cws.x) * og.width, vg.y + (nws.y - cws.y) * og.height);
}
}
};

void fini() override
{
for (auto & view : wf::get_core().get_all_views())
{
if (view->get_data<pin_view_data>())
{
view->release_data<pin_view_data>();
}
}

ipc_repo->unregister_method("pin-view/pin");
ipc_repo->unregister_method("pin-view/unpin");
on_workspace_changed.disconnect();
}
};
}
}

DECLARE_WAYFIRE_PLUGIN(wf::pin_view::wayfire_pin_view);

0 comments on commit 035fd0e

Please sign in to comment.