[PATCH 2/3] server: Lock thread input keystate whenever it is modified.

Rémi Bernon rbernon at codeweavers.com
Tue Apr 6 10:57:54 CDT 2021


And synchronize it with desktop async keystate, on GetKeyState calls,
if it is not locked yet.

Based on a patch from Sebastian Lackner <sebastian at fds-team.de>.

Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=26269
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=27238
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=31899
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=35907
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=45385
Signed-off-by: Rémi Bernon <rbernon at codeweavers.com>
---
 dlls/user32/tests/input.c |  6 ++---
 server/queue.c            | 51 ++++++++++++++++++++++++++++++++++++++-
 2 files changed, 53 insertions(+), 4 deletions(-)

diff --git a/dlls/user32/tests/input.c b/dlls/user32/tests/input.c
index c146e4b5cd9..246569961be 100644
--- a/dlls/user32/tests/input.c
+++ b/dlls/user32/tests/input.c
@@ -3842,15 +3842,15 @@ static DWORD WINAPI get_key_state_thread(void *arg)
         else expect_c = FALSE;
 
         check_get_keyboard_state(i, j, expect_c, FALSE, /* todo */ !has_queue);
-        check_get_key_state(i, j, expect_c, expect_x, /* todo */ has_queue || j == 0);
-        check_get_keyboard_state(i, j, expect_c, expect_x, /* todo */ has_queue || j == 0);
+        check_get_key_state(i, j, expect_c, expect_x, /* todo */ !has_queue && j == 0);
+        check_get_keyboard_state(i, j, expect_c, expect_x, /* todo */ !has_queue && j == 0);
 
         /* key released */
         ReleaseSemaphore(semaphores[0], 1, NULL);
         result = WaitForSingleObject(semaphores[1], 1000);
         ok(result == WAIT_OBJECT_0, "%d: WaitForSingleObject returned %u\n", i, result);
 
-        check_get_keyboard_state(i, j, expect_c, expect_x, /* todo */ has_queue || j > 0);
+        check_get_keyboard_state(i, j, expect_c, expect_x, /* todo */ !has_queue && j > 0);
         check_get_key_state(i, j, expect_c, FALSE, /* todo */ FALSE);
         check_get_keyboard_state(i, j, expect_c, FALSE, /* todo */ FALSE);
     }
diff --git a/server/queue.c b/server/queue.c
index 5c9f91a13c5..0782c526327 100644
--- a/server/queue.c
+++ b/server/queue.c
@@ -114,6 +114,8 @@ struct thread_input
     int                    cursor_count;  /* cursor show count */
     struct list            msg_list;      /* list of hardware messages */
     unsigned char          keystate[256]; /* state of each key */
+    unsigned char          desktop_keystate[256]; /* desktop keystate when keystate was synced */
+    int                    keystate_lock; /* keystate is locked */
 };
 
 struct msg_queue
@@ -140,6 +142,7 @@ struct msg_queue
     struct thread_input   *input;           /* thread input descriptor */
     struct hook_table     *hooks;           /* hook table */
     timeout_t              last_get_msg;    /* time of last get message call */
+    int                    keystate_lock;   /* owns an input keystate lock */
 };
 
 struct hotkey
@@ -265,12 +268,14 @@ static struct thread_input *create_thread_input( struct thread *thread )
         list_init( &input->msg_list );
         set_caret_window( input, 0 );
         memset( input->keystate, 0, sizeof(input->keystate) );
+        input->keystate_lock = 0;
 
         if (!(input->desktop = get_thread_desktop( thread, 0 /* FIXME: access rights */ )))
         {
             release_object( input );
             return NULL;
         }
+        memcpy( input->desktop_keystate, input->desktop->keystate, sizeof(input->desktop_keystate) );
     }
     return input;
 }
@@ -305,6 +310,7 @@ static struct msg_queue *create_msg_queue( struct thread *thread, struct thread_
         queue->input           = (struct thread_input *)grab_object( input );
         queue->hooks           = NULL;
         queue->last_get_msg    = current_time;
+        queue->keystate_lock   = 0;
         list_init( &queue->send_result );
         list_init( &queue->callback_result );
         list_init( &queue->pending_timers );
@@ -326,6 +332,31 @@ void free_msg_queue( struct thread *thread )
     thread->queue = NULL;
 }
 
+/* synchronize thread input keystate with the desktop */
+static void sync_input_keystate( struct thread_input *input )
+{
+    int i;
+    if (!input->desktop || input->keystate_lock) return;
+    for (i = 0; i < sizeof(input->keystate); ++i)
+    {
+        if (input->desktop_keystate[i] == input->desktop->keystate[i]) continue;
+        input->keystate[i] = input->desktop_keystate[i] = input->desktop->keystate[i];
+    }
+}
+
+/* locks thread input keystate to prevent synchronization */
+static void lock_input_keystate( struct thread_input *input )
+{
+    input->keystate_lock++;
+}
+
+/* unlock the thread input keystate and synchronize it again */
+static void unlock_input_keystate( struct thread_input *input )
+{
+    input->keystate_lock--;
+    if (!input->keystate_lock) sync_input_keystate( input );
+}
+
 /* change the thread input data of a given thread */
 static int assign_thread_input( struct thread *thread, struct thread_input *new_input )
 {
@@ -339,9 +370,11 @@ static int assign_thread_input( struct thread *thread, struct thread_input *new_
     if (queue->input)
     {
         queue->input->cursor_count -= queue->cursor_count;
+        if (queue->keystate_lock) unlock_input_keystate( queue->input );
         release_object( queue->input );
     }
     queue->input = (struct thread_input *)grab_object( new_input );
+    if (queue->keystate_lock) lock_input_keystate( queue->input );
     new_input->cursor_count += queue->cursor_count;
     return 1;
 }
@@ -477,6 +510,11 @@ static inline int is_signaled( struct msg_queue *queue )
 /* set some queue bits */
 static inline void set_queue_bits( struct msg_queue *queue, unsigned int bits )
 {
+    if (bits & (QS_KEY | QS_MOUSEBUTTON))
+    {
+        if (!queue->keystate_lock) lock_input_keystate( queue->input );
+        queue->keystate_lock = 1;
+    }
     queue->wake_bits |= bits;
     queue->changed_bits |= bits;
     if (is_signaled( queue )) wake_up( &queue->obj, 0 );
@@ -487,6 +525,11 @@ static inline void clear_queue_bits( struct msg_queue *queue, unsigned int bits
 {
     queue->wake_bits &= ~bits;
     queue->changed_bits &= ~bits;
+    if (!(queue->wake_bits & (QS_KEY | QS_MOUSEBUTTON)))
+    {
+        if (queue->keystate_lock) unlock_input_keystate( queue->input );
+        queue->keystate_lock = 0;
+    }
 }
 
 /* check whether msg is a keyboard message */
@@ -1031,6 +1074,7 @@ static void msg_queue_destroy( struct object *obj )
     }
     if (queue->timeout) remove_timeout_user( queue->timeout );
     queue->input->cursor_count -= queue->cursor_count;
+    if (queue->keystate_lock) unlock_input_keystate( queue->input );
     release_object( queue->input );
     if (queue->hooks) release_object( queue->hooks );
     if (queue->fd) release_object( queue->fd );
@@ -2997,7 +3041,11 @@ DECL_HANDLER(get_key_state)
     else
     {
         unsigned char *keystate = current->queue->input->keystate;
-        if (req->key >= 0) reply->state = keystate[req->key & 0xff];
+        if (req->key >= 0)
+        {
+            if (current->queue) sync_input_keystate( current->queue->input );
+            reply->state = keystate[req->key & 0xff];
+        }
         set_reply_data( keystate, size );
     }
 }
@@ -3011,6 +3059,7 @@ DECL_HANDLER(set_key_state)
     data_size_t size = min( 256, get_req_data_size() );
 
     memcpy( queue->input->keystate, get_req_data(), size );
+    memcpy( queue->input->desktop_keystate, queue->input->desktop->keystate, 256 );
     if (req->async && (desktop = get_thread_desktop( current, 0 )))
     {
         memcpy( desktop->keystate, get_req_data(), size );
-- 
2.31.0




More information about the wine-devel mailing list