/*
* Copyright (c) 2013, 2014, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
package com.sun.glass.ui.monocle;
import com.sun.glass.events.TouchEvent;
import com.sun.glass.ui.GestureSupport;
import com.sun.glass.ui.TouchInputSupport;
import com.sun.glass.ui.View;
import com.sun.glass.ui.Window;
import java.security.AccessController;
import java.security.PrivilegedAction;
Processes touch input events based on changes to touch state. Not
thread-safe.
/**
* Processes touch input events based on changes to touch state. Not
* thread-safe.
*/
class TouchInput {
This property determines the sensitivity of move events from touch. The
bigger the value the less sensitive is the touch screen. In practice move
events with a delta smaller then the value of this property will be
filtered out.The value of the property is in pixels.
/**
* This property determines the sensitivity of move events from touch. The
* bigger the value the less sensitive is the touch screen. In practice move
* events with a delta smaller then the value of this property will be
* filtered out.The value of the property is in pixels.
*/
private final int touchRadius = AccessController.doPrivileged(
(PrivilegedAction<Integer>) () -> Integer.getInteger(
"monocle.input.touchRadius", 20)
);
private static TouchInput instance = new TouchInput();
private TouchPipeline basePipeline;
private TouchState state = new TouchState();
private final GestureSupport gestures = new GestureSupport(false);
private final TouchInputSupport touches =
new TouchInputSupport(gestures.createTouchCountListener(), false);
static TouchInput getInstance() {
return instance;
}
private TouchInput() {
}
Gets the base touch filter pipeline common to all touch devices /** Gets the base touch filter pipeline common to all touch devices */
TouchPipeline getBasePipeline() {
if (basePipeline == null) {
basePipeline = new TouchPipeline();
String[] touchFilterNames = AccessController.doPrivileged(
(PrivilegedAction<String>) () -> System.getProperty(
"monocle.input.touchFilters",
"SmallMove")
).split(",");
if (touchFilterNames != null) {
for (String touchFilterName : touchFilterNames) {
basePipeline.addNamedFilter(touchFilterName.trim());
}
}
}
return basePipeline;
}
Copies the current state into the TouchState provided.
Params: - result – target into which to copy the touch state
/** Copies the current state into the TouchState provided.
*
* @param result target into which to copy the touch state
*/
void getState(TouchState result) {
state.copyTo(result);
}
Called from the input processor to update the touch state and send
touch and mouse events.
Params: - newState – The updated touch state
/** Called from the input processor to update the touch state and send
* touch and mouse events.
*
* @param newState The updated touch state
*/
void setState(TouchState newState) {
if (MonocleSettings.settings.traceEvents) {
MonocleTrace.traceEvent("Set %s", newState);
}
newState.sortPointsByID();
newState.assignPrimaryID();
// Get the cached window for the old state and compute the window for
// the new state
MonocleWindow oldWindow = state.getWindow(false, null);
boolean recalculateWindow = state.getPointCount() == 0;
MonocleWindow window = newState.getWindow(recalculateWindow, oldWindow);
View oldView = oldWindow == null ? null : oldWindow.getView();
View view = window == null ? null : window.getView();
if (!newState.equalsSorted(state)) {
// Post touch events
if (view != oldView) {
postTouchEvent(state, TouchEvent.TOUCH_RELEASED);
postTouchEvent(newState,
TouchEvent.TOUCH_PRESSED);
} else if (view != null) {
postTouchEvent(window, view, newState);
}
// Post mouse events
MouseInputSynthesizer.getInstance().setState(newState);
}
newState.copyTo(state);
newState.clearWindow();
}
private void dispatchPoint(Window window, View view, int state,
int id, int x, int y) {
touches.notifyNextTouchEvent(view, state, id,
x - window.getX(), y - window.getY(),
x, y);
}
private void postPoints(Window window, View view,
int[] states, int[] ids, int[] xs, int[] ys) {
RunnableProcessor.runLater(() -> {
touches.notifyBeginTouchEvent(view, 0, true, states.length);
for (int i = 0; i < states.length; i++) {
dispatchPoint(window, view, states[i], ids[i],
xs[i], ys[i]);
}
touches.notifyEndTouchEvent(view);
});
}
private void postPoint(Window window, View view,
int state, TouchState.Point p) {
int id = p.id;
int x = p.x;
int y = p.y;
RunnableProcessor.runLater(() -> {
touches.notifyBeginTouchEvent(view, 0, true, 1);
dispatchPoint(window, view, state, id, x, y);
touches.notifyEndTouchEvent(view);
});
}
private void postNoPoints(View view) {
RunnableProcessor.runLater(() -> {
touches.notifyBeginTouchEvent(view, 0, true, 0);
touches.notifyEndTouchEvent(view);
});
}
Sends the same event type for all points in the given state
Params: - state – The state for which to process all points
- eventType – The type of TouchEvent to send (e.g. TouchEvent.PRESSED)
/** Sends the same event type for all points in the given state
*
* @param state The state for which to process all points
* @param eventType The type of TouchEvent to send (e.g. TouchEvent.PRESSED)
*/
private void postTouchEvent(TouchState state, int eventType) {
Window window = state.getWindow(false, null);
View view = window == null ? null : window.getView();
if (view != null) {
switch (state.getPointCount()) {
case 0:
postNoPoints(view);
break;
case 1:
postPoint(window, view, eventType, state.getPoint(0));
break;
default: {
int count = state.getPointCount();
int[] states = new int[count];
int[] ids = new int[count];
int[] xs = new int[count];
int[] ys = new int[count];
for (int i = 0; i < count; i++) {
states[i] = eventType;
TouchState.Point p = state.getPoint(i);
ids[i] = p.id;
xs[i] = p.x;
ys[i] = p.y;
}
postPoints(window, view, states, ids, xs, ys);
}
}
}
}
Sends updated touch points within the same View as last processed
touch points.
Params: - window – The current Window
- view – The current View
- newState – The updated touch points
/** Sends updated touch points within the same View as last processed
* touch points.
*
* @param window The current Window
* @param view The current View
* @param newState The updated touch points
*/
private void postTouchEvent(MonocleWindow window,
View view,
TouchState newState) {
int count = countEvents(newState);
switch (count) {
case 0:
postNoPoints(view);
break;
case 1:
if (state.getPointCount() == 1) {
// There is one point and it already existed
TouchState.Point oldPoint = state.getPoint(0);
TouchState.Point newPoint = newState.getPointForID(
oldPoint.id);
if (newPoint != null) {
if (newPoint.x == oldPoint.x
&& newPoint.y == oldPoint.y) {
postPoint(window, view, TouchEvent.TOUCH_STILL, newPoint);
} else {
postPoint(window, view, TouchEvent.TOUCH_MOVED, newPoint);
}
} else {
postPoint(window, view, TouchEvent.TOUCH_RELEASED, oldPoint);
}
} else {
// There is one point and it is newly pressed
postPoint(window, view, TouchEvent.TOUCH_PRESSED, newState.getPoint(0));
}
break;
default: {
int[] states = new int[count];
int[] ids = new int[count];
int[] xs = new int[count];
int[] ys = new int[count];
for (int i = 0; i < state.getPointCount(); i++) {
TouchState.Point oldPoint = state.getPoint(i);
TouchState.Point newPoint = newState.getPointForID(
oldPoint.id);
if (newPoint != null) {
ids[i] = newPoint.id;
xs[i] = newPoint.x;
ys[i] = newPoint.y;
if (newPoint.x == oldPoint.x
&& newPoint.y == oldPoint.y) {
states[i] = TouchEvent.TOUCH_STILL;
} else {
states[i] = TouchEvent.TOUCH_MOVED;
}
} else {
states[i] = TouchEvent.TOUCH_RELEASED;
ids[i] = oldPoint.id;
xs[i] = oldPoint.x;
ys[i] = oldPoint.y;
}
}
// Once we have dealt with updates to old points, all that are left
// are new points.
for (int i = 0, j = state.getPointCount();
i < newState.getPointCount(); i++) {
TouchState.Point newPoint = newState.getPoint(i);
TouchState.Point oldPoint = state.getPointForID(
newPoint.id);
if (oldPoint == null) {
states[j] = TouchEvent.TOUCH_PRESSED;
ids[j] = newPoint.id;
xs[j] = newPoint.x;
ys[j] = newPoint.y;
j++;
}
}
postPoints(window, view, states, ids, xs, ys);
}
}
}
Calculate the number of touch point events that will be sent by
dispatchPoints(). This is the union of the touch points in the old and
new states.
/** Calculate the number of touch point events that will be sent by
* dispatchPoints(). This is the union of the touch points in the old and
* new states.
*/
private int countEvents(TouchState newState) {
int count = state.getPointCount();
for (int i = 0; i < newState.getPointCount(); i++) {
TouchState.Point newPoint = newState.getPoint(i);
TouchState.Point oldPoint = state.getPointForID(newPoint.id);
if (oldPoint == null) {
count ++;
}
}
return count;
}
int getTouchRadius() {
return touchRadius;
}
}