2
* Copyright (C) 2016 Canonical, Ltd.
4
* This program is free software: you can redistribute it and/or modify it under
5
* the terms of the GNU Lesser General Public License version 3, as published by
6
* the Free Software Foundation.
8
* This program is distributed in the hope that it will be useful, but WITHOUT
9
* ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10
* SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11
* Lesser General Public License for more details.
13
* You should have received a copy of the GNU Lesser General Public License
14
* along with this program. If not, see <http://www.gnu.org/licenses/>.
17
#include "windowmanagementpolicy.h"
19
#include "screensmodel.h"
20
#include "surfaceobserver.h"
22
#include "miral/window_manager_tools.h"
23
#include "miral/window_specification.h"
25
#include "mirqtconversion.h"
27
#include <mir/scene/surface.h>
31
std::shared_ptr<ExtraWindowInfo> getExtraInfo(const miral::WindowInfo &windowInfo) {
32
return std::static_pointer_cast<ExtraWindowInfo>(windowInfo.userdata());
36
using namespace qtmir;
38
WindowManagementPolicy::WindowManagementPolicy(const miral::WindowManagerTools &tools,
39
qtmir::WindowModelNotifier &windowModel,
40
qtmir::WindowController &windowController,
41
qtmir::AppNotifier &appNotifier,
42
const QSharedPointer<ScreensModel> screensModel)
43
: CanonicalWindowManagerPolicy(tools)
45
, m_windowModel(windowModel)
46
, m_appNotifier(appNotifier)
47
, m_eventFeeder(new QtEventFeeder(screensModel))
49
qRegisterMetaType<qtmir::NewWindow>();
50
qRegisterMetaType<std::vector<miral::Window>>();
51
qRegisterMetaType<miral::ApplicationInfo>();
52
windowController.setPolicy(this);
55
/* Following are hooks to allow custom policy be imposed */
56
miral::WindowSpecification WindowManagementPolicy::place_new_surface(
57
const miral::ApplicationInfo &app_info,
58
const miral::WindowSpecification &request_parameters)
60
auto parameters = CanonicalWindowManagerPolicy::place_new_surface(app_info, request_parameters);
62
parameters.userdata() = std::make_shared<ExtraWindowInfo>();
64
qDebug() << "Place surface" << parameters.top_left().value().x.as_int();
68
void WindowManagementPolicy::handle_window_ready(miral::WindowInfo &windowInfo)
70
qDebug("Window Ready");
71
CanonicalWindowManagerPolicy::handle_window_ready(windowInfo);
73
Q_EMIT m_windowModel.windowReady(windowInfo);
75
auto appInfo = m_tools.info_for(windowInfo.window().application());
76
Q_EMIT m_appNotifier.appCreatedWindow(appInfo);
79
void WindowManagementPolicy::handle_modify_window(
80
miral::WindowInfo &windowInfo,
81
const miral::WindowSpecification &modifications)
83
qDebug("Window Modified!");
85
// TODO this applies the default policy. Qt needs to process the request instead
86
CanonicalWindowManagerPolicy::handle_modify_window(windowInfo, modifications);
88
// TODO Once Qt processes the request we probably don't want to notify from here
89
std::shared_ptr<mir::scene::Surface> surface{windowInfo.window()};
90
if (SurfaceObserver *observer = SurfaceObserver::observerForSurface(surface.get())) {
91
observer->notifySurfaceModifications(modifications);
95
void WindowManagementPolicy::handle_raise_window(miral::WindowInfo &windowInfo)
97
qDebug("Window Raise");
98
CanonicalWindowManagerPolicy::handle_raise_window(windowInfo);
101
/* Handle input events - here just inject them into Qt event loop for later processing */
102
bool WindowManagementPolicy::handle_keyboard_event(const MirKeyboardEvent *event)
104
m_eventFeeder->dispatchKey(event);
108
bool WindowManagementPolicy::handle_touch_event(const MirTouchEvent *event)
110
m_eventFeeder->dispatchTouch(event);
114
bool WindowManagementPolicy::handle_pointer_event(const MirPointerEvent *event)
116
m_eventFeeder->dispatchPointer(event);
120
void WindowManagementPolicy::advise_new_window(const miral::WindowInfo &windowInfo)
122
// TODO: attach surface observer here
124
getExtraInfo(windowInfo)->persistentId = QString::fromStdString(m_tools.id_for_window(windowInfo.window()));
126
// FIXME: remove when possible
127
getExtraInfo(windowInfo)->state = toQtState(windowInfo.state());
129
Q_EMIT m_windowModel.windowAdded(NewWindow{windowInfo});
132
void WindowManagementPolicy::advise_delete_window(const miral::WindowInfo &windowInfo)
134
Q_EMIT m_windowModel.windowRemoved(windowInfo);
137
void WindowManagementPolicy::advise_raise(const std::vector<miral::Window> &windows)
139
Q_EMIT m_windowModel.windowsRaised(windows);
142
void WindowManagementPolicy::advise_new_app(miral::ApplicationInfo &application)
144
Q_EMIT m_appNotifier.appAdded(application);
147
void WindowManagementPolicy::advise_delete_app(const miral::ApplicationInfo &application)
149
Q_EMIT m_appNotifier.appRemoved(application);
152
void WindowManagementPolicy::advise_state_change(const miral::WindowInfo &windowInfo, MirSurfaceState state)
154
auto extraWinInfo = getExtraInfo(windowInfo);
156
// FIXME: Remove this mess once MirSurfaceState matches Mir::State
157
if (state == mir_surface_state_restored && extraWinInfo->state != Mir::RestoredState
158
&& toMirState(extraWinInfo->state) == state) {
159
// Ignore. That MirSurfaceState is just a placeholder for a Mir::State value that has no counterpart
160
// in MirSurfaceState.
162
extraWinInfo->state = toQtState(state);
165
Q_EMIT m_windowModel.windowStateChanged(windowInfo, extraWinInfo->state);
168
void WindowManagementPolicy::advise_move_to(const miral::WindowInfo &windowInfo, Point topLeft)
170
qDebug("Window Moved to (%d, %d)", topLeft.x.as_int(), topLeft.y.as_int());
171
Q_EMIT m_windowModel.windowMoved(windowInfo, toQPoint(topLeft));
174
void WindowManagementPolicy::advise_resize(const miral::WindowInfo &windowInfo, const Size &newSize)
176
qDebug("Window Resized to %dx%d", newSize.width.as_int(), newSize.height.as_int());
177
Q_EMIT m_windowModel.windowResized(windowInfo, toQSize(newSize));
180
void WindowManagementPolicy::advise_focus_lost(const miral::WindowInfo &windowInfo)
182
Q_EMIT m_windowModel.windowFocusChanged(windowInfo, false);
185
void WindowManagementPolicy::advise_focus_gained(const miral::WindowInfo &windowInfo)
187
// update Qt model ASAP, before applying Mir policy
188
Q_EMIT m_windowModel.windowFocusChanged(windowInfo, true);
190
CanonicalWindowManagerPolicy::advise_focus_gained(windowInfo);
193
void WindowManagementPolicy::advise_begin()
195
Q_EMIT m_windowModel.modificationsStarted();
198
void WindowManagementPolicy::advise_end()
200
Q_EMIT m_windowModel.modificationsEnded();
203
/* Following methods all called from the Qt GUI thread to deliver events to clients */
204
void WindowManagementPolicy::deliver_keyboard_event(const MirKeyboardEvent *event,
205
const miral::Window &window)
207
m_tools.invoke_under_lock([&window, this]() {
208
m_tools.select_active_window(window);
210
auto e = reinterpret_cast<MirEvent const*>(event); // naughty
212
if (auto surface = std::weak_ptr<mir::scene::Surface>(window).lock()) {
217
void WindowManagementPolicy::deliver_touch_event(const MirTouchEvent *event,
218
const miral::Window &window)
220
m_tools.invoke_under_lock([&window, this]() {
221
m_tools.select_active_window(window);
223
auto e = reinterpret_cast<MirEvent const*>(event); // naughty
225
if (auto surface = std::weak_ptr<mir::scene::Surface>(window).lock()) {
230
void WindowManagementPolicy::deliver_pointer_event(const MirPointerEvent *event,
231
const miral::Window &window)
233
// Prevent mouse hover events causing window focus to change
234
if (mir_pointer_event_action(event) == mir_pointer_action_button_down) {
235
m_tools.invoke_under_lock([&window, this]() {
236
m_tools.select_active_window(window);
239
auto e = reinterpret_cast<MirEvent const*>(event); // naughty
241
if (auto surface = std::weak_ptr<mir::scene::Surface>(window).lock()) {
246
/* Methods to allow Shell to request changes to the window stack. Called from the Qt GUI thread */
248
// raises the window tree and focus it.
249
void WindowManagementPolicy::activate(const miral::Window &window)
252
auto &windowInfo = m_tools.info_for(window);
254
// restore from minimized if needed
255
if (windowInfo.state() == mir_surface_state_minimized) {
256
auto extraInfo = getExtraInfo(windowInfo);
257
Q_ASSERT(extraInfo->previousState != Mir::MinimizedState);
258
requestState(window, extraInfo->previousState);
262
m_tools.invoke_under_lock([&]() {
263
m_tools.select_active_window(window);
267
// raises the window tree
268
void WindowManagementPolicy::raise(const miral::Window &window)
270
m_tools.invoke_under_lock([&window, this]() {
271
m_tools.raise_tree(window);
275
void WindowManagementPolicy::resize(const miral::Window &window, const Size size)
277
miral::WindowSpecification modifications;
278
modifications.size() = size;
279
m_tools.invoke_under_lock([&window, &modifications, this]() {
281
m_tools.modify_window(m_tools.info_for(window), modifications);
282
} catch (const std::out_of_range&) {
283
// usually shell trying to operate on a window which already closed, just ignore
284
// TODO: MirSurface extends the miral::Window lifetime by holding a shared pointer to
285
// the mir::scene::Surface, meaning it cannot detect when the window has been closed
286
// and thus avoid calling this method.
291
void WindowManagementPolicy::move(const miral::Window &window, const Point topLeft)
293
miral::WindowSpecification modifications;
294
modifications.top_left() = topLeft;
295
m_tools.invoke_under_lock([&window, &modifications, this]() {
297
m_tools.modify_window(m_tools.info_for(window), modifications);
298
} catch (const std::out_of_range&) {
299
// usually shell trying to operate on a window which already closed, just ignore
300
// TODO: see above comment in resize, same issue
305
void WindowManagementPolicy::ask_client_to_close(const miral::Window &window)
307
m_tools.invoke_under_lock([&window, this]() {
308
m_tools.ask_client_to_close(window);
312
void WindowManagementPolicy::forceClose(const miral::Window &window)
314
m_tools.invoke_under_lock([&window, this]() {
315
m_tools.force_close(window);
319
void WindowManagementPolicy::requestState(const miral::Window &window, const Mir::State state)
321
auto &windowInfo = m_tools.info_for(window);
322
auto extraWinInfo = getExtraInfo(windowInfo);
324
if (extraWinInfo->state == state)
327
miral::WindowSpecification modifications;
328
modifications.state() = toMirState(state);
330
// TODO: What if the window modification fails? Is that possible?
331
// Assuming here that the state will indeed change
332
extraWinInfo->previousState = extraWinInfo->state;
333
extraWinInfo->state = state;
335
if (modifications.state() == windowInfo.state()) {
336
Q_EMIT m_windowModel.windowStateChanged(windowInfo, state);
338
m_tools.invoke_under_lock([&]() {
339
m_tools.modify_window(windowInfo, modifications);