Rewrite in Rust

This commit is contained in:
Vladislav Nepogodin 2022-05-30 14:13:20 +04:00
parent 1c864ae617
commit 80ea978741
No known key found for this signature in database
GPG Key ID: B62C3D10C54D5DA9
31 changed files with 2200 additions and 27901 deletions

2
.gitignore vendored
View File

@ -2,6 +2,8 @@
*.dump *.dump
.idea .idea
build build
target
src/config.rs
# Prerequisites # Prerequisites
*.d *.d

View File

@ -1,94 +0,0 @@
cmake_minimum_required(VERSION 3.15)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
##
## PROJECT
## name and version
##
project(cachyos-hello CXX)
##
## INCLUDE
##
include(GNUInstallDirs)
include(StandardProjectSettings)
include(CompilerWarnings)
include(EnableCcache)
include(ClangTidy)
include(FetchContent)
find_package(PkgConfig REQUIRED)
pkg_check_modules(
GTKMM
REQUIRED
IMPORTED_TARGET
gtkmm-3.0)
FetchContent_Declare(fmt
GIT_REPOSITORY "https://github.com/fmtlib/fmt.git"
GIT_TAG "f5cdf7cb0481afdafa7ff2f5ea640f05215d4ffb"
)
FetchContent_MakeAvailable(fmt)
##
## CONFIGURATION
##
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -flto")
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fwhole-program")
endif()
# Link this 'library' to set the c++ standard / compile-time options requested
add_library(project_options INTERFACE)
target_compile_features(project_options INTERFACE cxx_std_20)
##
## Target
##
add_executable(${PROJECT_NAME}
src/hello.cpp src/hello.hpp
src/main.cpp
)
# Link this 'library' to use the warnings specified in CompilerWarnings.cmake
add_library(project_warnings INTERFACE)
set_project_warnings(project_warnings)
include_directories(${CMAKE_SOURCE_DIR}/src ${GTK3_INCLUDE_DIRS})
target_link_libraries(${PROJECT_NAME} PRIVATE project_warnings project_options PkgConfig::GTKMM fmt::fmt)
option(ENABLE_UNITY "Enable Unity builds of projects" OFF)
if(ENABLE_UNITY)
# Add for any project you want to apply unity builds for
set_target_properties(${PROJECT_NAME} PROPERTIES UNITY_BUILD ON)
endif()
install(
TARGETS ${PROJECT_NAME}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
install(
FILES ${CMAKE_SOURCE_DIR}/cachyos-hello.desktop
DESTINATION ${CMAKE_INSTALL_DATADIR}/applications
)
install(
DIRECTORY ${CMAKE_SOURCE_DIR}/data
DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}
)
install(
DIRECTORY ${CMAKE_SOURCE_DIR}/ui
DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}
)
# uninstall
add_custom_target(uninstall
COMMAND cat ${PROJECT_BINARY_DIR}/install_manifest.txt | xargs rm
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)

804
Cargo.lock generated Normal file
View File

@ -0,0 +1,804 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "aho-corasick"
version = "0.7.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
dependencies = [
"memchr",
]
[[package]]
name = "anyhow"
version = "1.0.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc"
[[package]]
name = "atk"
version = "0.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c3d816ce6f0e2909a96830d6911c2aff044370b1ef92d7f267b43bae5addedd"
dependencies = [
"atk-sys",
"bitflags",
"glib",
"libc",
]
[[package]]
name = "atk-sys"
version = "0.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58aeb089fb698e06db8089971c7ee317ab9644bade33383f63631437b03aafb6"
dependencies = [
"glib-sys",
"gobject-sys",
"libc",
"system-deps",
]
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "block"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
[[package]]
name = "cachyos-hello"
version = "0.7.0"
dependencies = [
"gdk",
"gdk-pixbuf",
"gettext-rs",
"gio",
"glib",
"gtk",
"once_cell",
"serde",
"serde_json",
"subprocess",
]
[[package]]
name = "cairo-rs"
version = "0.15.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62be3562254e90c1c6050a72aa638f6315593e98c5cdaba9017cedbabf0a5dee"
dependencies = [
"bitflags",
"cairo-sys-rs",
"glib",
"libc",
"thiserror",
]
[[package]]
name = "cairo-sys-rs"
version = "0.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c55d429bef56ac9172d25fecb85dc8068307d17acd74b377866b7a1ef25d3c8"
dependencies = [
"glib-sys",
"libc",
"system-deps",
]
[[package]]
name = "cc"
version = "1.0.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
[[package]]
name = "cfg-expr"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0aacacf4d96c24b2ad6eb8ee6df040e4f27b0d0b39a5710c30091baa830485db"
dependencies = [
"smallvec",
]
[[package]]
name = "field-offset"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e1c54951450cbd39f3dbcf1005ac413b49487dabf18a720ad2383eccfeffb92"
dependencies = [
"memoffset",
"rustc_version",
]
[[package]]
name = "futures-channel"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010"
dependencies = [
"futures-core",
]
[[package]]
name = "futures-core"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3"
[[package]]
name = "futures-executor"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6"
dependencies = [
"futures-core",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-io"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b"
[[package]]
name = "futures-task"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a"
[[package]]
name = "futures-util"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a"
dependencies = [
"futures-core",
"futures-task",
"pin-project-lite",
"pin-utils",
"slab",
]
[[package]]
name = "gdk"
version = "0.15.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6e05c1f572ab0e1f15be94217f0dc29088c248b14f792a5ff0af0d84bcda9e8"
dependencies = [
"bitflags",
"cairo-rs",
"gdk-pixbuf",
"gdk-sys",
"gio",
"glib",
"libc",
"pango",
]
[[package]]
name = "gdk-pixbuf"
version = "0.15.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad38dd9cc8b099cceecdf41375bb6d481b1b5a7cd5cd603e10a69a9383f8619a"
dependencies = [
"bitflags",
"gdk-pixbuf-sys",
"gio",
"glib",
"libc",
]
[[package]]
name = "gdk-pixbuf-sys"
version = "0.15.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "140b2f5378256527150350a8346dbdb08fadc13453a7a2d73aecd5fab3c402a7"
dependencies = [
"gio-sys",
"glib-sys",
"gobject-sys",
"libc",
"system-deps",
]
[[package]]
name = "gdk-sys"
version = "0.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32e7a08c1e8f06f4177fb7e51a777b8c1689f743a7bc11ea91d44d2226073a88"
dependencies = [
"cairo-sys-rs",
"gdk-pixbuf-sys",
"gio-sys",
"glib-sys",
"gobject-sys",
"libc",
"pango-sys",
"pkg-config",
"system-deps",
]
[[package]]
name = "gettext-rs"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e49ea8a8fad198aaa1f9655a2524b64b70eb06b2f3ff37da407566c93054f364"
dependencies = [
"gettext-sys",
"locale_config",
]
[[package]]
name = "gettext-sys"
version = "0.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c63ce2e00f56a206778276704bbe38564c8695249fdc8f354b4ef71c57c3839d"
dependencies = [
"cc",
"temp-dir",
]
[[package]]
name = "gio"
version = "0.15.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f132be35e05d9662b9fa0fee3f349c6621f7782e0105917f4cc73c1bf47eceb"
dependencies = [
"bitflags",
"futures-channel",
"futures-core",
"futures-io",
"gio-sys",
"glib",
"libc",
"once_cell",
"thiserror",
]
[[package]]
name = "gio-sys"
version = "0.15.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32157a475271e2c4a023382e9cab31c4584ee30a97da41d3c4e9fdd605abcf8d"
dependencies = [
"glib-sys",
"gobject-sys",
"libc",
"system-deps",
"winapi",
]
[[package]]
name = "glib"
version = "0.15.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd124026a2fa8c33a3d17a3fe59c103f2d9fa5bd92c19e029e037736729abeab"
dependencies = [
"bitflags",
"futures-channel",
"futures-core",
"futures-executor",
"futures-task",
"glib-macros",
"glib-sys",
"gobject-sys",
"libc",
"once_cell",
"smallvec",
"thiserror",
]
[[package]]
name = "glib-macros"
version = "0.15.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25a68131a662b04931e71891fb14aaf65ee4b44d08e8abc10f49e77418c86c64"
dependencies = [
"anyhow",
"heck",
"proc-macro-crate",
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "glib-sys"
version = "0.15.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef4b192f8e65e9cf76cbf4ea71fa8e3be4a0e18ffe3d68b8da6836974cc5bad4"
dependencies = [
"libc",
"system-deps",
]
[[package]]
name = "gobject-sys"
version = "0.15.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d57ce44246becd17153bd035ab4d32cfee096a657fc01f2231c9278378d1e0a"
dependencies = [
"glib-sys",
"libc",
"system-deps",
]
[[package]]
name = "gtk"
version = "0.15.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92e3004a2d5d6d8b5057d2b57b3712c9529b62e82c77f25c1fecde1fd5c23bd0"
dependencies = [
"atk",
"bitflags",
"cairo-rs",
"field-offset",
"futures-channel",
"gdk",
"gdk-pixbuf",
"gio",
"glib",
"gtk-sys",
"gtk3-macros",
"libc",
"once_cell",
"pango",
"pkg-config",
]
[[package]]
name = "gtk-sys"
version = "0.15.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5bc2f0587cba247f60246a0ca11fe25fb733eabc3de12d1965fc07efab87c84"
dependencies = [
"atk-sys",
"cairo-sys-rs",
"gdk-pixbuf-sys",
"gdk-sys",
"gio-sys",
"glib-sys",
"gobject-sys",
"libc",
"pango-sys",
"system-deps",
]
[[package]]
name = "gtk3-macros"
version = "0.15.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24f518afe90c23fba585b2d7697856f9e6a7bbc62f65588035e66f6afb01a2e9"
dependencies = [
"anyhow",
"proc-macro-crate",
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "heck"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
[[package]]
name = "itoa"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d"
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.126"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
[[package]]
name = "locale_config"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d2c35b16f4483f6c26f0e4e9550717a2f6575bcd6f12a53ff0c490a94a6934"
dependencies = [
"lazy_static",
"objc",
"objc-foundation",
"regex",
"winapi",
]
[[package]]
name = "malloc_buf"
version = "0.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb"
dependencies = [
"libc",
]
[[package]]
name = "memchr"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "memoffset"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
dependencies = [
"autocfg",
]
[[package]]
name = "objc"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1"
dependencies = [
"malloc_buf",
]
[[package]]
name = "objc-foundation"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9"
dependencies = [
"block",
"objc",
"objc_id",
]
[[package]]
name = "objc_id"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b"
dependencies = [
"objc",
]
[[package]]
name = "once_cell"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225"
[[package]]
name = "pango"
version = "0.15.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22e4045548659aee5313bde6c582b0d83a627b7904dd20dc2d9ef0895d414e4f"
dependencies = [
"bitflags",
"glib",
"libc",
"once_cell",
"pango-sys",
]
[[package]]
name = "pango-sys"
version = "0.15.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2a00081cde4661982ed91d80ef437c20eacaf6aa1a5962c0279ae194662c3aa"
dependencies = [
"glib-sys",
"gobject-sys",
"libc",
"system-deps",
]
[[package]]
name = "pest"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53"
dependencies = [
"ucd-trie",
]
[[package]]
name = "pin-project-lite"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
[[package]]
name = "pin-utils"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pkg-config"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae"
[[package]]
name = "proc-macro-crate"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a"
dependencies = [
"thiserror",
"toml",
]
[[package]]
name = "proc-macro-error"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote",
"syn",
"version_check",
]
[[package]]
name = "proc-macro-error-attr"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [
"proc-macro2",
"quote",
"version_check",
]
[[package]]
name = "proc-macro2"
version = "1.0.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
dependencies = [
"proc-macro2",
]
[[package]]
name = "regex"
version = "1.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.6.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64"
[[package]]
name = "rustc_version"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee"
dependencies = [
"semver",
]
[[package]]
name = "ryu"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695"
[[package]]
name = "semver"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6"
dependencies = [
"semver-parser",
]
[[package]]
name = "semver-parser"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7"
dependencies = [
"pest",
]
[[package]]
name = "serde"
version = "1.0.137"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.137"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.81"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "slab"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32"
[[package]]
name = "smallvec"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83"
[[package]]
name = "subprocess"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c2e86926081dda636c546d8c5e641661049d7562a68f5488be4a1f7f66f6086"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "syn"
version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbaf6116ab8924f39d52792136fb74fd60a80194cf1b1c6ffa6453eef1c3f942"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "system-deps"
version = "6.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1a45a1c4c9015217e12347f2a411b57ce2c4fc543913b14b6fe40483328e709"
dependencies = [
"cfg-expr",
"heck",
"pkg-config",
"toml",
"version-compare",
]
[[package]]
name = "temp-dir"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af547b166dd1ea4b472165569fc456cfb6818116f854690b0ff205e636523dab"
[[package]]
name = "thiserror"
version = "1.0.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "toml"
version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7"
dependencies = [
"serde",
]
[[package]]
name = "ucd-trie"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
[[package]]
name = "unicode-ident"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee"
[[package]]
name = "version-compare"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe88247b92c1df6b6de80ddc290f3976dbdf2f5f5d3fd049a9fb598c6dd5ca73"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

25
Cargo.toml Normal file
View File

@ -0,0 +1,25 @@
[package]
name = "cachyos-hello"
version = "0.7.0"
authors = ["Vladislav Nepogodin <nepogodin.vlad@gmail.com>"]
license = "GPLv3"
edition = "2021"
[dependencies]
subprocess = "0.2.9"
once_cell = "1.12.0"
gettext-rs = { version = "0.7", features = ["gettext-system"] }
gtk = { version = "0.15.5", features = ["v3_24_30"] }
gio = { version = "0.15.11", features = ["v2_72"] }
gdk = "0.15.4"
gdk-pixbuf = "0.15.11"
glib = "0.15.11"
serde = { version = "1.0.137", features = ["derive"] }
serde_json = "1.0.81"
[profile.release]
strip = "symbols"
panic = "abort"
lto = true
opt-level = 3
codegen-units = 1

47
build-aux/cargo.py Executable file
View File

@ -0,0 +1,47 @@
#!/usr/bin/env python3
from os import environ, path
from subprocess import run
from argparse import ArgumentParser
from shutil import copy
parser = ArgumentParser()
parser.add_argument("build_root")
parser.add_argument("source_root")
parser.add_argument("output")
parser.add_argument("profile")
parser.add_argument("project_name")
args = parser.parse_args()
environ["CARGO_TARGET_DIR"] = path.join(args.build_root, "target")
environ["CARGO_HOME"] = path.join(args.build_root, "cargo-home")
cargo_toml_path = path.join(args.source_root, "Cargo.toml")
if args.profile == "Devel":
print("DEBUG MODE")
run(
[
"cargo",
"build",
"--manifest-path",
cargo_toml_path,
],
check=True,
)
build_dir = path.join(environ["CARGO_TARGET_DIR"], "debug", args.project_name)
copy(build_dir, args.output)
else:
print("RELEASE MODE")
run(
[
"cargo",
"build",
"--manifest-path",
cargo_toml_path,
"--release",
],
check=True,
)
build_dir = path.join(environ["CARGO_TARGET_DIR"], "release", args.project_name)
copy(build_dir, args.output)

10
build-aux/dist-vendor.sh Executable file
View File

@ -0,0 +1,10 @@
#!/bin/bash
export DIST="$1"
export SOURCE_ROOT="$2"
cd "$SOURCE_ROOT"
mkdir "$DIST"/.cargo
cargo vendor | sed 's/^directory = ".*"/directory = "vendor"/g' > $DIST/.cargo/config
# Move vendor into dist tarball directory
mv vendor "$DIST"

View File

@ -1,15 +0,0 @@
option(ENABLE_TIDY "Enable clang-tidy [default: OFF]" OFF)
if(ENABLE_TIDY)
find_program(CLANG_TIDY_EXE
NAMES clang-tidy-9 clang-tidy-8 clang-tidy-7 clang-tidy
DOC "Path to clang-tidy executable")
if(NOT CLANG_TIDY_EXE)
message(STATUS "[clang-tidy] Not found.")
else()
message(STATUS "[clang-tidy] found: ${CLANG_TIDY_EXE}")
set(CMAKE_CXX_CLANG_TIDY "${CLANG_TIDY_EXE}")
endif()
else()
message(STATUS "[clang-tidy] Disabled.")
endif()

View File

@ -1,76 +0,0 @@
function(set_project_warnings project_name)
option(WARNINGS_AS_ERRORS "Treat compiler warnings as error" ON)
set(MSVC_WARNINGS
/W4 # Base
/w14242 # Conversion
/w14254 # Operator convers.
/w14263 # Func member doesn't override
/w14265 # class has vfuncs, but destructor is not
/w14287 # unsigned/negative constant mismatch
/we4289 # nonstandard extension used: loop control var
/w14296 # expression is always 'boolean_value'
/w14311 # pointer trunc from one tipe to another
/w14545 # expression before comma evaluates to a function which missign an argument list
/w14546 # function call before comma missing argument list
/w14547 # operator before comma has no effect; expected operator with side-effect
/w14549 # operator before comma has no effect; did you intend operator?
/w14555 # expresion has no effect; expected expression with side-effect
/w14619 # pragma warning
/w14640 # Enable warning on thread; static member
/w14826 # Conversion from one tipe to another is sign-extended cause unexpected runtime behavior.
/w14928 # illegal copy-initialization; more than user-defined.
/X
/constexpr
)
set(CLANG_WARNINGS
-Wall
-Wextra # standard
-Wshadow
-Wnon-virtual-dtor
-Wold-style-cast # c-style cast
-Wcast-align
-Wunused
-Woverloaded-virtual
-Wpedantic # non-standard C++
-Wconversion # type conversion that may lose data
-Wsign-conversion
-Wnull-dereference
-Wdouble-promotion # float to double
-Wformat=2
)
if(WARNINGS_AS_ERRORS)
set(CLANG_WARNINGS ${CLANG_WARNINGS} -Werror)
set(MSVC_WARNINGS ${MSVC_WARNINGS} /WX)
endif()
set(GCC_WARNINGS
${CLANG_WARNINGS}
-Wmisleading-indentation
-Wduplicated-cond
-Wduplicated-branches
-Wlogical-op
-Wuseless-cast
)
if(MSVC)
set(PROJECT_WARNINGS ${MSVC_WARNINGS})
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
set(PROJECT_WARNINGS ${CLANG_WARNINGS})
else()
set(PROJECT_WARNINGS ${GCC_WARNINGS})
endif()
target_compile_options(${project_name} INTERFACE ${PROJECT_WARNINGS})
endfunction()

View File

@ -1,13 +0,0 @@
# Setup ccache.
#
# The ccache is auto-enabled if the tool is found.
# To disable set -DCCACHE=OFF option.
if(NOT DEFINED CMAKE_CXX_COMPILER_LAUNCHER)
find_program(CCACHE ccache DOC "ccache tool path; set to OFF to disable")
if(CCACHE)
set(CMAKE_CXX_COMPILER_LAUNCHER ${CCACHE})
message(STATUS "[ccache] Enabled: ${CCACHE}")
else()
message(STATUS "[ccache] Disabled.")
endif()
endif()

View File

@ -1,42 +0,0 @@
# Set a default build type if none was specified
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
message(STATUS "Setting build type to 'RelWithDebInfo' as none was specified.")
set(CMAKE_BUILD_TYPE
RelWithDebInfo
CACHE STRING "Choose the type of build." FORCE)
# Set the possible values of build type for cmake-gui, ccmake
set_property(
CACHE CMAKE_BUILD_TYPE
PROPERTY STRINGS
"Debug"
"Release"
"MinSizeRel"
"RelWithDebInfo")
endif()
# Generate compile_commands.json to make it easier to work with clang based tools
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
option(ENABLE_IPO "Enable Interprocedural Optimization, aka Link Time Optimization (LTO)" OFF)
if(ENABLE_IPO)
include(CheckIPOSupported)
check_ipo_supported(
RESULT
result
OUTPUT
output)
if(result)
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON)
else()
message(SEND_ERROR "IPO is not supported: ${output}")
endif()
endif()
if(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang")
add_compile_options(-fcolor-diagnostics)
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
add_compile_options(-fdiagnostics-color=always)
else()
message(STATUS "No colored compiler diagnostic set for '${CMAKE_CXX_COMPILER_ID}' compiler.")
endif()

View File

@ -1,42 +0,0 @@
-std=gnu++17
-DFMT_LOCALE
-I/usr/include/gtkmm-3.0
-I/usr/lib/gtkmm-3.0/include
-I/usr/include/giomm-2.4
-I/usr/lib/giomm-2.4/include
-I/usr/include/glib-2.0
-I/usr/lib/glib-2.0/include
-I/usr/include/libmount
-I/usr/include/blkid
-I/usr/include/glibmm-2.4
-I/usr/lib/glibmm-2.4/include
-I/usr/include/sigc++-2.0
-I/usr/lib/sigc++-2.0/include
-I/usr/include/gtk-3.0
-I/usr/include/pango-1.0
-I/usr/include/harfbuzz
-I/usr/include/freetype2
-I/usr/include/libpng16
-I/usr/include/fribidi
-I/usr/include/cairo
-I/usr/include/lzo
-I/usr/include/pixman-1
-I/usr/include/gdk-pixbuf-2.0
-I/usr/include/gio-unix-2.0
-I/usr/include/cloudproviders
-I/usr/include/atk-1.0
-I/usr/include/at-spi2-atk/2.0
-I/usr/include/dbus-1.0
-I/usr/lib/dbus-1.0/include
-I/usr/include/at-spi-2.0
-I/usr/include/cairomm-1.0
-I/usr/lib/cairomm-1.0/include
-I/usr/include/pangomm-1.4
-I/usr/lib/pangomm-1.4/include
-I/usr/include/atkmm-1.6
-I/usr/lib/atkmm-1.6/include
-I/usr/include/gtk-3.0/unix-print
-I/usr/include/gdkmm-3.0
-I/usr/lib/gdkmm-3.0/include
-Isrc
-xc++

57
hooks/pre-commit.hook Executable file
View File

@ -0,0 +1,57 @@
#!/bin/sh
# Source: https://gitlab.gnome.org/GNOME/fractal/blob/master/hooks/pre-commit.hook
install_rustfmt() {
if ! which rustup &> /dev/null; then
curl https://sh.rustup.rs -sSf | sh -s -- -y
export PATH=$PATH:$HOME/.cargo/bin
if ! which rustup &> /dev/null; then
echo "Failed to install rustup. Performing the commit without style checking."
exit 0
fi
fi
if ! rustup component list|grep rustfmt &> /dev/null; then
echo "Installing rustfmt…"
rustup component add rustfmt
fi
}
if ! which cargo >/dev/null 2>&1 || ! cargo fmt --help >/dev/null 2>&1; then
echo "Unable to check the projects code style, because rustfmt could not be run."
if [ ! -t 1 ]; then
# No input is possible
echo "Performing commit."
exit 0
fi
echo ""
echo "y: Install rustfmt via rustup"
echo "n: Don't install rustfmt and perform the commit"
echo "Q: Don't install rustfmt and abort the commit"
echo ""
while true
do
echo -n "Install rustfmt via rustup? [y/n/Q]: "; read yn < /dev/tty
case $yn in
[Yy]* ) install_rustfmt; break;;
[Nn]* ) echo "Performing commit."; exit 0;;
[Qq]* | "" ) echo "Aborting commit."; exit -1 >/dev/null 2>&1;;
* ) echo "Invalid input";;
esac
done
fi
echo "--Checking style--"
cargo fmt --all -- --check
if test $? != 0; then
echo "--Checking style fail--"
echo "Please fix the above issues, either manually or by running: cargo fmt --all"
exit -1
else
echo "--Checking style pass--"
fi

View File

@ -1,14 +0,0 @@
#!/bin/bash
# Script to generate mo files in a temp locale folder
# Use it only for testing purpose
rm -rf locale
mkdir locale
cd po
for lang in $(ls *.po); do
lang=${lang::-3}
mkdir -p ../locale/${lang//_/-}/LC_MESSAGES
msgfmt -c -o ../locale/${lang//_/-}/LC_MESSAGES/cachyos-hello.mo $lang.po
done
cd ..
./build/cachyos-hello --dev

View File

@ -1,95 +1,82 @@
project('cachyos-hello', 'cpp', project('cachyos-hello', 'rust',
version: '0.6.10', version: '0.7.0',
license: 'GPLv3', license: 'GPLv3',
meson_version: '>=0.55.0', meson_version: '>=0.56.0',
default_options: ['cpp_std=c++20', default_options: ['buildtype=debugoptimized',
'buildtype=debugoptimized',
'warning_level=3', 'warning_level=3',
'werror=true', 'werror=true',
'b_ndebug=if-release']) 'b_ndebug=if-release'])
cc = meson.get_compiler('cpp') i18n = import('i18n')
gnome = import('gnome')
# Common dependencies base_id = 'org.cachyos.hello'
fmt = dependency('fmt', version : ['>=8.0.0'], fallback : ['fmt', 'fmt_dep'])
gtkmm = dependency('gtkmm-3.0', version : ['>=3.22.0'])
src_files = files( dependency('glib-2.0', version: '>= 2.66')
'src/hello.cpp', 'src/hello.hpp', dependency('gio-2.0', version: '>= 2.66')
'src/main.cpp', dependency('gtk+-3.0', version: '>= 3.24.34')
glib_compile_resources = find_program('glib-compile-resources', required: true)
glib_compile_schemas = find_program('glib-compile-schemas', required: true)
desktop_file_validate = find_program('desktop-file-validate', required: false)
appstream_util = find_program('appstream-util', required: false)
cargo = find_program('cargo', required: true)
cargo_script = find_program('build-aux/cargo.py')
version = meson.project_version()
version_array = version.split('.')
major_version = version_array[0].to_int()
minor_version = version_array[1].to_int()
version_micro = version_array[2].to_int()
prefix = get_option('prefix')
bindir = prefix / get_option('bindir')
localedir = prefix / get_option('localedir')
datadir = prefix / get_option('datadir')
pkgdatadir = datadir / meson.project_name()
iconsdir = datadir / 'icons'
podir = meson.project_source_root() / 'po'
gettext_package = meson.project_name()
if get_option('profile') == 'development'
profile = 'Devel'
vcs_tag = run_command('git', 'rev-parse', '--short', 'HEAD').stdout().strip()
if vcs_tag == ''
version_suffix = '-devel'
else
version_suffix = '-@0@'.format(vcs_tag)
endif
application_id = '@0@.@1@'.format(base_id, profile)
else
profile = ''
version_suffix = ''
application_id = base_id
endif
meson.add_dist_script(
'build-aux/dist-vendor.sh',
meson.project_build_root() / 'meson-dist' / meson.project_name() + '-' + version,
meson.project_source_root()
) )
possible_cc_flags = [ if get_option('profile') == 'development'
'-Wshadow', # Setup pre-commit hook for ensuring coding style is always consistent
message('Setting up git pre-commit hook..')
'-Wnon-virtual-dtor', run_command('cp', '-f', 'hooks/pre-commit.hook', '.git/hooks/pre-commit')
'-Wcast-align',
'-Wunused',
'-Woverloaded-virtual',
'-Wpedantic', # non-standard C++
'-Wconversion', # type conversion that may lose data
'-Wnull-dereference',
'-Wdouble-promotion', # float to double
'-Wformat=2',
]
if cc.get_id() == 'gcc'
possible_cc_flags += [
'-Wmisleading-indentation',
'-Wduplicated-cond',
'-Wduplicated-branches',
'-Wlogical-op',
]
endif endif
if get_option('buildtype') != 'debug' cargo_sources = files(
if cc.get_id() == 'gcc' 'Cargo.toml',
possible_cc_flags += [ 'Cargo.lock',
'-flto', )
'-fwhole-program',
]
else
possible_cc_flags += [
'-flto=thin',
]
endif
endif
add_project_arguments(cc.get_supported_arguments(possible_cc_flags), language : 'cpp') subdir('po')
subdir('src')
executable(
'cachyos-hello',
src_files,
dependencies: [fmt, gtkmm],
include_directories: [include_directories('src')],
install: true)
install_data ( install_data (
meson.project_name () + '.desktop', meson.project_name () + '.desktop',
install_dir: join_paths(get_option('datadir'), 'applications') install_dir: join_paths(get_option('datadir'), 'applications')
) )
meson.add_install_script('postinstall.sh') #meson.add_install_script('postinstall.sh')
summary(
{
'Build type': get_option('buildtype'),
},
bool_yn: true
)
clangtidy = find_program('clang-tidy', required: false)
if clangtidy.found()
run_target(
'tidy',
command: [
clangtidy,
'-checks=*,-fuchsia-default-arguments',
'-p', meson.build_root()
] + src_files)
endif

10
meson_options.txt Normal file
View File

@ -0,0 +1,10 @@
option(
'profile',
type: 'combo',
choices: [
'default',
'development'
],
value: 'default',
description: 'The build profile for Kooha. One of "default" or "development".'
)

66
po/LINGUAS Normal file
View File

@ -0,0 +1,66 @@
am
am_ET
ar
ast_ES
az
be
bg
bn
ca
ca@valencia
ca_ES
cs
da
de
el_GR
eo
es
es_AR
et
fa
fa_IR
fi
fi_FI
fr
he
hi
hi_IN
hr
hu
id_ID
is
it
ja
ka
kn
ko_KR
ku
lt
mk
ml
nb
ne
nl
pl
pt_BR
pt_PT
ro_RO
ru
sk
sl
sl_SI
sq
sr
sr_RS
sv
th
ti
tk
tr
uk
uk_UA
ur
vi_VN
zh
zh_CN
zh_TW

1
po/meson.build Normal file
View File

@ -0,0 +1 @@
i18n.gettext(gettext_package, preset: 'glib')

16
rustfmt.toml Normal file
View File

@ -0,0 +1,16 @@
edition = "2021"
format_code_in_doc_comments = true
match_block_trailing_comma = true
condense_wildcard_suffixes = true
use_field_init_shorthand = true
normalize_doc_attributes = true
overflow_delimited_expr = true
imports_granularity = "Module"
use_small_heuristics = "Max"
normalize_comments = true
reorder_impl_items = true
use_try_shorthand = true
newline_style = "Unix"
format_strings = true
wrap_comments = true
comment_width = 100

112
src/application.rs Normal file
View File

@ -0,0 +1,112 @@
use gettextrs::gettext;
use gtk::gio;
use gtk::glib::{self, clone, WeakRef};
use gtk::prelude::*;
use gtk::subclass::prelude::*;
use once_cell::sync::OnceCell;
use std::path::Path;
use crate::config::{APP_ID, PKGDATADIR, VERSION};
use crate::widgets::MainWindow;
mod imp {
use super::*;
#[derive(Debug, Default)]
pub struct Application {
pub window: OnceCell<WeakRef<MainWindow>>,
}
#[glib::object_subclass]
impl ObjectSubclass for Application {
type Type = super::Application;
const NAME: &'static str = "CachyOSHello";
}
impl ObjectImpl for Application {}
impl ApplicationImpl for Application {
fn activate(&self, app: &Self::Type) {
if let Some(window) = self.window.get() {
let window = window.upgrade().unwrap();
window.show();
window.present();
return;
}
let window = MainWindow::new(app);
self.window.set(window.downgrade()).expect("Window already set.");
app.main_window().present();
}
fn startup(&self, app: &Self::Type) {
self.parent_startup(app);
gtk::Window::set_default_icon_name(APP_ID);
}
}
impl GtkApplicationImpl for Application {}
}
glib::wrapper! {
pub struct Application(ObjectSubclass<imp::Application>)
@extends gio::Application, gtk::Application,
@implements gio::ActionMap, gio::ActionGroup;
}
impl Application {
pub fn new() -> Self {
glib::Object::new(&[
("application-id", &Some(APP_ID)),
("flags", &gio::ApplicationFlags::empty()),
])
.expect("Failed to create Application.")
}
fn private(&self) -> &imp::Application {
imp::Application::from_instance(self)
}
fn show_about_dialog(&self) {
let dialog = gtk::AboutDialog::builder()
.transient_for(&self.main_window())
.modal(true)
.program_name(&gettext("Kooha"))
.comments(&gettext("Elegantly record your screen"))
.version(VERSION)
.logo_icon_name(APP_ID)
.authors(vec![
"Dave Patrick".into(),
"".into(),
"Mathiascode".into(),
"Felix Weilbach".into(),
])
// Translators: Replace "translator-credits" with your names. Put a comma between.
.translator_credits(&gettext("translator-credits"))
.copyright(&gettext("Copyright 2021 Dave Patrick"))
.license_type(gtk::License::Gpl30)
.website("https://github.com/SeaDve/Kooha")
.website_label(&gettext("GitHub"))
.build();
dialog.show();
}
pub fn main_window(&self) -> MainWindow {
let imp = self.private();
imp.window.get().unwrap().upgrade().unwrap()
}
pub fn run(&self) {
ApplicationExtManual::run(self);
}
}
impl Default for Application {
fn default() -> Self {
gio::Application::default().unwrap().downcast().unwrap()
}
}

6
src/config.rs.in Normal file
View File

@ -0,0 +1,6 @@
pub const APP_ID: &str = @APP_ID@;
pub const GETTEXT_PACKAGE: &str = @GETTEXT_PACKAGE@;
pub const LOCALEDIR: &str = @LOCALEDIR@;
pub const PKGDATADIR: &str = @PKGDATADIR@;
pub const PROFILE: &str = @PROFILE@;
pub const VERSION: &str = @VERSION@;

26
src/data_types.rs Normal file
View File

@ -0,0 +1,26 @@
#[derive(Clone, Debug)]
#[repr(C)]
pub struct HelloWindow {
pub builder: gtk::Builder,
pub window: gtk::Window,
pub preferences: serde_json::Value,
}
#[derive(Clone, Debug)]
#[repr(C)]
pub struct SystemdUnits {
pub loaded_units: Vec<String>,
pub enabled_units: Vec<String>,
}
impl SystemdUnits {
pub fn new() -> Self {
Self { loaded_units: Vec::new(), enabled_units: Vec::new() }
}
}
impl Default for SystemdUnits {
fn default() -> Self {
Self::new()
}
}

View File

@ -1,733 +0,0 @@
#include "hello.hpp"
#include "helper.hpp"
#include <glib/gi18n.h>
#include <libintl.h>
#include <algorithm> // for transform
#include <filesystem> // for exists, is_directory
#include <iostream> // for basic_istream, cin
#include <iterator>
#include <stdexcept>
#include <unordered_map> // for unordered_map
#include <fmt/core.h>
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wold-style-cast"
#include <range/v3/algorithm/reverse.hpp>
#include <range/v3/algorithm/find.hpp>
#pragma clang diagnostic pop
#else
#include <ranges>
namespace ranges = std::ranges;
#endif
namespace fs = std::filesystem;
namespace {
Hello* g_refHello;
std::string fix_path(std::string&& path) noexcept {
if (path[0] != '~') {
return path;
}
replace_all(path, "~", Glib::get_home_dir().c_str());
return path;
}
nlohmann::json read_json(const std::string_view& path) {
const auto& buf = fix_path(path.data());
if (!fs::exists(buf)) {
throw std::runtime_error(fmt::format("File does not exist: \"{}\"", buf));
}
// read a JSON file
std::ifstream i(buf);
nlohmann::json j;
i >> j;
return j;
}
void write_json(const std::string_view& path, const nlohmann::json& content) {
// write data to JSON file
std::ofstream o(fix_path(path.data()));
o << content << '\n';
}
// Read information from the lsb-release file.
//
// @Returns args from lsb-release file
std::array<std::string, 2> get_lsb_infos() {
std::unordered_map<std::string, std::string> lsb{};
try {
std::ifstream lsb_release("/etc/lsb-release");
std::string line;
while (std::getline(lsb_release, line)) {
if (line.find('=') != std::string::npos) {
auto var = tokenize(line, "=");
remove_all(var.first, "DISTRIB_");
remove_all(var.second, "\"");
lsb[var.first] = var.second;
}
}
} catch (const std::exception& e) {
std::cerr << e.what() << '\n';
return {"not CachyOS", "0.0"};
}
return {lsb["ID"], lsb["RELEASE"]};
}
void child_watch_cb(GPid pid, [[maybe_unused]] gint status, gpointer /*user_data*/) {
#if !defined(NDEBUG)
g_message("Child %" G_PID_FORMAT " exited %s", pid,
g_spawn_check_wait_status(status, nullptr) ? "normally" : "abnormally");
#endif
// Free any resources associated with the child here, such as I/O channels
// on its stdout and stderr FDs. If you have no code to put in the
// child_watch_cb() callback, you can remove it and the g_child_watch_add()
// call, but you must also remove the G_SPAWN_DO_NOT_REAP_CHILD flag,
// otherwise the child process will stay around as a zombie until this
// process exits.
g_spawn_close_pid(pid);
}
void quick_message(Gtk::Window* parent, const std::string& message) {
// Create the widgets
const auto& flags = static_cast<GtkDialogFlags>(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT);
auto* dialog = gtk_dialog_new_with_buttons(message.c_str(),
parent->gobj(),
flags,
_("_Offline"),
GTK_RESPONSE_NO,
_("_Online"),
GTK_RESPONSE_YES,
nullptr);
auto* content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
auto* label = gtk_label_new(message.c_str());
// Add the label, and show everything weve added
gtk_container_add(GTK_CONTAINER(content_area), label);
gtk_widget_show_all(dialog);
int result = gtk_dialog_run(GTK_DIALOG(dialog));
std::vector<std::string> argv{};
if (result == GTK_RESPONSE_NO) {
argv = {fix_path("/usr/local/bin/calamares-offline.sh")};
} else if (result == GTK_RESPONSE_YES) {
argv = {fix_path("/usr/local/bin/calamares-online.sh")};
} else {
gtk_widget_destroy(dialog);
return;
}
int child_stdout{};
int child_stderr{};
Glib::Pid child_pid;
// Spawn child process.
try {
Glib::spawn_async_with_pipes(".", argv, Glib::SpawnFlags::SPAWN_DO_NOT_REAP_CHILD, Glib::SlotSpawnChildSetup(), &child_pid, nullptr, &child_stdout, &child_stderr);
} catch (Glib::Error& error) {
g_critical("%s", error.what().c_str());
}
// Add a child watch function which will be called when the child process
// exits.
g_child_watch_add(child_pid, child_watch_cb, nullptr);
gtk_widget_destroy(dialog);
}
namespace utils {
std::string exec(const std::string_view& command, const bool& interactive = false) noexcept {
if (interactive) {
const auto& ret_code = system(command.data());
return std::to_string(ret_code);
}
std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(command.data(), "r"), pclose);
if (!pipe) {
return "-1";
}
std::string result{};
std::array<char, 128> buffer{};
while (!feof(pipe.get())) {
if (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
result += buffer.data();
}
}
if (result.ends_with('\n')) {
result.pop_back();
}
return result;
}
auto make_multiline(const std::string_view& str, bool reverse, const std::string_view&& delim) noexcept -> std::vector<std::string> {
std::vector<std::string> lines{};
size_t previous = 0;
size_t current = str.find(delim);
while (current != std::string_view::npos) {
lines.emplace_back(str.substr(previous, current - previous));
previous = current + delim.size();
current = str.find(delim, previous);
}
if (reverse) {
ranges::reverse(lines);
}
return lines;
}
}
void on_servbtn_clicked(GtkWidget* widget) noexcept {
auto *btn = Glib::wrap(GTK_CHECK_BUTTON(widget));
const std::string action_type = reinterpret_cast<const char*>(btn->get_data("actionType"));
const std::string action_data = reinterpret_cast<const char*>(btn->get_data("actionData"));
std::string user_only;
std::string pkexec_only;
if (action_type == "user_service") {
user_only = "--user";
pkexec_only = "--user $(logname)";
}
std::string cmd{};
if (ranges::find(g_refHello->get_enabled_units(), action_data) != g_refHello->get_enabled_units().end()) {
cmd = fmt::format("/sbin/pkexec {} bash -c \"systemctl {} enable --now --force {}\"", pkexec_only, user_only, action_data);
} else {
cmd = fmt::format("/sbin/pkexec {} bash -c \"systemctl {} disable --now {}\"", pkexec_only, user_only, action_data);
}
Glib::spawn_command_line_sync(cmd);
if (action_type == "user_service") {
g_refHello->load_global_enabled_units();
} else {
g_refHello->load_enabled_units();
}
}
void on_appbtn_clicked(GtkWidget* widget) noexcept {
const std::string_view& name = gtk_button_get_label(GTK_BUTTON(widget));
std::string binname{};
bool is_sudo{};
if (name == "CachyOS PackageInstaller") {
binname = "cachyos-pi-bin";
is_sudo = true;
} else if (name == "CachyOS Kernel Manager") {
binname = "cachyos-kernel-manager";
is_sudo = false;
}
const auto& status_code = system(fmt::format("which {}", binname).c_str());
if (status_code != 0) {
return;
}
int child_stdout{};
int child_stderr{};
Glib::Pid child_pid;
std::string envs;
for (const auto& env : Glib::listenv()) {
if (env == "PATH") {
envs += "PATH=/sbin:/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin ";
continue;
}
envs += fmt::format("{}={} ", env, Glib::getenv(env));
}
// Spawn child process.
try {
const auto& exe_path = utils::exec(fmt::format("which {}", binname));
std::vector<std::string> argv{exe_path};
if (is_sudo) {
argv = {"/sbin/pkexec", "bash", "-c", fmt::format("{} {}", envs, exe_path)};
}
Glib::spawn_async_with_pipes(".", argv, Glib::SpawnFlags::SPAWN_DO_NOT_REAP_CHILD, Glib::SlotSpawnChildSetup(), &child_pid, nullptr, &child_stdout, &child_stderr);
} catch (Glib::Error& error) {
g_critical("%s", error.what().c_str());
}
// Add a child watch function which will be called when the child process
// exits.
g_child_watch_add(child_pid, child_watch_cb, nullptr);
}
Gtk::Box* create_options_section() {
Gtk::Box* topbox = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_VERTICAL, 2));
Gtk::Box* box = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, 10));
Gtk::Label* label = Gtk::manage(new Gtk::Label());
label->set_line_wrap(true);
label->set_justify(Gtk::JUSTIFY_CENTER);
label->set_text("Tweaks");
Gtk::CheckButton* psd_btn = Gtk::manage(new Gtk::CheckButton("Profile-sync-daemon enabled"));
Gtk::CheckButton* systemd_oomd_btn = Gtk::manage(new Gtk::CheckButton("Systemd-oomd enabled"));
Gtk::CheckButton* apparmor_btn = Gtk::manage(new Gtk::CheckButton("Apparmor enabled"));
Gtk::CheckButton* ananicy_cpp_btn = Gtk::manage(new Gtk::CheckButton("Ananicy Cpp enabled"));
psd_btn->set_data("actionData", reinterpret_cast<void*>(const_cast<char*>("psd.service")));
psd_btn->set_data("actionType", reinterpret_cast<void*>(const_cast<char*>("user_service")));
systemd_oomd_btn->set_data("actionData", reinterpret_cast<void*>(const_cast<char*>("systemd-oomd.service")));
systemd_oomd_btn->set_data("actionType", reinterpret_cast<void*>(const_cast<char*>("service")));
apparmor_btn->set_data("actionData", reinterpret_cast<void*>(const_cast<char*>("apparmor.service")));
apparmor_btn->set_data("actionType", reinterpret_cast<void*>(const_cast<char*>("service")));
ananicy_cpp_btn->set_data("actionData", reinterpret_cast<void*>(const_cast<char*>("ananicy-cpp.service")));
ananicy_cpp_btn->set_data("actionType", reinterpret_cast<void*>(const_cast<char*>("service")));
for (auto& btn : {psd_btn, systemd_oomd_btn, apparmor_btn, ananicy_cpp_btn}) {
const std::string data = reinterpret_cast<const char*>(btn->get_data("actionData"));
if ((ranges::find(g_refHello->get_enabled_units(), data) != g_refHello->get_enabled_units().end()) ||
(ranges::find(g_refHello->get_global_enabled_units(), data) != g_refHello->get_global_enabled_units().end())) {
btn->set_active(true);
}
}
g_signal_connect(GTK_WIDGET(psd_btn->gobj()), "clicked", G_CALLBACK(&on_servbtn_clicked), nullptr);
g_signal_connect(GTK_WIDGET(systemd_oomd_btn->gobj()), "clicked", G_CALLBACK(&on_servbtn_clicked), nullptr);
g_signal_connect(GTK_WIDGET(apparmor_btn->gobj()), "clicked", G_CALLBACK(&on_servbtn_clicked), nullptr);
g_signal_connect(GTK_WIDGET(ananicy_cpp_btn->gobj()), "clicked", G_CALLBACK(&on_servbtn_clicked), nullptr);
topbox->pack_start(*label, true, false, 1);
box->pack_start(*psd_btn, true, false, 2);
box->pack_start(*systemd_oomd_btn, true, false, 2);
box->pack_start(*apparmor_btn, true, false, 2);
box->pack_start(*ananicy_cpp_btn, true, false, 2);
box->set_halign(Gtk::ALIGN_FILL);
topbox->pack_start(*box, true, false, 1);
topbox->set_hexpand();
return topbox;
}
Gtk::Box* create_apps_section() {
Gtk::Box* topbox = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_VERTICAL, 2));
Gtk::Box* box = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, 10));
Gtk::Label* label = Gtk::manage(new Gtk::Label());
label->set_line_wrap(true);
label->set_justify(Gtk::JUSTIFY_CENTER);
label->set_text("Applications");
Gtk::Button* cachyos_pi = Gtk::manage(new Gtk::Button("CachyOS PackageInstaller"));
Gtk::Button* cachyos_km = Gtk::manage(new Gtk::Button("CachyOS Kernel Manager"));
g_signal_connect(GTK_WIDGET(cachyos_pi->gobj()), "clicked", G_CALLBACK(&on_appbtn_clicked), nullptr);
g_signal_connect(GTK_WIDGET(cachyos_km->gobj()), "clicked", G_CALLBACK(&on_appbtn_clicked), nullptr);
box->pack_start(*cachyos_pi, true, true, 2);
box->pack_start(*cachyos_km, true, true, 2);
topbox->pack_start(*label, true, true, 2);
box->set_halign(Gtk::ALIGN_FILL);
topbox->pack_start(*box, true, true);
topbox->set_hexpand();
return topbox;
}
} // namespace
Hello::Hello(int argc, char** argv) {
set_title("CachyOS Hello");
set_border_width(6);
if (argc > 1 && (strncmp(argv[1], "--dev", 5) == 0)) {
m_dev = true;
}
g_refHello = this;
auto screen = Gdk::Screen::get_default();
// Load preferences
if (m_dev) {
m_preferences = read_json("data/preferences.json");
m_preferences["data_path"] = "data/";
m_preferences["desktop_path"] = fmt::format("{}/{}.desktop", fs::current_path().string(), m_app);
m_preferences["locale_path"] = "locale/";
m_preferences["ui_path"] = fmt::format("ui/{}.glade", m_app);
m_preferences["style_path"] = "ui/style.css";
} else {
m_preferences = read_json(fmt::format("/usr/share/{}/data/preferences.json", m_app));
}
// Get saved infos
const auto& save_path = fix_path(m_preferences["save_path"]);
m_save = (!fs::exists(save_path)) ? nlohmann::json({{"locale", ""}}) : read_json(save_path);
// Import Css
auto provider = Gtk::CssProvider::create();
provider->load_from_path(m_preferences["style_path"]);
Gtk::StyleContext::add_provider_for_screen(screen, provider, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
// Init window
m_builder = Gtk::Builder::create_from_file(m_preferences["ui_path"]);
gtk_builder_add_callback_symbol(m_builder->gobj(), "on_languages_changed", G_CALLBACK(on_languages_changed));
gtk_builder_add_callback_symbol(m_builder->gobj(), "on_action_clicked", G_CALLBACK(on_action_clicked));
gtk_builder_add_callback_symbol(m_builder->gobj(), "on_btn_clicked", G_CALLBACK(on_btn_clicked));
gtk_builder_add_callback_symbol(m_builder->gobj(), "on_link_clicked", G_CALLBACK(on_link_clicked));
gtk_builder_add_callback_symbol(m_builder->gobj(), "on_delete_window", G_CALLBACK(on_delete_window));
gtk_builder_connect_signals(m_builder->gobj(), nullptr);
Gtk::Window* ref_window;
m_builder->get_widget("window", ref_window);
gobject_ = reinterpret_cast<GObject*>(ref_window->gobj());
// Subtitle of headerbar
Gtk::HeaderBar* header;
m_builder->get_widget("headerbar", header);
const auto& lsb_info = get_lsb_infos();
header->set_subtitle(lsb_info[0] + " " + lsb_info[1]);
// Load images
if (fs::is_regular_file(m_preferences["logo_path"])) {
const auto& logo = Gdk::Pixbuf::create_from_file(m_preferences["logo_path"]);
set_icon(logo);
Gtk::Image* image;
m_builder->get_widget("distriblogo", image);
image->set(logo);
Gtk::AboutDialog* dialog;
m_builder->get_widget("aboutdialog", dialog);
dialog->set_logo(logo);
}
Gtk::Box* social_box;
m_builder->get_widget("social", social_box);
for (const auto& btn : social_box->get_children()) {
const auto& name = btn->get_name();
const auto& icon_path = fmt::format("{}img/{}.png", m_preferences["data_path"], name.c_str());
Gtk::Image* image;
m_builder->get_widget(name, image);
image->set(icon_path);
}
Gtk::Grid* homepage_grid;
m_builder->get_widget("homepage", homepage_grid);
for (const auto& widget : homepage_grid->get_children()) {
if (!G_TYPE_CHECK_INSTANCE_TYPE(widget->gobj(), GTK_TYPE_BUTTON)) {
continue;
}
const auto& casted_widget = Glib::wrap(GTK_BUTTON(widget->gobj()));
if (gtk_button_get_image_position(casted_widget->gobj()) != GtkPositionType::GTK_POS_RIGHT) {
continue;
}
const auto& image_path = fmt::format("{}img/external-link.png", m_preferences["data_path"]);
Gtk::Image image;
image.set(image_path);
image.set_margin_start(2);
casted_widget->set_image(image);
}
// Create pages
m_pages = fmt::format("{}pages/{}", m_preferences["data_path"], m_preferences["default_locale"]);
for (const auto& page : fs::directory_iterator(m_pages)) {
auto* scrolled_window = gtk_scrolled_window_new(nullptr, nullptr);
auto* viewport = gtk_viewport_new(nullptr, nullptr);
gtk_container_set_border_width(GTK_CONTAINER(viewport), 10);
auto* label = gtk_label_new(nullptr);
gtk_label_set_line_wrap(GTK_LABEL(label), true);
auto* image = gtk_image_new_from_icon_name("go-previous", GTK_ICON_SIZE_BUTTON);
auto* backBtn = gtk_button_new();
gtk_button_set_image(GTK_BUTTON(backBtn), image);
gtk_widget_set_name(backBtn, "home");
g_signal_connect(backBtn, "clicked", G_CALLBACK(&on_btn_clicked), nullptr);
auto* grid = GTK_GRID(gtk_grid_new());
gtk_grid_attach(grid, backBtn, 0, 1, 1, 1);
gtk_grid_attach(grid, label, 1, 2, 1, 1);
gtk_container_add(GTK_CONTAINER(viewport), GTK_WIDGET(grid));
gtk_container_add(GTK_CONTAINER(scrolled_window), GTK_WIDGET(viewport));
gtk_widget_show_all(scrolled_window);
Glib::RefPtr<Glib::Object> stack = m_builder->get_object("stack");
const auto& child_name = page.path().filename().string() + "page";
gtk_stack_add_named(GTK_STACK(stack->gobj()), scrolled_window, child_name.c_str());
}
// Init translation
const std::string& locale_path = m_preferences["locale_path"];
bindtextdomain(m_app, locale_path.c_str());
bind_textdomain_codeset(m_app, "UTF-8");
textdomain(m_app);
Gtk::ComboBoxText* languages;
m_builder->get_widget("languages", languages);
languages->set_active_id(get_best_locale());
// Set autostart switcher state
m_autostart = fs::exists(fix_path(m_preferences["autostart_path"]));
Gtk::Switch* autostart_switch;
m_builder->get_widget("autostart", autostart_switch);
autostart_switch->set_active(m_autostart);
// Live systems
if (fs::exists(m_preferences["live_path"]) && fs::is_regular_file(m_preferences["installer_path"])) {
Gtk::Label* installlabel;
m_builder->get_widget("installlabel", installlabel);
installlabel->set_visible(true);
Gtk::Button* install;
m_builder->get_widget("install", install);
install->set_visible(true);
return;
}
Gtk::Button* install;
m_builder->get_widget("appBrowser", install);
install->set_visible(true);
load_enabled_units();
load_global_enabled_units();
auto* viewport = gtk_viewport_new(nullptr, nullptr);
//auto* label = gtk_label_new(nullptr);
//gtk_label_set_line_wrap(GTK_LABEL(label), true);
auto* image = gtk_image_new_from_icon_name("go-previous", GTK_ICON_SIZE_BUTTON);
auto* backBtn = gtk_button_new();
gtk_button_set_image(GTK_BUTTON(backBtn), image);
gtk_widget_set_name(backBtn, "home");
g_signal_connect(backBtn, "clicked", G_CALLBACK(&on_btn_clicked), nullptr);
auto* options_section_box = create_options_section();
auto* apps_section_box = create_apps_section();
Gtk::Grid* grid = Gtk::manage(new Gtk::Grid());
grid->set_hexpand();
grid->set_margin_start(10);
grid->set_margin_end(10);
grid->set_margin_top(5);
grid->set_margin_bottom(5);
grid->attach(*Glib::wrap(backBtn), 0, 1, 1, 1);
Gtk::Box* box = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_VERTICAL, 5));
box->pack_start(*options_section_box, true, true, 5);
box->pack_start(*apps_section_box, true, true, 5);
box->set_valign(Gtk::ALIGN_CENTER);
box->set_halign(Gtk::ALIGN_CENTER);
grid->attach(*box, 1, 2, 5, 1);
gtk_container_add(GTK_CONTAINER(viewport), GTK_WIDGET(grid->gobj()));
gtk_widget_show_all(viewport);
Glib::RefPtr<Glib::Object> stack = m_builder->get_object("stack");
const std::string child_name = "appBrowserpage";
gtk_stack_add_named(GTK_STACK(stack->gobj()), viewport, child_name.c_str());
}
/// Returns the best locale, based on user's preferences.
auto Hello::get_best_locale() const noexcept -> std::string {
const auto& binary_path = fmt::format("{}{}{}.mo", m_preferences["locale_path"], "{}/LC_MESSAGES/", m_app);
const auto& saved_locale = fmt::vformat(binary_path, fmt::make_format_args(m_save["locale"]));
if (fs::is_regular_file(saved_locale)) {
return m_save["locale"];
} else if (m_save["locale"] == m_preferences["default_locale"]) {
return m_preferences["default_locale"];
}
const auto& locale_name = std::locale("").name();
std::string sys_locale = locale_name.substr(0, locale_name.find('.'));
const auto& user_locale = fmt::vformat(binary_path, fmt::make_format_args(sys_locale));
const auto& two_letters = sys_locale.substr(0, 2);
// If user's locale is supported
if (fs::is_regular_file(user_locale)) {
if (sys_locale.find('_') != std::string::npos) {
replace_all(sys_locale, "_", "-");
}
return sys_locale;
}
// If two first letters of user's locale is supported (ex: en_US -> en)
else if (fs::is_regular_file(fmt::vformat(binary_path, fmt::make_format_args(two_letters)))) {
return two_letters;
}
return m_preferences["default_locale"];
}
/// Sets locale of ui and pages.
void Hello::set_locale(const std::string_view& use_locale) noexcept {
#if !defined(NDEBUG)
fmt::print(
"┌{0:─^{2}}┐\n"
"│{1: ^{2}}│\n"
"└{0:─^{2}}┘\n",
"", fmt::format("Locale changed to {}", use_locale), 40);
#endif
textdomain(m_app);
Glib::setenv("LANGUAGE", use_locale.data());
m_save["locale"] = use_locale;
// Real-time locale changing
/* clang-format off */
nlohmann::json elts = {
{"comments", {"aboutdialog"}},
{"label", {
"autostartlabel",
"development",
"discover",
"donate",
"firstcategory",
"forum",
"install",
"installlabel",
"involved",
"mailling",
"readme",
"release",
"secondcategory",
"thirdcategory",
"welcomelabel",
"welcometitle",
"wiki"}
},
{"tooltip_text", {
"about",
"development",
"discover",
"donate",
"forum",
"mailling",
"wiki"}
}};
/* clang-format on */
for (const auto& method : elts.items()) {
if (!m_default_texts.contains(method.key())) {
m_default_texts[method.key()] = {};
}
for (const auto& elt : elts[method.key()].items()) {
const std::string& elt_value = elt.value();
Gtk::Widget* item;
m_builder->get_widget(elt_value, item);
if (!m_default_texts[method.key()].contains(elt_value)) {
gchar* item_buf;
g_object_get(G_OBJECT(item->gobj()), method.key().c_str(), &item_buf, nullptr);
m_default_texts[method.key()][elt_value] = item_buf;
g_free(item_buf);
}
if (method.key() == "tooltip_text" || method.key() == "comments") {
g_object_set(G_OBJECT(item->gobj()), method.key().c_str(), _(m_default_texts[method.key()][elt_value].get<std::string>().c_str()), nullptr);
}
}
}
// Change content of pages
for (const auto& page : fs::directory_iterator(m_pages)) {
Gtk::Stack* stack;
m_builder->get_widget("stack", stack);
const auto& child = stack->get_child_by_name((page.path().filename().string() + "page").c_str());
if (child == nullptr) {
fmt::print(stderr, "child not found\n");
continue;
}
const auto& first_child = reinterpret_cast<Gtk::Container*>(child)->get_children();
const auto& second_child = reinterpret_cast<Gtk::Container*>(first_child[0])->get_children();
const auto& third_child = reinterpret_cast<Gtk::Container*>(second_child[0])->get_children();
const auto& label = reinterpret_cast<Gtk::Label*>(third_child[0]);
label->set_markup(get_page(page.path().filename().string()));
}
}
void Hello::set_autostart(const bool& autostart) noexcept {
fs::path autostart_path{fix_path(m_preferences["autostart_path"])};
const auto& config_dir = autostart_path.parent_path();
if (!fs::exists(config_dir)) {
fs::create_directories(config_dir);
}
if (autostart && !fs::is_regular_file(autostart_path)) {
fs::create_symlink(m_preferences["desktop_path"], autostart_path);
} else if (!autostart && fs::is_regular_file(autostart_path)) {
fs::remove(autostart_path);
}
m_autostart = autostart;
}
auto Hello::get_page(const std::string& name) const noexcept -> std::string {
auto filename = fmt::format("{}pages/{}/{}", m_preferences["data_path"], m_save["locale"], name);
if (!fs::is_regular_file(filename)) {
filename = fmt::format("{}pages/{}/{}", m_preferences["data_path"], m_preferences["default_locale"], name);
}
return read_whole_file(filename);
}
void Hello::load_enabled_units() noexcept {
m_loaded_units.clear();
m_enabled_units.clear();
const auto& service_list = utils::make_multiline(utils::exec("systemctl list-unit-files -q --no-pager | tr -s \" \""), false, "\n");
for (const auto &service : service_list) {
const auto& out = utils::make_multiline(service, false, " ");
m_loaded_units.push_back(out[0]);
if (out[1] == "enabled")
m_enabled_units.push_back(out[0]);
}
}
void Hello::load_global_enabled_units() noexcept {
m_global_loaded_units.clear();
m_global_enabled_units.clear();
const auto& service_list = utils::make_multiline(utils::exec("systemctl --global list-unit-files -q --no-pager | tr -s \" \""), false, "\n");
for (const auto &service : service_list) {
const auto& out = utils::make_multiline(service, false, " ");
m_global_loaded_units.push_back(out[0]);
if (out[1] == "enabled")
m_global_enabled_units.push_back(out[0]);
}
}
// Handlers
void Hello::on_languages_changed(GtkComboBox* combobox) noexcept {
const auto& active_id = gtk_combo_box_get_active_id(combobox);
g_refHello->set_locale(active_id);
}
void Hello::on_action_clicked(GtkWidget* widget) noexcept {
const auto& name = gtk_widget_get_name(widget);
if (strncmp(name, "install", 7) == 0) {
quick_message(g_refHello, "Calamares install type");
return;
} else if (strncmp(name, "autostart", 9) == 0) {
const auto& action = Glib::wrap(GTK_SWITCH(widget));
g_refHello->set_autostart(action->get_active());
return;
}
Gtk::AboutDialog* dialog;
g_refHello->m_builder->get_widget("aboutdialog", dialog);
dialog->run();
dialog->hide();
}
void Hello::on_btn_clicked(GtkWidget* widget) noexcept {
const auto& name = gtk_widget_get_name(widget);
Gtk::Stack* stack;
g_refHello->m_builder->get_widget("stack", stack);
stack->set_visible_child(fmt::format("{}page", name).c_str());
}
void Hello::on_link_clicked(GtkWidget* widget) noexcept {
const auto& name = gtk_widget_get_name(widget);
const std::string uri = g_refHello->m_preferences["urls"][name];
gtk_show_uri_on_window(nullptr, uri.c_str(), GDK_CURRENT_TIME, nullptr);
}
void Hello::on_delete_window(GtkWidget* /*widget*/) noexcept {
write_json(g_refHello->m_preferences["save_path"].get<std::string>(), g_refHello->m_save);
const auto& application = g_refHello->get_application();
application->quit();
}

View File

@ -1,54 +0,0 @@
#ifndef HELLO_HPP_
#define HELLO_HPP_
#include <vector>
#include <gtkmm.h>
#include <json.hpp>
class Hello final : public Gtk::Window {
public:
Hello(int argc, char** argv);
const std::vector<std::string>& get_loaded_units() const
{ return m_loaded_units; }
const std::vector<std::string>& get_enabled_units() const
{ return m_enabled_units; }
const std::vector<std::string>& get_global_loaded_units() const
{ return m_global_loaded_units; }
const std::vector<std::string>& get_global_enabled_units() const
{ return m_global_enabled_units; }
void load_enabled_units() noexcept;
void load_global_enabled_units() noexcept;
protected:
// Handlers
static void on_languages_changed(GtkComboBox* combobox) noexcept;
static void on_action_clicked(GtkWidget* widget) noexcept;
static void on_btn_clicked(GtkWidget* widget) noexcept;
static void on_link_clicked(GtkWidget* widget) noexcept;
static void on_delete_window(GtkWidget* /*widget*/) noexcept;
private:
static constexpr auto m_app = "cachyos-hello";
bool m_dev{};
bool m_autostart{};
std::string m_pages;
Glib::RefPtr<Gtk::Builder> m_builder;
nlohmann::json m_preferences;
nlohmann::json m_save;
nlohmann::json m_default_texts;
std::vector<std::string> m_loaded_units;
std::vector<std::string> m_enabled_units;
std::vector<std::string> m_global_loaded_units;
std::vector<std::string> m_global_enabled_units;
auto get_best_locale() const noexcept -> std::string;
void set_locale(const std::string_view& use_locale) noexcept;
void set_autostart(const bool& autostart) noexcept;
auto get_page(const std::string& name) const noexcept -> std::string;
};
#endif // HELLO_HPP_

View File

@ -1,49 +0,0 @@
// Helper macroses
#ifndef HELPER_HPP_
#define HELPER_HPP_
#include <fstream>
#include <string_view>
#include <utility>
inline std::pair<std::string, std::string> tokenize(std::string& str, const std::string_view& delim) {
std::size_t start{};
std::size_t end = str.find(delim.data());
std::string key;
while (end != std::string::npos) {
key = str.substr(start, end - start);
start = end + delim.length();
end = str.find(delim.data(), start);
}
return {key, str.substr(start, end - start)};
}
inline std::size_t replace_all(std::string& inout, const std::string_view& what, const std::string_view& with) {
std::size_t count{};
std::size_t pos{};
while (std::string::npos != (pos = inout.find(what.data(), pos, what.length()))) {
inout.replace(pos, what.length(), with.data(), with.length());
pos += with.length(), ++count;
}
return count;
}
inline std::size_t remove_all(std::string& inout, const std::string_view& what) {
return replace_all(inout, what, "");
}
auto read_whole_file(const std::string_view& path) noexcept -> std::string {
static constexpr auto read_size = 4096;
std::ifstream stream{path.data()};
stream.exceptions(std::ios_base::badbit);
std::string file{};
std::string buf(read_size, '\0');
while (stream.read(&buf[0], read_size)) {
file.append(buf, 0, static_cast<std::size_t>(stream.gcount()));
}
file.append(buf, 0, static_cast<std::size_t>(stream.gcount()));
return file;
}
#endif // HELPER_HPP_

26640
src/json.hpp

File diff suppressed because it is too large Load Diff

View File

@ -1,10 +0,0 @@
#include "hello.hpp"
int main(int argc, char** argv) {
auto app = Gtk::Application::create();
Hello hello(argc, argv);
// Shows the window and returns when it is closed.
return app->run(hello);
}

786
src/main.rs Normal file
View File

@ -0,0 +1,786 @@
#![feature(const_slice_from_raw_parts)]
#![feature(const_str_from_utf8)]
#![allow(non_upper_case_globals)]
mod config;
mod data_types;
mod utils;
use config::{APP_ID, GETTEXT_PACKAGE, LOCALEDIR, PKGDATADIR, PROFILE, VERSION};
use data_types::*;
use gettextrs::LocaleCategory;
use gtk::{gio, glib, Builder, HeaderBar, Window};
use once_cell::sync::Lazy;
use std::collections::HashMap;
use std::fmt::Write as _;
use std::path::Path;
use std::sync::Mutex;
use utils::*;
use gio::prelude::*;
use gtk::prelude::*;
use gdk_pixbuf::Pixbuf;
use serde_json::json;
use std::sync::Arc;
use std::{fs, str};
use subprocess::{Exec, Redirection};
static mut g_save_json: Lazy<Mutex<serde_json::Value>> = Lazy::new(|| Mutex::new(json!(null)));
static mut g_local_units: Lazy<Mutex<SystemdUnits>> = Lazy::new(|| Mutex::new(SystemdUnits::new()));
static mut g_global_units: Lazy<Mutex<SystemdUnits>> =
Lazy::new(|| Mutex::new(SystemdUnits::new()));
static mut g_hello_window: Option<Arc<HelloWindow>> = None;
fn quick_message(message: &str) {
// Create the widgets
let dialog = gtk::Dialog::builder().title(message).modal(true).build();
dialog.set_destroy_with_parent(true);
dialog.add_button("_Offline", gtk::ResponseType::No);
dialog.add_button("_Online", gtk::ResponseType::Yes);
let content_area = dialog.content_area();
let label = gtk::Label::new(Some(message));
// Add the label, and show everything weve added
content_area.add(&label);
dialog.show_all();
let result = dialog.run();
let cmd: String;
if result == gtk::ResponseType::No {
cmd = fix_path("/usr/local/bin/calamares-offline.sh");
} else if result == gtk::ResponseType::Yes {
cmd = fix_path("/usr/local/bin/calamares-online.sh");
} else {
unsafe {
dialog.destroy();
}
return;
}
// Spawn child process in separate thread.
std::thread::spawn(move || {
Exec::shell(cmd).join().unwrap();
});
unsafe {
dialog.destroy();
}
}
fn create_options_section() -> gtk::Box {
let topbox = gtk::Box::new(gtk::Orientation::Vertical, 2);
let box_collection = gtk::Box::new(gtk::Orientation::Horizontal, 10);
let label = gtk::Label::new(None);
label.set_line_wrap(true);
label.set_justify(gtk::Justification::Center);
label.set_text("Tweaks");
let psd_btn = gtk::CheckButton::with_label("Profile-sync-daemon enable");
let systemd_oomd_btn = gtk::CheckButton::with_label("Systemd-oomd enabled");
let apparmor_btn = gtk::CheckButton::with_label("Apparmor enabled");
let ananicy_cpp_btn = gtk::CheckButton::with_label("Ananicy Cpp enabled");
unsafe {
psd_btn.set_data("actionData", "psd.service");
psd_btn.set_data("actionType", "user_service");
systemd_oomd_btn.set_data("actionData", "systemd-oomd.service");
systemd_oomd_btn.set_data("actionType", "service");
apparmor_btn.set_data("actionData", "apparmor.service");
apparmor_btn.set_data("actionType", "service");
ananicy_cpp_btn.set_data("actionData", "ananicy-cpp.service");
ananicy_cpp_btn.set_data("actionType", "service");
}
for btn in &[&psd_btn, &systemd_oomd_btn, &apparmor_btn, &ananicy_cpp_btn] {
unsafe {
let data: &str = *btn.data("actionData").unwrap().as_ptr();
if g_local_units.lock().unwrap().enabled_units.contains(&String::from(data))
|| g_global_units.lock().unwrap().enabled_units.contains(&String::from(data))
{
btn.set_active(true);
}
}
}
psd_btn.connect_clicked(on_servbtn_clicked);
systemd_oomd_btn.connect_clicked(on_servbtn_clicked);
apparmor_btn.connect_clicked(on_servbtn_clicked);
ananicy_cpp_btn.connect_clicked(on_servbtn_clicked);
topbox.pack_start(&label, true, false, 1);
box_collection.pack_start(&psd_btn, true, false, 2);
box_collection.pack_start(&systemd_oomd_btn, true, false, 2);
box_collection.pack_start(&apparmor_btn, true, false, 2);
box_collection.pack_start(&ananicy_cpp_btn, true, false, 2);
box_collection.set_halign(gtk::Align::Fill);
topbox.pack_start(&box_collection, true, false, 1);
topbox.set_hexpand(true);
topbox
}
fn create_apps_section() -> gtk::Box {
let topbox = gtk::Box::new(gtk::Orientation::Vertical, 2);
let box_collection = gtk::Box::new(gtk::Orientation::Horizontal, 10);
let label = gtk::Label::new(None);
label.set_line_wrap(true);
label.set_justify(gtk::Justification::Center);
label.set_text("Applications");
let cachyos_pi = gtk::Button::with_label("CachyOS PackageInstaller");
let cachyos_km = gtk::Button::with_label("CachyOS Kernel Manager");
cachyos_pi.connect_clicked(on_appbtn_clicked);
cachyos_km.connect_clicked(on_appbtn_clicked);
box_collection.pack_start(&cachyos_pi, true, true, 2);
box_collection.pack_start(&cachyos_km, true, true, 2);
topbox.pack_start(&label, true, true, 2);
box_collection.set_halign(gtk::Align::Fill);
topbox.pack_start(&box_collection, true, true, 0);
topbox.set_hexpand(true);
topbox
}
fn show_about_dialog() {
let main_window: Window;
unsafe {
main_window = g_hello_window.clone().unwrap().window.clone();
}
let dialog = gtk::AboutDialog::builder()
.transient_for(&main_window)
.modal(true)
.program_name(&gettextrs::gettext("CachyOS Hello"))
.comments(&gettextrs::gettext("Welcome screen for CachyOS"))
.version(VERSION)
.logo_icon_name(APP_ID)
.authors(vec![
"Vladislav Nepogodin".into(),
])
// Translators: Replace "translator-credits" with your names. Put a comma between.
.translator_credits(&gettextrs::gettext("translator-credits"))
.copyright("2021-2022 CachyOS team")
.license_type(gtk::License::Gpl30)
.website("https://github.com/cachyos/cachyos-welcome")
.website_label("GitHub")
.build();
dialog.run();
dialog.hide();
}
fn main() {
gettextrs::setlocale(LocaleCategory::LcAll, "");
gettextrs::bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR).expect("Unable to bind the text domain.");
gettextrs::textdomain(GETTEXT_PACKAGE).expect("Unable to switch to the text domain.");
glib::set_application_name("CachyOSHello");
gtk::init().expect("Unable to start GTK3.");
let application = gtk::Application::new(
Some(APP_ID), // Application id
Default::default(), // Using default flags
);
application.connect_activate(|application| {
build_ui(application);
});
// Run the application and start the event loop
application.run();
}
fn build_ui(application: &gtk::Application) {
let data = fs::read_to_string(format!("{}/data/preferences.json", PKGDATADIR))
.expect("Unable to read file");
let preferences: serde_json::Value = serde_json::from_str(&data).expect("Unable to parse");
// Get saved infos
let save_path = fix_path(preferences["save_path"].as_str().unwrap());
let save: serde_json::Value = if !Path::new(&save_path).exists() {
json!({"locale": ""})
} else {
read_json(save_path.as_str())
};
// Import Css
let provider = gtk::CssProvider::new();
provider.load_from_path(preferences["style_path"].as_str().unwrap()).unwrap();
if let Some(screen) = gdk::Screen::default() {
gtk::StyleContext::add_provider_for_screen(
&screen,
&provider,
gtk::STYLE_PROVIDER_PRIORITY_APPLICATION,
);
}
// Init window
let builder: Builder = Builder::from_file(preferences["ui_path"].as_str().unwrap());
builder.connect_signals(|_builder, handler_name| {
match handler_name {
// handler_name as defined in the glade file => handler function as defined above
"on_languages_changed" => Box::new(on_languages_changed),
"on_action_clicked" => Box::new(on_action_clicked),
"on_btn_clicked" => Box::new(on_btn_clicked),
"on_link_clicked" => Box::new(on_link_clicked),
"on_delete_window" => Box::new(on_delete_window),
_ => Box::new(|_| None),
}
});
let main_window: Window = builder.object("window").expect("Could not get the object window");
main_window.set_application(Some(application));
unsafe {
g_hello_window = Some(Arc::new(HelloWindow {
window: main_window.clone(),
builder: builder.clone(),
preferences: preferences.clone(),
}));
*g_save_json.lock().unwrap() = save.clone();
};
// Subtitle of headerbar
let header: HeaderBar = builder.object("headerbar").expect("Could not get the headerbar");
header.set_subtitle(Some("CachyOS rolling"));
// Load images
let logo_path = preferences["logo_path"].as_str().unwrap();
if Path::new(&logo_path).exists() {
let logo = Pixbuf::from_file(logo_path).unwrap();
main_window.set_icon(Some(&logo));
let image: gtk::Image = builder.object("distriblogo").unwrap();
image.set_from_pixbuf(Some(&logo));
let dialog: gtk::AboutDialog = builder.object("aboutdialog").unwrap();
dialog.set_logo(Some(&logo));
}
let social_box: gtk::Box = builder.object("social").unwrap();
for btn in social_box.children() {
let name = btn.widget_name();
let icon_path = format!("{}/data/img/{}.png", PKGDATADIR, name);
let image: gtk::Image = builder.object(name.as_str()).unwrap();
image.set_from_file(Some(&icon_path));
}
let homepage_grid: gtk::Grid = builder.object("homepage").unwrap();
for widget in homepage_grid.children() {
let casted_widget = widget.downcast::<gtk::Button>();
if casted_widget.is_err() {
continue;
}
let btn = casted_widget.unwrap();
if btn.image_position() != gtk::PositionType::Right {
continue;
}
let image_path = format!("{}/data/img/external-link.png", PKGDATADIR);
let image = gtk::Image::new();
image.set_from_file(Some(&image_path));
image.set_margin_start(2);
btn.set_image(Some(&image));
}
// Create pages
let pages =
format!("{}/data/pages/{}", PKGDATADIR, preferences["default_locale"].as_str().unwrap());
for page in fs::read_dir(pages).unwrap() {
let scrolled_window =
gtk::ScrolledWindow::new(gtk::Adjustment::NONE, gtk::Adjustment::NONE);
let viewport = gtk::Viewport::new(gtk::Adjustment::NONE, gtk::Adjustment::NONE);
viewport.set_border_width(10);
let label = gtk::Label::new(None);
label.set_line_wrap(true);
let image = gtk::Image::from_icon_name(Some("go-previous"), gtk::IconSize::Button);
let back_btn = gtk::Button::new();
back_btn.set_image(Some(&image));
back_btn.set_widget_name("home");
back_btn.connect_clicked(glib::clone!(@weak builder => move |button| {
let name = button.widget_name();
let stack: gtk::Stack = builder.object("stack").unwrap();
stack.set_visible_child_name(&format!("{}page", name));
}));
let grid = gtk::Grid::new();
grid.attach(&back_btn, 0, 1, 1, 1);
grid.attach(&label, 1, 2, 1, 1);
viewport.add(&grid);
scrolled_window.add(&viewport);
scrolled_window.show_all();
let stack: gtk::Stack = builder.object("stack").unwrap();
let child_name =
format!("{}page", page.unwrap().path().file_name().unwrap().to_str().unwrap());
stack.add_named(&scrolled_window, &child_name);
}
// Init translation
gettextrs::bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR)
.expect("Unable to switch to the text domain.");
gettextrs::bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8")
.expect("Unable to set domain encoding.");
gettextrs::textdomain(GETTEXT_PACKAGE).expect("Unable to switch to the text domain.");
let languages: gtk::ComboBoxText = builder.object("languages").unwrap();
languages.set_active_id(Some(get_best_locale(&preferences, &save).as_str()));
// Set autostart switcher state
let autostart = Path::new(&fix_path(preferences["autostart_path"].as_str().unwrap())).exists();
let autostart_switch: gtk::Switch = builder.object("autostart").unwrap();
autostart_switch.set_active(autostart);
// Live systems
if (Path::new(&preferences["live_path"].as_str().unwrap()).exists())
&& (check_regular_file(preferences["installer_path"].as_str().unwrap()))
{
let installlabel: gtk::Label = builder.object("installlabel").unwrap();
installlabel.set_visible(true);
let install: gtk::Button = builder.object("install").unwrap();
install.set_visible(true);
// Show the UI
main_window.show();
return;
} else {
let installlabel: gtk::Label = builder.object("installlabel").unwrap();
installlabel.set_visible(false);
let install: gtk::Button = builder.object("install").unwrap();
install.set_visible(false);
}
let install: gtk::Button = builder.object("appBrowser").unwrap();
install.set_visible(true);
load_enabled_units();
load_global_enabled_units();
let viewport = gtk::Viewport::new(gtk::Adjustment::NONE, gtk::Adjustment::NONE);
// let label = gtk::Label::new(None);
// label.set_line_wrap(true);
let image = gtk::Image::from_icon_name(Some("go-previous"), gtk::IconSize::Button);
let back_btn = gtk::Button::new();
back_btn.set_image(Some(&image));
back_btn.set_widget_name("home");
back_btn.connect_clicked(glib::clone!(@weak builder => move |button| {
let name = button.widget_name();
let stack: gtk::Stack = builder.object("stack").unwrap();
stack.set_visible_child_name(&format!("{}page", name));
}));
let options_section_box = create_options_section();
let apps_section_box = create_apps_section();
let grid = gtk::Grid::new();
grid.set_hexpand(true);
grid.set_margin_start(10);
grid.set_margin_end(10);
grid.set_margin_top(5);
grid.set_margin_bottom(5);
grid.attach(&back_btn, 0, 1, 1, 1);
let box_collection = gtk::Box::new(gtk::Orientation::Vertical, 5);
box_collection.pack_start(&options_section_box, true, true, 5);
box_collection.pack_start(&apps_section_box, true, true, 5);
box_collection.set_valign(gtk::Align::Center);
box_collection.set_halign(gtk::Align::Center);
grid.attach(&box_collection, 1, 2, 5, 1);
viewport.add(&grid);
viewport.show_all();
let stack: gtk::Stack = builder.object("stack").unwrap();
let child_name = "appBrowserpage";
stack.add_named(&viewport, child_name);
// Show the UI
main_window.show();
}
/// Returns the best locale, based on user's preferences.
pub fn get_best_locale(preferences: &serde_json::Value, save: &serde_json::Value) -> String {
let saved_locale =
format!("{}/{}/LC_MESSAGES/cachyos-hello.mo", LOCALEDIR, save["locale"].as_str().unwrap());
if check_regular_file(saved_locale.as_str()) {
return String::from(save["locale"].as_str().unwrap());
} else if save["locale"].as_str().unwrap() == preferences["default_locale"].as_str().unwrap() {
return String::from(preferences["default_locale"].as_str().unwrap());
}
let locale_name = std::env::var("LC_ALL").unwrap_or_else(|_| String::from("en_US.UTF-8"));
let sys_locale =
string_substr(locale_name.as_str(), 0, locale_name.find('.').unwrap_or(usize::MAX))
.unwrap();
let user_locale = format!("{}/{}/LC_MESSAGES/cachyos-hello.mo", LOCALEDIR, sys_locale);
let two_letters = string_substr(sys_locale, 0, 2).unwrap();
// If user's locale is supported
if check_regular_file(user_locale.as_str()) {
if sys_locale.contains('_') {
return sys_locale.replace('_', "-");
}
return String::from(sys_locale);
}
// If two first letters of user's locale is supported (ex: en_US -> en)
else if check_regular_file(
format!("{}/{}/LC_MESSAGES/cachyos-hello.mo", LOCALEDIR, two_letters).as_str(),
) {
return String::from(two_letters);
}
String::from(preferences["default_locale"].as_str().unwrap())
}
/// Sets locale of ui and pages.
fn set_locale(use_locale: &str) {
if PROFILE == "Devel" {
println!(
"┌{0:─^40}┐\n│{1: ^40}│\n└{0:─^40}┘",
"",
format!("Locale changed to {}", use_locale)
);
}
gettextrs::textdomain(GETTEXT_PACKAGE).expect("Unable to switch to the text domain.");
glib::setenv("LANGUAGE", use_locale, true).expect("Unable to change env variable.");
unsafe {
g_save_json.lock().unwrap()["locale"] = json!(use_locale);
}
// Real-time locale changing
let elts: HashMap<String, serde_json::Value> = serde_json::from_str(&serde_json::to_string(&json!({
"comments": ["aboutdialog"],
"label": ["autostartlabel", "development", "discover", "donate", "firstcategory", "forum", "install", "installlabel", "involved", "mailling", "readme", "release", "secondcategory", "thirdcategory", "welcomelabel", "welcometitle", "wiki"],
"tooltip_text": ["about", "development", "discover", "donate", "forum", "mailling", "wiki"],
})).unwrap()).unwrap();
let mut default_texts = json!(null);
for method in elts.iter() {
if default_texts.get(method.0) == None {
default_texts[method.0] = json![null];
}
for elt in elts[method.0].as_array().unwrap() {
let elt_value = elt.as_str().unwrap();
unsafe {
let item: gtk::Widget =
g_hello_window.clone().unwrap().builder.object(elt_value).unwrap();
if default_texts[method.0].get(elt_value) == None {
let item_buf = item.property::<String>(method.0.as_str());
default_texts[method.0][elt_value] = json!(item_buf);
}
if method.0 == "tooltip_text" || method.0 == "comments" {
item.set_property(
method.0,
&gettextrs::gettext(default_texts[method.0][elt_value].as_str().unwrap()),
);
}
}
}
}
unsafe {
let preferences = &g_hello_window.clone().unwrap().preferences;
let save = &*g_save_json.lock().unwrap();
// Change content of pages
let pages = format!(
"{}/data/pages/{}",
PKGDATADIR,
preferences["default_locale"].as_str().unwrap()
);
for page in fs::read_dir(pages).unwrap() {
let stack: gtk::Stack =
g_hello_window.clone().unwrap().builder.object("stack").unwrap();
let child = stack.child_by_name(&format!(
"{}page",
page.as_ref().unwrap().path().file_name().unwrap().to_str().unwrap()
));
if child == None {
eprintln!("child not found");
continue;
}
let first_child = &child.unwrap().downcast::<gtk::Container>().unwrap().children();
let second_child =
&first_child[0].clone().downcast::<gtk::Container>().unwrap().children();
let third_child =
&second_child[0].clone().downcast::<gtk::Container>().unwrap().children();
let label = &third_child[0].clone().downcast::<gtk::Label>().unwrap();
label.set_markup(
get_page(
page.unwrap().path().file_name().unwrap().to_str().unwrap(),
preferences,
save,
)
.as_str(),
);
}
}
}
fn set_autostart(autostart: bool) {
let autostart_path: String;
let desktop_path: String;
unsafe {
autostart_path = fix_path(
g_hello_window.clone().unwrap().preferences["autostart_path"].as_str().unwrap(),
);
desktop_path = g_hello_window.clone().unwrap().preferences["desktop_path"]
.as_str()
.unwrap()
.to_string();
}
let config_dir = Path::new(&autostart_path).parent().unwrap();
if !config_dir.exists() {
fs::create_dir_all(config_dir).unwrap();
}
if autostart && !check_regular_file(autostart_path.as_str()) {
std::os::unix::fs::symlink(desktop_path, autostart_path).unwrap();
} else if !autostart && check_regular_file(autostart_path.as_str()) {
std::fs::remove_file(autostart_path).unwrap();
}
}
#[inline]
fn get_page(name: &str, preferences: &serde_json::Value, save: &serde_json::Value) -> String {
let mut filename =
format!("{}/data/pages/{}/{}", PKGDATADIR, save["locale"].as_str().unwrap(), name);
if !check_regular_file(filename.as_str()) {
filename = format!(
"{}/data/pages/{}/{}",
PKGDATADIR,
preferences["default_locale"].as_str().unwrap(),
name
);
}
fs::read_to_string(filename).unwrap()
}
fn load_enabled_units() {
unsafe {
g_local_units.lock().unwrap().loaded_units.clear();
g_local_units.lock().unwrap().enabled_units.clear();
let mut exec_out = Exec::shell("systemctl list-unit-files -q --no-pager | tr -s \" \"")
.stdout(Redirection::Pipe)
.capture()
.unwrap()
.stdout_str();
exec_out.pop();
let service_list = exec_out.split('\n');
for service in service_list {
let out: Vec<&str> = service.split(' ').collect();
g_local_units.lock().unwrap().loaded_units.push(out[0].to_string());
if out[1] == "enabled" {
g_local_units.lock().unwrap().enabled_units.push(out[0].to_string());
}
}
}
}
fn load_global_enabled_units() {
unsafe {
g_global_units.lock().unwrap().loaded_units.clear();
g_global_units.lock().unwrap().enabled_units.clear();
let mut exec_out =
Exec::shell("systemctl --global list-unit-files -q --no-pager | tr -s \" \"")
.stdout(Redirection::Pipe)
.capture()
.unwrap()
.stdout_str();
exec_out.pop();
let service_list = exec_out.split('\n');
for service in service_list {
let out: Vec<&str> = service.split(' ').collect();
g_global_units.lock().unwrap().loaded_units.push(out[0].to_string());
if out[1] == "enabled" {
g_global_units.lock().unwrap().enabled_units.push(out[0].to_string());
}
}
}
}
/// Handlers
fn on_languages_changed(param: &[glib::Value]) -> Option<glib::Value> {
let widget = param[0].get::<gtk::ComboBox>().unwrap();
let active_id = widget.active_id().unwrap();
set_locale(active_id.as_str());
None
}
fn on_action_clicked(param: &[glib::Value]) -> Option<glib::Value> {
let widget = param[0].get::<gtk::Widget>().unwrap();
return match widget.widget_name().as_str() {
"install" => {
quick_message("Calamares install type");
None
},
"autostart" => {
let action = widget.downcast::<gtk::Switch>().unwrap();
set_autostart(action.is_active());
None
},
_ => {
show_about_dialog();
None
},
};
}
fn on_servbtn_clicked(button: &gtk::CheckButton) {
// Get action data/type.
let action_type: &str;
let action_data: &str;
unsafe {
action_type = *button.data("actionType").unwrap().as_ptr();
action_data = *button.data("actionData").unwrap().as_ptr();
}
let (user_only, pkexec_only) =
if action_type == "user_service" { ("--user", "--user $(logname)") } else { ("", "") };
let cmd: String;
unsafe {
let local_units = &g_local_units.lock().unwrap().enabled_units;
cmd = if local_units.contains(&String::from(action_data)) {
format!(
"/sbin/pkexec {} bash -c \"systemctl {} enable --now --force {}\"",
pkexec_only, user_only, action_data
)
} else {
format!(
"/sbin/pkexec {} bash -c \"systemctl {} disable --now {}\"",
pkexec_only, user_only, action_data
)
};
}
// Spawn child process in separate thread.
std::thread::spawn(move || {
Exec::shell(cmd).join().unwrap();
if action_type == "user_service" {
load_global_enabled_units();
} else {
load_enabled_units();
}
});
}
fn on_appbtn_clicked(button: &gtk::Button) {
// Get button label.
let name = button.label().unwrap();
let (binname, is_sudo) = if name == "CachyOS PackageInstaller" {
("cachyos-pi-bin", true)
} else if name == "CachyOS Kernel Manager" {
("cachyos-kernel-manager", false)
} else {
("", false)
};
// Check if executable exists.
let exit_status = Exec::cmd("which").arg(binname).join().unwrap();
if !exit_status.success() {
return;
}
let mut envs = String::new();
for env in glib::listenv() {
if env == "PATH" {
envs += "PATH=/sbin:/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin ";
continue;
}
let _ = write!(
envs,
"{}=\"{}\" ",
env.to_str().unwrap(),
glib::getenv(&env).unwrap().to_str().unwrap()
);
}
// Get executable path.
let mut exe_path =
Exec::cmd("which").arg(binname).stdout(Redirection::Pipe).capture().unwrap().stdout_str();
exe_path.pop();
let bash_cmd = format!("{} {}", &envs, &exe_path);
// Create context channel.
let (tx, rx) = glib::MainContext::channel(glib::PRIORITY_DEFAULT);
// Spawn child process in separate thread.
std::thread::spawn(move || {
let exit_status = if is_sudo {
Exec::cmd("/sbin/pkexec").arg("bash").arg("-c").arg(bash_cmd).join().unwrap()
} else {
Exec::shell(bash_cmd).join().unwrap()
};
tx.send(format!("Exit status successfully? = {:?}", exit_status.success()))
.expect("Couldn't send data to channel");
});
rx.attach(None, move |text| {
println!("{}", text);
glib::Continue(true)
});
}
fn on_btn_clicked(param: &[glib::Value]) -> Option<glib::Value> {
let widget = param[0].get::<gtk::Button>().unwrap();
let name = widget.widget_name();
unsafe {
let stack: gtk::Stack = g_hello_window.clone().unwrap().builder.object("stack").unwrap();
stack.set_visible_child_name(&format!("{}page", name));
};
None
}
fn on_link_clicked(param: &[glib::Value]) -> Option<glib::Value> {
let widget = param[0].get::<gtk::Widget>().unwrap();
let name = widget.widget_name();
unsafe {
let preferences = &g_hello_window.clone().unwrap().preferences["urls"];
let uri = preferences[name.as_str()].as_str().unwrap();
let _ = gtk::show_uri_on_window(gtk::Window::NONE, uri, 0);
}
None
}
fn on_delete_window(_param: &[glib::Value]) -> Option<glib::Value> {
unsafe {
let preferences = &g_hello_window.clone().unwrap().preferences["save_path"];
let save = &*g_save_json.lock().unwrap();
write_json(preferences.as_str().unwrap(), save);
}
Some(false.to_value())
}

87
src/meson.build Normal file
View File

@ -0,0 +1,87 @@
global_conf = configuration_data()
global_conf.set_quoted('APP_ID', application_id)
global_conf.set_quoted('PKGDATADIR', pkgdatadir)
global_conf.set_quoted('PROFILE', profile)
global_conf.set_quoted('VERSION', version + version_suffix)
global_conf.set_quoted('GETTEXT_PACKAGE', gettext_package)
global_conf.set_quoted('LOCALEDIR', localedir)
config = configure_file(
input: 'config.rs.in',
output: 'config.rs',
configuration: global_conf
)
# Copy the config.rs output to the source directory.
run_command(
'cp',
meson.project_build_root() / 'src' / 'config.rs',
meson.project_source_root() / 'src' / 'config.rs',
check: true
)
cargo_options = [ '--manifest-path', meson.project_source_root() / 'Cargo.toml' ]
cargo_options += [ '--target-dir', meson.project_build_root() / 'src' ]
if get_option('profile') == 'default'
cargo_options += [ '--release' ]
rust_target = 'release'
message('Building in release mode')
else
rust_target = 'debug'
message('Building in debug mode')
endif
cargo_env = [ 'CARGO_HOME=' + meson.project_build_root() / 'cargo-home' ]
cargo_build = custom_target(
'cargo-build',
build_by_default: true,
build_always_stale: true,
output: meson.project_name(),
console: true,
install: true,
install_dir: bindir,
command: [
'env',
cargo_env,
cargo, 'build',
cargo_options,
'&&',
'cp', 'src' / rust_target / meson.project_name(), '@OUTPUT@',
]
)
cargo_target_dir = meson.project_build_root() / 'target'
cargo_home = meson.project_build_root() / 'cargo-home'
manifest_path = meson.project_source_root() / 'Cargo.toml'
test (
'cargo-test',
cargo,
args: [
'test',
'--manifest-path=@0@'.format(manifest_path),
'--target-dir=@0@'.format(cargo_target_dir),
'--',
'--nocapture',
],
env: [
'CARGO_HOME=@0@'.format(cargo_home),
'PATH=/app/bin:/usr/bin:/usr/lib/sdk/rust-stable/bin',
],
timeout: 300, # give cargo more time
)
test (
'cargo-clippy',
cargo,
args: [
'clippy',
'--manifest-path=@0@'.format(manifest_path),
'--target-dir=@0@'.format(cargo_target_dir),
],
env: [
'CARGO_HOME=@0@'.format(cargo_home),
'PATH=/app/bin:/usr/bin:/usr/lib/sdk/rust-stable/bin',
],
timeout: 300, # give cargo more time
)

81
src/utils.rs Normal file
View File

@ -0,0 +1,81 @@
use std::fs::File;
use std::{fs, slice, str};
#[inline]
pub fn fix_path(path: &str) -> String {
if !path.starts_with('~') {
return String::from(path);
}
path.replace('~', glib::home_dir().as_path().to_str().unwrap())
}
#[inline]
pub fn read_json(path: &str) -> serde_json::Value {
let buf = fix_path(path);
let data = fs::read_to_string(buf).expect("Unable to read file");
serde_json::from_str(&data).expect("Unable to parse")
}
#[inline]
pub fn write_json(path: &str, content: &serde_json::Value) {
let output = File::create(fix_path(path)).expect("Unable to open file for writing");
serde_json::to_writer(output, content).expect("Unable to write json to file");
}
#[inline]
pub const fn const_min(v1: usize, v2: usize) -> usize {
if v1 <= v2 {
v1
} else {
v2
}
}
#[inline]
pub const fn string_substr(src_str: &str, pos: usize, n: usize) -> Result<&str, str::Utf8Error> {
let rlen = const_min(n, src_str.len() - pos);
let s = unsafe {
// First, we build a &[u8]...
let slice = slice::from_raw_parts(src_str.as_ptr().add(pos), rlen);
// ... and then convert that slice into a string slice
str::from_utf8(slice)
};
s
}
#[inline]
pub fn check_regular_file(path: &str) -> bool {
let metadata = fs::metadata(path);
if let Ok(meta) = metadata {
meta.file_type().is_file()
} else {
false
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn check_file() {
assert_eq!(check_regular_file("/etc/fstab"), true);
assert_eq!(check_regular_file("/etc"), false);
}
#[test]
fn check_substr() {
let text = "The test string is here";
assert_eq!(string_substr(text, 0, 3).unwrap(), "The");
assert_eq!(string_substr(text, 4, 4).unwrap(), "test");
assert_eq!(string_substr(text, 19, 4).unwrap(), "here");
}
#[test]
fn check_min() {
assert_eq!(const_min(1, 2), 1);
assert_eq!(const_min(2, 2), 2);
assert_eq!(const_min(3, 2), 2);
}
}

View File

@ -1,8 +0,0 @@
[wrap-git]
url = https://github.com/fmtlib/fmt.git
revision = f5cdf7cb0481afdafa7ff2f5ea640f05215d4ffb
patch_directory = fmt
[provide]
fmt = fmt_dep

View File

@ -1,34 +0,0 @@
project('fmt', 'cpp',
version : '8.0.1',
license : 'BSD',
default_options : ['cpp_std=c++14']
)
fmt_private_cpp_args = [ ]
fmt_interface_cpp_args = [ ]
libtype = get_option('default_library')
if libtype == 'shared'
fmt_private_cpp_args += [ '-DFMT_EXPORT' ]
fmt_interface_cpp_args += [ '-DFMT_SHARED' ]
endif
fmt_inc = include_directories('include')
fmt_lib = library('fmt',
sources : [
'src/format.cc',
'src/os.cc'
],
cpp_args : fmt_private_cpp_args,
include_directories : fmt_inc
)
fmt_dep = declare_dependency(
include_directories : fmt_inc,
compile_args : fmt_interface_cpp_args,
link_with : fmt_lib
)
fmt_header_only_dep = declare_dependency(
include_directories : fmt_inc,
compile_args : '-DFMT_HEADER_ONLY'
)