├── .gitattributes ├── src ├── back_end │ ├── .gitignore │ ├── rustfmt.toml │ ├── src │ │ ├── core │ │ │ ├── macro.rs │ │ │ ├── result_enum.rs │ │ │ ├── time.rs │ │ │ ├── utf8.rs │ │ │ ├── sha.rs │ │ │ └── sqlite.rs │ │ ├── utils │ │ │ ├── memory.rs │ │ │ ├── browser.rs │ │ │ └── fs_utils.rs │ │ ├── lib.rs │ │ ├── config │ │ │ ├── constant.rs │ │ │ └── toml.rs │ │ └── internal │ │ │ └── app_config.rs │ ├── tests │ │ ├── memory.rs │ │ ├── fs_utils.rs │ │ ├── config.rs │ │ ├── time.rs │ │ ├── sqlite.rs │ │ └── utf8.rs │ ├── Cargo.toml │ └── Cargo.lock ├── clipboard │ ├── include │ │ ├── content_types.hxx │ │ └── text.hxx │ └── text.cxx ├── ffi │ └── include │ │ ├── sqlite.h │ │ ├── utils.h │ │ └── config.h ├── main.cxx ├── front_end │ ├── include │ │ ├── setting_widget.hxx │ │ ├── about_widget.hxx │ │ ├── table_widget.hxx │ │ ├── main_window_preload.hxx │ │ ├── theme_manager.hxx │ │ ├── main_window.hxx │ │ ├── about_window.hxx │ │ ├── abstract_table_model.hxx │ │ └── icon_bytes.hxx │ ├── setting_widget.cxx │ ├── about_widget.cxx │ ├── table_widget.cxx │ ├── main_window.cxx │ ├── about_window.cxx │ ├── main_window_preload.cxx │ └── theme_manager.cxx ├── front_end_db │ ├── include │ │ └── sqlite_manager.hxx │ └── sqlite_manager.cxx └── front_end_utils │ └── include │ ├── error_types.hxx │ └── utils.hxx ├── tools ├── Makefile ├── fmt.sh └── helper.py ├── tests-ffi ├── Makefile ├── demo.toml ├── config.cxx ├── time.cxx ├── fs.cxx ├── sqlite.cxx ├── tests.sh ├── sha.cxx └── toml_config.cxx ├── .gitignore ├── commands.gdb ├── resources └── 1.png ├── readme.md ├── .clang-format ├── .vscode └── settings.json ├── Makefile ├── scripts ├── Makefile ├── linux_deploy.sh └── debug.sh ├── .clang-tidy ├── CMakeLists.txt └── .github └── workflows ├── linux.yml └── linux_dev.yml /.gitattributes: -------------------------------------------------------------------------------- 1 | *.h linguist-language=C -------------------------------------------------------------------------------- /src/back_end/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /tools/Makefile: -------------------------------------------------------------------------------- 1 | 2 | fmt: 3 | @./fmt.sh -------------------------------------------------------------------------------- /tests-ffi/Makefile: -------------------------------------------------------------------------------- 1 | tests: 2 | @./tests.sh -------------------------------------------------------------------------------- /src/back_end/rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width = 80 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | compile_commands.json 2 | build 3 | .cache 4 | *.db -------------------------------------------------------------------------------- /commands.gdb: -------------------------------------------------------------------------------- 1 | run 2 | 3 | catch signal SIGSEGV 4 | commands 5 | bt full 6 | quit 7 | end -------------------------------------------------------------------------------- /resources/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Reim-developer/Lazyboard/HEAD/resources/1.png -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Lazyboard 2 | 3 | *Lazyboard is a clipboard manager, using C++ for front-end, & Rust for back-end.* 4 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | BasedOnStyle: Google 4 | IndentWidth: 4 5 | ColumnLimit: 80 6 | UseTab: Always 7 | TabWidth: 4 8 | IndentPPDirectives: BeforeHash 9 | PPIndentWidth: 4 -------------------------------------------------------------------------------- /src/back_end/src/core/macro.rs: -------------------------------------------------------------------------------- 1 | #[macro_export] 2 | macro_rules! ensure { 3 | ($cond:expr, $err:expr) => { 4 | if !$cond { 5 | return $err; 6 | } 7 | }; 8 | } 9 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.linkedProjects": [ 3 | "src/back_end/Cargo.toml" 4 | ], 5 | "rust-analyzer.check.command": "clippy", 6 | "files.associations": { 7 | ".clang-format": "yaml", 8 | ".clang-tidy": "yaml" 9 | } 10 | } -------------------------------------------------------------------------------- /src/clipboard/include/content_types.hxx: -------------------------------------------------------------------------------- 1 | #ifndef CONTENT_TYPES_HXX 2 | #define CONTENT_TYPES_HXX 3 | 4 | #include 5 | using std::uint8_t; 6 | namespace Lazyboard::clipboard { 7 | enum class ContentTypes : uint8_t { 8 | TEXT, 9 | HTML, 10 | URL, 11 | PATH, 12 | IMAGE, 13 | }; 14 | } 15 | 16 | #endif -------------------------------------------------------------------------------- /src/back_end/src/utils/memory.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::{CString, c_char}; 2 | 3 | #[unsafe(no_mangle)] 4 | /// # Safety 5 | /// Careful with double free. 6 | pub unsafe extern "C" fn free_alloc(c_str: *mut c_char) { 7 | unsafe { 8 | if !c_str.is_null() { 9 | let _ = CString::from_raw(c_str); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /tests-ffi/demo.toml: -------------------------------------------------------------------------------- 1 | [app_settings] 2 | hide_when_closed = false 3 | notification = true 4 | 5 | [app_gui_settings] 6 | background_color = "#2f3136" 7 | foreground_color = "#ffffff" 8 | background_button_color = "#2f3136" 9 | foreground_button_color = "#ffffff" 10 | background_header_table_color = "#2f3136" 11 | foreground_header_table_color = "#ffffff" 12 | -------------------------------------------------------------------------------- /src/back_end/src/core/result_enum.rs: -------------------------------------------------------------------------------- 1 | #[derive(PartialEq, Eq, Debug)] 2 | #[repr(C)] 3 | #[allow(non_camel_case_types)] 4 | pub enum AllocResult { 5 | OK, 6 | NULL_DEFERENCE_ERR, 7 | C_STRING_CONVERT_ERR, 8 | } 9 | 10 | #[derive(PartialEq, Eq, Debug)] 11 | #[repr(C)] 12 | #[allow(non_camel_case_types)] 13 | pub enum ResultContext { 14 | OK, 15 | FAILED, 16 | } 17 | -------------------------------------------------------------------------------- /src/back_end/tests/memory.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn test_memory_mod() { 3 | use back_end::utils::memory::free_alloc; 4 | use std::ffi::CString; 5 | use std::ptr::null_mut; 6 | 7 | let raw_str = CString::new("Reim-developer") 8 | .map(CString::into_raw) 9 | .unwrap_or(null_mut()); 10 | 11 | unsafe { 12 | free_alloc(raw_str); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/back_end/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "back_end" 3 | version = "1.0.0" 4 | edition = "2024" 5 | 6 | [lib] 7 | crate-type = ["staticlib", "lib"] 8 | 9 | [profile.dev] 10 | panic = "abort" 11 | 12 | [dependencies] 13 | chrono = "0.4.41" 14 | dirs = "6.0.0" 15 | hex = "0.4.3" 16 | rusqlite = { version = "0.37.0", features = ["bundled"] } 17 | serde = { version = "1.0.219", features = ["derive"] } 18 | sha2 = "0.10.9" 19 | toml = "0.9.5" 20 | webbrowser = "1.0.5" 21 | -------------------------------------------------------------------------------- /src/ffi/include/sqlite.h: -------------------------------------------------------------------------------- 1 | #ifndef RAW_SQLITE_HXX 2 | #define RAW_SQLITE_HXX 3 | 4 | #if defined(__cplusplus) 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | using std::uint8_t; 10 | 11 | enum class QueryResult : uint8_t { 12 | OK, 13 | OPEN_DATABASE_ERR, 14 | C_STR_CONVERT_ERR, 15 | EXECUTE_SQL_ERR 16 | }; 17 | 18 | auto init_clipboard_cache(const char *path) -> QueryResult; 19 | 20 | #if defined(__cplusplus) 21 | } 22 | #endif 23 | 24 | #endif // RAW_SQLITE_HXX -------------------------------------------------------------------------------- /src/main.cxx: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "front_end/include/main_window.hxx" 5 | 6 | using Lazyboard::front_end::MainWindow; 7 | using std::make_unique; 8 | using std::unique_ptr; 9 | 10 | int main(int argc, char *argv[]) { 11 | unique_ptr application = 12 | make_unique(argc, argv); 13 | unique_ptr main_window = make_unique(); 14 | 15 | main_window->init_main_window(); 16 | 17 | return application->exec(); 18 | } -------------------------------------------------------------------------------- /src/front_end/include/setting_widget.hxx: -------------------------------------------------------------------------------- 1 | #ifndef SETTING_WIDGET_HXX 2 | #define SETTING_WIDGET_HXX 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | using std::unique_ptr; 10 | 11 | namespace Lazyboard::front_end { 12 | class SettingWidget { 13 | private: 14 | unique_ptr open_setting; 15 | 16 | public: 17 | SettingWidget(); 18 | void setup_widget(QGridLayout *grid_layout); 19 | }; 20 | 21 | } // namespace Lazyboard::front_end 22 | 23 | #endif -------------------------------------------------------------------------------- /src/front_end/setting_widget.cxx: -------------------------------------------------------------------------------- 1 | #include "include/setting_widget.hxx" 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | using Lazyboard::front_end::SettingWidget; 9 | using std::make_unique; 10 | using Self = SettingWidget; 11 | 12 | SettingWidget::SettingWidget() { open_setting = make_unique(); } 13 | 14 | void Self::setup_widget(QGridLayout *grid_layout) { 15 | open_setting->setText("Settings"); 16 | 17 | grid_layout->addWidget(open_setting.get(), 0, 1); 18 | } -------------------------------------------------------------------------------- /src/back_end/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![deny(clippy::pedantic, clippy::all, clippy::nursery, clippy::perf)] 2 | 3 | pub mod utils { 4 | pub mod browser; 5 | pub mod fs_utils; 6 | pub mod memory; 7 | } 8 | 9 | pub mod core { 10 | pub mod r#macro; 11 | pub mod result_enum; 12 | pub mod sha; 13 | pub mod sqlite; 14 | pub mod time; 15 | pub mod utf8; 16 | } 17 | 18 | pub mod internal { 19 | pub mod app_config; 20 | } 21 | 22 | pub mod config { 23 | pub mod constant; 24 | pub mod toml; 25 | } 26 | -------------------------------------------------------------------------------- /src/back_end/src/utils/browser.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::{CStr, c_char}; 2 | use webbrowser::open; 3 | 4 | use crate::core::result_enum::ResultContext; 5 | 6 | /// # Safety 7 | /// Be careful with raw pointers. 8 | #[unsafe(no_mangle)] 9 | pub unsafe extern "C" fn open_browser(url: *const c_char) -> ResultContext { 10 | unsafe { 11 | use ResultContext as R; 12 | 13 | let c_str = CStr::from_ptr(url).to_string_lossy(); 14 | 15 | let is_ok = open(&c_str).is_ok(); 16 | 17 | if !is_ok { 18 | return R::FAILED; 19 | } 20 | 21 | R::OK 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tests-ffi/config.cxx: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | using std::uint8_t; 5 | 6 | enum class UtilsResult : uint8_t { 7 | OK, 8 | ALLOC_ERR, 9 | NULL_DEFERENCE_ERR, 10 | GET_DIR_ERR, 11 | }; 12 | 13 | extern "C" UtilsResult config_dir(char **out); 14 | 15 | void test_config_dir() { 16 | using R = UtilsResult; 17 | 18 | char *out = nullptr; 19 | auto result = config_dir(&out); 20 | 21 | assert(result != R::ALLOC_ERR); 22 | assert(result != R::NULL_DEFERENCE_ERR); 23 | assert(result != R::GET_DIR_ERR); 24 | assert(result == R::OK); 25 | } 26 | 27 | int main() { 28 | test_config_dir(); 29 | 30 | return 0; 31 | } -------------------------------------------------------------------------------- /src/front_end_db/include/sqlite_manager.hxx: -------------------------------------------------------------------------------- 1 | #ifndef SQLITE_MANAGER_HXX 2 | #define SQLITE_MANAGER_HXX 3 | #include 4 | 5 | #include "../../ffi/include/sqlite.h" 6 | #include "../../ffi/include/utils.h" 7 | 8 | namespace Lazyboard::front_end_db { 9 | class SQLiteManager { 10 | private: 11 | void on_create_folder_error(const ResultContext &result) noexcept; 12 | void on_create_clipboard_cache_error(const QueryResult &status) noexcept; 13 | 14 | public: 15 | void create_clipboard_cache(QMainWindow *main_window); 16 | 17 | private: 18 | QMainWindow *_main_window; 19 | }; 20 | } // namespace Lazyboard::front_end_db 21 | 22 | #endif // SQLITE_MANAGER_HXX -------------------------------------------------------------------------------- /src/clipboard/include/text.hxx: -------------------------------------------------------------------------------- 1 | #ifndef TEXT_CLIPBOARD_HXX 2 | #define TEXT_CLIPBOARD_HXX 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #include "../../front_end_db/include/sqlite_manager.hxx" 9 | 10 | using Lazyboard::front_end_db::SQLiteManager; 11 | using std::string; 12 | using std::unique_ptr; 13 | 14 | namespace Lazyboard::clipboard { 15 | class TextClipboard { 16 | private: 17 | QClipboard *clipboard = nullptr; 18 | unique_ptr sqlite_manager; 19 | 20 | private: 21 | void save_to_cache(); 22 | 23 | public: 24 | TextClipboard(); 25 | void on_changed(); 26 | string get(); 27 | }; 28 | } // namespace Lazyboard::clipboard 29 | 30 | #endif // TEXT_CLIPBOARD_HXX -------------------------------------------------------------------------------- /src/front_end/include/about_widget.hxx: -------------------------------------------------------------------------------- 1 | #ifndef ABOUT_WIDGET_HXX 2 | #define ABOUT_WIDGET_HXX 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | #include "about_window.hxx" 11 | 12 | using std::unique_ptr; 13 | 14 | namespace Lazyboard::front_end { 15 | class AboutWidget { 16 | public: 17 | AboutWidget(); 18 | 19 | private: 20 | unique_ptr show_about; 21 | unique_ptr about_window; 22 | 23 | private: 24 | void setup_event(); 25 | 26 | public: 27 | void setup_widget(QGridLayout *layout); 28 | void setup_about_window_event(QMainWindow *main_window); 29 | }; 30 | 31 | } // namespace Lazyboard::front_end 32 | 33 | #endif // ABOUT_WIDGET_HXX -------------------------------------------------------------------------------- /src/back_end/tests/fs_utils.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn test_raw_cache_dir() { 3 | use back_end::utils::fs_utils::UtilsResult; 4 | use back_end::utils::fs_utils::cache_dir; 5 | use std::ffi::CString; 6 | use std::ffi::c_char; 7 | use std::ptr::null_mut; 8 | 9 | use UtilsResult as R; 10 | unsafe { 11 | let mut out: *mut c_char = null_mut(); 12 | let result = cache_dir(&raw mut out); 13 | 14 | let out_c_string = CString::from_raw(out); 15 | let out_str = out_c_string.to_string_lossy(); 16 | 17 | assert_ne!(result, R::ALLOC_ERR); 18 | assert_ne!(result, R::NULL_DEFERENCE_ERR); 19 | assert_ne!(result, R::GET_DIR_ERR); 20 | assert_eq!(result, R::OK); 21 | assert!(!out_str.is_empty()); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/ffi/include/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILS_RAW_HXX 2 | #define UTILS_RAW_HXX 3 | 4 | #include 5 | #ifdef __cplusplus 6 | extern "C" { 7 | #endif 8 | 9 | using std::uint8_t; 10 | 11 | // clang-format off 12 | enum class ResultContext : uint8_t { 13 | OK, 14 | FAILED 15 | }; // clang-format on 16 | 17 | ResultContext open_browser(const char *url); 18 | ResultContext new_folder(const char *path); 19 | 20 | /***********/ 21 | 22 | enum class UtilsResult : uint8_t { 23 | OK, 24 | ALLOC_ERR, 25 | NULL_DEFERENCE_ERR, 26 | GET_DIR_ERR, 27 | }; 28 | 29 | UtilsResult cache_dir(char **out); 30 | UtilsResult config_dir(char **out); 31 | bool path_exists(const char *path); 32 | void free_alloc(char *c_str); 33 | 34 | #ifdef __cplusplus 35 | } 36 | #endif 37 | #endif // UTILS_RAW_HXX -------------------------------------------------------------------------------- /src/back_end/tests/config.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn test_config() { 3 | unsafe { 4 | use back_end::utils::fs_utils::UtilsResult; 5 | use back_end::utils::fs_utils::config_dir; 6 | use std::ffi::CString; 7 | use std::ffi::c_char; 8 | use std::ptr::null_mut; 9 | 10 | use UtilsResult as R; 11 | 12 | let mut out: *mut c_char = null_mut(); 13 | let result = config_dir(&raw mut out); 14 | let out_c_string = CString::from_raw(out); 15 | let out_str = out_c_string.to_string_lossy(); 16 | 17 | assert_ne!(result, R::ALLOC_ERR); 18 | assert_ne!(result, R::GET_DIR_ERR); 19 | assert_eq!(result, R::OK); 20 | assert!(out_str.is_ascii()); 21 | assert!(!out_str.is_empty()); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/clipboard/text.cxx: -------------------------------------------------------------------------------- 1 | #include "include/text.hxx" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | using Lazyboard::clipboard::TextClipboard; 10 | using Self = TextClipboard; 11 | using std::make_unique; 12 | 13 | Self::TextClipboard() { 14 | if (!this->clipboard) { 15 | this->clipboard = QApplication::clipboard(); 16 | } 17 | 18 | sqlite_manager = make_unique(); 19 | } 20 | 21 | void Self::save_to_cache() {} 22 | 23 | void Self::on_changed() { 24 | using O = QObject; 25 | 26 | O::connect(this->clipboard, &QClipboard::dataChanged, [] { 27 | 28 | }); 29 | } 30 | 31 | string Self::get() { 32 | auto clipboard_text = this->clipboard->text(); 33 | 34 | return clipboard_text.toStdString(); 35 | } -------------------------------------------------------------------------------- /src/back_end/src/core/time.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::{CString, c_schar}; 2 | 3 | use chrono::{DateTime, Utc}; 4 | 5 | use crate::{core::result_enum::AllocResult, ensure}; 6 | 7 | /// # Safety 8 | /// Careful with raw pointers and memory leaks. 9 | #[unsafe(no_mangle)] 10 | pub unsafe extern "C" fn time_now(out: *mut *mut c_schar) -> AllocResult { 11 | unsafe { 12 | use AllocResult as R; 13 | 14 | ensure!(!out.is_null(), R::NULL_DEFERENCE_ERR); 15 | 16 | let now_utc: DateTime = Utc::now(); 17 | let fmt_utc = now_utc.format("%Y-%m-%d %H:%M:%S").to_string(); 18 | 19 | let Ok(fmt_utc_cstr) = CString::new(fmt_utc) else { 20 | return R::C_STRING_CONVERT_ERR; 21 | }; 22 | 23 | *out = fmt_utc_cstr.into_raw(); 24 | 25 | R::OK 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/back_end/src/core/utf8.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::{CStr, CString, c_char}; 2 | 3 | use crate::{core::result_enum::AllocResult, ensure}; 4 | 5 | #[unsafe(no_mangle)] 6 | /// # Safety 7 | /// Careful with raw pointers & memory leaks 8 | pub unsafe extern "C" fn to_utf8( 9 | content: *const c_char, 10 | out: *mut *mut c_char, 11 | ) -> AllocResult { 12 | use AllocResult as R; 13 | 14 | unsafe { 15 | ensure!(!content.is_null(), R::NULL_DEFERENCE_ERR); 16 | ensure!(!content.is_null(), R::NULL_DEFERENCE_ERR); 17 | 18 | let result_str = CStr::from_ptr(content).to_string_lossy(); 19 | 20 | let Ok(c_str) = CString::new(result_str.as_ref()) else { 21 | return R::C_STRING_CONVERT_ERR; 22 | }; 23 | 24 | *out = c_str.into_raw(); 25 | 26 | R::OK 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/front_end/include/table_widget.hxx: -------------------------------------------------------------------------------- 1 | #ifndef TABLE_WIDGET_HXX 2 | #define TABLE_WIDGET_HXX 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #include "abstract_table_model.hxx" 14 | using std::unique_ptr; 15 | 16 | namespace Lazyboard::front_end { 17 | class TableWidget { 18 | public: 19 | TableWidget() noexcept; 20 | 21 | private: 22 | QStringList header_labels; 23 | static constexpr int DEFAULT_MAX_ROW = 10; 24 | 25 | private: 26 | void set_resize_mode(QHeaderView *header); 27 | 28 | private: 29 | unique_ptr table_view_widget; 30 | unique_ptr abstract_table_model; 31 | 32 | public: 33 | void setup_widget(QGridLayout *grid_layout); 34 | }; 35 | } // namespace Lazyboard::front_end 36 | 37 | #endif // TABLE_WIDGET_HXX -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: build tests-ffi 2 | 3 | scripts_folder = scripts 4 | 5 | debug: 6 | @$(MAKE) -C $(scripts_folder) debug-build 7 | @echo "[INFO]: Exit with status: $$?" 8 | 9 | debug-non-gui: 10 | @$(MAKE) -C $(scripts_folder) debug-non-gui 11 | 12 | debug-gdb: 13 | @$(MAKE) -C $(scripts_folder) debug-gdb 14 | 15 | backend-test: 16 | @$(MAKE) -C $(scripts_folder) backend-test 17 | 18 | check-backend: 19 | @$(MAKE) -C $(scripts_folder) check-backend 20 | 21 | check-frontend: 22 | @$(MAKE) -C $(scripts_folder) check-frontend 23 | 24 | dev-push: 25 | @$(MAKE) -C $(scripts_folder) dev-push 26 | 27 | master-push: 28 | @$(MAKE) -C $(scripts_folder) master-push 29 | 30 | stable-push: 31 | @$(MAKE) -C $(scripts_folder) stable-push 32 | 33 | build_options ?= none 34 | deploy-linux: 35 | @$(MAKE) -C $(scripts_folder) deploy-linux build_options="$(build_options)" 36 | 37 | tests-ffi: 38 | @$(MAKE) -C tests-ffi tests 39 | 40 | fmt: 41 | @$(MAKE) -C tools fmt -------------------------------------------------------------------------------- /src/front_end/include/main_window_preload.hxx: -------------------------------------------------------------------------------- 1 | #ifndef MAIN_WINDOW_PRELOAD_HXX 2 | #define MAIN_WINDOW_PRELOAD_HXX 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #include "../../ffi/include/config.h" 10 | #include "theme_manager.hxx" 11 | 12 | using std::string; 13 | using std::unique_ptr; 14 | 15 | namespace Lazyboard::front_end { 16 | class MainWindowPreload { 17 | private: 18 | unique_ptr raw_app_config; 19 | unique_ptr theme_manager; 20 | 21 | private: 22 | void on_gen_default_cfg_error(ConfigResult status, 23 | QMainWindow *main_window); 24 | 25 | void on_read_exists_cfg_error(ReadConfigResult status, 26 | QMainWindow *main_window); 27 | 28 | string application_config(); 29 | 30 | public: 31 | void create_default_config(QMainWindow *main_window); 32 | void read_if_exists_config(QMainWindow *main_window); 33 | }; 34 | } // namespace Lazyboard::front_end 35 | 36 | #endif // PRELOAD_HXX -------------------------------------------------------------------------------- /src/back_end/src/core/sha.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::{CStr, CString, c_char}; 2 | 3 | use crate::core::result_enum::AllocResult; 4 | use crate::ensure; 5 | use sha2::{Digest, Sha256}; 6 | 7 | /// # Safety 8 | /// Careful with raw pointers. 9 | #[unsafe(no_mangle)] 10 | pub unsafe extern "C" fn text_sha256( 11 | text: *const c_char, 12 | out: *mut *mut c_char, 13 | ) -> AllocResult { 14 | unsafe { 15 | use AllocResult as R; 16 | 17 | ensure!(!text.is_null(), R::NULL_DEFERENCE_ERR); 18 | ensure!(!out.is_null(), R::NULL_DEFERENCE_ERR); 19 | 20 | let mut hasher = Sha256::new(); 21 | 22 | let text_bytes = CStr::from_ptr(text).to_bytes(); 23 | hasher.update(text_bytes); 24 | let result = hasher.finalize(); 25 | 26 | let to_hex = hex::encode(result); 27 | 28 | let Ok(result_c_char) = CString::new(to_hex) else { 29 | return R::C_STRING_CONVERT_ERR; 30 | }; 31 | 32 | *out = result_c_char.into_raw(); 33 | 34 | R::OK 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /tests-ffi/time.cxx: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using std::string; 6 | using std::uint8_t; 7 | 8 | extern "C" enum class AllocResult : uint8_t { 9 | OK, 10 | NULL_DEFERENCE_ERR, 11 | CONVERT_TO_STR_FAILED, 12 | }; 13 | 14 | extern "C" AllocResult time_now(char **out); 15 | extern "C" void free_alloc(char *str); 16 | 17 | void null_deference_test() { 18 | using R = AllocResult; 19 | 20 | auto result = time_now(nullptr); 21 | assert(result != R::CONVERT_TO_STR_FAILED); 22 | assert(result != R::OK); 23 | assert(result == R::NULL_DEFERENCE_ERR); 24 | } 25 | 26 | void time_now_test() { 27 | using R = AllocResult; 28 | 29 | char *out = nullptr; 30 | auto result = time_now(&out); 31 | auto time_now_string = string(out); 32 | free_alloc(out); 33 | 34 | assert(result != R::CONVERT_TO_STR_FAILED); 35 | assert(result != R::NULL_DEFERENCE_ERR); 36 | assert(result == R::OK); 37 | assert(!time_now_string.empty()); 38 | } 39 | 40 | int main() { 41 | null_deference_test(); 42 | time_now_test(); 43 | return 0; 44 | } -------------------------------------------------------------------------------- /src/front_end/about_widget.cxx: -------------------------------------------------------------------------------- 1 | #include "include/about_widget.hxx" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | #include "include/about_window.hxx" 13 | 14 | using Lazyboard::front_end::AboutWidget; 15 | using std::make_unique; 16 | using Self = AboutWidget; 17 | 18 | AboutWidget::AboutWidget() { show_about = make_unique(); } 19 | 20 | void Self::setup_event() { 21 | about_window = make_unique(); 22 | 23 | // clang-format off 24 | const auto function = [this]() { 25 | about_window->show_window(); 26 | }; // clang-format on 27 | 28 | QObject::connect(show_about.get(), &QPushButton::clicked, function); 29 | } 30 | 31 | void Self::setup_widget(QGridLayout *grid_layout) { 32 | show_about->setText("About"); 33 | grid_layout->addWidget(show_about.get(), 1, 1); 34 | 35 | this->setup_event(); 36 | } 37 | 38 | void Self::setup_about_window_event(QMainWindow *main_window) { 39 | about_window->on_about_window_event(main_window); 40 | } -------------------------------------------------------------------------------- /src/front_end/include/theme_manager.hxx: -------------------------------------------------------------------------------- 1 | #ifndef THEME_MANAGER_HXX 2 | #define THEME_MANAGER_HXX 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | #include "../../ffi/include/config.h" 11 | 12 | using std::string_view; 13 | using std::unique_ptr; 14 | 15 | namespace Lazyboard::front_end { 16 | class ThemeManager { 17 | private: 18 | const char *data(string_view sv) noexcept { return sv.data(); } 19 | 20 | private: 21 | struct GuiSettings { 22 | string_view background_color; 23 | string_view foreground_color; 24 | string_view background_button_color; 25 | string_view foreground_button_color; 26 | string_view background_table_header_color; 27 | string_view foreground_table_header_color; 28 | }; 29 | 30 | private: 31 | void on_invalid_hex_color_error(QMainWindow *main_window); 32 | auto gui_settings(AppConfig *raw_app_config) noexcept -> GuiSettings; 33 | 34 | public: 35 | void set_main_window_theme(QMainWindow *main_window, 36 | AppConfig *raw_app_config) noexcept; 37 | }; 38 | } // namespace Lazyboard::front_end 39 | 40 | #endif -------------------------------------------------------------------------------- /tools/fmt.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | current_dir=$(basename "$(pwd)") 5 | 6 | function check() { 7 | local require_tool=$1 8 | 9 | if ! command -v "$require_tool" >/dev/null 2>&1; then 10 | echo "Could not find $require_tool in system" 11 | exit 12 | fi 13 | } 14 | 15 | function fmt() { 16 | local clang_fmt="clang-format" 17 | local cargo="cargo" 18 | local backend_dir="src/back_end" 19 | 20 | check "$clang_fmt" 21 | check "$cargo" 22 | 23 | local tools_dir="tools" 24 | local clang_fmt_cfg=".clang-format" 25 | 26 | if [[ "$tools_dir" == "$current_dir" ]]; then 27 | cd .. || exit 1 28 | fi 29 | 30 | if [ ! -f "$clang_fmt_cfg" ]; then 31 | echo "$clang_fmt_cfg not found" 32 | exit 1 33 | fi 34 | 35 | find . -name "*.cxx" -o -name "*.hxx" | while IFS= read -r file; do 36 | file_base_name=$(basename "$file") 37 | 38 | echo -e "\e[0;32m[+] Format file: $file_base_name\e[0;37m" 39 | "$clang_fmt" -i "$file" 40 | done 41 | 42 | cd "$backend_dir" || exit 1 43 | $cargo fmt 44 | echo -e "\e[0;32m[+] Format Rust backend source code sucessfully\e[0;37m" 45 | } 46 | 47 | function main() { 48 | fmt 49 | } 50 | 51 | main -------------------------------------------------------------------------------- /tests-ffi/fs.cxx: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | using std::span; 9 | using std::string; 10 | using std::uint8_t; 11 | 12 | enum class UtilsResult : uint8_t { 13 | OK, 14 | ALLOC_ERR, 15 | NULL_DEFERENCE_ERR, 16 | GET_DIR_ERR, 17 | }; 18 | 19 | extern "C" bool path_exists(const char *path); 20 | extern "C" UtilsResult cache_dir(char **out); 21 | extern "C" void free_alloc(char *c_str); 22 | 23 | int main(int argc, char *argv[]) { 24 | using R = UtilsResult; 25 | 26 | const auto this_file_span = span(argv, size_t(argc)); 27 | if (this_file_span.empty()) { 28 | abort(); 29 | } 30 | 31 | const char *file_name = this_file_span[0]; 32 | const char *wrong_path = "../abcxyzw1133"; 33 | 34 | char *out = nullptr; 35 | auto result = cache_dir(&out); 36 | const string cache_dir = string(out); 37 | free_alloc(out); 38 | 39 | assert(path_exists(file_name)); 40 | assert(!path_exists(wrong_path)); 41 | 42 | assert(result != R::ALLOC_ERR); 43 | assert(result != R::GET_DIR_ERR); 44 | assert(result != R::NULL_DEFERENCE_ERR); 45 | assert(result == R::OK); 46 | assert(!cache_dir.empty()); 47 | 48 | return 0; 49 | } -------------------------------------------------------------------------------- /src/back_end/tests/time.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn test_time_null_deference() { 3 | unsafe { 4 | use back_end::core::{result_enum::AllocResult, time::time_now}; 5 | use std::ffi::c_char; 6 | use std::ptr::null_mut; 7 | 8 | use AllocResult as R; 9 | 10 | let out: *mut *mut c_char = null_mut(); 11 | let result = time_now(out); 12 | 13 | assert_ne!(result, R::OK); 14 | assert_ne!(result, R::C_STRING_CONVERT_ERR); 15 | assert_eq!(result, R::NULL_DEFERENCE_ERR); 16 | } 17 | } 18 | 19 | #[test] 20 | fn test_time_now() { 21 | unsafe { 22 | use back_end::core::{result_enum::AllocResult, time::time_now}; 23 | use std::ffi::{CString, c_char}; 24 | use std::ptr::null_mut; 25 | 26 | use AllocResult as R; 27 | 28 | let mut out: *mut c_char = null_mut(); 29 | let result = time_now(&raw mut out); 30 | 31 | let out_str = CString::from_raw(out); 32 | let to_str = out_str.to_string_lossy(); 33 | 34 | assert_ne!(result, R::C_STRING_CONVERT_ERR); 35 | assert_ne!(result, R::NULL_DEFERENCE_ERR); 36 | assert_eq!(result, R::OK); 37 | assert!(!to_str.is_empty()); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/back_end/src/config/constant.rs: -------------------------------------------------------------------------------- 1 | /* Application settings */ 2 | pub const APP_SETTINGS: &str = "app_settings"; 3 | pub const HIDE_WHEN_CLOSED: &str = "hide_when_closed"; 4 | pub const NOTIFICATION: &str = "notification"; 5 | 6 | /* Default application settings fallback */ 7 | pub const HIDE_WHEN_CLOSED_FALLBACK: bool = false; 8 | pub const NOTIFICATION_FALLBACK: bool = false; 9 | 10 | /* Application GUI settings */ 11 | pub const APP_GUI_SETTINGS: &str = "app_gui_settings"; 12 | pub const BACKGROUND_COLOR: &str = "background_color"; 13 | pub const FOREGROUND_COLOR: &str = "foreground_color"; 14 | pub const BACKGROUND_COLOR_BUTTON: &str = "background_button_color"; 15 | pub const FOREGROUND_COLOR_BUTTON: &str = "fore_background_color"; 16 | pub const BACKGROUND_COLOR_TABLE_HEADER: &str = "background_table_header_color"; 17 | pub const FOREGROUND_COLOR_TABLE_HEADER: &str = "foreground_table_header_color"; 18 | 19 | /* Default application color fallback */ 20 | pub const FALLBACK_BG_COLOR: &str = "#2f3136"; 21 | pub const FALLBACK_FG_COLOR: &str = "#ffffff"; 22 | pub const FALLBACK_BG_BUTTON_COLOR: &str = "#2f3136"; 23 | pub const FALLBACK_FG_BUTTON_COLOR: &str = "#ffffff"; 24 | pub const FALLBACK_BG_TABLE_HEADER_COLOR: &str = "#2f3136"; 25 | pub const FALLBACK_FG_TABLE_HEADER_COLOR: &str = "#ffffff"; 26 | -------------------------------------------------------------------------------- /src/front_end/include/main_window.hxx: -------------------------------------------------------------------------------- 1 | #ifndef MAIN_WINDOWS_HXX 2 | #define MAIN_WINDOWS_HXX 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include "../../front_end_db/include/sqlite_manager.hxx" 12 | #include "about_widget.hxx" 13 | #include "main_window_preload.hxx" 14 | #include "setting_widget.hxx" 15 | #include "table_widget.hxx" 16 | 17 | using Lazyboard::front_end_db::SQLiteManager; 18 | using std::string; 19 | using std::unique_ptr; 20 | 21 | namespace Lazyboard::front_end { 22 | class MainWindow { 23 | private: 24 | using Self = MainWindow; 25 | using Window = QMainWindow; 26 | using Layout = QGridLayout; 27 | 28 | private: 29 | static constexpr int MIN_WIDTH = 600; 30 | static constexpr int MIN_HEIGHT = 400; 31 | 32 | public: 33 | MainWindow(); 34 | 35 | private: 36 | Layout *grid_layout; 37 | QWidget *central_widget; 38 | 39 | private: 40 | unique_ptr main_window; 41 | unique_ptr table_widget; 42 | unique_ptr setting_widget; 43 | unique_ptr about_widget; 44 | unique_ptr main_window_preload; 45 | unique_ptr sqlite_manager; 46 | 47 | public: 48 | Self *init_main_window(); 49 | void front_end_show(); 50 | }; 51 | } // namespace Lazyboard::front_end 52 | 53 | #endif // MAIN_WINDOWS_HXX -------------------------------------------------------------------------------- /src/ffi/include/config.h: -------------------------------------------------------------------------------- 1 | #ifndef CONFIG_RAW_H 2 | #define CONFIG_RAW_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | 10 | /* From module `utils::fs_utils` in Rust backend */ 11 | enum ConfigResult : uint8_t { 12 | OK, 13 | TOML_TO_STRING_FAILED, 14 | CREATE_DIR_FAILED, 15 | CREATE_FILE_FAILED, 16 | WRITE_FILE_FAILED, 17 | GET_CONFIG_DIR_FAILED, 18 | }; 19 | 20 | auto create_default_cfg() -> ConfigResult; 21 | /* End module */ 22 | 23 | /* From module `config` in Rust backend */ 24 | enum ReadConfigResult : uint8_t { 25 | READ_OK, 26 | READ_FILE_FAILED, 27 | UTF_8_ERROR, 28 | PARSE_TOML_FAILED, 29 | CONVERT_TO_MUT_FAILED, 30 | CONVERT_TO_C_STR_FAILED, 31 | }; 32 | 33 | typedef struct { 34 | bool hide_when_closed; 35 | bool notification; 36 | } AppSettings; 37 | 38 | typedef struct { 39 | char *background_color; 40 | char *foreground_color; 41 | char *background_button_color; 42 | char *foreground_button_color; 43 | char *background_table_header_color; 44 | char *foreground_table_header_color; 45 | } AppGuiSettings; 46 | 47 | typedef struct { 48 | AppSettings raw_app_settings; 49 | AppGuiSettings raw_app_gui_settings; 50 | } AppConfig; 51 | 52 | ReadConfigResult read_exists_config(const char *path, AppConfig *config_out); 53 | void free_app_config(AppConfig *config); 54 | /* End module */ 55 | 56 | #ifdef __cplusplus 57 | } 58 | #endif 59 | #endif // CONFIG_H -------------------------------------------------------------------------------- /scripts/Makefile: -------------------------------------------------------------------------------- 1 | build_options ?= none 2 | 3 | .PHONY: build target 4 | 5 | debug-build: 6 | @./debug.sh debug-build 7 | 8 | debug-non-gui: 9 | @./debug.sh debug-build-noshow 10 | 11 | debug-gdb: 12 | @./debug.sh debug-gdb 13 | 14 | check-frontend: 15 | @./debug.sh check-frontend 16 | 17 | check-backend: 18 | @./debug.sh check-backend 19 | 20 | dev-push: 21 | @./debug.sh push-dev 22 | 23 | master-push: 24 | @./debug.sh push-master 25 | 26 | stable-push: 27 | @./debug.sh push-stable 28 | 29 | backend-test: 30 | @./debug.sh backend-test 31 | 32 | deploy-linux: 33 | $(if $(filter $(build_options),frontend-build), \ 34 | @$(MAKE) __linux_front_end_build,) 35 | 36 | $(if $(filter $(build_options),backend-build), \ 37 | @$(MAKE) __linux_back_end_build,) 38 | 39 | $(if $(filter $(build_options),frontend-check), \ 40 | @$(MAKE) __linux_front_end_check,) 41 | 42 | $(if $(filter $(build_options),backend-check), \ 43 | @$(MAKE) __linux_back_end_check,) 44 | 45 | $(if $(filter $(build_options),backend-test), \ 46 | @$(MAKE) __linux_back_end_test,) 47 | 48 | __linux_back_end_check: 49 | @./linux_deploy.sh backend-check 50 | 51 | __linux_front_end_check: 52 | @./linux_deploy.sh frontend-check 53 | 54 | __linux_back_end_build: 55 | @./linux_deploy.sh backend-build 56 | 57 | __linux_front_end_build: 58 | @./linux_deploy.sh frontend-build 59 | 60 | __linux_back_end_test: 61 | @./linux_deploy.sh backend-test -------------------------------------------------------------------------------- /src/back_end/tests/sqlite.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn test_add_text_clipboard() { 3 | use back_end::core::sqlite::{ 4 | QueryResult, TextClipboard, add_text_clipboard, init_clipboard_cache, 5 | }; 6 | use std::ffi::CString; 7 | use std::fs; 8 | 9 | use QueryResult as R; 10 | use TextClipboard as T; 11 | 12 | let content = CString::new("my clipboard content").unwrap(); 13 | let content_hash = CString::new("my hash abc xyz ").unwrap(); 14 | let content_type = CString::new("text").unwrap(); 15 | let db_path = CString::new("test.db").unwrap(); 16 | 17 | let _ = fs::remove_file(db_path.to_str().unwrap()); 18 | 19 | let text_clipboard = T { 20 | content: content.as_ptr(), 21 | content_hash: content_hash.as_ptr(), 22 | content_type: content_type.as_ptr(), 23 | is_pinned: false, 24 | }; 25 | 26 | unsafe { 27 | let init_result = init_clipboard_cache(db_path.as_ptr()); 28 | let result = add_text_clipboard(db_path.as_ptr(), text_clipboard); 29 | 30 | assert_ne!(init_result, R::C_STR_CONVERT_ERR); 31 | assert_ne!(init_result, R::EXECUTE_SQL_ERR); 32 | assert_ne!(init_result, R::OPEN_DATABASE_ERR); 33 | assert_eq!(init_result, R::OK); 34 | 35 | assert_ne!(result, R::C_STR_CONVERT_ERR); 36 | assert_ne!(result, R::EXECUTE_SQL_ERR); 37 | assert_ne!(result, R::OPEN_DATABASE_ERR); 38 | assert_eq!(result, R::OK); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /tests-ffi/sqlite.cxx: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | using std::uint8_t; 6 | using std::filesystem::remove; 7 | 8 | extern "C" enum class QueryResult : uint8_t { 9 | OK, 10 | OPEN_DATABASE_ERR, 11 | C_STR_CONVERT_ERR, 12 | EXECUTE_SQL_ERR 13 | }; 14 | 15 | extern "C" struct TextClipboard { 16 | const char* content; 17 | const char* content_hash; 18 | const char* content_type; 19 | bool is_pinned; 20 | }; 21 | 22 | extern "C" auto init_clipboard_cache(const char* path) -> QueryResult; 23 | extern "C" QueryResult add_text_clipboard(const char* path, 24 | TextClipboard text_clipboard); 25 | 26 | int main() { 27 | using R = QueryResult; 28 | 29 | const char* test_path = "test_init.db"; 30 | remove(test_path); 31 | 32 | auto text_clipboard = TextClipboard{ 33 | .content = "test", 34 | .content_hash = "test_hash", 35 | .content_type = "text", 36 | .is_pinned = true, 37 | }; 38 | 39 | auto init_result = init_clipboard_cache(test_path); 40 | auto add_text_result = add_text_clipboard(test_path, text_clipboard); 41 | 42 | assert(init_result != R::OPEN_DATABASE_ERR); 43 | assert(init_result != R::C_STR_CONVERT_ERR); 44 | assert(init_result != R::EXECUTE_SQL_ERR); 45 | assert(init_result == R::OK); 46 | 47 | assert(add_text_result != R::C_STR_CONVERT_ERR); 48 | assert(add_text_result != R::EXECUTE_SQL_ERR); 49 | assert(add_text_result != R::OPEN_DATABASE_ERR); 50 | assert(add_text_result == R::OK); 51 | 52 | return 0; 53 | } -------------------------------------------------------------------------------- /src/back_end/src/internal/app_config.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Deserialize, Serialize)] 4 | pub struct AppSettings { 5 | pub hide_when_closed: bool, 6 | pub notification: bool, 7 | } 8 | 9 | #[derive(Deserialize, Serialize)] 10 | pub struct AppGuiSettings { 11 | pub background_color: String, 12 | pub foreground_color: String, 13 | pub background_button_color: String, 14 | pub foreground_button_color: String, 15 | pub background_table_header_color: String, 16 | pub foreground_table_header_color: String, 17 | } 18 | 19 | #[derive(Deserialize, Serialize)] 20 | pub struct AppConfig { 21 | pub app_settings: AppSettings, 22 | pub app_gui_settings: AppGuiSettings, 23 | } 24 | 25 | impl AppConfig { 26 | #[must_use] 27 | pub fn default_config() -> Self { 28 | let app_settings = AppSettings { 29 | hide_when_closed: false, 30 | notification: false, 31 | }; 32 | let app_gui_settings = AppGuiSettings { 33 | background_color: "#2f3136".to_string(), 34 | foreground_color: "#ffffff".to_string(), 35 | background_button_color: "#2f3136".to_string(), 36 | foreground_button_color: "#ffffff".to_string(), 37 | background_table_header_color: "#2f3136".to_string(), 38 | foreground_table_header_color: "#ffffff".to_string(), 39 | }; 40 | 41 | Self { 42 | app_settings, 43 | app_gui_settings, 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/front_end/table_widget.cxx: -------------------------------------------------------------------------------- 1 | #include "include/table_widget.hxx" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | #include "include/abstract_table_model.hxx" 13 | 14 | using Lazyboard::front_end::AbstractTableModel; 15 | using Lazyboard::front_end::TableWidget; 16 | using std::make_unique; 17 | using Self = TableWidget; 18 | 19 | Self::TableWidget() noexcept { 20 | table_view_widget = make_unique(); 21 | abstract_table_model = make_unique(); 22 | header_labels = {"Time", "Content", "Type", "Pinned"}; 23 | } 24 | 25 | void Self::set_resize_mode(QHeaderView *header) { 26 | header->setSectionResizeMode(AbstractTableModel::TIME, 27 | QHeaderView::ResizeToContents); 28 | header->setSectionResizeMode(AbstractTableModel::CONTENT, 29 | QHeaderView::Stretch); 30 | header->setSectionResizeMode(AbstractTableModel::TYPE, 31 | QHeaderView::ResizeToContents); 32 | header->setSectionResizeMode(AbstractTableModel::PINNED, 33 | QHeaderView::ResizeToContents); 34 | } 35 | 36 | void Self::setup_widget(QGridLayout *grid_layout) { 37 | table_view_widget->setModel(abstract_table_model.get()); 38 | table_view_widget->setEditTriggers(QAbstractItemView::AllEditTriggers); 39 | 40 | auto header = table_view_widget->horizontalHeader(); 41 | this->set_resize_mode(header); 42 | 43 | grid_layout->addWidget(table_view_widget.get(), 0, 0); 44 | } -------------------------------------------------------------------------------- /.clang-tidy: -------------------------------------------------------------------------------- 1 | Checks: > 2 | -* 3 | ,modernize-use-nullptr 4 | ,modernize-loop-convert 5 | ,readability-identifier-naming 6 | ,readability-magic-numbers 7 | ,performance-* 8 | ,bugprone-* 9 | ,cppcoreguidelines-* 10 | ,cppcoreguidelines-pro-type-reinterpret-cast 11 | ,cppcoreguidelines-pro-bounds-array-to-pointer-decay 12 | ,cert-err58-cpp 13 | ,cert-dcl58-cpp 14 | ,cert-oop54-cpp 15 | ,cert-oop11-cpp 16 | ,misc-misleading-indentation 17 | ,misc-definitions-in-headers 18 | ,misc-unused-parameters 19 | ,misc-static-assert 20 | ,-llvmlibc-* 21 | ,-fuchsia-* 22 | ,-abseil-* 23 | ,-google-* 24 | ,-zircon-* 25 | 26 | CheckOptions: 27 | - key: readability-identifier-naming.VariableCase 28 | value: lower_case 29 | - key: readability-identifier-naming.ParameterCase 30 | value: lower_case 31 | - key: readability-identifier-naming.FunctionCase 32 | value: lower_case 33 | - key: readability-identifier-naming.MethodCase 34 | value: lower_case 35 | - key: readability-identifier-naming.ClassCase 36 | value: CamelCase 37 | - key: readability-identifier-naming.StructCase 38 | value: CamelCase 39 | - key: readability-identifier-naming.EnumCase 40 | value: CamelCase 41 | - key: readability-identifier-naming.ConstVariableCase 42 | value: UPPER_CASE 43 | - key: readability-identifier-naming.ConstexprVariableCase 44 | value: UPPER_CASE 45 | - key: readability-identifier-naming.EnumConstantCase 46 | value: UPPER_CASE 47 | 48 | WarningsAsErrors: "*" 49 | HeaderFilterRegex: '^.*/src/**/*.hxx' -------------------------------------------------------------------------------- /src/front_end/include/about_window.hxx: -------------------------------------------------------------------------------- 1 | #ifndef ABOUT_WINDOW_HXX 2 | #define ABOUT_WINDOW_HXX 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | #include "../../ffi/include/utils.h" 12 | using std::unique_ptr; 13 | 14 | namespace Lazyboard::front_end { 15 | class AboutWindow : public QDialog { 16 | private: 17 | unique_ptr grid_layout; 18 | unique_ptr github_button; 19 | unique_ptr github_issue_button; 20 | unique_ptr github_pull_button; 21 | 22 | private: 23 | static constexpr int MIN_WIDTH = 600; 24 | static constexpr int MIN_HEIGHT = 600; 25 | const char *GITHUB_URL = "https://github.com/reim-developer/zClipboard"; 26 | const char *GITHUB_ISSUE_URL = 27 | "https://github.com/reim-developer/zClipboard/issues"; 28 | const char *GITHUB_PULL_URL = 29 | "https://github.com/reim-developer/zClipboard/pulls"; 30 | 31 | private: 32 | QMainWindow *_main_window = nullptr; 33 | 34 | protected: 35 | void showEvent(QShowEvent *event) override; 36 | 37 | private: 38 | void is_open_browser_ok(ResultContext &status); 39 | void open_browser_when_clicked(QPushButton *button, const char *url); 40 | 41 | private: 42 | void setup_front_end(); 43 | void setup_buttons(); 44 | void on_closed(QMainWindow *main_window); 45 | 46 | public: 47 | AboutWindow(); 48 | void show_window(); 49 | void on_about_window_event(QMainWindow *main_window); 50 | }; 51 | } // namespace Lazyboard::front_end 52 | 53 | #endif -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.19) 2 | project(Lazyboard) 3 | 4 | set(CMAKE_CXX_STANDARD 23) 5 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 6 | 7 | set(CMAKE_AUTOMOC ON) 8 | find_package(Qt6 REQUIRED COMPONENTS Widgets Gui Core) 9 | find_package(SQLite3 REQUIRED) 10 | 11 | set(RUST_BACKEND_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src/back_end) 12 | 13 | if(RELEASE) 14 | set(RUST_BACKEND_LIB ${RUST_BACKEND_DIR}/target/release/libback_end.a) 15 | 16 | elseif(LAZY_DEBUG) 17 | add_compile_definitions(PRIVATE LAZY_DEBUG) 18 | set(RUST_BACKEND_LIB ${RUST_BACKEND_DIR}/target/debug/libback_end.a) 19 | endif() 20 | 21 | 22 | file(GLOB_RECURSE SOURCES_CXX "src/*.cxx") 23 | file(GLOB_RECURSE SOURCES_C "src/*.c") 24 | file(GLOB_RECURSE HEADER_SOURCES "src/*.h" "src/*.hxx") 25 | 26 | add_custom_target( 27 | BuildRustBacked 28 | DEPENDS ${RUST_BACKEND_LIB} 29 | ) 30 | 31 | qt_add_executable(${PROJECT_NAME} ${SOURCES_CXX} ${SOURCES_C} ${HEADER_SOURCES}) 32 | add_dependencies(${PROJECT_NAME} BuildRustBacked) 33 | 34 | target_link_libraries( 35 | ${PROJECT_NAME} 36 | PRIVATE Qt6::Widgets 37 | PRIVATE Qt6::Core 38 | PRIVATE Qt6::Gui 39 | PRIVATE SQLite::SQLite3 40 | PRIVATE ${RUST_BACKEND_LIB} 41 | ) 42 | 43 | if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") 44 | target_compile_options(${PROJECT_NAME} PRIVATE "$<$:-fno-direct-access-external-data>") 45 | set(CMAKE_CXX_CLANG_TIDY clang-tidy) 46 | endif() 47 | 48 | message(STATUS "Current C Compiler: ${CMAKE_C_COMPILER}") 49 | message(STATUS "Current C++ Compiler: ${CMAKE_CXX_COMPILER}") 50 | 51 | install(TARGETS ${PROJECT_NAME} DESTINATION bin) -------------------------------------------------------------------------------- /tests-ffi/tests.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | set -e 3 | 4 | function check() { 5 | local required_tool=$1 6 | 7 | if ! command -v "$required_tool" >/dev/null 2>&1; then 8 | echo "$required_tool not found in system" 9 | exit 10 | fi 11 | } 12 | 13 | function build_backend() { 14 | local backend_dir="src/back_end" 15 | local tests_ffi_dir="tests-ffi" 16 | local cargo="cargo" 17 | 18 | check "$cargo" 19 | cd .. 20 | if [ ! -d "$backend_dir" ]; then 21 | echo "$backend_dir not found" 22 | exit 1 23 | fi 24 | 25 | cd "$backend_dir" || exit 1 26 | $cargo build 27 | cd ../.. 28 | cd "$tests_ffi_dir" || exit 1 29 | } 30 | 31 | function run_ffi_test() { 32 | local clang_cxx="clang++" 33 | local static_lib_path="../src/back_end/target/debug/libback_end.a" 34 | local build_dir="build" 35 | local sqlite_lib="sqlite3" 36 | check "$clang_cxx" 37 | 38 | echo -e "\e[0;32m[+] Build backend:\e[0;37m" 39 | build_backend 40 | echo -e "\e[0;32m[+] Build done\e[0;37m" 41 | 42 | if [ ! -d "$build_dir" ]; then 43 | mkdir -p "$build_dir" 44 | fi 45 | 46 | for file in *.cxx; do 47 | build_output=$(basename "$file" ".cxx") 48 | file_name=$(basename "$file") 49 | 50 | $clang_cxx "$file" -l"$sqlite_lib" "$static_lib_path" -o "$build_dir/$build_output" -std=c++20 51 | echo 52 | echo -e -n "\e[0;32m[+] Test for $file_name:\e[0;37m" 53 | 54 | time_test=$( { 55 | time ./"$build_dir/$build_output" 56 | } ) 57 | 58 | status_code=$? 59 | echo -e -n "$time_test" 60 | echo -e "\e[0;32m[+] Test for $file_name success with status code: $status_code\e[0;37m" 61 | 62 | for _ in {1..25}; do 63 | printf "_" 64 | done 65 | echo 66 | done 67 | } 68 | 69 | function main() { 70 | run_ffi_test 71 | } 72 | 73 | main -------------------------------------------------------------------------------- /.github/workflows/linux.yml: -------------------------------------------------------------------------------- 1 | name: Deploy Lazyboard for Linux 2 | 3 | on: 4 | push: 5 | branches: [ "master"] 6 | 7 | pull_request: 8 | branches: ["master"] 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | env: 14 | CI_SCRIPT: "scripts/linux_deploy.sh" 15 | APP_NAME: "Lazyboard" 16 | 17 | steps: 18 | - name: Checkout code 19 | uses: actions/checkout@v4 20 | 21 | - name: Install dependencies 22 | working-directory: ${{ github.workspace }} 23 | shell: bash 24 | run: | 25 | chmod +x ${{ github.workspace }}/${{ env.CI_SCRIPT }} 26 | ./${{ env.CI_SCRIPT }} install-dependency 27 | 28 | - name: Install Qt 29 | uses: jurplel/install-qt-action@v4 30 | 31 | - name: Install Rust 32 | shell: bash 33 | run: | 34 | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y 35 | echo "$HOME/.cargo/bin:$PATH" >> $GITHUB_PATH 36 | 37 | - name: Install Rust Components 38 | shell: bash 39 | run: | 40 | rustup component add clippy 41 | 42 | - name: Linter Check (Rust Backend) 43 | shell: bash 44 | run: | 45 | make deploy-linux build_options=backend-check 46 | 47 | - name: Test (Rust Backend) 48 | shell: bash 49 | run: | 50 | make deploy-linux build_options=backend-test 51 | 52 | - name: Build (Rust Backend) 53 | shell: bash 54 | run: 55 | make deploy-linux build_options=backend-build 56 | 57 | - name: Build (C++ Frontend) 58 | shell: bash 59 | run: | 60 | make deploy-linux build_options=frontend-build 61 | 62 | - name: Linter Check (C++ Frontend) 63 | shell: bash 64 | run: | 65 | make deploy-linux build_options=frontend-check 66 | -------------------------------------------------------------------------------- /tests-ffi/sha.cxx: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | // clang-format off 8 | enum class AllocResult : uint8_t { 9 | OK, 10 | NULL_DEFERENCE_ERR, 11 | C_STRING_CONVERT_ERR 12 | }; // clang-format on 13 | extern "C" AllocResult text_sha256(const char* text, char** out); 14 | extern "C" void free_alloc(char* str); 15 | 16 | using std::string; 17 | 18 | void test_success() { 19 | using R = AllocResult; 20 | 21 | const char* my_text = "Hello"; 22 | const char* real_result = 23 | "185f8db32271fe25f561a6fc938b2e264306ec304eda518007d1764826381969"; 24 | char* out = nullptr; 25 | 26 | auto result = text_sha256(my_text, &out); 27 | string hash_result = string(out); 28 | free_alloc(out); 29 | 30 | assert(!hash_result.empty()); 31 | assert(hash_result == real_result); 32 | 33 | assert(result != R::C_STRING_CONVERT_ERR); 34 | assert(result != R::NULL_DEFERENCE_ERR); 35 | assert(result == R::OK); 36 | } 37 | 38 | void test_deference_err() { 39 | using R = AllocResult; 40 | 41 | const char* my_text = nullptr; 42 | char* out = nullptr; 43 | 44 | auto result = text_sha256(my_text, nullptr); 45 | free_alloc(out); 46 | 47 | assert(result != R::OK); 48 | assert(result != R::C_STRING_CONVERT_ERR); 49 | assert(result == R::NULL_DEFERENCE_ERR); 50 | } 51 | 52 | void test_sha_mismatch_err() { 53 | using R = AllocResult; 54 | 55 | const char* my_text = "Hello but my SHA will mismatch, to sad..."; 56 | const char* real_result = "185f8db32271fe25f561a6..."; 57 | char* out = nullptr; 58 | 59 | auto result = text_sha256(my_text, &out); 60 | string hash_result = string(out); 61 | free_alloc(out); 62 | 63 | assert(result != R::C_STRING_CONVERT_ERR); 64 | assert(result != R::NULL_DEFERENCE_ERR); 65 | assert(result == R::OK); 66 | 67 | assert(!hash_result.empty()); 68 | assert(hash_result != real_result); 69 | } 70 | 71 | int main() { 72 | test_success(); 73 | test_deference_err(); 74 | test_sha_mismatch_err(); 75 | return 0; 76 | } -------------------------------------------------------------------------------- /src/front_end/main_window.cxx: -------------------------------------------------------------------------------- 1 | #include "include/main_window.hxx" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | #include "../front_end_utils/include/utils.hxx" 11 | #include "include/about_widget.hxx" 12 | #include "include/icon_bytes.hxx" 13 | #include "include/main_window_preload.hxx" 14 | #include "include/setting_widget.hxx" 15 | #include "include/table_widget.hxx" 16 | 17 | using Lazyboard::front_end::MainWindow; 18 | using Lazyboard::front_end_utils::image_from_bytes; 19 | using std::make_unique; 20 | 21 | using Self = MainWindow; 22 | 23 | Self::MainWindow() { 24 | main_window = make_unique(); 25 | main_window_preload = make_unique(); 26 | sqlite_manager = make_unique(); 27 | 28 | auto central_w = make_unique(); 29 | auto layout_w = make_unique(central_w.get()); 30 | 31 | main_window->setCentralWidget(central_w.get()); 32 | 33 | central_widget = central_w.release(); 34 | grid_layout = layout_w.release(); 35 | } 36 | 37 | Self *Self::init_main_window() { 38 | main_window->setMinimumSize(MIN_WIDTH, MIN_HEIGHT); 39 | main_window->setWindowTitle("Lazyboard"); 40 | main_window->setWindowIcon(image_from_bytes(image_bytes)); 41 | 42 | main_window_preload->create_default_config(main_window.get()); 43 | main_window_preload->read_if_exists_config(main_window.get()); 44 | sqlite_manager->create_clipboard_cache(main_window.get()); 45 | 46 | this->front_end_show(); 47 | main_window->show(); 48 | 49 | return this; 50 | } 51 | void Self::front_end_show() { 52 | table_widget = make_unique(); 53 | setting_widget = make_unique(); 54 | about_widget = make_unique(); 55 | 56 | table_widget->setup_widget(this->grid_layout); 57 | setting_widget->setup_widget(this->grid_layout); 58 | 59 | about_widget->setup_widget(this->grid_layout); 60 | about_widget->setup_about_window_event(main_window.get()); 61 | } -------------------------------------------------------------------------------- /.github/workflows/linux_dev.yml: -------------------------------------------------------------------------------- 1 | name: Build (Debug) Lazyboard for Linux 2 | 3 | on: 4 | push: 5 | branches: [ "dev"] 6 | 7 | pull_request: 8 | branches: ["dev"] 9 | 10 | env: 11 | CARGO_TERM_COLOR: always 12 | 13 | jobs: 14 | build: 15 | runs-on: ubuntu-latest 16 | env: 17 | APP_NAME: "Lazyboard" 18 | 19 | steps: 20 | - name: Checkout code 21 | uses: actions/checkout@v4 22 | 23 | - name: Install Rust 24 | shell: bash 25 | run: | 26 | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y 27 | echo "$HOME/.cargo/bin:$PATH" >> $GITHUB_PATH 28 | 29 | - name: Install requirements 30 | shell: bash 31 | run: | 32 | sudo apt-get update 33 | sudo apt-get install -y \ 34 | git cmake make ccache \ 35 | clang sqlite3 36 | 37 | - name: Install Qt 38 | uses: jurplel/install-qt-action@v4 39 | 40 | - name: Add Rust components 41 | shell: bash 42 | run: | 43 | rustup component add clippy 44 | 45 | - name: Linter Check (Backend) 46 | shell: bash 47 | run: | 48 | make check-backend 49 | 50 | - name: Backend Test 51 | shell: bash 52 | run: | 53 | make backend-test 54 | 55 | - name: Frontend + Backend FFI Test 56 | shell: bash 57 | run: | 58 | make tests-ffi 59 | 60 | - name: Build Lazyboard 61 | shell: bash 62 | run: | 63 | make debug-non-gui 64 | 65 | - name: Linter Check (Frontend) 66 | shell: bash 67 | run: | 68 | make check-frontend 69 | 70 | - name: Upload Release 71 | uses: actions/upload-artifact@v4 72 | with: 73 | name: ${{ env.APP_NAME }} 74 | path: build/${{ env.APP_NAME }} 75 | -------------------------------------------------------------------------------- /src/front_end_db/sqlite_manager.cxx: -------------------------------------------------------------------------------- 1 | #include "include/sqlite_manager.hxx" 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #include "../ffi/include/sqlite.h" 9 | #include "../ffi/include/utils.h" 10 | #include "../front_end_utils/include/utils.hxx" 11 | 12 | #if defined(LAZY_DEBUG) 13 | #include 14 | using std::cout; 15 | #endif 16 | 17 | using Lazyboard::front_end_db::SQLiteManager; 18 | using Lazyboard::front_end_utils::error_dialog_show; 19 | using Lazyboard::front_end_utils::ErrorTypes; 20 | using Self = SQLiteManager; 21 | using std::format; 22 | using std::string; 23 | using Status = QueryResult; 24 | 25 | void Self::on_create_clipboard_cache_error(const QueryResult& result) noexcept { 26 | using E = ErrorTypes; 27 | using R = QueryResult; 28 | 29 | switch (result) { 30 | case R::OK: 31 | break; 32 | 33 | case R::C_STR_CONVERT_ERR: 34 | error_dialog_show(this->_main_window, E::CONVERT_TO_C_STR_ERR); 35 | break; 36 | 37 | case R::OPEN_DATABASE_ERR: 38 | error_dialog_show(this->_main_window, E::OPEN_DATABASE_ERR); 39 | break; 40 | 41 | case R::EXECUTE_SQL_ERR: 42 | error_dialog_show(this->_main_window, E::EXECUTE_SQL_ERR); 43 | } 44 | } 45 | 46 | void Self::on_create_folder_error(const ResultContext& result) noexcept { 47 | using E = ErrorTypes; 48 | using R = ResultContext; 49 | 50 | switch (result) { 51 | case R::OK: 52 | break; 53 | 54 | case R::FAILED: 55 | error_dialog_show(this->_main_window, E::CREATE_DIR_FAILED); 56 | break; 57 | } 58 | } 59 | 60 | void Self::create_clipboard_cache(QMainWindow* main_window) { 61 | this->_main_window = main_window; 62 | 63 | char* out = nullptr; 64 | auto result = cache_dir(&out); 65 | 66 | string cache_dir_string = format("{}/Lazyboard", out); 67 | auto database_path = format("{}/clipboard_cache.db", cache_dir_string); 68 | free_alloc(out); 69 | 70 | const auto create_dir_result = new_folder(cache_dir_string.data()); 71 | on_create_folder_error(create_dir_result); 72 | 73 | const auto status = init_clipboard_cache(database_path.data()); 74 | on_create_clipboard_cache_error(status); 75 | 76 | // clang-format off 77 | #if defined (LAZY_DEBUG) 78 | cout << "Found cache directory path: " << database_path << "\n"; 79 | #endif // clang-format on 80 | } -------------------------------------------------------------------------------- /src/back_end/tests/utf8.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | /* Null deference beavior test. */ 3 | fn test_utf8_null_dererence() { 4 | use back_end::core::result_enum::AllocResult; 5 | use back_end::core::utf8::to_utf8; 6 | use std::ffi::c_char; 7 | use std::ptr; 8 | 9 | use AllocResult as R; 10 | 11 | unsafe { 12 | let mut output_null: *mut c_char = ptr::null_mut(); 13 | let result_null: *const c_char = ptr::null(); 14 | 15 | let result = to_utf8(result_null, &raw mut output_null); 16 | 17 | assert_ne!(result, R::OK); 18 | assert_ne!(result, R::C_STRING_CONVERT_ERR); 19 | assert_eq!(result, R::NULL_DEFERENCE_ERR); 20 | } 21 | } 22 | 23 | #[test] 24 | fn test_utf8() { 25 | unsafe { 26 | use back_end::core::result_enum::AllocResult; 27 | use back_end::core::utf8::to_utf8; 28 | use std::ffi::{CStr, CString, c_char}; 29 | use std::ptr::null_mut; 30 | 31 | use AllocResult as R; 32 | 33 | let mut out: *mut c_char = null_mut(); 34 | let content = CString::new( 35 | "café 世界 🌍\n春の海 ひねもすのたり のたりかな\nشكرا لك.\n 36 | Cá trèo lên cây cao, khỉ bơi tung tăng mặt hồ!\n 37 | Thấy em thật là vui khi không buồn và thật buồn khi mà không vui...\n 38 | Anh nói với họ rằng, có kì lân thực sự và con gà nhà anh đã đấm chết một 39 | con voi nặng 69 tấn nhưng họ lại bắt anh, anh tự hỏi vì sao nhỉ em?", 40 | ) 41 | .unwrap(); 42 | 43 | let result = to_utf8(content.as_ptr(), &raw mut out); 44 | let content_str = CStr::from_ptr(out).to_str().unwrap(); 45 | 46 | assert_ne!(result, R::C_STRING_CONVERT_ERR); 47 | assert_ne!(result, R::NULL_DEFERENCE_ERR); 48 | assert_eq!(result, R::OK); 49 | assert!(!content_str.is_empty()); 50 | assert_eq!( 51 | content_str, 52 | "café 世界 🌍\n春の海 ひねもすのたり のたりかな\nشكرا لك.\n 53 | Cá trèo lên cây cao, khỉ bơi tung tăng mặt hồ!\n 54 | Thấy em thật là vui khi không buồn và thật buồn khi mà không vui...\n 55 | Anh nói với họ rằng, có kì lân thực sự và con gà nhà anh đã đấm chết một 56 | con voi nặng 69 tấn nhưng họ lại bắt anh, anh tự hỏi vì sao nhỉ em?" 57 | ); 58 | 59 | let _ = CString::from_raw(out); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/front_end_utils/include/error_types.hxx: -------------------------------------------------------------------------------- 1 | #ifndef ERROR_TYPES_HXX 2 | #define ERROR_TYPES_HXX 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace Lazyboard::front_end_utils { 12 | 13 | using std::array; 14 | using std::initializer_list; 15 | using std::map; 16 | using std::pair; 17 | using std::string_view; 18 | using std::uint8_t; 19 | 20 | enum class ErrorTypes : uint8_t { 21 | INVALID_HEX_COLOR = 1, 22 | CREATE_DIR_FAILED, 23 | GET_CONFIG_DIR_FAILED, 24 | CREATE_FILE_FAILED, 25 | TOML_TO_STRING_FAILED, 26 | WRITE_FILE_FAILED, 27 | CONVERT_TO_MUT_FAILED, 28 | CONVERT_TO_C_STR_ERR, 29 | PARSE_TOML_FAILED, 30 | READ_FILE_FAILED, 31 | UTF_8_ERR, 32 | OPEN_DATABASE_ERR, 33 | EXECUTE_SQL_ERR, 34 | WRAP_C_STR_ERR, 35 | }; 36 | 37 | using Types = initializer_list>; 38 | inline constexpr Types error_types_map() noexcept { 39 | using E = ErrorTypes; 40 | 41 | static constexpr Types ERROR_TYPES = { 42 | {E::INVALID_HEX_COLOR, 43 | "Invalid HEX color, please check your TOML configuration and try " 44 | "again"}, 45 | {E::CREATE_DIR_FAILED, "Create directory failed"}, 46 | {E::GET_CONFIG_DIR_FAILED, "Get configuration directory failed"}, 47 | {E::CREATE_FILE_FAILED, "Create file faled"}, 48 | {E::TOML_TO_STRING_FAILED, "Could not convert TOML to string"}, 49 | {E::WRITE_FILE_FAILED, "Could not write file"}, 50 | {E::CONVERT_TO_MUT_FAILED, "Could not convert value to '*mut c_char'"}, 51 | {E::CONVERT_TO_C_STR_ERR, "Could not convert value to 'c_str'"}, 52 | {E::PARSE_TOML_FAILED, 53 | "Could not parse TOML, please check your configuration and try " 54 | "again"}, 55 | {E::READ_FILE_FAILED, "Could not read file"}, 56 | {E::UTF_8_ERR, "UTF-8 error"}, 57 | {E::EXECUTE_SQL_ERR, "Could not execute SQLite query"}, 58 | {E::OPEN_DATABASE_ERR, "Could not create database"}, 59 | {E::WRAP_C_STR_ERR, "Could not wrap 'c_str'"}, 60 | }; 61 | 62 | return ERROR_TYPES; 63 | } 64 | 65 | inline constexpr string_view error_to_string(ErrorTypes error) noexcept { 66 | for (const auto &error_types : error_types_map()) { 67 | if (error_types.first == error) { 68 | return error_types.second; 69 | } 70 | } 71 | 72 | return "UNKNOWN_ERROR"; 73 | } 74 | 75 | } // namespace Lazyboard::front_end_utils 76 | #endif // ERROR_TYPES_HXX -------------------------------------------------------------------------------- /tools/helper.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import argparse 4 | import os 5 | import sys 6 | 7 | RED = "\033[1;31m" 8 | WHITE = "\033[0m" 9 | GREEN = "\033[0;32m" 10 | 11 | stderr = sys.stderr.write 12 | abort = sys.exit 13 | 14 | parser = argparse.ArgumentParser(description = "Helper Tools For Lazyboard Project") 15 | sub_parsers = parser.add_subparsers(dest = "command") 16 | 17 | def gen_to_hex(data: bytes) -> str: 18 | return ", ".join(f"0x{b:02x}" for b in data) 19 | 20 | def file_name(file_str: str) -> str: 21 | return os.path.basename(file_str) 22 | 23 | def file_extension(file: str) -> str: 24 | _, extension = os.path.splitext(file) 25 | 26 | return extension 27 | 28 | def gen_res(args: argparse.Namespace) -> None: 29 | file_path = args.path 30 | cxx_output = args.output 31 | 32 | if not os.path.exists(file_path): 33 | stderr(f"{RED}error:{WHITE} Resource file path {file_path} not found\n") 34 | abort(1) 35 | 36 | if not file_extension(cxx_output) == ".cxx": 37 | cxx_output += ".hxx" 38 | 39 | try: 40 | with open(file = file_path, mode = "rb") as image_file: 41 | read_bytes = gen_to_hex(image_file.read()) 42 | 43 | with open(file = cxx_output, mode = "w", encoding = "utf-8") as output: 44 | header_name = file_name(cxx_output).upper().replace(".", "_") 45 | 46 | output.write(f"#ifndef {header_name}\n") 47 | output.write(f"#define {header_name}\n\n") 48 | output.write(f"#include \n\n") 49 | output.write("#include \n\n") 50 | output.write("using std::initializer_list;\n\n") 51 | output.write(f"const initializer_list image_bytes = {{\n") 52 | output.write(f" " + read_bytes + "\n") 53 | output.write("};\n") 54 | 55 | output.write(f"#endif // {header_name}") 56 | 57 | print(f"{GREEN}ok:{WHITE} Generated successfully, can found header file at: {cxx_output}") 58 | 59 | except Exception as error: 60 | stderr(f"{RED}error:{WHITE} Exception error: {error}") 61 | 62 | def gen_resources_command() -> None: 63 | gen_res_cmd = sub_parsers.add_parser("gen-res", help = "Gen resource to C++ header") 64 | gen_res_cmd.add_argument("--path", help = "Resource file path") 65 | gen_res_cmd.add_argument("--output", help = "C++ source output") 66 | gen_res_cmd.set_defaults(func = gen_res) 67 | 68 | def main() -> None: 69 | gen_resources_command() 70 | arguments = parser.parse_args() 71 | 72 | if hasattr(arguments, "func"): 73 | arguments.func(arguments) 74 | 75 | else: 76 | parser.print_help() 77 | 78 | main() -------------------------------------------------------------------------------- /scripts/linux_deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | build_dir="build" 4 | release_flags="-DCMAKE_BUILD_TYPE=Release" 5 | opt_flags="-O3 -march=native -flto -funroll-loops -fomit-frame-pointer -fstrict-aliasing -ftree-vectorize -fvisibility=hidden" 6 | nproc=$(nproc) 7 | 8 | install_dependency() { 9 | sudo apt-get update 10 | sudo apt-get install -y git cmake \ 11 | clang sqlite3 12 | } 13 | 14 | function check() { 15 | if ! command -v "$1" >/dev/null 2>&1; then 16 | echo "[ERR] Could not find $1 in your system. Aborting" 17 | exit 1 18 | fi 19 | } 20 | 21 | function linter_check() { 22 | local options=$1 23 | 24 | if [[ $options == "clippy" ]]; then 25 | local cargo="cargo" 26 | local backend="src/back_end" 27 | check "$cargo" 28 | 29 | cd .. 30 | cd "$backend" || exit 1 31 | "$cargo" clippy \ 32 | --all-targets --all-features \ 33 | -- -D clippy::all -D clippy::pedantic \ 34 | -D clippy::nursery -D clippy::perf 35 | 36 | else 37 | local clang_tidy="clang-tidy" 38 | check "$clang_tidy" 39 | 40 | cd .. 41 | clang-tidy src/**/*.cxx -p=build 42 | fi 43 | } 44 | 45 | 46 | build_backend() { 47 | local cargo="cargo" 48 | local back_end="src/back_end" 49 | check "$cargo" 50 | check "rustup" 51 | 52 | cd .. 53 | cd "$back_end" || exit 1 54 | "$cargo" build --release 55 | } 56 | 57 | backend_test() { 58 | local cargo="cargo" 59 | local back_end="src/back_end" 60 | check "$cargo" 61 | check "rustup" 62 | 63 | cd .. 64 | cd "$back_end" || exit 1 65 | "$cargo" test 66 | } 67 | 68 | build_frontend() { 69 | cd .. 70 | 71 | if [ ! -d "$build_dir" ]; then 72 | mkdir -p "$build_dir" 73 | fi 74 | 75 | cd "$build_dir" || exit 76 | 77 | cmake -DCMAKE_C_COMPILER=clang \ 78 | -DCMAKE_CXX_COMPILER=clang++ \ 79 | -DCMAKE_CXX_FLAGS="$opt_flags" \ 80 | -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ 81 | -DRELEASE=ON \ 82 | -DLAZY_DEBUG=OFF \ 83 | "$release_flags" \ 84 | .. 85 | 86 | make -j "$nproc" 87 | } 88 | 89 | match_options() { 90 | case "$1" in 91 | "install-dependency") install_dependency ;; 92 | "frontend-build") build_frontend ;; 93 | "backend-build") build_backend ;; 94 | "frontend-check") { 95 | local clang_tidy="clang-tidy" 96 | linter_check "$clang_tidy" 97 | } ;; 98 | "backend-check") { 99 | local clippy="clippy" 100 | linter_check "$clippy" 101 | } ;; 102 | "backend-test") { 103 | backend_test 104 | } ;; 105 | *) 106 | echo "Usage: $0 {install-dependency | frontend-build | backend-build}" 107 | exit 1 108 | esac 109 | } 110 | 111 | match_options "$1" -------------------------------------------------------------------------------- /src/front_end_utils/include/utils.hxx: -------------------------------------------------------------------------------- 1 | #ifndef FRONT_END_UTILS_HXX 2 | #define FRONT_END_UTILS_HXX 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include "error_types.hxx" 20 | 21 | using std::abort; 22 | using std::for_each; 23 | using std::forward; 24 | using std::initializer_list; 25 | using std::is_same_v; 26 | using std::string; 27 | using std::stringstream; 28 | using std::vector; 29 | 30 | #if defined(LAZY_DEBUG) 31 | #include 32 | #include 33 | using std::println; 34 | using std::source_location; 35 | #endif 36 | 37 | namespace Lazyboard::front_end_utils { 38 | template 39 | concept qcolor_bound = requires(const T& t) { QColor(t); }; 40 | 41 | template 42 | inline constexpr bool is_valid_hex_color(Args&&... args) noexcept { 43 | return (... && [&]() { 44 | QColor color(std::forward(args)); 45 | 46 | if (!color.isValid()) { 47 | return false; 48 | } 49 | 50 | return true; 51 | }()); 52 | } 53 | 54 | using init_list = initializer_list; 55 | inline QIcon image_from_bytes(const init_list& data) noexcept { 56 | QByteArray bytes_array; 57 | bytes_array.reserve(static_cast(data.size())); 58 | 59 | for_each(data.begin(), data.end(), [&bytes_array](auto bytes) { 60 | bytes_array.append(static_cast(bytes)); 61 | }); 62 | 63 | QPixmap pixmap; 64 | if (pixmap.loadFromData(bytes_array)) { 65 | return QIcon(pixmap); 66 | } 67 | 68 | return QIcon(); 69 | } 70 | 71 | inline void error_dialog_show(QWidget* parent, 72 | ErrorTypes error_types) noexcept { 73 | auto error_string = error_to_string(error_types).data(); 74 | 75 | QMessageBox::critical(parent, "Error", error_string); 76 | abort(); 77 | } 78 | 79 | #if defined(LAZY_DEBUG) 80 | const auto green = "\x1b[32m"; 81 | const auto white = "\x1b[37m"; 82 | 83 | using src_loc = source_location; 84 | template 85 | inline void dump_ptr_address(T* t, 86 | const src_loc& location = src_loc::current()) { 87 | auto file_name = location.file_name(); 88 | auto file_line = location.line(); 89 | 90 | println("{}[MEMORY_DEBUG]{} File: {}", green, white, file_name); 91 | println("{}[MEMORY_DEBUG]{} Line: {}", green, white, file_line); 92 | println("{}[MEMORY_DEBUG]{} {}", green, white, static_cast(t)); 93 | } 94 | 95 | template 96 | concept string_bound = requires(const T& t) { std::string(t); }; 97 | 98 | template 99 | inline void debug_info(string_bound&& information) { 100 | auto info = std::forward(information); 101 | 102 | println("{}[INFO_DEBUG]{} {}", green, white, info); 103 | } 104 | 105 | #endif 106 | 107 | } // namespace Lazyboard::front_end_utils 108 | 109 | #endif -------------------------------------------------------------------------------- /tests-ffi/toml_config.cxx: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | using std::make_unique; 7 | using std::string; 8 | using std::uint8_t; 9 | using std::unique_ptr; 10 | 11 | extern "C" enum ReadAppConfigStatus : uint8_t { 12 | OK, 13 | READ_FILE_FAILED, 14 | UTF_8_ERROR, 15 | PARSE_TOML_FAILED, 16 | CONVERT_TO_MUT_FAILED, 17 | CONVERT_TO_C_STR_FAILED, 18 | }; 19 | 20 | extern "C" typedef struct { 21 | bool hide_when_closed; 22 | bool notification; 23 | } AppSettings; 24 | 25 | extern "C" typedef struct { 26 | char *background_color; 27 | char *foreground_color; 28 | char *background_button_color; 29 | char *foreground_button_color; 30 | char *background_table_header_color; 31 | char *foreground_table_header_color; 32 | } AppGuiSettings; 33 | 34 | extern "C" typedef struct { 35 | AppSettings raw_app_settings; 36 | AppGuiSettings raw_app_gui_settings; 37 | } AppConfig; 38 | 39 | extern "C" ReadAppConfigStatus read_exists_config(const char *file_path, 40 | AppConfig *out); 41 | 42 | extern "C" void free_app_config(AppConfig *config); 43 | 44 | int main() { 45 | using Status = ReadAppConfigStatus; 46 | 47 | const char *path = "demo.toml"; 48 | unique_ptr raw = make_unique(); 49 | 50 | auto result = read_exists_config(path, raw.get()); 51 | auto bg_color = string(raw->raw_app_gui_settings.background_color); 52 | auto fg_color = string(raw->raw_app_gui_settings.foreground_color); 53 | auto bg_btn_color = 54 | string(raw->raw_app_gui_settings.background_button_color); 55 | auto fg_btn_color = 56 | string(raw->raw_app_gui_settings.foreground_button_color); 57 | auto bg_header_table_color = 58 | string(raw->raw_app_gui_settings.background_table_header_color); 59 | auto fg_header_table_color = 60 | string(raw->raw_app_gui_settings.foreground_table_header_color); 61 | 62 | free_app_config(raw.get()); 63 | 64 | assert(result != Status::UTF_8_ERROR); 65 | assert(result != Status::PARSE_TOML_FAILED); 66 | assert(result != Status::READ_FILE_FAILED); 67 | assert(result != Status::CONVERT_TO_MUT_FAILED); 68 | assert(result != Status::CONVERT_TO_C_STR_FAILED); 69 | assert(result == Status::OK); 70 | 71 | assert(raw->raw_app_settings.hide_when_closed != true); 72 | assert(raw->raw_app_settings.hide_when_closed == false); 73 | assert(raw->raw_app_settings.notification != false); 74 | assert(raw->raw_app_settings.notification == true); 75 | 76 | assert(!bg_color.empty()); 77 | assert(!fg_color.empty()); 78 | assert(!bg_btn_color.empty()); 79 | assert(!fg_btn_color.empty()); 80 | assert(!bg_header_table_color.empty()); 81 | assert(!fg_header_table_color.empty()); 82 | 83 | assert(fg_header_table_color != "&!$!$?"); 84 | assert(bg_header_table_color != "$!&#&!"); 85 | assert(fg_header_table_color == "#ffffff"); 86 | assert(bg_header_table_color == "#2f3136"); 87 | 88 | assert(bg_color != "717841xx18"); 89 | assert(fg_color != "81388181"); 90 | assert(bg_btn_color != "31831d$"); 91 | assert(bg_btn_color == "#2f3136"); 92 | 93 | assert(fg_btn_color != "#&$!&!&"); 94 | assert(fg_btn_color == "#ffffff"); 95 | assert(bg_color == "#2f3136"); 96 | assert(fg_color == "#ffffff"); 97 | 98 | return 0; 99 | } -------------------------------------------------------------------------------- /src/back_end/src/core/sqlite.rs: -------------------------------------------------------------------------------- 1 | use rusqlite::Connection; 2 | use std::ffi::{CStr, c_char}; 3 | 4 | #[derive(PartialEq, Eq, Debug)] 5 | #[repr(C)] 6 | #[allow(non_camel_case_types)] 7 | pub enum QueryResult { 8 | OK, 9 | OPEN_DATABASE_ERR, 10 | C_STR_CONVERT_ERR, 11 | EXECUTE_SQL_ERR, 12 | } 13 | 14 | const INIT_QUERY: &str = r"--sql 15 | CREATE TABLE IF NOT EXISTS clipboard_cache ( 16 | id INTEGER PRIMARY KEY AUTOINCREMENT, 17 | content_hash TEXT NOT NULL UNIQUE, 18 | time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, 19 | content TEXT NOT NULL, 20 | content_type TEXT NOT NULL, 21 | is_pinned BOOLEAN NOT NULL DEFAULT FALSE 22 | ); 23 | "; 24 | 25 | #[unsafe(no_mangle)] 26 | /// # Safety 27 | /// Careful with unsafe context & raw pointers. 28 | pub unsafe extern "C" fn init_clipboard_cache( 29 | file_path: *const c_char, 30 | ) -> QueryResult { 31 | unsafe { 32 | use QueryResult as R; 33 | 34 | let Ok(file_path_cstr) = CStr::from_ptr(file_path).to_str() else { 35 | return R::C_STR_CONVERT_ERR; 36 | }; 37 | 38 | let Ok(sql) = Connection::open(file_path_cstr) else { 39 | return R::OPEN_DATABASE_ERR; 40 | }; 41 | 42 | let is_err = sql.execute(INIT_QUERY, ()).is_err(); 43 | 44 | if is_err { 45 | return R::EXECUTE_SQL_ERR; 46 | } 47 | 48 | R::OK 49 | } 50 | } 51 | 52 | #[repr(C)] 53 | pub struct TextClipboard { 54 | pub content: *const c_char, 55 | pub content_hash: *const c_char, 56 | pub content_type: *const c_char, 57 | pub is_pinned: bool, 58 | } 59 | 60 | /// # Safety 61 | /// Careful with raw pointers & memory leaks. 62 | #[must_use] 63 | #[unsafe(no_mangle)] 64 | pub unsafe extern "C" fn add_text_clipboard( 65 | db_path: *const c_char, 66 | text_clipboard: TextClipboard, 67 | ) -> QueryResult { 68 | use QueryResult as R; 69 | 70 | unsafe { 71 | let Ok(file_path_cstr) = CStr::from_ptr(db_path).to_str() else { 72 | return R::C_STR_CONVERT_ERR; 73 | }; 74 | 75 | let Ok(sql) = Connection::open(file_path_cstr) else { 76 | return R::OPEN_DATABASE_ERR; 77 | }; 78 | 79 | let Ok(content_str) = CStr::from_ptr(text_clipboard.content).to_str() 80 | else { 81 | return R::C_STR_CONVERT_ERR; 82 | }; 83 | 84 | let Ok(content_hash_str) = 85 | CStr::from_ptr(text_clipboard.content_hash).to_str() 86 | else { 87 | return R::C_STR_CONVERT_ERR; 88 | }; 89 | 90 | let Ok(content_type_str) = 91 | CStr::from_ptr(text_clipboard.content_type).to_str() 92 | else { 93 | return R::C_STR_CONVERT_ERR; 94 | }; 95 | 96 | let query = r" 97 | INSERT INTO clipboard_cache (content, content_hash, content_type, is_pinned) 98 | VALUES (?, ?, ?, ?) 99 | "; 100 | 101 | let binds = ( 102 | content_str, 103 | content_hash_str, 104 | content_type_str, 105 | text_clipboard.is_pinned, 106 | ); 107 | let is_err = sql.execute(query, binds).is_err(); 108 | 109 | /* For panic testing only. Don't remove it. */ 110 | /* sql.execute(query, binds).unwrap(); */ 111 | 112 | if is_err { 113 | return R::EXECUTE_SQL_ERR; 114 | } 115 | 116 | R::OK 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/front_end/about_window.cxx: -------------------------------------------------------------------------------- 1 | #include "include/about_window.hxx" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | #include "../ffi/include/utils.h" 15 | using Lazyboard::front_end::AboutWindow; 16 | using std::make_unique; 17 | using Self = AboutWindow; 18 | 19 | #if defined(LAZY_DEBUG) 20 | #include 21 | 22 | #include "../front_end_utils/include/utils.hxx" 23 | using Lazyboard::front_end_utils::debug_info; 24 | using Lazyboard::front_end_utils::dump_ptr_address; 25 | using std::string; 26 | #endif 27 | 28 | AboutWindow::AboutWindow() { 29 | grid_layout = make_unique(); 30 | github_button = make_unique(); 31 | github_issue_button = make_unique(); 32 | github_pull_button = make_unique(); 33 | } 34 | 35 | void Self::is_open_browser_ok(ResultContext &status) { 36 | using R = ResultContext; 37 | 38 | switch (status) { 39 | case R::OK: 40 | break; 41 | 42 | case R::FAILED: 43 | QMessageBox::critical(this, "Error", "Could not open your browser"); 44 | } 45 | } 46 | 47 | void Self::open_browser_when_clicked(QPushButton *button, const char *url) { 48 | const auto fn = [this, url] { 49 | auto status = open_browser(url); 50 | is_open_browser_ok(status); 51 | }; 52 | 53 | QObject::connect(button, &QPushButton::clicked, fn); 54 | } 55 | 56 | void Self::setup_buttons() { 57 | github_button->setText("GitHub | Source Code"); 58 | github_button->setToolTip("Get Lazyboard source code"); 59 | 60 | github_issue_button->setText("Issue | Bug Report"); 61 | github_issue_button->setToolTip("Report issue/bug"); 62 | 63 | github_pull_button->setText("Pull Request | Contribute"); 64 | github_pull_button->setToolTip("Contribute"); 65 | 66 | this->open_browser_when_clicked(github_button.get(), GITHUB_URL); 67 | this->open_browser_when_clicked(github_issue_button.get(), 68 | GITHUB_ISSUE_URL); 69 | this->open_browser_when_clicked(github_pull_button.get(), GITHUB_PULL_URL); 70 | 71 | grid_layout->addWidget(github_button.get(), 0, 0); 72 | grid_layout->addWidget(github_issue_button.get(), 0, 1); 73 | grid_layout->addWidget(github_pull_button.get(), 1, 0); 74 | } 75 | 76 | void Self::setup_front_end() { 77 | this->setLayout(this->grid_layout.get()); 78 | this->setup_buttons(); 79 | } 80 | 81 | void Self::show_window() { 82 | this->setMinimumSize(MIN_WIDTH, MIN_HEIGHT); 83 | this->setWindowTitle("Lazyboard About"); 84 | 85 | this->setup_front_end(); 86 | this->exec(); 87 | } 88 | 89 | void Self::on_closed(QMainWindow *main_window) { 90 | using O = QObject; 91 | const auto fn = [this, main_window] { 92 | main_window->show(); 93 | 94 | // clang-format off 95 | #if defined (LAZY_DEBUG) 96 | debug_info("About window is closed"); 97 | #endif // clang-format on 98 | }; 99 | 100 | O::connect(this, &QDialog::rejected, fn); 101 | } 102 | 103 | void Self::showEvent(QShowEvent *event) { 104 | QDialog::showEvent(event); 105 | this->_main_window->setHidden(true); 106 | 107 | // clang-format off 108 | #if defined (LAZY_DEBUG) 109 | debug_info("About Dialog is open"); 110 | #endif // clang-format on 111 | } 112 | 113 | void Self::on_about_window_event(QMainWindow *main_window) { 114 | this->_main_window = main_window; 115 | this->on_closed(this->_main_window); 116 | 117 | // clang-format off 118 | #if defined (LAZY_DEBUG) 119 | dump_ptr_address(main_window); 120 | #endif // clang-format on 121 | } -------------------------------------------------------------------------------- /scripts/debug.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | program_name="Lazyboard" 4 | compile_command="compile_commands.json" 5 | build_dir="../build" 6 | gdb_prog="gdb" 7 | wall_flag="-Wall" 8 | 9 | function check() { 10 | if ! command -v "$1" >/dev/null 2>&1; then 11 | echo "[ERR] Could not find $1 in your system. Aborting" 12 | exit 1 13 | fi 14 | } 15 | 16 | function back_end_test() { 17 | check "cargo" 18 | check "rustup" 19 | 20 | local back_end="src/back_end" 21 | cd .. 22 | cd "$back_end" || exit 1 23 | 24 | cargo test 25 | } 26 | 27 | function debug_build() { 28 | check "clang" 29 | check "cmake" 30 | 31 | check "cargo" 32 | check "rustup" 33 | 34 | cd .. 35 | cd "src/back_end" || exit 1 36 | cargo build 37 | 38 | cd .. 39 | local show_gui="$1" 40 | 41 | if [ ! -d $build_dir ]; then 42 | mkdir "$build_dir" 43 | fi 44 | 45 | cd "$build_dir" || exit 1 46 | 47 | cmake "$wall_flag" \ 48 | -DCMAKE_C_COMPILER=clang \ 49 | -DCMAKE_C_COMPILER_LAUNCHER=ccache \ 50 | -DCMAKE_CXX_COMPILER=clang++ \ 51 | -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ 52 | -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ 53 | -DLAZY_DEBUG=ON \ 54 | -DRELEASE=OFF \ 55 | .. 56 | 57 | make 58 | 59 | if [[ $show_gui == 1 ]]; then 60 | ./"$program_name" 61 | fi 62 | 63 | cp "$compile_command" .. 64 | } 65 | 66 | function linter_check() { 67 | local options=$1 68 | 69 | if [[ $options == "clippy" ]]; then 70 | local cargo="cargo" 71 | local backend="src/back_end" 72 | check "$cargo" 73 | 74 | cd .. 75 | cd "$backend" || exit 1 76 | "$cargo" clippy \ 77 | --all-targets --all-features \ 78 | -- -D clippy::all -D clippy::pedantic \ 79 | -D clippy::nursery -D clippy::perf 80 | 81 | else 82 | local clang_tidy="clang-tidy" 83 | check "$clang_tidy" 84 | 85 | cd .. 86 | clang-tidy src/**/*.cxx -p=build 87 | fi 88 | } 89 | 90 | function debug_gdb() { 91 | check "gdb" 92 | local gdb_cmd="../commands.gdb" 93 | local not_show_gui=0 94 | 95 | debug_build $not_show_gui 96 | "$gdb_prog" --batch -x "$gdb_cmd" "$program_name" 97 | } 98 | 99 | function pre_push() { 100 | linter_check 101 | 102 | local branch="$1" 103 | 104 | git push origin "$branch" 105 | } 106 | 107 | function main() { 108 | case $1 in 109 | "debug-build") { 110 | local show_gui=1 111 | debug_build $show_gui 112 | } ;; 113 | "debug-build-noshow") { 114 | local show_gui=0 115 | debug_build $show_gui 116 | } ;; 117 | "debug-gdb") debug_gdb ;; 118 | "check-backend") { 119 | local option="clippy" 120 | linter_check $option 121 | };; 122 | "check-frontend") { 123 | local option="clang-tidy" 124 | linter_check "$option" 125 | } ;; 126 | "backend-test") back_end_test ;; 127 | "push-dev") { 128 | local dev_branch="dev" 129 | pre_push $dev_branch 130 | } ;; 131 | "push-master") { 132 | local master_branch="master" 133 | pre_push $master_branch 134 | } ;; 135 | "push-stable") { 136 | local stable_branch="stable" 137 | pre_push $stable_branch 138 | } ;; 139 | *) { 140 | echo "Invalid option: $1" 141 | exit 1 142 | } ;; 143 | esac 144 | } 145 | 146 | main "$1" 147 | -------------------------------------------------------------------------------- /src/front_end/main_window_preload.cxx: -------------------------------------------------------------------------------- 1 | #include "include/main_window_preload.hxx" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #include "../ffi/include/config.h" 14 | #include "../ffi/include/utils.h" 15 | #include "include/theme_manager.hxx" 16 | 17 | #if defined(LAZY_DEBUG) 18 | #include 19 | 20 | #include "../front_end_utils/include/utils.hxx" 21 | using Lazyboard::front_end_utils::dump_ptr_address; 22 | using std::println; 23 | #endif 24 | 25 | #include "../front_end_utils/include/error_types.hxx" 26 | #include "../front_end_utils/include/utils.hxx" 27 | 28 | using Lazyboard::front_end::MainWindowPreload; 29 | using Self = MainWindowPreload; 30 | 31 | using Lazyboard::front_end_utils::error_dialog_show; 32 | using Lazyboard::front_end_utils::ErrorTypes; 33 | using std::format; 34 | using std::make_unique; 35 | using std::string; 36 | 37 | void Self::on_gen_default_cfg_error(ConfigResult status, 38 | QMainWindow *main_window) { 39 | switch (status) { 40 | using E = ErrorTypes; 41 | 42 | case ConfigResult::OK: 43 | break; 44 | 45 | case ConfigResult::CREATE_DIR_FAILED: 46 | error_dialog_show(main_window, E::CREATE_DIR_FAILED); 47 | 48 | break; 49 | 50 | case ConfigResult::GET_CONFIG_DIR_FAILED: 51 | error_dialog_show(main_window, E::GET_CONFIG_DIR_FAILED); 52 | break; 53 | 54 | case ConfigResult::CREATE_FILE_FAILED: 55 | error_dialog_show(main_window, E::CREATE_FILE_FAILED); 56 | break; 57 | 58 | case ConfigResult::WRITE_FILE_FAILED: 59 | error_dialog_show(main_window, E::WRITE_FILE_FAILED); 60 | break; 61 | 62 | case ConfigResult::TOML_TO_STRING_FAILED: 63 | error_dialog_show(main_window, E::TOML_TO_STRING_FAILED); 64 | break; 65 | } 66 | } 67 | 68 | void Self::on_read_exists_cfg_error(ReadConfigResult status, 69 | QMainWindow *main_window) { 70 | using Status = ReadConfigResult; 71 | using E = ErrorTypes; 72 | 73 | switch (status) { 74 | case Status::READ_OK: 75 | break; 76 | 77 | case Status::CONVERT_TO_MUT_FAILED: 78 | error_dialog_show(main_window, E::CONVERT_TO_MUT_FAILED); 79 | break; 80 | 81 | case Status::PARSE_TOML_FAILED: 82 | error_dialog_show(main_window, E::PARSE_TOML_FAILED); 83 | break; 84 | 85 | case Status::READ_FILE_FAILED: 86 | error_dialog_show(main_window, E::READ_FILE_FAILED); 87 | break; 88 | 89 | case Status::UTF_8_ERROR: 90 | error_dialog_show(main_window, E::UTF_8_ERR); 91 | break; 92 | 93 | case Status::CONVERT_TO_C_STR_FAILED: 94 | error_dialog_show(main_window, E::CONVERT_TO_C_STR_ERR); 95 | break; 96 | } 97 | } 98 | 99 | string Self::application_config() { 100 | char *out = nullptr; 101 | auto result = config_dir(&out); 102 | string config_path = format("{}/Lazyboard/settings.toml", out); 103 | 104 | free_alloc(out); 105 | return config_path; 106 | } 107 | 108 | void Self::create_default_config(QMainWindow *main_window) { 109 | auto config_path = this->application_config(); 110 | auto is_config_exists = path_exists(config_path.data()); 111 | 112 | if (!is_config_exists) { 113 | auto status = create_default_cfg(); 114 | on_gen_default_cfg_error(status, main_window); 115 | 116 | // clang-format off 117 | #if defined(LAZY_DEBUG) 118 | println("[DEBUG] Config path not found, generate at: {}", config_path); 119 | #endif 120 | 121 | return; 122 | } 123 | 124 | #if defined(LAZY_DEBUG) 125 | println("[DEBUG] Found config path at: {}", config_path); 126 | #endif // clang-format on 127 | } 128 | 129 | void Self::read_if_exists_config(QMainWindow *main_window) { 130 | raw_app_config = make_unique(); 131 | theme_manager = make_unique(); 132 | 133 | auto config_path = this->application_config(); 134 | auto status = read_exists_config(config_path.data(), raw_app_config.get()); 135 | this->on_read_exists_cfg_error(status, main_window); 136 | 137 | theme_manager->set_main_window_theme(main_window, raw_app_config.get()); 138 | 139 | // clang-format off 140 | #if defined (LAZY_DEBUG) 141 | dump_ptr_address(raw_app_config.get()); 142 | dump_ptr_address(theme_manager.get()); 143 | dump_ptr_address(main_window); 144 | 145 | #endif // clang-format on 146 | } 147 | -------------------------------------------------------------------------------- /src/back_end/src/utils/fs_utils.rs: -------------------------------------------------------------------------------- 1 | use crate::core::result_enum::ResultContext; 2 | use crate::ensure; 3 | use crate::internal::app_config::AppConfig; 4 | use std::ffi::{CStr, CString, c_char}; 5 | use std::fs::{self, File}; 6 | use std::io::Write; 7 | use std::path::Path; 8 | 9 | const APP_NAME: &str = "Lazyboard"; 10 | 11 | /// # Safety 12 | /// Be careful with raw pointers. 13 | #[must_use] 14 | #[unsafe(no_mangle)] 15 | pub unsafe extern "C" fn new_folder( 16 | folder_path: *const c_char, 17 | ) -> ResultContext { 18 | unsafe { 19 | use ResultContext as R; 20 | let folder_path_str = CStr::from_ptr(folder_path).to_string_lossy(); 21 | 22 | let is_ok = fs::create_dir_all(&*folder_path_str).is_ok(); 23 | if !is_ok { 24 | return R::FAILED; 25 | } 26 | 27 | R::OK 28 | } 29 | } 30 | 31 | #[must_use] 32 | #[unsafe(no_mangle)] 33 | /// # Safety 34 | /// Careful with raw pointers. 35 | pub unsafe extern "C" fn path_exists(path: *const c_char) -> bool { 36 | unsafe { 37 | let path_str = CStr::from_ptr(path).to_string_lossy(); 38 | 39 | Path::new(&*path_str).exists() 40 | } 41 | } 42 | 43 | #[repr(C)] 44 | #[allow(non_camel_case_types)] 45 | pub enum ConfigResult { 46 | OK, 47 | TOML_TO_STRING_ERR, 48 | CREATE_DIR_ERR, 49 | CREATE_FILE_ERR, 50 | WRITE_FILE_ERR, 51 | GET_CONFIG_DIR_ERR, 52 | } 53 | 54 | #[derive(PartialEq, Eq, Debug)] 55 | #[repr(C)] 56 | #[allow(non_camel_case_types)] 57 | pub enum UtilsResult { 58 | OK, 59 | ALLOC_ERR, 60 | NULL_DEFERENCE_ERR, 61 | GET_DIR_ERR, 62 | } 63 | 64 | #[must_use] 65 | #[unsafe(no_mangle)] 66 | /// # Safety 67 | /// Careful with memory leaks & double-free. 68 | pub unsafe extern "C" fn config_dir(out: *mut *mut c_char) -> UtilsResult { 69 | use UtilsResult as R; 70 | 71 | unsafe { 72 | if let Some(config_path) = dirs::config_dir() { 73 | let path = config_path.to_string_lossy(); 74 | 75 | let Ok(config_path_cstring) = CString::new(path.to_string()) else { 76 | return R::ALLOC_ERR; 77 | }; 78 | 79 | ensure!(!out.is_null(), R::NULL_DEFERENCE_ERR); 80 | *out = config_path_cstring.into_raw(); 81 | 82 | return R::OK; 83 | } 84 | 85 | R::GET_DIR_ERR 86 | } 87 | } 88 | 89 | #[must_use] 90 | #[unsafe(no_mangle)] 91 | /// # Safety 92 | /// Careful with raw pointers & memory leaks 93 | pub unsafe extern "C" fn cache_dir(out: *mut *mut c_char) -> UtilsResult { 94 | use UtilsResult as R; 95 | 96 | unsafe { 97 | if let Some(result) = dirs::cache_dir() { 98 | let path_str = result.to_string_lossy(); 99 | 100 | let Ok(path_c_string) = CString::new(path_str.to_string()) else { 101 | return R::ALLOC_ERR; 102 | }; 103 | 104 | ensure!(!out.is_null(), R::NULL_DEFERENCE_ERR); 105 | *out = path_c_string.into_raw(); 106 | 107 | return R::OK; 108 | } 109 | 110 | R::GET_DIR_ERR 111 | } 112 | } 113 | 114 | #[must_use] 115 | #[unsafe(no_mangle)] 116 | pub extern "C" fn create_default_cfg() -> ConfigResult { 117 | use ConfigResult as R; 118 | 119 | let app_config = AppConfig::default_config(); 120 | 121 | let Ok(toml_string) = toml::to_string(&app_config) else { 122 | return R::TOML_TO_STRING_ERR; 123 | }; 124 | 125 | if let Some(data_local) = dirs::config_dir() { 126 | let config_dir_string = data_local.to_string_lossy().to_string(); 127 | 128 | let config_dir = format!("{config_dir_string}/{APP_NAME}"); 129 | if fs::create_dir_all(&config_dir).is_err() { 130 | return R::CREATE_DIR_ERR; 131 | } 132 | 133 | let config_file_path = format!("{config_dir}/settings.toml"); 134 | let path = Path::new(&config_file_path); 135 | 136 | if !path.exists() { 137 | let Ok(mut file) = File::create_new(config_file_path) else { 138 | return R::CREATE_FILE_ERR; 139 | }; 140 | 141 | match file.write_all(toml_string.as_bytes()) { 142 | Ok(()) => return R::OK, 143 | Err(_) => return R::WRITE_FILE_ERR, 144 | }; 145 | } 146 | 147 | return R::OK; 148 | } 149 | 150 | R::GET_CONFIG_DIR_ERR 151 | } 152 | -------------------------------------------------------------------------------- /src/front_end/include/abstract_table_model.hxx: -------------------------------------------------------------------------------- 1 | #ifndef ABSTRACT_TABLE_MODEL_HXX 2 | #define ABSTRACT_TABLE_MODEL_HXX 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | using std::min; 17 | using std::uint8_t; 18 | namespace Lazyboard::front_end { 19 | 20 | typedef struct TableItem { 21 | QString time; 22 | QString content; 23 | QString content_type; 24 | bool is_pinned = false; 25 | } TableItem; 26 | 27 | template 28 | constexpr void unused(T &t) { 29 | (void)t; 30 | } 31 | 32 | class AbstractTableModel : public QAbstractTableModel { 33 | Q_OBJECT 34 | 35 | public: 36 | // clang-format off 37 | enum Colum : uint8_t { 38 | TIME = 0, 39 | CONTENT = 1, 40 | TYPE = 2, 41 | PINNED, 42 | COLUMN_COUNT, 43 | }; // clang-format on 44 | 45 | int rowCount(const QModelIndex &parent = QModelIndex()) const override { 46 | unused(parent); 47 | return static_cast(min(table_item.size(), qsizetype(INT_MAX))); 48 | } 49 | 50 | int columnCount(const QModelIndex &parent = QModelIndex()) const override { 51 | unused(parent); 52 | return COLUMN_COUNT; 53 | } 54 | 55 | QVariant data(const QModelIndex &index, 56 | int role = Qt::DisplayRole) const override { 57 | if (!index.isValid() || index.row() >= table_item.size() || 58 | index.row() < 0) { 59 | return QVariant(); 60 | } 61 | 62 | const TableItem &item = table_item[index.row()]; 63 | 64 | if (role == Qt::DisplayRole || role == Qt::EditRole) { 65 | switch (index.column()) { 66 | case TIME: 67 | return item.time; 68 | 69 | case CONTENT: { 70 | const auto max_length = 20; 71 | if (item.content.length() > max_length) { 72 | auto content_length = item.content.length(); 73 | 74 | qsizetype remaining_length = 75 | content_length - max_length; 76 | 77 | QString truncated = 78 | item.content.left(max_length) + 79 | QString(" and %1 more...").arg(remaining_length); 80 | 81 | return truncated; 82 | } 83 | 84 | return item.content; 85 | } 86 | 87 | case TYPE | PINNED: 88 | return item.content_type; 89 | 90 | default: 91 | return {}; 92 | } 93 | } 94 | 95 | if (role == Qt::CheckStateRole && index.column() == PINNED) { 96 | return item.is_pinned ? Qt::Checked : Qt::Unchecked; 97 | } 98 | 99 | return QVariant(); 100 | } 101 | 102 | QVariant headerData(int section, Qt::Orientation orientation, 103 | int role = Qt::DisplayRole) const override { 104 | if (role != Qt::DisplayRole || orientation != Qt::Horizontal) { 105 | return QVariant(); 106 | } 107 | 108 | switch (section) { 109 | case TIME: 110 | return "Time"; 111 | 112 | case CONTENT: 113 | return "Content"; 114 | 115 | case TYPE: 116 | return "Type"; 117 | 118 | case PINNED: 119 | return "Pinned"; 120 | 121 | default: 122 | return QVariant(); 123 | } 124 | } 125 | 126 | Qt::ItemFlags flags(const QModelIndex &index) const override { 127 | if (!index.isValid()) { 128 | return Qt::NoItemFlags; 129 | } 130 | 131 | Qt::ItemFlags flag = QAbstractTableModel::flags(index); 132 | 133 | if (index.column() == PINNED) { 134 | flag |= Qt::ItemFlag::ItemIsUserCheckable | 135 | Qt::ItemFlag::ItemIsEditable; 136 | } else { 137 | flag |= Qt::ItemIsEditable; 138 | } 139 | 140 | return flag; 141 | } 142 | 143 | bool setData(const QModelIndex &index, const QVariant &value, 144 | int role) override { 145 | if (!index.isValid() || index.row() >= table_item.size()) { 146 | return false; 147 | } 148 | 149 | TableItem &item = table_item[index.row()]; 150 | 151 | if (index.column() == PINNED && role == Qt::CheckStateRole) { 152 | bool checked = (value.toInt() == Qt::Checked); 153 | 154 | if (item.is_pinned != checked) { 155 | item.is_pinned = checked; 156 | 157 | emit dataChanged(index, index, 158 | {Qt::CheckStateRole, Qt::DisplayRole}); 159 | 160 | return true; 161 | } 162 | } 163 | 164 | if (role == Qt::EditRole) { 165 | switch (index.column()) { 166 | case TIME: 167 | item.time = value.toString(); 168 | break; 169 | 170 | case CONTENT: 171 | item.content = value.toString(); 172 | break; 173 | 174 | case TYPE: 175 | item.content_type = value.toString(); 176 | break; 177 | 178 | default: 179 | return false; 180 | } 181 | 182 | emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole}); 183 | return true; 184 | } 185 | 186 | return false; 187 | } 188 | 189 | private: 190 | QList table_item; 191 | }; 192 | } // namespace Lazyboard::front_end 193 | 194 | #endif // ABSTRACT_TABLE_MODEL_HXX -------------------------------------------------------------------------------- /src/front_end/theme_manager.cxx: -------------------------------------------------------------------------------- 1 | #include "include/theme_manager.hxx" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include "../ffi/include/config.h" 13 | #include "../front_end_utils/include/error_types.hxx" 14 | #include "../front_end_utils/include/utils.hxx" 15 | 16 | #if defined(LAZY_DEBUG) 17 | #include 18 | using std::cout; 19 | #endif 20 | 21 | using Lazyboard::front_end_utils::error_dialog_show; 22 | using Lazyboard::front_end_utils::error_to_string; 23 | using Lazyboard::front_end_utils::ErrorTypes; 24 | using Lazyboard::front_end_utils::is_valid_hex_color; 25 | 26 | using std::make_unique; 27 | using std::string; 28 | using std::stringstream; 29 | using Self = Lazyboard::front_end::ThemeManager; 30 | 31 | auto Self::gui_settings(AppConfig *raw_app_config) noexcept -> GuiSettings { 32 | // clang-format off 33 | auto raw_background_color = raw_app_config->raw_app_gui_settings.background_color; 34 | auto raw_foreground_color = raw_app_config->raw_app_gui_settings.foreground_color; 35 | auto raw_background_button_color = raw_app_config->raw_app_gui_settings.background_button_color; 36 | auto raw_foreground_button_color = raw_app_config->raw_app_gui_settings.foreground_button_color; 37 | auto raw_background_header_table_color = raw_app_config->raw_app_gui_settings.background_table_header_color; 38 | auto raw_foreground_header_table_color = raw_app_config->raw_app_gui_settings.foreground_table_header_color; 39 | 40 | 41 | auto settings_gui = GuiSettings { 42 | .background_color = raw_background_color, 43 | .foreground_color = raw_foreground_color, 44 | .background_button_color = raw_background_button_color, 45 | .foreground_button_color = raw_foreground_color, 46 | .background_table_header_color = raw_background_header_table_color, 47 | .foreground_table_header_color = raw_foreground_header_table_color, 48 | }; // clang-format on 49 | 50 | return settings_gui; 51 | } 52 | 53 | void Self::on_invalid_hex_color_error(QMainWindow *main_window) { 54 | using E = ErrorTypes; 55 | 56 | error_dialog_show(main_window, E::INVALID_HEX_COLOR); 57 | } 58 | 59 | void Self::set_main_window_theme(QMainWindow *main_window, 60 | AppConfig *app_config) noexcept { 61 | auto settings_gui = this->gui_settings(app_config); 62 | 63 | // clang-format off 64 | auto bg_color = data(settings_gui.background_color); 65 | auto fg_color = data(settings_gui.foreground_color); 66 | auto bg_button_color = data(settings_gui.background_button_color); 67 | auto fg_button_color = data(settings_gui.foreground_button_color); 68 | auto bg_header_table_color = data(settings_gui.background_table_header_color); 69 | auto fg_header_table_color = data(settings_gui.foreground_table_header_color); 70 | // clang-format on 71 | 72 | auto bg_hex = QColor(bg_color); 73 | auto fg_hex = QColor(fg_color); 74 | auto bg_btn_hex = QColor(bg_button_color); 75 | auto fg_btn_hex = QColor(fg_button_color); 76 | auto bg_header_table_hex = QColor(bg_header_table_color); 77 | auto fg_header_table_hex = QColor(fg_header_table_color); 78 | free_app_config(app_config); 79 | 80 | const auto valid_color = 81 | is_valid_hex_color(bg_hex, fg_hex, bg_btn_hex, fg_btn_hex, 82 | bg_header_table_hex, fg_header_table_hex); 83 | 84 | if (!valid_color) { 85 | on_invalid_hex_color_error(main_window); 86 | 87 | // clang-format off 88 | #if defined (LAZY_DEBUG) 89 | const auto error_name = error_to_string(ErrorTypes::INVALID_HEX_COLOR); 90 | stringstream string_stream_debug; 91 | 92 | string_stream_debug 93 | << "[DEBUG] Found error when load TOML configuration:\n" 94 | << "[DEBUG] Error Type: " << error_name << "\n" 95 | << "[DEBUG] " << bg_color << "\n" 96 | << "[DEBUG] " << fg_color << "\n" 97 | << "[DEBUG] " << bg_button_color << "\n" 98 | << "[DEBUG] " << fg_button_color << "\n" 99 | << "[DEBUG] " << bg_header_table_color << "\n" 100 | << "[DEBUG] " << fg_header_table_color << "\n"; 101 | 102 | cout << string_stream_debug.str().c_str(); 103 | 104 | #endif // clang-format on 105 | 106 | return; 107 | } 108 | 109 | QPalette palette; 110 | 111 | palette.setColor(QPalette::Window, bg_hex); 112 | palette.setColor(QPalette::Base, bg_hex); 113 | palette.setColor(QPalette::Text, fg_hex); 114 | 115 | // clang-format off 116 | auto stylesheet = QString( 117 | R"""( 118 | QPushButton { 119 | background-color: %1; 120 | color: %2; 121 | } 122 | 123 | QHeaderView::section { 124 | background-color: %3; 125 | color: %4; 126 | } 127 | )""").arg(bg_btn_hex.name(), fg_btn_hex.name(), 128 | bg_header_table_hex.name(), fg_header_table_hex.name()); // clang-format on 129 | 130 | main_window->setPalette(palette); 131 | main_window->setAutoFillBackground(true); 132 | main_window->setStyleSheet(stylesheet); 133 | } -------------------------------------------------------------------------------- /src/back_end/src/config/toml.rs: -------------------------------------------------------------------------------- 1 | use crate::config::constant::{ 2 | APP_GUI_SETTINGS, APP_SETTINGS, BACKGROUND_COLOR, BACKGROUND_COLOR_BUTTON, 3 | BACKGROUND_COLOR_TABLE_HEADER, FALLBACK_BG_BUTTON_COLOR, FALLBACK_BG_COLOR, 4 | FALLBACK_BG_TABLE_HEADER_COLOR, FALLBACK_FG_BUTTON_COLOR, 5 | FALLBACK_FG_COLOR, FALLBACK_FG_TABLE_HEADER_COLOR, FOREGROUND_COLOR, 6 | FOREGROUND_COLOR_BUTTON, FOREGROUND_COLOR_TABLE_HEADER, HIDE_WHEN_CLOSED, 7 | HIDE_WHEN_CLOSED_FALLBACK, NOTIFICATION, NOTIFICATION_FALLBACK, 8 | }; 9 | use std::{ 10 | ffi::{CStr, CString, c_char}, 11 | fs, 12 | }; 13 | use toml::Value; 14 | 15 | #[repr(C)] 16 | pub struct AppSettings { 17 | pub hide_when_closed: bool, 18 | pub notification: bool, 19 | } 20 | 21 | #[repr(C)] 22 | pub struct AppGuiSettings { 23 | pub background_color: *mut c_char, 24 | pub foreground_color: *mut c_char, 25 | pub background_button_color: *mut c_char, 26 | pub foreground_button_color: *mut c_char, 27 | pub background_header_table_color: *mut c_char, 28 | pub foreground_header_table_color: *mut c_char, 29 | } 30 | 31 | #[repr(C)] 32 | pub struct AppConfig { 33 | pub app_settings: AppSettings, 34 | pub app_gui_settings: AppGuiSettings, 35 | } 36 | 37 | #[repr(C)] 38 | pub enum ReadConfigResult { 39 | Ok, 40 | ReadFileFailed, 41 | Utf8Error, 42 | ParseTomlFailed, 43 | ConvertToMutFailed, 44 | ConvertToCStringFailed, 45 | } 46 | 47 | fn color_by_settings( 48 | toml_value: &Value, 49 | field_name: &str, 50 | fallback: &str, 51 | ) -> String { 52 | let color = toml_value 53 | .get(APP_GUI_SETTINGS) 54 | .and_then(|value| value.get(field_name)) 55 | .and_then(Value::as_str) 56 | .unwrap_or(fallback); 57 | 58 | color.to_string() 59 | } 60 | 61 | fn app_settings(toml_value: &Value) -> AppSettings { 62 | AppSettings { 63 | hide_when_closed: toml_value 64 | .get(APP_SETTINGS) 65 | .and_then(|value| value.get(HIDE_WHEN_CLOSED)) 66 | .and_then(Value::as_bool) 67 | .unwrap_or(HIDE_WHEN_CLOSED_FALLBACK), 68 | 69 | notification: toml_value 70 | .get(APP_SETTINGS) 71 | .and_then(|value| value.get(NOTIFICATION)) 72 | .and_then(Value::as_bool) 73 | .unwrap_or(NOTIFICATION_FALLBACK), 74 | } 75 | } 76 | 77 | /// # Safety 78 | /// Careful with raw pointers. 79 | #[unsafe(no_mangle)] 80 | pub unsafe extern "C" fn read_exists_config( 81 | file_path: *const c_char, 82 | out: *mut AppConfig, 83 | ) -> ReadConfigResult { 84 | use ReadConfigResult as Status; 85 | 86 | let file_path_str = unsafe { CStr::from_ptr(file_path).to_str() }; 87 | let Ok(file_path) = file_path_str else { 88 | return Status::Utf8Error; 89 | }; 90 | 91 | let Ok(result) = fs::read_to_string(file_path) else { 92 | return Status::ReadFileFailed; 93 | }; 94 | 95 | let Ok(toml_value) = toml::from_str::(&result) else { 96 | return Status::ParseTomlFailed; 97 | }; 98 | 99 | let app_settings = app_settings(&toml_value); 100 | 101 | let bg_color = 102 | color_by_settings(&toml_value, BACKGROUND_COLOR, FALLBACK_BG_COLOR); 103 | let fg_color = 104 | color_by_settings(&toml_value, FOREGROUND_COLOR, FALLBACK_FG_COLOR); 105 | let bg_button_color = color_by_settings( 106 | &toml_value, 107 | BACKGROUND_COLOR_BUTTON, 108 | FALLBACK_BG_BUTTON_COLOR, 109 | ); 110 | let fg_button_color = color_by_settings( 111 | &toml_value, 112 | FOREGROUND_COLOR_BUTTON, 113 | FALLBACK_FG_BUTTON_COLOR, 114 | ); 115 | let bg_table_header_color = color_by_settings( 116 | &toml_value, 117 | BACKGROUND_COLOR_TABLE_HEADER, 118 | FALLBACK_BG_TABLE_HEADER_COLOR, 119 | ); 120 | let fg_table_header_color = color_by_settings( 121 | &toml_value, 122 | FOREGROUND_COLOR_TABLE_HEADER, 123 | FALLBACK_FG_TABLE_HEADER_COLOR, 124 | ); 125 | 126 | let Ok(bg_cstr) = CString::new(bg_color) else { 127 | return Status::ConvertToCStringFailed; 128 | }; 129 | 130 | let Ok(fg_cstr) = CString::new(fg_color) else { 131 | return Status::ConvertToCStringFailed; 132 | }; 133 | 134 | let Ok(bg_btn_cstr) = CString::new(bg_button_color) else { 135 | return Status::ConvertToCStringFailed; 136 | }; 137 | 138 | let Ok(fg_btn_cstr) = CString::new(fg_button_color) else { 139 | return Status::ConvertToCStringFailed; 140 | }; 141 | 142 | let Ok(bg_table_header_cstr) = CString::new(bg_table_header_color) else { 143 | return Status::ConvertToCStringFailed; 144 | }; 145 | 146 | let Ok(fg_table_header_cstr) = CString::new(fg_table_header_color) else { 147 | return Status::ConvertToCStringFailed; 148 | }; 149 | 150 | if let Some(config) = unsafe { out.as_mut() } { 151 | config.app_settings.hide_when_closed = app_settings.hide_when_closed; 152 | config.app_settings.notification = app_settings.notification; 153 | config.app_gui_settings.background_color = bg_cstr.into_raw(); 154 | config.app_gui_settings.foreground_color = fg_cstr.into_raw(); 155 | config.app_gui_settings.background_button_color = 156 | bg_btn_cstr.into_raw(); 157 | config.app_gui_settings.foreground_button_color = 158 | fg_btn_cstr.into_raw(); 159 | config.app_gui_settings.background_header_table_color = 160 | bg_table_header_cstr.into_raw(); 161 | config.app_gui_settings.foreground_header_table_color = 162 | fg_table_header_cstr.into_raw(); 163 | 164 | return ReadConfigResult::Ok; 165 | } 166 | 167 | ReadConfigResult::ConvertToMutFailed 168 | } 169 | 170 | #[unsafe(no_mangle)] 171 | /// # Safety 172 | /// Be careful with raw pointers & double-free. 173 | pub unsafe extern "C" fn free_app_config(config: *mut AppConfig) { 174 | unsafe { 175 | if let Some(cfg) = config.as_mut() { 176 | let _ = CString::from_raw(cfg.app_gui_settings.background_color); 177 | let _ = CString::from_raw(cfg.app_gui_settings.foreground_color); 178 | let _ = 179 | CString::from_raw(cfg.app_gui_settings.background_button_color); 180 | let _ = 181 | CString::from_raw(cfg.app_gui_settings.foreground_button_color); 182 | let _ = CString::from_raw( 183 | cfg.app_gui_settings.background_header_table_color, 184 | ); 185 | let _ = CString::from_raw( 186 | cfg.app_gui_settings.foreground_header_table_color, 187 | ); 188 | } 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /src/front_end/include/icon_bytes.hxx: -------------------------------------------------------------------------------- 1 | #ifndef ICON_BYTES_HXX 2 | #define ICON_BYTES_HXX 3 | 4 | #include 5 | 6 | #include 7 | 8 | using std::initializer_list; 9 | 10 | const initializer_list image_bytes = { 11 | 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 12 | 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0xe4, 0x00, 0x00, 0x00, 0xba, 13 | 0x08, 0x06, 0x00, 0x00, 0x00, 0xbe, 0xd3, 0xe1, 0x0a, 0x00, 0x00, 0x15, 14 | 0xc9, 0x49, 0x44, 0x41, 0x54, 0x78, 0x5e, 0xed, 0x5d, 0x79, 0x78, 0x55, 15 | 0xc5, 0x15, 0x3f, 0x49, 0x5e, 0xf6, 0x3d, 0x84, 0xb0, 0x04, 0x08, 0x61, 16 | 0x8b, 0xac, 0x62, 0x40, 0x05, 0x8b, 0x68, 0x51, 0xb1, 0x9f, 0xb5, 0x56, 17 | 0x44, 0x2d, 0x05, 0xb5, 0xb8, 0x80, 0x5b, 0xfd, 0x04, 0xdc, 0x50, 0xd0, 18 | 0x22, 0x4a, 0x01, 0x91, 0xe2, 0x52, 0xab, 0x88, 0xb8, 0xa3, 0x7e, 0x08, 19 | 0xa2, 0xad, 0x55, 0x8b, 0x55, 0x71, 0x29, 0x08, 0x08, 0xb2, 0x28, 0xbb, 20 | 0x84, 0x04, 0xc2, 0x12, 0xb2, 0xaf, 0x2f, 0x7b, 0xe7, 0x4c, 0x7c, 0x98, 21 | 0xe5, 0xe6, 0xbd, 0x9b, 0x77, 0xdf, 0x23, 0x73, 0xef, 0xfd, 0xcd, 0xf7, 22 | 0xe5, 0x0f, 0xde, 0x9d, 0x99, 0x7b, 0xce, 0xef, 0x37, 0xbf, 0x3b, 0x33, 23 | 0x67, 0xce, 0xbd, 0x04, 0x74, 0xec, 0xd4, 0xb9, 0x9e, 0x50, 0x80, 0x00, 24 | 0x10, 0x50, 0x02, 0x81, 0x00, 0x08, 0x52, 0x09, 0x1e, 0x60, 0x04, 0x10, 25 | 0x90, 0x08, 0x40, 0x90, 0x18, 0x08, 0x40, 0x40, 0x21, 0x04, 0x20, 0x48, 26 | 0x85, 0xc8, 0x80, 0x29, 0x40, 0x00, 0x82, 0xc4, 0x18, 0x00, 0x02, 0x0a, 27 | 0x21, 0x00, 0x41, 0x2a, 0x44, 0x06, 0x4c, 0x01, 0x02, 0x10, 0x24, 0xc6, 28 | 0x00, 0x10, 0x50, 0x08, 0x01, 0x08, 0x52, 0x21, 0x32, 0x60, 0x0a, 0x10, 29 | 0x80, 0x20, 0x31, 0x06, 0x80, 0x80, 0x42, 0x08, 0x40, 0x90, 0x0a, 0x91, 30 | 0x01, 0x53, 0x80, 0x00, 0x04, 0x89, 0x31, 0x00, 0x04, 0x14, 0x42, 0x00, 31 | 0x82, 0x54, 0x88, 0x0c, 0x98, 0x02, 0x04, 0x20, 0x48, 0x8c, 0x01, 0x20, 32 | 0xa0, 0x10, 0x02, 0x10, 0xa4, 0x42, 0x64, 0xc0, 0x14, 0x20, 0x00, 0x41, 33 | 0x62, 0x0c, 0x00, 0x01, 0x85, 0x10, 0x80, 0x20, 0x15, 0x22, 0x03, 0xa6, 34 | 0x00, 0x01, 0x08, 0x12, 0x63, 0x00, 0x08, 0x28, 0x84, 0x00, 0x04, 0xa9, 35 | 0x10, 0x19, 0x30, 0x05, 0x08, 0x40, 0x90, 0x18, 0x03, 0x40, 0x40, 0x21, 36 | 0x04, 0x20, 0x48, 0x85, 0xc8, 0x80, 0x29, 0x40, 0x00, 0x82, 0xc4, 0x18, 37 | 0x00, 0x02, 0x0a, 0x21, 0x00, 0x41, 0x2a, 0x44, 0x06, 0x4c, 0x01, 0x02, 38 | 0x10, 0x24, 0xc6, 0x00, 0x10, 0x50, 0x08, 0x01, 0x08, 0x52, 0x21, 0x32, 39 | 0x60, 0x0a, 0x10, 0x80, 0x20, 0x31, 0x06, 0x80, 0x80, 0x42, 0x08, 0x40, 40 | 0x90, 0x0a, 0x91, 0x01, 0x53, 0x80, 0x00, 0x04, 0x89, 0x31, 0x00, 0x04, 41 | 0x14, 0x42, 0x00, 0x82, 0x54, 0x88, 0x0c, 0x98, 0x02, 0x04, 0x20, 0x48, 42 | 0x8c, 0x01, 0x20, 0xa0, 0x10, 0x02, 0x10, 0xa4, 0x42, 0x64, 0xc0, 0x14, 43 | 0x20, 0xa0, 0xb4, 0x20, 0xd3, 0x23, 0xc3, 0xa9, 0x63, 0xb0, 0x83, 0x12, 44 | 0x1d, 0x0e, 0xc9, 0x54, 0x6e, 0x4d, 0x0d, 0x9d, 0xac, 0xae, 0xa1, 0xad, 45 | 0x65, 0x15, 0x4a, 0x30, 0x97, 0xde, 0x2b, 0x88, 0x12, 0x63, 0x02, 0xe4, 46 | 0x9f, 0xb4, 0xaf, 0xb8, 0x5e, 0xfe, 0x6d, 0x3d, 0x58, 0xab, 0x84, 0x7d, 47 | 0x01, 0x7d, 0x1d, 0x14, 0x10, 0x1b, 0x48, 0xc4, 0x7f, 0xfc, 0x7f, 0x9c, 48 | 0x15, 0xd5, 0x51, 0x7d, 0xb1, 0xf8, 0xdb, 0x5f, 0xa3, 0x84, 0x7d, 0x30, 49 | 0xa2, 0x25, 0x02, 0xca, 0x09, 0x72, 0x40, 0x78, 0x18, 0x4d, 0x4e, 0xea, 50 | 0x40, 0x23, 0xa3, 0x23, 0x88, 0x82, 0x82, 0x28, 0xc4, 0x11, 0x44, 0x91, 51 | 0x3f, 0x0b, 0xb2, 0x5c, 0x08, 0xb2, 0xb2, 0x46, 0x0c, 0xf6, 0xda, 0x5a, 52 | 0xda, 0x50, 0x52, 0x4e, 0xaf, 0xe5, 0xe4, 0xd1, 0x8f, 0x15, 0xce, 0xd3, 53 | 0xca, 0xeb, 0x80, 0xee, 0x81, 0x74, 0xc3, 0xaf, 0x43, 0x68, 0x64, 0x5a, 54 | 0x10, 0x05, 0x04, 0x04, 0x50, 0x48, 0x70, 0x20, 0x45, 0x84, 0x35, 0x08, 55 | 0xb2, 0xdc, 0x59, 0x4f, 0x55, 0xd5, 0x62, 0xc0, 0xd7, 0xd7, 0xd3, 0x86, 56 | 0xbd, 0xb5, 0xf4, 0xfa, 0x17, 0x55, 0xb4, 0xeb, 0x70, 0xdd, 0x69, 0xb5, 57 | 0x2f, 0x20, 0xc5, 0x41, 0x41, 0x63, 0xc3, 0x28, 0xa0, 0x7f, 0x30, 0x05, 58 | 0x06, 0x06, 0x52, 0x80, 0x78, 0xa0, 0x05, 0x86, 0x05, 0x49, 0x1b, 0xea, 59 | 0x9c, 0xb5, 0x54, 0x2f, 0x1e, 0x68, 0x75, 0x75, 0xc2, 0xc6, 0xdd, 0xd5, 60 | 0x54, 0xbb, 0xd6, 0x49, 0xf5, 0x99, 0x10, 0xe7, 0x69, 0x25, 0xc8, 0xc3, 61 | 0xcd, 0x94, 0x11, 0x64, 0x8c, 0x10, 0xdf, 0x83, 0xdd, 0x3a, 0xd1, 0xe8, 62 | 0x98, 0x28, 0xea, 0x1c, 0x1b, 0x43, 0xe1, 0xe1, 0xa1, 0xe4, 0x10, 0xbf, 63 | 0x69, 0x95, 0x1a, 0x21, 0xc8, 0x8a, 0x8a, 0x4a, 0x3a, 0x5e, 0x54, 0x4c, 64 | 0x5f, 0x15, 0x97, 0xd2, 0xfc, 0x23, 0x27, 0xa8, 0x58, 0xfc, 0xe6, 0xcf, 65 | 0x12, 0x13, 0x1e, 0x40, 0x33, 0xc7, 0x87, 0xd2, 0xf9, 0x03, 0x82, 0xa8, 66 | 0x4b, 0x62, 0x08, 0x45, 0x84, 0x3b, 0x84, 0x7d, 0x0d, 0x42, 0x6c, 0x5e, 67 | 0x6a, 0x6a, 0xeb, 0xa9, 0xbc, 0xa2, 0x86, 0x8e, 0xe5, 0x56, 0xd1, 0xd7, 68 | 0xbb, 0x6a, 0x69, 0xc1, 0xea, 0x4a, 0x2a, 0xae, 0xf0, 0xf3, 0x7f, 0xc3, 69 | 0x19, 0x11, 0x40, 0x8e, 0x09, 0x91, 0x14, 0x38, 0x24, 0x84, 0x42, 0x3a, 70 | 0x46, 0x91, 0x23, 0x22, 0x84, 0x02, 0x5b, 0xc1, 0xaf, 0x4e, 0x60, 0x55, 71 | 0x53, 0x5e, 0x45, 0x55, 0x27, 0x4b, 0xa9, 0x6e, 0x47, 0x15, 0xd5, 0xbc, 72 | 0x53, 0x26, 0x9e, 0x26, 0x7e, 0xb6, 0xcf, 0x9f, 0xe4, 0x58, 0xa8, 0x6f, 73 | 0x25, 0x04, 0xd9, 0x5f, 0xcc, 0x8a, 0x4b, 0x52, 0x93, 0xa9, 0x67, 0x5c, 74 | 0x0c, 0xc5, 0x47, 0x47, 0xc9, 0x99, 0x47, 0x4f, 0xe1, 0x99, 0xa8, 0xa0, 75 | 0xa4, 0x94, 0x0e, 0x15, 0x16, 0xd3, 0xf4, 0x8c, 0x6c, 0xda, 0xed, 0xa7, 76 | 0xd9, 0xb2, 0x7f, 0xb7, 0x40, 0x5a, 0x7c, 0x63, 0x38, 0xa5, 0x76, 0x09, 77 | 0xa6, 0x84, 0xd8, 0x10, 0x61, 0x9f, 0x1e, 0xeb, 0xc4, 0x2a, 0x51, 0x8c, 78 | 0xf1, 0xfc, 0xa2, 0x2a, 0xca, 0x38, 0x56, 0x4d, 0xf7, 0xbc, 0x52, 0x41, 79 | 0xbb, 0x8f, 0xf8, 0x67, 0xb6, 0x0c, 0xe8, 0x11, 0x44, 0x8e, 0xdb, 0xa2, 80 | 0x29, 0x24, 0x39, 0x92, 0x42, 0xe3, 0x22, 0xc5, 0x7f, 0x32, 0xa8, 0xdf, 81 | 0xc0, 0xca, 0xc2, 0x32, 0xaa, 0xca, 0x2e, 0xa3, 0x9a, 0x17, 0x4a, 0xa8, 82 | 0x3e, 0xcb, 0xbf, 0x0f, 0x35, 0x7d, 0xa8, 0xd9, 0xbb, 0x56, 0xbb, 0x0b, 83 | 0x92, 0xc5, 0xf8, 0x6a, 0x9f, 0x1e, 0xd4, 0x31, 0x3e, 0x96, 0xa2, 0x22, 84 | 0xc2, 0xbd, 0x62, 0xa3, 0xb4, 0xbc, 0x82, 0x4e, 0x16, 0x14, 0xd1, 0xe4, 85 | 0x03, 0x59, 0x3e, 0x17, 0x25, 0x8b, 0xf1, 0xe5, 0xbb, 0xc2, 0x29, 0x29, 86 | 0x21, 0x94, 0xa2, 0x23, 0x1b, 0xf6, 0xb2, 0x6d, 0x2d, 0x25, 0x65, 0x35, 87 | 0x94, 0x93, 0x5f, 0x49, 0x37, 0x3d, 0xeb, 0x7b, 0x51, 0x4a, 0x31, 0xde, 88 | 0x17, 0x43, 0x61, 0x89, 0x31, 0x14, 0x1c, 0x19, 0xd6, 0x56, 0xd3, 0x64, 89 | 0xfd, 0xea, 0x32, 0x27, 0x39, 0x73, 0x8b, 0xa9, 0x66, 0x51, 0x31, 0x44, 90 | 0xe9, 0x15, 0x82, 0xbe, 0x6b, 0xd4, 0xae, 0x82, 0xe4, 0x65, 0xea, 0xca, 91 | 0xb4, 0x9e, 0xd4, 0x2f, 0x31, 0xc1, 0x6b, 0x31, 0xba, 0xa0, 0x60, 0x51, 92 | 0xee, 0xcb, 0xcd, 0xa7, 0x6b, 0xf7, 0x1e, 0xf2, 0xd9, 0xf2, 0x95, 0x97, 93 | 0xa9, 0xef, 0xdc, 0x1b, 0x41, 0x69, 0x3d, 0xbc, 0x17, 0xa3, 0xcb, 0x3e, 94 | 0x16, 0xe5, 0xde, 0xac, 0x4a, 0x9a, 0xf0, 0x64, 0xb9, 0xef, 0x96, 0xaf, 95 | 0x62, 0x99, 0x1a, 0x3c, 0x3b, 0x96, 0xc2, 0x52, 0x63, 0xbd, 0x16, 0xa3, 96 | 0xcb, 0x3e, 0x29, 0xca, 0x8c, 0x22, 0xaa, 0x7e, 0xbc, 0x08, 0xcb, 0x57, 97 | 0xdf, 0xe9, 0xab, 0xcd, 0x3d, 0xb5, 0xab, 0x20, 0xe7, 0xa7, 0x74, 0xa5, 98 | 0x71, 0xc9, 0x9d, 0x28, 0x21, 0x26, 0xba, 0xcd, 0x86, 0x6b, 0x35, 0xc8, 99 | 0x2f, 0x2e, 0xa1, 0x35, 0xd9, 0x27, 0xe8, 0xc1, 0xcc, 0xa3, 0x3e, 0xe9, 100 | 0xef, 0xaf, 0xd7, 0x85, 0xd1, 0xb8, 0xf3, 0xc2, 0xa8, 0x43, 0x5c, 0x88, 101 | 0x4f, 0xfa, 0xcb, 0x2b, 0xac, 0xa2, 0x35, 0xeb, 0x9d, 0xf4, 0xd0, 0x9b, 102 | 0xbe, 0x09, 0x44, 0x39, 0x6e, 0x8e, 0xa2, 0xd0, 0xd1, 0xb1, 0x14, 0x1a, 103 | 0x1f, 0xe5, 0x13, 0xfb, 0x2a, 0x0b, 0x4a, 0xa9, 0xf2, 0xcb, 0x22, 0xaa, 104 | 0x79, 0xb9, 0xd4, 0x27, 0xfd, 0xa1, 0x93, 0xb6, 0x23, 0xd0, 0x6e, 0x82, 105 | 0x1c, 0x28, 0x96, 0xaa, 0x2f, 0x8a, 0xa5, 0x6a, 0x9f, 0xae, 0x9d, 0x74, 106 | 0xef, 0x19, 0x3d, 0xb9, 0xc7, 0x7b, 0xca, 0x03, 0x47, 0x4f, 0xd0, 0x14, 107 | 0xb1, 0x74, 0xdd, 0x65, 0x70, 0x3f, 0xc9, 0xd1, 0xd4, 0xa5, 0xb7, 0x87, 108 | 0x53, 0xbf, 0x94, 0x48, 0xdd, 0x5b, 0x32, 0xcf, 0xf6, 0x11, 0xed, 0xcb, 109 | 0x2c, 0xa3, 0x5b, 0x9f, 0xaf, 0x30, 0x1c, 0x7d, 0xe5, 0x68, 0x6a, 0xf0, 110 | 0xf4, 0x18, 0x8a, 0x4a, 0x4d, 0xd4, 0xbf, 0x67, 0xd4, 0x61, 0x60, 0x69, 111 | 0x46, 0x2e, 0x55, 0x2f, 0x11, 0x4b, 0x57, 0x44, 0x5f, 0x3d, 0xa1, 0xe5, 112 | 0x97, 0xeb, 0xed, 0x26, 0xc8, 0x27, 0x52, 0x92, 0x69, 0x7c, 0xf7, 0x2e, 113 | 0x14, 0x1d, 0x25, 0x8e, 0x37, 0x7c, 0x58, 0x4a, 0x4a, 0xcb, 0x69, 0xf5, 114 | 0xe1, 0x63, 0x74, 0x7f, 0x66, 0xb6, 0xa1, 0x5e, 0x17, 0xde, 0x10, 0x46, 115 | 0xe3, 0x47, 0x85, 0x53, 0x4c, 0x54, 0xb0, 0xa1, 0x7e, 0x9a, 0x37, 0x2e, 116 | 0x2e, 0xad, 0xa6, 0x55, 0xdf, 0x54, 0xd0, 0xcc, 0xd7, 0x8d, 0xcd, 0x92, 117 | 0x8e, 0x29, 0x51, 0x14, 0x76, 0x61, 0x02, 0x85, 0x44, 0x7b, 0xb7, 0xef, 118 | 0x6e, 0xcd, 0xa9, 0xaa, 0x92, 0x0a, 0x72, 0xae, 0xcb, 0xa7, 0x9a, 0x65, 119 | 0x98, 0x25, 0x7d, 0x4a, 0xbc, 0xce, 0xce, 0xda, 0x4d, 0x90, 0x5f, 0x0f, 120 | 0xea, 0x4b, 0xbd, 0xbb, 0x24, 0xb5, 0x7a, 0xb4, 0xa1, 0xd3, 0xfe, 0x16, 121 | 0xd5, 0xf8, 0x48, 0xe4, 0xa7, 0x63, 0x39, 0x74, 0xfe, 0x0f, 0xfb, 0xbd, 122 | 0xed, 0x42, 0xb6, 0xfb, 0x72, 0x5e, 0x24, 0xf5, 0xed, 0x11, 0xd1, 0xea, 123 | 0xd1, 0x86, 0xb7, 0x9d, 0xf3, 0x91, 0xc8, 0xfe, 0xac, 0x72, 0xba, 0x60, 124 | 0x96, 0x38, 0x6a, 0x30, 0x50, 0x82, 0xff, 0x16, 0x4f, 0x91, 0xa9, 0x1d, 125 | 0x5a, 0x3d, 0xda, 0xf0, 0xb6, 0x6b, 0x3e, 0x12, 0x29, 0xcb, 0xc8, 0xa3, 126 | 0xea, 0x19, 0x05, 0xde, 0x76, 0x81, 0x76, 0x06, 0x10, 0x68, 0x17, 0x41, 127 | 0x72, 0x06, 0xce, 0xd3, 0x7d, 0x52, 0x28, 0x4d, 0x08, 0xd2, 0x1f, 0x65, 128 | 0xaf, 0x10, 0xe4, 0xdd, 0x07, 0x32, 0xbd, 0xce, 0xe8, 0xe1, 0x0c, 0x9c, 129 | 0xa7, 0x6e, 0x09, 0xa7, 0x33, 0x7a, 0xfa, 0x76, 0xf6, 0x76, 0xf9, 0xba, 130 | 0xe7, 0x50, 0x39, 0x4d, 0x7b, 0xa9, 0xc2, 0xeb, 0x8c, 0x1e, 0xce, 0xc0, 131 | 0x09, 0xb9, 0x33, 0x96, 0x22, 0x7b, 0x89, 0xe5, 0xaa, 0x1f, 0x4a, 0xd9, 132 | 0xc1, 0x5c, 0xaa, 0x7a, 0xae, 0x08, 0x19, 0x3d, 0x7e, 0xc0, 0xd6, 0x53, 133 | 0x97, 0xed, 0x22, 0xc8, 0x4b, 0xe3, 0xa2, 0xe9, 0xb1, 0xde, 0x3d, 0xa8, 134 | 0x67, 0xc7, 0x0e, 0x9e, 0xec, 0xf3, 0xea, 0x7a, 0xc6, 0xc9, 0x3c, 0x7a, 135 | 0xe4, 0xa7, 0x2c, 0xfa, 0x4f, 0x61, 0x89, 0x57, 0xed, 0xc7, 0x0e, 0x75, 136 | 0xd0, 0xdc, 0x49, 0x11, 0xd4, 0x2b, 0xd9, 0xbb, 0x63, 0x04, 0x4f, 0x37, 137 | 0x3d, 0x98, 0xed, 0xa4, 0x47, 0x56, 0x94, 0xd3, 0xda, 0x6d, 0xde, 0x65, 138 | 0xc9, 0x04, 0x0e, 0x13, 0x87, 0xff, 0x37, 0xc5, 0x53, 0x44, 0xf7, 0x38, 139 | 0x4f, 0xb7, 0xf2, 0xea, 0x7a, 0xf9, 0xe1, 0x42, 0xaa, 0x7a, 0xa5, 0x80, 140 | 0xea, 0xbe, 0xab, 0xf2, 0xaa, 0x3d, 0x1a, 0x79, 0x8f, 0x40, 0xbb, 0x08, 141 | 0x72, 0x52, 0x62, 0x3c, 0x3d, 0x28, 0x04, 0x99, 0x24, 0xce, 0x1e, 0xfd, 142 | 0x51, 0x72, 0xc4, 0x99, 0xe4, 0x7c, 0x21, 0xc8, 0x15, 0xb9, 0xde, 0x2d, 143 | 0xbb, 0x26, 0x8e, 0x0e, 0xa6, 0x99, 0xd7, 0x44, 0x50, 0xe7, 0x0e, 0xa1, 144 | 0xfe, 0x30, 0x8f, 0x8e, 0xe7, 0x55, 0xd2, 0x82, 0x77, 0xcb, 0xe9, 0xad, 145 | 0xaf, 0xaa, 0xbd, 0xea, 0x3f, 0x70, 0x4c, 0x18, 0x85, 0x4d, 0x4a, 0xa0, 146 | 0xb0, 0x8e, 0xbe, 0x89, 0x4e, 0x37, 0x37, 0xc2, 0x79, 0xb2, 0x84, 0x9c, 147 | 0x6f, 0xe5, 0x53, 0xdd, 0x67, 0xc6, 0xf6, 0xb9, 0x5e, 0x39, 0x67, 0xf3, 148 | 0x46, 0x10, 0xa4, 0xc6, 0x00, 0x50, 0x5e, 0x90, 0x17, 0x09, 0x41, 0x4e, 149 | 0xf4, 0xb3, 0x20, 0x57, 0x08, 0x41, 0x7e, 0x0e, 0x41, 0x9e, 0xee, 0xe7, 150 | 0x43, 0xbb, 0x08, 0x92, 0x97, 0xac, 0x73, 0xc5, 0x0c, 0x99, 0xea, 0xa7, 151 | 0x25, 0xeb, 0x21, 0xb1, 0x64, 0x7d, 0xd8, 0xca, 0x4b, 0xd6, 0xe1, 0x62, 152 | 0xc9, 0x7a, 0xa3, 0x9f, 0x97, 0xac, 0x2f, 0x8b, 0x25, 0xeb, 0x16, 0x2c, 153 | 0x59, 0x6d, 0x21, 0x48, 0x04, 0x75, 0x10, 0xd4, 0x39, 0xdd, 0x03, 0xdd, 154 | 0x2c, 0xf7, 0x6b, 0x97, 0x19, 0x92, 0xc1, 0xc1, 0xb1, 0x07, 0x8e, 0x3d, 155 | 0xcc, 0x22, 0x92, 0xd3, 0x69, 0x67, 0xbb, 0x09, 0x72, 0xa1, 0x48, 0x0c, 156 | 0xb8, 0x1a, 0x89, 0x01, 0x5e, 0x73, 0x8d, 0xc4, 0x00, 0xaf, 0xa1, 0x53, 157 | 0xba, 0x61, 0xbb, 0x09, 0x92, 0x5f, 0x44, 0x5e, 0x86, 0xd4, 0x39, 0xaf, 158 | 0x07, 0x07, 0x52, 0xe7, 0xbc, 0x86, 0x4e, 0xe9, 0x86, 0xed, 0x26, 0x48, 159 | 0x46, 0x05, 0xc9, 0xe5, 0xc6, 0xc6, 0x86, 0xe3, 0x26, 0x91, 0x5c, 0x7e, 160 | 0x01, 0x92, 0xcb, 0x8d, 0xa1, 0xa8, 0x56, 0xeb, 0x76, 0x15, 0x24, 0x5e, 161 | 0xbf, 0x32, 0x38, 0x18, 0xf0, 0xfa, 0x95, 0x41, 0x00, 0xd5, 0x6b, 0xde, 162 | 0xae, 0x82, 0x64, 0x38, 0xf0, 0x82, 0xb2, 0xb1, 0x41, 0x81, 0x17, 0x94, 163 | 0x8d, 0xe1, 0xa7, 0x5a, 0xeb, 0x76, 0x17, 0xa4, 0x4b, 0x94, 0xf8, 0x84, 164 | 0x87, 0xf7, 0x43, 0x03, 0x9f, 0xf0, 0xf0, 0x1e, 0x3b, 0xd5, 0x5a, 0x2a, 165 | 0x21, 0x48, 0x06, 0x05, 0x1f, 0xb9, 0x32, 0x38, 0x34, 0xf8, 0x23, 0x57, 166 | 0x7f, 0x14, 0x1f, 0xb9, 0x1a, 0x8c, 0x8f, 0x5c, 0x19, 0x44, 0xb2, 0x5d, 167 | 0x9b, 0x2b, 0x23, 0x48, 0x17, 0x0a, 0xfc, 0xe2, 0xf2, 0x9f, 0x1a, 0x7d, 168 | 0x06, 0x32, 0x54, 0x7c, 0x06, 0x32, 0xe2, 0xe7, 0xcf, 0x40, 0x96, 0x89, 169 | 0xcf, 0x40, 0x56, 0x35, 0xfa, 0x0c, 0xe4, 0xab, 0xe2, 0x33, 0x90, 0x46, 170 | 0x5f, 0x44, 0x6e, 0x2b, 0xfa, 0xf8, 0x0c, 0x64, 0x5b, 0x11, 0x43, 0xfd, 171 | 0xb6, 0x20, 0xa0, 0x9c, 0x20, 0x1b, 0x1b, 0x8f, 0x0f, 0x25, 0xb7, 0x85, 172 | 0xca, 0x96, 0x75, 0xe5, 0x87, 0x92, 0x63, 0x7e, 0xfe, 0x50, 0x32, 0x7f, 173 | 0x88, 0x8e, 0x3f, 0x94, 0xcc, 0x7f, 0xf8, 0x50, 0xb2, 0x31, 0x60, 0xfd, 174 | 0xd8, 0x5a, 0x69, 0x41, 0xfa, 0xd1, 0x6f, 0x74, 0x0d, 0x04, 0x94, 0x44, 175 | 0x00, 0x82, 0x54, 0x92, 0x16, 0x18, 0x65, 0x57, 0x04, 0x20, 0x48, 0xbb, 176 | 0x32, 0x0f, 0xbf, 0x95, 0x44, 0x00, 0x82, 0x54, 0x92, 0x16, 0x18, 0x65, 177 | 0x57, 0x04, 0x20, 0x48, 0xbb, 0x32, 0x0f, 0xbf, 0x95, 0x44, 0x00, 0x82, 178 | 0x54, 0x92, 0x16, 0x18, 0x65, 0x57, 0x04, 0x20, 0x48, 0xbb, 0x32, 0x0f, 179 | 0xbf, 0x95, 0x44, 0x00, 0x82, 0x54, 0x92, 0x16, 0x18, 0x65, 0x57, 0x04, 180 | 0x20, 0x48, 0xbb, 0x32, 0x0f, 0xbf, 0x95, 0x44, 0x00, 0x82, 0x54, 0x92, 181 | 0x16, 0x18, 0x65, 0x57, 0x04, 0x20, 0x48, 0xbb, 0x32, 0x0f, 0xbf, 0x95, 182 | 0x44, 0x00, 0x82, 0x54, 0x92, 0x16, 0x18, 0x65, 0x57, 0x04, 0x20, 0x48, 183 | 0xbb, 0x32, 0x0f, 0xbf, 0x95, 0x44, 0x00, 0x82, 0x54, 0x92, 0x16, 0x18, 184 | 0x65, 0x57, 0x04, 0x20, 0x48, 0xbb, 0x32, 0x0f, 0xbf, 0x95, 0x44, 0x00, 185 | 0x82, 0x54, 0x92, 0x16, 0x18, 0x65, 0x57, 0x04, 0x20, 0x48, 0xbb, 0x32, 186 | 0x0f, 0xbf, 0x95, 0x44, 0x00, 0x82, 0x54, 0x92, 0x16, 0x18, 0x65, 0x57, 187 | 0x04, 0x20, 0x48, 0xbb, 0x32, 0x0f, 0xbf, 0x95, 0x44, 0x00, 0x82, 0x54, 188 | 0x92, 0x16, 0x18, 0x65, 0x57, 0x04, 0x20, 0x48, 0xbb, 0x32, 0x0f, 0xbf, 189 | 0x95, 0x44, 0x00, 0x82, 0x54, 0x92, 0x16, 0x18, 0x65, 0x57, 0x04, 0x20, 190 | 0x48, 0xbb, 0x32, 0x0f, 0xbf, 0x95, 0x44, 0x00, 0x82, 0x54, 0x92, 0x16, 191 | 0x18, 0x65, 0x57, 0x04, 0x20, 0x48, 0xbb, 0x32, 0x0f, 0xbf, 0x95, 0x44, 192 | 0x00, 0x82, 0x54, 0x92, 0x16, 0x18, 0x65, 0x57, 0x04, 0x20, 0x48, 0xbb, 193 | 0x32, 0x0f, 0xbf, 0x95, 0x44, 0x00, 0x82, 0x54, 0x92, 0x16, 0x18, 0x65, 194 | 0x57, 0x04, 0x20, 0x48, 0xbb, 0x32, 0x0f, 0xbf, 0x95, 0x44, 0x00, 0x82, 195 | 0x54, 0x92, 0x16, 0x18, 0x65, 0x57, 0x04, 0x20, 0x48, 0xbb, 0x32, 0x0f, 196 | 0xbf, 0x95, 0x44, 0x00, 0x82, 0x54, 0x92, 0x16, 0x18, 0x65, 0x57, 0x04, 197 | 0x20, 0x48, 0xbb, 0x32, 0x0f, 0xbf, 0x95, 0x44, 0x00, 0x82, 0x54, 0x92, 198 | 0x16, 0x18, 0x65, 0x57, 0x04, 0x20, 0x48, 0xbb, 0x32, 0x0f, 0xbf, 0x95, 199 | 0x44, 0x00, 0x82, 0x54, 0x92, 0x16, 0x18, 0x65, 0x57, 0x04, 0x20, 0x48, 200 | 0xbb, 0x32, 0x0f, 0xbf, 0x95, 0x44, 0x00, 0x82, 0x54, 0x92, 0x16, 0x18, 201 | 0x65, 0x57, 0x04, 0x20, 0x48, 0xbb, 0x32, 0x0f, 0xbf, 0x95, 0x44, 0x00, 202 | 0x82, 0x54, 0x92, 0x16, 0x18, 0x65, 0x57, 0x04, 0x20, 0x48, 0xbb, 0x32, 203 | 0x0f, 0xbf, 0x95, 0x44, 0x00, 0x82, 0x54, 0x92, 0x16, 0x18, 0x65, 0x57, 204 | 0x04, 0x20, 0x48, 0xbb, 0x32, 0x0f, 0xbf, 0x95, 0x44, 0x00, 0x82, 0x54, 205 | 0x92, 0x16, 0x18, 0x65, 0x57, 0x04, 0x20, 0x48, 0xbb, 0x32, 0x0f, 0xbf, 206 | 0x95, 0x44, 0x00, 0x82, 0x54, 0x92, 0x16, 0x18, 0x65, 0x57, 0x04, 0x20, 207 | 0x48, 0xbb, 0x32, 0x0f, 0xbf, 0x95, 0x44, 0x00, 0x82, 0x54, 0x92, 0x16, 208 | 0x18, 0x65, 0x57, 0x04, 0x20, 0x48, 0xbb, 0x32, 0x0f, 0xbf, 0x95, 0x44, 209 | 0x00, 0x82, 0x54, 0x92, 0x16, 0x18, 0x65, 0x57, 0x04, 0x20, 0x48, 0xbb, 210 | 0x32, 0x0f, 0xbf, 0x95, 0x44, 0x00, 0x82, 0x54, 0x92, 0x16, 0x18, 0x65, 211 | 0x57, 0x04, 0x20, 0x48, 0xbb, 0x32, 0x0f, 0xbf, 0x95, 0x44, 0x00, 0x82, 212 | 0x54, 0x92, 0x16, 0x18, 0x65, 0x57, 0x04, 0x20, 0x48, 0xbb, 0x32, 0x0f, 213 | 0xbf, 0x95, 0x44, 0x00, 0x82, 0x54, 0x92, 0x16, 0x18, 0x65, 0x57, 0x04, 214 | 0x94, 0x13, 0xe4, 0xb9, 0x0b, 0xde, 0xa1, 0xf0, 0xa4, 0x64, 0xca, 0xd9, 215 | 0xf4, 0x19, 0xed, 0x7a, 0x61, 0x8e, 0x5d, 0x79, 0xa1, 0x5f, 0x3d, 0xf3, 216 | 0x21, 0x05, 0x47, 0xc5, 0xd2, 0xd1, 0x75, 0x1f, 0xd0, 0xbe, 0xd7, 0x9f, 217 | 0xb4, 0x2d, 0x0e, 0x7a, 0x1d, 0xef, 0xfd, 0x87, 0x3b, 0xa9, 0xfb, 0xa5, 218 | 0x13, 0xa8, 0xae, 0xa6, 0x9a, 0xbe, 0x9a, 0x3a, 0x46, 0x6f, 0x33, 0xe5, 219 | 0xea, 0x41, 0x90, 0xca, 0x51, 0xd2, 0x60, 0x10, 0x04, 0xd9, 0x36, 0x62, 220 | 0x20, 0xc8, 0xb6, 0xe1, 0xa5, 0xbb, 0x36, 0x66, 0x48, 0x08, 0x52, 0xf7, 221 | 0x60, 0x69, 0x54, 0x11, 0x82, 0xf4, 0x06, 0x35, 0x1d, 0x6d, 0x20, 0x48, 222 | 0x08, 0x52, 0xc7, 0x30, 0x69, 0x51, 0x05, 0x82, 0xf4, 0x06, 0x35, 0x1d, 223 | 0x6d, 0x20, 0xc8, 0xe6, 0x82, 0x7c, 0x5f, 0xec, 0x21, 0x17, 0xeb, 0x40, 224 | 0xce, 0xde, 0x55, 0x7a, 0x5f, 0x7b, 0x07, 0x75, 0xff, 0xcd, 0x1f, 0xb1, 225 | 0x87, 0xf4, 0xf5, 0x30, 0xf0, 0x89, 0x20, 0x03, 0x02, 0xa9, 0xf3, 0xc8, 226 | 0xb1, 0x94, 0x34, 0xf2, 0x12, 0x8a, 0xea, 0xde, 0x57, 0x04, 0x47, 0x62, 227 | 0xa8, 0xb6, 0xd2, 0x49, 0x15, 0x27, 0x8e, 0x50, 0xde, 0xf6, 0xf5, 0x94, 228 | 0xfd, 0xdf, 0x55, 0x54, 0x5d, 0x56, 0xac, 0x69, 0x7a, 0xdc, 0x19, 0x67, 229 | 0xd1, 0xd0, 0xfb, 0x9f, 0xd1, 0xe5, 0x96, 0x56, 0x00, 0xc1, 0x35, 0x30, 230 | 0xb8, 0x83, 0xcd, 0x0f, 0xdf, 0x40, 0x65, 0xd9, 0x19, 0xad, 0xf6, 0x35, 231 | 0xec, 0x2f, 0xcb, 0x29, 0x3a, 0xa5, 0x1f, 0x95, 0x1f, 0xcf, 0xa2, 0x4d, 232 | 0x0f, 0x4d, 0x6a, 0x52, 0xcf, 0xb5, 0x87, 0xcc, 0xfe, 0xfc, 0x3d, 0x3a, 233 | 0xf0, 0xd6, 0xd3, 0xd4, 0x65, 0xf4, 0x15, 0xd4, 0xf5, 0xc2, 0x2b, 0x28, 234 | 0xbc, 0x53, 0x37, 0xaa, 0xaf, 0xad, 0xa5, 0xe2, 0x83, 0xbb, 0x28, 0xeb, 235 | 0xa3, 0x37, 0xa9, 0x70, 0xcf, 0xf7, 0x1e, 0x6d, 0x8d, 0xe8, 0x92, 0x42, 236 | 0xdd, 0xc7, 0xfe, 0x81, 0xe2, 0xfa, 0xa7, 0x53, 0x68, 0x7c, 0x47, 0xaa, 237 | 0xaf, 0xab, 0x23, 0xe7, 0xc9, 0xa3, 0x94, 0xb7, 0x63, 0x3d, 0x1d, 0x59, 238 | 0xfb, 0x2e, 0x55, 0x15, 0xe7, 0xbb, 0xef, 0xc3, 0x00, 0x9e, 0xdc, 0x71, 239 | 0x13, 0x4e, 0x97, 0xce, 0xa5, 0x2e, 0xe7, 0x5f, 0x46, 0xc9, 0x63, 0xae, 240 | 0x6a, 0xf0, 0x45, 0xd8, 0x52, 0x7a, 0xf8, 0x00, 0x65, 0x7f, 0xfa, 0x2e, 241 | 0x9d, 0xdc, 0xf2, 0xa5, 0x5b, 0x3b, 0x62, 0xfb, 0x0c, 0xa2, 0x1e, 0xbf, 242 | 0xbd, 0x9e, 0x62, 0xfb, 0x0c, 0xa6, 0x80, 0xe0, 0x60, 0xc1, 0x67, 0x36, 243 | 0x1d, 0xff, 0xfa, 0x43, 0xca, 0xfe, 0xe2, 0x7d, 0x4a, 0x1d, 0x77, 0x0b, 244 | 0xf5, 0xb8, 0x6c, 0x12, 0x04, 0xe9, 0x71, 0x34, 0xb4, 0xb1, 0x82, 0x51, 245 | 0x41, 0x3a, 0xc2, 0x23, 0x69, 0xf0, 0xdd, 0x0b, 0x29, 0xb6, 0xdf, 0x99, 246 | 0xad, 0xde, 0xb9, 0xb2, 0xe0, 0x24, 0x6d, 0x5f, 0x74, 0xb7, 0x10, 0xc2, 247 | 0xe1, 0x16, 0x75, 0x8c, 0x0a, 0x92, 0x07, 0xff, 0x39, 0xf3, 0xde, 0x94, 248 | 0xfd, 0x1e, 0xfe, 0xe4, 0x6d, 0xfa, 0x69, 0xe5, 0x3f, 0x34, 0xed, 0xe0, 249 | 0xc1, 0x78, 0xee, 0xfc, 0xb7, 0xe5, 0xb5, 0x8c, 0xf7, 0x96, 0x51, 0xe6, 250 | 0x87, 0xaf, 0x6b, 0x0a, 0xf2, 0x88, 0x78, 0x78, 0x04, 0x85, 0x86, 0x89, 251 | 0x41, 0x7c, 0x79, 0xcb, 0x7e, 0xea, 0xeb, 0x69, 0xef, 0xab, 0x4f, 0xd0, 252 | 0x31, 0x31, 0x28, 0x5b, 0x2b, 0x9d, 0xcf, 0xfb, 0x0d, 0xf5, 0x9b, 0x7c, 253 | 0x3f, 0x05, 0x3a, 0x82, 0x35, 0xab, 0x54, 0x97, 0x14, 0xd2, 0xce, 0x67, 254 | 0x66, 0x52, 0xf1, 0x4f, 0x3f, 0x6a, 0x5e, 0x37, 0x8a, 0x67, 0x73, 0x41, 255 | 0x56, 0xe4, 0x1c, 0xa5, 0x94, 0xcb, 0xaf, 0xd7, 0xbc, 0xd7, 0xa1, 0xf7, 256 | 0x97, 0xd3, 0xa1, 0x7f, 0xbe, 0xaa, 0x79, 0x2d, 0xe9, 0xdc, 0x8b, 0xa8, 257 | 0xff, 0x94, 0x47, 0x28, 0x20, 0x30, 0xb0, 0xc5, 0xf5, 0x9c, 0x8d, 0x9f, 258 | 0x51, 0x85, 0x78, 0xc0, 0x70, 0xbf, 0x88, 0xb2, 0xb6, 0x51, 0x70, 0x9e, 259 | 0xaa, 0x1b, 0x15, 0x64, 0xff, 0xa9, 0x0f, 0x53, 0xa7, 0x11, 0x63, 0xe5, 260 | 0x6d, 0x72, 0xb7, 0x7e, 0x2d, 0x9e, 0xba, 0xeb, 0xa8, 0xaa, 0x28, 0x8f, 261 | 0x1c, 0x91, 0x31, 0x14, 0x3f, 0x60, 0x38, 0x75, 0x19, 0x75, 0x19, 0x05, 262 | 0x04, 0x39, 0xa8, 0x34, 0x6b, 0x3f, 0x7d, 0xf7, 0xe8, 0xcd, 0x44, 0x62, 263 | 0x50, 0x37, 0x2e, 0x21, 0x31, 0xf1, 0x72, 0x26, 0x69, 0xad, 0x84, 0x44, 264 | 0xc7, 0x53, 0xef, 0x09, 0x77, 0xc9, 0x81, 0x51, 0x59, 0x98, 0x4b, 0x1b, 265 | 0x66, 0x8c, 0x6b, 0x51, 0xf5, 0xac, 0x87, 0x9e, 0x17, 0x4f, 0xf1, 0x41, 266 | 0xf2, 0xbe, 0x1b, 0xee, 0xb9, 0x4a, 0xce, 0x02, 0xcd, 0x4b, 0xcf, 0x2b, 267 | 0x26, 0x53, 0xcf, 0x2b, 0x1b, 0xee, 0xff, 0xed, 0xfd, 0xd7, 0x92, 0x33, 268 | 0xef, 0xb8, 0xa6, 0x20, 0x0b, 0x76, 0x7d, 0x27, 0xed, 0x39, 0x2a, 0x66, 269 | 0x81, 0x82, 0x5d, 0x5b, 0x64, 0x9d, 0xd8, 0xbe, 0x83, 0xe5, 0x0c, 0x13, 270 | 0x18, 0x1c, 0x22, 0x07, 0x20, 0xcf, 0xae, 0xce, 0xdc, 0x63, 0x2d, 0xee, 271 | 0x11, 0xdd, 0xf3, 0x0c, 0x4a, 0x9f, 0xfd, 0x82, 0xb0, 0x35, 0x88, 0xea, 272 | 0xaa, 0xab, 0x64, 0x1f, 0xc5, 0x19, 0xbb, 0xa5, 0x38, 0xb9, 0xcf, 0xce, 273 | 0x23, 0x2f, 0x25, 0x0a, 0x08, 0x90, 0x33, 0xe4, 0xe6, 0x59, 0xd7, 0x6b, 274 | 0xae, 0x1a, 0x8c, 0xe2, 0xd9, 0x58, 0x90, 0xf9, 0x3f, 0x6c, 0x22, 0x7e, 275 | 0xe0, 0x1d, 0xfb, 0xea, 0x5f, 0x54, 0xb8, 0x7b, 0x2b, 0xd5, 0xd5, 0xd6, 276 | 0x50, 0x4c, 0xaf, 0x81, 0x62, 0xf6, 0xbe, 0x96, 0x02, 0x43, 0x42, 0x05, 277 | 0x14, 0x75, 0xb4, 0x65, 0xce, 0xcd, 0x72, 0xc6, 0x6c, 0x5c, 0x42, 0xe3, 278 | 0x12, 0xe9, 0x1c, 0xf1, 0xf0, 0xe2, 0x07, 0x13, 0xfb, 0xcb, 0x7e, 0x14, 279 | 0xed, 0xdb, 0x2e, 0x57, 0x0a, 0xb1, 0x69, 0x43, 0xa9, 0xdb, 0x25, 0x57, 280 | 0xcb, 0xfe, 0xe2, 0x07, 0x9e, 0x0d, 0x41, 0x7a, 0x12, 0x58, 0x5b, 0xaf, 281 | 0x1b, 0x11, 0x24, 0x13, 0x36, 0xea, 0xb9, 0xff, 0x48, 0xb1, 0x1c, 0xfb, 282 | 0xea, 0x43, 0x31, 0x7b, 0x2c, 0x6c, 0x71, 0x7b, 0x3e, 0xab, 0xe2, 0x00, 283 | 0x00, 0x97, 0xad, 0xf3, 0x6e, 0x17, 0x33, 0xc3, 0x0f, 0xba, 0x4d, 0xe4, 284 | 0x7e, 0xcf, 0xbc, 0x77, 0x89, 0x18, 0x54, 0xe9, 0x42, 0x64, 0xb5, 0xb4, 285 | 0xfd, 0xc9, 0xe9, 0x9a, 0x4b, 0xc6, 0x2e, 0xe7, 0xff, 0x96, 0xd2, 0x6e, 286 | 0x9c, 0x29, 0xfb, 0xdd, 0xb1, 0xe4, 0x3e, 0xca, 0xdf, 0xf9, 0x6d, 0x8b, 287 | 0x7b, 0xf0, 0x2c, 0xca, 0xb3, 0x69, 0xe1, 0xde, 0x6d, 0xb4, 0x6d, 0xe1, 288 | 0x5d, 0x2d, 0xae, 0xbb, 0x96, 0xac, 0x7c, 0x9f, 0x3d, 0x2f, 0xcd, 0xa3, 289 | 0x13, 0xdf, 0x7e, 0xda, 0xa4, 0x4e, 0x9c, 0x58, 0x01, 0x0c, 0x9d, 0xf9, 290 | 0x77, 0xf9, 0x5b, 0xd6, 0xbf, 0xdf, 0xa0, 0x83, 0xab, 0x5f, 0x6c, 0xd1, 291 | 0xc7, 0xa0, 0x3f, 0xcf, 0xa3, 0xc4, 0xf4, 0xd1, 0x62, 0xe0, 0xd6, 0xd0, 292 | 0xd6, 0xc7, 0x6f, 0xa5, 0x92, 0xcc, 0x7d, 0x4d, 0xea, 0x74, 0x3a, 0xef, 293 | 0x52, 0xea, 0x7f, 0xcb, 0x6c, 0xf9, 0x5b, 0xc6, 0x9a, 0x97, 0x28, 0xf3, 294 | 0x5f, 0xaf, 0x35, 0xb9, 0xee, 0x2b, 0x3c, 0x5d, 0x9c, 0xf2, 0x43, 0x81, 295 | 0xcf, 0x54, 0x8f, 0xff, 0xef, 0xe3, 0x26, 0xf7, 0x49, 0x18, 0x78, 0x0e, 296 | 0x0d, 0xb9, 0xa7, 0x61, 0x9f, 0x7c, 0xe4, 0xd3, 0x95, 0x74, 0xe0, 0xed, 297 | 0x67, 0x9b, 0x5c, 0x4f, 0xb9, 0xfc, 0x06, 0x4a, 0xbd, 0x6a, 0x4a, 0x03, 298 | 0x9e, 0x8b, 0xef, 0xa1, 0xfc, 0x1f, 0x37, 0x35, 0xb9, 0x9e, 0x78, 0xd6, 299 | 0x28, 0x1a, 0x78, 0xe7, 0xe3, 0x0d, 0x0f, 0x1e, 0x9c, 0x43, 0xea, 0x1e, 300 | 0xcf, 0xba, 0x2a, 0x1a, 0x11, 0x24, 0x3f, 0xed, 0x83, 0x42, 0xc2, 0xe4, 301 | 0x7d, 0x98, 0x7c, 0x1e, 0xcc, 0xcd, 0x4b, 0x58, 0x87, 0xce, 0x34, 0x62, 302 | 0xd1, 0xbb, 0xf2, 0x67, 0x0e, 0x96, 0x1c, 0x5d, 0xf7, 0xbe, 0x2e, 0xbb, 303 | 0xb8, 0x52, 0xe3, 0xfd, 0xe1, 0x81, 0x77, 0x9e, 0x15, 0xfb, 0xaf, 0x95, 304 | 0x9a, 0x6d, 0x83, 0x42, 0xc3, 0xe9, 0xbc, 0xa7, 0x3e, 0x10, 0x4f, 0xf4, 305 | 0x70, 0xcd, 0x04, 0x87, 0xa8, 0xee, 0x7d, 0x68, 0xf8, 0xa3, 0xaf, 0xc8, 306 | 0xb6, 0x7b, 0x5f, 0x59, 0x20, 0x96, 0x9c, 0xff, 0x6e, 0xd1, 0x8f, 0x4b, 307 | 0x90, 0xbc, 0x07, 0xe5, 0xbd, 0xa8, 0x56, 0x19, 0x3e, 0xe7, 0x65, 0x8a, 308 | 0xea, 0xd1, 0x57, 0x53, 0xd4, 0xfc, 0xf0, 0x18, 0xf5, 0xdc, 0x27, 0xd2, 309 | 0x06, 0x16, 0xc0, 0x9e, 0xe5, 0x7f, 0xd5, 0xec, 0xe3, 0xec, 0xb9, 0xaf, 310 | 0x51, 0x64, 0xb7, 0x5e, 0x54, 0xb4, 0x7f, 0x27, 0x7d, 0x3f, 0xff, 0x8e, 311 | 0xa6, 0x75, 0x7c, 0x84, 0xa7, 0x8b, 0x53, 0x67, 0xde, 0x09, 0xb1, 0x1a, 312 | 0xb8, 0xa6, 0xc5, 0xaa, 0x84, 0x6f, 0xca, 0xcb, 0x77, 0x5e, 0xc6, 0x17, 313 | 0x8a, 0x99, 0x6f, 0xdb, 0x82, 0x3f, 0x37, 0xb1, 0x63, 0xf0, 0xb4, 0x27, 314 | 0xa8, 0xc3, 0x90, 0x91, 0x72, 0x8b, 0xb1, 0xe9, 0xa1, 0x89, 0x9a, 0x7e, 315 | 0xa4, 0x3f, 0xfc, 0x22, 0xc5, 0xa4, 0xf6, 0x87, 0x20, 0x75, 0x8f, 0x66, 316 | 0x9d, 0x15, 0x0d, 0x09, 0x52, 0xc7, 0x3d, 0x78, 0x80, 0x9e, 0xff, 0xfc, 317 | 0x5a, 0x59, 0xf3, 0xe0, 0xaa, 0x17, 0x44, 0x60, 0x64, 0x85, 0x8e, 0x56, 318 | 0x44, 0x1d, 0x87, 0x5d, 0x20, 0x9f, 0xc2, 0x5c, 0x72, 0x36, 0xfe, 0x97, 319 | 0x76, 0x2d, 0x7d, 0xd4, 0x6d, 0xbb, 0xb4, 0x1b, 0x1f, 0x90, 0xfb, 0x3e, 320 | 0x7e, 0x30, 0xac, 0x9f, 0xfe, 0x7b, 0xaa, 0x29, 0x2f, 0x3d, 0x55, 0xbf, 321 | 0xd7, 0xd5, 0xb7, 0x35, 0x04, 0x20, 0xaa, 0x2a, 0x1b, 0xae, 0x55, 0x94, 322 | 0xb5, 0x2a, 0x48, 0xde, 0x1f, 0xed, 0x5a, 0x3a, 0x47, 0xf3, 0x5e, 0x6c, 323 | 0x0f, 0xdb, 0x55, 0x7e, 0x2c, 0x93, 0x36, 0xcd, 0xba, 0xae, 0x49, 0x9d, 324 | 0x90, 0xd8, 0x04, 0x3a, 0x6f, 0xc9, 0x07, 0xf2, 0x37, 0x9e, 0x71, 0x78, 325 | 0xe6, 0xd1, 0x2a, 0xae, 0x25, 0x29, 0x2f, 0xaf, 0xd7, 0x4f, 0xbf, 0x52, 326 | 0x17, 0x16, 0x8d, 0x2b, 0xe9, 0xc1, 0xd3, 0xc5, 0x29, 0xcf, 0x6c, 0x3c, 327 | 0xc3, 0x69, 0x95, 0x21, 0xd3, 0x9f, 0xa4, 0x84, 0xc1, 0xe7, 0xca, 0x20, 328 | 0x58, 0xf3, 0x07, 0xd0, 0xb0, 0x47, 0x96, 0x11, 0x2f, 0xbf, 0x79, 0xc9, 329 | 0xbb, 0xe3, 0x6f, 0xda, 0xed, 0xd3, 0x26, 0x0b, 0xbc, 0x47, 0x0b, 0xbc, 330 | 0x31, 0x43, 0xb6, 0x99, 0x43, 0xb7, 0x0d, 0x7c, 0x21, 0x48, 0x7e, 0x9a, 331 | 0x26, 0x8b, 0x7d, 0x45, 0x74, 0x4a, 0x9a, 0x4c, 0x3f, 0x6b, 0xad, 0x1c, 332 | 0x5c, 0xb5, 0x54, 0x46, 0x2a, 0x3d, 0x95, 0x88, 0xce, 0xdd, 0x29, 0xfd, 333 | 0xe1, 0x65, 0xc4, 0x01, 0x8e, 0xd2, 0xc3, 0x3f, 0xd1, 0xf7, 0xf3, 0x6e, 334 | 0xa3, 0xda, 0x2a, 0xa7, 0xdb, 0x66, 0x31, 0xbd, 0x07, 0x51, 0xfa, 0xac, 335 | 0xe7, 0x65, 0x9d, 0x7d, 0xaf, 0x2d, 0xa2, 0xa3, 0x5f, 0xfe, 0xb3, 0xa1, 336 | 0xbe, 0x98, 0x75, 0x46, 0x3c, 0xb1, 0x92, 0x78, 0xa6, 0x76, 0x27, 0x36, 337 | 0x3d, 0x99, 0x3a, 0x03, 0x6e, 0x9b, 0x43, 0x49, 0xe7, 0x5c, 0x44, 0x15, 338 | 0x39, 0xd9, 0xb4, 0x71, 0xe6, 0x84, 0x26, 0xf6, 0x70, 0xfa, 0x21, 0x63, 339 | 0xc9, 0x85, 0x67, 0xc7, 0xe6, 0xcb, 0x44, 0x57, 0xe5, 0xbe, 0xd7, 0x4d, 340 | 0x97, 0xfb, 0xd1, 0xda, 0xca, 0x0a, 0xfa, 0xfa, 0xf6, 0x86, 0xbd, 0x77, 341 | 0xf3, 0x62, 0x14, 0x4f, 0x3d, 0x9c, 0x0e, 0xba, 0x6b, 0x3e, 0xf1, 0xd2, 342 | 0x53, 0x2b, 0xe2, 0xcc, 0xab, 0x09, 0x5e, 0x55, 0xe4, 0xed, 0xd8, 0x40, 343 | 0x3b, 0x9f, 0xba, 0x5f, 0xd3, 0x46, 0x9c, 0x43, 0x7a, 0x1a, 0xc5, 0x5e, 344 | 0x5e, 0xd7, 0x43, 0x9e, 0xbb, 0xae, 0x39, 0xfc, 0x9d, 0xf2, 0xbb, 0x3f, 345 | 0xe9, 0xba, 0xbb, 0x1e, 0x41, 0xf2, 0x3e, 0x2a, 0x7d, 0xf6, 0x8b, 0x14, 346 | 0x99, 0x9c, 0x4a, 0x35, 0x65, 0x25, 0xb4, 0x65, 0xee, 0x2d, 0x32, 0xa2, 347 | 0xa7, 0xa7, 0xb8, 0xf6, 0x89, 0xbc, 0x4f, 0xe5, 0xfd, 0x2a, 0x17, 0x0e, 348 | 0xf6, 0x70, 0xd0, 0x87, 0x0b, 0x0f, 0x2e, 0x1e, 0x64, 0x5a, 0xc5, 0xb8, 349 | 0x20, 0x45, 0x14, 0x77, 0x41, 0x43, 0x14, 0x57, 0x9f, 0x20, 0x9d, 0x42, 350 | 0x90, 0x97, 0xb4, 0x30, 0xc5, 0x17, 0x78, 0x1a, 0xe5, 0x14, 0x82, 0xd4, 351 | 0x33, 0xda, 0xfc, 0x54, 0xc7, 0x08, 0x79, 0xf1, 0x22, 0x72, 0x78, 0xe6, 352 | 0x7d, 0x4f, 0x4b, 0xcb, 0xf8, 0x9c, 0x6e, 0xff, 0x9b, 0x4b, 0x64, 0xc4, 353 | 0x8e, 0x83, 0x1a, 0xae, 0xd2, 0x74, 0x89, 0xe5, 0x79, 0x86, 0x1c, 0x70, 354 | 0xeb, 0x5f, 0x28, 0xe9, 0xdc, 0x8b, 0x65, 0x04, 0x90, 0x05, 0x94, 0xbf, 355 | 0x73, 0xa3, 0x6e, 0xcf, 0x1b, 0x07, 0x90, 0x78, 0xef, 0xc3, 0x7b, 0xa0, 356 | 0xbe, 0x13, 0xa7, 0x51, 0xf2, 0xc5, 0xe3, 0x65, 0x64, 0x73, 0xc3, 0x0c, 357 | 0x8e, 0xc0, 0xb6, 0xdc, 0xe7, 0xf2, 0x0d, 0x54, 0x10, 0xa4, 0xaf, 0xf0, 358 | 0x34, 0xc2, 0x29, 0x63, 0x01, 0x41, 0xea, 0x1e, 0x72, 0xbe, 0xaf, 0x68, 359 | 0x84, 0x3c, 0xd7, 0x12, 0x8e, 0x97, 0x5f, 0x1b, 0x1f, 0x98, 0xa0, 0x79, 360 | 0xe0, 0xdd, 0x16, 0x41, 0x76, 0xbb, 0xf8, 0x6a, 0xea, 0x33, 0xf1, 0x6e, 361 | 0xe9, 0x64, 0xc6, 0x7b, 0x2f, 0x8a, 0xb3, 0xc2, 0x37, 0xda, 0xe4, 0x30, 362 | 0x1f, 0xa1, 0x8c, 0x5c, 0xfc, 0x9e, 0x3c, 0x66, 0xe1, 0x73, 0xc6, 0x8c, 363 | 0x35, 0xcb, 0xc5, 0xbe, 0x6e, 0x0d, 0x85, 0xc4, 0x24, 0x68, 0x46, 0x13, 364 | 0x1b, 0x77, 0x6e, 0x5c, 0x90, 0xc6, 0x97, 0xac, 0xbe, 0xc2, 0xd3, 0x08, 365 | 0xa7, 0x10, 0x64, 0x9b, 0x86, 0x9c, 0xef, 0x2b, 0x1b, 0x21, 0xcf, 0x6d, 366 | 0xc4, 0xf0, 0x67, 0x53, 0x1d, 0x91, 0xd1, 0x34, 0xea, 0xd9, 0x8f, 0xe4, 367 | 0xbf, 0xdc, 0x2d, 0x59, 0x79, 0x69, 0x39, 0xf4, 0x81, 0x67, 0xa5, 0x98, 368 | 0xf8, 0x3c, 0xf3, 0x87, 0xe7, 0x66, 0x69, 0x46, 0x07, 0x3d, 0x21, 0xe0, 369 | 0x3a, 0x7a, 0xe0, 0x7d, 0x1e, 0x47, 0x54, 0xb9, 0x4f, 0x2e, 0xdf, 0xcd, 370 | 0xb9, 0x49, 0x9e, 0x85, 0xb6, 0x56, 0x5c, 0x82, 0x74, 0x17, 0x40, 0x72, 371 | 0x1b, 0xd4, 0x11, 0xa2, 0xe7, 0x48, 0x2f, 0x17, 0x3d, 0x41, 0x1d, 0xad, 372 | 0x33, 0x55, 0x5f, 0xe1, 0x69, 0x84, 0x53, 0xb6, 0x1f, 0x41, 0x1d, 0x4f, 373 | 0xa3, 0xcc, 0x8f, 0xd7, 0x8d, 0x90, 0x77, 0xf6, 0xe3, 0x6f, 0x50, 0x64, 374 | 0xd7, 0x9e, 0x0d, 0x87, 0xfe, 0x62, 0xc0, 0x6b, 0x95, 0xc6, 0x99, 0x38, 375 | 0xad, 0x09, 0x92, 0x67, 0xb0, 0x61, 0x73, 0x96, 0x13, 0x1f, 0x48, 0x73, 376 | 0x04, 0x73, 0xcb, 0x63, 0x53, 0xa9, 0xd6, 0x59, 0xee, 0x95, 0xd7, 0x1c, 377 | 0x10, 0xe1, 0xb0, 0x3d, 0x97, 0xbc, 0x6d, 0xff, 0xa3, 0x0e, 0x43, 0x7f, 378 | 0xa5, 0x19, 0x49, 0x6c, 0xde, 0xf9, 0x2f, 0xc7, 0x1e, 0x07, 0x45, 0xd4, 379 | 0x51, 0x7b, 0x4f, 0x7c, 0xea, 0xd8, 0x43, 0xe3, 0xa8, 0x40, 0x1c, 0xca, 380 | 0xd1, 0xa8, 0xbf, 0x7f, 0x24, 0x03, 0x51, 0xfa, 0x8e, 0x3d, 0x76, 0x88, 381 | 0x63, 0x8f, 0x86, 0xf3, 0x59, 0x57, 0xf1, 0x15, 0x9e, 0x46, 0x38, 0x65, 382 | 0x5b, 0x74, 0x1d, 0x7b, 0xcc, 0x5e, 0x2a, 0x92, 0x0c, 0x06, 0x20, 0xca, 383 | 0xea, 0xd5, 0x28, 0x75, 0xd3, 0xc8, 0x08, 0x79, 0x03, 0xef, 0x78, 0x8c, 384 | 0x3a, 0x0e, 0xbf, 0x50, 0xee, 0xcb, 0xb6, 0xcc, 0x9d, 0xd2, 0x62, 0x06, 385 | 0xe2, 0x33, 0x4a, 0x3e, 0x80, 0x8e, 0xed, 0x3b, 0x44, 0x5a, 0xa0, 0x25, 386 | 0x48, 0x79, 0xf8, 0x2f, 0xf6, 0xa1, 0x71, 0x22, 0x03, 0x84, 0x45, 0xc8, 387 | 0x62, 0x64, 0x51, 0x7a, 0x5b, 0xb8, 0xbf, 0x11, 0x8b, 0x56, 0xc9, 0x1c, 388 | 0x52, 0x57, 0x39, 0xf8, 0xee, 0xf3, 0x94, 0xf5, 0xf1, 0x5b, 0x6e, 0xbb, 389 | 0x6c, 0x9c, 0x18, 0xc0, 0x47, 0x2c, 0x27, 0x37, 0x7f, 0xd1, 0xa4, 0x3e, 390 | 0xfb, 0x70, 0xd6, 0x83, 0xcf, 0xc9, 0xdf, 0x38, 0x52, 0xcc, 0xbe, 0x34, 391 | 0x2f, 0xa7, 0xf0, 0xd0, 0x91, 0x18, 0xc0, 0x89, 0x05, 0x9c, 0x60, 0xd0, 392 | 0xb8, 0xf8, 0x02, 0x4f, 0xee, 0xcf, 0x08, 0xa7, 0xdc, 0x9e, 0x83, 0x74, 393 | 0x1c, 0x5c, 0xe2, 0xc2, 0xc9, 0x18, 0x9c, 0xbd, 0xd4, 0xb8, 0x24, 0x0c, 394 | 0x1e, 0x21, 0xd2, 0x25, 0x17, 0x20, 0x31, 0xc0, 0xdb, 0x41, 0xea, 0xae, 395 | 0x9d, 0x8b, 0x3c, 0x8e, 0x4c, 0x72, 0x1e, 0xa7, 0xbb, 0xc2, 0xe7, 0x78, 396 | 0xb9, 0xdf, 0x7f, 0x73, 0xaa, 0x4a, 0xc2, 0x20, 0x91, 0xf1, 0x31, 0xa3, 397 | 0x21, 0xe3, 0xa3, 0xba, 0xb4, 0x48, 0x0e, 0x30, 0x0e, 0xee, 0xf0, 0xb2, 398 | 0x93, 0xc3, 0xe6, 0xfc, 0x36, 0x40, 0x49, 0xe6, 0x5e, 0x4a, 0x1c, 0x3a, 399 | 0x4a, 0xd6, 0xd1, 0x12, 0x64, 0xf2, 0x98, 0x71, 0xd4, 0xf7, 0xba, 0x19, 400 | 0xf2, 0xfa, 0x89, 0x0d, 0x6b, 0x65, 0x02, 0xb6, 0xbb, 0xc2, 0xc9, 0xdd, 401 | 0x55, 0x45, 0xee, 0x93, 0xb3, 0x53, 0xaf, 0x9a, 0x7a, 0x2a, 0x7f, 0x93, 402 | 0x83, 0x43, 0xdf, 0xde, 0x7b, 0x35, 0x71, 0x3e, 0xad, 0xbb, 0xe2, 0x12, 403 | 0x64, 0x81, 0x48, 0x09, 0x8b, 0x4b, 0x3b, 0x53, 0x24, 0xc4, 0xaf, 0xa6, 404 | 0x82, 0xdd, 0xbf, 0xa4, 0xce, 0x75, 0xbb, 0xf8, 0x1a, 0x99, 0x6e, 0xc6, 405 | 0xe7, 0x6e, 0x9b, 0x67, 0x5f, 0x2f, 0x8f, 0x3e, 0x9a, 0x17, 0x4e, 0x1a, 406 | 0x48, 0x17, 0x33, 0x07, 0xa7, 0xca, 0xc9, 0xd4, 0x39, 0xf1, 0xf5, 0x81, 407 | 0x92, 0x43, 0x7b, 0xc4, 0xbf, 0x43, 0x64, 0x0a, 0x5b, 0xa7, 0x11, 0x22, 408 | 0xaa, 0xca, 0xa9, 0x73, 0xe2, 0x0c, 0x92, 0xcf, 0x31, 0x1b, 0x9f, 0x95, 409 | 0x72, 0x5f, 0xbe, 0xc0, 0xd3, 0x17, 0x82, 0xe4, 0x87, 0x19, 0x27, 0x0e, 410 | 0xb8, 0xfc, 0xcd, 0xfe, 0x6c, 0xb5, 0x4c, 0x86, 0x20, 0x91, 0x92, 0x18, 411 | 0xdb, 0x6f, 0x08, 0x75, 0x13, 0x89, 0xf3, 0x45, 0xfb, 0x76, 0x88, 0xd4, 412 | 0xc8, 0x61, 0x98, 0x21, 0x7d, 0x2d, 0x4a, 0x97, 0x20, 0xf5, 0xf4, 0xcb, 413 | 0x91, 0xca, 0xf5, 0xd3, 0x7e, 0xdf, 0xa4, 0x6a, 0x1f, 0x91, 0x67, 0xda, 414 | 0x4d, 0xe4, 0x46, 0x6a, 0x15, 0x7e, 0xc3, 0x63, 0xfb, 0xa2, 0x69, 0xc4, 415 | 0x4b, 0xbd, 0xd6, 0x04, 0xd9, 0xf8, 0x69, 0xac, 0xc7, 0x86, 0x1d, 0x4b, 416 | 0xee, 0xf5, 0x18, 0x79, 0x0d, 0x4f, 0xfa, 0xe5, 0x08, 0x82, 0x9f, 0xee, 417 | 0xfc, 0x94, 0xf7, 0x54, 0x1a, 0x07, 0x75, 0xf8, 0x81, 0xc2, 0xe9, 0x78, 418 | 0x2d, 0x8a, 0xc8, 0x83, 0xe5, 0x54, 0xb4, 0x53, 0x67, 0x9c, 0x1a, 0x9d, 419 | 0x72, 0x52, 0xf6, 0x19, 0x37, 0xcf, 0x6a, 0x35, 0xb9, 0x9c, 0x31, 0xdc, 420 | 0xf9, 0xd4, 0x03, 0x52, 0xa8, 0x5a, 0xc5, 0x28, 0x9e, 0xbe, 0x10, 0x24, 421 | 0xf7, 0xd1, 0x49, 0xbc, 0xbd, 0xc3, 0x7e, 0x68, 0x26, 0x97, 0x6f, 0xfe, 422 | 0x9c, 0x2a, 0xf3, 0x73, 0xf0, 0x09, 0x0f, 0x4f, 0x83, 0xca, 0x9b, 0xeb, 423 | 0x46, 0x05, 0xc9, 0xf7, 0xe4, 0xec, 0x95, 0xae, 0xe2, 0xb0, 0x9b, 0x5f, 424 | 0x6d, 0xe2, 0x04, 0x6c, 0x9e, 0x8d, 0x78, 0x76, 0x39, 0x2c, 0x96, 0x89, 425 | 0xce, 0xdc, 0xe3, 0x74, 0xc1, 0x4b, 0xeb, 0x4e, 0xab, 0x20, 0x1b, 0xe7, 426 | 0x9d, 0x72, 0x5e, 0xea, 0xf1, 0xf5, 0x9f, 0x78, 0x84, 0xa6, 0xb1, 0x20, 427 | 0xf9, 0xf8, 0xa6, 0xeb, 0x98, 0x2b, 0x85, 0x28, 0x7f, 0x27, 0xbf, 0x37, 428 | 0xc4, 0x4b, 0xf2, 0x12, 0x7e, 0xfd, 0xea, 0xe3, 0x15, 0xa7, 0x12, 0xce, 429 | 0xdd, 0x75, 0xc8, 0x89, 0x0d, 0x3c, 0x8b, 0x70, 0x72, 0x7d, 0x68, 0x42, 430 | 0x92, 0x6c, 0xef, 0x14, 0x6f, 0x5d, 0xe4, 0x6e, 0xfb, 0x46, 0xa6, 0xff, 431 | 0xf1, 0x6a, 0xc2, 0x5d, 0x31, 0x82, 0xa7, 0xaf, 0x04, 0xc9, 0xfd, 0x70, 432 | 0x52, 0xfd, 0xa9, 0xd7, 0xaf, 0x1c, 0x0e, 0xf9, 0x0a, 0xd9, 0xf1, 0x6f, 433 | 0x3e, 0x26, 0x9e, 0x31, 0x53, 0xc7, 0x4f, 0x85, 0x20, 0x3d, 0x8e, 0x2a, 434 | 0x54, 0x90, 0x08, 0xb8, 0xd2, 0xe8, 0xf8, 0x9d, 0xcc, 0xf5, 0xd3, 0xae, 435 | 0x90, 0x59, 0x31, 0x28, 0x40, 0x40, 0x0b, 0x01, 0xe5, 0x3e, 0x72, 0x65, 436 | 0x35, 0x9a, 0x78, 0xdf, 0xc3, 0x39, 0xa5, 0x1c, 0xed, 0xe4, 0x3d, 0xe9, 437 | 0xee, 0x65, 0x8f, 0x59, 0xcd, 0x45, 0xf8, 0xe3, 0x43, 0x04, 0x20, 0x48, 438 | 0x1f, 0x82, 0xa9, 0xd5, 0x15, 0x67, 0xf9, 0x70, 0xb6, 0x0f, 0x97, 0xed, 439 | 0x8b, 0x67, 0x50, 0xc1, 0x8f, 0x9b, 0xfd, 0x7c, 0x47, 0x74, 0x6f, 0x66, 440 | 0x04, 0x20, 0x48, 0x3f, 0xb3, 0xe7, 0x7a, 0x8b, 0x41, 0x1e, 0xbc, 0xdf, 441 | 0x33, 0x5e, 0x24, 0x17, 0xb4, 0x7c, 0x59, 0xd9, 0xcf, 0x26, 0xa0, 0x7b, 442 | 0x13, 0x21, 0x00, 0x41, 0xfa, 0x91, 0x2c, 0x7e, 0x05, 0x6a, 0xe4, 0xe2, 443 | 0x35, 0x32, 0x32, 0xe8, 0xee, 0x73, 0x1e, 0x7e, 0x34, 0x01, 0x5d, 0x9b, 444 | 0x0c, 0x01, 0x08, 0xd2, 0x64, 0x84, 0xc1, 0x5c, 0x6b, 0x23, 0x00, 0x41, 445 | 0x5a, 0x9b, 0x5f, 0x78, 0x67, 0x32, 0x04, 0x20, 0x48, 0x93, 0x11, 0x06, 446 | 0x73, 0xad, 0x8d, 0x00, 0x04, 0x69, 0x6d, 0x7e, 0xe1, 0x9d, 0xc9, 0x10, 447 | 0x80, 0x20, 0x4d, 0x46, 0x18, 0xcc, 0xb5, 0x36, 0x02, 0x10, 0xa4, 0xb5, 448 | 0xf9, 0x85, 0x77, 0x26, 0x43, 0x00, 0x82, 0x34, 0x19, 0x61, 0x30, 0xd7, 449 | 0xda, 0x08, 0x40, 0x90, 0xd6, 0xe6, 0x17, 0xde, 0x99, 0x0c, 0x01, 0x08, 450 | 0xd2, 0x64, 0x84, 0xc1, 0x5c, 0x6b, 0x23, 0x00, 0x41, 0x5a, 0x9b, 0x5f, 451 | 0x78, 0x67, 0x32, 0x04, 0x20, 0x48, 0x93, 0x11, 0x06, 0x73, 0xad, 0x8d, 452 | 0x00, 0x04, 0x69, 0x6d, 0x7e, 0xe1, 0x9d, 0xc9, 0x10, 0x80, 0x20, 0x4d, 453 | 0x46, 0x18, 0xcc, 0xb5, 0x36, 0x02, 0x10, 0xa4, 0xb5, 0xf9, 0x85, 0x77, 454 | 0x26, 0x43, 0x00, 0x82, 0x34, 0x19, 0x61, 0x30, 0xd7, 0xda, 0x08, 0x40, 455 | 0x90, 0xd6, 0xe6, 0x17, 0xde, 0x99, 0x0c, 0x01, 0x08, 0xd2, 0x64, 0x84, 456 | 0xc1, 0x5c, 0x6b, 0x23, 0x00, 0x41, 0x5a, 0x9b, 0x5f, 0x78, 0x67, 0x32, 457 | 0x04, 0x20, 0x48, 0x93, 0x11, 0x06, 0x73, 0xad, 0x8d, 0x00, 0x04, 0x69, 458 | 0x6d, 0x7e, 0xe1, 0x9d, 0xc9, 0x10, 0x80, 0x20, 0x4d, 0x46, 0x18, 0xcc, 459 | 0xb5, 0x36, 0x02, 0x10, 0xa4, 0xb5, 0xf9, 0x85, 0x77, 0x26, 0x43, 0x00, 460 | 0x82, 0x34, 0x19, 0x61, 0x30, 0xd7, 0xda, 0x08, 0x40, 0x90, 0xd6, 0xe6, 461 | 0x17, 0xde, 0x99, 0x0c, 0x01, 0x08, 0xd2, 0x64, 0x84, 0xc1, 0x5c, 0x6b, 462 | 0x23, 0x00, 0x41, 0x5a, 0x9b, 0x5f, 0x78, 0x67, 0x32, 0x04, 0x20, 0x48, 463 | 0x93, 0x11, 0x06, 0x73, 0xad, 0x8d, 0x00, 0x04, 0x69, 0x6d, 0x7e, 0xe1, 464 | 0x9d, 0xc9, 0x10, 0x80, 0x20, 0x4d, 0x46, 0x18, 0xcc, 0xb5, 0x36, 0x02, 465 | 0x10, 0xa4, 0xb5, 0xf9, 0x85, 0x77, 0x26, 0x43, 0x00, 0x82, 0x34, 0x19, 466 | 0x61, 0x30, 0xd7, 0xda, 0x08, 0x40, 0x90, 0xd6, 0xe6, 0x17, 0xde, 0x99, 467 | 0x0c, 0x01, 0x08, 0xd2, 0x64, 0x84, 0xc1, 0x5c, 0x6b, 0x23, 0x00, 0x41, 468 | 0x5a, 0x9b, 0x5f, 0x78, 0x67, 0x32, 0x04, 0x20, 0x48, 0x93, 0x11, 0x06, 469 | 0x73, 0xad, 0x8d, 0x00, 0x04, 0x69, 0x6d, 0x7e, 0xe1, 0x9d, 0xc9, 0x10, 470 | 0x80, 0x20, 0x4d, 0x46, 0x18, 0xcc, 0xb5, 0x36, 0x02, 0x10, 0xa4, 0xb5, 471 | 0xf9, 0x85, 0x77, 0x26, 0x43, 0x00, 0x82, 0x34, 0x19, 0x61, 0x30, 0xd7, 472 | 0xda, 0x08, 0x40, 0x90, 0xd6, 0xe6, 0x17, 0xde, 0x99, 0x0c, 0x01, 0x08, 473 | 0xd2, 0x64, 0x84, 0xc1, 0x5c, 0x6b, 0x23, 0x00, 0x41, 0x5a, 0x9b, 0x5f, 474 | 0x78, 0x67, 0x32, 0x04, 0x20, 0x48, 0x93, 0x11, 0x06, 0x73, 0xad, 0x8d, 475 | 0x00, 0x04, 0x69, 0x6d, 0x7e, 0xe1, 0x9d, 0xc9, 0x10, 0x80, 0x20, 0x4d, 476 | 0x46, 0x18, 0xcc, 0xb5, 0x36, 0x02, 0x10, 0xa4, 0xb5, 0xf9, 0x85, 0x77, 477 | 0x26, 0x43, 0x00, 0x82, 0x34, 0x19, 0x61, 0x30, 0xd7, 0xda, 0x08, 0x40, 478 | 0x90, 0xd6, 0xe6, 0x17, 0xde, 0x99, 0x0c, 0x81, 0xff, 0x03, 0xec, 0x9a, 479 | 0x2d, 0x0e, 0xaf, 0x07, 0xf7, 0xbd, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 480 | 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82}; 481 | #endif // ICON_BYTES_HXX -------------------------------------------------------------------------------- /src/back_end/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "android-tzdata" 7 | version = "0.1.1" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" 10 | 11 | [[package]] 12 | name = "android_system_properties" 13 | version = "0.1.5" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" 16 | dependencies = [ 17 | "libc", 18 | ] 19 | 20 | [[package]] 21 | name = "autocfg" 22 | version = "1.5.0" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" 25 | 26 | [[package]] 27 | name = "back_end" 28 | version = "1.0.0" 29 | dependencies = [ 30 | "chrono", 31 | "dirs", 32 | "hex", 33 | "rusqlite", 34 | "serde", 35 | "sha2", 36 | "toml", 37 | "webbrowser", 38 | ] 39 | 40 | [[package]] 41 | name = "bitflags" 42 | version = "2.9.1" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" 45 | 46 | [[package]] 47 | name = "block-buffer" 48 | version = "0.10.4" 49 | source = "registry+https://github.com/rust-lang/crates.io-index" 50 | checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" 51 | dependencies = [ 52 | "generic-array", 53 | ] 54 | 55 | [[package]] 56 | name = "bumpalo" 57 | version = "3.19.0" 58 | source = "registry+https://github.com/rust-lang/crates.io-index" 59 | checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" 60 | 61 | [[package]] 62 | name = "bytes" 63 | version = "1.10.1" 64 | source = "registry+https://github.com/rust-lang/crates.io-index" 65 | checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" 66 | 67 | [[package]] 68 | name = "cc" 69 | version = "1.2.33" 70 | source = "registry+https://github.com/rust-lang/crates.io-index" 71 | checksum = "3ee0f8803222ba5a7e2777dd72ca451868909b1ac410621b676adf07280e9b5f" 72 | dependencies = [ 73 | "shlex", 74 | ] 75 | 76 | [[package]] 77 | name = "cesu8" 78 | version = "1.1.0" 79 | source = "registry+https://github.com/rust-lang/crates.io-index" 80 | checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" 81 | 82 | [[package]] 83 | name = "cfg-if" 84 | version = "1.0.1" 85 | source = "registry+https://github.com/rust-lang/crates.io-index" 86 | checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" 87 | 88 | [[package]] 89 | name = "chrono" 90 | version = "0.4.41" 91 | source = "registry+https://github.com/rust-lang/crates.io-index" 92 | checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" 93 | dependencies = [ 94 | "android-tzdata", 95 | "iana-time-zone", 96 | "js-sys", 97 | "num-traits", 98 | "wasm-bindgen", 99 | "windows-link", 100 | ] 101 | 102 | [[package]] 103 | name = "combine" 104 | version = "4.6.7" 105 | source = "registry+https://github.com/rust-lang/crates.io-index" 106 | checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" 107 | dependencies = [ 108 | "bytes", 109 | "memchr", 110 | ] 111 | 112 | [[package]] 113 | name = "core-foundation" 114 | version = "0.10.1" 115 | source = "registry+https://github.com/rust-lang/crates.io-index" 116 | checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" 117 | dependencies = [ 118 | "core-foundation-sys", 119 | "libc", 120 | ] 121 | 122 | [[package]] 123 | name = "core-foundation-sys" 124 | version = "0.8.7" 125 | source = "registry+https://github.com/rust-lang/crates.io-index" 126 | checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" 127 | 128 | [[package]] 129 | name = "cpufeatures" 130 | version = "0.2.17" 131 | source = "registry+https://github.com/rust-lang/crates.io-index" 132 | checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" 133 | dependencies = [ 134 | "libc", 135 | ] 136 | 137 | [[package]] 138 | name = "crypto-common" 139 | version = "0.1.6" 140 | source = "registry+https://github.com/rust-lang/crates.io-index" 141 | checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" 142 | dependencies = [ 143 | "generic-array", 144 | "typenum", 145 | ] 146 | 147 | [[package]] 148 | name = "digest" 149 | version = "0.10.7" 150 | source = "registry+https://github.com/rust-lang/crates.io-index" 151 | checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" 152 | dependencies = [ 153 | "block-buffer", 154 | "crypto-common", 155 | ] 156 | 157 | [[package]] 158 | name = "dirs" 159 | version = "6.0.0" 160 | source = "registry+https://github.com/rust-lang/crates.io-index" 161 | checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" 162 | dependencies = [ 163 | "dirs-sys", 164 | ] 165 | 166 | [[package]] 167 | name = "dirs-sys" 168 | version = "0.5.0" 169 | source = "registry+https://github.com/rust-lang/crates.io-index" 170 | checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" 171 | dependencies = [ 172 | "libc", 173 | "option-ext", 174 | "redox_users", 175 | "windows-sys 0.59.0", 176 | ] 177 | 178 | [[package]] 179 | name = "displaydoc" 180 | version = "0.2.5" 181 | source = "registry+https://github.com/rust-lang/crates.io-index" 182 | checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" 183 | dependencies = [ 184 | "proc-macro2", 185 | "quote", 186 | "syn", 187 | ] 188 | 189 | [[package]] 190 | name = "equivalent" 191 | version = "1.0.2" 192 | source = "registry+https://github.com/rust-lang/crates.io-index" 193 | checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" 194 | 195 | [[package]] 196 | name = "fallible-iterator" 197 | version = "0.3.0" 198 | source = "registry+https://github.com/rust-lang/crates.io-index" 199 | checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" 200 | 201 | [[package]] 202 | name = "fallible-streaming-iterator" 203 | version = "0.1.9" 204 | source = "registry+https://github.com/rust-lang/crates.io-index" 205 | checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" 206 | 207 | [[package]] 208 | name = "foldhash" 209 | version = "0.1.5" 210 | source = "registry+https://github.com/rust-lang/crates.io-index" 211 | checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" 212 | 213 | [[package]] 214 | name = "form_urlencoded" 215 | version = "1.2.1" 216 | source = "registry+https://github.com/rust-lang/crates.io-index" 217 | checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" 218 | dependencies = [ 219 | "percent-encoding", 220 | ] 221 | 222 | [[package]] 223 | name = "generic-array" 224 | version = "0.14.7" 225 | source = "registry+https://github.com/rust-lang/crates.io-index" 226 | checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" 227 | dependencies = [ 228 | "typenum", 229 | "version_check", 230 | ] 231 | 232 | [[package]] 233 | name = "getrandom" 234 | version = "0.2.16" 235 | source = "registry+https://github.com/rust-lang/crates.io-index" 236 | checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" 237 | dependencies = [ 238 | "cfg-if", 239 | "libc", 240 | "wasi", 241 | ] 242 | 243 | [[package]] 244 | name = "hashbrown" 245 | version = "0.15.5" 246 | source = "registry+https://github.com/rust-lang/crates.io-index" 247 | checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" 248 | dependencies = [ 249 | "foldhash", 250 | ] 251 | 252 | [[package]] 253 | name = "hashlink" 254 | version = "0.10.0" 255 | source = "registry+https://github.com/rust-lang/crates.io-index" 256 | checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" 257 | dependencies = [ 258 | "hashbrown", 259 | ] 260 | 261 | [[package]] 262 | name = "hex" 263 | version = "0.4.3" 264 | source = "registry+https://github.com/rust-lang/crates.io-index" 265 | checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" 266 | 267 | [[package]] 268 | name = "iana-time-zone" 269 | version = "0.1.63" 270 | source = "registry+https://github.com/rust-lang/crates.io-index" 271 | checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" 272 | dependencies = [ 273 | "android_system_properties", 274 | "core-foundation-sys", 275 | "iana-time-zone-haiku", 276 | "js-sys", 277 | "log", 278 | "wasm-bindgen", 279 | "windows-core", 280 | ] 281 | 282 | [[package]] 283 | name = "iana-time-zone-haiku" 284 | version = "0.1.2" 285 | source = "registry+https://github.com/rust-lang/crates.io-index" 286 | checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" 287 | dependencies = [ 288 | "cc", 289 | ] 290 | 291 | [[package]] 292 | name = "icu_collections" 293 | version = "2.0.0" 294 | source = "registry+https://github.com/rust-lang/crates.io-index" 295 | checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" 296 | dependencies = [ 297 | "displaydoc", 298 | "potential_utf", 299 | "yoke", 300 | "zerofrom", 301 | "zerovec", 302 | ] 303 | 304 | [[package]] 305 | name = "icu_locale_core" 306 | version = "2.0.0" 307 | source = "registry+https://github.com/rust-lang/crates.io-index" 308 | checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" 309 | dependencies = [ 310 | "displaydoc", 311 | "litemap", 312 | "tinystr", 313 | "writeable", 314 | "zerovec", 315 | ] 316 | 317 | [[package]] 318 | name = "icu_normalizer" 319 | version = "2.0.0" 320 | source = "registry+https://github.com/rust-lang/crates.io-index" 321 | checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" 322 | dependencies = [ 323 | "displaydoc", 324 | "icu_collections", 325 | "icu_normalizer_data", 326 | "icu_properties", 327 | "icu_provider", 328 | "smallvec", 329 | "zerovec", 330 | ] 331 | 332 | [[package]] 333 | name = "icu_normalizer_data" 334 | version = "2.0.0" 335 | source = "registry+https://github.com/rust-lang/crates.io-index" 336 | checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" 337 | 338 | [[package]] 339 | name = "icu_properties" 340 | version = "2.0.1" 341 | source = "registry+https://github.com/rust-lang/crates.io-index" 342 | checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" 343 | dependencies = [ 344 | "displaydoc", 345 | "icu_collections", 346 | "icu_locale_core", 347 | "icu_properties_data", 348 | "icu_provider", 349 | "potential_utf", 350 | "zerotrie", 351 | "zerovec", 352 | ] 353 | 354 | [[package]] 355 | name = "icu_properties_data" 356 | version = "2.0.1" 357 | source = "registry+https://github.com/rust-lang/crates.io-index" 358 | checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" 359 | 360 | [[package]] 361 | name = "icu_provider" 362 | version = "2.0.0" 363 | source = "registry+https://github.com/rust-lang/crates.io-index" 364 | checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" 365 | dependencies = [ 366 | "displaydoc", 367 | "icu_locale_core", 368 | "stable_deref_trait", 369 | "tinystr", 370 | "writeable", 371 | "yoke", 372 | "zerofrom", 373 | "zerotrie", 374 | "zerovec", 375 | ] 376 | 377 | [[package]] 378 | name = "idna" 379 | version = "1.0.3" 380 | source = "registry+https://github.com/rust-lang/crates.io-index" 381 | checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" 382 | dependencies = [ 383 | "idna_adapter", 384 | "smallvec", 385 | "utf8_iter", 386 | ] 387 | 388 | [[package]] 389 | name = "idna_adapter" 390 | version = "1.2.1" 391 | source = "registry+https://github.com/rust-lang/crates.io-index" 392 | checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" 393 | dependencies = [ 394 | "icu_normalizer", 395 | "icu_properties", 396 | ] 397 | 398 | [[package]] 399 | name = "indexmap" 400 | version = "2.10.0" 401 | source = "registry+https://github.com/rust-lang/crates.io-index" 402 | checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" 403 | dependencies = [ 404 | "equivalent", 405 | "hashbrown", 406 | ] 407 | 408 | [[package]] 409 | name = "jni" 410 | version = "0.21.1" 411 | source = "registry+https://github.com/rust-lang/crates.io-index" 412 | checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" 413 | dependencies = [ 414 | "cesu8", 415 | "cfg-if", 416 | "combine", 417 | "jni-sys", 418 | "log", 419 | "thiserror 1.0.69", 420 | "walkdir", 421 | "windows-sys 0.45.0", 422 | ] 423 | 424 | [[package]] 425 | name = "jni-sys" 426 | version = "0.3.0" 427 | source = "registry+https://github.com/rust-lang/crates.io-index" 428 | checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" 429 | 430 | [[package]] 431 | name = "js-sys" 432 | version = "0.3.77" 433 | source = "registry+https://github.com/rust-lang/crates.io-index" 434 | checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" 435 | dependencies = [ 436 | "once_cell", 437 | "wasm-bindgen", 438 | ] 439 | 440 | [[package]] 441 | name = "libc" 442 | version = "0.2.175" 443 | source = "registry+https://github.com/rust-lang/crates.io-index" 444 | checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" 445 | 446 | [[package]] 447 | name = "libredox" 448 | version = "0.1.9" 449 | source = "registry+https://github.com/rust-lang/crates.io-index" 450 | checksum = "391290121bad3d37fbddad76d8f5d1c1c314cfc646d143d7e07a3086ddff0ce3" 451 | dependencies = [ 452 | "bitflags", 453 | "libc", 454 | ] 455 | 456 | [[package]] 457 | name = "libsqlite3-sys" 458 | version = "0.35.0" 459 | source = "registry+https://github.com/rust-lang/crates.io-index" 460 | checksum = "133c182a6a2c87864fe97778797e46c7e999672690dc9fa3ee8e241aa4a9c13f" 461 | dependencies = [ 462 | "cc", 463 | "pkg-config", 464 | "vcpkg", 465 | ] 466 | 467 | [[package]] 468 | name = "litemap" 469 | version = "0.8.0" 470 | source = "registry+https://github.com/rust-lang/crates.io-index" 471 | checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" 472 | 473 | [[package]] 474 | name = "log" 475 | version = "0.4.27" 476 | source = "registry+https://github.com/rust-lang/crates.io-index" 477 | checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" 478 | 479 | [[package]] 480 | name = "memchr" 481 | version = "2.7.5" 482 | source = "registry+https://github.com/rust-lang/crates.io-index" 483 | checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" 484 | 485 | [[package]] 486 | name = "ndk-context" 487 | version = "0.1.1" 488 | source = "registry+https://github.com/rust-lang/crates.io-index" 489 | checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" 490 | 491 | [[package]] 492 | name = "num-traits" 493 | version = "0.2.19" 494 | source = "registry+https://github.com/rust-lang/crates.io-index" 495 | checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 496 | dependencies = [ 497 | "autocfg", 498 | ] 499 | 500 | [[package]] 501 | name = "objc2" 502 | version = "0.6.1" 503 | source = "registry+https://github.com/rust-lang/crates.io-index" 504 | checksum = "88c6597e14493ab2e44ce58f2fdecf095a51f12ca57bec060a11c57332520551" 505 | dependencies = [ 506 | "objc2-encode", 507 | ] 508 | 509 | [[package]] 510 | name = "objc2-encode" 511 | version = "4.1.0" 512 | source = "registry+https://github.com/rust-lang/crates.io-index" 513 | checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" 514 | 515 | [[package]] 516 | name = "objc2-foundation" 517 | version = "0.3.1" 518 | source = "registry+https://github.com/rust-lang/crates.io-index" 519 | checksum = "900831247d2fe1a09a683278e5384cfb8c80c79fe6b166f9d14bfdde0ea1b03c" 520 | dependencies = [ 521 | "bitflags", 522 | "objc2", 523 | ] 524 | 525 | [[package]] 526 | name = "once_cell" 527 | version = "1.21.3" 528 | source = "registry+https://github.com/rust-lang/crates.io-index" 529 | checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" 530 | 531 | [[package]] 532 | name = "option-ext" 533 | version = "0.2.0" 534 | source = "registry+https://github.com/rust-lang/crates.io-index" 535 | checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" 536 | 537 | [[package]] 538 | name = "percent-encoding" 539 | version = "2.3.1" 540 | source = "registry+https://github.com/rust-lang/crates.io-index" 541 | checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" 542 | 543 | [[package]] 544 | name = "pkg-config" 545 | version = "0.3.32" 546 | source = "registry+https://github.com/rust-lang/crates.io-index" 547 | checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" 548 | 549 | [[package]] 550 | name = "potential_utf" 551 | version = "0.1.2" 552 | source = "registry+https://github.com/rust-lang/crates.io-index" 553 | checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" 554 | dependencies = [ 555 | "zerovec", 556 | ] 557 | 558 | [[package]] 559 | name = "proc-macro2" 560 | version = "1.0.96" 561 | source = "registry+https://github.com/rust-lang/crates.io-index" 562 | checksum = "beef09f85ae72cea1ef96ba6870c51e6382ebfa4f0e85b643459331f3daa5be0" 563 | dependencies = [ 564 | "unicode-ident", 565 | ] 566 | 567 | [[package]] 568 | name = "quote" 569 | version = "1.0.40" 570 | source = "registry+https://github.com/rust-lang/crates.io-index" 571 | checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 572 | dependencies = [ 573 | "proc-macro2", 574 | ] 575 | 576 | [[package]] 577 | name = "redox_users" 578 | version = "0.5.2" 579 | source = "registry+https://github.com/rust-lang/crates.io-index" 580 | checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" 581 | dependencies = [ 582 | "getrandom", 583 | "libredox", 584 | "thiserror 2.0.14", 585 | ] 586 | 587 | [[package]] 588 | name = "rusqlite" 589 | version = "0.37.0" 590 | source = "registry+https://github.com/rust-lang/crates.io-index" 591 | checksum = "165ca6e57b20e1351573e3729b958bc62f0e48025386970b6e4d29e7a7e71f3f" 592 | dependencies = [ 593 | "bitflags", 594 | "fallible-iterator", 595 | "fallible-streaming-iterator", 596 | "hashlink", 597 | "libsqlite3-sys", 598 | "smallvec", 599 | ] 600 | 601 | [[package]] 602 | name = "rustversion" 603 | version = "1.0.22" 604 | source = "registry+https://github.com/rust-lang/crates.io-index" 605 | checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" 606 | 607 | [[package]] 608 | name = "same-file" 609 | version = "1.0.6" 610 | source = "registry+https://github.com/rust-lang/crates.io-index" 611 | checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" 612 | dependencies = [ 613 | "winapi-util", 614 | ] 615 | 616 | [[package]] 617 | name = "serde" 618 | version = "1.0.219" 619 | source = "registry+https://github.com/rust-lang/crates.io-index" 620 | checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" 621 | dependencies = [ 622 | "serde_derive", 623 | ] 624 | 625 | [[package]] 626 | name = "serde_derive" 627 | version = "1.0.219" 628 | source = "registry+https://github.com/rust-lang/crates.io-index" 629 | checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" 630 | dependencies = [ 631 | "proc-macro2", 632 | "quote", 633 | "syn", 634 | ] 635 | 636 | [[package]] 637 | name = "serde_spanned" 638 | version = "1.0.0" 639 | source = "registry+https://github.com/rust-lang/crates.io-index" 640 | checksum = "40734c41988f7306bb04f0ecf60ec0f3f1caa34290e4e8ea471dcd3346483b83" 641 | dependencies = [ 642 | "serde", 643 | ] 644 | 645 | [[package]] 646 | name = "sha2" 647 | version = "0.10.9" 648 | source = "registry+https://github.com/rust-lang/crates.io-index" 649 | checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" 650 | dependencies = [ 651 | "cfg-if", 652 | "cpufeatures", 653 | "digest", 654 | ] 655 | 656 | [[package]] 657 | name = "shlex" 658 | version = "1.3.0" 659 | source = "registry+https://github.com/rust-lang/crates.io-index" 660 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 661 | 662 | [[package]] 663 | name = "smallvec" 664 | version = "1.15.1" 665 | source = "registry+https://github.com/rust-lang/crates.io-index" 666 | checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" 667 | 668 | [[package]] 669 | name = "stable_deref_trait" 670 | version = "1.2.0" 671 | source = "registry+https://github.com/rust-lang/crates.io-index" 672 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 673 | 674 | [[package]] 675 | name = "syn" 676 | version = "2.0.104" 677 | source = "registry+https://github.com/rust-lang/crates.io-index" 678 | checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" 679 | dependencies = [ 680 | "proc-macro2", 681 | "quote", 682 | "unicode-ident", 683 | ] 684 | 685 | [[package]] 686 | name = "synstructure" 687 | version = "0.13.2" 688 | source = "registry+https://github.com/rust-lang/crates.io-index" 689 | checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" 690 | dependencies = [ 691 | "proc-macro2", 692 | "quote", 693 | "syn", 694 | ] 695 | 696 | [[package]] 697 | name = "thiserror" 698 | version = "1.0.69" 699 | source = "registry+https://github.com/rust-lang/crates.io-index" 700 | checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" 701 | dependencies = [ 702 | "thiserror-impl 1.0.69", 703 | ] 704 | 705 | [[package]] 706 | name = "thiserror" 707 | version = "2.0.14" 708 | source = "registry+https://github.com/rust-lang/crates.io-index" 709 | checksum = "0b0949c3a6c842cbde3f1686d6eea5a010516deb7085f79db747562d4102f41e" 710 | dependencies = [ 711 | "thiserror-impl 2.0.14", 712 | ] 713 | 714 | [[package]] 715 | name = "thiserror-impl" 716 | version = "1.0.69" 717 | source = "registry+https://github.com/rust-lang/crates.io-index" 718 | checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" 719 | dependencies = [ 720 | "proc-macro2", 721 | "quote", 722 | "syn", 723 | ] 724 | 725 | [[package]] 726 | name = "thiserror-impl" 727 | version = "2.0.14" 728 | source = "registry+https://github.com/rust-lang/crates.io-index" 729 | checksum = "cc5b44b4ab9c2fdd0e0512e6bece8388e214c0749f5862b114cc5b7a25daf227" 730 | dependencies = [ 731 | "proc-macro2", 732 | "quote", 733 | "syn", 734 | ] 735 | 736 | [[package]] 737 | name = "tinystr" 738 | version = "0.8.1" 739 | source = "registry+https://github.com/rust-lang/crates.io-index" 740 | checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" 741 | dependencies = [ 742 | "displaydoc", 743 | "zerovec", 744 | ] 745 | 746 | [[package]] 747 | name = "toml" 748 | version = "0.9.5" 749 | source = "registry+https://github.com/rust-lang/crates.io-index" 750 | checksum = "75129e1dc5000bfbaa9fee9d1b21f974f9fbad9daec557a521ee6e080825f6e8" 751 | dependencies = [ 752 | "indexmap", 753 | "serde", 754 | "serde_spanned", 755 | "toml_datetime", 756 | "toml_parser", 757 | "toml_writer", 758 | "winnow", 759 | ] 760 | 761 | [[package]] 762 | name = "toml_datetime" 763 | version = "0.7.0" 764 | source = "registry+https://github.com/rust-lang/crates.io-index" 765 | checksum = "bade1c3e902f58d73d3f294cd7f20391c1cb2fbcb643b73566bc773971df91e3" 766 | dependencies = [ 767 | "serde", 768 | ] 769 | 770 | [[package]] 771 | name = "toml_parser" 772 | version = "1.0.2" 773 | source = "registry+https://github.com/rust-lang/crates.io-index" 774 | checksum = "b551886f449aa90d4fe2bdaa9f4a2577ad2dde302c61ecf262d80b116db95c10" 775 | dependencies = [ 776 | "winnow", 777 | ] 778 | 779 | [[package]] 780 | name = "toml_writer" 781 | version = "1.0.2" 782 | source = "registry+https://github.com/rust-lang/crates.io-index" 783 | checksum = "fcc842091f2def52017664b53082ecbbeb5c7731092bad69d2c63050401dfd64" 784 | 785 | [[package]] 786 | name = "typenum" 787 | version = "1.18.0" 788 | source = "registry+https://github.com/rust-lang/crates.io-index" 789 | checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" 790 | 791 | [[package]] 792 | name = "unicode-ident" 793 | version = "1.0.18" 794 | source = "registry+https://github.com/rust-lang/crates.io-index" 795 | checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" 796 | 797 | [[package]] 798 | name = "url" 799 | version = "2.5.4" 800 | source = "registry+https://github.com/rust-lang/crates.io-index" 801 | checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" 802 | dependencies = [ 803 | "form_urlencoded", 804 | "idna", 805 | "percent-encoding", 806 | ] 807 | 808 | [[package]] 809 | name = "utf8_iter" 810 | version = "1.0.4" 811 | source = "registry+https://github.com/rust-lang/crates.io-index" 812 | checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" 813 | 814 | [[package]] 815 | name = "vcpkg" 816 | version = "0.2.15" 817 | source = "registry+https://github.com/rust-lang/crates.io-index" 818 | checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" 819 | 820 | [[package]] 821 | name = "version_check" 822 | version = "0.9.5" 823 | source = "registry+https://github.com/rust-lang/crates.io-index" 824 | checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" 825 | 826 | [[package]] 827 | name = "walkdir" 828 | version = "2.5.0" 829 | source = "registry+https://github.com/rust-lang/crates.io-index" 830 | checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" 831 | dependencies = [ 832 | "same-file", 833 | "winapi-util", 834 | ] 835 | 836 | [[package]] 837 | name = "wasi" 838 | version = "0.11.1+wasi-snapshot-preview1" 839 | source = "registry+https://github.com/rust-lang/crates.io-index" 840 | checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" 841 | 842 | [[package]] 843 | name = "wasm-bindgen" 844 | version = "0.2.100" 845 | source = "registry+https://github.com/rust-lang/crates.io-index" 846 | checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" 847 | dependencies = [ 848 | "cfg-if", 849 | "once_cell", 850 | "rustversion", 851 | "wasm-bindgen-macro", 852 | ] 853 | 854 | [[package]] 855 | name = "wasm-bindgen-backend" 856 | version = "0.2.100" 857 | source = "registry+https://github.com/rust-lang/crates.io-index" 858 | checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" 859 | dependencies = [ 860 | "bumpalo", 861 | "log", 862 | "proc-macro2", 863 | "quote", 864 | "syn", 865 | "wasm-bindgen-shared", 866 | ] 867 | 868 | [[package]] 869 | name = "wasm-bindgen-macro" 870 | version = "0.2.100" 871 | source = "registry+https://github.com/rust-lang/crates.io-index" 872 | checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" 873 | dependencies = [ 874 | "quote", 875 | "wasm-bindgen-macro-support", 876 | ] 877 | 878 | [[package]] 879 | name = "wasm-bindgen-macro-support" 880 | version = "0.2.100" 881 | source = "registry+https://github.com/rust-lang/crates.io-index" 882 | checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" 883 | dependencies = [ 884 | "proc-macro2", 885 | "quote", 886 | "syn", 887 | "wasm-bindgen-backend", 888 | "wasm-bindgen-shared", 889 | ] 890 | 891 | [[package]] 892 | name = "wasm-bindgen-shared" 893 | version = "0.2.100" 894 | source = "registry+https://github.com/rust-lang/crates.io-index" 895 | checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" 896 | dependencies = [ 897 | "unicode-ident", 898 | ] 899 | 900 | [[package]] 901 | name = "web-sys" 902 | version = "0.3.77" 903 | source = "registry+https://github.com/rust-lang/crates.io-index" 904 | checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" 905 | dependencies = [ 906 | "js-sys", 907 | "wasm-bindgen", 908 | ] 909 | 910 | [[package]] 911 | name = "webbrowser" 912 | version = "1.0.5" 913 | source = "registry+https://github.com/rust-lang/crates.io-index" 914 | checksum = "aaf4f3c0ba838e82b4e5ccc4157003fb8c324ee24c058470ffb82820becbde98" 915 | dependencies = [ 916 | "core-foundation", 917 | "jni", 918 | "log", 919 | "ndk-context", 920 | "objc2", 921 | "objc2-foundation", 922 | "url", 923 | "web-sys", 924 | ] 925 | 926 | [[package]] 927 | name = "winapi-util" 928 | version = "0.1.9" 929 | source = "registry+https://github.com/rust-lang/crates.io-index" 930 | checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" 931 | dependencies = [ 932 | "windows-sys 0.59.0", 933 | ] 934 | 935 | [[package]] 936 | name = "windows-core" 937 | version = "0.61.2" 938 | source = "registry+https://github.com/rust-lang/crates.io-index" 939 | checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" 940 | dependencies = [ 941 | "windows-implement", 942 | "windows-interface", 943 | "windows-link", 944 | "windows-result", 945 | "windows-strings", 946 | ] 947 | 948 | [[package]] 949 | name = "windows-implement" 950 | version = "0.60.0" 951 | source = "registry+https://github.com/rust-lang/crates.io-index" 952 | checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" 953 | dependencies = [ 954 | "proc-macro2", 955 | "quote", 956 | "syn", 957 | ] 958 | 959 | [[package]] 960 | name = "windows-interface" 961 | version = "0.59.1" 962 | source = "registry+https://github.com/rust-lang/crates.io-index" 963 | checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" 964 | dependencies = [ 965 | "proc-macro2", 966 | "quote", 967 | "syn", 968 | ] 969 | 970 | [[package]] 971 | name = "windows-link" 972 | version = "0.1.3" 973 | source = "registry+https://github.com/rust-lang/crates.io-index" 974 | checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" 975 | 976 | [[package]] 977 | name = "windows-result" 978 | version = "0.3.4" 979 | source = "registry+https://github.com/rust-lang/crates.io-index" 980 | checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" 981 | dependencies = [ 982 | "windows-link", 983 | ] 984 | 985 | [[package]] 986 | name = "windows-strings" 987 | version = "0.4.2" 988 | source = "registry+https://github.com/rust-lang/crates.io-index" 989 | checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" 990 | dependencies = [ 991 | "windows-link", 992 | ] 993 | 994 | [[package]] 995 | name = "windows-sys" 996 | version = "0.45.0" 997 | source = "registry+https://github.com/rust-lang/crates.io-index" 998 | checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" 999 | dependencies = [ 1000 | "windows-targets 0.42.2", 1001 | ] 1002 | 1003 | [[package]] 1004 | name = "windows-sys" 1005 | version = "0.59.0" 1006 | source = "registry+https://github.com/rust-lang/crates.io-index" 1007 | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 1008 | dependencies = [ 1009 | "windows-targets 0.52.6", 1010 | ] 1011 | 1012 | [[package]] 1013 | name = "windows-targets" 1014 | version = "0.42.2" 1015 | source = "registry+https://github.com/rust-lang/crates.io-index" 1016 | checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" 1017 | dependencies = [ 1018 | "windows_aarch64_gnullvm 0.42.2", 1019 | "windows_aarch64_msvc 0.42.2", 1020 | "windows_i686_gnu 0.42.2", 1021 | "windows_i686_msvc 0.42.2", 1022 | "windows_x86_64_gnu 0.42.2", 1023 | "windows_x86_64_gnullvm 0.42.2", 1024 | "windows_x86_64_msvc 0.42.2", 1025 | ] 1026 | 1027 | [[package]] 1028 | name = "windows-targets" 1029 | version = "0.52.6" 1030 | source = "registry+https://github.com/rust-lang/crates.io-index" 1031 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 1032 | dependencies = [ 1033 | "windows_aarch64_gnullvm 0.52.6", 1034 | "windows_aarch64_msvc 0.52.6", 1035 | "windows_i686_gnu 0.52.6", 1036 | "windows_i686_gnullvm", 1037 | "windows_i686_msvc 0.52.6", 1038 | "windows_x86_64_gnu 0.52.6", 1039 | "windows_x86_64_gnullvm 0.52.6", 1040 | "windows_x86_64_msvc 0.52.6", 1041 | ] 1042 | 1043 | [[package]] 1044 | name = "windows_aarch64_gnullvm" 1045 | version = "0.42.2" 1046 | source = "registry+https://github.com/rust-lang/crates.io-index" 1047 | checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" 1048 | 1049 | [[package]] 1050 | name = "windows_aarch64_gnullvm" 1051 | version = "0.52.6" 1052 | source = "registry+https://github.com/rust-lang/crates.io-index" 1053 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 1054 | 1055 | [[package]] 1056 | name = "windows_aarch64_msvc" 1057 | version = "0.42.2" 1058 | source = "registry+https://github.com/rust-lang/crates.io-index" 1059 | checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" 1060 | 1061 | [[package]] 1062 | name = "windows_aarch64_msvc" 1063 | version = "0.52.6" 1064 | source = "registry+https://github.com/rust-lang/crates.io-index" 1065 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 1066 | 1067 | [[package]] 1068 | name = "windows_i686_gnu" 1069 | version = "0.42.2" 1070 | source = "registry+https://github.com/rust-lang/crates.io-index" 1071 | checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" 1072 | 1073 | [[package]] 1074 | name = "windows_i686_gnu" 1075 | version = "0.52.6" 1076 | source = "registry+https://github.com/rust-lang/crates.io-index" 1077 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 1078 | 1079 | [[package]] 1080 | name = "windows_i686_gnullvm" 1081 | version = "0.52.6" 1082 | source = "registry+https://github.com/rust-lang/crates.io-index" 1083 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 1084 | 1085 | [[package]] 1086 | name = "windows_i686_msvc" 1087 | version = "0.42.2" 1088 | source = "registry+https://github.com/rust-lang/crates.io-index" 1089 | checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" 1090 | 1091 | [[package]] 1092 | name = "windows_i686_msvc" 1093 | version = "0.52.6" 1094 | source = "registry+https://github.com/rust-lang/crates.io-index" 1095 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 1096 | 1097 | [[package]] 1098 | name = "windows_x86_64_gnu" 1099 | version = "0.42.2" 1100 | source = "registry+https://github.com/rust-lang/crates.io-index" 1101 | checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" 1102 | 1103 | [[package]] 1104 | name = "windows_x86_64_gnu" 1105 | version = "0.52.6" 1106 | source = "registry+https://github.com/rust-lang/crates.io-index" 1107 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 1108 | 1109 | [[package]] 1110 | name = "windows_x86_64_gnullvm" 1111 | version = "0.42.2" 1112 | source = "registry+https://github.com/rust-lang/crates.io-index" 1113 | checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" 1114 | 1115 | [[package]] 1116 | name = "windows_x86_64_gnullvm" 1117 | version = "0.52.6" 1118 | source = "registry+https://github.com/rust-lang/crates.io-index" 1119 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 1120 | 1121 | [[package]] 1122 | name = "windows_x86_64_msvc" 1123 | version = "0.42.2" 1124 | source = "registry+https://github.com/rust-lang/crates.io-index" 1125 | checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" 1126 | 1127 | [[package]] 1128 | name = "windows_x86_64_msvc" 1129 | version = "0.52.6" 1130 | source = "registry+https://github.com/rust-lang/crates.io-index" 1131 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 1132 | 1133 | [[package]] 1134 | name = "winnow" 1135 | version = "0.7.12" 1136 | source = "registry+https://github.com/rust-lang/crates.io-index" 1137 | checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95" 1138 | 1139 | [[package]] 1140 | name = "writeable" 1141 | version = "0.6.1" 1142 | source = "registry+https://github.com/rust-lang/crates.io-index" 1143 | checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" 1144 | 1145 | [[package]] 1146 | name = "yoke" 1147 | version = "0.8.0" 1148 | source = "registry+https://github.com/rust-lang/crates.io-index" 1149 | checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" 1150 | dependencies = [ 1151 | "serde", 1152 | "stable_deref_trait", 1153 | "yoke-derive", 1154 | "zerofrom", 1155 | ] 1156 | 1157 | [[package]] 1158 | name = "yoke-derive" 1159 | version = "0.8.0" 1160 | source = "registry+https://github.com/rust-lang/crates.io-index" 1161 | checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" 1162 | dependencies = [ 1163 | "proc-macro2", 1164 | "quote", 1165 | "syn", 1166 | "synstructure", 1167 | ] 1168 | 1169 | [[package]] 1170 | name = "zerofrom" 1171 | version = "0.1.6" 1172 | source = "registry+https://github.com/rust-lang/crates.io-index" 1173 | checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" 1174 | dependencies = [ 1175 | "zerofrom-derive", 1176 | ] 1177 | 1178 | [[package]] 1179 | name = "zerofrom-derive" 1180 | version = "0.1.6" 1181 | source = "registry+https://github.com/rust-lang/crates.io-index" 1182 | checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" 1183 | dependencies = [ 1184 | "proc-macro2", 1185 | "quote", 1186 | "syn", 1187 | "synstructure", 1188 | ] 1189 | 1190 | [[package]] 1191 | name = "zerotrie" 1192 | version = "0.2.2" 1193 | source = "registry+https://github.com/rust-lang/crates.io-index" 1194 | checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" 1195 | dependencies = [ 1196 | "displaydoc", 1197 | "yoke", 1198 | "zerofrom", 1199 | ] 1200 | 1201 | [[package]] 1202 | name = "zerovec" 1203 | version = "0.11.4" 1204 | source = "registry+https://github.com/rust-lang/crates.io-index" 1205 | checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" 1206 | dependencies = [ 1207 | "yoke", 1208 | "zerofrom", 1209 | "zerovec-derive", 1210 | ] 1211 | 1212 | [[package]] 1213 | name = "zerovec-derive" 1214 | version = "0.11.1" 1215 | source = "registry+https://github.com/rust-lang/crates.io-index" 1216 | checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" 1217 | dependencies = [ 1218 | "proc-macro2", 1219 | "quote", 1220 | "syn", 1221 | ] 1222 | --------------------------------------------------------------------------------