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:
2026-10-15 17:58:48 -04:00
parent 719370c8f7
commit 6081e3e325
1743 changed files with 205460 additions and 131705 deletions

View File

@@ -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.

View File

@@ -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

View File

@@ -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;
}

View 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;
}