Changeset View
Changeset View
Standalone View
Standalone View
src/Main.cpp
Show All 26 Lines | |||||
27 | #include <QApplication> | 27 | #include <QApplication> | ||
28 | #include <QCommandLineParser> | 28 | #include <QCommandLineParser> | ||
29 | #include <QDBusConnection> | 29 | #include <QDBusConnection> | ||
30 | 30 | | |||
31 | #include <KAboutData> | 31 | #include <KAboutData> | ||
32 | #include <KDBusService> | 32 | #include <KDBusService> | ||
33 | #include <KLocalizedString> | 33 | #include <KLocalizedString> | ||
34 | 34 | | |||
35 | // Workaround for a bug in KDBusService: https://bugs.kde.org/show_bug.cgi?id=355545 | ||||
36 | // It calls exit(), but the program can't exit before the QApplication is deleted: | ||||
37 | // https://bugreports.qt.io/browse/QTBUG-48709 | ||||
38 | static bool s_needToDeleteQApplication = true; | ||||
39 | void deleteQApplication() | ||||
40 | { | ||||
41 | if (s_needToDeleteQApplication && qApp) { | ||||
anthonyfieroni: `s_needToDeleteQApplication` is not needed any more. | |||||
42 | delete qApp; | ||||
anthonyfieroni: Deleting application allocated on stack is UB. Keep app on heap. | |||||
43 | } | ||||
44 | } | ||||
45 | | ||||
35 | int main(int argc, char **argv) | 46 | int main(int argc, char **argv) | ||
36 | { | 47 | { | ||
37 | // set up the application | 48 | // set up the application | ||
38 | 49 | | |||
39 | QApplication lApp(argc, argv); | 50 | QApplication lApp(argc, argv); | ||
40 | 51 | | |||
41 | lApp.setAttribute(Qt::AA_DontCreateNativeWidgetSiblings, true); | 52 | lApp.setAttribute(Qt::AA_DontCreateNativeWidgetSiblings, true); | ||
42 | lApp.setAttribute(Qt::AA_UseHighDpiPixmaps, true); | 53 | lApp.setAttribute(Qt::AA_UseHighDpiPixmaps, true); | ||
43 | 54 | | |||
44 | KLocalizedString::setApplicationDomain("spectacle"); | 55 | KLocalizedString::setApplicationDomain("spectacle"); | ||
56 | QCoreApplication::setOrganizationDomain(QStringLiteral("org.kde")); | ||||
45 | 57 | | |||
46 | KAboutData aboutData(QStringLiteral("spectacle"), | 58 | KAboutData aboutData(QStringLiteral("spectacle"), | ||
47 | i18n("Spectacle"), | 59 | i18n("Spectacle"), | ||
48 | QStringLiteral(SPECTACLE_VERSION), | 60 | QStringLiteral(SPECTACLE_VERSION), | ||
49 | i18n("KDE Screenshot Utility"), | 61 | i18n("KDE Screenshot Utility"), | ||
50 | KAboutLicense::GPL_V2, | 62 | KAboutLicense::GPL_V2, | ||
51 | i18n("(C) 2015 Boudhayan Gupta")); | 63 | i18n("(C) 2015 Boudhayan Gupta")); | ||
52 | aboutData.addAuthor(QStringLiteral("Boudhayan Gupta"), QString(), QStringLiteral("bgupta@kde.org")); | 64 | aboutData.addAuthor(QStringLiteral("Boudhayan Gupta"), QString(), QStringLiteral("bgupta@kde.org")); | ||
53 | aboutData.addAuthor(QStringLiteral("David Redondo"), QString(), QStringLiteral("kde@david-redondo.de")); | 65 | aboutData.addAuthor(QStringLiteral("David Redondo"), QString(), QStringLiteral("kde@david-redondo.de")); | ||
54 | aboutData.setTranslator(i18nc("NAME OF TRANSLATORS", "Your names"), i18nc("EMAIL OF TRANSLATORS", "Your emails")); | 66 | aboutData.setTranslator(i18nc("NAME OF TRANSLATORS", "Your names"), i18nc("EMAIL OF TRANSLATORS", "Your emails")); | ||
55 | KAboutData::setApplicationData(aboutData); | 67 | KAboutData::setApplicationData(aboutData); | ||
56 | lApp.setWindowIcon(QIcon::fromTheme(QStringLiteral("spectacle"))); | 68 | lApp.setWindowIcon(QIcon::fromTheme(QStringLiteral("spectacle"))); | ||
57 | 69 | | |||
58 | // set up the command line options parser | 70 | SpectacleCore* lCore = new SpectacleCore(); | ||
59 | | ||||
60 | QCommandLineParser lCmdLineParser; | | |||
61 | aboutData.setupCommandLine(&lCmdLineParser); | | |||
62 | | ||||
63 | lCmdLineParser.addOptions({ | | |||
64 | {{QStringLiteral("f"), QStringLiteral("fullscreen")}, i18n("Capture the entire desktop (default)")}, | | |||
65 | {{QStringLiteral("m"), QStringLiteral("current")}, i18n("Capture the current monitor")}, | | |||
66 | {{QStringLiteral("a"), QStringLiteral("activewindow")}, i18n("Capture the active window")}, | | |||
67 | {{QStringLiteral("u"), QStringLiteral("windowundercursor")}, i18n("Capture the window currently under the cursor, including parents of pop-up menus")}, | | |||
68 | {{QStringLiteral("t"), QStringLiteral("transientonly")}, i18n("Capture the window currently under the cursor, excluding parents of pop-up menus")}, | | |||
69 | {{QStringLiteral("r"), QStringLiteral("region")}, i18n("Capture a rectangular region of the screen")}, | | |||
70 | {{QStringLiteral("g"), QStringLiteral("gui")}, i18n("Start in GUI mode (default)")}, | | |||
71 | {{QStringLiteral("b"), QStringLiteral("background")}, i18n("Take a screenshot and exit without showing the GUI")}, | | |||
72 | {{QStringLiteral("s"), QStringLiteral("dbus")}, i18n("Start in DBus-Activation mode")}, | | |||
73 | {{QStringLiteral("n"), QStringLiteral("nonotify")}, i18n("In background mode, do not pop up a notification when the screenshot is taken")}, | | |||
74 | {{QStringLiteral("o"), QStringLiteral("output")}, i18n("In background mode, save image to specified file"), QStringLiteral("fileName")}, | | |||
75 | {{QStringLiteral("d"), QStringLiteral("delay")}, i18n("In background mode, delay before taking the shot (in milliseconds)"), QStringLiteral("delayMsec")}, | | |||
76 | {{QStringLiteral("c"), QStringLiteral("clipboard")}, i18n("In background mode, copy screenshot to clipboard")}, | | |||
77 | {{QStringLiteral("w"), QStringLiteral("onclick")}, i18n("Wait for a click before taking screenshot. Invalidates delay")} | | |||
78 | }); | | |||
79 | | ||||
80 | lCmdLineParser.process(lApp); | | |||
81 | aboutData.processCommandLine(&lCmdLineParser); | | |||
82 | | ||||
83 | // extract the capture mode | | |||
84 | | ||||
85 | Spectacle::CaptureMode lCaptureMode = Spectacle::CaptureMode::AllScreens; | | |||
86 | if (lCmdLineParser.isSet(QStringLiteral("current"))) { | | |||
87 | lCaptureMode = Spectacle::CaptureMode::CurrentScreen; | | |||
88 | } else if (lCmdLineParser.isSet(QStringLiteral("activewindow"))) { | | |||
89 | lCaptureMode = Spectacle::CaptureMode::ActiveWindow; | | |||
90 | } else if (lCmdLineParser.isSet(QStringLiteral("region"))) { | | |||
91 | lCaptureMode = Spectacle::CaptureMode::RectangularRegion; | | |||
92 | } else if (lCmdLineParser.isSet(QStringLiteral("windowundercursor"))) { | | |||
93 | lCaptureMode = Spectacle::CaptureMode::TransientWithParent; | | |||
94 | } else if (lCmdLineParser.isSet(QStringLiteral("transientonly"))) { | | |||
95 | lCaptureMode = Spectacle::CaptureMode::WindowUnderCursor; | | |||
96 | } | | |||
97 | | ||||
98 | // are we running in background or dbus mode? | | |||
99 | 71 | | |||
100 | SpectacleCore::StartMode lStartMode = SpectacleCore::StartMode::Gui; | 72 | QCommandLineParser *lCmdLineParser = new QCommandLineParser(); | ||
101 | bool lNotify = true; | 73 | aboutData.setupCommandLine(lCmdLineParser); | ||
102 | bool lCopyToClipboard = false; | | |||
103 | qint64 lDelayMsec = 0; | | |||
104 | QString lFileName = QString(); | | |||
105 | | ||||
106 | if (lCmdLineParser.isSet(QStringLiteral("background"))) { | | |||
107 | lStartMode = SpectacleCore::StartMode::Background; | | |||
108 | } else if (lCmdLineParser.isSet(QStringLiteral("dbus"))) { | | |||
109 | lStartMode = SpectacleCore::StartMode::DBus; | | |||
110 | } | | |||
111 | 74 | | |||
112 | switch(lStartMode) { | 75 | lCore->populateCommandLineParser(lCmdLineParser); | ||
113 | case SpectacleCore::StartMode::Background: | | |||
114 | if (lCmdLineParser.isSet(QStringLiteral("nonotify"))) { | | |||
115 | lNotify = false; | | |||
116 | } | | |||
117 | 76 | | |||
118 | if (lCmdLineParser.isSet(QStringLiteral("output"))) { | 77 | // first parsing for help-about | ||
119 | lFileName = lCmdLineParser.value(QStringLiteral("output")); | 78 | lCmdLineParser->process(lApp.arguments()); | ||
120 | } | 79 | aboutData.processCommandLine(lCmdLineParser); | ||
121 | 80 | | |||
122 | if (lCmdLineParser.isSet(QStringLiteral("delay"))) { | 81 | atexit(deleteQApplication); | ||
123 | bool lParseOk = false; | 82 | // Ensure that we only launch a new instance if we need to | ||
124 | qint64 lDelayValue = lCmdLineParser.value(QStringLiteral("delay")).toLongLong(&lParseOk); | 83 | // If there is already an instance running, we will quit here | ||
125 | if (lParseOk) { | 84 | // and activateRequested signal is triggered | ||
126 | lDelayMsec = lDelayValue; | 85 | KDBusService service(KDBusService::Unique, lCore); | ||
127 | } | 86 | atexit(qt_noop); | ||
128 | } | | |||
129 | 87 | | |||
130 | if (lCmdLineParser.isSet(QStringLiteral("onclick"))) { | 88 | s_needToDeleteQApplication = false; | ||
131 | lDelayMsec = -1; | | |||
132 | } | | |||
133 | 89 | | |||
Just use second function atexit(deleteQApplication); KDBusService service(KDBusService::Unique, lCore); // it does not need to be heap allocation atexit(noopfunc); anthonyfieroni: Just use second function
```
atexit(deleteQApplication);
KDBusService service(KDBusService… | |||||
134 | if (lCmdLineParser.isSet(QStringLiteral("clipboard"))) { | 90 | // Delay initialisation after we now we are in the single instance, to avoid doing it each time spectacle executable is called | ||
135 | lCopyToClipboard = true; | 91 | lCore->init(); | ||
136 | } | | |||
137 | 92 | | |||
davidre: whitespace | |||||
138 | lApp.setQuitOnLastWindowClosed(false); | 93 | // set up the KDBusService activateRequested slot | ||
139 | break; | 94 | QObject::connect(&service, &KDBusService::activateRequested, lCore, &SpectacleCore::onActivateRequested); | ||
140 | case SpectacleCore::StartMode::DBus: | | |||
141 | lApp.setQuitOnLastWindowClosed(false); | | |||
142 | break; | | |||
143 | case SpectacleCore::StartMode::Gui: | | |||
144 | break; | | |||
145 | } | | |||
146 | 95 | | |||
147 | // release the kraken | 96 | QObject::connect(lCore, &SpectacleCore::allDone, &lApp, &QCoreApplication::quit); | ||
148 | | ||||
149 | SpectacleCore lCore(lStartMode, lCaptureMode, lFileName, lDelayMsec, lNotify, lCopyToClipboard); | | |||
150 | QObject::connect(&lCore, &SpectacleCore::allDone, qApp, &QApplication::quit); | | |||
151 | QObject::connect(qApp, &QApplication::aboutToQuit, Settings::self(), &Settings::save); | 97 | QObject::connect(qApp, &QApplication::aboutToQuit, Settings::self(), &Settings::save); | ||
152 | 98 | | |||
153 | // create the dbus connections | 99 | // create the dbus connections | ||
154 | 100 | SpectacleDBusAdapter *lDBusAdapter = new SpectacleDBusAdapter(lCore); | |||
155 | new KDBusService(KDBusService::Multiple, &lCore); | 101 | QObject::connect(lCore, &SpectacleCore::grabFailed, lDBusAdapter, &SpectacleDBusAdapter::ScreenshotFailed); | ||
156 | SpectacleDBusAdapter *lDBusAdapter = new SpectacleDBusAdapter(&lCore); | 102 | QObject::connect(ExportManager::instance(), &ExportManager::imageSaved, lCore, [&](const QUrl &savedAt) { | ||
157 | QObject::connect(&lCore, &SpectacleCore::grabFailed, lDBusAdapter, &SpectacleDBusAdapter::ScreenshotFailed); | 103 | lDBusAdapter->ScreenshotTaken(savedAt.toLocalFile()); | ||
158 | QObject::connect(ExportManager::instance(), &ExportManager::imageSaved, &lCore, [&](const QUrl &savedAt) { | | |||
159 | emit lDBusAdapter->ScreenshotTaken(savedAt.toLocalFile()); | | |||
160 | }); | 104 | }); | ||
161 | QDBusConnection::sessionBus().registerObject(QStringLiteral("/"), &lCore); | 105 | QDBusConnection::sessionBus().registerObject(QStringLiteral("/"), lCore); | ||
162 | QDBusConnection::sessionBus().registerService(QStringLiteral("org.kde.Spectacle")); | 106 | QDBusConnection::sessionBus().registerService(QStringLiteral("org.kde.Spectacle")); | ||
163 | 107 | | |||
164 | // fire it up | 108 | // fire it up | ||
109 | lCore->onActivateRequested(lApp.arguments(), QStringLiteral("")); | ||||
165 | 110 | | |||
166 | return lApp.exec(); | 111 | return lApp.exec(); | ||
167 | } | 112 | } |
s_needToDeleteQApplication is not needed any more.