From 81e474ab4779e6b490b630ca99f6692ca2eaf392 Mon Sep 17 00:00:00 2001 From: hellishnoob Date: Thu, 18 Jan 2018 00:06:40 +0300 Subject: [PATCH 01/12] make firefox close gracefully, un-hardcode paths, switch to C++11, minor fixes --- CMakeLists.txt | 5 +- main.cpp | 177 +++++++++++++++++++++++++++++++++---------------- 2 files changed, 125 insertions(+), 57 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5f7a23d..e9aae4e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,3 +1,4 @@ +cmake_minimum_required(VERSION 3.1) project (activityfox) find_package(KDE4 REQUIRED) @@ -8,4 +9,6 @@ set(tutorial2_SRCS ) kde4_add_executable(activityfox ${tutorial2_SRCS}) -target_link_libraries(activityfox ${KDE4_KDEUI_LIBS}) \ No newline at end of file +target_link_libraries(activityfox ${KDE4_KDEUI_LIBS}) + +set_property(TARGET activityfox PROPERTY CXX_STANDARD 11) \ No newline at end of file diff --git a/main.cpp b/main.cpp index 621875c..540b329 100644 --- a/main.cpp +++ b/main.cpp @@ -1,79 +1,144 @@ #include #include #include -#include #include #include #include -//#include #include +#include -const char* firefoxbin_path = "/usr/lib64/firefox/firefox-bin"; - -class MainWindow : public KMainWindow -{ - public: - MainWindow(QString profile = "", QWidget *parent=0); - protected: - void saveProperties(KConfigGroup& ); - void readProperties(const KConfigGroup& ); - private: - QString firefoxprofile; +const char *defaultProfileDirTail = ".mozilla/firefox"; +const char *defaultBinPath = "/usr/lib/firefox/firefox"; + +class MainWindow : public KMainWindow { +public: + explicit MainWindow(const QString &binPath = nullptr, const QString &profileDir = nullptr, + const QString &templateProfile = nullptr, QString profile = nullptr, QWidget *parent = nullptr); + +protected: + void saveProperties(KConfigGroup &) override; + + void readProperties(const KConfigGroup &) override; + + QString getCurrentActivityId(); + +private: + QString firefoxProfile; + QString binPath; KProcess p; + }; -MainWindow::MainWindow(QString profile, QWidget *parent) : KMainWindow(parent) -{ - setGeometry(100,100,200,100); - if (profile != "") { - firefoxprofile = profile; +MainWindow::MainWindow(const QString &binPath, const QString &profileDir, const QString &templateProfile, + QString profile, QWidget *parent) : KMainWindow(parent) { + // args such as binPath are null when session is restored, not null on the first run + if (binPath != nullptr) { + if (profile == "") { + profile = getCurrentActivityId(); + } + + this->binPath = binPath; + + setGeometry(100, 100, 200, 100); + firefoxProfile = profile; QStringList createProfileCommand; - createProfileCommand << firefoxbin_path << "-CreateProfile" << firefoxprofile << "-no-remote"; + + // create profile with a specified profile name (or activity id) in a specified profile directory + createProfileCommand << binPath << "-CreateProfile" << firefoxProfile + ' ' + profileDir + '/' + firefoxProfile + << "-no-remote"; KProcess::execute(createProfileCommand); - p << firefoxbin_path << "-P" << firefoxprofile << "-no-remote"; + + // run Firefox + p << binPath << "-p" << firefoxProfile << "-no-remote"; p.start(); - } else { - firefoxprofile = ""; } } -void MainWindow::saveProperties(KConfigGroup& conf) { - conf.writeEntry("ffProfile", firefoxprofile); - p.kill(); +void MainWindow::saveProperties(KConfigGroup &conf) { + // check if Firefox is running + if (p.pid() > 0) { + conf.writeEntry("ffProfile", firefoxProfile); + conf.writeEntry("binPath", binPath); + + // trying to gracefully terminate Firefox by closing the window with wmctrl + QStringList closeFirefoxCommand; + char pid[21]; + + sprintf(pid, "%d", p.pid()); + std::string arguments = "wmctrl -i -c `wmctrl -lp | grep "; + arguments = arguments + pid + " | cut -c -10`"; + closeFirefoxCommand << arguments.c_str(); + + int exitcode = KProcess::execute(closeFirefoxCommand); + + // if we can't close the window, terminate the process anyway + if (exitcode != 0) { + p.terminate(); + } + } else { + // do not need to restore this app if Firefox was terminated + exit(0); + } } -void MainWindow::readProperties(const KConfigGroup& conf) { - firefoxprofile = conf.readEntry("ffProfile", QString()); - p << firefoxbin_path << "-P" << firefoxprofile << "-no-remote"; +void MainWindow::readProperties(const KConfigGroup &conf) { + // restart Firefox + firefoxProfile = conf.readEntry("ffProfile", QString()); + binPath = conf.readEntry("binPath", QString()); + p << binPath << "-p" << firefoxProfile << "-no-remote"; p.start(); } -int main (int argc, char *argv[]) -{ - KAboutData aboutData( "activityfox", 0, - ki18n("ActivityFox"), "0.1", - ki18n("Help Firefox work with KDE activities."), - KAboutData::License_GPL, - ki18n("Copyright (c) 2012 Yuen Hoe (Jason moofang)") ); - KCmdLineArgs::init( argc, argv, &aboutData ); - KCmdLineOptions options; - options.add("+profilename", ki18n("Firefox profile name")); - KCmdLineArgs::addCmdLineOptions(options); - if (argc <= 1) { - std::cout << "Please specify firefox profile name" << std::endl; - return 0; - } - - KApplication app; - if ( app.isSessionRestored() ) { - kRestoreMainWindows< MainWindow >(); - } else { - // create default application as usual - // example: - MainWindow * window = new MainWindow(argv[1]); - window->setObjectName("MyWindow#"); - window->show(); - } - - return app.exec(); +QString MainWindow::getCurrentActivityId() { + // get current activity id with QDBus, also possible to get by executing: + // qdbus org.kde.ActivityManager /ActivityManager/Activities org.kde.ActivityManager.Activities.CurrentActivity + QDBusInterface interface("org.kde.ActivityManager", + "/ActivityManager/Activities", + "org.kde.ActivityManager.Activities", + QDBusConnection::sessionBus()); + + QDBusMessage result = interface.call("CurrentActivity"); + return result.arguments().at(0).value(); +} + +int main(int argc, char *argv[]) { + std::string defaultProfileDir = getenv("HOME"); + defaultProfileDir = defaultProfileDir + '/' + defaultProfileDirTail; + + KAboutData aboutData("activityfox", nullptr, + ki18n("ActivityFox"), "0.1", + ki18n("Help Firefox work with KDE activities."), + KAboutData::License_GPL, + ki18n("Copyright (c) 2012 Yuen Hoe (Jason moofang), 2018 Leonid Kalichkin (hellishnoob)")); + KCmdLineArgs::init(argc, argv, &aboutData); + KCmdLineOptions options; + options.add("bin-path ", ki18n("Path to Firefox executable"), defaultBinPath); + options.add("profile-dir ", ki18n("Path to Firefox profile directory"), defaultProfileDir.c_str()); + options.add("template-profile ", ki18n("Template profile name")); + options.add("+[profile-name]", ki18n("Firefox profile name")); + KCmdLineArgs::addCmdLineOptions(options); + + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + + QString binPath = args->getOption("bin-path"); + QString profileDir = args->getOption("profile-dir"); + QString templateProfile = args->getOption("template-profile"); + QString profileName = nullptr; + + if (args->count() > 0) { + profileName = args->arg(0); + } + + KApplication app; + if (app.isSessionRestored()) { + kRestoreMainWindows(); + } else { + // create default application as usual + // example: + MainWindow *window = new MainWindow(binPath, profileDir, templateProfile, profileName); + window->setObjectName("ActivityFoxWindow"); + window->show(); + } + + return app.exec(); } From fb5a7b903251d756191d94c9999f7d751e7c253c Mon Sep 17 00:00:00 2001 From: hellishnoob Date: Thu, 18 Jan 2018 02:37:59 +0300 Subject: [PATCH 02/12] fix termination with wmctrl, wait for process to finish --- main.cpp | 55 ++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 48 insertions(+), 7 deletions(-) diff --git a/main.cpp b/main.cpp index 540b329..537d336 100644 --- a/main.cpp +++ b/main.cpp @@ -61,20 +61,25 @@ void MainWindow::saveProperties(KConfigGroup &conf) { conf.writeEntry("binPath", binPath); // trying to gracefully terminate Firefox by closing the window with wmctrl - QStringList closeFirefoxCommand; char pid[21]; + KProcess closeFirefox; sprintf(pid, "%d", p.pid()); - std::string arguments = "wmctrl -i -c `wmctrl -lp | grep "; - arguments = arguments + pid + " | cut -c -10`"; - closeFirefoxCommand << arguments.c_str(); + std::string closeFirefoxCommand = "wmctrl -i -c `wmctrl -lp | grep "; + closeFirefoxCommand = closeFirefoxCommand + pid + " | cut -c -10`"; - int exitcode = KProcess::execute(closeFirefoxCommand); + closeFirefox.setShellCommand(closeFirefoxCommand.c_str()); + int exitcode = closeFirefox.execute(); - // if we can't close the window, terminate the process anyway + // wmctrl failed, terminate Firefox normally if (exitcode != 0) { p.terminate(); } + + // wait until Firefox is closed, at most 30 seconds, terminate if wasn't closed after that + if (!p.waitForFinished()) { + p.terminate(); + } } else { // do not need to restore this app if Firefox was terminated exit(0); @@ -101,7 +106,39 @@ QString MainWindow::getCurrentActivityId() { return result.arguments().at(0).value(); } +FILE * fp; + + +// for debug purposes + +//void myMessageOutput(QtMsgType type, const char *msg) +//{ +// switch (type) { +// case QtDebugMsg: +// fprintf(fp, "Debug: %s\n", msg); +// fflush(fp); +// break; +// case QtWarningMsg: +// fprintf(fp, "Warning: %s\n", msg); +// fflush(fp); +// break; +// case QtCriticalMsg: +// fprintf(fp, "Critical: %s\n", msg); +// fflush(fp); +// break; +// case QtFatalMsg: +// fprintf(fp, "Fatal: %s\n", msg); +// fflush(fp); +// abort(); +// } +//} + int main(int argc, char *argv[]) { + // for debug purposes, use qDebug() << "message"; + + // fp = fopen("activityfox.log", "a"); + // qInstallMsgHandler(myMessageOutput); + std::string defaultProfileDir = getenv("HOME"); defaultProfileDir = defaultProfileDir + '/' + defaultProfileDirTail; @@ -140,5 +177,9 @@ int main(int argc, char *argv[]) { window->show(); } - return app.exec(); + int result = app.exec(); + + fclose(fp); + + return result; } From 765d2a7062e92d5bf759ea206d495d88e70fc941 Mon Sep 17 00:00:00 2001 From: hellishnoob Date: Thu, 18 Jan 2018 15:12:37 +0300 Subject: [PATCH 03/12] refactor, fix bugs --- main.cpp | 216 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 131 insertions(+), 85 deletions(-) diff --git a/main.cpp b/main.cpp index 537d336..5869b54 100644 --- a/main.cpp +++ b/main.cpp @@ -6,92 +6,86 @@ #include #include #include +#include const char *defaultProfileDirTail = ".mozilla/firefox"; const char *defaultBinPath = "/usr/lib/firefox/firefox"; class MainWindow : public KMainWindow { public: - explicit MainWindow(const QString &binPath = nullptr, const QString &profileDir = nullptr, - const QString &templateProfile = nullptr, QString profile = nullptr, QWidget *parent = nullptr); + MainWindow(const QString &binPath, const QString &profileDir, + const QString &templateProfileName, const QString &profileName); + + MainWindow(); protected: void saveProperties(KConfigGroup &) override; void readProperties(const KConfigGroup &) override; +private: + QString firefoxProfileName; + QString firefoxBinPath; + KProcess firefoxProcess; + + bool checkIfProfileExists(const QString &profileDir, const QString &profileName); + QString getCurrentActivityId(); -private: - QString firefoxProfile; - QString binPath; - KProcess p; + void terminateFirefox(KProcess &process); -}; + void startFirefox(KProcess &firefoxProcess, const QString &binPath, const QString &profileName); -MainWindow::MainWindow(const QString &binPath, const QString &profileDir, const QString &templateProfile, - QString profile, QWidget *parent) : KMainWindow(parent) { - // args such as binPath are null when session is restored, not null on the first run - if (binPath != nullptr) { - if (profile == "") { - profile = getCurrentActivityId(); - } + void createProfile(const QString &binPath, const QString &profileDir, const QString &profileName, + const QString &templateProfileName); +}; - this->binPath = binPath; +// empty constructor is used when session is restored +MainWindow::MainWindow() : KMainWindow() { + qDebug() << "MainWindow: Session was restored"; +} - setGeometry(100, 100, 200, 100); - firefoxProfile = profile; - QStringList createProfileCommand; +MainWindow::MainWindow(const QString &binPath, const QString &profileDir, const QString &templateProfileName, + const QString &profileName) : KMainWindow() { + // if profile name was not specified, use current activity id + if (profileName == nullptr || profileName == "") { + firefoxProfileName = getCurrentActivityId(); + } else { + firefoxProfileName = profileName; + } - // create profile with a specified profile name (or activity id) in a specified profile directory - createProfileCommand << binPath << "-CreateProfile" << firefoxProfile + ' ' + profileDir + '/' + firefoxProfile - << "-no-remote"; - KProcess::execute(createProfileCommand); + firefoxBinPath = binPath; - // run Firefox - p << binPath << "-p" << firefoxProfile << "-no-remote"; - p.start(); + if (!checkIfProfileExists(profileDir, firefoxProfileName)) { + qDebug() << "Profile doesn't exist, creating it, name is: " << firefoxProfileName; + createProfile(firefoxBinPath, profileDir, firefoxProfileName, templateProfileName); } + + qDebug() << "Starting firefox"; + startFirefox(firefoxProcess, firefoxBinPath, firefoxProfileName); } void MainWindow::saveProperties(KConfigGroup &conf) { + qDebug() << "ActivityFox is closing, saving session"; // check if Firefox is running - if (p.pid() > 0) { - conf.writeEntry("ffProfile", firefoxProfile); - conf.writeEntry("binPath", binPath); - - // trying to gracefully terminate Firefox by closing the window with wmctrl - char pid[21]; - KProcess closeFirefox; - - sprintf(pid, "%d", p.pid()); - std::string closeFirefoxCommand = "wmctrl -i -c `wmctrl -lp | grep "; - closeFirefoxCommand = closeFirefoxCommand + pid + " | cut -c -10`"; - - closeFirefox.setShellCommand(closeFirefoxCommand.c_str()); - int exitcode = closeFirefox.execute(); - - // wmctrl failed, terminate Firefox normally - if (exitcode != 0) { - p.terminate(); - } - - // wait until Firefox is closed, at most 30 seconds, terminate if wasn't closed after that - if (!p.waitForFinished()) { - p.terminate(); - } + if (firefoxProcess.pid() > 0) { + conf.writeEntry("ffProfileName", firefoxProfileName); + conf.writeEntry("ffBinPath", firefoxBinPath); + + terminateFirefox(firefoxProcess); } else { - // do not need to restore this app if Firefox was terminated + // do not need to restore this app if Firefox was closed by user or any other reason + qDebug("Firefox was closed, exiting from this app"); exit(0); } } void MainWindow::readProperties(const KConfigGroup &conf) { + qDebug() << "ActivityFox is restored, starting Firefox"; // restart Firefox - firefoxProfile = conf.readEntry("ffProfile", QString()); - binPath = conf.readEntry("binPath", QString()); - p << binPath << "-p" << firefoxProfile << "-no-remote"; - p.start(); + firefoxProfileName = conf.readEntry("ffProfileName", QString()); + firefoxBinPath = conf.readEntry("ffBinPath", QString()); + startFirefox(firefoxProcess, firefoxBinPath, firefoxProfileName); } QString MainWindow::getCurrentActivityId() { @@ -106,38 +100,90 @@ QString MainWindow::getCurrentActivityId() { return result.arguments().at(0).value(); } -FILE * fp; - - -// for debug purposes - -//void myMessageOutput(QtMsgType type, const char *msg) -//{ -// switch (type) { -// case QtDebugMsg: -// fprintf(fp, "Debug: %s\n", msg); -// fflush(fp); -// break; -// case QtWarningMsg: -// fprintf(fp, "Warning: %s\n", msg); -// fflush(fp); -// break; -// case QtCriticalMsg: -// fprintf(fp, "Critical: %s\n", msg); -// fflush(fp); -// break; -// case QtFatalMsg: -// fprintf(fp, "Fatal: %s\n", msg); -// fflush(fp); -// abort(); -// } -//} +bool MainWindow::checkIfProfileExists(const QString &profileDir, const QString &profileName) { + KProcess checkIfProfileExists; + + QString command = "cat " + profileDir + '/' + "profiles.ini" + " | grep " + profileName; + qDebug() << command; + + checkIfProfileExists.setShellCommand(command); + return (checkIfProfileExists.execute() == 0); +} + +void MainWindow::createProfile(const QString &binPath, const QString &profileDir, const QString &profileName, + const QString &templateProfileName) { + QStringList command; + + // create profile with a specified profile name (or activity id) in a specified profile directory + command << binPath << "-CreateProfile" << profileName + ' ' + profileDir + '/' + profileName + << "-no-remote"; + qDebug() << command; + KProcess::execute(command); +} + +void MainWindow::startFirefox(KProcess &firefoxProcess, const QString &binPath, const QString &profileName) { + firefoxProcess << binPath << "-p" << profileName << "-no-remote"; + firefoxProcess.start(); +} + +void MainWindow::terminateFirefox(KProcess &process) { + char pid[21]; + KProcess closeFirefox; + + sprintf(pid, "%d", process.pid()); + QString command = "wmctrl -i -c `wmctrl -lp | grep " + QString(pid) + " | cut -c -10"; + qDebug() << command; + + // trying to gracefully terminate Firefox by closing the window with wmctrl + closeFirefox.setShellCommand(command); + int exitcode = closeFirefox.execute(); + + // if wmctrl failed, terminate Firefox normally + if (exitcode != 0) { + qDebug() << "wmctrl failed"; + process.terminate(); + } + + // wait until Firefox is closed for at most 30 seconds, terminate if it wasn't closed after that + if (!process.waitForFinished()) { + qDebug() << "Firefox hasn't finished in 30 seconds"; + process.terminate(); + } +} + +#ifndef NDEBUG +FILE *fp; +#endif + +void myMessageOutput(QtMsgType type, const char *msg) { +#ifndef NDEBUG + switch (type) { + case QtDebugMsg: + fprintf(fp, "Debug: %s\n", msg); + fflush(fp); + break; + case QtWarningMsg: + fprintf(fp, "Warning: %s\n", msg); + fflush(fp); + break; + case QtCriticalMsg: + fprintf(fp, "Critical: %s\n", msg); + fflush(fp); + break; + case QtFatalMsg: + fprintf(fp, "Fatal: %s\n", msg); + fflush(fp); + abort(); + } +#endif +} int main(int argc, char *argv[]) { - // for debug purposes, use qDebug() << "message"; +#ifndef NDEBUG + fp = fopen("activityfox.log", "a"); +#endif - // fp = fopen("activityfox.log", "a"); - // qInstallMsgHandler(myMessageOutput); + qInstallMsgHandler(myMessageOutput); std::string defaultProfileDir = getenv("HOME"); defaultProfileDir = defaultProfileDir + '/' + defaultProfileDirTail; @@ -170,8 +216,6 @@ int main(int argc, char *argv[]) { if (app.isSessionRestored()) { kRestoreMainWindows(); } else { - // create default application as usual - // example: MainWindow *window = new MainWindow(binPath, profileDir, templateProfile, profileName); window->setObjectName("ActivityFoxWindow"); window->show(); @@ -179,7 +223,9 @@ int main(int argc, char *argv[]) { int result = app.exec(); +#ifndef NDEBUG fclose(fp); +#endif return result; } From 10ed3963ee75312a05264d3c4d2323e7246eab28 Mon Sep 17 00:00:00 2001 From: hellishnoob Date: Thu, 18 Jan 2018 17:16:27 +0300 Subject: [PATCH 04/12] add copying of a template profile --- main.cpp | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/main.cpp b/main.cpp index 5869b54..5285fbd 100644 --- a/main.cpp +++ b/main.cpp @@ -4,9 +4,7 @@ #include #include #include -#include #include -#include const char *defaultProfileDirTail = ".mozilla/firefox"; const char *defaultBinPath = "/usr/lib/firefox/firefox"; @@ -38,6 +36,8 @@ class MainWindow : public KMainWindow { void createProfile(const QString &binPath, const QString &profileDir, const QString &profileName, const QString &templateProfileName); + + void copyProfile(const QString &profileDir, const QString &fromName, const QString &toName); }; // empty constructor is used when session is restored @@ -112,9 +112,14 @@ bool MainWindow::checkIfProfileExists(const QString &profileDir, const QString & void MainWindow::createProfile(const QString &binPath, const QString &profileDir, const QString &profileName, const QString &templateProfileName) { + // if a template profile name is specified, copy this profile into the new profile directory + if (templateProfileName != nullptr && templateProfileName != "") { + copyProfile(profileDir, templateProfileName, profileName); + } + QStringList command; - // create profile with a specified profile name (or activity id) in a specified profile directory + // create a profile command << binPath << "-CreateProfile" << profileName + ' ' + profileDir + '/' + profileName << "-no-remote"; qDebug() << command; @@ -151,6 +156,16 @@ void MainWindow::terminateFirefox(KProcess &process) { } } +void MainWindow::copyProfile(const QString &profileDir, const QString &fromName, const QString &toName) { + QStringList command; + + // make copy of a profile + command << "cp" << "-r" << profileDir + '/' + fromName << profileDir + '/' + toName; + qDebug() << command; + + KProcess::execute(command); +} + #ifndef NDEBUG FILE *fp; #endif @@ -195,9 +210,12 @@ int main(int argc, char *argv[]) { ki18n("Copyright (c) 2012 Yuen Hoe (Jason moofang), 2018 Leonid Kalichkin (hellishnoob)")); KCmdLineArgs::init(argc, argv, &aboutData); KCmdLineOptions options; + options.add("b"); options.add("bin-path ", ki18n("Path to Firefox executable"), defaultBinPath); + options.add("p"); options.add("profile-dir ", ki18n("Path to Firefox profile directory"), defaultProfileDir.c_str()); - options.add("template-profile ", ki18n("Template profile name")); + options.add("t"); + options.add("template-profile-name ", ki18n("Template profile name")); options.add("+[profile-name]", ki18n("Firefox profile name")); KCmdLineArgs::addCmdLineOptions(options); @@ -205,7 +223,7 @@ int main(int argc, char *argv[]) { QString binPath = args->getOption("bin-path"); QString profileDir = args->getOption("profile-dir"); - QString templateProfile = args->getOption("template-profile"); + QString templateProfileName = args->getOption("template-profile-name"); QString profileName = nullptr; if (args->count() > 0) { @@ -216,7 +234,7 @@ int main(int argc, char *argv[]) { if (app.isSessionRestored()) { kRestoreMainWindows(); } else { - MainWindow *window = new MainWindow(binPath, profileDir, templateProfile, profileName); + MainWindow *window = new MainWindow(binPath, profileDir, templateProfileName, profileName); window->setObjectName("ActivityFoxWindow"); window->show(); } From 110f2c0e8c0c20d49bf117b8b75b2ef0293a4232 Mon Sep 17 00:00:00 2001 From: hellishnoob Date: Sat, 20 Jan 2018 12:03:15 +0300 Subject: [PATCH 05/12] add check if firefox was closed by our app, small bug fix --- main.cpp | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/main.cpp b/main.cpp index 5285fbd..78fafcc 100644 --- a/main.cpp +++ b/main.cpp @@ -25,12 +25,13 @@ class MainWindow : public KMainWindow { QString firefoxProfileName; QString firefoxBinPath; KProcess firefoxProcess; + bool firefoxWasClosedByThisApp = false; bool checkIfProfileExists(const QString &profileDir, const QString &profileName); QString getCurrentActivityId(); - void terminateFirefox(KProcess &process); + void closeFirefox(KProcess &process); void startFirefox(KProcess &firefoxProcess, const QString &binPath, const QString &profileName); @@ -38,6 +39,7 @@ class MainWindow : public KMainWindow { const QString &templateProfileName); void copyProfile(const QString &profileDir, const QString &fromName, const QString &toName); + }; // empty constructor is used when session is restored @@ -66,15 +68,17 @@ MainWindow::MainWindow(const QString &binPath, const QString &profileDir, const } void MainWindow::saveProperties(KConfigGroup &conf) { - qDebug() << "ActivityFox is closing, saving session"; + qDebug() << "ActivityFox is closing, saving the session"; + // check if Firefox is running if (firefoxProcess.pid() > 0) { conf.writeEntry("ffProfileName", firefoxProfileName); conf.writeEntry("ffBinPath", firefoxBinPath); - terminateFirefox(firefoxProcess); - } else { - // do not need to restore this app if Firefox was closed by user or any other reason + closeFirefox(firefoxProcess); + firefoxWasClosedByThisApp = true; + } else if (!firefoxWasClosedByThisApp) { + // don't need to restore this app and Firefox if Firefox was closed by user or unexpectedly qDebug("Firefox was closed, exiting from this app"); exit(0); } @@ -131,12 +135,13 @@ void MainWindow::startFirefox(KProcess &firefoxProcess, const QString &binPath, firefoxProcess.start(); } -void MainWindow::terminateFirefox(KProcess &process) { +// expects a non-zero pid, so the process must be running +void MainWindow::closeFirefox(KProcess &process) { char pid[21]; KProcess closeFirefox; sprintf(pid, "%d", process.pid()); - QString command = "wmctrl -i -c `wmctrl -lp | grep " + QString(pid) + " | cut -c -10"; + QString command = "wmctrl -i -c `wmctrl -lp | grep " + QString(pid) + " | cut -c -10`"; qDebug() << command; // trying to gracefully terminate Firefox by closing the window with wmctrl @@ -168,10 +173,8 @@ void MainWindow::copyProfile(const QString &profileDir, const QString &fromName, #ifndef NDEBUG FILE *fp; -#endif void myMessageOutput(QtMsgType type, const char *msg) { -#ifndef NDEBUG switch (type) { case QtDebugMsg: fprintf(fp, "Debug: %s\n", msg); @@ -190,15 +193,14 @@ void myMessageOutput(QtMsgType type, const char *msg) { fflush(fp); abort(); } -#endif } +#endif int main(int argc, char *argv[]) { #ifndef NDEBUG fp = fopen("activityfox.log", "a"); -#endif - qInstallMsgHandler(myMessageOutput); +#endif std::string defaultProfileDir = getenv("HOME"); defaultProfileDir = defaultProfileDir + '/' + defaultProfileDirTail; From 810474f0abf90d7d808219e628edc000b1a42d36 Mon Sep 17 00:00:00 2001 From: hellishnoob Date: Sun, 21 Jan 2018 01:40:11 +0300 Subject: [PATCH 06/12] rewrite everything, add chromium and Google Chrome support --- BrowserSessionManager.cpp | 92 ++++++++++++ BrowserSessionManager.h | 49 +++++++ CMakeLists.txt | 14 +- ChromiumSessionManager.cpp | 50 +++++++ ChromiumSessionManager.h | 29 ++++ FirefoxSessionManager.cpp | 61 ++++++++ FirefoxSessionManager.h | 28 ++++ GoogleChromeSessionManager.cpp | 18 +++ GoogleChromeSessionManager.h | 19 +++ main.cpp | 257 ++++++++------------------------- 10 files changed, 415 insertions(+), 202 deletions(-) create mode 100644 BrowserSessionManager.cpp create mode 100644 BrowserSessionManager.h create mode 100644 ChromiumSessionManager.cpp create mode 100644 ChromiumSessionManager.h create mode 100644 FirefoxSessionManager.cpp create mode 100644 FirefoxSessionManager.h create mode 100644 GoogleChromeSessionManager.cpp create mode 100644 GoogleChromeSessionManager.h diff --git a/BrowserSessionManager.cpp b/BrowserSessionManager.cpp new file mode 100644 index 0000000..a273308 --- /dev/null +++ b/BrowserSessionManager.cpp @@ -0,0 +1,92 @@ +#include "BrowserSessionManager.h" + +#include + +BrowserSessionManager::BrowserSessionManager(const QStringList allArguments, const QString &binPath, const QString &profileDir,const QString &templateProfileName, + const QString &profileName) : KApplication() { + restartArguments = allArguments; + browserBinPath = binPath; + browserProfileDir = profileDir; + browserTemplateProfileName = templateProfileName; + browserProfileName = profileName; +} + +void BrowserSessionManager::init() { + if (browserBinPath == nullptr || browserBinPath == "") { + browserBinPath = getDefaultBinPath(); + } + + if (browserProfileDir == nullptr || browserProfileDir == "") { + browserProfileDir = getDefaultProfileDir(); + } + + // if profile name was not specified, use current activity id + if (browserProfileName == nullptr || browserProfileName == "") { + browserProfileName = getCurrentActivityId(); + } + + if (!checkIfProfileExists(browserProfileDir, browserProfileName)) { + qDebug() << "Profile doesn't exist, creating it, name is: " << browserProfileName; + createProfile(browserBinPath, browserProfileDir, browserProfileName, browserTemplateProfileName); + } + + qDebug() << "Starting the browser"; + startBrowser(browserProcess, browserBinPath, browserProfileDir, browserProfileName); +} + +QString BrowserSessionManager::getCurrentActivityId() { + // get current activity id with QDBus, also possible to get by executing: + // qdbus org.kde.ActivityManager /ActivityManager/Activities org.kde.ActivityManager.Activities.CurrentActivity + QDBusInterface interface("org.kde.ActivityManager", + "/ActivityManager/Activities", + "org.kde.ActivityManager.Activities", + QDBusConnection::sessionBus()); + + QDBusMessage result = interface.call("CurrentActivity"); + return result.arguments().at(0).value(); +} + +// expects a non-zero pid, so the process must be running +void BrowserSessionManager::closeBrowser(KProcess &process) { + char pid[21]; + KProcess closeBrowser; + + sprintf(pid, "%d", process.pid()); + QString command = "wmctrl -i -c `wmctrl -lp | grep " + QString(pid) + " | cut -c -10`"; + qDebug() << command; + + // trying to gracefully terminate the browser by closing the window with wmctrl + closeBrowser.setShellCommand(command); + int exitcode = closeBrowser.execute(); + + // if wmctrl failed, terminate the browser normally + if (exitcode != 0) { + qDebug() << "wmctrl failed"; + process.terminate(); + } + + // wait until the browser is closed for at most 30 seconds, terminate if it wasn't closed after that + if (!process.waitForFinished()) { + qDebug() << "The browser hasn't finished in 30 seconds"; + process.terminate(); + } +} + + +void BrowserSessionManager::commitData(QSessionManager &sm) { + qDebug() << "Application::commitData was called"; + + // we will just tell the session manager to restart this application with the same arguments on session start + QStringList restartCommand = sm.restartCommand(); + restartCommand.append(restartArguments); + sm.setRestartCommand(restartCommand); + + if (browserProcess.pid() > 0) { + closeBrowser(browserProcess); + } else { + // the browser was closed by user or unexpectedly, so we don't want to restore it automatically + exit(0); + } + + KApplication::commitData(sm); +} diff --git a/BrowserSessionManager.h b/BrowserSessionManager.h new file mode 100644 index 0000000..55ad65d --- /dev/null +++ b/BrowserSessionManager.h @@ -0,0 +1,49 @@ +#ifndef ACTIVITYFOX_BROWSERSESSIONMANAGER_H +#define ACTIVITYFOX_BROWSERSESSIONMANAGER_H + +#include +#include +#include +#include + +class BrowserSessionManager : public KApplication { +public: + BrowserSessionManager(QStringList allArguments, const QString &binPath, const QString &profileDir, + const QString &templateProfileName, const QString &profileName); + + void commitData(QSessionManager &sm) override; + + virtual void init(); + +protected: + + +private: + QStringList restartArguments; + QString browserProfileName; + QString browserBinPath; + QString browserProfileDir; + QString browserTemplateProfileName; + KProcess browserProcess; + + QString getCurrentActivityId(); + + virtual bool checkIfProfileExists(const QString &profileDir, const QString &profileName) = 0; + + virtual void closeBrowser(KProcess &process); + + virtual void startBrowser(KProcess &browserProcess, const QString &binPath, const QString &profileDir, const QString &profileName) = 0; + + virtual void createProfile(const QString &binPath, const QString &profileDir, const QString &profileName, + const QString &templateProfileName) = 0; + + virtual void copyProfile(const QString &profileDir, const QString &fromName, const QString &toName) = 0; + + virtual QString getDefaultBinPath() = 0; + + virtual QString getDefaultProfileDir() = 0; + +}; + + +#endif //ACTIVITYFOX_BROWSERSESSIONMANAGER_H diff --git a/CMakeLists.txt b/CMakeLists.txt index e9aae4e..309b37a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,11 +4,19 @@ project (activityfox) find_package(KDE4 REQUIRED) include_directories(${KDE4_INCLUDES}) -set(tutorial2_SRCS - main.cpp +set(SOURCE_FILES + main.cpp + BrowserSessionManager.h + BrowserSessionManager.cpp + FirefoxSessionManager.h + FirefoxSessionManager.cpp + ChromiumSessionManager.h + ChromiumSessionManager.cpp + GoogleChromeSessionManager.h + GoogleChromeSessionManager.cpp ) -kde4_add_executable(activityfox ${tutorial2_SRCS}) +kde4_add_executable(activityfox ${SOURCE_FILES}) target_link_libraries(activityfox ${KDE4_KDEUI_LIBS}) set_property(TARGET activityfox PROPERTY CXX_STANDARD 11) \ No newline at end of file diff --git a/ChromiumSessionManager.cpp b/ChromiumSessionManager.cpp new file mode 100644 index 0000000..0cd873a --- /dev/null +++ b/ChromiumSessionManager.cpp @@ -0,0 +1,50 @@ +#include "ChromiumSessionManager.h" +#include + +bool ChromiumSessionManager::checkIfProfileExists(const QString &profileDir, const QString &profileName) { + DIR* dir = opendir((profileDir + '/' + profileName).toAscii()); + return dir != nullptr; +} + +void ChromiumSessionManager::createProfile(const QString &, const QString &profileDir, const QString &profileName, + const QString &templateProfileName) { + // if a template profile name is specified, copy this profile into the new profile directory + if (templateProfileName != nullptr && templateProfileName != "") { + copyProfile(profileDir, templateProfileName, profileName); + } + + // no need to call Chromium to create the profile, Chromium creates it automatically on the first run +} + +void ChromiumSessionManager::startBrowser(KProcess &browserProcess, const QString &binPath, const QString &profileDir, const QString &profileName) { + browserProcess << binPath << "--user-data-dir=" + profileDir + '/' + profileName; + qDebug() << browserProcess.program(); + browserProcess.start(); +} + +void ChromiumSessionManager::copyProfile(const QString &profileDir, const QString &fromName, const QString &toName) { + QStringList command; + + // make copy of a profile + command << "cp" << "-r" << profileDir + '/' + fromName << profileDir + '/' + toName; + qDebug() << command; + + KProcess::execute(command); +} + +QString ChromiumSessionManager::getDefaultProfileDir() { + static const char* defaultProfileDirTail = ".config/chromium"; + + std::string defaultProfileDir = getenv("HOME"); + return (defaultProfileDir + '/' + defaultProfileDirTail).c_str(); +} + +QString ChromiumSessionManager::getDefaultBinPath() { + static const char* defaultBinPath = "/usr/lib/chromium-browser/chromium-browser"; + + return defaultBinPath; +} + +ChromiumSessionManager::ChromiumSessionManager(QStringList allArguments, const QString &binPath, const QString &profileDir, const QString &templateProfileName, + const QString &profileName) : BrowserSessionManager(allArguments, binPath, profileDir, templateProfileName, + profileName) {} \ No newline at end of file diff --git a/ChromiumSessionManager.h b/ChromiumSessionManager.h new file mode 100644 index 0000000..4d59b35 --- /dev/null +++ b/ChromiumSessionManager.h @@ -0,0 +1,29 @@ +#ifndef ACTIVITYFOX_CHROMIUMSESSIONMANAGER_H +#define ACTIVITYFOX_CHROMIUMSESSIONMANAGER_H + +#include +#include "BrowserSessionManager.h" + +class ChromiumSessionManager : public BrowserSessionManager { +public: + ChromiumSessionManager(QStringList allArguments, const QString &binPath, const QString &profileDir, const QString &templateProfileName, + const QString &profileName); + +private: + + bool checkIfProfileExists(const QString &profileDir, const QString &profileName) override; + + void startBrowser(KProcess &browserProcess, const QString &binPath, const QString &profileDir, const QString &profileName) override; + + void createProfile(const QString &binPath, const QString &profileDir, const QString &profileName, + const QString &templateProfileName) override; + + void copyProfile(const QString &profileDir, const QString &fromName, const QString &toName) override; + + QString getDefaultBinPath() override; + + QString getDefaultProfileDir() override; +}; + + +#endif //ACTIVITYFOX_CHROMIUMSESSIONMANAGER_H diff --git a/FirefoxSessionManager.cpp b/FirefoxSessionManager.cpp new file mode 100644 index 0000000..76fe230 --- /dev/null +++ b/FirefoxSessionManager.cpp @@ -0,0 +1,61 @@ +#include "FirefoxSessionManager.h" + +bool FirefoxSessionManager::checkIfProfileExists(const QString &profileDir, const QString &profileName) { + KProcess checkIfProfileExists; + + QString command = "cat " + profileDir + '/' + "profiles.ini" + " | grep " + profileName; + qDebug() << command; + + checkIfProfileExists.setShellCommand(command); + return (checkIfProfileExists.execute() == 0); +} + +void FirefoxSessionManager::createProfile(const QString &binPath, const QString &profileDir, const QString &profileName, + const QString &templateProfileName) { + // if a template profile name is specified, copy this profile into the new profile directory + if (templateProfileName != nullptr && templateProfileName != "") { + copyProfile(profileDir, templateProfileName, profileName); + } + + QStringList command; + + // create a profile + command << binPath << "-CreateProfile" << profileName + ' ' + profileDir + '/' + profileName + << "-no-remote"; + qDebug() << command; + KProcess::execute(command); +} + +void FirefoxSessionManager::startBrowser(KProcess &browserProcess, const QString &binPath, const QString &, const QString &profileName) { + qDebug() << "FirefoxSessionManager::startBrowser was called"; + browserProcess << binPath << "-p" << profileName << "-no-remote"; + qDebug() << browserProcess.program(); + browserProcess.start(); +} + +void FirefoxSessionManager::copyProfile(const QString &profileDir, const QString &fromName, const QString &toName) { + QStringList command; + + // make copy of a profile + command << "cp" << "-r" << profileDir + '/' + fromName << profileDir + '/' + toName; + qDebug() << command; + + KProcess::execute(command); +} + +QString FirefoxSessionManager::getDefaultProfileDir() { + static const char* defaultProfileDirTail = ".mozilla/firefox"; + + std::string defaultProfileDir = getenv("HOME"); + return (defaultProfileDir + '/' + defaultProfileDirTail).c_str(); +} + +QString FirefoxSessionManager::getDefaultBinPath() { + static const char* defaultBinPath = "/usr/lib/firefox/firefox"; + + return defaultBinPath; +} + +FirefoxSessionManager::FirefoxSessionManager(QStringList allArguments, const QString &binPath, const QString &profileDir, const QString &templateProfileName, + const QString &profileName) : BrowserSessionManager(allArguments, binPath, profileDir, templateProfileName, + profileName) {} \ No newline at end of file diff --git a/FirefoxSessionManager.h b/FirefoxSessionManager.h new file mode 100644 index 0000000..1af74c1 --- /dev/null +++ b/FirefoxSessionManager.h @@ -0,0 +1,28 @@ +#ifndef ACTIVITYFOX_FIREFOXSESSIONMANAGER_H +#define ACTIVITYFOX_FIREFOXSESSIONMANAGER_H + +#include "BrowserSessionManager.h" + +class FirefoxSessionManager : public BrowserSessionManager { +public: + FirefoxSessionManager(QStringList allArguments, const QString &binPath, const QString &profileDir, const QString &templateProfileName, + const QString &profileName); + +private: + + bool checkIfProfileExists(const QString &profileDir, const QString &profileName) override; + + void startBrowser(KProcess &browserProcess, const QString &binPath, const QString &profileDir, const QString &profileName) override; + + void createProfile(const QString &binPath, const QString &profileDir, const QString &profileName, + const QString &templateProfileName) override; + + void copyProfile(const QString &profileDir, const QString &fromName, const QString &toName) override; + + QString getDefaultBinPath() override; + + QString getDefaultProfileDir() override; +}; + + +#endif //ACTIVITYFOX_FIREFOXSESSIONMANAGER_H diff --git a/GoogleChromeSessionManager.cpp b/GoogleChromeSessionManager.cpp new file mode 100644 index 0000000..aef6573 --- /dev/null +++ b/GoogleChromeSessionManager.cpp @@ -0,0 +1,18 @@ +#include "GoogleChromeSessionManager.h" + +QString GoogleChromeSessionManager::getDefaultProfileDir() { + static const char* defaultProfileDirTail = ".config/google-chrome"; + + std::string defaultProfileDir = getenv("HOME"); + return (defaultProfileDir + '/' + defaultProfileDirTail).c_str(); +} + +QString GoogleChromeSessionManager::getDefaultBinPath() { + static const char* defaultBinPath = "/opt/google/chrome/google-chrome"; + + return defaultBinPath; +} + +GoogleChromeSessionManager::GoogleChromeSessionManager(QStringList allArguments, const QString &binPath, const QString &profileDir, + const QString &templateProfileName, const QString &profileName) : ChromiumSessionManager(allArguments, + binPath, profileDir, templateProfileName, profileName) {} \ No newline at end of file diff --git a/GoogleChromeSessionManager.h b/GoogleChromeSessionManager.h new file mode 100644 index 0000000..641e818 --- /dev/null +++ b/GoogleChromeSessionManager.h @@ -0,0 +1,19 @@ +#ifndef ACTIVITYFOX_GOOGLECHROMESESSIONMANAGER_H +#define ACTIVITYFOX_GOOGLECHROMESESSIONMANAGER_H + +#include "ChromiumSessionManager.h" + +class GoogleChromeSessionManager : public ChromiumSessionManager { +public: + GoogleChromeSessionManager(QStringList allArguments, const QString &binPath, const QString &profileDir, const QString &templateProfileName, + const QString &profileName); + +private: + + QString getDefaultBinPath() override; + + QString getDefaultProfileDir() override; +}; + + +#endif //ACTIVITYFOX_GOOGLECHROMESESSIONMANAGER_H diff --git a/main.cpp b/main.cpp index 78fafcc..4e5e0d2 100644 --- a/main.cpp +++ b/main.cpp @@ -1,195 +1,35 @@ #include #include #include -#include -#include -#include -#include +#include +#include -const char *defaultProfileDirTail = ".mozilla/firefox"; -const char *defaultBinPath = "/usr/lib/firefox/firefox"; - -class MainWindow : public KMainWindow { -public: - MainWindow(const QString &binPath, const QString &profileDir, - const QString &templateProfileName, const QString &profileName); - - MainWindow(); - -protected: - void saveProperties(KConfigGroup &) override; - - void readProperties(const KConfigGroup &) override; - -private: - QString firefoxProfileName; - QString firefoxBinPath; - KProcess firefoxProcess; - bool firefoxWasClosedByThisApp = false; - - bool checkIfProfileExists(const QString &profileDir, const QString &profileName); - - QString getCurrentActivityId(); - - void closeFirefox(KProcess &process); - - void startFirefox(KProcess &firefoxProcess, const QString &binPath, const QString &profileName); - - void createProfile(const QString &binPath, const QString &profileDir, const QString &profileName, - const QString &templateProfileName); - - void copyProfile(const QString &profileDir, const QString &fromName, const QString &toName); - -}; - -// empty constructor is used when session is restored -MainWindow::MainWindow() : KMainWindow() { - qDebug() << "MainWindow: Session was restored"; -} - -MainWindow::MainWindow(const QString &binPath, const QString &profileDir, const QString &templateProfileName, - const QString &profileName) : KMainWindow() { - // if profile name was not specified, use current activity id - if (profileName == nullptr || profileName == "") { - firefoxProfileName = getCurrentActivityId(); - } else { - firefoxProfileName = profileName; - } - - firefoxBinPath = binPath; - - if (!checkIfProfileExists(profileDir, firefoxProfileName)) { - qDebug() << "Profile doesn't exist, creating it, name is: " << firefoxProfileName; - createProfile(firefoxBinPath, profileDir, firefoxProfileName, templateProfileName); - } - - qDebug() << "Starting firefox"; - startFirefox(firefoxProcess, firefoxBinPath, firefoxProfileName); -} - -void MainWindow::saveProperties(KConfigGroup &conf) { - qDebug() << "ActivityFox is closing, saving the session"; - - // check if Firefox is running - if (firefoxProcess.pid() > 0) { - conf.writeEntry("ffProfileName", firefoxProfileName); - conf.writeEntry("ffBinPath", firefoxBinPath); - - closeFirefox(firefoxProcess); - firefoxWasClosedByThisApp = true; - } else if (!firefoxWasClosedByThisApp) { - // don't need to restore this app and Firefox if Firefox was closed by user or unexpectedly - qDebug("Firefox was closed, exiting from this app"); - exit(0); - } -} - -void MainWindow::readProperties(const KConfigGroup &conf) { - qDebug() << "ActivityFox is restored, starting Firefox"; - // restart Firefox - firefoxProfileName = conf.readEntry("ffProfileName", QString()); - firefoxBinPath = conf.readEntry("ffBinPath", QString()); - startFirefox(firefoxProcess, firefoxBinPath, firefoxProfileName); -} - -QString MainWindow::getCurrentActivityId() { - // get current activity id with QDBus, also possible to get by executing: - // qdbus org.kde.ActivityManager /ActivityManager/Activities org.kde.ActivityManager.Activities.CurrentActivity - QDBusInterface interface("org.kde.ActivityManager", - "/ActivityManager/Activities", - "org.kde.ActivityManager.Activities", - QDBusConnection::sessionBus()); - - QDBusMessage result = interface.call("CurrentActivity"); - return result.arguments().at(0).value(); -} - -bool MainWindow::checkIfProfileExists(const QString &profileDir, const QString &profileName) { - KProcess checkIfProfileExists; - - QString command = "cat " + profileDir + '/' + "profiles.ini" + " | grep " + profileName; - qDebug() << command; - - checkIfProfileExists.setShellCommand(command); - return (checkIfProfileExists.execute() == 0); -} - -void MainWindow::createProfile(const QString &binPath, const QString &profileDir, const QString &profileName, - const QString &templateProfileName) { - // if a template profile name is specified, copy this profile into the new profile directory - if (templateProfileName != nullptr && templateProfileName != "") { - copyProfile(profileDir, templateProfileName, profileName); - } - - QStringList command; - - // create a profile - command << binPath << "-CreateProfile" << profileName + ' ' + profileDir + '/' + profileName - << "-no-remote"; - qDebug() << command; - KProcess::execute(command); -} - -void MainWindow::startFirefox(KProcess &firefoxProcess, const QString &binPath, const QString &profileName) { - firefoxProcess << binPath << "-p" << profileName << "-no-remote"; - firefoxProcess.start(); -} - -// expects a non-zero pid, so the process must be running -void MainWindow::closeFirefox(KProcess &process) { - char pid[21]; - KProcess closeFirefox; - - sprintf(pid, "%d", process.pid()); - QString command = "wmctrl -i -c `wmctrl -lp | grep " + QString(pid) + " | cut -c -10`"; - qDebug() << command; - - // trying to gracefully terminate Firefox by closing the window with wmctrl - closeFirefox.setShellCommand(command); - int exitcode = closeFirefox.execute(); - - // if wmctrl failed, terminate Firefox normally - if (exitcode != 0) { - qDebug() << "wmctrl failed"; - process.terminate(); - } - - // wait until Firefox is closed for at most 30 seconds, terminate if it wasn't closed after that - if (!process.waitForFinished()) { - qDebug() << "Firefox hasn't finished in 30 seconds"; - process.terminate(); - } -} - -void MainWindow::copyProfile(const QString &profileDir, const QString &fromName, const QString &toName) { - QStringList command; - - // make copy of a profile - command << "cp" << "-r" << profileDir + '/' + fromName << profileDir + '/' + toName; - qDebug() << command; - - KProcess::execute(command); -} +#include "BrowserSessionManager.h" +#include "FirefoxSessionManager.h" +#include "ChromiumSessionManager.h" +#include "GoogleChromeSessionManager.h" #ifndef NDEBUG FILE *fp; +static KCmdLineArgs *parseCommandLineArguments(int argc, char **argv); + void myMessageOutput(QtMsgType type, const char *msg) { switch (type) { case QtDebugMsg: - fprintf(fp, "Debug: %s\n", msg); + fprintf(fp, "Debug (%d): %s\n", getpid(), msg); fflush(fp); break; case QtWarningMsg: - fprintf(fp, "Warning: %s\n", msg); + fprintf(fp, "Warning (%d): %s\n", getpid(), msg); fflush(fp); break; case QtCriticalMsg: - fprintf(fp, "Critical: %s\n", msg); + fprintf(fp, "Critical (%d): %s\n", getpid(), msg); fflush(fp); break; case QtFatalMsg: - fprintf(fp, "Fatal: %s\n", msg); + fprintf(fp, "Fatal (%d): %s\n", getpid(), msg); fflush(fp); abort(); } @@ -202,46 +42,39 @@ int main(int argc, char *argv[]) { qInstallMsgHandler(myMessageOutput); #endif - std::string defaultProfileDir = getenv("HOME"); - defaultProfileDir = defaultProfileDir + '/' + defaultProfileDirTail; - - KAboutData aboutData("activityfox", nullptr, - ki18n("ActivityFox"), "0.1", - ki18n("Help Firefox work with KDE activities."), - KAboutData::License_GPL, - ki18n("Copyright (c) 2012 Yuen Hoe (Jason moofang), 2018 Leonid Kalichkin (hellishnoob)")); - KCmdLineArgs::init(argc, argv, &aboutData); - KCmdLineOptions options; - options.add("b"); - options.add("bin-path ", ki18n("Path to Firefox executable"), defaultBinPath); - options.add("p"); - options.add("profile-dir ", ki18n("Path to Firefox profile directory"), defaultProfileDir.c_str()); - options.add("t"); - options.add("template-profile-name ", ki18n("Template profile name")); - options.add("+[profile-name]", ki18n("Firefox profile name")); - KCmdLineArgs::addCmdLineOptions(options); - - KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + KCmdLineArgs *args = parseCommandLineArguments(argc, argv); QString binPath = args->getOption("bin-path"); QString profileDir = args->getOption("profile-dir"); QString templateProfileName = args->getOption("template-profile-name"); QString profileName = nullptr; + bool useChromium = args->isSet("chromium"); + bool useGoogleChrome = args->isSet("google-chrome"); + if (args->count() > 0) { profileName = args->arg(0); } - KApplication app; - if (app.isSessionRestored()) { - kRestoreMainWindows(); + BrowserSessionManager* sm; + QStringList allArguments = args->allArguments(); + allArguments.removeFirst(); + + if (useChromium) { + sm = new ChromiumSessionManager(allArguments, binPath, profileDir, templateProfileName, profileName); + } else if (useGoogleChrome) { + sm = new GoogleChromeSessionManager(allArguments, binPath, profileDir, templateProfileName, profileName); } else { - MainWindow *window = new MainWindow(binPath, profileDir, templateProfileName, profileName); - window->setObjectName("ActivityFoxWindow"); - window->show(); + sm = new FirefoxSessionManager(allArguments, binPath, profileDir, templateProfileName, profileName); } - int result = app.exec(); + // having a visible window is necessary for session management + QWidget* window = new QWidget(); + window->show(); + + sm->init(); + + int result = sm->exec(); #ifndef NDEBUG fclose(fp); @@ -249,3 +82,29 @@ int main(int argc, char *argv[]) { return result; } + +KCmdLineArgs *parseCommandLineArguments(int argc, char **argv) { + static KAboutData aboutData("activityfox", nullptr, + ki18n("ActivityFox"), "0.1", + ki18n("Help Firefox and Chromium-based browsers work with KDE activities."), + KAboutData::License_GPL, + ki18n("Copyright (c) 2012 Yuen Hoe (Jason moofang), 2018 Leonid Kalichkin (hellishnoob)")); + + KCmdLineArgs::init(argc, argv, &aboutData); + + static KCmdLineOptions options; + options.add("c"); + options.add("chromium", ki18n("Use proxy for Chromium-based browsers")); + options.add("g"); + options.add("google-chrome", ki18n("Use proxy for Google Chrome")); + options.add("b"); + options.add("bin-path ", ki18n("Path to the browser executable"), nullptr); + options.add("p"); + options.add("profile-dir ", ki18n("Path to the browser profile directory"), nullptr); + options.add("t"); + options.add("template-profile-name ", ki18n("Profile name of a template to be used for profile creation")); + options.add("+[profile-name]", ki18n("Profile name")); + KCmdLineArgs::addCmdLineOptions(options); + + return KCmdLineArgs::parsedArgs(); +} From 107ba18682cb905ced59df1b6864230da43e0c66 Mon Sep 17 00:00:00 2001 From: hellishnoob Date: Sun, 21 Jan 2018 11:27:29 +0300 Subject: [PATCH 07/12] clean up code --- BrowserSessionManager.cpp | 7 +++---- BrowserSessionManager.h | 19 ++++++++++++++----- CMakeLists.txt | 4 +++- ChromiumSessionManager.cpp | 21 ++++++++++++--------- ChromiumSessionManager.h | 8 +++++--- FirefoxSessionManager.cpp | 18 +++++++++++------- FirefoxSessionManager.h | 6 ++++-- GoogleChromeSessionManager.cpp | 12 +++++++----- GoogleChromeSessionManager.h | 3 ++- main.cpp | 25 ++++++++++++++++--------- 10 files changed, 77 insertions(+), 46 deletions(-) diff --git a/BrowserSessionManager.cpp b/BrowserSessionManager.cpp index a273308..5237af1 100644 --- a/BrowserSessionManager.cpp +++ b/BrowserSessionManager.cpp @@ -1,9 +1,8 @@ #include "BrowserSessionManager.h" -#include - -BrowserSessionManager::BrowserSessionManager(const QStringList allArguments, const QString &binPath, const QString &profileDir,const QString &templateProfileName, - const QString &profileName) : KApplication() { +BrowserSessionManager::BrowserSessionManager(const QStringList allArguments, const QString &binPath, + const QString &profileDir, const QString &templateProfileName, + const QString &profileName) : KApplication() { restartArguments = allArguments; browserBinPath = binPath; browserProfileDir = profileDir; diff --git a/BrowserSessionManager.h b/BrowserSessionManager.h index 55ad65d..25e672b 100644 --- a/BrowserSessionManager.h +++ b/BrowserSessionManager.h @@ -1,15 +1,23 @@ #ifndef ACTIVITYFOX_BROWSERSESSIONMANAGER_H #define ACTIVITYFOX_BROWSERSESSIONMANAGER_H +#ifdef NDEBUG +#define QT_NO_DEBUG_OUTPUT +#else + +#include + +#endif + #include -#include #include -#include +#include +#include class BrowserSessionManager : public KApplication { public: BrowserSessionManager(QStringList allArguments, const QString &binPath, const QString &profileDir, - const QString &templateProfileName, const QString &profileName); + const QString &templateProfileName, const QString &profileName); void commitData(QSessionManager &sm) override; @@ -32,10 +40,11 @@ class BrowserSessionManager : public KApplication { virtual void closeBrowser(KProcess &process); - virtual void startBrowser(KProcess &browserProcess, const QString &binPath, const QString &profileDir, const QString &profileName) = 0; + virtual void startBrowser(KProcess &browserProcess, const QString &binPath, const QString &profileDir, + const QString &profileName) = 0; virtual void createProfile(const QString &binPath, const QString &profileDir, const QString &profileName, - const QString &templateProfileName) = 0; + const QString &templateProfileName) = 0; virtual void copyProfile(const QString &profileDir, const QString &fromName, const QString &toName) = 0; diff --git a/CMakeLists.txt b/CMakeLists.txt index 309b37a..358b845 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,8 @@ cmake_minimum_required(VERSION 3.1) project (activityfox) - + +cmake_policy(SET CMP0059 NEW) + find_package(KDE4 REQUIRED) include_directories(${KDE4_INCLUDES}) diff --git a/ChromiumSessionManager.cpp b/ChromiumSessionManager.cpp index 0cd873a..3c1855a 100644 --- a/ChromiumSessionManager.cpp +++ b/ChromiumSessionManager.cpp @@ -1,13 +1,12 @@ #include "ChromiumSessionManager.h" -#include bool ChromiumSessionManager::checkIfProfileExists(const QString &profileDir, const QString &profileName) { - DIR* dir = opendir((profileDir + '/' + profileName).toAscii()); + DIR *dir = opendir((profileDir + '/' + profileName).toAscii()); return dir != nullptr; } void ChromiumSessionManager::createProfile(const QString &, const QString &profileDir, const QString &profileName, - const QString &templateProfileName) { + const QString &templateProfileName) { // if a template profile name is specified, copy this profile into the new profile directory if (templateProfileName != nullptr && templateProfileName != "") { copyProfile(profileDir, templateProfileName, profileName); @@ -16,7 +15,8 @@ void ChromiumSessionManager::createProfile(const QString &, const QString &profi // no need to call Chromium to create the profile, Chromium creates it automatically on the first run } -void ChromiumSessionManager::startBrowser(KProcess &browserProcess, const QString &binPath, const QString &profileDir, const QString &profileName) { +void ChromiumSessionManager::startBrowser(KProcess &browserProcess, const QString &binPath, const QString &profileDir, + const QString &profileName) { browserProcess << binPath << "--user-data-dir=" + profileDir + '/' + profileName; qDebug() << browserProcess.program(); browserProcess.start(); @@ -33,18 +33,21 @@ void ChromiumSessionManager::copyProfile(const QString &profileDir, const QStrin } QString ChromiumSessionManager::getDefaultProfileDir() { - static const char* defaultProfileDirTail = ".config/chromium"; + static const char *defaultProfileDirTail = ".config/chromium"; std::string defaultProfileDir = getenv("HOME"); return (defaultProfileDir + '/' + defaultProfileDirTail).c_str(); } QString ChromiumSessionManager::getDefaultBinPath() { - static const char* defaultBinPath = "/usr/lib/chromium-browser/chromium-browser"; + static const char *defaultBinPath = "/usr/lib/chromium-browser/chromium-browser"; return defaultBinPath; } -ChromiumSessionManager::ChromiumSessionManager(QStringList allArguments, const QString &binPath, const QString &profileDir, const QString &templateProfileName, - const QString &profileName) : BrowserSessionManager(allArguments, binPath, profileDir, templateProfileName, - profileName) {} \ No newline at end of file +ChromiumSessionManager::ChromiumSessionManager(QStringList allArguments, const QString &binPath, + const QString &profileDir, const QString &templateProfileName, + const QString &profileName) : BrowserSessionManager(allArguments, + binPath, profileDir, + templateProfileName, + profileName) {} \ No newline at end of file diff --git a/ChromiumSessionManager.h b/ChromiumSessionManager.h index 4d59b35..ef0118c 100644 --- a/ChromiumSessionManager.h +++ b/ChromiumSessionManager.h @@ -1,19 +1,21 @@ #ifndef ACTIVITYFOX_CHROMIUMSESSIONMANAGER_H #define ACTIVITYFOX_CHROMIUMSESSIONMANAGER_H -#include +#include #include "BrowserSessionManager.h" class ChromiumSessionManager : public BrowserSessionManager { public: - ChromiumSessionManager(QStringList allArguments, const QString &binPath, const QString &profileDir, const QString &templateProfileName, + ChromiumSessionManager(QStringList allArguments, const QString &binPath, const QString &profileDir, + const QString &templateProfileName, const QString &profileName); private: bool checkIfProfileExists(const QString &profileDir, const QString &profileName) override; - void startBrowser(KProcess &browserProcess, const QString &binPath, const QString &profileDir, const QString &profileName) override; + void startBrowser(KProcess &browserProcess, const QString &binPath, const QString &profileDir, + const QString &profileName) override; void createProfile(const QString &binPath, const QString &profileDir, const QString &profileName, const QString &templateProfileName) override; diff --git a/FirefoxSessionManager.cpp b/FirefoxSessionManager.cpp index 76fe230..6a8fa49 100644 --- a/FirefoxSessionManager.cpp +++ b/FirefoxSessionManager.cpp @@ -11,7 +11,7 @@ bool FirefoxSessionManager::checkIfProfileExists(const QString &profileDir, cons } void FirefoxSessionManager::createProfile(const QString &binPath, const QString &profileDir, const QString &profileName, - const QString &templateProfileName) { + const QString &templateProfileName) { // if a template profile name is specified, copy this profile into the new profile directory if (templateProfileName != nullptr && templateProfileName != "") { copyProfile(profileDir, templateProfileName, profileName); @@ -26,7 +26,8 @@ void FirefoxSessionManager::createProfile(const QString &binPath, const QString KProcess::execute(command); } -void FirefoxSessionManager::startBrowser(KProcess &browserProcess, const QString &binPath, const QString &, const QString &profileName) { +void FirefoxSessionManager::startBrowser(KProcess &browserProcess, const QString &binPath, const QString &, + const QString &profileName) { qDebug() << "FirefoxSessionManager::startBrowser was called"; browserProcess << binPath << "-p" << profileName << "-no-remote"; qDebug() << browserProcess.program(); @@ -44,18 +45,21 @@ void FirefoxSessionManager::copyProfile(const QString &profileDir, const QString } QString FirefoxSessionManager::getDefaultProfileDir() { - static const char* defaultProfileDirTail = ".mozilla/firefox"; + static const char *defaultProfileDirTail = ".mozilla/firefox"; std::string defaultProfileDir = getenv("HOME"); return (defaultProfileDir + '/' + defaultProfileDirTail).c_str(); } QString FirefoxSessionManager::getDefaultBinPath() { - static const char* defaultBinPath = "/usr/lib/firefox/firefox"; + static const char *defaultBinPath = "/usr/lib/firefox/firefox"; return defaultBinPath; } -FirefoxSessionManager::FirefoxSessionManager(QStringList allArguments, const QString &binPath, const QString &profileDir, const QString &templateProfileName, - const QString &profileName) : BrowserSessionManager(allArguments, binPath, profileDir, templateProfileName, - profileName) {} \ No newline at end of file +FirefoxSessionManager::FirefoxSessionManager(QStringList allArguments, const QString &binPath, + const QString &profileDir, const QString &templateProfileName, + const QString &profileName) : BrowserSessionManager(allArguments, binPath, + profileDir, + templateProfileName, + profileName) {} \ No newline at end of file diff --git a/FirefoxSessionManager.h b/FirefoxSessionManager.h index 1af74c1..adea8c5 100644 --- a/FirefoxSessionManager.h +++ b/FirefoxSessionManager.h @@ -5,14 +5,16 @@ class FirefoxSessionManager : public BrowserSessionManager { public: - FirefoxSessionManager(QStringList allArguments, const QString &binPath, const QString &profileDir, const QString &templateProfileName, + FirefoxSessionManager(QStringList allArguments, const QString &binPath, const QString &profileDir, + const QString &templateProfileName, const QString &profileName); private: bool checkIfProfileExists(const QString &profileDir, const QString &profileName) override; - void startBrowser(KProcess &browserProcess, const QString &binPath, const QString &profileDir, const QString &profileName) override; + void startBrowser(KProcess &browserProcess, const QString &binPath, const QString &profileDir, + const QString &profileName) override; void createProfile(const QString &binPath, const QString &profileDir, const QString &profileName, const QString &templateProfileName) override; diff --git a/GoogleChromeSessionManager.cpp b/GoogleChromeSessionManager.cpp index aef6573..fd6fc2a 100644 --- a/GoogleChromeSessionManager.cpp +++ b/GoogleChromeSessionManager.cpp @@ -1,18 +1,20 @@ #include "GoogleChromeSessionManager.h" QString GoogleChromeSessionManager::getDefaultProfileDir() { - static const char* defaultProfileDirTail = ".config/google-chrome"; + static const char *defaultProfileDirTail = ".config/google-chrome"; std::string defaultProfileDir = getenv("HOME"); return (defaultProfileDir + '/' + defaultProfileDirTail).c_str(); } QString GoogleChromeSessionManager::getDefaultBinPath() { - static const char* defaultBinPath = "/opt/google/chrome/google-chrome"; + static const char *defaultBinPath = "/opt/google/chrome/google-chrome"; return defaultBinPath; } -GoogleChromeSessionManager::GoogleChromeSessionManager(QStringList allArguments, const QString &binPath, const QString &profileDir, - const QString &templateProfileName, const QString &profileName) : ChromiumSessionManager(allArguments, - binPath, profileDir, templateProfileName, profileName) {} \ No newline at end of file +GoogleChromeSessionManager::GoogleChromeSessionManager(QStringList allArguments, const QString &binPath, + const QString &profileDir, + const QString &templateProfileName, const QString &profileName) + : ChromiumSessionManager(allArguments, + binPath, profileDir, templateProfileName, profileName) {} \ No newline at end of file diff --git a/GoogleChromeSessionManager.h b/GoogleChromeSessionManager.h index 641e818..fb2319c 100644 --- a/GoogleChromeSessionManager.h +++ b/GoogleChromeSessionManager.h @@ -5,7 +5,8 @@ class GoogleChromeSessionManager : public ChromiumSessionManager { public: - GoogleChromeSessionManager(QStringList allArguments, const QString &binPath, const QString &profileDir, const QString &templateProfileName, + GoogleChromeSessionManager(QStringList allArguments, const QString &binPath, const QString &profileDir, + const QString &templateProfileName, const QString &profileName); private: diff --git a/main.cpp b/main.cpp index 4e5e0d2..5edcfb8 100644 --- a/main.cpp +++ b/main.cpp @@ -1,7 +1,10 @@ +#ifdef NDEBUG +#define QT_NO_DEBUG_OUTPUT +#endif + #include #include #include -#include #include #include "BrowserSessionManager.h" @@ -10,9 +13,10 @@ #include "GoogleChromeSessionManager.h" #ifndef NDEBUG -FILE *fp; -static KCmdLineArgs *parseCommandLineArguments(int argc, char **argv); +#include + +FILE *fp; void myMessageOutput(QtMsgType type, const char *msg) { switch (type) { @@ -34,8 +38,11 @@ void myMessageOutput(QtMsgType type, const char *msg) { abort(); } } + #endif +KCmdLineArgs *parseCommandLineArguments(int argc, char **argv); + int main(int argc, char *argv[]) { #ifndef NDEBUG fp = fopen("activityfox.log", "a"); @@ -56,7 +63,7 @@ int main(int argc, char *argv[]) { profileName = args->arg(0); } - BrowserSessionManager* sm; + BrowserSessionManager *sm; QStringList allArguments = args->allArguments(); allArguments.removeFirst(); @@ -69,7 +76,7 @@ int main(int argc, char *argv[]) { } // having a visible window is necessary for session management - QWidget* window = new QWidget(); + QWidget *window = new QWidget(); window->show(); sm->init(); @@ -85,10 +92,10 @@ int main(int argc, char *argv[]) { KCmdLineArgs *parseCommandLineArguments(int argc, char **argv) { static KAboutData aboutData("activityfox", nullptr, - ki18n("ActivityFox"), "0.1", - ki18n("Help Firefox and Chromium-based browsers work with KDE activities."), - KAboutData::License_GPL, - ki18n("Copyright (c) 2012 Yuen Hoe (Jason moofang), 2018 Leonid Kalichkin (hellishnoob)")); + ki18n("ActivityFox"), "0.1", + ki18n("Help Firefox and Chromium-based browsers work with KDE activities."), + KAboutData::License_GPL, + ki18n("Copyright (c) 2012 Yuen Hoe (Jason moofang), 2018 Leonid Kalichkin (hellishnoob)")); KCmdLineArgs::init(argc, argv, &aboutData); From 72775e38016908a09b9d06d1a8f0b6d32e857018 Mon Sep 17 00:00:00 2001 From: hellishnoob Date: Sun, 21 Jan 2018 16:22:10 +0300 Subject: [PATCH 08/12] rewrite everything again, make browsers start on their home activity after reboot --- BrowserManager.cpp | 72 +++++++ BrowserSessionManager.h => BrowserManager.h | 38 ++-- BrowserSessionManager.cpp | 91 -------- CMakeLists.txt | 16 +- ChromiumManager.cpp | 52 +++++ FirefoxSessionManager.h => ChromiumManager.h | 17 +- ChromiumSessionManager.cpp | 53 ----- ...oxSessionManager.cpp => FirefoxManager.cpp | 32 +-- ChromiumSessionManager.h => FirefoxManager.h | 17 +- GoogleChromeManager.cpp | 19 ++ GoogleChromeManager.h | 20 ++ GoogleChromeSessionManager.cpp | 20 -- GoogleChromeSessionManager.h | 20 -- main.cpp | 202 ++++++++++++++---- 14 files changed, 382 insertions(+), 287 deletions(-) create mode 100644 BrowserManager.cpp rename BrowserSessionManager.h => BrowserManager.h (50%) delete mode 100644 BrowserSessionManager.cpp create mode 100644 ChromiumManager.cpp rename FirefoxSessionManager.h => ChromiumManager.h (59%) delete mode 100644 ChromiumSessionManager.cpp rename FirefoxSessionManager.cpp => FirefoxManager.cpp (55%) rename ChromiumSessionManager.h => FirefoxManager.h (61%) create mode 100644 GoogleChromeManager.cpp create mode 100644 GoogleChromeManager.h delete mode 100644 GoogleChromeSessionManager.cpp delete mode 100644 GoogleChromeSessionManager.h diff --git a/BrowserManager.cpp b/BrowserManager.cpp new file mode 100644 index 0000000..39a5c6a --- /dev/null +++ b/BrowserManager.cpp @@ -0,0 +1,72 @@ +#include "BrowserManager.h" + +BrowserManager::BrowserManager(const QString &startUpActivityId, const QString &binPath, + const QString &profileDir, const QString &templateProfileName, + const QString &profileName) { + this->startUpActivityId = startUpActivityId; + this->binPath = binPath; + this->profileDir = profileDir; + this->templateProfileName = templateProfileName; + this->profileName = profileName; +} + +void BrowserManager::init() { + if (binPath == nullptr || binPath == "") { + binPath = getDefaultBinPath(); + } + + if (profileDir == nullptr || profileDir == "") { + profileDir = getDefaultProfileDir(); + } + + // if profile name was not specified, use current activity id + if (profileName == nullptr || profileName == "") { + profileName = startUpActivityId; + } + + if (!checkIfProfileExists(profileDir, profileName)) { + qDebug() << "Profile doesn't exist, creating it, name is: " << profileName; + createProfile(binPath, profileDir, profileName, templateProfileName); + } + + qDebug() << "Starting the browser"; + startBrowser(process, binPath, profileDir, profileName); +} + +// expects a non-zero pid, so the process must be running +void BrowserManager::closeBrowser(KProcess &process) { + char pid[21]; + KProcess closeBrowser; + + sprintf(pid, "%d", process.pid()); + QString command = "wmctrl -i -c `wmctrl -lp | grep " + QString(pid) + " | cut -c -10`"; + qDebug() << command; + + // trying to gracefully terminate the browser by closing the window with wmctrl + closeBrowser.setShellCommand(command); + int exitcode = closeBrowser.execute(); + + // if wmctrl failed, terminate the browser normally + if (exitcode != 0) { + qDebug() << "wmctrl failed"; + process.terminate(); + } + + // wait until the browser is closed for at most 30 seconds, terminate if it wasn't closed after that + if (!process.waitForFinished()) { + qDebug() << "The browser hasn't finished in 30 seconds"; + process.terminate(); + } +} + + +void BrowserManager::onSaveProperties() { + qDebug() << "onSaveProperties() was called"; + + if (process.pid() > 0) { + closeBrowser(process); + } else { + // the browser was closed by user or unexpectedly, so we don't want to restore it automatically + exit(0); + } +} diff --git a/BrowserSessionManager.h b/BrowserManager.h similarity index 50% rename from BrowserSessionManager.h rename to BrowserManager.h index 25e672b..d83c36d 100644 --- a/BrowserSessionManager.h +++ b/BrowserManager.h @@ -1,5 +1,5 @@ -#ifndef ACTIVITYFOX_BROWSERSESSIONMANAGER_H -#define ACTIVITYFOX_BROWSERSESSIONMANAGER_H +#ifndef ACTIVITYFOX_BROWSERMANAGER_H +#define ACTIVITYFOX_BROWSERMANAGER_H #ifdef NDEBUG #define QT_NO_DEBUG_OUTPUT @@ -14,33 +14,37 @@ #include #include -class BrowserSessionManager : public KApplication { +class BrowserManager { public: - BrowserSessionManager(QStringList allArguments, const QString &binPath, const QString &profileDir, - const QString &templateProfileName, const QString &profileName); + BrowserManager(const QString &startUpActivityId, const QString &binPath, const QString &profileDir, + const QString &templateProfileName, const QString &profileName); - void commitData(QSessionManager &sm) override; + virtual ~BrowserManager() = default; - virtual void init(); + void init(); + + void onSaveProperties(); + + enum Browser { + CHROMIUM, GOOGLE_CHROME, FIREFOX + }; protected: private: - QStringList restartArguments; - QString browserProfileName; - QString browserBinPath; - QString browserProfileDir; - QString browserTemplateProfileName; - KProcess browserProcess; - - QString getCurrentActivityId(); + QString startUpActivityId; + QString profileName; + QString binPath; + QString profileDir; + QString templateProfileName; + KProcess process; virtual bool checkIfProfileExists(const QString &profileDir, const QString &profileName) = 0; virtual void closeBrowser(KProcess &process); - virtual void startBrowser(KProcess &browserProcess, const QString &binPath, const QString &profileDir, + virtual void startBrowser(KProcess &process, const QString &binPath, const QString &profileDir, const QString &profileName) = 0; virtual void createProfile(const QString &binPath, const QString &profileDir, const QString &profileName, @@ -55,4 +59,4 @@ class BrowserSessionManager : public KApplication { }; -#endif //ACTIVITYFOX_BROWSERSESSIONMANAGER_H +#endif //ACTIVITYFOX_BROWSERMANAGER_H diff --git a/BrowserSessionManager.cpp b/BrowserSessionManager.cpp deleted file mode 100644 index 5237af1..0000000 --- a/BrowserSessionManager.cpp +++ /dev/null @@ -1,91 +0,0 @@ -#include "BrowserSessionManager.h" - -BrowserSessionManager::BrowserSessionManager(const QStringList allArguments, const QString &binPath, - const QString &profileDir, const QString &templateProfileName, - const QString &profileName) : KApplication() { - restartArguments = allArguments; - browserBinPath = binPath; - browserProfileDir = profileDir; - browserTemplateProfileName = templateProfileName; - browserProfileName = profileName; -} - -void BrowserSessionManager::init() { - if (browserBinPath == nullptr || browserBinPath == "") { - browserBinPath = getDefaultBinPath(); - } - - if (browserProfileDir == nullptr || browserProfileDir == "") { - browserProfileDir = getDefaultProfileDir(); - } - - // if profile name was not specified, use current activity id - if (browserProfileName == nullptr || browserProfileName == "") { - browserProfileName = getCurrentActivityId(); - } - - if (!checkIfProfileExists(browserProfileDir, browserProfileName)) { - qDebug() << "Profile doesn't exist, creating it, name is: " << browserProfileName; - createProfile(browserBinPath, browserProfileDir, browserProfileName, browserTemplateProfileName); - } - - qDebug() << "Starting the browser"; - startBrowser(browserProcess, browserBinPath, browserProfileDir, browserProfileName); -} - -QString BrowserSessionManager::getCurrentActivityId() { - // get current activity id with QDBus, also possible to get by executing: - // qdbus org.kde.ActivityManager /ActivityManager/Activities org.kde.ActivityManager.Activities.CurrentActivity - QDBusInterface interface("org.kde.ActivityManager", - "/ActivityManager/Activities", - "org.kde.ActivityManager.Activities", - QDBusConnection::sessionBus()); - - QDBusMessage result = interface.call("CurrentActivity"); - return result.arguments().at(0).value(); -} - -// expects a non-zero pid, so the process must be running -void BrowserSessionManager::closeBrowser(KProcess &process) { - char pid[21]; - KProcess closeBrowser; - - sprintf(pid, "%d", process.pid()); - QString command = "wmctrl -i -c `wmctrl -lp | grep " + QString(pid) + " | cut -c -10`"; - qDebug() << command; - - // trying to gracefully terminate the browser by closing the window with wmctrl - closeBrowser.setShellCommand(command); - int exitcode = closeBrowser.execute(); - - // if wmctrl failed, terminate the browser normally - if (exitcode != 0) { - qDebug() << "wmctrl failed"; - process.terminate(); - } - - // wait until the browser is closed for at most 30 seconds, terminate if it wasn't closed after that - if (!process.waitForFinished()) { - qDebug() << "The browser hasn't finished in 30 seconds"; - process.terminate(); - } -} - - -void BrowserSessionManager::commitData(QSessionManager &sm) { - qDebug() << "Application::commitData was called"; - - // we will just tell the session manager to restart this application with the same arguments on session start - QStringList restartCommand = sm.restartCommand(); - restartCommand.append(restartArguments); - sm.setRestartCommand(restartCommand); - - if (browserProcess.pid() > 0) { - closeBrowser(browserProcess); - } else { - // the browser was closed by user or unexpectedly, so we don't want to restore it automatically - exit(0); - } - - KApplication::commitData(sm); -} diff --git a/CMakeLists.txt b/CMakeLists.txt index 358b845..767e22a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,14 +8,14 @@ include_directories(${KDE4_INCLUDES}) set(SOURCE_FILES main.cpp - BrowserSessionManager.h - BrowserSessionManager.cpp - FirefoxSessionManager.h - FirefoxSessionManager.cpp - ChromiumSessionManager.h - ChromiumSessionManager.cpp - GoogleChromeSessionManager.h - GoogleChromeSessionManager.cpp + BrowserManager.h + BrowserManager.cpp + FirefoxManager.h + FirefoxManager.cpp + ChromiumManager.h + ChromiumManager.cpp + GoogleChromeManager.h + GoogleChromeManager.cpp ) kde4_add_executable(activityfox ${SOURCE_FILES}) diff --git a/ChromiumManager.cpp b/ChromiumManager.cpp new file mode 100644 index 0000000..3177bbb --- /dev/null +++ b/ChromiumManager.cpp @@ -0,0 +1,52 @@ +#include "ChromiumManager.h" + +bool ChromiumManager::checkIfProfileExists(const QString &profileDir, const QString &profileName) { + DIR *dir = opendir((profileDir + '/' + profileName).toAscii()); + return dir != nullptr; +} + +void ChromiumManager::createProfile(const QString &, const QString &profileDir, const QString &profileName, + const QString &templateProfileName) { + // if a template profile name is specified, copy this profile into the new profile directory + if (templateProfileName != nullptr && templateProfileName != "") { + copyProfile(profileDir, templateProfileName, profileName); + } + + // no need to call Chromium to create the profile, Chromium creates it automatically on the first run +} + +void ChromiumManager::startBrowser(KProcess &browserProcess, const QString &binPath, const QString &profileDir, + const QString &profileName) { + browserProcess << binPath << "--user-data-dir=" + profileDir + '/' + profileName; + qDebug() << browserProcess.program(); + browserProcess.start(); +} + +void ChromiumManager::copyProfile(const QString &profileDir, const QString &fromName, const QString &toName) { + QStringList command; + + // make copy of a profile + command << "cp" << "-r" << profileDir + '/' + fromName << profileDir + '/' + toName; + qDebug() << command; + + KProcess::execute(command); +} + +QString ChromiumManager::getDefaultProfileDir() { + static const char *defaultProfileDirTail = ".config/chromium"; + + std::string defaultProfileDir = getenv("HOME"); + return (defaultProfileDir + '/' + defaultProfileDirTail).c_str(); +} + +QString ChromiumManager::getDefaultBinPath() { + static const char *defaultBinPath = "/usr/lib/chromium-browser/chromium-browser"; + + return defaultBinPath; +} + +ChromiumManager::ChromiumManager(const QString &startUpActivityId, const QString &binPath, const QString &profileDir, + const QString &templateProfileName, + const QString &profileName) : BrowserManager(startUpActivityId, binPath, profileDir, + templateProfileName, + profileName) {} \ No newline at end of file diff --git a/FirefoxSessionManager.h b/ChromiumManager.h similarity index 59% rename from FirefoxSessionManager.h rename to ChromiumManager.h index adea8c5..3a9f875 100644 --- a/FirefoxSessionManager.h +++ b/ChromiumManager.h @@ -1,13 +1,14 @@ -#ifndef ACTIVITYFOX_FIREFOXSESSIONMANAGER_H -#define ACTIVITYFOX_FIREFOXSESSIONMANAGER_H +#ifndef ACTIVITYFOX_CHROMIUMMANAGER_H +#define ACTIVITYFOX_CHROMIUMMANAGER_H -#include "BrowserSessionManager.h" +#include +#include "BrowserManager.h" -class FirefoxSessionManager : public BrowserSessionManager { +class ChromiumManager : public BrowserManager { public: - FirefoxSessionManager(QStringList allArguments, const QString &binPath, const QString &profileDir, - const QString &templateProfileName, - const QString &profileName); + ChromiumManager(const QString &startUpActivityId, const QString &binPath, const QString &profileDir, + const QString &templateProfileName, + const QString &profileName); private: @@ -27,4 +28,4 @@ class FirefoxSessionManager : public BrowserSessionManager { }; -#endif //ACTIVITYFOX_FIREFOXSESSIONMANAGER_H +#endif //ACTIVITYFOX_CHROMIUMMANAGER_H diff --git a/ChromiumSessionManager.cpp b/ChromiumSessionManager.cpp deleted file mode 100644 index 3c1855a..0000000 --- a/ChromiumSessionManager.cpp +++ /dev/null @@ -1,53 +0,0 @@ -#include "ChromiumSessionManager.h" - -bool ChromiumSessionManager::checkIfProfileExists(const QString &profileDir, const QString &profileName) { - DIR *dir = opendir((profileDir + '/' + profileName).toAscii()); - return dir != nullptr; -} - -void ChromiumSessionManager::createProfile(const QString &, const QString &profileDir, const QString &profileName, - const QString &templateProfileName) { - // if a template profile name is specified, copy this profile into the new profile directory - if (templateProfileName != nullptr && templateProfileName != "") { - copyProfile(profileDir, templateProfileName, profileName); - } - - // no need to call Chromium to create the profile, Chromium creates it automatically on the first run -} - -void ChromiumSessionManager::startBrowser(KProcess &browserProcess, const QString &binPath, const QString &profileDir, - const QString &profileName) { - browserProcess << binPath << "--user-data-dir=" + profileDir + '/' + profileName; - qDebug() << browserProcess.program(); - browserProcess.start(); -} - -void ChromiumSessionManager::copyProfile(const QString &profileDir, const QString &fromName, const QString &toName) { - QStringList command; - - // make copy of a profile - command << "cp" << "-r" << profileDir + '/' + fromName << profileDir + '/' + toName; - qDebug() << command; - - KProcess::execute(command); -} - -QString ChromiumSessionManager::getDefaultProfileDir() { - static const char *defaultProfileDirTail = ".config/chromium"; - - std::string defaultProfileDir = getenv("HOME"); - return (defaultProfileDir + '/' + defaultProfileDirTail).c_str(); -} - -QString ChromiumSessionManager::getDefaultBinPath() { - static const char *defaultBinPath = "/usr/lib/chromium-browser/chromium-browser"; - - return defaultBinPath; -} - -ChromiumSessionManager::ChromiumSessionManager(QStringList allArguments, const QString &binPath, - const QString &profileDir, const QString &templateProfileName, - const QString &profileName) : BrowserSessionManager(allArguments, - binPath, profileDir, - templateProfileName, - profileName) {} \ No newline at end of file diff --git a/FirefoxSessionManager.cpp b/FirefoxManager.cpp similarity index 55% rename from FirefoxSessionManager.cpp rename to FirefoxManager.cpp index 6a8fa49..bebe750 100644 --- a/FirefoxSessionManager.cpp +++ b/FirefoxManager.cpp @@ -1,6 +1,6 @@ -#include "FirefoxSessionManager.h" +#include "FirefoxManager.h" -bool FirefoxSessionManager::checkIfProfileExists(const QString &profileDir, const QString &profileName) { +bool FirefoxManager::checkIfProfileExists(const QString &profileDir, const QString &profileName) { KProcess checkIfProfileExists; QString command = "cat " + profileDir + '/' + "profiles.ini" + " | grep " + profileName; @@ -10,8 +10,8 @@ bool FirefoxSessionManager::checkIfProfileExists(const QString &profileDir, cons return (checkIfProfileExists.execute() == 0); } -void FirefoxSessionManager::createProfile(const QString &binPath, const QString &profileDir, const QString &profileName, - const QString &templateProfileName) { +void FirefoxManager::createProfile(const QString &binPath, const QString &profileDir, const QString &profileName, + const QString &templateProfileName) { // if a template profile name is specified, copy this profile into the new profile directory if (templateProfileName != nullptr && templateProfileName != "") { copyProfile(profileDir, templateProfileName, profileName); @@ -26,15 +26,15 @@ void FirefoxSessionManager::createProfile(const QString &binPath, const QString KProcess::execute(command); } -void FirefoxSessionManager::startBrowser(KProcess &browserProcess, const QString &binPath, const QString &, - const QString &profileName) { - qDebug() << "FirefoxSessionManager::startBrowser was called"; +void FirefoxManager::startBrowser(KProcess &browserProcess, const QString &binPath, const QString &, + const QString &profileName) { + qDebug() << "FirefoxManager::startBrowser was called"; browserProcess << binPath << "-p" << profileName << "-no-remote"; qDebug() << browserProcess.program(); browserProcess.start(); } -void FirefoxSessionManager::copyProfile(const QString &profileDir, const QString &fromName, const QString &toName) { +void FirefoxManager::copyProfile(const QString &profileDir, const QString &fromName, const QString &toName) { QStringList command; // make copy of a profile @@ -44,22 +44,22 @@ void FirefoxSessionManager::copyProfile(const QString &profileDir, const QString KProcess::execute(command); } -QString FirefoxSessionManager::getDefaultProfileDir() { +QString FirefoxManager::getDefaultProfileDir() { static const char *defaultProfileDirTail = ".mozilla/firefox"; std::string defaultProfileDir = getenv("HOME"); return (defaultProfileDir + '/' + defaultProfileDirTail).c_str(); } -QString FirefoxSessionManager::getDefaultBinPath() { +QString FirefoxManager::getDefaultBinPath() { static const char *defaultBinPath = "/usr/lib/firefox/firefox"; return defaultBinPath; } -FirefoxSessionManager::FirefoxSessionManager(QStringList allArguments, const QString &binPath, - const QString &profileDir, const QString &templateProfileName, - const QString &profileName) : BrowserSessionManager(allArguments, binPath, - profileDir, - templateProfileName, - profileName) {} \ No newline at end of file +FirefoxManager::FirefoxManager(const QString &startUpActivityId, const QString &binPath, const QString &profileDir, + const QString &templateProfileName, + const QString &profileName) : BrowserManager(startUpActivityId, binPath, + profileDir, + templateProfileName, + profileName) {} \ No newline at end of file diff --git a/ChromiumSessionManager.h b/FirefoxManager.h similarity index 61% rename from ChromiumSessionManager.h rename to FirefoxManager.h index ef0118c..055e372 100644 --- a/ChromiumSessionManager.h +++ b/FirefoxManager.h @@ -1,14 +1,13 @@ -#ifndef ACTIVITYFOX_CHROMIUMSESSIONMANAGER_H -#define ACTIVITYFOX_CHROMIUMSESSIONMANAGER_H +#ifndef ACTIVITYFOX_FIREFOXMANAGER_H +#define ACTIVITYFOX_FIREFOXMANAGER_H -#include -#include "BrowserSessionManager.h" +#include "BrowserManager.h" -class ChromiumSessionManager : public BrowserSessionManager { +class FirefoxManager : public BrowserManager { public: - ChromiumSessionManager(QStringList allArguments, const QString &binPath, const QString &profileDir, - const QString &templateProfileName, - const QString &profileName); + FirefoxManager(const QString &startUpActivityId, const QString &binPath, const QString &profileDir, + const QString &templateProfileName, + const QString &profileName); private: @@ -28,4 +27,4 @@ class ChromiumSessionManager : public BrowserSessionManager { }; -#endif //ACTIVITYFOX_CHROMIUMSESSIONMANAGER_H +#endif //ACTIVITYFOX_FIREFOXMANAGER_H diff --git a/GoogleChromeManager.cpp b/GoogleChromeManager.cpp new file mode 100644 index 0000000..16f337a --- /dev/null +++ b/GoogleChromeManager.cpp @@ -0,0 +1,19 @@ +#include "GoogleChromeManager.h" + +QString GoogleChromeManager::getDefaultProfileDir() { + static const char *defaultProfileDirTail = ".config/google-chrome"; + + std::string defaultProfileDir = getenv("HOME"); + return (defaultProfileDir + '/' + defaultProfileDirTail).c_str(); +} + +QString GoogleChromeManager::getDefaultBinPath() { + static const char *defaultBinPath = "/opt/google/chrome/google-chrome"; + + return defaultBinPath; +} + +GoogleChromeManager::GoogleChromeManager(const QString &startUpActivityId, const QString &binPath, + const QString &profileDir, const QString &templateProfileName, + const QString &profileName) + : ChromiumManager(startUpActivityId, binPath, profileDir, templateProfileName, profileName) {} \ No newline at end of file diff --git a/GoogleChromeManager.h b/GoogleChromeManager.h new file mode 100644 index 0000000..50fc847 --- /dev/null +++ b/GoogleChromeManager.h @@ -0,0 +1,20 @@ +#ifndef ACTIVITYFOX_GOOGLECHROMEMANAGER_H +#define ACTIVITYFOX_GOOGLECHROMEMANAGER_H + +#include "ChromiumManager.h" + +class GoogleChromeManager : public ChromiumManager { +public: + GoogleChromeManager(const QString &startUpActivityId, const QString &binPath, const QString &profileDir, + const QString &templateProfileName, + const QString &profileName); + +private: + + QString getDefaultBinPath() override; + + QString getDefaultProfileDir() override; +}; + + +#endif //ACTIVITYFOX_GOOGLECHROMEMANAGER_H diff --git a/GoogleChromeSessionManager.cpp b/GoogleChromeSessionManager.cpp deleted file mode 100644 index fd6fc2a..0000000 --- a/GoogleChromeSessionManager.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "GoogleChromeSessionManager.h" - -QString GoogleChromeSessionManager::getDefaultProfileDir() { - static const char *defaultProfileDirTail = ".config/google-chrome"; - - std::string defaultProfileDir = getenv("HOME"); - return (defaultProfileDir + '/' + defaultProfileDirTail).c_str(); -} - -QString GoogleChromeSessionManager::getDefaultBinPath() { - static const char *defaultBinPath = "/opt/google/chrome/google-chrome"; - - return defaultBinPath; -} - -GoogleChromeSessionManager::GoogleChromeSessionManager(QStringList allArguments, const QString &binPath, - const QString &profileDir, - const QString &templateProfileName, const QString &profileName) - : ChromiumSessionManager(allArguments, - binPath, profileDir, templateProfileName, profileName) {} \ No newline at end of file diff --git a/GoogleChromeSessionManager.h b/GoogleChromeSessionManager.h deleted file mode 100644 index fb2319c..0000000 --- a/GoogleChromeSessionManager.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef ACTIVITYFOX_GOOGLECHROMESESSIONMANAGER_H -#define ACTIVITYFOX_GOOGLECHROMESESSIONMANAGER_H - -#include "ChromiumSessionManager.h" - -class GoogleChromeSessionManager : public ChromiumSessionManager { -public: - GoogleChromeSessionManager(QStringList allArguments, const QString &binPath, const QString &profileDir, - const QString &templateProfileName, - const QString &profileName); - -private: - - QString getDefaultBinPath() override; - - QString getDefaultProfileDir() override; -}; - - -#endif //ACTIVITYFOX_GOOGLECHROMESESSIONMANAGER_H diff --git a/main.cpp b/main.cpp index 5edcfb8..be47482 100644 --- a/main.cpp +++ b/main.cpp @@ -1,21 +1,23 @@ #ifdef NDEBUG #define QT_NO_DEBUG_OUTPUT +#else + +#include #endif #include #include #include -#include +#include +#include -#include "BrowserSessionManager.h" -#include "FirefoxSessionManager.h" -#include "ChromiumSessionManager.h" -#include "GoogleChromeSessionManager.h" +#include "BrowserManager.h" +#include "FirefoxManager.h" +#include "ChromiumManager.h" +#include "GoogleChromeManager.h" #ifndef NDEBUG -#include - FILE *fp; void myMessageOutput(QtMsgType type, const char *msg) { @@ -41,54 +43,112 @@ void myMessageOutput(QtMsgType type, const char *msg) { #endif -KCmdLineArgs *parseCommandLineArguments(int argc, char **argv); - -int main(int argc, char *argv[]) { -#ifndef NDEBUG - fp = fopen("activityfox.log", "a"); - qInstallMsgHandler(myMessageOutput); -#endif +QString getCurrentActivityId() { + // get current activity id with QDBus, also possible to get by executing: + // qdbus org.kde.ActivityManager /ActivityManager/Activities org.kde.ActivityManager.Activities.CurrentActivity + QDBusInterface interface("org.kde.ActivityManager", + "/ActivityManager/Activities", + "org.kde.ActivityManager.Activities", + QDBusConnection::sessionBus()); - KCmdLineArgs *args = parseCommandLineArguments(argc, argv); - - QString binPath = args->getOption("bin-path"); - QString profileDir = args->getOption("profile-dir"); - QString templateProfileName = args->getOption("template-profile-name"); - QString profileName = nullptr; - - bool useChromium = args->isSet("chromium"); - bool useGoogleChrome = args->isSet("google-chrome"); + QDBusMessage result = interface.call("CurrentActivity"); + return result.arguments().at(0).value(); +} - if (args->count() > 0) { - profileName = args->arg(0); +class MainWindow : public KMainWindow { +public: + MainWindow(QString &startUpActivityId, unsigned browser, const QString &binPath, + const QString &profileDir, const QString &templateProfileName, + const QString &profileName) : KMainWindow() { + this->startUpActivityId = startUpActivityId; + this->browser = browser; + this->binPath = binPath; + this->profileDir = profileDir; + this->templateProfileName = templateProfileName; + this->profileName = profileName; + + if (browser == BrowserManager::CHROMIUM) { + bm = new ChromiumManager(startUpActivityId, binPath, profileDir, templateProfileName, profileName); + } else if (browser == BrowserManager::GOOGLE_CHROME) { + bm = new GoogleChromeManager(startUpActivityId, binPath, profileDir, templateProfileName, profileName); + } else { + bm = new FirefoxManager(startUpActivityId, binPath, profileDir, templateProfileName, profileName); + } + + bm->init(); } - BrowserSessionManager *sm; - QStringList allArguments = args->allArguments(); - allArguments.removeFirst(); - - if (useChromium) { - sm = new ChromiumSessionManager(allArguments, binPath, profileDir, templateProfileName, profileName); - } else if (useGoogleChrome) { - sm = new GoogleChromeSessionManager(allArguments, binPath, profileDir, templateProfileName, profileName); - } else { - sm = new FirefoxSessionManager(allArguments, binPath, profileDir, templateProfileName, profileName); + MainWindow() { + bm = nullptr; + browser = BrowserManager::FIREFOX; } - // having a visible window is necessary for session management - QWidget *window = new QWidget(); - window->show(); +protected: - sm->init(); + // that's a bit of a hack: listen to X11 PropertyNotify (28) event to see if activity was changed + // when it's changed to our start up activity, we start the browser, so it is shown in the right activity + bool x11Event(XEvent *event) override { + if (initWhenVisible && *(int *) event == 28 && startUpActivityId == getCurrentActivityId()) { + initWhenVisible = false; + bm->init(); + } - int result = sm->exec(); + return QWidget::x11Event(event); + } -#ifndef NDEBUG - fclose(fp); -#endif + void readProperties(const KConfigGroup &conf) override { + startUpActivityId = conf.readEntry("startUpActivityId"); + browser = conf.readEntry("browser", unsigned()); + binPath = conf.readEntry("binPath"); + profileDir = conf.readEntry("profileDir"); + profileName = conf.readEntry("profileName"); + templateProfileName = conf.readEntry("templateProfileName"); + + if (browser == BrowserManager::CHROMIUM) { + bm = new ChromiumManager(startUpActivityId, binPath, profileDir, templateProfileName, profileName); + } else if (browser == BrowserManager::GOOGLE_CHROME) { + bm = new GoogleChromeManager(startUpActivityId, binPath, profileDir, templateProfileName, profileName); + } else { + bm = new FirefoxManager(startUpActivityId, binPath, profileDir, templateProfileName, profileName); + } + + // if current activity is not the activity that the browser was started in, we will wait until it is + if (startUpActivityId == getCurrentActivityId()) { + bm->init(); + } else { + initWhenVisible = true; + } + } - return result; -} + void saveProperties(KConfigGroup &conf) override { + conf.writeEntry("startUpActivityId", startUpActivityId); + conf.writeEntry("browser", browser); + conf.writeEntry("binPath", binPath); + conf.writeEntry("profileDir", profileDir); + conf.writeEntry("templateProfileName", templateProfileName); + conf.writeEntry("profileName", profileName); + + static bool firstCall = true; + if (firstCall) { + firstCall = false; + + // we don't need to close the browser if we haven't yet opened it + if (!initWhenVisible) { + bm->onSaveProperties(); + } + } + } + +private: + BrowserManager *bm; + unsigned browser; + QString binPath; + QString profileDir; + QString templateProfileName; + QString profileName; + QString startUpActivityId; + bool initWhenVisible = false; +}; KCmdLineArgs *parseCommandLineArguments(int argc, char **argv) { static KAboutData aboutData("activityfox", nullptr, @@ -115,3 +175,55 @@ KCmdLineArgs *parseCommandLineArguments(int argc, char **argv) { return KCmdLineArgs::parsedArgs(); } + +int main(int argc, char *argv[]) { +#ifndef NDEBUG + fp = fopen("activityfox.log", "a"); + qInstallMsgHandler(myMessageOutput); +#endif + + KCmdLineArgs *args = parseCommandLineArguments(argc, argv); + + QString binPath = args->getOption("bin-path"); + QString profileDir = args->getOption("profile-dir"); + QString templateProfileName = args->getOption("template-profile-name"); + QString profileName = nullptr; + + bool useChromium = args->isSet("chromium"); + bool useGoogleChrome = args->isSet("google-chrome"); + + if (args->count() > 0) { + profileName = args->arg(0); + } + + BrowserManager::Browser browser; + + if (useChromium) { + browser = BrowserManager::CHROMIUM; + } else if (useGoogleChrome) { + browser = BrowserManager::GOOGLE_CHROME; + } else { + browser = BrowserManager::FIREFOX; + } + + QString startUpActivityId = getCurrentActivityId(); + + KApplication app; + + if (app.isSessionRestored()) { + kRestoreMainWindows(); + } else { + // having a visible window is necessary for session management + MainWindow *window = new MainWindow(startUpActivityId, browser, binPath, profileDir, templateProfileName, + profileName); + window->show(); + } + + int result = app.exec(); + +#ifndef NDEBUG + fclose(fp); +#endif + + return result; +} \ No newline at end of file From fa46ac49e6e1ac8528b9910b3b5a93dfba0ad2cc Mon Sep 17 00:00:00 2001 From: hellishnoob Date: Sun, 21 Jan 2018 16:25:25 +0300 Subject: [PATCH 09/12] move source files to src --- CMakeLists.txt | 18 +++++++++--------- BrowserManager.cpp => src/BrowserManager.cpp | 0 BrowserManager.h => src/BrowserManager.h | 0 ChromiumManager.cpp => src/ChromiumManager.cpp | 0 ChromiumManager.h => src/ChromiumManager.h | 0 FirefoxManager.cpp => src/FirefoxManager.cpp | 0 FirefoxManager.h => src/FirefoxManager.h | 0 .../GoogleChromeManager.cpp | 0 .../GoogleChromeManager.h | 0 main.cpp => src/main.cpp | 0 10 files changed, 9 insertions(+), 9 deletions(-) rename BrowserManager.cpp => src/BrowserManager.cpp (100%) rename BrowserManager.h => src/BrowserManager.h (100%) rename ChromiumManager.cpp => src/ChromiumManager.cpp (100%) rename ChromiumManager.h => src/ChromiumManager.h (100%) rename FirefoxManager.cpp => src/FirefoxManager.cpp (100%) rename FirefoxManager.h => src/FirefoxManager.h (100%) rename GoogleChromeManager.cpp => src/GoogleChromeManager.cpp (100%) rename GoogleChromeManager.h => src/GoogleChromeManager.h (100%) rename main.cpp => src/main.cpp (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 767e22a..b02d601 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,15 +7,15 @@ find_package(KDE4 REQUIRED) include_directories(${KDE4_INCLUDES}) set(SOURCE_FILES - main.cpp - BrowserManager.h - BrowserManager.cpp - FirefoxManager.h - FirefoxManager.cpp - ChromiumManager.h - ChromiumManager.cpp - GoogleChromeManager.h - GoogleChromeManager.cpp + src/main.cpp + src/BrowserManager.h + src/BrowserManager.cpp + src/FirefoxManager.h + src/FirefoxManager.cpp + src/ChromiumManager.h + src/ChromiumManager.cpp + src/GoogleChromeManager.h + src/GoogleChromeManager.cpp ) kde4_add_executable(activityfox ${SOURCE_FILES}) diff --git a/BrowserManager.cpp b/src/BrowserManager.cpp similarity index 100% rename from BrowserManager.cpp rename to src/BrowserManager.cpp diff --git a/BrowserManager.h b/src/BrowserManager.h similarity index 100% rename from BrowserManager.h rename to src/BrowserManager.h diff --git a/ChromiumManager.cpp b/src/ChromiumManager.cpp similarity index 100% rename from ChromiumManager.cpp rename to src/ChromiumManager.cpp diff --git a/ChromiumManager.h b/src/ChromiumManager.h similarity index 100% rename from ChromiumManager.h rename to src/ChromiumManager.h diff --git a/FirefoxManager.cpp b/src/FirefoxManager.cpp similarity index 100% rename from FirefoxManager.cpp rename to src/FirefoxManager.cpp diff --git a/FirefoxManager.h b/src/FirefoxManager.h similarity index 100% rename from FirefoxManager.h rename to src/FirefoxManager.h diff --git a/GoogleChromeManager.cpp b/src/GoogleChromeManager.cpp similarity index 100% rename from GoogleChromeManager.cpp rename to src/GoogleChromeManager.cpp diff --git a/GoogleChromeManager.h b/src/GoogleChromeManager.h similarity index 100% rename from GoogleChromeManager.h rename to src/GoogleChromeManager.h diff --git a/main.cpp b/src/main.cpp similarity index 100% rename from main.cpp rename to src/main.cpp From d2f5aa98f7f608ce18447b9274a5521d64e59ac8 Mon Sep 17 00:00:00 2001 From: hellishnoob Date: Sun, 21 Jan 2018 17:30:24 +0300 Subject: [PATCH 10/12] release 0.2 --- README | 99 ---------------------------------------------------- README.MD | 91 +++++++++++++++++++++++++++++++++++++++++++++++ src/main.cpp | 2 +- 3 files changed, 92 insertions(+), 100 deletions(-) delete mode 100644 README create mode 100644 README.MD diff --git a/README b/README deleted file mode 100644 index 9f51651..0000000 --- a/README +++ /dev/null @@ -1,99 +0,0 @@ -activityfox is a small KDE program that acts as a workaround to allow Firefox to -play nice with KDE Plasma Activities. - -See this blog post for more info: -http://yuenhoe.com/blog/2012/08/associating-firefox-profiles-with-kde-activities/ - -============ -Installing -============ - -Before you build, you may need to change the firefoxbin_path variable at the top of -main.cpp to the path of the firefox-bin executable on your system. It will usually be at - -"/usr/lib/firefox/firefox-bin" for 32bit systems -and -"/usr/lib64/firefox/firefox-bin" for 64bit systems. - -NOTE: It needs to be the *firefox-bin* executable path, not *firefox*, because -firefox will just spawn firefox-bin, which will end up out of reach of the program -preventing it from killing firefox when you stop the associated activity. - -When done, just do the usual: -> mkdir build -> cd build -> cmake .. -> make - -That should give you the 'activityfox' executable in the build folder. If you want -you can copy it somewhere accessible, like somewhere in $PATH - -=========== -Usage -=========== - -To associate a Firefox profile with a KDE activity, first activate and enter that -activity on your desktop. While in that activity, run: - -> activityfox - -The profile doesn't need to exist: the program will create it if it doesn't. A small -blank window should appear, which would launch the Firefox process by proxy. -Wait a bit, and a Firefox window for that profile should appear. If the profile is new -then everything will be set to default settings, with no extensions, themes and whatnot: -that's how Firefox profiles work. Do what you want with the profile. In particular, -you would want to set - -Edit > Preferences > General > When Firefox starts: -to -"Show my windows and tabs from last time" - -So that your tabs get restored a'la Konqueror after you stop and restart the activity. -You can do this for any number of activities: just make sure you associate a different -profile name to each activity. Then stopping any activity will close the associated -Firefox Profile's window while leaving the others alone, and restarting any activity -will reopen the associated Firefox profile's window. - -Sometimes when a Firefox profile window is restored, you'll get the "this is embarassing" -crash recovery tab. Just hit "restore session" and get your tabs back. Unfortunately -there doesn't seem to be a way to prevent this from happening once in awhile. - -See http://blip.tv/moofang/associating-firefox-profiles-with-kde-activities-6306721 for -a brief screencast demonstrating basic usage. - -=========== -FAQ -=========== - - -Q: My new Firefox profile instances have no plugins! This sucks! -A: If your new profile Firefox instance doesn't have any plugins, it may be because, -as in my system (OpenSUSE), somehow only the default instance looks in /usr/lib64/browser-plugins -for plugins. You can simply symlink all the plugins in that folder into ~/.mozilla/plugins, -for example: - -> ln -s /usr/lib64/browser-plugins/libflashplayer.so ~/.mozilla/plugins/libflashplayer.so - -And that should give your other profiles the plugin too. - - - -Q: Can't we do something about the initial little blank window? It's ugly and annoying. -A: To hide the initial blank window, we can do a little bit of Kwin window-rule magic: -Right-click the title-bar of the window, select More Actions > Special Application Settings. -In the window rules settings that pop up, set: - -- Size & Position > Minimized to Apply Initially, Yes -- Arrangement & Access > Keep below to Apply initially, Yes -- Arrangement & Access > Skip Taskbar to Apply initially, Yes -- Arrangement & Access > Skip Pager to Apply initially, Yes -- Arrangement & Access > Skip Switcher to Apply initially, Yes - -Basically this will keep it minimized while excluding it from the taskbar and from all -Kwin window switching capabilities, effectively rendering it phantom. - -=========== -License -=========== -GPL v3 -Copyright 2012 Yuen Hoe (Jason moofang) \ No newline at end of file diff --git a/README.MD b/README.MD new file mode 100644 index 0000000..564c378 --- /dev/null +++ b/README.MD @@ -0,0 +1,91 @@ +## Description + +ActivityFox is a small KDE program that acts as a workaround to allow Firefox, Google Chrome and Chromium-based browsers to +play nice with KDE Plasma Activities. + +ActivityFox lets you use separate browser instances for each activity, keeping tabs related to that activity. ActivityFox starts the browser with a profile linked to your current activity, closes the browser when you close the activity and automatically restores it when you open it again. + +## Usage + +When run without specifying command line arguments, ActivityFox starts Firefox with a profile named as your current activity id, creating it in a default profile directory if it doesn't exist. + +To launch Google Chrome or Chromium with a profile linked to your current activity, use the following commands: + +```bash +activityfox --google-chrome +activityfox --chromium +``` + +If your browser is located in a location different from the default one, you should specify the location of a binary file: + +```bash +activityfox --google-chrome --bin-path /usr/lib/firefox/firefox +activityfox --google-chrome --bin-path /opt/google/chrome/google-chrome +activityfox --chromium --bin-path /usr/lib/chromium-browser/chromium-browser +``` +In case your profiles are located in a different directory: + +```bash +activityfox --google-chrome --profile-dir /home//.mozilla/firefox +activityfox --google-chrome --profile-dir /home//.config/google-chrome +activityfox --chromium --profile-dir /home//.config/chromium +``` + +If you want to launch your browser with a specific profile, use: + +```bash +activityfox profile_name +``` +To make ActivityFox create profiles for new activities based on a template profile, first create it: + +```bash +activityfox template_profile_name +``` + +Set it up by making your browser show tabs from the last time, then use this command to automatically create new profiles based on your template: + +```bash +activityfox --template-profile-name template_profile_name +``` + +You may create a desktop file with this command and place it on your desktop or in your launcher menu. This way, in every new activity, you will be able to open a browser instance linked to that activity with an automatically created profile based on your template. + +## Hide ActivityFox window + +Creating a visible window is required to make the session manager call our application on session saving, but it's possible to hide it with KWin rules. + +Right-click the title-bar of the window, select More Actions > Special Application Settings. +In the window rules settings that pop up, set: + +- Size & Position > Minimized to Apply Initially, Yes +- Arrangement & Access > Skip Taskbar to Apply initially, Yes +- Arrangement & Access > Skip Pager to Apply initially, Yes +- Arrangement & Access > Skip Switcher to Apply initially, Yes + +## Build + +~~~bash +mkdir build +cd build +cmake -DCMAKE_BUILD_TYPE=Release .. +make +~~~ + +That should give you the 'activityfox' executable in the build folder. + +## Changelog + +### 0.2 +- Add Google Chrome and Chromium support +- ActivityFox automatically opens the browser with a profile linked to the current activity id +- ActivityFox gently closes browsers by sending a signal to close the window, mitigating crashes +- Creation of new profiles based on a template profile +- ActivityFox restores browsers in activites they were initially launched + +### 0.1 +- ActivityFox launches Firefox and terminates it on session saving, restores on session start + +## License + +GPL v3 +Copyright 2012 Yuen Hoe (Jason moofang), 2018 Leonid Kalichkin (hellishnoob) \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index be47482..1f428f4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -152,7 +152,7 @@ class MainWindow : public KMainWindow { KCmdLineArgs *parseCommandLineArguments(int argc, char **argv) { static KAboutData aboutData("activityfox", nullptr, - ki18n("ActivityFox"), "0.1", + ki18n("ActivityFox"), "0.2", ki18n("Help Firefox and Chromium-based browsers work with KDE activities."), KAboutData::License_GPL, ki18n("Copyright (c) 2012 Yuen Hoe (Jason moofang), 2018 Leonid Kalichkin (hellishnoob)")); From 7c98278922eb01aa765b80380e15a66a87fc7f9c Mon Sep 17 00:00:00 2001 From: Leonid Kalichkin Date: Sun, 21 Jan 2018 18:03:48 +0300 Subject: [PATCH 11/12] Update README.MD --- README.MD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.MD b/README.MD index 564c378..15d0de5 100644 --- a/README.MD +++ b/README.MD @@ -88,4 +88,4 @@ That should give you the 'activityfox' executable in the build folder. ## License GPL v3 -Copyright 2012 Yuen Hoe (Jason moofang), 2018 Leonid Kalichkin (hellishnoob) \ No newline at end of file +Copyright [2012 Yuen Hoe (Jason moofang)](http://yuenhoe.com/blog/2012/08/associating-firefox-profiles-with-kde-activities/), 2018 Leonid Kalichkin (hellishnoob) From 87627326ddc9640770303ec131f383f170edf548 Mon Sep 17 00:00:00 2001 From: hellishnoob Date: Sun, 21 Jan 2018 23:33:08 +0300 Subject: [PATCH 12/12] move KApplication init before qtdbus usage, so no warnings are thrown + additional debug messages --- src/main.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 1f428f4..df4cbc5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -104,6 +104,8 @@ class MainWindow : public KMainWindow { profileName = conf.readEntry("profileName"); templateProfileName = conf.readEntry("templateProfileName"); + qDebug() << browser << startUpActivityId << binPath << profileDir << templateProfileName << profileName; + if (browser == BrowserManager::CHROMIUM) { bm = new ChromiumManager(startUpActivityId, binPath, profileDir, templateProfileName, profileName); } else if (browser == BrowserManager::GOOGLE_CHROME) { @@ -128,6 +130,8 @@ class MainWindow : public KMainWindow { conf.writeEntry("templateProfileName", templateProfileName); conf.writeEntry("profileName", profileName); + qDebug() << browser << startUpActivityId << binPath << profileDir << templateProfileName << profileName; + static bool firstCall = true; if (firstCall) { firstCall = false; @@ -137,6 +141,7 @@ class MainWindow : public KMainWindow { bm->onSaveProperties(); } } + qDebug() << "returning from saveProperties"; } private: @@ -206,10 +211,10 @@ int main(int argc, char *argv[]) { browser = BrowserManager::FIREFOX; } - QString startUpActivityId = getCurrentActivityId(); - KApplication app; + QString startUpActivityId = getCurrentActivityId(); + if (app.isSessionRestored()) { kRestoreMainWindows(); } else {