chore(vendor): import pi 1.3.0
Imported as a linear vendor commit so forge rebases do not replay the upstream root tree. git-subtree-dir: third_party/pi git-subtree-split: 71023aec1546d6d677a65669c5665c0927ce0dd0
This commit is contained in:
@@ -1,20 +1,22 @@
|
||||
# Darwin native prebuilds
|
||||
|
||||
Build both macOS architectures from the repository root:
|
||||
Provides modifier-key state and asynchronous text, image, and file-path clipboard access using AppKit.
|
||||
|
||||
## Building
|
||||
|
||||
On macOS, run from the repository root:
|
||||
|
||||
```sh
|
||||
npm --prefix packages/tui run build:native:darwin
|
||||
```
|
||||
|
||||
The build uses macOS 11.0 as the arm64 deployment target and macOS 10.15 as the x86_64 deployment target. On macOS, `build.sh` finds Apple clang and the active macOS SDK through `xcrun`. Either an Intel or Apple Silicon host can build both outputs.
|
||||
The build uses Apple clang and the macOS SDK found through `xcrun`. Either Intel or Apple Silicon can build both targets: arm64 (macOS 11+) and x64 (macOS 10.15+).
|
||||
|
||||
A non-macOS host needs a complete Darwin cross-toolchain, including a macOS SDK and a Mach-O linker. For example, an osxcross installation can be selected with `CC` and `SDKROOT`:
|
||||
Cross-building requires a Darwin toolchain with a macOS SDK and Mach-O linker, such as osxcross:
|
||||
|
||||
```sh
|
||||
CC=/path/to/osxcross/clang SDKROOT=/path/to/MacOSX.sdk \
|
||||
npm --prefix packages/tui run build:native:darwin
|
||||
```
|
||||
|
||||
The SDK must be obtained and used in accordance with Apple's license. Plain Linux or Windows clang is not enough because the addon includes and links CoreGraphics.
|
||||
|
||||
Zig is not used here because it does not provide the Apple SDK or CoreGraphics framework stubs. It therefore does not make this build SDK-independent, and its clang driver does not currently handle this Mach-O bundle recipe as a drop-in replacement for Apple clang.
|
||||
The SDK must be obtained and used under Apple's license. Plain clang or Zig alone is not sufficient.
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
set -euo pipefail
|
||||
|
||||
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source_file="$script_dir/src/darwin-modifiers.c"
|
||||
source_file="$script_dir/src/darwin-platform.m"
|
||||
|
||||
if [[ -n "${CC:-}" ]]; then
|
||||
compiler="$CC"
|
||||
@@ -37,25 +37,32 @@ build() {
|
||||
local arch="$1"
|
||||
local target="$2"
|
||||
local output_dir="$script_dir/prebuilds/darwin-$arch"
|
||||
local temporary_output="$temporary_dir/darwin-$arch/darwin-modifiers.node"
|
||||
local temporary_output="$temporary_dir/darwin-$arch/darwin-platform.node"
|
||||
|
||||
mkdir -p "$(dirname "$temporary_output")"
|
||||
"$compiler" \
|
||||
-fobjc-arc \
|
||||
-std=c11 \
|
||||
-Wall \
|
||||
-Wextra \
|
||||
-O2 \
|
||||
-Oz \
|
||||
-flto \
|
||||
-fvisibility=hidden \
|
||||
"--target=$target" \
|
||||
"${sdk_flags[@]}" \
|
||||
-bundle \
|
||||
-undefined dynamic_lookup \
|
||||
-Wl,-dead_strip \
|
||||
-Wl,-x \
|
||||
-framework AppKit \
|
||||
-framework CoreGraphics \
|
||||
-framework Foundation \
|
||||
"$source_file" \
|
||||
-o "$temporary_output"
|
||||
|
||||
mkdir -p "$output_dir"
|
||||
install -m 755 "$temporary_output" "$output_dir/darwin-modifiers.node"
|
||||
echo "Built $output_dir/darwin-modifiers.node"
|
||||
install -m 755 "$temporary_output" "$output_dir/darwin-platform.node"
|
||||
echo "Built $output_dir/darwin-platform.node"
|
||||
}
|
||||
|
||||
build arm64 arm64-apple-macos11.0
|
||||
|
||||
Binary file not shown.
Binary file not shown.
BIN
third_party/pi/packages/tui/native/darwin/prebuilds/darwin-x64/darwin-platform.node
vendored
Executable file
BIN
third_party/pi/packages/tui/native/darwin/prebuilds/darwin-x64/darwin-platform.node
vendored
Executable file
Binary file not shown.
@@ -1,70 +0,0 @@
|
||||
#include <CoreGraphics/CoreGraphics.h>
|
||||
#include <dlfcn.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
#define NAPI_AUTO_LENGTH ((size_t)-1)
|
||||
|
||||
typedef void* napi_env;
|
||||
typedef void* napi_value;
|
||||
typedef void* napi_callback_info;
|
||||
typedef napi_value (*napi_callback)(napi_env, napi_callback_info);
|
||||
typedef int (*napi_create_function_fn)(napi_env, const char*, size_t, napi_callback, void*, napi_value*);
|
||||
typedef int (*napi_set_named_property_fn)(napi_env, napi_value, const char*, napi_value);
|
||||
typedef int (*napi_get_boolean_fn)(napi_env, bool, napi_value*);
|
||||
typedef int (*napi_get_cb_info_fn)(napi_env, napi_callback_info, size_t*, napi_value*, napi_value*, void**);
|
||||
typedef int (*napi_get_value_string_utf8_fn)(napi_env, napi_value, char*, size_t, size_t*);
|
||||
|
||||
static void* node_symbol(const char* name) {
|
||||
return dlsym(RTLD_DEFAULT, name);
|
||||
}
|
||||
|
||||
static CGEventFlags modifier_mask_for_name(const char* name) {
|
||||
if (strcmp(name, "shift") == 0) return kCGEventFlagMaskShift;
|
||||
if (strcmp(name, "command") == 0) return kCGEventFlagMaskCommand;
|
||||
if (strcmp(name, "control") == 0) return kCGEventFlagMaskControl;
|
||||
if (strcmp(name, "option") == 0) return kCGEventFlagMaskAlternate;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static napi_value is_modifier_pressed(napi_env env, napi_callback_info info) {
|
||||
napi_get_cb_info_fn napi_get_cb_info = (napi_get_cb_info_fn)node_symbol("napi_get_cb_info");
|
||||
napi_get_value_string_utf8_fn napi_get_value_string_utf8 = (napi_get_value_string_utf8_fn)node_symbol("napi_get_value_string_utf8");
|
||||
napi_get_boolean_fn napi_get_boolean = (napi_get_boolean_fn)node_symbol("napi_get_boolean");
|
||||
|
||||
bool pressed = false;
|
||||
if (napi_get_cb_info && napi_get_value_string_utf8) {
|
||||
size_t argc = 1;
|
||||
napi_value args[1] = {0};
|
||||
if (napi_get_cb_info(env, info, &argc, args, 0, 0) == 0 && argc >= 1 && args[0]) {
|
||||
char name[16] = {0};
|
||||
size_t copied = 0;
|
||||
if (napi_get_value_string_utf8(env, args[0], name, sizeof(name), &copied) == 0) {
|
||||
CGEventFlags mask = modifier_mask_for_name(name);
|
||||
if (mask != 0) {
|
||||
CGEventFlags flags = CGEventSourceFlagsState(kCGEventSourceStateCombinedSessionState);
|
||||
pressed = (flags & mask) != 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
napi_value result = 0;
|
||||
if (napi_get_boolean) napi_get_boolean(env, pressed, &result);
|
||||
return result;
|
||||
}
|
||||
|
||||
__attribute__((visibility("default"))) napi_value napi_register_module_v1(napi_env env, napi_value exports) {
|
||||
napi_create_function_fn napi_create_function = (napi_create_function_fn)node_symbol("napi_create_function");
|
||||
napi_set_named_property_fn napi_set_named_property = (napi_set_named_property_fn)node_symbol("napi_set_named_property");
|
||||
|
||||
napi_value fn = 0;
|
||||
if (napi_create_function &&
|
||||
napi_set_named_property &&
|
||||
napi_create_function(env, "isModifierPressed", NAPI_AUTO_LENGTH, is_modifier_pressed, 0, &fn) == 0) {
|
||||
napi_set_named_property(env, exports, "isModifierPressed", fn);
|
||||
}
|
||||
|
||||
return exports;
|
||||
}
|
||||
132
third_party/pi/packages/tui/native/darwin/src/darwin-platform.m
vendored
Normal file
132
third_party/pi/packages/tui/native/darwin/src/darwin-platform.m
vendored
Normal file
@@ -0,0 +1,132 @@
|
||||
#import <AppKit/AppKit.h>
|
||||
#import <CoreGraphics/CoreGraphics.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#define PI_CLIPBOARD_WRITE
|
||||
#include "../../clipboard.h"
|
||||
|
||||
static CGEventFlags modifier_mask_for_name(const char* name) {
|
||||
if (strcmp(name, "shift") == 0) return kCGEventFlagMaskShift;
|
||||
if (strcmp(name, "command") == 0) return kCGEventFlagMaskCommand;
|
||||
if (strcmp(name, "control") == 0) return kCGEventFlagMaskControl;
|
||||
if (strcmp(name, "option") == 0) return kCGEventFlagMaskAlternate;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static napi_value is_modifier_pressed(napi_env env, napi_callback_info info) {
|
||||
napi_get_cb_info_fn napi_get_cb_info = (napi_get_cb_info_fn)node_symbol("napi_get_cb_info");
|
||||
napi_get_value_string_utf8_fn napi_get_value_string_utf8 =
|
||||
(napi_get_value_string_utf8_fn)node_symbol("napi_get_value_string_utf8");
|
||||
napi_get_boolean_fn napi_get_boolean = (napi_get_boolean_fn)node_symbol("napi_get_boolean");
|
||||
|
||||
bool pressed = false;
|
||||
if (napi_get_cb_info && napi_get_value_string_utf8) {
|
||||
size_t argc = 1;
|
||||
napi_value args[1] = {0};
|
||||
if (napi_get_cb_info(env, info, &argc, args, 0, 0) == 0 && argc >= 1 && args[0]) {
|
||||
char name[16] = {0};
|
||||
size_t copied = 0;
|
||||
if (napi_get_value_string_utf8(env, args[0], name, sizeof(name), &copied) == 0) {
|
||||
CGEventFlags mask = modifier_mask_for_name(name);
|
||||
if (mask != 0) {
|
||||
CGEventFlags flags = CGEventSourceFlagsState(kCGEventSourceStateCombinedSessionState);
|
||||
pressed = (flags & mask) != 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
napi_value result = 0;
|
||||
if (!napi_get_boolean || napi_get_boolean(env, pressed, &result) != 0) {
|
||||
return fail(env, "Could not inspect modifier state");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void clipboard_execute(clipboard_job* job) {
|
||||
@autoreleasepool {
|
||||
NSPasteboard* pasteboard = [NSPasteboard generalPasteboard];
|
||||
if (!pasteboard) {
|
||||
job->error = "Could not open clipboard";
|
||||
} else if (job->operation == CLIPBOARD_WRITE) {
|
||||
NSString* text = [[NSString alloc] initWithBytes:job->data length:job->length encoding:NSUTF8StringEncoding];
|
||||
if (!text) {
|
||||
job->error = "Clipboard text is not valid UTF-8";
|
||||
return;
|
||||
}
|
||||
[pasteboard clearContents];
|
||||
if (![pasteboard setString:text forType:NSPasteboardTypeString]) job->error = "Could not set clipboard text";
|
||||
} else if (job->operation == CLIPBOARD_TEXT) {
|
||||
NSString* text = [pasteboard stringForType:NSPasteboardTypeString];
|
||||
if (!text) {
|
||||
job->format = CLIPBOARD_EMPTY;
|
||||
} else if (!text.UTF8String) {
|
||||
job->error = "Could not encode clipboard text";
|
||||
} else {
|
||||
clipboard_copy(job, text.UTF8String, [text lengthOfBytesUsingEncoding:NSUTF8StringEncoding], CLIPBOARD_UTF8);
|
||||
}
|
||||
} else if (job->operation == CLIPBOARD_FILES) {
|
||||
NSArray* urls = [pasteboard readObjectsForClasses:@[[NSURL class]]
|
||||
options:@{NSPasteboardURLReadingFileURLsOnlyKey: @YES}];
|
||||
size_t total = 0;
|
||||
for (NSURL* url in urls) {
|
||||
const char* path = url.fileSystemRepresentation;
|
||||
if (!path) continue;
|
||||
size_t length = strlen(path);
|
||||
if (length == SIZE_MAX || total > SIZE_MAX - length - 1) {
|
||||
job->error = "Clipboard file paths are too large";
|
||||
return;
|
||||
}
|
||||
total += length + 1;
|
||||
}
|
||||
if (total == 0) {
|
||||
job->format = CLIPBOARD_EMPTY;
|
||||
return;
|
||||
}
|
||||
char* paths = clipboard_alloc(total);
|
||||
if (!paths) {
|
||||
job->error = "Out of memory";
|
||||
return;
|
||||
}
|
||||
char* cursor = paths;
|
||||
for (NSURL* url in urls) {
|
||||
const char* path = url.fileSystemRepresentation;
|
||||
if (!path) continue;
|
||||
size_t length = strlen(path);
|
||||
memcpy(cursor, path, length + 1);
|
||||
cursor += length + 1;
|
||||
}
|
||||
job->data = paths;
|
||||
job->length = total;
|
||||
job->format = CLIPBOARD_PATHS;
|
||||
} else {
|
||||
if (![pasteboard availableTypeFromArray:@[ NSPasteboardTypePNG, NSPasteboardTypeTIFF ]]) {
|
||||
job->format = CLIPBOARD_EMPTY;
|
||||
return;
|
||||
}
|
||||
NSData* png = [pasteboard dataForType:NSPasteboardTypePNG];
|
||||
if (!png) {
|
||||
NSImage* image = [[NSImage alloc] initWithPasteboard:pasteboard];
|
||||
NSData* tiff = image.TIFFRepresentation;
|
||||
NSBitmapImageRep* bitmap = tiff ? [NSBitmapImageRep imageRepWithData:tiff] : nil;
|
||||
png = bitmap ? [bitmap representationUsingType:NSBitmapImageFileTypePNG properties:@{}] : nil;
|
||||
}
|
||||
if (png) clipboard_copy(job, png.bytes, png.length, CLIPBOARD_BUFFER);
|
||||
else job->error = "Clipboard does not contain an image";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static napi_value PI_NAPI_CALL get_clipboard_file_paths(napi_env env, napi_callback_info info) {
|
||||
return queue_clipboard(env, info, CLIPBOARD_FILES);
|
||||
}
|
||||
|
||||
PI_NAPI_EXPORT napi_value napi_register_module_v1(napi_env env, napi_value exports) {
|
||||
set_function_export(env, exports, "isModifierPressed", is_modifier_pressed);
|
||||
set_function_export(env, exports, "getText", get_clipboard_text);
|
||||
set_function_export(env, exports, "setText", set_clipboard_text);
|
||||
set_function_export(env, exports, "getImage", get_clipboard_image);
|
||||
set_function_export(env, exports, "getFilePaths", get_clipboard_file_paths);
|
||||
return exports;
|
||||
}
|
||||
Reference in New Issue
Block a user