Add support for bragi battery reading

Also clean up the GUI battery code.
This commit is contained in:
Tasos Sahanidis
2021-08-08 19:02:47 +03:00
parent cafb5435e0
commit 1e61686f60
13 changed files with 149 additions and 48 deletions
+2
View File
@@ -145,6 +145,8 @@ typedef union devcmd {
device_io write;
device_io read;
void (*get_battery_info)(usbdevice* kb);
};
} devcmd;
+23
View File
@@ -274,3 +274,26 @@ void clear_input_and_rgb(usbdevice* kb, const int active){
inputupdate(kb);
queued_mutex_unlock(imutex(kb));
}
static const ushort nxp_battery_lut[5] = {
0,
15,
30,
50,
100,
};
void nxp_get_battery_info(usbdevice* kb){
uchar msg[MSG_SIZE] = { CMD_GET, FIELD_BATTERY };
uchar in[MSG_SIZE] = { 0 };
if(!usbrecv(kb, msg, sizeof(msg), in)){
ckb_err("ckb%d: Failed to get battery info. Returning last known state.", INDEX_OF(kb, keyboard));
return;
}
if(in[4] > (sizeof(nxp_battery_lut)/sizeof(*nxp_battery_lut)) - 1){
ckb_err("ckb%d: Invalid battery level received 0x%hhx.", INDEX_OF(kb, keyboard), in[4]);
return;
}
kb->battery_level = nxp_battery_lut[in[4]];
kb->battery_status = in[5];
}
+3 -3
View File
@@ -99,9 +99,6 @@ int cmd_pollrate(usbdevice* kb, usbmode* dummy1, int dummy2, int rate, const cha
int cmd_pollrate_legacy(usbdevice* kb, usbmode* dummy1, int dummy2, int rate, const char* dummy3);
int cmd_pollrate_bragi(usbdevice* kb, usbmode* dummy1, int dummy2, int rate, const char* dummy3);
// Reads the battery of a wireless device
void* read_battery(void* context);
// Sets a device's current mode index. This is only used on the legacy K95; the RGB keyboards have no gettable HW index.
void setmodeindex_legacy(usbdevice* kb, int index);
@@ -111,6 +108,9 @@ void nxp_reset(usbdevice* kb, usbmode* mode, int dummy1, int dummy2, const char*
// Used to clear the input struct and force an rgb update when switching between hw and sw modes
void clear_input_and_rgb(usbdevice* kb, const int active);
void nxp_get_battery_info(usbdevice* kb);
void bragi_get_battery_info(usbdevice* kb);
// Per-key input settings for device setup
// The upper nybble controls input mode. 0x80 generates a normal HID interrupt, 0x40 generates a proprietary interrupt. 0xc0 generates both.
// The exceptions are the proprietary Corsair keys, which only report HID input in BIOS mode and only report Corsair input in non-BIOS mode.
+11
View File
@@ -264,3 +264,14 @@ int cmd_idle_bragi(usbdevice* kb, usbmode* dummy1, int dummy2, int dummy3, const
return setactive_bragi(kb, BRAGI_MODE_HARDWARE);
}
void bragi_get_battery_info(usbdevice* kb){
long int stat = bragi_get_property(kb, BRAGI_BATTERY_STATUS);
long int chg = bragi_get_property(kb, BRAGI_BATTERY_LEVEL);
if(stat < 0 || chg < 0){
ckb_err("ckb%d: Failed to get bragi battery properties", INDEX_OF(kb, keyboard));
return;
}
kb->battery_level = chg / 10;
kb->battery_status = stat;
}
+13
View File
@@ -49,6 +49,9 @@ static int int1_int_none(usbdevice* kb, int dummy){
return 0;
}
static void int0_void_none(usbdevice* kb){
(void)kb;
}
#if 0
static int cmd_io_stub(usbdevice* kb, void* ptr, int len, int is_recv, const char* file, int line){
@@ -107,6 +110,7 @@ const devcmd vtable_keyboard = {
.fill_input_eps = nxp_fill_input_eps,
.write = nxp_usb_write,
.read = nxp_usb_read,
.get_battery_info = int0_void_none,
};
const devcmd vtable_keyboard_wireless = {
@@ -157,6 +161,7 @@ const devcmd vtable_keyboard_wireless = {
.fill_input_eps = nxp_fill_input_eps,
.write = nxp_usb_write,
.read = nxp_usb_read,
.get_battery_info = nxp_get_battery_info,
};
// Legacy keyboard vtable (K70)
@@ -208,6 +213,7 @@ const devcmd vtable_keyboard_legacy = {
.fill_input_eps = legacy_fill_input_eps,
.write = legacy_dev_io,
.read = legacy_dev_io,
.get_battery_info = int0_void_none,
};
// RGB mouse vtable
@@ -259,6 +265,7 @@ const devcmd vtable_mouse = {
.fill_input_eps = nxp_fill_input_eps,
.write = nxp_usb_write,
.read = nxp_usb_read,
.get_battery_info = int0_void_none,
};
const devcmd vtable_mouse_wireless = {
@@ -309,6 +316,7 @@ const devcmd vtable_mouse_wireless = {
.fill_input_eps = nxp_fill_input_eps,
.write = nxp_usb_write,
.read = nxp_usb_read,
.get_battery_info = nxp_get_battery_info,
};
// RGB Mousepad vtable
@@ -360,6 +368,7 @@ const devcmd vtable_mousepad = {
.fill_input_eps = nxp_fill_input_eps,
.write = nxp_usb_write,
.read = nxp_usb_read,
.get_battery_info = int0_void_none,
};
// Legacy mouse vtable
@@ -411,6 +420,7 @@ const devcmd vtable_mouse_legacy = {
.fill_input_eps = legacy_fill_input_eps,
.write = legacy_dev_io,
.read = legacy_dev_io,
.get_battery_info = int0_void_none,
};
// Bragi vtables
@@ -461,6 +471,7 @@ const devcmd vtable_bragi_mouse = {
.fill_input_eps = bragi_fill_input_eps,
.write = bragi_usb_write,
.read = bragi_usb_read,
.get_battery_info = bragi_get_battery_info,
};
const devcmd vtable_bragi_keyboard = {
@@ -510,6 +521,7 @@ const devcmd vtable_bragi_keyboard = {
.fill_input_eps = bragi_fill_input_eps,
.write = bragi_usb_write,
.read = bragi_usb_read,
.get_battery_info = bragi_get_battery_info,
};
const devcmd vtable_bragi_dongle = {
@@ -559,4 +571,5 @@ const devcmd vtable_bragi_dongle = {
.fill_input_eps = bragi_fill_input_eps,
.write = bragi_usb_write,
.read = bragi_usb_read,
.get_battery_info = int0_void_none,
};
+11 -17
View File
@@ -4,6 +4,7 @@
#include "led.h"
#include "notify.h"
#include "profile.h"
#include "command.h"
void nprintf(usbdevice* kb, int nodenumber, usbmode* mode, const char* format, ...){
if(!kb)
@@ -15,10 +16,10 @@ void nprintf(usbdevice* kb, int nodenumber, usbmode* mode, const char* format, .
// If node number was given, print to that node (if open)
if((fifo = kb->outfifo[nodenumber] - 1) != -1){
va_start(va_args, format);
if(mode)
dprintf(fifo, "mode %d ", INDEX_OF(mode, profile->mode) + 1);
vdprintf(fifo, format, va_args);
va_end(va_args);
if(mode)
dprintf(fifo, "mode %d ", INDEX_OF(mode, profile->mode) + 1);
vdprintf(fifo, format, va_args);
va_end(va_args);
}
return;
}
@@ -26,10 +27,10 @@ void nprintf(usbdevice* kb, int nodenumber, usbmode* mode, const char* format, .
for(int i = 0; i < OUTFIFO_MAX; i++){
if((fifo = kb->outfifo[i] - 1) != -1){
va_start(va_args, format);
if(mode)
dprintf(fifo, "mode %d ", INDEX_OF(mode, profile->mode) + 1);
vdprintf(fifo, format, va_args);
va_end(va_args);
if(mode)
dprintf(fifo, "mode %d ", INDEX_OF(mode, profile->mode) + 1);
vdprintf(fifo, format, va_args);
va_end(va_args);
}
}
}
@@ -92,17 +93,10 @@ void cmd_notify(usbdevice* kb, usbmode* mode, int nnumber, int keyindex, const c
static void _cmd_get(usbdevice* kb, usbmode* mode, int nnumber, const char* setting){
usbprofile* profile = kb->profile;
if(!strcmp(setting, ":battery")){
if(!IS_WIRELESS_DEV(kb) || kb->protocol != PROTO_NXP)
return;
uchar msg[MSG_SIZE] = { CMD_GET, FIELD_BATTERY };
uchar in[MSG_SIZE] = {};
queued_mutex_unlock(imutex(kb));
if(!usbrecv(kb, msg, sizeof(msg), in)){
queued_mutex_lock(imutex(kb));
return;
}
kb->vtable->get_battery_info(kb);
queued_mutex_lock(imutex(kb));
nprintf(kb, nnumber, 0, "battery %hhu:%hhu\n", in[4], in[5]);
nprintf(kb, nnumber, 0, "battery %hhu:%hhu\n", kb->battery_level, kb->battery_status & 0xFF);
} else if(!strcmp(setting, ":mode")){
// Get the current mode number
nprintf(kb, nnumber, mode, "switch\n");
+8
View File
@@ -344,6 +344,14 @@ typedef struct usbdevice_ {
struct usbdevice_* children[8];
// Bragi child device id
unsigned char bragi_child_id;
// Battery information
enum {
BATT_STATUS_UNKNOWN,
BATT_STATUS_CHARGING,
BATT_STATUS_DISCHARGING,
BATT_STATUS_CHARGED,
} battery_status;
unsigned char battery_level;
} usbdevice;
#ifdef OS_LINUX
+19 -12
View File
@@ -2,19 +2,26 @@
#include "batterysystemtrayicon.h"
#include "kb.h"
void BatteryStatusTrayIcon::setBattery(uint battery, uint charging){
QString iconName = charging == 2 ? QString("charging") : QString::number(battery);
QIcon icon = QIcon(QString(":/img/ckb-next_battery_%1.png")
.arg(iconName));
QString tooltip = QString("%1:")
.arg(name);
QString subtooltip = QString("%2, %3")
.arg(BATTERY_VALUES[battery])
.arg(BATTERY_CHARGING_VALUES[charging]);
void BatteryStatusTrayIcon::setBattery(uint battery, BatteryStatus charging){
const int chglvl = getBatteryString(battery);
const QString chglvlstr = BATTERY_VALUES[chglvl];
const QString chgstatstr = BATTERY_STATUS_VALUES[charging];
setIcon(icon, QString("ckb-next_battery_%1").arg(iconName));
setToolTip(icon, tooltip, subtooltip);
QString iconName = charging == BatteryStatus::BATT_STATUS_CHARGING ? QString("charging") : QString::number(chglvl);
QIcon icon = QIcon(QString(":/img/ckb-next_battery_%1.png").arg(iconName));
setIcon(icon, QString("ckb-next_battery_%1").arg(iconName));
const QString tooltip = QString("%1:").arg(name);
const QString batlevelstr = batTrayLevel.arg(chglvlstr, QString::number(battery));
const QString subtooltip = QString("%1, %2").arg(batlevelstr, chgstatstr);
setToolTip(icon, tooltip, subtooltip);
batLevel->setText(batlevelstr);
batStatus->setText(batTrayStatus.arg(chgstatstr));
}
const QString BatteryStatusTrayIcon::BATTERY_VALUES[5] = {tr("Not connected"), tr("Critical"), tr("Low"), tr("Medium"), tr("High")};
const QString BatteryStatusTrayIcon::BATTERY_CHARGING_VALUES[3] = {tr("N/A"), tr("Not charging"), tr("Charging")};
const QString BatteryStatusTrayIcon::BATTERY_STATUS_VALUES[4] = {tr("N/A"), tr("Discharging"), tr("Charging"), tr("Fully Charged")};
const QString BatteryStatusTrayIcon::batTrayLevel = tr("Battery: %1 (%2%)");
const QString BatteryStatusTrayIcon::batTrayStatus = tr("Status: %1");
+43 -3
View File
@@ -1,20 +1,60 @@
#ifndef BATTERYSYSTEMTRAYICON_H
#define BATTERYSYSTEMTRAYICON_H
#include "ckbsystemtrayicon.h"
#include <QAction>
#include <QMenu>
enum BatteryStatus {
BATT_STATUS_UNKNOWN,
BATT_STATUS_CHARGING,
BATT_STATUS_DISCHARGING,
BATT_STATUS_CHARGED,
BATT_STATUS_INVALID, // Used for safety checks
};
class BatteryStatusTrayIcon : public CkbSystemTrayIcon {
Q_OBJECT
public:
BatteryStatusTrayIcon( QString _name, QObject* parent = 0 )
: CkbSystemTrayIcon(QIcon(), "", parent), name(_name) {}
: CkbSystemTrayIcon(QIcon(":/img/ckb-next-monochrome.png"), "ckb-next-monochrome", parent), name(_name) {
QMenu* menu = new QMenu();
QAction* nameAct = new QAction(name);
nameAct->setDisabled(true);
menu->addAction(nameAct);
menu->addSeparator();
batLevel = new QAction();
batLevel->setEnabled(false);
batStatus = new QAction();
batStatus->setEnabled(false);
menu->addAction(batLevel);
menu->addAction(batStatus);
setContextMenu(menu);
}
// Battery strings for indexes
const static QString BATTERY_VALUES[5];
const static QString BATTERY_CHARGING_VALUES[3];
const static QString BATTERY_STATUS_VALUES[4];
static_assert(sizeof(BATTERY_STATUS_VALUES)/sizeof(*BATTERY_STATUS_VALUES) == BATT_STATUS_INVALID, "BatteryStatus enum is not the same length as BATTERY_STATUS_VALUES");
const static QString batTrayLevel;
const static QString batTrayStatus;
static int getBatteryString(uint bat) {
if(bat > 50)
return 4;
if(bat > 30)
return 3;
if(bat > 15)
return 2;
return 1;
}
public slots:
void setBattery(uint battery, uint charging);
void setBattery(uint battery, BatteryStatus charging);
private:
QString name;
QAction* batLevel;
QAction* batStatus;
};
#endif
+7 -6
View File
@@ -19,7 +19,7 @@ bool Kb::_dither = false, Kb::_mouseAccel = true;
Kb::Kb(QObject *parent, const QString& path) :
QThread(parent), features(QStringList()), firmware("N/A"), pollrate("N/A"), monochrome(false), hwload(false), adjrate(false),
batteryTimer(0), batteryIcon(0), showBatteryIndicator(false), devpath(path), cmdpath(path + "/cmd"), notifyPath(path + "/notify1"), macroPath(path + "/notify2"),
_currentProfile(0), _currentMode(0), _model(KeyMap::NO_MODEL), battery(0), charging(0),
_currentProfile(0), _currentMode(0), _model(KeyMap::NO_MODEL), batteryLevel(0), batteryStatus(BatteryStatus::BATT_STATUS_UNKNOWN),
lastAutoSave(QDateTime::currentMSecsSinceEpoch()),
_hwProfile(0), prevProfile(0), prevMode(0),
cmd(cmdpath), notifyNumber(1), macroNumber(2), _needsSave(false), _layout(KeyMap::NO_LAYOUT), _maxDpi(0),
@@ -551,11 +551,12 @@ void Kb::readNotify(const QString& line){
return;
// Convert battery values into human readable text
bool ok, ok2;
uint newBattery = bComponents[0].toUInt(&ok), newCharging = bComponents[1].toUInt(&ok2);
if(!ok || !ok2 || newBattery > 4 || newCharging > 4 || (battery == newBattery && charging == newCharging)) return;
battery = newBattery;
charging = newCharging;
emit batteryChanged(newBattery, newCharging);
uint newBatteryLevel = bComponents[0].toUInt(&ok), newBatteryStatus = bComponents[1].toUInt(&ok2);
if(!ok || !ok2 || newBatteryStatus >= BatteryStatus::BATT_STATUS_INVALID || (batteryLevel == newBatteryLevel && batteryStatus == newBatteryStatus))
return;
batteryLevel = newBatteryLevel;
batteryStatus = static_cast<BatteryStatus>(newBatteryStatus);
emit batteryChanged(batteryLevel, batteryStatus);
} else if(components[0] == "i"){
// Indicator event
QString i = components[1];
+3 -2
View File
@@ -138,7 +138,7 @@ signals:
// Profile/mode updates
void profileAdded();
void profileRenamed();
void batteryChanged(uint battery, uint charging);
void batteryChanged(uint batteryLevel, BatteryStatus batteryStatus);
void profileChanged();
void profileAboutToChange();
@@ -196,7 +196,8 @@ private:
KeyMap::Model _model;
uint battery, charging;
uint batteryLevel;
BatteryStatus batteryStatus;
// Indicator light state
bool iState[KbPerf::HW_I_COUNT];
+5 -4
View File
@@ -357,10 +357,11 @@ void KbWidget::devUpdate(){
ui->batteryTrayBox->setChecked(device->showBatteryIndicator);
}
void KbWidget::updateBattery(uint battery, uint charging){
QString label = QString("%1, %2")
.arg(BatteryStatusTrayIcon::BATTERY_VALUES[battery])
.arg(BatteryStatusTrayIcon::BATTERY_CHARGING_VALUES[charging]);
void KbWidget::updateBattery(uint battery, BatteryStatus charging){
QString label = QString("%1 (%2%), %3")
.arg(BatteryStatusTrayIcon::BATTERY_VALUES[BatteryStatusTrayIcon::getBatteryString(battery)],
QString::number(battery),
BatteryStatusTrayIcon::BATTERY_STATUS_VALUES[charging]);
ui->batteryStatusLabel->setText(label);
}
+1 -1
View File
@@ -69,7 +69,7 @@ private slots:
void batteryTrayBox_stateChanged(int state);
void devUpdate();
void updateBattery(uint battery, uint charging);
void updateBattery(uint battery, BatteryStatus charging);
void on_hwSaveButton_clicked();
void on_tabWidget_currentChanged(int index);
void on_fwUpdButton_clicked();