Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I properly call NSOpenPanel from a JNI method to run on the proper thread?

I can get my JNI class to only partially execute due to my unfamiliarity with Objective-C and JNF. What I've tried so far is as follows:

Here's Open.java, which an MCV for a more significant Java Swing app that deserves a better-looking better-featured open file dialog than unreasonable java.awt.FileDialog or the ugly and less-featured javax.swing.JFileChooser.

import java.awt.event.*;
import javax.swing.*;
public class Open extends JFrame {
   public Open () {
      getContentPane().add(new JButton(new AbstractAction() {
         public void actionPerformed (ActionEvent e) {
            try {
               NativeOpenFileDialog.run();
            } catch (Throwable ex) {
               System.err.println(ex.getMessage());
            }
         }
      }));
      pack();
      setVisible(true);
   }
   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() { new Open(); }
      });
   }
}

Here's NativeOpenFileDialog.java

import javax.swing.*;

public class NativeOpenFileDialog {
   static {
       System.loadLibrary("natopndlg");
   };
   public static native void run ();
   public static void main (String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() { NativeOpenFileDialog.run(); }
      });

   }
}

Here's NativeOpenFileDialog.m

#import <Cocoa/Cocoa.h>

#include <jni.h>
#include "NativeOpenFileDialog.h"

JNIEXPORT void JNICALL Java_NativeOpenFileDialog_run (JNIEnv *thisEnv, jclass jcls) {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    NSOpenPanel *panel = [NSOpenPanel openPanel];
    [panel setCanChooseFiles:YES];
    [panel setCanChooseDirectories:YES];
    [panel setAllowsMultipleSelection:YES];
    if ([panel runModal] != NSModalResponseOK)
        NSLog(@"User did not press OK");
    [pool release];
}

And here's the makefile (I'm using Xcode 15.2):

SDK = /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk

Open.class: Open.java NativeOpenFileDialog.java NativeOpenFileDialog.m
    javac -h . NativeOpenFileDialog.java
    javac Open.java
    gcc -dynamiclib -o libnatopndlg.dylib -framework Cocoa -isysroot $(SDK) -I $(JAVA_HOME)/include -I $(JAVA_HOME)/include/darwin -fobjc-exceptions -std=c99 NativeOpenFileDialog.m

And here's how I execute it after executing make.

java Open

Clicking the button raises the exception:

NSInternalInconsistencyException', reason: 'NSWindow should only be instantiated on the main thread!

Which is understandable knowing that the panel is supposed to be opened on the UI thread. I believe all I have to do is make sure the window created by NSOpenPanel is a child of the JFrame and opened on the UI thread. And I believe that at leasat means I would have to execute a Java function to get the NSWindow of the JFrame.

like image 627
Jeff Holt Avatar asked Oct 31 '25 10:10

Jeff Holt


1 Answers

You can execute things on the UI thread using dispatch_async() and dispatch_sync(). NSObject also includes a method performSelectorOnMainThread:withObject:waitUntilDone: that will perform a selector on the main thread.

This is a gist using the Java objective-c bridge that does something similar to this:

https://gist.github.com/anonymous/3966989

For your specific problem you could wrap the method body in dispatch_async

JNIEXPORT void JNICALL Java_NativeOpenFileDialog_run (JNIEnv *thisEnv, jclass jcls) {
dispatch_async(dispatch_get_main_queue(), ^{
  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  NSOpenPanel *panel = [NSOpenPanel openPanel];
  [panel setCanChooseFiles:YES];
  [panel setCanChooseDirectories:YES];
  [panel setAllowsMultipleSelection:YES];
  if ([panel runModal] != NSModalResponseOK)
      NSLog(@"User did not press OK");
  [pool release];
});

}

Of course, this just opens the dialog and doesn't do anything with the file that the user selects, so you'll need to build on this.

like image 135
steve hannah Avatar answered Nov 02 '25 00:11

steve hannah