aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorFuwn <[email protected]>2022-12-31 23:08:28 +0000
committerFuwn <[email protected]>2023-03-19 02:17:16 +0000
commit1f062b0061dd347b1116b7abe8cece42f33b63d8 (patch)
tree56f4cda4526747f44c8600838e12fe2783b35a2b /src
parentMerge pull request #2 from Fuwn/renovate/serde-1.x (diff)
downloadelem-1f062b0061dd347b1116b7abe8cece42f33b63d8.tar.xz
elem-1f062b0061dd347b1116b7abe8cece42f33b63d8.zip
feat: nice messagebox errors
Diffstat (limited to 'src')
-rw-r--r--src/logitech.rs30
-rw-r--r--src/tray.rs79
2 files changed, 94 insertions, 15 deletions
diff --git a/src/logitech.rs b/src/logitech.rs
index 28ff45c..7f0a058 100644
--- a/src/logitech.rs
+++ b/src/logitech.rs
@@ -20,6 +20,8 @@ use std::collections::HashMap;
use serde_derive::{Deserialize, Serialize};
use tungstenite::{client::IntoClientRequest, Message};
+use crate::tray::quit;
+
#[derive(Serialize, Deserialize, Debug)]
pub struct DeviceInfo {
pub id: String,
@@ -90,22 +92,31 @@ impl Device {
fn connection() -> tungstenite::WebSocket<
tungstenite::stream::MaybeTlsStream<std::net::TcpStream>,
> {
+ // This will never fail because the URL is hardcoded
let url = url::Url::parse("ws://localhost:9010").unwrap();
let (mut ws_stream, _) = tungstenite::connect({
+ // This will never fail because the URL is valid
let mut request = url.into_client_request().unwrap();
// https://github.com/snapview/tungstenite-rs/issues/279
// https://github.com/snapview/tungstenite-rs/issues/145#issuecomment-713581499
+ //
+ // This unwrap will never fail either because we are parsing a hardcoded,
+ // known string
request
.headers_mut()
.insert("Sec-WebSocket-Protocol", "json".parse().unwrap());
request
})
- .unwrap();
+ .unwrap_or_else(|_| {
+ quit("failed to connect to the logitech g hub websocket. is it running?");
+ });
- ws_stream.read_message().unwrap();
+ ws_stream.read_message().unwrap_or_else(|_| {
+ quit("failed to read message from the logitech g hub websocket");
+ });
ws_stream
}
@@ -122,8 +133,12 @@ pub fn wireless_devices() -> HashMap<String, DeviceInfo> {
})
.to_string(),
))
- .unwrap();
+ .unwrap_or_else(|_| {
+ quit("failed to write message to the logitech g hub websocket");
+ });
+ // This will never fail because if we even got this far, the `WebSocket` is
+ // working.
let devices = serde_json::from_value::<DeviceList>(
serde_json::from_str(&stream.read_message().unwrap().into_text().unwrap())
.unwrap(),
@@ -166,14 +181,21 @@ pub fn device(display_name: &str) -> Device {
stream
.write_message(Message::binary(
+ // The `unwrap` for `get` should never fail because we know the devices that
+ // are available, and if the user unplugs one of their devices mid-battery state check,
+ // that's on them. :P
serde_json::json!({
"path": format!("/battery/{}/state", wireless_devices().get(display_name).unwrap().id),
"verb": "GET"
})
.to_string(),
))
- .unwrap();
+ .unwrap_or_else(|_| {
+ quit("failed to write message to the logitech g hub websocket");
+ });
+ // Once again, this should never fail because if we even got this far, the
+ // `WebSocket` is working.
serde_json::from_value::<Device>(
serde_json::from_str(&stream.read_message().unwrap().into_text().unwrap())
.unwrap(),
diff --git a/src/tray.rs b/src/tray.rs
index a33d081..7a4f3b2 100644
--- a/src/tray.rs
+++ b/src/tray.rs
@@ -15,7 +15,12 @@
// Copyright (C) 2022-2022 Fuwn <[email protected]>
// SPDX-License-Identifier: GPL-3.0-only
-use std::sync::{Arc, Mutex};
+use std::{
+ ffi::OsStr,
+ iter::once,
+ os::windows::ffi::OsStrExt,
+ sync::{Arc, Mutex},
+};
use tao::{
event::Event, event_loop::ControlFlow, menu, menu::CustomMenuItem,
@@ -79,17 +84,22 @@ impl Tray {
///
/// Useful for displaying informational icons
fn force_icon(code: &str) -> Icon {
- trace!("building forced icon: {}", code);
+ trace!("building forced icon '{}'", code);
let image = image::load_from_memory(&crate::ascii_art::number_to_image(
+ // This will never fail because we as the author provide a code that is
+ // always a number in string form.
code.parse().unwrap(),
))
- .unwrap()
+ .unwrap_or_else(|_| quit(&format!("failed to load forced icon '{code}'")))
.into_rgba8();
let (width, height) = image.dimensions();
- let icon = Icon::from_rgba(image.into_raw(), width, height).unwrap();
+ let icon =
+ Icon::from_rgba(image.into_raw(), width, height).unwrap_or_else(|_| {
+ quit(&format!("failed to convert forced icon '{code}' to rgba"))
+ });
- trace!("built forced icon: {}", code);
+ trace!("built forced icon '{}'", code);
icon
}
@@ -97,7 +107,7 @@ impl Tray {
/// Create a tray icon compatible icon from a devices battery level
fn icon(selected_device_display_name: &Option<String>) -> Icon {
trace!(
- "building icon for display name: {:?}",
+ "building icon for display name '{:?}'",
selected_device_display_name
);
@@ -117,13 +127,24 @@ impl Tray {
.percentage(),
)
})
- .unwrap()
+ .unwrap_or_else(|_| {
+ quit(&format!(
+ "failed to load icon for display name '{:?}'",
+ selected_device_display_name
+ ))
+ })
.into_rgba8();
let (width, height) = image.dimensions();
- let icon = Icon::from_rgba(image.into_raw(), width, height).unwrap();
+ let icon =
+ Icon::from_rgba(image.into_raw(), width, height).unwrap_or_else(|_| {
+ quit(&format!(
+ "failed to convert icon for display name '{:?}' to rgba",
+ selected_device_display_name
+ ))
+ });
trace!(
- "built icon for display name: {:?}",
+ "built icon for display name '{:?}'",
selected_device_display_name
);
@@ -217,7 +238,12 @@ impl Tray {
// Making sure that the last device, the default device, is never the
// dummy device
+ //
+ // There will always be a last device because the dummy device is always
+ // present.
if devices.last().unwrap().display_name == "Dummy (Debug)" {
+ // We can always pop the last device because there will always be a
+ // last element even if there is only one.
let last = devices.pop().unwrap();
devices.insert(0, last);
@@ -254,7 +280,7 @@ impl Tray {
.with_id(main_tray_id)
.with_tooltip("elem")
.build(&event_loop)
- .unwrap(),
+ .unwrap_or_else(|_| self::quit("failed to build system tray")),
));
let mut devices = local_self.lock().unwrap().devices.clone();
let icon_self = self.inner.clone();
@@ -307,7 +333,7 @@ impl Tray {
if devices.iter().any(|d| d.clone().id() == menu_id) {
for device in &mut devices {
if menu_id == device.clone().id() {
- debug!("selected device: {}", device.clone().title());
+ debug!("selected device '{}'", device.clone().title());
device.set_selected(true);
// Ellipsis icon to indicate background process
system_tray
@@ -365,3 +391,34 @@ impl Tray {
});
}
}
+
+pub fn quit(message: &str) -> ! {
+ message_box(message);
+ panic!("{}", message);
+}
+
+pub fn message_box(
+ // title: &str,
+ message: &str, // buttons: u32, icon: u32
+) -> i32 {
+ let buttons = winuser::MB_OK;
+ let icon = winuser::MB_ICONEXCLAMATION;
+ let other_options = winuser::MB_SETFOREGROUND | winuser::MB_TOPMOST;
+
+ unsafe {
+ winuser::MessageBoxW(
+ std::ptr::null_mut(),
+ OsStr::new(message)
+ .encode_wide()
+ .chain(once(0))
+ .collect::<Vec<u16>>()
+ .as_ptr(),
+ OsStr::new("elem") // title
+ .encode_wide()
+ .chain(once(0))
+ .collect::<Vec<u16>>()
+ .as_ptr(),
+ buttons | icon | other_options,
+ )
+ }
+}