From 8f5ab18e86ae4914f3a79e16f8317a8f297ac179 Mon Sep 17 00:00:00 2001
From: Sebastian Lackner <sebastian@fds-team.de>
Date: Sat, 21 Mar 2015 22:35:24 +0100
Subject: user32: Cache the result of GetForegroundWindow.

---
 dlls/user32/focus.c        | 21 ++++++++++++++++++++-
 dlls/user32/user_private.h |  2 ++
 server/protocol.def        |  5 ++++-
 server/queue.c             | 20 ++++++++++++++++++--
 4 files changed, 44 insertions(+), 4 deletions(-)

diff --git a/dlls/user32/focus.c b/dlls/user32/focus.c
index 35fe89b..8b17d1f 100644
--- a/dlls/user32/focus.c
+++ b/dlls/user32/focus.c
@@ -367,12 +367,31 @@ HWND WINAPI GetFocus(void)
  */
 HWND WINAPI GetForegroundWindow(void)
 {
+    struct user_thread_info *thread_info = get_user_thread_info();
+    shmglobal_t *shm = wine_get_shmglobal();
     HWND ret = 0;
+    DWORD epoch;
+
+    if (shm)
+    {
+        epoch = shm->foreground_wnd_epoch;
+
+        if (epoch == thread_info->foreground_wnd_epoch)
+            return thread_info->foreground_wnd;
+    }
 
     SERVER_START_REQ( get_thread_input )
     {
         req->tid = 0;
-        if (!wine_server_call_err( req )) ret = wine_server_ptr_handle( reply->foreground );
+        if (!wine_server_call_err( req ))
+        {
+            ret = wine_server_ptr_handle( reply->foreground );
+            if (shm)
+            {
+                thread_info->foreground_wnd         = ret;
+                thread_info->foreground_wnd_epoch   = epoch;
+            }
+        }
     }
     SERVER_END_REQ;
     return ret;
diff --git a/dlls/user32/user_private.h b/dlls/user32/user_private.h
index 1c87de4..26c771e 100644
--- a/dlls/user32/user_private.h
+++ b/dlls/user32/user_private.h
@@ -189,6 +189,8 @@ struct user_thread_info
     HWND                          top_window;             /* Desktop window */
     HWND                          msg_window;             /* HWND_MESSAGE parent window */
     RAWINPUT                     *rawinput;
+    HWND                          foreground_wnd;         /* Cache of the foreground window */
+    DWORD                         foreground_wnd_epoch;   /* Counter to invalidate foreground window */
 };
 
 C_ASSERT( sizeof(struct user_thread_info) <= sizeof(((TEB *)0)->Win32ClientInfo) );
diff --git a/server/protocol.def b/server/protocol.def
index 29c31da..a49d9d0 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -69,11 +69,14 @@ struct request_max_size
 #define FIRST_USER_HANDLE 0x0020  /* first possible value for low word of user handle */
 #define LAST_USER_HANDLE  0xffef  /* last possible value for low word of user handle */
 
+/* wineserver global shared memory block */
 typedef struct
 {
-    unsigned int last_input_time;   /* last input time */
+    unsigned int last_input_time;       /* last input time */
+    unsigned int foreground_wnd_epoch;  /* counter to invalidate foreground window */
 } shmglobal_t;
 
+/* wineserver local shared memory block */
 typedef struct
 {
     int             queue_bits;     /* queue wake bits */
diff --git a/server/queue.c b/server/queue.c
index 6bbd2f2..f430eef 100644
--- a/server/queue.c
+++ b/server/queue.c
@@ -448,6 +448,7 @@ static void set_foreground_input( struct desktop *desktop, struct thread_input *
     if (desktop->foreground_input == input) return;
     set_clip_rectangle( desktop, NULL, 1 );
     desktop->foreground_input = input;
+    if (shmglobal) interlocked_xchg_add( (int *)&shmglobal->foreground_wnd_epoch, 1 );
 }
 
 /* get the hook table for a given thread */
@@ -1093,7 +1094,12 @@ static inline void thread_input_cleanup_window( struct msg_queue *queue, user_ha
 
     if (window == input->focus) input->focus = 0;
     if (window == input->capture) input->capture = 0;
-    if (window == input->active) input->active = 0;
+    if (window == input->active)
+    {
+        input->active = 0;
+        if (shmglobal && input->desktop->foreground_input == input)
+            interlocked_xchg_add( (int *)&shmglobal->foreground_wnd_epoch, 1 );
+    }
     if (window == input->menu_owner) input->menu_owner = 0;
     if (window == input->move_size) input->move_size = 0;
     if (window == input->caret) set_caret_window( input, 0 );
@@ -1148,7 +1154,12 @@ int attach_thread_input( struct thread *thread_from, struct thread *thread_to )
     if (thread_from->queue)
     {
         if (!input->focus) input->focus = thread_from->queue->input->focus;
-        if (!input->active) input->active = thread_from->queue->input->active;
+        if (!input->active)
+        {
+            input->active = thread_from->queue->input->active;
+            if (shmglobal && input->desktop->foreground_input == input)
+                interlocked_xchg_add( (int *)&shmglobal->foreground_wnd_epoch, 1 );
+        }
     }
 
     ret = assign_thread_input( thread_from, input );
@@ -1180,6 +1191,9 @@ void detach_thread_input( struct thread *thread_from )
             {
                 input->active = old_input->active;
                 old_input->active = 0;
+                if (shmglobal && (input->desktop->foreground_input == input ||
+                                  old_input->desktop->foreground_input == old_input))
+                    interlocked_xchg_add( (int *)&shmglobal->foreground_wnd_epoch, 1 );
             }
             release_object( thread );
         }
@@ -3070,6 +3084,8 @@ DECL_HANDLER(set_active_window)
             reply->previous = queue->input->active;
             queue->input->active = get_user_full_handle( req->handle );
             update_shm_thread_input( queue->input );
+            if (shmglobal && queue->input->desktop->foreground_input == queue->input)
+                interlocked_xchg_add( (int *)&shmglobal->foreground_wnd_epoch, 1 );
         }
         else set_error( STATUS_INVALID_HANDLE );
     }
-- 
2.3.7

