From eddeda4346fd544e3b3ee30ea82c789fe6125ad9 Mon Sep 17 00:00:00 2001 From: Stef Tervelde Date: Thu, 27 Nov 2025 10:12:17 +0100 Subject: [PATCH 01/42] Set Java toolchain to version 17 in library plugin Configured the ProcessingLibraryPlugin to use Java toolchain version 17 via JavaPluginExtension. Also marked the 'createLibrary' task in dxf library build script as deprecated, recommending use of the 'bundleLibrary' task from the plugin. Add custom Gradle plugin for Processing libraries Introduces a new Gradle plugin module for Processing libraries, including plugin implementation, extension, and configuration classes. Updates the DXF library to use the new plugin and configuration DSL, and adjusts project settings to include and manage the plugin build. Add bundleLibrary task and update library plugin Introduces BundleLibraryFilesTask to handle bundling of Processing library files, replacing the removed CollectLibraryFilesTask. Updates ProcessingLibraryPlugin to register the new bundleLibrary task, which collects the jar, runtime dependencies, examples, javadoc, and generates library.properties. Also adds a 'name' property to ProcessingLibraryConfiguration and comments out the old createLibrary Copy task in dxf's build script. Refactor library bundling logic into task class Moved the logic for bundling Processing library files from the plugin registration into the BundleLibraryFilesTask class. The task now takes a ProcessingLibraryConfiguration and handles copying jars, dependencies, examples, javadocs, and generating library.properties internally. Also made ProcessingLibraryConfiguration serializable for safer Gradle usage. Update library version handling and add zip task Changed ProcessingLibraryConfiguration.version from String to Int for stricter versioning. Added a zipLibrary Gradle task to package the library folder as .zip and .pdex archives. Updated dxf library build script to use new version format and incremented version to 1. Add installLibrary task and update DXF build config Introduces an installLibrary Gradle task to automate Processing library installation using user preferences. Also re-enables the createLibrary copy task in the DXF library build script and removes minRevision/maxRevision constraints from the library configuration. Only run when actually running the task -.- Refactor library author metadata handling Changed the authors field in ProcessingLibraryConfiguration from a list of names to a map of author names to URLs, updating related code to format authors as markdown links. Updated the DXF library build configuration to use the new authors map structure. Added documentation comments to ProcessingLibraryConfiguration properties for clarity. --- gradle/plugins/library/build.gradle.kts | 28 ++++ .../src/main/kotlin/BundleLibraryFilesTask.kt | 77 +++++++++++ .../main/kotlin/ProcessingLibraryExtension.kt | 64 +++++++++ .../main/kotlin/ProcessingLibraryPlugin.kt | 125 ++++++++++++++++++ gradle/plugins/settings.gradle.kts | 5 + java/libraries/dxf/build.gradle.kts | 38 ++++-- settings.gradle.kts | 5 + 7 files changed, 330 insertions(+), 12 deletions(-) create mode 100644 gradle/plugins/library/build.gradle.kts create mode 100644 gradle/plugins/library/src/main/kotlin/BundleLibraryFilesTask.kt create mode 100644 gradle/plugins/library/src/main/kotlin/ProcessingLibraryExtension.kt create mode 100644 gradle/plugins/library/src/main/kotlin/ProcessingLibraryPlugin.kt create mode 100644 gradle/plugins/settings.gradle.kts diff --git a/gradle/plugins/library/build.gradle.kts b/gradle/plugins/library/build.gradle.kts new file mode 100644 index 0000000000..d2707eef4a --- /dev/null +++ b/gradle/plugins/library/build.gradle.kts @@ -0,0 +1,28 @@ +plugins { + `java-gradle-plugin` + kotlin("jvm") version "2.2.20" +} + +gradlePlugin { + plugins { + create("processing.library") { + id = "org.processing.library" + implementationClass = "ProcessingLibraryPlugin" + } + } +} + +repositories { + mavenCentral() +} + +dependencies { + testImplementation(kotlin("test")) +} + +tasks.test { + useJUnitPlatform() +} +kotlin { + jvmToolchain(17) +} \ No newline at end of file diff --git a/gradle/plugins/library/src/main/kotlin/BundleLibraryFilesTask.kt b/gradle/plugins/library/src/main/kotlin/BundleLibraryFilesTask.kt new file mode 100644 index 0000000000..1d5ab57eed --- /dev/null +++ b/gradle/plugins/library/src/main/kotlin/BundleLibraryFilesTask.kt @@ -0,0 +1,77 @@ +import org.gradle.api.DefaultTask +import org.gradle.api.GradleException +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.OutputDirectory +import org.gradle.api.tasks.TaskAction +import org.gradle.api.tasks.bundling.Jar +import org.gradle.api.tasks.javadoc.Javadoc + +abstract class BundleLibraryFilesTask : DefaultTask() { + @Input + var configuration: ProcessingLibraryConfiguration? = null + + @OutputDirectory + val outputDir = project.objects.directoryProperty() + + init { + outputDir.convention(project.layout.buildDirectory.dir("library")) + } + + @TaskAction + fun bundle() { + val configuration = configuration + ?: throw GradleException("Processing library configuration must be provided.") + val libraryName = configuration.name ?: project.name + + val buildDir = project.layout.buildDirectory.dir("library/$libraryName").get().asFile + buildDir.mkdirs() + + val libDir = buildDir.resolve("library") + libDir.mkdirs() + + // Copy the jar file + val jarFile = project.tasks.named("jar", Jar::class.java).get().archiveFile.get().asFile + jarFile.copyTo(libDir.resolve("$libraryName.jar"), overwrite = true) + + // Copy all runtime dependencies + val runtimeClasspath = project.configurations.getByName("runtimeClasspath") + runtimeClasspath.resolvedConfiguration.resolvedArtifacts.forEach { artifact -> + val depFile = artifact.file + depFile.copyTo(libDir.resolve(depFile.name), overwrite = true) + } + + // Copy Examples folder + val examplesDir = project.projectDir.resolve("examples") + if (!examplesDir.exists() || !examplesDir.isDirectory) { + throw GradleException("Examples folder not found in project directory.") + } + examplesDir.copyRecursively(buildDir.resolve("examples"), overwrite = true) + + // Copy javadoc to reference folder + val docsDir = project.tasks.named("javadoc", Javadoc::class.java).get().destinationDir + docsDir?.copyRecursively(buildDir.resolve("reference"), overwrite = true) + + // Create library.properties file + val propertiesFile = buildDir.resolve("library.properties") + propertiesFile.bufferedWriter().use { writer -> + val properties = mapOf( + "name" to libraryName, + "version" to (configuration.version ?: "1.0.0"), + "prettyVersion" to (configuration.prettyVersion ?: configuration.version ?: "1.0.0"), + "authors" to (configuration.authors.entries.joinToString(", ") { "[${it.key}](${it.value})" }), + "url" to configuration.url, + "category" to configuration.categories.joinToString(", "), + "sentence" to configuration.sentence, + "paragraph" to configuration.paragraph, + "minRevision" to configuration.minRevision, + "maxRevision" to configuration.maxRevision + ) + properties + .filter { it.value != null && it.value.toString().isNotEmpty() } + .forEach { (key, value) -> + writer.write("$key=$value\n") + } + } + propertiesFile.copyTo(buildDir.resolve("../$libraryName.txt"), overwrite = true) + } +} \ No newline at end of file diff --git a/gradle/plugins/library/src/main/kotlin/ProcessingLibraryExtension.kt b/gradle/plugins/library/src/main/kotlin/ProcessingLibraryExtension.kt new file mode 100644 index 0000000000..f03b4cb1d1 --- /dev/null +++ b/gradle/plugins/library/src/main/kotlin/ProcessingLibraryExtension.kt @@ -0,0 +1,64 @@ +import org.gradle.api.Action +import org.gradle.api.model.ObjectFactory +import java.io.Serializable +import javax.inject.Inject + +open class ProcessingLibraryExtension @Inject constructor(objects: ObjectFactory) { + var version: String? = null + val library = objects.newInstance(ProcessingLibraryConfiguration::class.java) + fun library(action: Action) { + action.execute(library) + } +} + +open class ProcessingLibraryConfiguration @Inject constructor() : Serializable { + /** + * Name of the library. If not set, the project name will be used. + */ + var name: String? = null + + /** + * Version number of the library. + */ + var version: Int? = null + + /** + * Pretty version string of the library. + */ + var prettyVersion: String? = null + + /** + * Map of author URLs to author names. + */ + var authors: Map = emptyMap() + + /** + * URL of the library where more information can be found. + */ + var url: String? = null + + /** + * List of categories the library belongs to. + */ + var categories: List = emptyList() + + /** + * A one-line sentence describing the library. + */ + var sentence: String? = null + + /** + * A longer paragraph describing the library. + */ + var paragraph: String? = null + + /** + * Minimum Processing revision required. + */ + var minRevision: Int? = null + + /** + * Maximum Processing revision supported. + */ + var maxRevision: Int? = null +} \ No newline at end of file diff --git a/gradle/plugins/library/src/main/kotlin/ProcessingLibraryPlugin.kt b/gradle/plugins/library/src/main/kotlin/ProcessingLibraryPlugin.kt new file mode 100644 index 0000000000..4514f581fd --- /dev/null +++ b/gradle/plugins/library/src/main/kotlin/ProcessingLibraryPlugin.kt @@ -0,0 +1,125 @@ +import org.gradle.api.GradleException +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.plugins.JavaPlugin +import org.gradle.api.plugins.JavaPluginExtension +import org.gradle.api.tasks.bundling.Jar +import org.gradle.api.tasks.bundling.Zip +import org.gradle.api.tasks.javadoc.Javadoc +import org.gradle.jvm.toolchain.JavaLanguageVersion +import java.util.prefs.Preferences + +class ProcessingLibraryPlugin : Plugin { + + override fun apply(target: Project) { + val extension = target.extensions.create("processing", ProcessingLibraryExtension::class.java) + target.plugins.apply(JavaPlugin::class.java) + + target.repositories.mavenCentral() + target.repositories.maven { it.setUrl("https://jogamp.org/deployment/maven/") } + + // Grab processing core if available, otherwise use the published version + val hasCore = try { + val core = target.project(":core") + target.dependencies.add("compileOnly", core) + true + } catch (_: Exception) { + false + } + + target.afterEvaluate { + if (!hasCore) { + if (extension.version == null) { + throw GradleException("Processing library version must be specified, please set processing.version in your build.gradle.kts") + } + val processingVersion = extension.version + target.dependencies.add("compileOnly", "org.processing:core:$processingVersion") + } + } + target.extensions.configure(JavaPluginExtension::class.java) { extension -> + extension.toolchain.languageVersion.set(JavaLanguageVersion.of(17)) + } + + target.plugins.withType(JavaPlugin::class.java) { + val jarTask = target.tasks.named("jar", Jar::class.java) + val javaDocTask = target.tasks.named("javadoc", Javadoc::class.java) + + val bundleTask = target.tasks.register("bundleLibrary", BundleLibraryFilesTask::class.java) { task -> + task.configuration = extension.library + task.group = "processing" + task.description = "Creates the Processing library folder with jar, library.properties, and examples." + task.dependsOn(jarTask, javaDocTask) + } + + val zipTask = target.tasks.register("zipLibrary", Zip::class.java) { task -> + task.apply { + val libraryName = extension.library.name ?: target.name + val sourceDir = bundleTask.get().outputDir.get().asFile + + group = "processing" + description = "Creates a zip & pdex archive of the Processing library folder." + dependsOn(bundleTask) + include("${libraryName}/**") + + archiveFileName.set("$libraryName.zip") + from(sourceDir) + destinationDirectory.set(sourceDir) + doLast { + val zip = task.outputs.files.files.first() + zip.copyTo(sourceDir.resolve("$libraryName.pdex"), overwrite = true) + } + } + } + + target.tasks.register("installLibrary") { task -> + task.apply { + group = "processing" + dependsOn(zipTask) + doLast { + val preferences = Preferences.userRoot().node("org/processing/app") + + val semverRe = Regex("""^(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:-([0-9A-Za-z.-]+))?""") + fun semverKey(v: String): Triple { + val m = semverRe.find(v) + val maj = m?.groupValues?.getOrNull(1)?.toLongOrNull() ?: 0L + val min = m?.groupValues?.getOrNull(2)?.toLongOrNull() ?: 0L + val pat = m?.groupValues?.getOrNull(3)?.toLongOrNull() ?: 0L + val pre = m?.groupValues?.getOrNull(4) + val packed = (maj shl 40) or (min shl 20) or pat + return Triple(packed, pre == null, pre ?: "") + } + + val installLocations = preferences.get("installLocations", "") + .split(",") + .filter { it.isNotEmpty() } + .mapNotNull { + val parts = it.split("^") + if (parts.size < 2) null else parts[1] to parts[0] // version to path + } + .sortedWith(Comparator { a, b -> + val ka = semverKey(a.first) + val kb = semverKey(b.first) + when { + ka.first != kb.first -> kb.first.compareTo(ka.first) + ka.second != kb.second -> kb.second.compareTo(ka.second) + else -> kb.third.compareTo(ka.third) + } + }) + + val installPath = installLocations.firstOrNull()?.second + ?: throw GradleException("Could not find Processing install location in preferences.") + + val libraryName = extension.library.name ?: target.name + val sourceDir = bundleTask.get().outputDir.get().asFile.resolve("$libraryName.pdex") + + ProcessBuilder() + .command(installPath, sourceDir.absolutePath) + .inheritIO() + .start() + } + } + } + + } + } +} \ No newline at end of file diff --git a/gradle/plugins/settings.gradle.kts b/gradle/plugins/settings.gradle.kts new file mode 100644 index 0000000000..ab39f6aca7 --- /dev/null +++ b/gradle/plugins/settings.gradle.kts @@ -0,0 +1,5 @@ +plugins { + id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" +} + +include("library") \ No newline at end of file diff --git a/java/libraries/dxf/build.gradle.kts b/java/libraries/dxf/build.gradle.kts index 93c114f41d..8947a3684e 100644 --- a/java/libraries/dxf/build.gradle.kts +++ b/java/libraries/dxf/build.gradle.kts @@ -1,5 +1,23 @@ plugins{ - java + id("org.processing.library") +} + +processing { + library { + version = 1 + prettyVersion = "1.0.0" + + authors = mapOf( + "The Processing Foundation" to "https://processing.org" + ) + url = "https://processing.org/" + categories = listOf("file", "exporter", "dxf") + + sentence = "DXF export library for Processing" + paragraph = + "This library allows you to export your Processing drawings as DXF files, which can be opened in CAD applications." + + } } sourceSets { @@ -9,27 +27,23 @@ sourceSets { } } } -repositories{ - mavenCentral() - maven("https://jogamp.org/deployment/maven/") -} - dependencies{ - compileOnly(project(":core")) - implementation("com.lowagie:itext:2.1.7") } -tasks.register("createLibrary"){ +/** + * @deprecated Legacy task, use 'bundleLibrary' task provided by 'org.processing.library' plugin + */ +tasks.register("createLibrary") { dependsOn("jar") into(layout.buildDirectory.dir("library")) - from(layout.projectDirectory){ - include ("library.properties") + from(layout.projectDirectory) { + include("library.properties") include("examples/**") } - from(configurations.runtimeClasspath){ + from(configurations.runtimeClasspath) { into("library") } diff --git a/settings.gradle.kts b/settings.gradle.kts index 7eacb06877..6f21e89838 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,4 +1,9 @@ rootProject.name = "processing" + +pluginManagement { + includeBuild("gradle/plugins") +} + include( "core", "core:examples", From dd3dce2817c82c98a3c5e7995d29cf24580a3a15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20de=20Courville?= Date: Fri, 30 Jan 2026 11:04:45 +0100 Subject: [PATCH 02/42] Update README to welcome new contributors (#1394) Removed the announcement for Processing 4.4 and added a welcome message for new contributors. Added the Discord link. --- README.md | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) 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! From e03e31b218b1fa7a7ffe6e3e8e63c1044bac921d Mon Sep 17 00:00:00 2001 From: Sumama Sonia Date: Fri, 30 Jan 2026 15:05:18 +0500 Subject: [PATCH 03/42] Added print() method to PMatrix (Fixes #811) (#1355) * Add print method to PMatrix interface Added a default method to print matrix data. * docs: add print() method declaration to PMatrix interface * refactor: implement toString and override print in PMatrix2D * refactor: implement toString and override print in PMatrix3D --- core/src/processing/core/PMatrix.java | 4 ++ core/src/processing/core/PMatrix2D.java | 25 ++++-------- core/src/processing/core/PMatrix3D.java | 52 +++++-------------------- 3 files changed, 20 insertions(+), 61 deletions(-) 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"; + } ////////////////////////////////////////////////////////////// From 27d235fb3a536b7782ca60f0cb6a2e971a99664a Mon Sep 17 00:00:00 2001 From: Moon Date: Fri, 30 Jan 2026 05:59:49 -0500 Subject: [PATCH 04/42] fix syntax error in all-contributors (#1395) --- .all-contributorsrc | 1 + 1 file changed, 1 insertion(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index e9e4764d85..d6ddd475d3 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1688,6 +1688,7 @@ "doc" ] }, + { "login": "avinxshKD", "name": "Avinash Kumar Deepak", "avatar_url": "https://avatars.githubusercontent.com/u/152387616?v=4", From 00cb62381b7d9b39f2af74aee4916044d67d98b0 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 30 Jan 2026 12:02:14 +0100 Subject: [PATCH 05/42] docs: add Sumamasonia as a contributor for code (#1396) * docs: update CONTRIBUTORS.md [skip ci] * docs: update .all-contributorsrc [skip ci] --------- Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> --- .all-contributorsrc | 9 +++++++++ CONTRIBUTORS.md | 1 + 2 files changed, 10 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index d6ddd475d3..142a5571c6 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1696,6 +1696,15 @@ "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" + ] } ], "repoType": "github", diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 8328a40c38..fa161a40d3 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -251,6 +251,7 @@ _Note: due to GitHub's limitations, this repository's [Contributors](https://git Madhav Majumdar
Madhav Majumdar

💻 Dino_Ww
Dino_Ww

📖 Avinash Kumar Deepak
Avinash Kumar Deepak

💻 + Sumama Sonia
Sumama Sonia

💻 From c81cda767143597ca80ec37e16a77ffeb098131f Mon Sep 17 00:00:00 2001 From: Stef Tervelde Date: Mon, 2 Feb 2026 06:57:28 +0100 Subject: [PATCH 06/42] Only set Newt window icons on Windows (#1398) Wrap the NewtFactory.setWindowIcons(res) call in a platform check so icons are only set when PApplet.platform == PConstants.WINDOWS. This avoids invoking the icon-setting behavior on non-Windows platforms. Also reorganize and consolidate import statements (add missing AWT/Swing, JOGL and Processing imports) to fix compilation and include required classes. --- core/src/processing/opengl/PSurfaceJOGL.java | 59 +++++++------------- 1 file changed, 20 insertions(+), 39 deletions(-) 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); } From 4be3f17a53e9c13aea06d6963a56adf9b3c5056c Mon Sep 17 00:00:00 2001 From: stefterv Date: Tue, 12 Aug 2025 18:02:07 +0200 Subject: [PATCH 07/42] Flatpak configuration file --- app/linux/org.processing.pde.yml | 44 ++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 app/linux/org.processing.pde.yml diff --git a/app/linux/org.processing.pde.yml b/app/linux/org.processing.pde.yml new file mode 100644 index 0000000000..b76bac3be5 --- /dev/null +++ b/app/linux/org.processing.pde.yml @@ -0,0 +1,44 @@ +id: org.processing.processingide +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: + - install -d /app/share/processing + - cp -r app/* /app/share/processing + - install -D processing.sh /app/bin/processing + - install -D src/build/linux/desktop.template /app/share/applications/org.processing.processingide.desktop + - install -D src/build/linux/processing.svg /app/share/icons/hicolor/scalable/apps/org.processing.processingide.svg + - install -D app/lib/app/resources/lib/icons/app-16.png /app/share/icons/hicolor/16x16/apps/org.processing.processingide.png +# # TODO: Add mimetype icons and app icon + sources: + - type: git + url: https://github.com/processing/processing4.git + tag: processing-1304-4.4.4 + dest: src + - type: archive + url: https://github.com/processing/processing4/releases/download/processing-1304-4.4.4/processing-4.4.4-linux-x64-portable.zip + sha256: 8f5141d09c06bc68a3c58ec3bd0e707003a347ea96e742f83f1868b9c460bb6f + only-arches: + - x86_64 + dest: app + - type: archive + url: https://github.com/processing/processing4/releases/download/processing-1304-4.4.4/processing-4.4.4-linux-aarch64-portable.zip + only-arches: + - aarch64 + dest: app + - type: script + dest-filename: "processing.sh" + commands: [ + "exec /app/share/processing/bin/Processing" + ] \ No newline at end of file From 46cd04c393b83b73207d027a3fd35bbdb76583e7 Mon Sep 17 00:00:00 2001 From: Stef Tervelde Date: Wed, 13 Aug 2025 11:28:24 +0200 Subject: [PATCH 08/42] file-associations --- app/build.gradle.kts | 8 ++-- app/linux/org.processing.pde.yml | 31 +++++++++++--- build/linux/processing-pde.xml | 69 ++++++++++++++++---------------- 3 files changed, 65 insertions(+), 43 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 48d49eea20..7a48177375 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -88,10 +88,10 @@ compose.desktop { // 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") + fileAssociation("pde", "Processing Source Code", "application/x-processing",rootProject.file("build/shared/lib/icons/pde-1024.png")) + fileAssociation("pyde", "Processing Python Source Code", "application/x-processing",rootProject.file("build/shared/lib/icons/pde-1024.png")) + fileAssociation("pdez", "Processing Sketch Bundle", "application/x-processing",rootProject.file("build/shared/lib/icons/pde-1024.png")) + fileAssociation("pdex", "Processing Contribution Bundle", "application/x-processing", rootProject.file("build/shared/lib/icons/pde-1024.png")) } } } diff --git a/app/linux/org.processing.pde.yml b/app/linux/org.processing.pde.yml index b76bac3be5..cc151c3fa3 100644 --- a/app/linux/org.processing.pde.yml +++ b/app/linux/org.processing.pde.yml @@ -1,4 +1,4 @@ -id: org.processing.processingide +id: org.processing.pde runtime: org.freedesktop.Platform runtime-version: '24.08' sdk: org.freedesktop.Sdk @@ -10,6 +10,10 @@ finish-args: - --filesystem=home - --socket=pulseaudio - --share=network + +env: + PROCESSING_ID: "org.processing.pde" + modules: - name: "processing" buildsystem: "simple" @@ -17,10 +21,27 @@ modules: - install -d /app/share/processing - cp -r app/* /app/share/processing - install -D processing.sh /app/bin/processing - - install -D src/build/linux/desktop.template /app/share/applications/org.processing.processingide.desktop - - install -D src/build/linux/processing.svg /app/share/icons/hicolor/scalable/apps/org.processing.processingide.svg - - install -D app/lib/app/resources/lib/icons/app-16.png /app/share/icons/hicolor/16x16/apps/org.processing.processingide.png -# # TODO: Add mimetype icons and app icon + + # Install the desktop file and icon + - install -D src/build/linux/desktop.template /app/share/applications/${PROCESSING_ID}.desktop + - sed -i 's/^Icon=.*/Icon=${PROCESSING_ID}/' /app/share/applications/${PROCESSING_ID}.desktop + + # Install icons in various sizes + - install -D src/build/linux/processing.svg /app/share/icons/hicolor/scalable/apps/${PROCESSING_ID}.svg + - install -D app/lib/app/resources/lib/icons/app-16.png /app/share/icons/hicolor/16x16/apps/${PROCESSING_ID}.png + - install -D app/lib/app/resources/lib/icons/app-32.png /app/share/icons/hicolor/32x32/apps/${PROCESSING_ID}.png + - install -D app/lib/app/resources/lib/icons/app-48.png /app/share/icons/hicolor/48x48/apps/${PROCESSING_ID}.png + - install -D app/lib/app/resources/lib/icons/app-64.png /app/share/icons/hicolor/64x64/apps/${PROCESSING_ID}.png + - install -D app/lib/app/resources/lib/icons/app-128.png /app/share/icons/hicolor/128x128/apps/${PROCESSING_ID}.png + - install -D app/lib/app/resources/lib/icons/app-256.png /app/share/icons/hicolor/256x256/apps/${PROCESSING_ID}.png + - install -D app/lib/app/resources/lib/icons/app-512.png /app/share/icons/hicolor/512x512/apps/${PROCESSING_ID}.png + - install -D app/lib/app/resources/lib/icons/pde-16.png /app/share/icons/hicolor/16x16/mimetypes/${PROCESSING_ID}-text-x-processing.png + - install -D app/lib/app/resources/lib/icons/pde-32.png /app/share/icons/hicolor/32x32/mimetypes/${PROCESSING_ID}-text-x-processing.png + - install -D app/lib/app/resources/lib/icons/pde-48.png /app/share/icons/hicolor/48x48/mimetypes/${PROCESSING_ID}-text-x-processing.png + - install -D app/lib/app/resources/lib/icons/pde-64.png /app/share/icons/hicolor/64x64/mimetypes/${PROCESSING_ID}-text-x-processing.png + - install -D app/lib/app/resources/lib/icons/pde-128.png /app/share/icons/hicolor/128x128/mimetypes/${PROCESSING_ID}-text-x-processing.png + - install -D app/lib/app/resources/lib/icons/pde-256.png /app/share/icons/hicolor/256x256/mimetypes/${PROCESSING_ID}-text-x-processing.png + - install -D app/lib/app/resources/lib/icons/pde-512.png /app/share/icons/hicolor/512x512/mimetypes/${PROCESSING_ID}-text-x-processing.png sources: - type: git url: https://github.com/processing/processing4.git 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 源代碼 + From d796996f33a92aaa14cab5bb1546bede1faf8a07 Mon Sep 17 00:00:00 2001 From: Stef Tervelde Date: Wed, 13 Aug 2025 12:40:59 +0200 Subject: [PATCH 09/42] Fixing file associations --- app/build.gradle.kts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 7a48177375..24437a2a61 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -82,16 +82,17 @@ 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",rootProject.file("build/shared/lib/icons/pde-1024.png")) - fileAssociation("pyde", "Processing Python Source Code", "application/x-processing",rootProject.file("build/shared/lib/icons/pde-1024.png")) - fileAssociation("pdez", "Processing Sketch Bundle", "application/x-processing",rootProject.file("build/shared/lib/icons/pde-1024.png")) - fileAssociation("pdex", "Processing Contribution Bundle", "application/x-processing", rootProject.file("build/shared/lib/icons/pde-1024.png")) + fileAssociation("application/x-processing","pde", "Processing Source Code",rootProject.file("build/shared/lib/icons/pde-1024.png")) + fileAssociation("application/x-processing","pyde", "Processing Python Source Code",rootProject.file("build/shared/lib/icons/pde-1024.png")) + fileAssociation("application/x-processing","pdez", "Processing Sketch Bundle",rootProject.file("build/shared/lib/icons/pde-1024.png")) + fileAssociation("application/x-processing","pdex", "Processing Contribution Bundle", rootProject.file("build/shared/lib/icons/pde-1024.png")) } } } From 144e8b4f3a250c82b0fc01f4cde52a4f5a77136b Mon Sep 17 00:00:00 2001 From: stefterv Date: Wed, 13 Aug 2025 13:12:08 +0200 Subject: [PATCH 10/42] Flatpak configuration file --- app/linux/org.processing.pde.yml | 68 ++++++++++---------------------- 1 file changed, 21 insertions(+), 47 deletions(-) diff --git a/app/linux/org.processing.pde.yml b/app/linux/org.processing.pde.yml index cc151c3fa3..2d14a06695 100644 --- a/app/linux/org.processing.pde.yml +++ b/app/linux/org.processing.pde.yml @@ -2,7 +2,7 @@ id: org.processing.pde runtime: org.freedesktop.Platform runtime-version: '24.08' sdk: org.freedesktop.Sdk -command: processing +command: Processing finish-args: - --socket=x11 - --share=ipc @@ -11,55 +11,29 @@ finish-args: - --socket=pulseaudio - --share=network -env: - PROCESSING_ID: "org.processing.pde" - modules: - - name: "processing" - buildsystem: "simple" + - name: Processing + buildsystem: simple build-commands: - - install -d /app/share/processing - - cp -r app/* /app/share/processing - - install -D processing.sh /app/bin/processing + # 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 src/build/linux/desktop.template /app/share/applications/${PROCESSING_ID}.desktop - - sed -i 's/^Icon=.*/Icon=${PROCESSING_ID}/' /app/share/applications/${PROCESSING_ID}.desktop + - install -D /app/lib/processing-Processing.desktop /app/share/applications/org.processing.pde.desktop + - sed -i 's/^Icon=.*/Icon=org.processing.pde/' /app/share/applications/org.processing.pde.desktop + - sed -i 's/^Exec=.*/Exec=\/app\/bin\/Processing/' /app/share/applications/org.processing.pde.desktop + + # Install the mimetype info + - install -D /app/lib/processing-Processing-MimeInfo.xml /app/share/mime/packages/org.processing.pde.xml + +# - install -D /app/lib/Processing.png /app/share/icons/hicolor/512x512/apps/org.processing.pde.png +# - install -D /app/lib/application-x-processing.png /app/share/icons/hicolor/512x512/mimetypes/org.processing.pde-text-x-processing.png - # Install icons in various sizes - - install -D src/build/linux/processing.svg /app/share/icons/hicolor/scalable/apps/${PROCESSING_ID}.svg - - install -D app/lib/app/resources/lib/icons/app-16.png /app/share/icons/hicolor/16x16/apps/${PROCESSING_ID}.png - - install -D app/lib/app/resources/lib/icons/app-32.png /app/share/icons/hicolor/32x32/apps/${PROCESSING_ID}.png - - install -D app/lib/app/resources/lib/icons/app-48.png /app/share/icons/hicolor/48x48/apps/${PROCESSING_ID}.png - - install -D app/lib/app/resources/lib/icons/app-64.png /app/share/icons/hicolor/64x64/apps/${PROCESSING_ID}.png - - install -D app/lib/app/resources/lib/icons/app-128.png /app/share/icons/hicolor/128x128/apps/${PROCESSING_ID}.png - - install -D app/lib/app/resources/lib/icons/app-256.png /app/share/icons/hicolor/256x256/apps/${PROCESSING_ID}.png - - install -D app/lib/app/resources/lib/icons/app-512.png /app/share/icons/hicolor/512x512/apps/${PROCESSING_ID}.png - - install -D app/lib/app/resources/lib/icons/pde-16.png /app/share/icons/hicolor/16x16/mimetypes/${PROCESSING_ID}-text-x-processing.png - - install -D app/lib/app/resources/lib/icons/pde-32.png /app/share/icons/hicolor/32x32/mimetypes/${PROCESSING_ID}-text-x-processing.png - - install -D app/lib/app/resources/lib/icons/pde-48.png /app/share/icons/hicolor/48x48/mimetypes/${PROCESSING_ID}-text-x-processing.png - - install -D app/lib/app/resources/lib/icons/pde-64.png /app/share/icons/hicolor/64x64/mimetypes/${PROCESSING_ID}-text-x-processing.png - - install -D app/lib/app/resources/lib/icons/pde-128.png /app/share/icons/hicolor/128x128/mimetypes/${PROCESSING_ID}-text-x-processing.png - - install -D app/lib/app/resources/lib/icons/pde-256.png /app/share/icons/hicolor/256x256/mimetypes/${PROCESSING_ID}-text-x-processing.png - - install -D app/lib/app/resources/lib/icons/pde-512.png /app/share/icons/hicolor/512x512/mimetypes/${PROCESSING_ID}-text-x-processing.png sources: - - type: git - url: https://github.com/processing/processing4.git - tag: processing-1304-4.4.4 - dest: src - - type: archive - url: https://github.com/processing/processing4/releases/download/processing-1304-4.4.4/processing-4.4.4-linux-x64-portable.zip - sha256: 8f5141d09c06bc68a3c58ec3bd0e707003a347ea96e742f83f1868b9c460bb6f - only-arches: - - x86_64 - dest: app - - type: archive - url: https://github.com/processing/processing4/releases/download/processing-1304-4.4.4/processing-4.4.4-linux-aarch64-portable.zip - only-arches: - - aarch64 - dest: app - - type: script - dest-filename: "processing.sh" - commands: [ - "exec /app/share/processing/bin/Processing" - ] \ No newline at end of file + - type: file + path: ../build/compose/binaries/main/deb/processing_1.0.0-1_amd64.deb + dest-filename: processing.deb \ No newline at end of file From 62af87b26dc82f77644d0039839028c0ca234577 Mon Sep 17 00:00:00 2001 From: Stef Tervelde Date: Wed, 13 Aug 2025 14:29:24 +0200 Subject: [PATCH 11/42] file association fix --- app/build.gradle.kts | 8 ++++---- app/linux/org.processing.pde.yml | 3 +-- build/shared/lib/icons/app-linux.svg | 19 +++++++++++++++++++ 3 files changed, 24 insertions(+), 6 deletions(-) create mode 100644 build/shared/lib/icons/app-linux.svg diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 24437a2a61..2319e6536e 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -89,10 +89,10 @@ compose.desktop { // Fix fonts on some Linux distributions jvmArgs("-Dawt.useSystemAAFontSettings=on") - fileAssociation("application/x-processing","pde", "Processing Source Code",rootProject.file("build/shared/lib/icons/pde-1024.png")) - fileAssociation("application/x-processing","pyde", "Processing Python Source Code",rootProject.file("build/shared/lib/icons/pde-1024.png")) - fileAssociation("application/x-processing","pdez", "Processing Sketch Bundle",rootProject.file("build/shared/lib/icons/pde-1024.png")) - fileAssociation("application/x-processing","pdex", "Processing Contribution Bundle", rootProject.file("build/shared/lib/icons/pde-1024.png")) + fileAssociation("application/x-processing","pde", "Processing Source Code",rootProject.file("build/shared/lib/icons/pde-512.png")) + fileAssociation("application/x-processing","pyde", "Processing Python Source Code",rootProject.file("build/shared/lib/icons/pde-512.png")) + fileAssociation("application/x-processing","pdez", "Processing Sketch Bundle",rootProject.file("build/shared/lib/icons/pde-512.png")) + fileAssociation("application/x-processing","pdex", "Processing Contribution Bundle", rootProject.file("build/shared/lib/icons/pde-512.png")) } } } diff --git a/app/linux/org.processing.pde.yml b/app/linux/org.processing.pde.yml index 2d14a06695..587bc22236 100644 --- a/app/linux/org.processing.pde.yml +++ b/app/linux/org.processing.pde.yml @@ -21,7 +21,6 @@ modules: - 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/org.processing.pde.desktop - sed -i 's/^Icon=.*/Icon=org.processing.pde/' /app/share/applications/org.processing.pde.desktop @@ -31,7 +30,7 @@ modules: - install -D /app/lib/processing-Processing-MimeInfo.xml /app/share/mime/packages/org.processing.pde.xml # - install -D /app/lib/Processing.png /app/share/icons/hicolor/512x512/apps/org.processing.pde.png -# - install -D /app/lib/application-x-processing.png /app/share/icons/hicolor/512x512/mimetypes/org.processing.pde-text-x-processing.png + - install -D /app/lib/application-x-processing.png /app/share/icons/hicolor/512x512/mimetypes/org.processing.pde-text-x-processing.png sources: - type: file 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 @@ + + + + + + + + + + + + + + + + + + + From ff8e0ead118fab28b1cee9eeeeb8ae047104e518 Mon Sep 17 00:00:00 2001 From: Stef Tervelde Date: Wed, 13 Aug 2025 14:41:25 +0200 Subject: [PATCH 12/42] delete legacy build system --- .github/workflows/build.yml | 77 ------------------- .github/workflows/lock.yml | 29 ------- .github/workflows/pull_request.yml | 63 --------------- .github/workflows/release.yml | 118 ----------------------------- 4 files changed, 287 deletions(-) delete mode 100644 .github/workflows/build.yml delete mode 100644 .github/workflows/lock.yml delete mode 100644 .github/workflows/pull_request.yml delete mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index b1b7f9ad62..0000000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,77 +0,0 @@ -name: Branch Builds (Legacy) -on: - push: - paths-ignore: - - '**/*.md' - - '.all-contributorsrc' - -jobs: - test: - runs-on: ubuntu-latest - name: Run Tests - 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 - runs-on: ${{ matrix.os }} - permissions: - contents: 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_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 }}" - - name: Add artifact - uses: actions/upload-artifact@v4 - 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}}-* - retention-days: 1 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.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.yml b/.github/workflows/release.yml deleted file mode 100644 index bf4d33cd89..0000000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,118 +0,0 @@ -name: Releases (Legacy) -on: - release: - types: [published] - -jobs: - version: - runs-on: ubuntu-latest - outputs: - build_number: ${{ steps.tag_info.outputs.build_number }} - version: ${{ steps.tag_info.outputs.version }} - steps: - - name: Extract version and build number - id: tag_info - shell: bash - run: | - TAG_NAME="${GITHUB_REF#refs/tags/}" - BUILD_NUMBER=$(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 "version=$VERSION" >> $GITHUB_OUTPUT - publish: - name: Publish Processing Core 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 }} - build: - name: Publish Release for ${{ matrix.os_prefix }} (${{ matrix.arch }}) - runs-on: ${{ matrix.os }} - needs: version - permissions: - contents: 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_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: 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 - - name: Install Certificates for Code Signing - if: ${{ matrix.os_prefix == 'macos' }} - uses: apple-actions/import-codesign-certs@v3 - 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 }} - 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 - 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 From 699b0f327827d5366e46f1333c135c438a0c5d05 Mon Sep 17 00:00:00 2001 From: Stef Tervelde Date: Wed, 13 Aug 2025 14:42:27 +0200 Subject: [PATCH 13/42] Rename action files --- .github/workflows/{build-gradle.yml => build.yml} | 0 .github/workflows/{pull_request-gradle.yml => pr.yml} | 0 .github/workflows/{release-gradle.yml => release.yml} | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{build-gradle.yml => build.yml} (100%) rename .github/workflows/{pull_request-gradle.yml => pr.yml} (100%) rename .github/workflows/{release-gradle.yml => release.yml} (100%) diff --git a/.github/workflows/build-gradle.yml b/.github/workflows/build.yml similarity index 100% rename from .github/workflows/build-gradle.yml rename to .github/workflows/build.yml diff --git a/.github/workflows/pull_request-gradle.yml b/.github/workflows/pr.yml similarity index 100% rename from .github/workflows/pull_request-gradle.yml rename to .github/workflows/pr.yml diff --git a/.github/workflows/release-gradle.yml b/.github/workflows/release.yml similarity index 100% rename from .github/workflows/release-gradle.yml rename to .github/workflows/release.yml From f71740355216fff6c0791ec00eea8d3356a40cec Mon Sep 17 00:00:00 2001 From: Stef Tervelde Date: Wed, 13 Aug 2025 15:08:38 +0200 Subject: [PATCH 14/42] Github actions simplification --- .github/actions/setup/action.yml | 18 ++ .github/workflows/build.yml | 40 +-- .github/workflows/pr.yml | 32 +- .github/workflows/release.yml | 300 +++++++++++++----- app/build.gradle.kts | 150 ++++++--- .../{org.processing.pde.yml => flathub.yml} | 16 +- .../{snapcraft.base.yml => snapcraft.yml} | 2 +- 7 files changed, 384 insertions(+), 174 deletions(-) create mode 100644 .github/actions/setup/action.yml rename app/linux/{org.processing.pde.yml => flathub.yml} (67%) rename app/linux/{snapcraft.base.yml => snapcraft.yml} (95%) 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.yml b/.github/workflows/build.yml index 254433edd0..53944d10df 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,6 +4,12 @@ on: paths-ignore: - '**/*.md' - '.all-contributorsrc' + pull_request: + paths-ignore: + - '**/*.md' + branches: + - main + jobs: test: @@ -12,13 +18,9 @@ jobs: 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: Setup Processing + uses: ./.github/actions/setup - name: Build with Gradle run: ./gradlew test @@ -33,11 +35,11 @@ jobs: - os: ubuntu-24.04-arm os_prefix: linux arch: aarch64 - binary: processing*.snap + binary: deb/processing*.deb - os: ubuntu-latest os_prefix: linux arch: x64 - binary: processing*.snap + binary: deb/processing*.deb - os: windows-latest os_prefix: windows arch: x64 @@ -51,25 +53,13 @@ jobs: 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 + - name: Setup Processing + uses: ./.github/actions/setup + + - name: Package Processing with Gradle run: ./gradlew packageDistributionForCurrentOS - name: Add artifact diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 4ea0bcc9db..c822c46e7d 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -13,13 +13,11 @@ jobs: 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 + fetch-depth: 0 + + - name: Setup Processing + uses: ./.github/actions/setup - name: Build with Gradle run: ./gradlew test @@ -34,11 +32,11 @@ jobs: - os: ubuntu-24.04-arm os_prefix: linux arch: aarch64 - binary: processing*.snap + binary: deb/processing*.deb - os: ubuntu-latest os_prefix: linux arch: x64 - binary: processing*.snap + binary: deb/processing*.deb - os: windows-latest os_prefix: windows arch: x64 @@ -52,23 +50,11 @@ jobs: 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: Setup Processing + uses: ./.github/actions/setup - name: Build with Gradle run: ./gradlew packageDistributionForCurrentOS diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8ec45cad0b..e8de46389b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -49,20 +49,16 @@ jobs: asset_name: processing-${{ needs.version.outputs.version }}-reference.zip file: reference.zip - publish: + 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 Java - uses: actions/setup-java@v4 - with: - distribution: 'temurin' - java-version: 17 - - name: Setup Gradle - uses: gradle/actions/setup-gradle@v4 + + - name: Setup Processing + uses: ./.github/actions/setup - name: Build with Gradle run: ./gradlew publish @@ -78,8 +74,8 @@ jobs: 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 + release-windows: + name: (windows/${{ matrix.arch }}) Create Processing Release runs-on: ${{ matrix.os }} needs: version permissions: @@ -88,57 +84,16 @@ jobs: 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 + - arch: x64 + os: windows-latest +# - arch: aarch64 +# os: windows-11-arm 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: Setup Processing + uses: ./.github/actions/setup - name: Build with Gradle run: ./gradlew packageDistributionForCurrentOS @@ -146,17 +101,8 @@ jobs: 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 }} @@ -171,24 +117,230 @@ jobs: timestamp-rfc3161: http://timestamp.acs.microsoft.com timestamp-digest: SHA256 + - name: Upload portable version + uses: svenstaro/upload-release-action@v2 + with: + 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 + 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: 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 }}-${{ matrix.os_prefix }}-${{ matrix.arch }}-portable.zip + 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 }}-${{ matrix.os_prefix }}-${{ matrix.arch }}.${{ matrix.extension }} - file: app/build/compose/binaries/main/${{ matrix.binary }}.${{ matrix.extension }} + 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: + 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 - if: runner.os == 'Linux' - run: snapcraft upload --release=beta app/build/compose/binaries/main/${{ matrix.binary }}.${{ matrix.extension }} + 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/ - \ No newline at end of file + - 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 }} + asset_name: processing-${{ needs.version.outputs.version }}-linux-${{ matrix.arch }}.flatpak + file: processing.flatpak \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 2319e6536e..070a7f20d7 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -167,6 +167,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 } @@ -176,11 +182,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 @@ -235,64 +240,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) @@ -318,7 +382,7 @@ afterEvaluate{ ){ dependsOn("notarizeDmg") } - dependsOn("packageSnap", "zipDistributable") + dependsOn("zipDistributable") } } diff --git a/app/linux/org.processing.pde.yml b/app/linux/flathub.yml similarity index 67% rename from app/linux/org.processing.pde.yml rename to app/linux/flathub.yml index 587bc22236..c92ab17d9d 100644 --- a/app/linux/org.processing.pde.yml +++ b/app/linux/flathub.yml @@ -1,4 +1,4 @@ -id: org.processing.pde +id: $identifier runtime: org.freedesktop.Platform runtime-version: '24.08' sdk: org.freedesktop.Sdk @@ -22,17 +22,17 @@ modules: - 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/org.processing.pde.desktop - - sed -i 's/^Icon=.*/Icon=org.processing.pde/' /app/share/applications/org.processing.pde.desktop - - sed -i 's/^Exec=.*/Exec=\/app\/bin\/Processing/' /app/share/applications/org.processing.pde.desktop + - 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/org.processing.pde.xml + - 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/org.processing.pde.png - - install -D /app/lib/application-x-processing.png /app/share/icons/hicolor/512x512/mimetypes/org.processing.pde-text-x-processing.png +# - 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: ../build/compose/binaries/main/deb/processing_1.0.0-1_amd64.deb + 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 From 7c520c067d7b1333620d84774ef6888462be9623 Mon Sep 17 00:00:00 2001 From: Stef Tervelde Date: Wed, 27 Aug 2025 16:34:20 +0200 Subject: [PATCH 15/42] Adding File associastions on macOS and Windows through the same system as Linux --- app/build.gradle.kts | 12 ++++++--- app/macos/info.plist | 59 -------------------------------------------- 2 files changed, 8 insertions(+), 63 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 070a7f20d7..26925b881a 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") @@ -89,10 +97,6 @@ compose.desktop { // Fix fonts on some Linux distributions jvmArgs("-Dawt.useSystemAAFontSettings=on") - fileAssociation("application/x-processing","pde", "Processing Source Code",rootProject.file("build/shared/lib/icons/pde-512.png")) - fileAssociation("application/x-processing","pyde", "Processing Python Source Code",rootProject.file("build/shared/lib/icons/pde-512.png")) - fileAssociation("application/x-processing","pdez", "Processing Sketch Bundle",rootProject.file("build/shared/lib/icons/pde-512.png")) - fileAssociation("application/x-processing","pdex", "Processing Contribution Bundle", rootProject.file("build/shared/lib/icons/pde-512.png")) } } } 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 From 735bfa637f288b151ecf08b5c08195a6596c6f21 Mon Sep 17 00:00:00 2001 From: Stef Tervelde Date: Mon, 2 Feb 2026 14:39:28 +0100 Subject: [PATCH 16/42] Update install script to create 'processing' instead of 'processing-java' on macOS (#1377) * Update install script to use 'processing' instead of 'processing-java' Changed references from 'processing-java' to 'processing' in the install script and related file paths for consistency and clarity. Also updated import statements for better organization. * Fixed the language files and install message --- .../main/resources/languages/PDE.properties | 2 +- .../resources/languages/PDE_ar.properties | 2 +- .../resources/languages/PDE_ca.properties | 2 +- .../resources/languages/PDE_de.properties | 2 +- .../resources/languages/PDE_el.properties | 2 +- .../resources/languages/PDE_es.properties | 2 +- .../resources/languages/PDE_fr.properties | 2 +- .../resources/languages/PDE_it.properties | 2 +- .../resources/languages/PDE_ja.properties | 2 +- .../resources/languages/PDE_ko.properties | 2 +- .../resources/languages/PDE_nl.properties | 2 +- .../resources/languages/PDE_pt.properties | 2 +- .../resources/languages/PDE_ru.properties | 2 +- .../resources/languages/PDE_tr.properties | 2 +- .../resources/languages/PDE_uk.properties | 2 +- .../resources/languages/PDE_zh-CN.properties | 2 +- .../resources/languages/PDE_zh-TW.properties | 2 +- .../app/tools/InstallCommander.java | 64 ++++++++++--------- 18 files changed, 51 insertions(+), 47 deletions(-) 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/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 { From 5317234c983ae4c48cc27a03801aa89e0fc5ae92 Mon Sep 17 00:00:00 2001 From: jSdCool <37940266+jSdCool@users.noreply.github.com> Date: Wed, 4 Feb 2026 17:58:54 -0500 Subject: [PATCH 17/42] allow exported apps on windows to be run from anywhere on the system instead of requiring it to be in the same folder as the executable (#1314) --- java/src/processing/mode/java/JavaBuild.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/src/processing/mode/java/JavaBuild.java b/java/src/processing/mode/java/JavaBuild.java index b09f7d8dc0..b696ab0e20 100644 --- a/java/src/processing/mode/java/JavaBuild.java +++ b/java/src/processing/mode/java/JavaBuild.java @@ -997,7 +997,7 @@ protected boolean exportApplication(File destFolder, XML clazzPath = config.addChild("classPath"); clazzPath.addChild("mainClass").setContent(sketch.getMainName()); for (String jarName : jarList) { - clazzPath.addChild("cp").setContent("lib/" + jarName); + clazzPath.addChild("cp").setContent("%EXEDIR%/lib/" + jarName); } XML jre = config.addChild("jre"); if (embedJava) { From f34498acea3a1a30896fb9112dc93fd5a07c208e Mon Sep 17 00:00:00 2001 From: Stef Tervelde Date: Thu, 5 Feb 2026 00:56:16 +0100 Subject: [PATCH 18/42] Reset defaults by setting value rather than remove (#1385) Initially reset to defaults was set up by removing the key from the preferences but that didn't work well upstream, changing the value to default instead. --- app/src/processing/app/Preferences.kt | 3 +++ app/src/processing/app/ui/PDEPreferences.kt | 11 ++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/app/src/processing/app/Preferences.kt b/app/src/processing/app/Preferences.kt index 1b344a5e11..ae2f18c482 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") + } } /* 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, "") } } } From 75926c871b2c6d9ab749e30f410999de098fa3d7 Mon Sep 17 00:00:00 2001 From: Stef Tervelde Date: Thu, 5 Feb 2026 15:11:29 +0100 Subject: [PATCH 19/42] Add PR artifact uploads and comments (#1401) * Add PR artifact uploads and comments Update the build workflow to handle pull request runs: make the main artifact upload conditional (skip for pull_request), add a separate upload step for PR artifacts with longer retention, and post a sticky PR comment containing a download link. Also adjust workflow permissions to allow pull-request write access and include updated artifact paths. * Update build workflow: PR comments and artifacts Add PR-facing comments and artifact wiring to the CI workflow. Post a sticky comment after Gradle tests to indicate artifacts availability, add an upload-artifact step with an id, and update the sticky-comment step to use the artifact output URL (steps.upload-artifact.outputs.artifact-url) and a consistent header. These changes make build artifacts discoverable on pull requests. * Switch to a table * update messaging * Use `processing-bot` --- .github/workflows/build.yml | 39 ++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 53944d10df..899b44057e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -24,10 +24,26 @@ jobs: - 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 + GITHUB_TOKEN: ${{ secrets.PR_PAT }} + 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: + pull-requests: write strategy: fail-fast: false matrix: @@ -64,7 +80,28 @@ jobs: - name: Add artifact uses: actions/upload-artifact@v4 + if: ${{ github.event_name != 'pull_request' }} 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 + 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 + GITHUB_TOKEN: ${{ secrets.PR_PAT }} + 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 }})| + From 5790327f494520827d4c1512f455e402056d250a Mon Sep 17 00:00:00 2001 From: Stef Tervelde Date: Thu, 5 Feb 2026 18:11:33 +0100 Subject: [PATCH 20/42] Remove PR workflow and disable PR comments (#1413) Delete the standalone .github/workflows/pr.yml workflow and comment out the sticky PR comment steps in .github/workflows/build.yml. Artifact upload and build steps remain unchanged; only the actions that post/update pull request comments (marocchino/sticky-pull-request-comment) and associated PR_PAT usage were disabled. --- .github/workflows/build.yml | 40 +++++++++++----------- .github/workflows/pr.yml | 67 ------------------------------------- 2 files changed, 19 insertions(+), 88 deletions(-) delete mode 100644 .github/workflows/pr.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 899b44057e..d4355a69c5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -25,18 +25,17 @@ jobs: - 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 - GITHUB_TOKEN: ${{ secrets.PR_PAT }} - message: | - Tests completed successfully. Build artifacts for this pull request will appear below once ready. - - ### Build Artifacts - | Platform | Link | - |:--|------------------------| + # - 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 @@ -95,13 +94,12 @@ jobs: 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 - GITHUB_TOKEN: ${{ secrets.PR_PAT }} - 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 }})| + # - 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/pr.yml b/.github/workflows/pr.yml deleted file mode 100644 index c822c46e7d..0000000000 --- a/.github/workflows/pr.yml +++ /dev/null @@ -1,67 +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 - with: - fetch-depth: 0 - - - name: Setup Processing - uses: ./.github/actions/setup - - - 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: 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 - binary: dmg/Processing-*.dmg - steps: - - name: Checkout Repository - uses: actions/checkout@v4 - - - name: Setup Processing - uses: ./.github/actions/setup - - - 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 From 451b399ff4a5118d7bc10b10f9186bbd33136c98 Mon Sep 17 00:00:00 2001 From: Moon Date: Thu, 5 Feb 2026 13:06:07 -0500 Subject: [PATCH 21/42] Remove Welcome Survey link, and unused function (#1408) --- app/src/processing/app/ui/PDEWelcome.kt | 6 +++--- app/src/processing/app/ui/WelcomeSurvey.kt | 11 ----------- 2 files changed, 3 insertions(+), 14 deletions(-) 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 From afd1d98739d4fd45510aa0c39fb37c10aff2db5c Mon Sep 17 00:00:00 2001 From: harsh kumar <135993950+hxrshxz@users.noreply.github.com> Date: Fri, 6 Feb 2026 00:48:53 +0530 Subject: [PATCH 22/42] Fix ArrayIndexOutOfBoundsException with compact SVG arc notation (#1282) * Enhance PShapeSVG to support compact arc notation and add corresponding unit tests * add suggested changes * add comments * chore: retrigger CI --- core/src/processing/core/PShapeSVG.java | 107 +++++++++++++++-- .../processing/core/PShapeSVGPathTest.java | 110 ++++++++++++++++++ 2 files changed, 207 insertions(+), 10 deletions(-) create mode 100644 core/test/processing/core/PShapeSVGPathTest.java 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/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); + } + } +} From 81e1d562a8808be58a3d5515953378f2cdc29551 Mon Sep 17 00:00:00 2001 From: Avinash Kumar Deepak <152387616+avinxshKD@users.noreply.github.com> Date: Fri, 6 Feb 2026 05:46:21 +0530 Subject: [PATCH 23/42] Port Net and SVG libraries from Ant to Gradle with Maven publishing (#1411) --- java/libraries/net/build.gradle.kts | 85 ++++++++++++++++++++++++++++- java/libraries/svg/build.gradle.kts | 46 ++++++++++++++++ 2 files changed, 130 insertions(+), 1 deletion(-) diff --git a/java/libraries/net/build.gradle.kts b/java/libraries/net/build.gradle.kts index a176f03df7..23289e4855 100644 --- a/java/libraries/net/build.gradle.kts +++ b/java/libraries/net/build.gradle.kts @@ -1 +1,84 @@ -ant.importBuild("build.xml") \ No newline at end of file +import com.vanniktech.maven.publish.SonatypeHost + +plugins { + java + alias(libs.plugins.mavenPublish) +} + +sourceSets { + main { + java { + srcDirs("src") + } + } +} + +repositories { + mavenCentral() +} + +dependencies { + compileOnly(project(":core")) +} + +tasks.register("createLibrary") { + dependsOn("jar") + into(layout.buildDirectory.dir("library")) + + from(layout.projectDirectory) { + include("library.properties") + include("examples/**") + } + + from(configurations.runtimeClasspath) { + into("library") + } + + from(tasks.jar) { + into("library") + rename { "net.jar" } + } +} + +publishing { + repositories { + maven { + name = "App" + url = uri(project(":app").layout.buildDirectory.dir("resources-bundled/common/repository").get().asFile.absolutePath) + } + } +} + +mavenPublishing { + coordinates("$group.core", name, version.toString()) + publishToMavenCentral(SonatypeHost.CENTRAL_PORTAL, automaticRelease = true) + + signAllPublications() + + pom { + name.set("Processing Net") + description.set("Processing Net") + url.set("https://processing.org") + licenses { + license { + name.set("LGPL") + url.set("https://www.gnu.org/licenses/lgpl-2.1.html") + } + } + developers { + developer { + id.set("steftervelde") + name.set("Stef Tervelde") + } + developer { + id.set("benfry") + name.set("Ben Fry") + } + } + scm { + url.set("https://github.com/processing/processing4") + connection.set("scm:git:git://github.com/processing/processing4.git") + developerConnection.set("scm:git:ssh://git@github.com/processing/processing4.git") + } + } +} \ No newline at end of file diff --git a/java/libraries/svg/build.gradle.kts b/java/libraries/svg/build.gradle.kts index ddc4397842..6189265ba1 100644 --- a/java/libraries/svg/build.gradle.kts +++ b/java/libraries/svg/build.gradle.kts @@ -1,5 +1,8 @@ +import com.vanniktech.maven.publish.SonatypeHost + plugins { java + alias(libs.plugins.mavenPublish) } sourceSets { @@ -9,6 +12,7 @@ sourceSets { } } } + repositories { mavenCentral() } @@ -38,3 +42,45 @@ tasks.register("createLibrary") { } } +publishing { + repositories { + maven { + name = "App" + url = uri(project(":app").layout.buildDirectory.dir("resources-bundled/common/repository").get().asFile.absolutePath) + } + } +} + +mavenPublishing { + coordinates("$group.core", name, version.toString()) + publishToMavenCentral(SonatypeHost.CENTRAL_PORTAL, automaticRelease = true) + + signAllPublications() + + pom { + name.set("Processing SVG") + description.set("Processing SVG") + url.set("https://processing.org") + licenses { + license { + name.set("LGPL") + url.set("https://www.gnu.org/licenses/lgpl-2.1.html") + } + } + developers { + developer { + id.set("steftervelde") + name.set("Stef Tervelde") + } + developer { + id.set("benfry") + name.set("Ben Fry") + } + } + scm { + url.set("https://github.com/processing/processing4") + connection.set("scm:git:git://github.com/processing/processing4.git") + developerConnection.set("scm:git:ssh://git@github.com/processing/processing4.git") + } + } +} From 8b049efab63f9b242a735718ed99c40845ce83e0 Mon Sep 17 00:00:00 2001 From: Avinash Kumar Deepak <152387616+avinxshKD@users.noreply.github.com> Date: Fri, 6 Feb 2026 05:46:25 +0530 Subject: [PATCH 24/42] Publish pdf library: add coordinates and simplify signing (#1407) --- java/libraries/pdf/build.gradle.kts | 48 ++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/java/libraries/pdf/build.gradle.kts b/java/libraries/pdf/build.gradle.kts index 634fd265e9..f0e0485a17 100644 --- a/java/libraries/pdf/build.gradle.kts +++ b/java/libraries/pdf/build.gradle.kts @@ -1,5 +1,8 @@ +import com.vanniktech.maven.publish.SonatypeHost + plugins{ java + alias(libs.plugins.mavenPublish) } sourceSets { @@ -37,4 +40,47 @@ tasks.register("createLibrary"){ into("library") rename { "pdf.jar" } } -} \ No newline at end of file +} + +publishing{ + repositories{ + maven { + name = "App" + url = uri(project(":app").layout.buildDirectory.dir("resources-bundled/common/repository").get().asFile.absolutePath) + } + } +} + +mavenPublishing{ + coordinates("$group.core", name, version.toString()) + publishToMavenCentral(SonatypeHost.CENTRAL_PORTAL, automaticRelease = true) + + signAllPublications() + + pom{ + name.set("Processing PDF") + description.set("Processing PDF") + url.set("https://processing.org") + licenses { + license { + name.set("LGPL") + url.set("https://www.gnu.org/licenses/lgpl-2.1.html") + } + } + developers { + developer { + id.set("steftervelde") + name.set("Stef Tervelde") + } + developer { + id.set("benfry") + name.set("Ben Fry") + } + } + scm{ + url.set("https://github.com/processing/processing4") + connection.set("scm:git:git://github.com/processing/processing4.git") + developerConnection.set("scm:git:ssh://git@github.com/processing/processing4.git") + } + } +} From 870d1160d704ed5caf29c5f907409c5e495eb8fa Mon Sep 17 00:00:00 2001 From: Stef Tervelde Date: Fri, 6 Feb 2026 15:29:27 +0100 Subject: [PATCH 25/42] Publish `org.processing.core.dxf` to Maven Central (Publish libraries to maven central) (#1403) * publish dxf library * different naming * Naming * Remove unused itext dependency from dxf module --- build.gradle.kts | 1 + java/libraries/dxf/build.gradle.kts | 37 +++++++++++++++++++++++++++-- 2 files changed, 36 insertions(+), 2 deletions(-) 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/java/libraries/dxf/build.gradle.kts b/java/libraries/dxf/build.gradle.kts index 8947a3684e..0fb74dfd3c 100644 --- a/java/libraries/dxf/build.gradle.kts +++ b/java/libraries/dxf/build.gradle.kts @@ -1,5 +1,6 @@ plugins{ id("org.processing.library") + alias(libs.plugins.mavenPublish) } processing { @@ -27,10 +28,42 @@ sourceSets { } } } -dependencies{ - implementation("com.lowagie:itext:2.1.7") + + +mavenPublishing { + publishToMavenCentral(com.vanniktech.maven.publish.SonatypeHost.CENTRAL_PORTAL, automaticRelease = true) + signAllPublications() + coordinates("$group.core", name, version.toString()) + + pom { + name.set("Processing DXF") + description.set("Processing DFX") + url.set("https://processing.org") + licenses { + license { + name.set("LGPL") + url.set("https://www.gnu.org/licenses/lgpl-2.1.html") + } + } + developers { + developer { + id.set("steftervelde") + name.set("Stef Tervelde") + } + developer { + id.set("benfry") + name.set("Ben Fry") + } + } + scm { + url.set("https://github.com/processing/processing4") + connection.set("scm:git:git://github.com/processing/processing4.git") + developerConnection.set("scm:git:ssh://git@github.com/processing/processing4.git") + } + } } + /** * @deprecated Legacy task, use 'bundleLibrary' task provided by 'org.processing.library' plugin */ From 05bc7555574a487a85a77918dda9f2d1edd969a2 Mon Sep 17 00:00:00 2001 From: Stef Tervelde Date: Fri, 6 Feb 2026 15:29:55 +0100 Subject: [PATCH 26/42] Dir permissions (#1400) Set dir permission to fix an issue for the nix packagemanager --- app/build.gradle.kts | 1 + java/build.gradle.kts | 1 + 2 files changed, 2 insertions(+) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 26925b881a..4f91e6d98c 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -411,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/java/build.gradle.kts b/java/build.gradle.kts index 47fa76e46b..d5d306a2e0 100644 --- a/java/build.gradle.kts +++ b/java/build.gradle.kts @@ -81,6 +81,7 @@ legacyLibraries.forEach { library -> include("library/**/*") include("examples/**/*") into( javaMode("/libraries/$library")) + dirPermissions { unix("rwx------") }; } bundle.configure { dependsOn("library-$library-extraResources") From f10710944bca34daa2db90f7e43f1298030e8a02 Mon Sep 17 00:00:00 2001 From: Stef Tervelde Date: Fri, 6 Feb 2026 15:32:04 +0100 Subject: [PATCH 27/42] Processing Gradle Plugin (#1233) * Squashed commit of the following: commit 96ce6118cdfc0db0b3e47951ad7cab23751e430e Author: Stef Tervelde Date: Tue Sep 23 14:14:56 2025 +0200 Add Processing build in libraries commit 374ba2c354d656adbf559a6a8ec87dc2689c94d4 Author: Stef Tervelde Date: Tue Sep 23 13:58:22 2025 +0200 Support multiple library directories in LibrariesTask Refactored LibrariesTask to accept multiple input directories via ConfigurableFileCollection instead of a single DirectoryProperty. commit e097d687c38989f28df1377de6553d2cec67e1c5 Author: Stef Tervelde Date: Tue Sep 23 13:34:44 2025 +0200 Enhance PDE source set and preprocessing in ProcessingPlugin Added comments and structure for improved clarity. Also added a placeholder test for internal libraries in ProcessingPluginTest. commit 2b8b1511677e5633f9ada1c8dde29c54b21204bb Author: Stef Tervelde Date: Tue Sep 23 12:38:11 2025 +0200 Add stdio connection to PDE and stub test for code jars commit d5e48e4356242711217ec998aac4cceb7aa7be57 Author: Stef Tervelde Date: Tue Sep 23 12:27:17 2025 +0200 Add test for importing and using Processing libraries Introduces a helper to create temporary Processing libraries and implements a test that imports and uses a custom library in a Processing sketch. This enhances test coverage for library integration in the Processing Gradle plugin. commit 6b873ce40572c1bc11e078f26a4f92c45ff32dd6 Author: Stef Tervelde Date: Tue Sep 23 12:01:56 2025 +0200 Publishing app:utils to internal library commit b2f79f6776f16c5863eb526922fc8184f0f34c9b Author: Stef Tervelde Date: Mon Sep 22 14:21:27 2025 +0200 Include root-level Java files in source set Uncommented and updated the configuration to include all .java files at the project root in the source set. This ensures that Java files located directly in the project directory are recognized and compiled. commit 4aeff012f802958be94ff545e52859dce2fa625c Merge: 046a216be 72db9b7db Author: Stef Tervelde Date: Mon Sep 22 14:07:25 2025 +0200 Merge branch 'main' into gradle-runner commit 046a216be25ee63690be773f8d6fca1f42ddfb6f Merge: 570e9db45 198f59397 Author: Stef Tervelde Date: Mon Sep 8 18:27:48 2025 +0200 Merge branch 'main' into gradle-runner commit 570e9db45767a5cd0368c819cc46eb2280462404 Author: Stef Tervelde Date: Fri Jul 18 21:42:08 2025 +0200 Automatically download jdk commit 578c2876066d07a0df415f8581a45d60cb092984 Author: Stef Tervelde Date: Fri Jul 18 10:43:33 2025 +0200 Build on save commit fdc43dc7d284fabd405acdce859e95cd3382b32d Author: Stef Tervelde Date: Thu Jul 17 15:49:40 2025 +0200 Hot Reloading plugin commit 0c674adc54adbe1b420c844989d198764086a351 Author: Stef Tervelde Date: Thu Jul 17 12:13:56 2025 +0200 Initial Gradle Plugin configuration commit f916d272acbd5297cf4865948a0500eefae99bd4 Author: Stef Tervelde Date: Wed Jul 16 11:26:37 2025 +0200 Adding a settings panel to the editor For upcoming plugin support we need a way to enable plugins based on each sketch commit 550200cc7437e5e3538878440b929b83de02fb50 Author: Stef Tervelde Date: Wed Jul 9 09:41:40 2025 +0200 Comments and planning commit c55e2864280013045ed0a8dbe784d022f07617d6 Author: Stef Tervelde Date: Tue Jul 8 12:23:05 2025 +0200 Speed up gradle compilations commit b7123906b4b1e698a4532da560911b7e3d6e9801 Author: Stef Tervelde Date: Tue Jul 8 11:59:28 2025 +0200 Fixed rename Windres task commit a36c8a353b4bdfef31a3089a8a1d9115e228be6a Author: Stef Tervelde Date: Tue Jul 8 10:31:12 2025 +0200 Plugin tests bugfixes commit 7379166bc482f3d17c59eeafbe272e9f6d647a32 Author: Stef Tervelde Date: Tue Jul 8 10:26:16 2025 +0200 Processing Plugin tests & Refactor commit b64505d47662419e9332a41cdf7a883b60fb2fba Author: Stef Tervelde Date: Mon Jul 7 16:21:59 2025 +0200 Gradle Service refactor commit a11d2ffabec8a1d18d1541eb1efb1d4cfd2220d7 Author: Stef Tervelde Date: Mon Jul 7 15:22:21 2025 +0200 Logging sketches to separate stream & small refactor commit 5729e8811882754e51868b7d443223870a001f3f Author: Stef Tervelde Date: Mon Jul 7 11:01:58 2025 +0200 Show error logs to the user commit b709fe3a9d0d1b40db0a970b5b728ed481e859fd Merge: cebc352db d7c6aeba9 Author: Stef Tervelde Date: Fri Jul 4 07:36:32 2025 +0200 Merge branch 'gradle-runner' of https://github.com/Stefterv/processing4 into gradle-runner commit cebc352db9fc863f12e8c3798073d27a84ab6594 Author: Stef Tervelde Date: Fri Jul 4 07:36:31 2025 +0200 Snap support commit d7c6aeba90d452727cb970b39cbdcf1b635538e0 Author: Stef Tervelde Date: Fri Jul 4 06:54:50 2025 +0200 Also copy Untitled sketches commit 24cb4f4f9b1161ec10472c11b86aee9481124162 Author: Stef Tervelde Date: Fri Jul 4 06:23:00 2025 +0200 Improved Error reporting commit e727dbf59654462b015d38cf06161d6c40a5f02a Author: Stef Tervelde Date: Wed Jul 2 21:33:12 2025 +0200 Support for readonly sketches commit b165d0a511b8e7b81327744daaf0efec7ce3c3f1 Author: Stef Tervelde Date: Wed Jul 2 21:25:52 2025 +0200 Add all library jars commit 772567c54074c59485e0c19a8ba0ffddf7c4252f Author: Stef Tervelde Date: Wed Jul 2 20:34:37 2025 +0200 Build system indicators, Language keys and filtering SLF4J warnings commit efd03dba54654775cb6c481f688776774ffe7602 Author: Stef Tervelde Date: Wed Jul 2 15:58:02 2025 +0200 Fixed gradle runner issues commit 5409912c38f2740dd165f04aa025a97dc36ff0d7 Author: Stef Tervelde Date: Wed Jul 2 13:42:41 2025 +0200 Improved Gradle Error logging commit 1762b74c90cb98d6cf28e15b944c5474882780e8 Author: Stef Tervelde Date: Wed Jul 2 11:56:34 2025 +0200 Simplified Gradle Service, improved sketch logging, fixed toolbar state commit e6474e189fa47bdffcd3cb8198095427188b3cd7 Author: Stef Tervelde Date: Wed Jul 2 10:29:20 2025 +0200 Small tweaks and wrote motivation for Gradle Build system commit 67de82f18d564555796f8184f5fddafd4b8102b5 Author: Stef Tervelde Date: Wed Jul 2 09:53:14 2025 +0200 Removed error & out streams, has been piped directly into System commit bd5aba46e8103d1546b213dabf127023b0fcabb2 Author: Stef Tervelde Date: Wed Jul 2 09:50:39 2025 +0200 Allowing libraries folder to not exist, but log instead commit fcb554a50c186a7c12e604cfe56df0bad8e2f7b1 Author: Stef Tervelde Date: Wed Jul 2 09:42:19 2025 +0200 Rename gradle plugin and further planning commit 1c429618dfc903f7050fc71bd3fbad20e9899f38 Author: Stef Tervelde Date: Tue Jul 1 17:35:32 2025 +0200 Gradle Task error printing commit 4cca647724a265d01084580676d5eb7eeeace08f Author: Stef Tervelde Date: Tue Jul 1 17:02:03 2025 +0200 Updated error reporting commit 0d1fcc45a94198a8663e8edf7986a49908a8bcf7 Author: Stef Tervelde Date: Tue Jul 1 14:40:30 2025 +0200 Added documentation and todo's commit 029920924527c6ec93614dfc397db99b3a265d2a Author: Stef Tervelde Date: Tue Jul 1 12:59:34 2025 +0200 Squashed commit of the following: commit 3a1aa8e712fa7e26c6fd4f61f11c25c3d63dd1b4 Author: Stef Tervelde Date: Tue Jul 1 12:53:59 2025 +0200 Restore welcome & Cleanup commit 7d94b30f60b3b2a685734d945693ab6f2c7abe55 Author: Stef Tervelde Date: Tue Jul 1 12:20:17 2025 +0200 PR Cleanup commit 27764e95cbab9b59ff6a714fe82236416b0f4f6f Author: Stef Tervelde Date: Mon Jun 23 13:33:50 2025 +0200 Update Toolbar.kt commit 97fc4c2ec9dea5c8bd21caefa7e5d08b49529232 Author: Stef Tervelde Date: Mon Jun 23 13:30:47 2025 +0200 Removed background job differentiation commit 85566c8434f3ded9b54377104fe374d3bfd5826d Author: Stef Tervelde Date: Mon Jun 23 13:09:11 2025 +0200 CLI Compatibility commit 7f2f2b667c63e8c255d615481355cb1878dbe9b5 Merge: 1c8581e5c bf34adee0 Author: Stef Tervelde Date: Mon Jun 23 11:14:57 2025 +0200 Merge branch 'main' into runner commit 1c8581e5cd51ecde11a5915035f143a0dcf31709 Author: Stef Tervelde Date: Thu May 15 13:39:21 2025 +0200 Move Gradle connection into GradleJob commit d7420021ceb2dc368860e1954f9f21a5a238e665 Author: Stef Tervelde Date: Thu May 15 10:10:33 2025 +0200 Exclude jnilib from sources commit 2c9ee1c77aac0e872e9ae124148d331e2dd1a6e0 Author: Stef Tervelde Date: Thu May 15 09:51:08 2025 +0200 Linux Platform addition commit 49988e9b022dcb28d23a43d134301e24072a4ae8 Author: Stef Tervelde Date: Thu May 15 09:34:44 2025 +0200 Use forward slashes on Windows commit da5092ef4c788ee3dd0b00463d9cbd0b698b9e0e Author: Stef Tervelde Date: Thu May 15 09:10:11 2025 +0200 Add support for .java files & Cleanup commit ab7c216272444d16931bf980db25698045c178c4 Author: Stef Tervelde Date: Thu May 15 08:15:11 2025 +0200 Preferences in the PDE will now be read from system before looking at files commit 7775d016dc7b79fba3830a087b97f344dfe81658 Author: Stef Tervelde Date: Wed May 14 18:07:50 2025 +0200 Identified Bugs commit 80c48f2fc584057bb7ce8a340dbd9f7b87855597 Author: Stef Tervelde Date: Wed May 14 17:59:07 2025 +0200 Windows Bugfix commit 5ad43e0b49125f2313fa602ff1ef2038f891c64f Author: Stef Tervelde Date: Wed May 14 17:51:57 2025 +0200 Set a default tab size commit 2f99a69a69b80d77166f17d25449e1974f20d495 Author: Stef Tervelde Date: Wed May 14 16:14:59 2025 +0200 Bundle necessary plugins commit f9b94c57018a0c0084c851a44c78d78ec73e052f Author: Stef Tervelde Date: Wed May 14 15:17:55 2025 +0200 Only make parent dirs commit 982c1113070a944890cf9dec92d4bcde9a5fe664 Author: Stef Tervelde Date: Wed May 14 15:11:54 2025 +0200 Show debug on gradle tests commit 917d0d321356c43c247e9d7b711935802dbe88b8 Author: Stef Tervelde Date: Wed May 14 15:08:47 2025 +0200 Listen for folder changes commit 12ce4422e1bc0cd5abf7173d7a001ea0a26cb35f Author: Stef Tervelde Date: Wed May 14 14:25:06 2025 +0200 Fix saved files using an old version commit 04ac9f2678d6308c6c8dffb68cd98bc2973e7863 Author: Stef Tervelde Date: Wed May 14 13:47:21 2025 +0200 Plugin rework for caching heavy work commit e31d37d64df6aa9ba29ecc0e5529cf91600a3b28 Merge: df1277e58 540d299cf Author: Stef Tervelde Date: Wed May 14 10:43:43 2025 +0200 Merge branch 'main' into runner commit df1277e58c5665acb901a3ea81088eafeccdf283 Author: Stef Tervelde Date: Wed May 14 10:39:22 2025 +0200 Disabled modern UI for now. Added preference instead commit c5c3b2f570cabdd1163405057bf8204d1a707336 Author: Stef Tervelde Date: Mon Apr 21 14:04:39 2025 +0200 Added todos commit 612126a11f690ab7ec7feb7b86c9f9a786cc10cd Merge: 3e89bfe9a ecd219b03 Author: Stef Tervelde Date: Thu Apr 17 12:37:06 2025 +0200 Merge branch 'main' into runner commit 3e89bfe9abef46ee456fabf4d7e054a8d593855f Merge: efa77bb8d 7489870dd Author: Stef Tervelde Date: Thu Apr 17 12:34:57 2025 +0200 Merge branch 'runner' of https://github.com/Stefterv/processing4 into runner commit efa77bb8d2b46680f13bba14b949e84586c81635 Author: Stef Tervelde Date: Tue Apr 1 18:19:58 2025 +0200 Cleanup [skip ci] commit 7489870ddf4287adaea4b02fdc3a22b54ba3a941 Author: Stef Tervelde Date: Tue Apr 1 18:19:58 2025 +0200 Cleanup [skip ci] commit 97ec5e4cb49eb980e87f37155fa3d8a96477e34c Merge: 221ee28a0 3d6a6ce1d Author: Stef Tervelde Date: Tue Apr 1 17:54:41 2025 +0200 Merge branch 'gradle-welcome-screen' into runner commit 221ee28a0fcb516b6e0d129ad1028ca4ff7a51ac Author: Stef Tervelde Date: Tue Apr 1 17:53:48 2025 +0200 Toolbar UI design start commit a870cd03fca9113952dc34bc26db5f134ce76b1c Author: Stef Tervelde Date: Tue Apr 1 10:14:22 2025 +0200 GradleJob Refactor commit 2269fd35d9f91b971a163f6b63fb288adb6f9156 Author: Stef Tervelde Date: Tue Apr 1 09:15:47 2025 +0200 Gradle Runner, build script management commit 6758d684e7863c887c3b559b7b07eeb8eab32080 Author: Stef Tervelde Date: Mon Mar 31 18:40:19 2025 +0200 Richer console output and richer exception output commit 15114a192e36296796f5640874fd816c05e944b5 Author: Stef Tervelde Date: Mon Mar 31 15:00:12 2025 +0200 Retry vm debug connection commit 36ce0ab1e3853eb22a28fcd84c560123aa82ae33 Author: Stef Tervelde Date: Mon Mar 31 12:05:58 2025 +0200 Runner: Fixing build errors commit 701f9f5db84eb114721c033cd2111d16e3320147 Author: Stef Tervelde Date: Sun Mar 30 23:58:36 2025 +0200 Gradle Runner PoC Refactor commit 947a90ea0c204ccd9c5b0139da5eba201dcf2320 Author: Stef Tervelde Date: Sun Mar 30 14:51:48 2025 +0200 Toolbar functionality PoC commit 3d6a6ce1dfbb11650eeaa65de7a985f9d23f49d2 Merge: f7f22c5f7 2222331c9 Author: Stef Tervelde Date: Sun Mar 30 08:52:37 2025 +0200 Merge branch 'processing:main' into gradle-welcome-screen commit 4602ef179ea92133ca5d264d8f40aea4f40d4024 Author: Stef Tervelde Date: Sat Mar 29 22:50:35 2025 +0100 Fixes for windows commit 0d3795f89343c98ed23d9cadffd1445df25da7ba Author: Stef Tervelde Date: Sat Mar 29 22:31:47 2025 +0100 Gradle Runner, more variables commit ab38e5f44b5e72cbace4551ecdeddc80b70b75a1 Author: Stef Tervelde Date: Sat Mar 29 20:54:02 2025 +0100 Arguments refactor commit 17acb01cbe30b1949c5ff60d35f5f8c59d6b567f Author: Stef Tervelde Date: Sat Mar 29 20:10:15 2025 +0100 Copy running JDK rather than download commit b534c26bca8737513f9808f4e737d96a173ce7ce Merge: d6cb3eec4 2222331c9 Author: Stef Tervelde Date: Sat Mar 29 18:17:20 2025 +0100 Merge branch 'main' into runner commit d6cb3eec45cdfeac944d5bb271ebd5733f2c36fe Author: Stef Tervelde Date: Sat Mar 29 10:20:17 2025 +0100 Fix for signing release commit bf82d5327f2368114e499ade3a5eb18b681a4717 Merge: 73dfa0a6e dd823a7d6 Author: Stef Tervelde Date: Sat Mar 29 10:17:06 2025 +0100 Merge branch 'runner' of https://github.com/Stefterv/processing4 into runner commit 73dfa0a6e19c9318504c5a80d7295e495902041b Author: Stef Tervelde Date: Sat Mar 29 10:17:04 2025 +0100 Only sign with signing set up commit 41ef1cc9813ac6638c85ef0cabfe2121129b363c Author: Stef Tervelde Date: Fri Mar 28 09:00:23 2025 +0100 Fix plugin name in tests commit dd823a7d6cf0f9100a3a8ee803a38e8f2c7718cd Author: Stef Tervelde Date: Fri Mar 28 09:00:23 2025 +0100 Fix plugin name in tests commit 3a14ff40a41979ff7ad25a144f34629c0405e3a0 Author: Stef Tervelde Date: Fri Mar 28 08:18:53 2025 +0100 Added Stubs commit 00f8afaa7cd179b720bbd1246c78876295939a2a Merge: 700599ed8 1ab2359ca Author: Stef Tervelde Date: Fri Mar 28 08:13:11 2025 +0100 Merge branch 'schema-threading' into runner commit 700599ed8abf775379a6d14c83d4da9b1b9a05a5 Merge: 1fae4618c 499d200ef Author: Stef Tervelde Date: Fri Mar 28 08:13:06 2025 +0100 Merge branch 'main' into runner commit 1fae4618c926cd27044ae1ed734930943fb8723f Author: Stef Tervelde Date: Thu Mar 27 21:37:55 2025 +0100 UI Cleanup commit f762a3ee12842bea96cee8b011c3bdcb716d2d21 Author: Stef Tervelde Date: Thu Mar 27 20:05:01 2025 +0100 Added debugging commit c2071a0c2624dde91f70aac1be5d60955dab624c Merge: f453eca87 8fb9ef57b Author: Stef Tervelde Date: Thu Mar 27 18:23:23 2025 +0100 Merge branch 'hide-scrollbars' into runner commit f453eca87fd77d4b2b8fc96bcc9a5574a99099c0 Merge: 8f47d2110 694eb0cbe Author: Stef Tervelde Date: Thu Mar 27 18:03:34 2025 +0100 Merge remote-tracking branch 'upstream/fix-jdk-requirement' into runner commit 8f47d211093bbd2bd41146a113b77931cb20ebf5 Author: Stef Tervelde Date: Thu Mar 27 18:00:15 2025 +0100 Scan libaries for dependencies commit 5d71244e0555d3d079e5b2149ba1381e524caa55 Author: Stef Tervelde Date: Thu Mar 27 14:58:11 2025 +0100 Plugin rework commit e164fe48bc1f7ee92029ba7a85ab6a8188fea501 Author: Stef Tervelde Date: Thu Mar 27 13:01:33 2025 +0100 Gradle variables from Processing, Group resolution commit 672c2ac479e6874965fac03a0398b6f68dc79f2d Author: Stef Tervelde Date: Thu Mar 27 11:51:08 2025 +0100 Removed the need for settings.gradle.kts & Embedding maven repo commit cdec594cdd57bd71b4bf0c9525af5c2921edbef0 Author: Stef Tervelde Date: Wed Mar 26 18:39:04 2025 +0100 Added export support commit 3ad36547296ed0bfa2f457cf822c2105e90ef8d7 Author: Stef Tervelde Date: Wed Mar 26 17:25:51 2025 +0100 Gradle Runner visual styling commit bc063e80dbda0c8f78f86b36227948954382528a Author: Stef Tervelde Date: Wed Mar 26 14:17:25 2025 +0100 Gradle Runner PoC commit 47d3428e6298f26aa8b6b951caaddc0d679a811e Author: Stef Tervelde Date: Sun Mar 23 18:18:54 2025 +0100 More todo's commit 29d76eb1d1406b81cd276ffd4ccd325edb86b930 Merge: ba496569d 9760a63b9 Author: Stef Tervelde Date: Sun Mar 23 17:58:39 2025 +0100 Merge branch 'processing:main' into gradle-plugin commit ba496569d8f048448dd6e8e92b2faaf443cf27a7 Author: Stef Tervelde Date: Tue Mar 18 10:15:40 2025 +0100 Adding tests and direct linking commit e01d02a8af6aa7ddc53e5a1900561473a2ed6a99 Author: Stef Tervelde Date: Mon Mar 17 15:31:25 2025 +0100 Update settings.gradle.kts commit b0b12ddad6676e81c2981c76b270ed745ac681bd Merge: 7e6c97add ad5c27ec4 Author: Stef Tervelde Date: Mon Mar 17 15:14:35 2025 +0100 Merge branch 'gradle-preprocessor' into gradle-plugin commit ad5c27ec42dc6f479c1c4cee2b119568ba8fa252 Author: Stef Tervelde Date: Mon Mar 17 15:11:24 2025 +0100 Apply same parser & lexer fixes from before commit 743ea141904c0d37127c4de6b52ed7d35ece3f32 Merge: c28a2676b 8e8f7dc5a Author: Stef Tervelde Date: Mon Mar 17 13:53:40 2025 +0100 Merge branch 'main' into gradle-preprocessor commit 7e6c97add90a4eb8f3ebc49faf40dcd0e61b9951 Merge: f126d4b62 8e8f7dc5a Author: Stef Tervelde Date: Mon Mar 17 13:43:21 2025 +0100 Merge branch 'main' into gradle-plugin commit f7f22c5f7ce8434a058d3cc5411f541777cc637e Author: Stef Tervelde Date: Mon Mar 17 10:40:37 2025 +0100 Create Directory as well commit d17ca0cdcda2080cf458c0352fc89e80ef4f57a7 Author: Stef Tervelde Date: Mon Mar 17 10:36:35 2025 +0100 Removed PlatformStart and create new file if doesn't exist commit b0e7f96bddafc1a40c59d47cd81ef9919632ce84 Author: Stef Tervelde Date: Mon Mar 17 10:28:12 2025 +0100 Added initial tests commit 0df4dc076ac6d50730e09348b941a39451197b33 Merge: 6974811be 8e8f7dc5a Author: Stef Tervelde Date: Sun Mar 16 21:18:22 2025 +0100 Merge branch 'main' into gradle-welcome-screen commit 6974811beb562c7c3bd6fb3ae4d94bf9831d6d3c Merge: 7452d658c 145551246 Author: Stef Tervelde Date: Tue Mar 11 20:42:46 2025 +0100 Merge branch 'test-schema' into gradle-welcome-screen commit 7452d658c8fdb53815d7e5f3ed7ddcaecef302aa Merge: babf54e04 fe152b752 Author: Stef Tervelde Date: Fri Mar 7 15:07:56 2025 +0100 Merge branch 'main' into gradle-welcome-screen commit c28a2676bd9439106800d502c856b798bd1cfc34 Merge: e18833608 fe152b752 Author: Stef Tervelde Date: Fri Mar 7 15:06:27 2025 +0100 Merge branch 'processing:main' into gradle-preprocessor commit f126d4b6286c3f7363d13cab8797702f8cadb66c Merge: da852b45a fe152b752 Author: Stef Tervelde Date: Fri Mar 7 15:06:08 2025 +0100 Merge branch 'processing:main' into gradle-plugin commit babf54e04dde6ba972139c8e5744d4a6f9b449f4 Merge: 0e56f895b 00c57607d Author: Stef Tervelde Date: Mon Feb 10 17:58:35 2025 +0100 Merge branch 'gradle-loggin' into gradle-welcome-screen commit 0e56f895bdaac2c7597d9870eba440cd6fad0997 Merge: 54944afc2 29b557a7c Author: Stef Tervelde Date: Sun Feb 9 21:15:36 2025 +0100 Merge branch 'main-gradle' into gradle-welcome-screen commit 54944afc24a1058bf6fcc5a9899bfdeaa1283229 Author: Stef Tervelde Date: Sun Feb 9 14:36:37 2025 +0100 Welcome Screen: JDK Module commit 8d8bd01d806257367b1a006f0da23b926b8f8ae3 Author: Stef Tervelde Date: Sun Feb 9 13:54:07 2025 +0100 Welcome Screen: Close Functionality commit 3df4da94618ed66d9a2b48a601b0ce7bac9a2d4e Author: Stef Tervelde Date: Sun Feb 9 12:57:55 2025 +0100 Welcome Screen: Bugfixes commit cb184fb104ac9c4ead2e6352573f726189da7b70 Author: Stef Tervelde Date: Sun Feb 9 12:19:33 2025 +0100 Welcome Screen: Layout fixes commit 3d81b02ffed2c28560bc2067f1584e05db0bad51 Author: Stef Tervelde Date: Sat Feb 8 22:47:28 2025 +0100 Welcome Screen: Language selection commit b0c61a4467e8c9f5d02bcc8c84e47908fad22d95 Author: Stef Tervelde Date: Sat Feb 8 11:10:33 2025 +0100 Welcome Screen: Load & Display examples commit 2f12d13422c0359562663d93202fe7ce80c3c949 Author: Stef Tervelde Date: Fri Feb 7 21:53:49 2025 +0100 Saveable/Reactive Preferences commit 8ed2d1b40acdc38e0ecde7a5831ef47a9ad1559b Author: Stef Tervelde Date: Fri Feb 7 16:34:23 2025 +0100 Welcome Screen: WIP commit 5c020dd0170153bd1a31ffaeed853966e66c5175 Author: Stef Tervelde Date: Fri Feb 7 15:49:25 2025 +0100 Welcome Screen: Initial Layout commit bd3a77ef17a456aa2ceb281cc2cf293f8be9d685 Author: Stef Tervelde Date: Fri Feb 7 12:09:58 2025 +0100 Welcome Screen: Window Abstraction commit b1910de2e61f6b8b2492b9847ba7f35eec67317a Author: Stef Tervelde Date: Fri Feb 7 11:24:30 2025 +0100 Welcome Screen: Initial Frame commit da852b45a109e27b3b3226fd78a0efd41b695fad Author: Stef Tervelde Date: Wed Feb 5 20:54:32 2025 +0100 Fullscreen support commit 44ab816ca9fe2113f1868c3bda3372afaa9fec5d Author: Stef Tervelde Date: Wed Feb 5 20:38:27 2025 +0100 disable full screen from settings and fixed a few bugs commit e18833608b1f66866d17dbf8f75708e409fe6022 Merge: df745bb09 3f3e7d785 Author: Stef Tervelde Date: Wed Feb 5 13:03:51 2025 +0100 Merge branch 'main-gradle' into gradle/preprocessor commit 031d304dbd6aa73998e07fa696b691e85dd9c2b1 Merge: 25e147eaf 3f3e7d785 Author: Stef Tervelde Date: Wed Feb 5 13:03:12 2025 +0100 Merge branch 'main-gradle' into gradle/plugin commit 25e147eaf1bdfa8b245e86447d2066bded147b44 Merge: 7f00d5b02 3b9aea1f4 Author: Stef Tervelde Date: Wed Feb 5 12:56:55 2025 +0100 Merge branch 'main-gradle' into gradle/plugin commit df745bb09c080929bc64d70bf90d27e9da807d8f Merge: f016cc474 3b9aea1f4 Author: Stef Tervelde Date: Wed Feb 5 12:56:38 2025 +0100 Merge branch 'main-gradle' into gradle/preprocessor commit 7f00d5b0289dda41d92b126149a81f20d1ccdc31 Author: Stef Tervelde Date: Wed Feb 5 12:36:18 2025 +0100 added example of plugin usage commit f4d7fbf99b399889de6b6d0bbafa0148dcb84782 Author: Stef Tervelde Date: Wed Feb 5 09:19:08 2025 +0100 Changed naming commit 1b8a8ed461cb39eb0489564589e144178fb1bef5 Author: Stef Tervelde Date: Wed Feb 5 09:10:42 2025 +0100 Gradle Plugin from PoC commit 5468da0fd6b42f790340acb3e2d00472ae5f42af Author: Stef Tervelde Date: Tue Feb 4 16:18:29 2025 +0100 Removed outdated ant backwards compatibility marks commit 23c8a4626ac370b560169df130d86d4b58d859e2 Author: Stef Tervelde Date: Mon Feb 3 12:07:01 2025 +0100 - Imports cleanup - Disable system look & feel on macOS for readability - Cleaned unnecessary space - Cleaned finished TODO commit f016cc474caa637f982c8dd21078801974aabb69 Author: Stef Tervelde Date: Wed Feb 5 12:01:47 2025 +0100 Removed core dependency from the pre-processor commit 971d98bba0a8acf24a9430015a7c1508e2c9d2a3 Author: Stef Tervelde Date: Thu Jan 30 22:59:16 2025 +0100 Preprocessor with antlr Plugin * Add Processing Gradle plugin README Introduce java/gradle/README.md documenting the Processing Gradle plugin. Explains how to turn a Processing sketch into a Gradle project using the org.processing.java plugin (build.gradle.kts examples), available tasks (gradle sketch, export, present), dependency and sketchbook configuration, and how to compose multi-sketch projects. Includes examples for using other Gradle plugins and switching Processing versions. * Update README.md --- .gitignore | 25 ++ app/utils/build.gradle.kts | 10 + core/build.gradle.kts | 5 + core/src/processing/core/PApplet.java | 18 +- java/gradle/README.md | 113 +++++++ java/gradle/build.gradle.kts | 41 +++ java/gradle/example/brightness.pde | 28 ++ java/gradle/example/build.gradle.kts | 3 + java/gradle/example/settings.gradle.kts | 5 + .../src/main/kotlin/DependenciesTask.kt | 79 +++++ java/gradle/src/main/kotlin/LibrariesTask.kt | 81 +++++ java/gradle/src/main/kotlin/PDETask.kt | 83 +++++ .../src/main/kotlin/ProcessingPlugin.kt | 216 +++++++++++++ .../src/test/kotlin/ProcessingPluginTest.kt | 303 ++++++++++++++++++ settings.gradle.kts | 1 + 15 files changed, 1003 insertions(+), 8 deletions(-) create mode 100644 java/gradle/README.md create mode 100644 java/gradle/build.gradle.kts create mode 100644 java/gradle/example/brightness.pde create mode 100644 java/gradle/example/build.gradle.kts create mode 100644 java/gradle/example/settings.gradle.kts create mode 100644 java/gradle/src/main/kotlin/DependenciesTask.kt create mode 100644 java/gradle/src/main/kotlin/LibrariesTask.kt create mode 100644 java/gradle/src/main/kotlin/PDETask.kt create mode 100644 java/gradle/src/main/kotlin/ProcessingPlugin.kt create mode 100644 java/gradle/src/test/kotlin/ProcessingPluginTest.kt 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/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/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/java/gradle/README.md b/java/gradle/README.md new file mode 100644 index 0000000000..b827972769 --- /dev/null +++ b/java/gradle/README.md @@ -0,0 +1,113 @@ +# Processing Gradle Plugin + +This folder contains the source for the Processing Gradle plugin. +The plugin will transform any Processing sketch into a Gradle project for easy compilation and advanced features. + +## Motivation + +Processing was designed to be easy to start with, and the PDE (Processing Development Environment) handles most things +for you: you can write code, import libraries, run your sketch, or even export it as an executable. This works very well +for learning and for small to medium sketches, but it isn’t ideal for larger projects. + +With the Processing Gradle Plugin, we want to make it possible to build more ambitious projects on top of Processing. +This is intended for users who are comfortable moving beyond the PDE, such as artists and developers working on larger +sketches, long running installations, multi sketch projects, or teams who want version control, automated builds, and +integration with standard Java tools and editors. It is optional and does not replace the PDE, but complements it for +more advanced workflows. + +## What is Gradle + +Gradle is a build tool commonly used in the Java ecosystem. It is responsible for tasks like compiling code, managing +dependencies, and running applications. You do not need to learn Gradle to use Processing in the P + +## Usage + +Add the following files to any Processing sketch alongside the `.pde` files + +`build.gradle.kts` +```kotlin +plugins { + id("org.processing.java") version "4.5.3" +} +``` + +The version number determines which version of Processing will be used. + +`settings.gradle.kts` +create the file but leave blank + +This will turn the Processing sketch into a Gradle project, usable with any editor that supports Gradle. +Including the `gradle` command if installed. If you want to use your own editor, or no editor at all, use the +gradle command if installed. Find installation instructions +here: https://docs.gradle.org/current/userguide/installation.html + +The plugin will add the `sketch` command to the Gradle tasks lists, so run the sketch with `gradle sketch`, this will +build and launch your sketch. + +The sketch can also be bundled into a standalone app by using the `gradle export` command. +Or run in fullscreen with `gradle present` + +To include libraries into your sketch add `processing.sketchbook=/path/to/sketchbook` to a `gradle.properties` file in +the same folder. + +To use any kind of dependency add as a normal gradle dependency, the plugin has already automatically added the Maven +Central repository. + +`build.gradle.kts` +```kotlin +plugins { + id("org.processing.java") version "4.5.3" +} + +dependencies { + implementation("com.lowagie:itext:2.1.7") +} +``` + +To use an older version of Processing just change the plugin version: + +`build.gradle.kts` +```kotlin +plugins { + id("org.processing.java") version "4.5.0" +} +``` + +Other gradle plugins are also supported + +`build.gradle.kts` +```kotlin +plugins { + id("org.processing.java") version "4.5.3" + id("com.gradleup.shadow") version "" +} +``` + +If you want to combine multiple sketches into a single project + +`sketch-a/build.gradle.kts` +```kotlin +plugins { + id("org.processing.java") version "4.5.3" +} +``` + +`sketch-b/build.gradle.kts` + +```kotlin +plugins { + id("org.processing.java") version "4.5.3" +} +``` + +`build.gradle.kts` + +```kotlin +plugins { + id("org.processing.java") version "4.5.3" apply false +} +``` + +`settings.gradle.kts` - create the file but leave blank + +Then run all sketches at once with `gradle sketch` \ No newline at end of file diff --git a/java/gradle/build.gradle.kts b/java/gradle/build.gradle.kts new file mode 100644 index 0000000000..0171384f44 --- /dev/null +++ b/java/gradle/build.gradle.kts @@ -0,0 +1,41 @@ +plugins{ + `java-gradle-plugin` + alias(libs.plugins.gradlePublish) + + kotlin("jvm") version libs.versions.kotlin +} + +repositories { + mavenCentral() + maven("https://jogamp.org/deployment/maven") +} + +dependencies{ + implementation(project(":java:preprocessor")) + + implementation(libs.composeGradlePlugin) + implementation(libs.kotlinGradlePlugin) + implementation(libs.kotlinComposePlugin) + + testImplementation(project(":core")) + testImplementation(libs.junit) +} + +// TODO: CI/CD for publishing the plugin to the Gradle Plugin Portal +gradlePlugin{ + plugins{ + create("processing.java"){ + id = "org.processing.java" + implementationClass = "org.processing.java.gradle.ProcessingPlugin" + } + } +} +publishing{ + repositories{ + mavenLocal() + maven { + name = "App" + url = uri(project(":app").layout.buildDirectory.dir("resources-bundled/common/repository").get().asFile.absolutePath) + } + } +} \ No newline at end of file diff --git a/java/gradle/example/brightness.pde b/java/gradle/example/brightness.pde new file mode 100644 index 0000000000..dad7885af3 --- /dev/null +++ b/java/gradle/example/brightness.pde @@ -0,0 +1,28 @@ +/** + * Brightness + * by Rusty Robison. + * + * Brightness is the relative lightness or darkness of a color. + * Move the cursor vertically over each bar to alter its brightness. + */ + +int barWidth = 20; +int lastBar = -1; + + +void setup() { + size(640, 360, P2D); + colorMode(HSB, width, 100, height); + noStroke(); + background(0); +} + +void draw() { + int whichBar = mouseX / barWidth; + if (whichBar != lastBar) { + int barX = whichBar * barWidth; + fill(barX, 100, mouseY); + rect(barX, 0, barWidth, height); + lastBar = whichBar; + } +} diff --git a/java/gradle/example/build.gradle.kts b/java/gradle/example/build.gradle.kts new file mode 100644 index 0000000000..b476d51bba --- /dev/null +++ b/java/gradle/example/build.gradle.kts @@ -0,0 +1,3 @@ +plugins{ + id("org.processing.java") +} \ No newline at end of file diff --git a/java/gradle/example/settings.gradle.kts b/java/gradle/example/settings.gradle.kts new file mode 100644 index 0000000000..ee9c97e155 --- /dev/null +++ b/java/gradle/example/settings.gradle.kts @@ -0,0 +1,5 @@ +rootProject.name = "processing-gradle-plugin-demo" + +pluginManagement { + includeBuild("../../../") +} \ No newline at end of file diff --git a/java/gradle/src/main/kotlin/DependenciesTask.kt b/java/gradle/src/main/kotlin/DependenciesTask.kt new file mode 100644 index 0000000000..8e2cb9bca3 --- /dev/null +++ b/java/gradle/src/main/kotlin/DependenciesTask.kt @@ -0,0 +1,79 @@ +package org.processing.java.gradle + +import org.gradle.api.DefaultTask +import org.gradle.api.GradleException +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.TaskAction +import java.io.File +import java.io.ObjectInputStream + +/* +* The DependenciesTask resolves the dependencies for the sketch based on the libraries used + */ +abstract class DependenciesTask: DefaultTask() { + @InputFile + val librariesMetaData: RegularFileProperty = project.objects.fileProperty() + + @InputFile + val sketchMetaData: RegularFileProperty = project.objects.fileProperty() + + init{ + librariesMetaData.convention(project.layout.buildDirectory.file("processing/libraries")) + sketchMetaData.convention(project.layout.buildDirectory.file("processing/sketch")) + } + + @TaskAction + fun execute() { + val sketchMetaFile = sketchMetaData.get().asFile + val librariesMetaFile = librariesMetaData.get().asFile + + val libraries = librariesMetaFile.inputStream().use { input -> + ObjectInputStream(input).readObject() as ArrayList + } + + val sketch = sketchMetaFile.inputStream().use { input -> + ObjectInputStream(input).readObject() as PDETask.SketchMeta + } + + val dependencies = mutableSetOf() + + // Loop over the import statements in the sketch and import the relevant jars from the libraries + sketch.importStatements.forEach import@{ statement -> + libraries.forEach { library -> + library.jars.forEach { jar -> + jar.classes.forEach { className -> + if (className.startsWith(statement)) { + dependencies.addAll(library.jars.map { it.path } ) + return@import + } + } + } + } + } + project.dependencies.add("implementation", project.files(dependencies) ) + + // TODO: Mutating the dependencies of configuration ':implementation' after it has been resolved or consumed. This + + // TODO: Add only if user is compiling for P2D or P3D + // Add JOGL and Gluegen dependencies + project.dependencies.add("runtimeOnly", "org.jogamp.jogl:jogl-all-main:2.5.0") + project.dependencies.add("runtimeOnly", "org.jogamp.gluegen:gluegen-rt:2.5.0") + + val os = System.getProperty("os.name").lowercase() + val arch = System.getProperty("os.arch").lowercase() + + val variant = when { + os.contains("mac") -> "macosx-universal" + os.contains("win") && arch.contains("64") -> "windows-amd64" + os.contains("linux") && arch.contains("aarch64") -> "linux-aarch64" + os.contains("linux") && arch.contains("arm") -> "linux-arm" + os.contains("linux") && arch.contains("amd64") -> "linux-amd64" + else -> throw GradleException("Unsupported OS/architecture: $os / $arch") + } + + project.dependencies.add("runtimeOnly", "org.jogamp.gluegen:gluegen-rt:2.5.0:natives-$variant") + project.dependencies.add("runtimeOnly", "org.jogamp.jogl:nativewindow:2.5.0:natives-$variant") + project.dependencies.add("runtimeOnly", "org.jogamp.jogl:newt:2.5.0:natives-$variant") + } +} \ No newline at end of file diff --git a/java/gradle/src/main/kotlin/LibrariesTask.kt b/java/gradle/src/main/kotlin/LibrariesTask.kt new file mode 100644 index 0000000000..2ccca5cde7 --- /dev/null +++ b/java/gradle/src/main/kotlin/LibrariesTask.kt @@ -0,0 +1,81 @@ +package org.processing.java.gradle + +import org.gradle.api.DefaultTask +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.tasks.InputFiles +import org.gradle.api.tasks.OutputFile +import org.gradle.api.tasks.TaskAction +import java.io.File +import java.io.ObjectOutputStream +import java.util.jar.JarFile + +/* +The libraries task scans the sketchbook libraries folder for all the libraries +This task stores the resulting information in a file that can be used later to resolve dependencies + */ +abstract class LibrariesTask : DefaultTask() { + + @InputFiles + val libraryDirectories: ConfigurableFileCollection = project.files() + + @OutputFile + val librariesMetaData: RegularFileProperty = project.objects.fileProperty() + + init{ + librariesMetaData.convention { project.gradle.gradleUserHomeDir.resolve("common/processing/libraries") } + } + + data class Jar( + val path: File, + val classes: List + ) : java.io.Serializable + + data class Library( + val jars: List + ) : java.io.Serializable + + @TaskAction + fun execute() { + val output = libraryDirectories.flatMap { librariesDirectory -> + if (!librariesDirectory.exists()) { + logger.error("Libraries directory (${librariesDirectory.path}) does not exist. Libraries will not be imported.") + return@flatMap emptyList() + } + val libraries = librariesDirectory + .listFiles { file -> file.isDirectory } + ?.map { folder -> + // Find all the jars in the sketchbook + val jars = folder.resolve("library") + .listFiles{ file -> file.extension == "jar" } + ?.map{ file -> + + // Inside each jar, look for the defined classes + val jar = JarFile(file) + val classes = jar.entries().asSequence() + .filter { entry -> entry.name.endsWith(".class") } + .map { entry -> entry.name } + .map { it.substringBeforeLast('/').replace('/', '.') } + .distinct() + .toList() + + // Return a reference to the jar and its classes + return@map Jar( + path = file, + classes = classes + ) + }?: emptyList() + + // Save the parsed jars and which folder + return@map Library( + jars = jars + ) + }?: emptyList() + + return@flatMap libraries + } + val meta = ObjectOutputStream(librariesMetaData.get().asFile.outputStream()) + meta.writeObject(output) + meta.close() + } +} \ No newline at end of file diff --git a/java/gradle/src/main/kotlin/PDETask.kt b/java/gradle/src/main/kotlin/PDETask.kt new file mode 100644 index 0000000000..76ac195e53 --- /dev/null +++ b/java/gradle/src/main/kotlin/PDETask.kt @@ -0,0 +1,83 @@ +package org.processing.java.gradle + +import org.gradle.api.file.* +import org.gradle.api.tasks.* +import org.gradle.internal.file.Deleter +import org.gradle.work.InputChanges +import processing.mode.java.preproc.PdePreprocessor +import java.io.File +import java.io.ObjectOutputStream +import java.io.Serializable +import java.util.concurrent.Callable +import java.util.jar.JarFile +import javax.inject.Inject + + +// TODO: Generate sourcemaps +/* +* The PDETask is the main task that processes the .pde files and generates the Java source code through the PdePreprocessor. + */ +abstract class PDETask : SourceTask() { + @get:InputFiles + @get:PathSensitive(PathSensitivity.RELATIVE) + @get:IgnoreEmptyDirectories + @get:SkipWhenEmpty + open val stableSources: FileCollection = project.files(Callable { this.source }) + + @OutputDirectory + val outputDirectory: DirectoryProperty = project.objects.directoryProperty() + + @get:Input + var sketchName: String = "processing" + + @OutputFile + val sketchMetaData: RegularFileProperty = project.objects.fileProperty() + + init{ + outputDirectory.convention(project.layout.buildDirectory.dir("generated/pde")) + sketchMetaData.convention(project.layout.buildDirectory.file("processing/sketch")) + } + + data class SketchMeta( + val sketchName: String, + val sketchRenderer: String?, + val importStatements: List + ) : Serializable + + @TaskAction + fun execute() { + // Using stableSources since we can only run the pre-processor on the full set of sources + val combined = stableSources + .files + .groupBy { it.name } + .map { entry -> + entry.value.firstOrNull { it.parentFile?.name == "unsaved" } + ?: entry.value.first() + } + .joinToString("\n"){ + it.readText() + } + val javaFile = File(outputDirectory.get().asFile, "$sketchName.java").bufferedWriter() + + val meta = PdePreprocessor + .builderFor(sketchName) + .setTabSize(4) + .build() + .write(javaFile, combined) + + // TODO: Save the edits to meta files + + javaFile.flush() + javaFile.close() + + val sketchMeta = SketchMeta( + sketchName = sketchName, + sketchRenderer = meta.sketchRenderer, + importStatements = meta.importStatements.map { importStatement -> importStatement.packageName } + ) + + val metaFile = ObjectOutputStream(sketchMetaData.get().asFile.outputStream()) + metaFile.writeObject(sketchMeta) + metaFile.close() + } +} \ No newline at end of file diff --git a/java/gradle/src/main/kotlin/ProcessingPlugin.kt b/java/gradle/src/main/kotlin/ProcessingPlugin.kt new file mode 100644 index 0000000000..df558710f3 --- /dev/null +++ b/java/gradle/src/main/kotlin/ProcessingPlugin.kt @@ -0,0 +1,216 @@ +package org.processing.java.gradle + +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.file.SourceDirectorySet +import org.gradle.api.internal.file.DefaultSourceDirectorySet +import org.gradle.api.internal.tasks.TaskDependencyFactory +import org.gradle.api.model.ObjectFactory +import org.gradle.api.plugins.JavaPlugin +import org.gradle.api.plugins.JavaPluginExtension +import org.gradle.api.tasks.JavaExec +import org.jetbrains.compose.ComposeExtension +import org.jetbrains.compose.desktop.DesktopExtension +import java.io.File +import java.net.Socket +import javax.inject.Inject + +class ProcessingPlugin @Inject constructor(private val objectFactory: ObjectFactory) : Plugin { + override fun apply(project: Project) { + val sketchName = project.layout.projectDirectory.asFile.name.replace(Regex("[^a-zA-Z0-9_]"), "_") + + val isProcessing = project.findProperty("processing.version") != null + val processingVersion = project.findProperty("processing.version") as String? ?: "4.3.4" + val processingGroup = project.findProperty("processing.group") as String? ?: "org.processing" + val workingDir = project.findProperty("processing.workingDir") as String? + val debugPort = project.findProperty("processing.debugPort") as String? + val logPort = project.findProperty("processing.logPort") as String? + val errPort = project.findProperty("processing.errPort") as String? + + // TODO: Setup sketchbook when using as a standalone plugin, use the Java Preferences + val sketchbook = project.findProperty("processing.sketchbook") as String? + val settings = project.findProperty("processing.settings") as String? + val root = project.findProperty("processing.root") as String? + + // Apply the Java plugin to the Project, equivalent of + // plugins { + // java + // } + project.plugins.apply(JavaPlugin::class.java) + + if(isProcessing){ + // Set the build directory to a temp file so it doesn't clutter up the sketch folder + // Only if the build directory doesn't exist, otherwise proceed as normal + if(!project.layout.buildDirectory.asFile.get().exists()) { + project.layout.buildDirectory.set(File(project.findProperty("processing.workingDir") as String)) + } + // Disable the wrapper in the sketch to keep it cleaner + project.tasks.findByName("wrapper")?.enabled = false + } + + // Add kotlin support, equivalent of + // plugins { + // kotlin("jvm") version "1.8.0" + // kotlin("plugin.compose") version "1.8.0" + // } + project.plugins.apply("org.jetbrains.kotlin.jvm") + // Add jetpack compose support + project.plugins.apply("org.jetbrains.kotlin.plugin.compose") + // Add the compose plugin to wrap the sketch in an executable + project.plugins.apply("org.jetbrains.compose") + + // Add the Processing core library (within Processing from the internal maven repo and outside from the internet), equivalent of + // dependencies { + // implementation("org.processing:core:4.3.4") + // } + project.dependencies.add("implementation", "$processingGroup:core:${processingVersion}") + + // Add the jars in the code folder, equivalent of + // dependencies { + // implementation(fileTree("src") { include("**/code/*.jar") }) + // } + project.dependencies.add("implementation", project.fileTree("src").apply { include("**/code/*.jar") }) + + // Add the repositories necessary for building the sketch, equivalent of + // repositories { + // maven("https://jogamp.org/deployment/maven") + // mavenCentral() + // mavenLocal() + // } + project.repositories.add(project.repositories.maven { it.setUrl("https://jogamp.org/deployment/maven") }) + project.repositories.add(project.repositories.mavenCentral()) + project.repositories.add(project.repositories.mavenLocal()) + + // Configure the compose Plugin, equivalent of + // compose { + // application { + // mainClass.set(sketchName) + // nativeDistributions { + // includeAllModules() + // } + // } + // } + project.extensions.configure(ComposeExtension::class.java) { extension -> + extension.extensions.getByType(DesktopExtension::class.java).application { application -> + // Set the class to be executed initially + application.mainClass = sketchName + application.nativeDistributions.includeAllModules = true + if(debugPort != null) { + application.jvmArgs("-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=$debugPort") + } + } + } + + // TODO: Add support for customizing distributables + // TODO: Setup sensible defaults for the distributables + + // Add convenience tasks for running, presenting, and exporting the sketch outside of Processing + if(!isProcessing) { + project.tasks.create("sketch").apply { + group = "processing" + description = "Runs the Processing sketch" + dependsOn("run") + } + project.tasks.create("present").apply { + group = "processing" + description = "Presents the Processing sketch" + doFirst { + project.tasks.withType(JavaExec::class.java).configureEach { task -> + task.systemProperty("processing.fullscreen", "true") + } + } + finalizedBy("run") + } + project.tasks.create("export").apply { + group = "processing" + description = "Creates a distributable version of the Processing sketch" + + dependsOn("createDistributable") + + } + } + + project.afterEvaluate { + // Copy the result of create distributable to the project directory + project.tasks.named("createDistributable") { task -> + task.doLast { + project.copy { + it.from(project.tasks.named("createDistributable").get().outputs.files) + it.into(project.layout.projectDirectory) + } + } + } + } + + // Move the processing variables into javaexec tasks so they can be used in the sketch as well + project.tasks.withType(JavaExec::class.java).configureEach { task -> + project.properties + .filterKeys { it.startsWith("processing") } + .forEach { (key, value) -> task.systemProperty(key, value) } + + // Connect the stdio to the PDE if ports are specified + if(logPort != null) task.standardOutput = Socket("localhost", logPort.toInt()).outputStream + if(errPort != null) task.errorOutput = Socket("localhost", errPort.toInt()).outputStream + + } + + // For every Java Source Set (main, test, etc) add a PDE source set that includes .pde files + // and a task to process them before compilation + project.extensions.getByType(JavaPluginExtension::class.java).sourceSets.first().let{ sourceSet -> + val pdeSourceSet = objectFactory.newInstance( + DefaultPDESourceDirectorySet::class.java, + objectFactory.sourceDirectorySet("${sourceSet.name}.pde", "${sourceSet.name} Processing Source") + ) + + // Configure the PDE source set to include all .pde files in the sketch folder except those in the build directory + pdeSourceSet.apply { + srcDir("./") + srcDir("$workingDir/unsaved") + + filter.include("**/*.pde") + filter.exclude("${project.layout.buildDirectory.asFile.get().name}/**") + } + sourceSet.allSource.source(pdeSourceSet) + + // Add top level java source files + sourceSet.java.srcDir(project.layout.projectDirectory).apply { + include("/*.java") + } + + // Scan the libraries before compiling the sketches + val librariesTaskName = sourceSet.getTaskName("scanLibraries", "PDE") + val librariesScan = project.tasks.register(librariesTaskName, LibrariesTask::class.java) { task -> + task.description = "Scans the libraries in the sketchbook" + task.libraryDirectories.from(sketchbook?.let { File(it, "libraries") }, root?.let { File(it).resolve("modes/java/libraries") }) + } + + // Create a task to process the .pde files before compiling the java sources + val pdeTaskName = sourceSet.getTaskName("preprocess", "PDE") + val pdeTask = project.tasks.register(pdeTaskName, PDETask::class.java) { task -> + task.description = "Processes the ${sourceSet.name} PDE" + task.source = pdeSourceSet + task.sketchName = sketchName + + // Set the output of the pre-processor as the input for the java compiler + sourceSet.java.srcDir(task.outputDirectory) + } + + val depsTaskName = sourceSet.getTaskName("addLegacyDependencies", "PDE") + project.tasks.register(depsTaskName, DependenciesTask::class.java){ task -> + // Link the output of the libraries task to the dependencies task + task.librariesMetaData.set(librariesScan.get().librariesMetaData) + task.dependsOn(pdeTask, librariesScan) + } + + // Make sure that the PDE tasks runs before the java compilation task + project.tasks.named(sourceSet.compileJavaTaskName) { task -> + task.dependsOn(pdeTaskName, depsTaskName) + } + } + } + abstract class DefaultPDESourceDirectorySet @Inject constructor( + sourceDirectorySet: SourceDirectorySet, + taskDependencyFactory: TaskDependencyFactory + ) : DefaultSourceDirectorySet(sourceDirectorySet, taskDependencyFactory), SourceDirectorySet +} + diff --git a/java/gradle/src/test/kotlin/ProcessingPluginTest.kt b/java/gradle/src/test/kotlin/ProcessingPluginTest.kt new file mode 100644 index 0000000000..7ffeeecb58 --- /dev/null +++ b/java/gradle/src/test/kotlin/ProcessingPluginTest.kt @@ -0,0 +1,303 @@ +import org.gradle.testkit.runner.BuildResult +import org.gradle.testkit.runner.GradleRunner +import org.junit.Test +import org.junit.rules.TemporaryFolder +import java.io.File +import java.lang.management.ManagementFactory +import java.net.URLClassLoader + +class ProcessingPluginTest{ + // TODO: Test on multiple platforms since there are meaningful differences between the platforms + data class TemporaryProcessingSketchResult( + val buildResult: BuildResult, + val sketchFolder: File, + val classLoader: ClassLoader + ) + + fun createTemporaryProcessingSketch(vararg arguments: String, configure: (sketchFolder: File) -> Unit): TemporaryProcessingSketchResult{ + val directory = TemporaryFolder() + directory.create() + val sketchFolder = directory.newFolder("sketch") + directory.newFile("sketch/build.gradle.kts").writeText(""" + plugins { + id("org.processing.java") + } + """.trimIndent()) + directory.newFile("sketch/settings.gradle.kts") + configure(sketchFolder) + + val buildResult = GradleRunner.create() + .withProjectDir(sketchFolder) + .withArguments(*arguments) + .withPluginClasspath() + .withDebug(true) + .build() + + val classDir = sketchFolder.resolve("build/classes/java/main") + val classLoader = URLClassLoader(arrayOf(classDir.toURI().toURL()), this::class.java.classLoader) + + return TemporaryProcessingSketchResult( + buildResult, + sketchFolder, + classLoader + ) + } + + data class TemporaryProcessingLibraryResult( + val buildResult: BuildResult, + val libraryFolder: File + ) + + fun createTemporaryProcessingLibrary(name: String): TemporaryProcessingLibraryResult{ + val directory = TemporaryFolder() + directory.create() + val libraryFolder = directory.newFolder("libraries",name) + directory.newFile("libraries/$name/build.gradle.kts").writeText(""" + plugins { + java + } + tasks.jar{ + destinationDirectory.set(file("library")) + } + """.trimIndent()) + val srcDirectory = directory.newFolder("libraries", name,"src", "main", "java") + directory.newFile("libraries/$name/src/main/java/Example.java").writeText(""" + package testing.example; + + public class Example { + public void exampleMethod() { + System.out.println("Hello from Example library"); + } + } + """.trimIndent()) + directory.newFile("libraries/$name/settings.gradle.kts") + directory.newFile("libraries/$name/library.properties").writeText(""" + name=$name + author=Test Author + version=1.0.0 + sentence=An example library + paragraph=This is a longer description of the example library. + category=Examples + url=http://example.com + """.trimIndent()) + + if(isDebuggerAttached()){ + openFolderInFinder(libraryFolder) + } + + val buildResult = GradleRunner.create() + .withProjectDir(libraryFolder) + .withArguments("jar") + .withPluginClasspath() + .withDebug(true) + .build() + + + return TemporaryProcessingLibraryResult( + buildResult, + libraryFolder + ) + } + + @Test + fun testSinglePDE(){ + val (buildResult, sketchFolder, classLoader) = createTemporaryProcessingSketch("build"){ sketchFolder -> + sketchFolder.resolve("sketch.pde").writeText(""" + void setup(){ + size(100, 100); + } + + void draw(){ + println("Hello World"); + } + """.trimIndent()) + } + + val sketchClass = classLoader.loadClass("sketch") + + assert(sketchClass != null) { + "Class sketch not found" + } + + assert(sketchClass?.methods?.find { method -> method.name == "setup" } != null) { + "Method setup not found in class sketch" + } + + assert(sketchClass?.methods?.find { method -> method.name == "draw" } != null) { + "Method draw not found in class sketch" + } + } + + @Test + fun testMultiplePDE(){ + val (buildResult, sketchFolder, classLoader) = createTemporaryProcessingSketch("build"){ sketchFolder -> + sketchFolder.resolve("sketch.pde").writeText(""" + void setup(){ + size(100, 100); + } + + void draw(){ + otherFunction(); + } + """.trimIndent()) + sketchFolder.resolve("sketch2.pde").writeText(""" + void otherFunction(){ + println("Hi"); + } + """.trimIndent()) + } + + val sketchClass = classLoader.loadClass("sketch") + + assert(sketchClass != null) { + "Class sketch not found" + } + + assert(sketchClass?.methods?.find { method -> method.name == "otherFunction" } != null) { + "Method otherFunction not found in class sketch" + } + + } + + @Test + fun testJavaSourceFile(){ + val (buildResult, sketchFolder, classLoader) = createTemporaryProcessingSketch("build"){ sketchFolder -> + sketchFolder.resolve("sketch.pde").writeText(""" + void setup(){ + size(100, 100); + } + + void draw(){ + println("Hello World"); + } + """.trimIndent()) + sketchFolder.resolve("extra.java").writeText(""" + class SketchJava { + public void javaMethod() { + System.out.println("Hello from Java"); + } + } + """.trimIndent()) + } + val sketchJavaClass = classLoader.loadClass("SketchJava") + + assert(sketchJavaClass != null) { + "Class SketchJava not found" + } + + assert(sketchJavaClass?.methods?.find { method -> method.name == "javaMethod" } != null) { + "Method javaMethod not found in class SketchJava" + } + } + + @Test + fun testWithUnsavedSource(){ + val (buildResult, sketchFolder, classLoader) = createTemporaryProcessingSketch("build"){ sketchFolder -> + sketchFolder.resolve("sketch.pde").writeText(""" + void setup(){ + size(100, 100); + } + + void draw(){ + println("Hello World"); + } + """.trimIndent()) + sketchFolder.resolve("../unsaved").mkdirs() + sketchFolder.resolve("../unsaved/sketch.pde").writeText(""" + void setup(){ + size(100, 100); + } + + void draw(){ + println("Hello World"); + } + + void newMethod(){ + println("This is an unsaved method"); + } + """.trimIndent()) + sketchFolder.resolve("gradle.properties").writeText(""") + processing.workingDir = ${sketchFolder.parentFile.absolutePath} + """.trimIndent()) + } + val sketchClass = classLoader.loadClass("sketch") + + assert(sketchClass != null) { + "Class sketch not found" + } + + assert(sketchClass?.methods?.find { method -> method.name == "newMethod" } != null) { + "Method otherFunction not found in class sketch" + } + } + + @Test + fun testImportingLibrary(){ + val libraryResult = createTemporaryProcessingLibrary("ExampleLibrary") + val (buildResult, sketchFolder, classLoader) = createTemporaryProcessingSketch("build") { sketchFolder -> + sketchFolder.resolve("sketch.pde").writeText(""" + import testing.example.*; + + Example example; + + void setup(){ + size(100, 100); + example = new Example(); + example.exampleMethod(); + } + + void draw(){ + println("Hello World"); + } + """.trimIndent()) + sketchFolder.resolve("gradle.properties").writeText(""") + processing.sketchbook = ${libraryResult.libraryFolder.parentFile.parentFile.absolutePath} + """.trimIndent()) + } + + val sketchClass = classLoader.loadClass("sketch") + + assert(sketchClass != null) { + "Class sketch not found" + } + + assert(sketchClass?.methods?.find { method -> method.name == "setup" } != null) { + "Method setup not found in class sketch" + } + + assert(sketchClass?.methods?.find { method -> method.name == "draw" } != null) { + "Method draw not found in class sketch" + } + } + + @Test + fun testUseInternalLibraries(){ + + } + + @Test + fun testUseCodeJar(){ + // TODO: test if adding jars to the code folder works + } + + fun isDebuggerAttached(): Boolean { + val runtimeMxBean = ManagementFactory.getRuntimeMXBean() + val inputArguments = runtimeMxBean.inputArguments + return inputArguments.any { + it.contains("-agentlib:jdwp") + } + } + fun openFolderInFinder(folder: File) { + if (!folder.exists() || !folder.isDirectory) { + println("Invalid directory: ${folder.absolutePath}") + return + } + + val process = ProcessBuilder("open", folder.absolutePath) + .inheritIO() + .start() + process.waitFor() + } +} + + diff --git a/settings.gradle.kts b/settings.gradle.kts index 6f21e89838..813b0a296c 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -11,6 +11,7 @@ include( "app:utils", "java", "java:preprocessor", + "java:gradle", "java:libraries:dxf", "java:libraries:io", "java:libraries:net", From e42055f867b582e7697df404720ebf32149bccdb Mon Sep 17 00:00:00 2001 From: harsh kumar <135993950+hxrshxz@users.noreply.github.com> Date: Fri, 6 Feb 2026 20:14:36 +0530 Subject: [PATCH 28/42] Add diagnostics gathering feature to debugger and UI (#1297) * Add diagnostics gathering feature to debugger and UI * Improve error messaging in Debugger and optimize token skipping in DiagnosticsDialog * add suggested chaanges * clean up --- app/src/processing/app/ui/Editor.java | 9 ++ app/src/processing/app/ui/EditorFooter.java | 16 ++- java/src/processing/mode/java/JavaEditor.java | 10 ++ .../processing/mode/java/debug/Debugger.java | 119 ++++++++++++++++++ 4 files changed, 151 insertions(+), 3 deletions(-) diff --git a/app/src/processing/app/ui/Editor.java b/app/src/processing/app/ui/Editor.java index 3ef108a27d..d5d964b164 100644 --- a/app/src/processing/app/ui/Editor.java +++ b/app/src/processing/app/ui/Editor.java @@ -2946,4 +2946,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/java/src/processing/mode/java/JavaEditor.java b/java/src/processing/mode/java/JavaEditor.java index 8d9b2252c8..561e38ba54 100644 --- a/java/src/processing/mode/java/JavaEditor.java +++ b/java/src/processing/mode/java/JavaEditor.java @@ -2234,4 +2234,14 @@ static private int howManyFloats(List> handles) { } return count; } + + @Override + public String getSketchDiagnostics() { + if (debugger.isStarted()) { + return debugger.getDiagnostics(); + } else if (runtime != null) { + return Debugger.getDiagnostics(runtime); + } + return super.getSketchDiagnostics(); + } } diff --git a/java/src/processing/mode/java/debug/Debugger.java b/java/src/processing/mode/java/debug/Debugger.java index 0136793200..e9d42895be 100644 --- a/java/src/processing/mode/java/debug/Debugger.java +++ b/java/src/processing/mode/java/debug/Debugger.java @@ -31,6 +31,7 @@ import java.awt.event.KeyEvent; import java.io.*; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -458,6 +459,124 @@ public synchronized void stepOut() { } + + /** + * Get diagnostics from the sketch, whether paused or running. + * If running, it will temporarily suspend the VM. + */ + public String getDiagnostics() { + return getDiagnostics(runtime); + } + + + /** + * Static helper to fetch diagnostics from a Runner, even if not debugging. + * Uses field reads instead of method invocations to avoid thread state issues. + */ + public static String getDiagnostics(Runner targetRuntime) { + if (targetRuntime == null) return ""; + VirtualMachine targetVM = targetRuntime.vm(); + if (targetVM == null) return ""; + + targetVM.suspend(); + try { + // Find the PApplet subclass + List pAppletClasses = targetVM.classesByName("processing.core.PApplet"); + if (pAppletClasses.isEmpty()) { + return "processing.core.PApplet not found in VM"; + } + ClassType pAppletBase = (ClassType) pAppletClasses.get(0); + + ClassType sketchClass = null; + for (ReferenceType type : targetVM.allClasses()) { + if (type instanceof ClassType) { + ClassType ct = (ClassType) type; + ClassType superclass = ct.superclass(); + while (superclass != null) { + if (superclass.equals(pAppletBase)) { + sketchClass = ct; + break; + } + superclass = superclass.superclass(); + } + if (sketchClass != null) break; + } + } + + if (sketchClass == null) { + return "Could not find sketch class extending PApplet"; + } + + // Find instance + List instances = sketchClass.instances(1); + if (instances.isEmpty()) { + return "No instance of " + sketchClass.name() + " found"; + } + ObjectReference appletInstance = instances.get(0); + + // Build diagnostics by reading fields directly (no thread required) + StringBuilder diag = new StringBuilder(); + diag.append("Sketch Diagnostics:\n"); + diag.append(" Class: ").append(sketchClass.name()).append("\n"); + + // Read PApplet fields + appendField(diag, appletInstance, pAppletBase, "width"); + appendField(diag, appletInstance, pAppletBase, "height"); + appendField(diag, appletInstance, pAppletBase, "pixelDensity"); + appendField(diag, appletInstance, pAppletBase, "frameCount"); + appendField(diag, appletInstance, pAppletBase, "frameRate"); + appendField(diag, appletInstance, pAppletBase, "focused"); + + // Try to get renderer class name from 'g' field (PGraphics) + try { + Field gField = pAppletBase.fieldByName("g"); + if (gField != null) { + Value gValue = appletInstance.getValue(gField); + if (gValue instanceof ObjectReference) { + ObjectReference graphics = (ObjectReference) gValue; + diag.append(" renderer: ").append(graphics.referenceType().name()).append("\n"); + } + } + } catch (Exception e) { + diag.append(" renderer: (unavailable)\n"); + } + + return diag.toString(); + + } catch (Exception e) { + return "Error gathering diagnostics: " + e.toString(); + } finally { + targetVM.resume(); + } + } + + /** + * Helper to append a field value to the diagnostics string. + */ + private static void appendField(StringBuilder sb, ObjectReference obj, ClassType type, String fieldName) { + try { + Field field = type.fieldByName(fieldName); + if (field != null) { + Value value = obj.getValue(field); + sb.append(" ").append(fieldName).append(": "); + if (value == null) { + sb.append("null"); + } else if (value instanceof com.sun.jdi.PrimitiveValue) { + sb.append(value.toString()); + } else if (value instanceof StringReference) { + sb.append(((StringReference) value).value()); + } else { + sb.append(value.toString()); + } + sb.append("\n"); + } + } catch (Exception e) { + sb.append(" ").append(fieldName).append(": (error: ").append(e.getMessage()).append(")\n"); + } + } + + + // /** Print the current stack trace. */ // public synchronized void printStackTrace() { // if (isStarted()) { From 21c2466f9d0adab8d2bb1473e0e967e6308175b4 Mon Sep 17 00:00:00 2001 From: iTut <83431472+iTutFadU@users.noreply.github.com> Date: Tue, 10 Feb 2026 03:23:48 +0300 Subject: [PATCH 29/42] Use `start` instead of 0 for the starting index in `text(char[], int, int, float, float)` (#1257) --- core/src/processing/core/PGraphics.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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); From 821d62c5e43b8e4e72aa2cb4fae56d092ef98c94 Mon Sep 17 00:00:00 2001 From: Stef Tervelde Date: Tue, 10 Feb 2026 01:25:49 +0100 Subject: [PATCH 30/42] Write and load version.properties for plugin (#1423) Add a writeVersion Gradle task that writes project.version to build/resources/main/version.properties, so the plugin can access the project version at runtime. This way the version number of the plugin and the used version of processing.core will be the same. --- java/gradle/build.gradle.kts | 12 ++++++++++++ java/gradle/src/main/kotlin/ProcessingPlugin.kt | 5 ++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/java/gradle/build.gradle.kts b/java/gradle/build.gradle.kts index 0171384f44..b5718a8905 100644 --- a/java/gradle/build.gradle.kts +++ b/java/gradle/build.gradle.kts @@ -38,4 +38,16 @@ publishing{ url = uri(project(":app").layout.buildDirectory.dir("resources-bundled/common/repository").get().asFile.absolutePath) } } +} + +tasks.register("writeVersion") { + // make the version available to the plugin at runtime by writing it to a properties file in the resources directory + doLast { + val file = layout.buildDirectory.file("resources/main/version.properties").get().asFile + file.parentFile.mkdirs() + file.writeText("version=${project.version}") + } +} +tasks.named("processResources") { + dependsOn("writeVersion") } \ No newline at end of file diff --git a/java/gradle/src/main/kotlin/ProcessingPlugin.kt b/java/gradle/src/main/kotlin/ProcessingPlugin.kt index df558710f3..375b17549a 100644 --- a/java/gradle/src/main/kotlin/ProcessingPlugin.kt +++ b/java/gradle/src/main/kotlin/ProcessingPlugin.kt @@ -20,7 +20,10 @@ class ProcessingPlugin @Inject constructor(private val objectFactory: ObjectFact val sketchName = project.layout.projectDirectory.asFile.name.replace(Regex("[^a-zA-Z0-9_]"), "_") val isProcessing = project.findProperty("processing.version") != null - val processingVersion = project.findProperty("processing.version") as String? ?: "4.3.4" + val processingVersion = project.findProperty("processing.version") as String? + ?: javaClass.classLoader.getResourceAsStream("version.properties")?.use { stream -> + java.util.Properties().apply { load(stream) }.getProperty("version") + } ?: "4.3.4" val processingGroup = project.findProperty("processing.group") as String? ?: "org.processing" val workingDir = project.findProperty("processing.workingDir") as String? val debugPort = project.findProperty("processing.debugPort") as String? From 05290b877eb4bf0b2e6946ebb74fe47711c602ad Mon Sep 17 00:00:00 2001 From: Salmane Khalili Date: Tue, 17 Feb 2026 15:44:14 +0000 Subject: [PATCH 31/42] Resource Leaks in processing/app/Util.java (#1436) * Add java tests to build path to put the test in the appropriate directory, id have to java files to grade build configs, because it currently only checks for kotlin tests. * Unit test for rsrc leakage in unzip create a temp zip file > create a destination that is a file not a directory (guaranteed exception) -> unzip throws ioexception because it expects a directory not a file -> catch it -> check if the zip file is still open -> if true == leak. * Add try() to manage opened files/rsrcs * Applying try() to more rsrcs Ive also removed the test since its OS specific, and new code is supposed to be in kotlin. --- app/src/processing/app/Util.java | 88 +++++++++++++++----------------- 1 file changed, 41 insertions(+), 47 deletions(-) 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(); } From 4f2644a9a212c295967e3921365fd2df16bc4687 Mon Sep 17 00:00:00 2001 From: Stef Tervelde Date: Tue, 17 Feb 2026 16:46:38 +0100 Subject: [PATCH 32/42] Publish Processing Gradle plugins (#1405) * Publish Processing Gradle plugin Add a publish-gradle job to the release workflow to publish Processing libraries to the Gradle Plugin Portal using gradle publish with required secrets and version/group env vars. Update gradle/plugins/library/build.gradle.kts to use the com.gradle.plugin-publish plugin, provide plugin metadata (website, vcsUrl, displayName, description, tags) and make the plugin id dynamic ("$group.library"). These changes enable automated publishing of the Gradle plugin with the metadata required by the portal. * Publish plugins in release workflow * Update build.gradle.kts * Set project.group system property for tests Expose the project's group to test JVMs by configuring tasks.withType() to set systemProperty("project.group", group ?: "org.processing"). Update ProcessingPluginTest to read the plugin id from System.getProperty("project.group") instead of hardcoding the group. Also close the publishing block in build.gradle.kts. This allows tests to adapt when the project group is overridden. * Update build.gradle.kts --- .github/workflows/release.yml | 37 +++++++++++++++++++ gradle/plugins/library/build.gradle.kts | 9 ++++- java/gradle/build.gradle.kts | 15 +++++++- .../src/test/kotlin/ProcessingPluginTest.kt | 2 +- 4 files changed, 58 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e8de46389b..e21fce7a40 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -74,6 +74,43 @@ jobs: ORG_GRADLE_PROJECT_version: ${{ needs.version.outputs.version }} 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 }} diff --git a/gradle/plugins/library/build.gradle.kts b/gradle/plugins/library/build.gradle.kts index d2707eef4a..f70338b4ec 100644 --- a/gradle/plugins/library/build.gradle.kts +++ b/gradle/plugins/library/build.gradle.kts @@ -1,12 +1,17 @@ plugins { - `java-gradle-plugin` + id("com.gradle.plugin-publish") version "2.0.0" kotlin("jvm") version "2.2.20" } gradlePlugin { + website = "https://processing.org/" + vcsUrl = "https://github.com/processing/processing4" plugins { create("processing.library") { - id = "org.processing.library" + id = project.properties.getOrElse("publishingGroup", { "org.processing" }).toString() + ".library" + displayName = "Processing Library Plugin" + description = "Gradle plugin for building Processing libraries" + tags = listOf("processing", "library", "dsl") implementationClass = "ProcessingLibraryPlugin" } } diff --git a/java/gradle/build.gradle.kts b/java/gradle/build.gradle.kts index b5718a8905..8680c0a851 100644 --- a/java/gradle/build.gradle.kts +++ b/java/gradle/build.gradle.kts @@ -21,15 +21,20 @@ dependencies{ testImplementation(libs.junit) } -// TODO: CI/CD for publishing the plugin to the Gradle Plugin Portal gradlePlugin{ + website = "https://processing.org/" + vcsUrl = "https://github.com/processing/processing4" plugins{ create("processing.java"){ - id = "org.processing.java" + id = "$group.java" + displayName = "Processing Plugin" + description = "Gradle plugin for building Processing sketches" + tags = listOf("processing", "sketch", "dsl") implementationClass = "org.processing.java.gradle.ProcessingPlugin" } } } + publishing{ repositories{ mavenLocal() @@ -39,6 +44,11 @@ publishing{ } } } +// Grab the group before running tests, since the group is used in the test configuration and may be modified by the publishing configuration +val testGroup = group.toString() +tasks.withType().configureEach { + systemProperty("project.group", testGroup) +} tasks.register("writeVersion") { // make the version available to the plugin at runtime by writing it to a properties file in the resources directory @@ -48,6 +58,7 @@ tasks.register("writeVersion") { file.writeText("version=${project.version}") } } + tasks.named("processResources") { dependsOn("writeVersion") } \ No newline at end of file diff --git a/java/gradle/src/test/kotlin/ProcessingPluginTest.kt b/java/gradle/src/test/kotlin/ProcessingPluginTest.kt index 7ffeeecb58..c67725e993 100644 --- a/java/gradle/src/test/kotlin/ProcessingPluginTest.kt +++ b/java/gradle/src/test/kotlin/ProcessingPluginTest.kt @@ -20,7 +20,7 @@ class ProcessingPluginTest{ val sketchFolder = directory.newFolder("sketch") directory.newFile("sketch/build.gradle.kts").writeText(""" plugins { - id("org.processing.java") + id("${System.getProperty("project.group")}.java") } """.trimIndent()) directory.newFile("sketch/settings.gradle.kts") From 9c3d45d084ddfaec69053b6b9663894266921dcc Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Tue, 17 Feb 2026 10:55:05 -0500 Subject: [PATCH 33/42] docs: add SalmaneKhalili as a contributor for bug (#1438) * docs: update CONTRIBUTORS.md [skip ci] * docs: update .all-contributorsrc [skip ci] --------- Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> --- .all-contributorsrc | 9 +++++++++ CONTRIBUTORS.md | 3 +++ 2 files changed, 12 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 142a5571c6..a2b857d7b9 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1705,6 +1705,15 @@ "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" + ] } ], "repoType": "github", diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index fa161a40d3..3ea24f87b4 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -253,6 +253,9 @@ _Note: due to GitHub's limitations, this repository's [Contributors](https://git Avinash Kumar Deepak
Avinash Kumar Deepak

💻 Sumama Sonia
Sumama Sonia

💻 + + Salmane Khalili
Salmane Khalili

🐛 + From 173f646e3b66d942bd2373bdfade195d9f374f57 Mon Sep 17 00:00:00 2001 From: Avinash Kumar Deepak <152387616+avinxshKD@users.noreply.github.com> Date: Thu, 19 Feb 2026 22:43:46 +0530 Subject: [PATCH 34/42] fix portable settings.path not respected for prefs and language (#1439) --- app/src/processing/app/Preferences.kt | 2 +- app/src/processing/app/ui/theme/Locale.kt | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/processing/app/Preferences.kt b/app/src/processing/app/Preferences.kt index ae2f18c482..4e139b08e1 100644 --- a/app/src/processing/app/Preferences.kt +++ b/app/src/processing/app/Preferences.kt @@ -80,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/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) From cd49b184fe3d8f3c1f1f302f61e891e2491c4d65 Mon Sep 17 00:00:00 2001 From: Avinash Kumar Deepak <152387616+avinxshKD@users.noreply.github.com> Date: Fri, 20 Feb 2026 23:15:59 +0530 Subject: [PATCH 35/42] Publish Serial and IO libraries to Maven Central (#1421) * serial: use jssc from maven instead of bundled jar * io: port from ant to gradle, add maven publishing * move io and net from legacy to regular library build * revert serial changes for separate pr * serial: add maven publishing, keep bundled jssc jar --- java/build.gradle.kts | 4 +- java/libraries/io/build.gradle.kts | 93 +++++++++++++++++++++++++- java/libraries/serial/build.gradle.kts | 51 +++++++++++++- 3 files changed, 144 insertions(+), 4 deletions(-) diff --git a/java/build.gradle.kts b/java/build.gradle.kts index d5d306a2e0..7c26a200f4 100644 --- a/java/build.gradle.kts +++ b/java/build.gradle.kts @@ -68,7 +68,7 @@ tasks.register("copyCore"){ into(coreProject.layout.projectDirectory.dir("library")) } -val legacyLibraries = arrayOf("io","net") +val legacyLibraries = emptyArray() legacyLibraries.forEach { library -> tasks.register("library-$library-extraResources"){ val build = project(":java:libraries:$library").tasks.named("build") @@ -88,7 +88,7 @@ legacyLibraries.forEach { library -> } } -val libraries = arrayOf("dxf", "pdf", "serial", "svg") +val libraries = arrayOf("dxf", "io", "net", "pdf", "serial", "svg") libraries.forEach { library -> val name = "create-$library-library" diff --git a/java/libraries/io/build.gradle.kts b/java/libraries/io/build.gradle.kts index a176f03df7..f6af4b2b13 100644 --- a/java/libraries/io/build.gradle.kts +++ b/java/libraries/io/build.gradle.kts @@ -1 +1,92 @@ -ant.importBuild("build.xml") \ No newline at end of file +import com.vanniktech.maven.publish.SonatypeHost + +plugins { + java + alias(libs.plugins.mavenPublish) +} + +sourceSets { + main { + java { + srcDirs("src") + } + } +} + +repositories { + mavenCentral() +} + +dependencies { + compileOnly(project(":core")) +} + +tasks.register("createLibrary") { + dependsOn("jar") + into(layout.buildDirectory.dir("library")) + + from(layout.projectDirectory) { + include("library.properties") + include("examples/**") + } + + from(configurations.runtimeClasspath) { + into("library") + } + + from(tasks.jar) { + into("library") + rename { "io.jar" } + } + + from(layout.projectDirectory.dir("library")) { + include("linux-arm64/**") + include("linux-armv6hf/**") + include("linux32/**") + include("linux64/**") + into("library") + } +} + +publishing { + repositories { + maven { + name = "App" + url = uri(project(":app").layout.buildDirectory.dir("resources-bundled/common/repository").get().asFile.absolutePath) + } + } +} + +mavenPublishing { + coordinates("$group.core", name, version.toString()) + publishToMavenCentral(SonatypeHost.CENTRAL_PORTAL, automaticRelease = true) + + signAllPublications() + + pom { + name.set("Processing IO") + description.set("Processing IO") + url.set("https://processing.org") + licenses { + license { + name.set("LGPL") + url.set("https://www.gnu.org/licenses/lgpl-2.1.html") + } + } + developers { + developer { + id.set("steftervelde") + name.set("Stef Tervelde") + } + developer { + id.set("benfry") + name.set("Ben Fry") + } + } + scm { + url.set("https://github.com/processing/processing4") + connection.set("scm:git:git://github.com/processing/processing4.git") + developerConnection.set("scm:git:ssh://git@github.com/processing/processing4.git") + } + } +} \ No newline at end of file diff --git a/java/libraries/serial/build.gradle.kts b/java/libraries/serial/build.gradle.kts index f7f1ff4136..652b1b398e 100644 --- a/java/libraries/serial/build.gradle.kts +++ b/java/libraries/serial/build.gradle.kts @@ -1,5 +1,8 @@ +import com.vanniktech.maven.publish.SonatypeHost + plugins { java + alias(libs.plugins.mavenPublish) } sourceSets { @@ -23,15 +26,61 @@ dependencies { tasks.register("createLibrary") { dependsOn("jar") into(layout.buildDirectory.dir("library")) + from(layout.projectDirectory) { include("library.properties") include("examples/**") } + from(configurations.runtimeClasspath) { into("library") } + from(tasks.jar) { into("library") rename { "serial.jar" } } -} \ No newline at end of file +} + +publishing { + repositories { + maven { + name = "App" + url = uri(project(":app").layout.buildDirectory.dir("resources-bundled/common/repository").get().asFile.absolutePath) + } + } +} + +mavenPublishing { + coordinates("$group.core", name, version.toString()) + publishToMavenCentral(SonatypeHost.CENTRAL_PORTAL, automaticRelease = true) + + signAllPublications() + + pom { + name.set("Processing Serial") + description.set("Processing Serial") + url.set("https://processing.org") + licenses { + license { + name.set("LGPL") + url.set("https://www.gnu.org/licenses/lgpl-2.1.html") + } + } + developers { + developer { + id.set("steftervelde") + name.set("Stef Tervelde") + } + developer { + id.set("benfry") + name.set("Ben Fry") + } + } + scm { + url.set("https://github.com/processing/processing4") + connection.set("scm:git:git://github.com/processing/processing4.git") + developerConnection.set("scm:git:ssh://git@github.com/processing/processing4.git") + } + } +} From 56b4108f69d5b177d659d074c405265a69da5b7f Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 20 Feb 2026 12:48:07 -0500 Subject: [PATCH 36/42] docs: add nickmcintyre as a contributor for test (#1442) * docs: update CONTRIBUTORS.md [skip ci] * docs: update .all-contributorsrc [skip ci] --------- Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> --- .all-contributorsrc | 9 +++++++++ CONTRIBUTORS.md | 1 + 2 files changed, 10 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index a2b857d7b9..bac55054d3 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1714,6 +1714,15 @@ "contributions": [ "bug" ] + }, + { + "login": "nickmcintyre", + "name": "Nick McIntyre", + "avatar_url": "https://avatars.githubusercontent.com/u/3719176?v=4", + "profile": "https://mcintyre.io", + "contributions": [ + "test" + ] } ], "repoType": "github", diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 3ea24f87b4..f163cf9256 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -255,6 +255,7 @@ _Note: due to GitHub's limitations, this repository's [Contributors](https://git Salmane Khalili
Salmane Khalili

🐛 + Nick McIntyre
Nick McIntyre

⚠️ From dcad827456cec483d9f5524ce0e20475f5a8e6d8 Mon Sep 17 00:00:00 2001 From: txandmj <92069790+txandmj@users.noreply.github.com> Date: Fri, 20 Feb 2026 09:53:44 -0800 Subject: [PATCH 37/42] add unit tests for IntList (#1102) Co-authored-by: Moon --- core/test/processing/data/IntListTest.java | 492 +++++++++++++++++++++ 1 file changed, 492 insertions(+) create mode 100644 core/test/processing/data/IntListTest.java 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 source = new ArrayList<>(Arrays.asList(1, "2", null, 4.5, -1)); + IntList testedList = new IntList(source); + assertEquals(5, testedList.size()); + + int[] expected = {1, 2, 0, 4, -1}; + assertArrayEquals(expected, testedList.values()); + } + @Test + public void testConstructorWithObject() { + String eleStr = "Hello"; + int eleInt = 10; + float eleFloat = 1.2f; + Object eleObj = new Object(); + + IntList testedList = new IntList(eleStr, eleInt, eleFloat, eleObj); + + int[] expected = {0, 10, 1, 0}; + assertArrayEquals(expected, testedList.values()); + } + @Test + public void testFromRangeWithStopIndex() { + IntList originalList = new IntList(new int[]{5,10,15,20,25}); + IntList result = originalList.fromRange(2); + assertArrayEquals(new int[]{0,1}, result.values()); + } + @Test + public void testFromRangeWithStartAndStopIndex() { + IntList originalList = new IntList(new int[]{5,10,15,20,25}); + IntList result = originalList.fromRange(1,3); + assertArrayEquals(new int[]{1,2}, result.values()); + } + @Test + public void testClear() { + IntList testedList = new IntList(new int[]{1, 2, 3}); + testedList.clear(); + assertEquals(0, testedList.size()); + } + @Test + public void testResize() { + IntList testedList = new IntList(new int[]{1, 2, 3}); + testedList.resize(5); + assertEquals(5, testedList.size()); + assertEquals(5, testedList.data.length); + } + @Test + public void testSet() { + IntList testedList = new IntList(); + testedList.set(0, 20); + assertEquals(1, testedList.size()); + assertEquals(20, testedList.get(0)); + + testedList.set(100, 2000); + assertEquals(101, testedList.size()); + assertEquals(2000, testedList.get(100)); + } + @Test + public void testPush() { + IntList testedList = new IntList(); + testedList.push(100); + assertEquals(1, testedList.size()); + assertEquals(100, testedList.get(0)); + } + @Test + public void testAppendWithInt() { + IntList testedList = new IntList(); + testedList.append(100); + assertEquals(1, testedList.size()); + assertEquals(100, testedList.get(0)); + } + @Test + public void testAppendWithIntArray() { + IntList testedList = new IntList(); + int[] source = {10, 20, 30}; + testedList.append(source); + assertArrayEquals(source, testedList.values()); + } + @Test + public void testAppendWithIntList() { + IntList testedList = new IntList(); + IntList source = new IntList(new int[]{10, 20, 30}); + testedList.append(source); + assertArrayEquals(source.values(), testedList.values()); + } + @Test + public void testAppendUnique() { + IntList testedList = new IntList(new int[]{10, 20, 30}); + testedList.appendUnique(100); + assertArrayEquals(new int[]{10, 20, 30, 100}, testedList.values()); + } + + @Test + public void testPop() { + IntList testedList = new IntList(new int[]{10, 20}); + + assertEquals(20,testedList.pop()); + assertEquals(1,testedList.size()); + assertEquals(10,testedList.pop()); + assertEquals(0,testedList.size()); + } + + @Test + public void testPopOnEmptyIntListThrowsException() { + IntList testedList = new IntList(); + RuntimeException exception = assertThrows(RuntimeException.class, () -> { + testedList.pop(); + }); + + assertEquals("Can't call pop() on an empty list", exception.getMessage()); + } + + @Test + public void testRemoveWithIndexGreaterThanSize() { + IntList testedList = new IntList(); + assertThrows(ArrayIndexOutOfBoundsException.class, + () -> testedList.remove(3)); + } + + @Test + public void testRemoveWithIndexLessThanZeroThrowsException() { + IntList testedList = new IntList(); + assertThrows(ArrayIndexOutOfBoundsException.class, + () -> testedList.remove(-1)); + } + + @Test + public void testRemoveWithIndex() { + IntList testedList = new IntList(new int[]{1, 2, 3}); + assertEquals(2,testedList.remove(1)); + assertEquals(2,testedList.size()); + assertArrayEquals(new int[]{1, 3},testedList.values()); + } + @Test + public void testRemoveValue() { + IntList testedList = new IntList(new int[]{10, 20, 20}); + assertEquals(1,testedList.removeValue(20)); + assertEquals(-1,testedList.removeValue(100)); + } + @Test + public void testRemoveValues() { + IntList testedList = new IntList(new int[]{10, 20, 20}); + assertEquals(2,testedList.removeValues(20)); + } + + @Test + public void testInsertWithInvalidIndexThrowsException() { + IntList testedList = new IntList(); + + ArrayIndexOutOfBoundsException exception = assertThrows(ArrayIndexOutOfBoundsException.class, + () -> testedList.insert(1, 20)); + + IllegalArgumentException negativeIndex = assertThrows(IllegalArgumentException.class, + () -> testedList.insert(-1, 20)); + assertEquals("insert() index cannot be negative: it was -1", negativeIndex.getMessage()); + } + + @Test + public void testInsertWithValidIndex() { + IntList testedList = new IntList(); + testedList.insert(0, 10); + assertEquals(10, testedList.get(0)); + } + + @Test + public void testInsertWithArray() { + IntList testedList = new IntList(new int[]{5, 5, 5}); + int[] source = {100, 200}; + testedList.insert(1, source); + int[] expectedList = new int[]{5, 100, 200, 5, 5}; + assertArrayEquals(expectedList, testedList.values()); + } + + @Test + public void testInsertWithIntList() { + IntList testedList = new IntList(new int[]{5, 5, 5}); + IntList source = new IntList(new int[]{100, 200}); + testedList.insert(1, source); + int[] expectedList = new int[]{5, 100, 200, 5, 5}; + assertArrayEquals(expectedList, testedList.values()); + } + + @Test + public void testIndex() { + IntList testedList = new IntList(new int[]{5, 5, 5}); + assertEquals(0,testedList.index(5)); + assertEquals(-1,testedList.index(10)); + } + + @Test + public void testHasValue() { + IntList testedList = new IntList(new int[]{1, 2, 3}); + assertTrue(testedList.hasValue(3)); + assertFalse(testedList.hasValue(100)); + } + + @Test + public void testIncrementWithIndex() { + IntList testedList = new IntList(new int[] {20}); + testedList.increment(0); + assertEquals(21,testedList.get(0)); + } + @Test + public void testIncrementWithIndexGreaterThanSize() { + IntList testedList = new IntList(new int[] {20}); + testedList.increment(5); + assertEquals(1,testedList.get(5)); + } + @Test + public void testAddWithValidIndex() { + IntList testedList = new IntList(new int[] {20}); + testedList.add(0, 20); + assertEquals(40,testedList.get(0)); + } + + @Test + public void testAddWithInvalidIndexThrowsException() { + IntList testedList = new IntList(new int[] {20}); + ArrayIndexOutOfBoundsException exception = assertThrows(ArrayIndexOutOfBoundsException.class, + ()->testedList.add(5, 20)); + + assertEquals("The list size is 1. You cannot add() to element 5.",exception.getMessage()); + } + @Test + public void testSubWithValidIndex() { + IntList testedList = new IntList(new int[] {20}); + testedList.sub(0, 20); + assertEquals(0,testedList.get(0)); + } + + @Test + public void testSubWithInvalidIndexThrowsException() { + IntList testedList = new IntList(new int[] {20}); + ArrayIndexOutOfBoundsException exception = assertThrows(ArrayIndexOutOfBoundsException.class, + ()->testedList.sub(5, 20)); + + assertEquals("The list size is 1. You cannot sub() to element 5.",exception.getMessage()); + } + @Test + public void testMultWithValidIndex() { + IntList testedList = new IntList(new int[] {20}); + testedList.mult(0, 20); + assertEquals(400,testedList.get(0)); + } + + @Test + public void testMultWithInvalidIndexThrowsException() { + IntList testedList = new IntList(new int[] {20}); + ArrayIndexOutOfBoundsException exception = assertThrows(ArrayIndexOutOfBoundsException.class, + ()->testedList.mult(5, 20)); + + assertEquals("The list size is 1. You cannot mult() to element 5.",exception.getMessage()); + } + @Test + public void testDivWithValidIndex() { + IntList testedList = new IntList(new int[] {20}); + testedList.div(0, 20); + assertEquals(1,testedList.get(0)); + } + + @Test + public void testDivWithInvalidIndexThrowsException() { + IntList testedList = new IntList(new int[] {20}); + ArrayIndexOutOfBoundsException exception = assertThrows(ArrayIndexOutOfBoundsException.class, + ()->testedList.div(5, 20)); + + assertEquals("The list size is 1. You cannot div() to element 5.",exception.getMessage()); + } + @Test + public void testMinWithValidIndex() { + IntList testedList = new IntList(new int[] {20, 10, -5}); + assertEquals(-5, testedList.min()); + } + + @Test + public void testMinWithInvalidIndexThrowsException() { + IntList testedList = new IntList(); + RuntimeException exception = assertThrows(RuntimeException.class, + ()->testedList.min()); + + assertEquals("Cannot use min() on an empty IntList.",exception.getMessage()); + } + @Test + public void testMinIndexWithValidIndex() { + IntList testedList = new IntList(new int[] {20, 10, -5}); + assertEquals(2, testedList.minIndex()); + } + + @Test + public void testMinIndexWithInvalidIndexThrowsException() { + IntList testedList = new IntList(); + RuntimeException exception = assertThrows(RuntimeException.class, + ()->testedList.minIndex()); + + assertEquals("Cannot use minIndex() on an empty IntList.",exception.getMessage()); + } + + @Test + public void testMaxWithValidIndex() { + IntList testedList = new IntList(new int[] {20, 10, -5}); + assertEquals(20, testedList.max()); + } + + @Test + public void testMaxWithInvalidIndexThrowsException() { + IntList testedList = new IntList(); + RuntimeException exception = assertThrows(RuntimeException.class, + ()->testedList.max()); + + assertEquals("Cannot use max() on an empty IntList.",exception.getMessage()); + } + + @Test + public void testMaxIndexWithValidIndex() { + IntList testedList = new IntList(new int[] {20, 10, -5}); + assertEquals(0, testedList.maxIndex()); + } + + @Test + public void testMaxIndexWithInvalidIndexThrowsException() { + IntList testedList = new IntList(); + RuntimeException exception = assertThrows(RuntimeException.class, + ()->testedList.maxIndex()); + + assertEquals("Cannot use maxIndex() on an empty IntList.",exception.getMessage()); + } + @Test + public void testSumLong() { + IntList testedList = new IntList(new int[] {20, 10, -5}); + assertEquals(25, testedList.sumLong()); + } + @Test + public void testSumWithValidIntegerValue() { + IntList testedList = new IntList(new int[] {20, 10, -5}); + assertEquals(25, testedList.sum()); + } + @Test + public void testSumGreaterThanMaxIntegerValueThrowsException() { + int value = Integer.MAX_VALUE; + IntList testedList = new IntList(new int[] {value, 1}); + RuntimeException exception = assertThrows(RuntimeException.class, + ()->testedList.sum()); + + assertEquals("sum() exceeds 2147483647, use sumLong()",exception.getMessage()); + } + @Test + public void testSumLessThanMinIntegerValueThrowsException() { + int value = Integer.MIN_VALUE; + IntList testedList = new IntList(new int[] {value, -1}); + RuntimeException exception = assertThrows(RuntimeException.class, + ()->testedList.sum()); + + assertEquals("sum() less than -2147483648, use sumLong()",exception.getMessage()); + } + @Test + public void testSort() { + IntList testedList = new IntList(new int[] {20, 10, -5}); + testedList.sort(); + assertArrayEquals(new int[]{-5,10,20}, testedList.values()); + } + @Test + public void testSortReverse() { + IntList testedList = new IntList(new int[] {20, 10, 100}); + testedList.sortReverse(); + assertArrayEquals(new int[]{100,20,10}, testedList.values()); + } + @Test + public void testReverse() { + IntList testedList = new IntList(new int[] {20, 10, 100}); + testedList.reverse(); + assertArrayEquals(new int[]{100,10,20}, testedList.values()); + } + @Test + public void testChoice() { + IntList testedList = new IntList(new int[] {20, 10, 100}); + int num = testedList.choice(); + assertTrue(testedList.hasValue(num)); + } + @Test + public void testChoiceOnEmptyIntListThrowsException() { + IntList testedList = new IntList(); + ArrayIndexOutOfBoundsException exception = assertThrows(ArrayIndexOutOfBoundsException.class, + ()->testedList.choice()); + assertEquals("No entries in this IntList", exception.getMessage()); + } + @Test + public void testRemoveChoice() { + IntList testedList = new IntList(new int[] {20, 10, 100}); + int num = testedList.removeChoice(); + assertEquals(2, testedList.size()); + } + + @Test + public void testRemoveChoiceOnEmptyIntListThrowsException() { + IntList testedList = new IntList(); + ArrayIndexOutOfBoundsException exception = assertThrows(ArrayIndexOutOfBoundsException.class, + ()->testedList.removeChoice()); + assertEquals("No entries in this IntList", exception.getMessage()); + } + @Test + public void testCopy() { + IntList originalList = new IntList(new int[] {20, 10, 100}); + IntList copyList = originalList.copy(); + assertArrayEquals(originalList.values(), copyList.values()); + assertEquals(originalList.size(), copyList.size()); + } + @Test + public void testToArray() { + IntList originalList = new IntList(new int[] {20, 10, 100}); + int[] result = originalList.toArray(); + assertArrayEquals(new int[] {20, 10, 100}, result); + } + @Test + public void testToArrayWithDestinationArray() { + IntList originalList = new IntList(new int[] {20, 10, 100}); + int[] oversize = new int[100]; + int[] result = originalList.toArray(oversize); + assertArrayEquals(new int[] {20, 10, 100}, result); + assertEquals(3, result.length); + } + @Test + public void testToArrayWithDestinationArrayNull() { + IntList originalList = new IntList(new int[] {20, 10, 100}); + int[] initialIsNull = null; + int[] result = originalList.toArray(initialIsNull); + assertArrayEquals(new int[] {20, 10, 100}, result); + assertEquals(3, result.length); + } + @Test + public void testGetPercent() { + IntList originalList = new IntList(new int[] {5, 10, 5}); + FloatList result = originalList.getPercent(); + assertArrayEquals(new float[] {0.25f, 0.5f, 0.25f}, result.values(), 1e-6f); + } + @Test + public void testGetSubset() { + IntList originalList = new IntList(new int[] {5, 10, 20}); + IntList result = originalList.getSubset(1); + assertArrayEquals(new int[]{10, 20}, result.values()); + } + @Test + public void testJoin() { + IntList originalList = new IntList(new int[] {5, 10}); + assertEquals("5&10", originalList.join("&")); + } + @Test + public void testJoinWithEmptyList() { + IntList originalList = new IntList(); + assertEquals("", originalList.join("&")); + } +} From d8767d95b81e4b3970e1adc3b043cc82e74b8ce9 Mon Sep 17 00:00:00 2001 From: Rishab Kumar Jha Date: Fri, 20 Feb 2026 23:25:42 +0530 Subject: [PATCH 38/42] Added unit tests for `PMatrix3D` in `processing:core` (#1029) * added unit tests for Pmatrix 3D * added unit tests for Pmatrix 3D --- core/test/processing/core/PMatrix3DTest.java | 226 +++++++++++++++++++ 1 file changed, 226 insertions(+) create mode 100644 core/test/processing/core/PMatrix3DTest.java 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); + } +} From 779a8afd6eb941ab69a37531eafefb40c1c4a42e Mon Sep 17 00:00:00 2001 From: Rishab Kumar Jha Date: Fri, 20 Feb 2026 23:27:55 +0530 Subject: [PATCH 39/42] added important PImage unit tests (#1049) --- core/test/processing/core/PImageTest.java | 317 ++++++++++++++++++++++ 1 file changed, 317 insertions(+) create mode 100644 core/test/processing/core/PImageTest.java 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 From e22a4fbe6088829c076642c34922f8664e0238e7 Mon Sep 17 00:00:00 2001 From: Salmane Khalili Date: Tue, 24 Feb 2026 15:11:59 +0000 Subject: [PATCH 40/42] Bug/androidcrash (#1447) * Add java tests to build path to put the test in the appropriate directory, id have to java files to grade build configs, because it currently only checks for kotlin tests. * Unit test for rsrc leakage in unzip create a temp zip file > create a destination that is a file not a directory (guaranteed exception) -> unzip throws ioexception because it expects a directory not a file -> catch it -> check if the zip file is still open -> if true == leak. * Add try() to manage opened files/rsrcs * Applying try() to more rsrcs Ive also removed the test since its OS specific, and new code is supposed to be in kotlin. * conditional for setting decorated only when the frame is displayable * type * Applying dispose before frame is displayable Moved the undecorated setup to the very beginning of the Editor constructor before any operations that could make the frame displayable happened. --- app/src/processing/app/ui/Editor.java | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/app/src/processing/app/ui/Editor.java b/app/src/processing/app/ui/Editor.java index d5d964b164..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(); From 9ceb607f5c5c194c6a68ca0610be094df7901b95 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 2 Mar 2026 09:01:36 -0500 Subject: [PATCH 41/42] docs: add Ebaron96 as a contributor for bug (#1454) * docs: update CONTRIBUTORS.md [skip ci] * docs: update .all-contributorsrc [skip ci] --------- Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> --- .all-contributorsrc | 9 +++++++++ CONTRIBUTORS.md | 1 + 2 files changed, 10 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index bac55054d3..23d63737fe 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1723,6 +1723,15 @@ "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/CONTRIBUTORS.md b/CONTRIBUTORS.md index f163cf9256..6dafe55e47 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -256,6 +256,7 @@ _Note: due to GitHub's limitations, this repository's [Contributors](https://git Salmane Khalili
Salmane Khalili

🐛 Nick McIntyre
Nick McIntyre

⚠️ + Elijah Baron
Elijah Baron

🐛 From 420687440c1441e1e81ee401bdf8bef12d9fc5a7 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 2 Mar 2026 09:01:55 -0500 Subject: [PATCH 42/42] docs: add Rishab87 as a contributor for code, and test (#1443) * docs: update CONTRIBUTORS.md [skip ci] * docs: update .all-contributorsrc [skip ci] --------- Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> --- .all-contributorsrc | 3 ++- CONTRIBUTORS.md | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.all-contributorsrc b/.all-contributorsrc index 23d63737fe..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" ] }, { diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 6dafe55e47..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
Benjamin Fox

💻 e1dem
e1dem

💻 Aditya Chaudhary
Aditya Chaudhary

💻 - Rishab Kumar Jha
Rishab Kumar Jha

💻 + Rishab Kumar Jha
Rishab Kumar Jha

💻 ⚠️ Yehia Rasheed
Yehia Rasheed

💻