From f18a119ac057a3256efffb3ec7d131949ccf48d3 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Mon, 19 Dec 2011 18:49:07 -0500 Subject: [PATCH] Implement "Start on window system startup" on Win32 + Linux. --- bitcoin-qt.pro | 6 +- src/init.cpp | 155 ++++++++++++++++++++++++++++++++++++++++ src/init.h | 3 + src/qt/optionsmodel.cpp | 5 +- 4 files changed, 166 insertions(+), 3 deletions(-) diff --git a/bitcoin-qt.pro b/bitcoin-qt.pro index 2b980ba86..cbb92353b 100644 --- a/bitcoin-qt.pro +++ b/bitcoin-qt.pro @@ -251,10 +251,14 @@ isEmpty(BOOST_INCLUDE_PATH) { macx:BOOST_INCLUDE_PATH = /opt/local/include } -windows:LIBS += -lws2_32 +windows:LIBS += -lws2_32 -lshlwapi windows:DEFINES += WIN32 windows:RC_FILE = src/qt/res/bitcoin-qt.rc +!windows:!mac { + DEFINES += LINUX +} + macx:HEADERS += src/qt/macdockiconhandler.h macx:OBJECTIVE_SOURCES += src/qt/macdockiconhandler.mm macx:LIBS += -framework Foundation -framework ApplicationServices -framework AppKit diff --git a/src/init.cpp b/src/init.cpp index 76317557b..0d83b345f 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -10,6 +10,7 @@ #include "strlcpy.h" #include #include +#include #include #if defined(BITCOIN_NEED_QT_PLUGINS) && !defined(_BITCOIN_QT_PLUGINS_INCLUDED) @@ -523,6 +524,11 @@ bool AppInit2(int argc, char* argv[]) if (fServer) CreateThread(ThreadRPCServer, NULL); +#ifdef QT_GUI + if(GetStartOnSystemStartup()) + SetStartOnSystemStartup(true); // Remove startup links to bitcoin-wx +#endif + #if !defined(QT_GUI) while (1) Sleep(5000); @@ -530,3 +536,152 @@ bool AppInit2(int argc, char* argv[]) return true; } + +#ifdef WIN32 +string StartupShortcutPath() +{ + return MyGetSpecialFolderPath(CSIDL_STARTUP, true) + "\\Bitcoin.lnk"; +} + +bool GetStartOnSystemStartup() +{ + return filesystem::exists(StartupShortcutPath().c_str()); +} + +bool SetStartOnSystemStartup(bool fAutoStart) +{ + // If the shortcut exists already, remove it for updating + remove(StartupShortcutPath().c_str()); + + if (fAutoStart) + { + CoInitialize(NULL); + + // Get a pointer to the IShellLink interface. + IShellLink* psl = NULL; + HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL, + CLSCTX_INPROC_SERVER, IID_IShellLink, + reinterpret_cast(&psl)); + + if (SUCCEEDED(hres)) + { + // Get the current executable path + TCHAR pszExePath[MAX_PATH]; + GetModuleFileName(NULL, pszExePath, sizeof(pszExePath)); + + TCHAR pszArgs[5] = TEXT("-min"); + + // Set the path to the shortcut target + psl->SetPath(pszExePath); + PathRemoveFileSpec(pszExePath); + psl->SetWorkingDirectory(pszExePath); + psl->SetShowCmd(SW_SHOWMINNOACTIVE); + psl->SetArguments(pszArgs); + + // Query IShellLink for the IPersistFile interface for + // saving the shortcut in persistent storage. + IPersistFile* ppf = NULL; + hres = psl->QueryInterface(IID_IPersistFile, + reinterpret_cast(&ppf)); + if (SUCCEEDED(hres)) + { + WCHAR pwsz[MAX_PATH]; + // Ensure that the string is ANSI. + MultiByteToWideChar(CP_ACP, 0, StartupShortcutPath().c_str(), -1, pwsz, MAX_PATH); + // Save the link by calling IPersistFile::Save. + hres = ppf->Save(pwsz, TRUE); + ppf->Release(); + psl->Release(); + CoUninitialize(); + return true; + } + psl->Release(); + } + CoUninitialize(); + return false; + } + return true; +} + +#elif defined(LINUX) + +// Follow the Desktop Application Autostart Spec: +// http://standards.freedesktop.org/autostart-spec/autostart-spec-latest.html + +boost::filesystem::path GetAutostartDir() +{ + namespace fs = boost::filesystem; + + char* pszConfigHome = getenv("XDG_CONFIG_HOME"); + if (pszConfigHome) return fs::path(pszConfigHome) / fs::path("autostart"); + char* pszHome = getenv("HOME"); + if (pszHome) return fs::path(pszHome) / fs::path(".config/autostart"); + return fs::path(); +} + +boost::filesystem::path GetAutostartFilePath() +{ + return GetAutostartDir() / boost::filesystem::path("bitcoin.desktop"); +} + +bool GetStartOnSystemStartup() +{ + boost::filesystem::ifstream optionFile(GetAutostartFilePath()); + if (!optionFile.good()) + return false; + // Scan through file for "Hidden=true": + string line; + while (!optionFile.eof()) + { + getline(optionFile, line); + if (line.find("Hidden") != string::npos && + line.find("true") != string::npos) + return false; + } + optionFile.close(); + + return true; +} + +bool SetStartOnSystemStartup(bool fAutoStart) +{ + if (!fAutoStart) + { +#if defined(BOOST_FILESYSTEM_VERSION) && BOOST_FILESYSTEM_VERSION >= 3 + unlink(GetAutostartFilePath().string().c_str()); +#else + unlink(GetAutostartFilePath().native_file_string().c_str()); +#endif + } + else + { + char pszExePath[MAX_PATH+1]; + memset(pszExePath, 0, sizeof(pszExePath)); + if (readlink("/proc/self/exe", pszExePath, sizeof(pszExePath)-1) == -1) + return false; + + boost::filesystem::create_directories(GetAutostartDir()); + + boost::filesystem::ofstream optionFile(GetAutostartFilePath(), ios_base::out|ios_base::trunc); + if (!optionFile.good()) + return false; + // Write a bitcoin.desktop file to the autostart directory: + optionFile << "[Desktop Entry]\n"; + optionFile << "Type=Application\n"; + optionFile << "Name=Bitcoin\n"; + optionFile << "Exec=" << pszExePath << " -min\n"; + optionFile << "Terminal=false\n"; + optionFile << "Hidden=false\n"; + optionFile.close(); + } + return true; +} +#else + +// TODO: OSX startup stuff; see: +// http://developer.apple.com/mac/library/documentation/MacOSX/Conceptual/BPSystemStartup/Articles/CustomLogin.html + +bool GetStartOnSystemStartup() { return false; } +bool SetStartOnSystemStartup(bool fAutoStart) { return false; } + +#endif diff --git a/src/init.h b/src/init.h index 4017f2570..6721b2899 100644 --- a/src/init.h +++ b/src/init.h @@ -11,4 +11,7 @@ void Shutdown(void* parg); bool AppInit(int argc, char* argv[]); bool AppInit2(int argc, char* argv[]); +bool GetStartOnSystemStartup(); +bool SetStartOnSystemStartup(bool fAutoStart); + #endif diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index efc216dab..35d0b57e7 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -2,6 +2,7 @@ #include "bitcoinunits.h" #include "headers.h" +#include "init.h" OptionsModel::OptionsModel(CWallet *wallet, QObject *parent) : QAbstractListModel(parent), @@ -27,7 +28,7 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const switch(index.row()) { case StartAtStartup: - return QVariant(); + return QVariant(GetStartOnSystemStartup()); case MinimizeToTray: return QVariant(fMinimizeToTray); case MapPortUPnP: @@ -62,7 +63,7 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in switch(index.row()) { case StartAtStartup: - successful = false; /*TODO*/ + successful = SetStartOnSystemStartup(value.toBool()); break; case MinimizeToTray: fMinimizeToTray = value.toBool();