]> code.delx.au - gnu-emacs/blob - src/gfilenotify.c
* src/gfilenotify.c (monitor_to_lisp, lisp_to_monitor):
[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 static Lisp_Object watch_list;
33
34 /* Convert a monitor to a Lisp integer and back. On all known glib
35 platforms, converting the sum of MONITOR and Lisp_Int0 directly to
36 a Lisp_Object value results in a Lisp integer, which is safe. */
37
38 static Lisp_Object
39 monitor_to_lisp (GFileMonitor *monitor)
40 {
41 return XIL (TAG_PTR (Lisp_Int0, monitor));
42 }
43
44 static GFileMonitor *
45 lisp_to_monitor (Lisp_Object watch_descriptor)
46 {
47 return XUNTAG (watch_descriptor, Lisp_Int0);
48 }
49
50 /* This is the callback function for arriving signals from
51 g_file_monitor. It shall create a Lisp event, and put it into
52 Emacs input queue. */
53 static gboolean
54 dir_monitor_callback (GFileMonitor *monitor,
55 GFile *file,
56 GFile *other_file,
57 GFileMonitorEvent event_type,
58 gpointer user_data)
59 {
60 Lisp_Object symbol, monitor_object, watch_object;
61 char *name = g_file_get_parse_name (file);
62 char *oname = other_file ? g_file_get_parse_name (other_file) : NULL;
63
64 /* Determine event symbol. */
65 switch (event_type)
66 {
67 case G_FILE_MONITOR_EVENT_CHANGED:
68 symbol = Qchanged;
69 break;
70 case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
71 symbol = Qchanges_done_hint;
72 break;
73 case G_FILE_MONITOR_EVENT_DELETED:
74 symbol = Qdeleted;
75 break;
76 case G_FILE_MONITOR_EVENT_CREATED:
77 symbol = Qcreated;
78 break;
79 case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED:
80 symbol = Qattribute_changed;
81 break;
82 case G_FILE_MONITOR_EVENT_PRE_UNMOUNT:
83 symbol = Qpre_unmount;
84 break;
85 case G_FILE_MONITOR_EVENT_UNMOUNTED:
86 symbol = Qunmounted;
87 break;
88 case G_FILE_MONITOR_EVENT_MOVED:
89 symbol = Qmoved;
90 break;
91 default:
92 goto cleanup;
93 }
94
95 /* Determine callback function. */
96 monitor_object = monitor_to_lisp (monitor);
97 eassert (INTEGERP (monitor_object));
98 watch_object = assq_no_quit (monitor_object, watch_list);
99
100 if (CONSP (watch_object))
101 {
102 /* Construct an event. */
103 struct input_event event;
104 Lisp_Object otail = oname ? list1 (build_string (oname)) : Qnil;
105 EVENT_INIT (event);
106 event.kind = FILE_NOTIFY_EVENT;
107 event.frame_or_window = Qnil;
108 event.arg = list2 (Fcons (monitor_object,
109 Fcons (symbol,
110 Fcons (build_string (name),
111 otail))),
112 XCDR (watch_object));
113
114 /* Store it into the input event queue. */
115 kbd_buffer_store_event (&event);
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 'watch-mounts' -- watch for mount events
139 'send-moved' -- pair 'deleted' and 'created' events caused by file
140 renames and send a single 'renamed' event instead
141
142 When any event happens, Emacs will call the CALLBACK function passing
143 it a single argument EVENT, which is of the form
144
145 (DESCRIPTOR ACTION FILE [FILE1])
146
147 DESCRIPTOR is the same object as the one returned by this function.
148 ACTION is the description of the event. It could be any one of the
149 following:
150
151 'changed' -- FILE has changed
152 'changes-done-hint' -- a hint that this was probably the last change
153 in a set of changes
154 'deleted' -- FILE was deleted
155 'created' -- FILE was created
156 'attribute-changed' -- a FILE attribute was changed
157 'pre-unmount' -- the FILE location will soon be unmounted
158 'unmounted' -- the FILE location was unmounted
159 'moved' -- FILE was moved to FILE1
160
161 FILE is the name of the file whose event is being reported. FILE1
162 will be reported only in case of the 'moved' event. */)
163 (Lisp_Object file, Lisp_Object flags, Lisp_Object callback)
164 {
165 Lisp_Object watch_object;
166 GFile *gfile;
167 GFileMonitor *monitor;
168 GFileMonitorFlags gflags = G_FILE_MONITOR_NONE;
169
170 /* Check parameters. */
171 CHECK_STRING (file);
172 file = Fdirectory_file_name (Fexpand_file_name (file, Qnil));
173 if (NILP (Ffile_exists_p (file)))
174 report_file_error ("File does not exist", file);
175
176 CHECK_LIST (flags);
177
178 if (!FUNCTIONP (callback))
179 wrong_type_argument (Qinvalid_function, callback);
180
181 /* Create GFile name. */
182 gfile = g_file_new_for_path (SSDATA (ENCODE_FILE (file)));
183
184 /* Assemble flags. */
185 if (!NILP (Fmember (Qwatch_mounts, flags)))
186 gflags |= G_FILE_MONITOR_WATCH_MOUNTS;
187 if (!NILP (Fmember (Qsend_moved, flags)))
188 gflags |= G_FILE_MONITOR_SEND_MOVED;
189
190 /* Enable watch. */
191 monitor = g_file_monitor (gfile, gflags, NULL, NULL);
192 if (! monitor)
193 xsignal2 (Qfile_notify_error, build_string ("Cannot watch file"), file);
194
195 Lisp_Object watch_descriptor = monitor_to_lisp (monitor);
196
197 /* Check the dicey assumption that monitor_to_lisp is safe. */
198 if (! INTEGERP (watch_descriptor))
199 {
200 g_object_unref (monitor);
201 xsignal2 (Qfile_notify_error, build_string ("Unsupported file watcher"),
202 file);
203 }
204
205 g_signal_connect (monitor, "changed",
206 (GCallback) dir_monitor_callback, NULL);
207
208 /* Store watch object in watch list. */
209 watch_object = Fcons (watch_descriptor, callback);
210 watch_list = Fcons (watch_object, watch_list);
211
212 return watch_descriptor;
213 }
214
215 DEFUN ("gfile-rm-watch", Fgfile_rm_watch, Sgfile_rm_watch, 1, 1, 0,
216 doc: /* Remove an existing WATCH-DESCRIPTOR.
217
218 WATCH-DESCRIPTOR should be an object returned by `gfile-add-watch'. */)
219 (Lisp_Object watch_descriptor)
220 {
221 Lisp_Object watch_object = assq_no_quit (watch_descriptor, watch_list);
222
223 if (! CONSP (watch_object))
224 xsignal2 (Qfile_notify_error, build_string ("Not a watch descriptor"),
225 watch_descriptor);
226
227 eassert (INTEGERP (watch_descriptor));
228 GFileMonitor *monitor = lisp_to_monitor (watch_descriptor);
229 if (!g_file_monitor_cancel (monitor))
230 xsignal2 (Qfile_notify_error, build_string ("Could not rm watch"),
231 watch_descriptor);
232
233 /* Remove watch descriptor from watch list. */
234 watch_list = Fdelq (watch_object, watch_list);
235
236 /* Cleanup. */
237 g_object_unref (monitor);
238
239 return Qt;
240 }
241
242 \f
243 void
244 globals_of_gfilenotify (void)
245 {
246 #if ! GLIB_CHECK_VERSION (2, 36, 0)
247 g_type_init ();
248 #endif
249 watch_list = Qnil;
250 }
251
252 void
253 syms_of_gfilenotify (void)
254 {
255 DEFSYM (Qgfile_add_watch, "gfile-add-watch");
256 defsubr (&Sgfile_add_watch);
257
258 DEFSYM (Qgfile_rm_watch, "gfile-rm-watch");
259 defsubr (&Sgfile_rm_watch);
260
261 /* Filter objects. */
262 DEFSYM (Qwatch_mounts, "watch-mounts"); /* G_FILE_MONITOR_WATCH_MOUNTS */
263 DEFSYM (Qsend_moved, "send-moved"); /* G_FILE_MONITOR_SEND_MOVED */
264
265 /* Event types. */
266 DEFSYM (Qchanged, "changed"); /* G_FILE_MONITOR_EVENT_CHANGED */
267 DEFSYM (Qchanges_done_hint, "changes-done-hint");
268 /* G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT */
269 DEFSYM (Qdeleted, "deleted"); /* G_FILE_MONITOR_EVENT_DELETED */
270 DEFSYM (Qcreated, "created"); /* G_FILE_MONITOR_EVENT_CREATED */
271 DEFSYM (Qattribute_changed, "attribute-changed");
272 /* G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED */
273 DEFSYM (Qpre_unmount, "pre-unmount"); /* G_FILE_MONITOR_EVENT_PRE_UNMOUNT */
274 DEFSYM (Qunmounted, "unmounted"); /* G_FILE_MONITOR_EVENT_UNMOUNTED */
275 DEFSYM (Qmoved, "moved"); /* G_FILE_MONITOR_EVENT_MOVED */
276
277 staticpro (&watch_list);
278
279 Fprovide (intern_c_string ("gfilenotify"), Qnil);
280
281 }
282
283 #endif /* HAVE_GFILENOTIFY */