[PATCH v2 3/3] ntdll: Semi-implement power request functions.

Chip Davis cdavis at codeweavers.com
Wed Aug 14 14:06:23 CDT 2019


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 at 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 */
+ at REQ(create_power_request)
+    VARARG(reason,unicode_str); /* reason for the request */
+ at REPLY
+    obj_handle_t handle;        /* power request handle */
+ at END
+
+/* Enable a request on a power request object */
+ at REQ(set_power_request)
+    obj_handle_t handle;        /* power request handle */
+    unsigned int request;       /* the request to enable */
+ at END
+
+/* Disable a request on a power request object */
+ at REQ(clear_power_request)
+    obj_handle_t handle;        /* power request handle */
+    unsigned int request;       /* the request to disable */
+ at END
-- 
2.21.0




More information about the wine-devel mailing list