Compare commits

..

3 Commits

Author SHA1 Message Date
Artem Bochkarev
ae41b6933c JBR-1794: backport 8234522: [macos] Crash with use of native file dialog
cherry-picked from hg changeset (Node ID e07a60855a7631b5eac32da1f1e71d72986a2c06, Parent  221a47a6f6d335560d9dd532a1ac593e316b5954)

(cherry picked from commit 09eabbd567)
2019-12-11 14:45:08 +03:00
Denis Fokin
c8ad353f45 JBR-1991 Focus problems in Windows with X-Mouse style focus 2019-12-06 12:37:53 +03:00
Vitaly Provodin
57162c91c3 updated JTreg exclude list 2019-12-06 10:04:05 +07:00
15 changed files with 89 additions and 821 deletions

View File

@@ -50,7 +50,7 @@ BOOL postEventDuringEventSynthesis = NO;
* Subtypes of NSApplicationDefined, which are used for custom events.
*/
enum {
ExecuteBlockEvent, NativeSyncQueueEvent
ExecuteBlockEvent = 777, NativeSyncQueueEvent
};
@implementation NSApplicationAWT
@@ -366,11 +366,15 @@ untilDate:(NSDate *)expiration inMode:(NSString *)mode dequeue:(BOOL)deqFlag {
{
if ([event type] == NSApplicationDefined
&& TS_EQUAL([event timestamp], dummyEventTimestamp)
&& [event subtype] == NativeSyncQueueEvent) {
&& (short)[event subtype] == NativeSyncQueueEvent
&& [event data1] == NativeSyncQueueEvent
&& [event data2] == NativeSyncQueueEvent) {
[seenDummyEventLock lockWhenCondition:NO];
[seenDummyEventLock unlockWithCondition:YES];
} else if ([event type] == NSApplicationDefined && [event subtype] == ExecuteBlockEvent) {
} else if ([event type] == NSApplicationDefined
&& (short)[event subtype] == ExecuteBlockEvent
&& [event data1] != 0 && [event data2] == ExecuteBlockEvent) {
void (^block)() = (void (^)()) [event data1];
block();
[block release];
@@ -401,7 +405,7 @@ untilDate:(NSDate *)expiration inMode:(NSString *)mode dequeue:(BOOL)deqFlag {
context: nil
subtype: ExecuteBlockEvent
data1: encode
data2: 0];
data2: ExecuteBlockEvent];
[NSApp postEvent: event atStart: NO];
[pool drain];
@@ -419,8 +423,8 @@ untilDate:(NSDate *)expiration inMode:(NSString *)mode dequeue:(BOOL)deqFlag {
windowNumber: 0
context: nil
subtype: NativeSyncQueueEvent
data1: 0
data2: 0];
data1: NativeSyncQueueEvent
data2: NativeSyncQueueEvent];
if (useCocoa) {
[NSApp postEvent:event atStart:NO];
} else {

View File

@@ -1651,7 +1651,7 @@ LRESULT AwtComponent::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
window->AwtSetActiveWindow(TRUE, LOWORD(lParam)/*hittest*/);
}
mr = mrConsume;
retValue = MA_NOACTIVATE;
retValue = AwtToolkit::GetInstance().IsActiveWindowTrackingEnabled() ? MA_ACTIVATE : MA_NOACTIVATE;
break;
}
case WM_CTLCOLORMSGBOX:

View File

@@ -700,6 +700,13 @@ BOOL AwtToolkit::Initialize(BOOL localPump) {
tk.m_touchKbrdAutoShowIsEnabled = env->CallStaticBooleanMethod(
sunToolkitCls, isTouchKeyboardAutoShowEnabledMID);
BOOL tracking = FALSE;
int result = 0;
result = ::SystemParametersInfo(SPI_GETACTIVEWINDOWTRACKING, 0,
&tracking, 0);
tk.m_active_window_tracking_mode_enabled = (tracking && (result != 0));
CATCH_BAD_ALLOC_RET(FALSE);
if (tk.m_isWin8OrLater && tk.m_touchKbrdAutoShowIsEnabled) {
@@ -803,6 +810,10 @@ BOOL AwtToolkit::IsDynamicLayoutSet() {
return m_isDynamicLayoutSet;
}
BOOL AwtToolkit::IsActiveWindowTrackingEnabled() {
return m_active_window_tracking_mode_enabled;
}
BOOL AwtToolkit::IsDynamicLayoutSupported() {
// SPI_GETDRAGFULLWINDOWS is only supported on Win95 if
// Windows Plus! is installed. Otherwise, box frame resize.

View File

@@ -240,6 +240,7 @@ public:
void SetDynamicLayout(BOOL dynamic);
BOOL IsDynamicLayoutSet();
BOOL IsDynamicLayoutSupported();
BOOL IsActiveWindowTrackingEnabled();
BOOL IsDynamicLayoutActive();
BOOL areExtraMouseButtonsEnabled();
void setExtraMouseButtonsEnabled(BOOL enable);
@@ -477,6 +478,7 @@ private:
BOOL m_isActive; // set to FALSE at beginning of Dispose
BOOL m_isDisposed; // set to TRUE at end of Dispose
BOOL m_areExtraMouseButtonsEnabled;
BOOL m_active_window_tracking_mode_enabled;
typedef BOOL (WINAPI *RegisterTouchWindowFunc)(HWND hWnd, ULONG ulFlags);
typedef BOOL (WINAPI *GetTouchInputInfoFunc)(HTOUCHINPUT hTouchInput,

View File

@@ -0,0 +1,62 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* 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.EventQueue;
import java.awt.FileDialog;
import java.awt.Frame;
import java.awt.Robot;
import java.awt.event.KeyEvent;
/**
* @test
* @key headful
* @bug 8234522
* @requires (os.family == "mac")
*/
public final class MacOSGoToFolderCrash {
public static void main(final String[] args) throws Exception {
EventQueue.invokeLater(() -> {
FileDialog fd = new FileDialog((Frame) null);
fd.setVisible(true);
});
Robot robot = new Robot();
robot.setAutoDelay(400);
robot.waitForIdle();
// "⌘+Shift+G" Open "Go To Folder" window
robot.keyPress(KeyEvent.VK_META);
robot.keyPress(KeyEvent.VK_SHIFT);
robot.keyPress(KeyEvent.VK_G);
robot.keyRelease(KeyEvent.VK_G);
robot.keyRelease(KeyEvent.VK_SHIFT);
robot.keyRelease(KeyEvent.VK_META);
// Select something
robot.keyPress(KeyEvent.VK_DOWN);
robot.keyRelease(KeyEvent.VK_DOWN);
robot.keyPress(KeyEvent.VK_ENTER);
robot.keyRelease(KeyEvent.VK_ENTER);
// Close File dialog
robot.keyPress(KeyEvent.VK_ESCAPE);
robot.keyRelease(KeyEvent.VK_ESCAPE);
}
}

View File

@@ -620,12 +620,13 @@ java/awt/dnd/DisposeFrameOnDragCrash/DisposeFrameOnDragTest.java
java/awt/dnd/DragInterceptorAppletTest/DragInterceptorAppletTest.html 8215538 windows-all,macosx-all,linux-all (macosx-all,linux-all: commit)
java/awt/dnd/DropTargetEnterExitTest/ExtraDragEnterTest.java 8029680 macosx-all,linux-all,windows-all
java/awt/dnd/DropTargetEnterExitTest/MissedDragExitTest.java 8028998,8159694 macosx-all,windows-all,linux-all
java/awt/dnd/FileListBetweenJVMsTest/FileListBetweenJVMsTest.html 7124379,7188825 macosx-all,linux-all,windows-all
java/awt/dnd/ImageTransferTest/ImageTransferTest.java 8176556 generic-all
java/awt/dnd/InterJVMGetDropSuccessTest/InterJVMGetDropSuccessTest.html
java/awt/dnd/InterJVMGetDropSuccessTest/InterJVMGetDropSuccessTest.html 8208555 windows-all
java/awt/dnd/MissingDragExitEventTest/MissingDragExitEventTest.java 8030121 macosx-all,windows-all,linux-all
java/awt/dnd/MissingEventsOnModalDialog/MissingEventsOnModalDialogTest.java 8164464 linux-all,macosx-all,windows-all
java/awt/dnd/NoFormatsCrashTest/NoFormatsCrashTest.html 8214221 windows-all,linux-all,macosx-all
java/awt/dnd/URIListBetweenJVMsTest/URIListBetweenJVMsTest.html 8171510 macosx-all
java/awt/dnd/URIListBetweenJVMsTest/URIListBetweenJVMsTest.html 8171510,7124379 macosx-all,linux-all
java/awt/dnd/URIListToFileListBetweenJVMsTest/URIListToFileListBetweenJVMsTest.html 8194947 generic-all
java/awt/event/ComponentEvent/MovedResizedTardyEventTest/MovedResizedTardyEventTest.html 6511207 generic-all
java/awt/event/HierarchyEvent/AncestorResized/AncestorResized.java 6618538 generic-all

View File

@@ -112,11 +112,9 @@ java/awt/datatransfer/MissedHtmlAndRtfBug/MissedHtmlAndRtfBug.html
java/awt/datatransfer/SystemSelection/SystemSelectionSwingTest.java nobug linux-all
java/awt/dnd/AcceptDropMultipleTimes/AcceptDropMultipleTimes.java nobug macosx-all,windows-all,linux-all
java/awt/dnd/DnDTestWithHIDPI/DragTestWithHIDPI.java nobug macosx-all,windows-all,linux-all
java/awt/dnd/FileListBetweenJVMsTest/FileListBetweenJVMsTest.html nobug macosx-all,linux-all,windows-all
java/awt/dnd/InterJVMGetDropSuccessTest/InterJVMGetDropSuccessTest.html nobug macosx-all,windows-all,linux-all
java/awt/dnd/RecognizedActionTest/RecognizedActionTest.java nobug macosx-all,windows-all,linux-all
java/awt/dnd/RemoveDropTargetCrashTest/RemoveDropTargetCrashTest.java nobug macosx-all,windows-all
java/awt/dnd/URIListBetweenJVMsTest/URIListBetweenJVMsTest.html nobug macosx-all,linux-all
java/awt/event/KeyEvent/AltCharAcceleratorTest/AltCharAcceleratorTest.java nobug macosx-all,windows-all,linux-all
java/awt/event/KeyEvent/DeadKey/DeadKeyMacOSXInputText.java nobug macosx-all
java/awt/event/KeyEvent/DeadKey/deadKeyMacOSX.java nobug macosx-all

View File

@@ -1,38 +0,0 @@
###Tips for compiling libraries and preparing env for test running
## Linux
prepare env for work with uinput: [group permissions](https://github.com/tuomasjjrasanen/python-uinput/issues/6#issuecomment-538710069)
```shell script
cd JetBrainsRuntime/test/jdk/jbu/native
# complile lib
gcc -shared -fPIC -I<path_to_jdk>/JetBrainsRuntime/build/linux-x86_64-normal-server-release/images/jdk/include/linux -I<path_to_jdk>/JetBrainsRuntime/build/linux-x86_64-normal-server-release/images/jdk/include touchscreen_device.c -o libtouchscreen_device.so
```
In IDEA run configurations set envvar to find libtouchscreen_device.so
```
LD_LIBRARY_PATH=<path_to_jdk>\JetBrainsRuntime\test\jdk\jbu\native
```
## Windows
prepare windows env: look for vcvarsall.bat [here](https://github.com/JetBrains/JetBrainsRuntime#windows)
```
cd JetBrainsRuntime\test\jdk\jbu\native
cl -I<path_to_jdk>\JetBrainsRuntime\build\windows-x86_64-normal-server-release\jdk\include\win32 -I<path_to_jdk>\JetBrainsRuntime\build\windows-x86_64-normal-server-release\jdk\include -MD -LD windows_touch_robot.c "<path_to_user32lib>\user32.lib" -Fewindows_touch_robot.dll
```
user32.lib is needed for WinUser.h (touch injection stuff)
In IDEA run configurations set envvar to find windows_touch_robot.dll
```
PATH="<path_to_jdk>\JetBrainsRuntime\test\jdk\jbu\native"
```
my paths are
```
user32: "C:\Program Files (x86)\Windows Kits\10\Lib\10.0.18362.0\um\x64\user32.lib"
path_to_jdk: "C:\Program_Files\cygwin64\home\Denis.Konoplev"
```

View File

@@ -1,205 +0,0 @@
#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;
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 (int 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;
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 (int 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;
const int cnt = 3;
TEventData eventData[3] = {
{EV_ABS, ABS_MT_TRACKING_ID, -1}, {EV_KEY, BTN_TOUCH, 0}, {EV_SYN, 0, 0}};
for (int 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: quality_util_TouchScreenDevice
* Method: create
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_quality_util_UnixTouchScreenDevice_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: quality_util_TouchScreenDevice
* Method: destroy
* Signature: (I)V
*/
JNIEXPORT jint JNICALL Java_quality_util_UnixTouchScreenDevice_destroy(JNIEnv *env,
jobject o,
jint fd) {
if (ioctl(fd, UI_DEV_DESTROY) < 0) {
return -1;
}
return close(fd);
}
/*
* Class: quality_util_TouchScreenDevice
* Method: clickImpl
* Signature: (IIII)V
*/
// todo return code with checked exception
JNIEXPORT jint JNICALL Java_quality_util_UnixTouchScreenDevice_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: quality_util_TouchScreenDevice
* Method: moveImpl
* Signature: (IIIIII)V
*/
JNIEXPORT jint JNICALL Java_quality_util_UnixTouchScreenDevice_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

@@ -1,84 +0,0 @@
#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_quality_util_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_quality_util_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);
}

View File

@@ -1,338 +0,0 @@
package quality.event;
import org.apache.commons.lang3.SystemUtils;
import org.junit.Test;
import quality.util.UnixTouchRobot;
import quality.util.TouchRobot;
import quality.util.WindowsTouchRobot;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
public class TouchScreenEventsTest {
static final int TIMEOUT = 2;
// 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;
@Test
public void testTouchClick() throws Exception {
runTest(new TouchClickSuite());
}
@Test
public void testTouchMove() throws Exception {
runTest(new TouchMoveSuite());
}
@Test
public void testTouchTinyMove() throws Exception {
runTest(new TouchTinyMoveSuite());
}
@Test
public void testTouchHorizontalScroll() throws Exception {
runTest(new TouchAxesScrollSuite(TouchAxesScrollSuite.AXIS.X));
}
@Test
public void testTouchVerticalScroll() throws Exception {
runTest(new TouchAxesScrollSuite(TouchAxesScrollSuite.AXIS.Y));
}
private static void runTest(TouchTestSuite suite) throws Exception {
GUI gui = new GUI();
try {
TouchRobot robot = getTouchRobot();
SwingUtilities.invokeAndWait(gui::createAndShow);
suite.addListener(gui.frame);
robot.waitForIdle();
suite.perform(gui, robot);
robot.waitForIdle();
suite.passed();
} finally {
SwingUtilities.invokeLater(() -> {
if (gui.frame != null) {
gui.frame.dispose();
}
});
}
}
private static TouchRobot getTouchRobot() throws IOException, AWTException {
if (SystemUtils.IS_OS_UNIX) {
return new UnixTouchRobot();
} else if (SystemUtils.IS_OS_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 perform(GUI gui, TouchRobot robot) throws IOException;
void passed() throws InterruptedException;
void addListener(JFrame frame);
}
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 void passed() throws InterruptedException {
latch.await(TouchScreenEventsTest.TIMEOUT, TimeUnit.SECONDS);
if (!pressed || !released || !clicked) {
String error = (pressed ? "" : "pressed: " + pressed) +
(released ? "" : ", released: " + released) +
(clicked ? "" : ", clicked: " + clicked);
throw new RuntimeException("Touch click failed: " + error);
}
}
@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 void passed() throws InterruptedException {
latch.await(TouchScreenEventsTest.TIMEOUT, TimeUnit.SECONDS);
if (!begin || !update || !end) {
String error = (begin ? "" : "begin: " + begin) +
(update ? "" : ", update: " + update) +
(end ? "" : ", end: " + end);
throw new RuntimeException("Touch move failed: " + error);
}
}
@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 void passed() throws InterruptedException {
latch.await(TouchScreenEventsTest.TIMEOUT, TimeUnit.SECONDS);
if (scroll || !click) {
String error = (scroll ? "scroll " + scroll : "") +
(click ? "" : ", click: " + click);
throw new RuntimeException("Tiny touch move failed: " + error);
}
}
@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 void passed() throws InterruptedException {
latch.await(TouchScreenEventsTest.TIMEOUT, TimeUnit.SECONDS);
String error = "horizontal " + horizontal + ", vertical: " + vertical;
switch (axis) {
case X:
if (!horizontal || vertical) {
throw new RuntimeException("Touch axes failed: " + error);
}
break;
case Y:
if (horizontal || !vertical) {
throw new RuntimeException("Touch axes failed: " + error);
}
break;
}
}
@Override
public void addListener(JFrame frame) {
frame.addMouseWheelListener(this);
}
}

View File

@@ -1,17 +0,0 @@
package quality.util;
import java.awt.*;
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

@@ -1,26 +0,0 @@
package quality.util;
import java.awt.*;
import java.io.IOException;
public class UnixTouchRobot extends TouchRobot {
private UnixTouchScreenDevice device;
public UnixTouchRobot() throws AWTException, IOException {
super();
device = new UnixTouchScreenDevice();
}
public UnixTouchRobot(GraphicsDevice screen) throws AWTException, IOException {
super(screen);
device = new UnixTouchScreenDevice();
}
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

@@ -1,68 +0,0 @@
package quality.util;
import java.awt.*;
import java.io.IOException;
public class UnixTouchScreenDevice implements AutoCloseable {
// TODO add product id
private int width;
private int height;
private int fileDescriptor;
static {
System.loadLibrary("touchscreen_device");
}
public UnixTouchScreenDevice() throws IOException {
this(Toolkit.getDefaultToolkit().getScreenSize().width, Toolkit.getDefaultToolkit().getScreenSize().height);
}
public UnixTouchScreenDevice(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

@@ -1,34 +0,0 @@
package quality.util;
import java.awt.*;
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);
}