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 9c39eb7e4d..c78d504b70 100644 --- a/include/winnt.h +++ b/include/winnt.h @@ -5619,12 +5619,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 --- 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 5afdf77986..81bac70b0f 100644 --- a/dlls/ntdll/nt.c +++ b/dlls/ntdll/nt.c @@ -3137,6 +3137,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.@] * @@ -3190,6 +3257,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 | 83 +++++++++++++++++++++++++++++++++ 2 files changed, 84 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..af488849e2 --- /dev/null +++ b/dlls/kernel32/tests/power.c @@ -0,0 +1,83 @@ +/* + * 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_UNKNOWN) + { + skip("GetSystemPowerStatus not implemented or not working\n"); + return; + } + else 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); + 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 | 48 +++++++++++++++++++++++++++++++-------- 1 file changed, 38 insertions(+), 10 deletions(-)
diff --git a/dlls/kernel32/powermgnt.c b/dlls/kernel32/powermgnt.c index 7efd84d410..d967dfefa7 100644 --- a/dlls/kernel32/powermgnt.c +++ b/dlls/kernel32/powermgnt.c @@ -45,19 +45,47 @@ BOOL WINAPI GetDevicePowerState(HANDLE hDevice, BOOL* pfOn) */ BOOL WINAPI GetSystemPowerStatus(LPSYSTEM_POWER_STATUS ps) { - WARN("(%p): stub, harmless.\n", ps); + SYSTEM_BATTERY_STATE bs; + NTSTATUS status; + + 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; + + status = NtPowerInformation(SystemBatteryState, NULL, 0, &bs, sizeof(bs)); + if (status == STATUS_NOT_IMPLEMENTED) return TRUE; + if (FAILED(status)) return FALSE; + + ps->ACLineStatus = bs.AcOnLine;
- if (ps) + 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; }
/***********************************************************************
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=58688
Your paranoid android.
=== debian10 (32 bit report) ===
kernel32: comm.c:919: Test failed: OutQueue should not be empty debugger: Timeout
=== debian10 (32 bit Chinese:China report) ===
kernel32: comm.c:919: Test failed: OutQueue should not be empty debugger.c:320: Test failed: GetThreadContext failed: 5