Skip to content
Advertisement

Platform specific code in Eclipse

We’re developing a java desktop/Eclipse RCP application with a graph-layout component. We faced issues with the GUI scaling on newer(8, 8.1, 10) versions on windows, it appeared that one can only retrieve the scaling factor through win32 api calls, so we added a little DLL that does this and call it through JNI. Works fine on windows, works fine in maven on linux/osx since the class is never instantiated there.

The problem is that to get the scaling factor, we need the window handle, which we retrieve like this:

public float getScale(GC gc) {
  return getScale(gc.getGCData().hwnd);
}

where GC is an org.eclipse.swt.graphics.GC and the following call is to the dll. However, that lines gives me a compilation error when debugging on linux, since that object doesn’t have a hwnd variable. How do I solve this in a cleaner way without compilation errors?

Advertisement

Answer

You can use a plug-in fragment, as Greg sugested. A fragment can have a platform filter which prevents it from being loaded when the current platform does not match the filter expression.

A platform filter for Windws 32bit would look like this:

Eclipse-PlatformFilter: (& (osgi.ws=win32) (osgi.os=win32) (osgi.arch=x86))

A fragment must also specify a host bundle. At runtime, the fragments resources are merged into the class space of the host bundle

Fragment-Host: the.parent.bundle

To avoid compilation errors, you would have an interface or abstract class in your host bundle and two fragments with different implementations for Windows and Linux.

The Windows implementation could safely access the hwnd field and the Linux implementation could return a value indicating that scaling is not available/fixed.

For example, the host bundle would have a ScaleProvider:

class abstract ScaleProvider {

  ScaleProvider getInstance() {
    String className = ScaleProvider.class.getName() + "Impl";
    try {
      Class<?> implClass = ScaleProvider.class.getClassLoader().loadClass( className );
      return ( ScaleProvider )implClass.newInstance();
    } catch( ClassNotFoundException | InstantiationException | IllegalAccessException e ) {
      throw new RuntimeException( e );
    }
  }

  abstract float getScale( Gc gc );
}

For brevity, the implementation loader and the interface description are in one type. The getInstance() code expects a class named ScaleProviderImpl to be present in the same package.

The ScaleProviderImpl class is provided by the fragment that matches the current platform and would have a class

class ScaleProviderImpl extends ScaleProvider {
  float getScale( Gc gc ) {
    // platform-specific implementation
  }
}

This example assumes that there is always a matching platform specific fragment present. But you could of course change the getInstance() code to return a default implementation if no ScaleProviderImpl can be found.

Advertisement