diff options
Diffstat (limited to 'src/kqueue.c')
| -rw-r--r-- | src/kqueue.c | 339 |
1 files changed, 339 insertions, 0 deletions
diff --git a/src/kqueue.c b/src/kqueue.c new file mode 100644 index 00000000000..69bf5f61080 --- /dev/null +++ b/src/kqueue.c | |||
| @@ -0,0 +1,339 @@ | |||
| 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_KQUEUE | ||
| 22 | #include <stdio.h> | ||
| 23 | #include <sys/event.h> | ||
| 24 | #include "lisp.h" | ||
| 25 | #include "coding.h" | ||
| 26 | #include "termhooks.h" | ||
| 27 | #include "keyboard.h" | ||
| 28 | |||
| 29 | |||
| 30 | /* File handle for kqueue. */ | ||
| 31 | static int kqueuefd = -1; | ||
| 32 | |||
| 33 | /* This is a list, elements are triples (DESCRIPTOR FILE FLAGS CALLBACK) */ | ||
| 34 | static Lisp_Object watch_list; | ||
| 35 | |||
| 36 | #if 0 | ||
| 37 | /* This is the callback function for arriving signals from | ||
| 38 | g_file_monitor. It shall create a Lisp event, and put it into | ||
| 39 | Emacs input queue. */ | ||
| 40 | static gboolean | ||
| 41 | dir_monitor_callback (GFileMonitor *monitor, | ||
| 42 | GFile *file, | ||
| 43 | GFile *other_file, | ||
| 44 | GFileMonitorEvent event_type, | ||
| 45 | gpointer user_data) | ||
| 46 | { | ||
| 47 | Lisp_Object symbol, monitor_object, watch_object, flags; | ||
| 48 | char *name = g_file_get_parse_name (file); | ||
| 49 | char *oname = other_file ? g_file_get_parse_name (other_file) : NULL; | ||
| 50 | |||
| 51 | /* Determine event symbol. */ | ||
| 52 | switch (event_type) | ||
| 53 | { | ||
| 54 | case G_FILE_MONITOR_EVENT_CHANGED: | ||
| 55 | symbol = Qchanged; | ||
| 56 | break; | ||
| 57 | case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT: | ||
| 58 | symbol = Qchanges_done_hint; | ||
| 59 | break; | ||
| 60 | case G_FILE_MONITOR_EVENT_DELETED: | ||
| 61 | symbol = Qdeleted; | ||
| 62 | break; | ||
| 63 | case G_FILE_MONITOR_EVENT_CREATED: | ||
| 64 | symbol = Qcreated; | ||
| 65 | break; | ||
| 66 | case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED: | ||
| 67 | symbol = Qattribute_changed; | ||
| 68 | break; | ||
| 69 | case G_FILE_MONITOR_EVENT_PRE_UNMOUNT: | ||
| 70 | symbol = Qpre_unmount; | ||
| 71 | break; | ||
| 72 | case G_FILE_MONITOR_EVENT_UNMOUNTED: | ||
| 73 | symbol = Qunmounted; | ||
| 74 | break; | ||
| 75 | case G_FILE_MONITOR_EVENT_MOVED: | ||
| 76 | symbol = Qmoved; | ||
| 77 | break; | ||
| 78 | default: | ||
| 79 | goto cleanup; | ||
| 80 | } | ||
| 81 | |||
| 82 | /* Determine callback function. */ | ||
| 83 | monitor_object = make_pointer_integer (monitor); | ||
| 84 | eassert (INTEGERP (monitor_object)); | ||
| 85 | watch_object = assq_no_quit (monitor_object, watch_list); | ||
| 86 | |||
| 87 | if (CONSP (watch_object)) | ||
| 88 | { | ||
| 89 | struct input_event event; | ||
| 90 | Lisp_Object otail = oname ? list1 (build_string (oname)) : Qnil; | ||
| 91 | |||
| 92 | /* Check, whether event_type is expected. */ | ||
| 93 | flags = XCAR (XCDR (XCDR (watch_object))); | ||
| 94 | if ((!NILP (Fmember (Qchange, flags)) && | ||
| 95 | !NILP (Fmember (symbol, list5 (Qchanged, Qchanges_done_hint, | ||
| 96 | Qdeleted, Qcreated, Qmoved)))) || | ||
| 97 | (!NILP (Fmember (Qattribute_change, flags)) && | ||
| 98 | ((EQ (symbol, Qattribute_changed))))) | ||
| 99 | { | ||
| 100 | /* Construct an event. */ | ||
| 101 | EVENT_INIT (event); | ||
| 102 | event.kind = FILE_NOTIFY_EVENT; | ||
| 103 | event.frame_or_window = Qnil; | ||
| 104 | event.arg = list2 (Fcons (monitor_object, | ||
| 105 | Fcons (symbol, | ||
| 106 | Fcons (build_string (name), | ||
| 107 | otail))), | ||
| 108 | XCAR (XCDR (XCDR (XCDR (watch_object))))); | ||
| 109 | |||
| 110 | /* Store it into the input event queue. */ | ||
| 111 | kbd_buffer_store_event (&event); | ||
| 112 | // XD_DEBUG_MESSAGE ("%s", XD_OBJECT_TO_STRING (event.arg)); | ||
| 113 | } | ||
| 114 | |||
| 115 | /* Cancel monitor if file or directory is deleted. */ | ||
| 116 | if (!NILP (Fmember (symbol, list2 (Qdeleted, Qmoved))) && | ||
| 117 | (strcmp (name, SSDATA (XCAR (XCDR (watch_object)))) == 0) && | ||
| 118 | !g_file_monitor_is_cancelled (monitor)) | ||
| 119 | g_file_monitor_cancel (monitor); | ||
| 120 | } | ||
| 121 | |||
| 122 | /* Cleanup. */ | ||
| 123 | cleanup: | ||
| 124 | g_free (name); | ||
| 125 | g_free (oname); | ||
| 126 | |||
| 127 | return TRUE; | ||
| 128 | } | ||
| 129 | #endif /* 0 */ | ||
| 130 | |||
| 131 | DEFUN ("kqueue-add-watch", Fkqueue_add_watch, Skqueue_add_watch, 3, 3, 0, | ||
| 132 | doc: /* Add a watch for filesystem events pertaining to FILE. | ||
| 133 | |||
| 134 | This arranges for filesystem events pertaining to FILE to be reported | ||
| 135 | to Emacs. Use `gfile-rm-watch' to cancel the watch. | ||
| 136 | |||
| 137 | Value is a descriptor for the added watch. If the file cannot be | ||
| 138 | watched for some reason, this function signals a `file-notify-error' error. | ||
| 139 | |||
| 140 | FLAGS is a list of conditions to set what will be watched for. It can | ||
| 141 | include the following symbols: | ||
| 142 | |||
| 143 | `change' -- watch for file changes | ||
| 144 | `attribute-change' -- watch for file attributes changes, like | ||
| 145 | permissions or modification time | ||
| 146 | `watch-mounts' -- watch for mount events | ||
| 147 | `send-moved' -- pair `deleted' and `created' events caused by | ||
| 148 | file renames and send a single `renamed' event | ||
| 149 | instead | ||
| 150 | |||
| 151 | When any event happens, Emacs will call the CALLBACK function passing | ||
| 152 | it a single argument EVENT, which is of the form | ||
| 153 | |||
| 154 | (DESCRIPTOR ACTION FILE [FILE1]) | ||
| 155 | |||
| 156 | DESCRIPTOR is the same object as the one returned by this function. | ||
| 157 | ACTION is the description of the event. It could be any one of the | ||
| 158 | following: | ||
| 159 | |||
| 160 | `changed' -- FILE has changed | ||
| 161 | `changes-done-hint' -- a hint that this was probably the last change | ||
| 162 | in a set of changes | ||
| 163 | `deleted' -- FILE was deleted | ||
| 164 | `created' -- FILE was created | ||
| 165 | `attribute-changed' -- a FILE attribute was changed | ||
| 166 | `pre-unmount' -- the FILE location will soon be unmounted | ||
| 167 | `unmounted' -- the FILE location was unmounted | ||
| 168 | `moved' -- FILE was moved to FILE1 | ||
| 169 | |||
| 170 | FILE is the name of the file whose event is being reported. FILE1 | ||
| 171 | will be reported only in case of the `moved' event. */) | ||
| 172 | (Lisp_Object file, Lisp_Object flags, Lisp_Object callback) | ||
| 173 | { | ||
| 174 | Lisp_Object watch_object; | ||
| 175 | GFile *gfile; | ||
| 176 | GFileMonitor *monitor; | ||
| 177 | GFileMonitorFlags gflags = G_FILE_MONITOR_NONE; | ||
| 178 | GError *gerror = NULL; | ||
| 179 | |||
| 180 | /* Check parameters. */ | ||
| 181 | CHECK_STRING (file); | ||
| 182 | file = Fdirectory_file_name (Fexpand_file_name (file, Qnil)); | ||
| 183 | if (NILP (Ffile_exists_p (file))) | ||
| 184 | report_file_error ("File does not exist", file); | ||
| 185 | |||
| 186 | CHECK_LIST (flags); | ||
| 187 | |||
| 188 | if (!FUNCTIONP (callback)) | ||
| 189 | wrong_type_argument (Qinvalid_function, callback); | ||
| 190 | |||
| 191 | /* Create GFile name. */ | ||
| 192 | // gfile = g_file_new_for_path (SSDATA (ENCODE_FILE (file))); | ||
| 193 | |||
| 194 | /* Assemble flags. */ | ||
| 195 | // if (!NILP (Fmember (Qwatch_mounts, flags))) | ||
| 196 | // gflags |= G_FILE_MONITOR_WATCH_MOUNTS; | ||
| 197 | // if (!NILP (Fmember (Qsend_moved, flags))) | ||
| 198 | // gflags |= G_FILE_MONITOR_SEND_MOVED; | ||
| 199 | |||
| 200 | if (kqueuefd < 0) | ||
| 201 | { | ||
| 202 | kqueuefd = kqueue (); | ||
| 203 | if (kqueuefd < 0) | ||
| 204 | report_file_notify_error ("File watching is not available", Qnil); | ||
| 205 | watch_list = Qnil; | ||
| 206 | // add_read_fd (inotifyfd, &inotify_callback, NULL); | ||
| 207 | } | ||
| 208 | |||
| 209 | |||
| 210 | } | ||
| 211 | #if 0 | ||
| 212 | |||
| 213 | mask = aspect_to_inotifymask (aspect); | ||
| 214 | encoded_file_name = ENCODE_FILE (file_name); | ||
| 215 | watchdesc = inotify_add_watch (inotifyfd, SSDATA (encoded_file_name), mask); | ||
| 216 | if (watchdesc == -1) | ||
| 217 | report_file_notify_error ("Could not add watch for file", file_name); | ||
| 218 | |||
| 219 | /* Enable watch. */ | ||
| 220 | monitor = g_file_monitor (gfile, gflags, NULL, &gerror); | ||
| 221 | g_object_unref (gfile); | ||
| 222 | if (gerror) | ||
| 223 | { | ||
| 224 | char msg[1024]; | ||
| 225 | strcpy (msg, gerror->message); | ||
| 226 | g_error_free (gerror); | ||
| 227 | xsignal1 (Qfile_notify_error, build_string (msg)); | ||
| 228 | } | ||
| 229 | if (! monitor) | ||
| 230 | xsignal2 (Qfile_notify_error, build_string ("Cannot watch file"), file); | ||
| 231 | |||
| 232 | Lisp_Object watch_descriptor = make_pointer_integer (monitor); | ||
| 233 | |||
| 234 | /* Check the dicey assumption that make_pointer_integer is safe. */ | ||
| 235 | if (! INTEGERP (watch_descriptor)) | ||
| 236 | { | ||
| 237 | g_object_unref (monitor); | ||
| 238 | xsignal2 (Qfile_notify_error, build_string ("Unsupported file watcher"), | ||
| 239 | file); | ||
| 240 | } | ||
| 241 | |||
| 242 | /* The default rate limit is 800 msec. We adapt this. */ | ||
| 243 | g_file_monitor_set_rate_limit (monitor, 100); | ||
| 244 | |||
| 245 | /* Subscribe to the "changed" signal. */ | ||
| 246 | g_signal_connect (monitor, "changed", | ||
| 247 | (GCallback) dir_monitor_callback, NULL); | ||
| 248 | |||
| 249 | /* Store watch object in watch list. */ | ||
| 250 | watch_object = list4 (watch_descriptor, file, flags, callback); | ||
| 251 | watch_list = Fcons (watch_object, watch_list); | ||
| 252 | |||
| 253 | return watch_descriptor; | ||
| 254 | } | ||
| 255 | |||
| 256 | DEFUN ("gfile-rm-watch", Fgfile_rm_watch, Sgfile_rm_watch, 1, 1, 0, | ||
| 257 | doc: /* Remove an existing WATCH-DESCRIPTOR. | ||
| 258 | |||
| 259 | WATCH-DESCRIPTOR should be an object returned by `gfile-add-watch'. */) | ||
| 260 | (Lisp_Object watch_descriptor) | ||
| 261 | { | ||
| 262 | Lisp_Object watch_object = assq_no_quit (watch_descriptor, watch_list); | ||
| 263 | |||
| 264 | if (! CONSP (watch_object)) | ||
| 265 | xsignal2 (Qfile_notify_error, build_string ("Not a watch descriptor"), | ||
| 266 | watch_descriptor); | ||
| 267 | |||
| 268 | eassert (INTEGERP (watch_descriptor)); | ||
| 269 | GFileMonitor *monitor = XINTPTR (watch_descriptor); | ||
| 270 | if (!g_file_monitor_is_cancelled (monitor) && | ||
| 271 | !g_file_monitor_cancel (monitor)) | ||
| 272 | xsignal2 (Qfile_notify_error, build_string ("Could not rm watch"), | ||
| 273 | watch_descriptor); | ||
| 274 | |||
| 275 | /* Remove watch descriptor from watch list. */ | ||
| 276 | watch_list = Fdelq (watch_object, watch_list); | ||
| 277 | |||
| 278 | /* Cleanup. */ | ||
| 279 | g_object_unref (monitor); | ||
| 280 | |||
| 281 | return Qt; | ||
| 282 | } | ||
| 283 | |||
| 284 | DEFUN ("gfile-valid-p", Fgfile_valid_p, Sgfile_valid_p, 1, 1, 0, | ||
| 285 | doc: /* "Check a watch specified by its WATCH-DESCRIPTOR. | ||
| 286 | |||
| 287 | WATCH-DESCRIPTOR should be an object returned by `gfile-add-watch'. | ||
| 288 | |||
| 289 | A watch can become invalid if the file or directory it watches is | ||
| 290 | deleted, or if the watcher thread exits abnormally for any other | ||
| 291 | reason. Removing the watch by calling `gfile-rm-watch' also makes it | ||
| 292 | invalid. */) | ||
| 293 | (Lisp_Object watch_descriptor) | ||
| 294 | { | ||
| 295 | Lisp_Object watch_object = Fassoc (watch_descriptor, watch_list); | ||
| 296 | if (NILP (watch_object)) | ||
| 297 | return Qnil; | ||
| 298 | else | ||
| 299 | { | ||
| 300 | GFileMonitor *monitor = XINTPTR (watch_descriptor); | ||
| 301 | return g_file_monitor_is_cancelled (monitor) ? Qnil : Qt; | ||
| 302 | } | ||
| 303 | } | ||
| 304 | #endif /* 0 */ | ||
| 305 | |||
| 306 | |||
| 307 | void | ||
| 308 | globals_of_kqueue (void) | ||
| 309 | { | ||
| 310 | watch_list = Qnil; | ||
| 311 | } | ||
| 312 | |||
| 313 | void | ||
| 314 | syms_of_kqueue (void) | ||
| 315 | { | ||
| 316 | defsubr (&Skqueue_add_watch); | ||
| 317 | // defsubr (&Skqueue_rm_watch); | ||
| 318 | // defsubr (&Skqueue_valid_p); | ||
| 319 | |||
| 320 | /* Filter objects. */ | ||
| 321 | DEFSYM (Qchange, "change"); | ||
| 322 | DEFSYM (Qattribute_change, "attribute-change"); | ||
| 323 | DEFSYM (Qwatch_mounts, "watch-mounts"); /* G_FILE_MONITOR_WATCH_MOUNTS */ | ||
| 324 | DEFSYM (Qsend_moved, "send-moved"); /* G_FILE_MONITOR_SEND_MOVED */ | ||
| 325 | |||
| 326 | /* Event types. */ | ||
| 327 | DEFSYM (Qdelete, "delete"); /* NOTE_DELETE */ | ||
| 328 | DEFSYM (Qwrite, "write"); /* NOTE_WRITE */ | ||
| 329 | DEFSYM (Qextend, "extend"); /* NOTE_EXTEND */ | ||
| 330 | DEFSYM (Qattrib, "attrib"); /* NOTE_ATTRIB */ | ||
| 331 | DEFSYM (Qlink, "link"); /* NOTE_LINK */ | ||
| 332 | DEFSYM (Qrename, "rename"); /* NOTE_RENAME */ | ||
| 333 | |||
| 334 | staticpro (&watch_list); | ||
| 335 | |||
| 336 | Fprovide (intern_c_string ("kqueue"), Qnil); | ||
| 337 | } | ||
| 338 | |||
| 339 | #endif /* HAVE_KQUEUE */ | ||