From 6341882a5acdd20761caaf57b82b9397fd0eca5a Mon Sep 17 00:00:00 2001
From: Sebastian Lackner <sebastian@fds-team.de>
Date: Fri, 10 Jul 2015 16:13:53 +0200
Subject: server: Implement locking and synchronization of keystate buffer.
 (rev 3)

---
 server/queue.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 57 insertions(+), 2 deletions(-)

diff --git a/server/queue.c b/server/queue.c
index e4b2969..4b4ff5d 100644
--- a/server/queue.c
+++ b/server/queue.c
@@ -113,7 +113,9 @@ struct thread_input
     user_handle_t          cursor;        /* current cursor */
     int                    cursor_count;  /* cursor show count */
     struct list            msg_list;      /* list of hardware messages */
+    int                    lock_count;    /* lock counter for keystate */
     unsigned char          keystate[256]; /* state of each key */
+    unsigned char          shadow_keystate[256]; /* shadow copy of keystate */
 };
 
 struct msg_queue
@@ -124,6 +126,7 @@ struct msg_queue
     unsigned int           wake_mask;       /* wakeup mask */
     unsigned int           changed_bits;    /* changed wakeup bits */
     unsigned int           changed_mask;    /* changed wakeup mask */
+    int                    keystate_locked; /* keystate is locked */
     int                    paint_count;     /* pending paint messages count */
     int                    hotkey_count;    /* pending hotkey messages count */
     int                    quit_message;    /* is there a pending quit message? */
@@ -251,9 +254,11 @@ static struct thread_input *create_thread_input( struct thread *thread )
         input->move_size    = 0;
         input->cursor       = 0;
         input->cursor_count = 0;
+        input->lock_count   = 0;
         list_init( &input->msg_list );
         set_caret_window( input, 0 );
         memset( input->keystate, 0, sizeof(input->keystate) );
+        memset( input->shadow_keystate, 0, sizeof(input->shadow_keystate) );
 
         if (!(input->desktop = get_thread_desktop( thread, 0 /* FIXME: access rights */ )))
         {
@@ -284,6 +289,7 @@ static struct msg_queue *create_msg_queue( struct thread *thread, struct thread_
         queue->wake_mask       = 0;
         queue->changed_bits    = 0;
         queue->changed_mask    = 0;
+        queue->keystate_locked = 0;
         queue->paint_count     = 0;
         queue->hotkey_count    = 0;
         queue->quit_message    = 0;
@@ -327,8 +333,10 @@ static int assign_thread_input( struct thread *thread, struct thread_input *new_
     }
     if (queue->input)
     {
+        if (queue->keystate_locked) queue->input->lock_count--;
         queue->input->cursor_count -= queue->cursor_count;
         release_object( queue->input );
+        queue->keystate_locked = 0;
     }
     queue->input = (struct thread_input *)grab_object( new_input );
     new_input->cursor_count += queue->cursor_count;
@@ -987,6 +995,7 @@ static void msg_queue_destroy( struct object *obj )
         free( timer );
     }
     if (queue->timeout) remove_timeout_user( queue->timeout );
+    if (queue->keystate_locked) queue->input->lock_count--;
     queue->input->cursor_count -= queue->cursor_count;
     release_object( queue->input );
     if (queue->hooks) release_object( queue->hooks );
@@ -1087,7 +1096,11 @@ int attach_thread_input( struct thread *thread_from, struct thread *thread_to )
     }
 
     ret = assign_thread_input( thread_from, input );
-    if (ret) memset( input->keystate, 0, sizeof(input->keystate) );
+    if (ret)
+    {
+        memset( input->keystate, 0, sizeof(input->keystate) );
+        memset( input->shadow_keystate, 0, sizeof(input->shadow_keystate) );
+    }
     release_object( input );
     return ret;
 }
@@ -1321,9 +1334,30 @@ static void update_key_state( struct desktop *desktop, unsigned char *keystate,
     }
 }
 
+/* synchronizes the thread input key state with the desktop */
+static void synchronize_input_key_state( struct thread_input *input )
+{
+    if (!input->lock_count)
+    {
+        unsigned char *shadow_keystate = input->shadow_keystate;
+        unsigned char *keystate = input->keystate;
+        unsigned int i;
+
+        for (i = 0; i < 256; i++)
+        {
+            if (input->desktop->keystate[i] != shadow_keystate[i])
+            {
+                keystate[i] = input->desktop->keystate[i] & ~0x40;
+                shadow_keystate[i] = input->desktop->keystate[i];
+            }
+        }
+    }
+}
+
 /* update the thread input key state for a keyboard message */
 static void update_input_key_state( struct thread_input *input, const struct message *msg )
 {
+    synchronize_input_key_state( input );
     update_key_state( input->desktop, input->keystate, msg );
 }
 
@@ -1532,6 +1566,15 @@ static void queue_hardware_message( struct desktop *desktop, struct message *msg
     else
     {
         msg->unique_id = 0;  /* will be set once we return it to the app */
+
+        /* lock the keystate on the first hardware message */
+        if (!thread->queue->keystate_locked)
+        {
+            synchronize_input_key_state( input );
+            input->lock_count++;
+            thread->queue->keystate_locked = 1;
+        }
+
         list_add_tail( &input->msg_list, &msg->entry );
         set_queue_bits( thread->queue, get_hardware_msg_bit(msg) );
     }
@@ -2411,6 +2454,13 @@ DECL_HANDLER(get_message)
     queue->last_get_msg = current_time;
     if (!filter) filter = QS_ALLINPUT;
 
+    /* no longer lock the keystate if we have processed all input */
+    if (queue->keystate_locked && !(queue->wake_bits & QS_ALLINPUT))
+    {
+        queue->input->lock_count--;
+        queue->keystate_locked = 0;
+    }
+
     /* first check for sent messages */
     if ((ptr = list_head( &queue->msg_list[SEND_MESSAGE] )))
     {
@@ -2861,7 +2911,12 @@ DECL_HANDLER(get_key_state)
         if (!(thread = get_thread_from_id( req->tid ))) return;
         if (thread->queue)
         {
-            if (req->key >= 0) reply->state = thread->queue->input->keystate[req->key & 0xff];
+            if (req->key >= 0)
+            {
+                /* synchronize with desktop keystate, but _only_ if req->key is given */
+                synchronize_input_key_state( thread->queue->input );
+                reply->state = thread->queue->input->keystate[req->key & 0xff];
+            }
             set_reply_data( thread->queue->input->keystate, size );
             release_object( thread );
             return;
-- 
2.5.0

