├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── feature_request.md │ └── other.md ├── .gitignore ├── CMakeLists.txt ├── Development └── Distributions │ ├── Arch_Linux │ ├── PKGBUILD │ └── README.md │ ├── Debian.12+Ubuntu.22.04- │ ├── README.md │ └── debian │ │ ├── changelog │ │ ├── control │ │ ├── patches │ │ ├── QT-5_Build.patch │ │ └── series │ │ └── rules │ ├── Debian.13+KDE_Neon+Ubuntu.24.10+ │ ├── README.md │ └── debian │ │ ├── changelog │ │ ├── control │ │ └── rules │ ├── Fedora+Mageia+SUSE │ └── koi.spec │ └── NixOS │ ├── README.md │ └── default.nix ├── LICENSE ├── README.md ├── Screenshot.svg ├── cmake_uninstall.cmake.in └── src ├── CMakeLists.txt ├── about.cpp ├── dbusinterface.cpp ├── headers ├── about.h ├── config.h.in ├── dbusinterface.h ├── license.h ├── mainwindow.h ├── plugin.h ├── runguard.h └── utils.h ├── koi.desktop ├── libraries ├── Cron.h ├── InterruptableSleep.h ├── Scheduler.h ├── SunRise.cpp ├── SunRise.h └── ctpl_stl.h ├── license.cpp ├── main.cpp ├── mainwindow.cpp ├── plugins ├── colorscheme.cpp ├── colorscheme.h ├── gtk.cpp ├── gtk.h ├── icons.cpp ├── icons.h ├── kvantumstyle.cpp ├── kvantumstyle.h ├── plasmastyle.cpp ├── plasmastyle.h ├── script.cpp ├── script.h ├── wallpaper.cpp └── wallpaper.h ├── resources ├── dbus │ └── services │ │ └── dev.baduhai.Koi.service ├── icons │ ├── koi.png │ ├── koi.svg │ ├── koi_tray.png │ └── koi_tray.svg ├── license.txt ├── resources.qrc └── sunrise-library.properties ├── runguard.cpp ├── ui ├── about.ui ├── license.ui └── mainwindow.ui └── utils.cpp /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "[BUG]" 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - Distro: [e.g. KDE Neon] 28 | - Koi Version: [e.g. 0.1] 29 | - Plasma Version: [e.g. 5.25.5] 30 | - Intallation method: [e.g. AppImage/nix/aur/copr/OBS/compiled from source] 31 | 32 | **Additional context** 33 | Add any other context about the problem here. 34 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "[REQUEST]" 5 | labels: feature request 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/other.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Other 3 | about: Open an issue about something other than a bug or feature request 4 | title: "[OTHER]" 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | src/build 3 | *.user 4 | *.autosave 5 | .vscode 6 | .clangd 7 | .cache/clangd/* -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | project(Koi VERSION 0.5.1 LANGUAGES CXX) 3 | 4 | configure_file(src/headers/config.h.in config.h) 5 | include_directories(${CMAKE_CURRENT_BINARY_DIR}) 6 | 7 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 8 | 9 | set(CMAKE_AUTOMOC ON) 10 | set(CMAKE_AUTORCC ON) 11 | set(CMAKE_AUTOUIC ON) 12 | 13 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) 14 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) 15 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) 16 | 17 | # Define Installation Data-Root Directory 18 | set(CMAKE_INSTALL_DATAROOTDIR share) 19 | # Define *SYSTEM-WIDE* DBUS_INTERFACES_INSTALL_DIR variable 20 | set(DBUS_INTERFACES_INSTALL_DIR share/dbus-1/interfaces) 21 | # Define *SYSTEM-WIDE* DBUS_SERVICES_INSTALL_DIR variable 22 | set(DBUS_SERVICES_INSTALL_DIR share/dbus-1/services) 23 | 24 | # Configure DBus Service File 25 | configure_file(src/resources/dbus/services/dev.baduhai.Koi.service ${CMAKE_CURRENT_BINARY_DIR}/dev.baduhai.Koi.service) 26 | 27 | # Install files not being a part of INSTALL TARGETS section 28 | install(FILES ${CMAKE_CURRENT_BINARY_DIR}/dev.baduhai.Koi.xml DESTINATION ${DBUS_INTERFACES_INSTALL_DIR}) 29 | install(FILES src/koi.desktop DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/applications 30 | RENAME local.KoiDbusInterface.desktop) 31 | install(FILES src/resources/dbus/services/dev.baduhai.Koi.service DESTINATION ${DBUS_SERVICES_INSTALL_DIR}) 32 | install(FILES src/resources/icons/koi.svg DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/scalable/apps) 33 | install(FILES src/resources/icons/koi_tray.svg DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/scalable/apps) 34 | 35 | # Look for DEPENDENCIES 36 | set(THREADS_PREFER_PTHREAD_FLAG ON) 37 | find_package(Threads REQUIRED) 38 | 39 | find_package(KF6Config) 40 | find_package(KF6CoreAddons) 41 | find_package(KF6WidgetsAddons) 42 | find_package(QT NAMES Qt6 REQUIRED COMPONENTS Core) 43 | find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS DBus Gui) 44 | find_package(Qt${QT_VERSION_MAJOR} OPTIONAL_COMPONENTS Widgets) 45 | find_package(Qt${QT_VERSION_MAJOR} OPTIONAL_COMPONENTS Xml) 46 | 47 | # Set SOURCES into a single BINARY 48 | set(koi_SRC 49 | # Main app stuff. 50 | src/about.cpp 51 | src/license.cpp 52 | src/mainwindow.cpp 53 | src/utils.cpp 54 | src/runguard.cpp 55 | # QObject headers needs to be added as sources to properly run MOC and UIC. 56 | src/headers/about.h 57 | src/headers/license.h 58 | src/headers/mainwindow.h 59 | src/headers/utils.h 60 | src/headers/runguard.h 61 | # DBus interface stuff 62 | src/dbusinterface.cpp 63 | src/headers/dbusinterface.h 64 | # DBus interface XML file -- generated at build time 65 | ${CMAKE_CURRENT_BINARY_DIR}/dev.baduhai.Koi.xml 66 | # DBus interface adapter -- generated at build time from the XML file 67 | ${CMAKE_CURRENT_BINARY_DIR}/koiadaptor.h 68 | # UI items 69 | src/resources/resources.qrc 70 | src/ui/about.ui 71 | src/ui/license.ui 72 | src/ui/mainwindow.ui 73 | # Libraries for additional stuff 74 | src/libraries/SunRise.cpp 75 | # All plugins 76 | src/plugins/colorscheme.cpp 77 | src/plugins/gtk.cpp 78 | src/plugins/icons.cpp 79 | src/plugins/kvantumstyle.cpp 80 | src/plugins/plasmastyle.cpp 81 | src/plugins/wallpaper.cpp 82 | src/plugins/script.cpp 83 | # Main entrypoint 84 | src/main.cpp 85 | ) 86 | 87 | qt_generate_dbus_interface(src/headers/dbusinterface.h 88 | dev.baduhai.Koi.xml 89 | OPTIONS -A 90 | ) 91 | 92 | qt_add_dbus_adaptor(koi_SRC ${CMAKE_CURRENT_BINARY_DIR}/dev.baduhai.Koi.xml 93 | src/headers/dbusinterface.h KoiDbusInterface) 94 | 95 | 96 | add_executable(koi 97 | ${koi_SRC} 98 | ) 99 | 100 | target_compile_definitions(koi PUBLIC 101 | QT_DEPRECATED_WARNINGS 102 | ) 103 | 104 | # Link SYSTEM libraries with the Koi project 105 | target_link_libraries(koi PUBLIC 106 | KF6::ConfigCore 107 | KF6::ConfigGui 108 | KF6::CoreAddons 109 | KF6::WidgetsAddons 110 | Qt::Core 111 | Qt::DBus 112 | Qt::Gui 113 | Qt::Widgets 114 | Qt::Xml 115 | Threads::Threads 116 | ) 117 | 118 | # Install target 119 | install(TARGETS koi 120 | RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" 121 | BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" 122 | LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" 123 | ) 124 | 125 | # Uninstall target 126 | configure_file( 127 | "${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in" 128 | "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" 129 | @ONLY 130 | ) 131 | add_custom_target(uninstall 132 | COMMAND "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" 133 | ) 134 | -------------------------------------------------------------------------------- /Development/Distributions/Arch_Linux/PKGBUILD: -------------------------------------------------------------------------------- 1 | # Author: William Franco Abdul Hai 2 | # Contributor: Martin Stibor 3 | # Maintainer: Martin Stibor 4 | 5 | pkgname='koi' 6 | _pkgname='Koi' 7 | pkgver=0.5.1 8 | pkgrel=1 9 | pkgdesc="Scheduled LIGHT/DARK Theme Switching for the KDE Plasma Desktop" 10 | arch=('x86_64' 'aarch64') 11 | url="https://github.com/baduhai/Koi" 12 | license=('LGPL3') 13 | depends=('gcc-libs' 'plasma-desktop' 'plasma-integration' 'plasma-workspace' 'qt6-svg' 'hicolor-icon-theme') 14 | makedepends=('gcc' 'qt6-base' 'qt6-tools' 'cmake' 'extra-cmake-modules' 'desktop-file-utils' 'fdupes') 15 | optdepends=('xsettingsd: Apply settings to GTK applications on the fly' 16 | 'kvantum: Powerful extra customisable themes') 17 | source=("${pkgname}-${pkgver}.tar.gz::${url}/archive/refs/tags/${pkgver}.tar.gz") 18 | md5sums=('SKIP') 19 | 20 | build() { 21 | cmake -DCMAKE_BUILD_TYPE=Release \ 22 | -DCMAKE_INSTALL_PREFIX="${pkgdir}/usr/" \ 23 | -S "${srcdir}/${_pkgname}-${pkgver}/" \ 24 | -B "${srcdir}/${_pkgname}-${pkgver}/build/" 25 | 26 | cmake --build "${srcdir}/${_pkgname}-${pkgver}/build/" --parallel 27 | } 28 | 29 | package() { 30 | cmake --install "${srcdir}/${_pkgname}-${pkgver}/build/" 31 | 32 | # Check the Application .DESKTOP file & Look for Duplicates within `pkgdir` ... 33 | desktop-file-validate "${pkgdir}/usr/share/applications/local.${_pkgname}DbusInterface.desktop" 34 | fdupes -r -s "${pkgdir}/" 35 | } 36 | -------------------------------------------------------------------------------- /Development/Distributions/Arch_Linux/README.md: -------------------------------------------------------------------------------- 1 | ***These are build instructions for Arch Linux and other Arch derived operating systems using KDE Plasma 6 . . .*** 2 | -------------------------------------------------------------------------------- /Development/Distributions/Debian.12+Ubuntu.22.04-/README.md: -------------------------------------------------------------------------------- 1 | ***These are build instructions for Debian 12 / Ubuntu 24.04 and lower versions of the operating systems using KDE Plasma 5 . . .*** 2 | -------------------------------------------------------------------------------- /Development/Distributions/Debian.12+Ubuntu.22.04-/debian/changelog: -------------------------------------------------------------------------------- 1 | koi (0.5) bookworm; urgency=medium 2 | 3 | * Update to the latest upstream version - 0.5 4 | 5 | -- Martin von Reichenberg Sat, 07 Apr 2025 03:10:36 6 | -------------------------------------------------------------------------------- /Development/Distributions/Debian.12+Ubuntu.22.04-/debian/control: -------------------------------------------------------------------------------- 1 | Source: koi 2 | Section: kde 3 | Priority: optional 4 | Maintainer: Martin Stibor 5 | Build-Depends: debhelper (>= 9), g++, cmake, cmake-extras, extra-cmake-modules, libkf5config-dev, libkf5coreaddons-dev, libkf5dbusaddons-dev, libkf5widgetsaddons-dev, qtbase5-dev 6 | Standards-Version: 0.5.1 7 | Vcs-Git: https://github.com/baduhai/Koi.git 8 | Homepage: https://github.com/baduhai/Koi/ 9 | 10 | Package: koi 11 | Architecture: any 12 | Depends: \${shlibs:Depends}, \${misc:Depends}, libc6, libgcc-s1, libkf5coreaddons5, libkf5widgetsaddons5, libqt5core5a, libqt5dbus5, libqt5gui5, libqt5widgets5, libstdc++6, libkf5configcore5, libkf5config-data, libkf5configgui5, libkf5guiaddons5, libkf5guiaddons-data, libkf5coreaddons-data, libkf5dbusaddons5, libkf5dbusaddons-data, libqt5test5, libkf5widgetsaddons-data, libqt5xml5, libqt5xmlpatterns5 13 | Description: Scheduled Light/Dark Theme Switcher for the KDE Plasma Desktop. 14 | Koi is a program designed to provide the KDE Plasma Desktop functionality 15 | to automatically switch between light and dark themes. Koi is under active development, 16 | and while it is stable enough to use daily, expect bugs. 17 | Koi is designed to be used with Plasma, 18 | and while some features may function under different desktop environments, 19 | they are unlikely to work and untested. 20 | -------------------------------------------------------------------------------- /Development/Distributions/Debian.12+Ubuntu.22.04-/debian/patches/QT-5_Build.patch: -------------------------------------------------------------------------------- 1 | --- a/CMakeLists.txt 2025-04-25 13:21:37.000000000 +0200 2 | +++ b/CMakeLists.txt 2025-04-25 19:42:24.382427433 +0200 3 | @@ -36,10 +36,10 @@ 4 | set(THREADS_PREFER_PTHREAD_FLAG ON) 5 | find_package(Threads REQUIRED) 6 | 7 | -find_package(KF6Config) 8 | -find_package(KF6CoreAddons) 9 | -find_package(KF6WidgetsAddons) 10 | -find_package(QT NAMES Qt6 REQUIRED COMPONENTS Core) 11 | +find_package(KF5Config) 12 | +find_package(KF5CoreAddons) 13 | +find_package(KF5WidgetsAddons) 14 | +find_package(QT NAMES Qt5 REQUIRED COMPONENTS Core) 15 | find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS DBus Gui) 16 | find_package(Qt${QT_VERSION_MAJOR} OPTIONAL_COMPONENTS Widgets) 17 | find_package(Qt${QT_VERSION_MAJOR} OPTIONAL_COMPONENTS Xml) 18 | @@ -103,10 +103,10 @@ 19 | 20 | # Link SYSTEM libraries with the Koi project 21 | target_link_libraries(koi PUBLIC 22 | - KF6::ConfigCore 23 | - KF6::ConfigGui 24 | - KF6::CoreAddons 25 | - KF6::WidgetsAddons 26 | + KF5::ConfigCore 27 | + KF5::ConfigGui 28 | + KF5::CoreAddons 29 | + KF5::WidgetsAddons 30 | Qt::Core 31 | Qt::DBus 32 | Qt::Gui 33 | 34 | --- a/src/utils.cpp 2025-04-25 12:02:54.000000000 +0200 35 | +++ b/src/utils.cpp 2025-04-25 19:42:22.094127893 +0200 36 | @@ -240,10 +240,10 @@ 37 | 38 | // restart plasmashell 39 | QStringList plasmashell = {"plasmashell"}; 40 | - QProcess::execute("/usr/bin/kquitapp6", plasmashell); 41 | + QProcess::execute("/usr/bin/kquitapp5", plasmashell); 42 | QProcess::startDetached("/usr/bin/kstart", plasmashell); 43 | } 44 | 45 | // stop krunner, it will be restarted when it is requested again 46 | - QProcess::startDetached("/usr/bin/kquitapp6", {"krunner"}); 47 | + QProcess::startDetached("/usr/bin/kquitapp5", {"krunner"}); 48 | } 49 | -------------------------------------------------------------------------------- /Development/Distributions/Debian.12+Ubuntu.22.04-/debian/patches/series: -------------------------------------------------------------------------------- 1 | QT-5_Build.patch 2 | -------------------------------------------------------------------------------- /Development/Distributions/Debian.12+Ubuntu.22.04-/debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | %: 3 | dh \$@ 4 | 5 | override_dh_auto_configure: 6 | dh_auto_configure --builddirectory=../build 7 | 8 | override_dh_auto_build: 9 | dh_auto_build --builddirectory=../build 10 | 11 | override_dh_auto_install: 12 | dh_auto_install --builddirectory=../build 13 | make install DESTDIR=\$(CURDIR)/debian/koi 14 | -------------------------------------------------------------------------------- /Development/Distributions/Debian.13+KDE_Neon+Ubuntu.24.10+/README.md: -------------------------------------------------------------------------------- 1 | ***These are build instructions for Debian 12 / KDE Neon / Ubuntu 24.04 and higher versions of the operating systems using KDE Plasma 6 . . .*** 2 | -------------------------------------------------------------------------------- /Development/Distributions/Debian.13+KDE_Neon+Ubuntu.24.10+/debian/changelog: -------------------------------------------------------------------------------- 1 | koi (0.5) trixie; urgency=medium 2 | 3 | * Update to the latest upstream version - 0.5 4 | 5 | -- Martin von Reichenberg Sat, 07 Apr 2025 03:10:36 6 | -------------------------------------------------------------------------------- /Development/Distributions/Debian.13+KDE_Neon+Ubuntu.24.10+/debian/control: -------------------------------------------------------------------------------- 1 | Source: koi 2 | Section: kde 3 | Priority: optional 4 | Maintainer: Martin Stibor 5 | Build-Depends: debhelper (>= 9), g++, cmake, cmake-extras, extra-cmake-modules, libkf6config-dev, libkf6coreaddons-dev, libkf6dbusaddons-dev, libkf6widgetsaddons-dev, qt6-base-dev 6 | Standards-Version: 0.5.1 7 | Vcs-Git: https://github.com/baduhai/Koi.git 8 | Homepage: https://github.com/baduhai/Koi/ 9 | 10 | Package: koi 11 | Architecture: any 12 | Depends: \${shlibs:Depends}, \${misc:Depends}, libc6, libgcc-s1, libkf6coreaddons6, libkf6widgetsaddons6, libqt6core6a, libqt6dbus6, libqt6gui6, libqt6widgets6, libstdc++6, libkf6configcore6, libkf6config-data, libkf6configgui6, libkf6guiaddons6, libkf6guiaddons-data, libkf6coreaddons-data, libkf6dbusaddons6, libkf6dbusaddons-data, libqt6test6, libkf6widgetsaddons-data, libqt6xml6, libqt6xmlpatterns6 13 | Description: Scheduled Light/Dark Theme Switcher for the KDE Plasma Desktop. 14 | Koi is a program designed to provide the KDE Plasma Desktop functionality 15 | to automatically switch between light and dark themes. Koi is under active development, 16 | and while it is stable enough to use daily, expect bugs. 17 | Koi is designed to be used with Plasma, 18 | and while some features may function under different desktop environments, 19 | they are unlikely to work and untested. 20 | -------------------------------------------------------------------------------- /Development/Distributions/Debian.13+KDE_Neon+Ubuntu.24.10+/debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | %: 3 | dh \$@ 4 | 5 | override_dh_auto_configure: 6 | dh_auto_configure --builddirectory=../build 7 | 8 | override_dh_auto_build: 9 | dh_auto_build --builddirectory=../build 10 | 11 | override_dh_auto_install: 12 | dh_auto_install --builddirectory=../build 13 | make install DESTDIR=\$(CURDIR)/debian/koi 14 | -------------------------------------------------------------------------------- /Development/Distributions/Fedora+Mageia+SUSE/koi.spec: -------------------------------------------------------------------------------- 1 | # 2 | # spec file for package koi 3 | # 4 | # Copyright (c) 2025 SUSE LLC 5 | # 6 | # All modifications and additions to the file contributed by third parties 7 | # remain the property of their copyright owners, unless otherwise agreed 8 | # upon. The license for this file, and modifications and additions to the 9 | # file, is the same license as for the pristine package itself (unless the 10 | # license for the pristine package is not an Open Source License, in which 11 | # case the license is the MIT License). An "Open Source License" is a 12 | # license that conforms to the Open Source Definition (Version 1.9) 13 | # published by the Open Source Initiative. 14 | 15 | # Please submit bugfixes or comments via https://bugs.opensuse.org/ 16 | # 17 | 18 | 19 | %define original_name Koi 20 | 21 | 22 | Name: koi 23 | Version: 0.5.1 24 | Release: 0%{?dist} 25 | Summary: Scheduled LIGHT/DARK Theme Switching for the KDE Plasma Desktop 26 | License: LGPL-3.0-only 27 | URL: https://github.com/baduhai/%{original_name} 28 | Source0: %{url}/archive/%{version}.tar.gz#/%{name}-%{version}.tar.gz 29 | 30 | %if 0%{?suse_version} >= 1600 && 0%{?is_opensuse} || 0%{?fedora} >= 40 || 0%{?mageia} >= 10 31 | BuildRequires: cmake 32 | BuildRequires: (cmake-full or cmake-fedora or cmake-extras or extra-cmake-modules) 33 | BuildRequires: cmake(KF6Config) 34 | BuildRequires: cmake(KF6ConfigWidgets) 35 | BuildRequires: cmake(KF6CoreAddons) 36 | BuildRequires: cmake(KF6WidgetsAddons) 37 | 38 | BuildRequires: cmake(Qt6Core) 39 | BuildRequires: cmake(Qt6DBus) 40 | BuildRequires: cmake(Qt6Gui) 41 | BuildRequires: cmake(Qt6Widgets) 42 | BuildRequires: cmake(Qt6Xml) 43 | 44 | BuildRequires: (kf6-extra-cmake-modules or extra-cmake-modules) 45 | 46 | Requires: (plasma6-desktop or plasma-desktop) 47 | Requires: (plasma6-workspace or plasma-workspace) 48 | 49 | %if 0%{?sle_version} <= 150600 && 0%{?is_opensuse} || 0%{?suse_version} < 1600 && 0%{?is_opensuse} 50 | BuildRequires: cmake 51 | BuildRequires: (cmake-full or cmake-fedora or cmake-extras or extra-cmake-modules) 52 | BuildRequires: cmake(KF5Config) 53 | BuildRequires: cmake(KF5ConfigWidgets) 54 | BuildRequires: cmake(KF5CoreAddons) 55 | BuildRequires: cmake(KF5WidgetsAddons) 56 | 57 | BuildRequires: cmake(Qt5Core) 58 | BuildRequires: cmake(Qt5DBus) 59 | BuildRequires: cmake(Qt5Gui) 60 | BuildRequires: cmake(Qt5Widgets) 61 | BuildRequires: cmake(Qt5Xml) 62 | 63 | Requires: plasma5-desktop 64 | Requires: plasma-workspace 65 | Requires: plasma-framework 66 | 67 | %if 0%{?mageia} >= 10 68 | BuildRequires: cmake 69 | BuildRequires: cmake-rpm-macros 70 | BuildRequires: (cmake-full or cmake-fedora or cmake-extras or extra-cmake-modules) 71 | BuildRequires: cmake(KF6Config) 72 | BuildRequires: cmake(KF6ConfigWidgets) 73 | BuildRequires: cmake(KF6CoreAddons) 74 | BuildRequires: cmake(KF6WidgetsAddons) 75 | 76 | BuildRequires: cmake(Qt6Core) 77 | BuildRequires: cmake(Qt6DBus) 78 | BuildRequires: cmake(Qt6Gui) 79 | BuildRequires: cmake(Qt6Widgets) 80 | BuildRequires: cmake(Qt6Xml) 81 | 82 | BuildRequires: (lib64kf6config-devel or libkf6config-devel) 83 | BuildRequires: (lib64kf6configwidgets-devel or libkf6configwidgets-devel) 84 | BuildRequires: (lib64kf6coreaddons-devel or libkf6coreaddons-devel) 85 | BuildRequires: (lib64kf6widgetsaddons-devel or libkf6widgetsaddons-devel) 86 | 87 | BuildRequires: (lib64qt6core-devel or libqt6core-devel) 88 | BuildRequires: (lib64qt6dbus-devel or libqt6dbus-devel) 89 | BuildRequires: (lib64qt6gui-devel or libqt6gui-devel) 90 | BuildRequires: (lib64qt6widgets-devel or libqt6widgets-devel) 91 | BuildRequires: (lib64qt6xml-devel or libqt6xml-devel) 92 | 93 | Requires: plasma-desktop 94 | Requires: plasma6-framework 95 | Requires: plasma-integration 96 | Requires: plasma-workspace 97 | 98 | %if 0%{?mageia} <= 9 99 | BuildRequires: cmake 100 | BuildRequires: cmake-rpm-macros 101 | BuildRequires: (cmake-full or cmake-fedora or cmake-extras or extra-cmake-modules) 102 | BuildRequires: cmake(KF5Config) 103 | BuildRequires: cmake(KF5ConfigWidgets) 104 | BuildRequires: cmake(KF5CoreAddons) 105 | BuildRequires: cmake(KF5WidgetsAddons) 106 | 107 | BuildRequires: cmake(Qt5Core) 108 | BuildRequires: cmake(Qt5DBus) 109 | BuildRequires: cmake(Qt5Gui) 110 | BuildRequires: cmake(Qt5Widgets) 111 | BuildRequires: cmake(Qt5Xml) 112 | 113 | BuildRequires: (lib64kf5config-devel or libkf5config-devel) 114 | BuildRequires: (lib64kf5configwidgets-devel or libkf5configwidgets-devel) 115 | BuildRequires: (lib64kf5coreaddons-devel or libkf5coreaddons-devel) 116 | BuildRequires: (lib64kf5widgetsaddons-devel or libkf5widgetsaddons-devel) 117 | 118 | BuildRequires: (lib64qt5core-devel or libqt5core-devel) 119 | BuildRequires: (lib64qt5dbus-devel or libqt5dbus-devel) 120 | BuildRequires: (lib64qt5gui-devel or libqt5gui-devel) 121 | BuildRequires: (lib64qt5widgets-devel or libqt5widgets-devel) 122 | BuildRequires: (lib64qt5xml-devel or libqt5xml-devel) 123 | 124 | Requires: kf5-plasma-framework 125 | Requires: plasma-desktop 126 | Requires: plasma-framework 127 | Requires: plasma-integration 128 | Requires: plasma-workspace 129 | 130 | %endif 131 | %endif 132 | %endif 133 | %endif 134 | 135 | BuildRequires: desktop-file-utils 136 | BuildRequires: fdupes 137 | 138 | 139 | %description 140 | Koi is a program designed to provide the KDE Plasma Desktop functionality 141 | to automatically switch between light and dark themes. 142 | Koi allows users to automate and manage their desktop themes, 143 | providing a convenient way to schedule theme changes on the KDE Plasma Desktop, 144 | enhancing the user experience with timely and automated visual adjustments. 145 | It supports full DBus service integration & running custom Bash scripts. 146 | 147 | %prep 148 | %autosetup -p1 -n %{original_name}-%{version} 149 | 150 | %build 151 | %if 0%{?suse_version} >= 1600 && 0%{?is_opensuse} || 0%{?fedora} >= 40 || 0%{?mageia} >= 10 152 | %cmake_kf6 153 | 154 | %kf6_build 155 | 156 | %if 0%{?sle_version} <= 150600 && 0%{?is_opensuse} || 0%{?suse_version} < 1600 && 0%{?is_opensuse} || 0%{?mageia} <= 9 157 | %cmake_kf5 158 | 159 | %kf5_build 160 | 161 | %endif 162 | %endif 163 | 164 | %install 165 | %if 0%{?suse_version} >= 1600 && 0%{?is_opensuse} || 0%{?fedora} >= 40 || 0%{?mageia} >= 10 166 | %kf6_install 167 | 168 | %if 0%{?sle_version} <= 150600 && 0%{?is_opensuse} || 0%{?suse_version} < 1600 && 0%{?is_opensuse} || 0%{?mageia} <= 9 169 | %kf5_install 170 | 171 | %endif 172 | %endif 173 | 174 | %check 175 | desktop-file-validate "%{buildroot}/%{_datadir}/applications/local.%{original_name}DbusInterface.desktop" 176 | %if 0%{?sle} && 0%{?is_opensuse} || 0%{?suse} && 0%{?is_opensuse} || 0%{?fedora} || 0%{?mageia} 177 | %fdupes %{buildroot}/ 178 | 179 | %else 180 | fdupes -s %{buildroot}/ 181 | 182 | %files 183 | %license LICENSE 184 | %doc "README.md" 185 | 186 | "%{_bindir}/%{name}" 187 | 188 | "%{_datadir}/applications/local.%{original_name}DbusInterface.desktop" 189 | "%{_datadir}/dbus-1/interfaces/dev.baduhai.%{original_name}.xml" 190 | "%{_datadir}/dbus-1/services/dev.baduhai.%{original_name}.service" 191 | %{_datadir}/icons/hicolor/scalable/apps/{%{name}.svg,%{name}_tray.svg} 192 | 193 | %changelog 194 | -------------------------------------------------------------------------------- /Development/Distributions/NixOS/README.md: -------------------------------------------------------------------------------- 1 | ***These are build instructions for Nix OS and *Nix' derived operating systems using KDE Plasma 6 . . .*** 2 | -------------------------------------------------------------------------------- /Development/Distributions/NixOS/default.nix: -------------------------------------------------------------------------------- 1 | { 2 | lib, 3 | stdenv, 4 | fetchFromGitHub, 5 | cmake, 6 | kconfig, 7 | kcoreaddons, 8 | kwidgetsaddons, 9 | wrapQtAppsHook, 10 | }: 11 | stdenv.mkDerivation rec { 12 | pname = "koi"; 13 | version = "0.5.1"; 14 | 15 | src = fetchFromGitHub { 16 | owner = "baduhai"; 17 | repo = "Koi"; 18 | rev = version; 19 | sha256 = "lib.fakeSha256"; 20 | }; 21 | 22 | # See https://github.com/baduhai/Koi/blob/master/development/Nix%20OS/dev.nix 23 | sourceRoot = "${src.name}/"; 24 | nativeBuildInputs = [ 25 | cmake 26 | wrapQtAppsHook 27 | ]; 28 | buildInputs = [ 29 | kconfig 30 | kcoreaddons 31 | kwidgetsaddons 32 | ]; 33 | 34 | meta = with lib; { 35 | description = "Scheduling LIGHT/DARK Theme Converter for the KDE Plasma Desktop"; 36 | longDescription = '' 37 | Koi is a program designed to provide the KDE Plasma Desktop functionality to automatically switch between light and dark themes. Koi is under semi-active development, and while it is stable enough to use daily, expect bugs. Koi is designed to be used with Plasma, and while some features may function under different desktop environments, they are unlikely to work and untested. 38 | 39 | Features: 40 | 41 | - Toggle between light and dark presets based on time 42 | - Change Plasma style 43 | - Change Qt colour scheme 44 | - Change Icon theme 45 | - Change GTK theme 46 | - Change wallpaper 47 | - Hide application to system tray 48 | - Toggle between LIGHT/DARK themes by clicking mouse wheel 49 | ''; 50 | license = licenses.lgpl3; 51 | platforms = platforms.linux; 52 | homepage = "https://github.com/baduhai/Koi"; 53 | maintainers = with lib.maintainers; [ fnune ]; 54 | }; 55 | } 56 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | GNU LESSER GENERAL PUBLIC LICENSE 3 | Version 3, 29 June 2007 4 | 5 | Copyright (C) 2007 Free Software Foundation, Inc. 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | 10 | This version of the GNU Lesser General Public License incorporates 11 | the terms and conditions of version 3 of the GNU General Public 12 | License, supplemented by the additional permissions listed below. 13 | 14 | 0. Additional Definitions. 15 | 16 | As used herein, "this License" refers to version 3 of the GNU Lesser 17 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 18 | General Public License. 19 | 20 | "The Library" refers to a covered work governed by this License, 21 | other than an Application or a Combined Work as defined below. 22 | 23 | An "Application" is any work that makes use of an interface provided 24 | by the Library, but which is not otherwise based on the Library. 25 | Defining a subclass of a class defined by the Library is deemed a mode 26 | of using an interface provided by the Library. 27 | 28 | A "Combined Work" is a work produced by combining or linking an 29 | Application with the Library. The particular version of the Library 30 | with which the Combined Work was made is also called the "Linked 31 | Version". 32 | 33 | The "Minimal Corresponding Source" for a Combined Work means the 34 | Corresponding Source for the Combined Work, excluding any source code 35 | for portions of the Combined Work that, considered in isolation, are 36 | based on the Application, and not on the Linked Version. 37 | 38 | The "Corresponding Application Code" for a Combined Work means the 39 | object code and/or source code for the Application, including any data 40 | and utility programs needed for reproducing the Combined Work from the 41 | Application, but excluding the System Libraries of the Combined Work. 42 | 43 | 1. Exception to Section 3 of the GNU GPL. 44 | 45 | You may convey a covered work under sections 3 and 4 of this License 46 | without being bound by section 3 of the GNU GPL. 47 | 48 | 2. Conveying Modified Versions. 49 | 50 | If you modify a copy of the Library, and, in your modifications, a 51 | facility refers to a function or data to be supplied by an Application 52 | that uses the facility (other than as an argument passed when the 53 | facility is invoked), then you may convey a copy of the modified 54 | version: 55 | 56 | a) under this License, provided that you make a good faith effort to 57 | ensure that, in the event an Application does not supply the 58 | function or data, the facility still operates, and performs 59 | whatever part of its purpose remains meaningful, or 60 | 61 | b) under the GNU GPL, with none of the additional permissions of 62 | this License applicable to that copy. 63 | 64 | 3. Object Code Incorporating Material from Library Header Files. 65 | 66 | The object code form of an Application may incorporate material from 67 | a header file that is part of the Library. You may convey such object 68 | code under terms of your choice, provided that, if the incorporated 69 | material is not limited to numerical parameters, data structure 70 | layouts and accessors, or small macros, inline functions and templates 71 | (ten or fewer lines in length), you do both of the following: 72 | 73 | a) Give prominent notice with each copy of the object code that the 74 | Library is used in it and that the Library and its use are 75 | covered by this License. 76 | 77 | b) Accompany the object code with a copy of the GNU GPL and this license 78 | document. 79 | 80 | 4. Combined Works. 81 | 82 | You may convey a Combined Work under terms of your choice that, 83 | taken together, effectively do not restrict modification of the 84 | portions of the Library contained in the Combined Work and reverse 85 | engineering for debugging such modifications, if you also do each of 86 | the following: 87 | 88 | a) Give prominent notice with each copy of the Combined Work that 89 | the Library is used in it and that the Library and its use are 90 | covered by this License. 91 | 92 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 93 | document. 94 | 95 | c) For a Combined Work that displays copyright notices during 96 | execution, include the copyright notice for the Library among 97 | these notices, as well as a reference directing the user to the 98 | copies of the GNU GPL and this license document. 99 | 100 | d) Do one of the following: 101 | 102 | 0) Convey the Minimal Corresponding Source under the terms of this 103 | License, and the Corresponding Application Code in a form 104 | suitable for, and under terms that permit, the user to 105 | recombine or relink the Application with a modified version of 106 | the Linked Version to produce a modified Combined Work, in the 107 | manner specified by section 6 of the GNU GPL for conveying 108 | Corresponding Source. 109 | 110 | 1) Use a suitable shared library mechanism for linking with the 111 | Library. A suitable mechanism is one that (a) uses at run time 112 | a copy of the Library already present on the user's computer 113 | system, and (b) will operate properly with a modified version 114 | of the Library that is interface-compatible with the Linked 115 | Version. 116 | 117 | e) Provide Installation Information, but only if you would otherwise 118 | be required to provide such information under section 6 of the 119 | GNU GPL, and only to the extent that such information is 120 | necessary to install and execute a modified version of the 121 | Combined Work produced by recombining or relinking the 122 | Application with a modified version of the Linked Version. (If 123 | you use option 4d0, the Installation Information must accompany 124 | the Minimal Corresponding Source and Corresponding Application 125 | Code. If you use option 4d1, you must provide the Installation 126 | Information in the manner specified by section 6 of the GNU GPL 127 | for conveying Corresponding Source.) 128 | 129 | 5. Combined Libraries. 130 | 131 | You may place library facilities that are a work based on the 132 | Library side by side in a single library together with other library 133 | facilities that are not Applications and are not covered by this 134 | License, and convey such a combined library under terms of your 135 | choice, if you do both of the following: 136 | 137 | a) Accompany the combined library with a copy of the same work based 138 | on the Library, uncombined with any other library facilities, 139 | conveyed under the terms of this License. 140 | 141 | b) Give prominent notice with the combined library that part of it 142 | is a work based on the Library, and explaining where to find the 143 | accompanying uncombined form of the same work. 144 | 145 | 6. Revised Versions of the GNU Lesser General Public License. 146 | 147 | The Free Software Foundation may publish revised and/or new versions 148 | of the GNU Lesser General Public License from time to time. Such new 149 | versions will be similar in spirit to the present version, but may 150 | differ in detail to address new problems or concerns. 151 | 152 | Each version is given a distinguishing version number. If the 153 | Library as you received it specifies that a certain numbered version 154 | of the GNU Lesser General Public License "or any later version" 155 | applies to it, you have the option of following the terms and 156 | conditions either of that published version or of any later version 157 | published by the Free Software Foundation. If the Library as you 158 | received it does not specify a version number of the GNU Lesser 159 | General Public License, you may choose any version of the GNU Lesser 160 | General Public License ever published by the Free Software Foundation. 161 | 162 | If the Library as you received it specifies that a proxy can decide 163 | whether future versions of the GNU Lesser General Public License shall 164 | apply, that proxy's public statement of acceptance of any version is 165 | permanent authorization for you to choose that version for the 166 | Library. 167 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

Koi

2 | 3 |

Koi

4 | 5 |

Scheduling LIGHT/DARK Theme Converter for the KDE Plasma Desktop

6 | 7 | Koi is a program designed to provide the KDE Plasma Desktop functionality to automatically switch between light and dark themes. Koi is under semi-active development, and while it is stable enough to use daily, expect bugs. Koi is designed to be used with Plasma, and while some features may function under different desktop environments, they are untested and unlikely to work . . . 8 | 9 | ___ 10 | 11 | ## Features 12 | 13 | - Toggle between **Light** and **Dark** preset themes based on **time** / **coordinated** schedule 14 | - Change **Plasma style** 15 | - Change **QT Colour scheme** 16 | - Change **Icon theme** 17 | - Change **GTK/GNOME theme** 18 | - Change **Light/Dark wallpapers** 19 | - **Hide application** to system tray *(it is currently unable to hide Koi from system tray)* 20 | - **Enable/Disable notifications** about Light/Dark theme switching 21 | - Toggle between LIGHT/DARK themes by clicking **middle mouse button** *(wheel)* 22 | - Full **DBus** service integration*; when setup correctly, enables possibility using Koi and theme switching using custom commands & keyboard shortcuts* 23 | - Run custom **BASH scripts** when switching between Light/Dark themes 24 | 25 | ___ 26 | 27 | ## Screenshots 28 | 29 | ![Screenshots](Screenshot.svg) 30 | 31 | ___ 32 | 33 | ## Using Koi 34 | 35 | Koi is designed to be very simple to use. After the initial run of Koi, settings for the **light** and **dark** presets will be empty, all you need to do is select which themes you would like for Koi to manage and select your desired themes by clicking the *Preferences* button. 36 | 37 | If you would like that Koi switch between your light and dark settings automatically, you can select this option in the main page of the program, and choose between following a user set schedule, or at **sunrise** and **sunset** *(planned/scheduled feature)*. To switch between **light** and **dark** manually right click the tray icon and select the desired preset, or do so from Koi's main window. 38 | 39 | After installation, Koi creates `~/.config/koirc` file inside user's Home configuration directory with established theme presets . . . 40 | 41 | #### Startup/Autostart 42 | In order to have Koi started at login, you can do so with the *KDE Plasma System Settings*. 43 | 44 | Search for: ***Autostart*** 45 | 46 | **Plasma** version **5** navigate to ***System Settings*** -> ***Startup and Shutdown*** -> ***Autostart***; and select "`+ Add...` -> `+ Add Application` -> `Utilities` -> `Koi`" 47 | 48 | 49 | **Plasma** version **6** navigate to ***System Settings*** -> ***System*** -> ***Autostart***, and select "`+ Add...` -> `+ Add Application` -> `Utilities` -> `Koi`" 50 | 51 | In addition, it is recommended having the option to *Start Koi hidden* checked, this will prevent Koi from popping up every time you start your operating system. 52 | 53 | ___ 54 | 55 | ## Getting Koi 56 | 57 | *Latest version: 0.5.1* 58 | 59 | ### Building from SOURCE 60 | 61 | **GENERIC Dependencies** 62 | 63 | KDE Frameworks [Development files]: 64 | 65 | * KF 6 Config 66 | * KF 6 Core Addons 67 | * KF 6 Widgets Addons 68 | 69 | KDE Plasma Desktop Environment 70 | 71 | QT [Development files]: 72 | 73 | * QT 6 Core 74 | * QT 6 DBus 75 | * QT 6 Gui 76 | * QT 6 Widgets 77 | * QT 6 Xml 78 | 79 | CMake Program 80 | 81 | 82 | **Build Instructions** 83 | 84 | Copy all: 85 | 86 | ``` 87 | # 1. 88 | git clone "https://github.com/baduhai/Koi.git" # Or: gh repo clone "baduhai/Koi" 89 | 90 | # 2. 91 | cmake -S "./Koi/" -B "./Koi/build/" 92 | 93 | # 3. 94 | cmake --build "./Koi/build/" --parallel # Remove [--parallel] Option on Weaker Hardware 95 | 96 | # 4. 97 | # a) 98 | sudo cmake --install "./Koi/build/" # This Will Install Koi into [/usr/local/bin/] Directory 99 | # b) Or: sudo cmake --install "./Koi/build/" --prefix="/usr/" # This Will Install Koi into [/usr/bin/] Directory 100 | 101 | # (5.) 102 | # sudo make uninstall -C "./Koi/build/" 103 | ``` 104 | 105 | ### Prerequisites: 106 | 107 | ### Arch Linux - [[AUR]](https://aur.archlinux.org/packages/koi/) 108 | 109 | **Build Dependencies:** 110 | 111 | - ``` 'gcc' 'qt6-base' 'qt6-tools' 'qt6-svg' 'cmake' 'extra-cmake-modules' ``` 112 | 113 | **Run Dependencies:** 114 | 115 | - ``` 'plasma-desktop' 'plasma-integration' ``` 116 | 117 | ### openSUSE TumbleWeed - [[OBS]](https://build.opensuse.org/package/show/KDE:Extra/koi) 118 | 119 | **Build Dependencies:** 120 | 121 | - ``` 'cmake' 'cmake-extras' 'kf6-kconfigwidgets-devel' 'kf6-kconfig-devel' 'kf6-kcoreaddons-devel' 'kf6-kdbusaddons-devel' 'qt6-base' 'qt6-dbus-devel' 'qt6-gui-devel' 'qt6-widgets-devel' 'qt6-xml-devel' 'hicolor-icon-theme' ``` 122 | 123 | **Run Dependencies:** 124 | 125 | - ``` 'plasma6-desktop' 'plasma6-workspace' ``` 126 | 127 | ### openSUSE Leap <= 15.6 - [[OBS]](https://build.opensuse.org/package/show/KDE:Extra/koi) 128 | 129 | **Build Dependencies:** 130 | 131 | - ``` 'cmake' 'cmake-extras' 'kf5-kconfigwidgets-devel' 'kf5-kconfig-devel' 'kf5-kcoreaddons-devel' 'kf5-kdbusaddons-devel' 'qt5-dbus-devel' 'qt5-gui-devel' 'qt5-widgets-devel' 'qt5-xml-devel' 'hicolor-icon-theme' ``` 132 | 133 | **Run Dependencies:** 134 | 135 | - ``` 'plasma5-desktop' 'plasma5-workspace' 'plasma-framework' ``` 136 | 137 | ### Fedora >= 40 - [[COPR]](https://copr.fedorainfracloud.org/coprs/birkch/Koi/) 138 | 139 | **Build Dependencies:** 140 | 141 | - ``` 'cmake' 'extra-cmake-modules' 'kf6-kconfigwidgets-devel' 'kf6-kconfig-devel' 'kf6-kcoreaddons-devel' 'kf6-kwidgetsaddons-devel' 'qt6-qtbase-devel' 'hicolor-icon-theme' ``` 142 | 143 | **Run Dependencies:** 144 | 145 | - ``` 'plasma-desktop' 'plasma-integration' 'plasma-workspace' ``` 146 | 147 | ### Debian <= 12 / Ubuntu <= 24.04 148 | 149 | **Build Dependencies:** 150 | 151 | - ``` 'g++' 'cmake' 'cmake-extras' 'libkf5config-dev' 'libkf5coreaddons-dev' 'libkf5dbusaddons-dev' 'libkf5widgetsaddons-dev' 'qtbase5-dev' ``` 152 | 153 | **Run Dependencies:** 154 | 155 | - ``` 'kde-plasma-desktop' 'plasma-framework' 'plasma-integration' 'plasma-workspace' ``` 156 | 157 | ### Debian >= 13 / KDE Neon / Ubuntu >= 24.10 158 | 159 | **Build Dependencies:** 160 | 161 | - ``` 'g++' 'cmake' 'cmake-extras' 'libkf6config-dev' 'libkf6coreaddons-dev' 'libkf6dbusaddons-dev' 'libkf6widgetsaddons-dev' 'qtbase6-dev' ``` 162 | 163 | **Run Dependencies:** 164 | 165 | - ``` 'kde-plasma-desktop' 'plasma-desktop' 'plasma-integration' 'plasma-workspace' ``` 166 | 167 | ### Void Linux 168 | 169 | **Build Dependencies:** 170 | 171 | - ``` 'gcc' 'cmake' 'extra-cmake-modules' 'qt6-base-devel' 'kf6-kcoreaddons-devel' 'kf6-kconfig-devel' 'kf6-kwidgetsaddons-devel' ``` 172 | 173 | **Run Dependencies:** 174 | 175 | - ``` 'kf6-kcoreaddons' 'kf6-kconfig' 'kf6-kconfigwidgets' 'kf6-kdbusaddons' 'plasma-desktop' 'plasma-integration' 'plasma-workspace' 'qt6-base' 'qt6-core' ``` 176 | 177 | ### *NixOS - [[NixPKGs]](https://github.com/NixOS/nixpkgs/blob/master/pkgs/kde/third-party/koi/default.nix)* 178 | 179 | - Install Directly from NixPKGs to your NIX Profile with: `nix-env -iA nixpkgs.koi` ; 180 | 181 | - Using NixOS Configuration (`configuration.nix`): 182 | `environment.systemPackages = with pkgs; [koi];` 183 | 184 | - Or Compile Manually Using `nix-build https://github.com/NixOS/nixpkgs/blob/master/pkgs/kde/third-party/koi/default.nix` 185 | 186 | 187 | 188 | ___ 189 | 190 | ## Credits: 191 | 192 | * William Franco Abdul Hai [(baduhai)](https://github.com/baduhai) 193 | * Martin Stibor [(Martin von Reichenberg)](https://github.com/MartinVonReichenberg) 194 | * Matthias Möller [(TinyTinni)](https://github.com/TinyTinni) 195 | * Spencer Williams [(spencerwi)](https://github.com/spencerwi) 196 | * Andrea Spelgatti [(Ourouk)](https://github.com/Ourouk) 197 | * Giovanni Santini [(Itachi San)](https://github.com/ItachiSan) 198 | * [DucVietCao](https://github.com/ducvietcao) 199 | * [PassportMidi](https://github.com/passportmidi) 200 | * [FinanceLurker](https://github.com/financelurker) 201 | * [miaouwu](https://github.com/miaouwu) 202 | * [Ari Melody](https://github.com/arimelody) 203 | * [ToBoil-Features](https://github.com/toboil-features) 204 | 205 | 206 | 207 | ## References: 208 | 209 | The following is a list of resources that was used as reference and inspiration for writing Koi. 210 | 211 | - [Yin-Yang](https://github.com/daehruoydeef/Yin-Yang) - For UI layout and features inspiration. 212 | - [system-tray-icon-qt](https://github.com/C0D1UM/system-tray-icon-qt) - For teaching me how to implement a system tray icon with Qt. 213 | - [plasma-theme-switcher](https://github.com/maldoinc/plasma-theme-switcher) - For teaching me how to set current Qt color scheme. 214 | - [ksetwallpaper](https://github.com/pashazz/ksetwallpaper) - For teaching me how to set the wallpaper on Plasma. 215 | - [SunRise](https://github.com/signetica/SunRise) - For scheduled sunrise/sunset light/dark theme switching. 216 | - [CTPL](https://github.com/vit-vit/CTPL) - Extra library for parallel job execution. 217 | - [Scheduler](https://github.com/Bosma/Scheduler) - For timed light/dark theme switching. 218 | - [This blog post from Zren](https://zren.github.io/2020/04/28/how-to-change-plasma-icon-theme-in-the-terminal) - For teaching me how to set the icon theme. 219 | 220 | 221 | *Koi was written as a project to practice QT by a novice programmer, expect code to be written sloppily.* 222 | -------------------------------------------------------------------------------- /cmake_uninstall.cmake.in: -------------------------------------------------------------------------------- 1 | # Check if the install manifest exists 2 | if(NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt") 3 | message(FATAL_ERROR "Cannot find install manifest: @CMAKE_BINARY_DIR@/install_manifest.txt") 4 | endif() 5 | 6 | # Read the list of installed files from the manifest 7 | file(READ "@CMAKE_BINARY_DIR@/install_manifest.txt" files) 8 | string(REGEX REPLACE "\n" ";" files "${files}") 9 | 10 | # Iterate over each file and remove it 11 | foreach(file ${files}) 12 | message(STATUS "Uninstalling \"${file}\"") 13 | if(EXISTS "${file}" OR IS_SYMLINK "${file}") 14 | execute_process( 15 | COMMAND @CMAKE_COMMAND@ -E remove "${file}" 16 | RESULT_VARIABLE rm_retval 17 | ) 18 | if(NOT "${rm_retval}" EQUAL 0) 19 | message(FATAL_ERROR "Problem when removing \"${file}\"") 20 | endif() 21 | else() 22 | message(STATUS "File \"${file}\" does not exist.") 23 | endif() 24 | endforeach() 25 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | project(Koi VERSION 0.5 LANGUAGES CXX) 3 | 4 | configure_file(headers/config.h.in config.h) 5 | include_directories(${CMAKE_CURRENT_BINARY_DIR}) 6 | 7 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 8 | 9 | set(CMAKE_AUTOMOC ON) 10 | set(CMAKE_AUTORCC ON) 11 | set(CMAKE_AUTOUIC ON) 12 | 13 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) 14 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) 15 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) 16 | 17 | # Define Installation Data-Root Directory 18 | set(CMAKE_INSTALL_DATAROOTDIR share) 19 | # Define *SYSTEM-WIDE* DBUS_INTERFACES_INSTALL_DIR variable 20 | set(DBUS_INTERFACES_INSTALL_DIR share/dbus-1/interfaces) 21 | # Define *SYSTEM-WIDE* DBUS_SERVICES_INSTALL_DIR variable 22 | set(DBUS_SERVICES_INSTALL_DIR share/dbus-1/services) 23 | 24 | # Configure DBus Service File 25 | configure_file(${CMAKE_CURRENT_SOURCE_DIR}/resources/dbus/services/dev.baduhai.Koi.service ${CMAKE_CURRENT_BINARY_DIR}/dev.baduhai.Koi.service) 26 | 27 | # Install files not being a part of INSTALL TARGETS section 28 | install(FILES ${CMAKE_CURRENT_BINARY_DIR}/dev.baduhai.Koi.xml DESTINATION ${DBUS_INTERFACES_INSTALL_DIR}) 29 | install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/koi.desktop DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/applications 30 | RENAME local.KoiDbusInterface.desktop) 31 | install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/resources/dbus/services/dev.baduhai.Koi.service DESTINATION ${DBUS_SERVICES_INSTALL_DIR}) 32 | install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/resources/icons/koi.svg DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/scalable/apps) 33 | install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/resources/icons/koi_tray.svg DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/scalable/apps) 34 | 35 | set(THREADS_PREFER_PTHREAD_FLAG ON) 36 | find_package(Threads REQUIRED) 37 | 38 | find_package(KF6Config) 39 | find_package(KF6CoreAddons) 40 | find_package(KF6WidgetsAddons) 41 | find_package(QT NAMES Qt6 REQUIRED COMPONENTS Core) 42 | find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS DBus Gui) 43 | find_package(Qt${QT_VERSION_MAJOR} OPTIONAL_COMPONENTS Widgets) 44 | find_package(Qt${QT_VERSION_MAJOR} OPTIONAL_COMPONENTS Xml) 45 | 46 | set(koi_SRC 47 | # Main app stuff. 48 | about.cpp 49 | license.cpp 50 | mainwindow.cpp 51 | utils.cpp 52 | runguard.cpp 53 | # QObject headers needs to be added as sources to properly run MOC and UIC. 54 | headers/about.h 55 | headers/license.h 56 | headers/mainwindow.h 57 | headers/utils.h 58 | headers/runguard.h 59 | # DBus interface stuff 60 | dbusinterface.cpp 61 | headers/dbusinterface.h 62 | # DBus interface XML file -- generated at build time 63 | ${CMAKE_CURRENT_BINARY_DIR}/dev.baduhai.Koi.xml 64 | # DBus interface adapter -- generated at build time from the XML file 65 | ${CMAKE_CURRENT_BINARY_DIR}/koiadaptor.h 66 | # UI items 67 | resources/resources.qrc 68 | ui/about.ui 69 | ui/license.ui 70 | ui/mainwindow.ui 71 | # Libraries for additional stuff 72 | libraries/SunRise.cpp 73 | # All plugins 74 | plugins/colorscheme.cpp 75 | plugins/gtk.cpp 76 | plugins/icons.cpp 77 | plugins/kvantumstyle.cpp 78 | plugins/plasmastyle.cpp 79 | plugins/wallpaper.cpp 80 | plugins/script.cpp 81 | # Main entrypoint 82 | main.cpp 83 | ) 84 | 85 | qt_generate_dbus_interface(headers/dbusinterface.h 86 | dev.baduhai.Koi.xml 87 | OPTIONS -A 88 | ) 89 | 90 | qt_add_dbus_adaptor(koi_SRC ${CMAKE_CURRENT_BINARY_DIR}/dev.baduhai.Koi.xml 91 | headers/dbusinterface.h KoiDbusInterface) 92 | 93 | 94 | add_executable(koi 95 | ${koi_SRC} 96 | ) 97 | 98 | target_compile_definitions(koi PUBLIC 99 | QT_DEPRECATED_WARNINGS 100 | ) 101 | 102 | target_link_libraries(koi PUBLIC 103 | KF6::ConfigCore 104 | KF6::ConfigGui 105 | KF6::CoreAddons 106 | KF6::WidgetsAddons 107 | Qt::Core 108 | Qt::DBus 109 | Qt::Gui 110 | Qt::Widgets 111 | Qt::Xml 112 | Threads::Threads 113 | ) 114 | 115 | install(TARGETS koi 116 | RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}" 117 | BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" 118 | LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" 119 | ) 120 | -------------------------------------------------------------------------------- /src/about.cpp: -------------------------------------------------------------------------------- 1 | #include "headers/about.h" 2 | #include "config.h" 3 | #include "headers/license.h" 4 | #include "ui/ui_about.h" 5 | 6 | #include 7 | #include 8 | 9 | About::About(QWidget *parent) : QDialog(parent), ui(new Ui::About) { 10 | ui->setupUi(this); 11 | this->setFixedSize(505, 330); 12 | QApplication::setApplicationName(PROJECT_NAME); 13 | ui->titleLabel->setText(QApplication::applicationName()); 14 | QString pgrmVersion = "Version "; 15 | QApplication::setApplicationVersion(PROJECT_VER); 16 | ui->verLabel->setText(pgrmVersion + QApplication::applicationVersion()); 17 | QString qtVersion = "
  • Qt "; 18 | qtVersion += qVersion(); 19 | qtVersion += "
"; 20 | ui->qtVerLabel->setText(qtVersion); 21 | ui->qtVerLabel->setTextFormat(Qt::RichText); 22 | QString kfVersion = "
  • KDE Frameworks "; 23 | kfVersion += KCoreAddons::versionString(); 24 | kfVersion += "
"; 25 | ui->kfVersion->setText(kfVersion); 26 | ui->kfVersion->setTextFormat(Qt::RichText); 27 | QString bosmaLabel = 28 | ""; 30 | ui->bosmaLib->setText(bosmaLabel); 31 | ui->bosmaLib->setTextFormat(Qt::RichText); 32 | ui->bosmaLib->setOpenExternalLinks(1); 33 | QString ctplLabel = 34 | ""; 35 | ui->ctpLib->setText(ctplLabel); 36 | ui->ctpLib->setTextFormat(Qt::RichText); 37 | ui->ctpLib->setOpenExternalLinks(1); 38 | QString sunriseLabel = 39 | ""; 41 | ui->sunriseLib->setText(sunriseLabel); 42 | ui->sunriseLib->setTextFormat(Qt::RichText); 43 | ui->sunriseLib->setOpenExternalLinks(1); 44 | ui->tabWidget->setCurrentIndex(0); 45 | } 46 | 47 | About::~About() { delete ui; } 48 | 49 | void About::on_closeBtn_clicked() { this->setVisible(0); } 50 | 51 | void About::on_licenseBtn_clicked() { 52 | auto *licence = new License(this); 53 | licence->open(); 54 | } 55 | 56 | void About::on_wWebsiteBtn_clicked() { 57 | QDesktopServices::openUrl( 58 | QUrl("https://github.com/baduhai", QUrl::TolerantMode)); 59 | } 60 | -------------------------------------------------------------------------------- /src/dbusinterface.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "headers/utils.h" 3 | #include "koiadaptor.h" 4 | 5 | KoiDbusInterface::KoiDbusInterface(QObject* parent) : QObject(parent) { 6 | new KoiDbusInterfaceAdaptor(this); 7 | QDBusConnection dbus = QDBusConnection::sessionBus(); 8 | dbus.registerObject("/Koi", this); 9 | dbus.registerService("dev.baduhai.Koi"); 10 | utils.initialiseSettings(); 11 | } 12 | 13 | KoiDbusInterface::~KoiDbusInterface() { 14 | QDBusConnection dbus = QDBusConnection::sessionBus(); 15 | dbus.unregisterService("dev.baduhai.Koi"); 16 | dbus.unregisterObject("/Koi"); 17 | } 18 | 19 | 20 | void KoiDbusInterface::goLightMode() { 21 | utils.goLight(); 22 | } 23 | 24 | void KoiDbusInterface::goDarkMode() { 25 | utils.goDark(); 26 | } 27 | 28 | void KoiDbusInterface::toggleMode() { 29 | utils.toggle(); 30 | } -------------------------------------------------------------------------------- /src/headers/about.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Qt libraries 4 | #include 5 | // KF libraries 6 | #include 7 | 8 | namespace Ui { 9 | class About; 10 | } 11 | 12 | class About : public QDialog { 13 | Q_OBJECT 14 | 15 | public: 16 | explicit About(QWidget *parent = nullptr); 17 | ~About(); 18 | 19 | private slots: 20 | void on_closeBtn_clicked(); 21 | void on_licenseBtn_clicked(); 22 | void on_wWebsiteBtn_clicked(); 23 | 24 | private: 25 | Ui::About *ui; 26 | }; 27 | -------------------------------------------------------------------------------- /src/headers/config.h.in: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define PROJECT_NAME "@PROJECT_NAME@" 4 | #define PROJECT_VER "@PROJECT_VERSION@" 5 | #define PROJECT_VER_MAJOR "@PROJECT_VERSION_MAJOR@" 6 | #define PROJECT_VER_MINOR "@PROJECT_VERSION_MINOR@" 7 | #define PTOJECT_VER_PATCH "@PROJECT_VERSION_PATCH@" 8 | -------------------------------------------------------------------------------- /src/headers/dbusinterface.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include "utils.h" 3 | 4 | class KoiDbusInterface : public QObject { 5 | Q_OBJECT 6 | Q_CLASSINFO("Koi D-Bus Interface", "dev.baduhai.Koi") 7 | 8 | public: 9 | KoiDbusInterface(QObject* parent); 10 | ~KoiDbusInterface(); 11 | 12 | public Q_SLOTS: 13 | void goLightMode(); 14 | void goDarkMode(); 15 | void toggleMode(); 16 | 17 | private: 18 | Utils utils; 19 | }; 20 | -------------------------------------------------------------------------------- /src/headers/license.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Qt libraries 4 | #include 5 | 6 | namespace Ui { 7 | class License; 8 | } 9 | 10 | class License : public QDialog { 11 | Q_OBJECT 12 | 13 | public: 14 | explicit License(QWidget *parent = nullptr); 15 | ~License(); 16 | 17 | private slots: 18 | void on_closeBtn_clicked(); 19 | 20 | private: 21 | Ui::License *ui; 22 | }; 23 | -------------------------------------------------------------------------------- /src/headers/mainwindow.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Qt libraries 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | // Headers 12 | #include "utils.h" 13 | 14 | namespace Bosma{ 15 | class Scheduler; 16 | } 17 | 18 | QT_BEGIN_NAMESPACE 19 | namespace Ui { 20 | class MainWindow; 21 | } 22 | QT_END_NAMESPACE 23 | 24 | class MainWindow : public QMainWindow { 25 | Q_OBJECT 26 | 27 | public: 28 | MainWindow(QWidget *parent = nullptr); 29 | ~MainWindow(); 30 | 31 | public slots: 32 | 33 | private slots: 34 | void closeEvent(QCloseEvent *event); 35 | 36 | void iconActivated(QSystemTrayIcon::ActivationReason); 37 | 38 | void refreshDirs(); 39 | void loadPrefs(); 40 | void savePrefs(); 41 | void toggleVisibility(); 42 | int prefsSaved(); 43 | void scheduleLight(Bosma::Scheduler& s); 44 | void scheduleDark(Bosma::Scheduler& s); 45 | void scheduleSunEvent(Bosma::Scheduler& s); 46 | 47 | void on_prefsBtn_clicked(); 48 | void on_backBtn_clicked(); 49 | void on_applyBtn_clicked(); 50 | void on_lightBtn_clicked(); 51 | void on_darkBtn_clicked(); 52 | // void on_refreshBtn_clicked(); 53 | 54 | void on_styleCheckBox_stateChanged(int arg1); 55 | void on_colorCheckBox_stateChanged(int arg1); 56 | void on_iconCheckBox_stateChanged(int arg1); 57 | void on_gtkCheckBox_stateChanged(int arg1); 58 | void on_kvantumStyleCheckBox_stateChanged(int arg1); 59 | void on_wallCheckBox_stateChanged(int arg1); 60 | void on_scriptCheckBox_stateChanged(int arg1); 61 | void on_autoCheckBox_stateChanged(int arg1); 62 | void on_scheduleRadioBtn_toggled(bool checked); 63 | 64 | void on_actionQuit_triggered(); 65 | void on_actionPrefs_triggered(); 66 | void on_actionAbout_triggered(); 67 | void on_actionHide_triggered(); 68 | // void on_actionRefresh_triggered(); 69 | 70 | void on_lightWallBtn_clicked(); 71 | void on_darkWallBtn_clicked(); 72 | void on_lightScriptBtn_clicked(); 73 | void on_darkScriptBtn_clicked(); 74 | void on_lightDropStyle_currentTextChanged(const QString &arg1); 75 | void on_darkDropStyle_currentTextChanged(const QString &arg1); 76 | void on_lightDropColor_currentTextChanged(const QString &arg1); 77 | void on_darkDropColor_currentTextChanged(const QString &arg1); 78 | void on_lightDropGtk_currentTextChanged(const QString &arg1); 79 | void on_darkDropGtk_currentTextChanged(const QString &arg1); 80 | void on_lightDropIcon_currentTextChanged(const QString &arg1); 81 | void on_darkDropIcon_currentTextChanged(const QString &arg1); 82 | void on_lightDropKvantumStyle_currentTextChanged(const QString &arg1); 83 | void on_darkDropKvantumStyle_currentTextChanged(const QString &arg1); 84 | void on_lightTimeEdit_userTimeChanged(const QTime &time); 85 | void on_darkTimeEdit_userTimeChanged(const QTime &time); 86 | void on_latitudeDSB_valueChanged(double lat); 87 | void on_longitudeDSB_valueChanged(double lon); 88 | void on_hiddenCheckBox_stateChanged(int arg1); 89 | void on_notifyCheckBox_stateChanged(int arg1); 90 | 91 | void on_actionRestart_triggered(); 92 | 93 | private: 94 | Ui::MainWindow *ui; 95 | QSystemTrayIcon *trayIcon; 96 | QMenu *trayMenu; 97 | QMenu *createMenu(); 98 | QString scheduleType; 99 | QString lightStyle; 100 | QString darkStyle; 101 | QString lightColor; 102 | QString darkColor; 103 | QString lightIcon; 104 | QString darkIcon; 105 | QString lightGtk; 106 | QString darkGtk; 107 | QString lightWall; 108 | QString darkWall; 109 | QString lightScript; 110 | QString darkScript; 111 | QString lightKvantumStyle; 112 | QString darkKvantumStyle; 113 | QString lightTime; 114 | QString darkTime; 115 | Utils utils; 116 | std::unique_ptr scheduler; 117 | 118 | protected: 119 | }; 120 | -------------------------------------------------------------------------------- /src/headers/plugin.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | // Generic plugin applicable to all supported plugins. 8 | class Plugin { 9 | public: 10 | virtual void setTheme(QString themeName) = 0; 11 | virtual QStringList getThemes() { return QStringList(); }; 12 | }; 13 | 14 | // Plugin that handles theme switching via DBus communication. 15 | class DbusPlugin : Plugin { 16 | protected: 17 | QDBusInterface* interface = nullptr; 18 | }; 19 | -------------------------------------------------------------------------------- /src/headers/runguard.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | // from https://stackoverflow.com/questions/5006547/qt-best-practice-for-a-single-instance-app-protection 9 | class RunGuard 10 | { 11 | 12 | public: 13 | RunGuard( const QString& key ); 14 | ~RunGuard(); 15 | 16 | bool isAnotherRunning(); 17 | bool tryToRun(); 18 | void release(); 19 | 20 | private: 21 | 22 | const QString key; 23 | const QString memLockKey; 24 | const QString sharedmemKey; 25 | 26 | QSharedMemory sharedMem; 27 | QSystemSemaphore memLock; 28 | 29 | Q_DISABLE_COPY( RunGuard ) 30 | }; 31 | -------------------------------------------------------------------------------- /src/headers/utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | // Qt libraries 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | // Headers 15 | #include "src/plugins/colorscheme.h" 16 | #include "src/plugins/gtk.h" 17 | #include "src/plugins/icons.h" 18 | #include "src/plugins/kvantumstyle.h" 19 | #include "src/plugins/plasmastyle.h" 20 | #include "src/plugins/script.h" 21 | #include "src/plugins/wallpaper.h" 22 | 23 | class Utils : public QObject { 24 | Q_OBJECT 25 | 26 | public: 27 | enum Mode { 28 | Undefined = 0, 29 | Dark = 1, 30 | Light = 2, 31 | }; 32 | Q_ENUM(Mode) 33 | 34 | Utils(); 35 | 36 | std::unique_ptr settings; 37 | void initialiseSettings(); 38 | 39 | QStringList getPlasmaStyles(void); 40 | QStringList getColorSchemes(void); 41 | QStringList getIconThemes(void); 42 | QStringList getCursorThemes(void); 43 | QStringList getGtkThemes(void); 44 | QStringList getKvantumStyles(void); 45 | 46 | void notify(QString notifySummary = "", QString notifyBody = "", 47 | int timeoutms = 5000); 48 | void startupTimeCheck(); 49 | void startupSunCheck(); 50 | 51 | void toggle(); 52 | void goLight(); 53 | void goDark(); 54 | void goLightStyle(); 55 | void goDarkStyle(); 56 | void goLightColors(); 57 | void goDarkColors(); 58 | void goLightIcons(); 59 | void goDarkIcons(); 60 | void goLightGtk(); 61 | void goDarkGtk(); 62 | void goLightKvantumStyle(); 63 | void goDarkKvantumStyle(); 64 | void goLightWall(); 65 | void goDarkWall(); 66 | void goLightScript(); 67 | void goDarkScript(); 68 | void restartProcess(); 69 | 70 | private: 71 | PlasmaStyle plasmastyle; 72 | ColorScheme colorscheme; 73 | Icons icons; 74 | Gtk gtk; 75 | Wallpaper wallpaper; 76 | KvantumStyle kvantumStyle; 77 | Script script; 78 | 79 | QDBusInterface* notifyInterface = nullptr; 80 | }; 81 | -------------------------------------------------------------------------------- /src/koi.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Type=Application 3 | Version=1.5 4 | Name=Koi 5 | GenericName=Theme Switcher 6 | Comment=Scheduling LIGHT/DARK Theme Switcher for the KDE Plasma Desktop 7 | Icon=koi 8 | DBusActivatable=true 9 | Implements=dev.baduhai.Koi;local.KoiDbusInterface;org.freedesktop.DBus.Introspectable;org.freedesktop.DBus.Properties;org.freedesktop.DBus.Peer 10 | TryExec=koi 11 | Exec=koi 12 | Terminal=false 13 | Categories=Utility 14 | Keywords=Theme;Switch;Light;Dark;Schedule;Sunrise;Sunset;Dusk;Dawn;Timer;Kvantum;Set; 15 | StartupNotify=true 16 | SingleMainWindow=true 17 | StartupWMClass=Koi 18 | -------------------------------------------------------------------------------- /src/libraries/Cron.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace Bosma { 10 | using Clock = std::chrono::system_clock; 11 | 12 | inline void add(std::tm &tm, Clock::duration time) { 13 | auto tp = Clock::from_time_t(std::mktime(&tm)); 14 | auto tp_adjusted = tp + time; 15 | auto tm_adjusted = Clock::to_time_t(tp_adjusted); 16 | tm = *std::localtime(&tm_adjusted); 17 | } 18 | 19 | class BadCronExpression : public std::exception { 20 | public: 21 | explicit BadCronExpression(std::string msg) : msg_(std::move(msg)) {} 22 | 23 | const char *what() const noexcept override { return (msg_.c_str()); } 24 | 25 | private: 26 | std::string msg_; 27 | }; 28 | 29 | inline void verify_and_set(const std::string &token, 30 | const std::string &expression, int &field, 31 | const int lower_bound, const int upper_bound, 32 | const bool adjust = false) { 33 | if (token == "*") 34 | field = -1; 35 | else { 36 | try { 37 | field = std::stoi(token); 38 | } catch (const std::invalid_argument &) { 39 | throw BadCronExpression("malformed cron string (`" + token + 40 | "` not an integer or *): " + expression); 41 | } catch (const std::out_of_range &) { 42 | throw BadCronExpression("malformed cron string (`" + token + 43 | "` not convertable to int): " + expression); 44 | } 45 | if (field < lower_bound || field > upper_bound) { 46 | std::ostringstream oss; 47 | oss << "malformed cron string ('" << token 48 | << "' must be <= " << upper_bound << " and >= " << lower_bound 49 | << "): " << expression; 50 | throw BadCronExpression(oss.str()); 51 | } 52 | if (adjust) 53 | field--; 54 | } 55 | } 56 | 57 | class Cron { 58 | public: 59 | explicit Cron(const std::string &expression) { 60 | std::istringstream iss(expression); 61 | std::vector tokens{std::istream_iterator{iss}, 62 | std::istream_iterator{}}; 63 | 64 | if (tokens.size() != 5) 65 | throw BadCronExpression("malformed cron string (must be 5 fields): " + 66 | expression); 67 | 68 | verify_and_set(tokens[0], expression, minute, 0, 59); 69 | verify_and_set(tokens[1], expression, hour, 0, 23); 70 | verify_and_set(tokens[2], expression, day, 1, 31); 71 | verify_and_set(tokens[3], expression, month, 1, 12, true); 72 | verify_and_set(tokens[4], expression, day_of_week, 0, 6); 73 | } 74 | 75 | // http://stackoverflow.com/a/322058/1284550 76 | Clock::time_point 77 | cron_to_next(const Clock::time_point from = Clock::now()) const { 78 | // get current time as a tm object 79 | auto now = Clock::to_time_t(from); 80 | std::tm next(*std::localtime(&now)); 81 | // it will always at least run the next minute 82 | next.tm_sec = 0; 83 | add(next, std::chrono::minutes(1)); 84 | while (true) { 85 | if (month != -1 && next.tm_mon != month) { 86 | // add a month 87 | // if this will bring us over a year, increment the year instead and 88 | // reset the month 89 | if (next.tm_mon + 1 > 11) { 90 | next.tm_mon = 0; 91 | next.tm_year++; 92 | } else 93 | next.tm_mon++; 94 | 95 | next.tm_mday = 1; 96 | next.tm_hour = 0; 97 | next.tm_min = 0; 98 | continue; 99 | } 100 | if (day != -1 && next.tm_mday != day) { 101 | add(next, std::chrono::hours(24)); 102 | next.tm_hour = 0; 103 | next.tm_min = 0; 104 | continue; 105 | } 106 | if (day_of_week != -1 && next.tm_wday != day_of_week) { 107 | add(next, std::chrono::hours(24)); 108 | next.tm_hour = 0; 109 | next.tm_min = 0; 110 | continue; 111 | } 112 | if (hour != -1 && next.tm_hour != hour) { 113 | add(next, std::chrono::hours(1)); 114 | next.tm_min = 0; 115 | continue; 116 | } 117 | if (minute != -1 && next.tm_min != minute) { 118 | add(next, std::chrono::minutes(1)); 119 | continue; 120 | } 121 | break; 122 | } 123 | 124 | // telling mktime to figure out dst 125 | next.tm_isdst = -1; 126 | return Clock::from_time_t(std::mktime(&next)); 127 | } 128 | 129 | int minute, hour, day, month, day_of_week; 130 | }; 131 | } // namespace Bosma -------------------------------------------------------------------------------- /src/libraries/InterruptableSleep.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace Bosma { 8 | class InterruptableSleep { 9 | 10 | using Clock = std::chrono::system_clock; 11 | 12 | // InterruptableSleep offers a sleep that can be interrupted by any thread. 13 | // It can be interrupted multiple times 14 | // and be interrupted before any sleep is called (the sleep will immediately 15 | // complete) Has same interface as condition_variables and futures, except 16 | // with sleep instead of wait. For a given object, sleep can be called on 17 | // multiple threads safely, but is not recommended as behaviour is undefined. 18 | 19 | public: 20 | InterruptableSleep() : interrupted(false) {} 21 | 22 | InterruptableSleep(const InterruptableSleep &) = delete; 23 | 24 | InterruptableSleep(InterruptableSleep &&) noexcept = delete; 25 | 26 | ~InterruptableSleep() noexcept = default; 27 | 28 | InterruptableSleep &operator=(const InterruptableSleep &) noexcept = delete; 29 | 30 | InterruptableSleep &operator=(InterruptableSleep &&) noexcept = delete; 31 | 32 | void sleep_for(Clock::duration duration) { 33 | std::unique_lock ul(m); 34 | cv.wait_for(ul, duration, [this] { return interrupted; }); 35 | interrupted = false; 36 | } 37 | 38 | void sleep_until(Clock::time_point time) { 39 | std::unique_lock ul(m); 40 | cv.wait_until(ul, time, [this] { return interrupted; }); 41 | interrupted = false; 42 | } 43 | 44 | void sleep() { 45 | std::unique_lock ul(m); 46 | cv.wait(ul, [this] { return interrupted; }); 47 | interrupted = false; 48 | } 49 | 50 | void interrupt() { 51 | std::lock_guard lg(m); 52 | interrupted = true; 53 | cv.notify_one(); 54 | } 55 | 56 | private: 57 | bool interrupted; 58 | std::mutex m; 59 | std::condition_variable cv; 60 | }; 61 | } // namespace Bosma 62 | -------------------------------------------------------------------------------- /src/libraries/Scheduler.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "ctpl_stl.h" 7 | 8 | #include "InterruptableSleep.h" 9 | #include "Cron.h" 10 | 11 | namespace Bosma { 12 | using Clock = std::chrono::system_clock; 13 | 14 | class Task { 15 | public: 16 | explicit Task(std::function &&f, bool recur = false, bool interval = false) : 17 | f(std::move(f)), recur(recur), interval(interval) {} 18 | 19 | virtual Clock::time_point get_new_time() const = 0; 20 | 21 | std::function f; 22 | 23 | bool recur; 24 | bool interval; 25 | }; 26 | 27 | class InTask : public Task { 28 | public: 29 | explicit InTask(std::function &&f) : Task(std::move(f)) {} 30 | 31 | // dummy time_point because it's not used 32 | Clock::time_point get_new_time() const override { return Clock::time_point(Clock::duration(0)); } 33 | }; 34 | 35 | class EveryTask : public Task { 36 | public: 37 | EveryTask(Clock::duration time, std::function &&f, bool interval = false) : 38 | Task(std::move(f), true, interval), time(time) {} 39 | 40 | Clock::time_point get_new_time() const override { 41 | return Clock::now() + time; 42 | }; 43 | Clock::duration time; 44 | }; 45 | 46 | class CronTask : public Task { 47 | public: 48 | CronTask(const std::string &expression, std::function &&f) : Task(std::move(f), true), 49 | cron(expression) {} 50 | 51 | Clock::time_point get_new_time() const override { 52 | return cron.cron_to_next(); 53 | }; 54 | Cron cron; 55 | }; 56 | 57 | inline bool try_parse(std::tm &tm, const std::string &expression, const std::string &format) { 58 | std::stringstream ss(expression); 59 | return !(ss >> std::get_time(&tm, format.c_str())).fail(); 60 | } 61 | 62 | class Scheduler { 63 | public: 64 | explicit Scheduler(unsigned int max_n_tasks = 4) : done(false), threads(max_n_tasks + 1) { 65 | threads.push([this](int) { 66 | while (!done) { 67 | if (tasks.empty()) { 68 | sleeper.sleep(); 69 | } else { 70 | auto time_of_first_task = (*tasks.begin()).first; 71 | sleeper.sleep_until(time_of_first_task); 72 | } 73 | manage_tasks(); 74 | } 75 | }); 76 | } 77 | 78 | Scheduler(const Scheduler &) = delete; 79 | 80 | Scheduler(Scheduler &&) noexcept = delete; 81 | 82 | Scheduler &operator=(const Scheduler &) = delete; 83 | 84 | Scheduler &operator=(Scheduler &&) noexcept = delete; 85 | 86 | ~Scheduler() { 87 | done = true; 88 | sleeper.interrupt(); 89 | } 90 | 91 | template 92 | void in(const Clock::time_point time, _Callable &&f, _Args &&... args) { 93 | std::shared_ptr t = std::make_shared( 94 | std::bind(std::forward<_Callable>(f), std::forward<_Args>(args)...)); 95 | add_task(time, std::move(t)); 96 | } 97 | 98 | template 99 | void in(const Clock::duration time, _Callable &&f, _Args &&... args) { 100 | in(Clock::now() + time, std::forward<_Callable>(f), std::forward<_Args>(args)...); 101 | } 102 | 103 | template 104 | void at(const std::string &time, _Callable &&f, _Args &&... args) { 105 | // get current time as a tm object 106 | auto time_now = Clock::to_time_t(Clock::now()); 107 | std::tm tm = *std::localtime(&time_now); 108 | 109 | // our final time as a time_point 110 | Clock::time_point tp; 111 | 112 | if (try_parse(tm, time, "%H:%M:%S")) { 113 | // convert tm back to time_t, then to a time_point and assign to final 114 | tp = Clock::from_time_t(std::mktime(&tm)); 115 | 116 | // if we've already passed this time, the user will mean next day, so add a day. 117 | if (Clock::now() >= tp) 118 | tp += std::chrono::hours(24); 119 | } else if (try_parse(tm, time, "%Y-%m-%d %H:%M:%S")) { 120 | tp = Clock::from_time_t(std::mktime(&tm)); 121 | } else if (try_parse(tm, time, "%Y/%m/%d %H:%M:%S")) { 122 | tp = Clock::from_time_t(std::mktime(&tm)); 123 | } else { 124 | // could not parse time 125 | throw std::runtime_error("Cannot parse time string: " + time); 126 | } 127 | 128 | in(tp, std::forward<_Callable>(f), std::forward<_Args>(args)...); 129 | } 130 | 131 | template 132 | void every(const Clock::duration time, _Callable &&f, _Args &&... args) { 133 | std::shared_ptr t = std::make_shared(time, std::bind(std::forward<_Callable>(f), 134 | std::forward<_Args>(args)...)); 135 | auto next_time = t->get_new_time(); 136 | add_task(next_time, std::move(t)); 137 | } 138 | 139 | // expression format: 140 | // from https://en.wikipedia.org/wiki/Cron#Overview 141 | // ┌───────────── minute (0 - 59) 142 | // │ ┌───────────── hour (0 - 23) 143 | // │ │ ┌───────────── day of month (1 - 31) 144 | // │ │ │ ┌───────────── month (1 - 12) 145 | // │ │ │ │ ┌───────────── day of week (0 - 6) (Sunday to Saturday) 146 | // │ │ │ │ │ 147 | // │ │ │ │ │ 148 | // * * * * * 149 | template 150 | void cron(const std::string &expression, _Callable &&f, _Args &&... args) { 151 | std::shared_ptr t = std::make_shared(expression, std::bind(std::forward<_Callable>(f), 152 | std::forward<_Args>(args)...)); 153 | auto next_time = t->get_new_time(); 154 | add_task(next_time, std::move(t)); 155 | } 156 | 157 | template 158 | void interval(const Clock::duration time, _Callable &&f, _Args &&... args) { 159 | std::shared_ptr t = std::make_shared(time, std::bind(std::forward<_Callable>(f), 160 | std::forward<_Args>(args)...), true); 161 | add_task(Clock::now(), std::move(t)); 162 | } 163 | 164 | private: 165 | std::atomic done; 166 | 167 | Bosma::InterruptableSleep sleeper; 168 | 169 | std::multimap> tasks; 170 | std::mutex lock; 171 | ctpl::thread_pool threads; 172 | 173 | void add_task(const Clock::time_point time, std::shared_ptr t) { 174 | std::lock_guard l(lock); 175 | tasks.emplace(time, std::move(t)); 176 | sleeper.interrupt(); 177 | } 178 | 179 | void manage_tasks() { 180 | std::lock_guard l(lock); 181 | 182 | auto end_of_tasks_to_run = tasks.upper_bound(Clock::now()); 183 | 184 | // if there are any tasks to be run and removed 185 | if (end_of_tasks_to_run != tasks.begin()) { 186 | // keep track of tasks that will be re-added 187 | decltype(tasks) recurred_tasks; 188 | 189 | // for all tasks that have been triggered 190 | for (auto i = tasks.begin(); i != end_of_tasks_to_run; ++i) { 191 | 192 | auto &task = (*i).second; 193 | 194 | if (task->interval) { 195 | // if it's an interval task, only add the task back after f() is completed 196 | threads.push([this, task](int) { 197 | task->f(); 198 | // no risk of race-condition, 199 | // add_task() will wait for manage_tasks() to release lock 200 | add_task(task->get_new_time(), task); 201 | }); 202 | } else { 203 | threads.push([task](int) { 204 | task->f(); 205 | }); 206 | // calculate time of next run and add the new task to the tasks to be recurred 207 | if (task->recur) 208 | recurred_tasks.emplace(task->get_new_time(), std::move(task)); 209 | } 210 | } 211 | 212 | // remove the completed tasks 213 | tasks.erase(tasks.begin(), end_of_tasks_to_run); 214 | 215 | // re-add the tasks that are recurring 216 | for (auto &task : recurred_tasks) 217 | tasks.emplace(task.first, std::move(task.second)); 218 | } 219 | } 220 | }; 221 | } -------------------------------------------------------------------------------- /src/libraries/SunRise.cpp: -------------------------------------------------------------------------------- 1 | // Compute times of sunrise and sunset at a specified latitude and longitude. 2 | // 3 | // This software minimizes computational work by performing the full calculation 4 | // of the solar position three times, at the beginning, middle, and end of the 5 | // period of interest. Three point interpolation is used to predict the position 6 | // for each hour, and the arithmetic mean is used to predict the half-hour positions. 7 | // 8 | // The full computational burden is negligible on modern computers, but the 9 | // algorithm is effective and still useful for small embedded systems. 10 | // 11 | // This software was originally adapted to javascript by Stephen R. Schmitt 12 | // from a BASIC program from the 'Astronomical Computing' column of Sky & Telescope, 13 | // August 1994, page 84, written by Roger W. Sinnott. 14 | // 15 | // Subsequently adapted from Stephen R. Schmitt's javascript to c++ for the Arduino 16 | // by Cyrus Rahman, this work is subject to Stephen Schmitt's copyright: 17 | // 18 | // Copyright 2007 Stephen R. Schmitt 19 | // Subsequent work Copyright 2020 Cyrus Rahman 20 | // You may use or modify this source code in any way you find useful, provided 21 | // that you agree that the author(s) have no warranty, obligations or liability. You 22 | // must determine the suitability of this source code for your use. 23 | // 24 | // Redistributions of this source code must retain this copyright notice. 25 | 26 | #include 27 | #include "SunRise.h" 28 | 29 | #define K1 15*(M_PI/180)*1.0027379 30 | 31 | struct skyCoordinates { 32 | double RA; // Right ascension 33 | double declination; // Declination 34 | }; 35 | 36 | // Determine the nearest sun rise or set event previous, and the nearest 37 | // sun rise or set event subsequent, to the specified time in seconds since the 38 | // Unix epoch (January 1, 1970) and at the specified latitude and longitude in 39 | // degrees. 40 | // 41 | // We look for events from SR_WINDOW/2 hours in the past to SR_WINDOW/2 hours 42 | // in the future. 43 | void 44 | SunRise::calculate(double latitude, double longitude, time_t t) { 45 | skyCoordinates sunPosition[3]; 46 | double offsetDays; 47 | 48 | initClass(); 49 | queryTime = t; 50 | offsetDays = julianDate(t) - 2451545L; // Days since Jan 1, 2000, 1200UTC. 51 | // Begin testing (SR_WINDOW / 2) hours before requested time. 52 | offsetDays -= (double)SR_WINDOW / (2 * 24) ; 53 | 54 | // Calculate coordinates at start, middle, and end of search period. 55 | for (int i = 0; i < 3; i ++) { 56 | sunPosition[i] = sun(offsetDays + i * (double)SR_WINDOW / (2 * 24)); 57 | } 58 | 59 | // If the RA wraps around during this period, unwrap it to keep the 60 | // sequence smooth for interpolation. 61 | if (sunPosition[1].RA <= sunPosition[0].RA) 62 | sunPosition[1].RA += 2 * M_PI; 63 | if (sunPosition[2].RA <= sunPosition[1].RA) 64 | sunPosition[2].RA += 2 * M_PI; 65 | 66 | // Initialize interpolation array. 67 | skyCoordinates spWindow[3]; 68 | spWindow[0].RA = sunPosition[0].RA; 69 | spWindow[0].declination = sunPosition[0].declination; 70 | 71 | for (int k = 0; k < SR_WINDOW; k++) { // Check each interval of search period 72 | float ph = (float)(k + 1)/SR_WINDOW; 73 | 74 | spWindow[2].RA = interpolate(sunPosition[0].RA, 75 | sunPosition[1].RA, 76 | sunPosition[2].RA, ph); 77 | spWindow[2].declination = interpolate(sunPosition[0].declination, 78 | sunPosition[1].declination, 79 | sunPosition[2].declination, ph); 80 | 81 | // Look for sunrise/set events during this interval. 82 | testSunRiseSet(k, offsetDays, latitude, longitude, spWindow); 83 | 84 | spWindow[0] = spWindow[2]; // Advance to next interval. 85 | } 86 | } 87 | 88 | // Look for sun rise or set events during an hour. 89 | void 90 | SunRise::testSunRiseSet(int k, double offsetDays, double latitude, double longitude, 91 | skyCoordinates *sp) { 92 | double ha[3], VHz[3]; 93 | double lSideTime; 94 | 95 | // Get (local_sidereal_time - SR_WINDOW / 2) hours in radians. 96 | lSideTime = localSiderealTime(offsetDays, longitude) * 2* M_PI / 360; 97 | 98 | // Calculate Hour Angle. 99 | ha[0] = lSideTime - sp[0].RA + k*K1; 100 | ha[2] = lSideTime - sp[2].RA + k*K1 + K1; 101 | 102 | // Hour Angle and declination at half hour. 103 | ha[1] = (ha[2] + ha[0])/2; 104 | sp[1].declination = (sp[2].declination + sp[0].declination)/2; 105 | 106 | double s = sin(M_PI / 180 * latitude); 107 | double c = cos(M_PI / 180 * latitude); 108 | 109 | // refraction + sun semidiameter at horizon 110 | double z = cos(M_PI / 180 * 90.833); 111 | 112 | VHz[0] = s * sin(sp[0].declination) + c * cos(sp[0].declination) * cos(ha[0]) - z; 113 | VHz[2] = s * sin(sp[2].declination) + c * cos(sp[2].declination) * cos(ha[2]) - z; 114 | 115 | if (signbit(VHz[0]) == signbit(VHz[2])) 116 | goto noevent; // No event this hour. 117 | 118 | VHz[1] = s * sin(sp[1].declination) + c * cos(sp[1].declination) * cos(ha[1]) - z; 119 | 120 | double a, b, d, e, time; 121 | a = 2 * VHz[2] - 4 * VHz[1] + 2 * VHz[0]; 122 | b = 4 * VHz[1] - 3 * VHz[0] - VHz[2]; 123 | d = b * b - 4 * a * VHz[0]; 124 | 125 | if (d < 0) 126 | goto noevent; // No event this hour. 127 | 128 | d = sqrt(d); 129 | e = (-b + d) / (2 * a); 130 | if ((e < 0) || (e > 1)) 131 | e = (-b - d) / (2 * a); 132 | time = k + e + 1 / 120; // Time since k=0 of event (in hours). 133 | 134 | // The time we started searching + the time from the start of the search to the 135 | // event is the time of the event. Add (time since k=0) - window/2 hours. 136 | time_t eventTime; 137 | eventTime = queryTime + (time - SR_WINDOW / 2) *60 *60; 138 | 139 | double hz, nz, dz, az; 140 | hz = ha[0] + e * (ha[2] - ha[0]); // Azimuth of the sun at the event. 141 | nz = -cos(sp[1].declination) * sin(hz); 142 | dz = c * sin(sp[1].declination) - s * cos(sp[1].declination) * cos(hz); 143 | az = atan2(nz, dz) / (M_PI / 180); 144 | if (az < 0) 145 | az += 360; 146 | 147 | // If there is no previously recorded event of this type, save this event. 148 | // 149 | // If this event is previous to queryTime, and is the nearest event to queryTime 150 | // of events of its type previous to queryType, save this event, replacing the 151 | // previously recorded event of its type. Events subsequent to queryTime are 152 | // treated similarly, although since events are tested in chronological order 153 | // no replacements will occur as successive events will be further from 154 | // queryTime. 155 | // 156 | // If this event is subsequent to queryTime and there is an event of its type 157 | // previous to queryTime, then there is an event of the other type between the 158 | // two events of this event's type. If the event of the other type is 159 | // previous to queryTime, then it is the nearest event to queryTime that is 160 | // previous to queryTime. In this case save the current event, replacing 161 | // the previously recorded event of its type. Otherwise discard the current 162 | // event. 163 | // 164 | if ((VHz[0] < 0) && (VHz[2] > 0)) { 165 | if (!hasRise || 166 | ((riseTime < queryTime) == (eventTime < queryTime) && 167 | fabs(riseTime - queryTime) > fabs(eventTime - queryTime)) || 168 | ((riseTime < queryTime) != (eventTime < queryTime) && 169 | (hasSet && 170 | (riseTime < queryTime) == (setTime < queryTime)))) { 171 | riseTime = eventTime; 172 | riseAz = az; 173 | hasRise = true; 174 | } 175 | } 176 | if ((VHz[0] > 0) && (VHz[2] < 0)) { 177 | if (!hasSet || 178 | ((setTime < queryTime) == (eventTime < queryTime) && 179 | fabs(setTime - queryTime) > fabs(eventTime - queryTime)) || 180 | ((setTime < queryTime) != (eventTime < queryTime) && 181 | (hasRise && 182 | (setTime < queryTime) == (riseTime < queryTime)))) { 183 | setTime = eventTime; 184 | setAz = az; 185 | hasSet = true; 186 | } 187 | } 188 | 189 | noevent: 190 | // There are obscure cases in the polar regions that require extra logic. 191 | if (!hasRise && !hasSet) 192 | isVisible = !signbit(VHz[2]); 193 | else if (hasRise && !hasSet) 194 | isVisible = (queryTime > riseTime); 195 | else if (!hasRise && hasSet) 196 | isVisible = (queryTime < setTime); 197 | else 198 | isVisible = ((riseTime < setTime && riseTime < queryTime && setTime > queryTime) || 199 | (riseTime > setTime && (riseTime < queryTime || setTime > queryTime))); 200 | 201 | return; 202 | } 203 | 204 | // Sun position using fundamental arguments 205 | // (Van Flandern & Pulkkinen, 1979) 206 | skyCoordinates 207 | SunRise::sun(double dayOffset) { 208 | double centuryOffset = dayOffset / 36525 + 1; // Centuries from 1900.0 209 | 210 | double l = 0.779072 + 0.00273790931 * dayOffset; 211 | double g = 0.993126 + 0.00273777850 * dayOffset; 212 | 213 | l = 2 * M_PI * (l - floor(l)); 214 | g = 2 * M_PI * (g - floor(g)); 215 | 216 | double v, u, w; 217 | v = 0.39785 * sin(l) 218 | - 0.01000 * sin(l - g) 219 | + 0.00333 * sin(l + g) 220 | - 0.00021 * centuryOffset * sin(l); 221 | 222 | u = 1 223 | - 0.03349 * cos(g) 224 | - 0.00014 * cos(2*l) 225 | + 0.00008 * cos(l); 226 | 227 | w = -0.00010 228 | - 0.04129 * sin(2*l) 229 | + 0.03211 * sin(g) 230 | + 0.00104 * sin(2*l - g) 231 | - 0.00035 * sin(2*l + g) 232 | - 0.00008 * centuryOffset * sin(g); 233 | 234 | double s; 235 | skyCoordinates sc; 236 | s = w / sqrt(u - v*v); // Right ascension 237 | sc.RA = l + atan(s / sqrt(1 - s*s)); 238 | 239 | s = v / sqrt(u); // Declination 240 | sc.declination = atan(s / sqrt(1 - s*s)); 241 | return(sc); 242 | } 243 | 244 | // 3-point interpolation 245 | double 246 | SunRise::interpolate(double f0, double f1, double f2, double p) { 247 | double a = f1 - f0; 248 | double b = f2 - f1 - a; 249 | return(f0 + p * (2*a + b * (2*p - 1))); 250 | } 251 | 252 | // Determine Julian date from Unix time. 253 | // Provides marginally accurate results with Arduino 4-byte double. 254 | double 255 | SunRise::julianDate(time_t t) { 256 | return (t / 86400.0L + 2440587.5); 257 | } 258 | 259 | #if __ISO_C_VISIBLE < 1999 260 | // Arduino compiler is missing this function as of 6/2020. 261 | // 262 | // The Arduino ATmega platforms (including the Uno) are also missing rint(). 263 | // This can be worked around by inserting "#define rint(x) (double)lrint(x)" here, 264 | // but since these platforms use only four bytes for double precision - which is 265 | // insufficient for the correct performance of the required calculations - you 266 | // should instead upgrade to an Arduino Due or better. 267 | // 268 | #define remainder(x, y) ((double)((double)x - (double)y * rint((double)x / (double)y))) 269 | 270 | // double remainder(double x, double y) { 271 | // return(x - (y * rint(x / y))); 272 | // } 273 | #endif 274 | 275 | // Local Sidereal Time 276 | // Provides local sidereal time in degrees, requires longitude in degrees 277 | // and time in fractional Julian days since Jan 1, 2000, 1200UTC (e.g. the 278 | // Julian date - 2451545). 279 | // cf. USNO Astronomical Almanac and 280 | // https://astronomy.stackexchange.com/questions/24859/local-sidereal-time 281 | double 282 | SunRise::localSiderealTime(double offsetDays, double longitude) { 283 | double lSideTime = (15.0L * (6.697374558L + 0.06570982441908L * offsetDays + 284 | remainder(offsetDays, 1) * 24 + 12 + 285 | 0.000026 * (offsetDays / 36525) * (offsetDays / 36525)) 286 | + longitude) / 360; 287 | lSideTime -= floor(lSideTime); 288 | lSideTime *= 360; // Convert to degrees. 289 | return(lSideTime); 290 | } 291 | 292 | // Class initialization. 293 | void 294 | SunRise::initClass() { 295 | queryTime = 0; 296 | riseTime = 0; 297 | setTime = 0; 298 | riseAz = 0; 299 | setAz = 0; 300 | hasRise = false; 301 | hasSet = false; 302 | isVisible = false; 303 | } 304 | -------------------------------------------------------------------------------- /src/libraries/SunRise.h: -------------------------------------------------------------------------------- 1 | #ifndef SunRise_h 2 | #define SunRise_h 3 | 4 | #include 5 | 6 | // Size of event search window in hours. 7 | // Events further away from the search time than MR_WINDOW/2 will not be 8 | // found. At higher latitudes the sun rise/set intervals become larger, so if 9 | // you want to find the nearest events this will need to increase. Larger 10 | // windows will increase interpolation error. Useful values are probably from 11 | // 12 - 48 but will depend upon your application. 12 | 13 | #define SR_WINDOW 48 // Even integer 14 | 15 | class SunRise { 16 | public: 17 | time_t queryTime; 18 | time_t riseTime; 19 | time_t setTime; 20 | float riseAz; 21 | float setAz; 22 | bool hasRise; 23 | bool hasSet; 24 | bool isVisible; 25 | 26 | void calculate(double latitude, double longitude, time_t t); 27 | 28 | private: 29 | void testSunRiseSet(int i, double offsetDays, double latitude, double longitude, 30 | struct skyCoordinates *mp); 31 | struct skyCoordinates sun(double dayOffset); 32 | double interpolate(double f0, double f1, double f2, double p); 33 | double julianDate(time_t t); 34 | double localSiderealTime(double offsetDays, double longitude); 35 | void initClass(); 36 | }; 37 | #endif 38 | -------------------------------------------------------------------------------- /src/libraries/ctpl_stl.h: -------------------------------------------------------------------------------- 1 | /********************************************************* 2 | * 3 | * Copyright (C) 2014 by Vitaliy Vitsentiy 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | *********************************************************/ 18 | 19 | #ifndef __ctpl_stl_thread_pool_H__ 20 | #define __ctpl_stl_thread_pool_H__ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | // thread pool to run user's functors with signature 32 | // ret func(int id, other_params) 33 | // where id is the index of the thread that runs the functor 34 | // ret is some return type 35 | 36 | namespace ctpl { 37 | 38 | namespace detail { 39 | template class Queue { 40 | public: 41 | bool push(T const &value) { 42 | std::unique_lock lock(this->mutex); 43 | this->q.push(value); 44 | return true; 45 | } 46 | // deletes the retrieved element, do not use for non integral types 47 | bool pop(T &v) { 48 | std::unique_lock lock(this->mutex); 49 | if (this->q.empty()) 50 | return false; 51 | v = this->q.front(); 52 | this->q.pop(); 53 | return true; 54 | } 55 | bool empty() { 56 | std::unique_lock lock(this->mutex); 57 | return this->q.empty(); 58 | } 59 | 60 | private: 61 | std::queue q; 62 | std::mutex mutex; 63 | }; 64 | } // namespace detail 65 | 66 | class thread_pool { 67 | 68 | public: 69 | thread_pool() { this->init(); } 70 | thread_pool(int nThreads) { 71 | this->init(); 72 | this->resize(nThreads); 73 | } 74 | 75 | // the destructor waits for all the functions in the queue to be finished 76 | ~thread_pool() { this->stop(true); } 77 | 78 | // get the number of running threads in the pool 79 | int size() { return static_cast(this->threads.size()); } 80 | 81 | // number of idle threads 82 | int n_idle() { return this->nWaiting; } 83 | std::thread &get_thread(int i) { return *this->threads[i]; } 84 | 85 | // change the number of threads in the pool 86 | // should be called from one thread, otherwise be careful to not interleave, 87 | // also with this->stop() nThreads must be >= 0 88 | void resize(int nThreads) { 89 | if (!this->isStop && !this->isDone) { 90 | int oldNThreads = static_cast(this->threads.size()); 91 | if (oldNThreads <= nThreads) { // if the number of threads is increased 92 | this->threads.resize(nThreads); 93 | this->flags.resize(nThreads); 94 | 95 | for (int i = oldNThreads; i < nThreads; ++i) { 96 | this->flags[i] = std::make_shared>(false); 97 | this->set_thread(i); 98 | } 99 | } else { // the number of threads is decreased 100 | for (int i = oldNThreads - 1; i >= nThreads; --i) { 101 | *this->flags[i] = true; // this thread will finish 102 | this->threads[i]->detach(); 103 | } 104 | { 105 | // stop the detached threads that were waiting 106 | std::unique_lock lock(this->mutex); 107 | this->cv.notify_all(); 108 | } 109 | this->threads.resize( 110 | nThreads); // safe to delete because the threads are detached 111 | this->flags.resize( 112 | nThreads); // safe to delete because the threads have copies of 113 | // shared_ptr of the flags, not originals 114 | } 115 | } 116 | } 117 | 118 | // empty the queue 119 | void clear_queue() { 120 | std::function *_f; 121 | while (this->q.pop(_f)) 122 | delete _f; // empty the queue 123 | } 124 | 125 | // pops a functional wrapper to the original function 126 | std::function pop() { 127 | std::function *_f = nullptr; 128 | this->q.pop(_f); 129 | std::unique_ptr> func( 130 | _f); // at return, delete the function even if an exception occurred 131 | std::function f; 132 | if (_f) 133 | f = *_f; 134 | return f; 135 | } 136 | 137 | // wait for all computing threads to finish and stop all threads 138 | // may be called asynchronously to not pause the calling thread while waiting 139 | // if isWait == true, all the functions in the queue are run, otherwise the 140 | // queue is cleared without running the functions 141 | void stop(bool isWait = false) { 142 | if (!isWait) { 143 | if (this->isStop) 144 | return; 145 | this->isStop = true; 146 | for (int i = 0, n = this->size(); i < n; ++i) { 147 | *this->flags[i] = true; // command the threads to stop 148 | } 149 | this->clear_queue(); // empty the queue 150 | } else { 151 | if (this->isDone || this->isStop) 152 | return; 153 | this->isDone = true; // give the waiting threads a command to finish 154 | } 155 | { 156 | std::unique_lock lock(this->mutex); 157 | this->cv.notify_all(); // stop all waiting threads 158 | } 159 | for (int i = 0; i < static_cast(this->threads.size()); 160 | ++i) { // wait for the computing threads to finish 161 | if (this->threads[i]->joinable()) 162 | this->threads[i]->join(); 163 | } 164 | // if there were no threads in the pool but some functors in the queue, the 165 | // functors are not deleted by the threads therefore delete them here 166 | this->clear_queue(); 167 | this->threads.clear(); 168 | this->flags.clear(); 169 | } 170 | 171 | template 172 | auto push(F &&f, Rest &&...rest) -> std::future { 173 | auto pck = 174 | std::make_shared>( 175 | std::bind(std::forward(f), std::placeholders::_1, 176 | std::forward(rest)...)); 177 | auto _f = new std::function([pck](int id) { (*pck)(id); }); 178 | this->q.push(_f); 179 | std::unique_lock lock(this->mutex); 180 | this->cv.notify_one(); 181 | return pck->get_future(); 182 | } 183 | 184 | // run the user's function that excepts argument int - id of the running 185 | // thread. returned value is templatized operator returns std::future, where 186 | // the user can get the result and rethrow the catched exceptins 187 | template auto push(F &&f) -> std::future { 188 | auto pck = std::make_shared>( 189 | std::forward(f)); 190 | auto _f = new std::function([pck](int id) { (*pck)(id); }); 191 | this->q.push(_f); 192 | std::unique_lock lock(this->mutex); 193 | this->cv.notify_one(); 194 | return pck->get_future(); 195 | } 196 | 197 | private: 198 | // deleted 199 | thread_pool(const thread_pool &); // = delete; 200 | thread_pool(thread_pool &&); // = delete; 201 | thread_pool &operator=(const thread_pool &); // = delete; 202 | thread_pool &operator=(thread_pool &&); // = delete; 203 | 204 | void set_thread(int i) { 205 | std::shared_ptr> flag( 206 | this->flags[i]); // a copy of the shared ptr to the flag 207 | auto f = [this, i, flag /* a copy of the shared ptr to the flag */]() { 208 | std::atomic &_flag = *flag; 209 | std::function *_f; 210 | bool isPop = this->q.pop(_f); 211 | while (true) { 212 | while (isPop) { // if there is anything in the queue 213 | std::unique_ptr> func( 214 | _f); // at return, delete the function even if an exception 215 | // occurred 216 | (*_f)(i); 217 | if (_flag) 218 | return; // the thread is wanted to stop, return even if the queue is 219 | // not empty yet 220 | else 221 | isPop = this->q.pop(_f); 222 | } 223 | // the queue is empty here, wait for the next command 224 | std::unique_lock lock(this->mutex); 225 | ++this->nWaiting; 226 | this->cv.wait(lock, [this, &_f, &isPop, &_flag]() { 227 | isPop = this->q.pop(_f); 228 | return isPop || this->isDone || _flag; 229 | }); 230 | --this->nWaiting; 231 | if (!isPop) 232 | return; // if the queue is empty and this->isDone == true or *flag 233 | // then return 234 | } 235 | }; 236 | this->threads[i].reset( 237 | new std::thread(f)); // compiler may not support std::make_unique() 238 | } 239 | 240 | void init() { 241 | this->nWaiting = 0; 242 | this->isStop = false; 243 | this->isDone = false; 244 | } 245 | 246 | std::vector> threads; 247 | std::vector>> flags; 248 | detail::Queue *> q; 249 | std::atomic isDone; 250 | std::atomic isStop; 251 | std::atomic nWaiting; // how many threads are waiting 252 | 253 | std::mutex mutex; 254 | std::condition_variable cv; 255 | }; 256 | 257 | } // namespace ctpl 258 | 259 | #endif // __ctpl_stl_thread_pool_H__ 260 | -------------------------------------------------------------------------------- /src/license.cpp: -------------------------------------------------------------------------------- 1 | #include "headers/license.h" 2 | #include "ui/ui_license.h" 3 | 4 | #include 5 | #include 6 | 7 | License::License(QWidget *parent) : 8 | QDialog(parent), 9 | ui(new Ui::License) 10 | { 11 | QFile license(":/resources/license.txt"); 12 | license.open(QFile::ReadOnly); 13 | QString licenseContent = QString::fromUtf8(license.readAll()); 14 | ui->setupUi(this); 15 | ui->licenseField->setPlainText(licenseContent); 16 | } 17 | 18 | License::~License() 19 | { 20 | delete ui; 21 | } 22 | 23 | void License::on_closeBtn_clicked() 24 | { 25 | this->setVisible(0); 26 | } 27 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "headers/mainwindow.h" 2 | #include "headers/utils.h" 3 | #include "headers/dbusinterface.h" 4 | #include "headers/runguard.h" 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | int main(int argc, char *argv[]) 11 | { 12 | RunGuard rg("koiDummyNetwork"); 13 | if (!rg.tryToRun()) 14 | { 15 | std::cout << "Another instance of Koi is already running" << std::endl; 16 | return 1; 17 | } 18 | 19 | Utils utils; 20 | utils.initialiseSettings(); 21 | QApplication a(argc, argv); 22 | MainWindow w; 23 | if (utils.settings->value("start-hidden").toBool() == 0) 24 | { 25 | w.show(); 26 | } 27 | KoiDbusInterface dbusIf(&a); 28 | return a.exec(); 29 | } 30 | -------------------------------------------------------------------------------- /src/mainwindow.cpp: -------------------------------------------------------------------------------- 1 | #include "headers/mainwindow.h" 2 | #include "headers/about.h" 3 | #include "libraries/Scheduler.h" 4 | #include "libraries/SunRise.h" 5 | #include "ui/ui_mainwindow.h" 6 | 7 | MainWindow::MainWindow(QWidget *parent) 8 | : QMainWindow(parent), ui(new Ui::MainWindow) { 9 | trayIcon = new QSystemTrayIcon(this); 10 | QIcon icon = 11 | QIcon::fromTheme("koi_tray", QIcon(":/resources/icons/koi_tray.png")); 12 | this->trayIcon->setIcon(icon); 13 | this->trayIcon->setVisible(true); 14 | trayMenu = this->createMenu(); 15 | this->trayIcon->setContextMenu(trayMenu); // Set tray context menu 16 | connect(trayIcon, &QSystemTrayIcon::activated, this, 17 | &MainWindow::iconActivated); // System tray interaction 18 | utils.initialiseSettings(); 19 | ui->setupUi(this); 20 | ui->mainStack->setCurrentIndex(0); // Always start window on main view 21 | refreshDirs(); 22 | loadPrefs(); // Load prefs on startup 23 | 24 | if (utils.settings->value("schedule").toBool()) { 25 | scheduler = std::make_unique(2); 26 | if (utils.settings->value("schedule-type").toString() == 27 | "time") { // Scheduled switch 28 | utils.startupTimeCheck(); // Switch themes on startup 29 | scheduleLight(*scheduler); 30 | scheduleDark(*scheduler); 31 | } else { // Auto sun switch 32 | utils.startupSunCheck(); // Switch themes on startup 33 | scheduleSunEvent(*scheduler); 34 | } 35 | } 36 | ui->resMsg->hide(); 37 | auto actionRes = new QAction("Restart", this); 38 | actionRes->setIcon(QIcon::fromTheme("view-refresh")); 39 | connect(actionRes, &QAction::triggered, this, 40 | &MainWindow::on_actionRestart_triggered); 41 | ui->resMsg->addAction(actionRes); 42 | } 43 | MainWindow::~MainWindow() { 44 | this->setVisible(0); 45 | delete ui; 46 | } 47 | 48 | // Override window managing events 49 | void MainWindow::closeEvent(QCloseEvent *event) { // Overide close event 50 | event->ignore(); 51 | toggleVisibility(); 52 | } 53 | 54 | // SysTray related functionality 55 | QMenu *MainWindow::createMenu() // Define context menu items for SysTray - 56 | // R-click to show context menu 57 | { 58 | // Tray action menu 59 | auto actionMenuQuit = new QAction("&Quit", this); // Quit app 60 | connect(actionMenuQuit, &QAction::triggered, this, &QCoreApplication::quit); 61 | auto actionMenuLight = new QAction("&Light", this); // Switch to light 62 | connect(actionMenuLight, &QAction::triggered, this, 63 | &MainWindow::on_lightBtn_clicked); // Doesn't work. 64 | auto actionMenuDark = new QAction("&Dark", this); // Switch to dark 65 | connect(actionMenuDark, &QAction::triggered, this, 66 | &MainWindow::on_darkBtn_clicked); // Doesn't work. 67 | auto actionMenuToggle = new QAction("&Toggle Window", this); 68 | connect(actionMenuToggle, &QAction::triggered, this, 69 | &MainWindow::toggleVisibility); 70 | 71 | // Build tray items 72 | auto trayMenu = new QMenu(this); 73 | trayMenu->addAction(actionMenuToggle); 74 | trayMenu->addAction(actionMenuLight); 75 | trayMenu->addAction(actionMenuDark); 76 | trayMenu->addAction(actionMenuQuit); 77 | return trayMenu; 78 | } 79 | void MainWindow::iconActivated( 80 | QSystemTrayIcon::ActivationReason 81 | reason) // Define actions for SysTray L&M-click 82 | { 83 | switch (reason) { 84 | case QSystemTrayIcon::Trigger: // Left-click to toggle window visibility 85 | toggleVisibility(); 86 | break; 87 | 88 | // case QSystemTrayIcon::MiddleClick: // Middle-click to toggle between 89 | // light and dark 90 | // utils.notify("Hello!", "You middle-clicked me", 0); // Must implement 91 | // toggle break; 92 | case QSystemTrayIcon::MiddleClick: // Middle-click to toggle between light and 93 | // dark 94 | utils.toggle(); 95 | break; 96 | 97 | // Must understand tray better - Why can't right click be part of switch 98 | // statement? 99 | 100 | default: // Need to understand switch statements better - Why is this 101 | // required? 102 | break; 103 | } 104 | } 105 | 106 | // Independent functions 107 | void MainWindow::loadPrefs() { 108 | // Load notify prefs 109 | if (utils.settings->value("notify", true).toBool()) { 110 | ui->notifyCheckBox->setChecked(true); 111 | } else { 112 | ui->notifyCheckBox->setChecked(false); 113 | } 114 | // Load startup prefs 115 | if (utils.settings->value("start-hidden").toBool()) { 116 | ui->hiddenCheckBox->setChecked(true); 117 | } else { 118 | ui->hiddenCheckBox->setChecked(false); 119 | } 120 | 121 | // Load scheduling prefs 122 | if (utils.settings->value("schedule").toBool()) { 123 | ui->autoCheckBox->setChecked(1); 124 | } else { 125 | ui->autoCheckBox->setChecked(0); 126 | } 127 | if (utils.settings->value("schedule-type") == "time") { 128 | ui->scheduleRadioBtn->setChecked(1); 129 | ui->lightTimeLabel->setVisible(true); 130 | ui->darkTimeLabel->setVisible(true); 131 | ui->lightTimeEdit->setVisible(true); 132 | ui->darkTimeEdit->setVisible(true); 133 | ui->latitudeLabel->setVisible(false); 134 | ui->longitudeLabel->setVisible(false); 135 | ui->latitudeDSB->setVisible(false); 136 | ui->longitudeDSB->setVisible(false); 137 | } else { 138 | ui->sunRadioBtn->setChecked(1); 139 | ui->lightTimeLabel->setVisible(false); 140 | ui->darkTimeLabel->setVisible(false); 141 | ui->lightTimeEdit->setVisible(false); 142 | ui->darkTimeEdit->setVisible(false); 143 | ui->latitudeLabel->setVisible(true); 144 | ui->longitudeLabel->setVisible(true); 145 | ui->latitudeDSB->setVisible(true); 146 | ui->longitudeDSB->setVisible(true); 147 | } 148 | 149 | ui->lightTimeEdit->setTime(utils.settings->value("time-light").toTime()); 150 | ui->darkTimeEdit->setTime(utils.settings->value("time-dark").toTime()); 151 | 152 | ui->latitudeDSB->setValue(utils.settings->value("latitude").toDouble()); 153 | ui->longitudeDSB->setValue(utils.settings->value("longitude").toDouble()); 154 | 155 | // Load Plasma style prefs 156 | if (utils.settings->value("PlasmaStyle/enabled").toBool()) { 157 | ui->styleCheckBox->setChecked(true); 158 | } else { 159 | ui->styleCheckBox->setChecked(false); 160 | } 161 | ui->lightDropStyle->setCurrentText( 162 | utils.settings->value("PlasmaStyle/light").toString()); 163 | ui->darkDropStyle->setCurrentText( 164 | utils.settings->value("PlasmaStyle/dark").toString()); 165 | 166 | // Load color scheme prefs 167 | if (utils.settings->value("ColorScheme/enabled").toBool()) { 168 | ui->colorCheckBox->setChecked(true); 169 | } else { 170 | ui->colorCheckBox->setChecked(false); 171 | } 172 | QFileInfo lightColorsPref( 173 | utils.settings->value("ColorScheme/light").toString()); 174 | QFileInfo darkColorsPref( 175 | utils.settings->value("ColorScheme/dark").toString()); 176 | QString lightColorsPrefString = lightColorsPref.baseName(); 177 | QString darkColorsPrefString = darkColorsPref.baseName(); 178 | ui->lightDropColor->setCurrentText(lightColorsPrefString); 179 | ui->darkDropColor->setCurrentText(darkColorsPrefString); 180 | 181 | // Load icon theme prefs 182 | if (utils.settings->value("IconTheme/enabled").toBool()) { 183 | ui->iconCheckBox->setChecked(true); 184 | } else { 185 | ui->iconCheckBox->setChecked(false); 186 | } 187 | ui->lightDropIcon->setCurrentText( 188 | utils.settings->value("IconTheme/light").toString()); 189 | ui->darkDropIcon->setCurrentText( 190 | utils.settings->value("IconTheme/dark").toString()); 191 | 192 | // Load GTK Theme prefs 193 | if (utils.settings->value("GTKTheme/enabled").toBool()) { 194 | ui->gtkCheckBox->setChecked(true); 195 | } else { 196 | ui->gtkCheckBox->setChecked(false); 197 | } 198 | ui->lightDropGtk->setCurrentText( 199 | utils.settings->value("GTKTheme/light").toString()); 200 | ui->darkDropGtk->setCurrentText( 201 | utils.settings->value("GTKTheme/dark").toString()); 202 | 203 | // Load Kvantum Style theme prefs 204 | if (utils.settings->value("KvantumStyle/enabled").toBool()) { 205 | ui->kvantumStyleCheckBox->setChecked(true); 206 | } else { 207 | ui->kvantumStyleCheckBox->setChecked(false); 208 | } 209 | // sets the displayed text on the combo box of the kvantum style. 210 | ui->lightDropKvantumStyle->setCurrentText( 211 | utils.settings->value("KvantumStyle/light").toString()); 212 | ui->darkDropKvantumStyle->setCurrentText( 213 | utils.settings->value("KvantumStyle/dark").toString()); 214 | 215 | // Load Wallpaper prefs 216 | if (utils.settings->value("Wallpaper/enabled").toBool()) { 217 | ui->wallCheckBox->setChecked(true); 218 | } else { 219 | ui->wallCheckBox->setChecked(false); 220 | } 221 | QFileInfo lw(utils.settings->value("Wallpaper/light").toString()); 222 | QString lightWallBtnText = lw.fileName(); 223 | if (lightWall.isEmpty()) { 224 | ui->lightWallBtn->setText("Select..."); 225 | } else { 226 | ui->lightWallBtn->setText(lightWallBtnText); 227 | } 228 | QFileInfo dw(utils.settings->value("Wallpaper/dark").toString()); 229 | QString darkWallBtnText = dw.fileName(); 230 | if (darkWall.isEmpty()) { 231 | ui->darkWallBtn->setText("Select..."); 232 | } else { 233 | ui->darkWallBtn->setText(darkWallBtnText); 234 | } 235 | 236 | // Load Script prefs 237 | if (utils.settings->value("Script/enabled").toBool()) { 238 | ui->scriptCheckBox->setChecked(true); 239 | } else { 240 | ui->scriptCheckBox->setChecked(false); 241 | } 242 | QFileInfo ls(utils.settings->value("Script/light").toString()); 243 | QString lightScriptBtnText = ls.fileName(); 244 | if (lightScript.isEmpty()) { 245 | ui->lightScriptBtn->setText("Select..."); 246 | } else { 247 | ui->lightScriptBtn->setText(lightScriptBtnText); 248 | } 249 | QFileInfo ds(utils.settings->value("Script/dark").toString()); 250 | QString darkScriptBtnText = ds.fileName(); 251 | if (darkScript.isEmpty()) { 252 | ui->darkScriptBtn->setText("Select..."); 253 | } else { 254 | ui->darkScriptBtn->setText(darkScriptBtnText); 255 | } 256 | } 257 | 258 | void MainWindow::savePrefs() { 259 | // Plasma Style enabling 260 | if (ui->styleCheckBox->isChecked() == 0) { 261 | utils.settings->setValue("PlasmaStyle/enabled", false); 262 | } else { 263 | utils.settings->setValue("PlasmaStyle/enabled", true); 264 | } 265 | // Plasma Style saving prefs 266 | utils.settings->setValue("PlasmaStyle/light", lightStyle); 267 | utils.settings->setValue("PlasmaStyle/dark", darkStyle); 268 | 269 | // Color scheme enabling 270 | if (ui->colorCheckBox->isChecked() == 0) { 271 | utils.settings->setValue("ColorScheme/enabled", false); 272 | } else { 273 | utils.settings->setValue("ColorScheme/enabled", true); 274 | } 275 | // Color scheme saving prefs 276 | utils.settings->setValue("ColorScheme/light", lightColor); 277 | utils.settings->setValue("ColorScheme/dark", darkColor); 278 | 279 | // Icon theme enabling 280 | if (ui->iconCheckBox->checkState() == 0) { 281 | utils.settings->setValue("IconTheme/enabled", false); 282 | } else { 283 | utils.settings->setValue("IconTheme/enabled", true); 284 | } 285 | // Icon theme saving prefs 286 | utils.settings->setValue("IconTheme/light", lightIcon); 287 | utils.settings->setValue("IconTheme/dark", darkIcon); 288 | 289 | // GTK theme enabling 290 | if (ui->gtkCheckBox->isChecked() == 0) { 291 | utils.settings->setValue("GTKTheme/enabled", false); 292 | } else { 293 | utils.settings->setValue("GTKTheme/enabled", true); 294 | } 295 | // GTK theme saving prefs 296 | utils.settings->setValue("GTKTheme/light", lightGtk); 297 | utils.settings->setValue("GTKTheme/dark", darkGtk); 298 | 299 | // Kvantum Style enabling 300 | if (ui->kvantumStyleCheckBox->isChecked() == 0) { 301 | utils.settings->setValue("KvantumStyle/enabled", false); 302 | } else { 303 | utils.settings->setValue("KvantumStyle/enabled", true); 304 | } 305 | // Kvantum Style Theme saving Prefs 306 | utils.settings->setValue("KvantumStyle/light", lightKvantumStyle); 307 | utils.settings->setValue("KvantumStyle/dark", darkKvantumStyle); 308 | 309 | // Wallpaper enabling 310 | if (ui->wallCheckBox->isChecked() == 0) { 311 | utils.settings->setValue("Wallpaper/enabled", false); 312 | } else { 313 | utils.settings->setValue("Wallpaper/enabled", true); 314 | } 315 | // Wallpaper saving prefs 316 | utils.settings->setValue("Wallpaper/light", lightWall); 317 | utils.settings->setValue("Wallpaper/dark", darkWall); 318 | utils.settings->sync(); 319 | 320 | // Script enabling 321 | if (ui->scriptCheckBox->isChecked() == 0) { 322 | utils.settings->setValue("Script/enabled", false); 323 | } else { 324 | utils.settings->setValue("Script/enabled", true); 325 | } 326 | // Script saving prefs 327 | utils.settings->setValue("Script/light", lightScript); 328 | utils.settings->setValue("Script/dark", darkScript); 329 | utils.settings->sync(); 330 | } 331 | void MainWindow::refreshDirs() // Refresh function to find new themes 332 | { 333 | // Refresh plasma styles 334 | QStringList plasmaStyles = utils.getPlasmaStyles(); 335 | ui->lightDropStyle->clear(); 336 | ui->lightDropStyle->addItems(plasmaStyles); 337 | ui->darkDropStyle->clear(); 338 | ui->darkDropStyle->addItems(plasmaStyles); 339 | // Refresh color schemes 340 | QStringList colorSchemes = utils.getColorSchemes(); 341 | ui->lightDropColor->clear(); 342 | ui->lightDropColor->addItems(colorSchemes); 343 | ui->darkDropColor->clear(); 344 | ui->darkDropColor->addItems(colorSchemes); 345 | // Refresh icon themes 346 | QStringList iconThemes = utils.getIconThemes(); 347 | ui->lightDropIcon->clear(); 348 | ui->lightDropIcon->addItems(iconThemes); 349 | ui->darkDropIcon->clear(); 350 | ui->darkDropIcon->addItems(iconThemes); 351 | // Refresh gtk themes 352 | QStringList gtkThemes = utils.getGtkThemes(); 353 | ui->lightDropGtk->clear(); 354 | ui->lightDropGtk->addItems(gtkThemes); 355 | ui->darkDropGtk->clear(); 356 | ui->darkDropGtk->addItems(gtkThemes); 357 | // Refresh Kvantum Style themes. 358 | QStringList kvantumStyle = utils.getKvantumStyles(); 359 | ui->lightDropKvantumStyle 360 | ->clear(); // clears everything from the kvantum drop down menu 361 | ui->darkDropKvantumStyle->clear(); 362 | ui->lightDropKvantumStyle->addItems( 363 | kvantumStyle); // adds the new loaded kvantum styles 364 | ui->darkDropKvantumStyle->addItems(kvantumStyle); 365 | loadPrefs(); 366 | } 367 | void MainWindow::toggleVisibility() { 368 | if (this->isVisible() == 0) { 369 | this->setVisible(1); 370 | this->activateWindow(); 371 | } else { 372 | this->setVisible(0); 373 | } 374 | } 375 | int MainWindow::prefsSaved() // Lots of ifs, don't know how to do it any other 376 | // way. Maybe an array? 377 | { 378 | if (ui->styleCheckBox->isChecked() != 379 | utils.settings->value("PlasmaStyle/enabled").toBool()) { 380 | return 0; 381 | } 382 | if (lightStyle != utils.settings->value("PlasmaStyle/light").toString()) { 383 | return 0; 384 | } 385 | if (darkStyle != utils.settings->value("PlasmaStyle/dark").toString()) { 386 | return 0; 387 | } 388 | if (ui->colorCheckBox->isChecked() != 389 | utils.settings->value("ColorScheme/enabled").toBool()) { 390 | return 0; 391 | } 392 | if (lightColor != utils.settings->value("ColorScheme/light").toString()) { 393 | return 0; 394 | } 395 | if (darkColor != utils.settings->value("ColorScheme/dark").toString()) { 396 | return 0; 397 | } 398 | if (ui->iconCheckBox->isChecked() != 399 | utils.settings->value("IconTheme/enabled").toBool()) { 400 | return 0; 401 | } 402 | if (lightIcon != utils.settings->value("IconTheme/light").toString()) { 403 | return 0; 404 | } 405 | if (darkIcon != utils.settings->value("IconTheme/dark").toString()) { 406 | return 0; 407 | } 408 | if (ui->gtkCheckBox->isChecked() != 409 | utils.settings->value("GTKTheme/enabled").toBool()) { 410 | return 0; 411 | } 412 | if (lightGtk != utils.settings->value("GTKTheme/light").toString()) { 413 | return 0; 414 | } 415 | if (darkGtk != utils.settings->value("GTKTheme/dark").toString()) { 416 | return 0; 417 | } 418 | /* 419 | for kvantum style to make sure the settings is applied upon exit 420 | will try to change it to nested if statement as the drop down box is 421 | dependent on whether the check box is clicked 422 | */ 423 | if (ui->kvantumStyleCheckBox->isChecked() != 424 | utils.settings->value("KvantumStyle/enabled").toBool()) { 425 | return 0; 426 | } 427 | if (lightKvantumStyle != 428 | utils.settings->value("KvantumStyle/light").toString()) { 429 | return 0; 430 | } 431 | if (darkKvantumStyle != 432 | utils.settings->value("KvantumStyle/dark").toString()) { 433 | return 0; 434 | } 435 | if (ui->wallCheckBox->isChecked() != 436 | utils.settings->value("Wallpaper/enabled").toBool()) { 437 | return 0; 438 | } 439 | if (lightWall != utils.settings->value("Wallpaper/light").toString()) { 440 | return 0; 441 | } 442 | if (darkWall != utils.settings->value("Wallpaper/dark").toString()) { 443 | return 0; 444 | } 445 | if (ui->scriptCheckBox->isChecked() != 446 | utils.settings->value("Script/enabled").toBool()) { 447 | return 0; 448 | } 449 | if (lightScript != utils.settings->value("Script/light").toString()) { 450 | return 0; 451 | } 452 | if (darkScript != utils.settings->value("Script/dark").toString()) { 453 | return 0; 454 | } 455 | return 1; 456 | } 457 | void MainWindow::scheduleLight(Bosma::Scheduler& s) { 458 | int lightCronMin = 459 | QTime::fromString(utils.settings->value("time-light").toString()) 460 | .minute(); 461 | int lightCronHr = 462 | QTime::fromString(utils.settings->value("time-light").toString()).hour(); 463 | if (lightCronMin <= 0) { 464 | lightCronMin = 0; 465 | } 466 | if (lightCronHr <= 0) { 467 | lightCronHr = 0; 468 | } 469 | std::string lightCron = std::to_string(lightCronMin) + " " + 470 | std::to_string(lightCronHr) + " * * *"; 471 | s.cron(lightCron, [this]() { utils.goLight(); }); 472 | } 473 | void MainWindow::scheduleDark(Bosma::Scheduler& s) { 474 | int darkCronMin = 475 | QTime::fromString(utils.settings->value("time-dark").toString()).minute(); 476 | int darkCronHr = 477 | QTime::fromString(utils.settings->value("time-dark").toString()).hour(); 478 | if (darkCronMin <= 0) { 479 | darkCronMin = 0; 480 | } 481 | if (darkCronHr <= 0) { 482 | darkCronHr = 0; 483 | } 484 | std::string darkCron = 485 | std::to_string(darkCronMin) + " " + std::to_string(darkCronHr) + " * * *"; 486 | s.cron(darkCron, [this]() { utils.goDark(); }); 487 | } 488 | 489 | void MainWindow::scheduleSunEvent(Bosma::Scheduler& s) { 490 | // Schedules a theme change for the next sunrise or sunfall 491 | double latitude = utils.settings->value("latitude").toDouble(); 492 | double longitude = utils.settings->value("longitude").toDouble(); 493 | 494 | time_t t = time(NULL); 495 | 496 | SunRise sr; 497 | sr.calculate(latitude, longitude, t); 498 | 499 | char buffer[20]; 500 | struct tm *timeinfo; 501 | 502 | if ((!sr.hasRise || (sr.hasRise && sr.riseTime < sr.queryTime)) && 503 | (!sr.hasSet || (sr.hasSet && sr.setTime < sr.queryTime))) { 504 | // No events found in the next SR_WINDOW/2 hours, check again later - may 505 | // happen in polar regions 506 | s.in(std::chrono::hours(SR_WINDOW / 2), [this, &s]() { scheduleSunEvent(s); }); 507 | } else if (sr.hasRise && sr.riseTime > sr.queryTime) { 508 | timeinfo = localtime(&sr.riseTime); 509 | strftime(buffer, 20, "%Y-%m-%d %H:%M:%S", timeinfo); 510 | // puts("Scheduling Light Theme for:"); 511 | // puts(buffer); 512 | std::string sunEventCron = buffer; 513 | s.at(sunEventCron, [this, &s]() { 514 | utils.goLight(); 515 | scheduleSunEvent(s); 516 | }); 517 | } else if (sr.hasSet && sr.setTime > sr.queryTime) { 518 | timeinfo = localtime(&sr.setTime); 519 | strftime(buffer, 20, "%Y-%m-%d %H:%M:%S", timeinfo); 520 | // puts("Scheduling Dark Theme for:"); 521 | // puts(buffer); 522 | std::string sunEventCron = buffer; 523 | s.at(sunEventCron, [this, &s]() { 524 | utils.goDark(); 525 | scheduleSunEvent(s); 526 | }); 527 | } 528 | } 529 | 530 | // Functionality of buttons - Related to program navigation, interaction and 531 | // saving settings 532 | void MainWindow::on_prefsBtn_clicked() // Preferences button - Sets all 533 | // preferences as found in koirc file 534 | { 535 | lightWall = utils.settings->value("Wallpaper/light").toString(); 536 | darkWall = utils.settings->value("Wallpaper/dark").toString(); 537 | /* 538 | * The two lines above fix a bug where when applying the settings without 539 | * having changed the wallpapers, the wallpaper preferences would be set as 540 | * empty strings, and not stay the same, as is expected behaviour. Unsure why 541 | * this fixes said bug... ¯\_(ツ)_/¯ 542 | */ 543 | lightScript = utils.settings->value("Script/light").toString(); 544 | darkScript = utils.settings->value("Script/dark").toString(); 545 | // Added the above lines in just in case the same bug affects setting the 546 | // script properly 547 | loadPrefs(); 548 | ui->mainStack->setCurrentIndex(1); 549 | } 550 | void MainWindow::on_backBtn_clicked() // Back button in preferences view - Must 551 | // setup cheking if prefs saved 552 | { 553 | if (prefsSaved()) { 554 | ui->mainStack->setCurrentIndex(0); 555 | } else { 556 | QMessageBox applyConfs; // Verify if user wants to save settings 557 | applyConfs.setWindowTitle("Save Settings — Koi"); 558 | applyConfs.setText( 559 | "You have unsaved changes, would you like to save or discard them?"); 560 | applyConfs.setIcon(QMessageBox::Warning); 561 | applyConfs.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | 562 | QMessageBox::Cancel); 563 | applyConfs.setDefaultButton(QMessageBox::Save); 564 | int ret = applyConfs.exec(); 565 | switch (ret) { 566 | case QMessageBox::Save: // Save and change stack 567 | savePrefs(); 568 | ui->mainStack->setCurrentIndex(0); 569 | break; 570 | case QMessageBox::Discard: // Change stack 571 | ui->mainStack->setCurrentIndex(0); 572 | lightWall = utils.settings->value("Wallpaper/light").toString(); 573 | darkWall = utils.settings->value("Wallpaper/dark").toString(); 574 | lightScript = utils.settings->value("Script/light").toString(); 575 | darkScript = utils.settings->value("Script/dark").toString(); 576 | loadPrefs(); 577 | break; 578 | case QMessageBox::Cancel: // Do nothin //probably dont need this case 579 | break; 580 | default: 581 | break; 582 | } 583 | } 584 | } 585 | void MainWindow::on_applyBtn_clicked() { savePrefs(); } 586 | void MainWindow::on_lightBtn_clicked() { utils.goLight(); } 587 | void MainWindow::on_darkBtn_clicked() { utils.goDark(); } 588 | /*void MainWindow::on_refreshBtn_clicked() // Refresh dirs contents 589 | { 590 | loadPrefs(); 591 | refreshDirs(); 592 | }*/ 593 | 594 | // Editing options 595 | void MainWindow::on_styleCheckBox_stateChanged( 596 | int styleEnabled) // Plasma style checkbox logic 597 | { 598 | ui->darkStyle->setEnabled(styleEnabled); 599 | ui->lightStyle->setEnabled(styleEnabled); 600 | ui->darkDropStyle->setEnabled(styleEnabled); 601 | ui->lightDropStyle->setEnabled(styleEnabled); 602 | } 603 | void MainWindow::on_colorCheckBox_stateChanged( 604 | int colorEnabled) // Color scheme checkbox logic 605 | { 606 | ui->darkColor->setEnabled(colorEnabled); 607 | ui->lightColor->setEnabled(colorEnabled); 608 | ui->darkDropColor->setEnabled(colorEnabled); 609 | ui->lightDropColor->setEnabled(colorEnabled); 610 | } 611 | void MainWindow::on_iconCheckBox_stateChanged( 612 | int iconEnabled) // Icon theme checkbox logic 613 | { 614 | ui->darkIcon->setEnabled(iconEnabled); 615 | ui->lightIcon->setEnabled(iconEnabled); 616 | ui->darkDropIcon->setEnabled(iconEnabled); 617 | ui->lightDropIcon->setEnabled(iconEnabled); 618 | } 619 | void MainWindow::on_kvantumStyleCheckBox_stateChanged( 620 | int kvantumStyleEnabled) // kvantum theme checkbox logic used to perform an 621 | // actioni after enabling the check box 622 | { 623 | ui->darkKvantumStyle->setEnabled(kvantumStyleEnabled); 624 | ui->lightDropKvantumStyle->setEnabled(kvantumStyleEnabled); 625 | ui->darkDropKvantumStyle->setEnabled(kvantumStyleEnabled); 626 | ui->darkKvantumStyle->setEnabled(kvantumStyleEnabled); 627 | } 628 | void MainWindow::on_lightDropStyle_currentTextChanged( 629 | const QString &lightStyleUN) // Set light plasma style 630 | { 631 | lightStyle = lightStyleUN; 632 | } 633 | void MainWindow::on_darkDropStyle_currentTextChanged( 634 | const QString &darkStyleUN) // Set dark plasma style 635 | { 636 | darkStyle = darkStyleUN; 637 | } 638 | void MainWindow::on_lightDropColor_currentTextChanged( 639 | const QString &lightColorUN) // Set light color scheme 640 | { 641 | lightColor = lightColorUN; 642 | } 643 | void MainWindow::on_darkDropColor_currentTextChanged( 644 | const QString &darkColorUN) // Set dark color scheme 645 | { 646 | darkColor = darkColorUN; 647 | } 648 | void MainWindow::on_lightDropIcon_currentTextChanged( 649 | const QString &lightIconUN) // Set light icon theme 650 | { 651 | lightIcon = lightIconUN; 652 | } 653 | void MainWindow::on_darkDropIcon_currentTextChanged( 654 | const QString &darkIconUN) // Set dark icon theme 655 | { 656 | darkIcon = darkIconUN; 657 | } 658 | void MainWindow::on_gtkCheckBox_stateChanged( 659 | int gtkEnabled) // GTK theme checkbox logic 660 | { 661 | ui->darkGtk->setEnabled(gtkEnabled); 662 | ui->lightGtk->setEnabled(gtkEnabled); 663 | ui->darkDropGtk->setEnabled(gtkEnabled); 664 | ui->lightDropGtk->setEnabled(gtkEnabled); 665 | } 666 | void MainWindow::on_lightDropGtk_currentTextChanged( 667 | const QString &lightGtkUN) // Set light gtk theme 668 | { 669 | lightGtk = lightGtkUN; 670 | } 671 | void MainWindow::on_darkDropGtk_currentTextChanged( 672 | const QString &darkGtkUN) // Set dark gtk theme 673 | { 674 | darkGtk = darkGtkUN; 675 | } 676 | void MainWindow::on_lightDropKvantumStyle_currentTextChanged( 677 | const QString 678 | &lightKvantumStyleUN) // sets the kvantum style from the drop menu 679 | { 680 | lightKvantumStyle = lightKvantumStyleUN; 681 | } 682 | void MainWindow::on_darkDropKvantumStyle_currentTextChanged( 683 | const QString &darkKvantumStyleUN) { 684 | darkKvantumStyle = darkKvantumStyleUN; 685 | } 686 | void MainWindow::on_wallCheckBox_stateChanged( 687 | int wallEnabled) // Wallpaper checkbox logic 688 | { 689 | ui->darkWall->setEnabled(wallEnabled); 690 | ui->lightWall->setEnabled(wallEnabled); 691 | ui->darkWallBtn->setEnabled(wallEnabled); 692 | ui->lightWallBtn->setEnabled(wallEnabled); 693 | } 694 | void MainWindow::on_lightWallBtn_clicked() // Set light wallpaper 695 | { 696 | lightWall = QFileDialog::getOpenFileName( 697 | this, tr("Select Image"), QDir::homePath() + "/Pictures", 698 | tr("Images(*.png *.jpg *.jpeg *.bmp)")); 699 | QFileInfo lw(lightWall); 700 | QString lightWallName = lw.fileName(); 701 | ui->lightWallBtn->setText(lightWallName); 702 | ui->lightWallBtn->setToolTip(lightWall); 703 | } 704 | void MainWindow::on_darkWallBtn_clicked() // Set dark wallpaper 705 | { 706 | darkWall = QFileDialog::getOpenFileName( 707 | this, tr("Select Image"), QDir::homePath() + "/Pictures", 708 | tr("Images(*.png *.jpg *.jpeg *.bmp)")); 709 | QFileInfo dw(darkWall); 710 | QString darkWallName = dw.fileName(); 711 | ui->darkWallBtn->setText(darkWallName); 712 | ui->darkWallBtn->setToolTip(darkWall); 713 | } 714 | void MainWindow::on_scriptCheckBox_stateChanged( 715 | int scriptEnabled) // Script checkbox logic 716 | { 717 | ui->darkScript->setEnabled(scriptEnabled); 718 | ui->lightScript->setEnabled(scriptEnabled); 719 | ui->darkScriptBtn->setEnabled(scriptEnabled); 720 | ui->lightScriptBtn->setEnabled(scriptEnabled); 721 | } 722 | void MainWindow::on_lightScriptBtn_clicked() // Set light script 723 | { 724 | lightScript = 725 | QFileDialog::getOpenFileName(this, tr("Select Script"), QDir::homePath(), 726 | tr("Bash script files(*.sh)")); 727 | QFileInfo lw(lightScript); 728 | QString lightScriptName = lw.fileName(); 729 | ui->lightScriptBtn->setText(lightScriptName); 730 | ui->lightScriptBtn->setToolTip(lightScript); 731 | } 732 | void MainWindow::on_darkScriptBtn_clicked() // Set dark script 733 | { 734 | darkScript = 735 | QFileDialog::getOpenFileName(this, tr("Select Script"), QDir::homePath(), 736 | tr("Bash script files(*.sh)")); 737 | QFileInfo dw(darkScript); 738 | QString darkScriptName = dw.fileName(); 739 | ui->darkScriptBtn->setText(darkScriptName); 740 | ui->darkScriptBtn->setToolTip(darkScript); 741 | } 742 | void MainWindow::on_autoCheckBox_stateChanged( 743 | int automaticEnabled) // Logic for enabling scheduling of themes 744 | { 745 | ui->scheduleRadioBtn->setEnabled(automaticEnabled); 746 | ui->sunRadioBtn->setEnabled(automaticEnabled); 747 | 748 | ui->lightTimeLabel->setEnabled(automaticEnabled); 749 | ui->darkTimeLabel->setEnabled(automaticEnabled); 750 | ui->lightTimeEdit->setEnabled(automaticEnabled); 751 | ui->darkTimeEdit->setEnabled(automaticEnabled); 752 | ui->latitudeLabel->setEnabled(automaticEnabled); 753 | ui->longitudeLabel->setEnabled(automaticEnabled); 754 | ui->latitudeDSB->setEnabled(automaticEnabled); 755 | ui->longitudeDSB->setEnabled(automaticEnabled); 756 | 757 | utils.settings->setValue("schedule", automaticEnabled); 758 | utils.settings->sync(); 759 | if (automaticEnabled) { 760 | ui->resMsg->setText(tr("To enable automatic mode, Koi must be restarted.")); 761 | } else { 762 | ui->resMsg->setText( 763 | tr("To disable automatic mode, Koi must be restarted.")); 764 | } 765 | ui->resMsg->setMessageType(KMessageWidget::Warning); 766 | ui->resMsg->animatedShow(); 767 | } 768 | void MainWindow::on_scheduleRadioBtn_toggled( 769 | bool scheduleSun) // Toggle between manual schedule, and sun schedule 770 | { 771 | if (scheduleSun) { 772 | scheduleType = "time"; 773 | 774 | ui->lightTimeLabel->setVisible(true); 775 | ui->darkTimeLabel->setVisible(true); 776 | ui->lightTimeEdit->setVisible(true); 777 | ui->darkTimeEdit->setVisible(true); 778 | ui->latitudeLabel->setVisible(false); 779 | ui->longitudeLabel->setVisible(false); 780 | ui->latitudeDSB->setVisible(false); 781 | ui->longitudeDSB->setVisible(false); 782 | 783 | } else { 784 | scheduleType = "sun"; 785 | 786 | ui->lightTimeLabel->setVisible(false); 787 | ui->darkTimeLabel->setVisible(false); 788 | ui->lightTimeEdit->setVisible(false); 789 | ui->darkTimeEdit->setVisible(false); 790 | ui->latitudeLabel->setVisible(true); 791 | ui->longitudeLabel->setVisible(true); 792 | ui->latitudeDSB->setVisible(true); 793 | ui->longitudeDSB->setVisible(true); 794 | } 795 | utils.settings->setValue("schedule-type", scheduleType); 796 | utils.settings->sync(); 797 | 798 | ui->resMsg->setText(tr("To change automatic mode, Koi must be restarted.")); 799 | ui->resMsg->setMessageType(KMessageWidget::Warning); 800 | ui->resMsg->animatedShow(); 801 | } 802 | void MainWindow::on_lightTimeEdit_userTimeChanged( 803 | const QTime &time) // Set light time 804 | { 805 | lightTime = time.toString(); 806 | utils.settings->setValue("time-light", lightTime); 807 | utils.settings->sync(); 808 | ui->resMsg->setText(tr("Koi must be restarted for new schedule to be used.")); 809 | ui->resMsg->setMessageType(KMessageWidget::Warning); 810 | ui->resMsg->animatedShow(); 811 | } 812 | void MainWindow::on_darkTimeEdit_userTimeChanged( 813 | const QTime &time) // Set dark time 814 | { 815 | darkTime = time.toString(); 816 | utils.settings->setValue("time-dark", darkTime); 817 | utils.settings->sync(); 818 | ui->resMsg->setText(tr("Koi must be restarted for new schedule to be used.")); 819 | ui->resMsg->setMessageType(KMessageWidget::Warning); 820 | ui->resMsg->animatedShow(); 821 | } 822 | void MainWindow::on_latitudeDSB_valueChanged(double lat) { 823 | utils.settings->setValue("latitude", lat); 824 | utils.settings->sync(); 825 | ui->resMsg->setText( 826 | tr("Koi must be restarted for new coordinates to be used.")); 827 | ui->resMsg->setMessageType(KMessageWidget::Warning); 828 | ui->resMsg->animatedShow(); 829 | } 830 | void MainWindow::on_longitudeDSB_valueChanged(double lon) { 831 | utils.settings->setValue("longitude", lon); 832 | utils.settings->sync(); 833 | ui->resMsg->setText( 834 | tr("Koi must be restarted for new coordinates to be used.")); 835 | ui->resMsg->setMessageType(KMessageWidget::Warning); 836 | ui->resMsg->animatedShow(); 837 | } 838 | void MainWindow::on_hiddenCheckBox_stateChanged(int hiddenEnabled) { 839 | utils.settings->setValue("start-hidden", hiddenEnabled); 840 | } 841 | void MainWindow::on_notifyCheckBox_stateChanged(int notifyEnabled) { 842 | utils.settings->setValue("notify", notifyEnabled); 843 | } 844 | 845 | // Menubar actions 846 | void MainWindow::on_actionQuit_triggered() // Quit app 847 | { 848 | QApplication::quit(); 849 | } 850 | void MainWindow::on_actionPrefs_triggered() // Set preferences 851 | { 852 | on_prefsBtn_clicked(); // Triggers "Preferences" button 853 | } 854 | void MainWindow::on_actionAbout_triggered() // Open about dialog 855 | { 856 | auto *about = new About(this); 857 | about->open(); 858 | } 859 | void MainWindow::on_actionHide_triggered() // Hide to tray 860 | { 861 | this->setVisible(0); 862 | } 863 | /*void MainWindow::on_actionRefresh_triggered() // Refresh dirs 864 | { 865 | on_refreshBtn_clicked(); 866 | }*/ 867 | void MainWindow::on_actionRestart_triggered() { 868 | QProcess::startDetached(QApplication::applicationFilePath(), QStringList()); 869 | exit(12); 870 | } 871 | -------------------------------------------------------------------------------- /src/plugins/colorscheme.cpp: -------------------------------------------------------------------------------- 1 | #include "colorscheme.h" 2 | 3 | #include 4 | 5 | void ColorScheme::setTheme(QString themeName) { 6 | QProcess process; 7 | QString locateProgram = "whereis"; 8 | QStringList programToLocate = {"plasma-apply-colorscheme"}; 9 | 10 | process.start(locateProgram, programToLocate); 11 | process.waitForFinished(); 12 | 13 | QString program(process.readAllStandardOutput()); 14 | program.replace("plasma-apply-colorscheme: ", ""); 15 | program.replace("\n", ""); 16 | 17 | QStringList arguments{themeName}; 18 | QProcess::startDetached(program, arguments); 19 | } 20 | 21 | QStringList ColorScheme::getThemes() { 22 | QStringList colorSchemesNames; 23 | QDir colorsLocalDir(QDir::homePath() + "/.local/share/color-schemes"); 24 | QDir colorsSystemDir("/usr/share/color-schemes"); 25 | QDir colorsNixDir("/var/run/current-system/sw/share/color-schemes"); 26 | if (colorsLocalDir.exists()) { 27 | colorsLocalDir.setNameFilters(QStringList() << "*.colors"); 28 | colorsLocalDir.setFilter(QDir::Files); 29 | colorsLocalDir.setSorting(QDir::Name); 30 | QList colorSchemesLocal = colorsLocalDir.entryInfoList(); 31 | QStringList colorSchemesLocalNames; 32 | for (int i = 0; i < colorSchemesLocal.size(); i++) { 33 | colorSchemesLocalNames.append(colorSchemesLocal.at(i).baseName()); 34 | } 35 | colorSchemesNames = colorSchemesNames + colorSchemesLocalNames; 36 | } 37 | if (colorsSystemDir.exists()) { 38 | colorsSystemDir.setNameFilters(QStringList() << "*.colors"); 39 | colorsSystemDir.setFilter(QDir::Files); 40 | colorsSystemDir.setSorting(QDir::Name); 41 | QList colorSchemesSystem = colorsSystemDir.entryInfoList(); 42 | QStringList colorSchemesSystemNames; 43 | for (int i = 0; i < colorSchemesSystem.size(); i++) { 44 | colorSchemesSystemNames.append(colorSchemesSystem.at(i).baseName()); 45 | } 46 | colorSchemesNames = colorSchemesNames + colorSchemesSystemNames; 47 | } 48 | if (colorsNixDir.exists()) { 49 | colorsNixDir.setNameFilters(QStringList() << "*.colors"); 50 | colorsNixDir.setFilter(QDir::Files); 51 | colorsNixDir.setSorting(QDir::Name); 52 | QList colorSchemesNix = colorsNixDir.entryInfoList(); 53 | QStringList colorSchemesNixNames; 54 | for (int i = 0; i < colorSchemesNix.size(); i++) { 55 | colorSchemesNixNames.append(colorSchemesNix.at(i).baseName()); 56 | } 57 | colorSchemesNames = colorSchemesNames + colorSchemesNixNames; 58 | } 59 | colorSchemesNames.removeDuplicates(); 60 | return colorSchemesNames; 61 | } 62 | -------------------------------------------------------------------------------- /src/plugins/colorscheme.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "src/headers/plugin.h" 4 | 5 | class ColorScheme : protected Plugin { 6 | public: 7 | void setTheme(QString themeName) override; 8 | QStringList getThemes() override; 9 | }; 10 | -------------------------------------------------------------------------------- /src/plugins/gtk.cpp: -------------------------------------------------------------------------------- 1 | #include "gtk.h" 2 | 3 | void Gtk::setTheme(QString theme) { 4 | 5 | if(!interface) 6 | { 7 | interface = new QDBusInterface("org.kde.GtkConfig", "/GtkConfig", 8 | "org.kde.GtkConfig", QDBusConnection::sessionBus()); 9 | } 10 | 11 | interface->call("setGtk2Theme", theme); 12 | interface->call("setGtk3Theme", theme); 13 | interface->call("setGtkTheme", theme); 14 | } 15 | 16 | QStringList Gtk::getThemes() { 17 | QDir gtkLocalDir(QDir::homePath() + "/.themes"); 18 | QDir gtkSystemDir("/usr/share/themes"); 19 | QDir gtkNixDir("/var/run/current-system/sw/share/themes"); 20 | QStringList gtkThemes; 21 | if (gtkLocalDir.exists()) { 22 | gtkThemes = 23 | gtkThemes + gtkLocalDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); 24 | ; 25 | } 26 | if (gtkSystemDir.exists()) { 27 | gtkThemes = 28 | gtkThemes + gtkSystemDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); 29 | ; 30 | }; 31 | if (gtkNixDir.exists()) { 32 | gtkThemes = 33 | gtkThemes + gtkNixDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); 34 | ; 35 | } 36 | gtkThemes.removeDuplicates(); 37 | gtkThemes.sort(); 38 | return gtkThemes; 39 | } 40 | -------------------------------------------------------------------------------- /src/plugins/gtk.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "src/headers/plugin.h" 4 | 5 | class Gtk : DbusPlugin { 6 | public: 7 | void setTheme(QString theme) override; 8 | QStringList getThemes() override; 9 | }; 10 | -------------------------------------------------------------------------------- /src/plugins/icons.cpp: -------------------------------------------------------------------------------- 1 | #include "icons.h" 2 | 3 | #include 4 | 5 | void Icons::setTheme(QString iconTheme) { 6 | 7 | // locate plasma-changeicons program 8 | QProcess process; 9 | QString locateProgram = "whereis"; 10 | QStringList programToLocate = {"plasma-changeicons"}; 11 | 12 | process.start(locateProgram, programToLocate); 13 | process.waitForFinished(); 14 | 15 | QString program(process.readAllStandardOutput()); 16 | program.replace("plasma-changeicons: ", ""); 17 | program.replace("\n", ""); 18 | 19 | // apply the icon theme 20 | QStringList arguments{iconTheme}; 21 | QProcess::startDetached(program, arguments); 22 | } 23 | 24 | QStringList Icons::getThemes() { 25 | QDir iconsOldLocalDir(QDir::homePath() + "/.icons"); 26 | QDir iconsLocalDir(QDir::homePath() + "/.local/share/icons"); 27 | QDir iconsSystemDir("/usr/share/icons"); 28 | QDir iconsNixDir("/var/run/current-system/sw/share/icons"); 29 | QStringList iconThemes; 30 | if (iconsOldLocalDir.exists()) { 31 | iconThemes = iconThemes + 32 | iconsOldLocalDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); 33 | } 34 | if (iconsLocalDir.exists()) { 35 | iconThemes = 36 | iconThemes + iconsLocalDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); 37 | } 38 | if (iconsSystemDir.exists()) { 39 | iconThemes = iconThemes + 40 | iconsSystemDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); 41 | } 42 | if (iconsNixDir.exists()) { 43 | iconThemes = 44 | iconThemes + iconsNixDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); 45 | } 46 | iconThemes.removeDuplicates(); 47 | iconThemes.sort(); 48 | return iconThemes; 49 | } 50 | -------------------------------------------------------------------------------- /src/plugins/icons.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "src/headers/plugin.h" 4 | 5 | class Icons : Plugin { 6 | public: 7 | void setTheme(QString iconTheme) override; 8 | QStringList getThemes() override; 9 | }; 10 | -------------------------------------------------------------------------------- /src/plugins/kvantumstyle.cpp: -------------------------------------------------------------------------------- 1 | #include "kvantumstyle.h" 2 | 3 | #include 4 | 5 | void KvantumStyle::setTheme(QString kvantumStyle) { 6 | QStringList arguments{"--set", kvantumStyle}; 7 | QProcess::startDetached("/usr/bin/kvantummanager", arguments); 8 | } 9 | 10 | QStringList KvantumStyle::getThemes() { 11 | QDir kvantumStyleLocalDir(QDir::homePath() + "/.config/Kvantum"); 12 | QDir kvantumStyleSystemDir("/usr/share/Kvantum"); 13 | QDir kvantumStyleNixDir("/var/run/current-system/sw/Kvantum"); 14 | QDir kvantumStyleDirs[] = {kvantumStyleLocalDir, kvantumStyleSystemDir, kvantumStyleNixDir}; 15 | QStringList kvantumStyles; 16 | for (const auto& dir : kvantumStyleDirs) { 17 | if (dir.exists()) { 18 | //Navigate subdirectories 19 | for (const auto& entry : dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot)) { 20 | //Check inside the directory for all ".kconfig" files 21 | QDir subDir(entry.absoluteFilePath()); 22 | for (const auto& subEntry : subDir.entryInfoList(QDir::Files)) { 23 | if (subEntry.suffix() == "kvconfig") { 24 | kvantumStyles.append(subEntry.baseName()); 25 | } 26 | } 27 | } 28 | } 29 | } 30 | kvantumStyles.removeDuplicates(); 31 | kvantumStyles.sort(); 32 | return kvantumStyles; 33 | } 34 | -------------------------------------------------------------------------------- /src/plugins/kvantumstyle.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "src/headers/plugin.h" 4 | 5 | class KvantumStyle : Plugin { 6 | public: 7 | void setTheme(QString kvantumStyle) override; 8 | QStringList getThemes() override; 9 | }; 10 | -------------------------------------------------------------------------------- /src/plugins/plasmastyle.cpp: -------------------------------------------------------------------------------- 1 | #include "plasmastyle.h" 2 | 3 | #include 4 | 5 | void PlasmaStyle::setTheme(QString plasmaStyle) { 6 | QStringList styleArgs = {plasmaStyle}; 7 | QProcess::execute("/usr/bin/plasma-apply-desktoptheme", styleArgs); 8 | } 9 | 10 | QStringList PlasmaStyle::getThemes(void) { 11 | QStringList plasmaStyles; 12 | QDir stylesLocalDir(QDir::homePath() + "/.local/share/plasma/desktoptheme"); 13 | QDir stylesSystemDir("/usr/share/plasma/desktoptheme"); 14 | QDir stylesNixDir("/var/run/current-system/sw/share/plasma/desktoptheme"); 15 | if (stylesLocalDir.exists()) { 16 | plasmaStyles = plasmaStyles + 17 | stylesLocalDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); 18 | ; 19 | } 20 | if (stylesSystemDir.exists()) { 21 | plasmaStyles = plasmaStyles + 22 | stylesSystemDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); 23 | ; 24 | }; 25 | if (stylesNixDir.exists()) { 26 | plasmaStyles = plasmaStyles + 27 | stylesNixDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); 28 | ; 29 | } 30 | plasmaStyles.removeDuplicates(); 31 | plasmaStyles.sort(); 32 | return plasmaStyles; 33 | } -------------------------------------------------------------------------------- /src/plugins/plasmastyle.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "src/headers/plugin.h" 4 | class PlasmaStyle : Plugin { 5 | public: 6 | void setTheme(QString plasmaStyle) override; 7 | QStringList getThemes() override; 8 | }; 9 | -------------------------------------------------------------------------------- /src/plugins/script.cpp: -------------------------------------------------------------------------------- 1 | #include "script.h" 2 | 3 | #include 4 | 5 | void Script::setTheme(QString scriptFile) { 6 | 7 | QProcess process; 8 | QString locateProgram = "which"; 9 | QStringList programToLocate = {"bash"}; 10 | 11 | process.start(locateProgram, programToLocate); 12 | process.waitForFinished(); 13 | 14 | QString program(process.readAllStandardOutput()); 15 | program.replace("\n", ""); 16 | 17 | QStringList arguments{"-c", scriptFile}; 18 | QProcess::execute(program, arguments); 19 | } -------------------------------------------------------------------------------- /src/plugins/script.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "src/headers/plugin.h" 4 | 5 | class Script : Plugin { 6 | public: 7 | void setTheme(QString scriptFile) override; 8 | }; 9 | -------------------------------------------------------------------------------- /src/plugins/wallpaper.cpp: -------------------------------------------------------------------------------- 1 | #include "wallpaper.h" 2 | 3 | void Wallpaper::setTheme(QString wallFile) { 4 | 5 | if(!interface) 6 | { 7 | interface = new QDBusInterface("org.kde.plasmashell", "/PlasmaShell", 8 | "org.kde.PlasmaShell", QDBusConnection::sessionBus()); 9 | } 10 | QString script = "var allDesktops = desktops();"; 11 | script += "print (allDesktops);"; 12 | script += "for (i=0;icall("evaluateScript", script); 21 | } 22 | -------------------------------------------------------------------------------- /src/plugins/wallpaper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "src/headers/plugin.h" 4 | class Wallpaper : DbusPlugin { 5 | public: 6 | void setTheme(QString wallFile) override; 7 | }; 8 | -------------------------------------------------------------------------------- /src/resources/dbus/services/dev.baduhai.Koi.service: -------------------------------------------------------------------------------- 1 | [D-BUS Service] 2 | Name=dev.baduhai.Koi 3 | Exec=koi 4 | -------------------------------------------------------------------------------- /src/resources/icons/koi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baduhai/Koi/e90b850aa3d7665e9ce0828b9b0108c4e542d204/src/resources/icons/koi.png -------------------------------------------------------------------------------- /src/resources/icons/koi.svg: -------------------------------------------------------------------------------- 1 | 2 | 18 | 20 | 23 | 27 | 31 | 32 | 35 | 39 | 43 | 44 | 53 | 63 | 64 | 86 | 91 | 96 | 101 | 106 | 111 | 116 | 121 | 126 | 131 | 136 | 141 | 146 | 151 | 156 | 161 | 166 | 167 | 169 | 170 | 172 | image/svg+xml 173 | 175 | 176 | 177 | 178 | 179 | 183 | 188 | 190 | 195 | 200 | 201 | 206 | 210 | 215 | 220 | 221 | 222 | 223 | -------------------------------------------------------------------------------- /src/resources/icons/koi_tray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baduhai/Koi/e90b850aa3d7665e9ce0828b9b0108c4e542d204/src/resources/icons/koi_tray.png -------------------------------------------------------------------------------- /src/resources/icons/koi_tray.svg: -------------------------------------------------------------------------------- 1 | 4 | 9 | 10 | 12 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/resources/license.txt: -------------------------------------------------------------------------------- 1 | (C) 2020-2024, William Franco Abdul Hai 2 | 2023-2025, Martin Stibor (Martin von Reichenberg) 3 | 4 | GNU LESSER GENERAL PUBLIC LICENSE 5 | Version 3, 29 June 2007 6 | 7 | Copyright (C) 2007 Free Software Foundation, Inc. 8 | Everyone is permitted to copy and distribute verbatim copies 9 | of this license document, but changing it is not allowed. 10 | 11 | 12 | This version of the GNU Lesser General Public License incorporates 13 | the terms and conditions of version 3 of the GNU General Public 14 | License, supplemented by the additional permissions listed below. 15 | 16 | 0. Additional Definitions. 17 | 18 | As used herein, "this License" refers to version 3 of the GNU Lesser 19 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 20 | General Public License. 21 | 22 | "The Library" refers to a covered work governed by this License, 23 | other than an Application or a Combined Work as defined below. 24 | 25 | An "Application" is any work that makes use of an interface provided 26 | by the Library, but which is not otherwise based on the Library. 27 | Defining a subclass of a class defined by the Library is deemed a mode 28 | of using an interface provided by the Library. 29 | 30 | A "Combined Work" is a work produced by combining or linking an 31 | Application with the Library. The particular version of the Library 32 | with which the Combined Work was made is also called the "Linked 33 | Version". 34 | 35 | The "Minimal Corresponding Source" for a Combined Work means the 36 | Corresponding Source for the Combined Work, excluding any source code 37 | for portions of the Combined Work that, considered in isolation, are 38 | based on the Application, and not on the Linked Version. 39 | 40 | The "Corresponding Application Code" for a Combined Work means the 41 | object code and/or source code for the Application, including any data 42 | and utility programs needed for reproducing the Combined Work from the 43 | Application, but excluding the System Libraries of the Combined Work. 44 | 45 | 1. Exception to Section 3 of the GNU GPL. 46 | 47 | You may convey a covered work under sections 3 and 4 of this License 48 | without being bound by section 3 of the GNU GPL. 49 | 50 | 2. Conveying Modified Versions. 51 | 52 | If you modify a copy of the Library, and, in your modifications, a 53 | facility refers to a function or data to be supplied by an Application 54 | that uses the facility (other than as an argument passed when the 55 | facility is invoked), then you may convey a copy of the modified 56 | version: 57 | 58 | a) under this License, provided that you make a good faith effort to 59 | ensure that, in the event an Application does not supply the 60 | function or data, the facility still operates, and performs 61 | whatever part of its purpose remains meaningful, or 62 | 63 | b) under the GNU GPL, with none of the additional permissions of 64 | this License applicable to that copy. 65 | 66 | 3. Object Code Incorporating Material from Library Header Files. 67 | 68 | The object code form of an Application may incorporate material from 69 | a header file that is part of the Library. You may convey such object 70 | code under terms of your choice, provided that, if the incorporated 71 | material is not limited to numerical parameters, data structure 72 | layouts and accessors, or small macros, inline functions and templates 73 | (ten or fewer lines in length), you do both of the following: 74 | 75 | a) Give prominent notice with each copy of the object code that the 76 | Library is used in it and that the Library and its use are 77 | covered by this License. 78 | 79 | b) Accompany the object code with a copy of the GNU GPL and this license 80 | document. 81 | 82 | 4. Combined Works. 83 | 84 | You may convey a Combined Work under terms of your choice that, 85 | taken together, effectively do not restrict modification of the 86 | portions of the Library contained in the Combined Work and reverse 87 | engineering for debugging such modifications, if you also do each of 88 | the following: 89 | 90 | a) Give prominent notice with each copy of the Combined Work that 91 | the Library is used in it and that the Library and its use are 92 | covered by this License. 93 | 94 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 95 | document. 96 | 97 | c) For a Combined Work that displays copyright notices during 98 | execution, include the copyright notice for the Library among 99 | these notices, as well as a reference directing the user to the 100 | copies of the GNU GPL and this license document. 101 | 102 | d) Do one of the following: 103 | 104 | 0) Convey the Minimal Corresponding Source under the terms of this 105 | License, and the Corresponding Application Code in a form 106 | suitable for, and under terms that permit, the user to 107 | recombine or relink the Application with a modified version of 108 | the Linked Version to produce a modified Combined Work, in the 109 | manner specified by section 6 of the GNU GPL for conveying 110 | Corresponding Source. 111 | 112 | 1) Use a suitable shared library mechanism for linking with the 113 | Library. A suitable mechanism is one that (a) uses at run time 114 | a copy of the Library already present on the user's computer 115 | system, and (b) will operate properly with a modified version 116 | of the Library that is interface-compatible with the Linked 117 | Version. 118 | 119 | e) Provide Installation Information, but only if you would otherwise 120 | be required to provide such information under section 6 of the 121 | GNU GPL, and only to the extent that such information is 122 | necessary to install and execute a modified version of the 123 | Combined Work produced by recombining or relinking the 124 | Application with a modified version of the Linked Version. (If 125 | you use option 4d0, the Installation Information must accompany 126 | the Minimal Corresponding Source and Corresponding Application 127 | Code. If you use option 4d1, you must provide the Installation 128 | Information in the manner specified by section 6 of the GNU GPL 129 | for conveying Corresponding Source.) 130 | 131 | 5. Combined Libraries. 132 | 133 | You may place library facilities that are a work based on the 134 | Library side by side in a single library together with other library 135 | facilities that are not Applications and are not covered by this 136 | License, and convey such a combined library under terms of your 137 | choice, if you do both of the following: 138 | 139 | a) Accompany the combined library with a copy of the same work based 140 | on the Library, uncombined with any other library facilities, 141 | conveyed under the terms of this License. 142 | 143 | b) Give prominent notice with the combined library that part of it 144 | is a work based on the Library, and explaining where to find the 145 | accompanying uncombined form of the same work. 146 | 147 | 6. Revised Versions of the GNU Lesser General Public License. 148 | 149 | The Free Software Foundation may publish revised and/or new versions 150 | of the GNU Lesser General Public License from time to time. Such new 151 | versions will be similar in spirit to the present version, but may 152 | differ in detail to address new problems or concerns. 153 | 154 | Each version is given a distinguishing version number. If the 155 | Library as you received it specifies that a certain numbered version 156 | of the GNU Lesser General Public License "or any later version" 157 | applies to it, you have the option of following the terms and 158 | conditions either of that published version or of any later version 159 | published by the Free Software Foundation. If the Library as you 160 | received it does not specify a version number of the GNU Lesser 161 | General Public License, you may choose any version of the GNU Lesser 162 | General Public License ever published by the Free Software Foundation. 163 | 164 | If the Library as you received it specifies that a proxy can decide 165 | whether future versions of the GNU Lesser General Public License shall 166 | apply, that proxy's public statement of acceptance of any version is 167 | permanent authorization for you to choose that version for the 168 | Library. 169 | -------------------------------------------------------------------------------- /src/resources/resources.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | dbus/services/dev.baduhai.Koi.service 4 | icons/koi.svg 5 | icons/koi_tray.svg 6 | icons/koi_tray.png 7 | icons/koi.png 8 | license.txt 9 | sunrise-library.properties 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/resources/sunrise-library.properties: -------------------------------------------------------------------------------- 1 | name=SunRise 2 | version=2.0.4 3 | author=Cyrus Rahman 4 | maintainer=Cyrus Rahman 5 | sentence=Calculate sun rise/set times. 6 | paragraph=Find the previous and next sun rise and set times. 7 | category=Other 8 | url=https://github.com/signetica/SunRise 9 | architectures=* 10 | -------------------------------------------------------------------------------- /src/runguard.cpp: -------------------------------------------------------------------------------- 1 | #include "headers/runguard.h" 2 | 3 | #include 4 | 5 | 6 | namespace 7 | { 8 | 9 | QString generateKeyHash( const QString& key, const QString& salt ) 10 | { 11 | QByteArray data; 12 | 13 | data.append( key.toUtf8() ); 14 | data.append( salt.toUtf8() ); 15 | data = QCryptographicHash::hash( data, QCryptographicHash::Sha1 ).toHex(); 16 | 17 | return data; 18 | } 19 | 20 | } 21 | 22 | 23 | RunGuard::RunGuard( const QString& key ) 24 | : key( key ) 25 | , memLockKey( generateKeyHash( key, "_memLockKey" ) ) 26 | , sharedmemKey( generateKeyHash( key, "_sharedmemKey" ) ) 27 | , sharedMem( sharedmemKey ) 28 | , memLock( memLockKey, 1 ) 29 | { 30 | memLock.acquire(); 31 | { 32 | QSharedMemory fix( sharedmemKey ); // Fix for *nix: http://habrahabr.ru/post/173281/ 33 | fix.attach(); 34 | } 35 | memLock.release(); 36 | } 37 | 38 | RunGuard::~RunGuard() 39 | { 40 | release(); 41 | } 42 | 43 | bool RunGuard::isAnotherRunning() 44 | { 45 | if ( sharedMem.isAttached() ) 46 | return false; 47 | 48 | memLock.acquire(); 49 | const bool isRunning = sharedMem.attach(); 50 | if ( isRunning ) 51 | sharedMem.detach(); 52 | memLock.release(); 53 | 54 | return isRunning; 55 | } 56 | 57 | bool RunGuard::tryToRun() 58 | { 59 | if ( isAnotherRunning() ) // Extra check 60 | return false; 61 | 62 | memLock.acquire(); 63 | const bool result = sharedMem.create( sizeof( quint64 ) ); 64 | memLock.release(); 65 | if ( !result ) 66 | { 67 | release(); 68 | return false; 69 | } 70 | 71 | return true; 72 | } 73 | 74 | void RunGuard::release() 75 | { 76 | memLock.acquire(); 77 | if ( sharedMem.isAttached() ) 78 | sharedMem.detach(); 79 | memLock.release(); 80 | } 81 | -------------------------------------------------------------------------------- /src/ui/about.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | About 4 | 5 | 6 | Qt::ApplicationModal 7 | 8 | 9 | true 10 | 11 | 12 | 13 | 0 14 | 0 15 | 603 16 | 437 17 | 18 | 19 | 20 | 21 | 4 22 | 4 23 | 24 | 25 | 26 | 27 | 1000 28 | 1000 29 | 30 | 31 | 32 | About — Koi 33 | 34 | 35 | 36 | :/resources/icons/koi_tray.svg 37 | :/resources/icons/koi.svg 38 | :/resources/icons/koi_tray.svg 39 | :/resources/icons/koi.svg 40 | :/resources/icons/koi_tray.svg 41 | :/resources/icons/koi.svg 42 | :/resources/icons/koi_tray.svg 43 | :/resources/icons/koi.svg:/resources/icons/koi_tray.svg 44 | 45 | 46 | Qt::LeftToRight 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | Close 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | border: none 69 | 70 | 71 | 72 | 73 | 74 | 75 | :/resources/icons/koi.svg:/resources/icons/koi.svg 76 | 77 | 78 | 79 | 40 80 | 40 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | App Name 91 | 92 | 93 | 94 | 95 | 96 | 97 | Version NO 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | Qt::Horizontal 107 | 108 | 109 | 110 | 40 111 | 20 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | QTabWidget::Rounded 122 | 123 | 124 | 2 125 | 126 | 127 | 128 | About 129 | 130 | 131 | 132 | 133 | 134 | ***We want to thank everyone for their support & contribution*** 135 | 136 | 137 | Qt::MarkdownText 138 | 139 | 140 | 141 | 142 | 143 | 144 | Scheduling LIGHT/DARK Theme Switcher for KDE Plasma Desktop 145 | 146 | 147 | 148 | 149 | 150 | 151 | <html><head/><body><p>Please report bugs or request new features on <a href="https://github.com/baduhai/Koi/issues"><span style=" text-decoration: underline; color:#2980b9;">GitHub</span></a></p></body></html> 152 | 153 | 154 | Qt::AutoText 155 | 156 | 157 | true 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 0 166 | 0 167 | 168 | 169 | 170 | 171 | 0 172 | 1 173 | 174 | 175 | 176 | PointingHandCursor 177 | 178 | 179 | border: none; 180 | color: #315bef; 181 | text-decoration: underline; 182 | text-align: left; 183 | 184 | 185 | GNU Lesser General Public License Version 3 186 | 187 | 188 | true 189 | 190 | 191 | true 192 | 193 | 194 | 195 | 196 | 197 | 198 | PointingHandCursor 199 | 200 | 201 | Qt::LeftToRight 202 | 203 | 204 | <html><head/><body><p>Project's <a href="https://github.com/baduhai/Koi"><span style=" text-decoration: underline; color:#2980b9;">HOME</span></a></p></body></html> 205 | 206 | 207 | Qt::AutoText 208 | 209 | 210 | true 211 | 212 | 213 | 214 | 215 | 216 | 217 | <html><head/><body><p><span style=" font-style:italic;">(C) 2020-2025</span>, <span style=" text-decoration: underline;">William Franco Abdul Hai</span>, <span style=" font-style:italic;">2022-2025 </span><span style=" text-decoration: underline;">Martin Stibor</span></p></body></html> 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | Libraries 226 | 227 | 228 | 229 | 230 | 231 | Qt Version 232 | 233 | 234 | 235 | 236 | 237 | 238 | KF Version 239 | 240 | 241 | 242 | 243 | 244 | 245 | <html><head/><body><p><a href="https://github.com/Bosma/Scheduler"><span style=" text-decoration: underline; color:#2980b9;">Scheduler</span></a></p></body></html> 246 | 247 | 248 | 249 | 250 | 251 | 252 | <html><head/><body><p><a href="https://github.com/vit-vit/CTPL"><span style=" text-decoration: underline; color:#2980b9;">CTPL</span></a></p></body></html> 253 | 254 | 255 | 256 | 257 | 258 | 259 | <html><head/><body><p><a href="https://github.com/signetica/SunRise"><span style=" text-decoration: underline; color:#2980b9;">SunRise</span></a></p></body></html> 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | Authors 268 | 269 | 270 | 271 | 272 | 273 | 274 | 65 275 | 21 276 | 277 | 278 | 279 | Qt::LeftToRight 280 | 281 | 282 | **All participants:** 283 | 284 | 285 | Qt::MarkdownText 286 | 287 | 288 | 289 | 290 | 291 | 292 | Qt::Vertical 293 | 294 | 295 | 296 | 297 | 298 | 299 | false 300 | 301 | 302 | true 303 | 304 | 305 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> 306 | <html><head><meta name="qrichtext" content="1" /><style type="text/css"> 307 | p, li { white-space: pre-wrap; } 308 | </style></head><body style=" font-family:'Noto Sans'; font-size:11pt; font-weight:400; font-style:normal;"> 309 | <p align="justify" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">(C) 2020-2024, <span style=" text-decoration: underline;">William Franco Abdul Hai</span> <span style=" font-style:italic;">(baduhai)</span></p> 310 | <p align="justify" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">2023-2025, <span style=" text-decoration: underline;">Martin Stibor</span> <span style=" font-style:italic;">(Martin von reichenberg)</span></p> 311 | <p align="justify" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">2025, <span style=" text-decoration: underline;">Matthias Möller</span> <span style=" font-style:italic;">(TinyTinni)</span></p> 312 | <p align="justify" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">2025, <span style=" text-decoration: underline;">Spencer Williams</span> <span style=" font-style:italic;">(spencerwi)</span></p> 313 | <p align="justify" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">2025, <span style=" text-decoration: underline;">Andrea Spelgatti</span> <span style=" font-style:italic;">(Ourouk)</span></p> 314 | <p align="justify" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">2024, <span style=" text-decoration: underline;">Giovanni Santini</span> <span style=" font-style:italic;">(ItachiSan)</span></p> 315 | <p align="justify" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">2025, <span style=" font-style:italic;">miaouwu</span></p> 316 | <p align="justify" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">2024, <span style=" font-style:italic;">passportmidi</span></p> 317 | <p align="justify" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">2024, <span style=" font-style:italic;">financelurker</span></p> 318 | <p align="justify" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">2024, <span style=" font-style:italic;">ducvietcao</span></p></body></html> 319 | 320 | 321 | Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse 322 | 323 | 324 | true 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | -------------------------------------------------------------------------------- /src/ui/license.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | License 4 | 5 | 6 | 7 | 0 8 | 0 9 | 757 10 | 544 11 | 12 | 13 | 14 | License Agreement — Koi 15 | 16 | 17 | 18 | 19 | 20 | Qt::TextInteractionFlag::NoTextInteraction 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 0 29 | 0 30 | 31 | 32 | 33 | Close 34 | 35 | 36 | 37 | 38 | 39 | false 40 | 41 | 42 | true 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /src/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "headers/utils.h" 2 | #include "libraries/SunRise.h" 3 | 4 | #include 5 | 6 | Utils::Utils() {} 7 | 8 | // Global settings stuff 9 | void Utils::initialiseSettings() { 10 | settings = 11 | std::make_unique(QDir::homePath() + "/.config/koirc", 12 | QSettings::IniFormat); // Setting config path and format 13 | } 14 | 15 | // Miscelaneous functions 16 | void Utils::notify(QString notifySummary, QString notifyBody, 17 | int timeoutms) // Push notification through DBus 18 | { 19 | if (!notifyInterface) 20 | { 21 | notifyInterface = new QDBusInterface("org.freedesktop.Notifications", 22 | "/org/freedesktop/Notifications", 23 | "org.freedesktop.Notifications", QDBusConnection::sessionBus()); 24 | } 25 | QString app_name = "Koi"; // What program is the notification coming from? 26 | uint replaces_id = 27 | 0; // Not sure what this is. Think it has something to do with pid. 28 | QString app_icon; // Not actually specifying app icon, this is if you'd like 29 | // to push an image alog with notification. 30 | QString summary = notifySummary; // Title of notification. 31 | QString body = notifyBody; // Notification body. 32 | QStringList actions; // No idea how to use. 33 | QVariantMap hints; // Provide .desktop file for notification sorting in the DE 34 | hints.insert("desktop-entry", "Koi"); 35 | int timeout = timeoutms; // Notification timeout, there's no way to assume 36 | // system has a default timeout unfortunately. 37 | notifyInterface->call("Notify", app_name, replaces_id, app_icon, summary, 38 | body, actions, hints, timeout); 39 | } 40 | void Utils::startupTimeCheck() // Switch to the theme set for the current time 41 | { 42 | QTime lightTime = 43 | QTime::fromString(settings->value("time-light").toString(), "hh:mm:ss"); 44 | QTime darkTime = 45 | QTime::fromString(settings->value("time-dark").toString(), "hh:mm:ss"); 46 | QTime now = QTime::currentTime(); 47 | if (now < lightTime && now < darkTime) { 48 | QThread::msleep(1000); // Needed delay, or Koi may use the wrong color scheme. 49 | goDark(); 50 | } else if (now == lightTime) // Highly unlikely 51 | { 52 | QThread::msleep(1000); 53 | goLight(); 54 | } else if (now > lightTime && now < darkTime) { 55 | QThread::msleep(1000); 56 | goLight(); 57 | } else if (now == darkTime) // Highly unlikely 58 | { 59 | QThread::msleep(1000); 60 | goDark(); 61 | } else { 62 | QThread::msleep(1000); 63 | goDark(); 64 | } 65 | } 66 | 67 | void Utils::startupSunCheck() { // Switch to the theme set for the current sun 68 | // status 69 | 70 | double latitude = settings->value("latitude").toDouble(); 71 | double longitude = settings->value("longitude").toDouble(); 72 | time_t t = time(NULL); 73 | 74 | SunRise sr; 75 | sr.calculate(latitude, longitude, t); 76 | 77 | if (sr.isVisible) { 78 | QThread::msleep(1000); 79 | goLight(); 80 | } else { 81 | QThread::msleep(1000); 82 | goDark(); 83 | } 84 | } 85 | 86 | // Get stuff 87 | QStringList Utils::getPlasmaStyles(void) { return plasmastyle.getThemes(); } 88 | // Get all available color schemes 89 | QStringList Utils::getColorSchemes(void) { return colorscheme.getThemes(); } 90 | // Get all available icon themes 91 | QStringList Utils::getIconThemes(void) { return icons.getThemes(); } 92 | // Get all available gtk themes 93 | QStringList Utils::getGtkThemes(void) { return gtk.getThemes(); } 94 | // Get all available kvantum styles 95 | QStringList Utils::getKvantumStyles(void) { return kvantumStyle.getThemes(); } 96 | // Manage switching themes functions 97 | void Utils::toggle() { 98 | const auto current = 99 | settings->value("current", QVariant::fromValue(Mode::Undefined)) 100 | .value(); 101 | if (current == Mode::Light) { 102 | goDark(); 103 | } else if (current == Mode::Dark) { 104 | goLight(); 105 | } 106 | } 107 | void Utils::goLight() { 108 | goLightStyle(); 109 | goLightColors(); 110 | goLightIcons(); 111 | goLightGtk(); 112 | goLightKvantumStyle(); 113 | goLightWall(); 114 | goLightScript(); 115 | restartProcess(); 116 | if (settings->value("notify").toBool()) { 117 | notify("Switched to light mode!", 118 | "Some applications may need to be restarted for applied changes to " 119 | "take effect."); 120 | } 121 | settings->setValue("current", QVariant::fromValue(Mode::Light).toString()); 122 | } 123 | void Utils::goDark() { 124 | goDarkStyle(); 125 | goDarkColors(); 126 | goDarkIcons(); 127 | goDarkGtk(); 128 | goDarkKvantumStyle(); 129 | goDarkWall(); 130 | goDarkScript(); 131 | restartProcess(); 132 | if (settings->value("notify").toBool()) { 133 | notify("Switched to dark mode!", 134 | "Some applications may need to be restarted for applied changes to " 135 | "take effect."); 136 | } 137 | settings->setValue("current", QVariant::fromValue(Mode::Dark).toString()); 138 | } 139 | void Utils::goLightStyle() { 140 | if (settings->value("PlasmaStyle/enabled").toBool()) { 141 | plasmastyle.setTheme(settings->value("PlasmaStyle/light").toString()); 142 | } 143 | } 144 | void Utils::goDarkStyle() { 145 | if (settings->value("PlasmaStyle/enabled").toBool()) { 146 | plasmastyle.setTheme(settings->value("PlasmaStyle/dark").toString()); 147 | } 148 | } 149 | void Utils::goLightColors() { 150 | if (settings->value("ColorScheme/enabled").toBool()) { 151 | colorscheme.setTheme(settings->value("ColorScheme/light").toString()); 152 | } 153 | } 154 | void Utils::goDarkColors() { 155 | if (settings->value("ColorScheme/enabled").toBool()) { 156 | colorscheme.setTheme(settings->value("ColorScheme/dark").toString()); 157 | } 158 | } 159 | void Utils::goLightIcons() { 160 | if (settings->value("IconTheme/enabled").toBool()) { 161 | icons.setTheme(settings->value("IconTheme/light").toString()); 162 | } 163 | } 164 | void Utils::goDarkIcons() { 165 | if (settings->value("IconTheme/enabled").toBool()) { 166 | icons.setTheme(settings->value("IconTheme/dark").toString()); 167 | } 168 | } 169 | void Utils::goLightGtk() { 170 | if (settings->value("GTKTheme/enabled").toBool()) { 171 | gtk.setTheme(settings->value("GTKTheme/light").toString()); 172 | } 173 | } 174 | void Utils::goDarkGtk() { 175 | if (settings->value("GTKTheme/enabled").toBool()) { 176 | gtk.setTheme(settings->value("GTKTheme/dark").toString()); 177 | } 178 | } 179 | void Utils::goLightKvantumStyle() { 180 | if (settings->value("KvantumStyle/enabled").toBool()) { 181 | kvantumStyle.setTheme(settings->value("KvantumStyle/light").toString()); 182 | } 183 | } 184 | void Utils::goDarkKvantumStyle() { 185 | if (settings->value("KvantumStyle/enabled").toBool()) { 186 | kvantumStyle.setTheme(settings->value("KvantumStyle/dark").toString()); 187 | } 188 | } 189 | void Utils::goLightWall() { 190 | if (settings->value("Wallpaper/enabled").toBool()) { 191 | if (!settings->value("Wallpaper/light").isNull()) { 192 | wallpaper.setTheme(settings->value("Wallpaper/light").toString()); 193 | } else { 194 | notify("Error setting Wallpaper", 195 | "Koi tried to change your wallpaper, but no wallpaper file was " 196 | "selected", 197 | 0); 198 | } 199 | } 200 | } 201 | void Utils::goDarkWall() { 202 | if (settings->value("Wallpaper/enabled").toBool()) { 203 | if (!settings->value("Wallpaper/dark").isNull()) { 204 | wallpaper.setTheme(settings->value("Wallpaper/dark").toString()); 205 | } else { 206 | notify("Error setting Wallpaper", 207 | "Koi tried to change your wallpaper, but no wallpaper file was " 208 | "selected", 209 | 0); 210 | } 211 | } 212 | } 213 | void Utils::goLightScript() { 214 | if (settings->value("Script/enabled").toBool()) { 215 | if (!settings->value("Script/light").isNull()) { 216 | script.setTheme(settings->value("Script/light").toString()); 217 | } else { 218 | notify("Error running script", 219 | "Koi tried to run your script, but no script file was selected", 220 | 0); 221 | } 222 | } 223 | } 224 | void Utils::goDarkScript() { 225 | if (settings->value("Script/enabled").toBool()) { 226 | if (!settings->value("Script/dark").isNull()) { 227 | script.setTheme(settings->value("Script/dark").toString()); 228 | } else { 229 | notify("Error running script", 230 | "Koi tried to run your script, but no script file was selected", 231 | 0); 232 | } 233 | } 234 | } 235 | /* this updates the style of both the plasma shell and latte dock if it is 236 | * available It also restart krunner to force the theme on it 237 | */ 238 | void Utils::restartProcess() { 239 | if (settings->value("KvantumStyle/enabled").toBool()) { 240 | 241 | // restart plasmashell 242 | QStringList plasmashell = {"plasmashell"}; 243 | QProcess::execute("/usr/bin/kquitapp6", plasmashell); 244 | QProcess::startDetached("/usr/bin/kstart", plasmashell); 245 | } 246 | 247 | // stop krunner, it will be restarted when it is requested again 248 | QProcess::startDetached("/usr/bin/kquitapp6", {"krunner"}); 249 | } 250 | --------------------------------------------------------------------------------