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 */