2
* Copyright Ā© 2015 Canonical Ltd.
4
* This program is free software: you can redistribute it and/or modify it
5
* under the terms of the GNU General Public License version 3,
6
* as published by the Free Software Foundation.
8
* This program is distributed in the hope that it will be useful,
9
* but WITHOUT ANY WARRANTY; without even the implied warranty of
10
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
* GNU General Public License for more details.
13
* You should have received a copy of the GNU General Public License
14
* along with this program. If not, see <http://www.gnu.org/licenses/>.
16
* Authored By: Alan Griffiths <alan@octopull.co.uk>
19
#include "server_example_tiling_window_manager.h"
21
#include "mir/scene/surface.h"
22
#include "mir/scene/surface_coordinator.h"
23
#include "mir/shell/surface_specification.h"
24
#include "mir/geometry/displacement.h"
26
#include <linux/input.h>
29
namespace me = mir::examples;
30
namespace ms = mir::scene;
31
using namespace mir::geometry;
33
///\example server_example_tiling_window_manager.cpp
34
/// Demonstrate implementing a simple tiling algorithm
36
me::TilingSurfaceInfo::TilingSurfaceInfo(
37
std::shared_ptr<scene::Session> const& session,
38
std::shared_ptr<scene::Surface> const& surface,
39
scene::SurfaceCreationParameters const& /*params*/) :
41
state{mir_surface_state_restored},
42
restore_rect{surface->top_left(), surface->size()}
46
me::TilingWindowManagerPolicy::TilingWindowManagerPolicy(Tools* const tools) :
51
void me::TilingWindowManagerPolicy::click(Point cursor)
53
auto const session = session_under(cursor);
54
auto const surface = tools->surface_at(cursor);
55
tools->set_focus_to(session, surface);
56
if (auto const surface = tools->focused_surface())
57
tools->raise({surface});
61
void me::TilingWindowManagerPolicy::handle_session_info_updated(TilingSessionInfoMap& session_info, Rectangles const& displays)
63
update_tiles(session_info, displays);
66
void me::TilingWindowManagerPolicy::handle_displays_updated(TilingSessionInfoMap& session_info, Rectangles const& displays)
68
update_tiles(session_info, displays);
71
void me::TilingWindowManagerPolicy::resize(Point cursor)
73
if (auto const session = session_under(cursor))
75
if (session == session_under(old_cursor))
77
auto const& info = tools->info_for(session);
79
if (resize(tools->focused_surface(), cursor, old_cursor, info.tile))
81
// Still dragging the same surface
85
auto const new_surface = tools->surface_at(old_cursor);
87
if (new_surface && tools->info_for(new_surface).session.lock() == session)
89
if (resize(new_surface, cursor, old_cursor, info.tile))
91
tools->set_focus_to(session, new_surface);
92
if (auto const surface = tools->focused_surface())
93
tools->raise({surface});
103
auto me::TilingWindowManagerPolicy::handle_place_new_surface(
104
std::shared_ptr<ms::Session> const& session,
105
ms::SurfaceCreationParameters const& request_parameters)
106
-> ms::SurfaceCreationParameters
108
auto parameters = request_parameters;
110
Rectangle const& tile = tools->info_for(session).tile;
111
parameters.top_left = parameters.top_left + (tile.top_left - Point{0, 0});
113
clip_to_tile(parameters, tile);
117
void me::TilingWindowManagerPolicy::generate_decorations_for(
118
std::shared_ptr<ms::Session> const&,
119
std::shared_ptr<ms::Surface> const&,
120
TilingSurfaceInfoMap&)
124
void me::TilingWindowManagerPolicy::handle_new_surface(std::shared_ptr<ms::Session> const& session, std::shared_ptr<ms::Surface> const& surface)
126
tools->info_for(session).surfaces.push_back(surface);
129
void me::TilingWindowManagerPolicy::handle_modify_surface(
130
std::shared_ptr<scene::Session> const& /*session*/,
131
std::shared_ptr<scene::Surface> const& surface,
132
shell::SurfaceSpecification const& modifications)
134
if (modifications.name.is_set())
135
surface->rename(modifications.name.value());
138
void me::TilingWindowManagerPolicy::handle_delete_surface(std::shared_ptr<ms::Session> const& session, std::weak_ptr<ms::Surface> const& surface)
140
auto& surfaces = tools->info_for(session).surfaces;
142
for (auto i = begin(surfaces); i != end(surfaces); ++i)
144
if (surface.lock() == i->lock())
151
if (surfaces.empty() && session == tools->focused_session())
153
tools->focus_next_session();
154
if (auto const surface = tools->focused_surface())
155
tools->raise({surface});
159
int me::TilingWindowManagerPolicy::handle_set_state(std::shared_ptr<ms::Surface> const& surface, MirSurfaceState value)
161
auto& info = tools->info_for(surface);
165
case mir_surface_state_restored:
166
case mir_surface_state_maximized:
167
case mir_surface_state_vertmaximized:
168
case mir_surface_state_horizmaximized:
175
if (info.state == mir_surface_state_restored)
177
info.restore_rect = {surface->top_left(), surface->size()};
180
if (info.state == value)
185
auto const& tile = tools->info_for(info.session).tile;
189
case mir_surface_state_restored:
190
surface->move_to(info.restore_rect.top_left);
191
surface->resize(info.restore_rect.size);
194
case mir_surface_state_maximized:
195
surface->move_to(tile.top_left);
196
surface->resize(tile.size);
199
case mir_surface_state_horizmaximized:
200
surface->move_to({tile.top_left.x, info.restore_rect.top_left.y});
201
surface->resize({tile.size.width, info.restore_rect.size.height});
204
case mir_surface_state_vertmaximized:
205
surface->move_to({info.restore_rect.top_left.x, tile.top_left.y});
206
surface->resize({info.restore_rect.size.width, tile.size.height});
213
return info.state = value;
216
void me::TilingWindowManagerPolicy::drag(Point cursor)
218
if (auto const session = session_under(cursor))
220
if (session == session_under(old_cursor))
222
auto const& info = tools->info_for(session);
223
if (drag(tools->focused_surface(), cursor, old_cursor, info.tile))
225
// Still dragging the same surface
229
auto const new_surface = tools->surface_at(old_cursor);
231
if (new_surface && tools->info_for(new_surface).session.lock() == session)
233
if (resize(new_surface, cursor, old_cursor, info.tile))
235
tools->set_focus_to(session, new_surface);
236
if (auto const surface = tools->focused_surface())
237
tools->raise({surface});
247
bool me::TilingWindowManagerPolicy::handle_keyboard_event(MirKeyboardEvent const* event)
249
auto const action = mir_keyboard_event_action(event);
250
auto const scan_code = mir_keyboard_event_scan_code(event);
251
auto const modifiers = mir_keyboard_event_modifiers(event) & modifier_mask;
253
if (action == mir_keyboard_action_down && scan_code == KEY_F11)
255
switch (modifiers & modifier_mask)
257
case mir_input_event_modifier_alt:
258
toggle(mir_surface_state_maximized);
261
case mir_input_event_modifier_shift:
262
toggle(mir_surface_state_vertmaximized);
265
case mir_input_event_modifier_ctrl:
266
toggle(mir_surface_state_horizmaximized);
273
else if (action == mir_keyboard_action_down && scan_code == KEY_F4)
275
if (auto const session = tools->focused_session())
277
switch (modifiers & modifier_mask)
279
case mir_input_event_modifier_alt:
280
kill(session->process_id(), SIGTERM);
283
case mir_input_event_modifier_ctrl:
284
if (auto const surf = session->default_surface())
286
surf->request_client_surface_close();
299
bool me::TilingWindowManagerPolicy::handle_touch_event(MirTouchEvent const* event)
301
auto const count = mir_touch_event_point_count(event);
306
for (auto i = 0U; i != count; ++i)
308
total_x += mir_touch_event_axis_value(event, i, mir_touch_axis_x);
309
total_y += mir_touch_event_axis_value(event, i, mir_touch_axis_y);
312
Point const cursor{total_x/count, total_y/count};
315
for (auto i = 0U; i != count; ++i)
317
switch (mir_touch_event_action(event, i))
319
case mir_touch_action_up:
322
case mir_touch_action_down:
325
case mir_touch_action_change:
330
if (is_drag && count == 3)
342
bool me::TilingWindowManagerPolicy::handle_pointer_event(MirPointerEvent const* event)
344
auto const action = mir_pointer_event_action(event);
345
auto const modifiers = mir_pointer_event_modifiers(event) & modifier_mask;
347
mir_pointer_event_axis_value(event, mir_pointer_axis_x),
348
mir_pointer_event_axis_value(event, mir_pointer_axis_y)};
350
if (action == mir_pointer_action_button_down)
355
else if (action == mir_pointer_action_motion &&
356
modifiers == mir_input_event_modifier_alt)
358
if (mir_pointer_event_button_state(event, mir_pointer_button_primary))
364
if (mir_pointer_event_button_state(event, mir_pointer_button_tertiary))
374
void me::TilingWindowManagerPolicy::toggle(MirSurfaceState state)
376
if (auto const session = tools->focused_session())
378
if (auto const surface = session->default_surface())
380
if (surface->state() == state)
381
state = mir_surface_state_restored;
383
auto const value = handle_set_state(surface, MirSurfaceState(state));
384
surface->configure(mir_surface_attrib_state, value);
389
std::shared_ptr<ms::Session> me::TilingWindowManagerPolicy::session_under(Point position)
391
return tools->find_session([&](TilingSessionInfo const& info) { return info.tile.contains(position);});
394
void me::TilingWindowManagerPolicy::update_tiles(
395
TilingSessionInfoMap& session_info,
396
Rectangles const& displays)
398
if (session_info.size() < 1 || displays.size() < 1) return;
400
auto const sessions = session_info.size();
402
auto const bounding_rect = displays.bounding_rectangle();
404
auto const total_width = bounding_rect.size.width.as_int();
405
auto const total_height = bounding_rect.size.height.as_int();
409
for (auto& info : session_info)
411
auto const x = (total_width*index)/sessions;
413
auto const dx = (total_width*index)/sessions - x;
415
auto const old_tile = info.second.tile;
416
Rectangle const new_tile{{x, 0}, {dx, total_height}};
418
update_surfaces(info.first, old_tile, new_tile);
420
info.second.tile = new_tile;
424
void me::TilingWindowManagerPolicy::update_surfaces(std::weak_ptr<ms::Session> const& session, Rectangle const& old_tile, Rectangle const& new_tile)
426
auto displacement = new_tile.top_left - old_tile.top_left;
427
auto& info = tools->info_for(session);
429
for (auto const& ps : info.surfaces)
431
if (auto const surface = ps.lock())
433
auto const old_pos = surface->top_left();
434
surface->move_to(old_pos + displacement);
436
fit_to_new_tile(*surface, old_tile, new_tile);
441
void me::TilingWindowManagerPolicy::clip_to_tile(ms::SurfaceCreationParameters& parameters, Rectangle const& tile)
443
auto const displacement = parameters.top_left - tile.top_left;
445
auto width = std::min(tile.size.width.as_int()-displacement.dx.as_int(), parameters.size.width.as_int());
446
auto height = std::min(tile.size.height.as_int()-displacement.dy.as_int(), parameters.size.height.as_int());
448
parameters.size = Size{width, height};
451
void me::TilingWindowManagerPolicy::fit_to_new_tile(ms::Surface& surface, Rectangle const& old_tile, Rectangle const& new_tile)
453
auto const displacement = surface.top_left() - new_tile.top_left;
455
// For now just scale if was filling width/height of tile
456
auto const old_size = surface.size();
457
auto const scaled_width = old_size.width == old_tile.size.width ? new_tile.size.width : old_size.width;
458
auto const scaled_height = old_size.height == old_tile.size.height ? new_tile.size.height : old_size.height;
460
auto width = std::min(new_tile.size.width.as_int()-displacement.dx.as_int(), scaled_width.as_int());
461
auto height = std::min(new_tile.size.height.as_int()-displacement.dy.as_int(), scaled_height.as_int());
463
surface.resize({width, height});
466
bool me::TilingWindowManagerPolicy::drag(std::shared_ptr<ms::Surface> surface, Point to, Point from, Rectangle bounds)
468
if (surface && surface->input_area_contains(from))
470
auto const top_left = surface->top_left();
471
auto const surface_size = surface->size();
472
auto const bottom_right = top_left + as_displacement(surface_size);
474
auto movement = to - from;
476
if (movement.dx < DeltaX{0})
477
movement.dx = std::max(movement.dx, (bounds.top_left - top_left).dx);
479
if (movement.dy < DeltaY{0})
480
movement.dy = std::max(movement.dy, (bounds.top_left - top_left).dy);
482
if (movement.dx > DeltaX{0})
483
movement.dx = std::min(movement.dx, (bounds.bottom_right() - bottom_right).dx);
485
if (movement.dy > DeltaY{0})
486
movement.dy = std::min(movement.dy, (bounds.bottom_right() - bottom_right).dy);
488
auto new_pos = surface->top_left() + movement;
490
surface->move_to(new_pos);
497
bool me::TilingWindowManagerPolicy::resize(std::shared_ptr<ms::Surface> surface, Point cursor, Point old_cursor, Rectangle bounds)
499
if (surface && surface->input_area_contains(old_cursor))
501
auto const top_left = surface->top_left();
503
auto const old_displacement = old_cursor - top_left;
504
auto const new_displacement = cursor - top_left;
506
auto const scale_x = new_displacement.dx.as_float()/std::max(1.0f, old_displacement.dx.as_float());
507
auto const scale_y = new_displacement.dy.as_float()/std::max(1.0f, old_displacement.dy.as_float());
509
if (scale_x <= 0.0f || scale_y <= 0.0f) return false;
511
auto const old_size = surface->size();
512
Size new_size{scale_x*old_size.width, scale_y*old_size.height};
514
auto const size_limits = as_size(bounds.bottom_right() - top_left);
516
if (new_size.width > size_limits.width)
517
new_size.width = size_limits.width;
519
if (new_size.height > size_limits.height)
520
new_size.height = size_limits.height;
522
surface->resize(new_size);