,
73 | cwd: String,
74 | }
75 |
--------------------------------------------------------------------------------
/frontend/src/routes/settings/+page.svelte:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 | About
8 |
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 |

18 |
19 |
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 |
--------------------------------------------------------------------------------