blob: acc5b0794d777c6e931d081a721c5d205645fcd8 [file] [log] [blame]
Bram Moolenaaredf3f972016-08-29 22:49:24 +02001/* vi:set ts=8 sts=4 sw=4 noet:
Bram Moolenaar071d4272004-06-13 20:20:40 +00002 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
Bram Moolenaard0573012017-10-28 21:11:06 +020011 * os_macosx.m -- Mac specific things for Mac OS X.
Bram Moolenaar071d4272004-06-13 20:20:40 +000012 */
13
Bram Moolenaarf536bf62018-03-06 17:55:01 +010014/* Suppress compiler warnings to non-C89 code. */
15#if defined(__clang__) && defined(__STRICT_ANSI__)
16# pragma clang diagnostic push
17# pragma clang diagnostic ignored "-Wc99-extensions"
18# pragma clang diagnostic push
19# pragma clang diagnostic ignored "-Wdeclaration-after-statement"
20#endif
21
Bram Moolenaar423f9722010-10-10 17:08:43 +020022/* Avoid a conflict for the definition of Boolean between Mac header files and
23 * X11 header files. */
24#define NO_X11_INCLUDES
25
Paul Ollis65745772022-06-05 16:55:54 +010026#include <stdbool.h>
27#include <mach/boolean.h>
28#include <sys/errno.h>
29#include <stdlib.h>
30
31#include <dispatch/dispatch.h>
32
Bram Moolenaar164fca32010-07-14 13:58:07 +020033#include "vim.h"
Bram Moolenaard0573012017-10-28 21:11:06 +020034#import <AppKit/AppKit.h>
Bram Moolenaar164fca32010-07-14 13:58:07 +020035
36
Bram Moolenaare00289d2010-08-14 21:56:42 +020037/*
38 * Clipboard support for the console.
Bram Moolenaare00289d2010-08-14 21:56:42 +020039 */
Bram Moolenaarbe7529e2020-08-13 21:05:39 +020040#if defined(FEAT_CLIPBOARD)
Bram Moolenaar164fca32010-07-14 13:58:07 +020041
Bram Moolenaar66bd1c92010-07-14 20:31:44 +020042/* Used to identify clipboard data copied from Vim. */
43
44NSString *VimPboardType = @"VimPboardType";
45
Bram Moolenaar164fca32010-07-14 13:58:07 +020046 void
Bram Moolenaar2fc39ae2019-06-14 23:27:29 +020047clip_mch_lose_selection(Clipboard_T *cbd UNUSED)
Bram Moolenaar164fca32010-07-14 13:58:07 +020048{
49}
50
51
52 int
Bram Moolenaar2fc39ae2019-06-14 23:27:29 +020053clip_mch_own_selection(Clipboard_T *cbd UNUSED)
Bram Moolenaar164fca32010-07-14 13:58:07 +020054{
55 /* This is called whenever there is a new selection and 'guioptions'
56 * contains the "a" flag (automatically copy selection). Return TRUE, else
57 * the "a" flag does nothing. Note that there is no concept of "ownership"
58 * of the clipboard in Mac OS X.
59 */
60 return TRUE;
61}
62
63
64 void
Bram Moolenaar2fc39ae2019-06-14 23:27:29 +020065clip_mch_request_selection(Clipboard_T *cbd)
Bram Moolenaar164fca32010-07-14 13:58:07 +020066{
67 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
68
69 NSPasteboard *pb = [NSPasteboard generalPasteboard];
Bram Moolenaar1d3dbcf2018-10-08 20:07:39 +020070#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
Bram Moolenaard595a192018-06-17 15:01:04 +020071 NSArray *supportedTypes = [NSArray arrayWithObjects:VimPboardType,
72 NSPasteboardTypeString, nil];
73#else
Bram Moolenaar164fca32010-07-14 13:58:07 +020074 NSArray *supportedTypes = [NSArray arrayWithObjects:VimPboardType,
75 NSStringPboardType, nil];
Bram Moolenaard595a192018-06-17 15:01:04 +020076#endif
Bram Moolenaar164fca32010-07-14 13:58:07 +020077 NSString *bestType = [pb availableTypeFromArray:supportedTypes];
78 if (!bestType) goto releasepool;
79
Bram Moolenaar54b08a52011-06-20 00:25:44 +020080 int motion_type = MAUTO;
Bram Moolenaar164fca32010-07-14 13:58:07 +020081 NSString *string = nil;
82
83 if ([bestType isEqual:VimPboardType])
84 {
85 /* This type should consist of an array with two objects:
86 * 1. motion type (NSNumber)
87 * 2. text (NSString)
Bram Moolenaard595a192018-06-17 15:01:04 +020088 * If this is not the case we fall back on using NSPasteboardTypeString.
Bram Moolenaar164fca32010-07-14 13:58:07 +020089 */
90 id plist = [pb propertyListForType:VimPboardType];
91 if ([plist isKindOfClass:[NSArray class]] && [plist count] == 2)
92 {
93 id obj = [plist objectAtIndex:1];
94 if ([obj isKindOfClass:[NSString class]])
95 {
96 motion_type = [[plist objectAtIndex:0] intValue];
97 string = obj;
98 }
99 }
100 }
101
102 if (!string)
103 {
Bram Moolenaard595a192018-06-17 15:01:04 +0200104 /* Use NSPasteboardTypeString. The motion type is detected automatically.
Bram Moolenaar164fca32010-07-14 13:58:07 +0200105 */
Bram Moolenaar1d3dbcf2018-10-08 20:07:39 +0200106#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
Bram Moolenaard595a192018-06-17 15:01:04 +0200107 NSMutableString *mstring =
108 [[pb stringForType:NSPasteboardTypeString] mutableCopy];
109#else
Bram Moolenaar164fca32010-07-14 13:58:07 +0200110 NSMutableString *mstring =
111 [[pb stringForType:NSStringPboardType] mutableCopy];
Bram Moolenaard595a192018-06-17 15:01:04 +0200112#endif
Bram Moolenaar164fca32010-07-14 13:58:07 +0200113 if (!mstring) goto releasepool;
114
115 /* Replace unrecognized end-of-line sequences with \x0a (line feed). */
116 NSRange range = { 0, [mstring length] };
117 unsigned n = [mstring replaceOccurrencesOfString:@"\x0d\x0a"
118 withString:@"\x0a" options:0
119 range:range];
120 if (0 == n)
121 {
122 n = [mstring replaceOccurrencesOfString:@"\x0d" withString:@"\x0a"
123 options:0 range:range];
124 }
Bram Moolenaard43848c2010-07-14 14:28:26 +0200125
Bram Moolenaar164fca32010-07-14 13:58:07 +0200126 string = mstring;
127 }
128
Bram Moolenaar54b08a52011-06-20 00:25:44 +0200129 /* Default to MAUTO, uses MCHAR or MLINE depending on trailing NL. */
Bram Moolenaar164fca32010-07-14 13:58:07 +0200130 if (!(MCHAR == motion_type || MLINE == motion_type || MBLOCK == motion_type
131 || MAUTO == motion_type))
Bram Moolenaar54b08a52011-06-20 00:25:44 +0200132 motion_type = MAUTO;
Bram Moolenaar164fca32010-07-14 13:58:07 +0200133
134 char_u *str = (char_u*)[string UTF8String];
135 int len = [string lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
136
Bram Moolenaar164fca32010-07-14 13:58:07 +0200137 if (input_conv.vc_type != CONV_NONE)
138 str = string_convert(&input_conv, str, &len);
Bram Moolenaar164fca32010-07-14 13:58:07 +0200139
140 if (str)
141 clip_yank_selection(motion_type, str, len, cbd);
142
Bram Moolenaar164fca32010-07-14 13:58:07 +0200143 if (input_conv.vc_type != CONV_NONE)
144 vim_free(str);
Bram Moolenaar164fca32010-07-14 13:58:07 +0200145
146releasepool:
147 [pool release];
148}
149
150
151/*
152 * Send the current selection to the clipboard.
153 */
154 void
Bram Moolenaar2fc39ae2019-06-14 23:27:29 +0200155clip_mch_set_selection(Clipboard_T *cbd)
Bram Moolenaar164fca32010-07-14 13:58:07 +0200156{
157 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
158
159 /* If the '*' register isn't already filled in, fill it in now. */
160 cbd->owned = TRUE;
161 clip_get_selection(cbd);
162 cbd->owned = FALSE;
Bram Moolenaard43848c2010-07-14 14:28:26 +0200163
Bram Moolenaar164fca32010-07-14 13:58:07 +0200164 /* Get the text to put on the pasteboard. */
165 long_u llen = 0; char_u *str = 0;
166 int motion_type = clip_convert_selection(&str, &llen, cbd);
167 if (motion_type < 0)
168 goto releasepool;
169
170 /* TODO: Avoid overflow. */
171 int len = (int)llen;
Bram Moolenaar164fca32010-07-14 13:58:07 +0200172 if (output_conv.vc_type != CONV_NONE)
173 {
174 char_u *conv_str = string_convert(&output_conv, str, &len);
175 if (conv_str)
176 {
177 vim_free(str);
178 str = conv_str;
179 }
180 }
Bram Moolenaar164fca32010-07-14 13:58:07 +0200181
182 if (len > 0)
183 {
184 NSString *string = [[NSString alloc]
185 initWithBytes:str length:len encoding:NSUTF8StringEncoding];
186
187 /* See clip_mch_request_selection() for info on pasteboard types. */
188 NSPasteboard *pb = [NSPasteboard generalPasteboard];
Bram Moolenaar1d3dbcf2018-10-08 20:07:39 +0200189#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
Bram Moolenaard595a192018-06-17 15:01:04 +0200190 NSArray *supportedTypes = [NSArray arrayWithObjects:VimPboardType,
191 NSPasteboardTypeString, nil];
192#else
Bram Moolenaar164fca32010-07-14 13:58:07 +0200193 NSArray *supportedTypes = [NSArray arrayWithObjects:VimPboardType,
194 NSStringPboardType, nil];
Bram Moolenaard595a192018-06-17 15:01:04 +0200195#endif
Bram Moolenaar164fca32010-07-14 13:58:07 +0200196 [pb declareTypes:supportedTypes owner:nil];
197
198 NSNumber *motion = [NSNumber numberWithInt:motion_type];
199 NSArray *plist = [NSArray arrayWithObjects:motion, string, nil];
200 [pb setPropertyList:plist forType:VimPboardType];
201
Bram Moolenaar1d3dbcf2018-10-08 20:07:39 +0200202#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
Bram Moolenaard595a192018-06-17 15:01:04 +0200203 [pb setString:string forType:NSPasteboardTypeString];
204#else
Bram Moolenaar164fca32010-07-14 13:58:07 +0200205 [pb setString:string forType:NSStringPboardType];
Bram Moolenaard595a192018-06-17 15:01:04 +0200206#endif
Bram Moolenaard43848c2010-07-14 14:28:26 +0200207
Bram Moolenaar164fca32010-07-14 13:58:07 +0200208 [string release];
209 }
210
211 vim_free(str);
212releasepool:
213 [pool release];
214}
215
216#endif /* FEAT_CLIPBOARD */
Bram Moolenaarf536bf62018-03-06 17:55:01 +0100217
Paul Ollis65745772022-06-05 16:55:54 +0100218#ifdef FEAT_RELTIME
219/*
220 * The following timer code is based on a Gist by Jorgen Lundman:
221 *
222 * https://gist.github.com/lundman
223 */
224
225typedef struct macos_timer macos_timer_T;
226
227 static void
228_timer_cancel(void *arg UNUSED)
229{
230 // This is not currently used, but it might be useful in the future and
231 // it is non-trivial enough to provide as usable implementation.
232# if 0
233 macos_timer_T *timerid = (macos_timer_T *)arg;
234
235 dispatch_release(timerid->tim_timer);
236 dispatch_release(timerid->tim_queue);
237 timerid->tim_timer = NULL;
238 timerid->tim_queue = NULL;
239 free(timerid);
240# endif
241}
242
243 static void
244_timer_handler(void *arg)
245{
246 macos_timer_T *timerid = (macos_timer_T *)arg;
247 union sigval sv;
248
249 sv.sival_ptr = timerid->tim_arg;
250
251 if (timerid->tim_func != NULL)
252 timerid->tim_func(sv);
253}
254
255 static uint64_t
256itime_to_ns(const struct timespec *it)
257{
258 time_t sec = it->tv_sec;
259 long nsec = it->tv_nsec;
260 uint64_t ns = NSEC_PER_SEC * sec + nsec;
261
262 return ns == 0 ? DISPATCH_TIME_FOREVER : ns;
263}
264
265/*
266 * A partial emulation of the POSIX timer_create function.
267 *
268 * The limitations and differences include:
269 *
270 * - Only CLOCK_REALTIME and CLOCK_MONOTONIC are supported as clockid
271 * values.
272 * - Even if CLOCK_REALTIME is specified, internally the mach_absolute_time
273 * source is used internally.
274 * - The only notification method supported is SIGEV_THREAD.
275 */
276 inline int
277timer_create(clockid_t clockid, struct sigevent *sevp, timer_t *timerid)
278{
279 macos_timer_T *timer = NULL;
280
281 // We only support real time and monotonic clocks; and SIGEV_THREAD
282 // notification. In practice, there is no difference between the two
283 // types of clocks on MacOS - we always use the mach_machine_time
284 // source.
285 if ( (clockid != CLOCK_REALTIME && clockid != CLOCK_MONOTONIC)
286 || sevp->sigev_notify != SIGEV_THREAD)
287 {
288 semsg("clockid: %d %d", clockid, CLOCK_REALTIME);
289 semsg("notify: %d %d", sevp->sigev_notify, SIGEV_THREAD);
290 errno = ENOTSUP;
291 return -1;
292 }
293
294 timer = (macos_timer_T *)malloc(sizeof(macos_timer_T));
295 if (timer == NULL)
296 {
297 errno = ENOMEM;
298 return -1;
299 }
300 *timerid = timer;
301
302 timer->tim_queue = dispatch_queue_create(
303 "org.vim.timerqueue", NULL);
304 if (timer->tim_queue == NULL)
305 {
306 errno = ENOMEM;
307 return -1;
308 }
309
310 timer->tim_timer = dispatch_source_create(
311 DISPATCH_SOURCE_TYPE_TIMER, 0, 0, timer->tim_queue);
312 if (timer->tim_timer == NULL)
313 {
314 errno = ENOMEM;
315 return -1;
316 }
317
318 timer->tim_func = sevp->sigev_notify_function;
319 timer->tim_arg = sevp->sigev_value.sival_ptr;
320
321 dispatch_set_context(timer->tim_timer, timer);
322 dispatch_source_set_event_handler_f(timer->tim_timer, _timer_handler);
323 dispatch_source_set_cancel_handler_f(timer->tim_timer, _timer_cancel);
324
325 dispatch_resume(timer->tim_timer);
326
327 return 0;
328}
329
330/*
331 * A partial emulation of the POSIX timer_settime function.
332 *
333 * The limitations and differences include:
334 *
335 * - The flags argument is ignored. The supplied new_value is therfore
336 * always treated as a relative time.
337 * - The old_value argument is ignored.
338 */
339 int
340timer_settime(
341 timer_t timerid,
342 int unused_flags UNUSED,
343 const struct itimerspec *new_value,
344 struct itimerspec *old_value UNUSED)
345{
346 uint64_t first_shot = itime_to_ns(&new_value->it_value);
347
348 if (timerid == NULL)
349 return 0;
350
351 if (first_shot == DISPATCH_TIME_FOREVER)
352 {
353 dispatch_source_set_timer(
354 timerid->tim_timer, first_shot, first_shot, 0);
355 }
356 else
357 {
358 uint64_t interval = itime_to_ns(&new_value->it_interval);
359
360 dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, first_shot);
361 dispatch_source_set_timer(timerid->tim_timer, start, interval, 0);
362 }
363
364 return 0;
365}
366
367/*
368 * An emulation of the POSIX timer_delete function.
369 *
370 * Disabled because it is not currently used, but an implemented provided
371 * for completeness and possible future use.
372 */
Paul Ollis65745772022-06-05 16:55:54 +0100373 int
374timer_delete(timer_t timerid)
375{
376 /* Calls _timer_cancel() */
377 if (timerid != NULL)
378 dispatch_source_cancel(timerid->tim_timer);
379
380 return 0;
381}
Paul Ollis65745772022-06-05 16:55:54 +0100382
383#endif /* FEAT_RELTIME */
384
Bram Moolenaarf536bf62018-03-06 17:55:01 +0100385/* Lift the compiler warning suppression. */
386#if defined(__clang__) && defined(__STRICT_ANSI__)
387# pragma clang diagnostic pop
388# pragma clang diagnostic pop
389#endif