如何使透明 JavaFX 阶段对 Linux X11 的鼠标事件透明?

问题描述 投票:0回答:1

我有一个透明的舞台,当用户在该舞台内单击时,我需要将鼠标事件传递到 JavaFX 舞台后面的窗口(例如,如果我的舞台后面有一个文本编辑器,则应将鼠标事件传递到那个编辑器窗口)。换句话说,我需要舞台对鼠标事件是透明的。

这是我的代码:

public class Test12 extends Application {

    @Override
    public void start(Stage primaryStage) {
        var vBox = new VBox();
        vBox.setStyle("-fx-border-width: 1; -fx-border-color: black");
        vBox.setMouseTransparent(true);
        var scene = new Scene(vBox, 400, 300);
        scene.setFill(Color.TRANSPARENT);
        scene.getStylesheets().add(this.getClass().getResource("test.css").toExternalForm());
        primaryStage.initStyle(StageStyle.TRANSPARENT);
        primaryStage.setScene(scene);
        primaryStage.show();
        primaryStage.setAlwaysOnTop(true);

    }

    public static void main(String[] args) {
        launch(args);
    }
}

CSS

.root { -fx-background-color: transparent; }

如果可能的话,谁能告诉我该怎么做。

更新
这是一个 C 程序,使窗口的右半部分对鼠标事件透明(使用

gcc -o program program.c -lX11 -lXext
):

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <stdio.h>
#include <X11/extensions/shape.h>

/* ----------------------------------------------------------------------------------------------- */
#define _NET_WM_STATE_REMOVE        0    // remove/unset property
#define _NET_WM_STATE_ADD           1    // add/set property
#define _NET_WM_STATE_TOGGLE        2    // toggle property

Bool makeAlwaysOnTop(Display* display,  Window mywin) {
    int screen = DefaultScreen(display);
    Window root = RootWindow(display, screen);
    Atom wmStateAbove = XInternAtom( display, "_NET_WM_STATE_ABOVE", 1 );
    if( wmStateAbove != None ) {
        printf( "_NET_WM_STATE_ABOVE has atom of %ld\n", (long)wmStateAbove );
    } else {
        printf( "ERROR: cannot find atom for _NET_WM_STATE_ABOVE !\n" );
        return False;
    }

    Atom wmNetWmState = XInternAtom( display, "_NET_WM_STATE", 1 );
    if( wmNetWmState != None ) {
        printf( "_NET_WM_STATE has atom of %ld\n", (long)wmNetWmState );
    } else {
        printf( "ERROR: cannot find atom for _NET_WM_STATE !\n" );
        return False;
    }

    if( wmStateAbove != None )
    {
        XClientMessageEvent xclient;
        memset( &xclient, 0, sizeof (xclient) );
        xclient.type = ClientMessage;
        xclient.window = mywin;
        xclient.message_type = wmNetWmState;
        xclient.format = 32;
        xclient.data.l[0] = _NET_WM_STATE_ADD;
        xclient.data.l[1] = wmStateAbove;
        xclient.data.l[2] = 0;
        xclient.data.l[3] = 0;
        xclient.data.l[4] = 0;
        XSendEvent( display,
          root,
          False,
          SubstructureRedirectMask | SubstructureNotifyMask,
          (XEvent *)&xclient );
        XFlush(display);
        return True;
    }
    return False;
}

/* ----------------------------------------------------------------------------------------------- */

void makeHalfTransparent(Display* dpy,  Window win) {
    Pixmap shape = XCreatePixmap(dpy, win, 400, 300, 1);
    GC gc = XCreateGC(dpy, shape, 0, NULL);
    XSetFillStyle(dpy, gc, FillSolid);
    XSetForeground(dpy, gc, 0);
    XFillRectangle(dpy, shape, gc, 0, 0, 400, 300);
    XSetForeground(dpy, gc, 1);
    XFillRectangle(dpy, shape, gc, 0, 0, 200, 300);
    XShapeCombineMask(dpy, win, ShapeBounding, 0, 0, shape, ShapeSet);
    XFreeGC(dpy, gc);
}

/* ----------------------------------------------------------------------------------------------- */

int main() {
    Display *dpy;
    Window win;

    XEvent event;
    int screen;

    dpy = XOpenDisplay(NULL);
    if (dpy == NULL) {
        fprintf(stderr, "Cannot open display\n");
        return 1;
    }

    screen = DefaultScreen(dpy);
    win = XCreateSimpleWindow(dpy, RootWindow(dpy, screen), 10, 10, 400, 300, 1, BlackPixel(dpy, screen), WhitePixel(dpy, screen));
    XSelectInput(dpy, win, ExposureMask | KeyPressMask);

    XMapWindow(dpy, win);
    makeAlwaysOnTop(dpy, win);
    makeHalfTransparent(dpy, win);

    while (1) {
        XNextEvent(dpy, &event);
        if (event.type == KeyPress) {
            break;
        }
    }

    XDestroyWindow(dpy, win);
    XCloseDisplay(dpy);
    return 0;
}

这是我的 JavaFX 应用程序,使用 JNA 必须使窗口的右半部分对鼠标事件透明(换句话说,我将

makeHalfTransparent
函数从 C 移植到 Java):

import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.platform.unix.X11;
import com.sun.jna.ptr.PointerByReference;
import java.lang.reflect.Method;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.stage.Window;
import javafx.stage.StageStyle;


public class JavaFxTest10 extends Application {

    public static interface CustomX11 extends Library {

        CustomX11 INSTANCE = (CustomX11) Native.load("X11", CustomX11.class);

        X11.Display XOpenDisplay(String display_name);

        int XFreeGC(X11.Display display, X11.GC gc);

        void XSetForeground(X11.Display display, X11.GC gc, int foreground);

        X11.Pixmap XCreatePixmap(X11.Display display, X11.Window w, int width, int height, int depth);

        void XFillRectangle(X11.Display display, X11.Pixmap drawable, X11.GC gc, int x, int y, int width, int height);

        X11.GC XCreateGC(X11.Display display, X11.Pixmap drawable, int valuemask, X11.XGCValues values);

        void XFreePixmap(X11.Display display, X11.Pixmap pixmap);

        int XFetchName(X11.Display display, Window w, PointerByReference windowName);

        X11.Window XDefaultRootWindow(X11.Display display);

        X11.Window XCreateSimpleWindow(X11.Display display, X11.Window rootWindow, int a, int b, int c, int d, int e, int h, int g);

        X11.Atom XInternAtom(X11.Display display, String atom_name, boolean only_if_exists);

        void XChangeProperty(X11.Display display, X11.Window window, X11.Atom property, X11.Atom type, int format, int mode, Pointer data, int nelements);

        void XSetFillStyle(X11.Display display, X11.GC gc, int fillStyle);

        void XFillRectangle(X11.Display display, X11.Drawable drawable, X11.GC gc, int x, int y, int width, int height);
    }

    public interface Xext extends Library {

       Xext INSTANCE = Native.load("Xext", Xext.class);

       void XShapeCombineMask(X11.Display display, X11.Window w, int shapeOp, int x, int y, X11.Pixmap shape, int shapeKind);
    }

    @Override
    public void start(Stage primaryStage) {
        var button = new Button("Push Me");
        button.setOnAction(e -> {
            var p = getWIndowPoiter(primaryStage);
            var window = getWindow(p);
            printWindowTitle(window);//checking pointer
            makeHalfTransparent(window, (int) primaryStage.getWidth(), (int) primaryStage.getHeight());
        });
        var hBox = new HBox(button);
        hBox.setStyle("-fx-background-color: green");
        var content = new VBox();
        content.setMouseTransparent(true);
        VBox.setVgrow(content, Priority.ALWAYS);

        var mainBox = new VBox(hBox, content);
        mainBox.setStyle("-fx-border-width: 1; -fx-border-color: black");
        var scene = new Scene(mainBox, 400, 300);
        scene.setFill(Color.TRANSPARENT);
        scene.getStylesheets().add(this.getClass().getResource("test.css").toExternalForm());
        primaryStage.initStyle(StageStyle.TRANSPARENT);
        primaryStage.setScene(scene);
        primaryStage.show();
        primaryStage.setAlwaysOnTop(true);
        primaryStage.setTitle("JafaFX Window 777");
    }

    private Pointer getWIndowPoiter(Stage stage) {
        try {
            final Method getPeer = Window.class.getDeclaredMethod("getPeer", null);
            getPeer.setAccessible(true);
            final Object tkStage = getPeer.invoke(stage);
            final Method getRawHandle = tkStage.getClass().getMethod("getRawHandle");
            getRawHandle.setAccessible(true);
            var l = (Long) getRawHandle.invoke(tkStage);
            final Pointer pointer = new Pointer(l);
            System.out.println(pointer);
            return pointer;
        } catch (Exception ex) {
            System.out.println(ex.getMessage());
            ex.printStackTrace();
            return null;
        }
    }

    private X11.Window getWindow( Pointer windowPointer) {
        X11.Window window = new X11.Window(Pointer.nativeValue(windowPointer));
        return window;
    }

    private void printWindowTitle(X11.Window window) {
        final X11 x11 = X11.INSTANCE;
        X11.Display display = x11.XOpenDisplay(null);
        PointerByReference windowNameReference = new PointerByReference();
        int status = X11.INSTANCE.XFetchName(display, window, windowNameReference);

        if (status == 1) {
            Pointer windowNamePointer = windowNameReference.getValue();
            String windowTitle = windowNamePointer.getString(0);
            System.out.println("Window Title: " + windowTitle);
        } else {
            System.out.println("Failed to fetch window name.");
        }
    }

    public static final int FillSolid = 0;
    public static final int ShapeBounding = 0;
    public static final int ShapeSet = 0;

    private void makeHalfTransparent(X11.Window win, int width, int height) {
        X11.Display display = CustomX11.INSTANCE.XOpenDisplay(null);
        //Pixmap shape = XCreatePixmap(dpy, win, 400, 300, 1);
        X11.Pixmap shape = CustomX11.INSTANCE.XCreatePixmap(display, win, width, height, 1);
        //GC gc = XCreateGC(dpy, shape, 0, NULL);
        X11.GC gc2 = CustomX11.INSTANCE.XCreateGC(display, shape, 0, null);
        //XSetFillStyle(dpy, gc, FillSolid);
        CustomX11.INSTANCE.XSetFillStyle(display, gc2, FillSolid);
        //XSetForeground(dpy, gc, 0);
        CustomX11.INSTANCE.XSetForeground(display, gc2, 0);
        //XFillRectangle(dpy, shape, gc, 0, 0, 400, 300);
        CustomX11.INSTANCE.XFillRectangle(display, shape, gc2, 0, 0, width, height);
        //XSetForeground(dpy, gc, 1);
        CustomX11.INSTANCE.XSetForeground(display, gc2, 1);
        //XFillRectangle(dpy, shape, gc, 0, 0, 200, 300);
        CustomX11.INSTANCE.XFillRectangle(display, shape, gc2, 0, 0, width / 2, height);
        //XShapeCombineMask(dpy, win, ShapeBounding, 0, 0, shape, ShapeSet);
        Xext.INSTANCE.XShapeCombineMask(display, win, ShapeBounding, 0, 0, shape, ShapeSet);
        //XFreeGC(dpy, gc);
        CustomX11.INSTANCE.XFreeGC(display, gc2);
    }

    public static void main(String[] args) {
        launch(args);
    }
}

但它在 JavaFX 中不起作用。谁能帮助我使 Linux X11 的 JavaFX 窗口对鼠标事件透明?

java javafx x11 jna
1个回答
0
投票

不确定这种解决方法是否适合您的用例,但您可以通过暂时最小化窗口、使用 java fx Robot 单击鼠标并最大化窗口来实现此效果。这可以通过事件处理程序来完成,如下所示:

    Robot robot = new Robot();
    scene.setOnMouseClicked(event -> {
        primaryStage.setIconified(true);
        robot.mousePress(MouseButton.PRIMARY);
        robot.mouseRelease(MouseButton.PRIMARY);
        primaryStage.setIconified(false);
    });
© www.soinside.com 2019 - 2024. All rights reserved.