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