root/src/gfilenotify.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. dir_monitor_callback
  2. DEFUN
  3. DEFUN
  4. DEFUN
  5. globals_of_gfilenotify
  6. syms_of_gfilenotify

     1 /* Filesystem notifications support with glib API.
     2    Copyright (C) 2013-2023 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 (at
     9 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 <https://www.gnu.org/licenses/>.  */
    18 
    19 #include <config.h>
    20 
    21 #include <gio/gio.h>
    22 #include "lisp.h"
    23 #include "coding.h"
    24 #include "termhooks.h"
    25 #include "keyboard.h"
    26 
    27 
    28 /* This is a list, elements are quadruples (DESCRIPTOR FILE FLAGS CALLBACK)  */
    29 static Lisp_Object watch_list;
    30 
    31 /* This is the callback function for arriving signals from
    32    g_file_monitor.  It shall create a Lisp event, and put it into
    33    Emacs input queue.  */
    34 static gboolean
    35 dir_monitor_callback (GFileMonitor *monitor,
    36                       GFile *file,
    37                       GFile *other_file,
    38                       GFileMonitorEvent event_type,
    39                       gpointer user_data)
    40 {
    41   Lisp_Object symbol, monitor_object, watch_object, flags;
    42   char *name = g_file_get_parse_name (file);
    43   char *oname = other_file ? g_file_get_parse_name (other_file) : NULL;
    44 
    45   /* Determine event symbol.  */
    46   switch (event_type)
    47     {
    48     case G_FILE_MONITOR_EVENT_CHANGED:
    49       symbol = Qchanged;
    50       break;
    51     case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
    52       symbol = Qchanges_done_hint;
    53       break;
    54     case G_FILE_MONITOR_EVENT_DELETED:
    55       symbol = Qdeleted;
    56       break;
    57     case G_FILE_MONITOR_EVENT_CREATED:
    58       symbol = Qcreated;
    59       break;
    60     case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED:
    61       symbol = Qattribute_changed;
    62       break;
    63     case G_FILE_MONITOR_EVENT_PRE_UNMOUNT:
    64       symbol = Qpre_unmount;
    65       break;
    66     case G_FILE_MONITOR_EVENT_UNMOUNTED:
    67       symbol = Qunmounted;
    68       break;
    69     case G_FILE_MONITOR_EVENT_MOVED:
    70       symbol = Qmoved;
    71       break;
    72     default:
    73       goto cleanup;
    74     }
    75 
    76   /* Determine callback function.  */
    77   monitor_object = make_pointer_integer (monitor);
    78   watch_object = assq_no_quit (monitor_object, watch_list);
    79 
    80   if (CONSP (watch_object))
    81     {
    82       struct input_event event;
    83       Lisp_Object otail = oname ? list1 (build_string (oname)) : Qnil;
    84 
    85       /* Check, whether event_type is expected.  */
    86       flags = XCAR (XCDR (XCDR (watch_object)));
    87       if ((!NILP (Fmember (Qchange, flags))
    88            && !NILP (Fmember (symbol, list5 (Qchanged, Qchanges_done_hint,
    89                                              Qdeleted, Qcreated, Qmoved))))
    90           || (!NILP (Fmember (Qattribute_change, flags))
    91               && EQ (symbol, Qattribute_changed)))
    92         {
    93           /* Construct an event.  */
    94           EVENT_INIT (event);
    95           event.kind = FILE_NOTIFY_EVENT;
    96           event.frame_or_window = Qnil;
    97           event.arg = list2 (Fcons (monitor_object,
    98                                     Fcons (symbol,
    99                                            Fcons (build_string (name),
   100                                                   otail))),
   101                              XCAR (XCDR (XCDR (XCDR (watch_object)))));
   102 
   103           /* Store it into the input event queue.  */
   104           kbd_buffer_store_event (&event);
   105           /* XD_DEBUG_MESSAGE ("%s", XD_OBJECT_TO_STRING (event.arg));  */
   106         }
   107 
   108       /* Cancel monitor if file or directory is deleted.  */
   109       if (!NILP (Fmember (symbol, list2 (Qdeleted, Qmoved)))
   110           && strcmp (name, SSDATA (XCAR (XCDR (watch_object)))) == 0
   111           && !g_file_monitor_is_cancelled (monitor))
   112         g_file_monitor_cancel (monitor);
   113     }
   114 
   115   /* Cleanup.  */
   116  cleanup:
   117   g_free (name);
   118   g_free (oname);
   119 
   120   return TRUE;
   121 }
   122 
   123 DEFUN ("gfile-add-watch", Fgfile_add_watch, Sgfile_add_watch, 3, 3, 0,
   124        doc: /* Add a watch for filesystem events pertaining to FILE.
   125 
   126 This arranges for filesystem events pertaining to FILE to be reported
   127 to Emacs.  Use `gfile-rm-watch' to cancel the watch.
   128 
   129 Value is a descriptor for the added watch.  If the file cannot be
   130 watched for some reason, this function signals a `file-notify-error' error.
   131 
   132 FLAGS is a list of conditions to set what will be watched for.  It can
   133 include the following symbols:
   134 
   135   `change'           -- watch for file changes
   136   `attribute-change' -- watch for file attributes changes, like
   137                         permissions or modification time
   138   `watch-mounts'     -- watch for mount events
   139   `send-moved'       -- pair `deleted' and `created' events caused by
   140                         file renames and send a single `renamed' event
   141                         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   GError *gerror = NULL;
   171 
   172   /* Check parameters.  */
   173   CHECK_STRING (file);
   174   file = Fdirectory_file_name (Fexpand_file_name (file, Qnil));
   175   if (NILP (Ffile_exists_p (file)))
   176     report_file_error ("File does not exist", file);
   177 
   178   if (!FUNCTIONP (callback))
   179     wrong_type_argument (Qinvalid_function, callback);
   180 
   181   /* Assemble flags.  */
   182   if (!NILP (Fmember (Qwatch_mounts, flags)))
   183     gflags |= G_FILE_MONITOR_WATCH_MOUNTS;
   184   if (!NILP (Fmember (Qsend_moved, flags)))
   185     gflags |= G_FILE_MONITOR_SEND_MOVED;
   186 
   187   /* Create GFile name.  */
   188   gfile = g_file_new_for_path (SSDATA (ENCODE_FILE (file)));
   189 
   190   /* Enable watch.  */
   191   monitor = g_file_monitor (gfile, gflags, NULL, &gerror);
   192   g_object_unref (gfile);
   193   if (gerror)
   194     {
   195       char msg[1024];
   196       strcpy (msg, gerror->message);
   197       g_error_free (gerror);
   198       xsignal1 (Qfile_notify_error, build_string (msg));
   199     }
   200   if (! monitor)
   201     xsignal2 (Qfile_notify_error, build_string ("Cannot watch file"), file);
   202 
   203   Lisp_Object watch_descriptor = make_pointer_integer_unsafe (monitor);
   204 
   205   if (! (FIXNUMP (watch_descriptor)
   206          && XFIXNUMPTR (watch_descriptor) == monitor))
   207     {
   208       g_object_unref (monitor);
   209       xsignal2 (Qfile_notify_error, build_string ("Unsupported file watcher"),
   210                 file);
   211     }
   212 
   213   /* The default rate limit is 800 msec.  We adapt this.  */
   214   g_file_monitor_set_rate_limit (monitor, 100);
   215 
   216   /* Subscribe to the "changed" signal.  */
   217   g_signal_connect (monitor, "changed",
   218                     (GCallback) dir_monitor_callback, NULL);
   219 
   220   /* Store watch object in watch list.  */
   221   watch_object = list4 (watch_descriptor, file, flags, callback);
   222   watch_list = Fcons (watch_object, watch_list);
   223 
   224   return watch_descriptor;
   225 }
   226 
   227 DEFUN ("gfile-rm-watch", Fgfile_rm_watch, Sgfile_rm_watch, 1, 1, 0,
   228        doc: /* Remove an existing WATCH-DESCRIPTOR.
   229 
   230 WATCH-DESCRIPTOR should be an object returned by `gfile-add-watch'.  */)
   231      (Lisp_Object watch_descriptor)
   232 {
   233   Lisp_Object watch_object = assq_no_quit (watch_descriptor, watch_list);
   234 
   235   if (! CONSP (watch_object))
   236     xsignal2 (Qfile_notify_error, build_string ("Not a watch descriptor"),
   237               watch_descriptor);
   238 
   239   eassert (FIXNUMP (watch_descriptor));
   240   GFileMonitor *monitor = XFIXNUMPTR (watch_descriptor);
   241   if (!g_file_monitor_is_cancelled (monitor)
   242       && !g_file_monitor_cancel (monitor))
   243     xsignal2 (Qfile_notify_error, build_string ("Could not rm watch"),
   244               watch_descriptor);
   245 
   246   /* Remove watch descriptor from watch list.  */
   247   watch_list = Fdelq (watch_object, watch_list);
   248 
   249   /* Cleanup.  */
   250   g_object_unref (monitor);
   251 
   252   return Qt;
   253 }
   254 
   255 DEFUN ("gfile-valid-p", Fgfile_valid_p, Sgfile_valid_p, 1, 1, 0,
   256        doc: /* Check a watch specified by its WATCH-DESCRIPTOR.
   257 
   258 WATCH-DESCRIPTOR should be an object returned by `gfile-add-watch'.
   259 
   260 A watch can become invalid if the file or directory it watches is
   261 deleted, or if the watcher thread exits abnormally for any other
   262 reason.  Removing the watch by calling `gfile-rm-watch' also makes it
   263 invalid.  */)
   264      (Lisp_Object watch_descriptor)
   265 {
   266   Lisp_Object watch_object = Fassoc (watch_descriptor, watch_list, Qnil);
   267   if (NILP (watch_object))
   268     return Qnil;
   269   else
   270     {
   271       GFileMonitor *monitor = XFIXNUMPTR (watch_descriptor);
   272       return g_file_monitor_is_cancelled (monitor) ? Qnil : Qt;
   273     }
   274 }
   275 
   276 DEFUN ("gfile-monitor-name", Fgfile_monitor_name, Sgfile_monitor_name, 1, 1, 0,
   277        doc: /* Return the internal monitor name for WATCH-DESCRIPTOR.
   278 
   279 The result is a symbol, either `GInotifyFileMonitor',
   280 `GKqueueFileMonitor', `GFamFileMonitor', or `GPollFileMonitor'.
   281 
   282 WATCH-DESCRIPTOR should be an object returned by `gfile-add-watch'.
   283 If WATCH-DESCRIPTOR is not valid, nil is returned.  */)
   284      (Lisp_Object watch_descriptor)
   285 {
   286   if (NILP (Fgfile_valid_p (watch_descriptor)))
   287     return Qnil;
   288   else
   289     {
   290       GFileMonitor *monitor = XFIXNUMPTR (watch_descriptor);
   291       return intern (G_OBJECT_TYPE_NAME (monitor));
   292     }
   293 }
   294 
   295 
   296 void
   297 globals_of_gfilenotify (void)
   298 {
   299 #if ! GLIB_CHECK_VERSION (2, 36, 0)
   300   g_type_init ();
   301 #endif
   302   watch_list = Qnil;
   303 }
   304 
   305 void
   306 syms_of_gfilenotify (void)
   307 {
   308   defsubr (&Sgfile_add_watch);
   309   defsubr (&Sgfile_rm_watch);
   310   defsubr (&Sgfile_valid_p);
   311   defsubr (&Sgfile_monitor_name);
   312 
   313   /* Filter objects.  */
   314   DEFSYM (Qchange, "change");
   315   DEFSYM (Qattribute_change, "attribute-change");
   316   DEFSYM (Qwatch_mounts, "watch-mounts"); /* G_FILE_MONITOR_WATCH_MOUNTS  */
   317   DEFSYM (Qsend_moved, "send-moved");   /* G_FILE_MONITOR_SEND_MOVED  */
   318 
   319   /* Event types.  */
   320   DEFSYM (Qchanged, "changed"); /* G_FILE_MONITOR_EVENT_CHANGED  */
   321   DEFSYM (Qchanges_done_hint, "changes-done-hint");
   322                                 /* G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT  */
   323   DEFSYM (Qdeleted, "deleted"); /* G_FILE_MONITOR_EVENT_DELETED  */
   324   DEFSYM (Qcreated, "created"); /* G_FILE_MONITOR_EVENT_CREATED  */
   325   DEFSYM (Qattribute_changed, "attribute-changed");
   326                                 /* G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED  */
   327   DEFSYM (Qpre_unmount, "pre-unmount"); /* G_FILE_MONITOR_EVENT_PRE_UNMOUNT  */
   328   DEFSYM (Qunmounted, "unmounted");     /* G_FILE_MONITOR_EVENT_UNMOUNTED  */
   329   DEFSYM (Qmoved, "moved");     /* G_FILE_MONITOR_EVENT_MOVED  */
   330 
   331   staticpro (&watch_list);
   332 
   333   Fprovide (intern_c_string ("gfilenotify"), Qnil);
   334 }

/* [<][>][^][v][top][bottom][index][help] */