]> code.delx.au - gnu-emacs/blob - src/gfilenotify.c
Merge branch 'master' of git.sv.gnu.org:/srv/git/emacs
[gnu-emacs] / src / gfilenotify.c
1 /* Filesystem notifications support with glib API.
2 Copyright (C) 2013-2015 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 /* This is a list, elements are triples (DESCRIPTOR FILE FLAGS CALLBACK) */
33 static Lisp_Object watch_list;
34
35 /* This is the callback function for arriving signals from
36 g_file_monitor. It shall create a Lisp event, and put it into
37 Emacs input queue. */
38 static gboolean
39 dir_monitor_callback (GFileMonitor *monitor,
40 GFile *file,
41 GFile *other_file,
42 GFileMonitorEvent event_type,
43 gpointer user_data)
44 {
45 Lisp_Object symbol, monitor_object, watch_object, flags;
46 char *name = g_file_get_parse_name (file);
47 char *oname = other_file ? g_file_get_parse_name (other_file) : NULL;
48
49 /* Determine event symbol. */
50 switch (event_type)
51 {
52 case G_FILE_MONITOR_EVENT_CHANGED:
53 symbol = Qchanged;
54 break;
55 case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
56 symbol = Qchanges_done_hint;
57 break;
58 case G_FILE_MONITOR_EVENT_DELETED:
59 symbol = Qdeleted;
60 break;
61 case G_FILE_MONITOR_EVENT_CREATED:
62 symbol = Qcreated;
63 break;
64 case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED:
65 symbol = Qattribute_changed;
66 break;
67 case G_FILE_MONITOR_EVENT_PRE_UNMOUNT:
68 symbol = Qpre_unmount;
69 break;
70 case G_FILE_MONITOR_EVENT_UNMOUNTED:
71 symbol = Qunmounted;
72 break;
73 case G_FILE_MONITOR_EVENT_MOVED:
74 symbol = Qmoved;
75 break;
76 default:
77 goto cleanup;
78 }
79
80 /* Determine callback function. */
81 monitor_object = make_pointer_integer (monitor);
82 eassert (INTEGERP (monitor_object));
83 watch_object = assq_no_quit (monitor_object, watch_list);
84
85 if (CONSP (watch_object))
86 {
87 struct input_event event;
88 Lisp_Object otail = oname ? list1 (build_string (oname)) : Qnil;
89
90 /* Check, whether event_type is expected. */
91 flags = XCAR (XCDR (XCDR (watch_object)));
92 if ((!NILP (Fmember (Qchange, flags)) &&
93 !NILP (Fmember (symbol, list5 (Qchanged, Qchanges_done_hint,
94 Qdeleted, Qcreated, Qmoved)))) ||
95 (!NILP (Fmember (Qattribute_change, flags)) &&
96 ((EQ (symbol, Qattribute_changed)))))
97 {
98 /* Construct an event. */
99 EVENT_INIT (event);
100 event.kind = FILE_NOTIFY_EVENT;
101 event.frame_or_window = Qnil;
102 event.arg = list2 (Fcons (monitor_object,
103 Fcons (symbol,
104 Fcons (build_string (name),
105 otail))),
106 XCAR (XCDR (XCDR (XCDR (watch_object)))));
107
108 /* Store it into the input event queue. */
109 kbd_buffer_store_event (&event);
110 // XD_DEBUG_MESSAGE ("%s", XD_OBJECT_TO_STRING (event.arg));
111 }
112
113 /* Cancel monitor if file or directory is deleted. */
114 if (!NILP (Fmember (symbol, list2 (Qdeleted, Qmoved))) &&
115 !g_file_monitor_is_cancelled (monitor))
116 g_file_monitor_cancel (monitor);
117 }
118
119 /* Cleanup. */
120 cleanup:
121 g_free (name);
122 g_free (oname);
123
124 return TRUE;
125 }
126
127 DEFUN ("gfile-add-watch", Fgfile_add_watch, Sgfile_add_watch, 3, 3, 0,
128 doc: /* Add a watch for filesystem events pertaining to FILE.
129
130 This arranges for filesystem events pertaining to FILE to be reported
131 to Emacs. Use `gfile-rm-watch' to cancel the watch.
132
133 Value is a descriptor for the added watch. If the file cannot be
134 watched for some reason, this function signals a `file-notify-error' error.
135
136 FLAGS is a list of conditions to set what will be watched for. It can
137 include the following symbols:
138
139 `change' -- watch for file changes
140 `attribute-change' -- watch for file attributes changes, like
141 permissions or modification time
142 `watch-mounts' -- watch for mount events
143 `send-moved' -- pair `deleted' and `created' events caused by
144 file renames and send a single `renamed' event
145 instead
146
147 When any event happens, Emacs will call the CALLBACK function passing
148 it a single argument EVENT, which is of the form
149
150 (DESCRIPTOR ACTION FILE [FILE1])
151
152 DESCRIPTOR is the same object as the one returned by this function.
153 ACTION is the description of the event. It could be any one of the
154 following:
155
156 `changed' -- FILE has changed
157 `changes-done-hint' -- a hint that this was probably the last change
158 in a set of changes
159 `deleted' -- FILE was deleted
160 `created' -- FILE was created
161 `attribute-changed' -- a FILE attribute was changed
162 `pre-unmount' -- the FILE location will soon be unmounted
163 `unmounted' -- the FILE location was unmounted
164 `moved' -- FILE was moved to FILE1
165
166 FILE is the name of the file whose event is being reported. FILE1
167 will be reported only in case of the `moved' event. */)
168 (Lisp_Object file, Lisp_Object flags, Lisp_Object callback)
169 {
170 Lisp_Object watch_object;
171 GFile *gfile;
172 GFileMonitor *monitor;
173 GFileMonitorFlags gflags = G_FILE_MONITOR_NONE;
174 GError *gerror = NULL;
175
176 /* Check parameters. */
177 CHECK_STRING (file);
178 file = Fdirectory_file_name (Fexpand_file_name (file, Qnil));
179 if (NILP (Ffile_exists_p (file)))
180 report_file_error ("File does not exist", file);
181
182 CHECK_LIST (flags);
183
184 if (!FUNCTIONP (callback))
185 wrong_type_argument (Qinvalid_function, callback);
186
187 /* Create GFile name. */
188 gfile = g_file_new_for_path (SSDATA (ENCODE_FILE (file)));
189
190 /* Assemble flags. */
191 if (!NILP (Fmember (Qwatch_mounts, flags)))
192 gflags |= G_FILE_MONITOR_WATCH_MOUNTS;
193 if (!NILP (Fmember (Qsend_moved, flags)))
194 gflags |= G_FILE_MONITOR_SEND_MOVED;
195
196 /* Enable watch. */
197 monitor = g_file_monitor (gfile, gflags, NULL, &gerror);
198 g_object_unref (gfile);
199 if (gerror)
200 {
201 char msg[1024];
202 strcpy (msg, gerror->message);
203 g_error_free (gerror);
204 xsignal1 (Qfile_notify_error, build_string (msg));
205 }
206 if (! monitor)
207 xsignal2 (Qfile_notify_error, build_string ("Cannot watch file"), file);
208
209 Lisp_Object watch_descriptor = make_pointer_integer (monitor);
210
211 /* Check the dicey assumption that make_pointer_integer is safe. */
212 if (! INTEGERP (watch_descriptor))
213 {
214 g_object_unref (monitor);
215 xsignal2 (Qfile_notify_error, build_string ("Unsupported file watcher"),
216 file);
217 }
218
219 /* The default rate limit is 800 msec. We adapt this. */
220 g_file_monitor_set_rate_limit (monitor, 100);
221
222 /* Subscribe to the "changed" signal. */
223 g_signal_connect (monitor, "changed",
224 (GCallback) dir_monitor_callback, NULL);
225
226 /* Store watch object in watch list. */
227 watch_object = list4 (watch_descriptor, file, flags, callback);
228 watch_list = Fcons (watch_object, watch_list);
229
230 return watch_descriptor;
231 }
232
233 DEFUN ("gfile-rm-watch", Fgfile_rm_watch, Sgfile_rm_watch, 1, 1, 0,
234 doc: /* Remove an existing WATCH-DESCRIPTOR.
235
236 WATCH-DESCRIPTOR should be an object returned by `gfile-add-watch'. */)
237 (Lisp_Object watch_descriptor)
238 {
239 Lisp_Object watch_object = assq_no_quit (watch_descriptor, watch_list);
240
241 if (! CONSP (watch_object))
242 xsignal2 (Qfile_notify_error, build_string ("Not a watch descriptor"),
243 watch_descriptor);
244
245 eassert (INTEGERP (watch_descriptor));
246 GFileMonitor *monitor = XINTPTR (watch_descriptor);
247 if (!g_file_monitor_is_cancelled (monitor) &&
248 !g_file_monitor_cancel (monitor))
249 xsignal2 (Qfile_notify_error, build_string ("Could not rm watch"),
250 watch_descriptor);
251
252 /* Remove watch descriptor from watch list. */
253 watch_list = Fdelq (watch_object, watch_list);
254
255 /* Cleanup. */
256 g_object_unref (monitor);
257
258 return Qt;
259 }
260
261 DEFUN ("gfile-valid-p", Fgfile_valid_p, Sgfile_valid_p, 1, 1, 0,
262 doc: /* "Check a watch specified by its WATCH-DESCRIPTOR.
263
264 WATCH-DESCRIPTOR should be an object returned by `gfile-add-watch'.
265
266 A watch can become invalid if the file or directory it watches is
267 deleted, or if the watcher thread exits abnormally for any other
268 reason. Removing the watch by calling `gfile-rm-watch' also makes it
269 invalid. */)
270 (Lisp_Object watch_descriptor)
271 {
272 Lisp_Object watch_object = Fassoc (watch_descriptor, watch_list);
273 if (NILP (watch_object))
274 return Qnil;
275 else
276 {
277 GFileMonitor *monitor = XINTPTR (watch_descriptor);
278 return g_file_monitor_is_cancelled (monitor) ? Qnil : Qt;
279 }
280 }
281
282 \f
283 void
284 globals_of_gfilenotify (void)
285 {
286 #if ! GLIB_CHECK_VERSION (2, 36, 0)
287 g_type_init ();
288 #endif
289 watch_list = Qnil;
290 }
291
292 void
293 syms_of_gfilenotify (void)
294 {
295 defsubr (&Sgfile_add_watch);
296 defsubr (&Sgfile_rm_watch);
297 defsubr (&Sgfile_valid_p);
298
299 /* Filter objects. */
300 DEFSYM (Qchange, "change");
301 DEFSYM (Qattribute_change, "attribute-change");
302 DEFSYM (Qwatch_mounts, "watch-mounts"); /* G_FILE_MONITOR_WATCH_MOUNTS */
303 DEFSYM (Qsend_moved, "send-moved"); /* G_FILE_MONITOR_SEND_MOVED */
304
305 /* Event types. */
306 DEFSYM (Qchanged, "changed"); /* G_FILE_MONITOR_EVENT_CHANGED */
307 DEFSYM (Qchanges_done_hint, "changes-done-hint");
308 /* G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT */
309 DEFSYM (Qdeleted, "deleted"); /* G_FILE_MONITOR_EVENT_DELETED */
310 DEFSYM (Qcreated, "created"); /* G_FILE_MONITOR_EVENT_CREATED */
311 DEFSYM (Qattribute_changed, "attribute-changed");
312 /* G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED */
313 DEFSYM (Qpre_unmount, "pre-unmount"); /* G_FILE_MONITOR_EVENT_PRE_UNMOUNT */
314 DEFSYM (Qunmounted, "unmounted"); /* G_FILE_MONITOR_EVENT_UNMOUNTED */
315 DEFSYM (Qmoved, "moved"); /* G_FILE_MONITOR_EVENT_MOVED */
316
317 staticpro (&watch_list);
318
319 Fprovide (intern_c_string ("gfilenotify"), Qnil);
320
321 }
322
323 #endif /* HAVE_GFILENOTIFY */