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