mirror of
https://github.com/vim/vim
synced 2025-03-16 06:47:52 +01:00
Problem: Warnings for function declarations. Solution: Add argument types. (Michael Jarvis, closes #12277)
524 lines
13 KiB
Objective-C
524 lines
13 KiB
Objective-C
/* vi:set ts=8 sts=4 sw=4 noet:
|
|
*
|
|
* VIM - Vi IMproved by Bram Moolenaar
|
|
*
|
|
* Do ":help uganda" in Vim to read copying and usage conditions.
|
|
* Do ":help credits" in Vim to see a list of people who contributed.
|
|
* See README.txt for an overview of the Vim source code.
|
|
*/
|
|
|
|
/*
|
|
* os_macosx.m -- Mac specific things for Mac OS X.
|
|
*/
|
|
|
|
/* Suppress compiler warnings to non-C89 code. */
|
|
#if defined(__clang__) && defined(__STRICT_ANSI__)
|
|
# pragma clang diagnostic push
|
|
# pragma clang diagnostic ignored "-Wc99-extensions"
|
|
# pragma clang diagnostic push
|
|
# pragma clang diagnostic ignored "-Wdeclaration-after-statement"
|
|
#endif
|
|
|
|
/* Avoid a conflict for the definition of Boolean between Mac header files and
|
|
* X11 header files. */
|
|
#define NO_X11_INCLUDES
|
|
|
|
#include <stdbool.h>
|
|
#include <mach/boolean.h>
|
|
#include <sys/errno.h>
|
|
#include <stdlib.h>
|
|
|
|
#ifdef FEAT_RELTIME
|
|
#include <dispatch/dispatch.h>
|
|
#endif
|
|
|
|
#include "vim.h"
|
|
#import <AppKit/AppKit.h>
|
|
|
|
|
|
/*
|
|
* Clipboard support for the console.
|
|
*/
|
|
#if defined(FEAT_CLIPBOARD)
|
|
|
|
/* Used to identify clipboard data copied from Vim. */
|
|
|
|
NSString *VimPboardType = @"VimPboardType";
|
|
|
|
void
|
|
clip_mch_lose_selection(Clipboard_T *cbd UNUSED)
|
|
{
|
|
}
|
|
|
|
|
|
int
|
|
clip_mch_own_selection(Clipboard_T *cbd UNUSED)
|
|
{
|
|
/* This is called whenever there is a new selection and 'guioptions'
|
|
* contains the "a" flag (automatically copy selection). Return TRUE, else
|
|
* the "a" flag does nothing. Note that there is no concept of "ownership"
|
|
* of the clipboard in Mac OS X.
|
|
*/
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void
|
|
clip_mch_request_selection(Clipboard_T *cbd)
|
|
{
|
|
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
|
|
|
NSPasteboard *pb = [NSPasteboard generalPasteboard];
|
|
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
|
|
NSArray *supportedTypes = [NSArray arrayWithObjects:VimPboardType,
|
|
NSPasteboardTypeString, nil];
|
|
#else
|
|
NSArray *supportedTypes = [NSArray arrayWithObjects:VimPboardType,
|
|
NSStringPboardType, nil];
|
|
#endif
|
|
NSString *bestType = [pb availableTypeFromArray:supportedTypes];
|
|
if (!bestType) goto releasepool;
|
|
|
|
int motion_type = MAUTO;
|
|
NSString *string = nil;
|
|
|
|
if ([bestType isEqual:VimPboardType])
|
|
{
|
|
/* This type should consist of an array with two objects:
|
|
* 1. motion type (NSNumber)
|
|
* 2. text (NSString)
|
|
* If this is not the case we fall back on using NSPasteboardTypeString.
|
|
*/
|
|
id plist = [pb propertyListForType:VimPboardType];
|
|
if ([plist isKindOfClass:[NSArray class]] && [plist count] == 2)
|
|
{
|
|
id obj = [plist objectAtIndex:1];
|
|
if ([obj isKindOfClass:[NSString class]])
|
|
{
|
|
motion_type = [[plist objectAtIndex:0] intValue];
|
|
string = obj;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!string)
|
|
{
|
|
/* Use NSPasteboardTypeString. The motion type is detected automatically.
|
|
*/
|
|
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
|
|
NSMutableString *mstring =
|
|
[[pb stringForType:NSPasteboardTypeString] mutableCopy];
|
|
#else
|
|
NSMutableString *mstring =
|
|
[[pb stringForType:NSStringPboardType] mutableCopy];
|
|
#endif
|
|
if (!mstring) goto releasepool;
|
|
|
|
/* Replace unrecognized end-of-line sequences with \x0a (line feed). */
|
|
NSRange range = { 0, [mstring length] };
|
|
unsigned n = [mstring replaceOccurrencesOfString:@"\x0d\x0a"
|
|
withString:@"\x0a" options:0
|
|
range:range];
|
|
if (0 == n)
|
|
{
|
|
n = [mstring replaceOccurrencesOfString:@"\x0d" withString:@"\x0a"
|
|
options:0 range:range];
|
|
}
|
|
|
|
string = mstring;
|
|
}
|
|
|
|
/* Default to MAUTO, uses MCHAR or MLINE depending on trailing NL. */
|
|
if (!(MCHAR == motion_type || MLINE == motion_type || MBLOCK == motion_type
|
|
|| MAUTO == motion_type))
|
|
motion_type = MAUTO;
|
|
|
|
char_u *str = (char_u*)[string UTF8String];
|
|
int len = [string lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
|
|
|
|
if (input_conv.vc_type != CONV_NONE)
|
|
str = string_convert(&input_conv, str, &len);
|
|
|
|
if (str)
|
|
clip_yank_selection(motion_type, str, len, cbd);
|
|
|
|
if (input_conv.vc_type != CONV_NONE)
|
|
vim_free(str);
|
|
|
|
releasepool:
|
|
[pool release];
|
|
}
|
|
|
|
|
|
/*
|
|
* Send the current selection to the clipboard.
|
|
*/
|
|
void
|
|
clip_mch_set_selection(Clipboard_T *cbd)
|
|
{
|
|
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
|
|
|
/* If the '*' register isn't already filled in, fill it in now. */
|
|
cbd->owned = TRUE;
|
|
clip_get_selection(cbd);
|
|
cbd->owned = FALSE;
|
|
|
|
/* Get the text to put on the pasteboard. */
|
|
long_u llen = 0; char_u *str = 0;
|
|
int motion_type = clip_convert_selection(&str, &llen, cbd);
|
|
if (motion_type < 0)
|
|
goto releasepool;
|
|
|
|
/* TODO: Avoid overflow. */
|
|
int len = (int)llen;
|
|
if (output_conv.vc_type != CONV_NONE)
|
|
{
|
|
char_u *conv_str = string_convert(&output_conv, str, &len);
|
|
if (conv_str)
|
|
{
|
|
vim_free(str);
|
|
str = conv_str;
|
|
}
|
|
}
|
|
|
|
if (len > 0)
|
|
{
|
|
NSString *string = [[NSString alloc]
|
|
initWithBytes:str length:len encoding:NSUTF8StringEncoding];
|
|
|
|
/* See clip_mch_request_selection() for info on pasteboard types. */
|
|
NSPasteboard *pb = [NSPasteboard generalPasteboard];
|
|
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
|
|
NSArray *supportedTypes = [NSArray arrayWithObjects:VimPboardType,
|
|
NSPasteboardTypeString, nil];
|
|
#else
|
|
NSArray *supportedTypes = [NSArray arrayWithObjects:VimPboardType,
|
|
NSStringPboardType, nil];
|
|
#endif
|
|
[pb declareTypes:supportedTypes owner:nil];
|
|
|
|
NSNumber *motion = [NSNumber numberWithInt:motion_type];
|
|
NSArray *plist = [NSArray arrayWithObjects:motion, string, nil];
|
|
[pb setPropertyList:plist forType:VimPboardType];
|
|
|
|
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
|
|
[pb setString:string forType:NSPasteboardTypeString];
|
|
#else
|
|
[pb setString:string forType:NSStringPboardType];
|
|
#endif
|
|
|
|
[string release];
|
|
}
|
|
|
|
vim_free(str);
|
|
releasepool:
|
|
[pool release];
|
|
}
|
|
|
|
#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 therefore
|
|
* 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.
|
|
*/
|
|
int
|
|
timer_delete(timer_t timerid)
|
|
{
|
|
/* Calls _timer_cancel() */
|
|
if (timerid != NULL)
|
|
dispatch_source_cancel(timerid->tim_timer);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif /* FEAT_RELTIME */
|
|
|
|
#ifdef FEAT_SOUND
|
|
|
|
static NSMutableDictionary<NSNumber*, NSSound*> *sounds_list = nil;
|
|
|
|
/// A delegate for handling when a sound has stopped playing, in
|
|
/// order to clean up the sound and to send a callback.
|
|
@interface SoundDelegate : NSObject<NSSoundDelegate>;
|
|
|
|
- (id) init:(long) sound_id callback:(soundcb_T*) callback;
|
|
- (void) sound:(NSSound *)sound didFinishPlaying:(BOOL)flag;
|
|
|
|
@property (readonly) long sound_id;
|
|
@property (readonly) soundcb_T *callback;
|
|
|
|
@end
|
|
|
|
@implementation SoundDelegate
|
|
- (id) init:(long) sound_id callback:(soundcb_T*) callback
|
|
{
|
|
if ([super init])
|
|
{
|
|
_sound_id = sound_id;
|
|
_callback = callback;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (void) sound:(NSSound *)sound didFinishPlaying:(BOOL)flag
|
|
{
|
|
if (sounds_list != nil)
|
|
{
|
|
if (_callback)
|
|
{
|
|
call_sound_callback(_callback, _sound_id, flag ? 0 : 1);
|
|
delete_sound_callback(_callback);
|
|
_callback = NULL;
|
|
}
|
|
[sounds_list removeObjectForKey:[NSNumber numberWithLong:_sound_id]];
|
|
}
|
|
// Release itself. Do that here instead of earlier because NSSound only
|
|
// holds weak reference to this object.
|
|
[self release];
|
|
}
|
|
@end
|
|
|
|
void
|
|
process_cfrunloop(void)
|
|
{
|
|
if (sounds_list != nil && [sounds_list count] > 0)
|
|
{
|
|
// Continually drain the run loop of events. Currently, this
|
|
// is only used for processing sound callbacks, because
|
|
// NSSound relies of this runloop to call back to the
|
|
// delegate.
|
|
@autoreleasepool
|
|
{
|
|
while (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, true)
|
|
== kCFRunLoopRunHandledSource)
|
|
; // do nothing
|
|
}
|
|
}
|
|
}
|
|
|
|
bool
|
|
sound_mch_play(const char_u* sound_name, long sound_id, soundcb_T *callback, bool playfile)
|
|
{
|
|
@autoreleasepool
|
|
{
|
|
NSString *sound_name_ns = [[[NSString alloc] initWithUTF8String:(const char*)sound_name] autorelease];
|
|
NSSound* sound = playfile ?
|
|
[[[NSSound alloc] initWithContentsOfFile:sound_name_ns byReference:YES] autorelease] :
|
|
[NSSound soundNamed:sound_name_ns];
|
|
if (!sound)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (sounds_list == nil)
|
|
{
|
|
sounds_list = [[NSMutableDictionary<NSNumber*, NSSound*> alloc] init];
|
|
}
|
|
sounds_list[[NSNumber numberWithLong:sound_id]] = sound;
|
|
|
|
// Make a delegate to handle when the sound stops. No need to call
|
|
// autorelease because NSSound only holds a weak reference to it.
|
|
SoundDelegate *delegate = [[SoundDelegate alloc] init:sound_id callback:callback];
|
|
|
|
[sound setDelegate:delegate];
|
|
[sound play];
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void
|
|
sound_mch_stop(long sound_id)
|
|
{
|
|
@autoreleasepool
|
|
{
|
|
NSSound *sound = sounds_list[[NSNumber numberWithLong:sound_id]];
|
|
if (sound != nil)
|
|
{
|
|
// Stop the sound. No need to release it because the delegate will do
|
|
// it for us.
|
|
[sound stop];
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
sound_mch_clear(void)
|
|
{
|
|
if (sounds_list != nil)
|
|
{
|
|
@autoreleasepool
|
|
{
|
|
for (NSSound *sound in [sounds_list allValues])
|
|
{
|
|
[sound stop];
|
|
}
|
|
[sounds_list release];
|
|
sounds_list = nil;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
sound_mch_free(void)
|
|
{
|
|
sound_mch_clear();
|
|
}
|
|
|
|
#endif // FEAT_SOUND
|
|
|
|
/* Lift the compiler warning suppression. */
|
|
#if defined(__clang__) && defined(__STRICT_ANSI__)
|
|
# pragma clang diagnostic pop
|
|
# pragma clang diagnostic pop
|
|
#endif
|