问题
Trying to port to Ubuntu
my SWT
application that works fine in Windows
, I have encountered an issue with overlay scrollbars
. When I run the application from under Eclipse
, everything is OK, scrollbars look and behave normally. But when I pack the application into a JAR
and execute this JAR
, the scrollbars in the application will become "overlay scrollbars" and look like thin orange strips, and the scrollbar events, that I need to handle, are supposed to have some strange field values, so that my app totally ignore them. This is unacceptable for me, it's important for my app to handle the scrollbars in a special, non-standard way, so I need normal look of the scrollbars and normal events from them.
This issue can easily be mended by adding export LIBOVERLAY_SCROLLBAR=0
to the ~/.profile
, but I don't want to force the end users to make custom system settings in order to run my application, I want them to be able to run it just by clicking on the JAR without any special efforts.
I'm new in programming for Linux, and I dont't know how to set up the environment programmatically. Placing
System.setProperty("LIBOVERLAY_SCROLLBAR", "0");
in the beginning of my code has no effect, neither has
new ProcessBuilder("export LIBOVERLAY_SCROLLBAR=0").start();
How do I disable this "overlay scrollbars" from my code?
P.S. Solutions for any other language perhaps may be a clue, too
回答1:
Changing an environment variable of the current process (your java program) is difficult and may not always work. What you can do though, is distribute a shell script with your jar that people on Linux can use to start your application. Something like this should do the trick:
#!/bin/sh
export LIBOVERLAY_SCROLLBAR=0
java -jar yourjar.jar
回答2:
There exist ways to set environment variables for the currently running VM, 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 at How do I set environment variables from Java?)
Yet in my case I need the env vars to affect the libraries that are executed outside the VM, so this method does not solve my problem.
After thinking a little I have realized that I want to set environment for the parent process of the JVM, so I need first to set the required variables and then recursively run another JVM instance that would execute my app -- then the variables would affect the libraries even though their code is executed outside the VM.
So the logic should be as the following:
if (required vars are absent) {
start a process that {
set required vars;
run another instance of the JVM with the application inside;
}
exit;
}
// here the vars already set
do whatever we need in the proper environment
As it comes to Java, the code may 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
}
}
In case of running the jar from a shell script, we have to wait for the child process to finish, before exiting the original process, so this solution leads to an overhead of running two instances of JVM simultaneously. If there is no need to provide a possibility to run it from a script, the p.waitFor(12, TimeUnit.HOURS);
may be replaced with p.waitFor(12, TimeUnit.MILLISECONDS);
or, perhaps, removed at all (I have not tested without it), so we can have a single instance of JVM, like with a usual Java program.
A working snippet with a text
widget and a scrollbar
is at http://ideone.com/eRjePQ
来源:https://stackoverflow.com/questions/42048599/how-to-disable-overlay-scrollbars-programmatically-ubuntu-java