如何以编程方式禁用覆盖滚动条(UbuntuJava)



试图将在Windows中运行良好的SWT应用程序移植到Ubuntu时,遇到了overlay scrollbars问题。当我在Eclipse下运行应用程序时,一切正常,滚动条看起来和行为正常。但是,当我将应用程序打包到一个JAR中并执行这个JAR时,应用程序中的滚动条将变成"覆盖滚动条">,看起来像橙色的细条,而我需要处理的滚动条事件应该有一些奇怪的字段值,所以我的应用程序完全忽略了它们。这对我来说是不可接受的,我的应用程序以一种特殊的、非标准的方式处理滚动条很重要,所以我需要滚动条的正常外观和它们的正常事件。

通过将export LIBOVERLAY_SCROLLBAR=0添加到~/.profile中,可以很容易地修复这个问题,但我不想强迫最终用户为了运行我的应用程序而进行自定义系统设置,我希望他们只需点击JAR即可运行它,而无需付出任何特殊的努力。

我是Linux编程的新手,不知道如何以编程方式设置环境。放置

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

在我的代码开头没有效果,也没有

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

如何从代码中禁用此"覆盖滚动条">

附言:任何其他语言的解决方案可能也是一条线索。

更改当前进程(java程序)的环境变量很困难,而且可能并不总是有效。不过,您可以使用jar分发一个shell脚本,Linux上的用户可以使用它来启动您的应用程序。像这样的东西应该可以做到:

#!/bin/sh
export LIBOVERLAY_SCROLLBAR=0
java -jar yourjar.jar

有一些方法可以为当前运行的VM设置环境变量,例如:

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);
}

(这个想法取自"如何从Java设置环境变量?">

然而,在我的情况下,我需要env-var来影响在VM之外执行的库,所以这种方法不能解决我的问题。

经过思考,我意识到我想为JVM的父进程设置环境,所以我需要首先设置所需的变量,然后递归地运行另一个执行我的应用程序的JVM实例——然后这些变量会影响库,即使它们的代码是在VM外执行的。

因此,逻辑应该如下:

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

对于Java,代码可能如下所示:

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 
}
}

在从shell脚本运行jar的情况下,我们必须等待子进程完成,然后才能退出原始进程,因此此解决方案会导致同时运行JVM的两个实例的开销。如果不需要提供从脚本中运行它的可能性,p.waitFor(12, TimeUnit.HOURS);可能会被p.waitFor(12, TimeUnit.MILLISECONDS);取代,或者可能会被完全删除(我还没有测试过没有它的情况),所以我们可以拥有JVM的单个实例,就像普通的Java程序一样。

带有text小部件和scrollbar的工作片段位于http://ideone.com/eRjePQ

最新更新