Как перетащить мышку с платформой JNA win32 - PullRequest
0 голосов
/ 05 сентября 2018

Я бы хотел перетащить мышь через окно из Java-программы. Например, для панорамирования карты в Google Maps.

Я предполагал, что смогу сделать это с помощью нажатия мыши, движения мыши и затем отпускания мыши,

wc.pressMouse();
wc.moveMouse(x, y);
wc.releaseMouse();

Однако, это просто перемещает указатель, а не экран.

Есть идеи, что я делаю не так?

Вот два файла, один для контроллера JFrame и один для класса JNA.

Используйте клавиши со стрелками для перемещения и цифровые клавиши для изменения перемещаемых пикселей. Для этого вам понадобятся карты Google, открытые в Google Chrome.

package uk.co.moonsit.windows;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.*;

public class KeyMouseMove extends JFrame
    implements KeyListener,
    ActionListener {

private static final Logger LOG = Logger.getLogger(KeyMouseMove.class.getName());

JTextArea displayArea;
JTextField typingArea;
static final String NEWLINE = System.getProperty("line.separator");
WindowsComms wc;
int x = 600, y = 200;
int xshift = 10, yshift = 10;
static String appName = "KeyMouseMove";
//String winName = "Windows Task Manager";
String winName = "Google Maps - Google Chrome";
//String winName = "Viewpoint Control - Google Chrome";
int location;

public static void main(String[] args) {
    /* Use an appropriate Look and Feel */
    try {
        //UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
        //UIManager.setLookAndFeel("com.sun.java.swing.plaf.gtk.GTKLookAndFeel");
        UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel");
    } catch (UnsupportedLookAndFeelException | IllegalAccessException | InstantiationException | ClassNotFoundException ex) {
        ex.printStackTrace();
    }
    /* Turn off metal's use of bold fonts */
    UIManager.put("swing.boldMetal", Boolean.FALSE);

    //Schedule a job for event dispatch thread:
    //creating and showing this application's GUI.
    javax.swing.SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            createAndShowGUI();
        }
    });
}

/**
 * Create the GUI and show it. For thread safety, this method should be
 * invoked from the event-dispatching thread.
 */
private static void createAndShowGUI() {
    //Create and set up the window.
    KeyMouseMove frame = new KeyMouseMove(appName);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    //Set up the content pane.
    frame.addComponentsToPane();

    //Display the window.
    frame.pack();
    frame.setVisible(true);
}

private void setupWindows() {

    wc = new WindowsComms();
    wc.findWindow(winName);
    wc.setForegroundWindow();
    wc.moveMouse(x, y);

}

private void addComponentsToPane() {
    setupWindows();
    JButton button = new JButton("Clear");
    button.addActionListener(this);

    typingArea = new JTextField(40);
    typingArea.addKeyListener(this);

    //Uncomment this if you wish to turn off focus
    //traversal.  The focus subsystem consumes
    //focus traversal keys, such as Tab and Shift Tab.
    //If you uncomment the following line of code, this
    //disables focus traversal and the Tab events will
    //become available to the key event listener.
    //typingArea.setFocusTraversalKeysEnabled(false);
    displayArea = new JTextArea();
    displayArea.setEditable(false);
    JScrollPane scrollPane = new JScrollPane(displayArea);
    scrollPane.setPreferredSize(new Dimension(375, 250));

    getContentPane().add(typingArea, BorderLayout.PAGE_START);
    getContentPane().add(scrollPane, BorderLayout.CENTER);
    getContentPane().add(button, BorderLayout.PAGE_END);
}

public KeyMouseMove(String name) {
    super(name);
}

/**
 * Handle the key typed event from the text field.
 * @param e
 */
@Override
public void keyTyped(KeyEvent e) {
    displayInfo(e, "KEY TYPED: ");
    char c = e.getKeyChar();
    LOG.log(Level.INFO, "char {0}", c);

    switch (location) {
        case KeyEvent.KEY_LOCATION_STANDARD:
            xshift = c - 48;
            break;
        case KeyEvent.KEY_LOCATION_NUMPAD:
            yshift = c - 48;
            break;
        default:
    }

    LOG.info("dx dy " + xshift + " " + yshift);

}

/**
 * Handle the key pressed event from the text field.
 * @param e
 */
@Override
public void keyPressed(KeyEvent e) {
    displayInfo(e, "KEY PRESSED: ");
    if (e.getKeyCode()<=40){
        move(e);
    }
    location = e.getKeyLocation();
}

/**
 * Handle the key released event from the text field.
 * @param e
 */
@Override
public void keyReleased(KeyEvent e) {
    displayInfo(e, "KEY RELEASED: ");
}

private void move(KeyEvent e) {
    int keyCode = e.getKeyCode();
    wc.findWindow(winName);

    switch (keyCode) {
        case 37:
            x = x - xshift;
            break;
        case 38:
            x = x + xshift;
            y = y - yshift;
            break;
        case 39:
            x = x + xshift;
            break;
        case 40:
            y = y + yshift;
            break;
    }
    wc.pressMouse();
    wc.moveMouse(x, y);
    wc.releaseMouse();
    LOG.info("x y " + x + " " + y);
    wc.findWindow(appName);
    wc.setWindowFocus();
    wc.moveMouse(10, 10);

}

/**
 * Handle the button click.
 */
@Override
public void actionPerformed(ActionEvent e) {
    //Clear the text components.
    displayArea.setText("");
    typingArea.setText("");

    //Return the focus to the typing area.
    typingArea.requestFocusInWindow();
}

/*
 * We have to jump through some hoops to avoid
 * trying to print non-printing characters
 * such as Shift.  (Not only do they not print,
 * but if you put them in a String, the characters
 * afterward won't show up in the text area.)
 */
private void displayInfo(KeyEvent e, String keyStatus) {

    //You should only rely on the key char if the event
    //is a key typed event.
    int id = e.getID();
    String keyString;
    if (id == KeyEvent.KEY_TYPED) {
        char c = e.getKeyChar();
        keyString = "key character = '" + c + "'";
    } else {
        int keyCode = e.getKeyCode();
        keyString = "key code = " + keyCode
                + " ("
                + KeyEvent.getKeyText(keyCode)
                + ")";
    }

    String locationString = "key location: ";
    int location = e.getKeyLocation();
    switch (location) {
        case KeyEvent.KEY_LOCATION_STANDARD:
            locationString += "standard";
            break;
        case KeyEvent.KEY_LOCATION_LEFT:
            locationString += "left";
            break;
        case KeyEvent.KEY_LOCATION_RIGHT:
            locationString += "right";
            break;
        case KeyEvent.KEY_LOCATION_NUMPAD:
            locationString += "numpad";
            break;
        default:
            // (location == KeyEvent.KEY_LOCATION_UNKNOWN)
            locationString += "unknown";
            break;
    }

    displayArea.append(keyStatus + "    " + keyString + "    " + locationString + NEWLINE);
    displayArea.setCaretPosition(displayArea.getDocument().getLength());
}
}

package uk.co.moonsit.windows;

import com.sun.jna.platform.win32.BaseTSD.ULONG_PTR;
import static com.sun.jna.platform.win32.User32.INSTANCE;
import com.sun.jna.platform.win32.WinDef.DWORD;
import com.sun.jna.platform.win32.WinDef.HWND;
import com.sun.jna.platform.win32.WinDef.LONG;
import com.sun.jna.platform.win32.WinDef.WORD;
import com.sun.jna.platform.win32.WinUser.INPUT;
import static com.sun.jna.platform.win32.WinUser.SM_CXSCREEN;
import static com.sun.jna.platform.win32.WinUser.SM_CYSCREEN;
import com.sun.jna.platform.win32.WinUser.WINDOWINFO;
import com.sun.jna.win32.StdCallLibrary;
import java.util.logging.Level;
import java.util.logging.Logger;
import uk.co.moonsit.sockets.QMClient;


public class WindowsComms {
private static final Logger LOG = Logger.getLogger(WindowsComms.class.getName());
private HWND hwnd;
int[] screenDimensions;
int[] windowCorners;
int width, height;

/*
public static final long MOUSEEVENTF_MOVE = 0x0001L;
public static final long MOUSEEVENTF_VIRTUALDESK = 0x4000L;
public static final long MOUSEEVENTF_ABSOLUTE = 0x8000L;
 */
public interface MUser32 extends StdCallLibrary {

    public static final long MOUSEEVENTF_MOVE = 0x0001L;
    public static final long MOUSEEVENTF_VIRTUALDESK = 0x4000L;
    public static final long MOUSEEVENTF_ABSOLUTE = 0x8000L;
    public static final long MOUSEEVENTF_LEFTDOWN = 0x0002L;
    public static final long MOUSEEVENTF_LEFTUP = 0x0004L;

    //User32 INSTANCE = (User32) Native.loadLibrary("user32", User32.class);
    //DWORD SendInput(DWORD dWord, INPUT[] input, int cbSize);
}

private int[] getSystemMetrics() {
    int[] dims = new int[2];
    int screenwidth = INSTANCE.GetSystemMetrics(SM_CXSCREEN);
    int screenheight = INSTANCE.GetSystemMetrics(SM_CYSCREEN);
    LOG.log(Level.INFO, "w h {0} {1}", new Object[]{screenwidth, screenheight});
    dims[0] = screenwidth;
    dims[1] = screenheight;
    return dims;
}

private int[] getWindowInfo() {
    int[] corner = new int[4];
    WINDOWINFO pwi = new WINDOWINFO();
    pwi.cbSize = pwi.size();
    INSTANCE.GetWindowInfo(hwnd, pwi);
    corner[0] = pwi.rcClient.left;
    corner[1] = pwi.rcClient.top;
    corner[2] = pwi.rcClient.right;
    corner[3] = pwi.rcClient.bottom;

    return corner;
}







@SuppressWarnings("SleepWhileInLoop")
public boolean findWindow(String name) {
    boolean rtn = false;
    hwnd = INSTANCE.FindWindow(null, name);
    int cnt = 0;
    while (hwnd == null) {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException ex) {
            Logger.getLogger(QMClient.class.getName()).log(Level.SEVERE, null, ex);
        }
        hwnd = INSTANCE.FindWindow(null, name);
        if (cnt++ == 10) {
            break;
        }
    }
    if (hwnd != null) {
        rtn = true;
    }
    screenDimensions = getSystemMetrics();
    windowCorners = getWindowInfo();
    width = windowCorners[2] - windowCorners[0];
    height = windowCorners[3] - windowCorners[1];
    LOG.log(Level.INFO, "WindowInfo {0} {1} {2} {3} {4} {5}", new Object[]{windowCorners[0], windowCorners[1], windowCorners[2], windowCorners[3], width, height});

    return rtn;
}

public void waitabit(long pause) throws InterruptedException {
    for (int i = 0; i < 1; i++) {
        Thread.sleep(pause);
        //hwnd = WindowsComms.findWindow(name);
        //LOG.log(Level.INFO, "Window {0}", hwnd.toString());
        //LOG.log(Level.INFO, "Paused {0}", pause);
    }

}

public void pause(long pause) throws InterruptedException {
    Thread.sleep(pause);
}

public boolean openWindow(String name) {
    return findWindow(name);
    //int len = User32.INSTANCE.GetWindowTextLength(hwnd);
    //char[] s = new char[len];
    //User32.INSTANCE.GetWindowText(hwnd, s, len);
    //LOG.info("Text: " + new String(s));
}

public String getWindowText() {
    int len = INSTANCE.GetWindowTextLength(hwnd);
    char[] s = new char[len + 1];

    INSTANCE.GetWindowText(hwnd, s, len + 1);
    String str = new String(s);

    return str;
}

public void setForegroundWindow() {
    INSTANCE.SetForegroundWindow(hwnd);
}

public void setWindowFocus() {
    //User32.INSTANCE.SetForegroundWindow(hwnd);
    INSTANCE.SetFocus(hwnd);//.SetForegroundWindow(hwnd);
}

public void pressReturn() {
    // Bring the window to the front
    //User32.INSTANCE.SetForegroundWindow(hwnd);
    //User32.INSTANCE.SetFocus(hwnd);
    INPUT input = new INPUT();
    input.type = new DWORD(INPUT.INPUT_KEYBOARD);
    input.input.setType("ki"); // Because setting INPUT_INPUT_KEYBOARD is not enough: https://groups.google.com/d/msg/jna-users/NDBGwC1VZbU/cjYCQ1CjBwAJ
    input.input.ki.time = new DWORD(0); //The timestamp
    //input.input.ki.dwFlags = new DWORD(KEYBDINPUT.KEYEVENTF_UNICODE); //I am handing you a unicode character
    input.input.ki.wScan = new WORD(0); //The unicode code in decimal (right?)
    input.input.ki.wVk = new WORD(0); //Virtual key code, i am setting this to 0 because of the unicode flag in dwFlags
    input.input.ki.dwExtraInfo = new ULONG_PTR(0); //I have no idea in hell of what this does :)
    // Press carriage return
    byte cr = '\r';
    input.input.ki.wVk = new WORD(cr);
    input.input.ki.dwFlags = new DWORD(0);  // keydown
    INSTANCE.SendInput(new DWORD(1), (INPUT[]) input.toArray(1), input.size());
    // Release carriage return
    //input.input.ki.wVk = new WORD(cr);
    //input.input.ki.dwFlags = new DWORD(2);  // keyup

}

public void moveWindow(String name, int x, int y, int width, int height) {
    hwnd = INSTANCE.FindWindow(null, name);
    INSTANCE.MoveWindow(hwnd, x, y, width, height, true);
}

public void moveMouse(int xr, int yr) {
    int x = windowCorners[0] + xr;
    int y = windowCorners[1] + yr;

    INPUT input = new INPUT();
    input.type = new DWORD(INPUT.INPUT_MOUSE);
    input.input.setType("mi");

    input.input.mi.dx = new LONG(x * 65536 / screenDimensions[0]);
    input.input.mi.dy = new LONG(y * 65536 / screenDimensions[1]);
    input.input.mi.mouseData = new DWORD(0);
    //input.input.mi.dwFlags = new DWORD(MUser32.MOUSEEVENTF_MOVE | MUser32.MOUSEEVENTF_ABSOLUTE | MUser32.MOUSEEVENTF_VIRTUALDESK );
    input.input.mi.dwFlags = new DWORD(MUser32.MOUSEEVENTF_MOVE | MUser32.MOUSEEVENTF_ABSOLUTE  );
    input.input.mi.time = new DWORD(0);

    INPUT[] inArray = {input};

    int cbSize = input.size(); // mouse input struct size
    DWORD nInputs = new DWORD(1); // number of inputs
    DWORD result = INSTANCE.SendInput(nInputs, inArray, cbSize);
    //LOG.log(Level.INFO, "result: {0}", result); // return 1 if the 1 event successfully inserted
    //LOG.log(Level.INFO, "Moved to {0} {1}", new Object[]{x, y});

}

public void click() {
    pressMouse();
    releaseMouse();
}

public void pressMouse() {
    INPUT input = new INPUT();
    input.type = new DWORD(INPUT.INPUT_MOUSE);
    input.input.setType("mi");

    input.input.mi.time = new DWORD(0);
    input.input.mi.mouseData = new DWORD(0);
    input.input.mi.dwFlags = new DWORD(MUser32.MOUSEEVENTF_LEFTDOWN);

    INPUT[] inArray = {input};
    DWORD result = INSTANCE.SendInput(new DWORD(1), inArray, input.size());
    //LOG.log(Level.INFO, "result: {0}", result); // return 1 if the 1 event successfully inserted

}

public void releaseMouse() {
    INPUT input = new INPUT();
    input.type = new DWORD(INPUT.INPUT_MOUSE);
    input.input.setType("mi");

    input.input.mi.time = new DWORD(0);
    input.input.mi.mouseData = new DWORD(0);
    input.input.mi.dwFlags = new DWORD(MUser32.MOUSEEVENTF_LEFTUP);

    INPUT[] inArray = {input};
    DWORD result = INSTANCE.SendInput(new DWORD(1), inArray, input.size());
    //LOG.log(Level.INFO, "result: {0}", result); // return 1 if the 1 event successfully inserted

}
}
...