summaryrefslogtreecommitdiffstats
path: root/src/os_macosx.m
diff options
context:
space:
mode:
Diffstat (limited to 'src/os_macosx.m')
-rw-r--r--src/os_macosx.m176
1 files changed, 176 insertions, 0 deletions
diff --git a/src/os_macosx.m b/src/os_macosx.m
index 0299fe4134..1fdfbc70dd 100644
--- a/src/os_macosx.m
+++ b/src/os_macosx.m
@@ -23,6 +23,13 @@
* X11 header files. */
#define NO_X11_INCLUDES
+#include <stdbool.h>
+#include <mach/boolean.h>
+#include <sys/errno.h>
+#include <stdlib.h>
+
+#include <dispatch/dispatch.h>
+
#include "vim.h"
#import <AppKit/AppKit.h>
@@ -208,6 +215,175 @@ releasepool:
#endif /* FEAT_CLIPBOARD */
+#ifdef FEAT_RELTIME
+/*
+ * The following timer code is based on a Gist by Jorgen Lundman:
+ *
+ * https://gist.github.com/lundman
+ */
+
+typedef struct macos_timer macos_timer_T;
+
+ static void
+_timer_cancel(void *arg UNUSED)
+{
+ // This is not currently used, but it might be useful in the future and
+ // it is non-trivial enough to provide as usable implementation.
+# if 0
+ macos_timer_T *timerid = (macos_timer_T *)arg;
+
+ dispatch_release(timerid->tim_timer);
+ dispatch_release(timerid->tim_queue);
+ timerid->tim_timer = NULL;
+ timerid->tim_queue = NULL;
+ free(timerid);
+# endif
+}
+
+ static void
+_timer_handler(void *arg)
+{
+ macos_timer_T *timerid = (macos_timer_T *)arg;
+ union sigval sv;
+
+ sv.sival_ptr = timerid->tim_arg;
+
+ if (timerid->tim_func != NULL)
+ timerid->tim_func(sv);
+}
+
+ static uint64_t
+itime_to_ns(const struct timespec *it)
+{
+ time_t sec = it->tv_sec;
+ long nsec = it->tv_nsec;
+ uint64_t ns = NSEC_PER_SEC * sec + nsec;
+
+ return ns == 0 ? DISPATCH_TIME_FOREVER : ns;
+}
+
+/*
+ * A partial emulation of the POSIX timer_create function.
+ *
+ * The limitations and differences include:
+ *
+ * - Only CLOCK_REALTIME and CLOCK_MONOTONIC are supported as clockid
+ * values.
+ * - Even if CLOCK_REALTIME is specified, internally the mach_absolute_time
+ * source is used internally.
+ * - The only notification method supported is SIGEV_THREAD.
+ */
+ inline int
+timer_create(clockid_t clockid, struct sigevent *sevp, timer_t *timerid)
+{
+ macos_timer_T *timer = NULL;
+
+ // We only support real time and monotonic clocks; and SIGEV_THREAD
+ // notification. In practice, there is no difference between the two
+ // types of clocks on MacOS - we always use the mach_machine_time
+ // source.
+ if ( (clockid != CLOCK_REALTIME && clockid != CLOCK_MONOTONIC)
+ || sevp->sigev_notify != SIGEV_THREAD)
+ {
+ semsg("clockid: %d %d", clockid, CLOCK_REALTIME);
+ semsg("notify: %d %d", sevp->sigev_notify, SIGEV_THREAD);
+ errno = ENOTSUP;
+ return -1;
+ }
+
+ timer = (macos_timer_T *)malloc(sizeof(macos_timer_T));
+ if (timer == NULL)
+ {
+ errno = ENOMEM;
+ return -1;
+ }
+ *timerid = timer;
+
+ timer->tim_queue = dispatch_queue_create(
+ "org.vim.timerqueue", NULL);
+ if (timer->tim_queue == NULL)
+ {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ timer->tim_timer = dispatch_source_create(
+ DISPATCH_SOURCE_TYPE_TIMER, 0, 0, timer->tim_queue);
+ if (timer->tim_timer == NULL)
+ {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ timer->tim_func = sevp->sigev_notify_function;
+ timer->tim_arg = sevp->sigev_value.sival_ptr;
+
+ dispatch_set_context(timer->tim_timer, timer);
+ dispatch_source_set_event_handler_f(timer->tim_timer, _timer_handler);
+ dispatch_source_set_cancel_handler_f(timer->tim_timer, _timer_cancel);
+
+ dispatch_resume(timer->tim_timer);
+
+ return 0;
+}
+
+/*
+ * A partial emulation of the POSIX timer_settime function.
+ *
+ * The limitations and differences include:
+ *
+ * - The flags argument is ignored. The supplied new_value is therfore
+ * always treated as a relative time.
+ * - The old_value argument is ignored.
+ */
+ int
+timer_settime(
+ timer_t timerid,
+ int unused_flags UNUSED,
+ const struct itimerspec *new_value,
+ struct itimerspec *old_value UNUSED)
+{
+ uint64_t first_shot = itime_to_ns(&new_value->it_value);
+
+ if (timerid == NULL)
+ return 0;
+
+ if (first_shot == DISPATCH_TIME_FOREVER)
+ {
+ dispatch_source_set_timer(
+ timerid->tim_timer, first_shot, first_shot, 0);
+ }
+ else
+ {
+ uint64_t interval = itime_to_ns(&new_value->it_interval);
+
+ dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, first_shot);
+ dispatch_source_set_timer(timerid->tim_timer, start, interval, 0);
+ }
+
+ return 0;
+}
+
+/*
+ * An emulation of the POSIX timer_delete function.
+ *
+ * Disabled because it is not currently used, but an implemented provided
+ * for completeness and possible future use.
+ */
+#if 0
+ int
+timer_delete(timer_t timerid)
+{
+ /* Calls _timer_cancel() */
+ if (timerid != NULL)
+ dispatch_source_cancel(timerid->tim_timer);
+
+ return 0;
+}
+#endif
+
+#endif /* FEAT_RELTIME */
+
/* Lift the compiler warning suppression. */
#if defined(__clang__) && defined(__STRICT_ANSI__)
# pragma clang diagnostic pop