From e9f3a1b3b9e1462d901d3c872f2ada7a9ae383a3 Mon Sep 17 00:00:00 2001 From: Cheerpipe Date: Sat, 13 Feb 2021 00:57:53 -0300 Subject: [PATCH] Logitech G560 initial support Logitech G560 Speaker working with Direct Mode using same bits as the Logitech Ambilight and Audioviz. Other modes are not working (get) as expected. Note: G560 Direct mode is slow because Logitech hardware implementation but it is working. Changing volume at the same time colors are changing can reduce FPS because G560 uses the same channel for led and audio modes commands. Commits squashed and amended for code style by Adam Honse --- .../LogitechControllerDetect.cpp | 23 +++ .../LogitechG560Controller.cpp | 147 +++++++++++++++++ .../LogitechG560Controller.h | 62 ++++++++ .../RGBController_LogitechG560.cpp | 148 ++++++++++++++++++ .../RGBController_LogitechG560.h | 34 ++++ OpenRGB.pro | 6 +- RGBController/RGBController.cpp | 2 + RGBController/RGBController.h | 1 + qt/OpenRGBDialog2.cpp | 3 + qt/resources.qrc | 2 + qt/speaker.png | Bin 0 -> 1685 bytes qt/speaker_dark.png | Bin 0 -> 159 bytes 12 files changed, 427 insertions(+), 1 deletion(-) create mode 100644 Controllers/LogitechController/LogitechG560Controller.cpp create mode 100644 Controllers/LogitechController/LogitechG560Controller.h create mode 100644 Controllers/LogitechController/RGBController_LogitechG560.cpp create mode 100644 Controllers/LogitechController/RGBController_LogitechG560.h create mode 100644 qt/speaker.png create mode 100644 qt/speaker_dark.png diff --git a/Controllers/LogitechController/LogitechControllerDetect.cpp b/Controllers/LogitechController/LogitechControllerDetect.cpp index c0c50706c..492177489 100644 --- a/Controllers/LogitechController/LogitechControllerDetect.cpp +++ b/Controllers/LogitechController/LogitechControllerDetect.cpp @@ -7,6 +7,7 @@ #include "LogitechG213Controller.h" #include "LogitechGProWirelessController.h" #include "LogitechGPowerPlayController.h" +#include "LogitechG560Controller.h" #include "RGBController.h" #include "RGBController_LogitechG203.h" #include "RGBController_LogitechG203L.h" @@ -16,6 +17,7 @@ #include "RGBController_LogitechG213.h" #include "RGBController_LogitechGProWireless.h" #include "RGBController_LogitechGPowerPlay.h" +#include "RGBController_LogitechG560.h" #include #include @@ -50,6 +52,10 @@ | Mousemat product IDs | \*-----------------------------------------------------*/ #define LOGITECH_G_LIGHTSPEED_POWERPLAY_PID 0xC53A +/*-----------------------------------------------------*\ +| Speaker product IDs | +\*-----------------------------------------------------*/ +#define LOGITECH_G560_PID 0x0A78 void DetectLogitechKeyboardG810(hid_device_info* info, const std::string& name) { @@ -199,6 +205,19 @@ void DetectLogitechMouseGLS(hid_device_info* info, const std::string& name) } } +void DetectLogitechG560(hid_device_info* info, const std::string& name) +{ + hid_device* dev = hid_open_path(info->path); + if(dev) + { + //Add G560 Speaker + LogitechG560Controller* speaker_controller = new LogitechG560Controller(dev, info->path); + RGBController_LogitechG560* speaker_rgb_controller = new RGBController_LogitechG560(speaker_controller); + speaker_rgb_controller->name = name; + ResourceManager::get()->RegisterRGBController(speaker_rgb_controller); + } +} + /*-------------------------------------------------------------------------------------------------------------------------------------------------*\ | Keyboards | \*-------------------------------------------------------------------------------------------------------------------------------------------------*/ @@ -226,3 +245,7 @@ REGISTER_HID_DETECTOR_IPU("Logitech G Pro Wireless Gaming Mouse (Wired)", Detec | Mousemats | \*-------------------------------------------------------------------------------------------------------------------------------------------------*/ REGISTER_HID_DETECTOR_IPU("Logitech G Powerplay Mousepad with Lightspeed", DetectLogitechMouseGLS, LOGITECH_VID, LOGITECH_G_LIGHTSPEED_POWERPLAY_PID, 2, 0xFF00, 2); +/*-------------------------------------------------------------------------------------------------------------------------------------------------*\ +| Speakers | +\*-------------------------------------------------------------------------------------------------------------------------------------------------*/ +REGISTER_HID_DETECTOR_IPU("Logitech G560 Lightsync Speaker", DetectLogitechG560, LOGITECH_VID, LOGITECH_G560_PID, 2, 0xFF43, 514); diff --git a/Controllers/LogitechController/LogitechG560Controller.cpp b/Controllers/LogitechController/LogitechG560Controller.cpp new file mode 100644 index 000000000..2eb1e6c78 --- /dev/null +++ b/Controllers/LogitechController/LogitechG560Controller.cpp @@ -0,0 +1,147 @@ +/*-----------------------------------------*\ +| LogitechG560Controller.cpp | +| | +| Driver for Logitech G560 RGB Speaker | +| Charging System | +| | +| Cheerpipe 10/28/2020 | +| Based on | +| TheRogueZeta 8/31/2020 | +\*-----------------------------------------*/ + +#include "LogitechG560Controller.h" +#include +#include +#include + +using namespace std::chrono_literals; + +LogitechG560Controller::LogitechG560Controller(hid_device* dev_handle, const char* path) +{ + dev = dev_handle; + location = path; +} + +LogitechG560Controller::~LogitechG560Controller() +{ + hid_close(dev); +} + +std::string LogitechG560Controller::GetDeviceLocation() +{ + return("HID: " + location); +} + +void LogitechG560Controller::SetDirectMode(uint8_t zone) +{ + unsigned char usb_buf[20]; + + usb_buf[0x00] = 0x11; + usb_buf[0x01] = 0xFF; + usb_buf[0x02] = 0x04; + usb_buf[0x03] = 0xCA; + usb_buf[0x04] = zone; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + fail_retry_write(dev, usb_buf, 20); +} + +void LogitechG560Controller::SetOffMode(uint8_t zone) +{ + unsigned char usb_buf[20]; + + usb_buf[0x00] = 0x11; + usb_buf[0x01] = 0xFF; + usb_buf[0x02] = 0x04; + usb_buf[0x03] = 0x3F; + usb_buf[0x04] = zone; + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + fail_retry_write(dev, usb_buf, 20); +} + +void LogitechG560Controller::SendSpeakerMode + ( + unsigned char zone, //0x04 + unsigned char mode, //0x05 + unsigned char speed, //0x0B // not working so far + unsigned char red, //0x06 + unsigned char green, //0x07 + unsigned char blue //0x08 + ) +{ + unsigned char usb_buf[20]; + + /*-----------------------------------------------------*\ + | Zero out buffer | + \*-----------------------------------------------------*/ + memset(usb_buf, 0x00, sizeof(usb_buf)); + + /*-----------------------------------------------------*\ + | Set up Lighting Control | + \*-----------------------------------------------------*/ + usb_buf[0x00] = 0x11; + usb_buf[0x01] = 0xFF; + usb_buf[0x02] = 0x04; + + /*-----------------------------------------------------*\ + | This packet sets speaker into direct mode. This mode | + | is used by Lightsync Ambilight and Music Visualizer | + | realtime effect. | + \*-----------------------------------------------------*/ + usb_buf[0x03] = 0x3A; + + /*-----------------------------------------------------*\ + | Set up mode and speed | + \*-----------------------------------------------------*/ + usb_buf[0x04] = zone; + usb_buf[0x05] = mode; + + /*-----------------------------------------------------*\ + | And set up the colors | + \*-----------------------------------------------------*/ + usb_buf[0x06] = red; + usb_buf[0x07] = green; + usb_buf[0x08] = blue; + + if(mode == LOGITECH_G560_MODE_DIRECT) //G560 only has Direct Mode. + { + usb_buf[0x09] = 0x02; + } + + /*-----------------------------------------------------*\ + | Send packet | + \*-----------------------------------------------------*/ + fail_retry_write(dev, usb_buf, 20); +} + +void LogitechG560Controller::fail_retry_write(hid_device *device, const unsigned char *data, size_t length) +{ + unsigned char usb_buf_out[33]; + uint8_t write_max_retry=3; + do + { + std::this_thread::sleep_for(1ms); + int ret = hid_write(device, data, length); + + /*-------------------------------------------------------------------------------------*\ + | HID write fails if a change led color and set volume command are sent at | + | the same time because RGB controller and volume control shares the same interface. | + \*-------------------------------------------------------------------------------------*/ + if (ret > 0) + { + std::this_thread::sleep_for(1ms); + hid_read_timeout(dev, usb_buf_out, 33, 100); + break; + } + else + { + write_max_retry--; + } + + }while (write_max_retry > 0); +} diff --git a/Controllers/LogitechController/LogitechG560Controller.h b/Controllers/LogitechController/LogitechG560Controller.h new file mode 100644 index 000000000..2b432d1fa --- /dev/null +++ b/Controllers/LogitechController/LogitechG560Controller.h @@ -0,0 +1,62 @@ +/*-----------------------------------------*\ +| LogitechG560Controller.h | +| | +| Definitions and types for Logitech G560 | +| RGB Speaker | +| | +| Cheerpipe 10/28/2020 | +| Based on | +| TheRogueZeta 8/31/2020 | +\*-----------------------------------------*/ + +#include "RGBController.h" + +#include +#include + +#pragma once + +enum +{ + LOGITECH_G560_MODE_OFF = 0x00, + LOGITECH_G560_MODE_DIRECT = 0x01, + LOGITECH_G560_MODE_CYCLE = 0x02, + LOGITECH_G560_MODE_BREATHING = 0x03, +}; + +/*---------------------------------------------------------------------------------------------*\ +| Speed is 1000 for fast and 20000 for slow. | +| Values are mutipled by 100 later to give lots of GUI steps. | +\*---------------------------------------------------------------------------------------------*/ +enum +{ + LOGITECH_G560_SPEED_SLOWEST = 0xC8, /* Slowest speed */ + LOGITECH_G560_SPEED_NORMAL = 0x32, /* Normal speed */ + LOGITECH_G560_SPEED_FASTEST = 0x0A, /* Fastest speed */ +}; + +class LogitechG560Controller +{ +public: + LogitechG560Controller(hid_device* dev_handle, const char* path); + ~LogitechG560Controller(); + std::string GetDeviceLocation(); + void SetDirectMode(uint8_t zone); + void SetOffMode(uint8_t zone); + void SendSpeakerMode + ( + unsigned char zone, //0x04 + unsigned char mode, //0x05 + unsigned char speed, //0x0B + unsigned char red, //0x06 + unsigned char green, //0x07 + unsigned char blue //0x08 + ); + +private: + hid_device* dev; + std::string location; + void fail_retry_write(hid_device *device, const unsigned char *data, size_t length); +}; + + diff --git a/Controllers/LogitechController/RGBController_LogitechG560.cpp b/Controllers/LogitechController/RGBController_LogitechG560.cpp new file mode 100644 index 000000000..1b3202184 --- /dev/null +++ b/Controllers/LogitechController/RGBController_LogitechG560.cpp @@ -0,0 +1,148 @@ +/*-----------------------------------------*\ +| RGBController_LogitechG560.cpp | +| | +| Generic RGB Interface for | +| Logitech G560 RGB Speaker | +| | +| Cheerpipe 10/28/2020 | +| Based on | +| TheRogueZeta 8/31/2020 | +\*-----------------------------------------*/ + +#include "RGBController_LogitechG560.h" + +RGBController_LogitechG560::RGBController_LogitechG560(LogitechG560Controller* logitech_ptr) +{ + logitech = logitech_ptr; + + name = "Logitech G560 Lightsync Speaker"; + vendor = "Logitech"; + type = DEVICE_TYPE_SPEAKER; + description = "Logitech G560 Lightsync Speaker"; + location = logitech->GetDeviceLocation(); + + mode Off; + Off.name = "Off"; + Off.value = LOGITECH_G560_MODE_OFF; + Off.flags = 0; + Off.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Off); + + mode Direct; + Direct.name = "Direct"; + Direct.value = LOGITECH_G560_MODE_DIRECT; + Direct.flags = MODE_FLAG_HAS_PER_LED_COLOR; + Direct.color_mode = MODE_COLORS_PER_LED; + modes.push_back(Direct); + SetupZones(); +} + +void RGBController_LogitechG560::SetupZones() +{ + zone G560_left_front; + G560_left_front.name = "Left Front"; + G560_left_front.type = ZONE_TYPE_SINGLE; + G560_left_front.leds_min = 1; + G560_left_front.leds_max = 1; + G560_left_front.leds_count = 1; + G560_left_front.matrix_map = NULL; + zones.push_back(G560_left_front); + + led G560_left_front_led; + G560_left_front_led.name = "Left Front"; + G560_left_front_led.value = 0x00; + leds.push_back(G560_left_front_led); + + zone G560_right_front; + G560_right_front.name = "Right Front"; + G560_right_front.type = ZONE_TYPE_SINGLE; + G560_right_front.leds_min = 1; + G560_right_front.leds_max = 1; + G560_right_front.leds_count = 1; + G560_right_front.matrix_map = NULL; + zones.push_back(G560_right_front); + + led G560_right_front_led; + G560_right_front_led.name = "Right Front"; + G560_right_front_led.value = 0x01; + leds.push_back(G560_right_front_led); + + + zone G560_left_rear; + G560_left_rear.name = "Left Rear"; + G560_left_rear.type = ZONE_TYPE_SINGLE; + G560_left_rear.leds_min = 1; + G560_left_rear.leds_max = 1; + G560_left_rear.leds_count = 1; + G560_left_rear.matrix_map = NULL; + zones.push_back(G560_left_rear); + + led G560_left_read_led; + G560_left_read_led.name = "Left Rear"; + G560_left_read_led.value = 0x02; + leds.push_back(G560_left_read_led); + + + zone G560_right_rear; + G560_right_rear.name = "Right Rear"; + G560_right_rear.type = ZONE_TYPE_SINGLE; + G560_right_rear.leds_min = 1; + G560_right_rear.leds_max = 1; + G560_right_rear.leds_count = 1; + G560_right_rear.matrix_map = NULL; + zones.push_back(G560_right_rear); + + led G560_right_rear_led; + G560_right_rear_led.name = "Right Rear"; + G560_right_rear_led.value = 0x03; + leds.push_back(G560_right_rear_led); + + SetupColors(); +} + +void RGBController_LogitechG560::ResizeZone(int /*zone*/, int /*new_size*/) +{ + /*---------------------------------------------------------*\ + | This device does not support resizing zones | + \*---------------------------------------------------------*/ +} + +void RGBController_LogitechG560::DeviceUpdateLEDs() +{ + for(std::size_t led_idx = 0; led_idx < leds.size(); led_idx++) + { + unsigned char red = RGBGetRValue(colors[led_idx]); + unsigned char grn = RGBGetGValue(colors[led_idx]); + unsigned char blu = RGBGetBValue(colors[led_idx]); + + if (modes[active_mode].value==LOGITECH_G560_MODE_DIRECT) + { + logitech->SetDirectMode(leds[led_idx].value); //Required to "reset" RGB controller and start receiving color in direct mode + } + else if (modes[active_mode].value==LOGITECH_G560_MODE_OFF) + { + logitech->SetOffMode(leds[led_idx].value); + } + logitech->SendSpeakerMode((unsigned char)leds[led_idx].value, modes[active_mode].value, modes[active_mode].speed,red, grn, blu); + } +} + +void RGBController_LogitechG560::UpdateZoneLEDs(int zone) +{ + DeviceUpdateLEDs(); +} + +void RGBController_LogitechG560::UpdateSingleLED(int led) +{ + DeviceUpdateLEDs(); +} + +void RGBController_LogitechG560::SetCustomMode() +{ + active_mode = 1; +} + +void RGBController_LogitechG560::DeviceUpdateMode() +{ + DeviceUpdateLEDs(); +} diff --git a/Controllers/LogitechController/RGBController_LogitechG560.h b/Controllers/LogitechController/RGBController_LogitechG560.h new file mode 100644 index 000000000..caebf2923 --- /dev/null +++ b/Controllers/LogitechController/RGBController_LogitechG560.h @@ -0,0 +1,34 @@ +/*-----------------------------------------*\ +| RGBController_LogitechG560.h | +| | +| Generic RGB Interface for | +| G560 RGB Speaker | +| | +| Cheerpipe 10/28/2020 | +| Based on | +| TheRogueZeta 8/31/2020 | +\*-----------------------------------------*/ + +#pragma once +#include "RGBController.h" +#include "LogitechG560Controller.h" + +class RGBController_LogitechG560 : public RGBController +{ +public: + RGBController_LogitechG560(LogitechG560Controller* logitech_ptr); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void SetCustomMode(); + void DeviceUpdateMode(); + +private: + LogitechG560Controller* logitech; +}; diff --git a/OpenRGB.pro b/OpenRGB.pro index 67042266a..e42413340 100644 --- a/OpenRGB.pro +++ b/OpenRGB.pro @@ -229,7 +229,7 @@ HEADERS += Controllers/HyperXMouseController/HyperXPulsefireDartController.h \ Controllers/HyperXMouseController/RGBController_HyperXPulsefireFPSPro.h \ Controllers/HyperXMouseController/RGBController_HyperXPulsefireSurge.h \ - Controllers/HyperXMouseController/RGBController_HyperXPulsefireDart.h \ + Controllers/HyperXMouseController/RGBController_HyperXPulsefireDart.h \ Controllers/HyperXMousematController/HyperXMousematController.h \ Controllers/HyperXMousematController/RGBController_HyperXMousemat.h \ Controllers/LEDStripController/LEDStripController.h \ @@ -238,6 +238,7 @@ HEADERS += Controllers/LogitechController/LogitechG203LController.h \ Controllers/LogitechController/LogitechG403Controller.h \ Controllers/LogitechController/LogitechG502PSController.h \ + Controllers/LogitechController/LogitechG560Controller.h \ Controllers/LogitechController/LogitechG810Controller.h \ Controllers/LogitechController/LogitechG213Controller.h \ Controllers/LogitechController/LogitechGPowerPlayController.h \ @@ -246,6 +247,7 @@ HEADERS += Controllers/LogitechController/RGBController_LogitechG203L.h \ Controllers/LogitechController/RGBController_LogitechG403.h \ Controllers/LogitechController/RGBController_LogitechG502PS.h \ + Controllers/LogitechController/RGBController_LogitechG560.h \ Controllers/LogitechController/RGBController_LogitechG810.h \ Controllers/LogitechController/RGBController_LogitechG213.h \ Controllers/LogitechController/RGBController_LogitechGPowerPlay.h \ @@ -473,6 +475,7 @@ SOURCES += Controllers/LogitechController/LogitechG203LController.cpp \ Controllers/LogitechController/LogitechG403Controller.cpp \ Controllers/LogitechController/LogitechG502PSController.cpp \ + Controllers/LogitechController/LogitechG560Controller.cpp \ Controllers/LogitechController/LogitechG810Controller.cpp \ Controllers/LogitechController/LogitechG213Controller.cpp \ Controllers/LogitechController/LogitechGPowerPlayController.cpp \ @@ -481,6 +484,7 @@ SOURCES += Controllers/LogitechController/RGBController_LogitechG203L.cpp \ Controllers/LogitechController/RGBController_LogitechG403.cpp \ Controllers/LogitechController/RGBController_LogitechG502PS.cpp \ + Controllers/LogitechController/RGBController_LogitechG560.cpp \ Controllers/LogitechController/RGBController_LogitechG810.cpp \ Controllers/LogitechController/RGBController_LogitechG213.cpp \ Controllers/LogitechController/RGBController_LogitechGPowerPlay.cpp \ diff --git a/RGBController/RGBController.cpp b/RGBController/RGBController.cpp index 645f07046..9007efc72 100644 --- a/RGBController/RGBController.cpp +++ b/RGBController/RGBController.cpp @@ -1457,6 +1457,8 @@ std::string device_type_to_str(device_type type) return "Gamepad"; case DEVICE_TYPE_LIGHT: return "Light"; + case DEVICE_TYPE_SPEAKER: + return "Speaker"; default: return "Unknown"; } diff --git a/RGBController/RGBController.h b/RGBController/RGBController.h index c8753572b..cd96396e9 100644 --- a/RGBController/RGBController.h +++ b/RGBController/RGBController.h @@ -117,6 +117,7 @@ enum DEVICE_TYPE_HEADSET_STAND, DEVICE_TYPE_GAMEPAD, DEVICE_TYPE_LIGHT, + DEVICE_TYPE_SPEAKER, DEVICE_TYPE_UNKNOWN }; diff --git a/qt/OpenRGBDialog2.cpp b/qt/OpenRGBDialog2.cpp index 856813f50..c5632aee4 100644 --- a/qt/OpenRGBDialog2.cpp +++ b/qt/OpenRGBDialog2.cpp @@ -61,6 +61,9 @@ static QString GetIconString(device_type type, bool dark) case DEVICE_TYPE_LIGHT: filename = "light"; break; + case DEVICE_TYPE_SPEAKER: + filename = "speaker"; + break; default: filename = "unknown"; break; diff --git a/qt/resources.qrc b/qt/resources.qrc index d9702cbff..f832d2d61 100644 --- a/qt/resources.qrc +++ b/qt/resources.qrc @@ -34,5 +34,7 @@ arrow-up.png plugin.png plugin_dark.png + speaker.png + speaker_dark.png diff --git a/qt/speaker.png b/qt/speaker.png new file mode 100644 index 0000000000000000000000000000000000000000..66e124522b0b60f8fc697e87518811e2c791be81 GIT binary patch literal 1685 zcmV;G25R| zaB^>EX>4U6ba`-PAZ2)IW&i+q+O1Yuvg05O{MRYw2p}Pr+^%)hP=GWDNMso9EKj2he7F{8IwAK9EOBfxgGD6c%5XwOK1Gb_;~Sb&hIV& zjH<9FOgxJfvpjkAB-B%bRC@p?WgG?5`but<~7tyk?i3C&1-LQjb)Y859fPT+#5j1AdBmSnh;7 znrtSpR*`BUQ}B{31KDPstv1_c=VhGigb*Fi956CZEQ>5k;KUO^x8nM38wZjkRU){V zz??wLTP$|*o4P+Zi||}6G}*!!IDWn9DEQa=#ZYU#%&j$X!wPY6uP=nrnd?tzfe_mU zrn=y3X&ub*>ck2d)CJ~<1hXltiK6ydTe9#hMhU9Utoy}=It&0J_*MjC1q>K(MXk3c zFb8XaV+21Lk+V21V8ALVS47E9#Dkp3-8a4ocF|?!RgCrmB2>HtP?IEsEa%jD?6-&# z%A}%BRlNp{nlyXy&a3x6_}-^-kl=y_A3}&Bg&ZZisL{s|V@xq8PJ%Y3lza*)rj&Aq z)9f;AW$0%J3KuD^Xz?YKSW?Lo%4c$uPJRkgoYIslSE*QLsXOzU z<~XN0H`H2_=9)I&LW?c6ys?{}$ zodFaycg{?-&T(h1b7m3?i6|pV?c`M383TiEwye!=+&!7Q<&A*ur@ZkMbJVH(3FfF% zw`1m};`F4Vt@ zCMT!X;cN@FA)ECD1v#Y05l8S*q$MxLjLglAsTm`gZEcf1MXfzo%VCwXE)K9+LaUa> z9PU=Jd0D<)_45HVx&rjOplx8`K^tmkXs#f+K!9sq4fQPYA@pa0Z*tZ#HFQ?HGkRhC z3Ds7Rw}tjy$gaTNWBBE$yON;00{gVS=%n_KBmG1)y~^p9+s{Y?*gr#BZzC^V;nV_b zIEw0=E*b@$)K12}=qo#I+WAzsLv)XLc|%<1i_QeCoAA>;NH?3%>W7EG#a`RdRKh*N z2k7XC-J_YCJMeCxqZ{l#hK@e#UzgBrJ^KQ~i%Ne39eq}ISHP{g)1my#_Zz`0X!O5L z|FKH?-Ml@*31iI=D!90004nX+uL$Nkc;* zaB^>EX>4Tx0C=2zkv&MmKpe$iQ$;Bi2Ro=JWT*~eK~%(1s#pXIrLEAagUO{|(4-+r zad8w}3l4rPRvlcNb#-tR1i=pwCr2km7b)?7NufoI2gm(*ckglc4iM^PrkWiSfT~$W zG8Ppx*;TRY6#)#xj{%HH%+%wl#WXy}*FAiEy^HWH?{j~S9wlcoz$X&VG2O6;H;898 zEuHf|aflTqh4`F!+@K2*KXP4m`HgeIVS#6cjC69II7BQK+E{L5Ry0)NY2vV=YLqWz zTvj-5aaPM!*1RWwVKAqyq_|FV2r(=njs!%=sG@{2EJSG4NHLM5{iuh3(D5h8C6lWJ zMvetkph9x|;D7MDTQfg3=_UoEKlA>>?i59 zmKHt&dbfd#>y{?(0hc>K|C25mk|X(P3i&+nen#Jv1^RA*z?$1zb04Pjlyo{jei*a`~GpASBnQDrkd_eIZ0s0-|+a>x~nlfLM!~Gp! f=8O7bqFufN8bUFzKrHi>00000NkvXXu0mjfj`0nC literal 0 HcmV?d00001 diff --git a/qt/speaker_dark.png b/qt/speaker_dark.png new file mode 100644 index 0000000000000000000000000000000000000000..f1a7b8a93d28665a9559064a06e11405898c376a GIT binary patch literal 159 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`X`U{QAr_~%2A}3TV8Frj`2YV+ zyU#3Pzw@R$_%fGP7?XnwicJ$E+#k(K9|Ju$8lftencf?pMF~+K;RHFW+Tmc<|23K0VxrTWiJgB%qB9p00i_ I>zopr0EUG-(EtDd literal 0 HcmV?d00001