diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 14b088e..23ef780 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,18 +11,18 @@ jobs: matrix: include: - os: ubuntu-latest - swift-version: swift-6.1.2-release - swift-build: 6.1.2-RELEASE + swift-version: swift-6.2.1-release + swift-build: 6.2.1-RELEASE build-args: "--traits GNU" - os: macos-latest - swift-version: swift-6.1.2-release - swift-build: 6.1.2-RELEASE + swift-version: swift-6.2.1-release + swift-build: 6.2.1-RELEASE build-args: "" - os: windows-latest - swift-version: swift-6.1.2-release - swift-build: 6.1.2-RELEASE + swift-version: swift-6.2.1-release + swift-build: 6.2.1-RELEASE build-args: "" runs-on: ${{ matrix.os }} diff --git a/Package.swift b/Package.swift index 7dca1a4..7bfac77 100644 --- a/Package.swift +++ b/Package.swift @@ -1,11 +1,11 @@ -// swift-tools-version:6.1 +// swift-tools-version:6.2 import PackageDescription let _: Package = .init(name: "VirtualTerminal", platforms: [ - .macOS(.v15), + .macOS(.v26), ], products: [ .executable(name: "VTDemo", targets: ["VTDemo"]), @@ -20,12 +20,14 @@ let _: Package = ], targets: [ .target(name: "libunistring"), + .target(name: "xlocale_wrapper"), .target(name: "Geometry"), .target(name: "Primitives"), .target(name: "VirtualTerminal", dependencies: [ .target(name: "Geometry"), .target(name: "Primitives"), .target(name: "libunistring", condition: .when(traits: ["GNU"])), + .target(name: "xlocale_wrapper", condition: .when(platforms: [.macOS])), .product(name: "POSIXCore", package: "swift-platform-core", condition: .when(platforms: [.macOS, .linux])), .product(name: "WindowsCore", package: "swift-platform-core", condition: .when(platforms: [.windows])), ]), diff --git a/Sources/VirtualTerminal/Buffer/TextMetrics.swift b/Sources/VirtualTerminal/Buffer/TextMetrics.swift index 28f0f09..9f2409d 100644 --- a/Sources/VirtualTerminal/Buffer/TextMetrics.swift +++ b/Sources/VirtualTerminal/Buffer/TextMetrics.swift @@ -4,12 +4,22 @@ #if os(Windows) import WindowsCore #else +import POSIXCore +import Synchronization +#if canImport(Darwin) +import Darwin +import xlocale_wrapper +#elseif canImport(Glibc) +import Glibc +#endif #if GNU import libunistring #endif import POSIXCore import Synchronization +#endif +#if canImport(Glibc) private enum Locale { private static let utf8: Mutex = Mutex(nil) @@ -21,7 +31,18 @@ private enum Locale { } } } +#elseif canImport(Darwin) +private enum Locale { + private static let utf8: Mutex = Mutex(nil) + static var ID_UTF8: UnsafeMutableRawPointer? { + return utf8.withLock { locale in + if let locale { return locale } + locale = vt_newlocale(LC_CTYPE_MASK, "en_US.UTF-8", nil) + return locale + } + } +} #endif extension UnicodeScalar { @@ -87,12 +108,14 @@ extension UnicodeScalar { // Normal width character -> 1 // Wide character (CJK, etc.) -> 2 return max(1, Int(uc_width(UInt32(value), "C.UTF-8"))) -#else +#elseif canImport(Glibc) // Control character or invalid - zero width -> -1 // Zero-width character (combining marks, etc.) -> 0 // Normal width character -> 1 // Wide character (CJK, etc.) -> 2 return max(1, Int(wcwidth_l(wchar_t(value), Locale.ID_UTF8))) +#else + return max(1, Int(vt_wcwidth_l(wchar_t(value), Locale.ID_UTF8))) #endif } } diff --git a/Sources/VirtualTerminal/Platform/POSIXTerminal.swift b/Sources/VirtualTerminal/Platform/POSIXTerminal.swift index b061b74..705124a 100644 --- a/Sources/VirtualTerminal/Platform/POSIXTerminal.swift +++ b/Sources/VirtualTerminal/Platform/POSIXTerminal.swift @@ -6,6 +6,9 @@ import Geometry import POSIXCore import Synchronization +#if !GNU +import unistd +#endif /// POSIX/Unix terminal implementation using standard file descriptors. /// diff --git a/Sources/xlocale_wrapper/include/xlocale_wrapper.h b/Sources/xlocale_wrapper/include/xlocale_wrapper.h new file mode 100644 index 0000000..6505f31 --- /dev/null +++ b/Sources/xlocale_wrapper/include/xlocale_wrapper.h @@ -0,0 +1,27 @@ +/* Copyright © 2025 Saleem Abdulrasool */ +/* SPDX-License-Identifier: BSD-3-Clause */ + +#ifndef xlocale_wrapper_h +#define xlocale_wrapper_h + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// Wrapper functions for xlocale functionality +int vt_wcwidth_l(wchar_t wc, void *locale); +void *vt_newlocale(int mask, const char *locale, void *base); +void vt_freelocale(void *locale); + +// Constants +#if __APPLE__ +#define LC_CTYPE_MASK (1 << 1) +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* xlocale_wrapper_h */ \ No newline at end of file diff --git a/Sources/xlocale_wrapper/xlocale_wrapper.c b/Sources/xlocale_wrapper/xlocale_wrapper.c new file mode 100644 index 0000000..c026e2e --- /dev/null +++ b/Sources/xlocale_wrapper/xlocale_wrapper.c @@ -0,0 +1,21 @@ +/* Copyright © 2025 Saleem Abdulrasool */ +/* SPDX-License-Identifier: BSD-3-Clause */ + +#include +#include + +int vt_wcwidth_l(wchar_t wc, void *locale) { + return wcwidth_l(wc, (locale_t)locale); +} + +void *vt_newlocale(int mask, const char *locale, void *base) { + return newlocale(mask, locale, (locale_t)base); +} + +void vt_freelocale(void *locale) { + freelocale((locale_t)locale); +} + +#if __APPLE__ +#define LC_CTYPE_MASK (1 << 1) +#endif