Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }}
Expand Down
6 changes: 4 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -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"]),
Expand All @@ -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])),
]),
Expand Down
25 changes: 24 additions & 1 deletion Sources/VirtualTerminal/Buffer/TextMetrics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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<locale_t?> = Mutex(nil)

Expand All @@ -21,7 +31,18 @@ private enum Locale {
}
}
}
#elseif canImport(Darwin)
private enum Locale {
private static let utf8: Mutex<UnsafeMutableRawPointer?> = 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 {
Expand Down Expand Up @@ -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
}
}
Expand Down
3 changes: 3 additions & 0 deletions Sources/VirtualTerminal/Platform/POSIXTerminal.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
import Geometry
import POSIXCore
import Synchronization
#if !GNU
import unistd
#endif

/// POSIX/Unix terminal implementation using standard file descriptors.
///
Expand Down
27 changes: 27 additions & 0 deletions Sources/xlocale_wrapper/include/xlocale_wrapper.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/* Copyright © 2025 Saleem Abdulrasool <compnerd@compnerd.org> */
/* SPDX-License-Identifier: BSD-3-Clause */

#ifndef xlocale_wrapper_h
#define xlocale_wrapper_h

#include <wchar.h>

#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 */
21 changes: 21 additions & 0 deletions Sources/xlocale_wrapper/xlocale_wrapper.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/* Copyright © 2025 Saleem Abdulrasool <compnerd@compnerd.org> */
/* SPDX-License-Identifier: BSD-3-Clause */

#include <wchar.h>
#include <xlocale.h>

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