Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

attempt to fix crash on amd #9

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 9 additions & 8 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use cosmic::app::{Core, Task};
use cosmic::applet::padded_control;
use cosmic::cosmic_config::CosmicConfigEntry;
use cosmic::cosmic_theme::{ThemeMode, THEME_MODE_ID};
use cosmic::iced::futures::executor::block_on;
use cosmic::iced::window::Id;
use cosmic::iced::{Alignment, Length, Limits, Subscription};
use cosmic::iced_runtime::core::window;
Expand All @@ -15,7 +16,7 @@ use cosmic::{iced_runtime, Element};
// use tokio::sync::mpsc::Sender;
use crate::monitor::{DisplayId, EventToSub, Monitor};
use crate::{fl, monitor};
use tokio::sync::watch::Sender;
use tokio::sync::mpsc::Sender;

const ID: &str = "io.github.maciekk64.CosmicExtAppletExternalMonitorBrightness";
const ICON_HIGH: &str = "cosmic-applet-battery-display-brightness-high-symbolic";
Expand All @@ -41,15 +42,13 @@ pub enum Message {
ThemeModeConfigChanged(ThemeMode),
SetDarkMode(bool),
Ready((HashMap<DisplayId, Monitor>, Sender<EventToSub>)),
BrightnessWasUpdated(DisplayId, u16),
BrightnessWasUpdated(HashMap<DisplayId, u16>),
}

impl Window {
pub fn send(&self, e: EventToSub) {
if let Some(sender) = &self.sender {
sender.send(e).unwrap();

// block_on(sender.send(e)).unwrap();
block_on(sender.send(e)).unwrap();
}
}
}
Expand Down Expand Up @@ -139,9 +138,11 @@ impl cosmic::Application for Window {
self.monitors = mon;
self.sender.replace(sender);
}
Message::BrightnessWasUpdated(id, value) => {
if let Some(monitor) = self.monitors.get_mut(&id) {
monitor.brightness = value;
Message::BrightnessWasUpdated(updates) => {
for (id, value) in updates {
if let Some(monitor) = self.monitors.get_mut(&id) {
monitor.brightness = value;
}
}
}
}
Expand Down
225 changes: 140 additions & 85 deletions src/monitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use cosmic::iced::{
stream,
};
use ddc_hi::{Ddc, Display};
use tokio::sync::watch::Receiver;
use tokio::time::sleep;

use crate::app::Message;

Expand All @@ -30,110 +30,165 @@ pub enum EventToSub {
}

enum State {
Waiting,
Fetch,
Ready(HashMap<String, Arc<Mutex<Display>>>, Receiver<EventToSub>),
Fetching,
Ready,
}

const MAX_WAITING: Duration = Duration::from_secs(4);
const DEFAULT_WAITING: Duration = Duration::from_millis(50);

pub fn sub() -> impl Stream<Item = Message> {
stream::channel(100, |mut output| async move {
let mut state = State::Waiting;
let displays = Arc::new(Mutex::new(HashMap::new()));

let mut rx = {
let mut res = HashMap::new();

for display in Display::enumerate() {
let info = &display.info;
info!("{:?}", info);

let mut duration = Duration::from_millis(50);
let mon = Monitor {
name: display.info.model_name.clone().unwrap_or_default(),
brightness: 0,
};

res.insert(display.info.id.clone(), mon);
displays
.lock()
.unwrap()
.insert(display.info.id.clone(), display);
}

let (tx, rx) = tokio::sync::mpsc::channel(100);
output.send(Message::Ready((res, tx))).await.unwrap();
rx
};

let mut state = State::Ready;
let mut duration = DEFAULT_WAITING;

let mut request_buff = Vec::new();

loop {
match &mut state {
State::Waiting => {
State::Fetching => {
tokio::time::sleep(duration).await;
duration *= 2;
state = State::Fetch;
}
State::Fetch => {
let mut res = HashMap::new();

let mut displays = HashMap::new();

debug!("start enumerate");

for mut display in Display::enumerate() {
let brightness = match display.handle.get_vcp_feature(BRIGHTNESS_CODE) {
Ok(v) => v.value(),
Err(e) => {
// on my machine, i get this error when starting the session
// can't get_vcp_feature: DDC/CI error: Expected DDC/CI length bit
// This go away after the third attempt
error!("can't get_vcp_feature: {e}");
state = State::Waiting;
break;
}
};

let mon = Monitor {
name: display.info.model_name.clone().unwrap_or_default(),
brightness,
};
let (error, res) = {
let displays = displays.clone();

let j = tokio::task::spawn_blocking(move || {
let mut res = HashMap::new();

let mut displays = displays.lock().unwrap();

res.insert(display.info.id.clone(), mon);
displays.insert(display.info.id.clone(), Arc::new(Mutex::new(display)));
debug!("start enumerate");
for (id, display) in displays.iter_mut() {
match display.handle.get_vcp_feature(BRIGHTNESS_CODE) {
Ok(v) => {
res.insert(id.clone(), v.value());
}
Err(e) => {
// on my machine, i get this error when starting the session
// can't get_vcp_feature: DDC/CI error: Expected DDC/CI length bit
// This go away after the third attempt
error!("can't get_vcp_feature: {e}");
continue;
}
};
}
(res.len() != displays.len(), res)
});

j.await.unwrap()
};

output
.send(Message::BrightnessWasUpdated(res))
.await
.unwrap();

if error {
duration *= 2;
if duration > MAX_WAITING {
state = State::Ready;
duration = DEFAULT_WAITING;
}
} else {
duration = DEFAULT_WAITING;
state = State::Ready;
}
}
State::Ready => {
if let Some(e) = rx.recv().await {
request_buff.push(e);
}

if let State::Waiting = state {
continue;
while let Ok(e) = rx.try_recv() {
request_buff.push(e);
}

debug!("end enumerate");
let mut set = HashMap::new();
let mut refresh = false;

let (tx, mut rx) = tokio::sync::watch::channel(EventToSub::Refresh);
rx.mark_unchanged();
for request in request_buff.drain(..) {
match request {
EventToSub::Refresh => refresh = true,
EventToSub::Set(id, value) => {
set.insert(id, value);
}
}
}

output.send(Message::Ready((res, tx))).await.unwrap();
state = State::Ready(displays, rx);
}
State::Ready(displays, rx) => {
rx.changed().await.unwrap();

let last = rx.borrow_and_update().clone();
match last {
EventToSub::Refresh => {
for (id, display) in displays {
let res = display
.lock()
.unwrap()
.handle
.get_vcp_feature(BRIGHTNESS_CODE);

match res {
Ok(value) => {
output
.send(Message::BrightnessWasUpdated(
id.clone(),
value.value(),
))
.await
.unwrap();
if refresh {
let displays = displays.clone();

let j = tokio::task::spawn_blocking(move || {
let mut res = HashMap::new();

for (id, display) in displays.lock().unwrap().iter_mut() {
match display.handle.get_vcp_feature(BRIGHTNESS_CODE) {
Ok(v) => {
res.insert(id.clone(), v.value());
}
Err(e) => {
// on my machine, i get this error when starting the session
// can't get_vcp_feature: DDC/CI error: Expected DDC/CI length bit
// This go away after the third attempt
error!("can't get_vcp_feature: {e}");
continue;
}
Err(err) => error!("{:?}", err),
}
};
}
}
EventToSub::Set(id, value) => {
let display = displays.get_mut(&id).unwrap().clone();

let j = tokio::task::spawn_blocking(move || {
if let Err(err) = display
.lock()
.unwrap()
.handle
.set_vcp_feature(BRIGHTNESS_CODE, value)
{
error!("{:?}", err);
}
});

j.await.unwrap();
tokio::time::sleep(Duration::from_millis(50)).await;
}
res
});

let res = j.await.unwrap();

output
.send(Message::BrightnessWasUpdated(res))
.await
.unwrap();
}

let displays = displays.clone();

let j = tokio::task::spawn_blocking(move || {
for (id, value) in set.drain() {
let mut displays = displays.lock().unwrap();

let display = displays.get_mut(&id).unwrap();

debug!("set {} to {}", id, value);
if let Err(err) = display.handle.set_vcp_feature(BRIGHTNESS_CODE, value)
{
error!("{:?}", err);
}
}
});
j.await.unwrap();
sleep(Duration::from_millis(50)).await;
}
}
}
Expand Down