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/geometry/displacement.h"
24
#include <linux/input.h>
27
namespace me = mir::examples;
28
namespace ms = mir::scene;
29
using namespace mir::geometry;
31
///\example server_example_tiling_window_manager.cpp
32
/// Demonstrate implementing a simple tiling algorithm
34
me::TilingSurfaceInfo::TilingSurfaceInfo(
35
std::shared_ptr<scene::Session> const& session,
36
std::shared_ptr<scene::Surface> const& surface) :
38
state{mir_surface_state_restored},
39
restore_rect{surface->top_left(), surface->size()}
43
me::TilingWindowManagerPolicy::TilingWindowManagerPolicy(Tools* const tools) :
48
void me::TilingWindowManagerPolicy::click(Point cursor)
50
if (const auto session = session_under(cursor))
51
tools->set_focus_to(session);
55
void me::TilingWindowManagerPolicy::handle_session_info_updated(TilingSessionInfoMap& session_info, Rectangles const& displays)
57
update_tiles(session_info, displays);
60
void me::TilingWindowManagerPolicy::handle_displays_updated(TilingSessionInfoMap& session_info, Rectangles const& displays)
62
update_tiles(session_info, displays);
65
void me::TilingWindowManagerPolicy::resize(Point cursor)
67
if (auto const session = session_under(cursor))
69
if (session == session_under(old_cursor))
71
auto const& info = tools->info_for(session);
73
if (resize(old_surface.lock(), cursor, old_cursor, info.tile))
75
// Still dragging the same old_surface
77
else if (resize(session->default_surface(), cursor, old_cursor, info.tile))
79
old_surface = session->default_surface();
83
for (auto const& ps : info.surfaces)
85
auto const new_surface = ps.lock();
87
if (resize(new_surface, cursor, old_cursor, info.tile))
89
old_surface = new_surface;
99
auto me::TilingWindowManagerPolicy::handle_place_new_surface(
100
std::shared_ptr<ms::Session> const& session,
101
ms::SurfaceCreationParameters const& request_parameters)
102
-> ms::SurfaceCreationParameters
104
auto parameters = request_parameters;
106
Rectangle const& tile = tools->info_for(session).tile;
107
parameters.top_left = parameters.top_left + (tile.top_left - Point{0, 0});
109
clip_to_tile(parameters, tile);
113
void me::TilingWindowManagerPolicy::handle_new_surface(std::shared_ptr<ms::Session> const& session, std::shared_ptr<ms::Surface> const& surface)
115
tools->info_for(session).surfaces.push_back(surface);
118
void me::TilingWindowManagerPolicy::handle_delete_surface(std::shared_ptr<ms::Session> const& session, std::weak_ptr<ms::Surface> const& surface)
120
auto& surfaces = tools->info_for(session).surfaces;
122
for (auto i = begin(surfaces); i != end(surfaces); ++i)
124
if (surface.lock() == i->lock())
131
if (surfaces.empty() && session == tools->focussed_application().lock())
137
int me::TilingWindowManagerPolicy::handle_set_state(std::shared_ptr<ms::Surface> const& surface, MirSurfaceState value)
139
auto& info = tools->info_for(surface);
143
case mir_surface_state_restored:
144
case mir_surface_state_maximized:
145
case mir_surface_state_vertmaximized:
146
case mir_surface_state_horizmaximized:
153
if (info.state == mir_surface_state_restored)
155
info.restore_rect = {surface->top_left(), surface->size()};
158
if (info.state == value)
163
auto const& tile = tools->info_for(info.session).tile;
167
case mir_surface_state_restored:
168
surface->move_to(info.restore_rect.top_left);
169
surface->resize(info.restore_rect.size);
172
case mir_surface_state_maximized:
173
surface->move_to(tile.top_left);
174
surface->resize(tile.size);
177
case mir_surface_state_horizmaximized:
178
surface->move_to({tile.top_left.x, info.restore_rect.top_left.y});
179
surface->resize({tile.size.width, info.restore_rect.size.height});
182
case mir_surface_state_vertmaximized:
183
surface->move_to({info.restore_rect.top_left.x, tile.top_left.y});
184
surface->resize({info.restore_rect.size.width, tile.size.height});
191
return info.state = value;
194
void me::TilingWindowManagerPolicy::drag(Point cursor)
196
if (const auto session = session_under(cursor))
198
if (session == session_under(old_cursor))
200
const auto& info = tools->info_for(session);
201
if (drag(old_surface.lock(), cursor, old_cursor, info.tile))
203
// Still dragging the same old_surface
205
else if (drag(session->default_surface(), cursor, old_cursor, info.tile))
207
old_surface = session->default_surface();
211
for (const auto& ps : info.surfaces)
213
const auto new_surface = ps.lock();
214
if (drag(new_surface, cursor, old_cursor, info.tile))
216
old_surface = new_surface;
226
bool me::TilingWindowManagerPolicy::handle_key_event(MirKeyInputEvent const* event)
228
auto const action = mir_key_input_event_get_action(event);
229
auto const scan_code = mir_key_input_event_get_scan_code(event);
230
auto const modifiers = mir_key_input_event_get_modifiers(event) & modifier_mask;
232
if (action == mir_key_input_event_action_down && scan_code == KEY_F11)
234
switch (modifiers & modifier_mask)
236
case mir_input_event_modifier_alt:
237
toggle(mir_surface_state_maximized);
240
case mir_input_event_modifier_shift:
241
toggle(mir_surface_state_vertmaximized);
244
case mir_input_event_modifier_ctrl:
245
toggle(mir_surface_state_horizmaximized);
252
else if (action == mir_key_input_event_action_down && scan_code == KEY_F4)
254
if (auto const session = tools->focussed_application().lock())
256
switch (modifiers & modifier_mask)
258
case mir_input_event_modifier_alt:
259
kill(session->process_id(), SIGTERM);
262
case mir_input_event_modifier_ctrl:
263
if (auto const surf = session->default_surface())
265
surf->request_client_surface_close();
278
bool me::TilingWindowManagerPolicy::handle_touch_event(MirTouchInputEvent const* event)
280
auto const count = mir_touch_input_event_get_touch_count(event);
285
for (auto i = 0U; i != count; ++i)
287
total_x += mir_touch_input_event_get_touch_axis_value(event, i, mir_touch_input_axis_x);
288
total_y += mir_touch_input_event_get_touch_axis_value(event, i, mir_touch_input_axis_y);
291
Point const cursor{total_x/count, total_y/count};
294
for (auto i = 0U; i != count; ++i)
296
switch (mir_touch_input_event_get_touch_action(event, i))
298
case mir_touch_input_event_action_up:
301
case mir_touch_input_event_action_down:
304
case mir_touch_input_event_action_change:
309
if (is_drag && count == 3)
321
bool me::TilingWindowManagerPolicy::handle_pointer_event(MirPointerInputEvent const* event)
323
auto const action = mir_pointer_input_event_get_action(event);
324
auto const modifiers = mir_pointer_input_event_get_modifiers(event) & modifier_mask;
326
mir_pointer_input_event_get_axis_value(event, mir_pointer_input_axis_x),
327
mir_pointer_input_event_get_axis_value(event, mir_pointer_input_axis_y)};
329
if (action == mir_pointer_input_event_action_button_down)
334
else if (action == mir_pointer_input_event_action_motion &&
335
modifiers == mir_input_event_modifier_alt)
337
if (mir_pointer_input_event_get_button_state(event, mir_pointer_input_button_primary))
343
if (mir_pointer_input_event_get_button_state(event, mir_pointer_input_button_tertiary))
353
void me::TilingWindowManagerPolicy::toggle(MirSurfaceState state)
355
if (auto const session = tools->focussed_application().lock())
357
if (auto const surface = session->default_surface())
359
if (surface->state() == state)
360
state = mir_surface_state_restored;
362
auto const value = handle_set_state(surface, MirSurfaceState(state));
363
surface->configure(mir_surface_attrib_state, value);
368
std::shared_ptr<ms::Session> me::TilingWindowManagerPolicy::session_under(Point position)
370
return tools->find_session([&](TilingSessionInfo const& info) { return info.tile.contains(position);});
373
void me::TilingWindowManagerPolicy::update_tiles(
374
TilingSessionInfoMap& session_info,
375
Rectangles const& displays)
377
if (session_info.size() < 1 || displays.size() < 1) return;
379
auto const sessions = session_info.size();
381
auto const bounding_rect = displays.bounding_rectangle();
383
auto const total_width = bounding_rect.size.width.as_int();
384
auto const total_height = bounding_rect.size.height.as_int();
388
for (auto& info : session_info)
390
auto const x = (total_width*index)/sessions;
392
auto const dx = (total_width*index)/sessions - x;
394
auto const old_tile = info.second.tile;
395
Rectangle const new_tile{{x, 0}, {dx, total_height}};
397
update_surfaces(info.first, old_tile, new_tile);
399
info.second.tile = new_tile;
403
void me::TilingWindowManagerPolicy::update_surfaces(std::weak_ptr<ms::Session> const& session, Rectangle const& old_tile, Rectangle const& new_tile)
405
auto displacement = new_tile.top_left - old_tile.top_left;
406
auto& info = tools->info_for(session);
408
for (auto const& ps : info.surfaces)
410
if (auto const surface = ps.lock())
412
auto const old_pos = surface->top_left();
413
surface->move_to(old_pos + displacement);
415
fit_to_new_tile(*surface, old_tile, new_tile);
420
void me::TilingWindowManagerPolicy::clip_to_tile(ms::SurfaceCreationParameters& parameters, Rectangle const& tile)
422
auto const displacement = parameters.top_left - tile.top_left;
424
auto width = std::min(tile.size.width.as_int()-displacement.dx.as_int(), parameters.size.width.as_int());
425
auto height = std::min(tile.size.height.as_int()-displacement.dy.as_int(), parameters.size.height.as_int());
427
parameters.size = Size{width, height};
430
void me::TilingWindowManagerPolicy::fit_to_new_tile(ms::Surface& surface, Rectangle const& old_tile, Rectangle const& new_tile)
432
auto const displacement = surface.top_left() - new_tile.top_left;
434
// For now just scale if was filling width/height of tile
435
auto const old_size = surface.size();
436
auto const scaled_width = old_size.width == old_tile.size.width ? new_tile.size.width : old_size.width;
437
auto const scaled_height = old_size.height == old_tile.size.height ? new_tile.size.height : old_size.height;
439
auto width = std::min(new_tile.size.width.as_int()-displacement.dx.as_int(), scaled_width.as_int());
440
auto height = std::min(new_tile.size.height.as_int()-displacement.dy.as_int(), scaled_height.as_int());
442
surface.resize({width, height});
445
bool me::TilingWindowManagerPolicy::drag(std::shared_ptr<ms::Surface> surface, Point to, Point from, Rectangle bounds)
447
if (surface && surface->input_area_contains(from))
449
auto const top_left = surface->top_left();
450
auto const surface_size = surface->size();
451
auto const bottom_right = top_left + as_displacement(surface_size);
453
auto movement = to - from;
455
if (movement.dx < DeltaX{0})
456
movement.dx = std::max(movement.dx, (bounds.top_left - top_left).dx);
458
if (movement.dy < DeltaY{0})
459
movement.dy = std::max(movement.dy, (bounds.top_left - top_left).dy);
461
if (movement.dx > DeltaX{0})
462
movement.dx = std::min(movement.dx, (bounds.bottom_right() - bottom_right).dx);
464
if (movement.dy > DeltaY{0})
465
movement.dy = std::min(movement.dy, (bounds.bottom_right() - bottom_right).dy);
467
auto new_pos = surface->top_left() + movement;
469
surface->move_to(new_pos);
476
bool me::TilingWindowManagerPolicy::resize(std::shared_ptr<ms::Surface> surface, Point cursor, Point old_cursor, Rectangle bounds)
478
if (surface && surface->input_area_contains(old_cursor))
480
auto const top_left = surface->top_left();
482
auto const old_displacement = old_cursor - top_left;
483
auto const new_displacement = cursor - top_left;
485
auto const scale_x = new_displacement.dx.as_float()/std::max(1.0f, old_displacement.dx.as_float());
486
auto const scale_y = new_displacement.dy.as_float()/std::max(1.0f, old_displacement.dy.as_float());
488
if (scale_x <= 0.0f || scale_y <= 0.0f) return false;
490
auto const old_size = surface->size();
491
Size new_size{scale_x*old_size.width, scale_y*old_size.height};
493
auto const size_limits = as_size(bounds.bottom_right() - top_left);
495
if (new_size.width > size_limits.width)
496
new_size.width = size_limits.width;
498
if (new_size.height > size_limits.height)
499
new_size.height = size_limits.height;
501
surface->resize(new_size);