commit
						ef374d817c
					
				| 
						 | 
				
			
			@ -2,6 +2,8 @@
 | 
			
		|||
*.dump
 | 
			
		||||
.idea
 | 
			
		||||
build
 | 
			
		||||
target
 | 
			
		||||
src/config.rs
 | 
			
		||||
 | 
			
		||||
# Prerequisites
 | 
			
		||||
*.d
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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}
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			@ -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"
 | 
			
		||||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
| 
						 | 
				
			
			@ -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"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -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()
 | 
			
		||||
| 
						 | 
				
			
			@ -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()
 | 
			
		||||
| 
						 | 
				
			
			@ -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()
 | 
			
		||||
| 
						 | 
				
			
			@ -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()
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -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++
 | 
			
		||||
| 
						 | 
				
			
			@ -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 project’s 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
 | 
			
		||||
							
								
								
									
										14
									
								
								launch.sh
								
								
								
								
							
							
						
						
									
										14
									
								
								launch.sh
								
								
								
								
							| 
						 | 
				
			
			@ -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
 | 
			
		||||
							
								
								
									
										141
									
								
								meson.build
								
								
								
								
							
							
						
						
									
										141
									
								
								meson.build
								
								
								
								
							| 
						 | 
				
			
			@ -1,95 +1,82 @@
 | 
			
		|||
project('cachyos-hello', 'cpp',
 | 
			
		||||
        version: '0.6.10',
 | 
			
		||||
project('cachyos-hello', 'rust',
 | 
			
		||||
        version: '0.7.0',
 | 
			
		||||
        license: 'GPLv3',
 | 
			
		||||
        meson_version: '>=0.55.0',
 | 
			
		||||
        default_options: ['cpp_std=c++20',
 | 
			
		||||
                          'buildtype=debugoptimized',
 | 
			
		||||
        meson_version: '>=0.56.0',
 | 
			
		||||
        default_options: ['buildtype=debugoptimized',
 | 
			
		||||
                          'warning_level=3',
 | 
			
		||||
                          'werror=true',
 | 
			
		||||
                          'b_ndebug=if-release'])
 | 
			
		||||
 | 
			
		||||
cc = meson.get_compiler('cpp')
 | 
			
		||||
i18n = import('i18n')
 | 
			
		||||
gnome = import('gnome')
 | 
			
		||||
 | 
			
		||||
# Common dependencies
 | 
			
		||||
fmt = dependency('fmt', version : ['>=8.0.0'], fallback : ['fmt', 'fmt_dep'])
 | 
			
		||||
gtkmm = dependency('gtkmm-3.0', version : ['>=3.22.0'])
 | 
			
		||||
base_id = 'org.cachyos.hello'
 | 
			
		||||
 | 
			
		||||
src_files = files(
 | 
			
		||||
    'src/hello.cpp', 'src/hello.hpp',
 | 
			
		||||
    'src/main.cpp',
 | 
			
		||||
dependency('glib-2.0', version: '>= 2.66')
 | 
			
		||||
dependency('gio-2.0', version: '>= 2.66')
 | 
			
		||||
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 = [
 | 
			
		||||
        '-Wshadow',
 | 
			
		||||
 | 
			
		||||
        '-Wnon-virtual-dtor',
 | 
			
		||||
 | 
			
		||||
        '-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',
 | 
			
		||||
        ]
 | 
			
		||||
if get_option('profile') == 'development'
 | 
			
		||||
  # Setup pre-commit hook for ensuring coding style is always consistent
 | 
			
		||||
  message('Setting up git pre-commit hook..')
 | 
			
		||||
  run_command('cp', '-f', 'hooks/pre-commit.hook', '.git/hooks/pre-commit')
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
if get_option('buildtype') != 'debug'
 | 
			
		||||
   if cc.get_id() == 'gcc'
 | 
			
		||||
      possible_cc_flags += [
 | 
			
		||||
         '-flto',
 | 
			
		||||
         '-fwhole-program',
 | 
			
		||||
      ]
 | 
			
		||||
   else
 | 
			
		||||
      possible_cc_flags += [
 | 
			
		||||
         '-flto=thin',
 | 
			
		||||
      ]
 | 
			
		||||
   endif
 | 
			
		||||
endif
 | 
			
		||||
cargo_sources = files(
 | 
			
		||||
  'Cargo.toml',
 | 
			
		||||
  'Cargo.lock',
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
add_project_arguments(cc.get_supported_arguments(possible_cc_flags), language : 'cpp')
 | 
			
		||||
 | 
			
		||||
executable(
 | 
			
		||||
  'cachyos-hello',
 | 
			
		||||
  src_files,
 | 
			
		||||
  dependencies: [fmt, gtkmm],
 | 
			
		||||
  include_directories: [include_directories('src')],
 | 
			
		||||
  install: true)
 | 
			
		||||
subdir('po')
 | 
			
		||||
subdir('src')
 | 
			
		||||
 | 
			
		||||
install_data (
 | 
			
		||||
    meson.project_name () + '.desktop',
 | 
			
		||||
    install_dir: join_paths(get_option('datadir'), 'applications')
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
#meson.add_install_script('postinstall.sh')
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,10 @@
 | 
			
		|||
option(
 | 
			
		||||
  'profile',
 | 
			
		||||
  type: 'combo',
 | 
			
		||||
  choices: [
 | 
			
		||||
    'default',
 | 
			
		||||
    'development'
 | 
			
		||||
  ],
 | 
			
		||||
  value: 'default',
 | 
			
		||||
  description: 'The build profile for Kooha. One of "default" or "development".'
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1 @@
 | 
			
		|||
i18n.gettext(gettext_package, preset: 'glib')
 | 
			
		||||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			@ -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()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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@;
 | 
			
		||||
| 
						 | 
				
			
			@ -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()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										733
									
								
								src/hello.cpp
								
								
								
								
							
							
						
						
									
										733
									
								
								src/hello.cpp
								
								
								
								
							| 
						 | 
				
			
			@ -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 we’ve 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();
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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_
 | 
			
		||||
| 
						 | 
				
			
			@ -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
								
								
								
								
							
							
						
						
									
										26640
									
								
								src/json.hpp
								
								
								
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										10
									
								
								src/main.cpp
								
								
								
								
							
							
						
						
									
										10
									
								
								src/main.cpp
								
								
								
								
							| 
						 | 
				
			
			@ -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);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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 we’ve 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: >k::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: >k::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: >k::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())
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,8 +0,0 @@
 | 
			
		|||
[wrap-git]
 | 
			
		||||
url = https://github.com/fmtlib/fmt.git
 | 
			
		||||
revision = f5cdf7cb0481afdafa7ff2f5ea640f05215d4ffb
 | 
			
		||||
 | 
			
		||||
patch_directory = fmt
 | 
			
		||||
 | 
			
		||||
[provide]
 | 
			
		||||
fmt = fmt_dep
 | 
			
		||||
| 
						 | 
				
			
			@ -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'
 | 
			
		||||
)
 | 
			
		||||
		Loading…
	
		Reference in New Issue