Signed-off-by: Chip Davis cdavis@codeweavers.com --- dlls/ntdll/nt.c | 10 +++++----- dlls/ntdll/ntdll_misc.h | 1 + dlls/ntdll/server.c | 1 + 3 files changed, 7 insertions(+), 5 deletions(-)
diff --git a/dlls/ntdll/nt.c b/dlls/ntdll/nt.c index a9985fbc224..c1b0816a35e 100644 --- a/dlls/ntdll/nt.c +++ b/dlls/ntdll/nt.c @@ -3062,14 +3062,14 @@ 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; + struct ntdll_thread_data *thread_data = ntdll_get_thread_data(); + + *old_state = thread_data->exec_state;
WARN( "(0x%x, %p): stub, harmless.\n", new_state, old_state );
- if (!(current & ES_CONTINUOUS) || (new_state & ES_CONTINUOUS)) - current = new_state; + if (new_state & ES_CONTINUOUS) + thread_data->exec_state = new_state; return STATUS_SUCCESS; }
diff --git a/dlls/ntdll/ntdll_misc.h b/dlls/ntdll/ntdll_misc.h index 4556e1e5ce3..30080a47e53 100644 --- a/dlls/ntdll/ntdll_misc.h +++ b/dlls/ntdll/ntdll_misc.h @@ -245,6 +245,7 @@ struct ntdll_thread_data int wait_fd[2]; /* fd for sleeping server requests */ BOOL wow64_redir; /* Wow64 filesystem redirection flag */ pthread_t pthread_id; /* pthread thread id */ + EXECUTION_STATE exec_state; /* current thread power execution state */ };
C_ASSERT( sizeof(struct ntdll_thread_data) <= sizeof(((TEB *)0)->GdiTebBatch) ); diff --git a/dlls/ntdll/server.c b/dlls/ntdll/server.c index 33ba95c00cc..385f5af6ebb 100644 --- a/dlls/ntdll/server.c +++ b/dlls/ntdll/server.c @@ -1525,6 +1525,7 @@ size_t server_init_thread( void *entry_point, BOOL *suspend )
is_wow64 = !is_win64 && (server_cpus & ((1 << CPU_x86_64) | (1 << CPU_ARM64))) != 0; ntdll_get_thread_data()->wow64_redir = is_wow64; + ntdll_get_thread_data()->exec_state = ES_CONTINUOUS;
switch (ret) {
Test interaction between SetThreadExecutionState(), PowerSetRequest(), and this parameter.
Signed-off-by: Chip Davis cdavis@codeweavers.com --- configure | 1 + configure.ac | 1 + dlls/powrprof/tests/Makefile.in | 4 + dlls/powrprof/tests/powrprof.c | 216 ++++++++++++++++++++++++++++++++ include/powrprof.h | 5 + 5 files changed, 227 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..67eea95d1fe --- /dev/null +++ b/dlls/powrprof/tests/powrprof.c @@ -0,0 +1,216 @@ +/* + * 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); + 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); + 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); + + old_es = SetThreadExecutionState(ES_CONTINUOUS); + ok(old_es == (ES_CONTINUOUS|ES_DISPLAY_REQUIRED), "unexpected execution state 0x%08x\n", old_es); + + 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); + 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); + ok(es == ES_CONTINUOUS, "unexpected execution state 0x%08x\n", es); + SignalObjectAndWait(events[1], thread, INFINITE, FALSE); + + 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, hntdll; + + 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);
Signed-off-by: Chip Davis cdavis@codeweavers.com --- include/winnt.h | 1 + server/Makefile.in | 1 + server/power.c | 241 ++++++++++++++++++++++++++++++++++++++++++++ server/protocol.def | 33 ++++++ 4 files changed, 276 insertions(+) create mode 100644 server/power.c
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..4ac35589182 --- /dev/null +++ b/server/power.c @@ -0,0 +1,241 @@ +/* + * 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 "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 ); +} + + +/* 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; +} + +/* Increment the current system execution state */ +DECL_HANDLER(change_system_execution_state) +{ + if ((req->old_state & ES_SYSTEM_REQUIRED) && !(req->new_state & ES_SYSTEM_REQUIRED)) + --sys_count; + else if (!(req->old_state & ES_SYSTEM_REQUIRED) && (req->new_state & ES_SYSTEM_REQUIRED)) + ++sys_count; + + if ((req->old_state & ES_DISPLAY_REQUIRED) && !(req->new_state & ES_DISPLAY_REQUIRED)) + --disp_count; + else if (!(req->old_state & ES_DISPLAY_REQUIRED) && (req->new_state & ES_DISPLAY_REQUIRED)) + ++disp_count; + + if ((req->old_state & ES_AWAYMODE_REQUIRED) && !(req->new_state & ES_AWAYMODE_REQUIRED)) + --away_count; + else if (!(req->old_state & ES_AWAYMODE_REQUIRED) && (req->new_state & ES_AWAYMODE_REQUIRED)) + ++away_count; +} + +/* 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 ); +} + +/* 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 ); +} + diff --git a/server/protocol.def b/server/protocol.def index 8157199f2fa..7dea95e25fb 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -3944,3 +3944,36 @@ 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 + +/* Change the current system execution state */ +@REQ(change_system_execution_state) + unsigned int old_state; /* thread's old execution state */ + unsigned int new_state; /* thread's new 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 + +
Get it from the server in NtPowerInformation(), and change it when NtSetThreadExecutionState() is called.
Signed-off-by: Chip Davis cdavis@codeweavers.com --- dlls/ntdll/nt.c | 25 ++++++++++++++++++++----- dlls/powrprof/tests/powrprof.c | 6 +++--- 2 files changed, 23 insertions(+), 8 deletions(-)
diff --git a/dlls/ntdll/nt.c b/dlls/ntdll/nt.c index c1b0816a35e..a62c321883e 100644 --- a/dlls/ntdll/nt.c +++ b/dlls/ntdll/nt.c @@ -3069,7 +3069,18 @@ NTSTATUS WINAPI NtSetThreadExecutionState( EXECUTION_STATE new_state, EXECUTION_ WARN( "(0x%x, %p): stub, harmless.\n", new_state, old_state );
if (new_state & ES_CONTINUOUS) + { + NTSTATUS status; thread_data->exec_state = new_state; + SERVER_START_REQ( change_system_execution_state ) + { + req->old_state = *old_state; + req->new_state = new_state; + status = wine_server_call( req ); + } + SERVER_END_REQ; + return status; + } return STATUS_SUCCESS; }
@@ -3189,13 +3200,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 67eea95d1fe..5a1ebfa813e 100644 --- a/dlls/powrprof/tests/powrprof.c +++ b/dlls/powrprof/tests/powrprof.c @@ -54,7 +54,7 @@ static void test_system_execution_state(void)
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); @@ -130,14 +130,14 @@ 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); 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); ok(es == ES_CONTINUOUS, "unexpected execution state 0x%08x\n", es); SignalObjectAndWait(events[1], thread, INFINITE, FALSE);
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 --- dlls/ntdll/nt.c | 90 +++++++++++++++++++++++++++++++--- dlls/powrprof/tests/powrprof.c | 14 +++--- 2 files changed, 91 insertions(+), 13 deletions(-)
diff --git a/dlls/ntdll/nt.c b/dlls/ntdll/nt.c index a62c321883e..e534e6e0bec 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,15 +3085,72 @@ NTSTATUS WINAPI NtSetThreadExecutionState( EXECUTION_STATE new_state, EXECUTION_ return STATUS_SUCCESS; }
+static NTSTATUS do_format_string( const UNICODE_STRING *file, unsigned int id, unsigned int count, + UNICODE_STRING *params, UNICODE_STRING *dest ) +{ + NTSTATUS status; + HMODULE mod; + 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 ))) + goto done; + if ((status = LdrAccessResource( mod, entry, &rsrc, NULL ))) + goto done; + str = rsrc; + for (i = 0; i < (id & 0xf); i++) + str += *str + 1; + ++str; + if (!RtlCreateUnicodeString( dest, str )) + status = STATUS_NO_MEMORY; + +done: + LdrUnloadDll( mod ); + return status; +} + /****************************************************************************** * NtCreatePowerRequest [NTDLL.@] * */ NTSTATUS WINAPI NtCreatePowerRequest( HANDLE *handle, COUNTED_REASON_CONTEXT *context ) { - FIXME( "(%p, %p): stub\n", handle, context ); + UNICODE_STRING reason; + NTSTATUS status;
- 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 ))) + 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; + + return status; }
/****************************************************************************** @@ -3101,9 +3159,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; }
/****************************************************************************** @@ -3112,9 +3180,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 5a1ebfa813e..2b1d1069b6a 100644 --- a/dlls/powrprof/tests/powrprof.c +++ b/dlls/powrprof/tests/powrprof.c @@ -170,31 +170,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);
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=55435
Your paranoid android.
=== debian10 (32 bit report) ===
powrprof: powrprof.c:201: Test failed: unexpected execution state 0x00000002
=== debian10 (32 bit French report) ===
powrprof: powrprof.c:201: Test failed: unexpected execution state 0x00000002
=== debian10 (32 bit Japanese:Japan report) ===
powrprof: powrprof.c:201: Test failed: unexpected execution state 0x00000002
=== debian10 (32 bit Chinese:China report) ===
powrprof: powrprof.c:201: Test failed: unexpected execution state 0x00000002
=== debian10 (32 bit WoW report) ===
powrprof: powrprof.c:201: Test failed: unexpected execution state 0x00000002
=== debian10 (64 bit WoW report) ===
powrprof: powrprof.c:201: Test failed: unexpected execution state 0x00000002
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=55433
Your paranoid android.
=== debian10 (32 bit WoW report) ===
ntdll: pipe.c:1576: Test failed: pipe is not signaled