]> code.delx.au - gnu-emacs/blob - src/gfilenotify.c
Ibuffer change marks
[gnu-emacs] / src / gfilenotify.c
1 /* Filesystem notifications support with glib API.
2 Copyright (C) 2013-2016 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 (at
9 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 (strcmp (name, SSDATA (XCAR (XCDR (watch_object)))) == 0) &&
114 !g_file_monitor_is_cancelled (monitor))
115 g_file_monitor_cancel (monitor);
116 }
117
118 /* Cleanup. */
119 cleanup:
120 g_free (name);
121 g_free (oname);
122
123 return TRUE;
124 }
125
126 DEFUN ("gfile-add-watch", Fgfile_add_watch, Sgfile_add_watch, 3, 3, 0,
127 doc: /* Add a watch for filesystem events pertaining to FILE.
128
129 This arranges for filesystem events pertaining to FILE to be reported
130 to Emacs. Use `gfile-rm-watch' to cancel the watch.
131
132 Value is a descriptor for the added watch. If the file cannot be
133 watched for some reason, this function signals a `file-notify-error' error.
134
135 FLAGS is a list of conditions to set what will be watched for. It can
136 include the following symbols:
137
138 `change' -- watch for file changes
139 `attribute-change' -- watch for file attributes changes, like
140 permissions or modification time
141 `watch-mounts' -- watch for mount events
142 `send-moved' -- pair `deleted' and `created' events caused by
143 file renames and send a single `renamed' event
144 instead
145
146 When any event happens, Emacs will call the CALLBACK function passing
147 it a single argument EVENT, which is of the form
148
149 (DESCRIPTOR ACTION FILE [FILE1])
150
151 DESCRIPTOR is the same object as the one returned by this function.
152 ACTION is the description of the event. It could be any one of the
153 following:
154
155 `changed' -- FILE has changed
156 `changes-done-hint' -- a hint that this was probably the last change
157 in a set of changes
158 `deleted' -- FILE was deleted
159 `created' -- FILE was created
160 `attribute-changed' -- a FILE attribute was changed
161 `pre-unmount' -- the FILE location will soon be unmounted
162 `unmounted' -- the FILE location was unmounted
163 `moved' -- FILE was moved to FILE1
164
165 FILE is the name of the file whose event is being reported. FILE1
166 will be reported only in case of the `moved' event. */)
167 (Lisp_Object file, Lisp_Object flags, Lisp_Object callback)
168 {
169 Lisp_Object watch_object;
170 GFile *gfile;
171 GFileMonitor *monitor;
172 GFileMonitorFlags gflags = G_FILE_MONITOR_NONE;
173 GError *gerror = NULL;
174
175 /* Check parameters. */
176 CHECK_STRING (file);
177 file = Fdirectory_file_name (Fexpand_file_name (file, Qnil));
178 if (NILP (Ffile_exists_p (file)))
179 report_file_error ("File does not exist", file);
180
181 CHECK_LIST (flags);
182
183 if (!FUNCTIONP (callback))
184 wrong_type_argument (Qinvalid_function, callback);
185
186 /* Create GFile name. */
187 gfile = g_file_new_for_path (SSDATA (ENCODE_FILE (file)));
188
189 /* Assemble flags. */
190 if (!NILP (Fmember (Qwatch_mounts, flags)))
191 gflags |= G_FILE_MONITOR_WATCH_MOUNTS;
192 if (!NILP (Fmember (Qsend_moved, flags)))
193 gflags |= G_FILE_MONITOR_SEND_MOVED;
194
195 /* Enable watch. */
196 monitor = g_file_monitor (gfile, gflags, NULL, &gerror);
197 g_object_unref (gfile);
198 if (gerror)
199 {
200 char msg[1024];
201 strcpy (msg, gerror->message);
202 g_error_free (gerror);
203 xsignal1 (Qfile_notify_error, build_string (msg));
204 }
205 if (! monitor)
206 xsignal2 (Qfile_notify_error, build_string ("Cannot watch file"), file);
207
208 Lisp_Object watch_descriptor = make_pointer_integer (monitor);
209
210 /* Check the dicey assumption that make_pointer_integer is safe. */
211 if (! INTEGERP (watch_descriptor))
212 {
213 g_object_unref (monitor);
214 xsignal2 (Qfile_notify_error, build_string ("Unsupported file watcher"),
215 file);
216 }
217
218 /* The default rate limit is 800 msec. We adapt this. */
219 g_file_monitor_set_rate_limit (monitor, 100);
220
221 /* Subscribe to the "changed" signal. */
222 g_signal_connect (monitor, "changed",
223 (GCallback) dir_monitor_callback, NULL);
224
225 /* Store watch object in watch list. */
226 watch_object = list4 (watch_descriptor, file, flags, callback);
227 watch_list = Fcons (watch_object, watch_list);
228
229 return watch_descriptor;
230 }
231
232 DEFUN ("gfile-rm-watch", Fgfile_rm_watch, Sgfile_rm_watch, 1, 1, 0,
233 doc: /* Remove an existing WATCH-DESCRIPTOR.
234
235 WATCH-DESCRIPTOR should be an object returned by `gfile-add-watch'. */)
236 (Lisp_Object watch_descriptor)
237 {
238 Lisp_Object watch_object = assq_no_quit (watch_descriptor, watch_list);
239
240 if (! CONSP (watch_object))
241 xsignal2 (Qfile_notify_error, build_string ("Not a watch descriptor"),
242 watch_descriptor);
243
244 eassert (INTEGERP (watch_descriptor));
245 GFileMonitor *monitor = XINTPTR (watch_descriptor);
246 if (!g_file_monitor_is_cancelled (monitor) &&
247 !g_file_monitor_cancel (monitor))
248 xsignal2 (Qfile_notify_error, build_string ("Could not rm watch"),
249 watch_descriptor);
250
251 /* Remove watch descriptor from watch list. */
252 watch_list = Fdelq (watch_object, watch_list);
253
254 /* Cleanup. */
255 g_object_unref (monitor);
256
257 return Qt;
258 }
259
260 DEFUN ("gfile-valid-p", Fgfile_valid_p, Sgfile_valid_p, 1, 1, 0,
261 doc: /* "Check a watch specified by its WATCH-DESCRIPTOR.
262
263 WATCH-DESCRIPTOR should be an object returned by `gfile-add-watch'.
264
265 A watch can become invalid if the file or directory it watches is
266 deleted, or if the watcher thread exits abnormally for any other
267 reason. Removing the watch by calling `gfile-rm-watch' also makes it
268 invalid. */)
269 (Lisp_Object watch_descriptor)
270 {
271 Lisp_Object watch_object = Fassoc (watch_descriptor, watch_list);
272 if (NILP (watch_object))
273 return Qnil;
274 else
275 {
276 GFileMonitor *monitor = XINTPTR (watch_descriptor);
277 return g_file_monitor_is_cancelled (monitor) ? Qnil : Qt;
278 }
279 }
280
281 \f
282 void
283 globals_of_gfilenotify (void)
284 {
285 #if ! GLIB_CHECK_VERSION (2, 36, 0)
286 g_type_init ();
287 #endif
288 watch_list = Qnil;
289 }
290
291 void
292 syms_of_gfilenotify (void)
293 {
294 defsubr (&Sgfile_add_watch);
295 defsubr (&Sgfile_rm_watch);
296 defsubr (&Sgfile_valid_p);
297
298 /* Filter objects. */
299 DEFSYM (Qchange, "change");
300 DEFSYM (Qattribute_change, "attribute-change");
301 DEFSYM (Qwatch_mounts, "watch-mounts"); /* G_FILE_MONITOR_WATCH_MOUNTS */
302 DEFSYM (Qsend_moved, "send-moved"); /* G_FILE_MONITOR_SEND_MOVED */
303
304 /* Event types. */
305 DEFSYM (Qchanged, "changed"); /* G_FILE_MONITOR_EVENT_CHANGED */
306 DEFSYM (Qchanges_done_hint, "changes-done-hint");
307 /* G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT */
308 DEFSYM (Qdeleted, "deleted"); /* G_FILE_MONITOR_EVENT_DELETED */
309 DEFSYM (Qcreated, "created"); /* G_FILE_MONITOR_EVENT_CREATED */
310 DEFSYM (Qattribute_changed, "attribute-changed");
311 /* G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED */
312 DEFSYM (Qpre_unmount, "pre-unmount"); /* G_FILE_MONITOR_EVENT_PRE_UNMOUNT */
313 DEFSYM (Qunmounted, "unmounted"); /* G_FILE_MONITOR_EVENT_UNMOUNTED */
314 DEFSYM (Qmoved, "moved"); /* G_FILE_MONITOR_EVENT_MOVED */
315
316 staticpro (&watch_list);
317
318 Fprovide (intern_c_string ("gfilenotify"), Qnil);
319
320 }
321
322 #endif /* HAVE_GFILENOTIFY */