Unity 8
TopLevelWindowModel.cpp
1 /*
2  * Copyright (C) 2016-2017 Canonical, Ltd.
3  * Copyright 2019 UBports Foundation
4  *
5  * This program is free software: you can redistribute it and/or modify it under
6  * the terms of the GNU Lesser General Public License version 3, as published by
7  * the Free Software Foundation.
8  *
9  * This program is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
11  * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public License
15  * along with this program. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "TopLevelWindowModel.h"
19 #include "WindowManagerObjects.h"
20 
21 // unity-api
22 #include <unity/shell/application/ApplicationInfoInterface.h>
23 #include <unity/shell/application/ApplicationManagerInterface.h>
24 #include <unity/shell/application/MirSurfaceInterface.h>
25 #include <unity/shell/application/MirSurfaceListInterface.h>
26 #include <unity/shell/application/SurfaceManagerInterface.h>
27 
28 // Qt
29 #include <QDebug>
30 
31 // local
32 #include "Window.h"
33 #include "Workspace.h"
34 #include "InputMethodManager.h"
35 
36 Q_LOGGING_CATEGORY(TOPLEVELWINDOWMODEL, "toplevelwindowmodel", QtInfoMsg)
37 
38 #define DEBUG_MSG qCDebug(TOPLEVELWINDOWMODEL).nospace().noquote() << __func__
39 #define INFO_MSG qCInfo(TOPLEVELWINDOWMODEL).nospace().noquote() << __func__
40 
42 
43 TopLevelWindowModel::TopLevelWindowModel(Workspace* workspace)
44  : m_nullWindow(createWindow(nullptr)),
45  m_workspace(workspace),
46  m_surfaceManagerBusy(false)
47 {
48  connect(WindowManagerObjects::instance(), &WindowManagerObjects::applicationManagerChanged,
49  this, &TopLevelWindowModel::setApplicationManager);
50  connect(WindowManagerObjects::instance(), &WindowManagerObjects::surfaceManagerChanged,
51  this, &TopLevelWindowModel::setSurfaceManager);
52 
53  setApplicationManager(WindowManagerObjects::instance()->applicationManager());
54  setSurfaceManager(WindowManagerObjects::instance()->surfaceManager());
55 
56  connect(m_nullWindow, &Window::focusedChanged, this, [this] {
57  Q_EMIT rootFocusChanged();
58  });
59 }
60 
61 TopLevelWindowModel::~TopLevelWindowModel()
62 {
63 }
64 
65 void TopLevelWindowModel::setApplicationManager(unityapi::ApplicationManagerInterface* value)
66 {
67  if (m_applicationManager == value) {
68  return;
69  }
70 
71  DEBUG_MSG << "(" << value << ")";
72 
73  Q_ASSERT(m_modelState == IdleState);
74  m_modelState = ResettingState;
75 
76  beginResetModel();
77 
78  if (m_applicationManager) {
79  disconnect(m_applicationManager, 0, this, 0);
80  }
81 
82  m_applicationManager = value;
83 
84  if (m_applicationManager) {
85  connect(m_applicationManager, &QAbstractItemModel::rowsInserted,
86  this, [this](const QModelIndex &/*parent*/, int first, int last) {
87  if (!m_workspace || !m_workspace->isActive())
88  return;
89 
90  for (int i = first; i <= last; ++i) {
91  auto application = m_applicationManager->get(i);
92  addApplication(application);
93  }
94  });
95 
96  connect(m_applicationManager, &QAbstractItemModel::rowsAboutToBeRemoved,
97  this, [this](const QModelIndex &/*parent*/, int first, int last) {
98  for (int i = first; i <= last; ++i) {
99  auto application = m_applicationManager->get(i);
100  removeApplication(application);
101  }
102  });
103  }
104 
105  refreshWindows();
106 
107  endResetModel();
108  m_modelState = IdleState;
109 }
110 
111 void TopLevelWindowModel::setSurfaceManager(unityapi::SurfaceManagerInterface *surfaceManager)
112 {
113  if (surfaceManager == m_surfaceManager) {
114  return;
115  }
116 
117  DEBUG_MSG << "(" << surfaceManager << ")";
118 
119  Q_ASSERT(m_modelState == IdleState);
120  m_modelState = ResettingState;
121 
122  beginResetModel();
123 
124  if (m_surfaceManager) {
125  disconnect(m_surfaceManager, 0, this, 0);
126  }
127 
128  m_surfaceManager = surfaceManager;
129 
130  if (m_surfaceManager) {
131  connect(m_surfaceManager, &unityapi::SurfaceManagerInterface::surfacesAddedToWorkspace, this, &TopLevelWindowModel::onSurfacesAddedToWorkspace);
132  connect(m_surfaceManager, &unityapi::SurfaceManagerInterface::surfacesRaised, this, &TopLevelWindowModel::onSurfacesRaised);
133  connect(m_surfaceManager, &unityapi::SurfaceManagerInterface::modificationsStarted, this, &TopLevelWindowModel::onModificationsStarted);
134  connect(m_surfaceManager, &unityapi::SurfaceManagerInterface::modificationsEnded, this, &TopLevelWindowModel::onModificationsEnded);
135  }
136 
137  refreshWindows();
138 
139  endResetModel();
140  m_modelState = IdleState;
141 }
142 
143 void TopLevelWindowModel::addApplication(unityapi::ApplicationInfoInterface *application)
144 {
145  DEBUG_MSG << "(" << application->appId() << ")";
146 
147  if (application->state() != unityapi::ApplicationInfoInterface::Stopped && application->surfaceList()->count() == 0) {
148  prependPlaceholder(application);
149  }
150 }
151 
152 void TopLevelWindowModel::removeApplication(unityapi::ApplicationInfoInterface *application)
153 {
154  DEBUG_MSG << "(" << application->appId() << ")";
155 
156  Q_ASSERT(m_modelState == IdleState);
157 
158  int i = 0;
159  while (i < m_windowModel.count()) {
160  if (m_windowModel.at(i).application == application) {
161  deleteAt(i);
162  } else {
163  ++i;
164  }
165  }
166 }
167 
168 void TopLevelWindowModel::prependPlaceholder(unityapi::ApplicationInfoInterface *application)
169 {
170  INFO_MSG << "(" << application->appId() << ")";
171 
172  prependSurfaceHelper(nullptr, application);
173 }
174 
175 void TopLevelWindowModel::prependSurface(unityapi::MirSurfaceInterface *surface, unityapi::ApplicationInfoInterface *application)
176 {
177  Q_ASSERT(surface != nullptr);
178 
179  connectSurface(surface);
180  m_allSurfaces.insert(surface);
181 
182  bool filledPlaceholder = false;
183  for (int i = 0; i < m_windowModel.count() && !filledPlaceholder; ++i) {
184  ModelEntry &entry = m_windowModel[i];
185  if (entry.application == application && entry.window->surface() == nullptr) {
186  entry.window->setSurface(surface);
187  INFO_MSG << " appId=" << application->appId() << " surface=" << surface
188  << ", filling out placeholder. after: " << toString();
189  filledPlaceholder = true;
190  }
191  }
192 
193  if (!filledPlaceholder) {
194  INFO_MSG << " appId=" << application->appId() << " surface=" << surface << ", adding new row";
195  prependSurfaceHelper(surface, application);
196  }
197 }
198 
199 void TopLevelWindowModel::prependSurfaceHelper(unityapi::MirSurfaceInterface *surface, unityapi::ApplicationInfoInterface *application)
200 {
201 
202  Window *window = createWindow(surface);
203 
204  connect(window, &Window::stateChanged, this, [=](Mir::State newState) {
205  if (newState == Mir::HiddenState) {
206  // Comply, removing it from our model. Just as if it didn't exist anymore.
207  removeAt(indexForId(window->id()));
208  } else {
209  if (indexForId(window->id()) == -1) {
210  // was probably hidden before. put it back on the list
211  auto *application = m_applicationManager->findApplicationWithSurface(window->surface());
212  Q_ASSERT(application);
213  prependWindow(window, application);
214  }
215  }
216  });
217 
218  prependWindow(window, application);
219 
220  // Activate the newly-prepended window.
221  window->activate();
222 
223  INFO_MSG << " after " << toString();
224 }
225 
226 void TopLevelWindowModel::prependWindow(Window *window, unityapi::ApplicationInfoInterface *application)
227 {
228  if (m_modelState == IdleState) {
229  m_modelState = InsertingState;
230  beginInsertRows(QModelIndex(), 0 /*first*/, 0 /*last*/);
231  } else {
232  Q_ASSERT(m_modelState == ResettingState);
233  // No point in signaling anything if we're resetting the whole model
234  }
235 
236  m_windowModel.prepend(ModelEntry(window, application));
237 
238  if (m_modelState == InsertingState) {
239  endInsertRows();
240  Q_EMIT countChanged();
241  Q_EMIT listChanged();
242  m_modelState = IdleState;
243  }
244 }
245 
246 void TopLevelWindowModel::connectWindow(Window *window)
247 {
248  connect(window, &Window::focusRequested, this, [this, window]() {
249  if (!window->surface()) {
250  activateEmptyWindow(window);
251  }
252  });
253 
254  connect(window, &Window::focusedChanged, this, [this, window](bool focused) {
255  if (window->surface()) {
256  if (focused) {
257  setFocusedWindow(window);
258  m_focusedWindowCleared = false;
259  } else if (m_focusedWindow == window) {
260  // Condense changes to the focused window
261  // eg: Do focusedWindow=A to focusedWindow=B instead of
262  // focusedWindow=A to focusedWindow=null to focusedWindow=B
263  m_focusedWindowCleared = true;
264  } else {
265  // don't clear the focused window if you were not there in the first place
266  // happens when a filled window gets replaced with an empty one (no surface) as the focused window.
267  }
268  }
269  });
270 
271  connect(window, &Window::closeRequested, this, [this, window]() {
272  if (!window->surface()) {
273  // do things ourselves as miral doesn't know about this window
274  int id = window->id();
275  int index = indexForId(id);
276  bool focusOther = false;
277  Q_ASSERT(index >= 0);
278  if (window->focused()) {
279  focusOther = true;
280  }
281  m_windowModel[index].application->close();
282  if (focusOther) {
283  activateTopMostWindowWithoutId(id);
284  }
285  }
286  });
287 
288  connect(window, &Window::emptyWindowActivated, this, [this, window]() {
289  activateEmptyWindow(window);
290  });
291 
292  connect(window, &Window::liveChanged, this, [this, window](bool isAlive) {
293  if (!isAlive && window->state() == Mir::HiddenState) {
294  // Hidden windows are not in the model. So just delete it right away.
295  delete window;
296  }
297  });
298 }
299 
300 void TopLevelWindowModel::activateEmptyWindow(Window *window)
301 {
302  Q_ASSERT(!window->surface());
303  DEBUG_MSG << "(" << window << ")";
304 
305  // miral doesn't know about empty windows (ie, windows that are not backed up by MirSurfaces)
306  // So we have to activate them ourselves (instead of asking SurfaceManager to do it for us).
307 
308  window->setFocused(true);
309  raiseId(window->id());
310  Window *previousWindow = m_focusedWindow;
311  setFocusedWindow(window);
312  if (previousWindow && previousWindow->surface() && previousWindow->surface()->focused()) {
313  m_surfaceManager->activate(nullptr);
314  }
315 }
316 
317 void TopLevelWindowModel::connectSurface(unityapi::MirSurfaceInterface *surface)
318 {
319  connect(surface, &unityapi::MirSurfaceInterface::liveChanged, this, [this, surface](bool live){
320  if (!live) {
321  onSurfaceDied(surface);
322  }
323  });
324  connect(surface, &QObject::destroyed, this, [this, surface](){ this->onSurfaceDestroyed(surface); });
325 }
326 
327 void TopLevelWindowModel::onSurfaceDied(unityapi::MirSurfaceInterface *surface)
328 {
329  if (surface->type() == Mir::InputMethodType) {
330  removeInputMethodWindow();
331  return;
332  }
333 
334  int i = indexOf(surface);
335  if (i == -1) {
336  return;
337  }
338 
339  auto application = m_windowModel[i].application;
340 
341  // can't be starting if it already has a surface
342  Q_ASSERT(application->state() != unityapi::ApplicationInfoInterface::Starting);
343 
344  if (application->state() == unityapi::ApplicationInfoInterface::Running) {
345  m_windowModel[i].removeOnceSurfaceDestroyed = true;
346  } else {
347  // assume it got killed by the out-of-memory daemon.
348  //
349  // So leave entry in the model and only remove its surface, so shell can display a screenshot
350  // in its place.
351  m_windowModel[i].removeOnceSurfaceDestroyed = false;
352  }
353 }
354 
355 void TopLevelWindowModel::onSurfaceDestroyed(unityapi::MirSurfaceInterface *surface)
356 {
357  int i = indexOf(surface);
358  if (i == -1) {
359  return;
360  }
361 
362  if (m_windowModel[i].removeOnceSurfaceDestroyed) {
363  deleteAt(i);
364  } else {
365  auto window = m_windowModel[i].window;
366  window->setSurface(nullptr);
367  window->setFocused(false);
368  m_allSurfaces.remove(surface);
369  INFO_MSG << " Removed surface from entry. After: " << toString();
370  }
371 }
372 
373 Window *TopLevelWindowModel::createWindow(unityapi::MirSurfaceInterface *surface)
374 {
375  int id = m_nextId.fetchAndAddAcquire(1);
376  return createWindowWithId(surface, id);
377 }
378 
379 Window *TopLevelWindowModel::createNullWindow()
380 {
381  return createWindowWithId(nullptr, 0);
382 }
383 
384 Window *TopLevelWindowModel::createWindowWithId(unityapi::MirSurfaceInterface *surface, int id)
385 {
386  Window *qmlWindow = new Window(id, this);
387  connectWindow(qmlWindow);
388  if (surface) {
389  qmlWindow->setSurface(surface);
390  }
391  return qmlWindow;
392 }
393 
394 void TopLevelWindowModel::onSurfacesAddedToWorkspace(const std::shared_ptr<miral::Workspace>& workspace,
395  const QVector<unity::shell::application::MirSurfaceInterface*> surfaces)
396 {
397  if (!m_workspace || !m_applicationManager) return;
398  if (workspace != m_workspace->workspace()) {
399  removeSurfaces(surfaces);
400  return;
401  }
402 
403  Q_FOREACH(auto surface, surfaces) {
404  if (m_allSurfaces.contains(surface)) continue;
405 
406  if (surface->parentSurface()) {
407  // Wrap it in a Window so that we keep focusedWindow() up to date.
408  Window *window = createWindow(surface);
409  connect(surface, &QObject::destroyed, window, [=](){
410  window->setSurface(nullptr);
411  window->deleteLater();
412  });
413  } else {
414  if (surface->type() == Mir::InputMethodType) {
415  connectSurface(surface);
416  setInputMethodWindow(createWindow(surface));
417  } else {
418  auto *application = m_applicationManager->findApplicationWithSurface(surface);
419  if (application) {
420  if (surface->state() == Mir::HiddenState) {
421  // Ignore it until it's finally shown
422  connect(surface, &unityapi::MirSurfaceInterface::stateChanged, this, [=](Mir::State newState) {
423  Q_ASSERT(newState != Mir::HiddenState);
424  disconnect(surface, &unityapi::MirSurfaceInterface::stateChanged, this, 0);
425  prependSurface(surface, application);
426  });
427  } else {
428  prependSurface(surface, application);
429  }
430  } else {
431  // Must be a prompt session. No need to do add it as a prompt surface is not top-level.
432  // It will show up in the ApplicationInfoInterface::promptSurfaceList of some application.
433  // Still wrap it in a Window though, so that we keep focusedWindow() up to date.
434  Window *promptWindow = createWindow(surface);
435  connect(surface, &QObject::destroyed, promptWindow, [=](){
436  promptWindow->setSurface(nullptr);
437  promptWindow->deleteLater();
438  });
439  }
440  }
441  }
442  }
443 }
444 
445 void TopLevelWindowModel::removeSurfaces(const QVector<unity::shell::application::MirSurfaceInterface *> surfaces)
446 {
447  int start = -1;
448  int end = -1;
449  for (auto iter = surfaces.constBegin(); iter != surfaces.constEnd();) {
450  auto surface = *iter;
451  iter++;
452 
453  // Do removals in adjacent blocks.
454  start = end = indexOf(surface);
455  if (start == -1) {
456  // could be a child surface
457  m_allSurfaces.remove(surface);
458  continue;
459  }
460  while(iter != surfaces.constEnd()) {
461  int index = indexOf(*iter);
462  if (index != end+1) {
463  break;
464  }
465  end++;
466  iter++;
467  }
468 
469  if (m_modelState == IdleState) {
470  beginRemoveRows(QModelIndex(), start, end);
471  m_modelState = RemovingState;
472  } else {
473  Q_ASSERT(m_modelState == ResettingState);
474  // No point in signaling anything if we're resetting the whole model
475  }
476 
477  for (int index = start; index <= end; index++) {
478  auto window = m_windowModel[start].window;
479  window->setSurface(nullptr);
480  window->setFocused(false);
481 
482  if (window == m_previousWindow) {
483  m_previousWindow = nullptr;
484  }
485 
486  m_windowModel.removeAt(start);
487  m_allSurfaces.remove(surface);
488  }
489 
490  if (m_modelState == RemovingState) {
491  endRemoveRows();
492  Q_EMIT countChanged();
493  Q_EMIT listChanged();
494  m_modelState = IdleState;
495  }
496  }
497 }
498 
499 void TopLevelWindowModel::deleteAt(int index)
500 {
501  auto window = m_windowModel[index].window;
502 
503  removeAt(index);
504 
505  window->setSurface(nullptr);
506 
507  delete window;
508 }
509 
510 void TopLevelWindowModel::removeAt(int index)
511 {
512  if (m_modelState == IdleState) {
513  beginRemoveRows(QModelIndex(), index, index);
514  m_modelState = RemovingState;
515  } else {
516  Q_ASSERT(m_modelState == ResettingState);
517  // No point in signaling anything if we're resetting the whole model
518  }
519 
520  auto window = m_windowModel[index].window;
521  auto surface = window->surface();
522 
523  if (!window->surface()) {
524  window->setFocused(false);
525  }
526 
527  if (window == m_previousWindow) {
528  m_previousWindow = nullptr;
529  }
530 
531  m_windowModel.removeAt(index);
532  m_allSurfaces.remove(surface);
533 
534  if (m_modelState == RemovingState) {
535  endRemoveRows();
536  Q_EMIT countChanged();
537  Q_EMIT listChanged();
538  m_modelState = IdleState;
539  }
540 
541  if (m_focusedWindow == window) {
542  setFocusedWindow(nullptr);
543  m_focusedWindowCleared = false;
544  }
545 
546  if (m_previousWindow == window) {
547  m_previousWindow = nullptr;
548  }
549 
550  if (m_closingAllApps) {
551  if (m_windowModel.isEmpty()) {
552  Q_EMIT closedAllWindows();
553  }
554  }
555 
556  INFO_MSG << " after " << toString() << " apps left " << m_windowModel.count();
557 }
558 
559 void TopLevelWindowModel::setInputMethodWindow(Window *window)
560 {
561  if (m_inputMethodWindow) {
562  qWarning("Multiple Input Method Surfaces created, removing the old one!");
563  delete m_inputMethodWindow;
564  }
565  m_inputMethodWindow = window;
566  Q_EMIT inputMethodSurfaceChanged(m_inputMethodWindow->surface());
567  InputMethodManager::instance()->setWindow(window);
568 }
569 
570 void TopLevelWindowModel::removeInputMethodWindow()
571 {
572  if (m_inputMethodWindow) {
573  auto surface = m_inputMethodWindow->surface();
574  if (surface) {
575  m_allSurfaces.remove(surface);
576  }
577  if (m_focusedWindow == m_inputMethodWindow) {
578  setFocusedWindow(nullptr);
579  m_focusedWindowCleared = false;
580  }
581 
582  delete m_inputMethodWindow;
583  m_inputMethodWindow = nullptr;
584  Q_EMIT inputMethodSurfaceChanged(nullptr);
585  InputMethodManager::instance()->setWindow(nullptr);
586  }
587 }
588 
589 void TopLevelWindowModel::onSurfacesRaised(const QVector<unityapi::MirSurfaceInterface*> &surfaces)
590 {
591  DEBUG_MSG << "(" << surfaces << ")";
592  const int raiseCount = surfaces.size();
593  for (int i = 0; i < raiseCount; i++) {
594  int fromIndex = indexOf(surfaces[i]);
595  if (fromIndex != -1) {
596  move(fromIndex, 0);
597  }
598  }
599 }
600 
601 int TopLevelWindowModel::rowCount(const QModelIndex &/*parent*/) const
602 {
603  return m_windowModel.count();
604 }
605 
606 QVariant TopLevelWindowModel::data(const QModelIndex& index, int role) const
607 {
608  if (index.row() < 0 || index.row() >= m_windowModel.size())
609  return QVariant();
610 
611  if (role == WindowRole) {
612  Window *window = m_windowModel.at(index.row()).window;
613  return QVariant::fromValue(window);
614  } else if (role == ApplicationRole) {
615  return QVariant::fromValue(m_windowModel.at(index.row()).application);
616  } else {
617  return QVariant();
618  }
619 }
620 
621 QString TopLevelWindowModel::toString()
622 {
623  QString str;
624  for (int i = 0; i < m_windowModel.count(); ++i) {
625  auto item = m_windowModel.at(i);
626 
627  QString itemStr = QString("(index=%1,appId=%2,surface=0x%3,id=%4)")
628  .arg(QString::number(i),
629  item.application->appId(),
630  QString::number((qintptr)item.window->surface(), 16),
631  QString::number(item.window->id()));
632 
633  if (i > 0) {
634  str.append(",");
635  }
636  str.append(itemStr);
637  }
638  return str;
639 }
640 
641 int TopLevelWindowModel::indexOf(unityapi::MirSurfaceInterface *surface)
642 {
643  for (int i = 0; i < m_windowModel.count(); ++i) {
644  if (m_windowModel.at(i).window->surface() == surface) {
645  return i;
646  }
647  }
648  return -1;
649 }
650 
652 {
653  for (int i = 0; i < m_windowModel.count(); ++i) {
654  if (m_windowModel[i].window->id() == id) {
655  return i;
656  }
657  }
658  return -1;
659 }
660 
662 {
663  if (index >=0 && index < m_windowModel.count()) {
664  return m_windowModel[index].window;
665  } else {
666  return nullptr;
667  }
668 }
669 
670 unityapi::MirSurfaceInterface *TopLevelWindowModel::surfaceAt(int index) const
671 {
672  if (index >=0 && index < m_windowModel.count()) {
673  return m_windowModel[index].window->surface();
674  } else {
675  return nullptr;
676  }
677 }
678 
679 unityapi::ApplicationInfoInterface *TopLevelWindowModel::applicationAt(int index) const
680 {
681  if (index >=0 && index < m_windowModel.count()) {
682  return m_windowModel[index].application;
683  } else {
684  return nullptr;
685  }
686 }
687 
688 int TopLevelWindowModel::idAt(int index) const
689 {
690  if (index >=0 && index < m_windowModel.count()) {
691  return m_windowModel[index].window->id();
692  } else {
693  return 0;
694  }
695 }
696 
698 {
699  if (m_modelState == IdleState) {
700  DEBUG_MSG << "(id=" << id << ") - do it now.";
701  doRaiseId(id);
702  } else {
703  DEBUG_MSG << "(id=" << id << ") - Model busy (modelState=" << m_modelState << "). Try again in the next event loop.";
704  // The model has just signalled some change. If we have a Repeater responding to this update, it will get nuts
705  // if we perform yet another model change straight away.
706  //
707  // A bad sympton of this problem is a Repeater.itemAt(index) call returning null event though Repeater.count says
708  // the index is definitely within bounds.
709  QMetaObject::invokeMethod(this, "raiseId", Qt::QueuedConnection, Q_ARG(int, id));
710  }
711 }
712 
713 void TopLevelWindowModel::doRaiseId(int id)
714 {
715  int fromIndex = indexForId(id);
716  // can't raise something that doesn't exist or that it's already on top
717  if (fromIndex != -1 && fromIndex != 0) {
718  auto surface = m_windowModel[fromIndex].window->surface();
719  if (surface) {
720  m_surfaceManager->raise(surface);
721  } else {
722  // move it ourselves. Since there's no mir::scene::Surface/miral::Window, there's nothing
723  // miral can do about it.
724  move(fromIndex, 0);
725  }
726  }
727 }
728 
729 void TopLevelWindowModel::setFocusedWindow(Window *window)
730 {
731  if (window != m_focusedWindow) {
732  INFO_MSG << "(" << window << ")";
733 
734  m_previousWindow = m_focusedWindow;
735 
736  m_focusedWindow = window;
737  Q_EMIT focusedWindowChanged(m_focusedWindow);
738 
739  if (m_previousWindow && m_previousWindow->focused() && !m_previousWindow->surface()) {
740  // do it ourselves. miral doesn't know about this window
741  m_previousWindow->setFocused(false);
742  }
743  }
744 
745  // Reset
746  m_pendingActivation = false;
747 }
748 
749 unityapi::MirSurfaceInterface* TopLevelWindowModel::inputMethodSurface() const
750 {
751  return m_inputMethodWindow ? m_inputMethodWindow->surface() : nullptr;
752 }
753 
755 {
756  return m_focusedWindow;
757 }
758 
759 void TopLevelWindowModel::move(int from, int to)
760 {
761  if (from == to) return;
762  DEBUG_MSG << " from=" << from << " to=" << to;
763 
764  if (from >= 0 && from < m_windowModel.size() && to >= 0 && to < m_windowModel.size()) {
765  QModelIndex parent;
766  /* When moving an item down, the destination index needs to be incremented
767  by one, as explained in the documentation:
768  http://qt-project.org/doc/qt-5.0/qtcore/qabstractitemmodel.html#beginMoveRows */
769 
770  Q_ASSERT(m_modelState == IdleState);
771  m_modelState = MovingState;
772 
773  beginMoveRows(parent, from, from, parent, to + (to > from ? 1 : 0));
774 #if QT_VERSION < QT_VERSION_CHECK(5, 6, 0)
775  const auto &window = m_windowModel.takeAt(from);
776  m_windowModel.insert(to, window);
777 #else
778  m_windowModel.move(from, to);
779 #endif
780  endMoveRows();
781 
782  Q_EMIT listChanged();
783  m_modelState = IdleState;
784 
785  INFO_MSG << " after " << toString();
786  }
787 }
788 void TopLevelWindowModel::onModificationsStarted()
789 {
790  m_surfaceManagerBusy = true;
791 }
792 
793 void TopLevelWindowModel::onModificationsEnded()
794 {
795  if (m_focusedWindowCleared) {
796  setFocusedWindow(nullptr);
797  }
798  // reset
799  m_focusedWindowCleared = false;
800  m_surfaceManagerBusy = false;
801 }
802 
803 void TopLevelWindowModel::activateTopMostWindowWithoutId(int forbiddenId)
804 {
805  DEBUG_MSG << "(" << forbiddenId << ")";
806 
807  for (int i = 0; i < m_windowModel.count(); ++i) {
808  Window *window = m_windowModel[i].window;
809  if (window->id() != forbiddenId) {
810  window->activate();
811  break;
812  }
813  }
814 }
815 
816 void TopLevelWindowModel::refreshWindows()
817 {
818  DEBUG_MSG << "()";
819  clear();
820 
821  if (!m_workspace || !m_applicationManager || !m_surfaceManager) return;
822 
823  m_surfaceManager->forEachSurfaceInWorkspace(m_workspace->workspace(), [this](unity::shell::application::MirSurfaceInterface* surface) {
824  if (surface->parentSurface()) {
825  // Wrap it in a Window so that we keep focusedWindow() up to date.
826  Window *window = createWindow(surface);
827  connect(surface, &QObject::destroyed, window, [=](){
828  window->setSurface(nullptr);
829  window->deleteLater();
830  });
831  } else {
832  if (surface->type() == Mir::InputMethodType) {
833  setInputMethodWindow(createWindow(surface));
834  } else {
835  auto *application = m_applicationManager->findApplicationWithSurface(surface);
836  if (application) {
837  prependSurface(surface, application);
838  } else {
839  // Must be a prompt session. No need to do add it as a prompt surface is not top-level.
840  // It will show up in the ApplicationInfoInterface::promptSurfaceList of some application.
841  // Still wrap it in a Window though, so that we keep focusedWindow() up to date.
842  Window *promptWindow = createWindow(surface);
843  connect(surface, &QObject::destroyed, promptWindow, [=](){
844  promptWindow->setSurface(nullptr);
845  promptWindow->deleteLater();
846  });
847  }
848  }
849  }
850  });
851 }
852 
853 void TopLevelWindowModel::clear()
854 {
855  DEBUG_MSG << "()";
856 
857  while(m_windowModel.count() > 0) {
858  ModelEntry entry = m_windowModel.takeAt(0);
859  disconnect(entry.window, 0, this, 0);
860  delete entry.window;
861  }
862  m_allSurfaces.clear();
863  setFocusedWindow(nullptr);
864  m_focusedWindowCleared = false;
865  m_previousWindow = nullptr;
866 }
867 
869 {
870  m_closingAllApps = true;
871  for (auto win : m_windowModel) {
872  win.window->close();
873  }
874 
875  // This is done after the for loop in the unlikely event that
876  // an app starts in between this
877  if (m_windowModel.isEmpty()) {
878  Q_EMIT closedAllWindows();
879  }
880 }
881 
883 {
884  return !m_nullWindow->focused();
885 }
886 
887 void TopLevelWindowModel::setRootFocus(bool focus)
888 {
889  INFO_MSG << "(" << focus << "), surfaceManagerBusy is " << m_surfaceManagerBusy;
890 
891  if (m_surfaceManagerBusy) {
892  // Something else is probably being focused already, let's not add to
893  // the noise.
894  return;
895  }
896 
897  if (focus) {
898  // Give focus back to previous focused window, only if null window is focused.
899  // If null window is not focused, a different app had taken the focus and we
900  // should repect that, or if a pendingActivation is going on.
901  if (m_previousWindow && !m_previousWindow->focused() && !m_pendingActivation &&
902  m_nullWindow == m_focusedWindow && m_previousWindow != m_nullWindow) {
903  m_previousWindow->activate();
904  } else if (!m_pendingActivation) {
905  // The previous window does not exist any more, focus top window.
906  activateTopMostWindowWithoutId(-1);
907  }
908  } else {
909  if (!m_nullWindow->focused()) {
910  m_nullWindow->activate();
911  }
912  }
913 }
914 
915 // Pending Activation will block refocus of previous focused window
916 // this is needed since surface activation with miral is async,
917 // and activation of placeholder is sync. This causes a race condition
918 // between placeholder and prev window. This results in prev window
919 // gets focused, as it will always be later than the placeholder.
921 {
922  m_pendingActivation = true;
923 }
bool rootFocus
Sets whether a user Window or "nothing" should be focused.
Q_INVOKABLE unity::shell::application::MirSurfaceInterface * surfaceAt(int index) const
Returns the surface at the given index.
Q_INVOKABLE Window * windowAt(int index) const
Returns the window at the given index.
A slightly higher concept than MirSurface.
Definition: Window.h:47
void focusRequested()
Emitted when focus for this window is requested by an external party.
Mir::State state
State of the surface.
Definition: Window.h:64
Q_INVOKABLE int idAt(int index) const
Returns the unique id of the element at the given index.
Q_INVOKABLE unity::shell::application::ApplicationInfoInterface * applicationAt(int index) const
Returns the application at the given index.
void activate()
Focuses and raises the window.
Definition: Window.cpp:137
unity::shell::application::MirSurfaceInterface surface
Surface backing up this window It might be null if a surface hasn&#39;t been created yet (application is ...
Definition: Window.h:92
bool focused
Whether the surface is focused.
Definition: Window.h:71
Q_INVOKABLE void pendingActivation()
Sets pending activation flag.
int id
A unique identifier for this window. Useful for telling windows apart in a list model as they get mov...
Definition: Window.h:84
unity::shell::application::MirSurfaceInterface inputMethodSurface
The input method surface, if any.
Q_INVOKABLE int indexForId(int id) const
Returns the index where the row with the given id is located.
Window focusedWindow
The currently focused window, if any.
Q_INVOKABLE void raiseId(int id)
Raises the row with the given id to the top of the window stack (index == count-1) ...
Q_INVOKABLE void closeAllWindows()
Closes all windows, emits closedAllWindows when done.