mirror of
https://github.com/miracle-wm-org/miracle-wm.git
synced 2024-11-23 12:15:59 +03:00
I have something functionng, time to dissect!
This commit is contained in:
parent
3043463965
commit
cb50196482
@ -16,7 +16,7 @@ find_package(PkgConfig)
|
||||
pkg_check_modules(MIRAL miral REQUIRED)
|
||||
find_package(OpenGL REQUIRED)
|
||||
|
||||
add_executable(compositor src/main.cpp)
|
||||
add_executable(compositor src/main.cpp src/FloatingWindowManager.cpp)
|
||||
|
||||
target_include_directories(compositor PUBLIC SYSTEM ${MIRAL_INCLUDE_DIRS})
|
||||
target_link_libraries( compositor ${MIRAL_LDFLAGS})
|
16
NOTES.md
16
NOTES.md
@ -2,22 +2,18 @@
|
||||
The following file will serve the purpose of documenting my thought processes and progress as I complete the assignment. I am starting this project on February 18, 2023.
|
||||
|
||||
## Step 1: Setting up the miral app
|
||||
At first, I went the virtual box route, but then I saw how easy it would be to do this locally, so I opted for that instead. On Arch, I did:
|
||||
```sh
|
||||
git clone https://aur.archlinux.org/mir.git
|
||||
cd mir
|
||||
makepkg -si
|
||||
```
|
||||
|
||||
and voila. It lives!
|
||||
.. TODO
|
||||
|
||||
## Step 2: Hacking
|
||||
|
||||
### Research
|
||||
I will begin my searching for documentation on how I made write a Mir-based compositor:
|
||||
1. https://mir-server.io/docs/developing-a-wayland-compositor-using-mir This seems like a good match!
|
||||
1. https://mir-server.io/docs/developing-a-wayland-compositor-using-mir This seems like a good match.
|
||||
2. I will also look extensively at the `miral-app` demo to see how it is starting the miral server. The `miral-shell` looks like the easiest barebone candidate for me to follow.
|
||||
|
||||
###
|
||||
### Programming
|
||||
1. I started off by implementing the simple miral-terminal example, however I am runnning into an issue where the `ExternalClientLauncher` is claiming that the server has not yet been set.
|
||||
2.
|
||||
My task is to build a compositor, so I will first define what is that I need in my compositor. As an MVP, I would like to support:
|
||||
- Window transparency
|
||||
- Drop shadows
|
||||
|
3
run.sh
3
run.sh
@ -8,5 +8,6 @@ if [ -O "${XDG_RUNTIME_DIR}/${WAYLAND_DISPLAY}" ]
|
||||
then
|
||||
unset WAYLAND_DISPLAY
|
||||
fi
|
||||
# miral-shell can launch it's own terminal with Ctrl-Alt-T
|
||||
|
||||
echo $bindir
|
||||
MIR_SERVER_ENABLE_X11=1 MIR_SERVER_SHELL_TERMINAL_EMULATOR=${terminal} exec ${bindir}${miral_server} $*
|
@ -1,29 +1,601 @@
|
||||
/*
|
||||
* Copyright © Canonical Ltd.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 or 3 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "FloatingWindowManager.hpp"
|
||||
#include <miral/minimal_window_manager.h>
|
||||
|
||||
const int title_bar_height = 16;
|
||||
#include <miral/application_info.h>
|
||||
#include <miral/internal_client.h>
|
||||
#include <miral/toolkit_event.h>
|
||||
#include <miral/window_info.h>
|
||||
#include <miral/window_manager_tools.h>
|
||||
#include <miral/zone.h>
|
||||
|
||||
FloatingWindowManager::FloatingWindowManager(const miral::WindowManagerTools& tools) {
|
||||
#include <linux/input.h>
|
||||
#include <csignal>
|
||||
|
||||
using namespace miral;
|
||||
using namespace miral::toolkit;
|
||||
|
||||
namespace
|
||||
{
|
||||
DeltaY const title_bar_height{12};
|
||||
|
||||
struct PolicyData
|
||||
{
|
||||
bool in_hidden_workspace{false};
|
||||
|
||||
MirWindowState old_state;
|
||||
};
|
||||
|
||||
inline PolicyData& policy_data_for(WindowInfo const& info)
|
||||
{
|
||||
return *std::static_pointer_cast<PolicyData>(info.userdata());
|
||||
}
|
||||
}
|
||||
|
||||
miral::WindowSpecification FloatingWindowManager::place_new_window(
|
||||
miral::ApplicationInfo const& app_info,
|
||||
miral::WindowSpecification const& requested_specification)
|
||||
FloatingWindowManagerPolicy::FloatingWindowManagerPolicy(
|
||||
WindowManagerTools const& tools,
|
||||
miral::InternalClientLauncher const& launcher,
|
||||
std::function<void()>& shutdown_hook) :
|
||||
MinimalWindowManager(tools)
|
||||
{
|
||||
auto parameters = miral::MinimalWindowManager::place_new_window(app_info, requested_specification);
|
||||
shutdown_hook = [this] { };
|
||||
|
||||
// if (app_info.application() == miral::decoration_provider->session())
|
||||
// {
|
||||
// parameters.type() = mir_window_type_decoration;
|
||||
// parameters.depth_layer() = mir_depth_layer_background;
|
||||
// }
|
||||
for (auto key : {KEY_F1, KEY_F2, KEY_F3, KEY_F4})
|
||||
key_to_workspace[key] = this->tools.create_workspace();
|
||||
|
||||
bool const needs_titlebar = miral::WindowInfo::needs_titlebar(parameters.type().value());
|
||||
active_workspace = key_to_workspace[KEY_F1];
|
||||
}
|
||||
|
||||
FloatingWindowManagerPolicy::~FloatingWindowManagerPolicy() = default;
|
||||
|
||||
bool FloatingWindowManagerPolicy::handle_pointer_event(MirPointerEvent const* event)
|
||||
{
|
||||
if (MinimalWindowManager::handle_pointer_event(event))
|
||||
return true;
|
||||
|
||||
auto const action = mir_pointer_event_action(event);
|
||||
auto const modifiers = mir_pointer_event_modifiers(event) & modifier_mask;
|
||||
Point const cursor{
|
||||
mir_pointer_event_axis_value(event, mir_pointer_axis_x),
|
||||
mir_pointer_event_axis_value(event, mir_pointer_axis_y)};
|
||||
|
||||
bool consumes_event = false;
|
||||
|
||||
if (action == mir_pointer_action_button_down)
|
||||
{
|
||||
if (auto const window = tools.window_at(cursor))
|
||||
tools.select_active_window(window);
|
||||
|
||||
if (auto const window = tools.active_window())
|
||||
{
|
||||
if (mir_pointer_event_button_state(event, mir_pointer_button_tertiary))
|
||||
{
|
||||
if (modifiers == mir_input_event_modifier_alt)
|
||||
{
|
||||
Rectangle const old_pos{window.top_left(), window.size()};
|
||||
|
||||
auto anchor = old_pos.bottom_right();
|
||||
auto edge = mir_resize_edge_northwest;
|
||||
|
||||
struct Corner { Point point; MirResizeEdge edge; };
|
||||
|
||||
for (auto const& corner : {
|
||||
Corner{old_pos.top_right(), mir_resize_edge_southwest},
|
||||
Corner{old_pos.bottom_left(), mir_resize_edge_northeast},
|
||||
Corner{old_pos.top_left, mir_resize_edge_southeast}})
|
||||
{
|
||||
if ((cursor - anchor).length_squared() <
|
||||
(cursor - corner.point).length_squared())
|
||||
{
|
||||
anchor = corner.point;
|
||||
edge = corner.edge;
|
||||
}
|
||||
}
|
||||
|
||||
begin_pointer_resize(tools.info_for(window), mir_pointer_event_input_event(event), edge);
|
||||
consumes_event = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return consumes_event;
|
||||
}
|
||||
|
||||
bool FloatingWindowManagerPolicy::handle_touch_event(MirTouchEvent const* event)
|
||||
{
|
||||
auto const count = mir_touch_event_point_count(event);
|
||||
|
||||
if (MinimalWindowManager::handle_touch_event(event) || count != 3)
|
||||
{
|
||||
pinching = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto i = 0U; i != count; ++i)
|
||||
{
|
||||
switch (mir_touch_event_action(event, i))
|
||||
{
|
||||
case mir_touch_action_up:
|
||||
case mir_touch_action_down:
|
||||
pinching = false;
|
||||
return false;
|
||||
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
int touch_pinch_top = std::numeric_limits<int>::max();
|
||||
int touch_pinch_left = std::numeric_limits<int>::max();
|
||||
int touch_pinch_width = 0;
|
||||
int touch_pinch_height = 0;
|
||||
|
||||
for (auto i = 0U; i != count; ++i)
|
||||
{
|
||||
for (auto j = 0U; j != i; ++j)
|
||||
{
|
||||
int dx = mir_touch_event_axis_value(event, i, mir_touch_axis_x) -
|
||||
mir_touch_event_axis_value(event, j, mir_touch_axis_x);
|
||||
|
||||
int dy = mir_touch_event_axis_value(event, i, mir_touch_axis_y) -
|
||||
mir_touch_event_axis_value(event, j, mir_touch_axis_y);
|
||||
|
||||
if (touch_pinch_width < dx)
|
||||
touch_pinch_width = dx;
|
||||
|
||||
if (touch_pinch_height < dy)
|
||||
touch_pinch_height = dy;
|
||||
}
|
||||
|
||||
int const x = mir_touch_event_axis_value(event, i, mir_touch_axis_x);
|
||||
|
||||
int const y = mir_touch_event_axis_value(event, i, mir_touch_axis_y);
|
||||
|
||||
if (touch_pinch_top > y)
|
||||
touch_pinch_top = y;
|
||||
|
||||
if (touch_pinch_left > x)
|
||||
touch_pinch_left = x;
|
||||
}
|
||||
|
||||
if (auto window = tools.active_window())
|
||||
{
|
||||
auto const old_size = window.size();
|
||||
auto const delta_width = DeltaX{touch_pinch_width - old_touch_pinch_width};
|
||||
auto const delta_height = DeltaY{touch_pinch_height - old_touch_pinch_height};
|
||||
|
||||
auto new_width = std::max(old_size.width + delta_width, Width{5});
|
||||
auto new_height = std::max(old_size.height + delta_height, Height{5});
|
||||
Displacement movement{
|
||||
DeltaX{touch_pinch_left - old_touch_pinch_left},
|
||||
DeltaY{touch_pinch_top - old_touch_pinch_top}};
|
||||
|
||||
auto& window_info = tools.info_for(window);
|
||||
keep_window_within_constraints(window_info, movement, new_width, new_height);
|
||||
|
||||
auto new_pos = window.top_left() + movement;
|
||||
Size new_size{new_width, new_height};
|
||||
|
||||
{ // Workaround for lp:1627697
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
if (pinching && now < last_resize+std::chrono::milliseconds(20))
|
||||
return true;
|
||||
|
||||
last_resize = now;
|
||||
}
|
||||
|
||||
if (pinching)
|
||||
{
|
||||
WindowSpecification modifications;
|
||||
modifications.top_left() = new_pos;
|
||||
modifications.size() = new_size;
|
||||
tools.modify_window(window_info, modifications);
|
||||
}
|
||||
else
|
||||
{
|
||||
pinching = true;
|
||||
}
|
||||
|
||||
old_touch_pinch_top = touch_pinch_top;
|
||||
old_touch_pinch_left = touch_pinch_left;
|
||||
old_touch_pinch_width = touch_pinch_width;
|
||||
old_touch_pinch_height = touch_pinch_height;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FloatingWindowManagerPolicy::advise_new_window(WindowInfo const& window_info)
|
||||
{
|
||||
MinimalWindowManager::advise_new_window(window_info);
|
||||
|
||||
auto const parent = window_info.parent();
|
||||
|
||||
if (!parent)
|
||||
tools.add_tree_to_workspace(window_info.window(), active_workspace);
|
||||
else
|
||||
{
|
||||
if (policy_data_for(tools.info_for(parent)).in_hidden_workspace)
|
||||
apply_workspace_hidden_to(window_info.window());
|
||||
}
|
||||
}
|
||||
|
||||
void FloatingWindowManagerPolicy::handle_window_ready(WindowInfo& window_info)
|
||||
{
|
||||
MinimalWindowManager::handle_window_ready(window_info);
|
||||
}
|
||||
|
||||
void FloatingWindowManagerPolicy::advise_focus_gained(WindowInfo const& info)
|
||||
{
|
||||
MinimalWindowManager::advise_focus_gained(info);
|
||||
}
|
||||
|
||||
bool FloatingWindowManagerPolicy::handle_keyboard_event(MirKeyboardEvent const* event)
|
||||
{
|
||||
if (MinimalWindowManager::handle_keyboard_event(event))
|
||||
return true;
|
||||
|
||||
auto const action = mir_keyboard_event_action(event);
|
||||
auto const scan_code = mir_keyboard_event_scan_code(event);
|
||||
auto const modifiers = mir_keyboard_event_modifiers(event) & modifier_mask;
|
||||
|
||||
// Switch workspaces
|
||||
if (action == mir_keyboard_action_down &&
|
||||
modifiers == (mir_input_event_modifier_alt | mir_input_event_modifier_meta))
|
||||
{
|
||||
auto const found = key_to_workspace.find(scan_code);
|
||||
|
||||
if (found != key_to_workspace.end())
|
||||
{
|
||||
switch_workspace_to(found->second);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Switch workspace taking the active window
|
||||
if (action == mir_keyboard_action_down &&
|
||||
modifiers == (mir_input_event_modifier_ctrl | mir_input_event_modifier_meta))
|
||||
{
|
||||
auto const found = key_to_workspace.find(scan_code);
|
||||
|
||||
if (found != key_to_workspace.end())
|
||||
{
|
||||
switch_workspace_to(found->second, tools.active_window());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (action == mir_keyboard_action_down && scan_code == KEY_F11)
|
||||
{
|
||||
switch (modifiers)
|
||||
{
|
||||
case mir_input_event_modifier_alt:
|
||||
toggle(mir_window_state_maximized);
|
||||
return true;
|
||||
|
||||
case mir_input_event_modifier_shift:
|
||||
toggle(mir_window_state_vertmaximized);
|
||||
return true;
|
||||
|
||||
case mir_input_event_modifier_ctrl:
|
||||
toggle(mir_window_state_horizmaximized);
|
||||
return true;
|
||||
|
||||
case mir_input_event_modifier_meta:
|
||||
toggle(mir_window_state_fullscreen);
|
||||
return true;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (action == mir_keyboard_action_down && scan_code == KEY_F4 &&
|
||||
modifiers == (mir_input_event_modifier_alt|mir_input_event_modifier_shift))
|
||||
{
|
||||
if (auto const& window = tools.active_window())
|
||||
kill(window.application(), SIGTERM);
|
||||
return true;
|
||||
}
|
||||
else if (action == mir_keyboard_action_down &&
|
||||
modifiers == (mir_input_event_modifier_ctrl|mir_input_event_modifier_meta))
|
||||
{
|
||||
if (auto active_window = tools.active_window())
|
||||
{
|
||||
auto active_zone = tools.active_application_zone().extents();
|
||||
auto& window_info = tools.info_for(active_window);
|
||||
WindowSpecification modifications;
|
||||
|
||||
switch (scan_code)
|
||||
{
|
||||
case KEY_LEFT:
|
||||
modifications.state() = mir_window_state_vertmaximized;
|
||||
tools.place_and_size_for_state(modifications, window_info);
|
||||
modifications.top_left() = window_info.needs_titlebar(window_info.type()) ?
|
||||
active_zone.top_left + title_bar_height :
|
||||
active_zone.top_left;
|
||||
break;
|
||||
|
||||
case KEY_RIGHT:
|
||||
{
|
||||
modifications.state() = mir_window_state_vertmaximized;
|
||||
tools.place_and_size_for_state(modifications, window_info);
|
||||
|
||||
auto const new_width =
|
||||
(modifications.size().is_set() ? modifications.size().value() : active_window.size()).width;
|
||||
|
||||
modifications.top_left() = window_info.needs_titlebar(window_info.type()) ?
|
||||
active_zone.top_right() - Displacement{as_delta(new_width), 0} + title_bar_height :
|
||||
active_zone.top_right() - Displacement{as_delta(new_width), 0};
|
||||
break;
|
||||
}
|
||||
|
||||
case KEY_UP:
|
||||
modifications.state() = mir_window_state_horizmaximized;
|
||||
tools.place_and_size_for_state(modifications, window_info);
|
||||
modifications.top_left() = window_info.needs_titlebar(window_info.type()) ?
|
||||
active_zone.top_left + title_bar_height :
|
||||
active_zone.top_left;
|
||||
break;
|
||||
|
||||
case KEY_DOWN:
|
||||
modifications.state() = mir_window_state_horizmaximized;
|
||||
tools.place_and_size_for_state(modifications, window_info);
|
||||
modifications.top_left() =
|
||||
active_zone.bottom_right() - as_displacement(
|
||||
modifications.size().is_set() ? modifications.size().value() : active_window.size());
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
tools.modify_window(window_info, modifications);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void FloatingWindowManagerPolicy::toggle(MirWindowState state)
|
||||
{
|
||||
if (auto const window = tools.active_window())
|
||||
{
|
||||
auto& info = tools.info_for(window);
|
||||
|
||||
WindowSpecification modifications;
|
||||
|
||||
modifications.state() = (info.state() == state) ? mir_window_state_restored : state;
|
||||
tools.place_and_size_for_state(modifications, info);
|
||||
tools.modify_window(info, modifications);
|
||||
}
|
||||
}
|
||||
|
||||
void FloatingWindowManagerPolicy::keep_window_within_constraints(
|
||||
WindowInfo const& window_info, Displacement& movement, Width& new_width, Height& new_height) const
|
||||
{
|
||||
switch (window_info.state())
|
||||
{
|
||||
case mir_window_state_maximized:
|
||||
case mir_window_state_fullscreen:
|
||||
new_width = window_info.window().size().width;
|
||||
new_height = window_info.window().size().height;
|
||||
movement = {0, 0};
|
||||
break;
|
||||
|
||||
case mir_window_state_vertmaximized:
|
||||
new_height = window_info.window().size().height;
|
||||
movement.dy = DeltaY{0};
|
||||
break;
|
||||
|
||||
case mir_window_state_horizmaximized:
|
||||
new_width = window_info.window().size().width;
|
||||
movement.dx - DeltaX{0};
|
||||
break;
|
||||
|
||||
default:;
|
||||
}
|
||||
|
||||
auto const min_width = std::max(window_info.min_width(), Width{5});
|
||||
auto const min_height = std::max(window_info.min_height(), Height{5});
|
||||
|
||||
if (new_width < min_width)
|
||||
{
|
||||
new_width = min_width;
|
||||
if (movement.dx > DeltaX{0})
|
||||
movement.dx = DeltaX{0};
|
||||
}
|
||||
|
||||
if (new_height < min_height)
|
||||
{
|
||||
new_height = min_height;
|
||||
if (movement.dy > DeltaY{0})
|
||||
movement.dy = DeltaY{0};
|
||||
}
|
||||
|
||||
auto const max_width = window_info.max_width();
|
||||
auto const max_height = window_info.max_height();
|
||||
|
||||
if (new_width > max_width)
|
||||
{
|
||||
new_width = max_width;
|
||||
if (movement.dx < DeltaX{0})
|
||||
movement.dx = DeltaX{0};
|
||||
}
|
||||
|
||||
if (new_height > max_height)
|
||||
{
|
||||
new_height = max_height;
|
||||
if (movement.dy < DeltaY{0})
|
||||
movement.dy = DeltaY{0};
|
||||
}
|
||||
}
|
||||
|
||||
WindowSpecification FloatingWindowManagerPolicy::place_new_window(
|
||||
ApplicationInfo const& app_info, WindowSpecification const& request_parameters)
|
||||
{
|
||||
auto parameters = MinimalWindowManager::place_new_window(app_info, request_parameters);
|
||||
|
||||
bool const needs_titlebar = WindowInfo::needs_titlebar(parameters.type().value());
|
||||
|
||||
if (parameters.state().value() != mir_window_state_fullscreen && needs_titlebar)
|
||||
parameters.top_left() = miral::Point{parameters.top_left().value().x, parameters.top_left().value().y + title_bar_height};
|
||||
parameters.top_left() = Point{parameters.top_left().value().x, parameters.top_left().value().y + title_bar_height};
|
||||
|
||||
parameters.userdata() = std::make_shared<miral::PolicyData>();
|
||||
parameters.userdata() = std::make_shared<PolicyData>();
|
||||
return parameters;
|
||||
}
|
||||
|
||||
void FloatingWindowManagerPolicy::advise_adding_to_workspace(
|
||||
std::shared_ptr<Workspace> const& workspace, std::vector<Window> const& windows)
|
||||
{
|
||||
if (windows.empty())
|
||||
return;
|
||||
|
||||
for (auto const& window : windows)
|
||||
{
|
||||
if (workspace == active_workspace)
|
||||
{
|
||||
apply_workspace_visible_to(window);
|
||||
}
|
||||
else
|
||||
{
|
||||
apply_workspace_hidden_to(window);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto FloatingWindowManagerPolicy::confirm_placement_on_display(
|
||||
miral::WindowInfo const& window_info, MirWindowState new_state, Rectangle const& new_placement) -> Rectangle
|
||||
{
|
||||
switch (new_state)
|
||||
{
|
||||
case mir_window_state_maximized:
|
||||
case mir_window_state_vertmaximized:
|
||||
if (window_info.needs_titlebar(window_info.type()))
|
||||
{
|
||||
auto result = new_placement;
|
||||
|
||||
result.top_left.y = result.top_left.y + title_bar_height;
|
||||
result.size.height = result.size.height - title_bar_height;
|
||||
return result;
|
||||
}
|
||||
// else
|
||||
// Falls through.
|
||||
default:
|
||||
return new_placement;
|
||||
}
|
||||
}
|
||||
|
||||
void FloatingWindowManagerPolicy::switch_workspace_to(
|
||||
std::shared_ptr<Workspace> const& workspace,
|
||||
Window const& window)
|
||||
{
|
||||
if (workspace == active_workspace)
|
||||
return;
|
||||
|
||||
auto const old_active = active_workspace;
|
||||
active_workspace = workspace;
|
||||
|
||||
auto const old_active_window = tools.active_window();
|
||||
|
||||
if (!old_active_window)
|
||||
{
|
||||
// If there's no active window, the first shown grabs focus: get the right one
|
||||
if (auto const ww = workspace_to_active[workspace])
|
||||
{
|
||||
tools.for_each_workspace_containing(ww, [&](std::shared_ptr<miral::Workspace> const& ws)
|
||||
{
|
||||
if (ws == workspace)
|
||||
{
|
||||
apply_workspace_visible_to(ww);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
tools.remove_tree_from_workspace(window, old_active);
|
||||
tools.add_tree_to_workspace(window, active_workspace);
|
||||
|
||||
tools.for_each_window_in_workspace(active_workspace, [&](Window const& window)
|
||||
{
|
||||
apply_workspace_visible_to(window);
|
||||
});
|
||||
|
||||
bool hide_old_active = false;
|
||||
tools.for_each_window_in_workspace(old_active, [&](Window const& window)
|
||||
{
|
||||
if (window == old_active_window)
|
||||
{
|
||||
// If we hide the active window focus will shift: do that last
|
||||
hide_old_active = true;
|
||||
return;
|
||||
}
|
||||
|
||||
apply_workspace_hidden_to(window);
|
||||
});
|
||||
|
||||
if (hide_old_active)
|
||||
{
|
||||
apply_workspace_hidden_to(old_active_window);
|
||||
|
||||
// Remember the old active_window when we switch away
|
||||
workspace_to_active[old_active] = old_active_window;
|
||||
}
|
||||
}
|
||||
|
||||
void FloatingWindowManagerPolicy::apply_workspace_hidden_to(Window const& window)
|
||||
{
|
||||
auto const& window_info = tools.info_for(window);
|
||||
auto& pdata = policy_data_for(window_info);
|
||||
if (!pdata.in_hidden_workspace)
|
||||
{
|
||||
pdata.in_hidden_workspace = true;
|
||||
pdata.old_state = window_info.state();
|
||||
|
||||
WindowSpecification modifications;
|
||||
modifications.state() = mir_window_state_hidden;
|
||||
tools.place_and_size_for_state(modifications, window_info);
|
||||
tools.modify_window(window_info.window(), modifications);
|
||||
}
|
||||
}
|
||||
|
||||
void FloatingWindowManagerPolicy::apply_workspace_visible_to(Window const& window)
|
||||
{
|
||||
auto const& window_info = tools.info_for(window);
|
||||
auto& pdata = policy_data_for(window_info);
|
||||
if (pdata.in_hidden_workspace)
|
||||
{
|
||||
pdata.in_hidden_workspace = false;
|
||||
WindowSpecification modifications;
|
||||
modifications.state() = pdata.old_state;
|
||||
tools.place_and_size_for_state(modifications, window_info);
|
||||
tools.modify_window(window_info.window(), modifications);
|
||||
}
|
||||
}
|
||||
|
||||
void FloatingWindowManagerPolicy::handle_modify_window(WindowInfo& window_info, WindowSpecification const& modifications)
|
||||
{
|
||||
auto mods = modifications;
|
||||
|
||||
auto& pdata = policy_data_for(window_info);
|
||||
|
||||
if (pdata.in_hidden_workspace && mods.state().is_set())
|
||||
pdata.old_state = mods.state().consume();
|
||||
|
||||
MinimalWindowManager::handle_modify_window(window_info, mods);
|
||||
}
|
||||
|
@ -1,38 +1,117 @@
|
||||
#ifndef FLOATING_WINDOW_MANAGER_HPP
|
||||
#define FLOATING_WINDOW_MANAGER_HPP
|
||||
/*
|
||||
* Copyright © Canonical Ltd.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 or 3 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef MIRAL_SHELL_FLOATING_WINDOW_MANAGER_H
|
||||
#define MIRAL_SHELL_FLOATING_WINDOW_MANAGER_H
|
||||
|
||||
#include "mir/geometry/rectangles.h"
|
||||
#include <miral/window_management_policy.h>
|
||||
#include <miral/window_manager_tools.h>
|
||||
#include <mir_toolkit/events/enums.h>
|
||||
#include <miral/minimal_window_manager.h>
|
||||
#include <miral/application_info.h>
|
||||
#include <miral/internal_client.h>
|
||||
#include <miral/toolkit_event.h>
|
||||
#include <miral/window_info.h>
|
||||
#include <miral/window_manager_tools.h>
|
||||
#include <miral/zone.h>
|
||||
|
||||
class FloatingWindowManager : public miral::WindowManagementPolicy {
|
||||
#include <mir_toolkit/events/enums.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <map>
|
||||
|
||||
namespace miral { class InternalClientLauncher; }
|
||||
|
||||
using namespace mir::geometry;
|
||||
|
||||
class FloatingWindowManagerPolicy : public miral::MinimalWindowManager
|
||||
{
|
||||
public:
|
||||
FloatingWindowManager(const miral::WindowManagerTools&);
|
||||
~FloatingWindowManager();
|
||||
miral::WindowSpecification place_new_window(
|
||||
miral::ApplicationInfo const& app_info,
|
||||
miral::WindowSpecification const& requested_specification) override;
|
||||
FloatingWindowManagerPolicy(
|
||||
miral::WindowManagerTools const& tools,
|
||||
miral::InternalClientLauncher const& launcher,
|
||||
std::function<void()>& shutdown_hook);
|
||||
~FloatingWindowManagerPolicy();
|
||||
|
||||
virtual miral::WindowSpecification place_new_window(
|
||||
miral::ApplicationInfo const& app_info, miral::WindowSpecification const& request_parameters) override;
|
||||
|
||||
/** @name example event handling:
|
||||
* o Switch apps: Alt+Tab, tap or click on the corresponding window
|
||||
* o Switch window: Alt+`, tap or click on the corresponding window
|
||||
* o Move window: Alt-leftmousebutton drag (three finger drag)
|
||||
* o Resize window: Alt-middle_button drag (three finger pinch)
|
||||
* o Maximize/restore current window (to display size): Alt-F11
|
||||
* o Maximize/restore current window (to display height): Shift-F11
|
||||
* o Maximize/restore current window (to display width): Ctrl-F11
|
||||
* o Switch workspace . . . . . . . . . . : Meta-Alt-[F1|F2|F3|F4]
|
||||
* o Switch workspace taking active window: Meta-Ctrl-[F1|F2|F3|F4]
|
||||
* @{ */
|
||||
bool handle_pointer_event(MirPointerEvent const* event) override;
|
||||
bool handle_touch_event(MirTouchEvent const* event) override;
|
||||
bool handle_keyboard_event(MirKeyboardEvent const* event) override;
|
||||
/** @} */
|
||||
|
||||
/** @name track events that affect titlebar
|
||||
* @{ */
|
||||
void advise_new_window(miral::WindowInfo const& window_info) override;
|
||||
void handle_window_ready(miral::WindowInfo& window_info) override;
|
||||
void handle_raise_window (miral::WindowInfo &window_info) override;
|
||||
void advise_focus_gained(miral::WindowInfo const& info) override;
|
||||
|
||||
void handle_modify_window(miral::WindowInfo& window_info, miral::WindowSpecification const& modifications) override;
|
||||
mir::geometry::Rectangle confirm_placement_on_display (miral::WindowInfo const &window_info, MirWindowState new_state, mir::geometry::Rectangle const &new_placement) override;
|
||||
void handle_request_drag_and_drop (miral::WindowInfo &window_info) override;
|
||||
void handle_request_move (miral::WindowInfo &window_info, MirInputEvent const *input_event) override;
|
||||
void handle_request_resize (miral::WindowInfo &window_info, MirInputEvent const *input_event, MirResizeEdge edge) override;
|
||||
mir::geometry::Rectangle confirm_inherited_move (miral::WindowInfo const &window_info, mir::geometry::Displacement movement) override;
|
||||
/** @} */
|
||||
|
||||
protected:
|
||||
static const int modifier_mask =
|
||||
mir_input_event_modifier_alt |
|
||||
mir_input_event_modifier_shift |
|
||||
mir_input_event_modifier_sym |
|
||||
mir_input_event_modifier_ctrl |
|
||||
mir_input_event_modifier_meta;
|
||||
|
||||
private:
|
||||
void toggle(MirWindowState state);
|
||||
|
||||
int old_touch_pinch_top = 0;
|
||||
int old_touch_pinch_left = 0;
|
||||
int old_touch_pinch_width = 0;
|
||||
int old_touch_pinch_height = 0;
|
||||
bool pinching = false;
|
||||
|
||||
void keep_window_within_constraints(
|
||||
miral::WindowInfo const& window_info,
|
||||
Displacement& movement,
|
||||
Width& new_width,
|
||||
Height& new_height) const;
|
||||
|
||||
// Workaround for lp:1627697
|
||||
std::chrono::steady_clock::time_point last_resize;
|
||||
|
||||
void advise_adding_to_workspace(
|
||||
std::shared_ptr<miral::Workspace> const& workspace,
|
||||
std::vector<miral::Window> const& windows) override;
|
||||
|
||||
auto confirm_placement_on_display(
|
||||
miral::WindowInfo const& window_info,
|
||||
MirWindowState new_state,
|
||||
Rectangle const& new_placement) -> Rectangle override;
|
||||
|
||||
// Switch workspace, taking window (if not null)
|
||||
void switch_workspace_to(
|
||||
std::shared_ptr<miral::Workspace> const& workspace,
|
||||
miral::Window const& window = miral::Window{});
|
||||
|
||||
std::shared_ptr<miral::Workspace> active_workspace;
|
||||
std::map<int, std::shared_ptr<miral::Workspace>> key_to_workspace;
|
||||
std::map<std::shared_ptr<miral::Workspace>, miral::Window> workspace_to_active;
|
||||
|
||||
void apply_workspace_visible_to(miral::Window const& window);
|
||||
|
||||
void apply_workspace_hidden_to(miral::Window const& window);
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif //MIRAL_SHELL_FLOATING_WINDOW_MANAGER_H
|
||||
|
35
src/main.cpp
35
src/main.cpp
@ -1,6 +1,7 @@
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
|
||||
#include "FloatingWindowManager.hpp"
|
||||
#include <miral/set_window_management_policy.h>
|
||||
#include <miral/display_configuration_option.h>
|
||||
#include <miral/external_client.h>
|
||||
@ -26,14 +27,15 @@ int main(int argc, char const* argv[]) {
|
||||
std::function<void()> shutdown_hook{[]{}};
|
||||
runner.add_stop_callback([&] { shutdown_hook(); });
|
||||
|
||||
WindowManagerOptions windowManagers
|
||||
{
|
||||
add_window_manager_policy<MinimalWindowManager>("floating"),
|
||||
};
|
||||
InternalClientLauncher launcher;
|
||||
|
||||
ExternalClientLauncher external_client_launcher;
|
||||
WindowManagerOptions window_managers
|
||||
{
|
||||
add_window_manager_policy<FloatingWindowManagerPolicy>("floating", launcher, shutdown_hook)
|
||||
};
|
||||
|
||||
std::string terminal_cmd{"miral-terminal"};
|
||||
std::string terminal_cmd{"gedit"};
|
||||
|
||||
auto const onEvent = [&](MirEvent const* event)
|
||||
{
|
||||
@ -75,23 +77,20 @@ int main(int argc, char const* argv[]) {
|
||||
|
||||
Keymap config_keymap;
|
||||
|
||||
auto run_startup_apps = [&](std::string const& apps)
|
||||
{
|
||||
for (auto i = begin(apps); i != end(apps); )
|
||||
{
|
||||
auto const j = find(i, end(apps), ':');
|
||||
external_client_launcher.launch(std::vector<std::string>{std::string{i, j}});
|
||||
if ((i = j) != end(apps)) ++i;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
return runner.run_with(
|
||||
auto runResult = runner.run_with(
|
||||
{
|
||||
windowManagers,
|
||||
window_managers,
|
||||
WaylandExtensions{},
|
||||
X11Support{},
|
||||
AppendEventFilter{onEvent},
|
||||
config_keymap
|
||||
config_keymap,
|
||||
external_client_launcher
|
||||
});
|
||||
|
||||
if (runResult == EXIT_FAILURE) {
|
||||
return runResult;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
Loading…
Reference in New Issue
Block a user