diff --git a/.all-contributorsrc b/.all-contributorsrc
index e9e4764d85..95122bc1fd 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -1473,7 +1473,8 @@
"avatar_url": "https://avatars.githubusercontent.com/u/138858208?v=4",
"profile": "https://github.com/Rishab87",
"contributions": [
- "code"
+ "code",
+ "test"
]
},
{
@@ -1688,6 +1689,7 @@
"doc"
]
},
+ {
"login": "avinxshKD",
"name": "Avinash Kumar Deepak",
"avatar_url": "https://avatars.githubusercontent.com/u/152387616?v=4",
@@ -1695,6 +1697,42 @@
"contributions": [
"code"
]
+ },
+ {
+ "login": "Sumamasonia",
+ "name": "Sumama Sonia",
+ "avatar_url": "https://avatars.githubusercontent.com/u/214366437?v=4",
+ "profile": "https://www.linkedin.com/in/sumamasonia/",
+ "contributions": [
+ "code"
+ ]
+ },
+ {
+ "login": "SalmaneKhalili",
+ "name": "Salmane Khalili",
+ "avatar_url": "https://avatars.githubusercontent.com/u/141567440?v=4",
+ "profile": "https://www.linkedin.com/in/salmane-khalili-4223562aa/",
+ "contributions": [
+ "bug"
+ ]
+ },
+ {
+ "login": "nickmcintyre",
+ "name": "Nick McIntyre",
+ "avatar_url": "https://avatars.githubusercontent.com/u/3719176?v=4",
+ "profile": "https://mcintyre.io",
+ "contributions": [
+ "test"
+ ]
+ },
+ {
+ "login": "Ebaron96",
+ "name": "Elijah Baron",
+ "avatar_url": "https://avatars.githubusercontent.com/u/180047692?v=4",
+ "profile": "https://github.com/Ebaron96",
+ "contributions": [
+ "bug"
+ ]
}
],
"repoType": "github",
diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml
new file mode 100644
index 0000000000..cc65b83095
--- /dev/null
+++ b/.github/actions/setup/action.yml
@@ -0,0 +1,18 @@
+name: "Setup Processing"
+description: "Setup the required contents for Processing to build"
+inputs:
+ arch:
+ description: 'Architecture of the JDK to download'
+ required: false
+runs:
+ using: "composite"
+ steps:
+ - name: Install Java
+ uses: actions/setup-java@v4
+ with:
+ java-version: '17'
+ distribution: 'temurin'
+ architecture: ${{ inputs.arch }}
+
+ - name: Setup Gradle
+ uses: gradle/actions/setup-gradle@v4
\ No newline at end of file
diff --git a/.github/workflows/build-gradle.yml b/.github/workflows/build-gradle.yml
deleted file mode 100644
index 254433edd0..0000000000
--- a/.github/workflows/build-gradle.yml
+++ /dev/null
@@ -1,80 +0,0 @@
-name: Branch Builds
-on:
- push:
- paths-ignore:
- - '**/*.md'
- - '.all-contributorsrc'
-
-jobs:
- test:
- runs-on: ubuntu-latest
- name: Test Processing
- steps:
- - name: Checkout Repository
- uses: actions/checkout@v4
- - name: Install Java
- uses: actions/setup-java@v4
- with:
- java-version: '17'
- distribution: 'temurin'
- - name: Setup Gradle
- uses: gradle/actions/setup-gradle@v4
-
- - name: Build with Gradle
- run: ./gradlew test
- build:
- name: (${{ matrix.os_prefix }}/${{ matrix.arch }}) Create Processing Build
- runs-on: ${{ matrix.os }}
- needs: test
- strategy:
- fail-fast: false
- matrix:
- include:
- - os: ubuntu-24.04-arm
- os_prefix: linux
- arch: aarch64
- binary: processing*.snap
- - os: ubuntu-latest
- os_prefix: linux
- arch: x64
- binary: processing*.snap
- - os: windows-latest
- os_prefix: windows
- arch: x64
- binary: msi/Processing-*.msi
- - os: macos-latest
- os_prefix: macos
- arch: x64
- binary: dmg/Processing-*.dmg
- - os: macos-latest
- os_prefix: macos
- arch: aarch64
- binary: dmg/Processing-*.dmg
- steps:
- - name: Install Snapcraft
- if: runner.os == 'Linux'
- uses: samuelmeuli/action-snapcraft@v3
- - name: Install LXD
- if: runner.os == 'Linux'
- uses: canonical/setup-lxd@main
-
- - name: Checkout Repository
- uses: actions/checkout@v4
- - name: Install Java
- uses: actions/setup-java@v4
- with:
- java-version: '17'
- distribution: 'temurin'
- architecture: ${{ matrix.arch }}
- - name: Setup Gradle
- uses: gradle/actions/setup-gradle@v4
-
- - name: Build with Gradle
- run: ./gradlew packageDistributionForCurrentOS
-
- - name: Add artifact
- uses: actions/upload-artifact@v4
- with:
- name: processing-${{ matrix.os_prefix }}-${{ matrix.arch }}-br_${{ github.ref_name }}
- retention-days: 1
- path: app/build/compose/binaries/main/${{ matrix.binary }}
\ No newline at end of file
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index b1b7f9ad62..d4355a69c5 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -1,77 +1,105 @@
-name: Branch Builds (Legacy)
-on:
+name: Branch Builds
+on:
push:
paths-ignore:
- '**/*.md'
- '.all-contributorsrc'
+ pull_request:
+ paths-ignore:
+ - '**/*.md'
+ branches:
+ - main
+
jobs:
test:
runs-on: ubuntu-latest
- name: Run Tests
+ name: Test Processing
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- - name: Install Java
- uses: actions/setup-java@v4
- with:
- java-version: '17'
- distribution: 'temurin'
- architecture: x64
- - name: Build
- run: >
- cd build;
- ant -noinput build test;
- build:
- name: Create Pre-release for ${{ matrix.os_prefix }} (${{ matrix.arch }})
- needs: test
+
+ - name: Setup Processing
+ uses: ./.github/actions/setup
+
+ - name: Build with Gradle
+ run: ./gradlew test
+
+ # - name: Add comment with binaries
+ # if: ${{ github.event_name == 'pull_request' }}
+ # uses: marocchino/sticky-pull-request-comment@v2
+ # with:
+ # header: artifacts
+ # message: |
+ # Tests completed successfully. Build artifacts for this pull request will appear below once ready.
+ #
+ # ### Build Artifacts
+ # | Platform | Link |
+ # |:--|------------------------|
+
+ build:
+ name: (${{ matrix.os_prefix }}/${{ matrix.arch }}) Create Processing Build
runs-on: ${{ matrix.os }}
+ needs: test
permissions:
- contents: write
+ pull-requests: write
strategy:
fail-fast: false
matrix:
include:
- # compiling for arm32 needs a self-hosted runner on Raspi OS (32-bit)
- - os: [self-hosted, linux, ARM]
+ - os: ubuntu-24.04-arm
os_prefix: linux
- arch: arm
+ arch: aarch64
+ binary: deb/processing*.deb
- os: ubuntu-latest
os_prefix: linux
arch: x64
+ binary: deb/processing*.deb
- os: windows-latest
os_prefix: windows
arch: x64
+ binary: msi/Processing-*.msi
- os: macos-latest
os_prefix: macos
arch: x64
+ binary: dmg/Processing-*.dmg
- os: macos-latest
os_prefix: macos
arch: aarch64
- - os: macos-latest
- os_prefix: linux
- arch: aarch64
+ binary: dmg/Processing-*.dmg
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- - name: Checkout Examples Repository
- uses: actions/checkout@v4
- with:
- repository: processing/processing-examples
- path: processing-examples
- - name: Install Java
- uses: actions/setup-java@v4
- with:
- java-version: '17'
- distribution: 'temurin'
- architecture: ${{ matrix.arch }}
- - name: Setup Ant
- uses: cedx/setup-ant@v3
- - name: Build Release
- run: ant -noinput -buildfile build/build.xml ${{ matrix.os_prefix }}-dist -Dversion="${{ github.sha }}"
+
+ - name: Setup Processing
+ uses: ./.github/actions/setup
+
+ - name: Package Processing with Gradle
+ run: ./gradlew packageDistributionForCurrentOS
+
- name: Add artifact
uses: actions/upload-artifact@v4
+ if: ${{ github.event_name != 'pull_request' }}
with:
- name: processing-${{ github.ref_name }}-${{github.sha}}-${{ matrix.os_prefix }}-${{ matrix.arch }}-ant
- path: ./build/${{ matrix.os_prefix }}/processing-${{github.sha}}-${{ matrix.os_prefix}}-*
+ name: processing-${{ matrix.os_prefix }}-${{ matrix.arch }}-br_${{ github.ref_name }}
retention-days: 1
+ path: app/build/compose/binaries/main/${{ matrix.binary }}
+
+ - name: Add artifact for PR
+ if: ${{ github.event_name == 'pull_request' }}
+ id: upload-artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: processing-${{ matrix.os_prefix }}-${{ matrix.arch }}-pr_${{ github.event.pull_request.number }}
+ retention-days: 5
+ path: app/build/compose/binaries/main/${{ matrix.binary }}
+
+ # - name: Add comment with binaries
+ # if: ${{ github.event_name == 'pull_request' }}
+ # uses: marocchino/sticky-pull-request-comment@v2
+ # with:
+ # append: true
+ # header: artifacts
+ # message: |
+ # |(${{ matrix.os_prefix }}/${{ matrix.arch }})|[Download processing-${{ matrix.os_prefix }}-${{ matrix.arch }}-pr_${{ github.event.pull_request.number }}](${{ steps.upload-artifact.outputs.artifact-url }})|
+
diff --git a/.github/workflows/lock.yml b/.github/workflows/lock.yml
deleted file mode 100644
index 1997a5a4e9..0000000000
--- a/.github/workflows/lock.yml
+++ /dev/null
@@ -1,29 +0,0 @@
-name: 'Lock Threads'
-
-on:
- schedule:
- - cron: '0 6 * * *'
-
-permissions:
- contents: read
-
-jobs:
- lock:
- permissions:
- issues: write
- pull-requests: write
- runs-on: ubuntu-latest
- steps:
- - uses: dessant/lock-threads@v4
- with:
- github-token: ${{ github.token }}
- issue-lock-inactive-days: '30'
- issue-lock-comment: >
- This issue has been automatically locked. To avoid confusion
- with reports that have already been resolved, closed issues
- are automatically locked 30 days after the last comment.
- Please open a new issue for related bugs.
- pr-lock-comment: >
- This pull request has been automatically locked.
- Pull requests that have been closed are automatically
- locked 30 days after the last comment.
diff --git a/.github/workflows/pull_request-gradle.yml b/.github/workflows/pull_request-gradle.yml
deleted file mode 100644
index 4ea0bcc9db..0000000000
--- a/.github/workflows/pull_request-gradle.yml
+++ /dev/null
@@ -1,81 +0,0 @@
-name: Pull Requests with Gradle
-on:
- pull_request:
- paths-ignore:
- - '**/*.md'
- branches:
- - main
-
-jobs:
- test:
- runs-on: ubuntu-latest
- name: Test Processing
- steps:
- - name: Checkout Repository
- uses: actions/checkout@v4
- - name: Install Java
- uses: actions/setup-java@v4
- with:
- java-version: '17'
- distribution: 'temurin'
- - name: Setup Gradle
- uses: gradle/actions/setup-gradle@v4
-
- - name: Build with Gradle
- run: ./gradlew test
- build:
- name: (${{ matrix.os_prefix }}/${{ matrix.arch }}) Create Processing Build
- runs-on: ${{ matrix.os }}
- needs: test
- strategy:
- fail-fast: false
- matrix:
- include:
- - os: ubuntu-24.04-arm
- os_prefix: linux
- arch: aarch64
- binary: processing*.snap
- - os: ubuntu-latest
- os_prefix: linux
- arch: x64
- binary: processing*.snap
- - os: windows-latest
- os_prefix: windows
- arch: x64
- binary: msi/Processing-*.msi
- - os: macos-latest
- os_prefix: macos
- arch: x64
- binary: dmg/Processing-*.dmg
- - os: macos-latest
- os_prefix: macos
- arch: aarch64
- binary: dmg/Processing-*.dmg
- steps:
- - name: Install Snapcraft
- if: runner.os == 'Linux'
- uses: samuelmeuli/action-snapcraft@v3
- - name: Install LXD
- if: runner.os == 'Linux'
- uses: canonical/setup-lxd@main
-
- - name: Checkout Repository
- uses: actions/checkout@v4
- - name: Install Java
- uses: actions/setup-java@v4
- with:
- java-version: '17'
- distribution: 'temurin'
- architecture: ${{ matrix.arch }}
- - name: Setup Gradle
- uses: gradle/actions/setup-gradle@v4
-
- - name: Build with Gradle
- run: ./gradlew packageDistributionForCurrentOS
-
- - name: Add artifact
- uses: actions/upload-artifact@v4
- with:
- name: processing-${{ matrix.os_prefix }}-${{ matrix.arch }}-pr_${{ github.event.pull_request.number }}
- retention-days: 5
- path: app/build/compose/binaries/main/${{ matrix.binary }}
\ No newline at end of file
diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml
deleted file mode 100644
index 5831a166a4..0000000000
--- a/.github/workflows/pull_request.yml
+++ /dev/null
@@ -1,63 +0,0 @@
-name: Pull Requests (Legacy)
-on:
- pull_request:
- paths-ignore:
- - '**/*.md'
- branches:
- - main
-
-jobs:
- build:
- name: Create Pull Request Build for ${{ matrix.os_prefix }} (${{ matrix.arch }})
- runs-on: ${{ matrix.os }}
- permissions:
- pull-requests: write
- contents: read
- strategy:
- fail-fast: false
- matrix:
- include:
- # compiling for arm32 needs a self-hosted runner on Raspi OS (32-bit)
- - os: [self-hosted, linux, ARM]
- os_prefix: linux
- arch: arm
- - os: ubuntu-latest
- os_prefix: linux
- arch: x64
- - os: windows-latest
- os_prefix: windows
- arch: x64
- - os: macos-latest
- os_prefix: macos
- arch: x64
- - os: macos-latest
- os_prefix: macos
- arch: aarch64
- - os: macos-latest
- os_prefix: linux
- arch: aarch64
- steps:
- - name: Checkout Repository
- uses: actions/checkout@v4
- - name: Checkout Examples Repository
- uses: actions/checkout@v4
- with:
- repository: processing/processing-examples
- path: processing-examples
- - name: Install Java
- uses: actions/setup-java@v4
- with:
- java-version: '17'
- distribution: 'temurin'
- architecture: ${{ matrix.arch }}
- - name: Setup Ant
- uses: cedx/setup-ant@v3
- - name: Build Release
- run: ant -noinput -buildfile build/build.xml ${{ matrix.os_prefix }}-dist -Dversion="${{ github.sha }}" -Dplatform=${{ matrix.os_prefix }}
- - name: Add artifact
- uses: actions/upload-artifact@v4
- id: upload
- with:
- name: processing-pr${{ github.event.pull_request.number }}-${{github.sha}}-${{ matrix.os_prefix }}-${{ matrix.arch }}-ant
- path: ./build/${{ matrix.os_prefix }}/processing-${{github.sha}}-${{ matrix.os_prefix}}-*
- retention-days: 5
diff --git a/.github/workflows/release-gradle.yml b/.github/workflows/release-gradle.yml
deleted file mode 100644
index 8ec45cad0b..0000000000
--- a/.github/workflows/release-gradle.yml
+++ /dev/null
@@ -1,194 +0,0 @@
-name: Releases
-on:
- release:
- types: [published]
-
-jobs:
- version:
- runs-on: ubuntu-latest
- outputs:
- revision: ${{ steps.tag_info.outputs.revision }}
- version: ${{ steps.tag_info.outputs.version }}
- steps:
- - name: Extract version and revision
- id: tag_info
- shell: bash
- run: |
- TAG_NAME="${GITHUB_REF#refs/tags/}"
- REVISION=$(echo "$TAG_NAME" | cut -d'-' -f2)
- VERSION=$(echo "$TAG_NAME" | cut -d'-' -f3)
-
- # Set outputs for use in later jobs or steps
- echo "revision=$REVISION" >> $GITHUB_OUTPUT
- echo "version=$VERSION" >> $GITHUB_OUTPUT
- reference:
- name: Publish Processing Reference to release
- runs-on: ubuntu-latest
- permissions:
- contents: write
- needs: version
- steps:
- - name: Checkout Website Repository
- uses: actions/checkout@v4
- with:
- repository: processing/processing-website
- - name: Use Node.js 16
- uses: actions/setup-node@v3
- with:
- node-version: 16
- - name: Install dependencies
- run: npm ci
- - name: Build
- run: npm run build
- - name: Make reference.zip
- run: npm run zip
- - name: Upload reference to release
- uses: svenstaro/upload-release-action@v2
- with:
- repo_token: ${{ secrets.GITHUB_TOKEN }}
- asset_name: processing-${{ needs.version.outputs.version }}-reference.zip
- file: reference.zip
-
- publish:
- name: Publish Processing Libraries to Maven Central
- runs-on: ubuntu-latest
- needs: version
- steps:
- - name: Checkout Repository
- uses: actions/checkout@v4
- - name: Setup Java
- uses: actions/setup-java@v4
- with:
- distribution: 'temurin'
- java-version: 17
- - name: Setup Gradle
- uses: gradle/actions/setup-gradle@v4
-
- - name: Build with Gradle
- run: ./gradlew publish
- env:
- MAVEN_CENTRAL_USERNAME: ${{ secrets.MAVEN_CENTRAL_USERNAME }}
- MAVEN_CENTRAL_PASSWORD: ${{ secrets.MAVEN_CENTRAL_PASSWORD }}
-
- ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.MAVEN_CENTRAL_USERNAME }}
- ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.MAVEN_CENTRAL_PASSWORD }}
-
- ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.SIGNING_IN_MEMORY_KEY }}
- ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.SIGNING_IN_MEMORY_KEY_PASSWORD }}
-
- ORG_GRADLE_PROJECT_version: ${{ needs.version.outputs.version }}
- ORG_GRADLE_PROJECT_group: ${{ vars.GRADLE_GROUP }}
- build:
- name: (${{ matrix.os_prefix }}/${{ matrix.arch }}) Create Processing Release
- runs-on: ${{ matrix.os }}
- needs: version
- permissions:
- contents: write
- strategy:
- fail-fast: false
- matrix:
- include:
- - os: ubuntu-24.04-arm
- os_prefix: linux
- arch: aarch64
- binary: ${{ vars.SNAP_NAME }}_${{ needs.version.outputs.version }}_arm64
- extension: snap
- - os: ubuntu-latest
- os_prefix: linux
- arch: x64
- binary: ${{ vars.SNAP_NAME }}_${{ needs.version.outputs.version }}_amd64
- extension: snap
- - os: windows-latest
- os_prefix: windows
- arch: x64
- binary: msi/Processing-${{ needs.version.outputs.version }}
- extension: msi
- - os: macos-latest
- os_prefix: macos
- arch: x64
- binary: dmg/Processing-${{ needs.version.outputs.version }}
- extension: dmg
- - os: macos-latest
- os_prefix: macos
- arch: aarch64
- binary: dmg/Processing-${{ needs.version.outputs.version }}
- extension: dmg
- steps:
- - name: Install Certificates for Code Signing
- if: runner.os == 'macOS'
- continue-on-error: true
- uses: apple-actions/import-codesign-certs@v3
- with:
- p12-file-base64: ${{ secrets.CERTIFICATES_P12 }}
- p12-password: ${{ secrets.CERTIFICATES_P12_PASSWORD }}
-
- - name: Install Snapcraft
- if: runner.os == 'Linux'
- uses: samuelmeuli/action-snapcraft@v3
- - name: Install LXD
- if: runner.os == 'Linux'
- uses: canonical/setup-lxd@main
-
- - name: Checkout Repository
- uses: actions/checkout@v4
- - name: Install Java
- uses: actions/setup-java@v4
- with:
- java-version: '17.0.8'
- distribution: 'temurin'
- architecture: ${{ matrix.arch }}
- - name: Setup Gradle
- uses: gradle/actions/setup-gradle@v4
-
- - name: Build with Gradle
- run: ./gradlew packageDistributionForCurrentOS
- env:
- ORG_GRADLE_PROJECT_version: ${{ needs.version.outputs.version }}
- ORG_GRADLE_PROJECT_group: ${{ vars.GRADLE_GROUP }}
- ORG_GRADLE_PROJECT_revision: ${{ needs.version.outputs.revision }}
- ORG_GRADLE_PROJECT_compose.desktop.verbose: true
- ORG_GRADLE_PROJECT_compose.desktop.mac.sign: ${{ secrets.PROCESSING_SIGNING }}
- ORG_GRADLE_PROJECT_compose.desktop.mac.signing.identity: ${{ secrets.PROCESSING_SIGNING_IDENTITY }}
- ORG_GRADLE_PROJECT_compose.desktop.mac.notarization.appleID: ${{ secrets.PROCESSING_APPLE_ID }}
- ORG_GRADLE_PROJECT_compose.desktop.mac.notarization.password: ${{ secrets.PROCESSING_APP_PASSWORD }}
- ORG_GRADLE_PROJECT_compose.desktop.mac.notarization.teamID: ${{ secrets.PROCESSING_TEAM_ID }}
- ORG_GRADLE_PROJECT_snapname: ${{ vars.SNAP_NAME }}
- ORG_GRADLE_PROJECT_snapconfinement: ${{ vars.SNAP_CONFINEMENT }}
-
- - name: Sign files with Trusted Signing
- if: runner.os == 'Windows'
- uses: azure/trusted-signing-action@v0
- with:
- azure-tenant-id: ${{ secrets.AZURE_TENANT_ID }}
- azure-client-id: ${{ secrets.AZURE_CLIENT_ID }}
- azure-client-secret: ${{ secrets.AZURE_CLIENT_SECRET }}
- endpoint: https://eus.codesigning.azure.net/
- trusted-signing-account-name: ${{ secrets.AZURE_SIGNING_ACCOUNT_NAME }}
- certificate-profile-name: ${{ secrets.AZURE_CERTIFICATE_PROFILE_NAME }}
- files-folder: app/build/compose/binaries/main/msi
- files-folder-filter: msi
- file-digest: SHA256
- timestamp-rfc3161: http://timestamp.acs.microsoft.com
- timestamp-digest: SHA256
-
- - name: Upload portables to release
- uses: svenstaro/upload-release-action@v2
- with:
- repo_token: ${{ secrets.GITHUB_TOKEN }}
- asset_name: processing-${{ needs.version.outputs.version }}-${{ matrix.os_prefix }}-${{ matrix.arch }}-portable.zip
- file: app/build/compose/binaries/main/Processing-${{ needs.version.outputs.version }}.zip
-
- - name: Upload installers to release
- uses: svenstaro/upload-release-action@v2
- with:
- repo_token: ${{ secrets.GITHUB_TOKEN }}
- asset_name: processing-${{ needs.version.outputs.version }}-${{ matrix.os_prefix }}-${{ matrix.arch }}.${{ matrix.extension }}
- file: app/build/compose/binaries/main/${{ matrix.binary }}.${{ matrix.extension }}
-
- - name: Upload snap to Snap Store
- if: runner.os == 'Linux'
- run: snapcraft upload --release=beta app/build/compose/binaries/main/${{ matrix.binary }}.${{ matrix.extension }}
- env:
- SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.PROCESSING_SNAPCRAFT_TOKEN }}
-
-
\ No newline at end of file
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index bf4d33cd89..e21fce7a40 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -1,4 +1,4 @@
-name: Releases (Legacy)
+name: Releases
on:
release:
types: [published]
@@ -7,34 +7,59 @@ jobs:
version:
runs-on: ubuntu-latest
outputs:
- build_number: ${{ steps.tag_info.outputs.build_number }}
+ revision: ${{ steps.tag_info.outputs.revision }}
version: ${{ steps.tag_info.outputs.version }}
steps:
- - name: Extract version and build number
+ - name: Extract version and revision
id: tag_info
shell: bash
run: |
TAG_NAME="${GITHUB_REF#refs/tags/}"
- BUILD_NUMBER=$(echo "$TAG_NAME" | cut -d'-' -f2)
+ REVISION=$(echo "$TAG_NAME" | cut -d'-' -f2)
VERSION=$(echo "$TAG_NAME" | cut -d'-' -f3)
# Set outputs for use in later jobs or steps
- echo "build_number=$BUILD_NUMBER" >> $GITHUB_OUTPUT
+ echo "revision=$REVISION" >> $GITHUB_OUTPUT
echo "version=$VERSION" >> $GITHUB_OUTPUT
- publish:
- name: Publish Processing Core to Maven Central
+ reference:
+ name: Publish Processing Reference to release
runs-on: ubuntu-latest
+ permissions:
+ contents: write
needs: version
steps:
- - name: Checkout Repository
+ - name: Checkout Website Repository
uses: actions/checkout@v4
- - name: Setup Java
- uses: actions/setup-java@v4
with:
- distribution: 'temurin'
- java-version: 17
- - name: Setup Gradle
- uses: gradle/actions/setup-gradle@v4
+ repository: processing/processing-website
+ - name: Use Node.js 16
+ uses: actions/setup-node@v3
+ with:
+ node-version: 16
+ - name: Install dependencies
+ run: npm ci
+ - name: Build
+ run: npm run build
+ - name: Make reference.zip
+ run: npm run zip
+ - name: Upload reference to release
+ uses: svenstaro/upload-release-action@v2
+ with:
+ repo_token: ${{ secrets.GITHUB_TOKEN }}
+ asset_name: processing-${{ needs.version.outputs.version }}-reference.zip
+ file: reference.zip
+
+ publish-maven:
+ name: Publish Processing Libraries to Maven Central
+ runs-on: ubuntu-latest
+ needs: version
+ steps:
+ - name: Checkout Repository
+ uses: actions/checkout@v4
+
+ - name: Setup Processing
+ uses: ./.github/actions/setup
+
- name: Build with Gradle
run: ./gradlew publish
env:
@@ -48,8 +73,46 @@ jobs:
ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.SIGNING_IN_MEMORY_KEY_PASSWORD }}
ORG_GRADLE_PROJECT_version: ${{ needs.version.outputs.version }}
- build:
- name: Publish Release for ${{ matrix.os_prefix }} (${{ matrix.arch }})
+ ORG_GRADLE_PROJECT_group: ${{ vars.GRADLE_GROUP }}
+
+ publish-gradle:
+ name: Publish Processing Plugins to Gradle Plugin Portal
+ runs-on: ubuntu-latest
+ needs: version
+ steps:
+ - name: Checkout Repository
+ uses: actions/checkout@v4
+
+ - name: Setup Processing
+ uses: ./.github/actions/setup
+
+ - name: Publish plugins to Gradle Plugin Portal
+ run: ./gradlew publishPlugins
+ env:
+ GRADLE_PUBLISH_KEY: ${{ secrets.GRADLE_PUBLISH_KEY }}
+ GRADLE_PUBLISH_SECRET: ${{ secrets.GRADLE_PUBLISH_SECRET }}
+
+ ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.SIGNING_IN_MEMORY_KEY }}
+ ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.SIGNING_IN_MEMORY_KEY_PASSWORD }}
+
+ ORG_GRADLE_PROJECT_version: ${{ needs.version.outputs.version }}
+ ORG_GRADLE_PROJECT_group: ${{ vars.GRADLE_GROUP }}
+
+ - name: Publish internal plugins to Gradle Plugin Portal
+ run: ./gradlew -c gradle/plugins/settings.gradle.kts publishPlugins
+ env:
+ GRADLE_PUBLISH_KEY: ${{ secrets.GRADLE_PUBLISH_KEY }}
+ GRADLE_PUBLISH_SECRET: ${{ secrets.GRADLE_PUBLISH_SECRET }}
+
+ ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.SIGNING_IN_MEMORY_KEY }}
+ ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.SIGNING_IN_MEMORY_KEY_PASSWORD }}
+
+ ORG_GRADLE_PROJECT_version: ${{ needs.version.outputs.version }}
+ ORG_GRADLE_PROJECT_group: ${{ vars.GRADLE_GROUP }}
+ ORG_GRADLE_PROJECT_publishingGroup: ${{ vars.GRADLE_GROUP }}
+
+ release-windows:
+ name: (windows/${{ matrix.arch }}) Create Processing Release
runs-on: ${{ matrix.os }}
needs: version
permissions:
@@ -58,61 +121,263 @@ jobs:
fail-fast: false
matrix:
include:
- # compiling for arm32 needs a self-hosted runner on Raspi OS (32-bit)
- - os: [self-hosted, linux, ARM]
- os_prefix: linux
- arch: arm
- - os: ubuntu-latest
- os_prefix: linux
- arch: x64
- - os: windows-latest
- os_prefix: windows
- arch: x64
- - os: macos-latest
- os_prefix: macos
- arch: x64
- - os: macos-latest
- os_prefix: macos
- arch: aarch64
- - os: macos-latest
- os_prefix: linux
- arch: aarch64
+ - arch: x64
+ os: windows-latest
+# - arch: aarch64
+# os: windows-11-arm
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- - name: Checkout Examples Repository
- uses: actions/checkout@v4
+
+ - name: Setup Processing
+ uses: ./.github/actions/setup
+
+ - name: Build with Gradle
+ run: ./gradlew packageDistributionForCurrentOS
+ env:
+ ORG_GRADLE_PROJECT_version: ${{ needs.version.outputs.version }}
+ ORG_GRADLE_PROJECT_group: ${{ vars.GRADLE_GROUP }}
+ ORG_GRADLE_PROJECT_revision: ${{ needs.version.outputs.revision }}
+
+ - name: Sign files with Trusted Signing
+ uses: azure/trusted-signing-action@v0
with:
- repository: processing/processing-examples
- path: processing-examples
- - name: Install Java
- uses: actions/setup-java@v4
+ azure-tenant-id: ${{ secrets.AZURE_TENANT_ID }}
+ azure-client-id: ${{ secrets.AZURE_CLIENT_ID }}
+ azure-client-secret: ${{ secrets.AZURE_CLIENT_SECRET }}
+ endpoint: https://eus.codesigning.azure.net/
+ trusted-signing-account-name: ${{ secrets.AZURE_SIGNING_ACCOUNT_NAME }}
+ certificate-profile-name: ${{ secrets.AZURE_CERTIFICATE_PROFILE_NAME }}
+ files-folder: app/build/compose/binaries/main/msi
+ files-folder-filter: msi
+ file-digest: SHA256
+ timestamp-rfc3161: http://timestamp.acs.microsoft.com
+ timestamp-digest: SHA256
+
+ - name: Upload portable version
+ uses: svenstaro/upload-release-action@v2
with:
- java-version: '17'
- distribution: 'temurin'
- architecture: ${{ matrix.arch }}
- - name: Setup Ant
- uses: cedx/setup-ant@v3
- - name: Write build_number and revision to todo.txt and Base.java
- run: |
- echo "${{ needs.version.outputs.build_number }} (${{ needs.version.outputs.version }})" > todo.txt
- perl -pi -e 's/static private final int REVISION = \d+;/static private final int REVISION = ${{ needs.version.outputs.build_number }};/g; s/static private String VERSION_NAME = "\d+";\s*\/\/\$NON-NLS-1\$/static private String VERSION_NAME = "${{ needs.version.outputs.build_number }}"; \/\/\$NON-NLS-1\$/g' app/src/processing/app/Base.java
+ repo_token: ${{ secrets.GITHUB_TOKEN }}
+ asset_name: processing-${{ needs.version.outputs.version }}-windows-${{ matrix.arch }}-portable.zip
+ file: app/build/compose/binaries/main/Processing-${{ needs.version.outputs.version }}.zip
+
+ - name: Upload installer
+ uses: svenstaro/upload-release-action@v2
+ with:
+ repo_token: ${{ secrets.GITHUB_TOKEN }}
+ asset_name: processing-${{ needs.version.outputs.version }}-windows-${{ matrix.arch }}.msi
+ file: app/build/compose/binaries/main/msi/Processing-${{ needs.version.outputs.version }}.msi
+
+ release-macos:
+ name: (macOS/${{ matrix.arch }}) Create Processing Release
+ runs-on: macos-latest
+ needs: version
+ permissions:
+ contents: write
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - arch: x64
+ - arch: aarch64
+ steps:
- name: Install Certificates for Code Signing
- if: ${{ matrix.os_prefix == 'macos' }}
+ continue-on-error: true
uses: apple-actions/import-codesign-certs@v3
- with:
+ with:
p12-file-base64: ${{ secrets.CERTIFICATES_P12 }}
p12-password: ${{ secrets.CERTIFICATES_P12_PASSWORD }}
- - name: Build Release
- run: ant -noinput -buildfile build/build.xml ${{ matrix.os_prefix }}-dist -Dversion="${{ needs.version.outputs.version }}" -Dplatform=${{ matrix.os_prefix }}
+
+ - name: Checkout Repository
+ uses: actions/checkout@v4
+
+ - name: Setup Processing
+ uses: ./.github/actions/setup
+ with:
+ arch: ${{ matrix.arch }}
+
+ - name: Build with Gradle
+ run: ./gradlew packageDistributionForCurrentOS
+ env:
+ ORG_GRADLE_PROJECT_version: ${{ needs.version.outputs.version }}
+ ORG_GRADLE_PROJECT_group: ${{ vars.GRADLE_GROUP }}
+ ORG_GRADLE_PROJECT_revision: ${{ needs.version.outputs.revision }}
+ ORG_GRADLE_PROJECT_compose.desktop.verbose: true
+ ORG_GRADLE_PROJECT_compose.desktop.mac.sign: ${{ secrets.PROCESSING_SIGNING }}
+ ORG_GRADLE_PROJECT_compose.desktop.mac.signing.identity: ${{ secrets.PROCESSING_SIGNING_IDENTITY }}
+ ORG_GRADLE_PROJECT_compose.desktop.mac.notarization.appleID: ${{ secrets.PROCESSING_APPLE_ID }}
+ ORG_GRADLE_PROJECT_compose.desktop.mac.notarization.password: ${{ secrets.PROCESSING_APP_PASSWORD }}
+ ORG_GRADLE_PROJECT_compose.desktop.mac.notarization.teamID: ${{ secrets.PROCESSING_TEAM_ID }}
+
+ - name: Upload portables to release
+ uses: svenstaro/upload-release-action@v2
+ with:
+ repo_token: ${{ secrets.GITHUB_TOKEN }}
+ asset_name: processing-${{ needs.version.outputs.version }}-macos-${{ matrix.arch }}-portable.zip
+ file: app/build/compose/binaries/main/Processing-${{ needs.version.outputs.version }}.zip
+
+ - name: Upload installers to release
+ uses: svenstaro/upload-release-action@v2
+ with:
+ repo_token: ${{ secrets.GITHUB_TOKEN }}
+ asset_name: processing-${{ needs.version.outputs.version }}-macos-${{ matrix.arch }}.dmg
+ file: app/build/compose/binaries/main/dmg/Processing-${{ needs.version.outputs.version }}.dmg
+
+ release-linux:
+ name: (linux/${{ matrix.arch }}) Create Processing Release
+ runs-on: ${{ matrix.os }}
+ needs: version
+ permissions:
+ contents: write
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - os: ubuntu-24.04-arm
+ arch: aarch64
+ deb: arm64
+ - os: ubuntu-latest
+ arch: x64
+ deb: amd64
+ steps:
+ - name: Checkout Repository
+ uses: actions/checkout@v4
+
+ - name: Setup Processing
+ uses: ./.github/actions/setup
+
+ - name: Build with Gradle
+ run: ./gradlew packageDistributionForCurrentOS
env:
- PROCESSING_APP_SIGNING: true
- PROCESSING_APP_PASSWORD: ${{ secrets.PROCESSING_APP_PASSWORD }}
- PROCESSING_APPLE_ID: ${{ secrets.PROCESSING_APPLE_ID }}
- PROCESSING_TEAM_ID: ${{ secrets.PROCESSING_TEAM_ID }}
- - name: Upload binaries to release
+ ORG_GRADLE_PROJECT_version: ${{ needs.version.outputs.version }}
+ ORG_GRADLE_PROJECT_group: ${{ vars.GRADLE_GROUP }}
+ ORG_GRADLE_PROJECT_revision: ${{ needs.version.outputs.revision }}
+ ORG_GRADLE_PROJECT_compose.desktop.verbose: true
+
+ - name: Upload portable to release
+ uses: svenstaro/upload-release-action@v2
+ with:
+ repo_token: ${{ secrets.GITHUB_TOKEN }}
+ asset_name: processing-${{ needs.version.outputs.version }}-linux-${{ matrix.arch }}-portable.zip
+ file: app/build/compose/binaries/main/Processing-${{ needs.version.outputs.version }}.zip
+
+ - name: Upload installer to release
+ uses: svenstaro/upload-release-action@v2
+ with:
+ repo_token: ${{ secrets.GITHUB_TOKEN }}
+ asset_name: processing-${{ needs.version.outputs.version }}-linux-${{ matrix.arch }}.deb
+ file: app/build/compose/binaries/main/deb/processing_${{ needs.version.outputs.version }}-1_${{ matrix.deb }}.deb
+
+ - name: Add artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: processing_${{ needs.version.outputs.version }}-1_${{ matrix.deb }}.deb
+ retention-days: 1
+ path: app/build/compose/binaries/main/deb/processing_${{ needs.version.outputs.version }}-1_${{ matrix.deb }}.deb
+
+ release-linux-snap:
+ name: (linux/${{ matrix.arch }}) Create Processing Snap Release
+ runs-on: ${{ matrix.os }}
+ needs: [version, release-linux]
+ permissions:
+ contents: write
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - os: ubuntu-24.04-arm
+ arch: aarch64
+ deb: arm64
+ - os: ubuntu-latest
+ arch: x64
+ deb: amd64
+ steps:
+ - name: Install Snapcraft
+ uses: samuelmeuli/action-snapcraft@v3
+
+ - name: Install LXD
+ uses: canonical/setup-lxd@main
+
+ - name: Checkout Repository
+ uses: actions/checkout@v4
+
+ - name: Download artifact
+ uses: actions/download-artifact@v4
+ with:
+ name: processing_${{ needs.version.outputs.version }}-1_${{ matrix.deb }}.deb
+ path: app/build/compose/binaries/main/deb/
+
+ - name: Build with Gradle
+ run: ./gradlew packageSnap
+ env:
+ ORG_GRADLE_PROJECT_version: ${{ needs.version.outputs.version }}
+ ORG_GRADLE_PROJECT_group: ${{ vars.GRADLE_GROUP }}
+ ORG_GRADLE_PROJECT_revision: ${{ needs.version.outputs.revision }}
+ ORG_GRADLE_PROJECT_snapname: ${{ vars.SNAP_NAME }}
+ ORG_GRADLE_PROJECT_snapconfinement: ${{ vars.SNAP_CONFINEMENT }}
+
+ - name: Upload snap to release
+ uses: svenstaro/upload-release-action@v2
+ with:
+ repo_token: ${{ secrets.GITHUB_TOKEN }}
+ asset_name: processing-${{ needs.version.outputs.version }}-linux-${{ matrix.arch }}.snap
+ file: app/build/compose/binaries/main/${{ vars.SNAP_NAME }}_${{ needs.version.outputs.version }}_${{ matrix.deb }}.snap
+
+ - name: Upload snap to Snap Store
+ run: snapcraft upload --release=beta app/build/compose/binaries/main/${{ vars.SNAP_NAME }}_${{ needs.version.outputs.version }}_${{ matrix.deb }}.snap
+ env:
+ SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.PROCESSING_SNAPCRAFT_TOKEN }}
+ release-linux-flatpak:
+ name: (linux/${{ matrix.arch }}) Create Processing Flatpak Release
+ runs-on: ${{ matrix.os }}
+ needs: [ version, release-linux ]
+ container:
+ image: ghcr.io/flathub-infra/flatpak-github-actions:gnome-48
+ options: --privileged
+ permissions:
+ contents: write
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - os: ubuntu-24.04-arm
+ arch: aarch64
+ deb: arm64
+ farch: aarch64
+ - os: ubuntu-latest
+ arch: x64
+ deb: amd64
+ farch: x86_64
+ steps:
+ - name: Checkout Repository
+ uses: actions/checkout@v4
+
+ - name: Download artifact
+ uses: actions/download-artifact@v4
+ with:
+ name: processing_${{ needs.version.outputs.version }}-1_${{ matrix.deb }}.deb
+ path: app/build/compose/binaries/main/deb/
+
+ - name: Setup Processing
+ uses: ./.github/actions/setup
+
+ - name: Build with Gradle
+ run: ./gradlew generateFlatpakConfiguration
+ env:
+ ORG_GRADLE_PROJECT_version: ${{ needs.version.outputs.version }}
+ ORG_GRADLE_PROJECT_group: ${{ vars.GRADLE_GROUP }}
+ ORG_GRADLE_PROJECT_revision: ${{ needs.version.outputs.revision }}
+
+ - uses: flatpak/flatpak-github-actions/flatpak-builder@v6
+ with:
+ bundle: processing.flatpak
+ manifest-path: app/build/compose/binaries/main/flatpak/org.processing.pde.yml
+ cache-key: flatpak-builder-${{ github.sha }}
+ arch: ${{ matrix.farch }}
+
+ - name: Upload Flatpak to release
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
- file: ./build/${{ matrix.os_prefix }}/processing-${{ needs.version.outputs.version }}-${{ matrix.os_prefix}}-*
- file_glob: true
+ asset_name: processing-${{ needs.version.outputs.version }}-linux-${{ matrix.arch }}.flatpak
+ file: processing.flatpak
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 40c07a3035..a6e0752889 100644
--- a/.gitignore
+++ b/.gitignore
@@ -66,6 +66,19 @@ gen-external-apklibs
hs_err_pid*
replay_pid*
+# Maven ignores
+.kotlin
+.gradle
+.build/
+/core/build/
+/build/publish/
+/app/build
+/java/build/
+/build/reports
+/java/bin
+/java/libraries/svg/bin
+/java/preprocessor/build
+/java/lsp/build
### Gradle ###
.gradle
**/build/
@@ -124,4 +137,16 @@ generated/
!java/libraries/serial/library/jssc.jar
/app/windows/obj
/java/gradle/build
+/core/examples/build
/java/gradle/example/.processing
+/app/windows/obj
+/java/android/example/build
+/java/android/example/.processing
+/java/gradle/example/build
+/java/gradle/example/gradle/wrapper/gradle-wrapper.jar
+/java/gradle/example/gradle/wrapper/gradle-wrapper.properties
+/java/gradle/example/gradlew
+/java/gradle/example/gradlew.bat
+/java/gradle/example/.kotlin/errors
+/java/gradle/hotreload/build
+*.iml
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index 8328a40c38..ad532703d8 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -218,7 +218,7 @@ _Note: due to GitHub's limitations, this repository's [Contributors](https://git
 Benjamin Fox 💻 |
 e1dem 💻 |
 Aditya Chaudhary 💻 |
-  Rishab Kumar Jha 💻 |
+  Rishab Kumar Jha 💻 ⚠️ |
 Yehia Rasheed 💻 |
@@ -251,6 +251,12 @@ _Note: due to GitHub's limitations, this repository's [Contributors](https://git
 Madhav Majumdar 💻 |
 Dino_Ww 📖 |
 Avinash Kumar Deepak 💻 |
+  Sumama Sonia 💻 |
+
+
+  Salmane Khalili 🐛 |
+  Nick McIntyre ⚠️ |
+  Elijah Baron 🐛 |
diff --git a/README.md b/README.md
index 0a8a6d4369..c71c919019 100644
--- a/README.md
+++ b/README.md
@@ -7,11 +7,7 @@ Processing is a flexible software sketchbook and a programming language designed
This repository contains the source code for the [Processing](https://processing.org/) project for people who want to help improve the code.
-## Welcome to Processing 4.4!
-
-We’re excited to announce the release of Processing 4.4! This update modernizes Processing under the hood to make future development easier. Key changes include switching the build system from Ant to Gradle, starting the transition from Swing to Jetpack Compose Multiplatform for the UI, and adding Kotlin support to the codebase. To learn more, check out [Changes in 4.4.0](https://github.com/processing/processing4/wiki/Changes-in-4.4).
-
-We hope these updates will make it easier for more people to contribute to Processing. If you'd like to get involved, have a look at our [Contributor Guide](CONTRIBUTING.md).
+We welcome new contributors! Join us on the [Discord Server](https://discord.processing.org), and see the [Contributor Guide](CONTRIBUTING.md) for guidelines and tips on getting started.
## Acknowledgement
Processing was initiated in 2001 by Ben Fry and Casey Reas, who led the development and maintenance of the project until 2023. We are grateful for their vision and dedication to the project. Processing is also indebted to over two decades of contributions from the broader Processing community.
@@ -73,4 +69,4 @@ Copyright (c) 2015-now The Processing Foundation
## Contributors
See [CONTRIBUTORS.md](./CONTRIBUTORS.md) for a list of all contributors to the project.
-This project follows the [all-contributors specification](https://github.com/all-contributors/all-contributors) and the [Emoji Key](https://all-contributors.github.io/emoji-key/) ✨ for contribution types. Detailed instructions on how to add yourself or add contribution emojis to your name are [here](https://github.com/processing/processing4/issues/839). You can also post an issue or comment on a pull request with the text: `@all-contributors please add @YOUR-USERNAME for THINGS` (where `THINGS` is a comma-separated list of entries from the [list of possible contribution types](https://all-contributors.github.io/emoji-key/)) and our nice bot will add you to [CONTRIBUTORS.md](./CONTRIBUTORS.md) automatically!
\ No newline at end of file
+This project follows the [all-contributors specification](https://github.com/all-contributors/all-contributors) and the [Emoji Key](https://all-contributors.github.io/emoji-key/) ✨ for contribution types. Detailed instructions on how to add yourself or add contribution emojis to your name are [here](https://github.com/processing/processing4/issues/839). You can also post an issue or comment on a pull request with the text: `@all-contributors please add @YOUR-USERNAME for THINGS` (where `THINGS` is a comma-separated list of entries from the [list of possible contribution types](https://all-contributors.github.io/emoji-key/)) and our nice bot will add you to [CONTRIBUTORS.md](./CONTRIBUTORS.md) automatically!
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 48d49eea20..4f91e6d98c 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -65,6 +65,14 @@ compose.desktop {
targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
packageName = "Processing"
+
+
+
+ fileAssociation("application/x-processing","pde", "Processing Source Code",rootProject.file("build/shared/lib/icons/pde-512.png"), rootProject.file("build/windows/pde.ico"), rootProject.file("build/macos/pde.icns"))
+ fileAssociation("application/x-processing","pyde", "Processing Python Source Code",rootProject.file("build/shared/lib/icons/pde-512.png"), rootProject.file("build/windows/pde.ico"), rootProject.file("build/macos/pde.icns"))
+ fileAssociation("application/x-processing","pdez", "Processing Sketch Bundle",rootProject.file("build/shared/lib/icons/pde-512.png"), rootProject.file("build/windows/pdze.ico"), rootProject.file("build/macos/pdez.icns"))
+ fileAssociation("application/x-processing","pdex", "Processing Contribution Bundle", rootProject.file("build/shared/lib/icons/pde-512.png"), rootProject.file("build/windows/pdex.ico"), rootProject.file("build/macos/pdex.icns"))
+
macOS{
bundleID = "${rootProject.group}.app"
iconFile = rootProject.file("build/macos/processing.icns")
@@ -82,16 +90,13 @@ compose.desktop {
upgradeUuid = "89d8d7fe-5602-4b12-ba10-0fe78efbd602"
}
linux {
- appCategory = "Programming"
+ debMaintainer = "hello@processing.org"
menuGroup = "Development;Programming;"
+ appCategory = "Programming"
iconFile = rootProject.file("build/linux/processing.png")
// Fix fonts on some Linux distributions
jvmArgs("-Dawt.useSystemAAFontSettings=on")
- fileAssociation("pde", "Processing Source Code", "application/x-processing")
- fileAssociation("pyde", "Processing Python Source Code", "application/x-processing")
- fileAssociation("pdez", "Processing Sketch Bundle", "application/x-processing")
- fileAssociation("pdex", "Processing Contribution Bundle", "application/x-processing")
}
}
}
@@ -166,6 +171,12 @@ tasks.register("lsp-develop"){
}
val version = if(project.version == "unspecified") "1.0.0" else project.version
+val distributable = { tasks.named("createDistributable").get() }
+val arch = when (System.getProperty("os.arch")) {
+ "amd64", "x86_64" -> "amd64"
+ "aarch64" -> "arm64"
+ else -> System.getProperty("os.arch")
+}
tasks.register("installCreateDmg") {
onlyIf { OperatingSystem.current().isMacOsX }
@@ -175,11 +186,10 @@ tasks.register("packageCustomDmg"){
onlyIf { OperatingSystem.current().isMacOsX }
group = "compose desktop"
- val distributable = tasks.named("createDistributable").get()
- dependsOn(distributable, "installCreateDmg")
+ dependsOn(distributable(), "installCreateDmg")
- val packageName = distributable.packageName.get()
- val dir = distributable.destinationDir.get()
+ val packageName = distributable().packageName.get()
+ val dir = distributable().destinationDir.get()
val dmg = dir.file("../dmg/$packageName-$version.dmg").asFile
val app = dir.file("$packageName.app").asFile
@@ -234,64 +244,123 @@ tasks.register("packageCustomMsi"){
)
}
-
tasks.register("generateSnapConfiguration"){
- onlyIf { OperatingSystem.current().isLinux }
-
- val distributable = tasks.named("createDistributable").get()
- dependsOn(distributable)
-
val name = findProperty("snapname") as String? ?: rootProject.name
- val arch = when (System.getProperty("os.arch")) {
- "amd64", "x86_64" -> "amd64"
- "aarch64" -> "arm64"
- else -> System.getProperty("os.arch")
+ val confinement = (findProperty("snapconfinement") as String?).takeIf { !it.isNullOrBlank() } ?: "strict"
+ val dir = distributable().destinationDir.get()
+ val base = layout.projectDirectory.file("linux/snapcraft.yml")
+
+ doFirst {
+ replaceVariablesInFile(
+ base,
+ dir.file("../snapcraft.yaml"),
+ mapOf(
+ "name" to name,
+ "arch" to arch,
+ "version" to version as String,
+ "confinement" to confinement,
+ "deb" to "deb/${rootProject.name}_${version}-1_${arch}.deb"
+ ),
+ if (confinement == "classic") listOf("PLUGS") else emptyList()
+ )
}
- val confinement = findProperty("snapconfinement") as String? ?: "strict"
- val dir = distributable.destinationDir.get()
- val base = layout.projectDirectory.file("linux/snapcraft.base.yml")
+}
+tasks.register("generateFlatpakConfiguration"){
+ val identifier = findProperty("flathubidentifier") as String? ?: "org.processing.pde"
+
+ val dir = distributable().destinationDir.get()
+ val base = layout.projectDirectory.file("linux/flathub.yml")
doFirst {
+ replaceVariablesInFile(
+ base,
+ dir.file("../flatpak/$identifier.yml"),
+ mapOf(
+ "identifier" to identifier,
+ "deb" to dir.file("../deb/${rootProject.name}_${version}-1_${arch}.deb").asFile.absolutePath
+ ),
+ emptyList()
+ )
+ }
+}
- var content = base
- .asFile
- .readText()
- .replace("\$name", name)
- .replace("\$arch", arch)
- .replace("\$version", version as String)
- .replace("\$confinement", confinement)
- .let {
- if (confinement != "classic") return@let it
- // If confinement is not strict, remove the PLUGS section
- val start = it.indexOf("# PLUGS START")
- val end = it.indexOf("# PLUGS END")
- if (start != -1 && end != -1) {
- val before = it.substring(0, start)
- val after = it.substring(end + "# PLUGS END".length)
- return@let before + after
- }
- return@let it
+fun replaceVariablesInFile(
+ source: RegularFile,
+ target: RegularFile,
+ variables: Map,
+ sections: List
+){
+ var content = source.asFile.readText()
+ for ((key, value) in variables) {
+ content = content.replace("\$$key", value)
+ }
+ if (sections.isNotEmpty()) {
+ for (section in sections) {
+ val start = content.indexOf("# $section START")
+ val end = content.indexOf("# $section END")
+ if (start != -1 && end != -1) {
+ val before = content.substring(0, start)
+ val after = content.substring(end + "# $section END".length)
+ content = before + after
}
- dir.file("../snapcraft.yaml").asFile.writeText(content)
+ }
}
+ target.asFile.parentFile.mkdirs()
+ target.asFile.writeText(content)
}
tasks.register("packageSnap"){
onlyIf { OperatingSystem.current().isLinux }
- dependsOn("packageDeb", "generateSnapConfiguration")
+ dependsOn("generateSnapConfiguration")
group = "compose desktop"
- val distributable = tasks.named("createDistributable").get()
- workingDir = distributable.destinationDir.dir("../").get().asFile
+ workingDir = distributable().destinationDir.dir("../").get().asFile
commandLine("snapcraft")
}
+
+tasks.register("buildFlatpak"){
+ onlyIf { OperatingSystem.current().isLinux }
+ dependsOn("generateFlatpakConfiguration")
+ group = "compose desktop"
+
+ val dir = distributable().destinationDir.get()
+ val identifier = findProperty("flathubidentifier") as String? ?: "org.processing.pde"
+
+ workingDir = dir.file("../flatpak").asFile
+ commandLine(
+ "flatpak-builder",
+ "--install-deps-from=https://flathub.org/repo/flathub.flatpakrepo",
+ "--user",
+ "--force-clean",
+ "--repo=repo",
+ "output",
+ "$identifier.yml"
+ )
+}
+
+tasks.register("packageFlatpak"){
+ onlyIf { OperatingSystem.current().isLinux }
+ dependsOn("buildFlatpak")
+ group = "compose desktop"
+
+ val dir = distributable().destinationDir.get()
+ val identifier = findProperty("flathubidentifier") as String? ?: "org.processing.pde"
+
+ workingDir = dir.file("../flatpak").asFile
+ commandLine(
+ "flatpak",
+ "build-bundle",
+ "./repo",
+ "$identifier.flatpak",
+ identifier
+ )
+}
tasks.register("zipDistributable"){
dependsOn("createDistributable", "setExecutablePermissions")
group = "compose desktop"
- val distributable = tasks.named("createDistributable").get()
- val dir = distributable.destinationDir.get()
- val packageName = distributable.packageName.get()
+ val dir = distributable().destinationDir.get()
+ val packageName = distributable().packageName.get()
from(dir){ eachFile{ permissions{ unix("755") } } }
archiveBaseName.set(packageName)
@@ -317,7 +386,7 @@ afterEvaluate{
){
dependsOn("notarizeDmg")
}
- dependsOn("packageSnap", "zipDistributable")
+ dependsOn("zipDistributable")
}
}
@@ -342,6 +411,7 @@ tasks.register("includeJavaMode") {
from(java.configurations.runtimeClasspath)
into(composeResources("modes/java/mode"))
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
+ dirPermissions { unix("rwx------") }
}
tasks.register("includeJdk") {
from(Jvm.current().javaHome.absolutePath)
diff --git a/app/linux/flathub.yml b/app/linux/flathub.yml
new file mode 100644
index 0000000000..c92ab17d9d
--- /dev/null
+++ b/app/linux/flathub.yml
@@ -0,0 +1,38 @@
+id: $identifier
+runtime: org.freedesktop.Platform
+runtime-version: '24.08'
+sdk: org.freedesktop.Sdk
+command: Processing
+finish-args:
+ - --socket=x11
+ - --share=ipc
+ - --device=all
+ - --filesystem=home
+ - --socket=pulseaudio
+ - --share=network
+
+modules:
+ - name: Processing
+ buildsystem: simple
+ build-commands:
+ # Unpack Processing deb
+ - ar x processing.deb
+ - tar --zstd -xf data.tar.zst
+ - mv opt/processing/* /app/
+ - find /app/lib/app/resources/jdk/bin -type f -exec chmod +x {} +
+
+ # Install the desktop file and icon
+ - install -D /app/lib/processing-Processing.desktop /app/share/applications/$identifier.desktop
+ - sed -i 's/^Icon=.*/Icon=$identifier/' /app/share/applications/$identifier.desktop
+ - sed -i 's/^Exec=.*/Exec=\/app\/bin\/Processing/' /app/share/applications/$identifier.desktop
+
+ # Install the mimetype info
+ - install -D /app/lib/processing-Processing-MimeInfo.xml /app/share/mime/packages/$identifier.xml
+
+# - install -D /app/lib/Processing.png /app/share/icons/hicolor/512x512/apps/$identifier.png
+ - install -D /app/lib/application-x-processing.png /app/share/icons/hicolor/512x512/mimetypes/$identifier-text-x-processing.png
+
+ sources:
+ - type: file
+ path: $deb
+ dest-filename: processing.deb
\ No newline at end of file
diff --git a/app/linux/snapcraft.base.yml b/app/linux/snapcraft.yml
similarity index 95%
rename from app/linux/snapcraft.base.yml
rename to app/linux/snapcraft.yml
index 4847f0a7c8..8206681ef0 100644
--- a/app/linux/snapcraft.base.yml
+++ b/app/linux/snapcraft.yml
@@ -32,7 +32,7 @@ apps:
parts:
processing:
plugin: dump
- source: deb/processing_$version-1_$arch.deb
+ source: $deb
source-type: deb
stage-packages:
- openjdk-17-jre
diff --git a/app/macos/info.plist b/app/macos/info.plist
index 974db7d45b..f4569e85b2 100644
--- a/app/macos/info.plist
+++ b/app/macos/info.plist
@@ -9,65 +9,6 @@
- CFBundleDocumentTypes
-
-
- CFBundleTypeExtensions
-
- pde
-
- LSTypeIsPackage
-
- CFBundleTypeIconFile
- macos/pde.icns
- CFBundleTypeName
- Processing Source Code
- CFBundleTypeRole
- Editor
-
-
- CFBundleTypeExtensions
-
- pyde
-
- LSTypeIsPackage
-
- CFBundleTypeIconFile
- macos/pde.icns
- CFBundleTypeName
- Processing Python Source Code
- CFBundleTypeRole
- Editor
-
-
- CFBundleTypeExtensions
-
- pdez
-
- LSTypeIsPackage
-
- CFBundleTypeIconFile
- macos/pdez.icns
- CFBundleTypeName
- Processing Sketch Bundle
- CFBundleTypeRole
- Editor
-
-
- CFBundleTypeExtensions
-
- pdex
-
- LSTypeIsPackage
-
- CFBundleTypeIconFile
- macos/pdex.icns
- CFBundleTypeName
- Processing Contribution Bundle
- CFBundleTypeRole
- Viewer
-
-
NSCameraUsageDescription
The sketch you're running needs access to your video camera.
NSMicrophoneUsageDescription
diff --git a/app/src/main/resources/languages/PDE.properties b/app/src/main/resources/languages/PDE.properties
index 3c1ad2ab70..c9189efa3a 100644
--- a/app/src/main/resources/languages/PDE.properties
+++ b/app/src/main/resources/languages/PDE.properties
@@ -135,7 +135,7 @@ menu.tools.color_selector = Color Selector...
menu.tools.create_font = Create Font...
menu.tools.archive_sketch = Archive Sketch
menu.tools.fix_the_serial_lbrary = Fix the Serial Library
-menu.tools.install_processing_java = Install “processing-java”
+menu.tools.install_processing_java=Install “processing”
# menu.tools.add_tool = Add Tool...
menu.tools.manage_tools = Manage Tools…
diff --git a/app/src/main/resources/languages/PDE_ar.properties b/app/src/main/resources/languages/PDE_ar.properties
index 2f25f33e4d..2365f40fea 100644
--- a/app/src/main/resources/languages/PDE_ar.properties
+++ b/app/src/main/resources/languages/PDE_ar.properties
@@ -105,7 +105,7 @@ menu.tools.color_selector = أداة اختيار الألوان...
menu.tools.create_font = أداة صناعة الخطوط
menu.tools.archive_sketch = أرشفة المخطوط
menu.tools.fix_the_serial_lbrary = إصلاح مكتبة الاتصالات التسلسلية
-menu.tools.install_processing_java = تثبيت"processing-java"
+menu.tools.install_processing_java = تثبيت"processing"
menu.tools.add_tool = أضف أداة
# | File | Edit | Sketch | Debug | Tools | Help |
diff --git a/app/src/main/resources/languages/PDE_ca.properties b/app/src/main/resources/languages/PDE_ca.properties
index cdff9ec65b..77187986ba 100644
--- a/app/src/main/resources/languages/PDE_ca.properties
+++ b/app/src/main/resources/languages/PDE_ca.properties
@@ -126,7 +126,7 @@ menu.tools.color_selector = Selector de color...
menu.tools.create_font = Creació de fonts tipogràfiques...
menu.tools.archive_sketch = Arxiva l'sketch
menu.tools.fix_the_serial_lbrary = Arregla la «Serial Library»
-menu.tools.install_processing_java = Instal·la «processing-java»
+menu.tools.install_processing_java = Instal·la «processing»
# menu.tools.add_tool = Afegeix una eina...
menu.tools.manage_tools = Gestiona les eines...
diff --git a/app/src/main/resources/languages/PDE_de.properties b/app/src/main/resources/languages/PDE_de.properties
index 1b0c4b48d3..53dadf64bb 100644
--- a/app/src/main/resources/languages/PDE_de.properties
+++ b/app/src/main/resources/languages/PDE_de.properties
@@ -100,7 +100,7 @@ menu.tools.color_selector = Farbauswahl ...
menu.tools.create_font = Schrift erstellen ...
menu.tools.archive_sketch = Sketch archivieren ...
menu.tools.fix_the_serial_lbrary = "Serial Library" beheben ...
-menu.tools.install_processing_java = "processing-java" installieren ...
+menu.tools.install_processing_java="processing" installieren ...
#menu.tools.add_tool = Tool hinzufügen ...
menu.tools.manage_tools = Tools verwalten...
diff --git a/app/src/main/resources/languages/PDE_el.properties b/app/src/main/resources/languages/PDE_el.properties
index d4c794bde8..e7e29cb509 100644
--- a/app/src/main/resources/languages/PDE_el.properties
+++ b/app/src/main/resources/languages/PDE_el.properties
@@ -100,7 +100,7 @@ menu.tools.color_selector = Επιλογή Χρώματος...
menu.tools.create_font = Δημιουργία Γραμματοσειράς...
menu.tools.archive_sketch = Αρχειοθέτηση Σχεδίου
menu.tools.fix_the_serial_lbrary = Διόρθωση Σειριακής Βιβλιοθήκης
-menu.tools.install_processing_java = Εγκατάσταση της "processing-java"
+menu.tools.install_processing_java = Εγκατάσταση της "processing"
menu.tools.add_tool = Προσθήκη Εργαλείου...
# | File | Edit | Sketch | Debug | Tools | Help |
diff --git a/app/src/main/resources/languages/PDE_es.properties b/app/src/main/resources/languages/PDE_es.properties
index 781a5a261a..1df196955b 100644
--- a/app/src/main/resources/languages/PDE_es.properties
+++ b/app/src/main/resources/languages/PDE_es.properties
@@ -126,7 +126,7 @@ menu.tools.color_selector = Selector de colores...
menu.tools.create_font = Crear fuente...
menu.tools.archive_sketch = Archivar sketch
menu.tools.fix_the_serial_lbrary = Corregir «Serial library»
-menu.tools.install_processing_java = Instalar «processing-java»
+menu.tools.install_processing_java = Instalar «processing»
# menu.tools.add_tool = Añadir herramienta...
menu.tools.manage_tools = Gestionar herramientas...
diff --git a/app/src/main/resources/languages/PDE_fr.properties b/app/src/main/resources/languages/PDE_fr.properties
index d519f2e1d8..17a222a671 100644
--- a/app/src/main/resources/languages/PDE_fr.properties
+++ b/app/src/main/resources/languages/PDE_fr.properties
@@ -83,7 +83,7 @@ menu.tools.color_selector = Sélecteur de couleurs...
menu.tools.create_font = Générer la police...
menu.tools.archive_sketch = Archiver le sketch...
menu.tools.fix_the_serial_lbrary = Réparer la "Serial Library"...
-menu.tools.install_processing_java = Installer "processing-java"...
+menu.tools.install_processing_java = Installer "processing"...
menu.tools.add_tool = Ajouter un outil...
# | File | Edit | Sketch | Debug | Tools | Help |
diff --git a/app/src/main/resources/languages/PDE_it.properties b/app/src/main/resources/languages/PDE_it.properties
index 374232430e..c7931526ac 100644
--- a/app/src/main/resources/languages/PDE_it.properties
+++ b/app/src/main/resources/languages/PDE_it.properties
@@ -100,7 +100,7 @@ menu.tools.color_selector = Selezionatore dei colori...
menu.tools.create_font = Crea Font...
menu.tools.archive_sketch = Archivia Sketch
menu.tools.fix_the_serial_lbrary = Ripara la "Serial Library"
-menu.tools.install_processing_java = Installa "processing-java"
+menu.tools.install_processing_java = Installa "processing"
menu.tools.add_tool = Aggiungi Strumento...
# | File | Edit | Sketch | Debug | Tools | Help |
diff --git a/app/src/main/resources/languages/PDE_ja.properties b/app/src/main/resources/languages/PDE_ja.properties
index 3297f9ced1..e506f9c7e2 100644
--- a/app/src/main/resources/languages/PDE_ja.properties
+++ b/app/src/main/resources/languages/PDE_ja.properties
@@ -105,7 +105,7 @@ menu.tools.color_selector = 色選択...
menu.tools.create_font = フォント作成...
menu.tools.archive_sketch = スケッチをアーカイブ
menu.tools.fix_the_serial_lbrary = シリアルライブラリを修正
-menu.tools.install_processing_java = "processing-java" をインストール
+menu.tools.install_processing_java = "processing" をインストール
menu.tools.add_tool = ツールを追加...
# | File | Edit | Sketch | Debug | Tools | Help |
diff --git a/app/src/main/resources/languages/PDE_ko.properties b/app/src/main/resources/languages/PDE_ko.properties
index 1e449dacc0..f3491ccc96 100644
--- a/app/src/main/resources/languages/PDE_ko.properties
+++ b/app/src/main/resources/languages/PDE_ko.properties
@@ -77,7 +77,7 @@ menu.tools.create_font = 글꼴 생성...
menu.tools.color_selector = 색상 선택
menu.tools.archive_sketch = .zip으로 압축하기
menu.tools.fix_the_serial_lbrary = 시리얼 라이브러리 오류 수정
-menu.tools.install_processing_java = "processing-java" 설치
+menu.tools.install_processing_java = "processing" 설치
menu.tools.add_tool = 추가도구 생성...
# | File | Edit | Sketch | Debug | Tools | Help |
diff --git a/app/src/main/resources/languages/PDE_nl.properties b/app/src/main/resources/languages/PDE_nl.properties
index 7c43e5d68b..b8278d1c24 100644
--- a/app/src/main/resources/languages/PDE_nl.properties
+++ b/app/src/main/resources/languages/PDE_nl.properties
@@ -72,7 +72,7 @@ menu.tools.color_selector = Kleur Selecteren...
menu.tools.create_font = Lettertype Maken...
menu.tools.archive_sketch = Schets Archiveren
menu.tools.fix_the_serial_lbrary = Seriële Bibliotheek Herstellen
-menu.tools.install_processing_java = Installeren "processing-java"
+menu.tools.install_processing_java = Installeren "processing"
menu.tools.add_tool = Tool Toevoegen...
# | File | Edit | Sketch | Debug | Tools | Help |
diff --git a/app/src/main/resources/languages/PDE_pt.properties b/app/src/main/resources/languages/PDE_pt.properties
index 44b5c7f639..2c9e3bc527 100644
--- a/app/src/main/resources/languages/PDE_pt.properties
+++ b/app/src/main/resources/languages/PDE_pt.properties
@@ -71,7 +71,7 @@ menu.tools.color_selector = Selector de Cor...
menu.tools.create_font = Criar Fonte...
menu.tools.archive_sketch = Arquivar Sketch
menu.tools.fix_the_serial_lbrary = Corrijir a Biblioteca Serial
-menu.tools.install_processing_java = Instalar "processing-java"
+menu.tools.install_processing_java = Instalar "processing"
menu.tools.add_tool = Adicionar Ferramenta...
# | File | Edit | Sketch | Debug | Tools | Help |
diff --git a/app/src/main/resources/languages/PDE_ru.properties b/app/src/main/resources/languages/PDE_ru.properties
index ff71d4ce49..b5df6de76f 100644
--- a/app/src/main/resources/languages/PDE_ru.properties
+++ b/app/src/main/resources/languages/PDE_ru.properties
@@ -100,7 +100,7 @@ menu.tools.color_selector = Выбрать цвет...
menu.tools.create_font = Создать шрифты...
menu.tools.archive_sketch = Архивировать набросок
menu.tools.fix_the_serial_lbrary = Исправить библиотеку Serial
-menu.tools.install_processing_java = Установить "processing-java"
+menu.tools.install_processing_java = Установить "processing"
menu.tools.add_tool = Добавить инструмент...
# | File | Edit | Sketch | Debug | Tools | Help |
diff --git a/app/src/main/resources/languages/PDE_tr.properties b/app/src/main/resources/languages/PDE_tr.properties
index 8b8b7f7f55..8d0782939b 100644
--- a/app/src/main/resources/languages/PDE_tr.properties
+++ b/app/src/main/resources/languages/PDE_tr.properties
@@ -72,7 +72,7 @@ menu.tools.color_selector = Renk Seçici
menu.tools.create_font = Yazı Tipi Oluştur...
menu.tools.archive_sketch = Sketch'i Arşivle
menu.tools.fix_the_serial_lbrary = "Serial Kütüphanesi"ni Onar...
-menu.tools.install_processing_java = "Processing-Java"yı Yükle...
+menu.tools.install_processing_java = "Processing"yı Yükle...
menu.tools.add_tool = Araç Ekle...
# | File | Edit | Sketch | Debug | Tools | Help |
diff --git a/app/src/main/resources/languages/PDE_uk.properties b/app/src/main/resources/languages/PDE_uk.properties
index c4d4d6baf6..78c8b07050 100644
--- a/app/src/main/resources/languages/PDE_uk.properties
+++ b/app/src/main/resources/languages/PDE_uk.properties
@@ -126,7 +126,7 @@ menu.tools.color_selector = Вибрати колір...
menu.tools.create_font = Створити шрифт...
menu.tools.archive_sketch = Архівувати ескіз
menu.tools.fix_the_serial_lbrary = Виправити Serial Library
-menu.tools.install_processing_java = Встановити "processing-java"
+menu.tools.install_processing_java = Встановити "processing"
# menu.tools.add_tool = Додати інструмент...
menu.tools.manage_tools = Керувати інструментами...
diff --git a/app/src/main/resources/languages/PDE_zh-CN.properties b/app/src/main/resources/languages/PDE_zh-CN.properties
index 77d3c15acc..d475571a58 100644
--- a/app/src/main/resources/languages/PDE_zh-CN.properties
+++ b/app/src/main/resources/languages/PDE_zh-CN.properties
@@ -92,7 +92,7 @@ menu.tools.color_selector = 颜色选择器...
menu.tools.create_font = 创建字体...
menu.tools.archive_sketch = 速写本压缩输出
menu.tools.fix_the_serial_lbrary = 修复串口库文件
-menu.tools.install_processing_java = 安装 "processing-java"
+menu.tools.install_processing_java = 安装 "processing"
menu.tools.add_tool = 添加工具...
# | File | Edit | Sketch | Debug | Tools | Help |
diff --git a/app/src/main/resources/languages/PDE_zh-TW.properties b/app/src/main/resources/languages/PDE_zh-TW.properties
index e3642d0999..33a7e48e99 100644
--- a/app/src/main/resources/languages/PDE_zh-TW.properties
+++ b/app/src/main/resources/languages/PDE_zh-TW.properties
@@ -126,7 +126,7 @@ menu.tools.color_selector = 顏色選擇器(Color Selector)...
menu.tools.create_font = 建字型檔(Create Font)...
menu.tools.archive_sketch = 壓縮程式專案(Archive Sketch)
menu.tools.fix_the_serial_library = 修復Serial函式庫(Fix the Serial Library)
-menu.tools.install_processing_java = 安裝 "processing-java"
+menu.tools.install_processing_java = 安裝 "processing"
menu.tools.add_tool = 新增工具(Add Tool)...
# | File | Edit | Sketch | Debug | Tools | Help |
diff --git a/app/src/processing/app/Preferences.kt b/app/src/processing/app/Preferences.kt
index 1b344a5e11..4e139b08e1 100644
--- a/app/src/processing/app/Preferences.kt
+++ b/app/src/processing/app/Preferences.kt
@@ -35,6 +35,9 @@ class ReactiveProperties : Properties() {
operator fun set(key: String, value: String) {
setProperty(key, value)
}
+ fun remove() {
+ TODO("Not yet implemented")
+ }
}
/*
@@ -77,7 +80,7 @@ fun PreferencesProvider(content: @Composable () -> Unit) {
val preferencesFileOverride: File? = System.getProperty("processing.app.preferences.file")?.let { File(it) }
val preferencesDebounceOverride: Long? = System.getProperty("processing.app.preferences.debounce")?.toLongOrNull()
- val settingsFolder = Settings.getFolder()
+ val settingsFolder = Base.getSettingsOverride() ?: Settings.getFolder()
val preferencesFile = preferencesFileOverride ?: settingsFolder.resolve(PREFERENCES_FILE_NAME)
if (!preferencesFile.exists()) {
diff --git a/app/src/processing/app/Util.java b/app/src/processing/app/Util.java
index 4c94af5fe5..f87a6fdcef 100644
--- a/app/src/processing/app/Util.java
+++ b/app/src/processing/app/Util.java
@@ -60,16 +60,17 @@ static public int countLines(String what) {
*/
static public byte[] loadBytesRaw(File file) throws IOException {
int size = (int) file.length();
- FileInputStream input = new FileInputStream(file);
- byte[] buffer = new byte[size];
- int offset = 0;
- int bytesRead;
- while ((bytesRead = input.read(buffer, offset, size-offset)) != -1) {
- offset += bytesRead;
- if (bytesRead == 0) break;
- }
- input.close(); // weren't properly being closed
- return buffer;
+ byte[] buffer;
+ try (FileInputStream input = new FileInputStream(file)) {
+ buffer = new byte[size];
+ int offset = 0;
+ int bytesRead;
+ while ((bytesRead = input.read(buffer, offset, size - offset)) != -1) {
+ offset += bytesRead;
+ if (bytesRead == 0) break;
+ }
+ }
+ return buffer;
}
@@ -143,7 +144,7 @@ static public StringDict readSettings(String filename, String[] lines, boolean a
line = line.substring(0, line.indexOf('#')).trim();
}
- if (line.length() != 0 && line.charAt(0) != '#') {
+ if (!line.isEmpty() && line.charAt(0) != '#') {
int equals = line.indexOf('=');
if (equals == -1) {
if (filename != null) {
@@ -161,26 +162,20 @@ static public StringDict readSettings(String filename, String[] lines, boolean a
}
- static public void copyFile(File sourceFile,
- File targetFile) throws IOException {
- BufferedInputStream from =
- new BufferedInputStream(new FileInputStream(sourceFile));
- BufferedOutputStream to =
- new BufferedOutputStream(new FileOutputStream(targetFile));
+ static public void copyFile(File sourceFile, File targetFile) throws IOException {
+ try (
+ BufferedInputStream from = new BufferedInputStream(new FileInputStream(sourceFile));
+ BufferedOutputStream to = new BufferedOutputStream(new FileOutputStream(targetFile))) {
byte[] buffer = new byte[16 * 1024];
int bytesRead;
while ((bytesRead = from.read(buffer)) != -1) {
to.write(buffer, 0, bytesRead);
}
- from.close();
-
- to.flush();
- to.close();
-
//noinspection ResultOfMethodCallIgnored
targetFile.setLastModified(sourceFile.lastModified());
//noinspection ResultOfMethodCallIgnored
targetFile.setExecutable(sourceFile.canExecute());
+ }
}
@@ -218,13 +213,15 @@ static public void saveFile(String text, File file) throws IOException {
file.getAbsolutePath());
}
// Could use saveStrings(), but we wouldn't be able to checkError()
- PrintWriter writer = PApplet.createWriter(temp);
- for (String line : lines) {
- writer.println(line);
- }
- boolean error = writer.checkError(); // calls flush()
- writer.close(); // attempt to close regardless
- if (error) {
+ boolean error;
+ try (PrintWriter writer = PApplet.createWriter(temp)) {
+ for (String line : lines) {
+ writer.println(line);
+ }
+ // calls flush()
+ error = writer.checkError();
+ }
+ if (error) {
throw new IOException("Error while trying to save " + file);
}
@@ -589,7 +586,7 @@ static public StringList packageListFromClassPath(String path) {
for (String piece : pieces) {
//System.out.println("checking piece '" + pieces[i] + "'");
- if (piece.length() != 0) {
+ if (!piece.isEmpty()) {
if (piece.toLowerCase().endsWith(".jar") ||
piece.toLowerCase().endsWith(".zip")) {
//System.out.println("checking " + pieces[i]);
@@ -623,8 +620,7 @@ static public StringList packageListFromClassPath(String path) {
static private void packageListFromZip(String filename, StringList list) {
- try {
- ZipFile file = new ZipFile(filename);
+ try (ZipFile file = new ZipFile(filename);) {
Enumeration> entries = file.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = (ZipEntry) entries.nextElement();
@@ -643,7 +639,6 @@ static private void packageListFromZip(String filename, StringList list) {
}
}
}
- file.close();
} catch (IOException e) {
System.err.println("Ignoring " + filename + " (" + e.getMessage() + ")");
//e.printStackTrace();
@@ -688,9 +683,7 @@ static private void packageListFromFolder(File dir, String sofar,
* Ignores (does not extract) any __MACOSX files from macOS archives.
*/
static public void unzip(File zipFile, File dest) throws IOException {
- FileInputStream fis = new FileInputStream(zipFile);
- CheckedInputStream checksum = new CheckedInputStream(fis, new Adler32());
- ZipInputStream zis = new ZipInputStream(new BufferedInputStream(checksum));
+ try (ZipInputStream zis = new ZipInputStream( new BufferedInputStream( new CheckedInputStream( new FileInputStream(zipFile), new Adler32())))) {
ZipEntry entry;
while ((entry = zis.getNextEntry()) != null) {
final String name = entry.getName();
@@ -710,25 +703,26 @@ static public void unzip(File zipFile, File dest) throws IOException {
}
}
}
+ }
static protected void unzipEntry(ZipInputStream zin, File f) throws IOException {
- FileOutputStream out = new FileOutputStream(f);
- byte[] b = new byte[512];
- int len;
- while ((len = zin.read(b)) != -1) {
- out.write(b, 0, len);
- }
- out.flush();
- out.close();
+ try (FileOutputStream out = new FileOutputStream(f)) {
+ byte[] b = new byte[512];
+ int len;
+ while ((len = zin.read(b)) != -1) {
+ out.write(b, 0, len);
+ }
+ out.flush();
+ }
}
static public byte[] gzipEncode(byte[] what) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
- GZIPOutputStream output = new GZIPOutputStream(baos);
- PApplet.saveStream(output, new ByteArrayInputStream(what));
- output.close();
+ try (GZIPOutputStream output = new GZIPOutputStream(baos);) {
+ PApplet.saveStream(output, new ByteArrayInputStream(what));
+ }
return baos.toByteArray();
}
diff --git a/app/src/processing/app/tools/InstallCommander.java b/app/src/processing/app/tools/InstallCommander.java
index 33eabc6f68..2978cc1a55 100644
--- a/app/src/processing/app/tools/InstallCommander.java
+++ b/app/src/processing/app/tools/InstallCommander.java
@@ -21,13 +21,6 @@
package processing.app.tools;
-import java.io.File;
-import java.io.FilenameFilter;
-import java.io.IOException;
-import java.io.PrintWriter;
-
-import javax.swing.JOptionPane;
-
import processing.app.Base;
import processing.app.Language;
import processing.app.Messages;
@@ -36,6 +29,12 @@
import processing.core.PApplet;
import processing.data.StringList;
+import javax.swing.*;
+import java.io.File;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.PrintWriter;
+
public class InstallCommander implements Tool {
Base base;
@@ -55,26 +54,31 @@ public void run() {
try {
Editor editor = base.getActiveEditor();
- final String primary =
- "Install processing-java for all users?";
- final String secondary =
- "This will install the processing-java program, which is capable " +
- "of building and running Java Mode sketches from the command line. " +
- "Click “Yes” to install it for all users (an administrator password " +
- "is required), or “No” to place the program in your home directory. " +
- "If you rename or move Processing.app, " +
- "you'll need to reinstall the tool.";
+ final String messageHtml = """
+
+
+ Install processing for all users?
+
+ This utility will install the processing command line interface,
+ which is capable of building and running
+ sketches from the command line.
+
+ Click “Yes” to install it for all users
+ (an administrator password is required).
+
+ or “No” to place the program in your home directory.
+
+ If you rename or move Processing.app,
+ you'll need to reinstall the tool.
+
+ """.replaceAll("\n", " ");
int result =
- JOptionPane.showConfirmDialog(editor,
- " " +
- " " +
- "" + primary + "" +
- "" + secondary + "
",
- "Commander",
+ JOptionPane.showConfirmDialog(editor, messageHtml,
+ "Processing CLI",
JOptionPane.YES_NO_CANCEL_OPTION,
JOptionPane.QUESTION_MESSAGE);
@@ -92,7 +96,7 @@ public void run() {
var appBinary = (resourcesDir
.split("\\.app")[0] + ".app/Contents/MacOS/Processing")
.replaceAll(" ", "\\\\ ");
- writer.print(appBinary + " cli $@");
+ writer.print(appBinary + " $@");
} else {
// Ant based distributable
@@ -130,7 +134,7 @@ public void run() {
// Moving to /usr/local/bin instead of /usr/bin for compatibility
// with OS X 10.11 and its "System Integrity Protection"
// https://github.com/processing/processing/issues/3497
- String targetPath = "/usr/local/bin/processing-java";
+ String targetPath = "/usr/local/bin/processing";
// Remove the old version in case it exists
// https://github.com/processing/processing/issues/3786
String oldPath = "/usr/bin/processing-java";
@@ -139,14 +143,14 @@ public void run() {
" && /bin/mv " + sourcePath + " " + targetPath;
String appleScript =
"do shell script \"" + shellScript + "\" with administrator privileges";
- PApplet.exec(new String[] { "osascript", "-e", appleScript });
+ PApplet.exec("osascript", "-e", appleScript);
} else if (result == JOptionPane.NO_OPTION) {
- File targetFile = new File(System.getProperty("user.home"), "processing-java");
+ File targetFile = new File(System.getProperty("user.home"), "processing");
String targetPath = targetFile.getAbsolutePath();
if (targetFile.exists()) {
Messages.showWarning("File Already Exists",
- "The processing-java program already exists at:\n" +
+ "The processing program already exists at:\n" +
targetPath + "\n" +
"Please remove it and try again.");
} else {
diff --git a/app/src/processing/app/ui/Editor.java b/app/src/processing/app/ui/Editor.java
index 3ef108a27d..52650ad659 100644
--- a/app/src/processing/app/ui/Editor.java
+++ b/app/src/processing/app/ui/Editor.java
@@ -148,6 +148,18 @@ public abstract class Editor extends JFrame implements RunnerListener {
protected Editor(final Base base, String path, final EditorState state,
final Mode mode) throws EditorException {
super("Processing", state.getConfig());
+ if (Platform.isLinux()) {
+ // If the frame is already displayable, dispose it to allow undecorated change
+ if (isDisplayable()) {
+ dispose();
+ }
+ try {
+ setUndecorated(true);
+ } catch (IllegalComponentStateException e) {
+ System.err.println("Could not set undecorated: " + e.getMessage());
+ }
+ getRootPane().setWindowDecorationStyle(JRootPane.FRAME);
+ }
this.base = base;
this.state = state;
this.mode = mode;
@@ -211,10 +223,7 @@ public void windowDeactivated(WindowEvent e) {
spacer.setAlignmentX(Component.LEFT_ALIGNMENT);
box.add(spacer);
}
- if (Platform.isLinux()) {
- setUndecorated(true);
- getRootPane().setWindowDecorationStyle(JRootPane.FRAME);
- }
+
rebuildModePopup();
toolbar = createToolbar();
@@ -2946,4 +2955,13 @@ public void show(Component component, int x, int y) {
super.show(component, x, y);
}
}
+
+ /**
+ * Called when clicking on the version number in the footer.
+ * Return a string with diagnostic info from the sketch,
+ * or empty string (or null) if not implemented/available.
+ */
+ public String getSketchDiagnostics() {
+ return "";
+ }
}
diff --git a/app/src/processing/app/ui/EditorFooter.java b/app/src/processing/app/ui/EditorFooter.java
index 94860a0abf..7efef4132e 100644
--- a/app/src/processing/app/ui/EditorFooter.java
+++ b/app/src/processing/app/ui/EditorFooter.java
@@ -109,7 +109,7 @@ public void mousePressed(MouseEvent e) {
Base.DEBUG = !Base.DEBUG;
editor.updateDevelopMenu();
}
- copyDebugInformationToClipboard();
+ copyFullDiagnosticsToClipboard();
}
});
@@ -120,13 +120,23 @@ public void mousePressed(MouseEvent e) {
updateTheme();
}
- public static void copyDebugInformationToClipboard() {
- var debugInformation = String.join("\n",
+ public static String getSystemDebugInformation() {
+ return String.join("\n",
"Version: " + Base.getVersionName(),
"Revision: " + Base.getRevision(),
"OS: " + System.getProperty("os.name") + " " + System.getProperty("os.version") + " " + System.getProperty("os.arch"),
"Java: " + System.getProperty("java.version") + " " + System.getProperty("java.vendor")
);
+ }
+
+ public static void copyDebugInformationToClipboard() {
+ var stringSelection = new StringSelection(getSystemDebugInformation());
+ var clipboard = java.awt.Toolkit.getDefaultToolkit().getSystemClipboard();
+ clipboard.setContents(stringSelection, null);
+ }
+
+ public void copyFullDiagnosticsToClipboard() {
+ var debugInformation = getSystemDebugInformation() + "\n\n" + editor.getSketchDiagnostics();
var stringSelection = new StringSelection(debugInformation);
var clipboard = java.awt.Toolkit.getDefaultToolkit().getSystemClipboard();
clipboard.setContents(stringSelection, null);
diff --git a/app/src/processing/app/ui/PDEPreferences.kt b/app/src/processing/app/ui/PDEPreferences.kt
index 020856b8d4..8d1e749c9f 100644
--- a/app/src/processing/app/ui/PDEPreferences.kt
+++ b/app/src/processing/app/ui/PDEPreferences.kt
@@ -27,6 +27,7 @@ import androidx.compose.ui.window.application
import com.mikepenz.markdown.compose.Markdown
import com.mikepenz.markdown.m3.markdownColor
import com.mikepenz.markdown.m3.markdownTypography
+import processing.app.DEFAULTS_FILE_NAME
import processing.app.LocalPreferences
import processing.app.ReactiveProperties
import processing.app.ui.PDEPreferences.Companion.preferences
@@ -35,6 +36,7 @@ import processing.app.ui.theme.*
import java.awt.Dimension
import java.awt.event.WindowEvent
import java.awt.event.WindowListener
+import java.util.*
import javax.swing.SwingUtilities
import javax.swing.WindowConstants
@@ -592,9 +594,16 @@ fun PDEPreferencePane.showPane(groups: PDEPreferenceGroups) {
val prefs = LocalPreferences.current
TextButton(
onClick = {
+ val defaultsStream =
+ ClassLoader.getSystemResourceAsStream(DEFAULTS_FILE_NAME) ?: return@TextButton
+ val defaults = Properties().apply {
+ defaultsStream.reader(Charsets.UTF_8).use {
+ load(it)
+ }
+ }
groups.forEach { group ->
group.forEach { pref ->
- prefs.remove(pref.key)
+ prefs[pref.key] = defaults.getProperty(pref.key, "")
}
}
}
diff --git a/app/src/processing/app/ui/PDEWelcome.kt b/app/src/processing/app/ui/PDEWelcome.kt
index 0370fc7533..0a13fa5346 100644
--- a/app/src/processing/app/ui/PDEWelcome.kt
+++ b/app/src/processing/app/ui/PDEWelcome.kt
@@ -571,7 +571,7 @@ fun showWelcomeScreen(base: Base? = null) {
unique = WelcomeScreen::class,
fullWindowContent = true
) {
- PDEWelcomeWithSurvey(base)
+ PDEWelcome(base)
}
}
@@ -620,12 +620,12 @@ fun main(){
application {
PDEComposeWindow(titleKey = titleKey, size = size, fullWindowContent = true) {
PDETheme(darkTheme = true) {
- PDEWelcomeWithSurvey()
+ PDEWelcome()
}
}
PDEComposeWindow(titleKey = titleKey, size = size, fullWindowContent = true) {
PDETheme(darkTheme = false) {
- PDEWelcomeWithSurvey()
+ PDEWelcome()
}
}
}
diff --git a/app/src/processing/app/ui/WelcomeSurvey.kt b/app/src/processing/app/ui/WelcomeSurvey.kt
index c0ebb23acf..e68dc44655 100644
--- a/app/src/processing/app/ui/WelcomeSurvey.kt
+++ b/app/src/processing/app/ui/WelcomeSurvey.kt
@@ -23,17 +23,6 @@ import processing.app.ui.theme.LocalLocale
import processing.app.ui.theme.PDETheme
import javax.swing.JComponent
-
-fun addSurveyToWelcomeScreen(): JComponent {
- return ComposePanel().apply {
- setContent {
- PDETheme {
- SurveyInvitation()
- }
- }
- }
-}
-
@Composable
fun SurveyInvitation() {
val locale = LocalLocale.current
diff --git a/app/src/processing/app/ui/theme/Locale.kt b/app/src/processing/app/ui/theme/Locale.kt
index 90de50a712..4d16f2a5a5 100644
--- a/app/src/processing/app/ui/theme/Locale.kt
+++ b/app/src/processing/app/ui/theme/Locale.kt
@@ -3,6 +3,7 @@ package processing.app.ui.theme
import androidx.compose.runtime.*
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.unit.LayoutDirection
+import processing.app.Base
import processing.app.Messages
import processing.app.watchFile
import processing.utils.Settings
@@ -90,7 +91,7 @@ var LastLocaleUpdate by mutableStateOf(0L)
*/
@Composable
fun LocaleProvider(content: @Composable () -> Unit) {
- val settingsFolder = Settings.getFolder()
+ val settingsFolder = Base.getSettingsOverride() ?: Settings.getFolder()
val languageFile = File(settingsFolder, "language.txt")
watchFile(languageFile)
diff --git a/app/utils/build.gradle.kts b/app/utils/build.gradle.kts
index 193188f956..1618e1706b 100644
--- a/app/utils/build.gradle.kts
+++ b/app/utils/build.gradle.kts
@@ -1,5 +1,6 @@
plugins {
id("java")
+ alias(libs.plugins.mavenPublish)
}
repositories {
@@ -11,6 +12,15 @@ dependencies {
testImplementation("org.junit.jupiter:junit-jupiter")
}
+publishing{
+ repositories{
+ maven {
+ name = "App"
+ url = uri(project(":app").layout.buildDirectory.dir("resources-bundled/common/repository").get().asFile.absolutePath)
+ }
+ }
+}
+
tasks.test {
useJUnitPlatform()
}
\ No newline at end of file
diff --git a/build.gradle.kts b/build.gradle.kts
index 6c8c5262cb..371e34bc29 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -3,6 +3,7 @@ plugins {
alias(libs.plugins.compose.compiler) apply false
alias(libs.plugins.jetbrainsCompose) apply false
+ alias(libs.plugins.mavenPublish) apply false
alias(libs.plugins.versions)
}
diff --git a/build/linux/processing-pde.xml b/build/linux/processing-pde.xml
index 3b8a6837be..a869b1977d 100644
--- a/build/linux/processing-pde.xml
+++ b/build/linux/processing-pde.xml
@@ -2,41 +2,42 @@
Processing source code
- شفرة مصدر Processing
- Kryničny kod Processing
- Изходен код на Processing
- codi font en Processing
- Processingkildekode
- Processing-Quelltext
- πηγαίος κώδικας Processing
- Processing source code
- Processing-fontkodo
- código fuente en Processing
- Processing iturburu-kodea
- Processing-lähdekoodi
- code source Processing
- cód foinseach Processing
- Processing-forráskód
- Kode program Processing
- Codice sorgente Processing
- Processing ソースコード
- Processing pradinis kodas
- Processing pirmkods
- Kod sumber Processing
- Processing-kildekode
- Processing-broncode
- Processing-kjeldekode
- Kod źródłowy Processing
- código fonte Processing
- Código fonte Processing
- исходный код Processing
- Kod burues Processing
- Processing-källkod
- Вихідний код на мові Processing
- Mã nguồn Processing
- Processing 源代码
- Processing 源代碼
+ شفرة مصدر Processing
+ Kryničny kod Processing
+ Изходен код на Processing
+ codi font en Processing
+ Processingkildekode
+ Processing-Quelltext
+ πηγαίος κώδικας Processing
+ Processing source code
+ Processing-fontkodo
+ código fuente en Processing
+ Processing iturburu-kodea
+ Processing-lähdekoodi
+ code source Processing
+ cód foinseach Processing
+ Processing-forráskód
+ Kode program Processing
+ Codice sorgente Processing
+ Processing ソースコード
+ Processing pradinis kodas
+ Processing pirmkods
+ Kod sumber Processing
+ Processing-kildekode
+ Processing-broncode
+ Processing-kjeldekode
+ Kod źródłowy Processing
+ código fonte Processing
+ Código fonte Processing
+ исходный код Processing
+ Kod burues Processing
+ Processing-källkod
+ Вихідний код на мові Processing
+ Mã nguồn Processing
+ Processing 源代码
+ Processing 源代碼
+
diff --git a/build/shared/lib/icons/app-linux.svg b/build/shared/lib/icons/app-linux.svg
new file mode 100644
index 0000000000..9b5ccd52b1
--- /dev/null
+++ b/build/shared/lib/icons/app-linux.svg
@@ -0,0 +1,19 @@
+
diff --git a/core/build.gradle.kts b/core/build.gradle.kts
index e0c028f9ab..16593450ec 100644
--- a/core/build.gradle.kts
+++ b/core/build.gradle.kts
@@ -8,12 +8,14 @@ plugins {
repositories {
mavenCentral()
+ maven { url = uri("https://jogamp.org/deployment/maven") }
}
sourceSets{
main{
java{
srcDirs("src")
+ exclude("**/*.jnilib")
}
resources{
srcDirs("src")
@@ -76,3 +78,6 @@ tasks.withType {
tasks.compileJava{
options.encoding = "UTF-8"
}
+tasks.javadoc{
+ options.encoding = "UTF-8"
+}
diff --git a/core/src/processing/core/PApplet.java b/core/src/processing/core/PApplet.java
index 4fccd1a535..d9df211eb7 100644
--- a/core/src/processing/core/PApplet.java
+++ b/core/src/processing/core/PApplet.java
@@ -705,7 +705,7 @@ public class PApplet implements PConstants {
protected boolean exitCalled;
// ok to be static because it's not possible to mix enabled/disabled
- static protected boolean disableAWT;
+ static protected boolean disableAWT = System.getProperty("processing.awt.disable", "false").equals("true");;
// messages to send if attached as an external vm
@@ -9940,19 +9940,21 @@ static public void runSketch(final String[] args,
System.exit(1);
}
- boolean external = false;
- int[] location = null;
- int[] editorLocation = null;
+ boolean external = System.getProperty("processing.external", "false").equals("true");;
+ int[] location = System.getProperty("processing.location", null) != null ?
+ parseInt(split(System.getProperty("processing.location"), ',')) : null;
+ int[] editorLocation = System.getProperty("processing.editor.location", null) != null ?
+ parseInt(split(System.getProperty("processing.editor.location"), ',')) : null;
String name = null;
int windowColor = 0;
int stopColor = 0xff808080;
- boolean hideStop = false;
+ boolean hideStop = System.getProperty("processing.stop.hide", "false").equals("true");
int displayNum = -1; // use default
- boolean present = false;
- boolean fullScreen = false;
- float uiScale = 0;
+ boolean present = System.getProperty("processing.present", "false").equals("true");
+ boolean fullScreen = System.getProperty("processing.fullscreen", "false").equals("true");
+ float uiScale = parseInt(System.getProperty("processing.uiScale", "0"), 0);
String param, value;
String folder = calcSketchPath();
diff --git a/core/src/processing/core/PGraphics.java b/core/src/processing/core/PGraphics.java
index 0b9f0d2ed4..1ada6aa2ae 100644
--- a/core/src/processing/core/PGraphics.java
+++ b/core/src/processing/core/PGraphics.java
@@ -4768,7 +4768,7 @@ public void text(char[] chars, int start, int stop, float x, float y) {
}
// int start = 0;
- int index = 0;
+ int index = start;
while (index < stop) { //length) {
if (chars[index] == '\n') {
textLineAlignImpl(chars, start, index, x, y);
diff --git a/core/src/processing/core/PMatrix.java b/core/src/processing/core/PMatrix.java
index edb1d260eb..df15dc4a40 100644
--- a/core/src/processing/core/PMatrix.java
+++ b/core/src/processing/core/PMatrix.java
@@ -205,4 +205,8 @@ public void preApply(float n00, float n01, float n02, float n03,
* @return the determinant of the matrix
*/
public float determinant();
+ /**
+ * Print the matrix data to the console.
+ */
+ public void print();
}
diff --git a/core/src/processing/core/PMatrix2D.java b/core/src/processing/core/PMatrix2D.java
index c30a3504e3..bc6538f918 100644
--- a/core/src/processing/core/PMatrix2D.java
+++ b/core/src/processing/core/PMatrix2D.java
@@ -466,26 +466,15 @@ public float determinant() {
//////////////////////////////////////////////////////////////
+ @Override
public void print() {
- int big = (int) abs(max(PApplet.max(abs(m00), abs(m01), abs(m02)),
- PApplet.max(abs(m10), abs(m11), abs(m12))));
-
- int digits = 1;
- if (Float.isNaN(big) || Float.isInfinite(big)) { // avoid infinite loop
- digits = 5;
- } else {
- while ((big /= 10) != 0) digits++; // cheap log()
- }
-
- System.out.println(PApplet.nfs(m00, digits, 4) + " " +
- PApplet.nfs(m01, digits, 4) + " " +
- PApplet.nfs(m02, digits, 4));
-
- System.out.println(PApplet.nfs(m10, digits, 4) + " " +
- PApplet.nfs(m11, digits, 4) + " " +
- PApplet.nfs(m12, digits, 4));
+ System.out.print(toString());
+ }
- System.out.println();
+ @Override
+ public String toString() {
+ return PApplet.nfs(m00, 1, 4) + " " + PApplet.nfs(m01, 1, 4) + " " + PApplet.nfs(m02, 1, 4) + "\n" +
+ PApplet.nfs(m10, 1, 4) + " " + PApplet.nfs(m11, 1, 4) + " " + PApplet.nfs(m12, 1, 4) + "\n";
}
diff --git a/core/src/processing/core/PMatrix3D.java b/core/src/processing/core/PMatrix3D.java
index 831d9ad635..082d7fa8ca 100644
--- a/core/src/processing/core/PMatrix3D.java
+++ b/core/src/processing/core/PMatrix3D.java
@@ -809,52 +809,18 @@ protected boolean invApply(float n00, float n01, float n02, float n03,
//////////////////////////////////////////////////////////////
+ @Override
public void print() {
- /*
- System.out.println(m00 + " " + m01 + " " + m02 + " " + m03 + "\n" +
- m10 + " " + m11 + " " + m12 + " " + m13 + "\n" +
- m20 + " " + m21 + " " + m22 + " " + m23 + "\n" +
- m30 + " " + m31 + " " + m32 + " " + m33 + "\n");
- */
- int big = (int) Math.abs(max(max(max(max(abs(m00), abs(m01)),
- max(abs(m02), abs(m03))),
- max(max(abs(m10), abs(m11)),
- max(abs(m12), abs(m13)))),
- max(max(max(abs(m20), abs(m21)),
- max(abs(m22), abs(m23))),
- max(max(abs(m30), abs(m31)),
- max(abs(m32), abs(m33))))));
-
- int digits = 1;
- if (Float.isNaN(big) || Float.isInfinite(big)) { // avoid infinite loop
- digits = 5;
- } else {
- while ((big /= 10) != 0) digits++; // cheap log()
- }
-
- System.out.println(PApplet.nfs(m00, digits, 4) + " " +
- PApplet.nfs(m01, digits, 4) + " " +
- PApplet.nfs(m02, digits, 4) + " " +
- PApplet.nfs(m03, digits, 4));
-
- System.out.println(PApplet.nfs(m10, digits, 4) + " " +
- PApplet.nfs(m11, digits, 4) + " " +
- PApplet.nfs(m12, digits, 4) + " " +
- PApplet.nfs(m13, digits, 4));
-
- System.out.println(PApplet.nfs(m20, digits, 4) + " " +
- PApplet.nfs(m21, digits, 4) + " " +
- PApplet.nfs(m22, digits, 4) + " " +
- PApplet.nfs(m23, digits, 4));
-
- System.out.println(PApplet.nfs(m30, digits, 4) + " " +
- PApplet.nfs(m31, digits, 4) + " " +
- PApplet.nfs(m32, digits, 4) + " " +
- PApplet.nfs(m33, digits, 4));
-
- System.out.println();
+ System.out.print(toString());
}
+ @Override
+ public String toString() {
+ return PApplet.nfs(m00, 1, 4) + " " + PApplet.nfs(m01, 1, 4) + " " + PApplet.nfs(m02, 1, 4) + " " + PApplet.nfs(m03, 1, 4) + "\n" +
+ PApplet.nfs(m10, 1, 4) + " " + PApplet.nfs(m11, 1, 4) + " " + PApplet.nfs(m12, 1, 4) + " " + PApplet.nfs(m13, 1, 4) + "\n" +
+ PApplet.nfs(m20, 1, 4) + " " + PApplet.nfs(m21, 1, 4) + " " + PApplet.nfs(m22, 1, 4) + " " + PApplet.nfs(m23, 1, 4) + "\n" +
+ PApplet.nfs(m30, 1, 4) + " " + PApplet.nfs(m31, 1, 4) + " " + PApplet.nfs(m32, 1, 4) + " " + PApplet.nfs(m33, 1, 4) + "\n";
+ }
//////////////////////////////////////////////////////////////
diff --git a/core/src/processing/core/PShapeSVG.java b/core/src/processing/core/PShapeSVG.java
index f8aa3400fb..7e00b9a05b 100644
--- a/core/src/processing/core/PShapeSVG.java
+++ b/core/src/processing/core/PShapeSVG.java
@@ -961,14 +961,47 @@ else if (lexState == LexState.EXP_HEAD) {
float rx = PApplet.parseFloat(pathTokens[i + 1]);
float ry = PApplet.parseFloat(pathTokens[i + 2]);
float angle = PApplet.parseFloat(pathTokens[i + 3]);
- boolean fa = PApplet.parseFloat(pathTokens[i + 4]) != 0;
- boolean fs = PApplet.parseFloat(pathTokens[i + 5]) != 0;
- float endX = PApplet.parseFloat(pathTokens[i + 6]);
- float endY = PApplet.parseFloat(pathTokens[i + 7]);
+ // In compact arc notation, flags and coordinates may be concatenated.
+ // e.g. "013" is parsed as large-arc=0, sweep=1, x=3
+ String token4 = pathTokens[i + 4];
+ boolean fa;
+ boolean fs;
+ float endX;
+ float endY;
+ int tokenOffset = 0;
+ if (isCompactArcNotation(token4)) {
+ fa = token4.charAt(0) == '1';
+ fs = token4.charAt(1) == '1';
+ // Case: flags and x-coordinate are concatenated (e.g. "01100")
+ // token4 contains flags + x, so y is at i+5.
+ // We consume 2 fewer tokens than standard (8-2=6).
+ if (token4.length() > 2) {
+ endX = PApplet.parseFloat(token4.substring(2));
+ endY = PApplet.parseFloat(pathTokens[i + 5]);
+ tokenOffset = -2;
+ } else {
+ // Case: flags are concatenated but separated from x (e.g. "01 100")
+ // token4 is flags, x is at i+5, y is at i+6.
+ // We consume 1 fewer token than standard (8-1=7).
+ endX = PApplet.parseFloat(pathTokens[i + 5]);
+ endY = PApplet.parseFloat(pathTokens[i + 6]);
+ tokenOffset = -1;
+ }
+ } else {
+ // Standard notation: flags and coordinates are separate tokens.
+ // The 'A' command takes 7 arguments:
+ // rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, x, y
+ // Here, we've already parsed rx (i+1), ry (i+2), and angle (i+3).
+ // token4 (i+4) is the large-arc-flag.
+ fa = PApplet.parseFloat(token4) != 0;
+ fs = PApplet.parseFloat(pathTokens[i + 5]) != 0; // sweep-flag
+ endX = PApplet.parseFloat(pathTokens[i + 6]); // x
+ endY = PApplet.parseFloat(pathTokens[i + 7]); // y
+ }
parsePathArcto(cx, cy, rx, ry, angle, fa, fs, endX, endY);
cx = endX;
cy = endY;
- i += 8;
+ i += 8 + tokenOffset;
prevCurve = true;
}
break;
@@ -978,14 +1011,41 @@ else if (lexState == LexState.EXP_HEAD) {
float rx = PApplet.parseFloat(pathTokens[i + 1]);
float ry = PApplet.parseFloat(pathTokens[i + 2]);
float angle = PApplet.parseFloat(pathTokens[i + 3]);
- boolean fa = PApplet.parseFloat(pathTokens[i + 4]) != 0;
- boolean fs = PApplet.parseFloat(pathTokens[i + 5]) != 0;
- float endX = cx + PApplet.parseFloat(pathTokens[i + 6]);
- float endY = cy + PApplet.parseFloat(pathTokens[i + 7]);
+ String token4 = pathTokens[i + 4];
+ boolean fa;
+ boolean fs;
+ float endX;
+ float endY;
+ int tokenOffset = 0;
+ if (isCompactArcNotation(token4)) {
+ fa = token4.charAt(0) == '1';
+ fs = token4.charAt(1) == '1';
+ // Case: flags and x-coordinate are concatenated
+ if (token4.length() > 2) {
+ endX = cx + PApplet.parseFloat(token4.substring(2));
+ endY = cy + PApplet.parseFloat(pathTokens[i + 5]);
+ tokenOffset = -2;
+ } else {
+ // Case: flags are concatenated but separated from x
+ endX = cx + PApplet.parseFloat(pathTokens[i + 5]);
+ endY = cy + PApplet.parseFloat(pathTokens[i + 6]);
+ tokenOffset = -1;
+ }
+ } else {
+ // Standard notation: flags and coordinates are separate tokens.
+ // The 'a' command takes 7 arguments:
+ // rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, x, y
+ // Here, we've already parsed rx (i+1), ry (i+2), and angle (i+3).
+ // token4 (i+4) is the large-arc-flag.
+ fa = PApplet.parseFloat(token4) != 0;
+ fs = PApplet.parseFloat(pathTokens[i + 5]) != 0; // sweep-flag
+ endX = cx + PApplet.parseFloat(pathTokens[i + 6]); // x
+ endY = cy + PApplet.parseFloat(pathTokens[i + 7]); // y
+ }
parsePathArcto(cx, cy, rx, ry, angle, fa, fs, endX, endY);
cx = endX;
cy = endY;
- i += 8;
+ i += 8 + tokenOffset;
prevCurve = true;
}
break;
@@ -1054,6 +1114,33 @@ private void parsePathMoveto(float px, float py) {
}
+ /**
+ * Checks if a token represents compact arc notation where flags and coordinates
+ * are concatenated (e.g., "013" for large-arc=0, sweep=1, x=3).
+ *
+ * @param token the token to check
+ * @return true if the token is in compact arc notation format
+ */
+ private boolean isCompactArcNotation(String token) {
+ if (token == null) {
+ return false;
+ }
+ return token.length() > 1 &&
+ // First two characters must be '0' or '1' (flags)
+ (token.charAt(0) == '0' || token.charAt(0) == '1') &&
+ (token.charAt(1) == '0' || token.charAt(1) == '1') &&
+ // Either it's just the flags (length 2),
+ (token.length() == 2 ||
+ // Or the flags are followed by the start of a number coordinate
+ // (digit, sign, or decimal point)
+ (token.length() > 2 && (
+ Character.isDigit(token.charAt(2)) ||
+ token.charAt(2) == '+' ||
+ token.charAt(2) == '-' ||
+ token.charAt(2) == '.')));
+ }
+
+
private void parsePathLineto(float px, float py) {
parsePathCode(VERTEX);
parsePathVertex(px, py);
diff --git a/core/src/processing/opengl/PSurfaceJOGL.java b/core/src/processing/opengl/PSurfaceJOGL.java
index 064f9a8dd0..27ebd41d01 100644
--- a/core/src/processing/opengl/PSurfaceJOGL.java
+++ b/core/src/processing/opengl/PSurfaceJOGL.java
@@ -24,61 +24,42 @@
package processing.opengl;
-import java.awt.Component;
-import java.awt.EventQueue;
-import java.awt.FileDialog;
-import java.awt.GraphicsDevice;
-import java.awt.GraphicsEnvironment;
-import java.awt.Point;
-import java.awt.Rectangle;
-import java.awt.image.BufferedImage;
-import java.awt.image.DataBufferInt;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URL;
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Map;
-
-import javax.swing.ImageIcon;
-
import com.jogamp.common.util.IOUtil;
import com.jogamp.common.util.IOUtil.ClassResources;
+import com.jogamp.nativewindow.MutableGraphicsConfiguration;
import com.jogamp.nativewindow.NativeSurface;
import com.jogamp.nativewindow.ScalableSurface;
+import com.jogamp.nativewindow.WindowClosingProtocol;
import com.jogamp.nativewindow.util.Dimension;
import com.jogamp.nativewindow.util.PixelFormat;
import com.jogamp.nativewindow.util.PixelRectangle;
-import com.jogamp.opengl.GLAutoDrawable;
-import com.jogamp.opengl.GLCapabilities;
-import com.jogamp.opengl.GLEventListener;
-import com.jogamp.opengl.GLException;
-import com.jogamp.opengl.GLProfile;
-import com.jogamp.opengl.GLDrawableFactory;
-import com.jogamp.nativewindow.MutableGraphicsConfiguration;
-import com.jogamp.nativewindow.WindowClosingProtocol;
import com.jogamp.newt.Display;
import com.jogamp.newt.Display.PointerIcon;
import com.jogamp.newt.NewtFactory;
import com.jogamp.newt.Screen;
import com.jogamp.newt.awt.NewtCanvasAWT;
import com.jogamp.newt.opengl.GLWindow;
+import com.jogamp.opengl.*;
import com.jogamp.opengl.util.FPSAnimator;
-
-import processing.core.PApplet;
-import processing.core.PConstants;
-import processing.core.PGraphics;
-import processing.core.PImage;
-import processing.core.PSurface;
+import processing.awt.PImageAWT;
+import processing.awt.ShimAWT;
+import processing.core.*;
import processing.event.KeyEvent;
import processing.event.MouseEvent;
-import processing.awt.PImageAWT;
-// have this removed by 4.0 final
-import processing.awt.ShimAWT;
+import javax.swing.*;
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.awt.image.DataBufferInt;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
public class PSurfaceJOGL implements PSurface {
@@ -489,7 +470,7 @@ protected void initIcons() {
sketch.getClass().getClassLoader(),
sketch.getClass());
}
- NewtFactory.setWindowIcons(res);
+ if (PApplet.platform == PConstants.WINDOWS) NewtFactory.setWindowIcons(res);
}
diff --git a/core/test/processing/core/PImageTest.java b/core/test/processing/core/PImageTest.java
new file mode 100644
index 0000000000..ea5116e85f
--- /dev/null
+++ b/core/test/processing/core/PImageTest.java
@@ -0,0 +1,317 @@
+package processing.core;
+
+import static org.junit.Assert.*;
+import org.junit.Before;
+import org.junit.Test;
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+
+public class PImageTest {
+
+ private PImage img;
+ private PApplet applet;
+
+ @Before
+ public void setUp() {
+ applet = new PApplet();
+ img = new PImage(10, 10, PConstants.ARGB);
+ for (int i = 0; i < img.pixels.length; i++) {
+ img.pixels[i] = 0xFF000000 | (i % 255) << 16 | ((i * 3) % 255) << 8 | ((i * 7) % 255);
+ }
+ img.updatePixels();
+ }
+
+ @Test
+ public void testConstructors() {
+ PImage img1 = new PImage();
+ assertEquals(PConstants.ARGB, img1.format);
+
+ PImage img2 = new PImage(20, 30);
+ assertEquals(20, img2.width);
+ assertEquals(30, img2.height);
+ assertEquals(PConstants.RGB, img2.format);
+
+ PImage img3 = new PImage(20, 30, PConstants.ALPHA);
+ assertEquals(PConstants.ALPHA, img3.format);
+
+ PImage img4 = new PImage(20, 30, PConstants.RGB, 2);
+ assertEquals(2, img4.pixelDensity);
+ assertEquals(40, img4.pixelWidth);
+ assertEquals(60, img4.pixelHeight);
+
+ PImage zeroImg = new PImage(0, 0);
+ assertEquals(0, zeroImg.width);
+ assertEquals(0, zeroImg.height);
+ assertEquals(0, zeroImg.pixels.length);
+ }
+
+ @Test
+ public void testPixelManipulation() {
+ img.loadPixels();
+ img.pixels[0] = 0xFFFF0000;
+ img.updatePixels();
+ assertEquals(0xFFFF0000, img.get(0, 0));
+
+ assertEquals(0xFFFF0000, img.get(0, 0));
+ assertEquals(0, img.get(-1, -1));
+ assertEquals(0, img.get(100, 100));
+
+ img.set(1, 1, 0xFF00FF00);
+ assertEquals(0xFF00FF00, img.get(1, 1));
+
+ img.set(-1, -1, 0xFFFFFFFF);
+ img.set(100, 100, 0xFFFFFFFF);
+
+ PImage region = img.get(0, 0, 2, 2);
+ assertEquals(2, region.width);
+ assertEquals(2, region.height);
+ assertEquals(0xFFFF0000, region.get(0, 0));
+ assertEquals(0xFF00FF00, region.get(1, 1));
+
+ PImage copy = img.get();
+ assertEquals(img.width, copy.width);
+ assertEquals(img.height, copy.height);
+ assertEquals(0xFFFF0000, copy.get(0, 0));
+ assertEquals(0xFF00FF00, copy.get(1, 1));
+
+ PImage negCopy = img.get(-5, -5, 20, 20);
+ assertEquals(20, negCopy.width);
+ assertEquals(20, negCopy.height);
+ }
+
+ @Test
+ public void testCopyAndResize() {
+ PImage copy = img.copy();
+ assertEquals(img.width, copy.width);
+ assertEquals(img.height, copy.height);
+ assertEquals(img.get(0, 0), copy.get(0, 0));
+
+ PImage resized = img.copy();
+ resized.resize(20, 0);
+ assertEquals(20, resized.width);
+ assertTrue(resized.height > 0);
+
+ PImage resized2 = img.copy();
+ resized2.resize(20, 15);
+ assertEquals(20, resized2.width);
+ assertEquals(15, resized2.height);
+
+ img.set(0, 0, 0xFFFF0000);
+ img.set(1, 0, 0xFF00FF00);
+ img.set(0, 1, 0xFF0000FF);
+ img.set(1, 1, 0xFFFFFF00);
+
+ PImage dest = new PImage(4, 4, PConstants.ARGB);
+ dest.copy(img, 0, 0, 2, 2, 0, 0, 4, 4);
+
+ int topLeft = dest.get(0, 0);
+ int topRight = dest.get(3, 0);
+ int bottomLeft = dest.get(0, 3);
+ int bottomRight = dest.get(3, 3);
+
+ assertTrue((topLeft & 0x00FF0000) > 0);
+ assertTrue((topRight & 0x0000FF00) > 0);
+ assertTrue((bottomLeft & 0x000000FF) > 0);
+ assertTrue((bottomRight & 0x00FFFF00) > 0);
+
+ PImage smallImg = new PImage(5, 5, PConstants.ARGB);
+ smallImg.copy(img, 0, 0, 10, 10, 0, 0, 5, 5);
+ img.copy(smallImg, 0, 0, 5, 5, 0, 0, 10, 10);
+ }
+
+ @Test
+ public void testMask() {
+ PImage mask = new PImage(10, 10, PConstants.ALPHA);
+ for (int i = 0; i < mask.pixels.length; i++) {
+ mask.pixels[i] = (i * 255) / mask.pixels.length;
+ }
+ mask.updatePixels();
+
+ PImage original = img.copy();
+ img.mask(mask);
+
+ assertTrue((img.get(0, 0) >>> 24) < 10);
+
+ assertTrue((img.get(9, 9) >>> 24) > 240);
+
+ img = original.copy();
+ img.mask(mask.pixels);
+
+ assertTrue((img.get(0, 0) >>> 24) < 10);
+
+ assertTrue((img.get(9, 9) >>> 24) > 240);
+
+ PImage smallMask = new PImage(5, 5);
+ try {
+ img.mask(smallMask.pixels);
+ fail("Should throw IllegalArgumentException for wrong size mask");
+ } catch (IllegalArgumentException e) {
+ }
+ }
+
+ @Test
+ public void testFilter() {
+ for (int i = 0; i < img.pixels.length; i++) {
+ img.pixels[i] = 0xFF808080;
+ }
+ img.updatePixels();
+
+ PImage thresholdImg = img.copy();
+ thresholdImg.filter(PConstants.THRESHOLD, 0.7f);
+ int thresholdColor = thresholdImg.get(0, 0);
+ assertTrue((thresholdColor & 0x00FFFFFF) < 0x00808080);
+
+ thresholdImg = img.copy();
+ thresholdImg.filter(PConstants.THRESHOLD, 0.3f);
+ thresholdColor = thresholdImg.get(0, 0);
+ assertTrue((thresholdColor & 0x00FFFFFF) > 0x00808080);
+
+ PImage grayImg = img.copy();
+ grayImg.filter(PConstants.GRAY);
+ int grayColor = grayImg.get(0, 0);
+ int r = (grayColor >> 16) & 0xFF;
+ int g = (grayColor >> 8) & 0xFF;
+ int b = grayColor & 0xFF;
+ assertEquals(r, g, 5);
+ assertEquals(g, b, 5);
+
+ PImage invertImg = img.copy();
+ invertImg.filter(PConstants.INVERT);
+ int originalColor = img.get(0, 0) & 0x00FFFFFF;
+ int invertedColor = invertImg.get(0, 0) & 0x00FFFFFF;
+ assertTrue(originalColor + invertedColor > 0x00FFFFFF - 10 &&
+ originalColor + invertedColor < 0x00FFFFFF + 10);
+
+ PImage posterizeImg = img.copy();
+ posterizeImg.filter(PConstants.POSTERIZE, 2);
+
+ PImage blurImg = img.copy();
+ blurImg.filter(PConstants.BLUR, 1.0f);
+
+ img.pixels[0] = 0x80808080;
+ img.updatePixels();
+ PImage opaqueImg = img.copy();
+ opaqueImg.filter(PConstants.OPAQUE);
+ assertTrue((opaqueImg.get(0, 0) >>> 24) > (img.get(0, 0) >>> 24));
+
+ PImage img2 = new PImage(10, 10, PConstants.RGB);
+ for (int y = 0; y < img2.height; y++) {
+ for (int x = 0; x < img2.width; x++) {
+ img2.pixels[y * img2.width + x] = (x == 5 || y == 5) ? 0xFFFFFFFF : 0xFF000000;
+ }
+ }
+ img2.updatePixels();
+
+ PImage erodeImg = img2.copy();
+ erodeImg.filter(PConstants.ERODE);
+
+ PImage dilateImg = img2.copy();
+ dilateImg.filter(PConstants.DILATE);
+
+ int blackPixelsInOriginal = 0;
+ int blackPixelsInDilated = 0;
+ for (int i = 0; i < img2.pixels.length; i++) {
+ if ((img2.pixels[i] & 0x00FFFFFF) == 0) blackPixelsInOriginal++;
+ if ((dilateImg.pixels[i] & 0x00FFFFFF) == 0) blackPixelsInDilated++;
+ }
+ assertTrue(blackPixelsInDilated < blackPixelsInOriginal);
+ }
+
+ @Test
+ public void testAllBlendModesExactMatchStaticHelper() {
+ final int W = 10, H = 10;
+ final int red = 0x80FF0000;
+ final int blue = 0x400000FF;
+
+ PImage img1 = new PImage(W, H, PConstants.ARGB);
+ PImage img2 = new PImage(W, H, PConstants.ARGB);
+ Arrays.fill(img1.pixels, red);
+ Arrays.fill(img2.pixels, blue);
+ img1.updatePixels();
+ img2.updatePixels();
+
+ int[] modes = {
+ PConstants.BLEND, PConstants.ADD, PConstants.SUBTRACT, PConstants.LIGHTEST,
+ PConstants.DARKEST, PConstants.DIFFERENCE, PConstants.EXCLUSION,
+ PConstants.MULTIPLY, PConstants.SCREEN, PConstants.REPLACE
+ };
+
+ for (int mode : modes) {
+ PImage out = img1.copy();
+ out.blend(img2, 0,0,W,H, 0,0,W,H, mode);
+ out.loadPixels();
+
+ int[] expected = new int[W*H];
+ for (int i = 0; i < expected.length; i++) {
+ expected[i] = (mode == PConstants.REPLACE)
+ ? img2.pixels[i]
+ : PImage.blendColor(img1.pixels[i], img2.pixels[i], mode);
+ }
+
+ for (int i = 0; i < expected.length; i++) {
+ assertEquals(
+ String.format("Mode %d failed at pixel %d: got 0x%08X, expected 0x%08X",
+ mode, i, out.pixels[i], expected[i]),
+ expected[i], out.pixels[i]
+ );
+ }
+ }
+ }
+
+
+ @Test
+ public void testSaveAndLoad_pngRoundTrip() throws IOException {
+ PImage out = new PImage(10, 10, PConstants.ARGB);
+ for (int y = 0; y < out.height; y++) {
+ for (int x = 0; x < out.width; x++) {
+ out.pixels[y*out.width + x] =
+ ((x + y) % 2 == 0)
+ ? 0xFFFFFFFF
+ : 0xFF000000;
+ }
+ }
+ out.updatePixels();
+ out.parent = applet;
+
+ File f = File.createTempFile("test", ".png");
+ f.deleteOnExit();
+ assertTrue(out.save(f.getAbsolutePath()));
+
+ PImage in = applet.loadImage(f.getAbsolutePath());
+ assertNotNull(in);
+ assertEquals(out.width, in.width);
+ assertEquals(out.height, in.height);
+
+ in.loadPixels();
+ for (int i = 0; i < out.pixels.length; i++) {
+ assertEquals(
+ String.format(
+ "Pixel %d mismatch: saved=0x%08X loaded=0x%08X",
+ i, out.pixels[i], in.pixels[i]
+ ),
+ out.pixels[i],
+ in.pixels[i]
+ );
+ }
+ }
+
+
+ @Test
+ public void testCheckAlpha() {
+ PImage opaqueImg = new PImage(5, 5, PConstants.RGB);
+ for (int i = 0; i < opaqueImg.pixels.length; i++) {
+ opaqueImg.pixels[i] = 0xFFFFFFFF;
+ }
+ opaqueImg.checkAlpha();
+ assertEquals(PConstants.RGB, opaqueImg.format);
+
+ PImage transImg = new PImage(5, 5, PConstants.RGB);
+ for (int i = 0; i < transImg.pixels.length; i++) {
+ transImg.pixels[i] = 0x80FFFFFF;
+ }
+ transImg.checkAlpha();
+ assertEquals(PConstants.ARGB, transImg.format);
+ }
+
+}
\ No newline at end of file
diff --git a/core/test/processing/core/PMatrix3DTest.java b/core/test/processing/core/PMatrix3DTest.java
new file mode 100644
index 0000000000..7cb03a63af
--- /dev/null
+++ b/core/test/processing/core/PMatrix3DTest.java
@@ -0,0 +1,226 @@
+package processing.core;
+
+import static org.junit.Assert.*;
+import org.junit.Test;
+
+public class PMatrix3DTest {
+
+ @Test
+ public void testConstructorsResetAndGet() {
+ PMatrix3D m = new PMatrix3D();
+ float[] vals = m.get(null);
+ assertEquals(1, vals[0], 1e-6);
+ assertEquals(1, vals[5], 1e-6);
+ assertEquals(1, vals[10], 1e-6);
+ assertEquals(1, vals[15], 1e-6);
+
+ m.m00 = 2;
+ m.reset();
+ vals = m.get(null);
+ assertEquals(1, vals[0], 1e-6);
+ }
+
+ @Test
+ public void testSetAndGetMethods() {
+ PMatrix3D m = new PMatrix3D();
+ float[] source = {
+ 2, 3, 4, 5,
+ 6, 7, 8, 9,
+ 10,11,12,13,
+ 14,15,16,17
+ };
+ m.set(source);
+ float[] target = m.get(null);
+ for (int i = 0; i < 16; i++) {
+ assertEquals(source[i], target[i], 1e-6);
+ }
+
+ m.set(1, 2, 3, 4, 5, 6);
+ target = m.get(null);
+ assertEquals(1, target[0], 1e-6);
+ assertEquals(2, target[1], 1e-6);
+ assertEquals(0, target[2], 1e-6);
+ assertEquals(3, target[3], 1e-6);
+
+ PMatrix2D m2d = new PMatrix2D(1, 2, 3, 4, 5, 6);
+ m.set(m2d);
+ target = m.get(null);
+ assertEquals(1, target[0], 1e-6);
+ assertEquals(2, target[1], 1e-6);
+ assertEquals(0, target[2], 1e-6);
+ assertEquals(3, target[3], 1e-6);
+ }
+
+ @Test
+ public void testTranslate() {
+ PMatrix3D m = new PMatrix3D();
+ m.translate(0, 0, 0);
+ float[] original = new PMatrix3D().get(null);
+ float[] result = m.get(null);
+ for (int i = 0; i < 16; i++) {
+ assertEquals(original[i], result[i], 1e-6);
+ }
+
+ m.reset();
+ m.translate(10, -5, 3);
+ result = m.get(null);
+ assertEquals(10, result[3], 1e-6);
+ assertEquals(-5, result[7], 1e-6);
+ assertEquals(3, result[11], 1e-6);
+ assertEquals(1, result[15], 1e-6);
+ }
+
+ @Test
+ public void testRotateAndShear() {
+ PMatrix3D m = new PMatrix3D();
+ m.reset();
+ m.rotate(0);
+ float[] original = new PMatrix3D().get(null);
+ float[] result = m.get(null);
+ for (int i = 0; i < 16; i++) {
+ assertEquals(original[i], result[i], 1e-6);
+ }
+
+ m.reset();
+ m.rotateZ((float) Math.PI / 2);
+ result = m.get(null);
+ assertEquals(0, result[0], 1e-6);
+ assertEquals(-1, result[1], 1e-6);
+ assertEquals(1, result[4], 1e-6);
+ assertEquals(0, result[5], 1e-6);
+
+ m.reset();
+ m.shearX((float) Math.PI / 4);
+ result = m.get(null);
+ float expectedT = (float) Math.tan(Math.PI / 4);
+ assertEquals(expectedT, result[1], 1e-6);
+
+ m.reset();
+ m.shearY((float) Math.PI / 4);
+ result = m.get(null);
+ assertEquals(expectedT, result[4], 1e-6);
+ }
+
+ @Test
+ public void testScale() {
+ PMatrix3D m = new PMatrix3D();
+ m.reset();
+ m.scale(1);
+ float[] original = new PMatrix3D().get(null);
+ float[] result = m.get(null);
+ for (int i = 0; i < 16; i++) {
+ assertEquals(original[i], result[i], 1e-6);
+ }
+
+ m.reset();
+ m.scale(2, 3, 4);
+ result = m.get(null);
+ assertEquals(2, result[0], 1e-6);
+ assertEquals(3, result[5], 1e-6);
+ assertEquals(4, result[10], 1e-6);
+
+ m.reset();
+ m.scale(0, 1, 1);
+ result = m.get(null);
+ assertEquals(0, result[0], 1e-6);
+ }
+
+ @Test
+ public void testApplyAndPreApply() {
+ PMatrix3D m = new PMatrix3D();
+ m.reset();
+ PMatrix3D n = new PMatrix3D(
+ 2, 3, 4, 5,
+ 6, 7, 8, 9,
+ 10,11,12,13,
+ 14,15,16,17
+ );
+ m.apply(n);
+ float[] result = m.get(null);
+ float[] expected = n.get(null);
+ for (int i = 0; i < 16; i++) {
+ assertEquals(expected[i], result[i], 1e-6);
+ }
+
+ m.reset();
+ m.preApply(n);
+ result = m.get(null);
+ for (int i = 0; i < 16; i++) {
+ assertEquals(expected[i], result[i], 1e-6);
+ }
+ }
+
+ @Test
+ public void testMultMethods() {
+ PMatrix3D m = new PMatrix3D();
+ m.reset();
+ PVector p = new PVector(1, 2, 3);
+ PVector resultP = m.mult(p, null);
+ assertEquals(1, resultP.x, 1e-6);
+ assertEquals(2, resultP.y, 1e-6);
+ assertEquals(3, resultP.z, 1e-6);
+
+ float[] vec3 = {1, 2, 3};
+ float[] out3 = m.mult(vec3, new float[3]);
+ assertEquals(1, out3[0], 1e-6);
+ assertEquals(2, out3[1], 1e-6);
+ assertEquals(3, out3[2], 1e-6);
+
+ float[] vec4 = {1, 2, 3, 4};
+ float[] out4 = m.mult(vec4, new float[4]);
+ for (int i = 0; i < 4; i++) {
+ assertEquals(vec4[i], out4[i], 1e-6);
+ }
+
+ try {
+ m.mult(vec3, vec3);
+ fail("Expected RuntimeException for identical source and target arrays.");
+ } catch (RuntimeException e) {
+ // Exception expected.
+ }
+
+ m.reset();
+ m.translate(10, 20, 30);
+ float x = m.multX(1, 2, 3);
+ float y = m.multY(1, 2, 3);
+ float z = m.multZ(1, 2, 3);
+ float w = m.multW(1, 2, 3);
+ assertEquals(1 + 10, x, 1e-6);
+ assertEquals(2 + 20, y, 1e-6);
+ assertEquals(3 + 30, z, 1e-6);
+ assertEquals(1, w, 1e-6);
+ }
+
+ @Test
+ public void testTransposeAndDeterminant() {
+ PMatrix3D m = new PMatrix3D(
+ 2, 3, 4, 5,
+ 6, 7, 8, 9,
+ 10,11,12,13,
+ 14,15,16,17
+ );
+ float det = m.determinant();
+ m.transpose();
+ float det2 = m.determinant();
+ assertEquals(det, det2, 1e-6);
+ }
+
+ @Test
+ public void testInvert() {
+ PMatrix3D m = new PMatrix3D();
+ m.reset();
+ boolean inverted = m.invert();
+ assertTrue(inverted);
+ float[] result = m.get(null);
+ PMatrix3D identity = new PMatrix3D();
+ float[] idArr = identity.get(null);
+ for (int i = 0; i < 16; i++) {
+ assertEquals(idArr[i], result[i], 1e-6);
+ }
+
+ m.reset();
+ m.scale(0, 1, 1);
+ inverted = m.invert();
+ assertFalse(inverted);
+ }
+}
diff --git a/core/test/processing/core/PShapeSVGPathTest.java b/core/test/processing/core/PShapeSVGPathTest.java
new file mode 100644
index 0000000000..298fb085de
--- /dev/null
+++ b/core/test/processing/core/PShapeSVGPathTest.java
@@ -0,0 +1,110 @@
+package processing.core;
+
+import org.junit.Assert;
+import org.junit.Test;
+import processing.data.XML;
+
+public class PShapeSVGPathTest {
+
+ @Test
+ public void testCompactPathNotation() {
+ String svgContent = "";
+
+ try {
+ XML xml = XML.parse(svgContent);
+ PShapeSVG shape = new PShapeSVG(xml);
+ Assert.assertNotNull(shape);
+ Assert.assertTrue(shape.getChildCount() > 0);
+
+ PShape path = shape.getChild(0);
+ Assert.assertNotNull(path);
+ Assert.assertTrue(path.getVertexCount() > 5);
+ } catch (Exception e) {
+ Assert.fail("Encountered exception " + e);
+ }
+ }
+
+ @Test
+ public void testWorkingPathNotation() {
+ // Test the working SVG (with explicit decimal points)
+ String svgContent = "";
+
+ try {
+ XML xml = XML.parse(svgContent);
+ PShapeSVG shape = new PShapeSVG(xml);
+ Assert.assertNotNull(shape);
+ } catch (Exception e) {
+ Assert.fail("Encountered exception " + e);
+ }
+ }
+
+ @Test
+ public void testCompactArcNotationVariations() {
+ String svgContent1 = "";
+
+ try {
+ XML xml = XML.parse(svgContent1);
+ PShapeSVG shape = new PShapeSVG(xml);
+ PShape path = shape.getChild(0);
+ int vertexCount = path.getVertexCount();
+ PVector lastVertex = path.getVertex(vertexCount - 1);
+ Assert.assertEquals(3.0f, lastVertex.x, 0.0001f);
+ Assert.assertEquals(50.0f, lastVertex.y, 0.0001f);
+ } catch (Exception e) {
+ Assert.fail("Encountered exception " + e);
+ }
+
+ String svgContent2 = "";
+
+ try {
+ XML xml = XML.parse(svgContent2);
+ PShapeSVG shape = new PShapeSVG(xml);
+ PShape path = shape.getChild(0);
+ int vertexCount = path.getVertexCount();
+ PVector lastVertex = path.getVertex(vertexCount - 1);
+ Assert.assertEquals(10.0f, lastVertex.x, 0.0001f);
+ Assert.assertEquals(50.0f, lastVertex.y, 0.0001f);
+ } catch (Exception e) {
+ Assert.fail("Encountered exception " + e);
+ }
+
+ String svgContent3 = "";
+
+ try {
+ XML xml = XML.parse(svgContent3);
+ PShapeSVG shape = new PShapeSVG(xml);
+ PShape path = shape.getChild(0);
+ int vertexCount = path.getVertexCount();
+ PVector lastVertex = path.getVertex(vertexCount - 1);
+ Assert.assertEquals(10.0f, lastVertex.x, 0.0001f);
+ Assert.assertEquals(50.0f, lastVertex.y, 0.0001f);
+ } catch (Exception e) {
+ Assert.fail("Encountered exception " + e);
+ }
+ }
+
+ @Test
+ public void testCompactArcWithNegativeCoordinates() {
+ String svgContent = "";
+
+ try {
+ XML xml = XML.parse(svgContent);
+ PShapeSVG shape = new PShapeSVG(xml);
+ PShape path = shape.getChild(0);
+ int vertexCount = path.getVertexCount();
+ PVector lastVertex = path.getVertex(vertexCount - 1);
+ Assert.assertEquals(40.0f, lastVertex.x, 0.0001f);
+ Assert.assertEquals(70.0f, lastVertex.y, 0.0001f);
+ } catch (Exception e) {
+ Assert.fail("Encountered exception " + e);
+ }
+ }
+}
diff --git a/core/test/processing/data/IntListTest.java b/core/test/processing/data/IntListTest.java
new file mode 100644
index 0000000000..3c422d84e4
--- /dev/null
+++ b/core/test/processing/data/IntListTest.java
@@ -0,0 +1,492 @@
+package processing.data;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.junit.Assert.*;
+
+/**
+ * IntList.java has two fields:
+ * 1. count - the number of elements currently stored in the list with an initial value of 0.
+ * 2. int[] data - An array to store elements with an initial capacity of 10.
+ */
+public class IntListTest {
+ @Test
+ public void testDefaultConstructor() {
+ IntList testedList = new IntList();
+ assertEquals(0, testedList.size());
+ assertEquals(10, testedList.data.length);
+ }
+
+ @Test
+ public void testConstructorWithLength() {
+ IntList testedList = new IntList(20);
+ assertEquals(0, testedList.size());
+ assertEquals(20, testedList.data.length);
+ }
+
+ @Test
+ public void testConstructorWithArray() {
+ int[] source = {1, 2};
+ IntList testedList = new IntList(source);
+ assertEquals(2, testedList.size());
+ assertEquals(2, testedList.data.length);
+
+ assertEquals(1, testedList.get(0));
+ assertEquals(2, testedList.get(1));
+ }
+
+ @Test
+ public void testConstructorWithIterableObject() {
+ List