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. v3: Fix warnings. v4: Move to ntdll. Get rid of new test executable.
dlls/ntdll/tests/info.c | 184 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 184 insertions(+)
diff --git a/dlls/ntdll/tests/info.c b/dlls/ntdll/tests/info.c index 93920714f215..91aff16a861a 100644 --- a/dlls/ntdll/tests/info.c +++ b/dlls/ntdll/tests/info.c @@ -41,6 +41,9 @@ static BOOL (WINAPI * pGetLogicalProcessorInformationEx)(LOGICAL_PROCESSOR_R static DEP_SYSTEM_POLICY_TYPE (WINAPI * pGetSystemDEPPolicy)(void); static NTSTATUS (WINAPI * pNtOpenThread)(HANDLE *, ACCESS_MASK, const OBJECT_ATTRIBUTES *, const CLIENT_ID *); static NTSTATUS (WINAPI * pNtQueryObject)(HANDLE, OBJECT_INFORMATION_CLASS, void *, ULONG, ULONG *); +static HANDLE (WINAPI *pPowerCreateRequest)(REASON_CONTEXT *); +static BOOL (WINAPI *pPowerSetRequest)(HANDLE, POWER_REQUEST_TYPE); +static BOOL (WINAPI *pPowerClearRequest)(HANDLE, POWER_REQUEST_TYPE);
static BOOL is_wow64;
@@ -104,6 +107,10 @@ static BOOL InitFunctionPtrs(void)
pGetLogicalProcessorInformationEx = (void *) GetProcAddress(hkernel32, "GetLogicalProcessorInformationEx");
+ pPowerCreateRequest = (void *) GetProcAddress(hkernel32, "PowerCreateRequest"); + pPowerSetRequest = (void *) GetProcAddress(hkernel32, "PowerSetRequest"); + pPowerClearRequest = (void *) GetProcAddress(hkernel32, "PowerClearRequest"); + return TRUE; }
@@ -1003,6 +1010,177 @@ static void test_query_processor_power_info(void) HeapFree(GetProcessHeap(), 0, ppi); }
+static void test_system_execution_state(void) +{ + EXECUTION_STATE es, old_es; + NTSTATUS status; + + status = pNtPowerInformation(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 = pNtPowerInformation(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 = pNtPowerInformation(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 = pNtPowerInformation(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 = pNtPowerInformation(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 = pNtPowerInformation(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 = pNtPowerInformation(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 = pNtPowerInformation(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 = pNtPowerInformation(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 = pNtPowerInformation(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 = pNtPowerInformation(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(void) +{ + HANDLE req; + REASON_CONTEXT reason; + BOOL ret; + NTSTATUS status; + EXECUTION_STATE base_es, es; + static WCHAR reasonW[] = {'W', 'i', 'n', 'e', ' ', 't', 'e', 's', 't', 0}; + + if (!pPowerCreateRequest) + { + win_skip("Power request objects unavailable\n"); + return; + } + + status = pNtPowerInformation(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 = pNtPowerInformation(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 = pNtPowerInformation(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 = pNtPowerInformation(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 = pNtPowerInformation(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); +} + static void test_query_process_wow64(void) { NTSTATUS status; @@ -2424,6 +2602,12 @@ START_TEST(info) trace("Starting test_query_processor_power_info()\n"); test_query_processor_power_info();
+ /* 0x10 SystemExecutionState */ + trace("Starting test_system_execution_state()\n"); + test_system_execution_state(); + test_system_execution_state_other_thread(); + test_system_execution_state_power_request(); + /* NtQueryInformationProcess */
/* 0x0 ProcessBasicInformation */
Signed-off-by: Chip Davis cdavis@codeweavers.com --- include/winnt.h | 1 + server/Makefile.in | 1 + server/power.c | 263 ++++++++++++++++++++++++++++++++++++++++++++ server/protocol.def | 32 ++++++ server/thread.c | 2 + server/thread.h | 2 + 6 files changed, 301 insertions(+) create mode 100644 server/power.c
diff --git a/include/winnt.h b/include/winnt.h index 3f59bd9f30cc..b2b8cdab3f1c 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
#include <excpt.h> diff --git a/server/Makefile.in b/server/Makefile.in index b39bd30305b5..e619d8223fb5 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 000000000000..5a8a576b2241 --- /dev/null +++ b/server/power.c @@ -0,0 +1,263 @@ +/* + * 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 */ + +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) + --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; +} + +/* 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 6af0ae0cff89..191a8a810c58 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -3944,3 +3944,35 @@ 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 + +/* 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 diff --git a/server/thread.c b/server/thread.c index dc327603daa4..d34d23aec6ab 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 9f7914803dec..8e29f9eda33e 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 */
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. v3: Put server changes back in their own patch. v4: Update for test changes.
dlls/ntdll/nt.c | 29 ++++++++++++++++++----------- dlls/ntdll/tests/info.c | 16 ++++++++-------- 2 files changed, 26 insertions(+), 19 deletions(-)
diff --git a/dlls/ntdll/nt.c b/dlls/ntdll/nt.c index 5afdf77986ae..21399e0c4310 100644 --- a/dlls/ntdll/nt.c +++ b/dlls/ntdll/nt.c @@ -3064,15 +3064,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; }
/****************************************************************************** @@ -3191,13 +3194,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/ntdll/tests/info.c b/dlls/ntdll/tests/info.c index 91aff16a861a..752660c7b8a4 100644 --- a/dlls/ntdll/tests/info.c +++ b/dlls/ntdll/tests/info.c @@ -1019,7 +1019,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 = pNtPowerInformation(SystemExecutionState, NULL, 0, &es, sizeof(es)); @@ -1027,11 +1027,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 = pNtPowerInformation(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); @@ -1104,23 +1104,23 @@ static void test_system_execution_state_other_thread(void)
status = pNtPowerInformation(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 = pNtPowerInformation(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 = pNtPowerInformation(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]);
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. v3: Remove half-implemented support for detailed reasons. Fix length passed to server. Put server changes in their own patch. v4: Update for test changes. Fix broken build.
dlls/ntdll/nt.c | 56 ++++++++++++++++++++++++++++++++++++----- dlls/ntdll/tests/info.c | 14 +++++------ 2 files changed, 57 insertions(+), 13 deletions(-)
diff --git a/dlls/ntdll/nt.c b/dlls/ntdll/nt.c index 21399e0c4310..46f29115253e 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" @@ -3084,9 +3085,32 @@ NTSTATUS WINAPI NtSetThreadExecutionState( EXECUTION_STATE new_state, EXECUTION_ */ NTSTATUS WINAPI NtCreatePowerRequest( HANDLE *handle, COUNTED_REASON_CONTEXT *context ) { - FIXME( "(%p, %p): stub\n", handle, context ); + UNICODE_STRING reason; + NTSTATUS status; + HMODULE mod = NULL; + static const WCHAR emptyW[] = {0};
- 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) + { + FIXME( "detailed reason strings are not supported\n" ); + RtlInitUnicodeString( &reason, emptyW ); + } + + SERVER_START_REQ( create_power_request ) + { + wine_server_add_data( req, reason.Buffer, reason.Length ); + status = wine_server_call( req ); + if (!status) + *handle = wine_server_ptr_handle( reply->handle ); + } + SERVER_END_REQ; + + if (mod) LdrUnloadDll( mod ); + return status; }
/****************************************************************************** @@ -3095,9 +3119,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; }
/****************************************************************************** @@ -3106,9 +3140,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/ntdll/tests/info.c b/dlls/ntdll/tests/info.c index 752660c7b8a4..e42c40d8b80e 100644 --- a/dlls/ntdll/tests/info.c +++ b/dlls/ntdll/tests/info.c @@ -1150,31 +1150,31 @@ static void test_system_execution_state_power_request(void) 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 = pNtPowerInformation(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 = pNtPowerInformation(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 = pNtPowerInformation(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 = pNtPowerInformation(SystemExecutionState, NULL, 0, &es, sizeof(es)); ok(status == STATUS_SUCCESS, "status %08x\n", status);