This patch series is intended to replace Derek Lesho's patch because I don't think that it's right to treat "no battery information" the same as "no battery".
The latest Windows SDK calls the fourth member of SYSTEM_POWER_STATUS SystemStatusFlag instead of Reserved1 and also defines a bunch of constants for setting flags in SYSTEM_POWER_STATUS.
I ran down my laptop battery to verify that the thresholds for setting the high, low, and critical flags are correct.
-Alex
Alex Henrie (4): include: Rename Reserved1 to SystemStatusFlag in SYSTEM_POWER_STATUS include: Add constants needed for SYSTEM_POWER_STATUS kernel32/tests: Add tests for GetSystemPowerStatus kernel32: Implement GetSystemPowerStatus on Linux
dlls/kernel32/powermgnt.c | 72 ++++++++++++++++++++++++++++----- dlls/kernel32/tests/Makefile.in | 1 + dlls/kernel32/tests/generated.c | 12 +++--- dlls/kernel32/tests/power.c | 65 +++++++++++++++++++++++++++++ include/winbase.h | 20 ++++++++- 5 files changed, 153 insertions(+), 17 deletions(-) create mode 100644 dlls/kernel32/tests/power.c
Signed-off-by: Alex Henrie alexhenrie24@gmail.com --- dlls/kernel32/powermgnt.c | 2 +- dlls/kernel32/tests/generated.c | 12 ++++++------ include/winbase.h | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/dlls/kernel32/powermgnt.c b/dlls/kernel32/powermgnt.c index 190dc016c3..7efd84d410 100644 --- a/dlls/kernel32/powermgnt.c +++ b/dlls/kernel32/powermgnt.c @@ -52,7 +52,7 @@ BOOL WINAPI GetSystemPowerStatus(LPSYSTEM_POWER_STATUS ps) ps->ACLineStatus = 255; ps->BatteryFlag = 255; ps->BatteryLifePercent = 255; - ps->Reserved1 = 0; + ps->SystemStatusFlag = 0; ps->BatteryLifeTime = ~0u; ps->BatteryFullLifeTime = ~0u; return TRUE; diff --git a/dlls/kernel32/tests/generated.c b/dlls/kernel32/tests/generated.c index 36053b8491..ae1f96613a 100644 --- a/dlls/kernel32/tests/generated.c +++ b/dlls/kernel32/tests/generated.c @@ -1755,9 +1755,9 @@ static void test_pack_SYSTEM_POWER_STATUS(void) TEST_FIELD_SIZE (SYSTEM_POWER_STATUS, BatteryLifePercent, 1) TEST_FIELD_ALIGN (SYSTEM_POWER_STATUS, BatteryLifePercent, 1) TEST_FIELD_OFFSET(SYSTEM_POWER_STATUS, BatteryLifePercent, 2) - TEST_FIELD_SIZE (SYSTEM_POWER_STATUS, Reserved1, 1) - TEST_FIELD_ALIGN (SYSTEM_POWER_STATUS, Reserved1, 1) - TEST_FIELD_OFFSET(SYSTEM_POWER_STATUS, Reserved1, 3) + TEST_FIELD_SIZE (SYSTEM_POWER_STATUS, SystemStatusFlag, 1) + TEST_FIELD_ALIGN (SYSTEM_POWER_STATUS, SystemStatusFlag, 1) + TEST_FIELD_OFFSET(SYSTEM_POWER_STATUS, SystemStatusFlag, 3) TEST_FIELD_SIZE (SYSTEM_POWER_STATUS, BatteryLifeTime, 4) TEST_FIELD_ALIGN (SYSTEM_POWER_STATUS, BatteryLifeTime, 4) TEST_FIELD_OFFSET(SYSTEM_POWER_STATUS, BatteryLifeTime, 4) @@ -3612,9 +3612,9 @@ static void test_pack_SYSTEM_POWER_STATUS(void) TEST_FIELD_SIZE (SYSTEM_POWER_STATUS, BatteryLifePercent, 1) TEST_FIELD_ALIGN (SYSTEM_POWER_STATUS, BatteryLifePercent, 1) TEST_FIELD_OFFSET(SYSTEM_POWER_STATUS, BatteryLifePercent, 2) - TEST_FIELD_SIZE (SYSTEM_POWER_STATUS, Reserved1, 1) - TEST_FIELD_ALIGN (SYSTEM_POWER_STATUS, Reserved1, 1) - TEST_FIELD_OFFSET(SYSTEM_POWER_STATUS, Reserved1, 3) + TEST_FIELD_SIZE (SYSTEM_POWER_STATUS, SystemStatusFlag, 1) + TEST_FIELD_ALIGN (SYSTEM_POWER_STATUS, SystemStatusFlag, 1) + TEST_FIELD_OFFSET(SYSTEM_POWER_STATUS, SystemStatusFlag, 3) TEST_FIELD_SIZE (SYSTEM_POWER_STATUS, BatteryLifeTime, 4) TEST_FIELD_ALIGN (SYSTEM_POWER_STATUS, BatteryLifeTime, 4) TEST_FIELD_OFFSET(SYSTEM_POWER_STATUS, BatteryLifeTime, 4) diff --git a/include/winbase.h b/include/winbase.h index 3a213df4e4..33a2f3e45a 100644 --- a/include/winbase.h +++ b/include/winbase.h @@ -961,7 +961,7 @@ typedef struct _SYSTEM_POWER_STATUS BYTE ACLineStatus; BYTE BatteryFlag; BYTE BatteryLifePercent; - BYTE Reserved1; + BYTE SystemStatusFlag; DWORD BatteryLifeTime; DWORD BatteryFullLifeTime; } SYSTEM_POWER_STATUS, *LPSYSTEM_POWER_STATUS;
Signed-off-by: Alex Henrie alexhenrie24@gmail.com --- include/winbase.h | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+)
diff --git a/include/winbase.h b/include/winbase.h index 33a2f3e45a..31d45aa0cb 100644 --- a/include/winbase.h +++ b/include/winbase.h @@ -966,6 +966,24 @@ typedef struct _SYSTEM_POWER_STATUS DWORD BatteryFullLifeTime; } SYSTEM_POWER_STATUS, *LPSYSTEM_POWER_STATUS;
+#define AC_LINE_OFFLINE 0x00 +#define AC_LINE_ONLINE 0x01 +#define AC_LINE_BACKUP_POWER 0x02 +#define AC_LINE_UNKNOWN 0xFF + +#define BATTERY_FLAG_HIGH 0x01 +#define BATTERY_FLAG_LOW 0x02 +#define BATTERY_FLAG_CRITICAL 0x04 +#define BATTERY_FLAG_CHARGING 0x08 +#define BATTERY_FLAG_NO_BATTERY 0x80 +#define BATTERY_FLAG_UNKNOWN 0xFF + +#define BATTERY_PERCENTAGE_UNKNOWN 0xFF + +#define SYSTEM_STATUS_FLAG_POWER_SAVING_ON 0x01 + +#define BATTERY_LIFE_UNKNOWN 0xFFFFFFFF + typedef struct _SYSTEM_INFO { union {
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=56746
Your paranoid android.
=== debian10 (32 bit WoW report) ===
kernel32: debugger.c:320: Test failed: GetThreadContext failed: 5 debugger.c:320: Test failed: GetThreadContext failed: 5 debugger.c:320: Test failed: GetThreadContext failed: 5 debugger.c:320: Test failed: GetThreadContext failed: 5
=== debian10 (64 bit WoW report) ===
kernel32: debugger.c:320: Test failed: GetThreadContext failed: 5
Signed-off-by: Alex Henrie alexhenrie24@gmail.com --- dlls/kernel32/tests/Makefile.in | 1 + dlls/kernel32/tests/power.c | 65 +++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 dlls/kernel32/tests/power.c
diff --git a/dlls/kernel32/tests/Makefile.in b/dlls/kernel32/tests/Makefile.in index e06141d9f6..e9516603ce 100644 --- a/dlls/kernel32/tests/Makefile.in +++ b/dlls/kernel32/tests/Makefile.in @@ -25,6 +25,7 @@ SOURCES = \ module.c \ path.c \ pipe.c \ + power.c \ process.c \ profile.c \ resource.c \ diff --git a/dlls/kernel32/tests/power.c b/dlls/kernel32/tests/power.c new file mode 100644 index 0000000000..5dc5ef7657 --- /dev/null +++ b/dlls/kernel32/tests/power.c @@ -0,0 +1,65 @@ +/* + * Unit tests for power management functions + * + * Copyright (c) 2019 Alex Henrie + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "wine/test.h" + +void test_GetSystemPowerStatus(void) +{ + SYSTEM_POWER_STATUS ps; + BOOL ret; + BYTE capacity_flags, expected_capacity_flags; + + if (0) /* crashes */ + GetSystemPowerStatus(NULL); + + ret = GetSystemPowerStatus(&ps); + ok(ret == TRUE, "expected TRUE\n"); + + ok(ps.ACLineStatus <= AC_LINE_BACKUP_POWER || ps.ACLineStatus == AC_LINE_UNKNOWN, + "got unexpected ACLineStatus 0x%02x\n", ps.ACLineStatus); + + if (ps.BatteryFlag != BATTERY_FLAG_NO_BATTERY && ps.BatteryFlag != BATTERY_FLAG_UNKNOWN && + ps.BatteryLifePercent != BATTERY_PERCENTAGE_UNKNOWN) + { + expected_capacity_flags = 0; + if (ps.BatteryLifePercent > 66) + expected_capacity_flags |= BATTERY_FLAG_HIGH; + if (ps.BatteryLifePercent < 33) + expected_capacity_flags |= BATTERY_FLAG_LOW; + if (ps.BatteryLifePercent < 5) + expected_capacity_flags |= BATTERY_FLAG_CRITICAL; + capacity_flags = (ps.BatteryFlag & ~BATTERY_FLAG_CHARGING); + ok(capacity_flags == expected_capacity_flags, + "expected %d%%-charged battery to have capacity flags 0x%02x, got 0x%02x", + ps.BatteryLifePercent, expected_capacity_flags, capacity_flags); + } + else + { + skip("no battery charge information\n"); + } + + ok(ps.SystemStatusFlag <= SYSTEM_STATUS_FLAG_POWER_SAVING_ON, + "got unexpected SystemStatusFlag 0x%02x\n", ps.SystemStatusFlag); +} + +START_TEST(power) +{ + test_GetSystemPowerStatus(); +}
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=56747
Your paranoid android.
=== debian10 (32 bit Chinese:China report) ===
kernel32: debugger.c:320: Test failed: GetThreadContext failed: 5
Signed-off-by: Alex Henrie alexhenrie24@gmail.com --- dlls/kernel32/powermgnt.c | 72 +++++++++++++++++++++++++++++++++------ 1 file changed, 62 insertions(+), 10 deletions(-)
diff --git a/dlls/kernel32/powermgnt.c b/dlls/kernel32/powermgnt.c index 7efd84d410..1382f19c50 100644 --- a/dlls/kernel32/powermgnt.c +++ b/dlls/kernel32/powermgnt.c @@ -18,6 +18,8 @@ */
#include <stdarg.h> +#include <stdlib.h> +#include <unistd.h>
#define NONAMELESSUNION #define NONAMELESSSTRUCT @@ -45,19 +47,69 @@ BOOL WINAPI GetDevicePowerState(HANDLE hDevice, BOOL* pfOn) */ BOOL WINAPI GetSystemPowerStatus(LPSYSTEM_POWER_STATUS ps) { - WARN("(%p): stub, harmless.\n", ps); +#ifdef linux + FILE *f; + char s[16]; + BOOL unknown; +#endif + + TRACE("(%p)\n", ps); + + ps->ACLineStatus = AC_LINE_UNKNOWN; + ps->BatteryFlag = BATTERY_FLAG_UNKNOWN; + ps->BatteryLifePercent = BATTERY_PERCENTAGE_UNKNOWN; + ps->SystemStatusFlag = 0; + ps->BatteryLifeTime = BATTERY_LIFE_UNKNOWN; + ps->BatteryFullLifeTime = BATTERY_LIFE_UNKNOWN; + +#ifdef linux + if ((f = fopen("/sys/class/power_supply/AC/online", "r"))) + { + if (fgets(s, sizeof(s), f)) + ps->ACLineStatus = atoi(s); + fclose(f); + }
- if (ps) + if (access("/sys/class/power_supply/BAT0", F_OK) == -1) { - ps->ACLineStatus = 255; - ps->BatteryFlag = 255; - ps->BatteryLifePercent = 255; - ps->SystemStatusFlag = 0; - ps->BatteryLifeTime = ~0u; - ps->BatteryFullLifeTime = ~0u; - return TRUE; + ps->BatteryFlag = BATTERY_FLAG_NO_BATTERY; } - return FALSE; + else + { + ps->BatteryFlag = 0; + unknown = TRUE; + if ((f = fopen("/sys/class/power_supply/BAT0/capacity", "r"))) + { + if (fgets(s, sizeof(s), f)) + { + ps->BatteryLifePercent = atoi(s); + if (ps->BatteryLifePercent > 66) + ps->BatteryFlag |= BATTERY_FLAG_HIGH; + if (ps->BatteryLifePercent < 33) + ps->BatteryFlag |= BATTERY_FLAG_LOW; + if (ps->BatteryLifePercent < 5) + ps->BatteryFlag |= BATTERY_FLAG_CRITICAL; + unknown = FALSE; + } + fclose(f); + } + if ((f = fopen("/sys/class/power_supply/BAT0/status", "r"))) + { + if (fgets(s, sizeof(s), f)) + { + if (strcmp(s, "Charging") == 0) + ps->BatteryFlag |= BATTERY_FLAG_CHARGING; + unknown = FALSE; + } + fclose(f); + } + if (unknown) ps->BatteryFlag = BATTERY_FLAG_UNKNOWN; + } +#else + FIXME("Not implemented on this platform\n"); +#endif + + return TRUE; }
/***********************************************************************
This really ought to be implemented on top of NtPowerInformation(SystemBatteryState).
Chip
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=56748
Your paranoid android.
=== debian10 (32 bit Chinese:China report) ===
kernel32: debugger: Timeout
=== debian10 (64 bit WoW report) ===
kernel32: debugger.c:320: Test failed: GetThreadContext failed: 5