Page MenuHomePhorge

No OneTemporary

Authored By
Unknown
Size
19 KB
Referenced Files
None
Subscribers
None
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 22a3579..e8353bb 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,52 +1,53 @@
cmake_minimum_required(VERSION 3.13)
project(kazv)
include(FeatureSummary)
set(kazv_VERSION_MAJOR 0)
set(kazv_VERSION_MINOR 0)
set(kazv_VERSION_PATCH 1)
set(kazv_VERSION_STRING ${kazv_VERSION_MAJOR}.${kazv_VERSION_MINOR}.${kazv_VERSION_PATCH})
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
set(CMAKE_CXX_STANDARD 17)
set(QT5_MIN_VERSION 5.15)
set(KF5_MIN_VERSION 5.78)
find_package(ECM ${KF5_MIN_VERSION} REQUIRED NO_MODULE)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH})
include(KDEInstallDirs)
include(KDECMakeSettings)
include(KDECompilerSettings NO_POLICY_SCOPE)
include(ECMSetupVersion)
include(ECMGenerateHeaders)
include(ECMPoQmTools)
include(ECMAddTests)
include(ECMQtDeclareLoggingCategory)
find_package(Qt5 ${QT5_MIN_VERSION} REQUIRED COMPONENTS
Core Gui Qml QuickControls2 Svg Concurrent Widgets
Multimedia Test Network
)
# Sql Quick QuickControls2 Network DBus Xml)
find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS Kirigami2 Config KIO)
# Notifications Config Service KIO)
#find_package(MauiKit REQUIRED)
+find_package(Threads REQUIRED)
find_package(nlohmann_json REQUIRED)
-find_package(libkazv REQUIRED COMPONENTS job)
+find_package(libkazv REQUIRED)
kde_enable_exceptions()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -foperator-names -DQT_NO_EMIT")
add_subdirectory(src)
install(PROGRAMS moe.kazv.mxc.kazv.desktop DESTINATION ${KDE_INSTALL_APPDIR})
install(FILES moe.kazv.mxc.kazv.appdata.xml DESTINATION ${KDE_INSTALL_METAINFODIR})
feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES)
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 23f12b0..cabb34b 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,66 +1,65 @@
set(KAZV_DATA_DIR ${KDE_INSTALL_DATADIR})
set(KAZV_L10N_DIR ${KAZV_DATA_DIR}/l10n)
configure_file(kazv-path-config.hpp.in kazv-path-config.hpp)
configure_file(kazv-version.cpp.in kazv-version.cpp)
set(kazvprivlib_SRCS
qt-job-handler.cpp
qt-job.cpp
)
ecm_qt_declare_logging_category(kazvprivlib_SRCS
HEADER kazv-log.hpp
IDENTIFIER kazvLog
CATEGORY_NAME moe.kazv.mxc.kazv
)
add_library(kazvprivlib STATIC ${kazvprivlib_SRCS})
target_link_libraries(kazvprivlib PUBLIC
libkazv::kazvall
Qt5::Core
Qt5::Network
)
target_include_directories(kazvprivlib PUBLIC ${CMAKE_CURRENT_BINARY_DIR})
set(kazv_SRCS
${CMAKE_CURRENT_BINARY_DIR}/kazv-version.cpp
main.cpp
matrix-sdk.cpp
matrix-room.cpp
matrix-room-list.cpp
matrix-room-timeline.cpp
matrix-room-member.cpp
matrix-event.cpp
meta-types.cpp
l10n-provider.cpp
qt-rand-adapter.cpp
kazv-config.cpp
kazv-io-manager.cpp
kazv-io-job.cpp
upload-job-model.cpp
shortcuts/shortcut-util.cpp
resources.qrc
)
add_executable(kazv ${kazv_SRCS})
target_link_libraries(kazv
PRIVATE
- libkazv::kazvjob
Threads::Threads
Qt5::Gui Qt5::Qml Qt5::Quick Qt5::QuickControls2 Qt5::Svg Qt5::Concurrent Qt5::Widgets
KF5::ConfigCore KF5::KIOCore
kazvprivlib
)
install(TARGETS kazv ${KF5_INSTALL_TARGETS_DEFAULT_ARGS})
install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/l10n/
DESTINATION ${KAZV_L10N_DIR}
FILES_MATCHING PATTERN "*.ftl"
PATTERN "*.json"
)
add_subdirectory(tests)
diff --git a/src/matrix-sdk.cpp b/src/matrix-sdk.cpp
index a3eef17..7ac84f8 100644
--- a/src/matrix-sdk.cpp
+++ b/src/matrix-sdk.cpp
@@ -1,419 +1,414 @@
/*
* This file is part of kazv.
* SPDX-FileCopyrightText: 2020-2023 Tusooa Zhu <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#include <libkazv-config.hpp>
#include <immer/config.hpp> // https://github.com/arximboldi/immer/issues/168
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <fstream>
#include <filesystem>
#include <chrono>
#include <QMutex>
#include <QMutexLocker>
#include <QtConcurrent>
+#include <QThreadPool>
#include <KConfig>
#include <KConfigGroup>
#include <eventemitter/lagerstoreeventemitter.hpp>
-#include <job/cprjobhandler.hpp>
-#include <asio-promise-handler.hpp>
#include <client/sdk.hpp>
#include <base/descendent.hpp>
#include <zug/util.hpp>
-#include <boost/asio.hpp>
-#include <lager/event_loop/boost_asio.hpp>
#include <lager/event_loop/qt.hpp>
#include "matrix-sdk.hpp"
#include "matrix-room-list.hpp"
#include "helper.hpp"
#include "kazv-path-config.hpp"
#include "kazv-version.hpp"
#include "qt-rand-adapter.hpp"
+#include "qt-promise-handler.hpp"
+#include "qt-job-handler.hpp"
using namespace Kazv;
-// Sdk with Boost event loop, identity transform and no enhancers
-using ExecT = decltype(std::declval<boost::asio::io_context>().get_executor());
+// Sdk with qt event loop, identity transform and no enhancers
using SdkT =
decltype(makeSdk(
SdkModel{},
detail::declref<JobInterface>(),
detail::declref<EventInterface>(),
- AsioPromiseHandler{std::declval<boost::asio::io_context>().get_executor()},
+ QtPromiseHandler(detail::declref<QObject>()),
zug::identity,
withRandomGenerator(detail::declref<RandomInterface>())));
struct MatrixSdkPrivate
{
MatrixSdkPrivate(MatrixSdk *q);
MatrixSdkPrivate(MatrixSdk *q, SdkModel model);
RandomInterface randomGenerator;
- boost::asio::io_context io;
- CprJobHandler jobHandler;
+ QThread thread;
+ QThreadPool pool;
+ QObject obj;
+ QtJobHandler jobHandler;
LagerStoreEventEmitter ee;
LagerStoreEventEmitter::Watchable watchable;
SdkT sdk;
QTimer saveTimer;
- QMutex ioContextLock;
-
using SecondaryRootT = decltype(sdk.createSecondaryRoot(std::declval<lager::with_qt_event_loop>()));
SecondaryRootT secondaryRoot;
Client clientOnSecondaryRoot;
void runIoContext() {
- std::thread([this] {
- // This lock is to persist as long as io.run() does not return.
- // Together with the measures in emplace(), it ensures that
- // the io context is not destructed prematurely.
- QMutexLocker l(&ioContextLock);
- qDebug() << "running io context...";
- io.run();
- qDebug() << "io context is done";
- }).detach();
+ thread.start();
}
};
static void serializeClientToFile(Client c)
{
using namespace Kazv::CursorOp;
auto userId = +c.userId();
auto deviceId = +c.deviceId();
if (userId.empty() || deviceId.empty()) {
qDebug() << "Not logged in, nothing to serialize";
return;
}
using StdPath = std::filesystem::path;
auto userDataDir = StdPath(kazvUserDataDir().toStdString());
auto sessionDir = userDataDir / "sessions"
/ userId / deviceId;
auto storeFile = sessionDir / "store";
auto metadataFile = sessionDir / "metadata";
qDebug() << "storeFile=" << QString::fromStdString(storeFile.native());
std::error_code err;
if ((! std::filesystem::create_directories(sessionDir, err))
&& err) {
qDebug() << "Unable to create sessionDir";
return;
}
{
auto storeStream = std::ofstream(storeFile);
if (! storeStream) {
qDebug() << "Unable to open storeFile";
return;
}
using OAr = boost::archive::text_oarchive;
auto archive = OAr{storeStream};
c.serializeTo(archive);
qDebug() << "Serialization done";
}
// store metadata
{
KConfig metadata(QString::fromStdString(metadataFile.native()));
KConfigGroup mdGroup(&metadata, "Metadata");
mdGroup.writeEntry("kazvVersion", QString::fromStdString(kazvVersionString()));
mdGroup.writeEntry("archiveFormat", "text");
}
}
MatrixSdkPrivate::MatrixSdkPrivate(MatrixSdk *q)
: randomGenerator(QtRandAdapter{})
- , io{}
- , jobHandler{io.get_executor()}
- , ee{lager::with_boost_asio_event_loop{io.get_executor()}}
+ , thread()
+ , pool()
+ , obj(&thread)
+ , jobHandler(&obj)
+ , ee{lager::with_qt_event_loop{obj, pool}}
, watchable(ee.watchable())
, sdk(makeDefaultSdkWithCryptoRandom(
randomGenerator.generateRange<std::string>(makeDefaultSdkWithCryptoRandomSize()),
static_cast<JobInterface &>(jobHandler),
static_cast<EventInterface &>(ee),
- AsioPromiseHandler{io.get_executor()},
+ QtPromiseHandler(obj),
zug::identity,
withRandomGenerator(randomGenerator)))
, secondaryRoot(sdk.createSecondaryRoot(lager::with_qt_event_loop{*q}))
, clientOnSecondaryRoot(sdk.clientFromSecondaryRoot(secondaryRoot))
{
}
MatrixSdkPrivate::MatrixSdkPrivate(MatrixSdk *q, SdkModel model)
: randomGenerator(QtRandAdapter{})
- , io{}
- , jobHandler{io.get_executor()}
- , ee{lager::with_boost_asio_event_loop{io.get_executor()}}
+ , thread()
+ , pool()
+ , obj(&thread)
+ , jobHandler(&obj)
+ , ee{lager::with_qt_event_loop{obj, pool}}
, watchable(ee.watchable())
, sdk(makeSdk(
std::move(model),
static_cast<JobInterface &>(jobHandler),
static_cast<EventInterface &>(ee),
- AsioPromiseHandler{io.get_executor()},
+ QtPromiseHandler(obj),
zug::identity,
withRandomGenerator(randomGenerator)))
, secondaryRoot(sdk.createSecondaryRoot(lager::with_qt_event_loop{*q}))
, clientOnSecondaryRoot(sdk.clientFromSecondaryRoot(secondaryRoot))
{
}
MatrixSdk::MatrixSdk(std::unique_ptr<MatrixSdkPrivate> d, QObject *parent)
: QObject(parent)
, m_d(std::move(d))
{
init();
connect(this, &MatrixSdk::trigger,
this, [](KazvEvent e) {
qDebug() << "receiving trigger:";
if (std::holds_alternative<LoginSuccessful>(e)) {
qDebug() << "Login successful";
}
});
}
void MatrixSdk::init()
{
LAGER_QT(serverUrl) = m_d->clientOnSecondaryRoot.serverUrl().xform(strToQt); Q_EMIT serverUrlChanged(serverUrl());
LAGER_QT(userId) = m_d->clientOnSecondaryRoot.userId().xform(strToQt); Q_EMIT userIdChanged(userId());
LAGER_QT(token) = m_d->clientOnSecondaryRoot.token().xform(strToQt); Q_EMIT tokenChanged(token());
LAGER_QT(deviceId) = m_d->clientOnSecondaryRoot.deviceId().xform(strToQt); Q_EMIT deviceIdChanged(deviceId());
m_d->watchable.afterAll(
[this](KazvEvent e) {
Q_EMIT this->trigger(e);
});
m_d->watchable.after<LoginSuccessful>(
[this](LoginSuccessful e) {
Q_EMIT this->loginSuccessful(e);
});
m_d->watchable.after<LoginFailed>(
[this](LoginFailed e) {
Q_EMIT this->loginFailed(
QString::fromStdString(e.errorCode),
QString::fromStdString(e.error)
);
});
connect(&m_d->saveTimer, &QTimer::timeout, &m_d->saveTimer,
[m_d=m_d.get()]() {
serializeClientToFile(m_d->clientOnSecondaryRoot);
});
const int saveIntervalMs = 1000 * 60 * 5;
m_d->saveTimer.start(std::chrono::milliseconds{saveIntervalMs});
}
MatrixSdk::MatrixSdk(QObject *parent)
: MatrixSdk(std::make_unique<MatrixSdkPrivate>(this), parent)
{
}
MatrixSdk::MatrixSdk(SdkModel model, QObject *parent)
: MatrixSdk(std::make_unique<MatrixSdkPrivate>(this, std::move(model)), parent)
{
}
static void cleanupDPointer(std::unique_ptr<MatrixSdkPrivate> oldD)
{
oldD->saveTimer.disconnect();
oldD->saveTimer.stop();
QtConcurrent::run(
[oldD=std::shared_ptr(std::move(oldD))] {
qDebug() << "start to clean up everything";
// Not running io_context::stop() because it will
// disregard any remaining work guards. Here we just
// want to end any periodic jobs but still wait for
// current remaining jobs.
oldD->clientOnSecondaryRoot.stopSyncing();
- oldD->jobHandler.stop();
+ oldD->jobHandler.deleteLater();
+ oldD->obj.deleteLater();
// This lock will not be unlocked as long as io.run()
// in runIoContext() does not return.
- QMutexLocker l(&(oldD->ioContextLock));
+ oldD->thread.wait();
// qDebug() << "serialize final client state";
// serializeClientToFile(oldD->sdk.client());
qDebug() << "from here, the old data can be destructed";
});
}
MatrixSdk::~MatrixSdk()
{
if (m_d) {
serializeToFile();
cleanupDPointer(std::move(m_d));
}
}
QString MatrixSdk::mxcUriToHttp(QString mxcUri) const
{
return QString::fromStdString(m_d->clientOnSecondaryRoot.mxcUriToHttp(mxcUri.toStdString()));
}
void MatrixSdk::login(const QString &userId, const QString &password)
{
m_d->clientOnSecondaryRoot
.autoDiscover(userId.toStdString())
.then([
this,
client=m_d->clientOnSecondaryRoot.toEventLoop(),
userId,
password
](auto res) {
if (!res.success()) {
// FIXME use real error codes and msgs when available in libkazv
Q_EMIT this->discoverFailed("", "");
return res;
}
auto serverUrl = res.dataStr("homeserverUrl");
client.passwordLogin(
serverUrl,
userId.toStdString(),
password.toStdString(),
"kazv 0.0.0"
);
return res;
});
m_d->runIoContext();
}
MatrixRoomList *MatrixSdk::roomList() const
{
return new MatrixRoomList(m_d->clientOnSecondaryRoot);
}
void MatrixSdk::emplace(std::optional<SdkModel> model)
{
if (m_d) {
cleanupDPointer(std::move(m_d));
}
m_d = (model.has_value()
? std::make_unique<MatrixSdkPrivate>(this, std::move(model.value()))
: std::make_unique<MatrixSdkPrivate>(this));
// Re-initialize lager-qt cursors and watchable connections
init();
m_d->runIoContext();
m_d->clientOnSecondaryRoot.startSyncing();
Q_EMIT sessionChanged();
}
QStringList MatrixSdk::allSessions() const
{
using StdPath = std::filesystem::path;
auto userDataDir = StdPath(kazvUserDataDir().toStdString());
auto allSessionsDir = userDataDir / "sessions";
QStringList sessionNames;
try {
for (const auto &p : std::filesystem::directory_iterator(allSessionsDir)) {
if (p.is_directory()) {
auto userId = p.path().filename().native();
for (const auto &q : std::filesystem::directory_iterator(p.path())) {
auto path = q.path();
auto deviceId = path.filename().native();
std::error_code err;
if (std::filesystem::exists(path / "store", err)) {
sessionNames.append(QString::fromStdString(userId + "/" + deviceId));
}
}
}
}
} catch (const std::filesystem::filesystem_error &) {
qDebug() << "sessionDir not available, ignoring";
}
return sessionNames;
}
void MatrixSdk::serializeToFile() const
{
serializeClientToFile(m_d->clientOnSecondaryRoot);
}
bool MatrixSdk::loadSession(QString sessionName)
{
qDebug() << "in loadSession(), sessionName=" << sessionName;
using StdPath = std::filesystem::path;
auto userDataDir = StdPath(kazvUserDataDir().toStdString());
auto sessionDir = userDataDir / "sessions" / sessionName.toStdString();
auto storeFile = sessionDir / "store";
auto metadataFile = sessionDir / "metadata";
if (! std::filesystem::exists(storeFile)) {
qDebug() << "storeFile does not exist, skip loading session " << sessionName;
return false;
}
if (std::filesystem::exists(metadataFile)) {
KConfig metadata(QString::fromStdString(metadataFile.native()));
KConfigGroup mdGroup(&metadata, "Metadata");
auto format = mdGroup.readEntry("archiveFormat");
if (format != QStringLiteral("text")) {
qDebug() << "Unknown archive format:" << format;
return false;
}
auto version = mdGroup.readEntry("kazvVersion");
auto curVersion = kazvVersionString();
if (version != QString::fromStdString(curVersion)) {
qDebug() << "A different version from the current one, making a backup";
std::error_code err;
auto now = std::chrono::system_clock::now();
auto backupName =
std::to_string(std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch()).count());
auto backupDir = sessionDir / "backup" / backupName;
if (! std::filesystem::create_directories(backupDir, err)
&& err) {
qDebug() << "Cannot create backup directory";
return false;
}
std::filesystem::copy_file(storeFile, backupDir / "store");
std::filesystem::copy_file(metadataFile, backupDir / "metadata");
}
}
SdkModel model;
try {
auto storeStream = std::ifstream(storeFile);
if (! storeStream) {
qDebug() << "Unable to open storeFile";
return false;
}
using IAr = boost::archive::text_iarchive;
auto archive = IAr{storeStream};
archive >> model;
qDebug() << "Finished loading session";
} catch (const std::exception &e) {
qDebug() << "Error when loading session:" << QString::fromStdString(e.what());
return false;
}
emplace(std::move(model));
return true;
}
bool MatrixSdk::startNewSession()
{
emplace(std::nullopt);
return true;
}
diff --git a/src/qt-job-handler.cpp b/src/qt-job-handler.cpp
index ffbaa2d..0224ca6 100644
--- a/src/qt-job-handler.cpp
+++ b/src/qt-job-handler.cpp
@@ -1,76 +1,72 @@
/*
* This file is part of kazv.
* SPDX-FileCopyrightText: 2022 tusooa <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#include <libkazv-config.hpp>
#include <unordered_map>
#include <QNetworkAccessManager>
#include <QByteArray>
#include <QThreadPool>
#include <QTimer>
#include <zug/transducer/filter.hpp>
#include <zug/into.hpp>
#include "qt-job-handler.hpp"
#include "qt-job.hpp"
#include "kazv-log.hpp"
struct QtJobHandler::Private
{
QNetworkAccessManager *manager;
-
- Private(QtJobHandler *q)
- : manager(new QNetworkAccessManager(q))
- {
- }
};
QtJobHandler::QtJobHandler(QObject *parent)
: QObject(parent)
- , m_d(new Private(this))
+ , m_d(new Private)
{
+ m_d->manager = new QNetworkAccessManager(this);
}
QtJobHandler::~QtJobHandler() = default;
void QtJobHandler::async(std::function<void()> func)
{
QThreadPool::globalInstance()->start(func);
}
void QtJobHandler::setTimeout(std::function<void()> func, int ms,
std::optional<std::string> timerId)
{
Q_UNUSED(timerId);
QTimer::singleShot(ms, this, func);
}
void QtJobHandler::setInterval(std::function<void()> func, int ms,
std::optional<std::string> timerId)
{
Q_UNUSED(func);
Q_UNUSED(ms);
Q_UNUSED(timerId);
qCCritical(kazvLog) << "QtJobHandler::setInterval: unimplemented";
}
void QtJobHandler::cancel(std::string timerId)
{
Q_UNUSED(timerId);
qCCritical(kazvLog) << "QtJobHandler::cancel: unimplemented";
}
void QtJobHandler::submit(BaseJob job,
std::function<void(Response)> callback)
{
new QtJob(this, m_d->manager, std::move(job), std::move(callback));
}
void QtJobHandler::stop()
{
}

File Metadata

Mime Type
text/x-diff
Expires
Mon, Nov 2, 8:05 AM (16 h, 56 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1803689
Default Alt Text
(19 KB)

Event Timeline