Commit dec0e6ce authored by Méven Car's avatar Méven Car
Browse files

Refactor Spectacle to run in single instance mode with KDBusService::Unique

Summary:
Since Spectacle got DBus activation support in 09cd1188, it has been run in multiple instance mode.
But it has been shaddowed by using qbus ee862d16 in the desktop file to mimic singleton behavior.

This refactors Spectacle to run in true single instance mode using KDBusService::Unique.
This is to allow Spectacle to have its executable in its Exec desktop Entry, to allow KWin to match the executable to the service file, itself to allow screenshots under Wayland without intermediate click.

The listed bugs are fixed incidently except for 414739 where a simple fix was added in screenShotUpdated.

BUG: 420477
BUG: 414739

CCBUG: 412186

Test Plan:
Start spectacle, alternatively, from shortcut, command line.
Take screenshots, from alternative ways.

There is always at most one Spectacle window unless launched with `--new-instance`.

Reviewers: davidre, ngraham, bport, davidedmundson, apol

Reviewed By: davidre

Subscribers: broulik, anthonyfieroni, #spectacle

Tags: #spectacle

Differential Revision: https://phabricator.kde.org/D29487
parent 246ac1f8
......@@ -140,13 +140,13 @@ Keywords[uk]=знімок;захоплення;друк;знімок вікна;
Keywords[x-test]=xxsnapshotxx;xxcapturexx;xxprintxx;xxscreenshotxx;xxsnippingxx;xxsnipxx;
Keywords[zh_CN]=snapshot;capture;print;screenshot;snipping;snip;快照;截图;抓取;屏幕截图;截屏;抓屏;截取;
Keywords[zh_TW]=snapshot;capture;print;screenshot;snipping;snip;截圖;擷取;截圖程式
Exec=@QtBinariesDir@/qdbus org.kde.Spectacle / StartAgent
Exec=${CMAKE_INSTALL_PREFIX}/bin/spectacle
Icon=spectacle
Type=Application
Terminal=false
StartupNotify=false
Actions=FullScreenScreenShot;CurrentMonitorScreenShot;ActiveWindowScreenShot;RectangularRegionScreenShot;
X-DBUS-StartupType=Multi
X-DBUS-StartupType=Unique
X-DBUS-ServiceName=org.kde.Spectacle
X-KDE-Shortcuts=Print
......
......@@ -36,12 +36,13 @@ int main(int argc, char **argv)
{
// set up the application
QApplication lApp(argc, argv);
QApplication app(argc, argv);
lApp.setAttribute(Qt::AA_DontCreateNativeWidgetSiblings, true);
lApp.setAttribute(Qt::AA_UseHighDpiPixmaps, true);
app.setAttribute(Qt::AA_DontCreateNativeWidgetSiblings, true);
app.setAttribute(Qt::AA_UseHighDpiPixmaps, true);
KLocalizedString::setApplicationDomain("spectacle");
QCoreApplication::setOrganizationDomain(QStringLiteral("org.kde"));
KAboutData aboutData(QStringLiteral("spectacle"),
i18n("Spectacle"),
......@@ -53,115 +54,55 @@ int main(int argc, char **argv)
aboutData.addAuthor(QStringLiteral("David Redondo"), QString(), QStringLiteral("kde@david-redondo.de"));
aboutData.setTranslator(i18nc("NAME OF TRANSLATORS", "Your names"), i18nc("EMAIL OF TRANSLATORS", "Your emails"));
KAboutData::setApplicationData(aboutData);
lApp.setWindowIcon(QIcon::fromTheme(QStringLiteral("spectacle")));
app.setWindowIcon(QIcon::fromTheme(QStringLiteral("spectacle")));
// set up the command line options parser
SpectacleCore lCore;
QCommandLineParser lCmdLineParser;
aboutData.setupCommandLine(&lCmdLineParser);
lCore.populateCommandLineParser(&lCmdLineParser);
lCmdLineParser.addOptions({
{{QStringLiteral("f"), QStringLiteral("fullscreen")}, i18n("Capture the entire desktop (default)")},
{{QStringLiteral("m"), QStringLiteral("current")}, i18n("Capture the current monitor")},
{{QStringLiteral("a"), QStringLiteral("activewindow")}, i18n("Capture the active window")},
{{QStringLiteral("u"), QStringLiteral("windowundercursor")}, i18n("Capture the window currently under the cursor, including parents of pop-up menus")},
{{QStringLiteral("t"), QStringLiteral("transientonly")}, i18n("Capture the window currently under the cursor, excluding parents of pop-up menus")},
{{QStringLiteral("r"), QStringLiteral("region")}, i18n("Capture a rectangular region of the screen")},
{{QStringLiteral("g"), QStringLiteral("gui")}, i18n("Start in GUI mode (default)")},
{{QStringLiteral("b"), QStringLiteral("background")}, i18n("Take a screenshot and exit without showing the GUI")},
{{QStringLiteral("s"), QStringLiteral("dbus")}, i18n("Start in DBus-Activation mode")},
{{QStringLiteral("n"), QStringLiteral("nonotify")}, i18n("In background mode, do not pop up a notification when the screenshot is taken")},
{{QStringLiteral("o"), QStringLiteral("output")}, i18n("In background mode, save image to specified file"), QStringLiteral("fileName")},
{{QStringLiteral("d"), QStringLiteral("delay")}, i18n("In background mode, delay before taking the shot (in milliseconds)"), QStringLiteral("delayMsec")},
{{QStringLiteral("c"), QStringLiteral("clipboard")}, i18n("In background mode, copy screenshot to clipboard")},
{{QStringLiteral("w"), QStringLiteral("onclick")}, i18n("Wait for a click before taking screenshot. Invalidates delay")}
});
lCmdLineParser.process(lApp);
// first parsing for help-about
lCmdLineParser.process(app.arguments());
aboutData.processCommandLine(&lCmdLineParser);
// extract the capture mode
Spectacle::CaptureMode lCaptureMode = Spectacle::CaptureMode::AllScreens;
if (lCmdLineParser.isSet(QStringLiteral("current"))) {
lCaptureMode = Spectacle::CaptureMode::CurrentScreen;
} else if (lCmdLineParser.isSet(QStringLiteral("activewindow"))) {
lCaptureMode = Spectacle::CaptureMode::ActiveWindow;
} else if (lCmdLineParser.isSet(QStringLiteral("region"))) {
lCaptureMode = Spectacle::CaptureMode::RectangularRegion;
} else if (lCmdLineParser.isSet(QStringLiteral("windowundercursor"))) {
lCaptureMode = Spectacle::CaptureMode::TransientWithParent;
} else if (lCmdLineParser.isSet(QStringLiteral("transientonly"))) {
lCaptureMode = Spectacle::CaptureMode::WindowUnderCursor;
}
// and new-instance
if (lCmdLineParser.isSet(QStringLiteral("new-instance"))){
lCore.init();
// are we running in background or dbus mode?
QObject::connect(&lCore, &SpectacleCore::allDone, &app, &QCoreApplication::quit, Qt::QueuedConnection);
SpectacleCore::StartMode lStartMode = SpectacleCore::StartMode::Gui;
bool lNotify = true;
bool lCopyToClipboard = false;
qint64 lDelayMsec = 0;
QString lFileName = QString();
// fire it up
lCore.onActivateRequested(app.arguments(), QStringLiteral());
if (lCmdLineParser.isSet(QStringLiteral("background"))) {
lStartMode = SpectacleCore::StartMode::Background;
} else if (lCmdLineParser.isSet(QStringLiteral("dbus"))) {
lStartMode = SpectacleCore::StartMode::DBus;
return app.exec();
}
switch(lStartMode) {
case SpectacleCore::StartMode::Background:
if (lCmdLineParser.isSet(QStringLiteral("nonotify"))) {
lNotify = false;
}
if (lCmdLineParser.isSet(QStringLiteral("output"))) {
lFileName = lCmdLineParser.value(QStringLiteral("output"));
}
if (lCmdLineParser.isSet(QStringLiteral("delay"))) {
bool lParseOk = false;
qint64 lDelayValue = lCmdLineParser.value(QStringLiteral("delay")).toLongLong(&lParseOk);
if (lParseOk) {
lDelayMsec = lDelayValue;
}
}
if (lCmdLineParser.isSet(QStringLiteral("onclick"))) {
lDelayMsec = -1;
}
if (lCmdLineParser.isSet(QStringLiteral("clipboard"))) {
lCopyToClipboard = true;
}
lApp.setQuitOnLastWindowClosed(false);
break;
case SpectacleCore::StartMode::DBus:
lApp.setQuitOnLastWindowClosed(false);
break;
case SpectacleCore::StartMode::Gui:
break;
}
// Ensure that we only launch a new instance if we need to
// If there is already an instance running, we will quit here
// and activateRequested signal is triggered
// For some reason this does not work properly if behind an if
KDBusService service(KDBusService::Unique, &lCore);
// release the kraken
// Delay initialisation after we now we are in the single instance or new-instance was passed, to avoid doing it each time spectacle executable is called
lCore.init();
SpectacleCore lCore(lStartMode, lCaptureMode, lFileName, lDelayMsec, lNotify, lCopyToClipboard);
QObject::connect(&lCore, &SpectacleCore::allDone, qApp, &QApplication::quit);
// set up the KDBusService activateRequested slot
QObject::connect(&service, &KDBusService::activateRequested, &lCore, &SpectacleCore::onActivateRequested);
QObject::connect(&lCore, &SpectacleCore::allDone, &app, &QCoreApplication::quit, Qt::QueuedConnection);
QObject::connect(qApp, &QApplication::aboutToQuit, Settings::self(), &Settings::save);
// create the dbus connections
new KDBusService(KDBusService::Multiple, &lCore);
// create the dbus connections
SpectacleDBusAdapter *lDBusAdapter = new SpectacleDBusAdapter(&lCore);
QObject::connect(&lCore, &SpectacleCore::grabFailed, lDBusAdapter, &SpectacleDBusAdapter::ScreenshotFailed);
QObject::connect(ExportManager::instance(), &ExportManager::imageSaved, &lCore, [&](const QUrl &savedAt) {
emit lDBusAdapter->ScreenshotTaken(savedAt.toLocalFile());
lDBusAdapter->ScreenshotTaken(savedAt.toLocalFile());
});
QDBusConnection::sessionBus().registerObject(QStringLiteral("/"), &lCore);
QDBusConnection::sessionBus().registerService(QStringLiteral("org.kde.Spectacle"));
// fire it up
lCore.onActivateRequested(app.arguments(), QStringLiteral());
return lApp.exec();
return app.exec();
}
......@@ -44,48 +44,25 @@
#include <QProcess>
#include <QTimer>
SpectacleCore::SpectacleCore(StartMode theStartMode,
Spectacle::CaptureMode theCaptureMode,
QString &theSaveFileName,
qint64 theDelayMsec,
bool theNotifyOnGrab,
bool theCopyToClipboard,
QObject *parent) :
SpectacleCore::SpectacleCore(QObject *parent):
QObject(parent),
mStartMode(theStartMode),
mNotify(theNotifyOnGrab),
mPlatform(loadPlatform()),
mMainWindow(nullptr),
mIsGuiInited(false),
mCopyToClipboard(theCopyToClipboard),
mWaylandPlasmashell(nullptr)
{
if (!(theSaveFileName.isEmpty() || theSaveFileName.isNull())) {
if (QDir::isRelativePath(theSaveFileName)) {
theSaveFileName = QDir::current().absoluteFilePath(theSaveFileName);
}
setFilename(theSaveFileName);
}
}
void SpectacleCore::init()
{
mPlatform = loadPlatform();
// essential connections
connect(this, &SpectacleCore::errorMessage, this, &SpectacleCore::showErrorMessage);
connect(mPlatform.get(), &Platform::newScreenshotTaken, this, &SpectacleCore::screenshotUpdated);
connect(mPlatform.get(), &Platform::newScreenshotFailed, this, &SpectacleCore::screenshotFailed);
auto lImmediateAvailable = mPlatform->supportedShutterModes().testFlag(Platform::ShutterMode::Immediate);
auto lOnClickAvailable = mPlatform->supportedShutterModes().testFlag(Platform::ShutterMode::OnClick);
if ((!lOnClickAvailable) && (theDelayMsec < 0)) {
theDelayMsec = 0;
}
// reset last region if it should not be remembered across restarts
if(!Settings::alwaysRememberRegion()) {
Settings::setCropRegion({0, 0, 0, 0});
}
// set up the export manager
auto lExportManager = ExportManager::instance();
lExportManager->setCaptureMode(theCaptureMode);
connect(lExportManager, &ExportManager::errorMessage, this, &SpectacleCore::showErrorMessage);
connect(lExportManager, &ExportManager::imageSaved, this, &SpectacleCore::doCopyPath);
connect(lExportManager, &ExportManager::forceNotify, this, &SpectacleCore::doNotify);
......@@ -108,26 +85,137 @@ SpectacleCore::SpectacleCore(StartMode theStartMode,
registry->setup();
connection->roundtrip();
}
setUpShortcuts();
}
void SpectacleCore::onActivateRequested(QStringList arguments, const QString& /*workingDirectory */)
{
// QCommandLineParser expects the first argument to be the executable name
// In the current version it just strips it away
arguments.prepend(qApp->applicationFilePath());
// We can't re-use QCommandLineParser instances, it preserves earlier parsed values
auto parser = new QCommandLineParser;
populateCommandLineParser(parser);
parser->parse(arguments);
// extract the capture mode
Spectacle::CaptureMode lCaptureMode = Spectacle::CaptureMode::AllScreens;
if (parser->isSet(QStringLiteral("current"))) {
lCaptureMode = Spectacle::CaptureMode::CurrentScreen;
} else if (parser->isSet(QStringLiteral("activewindow"))) {
lCaptureMode = Spectacle::CaptureMode::ActiveWindow;
} else if (parser->isSet(QStringLiteral("region"))) {
lCaptureMode = Spectacle::CaptureMode::RectangularRegion;
} else if (parser->isSet(QStringLiteral("windowundercursor"))) {
lCaptureMode = Spectacle::CaptureMode::TransientWithParent;
} else if (parser->isSet(QStringLiteral("transientonly"))) {
lCaptureMode = Spectacle::CaptureMode::WindowUnderCursor;
}
mStartMode = SpectacleCore::StartMode::Gui;
mNotify = true;
mCopyToClipboard = Settings::copyImageToClipboard();
qint64 lDelayMsec = 0;
QString lFileName = QString();
// are we ask to run in background or dbus mode?
if (parser->isSet(QStringLiteral("background"))) {
mStartMode = SpectacleCore::StartMode::Background;
} else if (parser->isSet(QStringLiteral("dbus"))) {
mStartMode = SpectacleCore::StartMode::DBus;
}
auto lExportManager = ExportManager::instance();
lExportManager->setCaptureMode(lCaptureMode);
auto lOnClickAvailable = mPlatform->supportedShutterModes().testFlag(Platform::ShutterMode::OnClick);
if ((!lOnClickAvailable) && (lDelayMsec < 0)) {
lDelayMsec = 0;
}
if (!(lFileName.isEmpty() || lFileName.isNull())) {
if (QDir::isRelativePath(lFileName)) {
lFileName = QDir::current().absoluteFilePath(lFileName);
}
setFilename(lFileName);
}
// reset last region if it should not be remembered across restarts
if(!Settings::alwaysRememberRegion()) {
Settings::setCropRegion({0, 0, 0, 0});
}
switch (mStartMode) {
switch (theStartMode) {
case StartMode::DBus:
qApp->setQuitOnLastWindowClosed(false);
break;
case StartMode::Background: {
auto lMsec = (KWindowSystem::compositingActive() ? 200 : 50) + theDelayMsec;
auto lShutterMode = lImmediateAvailable ? Platform::ShutterMode::Immediate : Platform::ShutterMode::OnClick;
auto lIncludePointer = Settings::includePointer();
auto lIncludeDecorations = Settings::includeDecorations();
const Platform::GrabMode lCaptureMode = toPlatformGrabMode(theCaptureMode);
QTimer::singleShot(lMsec, this, [ this, lCaptureMode, lShutterMode, lIncludePointer, lIncludeDecorations ]() {
mPlatform->doGrab(lShutterMode, lCaptureMode, lIncludePointer, lIncludeDecorations);
});
if (parser->isSet(QStringLiteral("nonotify"))) {
mNotify = false;
}
if (parser->isSet(QStringLiteral("output"))) {
lFileName = parser->value(QStringLiteral("output"));
}
if (parser->isSet(QStringLiteral("delay"))) {
bool lParseOk = false;
qint64 lDelayValue = parser->value(QStringLiteral("delay")).toLongLong(&lParseOk);
if (lParseOk) {
lDelayMsec = lDelayValue;
}
}
if (parser->isSet(QStringLiteral("onclick"))) {
lDelayMsec = -1;
}
if (parser->isSet(QStringLiteral("clipboard"))) {
mCopyToClipboard = true;
}
if (!mIsGuiInited) {
static_cast<QGuiApplication *>(qApp->instance())->setQuitOnLastWindowClosed(false);
}
auto lIncludePointer = Settings::includePointer();
auto lIncludeDecorations = Settings::includeDecorations();
takeNewScreenshot(lCaptureMode, lDelayMsec, lIncludePointer, lIncludeDecorations);
}
break;
case StartMode::Gui:
initGui(Settings::includePointer(), Settings::includeDecorations());
if (!mIsGuiInited) {
initGui(lDelayMsec, Settings::includePointer(), Settings::includeDecorations());
} else {
using Actions = Settings::EnumPrintKeyActionRunning;
switch (Settings::printKeyActionRunning()) {
case Actions::TakeNewScreenshot: {
// 0 means Immediate, -1 onClick
int timeout = mPlatform->supportedShutterModes().testFlag(Platform::ShutterMode::Immediate) ? 0 : -1;
takeNewScreenshot(Settings::captureMode(), timeout, Settings::includePointer(), Settings::includeDecorations());
break;
}
case Actions::FocusWindow:
if (mMainWindow->isMinimized()) {
mMainWindow->setWindowState(mMainWindow->windowState() & ~Qt::WindowMinimized);
}
mMainWindow->activateWindow();
break;
case Actions::StartNewInstance:
QProcess newInstance;
newInstance.setProgram(QStringLiteral("spectacle"));
newInstance.setArguments({QStringLiteral("--new-instance")});
newInstance.startDetached();
break;
}
}
break;
}
setUpShortcuts();
}
void SpectacleCore::setUpShortcuts()
......@@ -154,44 +242,6 @@ void SpectacleCore::setFilename(const QString &filename)
mFileNameUrl = QUrl::fromUserInput(filename);
}
// Slots
void SpectacleCore::dbusStartAgent()
{
qApp->setQuitOnLastWindowClosed(true);
auto lConfig = KSharedConfig::openConfig(QStringLiteral("spectaclerc"));
KConfigGroup lGuiConfig(lConfig, "GuiConfig");
auto lIncludePointer = lGuiConfig.readEntry("includePointer", true);
auto lIncludeDecorations = lGuiConfig.readEntry("includeDecorations", true);
if (!(mStartMode == StartMode::Gui)) {
mStartMode = StartMode::Gui;
initGui(lIncludePointer, lIncludeDecorations);
} else {
using Actions = Settings::EnumPrintKeyActionRunning;
switch (Settings::printKeyActionRunning()) {
case Actions::TakeNewScreenshot: {
// 0 means Immediate, -1 onClick
int timeout = mPlatform->supportedShutterModes().testFlag(Platform::ShutterMode::Immediate) ? 0 : -1;
takeNewScreenshot(Settings::captureMode(), timeout, Settings::includePointer(), Settings::includeDecorations());
break;
}
case Actions::FocusWindow:
if (mMainWindow->isMinimized()) {
mMainWindow->setWindowState(mMainWindow->windowState() & ~Qt::WindowMinimized);
}
mMainWindow->activateWindow();
break;
case Actions::StartNewInstance:
QProcess newInstance;
newInstance.setProgram(QStringLiteral("spectacle"));
newInstance.startDetached();
break;
}
}
}
void SpectacleCore::takeNewScreenshot(Spectacle::CaptureMode theCaptureMode,
int theTimeout,
bool theIncludePointer,
......@@ -200,7 +250,7 @@ void SpectacleCore::takeNewScreenshot(Spectacle::CaptureMode theCaptureMode,
ExportManager::instance()->setCaptureMode(theCaptureMode);
auto lGrabMode = toPlatformGrabMode(theCaptureMode);
if (theTimeout < 0) {
if (theTimeout < 0 || !mPlatform->supportedShutterModes().testFlag(Platform::ShutterMode::Immediate)) {
mPlatform->doGrab(Platform::ShutterMode::OnClick, lGrabMode, theIncludePointer, theIncludeDecorations);
return;
}
......@@ -253,26 +303,25 @@ void SpectacleCore::screenshotUpdated(const QPixmap &thePixmap)
case StartMode::Background:
case StartMode::DBus:
{
if (mNotify) {
connect(lExportManager, &ExportManager::imageSaved, this, &SpectacleCore::doNotify);
}
if (mCopyToClipboard) {
lExportManager->doCopyToClipboard(mNotify);
} else {
QUrl lSavePath = (mStartMode == StartMode::Background && mFileNameUrl.isValid() && mFileNameUrl.isLocalFile()) ?
mFileNameUrl : QUrl();
lExportManager->doSave(lSavePath);
lExportManager->doSave(lSavePath, mNotify);
}
// if we notify, we emit allDone only if the user either dismissed the notification or pressed
// the "Open" button, otherwise the app closes before it can react to it.
if (!mNotify && mCopyToClipboard) {
// Allow some time for clipboard content to transfer if '--nonotify' is used, see Bug #411263
// TODO: Find better solution
QTimer::singleShot(250, this, &SpectacleCore::allDone);
} else if (!mNotify) {
emit allDone();
// if we don't have a Gui already opened, emit allDone
if (!this->mIsGuiInited) {
// if we notify, we emit allDone only if the user either dismissed the notification or pressed
// the "Open" button, otherwise the app closes before it can react to it.
if (!mNotify && mCopyToClipboard) {
// Allow some time for clipboard content to transfer if '--nonotify' is used, see Bug #411263
// TODO: Find better solution
QTimer::singleShot(250, this, &SpectacleCore::allDone);
} else if (!mNotify) {
emit allDone();
}
}
}
break;
......@@ -280,13 +329,13 @@ void SpectacleCore::screenshotUpdated(const QPixmap &thePixmap)
mMainWindow->setScreenshotAndShow(thePixmap);
bool autoSaveImage = Settings::autoSaveImage();
bool copyImageToClipboard = Settings::copyImageToClipboard();
bool mCopyImageToClipboard = Settings::copyImageToClipboard();
if (autoSaveImage && copyImageToClipboard) {
if (autoSaveImage && mCopyImageToClipboard) {
lExportManager->doSaveAndCopy();
} else if (autoSaveImage) {
lExportManager->doSave();
} else if (copyImageToClipboard) {
} else if (mCopyImageToClipboard) {
lExportManager->doCopyToClipboard(false);
}
}
......@@ -355,7 +404,7 @@ void SpectacleCore::doNotify(const QUrl &theSavedAt)
if (index == 0) {
new KRun(theSavedAt, nullptr);
QTimer::singleShot(250, this, [this] {
if (mStartMode != StartMode::Gui || Settings::quitAfterSaveCopyExport()) {
if (!mIsGuiInited || Settings::quitAfterSaveCopyExport()) {
emit allDone();
}
});
......@@ -364,7 +413,7 @@ void SpectacleCore::doNotify(const QUrl &theSavedAt)
}
connect(lNotify, &QObject::destroyed, this, [this] {
if (mStartMode != StartMode::Gui || Settings::quitAfterSaveCopyExport()) {
if (!mIsGuiInited || Settings::quitAfterSaveCopyExport()) {
emit allDone();
}
});
......@@ -379,6 +428,27 @@ void SpectacleCore::doCopyPath(const QUrl &savedAt)
}
}
void SpectacleCore::populateCommandLineParser(QCommandLineParser *lCmdLineParser)
{
lCmdLineParser->addOptions({
{{QStringLiteral("f"), QStringLiteral("fullscreen")}, i18n("Capture the entire desktop (default)")},
{{QStringLiteral("m"), QStringLiteral("current")}, i18n("Capture the current monitor")},
{{QStringLiteral("a"), QStringLiteral("activewindow")}, i18n("Capture the active window")},
{{QStringLiteral("u"), QStringLiteral("windowundercursor")}, i18n("Capture the window currently under the cursor, including parents of pop-up menus")},
{{QStringLiteral("t"), QStringLiteral("transientonly")}, i18n("Capture the window currently under the cursor, excluding parents of pop-up menus")},
{{QStringLiteral("r"), QStringLiteral("region")}, i18n("Capture a rectangular region of the screen")},
{{QStringLiteral("g"), QStringLiteral("gui")}, i18n("Start in GUI mode (default)")},
{{QStringLiteral("b"), QStringLiteral("background")}, i18n("Take a screenshot and exit without showing the GUI")},
{{QStringLiteral("s"), QStringLiteral("dbus")}, i18n("Start in DBus-Activation mode")},
{{QStringLiteral("n"), QStringLiteral("nonotify")}, i18n("In background mode, do not pop up a notification when the screenshot is taken")},
{{QStringLiteral("o"), QStringLiteral("output")}, i18n("In background mode, save image to specified file"), QStringLiteral("fileName")},
{{QStringLiteral("d"), QStringLiteral("delay")}, i18n("In background mode, delay before taking the shot (in milliseconds)"), QStringLiteral("delayMsec")},
{{QStringLiteral("c"), QStringLiteral("clipboard")}, i18n("In background mode, copy screenshot to clipboard")},
{{QStringLiteral("w"), QStringLiteral("onclick")}, i18n("Wait for a click before taking screenshot. Invalidates delay")},
{{QStringLiteral("i"), QStringLiteral("new-instance")}, i18n("Starts a new GUI instance of spectacle without registering to DBus")}
});
}
void SpectacleCore::doStartDragAndDrop()
{
auto lExportManager = ExportManager::instance();
......@@ -419,7 +489,7 @@ Platform::GrabMode SpectacleCore::toPlatformGrabMode(Spectacle::CaptureMode theC
return Platform::GrabMode::InvalidChoice;
}
void SpectacleCore::initGui(bool theIncludePointer, bool theIncludeDecorations)
void SpectacleCore::initGui(int theDelay, bool theIncludePointer, bool theIncludeDecorations)
{
if (!mIsGuiInited) {
mMainWindow = std::make_unique<KSMainWindow>(mPlatform->supportedGrabModes(), mPlatform->supportedShutterModes());
......@@ -428,12 +498,7 @@ void SpectacleCore::initGui(bool theIncludePointer, bool theIncludeDecorations)
connect(mMainWindow.get(), &KSMainWindow::dragAndDropRequest, this, &SpectacleCore::doStartDragAndDrop);
mIsGuiInited = true;
auto lShutterMode = mPlatform->supportedShutterModes().testFlag(Platform::ShutterMode::Immediate) ? Platform::ShutterMode::Immediate : Platform::ShutterMode::OnClick;
auto lGrabMode = toPlatformGrabMode(ExportManager::instance()->captureMode());
QTimer::singleShot(0, this, [this, lShutterMode, lGrabMode, theIncludePointer, theIncludeDecorations]() {
mPlatform->doGrab(lShutterMode, lGrabMode, theIncludePointer, theIncludeDecorations);
});
}
takeNewScreenshot(ExportManager::instance()->captureMode(), theDelay, theIncludePointer, theIncludeDecorations);
}
......@@ -22,6 +22,7 @@
#pragma once
#include <QObject>
#include <QCommandLineParser>
#include "ExportManager.h"
#include "Gui/KSMainWindow.h"
......@@ -51,18 +52,15 @@ class SpectacleCore: public QObject
Background = 2
};
explicit SpectacleCore(StartMode theStartMode,
Spectacle::CaptureMode theCaptureMode,
QString &theSaveFileName,
qint64 theDelayMsec,
bool theNotifyOnGrab,
bool theCopyToClipboard,
QObject *parent = nullptr);
explicit SpectacleCore(QObject *parent = nullptr);
virtual ~SpectacleCore() = default;
void init();
QString filename() const;
void setFilename(const QString &filename);
void populateCommandLineParser(QCommandLineParser *lCmdLineParser);
Q_SIGNALS:
void errorMessage(const QString &errString);
......@@ -76,15 +74,15 @@ class SpectacleCore: public QObject
void showErrorMessage(const QString &theErrString);
void screenshotUpdated(const QPixmap &thePixmap);
void screenshotFailed();
void dbusStartAgent();
void doStartDragAndDrop();
void doNotify(const QUrl &theSavedAt);
void doCopyPath(const QUrl &savedAt);
private:
void onActivateRequested(QStringList arguments, const QString& /*workingDirectory */);
private:
void initGui(bool theIncludePointer, bool theIncludeDecorations);
void initGui(int theDelay, bool theIncludePointer, bool theIncludeDecorations);
Platform::GrabMode toPlatformGrabMode(Spectacle::CaptureMode theCaptureMode);
void setUpShortcuts();
......