JBR-2041 [TEST] Added new regression test (Touchscreen devices support)

(cherry picked from commit 2d587b3728)
(cherry picked from commit 92606f2c7f)
(cherry picked from commit 05af375909)
(cherry picked from commit 0f895bf1b2)
(cherry picked from commit 08aa0852b7)

with fix for JBR-5300 Change source code and test files to use GPL license

(cherry picked from commit 9bd62fade9)
This commit is contained in:
Elena Sayapina
2020-12-19 15:14:40 +07:00
committed by jbrbot
parent 18ef00cc33
commit aee50ce67b
9 changed files with 1006 additions and 0 deletions

View File

@@ -100,6 +100,17 @@ else
BUILD_JDK_JTREG_EXCLUDE += exeLibraryCache.c
endif
ifeq ($(OPENJDK_TARGET_OS), windows)
BUILD_JDK_JTREG_LIBRARIES_LIBS_libwindows_touch_robot := user32.lib
BUILD_JDK_JTREG_EXCLUDE += libtouchscreen_device.c
else
ifeq ($(OPENJDK_TARGET_OS), linux)
BUILD_JDK_JTREG_EXCLUDE += libwindows_touch_robot.c
else
BUILD_JDK_JTREG_EXCLUDE += libtouchscreen_device.c libwindows_touch_robot.c
endif
endif
ifeq ($(call isTargetOs, linux), true)
# Unconditionally compile with debug symbols and don't ever perform
# stripping during the test libraries' build.

View File

@@ -0,0 +1,48 @@
/*
* Copyright 2000-2023 JetBrains s.r.o.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.awt.AWTException;
import java.awt.GraphicsDevice;
import java.io.IOException;
public class LinuxTouchRobot extends TouchRobot {
private LinuxTouchScreenDevice device;
public LinuxTouchRobot() throws AWTException, IOException {
super();
device = new LinuxTouchScreenDevice();
}
public LinuxTouchRobot(GraphicsDevice screen) throws AWTException, IOException {
super(screen);
device = new LinuxTouchScreenDevice();
}
public void touchClick(int fingerId, int x, int y) throws IOException {
device.click(fingerId, x, y);
}
public void touchMove(int fingerId, int fromX, int fromY, int toX, int toY) throws IOException {
device.move(fingerId, fromX, fromY, toX, toY);
}
}

View File

@@ -0,0 +1,89 @@
/*
* Copyright 2000-2023 JetBrains s.r.o.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.awt.Toolkit;
import java.io.IOException;
public class LinuxTouchScreenDevice implements AutoCloseable {
// TODO add product id
private int width;
private int height;
private int fileDescriptor;
static {
System.loadLibrary("touchscreen_device");
}
public LinuxTouchScreenDevice() throws IOException {
this(Toolkit.getDefaultToolkit().getScreenSize().width, Toolkit.getDefaultToolkit().getScreenSize().height);
}
public LinuxTouchScreenDevice(int width, int height) throws IOException {
this.width = width;
this.height = height;
fileDescriptor = create(getWidth(), getHeight());
checkCompletion(fileDescriptor,
"Failed to create virtual touchscreen device");
}
@Override
public void close() throws Exception {
checkCompletion(destroy(fileDescriptor),
"Failed to close touchscreen device");
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public void click(int trackingId, int x, int y) throws IOException {
checkCompletion(clickImpl(fileDescriptor, trackingId, x, y),
"Failed to click on touchscreen device");
}
public void move(int trackingId, int fromX, int fromY, int toX, int toY) throws IOException {
checkCompletion(moveImpl(fileDescriptor, trackingId, fromX, fromY, toX, toY),
"Failed to move on virtual touchscreen device");
}
private void checkCompletion(int code, String errorMessage) throws IOException {
if (code < 0) {
throw new IOException(errorMessage);
}
}
private native int create(int width, int height);
private native int destroy(int fd);
private native int clickImpl(int fd, int trackingId, int x, int y);
private native int moveImpl(int fd, int trackingId, int fromX, int fromY, int toX, int toY);
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright 2000-2023 JetBrains s.r.o.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.awt.AWTException;
import java.awt.GraphicsDevice;
import java.awt.Robot;
import java.io.IOException;
public abstract class TouchRobot extends Robot {
public TouchRobot() throws AWTException {
}
public TouchRobot(GraphicsDevice screen) throws AWTException {
super(screen);
}
public abstract void touchClick(int fingerId, int x, int y) throws IOException;
public abstract void touchMove(int fingerId, int fromX, int fromY, int toX, int toY) throws IOException;
}

View File

@@ -0,0 +1,376 @@
/*
* Copyright 2000-2023 JetBrains s.r.o.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import java.awt.AWTException;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* @test
* @summary Regression test for JBR-2041: Touchscreen devices support
* @requires (jdk.version.major >= 11) & (os.family == "windows")
* @build WindowsTouchRobot TouchScreenEventsTest
* @run main/othervm/native TouchScreenEventsTest
*/
public class TouchScreenEventsTest {
static final int TIMEOUT = 2;
static final int PAUSE = 1000;
// TODO make this constants accessible within jdk
static final int TOUCH_BEGIN = 2;
static final int TOUCH_UPDATE = 3;
static final int TOUCH_END = 4;
public static void main(String[] args) throws Exception {
if(runTest(new TouchClickSuite())
& runTest(new TouchMoveSuite())
& runTest(new TouchTinyMoveSuite())
& runTest(new TouchAxesScrollSuite(TouchAxesScrollSuite.AXIS.X))
& runTest(new TouchAxesScrollSuite(TouchAxesScrollSuite.AXIS.Y))) {
System.out.println("TEST PASSED");
} else {
throw new RuntimeException("TEST FAILED");
}
}
private static boolean runTest(TouchTestSuite suite) throws Exception {
GUI gui = new GUI();
try {
TouchRobot robot = getTouchRobot();
SwingUtilities.invokeAndWait(gui::createAndShow);
suite.addListener(gui.frame);
robot.waitForIdle();
Thread.sleep(PAUSE);
suite.perform(gui, robot);
robot.waitForIdle();
return suite.passed();
} finally {
SwingUtilities.invokeLater(() -> {
if (gui.frame != null) {
gui.frame.dispose();
}
});
Thread.sleep(PAUSE);
}
}
private static TouchRobot getTouchRobot() throws IOException, AWTException {
String osName = System.getProperty("os.name").toLowerCase();
if (osName.contains("linux")) {
return new LinuxTouchRobot();
} else if (osName.contains("windows")) {
return new WindowsTouchRobot();
}
throw new RuntimeException("Touch robot for this platform isn't implemented");
}
}
class GUI {
JFrame frame;
Rectangle frameBounds;
void createAndShow() {
frame = new JFrame();
frame.setSize(640, 480);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
frameBounds = frame.getBounds();
}
}
interface TouchTestSuite {
void addListener(JFrame frame);
void perform(GUI gui, TouchRobot robot) throws IOException;
boolean passed() throws InterruptedException;
}
class TouchClickSuite implements MouseListener, TouchTestSuite {
private volatile boolean pressed;
private volatile boolean released;
private volatile boolean clicked;
private final CountDownLatch latch = new CountDownLatch(3);
@Override
public void mouseClicked(MouseEvent e) {
if (!clicked) {
clicked = true;
latch.countDown();
}
}
@Override
public void mousePressed(MouseEvent e) {
pressed = true;
if (!pressed) {
pressed = true;
latch.countDown();
}
}
@Override
public void mouseReleased(MouseEvent e) {
if (!released) {
released = true;
latch.countDown();
}
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
@Override
public void perform(GUI gui, TouchRobot robot) throws IOException {
int x = gui.frameBounds.x + gui.frameBounds.width / 2;
int y = gui.frameBounds.y + gui.frameBounds.height / 2;
robot.touchClick(42, x, y);
}
@Override
public boolean passed() throws InterruptedException {
latch.await(TouchScreenEventsTest.TIMEOUT, TimeUnit.SECONDS);
if (!pressed || !released || !clicked) {
String error = (pressed ? "" : "pressed: " + pressed) +
(released ? "" : ", released: " + released) +
(clicked ? "" : ", clicked: " + clicked);
System.out.println("Touch click failed: " + error);
return false;
}
System.out.println("Touch click passed");
return true;
}
@Override
public void addListener(JFrame frame) {
frame.addMouseListener(this);
}
}
class TouchMoveSuite implements MouseWheelListener, TouchTestSuite {
private volatile boolean begin;
private volatile boolean update;
private volatile boolean end;
private final CountDownLatch latch = new CountDownLatch(3);
@Override
public void mouseWheelMoved(MouseWheelEvent e) {
if (e.getScrollType() == TouchScreenEventsTest.TOUCH_BEGIN) {
if (!begin) {
begin = true;
latch.countDown();
}
} else if (e.getScrollType() == TouchScreenEventsTest.TOUCH_UPDATE) {
if (!update) {
update = true;
latch.countDown();
}
} else if (e.getScrollType() == TouchScreenEventsTest.TOUCH_END) {
if (!end) {
end = true;
latch.countDown();
}
}
}
@Override
public void perform(GUI gui, TouchRobot robot) throws IOException {
int x1 = gui.frameBounds.x + gui.frameBounds.width / 4;
int y1 = gui.frameBounds.y + gui.frameBounds.height / 4;
int x2 = gui.frameBounds.x + gui.frameBounds.width / 2;
int y2 = gui.frameBounds.y + gui.frameBounds.height / 2;
robot.touchMove(42, x1, y1, x2, y2);
}
@Override
public boolean passed() throws InterruptedException {
latch.await(TouchScreenEventsTest.TIMEOUT, TimeUnit.SECONDS);
if (!begin || !update || !end) {
String error = (begin ? "" : "begin: " + begin) +
(update ? "" : ", update: " + update) +
(end ? "" : ", end: " + end);
System.out.println("Touch move failed: " + error);
return false;
}
System.out.println("Touch move passed");
return true;
}
@Override
public void addListener(JFrame frame) {
frame.addMouseWheelListener(this);
}
}
class TouchTinyMoveSuite implements MouseWheelListener, MouseListener, TouchTestSuite {
private volatile boolean scroll;
private volatile boolean click;
private final CountDownLatch latch = new CountDownLatch(1);
@Override
public void mouseWheelMoved(MouseWheelEvent e) {
if (e.getScrollType() == TouchScreenEventsTest.TOUCH_UPDATE) {
scroll = true;
latch.countDown();
}
}
@Override
public void perform(GUI gui, TouchRobot robot) throws IOException {
int x1 = gui.frameBounds.x + gui.frameBounds.width / 4;
int y1 = gui.frameBounds.y + gui.frameBounds.height / 4;
// move inside tiny area
int x2 = x1 + 1;
int y2 = y1 + 1;
robot.touchMove(42, x1, y1, x2, y2);
}
@Override
public boolean passed() throws InterruptedException {
latch.await(TouchScreenEventsTest.TIMEOUT, TimeUnit.SECONDS);
if (scroll || !click) {
String error = (scroll ? "scroll " + scroll : "") +
(click ? "" : ", click: " + click);
System.out.println("Tiny touch move failed: " + error);
return false;
}
System.out.println("Tiny touch move passed");
return true;
}
@Override
public void addListener(JFrame frame) {
frame.addMouseWheelListener(this);
frame.addMouseListener(this);
}
@Override
public void mouseClicked(MouseEvent e) {
click = true;
latch.countDown();
}
@Override
public void mousePressed(MouseEvent e) {
}
@Override
public void mouseReleased(MouseEvent e) {
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
}
class TouchAxesScrollSuite implements MouseWheelListener, TouchTestSuite {
enum AXIS {X, Y}
private final AXIS axis;
private volatile boolean vertical;
private volatile boolean horizontal;
private final CountDownLatch latch = new CountDownLatch(1);
TouchAxesScrollSuite(AXIS axis) {
this.axis = axis;
}
@Override
public void mouseWheelMoved(MouseWheelEvent e) {
if (e.getScrollType() == TouchScreenEventsTest.TOUCH_UPDATE) {
if (e.isShiftDown()) {
horizontal = true;
} else {
vertical = true;
}
latch.countDown();
}
}
@Override
public void perform(GUI gui, TouchRobot robot) throws IOException {
int x1 = gui.frameBounds.x + gui.frameBounds.width / 4;
int y1 = gui.frameBounds.y + gui.frameBounds.height / 4;
switch (axis) {
case X:
int x2 = gui.frameBounds.x + gui.frameBounds.width / 2;
robot.touchMove(42, x1, y1, x2, y1);
break;
case Y:
int y2 = gui.frameBounds.y + gui.frameBounds.height / 2;
robot.touchMove(42, x1, y1, x1, y2);
break;
}
}
@Override
public boolean passed() throws InterruptedException {
latch.await(TouchScreenEventsTest.TIMEOUT, TimeUnit.SECONDS);
String info = "horizontal: " + horizontal + ", vertical: " + vertical;
switch (axis) {
case X:
if (!horizontal || vertical) {
System.out.println("Touch axes failed: " + info);
return false;
}
break;
case Y:
if (horizontal || !vertical) {
System.out.println("Touch axes failed: " + info);
return false;
}
break;
}
System.out.println("Touch axes passed: " + info);
return true;
}
@Override
public void addListener(JFrame frame) {
frame.addMouseWheelListener(this);
}
}

View File

@@ -0,0 +1,51 @@
#!/bin/bash
#
# Copyright 2000-2023 JetBrains s.r.o.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 only, as
# published by the Free Software Foundation.
#
# This code is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# version 2 for more details (a copy is included in the LICENSE file that
# accompanied this code).
#
# You should have received a copy of the GNU General Public License version
# 2 along with this work; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
# or visit www.oracle.com if you need additional information or have any
# questions.
#
# @test
# @summary Regression test for JBR-2041: Touchscreen devices support
# @requires (jdk.version.major >= 11) & (os.family == "linux")
# @build TouchRobot LinuxTouchRobot LinuxTouchScreenDevice TouchScreenEventsTest
# @run shell TouchScreenEventsTestLinux.sh
# password for sudo
PASSWORD=${BUPWD}
echo "Allow current user write to /dev/uinput:"
echo "> sudo chown `whoami` /dev/uinput"
echo ${PASSWORD} | sudo -S chown `whoami` /dev/uinput
echo "result=$?"
echo "Launching TouchScreenEventsTest.java:"
echo "> $TESTJAVA/bin/java $TESTVMOPTS -cp $TESTCLASSES TouchScreenEventsTest"
$TESTJAVA/bin/java $TESTVMOPTS -cp $TESTCLASSES TouchScreenEventsTest
result=$?
echo "result=$result"
echo "Restore permissions for /dev/uinput:"
echo "> sudo chown root /dev/uinput"
echo ${PASSWORD} | sudo -S chown root /dev/uinput
echo "result=$?"
exit $result

View File

@@ -0,0 +1,56 @@
/*
* Copyright 2000-2023 JetBrains s.r.o.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.awt.AWTException;
import java.awt.GraphicsDevice;
import java.io.IOException;
public class WindowsTouchRobot extends TouchRobot {
static {
System.loadLibrary("windows_touch_robot");
}
public WindowsTouchRobot() throws AWTException, IOException {
super();
}
public WindowsTouchRobot(GraphicsDevice screen) throws AWTException, IOException {
super(screen);
}
@Override
public void touchClick(int fingerId, int x, int y) throws IOException {
// TODO unused finger id cause windows use different finger id model
clickImpl(x, y);
}
@Override
public void touchMove(int fingerId, int fromX, int fromY, int toX, int toY) throws IOException {
// TODO unused finger id cause windows use different finger id model
moveImpl(fromX, fromY, toX, toY);
}
private native void clickImpl(int x, int y);
private native void moveImpl(int fromX, int fromY, int toX, int toY);
}

View File

@@ -0,0 +1,228 @@
/*
* Copyright 2000-2023 JetBrains s.r.o.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include <jni.h>
#include <errno.h>
#include <fcntl.h>
#include <linux/input.h>
#include <linux/types.h>
#include <linux/uinput.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
// TODO sort includes
// TODO add casts
typedef struct {
__u16 type;
__u16 code;
__s32 value;
} TEventData;
static int set_bit(int fd, unsigned long int request, unsigned long int bit) {
return ioctl(fd, request, bit);
}
static int touch_begin(int fd, int tracking_id, int x, int y) {
struct input_event ev;
int i = 0;
const int cnt = 7;
TEventData eventData[7] = {{EV_ABS, ABS_MT_TRACKING_ID, tracking_id},
{EV_ABS, ABS_MT_POSITION_X, x},
{EV_ABS, ABS_MT_POSITION_Y, y},
{EV_KEY, BTN_TOUCH, 1},
{EV_ABS, ABS_X, x},
{EV_ABS, ABS_Y, y},
{EV_SYN, 0, 0}};
for (i = 0; i < cnt; i++) {
memset(&ev, 0, sizeof(struct input_event));
ev.type = eventData[i].type;
ev.code = eventData[i].code;
ev.value = eventData[i].value;
if (write(fd, &ev, sizeof(struct input_event)) < 0) {
return -1;
}
}
return 0;
}
static int touch_update(int fd, int x, int y) {
struct input_event ev;
int i = 0;
const int cnt = 5;
TEventData eventData[5] = {{EV_ABS, ABS_MT_POSITION_X, x},
{EV_ABS, ABS_MT_POSITION_Y, y},
{EV_ABS, ABS_X, x},
{EV_ABS, ABS_Y, y},
{EV_SYN, 0, 0}};
for (i = 0; i < cnt; i++) {
memset(&ev, 0, sizeof(struct input_event));
ev.type = eventData[i].type;
ev.code = eventData[i].code;
ev.value = eventData[i].value;
if (write(fd, &ev, sizeof(struct input_event)) < 0) {
return -1;
}
}
return 0;
}
// TODO consider different name in case of multitouch
static int touch_end(int fd) {
struct input_event ev;
int i = 0;
const int cnt = 3;
TEventData eventData[3] = {
{EV_ABS, ABS_MT_TRACKING_ID, -1}, {EV_KEY, BTN_TOUCH, 0}, {EV_SYN, 0, 0}};
for (i = 0; i < cnt; i++) {
memset(&ev, 0, sizeof(struct input_event));
ev.type = eventData[i].type;
ev.code = eventData[i].code;
ev.value = eventData[i].value;
if (write(fd, &ev, sizeof(struct input_event)) < 0) {
return -1;
}
}
return 0;
}
/*
* Class: TouchScreenDevice
* Method: create
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_LinuxTouchScreenDevice_create(JNIEnv *env,
jobject o,
jint width,
jint height) {
int productId = 123;
const int FAKE_VENDOR_ID = 0x453;
const int MAX_FINGER_COUNT = 9;
const int MAX_TRACKING_ID = 65535;
int fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK);
if (fd < 0) {
return -1;
}
int ret_code = 0;
ret_code = set_bit(fd, UI_SET_EVBIT, EV_SYN);
ret_code = set_bit(fd, UI_SET_EVBIT, EV_KEY);
ret_code = set_bit(fd, UI_SET_KEYBIT, BTN_TOUCH);
ret_code = set_bit(fd, UI_SET_EVBIT, EV_ABS);
ret_code = set_bit(fd, UI_SET_ABSBIT, ABS_X);
ret_code = set_bit(fd, UI_SET_ABSBIT, ABS_Y);
ret_code = set_bit(fd, UI_SET_ABSBIT, ABS_MT_SLOT);
ret_code = set_bit(fd, UI_SET_ABSBIT, ABS_MT_POSITION_X);
ret_code = set_bit(fd, UI_SET_ABSBIT, ABS_MT_POSITION_Y);
ret_code = set_bit(fd, UI_SET_ABSBIT, ABS_MT_TRACKING_ID);
ret_code = set_bit(fd, UI_SET_PROPBIT, INPUT_PROP_DIRECT);
if (ret_code < 0) {
return -1;
}
struct uinput_user_dev virtual_touch_device;
memset(&virtual_touch_device, 0, sizeof(virtual_touch_device));
snprintf(virtual_touch_device.name, UINPUT_MAX_NAME_SIZE,
"Virtual Touch Device - %#x", productId);
virtual_touch_device.id.bustype = BUS_VIRTUAL;
virtual_touch_device.id.vendor = FAKE_VENDOR_ID;
virtual_touch_device.id.product = productId;
virtual_touch_device.id.version = 1;
virtual_touch_device.absmin[ABS_X] = 0;
virtual_touch_device.absmax[ABS_X] = width;
virtual_touch_device.absmin[ABS_Y] = 0;
virtual_touch_device.absmax[ABS_Y] = height;
virtual_touch_device.absmin[ABS_MT_SLOT] = 0;
virtual_touch_device.absmax[ABS_MT_SLOT] = MAX_FINGER_COUNT;
virtual_touch_device.absmin[ABS_MT_POSITION_X] = 0;
virtual_touch_device.absmax[ABS_MT_POSITION_X] = width;
virtual_touch_device.absmin[ABS_MT_POSITION_Y] = 0;
virtual_touch_device.absmax[ABS_MT_POSITION_Y] = height;
virtual_touch_device.absmin[ABS_MT_TRACKING_ID] = 0;
virtual_touch_device.absmax[ABS_MT_TRACKING_ID] = MAX_TRACKING_ID;
ret_code = write(fd, &virtual_touch_device, sizeof(virtual_touch_device));
ret_code = ioctl(fd, UI_DEV_CREATE);
if (ret_code < 0) {
return -1;
}
return fd;
}
/*
* Class: TouchScreenDevice
* Method: destroy
* Signature: (I)V
*/
JNIEXPORT jint JNICALL Java_LinuxTouchScreenDevice_destroy(JNIEnv *env,
jobject o,
jint fd) {
if (ioctl(fd, UI_DEV_DESTROY) < 0) {
return -1;
}
return close(fd);
}
/*
* Class: TouchScreenDevice
* Method: clickImpl
* Signature: (IIII)V
*/
// TODO return code with checked exception
JNIEXPORT jint JNICALL Java_LinuxTouchScreenDevice_clickImpl(
JNIEnv *env, jobject o, jint fd, jint trackingId, jint x, jint y) {
touch_begin(fd, trackingId, x, y);
touch_end(fd);
return 0;
}
/*
* Class: TouchScreenDevice
* Method: moveImpl
* Signature: (IIIIII)V
*/
JNIEXPORT jint JNICALL Java_LinuxTouchScreenDevice_moveImpl(
JNIEnv *env, jobject o, jint fd, jint trackingId, jint fromX, jint fromY,
jint toX, jint toY) {
touch_begin(fd, trackingId, fromX, fromY);
touch_update(fd, toX, toY);
touch_end(fd);
return 0;
}

View File

@@ -0,0 +1,107 @@
/*
* Copyright 2000-2023 JetBrains s.r.o.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include <jni.h>
#include <windows.h>
#include <WinUser.h>
POINTER_TOUCH_INFO create_touch_info()
{
POINTER_TOUCH_INFO contact;
InitializeTouchInjection(1, TOUCH_FEEDBACK_DEFAULT); // Number of contact point
memset(&contact, 0, sizeof(POINTER_TOUCH_INFO));
contact.touchFlags = TOUCH_FLAG_NONE;
contact.touchMask = TOUCH_MASK_CONTACTAREA | TOUCH_MASK_ORIENTATION | TOUCH_MASK_PRESSURE;
contact.orientation = 90; // Orientation of 90 means touching perpendicular to screen.
contact.pressure = 32000;
return contact;
}
void set_contact_area(POINTER_TOUCH_INFO *contact)
{
// 4 x 4 pixel contact area
contact->rcContact.top = contact->pointerInfo.ptPixelLocation.y - 2;
contact->rcContact.bottom = contact->pointerInfo.ptPixelLocation.y + 2;
contact->rcContact.left = contact->pointerInfo.ptPixelLocation.x - 2;
contact->rcContact.right = contact->pointerInfo.ptPixelLocation.x + 2;
}
void touchBegin(POINTER_TOUCH_INFO* contact, LONG x, LONG y)
{
contact->pointerInfo.pointerType = PT_TOUCH;
// primary finger pointerId == 0
contact->pointerInfo.pointerId = 0;
contact->pointerInfo.ptPixelLocation.x = x;
contact->pointerInfo.ptPixelLocation.y = y;
set_contact_area(contact);
contact->pointerInfo.pointerFlags = POINTER_FLAG_DOWN | POINTER_FLAG_INRANGE | POINTER_FLAG_INCONTACT;
InjectTouchInput(1, contact);
}
void touchUpdate(POINTER_TOUCH_INFO* contact, LONG x, LONG y)
{
contact->pointerInfo.pointerFlags = POINTER_FLAG_UPDATE | POINTER_FLAG_INRANGE | POINTER_FLAG_INCONTACT;
contact->pointerInfo.ptPixelLocation.x = x;
contact->pointerInfo.ptPixelLocation.y = y;
InjectTouchInput(1, contact);
}
void touchEnd(POINTER_TOUCH_INFO* contact)
{
contact->pointerInfo.pointerFlags = POINTER_FLAG_UP;
InjectTouchInput(1, contact);
}
/*
* Class: quality_util_WindowsTouchRobot
* Method: clickImpl
* Signature: (II)V
*/
JNIEXPORT void JNICALL
Java_WindowsTouchRobot_clickImpl(JNIEnv* env, jobject o,
jint x, jint y)
{
POINTER_TOUCH_INFO contact = create_touch_info();
touchBegin(&contact, x, y);
touchEnd(&contact);
}
/*
* Class: quality_util_WindowsTouchRobot
* Method: moveImpl
* Signature: (IIII)V
*/
JNIEXPORT void JNICALL
Java_WindowsTouchRobot_moveImpl(JNIEnv* env, jobject o,
jint fromX, jint fromY,
jint toX, jint toY)
{
POINTER_TOUCH_INFO contact = create_touch_info();
touchBegin(&contact, fromX, fromY);
touchUpdate(&contact, toX, toY);
touchEnd(&contact);
}