ruihe774 / brightness-tray

0 stars 0 forks source link

Can't scroll over the sliders to change brightness #1

Closed rp1231 closed 11 months ago

rp1231 commented 11 months ago

Looks good, It detected my internal monitor and external monitor. And the ddcci brightness/contrast/volume controls work perfectly for the external one. Except you can't scroll over the sliders to change the values.

rp1231 commented 11 months ago

Also, looking at the source code, I see that you've enabled mica. That doesn't really seem to be working for me on windows 11.

ruihe774 commented 11 months ago

Also, looking at the source code, I see that you've enabled mica. That doesn't really seem to be working for me on windows 11.

Could you provide a screenshot and your OS build (in Settings/System/About)? Mica works fine on my 22631.2715

rp1231 commented 11 months ago

I'm on the same build as you. I've posted a screenshot of both twinkle tray with mica blur enabled and brightness-tray for comparison.

Twinkle_Tray_o02LRtntjM brightness-tray_3SucMmxEqY
rp1231 commented 11 months ago

Also the power button isn't working. Not that I have a need for it. But just thought I would mention it just in case.

ruihe774 commented 11 months ago

you can't scroll over the sliders to change the values.

Done in d6ee080c3cec57f956a169dc4a8dd4ea89c24600

rp1231 commented 11 months ago

Confirmed working.

ruihe774 commented 11 months ago

I've posted a screenshot of both twinkle tray with mica blur enabled and brightness-tray for comparison.

Mica is actually working. You can try a dark theme or a vivid wallpaper to see it clearly.

The difference is caused by different implementation. Twinkle Tray is mimicking Mica by itself -- its panel is actually transparent, and it retrieves your wallpaper and blends with it, see https://github.com/xanderfrangos/twinkle-tray/blob/master/src/electron.js#L3455-L3507. On the contrary, my implementation uses the official API DWM_SYSTEMBACKDROP_TYPE which is introduced in Windows 11 22H2 (that is also the reason why we need a very high Windows version). The Mica effect is done by Desktop Window Manager (DWM).

In the custom implementation of Mica in Twinkle Tray, Mica is blended in a more transparent (or more vivid?) way than system default. I don't know whether it is intentional. Maybe we can use HostBackdropBrush to achieve a similar effect, but I think we can do it latter.

rp1231 commented 11 months ago

I see. I'll close this issue in that case.

ruihe774 commented 11 months ago

Also the power button isn't working. Not that I have a need for it. But just thought I would mention it just in case.

My implementation will display the power button if it can read the powerstate of your monitor. However, being able to read it does not mean being able to write it. You can have a look at the DevTool to see if it is an error whiling setting the powerstate.

In Twinkle Tray, power button is not displayed by default even the powerstate is detected. You need to enable it in the settings. You can try to enable it and see whether Twinkle Tray is able to control powerstate. We can adopt a similar approach after the settings page is implemented.

rp1231 commented 11 months ago

The devtools console doesn't show any error while pressing the power button. However I get this error when scrolling over any of the controls.

msedgewebview2_1dQhnVJfR7
rp1231 commented 11 months ago

Also I'm able to turn off my screen via twinkle tray through both the software signal and the ddcci method

ruihe774 commented 11 months ago

Ok, I've tweaked the power button in 01a1327. It will not show if the reported powerstate maximum is less than 4, and it will try the maximum state by default. Could you have a try to see whether it works?

ruihe774 commented 11 months ago

However I get this error when scrolling over any of the controls.

https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#passive

Well, I've learned new knowledge:

If this option is not specified it defaults to false – except that in browsers other than Safari, it defaults to true for wheel, mousewheel, touchstart and touchmove events.

Meanwhile, React has made decision for me: https://github.com/facebook/react/issues/22794#issuecomment-975618944 🤣

ruihe774 commented 11 months ago

I'm on the same build as you. I've posted a screenshot of both twinkle tray with mica blur enabled and brightness-tray for comparison.

Twinkle_Tray_o02LRtntjM brightness-tray_3SucMmxEqY

It seems that your BOE0A9B (Display 1) is an internal laptop display and cannot be controlled by DDC/CI but WMI. WMI support is currently not implemented yet. Maybe someday I will get a laptop to implement it.

rp1231 commented 11 months ago

There's no way to turn the eventlistener passive mode to false?

It seems that your BOE0A9B (Display 1) is an internal laptop display and cannot be controlled by DDC/CI but WMI. WMI support is currently not implemented yet. Maybe someday I will get a laptop to implement it.

Yes, it also needs to have a different icon for the laptop internal monitor as seen in the screenshot.

Also regarding the power button, it's working now but just for turning the screen off.

msedgewebview2_HF11XUxOyi

Twinkle tray also doesn't support turning on the monitor, if turned off.

ruihe774 commented 11 months ago

Also regarding the power button, it's working now but just for turning the screen off.

Yes, it is just a power off button. You can see https://github.com/ruihe774/brightness-tray/blob/01a132778e84af34df1b2c9ca9588841630bd5f8/src/MonitorList.tsx#L39 The text label is hidden for beauty. Maybe I can hide the button if the monitor is powered off. I did not spot this issue because I have only one display, so if it is powered off, I cannot see this button literally. 🤣

rp1231 commented 11 months ago

Switching to vue seems to have solved the eventlistener preventdefault errors. But there's still this one strange error showing up in the console:

:1420/favicon.ico:1 
Failed to load resource: the server responded with a status of 404 (Not Found)
ruihe774 commented 11 months ago

Switching to vue seems to have solved the eventlistener preventdefault errors. But there's still this one strange error showing up in the console:

:1420/favicon.ico:1 
Failed to load resource: the server responded with a status of 404 (Not Found)

Well, switching to Vue is for other reasons, yet it does solve the onWheel problem. The 404 of icon is known. I don't think we need a favicon here, so save some space.

rp1231 commented 11 months ago

@ruihe774 Can you help me with a bit of code I'm stuck on with my tauri app? I need to connect to this bulb once:

let my_bulb_ip = "<my_bulb_ip>";
let mut bulb = Bulb::connect(my_bulb_ip, 55443).await?;

And then keep the connection alive so that other functions can use this connection. Right now I'm using this code:

use std::sync::Mutex;
use std::time::Duration;
use tauri::State;
use yeelight::{Bulb, Effect, Mode, Power};

#[tauri::command]
async fn bulb_toggle() {
    let my_bulb_ip = "<my_bulb_ip>";
    let mut bulb = Bulb::connect(my_bulb_ip, 55443)
        .await
        .expect("Connection failed");
    if let Some(response) = bulb.toggle().await.expect("Error") {
        for v in response.iter() {
            println!("{}", v);
        }
    }
}

#[tauri::command]
async fn set_brightness(level: u8) {
    let my_bulb_ip = "<my_bulb_ip>";

    let mut bulb = Bulb::connect(my_bulb_ip, 55443)
        .await
        .expect("Connection failed");

    if let Some(response) = bulb
        .set_bright(level, Effect::Sudden, Duration::new(0, 0))
        .await
        .expect("Error")
    {
        for v in response.iter() {
            println!("{}", v);
        }
    }
}

// Ideally I want to call this function once on app start and use this connection in the set_brightness and bulb_toggle functions
//#[tauri::command]
//async fn bulb_connect() -> Result<(), Box<dyn std::error::Error>> {
//    let my_bulb_ip = "<my_bulb_ip>";
//   let mut bulb = Bulb::connect(my_bulb_ip, 55443).await?;
//    // .expect("Connection Failed");
//    Ok(())
//}

fn main() {
    env_logger::init();
    tauri::Builder::default()
        .invoke_handler(tauri::generate_handler![set_brightness, bulb_toggle, bulb_connect])
        // .manage(Mutex::new(BulbConnection { bulb: "" }))
        //.manage(AppState { bulb: None })
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

As you can see in this, I'm connecting to this bulb every time the tauri command is called from the frontend

ruihe774 commented 11 months ago

@ruihe774 Can you help me with a bit of code I'm stuck on with my tauri app? I need to connect to this bulb once:

let my_bulb_ip = "<my_bulb_ip>";
let mut bulb = Bulb::connect(my_bulb_ip, 55443).await?;

And then keep the connection alive so that other functions can use this connection. Right now I'm using this code:

use std::sync::Mutex;
use std::time::Duration;
use tauri::State;
use yeelight::{Bulb, Effect, Mode, Power};

#[tauri::command]
async fn bulb_toggle() {
    let my_bulb_ip = "<my_bulb_ip>";
    let mut bulb = Bulb::connect(my_bulb_ip, 55443)
        .await
        .expect("Connection failed");
    if let Some(response) = bulb.toggle().await.expect("Error") {
        for v in response.iter() {
            println!("{}", v);
        }
    }
}

#[tauri::command]
async fn set_brightness(level: u8) {
    let my_bulb_ip = "<my_bulb_ip>";

    let mut bulb = Bulb::connect(my_bulb_ip, 55443)
        .await
        .expect("Connection failed");

    if let Some(response) = bulb
        .set_bright(level, Effect::Sudden, Duration::new(0, 0))
        .await
        .expect("Error")
    {
        for v in response.iter() {
            println!("{}", v);
        }
    }
}

// Ideally I want to call this function once on app start and use this connection in the set_brightness and bulb_toggle functions
//#[tauri::command]
//async fn bulb_connect() -> Result<(), Box<dyn std::error::Error>> {
//    let my_bulb_ip = "<my_bulb_ip>";
//   let mut bulb = Bulb::connect(my_bulb_ip, 55443).await?;
//    // .expect("Connection Failed");
//    Ok(())
//}

fn main() {
    env_logger::init();
    tauri::Builder::default()
        .invoke_handler(tauri::generate_handler![set_brightness, bulb_toggle, bulb_connect])
        // .manage(Mutex::new(BulbConnection { bulb: "" }))
        //.manage(AppState { bulb: None })
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

As you can see in this, I'm connecting to this bulb every time the tauri command is called from the frontend

You can use manage and access your bulb using a State. You can use OnceLock or Mutex for deferred connection.

# in main()
.manage(OnceLock::<BulbConnection>::new())

#[tauri::command]
async fn bulb_connect(bulb: State<OnceLock<BulbConnection>>) -> Result<(), Box<dyn std::error::Error>> {
    let my_bulb_ip = "<my_bulb_ip>";
    bulb.set(Bulb::connect(my_bulb_ip, 55443).await?)?;
    Ok(())
}

Another way

# in main()
.manage(Mutex::<Option<BulbConnection>>::new())

#[tauri::command]
async fn bulb_connect(bulb: State<Mutex<Option<BulbConnection>>>) -> Result<(), Box<dyn std::error::Error>> {
    let my_bulb_ip = "<my_bulb_ip>";
    bulb.lock()?.insert(Bulb::connect(my_bulb_ip, 55443).await?)?;
    Ok(())
}

Or use Mutex<HashMap> if you have multiple bulbs.

rp1231 commented 10 months ago

Thanks a lot!

rp1231 commented 10 months ago

@ruihe774 After a lot of ChatGpt'ing I finally arrived at this code with only one error:

use std::sync::{Arc, Mutex};
use tauri::{Manager, State, Window};
use yeelight::{Bulb, Effect, Mode, Power};

// Bulb state
#[derive(Default)]
struct BulbState {
    bulb: Option<Mutex<Option<Bulb>>>,
}

// Command to connect
#[tauri::command]
async fn connect_bulb(window: Window) {
    let ip = "<ip>";
    let bulb = Bulb::connect(ip, 55443).await.unwrap();

    // Get the Mutex
    let state_mutex = window.state::<Mutex<BulbState>>();
    let mut state = state_mutex.lock().unwrap();

    // Lock the Mutex and access the inner state
    if let Some(ref mut bulb_mutex) = state.bulb {
        let mut bulb_guard = bulb_mutex.lock().unwrap();
        *bulb_guard = Some(bulb);
    }
}

// Main app
fn main() {
    let mutex = Mutex::new(BulbState::default());
    let window = tauri::Builder::default()
        .manage(mutex)
        .invoke_handler(tauri::generate_handler![connect_bulb])
        .run(tauri::generate_context!())
        .expect("error");

    // Now you can call connect_bulb with the actual window instance
    connect_bulb(window);
}

When I call the connect_bulb function from inside fn main, it requires an argument.

Code_lpcS3gX9F3

Do you have any idea of what to do about that? I'm trying to connect to the bulb on app start so that I can access it from other functions. Thanks