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