Implemented registry change notifications.

diff --git a/server/handle.c b/server/handle.c
index 00dbc54..9df4293 100644
--- a/server/handle.c
+++ b/server/handle.c
@@ -332,6 +332,8 @@
     table = handle_is_global(handle) ? global_table : process->handles;
     if (entry < table->entries + table->free) table->free = entry - table->entries;
     if (entry == table->entries + table->last) shrink_handle_table( table );
+    /* hack: windows seems to treat registry handles differently */
+    registry_close_handle( obj, handle );
     release_object( obj );
     return 1;
 }
diff --git a/server/object.h b/server/object.h
index d2f57bc..711f584 100644
--- a/server/object.h
+++ b/server/object.h
@@ -195,6 +195,7 @@
 extern void init_registry(void);
 extern void flush_registry(void);
 extern void close_registry(void);
+extern void registry_close_handle( struct object *obj, obj_handle_t hkey );
 
 /* atom functions */
 
diff --git a/server/protocol.def b/server/protocol.def
index 1b15d85..056902c 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -1358,6 +1358,14 @@
 @END
 
 
+@REQ(set_registry_notification)
+    obj_handle_t hkey;         /* key to watch for changes */
+    obj_handle_t event;        /* event to set */
+    int          subtree;      /* should we watch the whole subtree? */
+    unsigned int filter;       /* things to watch */
+@END
+
+
 /* Create a waitable timer */
 @REQ(create_timer)
     int          inherit;       /* inherit flag */
diff --git a/server/registry.c b/server/registry.c
index 792750c..77ddf9f 100644
--- a/server/registry.c
+++ b/server/registry.c
@@ -48,6 +48,16 @@
 #include "winternl.h"
 #include "wine/library.h"
 
+struct notify
+{
+    struct event     *event;    /* event to set when changing this key */
+    int               subtree;  /* true if subtree notification */
+    unsigned int      filter;   /* which events to notify on */
+    obj_handle_t      hkey;     /* hkey associated with this notification */
+    struct notify    *next;     /* list of notifications */
+    struct notify    *prev;     /* list of notifications */
+};
+
 /* a registry key */
 struct key
 {
@@ -64,6 +74,8 @@
     short             flags;       /* flags */
     short             level;       /* saving level */
     time_t            modif;       /* last modification time */
+    struct notify    *first_notify; /* list of notifications */
+    struct notify    *last_notify; /* list of notifications */
 };
 
 /* key flags */
@@ -284,6 +296,53 @@
     fprintf( stderr, "\n" );
 }
 
+/* notify waiter and maybe delete the notification */
+static void do_notification( struct key *key, struct notify *notify, int del )
+{
+    if( notify->event )
+    {
+        set_event( notify->event );
+        release_object( notify->event );
+        notify->event = NULL;
+    }
+
+    if ( !del )
+        return;
+    if( notify->next )
+        notify->next->prev = notify->prev;
+    else
+        key->last_notify = notify->prev;
+    if( notify->prev )
+        notify->prev->next = notify->next;
+    else
+        key->first_notify = notify->next;
+    free( notify );
+}
+
+static struct notify *find_notify( struct key *key, obj_handle_t hkey)
+{
+    struct notify *n;
+
+    for( n=key->first_notify; n; n = n->next)
+        if( n->hkey == hkey )
+            break;
+    return n;
+}
+
+/* close the notification associated with a handle */
+void registry_close_handle( struct object *obj, obj_handle_t hkey )
+{
+    struct key * key = (struct key *) obj;
+    struct notify *notify;
+
+    if( obj->ops != &key_ops )
+        return;
+    notify = find_notify( key, hkey );
+    if( !notify )
+        return;
+    do_notification( key, notify, 1 );
+}
+
 static void key_destroy( struct object *obj )
 {
     int i;
@@ -302,6 +361,9 @@
         key->subkeys[i]->parent = NULL;
         release_object( key->subkeys[i] );
     }
+    /* unconditionally notify everything waiting on this key */
+    while ( key->first_notify )
+        do_notification( key, key->first_notify, 1 );
 }
 
 /* duplicate a key path */
@@ -389,6 +451,8 @@
         key->level       = current_level;
         key->modif       = modif;
         key->parent      = NULL;
+        key->first_notify = NULL;
+        key->last_notify  = NULL;
         if (!(key->name = strdupW( name )))
         {
             release_object( key );
@@ -420,12 +484,32 @@
     for (i = 0; i <= key->last_subkey; i++) make_clean( key->subkeys[i] );
 }
 
-/* update key modification time */
-static void touch_key( struct key *key )
+/* go through all the notifications and send them if necessary */
+void check_notify( struct key *key, unsigned int change, int not_subtree )
 {
+    struct notify *n = key->first_notify;
+    while (n)
+    {
+        struct notify *next = n->next;
+        if ( ( not_subtree || n->subtree ) && ( change & n->filter ) )
+            do_notification( key, n, 0 );
+        n = next;
+    }
+}
+
+/* update key modification time */
+static void touch_key( struct key *key, unsigned int change )
+{
+    struct key *k;
+
     key->modif = time(NULL);
     key->level = max( key->level, current_level );
     make_dirty( key );
+
+    /* do notifications */
+    check_notify( key, change, 1 );
+    for ( k = key->parent; k; k = k->parent )
+        check_notify( k, change & ~REG_NOTIFY_CHANGE_LAST_SET, 0 );
 }
 
 /* try to grow the array of subkeys; return 1 if OK, 0 on error */
@@ -583,6 +667,7 @@
 
     if (!*path) goto done;
     *created = 1;
+    touch_key( key, REG_NOTIFY_CHANGE_NAME ); /* FIXME: is this right? */
     if (flags & KEY_DIRTY) make_dirty( key );
     base = key;
     base_idx = index;
@@ -718,7 +803,7 @@
     }
     if (debug_level > 1) dump_operation( key, NULL, "Delete" );
     free_subkey( parent, index );
-    touch_key( parent );
+    touch_key( parent, REG_NOTIFY_CHANGE_NAME );
 }
 
 /* try to grow the array of values; return 1 if OK, 0 on error */
@@ -821,7 +906,7 @@
     value->type  = type;
     value->len   = len;
     value->data  = ptr;
-    touch_key( key );
+    touch_key( key, REG_NOTIFY_CHANGE_LAST_SET );
     if (debug_level > 1) dump_operation( key, value, "Set" );
 }
 
@@ -912,7 +997,7 @@
     if (value->data) free( value->data );
     for (i = index; i < key->last_value; i++) key->values[i] = key->values[i + 1];
     key->last_value--;
-    touch_key( key );
+    touch_key( key, REG_NOTIFY_CHANGE_LAST_SET );
 
     /* try to shrink the array */
     nb_values = key->nb_values;
@@ -1817,3 +1902,52 @@
         release_object( key );
     }
 }
+
+/* add a registry key change notification */
+DECL_HANDLER(set_registry_notification)
+{
+    struct key *key;
+    struct event *event;
+    struct notify *notify;
+
+    key = get_hkey_obj( req->hkey, KEY_NOTIFY );
+    if( key )
+    {
+        event = get_event_obj( current->process, req->event, SYNCHRONIZE );
+        if( event )
+        {
+            notify = find_notify( key, req->hkey );
+            if( notify )
+            {
+                release_object( notify->event );
+                grab_object( event );
+                notify->event = event;
+            }
+            else
+            {
+                notify = (struct notify *) malloc (sizeof *notify);
+                if( notify )
+                {
+                    grab_object( event );
+                    notify->event   = event;
+                    notify->subtree = req->subtree;
+                    notify->filter  = req->filter;
+                    notify->hkey    = req->hkey;
+    
+                    /* add to linked list */
+                    notify->prev = NULL;
+                    notify->next = key->first_notify;
+                    if ( notify->next )
+                        notify->next->prev = notify;
+                    else
+                        key->last_notify = notify;
+                    key->first_notify = notify;
+                }
+                else
+                    set_error( STATUS_NO_MEMORY );
+            }
+            release_object( event );
+        }
+        release_object( key );
+    }
+}
diff --git a/server/request.h b/server/request.h
index f5fd97d..213da98 100644
--- a/server/request.h
+++ b/server/request.h
@@ -206,6 +206,7 @@
 DECL_HANDLER(save_registry);
 DECL_HANDLER(save_registry_atexit);
 DECL_HANDLER(set_registry_levels);
+DECL_HANDLER(set_registry_notification);
 DECL_HANDLER(create_timer);
 DECL_HANDLER(open_timer);
 DECL_HANDLER(set_timer);
@@ -381,6 +382,7 @@
     (req_handler)req_save_registry,
     (req_handler)req_save_registry_atexit,
     (req_handler)req_set_registry_levels,
+    (req_handler)req_set_registry_notification,
     (req_handler)req_create_timer,
     (req_handler)req_open_timer,
     (req_handler)req_set_timer,
diff --git a/server/trace.c b/server/trace.c
index 368cca2..6412212 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -1569,6 +1569,14 @@
     fprintf( stderr, " period=%d", req->period );
 }
 
+static void dump_set_registry_notification_request( const struct set_registry_notification_request *req )
+{
+    fprintf( stderr, " hkey=%p,", req->hkey );
+    fprintf( stderr, " event=%p,", req->event );
+    fprintf( stderr, " subtree=%d,", req->subtree );
+    fprintf( stderr, " filter=%08x", req->filter );
+}
+
 static void dump_create_timer_request( const struct create_timer_request *req )
 {
     fprintf( stderr, " inherit=%d,", req->inherit );
@@ -2445,6 +2453,7 @@
     (dump_func)dump_save_registry_request,
     (dump_func)dump_save_registry_atexit_request,
     (dump_func)dump_set_registry_levels_request,
+    (dump_func)dump_set_registry_notification_request,
     (dump_func)dump_create_timer_request,
     (dump_func)dump_open_timer_request,
     (dump_func)dump_set_timer_request,
@@ -2617,6 +2626,7 @@
     (dump_func)0,
     (dump_func)0,
     (dump_func)0,
+    (dump_func)0,
     (dump_func)dump_create_timer_reply,
     (dump_func)dump_open_timer_reply,
     (dump_func)0,
@@ -2789,6 +2799,7 @@
     "save_registry",
     "save_registry_atexit",
     "set_registry_levels",
+    "set_registry_notification",
     "create_timer",
     "open_timer",
     "set_timer",