├── frontend ├── .npmrc ├── src │ ├── routes │ │ ├── device │ │ │ └── +page.svelte │ │ ├── settings │ │ │ ├── sensors │ │ │ │ └── +page.svelte │ │ │ ├── +layout.svelte │ │ │ └── +page.svelte │ │ ├── now-playing │ │ │ └── +page.svelte │ │ ├── +layout.ts │ │ ├── theme │ │ │ ├── +page.ts │ │ │ └── +page.svelte │ │ ├── +page.svelte │ │ └── +layout.svelte │ ├── lib │ │ ├── index.ts │ │ ├── assets │ │ │ ├── 128x128.png │ │ │ ├── 32x32.png │ │ │ └── about_card.jpg │ │ ├── types │ │ │ └── toastEvent.ts │ │ ├── components │ │ │ ├── header │ │ │ │ ├── Controls │ │ │ │ │ ├── Close.svelte │ │ │ │ │ ├── Minimise.svelte │ │ │ │ │ ├── ExitApp.svelte │ │ │ │ │ └── IconButton.svelte │ │ │ │ ├── AppHeader.svelte │ │ │ │ └── WindowControls.svelte │ │ │ ├── ui │ │ │ │ └── IconButton.svelte │ │ │ ├── shell │ │ │ │ └── Shell.svelte │ │ │ ├── appNav │ │ │ │ └── AppNav.svelte │ │ │ └── themes │ │ │ │ ├── ThemeCard.svelte │ │ │ │ └── ThemeControls.svelte │ │ └── stores │ │ │ └── installedThemes.ts │ ├── app.postcss │ ├── index.test.ts │ ├── app.d.ts │ └── app.html ├── static │ └── favicon.png ├── .gitignore ├── .eslintignore ├── .prettierignore ├── .prettierrc ├── tests │ └── test.ts ├── playwright.config.ts ├── postcss.config.cjs ├── tsconfig.json ├── tailwind.config.ts ├── .eslintrc.cjs ├── svelte.config.js ├── README.md ├── package.json ├── vite.config.ts ├── .vscode │ └── settings.json └── theme.ts ├── crates ├── ultralight │ ├── src │ │ ├── types │ │ │ ├── gpu │ │ │ │ ├── texture.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── index_buffer.rs │ │ │ │ ├── render_buffer.rs │ │ │ │ ├── shader_type.rs │ │ │ │ ├── ul_arr_macro.rs │ │ │ │ ├── gpu_command.rs │ │ │ │ ├── rect.rs │ │ │ │ ├── vertex_buffer.rs │ │ │ │ ├── gpu_state.rs │ │ │ │ └── worker_command.rs │ │ │ ├── surface │ │ │ │ ├── mod.rs │ │ │ │ └── impl.rs │ │ │ ├── bitmap │ │ │ │ ├── mod.rs │ │ │ │ ├── format.rs │ │ │ │ └── pixels.rs │ │ │ ├── face_winding.rs │ │ │ ├── mod.rs │ │ │ ├── font_hinting.rs │ │ │ ├── log_level.rs │ │ │ └── error.rs │ │ ├── platform │ │ │ ├── gpu │ │ │ │ ├── mod.rs │ │ │ │ ├── shaders.rs │ │ │ │ └── driver_trait.rs │ │ │ ├── tests.rs │ │ │ └── logger.rs │ │ ├── js │ │ │ ├── mod.rs │ │ │ ├── function.rs │ │ │ ├── internal_string.rs │ │ │ ├── value.rs │ │ │ ├── context.rs │ │ │ └── types.rs │ │ ├── view │ │ │ ├── mod.rs │ │ │ ├── guard.rs │ │ │ ├── load_future.rs │ │ │ └── tests.rs │ │ ├── renderer │ │ │ ├── mod.rs │ │ │ └── tests.rs │ │ ├── lib.rs │ │ └── string.rs │ ├── .gitignore │ ├── Cargo.toml │ ├── shaders │ │ └── vertex │ │ │ ├── v2f_c4f_t2f.hlsl │ │ │ └── v2f_c4f_t2f_d28f.hlsl │ └── build.rs ├── ultralight-build │ ├── .gitignore │ └── Cargo.toml ├── lcd-coolers │ ├── src │ │ ├── corsair │ │ │ ├── mod.rs │ │ │ └── h150i │ │ │ │ ├── mod.rs │ │ │ │ ├── constants.rs │ │ │ │ ├── counter.rs │ │ │ │ ├── creator.rs │ │ │ │ └── tests.rs │ │ ├── traits │ │ │ ├── mod.rs │ │ │ ├── device_creator.rs │ │ │ └── display_cooler.rs │ │ ├── finder │ │ │ ├── mod.rs │ │ │ └── create.rs │ │ ├── info │ │ │ └── mod.rs │ │ └── lib.rs │ └── Cargo.toml ├── librehardwaremonitor-rs │ ├── src │ │ ├── sensor │ │ │ ├── mod.rs │ │ │ └── impl.rs │ │ ├── hardware │ │ │ ├── mod.rs │ │ │ ├── iter │ │ │ │ ├── mod.rs │ │ │ │ ├── sensor.rs │ │ │ │ └── hardware.rs │ │ │ └── impl.rs │ │ ├── computer │ │ │ ├── mod.rs │ │ │ ├── iter.rs │ │ │ ├── impl.rs │ │ │ └── params.rs │ │ ├── types │ │ │ ├── mod.rs │ │ │ ├── error.rs │ │ │ ├── hardware_type.rs │ │ │ └── sensor_type.rs │ │ ├── lib.rs │ │ └── tests.rs │ ├── build.rs │ └── Cargo.toml ├── smol-static │ ├── src │ │ ├── lib.rs │ │ ├── filesystem.rs │ │ └── server.rs │ └── Cargo.toml ├── ultralight-sys │ ├── .gitignore │ ├── wrapped │ │ └── wrapper.h │ ├── Cargo.toml │ ├── src │ │ └── lib.rs │ └── build.rs ├── librehardwaremonitor-sys │ ├── build.rs │ ├── Cargo.toml │ └── csharp-project │ │ ├── LibreHardwareMonitorNative.csproj │ │ └── LibreHardwareMonitorNative.sln └── dotnetaot-build │ └── Cargo.toml ├── src ├── utils │ ├── app │ │ ├── mod.rs │ │ └── paths.rs │ ├── mod.rs │ └── themes │ │ ├── mod.rs │ │ ├── config.rs │ │ ├── manifest.rs │ │ └── paths.rs ├── services │ ├── discord │ │ ├── mod.rs │ │ ├── register.rs │ │ └── presence.rs │ ├── server │ │ └── mod.rs │ ├── app │ │ ├── main_window.rs │ │ ├── events.rs │ │ ├── tray.rs │ │ └── mod.rs │ ├── rendering │ │ ├── mod.rs │ │ ├── dispatch_sensors.rs │ │ ├── render_helpers.rs │ │ ├── setup.rs │ │ ├── message.rs │ │ ├── config.rs │ │ └── loop.rs │ ├── sensors │ │ ├── mod.rs │ │ └── message.rs │ ├── config │ │ └── mod.rs │ └── mod.rs ├── lifecycle │ ├── mod.rs │ ├── setup.rs │ └── exit.rs ├── themes │ ├── mod.rs │ ├── config.rs │ ├── now_playing.rs │ ├── validate.rs │ ├── play.rs │ └── get_all.rs └── main.rs ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── feature_request.md │ ├── device_request.md │ └── bug_report.md └── workflows │ └── sponsors.yml ├── .cargo └── config.toml ├── icons ├── 32x32.png ├── icon.icns ├── icon.ico ├── icon.png ├── 128x128.png ├── 128x128@2x.png ├── StoreLogo.png ├── installer.ico ├── installer.png ├── Square30x30Logo.png ├── Square44x44Logo.png ├── Square71x71Logo.png ├── Square89x89Logo.png ├── Square107x107Logo.png ├── Square142x142Logo.png ├── Square150x150Logo.png ├── Square284x284Logo.png ├── Square310x310Logo.png ├── installer-header.bmp ├── installer-header.png ├── installer-sidebar.bmp └── installer-sidebar.png ├── resources ├── cat.png └── default.html ├── rust-toolchain.toml ├── .gitignore ├── .taurignore ├── .vscode ├── settings.json ├── tasks.json └── launch.json ├── manifest.xml ├── Cargo.toml └── tauri.conf.json /frontend/.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true 2 | -------------------------------------------------------------------------------- /crates/ultralight/src/types/gpu/texture.rs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/src/routes/device/+page.svelte: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/utils/app/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod paths; 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [brunostjohn] 2 | -------------------------------------------------------------------------------- /crates/ultralight-build/.gitignore: -------------------------------------------------------------------------------- 1 | /ultralight-deps -------------------------------------------------------------------------------- /frontend/src/routes/settings/sensors/+page.svelte: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/utils/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod app; 2 | pub mod themes; 3 | -------------------------------------------------------------------------------- /crates/lcd-coolers/src/corsair/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod h150i; 2 | -------------------------------------------------------------------------------- /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [env] 2 | AOT_COMPRESSION_REMOVE_BROTLI = "TRUE" -------------------------------------------------------------------------------- /crates/ultralight/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock 3 | /logs.txt -------------------------------------------------------------------------------- /src/services/discord/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod presence; 2 | pub mod register; 3 | -------------------------------------------------------------------------------- /crates/ultralight/src/types/surface/mod.rs: -------------------------------------------------------------------------------- 1 | mod r#impl; 2 | pub use r#impl::*; 3 | -------------------------------------------------------------------------------- /crates/librehardwaremonitor-rs/src/sensor/mod.rs: -------------------------------------------------------------------------------- 1 | mod r#impl; 2 | pub use r#impl::*; 3 | -------------------------------------------------------------------------------- /src/utils/themes/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod config; 2 | pub mod manifest; 3 | pub mod paths; 4 | -------------------------------------------------------------------------------- /crates/lcd-coolers/src/traits/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod device_creator; 2 | pub mod display_cooler; 3 | -------------------------------------------------------------------------------- /frontend/src/routes/now-playing/+page.svelte: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /crates/smol-static/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod filesystem; 2 | pub mod server; 3 | 4 | pub use server::*; 5 | -------------------------------------------------------------------------------- /crates/ultralight/src/platform/gpu/mod.rs: -------------------------------------------------------------------------------- 1 | mod driver_trait; 2 | mod receiver; 3 | mod shaders; 4 | -------------------------------------------------------------------------------- /frontend/src/routes/+layout.ts: -------------------------------------------------------------------------------- 1 | export const prerender = true; 2 | export const ssr = false; 3 | -------------------------------------------------------------------------------- /icons/32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brunostjohn/zefirs-flashy-cooler/HEAD/icons/32x32.png -------------------------------------------------------------------------------- /icons/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brunostjohn/zefirs-flashy-cooler/HEAD/icons/icon.icns -------------------------------------------------------------------------------- /icons/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brunostjohn/zefirs-flashy-cooler/HEAD/icons/icon.ico -------------------------------------------------------------------------------- /icons/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brunostjohn/zefirs-flashy-cooler/HEAD/icons/icon.png -------------------------------------------------------------------------------- /crates/lcd-coolers/src/finder/mod.rs: -------------------------------------------------------------------------------- 1 | mod create; 2 | 3 | pub use create::find_and_create_device; 4 | -------------------------------------------------------------------------------- /crates/librehardwaremonitor-rs/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | dotnetaot_build::use_aot_lib(); 3 | } 4 | -------------------------------------------------------------------------------- /crates/ultralight-sys/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock 3 | /wrapped/ultralight 4 | /ultralight-deps -------------------------------------------------------------------------------- /crates/ultralight-sys/wrapped/wrapper.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include "ultralight/AppCore/CAPI.h" -------------------------------------------------------------------------------- /icons/128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brunostjohn/zefirs-flashy-cooler/HEAD/icons/128x128.png -------------------------------------------------------------------------------- /resources/cat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brunostjohn/zefirs-flashy-cooler/HEAD/resources/cat.png -------------------------------------------------------------------------------- /src/lifecycle/mod.rs: -------------------------------------------------------------------------------- 1 | mod exit; 2 | mod setup; 3 | 4 | pub use exit::*; 5 | pub use setup::setup; 6 | -------------------------------------------------------------------------------- /frontend/src/lib/index.ts: -------------------------------------------------------------------------------- 1 | // place files you want to import through the `$lib` alias in this folder. 2 | -------------------------------------------------------------------------------- /icons/128x128@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brunostjohn/zefirs-flashy-cooler/HEAD/icons/128x128@2x.png -------------------------------------------------------------------------------- /icons/StoreLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brunostjohn/zefirs-flashy-cooler/HEAD/icons/StoreLogo.png -------------------------------------------------------------------------------- /icons/installer.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brunostjohn/zefirs-flashy-cooler/HEAD/icons/installer.ico -------------------------------------------------------------------------------- /icons/installer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brunostjohn/zefirs-flashy-cooler/HEAD/icons/installer.png -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "beta" 3 | components = ["clippy", "rustfmt", "rustc", "cargo"] -------------------------------------------------------------------------------- /icons/Square30x30Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brunostjohn/zefirs-flashy-cooler/HEAD/icons/Square30x30Logo.png -------------------------------------------------------------------------------- /icons/Square44x44Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brunostjohn/zefirs-flashy-cooler/HEAD/icons/Square44x44Logo.png -------------------------------------------------------------------------------- /icons/Square71x71Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brunostjohn/zefirs-flashy-cooler/HEAD/icons/Square71x71Logo.png -------------------------------------------------------------------------------- /icons/Square89x89Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brunostjohn/zefirs-flashy-cooler/HEAD/icons/Square89x89Logo.png -------------------------------------------------------------------------------- /frontend/src/app.postcss: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | @tailwind variants; 5 | -------------------------------------------------------------------------------- /frontend/static/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brunostjohn/zefirs-flashy-cooler/HEAD/frontend/static/favicon.png -------------------------------------------------------------------------------- /icons/Square107x107Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brunostjohn/zefirs-flashy-cooler/HEAD/icons/Square107x107Logo.png -------------------------------------------------------------------------------- /icons/Square142x142Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brunostjohn/zefirs-flashy-cooler/HEAD/icons/Square142x142Logo.png -------------------------------------------------------------------------------- /icons/Square150x150Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brunostjohn/zefirs-flashy-cooler/HEAD/icons/Square150x150Logo.png -------------------------------------------------------------------------------- /icons/Square284x284Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brunostjohn/zefirs-flashy-cooler/HEAD/icons/Square284x284Logo.png -------------------------------------------------------------------------------- /icons/Square310x310Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brunostjohn/zefirs-flashy-cooler/HEAD/icons/Square310x310Logo.png -------------------------------------------------------------------------------- /icons/installer-header.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brunostjohn/zefirs-flashy-cooler/HEAD/icons/installer-header.bmp -------------------------------------------------------------------------------- /icons/installer-header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brunostjohn/zefirs-flashy-cooler/HEAD/icons/installer-header.png -------------------------------------------------------------------------------- /icons/installer-sidebar.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brunostjohn/zefirs-flashy-cooler/HEAD/icons/installer-sidebar.bmp -------------------------------------------------------------------------------- /icons/installer-sidebar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brunostjohn/zefirs-flashy-cooler/HEAD/icons/installer-sidebar.png -------------------------------------------------------------------------------- /src/themes/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod config; 2 | pub mod get_all; 3 | pub mod now_playing; 4 | pub mod play; 5 | pub mod validate; 6 | -------------------------------------------------------------------------------- /crates/librehardwaremonitor-rs/src/hardware/mod.rs: -------------------------------------------------------------------------------- 1 | mod r#impl; 2 | mod iter; 3 | 4 | pub use iter::*; 5 | pub use r#impl::*; 6 | -------------------------------------------------------------------------------- /crates/librehardwaremonitor-sys/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | dotnetaot_build::build("LibreHardwareMonitorNative", None); 3 | } 4 | -------------------------------------------------------------------------------- /frontend/src/lib/assets/128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brunostjohn/zefirs-flashy-cooler/HEAD/frontend/src/lib/assets/128x128.png -------------------------------------------------------------------------------- /frontend/src/lib/assets/32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brunostjohn/zefirs-flashy-cooler/HEAD/frontend/src/lib/assets/32x32.png -------------------------------------------------------------------------------- /crates/librehardwaremonitor-rs/src/hardware/iter/mod.rs: -------------------------------------------------------------------------------- 1 | mod hardware; 2 | mod sensor; 3 | 4 | pub use hardware::*; 5 | pub use sensor::*; 6 | -------------------------------------------------------------------------------- /frontend/src/lib/assets/about_card.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brunostjohn/zefirs-flashy-cooler/HEAD/frontend/src/lib/assets/about_card.jpg -------------------------------------------------------------------------------- /crates/ultralight/src/js/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod context; 2 | pub mod function; 3 | mod internal_string; 4 | pub mod object; 5 | pub mod types; 6 | pub mod value; 7 | -------------------------------------------------------------------------------- /crates/lcd-coolers/src/corsair/h150i/mod.rs: -------------------------------------------------------------------------------- 1 | mod constants; 2 | mod counter; 3 | pub(crate) mod creator; 4 | pub mod device; 5 | 6 | #[cfg(test)] 7 | mod tests; 8 | -------------------------------------------------------------------------------- /crates/librehardwaremonitor-rs/src/computer/mod.rs: -------------------------------------------------------------------------------- 1 | mod r#impl; 2 | mod iter; 3 | mod params; 4 | 5 | pub use iter::*; 6 | pub use params::*; 7 | pub use r#impl::*; 8 | -------------------------------------------------------------------------------- /frontend/src/lib/types/toastEvent.ts: -------------------------------------------------------------------------------- 1 | export interface ToastEvent { 2 | message: string; 3 | autohide?: boolean; 4 | timeout?: number; 5 | background?: string; 6 | } 7 | -------------------------------------------------------------------------------- /crates/lcd-coolers/src/info/mod.rs: -------------------------------------------------------------------------------- 1 | pub struct DeviceInfo { 2 | pub name: String, 3 | pub width: u32, 4 | pub height: u32, 5 | pub manufacturer: String, 6 | } 7 | -------------------------------------------------------------------------------- /crates/ultralight/src/view/mod.rs: -------------------------------------------------------------------------------- 1 | mod builder; 2 | mod guard; 3 | mod r#impl; 4 | mod load_future; 5 | 6 | pub use builder::*; 7 | pub use r#impl::*; 8 | 9 | #[cfg(test)] 10 | mod tests; 11 | -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env 7 | .env.* 8 | !.env.example 9 | vite.config.js.timestamp-* 10 | vite.config.ts.timestamp-* 11 | -------------------------------------------------------------------------------- /frontend/src/index.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, it, expect } from "vitest"; 2 | 3 | describe("sum test", () => { 4 | it("adds 1 + 2 to equal 3", () => { 5 | expect(1 + 2).toBe(3); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /src/utils/app/paths.rs: -------------------------------------------------------------------------------- 1 | use std::path::PathBuf; 2 | 3 | pub fn get_app_path() -> anyhow::Result { 4 | let mut path = std::env::current_exe()?; 5 | path.pop(); 6 | 7 | Ok(path) 8 | } 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock 3 | logs.txt 4 | /AppCore.dll 5 | /Ultralight.dll 6 | /UltralightCore.dll 7 | /WebCore.dll 8 | /resources/cacert.pem 9 | /resources/icudt67l.dat 10 | /ultralight-deps -------------------------------------------------------------------------------- /crates/librehardwaremonitor-sys/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "librehardwaremonitor-sys" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [build-dependencies] 7 | dotnetaot-build ={path="../dotnetaot-build"} 8 | -------------------------------------------------------------------------------- /src/themes/config.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::themes::config::MergedConfigItem; 2 | 3 | #[tauri::command] 4 | pub async fn get_theme_config(fs_name: String) -> Result { 5 | Err("Not implemented!".into()) 6 | } 7 | -------------------------------------------------------------------------------- /frontend/src/lib/components/header/Controls/Close.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /frontend/.eslintignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env 7 | .env.* 8 | !.env.example 9 | 10 | # Ignore files for PNPM, NPM and YARN 11 | pnpm-lock.yaml 12 | package-lock.json 13 | yarn.lock 14 | -------------------------------------------------------------------------------- /.taurignore: -------------------------------------------------------------------------------- 1 | /frontend/**/* 2 | /frontend/* 3 | !/frontend/package.json 4 | /frontend/node_modules/**/* 5 | /frontend/.svelte-kit/**/* 6 | /crates/ultralight-sys/wrapped/ultralight/**/* 7 | /crates/ultralight/shaders/**/* 8 | /crates/ultralight/shaders/* -------------------------------------------------------------------------------- /frontend/.prettierignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env 7 | .env.* 8 | !.env.example 9 | 10 | # Ignore files for PNPM, NPM and YARN 11 | pnpm-lock.yaml 12 | package-lock.json 13 | yarn.lock 14 | -------------------------------------------------------------------------------- /frontend/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "useTabs": true, 3 | "singleQuote": false, 4 | "trailingComma": "es5", 5 | "printWidth": 100, 6 | "plugins": ["prettier-plugin-svelte"], 7 | "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }] 8 | } 9 | -------------------------------------------------------------------------------- /crates/librehardwaremonitor-rs/src/types/mod.rs: -------------------------------------------------------------------------------- 1 | mod error; 2 | mod hardware_type; 3 | mod sensor_type; 4 | 5 | pub use error::*; 6 | pub use hardware_type::*; 7 | pub use sensor_type::*; 8 | 9 | pub type LibreResult = Result; 10 | -------------------------------------------------------------------------------- /frontend/src/lib/components/header/Controls/Minimise.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /frontend/tests/test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from "@playwright/test"; 2 | 3 | test("index page has expected h1", async ({ page }) => { 4 | await page.goto("/"); 5 | await expect(page.getByRole("heading", { name: "Welcome to SvelteKit" })).toBeVisible(); 6 | }); 7 | -------------------------------------------------------------------------------- /crates/librehardwaremonitor-rs/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod computer; 2 | pub mod hardware; 3 | pub mod sensor; 4 | pub mod types; 5 | 6 | pub use computer::*; 7 | pub use hardware::*; 8 | pub use sensor::*; 9 | pub use types::*; 10 | 11 | #[cfg(test)] 12 | mod tests; 13 | -------------------------------------------------------------------------------- /crates/lcd-coolers/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod corsair; 2 | mod finder; 3 | mod info; 4 | mod traits; 5 | 6 | pub use finder::find_and_create_device; 7 | pub use info::DeviceInfo; 8 | pub use traits::device_creator::DeviceCreator; 9 | pub use traits::display_cooler::DisplayCooler; 10 | -------------------------------------------------------------------------------- /frontend/src/lib/components/header/Controls/ExitApp.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /frontend/src/routes/theme/+page.ts: -------------------------------------------------------------------------------- 1 | import { error } from "@sveltejs/kit"; 2 | 3 | export const load = async ({ url: { searchParams } }) => { 4 | const fsName = searchParams.get("fsName"); 5 | if (!fsName) throw error(404, "Not found"); 6 | 7 | return { 8 | fsName, 9 | }; 10 | }; 11 | -------------------------------------------------------------------------------- /crates/lcd-coolers/src/traits/device_creator.rs: -------------------------------------------------------------------------------- 1 | use crate::DeviceInfo; 2 | 3 | #[allow(async_fn_in_trait)] 4 | pub trait DeviceCreator { 5 | async fn create_device() -> anyhow::Result 6 | where 7 | Self: Sized; 8 | async fn device_info(&self) -> DeviceInfo; 9 | } 10 | -------------------------------------------------------------------------------- /crates/dotnetaot-build/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dotnetaot-build" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | cc = "1.0.83" 10 | 11 | [build-dependencies] 12 | -------------------------------------------------------------------------------- /crates/ultralight/src/platform/tests.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[test] 4 | fn configures_platform() { 5 | ULPlatformBuilder::new() 6 | .enable_file_logger("./logs.txt") 7 | .enable_platform_file_system() 8 | .enable_platform_font_loader() 9 | .build(); 10 | } 11 | -------------------------------------------------------------------------------- /frontend/src/app.d.ts: -------------------------------------------------------------------------------- 1 | // See https://kit.svelte.dev/docs/types#app 2 | // for information about these interfaces 3 | // and what to do when importing types 4 | declare namespace App { 5 | // interface Locals {} 6 | // interface PageData {} 7 | // interface Error {} 8 | // interface Platform {} 9 | } 10 | -------------------------------------------------------------------------------- /crates/lcd-coolers/src/finder/create.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | corsair::h150i::device::CorsairH150i, 3 | traits::{device_creator::DeviceCreator, display_cooler::DisplayCooler}, 4 | }; 5 | 6 | pub async fn find_and_create_device() -> anyhow::Result { 7 | CorsairH150i::create_device().await 8 | } 9 | -------------------------------------------------------------------------------- /crates/librehardwaremonitor-rs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "librehardwaremonitor-rs" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | librehardwaremonitor-sys = { path = "../librehardwaremonitor-sys" } 8 | thiserror = "1.0.50" 9 | 10 | [build-dependencies] 11 | dotnetaot-build = {path="../dotnetaot-build"} -------------------------------------------------------------------------------- /resources/default.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /frontend/src/routes/+page.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 |
7 | {#each $themeStore as theme} 8 | 9 | {/each} 10 |
11 | -------------------------------------------------------------------------------- /crates/smol-static/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "smol-static" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | anyhow = "1.0.75" 8 | async-compression = { version = "0.4.5", features = ["tokio", "gzip"] } 9 | mime_guess = "2.0.4" 10 | tachyonix = "0.2.1" 11 | tokio = { version = "1.35.0", features = ["full"] } 12 | -------------------------------------------------------------------------------- /frontend/playwright.config.ts: -------------------------------------------------------------------------------- 1 | import type { PlaywrightTestConfig } from "@playwright/test"; 2 | 3 | const config: PlaywrightTestConfig = { 4 | webServer: { 5 | command: "npm run build && npm run preview", 6 | port: 4173, 7 | }, 8 | testDir: "tests", 9 | testMatch: /(.+\.)?(test|spec)\.[jt]s/, 10 | }; 11 | 12 | export default config; 13 | -------------------------------------------------------------------------------- /crates/lcd-coolers/src/corsair/h150i/constants.rs: -------------------------------------------------------------------------------- 1 | pub const VENDOR_ID: u16 = 0x1b1c; 2 | pub const PRODUCT_ID: u16 = 0x0c39; 3 | pub const PRODUCT_ID_V2: u16 = 0x0c33; 4 | pub const CONTROL_REQUEST: u8 = 0x03; 5 | pub const DEVICE_STAT: u8 = 0x1d; 6 | pub const DEVICE_ALIVE: u8 = 0x19; 7 | pub const SET_INTERFACE: u8 = 0x0b; 8 | pub const IMG_TX: u8 = 0x02; 9 | -------------------------------------------------------------------------------- /src/themes/now_playing.rs: -------------------------------------------------------------------------------- 1 | use crate::services::config::AppConfig; 2 | 3 | #[tauri::command] 4 | pub fn get_now_playing() -> Result { 5 | let config = AppConfig::load(); 6 | 7 | config 8 | .theme_path 9 | .and_then(|x| x.split('\\').last().map(|x| x.to_string())) 10 | .ok_or("No theme playing!".into()) 11 | } 12 | -------------------------------------------------------------------------------- /crates/ultralight/src/types/gpu/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod gpu_command; 2 | pub(crate) mod gpu_state; 3 | pub(crate) mod index_buffer; 4 | pub(crate) mod rect; 5 | pub(crate) mod render_buffer; 6 | pub(crate) mod shader_type; 7 | pub(crate) mod ul_arr_macro; 8 | pub(crate) mod vertex_buffer; 9 | pub(crate) mod worker_command; 10 | pub(crate) use ul_arr_macro::from_ul_arr; 11 | -------------------------------------------------------------------------------- /crates/librehardwaremonitor-rs/src/types/error.rs: -------------------------------------------------------------------------------- 1 | use thiserror::Error; 2 | 3 | #[derive(Debug, Error)] 4 | pub enum LibreError { 5 | #[error("The name provided is invalid.")] 6 | InvalidName, 7 | #[error("Failed to set the provided name.")] 8 | FailedToSetName, 9 | #[error("Failed to get the value of the sensor.")] 10 | FailedToGetSensorValue, 11 | } 12 | -------------------------------------------------------------------------------- /frontend/src/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | %sveltekit.head% 8 | 9 | 10 |
%sveltekit.body%
11 | 12 | 13 | -------------------------------------------------------------------------------- /crates/lcd-coolers/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lcd-coolers" 3 | version = "0.1.0" 4 | edition = "2021" 5 | rust-version = "1.75.0" 6 | 7 | [dependencies] 8 | anyhow = "1.0.75" 9 | hidapi = {git="https://github.com/ruabmbua/hidapi-rs.git", features=["windows-native"]} 10 | tokio = { version = "1.35.0", features = ["rt-multi-thread"] } 11 | turbojpeg = "0.5.4" 12 | 13 | [dev-dependencies] 14 | tokio = { version = "1.35.0", features = ["full"] } 15 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "liveServer.settings.port": 5501, 3 | "files.exclude": { 4 | "**/.git": true, 5 | "**/.svn": true, 6 | "**/.hg": true, 7 | "**/CVS": true, 8 | "**/.DS_Store": true, 9 | "**/Thumbs.db": true, 10 | "node_modules/": true, 11 | "build/": true, 12 | ".svelte-kit/": true, 13 | "*.dll": true, 14 | "target": true, 15 | ".idea/": true, 16 | "ultralight-deps": true 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /crates/ultralight/src/types/bitmap/mod.rs: -------------------------------------------------------------------------------- 1 | //! Utilities for working with bitmaps. 2 | //! 3 | //! # Example 4 | //! ```rust 5 | //! use ultralight::{Bitmap, BitmapFormat}; 6 | //! 7 | //! let bitmap = Bitmap::create_borrowed(100, 100, BitmapFormat::Bgra8UnormSrgb).unwrap(); 8 | //! assert_eq!(bitmap.width(), 100); 9 | //! ``` 10 | 11 | mod format; 12 | mod r#impl; 13 | mod pixels; 14 | 15 | pub use format::*; 16 | pub use pixels::*; 17 | pub use r#impl::*; 18 | -------------------------------------------------------------------------------- /crates/ultralight-build/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ultralight-build" 3 | version = "0.1.4" 4 | edition = "2021" 5 | license = "MIT" 6 | description = "A build tool helper for Ultralight crates." 7 | homepage = "https://github.com/brunostjohn/zefirs-flashy-cooler" 8 | include = ["/ultralight-deps/lib", "/ultralight-deps/include", "/src"] 9 | 10 | 11 | [dependencies] 12 | reqwest = { version = "0.11.22", features = ["blocking"] } 13 | sevenz-rust = "0.5.3" 14 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] 2 | 3 | use tokio::runtime::Handle; 4 | 5 | mod lifecycle; 6 | mod services; 7 | mod themes; 8 | mod utils; 9 | use crate::services::spawn_services; 10 | 11 | #[tokio::main] 12 | async fn main() { 13 | tauri::async_runtime::set(Handle::current()); 14 | 15 | let (local, renderer, server, sensors, app) = spawn_services().await; 16 | 17 | let _ = tokio::join!(local, renderer, server, sensors, app); 18 | } 19 | -------------------------------------------------------------------------------- /src/services/server/mod.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Error; 2 | use smol_static::{Server, ServerMessage}; 3 | use std::path::Path; 4 | use tachyonix::Sender; 5 | use tokio::task::JoinHandle; 6 | 7 | pub async fn spawn_server>( 8 | base_path: P, 9 | port: usize, 10 | ) -> (Sender, JoinHandle>) { 11 | let (sender, mut server) = Server::new(base_path); 12 | let handle = tokio::spawn(async move { server.run(port).await }); 13 | 14 | (sender, handle) 15 | } 16 | -------------------------------------------------------------------------------- /frontend/src/lib/components/ui/IconButton.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 20 | -------------------------------------------------------------------------------- /crates/lcd-coolers/src/traits/display_cooler.rs: -------------------------------------------------------------------------------- 1 | use std::future::Future; 2 | 3 | pub trait DisplayCooler: Sync + Send { 4 | fn initialise(&mut self) -> impl Future> + '_ + Sync + Send; 5 | fn close(&mut self) -> impl Future> + '_ + Sync + Send; 6 | fn reopen(&mut self) -> impl Future> + Sync + Send; 7 | fn send_image<'a, 'b>( 8 | &'a mut self, 9 | image: &'b [u8], 10 | ) -> impl Future> + Sync + Send; 11 | } 12 | -------------------------------------------------------------------------------- /crates/ultralight/src/types/face_winding.rs: -------------------------------------------------------------------------------- 1 | pub enum ULFaceWinding { 2 | CounterClockwise, 3 | Clockwise, 4 | } 5 | 6 | impl From for ultralight_sys::ULFaceWinding { 7 | fn from(face_winding: ULFaceWinding) -> Self { 8 | match face_winding { 9 | ULFaceWinding::CounterClockwise => { 10 | ultralight_sys::ULFaceWinding_kFaceWinding_CounterClockwise 11 | } 12 | ULFaceWinding::Clockwise => ultralight_sys::ULFaceWinding_kFaceWinding_Clockwise, 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /frontend/postcss.config.cjs: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | const autoprefixer = require("autoprefixer"); 3 | const tailwindcss = require("tailwindcss"); 4 | 5 | /** @type {import('tailwindcss').Config} */ 6 | module.exports = { 7 | darkMode: "class", 8 | content: [ 9 | "./src/**/*.{html,js,svelte,ts}", 10 | // 3. Append the path to the Skeleton package 11 | require("path").join(require.resolve("@skeletonlabs/skeleton"), "../**/*.{html,js,svelte,ts}"), 12 | ], 13 | theme: { 14 | extend: {}, 15 | }, 16 | plugins: [tailwindcss, autoprefixer], 17 | }; 18 | -------------------------------------------------------------------------------- /crates/ultralight/src/types/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod bitmap; 2 | pub(crate) mod error; 3 | pub(crate) mod face_winding; 4 | pub(crate) mod font_hinting; 5 | pub(crate) mod gpu; 6 | pub(crate) mod log_level; 7 | pub mod surface; 8 | 9 | pub type ULResult = Result; 10 | 11 | pub use bitmap::Bitmap; 12 | pub use bitmap::BitmapFormat; 13 | pub use error::ULError; 14 | pub use face_winding::ULFaceWinding; 15 | pub use font_hinting::ULFontHinting; 16 | pub use log_level::ULLogLevel; 17 | pub use surface::ULSurface; 18 | pub use surface::ULSurfaceBounds; 19 | -------------------------------------------------------------------------------- /src/services/app/main_window.rs: -------------------------------------------------------------------------------- 1 | use tauri::{AppHandle, WindowBuilder}; 2 | use tauri_plugin_window_state::{StateFlags, WindowExt}; 3 | use window_shadows::set_shadow; 4 | 5 | pub fn recreate_main_window(app: &AppHandle) { 6 | let window = 7 | WindowBuilder::from_config(app, app.config().tauri.windows.first().unwrap().clone()) 8 | .build() 9 | .expect("Failed to build window!"); 10 | 11 | let _ = window.restore_state(StateFlags::all()); 12 | 13 | set_shadow(&window, true).expect("Failed to set window shadows!"); 14 | } 15 | -------------------------------------------------------------------------------- /crates/ultralight/src/types/font_hinting.rs: -------------------------------------------------------------------------------- 1 | pub enum ULFontHinting { 2 | Smooth, 3 | Normal, 4 | Monochrome, 5 | } 6 | 7 | impl From for ultralight_sys::ULFontHinting { 8 | fn from(font_hinting: ULFontHinting) -> Self { 9 | match font_hinting { 10 | ULFontHinting::Smooth => ultralight_sys::ULFontHinting_kFontHinting_Smooth, 11 | ULFontHinting::Normal => ultralight_sys::ULFontHinting_kFontHinting_Normal, 12 | ULFontHinting::Monochrome => ultralight_sys::ULFontHinting_kFontHinting_Monochrome, 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /crates/ultralight-sys/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ultralight-sys" 3 | version = "0.1.1" 4 | edition = "2021" 5 | license = "MIT" 6 | description = "C-level bindings for the Ultralight rendering engine." 7 | homepage = "https://github.com/brunostjohn/zefirs-flashy-cooler" 8 | include = ["/ultralight-deps/lib", "/ultralight-deps/include", "/src", "/wrapped", "/build.rs"] 9 | 10 | [build-dependencies] 11 | bindgen = "0.69.1" 12 | reqwest = { version = "0.11.22", features = ["blocking"] } 13 | sevenz-rust = "0.5.3" 14 | ultralight-build = { path = "../ultralight-build", version = "0.1.4" } 15 | 16 | [dependencies] 17 | -------------------------------------------------------------------------------- /crates/ultralight/src/types/gpu/index_buffer.rs: -------------------------------------------------------------------------------- 1 | use std::slice; 2 | use ultralight_sys::ULIndexBuffer; 3 | 4 | #[derive(Debug)] 5 | pub struct IndexBuffer { 6 | pub buffer: Vec, 7 | } 8 | 9 | impl From for IndexBuffer { 10 | #[inline(always)] 11 | fn from(vb: ULIndexBuffer) -> Self { 12 | debug_assert!(vb.size % 4 == 0); 13 | debug_assert!(!vb.data.is_null()); 14 | let index_slice = unsafe { slice::from_raw_parts(vb.data as _, vb.size as usize / 4) }; 15 | IndexBuffer { 16 | buffer: index_slice.to_vec(), 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/utils/themes/config.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Serialize, Deserialize, Debug, Clone)] 4 | pub struct ThemeConfigItem { 5 | pub r#type: String, 6 | pub value: String, 7 | pub name: String, 8 | pub path: Option, 9 | } 10 | 11 | #[derive(Serialize, Deserialize, Debug, Clone)] 12 | pub struct MergedConfigItem { 13 | pub r#type: String, 14 | pub display_as: String, 15 | pub name: String, 16 | pub min: Option, 17 | pub max: Option, 18 | pub step: Option, 19 | pub default: Option, 20 | pub value: Option, 21 | } 22 | -------------------------------------------------------------------------------- /frontend/src/lib/components/header/AppHeader.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 |
10 | 11 |

Zefir's Flashy Cooler

12 | 13 |
14 | 15 | 22 | -------------------------------------------------------------------------------- /src/lifecycle/setup.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Context; 2 | use std::error::Error; 3 | use tauri::{App, Manager}; 4 | use window_shadows::set_shadow; 5 | 6 | use crate::services::config::AppConfig; 7 | 8 | pub fn setup(app: &mut App) -> Result<(), Box> { 9 | let window = app 10 | .get_window("main") 11 | .context("Failed to find main window!")?; 12 | 13 | set_shadow(&window, true).expect("Failed to set window shadow"); 14 | 15 | let AppConfig { 16 | start_minimised, .. 17 | } = AppConfig::load(); 18 | 19 | if start_minimised { 20 | let _ = window.close(); 21 | } 22 | 23 | Ok(()) 24 | } 25 | -------------------------------------------------------------------------------- /frontend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./.svelte-kit/tsconfig.json", 3 | "compilerOptions": { 4 | "allowJs": true, 5 | "checkJs": true, 6 | "esModuleInterop": true, 7 | "forceConsistentCasingInFileNames": true, 8 | "resolveJsonModule": true, 9 | "skipLibCheck": true, 10 | "sourceMap": true, 11 | "strict": true, 12 | "moduleResolution": "bundler" 13 | } 14 | // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias 15 | // 16 | // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes 17 | // from the referenced tsconfig.json - TypeScript does not merge them in 18 | } 19 | -------------------------------------------------------------------------------- /src/services/rendering/mod.rs: -------------------------------------------------------------------------------- 1 | mod config; 2 | mod dispatch_sensors; 3 | mod r#loop; 4 | pub mod message; 5 | mod render_helpers; 6 | mod setup; 7 | use self::message::RendererMessage; 8 | pub use self::r#loop::main_loop; 9 | use tachyonix::{Receiver, Sender}; 10 | use tokio::task::{JoinHandle, LocalSet}; 11 | 12 | use super::sensors::SensorMessage; 13 | 14 | pub fn spawn_renderer( 15 | local: &LocalSet, 16 | sensor_sender: Sender, 17 | receiver_renderer: Receiver, 18 | ) -> JoinHandle<()> { 19 | local.spawn_local(async move { 20 | main_loop(receiver_renderer, sensor_sender).await; 21 | }) 22 | } 23 | -------------------------------------------------------------------------------- /src/themes/validate.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::themes::paths::get_all_themes_path; 2 | use std::path::Path; 3 | use tokio::fs; 4 | 5 | async fn file_exists>(path: P) -> bool { 6 | fs::metadata(path).await.is_ok() 7 | } 8 | 9 | pub async fn validate_theme>(fs_name: S) -> bool { 10 | let theme_path = get_all_themes_path().join(fs_name.as_ref()); 11 | 12 | let (index, preview, theme) = tokio::join!( 13 | file_exists(theme_path.join("index.html")), 14 | file_exists(theme_path.join("preview.jpg")), 15 | file_exists(theme_path.join("theme.json")) 16 | ); 17 | 18 | index && preview && theme 19 | } 20 | -------------------------------------------------------------------------------- /frontend/src/lib/components/shell/Shell.svelte: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | {#key pathname} 18 |
19 | 20 |
21 | {/key} 22 | -------------------------------------------------------------------------------- /crates/ultralight/src/renderer/mod.rs: -------------------------------------------------------------------------------- 1 | //! The main way to interact with Ultralight. This module contains the main types and functions for interacting with the Ultralight renderer. 2 | //! 3 | //! # Example 4 | //! ```rust 5 | //! use ultralight::{platform::ULPlatformBuilder, renderer::ULRendererBuilder}; 6 | //! 7 | //! let platform = ULPlatformBuilder::new() 8 | //! .enable_platform_file_system() 9 | //! .enable_platform_font_loader() 10 | //! .build(); 11 | //! 12 | //! let renderer = ULRendererBuilder::new() 13 | //! .build(); 14 | //! ``` 15 | 16 | mod builder; 17 | mod r#impl; 18 | 19 | pub use builder::*; 20 | pub use r#impl::*; 21 | 22 | #[cfg(test)] 23 | mod tests; 24 | -------------------------------------------------------------------------------- /frontend/src/lib/stores/installedThemes.ts: -------------------------------------------------------------------------------- 1 | import { writable } from "svelte/store"; 2 | 3 | export type LCDDevice = "capellix" | "ttultra"; 4 | 5 | export interface Theme { 6 | description?: string; 7 | author?: string; 8 | name: string; 9 | fs_name: string; 10 | image_src: string; 11 | dls?: number; 12 | customisable_parameters: Parameter[]; 13 | tested_on: LCDDevice[]; 14 | version?: string; 15 | fileSizeKB?: number; 16 | } 17 | 18 | export interface Parameter { 19 | name: string; 20 | type: string; 21 | max: string; 22 | min: string; 23 | step: string; 24 | display_as: string; 25 | default: string; 26 | } 27 | 28 | export const themeStore = writable([]); 29 | -------------------------------------------------------------------------------- /src/utils/themes/manifest.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Serialize, Deserialize, Debug)] 4 | pub struct Parameter { 5 | pub r#type: String, 6 | pub display_as: String, 7 | pub name: String, 8 | pub min: Option, 9 | pub max: Option, 10 | pub step: Option, 11 | pub default: Option, 12 | } 13 | 14 | #[derive(Serialize, Deserialize, Debug)] 15 | pub struct Theme { 16 | pub name: String, 17 | pub author: String, 18 | pub description: String, 19 | pub version: String, 20 | pub fs_name: Option, 21 | pub tested_on: Option>, 22 | pub customisable_parameters: Vec, 23 | } 24 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "" 5 | labels: "" 6 | assignees: "" 7 | --- 8 | 9 | **Is your feature request related to a problem? Please describe.** 10 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 11 | 12 | **Describe the solution you'd like** 13 | A clear and concise description of what you want to happen. 14 | 15 | **Describe alternatives you've considered** 16 | A clear and concise description of any alternative solutions or features you've considered. 17 | 18 | **Additional context** 19 | Add any other context or screenshots about the feature request here. 20 | -------------------------------------------------------------------------------- /crates/ultralight/src/types/gpu/render_buffer.rs: -------------------------------------------------------------------------------- 1 | use ultralight_sys::ULRenderBuffer; 2 | 3 | #[derive(Debug)] 4 | pub struct RenderBuffer { 5 | pub texture_id: u32, 6 | pub width: u32, 7 | pub height: u32, 8 | pub has_stencil_buffer: bool, 9 | pub has_depth_buffer: bool, 10 | } 11 | 12 | impl From for RenderBuffer { 13 | #[inline(always)] 14 | fn from(rb: ULRenderBuffer) -> Self { 15 | RenderBuffer { 16 | texture_id: rb.texture_id, 17 | width: rb.width, 18 | height: rb.height, 19 | has_stencil_buffer: rb.has_stencil_buffer, 20 | has_depth_buffer: rb.has_depth_buffer, 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /crates/ultralight/src/platform/gpu/shaders.rs: -------------------------------------------------------------------------------- 1 | use wgpu::{include_spirv, ShaderModuleDescriptor}; 2 | 3 | pub(crate) fn get_fill_shader() -> ShaderModuleDescriptor<'static> { 4 | include_spirv!(concat!(env!("OUT_DIR"), "/fill.spv")) 5 | } 6 | 7 | pub(crate) fn get_fill_path_shader() -> ShaderModuleDescriptor<'static> { 8 | include_spirv!(concat!(env!("OUT_DIR"), "/fill_path.spv")) 9 | } 10 | 11 | pub(crate) fn get_v2f_c4f_t2f_shader() -> ShaderModuleDescriptor<'static> { 12 | include_spirv!(concat!(env!("OUT_DIR"), "/v2f_c4f_t2f.spv")) 13 | } 14 | 15 | pub(crate) fn get_v2f_c4f_t2f_d28f_shader() -> ShaderModuleDescriptor<'static> { 16 | include_spirv!(concat!(env!("OUT_DIR"), "/v2f_c4f_t2f_d28f.spv")) 17 | } 18 | -------------------------------------------------------------------------------- /frontend/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import { join } from "path"; 2 | import type { Config } from "tailwindcss"; 3 | import forms from "@tailwindcss/forms"; 4 | import typography from "@tailwindcss/typography"; 5 | import { skeleton } from "@skeletonlabs/tw-plugin"; 6 | import { zfcTheme } from "./theme.js"; 7 | 8 | export default { 9 | // jit: true, 10 | darkMode: "class", 11 | content: [ 12 | "./src/**/*.{html,js,svelte,ts}", 13 | join(require.resolve("@skeletonlabs/skeleton"), "../**/*.{html,js,svelte,ts}"), 14 | ], 15 | theme: { 16 | extend: {}, 17 | }, 18 | plugins: [ 19 | forms, 20 | typography, 21 | skeleton({ 22 | themes: { 23 | custom: [zfcTheme], 24 | }, 25 | }), 26 | ], 27 | } satisfies Config; 28 | -------------------------------------------------------------------------------- /crates/librehardwaremonitor-rs/src/computer/iter.rs: -------------------------------------------------------------------------------- 1 | use crate::{Computer, Hardware}; 2 | 3 | pub struct ComputerHardwareIter<'a> { 4 | pub(crate) inner: &'a Computer, 5 | pub(crate) index: usize, 6 | pub(crate) len: usize, 7 | } 8 | 9 | impl<'a> Iterator for ComputerHardwareIter<'a> { 10 | type Item = Hardware<'a>; 11 | 12 | fn next(&mut self) -> Option { 13 | if self.index < (self.len - 1) { 14 | let hardware = Hardware { 15 | guard: self.inner, 16 | indices: vec![self.index as i32], 17 | }; 18 | 19 | self.index += 1; 20 | 21 | Some(hardware) 22 | } else { 23 | None 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /frontend/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | /** @type { import("eslint").Linter.FlatConfig } */ 2 | module.exports = { 3 | root: true, 4 | extends: [ 5 | "eslint:recommended", 6 | "plugin:@typescript-eslint/recommended", 7 | "plugin:svelte/recommended", 8 | "prettier", 9 | ], 10 | parser: "@typescript-eslint/parser", 11 | plugins: ["@typescript-eslint"], 12 | parserOptions: { 13 | sourceType: "module", 14 | ecmaVersion: 2020, 15 | extraFileExtensions: [".svelte"], 16 | }, 17 | env: { 18 | browser: true, 19 | es2017: true, 20 | node: true, 21 | }, 22 | overrides: [ 23 | { 24 | files: ["*.svelte"], 25 | parser: "svelte-eslint-parser", 26 | parserOptions: { 27 | parser: "@typescript-eslint/parser", 28 | }, 29 | }, 30 | ], 31 | }; 32 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/device_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Device request 3 | about: Suggest a device to be added to the app 4 | title: "" 5 | labels: "" 6 | assignees: "" 7 | --- 8 | 9 | **Describe the device you'd like to be added** 10 | A clear and concise description of what the device is. 11 | 12 | **Paste a link to a capture of this device's communications** 13 | Use [this part of the documentation](https://zefirsflashycooler.app/docs/devices/reverse-engineering.html) to capture how the device communicates with your computer. 14 | 15 | **Paste a link to the device's product page** 16 | Please make sure that the link will send me to your exact device's product page. 17 | 18 | **Additional context** 19 | Add any other context or screenshots about the feature request here. 20 | -------------------------------------------------------------------------------- /crates/librehardwaremonitor-rs/src/hardware/iter/sensor.rs: -------------------------------------------------------------------------------- 1 | use crate::{Hardware, Sensor}; 2 | 3 | pub struct SensorIter<'a> { 4 | pub(crate) inner: &'a Hardware<'a>, 5 | pub(crate) index: usize, 6 | pub(crate) len: usize, 7 | } 8 | 9 | impl<'a> Iterator for SensorIter<'a> { 10 | type Item = Sensor<'a>; 11 | 12 | fn next(&mut self) -> Option { 13 | if self.index < self.len { 14 | let hardware = Sensor { 15 | computer_guard: self.inner.guard, 16 | hardware_guard: self.inner, 17 | index: self.index as i32, 18 | }; 19 | 20 | self.index += 1; 21 | 22 | Some(hardware) 23 | } else { 24 | None 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "ui:dev", 8 | "type": "shell", 9 | // `dev` keeps running in the background 10 | // ideally you should also configure a `problemMatcher` 11 | // see https://code.visualstudio.com/docs/editor/tasks#_can-a-background-task-be-used-as-a-prelaunchtask-in-launchjson 12 | "isBackground": true, 13 | // change this to your `beforeDevCommand`: 14 | "command": "npm", 15 | "args": ["run", "dev"] 16 | }, 17 | { 18 | "label": "ui:build", 19 | "type": "shell", 20 | // change this to your `beforeBuildCommand`: 21 | "command": "npm", 22 | "args": ["run", "build"] 23 | } 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /manifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /.github/workflows/sponsors.yml: -------------------------------------------------------------------------------- 1 | name: Generate sponsors section in readme 2 | permissions: 3 | contents: write 4 | on: 5 | workflow_dispatch: 6 | schedule: 7 | - cron: 30 15 * * 0-6 8 | jobs: 9 | deploy: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout 🛎️ 13 | uses: actions/checkout@v2 14 | 15 | - name: Generate Sponsors 💖 16 | uses: JamesIves/github-sponsors-readme-action@v1 17 | with: 18 | token: ${{ secrets.GHA_README_SPONSORS }} 19 | file: "README.md" 20 | 21 | - name: Deploy to GitHub 🚀 22 | uses: JamesIves/github-pages-deploy-action@v4 23 | with: 24 | branch: main 25 | folder: "." 26 | token: ${{ secrets.GHA_README_SPONSORS }} 27 | commit-message: "chore: update sponsors" 28 | -------------------------------------------------------------------------------- /frontend/svelte.config.js: -------------------------------------------------------------------------------- 1 | import adapter from "@sveltejs/adapter-static"; 2 | import { vitePreprocess } from "@sveltejs/kit/vite"; 3 | 4 | /** @type {import('@sveltejs/kit').Config} */ 5 | const config = { 6 | extensions: [".svelte"], 7 | // Consult https://kit.svelte.dev/docs/integrations#preprocessors 8 | // for more information about preprocessors 9 | preprocess: [vitePreprocess()], 10 | 11 | vitePlugin: { 12 | inspector: true, 13 | }, 14 | kit: { 15 | // adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list. 16 | // If your environment is not supported or you settled on a specific environment, switch out the adapter. 17 | // See https://kit.svelte.dev/docs/adapters for more information about adapters. 18 | adapter: adapter(), 19 | }, 20 | }; 21 | export default config; 22 | -------------------------------------------------------------------------------- /crates/ultralight/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ultralight-rs" 3 | version = "0.1.1" 4 | edition = "2021" 5 | rust-version = "1.75.0" 6 | description = "High-level Rust bindings for Ultralight." 7 | homepage = "https://github.com/brunostjohn/zefirs-flashy-cooler" 8 | license = "MIT" 9 | 10 | [dependencies] 11 | bytemuck = { version = "1.14.0", features = ["derive"] } 12 | libffi = "3.2.0" 13 | nohash-hasher = "0.2.0" 14 | thiserror = "1.0.50" 15 | ultralight-sys = { path = "../ultralight-sys", version = "0.1.1" } 16 | wgpu = { version = "0.18.0", features = ["spirv"] } 17 | wgpu-async = "0.18.0" 18 | 19 | [dev-dependencies] 20 | tokio = { version = "1.35.0", features = ["full"] } 21 | rusty-fork = "0.3.0" 22 | 23 | [build-dependencies] 24 | spirv-compiler = "0.2.0" 25 | ultralight-build = { path = "../ultralight-build", version = "0.1.3" } 26 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "" 5 | labels: "" 6 | assignees: "" 7 | --- 8 | 9 | **Describe the bug** 10 | A clear and concise description of what the bug is. 11 | 12 | **To Reproduce** 13 | Steps to reproduce the behavior: 14 | 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | 28 | - OS: [e.g. Windows 11 22H2] 29 | - Device: [e.g. Capellix, Thermaltake] 30 | - Version [e.g. 0.1.0] 31 | 32 | **Additional context** 33 | Add any other context about the problem here. 34 | -------------------------------------------------------------------------------- /crates/ultralight/src/types/gpu/shader_type.rs: -------------------------------------------------------------------------------- 1 | use ultralight_sys::{ 2 | ULShaderType, ULShaderType_kShaderType_Fill, ULShaderType_kShaderType_FillPath, 3 | }; 4 | 5 | use crate::error::ULError; 6 | 7 | #[derive(Debug)] 8 | pub enum ShaderType { 9 | Fill = ULShaderType_kShaderType_Fill as isize, 10 | FillPath = ULShaderType_kShaderType_FillPath as isize, 11 | } 12 | 13 | impl TryFrom for ShaderType { 14 | type Error = ULError; 15 | 16 | #[allow(non_upper_case_globals)] 17 | #[inline(always)] 18 | fn try_from(st: ULShaderType) -> Result { 19 | match st { 20 | ULShaderType_kShaderType_Fill => Ok(ShaderType::Fill), 21 | ULShaderType_kShaderType_FillPath => Ok(ShaderType::FillPath), 22 | _ => Err(ULError::ShaderUnsupportedType), 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /crates/librehardwaremonitor-rs/src/hardware/iter/hardware.rs: -------------------------------------------------------------------------------- 1 | use crate::Hardware; 2 | 3 | pub struct HardwareIter<'a> { 4 | pub(crate) inner: &'a Hardware<'a>, 5 | pub(crate) index: usize, 6 | pub(crate) len: usize, 7 | } 8 | 9 | impl<'a> Iterator for HardwareIter<'a> { 10 | type Item = Hardware<'a>; 11 | 12 | fn next(&mut self) -> Option { 13 | if self.index < self.len { 14 | let hardware = Hardware { 15 | guard: self.inner.guard, 16 | indices: { 17 | let mut indices = self.inner.indices.clone(); 18 | 19 | indices.push(self.index as i32); 20 | 21 | indices 22 | }, 23 | }; 24 | 25 | self.index += 1; 26 | 27 | Some(hardware) 28 | } else { 29 | None 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/services/rendering/dispatch_sensors.rs: -------------------------------------------------------------------------------- 1 | use super::message::SensorSubscriptionNotification; 2 | use ultralight::ULView; 3 | 4 | pub fn dispatch_sensors(view: &mut ULView<'_>, values: &[SensorSubscriptionNotification]) { 5 | let mut stringified = "{".to_string(); 6 | 7 | for value in values { 8 | stringified.push('"'); 9 | stringified.push_str(&value.code_name); 10 | stringified.push_str("\":"); 11 | let serialised = serde_json::to_string(value).unwrap_or("{}".to_string()); 12 | stringified.push_str(&serialised); 13 | stringified.push(','); 14 | } 15 | 16 | stringified.pop(); 17 | stringified.push('}'); 18 | 19 | let script = format!( 20 | "document.dispatchEvent(new CustomEvent('sensorValues', {{ detail: JSON.parse('{}') }}));", 21 | stringified 22 | ); 23 | 24 | view.evaluate_script(script); 25 | } 26 | -------------------------------------------------------------------------------- /crates/ultralight/src/renderer/tests.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use crate::ULPlatformBuilder; 3 | use rusty_fork::rusty_fork_test; 4 | 5 | rusty_fork_test! { 6 | #[test] 7 | fn creates_renderer() { 8 | ULPlatformBuilder::new() 9 | .enable_file_logger("./logs.txt") 10 | .enable_platform_file_system() 11 | .enable_platform_font_loader() 12 | .build(); 13 | 14 | ULRendererBuilder::new() 15 | .set_resource_path_prefix({ 16 | let mut newthing = std::env::current_dir().unwrap(); 17 | newthing.pop(); 18 | newthing.pop(); 19 | newthing.push("target"); 20 | newthing.push("debug"); 21 | newthing.push("deps"); 22 | newthing.push("resources\\"); 23 | 24 | newthing 25 | }) 26 | .build(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /crates/ultralight/src/platform/logger.rs: -------------------------------------------------------------------------------- 1 | use crate::types::log_level; 2 | use ultralight_sys::ULLogLevel; 3 | 4 | static mut CALLBACK: Option> = None; 5 | 6 | pub(crate) unsafe extern "C" fn logger_wrapper( 7 | log_level: ULLogLevel, 8 | message: ultralight_sys::ULString, 9 | ) { 10 | let message = crate::string::ULString::from_raw(message); 11 | let message = message.as_str(); 12 | if let Some(ref mut callback) = CALLBACK { 13 | callback(log_level.into(), message); 14 | } 15 | } 16 | 17 | pub(crate) fn set_logger(logger: F) 18 | where 19 | F: Fn(crate::types::log_level::ULLogLevel, &str) + 'static, 20 | { 21 | unsafe { 22 | CALLBACK = Some(Box::new(logger)); 23 | } 24 | } 25 | 26 | pub(crate) fn default_logger(log_level: crate::types::log_level::ULLogLevel, message: &str) { 27 | println!("{:?}: {}", log_level, message); 28 | } 29 | -------------------------------------------------------------------------------- /src/services/sensors/mod.rs: -------------------------------------------------------------------------------- 1 | mod r#impl; 2 | mod message; 3 | pub use message::SensorMessage; 4 | use r#impl::*; 5 | use tachyonix::{Receiver, Sender}; 6 | use tokio::{ 7 | task::{JoinHandle, LocalSet}, 8 | time::Duration, 9 | }; 10 | 11 | use super::rendering::message::RendererMessage; 12 | 13 | pub async fn spawn_sensors( 14 | local: &LocalSet, 15 | interval: Duration, 16 | sender_renderer: Sender, 17 | ) -> ( 18 | Sender, 19 | Receiver, 20 | JoinHandle<()>, 21 | ) { 22 | let (sender_from, receiver_from) = tachyonix::channel(10); 23 | let (sender_to, receiver_to) = tachyonix::channel(10); 24 | 25 | let handle = local.spawn_local(async move { 26 | let sensors = Sensors::new(interval, receiver_to, sender_from, sender_renderer); 27 | sensors.run().await; 28 | }); 29 | 30 | (sender_to, receiver_from, handle) 31 | } 32 | -------------------------------------------------------------------------------- /src/services/rendering/render_helpers.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Context; 2 | use lcd_coolers::DisplayCooler; 3 | use ultralight::{ULRenderer, ULView}; 4 | 5 | pub async fn render_bitmap_and_send( 6 | view: &mut ULView<'_>, 7 | device: &mut impl DisplayCooler, 8 | ) -> anyhow::Result<()> { 9 | let mut surface = view.get_surface(); 10 | let mut bitmap = surface.get_bitmap().context("Failed to get bitmap")?; 11 | bitmap.swap_red_blue_channels()?; 12 | let pixels = bitmap.lock_pixels().context("Failed to lock pixels"); 13 | if let Ok(ref pixels) = pixels { 14 | if device.send_image(pixels.as_ref()).await.is_err() { 15 | let _ = device.reopen().await; 16 | } 17 | } 18 | drop(pixels); 19 | 20 | let _ = bitmap.swap_red_blue_channels(); 21 | 22 | Ok(()) 23 | } 24 | 25 | pub fn update_and_render(renderer: &ULRenderer) { 26 | renderer.update(); 27 | renderer.render(); 28 | } 29 | -------------------------------------------------------------------------------- /src/services/sensors/message.rs: -------------------------------------------------------------------------------- 1 | use librehardwaremonitor_rs::SensorType; 2 | use tokio::time::Duration; 3 | 4 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 5 | pub enum SensorMessage { 6 | ChangePoll(Duration), 7 | RequestAllSensors, 8 | AllSensorsResponse(Vec), 9 | SubscribeRequestList(Vec), 10 | FindAndSubscribeByIdWithCodeName(Vec<(String, String)>), 11 | Shutdown, 12 | } 13 | 14 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 15 | pub struct SensorsResponse { 16 | pub sensor_name: String, 17 | pub hardware_name: String, 18 | pub hardware_indices: Vec, 19 | pub sensor_type: SensorType, 20 | pub sensor_idx: usize, 21 | pub sensor_persist_id: String, 22 | } 23 | 24 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 25 | pub struct SubscribeRequest { 26 | pub hardware_indices: Vec, 27 | pub name_as: String, 28 | pub sensor_idx: usize, 29 | } 30 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "lldb", 9 | "request": "launch", 10 | "name": "Tauri Development Debug", 11 | "cargo": { 12 | "args": ["build", "--manifest-path=./src-tauri/Cargo.toml", "--no-default-features"] 13 | }, 14 | // task for the `beforeDevCommand` if used, must be configured in `.vscode/tasks.json` 15 | "preLaunchTask": "ui:dev" 16 | }, 17 | { 18 | "type": "lldb", 19 | "request": "launch", 20 | "name": "Tauri Production Debug", 21 | "cargo": { 22 | "args": ["build", "--release", "--manifest-path=./src-tauri/Cargo.toml"] 23 | }, 24 | // task for the `beforeBuildCommand` if used, must be configured in `.vscode/tasks.json` 25 | "preLaunchTask": "ui:build" 26 | } 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /src/services/rendering/setup.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::app::paths::get_app_path; 2 | use anyhow::Context; 3 | use lcd_coolers::{find_and_create_device, DeviceCreator, DisplayCooler}; 4 | use ultralight::{ULPlatformBuilder, ULRenderer, ULRendererBuilder}; 5 | 6 | pub async fn setup_rendering() -> anyhow::Result<(ULRenderer, impl DisplayCooler + DeviceCreator)> { 7 | let app_path = get_app_path()?; 8 | 9 | ULPlatformBuilder::new() 10 | .enable_file_logger(app_path.join("ul-log.txt")) 11 | .enable_platform_file_system() 12 | .enable_platform_font_loader() 13 | .build(); 14 | 15 | let renderer = ULRendererBuilder::new() 16 | .set_resource_path_prefix(app_path.join("resources")) 17 | .build(); 18 | 19 | let mut device = find_and_create_device().await.context("No device found!")?; 20 | device 21 | .initialise() 22 | .await 23 | .context("Failed to initialise device!")?; 24 | 25 | Ok((renderer, device)) 26 | } 27 | -------------------------------------------------------------------------------- /crates/ultralight-sys/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_upper_case_globals)] 2 | #![allow(non_camel_case_types)] 3 | #![allow(non_snake_case)] 4 | 5 | include!(concat!(env!("OUT_DIR"), "/bindings.rs")); 6 | 7 | pub fn check_for_resources() { 8 | let mut current_exe = std::env::current_exe().expect("Failed to get current exe path!"); 9 | current_exe.pop(); 10 | 11 | let dll_names = [ 12 | "AppCore.dll", 13 | "Ultralight.dll", 14 | "UltralightCore.dll", 15 | "WebCore.dll", 16 | ]; 17 | 18 | for dll_name in dll_names.iter() { 19 | if !current_exe.join(dll_name).exists() { 20 | panic!("Missing Ultralight resource: {}", dll_name); 21 | } 22 | } 23 | 24 | let resource_names = ["cacert.pem", "icudt67l.dat"]; 25 | 26 | for resource_name in resource_names.iter() { 27 | if !current_exe.join("resources").join(resource_name).exists() { 28 | panic!("Missing Ultralight resource: {}", resource_name); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /crates/ultralight/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # Ultralight 2 | //! 3 | //! `ultralight` is a WIP Rust wrapper for the [Ultralight](https://ultralig.ht/) web rendering engine. 4 | //! Its goal is to provide a safe and idiomatic Rust interface while maintaining the performance of the 5 | //! underlying Ultralight C API. It is currently in a very early stage of development and is not yet 6 | //! ready for production use. It uses a lot of unsafe code and is not yet fully tested. It is also 7 | //! not yet feature complete. If you are interested in contributing, please see the [GitHub repository](https://github.com/brunostjohn/zefirs-flashy-cooler). 8 | //! As it is part of a larger project, it is in the `crates/ultralight` directory. If there is enough interest, it will be moved to its own repository. 9 | 10 | pub mod js; 11 | pub mod platform; 12 | pub mod renderer; 13 | mod string; 14 | mod types; 15 | pub mod view; 16 | 17 | pub use js::*; 18 | pub use platform::*; 19 | pub use renderer::*; 20 | pub use types::*; 21 | pub use view::*; 22 | -------------------------------------------------------------------------------- /src/services/config/mod.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Context; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | #[derive(Debug, Serialize, Deserialize, Clone)] 5 | pub struct AppConfig { 6 | pub port: usize, 7 | pub start_at_login: bool, 8 | pub start_minimised: bool, 9 | pub theme_path: Option, 10 | pub sensor_poll_rate_ms: u64, 11 | } 12 | 13 | impl Default for AppConfig { 14 | fn default() -> Self { 15 | Self { 16 | port: 2137, 17 | start_at_login: false, 18 | start_minimised: false, 19 | theme_path: None, 20 | sensor_poll_rate_ms: 1000, 21 | } 22 | } 23 | } 24 | 25 | impl AppConfig { 26 | pub fn load() -> Self { 27 | confy::load("zefirs-flashy-cooler", Some("AppConfig")).unwrap_or_default() 28 | } 29 | 30 | pub fn save(&self) -> anyhow::Result<()> { 31 | confy::store("zefirs-flashy-cooler", Some("AppConfig"), self.clone()) 32 | .context("Failed to store config!") 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /crates/ultralight/src/types/log_level.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug)] 2 | pub enum ULLogLevel { 3 | Error, 4 | Warning, 5 | Info, 6 | } 7 | 8 | impl From for ultralight_sys::ULLogLevel { 9 | fn from(log_level: ULLogLevel) -> Self { 10 | match log_level { 11 | ULLogLevel::Error => ultralight_sys::ULLogLevel_kLogLevel_Error, 12 | ULLogLevel::Warning => ultralight_sys::ULLogLevel_kLogLevel_Warning, 13 | ULLogLevel::Info => ultralight_sys::ULLogLevel_kLogLevel_Info, 14 | } 15 | } 16 | } 17 | 18 | impl From for ULLogLevel { 19 | fn from(log_level: ultralight_sys::ULLogLevel) -> Self { 20 | match log_level { 21 | ultralight_sys::ULLogLevel_kLogLevel_Error => ULLogLevel::Error, 22 | ultralight_sys::ULLogLevel_kLogLevel_Warning => ULLogLevel::Warning, 23 | ultralight_sys::ULLogLevel_kLogLevel_Info => ULLogLevel::Info, 24 | _ => panic!("Unknown log level"), 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /crates/ultralight/shaders/vertex/v2f_c4f_t2f.hlsl: -------------------------------------------------------------------------------- 1 | cbuffer Uniforms : register(b0) 2 | { 3 | float4 State; 4 | matrix Transform; 5 | float4 Scalar4[2]; 6 | float4 Vector[8]; 7 | uint ClipSize; 8 | matrix Clip[8]; 9 | }; 10 | 11 | float ScreenWidth() { return State[1]; } 12 | float ScreenHeight() { return State[2]; } 13 | float Scalar(int i) { if (i < 4) return Scalar4[0][i]; else return Scalar4[1][i - 4]; } 14 | float4 sRGBToLinear(float4 val) { return float4(val.xyz * (val.xyz * (val.xyz * 0.305306011 + 0.682171111) + 0.012522878), val.w); } 15 | 16 | struct VS_OUTPUT 17 | { 18 | float4 Position : SV_POSITION; 19 | float4 Color : COLOR0; 20 | float2 ObjectCoord : TEXCOORD0; 21 | }; 22 | 23 | VS_OUTPUT VS(float2 Position : POSITION, 24 | uint4 Color : COLOR0, 25 | float2 ObjCoord : TEXCOORD0) 26 | { 27 | VS_OUTPUT output; 28 | output.Position = mul(Transform, float4(Position, 0.0, 1.0)); 29 | output.Color = float4(Color) / 255.0; 30 | output.ObjectCoord = ObjCoord; 31 | return output; 32 | } -------------------------------------------------------------------------------- /frontend/README.md: -------------------------------------------------------------------------------- 1 | # create-svelte 2 | 3 | Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/master/packages/create-svelte). 4 | 5 | ## Creating a project 6 | 7 | If you're seeing this, you've probably already done this step. Congrats! 8 | 9 | ```bash 10 | # create a new project in the current directory 11 | npm create svelte@latest 12 | 13 | # create a new project in my-app 14 | npm create svelte@latest my-app 15 | ``` 16 | 17 | ## Developing 18 | 19 | Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server: 20 | 21 | ```bash 22 | npm run dev 23 | 24 | # or start the server and open the app in a new browser tab 25 | npm run dev -- --open 26 | ``` 27 | 28 | ## Building 29 | 30 | To create a production version of your app: 31 | 32 | ```bash 33 | npm run build 34 | ``` 35 | 36 | You can preview the production build with `npm run preview`. 37 | 38 | > To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment. 39 | -------------------------------------------------------------------------------- /crates/librehardwaremonitor-sys/csharp-project/LibreHardwareMonitorNative.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | true 11 | Speed 12 | 13 | 14 | 15 | Speed 16 | 17 | 18 | 19 | avx2,bmi2,fma,pclmul,popcnt,aes 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | true 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /crates/ultralight/src/types/gpu/ul_arr_macro.rs: -------------------------------------------------------------------------------- 1 | macro_rules! from_ul_arr { 2 | ($arr:expr, $from:ident) => { 3 | [ 4 | $arr[0].$from, 5 | $arr[1].$from, 6 | $arr[2].$from, 7 | $arr[3].$from, 8 | $arr[4].$from, 9 | $arr[5].$from, 10 | $arr[6].$from, 11 | $arr[7].$from, 12 | ] 13 | }; 14 | (mat $arr:expr, $from:ident) => { 15 | [ 16 | from_ul_arr!(mat $arr[0].$from), 17 | from_ul_arr!(mat $arr[1].$from), 18 | from_ul_arr!(mat $arr[2].$from), 19 | from_ul_arr!(mat $arr[3].$from), 20 | from_ul_arr!(mat $arr[4].$from), 21 | from_ul_arr!(mat $arr[5].$from), 22 | from_ul_arr!(mat $arr[6].$from), 23 | from_ul_arr!(mat $arr[7].$from), 24 | ] 25 | }; 26 | (mat $arr: expr) => { 27 | [ 28 | [$arr[0], $arr[1], $arr[2], $arr[3]], 29 | [$arr[4], $arr[5], $arr[6], $arr[7]], 30 | [$arr[8], $arr[9], $arr[10], $arr[11]], 31 | [$arr[12], $arr[13], $arr[14], $arr[15]], 32 | ] 33 | }; 34 | } 35 | 36 | pub(crate) use from_ul_arr; 37 | -------------------------------------------------------------------------------- /frontend/src/lib/components/appNav/AppNav.svelte: -------------------------------------------------------------------------------- 1 | 31 | 32 | 47 | -------------------------------------------------------------------------------- /crates/ultralight-sys/build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::path::PathBuf; 3 | 4 | fn main() { 5 | println!("cargo:rerun-if-changed=build.rs"); 6 | 7 | let header_location = { 8 | let mut dir: PathBuf = std::env::var("CARGO_MANIFEST_DIR") 9 | .expect("Failed to get project dir!") 10 | .into(); 11 | dir.push("wrapped"); 12 | dir.push("ultralight"); 13 | 14 | dir 15 | }; 16 | 17 | ultralight_build::build_with_headers(header_location); 18 | 19 | let bindings = bindgen::Builder::default() 20 | .header("wrapped/wrapper.h") 21 | .clang_arg("-I./wrapped/ultralight") 22 | .impl_debug(true) 23 | .impl_partialeq(true) 24 | .generate_comments(true) 25 | .generate_inline_functions(true) 26 | .allowlist_var("^UL.*|JS.*|ul.*|WK.*|kJS.*") 27 | .allowlist_type("^UL.*|JS.*|ul.*|WK.*") 28 | .allowlist_function("^UL.*|JS.*|ul.*|WK.*") 29 | .generate() 30 | .expect("Unable to generate bindings"); 31 | 32 | let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); 33 | 34 | bindings 35 | .write_to_file(out_path.join("bindings.rs")) 36 | .expect("Couldn't write bindings!"); 37 | } 38 | -------------------------------------------------------------------------------- /crates/librehardwaremonitor-sys/csharp-project/LibreHardwareMonitorNative.sln: -------------------------------------------------------------------------------- 1 | Microsoft Visual Studio Solution File, Format Version 12.00 2 | # Visual Studio Version 17 3 | VisualStudioVersion = 17.5.33627.172 4 | MinimumVisualStudioVersion = 10.0.40219.1 5 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LibreHardwareMonitorNative", "LibreHardwareMonitorNative.csproj", "{C3FDE135-ED2C-4775-AB81-B6E197129A59}" 6 | EndProject 7 | Global 8 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 9 | Debug|Any CPU = Debug|Any CPU 10 | Release|Any CPU = Release|Any CPU 11 | EndGlobalSection 12 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 13 | {C3FDE135-ED2C-4775-AB81-B6E197129A59}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 14 | {C3FDE135-ED2C-4775-AB81-B6E197129A59}.Debug|Any CPU.Build.0 = Debug|Any CPU 15 | {C3FDE135-ED2C-4775-AB81-B6E197129A59}.Release|Any CPU.ActiveCfg = Release|Any CPU 16 | {C3FDE135-ED2C-4775-AB81-B6E197129A59}.Release|Any CPU.Build.0 = Release|Any CPU 17 | EndGlobalSection 18 | GlobalSection(SolutionProperties) = preSolution 19 | HideSolutionNode = FALSE 20 | EndGlobalSection 21 | GlobalSection(ExtensibilityGlobals) = postSolution 22 | SolutionGuid = {92FA3057-C8AF-424B-B501-F64DCFD5C7B5} 23 | EndGlobalSection 24 | EndGlobal -------------------------------------------------------------------------------- /crates/ultralight/src/types/bitmap/format.rs: -------------------------------------------------------------------------------- 1 | use ultralight_sys::{ 2 | ULBitmapFormat, ULBitmapFormat_kBitmapFormat_A8_UNORM, 3 | ULBitmapFormat_kBitmapFormat_BGRA8_UNORM_SRGB, 4 | }; 5 | 6 | use crate::error::ULError; 7 | 8 | /// Describes the format of a bitmap. 9 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 10 | pub enum BitmapFormat { 11 | /// 8-bit alpha-only. 12 | A8Unorm = ULBitmapFormat_kBitmapFormat_A8_UNORM as isize, 13 | /// 32-bit BGRA, 8-bits per channel. 14 | Bgra8UnormSrgb = ULBitmapFormat_kBitmapFormat_BGRA8_UNORM_SRGB as isize, 15 | } 16 | 17 | impl TryFrom for BitmapFormat { 18 | type Error = ULError; 19 | 20 | #[allow(non_upper_case_globals)] 21 | fn try_from(value: ULBitmapFormat) -> Result { 22 | match value { 23 | ULBitmapFormat_kBitmapFormat_A8_UNORM => Ok(Self::A8Unorm), 24 | ULBitmapFormat_kBitmapFormat_BGRA8_UNORM_SRGB => Ok(Self::Bgra8UnormSrgb), 25 | _ => Err(ULError::BitmapUnsupportedFormat), 26 | } 27 | } 28 | } 29 | 30 | impl BitmapFormat { 31 | pub(crate) fn bytes_per_pixel(&self) -> usize { 32 | match self { 33 | Self::A8Unorm => 1, 34 | Self::Bgra8UnormSrgb => 4, 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/services/rendering/message.rs: -------------------------------------------------------------------------------- 1 | use librehardwaremonitor_rs::SensorType; 2 | use serde::Serialize; 3 | 4 | pub enum RendererMessage { 5 | NewSubscribedSensors(Vec), 6 | SensorValues(Vec), 7 | ReloadCurrentUrl(String), 8 | Shutdown, 9 | } 10 | 11 | // pub enum SensorValueType { 12 | // Min, 13 | // Max, 14 | // Current, 15 | // } 16 | 17 | pub struct SensorValue { 18 | pub sensor_id: usize, 19 | pub sensor_value: f32, 20 | } 21 | 22 | #[derive(Serialize)] 23 | #[serde(remote = "SensorType")] 24 | pub enum SensorTypeLocal { 25 | Voltage, 26 | Current, 27 | Power, 28 | Clock, 29 | Temperature, 30 | Load, 31 | Frequency, 32 | Fan, 33 | Flow, 34 | Control, 35 | Level, 36 | Factor, 37 | Data, 38 | SmallData, 39 | Throughput, 40 | TimeSpan, 41 | Energy, 42 | Noise, 43 | } 44 | 45 | #[derive(Serialize)] 46 | pub struct SensorSubscriptionNotification { 47 | pub sensor_name: String, 48 | pub hardware_name: String, 49 | #[serde(with = "SensorTypeLocal")] 50 | pub sensor_type: SensorType, 51 | pub sensor_id: usize, 52 | pub sensor_value: f32, 53 | #[serde(skip)] 54 | pub code_name: String, 55 | // pub sensor_value_type: SensorValueType, 56 | } 57 | -------------------------------------------------------------------------------- /crates/ultralight/src/platform/gpu/driver_trait.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | bitmap::Bitmap, 3 | gpu::{ 4 | gpu_command::GPUCommand, index_buffer::IndexBuffer, render_buffer::RenderBuffer, 5 | vertex_buffer::VertexBuffer, 6 | }, 7 | }; 8 | 9 | pub(crate) trait GPUDriver { 10 | fn begin_synchronize(&mut self); 11 | fn end_synchronize(&mut self); 12 | fn next_texture_id(&mut self) -> u32; 13 | fn create_texture(&mut self, texture_id: u32, bitmap: Bitmap); 14 | fn update_texture(&mut self, texture_id: u32, bitmap: Bitmap); 15 | fn destroy_texture(&mut self, texture_id: u32); 16 | fn next_render_buffer_id(&mut self) -> u32; 17 | fn create_render_buffer(&mut self, render_buffer_id: u32, render_buffer: RenderBuffer); 18 | fn destroy_render_buffer(&mut self, render_buffer_id: u32); 19 | fn next_geometry_id(&mut self) -> u32; 20 | fn create_geometry( 21 | &mut self, 22 | geometry_id: u32, 23 | vertex_buffer: VertexBuffer, 24 | index_buffer: IndexBuffer, 25 | ); 26 | fn update_geometry( 27 | &mut self, 28 | geometry_id: u32, 29 | vertex_buffer: VertexBuffer, 30 | index_buffer: IndexBuffer, 31 | ); 32 | fn destroy_geometry(&mut self, geometry_id: u32); 33 | fn update_command_list(&mut self, command_list: Vec); 34 | } 35 | -------------------------------------------------------------------------------- /crates/ultralight/src/types/gpu/gpu_command.rs: -------------------------------------------------------------------------------- 1 | use ultralight_sys::ULCommand; 2 | 3 | use crate::error::ULError; 4 | 5 | use super::gpu_state::GPUState; 6 | 7 | #[derive(Debug)] 8 | pub enum GPUCommand { 9 | ClearRenderBuffer { 10 | render_buffer_id: u32, 11 | }, 12 | DrawGeometry { 13 | gpu_state: Box, 14 | geometry_id: u32, 15 | indices_offset: u32, 16 | indices_count: u32, 17 | }, 18 | } 19 | 20 | impl TryFrom for GPUCommand { 21 | type Error = ULError; 22 | 23 | #[inline] 24 | #[allow(non_upper_case_globals)] 25 | #[allow(non_snake_case)] 26 | fn try_from(gc: ULCommand) -> Result { 27 | match gc.command_type as u32 { 28 | ULCommandType_kCommandType_DrawGeometry => Ok(GPUCommand::DrawGeometry { 29 | gpu_state: Box::new(GPUState::try_from(gc.gpu_state)?), 30 | geometry_id: gc.geometry_id, 31 | indices_count: gc.indices_count, 32 | indices_offset: gc.indices_offset, 33 | }), 34 | ULCommandType_kCommandType_ClearRenderBuffer => Ok(GPUCommand::ClearRenderBuffer { 35 | render_buffer_id: gc.gpu_state.render_buffer_id, 36 | }), 37 | _ => Err(ULError::GPUCommandUnsupportedType), 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /frontend/src/routes/settings/+layout.svelte: -------------------------------------------------------------------------------- 1 | 27 | 28 |
29 | 30 | {#each routes as { name, icon, path }} 31 | goto(path)} 37 | > 38 | 39 | 40 | 41 | {name} 42 | 43 | {/each} 44 | 45 |
46 | 47 |
48 |
49 | -------------------------------------------------------------------------------- /frontend/src/lib/components/header/WindowControls.svelte: -------------------------------------------------------------------------------- 1 | 31 | 32 |
33 | handleWindowClose()} /> 34 | handleWindowExit()} /> 35 | handleWindowMinimise()} /> 36 |
37 | -------------------------------------------------------------------------------- /frontend/src/routes/theme/+page.svelte: -------------------------------------------------------------------------------- 1 | 20 | 21 |
22 |
23 |
24 |
25 |

{name}

26 |

by {author}

27 |
28 |
29 | Version: {version} 32 | Tested on: {tested_on.join(" ,")} 35 |
36 |
37 |
38 | 39 |
40 |
41 | 42 |
43 | -------------------------------------------------------------------------------- /src/services/discord/register.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Context; 2 | use discord_sdk::{ 3 | handlers::Printer, 4 | registration::{Application, BinArg, LaunchCommand}, 5 | Discord, Subscriptions, 6 | }; 7 | 8 | const APP_ID: i64 = 1185981801747988620; 9 | 10 | pub fn register() -> anyhow::Result { 11 | let exe_location = LaunchCommand::Bin { 12 | path: std::env::current_exe().context("Failed to find executable location!")?, 13 | args: vec![BinArg::Arg("--ignore-minimised".into())], 14 | }; 15 | 16 | let app = Application { 17 | id: APP_ID, 18 | name: Some("Zefir's Flashy Cooler".into()), 19 | command: exe_location, 20 | }; 21 | 22 | discord_sdk::registration::register_app(app).context("Failed to register app with Discord!")?; 23 | 24 | // the developer seriously didnt derive clone?? silly 25 | let exe_location = LaunchCommand::Bin { 26 | path: std::env::current_exe().context("Failed to find executable location!")?, 27 | args: vec![BinArg::Arg("--ignore-minimised".into())], 28 | }; 29 | 30 | let app = Application { 31 | id: APP_ID, 32 | name: Some("Zefir's Flashy Cooler".into()), 33 | command: exe_location, 34 | }; 35 | 36 | let discord = Discord::new(app, Subscriptions::ACTIVITY, Box::new(Printer)) 37 | .context("Failed to create Discord client!")?; 38 | 39 | Ok(discord) 40 | } 41 | -------------------------------------------------------------------------------- /frontend/src/lib/components/header/Controls/IconButton.svelte: -------------------------------------------------------------------------------- 1 | 37 | 38 | 48 | -------------------------------------------------------------------------------- /crates/lcd-coolers/src/corsair/h150i/counter.rs: -------------------------------------------------------------------------------- 1 | use std::time::{Duration, SystemTime}; 2 | 3 | pub(crate) struct Counter { 4 | frequency: Duration, 5 | time: SystemTime, 6 | } 7 | 8 | impl Counter { 9 | pub fn new(frequency_ms: u64) -> Self { 10 | Self { 11 | frequency: Duration::from_millis(frequency_ms), 12 | time: SystemTime::now(), 13 | } 14 | } 15 | 16 | #[inline(always)] 17 | pub fn check_time(&mut self) -> bool { 18 | if self.time.try_elapsed(self.frequency) { 19 | self.time = SystemTime::now(); 20 | return true; 21 | } 22 | false 23 | } 24 | } 25 | 26 | pub(crate) trait TryElapsed { 27 | fn try_elapsed(&self, duration: T) -> bool 28 | where 29 | Self: Sized; 30 | } 31 | 32 | impl TryElapsed for SystemTime { 33 | #[inline(always)] 34 | fn try_elapsed(&self, duration_ms: u64) -> bool 35 | where 36 | Self: Sized, 37 | { 38 | match self.elapsed() { 39 | Ok(time) => time >= Duration::from_millis(duration_ms), 40 | Err(_) => false, 41 | } 42 | } 43 | } 44 | 45 | impl TryElapsed for SystemTime { 46 | #[inline(always)] 47 | fn try_elapsed(&self, duration: Duration) -> bool 48 | where 49 | Self: Sized, 50 | { 51 | match self.elapsed() { 52 | Ok(time) => time >= duration, 53 | Err(_) => false, 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /frontend/src/lib/components/themes/ThemeCard.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | 17 | {name} 23 |
24 | {name} 30 |
31 |
34 |

{name}

35 |

{author}

36 |
37 |
38 | 39 | 44 | -------------------------------------------------------------------------------- /src/utils/themes/paths.rs: -------------------------------------------------------------------------------- 1 | use std::path::PathBuf; 2 | 3 | const DEFAULT_HTML: &str = include_str!("../../../resources/default.html"); 4 | const DEFAULT_CAT_IMG: &[u8] = include_bytes!("../../../resources/cat.png"); 5 | 6 | pub fn get_default_theme_path() -> String { 7 | let mut path = std::env::var("USERPROFILE") 8 | .map(|val| { 9 | let mut temp = PathBuf::from(val); 10 | temp.push("Documents"); 11 | temp.push("Zefir's Flashy Cooler"); 12 | 13 | temp 14 | }) 15 | .unwrap_or({ 16 | let mut temp = std::env::current_exe().unwrap(); 17 | temp.pop(); 18 | 19 | temp 20 | }); 21 | path.push("Themes"); 22 | path.push("__DEFAULT__"); 23 | let _ = std::fs::create_dir_all(&path); 24 | let _ = std::fs::write(path.join("index.html"), DEFAULT_HTML); 25 | let _ = std::fs::write(path.join("cat.png"), DEFAULT_CAT_IMG); 26 | path.to_str().unwrap().to_string() 27 | } 28 | 29 | pub fn get_all_themes_path() -> PathBuf { 30 | let mut path = std::env::var("USERPROFILE") 31 | .map(|val| { 32 | let mut temp = PathBuf::from(val); 33 | temp.push("Documents"); 34 | temp.push("Zefir's Flashy Cooler"); 35 | 36 | temp 37 | }) 38 | .unwrap_or({ 39 | let mut temp = std::env::current_exe().unwrap(); 40 | temp.pop(); 41 | 42 | temp 43 | }); 44 | path.push("Themes"); 45 | 46 | path 47 | } 48 | -------------------------------------------------------------------------------- /crates/ultralight/src/js/function.rs: -------------------------------------------------------------------------------- 1 | use ultralight_sys::JSValueRef; 2 | 3 | use crate::value::JSValue; 4 | 5 | use super::types::{IsJSValue, JSFunction, JSOpaque}; 6 | 7 | impl<'a> JSValue> { 8 | pub fn call( 9 | &mut self, 10 | arguments: &[JSValue], 11 | ) -> Result>>, JSValue>> 12 | where 13 | T: IsJSValue, 14 | { 15 | let arguments: Vec<_> = arguments 16 | .iter() 17 | .map(|value| value.internal.get_value()) 18 | .collect(); 19 | let mut exception = 0 as JSValueRef; 20 | 21 | let returned = unsafe { 22 | ultralight_sys::JSObjectCallAsFunction( 23 | **self.internal.guard, 24 | self.internal.get_value() as _, 25 | self.value as _, 26 | arguments.len() as _, 27 | arguments.as_ptr(), 28 | &mut exception, 29 | ) 30 | }; 31 | 32 | if exception.is_null() { 33 | if returned.is_null() { 34 | Ok(None) 35 | } else { 36 | Ok(Some(JSValue::new(JSOpaque { 37 | internal: returned, 38 | guard: self.internal.guard, 39 | }))) 40 | } 41 | } else { 42 | Err(JSValue::new(JSOpaque { 43 | internal: exception, 44 | guard: self.internal.guard, 45 | })) 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /crates/smol-static/src/filesystem.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Context; 2 | use async_compression::tokio::write::GzipEncoder; 3 | use std::path::Path; 4 | use tokio::io::AsyncWriteExt; 5 | 6 | pub(crate) struct HTTPFile { 7 | pub(crate) content_type: String, 8 | pub(crate) content: Vec, 9 | pub(crate) content_length: usize, 10 | } 11 | 12 | pub(crate) async fn read_http_file(filename: &str, path: &Path) -> anyhow::Result { 13 | let mut path = path.to_path_buf(); 14 | 15 | filename 16 | .split('/') 17 | .flat_map(|s| s.split('\\')) 18 | .filter(|s| !s.starts_with("..")) 19 | .for_each(|piece| path.push(piece)); 20 | 21 | let contents = tokio::fs::read(path) 22 | .await 23 | .context("Failed to read file!")?; 24 | 25 | let mut encoder = GzipEncoder::new(Vec::with_capacity(contents.len())); 26 | encoder 27 | .write_all(&contents) 28 | .await 29 | .context("Failed to compress file!")?; 30 | encoder.flush().await.context("Failed to flush encoder!")?; 31 | encoder 32 | .shutdown() 33 | .await 34 | .context("Failed to shutdown encoder!")?; 35 | 36 | let content_type = mime_guess::from_path(filename) 37 | .first_or_octet_stream() 38 | .essence_str() 39 | .to_owned(); 40 | 41 | let encoded = encoder.into_inner(); 42 | let content_length = encoded.len(); 43 | 44 | Ok(HTTPFile { 45 | content_type, 46 | content: encoded, 47 | content_length, 48 | }) 49 | } 50 | -------------------------------------------------------------------------------- /crates/ultralight/src/types/gpu/rect.rs: -------------------------------------------------------------------------------- 1 | use ultralight_sys::{ULIntRect, ULRect}; 2 | 3 | #[derive(Debug)] 4 | pub struct Rect { 5 | pub left: T, 6 | pub top: T, 7 | pub right: T, 8 | pub bottom: T, 9 | } 10 | 11 | impl Rect { 12 | #[inline(always)] 13 | pub fn is_empty(&self) -> bool { 14 | self.left == 0 && self.top == 0 && self.right == 0 && self.bottom == 0 15 | } 16 | } 17 | 18 | impl From for Rect { 19 | #[inline(always)] 20 | fn from(r: ULRect) -> Self { 21 | Rect { 22 | left: r.left, 23 | top: r.top, 24 | right: r.right, 25 | bottom: r.bottom, 26 | } 27 | } 28 | } 29 | 30 | impl From for Rect { 31 | #[inline(always)] 32 | fn from(r: ULIntRect) -> Self { 33 | Rect { 34 | left: r.left, 35 | top: r.top, 36 | right: r.right, 37 | bottom: r.bottom, 38 | } 39 | } 40 | } 41 | 42 | impl From> for ULRect { 43 | #[inline(always)] 44 | fn from(r: Rect) -> Self { 45 | ULRect { 46 | left: r.left, 47 | top: r.top, 48 | right: r.right, 49 | bottom: r.bottom, 50 | } 51 | } 52 | } 53 | 54 | impl From> for ULIntRect { 55 | #[inline(always)] 56 | fn from(r: Rect) -> Self { 57 | ULIntRect { 58 | left: r.left, 59 | top: r.top, 60 | right: r.right, 61 | bottom: r.bottom, 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/themes/play.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | services::{config::AppConfig, rendering::message::RendererMessage}, 3 | utils::themes::paths::get_all_themes_path, 4 | }; 5 | use smol_static::ServerMessage; 6 | use tachyonix::Sender; 7 | use tauri::State; 8 | 9 | use super::validate::validate_theme; 10 | 11 | #[tauri::command] 12 | pub async fn play_theme_handler( 13 | fs_name: String, 14 | server_sender: State<'_, Sender>, 15 | renderer_sender: State<'_, Sender>, 16 | ) -> Result<(), String> { 17 | play_theme(fs_name, server_sender.inner(), renderer_sender.inner()).await 18 | } 19 | 20 | async fn play_theme>( 21 | fs_name: S, 22 | server_sender: &Sender, 23 | renderer_sender: &Sender, 24 | ) -> Result<(), String> { 25 | if !validate_theme(fs_name.as_ref()).await { 26 | return Err("Invalid theme name!".into()); 27 | } 28 | let path = get_all_themes_path().join(fs_name.as_ref()); 29 | if let Some(path_str) = path.clone().to_str() { 30 | let mut config = AppConfig::load(); 31 | config.theme_path = Some(path_str.to_string()); 32 | let _ = config.save(); 33 | } 34 | server_sender 35 | .send(ServerMessage::SetBasePath(path)) 36 | .await 37 | .or::(Err("Failed to send server message!".into()))?; 38 | renderer_sender 39 | .send(RendererMessage::ReloadCurrentUrl( 40 | fs_name.as_ref().to_string(), 41 | )) 42 | .await 43 | .or::(Err("Failed to send render message!".into()))?; 44 | Ok(()) 45 | } 46 | -------------------------------------------------------------------------------- /crates/ultralight/src/js/internal_string.rs: -------------------------------------------------------------------------------- 1 | use std::ops::Deref; 2 | 3 | use crate::context::JSContext; 4 | 5 | pub(crate) struct JSCString<'a> { 6 | pub(crate) internal: ultralight_sys::JSStringRef, 7 | #[allow(unused)] 8 | pub(crate) guard: &'a JSContext<'a>, 9 | } 10 | 11 | impl Deref for JSCString<'_> { 12 | type Target = ultralight_sys::JSStringRef; 13 | 14 | fn deref(&self) -> &Self::Target { 15 | &self.internal 16 | } 17 | } 18 | 19 | impl From> for String { 20 | fn from(string: JSCString<'_>) -> Self { 21 | let internal = string.internal; 22 | 23 | let length = unsafe { ultralight_sys::JSStringGetMaximumUTF8CStringSize(internal) }; 24 | let mut buffer = vec![0; length]; 25 | 26 | unsafe { 27 | ultralight_sys::JSStringGetUTF8CString(internal, buffer.as_mut_ptr() as *mut i8, length) 28 | }; 29 | 30 | String::from_utf8(buffer).unwrap() 31 | } 32 | } 33 | 34 | impl<'a> JSCString<'a> { 35 | pub(crate) fn from_raw( 36 | internal: ultralight_sys::JSStringRef, 37 | guard: &'a JSContext<'a>, 38 | ) -> Self { 39 | Self { internal, guard } 40 | } 41 | 42 | pub(crate) fn new>(string: S, guard: &'a JSContext<'a>) -> Self { 43 | let internal = unsafe { 44 | ultralight_sys::JSStringCreateWithUTF8CString(string.as_ref().as_ptr() as *const i8) 45 | }; 46 | 47 | Self { internal, guard } 48 | } 49 | } 50 | 51 | impl<'a> Drop for JSCString<'a> { 52 | fn drop(&mut self) { 53 | unsafe { ultralight_sys::JSStringRelease(**self) } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/lifecycle/exit.rs: -------------------------------------------------------------------------------- 1 | use crate::services::{rendering::message::RendererMessage, sensors::SensorMessage}; 2 | use discord_sdk::Discord; 3 | use smol_static::ServerMessage; 4 | use std::sync::Arc; 5 | use tachyonix::Sender; 6 | use tauri::{AppHandle, State, Window}; 7 | use tokio::sync::RwLock; 8 | 9 | #[tauri::command] 10 | pub async fn exit_handler( 11 | window: Window, 12 | app: AppHandle, 13 | sender_renderer: State<'_, Sender>, 14 | sender_sensors: State<'_, Sender>, 15 | sender_server: State<'_, Sender>, 16 | discord: State<'_, Arc>>>, 17 | ) -> Result<(), ()> { 18 | exit( 19 | Some(window), 20 | &app, 21 | sender_renderer, 22 | sender_sensors, 23 | sender_server, 24 | discord, 25 | ) 26 | .await; 27 | 28 | Ok(()) 29 | } 30 | 31 | pub async fn exit( 32 | window: Option, 33 | app: &AppHandle, 34 | sender_renderer: State<'_, Sender>, 35 | sender_sensors: State<'_, Sender>, 36 | sender_server: State<'_, Sender>, 37 | discord: State<'_, Arc>>>, 38 | ) { 39 | if let Some(window) = window { 40 | let _ = window.close(); 41 | } 42 | 43 | let _ = sender_renderer.send(RendererMessage::Shutdown).await; 44 | let _ = sender_sensors.send(SensorMessage::Shutdown).await; 45 | let _ = sender_server.send(ServerMessage::Shutdown).await; 46 | 47 | if let Some(discord) = discord.write().await.take() { 48 | let _ = discord.disconnect().await; 49 | } 50 | 51 | app.exit(0); 52 | } 53 | -------------------------------------------------------------------------------- /src/services/mod.rs: -------------------------------------------------------------------------------- 1 | use self::{config::AppConfig, discord::register::register}; 2 | use crate::utils::themes::paths::get_default_theme_path; 3 | use std::sync::Arc; 4 | use tokio::{ 5 | sync::RwLock, 6 | task::{JoinHandle, LocalSet}, 7 | time::Duration, 8 | }; 9 | 10 | pub mod app; 11 | pub mod config; 12 | pub mod discord; 13 | pub mod rendering; 14 | pub mod sensors; 15 | pub mod server; 16 | 17 | pub async fn spawn_services() -> ( 18 | LocalSet, 19 | JoinHandle<()>, 20 | JoinHandle<()>, 21 | JoinHandle>, 22 | JoinHandle>, 23 | ) { 24 | let config = AppConfig::load(); 25 | let (sender_server, server) = server::spawn_server( 26 | config 27 | .theme_path 28 | .as_ref() 29 | .map(|x| x.to_owned()) 30 | .unwrap_or(get_default_theme_path()), 31 | config.port, 32 | ) 33 | .await; 34 | let local = LocalSet::new(); 35 | let interval = Duration::from_millis(config.sensor_poll_rate_ms); 36 | let (sender_renderer, receiver_renderer) = tachyonix::channel(10); 37 | let (sender_sensors, receiver_sensors, sensors) = 38 | sensors::spawn_sensors(&local, interval, sender_renderer.clone()).await; 39 | let rendering = rendering::spawn_renderer(&local, sender_sensors.clone(), receiver_renderer); 40 | let discord = register().ok(); 41 | let app = app::spawn_app( 42 | sender_renderer, 43 | sender_sensors, 44 | receiver_sensors, 45 | sender_server, 46 | Arc::new(RwLock::new(discord)), 47 | ) 48 | .await; 49 | 50 | (local, rendering, sensors, server, app) 51 | } 52 | -------------------------------------------------------------------------------- /src/services/app/events.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | lifecycle::exit, 3 | services::{rendering::message::RendererMessage, sensors::SensorMessage}, 4 | }; 5 | use discord_sdk::Discord; 6 | use smol_static::ServerMessage; 7 | use std::sync::Arc; 8 | use tachyonix::Sender; 9 | use tauri::{AppHandle, Manager, RunEvent, State}; 10 | use tokio::{runtime::Handle, sync::RwLock, task}; 11 | 12 | #[allow(unused_variables)] 13 | pub fn handle_events(app: &AppHandle, event: RunEvent) { 14 | match event { 15 | RunEvent::Exit => { 16 | let sender_renderer: State<'_, Sender> = app.state(); 17 | let sender_sensors: State<'_, Sender> = app.state(); 18 | let sender_server: State<'_, Sender> = app.state(); 19 | let discord: State<'_, Arc>>> = app.state(); 20 | 21 | task::block_in_place(move || { 22 | Handle::current().block_on(async { 23 | exit( 24 | app.get_window("main"), 25 | app, 26 | sender_renderer, 27 | sender_sensors, 28 | sender_server, 29 | discord, 30 | ) 31 | .await; 32 | }) 33 | }); 34 | } 35 | RunEvent::ExitRequested { api, .. } => { 36 | api.prevent_exit(); 37 | } 38 | RunEvent::WindowEvent { label, event, .. } => {} 39 | RunEvent::Ready => {} 40 | RunEvent::Resumed => {} 41 | RunEvent::MainEventsCleared => {} 42 | _ => {} 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /crates/ultralight/src/view/guard.rs: -------------------------------------------------------------------------------- 1 | use std::ops::{Deref, DerefMut}; 2 | 3 | pub(crate) struct ULViewGuard(pub(crate) ultralight_sys::ULView); 4 | 5 | unsafe impl Sync for ULViewGuard {} 6 | unsafe impl Send for ULViewGuard {} 7 | 8 | impl ULViewGuard { 9 | pub fn new(view: ultralight_sys::ULView) -> Self { 10 | Self(view) 11 | } 12 | } 13 | 14 | impl Drop for ULViewGuard { 15 | fn drop(&mut self) { 16 | unsafe { ultralight_sys::ulDestroyView(self.0) }; 17 | } 18 | } 19 | 20 | impl Deref for ULViewGuard { 21 | type Target = ultralight_sys::ULView; 22 | 23 | fn deref(&self) -> &Self::Target { 24 | &self.0 25 | } 26 | } 27 | 28 | impl DerefMut for ULViewGuard { 29 | fn deref_mut(&mut self) -> &mut Self::Target { 30 | &mut self.0 31 | } 32 | } 33 | 34 | pub struct ULViewConfigGuard(ultralight_sys::ULViewConfig); 35 | 36 | impl Default for ULViewConfigGuard { 37 | fn default() -> Self { 38 | Self::new() 39 | } 40 | } 41 | 42 | impl ULViewConfigGuard { 43 | pub fn new() -> Self { 44 | let config = unsafe { ultralight_sys::ulCreateViewConfig() }; 45 | assert!(!config.is_null()); 46 | Self(config) 47 | } 48 | } 49 | 50 | impl Drop for ULViewConfigGuard { 51 | fn drop(&mut self) { 52 | unsafe { ultralight_sys::ulDestroyViewConfig(self.0) }; 53 | } 54 | } 55 | 56 | impl Deref for ULViewConfigGuard { 57 | type Target = ultralight_sys::ULViewConfig; 58 | 59 | fn deref(&self) -> &Self::Target { 60 | &self.0 61 | } 62 | } 63 | 64 | impl DerefMut for ULViewConfigGuard { 65 | fn deref_mut(&mut self) -> &mut Self::Target { 66 | &mut self.0 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /crates/ultralight/shaders/vertex/v2f_c4f_t2f_d28f.hlsl: -------------------------------------------------------------------------------- 1 | cbuffer Uniforms : register(b0) 2 | { 3 | float4 State; 4 | matrix Transform; 5 | float4 Scalar4[2]; 6 | float4 Vector[8]; 7 | uint ClipSize; 8 | matrix Clip[8]; 9 | }; 10 | 11 | float4 sRGBToLinear(float4 val) { return float4(val.xyz * (val.xyz * (val.xyz * 0.305306011 + 0.682171111) + 0.012522878), val.w); } 12 | 13 | struct VS_OUTPUT 14 | { 15 | float4 Position : SV_POSITION; 16 | float4 Color : COLOR0; 17 | float2 TexCoord : TEXCOORD0; 18 | float4 Data0 : COLOR1; 19 | float4 Data1 : COLOR2; 20 | float4 Data2 : COLOR3; 21 | float4 Data3 : COLOR4; 22 | float4 Data4 : COLOR5; 23 | float4 Data5 : COLOR6; 24 | float4 Data6 : COLOR7; 25 | float2 ObjectCoord : TEXCOORD1; 26 | }; 27 | 28 | VS_OUTPUT VS(float2 Position : POSITION, 29 | uint4 Color : COLOR0, 30 | float2 TexCoord : TEXCOORD0, 31 | float2 ObjCoord : TEXCOORD1, 32 | float4 Data0 : COLOR1, 33 | float4 Data1 : COLOR2, 34 | float4 Data2 : COLOR3, 35 | float4 Data3 : COLOR4, 36 | float4 Data4 : COLOR5, 37 | float4 Data5 : COLOR6, 38 | float4 Data6 : COLOR7) 39 | { 40 | VS_OUTPUT output; 41 | output.ObjectCoord = ObjCoord; 42 | output.Position = mul(Transform, float4(Position, 0.0, 1.0)); 43 | output.Color = float4(Color) / 255.0; 44 | output.TexCoord = TexCoord; 45 | output.Data0 = Data0; 46 | output.Data1 = Data1; 47 | output.Data2 = Data2; 48 | output.Data3 = Data3; 49 | output.Data4 = Data4; 50 | output.Data5 = Data5; 51 | output.Data6 = Data6; 52 | return output; 53 | } -------------------------------------------------------------------------------- /src/themes/get_all.rs: -------------------------------------------------------------------------------- 1 | use std::path::PathBuf; 2 | 3 | use crate::utils::themes::{manifest::Theme, paths::get_default_theme_path}; 4 | use tokio::fs; 5 | 6 | #[tauri::command] 7 | pub async fn get_all_themes_handler() -> Result, String> { 8 | get_all_themes().await.map_err(|e| e.to_string()) 9 | } 10 | 11 | async fn get_all_themes() -> anyhow::Result> { 12 | let mut theme_path: PathBuf = get_default_theme_path().into(); 13 | theme_path.pop(); 14 | theme_path.pop(); 15 | theme_path.push("Themes"); 16 | let mut themes_dir = fs::read_dir(theme_path).await?; 17 | let mut themes = vec![]; 18 | 19 | while let Ok(Some(theme_dir)) = themes_dir.next_entry().await { 20 | if theme_dir.file_name() == "__DEFAULT__" { 21 | continue; 22 | } 23 | let theme_path = theme_dir.path(); 24 | let theme_manifest_path = theme_path.join("theme.json"); 25 | let theme_manifest = if let Ok(manifest) = fs::read_to_string(theme_manifest_path).await { 26 | manifest 27 | } else { 28 | continue; 29 | }; 30 | let mut theme_manifest: Theme = if let Ok(manifest) = serde_json::from_str(&theme_manifest) 31 | { 32 | manifest 33 | } else { 34 | continue; 35 | }; 36 | 37 | theme_manifest.fs_name = Some( 38 | theme_path 39 | .file_name() 40 | .unwrap() 41 | .to_str() 42 | .unwrap() 43 | .to_string(), 44 | ); 45 | 46 | if theme_path.join("index.html").exists() && theme_path.join("preview.jpg").exists() { 47 | themes.push(theme_manifest); 48 | } 49 | } 50 | 51 | Ok(themes) 52 | } 53 | -------------------------------------------------------------------------------- /crates/ultralight/src/types/gpu/vertex_buffer.rs: -------------------------------------------------------------------------------- 1 | use std::slice; 2 | 3 | use ultralight_sys::{ 4 | ULVertexBuffer, ULVertexBufferFormat, ULVertexBufferFormat_kVertexBufferFormat_2f_4ub_2f, 5 | ULVertexBufferFormat_kVertexBufferFormat_2f_4ub_2f_2f_28f, 6 | }; 7 | 8 | use crate::error::ULError; 9 | 10 | #[allow(non_camel_case_types)] 11 | pub enum VertexBufferFormat { 12 | Format_2f_4ub_2f = ULVertexBufferFormat_kVertexBufferFormat_2f_4ub_2f as isize, 13 | Format_2f_4ub_2f_2f_28f = ULVertexBufferFormat_kVertexBufferFormat_2f_4ub_2f_2f_28f as isize, 14 | } 15 | 16 | impl TryFrom for VertexBufferFormat { 17 | type Error = ULError; 18 | 19 | #[allow(non_upper_case_globals)] 20 | fn try_from(vbf: ULVertexBufferFormat) -> Result { 21 | match vbf { 22 | ULVertexBufferFormat_kVertexBufferFormat_2f_4ub_2f => { 23 | Ok(VertexBufferFormat::Format_2f_4ub_2f) 24 | } 25 | ULVertexBufferFormat_kVertexBufferFormat_2f_4ub_2f_2f_28f => { 26 | Ok(VertexBufferFormat::Format_2f_4ub_2f_2f_28f) 27 | } 28 | _ => Err(ULError::VertexBufferUnsupportedFormat), 29 | } 30 | } 31 | } 32 | 33 | pub(crate) struct VertexBuffer { 34 | pub(crate) format: VertexBufferFormat, 35 | pub(crate) buffer: Vec, 36 | } 37 | 38 | impl TryFrom for VertexBuffer { 39 | type Error = ULError; 40 | 41 | fn try_from(vb: ULVertexBuffer) -> Result { 42 | if vb.data.is_null() { 43 | return Err(ULError::VertexBufferNullReference); 44 | } 45 | let format = VertexBufferFormat::try_from(vb.format)?; 46 | let buffer = unsafe { slice::from_raw_parts(vb.data, vb.size as usize) }; 47 | Ok(VertexBuffer { 48 | format, 49 | buffer: buffer.to_vec(), 50 | }) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /crates/ultralight/src/types/bitmap/pixels.rs: -------------------------------------------------------------------------------- 1 | use std::ops::{Deref, DerefMut}; 2 | 3 | /// A trait for types that can be locked for pixel access. 4 | pub trait LockablePixels { 5 | /// Locks the pixels for access. 6 | /// 7 | /// # Safety 8 | /// - Must return a non-null pointer. 9 | /// - Must be balanced with a call to `raw_unlock_pixels`. 10 | /// - The returned pointer must be valid for the lifetime of the lock. 11 | /// - The returned pointer must be aligned to the value returned by `raw_bytes_per_row`. 12 | /// - The returned pointer must point to a buffer of at least `raw_size` bytes. 13 | unsafe fn raw_lock_pixels(&mut self) -> *mut u8; 14 | /// Unlocks the pixels. 15 | /// 16 | /// # Safety 17 | /// - Must be balanced with a call to `raw_lock_pixels`. 18 | /// - The pointer returned by `raw_lock_pixels` must be valid for the lifetime of the lock. 19 | /// - The pointer returned by `raw_lock_pixels` must be aligned to the value returned by `raw_bytes_per_row`. 20 | unsafe fn raw_unlock_pixels(&mut self); 21 | } 22 | 23 | /// A guard that locks pixels for access. 24 | pub struct PixelsGuard<'a, T: LockablePixels> { 25 | lock: &'a mut T, 26 | pixels: &'a mut [u8], 27 | } 28 | 29 | impl<'a, T: LockablePixels> PixelsGuard<'a, T> { 30 | pub(crate) unsafe fn new(lock: &'a mut T, pixels: &'a mut [u8]) -> PixelsGuard<'a, T> { 31 | PixelsGuard { lock, pixels } 32 | } 33 | } 34 | 35 | impl Deref for PixelsGuard<'_, T> { 36 | type Target = [u8]; 37 | 38 | fn deref(&self) -> &[u8] { 39 | self.pixels 40 | } 41 | } 42 | 43 | impl DerefMut for PixelsGuard<'_, T> { 44 | fn deref_mut(&mut self) -> &mut [u8] { 45 | self.pixels 46 | } 47 | } 48 | 49 | impl Drop for PixelsGuard<'_, T> { 50 | fn drop(&mut self) { 51 | unsafe { 52 | self.lock.raw_unlock_pixels(); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@zfc/frontend", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "dev": "vite dev", 7 | "build": "vite build", 8 | "preview": "vite preview", 9 | "test": "npm run test:integration && npm run test:unit", 10 | "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", 11 | "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", 12 | "lint": "prettier --check . && eslint .", 13 | "format": "prettier --write .", 14 | "test:integration": "playwright test", 15 | "test:unit": "vitest" 16 | }, 17 | "devDependencies": { 18 | "@playwright/test": "^1.28.1", 19 | "@skeletonlabs/skeleton": "2.6.0", 20 | "@skeletonlabs/tw-plugin": "0.3.0", 21 | "@steeze-ui/heroicons": "^2.2.3", 22 | "@steeze-ui/svelte-icon": "^1.5.0", 23 | "@sveltejs/adapter-auto": "^2.0.0", 24 | "@sveltejs/adapter-static": "^2.0.3", 25 | "@sveltejs/kit": "^1.27.4", 26 | "@tailwindcss/forms": "0.5.7", 27 | "@tailwindcss/typography": "0.5.10", 28 | "@tauri-apps/api": "^1.5.2", 29 | "@types/node": "20.10.4", 30 | "@typescript-eslint/eslint-plugin": "^6.0.0", 31 | "@typescript-eslint/parser": "^6.0.0", 32 | "autoprefixer": "10.4.16", 33 | "eslint": "^8.28.0", 34 | "eslint-config-prettier": "^9.0.0", 35 | "eslint-plugin-svelte": "^2.30.0", 36 | "postcss": "8.4.32", 37 | "prettier": "^3.0.0", 38 | "prettier-plugin-svelte": "^3.0.0", 39 | "svelte": "^4.2.7", 40 | "svelte-check": "^3.6.0", 41 | "tailwindcss": "3.3.6", 42 | "ts-node": "^10.9.2", 43 | "tslib": "^2.4.1", 44 | "typescript": "^5.0.0", 45 | "vite": "^4.4.2", 46 | "vite-plugin-tailwind-purgecss": "0.1.4", 47 | "vitest": "^0.32.2" 48 | }, 49 | "type": "module", 50 | "dependencies": { 51 | "@floating-ui/dom": "1.5.3", 52 | "svelte-markdown": "^0.4.0", 53 | "tauri-plugin-context-menu": "^0.6.0", 54 | "tauri-plugin-log-api": "github:tauri-apps/tauri-plugin-log#v1", 55 | "tauri-plugin-window-state-api": "github:tauri-apps/tauri-plugin-window-state#v1" 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/services/discord/presence.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Context; 2 | use discord_sdk::{ 3 | activity::{ActivityBuilder, Assets, Button}, 4 | Discord, 5 | }; 6 | use std::sync::Arc; 7 | use tauri::State; 8 | use tokio::sync::RwLock; 9 | 10 | #[tauri::command] 11 | pub async fn activity_handler( 12 | set_to: String, 13 | discord: State<'_, Arc>>>, 14 | ) -> Result<(), String> { 15 | match set_to.as_str() { 16 | "browsing_themes" => { 17 | set_to_browsing_themes(&discord) 18 | .await 19 | .map_err(|e| e.to_string())?; 20 | } 21 | "clear" => { 22 | clear_activity(&discord).await.map_err(|e| e.to_string())?; 23 | } 24 | _ => { 25 | return Err(format!("Unknown activity: {}", set_to)); 26 | } 27 | } 28 | 29 | Ok(()) 30 | } 31 | 32 | pub async fn set_to_browsing_themes(discord: &Arc>>) -> anyhow::Result<()> { 33 | if let Some(discord) = discord.read().await.as_ref() { 34 | let activity = ActivityBuilder::new() 35 | .state("Browsing themes.") 36 | .details("Currently checking out themes.") 37 | .button(Button { 38 | label: "Check it out yourself!".into(), 39 | url: "https://zefirsflashycooler.app".into(), 40 | }) 41 | .assets(Assets { 42 | large_image: Some("default-img".into()), 43 | large_text: Some("Zefir's Flashy Cooler".into()), 44 | ..Default::default() 45 | }); 46 | 47 | discord 48 | .update_activity(activity) 49 | .await 50 | .context("Failed to update activity!")?; 51 | } 52 | 53 | Ok(()) 54 | } 55 | 56 | pub async fn clear_activity(discord: &Arc>>) -> anyhow::Result<()> { 57 | if let Some(discord) = discord.read().await.as_ref() { 58 | discord 59 | .clear_activity() 60 | .await 61 | .context("Failed to clear activity!")?; 62 | } 63 | 64 | Ok(()) 65 | } 66 | -------------------------------------------------------------------------------- /crates/lcd-coolers/src/corsair/h150i/creator.rs: -------------------------------------------------------------------------------- 1 | use super::{ 2 | constants::{PRODUCT_ID, PRODUCT_ID_V2, VENDOR_ID}, 3 | counter::Counter, 4 | device::CorsairH150i, 5 | }; 6 | use crate::{traits::device_creator::DeviceCreator, DeviceInfo}; 7 | use anyhow::Context; 8 | use hidapi::HidApi; 9 | use tokio::task; 10 | use turbojpeg::{Compressor, Image, OutputBuf, PixelFormat, Subsamp}; 11 | 12 | impl<'a> DeviceCreator for CorsairH150i<'a> { 13 | async fn create_device() -> anyhow::Result 14 | where 15 | Self: Sized, 16 | { 17 | let (api, device) = task::spawn_blocking(|| -> anyhow::Result<_> { 18 | let api = HidApi::new().context("Failed to get HID API")?; 19 | let device = api 20 | .open(VENDOR_ID, PRODUCT_ID) 21 | .or_else(|_| api.open(VENDOR_ID, PRODUCT_ID_V2)) 22 | .context("Failed to open device")?; 23 | 24 | Ok((api, device)) 25 | }) 26 | .await??; 27 | 28 | let mut compressor = Compressor::new().context("Failed to create compressor")?; 29 | compressor.set_quality(95); 30 | compressor.set_subsamp(Subsamp::Sub2x2); 31 | 32 | Ok(Self { 33 | api, 34 | device, 35 | image_frame: Image { 36 | pixels: &[], 37 | width: 480, 38 | height: 480, 39 | pitch: 480 * 4, 40 | format: PixelFormat::RGBA, 41 | }, 42 | compression_buffer: OutputBuf::allocate_owned( 43 | compressor 44 | .buf_len(480, 480) 45 | .context("Failed to get buffer length")?, 46 | ), 47 | compressor, 48 | packet: Vec::with_capacity(1024), 49 | unfuck_counter: Counter::new(1000 * 29), 50 | }) 51 | } 52 | 53 | async fn device_info(&self) -> DeviceInfo { 54 | DeviceInfo { 55 | name: "Capellix LCD Cap".into(), 56 | width: 480, 57 | height: 480, 58 | manufacturer: "Corsair".into(), 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /crates/lcd-coolers/src/corsair/h150i/tests.rs: -------------------------------------------------------------------------------- 1 | use crate::traits::{device_creator::DeviceCreator, display_cooler::DisplayCooler}; 2 | 3 | use super::device::CorsairH150i; 4 | 5 | #[test] 6 | fn creates_a_device() { 7 | tokio::runtime::Builder::new_multi_thread() 8 | .build() 9 | .unwrap() 10 | .block_on(async { 11 | let _ = CorsairH150i::create_device().await.unwrap(); 12 | }); 13 | } 14 | 15 | #[test] 16 | fn initialises_a_device() { 17 | tokio::runtime::Builder::new_multi_thread() 18 | .enable_time() 19 | .build() 20 | .unwrap() 21 | .block_on(async { 22 | let mut device = CorsairH150i::create_device().await.unwrap(); 23 | device.initialise().await.unwrap(); 24 | }); 25 | } 26 | 27 | #[test] 28 | fn sends_red_rgba_image() { 29 | tokio::runtime::Builder::new_multi_thread() 30 | .enable_time() 31 | .build() 32 | .unwrap() 33 | .block_on(async { 34 | let mut device = CorsairH150i::create_device().await.unwrap(); 35 | device.initialise().await.unwrap(); 36 | 37 | let image: Vec = vec![&[255u8, 0u8, 0u8, 255u8]; 480 * 480 * 4] 38 | .into_iter() 39 | .flatten() 40 | .copied() 41 | .collect(); 42 | device.send_image(&image).await.unwrap(); 43 | }); 44 | } 45 | 46 | #[test] 47 | fn sends_green_rgba_image_and_closes() { 48 | tokio::runtime::Builder::new_multi_thread() 49 | .enable_time() 50 | .build() 51 | .unwrap() 52 | .block_on(async { 53 | let mut device = CorsairH150i::create_device().await.unwrap(); 54 | device.initialise().await.unwrap(); 55 | 56 | let image: Vec = vec![&[0u8, 255u8, 0u8, 255u8]; 480 * 480 * 4] 57 | .into_iter() 58 | .flatten() 59 | .copied() 60 | .collect(); 61 | device.send_image(&image).await.unwrap(); 62 | 63 | tokio::time::sleep(tokio::time::Duration::from_secs(2)).await; 64 | 65 | device.close().await.unwrap(); 66 | }); 67 | } 68 | -------------------------------------------------------------------------------- /crates/librehardwaremonitor-rs/src/computer/impl.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::CStr; 2 | 3 | use crate::{ComputerHardwareIter, ComputerParams, Hardware, Sensor}; 4 | 5 | pub struct Computer { 6 | pub(crate) id: i32, 7 | } 8 | 9 | impl Default for Computer { 10 | fn default() -> Self { 11 | Self::new() 12 | } 13 | } 14 | 15 | impl Drop for Computer { 16 | fn drop(&mut self) { 17 | unsafe { librehardwaremonitor_sys::destroy_computer_object(self.id) }; 18 | } 19 | } 20 | 21 | impl Computer { 22 | pub fn new() -> Self { 23 | let id = unsafe { librehardwaremonitor_sys::create_computer_object() }; 24 | 25 | Self { id } 26 | } 27 | 28 | pub fn new_with_params(params: ComputerParams) -> Self { 29 | let id = unsafe { librehardwaremonitor_sys::create_computer_object() }; 30 | let mut new_me = Self { id }; 31 | 32 | new_me.set_params(params); 33 | 34 | new_me 35 | } 36 | 37 | pub fn update(&mut self) { 38 | unsafe { librehardwaremonitor_sys::update_computer_object(self.id) }; 39 | } 40 | 41 | pub fn reset(&mut self) { 42 | unsafe { librehardwaremonitor_sys::reset_computer_object(self.id) }; 43 | } 44 | 45 | pub fn get_report(&mut self) -> Option { 46 | let report_ptr = unsafe { librehardwaremonitor_sys::get_computer_report(self.id) }; 47 | 48 | if report_ptr.is_null() { 49 | None 50 | } else { 51 | let report = unsafe { CStr::from_ptr(report_ptr as _) } 52 | .to_string_lossy() 53 | .into_owned(); 54 | 55 | unsafe { librehardwaremonitor_sys::free_dotnet_string(report_ptr) }; 56 | 57 | Some(report) 58 | } 59 | } 60 | 61 | pub fn get_hardware_len(&self) -> usize { 62 | unsafe { librehardwaremonitor_sys::get_computer_hardware_len(self.id) } 63 | .try_into() 64 | .unwrap() 65 | } 66 | 67 | pub fn iter(&self) -> ComputerHardwareIter { 68 | let len = self.get_hardware_len(); 69 | 70 | ComputerHardwareIter { 71 | inner: self, 72 | index: 0, 73 | len, 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /frontend/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { purgeCss } from "vite-plugin-tailwind-purgecss"; 2 | import { sveltekit } from "@sveltejs/kit/vite"; 3 | import { defineConfig } from "vite"; 4 | import * as fs from "fs"; 5 | 6 | export default defineConfig({ 7 | plugins: [sveltekit(), purgeCss()], 8 | define: { 9 | __UI_VERSION__: getUIVersion(), 10 | __APP_VERSION__: getAppVersion(), 11 | __BACKEND_VERSION__: getBackendVersion(), 12 | __LIBRE_VERSION__: getLibreVesion(), 13 | __UL_VERSION__: getULVersion(), 14 | }, 15 | }); 16 | 17 | function getAppVersion() { 18 | try { 19 | const tauriConfig = JSON.parse(fs.readFileSync("../tauri.conf.json", "utf-8")); 20 | return `"${tauriConfig.package.version}"`; 21 | } catch { 22 | return `"0.0.0"`; 23 | } 24 | } 25 | 26 | function getBackendVersion() { 27 | try { 28 | const cargoToml = fs.readFileSync("../Cargo.toml", "utf-8"); 29 | const regex = /version = "([0-9.]+)"/; 30 | const matches = cargoToml.match(regex); 31 | if (matches) { 32 | return `"${matches[1]}"`; 33 | } else { 34 | return `"0.0.0"`; 35 | } 36 | } catch { 37 | return `"0.0.0"`; 38 | } 39 | } 40 | 41 | function getLibreVesion() { 42 | try { 43 | const cargoToml = fs.readFileSync("../crates/librehardwaremonitor-rs/Cargo.toml", "utf-8"); 44 | const regex = /version = "([0-9.]+)"/; 45 | const matches = cargoToml.match(regex); 46 | if (matches) { 47 | return `"${matches[1]}"`; 48 | } else { 49 | return `"0.0.0"`; 50 | } 51 | } catch { 52 | return `"0.0.0"`; 53 | } 54 | } 55 | 56 | function getULVersion() { 57 | try { 58 | const cargoToml = fs.readFileSync("../crates/ultralight/Cargo.toml", "utf-8"); 59 | const regex = /version = "([0-9.]+)"/; 60 | const matches = cargoToml.match(regex); 61 | if (matches) { 62 | return `"${matches[1]}"`; 63 | } else { 64 | return `"0.0.0"`; 65 | } 66 | } catch { 67 | return `"0.0.0"`; 68 | } 69 | } 70 | 71 | function getUIVersion() { 72 | try { 73 | const packageJson = JSON.parse(fs.readFileSync("./package.json", "utf-8")); 74 | const matches = packageJson.version; 75 | if (matches) { 76 | return `"${matches}"`; 77 | } else { 78 | return `"0.0.0"`; 79 | } 80 | } catch { 81 | return `"0.0.0"`; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /crates/ultralight/src/types/gpu/gpu_state.rs: -------------------------------------------------------------------------------- 1 | use ultralight_sys::ULGPUState; 2 | 3 | use crate::error::ULError; 4 | 5 | use super::{from_ul_arr, rect::Rect, shader_type::ShaderType}; 6 | 7 | #[derive(Debug)] 8 | pub struct GPUState { 9 | pub viewport_width: u32, 10 | pub viewport_height: u32, 11 | pub transform: [f32; 16], 12 | pub enable_texturing: bool, 13 | pub enable_blend: bool, 14 | pub shader_type: ShaderType, 15 | pub render_buffer_id: u32, 16 | pub texture_1_id: Option, 17 | pub texture_2_id: Option, 18 | pub texture_3_id: Option, 19 | pub uniform_scalar: [f32; 8], 20 | pub uniform_vector: [[f32; 4]; 8], 21 | pub clip_size: u8, 22 | pub clip: [[[f32; 4]; 4]; 8], 23 | pub enable_scissor: bool, 24 | pub scissor_rect: Rect, 25 | } 26 | 27 | impl TryFrom for GPUState { 28 | type Error = ULError; 29 | 30 | #[inline] 31 | fn try_from(gs: ULGPUState) -> Result { 32 | Ok(GPUState { 33 | viewport_width: gs.viewport_width, 34 | viewport_height: gs.viewport_height, 35 | transform: gs.transform.data, 36 | enable_texturing: gs.enable_texturing, 37 | enable_blend: gs.enable_blend, 38 | shader_type: ShaderType::try_from(gs.shader_type as i32)?, 39 | render_buffer_id: gs.render_buffer_id, 40 | texture_1_id: if gs.texture_1_id == 0 { 41 | None 42 | } else { 43 | Some(gs.texture_1_id) 44 | }, 45 | texture_2_id: if gs.texture_2_id == 0 { 46 | None 47 | } else { 48 | Some(gs.texture_2_id) 49 | }, 50 | texture_3_id: if gs.texture_3_id == 0 { 51 | None 52 | } else { 53 | Some(gs.texture_3_id) 54 | }, 55 | uniform_scalar: gs.uniform_scalar, 56 | uniform_vector: from_ul_arr!(gs.uniform_vector, value), 57 | clip_size: gs.clip_size, 58 | clip: from_ul_arr!(mat gs.clip, data), 59 | enable_scissor: gs.enable_scissor, 60 | scissor_rect: Rect::from(gs.scissor_rect), 61 | }) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "app" 3 | version = "0.1.0" 4 | description = "A Tauri App" 5 | authors = ["Bruno St John"] 6 | license = "" 7 | repository = "" 8 | default-run = "app" 9 | edition = "2021" 10 | rust-version = "1.60" 11 | 12 | [build-dependencies] 13 | tauri-build = { version = "1.5.0", features = [] } 14 | dotnetaot-build = { path = "./crates/dotnetaot-build" } 15 | anyhow = "1.0.75" 16 | ultralight-build = { path = "./crates/ultralight-build" } 17 | 18 | [dependencies] 19 | serde_json = "1.0" 20 | serde = { version = "1.0", features = ["derive"] } 21 | tauri = { version = "1.5.3", features = [ 22 | "path-all", 23 | "fs-read-file", 24 | "protocol-asset", 25 | "fs-read-dir", 26 | "shell-open", 27 | "window-unmaximize", 28 | "window-unminimize", 29 | "window-minimize", 30 | "window-close", 31 | "window-maximize", 32 | "window-start-dragging", 33 | "window-show", 34 | "window-hide", 35 | "system-tray", 36 | ] } 37 | tokio = { version = "1.35.0", features = ["full"] } 38 | windows = { version = "0.52.0", features = ["Win32_Foundation"] } 39 | sevenz-rust = "0.5.3" 40 | window-shadows = "0.2.2" 41 | anyhow = "1.0.75" 42 | ultralight = { package = "ultralight-rs", path = "./crates/ultralight" } 43 | lcd-coolers = { path = "./crates/lcd-coolers" } 44 | librehardwaremonitor-rs = { path = "./crates/librehardwaremonitor-rs" } 45 | tachyonix = "0.2.1" 46 | smol-static = { path = "./crates/smol-static" } 47 | confy = "0.5.1" 48 | tauri-plugin-context-menu = "0.6.1" 49 | tauri-plugin-log = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" } 50 | tauri-plugin-window-state = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" } 51 | tauri-plugin-single-instance = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" } 52 | log = "^0.4" 53 | bincode = "1.3.3" 54 | discord-sdk = "0.3.5" 55 | async-trait = "0.1.74" 56 | 57 | 58 | [features] 59 | custom-protocol = ["tauri/custom-protocol"] 60 | 61 | [workspace] 62 | members = ["crates/*"] 63 | 64 | [profile.release] 65 | lto = "fat" 66 | opt-level = 3 67 | codegen-units = 1 68 | debug = false 69 | panic = "abort" 70 | 71 | [profile.release.package."*"] 72 | opt-level = 3 73 | codegen-units = 1 74 | debug = false 75 | -------------------------------------------------------------------------------- /frontend/src/lib/components/themes/ThemeControls.svelte: -------------------------------------------------------------------------------- 1 | 47 | 48 |
49 | 50 |
51 | {#if isThemePlaying} 52 | goto("/now-playing")}>Now Playing 57 | {:else} 58 | Play 61 | {/if} 62 | Uninstall 67 |
68 |
69 | -------------------------------------------------------------------------------- /crates/ultralight/src/view/load_future.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | future::Future, 3 | os::raw::c_void, 4 | sync::atomic::{AtomicBool, Ordering}, 5 | }; 6 | 7 | use ultralight_sys::{ulRender, ulUpdate}; 8 | 9 | use crate::{error::ULError, ULResult}; 10 | 11 | pub(super) static IS_LOADED: AtomicBool = AtomicBool::new(false); 12 | pub(super) static FAILED_LOADING: AtomicBool = AtomicBool::new(false); 13 | 14 | #[allow(unused_variables)] 15 | pub(super) unsafe extern "C" fn done_loading( 16 | user_data: *mut c_void, 17 | caller: ultralight_sys::ULView, 18 | frame_id: u64, 19 | is_main_frame: bool, 20 | url: ultralight_sys::ULString, 21 | ) { 22 | IS_LOADED.store(true, Ordering::Release); 23 | } 24 | 25 | #[allow(unused_variables)] 26 | pub(super) unsafe extern "C" fn failed_loading( 27 | user_data: *mut c_void, 28 | caller: ultralight_sys::ULView, 29 | frame_id: u64, 30 | is_main_frame: bool, 31 | url: ultralight_sys::ULString, 32 | description: ultralight_sys::ULString, 33 | error_domain: ultralight_sys::ULString, 34 | error_code: i32, 35 | ) { 36 | FAILED_LOADING.store(true, Ordering::Release); 37 | } 38 | 39 | pub(crate) struct LoadFutureContainer(pub(crate) ultralight_sys::ULRenderer); 40 | 41 | unsafe impl Send for LoadFutureContainer {} 42 | unsafe impl Sync for LoadFutureContainer {} 43 | 44 | pub struct LoadFuture<'a> { 45 | pub(super) renderer: &'a LoadFutureContainer, 46 | } 47 | 48 | impl<'a> Future for LoadFuture<'a> { 49 | type Output = ULResult<()>; 50 | 51 | fn poll( 52 | self: std::pin::Pin<&mut Self>, 53 | cx: &mut std::task::Context<'_>, 54 | ) -> std::task::Poll { 55 | if IS_LOADED.load(Ordering::Acquire) { 56 | IS_LOADED.store(false, Ordering::Release); 57 | std::task::Poll::Ready(Ok(())) 58 | } else if FAILED_LOADING.load(Ordering::Acquire) { 59 | FAILED_LOADING.store(false, Ordering::Release); 60 | std::task::Poll::Ready(Err(ULError::FailedToLoadWebpage)) 61 | } else { 62 | cx.waker().wake_by_ref(); 63 | unsafe { ulUpdate(self.renderer.0) }; 64 | // unsafe { ulRender(self.renderer.0) }; 65 | std::task::Poll::Pending 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/services/rendering/config.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | services::sensors::SensorMessage, 3 | utils::themes::{config::ThemeConfigItem, paths::get_all_themes_path}, 4 | }; 5 | use anyhow::Context; 6 | use tachyonix::Sender; 7 | use tokio::fs; 8 | use ultralight::ULView; 9 | 10 | pub async fn load_theme_with_config( 11 | view: &mut ULView<'_>, 12 | fs_name: &str, 13 | sensor_sender: &Sender, 14 | ) -> anyhow::Result<()> { 15 | view.reload(); 16 | 17 | // load non-sensor values 18 | let theme_config_path = get_all_themes_path().join(fs_name).join("config.json"); 19 | let theme_config_unparsed = fs::read_to_string(theme_config_path) 20 | .await 21 | .context("Failed to read theme config file!")?; 22 | let theme_config = serde_json::from_str::>(&theme_config_unparsed) 23 | .context("Failed to parse theme config file!")?; 24 | let mut event_string = String::from("{"); 25 | theme_config 26 | .iter() 27 | .filter(|item| item.r#type != "sensor") 28 | .flat_map(serde_json::to_string) 29 | .zip(theme_config.iter().filter(|item| item.r#type != "sensor")) 30 | .for_each(|(serialised, item)| { 31 | event_string.push('\"'); 32 | event_string.push_str(&item.name); 33 | event_string.push_str("\":"); 34 | event_string.push_str(&serialised); 35 | event_string.push(','); 36 | }); 37 | event_string.pop(); 38 | event_string.push('}'); 39 | 40 | let script = format!( 41 | "document.dispatchEvent(new CustomEvent('configLoaded', {{ detail: JSON.parse('{}') }}));", 42 | event_string 43 | ); 44 | 45 | view.evaluate_script(script); 46 | 47 | // load sensor values 48 | let sensor_values = theme_config 49 | .iter() 50 | .filter(|item| item.r#type == "sensor" && item.path.is_some()) 51 | .map( 52 | |ThemeConfigItem { 53 | value, name, path, .. 54 | }| (value, name, path.as_ref().unwrap()), 55 | ) 56 | .collect::>(); 57 | 58 | Ok(()) 59 | } 60 | 61 | async fn subscribe_to_sensors( 62 | values: &Vec<(&String, &String, &String)>, 63 | sensor_sender: &Sender, 64 | ) { 65 | // 66 | } 67 | -------------------------------------------------------------------------------- /crates/librehardwaremonitor-rs/src/types/hardware_type.rs: -------------------------------------------------------------------------------- 1 | use librehardwaremonitor_sys::HardwareType as HardwareTypeNative; 2 | 3 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 4 | pub enum HardwareType { 5 | Motherboard, 6 | SuperIO, 7 | Cpu, 8 | Memory, 9 | GpuNvidia, 10 | GpuAmd, 11 | GpuIntel, 12 | Storage, 13 | Network, 14 | Cooler, 15 | EmbeddedController, 16 | Psu, 17 | Battery, 18 | } 19 | 20 | impl From for HardwareType { 21 | fn from(hardware_type: HardwareTypeNative) -> Self { 22 | match hardware_type { 23 | HardwareTypeNative::Motherboard => Self::Motherboard, 24 | HardwareTypeNative::SuperIO => Self::SuperIO, 25 | HardwareTypeNative::Cpu => Self::Cpu, 26 | HardwareTypeNative::Memory => Self::Memory, 27 | HardwareTypeNative::GpuNvidia => Self::GpuNvidia, 28 | HardwareTypeNative::GpuAmd => Self::GpuAmd, 29 | HardwareTypeNative::GpuIntel => Self::GpuIntel, 30 | HardwareTypeNative::Storage => Self::Storage, 31 | HardwareTypeNative::Network => Self::Network, 32 | HardwareTypeNative::Cooler => Self::Cooler, 33 | HardwareTypeNative::EmbeddedController => Self::EmbeddedController, 34 | HardwareTypeNative::Psu => Self::Psu, 35 | HardwareTypeNative::Battery => Self::Battery, 36 | } 37 | } 38 | } 39 | 40 | impl From for HardwareTypeNative { 41 | fn from(hardware_type: HardwareType) -> Self { 42 | match hardware_type { 43 | HardwareType::Motherboard => Self::Motherboard, 44 | HardwareType::SuperIO => Self::SuperIO, 45 | HardwareType::Cpu => Self::Cpu, 46 | HardwareType::Memory => Self::Memory, 47 | HardwareType::GpuNvidia => Self::GpuNvidia, 48 | HardwareType::GpuAmd => Self::GpuAmd, 49 | HardwareType::GpuIntel => Self::GpuIntel, 50 | HardwareType::Storage => Self::Storage, 51 | HardwareType::Network => Self::Network, 52 | HardwareType::Cooler => Self::Cooler, 53 | HardwareType::EmbeddedController => Self::EmbeddedController, 54 | HardwareType::Psu => Self::Psu, 55 | HardwareType::Battery => Self::Battery, 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /crates/ultralight/src/types/error.rs: -------------------------------------------------------------------------------- 1 | use thiserror::Error; 2 | 3 | #[derive(Error, Debug)] 4 | pub enum ULError { 5 | #[error("Failed to start remote inspector server")] 6 | FailedToStartInspectorServer, 7 | #[error("Failed to load webpage")] 8 | FailedToLoadWebpage, 9 | #[error("Creation of bitmap failed because Ultralight returned a null pointer")] 10 | BitmapNullReference, 11 | #[error( 12 | "Creation of bitmap failed because it required {required} bytes, but got {got} bytes only" 13 | )] 14 | BitmapPixelBufferSizeMismatch { got: usize, required: usize }, 15 | #[error("Tried to swap red and blue channels on an unsupported format")] 16 | BitmapUnsupportedOperationForPixelFormat, 17 | #[error("Could not write bitmap to PNG successfully")] 18 | BitmapFailedPngWrite, 19 | #[error("Could not create bitmap because its empty")] 20 | BitmapEmptyBitmap, 21 | #[error("Could not create bitmap because its format is not supported")] 22 | BitmapUnsupportedFormat, 23 | #[error("Could not save bitmap to file because of invalid path")] 24 | BitmapPNGInvalidPath, 25 | #[error("This is an unsupported operation for an owned bitmap")] 26 | BitmapUnsupportedOperationForOwnedBitmap, 27 | #[error("Failed to convert vertex buffer format due to an unsupported one being provided")] 28 | VertexBufferUnsupportedFormat, 29 | #[error("Failed to convert vertex buffer due to a null pointer being provided")] 30 | VertexBufferNullReference, 31 | #[error("Failed to convert shader type due to an unsupported one being provided")] 32 | ShaderUnsupportedType, 33 | #[error("Failed to convert GPU command due to an unsupported one being provided")] 34 | GPUCommandUnsupportedType, 35 | #[error("Failed to start GPU driver due to a lack of a supported GPU adapter")] 36 | GPUDriverNoCompatibleAdapter, 37 | #[error("Failed to start GPU driver due to a lack of a supported GPU device")] 38 | GPUDriverNoCompatibleDevice, 39 | #[error("Failed to perform operation as it is unsupported for a borrowed bitmap")] 40 | BitmapUnsupportedOperationForBorrowedBitmap, 41 | #[error("Failed to get geometry from map")] 42 | GPUFailedToGetGeometry, 43 | #[error("Failed to get render buffer from map")] 44 | GPUFailedToGetRenderBuffer, 45 | #[error("JS value type not known")] 46 | JSValueTypeNotKnown, 47 | #[error("JS value type invalid for this cast")] 48 | JSInvalidCast, 49 | } 50 | -------------------------------------------------------------------------------- /crates/ultralight/src/view/tests.rs: -------------------------------------------------------------------------------- 1 | use crate::{ULPlatformBuilder, ULRendererBuilder}; 2 | use rusty_fork::rusty_fork_test; 3 | 4 | use super::*; 5 | 6 | rusty_fork_test! { 7 | #[test] 8 | fn creates_view() { 9 | ULPlatformBuilder::new() 10 | .enable_file_logger("./logs.txt") 11 | .enable_platform_file_system() 12 | .enable_platform_font_loader() 13 | .build(); 14 | let renderer = ULRendererBuilder::new() 15 | .set_resource_path_prefix({ 16 | let mut newthing = std::env::current_dir().unwrap(); 17 | newthing.pop(); 18 | newthing.pop(); 19 | newthing.push("target"); 20 | newthing.push("debug"); 21 | newthing.push("deps"); 22 | newthing.push("resources\\"); 23 | 24 | newthing 25 | }) 26 | .build(); 27 | let _ = ULViewBuilder::new(&renderer) 28 | .set_width(480) 29 | .set_height(480) 30 | .build(); 31 | } 32 | 33 | #[test] 34 | fn loads_with_future() { 35 | tokio::runtime::Builder::new_multi_thread() 36 | .enable_all() 37 | .build() 38 | .unwrap() 39 | .block_on(async { 40 | ULPlatformBuilder::new() 41 | .enable_file_logger("./logs.txt") 42 | .enable_platform_file_system() 43 | .enable_platform_font_loader() 44 | .build(); 45 | let renderer = ULRendererBuilder::new() 46 | .set_resource_path_prefix({ 47 | let mut newthing = std::env::current_dir().unwrap(); 48 | newthing.pop(); 49 | newthing.pop(); 50 | newthing.push("target"); 51 | newthing.push("debug"); 52 | newthing.push("deps"); 53 | newthing.push("resources\\"); 54 | 55 | newthing 56 | }) 57 | .build(); 58 | let mut view = ULViewBuilder::new(&renderer) 59 | .set_width(480) 60 | .set_height(480) 61 | .build(); 62 | 63 | view.load_url("https://www.google.com") 64 | .await 65 | .expect("Failed to load URL"); 66 | }) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /frontend/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "prettier.documentSelectors": ["**/*.svelte"], 3 | "tailwindCSS.classAttributes": [ 4 | "class", 5 | "accent", 6 | "active", 7 | "aspectRatio", 8 | "background", 9 | "badge", 10 | "bgBackdrop", 11 | "bgDark", 12 | "bgDrawer", 13 | "bgLight", 14 | "blur", 15 | "border", 16 | "button", 17 | "buttonAction", 18 | "buttonBack", 19 | "buttonClasses", 20 | "buttonComplete", 21 | "buttonDismiss", 22 | "buttonNeutral", 23 | "buttonNext", 24 | "buttonPositive", 25 | "buttonTextCancel", 26 | "buttonTextConfirm", 27 | "buttonTextFirst", 28 | "buttonTextLast", 29 | "buttonTextNext", 30 | "buttonTextPrevious", 31 | "buttonTextSubmit", 32 | "caretClosed", 33 | "caretOpen", 34 | "chips", 35 | "color", 36 | "controlSeparator", 37 | "controlVariant", 38 | "cursor", 39 | "display", 40 | "element", 41 | "fill", 42 | "fillDark", 43 | "fillLight", 44 | "flex", 45 | "gap", 46 | "gridColumns", 47 | "height", 48 | "hover", 49 | "inactive", 50 | "indent", 51 | "justify", 52 | "meter", 53 | "padding", 54 | "position", 55 | "regionAnchor", 56 | "regionBackdrop", 57 | "regionBody", 58 | "regionCaption", 59 | "regionCaret", 60 | "regionCell", 61 | "regionChildren", 62 | "regionChipList", 63 | "regionChipWrapper", 64 | "regionCone", 65 | "regionContent", 66 | "regionControl", 67 | "regionDefault", 68 | "regionDrawer", 69 | "regionFoot", 70 | "regionFootCell", 71 | "regionFooter", 72 | "regionHead", 73 | "regionHeadCell", 74 | "regionHeader", 75 | "regionIcon", 76 | "regionInput", 77 | "regionInterface", 78 | "regionInterfaceText", 79 | "regionLabel", 80 | "regionLead", 81 | "regionLegend", 82 | "regionList", 83 | "regionListItem", 84 | "regionNavigation", 85 | "regionPage", 86 | "regionPanel", 87 | "regionRowHeadline", 88 | "regionRowMain", 89 | "regionSummary", 90 | "regionSymbol", 91 | "regionTab", 92 | "regionTrail", 93 | "ring", 94 | "rounded", 95 | "select", 96 | "shadow", 97 | "slotDefault", 98 | "slotFooter", 99 | "slotHeader", 100 | "slotLead", 101 | "slotMessage", 102 | "slotMeta", 103 | "slotPageContent", 104 | "slotPageFooter", 105 | "slotPageHeader", 106 | "slotSidebarLeft", 107 | "slotSidebarRight", 108 | "slotTrail", 109 | "spacing", 110 | "text", 111 | "track", 112 | "transition", 113 | "width", 114 | "zIndex" 115 | ] 116 | } 117 | -------------------------------------------------------------------------------- /crates/ultralight/src/types/gpu/worker_command.rs: -------------------------------------------------------------------------------- 1 | use ultralight_sys::{ULVertex_2f_4ub_2f, ULVertex_2f_4ub_2f_2f_28f}; 2 | use wgpu::{ 3 | util::{BufferInitDescriptor, DeviceExt}, 4 | Buffer, BufferUsages, 5 | }; 6 | use wgpu_async::AsyncDevice; 7 | 8 | use crate::bitmap::Bitmap; 9 | 10 | use super::{gpu_command::GPUCommand, index_buffer::IndexBuffer, render_buffer::RenderBuffer}; 11 | 12 | #[derive(Debug)] 13 | pub enum GPUDriverCommand { 14 | CreateTexture(u32, Bitmap), 15 | UpdateTexture(u32, Bitmap), 16 | DestroyTexture(u32), 17 | CreateRenderBuffer(u32, RenderBuffer), 18 | DestroyRenderBuffer(u32), 19 | CreateGeometry(u32, DriverVertexBuffer, IndexBuffer), 20 | UpdateGeometry(u32, DriverVertexBuffer, IndexBuffer), 21 | DestroyGeometry(u32), 22 | UpdateCommandList(Vec), 23 | } 24 | 25 | #[derive(Debug)] 26 | pub enum DriverVertexBuffer { 27 | Format2f4ub2f(Vec), 28 | Format2f4ub2f2f28f(Vec), 29 | } 30 | 31 | impl DriverVertexBuffer { 32 | pub(crate) fn size(&self) -> usize { 33 | match self { 34 | DriverVertexBuffer::Format2f4ub2f(v) => { 35 | v.len() * std::mem::size_of::() 36 | } 37 | DriverVertexBuffer::Format2f4ub2f2f28f(v) => { 38 | v.len() * std::mem::size_of::() 39 | } 40 | } 41 | } 42 | 43 | pub(crate) fn into_gpu_buffer(self, device: &AsyncDevice) -> Buffer { 44 | match self { 45 | DriverVertexBuffer::Format2f4ub2f(v) => { 46 | let (head, body, _tail) = unsafe { v.align_to::() }; 47 | assert!(head.is_empty(), "ULVertex_2f_4ub_2f is not aligned"); 48 | device.create_buffer_init(&BufferInitDescriptor { 49 | label: Some("vertex_buffer 2f_4ub_2f"), 50 | usage: BufferUsages::VERTEX, 51 | contents: body, 52 | }) 53 | } 54 | DriverVertexBuffer::Format2f4ub2f2f28f(v) => { 55 | let (head, body, _tail) = unsafe { v.align_to::() }; 56 | assert!(head.is_empty(), "ULVertex_2f_4ub_2f_2f_28f is not aligned"); 57 | device.create_buffer_init(&BufferInitDescriptor { 58 | label: Some("vertex_buffer 2f_4ub_2f_2f_28f"), 59 | usage: BufferUsages::VERTEX, 60 | contents: body, 61 | }) 62 | } 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /crates/ultralight/src/js/value.rs: -------------------------------------------------------------------------------- 1 | use crate::context::JSContext; 2 | 3 | use super::types::{ 4 | IsJSValue, JSBoolean, JSNull, JSNumber, JSObject, JSOpaque, JSString, JSType, JSUndefined, 5 | }; 6 | 7 | pub struct JSValue { 8 | pub(crate) internal: T, 9 | #[allow(unused)] 10 | pub(crate) value: ultralight_sys::JSValueRef, 11 | } 12 | 13 | impl JSValue { 14 | pub(crate) fn new(internal: T) -> Self { 15 | let value = internal.get_value(); 16 | Self { internal, value } 17 | } 18 | } 19 | 20 | impl<'a> JSValue> { 21 | pub(crate) fn new_opaque( 22 | internal: ultralight_sys::JSValueRef, 23 | guard: &'a JSContext<'_>, 24 | ) -> Self { 25 | Self { 26 | internal: JSOpaque { internal, guard }, 27 | value: internal, 28 | } 29 | } 30 | 31 | pub fn get_type(&self) -> JSType { 32 | let guard = self.internal.guard; 33 | let internal = self.internal.internal; 34 | let value_type: JSType = unsafe { ultralight_sys::JSValueGetType(**guard, internal) } 35 | .try_into() 36 | .unwrap(); 37 | 38 | value_type 39 | } 40 | 41 | pub unsafe fn cast_undefined(self) -> JSValue> { 42 | JSValue::new(JSUndefined { 43 | internal: self.internal.internal, 44 | guard: self.internal.guard, 45 | }) 46 | } 47 | 48 | pub unsafe fn cast_null(self) -> JSValue> { 49 | JSValue::new(JSNull { 50 | internal: self.internal.internal, 51 | guard: self.internal.guard, 52 | }) 53 | } 54 | 55 | pub unsafe fn cast_boolean(self) -> JSValue> { 56 | JSValue::new(JSBoolean { 57 | internal: self.internal.internal, 58 | guard: self.internal.guard, 59 | }) 60 | } 61 | 62 | pub unsafe fn cast_number(self) -> JSValue> { 63 | JSValue::new(JSNumber { 64 | internal: self.internal.internal, 65 | guard: self.internal.guard, 66 | }) 67 | } 68 | 69 | pub unsafe fn cast_string(self) -> JSValue> { 70 | JSValue::new(JSString { 71 | internal: self.internal.internal, 72 | guard: self.internal.guard, 73 | }) 74 | } 75 | 76 | pub unsafe fn cast_object(self) -> JSValue> { 77 | JSValue::new(JSObject { 78 | internal: self.internal.internal, 79 | guard: self.internal.guard, 80 | }) 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/services/app/tray.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | lifecycle::exit, 3 | services::{rendering::message::RendererMessage, sensors::SensorMessage}, 4 | }; 5 | use discord_sdk::Discord; 6 | use smol_static::ServerMessage; 7 | use std::sync::Arc; 8 | use tachyonix::Sender; 9 | use tauri::{ 10 | AppHandle, CustomMenuItem, Manager, State, SystemTray, SystemTrayEvent, SystemTrayMenu, 11 | SystemTrayMenuItem, 12 | }; 13 | use tokio::{runtime::Handle, sync::RwLock, task}; 14 | 15 | use super::main_window::recreate_main_window; 16 | 17 | pub fn build_tray() -> SystemTray { 18 | let open_window = CustomMenuItem::new("open_window".to_string(), "Open Window"); 19 | let quit_app = CustomMenuItem::new("quit_app".to_string(), "Quit App"); 20 | 21 | let menu = SystemTrayMenu::new() 22 | .add_item(open_window) 23 | .add_native_item(SystemTrayMenuItem::Separator) 24 | .add_item(quit_app); 25 | 26 | SystemTray::new() 27 | .with_menu(menu) 28 | .with_tooltip("Zefir's Flashy Cooler") 29 | .with_id("main_tray") 30 | } 31 | 32 | pub fn tray_event_handler(app: &AppHandle, event: SystemTrayEvent) { 33 | match event { 34 | SystemTrayEvent::LeftClick { 35 | position: _, 36 | size: _, 37 | .. 38 | } => { 39 | if app.get_window("main").is_none() { 40 | recreate_main_window(app); 41 | } 42 | } 43 | SystemTrayEvent::MenuItemClick { id, .. } => match id.as_str() { 44 | "quit_app" => { 45 | let sender_renderer: State<'_, Sender> = app.state(); 46 | let sender_sensors: State<'_, Sender> = app.state(); 47 | let sender_server: State<'_, Sender> = app.state(); 48 | let discord: State<'_, Arc>>> = app.state(); 49 | 50 | task::block_in_place(move || { 51 | Handle::current().block_on(async { 52 | exit( 53 | app.get_window("main"), 54 | app, 55 | sender_renderer, 56 | sender_sensors, 57 | sender_server, 58 | discord, 59 | ) 60 | .await; 61 | }) 62 | }); 63 | } 64 | "open_window" => { 65 | recreate_main_window(app); 66 | } 67 | _ => {} 68 | }, 69 | _ => {} 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /crates/librehardwaremonitor-rs/src/tests.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | #[test] 4 | fn sets_params() { 5 | let mut computer = Computer::new(); 6 | let old_params = computer.get_params(); 7 | let new_params = ComputerParams { 8 | is_psu_enabled: true, 9 | is_gpu_enabled: true, 10 | is_cpu_enabled: true, 11 | is_motherboard_enabled: true, 12 | is_memory_enabled: true, 13 | is_storage_enabled: true, 14 | is_network_enabled: true, 15 | is_controller_enabled: true, 16 | is_battery_enabled: true, 17 | }; 18 | computer.set_params(new_params); 19 | let params = computer.get_params(); 20 | 21 | drop(computer); 22 | 23 | assert!( 24 | params.is_psu_enabled 25 | && params.is_gpu_enabled 26 | && params.is_cpu_enabled 27 | && params.is_motherboard_enabled 28 | && params.is_memory_enabled 29 | && params.is_storage_enabled 30 | && params.is_network_enabled 31 | && params.is_controller_enabled 32 | && params.is_battery_enabled 33 | && !old_params.is_psu_enabled 34 | && !old_params.is_gpu_enabled 35 | && !old_params.is_cpu_enabled 36 | && !old_params.is_motherboard_enabled 37 | && !old_params.is_memory_enabled 38 | && !old_params.is_storage_enabled 39 | && !old_params.is_network_enabled 40 | && !old_params.is_controller_enabled 41 | && !old_params.is_battery_enabled 42 | ); 43 | } 44 | 45 | #[test] 46 | fn gets_hardware() { 47 | let mut computer = Computer::new_with_params(ComputerParams { 48 | is_psu_enabled: true, 49 | is_gpu_enabled: true, 50 | is_cpu_enabled: true, 51 | is_motherboard_enabled: true, 52 | is_memory_enabled: true, 53 | is_storage_enabled: true, 54 | is_network_enabled: true, 55 | is_controller_enabled: true, 56 | is_battery_enabled: true, 57 | }); 58 | 59 | for mut hardware in computer.iter() { 60 | let hardware_name = hardware.get_name().unwrap_or("".into()); 61 | println!("Hardware: {hardware_name}\nSensors:"); 62 | 63 | for mut sensor in hardware.sensor_iter() { 64 | let sensor_name = sensor.get_name().unwrap_or("".into()); 65 | let sensor_value = sensor.get_value().unwrap_or(0f32); 66 | let sensor_type = sensor.get_type(); 67 | println!( 68 | "\t{sensor_name}\n\t\tvalue: {sensor_value}\n\t\ttype: {:#?}", 69 | sensor_type 70 | ); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /frontend/src/routes/+layout.svelte: -------------------------------------------------------------------------------- 1 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /crates/ultralight/build.rs: -------------------------------------------------------------------------------- 1 | use spirv_compiler::*; 2 | 3 | fn compile_shader_and_write_to_file( 4 | compiler: &mut Compiler, 5 | shader_path: &str, 6 | shader_kind: ShaderKind, 7 | output_path: &str, 8 | ) -> Result<(), CompilerError> { 9 | let _ = compiler.compile_from_file(shader_path, shader_kind, true)?; 10 | std::fs::copy(shader_path.to_owned() + ".spv", output_path) 11 | .expect("Failed to copy shader to output directory"); 12 | std::fs::remove_file(shader_path.to_owned() + ".spv") 13 | .expect("Failed to remove shader from source directory"); 14 | 15 | Ok(()) 16 | } 17 | 18 | fn compile_shaders() -> Result<(), CompilerError> { 19 | let mut compiler = CompilerBuilder::new() 20 | .with_source_language(SourceLanguage::HLSL) 21 | .with_opt_level(OptimizationLevel::Performance) 22 | .build() 23 | .expect("Failed to initialise shaderc compiler"); 24 | 25 | compile_shader_and_write_to_file( 26 | &mut compiler, 27 | "./shaders/path/fill.hlsl", 28 | ShaderKind::Fragment, 29 | &(std::env::var("OUT_DIR").unwrap() + "/fill.spv"), 30 | )?; 31 | 32 | compile_shader_and_write_to_file( 33 | &mut compiler, 34 | "./shaders/path/fill_path.hlsl", 35 | ShaderKind::Fragment, 36 | &(std::env::var("OUT_DIR").unwrap() + "/fill_path.spv"), 37 | )?; 38 | 39 | compile_shader_and_write_to_file( 40 | &mut compiler, 41 | "./shaders/vertex/v2f_c4f_t2f.hlsl", 42 | ShaderKind::Vertex, 43 | &(std::env::var("OUT_DIR").unwrap() + "/v2f_c4f_t2f.spv"), 44 | )?; 45 | 46 | compile_shader_and_write_to_file( 47 | &mut compiler, 48 | "./shaders/vertex/v2f_c4f_t2f_d28f.hlsl", 49 | ShaderKind::Vertex, 50 | &(std::env::var("OUT_DIR").unwrap() + "/v2f_c4f_t2f_d28f.spv"), 51 | )?; 52 | 53 | Ok(()) 54 | } 55 | 56 | fn print_compiler_error_with_panic(error: CompilerError) { 57 | match error { 58 | CompilerError::Log(log_error) => { 59 | let CompilationError { file, description } = log_error; 60 | panic!( 61 | "Failed to compile shader\nFile: {:#?}\nError: {}", 62 | file, description 63 | ); 64 | } 65 | CompilerError::LoadError(load_error) => { 66 | panic!("Failed to load shader\nError: {}", load_error); 67 | } 68 | CompilerError::WriteError(write_error) => { 69 | panic!("Failed to write shader\nError: {}", write_error); 70 | } 71 | } 72 | } 73 | 74 | pub fn main() { 75 | ultralight_build::build(); 76 | 77 | compile_shaders() 78 | .map_err(print_compiler_error_with_panic) 79 | .expect("Failed to compile shaders!"); 80 | } 81 | -------------------------------------------------------------------------------- /crates/librehardwaremonitor-rs/src/types/sensor_type.rs: -------------------------------------------------------------------------------- 1 | use librehardwaremonitor_sys::SensorType as SensorTypeNative; 2 | 3 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 4 | pub enum SensorType { 5 | Voltage, 6 | Current, 7 | Power, 8 | Clock, 9 | Temperature, 10 | Load, 11 | Frequency, 12 | Fan, 13 | Flow, 14 | Control, 15 | Level, 16 | Factor, 17 | Data, 18 | SmallData, 19 | Throughput, 20 | TimeSpan, 21 | Energy, 22 | Noise, 23 | } 24 | 25 | impl From for SensorType { 26 | fn from(sensor_type: SensorTypeNative) -> Self { 27 | match sensor_type { 28 | SensorTypeNative::Voltage => Self::Voltage, 29 | SensorTypeNative::Current => Self::Current, 30 | SensorTypeNative::Power => Self::Power, 31 | SensorTypeNative::Clock => Self::Clock, 32 | SensorTypeNative::Temperature => Self::Temperature, 33 | SensorTypeNative::Load => Self::Load, 34 | SensorTypeNative::Frequency => Self::Frequency, 35 | SensorTypeNative::Fan => Self::Fan, 36 | SensorTypeNative::Flow => Self::Flow, 37 | SensorTypeNative::Control => Self::Control, 38 | SensorTypeNative::Level => Self::Level, 39 | SensorTypeNative::Factor => Self::Factor, 40 | SensorTypeNative::Data => Self::Data, 41 | SensorTypeNative::SmallData => Self::SmallData, 42 | SensorTypeNative::Throughput => Self::Throughput, 43 | SensorTypeNative::TimeSpan => Self::TimeSpan, 44 | SensorTypeNative::Energy => Self::Energy, 45 | SensorTypeNative::Noise => Self::Noise, 46 | } 47 | } 48 | } 49 | 50 | impl From for SensorTypeNative { 51 | fn from(sensor_type: SensorType) -> Self { 52 | match sensor_type { 53 | SensorType::Voltage => Self::Voltage, 54 | SensorType::Current => Self::Current, 55 | SensorType::Power => Self::Power, 56 | SensorType::Clock => Self::Clock, 57 | SensorType::Temperature => Self::Temperature, 58 | SensorType::Load => Self::Load, 59 | SensorType::Frequency => Self::Frequency, 60 | SensorType::Fan => Self::Fan, 61 | SensorType::Flow => Self::Flow, 62 | SensorType::Control => Self::Control, 63 | SensorType::Level => Self::Level, 64 | SensorType::Factor => Self::Factor, 65 | SensorType::Data => Self::Data, 66 | SensorType::SmallData => Self::SmallData, 67 | SensorType::Throughput => Self::Throughput, 68 | SensorType::TimeSpan => Self::TimeSpan, 69 | SensorType::Energy => Self::Energy, 70 | SensorType::Noise => Self::Noise, 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/services/app/mod.rs: -------------------------------------------------------------------------------- 1 | use self::{ 2 | events::handle_events, 3 | tray::{build_tray, tray_event_handler}, 4 | }; 5 | use super::{rendering::message::RendererMessage, sensors::SensorMessage}; 6 | use crate::lifecycle::setup; 7 | use discord_sdk::Discord; 8 | use smol_static::ServerMessage; 9 | use std::sync::Arc; 10 | use tachyonix::{Receiver, Sender}; 11 | use tauri::Manager; 12 | use tauri_plugin_context_menu::init as context_menu_init; 13 | use tauri_plugin_log::{Builder as LogBuilder, LogTarget}; 14 | use tauri_plugin_single_instance::init as single_instance_init; 15 | use tauri_plugin_window_state::Builder as WindowStateBuilder; 16 | use tokio::{ 17 | sync::RwLock, 18 | task::{self, JoinHandle}, 19 | }; 20 | 21 | mod events; 22 | mod main_window; 23 | mod tray; 24 | 25 | pub async fn spawn_app( 26 | sender_renderer: Sender, 27 | sender_sensors: Sender, 28 | receiver_sensors: Receiver, 29 | sender_server: Sender, 30 | discord: Arc>>, 31 | ) -> JoinHandle> { 32 | task::spawn_blocking(|| { 33 | tauri::Builder::default() 34 | .plugin(single_instance_init(|app, argv, cwd| { 35 | println!("{}, {argv:?}, {cwd}", app.package_info().name); 36 | app.emit_all("single-instance", Payload { args: argv, cwd }) 37 | .expect("Failed to emit single instance event!"); 38 | })) 39 | .plugin(WindowStateBuilder::default().build()) 40 | .plugin( 41 | LogBuilder::default() 42 | .targets([LogTarget::LogDir, LogTarget::Stdout, LogTarget::Webview]) 43 | .build(), 44 | ) 45 | .plugin(context_menu_init()) 46 | .manage(sender_renderer) 47 | .manage(sender_sensors) 48 | .manage(receiver_sensors) 49 | .manage(sender_server) 50 | .manage(discord) 51 | .system_tray(build_tray()) 52 | .on_system_tray_event(tray_event_handler) 53 | .setup(setup) 54 | .invoke_handler(tauri::generate_handler![ 55 | crate::lifecycle::exit_handler, 56 | crate::themes::get_all::get_all_themes_handler, 57 | crate::themes::play::play_theme_handler, 58 | crate::services::discord::presence::activity_handler, 59 | crate::themes::now_playing::get_now_playing, 60 | crate::themes::config::get_theme_config, 61 | ]) 62 | .any_thread() 63 | .build(tauri::generate_context!()) 64 | .map(|app| { 65 | app.run(handle_events); 66 | }) 67 | }) 68 | } 69 | 70 | #[derive(Clone, serde::Serialize)] 71 | struct Payload { 72 | args: Vec, 73 | cwd: String, 74 | } 75 | -------------------------------------------------------------------------------- /frontend/src/routes/settings/+page.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 |

7 | About 8 | Zefir's Flashy Cooler 9 | Zefir's Flashy Cooler. 10 |

11 |

12 | Zefir's Flashy Cooler is a simple app that allows you to control your LCD CPU cooler without the 13 | manufacturers' limitations and bloatware. 14 |

15 |
16 |
17 | Zefir 18 | 20 | 21 |

22 | This software comes AS IS with no warranties or guaranties of ANY kind. If you feel it's shady, 23 | please check its source out on GitHub. Remember, this is third party software based on a 24 | reverse-engineering effort. If something breaks, sorry but I'm not responsible. I do my best so 25 | that this app works as intended and use it myself but I can't promise anything. 26 |

27 | 28 |

Made with ❤️ in Cork, Ireland.

29 | 30 |

31 | The picture on the right is my cat Zefir. He's supported me emotionally through the development of 32 | this app. 33 |

34 | 35 |

36 | You can support me by buying me a coffee or a beer. I'd appreciate it a lot. You can also support me by spreading the word about this app. The more 41 | people use it, the more I'll be motivated to keep it up to date. 42 |

43 | 44 |
45 | 46 | App: {window.__APP_VERSION__ ?? "0.0.0"} 47 | 48 | 49 | UI: {window.__UI_VERSION__ ?? "0.0.0"} 50 | 51 | 52 | Ultralight: {window.__UL_VERSION__ ?? "0.0.0"} 53 | 54 | 55 | Backend: {window.__BACKEND_VERSION__ ?? "0.0.0"} 56 | 57 | 58 | LibreHardwareMonitor: {window.__LIBRE_VERSION__ ?? "0.0.0"} 59 | 60 |
61 | 62 | 73 | -------------------------------------------------------------------------------- /crates/ultralight/src/string.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | fmt::{Debug, Display}, 3 | ops::{Deref, DerefMut}, 4 | }; 5 | 6 | pub struct ULString { 7 | string: ultralight_sys::ULString, 8 | } 9 | 10 | impl Deref for ULString { 11 | type Target = ultralight_sys::ULString; 12 | 13 | fn deref(&self) -> &Self::Target { 14 | &self.string 15 | } 16 | } 17 | 18 | impl DerefMut for ULString { 19 | fn deref_mut(&mut self) -> &mut Self::Target { 20 | &mut self.string 21 | } 22 | } 23 | 24 | impl Clone for ULString { 25 | fn clone(&self) -> Self { 26 | let string = unsafe { ultralight_sys::ulCreateStringFromCopy(self.string) }; 27 | assert!(!string.is_null()); 28 | Self { string } 29 | } 30 | } 31 | 32 | impl Drop for ULString { 33 | fn drop(&mut self) { 34 | unsafe { ultralight_sys::ulDestroyString(self.string) }; 35 | } 36 | } 37 | 38 | impl Display for ULString { 39 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 40 | let string = 41 | unsafe { std::ffi::CStr::from_ptr(ultralight_sys::ulStringGetData(self.string)) }; 42 | write!(f, "{}", string.to_str().unwrap()) 43 | } 44 | } 45 | 46 | impl Debug for ULString { 47 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 48 | let string = 49 | unsafe { std::ffi::CStr::from_ptr(ultralight_sys::ulStringGetData(self.string)) }; 50 | write!(f, "{}", string.to_str().unwrap()) 51 | } 52 | } 53 | 54 | impl From<&str> for ULString { 55 | fn from(string: &str) -> Self { 56 | Self::new(string) 57 | } 58 | } 59 | 60 | impl From for ULString { 61 | fn from(string: String) -> Self { 62 | Self::new(string) 63 | } 64 | } 65 | 66 | impl ULString { 67 | pub fn empty() -> Self { 68 | let string = unsafe { ultralight_sys::ulCreateString([].as_mut_ptr()) }; 69 | assert!(!string.is_null()); 70 | Self { string } 71 | } 72 | 73 | pub fn new>(string: S) -> Self { 74 | let string = unsafe { 75 | ultralight_sys::ulCreateStringUTF8( 76 | string.as_ref().as_ptr() as *const i8, 77 | string.as_ref().len(), 78 | ) 79 | }; 80 | assert!(!string.is_null()); 81 | Self { string } 82 | } 83 | 84 | pub fn as_str(&self) -> &str { 85 | unsafe { 86 | std::ffi::CStr::from_ptr(ultralight_sys::ulStringGetData(self.string)) 87 | .to_str() 88 | .unwrap() 89 | } 90 | } 91 | 92 | pub fn as_ptr(&self) -> *const i8 { 93 | unsafe { ultralight_sys::ulStringGetData(self.string) } 94 | } 95 | 96 | pub fn len(&self) -> usize { 97 | unsafe { ultralight_sys::ulStringGetLength(self.string) } 98 | } 99 | 100 | pub fn is_empty(&self) -> bool { 101 | unsafe { ultralight_sys::ulStringIsEmpty(self.string) } 102 | } 103 | 104 | pub fn from_raw(string: ultralight_sys::ULString) -> Self { 105 | Self { string } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /crates/ultralight/src/js/context.rs: -------------------------------------------------------------------------------- 1 | use std::{ops::Deref, ptr}; 2 | 3 | use super::types::{JSBoolean, JSNull, JSNumber, JSObject, JSString, JSUndefined}; 4 | use crate::{value::JSValue, ULRendererGuard, ULView}; 5 | 6 | pub struct JSContext<'a> { 7 | pub(crate) internal: ultralight_sys::JSContextRef, 8 | #[allow(unused)] 9 | pub(crate) renderer: &'a ULRendererGuard, 10 | pub(crate) view: &'a ULView<'a>, 11 | } 12 | 13 | impl<'a> Deref for JSContext<'a> { 14 | type Target = ultralight_sys::JSContextRef; 15 | 16 | fn deref(&self) -> &Self::Target { 17 | &self.internal 18 | } 19 | } 20 | 21 | impl<'a> Drop for JSContext<'a> { 22 | fn drop(&mut self) { 23 | unsafe { 24 | ultralight_sys::ulViewUnlockJSContext(*self.view.internal); 25 | } 26 | } 27 | } 28 | 29 | impl<'a> JSContext<'a> { 30 | pub(crate) fn new( 31 | internal: ultralight_sys::JSContextRef, 32 | renderer: &'a ULRendererGuard, 33 | view: &'a ULView<'a>, 34 | ) -> Self { 35 | Self { 36 | internal, 37 | renderer, 38 | view, 39 | } 40 | } 41 | 42 | pub fn get_global_object(&'a self) -> JSValue> { 43 | let value_ptr = unsafe { ultralight_sys::JSContextGetGlobalObject(**self) }; 44 | 45 | unsafe { JSValue::new_opaque(value_ptr, self).cast_object() } 46 | } 47 | 48 | pub fn make_null(&'a self) -> JSValue> { 49 | let value_ptr = unsafe { ultralight_sys::JSValueMakeNull(**self) }; 50 | 51 | unsafe { JSValue::new_opaque(value_ptr, self).cast_null() } 52 | } 53 | 54 | pub fn make_undefined(&'a self) -> JSValue> { 55 | let value_ptr = unsafe { ultralight_sys::JSValueMakeUndefined(**self) }; 56 | 57 | unsafe { JSValue::new_opaque(value_ptr, self).cast_undefined() } 58 | } 59 | 60 | pub fn make_boolean(&'a self, boolean: bool) -> JSValue> { 61 | let value_ptr = unsafe { ultralight_sys::JSValueMakeBoolean(**self, boolean) }; 62 | 63 | unsafe { JSValue::new_opaque(value_ptr, self).cast_boolean() } 64 | } 65 | 66 | pub fn make_number(&'a self, number: f64) -> JSValue> { 67 | let value_ptr = unsafe { ultralight_sys::JSValueMakeNumber(**self, number) }; 68 | 69 | unsafe { JSValue::new_opaque(value_ptr, self).cast_number() } 70 | } 71 | 72 | pub fn make_string>(&'a self, string: S) -> JSValue> { 73 | let string = std::ffi::CString::new(string.as_ref()).unwrap(); 74 | let js_string = unsafe { ultralight_sys::JSStringCreateWithUTF8CString(string.as_ptr()) }; 75 | let value_ptr = unsafe { ultralight_sys::JSValueMakeString(**self, js_string as _) }; 76 | unsafe { ultralight_sys::JSStringRelease(js_string) }; 77 | 78 | unsafe { JSValue::new_opaque(value_ptr, self).cast_string() } 79 | } 80 | 81 | pub fn make_object(&'a self) -> JSValue> { 82 | let value_ptr = 83 | unsafe { ultralight_sys::JSObjectMake(**self, ptr::null_mut(), ptr::null_mut()) }; 84 | 85 | unsafe { JSValue::new_opaque(value_ptr, self).cast_object() } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/services/rendering/loop.rs: -------------------------------------------------------------------------------- 1 | use super::{ 2 | config::load_theme_with_config, 3 | dispatch_sensors::dispatch_sensors, 4 | message::{RendererMessage, SensorSubscriptionNotification}, 5 | render_helpers::{render_bitmap_and_send, update_and_render}, 6 | setup::setup_rendering, 7 | }; 8 | use crate::services::sensors::SensorMessage; 9 | use lcd_coolers::{DeviceCreator, DeviceInfo, DisplayCooler}; 10 | use tachyonix::{Receiver, Sender, TryRecvError}; 11 | use tokio::time::{self, Duration}; 12 | use ultralight::{ULView, ULViewBuilder}; 13 | 14 | pub async fn main_loop(receiver: Receiver, sensor_sender: Sender) { 15 | let (renderer, mut device) = setup_rendering().await.expect("Failed to setup rendering!"); 16 | let mut receiver = receiver; 17 | let DeviceInfo { width, height, .. } = device.device_info().await; 18 | let mut view = ULViewBuilder::new(&renderer) 19 | .set_width(width) 20 | .set_height(height) 21 | .build(); 22 | 23 | view.load_url("http://127.0.0.1:2137") 24 | .await 25 | .expect("Failed to load URL"); 26 | 27 | let mut interval = time::interval(Duration::from_millis(40)); 28 | 29 | let mut subscribed_sensors = vec![]; 30 | 31 | loop { 32 | update_and_render(&renderer); 33 | if !handle_messages( 34 | &mut receiver, 35 | &mut view, 36 | &mut subscribed_sensors, 37 | &sensor_sender, 38 | ) 39 | .await 40 | { 41 | break; 42 | } 43 | 44 | if let Err(err) = render_bitmap_and_send(&mut view, &mut device).await { 45 | eprintln!("Failed to render bitmap: {}", err); 46 | } 47 | 48 | interval.tick().await; 49 | } 50 | 51 | let _ = device.close().await; 52 | } 53 | 54 | async fn handle_messages<'a>( 55 | receiver: &mut Receiver, 56 | view: &mut ULView<'a>, 57 | subscribed_sensors: &mut Vec, 58 | sensor_sender: &Sender, 59 | ) -> bool { 60 | let received = receiver.try_recv(); 61 | 62 | if let Ok(message) = received { 63 | match message { 64 | RendererMessage::Shutdown => { 65 | return false; 66 | } 67 | RendererMessage::ReloadCurrentUrl(fs_name) => { 68 | let _ = load_theme_with_config(view, &fs_name, sensor_sender).await; 69 | } 70 | RendererMessage::NewSubscribedSensors(sensors) => { 71 | *subscribed_sensors = sensors; 72 | } 73 | RendererMessage::SensorValues(values) => { 74 | for value in values { 75 | if let Some(subscription) = subscribed_sensors 76 | .iter_mut() 77 | .find(|subscription| subscription.sensor_id == value.sensor_id) 78 | { 79 | subscription.sensor_value = value.sensor_value; 80 | } 81 | } 82 | 83 | dispatch_sensors(view, subscribed_sensors); 84 | } 85 | }; 86 | 87 | true 88 | } else { 89 | !matches!(received, Err(TryRecvError::Closed)) 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /tauri.conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "build": { 3 | "beforeBuildCommand": "pnpm build", 4 | "beforeDevCommand": "pnpm dev", 5 | "devPath": "http://localhost:5173", 6 | "distDir": "./frontend/build" 7 | }, 8 | "package": { 9 | "productName": "zefirs-flashy-cooler", 10 | "version": "0.1.0" 11 | }, 12 | "tauri": { 13 | "systemTray": { 14 | "iconPath": "icons/icon.png", 15 | "iconAsTemplate": true 16 | }, 17 | "allowlist": { 18 | "all": false, 19 | "window": { 20 | "all": false, 21 | "close": true, 22 | "hide": true, 23 | "show": true, 24 | "maximize": true, 25 | "minimize": true, 26 | "unmaximize": true, 27 | "unminimize": true, 28 | "startDragging": true 29 | }, 30 | "shell": { 31 | "all": false, 32 | "open": true 33 | }, 34 | "path": { 35 | "all": true 36 | }, 37 | "protocol": { 38 | "asset": true, 39 | "assetScope": [ 40 | "$DOCUMENT/Zefir's Flashy Cooler/Themes/**/*.jpg", 41 | "$EXE/**/*" 42 | ] 43 | }, 44 | "fs": { 45 | "readFile": true, 46 | "readDir": true 47 | } 48 | }, 49 | "bundle": { 50 | "active": true, 51 | "category": "Utility", 52 | "copyright": "AGPL", 53 | "externalBin": [], 54 | "icon": [ 55 | "icons/32x32.png", 56 | "icons/128x128.png", 57 | "icons/128x128@2x.png", 58 | "icons/icon.icns", 59 | "icons/icon.ico" 60 | ], 61 | "identifier": "com.brunostjohn.zefirsflashycooler", 62 | "publisher": "Bruno St John", 63 | "longDescription": "Take your cooler to the next level with Zefir's Flashy Cooler and break free of manufacturers' restrictions.", 64 | "resources": [ 65 | "./AppCore.dll", 66 | "./resources/cacert.pem", 67 | "./resources/icudt67l.dat", 68 | "./Ultralight.dll", 69 | "./UltralightCore.dll", 70 | "./WebCore.dll" 71 | ], 72 | "shortDescription": "Zefirs Flashy Cooler", 73 | "targets": ["nsis", "updater"], 74 | "windows": { 75 | "webviewInstallMode": { 76 | "type": "embedBootstrapper", 77 | "silent": true 78 | }, 79 | "nsis": { 80 | "languages": ["English"], 81 | "license": "../LICENSE", 82 | "installerIcon": "./icons/installer.ico", 83 | "sidebarImage": "./icons/installer-sidebar.bmp", 84 | "headerImage": "./icons/installer-header.bmp", 85 | "installMode": "perMachine" 86 | } 87 | } 88 | }, 89 | "security": { 90 | "csp": null 91 | }, 92 | "updater": { 93 | "active": false 94 | }, 95 | "windows": [ 96 | { 97 | "fullscreen": false, 98 | "height": 800, 99 | "label": "main", 100 | "minHeight": 800, 101 | "resizable": true, 102 | "title": "Zefir's Flashy Cooler", 103 | "width": 1130, 104 | "minWidth": 1130, 105 | "transparent": true, 106 | "theme": "Dark", 107 | "decorations": false 108 | } 109 | ] 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /crates/ultralight/src/types/surface/impl.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | bitmap::{Bitmap, LockablePixels, PixelsGuard}, 3 | error::ULError, 4 | ULResult, 5 | }; 6 | 7 | pub struct ULSurface(ultralight_sys::ULSurface); 8 | 9 | unsafe impl Send for ULSurface {} 10 | unsafe impl Sync for ULSurface {} 11 | 12 | pub struct ULSurfaceBounds { 13 | pub left: i32, 14 | pub top: i32, 15 | pub right: i32, 16 | pub bottom: i32, 17 | } 18 | 19 | impl From for ultralight_sys::ULIntRect { 20 | fn from(bounds: ULSurfaceBounds) -> Self { 21 | Self { 22 | left: bounds.left, 23 | top: bounds.top, 24 | right: bounds.right, 25 | bottom: bounds.bottom, 26 | } 27 | } 28 | } 29 | 30 | impl From for ULSurfaceBounds { 31 | fn from(bounds: ultralight_sys::ULIntRect) -> Self { 32 | Self { 33 | left: bounds.left, 34 | top: bounds.top, 35 | right: bounds.right, 36 | bottom: bounds.bottom, 37 | } 38 | } 39 | } 40 | 41 | impl LockablePixels for ULSurface { 42 | unsafe fn raw_lock_pixels(&mut self) -> *mut u8 { 43 | ultralight_sys::ulSurfaceLockPixels(self.0) as _ 44 | } 45 | 46 | unsafe fn raw_unlock_pixels(&mut self) { 47 | ultralight_sys::ulSurfaceUnlockPixels(self.0) 48 | } 49 | } 50 | 51 | impl ULSurface { 52 | pub unsafe fn from_raw(surface: ultralight_sys::ULSurface) -> Self { 53 | assert!(!surface.is_null(), "ULSurface is null"); 54 | Self(surface) 55 | } 56 | 57 | pub fn get_bitmap(&mut self) -> ULResult { 58 | unsafe { 59 | let bitmap = ultralight_sys::ulBitmapSurfaceGetBitmap(self.0); 60 | Bitmap::from_raw(bitmap).ok_or(ULError::BitmapNullReference) 61 | } 62 | } 63 | 64 | pub fn get_width(&self) -> u32 { 65 | unsafe { ultralight_sys::ulSurfaceGetWidth(self.0) } 66 | } 67 | 68 | pub fn get_height(&self) -> u32 { 69 | unsafe { ultralight_sys::ulSurfaceGetHeight(self.0) } 70 | } 71 | 72 | pub fn get_row_bytes(&self) -> u32 { 73 | unsafe { ultralight_sys::ulSurfaceGetRowBytes(self.0) } 74 | } 75 | 76 | pub fn get_size(&self) -> usize { 77 | unsafe { ultralight_sys::ulSurfaceGetSize(self.0) } 78 | } 79 | 80 | pub fn lock_pixels(&mut self) -> ULResult> { 81 | unsafe { 82 | let pixels = self.raw_lock_pixels(); 83 | let pixels = std::slice::from_raw_parts_mut(pixels, self.get_size()); 84 | Ok(PixelsGuard::new(self, pixels)) 85 | } 86 | } 87 | 88 | pub fn resize(&mut self, width: u32, height: u32) { 89 | unsafe { ultralight_sys::ulSurfaceResize(self.0, width, height) } 90 | } 91 | 92 | pub fn set_dirty_bounds(&mut self, bounds: ULSurfaceBounds) { 93 | unsafe { ultralight_sys::ulSurfaceSetDirtyBounds(self.0, bounds.into()) } 94 | } 95 | 96 | pub fn get_dirty_bounds(&self) -> ULSurfaceBounds { 97 | unsafe { ultralight_sys::ulSurfaceGetDirtyBounds(self.0).into() } 98 | } 99 | 100 | pub fn clear_dirty_bounds(&mut self) { 101 | unsafe { ultralight_sys::ulSurfaceClearDirtyBounds(self.0) } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /crates/librehardwaremonitor-rs/src/computer/params.rs: -------------------------------------------------------------------------------- 1 | use super::r#impl::Computer; 2 | 3 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 4 | pub struct ComputerParams { 5 | pub is_psu_enabled: bool, 6 | pub is_gpu_enabled: bool, 7 | pub is_cpu_enabled: bool, 8 | pub is_motherboard_enabled: bool, 9 | pub is_memory_enabled: bool, 10 | pub is_storage_enabled: bool, 11 | pub is_network_enabled: bool, 12 | pub is_controller_enabled: bool, 13 | pub is_battery_enabled: bool, 14 | } 15 | 16 | impl Computer { 17 | pub fn get_params(&mut self) -> ComputerParams { 18 | ComputerParams { 19 | is_psu_enabled: unsafe { 20 | librehardwaremonitor_sys::get_computer_is_psu_enabled(self.id) 21 | }, 22 | is_gpu_enabled: unsafe { 23 | librehardwaremonitor_sys::get_computer_is_gpu_enabled(self.id) 24 | }, 25 | is_cpu_enabled: unsafe { 26 | librehardwaremonitor_sys::get_computer_is_cpu_enabled(self.id) 27 | }, 28 | is_motherboard_enabled: unsafe { 29 | librehardwaremonitor_sys::get_computer_is_motherboard_enabled(self.id) 30 | }, 31 | is_memory_enabled: unsafe { 32 | librehardwaremonitor_sys::get_computer_is_memory_enabled(self.id) 33 | }, 34 | is_storage_enabled: unsafe { 35 | librehardwaremonitor_sys::get_computer_is_storage_enabled(self.id) 36 | }, 37 | is_network_enabled: unsafe { 38 | librehardwaremonitor_sys::get_computer_is_network_enabled(self.id) 39 | }, 40 | is_controller_enabled: unsafe { 41 | librehardwaremonitor_sys::get_computer_is_controller_enabled(self.id) 42 | }, 43 | is_battery_enabled: unsafe { 44 | librehardwaremonitor_sys::get_computer_is_battery_enabled(self.id) 45 | }, 46 | } 47 | } 48 | 49 | pub fn set_params(&mut self, params: ComputerParams) { 50 | let ComputerParams { 51 | is_psu_enabled, 52 | is_gpu_enabled, 53 | is_cpu_enabled, 54 | is_motherboard_enabled, 55 | is_memory_enabled, 56 | is_storage_enabled, 57 | is_network_enabled, 58 | is_controller_enabled, 59 | is_battery_enabled, 60 | } = params; 61 | 62 | unsafe { 63 | librehardwaremonitor_sys::set_computer_is_psu_enabled(self.id, is_psu_enabled); 64 | librehardwaremonitor_sys::set_computer_is_gpu_enabled(self.id, is_gpu_enabled); 65 | librehardwaremonitor_sys::set_computer_is_cpu_enabled(self.id, is_cpu_enabled); 66 | librehardwaremonitor_sys::set_computer_is_motherboard_enabled( 67 | self.id, 68 | is_motherboard_enabled, 69 | ); 70 | librehardwaremonitor_sys::set_computer_is_memory_enabled(self.id, is_memory_enabled); 71 | librehardwaremonitor_sys::set_computer_is_storage_enabled(self.id, is_storage_enabled); 72 | librehardwaremonitor_sys::set_computer_is_network_enabled(self.id, is_network_enabled); 73 | librehardwaremonitor_sys::set_computer_is_controller_enabled( 74 | self.id, 75 | is_controller_enabled, 76 | ); 77 | librehardwaremonitor_sys::set_computer_is_battery_enabled(self.id, is_battery_enabled); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /crates/ultralight/src/js/types.rs: -------------------------------------------------------------------------------- 1 | use crate::{context::JSContext, ULError}; 2 | 3 | pub trait IsJSValue { 4 | fn get_value(&self) -> ultralight_sys::JSValueRef; 5 | } 6 | 7 | pub struct JSOpaque<'a> { 8 | pub(crate) internal: ultralight_sys::JSValueRef, 9 | #[allow(unused)] 10 | pub(crate) guard: &'a JSContext<'a>, 11 | } 12 | 13 | impl IsJSValue for JSOpaque<'_> { 14 | fn get_value(&self) -> ultralight_sys::JSValueRef { 15 | self.internal 16 | } 17 | } 18 | 19 | pub struct JSFunction<'a> { 20 | pub(crate) internal: ultralight_sys::JSValueRef, 21 | #[allow(unused)] 22 | pub(crate) guard: &'a JSContext<'a>, 23 | } 24 | 25 | impl IsJSValue for JSFunction<'_> { 26 | fn get_value(&self) -> ultralight_sys::JSValueRef { 27 | self.internal 28 | } 29 | } 30 | 31 | pub struct JSString<'a> { 32 | pub(crate) internal: ultralight_sys::JSValueRef, 33 | #[allow(unused)] 34 | pub(crate) guard: &'a JSContext<'a>, 35 | } 36 | 37 | impl IsJSValue for JSString<'_> { 38 | fn get_value(&self) -> ultralight_sys::JSValueRef { 39 | self.internal 40 | } 41 | } 42 | 43 | pub struct JSUndefined<'a> { 44 | pub(crate) internal: ultralight_sys::JSValueRef, 45 | #[allow(unused)] 46 | pub(crate) guard: &'a JSContext<'a>, 47 | } 48 | 49 | impl IsJSValue for JSUndefined<'_> { 50 | fn get_value(&self) -> ultralight_sys::JSValueRef { 51 | self.internal 52 | } 53 | } 54 | 55 | pub struct JSNull<'a> { 56 | pub(crate) internal: ultralight_sys::JSValueRef, 57 | #[allow(unused)] 58 | pub(crate) guard: &'a JSContext<'a>, 59 | } 60 | 61 | impl IsJSValue for JSNull<'_> { 62 | fn get_value(&self) -> ultralight_sys::JSValueRef { 63 | self.internal 64 | } 65 | } 66 | 67 | pub struct JSBoolean<'a> { 68 | pub(crate) internal: ultralight_sys::JSValueRef, 69 | #[allow(unused)] 70 | pub(crate) guard: &'a JSContext<'a>, 71 | } 72 | 73 | impl IsJSValue for JSBoolean<'_> { 74 | fn get_value(&self) -> ultralight_sys::JSValueRef { 75 | self.internal 76 | } 77 | } 78 | 79 | pub struct JSNumber<'a> { 80 | pub(crate) internal: ultralight_sys::JSValueRef, 81 | #[allow(unused)] 82 | pub(crate) guard: &'a JSContext<'a>, 83 | } 84 | 85 | impl IsJSValue for JSNumber<'_> { 86 | fn get_value(&self) -> ultralight_sys::JSValueRef { 87 | self.internal 88 | } 89 | } 90 | 91 | pub struct JSObject<'a> { 92 | pub(crate) internal: ultralight_sys::JSValueRef, 93 | #[allow(unused)] 94 | pub(crate) guard: &'a JSContext<'a>, 95 | } 96 | 97 | impl IsJSValue for JSObject<'_> { 98 | fn get_value(&self) -> ultralight_sys::JSValueRef { 99 | self.internal 100 | } 101 | } 102 | 103 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 104 | pub enum JSType { 105 | Undefined, 106 | Null, 107 | Boolean, 108 | Number, 109 | String, 110 | Object, 111 | Opaque, 112 | } 113 | 114 | impl TryFrom for JSType { 115 | type Error = ULError; 116 | fn try_from(value: ultralight_sys::JSType) -> Result { 117 | match value { 118 | ultralight_sys::JSType_kJSTypeUndefined => Ok(Self::Undefined), 119 | ultralight_sys::JSType_kJSTypeNull => Ok(Self::Null), 120 | ultralight_sys::JSType_kJSTypeBoolean => Ok(Self::Boolean), 121 | ultralight_sys::JSType_kJSTypeNumber => Ok(Self::Number), 122 | ultralight_sys::JSType_kJSTypeString => Ok(Self::String), 123 | ultralight_sys::JSType_kJSTypeObject => Ok(Self::Object), 124 | _ => Err(ULError::JSValueTypeNotKnown), 125 | } 126 | } 127 | } 128 | 129 | impl From for ultralight_sys::JSType { 130 | fn from(value: JSType) -> Self { 131 | match value { 132 | JSType::Undefined => ultralight_sys::JSType_kJSTypeUndefined, 133 | JSType::Null => ultralight_sys::JSType_kJSTypeNull, 134 | JSType::Boolean => ultralight_sys::JSType_kJSTypeBoolean, 135 | JSType::Number => ultralight_sys::JSType_kJSTypeNumber, 136 | JSType::String => ultralight_sys::JSType_kJSTypeString, 137 | JSType::Object => ultralight_sys::JSType_kJSTypeObject, 138 | JSType::Opaque => panic!("Cannot convert opaque type to JSType"), 139 | } 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /crates/librehardwaremonitor-rs/src/hardware/impl.rs: -------------------------------------------------------------------------------- 1 | use crate::{Computer, HardwareIter, HardwareType, LibreError, LibreResult, SensorIter}; 2 | 3 | pub struct Hardware<'a> { 4 | pub(crate) guard: &'a Computer, 5 | pub(crate) indices: Vec, 6 | } 7 | 8 | impl<'a> Hardware<'a> { 9 | pub(crate) fn new(guard: &'a Computer, indices: Vec) -> Self { 10 | Self { guard, indices } 11 | } 12 | 13 | pub fn subhardware_iter(&self) -> HardwareIter<'_> { 14 | HardwareIter { 15 | inner: self, 16 | index: 0, 17 | len: self.get_subhardware_len(), 18 | } 19 | } 20 | 21 | pub fn get_name(&self) -> Option { 22 | let name_ptr = unsafe { 23 | librehardwaremonitor_sys::get_hardware_name( 24 | self.guard.id, 25 | self.indices.as_ptr() as _, 26 | self.indices.len() as i32, 27 | ) 28 | }; 29 | if name_ptr.is_null() { 30 | None 31 | } else { 32 | let name_cstr = unsafe { std::ffi::CStr::from_ptr(name_ptr as _) }; 33 | let name = name_cstr.to_str().ok()?.to_owned(); 34 | 35 | Some(name) 36 | } 37 | } 38 | 39 | pub fn set_name(&mut self, name: &str) -> LibreResult<()> { 40 | let name = std::ffi::CString::new(name).or(Err(LibreError::InvalidName))?; 41 | let result = unsafe { 42 | librehardwaremonitor_sys::set_hardware_name( 43 | self.guard.id, 44 | self.indices.as_mut_ptr(), 45 | self.indices.len() as i32, 46 | name.as_ptr() as _, 47 | ) 48 | }; 49 | 50 | if result == -1 { 51 | Err(LibreError::FailedToSetName) 52 | } else { 53 | Ok(()) 54 | } 55 | } 56 | 57 | pub fn get_type(&mut self) -> HardwareType { 58 | let hardware_type = unsafe { 59 | librehardwaremonitor_sys::get_hardware_type( 60 | self.guard.id, 61 | self.indices.as_mut_ptr(), 62 | self.indices.len() as i32, 63 | ) 64 | }; 65 | 66 | hardware_type.into() 67 | } 68 | 69 | pub fn get_sensors_len(&self) -> usize { 70 | unsafe { 71 | librehardwaremonitor_sys::get_sensors_len_hardware( 72 | self.guard.id, 73 | self.indices.as_ptr() as _, 74 | self.indices.len() as i32, 75 | ) 76 | } 77 | .try_into() 78 | .unwrap() 79 | } 80 | 81 | pub fn get_subhardware_len(&self) -> usize { 82 | unsafe { 83 | librehardwaremonitor_sys::get_subhardware_len_hardware( 84 | self.guard.id, 85 | self.indices.as_ptr() as _, 86 | self.indices.len() as i32, 87 | ) 88 | } 89 | .try_into() 90 | .unwrap() 91 | } 92 | 93 | pub fn sensor_iter(&self) -> SensorIter<'_> { 94 | SensorIter { 95 | inner: self, 96 | index: 0, 97 | len: self.get_sensors_len(), 98 | } 99 | } 100 | 101 | pub fn update(&mut self) { 102 | unsafe { 103 | librehardwaremonitor_sys::update_hardware_object( 104 | self.guard.id, 105 | self.indices.as_ptr() as _, 106 | self.indices.len() as i32, 107 | ) 108 | }; 109 | } 110 | 111 | pub fn get_report(&mut self) -> Option { 112 | let report_ptr = unsafe { 113 | librehardwaremonitor_sys::get_hardware_report( 114 | self.guard.id, 115 | self.indices.as_mut_ptr(), 116 | self.indices.len() as i32, 117 | ) 118 | }; 119 | if report_ptr.is_null() { 120 | None 121 | } else { 122 | let report_cstr = unsafe { std::ffi::CStr::from_ptr(report_ptr as _) }; 123 | let report = report_cstr.to_str().ok()?.to_owned(); 124 | 125 | Some(report) 126 | } 127 | } 128 | 129 | pub fn get_parent(&mut self) -> Option> { 130 | let mut parent_indices = self.indices.clone(); 131 | let parent_indices_len = parent_indices.len(); 132 | if parent_indices_len < 2 { 133 | None 134 | } else { 135 | parent_indices.truncate(parent_indices_len - 1); 136 | 137 | Some(Hardware::new(self.guard, parent_indices)) 138 | } 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /crates/smol-static/src/server.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Context; 2 | use std::path::{Path, PathBuf}; 3 | use tachyonix::TryRecvError; 4 | use tokio::{ 5 | io::{AsyncReadExt, AsyncWriteExt}, 6 | net::{TcpListener, TcpStream}, 7 | }; 8 | 9 | use crate::filesystem::{read_http_file, HTTPFile}; 10 | 11 | pub enum ServerMessage { 12 | SetBasePath(PathBuf), 13 | Shutdown, 14 | } 15 | 16 | pub struct Server { 17 | base_path: PathBuf, 18 | receiver: tachyonix::Receiver, 19 | } 20 | 21 | unsafe impl Send for Server {} 22 | unsafe impl Sync for Server {} 23 | 24 | impl Server { 25 | pub fn new>(base_path: P) -> (tachyonix::Sender, Self) { 26 | let base_path = base_path.as_ref().to_path_buf(); 27 | let (sender, receiver) = tachyonix::channel(10); 28 | 29 | ( 30 | sender, 31 | Self { 32 | base_path, 33 | receiver, 34 | }, 35 | ) 36 | } 37 | 38 | pub fn set_base_path>(&mut self, base_path: P) { 39 | self.base_path = base_path.as_ref().to_path_buf(); 40 | } 41 | 42 | fn rx_from_mpsc(&mut self) -> bool { 43 | if let Ok(message) = self.receiver.try_recv() { 44 | match message { 45 | ServerMessage::SetBasePath(base_path) => { 46 | self.set_base_path(base_path); 47 | } 48 | ServerMessage::Shutdown => { 49 | return false; 50 | } 51 | } 52 | 53 | true 54 | } else { 55 | !matches!(self.receiver.try_recv(), Err(TryRecvError::Closed)) 56 | } 57 | } 58 | 59 | pub async fn run(&mut self, port: usize) -> anyhow::Result<()> { 60 | let listener = TcpListener::bind(&format!("127.0.0.1:{port}")) 61 | .await 62 | .context("Failed to bind http server!")?; 63 | 64 | loop { 65 | let (mut socket, _) = listener 66 | .accept() 67 | .await 68 | .context("Failed to accept connection!")?; 69 | 70 | if !self.rx_from_mpsc() { 71 | break; 72 | } 73 | 74 | let cloned_base = self.base_path.clone(); 75 | 76 | tokio::spawn(async move { 77 | let base = cloned_base; 78 | let mut buf = [0; 2048]; 79 | let n = socket 80 | .read(&mut buf) 81 | .await 82 | .expect("Failed to read request!"); 83 | let request = String::from_utf8_lossy(&buf[..n]); 84 | let request = request.split_whitespace().collect::>(); 85 | let second_node = request.get(1); 86 | 87 | if let Some(path) = second_node { 88 | let path = path.trim_start_matches('/'); 89 | let path = if path.is_empty() { "index.html" } else { path }; 90 | if let Ok(HTTPFile { 91 | content, 92 | content_length, 93 | content_type, 94 | }) = read_http_file(path, &base).await 95 | { 96 | let response_header = format!( 97 | "HTTP/1.1 200 OK\r\nContent-Encoding: gzip\r\nContent-Type: {}\r\nContent-Length: {}\r\nConnection: close\r\n\r\n", 98 | content_type, content_length 99 | ); 100 | let mut response = vec![]; 101 | response.extend(response_header.as_bytes().iter()); 102 | response.extend(content.iter()); 103 | socket 104 | .write_all(response.as_slice()) 105 | .await 106 | .expect("Failed to write response!"); 107 | socket.flush().await.expect("Failed to flush socket!"); 108 | } else { 109 | return_404(socket).await.expect("Failed to write 404!"); 110 | } 111 | } 112 | }); 113 | } 114 | 115 | Ok(()) 116 | } 117 | } 118 | 119 | async fn return_404(mut stream: TcpStream) -> anyhow::Result<()> { 120 | let response_header = "HTTP/1.1 404 Not Found\r\nConnection: close\r\n\r\n".to_string(); 121 | 122 | stream 123 | .write_all(response_header.as_bytes()) 124 | .await 125 | .context("Failed to close stream!") 126 | } 127 | -------------------------------------------------------------------------------- /crates/librehardwaremonitor-rs/src/sensor/impl.rs: -------------------------------------------------------------------------------- 1 | use crate::{Computer, Hardware, LibreError, LibreResult, SensorType}; 2 | 3 | pub struct Sensor<'a> { 4 | pub(crate) computer_guard: &'a Computer, 5 | pub(crate) hardware_guard: &'a Hardware<'a>, 6 | pub(crate) index: i32, 7 | } 8 | 9 | impl<'a> Sensor<'a> { 10 | pub fn get_name(&mut self) -> Option { 11 | let name_ptr = unsafe { 12 | librehardwaremonitor_sys::get_sensor_name( 13 | self.computer_guard.id, 14 | self.hardware_guard.indices.as_ptr() as _, 15 | self.hardware_guard.indices.len() as i32, 16 | self.index, 17 | ) 18 | }; 19 | if name_ptr.is_null() { 20 | None 21 | } else { 22 | let name_cstr = unsafe { std::ffi::CStr::from_ptr(name_ptr as _) }; 23 | let name = name_cstr.to_str().ok()?.to_owned(); 24 | 25 | Some(name) 26 | } 27 | } 28 | 29 | pub fn set_name(&mut self, name: &str) -> LibreResult<()> { 30 | let name = std::ffi::CString::new(name).or(Err(LibreError::InvalidName))?; 31 | let result = unsafe { 32 | librehardwaremonitor_sys::set_sensor_name( 33 | self.computer_guard.id, 34 | self.hardware_guard.indices.as_ptr() as _, 35 | self.hardware_guard.indices.len() as i32, 36 | self.index, 37 | name.as_ptr() as _, 38 | ) 39 | }; 40 | 41 | if result == -1 { 42 | Err(LibreError::FailedToSetName) 43 | } else { 44 | Ok(()) 45 | } 46 | } 47 | 48 | pub fn get_value(&mut self) -> LibreResult { 49 | let value = unsafe { 50 | librehardwaremonitor_sys::get_sensor_value( 51 | self.computer_guard.id, 52 | self.hardware_guard.indices.as_ptr() as _, 53 | self.hardware_guard.indices.len() as i32, 54 | self.index, 55 | ) 56 | }; 57 | 58 | if value == -1f32 { 59 | Err(LibreError::FailedToGetSensorValue) 60 | } else { 61 | Ok(value) 62 | } 63 | } 64 | 65 | pub fn get_type(&mut self) -> SensorType { 66 | let sensor_type = unsafe { 67 | librehardwaremonitor_sys::get_sensor_type( 68 | self.computer_guard.id, 69 | self.hardware_guard.indices.as_ptr() as _, 70 | self.hardware_guard.indices.len() as i32, 71 | self.index, 72 | ) 73 | }; 74 | 75 | sensor_type.into() 76 | } 77 | 78 | pub fn get_min_value(&mut self) -> f32 { 79 | unsafe { 80 | librehardwaremonitor_sys::get_min_value_sensor( 81 | self.computer_guard.id, 82 | self.hardware_guard.indices.as_ptr() as _, 83 | self.hardware_guard.indices.len() as i32, 84 | self.index, 85 | ) 86 | } 87 | } 88 | 89 | pub fn get_max_value(&mut self) -> f32 { 90 | unsafe { 91 | librehardwaremonitor_sys::get_max_value_sensor( 92 | self.computer_guard.id, 93 | self.hardware_guard.indices.as_ptr() as _, 94 | self.hardware_guard.indices.len() as i32, 95 | self.index, 96 | ) 97 | } 98 | } 99 | 100 | pub fn clear_sensor_values(&mut self) { 101 | unsafe { 102 | librehardwaremonitor_sys::clear_sensor_values( 103 | self.computer_guard.id, 104 | self.hardware_guard.indices.as_ptr() as _, 105 | self.hardware_guard.indices.len() as i32, 106 | self.index, 107 | ) 108 | }; 109 | } 110 | 111 | pub fn get_parent(&mut self) -> Hardware<'a> { 112 | Hardware::new(self.computer_guard, self.hardware_guard.indices.clone()) 113 | } 114 | 115 | pub fn get_id(&mut self) -> Option { 116 | let id_ptr = unsafe { 117 | librehardwaremonitor_sys::get_sensor_id( 118 | self.computer_guard.id, 119 | self.hardware_guard.indices.as_ptr() as _, 120 | self.hardware_guard.indices.len() as i32, 121 | self.index, 122 | ) 123 | }; 124 | if id_ptr.is_null() { 125 | None 126 | } else { 127 | let id_cstr = unsafe { std::ffi::CStr::from_ptr(id_ptr as _) }; 128 | let id = id_cstr.to_str().ok()?.to_owned(); 129 | 130 | Some(id) 131 | } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /frontend/theme.ts: -------------------------------------------------------------------------------- 1 | import type { CustomThemeConfig } from "@skeletonlabs/tw-plugin"; 2 | 3 | export const zfcTheme: CustomThemeConfig = { 4 | name: "zfcTheme", 5 | properties: { 6 | // =~= Theme Properties =~= 7 | "--theme-font-family-base": `Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'`, 8 | "--theme-font-family-heading": `ui-serif, Georgia, Cambria, 'Times New Roman', Times, serif`, 9 | "--theme-font-color-base": "0 0 0", 10 | "--theme-font-color-dark": "255 255 255", 11 | "--theme-rounded-base": "9999px", 12 | "--theme-rounded-container": "12px", 13 | "--theme-border-base": "1px", 14 | // =~= Theme On-X Colors =~= 15 | "--on-primary": "0 0 0", 16 | "--on-secondary": "255 255 255", 17 | "--on-tertiary": "0 0 0", 18 | "--on-success": "0 0 0", 19 | "--on-warning": "0 0 0", 20 | "--on-error": "255 255 255", 21 | "--on-surface": "255 255 255", 22 | // =~= Theme Colors =~= 23 | // primary | #fb7ec1 24 | "--color-primary-50": "254 236 246", // #feecf6 25 | "--color-primary-100": "254 229 243", // #fee5f3 26 | "--color-primary-200": "254 223 240", // #fedff0 27 | "--color-primary-300": "253 203 230", // #fdcbe6 28 | "--color-primary-400": "252 165 212", // #fca5d4 29 | "--color-primary-500": "251 126 193", // #fb7ec1 30 | "--color-primary-600": "226 113 174", // #e271ae 31 | "--color-primary-700": "188 95 145", // #bc5f91 32 | "--color-primary-800": "151 76 116", // #974c74 33 | "--color-primary-900": "123 62 95", // #7b3e5f 34 | // secondary | #942afe 35 | "--color-secondary-50": "239 223 255", // #efdfff 36 | "--color-secondary-100": "234 212 255", // #ead4ff 37 | "--color-secondary-200": "228 202 255", // #e4caff 38 | "--color-secondary-300": "212 170 255", // #d4aaff 39 | "--color-secondary-400": "180 106 254", // #b46afe 40 | "--color-secondary-500": "148 42 254", // #942afe 41 | "--color-secondary-600": "133 38 229", // #8526e5 42 | "--color-secondary-700": "111 32 191", // #6f20bf 43 | "--color-secondary-800": "89 25 152", // #591998 44 | "--color-secondary-900": "73 21 124", // #49157c 45 | // tertiary | #0EA5E9 46 | "--color-tertiary-50": "219 242 252", // #dbf2fc 47 | "--color-tertiary-100": "207 237 251", // #cfedfb 48 | "--color-tertiary-200": "195 233 250", // #c3e9fa 49 | "--color-tertiary-300": "159 219 246", // #9fdbf6 50 | "--color-tertiary-400": "86 192 240", // #56c0f0 51 | "--color-tertiary-500": "14 165 233", // #0EA5E9 52 | "--color-tertiary-600": "13 149 210", // #0d95d2 53 | "--color-tertiary-700": "11 124 175", // #0b7caf 54 | "--color-tertiary-800": "8 99 140", // #08638c 55 | "--color-tertiary-900": "7 81 114", // #075172 56 | // success | #84cc16 57 | "--color-success-50": "237 247 220", // #edf7dc 58 | "--color-success-100": "230 245 208", // #e6f5d0 59 | "--color-success-200": "224 242 197", // #e0f2c5 60 | "--color-success-300": "206 235 162", // #ceeba2 61 | "--color-success-400": "169 219 92", // #a9db5c 62 | "--color-success-500": "132 204 22", // #84cc16 63 | "--color-success-600": "119 184 20", // #77b814 64 | "--color-success-700": "99 153 17", // #639911 65 | "--color-success-800": "79 122 13", // #4f7a0d 66 | "--color-success-900": "65 100 11", // #41640b 67 | // warning | #EAB308 68 | "--color-warning-50": "252 244 218", // #fcf4da 69 | "--color-warning-100": "251 240 206", // #fbf0ce 70 | "--color-warning-200": "250 236 193", // #faecc1 71 | "--color-warning-300": "247 225 156", // #f7e19c 72 | "--color-warning-400": "240 202 82", // #f0ca52 73 | "--color-warning-500": "234 179 8", // #EAB308 74 | "--color-warning-600": "211 161 7", // #d3a107 75 | "--color-warning-700": "176 134 6", // #b08606 76 | "--color-warning-800": "140 107 5", // #8c6b05 77 | "--color-warning-900": "115 88 4", // #735804 78 | // error | #D41976 79 | "--color-error-50": "249 221 234", // #f9ddea 80 | "--color-error-100": "246 209 228", // #f6d1e4 81 | "--color-error-200": "244 198 221", // #f4c6dd 82 | "--color-error-300": "238 163 200", // #eea3c8 83 | "--color-error-400": "225 94 159", // #e15e9f 84 | "--color-error-500": "212 25 118", // #D41976 85 | "--color-error-600": "191 23 106", // #bf176a 86 | "--color-error-700": "159 19 89", // #9f1359 87 | "--color-error-800": "127 15 71", // #7f0f47 88 | "--color-error-900": "104 12 58", // #680c3a 89 | // surface | #2b2b2b 90 | "--color-surface-50": "223 223 223", // #dfdfdf 91 | "--color-surface-100": "213 213 213", // #d5d5d5 92 | "--color-surface-200": "202 202 202", // #cacaca 93 | "--color-surface-300": "170 170 170", // #aaaaaa 94 | "--color-surface-400": "107 107 107", // #6b6b6b 95 | "--color-surface-500": "43 43 43", // #2b2b2b 96 | "--color-surface-600": "39 39 39", // #272727 97 | "--color-surface-700": "32 32 32", // #202020 98 | "--color-surface-800": "26 26 26", // #1a1a1a 99 | "--color-surface-900": "21 21 21", // #151515 100 | }, 101 | }; 102 | --------------------------------------------------------------------------------