Added. Added. Added. Added.
[platform/upstream/glib.git] / gio / gfilemonitor.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: Alexander Larsson <alexl@redhat.com>
21  */
22
23 #include <config.h>
24 #include <string.h>
25
26 #include "gfilemonitor.h"
27 #include "gio-marshal.h"
28 #include "gvfs.h"
29 #include "glibintl.h"
30
31 #include "gioalias.h"
32
33 /**
34  * SECTION:gfilemonitor
35  * @short_description: File Monitor
36  * @see_also: #GDirectoryMonitor
37  *
38  * Monitors a file for changes.
39  * 
40  **/
41
42 enum {
43   CHANGED,
44   LAST_SIGNAL
45 };
46
47 G_DEFINE_ABSTRACT_TYPE (GFileMonitor, g_file_monitor, G_TYPE_OBJECT);
48
49 struct _GFileMonitorPrivate {
50   gboolean cancelled;
51   int rate_limit_msec;
52
53   /* Rate limiting change events */
54   guint32 last_sent_change_time; /* Some monitonic clock in msecs */
55   GFile *last_sent_change_file;
56   
57   guint send_delayed_change_timeout;
58
59   /* Virtual CHANGES_DONE_HINT emission */
60   GSource *virtual_changes_done_timeout;
61   GFile *virtual_changes_done_file;
62 };
63
64 #define DEFAULT_RATE_LIMIT_MSECS 800
65 #define DEFAULT_VIRTUAL_CHANGES_DONE_DELAY_SECS 2
66
67 static guint signals[LAST_SIGNAL] = { 0 };
68
69 static void
70 g_file_monitor_finalize (GObject *object)
71 {
72   GFileMonitor *monitor;
73
74   monitor = G_FILE_MONITOR (object);
75
76   if (monitor->priv->last_sent_change_file)
77     g_object_unref (monitor->priv->last_sent_change_file);
78
79   if (monitor->priv->send_delayed_change_timeout != 0)
80     g_source_remove (monitor->priv->send_delayed_change_timeout);
81
82   if (monitor->priv->virtual_changes_done_file)
83     g_object_unref (monitor->priv->virtual_changes_done_file);
84
85   if (monitor->priv->virtual_changes_done_timeout)
86     g_source_destroy (monitor->priv->virtual_changes_done_timeout);
87   
88   if (G_OBJECT_CLASS (g_file_monitor_parent_class)->finalize)
89     (*G_OBJECT_CLASS (g_file_monitor_parent_class)->finalize) (object);
90 }
91
92 static void
93 g_file_monitor_dispose (GObject *object)
94 {
95   GFileMonitor *monitor;
96   
97   monitor = G_FILE_MONITOR (object);
98
99   /* Make sure we cancel on last unref */
100   if (!monitor->priv->cancelled)
101     g_file_monitor_cancel (monitor);
102   
103   if (G_OBJECT_CLASS (g_file_monitor_parent_class)->dispose)
104     (*G_OBJECT_CLASS (g_file_monitor_parent_class)->dispose) (object);
105 }
106
107 static void
108 g_file_monitor_class_init (GFileMonitorClass *klass)
109 {
110   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
111   
112   g_type_class_add_private (klass, sizeof (GFileMonitorPrivate));
113   
114   gobject_class->finalize = g_file_monitor_finalize;
115   gobject_class->dispose = g_file_monitor_dispose;
116
117   /**
118    * GFileMonitor::changed:
119    * @monitor: a #GFileMonitor.
120    * @file: a #GFile.
121    * @other_file: a #GFile.
122    * @event_type: a #GFileMonitorEvent.
123    * 
124    * Emitted when a file has been changed. 
125    **/
126   signals[CHANGED] =
127     g_signal_new (I_("changed"),
128                   G_TYPE_FILE_MONITOR,
129                   G_SIGNAL_RUN_LAST,
130                   G_STRUCT_OFFSET (GFileMonitorClass, changed),
131                   NULL, NULL,
132                   _gio_marshal_VOID__OBJECT_OBJECT_INT,
133                   G_TYPE_NONE,3,
134                   G_TYPE_FILE,
135                   G_TYPE_FILE,
136                   G_TYPE_INT);
137 }
138
139 static void
140 g_file_monitor_init (GFileMonitor *monitor)
141 {
142   monitor->priv = G_TYPE_INSTANCE_GET_PRIVATE (monitor,
143                                                G_TYPE_FILE_MONITOR,
144                                                GFileMonitorPrivate);
145   monitor->priv->rate_limit_msec = DEFAULT_RATE_LIMIT_MSECS;
146 }
147
148 /**
149  * g_file_monitor_is_cancelled:
150  * @monitor: a #GFileMonitor
151  * 
152  * Returns whether the monitor is canceled.
153  *
154  * Returns: %TRUE if monitor is canceled. %FALSE otherwise.
155  **/
156 gboolean
157 g_file_monitor_is_cancelled (GFileMonitor *monitor)
158 {
159   g_return_val_if_fail (G_IS_FILE_MONITOR (monitor), FALSE);
160
161   return monitor->priv->cancelled;
162 }
163
164 /**
165  * g_file_monitor_cancel:
166  * @monitor: a #GFileMonitor.
167  * 
168  * Cancels a file monitor.
169  * 
170  * Returns: %TRUE if monitor was cancelled.
171  **/
172 gboolean
173 g_file_monitor_cancel (GFileMonitor* monitor)
174 {
175   GFileMonitorClass *klass;
176   
177   g_return_val_if_fail (G_IS_FILE_MONITOR (monitor), FALSE);
178   
179   if (monitor->priv->cancelled)
180     return TRUE;
181   
182   monitor->priv->cancelled = TRUE;
183   
184   klass = G_FILE_MONITOR_GET_CLASS (monitor);
185   return (* klass->cancel) (monitor);
186 }
187
188 /**
189  * g_file_monitor_set_rate_limit:
190  * @monitor: a #GFileMonitor.
191  * @limit_msecs: a integer with the limit in milliseconds to 
192  * poll for changes.
193  *
194  * Sets the rate limit to which the @monitor will poll for changes 
195  * on the device. 
196  * 
197  **/
198 void
199 g_file_monitor_set_rate_limit (GFileMonitor *monitor,
200                                int           limit_msecs)
201 {
202   g_return_if_fail (G_IS_FILE_MONITOR (monitor));
203
204   monitor->priv->rate_limit_msec = limit_msecs;
205 }
206
207 static guint32
208 get_time_msecs (void)
209 {
210   return g_thread_gettime() / (1000 * 1000);
211 }
212
213 static guint32
214 time_difference (guint32 from, guint32 to)
215 {
216   if (from > to)
217     return 0;
218   return to - from;
219 }
220
221 /* Change event rate limiting support: */
222
223 static void
224 update_last_sent_change (GFileMonitor *monitor, GFile *file, guint32 time_now)
225 {
226   if (monitor->priv->last_sent_change_file != file)
227     {
228       if (monitor->priv->last_sent_change_file)
229         {
230           g_object_unref (monitor->priv->last_sent_change_file);
231           monitor->priv->last_sent_change_file = NULL;
232         }
233       if (file)
234         monitor->priv->last_sent_change_file = g_object_ref (file);
235     }
236   
237   monitor->priv->last_sent_change_time = time_now;
238 }
239
240 static void
241 send_delayed_change_now (GFileMonitor *monitor)
242 {
243   if (monitor->priv->send_delayed_change_timeout)
244     {
245       g_signal_emit (monitor, signals[CHANGED], 0,
246                      monitor->priv->last_sent_change_file, NULL,
247                      G_FILE_MONITOR_EVENT_CHANGED);
248       
249       g_source_remove (monitor->priv->send_delayed_change_timeout);
250       monitor->priv->send_delayed_change_timeout = 0;
251
252       /* Same file, new last_sent time */
253       monitor->priv->last_sent_change_time = get_time_msecs ();
254     }
255 }
256
257 static gboolean
258 delayed_changed_event_timeout (gpointer data)
259 {
260   GFileMonitor *monitor = data;
261
262   send_delayed_change_now (monitor);
263   
264   return FALSE;
265 }
266
267 static void
268 schedule_delayed_change (GFileMonitor *monitor, GFile *file, guint32 delay_msec)
269 {
270   if (monitor->priv->send_delayed_change_timeout == 0) /* Only set the timeout once */
271     {
272       monitor->priv->send_delayed_change_timeout = 
273         g_timeout_add (delay_msec, delayed_changed_event_timeout, monitor);
274     }
275 }
276
277 static void
278 cancel_delayed_change (GFileMonitor *monitor)
279 {
280   if (monitor->priv->send_delayed_change_timeout != 0)
281     {
282       g_source_remove (monitor->priv->send_delayed_change_timeout);
283       monitor->priv->send_delayed_change_timeout = 0;
284     }
285 }
286
287 /* Virtual changes_done_hint support: */
288
289 static void
290 send_virtual_changes_done_now (GFileMonitor *monitor)
291 {
292   if (monitor->priv->virtual_changes_done_timeout)
293     {
294       g_signal_emit (monitor, signals[CHANGED], 0,
295                      monitor->priv->virtual_changes_done_file, NULL,
296                      G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT);
297       
298       g_source_destroy (monitor->priv->virtual_changes_done_timeout);
299       monitor->priv->virtual_changes_done_timeout = NULL;
300
301       g_object_unref (monitor->priv->virtual_changes_done_file);
302       monitor->priv->virtual_changes_done_file = NULL;
303     }
304 }
305
306 static gboolean
307 virtual_changes_done_timeout (gpointer data)
308 {
309   GFileMonitor *monitor = data;
310
311   send_virtual_changes_done_now (monitor);
312   
313   return FALSE;
314 }
315
316 static void
317 schedule_virtual_change_done (GFileMonitor *monitor, GFile *file)
318 {
319   GSource *source;
320   
321   source = g_timeout_source_new_seconds (DEFAULT_VIRTUAL_CHANGES_DONE_DELAY_SECS);
322   
323   g_source_set_callback (source, virtual_changes_done_timeout, monitor, NULL);
324   g_source_attach (source, NULL);
325   monitor->priv->virtual_changes_done_timeout = source;
326   monitor->priv->virtual_changes_done_file = g_object_ref (file);
327   g_source_unref (source);
328 }
329
330 static void
331 cancel_virtual_changes_done (GFileMonitor *monitor)
332 {
333   if (monitor->priv->virtual_changes_done_timeout)
334     {
335       g_source_destroy (monitor->priv->virtual_changes_done_timeout);
336       monitor->priv->virtual_changes_done_timeout = NULL;
337       
338       g_object_unref (monitor->priv->virtual_changes_done_file);
339       monitor->priv->virtual_changes_done_file = NULL;
340     }
341 }
342
343 /**
344  * g_file_monitor_emit_event:
345  * @monitor: a #GFileMonitor.
346  * @file: a #GFile.
347  * @other_file: a #GFile.
348  * @event_type: a #GFileMonitorEvent
349  * 
350  * Emits a file monitor event. This is mainly necessary for implementations
351  * of GFileMonitor.
352  * 
353  **/
354 void
355 g_file_monitor_emit_event (GFileMonitor *monitor,
356                            GFile *file,
357                            GFile *other_file,
358                            GFileMonitorEvent event_type)
359 {
360   guint32 time_now, since_last;
361   gboolean emit_now;
362
363   g_return_if_fail (G_IS_FILE_MONITOR (monitor));
364   g_return_if_fail (G_IS_FILE (file));
365
366   if (event_type != G_FILE_MONITOR_EVENT_CHANGED)
367     {
368       send_delayed_change_now (monitor);
369       update_last_sent_change (monitor, NULL, 0);
370       if (event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT)
371         cancel_virtual_changes_done (monitor);
372       else
373         send_virtual_changes_done_now (monitor);
374       g_signal_emit (monitor, signals[CHANGED], 0, file, other_file, event_type);
375     }
376   else
377     {
378       time_now = get_time_msecs ();
379       emit_now = TRUE;
380       
381       if (monitor->priv->last_sent_change_file)
382         {
383           since_last = time_difference (monitor->priv->last_sent_change_time, time_now);
384           if (since_last < monitor->priv->rate_limit_msec)
385             {
386               /* We ignore this change, but arm a timer so that we can fire it later if we
387                  don't get any other events (that kill this timeout) */
388               emit_now = FALSE;
389               schedule_delayed_change (monitor, file,
390                                        monitor->priv->rate_limit_msec - since_last);
391             }
392         }
393       
394       if (emit_now)
395         {
396           g_signal_emit (monitor, signals[CHANGED], 0, file, other_file, event_type);
397           
398           cancel_delayed_change (monitor);
399           update_last_sent_change (monitor, file, time_now);
400         }
401
402       /* Schedule a virtual change done. This is removed if we get a real one, and
403          postponed if we get more change events. */
404       cancel_virtual_changes_done (monitor);
405       schedule_virtual_change_done (monitor, file);
406     }
407 }
408
409 #define __G_FILE_MONITOR_C__
410 #include "gioaliasdef.c"