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