on bionic help sign in
unity8/plugins/Utils/windowstatestorage.cpp
221 lines 7.6 kB history raw
1/*
2 * Copyright 2015-2016 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
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 Lesser General Public License for more details.
12 *
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/>.
15 */
17#include "windowstatestorage.h"
19#include <QtConcurrent>
20#include <QDebug>
21#include <QFutureSynchronizer>
22#include <QSqlQuery>
23#include <QSqlError>
24#include <QSqlResult>
25#include <QRect>
26#include <unity/shell/application/ApplicationInfoInterface.h>
28QMutex WindowStateStorage::s_mutex;
30inline QString sanitiseString(QString string) {
31 return string.remove(QLatin1Char('\"'))
32 .remove(QLatin1Char('\''))
33 .remove(QLatin1Char('\\'));
36WindowStateStorage::WindowStateStorage(QObject *parent):
37 QObject(parent)
39 const QString dbPath = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + QStringLiteral("/unity8/");
40 m_db = QSqlDatabase::addDatabase(QStringLiteral("QSQLITE"));
41 QDir dir;
42 dir.mkpath(dbPath);
43 m_db.setDatabaseName(dbPath + "windowstatestorage.sqlite");
44 initdb();
47WindowStateStorage::~WindowStateStorage()
49 QFutureSynchronizer<void> futureSync;
50 for (int i = 0; i < m_asyncQueries.count(); ++i) {
51 futureSync.addFuture(m_asyncQueries[i]);
52 }
53 futureSync.waitForFinished();
54 m_db.close();
57void WindowStateStorage::saveState(const QString &windowId, WindowStateStorage::WindowState state)
59 const QString queryString = QStringLiteral("INSERT OR REPLACE INTO state (windowId, state) values ('%1', '%2');")
60 .arg(sanitiseString(windowId))
61 .arg((int)state);
63 saveValue(queryString);
66WindowStateStorage::WindowState WindowStateStorage::getState(const QString &windowId, WindowStateStorage::WindowState defaultValue) const
68 const QString queryString = QStringLiteral("SELECT state FROM state WHERE windowId = '%1';")
69 .arg(sanitiseString(windowId));
71 QSqlQuery query = getValue(queryString);
73 if (!query.first()) {
74 return defaultValue;
75 }
76 return (WindowState)query.value(QStringLiteral("state")).toInt();
79void WindowStateStorage::saveGeometry(const QString &windowId, const QRect &rect)
81 const QString queryString = QStringLiteral("INSERT OR REPLACE INTO geometry (windowId, x, y, width, height) values ('%1', '%2', '%3', '%4', '%5');")
82 .arg(sanitiseString(windowId))
83 .arg(rect.x())
84 .arg(rect.y())
85 .arg(rect.width())
86 .arg(rect.height());
88 saveValue(queryString);
91void WindowStateStorage::saveStage(const QString &appId, int stage)
93 const QString queryString = QStringLiteral("INSERT OR REPLACE INTO stage (appId, stage) values ('%1', '%2');")
94 .arg(sanitiseString(appId))
95 .arg(stage);
97 saveValue(queryString);
100int WindowStateStorage::getStage(const QString &appId, int defaultValue) const
102 const QString queryString = QStringLiteral("SELECT stage FROM stage WHERE appId = '%1';")
103 .arg(sanitiseString(appId));
105 QSqlQuery query = getValue(queryString);
107 if (!query.first()) {
108 return defaultValue;
109 }
110 return query.value("stage").toInt();
113void WindowStateStorage::executeAsyncQuery(const QString &queryString)
115 QMutexLocker l(&s_mutex);
116 QSqlQuery query;
118 bool ok = query.exec(queryString);
119 if (!ok) {
120 qWarning() << "Error executing query" << queryString
121 << "Driver error:" << query.lastError().driverText()
122 << "Database error:" << query.lastError().databaseText();
123 }
126QRect WindowStateStorage::getGeometry(const QString &windowId, const QRect &defaultValue) const
128 QString queryString = QStringLiteral("SELECT * FROM geometry WHERE windowId = '%1';")
129 .arg(sanitiseString(windowId));
131 QSqlQuery query = getValue(queryString);
133 if (!query.first()) {
134 return defaultValue;
135 }
137 const QRect result(query.value(QStringLiteral("x")).toInt(), query.value(QStringLiteral("y")).toInt(),
138 query.value(QStringLiteral("width")).toInt(), query.value(QStringLiteral("height")).toInt());
140 if (result.isValid()) {
141 return result;
142 }
144 return defaultValue;
147void WindowStateStorage::initdb()
149 m_db.open();
150 if (!m_db.open()) {
151 qWarning() << "Error opening state database:" << m_db.lastError().driverText() << m_db.lastError().databaseText();
152 return;
153 }
155 if (!m_db.tables().contains(QStringLiteral("geometry"))) {
156 QSqlQuery query;
157 query.exec(QStringLiteral("CREATE TABLE geometry(windowId TEXT UNIQUE, x INTEGER, y INTEGER, width INTEGER, height INTEGER);"));
158 }
160 if (!m_db.tables().contains(QStringLiteral("state"))) {
161 QSqlQuery query;
162 query.exec(QStringLiteral("CREATE TABLE state(windowId TEXT UNIQUE, state INTEGER);"));
163 }
165 if (!m_db.tables().contains(QStringLiteral("stage"))) {
166 QSqlQuery query;
167 query.exec(QStringLiteral("CREATE TABLE stage(appId TEXT UNIQUE, stage INTEGER);"));
168 }
171void WindowStateStorage::saveValue(const QString &queryString)
173 QMutexLocker mutexLocker(&s_mutex);
175 QFuture<void> future = QtConcurrent::run(&m_threadPool, executeAsyncQuery, queryString);
176 m_asyncQueries.append(future);
178 QFutureWatcher<void> *futureWatcher = new QFutureWatcher<void>();
179 futureWatcher->setFuture(future);
180 connect(futureWatcher, &QFutureWatcher<void>::finished,
181 this,
182 [=](){ m_asyncQueries.removeAll(futureWatcher->future());
183 futureWatcher->deleteLater(); });
186QSqlQuery WindowStateStorage::getValue(const QString &queryString) const
188 QMutexLocker l(&s_mutex);
189 QSqlQuery query;
191 bool ok = query.exec(queryString);
192 if (!ok) {
193 qWarning() << "Error retrieving database query:" << queryString
194 << "Driver error:" << query.lastError().driverText()
195 << "Database error:" << query.lastError().databaseText();
196 }
197 return query;
200Mir::State WindowStateStorage::toMirState(WindowState state) const
202 // assumes a single state (not an OR of several)
203 switch (state) {
204 case WindowStateMaximized: return Mir::MaximizedState;
205 case WindowStateMinimized: return Mir::MinimizedState;
206 case WindowStateFullscreen: return Mir::FullscreenState;
207 case WindowStateMaximizedLeft: return Mir::MaximizedLeftState;
208 case WindowStateMaximizedRight: return Mir::MaximizedRightState;
209 case WindowStateMaximizedHorizontally: return Mir::HorizMaximizedState;
210 case WindowStateMaximizedVertically: return Mir::VertMaximizedState;
211 case WindowStateMaximizedTopLeft: return Mir::MaximizedTopLeftState;
212 case WindowStateMaximizedTopRight: return Mir::MaximizedTopRightState;
213 case WindowStateMaximizedBottomLeft: return Mir::MaximizedBottomLeftState;
214 case WindowStateMaximizedBottomRight: return Mir::MaximizedBottomRightState;
216 case WindowStateNormal:
217 case WindowStateRestored:
218 default:
219 return Mir::RestoredState;
220 }