Store a bitmap of active hooks on the client side to try to avoid
server calls for hooks that are not set.

diff --git a/dlls/user/hook.c b/dlls/user/hook.c
index 7590247..37e53b3 100644
--- a/dlls/user/hook.c
+++ b/dlls/user/hook.c
@@ -189,7 +189,11 @@
         }
         else req->proc = proc;
 
-        if (!wine_server_call_err( req )) handle = reply->handle;
+        if (!wine_server_call_err( req ))
+        {
+            handle = reply->handle;
+            get_user_thread_info()->active_hooks = reply->active_hooks;
+        }
     }
     SERVER_END_REQ;
 
@@ -333,6 +337,12 @@
 
     USER_CheckNotLock();
 
+    if (!HOOK_IsHooked( id ))
+    {
+        TRACE( "skipping hook %s mask %x\n", hook_names[id-WH_MINHOOK], thread_info->active_hooks );
+        return 0;
+    }
+
     SERVER_START_REQ( start_hook_chain )
     {
         req->id = id;
@@ -346,6 +356,7 @@
             pid          = reply->pid;
             tid          = reply->tid;
             unicode_hook = reply->unicode;
+            thread_info->active_hooks = reply->active_hooks;
         }
     }
     SERVER_END_REQ;
@@ -402,7 +413,10 @@
  */
 BOOL HOOK_IsHooked( INT id )
 {
-    return TRUE;  /* FIXME */
+    struct user_thread_info *thread_info = get_user_thread_info();
+
+    if (!thread_info->active_hooks) return TRUE;
+    return (thread_info->active_hooks & (1 << (id - WH_MINHOOK))) != 0;
 }
 
 
@@ -456,6 +470,7 @@
         req->id   = id;
         req->proc = proc;
         ret = !wine_server_call_err( req );
+        if (ret) get_user_thread_info()->active_hooks = reply->active_hooks;
     }
     SERVER_END_REQ;
     if (!ret && GetLastError() == ERROR_INVALID_HANDLE) SetLastError( ERROR_INVALID_HOOK_HANDLE );
@@ -478,6 +493,7 @@
         req->handle = hhook;
         req->id     = 0;
         ret = !wine_server_call_err( req );
+        if (ret) get_user_thread_info()->active_hooks = reply->active_hooks;
     }
     SERVER_END_REQ;
     if (!ret && GetLastError() == ERROR_INVALID_HANDLE) SetLastError( ERROR_INVALID_HOOK_HANDLE );
@@ -643,7 +659,11 @@
         }
         else req->proc = proc;
 
-        if (!wine_server_call_err( req )) handle = reply->handle;
+        if (!wine_server_call_err( req ))
+        {
+            handle = reply->handle;
+            get_user_thread_info()->active_hooks = reply->active_hooks;
+        }
     }
     SERVER_END_REQ;
 
@@ -675,6 +695,7 @@
         req->handle = hEventHook;
         req->id     = WH_WINEVENT;
         ret = !wine_server_call_err( req );
+        if (ret) get_user_thread_info()->active_hooks = reply->active_hooks;
     }
     SERVER_END_REQ;
     return ret;
@@ -683,8 +704,15 @@
 inline static BOOL find_first_hook(DWORD id, DWORD event, HWND hwnd, LONG object_id,
                                    LONG child_id, struct hook_info *info)
 {
+    struct user_thread_info *thread_info = get_user_thread_info();
     BOOL ret;
 
+    if (!HOOK_IsHooked( id ))
+    {
+        TRACE( "skipping hook %s mask %x\n", hook_names[id-WH_MINHOOK], thread_info->active_hooks );
+        return FALSE;
+    }
+
     SERVER_START_REQ( start_hook_chain )
     {
         req->id = id;
@@ -700,6 +728,7 @@
             info->handle    = reply->handle;
             info->proc      = reply->proc;
             info->tid       = reply->tid;
+            thread_info->active_hooks = reply->active_hooks;
         }
     }
     SERVER_END_REQ;
diff --git a/dlls/user/message.c b/dlls/user/message.c
index 86103dd..33aa87c 100644
--- a/dlls/user/message.c
+++ b/dlls/user/message.c
@@ -1942,6 +1942,7 @@
                     info.hook_proc   = reply->hook_proc;
                     hw_id            = reply->hw_id;
                     extra_info       = reply->info;
+                    thread_info->active_hooks = reply->active_hooks;
                 }
                 else
                 {
diff --git a/dlls/user/user_private.h b/dlls/user/user_private.h
index 874b03e..c31d178 100644
--- a/dlls/user/user_private.h
+++ b/dlls/user/user_private.h
@@ -138,7 +138,8 @@
     DWORD                         GetMessageExtraInfoVal; /* 1c Value for GetMessageExtraInfo */
     HCURSOR                       cursor;                 /* 20 Current cursor */
     INT                           cursor_count;           /* 24 Cursor show count */
-                                                          /* 28-7c Available for more data */
+    UINT                          active_hooks;           /* 28 Bitmap of active hooks */
+                                                          /* 2c-7c Available for more data */
 };
 
 static inline struct user_thread_info *get_user_thread_info(void)
diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h
index b30816a..04457fd 100644
--- a/include/wine/server_protocol.h
+++ b/include/wine/server_protocol.h
@@ -2203,6 +2203,7 @@
     unsigned int    time;
     unsigned int    info;
     unsigned int    hw_id;
+    unsigned int    active_hooks;
     size_t          total;
     /* VARARG(data,bytes); */
 };
@@ -3044,6 +3045,7 @@
 {
     struct reply_header __header;
     user_handle_t  handle;
+    unsigned int   active_hooks;
 };
 
 
@@ -3058,6 +3060,7 @@
 struct remove_hook_reply
 {
     struct reply_header __header;
+    unsigned int   active_hooks;
 };
 
 
@@ -3079,6 +3082,7 @@
     thread_id_t    tid;
     void*          proc;
     int            unicode;
+    unsigned int   active_hooks;
     /* VARARG(module,unicode_str); */
 };
 
@@ -3954,6 +3958,6 @@
     struct set_mailslot_info_reply set_mailslot_info_reply;
 };
 
-#define SERVER_PROTOCOL_VERSION 173
+#define SERVER_PROTOCOL_VERSION 174
 
 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */
diff --git a/server/hook.c b/server/hook.c
index c228f98..125f346 100644
--- a/server/hook.c
+++ b/server/hook.c
@@ -172,6 +172,19 @@
     return elem ? HOOK_ENTRY( elem ) : NULL;
 }
 
+/* check if a given hook should run in the current thread */
+inline static int run_hook_in_current_thread( struct hook *hook )
+{
+    if ((!hook->process || hook->process == current->process) &&
+        (!(hook->flags & WINEVENT_SKIPOWNPROCESS) || hook->process != current->process))
+    {
+        if ((!hook->thread || hook->thread == current) &&
+            (!(hook->flags & WINEVENT_SKIPOWNTHREAD) || hook->thread != current))
+            return 1;
+    }
+    return 0;
+}
+
 /* find the first non-deleted hook in the chain */
 inline static struct hook *get_first_valid_hook( struct hook_table *table, int index,
                                                  int event, user_handle_t win,
@@ -181,22 +194,17 @@
 
     while (hook)
     {
-        if ((!hook->process || hook->process == current->process) &&
-            (!(hook->flags & WINEVENT_SKIPOWNPROCESS) || hook->process != current->process))
+        if (hook->proc && run_hook_in_current_thread( hook ))
         {
-            if ((!hook->thread || hook->thread == current) &&
-                (!(hook->flags & WINEVENT_SKIPOWNTHREAD) || hook->thread != current))
+            if (event >= hook->event_min && event <= hook->event_max)
             {
-                if (hook->proc && event >= hook->event_min && event <= hook->event_max)
-                {
-                    if (hook->flags & WINEVENT_INCONTEXT) return hook;
+                if (hook->flags & WINEVENT_INCONTEXT) return hook;
 
-                    /* only winevent hooks may be out of context */
-                    assert(hook->index + WH_MINHOOK == WH_WINEVENT);
-                    post_win_event( hook->owner, event, win, object_id, child_id,
-                                    hook->proc, hook->module, hook->module_size,
-                                    hook->handle );
-                }
+                /* only winevent hooks may be out of context */
+                assert(hook->index + WH_MINHOOK == WH_WINEVENT);
+                post_win_event( hook->owner, event, win, object_id, child_id,
+                                hook->proc, hook->module, hook->module_size,
+                                hook->handle );
             }
         }
         hook = HOOK_ENTRY( list_next( &table->hooks[index], &hook->chain ) );
@@ -213,22 +221,17 @@
 
     while ((hook = HOOK_ENTRY( list_next( &table->hooks[index], &hook->chain ) )))
     {
-        if ((!hook->process || hook->process == current->process) &&
-            (!(hook->flags & WINEVENT_SKIPOWNPROCESS) || hook->process != current->process))
+        if (hook->proc && run_hook_in_current_thread( hook ))
         {
-            if ((!hook->thread || hook->thread == current) &&
-                (!(hook->flags & WINEVENT_SKIPOWNTHREAD) || hook->thread != current))
+            if (event >= hook->event_min && event <= hook->event_max)
             {
-                if (hook->proc && event >= hook->event_min && event <= hook->event_max)
-                {
-                    if (hook->flags & WINEVENT_INCONTEXT) return hook;
+                if (hook->flags & WINEVENT_INCONTEXT) return hook;
 
-                    /* only winevent hooks may be out of context */
-                    assert(hook->index + WH_MINHOOK == WH_WINEVENT);
-                    post_win_event( hook->owner, event, win, object_id, child_id,
-                                    hook->proc, hook->module, hook->module_size,
-                                    hook->handle );
-                }
+                /* only winevent hooks may be out of context */
+                assert(hook->index + WH_MINHOOK == WH_WINEVENT);
+                post_win_event( hook->owner, event, win, object_id, child_id,
+                                hook->proc, hook->module, hook->module_size,
+                                hook->handle );
             }
         }
     }
@@ -314,6 +317,35 @@
     }
 }
 
+/* get a bitmap of active hooks in a hook table */
+static int is_hook_active( struct hook_table *table, int index )
+{
+    struct hook *hook = get_first_hook( table, index );
+
+    while (hook)
+    {
+        if (hook->proc && run_hook_in_current_thread( hook )) return 1;
+        hook = HOOK_ENTRY( list_next( &table->hooks[index], &hook->chain ) );
+    }
+    return 0;
+}
+
+/* get a bitmap of all active hooks for the current thread */
+unsigned int get_active_hooks(void)
+{
+    struct hook_table *table = get_queue_hooks( current );
+    unsigned int ret = 1 << 31;  /* set high bit to indicate that the bitmap is valid */
+    int id;
+
+    for (id = WH_MINHOOK; id <= WH_WINEVENT; id++)
+    {
+        if ((table && is_hook_active( table, id - WH_MINHOOK )) ||
+            (global_hooks && is_hook_active( global_hooks, id - WH_MINHOOK )))
+            ret |= 1 << (id - WH_MINHOOK);
+    }
+    return ret;
+}
+
 /* set a window hook */
 DECL_HANDLER(set_hook)
 {
@@ -388,6 +420,7 @@
         hook->module      = module;
         hook->module_size = module_size;
         reply->handle = hook->handle;
+        reply->active_hooks = get_active_hooks();
     }
     else if (module) free( module );
 
@@ -423,6 +456,7 @@
         }
     }
     remove_hook( hook );
+    reply->active_hooks = get_active_hooks();
 }
 
 
@@ -438,11 +472,15 @@
         return;
     }
 
-    if (!table || !(hook = get_first_valid_hook( table, req->id - WH_MINHOOK, req->event, req->window, req->object_id, req->child_id )))
+    reply->active_hooks = get_active_hooks();
+
+    if (!table || !(hook = get_first_valid_hook( table, req->id - WH_MINHOOK, req->event,
+                                                 req->window, req->object_id, req->child_id )))
     {
         /* try global table */
         if (!(table = global_hooks) ||
-            !(hook = get_first_valid_hook( global_hooks, req->id - WH_MINHOOK, req->event, req->window, req->object_id, req->child_id )))
+            !(hook = get_first_valid_hook( global_hooks, req->id - WH_MINHOOK, req->event,
+                                           req->window, req->object_id, req->child_id )))
             return;  /* no hook set */
     }
 
diff --git a/server/protocol.def b/server/protocol.def
index c21c8f5..6b07874 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -1564,6 +1564,7 @@
     unsigned int    time;      /* message time */
     unsigned int    info;      /* extra info (callback argument for MSG_CALLBACK_RESULT) */
     unsigned int    hw_id;     /* id if hardware message */
+    unsigned int    active_hooks; /* active hooks bitmap */
     size_t          total;     /* total size of extra data */
     VARARG(data,bytes);        /* message data for sent messages */
 @END
@@ -2129,6 +2130,7 @@
     VARARG(module,unicode_str);    /* module name */
 @REPLY
     user_handle_t  handle;         /* handle to the hook */
+    unsigned int   active_hooks;   /* active hooks bitmap */
 @END
 
 
@@ -2137,6 +2139,8 @@
     user_handle_t  handle;         /* handle to the hook */
     int            id;             /* id of the hook if handle is 0 */
     void*          proc;           /* hook procedure if handle is 0 */
+@REPLY
+    unsigned int   active_hooks;   /* active hooks bitmap */
 @END
 
 
@@ -2153,6 +2157,7 @@
     thread_id_t    tid;            /* thread id for low-level keyboard/mouse hooks */
     void*          proc;           /* hook procedure */
     int            unicode;        /* is it a unicode hook? */
+    unsigned int   active_hooks;   /* active hooks bitmap */
     VARARG(module,unicode_str);    /* module name */
 @END
 
diff --git a/server/queue.c b/server/queue.c
index a0617a1..bdce76a 100644
--- a/server/queue.c
+++ b/server/queue.c
@@ -1572,6 +1572,8 @@
     struct msg_queue *queue = get_current_queue();
     user_handle_t get_win = get_user_full_handle( req->get_win );
 
+    reply->active_hooks = get_active_hooks();
+
     if (!queue) return;
     gettimeofday( &queue->last_get_msg, NULL );
 
diff --git a/server/trace.c b/server/trace.c
index 65ee3e9..cd05668 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -1909,6 +1909,7 @@
     fprintf( stderr, " time=%08x,", req->time );
     fprintf( stderr, " info=%08x,", req->info );
     fprintf( stderr, " hw_id=%08x,", req->hw_id );
+    fprintf( stderr, " active_hooks=%08x,", req->active_hooks );
     fprintf( stderr, " total=%d,", req->total );
     fprintf( stderr, " data=" );
     dump_varargs_bytes( cur_size );
@@ -2542,7 +2543,8 @@
 
 static void dump_set_hook_reply( const struct set_hook_reply *req )
 {
-    fprintf( stderr, " handle=%p", req->handle );
+    fprintf( stderr, " handle=%p,", req->handle );
+    fprintf( stderr, " active_hooks=%08x", req->active_hooks );
 }
 
 static void dump_remove_hook_request( const struct remove_hook_request *req )
@@ -2552,6 +2554,11 @@
     fprintf( stderr, " proc=%p", req->proc );
 }
 
+static void dump_remove_hook_reply( const struct remove_hook_reply *req )
+{
+    fprintf( stderr, " active_hooks=%08x", req->active_hooks );
+}
+
 static void dump_start_hook_chain_request( const struct start_hook_chain_request *req )
 {
     fprintf( stderr, " id=%d,", req->id );
@@ -2568,6 +2575,7 @@
     fprintf( stderr, " tid=%04x,", req->tid );
     fprintf( stderr, " proc=%p,", req->proc );
     fprintf( stderr, " unicode=%d,", req->unicode );
+    fprintf( stderr, " active_hooks=%08x,", req->active_hooks );
     fprintf( stderr, " module=" );
     dump_varargs_unicode_str( cur_size );
 }
@@ -3161,7 +3169,7 @@
     (dump_func)dump_set_caret_window_reply,
     (dump_func)dump_set_caret_info_reply,
     (dump_func)dump_set_hook_reply,
-    (dump_func)0,
+    (dump_func)dump_remove_hook_reply,
     (dump_func)dump_start_hook_chain_reply,
     (dump_func)0,
     (dump_func)dump_get_next_hook_reply,
diff --git a/server/user.h b/server/user.h
index 36fd880..0f66a4d 100644
--- a/server/user.h
+++ b/server/user.h
@@ -53,6 +53,7 @@
 
 extern void close_global_hooks(void);
 extern void remove_thread_hooks( struct thread *thread );
+extern unsigned int get_active_hooks(void);
 
 /* queue functions */