I'm trying to port my application on SWT to Ubuntu , and I ran into a problem with the so-called. "overlay scrollbars" . When the program runs from under Eclipse , everything is fine, but when it starts from the JAR , the scrollbars become "overlay scrollbars" - they turn into thin orange bars. At the same time, the events from the scrollbars also change (apparently, some other values ​​in stateMask or in detail ), and the program stops processing them (ignores). For my program, such scrollbars are absolutely not suitable (even if I figured out the events), I need traditional scrollbars and I need to handle their events in a special way.

It is easy to correct this behavior - it’s enough to add export LIBOVERLAY_SCROLLBAR=0 to ~/.profile , but this is not a good option - I need to let any user start the program by simply sticking the JAR file without needing any special settings on the system , so I'm looking for a way to do the same from my code. I am new to Linux programming, and apparently I don’t understand how to solve such problems programmatically. Tried to run SWT before loading

  System.setProperty("LIBOVERLAY_SCROLLBAR", "0"); 

and

  new ProcessBuilder("export LIBOVERLAY_SCROLLBAR=0").start(); 

but neither one nor the other does not. In this case, setting the variable SWT_GTK3=0 in this way

  System.setProperty("SWT_GTK3","0"); 

obviously triggered, i.e. gives the expected effect. Perhaps the fact is that LIBOVERLAY_SCROLLBAR works only when the user logs in, and SWT_GTK3 - at any time, if only it was already installed by the time SWT was loaded.

How can I overcome these "overlay scrollbars" from the program code? PS Working solutions for other languages ​​may help to find a solution for JAVA as well - the problem is not JAVA-specific.

  • In English, SO is suggested to be run from a script in which the environment variable is set in advance, but this is clear without SO, I am looking for a more elegant solution. - m. vokhm

1 answer 1

There are ways to set environment variables for a running JVM instance (under which our program runs), for example like this:

  private static void setEnv(Map<String, String> newEnv) throws Exception { Map<String, String> env = System.getenv(); Class<?> cl = env.getClass(); Field field = cl.getDeclaredField("m"); field.setAccessible(true); @SuppressWarnings("unchecked") Map<String, String> envMap = (Map<String, String>) field.get(env); envMap.putAll(newEnv); } 

(the idea is taken from the answers to https://stackoverflow.com/questions/318239/how-do-i-set-environment-variables-from-java )

However, in my case, I need to set variables for libraries whose code does not work under the JVM (namely, for GTK), so this method does not solve my problem.

By some thought, I realized that I want to set the environment variables for the parent process of the JVM, so I need to set the environment variables first, and then run the JVM that will execute my program - then these variables will affect the libraries even then when their code is executed not from under the JVM.

So the logic should be some such:

  if (нужных переменных нет) { запустить процесс, который { установит нужные переменные; запустит второй экземпляр JVM с моей программой; } выйти; } // здесь переменные будут уже установлены делаем всё то, что хотели 

On Java, the code might look like this:

 public class SecondVM { public static void main(String[] args) { if ( System.getenv("SWT_GTK3") == null || System.getenv("LIBOVERLAY_SCROLLBAR") == null ) { URL classResource = SecondVM.class.getResource("SecondVM.class"); boolean fromJar = classResource.getProtocol().equals("rsrc"); String exePath = ClassLoader.getSystemClassLoader().getResource(".").getPath(); exePath = new File(exePath).getAbsolutePath().replaceFirst("\\.$", "").replaceFirst("bin$", ""); if (!exePath.endsWith(System.getProperty("file.separator"))) exePath += System.getProperty("file.separator"); String[] script = { "/bin/bash", "-c", "export SWT_GTK3=0; " + "export LIBOVERLAY_SCROLLBAR=0; " + (fromJar? // TODO: Put the proper paths, packages and class names here "java -jar " + exePath + "SecondVM.jar" : // if runs from jar "java -cp ./bin/:../ExtLibs/swt_linux64/swt.jar " // if runs from under Eclipse or somewhat alike + "com.m_v.test.SecondVM") }; try { Process p = new ProcessBuilder(script).start(); // When jar is run from a bash script, it kills the second VM when exits. // Let it has some time to take a breath p.waitFor(12, TimeUnit.HOURS); } catch (Exception e) { e.printStackTrace(); } System.exit(0); } // Now the env vars are OK. We can use SWT with normal scrollbars Display display = Display.getDefault(); // .... do watever we need } } 

When running a JAR file from a shell script, you have to wait for the nested process to finish before leaving the first one, so this solution leads to overhead in the form of two concurrent JVM instances. If it is not necessary to provide the ability to run from a script, you can replace p.waitFor(12, TimeUnit.HOURS); on p.waitFor(12, TimeUnit.MILLISECONDS); or maybe remove it altogether (I haven't tried it), so a single instance of JVM will work for us, like when running a regular Java program.

A working code example with a text field ( text widget - how in Russian?) And a scrollbar is on http://ideone.com/eRjePQ