Skip to content
Merged
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
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
* text=auto eol=lf
*.MF text eol=crlf
*.jar binary
*.dylib binary
2 changes: 2 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# DurianSwt releases

## [Unreleased]
### Added
- `MacDeepLink` class and dylibs for supporting deep links on modern versions of macOS.

## [5.1.0] - 2025-08-21
### Added
Expand Down
39 changes: 39 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Build Commands

This is a Gradle-based Java/Kotlin project focused on SWT (Standard Widget Toolkit) utilities. Standard gradle tasks.

### macOS-specific Requirements
- On macOS, SWT tests require the `-XstartOnFirstThread` JVM argument (automatically configured in build.gradle).

## Project Architecture
### Multi-Module Structure
DurianSwt is organized as a multi-module Gradle project with platform-specific implementations:

- **durian-swt.os**: Platform detection utilities (OS, Arch, SwtPlatform) - no SWT dependencies
- **durian-swt**: Main module with core SWT utilities and builders
- **platform-specific modules**: (durian-swt.cocoa.macosx.aarch64, durian-swt.cocoa.macosx.x86_64, durian-swt.gtk.linux.x86_64, durian-swt.win32.win32.x86_64)

## Native Components

### macOS Deep Link Support (`natives/mac-deep-link/`)

Contains Objective-C code for handling deep links via a custom `diffplug://` protocol on macOS without breaking SWT:

- **DeepLinkBridge.m**: JNI bridge that intercepts URL open events from macOS
- **compile-one.sh**: Compiles the native library for a specific architecture (x86_64 or arm64)
- **clean-and-build.sh**: Builds for both architectures and deploys to appropriate module resources

To rebuild native libraries:
```bash
cd natives/mac-deep-link
./clean-and-build.sh # Builds for both architectures
# Or compile for specific architecture:
./compile-one.sh arm64 # Apple Silicon
./compile-one.sh x86_64 # Intel
```

The resulting `DeepLinkBridge.dylib` files are placed in the platform-specific modules under `src/main/resources/durian-swt-natives/`.
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ subprojects { subProject ->
}
dependencies {
api project(':durian-swt')
implementation "com.diffplug.durian:durian-core:$VER_DURIAN"
}
configurations.all {
resolutionStrategy.eachDependency { DependencyResolveDetails details ->
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
* Copyright (C) 2025 DiffPlug
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.diffplug.common.swt;

import com.diffplug.common.base.Preconditions;
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import org.jetbrains.annotations.Nullable;

/**
* - immediately on app startup, call `MacDeepLink.startCapturingBeforeSwt()`
* - once SWT has initialized, call `MacDeepLink.swtHasInitializedBeginReceiving(Consumer<String>)`
* - all urls which were captured before SWT initialized will be passed immediately (on the SWT thread)
*
* That's all! Don't do anything else.
*/
public class MacDeepLink {
/**
* state transitions are:
* - `null` on startup
* - `startCapturingBeforeSwt()` transitions to an `ArrayList<String>`, backlog urls get added to it
* - `swtHasInitializedBeginReceiving()` transitions to a `Consumer<String>`, all new urls go there
*/
private static final AtomicReference<@Nullable Object> state = new AtomicReference<>();

public static void startCapturingBeforeSwt() {
String libPath = System.getProperty("durian-swt.library.path");
if (libPath != null) {
System.load(libPath + "/durian-swt-natives/DeepLinkBridge.dylib");
} else {
throw new IllegalArgumentException("You need to set 'durian-swt.library.path'");
}

var was = state.getAndSet(new ArrayList<>());
Preconditions.checkArgument(was == null, "`startCapturingBeforeSwt() should be called first`");
nativeBeforeSwt();
}

public static void swtHasInitializedBeginReceiving(Consumer<String> handler) {
SwtMisc.assertUI();
var was = state.getAndSet(handler);
Preconditions.checkArgument(was instanceof ArrayList<?>, "Call `applicationStartBeforeSwt()` first.");

var backlog = (ArrayList<String>) was;
backlog.forEach(handler);

nativeAfterSwt();
}

// Native method declarations - implemented in DeepLinkBridge.m
private static native void nativeBeforeSwt();

private static native void nativeAfterSwt();

/**
* Called from native code when a URL is received.
* This method is invoked on various threads by the native code.
*
* @param url The URL string received from the operating system
*/
public static void __internal_deliverUrl(String url) {
var was = state.get();
if (was instanceof Consumer) {
((Consumer<String>) was).accept(url);
} else if (was instanceof ArrayList<?>) {
((ArrayList<String>) was).add(url);
} else {
throw new IllegalStateException("Expected Consumer or ArrayList, was " + was);
}
}
}
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
* Copyright (C) 2025 DiffPlug
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.diffplug.common.swt;

import com.diffplug.common.base.Preconditions;
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import org.jetbrains.annotations.Nullable;

/**
* - immediately on app startup, call `MacDeepLink.startCapturingBeforeSwt()`
* - once SWT has initialized, call `MacDeepLink.swtHasInitializedBeginReceiving(Consumer<String>)`
* - all urls which were captured before SWT initialized will be passed immediately (on the SWT thread)
*
* That's all! Don't do anything else.
*/
public class MacDeepLink {
/**
* state transitions are:
* - `null` on startup
* - `startCapturingBeforeSwt()` transitions to an `ArrayList<String>`, backlog urls get added to it
* - `swtHasInitializedBeginReceiving()` transitions to a `Consumer<String>`, all new urls go there
*/
private static final AtomicReference<@Nullable Object> state = new AtomicReference<>();

public static void startCapturingBeforeSwt() {
String libPath = System.getProperty("durian-swt.library.path");
if (libPath != null) {
System.load(libPath + "/durian-swt-natives/DeepLinkBridge.dylib");
} else {
throw new IllegalArgumentException("You need to set 'durian-swt.library.path'");
}

var was = state.getAndSet(new ArrayList<>());
Preconditions.checkArgument(was == null, "`startCapturingBeforeSwt() should be called first`");
nativeBeforeSwt();
}

public static void swtHasInitializedBeginReceiving(Consumer<String> handler) {
SwtMisc.assertUI();
var was = state.getAndSet(handler);
Preconditions.checkArgument(was instanceof ArrayList<?>, "Call `applicationStartBeforeSwt()` first.");

var backlog = (ArrayList<String>) was;
backlog.forEach(handler);

nativeAfterSwt();
}

// Native method declarations - implemented in DeepLinkBridge.m
private static native void nativeBeforeSwt();

private static native void nativeAfterSwt();

/**
* Called from native code when a URL is received.
* This method is invoked on various threads by the native code.
*
* @param url The URL string received from the operating system
*/
public static void __internal_deliverUrl(String url) {
var was = state.get();
if (was instanceof Consumer) {
((Consumer<String>) was).accept(url);
} else if (was instanceof ArrayList<?>) {
((ArrayList<String>) was).add(url);
} else {
throw new IllegalStateException("Expected Consumer or ArrayList, was " + was);
}
}
}
Binary file not shown.
Loading
Loading