summaryrefslogtreecommitdiff
path: root/src/kqueue.c
diff options
context:
space:
mode:
authorMichael Albinus <michael.albinus@gmx.de>2015-11-09 10:00:56 +0100
committerMichael Albinus <michael.albinus@gmx.de>2015-11-25 15:07:10 +0100
commite3354e2265bc442e4c7b84b806be482db88581a2 (patch)
tree21c79b24540c150ada3fe3b7c7941985327cc16f /src/kqueue.c
parentc6457cef92342d586d894504fdefc1bec1367725 (diff)
downloademacs-e3354e2265bc442e4c7b84b806be482db88581a2.tar.gz
emacs-e3354e2265bc442e4c7b84b806be482db88581a2.tar.bz2
emacs-e3354e2265bc442e4c7b84b806be482db88581a2.zip
Add kqueue support
* configure.ac (--with-file-notification): Add kqueue. (top): Remove special test for "${HAVE_NS}" and ${with_file_notification}, this is handled inside gfilenotify tests. Add kqueue tests. Use NOTIFY_CFLAGS and NOTIFY_LIBS instead of library specific variables. * src/Makefile.in: Use NOTIFY_CFLAGS and NOTIFY_LIBS. * src/emacs.c (main): Call globals_of_kqueue and syms_of_kqueue. * src/kqueue.c: New file. * src/lisp.h: Declare extern globals_of_kqueue and syms_of_kqueue.
Diffstat (limited to 'src/kqueue.c')
-rw-r--r--src/kqueue.c339
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 @@
+/* Filesystem notifications support with glib API.
+ Copyright (C) 2013-2015 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#ifdef HAVE_KQUEUE
+#include <stdio.h>
+#include <sys/event.h>
+#include "lisp.h"
+#include "coding.h"
+#include "termhooks.h"
+#include "keyboard.h"
+
+
+/* File handle for kqueue. */
+static int kqueuefd = -1;
+
+/* This is a list, elements are triples (DESCRIPTOR FILE FLAGS CALLBACK) */
+static Lisp_Object watch_list;
+
+#if 0
+/* This is the callback function for arriving signals from
+ g_file_monitor. It shall create a Lisp event, and put it into
+ Emacs input queue. */
+static gboolean
+dir_monitor_callback (GFileMonitor *monitor,
+ GFile *file,
+ GFile *other_file,
+ GFileMonitorEvent event_type,
+ gpointer user_data)
+{
+ Lisp_Object symbol, monitor_object, watch_object, flags;
+ char *name = g_file_get_parse_name (file);
+ char *oname = other_file ? g_file_get_parse_name (other_file) : NULL;
+
+ /* Determine event symbol. */
+ switch (event_type)
+ {
+ case G_FILE_MONITOR_EVENT_CHANGED:
+ symbol = Qchanged;
+ break;
+ case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
+ symbol = Qchanges_done_hint;
+ break;
+ case G_FILE_MONITOR_EVENT_DELETED:
+ symbol = Qdeleted;
+ break;
+ case G_FILE_MONITOR_EVENT_CREATED:
+ symbol = Qcreated;
+ break;
+ case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED:
+ symbol = Qattribute_changed;
+ break;
+ case G_FILE_MONITOR_EVENT_PRE_UNMOUNT:
+ symbol = Qpre_unmount;
+ break;
+ case G_FILE_MONITOR_EVENT_UNMOUNTED:
+ symbol = Qunmounted;
+ break;
+ case G_FILE_MONITOR_EVENT_MOVED:
+ symbol = Qmoved;
+ break;
+ default:
+ goto cleanup;
+ }
+
+ /* Determine callback function. */
+ monitor_object = make_pointer_integer (monitor);
+ eassert (INTEGERP (monitor_object));
+ watch_object = assq_no_quit (monitor_object, watch_list);
+
+ if (CONSP (watch_object))
+ {
+ struct input_event event;
+ Lisp_Object otail = oname ? list1 (build_string (oname)) : Qnil;
+
+ /* Check, whether event_type is expected. */
+ flags = XCAR (XCDR (XCDR (watch_object)));
+ if ((!NILP (Fmember (Qchange, flags)) &&
+ !NILP (Fmember (symbol, list5 (Qchanged, Qchanges_done_hint,
+ Qdeleted, Qcreated, Qmoved)))) ||
+ (!NILP (Fmember (Qattribute_change, flags)) &&
+ ((EQ (symbol, Qattribute_changed)))))
+ {
+ /* Construct an event. */
+ EVENT_INIT (event);
+ event.kind = FILE_NOTIFY_EVENT;
+ event.frame_or_window = Qnil;
+ event.arg = list2 (Fcons (monitor_object,
+ Fcons (symbol,
+ Fcons (build_string (name),
+ otail))),
+ XCAR (XCDR (XCDR (XCDR (watch_object)))));
+
+ /* Store it into the input event queue. */
+ kbd_buffer_store_event (&event);
+ // XD_DEBUG_MESSAGE ("%s", XD_OBJECT_TO_STRING (event.arg));
+ }
+
+ /* Cancel monitor if file or directory is deleted. */
+ if (!NILP (Fmember (symbol, list2 (Qdeleted, Qmoved))) &&
+ (strcmp (name, SSDATA (XCAR (XCDR (watch_object)))) == 0) &&
+ !g_file_monitor_is_cancelled (monitor))
+ g_file_monitor_cancel (monitor);
+ }
+
+ /* Cleanup. */
+ cleanup:
+ g_free (name);
+ g_free (oname);
+
+ return TRUE;
+}
+#endif /* 0 */
+
+DEFUN ("kqueue-add-watch", Fkqueue_add_watch, Skqueue_add_watch, 3, 3, 0,
+ doc: /* Add a watch for filesystem events pertaining to FILE.
+
+This arranges for filesystem events pertaining to FILE to be reported
+to Emacs. Use `gfile-rm-watch' to cancel the watch.
+
+Value is a descriptor for the added watch. If the file cannot be
+watched for some reason, this function signals a `file-notify-error' error.
+
+FLAGS is a list of conditions to set what will be watched for. It can
+include the following symbols:
+
+ `change' -- watch for file changes
+ `attribute-change' -- watch for file attributes changes, like
+ permissions or modification time
+ `watch-mounts' -- watch for mount events
+ `send-moved' -- pair `deleted' and `created' events caused by
+ file renames and send a single `renamed' event
+ instead
+
+When any event happens, Emacs will call the CALLBACK function passing
+it a single argument EVENT, which is of the form
+
+ (DESCRIPTOR ACTION FILE [FILE1])
+
+DESCRIPTOR is the same object as the one returned by this function.
+ACTION is the description of the event. It could be any one of the
+following:
+
+ `changed' -- FILE has changed
+ `changes-done-hint' -- a hint that this was probably the last change
+ in a set of changes
+ `deleted' -- FILE was deleted
+ `created' -- FILE was created
+ `attribute-changed' -- a FILE attribute was changed
+ `pre-unmount' -- the FILE location will soon be unmounted
+ `unmounted' -- the FILE location was unmounted
+ `moved' -- FILE was moved to FILE1
+
+FILE is the name of the file whose event is being reported. FILE1
+will be reported only in case of the `moved' event. */)
+ (Lisp_Object file, Lisp_Object flags, Lisp_Object callback)
+{
+ Lisp_Object watch_object;
+ GFile *gfile;
+ GFileMonitor *monitor;
+ GFileMonitorFlags gflags = G_FILE_MONITOR_NONE;
+ GError *gerror = NULL;
+
+ /* Check parameters. */
+ CHECK_STRING (file);
+ file = Fdirectory_file_name (Fexpand_file_name (file, Qnil));
+ if (NILP (Ffile_exists_p (file)))
+ report_file_error ("File does not exist", file);
+
+ CHECK_LIST (flags);
+
+ if (!FUNCTIONP (callback))
+ wrong_type_argument (Qinvalid_function, callback);
+
+ /* Create GFile name. */
+ // gfile = g_file_new_for_path (SSDATA (ENCODE_FILE (file)));
+
+ /* Assemble flags. */
+ // if (!NILP (Fmember (Qwatch_mounts, flags)))
+ // gflags |= G_FILE_MONITOR_WATCH_MOUNTS;
+ // if (!NILP (Fmember (Qsend_moved, flags)))
+ // gflags |= G_FILE_MONITOR_SEND_MOVED;
+
+ if (kqueuefd < 0)
+ {
+ kqueuefd = kqueue ();
+ if (kqueuefd < 0)
+ report_file_notify_error ("File watching is not available", Qnil);
+ watch_list = Qnil;
+ // add_read_fd (inotifyfd, &inotify_callback, NULL);
+ }
+
+
+}
+#if 0
+
+ mask = aspect_to_inotifymask (aspect);
+ encoded_file_name = ENCODE_FILE (file_name);
+ watchdesc = inotify_add_watch (inotifyfd, SSDATA (encoded_file_name), mask);
+ if (watchdesc == -1)
+ report_file_notify_error ("Could not add watch for file", file_name);
+
+ /* Enable watch. */
+ monitor = g_file_monitor (gfile, gflags, NULL, &gerror);
+ g_object_unref (gfile);
+ if (gerror)
+ {
+ char msg[1024];
+ strcpy (msg, gerror->message);
+ g_error_free (gerror);
+ xsignal1 (Qfile_notify_error, build_string (msg));
+ }
+ if (! monitor)
+ xsignal2 (Qfile_notify_error, build_string ("Cannot watch file"), file);
+
+ Lisp_Object watch_descriptor = make_pointer_integer (monitor);
+
+ /* Check the dicey assumption that make_pointer_integer is safe. */
+ if (! INTEGERP (watch_descriptor))
+ {
+ g_object_unref (monitor);
+ xsignal2 (Qfile_notify_error, build_string ("Unsupported file watcher"),
+ file);
+ }
+
+ /* The default rate limit is 800 msec. We adapt this. */
+ g_file_monitor_set_rate_limit (monitor, 100);
+
+ /* Subscribe to the "changed" signal. */
+ g_signal_connect (monitor, "changed",
+ (GCallback) dir_monitor_callback, NULL);
+
+ /* Store watch object in watch list. */
+ watch_object = list4 (watch_descriptor, file, flags, callback);
+ watch_list = Fcons (watch_object, watch_list);
+
+ return watch_descriptor;
+}
+
+DEFUN ("gfile-rm-watch", Fgfile_rm_watch, Sgfile_rm_watch, 1, 1, 0,
+ doc: /* Remove an existing WATCH-DESCRIPTOR.
+
+WATCH-DESCRIPTOR should be an object returned by `gfile-add-watch'. */)
+ (Lisp_Object watch_descriptor)
+{
+ Lisp_Object watch_object = assq_no_quit (watch_descriptor, watch_list);
+
+ if (! CONSP (watch_object))
+ xsignal2 (Qfile_notify_error, build_string ("Not a watch descriptor"),
+ watch_descriptor);
+
+ eassert (INTEGERP (watch_descriptor));
+ GFileMonitor *monitor = XINTPTR (watch_descriptor);
+ if (!g_file_monitor_is_cancelled (monitor) &&
+ !g_file_monitor_cancel (monitor))
+ xsignal2 (Qfile_notify_error, build_string ("Could not rm watch"),
+ watch_descriptor);
+
+ /* Remove watch descriptor from watch list. */
+ watch_list = Fdelq (watch_object, watch_list);
+
+ /* Cleanup. */
+ g_object_unref (monitor);
+
+ return Qt;
+}
+
+DEFUN ("gfile-valid-p", Fgfile_valid_p, Sgfile_valid_p, 1, 1, 0,
+ doc: /* "Check a watch specified by its WATCH-DESCRIPTOR.
+
+WATCH-DESCRIPTOR should be an object returned by `gfile-add-watch'.
+
+A watch can become invalid if the file or directory it watches is
+deleted, or if the watcher thread exits abnormally for any other
+reason. Removing the watch by calling `gfile-rm-watch' also makes it
+invalid. */)
+ (Lisp_Object watch_descriptor)
+{
+ Lisp_Object watch_object = Fassoc (watch_descriptor, watch_list);
+ if (NILP (watch_object))
+ return Qnil;
+ else
+ {
+ GFileMonitor *monitor = XINTPTR (watch_descriptor);
+ return g_file_monitor_is_cancelled (monitor) ? Qnil : Qt;
+ }
+}
+#endif /* 0 */
+
+
+void
+globals_of_kqueue (void)
+{
+ watch_list = Qnil;
+}
+
+void
+syms_of_kqueue (void)
+{
+ defsubr (&Skqueue_add_watch);
+ // defsubr (&Skqueue_rm_watch);
+ // defsubr (&Skqueue_valid_p);
+
+ /* Filter objects. */
+ DEFSYM (Qchange, "change");
+ DEFSYM (Qattribute_change, "attribute-change");
+ DEFSYM (Qwatch_mounts, "watch-mounts"); /* G_FILE_MONITOR_WATCH_MOUNTS */
+ DEFSYM (Qsend_moved, "send-moved"); /* G_FILE_MONITOR_SEND_MOVED */
+
+ /* Event types. */
+ DEFSYM (Qdelete, "delete"); /* NOTE_DELETE */
+ DEFSYM (Qwrite, "write"); /* NOTE_WRITE */
+ DEFSYM (Qextend, "extend"); /* NOTE_EXTEND */
+ DEFSYM (Qattrib, "attrib"); /* NOTE_ATTRIB */
+ DEFSYM (Qlink, "link"); /* NOTE_LINK */
+ DEFSYM (Qrename, "rename"); /* NOTE_RENAME */
+
+ staticpro (&watch_list);
+
+ Fprovide (intern_c_string ("kqueue"), Qnil);
+}
+
+#endif /* HAVE_KQUEUE */