/* * Copyright 2015-2016 Canonical Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include "windowstatestorage.h" #include #include #include #include #include #include #include #include QMutex WindowStateStorage::s_mutex; inline QString sanitiseString(QString string) { return string.remove(QLatin1Char('\"')) .remove(QLatin1Char('\'')) .remove(QLatin1Char('\\')); } WindowStateStorage::WindowStateStorage(QObject *parent): QObject(parent) { const QString dbPath = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + QStringLiteral("/unity8/"); m_db = QSqlDatabase::addDatabase(QStringLiteral("QSQLITE")); QDir dir; dir.mkpath(dbPath); m_db.setDatabaseName(dbPath + "windowstatestorage.sqlite"); initdb(); } WindowStateStorage::~WindowStateStorage() { QFutureSynchronizer futureSync; for (int i = 0; i < m_asyncQueries.count(); ++i) { futureSync.addFuture(m_asyncQueries[i]); } futureSync.waitForFinished(); m_db.close(); } void WindowStateStorage::saveState(const QString &windowId, WindowStateStorage::WindowState state) { const QString queryString = QStringLiteral("INSERT OR REPLACE INTO state (windowId, state) values ('%1', '%2');") .arg(sanitiseString(windowId)) .arg((int)state); saveValue(queryString); } WindowStateStorage::WindowState WindowStateStorage::getState(const QString &windowId, WindowStateStorage::WindowState defaultValue) const { const QString queryString = QStringLiteral("SELECT state FROM state WHERE windowId = '%1';") .arg(sanitiseString(windowId)); QSqlQuery query = getValue(queryString); if (!query.first()) { return defaultValue; } return (WindowState)query.value(QStringLiteral("state")).toInt(); } void WindowStateStorage::saveGeometry(const QString &windowId, const QRect &rect) { const QString queryString = QStringLiteral("INSERT OR REPLACE INTO geometry (windowId, x, y, width, height) values ('%1', '%2', '%3', '%4', '%5');") .arg(sanitiseString(windowId)) .arg(rect.x()) .arg(rect.y()) .arg(rect.width()) .arg(rect.height()); saveValue(queryString); } void WindowStateStorage::saveStage(const QString &appId, int stage) { const QString queryString = QStringLiteral("INSERT OR REPLACE INTO stage (appId, stage) values ('%1', '%2');") .arg(sanitiseString(appId)) .arg(stage); saveValue(queryString); } int WindowStateStorage::getStage(const QString &appId, int defaultValue) const { const QString queryString = QStringLiteral("SELECT stage FROM stage WHERE appId = '%1';") .arg(sanitiseString(appId)); QSqlQuery query = getValue(queryString); if (!query.first()) { return defaultValue; } return query.value("stage").toInt(); } void WindowStateStorage::executeAsyncQuery(const QString &queryString) { QMutexLocker l(&s_mutex); QSqlQuery query; bool ok = query.exec(queryString); if (!ok) { qWarning() << "Error executing query" << queryString << "Driver error:" << query.lastError().driverText() << "Database error:" << query.lastError().databaseText(); } } QRect WindowStateStorage::getGeometry(const QString &windowId, const QRect &defaultValue) const { QString queryString = QStringLiteral("SELECT * FROM geometry WHERE windowId = '%1';") .arg(sanitiseString(windowId)); QSqlQuery query = getValue(queryString); if (!query.first()) { return defaultValue; } const QRect result(query.value(QStringLiteral("x")).toInt(), query.value(QStringLiteral("y")).toInt(), query.value(QStringLiteral("width")).toInt(), query.value(QStringLiteral("height")).toInt()); if (result.isValid()) { return result; } return defaultValue; } void WindowStateStorage::initdb() { m_db.open(); if (!m_db.open()) { qWarning() << "Error opening state database:" << m_db.lastError().driverText() << m_db.lastError().databaseText(); return; } if (!m_db.tables().contains(QStringLiteral("geometry"))) { QSqlQuery query; query.exec(QStringLiteral("CREATE TABLE geometry(windowId TEXT UNIQUE, x INTEGER, y INTEGER, width INTEGER, height INTEGER);")); } if (!m_db.tables().contains(QStringLiteral("state"))) { QSqlQuery query; query.exec(QStringLiteral("CREATE TABLE state(windowId TEXT UNIQUE, state INTEGER);")); } if (!m_db.tables().contains(QStringLiteral("stage"))) { QSqlQuery query; query.exec(QStringLiteral("CREATE TABLE stage(appId TEXT UNIQUE, stage INTEGER);")); } } void WindowStateStorage::saveValue(const QString &queryString) { QMutexLocker mutexLocker(&s_mutex); QFuture future = QtConcurrent::run(&m_threadPool, executeAsyncQuery, queryString); m_asyncQueries.append(future); QFutureWatcher *futureWatcher = new QFutureWatcher(); futureWatcher->setFuture(future); connect(futureWatcher, &QFutureWatcher::finished, this, [=](){ m_asyncQueries.removeAll(futureWatcher->future()); futureWatcher->deleteLater(); }); } QSqlQuery WindowStateStorage::getValue(const QString &queryString) const { QMutexLocker l(&s_mutex); QSqlQuery query; bool ok = query.exec(queryString); if (!ok) { qWarning() << "Error retrieving database query:" << queryString << "Driver error:" << query.lastError().driverText() << "Database error:" << query.lastError().databaseText(); } return query; } Mir::State WindowStateStorage::toMirState(WindowState state) const { // assumes a single state (not an OR of several) switch (state) { case WindowStateMaximized: return Mir::MaximizedState; case WindowStateMinimized: return Mir::MinimizedState; case WindowStateFullscreen: return Mir::FullscreenState; case WindowStateMaximizedLeft: return Mir::MaximizedLeftState; case WindowStateMaximizedRight: return Mir::MaximizedRightState; case WindowStateMaximizedHorizontally: return Mir::HorizMaximizedState; case WindowStateMaximizedVertically: return Mir::VertMaximizedState; case WindowStateMaximizedTopLeft: return Mir::MaximizedTopLeftState; case WindowStateMaximizedTopRight: return Mir::MaximizedTopRightState; case WindowStateMaximizedBottomLeft: return Mir::MaximizedBottomLeftState; case WindowStateMaximizedBottomRight: return Mir::MaximizedBottomRightState; case WindowStateNormal: case WindowStateRestored: default: return Mir::RestoredState; } }