gpt4 book ai didi

java - XLib:使用正确翻译的弹出窗口来代替Java窗口

转载 作者:太空宇宙 更新时间:2023-11-04 13:36:26 27 4
gpt4 key购买 nike

我有一个可以捕获X窗口并将其作为子窗口托管的应用程序。
它以某种方式实现了一个非常基本的窗口管理器。没有窗口管理器正在运行。
除Java应用程序外,效果很好。

例如,如果Java应用程序具有菜单或弹出窗口,则这些弹出窗口的位置不正确。
我已经用一个非常简单的单元测试来复制了它。
Java应用程序是带有菜单栏和菜单的基本JFrame

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JMenuBar;

public class test extends JFrame {
private static test window;
public test() {
initialize();
}

/**
* @param args
*/
public static void main(String[] args) {
System.out.println("Hello world");
EventQueue.invokeLater(new Runnable() {

@Override
public void run() {
window = new test();
window.setVisible(true);
}
});
}

/**
* Initialize the contents of the frame.
*/
private void initialize() {
setTitle("ResizeTest");

setBounds(100, 100, 888, 439);
setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
getContentPane().setLayout(new BorderLayout(0, 0));

JMenuBar menuBar = new JMenuBar();
getContentPane().add(menuBar, BorderLayout.NORTH);

JMenu fileMenu = new JMenu("File");
fileMenu.add("Open");
fileMenu.add("Close");
menuBar.add(fileMenu);
add(menuBar);

addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
dispose();
System.exit(0);
}
});

JPanel panel = new JPanel();
panel.add(new JLabel("Hello world"));
getContentPane().add(panel, BorderLayout.SOUTH);

pack();
}
}


然后另一个单元测试(在C中):

/*
Simple Xlib application drawing a box in a window.
To Compile: gcc -O2 -Wall -o test test.c -L /usr/X11R6/lib -lX11 -lm
*/
#include<X11/Xlib.h>
#include<stdio.h>
#include<stdlib.h> // prevents error for exit on line 18 when compiling with gcc

#include <X11/X.h>
#include <X11/Xatom.h>
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/Shell.h>

int WINDOW_ID;

void processExistingWindows(Display *display, Window parentWindow) {

Window *children;
Window parent;
Window root;
unsigned int nchildren;
printf("Entering processExistingWindows\n");

Window rootWindow = XDefaultRootWindow(display);
int result = XQueryTree(display, rootWindow, &root, &parent, &children, &nchildren);
printf("XQueryTree result is %d\n", result);
unsigned int windowCount = 0;
printf("Iterating through %d windows\n", nchildren);
for (windowCount = 0; result && windowCount < nchildren; windowCount++) {
Window currentWindow = children[windowCount];
if ((int)currentWindow == WINDOW_ID) {
int reparentResult = XReparentWindow(display, currentWindow, parentWindow, 0, 0);
printf("XReparentWindow result: %d\n", reparentResult);
printf("Reparented window: %x into %x\n", (int)currentWindow, (int)parentWindow);
}
}
if (result && children != NULL) {
XFree((char *) children);
}
}

int main(int argc, char** argv) {
Display *d;
int s;
Window w;
XEvent e;

if (argc < 2) {
printf("Please give the window ID you want to reparent\n");
exit(1);
}
WINDOW_ID = atoi(argv[1]);
printf("Will try to reparent the Window: %x \n", WINDOW_ID);

printf("Forcing synchronous calls to the X Server\n");
_Xdebug = 1; // To allow proper debugging by forcing synchronous calls to the X Server

/* open connection with the server */
d=XOpenDisplay(NULL);
if(d==NULL) {
printf("Cannot open display\n");
exit(1);
}
s=DefaultScreen(d);

/* create window */
w = XCreateSimpleWindow(d, RootWindow(d, s), 0, 0, 400, 400, 1,
BlackPixel(d, s), WhitePixel(d, s));

printf("Current window ID is: %x\n", (int) w);

// Process Window Close Event through event handler so XNextEvent does Not fail
Atom delWindow = XInternAtom( d, "WM_DELETE_WINDOW", 0 );
XSetWMProtocols(d , w, &delWindow, 1);

/* select kind of events we are interested in */
XSelectInput(d, w, ExposureMask | KeyPressMask | StructureNotifyMask );

/* map (show) the window */
XMapWindow(d, w);

processExistingWindows(d, w);
/* event loop */
printf("Starting the loop\n");
XMoveWindow(d, w, 100, 200);

while(1) {
XNextEvent(d, &e);
/* draw or redraw the window */
if(e.type==Expose) {
XFillRectangle(d, w, DefaultGC(d, s), 20, 20, 10, 10);
}
/* exit on key press */
if(e.type==KeyPress)
break;

// Handle Windows Close Event
if(e.type==ClientMessage)
break;
}

/* destroy our window */
XDestroyWindow(d, w);

/* close connection to server */
XCloseDisplay(d);

return 0;
}


只需启动Java应用程序。
它会正确显示,并且菜单会显示预期的位置。

抓取窗口ID(使用 xwininfo -root -tree -int),并以该窗口ID作为参数启动单元测试,以在基本窗口中重新显示它。
Java框架已正确移动并重新放置在窗口中,但是菜单仍在先前的JFrame位置弹出!

就像JFrame相对于其新的父窗口正确地重新定位了其原点,但是菜单/弹出窗口未考虑到此新位置。

这很奇怪。
如果不是执行XReparentWindow而是执行XMoveWindow,则菜单会出现在应该显示的位置。

我用谷歌搜索了这个问题,没有任何东西可以带来任何明确的解决方案。
关于JVM中的切片窗口管理器的硬编码支持,有很多讨论,而OpenJDK显然解决了这一问题。但是我所采用的版本都没有起作用(Java 6、7、8和OpenJDK 7)。

尽管在我看来,它看起来像是JDK中的一个普通错误(我以这种方式托管了其他基于C / C ++的应用程序,可以与菜单/弹出窗口很好地配合使用),但使用窗口管理器(如FVWM)运行的事实证明了它可以工作。
我还查看了 twm中的实现,它看起来没有什么不同。

有没有人遇到(并解决)这个问题?

谢谢!



编辑

经过数小时的调试,它的确来自Java及其如何处理窗口管理器(或不存在)。该代码有趣的部分在XDecoratedPeer.java和XWM.java中。

Java会尝试猜测正在运行的窗口管理器,并会基于此窗口管理器调整其行为(并非所有人都以相同的方式对待客户端应用程序)。在我们的例子中,没有窗口管理器在运行,因此Java将默认为No / Other WM。

一旦启动,Java应用程序将永远不会重新检查窗口管理器的更改。因此,在主机内部我们无法“伪造”任何特定的WM来更改来宾的行为,因为为时已晚。

在没有WM的情况下,Java仍然会对收到的各种X事件做出反应(单击,移动,重设……),但是期望事件的序列非常严格。

在我们的例子中,ReparentNotifyEvent不足以触发对JVM中窗口位置的完全重新计算。因此,在从主机重新定向之后,来宾仍然认为它在其以前的位置。这不是立即可见的,因为所有窗口都是由X Server相对于彼此绘制的,但是所有弹出窗口(以及所有Java直接创建的窗口,即设置了override_redirect标志)将在错误的位置绘制。

Java需要重新计算其屏幕坐标的是ConfigureNotifyEvent(包含绝对屏幕坐标)。

但是,此事件必须具有两个特征:
 -不能合成(即通过XSendEvent()调用显式发送)
 -具有与ReparentNotify事件不同的序列。这有点棘手,除了在XReparentWindow和XSendEvent之间插入sleep(1)外,我不知道该怎么做。 XFlush(),XSync(),XNoOp()等无法使用,因为它们没有创建新的请求,因此不会增加序列号。

我研究过的所有Java版本都具有几乎相同的行为。
无论如何,通过此更改,Java客户端可以正确地重新计算其屏幕位置,并且现在可以正确显示弹出窗口。

最佳答案

所以我终于设法使它起作用。

以下是需要执行的操作序列:

// We need to move the child window before reparenting it to avoid some nasty offsets
XMoveWindow(display, childWindowId, 0, 0);

// Do the reparenting
XReparentWindow(display, childWindowId, parentWindowId, 0, 0);

// Ask the XServer to take ownership back if we die
XFixesChangeSaveSet(display, childWindowId, SetModeInsert, SaveSetRoot, SaveSetUnmap);

// We have to explicitly notify the Java child of its location change.
XEvent client_event;
XWindowAttributes childAttributes;
XWindowAttributes parentAttributes;
XGetWindowAttributes(display, childWindowId, &childAttributes);
XGetWindowAttributes(display, parentWindowId, &parentAttributes);
WindowDimension windowDecorationSize = // Your decoration if applicable

client_event.type = ConfigureNotify;
client_event.xconfigure.send_event = True;
client_event.xconfigure.display = display;
client_event.xconfigure.event = childWindowId;
client_event.xconfigure.window = childWindowId ;
client_event.xconfigure.x = parentAttributes.x + windowDecorationSize.width;
client_event.xconfigure.y = parentAttributes.y + windowDecorationSize.height;
client_event.xconfigure.width = childAttributes.width;
client_event.xconfigure.height = childAttributes.height;
client_event.xconfigure.border_width = 0;
client_event.xconfigure.above = None;
client_event.xconfigure.override_redirect = True; // Set to true to filter the event out in the processing of the parent Java frame

XSendEvent(display, childWindowId, False, StructureNotifyMask, &client_event);

关于java - XLib:使用正确翻译的弹出窗口来代替Java窗口,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31646544/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com