diff options
Diffstat (limited to 'src/os_macosx.m')
-rw-r--r-- | src/os_macosx.m | 176 |
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 |