Skip to content

New ThinkDifferent method activateSketchWindow() #912

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jan 14, 2025

Conversation

hx2A
Copy link
Collaborator

@hx2A hx2A commented Jan 12, 2025

The purpose of this PR is to add new methods to the ThinkDifferent class to facilitate giving the Sketch window focus with macOS native function calls. This method will be used by py5 when opening Sketch windows.

Currently the ThinkDifferent class has this native method:

static native public void activateIgnoringOtherApps();

The existing comment says that processing.py uses the method. That method, however, is a JNI call to a native macOS function that is now deprecated:

https://developer.apple.com/documentation/appkit/nsapplication/activate(ignoringotherapps:)

The macOS documentation says that we are to use a different macOS function:

https://developer.apple.com/documentation/appkit/nsapplication/activate()

The function activateIgnoringOtherApps was added in macOS 10. The activate function was added in macOS 14.

Calling a native macOS function that does not exist in the user's version of macOS results in a nasty crash. Since some day that deprecated function will be removed but the replacement was not added until macOS 14, I created a new static method, activateSketchWindow(), that determines the macOS version number and makes the appropriate JNI call. It returns a boolean value to indicate if the call was made successfully.

You can test this PR with the following code:

import processing.core.ThinkDifferent;

void setup() {
  size(200, 200);
}

void draw() {
  if (frameCount % 60 == 0) {
    println(ThinkDifferent.activateSketchWindow());
    println("Boo!");
  }
}

After the Sketch window opens, move another window on top. The Sketch window will reappear.

I tested this on macOS 12 and 15 laptops and it works correctly on both. Also works with OpenGL renderers.

I know PSurface also has a method setAlwaysOnTop() to move a window to the front, but that forces it to the front and keeps it there. Calling setAlwaysOnTop(true) and then setAlwaysOnTop(false) isn't a good way to code and seems to have other side effects.

The latest version of py5 makes a call to the deprecated method activateIgnoringOtherApps() but this new method activateSketchWindow() will be a better choice moving forward.

@Stefterv
Copy link
Collaborator

Hey Jim! Just tested it and it works. I had trouble recompiling the binaries when I tried could you explain to me how you did it?
Ideally I would like the recompiling to be part of the build system in the future so if that is something your interested in adding that would be awesome

@hx2A
Copy link
Collaborator Author

hx2A commented Jan 12, 2025

I had trouble recompiling the binaries when I tried could you explain to me how you did it?

Sure, @Stefterv ! On a macOS machine (Sequoia, M1 chip) I moved to the directory with the code and used the make command:

cd core/different/
make

I imagine this cannot run correctly on a non-macOS machine, but I didn't try it.

I'll also point out that objective c and native macOS calls are all new to me. The initial research for this PR relied heavily on AI, which in this case did a good job.

Ideally I would like the recompiling to be part of the build system in the future so if that is something your interested in adding that would be awesome

I did notice that recompiling this is not a part of the build and that running make had to be done manually. If you'd like to add it to the build system, I can certainly help with that.

But first, are you sure this should be in the build system? If this piece can only compile on macOS, I don't want to complicate contributing for non-macOS folks. Also, there must be a reason why this is set up the way it is, where the Makefile must be run separately and then that libDifferent.jnilib gets written to another directory where it can be picked up by the build and included in core.jar.

Copy link
Collaborator

@Stefterv Stefterv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay that seems straight forward to do!

Yes, of course this would not work on a non macOS machine. Maybe it's not a requirement for compiling core but at least it is setup as a separate gradle & ant task and it has a CI/CD component to it.

I'd like to have that set up because not having automated system runs the risk of losing the capability of completely building Processing from scratch if underlying requirements change.

@hx2A
Copy link
Collaborator Author

hx2A commented Jan 12, 2025

Yes, of course this would not work on a non macOS machine. Maybe it's not a requirement for compiling core but at least it is setup as a separate gradle & ant task and it has a CI/CD component to it.

I'd like to have that set up because not having automated system runs the risk of losing the capability of completely building Processing from scratch if underlying requirements change.

OK, that makes sense. I don't know how to set this up though. I see in core/build.xml there is a copy command so that libDifferent.jnilib is included in core.jar. Should there be something added in there to check if it is being run on macOS and if so run make? What else needs to be done?

@Stefterv
Copy link
Collaborator

Thinking about it, since the build.xml is on its way out, maybe you can just add it to the core/build.gradle.kts as a gradle task.

A helpful doc
https://docs.gradle.org/current/dsl/org.gradle.api.tasks.Exec.html

@hx2A
Copy link
Collaborator Author

hx2A commented Jan 12, 2025

Here's my first attempt at making a gradle task for this:

task compileThinkDifferent(type:Exec) {
  workingDir './different'

  commandLine '/usr/bin/make'

  standardOutput = new ByteArrayOutputStream()

  ext.output = {
    return standardOutput.toString()
  }
}

Is it close? It seems like there needs to be more in core/build.gradle.kts for that to do anything though.

@Stefterv
Copy link
Collaborator

Yep! Thank you! Only change would be to write in Gradle Kotlin rather than Groovy.

Yeah it is really straight forward, I'm not sure what would trigger it at this stage, since core is not yet getting build on macOS in the GitHub Actions, this is a change we could make In the core release action or we could create a separate action that triggers on a file change within the core/different folder

@hx2A
Copy link
Collaborator Author

hx2A commented Jan 14, 2025

Here's my attempt at converting that to Gradle Kotlin, which I will admit required some assistance from AI and is my first Kotlin code ever:

tasks.register<Exec>("compileThinkDifferent") {
    workingDir = File("./different") 
    commandLine = "/usr/bin/make"
    standardOutput = ByteArrayOutputStream()

    extensions.create("output", String::class.java) {
        return@create standardOutput.toString()
    }
}

I did go through some Kotlin and Gradle tutorials in attempt to get an idea of what this about. The three lines of code that set the properties make sense to me and seem consistent with what is in the documentation link you sent. The last bit with extensions.create is confusing, but I know it is trying to provide a way to make the output stored in the ByteArrayOutputStream object accessible outside of the task. Does it look right to you?

@Stefterv
Copy link
Collaborator

Thanks! I have thought a bit more about this idea, see the new issue I created. Let's continue the conversation there.

This PR can be merged as far as I am concerned as the CI question is not a blocker

@Stefterv Stefterv requested a review from SableRaf January 14, 2025 09:48
@hx2A
Copy link
Collaborator Author

hx2A commented Jan 14, 2025

OK, @Stefterv ! Sounds good to me. And I'll comment in the other issue you just created.

@Stefterv Stefterv merged commit 342a6eb into processing:main Jan 14, 2025
6 checks passed
@hx2A hx2A deleted the thinkdifferent-activate branch January 14, 2025 16:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants