#include <linux/input.h>
#include <csignal>
#include <algorithm>
namespace
{
int const title_bar_height = 10;
Size titlebar_size_for_window(
Size window_size)
{
}
Point titlebar_position_for_window(
Point window_position)
{
return {
window_position.
y -
DeltaY(title_bar_height)
};
}
}
WindowManagerTools* const tools,
std::shared_ptr<shell::DisplayLayout> const& display_layout) :
tools{tools},
display_layout{display_layout}
{
}
{
if (auto const surface = tools->surface_at(cursor))
select_active_surface(surface);
}
{
}
{
for (auto const weak_surface : fullscreen_surfaces)
{
if (auto const surface = weak_surface.lock())
{
auto const& info = tools->info_for(weak_surface);
display_layout->place_in_output(info.output_id.value(), rect);
surface->move_to(rect.top_left);
surface->resize(rect.size);
}
}
}
{
if (!resizing) select_active_surface(tools->surface_at(old_cursor));
resize(active_surface(), cursor, old_cursor, display_area);
}
std::shared_ptr<ms::Session> const& session,
{
auto parameters = request_parameters;
bool const needs_titlebar = SurfaceInfo::needs_titlebar(surf_type);
if (needs_titlebar)
parameters.size.height = parameters.size.height +
DeltaY{title_bar_height};
if (!parameters.state.is_set())
auto const active_display = tools->active_display();
auto const width = parameters.size.width.as_int();
auto const height = parameters.size.height.as_int();
bool positioned = false;
auto const parent = parameters.parent.lock();
{
display_layout->place_in_output(parameters.output_id, rect);
parameters.top_left = rect.top_left;
parameters.size = rect.size;
positioned = true;
}
else if (!parent)
{
if (auto const default_surface = session->default_surface())
{
static Displacement const offset{title_bar_height, title_bar_height};
parameters.top_left = default_surface->top_left() + offset;
geometry::Rectangle display_for_app{default_surface->top_left(), default_surface->size()};
display_layout->size_to_output(display_for_app);
positioned = display_for_app.overlaps(
Rectangle{parameters.
top_left, parameters.size});
}
}
if (parent && parameters.aux_rect.is_set() && parameters.edge_attachment.is_set())
{
auto const edge_attachment = parameters.edge_attachment.value();
auto const aux_rect = parameters.aux_rect.value();
auto const parent_top_left = parent->top_left();
auto const top_left = aux_rect.top_left -
Point{} + parent_top_left;
auto const top_right= aux_rect.top_right() -
Point{} + parent_top_left;
auto const bot_left = aux_rect.bottom_left()-
Point{} + parent_top_left;
{
if (active_display.contains(top_right +
Displacement{width, height}))
{
parameters.top_left = top_right;
positioned = true;
}
else if (active_display.contains(top_left +
Displacement{-width, height}))
{
positioned = true;
}
}
{
if (active_display.contains(bot_left +
Displacement{width, height}))
{
parameters.top_left = bot_left;
positioned = true;
}
else if (active_display.contains(top_left +
Displacement{width, -height}))
{
positioned = true;
}
}
}
else if (parent)
{
auto const parent_top_left = parent->top_left();
auto const centred = parent_top_left
+ 0.5*(as_displacement(parent->size()) - as_displacement(parameters.size))
parameters.top_left = centred;
positioned = true;
}
if (!positioned)
{
auto const centred = active_display.top_left
+ 0.5*(as_displacement(active_display.size) - as_displacement(parameters.size))
switch (parameters.state.value())
{
parameters.top_left = active_display.top_left;
parameters.size = active_display.size;
break;
parameters.top_left = centred;
parameters.top_left.y = active_display.top_left.y;
parameters.size.height = active_display.size.height;
break;
parameters.top_left = centred;
parameters.top_left.x = active_display.top_left.x;
parameters.size.width = active_display.size.width;
break;
default:
parameters.top_left = centred;
}
if (parameters.top_left.y < display_area.top_left.y)
parameters.top_left.y = display_area.top_left.y;
}
{
parameters.top_left.y = parameters.top_left.y +
DeltaY{title_bar_height};
parameters.size.height = parameters.size.height -
DeltaY{title_bar_height};
}
return parameters;
}
std::shared_ptr<scene::Session> const& session,
std::shared_ptr<scene::Surface> const& surface,
SurfaceInfoMap& surface_map,
std::function<
frontend::SurfaceId(std::shared_ptr<scene::Session>
const& session, scene::SurfaceCreationParameters
const& params)>
const& build)
{
if (!SurfaceInfo::needs_titlebar(surface->type()))
return;
auto stream_id = session->create_buffer_stream(properties);
.
of_size(titlebar_size_for_window(surface->size()))
.
of_position(titlebar_position_for_window(surface->top_left()))
auto id = build(session, params);
auto titlebar = session->surface(id);
titlebar->set_alpha(0.9);
auto& surface_info = tools->info_for(surface);
surface_info.titlebar = titlebar;
surface_info.titlebar_id = id;
surface_info.titlebar_stream_id = stream_id;
surface_info.children.push_back(titlebar);
SurfaceInfo& titlebar_info =
surface_map.emplace(titlebar, SurfaceInfo{session, titlebar, {}}).first->second;
titlebar_info.is_titlebar = true;
titlebar_info.parent = surface;
titlebar_info.init_titlebar(session, titlebar);
}
{
auto& surface_info = tools->info_for(surface);
if (auto const parent = surface_info.parent.lock())
{
tools->info_for(parent).children.push_back(surface);
}
tools->info_for(session).surfaces.push_back(surface);
if (surface_info.can_be_active())
{
surface->add_observer(std::make_shared<shell::SurfaceReadyObserver>(
[this](std::shared_ptr<scene::Session> const& ,
std::shared_ptr<scene::Surface> const& surface)
{
select_active_surface(surface);
},
session,
surface));
}
fullscreen_surfaces.insert(surface);
}
std::shared_ptr<scene::Session> const& session,
std::shared_ptr<scene::Surface> const& surface,
shell::SurfaceSpecification const& modifications)
{
auto& surface_info_old = tools->info_for(surface);
auto surface_info = surface_info_old;
if (modifications.parent.is_set())
surface_info.parent = modifications.parent.value();
if (modifications.type.is_set() &&
surface_info.type != modifications.type.value())
{
auto const new_type = modifications.type.value();
if (!surface_info.can_morph_to(new_type))
{
throw std::runtime_error("Unsupported surface type change");
}
surface_info.type = new_type;
if (surface_info.must_not_have_parent())
{
if (modifications.parent.is_set())
throw std::runtime_error("Target surface type does not support parent");
surface_info.parent.reset();
}
else if (surface_info.must_have_parent())
{
if (!surface_info.parent.lock())
throw std::runtime_error("Target surface type requires parent");
}
}
#define COPY_IF_SET(field)\
if (modifications.field.is_set())\
surface_info.field = modifications.field.value()
#undef COPY_IF_SET
std::swap(surface_info, surface_info_old);
if (modifications.name.is_set())
surface->rename(modifications.name.value());
if (modifications.streams.is_set())
{
auto v = modifications.streams.value();
std::vector<shell::StreamSpecification> l (v.begin(), v.end());
session->configure_streams(*surface, l);
}
if (modifications.width.is_set() || modifications.height.is_set())
{
auto new_size = surface->size();
if (modifications.width.is_set())
new_size.width = modifications.width.value();
if (modifications.height.is_set())
new_size.height = modifications.height.value();
auto top_left = surface->top_left();
surface_info.constrain_resize(
surface,
top_left,
new_size,
false,
false,
display_area);
apply_resize(surface, surface_info.titlebar, top_left, new_size);
}
if (modifications.input_shape.is_set())
{
auto rectangles = modifications.input_shape.value();
auto displacement = surface->top_left() -
Point{0, 0};
for(auto& rect : rectangles)
{
rect.top_left = rect.top_left + displacement;
rect = rect.intersection_with({surface->top_left(), surface->size()});
rect.top_left = rect.top_left - displacement;
}
surface->set_input_region(rectangles);
}
if (modifications.state.is_set())
{
auto const state = handle_set_state(surface, modifications.state.value());
}
if (modifications.confine_pointer.is_set())
{
surface->set_confine_pointer_state(modifications.confine_pointer.value());
}
}
{
fullscreen_surfaces.erase(surface);
bool const is_active_surface{surface.lock() == active_surface()};
auto& info = tools->info_for(surface);
if (auto const parent = info.parent.lock())
{
auto& siblings = tools->info_for(parent).children;
for (
auto i =
begin(siblings); i !=
end(siblings); ++i)
{
if (surface.lock() == i->lock())
{
siblings.erase(i);
break;
}
}
}
session->destroy_surface(surface);
if (info.titlebar)
{
session->destroy_surface(info.titlebar_id);
session->destroy_buffer_stream(info.titlebar_stream_id);
tools->forget(info.titlebar);
}
auto& surfaces = tools->info_for(session).surfaces;
for (
auto i =
begin(surfaces); i !=
end(surfaces); ++i)
{
if (surface.lock() == i->lock())
{
surfaces.erase(i);
break;
}
}
if (is_active_surface)
{
active_surface_.reset();
if (surfaces.empty())
{
tools->focus_next_session();
select_active_surface(tools->focused_surface());
}
else
{
select_active_surface(surfaces[0].lock());
}
}
}
{
auto& info = tools->info_for(surface);
switch (value)
{
break;
default:
return info.state;
}
{
}
{
info.output_id = decltype(info.output_id){};
fullscreen_surfaces.erase(surface);
}
else
{
fullscreen_surfaces.insert(surface);
}
if (info.state == value)
{
return info.state;
}
auto const old_pos = surface->
top_left();
switch (value)
{
movement = info.restore_rect.top_left - old_pos;
surface->
resize(info.restore_rect.size);
if (info.titlebar)
{
info.titlebar->resize(titlebar_size_for_window(info.restore_rect.size));
info.titlebar->show();
}
break;
movement = display_area.top_left - old_pos;
surface->
resize(display_area.size);
if (info.titlebar)
info.titlebar->hide();
break;
movement =
Point{display_area.top_left.
x, info.restore_rect.top_left.y} - old_pos;
surface->
resize({display_area.size.width, info.restore_rect.size.height});
if (info.titlebar)
{
info.titlebar->resize(titlebar_size_for_window({display_area.size.width, info.restore_rect.size.height}));
info.titlebar->show();
}
break;
movement =
Point{info.restore_rect.top_left.
x, display_area.top_left.y} - old_pos;
surface->
resize({info.restore_rect.size.width, display_area.size.height});
if (info.titlebar)
info.titlebar->hide();
break;
{
if (info.output_id.is_set())
{
display_layout->place_in_output(info.output_id.value(), rect);
}
else
{
display_layout->size_to_output(rect);
}
movement = rect.top_left - old_pos;
break;
}
if (info.titlebar)
info.titlebar->hide();
return info.state = value;
default:
break;
}
move_tree(surface, movement);
info.state = value;
if (info.is_visible())
return info.state;
}
{
select_active_surface(tools->surface_at(old_cursor));
drag(active_surface(), cursor, old_cursor, display_area);
}
std::shared_ptr<ms::Session> const& ,
std::shared_ptr<ms::Surface> const& surface)
{
select_active_surface(surface);
}
{
{
switch (modifiers)
{
return true;
return true;
return true;
default:
break;
}
}
{
if (auto const session = tools->focused_session())
{
switch (modifiers)
{
kill(session->process_id(), SIGTERM);
return true;
if (auto const surf = session->default_surface())
{
surf->request_client_surface_close();
return true;
}
default:
break;
}
}
}
scan_code == KEY_TAB)
{
tools->focus_next_session();
if (auto const surface = tools->focused_surface())
select_active_surface(surface);
return true;
}
scan_code == KEY_GRAVE)
{
if (auto const prev = tools->focused_surface())
{
if (auto const app = tools->focused_session())
select_active_surface(app->surface_after(prev));
}
return true;
}
return false;
}
{
long total_x = 0;
long total_y = 0;
for (auto i = 0U; i != count; ++i)
{
}
Point const cursor{total_x/count, total_y/count};
bool is_drag = true;
for (auto i = 0U; i != count; ++i)
{
{
return false;
is_drag = false;
continue;
abort();
break;
}
}
bool consumes_event = false;
if (is_drag)
{
switch (count)
{
case 4:
resize(cursor);
consumes_event = true;
break;
case 3:
drag(cursor);
consumes_event = true;
break;
}
}
old_cursor = cursor;
return consumes_event;
}
{
bool consumes_event = false;
bool resize_event = false;
{
click(cursor);
}
{
{
drag(cursor);
consumes_event = true;
}
{
resize(cursor);
resize_event = active_surface_.lock().get();
consumes_event = true;
}
}
{
{
if (auto const possible_titlebar = tools->surface_at(old_cursor))
{
if (tools->info_for(possible_titlebar).is_titlebar)
{
drag(cursor);
consumes_event = true;
}
}
}
}
resizing = resize_event;
old_cursor = cursor;
return consumes_event;
}
void me::CanonicalWindowManagerPolicyCopy::toggle(
MirWindowState state)
{
if (auto const surface = active_surface())
{
auto& info = tools->info_for(surface);
if (info.state == state)
}
}
void me::CanonicalWindowManagerPolicyCopy::select_active_surface(std::shared_ptr<ms::Surface> const& surface)
{
if (surface == active_surface_.lock())
return;
if (!surface)
{
if (auto const active_surface = active_surface_.lock())
{
if (auto const titlebar = tools->info_for(active_surface).titlebar)
{
tools->info_for(titlebar).paint_titlebar(0x3F);
}
}
if (active_surface_.lock())
tools->set_focus_to({}, {});
active_surface_.reset();
return;
}
auto const& info_for = tools->info_for(surface);
if (info_for.can_be_active())
{
if (auto const active_surface = active_surface_.lock())
{
if (auto const titlebar = tools->info_for(active_surface).titlebar)
{
tools->info_for(titlebar).paint_titlebar(0x3F);
}
}
if (auto const titlebar = tools->info_for(surface).titlebar)
{
tools->info_for(titlebar).paint_titlebar(0xFF);
}
tools->set_focus_to(info_for.session.lock(), surface);
tools->raise_tree(surface);
active_surface_ = surface;
}
else
{
if (auto const parent = info_for.parent.lock())
select_active_surface(parent);
}
}
auto me::CanonicalWindowManagerPolicyCopy::active_surface() const
-> std::shared_ptr<ms::Surface>
{
if (auto const surface = active_surface_.lock())
return surface;
if (auto const session = tools->focused_session())
{
if (auto const surface = session->default_surface())
return surface;
}
return std::shared_ptr<ms::Surface>{};
}
{
if (!surface)
return false;
auto const& surface_info = tools->info_for(surface);
auto const top_left = surface->
top_left();
if (!resizing)
{
auto anchor = old_pos.bottom_right();
for (auto const& corner : {
old_pos.top_right(),
old_pos.bottom_left(),
top_left})
{
if ((old_cursor - anchor).length_squared() <
(old_cursor - corner).length_squared())
{
anchor = corner;
}
}
left_resize = anchor.x != top_left.x;
top_resize = anchor.y != top_left.y;
}
int const x_sign = left_resize? -1 : 1;
int const y_sign = top_resize? -1 : 1;
auto delta = cursor-old_cursor;
auto new_width = old_pos.size.width + x_sign * delta.dx;
auto new_height = old_pos.size.height + y_sign * delta.dy;
auto const min_width = std::max(surface_info.min_width,
Width{5});
auto const min_height = std::max(surface_info.min_height,
Height{5});
if (new_width < min_width)
{
new_width = min_width;
}
if (new_height < min_height)
{
new_height = min_height;
}
Size new_size{new_width, new_height};
Point new_pos = top_left + left_resize*delta.dx + top_resize*delta.dy;
surface_info.constrain_resize(surface, new_pos, new_size, left_resize, top_resize, bounds);
apply_resize(surface, surface_info.titlebar, new_pos, new_size);
return true;
}
void me::CanonicalWindowManagerPolicyCopy::apply_resize(
std::shared_ptr<ms::Surface> const& surface,
std::shared_ptr<ms::Surface> const& titlebar,
Size const& new_size)
const {
if (titlebar)
titlebar->resize({new_size.
width,
Height{title_bar_height}});
move_tree(surface, new_pos-surface->
top_left());
}
{
if (!surface)
return false;
return false;
auto movement = to - from;
switch (tools->info_for(surface).state)
{
break;
break;
break;
default:
return true;
}
move_tree(surface, movement);
return true;
}
void me::CanonicalWindowManagerPolicyCopy::move_tree(std::shared_ptr<ms::Surface>
const& root,
Displacement movement)
const {
root->move_to(root->top_left() + movement);
for (auto const& child: tools->info_for(root).children)
{
move_tree(child.lock(), movement);
}
}