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