Some more property work
[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 monotonic 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 enum {
65   PROP_0,
66   PROP_RATE_LIMIT,
67   PROP_CANCELLED
68 };
69
70 static void
71 g_file_monitor_set_property (GObject      *object,
72                              guint         prop_id,
73                              const GValue *value,
74                              GParamSpec   *pspec)
75 {
76   GFileMonitor *monitor;
77
78   monitor = G_FILE_MONITOR (object);
79
80   switch (prop_id)
81     {
82     case PROP_RATE_LIMIT:
83       g_file_monitor_set_rate_limit (monitor, g_value_get_int (value));
84       break;
85
86     default:
87       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
88       break;
89     }
90 }
91
92 static void
93 g_file_monitor_get_property (GObject    *object,
94                              guint       prop_id,
95                              GValue     *value,
96                              GParamSpec *pspec)
97 {
98   GFileMonitor *monitor;
99   GFileMonitorPrivate *priv;
100
101   monitor = G_FILE_MONITOR (object);
102   priv = monitor->priv;
103
104   switch (prop_id)
105     {
106     case PROP_RATE_LIMIT:
107       g_value_set_int (value, priv->rate_limit_msec);
108       break;
109
110     case PROP_CANCELLED:
111       g_value_set_boolean (value, priv->cancelled);
112       break;
113
114     default:
115       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
116       break;
117     }
118 }
119
120 #define DEFAULT_RATE_LIMIT_MSECS 800
121 #define DEFAULT_VIRTUAL_CHANGES_DONE_DELAY_SECS 2
122
123 static guint signals[LAST_SIGNAL] = { 0 };
124
125
126 static void
127 g_file_monitor_finalize (GObject *object)
128 {
129   GFileMonitor *monitor;
130
131   monitor = G_FILE_MONITOR (object);
132
133   if (monitor->priv->last_sent_change_file)
134     g_object_unref (monitor->priv->last_sent_change_file);
135
136   if (monitor->priv->send_delayed_change_timeout != 0)
137     g_source_remove (monitor->priv->send_delayed_change_timeout);
138
139   if (monitor->priv->virtual_changes_done_file)
140     g_object_unref (monitor->priv->virtual_changes_done_file);
141
142   if (monitor->priv->virtual_changes_done_timeout)
143     g_source_destroy (monitor->priv->virtual_changes_done_timeout);
144   
145   if (G_OBJECT_CLASS (g_file_monitor_parent_class)->finalize)
146     (*G_OBJECT_CLASS (g_file_monitor_parent_class)->finalize) (object);
147 }
148
149 static void
150 g_file_monitor_dispose (GObject *object)
151 {
152   GFileMonitor *monitor;
153   
154   monitor = G_FILE_MONITOR (object);
155
156   /* Make sure we cancel on last unref */
157   if (!monitor->priv->cancelled)
158     g_file_monitor_cancel (monitor);
159   
160   if (G_OBJECT_CLASS (g_file_monitor_parent_class)->dispose)
161     (*G_OBJECT_CLASS (g_file_monitor_parent_class)->dispose) (object);
162 }
163
164 static void
165 g_file_monitor_class_init (GFileMonitorClass *klass)
166 {
167   GObjectClass *object_class;
168   
169   g_type_class_add_private (klass, sizeof (GFileMonitorPrivate));
170   
171   object_class = G_OBJECT_CLASS (klass);
172   object_class->finalize = g_file_monitor_finalize;
173   object_class->dispose = g_file_monitor_dispose;
174   object_class->get_property = g_file_monitor_get_property;
175   object_class->set_property = g_file_monitor_set_property;
176
177   /**
178    * GFileMonitor::changed:
179    * @monitor: a #GFileMonitor.
180    * @file: a #GFile.
181    * @other_file: a #GFile.
182    * @event_type: a #GFileMonitorEvent.
183    * 
184    * Emitted when a file has been changed. 
185    **/
186   signals[CHANGED] =
187     g_signal_new (I_("changed"),
188                   G_TYPE_FILE_MONITOR,
189                   G_SIGNAL_RUN_LAST,
190                   G_STRUCT_OFFSET (GFileMonitorClass, changed),
191                   NULL, NULL,
192                   _gio_marshal_VOID__OBJECT_OBJECT_INT,
193                   G_TYPE_NONE, 3,
194                   G_TYPE_FILE, G_TYPE_FILE, G_TYPE_INT); 
195
196   g_object_class_install_property (object_class,
197                                    PROP_RATE_LIMIT,
198                                    g_param_spec_int ("rate-limit",
199                                                      P_("Rate limit"),
200                                                      P_("The limit of the monitor to watch for changes, in milliseconds"),
201                                                      0, G_MAXINT,
202                                                      DEFAULT_RATE_LIMIT_MSECS,
203                                                      G_PARAM_READWRITE|
204                                                      G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB));
205
206   g_object_class_install_property (object_class,
207                                    PROP_CANCELLED,
208                                    g_param_spec_boolean ("cancelled",
209                                                          P_("Cancelled"),
210                                                          P_("Whether the monitor has been cancelled"),
211                                                          FALSE,
212                                                          G_PARAM_READABLE|
213                                                          G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB));
214 }
215
216 static void
217 g_file_monitor_init (GFileMonitor *monitor)
218 {
219   monitor->priv = G_TYPE_INSTANCE_GET_PRIVATE (monitor,
220                                                G_TYPE_FILE_MONITOR,
221                                                GFileMonitorPrivate);
222   monitor->priv->rate_limit_msec = DEFAULT_RATE_LIMIT_MSECS;
223 }
224
225 /**
226  * g_file_monitor_is_cancelled:
227  * @monitor: a #GFileMonitor
228  * 
229  * Returns whether the monitor is canceled.
230  *
231  * Returns: %TRUE if monitor is canceled. %FALSE otherwise.
232  **/
233 gboolean
234 g_file_monitor_is_cancelled (GFileMonitor *monitor)
235 {
236   g_return_val_if_fail (G_IS_FILE_MONITOR (monitor), FALSE);
237
238   return monitor->priv->cancelled;
239 }
240
241 /**
242  * g_file_monitor_cancel:
243  * @monitor: a #GFileMonitor.
244  * 
245  * Cancels a file monitor.
246  * 
247  * Returns: %TRUE if monitor was cancelled.
248  **/
249 gboolean
250 g_file_monitor_cancel (GFileMonitor* monitor)
251 {
252   GFileMonitorClass *klass;
253   
254   g_return_val_if_fail (G_IS_FILE_MONITOR (monitor), FALSE);
255   
256   if (monitor->priv->cancelled)
257     return TRUE;
258   
259   monitor->priv->cancelled = TRUE;
260   g_object_notify (G_OBJECT (monitor), "cancelled");
261
262   klass = G_FILE_MONITOR_GET_CLASS (monitor);
263   return (* klass->cancel) (monitor);
264 }
265
266 /**
267  * g_file_monitor_set_rate_limit:
268  * @monitor: a #GFileMonitor.
269  * @limit_msecs: a integer with the limit in milliseconds to 
270  * poll for changes.
271  *
272  * Sets the rate limit to which the @monitor will poll for changes 
273  * on the device. 
274  * 
275  **/
276 void
277 g_file_monitor_set_rate_limit (GFileMonitor *monitor,
278                                int           limit_msecs)
279 {
280   GFileMonitorPrivate *priv;
281   g_return_if_fail (G_IS_FILE_MONITOR (monitor));
282   priv = monitor->priv;
283   if (priv->rate_limit_msec != limit_msecs)
284     {
285       monitor->priv->rate_limit_msec = limit_msecs;
286       g_object_notify (G_OBJECT (monitor), "rate-limit");
287     }
288 }
289
290 static guint32
291 get_time_msecs (void)
292 {
293   return g_thread_gettime() / (1000 * 1000);
294 }
295
296 static guint32
297 time_difference (guint32 from, guint32 to)
298 {
299   if (from > to)
300     return 0;
301   return to - from;
302 }
303
304 /* Change event rate limiting support: */
305
306 static void
307 update_last_sent_change (GFileMonitor *monitor, GFile *file, guint32 time_now)
308 {
309   if (monitor->priv->last_sent_change_file != file)
310     {
311       if (monitor->priv->last_sent_change_file)
312         {
313           g_object_unref (monitor->priv->last_sent_change_file);
314           monitor->priv->last_sent_change_file = NULL;
315         }
316       if (file)
317         monitor->priv->last_sent_change_file = g_object_ref (file);
318     }
319   
320   monitor->priv->last_sent_change_time = time_now;
321 }
322
323 static void
324 send_delayed_change_now (GFileMonitor *monitor)
325 {
326   if (monitor->priv->send_delayed_change_timeout)
327     {
328       g_signal_emit (monitor, signals[CHANGED], 0,
329                      monitor->priv->last_sent_change_file, NULL,
330                      G_FILE_MONITOR_EVENT_CHANGED);
331       
332       g_source_remove (monitor->priv->send_delayed_change_timeout);
333       monitor->priv->send_delayed_change_timeout = 0;
334
335       /* Same file, new last_sent time */
336       monitor->priv->last_sent_change_time = get_time_msecs ();
337     }
338 }
339
340 static gboolean
341 delayed_changed_event_timeout (gpointer data)
342 {
343   GFileMonitor *monitor = data;
344
345   send_delayed_change_now (monitor);
346   
347   return FALSE;
348 }
349
350 static void
351 schedule_delayed_change (GFileMonitor *monitor, GFile *file, guint32 delay_msec)
352 {
353   if (monitor->priv->send_delayed_change_timeout == 0) /* Only set the timeout once */
354     {
355       monitor->priv->send_delayed_change_timeout = 
356         g_timeout_add (delay_msec, delayed_changed_event_timeout, monitor);
357     }
358 }
359
360 static void
361 cancel_delayed_change (GFileMonitor *monitor)
362 {
363   if (monitor->priv->send_delayed_change_timeout != 0)
364     {
365       g_source_remove (monitor->priv->send_delayed_change_timeout);
366       monitor->priv->send_delayed_change_timeout = 0;
367     }
368 }
369
370 /* Virtual changes_done_hint support: */
371
372 static void
373 send_virtual_changes_done_now (GFileMonitor *monitor)
374 {
375   if (monitor->priv->virtual_changes_done_timeout)
376     {
377       g_signal_emit (monitor, signals[CHANGED], 0,
378                      monitor->priv->virtual_changes_done_file, NULL,
379                      G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT);
380       
381       g_source_destroy (monitor->priv->virtual_changes_done_timeout);
382       monitor->priv->virtual_changes_done_timeout = NULL;
383
384       g_object_unref (monitor->priv->virtual_changes_done_file);
385       monitor->priv->virtual_changes_done_file = NULL;
386     }
387 }
388
389 static gboolean
390 virtual_changes_done_timeout (gpointer data)
391 {
392   GFileMonitor *monitor = data;
393
394   send_virtual_changes_done_now (monitor);
395   
396   return FALSE;
397 }
398
399 static void
400 schedule_virtual_change_done (GFileMonitor *monitor, GFile *file)
401 {
402   GSource *source;
403   
404   source = g_timeout_source_new_seconds (DEFAULT_VIRTUAL_CHANGES_DONE_DELAY_SECS);
405   
406   g_source_set_callback (source, virtual_changes_done_timeout, monitor, NULL);
407   g_source_attach (source, NULL);
408   monitor->priv->virtual_changes_done_timeout = source;
409   monitor->priv->virtual_changes_done_file = g_object_ref (file);
410   g_source_unref (source);
411 }
412
413 static void
414 cancel_virtual_changes_done (GFileMonitor *monitor)
415 {
416   if (monitor->priv->virtual_changes_done_timeout)
417     {
418       g_source_destroy (monitor->priv->virtual_changes_done_timeout);
419       monitor->priv->virtual_changes_done_timeout = NULL;
420       
421       g_object_unref (monitor->priv->virtual_changes_done_file);
422       monitor->priv->virtual_changes_done_file = NULL;
423     }
424 }
425
426 /**
427  * g_file_monitor_emit_event:
428  * @monitor: a #GFileMonitor.
429  * @file: a #GFile.
430  * @other_file: a #GFile.
431  * @event_type: a #GFileMonitorEvent
432  * 
433  * Emits a file monitor event. This is mainly necessary for implementations
434  * of GFileMonitor.
435  * 
436  **/
437 void
438 g_file_monitor_emit_event (GFileMonitor *monitor,
439                            GFile *file,
440                            GFile *other_file,
441                            GFileMonitorEvent event_type)
442 {
443   guint32 time_now, since_last;
444   gboolean emit_now;
445
446   g_return_if_fail (G_IS_FILE_MONITOR (monitor));
447   g_return_if_fail (G_IS_FILE (file));
448
449   if (event_type != G_FILE_MONITOR_EVENT_CHANGED)
450     {
451       send_delayed_change_now (monitor);
452       update_last_sent_change (monitor, NULL, 0);
453       if (event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT)
454         cancel_virtual_changes_done (monitor);
455       else
456         send_virtual_changes_done_now (monitor);
457       g_signal_emit (monitor, signals[CHANGED], 0, file, other_file, event_type);
458     }
459   else
460     {
461       time_now = get_time_msecs ();
462       emit_now = TRUE;
463       
464       if (monitor->priv->last_sent_change_file)
465         {
466           since_last = time_difference (monitor->priv->last_sent_change_time, time_now);
467           if (since_last < monitor->priv->rate_limit_msec)
468             {
469               /* We ignore this change, but arm a timer so that we can fire it later if we
470                  don't get any other events (that kill this timeout) */
471               emit_now = FALSE;
472               schedule_delayed_change (monitor, file,
473                                        monitor->priv->rate_limit_msec - since_last);
474             }
475         }
476       
477       if (emit_now)
478         {
479           g_signal_emit (monitor, signals[CHANGED], 0, file, other_file, event_type);
480           
481           cancel_delayed_change (monitor);
482           update_last_sent_change (monitor, file, time_now);
483         }
484
485       /* Schedule a virtual change done. This is removed if we get a real one, and
486          postponed if we get more change events. */
487       cancel_virtual_changes_done (monitor);
488       schedule_virtual_change_done (monitor, file);
489     }
490 }
491
492 #define __G_FILE_MONITOR_C__
493 #include "gioaliasdef.c"