c8d12ce8fa0fc465410cf548ce0ed979ff8e216f
[bpt/emacs.git] / src / gfilenotify.c
1 /* Filesystem notifications support with glib API.
2 Copyright (C) 2013 Free Software Foundation, Inc.
3
4 This file is part of GNU Emacs.
5
6 GNU Emacs is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 GNU Emacs is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
18
19 #include <config.h>
20
21 #ifdef HAVE_GFILENOTIFY
22 #include <stdio.h>
23 #include <gio/gio.h>
24 #include "lisp.h"
25 #include "coding.h"
26 #include "frame.h"
27 #include "termhooks.h"
28 #include "keyboard.h"
29 #include "process.h"
30
31 \f
32 /* Subroutines. */
33 static Lisp_Object Qgfile_add_watch;
34 static Lisp_Object Qgfile_rm_watch;
35
36 /* Filter objects. */
37 static Lisp_Object Qwatch_mounts; /* G_FILE_MONITOR_WATCH_MOUNTS */
38 static Lisp_Object Qsend_moved; /* G_FILE_MONITOR_SEND_MOVED */
39
40 /* Event types. */
41 static Lisp_Object Qchanged; /* G_FILE_MONITOR_EVENT_CHANGED */
42 static Lisp_Object Qchanges_done_hint; /* G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT */
43 static Lisp_Object Qdeleted; /* G_FILE_MONITOR_EVENT_DELETED */
44 static Lisp_Object Qcreated; /* G_FILE_MONITOR_EVENT_CREATED */
45 static Lisp_Object Qattribute_changed; /* G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED */
46 static Lisp_Object Qpre_unmount; /* G_FILE_MONITOR_EVENT_PRE_UNMOUNT */
47 static Lisp_Object Qunmounted; /* G_FILE_MONITOR_EVENT_UNMOUNTED */
48 static Lisp_Object Qmoved; /* G_FILE_MONITOR_EVENT_MOVED */
49
50 static Lisp_Object watch_list;
51
52 /* This is the callback function for arriving signals from
53 g_file_monitor. It shall create a Lisp event, and put it into
54 Emacs input queue. */
55 static gboolean
56 dir_monitor_callback (GFileMonitor *monitor,
57 GFile *file,
58 GFile *other_file,
59 GFileMonitorEvent event_type,
60 gpointer user_data)
61 {
62 Lisp_Object symbol, monitor_object, watch_object;
63 char *name = g_file_get_parse_name (file);
64 char *oname = other_file ? g_file_get_parse_name (other_file) : NULL;
65
66 /* Determine event symbol. */
67 switch (event_type)
68 {
69 case G_FILE_MONITOR_EVENT_CHANGED:
70 symbol = Qchanged;
71 break;
72 case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
73 symbol = Qchanges_done_hint;
74 break;
75 case G_FILE_MONITOR_EVENT_DELETED:
76 symbol = Qdeleted;
77 break;
78 case G_FILE_MONITOR_EVENT_CREATED:
79 symbol = Qcreated;
80 break;
81 case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED:
82 symbol = Qattribute_changed;
83 break;
84 case G_FILE_MONITOR_EVENT_PRE_UNMOUNT:
85 symbol = Qpre_unmount;
86 break;
87 case G_FILE_MONITOR_EVENT_UNMOUNTED:
88 symbol = Qunmounted;
89 break;
90 case G_FILE_MONITOR_EVENT_MOVED:
91 symbol = Qmoved;
92 break;
93 default:
94 goto cleanup;
95 }
96
97 /* Determine callback function. */
98 monitor_object = XIL ((intptr_t) monitor);
99 eassert (INTEGERP (monitor_object));
100 watch_object = assq_no_quit (monitor_object, watch_list);
101
102 if (CONSP (watch_object))
103 {
104 /* Construct an event. */
105 struct input_event event;
106 Lisp_Object otail = oname ? list1 (build_string (oname)) : Qnil;
107 EVENT_INIT (event);
108 event.kind = FILE_NOTIFY_EVENT;
109 event.frame_or_window = Qnil;
110 event.arg = list2 (Fcons (monitor_object,
111 Fcons (symbol,
112 Fcons (build_string (name),
113 otail))),
114 XCDR (watch_object));
115
116 /* Store it into the input event queue. */
117 kbd_buffer_store_event (&event);
118 }
119
120 /* Cleanup. */
121 cleanup:
122 g_free (name);
123 g_free (oname);
124
125 return TRUE;
126 }
127
128 DEFUN ("gfile-add-watch", Fgfile_add_watch, Sgfile_add_watch, 3, 3, 0,
129 doc: /* Add a watch for filesystem events pertaining to FILE.
130
131 This arranges for filesystem events pertaining to FILE to be reported
132 to Emacs. Use `gfile-rm-watch' to cancel the watch.
133
134 Value is a descriptor for the added watch. If the file cannot be
135 watched for some reason, this function signals a `file-error' error.
136
137 FLAGS is a list of conditions to set what will be watched for. It can
138 include the following symbols:
139
140 'watch-mounts' -- watch for mount events
141 'send-moved' -- pair 'deleted' and 'created' events caused by file
142 renames (moves) and send a single 'event-moved'
143 event instead
144
145 When any event happens, Emacs will call the CALLBACK function passing
146 it a single argument EVENT, which is of the form
147
148 (DESCRIPTOR ACTION FILE [FILE1])
149
150 DESCRIPTOR is the same object as the one returned by this function.
151 ACTION is the description of the event. It could be any one of the
152 following:
153
154 'changed' -- FILE has changed
155 'changes-done-hint' -- a hint that this was probably the last change
156 in a set of changes
157 'deleted' -- FILE was deleted
158 'created' -- FILE was created
159 'attribute-changed' -- a FILE attribute was changed
160 'pre-unmount' -- the FILE location will soon be unmounted
161 'unmounted' -- the FILE location was unmounted
162 'moved' -- FILE was moved to FILE1
163
164 FILE is the name of the file whose event is being reported. FILE1
165 will be reported only in case of the 'moved' event. */)
166 (Lisp_Object file, Lisp_Object flags, Lisp_Object callback)
167 {
168 Lisp_Object watch_descriptor, watch_object;
169 GFile *gfile;
170 GFileMonitor *monitor;
171 GFileMonitorFlags gflags = G_FILE_MONITOR_NONE;
172
173 /* Check parameters. */
174 CHECK_STRING (file);
175 file = Fdirectory_file_name (Fexpand_file_name (file, Qnil));
176 if (NILP (Ffile_exists_p (file)))
177 report_file_error ("File does not exists", Fcons (file, Qnil));
178
179 CHECK_LIST (flags);
180
181 if (!FUNCTIONP (callback))
182 wrong_type_argument (Qinvalid_function, callback);
183
184 /* Create GFile name. */
185 gfile = g_file_new_for_path (SSDATA (ENCODE_FILE (file)));
186
187 /* Assemble flags. */
188 if (!NILP (Fmember (Qwatch_mounts, flags)))
189 gflags |= G_FILE_MONITOR_WATCH_MOUNTS;
190 if (!NILP (Fmember (Qsend_moved, flags)))
191 gflags |= G_FILE_MONITOR_SEND_MOVED;
192
193 /* Enable watch. */
194 monitor = g_file_monitor (gfile, gflags, NULL, NULL);
195 if (! monitor)
196 xsignal2 (Qfile_error, build_string ("Cannot watch file"), file);
197
198 /* On all known glib platforms, converting MONITOR directly to a
199 Lisp_Object value results is a Lisp integer, which is safe. This
200 assumption is dicey, though, so check it now. */
201 watch_descriptor = XIL ((intptr_t) monitor);
202 if (! INTEGERP (watch_descriptor))
203 {
204 g_object_unref (monitor);
205 xsignal2 (Qfile_error, build_string ("Unsupported file watcher"), file);
206 }
207
208 g_signal_connect (monitor, "changed",
209 (GCallback) dir_monitor_callback, NULL);
210
211 /* Store watch object in watch list. */
212 watch_object = Fcons (watch_descriptor, callback);
213 watch_list = Fcons (watch_object, watch_list);
214
215 return watch_descriptor;
216 }
217
218 DEFUN ("gfile-rm-watch", Fgfile_rm_watch, Sgfile_rm_watch, 1, 1, 0,
219 doc: /* Remove an existing WATCH-DESCRIPTOR.
220
221 WATCH-DESCRIPTOR should be an object returned by `gfile-add-watch'. */)
222 (Lisp_Object watch_descriptor)
223 {
224 intptr_t int_monitor;
225 GFileMonitor *monitor;
226 Lisp_Object watch_object = assq_no_quit (watch_descriptor, watch_list);
227
228 if (! CONSP (watch_object))
229 xsignal2 (Qfile_error, build_string ("Not a watch descriptor"),
230 watch_descriptor);
231
232 eassert (INTEGERP (watch_descriptor));
233 int_monitor = XLI (watch_descriptor);
234 monitor = (GFileMonitor *) int_monitor;
235 if (!g_file_monitor_cancel (monitor))
236 xsignal2 (Qfile_error, build_string ("Could not rm watch"),
237 watch_descriptor);
238
239 /* Remove watch descriptor from watch list. */
240 watch_list = Fdelq (watch_object, watch_list);
241
242 /* Cleanup. */
243 g_object_unref (monitor);
244
245 return Qt;
246 }
247
248 \f
249 void
250 globals_of_gfilenotify (void)
251 {
252 g_type_init ();
253 watch_list = Qnil;
254 }
255
256 void
257 syms_of_gfilenotify (void)
258 {
259
260 DEFSYM (Qgfile_add_watch, "gfile-add-watch");
261 defsubr (&Sgfile_add_watch);
262
263 DEFSYM (Qgfile_rm_watch, "gfile-rm-watch");
264 defsubr (&Sgfile_rm_watch);
265
266 DEFSYM (Qwatch_mounts, "watch-mounts");
267 DEFSYM (Qsend_moved, "send-moved");
268 DEFSYM (Qchanged, "changed");
269 DEFSYM (Qchanges_done_hint, "changes-done-hint");
270 DEFSYM (Qdeleted, "deleted");
271 DEFSYM (Qcreated, "created");
272 DEFSYM (Qattribute_changed, "attribute-changed");
273 DEFSYM (Qpre_unmount, "pre-unmount");
274 DEFSYM (Qunmounted, "unmounted");
275 DEFSYM (Qmoved, "moved");
276
277 staticpro (&watch_list);
278
279 Fprovide (intern_c_string ("gfilenotify"), Qnil);
280
281 }
282
283 #endif /* HAVE_GFILENOTIFY */