Expand gio/win32/Makefile.
[platform/upstream/glib.git] / gio / win32 / gwin32directorymonitor.c
1 /* GIO - GLib Input, Output and Streaming Library
2  * 
3  * Copyright (C) 2006-2007 Red Hat, Inc.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General
16  * Public License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
18  * Boston, MA 02111-1307, USA.
19  *
20  * Author: Vlad Grecescu <b100dian@gmail.com>
21  * 
22  */
23
24 #include "config.h"
25 #include "gwin32directorymonitor.h"
26 #include "giomodule.h"
27 #include <windows.h>
28
29 G_DEFINE_TYPE_WITH_CODE (GWin32DirectoryMonitor, g_win32_directory_monitor, G_TYPE_LOCAL_DIRECTORY_MONITOR,
30 g_io_extension_point_implement (G_LOCAL_DIRECTORY_MONITOR_EXTENSION_POINT_NAME,
31                                                          g_define_type_id,
32                                                          "readdirectorychanges",
33                                                          20))
34
35 struct _GWin32DirectoryMonitorPrivate {
36         OVERLAPPED overlapped;
37         DWORD buffer_allocated_bytes;
38         gchar* file_notify_buffer;
39         DWORD buffer_filled_bytes;
40         HANDLE hDirectory;
41         /** needed in the APC where we only have this private struct */
42         GFileMonitor * self;
43 };
44
45 static void g_win32_directory_monitor_finalize (GObject* base);
46 static gboolean g_win32_directory_monitor_cancel (GFileMonitor* base);
47 static GObject * g_win32_directory_monitor_constructor (GType type, guint n_construct_properties, GObjectConstructParam * construct_properties);
48
49 static gboolean g_win32_directory_monitor_is_supported(void) {
50         return TRUE;
51 }
52
53 static void g_win32_directory_monitor_finalize (GObject* base) {
54         GWin32DirectoryMonitor * self;
55         self = G_WIN32_DIRECTORY_MONITOR (base);
56
57         g_free (self->priv->file_notify_buffer);
58         g_free (self->priv);
59         
60         if (G_OBJECT_CLASS (g_win32_directory_monitor_parent_class)->finalize)
61                 (*G_OBJECT_CLASS (g_win32_directory_monitor_parent_class)->finalize) (base);
62 }
63
64
65 static gboolean g_win32_directory_monitor_cancel (GFileMonitor* base) {
66         GWin32DirectoryMonitor * self;
67         self = G_WIN32_DIRECTORY_MONITOR (base);
68         
69         /* this triggers a last callback() with nBytes=0 */ 
70         CloseHandle (self->priv->hDirectory);
71
72         if (G_FILE_MONITOR_CLASS (g_win32_directory_monitor_parent_class)->cancel)
73         (*G_FILE_MONITOR_CLASS (g_win32_directory_monitor_parent_class)->cancel) (base);
74         return TRUE;
75 }
76
77 void CALLBACK g_win32_directory_monitor_callback (DWORD error, DWORD nBytes, GWin32DirectoryMonitorPrivate* lpOverlapped)
78 {
79         gulong offset;
80         PFILE_NOTIFY_INFORMATION pfile_notify_walker;
81         gulong file_name_len;
82         gchar* file_name;
83         GFile * file;
84
85         static GFileMonitorEvent events[] = {0, 
86                 G_FILE_MONITOR_EVENT_CREATED, /* FILE_ACTION_ADDED            */
87                 G_FILE_MONITOR_EVENT_DELETED, /* FILE_ACTION_REMOVED          */
88                 G_FILE_MONITOR_EVENT_CHANGED, /* FILE_ACTION_MODIFIED         */
89                 G_FILE_MONITOR_EVENT_DELETED, /* FILE_ACTION_RENAMED_OLD_NAME */
90                 G_FILE_MONITOR_EVENT_CREATED, /* FILE_ACTION_RENAMED_NEW_NAME */
91         };
92
93         if (!nBytes) /* monitor was cancelled/finalized */
94                 return;
95         
96         if (g_file_monitor_is_cancelled (G_FILE_MONITOR (lpOverlapped->self)))
97                 return; /* and ReadDirectoryChangesW doesn't get called this time */
98
99         offset = 0;
100         do {
101                 pfile_notify_walker = (PFILE_NOTIFY_INFORMATION)(lpOverlapped->file_notify_buffer + offset);
102                 offset += pfile_notify_walker->NextEntryOffset;
103                 file_name = g_utf16_to_utf8 (pfile_notify_walker->FileName, pfile_notify_walker->FileNameLength / sizeof(WCHAR), NULL, &file_name_len, NULL);
104                 file = g_file_new_for_path (file_name); 
105                 g_file_monitor_emit_event (lpOverlapped->self, file, NULL, events [pfile_notify_walker->Action]);
106                 g_object_unref (file);
107                 g_free (file_name);
108         } while (pfile_notify_walker->NextEntryOffset);
109         
110         ReadDirectoryChangesW (lpOverlapped->hDirectory, (gpointer)lpOverlapped->file_notify_buffer, lpOverlapped->buffer_allocated_bytes, FALSE, 
111                 FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_ATTRIBUTES |
112                 FILE_NOTIFY_CHANGE_SIZE, &lpOverlapped->buffer_filled_bytes, &lpOverlapped->overlapped, g_win32_directory_monitor_callback);
113 }
114
115 static GObject * g_win32_directory_monitor_constructor (GType type, guint n_construct_properties, GObjectConstructParam * construct_properties) {
116         GObject * obj;
117         GWin32DirectoryMonitorClass * klass;
118         GObjectClass * parent_class;
119         GWin32DirectoryMonitor * self;
120         gchar * dirname;
121         gboolean result;
122         
123         klass = G_WIN32_DIRECTORY_MONITOR_CLASS (g_type_class_peek (G_TYPE_WIN32_DIRECTORY_MONITOR));
124         parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (klass));
125         obj = parent_class->constructor (type, n_construct_properties, construct_properties);
126         self = G_WIN32_DIRECTORY_MONITOR (obj);
127         dirname = G_LOCAL_DIRECTORY_MONITOR (obj)->dirname;
128         
129         self->priv->hDirectory = CreateFile (dirname, FILE_LIST_DIRECTORY, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, 
130                 NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL); 
131         g_assert (self->priv->hDirectory != INVALID_HANDLE_VALUE); /* harsh */
132
133         result = ReadDirectoryChangesW (self->priv->hDirectory, (gpointer)self->priv->file_notify_buffer, self->priv->buffer_allocated_bytes, FALSE, 
134                 FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_ATTRIBUTES |
135                 FILE_NOTIFY_CHANGE_SIZE, &self->priv->buffer_filled_bytes, &self->priv->overlapped, g_win32_directory_monitor_callback);
136         g_assert (result); /* harsh */
137
138         return obj;
139 }
140
141 static void g_win32_directory_monitor_class_init (GWin32DirectoryMonitorClass * klass) {
142         
143         g_win32_directory_monitor_parent_class = g_type_class_peek_parent (klass);
144
145         G_OBJECT_CLASS (klass)->constructor = g_win32_directory_monitor_constructor;
146         G_OBJECT_CLASS (klass)->finalize = g_win32_directory_monitor_finalize;
147         G_FILE_MONITOR_CLASS (klass)->cancel = g_win32_directory_monitor_cancel;
148         
149         G_LOCAL_DIRECTORY_MONITOR_CLASS (klass)->mount_notify = FALSE;
150         G_LOCAL_DIRECTORY_MONITOR_CLASS (klass)->is_supported = g_win32_directory_monitor_is_supported;
151 }
152
153 static void g_win32_directory_monitor_init (GWin32DirectoryMonitor * self) 
154 {
155         self->priv = (GWin32DirectoryMonitorPrivate*)g_new0 (GWin32DirectoryMonitorPrivate, 1);
156         g_assert (self->priv != 0);
157         
158         self->priv->buffer_allocated_bytes = 32768;
159         self->priv->file_notify_buffer = g_new0 (gchar, self->priv->buffer_allocated_bytes);
160         g_assert (self->priv->file_notify_buffer);
161         
162         self->priv->self = G_FILE_MONITOR (self);
163 }