diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 58b6f0e..74f6ec9 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -11,6 +11,7 @@ permissions: contents: read env: BUILD_TYPE: Release + GITHUB_ACTIONS: true jobs: build: name: ${{ matrix.config.name }} diff --git a/CMakeLists.txt b/CMakeLists.txt index d2ec206..a68d2af 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,7 +19,8 @@ include(GNUInstallDirs) #libaura Library Setup include_directories(${PROJECT_SOURCE_DIR}/include) add_library (${PROJECT_NAME} - src/events/eventargs.cpp + src/filesystem/filesystemchangedeventargs.cpp + src/filesystem/filesystemwatcher.cpp src/helpers/stringhelpers.cpp src/helpers/webhelpers.cpp src/keyring/credential.cpp @@ -33,6 +34,7 @@ add_library (${PROJECT_NAME} src/network/networkstatechangedeventargs.cpp src/notifications/notificationsenteventargs.cpp src/notifications/shellnotificationsenteventargs.cpp + src/taskbar/taskbaritem.cpp src/update/updater.cpp src/appinfo.cpp src/aura.cpp @@ -53,15 +55,17 @@ find_package(maddy REQUIRED) find_package(sqlcipher REQUIRED CONFIG) target_link_libraries(${PROJECT_NAME} PUBLIC Intl::Intl JsonCpp::JsonCpp CURL::libcurl maddy::maddy sqlcipher::sqlcipher) if(LINUX) - find_package(PkgConfig REQUIRED) + set(THREADS_PREFER_PTHREAD_FLAG ON) + find_package(Threads REQUIRED) find_package(libsecret REQUIRED CONFIG) find_package(libuuid REQUIRED CONFIG) + find_package(PkgConfig REQUIRED) pkg_check_modules(glib-2.0 REQUIRED IMPORTED_TARGET glib-2.0) pkg_check_modules(gio-2.0 REQUIRED IMPORTED_TARGET gio-2.0) pkg_check_modules(gmodule-2.0 REQUIRED IMPORTED_TARGET gmodule-2.0) pkg_check_modules(gobject-2.0 REQUIRED IMPORTED_TARGET gobject-2.0) pkg_check_modules(gthread-2.0 REQUIRED IMPORTED_TARGET gthread-2.0) - target_link_libraries(${PROJECT_NAME} PUBLIC libsecret::libsecret libuuid::libuuid PkgConfig::glib-2.0 PkgConfig::gio-2.0 PkgConfig::gmodule-2.0 PkgConfig::gobject-2.0 PkgConfig::gthread-2.0) + target_link_libraries(${PROJECT_NAME} PUBLIC Threads::Threads libsecret::libsecret libuuid::libuuid PkgConfig::glib-2.0 PkgConfig::gio-2.0 PkgConfig::gmodule-2.0 PkgConfig::gobject-2.0 PkgConfig::gthread-2.0) endif() if(MSVC) target_compile_options(${PROJECT_NAME} PRIVATE /W4) @@ -82,6 +86,7 @@ if(NOT SKIP_TESTS) add_executable(${PROJECT_NAME}_test tests/auratests.cpp tests/eventtests.cpp + tests/filewatchertests.cpp tests/keyringtests.cpp tests/main.cpp tests/networktests.cpp @@ -89,6 +94,7 @@ if(NOT SKIP_TESTS) tests/storetests.cpp tests/stringtests.cpp tests/systemcredentialstests.cpp + tests/taskbartests.cpp tests/updatertests.cpp tests/versiontests.cpp tests/webtests.cpp) diff --git a/include/aura.h b/include/aura.h index 4fe497d..14b28cb 100644 --- a/include/aura.h +++ b/include/aura.h @@ -1,3 +1,7 @@ +#if (defined(_WIN32) && !defined(_CRT_SECURE_NO_WARNINGS)) +#define _CRT_SECURE_NO_WARNINGS +#endif + #ifndef AURA_H #define AURA_H @@ -15,10 +19,6 @@ namespace Nickvision::Aura class Aura { public: - /** - * @brief Destructs an Aura object. - */ - ~Aura(); /** * @brief Gets the AppInfo object for the application. */ @@ -35,15 +35,15 @@ namespace Nickvision::Aura static_assert(std::is_base_of_v == true, "T must derive from ConfigurationBase"); if (!m_configFiles.contains(key)) { - m_configFiles[key] = static_cast(new T(key)); + m_configFiles[key] = std::make_unique(key); } - return *static_cast(m_configFiles[key]); + return *static_cast(m_configFiles[key].get()); } private: Aura(const std::string& id, const std::string& name); AppInfo m_appInfo; - std::map m_configFiles; + std::map> m_configFiles; public: /** @@ -59,6 +59,12 @@ namespace Nickvision::Aura * @return The active aura instance */ static Aura& getActive(); + /** + * @brief Gets a system environment variable. + * @param key The environment variable to get + * @return The environment variable if found, else empty string + */ + static std::string getEnvVar(const std::string& key); private: static std::unique_ptr m_instance; diff --git a/include/events/eventargs.h b/include/events/eventargs.h index 820e44c..2be2f0a 100644 --- a/include/events/eventargs.h +++ b/include/events/eventargs.h @@ -12,7 +12,7 @@ namespace Nickvision::Aura::Events /** * @brief Constructs an EventArgs. */ - EventArgs(); + EventArgs() = default; }; } diff --git a/include/filesystem/fileaction.h b/include/filesystem/fileaction.h new file mode 100644 index 0000000..fa7ee19 --- /dev/null +++ b/include/filesystem/fileaction.h @@ -0,0 +1,18 @@ +#ifndef FILEACTION_H +#define FILEACTION_H + +namespace Nickvision::Aura::Filesystem +{ + /** + * @brief Actions that cause a file to change. + */ + enum class FileAction + { + Added = 1, + Removed, + Modified, + Renamed + }; +} + +#endif //FILEACTION_H \ No newline at end of file diff --git a/include/filesystem/filesystemchangedeventargs.h b/include/filesystem/filesystemchangedeventargs.h new file mode 100644 index 0000000..4ca0937 --- /dev/null +++ b/include/filesystem/filesystemchangedeventargs.h @@ -0,0 +1,39 @@ +#ifndef FILESYSTEMCHANGEDEVENTARGS_H +#define FILESYSTEMCHANGEDEVENTARGS_H + +#include +#include "fileaction.h" +#include "events/eventargs.h" + +namespace Nickvision::Aura::Filesystem +{ + /** + * @brief Event args for when a file system object is changed. + */ + class FileSystemChangedEventArgs : public Events::EventArgs + { + public: + /** + * @brief Constructs a FileSystemChangedEventArgs. + * @param path The path of the file/folder that changed + * @param why The action that caused the file to change + */ + FileSystemChangedEventArgs(const std::filesystem::path& path, FileAction why); + /** + * @brief Gets the path of the changed file system object. + * @return The path of the changed file/folder + */ + const std::filesystem::path& getPath() const; + /** + * @brief Gets the action that caused the file to change. + * @return The action that caused the file to change + */ + FileAction getWhy() const; + + private: + std::filesystem::path m_path; + FileAction m_why; + }; +} + +#endif //FILESYSTEMCHANGEDEVENTARGS_H \ No newline at end of file diff --git a/include/filesystem/filesystemwatcher.h b/include/filesystem/filesystemwatcher.h new file mode 100644 index 0000000..eca7804 --- /dev/null +++ b/include/filesystem/filesystemwatcher.h @@ -0,0 +1,100 @@ +#ifndef FILESYSTEMWATCHER_H +#define FILESYSTEMWATCHER_H + +#include +#include +#include +#include +#include "filesystemchangedeventargs.h" +#include "watcherflags.h" +#include "events/event.h" +#ifdef _WIN32 +#include +#endif + +namespace Nickvision::Aura::Filesystem +{ + /** + * @brief A watcher of a file system folder. + */ + class FileSystemWatcher + { + public: + /** + * @brief Constructs a FileSystemWatcher. + * @param path The path of the folder to watch + * @param includeSubdirectories Whether or not to include subdirectories for the folder + * @param watcherFlags The flags of what to watch changes for + * @exception std::runtime_error Thrown if unable to initialize watcher + */ + FileSystemWatcher(const std::filesystem::path& path, bool includeSubdirectories, WatcherFlags watcherFlags = WatcherFlags::FileName | WatcherFlags::DirectoryName | WatcherFlags::Attributes | WatcherFlags::Size | WatcherFlags::LastWrite | WatcherFlags::LastAccess); + /** + * @brief Deconstructs a FileSystemWatcher. + */ + ~FileSystemWatcher(); + /** + * @brief Gets the path of the file system object being watched. + * @return The path of the folder being watched + */ + const std::filesystem::path& getPath() const; + /** + * @brief Gets the flags of what to watch changed for. + * @return The flags of watched properties + */ + WatcherFlags getWatcherFlags() const; + /** + * @brief Gets whether or not subdirectories of the folder are watched. + * @return True if subdirectories watched, else false + */ + bool getIncludeSubdirectories() const; + /** + * @brief Gets the event for when the watched file system object is changed. + * @return The changed event + */ + Events::Event& changed(); + /** + * @brief Gets whether or not the file extension is being watched. + * @param extension The file extension to check + * @return True if watched, else false + */ + bool containsExtension(const std::filesystem::path& extension); + /** + * @brief Adds an extension of a file to watch for changes in the folder. + * @param extension The file extension to add + * @return True if successful, else false + */ + bool addExtensionFilter(const std::filesystem::path& extension); + /** + * @brief Removes an extension of a file to watch for changes in the folder. + * @param extension The file extension to remove + * @return True if successful, else false + */ + bool removeExtensionFilter(const std::filesystem::path& extension); + /** + * @brief Clears all extensions to watch. + * @return True if successful, else false + */ + bool clearExtensionFilters(); + + private: + /** + * @brief Runs the loop to watch a folder for changes. + */ + void watch(); + mutable std::mutex m_mutex; + std::filesystem::path m_path; + bool m_includeSubdirectories; + WatcherFlags m_watcherFlags; + Events::Event m_changed; + bool m_watching; + std::vector m_extensionFilters; + std::jthread m_watchThread; +#ifdef _WIN32 + HANDLE m_terminateEvent; +#elif defined(__linux__) + int m_notify; +#endif + }; +} + +#endif //FILESYSTEMWATCHER_H \ No newline at end of file diff --git a/include/filesystem/watcherflags.h b/include/filesystem/watcherflags.h new file mode 100644 index 0000000..bbb6b8e --- /dev/null +++ b/include/filesystem/watcherflags.h @@ -0,0 +1,24 @@ +#ifndef WATCHERFLAGS_H +#define WATCHERFLAGS_H + +#include "enumflags.h" + +namespace Nickvision::Aura::Filesystem +{ + /** + * @brief Flags to describe properties of a file system object that can change. + */ + enum class WatcherFlags + { + FileName = 1, + DirectoryName = 2, + Attributes = 4, + Size = 8, + LastWrite = 16, + LastAccess = 32 + }; + + DEFINE_ENUM_FLAG_OPERATORS(WatcherFlags); +} + +#endif \ No newline at end of file diff --git a/include/network/networkmonitor.h b/include/network/networkmonitor.h index db9cf7e..373c70b 100644 --- a/include/network/networkmonitor.h +++ b/include/network/networkmonitor.h @@ -1,10 +1,6 @@ #ifndef NETWORKMONITOR_H #define NETWORKMONITOR_H -#ifdef _WIN32 -#define _CRT_SECURE_NO_WARNINGS -#endif - #include "networkstatechangedeventargs.h" #include "events/event.h" @@ -42,7 +38,7 @@ namespace Nickvision::Aura::Network private: Events::Event m_stateChanged; NetworkState m_connectionState; -#ifndef _WIN32 +#ifdef __linux__ unsigned long m_networkChangedHandlerId; #endif }; diff --git a/include/notifications/notificationsenteventargs.h b/include/notifications/notificationsenteventargs.h index d28b453..1012028 100644 --- a/include/notifications/notificationsenteventargs.h +++ b/include/notifications/notificationsenteventargs.h @@ -41,6 +41,7 @@ namespace Nickvision::Aura::Notifications * @return The parameter of the additional action */ const std::string& getActionParam() const; + protected: std::string m_message; NotificationSeverity m_severity; diff --git a/include/notifications/shellnotificationsenteventargs.h b/include/notifications/shellnotificationsenteventargs.h index 4505775..2d9c082 100644 --- a/include/notifications/shellnotificationsenteventargs.h +++ b/include/notifications/shellnotificationsenteventargs.h @@ -25,6 +25,7 @@ namespace Nickvision::Aura::Notifications * @return The title of the notification */ const std::string& getTitle() const; + protected: std::string m_title; }; diff --git a/include/systemdirectories.h b/include/systemdirectories.h index de92a8a..22ffcd2 100644 --- a/include/systemdirectories.h +++ b/include/systemdirectories.h @@ -1,10 +1,6 @@ #ifndef SYSTEMDIRECTORIES_H #define SYSTEMDIRECTORIES_H -#ifdef _WIN32 -#define _CRT_SECURE_NO_WARNINGS -#endif - #include #include diff --git a/include/taskbar/progressstate.h b/include/taskbar/progressstate.h new file mode 100644 index 0000000..c3598a4 --- /dev/null +++ b/include/taskbar/progressstate.h @@ -0,0 +1,19 @@ +#ifndef PROGRESSSTATE_H +#define PROGRESSSTATE_H + +namespace Nickvision::Aura::Taskbar +{ + /** + * @brief States of progress on a taskbar button. + */ + enum class ProgressState + { + NoProgress = 0, + Indeterminate = 1, + Normal = 2, + Error = 4, + Paused = 8 + }; +} + +#endif //PROGRESSSTATE_H \ No newline at end of file diff --git a/include/taskbar/taskbaritem.h b/include/taskbar/taskbaritem.h new file mode 100644 index 0000000..2048a74 --- /dev/null +++ b/include/taskbar/taskbaritem.h @@ -0,0 +1,117 @@ +#ifndef TASKBARITEM_H +#define TASKBARITEM_H + +#include +#include +#include "progressstate.h" +#ifdef _WIN32 +#include +#include +#include +#include +#pragma comment(lib,"gdiplus.lib") +#elif defined(__linux__) +#include +#include +#endif + +namespace Nickvision::Aura::Taskbar +{ + /** + * @brief An item on the taskbar. + */ + class TaskbarItem + { + public: + /** + * @brief Constructs a TaskbarItem. + */ + TaskbarItem(); + /** + * @brief Deconstructs a TaskbarItem. + */ + ~TaskbarItem(); + /** + * @brieg Gets the state of the progress. + * @return ProgressState + */ + ProgressState getProgressState() const; + /** + * @brief Sets the state of the progress. + * @param state The new ProgressState + */ + void setProgressState(ProgressState state); + /** + * @brief Gets the value of the progress. + * @return The progress value + */ + double getProgress() const; + /** + * @brief Sets the value of the progress. Settings the progress value will set the progress state to normal. + * @param progress The new progress value + */ + void setProgress(double progress); + /** + * @brief Gets whether or not the taskbar item is shown in an urgent state. + * @return True if in urgent state, else false + */ + bool getUrgent() const; + /** + * @brief Sets whether or not the taskbar item is shown in an urgent state. + * @param urgent True for urgent state, else false + */ + void setUrgent(bool urgent); + /** + * @brief Gets whether or not the count is visible on the taskbar item. + * @return True if count visible, else false + */ + bool getCountVisible() const; + /** + * @brief Sets whether or not the count is visible on the taskbar item. + * @param countVisible True for visible count, else false + */ + void setCountVisible(bool countVisible); + /** + * @brief Gets the count shown on the taskbar item. + * @return The count value + */ + long getCount() const; + /** + * @brief Sets the count shown on the taskbar item. + * @param count The new count value + */ + void setCount(long count); +#ifdef _WIN32 + /** + * @brief Connects a taskbar item to the application. + * @param hwnd The HWND of the application + * @return True if connection successful, else false + */ + bool connect(HWND hwnd); +#elif defined(__linux__) + /** + * @brief Connects a taskbar item to the application. + * @param desktopFile Desktop file name with extension + * @return True if connection successful, else false + */ + bool connect(const std::string& desktopFile); +#endif + + private: + ProgressState m_progressState; + double m_progress; + bool m_urgent; + bool m_countVisible; + long m_count; +#ifdef _WIN32 + HWND m_hwnd; + CComPtr m_taskbar; + ULONG_PTR m_gdi; +#elif defined(__linux__) + std::shared_ptr m_connection; + std::string m_appUri; +#endif + }; +} + +#endif //TASKBARITEM_H \ No newline at end of file diff --git a/include/update/updater.h b/include/update/updater.h index 7e4c603..fcaeb25 100644 --- a/include/update/updater.h +++ b/include/update/updater.h @@ -30,12 +30,14 @@ namespace Nickvision::Aura::Update * @return The current preview version if available, else empty Version */ Version fetchCurrentPreviewVersion(); +#ifdef _WIN32 /** * @brief Downloads and installs an application update for Windows. getCurrentStableVersion or getCurrentPreviewVersion should be called first before running this method. This method will force quit the current running application to install the update. * @param versionType The type of version update to install * @return True if successful, else false */ bool windowsUpdate(VersionType versionType); +#endif private: mutable std::mutex m_mutex; diff --git a/include/userdirectories.h b/include/userdirectories.h index 0eab76b..e7cea51 100644 --- a/include/userdirectories.h +++ b/include/userdirectories.h @@ -1,10 +1,6 @@ #ifndef USERDIRECTORIES_H #define USERDIRECTORIES_H -#ifdef _WIN32 -#define _CRT_SECURE_NO_WARNINGS -#endif - #include namespace Nickvision::Aura::UserDirectories diff --git a/src/aura.cpp b/src/aura.cpp index 598bc13..5e99855 100644 --- a/src/aura.cpp +++ b/src/aura.cpp @@ -1,4 +1,5 @@ #include "aura.h" +#include #include namespace Nickvision::Aura @@ -11,14 +12,6 @@ namespace Nickvision::Aura m_appInfo.setName(name); } - Aura::~Aura() - { - for (const std::pair& pair : m_configFiles) - { - delete pair.second; - } - } - AppInfo& Aura::getAppInfo() { return m_appInfo; @@ -41,4 +34,14 @@ namespace Nickvision::Aura } return *m_instance; } + + std::string Aura::getEnvVar(const std::string& key) + { + char* var = std::getenv(key.c_str()); + if (var) + { + return { var }; + } + return ""; + } } \ No newline at end of file diff --git a/src/events/eventargs.cpp b/src/events/eventargs.cpp deleted file mode 100644 index 988b96d..0000000 --- a/src/events/eventargs.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#include "events/eventargs.h" - -namespace Nickvision::Aura::Events -{ - EventArgs::EventArgs() - { - - } -} \ No newline at end of file diff --git a/src/filesystem/filesystemchangedeventargs.cpp b/src/filesystem/filesystemchangedeventargs.cpp new file mode 100644 index 0000000..65f72c0 --- /dev/null +++ b/src/filesystem/filesystemchangedeventargs.cpp @@ -0,0 +1,21 @@ +#include "filesystem/filesystemchangedeventargs.h" + +namespace Nickvision::Aura::Filesystem +{ + FileSystemChangedEventArgs::FileSystemChangedEventArgs(const std::filesystem::path& path, FileAction why) + : m_path{ path }, + m_why{ why } + { + + } + + const std::filesystem::path& FileSystemChangedEventArgs::getPath() const + { + return m_path; + } + + FileAction FileSystemChangedEventArgs::getWhy() const + { + return m_why; + } +} \ No newline at end of file diff --git a/src/filesystem/filesystemwatcher.cpp b/src/filesystem/filesystemwatcher.cpp new file mode 100644 index 0000000..566843a --- /dev/null +++ b/src/filesystem/filesystemwatcher.cpp @@ -0,0 +1,231 @@ +#include "filesystem/filesystemwatcher.h" +#include +#include +#ifdef __linux__ +#include +#include +#include +#endif + +namespace Nickvision::Aura::Filesystem +{ + FileSystemWatcher::FileSystemWatcher(const std::filesystem::path& path, bool incudeSubdirectories, WatcherFlags watcherFlags) + : m_path{ path }, + m_includeSubdirectories{ incudeSubdirectories }, + m_watcherFlags{ watcherFlags }, + m_watching{ true } + { +#ifdef _WIN32 + m_terminateEvent = CreateEventA(nullptr, 1, 0, nullptr); + if (!m_terminateEvent) + { + throw std::runtime_error("Unable to create event."); + } +#elif defined(__linux__) + m_notify = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); + if (m_notify == -1) + { + throw std::runtime_error("Unable to init inotify."); + } +#endif + m_watchThread = std::jthread(&FileSystemWatcher::watch, this); + } + + FileSystemWatcher::~FileSystemWatcher() + { + m_watching = false; +#ifdef _WIN32 + SetEvent(m_terminateEvent); + CloseHandle(m_terminateEvent); +#elif defined(__linux__) + close(m_notify); +#endif + } + + const std::filesystem::path& FileSystemWatcher::getPath() const + { + return m_path; + } + + WatcherFlags FileSystemWatcher::getWatcherFlags() const + { + return m_watcherFlags; + } + + bool FileSystemWatcher::getIncludeSubdirectories() const + { + return m_includeSubdirectories; + } + + Events::Event& FileSystemWatcher::changed() + { + return m_changed; + } + + bool FileSystemWatcher::containsExtension(const std::filesystem::path& extension) + { + std::lock_guard lock{ m_mutex }; + return std::find(m_extensionFilters.begin(), m_extensionFilters.end(), extension) != m_extensionFilters.end(); + } + + bool FileSystemWatcher::addExtensionFilter(const std::filesystem::path& extension) + { + std::lock_guard lock{ m_mutex }; + if (std::find(m_extensionFilters.begin(), m_extensionFilters.end(), extension) == m_extensionFilters.end()) + { + m_extensionFilters.push_back(extension); + return true; + } + return false; + } + + bool FileSystemWatcher::removeExtensionFilter(const std::filesystem::path& extension) + { + std::lock_guard lock{ m_mutex }; + auto find{ std::find(m_extensionFilters.begin(), m_extensionFilters.end(), extension) }; + if (find != m_extensionFilters.end()) + { + m_extensionFilters.erase(find); + return true; + } + return false; + } + + bool FileSystemWatcher::clearExtensionFilters() + { + std::lock_guard lock{ m_mutex }; + m_extensionFilters.clear(); + return true; + } + + void FileSystemWatcher::watch() + { +#ifdef _WIN32 + HANDLE folder{ CreateFileW(m_path.c_str(), FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, nullptr) }; + if (folder == INVALID_HANDLE_VALUE) + { + return; + } + OVERLAPPED overlapped{ 0 }; + overlapped.hEvent = CreateEvent(nullptr, 1, 0, nullptr); + if (!overlapped.hEvent) + { + CloseHandle(folder); + return; + } + std::vector buffer(1024 * 256); + DWORD bytes{ 0 }; + bool pending{ false }; + HANDLE events[2]{ overlapped.hEvent, m_terminateEvent }; + while (m_watching) + { + pending = ReadDirectoryChangesW(folder, &buffer[0], DWORD(buffer.size()), m_includeSubdirectories ? 1 : 0, DWORD(m_watcherFlags), &bytes, &overlapped, nullptr); + if (WaitForMultipleObjects(2, events, 0, INFINITE) == WAIT_OBJECT_0) + { + if (!GetOverlappedResult(folder, &overlapped, &bytes, 1) || bytes == 0) + { + CloseHandle(folder); + return; + } + pending = false; + FILE_NOTIFY_INFORMATION* info{ reinterpret_cast(&buffer[0]) }; + while (true) + { + if (info->Action != FILE_ACTION_RENAMED_NEW_NAME) + { + std::filesystem::path changed{ std::wstring(info->FileName, info->FileNameLength / sizeof(info->FileName[0])) }; + if (m_extensionFilters.size() == 0 || containsExtension(changed.extension())) + { + m_changed.invoke({ changed , static_cast(info->Action) }); + } + } + if (info->NextEntryOffset == 0) + { + break; + } + info = reinterpret_cast(reinterpret_cast(info) + info->NextEntryOffset); + } + } + else + { + break; + } + } + if (pending) + { + CancelIo(folder); + GetOverlappedResult(folder, &overlapped, &bytes, TRUE); + } + CloseHandle(folder); +#elif defined(__linux__) + int mask{ 0 }; + if ((m_watcherFlags & WatcherFlags::FileName) == WatcherFlags::FileName) + { + mask |= IN_CREATE; + mask |= IN_DELETE; + mask |= IN_MOVED_FROM; + } + if ((m_watcherFlags & WatcherFlags::DirectoryName) == WatcherFlags::DirectoryName) + { + mask |= IN_DELETE_SELF; + mask |= IN_MOVE_SELF; + } + if ((m_watcherFlags & WatcherFlags::Attributes) == WatcherFlags::Attributes) + { + mask |= IN_ATTRIB; + } + if ((m_watcherFlags & WatcherFlags::Size) == WatcherFlags::Size) + { + mask |= IN_MODIFY; + } + if ((m_watcherFlags & WatcherFlags::LastWrite) == WatcherFlags::LastWrite) + { + mask |= IN_CLOSE_WRITE; + } + if ((m_watcherFlags & WatcherFlags::LastAccess) == WatcherFlags::LastAccess) + { + mask |= IN_ACCESS; + mask |= IN_OPEN; + } + int watch{ inotify_add_watch(m_notify, m_path.c_str(), mask) }; + while (m_watching) + { + std::vector buf(1024 * (sizeof(struct inotify_event) + 16)); + ssize_t length{ read(m_notify, &buf[0], buf.size()) }; + if (length < 0) + { + continue; + } + struct inotify_event* event{ nullptr }; + for (ssize_t i = 0; i < length; i += sizeof(struct inotify_event) + event->len) + { + event = reinterpret_cast(&buf[i]); + if (event->len) + { + std::filesystem::path changed{ m_path / event->name }; + if (m_extensionFilters.size() == 0 || containsExtension(changed.extension())) + { + if (event->mask & IN_CREATE) + { + m_changed.invoke({ changed , FileAction::Added }); + } + else if ((event->mask & IN_DELETE) || (event->mask & IN_DELETE_SELF)) + { + m_changed.invoke({ changed , FileAction::Removed }); + } + else if ((event->mask & IN_MOVED_FROM) || (event->mask & IN_MOVE_SELF)) + { + m_changed.invoke({ changed , FileAction::Renamed }); + } + else + { + m_changed.invoke({ changed , FileAction::Modified }); + } + } + } + } + } + inotify_rm_watch(m_notify, watch); +#endif + } +} \ No newline at end of file diff --git a/src/helpers/stringhelpers.cpp b/src/helpers/stringhelpers.cpp index 67b3a2b..7cbdb1b 100644 --- a/src/helpers/stringhelpers.cpp +++ b/src/helpers/stringhelpers.cpp @@ -7,7 +7,7 @@ #include #ifdef _WIN32 #include -#else +#elif defined(__linux__) #include #endif @@ -83,7 +83,7 @@ namespace Nickvision::Aura (unsigned char)win.Data4[6], (unsigned char)win.Data4[7] }; -#else +#elif defined(__linux__) uuid_generate(guid.data()); #endif std::ostringstream out; diff --git a/src/keyring/systemcredentials.cpp b/src/keyring/systemcredentials.cpp index 8f55b6e..0e4d7d3 100644 --- a/src/keyring/systemcredentials.cpp +++ b/src/keyring/systemcredentials.cpp @@ -4,13 +4,13 @@ #ifdef _WIN32 #include #include -#else +#elif defined(__linux__) #include #endif namespace Nickvision::Aura::Keyring { -#ifndef _WIN32 +#ifdef __linux__ static const SecretSchema KEYRING_SCHEMA = { "org.nickvision.aura.keyring", SECRET_SCHEMA_NONE, { { "application", SECRET_SCHEMA_ATTRIBUTE_STRING }, { "NULL", SECRET_SCHEMA_ATTRIBUTE_STRING } } }; #endif @@ -30,7 +30,7 @@ namespace Nickvision::Aura::Keyring return { { credName, credUrl, credUsername, credPassword } }; } } -#else +#elif defined(__linux__) GError* error{ nullptr }; char* password = secret_password_lookup_sync(&KEYRING_SCHEMA, nullptr, &error, "application", name.c_str(), NULL); if (!error && password) @@ -71,7 +71,7 @@ namespace Nickvision::Aura::Keyring bool res = CredWriteA(cred, 0); CredFree(cred); return res; -#else +#elif defined(__linux__) GError* error{ nullptr }; secret_password_store_sync(&KEYRING_SCHEMA, SECRET_COLLECTION_DEFAULT, credential.getName().c_str(), credential.getPassword().c_str(), nullptr, &error, "application", credential.getName().c_str(), NULL); if (error) @@ -102,7 +102,7 @@ namespace Nickvision::Aura::Keyring CredFree(cred); return res; } -#else +#elif defined(__linux__) GError* error{ nullptr }; char* password = secret_password_lookup_sync(&KEYRING_SCHEMA, nullptr, &error, "application", credential.getName().c_str(), NULL); if (!error && password) @@ -125,7 +125,7 @@ namespace Nickvision::Aura::Keyring { #ifdef _WIN32 return CredDeleteA(name.c_str(), CRED_TYPE_GENERIC, 0); -#else +#elif defined(__linux__) GError* error{ nullptr }; bool res = secret_password_clear_sync(&KEYRING_SCHEMA, nullptr, &error, "application", name.c_str(), NULL); if (!error) diff --git a/src/network/networkmonitor.cpp b/src/network/networkmonitor.cpp index c1a921c..96004ed 100644 --- a/src/network/networkmonitor.cpp +++ b/src/network/networkmonitor.cpp @@ -1,11 +1,11 @@ #include "network/networkmonitor.h" -#include +#include "aura.h" #include "helpers/stringhelpers.h" #ifdef _WIN32 #include #include #include -#else +#elif defined(__linux__) #include #include #endif @@ -17,7 +17,7 @@ namespace Nickvision::Aura::Network { #ifdef _WIN32 CoInitialize(nullptr); -#else +#elif defined(__linux__) m_networkChangedHandlerId = g_signal_connect_data(G_OBJECT(g_network_monitor_get_default()), "network-changed", G_CALLBACK((void(*)(GNetworkMonitor*, bool, void*))([](GNetworkMonitor*, bool, void* data) { static_cast(data)->checkConnectionState(); @@ -27,9 +27,7 @@ namespace Nickvision::Aura::Network NetworkMonitor::~NetworkMonitor() { -#ifdef _WIN32 - CoUninitialize(); -#else +#ifdef __linux__ g_signal_handler_disconnect(G_OBJECT(g_network_monitor_get_default()), m_networkChangedHandlerId); #endif } @@ -47,8 +45,7 @@ namespace Nickvision::Aura::Network void NetworkMonitor::checkConnectionState() { NetworkState newState{ NetworkState::Disconnected }; - char* envNoNetCheck{ std::getenv("AURA_DISABLE_NETCHECK") }; - std::string noNetCheck{ envNoNetCheck ? StringHelpers::toLower(envNoNetCheck) : "" }; + std::string noNetCheck{ StringHelpers::toLower(Aura::getEnvVar("AURA_DISABLE_NETCHECK")) }; if (!noNetCheck.empty() && (noNetCheck == "true" || noNetCheck == "t" || noNetCheck == "yes" || noNetCheck == "y" || noNetCheck == "1")) { newState = NetworkState::ConnectedGlobal; @@ -76,7 +73,7 @@ namespace Nickvision::Aura::Network } } } -#else +#elif defined(__linux__) GNetworkMonitor* netMon{ g_network_monitor_get_default() }; GNetworkConnectivity connection{ g_network_monitor_get_connectivity(netMon) }; switch (connection) diff --git a/src/systemdirectories.cpp b/src/systemdirectories.cpp index f297e62..0e42a13 100644 --- a/src/systemdirectories.cpp +++ b/src/systemdirectories.cpp @@ -1,18 +1,17 @@ #include "systemdirectories.h" -#include +#include "aura.h" #include "helpers/stringhelpers.h" namespace Nickvision::Aura { - std::vector SystemDirectories::getPath() { - char* var = std::getenv("PATH"); - if (var) + std::string var{ Aura::getEnvVar("PATH") }; + if (!var.empty()) { #ifdef _WIN32 return StringHelpers::split(var, ";"); -#else +#elif defined(__linux__) return StringHelpers::split(var, ":"); #endif } @@ -21,12 +20,12 @@ namespace Nickvision::Aura std::vector SystemDirectories::getConfig() { - char* var = std::getenv("XDG_CONFIG_DIRS"); - if (var) + std::string var{ Aura::getEnvVar("XDG_CONFIG_DIRS") }; + if (!var.empty()) { #ifdef _WIN32 return StringHelpers::split(var, ";"); -#else +#elif defined(__linux__) return StringHelpers::split(var, ":"); #endif } @@ -35,12 +34,12 @@ namespace Nickvision::Aura std::vector SystemDirectories::getData() { - char* var = std::getenv("XDG_DATA_DIRS"); - if (var) + std::string var{ Aura::getEnvVar("XDG_DATA_DIRS") }; + if (!var.empty()) { #ifdef _WIN32 return StringHelpers::split(var, ";"); -#else +#elif defined(__linux__) return StringHelpers::split(var, ":"); #endif } diff --git a/src/taskbar/taskbaritem.cpp b/src/taskbar/taskbaritem.cpp new file mode 100644 index 0000000..290896a --- /dev/null +++ b/src/taskbar/taskbaritem.cpp @@ -0,0 +1,239 @@ +#include "taskbar/taskbaritem.h" +#include +#ifdef _WIN32 +#include +#pragma comment(lib,"dwmapi.lib") +using namespace Gdiplus; +#elif defined(__linux__) +#include +#endif + +namespace Nickvision::Aura::Taskbar +{ + TaskbarItem::TaskbarItem() + : m_progressState{ ProgressState::NoProgress }, + m_progress{ 0.0 }, + m_urgent{ false }, + m_countVisible{ false }, + m_count{ 0 } + { +#ifdef _WIN32 + GdiplusStartupInput gdiStartupIn; + GdiplusStartupOutput gdiStartupOut; + CoInitialize(nullptr); + GdiplusStartup(&m_gdi, &gdiStartupIn, &gdiStartupOut); + m_hwnd = nullptr; + m_taskbar = nullptr; +#elif defined(__linux__) + m_connection = nullptr; + m_appUri = ""; +#endif + } + + TaskbarItem::~TaskbarItem() + { + setProgressState(ProgressState::NoProgress); + setUrgent(false); + setCountVisible(false); +#ifdef _WIN32 + GdiplusShutdown(m_gdi); +#endif + } + + ProgressState TaskbarItem::getProgressState() const + { + return m_progressState; + } + + void TaskbarItem::setProgressState(ProgressState state) + { + m_progressState = state; +#ifdef _WIN32 + if (m_taskbar) + { + m_taskbar->SetProgressState(m_hwnd, (TBPFLAG)m_progressState); + } +#elif defined(__linux__) + if (m_connection) + { + GDBusMessage* message{ g_dbus_message_new_signal("/", "com.canonical.Unity.LauncherEntry", "Update") }; + GVariant* params[2]{ g_variant_new_string(m_appUri.c_str()), g_variant_new_dict_entry(g_variant_new_string("progress-visible"), g_variant_new_boolean(m_progressState >= ProgressState::Normal)) }; + GVariant* tuple{ g_variant_new_tuple(params, 2) }; + g_dbus_message_set_body(message, tuple); + g_dbus_connection_send_message(m_connection.get(), message, G_DBUS_SEND_MESSAGE_FLAGS_NONE, nullptr, nullptr); + g_object_unref(G_OBJECT(message)); + g_object_unref(G_OBJECT(tuple)); + } +#endif + } + + double TaskbarItem::getProgress() const + { + return m_progress; + } + + void TaskbarItem::setProgress(double progress) + { + m_progress = progress; +#ifdef _WIN32 + if (m_taskbar) + { + m_taskbar->SetProgressValue(m_hwnd, static_cast(m_progress * 100), 100u); + } +#elif defined(__linux__) + if (m_connection) + { + GDBusMessage* message{ g_dbus_message_new_signal("/", "com.canonical.Unity.LauncherEntry", "Update") }; + GVariant* params[2]{ g_variant_new_string(m_appUri.c_str()), g_variant_new_dict_entry(g_variant_new_string("progress"), g_variant_new_double(m_progress)) }; + GVariant* tuple{ g_variant_new_tuple(params, 2) }; + g_dbus_message_set_body(message, tuple); + g_dbus_connection_send_message(m_connection.get(), message, G_DBUS_SEND_MESSAGE_FLAGS_NONE, nullptr, nullptr); + g_object_unref(G_OBJECT(message)); + g_object_unref(G_OBJECT(tuple)); + } +#endif + setProgressState(ProgressState::Normal); + } + + bool TaskbarItem::getUrgent() const + { + return m_urgent; + } + + void TaskbarItem::setUrgent(bool urgent) + { + m_urgent = urgent; +#ifdef _WIN32 + if (m_taskbar) + { + FLASHWINFO flashInfo; + flashInfo.cbSize = sizeof(FLASHWINFO); + flashInfo.hwnd = m_hwnd; + flashInfo.dwFlags = m_urgent ? (FLASHW_TRAY | FLASHW_TIMER) : FLASHW_STOP; + flashInfo.uCount = UINT_MAX; + flashInfo.dwTimeout = 0; + FlashWindowEx(&flashInfo); + } +#elif defined(__linux__) + if (m_connection) + { + GDBusMessage* message{ g_dbus_message_new_signal("/", "com.canonical.Unity.LauncherEntry", "Update") }; + GVariant* params[2]{ g_variant_new_string(m_appUri.c_str()), g_variant_new_dict_entry(g_variant_new_string("urgent"), g_variant_new_boolean(m_urgent)) }; + GVariant* tuple{ g_variant_new_tuple(params, 2) }; + g_dbus_message_set_body(message, tuple); + g_dbus_connection_send_message(m_connection.get(), message, G_DBUS_SEND_MESSAGE_FLAGS_NONE, nullptr, nullptr); + g_object_unref(G_OBJECT(message)); + g_object_unref(G_OBJECT(tuple)); + } +#endif + } + + bool TaskbarItem::getCountVisible() const + { + return m_countVisible; + } + + void TaskbarItem::setCountVisible(bool countVisible) + { + m_countVisible = countVisible; +#ifdef _WIN32 + if (m_taskbar) + { + if (!m_countVisible) + { + m_taskbar->SetOverlayIcon(m_hwnd, nullptr, L""); + } + else + { + DWORD accentColor; + BOOL opaue{ FALSE }; + Graphics windowGraphics{ m_hwnd }; + SolidBrush background{ DwmGetColorizationColor(&accentColor, &opaue) == S_OK ? Color::Color(accentColor) : Color::Color(0, 0, 0) }; + SolidBrush foreground{ Color::Color(255, 255, 255) }; + Bitmap bitmap{ 16, 16, &windowGraphics }; + Graphics graphics{ &bitmap }; + FontFamily fontFamily{ L"Microsoft Sans Serif" }; + Font font{ &fontFamily, m_count <= 99 ? (m_count < 10 ? 9.0f : 7.5f) : 7.0f }; + std::wstring countStr{ m_count > 99 ? L"99+" : std::to_wstring(m_count) }; + SizeF stringSize; + graphics.MeasureString(countStr.c_str(), (int)countStr.length(), &font, SizeF(16, 16), StringFormat::GenericDefault(), &stringSize); + graphics.FillEllipse(&background, Rect(0, 0, 16, 16)); + graphics.DrawString(countStr.c_str(), (int)countStr.length(), &font, PointF((16 - stringSize.Width) / 2, (16 - stringSize.Height) / 2), &foreground); + HICON icon{ nullptr }; + bitmap.GetHICON(&icon); + m_taskbar->SetOverlayIcon(m_hwnd, icon, std::to_wstring(m_count).c_str()); + DestroyIcon(icon); + } + } +#elif defined(__linux__) + if (m_connection) + { + GDBusMessage* message{ g_dbus_message_new_signal("/", "com.canonical.Unity.LauncherEntry", "Update") }; + GVariant* params[2]{ g_variant_new_string(m_appUri.c_str()), g_variant_new_dict_entry(g_variant_new_string("count-visible"), g_variant_new_boolean(m_countVisible)) }; + GVariant* tuple{ g_variant_new_tuple(params, 2) }; + g_dbus_message_set_body(message, tuple); + g_dbus_connection_send_message(m_connection.get(), message, G_DBUS_SEND_MESSAGE_FLAGS_NONE, nullptr, nullptr); + g_object_unref(G_OBJECT(message)); + g_object_unref(G_OBJECT(tuple)); + } +#endif + } + + long TaskbarItem::getCount() const + { + return m_count; + } + + void TaskbarItem::setCount(long count) + { + m_count = count; +#ifdef __linux__ + if (m_connection) + { + GDBusMessage* message{ g_dbus_message_new_signal("/", "com.canonical.Unity.LauncherEntry", "Update") }; + GVariant* params[2]{ g_variant_new_string(m_appUri.c_str()), g_variant_new_dict_entry(g_variant_new_string("count"), g_variant_new_int64(m_count)) }; + GVariant* tuple{ g_variant_new_tuple(params, 2) }; + g_dbus_message_set_body(message, tuple); + g_dbus_connection_send_message(m_connection.get(), message, G_DBUS_SEND_MESSAGE_FLAGS_NONE, nullptr, nullptr); + g_object_unref(G_OBJECT(message)); + g_object_unref(G_OBJECT(tuple)); + } +#endif + setCountVisible(count >= 0); + } + +#ifdef _WIN32 + bool TaskbarItem::connect(HWND hwnd) + { + if (!hwnd) + { + return false; + } + if (CoCreateInstance(CLSID_TaskbarList, nullptr, CLSCTX_ALL, __uuidof(ITaskbarList3), (LPVOID*)&m_taskbar) == S_OK) + { + m_hwnd = hwnd; + return m_taskbar->HrInit() == S_OK; + } + return false; + } +#elif defined(__linux__) + bool TaskbarItem::connect(const std::string& desktopFile) + { + if (desktopFile.empty()) + { + return false; + } + m_connection = { g_bus_get_sync(G_BUS_TYPE_SESSION, nullptr, nullptr), [](GDBusConnection* connection) + { + g_dbus_connection_close_sync(connection, nullptr, nullptr); + g_object_unref(G_OBJECT(connection)); + }}; + if (m_connection) + { + m_appUri = "application://" + desktopFile; + return true; + } + return false; + } +#endif +} \ No newline at end of file diff --git a/src/update/updater.cpp b/src/update/updater.cpp index 88b5084..e325563 100644 --- a/src/update/updater.cpp +++ b/src/update/updater.cpp @@ -48,10 +48,10 @@ namespace Nickvision::Aura::Update return fetchCurrentVersion(VersionType::Preview); } +#ifdef _WIN32 bool Updater::windowsUpdate(VersionType versionType) { std::lock_guard lock{ m_mutex }; -#ifdef _WIN32 if (versionType == VersionType::Stable ? m_latestStableReleaseId == -1 : m_latestPreviewReleaseId == -1) { return false; @@ -63,7 +63,7 @@ namespace Nickvision::Aura::Update Json::Reader reader; if (reader.parse(release, root, false)) { - for (const Json::Value& asset : root["assets"]) + for (const Json::Value& asset : root.get("assets", {})) { std::string name{ asset.get("name", "").asString() }; if (StringHelpers::toLower(name).find("setup.exe") != std::string::npos) @@ -72,17 +72,20 @@ namespace Nickvision::Aura::Update if (WebHelpers::downloadFile(asset.get("browser_download_url", "").asString(), setup)) { std::string cmd{ "\"" + setup.string() + "\"" }; - ShellExecute(nullptr, "open", cmd.c_str(), nullptr, nullptr, SW_SHOWDEFAULT); - exit(0); - return true; + if ((INT_PTR)ShellExecuteA(nullptr, "open", cmd.c_str(), nullptr, nullptr, SW_SHOWDEFAULT) > 32) + { + std::exit(0); + return true; + } + return false; } } } } } -#endif return false; } +#endif Version Updater::fetchCurrentVersion(VersionType versionType) { @@ -96,7 +99,11 @@ namespace Nickvision::Aura::Update { for (const Json::Value& release : root) { - std::string version{ release.get("tag_name", "").asString() }; + std::string version{ release.get("tag_name", "NULL").asString() }; + if (version == "NULL") + { + return {}; + } if (versionType == VersionType::Stable && version.find('-') == std::string::npos) { m_latestStableReleaseId = release.get("id", -1).asInt(); diff --git a/src/userdirectories.cpp b/src/userdirectories.cpp index 629cdaf..44d207c 100644 --- a/src/userdirectories.cpp +++ b/src/userdirectories.cpp @@ -1,12 +1,12 @@ #include "userdirectories.h" -#include #include #include #include "aura.h" #include "helpers/stringhelpers.h" #ifdef _WIN32 #include -#else +#elif defined(__linux__) +#include #include #include #include @@ -16,8 +16,8 @@ namespace Nickvision::Aura { std::filesystem::path getXDGDir(const std::string& key) { - char* var = getenv(key.c_str()); - if (var) + std::string var{ Aura::getEnvVar(key) }; + if (!var.empty()) { return var; } @@ -57,9 +57,9 @@ namespace Nickvision::Aura result = p; } CoTaskMemFree(static_cast(p)); -#else - char* var = getenv("HOME"); - result = var ? var : getpwuid(getuid())->pw_dir; +#elif defined(__linux__) + std::filesystem::path var{ Aura::getEnvVar("HOME") }; + result = !var.empty() ? var : getpwuid(getuid())->pw_dir; #endif return result; } @@ -74,9 +74,9 @@ namespace Nickvision::Aura result = p; } CoTaskMemFree(static_cast(p)); -#else - result = getXDGDir("XDG_CONFIG_HOME"); - result = result.empty() ? (getHome() / ".config") : result; +#elif defined(__linux__) + std::filesystem::path var{ Aura::getEnvVar("XDG_CONFIG_HOME") }; + result = !var.empty() ? var : (getHome() / ".config"); #endif std::filesystem::create_directories(result); return result; @@ -99,9 +99,9 @@ namespace Nickvision::Aura result = p; } CoTaskMemFree(static_cast(p)); -#else - result = getXDGDir("XDG_CACHE_HOME"); - result = result.empty() ? (getHome() / ".cache") : result; +#elif defined(__linux__) + std::filesystem::path var{ Aura::getEnvVar("XDG_CACHE_HOME") }; + result = !var.empty() ? var : (getHome() / ".cache"); #endif std::filesystem::create_directories(result); return result; @@ -119,9 +119,9 @@ namespace Nickvision::Aura std::filesystem::path result; #ifdef _WIN32 result = getConfig(); -#else - result = getXDGDir("XDG_DATA_HOME"); - result = result.empty() ? (getHome() / ".local/share") : result; +#elif defined(__linux__) + std::filesystem::path var{ Aura::getEnvVar("XDG_DATA_HOME") }; + result = !var.empty() ? var : (getHome() / ".local/share"); #endif std::filesystem::create_directories(result); return result; @@ -137,9 +137,9 @@ namespace Nickvision::Aura std::filesystem::path UserDirectories::getRuntime() { std::filesystem::path result; -#ifndef _WIN32 - result = getXDGDir("XDG_RUNTIME_DIR"); - result = result.empty() ? (std::filesystem::path("/run/user/") / std::filesystem::path(getenv("UID"))) : result; +#ifdef __linux__ + std::filesystem::path var{ Aura::getEnvVar("XDG_RUNTIME_DIR") }; + result = !var.empty() ? var : (std::filesystem::path("/run/user/") / Aura::getEnvVar("UID")); #endif return result; } @@ -154,7 +154,7 @@ namespace Nickvision::Aura result = p; } CoTaskMemFree(static_cast(p)); -#else +#elif defined(__linux__) result = getXDGDir("XDG_DESKTOP_DIR"); result = result.empty() ? (getHome() / "Desktop") : result; #endif @@ -172,7 +172,7 @@ namespace Nickvision::Aura result = p; } CoTaskMemFree(static_cast(p)); -#else +#elif defined(__linux__) result = getXDGDir("XDG_DOCUMENTS_DIR"); result = result.empty() ? (getHome() / "Documents") : result; #endif @@ -190,7 +190,7 @@ namespace Nickvision::Aura result = p; } CoTaskMemFree(static_cast(p)); -#else +#elif defined(__linux__) result = getXDGDir("XDG_DOWNLOAD_DIR"); result = result.empty() ? (getHome() / "Downloads") : result; #endif @@ -208,7 +208,7 @@ namespace Nickvision::Aura result = p; } CoTaskMemFree(static_cast(p)); -#else +#elif defined(__linux__) result = getXDGDir("XDG_MUSIC_DIR"); result = result.empty() ? (getHome() / "Music") : result; #endif @@ -226,7 +226,7 @@ namespace Nickvision::Aura result = p; } CoTaskMemFree(static_cast(p)); -#else +#elif defined(__linux__) result = getXDGDir("XDG_PICTURES_DIR"); result = result.empty() ? (getHome() / "Pictures") : result; #endif @@ -237,7 +237,7 @@ namespace Nickvision::Aura std::filesystem::path UserDirectories::getPublicShare() { std::filesystem::path result; -#ifndef _WIN32 +#ifdef __linux__ result = getXDGDir("XDG_PUBLICSHARE_DIR"); #endif return result; @@ -253,7 +253,7 @@ namespace Nickvision::Aura result = p; } CoTaskMemFree(static_cast(p)); -#else +#elif defined(__linux__) result = getXDGDir("XDG_TEMPLATES_DIR"); result = result.empty() ? (getHome() / "Templates") : result; #endif @@ -271,7 +271,7 @@ namespace Nickvision::Aura result = p; } CoTaskMemFree(static_cast(p)); -#else +#elif defined(__linux__) result = getXDGDir("XDG_VIDEOS_DIR"); result = result.empty() ? (getHome() / "Videos") : result; #endif diff --git a/tests/auratests.cpp b/tests/auratests.cpp index 68ae117..b88cb18 100644 --- a/tests/auratests.cpp +++ b/tests/auratests.cpp @@ -77,7 +77,7 @@ TEST_F(AuraTest, DependencyCheckCmd) } #endif -#ifndef _WIN32 +#ifdef __linux__ TEST_F(AuraTest, DependencyCheckLs) { ASSERT_FALSE(DependencyLocator::find("ls").empty()); diff --git a/tests/filewatchertests.cpp b/tests/filewatchertests.cpp new file mode 100644 index 0000000..1ff3838 --- /dev/null +++ b/tests/filewatchertests.cpp @@ -0,0 +1,68 @@ +#include +#include +#include "aura.h" +#include "filesystem/filesystemwatcher.h" + +using namespace Nickvision::Aura; +using namespace Nickvision::Aura::Filesystem; + +class FileWatcherTest : public testing::Test +{ +public: + static std::unique_ptr m_watcher; + static int m_modifications; + + static void SetUpTestSuite() + { + m_watcher = std::make_unique(std::filesystem::current_path(), false); + m_watcher->addExtensionFilter(".txt"); + m_watcher->changed() += onChanged; + } + +private: + static void onChanged(const FileSystemChangedEventArgs& e) + { + m_modifications++; + } +}; + +std::unique_ptr FileWatcherTest::m_watcher = nullptr; +int FileWatcherTest::m_modifications = 0; + +static std::filesystem::path a{ "a.txt" }; +static std::filesystem::path b{ "b.md" }; +static std::filesystem::path c{ "c.txt" }; + +TEST_F(FileWatcherTest, AddFileA) +{ + std::ofstream out{ a }; + ASSERT_NO_THROW(out.close()); + std::this_thread::sleep_for(std::chrono::seconds(1)); +} + +TEST_F(FileWatcherTest, AddFileB) +{ + std::ofstream out{ b }; + ASSERT_NO_THROW(out.close()); + std::this_thread::sleep_for(std::chrono::seconds(1)); +} + +TEST_F(FileWatcherTest, AddFileC) +{ + std::ofstream out{ c }; + ASSERT_NO_THROW(out.close()); + std::this_thread::sleep_for(std::chrono::seconds(1)); +} + +TEST_F(FileWatcherTest, CheckResults) +{ + ASSERT_TRUE(Aura::getEnvVar("GITHUB_ACTIONS") == "true" || m_modifications >= 1); +} + +TEST_F(FileWatcherTest, Cleanup) +{ + ASSERT_NO_THROW(m_watcher.reset()); + ASSERT_TRUE(std::filesystem::remove(a)); + ASSERT_TRUE(std::filesystem::remove(b)); + ASSERT_TRUE(std::filesystem::remove(c)); +} \ No newline at end of file diff --git a/tests/keyringtests.cpp b/tests/keyringtests.cpp index 593cdb4..8ddfd52 100644 --- a/tests/keyringtests.cpp +++ b/tests/keyringtests.cpp @@ -7,16 +7,16 @@ using namespace Nickvision::Aura::Keyring; class KeyringTest : public testing::Test { public: - static std::shared_ptr m_controller; + static std::unique_ptr m_controller; static void SetUpTestSuite() { Keyring::destroy("org.nickvision.aura.test.keyring"); - m_controller = std::make_shared("org.nickvision.aura.test.keyring", Keyring::access("org.nickvision.aura.test.keyring")); + m_controller = std::make_unique("org.nickvision.aura.test.keyring", Keyring::access("org.nickvision.aura.test.keyring")); } }; -std::shared_ptr KeyringTest::m_controller = nullptr; +std::unique_ptr KeyringTest::m_controller = nullptr; TEST_F(KeyringTest, CheckValidKeyring) { diff --git a/tests/networktests.cpp b/tests/networktests.cpp index 682d99c..9ca2475 100644 --- a/tests/networktests.cpp +++ b/tests/networktests.cpp @@ -1,6 +1,6 @@ #include #include "network/networkmonitor.h" -#ifndef _WIN32 +#ifdef __linux__ #include #endif @@ -20,7 +20,7 @@ TEST(NetworkTests, DisableNetCheck) { #ifdef _WIN32 ASSERT_EQ(_putenv("AURA_DISABLE_NETCHECK=true"), 0); -#else +#elif defined(__linux__) ASSERT_EQ(setenv("AURA_DISABLE_NETCHECK", "true", true), 0); #endif NetworkMonitor netmon; diff --git a/tests/storetests.cpp b/tests/storetests.cpp index b73d69b..c48641d 100644 --- a/tests/storetests.cpp +++ b/tests/storetests.cpp @@ -7,17 +7,17 @@ using namespace Nickvision::Aura::Keyring; class StoreTest : public testing::Test { public: - static std::shared_ptr m_store; + static std::unique_ptr m_store; static void SetUpTestSuite() { Store::destroy("org.nickvision.aura.test"); PasswordGenerator passGen; - m_store = std::make_shared("org.nickvision.aura.test", passGen.next()); + m_store = std::make_unique("org.nickvision.aura.test", passGen.next()); } }; -std::shared_ptr StoreTest::m_store = nullptr; +std::unique_ptr StoreTest::m_store = nullptr; TEST_F(StoreTest, CheckValidStore) { diff --git a/tests/taskbartests.cpp b/tests/taskbartests.cpp new file mode 100644 index 0000000..1bbe98a --- /dev/null +++ b/tests/taskbartests.cpp @@ -0,0 +1,50 @@ +#include +#include "taskbar/taskbaritem.h" + +using namespace Nickvision::Aura::Taskbar; + +class TaskbarTest : public testing::Test +{ +public: + static std::unique_ptr m_taskbar; + + static void SetUpTestSuite() + { + m_taskbar = std::make_unique(); + } +}; + +std::unique_ptr TaskbarTest::m_taskbar = nullptr; + +#ifdef _WIN32 +TEST_F(TaskbarTest, ConnectWindows) +{ + HWND hwnd{ GetConsoleWindow() }; + if (hwnd) + { + ASSERT_TRUE(m_taskbar->connect(hwnd)); + } +} +#endif + +TEST_F(TaskbarTest, SetProgress) +{ + ASSERT_NO_THROW(m_taskbar->setProgress(0.35)); + ASSERT_TRUE(m_taskbar->getProgressState() == ProgressState::Normal); +} + +TEST_F(TaskbarTest, SetProgressPaused) +{ + ASSERT_NO_THROW(m_taskbar->setProgressState(ProgressState::Paused)); +} + +TEST_F(TaskbarTest, SetUrgent) +{ + ASSERT_NO_THROW(m_taskbar->setUrgent(true)); +} + +TEST_F(TaskbarTest, SetCount) +{ + ASSERT_NO_THROW(m_taskbar->setCount(5)); + ASSERT_TRUE(m_taskbar->getCountVisible()); +} \ No newline at end of file diff --git a/tests/webtests.cpp b/tests/webtests.cpp index 34017f0..a0f9ba9 100644 --- a/tests/webtests.cpp +++ b/tests/webtests.cpp @@ -1,4 +1,5 @@ #include +#include #include "helpers/webhelpers.h" using namespace Nickvision::Aura; @@ -16,6 +17,7 @@ TEST(WebTests, ValidWebsite2) TEST(WebTests, DownloadFile1) { ASSERT_TRUE(WebHelpers::downloadFile("https://raw.githubusercontent.com/nlogozzo/nlogozzo/main/README.md", "readme.md")); + ASSERT_TRUE(std::filesystem::remove("readme.md")); } TEST(WebTests, FetchJsonString1)