Signed-off-by: Alex Henrie alexhenrie24@gmail.com --- include/winnt.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/include/winnt.h b/include/winnt.h index 9c4174f310..3db86ae155 100644 --- a/include/winnt.h +++ b/include/winnt.h @@ -5607,7 +5607,8 @@ typedef struct { BOOLEAN BatteryPresent; BOOLEAN Charging; BOOLEAN Discharging; - BOOLEAN Spare1[4]; + BOOLEAN Spare1[3]; + BYTE Tag; ULONG MaxCapacity; ULONG RemainingCapacity; ULONG Rate;
Signed-off-by: Alex Henrie alexhenrie24@gmail.com --- include/winnt.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/include/winnt.h b/include/winnt.h index 3db86ae155..7bfbcb7701 100644 --- a/include/winnt.h +++ b/include/winnt.h @@ -5609,12 +5609,12 @@ typedef struct { BOOLEAN Discharging; BOOLEAN Spare1[3]; BYTE Tag; - ULONG MaxCapacity; - ULONG RemainingCapacity; - ULONG Rate; - ULONG EstimatedTime; - ULONG DefaultAlert1; - ULONG DefaultAlert2; + ULONG MaxCapacity; /* milliwatt-hours */ + ULONG RemainingCapacity; /* milliwatt-hours */ + ULONG Rate; /* milliwatts */ + ULONG EstimatedTime; /* seconds */ + ULONG DefaultAlert1; /* milliwatt-hours */ + ULONG DefaultAlert2; /* milliwatt-hours */ } SYSTEM_BATTERY_STATE, *PSYSTEM_BATTERY_STATE;
Signed-off-by: Alex Henrie alexhenrie24@gmail.com --- dlls/ntdll/tests/info.c | 46 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+)
diff --git a/dlls/ntdll/tests/info.c b/dlls/ntdll/tests/info.c index 25c379929b..93920714f2 100644 --- a/dlls/ntdll/tests/info.c +++ b/dlls/ntdll/tests/info.c @@ -888,6 +888,48 @@ static void test_query_firmware(void) HeapFree(GetProcessHeap(), 0, sfti); }
+static void test_query_battery(void) +{ + SYSTEM_BATTERY_STATE bs; + NTSTATUS status; + DWORD time_left; + + memset(&bs, 0x23, sizeof(bs)); + status = NtPowerInformation(SystemBatteryState, NULL, 0, &bs, sizeof(bs)); + if (status == STATUS_NOT_IMPLEMENTED) + { + skip("SystemBatteryState not implemented\n"); + return; + } + ok(status == STATUS_SUCCESS, "expected success\n"); + + trace("Battery state:\n"); + trace("AcOnLine : %u\n", bs.AcOnLine); + trace("BatteryPresent : %u\n", bs.BatteryPresent); + trace("Charging : %u\n", bs.Charging); + trace("Discharging : %u\n", bs.Discharging); + trace("Tag : %u\n", bs.Tag); + trace("MaxCapacity : %u\n", bs.MaxCapacity); + trace("RemainingCapacity : %u\n", bs.RemainingCapacity); + trace("Rate : %d\n", (LONG)bs.Rate); + trace("EstimatedTime : %u\n", bs.EstimatedTime); + trace("DefaultAlert1 : %u\n", bs.DefaultAlert1); + trace("DefaultAlert2 : %u\n", bs.DefaultAlert2); + + ok(bs.MaxCapacity >= bs.RemainingCapacity, + "expected MaxCapacity %u to be greater than or equal to RemainingCapacity %u\n", + bs.MaxCapacity, bs.RemainingCapacity); + + if (!bs.BatteryPresent) + time_left = 0; + else if (!bs.Charging && (LONG)bs.Rate < 0) + time_left = 3600 * bs.RemainingCapacity / -(LONG)bs.Rate; + else + time_left = ~0u; + ok(bs.EstimatedTime == time_left, + "expected %u minutes remaining got %u minutes\n", time_left, bs.EstimatedTime); +} + static void test_query_processor_power_info(void) { NTSTATUS status; @@ -2374,6 +2416,10 @@ START_TEST(info)
/* NtPowerInformation */
+ /* 0x5 SystemBatteryState */ + trace("Starting test_query_battery()\n"); + test_query_battery(); + /* 0xb ProcessorInformation */ trace("Starting test_query_processor_power_info()\n"); test_query_processor_power_info();
Signed-off-by: Alex Henrie alexhenrie24@gmail.com --- MSDN says that "Charging" and "Discharging" are set based on all batteries in the computer, but then it makes it sound like the remaining fields are set based on a single battery. Since I do not have a computer with multiple batteries to test, I am proposing that we implement this function according to MSDN's description and change it later if that turns out to be incorrect. --- dlls/ntdll/nt.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+)
diff --git a/dlls/ntdll/nt.c b/dlls/ntdll/nt.c index efc4c8adcf..fa2330ba66 100644 --- a/dlls/ntdll/nt.c +++ b/dlls/ntdll/nt.c @@ -3141,6 +3141,73 @@ static ULONG mhz_from_cpuinfo(void) } #endif
+#ifdef linux + +static const char * get_sys_str(const char *path) +{ + static char s[16]; + FILE *f = fopen(path, "r"); + const char *ret = NULL; + if (f) + { + if (fgets(s, sizeof(s), f)) + ret = s; + fclose(f); + } + return ret; +} + +static int get_sys_int(const char *path, int def) +{ + const char *s = get_sys_str(path); + return s ? atoi(s) : def; +} + +static NTSTATUS fill_battery_state(SYSTEM_BATTERY_STATE *bs) +{ + char path[64]; + const char *s; + unsigned int i = 0; + LONG64 voltage; /* microvolts */ + + bs->AcOnLine = get_sys_int("/sys/class/power_supply/AC/online", 1); + + for (;;) + { + sprintf(path, "/sys/class/power_supply/BAT%u/status", i); + s = get_sys_str(path); + if (!s) break; + bs->Charging |= (strcmp(s, "Charging\n") == 0); + bs->Discharging |= (strcmp(s, "Discharging\n") == 0); + bs->BatteryPresent = TRUE; + i++; + } + + if (bs->BatteryPresent) + { + voltage = get_sys_int("/sys/class/power_supply/BAT0/voltage_now", 0); + bs->MaxCapacity = get_sys_int("/sys/class/power_supply/BAT0/charge_full", 0) * voltage / 1e9; + bs->RemainingCapacity = get_sys_int("/sys/class/power_supply/BAT0/charge_now", 0) * voltage / 1e9; + bs->Rate = -get_sys_int("/sys/class/power_supply/BAT0/current_now", 0) * voltage / 1e9; + if (!bs->Charging && (LONG)bs->Rate < 0) + bs->EstimatedTime = 3600 * bs->RemainingCapacity / -(LONG)bs->Rate; + else + bs->EstimatedTime = ~0u; + } + + return STATUS_SUCCESS; +} + +#else + +static NTSTATUS fill_battery_state(SYSTEM_BATTERY_STATE *) +{ + FIXME("SystemBatteryState not implemented on this platform\n"); + return STATUS_NOT_IMPLEMENTED; +} + +#endif + /****************************************************************************** * NtPowerInformation [NTDLL.@] * @@ -3194,6 +3261,12 @@ NTSTATUS WINAPI NtPowerInformation( PowerCaps->DefaultLowLatencyWake = PowerSystemUnspecified; return STATUS_SUCCESS; } + case SystemBatteryState: { + if (nOutputBufferSize < sizeof(SYSTEM_BATTERY_STATE)) + return STATUS_BUFFER_TOO_SMALL; + memset(lpOutputBuffer, 0, sizeof(SYSTEM_BATTERY_STATE)); + return fill_battery_state(lpOutputBuffer); + } case SystemExecutionState: { PULONG ExecutionState = lpOutputBuffer; WARN("semi-stub: SystemExecutionState\n"); /* Needed for .NET Framework, but using a FIXME is really noisy. */
Signed-off-by: Alex Henrie alexhenrie24@gmail.com --- dlls/kernel32/tests/Makefile.in | 1 + dlls/kernel32/tests/power.c | 79 +++++++++++++++++++++++++++++++++ 2 files changed, 80 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..ae5ce980c2 --- /dev/null +++ b/dlls/kernel32/tests/power.c @@ -0,0 +1,79 @@ +/* + * 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); + + memset(&ps, 0x23, sizeof(ps)); + ret = GetSystemPowerStatus(&ps); + ok(ret == TRUE, "expected TRUE\n"); + + if (ps.BatteryFlag != BATTERY_FLAG_NO_BATTERY) + { + trace("battery detected\n"); + 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); +todo_wine + ok(capacity_flags == expected_capacity_flags, + "expected %u%%-charged battery to have capacity flags 0x%02x, got 0x%02x\n", + ps.BatteryLifePercent, expected_capacity_flags, capacity_flags); + ok(ps.BatteryLifeTime <= ps.BatteryFullLifeTime, + "expected BatteryLifeTime %u to be less than or equal to BatteryFullLifeTime %u\n", + ps.BatteryLifeTime, ps.BatteryFullLifeTime); + if (ps.BatteryFlag & BATTERY_FLAG_CHARGING) + { + ok(ps.BatteryLifeTime == BATTERY_LIFE_UNKNOWN, + "expected BatteryLifeTime to be -1 when charging, got %u\n", ps.BatteryLifeTime); + ok(ps.BatteryFullLifeTime == BATTERY_LIFE_UNKNOWN, + "expected BatteryFullLifeTime to be -1 when charging, got %u\n", ps.BatteryFullLifeTime); + } + } + else + { + trace("no battery detected\n"); + ok(ps.ACLineStatus == AC_LINE_ONLINE, + "expected ACLineStatus to be 1, got %u\n", ps.ACLineStatus); + ok(ps.BatteryLifePercent == BATTERY_PERCENTAGE_UNKNOWN, + "expected BatteryLifePercent to be -1, got %u\n", ps.BatteryLifePercent); + ok(ps.BatteryLifeTime == BATTERY_LIFE_UNKNOWN, + "expected BatteryLifeTime to be -1, got %u\n", ps.BatteryLifeTime); + ok(ps.BatteryFullLifeTime == BATTERY_LIFE_UNKNOWN, + "expected BatteryFullLifeTime to be -1, got %u\n", ps.BatteryFullLifeTime); + } +} + +START_TEST(power) +{ + test_GetSystemPowerStatus(); +}
Signed-off-by: Alex Henrie alexhenrie24@gmail.com --- dlls/kernel32/powermgnt.c | 46 +++++++++++++++++++++++++++++-------- dlls/kernel32/tests/power.c | 1 - 2 files changed, 36 insertions(+), 11 deletions(-)
diff --git a/dlls/kernel32/powermgnt.c b/dlls/kernel32/powermgnt.c index 7efd84d410..13e7e391de 100644 --- a/dlls/kernel32/powermgnt.c +++ b/dlls/kernel32/powermgnt.c @@ -45,19 +45,45 @@ BOOL WINAPI GetDevicePowerState(HANDLE hDevice, BOOL* pfOn) */ BOOL WINAPI GetSystemPowerStatus(LPSYSTEM_POWER_STATUS ps) { - WARN("(%p): stub, harmless.\n", ps); + SYSTEM_BATTERY_STATE bs;
- if (ps) + 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; + + if (FAILED(NtPowerInformation(SystemBatteryState, NULL, 0, &bs, sizeof(bs)))) + return FALSE; + + ps->ACLineStatus = bs.AcOnLine; + + if (bs.BatteryPresent) { - ps->ACLineStatus = 255; - ps->BatteryFlag = 255; - ps->BatteryLifePercent = 255; - ps->SystemStatusFlag = 0; - ps->BatteryLifeTime = ~0u; - ps->BatteryFullLifeTime = ~0u; - return TRUE; + ps->BatteryLifePercent = bs.MaxCapacity ? bs.RemainingCapacity / bs.MaxCapacity : 100; + ps->BatteryLifeTime = bs.EstimatedTime; + if (!bs.Charging && (LONG)bs.Rate < 0) + ps->BatteryFullLifeTime = 3600 * bs.MaxCapacity / -(LONG)bs.Rate; + + ps->BatteryFlag = 0; + if (bs.Charging) + ps->BatteryFlag |= BATTERY_FLAG_CHARGING; + 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; } - return FALSE; + else + { + ps->BatteryFlag = BATTERY_FLAG_NO_BATTERY; + } + + return TRUE; }
/*********************************************************************** diff --git a/dlls/kernel32/tests/power.c b/dlls/kernel32/tests/power.c index ae5ce980c2..74a16537e1 100644 --- a/dlls/kernel32/tests/power.c +++ b/dlls/kernel32/tests/power.c @@ -44,7 +44,6 @@ void test_GetSystemPowerStatus(void) if (ps.BatteryLifePercent < 5) expected_capacity_flags |= BATTERY_FLAG_CRITICAL; capacity_flags = (ps.BatteryFlag & ~BATTERY_FLAG_CHARGING); -todo_wine ok(capacity_flags == expected_capacity_flags, "expected %u%%-charged battery to have capacity flags 0x%02x, got 0x%02x\n", ps.BatteryLifePercent, expected_capacity_flags, capacity_flags);
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=56909
Your paranoid android.
=== debian10 (32 bit report) ===
kernel32: console.c:2733: Test failed: got 16, expected 0 console.c:2923: Test succeeded inside todo block: got 0, expected 0 console.c:2934: Test succeeded inside todo block: got 0, expected 0 process.c:1628: Test failed: Console:winRight expected 79, but got 12
=== debian10 (32 bit Chinese:China report) ===
kernel32: console.c:2733: Test failed: got 16, expected 0 console.c:2923: Test succeeded inside todo block: got 0, expected 0 console.c:2934: Test succeeded inside todo block: got 0, expected 0 process.c:1628: Test failed: Console:winRight expected 79, but got 12
=== debian10 (32 bit WoW report) ===
kernel32: console.c:2733: Test failed: got 16, expected 0 console.c:2923: Test succeeded inside todo block: got 0, expected 0 console.c:2934: Test succeeded inside todo block: got 0, expected 0 process.c:1628: Test failed: Console:winRight expected 79, but got 12
=== debian10 (64 bit WoW report) ===
kernel32: console.c:2733: Test failed: got 16, expected 0 console.c:2923: Test succeeded inside todo block: got 0, expected 0 console.c:2934: Test succeeded inside todo block: got 0, expected 0 debugger.c:320: Test failed: GetThreadContext failed: 5 process.c:1628: Test failed: Console:winRight expected 79, but got 12