Test interaction between SetThreadExecutionState(), PowerSetRequest(), and this parameter.
Signed-off-by: Chip Davis cdavis@codeweavers.com ---
Notes: v2: Test that, when a thread terminates, its execution state is cleaned up.
configure.ac | 1 + dlls/powrprof/tests/Makefile.in | 4 + dlls/powrprof/tests/powrprof.c | 219 ++++++++++++++++++++++++++++++++ include/powrprof.h | 5 + 4 files changed, 229 insertions(+) create mode 100644 dlls/powrprof/tests/Makefile.in create mode 100644 dlls/powrprof/tests/powrprof.c
diff --git a/configure.ac b/configure.ac index c69c5ecb45b..a3b0f1eac01 100644 --- a/configure.ac +++ b/configure.ac @@ -3578,6 +3578,7 @@ WINE_CONFIG_MAKEFILE(dlls/pdh/tests) WINE_CONFIG_MAKEFILE(dlls/photometadatahandler) WINE_CONFIG_MAKEFILE(dlls/pidgen) WINE_CONFIG_MAKEFILE(dlls/powrprof) +WINE_CONFIG_MAKEFILE(dlls/powrprof/tests) WINE_CONFIG_MAKEFILE(dlls/printui) WINE_CONFIG_MAKEFILE(dlls/prntvpt) WINE_CONFIG_MAKEFILE(dlls/propsys) diff --git a/dlls/powrprof/tests/Makefile.in b/dlls/powrprof/tests/Makefile.in new file mode 100644 index 00000000000..87aa06be502 --- /dev/null +++ b/dlls/powrprof/tests/Makefile.in @@ -0,0 +1,4 @@ +TESTDLL = powrprof.dll +IMPORTS = powrprof + +C_SRCS = powrprof.c diff --git a/dlls/powrprof/tests/powrprof.c b/dlls/powrprof/tests/powrprof.c new file mode 100644 index 00000000000..d744895cc48 --- /dev/null +++ b/dlls/powrprof/tests/powrprof.c @@ -0,0 +1,219 @@ +/* + * Copyright 2019 Chip Davis for CodeWeavers + * + * 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 <stdarg.h> +#include <stdio.h> + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winbase.h" +#include "winternl.h" +#include "powrprof.h" + +#include "wine/test.h" + +HANDLE (WINAPI *pPowerCreateRequest)(REASON_CONTEXT *); +BOOL (WINAPI *pPowerSetRequest)(HANDLE, POWER_REQUEST_TYPE); +BOOL (WINAPI *pPowerClearRequest)(HANDLE, POWER_REQUEST_TYPE); + +static void test_system_execution_state(void) +{ + EXECUTION_STATE es, old_es; + NTSTATUS status; + + status = CallNtPowerInformation(SystemExecutionState, NULL, 0, &es, sizeof(es)); + ok(status == STATUS_SUCCESS, "status %08x\n", status); + + old_es = SetThreadExecutionState(ES_SYSTEM_REQUIRED); + todo_wine ok(old_es == ES_CONTINUOUS, "unexpected execution state 0x%08x\n", old_es); + + old_es = es; + status = CallNtPowerInformation(SystemExecutionState, NULL, 0, &es, sizeof(es)); + ok(status == STATUS_SUCCESS, "status %08x\n", status); + ok(es == old_es, "unexpected execution state 0x%08x vs 0x%08x\n", es, old_es); + + old_es = SetThreadExecutionState(ES_CONTINUOUS | ES_DISPLAY_REQUIRED); + todo_wine ok(old_es == ES_CONTINUOUS, "unexpected execution state 0x%08x\n", old_es); + + status = CallNtPowerInformation(SystemExecutionState, NULL, 0, &es, sizeof(es)); + ok(status == STATUS_SUCCESS, "status %08x\n", status); + todo_wine ok(es & ES_DISPLAY_REQUIRED, "unexpected execution state 0x%08x\n", es); + + old_es = SetThreadExecutionState(ES_CONTINUOUS); + ok(old_es == (ES_CONTINUOUS|ES_DISPLAY_REQUIRED), "unexpected execution state 0x%08x\n", old_es); + + status = CallNtPowerInformation(SystemExecutionState, NULL, 0, &es, sizeof(es)); + ok(status == STATUS_SUCCESS, "status %08x\n", status); + + old_es = SetThreadExecutionState(ES_DISPLAY_REQUIRED); + ok(old_es == ES_CONTINUOUS, "unexpected execution state 0x%08x\n", old_es); + + old_es = es; + status = CallNtPowerInformation(SystemExecutionState, NULL, 0, &es, sizeof(es)); + ok(status == STATUS_SUCCESS, "status %08x\n", status); + ok(es == old_es, "unexpected execution state 0x%08x vs 0x%08x\n", es, old_es); +} + +static HANDLE events[2]; + +static DWORD CALLBACK execution_state_thread(LPVOID param) +{ + EXECUTION_STATE old_es; + + SetThreadExecutionState(ES_SYSTEM_REQUIRED); + SignalObjectAndWait(events[0], events[1], INFINITE, FALSE); + + old_es = SetThreadExecutionState(ES_DISPLAY_REQUIRED); + ok(old_es == ES_CONTINUOUS, "unexpected execution state 0x%08x\n", old_es); + SignalObjectAndWait(events[0], events[1], INFINITE, FALSE); + + old_es = SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED); + ok(old_es == ES_CONTINUOUS, "unexpected execution state 0x%08x\n", old_es); + SignalObjectAndWait(events[0], events[1], INFINITE, FALSE); + + old_es = SetThreadExecutionState(ES_CONTINUOUS | ES_DISPLAY_REQUIRED); + ok(old_es == (ES_CONTINUOUS|ES_SYSTEM_REQUIRED), "unexpected execution state 0x%08x\n", old_es); + SignalObjectAndWait(events[0], events[1], INFINITE, FALSE); + + return 0; +} + +static void test_system_execution_state_other_thread(void) +{ + HANDLE thread; + EXECUTION_STATE base_es, es; + NTSTATUS status; + + status = CallNtPowerInformation(SystemExecutionState, NULL, 0, &base_es, sizeof(base_es)); + ok(status == STATUS_SUCCESS, "status %08x\n", status); + trace("base execution state = 0x%08x\n", base_es); + + events[0] = CreateEventW(NULL, FALSE, FALSE, NULL); + events[1] = CreateEventW(NULL, FALSE, FALSE, NULL); + thread = CreateThread(NULL, 0, execution_state_thread, NULL, 0, NULL); + ok(thread != NULL, "Failed to create thread, err %u\n", GetLastError()); + + WaitForSingleObject(events[0], INFINITE); + status = CallNtPowerInformation(SystemExecutionState, NULL, 0, &es, sizeof(es)); + ok(status == STATUS_SUCCESS, "status %08x\n", status); + ok(es == base_es, "unexpected execution state 0x%08x\n", es); + es = SetThreadExecutionState(0); + ok(es == ES_CONTINUOUS, "unexpected execution state 0x%08x\n", es); + SignalObjectAndWait(events[1], events[0], INFINITE, FALSE); + + status = CallNtPowerInformation(SystemExecutionState, NULL, 0, &es, sizeof(es)); + ok(status == STATUS_SUCCESS, "status %08x\n", status); + ok(es == base_es, "unexpected execution state 0x%08x\n", es); + es = SetThreadExecutionState(0); + ok(es == ES_CONTINUOUS, "unexpected execution state 0x%08x\n", es); + SignalObjectAndWait(events[1], events[0], INFINITE, FALSE); + + status = CallNtPowerInformation(SystemExecutionState, NULL, 0, &es, sizeof(es)); + ok(status == STATUS_SUCCESS, "status %08x\n", status); + todo_wine ok(es & ES_SYSTEM_REQUIRED, "unexpected execution state 0x%08x\n", es); + es = SetThreadExecutionState(0); + todo_wine ok(es == ES_CONTINUOUS, "unexpected execution state 0x%08x\n", es); + SignalObjectAndWait(events[1], events[0], INFINITE, FALSE); + + status = CallNtPowerInformation(SystemExecutionState, NULL, 0, &es, sizeof(es)); + ok(status == STATUS_SUCCESS, "status %08x\n", status); + todo_wine ok(es & ES_DISPLAY_REQUIRED, "unexpected execution state 0x%08x\n", es); + es = SetThreadExecutionState(0); + todo_wine ok(es == ES_CONTINUOUS, "unexpected execution state 0x%08x\n", es); + SignalObjectAndWait(events[1], thread, INFINITE, FALSE); + + status = CallNtPowerInformation(SystemExecutionState, NULL, 0, &es, sizeof(es)); + ok(status == STATUS_SUCCESS, "status %08x\n", status); + ok(es == base_es, "unexpected execution state 0x%08x\n", es); + es = SetThreadExecutionState(0); + todo_wine ok(es == ES_CONTINUOUS, "unexpected execution state 0x%08x\n", es); + + CloseHandle(thread); + CloseHandle(events[0]); + CloseHandle(events[1]); +} + +static void test_system_execution_state_power_request() +{ + HANDLE req; + REASON_CONTEXT reason; + BOOL ret; + NTSTATUS status; + EXECUTION_STATE base_es, es; + static const WCHAR reasonW[] = {'W', 'i', 'n', 'e', ' ', 't', 'e', 's', 't', 0}; + + if (!pPowerCreateRequest) + { + win_skip("Power request objects unavailable\n"); + return; + } + + status = CallNtPowerInformation(SystemExecutionState, NULL, 0, &base_es, sizeof(base_es)); + ok(status == STATUS_SUCCESS, "status %08x\n", status); + trace("base execution state = 0x%08x\n", base_es); + + reason.Version = 0; + reason.Flags = POWER_REQUEST_CONTEXT_SIMPLE_STRING; + reason.Reason.SimpleReasonString = reasonW; + req = pPowerCreateRequest(&reason); + todo_wine ok(req != INVALID_HANDLE_VALUE, "err %u\n", GetLastError()); + + ret = pPowerSetRequest(req, PowerRequestSystemRequired); + todo_wine ok(ret, "err %u\n", GetLastError()); + + status = CallNtPowerInformation(SystemExecutionState, NULL, 0, &es, sizeof(es)); + ok(status == STATUS_SUCCESS, "status %08x\n", status); + todo_wine ok(es & ES_SYSTEM_REQUIRED, "unexpected execution state 0x%08x\n", es); + + ret = pPowerClearRequest(req, PowerRequestSystemRequired); + todo_wine ok(ret, "err %u\n", GetLastError()); + + status = CallNtPowerInformation(SystemExecutionState, NULL, 0, &es, sizeof(es)); + ok(status == STATUS_SUCCESS, "status %08x\n", status); + ok(!(es & ES_SYSTEM_REQUIRED) || (base_es & ES_SYSTEM_REQUIRED), "unexpected execution state 0x%08x\n", es); + + ret = pPowerSetRequest(req, PowerRequestDisplayRequired); + todo_wine ok(ret, "err %u\n", GetLastError()); + + status = CallNtPowerInformation(SystemExecutionState, NULL, 0, &es, sizeof(es)); + ok(status == STATUS_SUCCESS, "status %08x\n", status); + todo_wine ok(es & ES_DISPLAY_REQUIRED, "unexpected execution state 0x%08x\n", es); + + ret = CloseHandle(req); + todo_wine ok(ret, "err %u\n", GetLastError()); + + status = CallNtPowerInformation(SystemExecutionState, NULL, 0, &es, sizeof(es)); + ok(status == STATUS_SUCCESS, "status %08x\n", status); + ok(!(es & ES_DISPLAY_REQUIRED) || (base_es & ES_DISPLAY_REQUIRED), "unexpected execution state 0x%08x\n", es); +} + +START_TEST(powrprof) +{ + HMODULE hkernel32; + + hkernel32 = GetModuleHandleA("kernel32.dll"); + pPowerCreateRequest = (void *)GetProcAddress(hkernel32, "PowerCreateRequest"); + pPowerSetRequest = (void *)GetProcAddress(hkernel32, "PowerSetRequest"); + pPowerClearRequest = (void *)GetProcAddress(hkernel32, "PowerClearRequest"); + + test_system_execution_state(); + test_system_execution_state_other_thread(); + test_system_execution_state_power_request(); +} diff --git a/include/powrprof.h b/include/powrprof.h index 318188e465e..421d9d4fc76 100644 --- a/include/powrprof.h +++ b/include/powrprof.h @@ -146,6 +146,11 @@ extern "C" {
typedef BOOLEAN (CALLBACK* PWRSCHEMESENUMPROC)(UINT, DWORD, LPWSTR, DWORD, LPWSTR, PPOWER_POLICY, LPARAM);
+#ifndef WINE_NTSTATUS_DECLARED +#define WINE_NTSTATUS_DECLARED +typedef LONG NTSTATUS; +#endif + NTSTATUS WINAPI CallNtPowerInformation(POWER_INFORMATION_LEVEL, PVOID, ULONG, PVOID, ULONG); BOOLEAN WINAPI CanUserWritePwrScheme(VOID); BOOLEAN WINAPI DeletePwrScheme(UINT);
Get it from the server in NtPowerInformation(), and change it when NtSetThreadExecutionState() is called.
Signed-off-by: Chip Davis cdavis@codeweavers.com ---
Notes: v2: Handle thread termination.
dlls/ntdll/nt.c | 29 ++++++----- dlls/powrprof/tests/powrprof.c | 16 +++--- include/winnt.h | 1 + server/Makefile.in | 1 + server/power.c | 90 ++++++++++++++++++++++++++++++++++ server/protocol.def | 13 +++++ server/thread.c | 2 + server/thread.h | 2 + 8 files changed, 135 insertions(+), 19 deletions(-) create mode 100644 server/power.c
diff --git a/dlls/ntdll/nt.c b/dlls/ntdll/nt.c index a9985fbc224..7af01a775c1 100644 --- a/dlls/ntdll/nt.c +++ b/dlls/ntdll/nt.c @@ -3062,15 +3062,18 @@ NTSTATUS WINAPI NtInitiatePowerAction( */ NTSTATUS WINAPI NtSetThreadExecutionState( EXECUTION_STATE new_state, EXECUTION_STATE *old_state ) { - static EXECUTION_STATE current = - ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED | ES_USER_PRESENT; - *old_state = current; + NTSTATUS status;
WARN( "(0x%x, %p): stub, harmless.\n", new_state, old_state );
- if (!(current & ES_CONTINUOUS) || (new_state & ES_CONTINUOUS)) - current = new_state; - return STATUS_SUCCESS; + SERVER_START_REQ( set_thread_execution_state ) + { + req->new_state = new_state; + status = wine_server_call( req ); + *old_state = reply->old_state; + } + SERVER_END_REQ; + return status; }
/****************************************************************************** @@ -3189,13 +3192,17 @@ NTSTATUS WINAPI NtPowerInformation( return STATUS_SUCCESS; } case SystemExecutionState: { - PULONG ExecutionState = lpOutputBuffer; - WARN("semi-stub: SystemExecutionState\n"); /* Needed for .NET Framework, but using a FIXME is really noisy. */ + EXECUTION_STATE *exec_state = lpOutputBuffer; + NTSTATUS status; if (lpInputBuffer != NULL) return STATUS_INVALID_PARAMETER; - /* FIXME: The actual state should be the value set by SetThreadExecutionState which is not currently implemented. */ - *ExecutionState = ES_USER_PRESENT; - return STATUS_SUCCESS; + SERVER_START_REQ( get_system_execution_state ) + { + status = wine_server_call( req ); + if (!status) *exec_state = reply->exec_state; + } + SERVER_END_REQ; + return status; } case ProcessorInformation: { const int cannedMHz = 1000; /* We fake a 1GHz processor if we can't conjure up real values */ diff --git a/dlls/powrprof/tests/powrprof.c b/dlls/powrprof/tests/powrprof.c index d744895cc48..ce9f96bfd90 100644 --- a/dlls/powrprof/tests/powrprof.c +++ b/dlls/powrprof/tests/powrprof.c @@ -42,7 +42,7 @@ static void test_system_execution_state(void) ok(status == STATUS_SUCCESS, "status %08x\n", status);
old_es = SetThreadExecutionState(ES_SYSTEM_REQUIRED); - todo_wine ok(old_es == ES_CONTINUOUS, "unexpected execution state 0x%08x\n", old_es); + ok(old_es == ES_CONTINUOUS, "unexpected execution state 0x%08x\n", old_es);
old_es = es; status = CallNtPowerInformation(SystemExecutionState, NULL, 0, &es, sizeof(es)); @@ -50,11 +50,11 @@ static void test_system_execution_state(void) ok(es == old_es, "unexpected execution state 0x%08x vs 0x%08x\n", es, old_es);
old_es = SetThreadExecutionState(ES_CONTINUOUS | ES_DISPLAY_REQUIRED); - todo_wine ok(old_es == ES_CONTINUOUS, "unexpected execution state 0x%08x\n", old_es); + ok(old_es == ES_CONTINUOUS, "unexpected execution state 0x%08x\n", old_es);
status = CallNtPowerInformation(SystemExecutionState, NULL, 0, &es, sizeof(es)); ok(status == STATUS_SUCCESS, "status %08x\n", status); - todo_wine ok(es & ES_DISPLAY_REQUIRED, "unexpected execution state 0x%08x\n", es); + ok(es & ES_DISPLAY_REQUIRED, "unexpected execution state 0x%08x\n", es);
old_es = SetThreadExecutionState(ES_CONTINUOUS); ok(old_es == (ES_CONTINUOUS|ES_DISPLAY_REQUIRED), "unexpected execution state 0x%08x\n", old_es); @@ -127,23 +127,23 @@ static void test_system_execution_state_other_thread(void)
status = CallNtPowerInformation(SystemExecutionState, NULL, 0, &es, sizeof(es)); ok(status == STATUS_SUCCESS, "status %08x\n", status); - todo_wine ok(es & ES_SYSTEM_REQUIRED, "unexpected execution state 0x%08x\n", es); + ok(es & ES_SYSTEM_REQUIRED, "unexpected execution state 0x%08x\n", es); es = SetThreadExecutionState(0); - todo_wine ok(es == ES_CONTINUOUS, "unexpected execution state 0x%08x\n", es); + ok(es == ES_CONTINUOUS, "unexpected execution state 0x%08x\n", es); SignalObjectAndWait(events[1], events[0], INFINITE, FALSE);
status = CallNtPowerInformation(SystemExecutionState, NULL, 0, &es, sizeof(es)); ok(status == STATUS_SUCCESS, "status %08x\n", status); - todo_wine ok(es & ES_DISPLAY_REQUIRED, "unexpected execution state 0x%08x\n", es); + ok(es & ES_DISPLAY_REQUIRED, "unexpected execution state 0x%08x\n", es); es = SetThreadExecutionState(0); - todo_wine ok(es == ES_CONTINUOUS, "unexpected execution state 0x%08x\n", es); + ok(es == ES_CONTINUOUS, "unexpected execution state 0x%08x\n", es); SignalObjectAndWait(events[1], thread, INFINITE, FALSE);
status = CallNtPowerInformation(SystemExecutionState, NULL, 0, &es, sizeof(es)); ok(status == STATUS_SUCCESS, "status %08x\n", status); ok(es == base_es, "unexpected execution state 0x%08x\n", es); es = SetThreadExecutionState(0); - todo_wine ok(es == ES_CONTINUOUS, "unexpected execution state 0x%08x\n", es); + ok(es == ES_CONTINUOUS, "unexpected execution state 0x%08x\n", es);
CloseHandle(thread); CloseHandle(events[0]); diff --git a/include/winnt.h b/include/winnt.h index 87b4adbfea0..e94ca65f0aa 100644 --- a/include/winnt.h +++ b/include/winnt.h @@ -906,6 +906,7 @@ typedef enum _HEAP_INFORMATION_CLASS { #define ES_SYSTEM_REQUIRED 0x00000001 #define ES_DISPLAY_REQUIRED 0x00000002 #define ES_USER_PRESENT 0x00000004 +#define ES_AWAYMODE_REQUIRED 0x00000040 #define ES_CONTINUOUS 0x80000000
/* The Win32 register context */ diff --git a/server/Makefile.in b/server/Makefile.in index b39bd30305b..e619d8223fb 100644 --- a/server/Makefile.in +++ b/server/Makefile.in @@ -27,6 +27,7 @@ C_SRCS = \ procfs.c \ ptrace.c \ queue.c \ + power.c \ region.c \ registry.c \ request.c \ diff --git a/server/power.c b/server/power.c new file mode 100644 index 00000000000..d629dac0218 --- /dev/null +++ b/server/power.c @@ -0,0 +1,90 @@ +/* + * Power management support + * + * Copyright (C) 1998 Alexandre Julliard + * Copyright (C) 2003 Mike McCormack + * Copyright (C) 2005 Robert Shearman + * Copyright (C) 2019 Chip Davis for CodeWeavers + * + * 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 "config.h" + +#include <assert.h> +#include <stdio.h> +#include <stdarg.h> + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winternl.h" + +#include "handle.h" +#include "request.h" +#include "thread.h" +#include "unicode.h" + +static unsigned int sys_count = 0; /* number of holds on system sleep */ +static unsigned int disp_count = 0; /* number of holds on display sleep */ +static unsigned int away_count = 0; /* number of away mode requests */ + +void release_thread_execution_state( struct thread *thread ) +{ + if (thread->exec_state & ES_SYSTEM_REQUIRED) + --sys_count; + if (thread->exec_state & ES_DISPLAY_REQUIRED) + --disp_count; + if (thread->exec_state & ES_AWAYMODE_REQUIRED) + --away_count; +} + + +/* Get the current system execution state */ +DECL_HANDLER(get_system_execution_state) +{ + reply->exec_state = 0; + if (sys_count != 0) + reply->exec_state |= ES_SYSTEM_REQUIRED; + if (disp_count != 0) + reply->exec_state |= ES_DISPLAY_REQUIRED; + if (away_count != 0) + reply->exec_state |= ES_AWAYMODE_REQUIRED; +} + +/* Set the current thread's execution state */ +DECL_HANDLER(set_thread_execution_state) +{ + reply->old_state = current->exec_state; + + if (!(req->new_state & ES_CONTINUOUS)) + return; + + if ((current->exec_state & ES_SYSTEM_REQUIRED) && !(req->new_state & ES_SYSTEM_REQUIRED)) + --sys_count; + else if (!(current->exec_state & ES_SYSTEM_REQUIRED) && (req->new_state & ES_SYSTEM_REQUIRED)) + ++sys_count; + + if ((current->exec_state & ES_DISPLAY_REQUIRED) && !(req->new_state & ES_DISPLAY_REQUIRED)) + --disp_count; + else if (!(current->exec_state & ES_DISPLAY_REQUIRED) && (req->new_state & ES_DISPLAY_REQUIRED)) + ++disp_count; + + if ((current->exec_state & ES_AWAYMODE_REQUIRED) && !(req->new_state & ES_AWAYMODE_REQUIRED)) + --away_count; + else if (!(current->exec_state & ES_AWAYMODE_REQUIRED) && (req->new_state & ES_AWAYMODE_REQUIRED)) + ++away_count; + current->exec_state = req->new_state; +} diff --git a/server/protocol.def b/server/protocol.def index 8157199f2fa..22640bb625b 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -3944,3 +3944,16 @@ struct handle_info @REQ(resume_process) obj_handle_t handle; /* process handle */ @END + +/* Get the current system execution state */ +@REQ(get_system_execution_state) +@REPLY + unsigned int exec_state; /* current execution state */ +@END + +/* Set the current thread's execution state */ +@REQ(set_thread_execution_state) + unsigned int new_state; /* thread's new execution state */ +@REPLY + unsigned int old_state; /* thread's old execution state */ +@END diff --git a/server/thread.c b/server/thread.c index dc327603daa..d34d23aec6a 100644 --- a/server/thread.c +++ b/server/thread.c @@ -205,6 +205,7 @@ static inline void init_thread_structure( struct thread *thread ) thread->reply_fd = NULL; thread->wait_fd = NULL; thread->state = RUNNING; + thread->exec_state = ES_CONTINUOUS; thread->exit_code = 0; thread->priority = 0; thread->suspend = 0; @@ -1223,6 +1224,7 @@ void kill_thread( struct thread *thread, int violent_death ) wake_up( &thread->obj, 0 ); if (violent_death) send_thread_signal( thread, SIGQUIT ); cleanup_thread( thread ); + release_thread_execution_state( thread ); remove_process_thread( thread->process, thread ); release_object( thread ); } diff --git a/server/thread.h b/server/thread.h index 9f7914803de..8e29f9eda33 100644 --- a/server/thread.h +++ b/server/thread.h @@ -72,6 +72,7 @@ struct thread struct fd *reply_fd; /* fd to send a reply to a client */ struct fd *wait_fd; /* fd to use to wake a sleeping client */ enum run_state state; /* running state */ + unsigned int exec_state; /* power execution state */ int exit_code; /* thread exit code */ int unix_pid; /* Unix pid of client */ int unix_tid; /* Unix tid of client */ @@ -130,6 +131,7 @@ extern int is_cpu_supported( enum cpu_type cpu ); extern unsigned int get_supported_cpu_mask(void); extern int suspend_thread( struct thread *thread ); extern int resume_thread( struct thread *thread ); +extern void release_thread_execution_state( struct thread *thread );
/* ptrace functions */
These create the object and alter the system execution state, but don't actually force the computer to stay awake. It should be simple enough to implement that on Mac OS. Linux might be a problem--in this instance, we'll probably need to call out to some daemon over DBus.
Signed-off-by: Chip Davis cdavis@codeweavers.com ---
Notes: v2: Fix object leak.
dlls/ntdll/nt.c | 92 ++++++++++++++++-- dlls/powrprof/tests/powrprof.c | 14 +-- server/power.c | 173 +++++++++++++++++++++++++++++++++ server/protocol.def | 19 ++++ 4 files changed, 285 insertions(+), 13 deletions(-)
diff --git a/dlls/ntdll/nt.c b/dlls/ntdll/nt.c index 7af01a775c1..adadd6f2536 100644 --- a/dlls/ntdll/nt.c +++ b/dlls/ntdll/nt.c @@ -51,6 +51,7 @@ #include <time.h>
#define NONAMELESSUNION +#define NONAMELESSSTRUCT #include "ntstatus.h" #define WIN32_NO_STATUS #include "wine/debug.h" @@ -3076,15 +3077,74 @@ NTSTATUS WINAPI NtSetThreadExecutionState( EXECUTION_STATE new_state, EXECUTION_ return status; }
+static NTSTATUS do_format_string( const UNICODE_STRING *file, unsigned int id, unsigned int count, + UNICODE_STRING *params, UNICODE_STRING *dest, HMODULE *mod ) +{ + NTSTATUS status; + LDR_RESOURCE_INFO info; + const IMAGE_RESOURCE_DATA_ENTRY *entry; + void *rsrc; + LANGID lang_id; + const WCHAR *str; + int i; + + FIXME( "Detailed reason string formatting is not implemented\n" ); + + if ((status = LdrLoadDll( NULL, LDR_DONT_RESOLVE_REFS | LDR_NO_DLL_CALLS, file, mod ))) + return status; + info.Type = 6; /* RT_STRING */ + info.Name = (id >> 4) + 1; + NtQueryDefaultUILanguage( &lang_id ); + info.Language = lang_id; + if ((status = LdrFindResource_U( *mod, &info, 3, &entry ))) + return status; + if ((status = LdrAccessResource( *mod, entry, &rsrc, NULL ))) + return status; + str = rsrc; + for (i = 0; i < (id & 0xf); i++) + str += *str + 1; + ++str; + if ((status = RtlInitUnicodeStringEx( dest, str ))) + return status; + + return STATUS_SUCCESS; +} + /****************************************************************************** * NtCreatePowerRequest [NTDLL.@] * */ NTSTATUS WINAPI NtCreatePowerRequest( HANDLE *handle, COUNTED_REASON_CONTEXT *context ) { - FIXME( "(%p, %p): stub\n", handle, context ); + UNICODE_STRING reason; + NTSTATUS status; + HMODULE mod = NULL;
- return STATUS_NOT_IMPLEMENTED; + WARN( "(%p, %p): semi-stub\n", handle, context ); + + if (context->Flags & POWER_REQUEST_CONTEXT_SIMPLE_STRING) + reason = context->u.SimpleString; + else if (context->Flags & POWER_REQUEST_CONTEXT_DETAILED_STRING) + { + if ((status = do_format_string( &context->u.s.ResourceFileName, context->u.s.ResourceReasonId, + context->u.s.StringCount, context->u.s.ReasonStrings, &reason, &mod ))) + { + if (mod) LdrUnloadDll( mod ); + return status; + } + } + + SERVER_START_REQ( create_power_request ) + { + wine_server_add_data( req, reason.Buffer, reason.Length * sizeof(WCHAR) ); + status = wine_server_call( req ); + if (!status) + *handle = wine_server_ptr_handle( reply->handle ); + } + SERVER_END_REQ; + + if (mod) LdrUnloadDll( mod ); + return status; }
/****************************************************************************** @@ -3093,9 +3153,19 @@ NTSTATUS WINAPI NtCreatePowerRequest( HANDLE *handle, COUNTED_REASON_CONTEXT *co */ NTSTATUS WINAPI NtSetPowerRequest( HANDLE handle, POWER_REQUEST_TYPE type ) { - FIXME( "(%p, %u): stub\n", handle, type ); + NTSTATUS status;
- return STATUS_NOT_IMPLEMENTED; + WARN( "(%p, %u): semi-stub\n", handle, type ); + + SERVER_START_REQ( set_power_request ) + { + req->handle = wine_server_obj_handle( handle ); + req->request = type; + status = wine_server_call( req ); + } + SERVER_END_REQ; + + return status; }
/****************************************************************************** @@ -3104,9 +3174,19 @@ NTSTATUS WINAPI NtSetPowerRequest( HANDLE handle, POWER_REQUEST_TYPE type ) */ NTSTATUS WINAPI NtClearPowerRequest( HANDLE handle, POWER_REQUEST_TYPE type ) { - FIXME( "(%p, %u): stub\n", handle, type ); + NTSTATUS status;
- return STATUS_NOT_IMPLEMENTED; + WARN( "(%p, %u): semi-stub\n", handle, type ); + + SERVER_START_REQ( clear_power_request ) + { + req->handle = wine_server_obj_handle( handle ); + req->request = type; + status = wine_server_call( req ); + } + SERVER_END_REQ; + + return status; }
#ifdef linux diff --git a/dlls/powrprof/tests/powrprof.c b/dlls/powrprof/tests/powrprof.c index ce9f96bfd90..8fcb862b798 100644 --- a/dlls/powrprof/tests/powrprof.c +++ b/dlls/powrprof/tests/powrprof.c @@ -173,31 +173,31 @@ static void test_system_execution_state_power_request() reason.Flags = POWER_REQUEST_CONTEXT_SIMPLE_STRING; reason.Reason.SimpleReasonString = reasonW; req = pPowerCreateRequest(&reason); - todo_wine ok(req != INVALID_HANDLE_VALUE, "err %u\n", GetLastError()); + ok(req != INVALID_HANDLE_VALUE, "err %u\n", GetLastError());
ret = pPowerSetRequest(req, PowerRequestSystemRequired); - todo_wine ok(ret, "err %u\n", GetLastError()); + ok(ret, "err %u\n", GetLastError());
status = CallNtPowerInformation(SystemExecutionState, NULL, 0, &es, sizeof(es)); ok(status == STATUS_SUCCESS, "status %08x\n", status); - todo_wine ok(es & ES_SYSTEM_REQUIRED, "unexpected execution state 0x%08x\n", es); + ok(es & ES_SYSTEM_REQUIRED, "unexpected execution state 0x%08x\n", es);
ret = pPowerClearRequest(req, PowerRequestSystemRequired); - todo_wine ok(ret, "err %u\n", GetLastError()); + ok(ret, "err %u\n", GetLastError());
status = CallNtPowerInformation(SystemExecutionState, NULL, 0, &es, sizeof(es)); ok(status == STATUS_SUCCESS, "status %08x\n", status); ok(!(es & ES_SYSTEM_REQUIRED) || (base_es & ES_SYSTEM_REQUIRED), "unexpected execution state 0x%08x\n", es);
ret = pPowerSetRequest(req, PowerRequestDisplayRequired); - todo_wine ok(ret, "err %u\n", GetLastError()); + ok(ret, "err %u\n", GetLastError());
status = CallNtPowerInformation(SystemExecutionState, NULL, 0, &es, sizeof(es)); ok(status == STATUS_SUCCESS, "status %08x\n", status); - todo_wine ok(es & ES_DISPLAY_REQUIRED, "unexpected execution state 0x%08x\n", es); + ok(es & ES_DISPLAY_REQUIRED, "unexpected execution state 0x%08x\n", es);
ret = CloseHandle(req); - todo_wine ok(ret, "err %u\n", GetLastError()); + ok(ret, "err %u\n", GetLastError());
status = CallNtPowerInformation(SystemExecutionState, NULL, 0, &es, sizeof(es)); ok(status == STATUS_SUCCESS, "status %08x\n", status); diff --git a/server/power.c b/server/power.c index d629dac0218..5a8a576b224 100644 --- a/server/power.c +++ b/server/power.c @@ -41,6 +41,137 @@ static unsigned int sys_count = 0; /* number of holds on system sleep */ static unsigned int disp_count = 0; /* number of holds on display sleep */ static unsigned int away_count = 0; /* number of away mode requests */
+struct power_request +{ + struct object obj; /* object header */ + struct unicode_str reason; /* stated reason for the request */ + unsigned sys : 1; /* hold system sleep? */ + unsigned disp : 1; /* hold display sleep? */ + unsigned away : 1; /* request away mode? */ +}; + +static void power_request_dump( struct object *obj, int verbose ); +static struct object_type *power_request_get_type( struct object *obj ); +static void power_request_destroy( struct object *obj ); + +static const struct object_ops power_request_ops = +{ + sizeof(struct power_request), /* size */ + power_request_dump, /* dump */ + power_request_get_type, /* get_type */ + no_add_queue, /* add_queue */ + NULL, /* remove_queue */ + NULL, /* signaled */ + NULL, /* satisfied */ + no_signal, /* signal */ + no_get_fd, /* get_fd */ + no_map_access, /* map_access */ + default_get_sd, /* get_sd */ + default_set_sd, /* set_sd */ + no_lookup_name, /* lookup_name */ + no_link_name, /* link_name */ + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ + no_close_handle, /* close_handle */ + power_request_destroy /* destroy */ +}; + +static void power_request_dump( struct object *obj, int verbose ) +{ + struct power_request *power_req = (struct power_request *)obj; + assert( obj->ops == &power_request_ops ); + fprintf( stderr, "PowerRequest reason=" ); + dump_strW( power_req->reason.str, power_req->reason.len, stderr, """" ); + fprintf( stderr, " hold system=%s hold display=%s away mode=%s\n", power_req->sys ? "TRUE" : "FALSE", + power_req->disp ? "TRUE" : "FALSE", power_req->away ? "TRUE" : "FALSE" ); +} + +static struct object_type *power_request_get_type( struct object *obj ) +{ + static const WCHAR name[] = {'P', 'o', 'w', 'e', 'r', 'R', 'e', 'q', 'u', 'e', 's', 't'}; + static const struct unicode_str str = { name, sizeof(name) }; + return get_object_type( &str ); +} + +static void set_power_request( struct power_request *power_req, POWER_REQUEST_TYPE type ) +{ + switch (type) + { + case PowerRequestDisplayRequired: + if (!power_req->disp) + ++disp_count; + power_req->disp = TRUE; + break; + case PowerRequestSystemRequired: + if (!power_req->sys) + ++sys_count; + power_req->sys = TRUE; + break; + case PowerRequestAwayModeRequired: + if (!power_req->away) + ++away_count; + power_req->away = TRUE; + break; + } +} + +static void clear_power_request( struct power_request *power_req, POWER_REQUEST_TYPE type ) +{ + switch (type) + { + case PowerRequestDisplayRequired: + if (power_req->disp) + --disp_count; + power_req->disp = FALSE; + break; + case PowerRequestSystemRequired: + if (power_req->sys) + --sys_count; + power_req->sys = FALSE; + break; + case PowerRequestAwayModeRequired: + if (power_req->away) + --away_count; + power_req->away = FALSE; + break; + } +} + +static void power_request_destroy( struct object *obj ) +{ + struct power_request *power_req; + + assert( obj->ops == &power_request_ops ); + power_req = (struct power_request *)obj; + + clear_power_request( power_req, PowerRequestDisplayRequired ); + clear_power_request( power_req, PowerRequestSystemRequired ); + clear_power_request( power_req, PowerRequestAwayModeRequired ); + + free( (void *)power_req->reason.str ); +} + +/* creates a new power request */ +static struct power_request *create_power_request( struct unicode_str *reason ) +{ + struct power_request *power_req = alloc_object( &power_request_ops ); + if (power_req) + { + power_req->reason.str = memdup(reason->str, reason->len * sizeof(WCHAR)); + power_req->reason.len = reason->len; + power_req->sys = 0; + power_req->disp = 0; + power_req->away = 0; + } + return power_req; +} + +static struct power_request *get_power_request_obj( struct process *process, obj_handle_t handle ) +{ + return (struct power_request *)get_handle_obj( process, handle, 0, &power_request_ops ); +} + void release_thread_execution_state( struct thread *thread ) { if (thread->exec_state & ES_SYSTEM_REQUIRED) @@ -88,3 +219,45 @@ DECL_HANDLER(set_thread_execution_state) ++away_count; current->exec_state = req->new_state; } + +/* Create a power request object */ +DECL_HANDLER(create_power_request) +{ + struct unicode_str reason = get_req_unicode_str(); + + struct power_request *power_req = create_power_request( &reason ); + + if (power_req) + { + reply->handle = alloc_handle_no_access_check( current->process, power_req, 0, 0 ); + release_object( power_req ); + } + else + { + set_error( STATUS_NO_MEMORY ); + } +} + +/* Enable a request on a power request object */ +DECL_HANDLER(set_power_request) +{ + struct power_request *power_req = get_power_request_obj( current->process, req->handle ); + + if (power_req) + { + set_power_request( power_req, req->request ); + release_object( power_req ); + } +} + +/* Disable a request on a power request object */ +DECL_HANDLER(clear_power_request) +{ + struct power_request *power_req = get_power_request_obj( current->process, req->handle ); + + if (power_req) + { + clear_power_request( power_req, req->request ); + release_object( power_req ); + } +} diff --git a/server/protocol.def b/server/protocol.def index 22640bb625b..08e2cbe966b 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -3957,3 +3957,22 @@ struct handle_info @REPLY unsigned int old_state; /* thread's old execution state */ @END + +/* Create a power request object */ +@REQ(create_power_request) + VARARG(reason,unicode_str); /* reason for the request */ +@REPLY + obj_handle_t handle; /* power request handle */ +@END + +/* Enable a request on a power request object */ +@REQ(set_power_request) + obj_handle_t handle; /* power request handle */ + unsigned int request; /* the request to enable */ +@END + +/* Disable a request on a power request object */ +@REQ(clear_power_request) + obj_handle_t handle; /* power request handle */ + unsigned int request; /* the request to disable */ +@END