com.sun.glass.ui.monocle.LinuxEventBuffer Maven / Gradle / Ivy
/*
* Copyright (c) 2013, 2022, 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 java.nio.ByteBuffer;
import java.nio.ByteOrder;
/**
* A buffer holding raw Linux input events waiting to be processed
*/
class LinuxEventBuffer {
interface EventStruct {
int getTypeIndex();
int getCodeIndex();
int getValueIndex();
int getSize();
}
class EventStruct32Bit implements EventStruct {
@Override
public int getTypeIndex() { return 8; }
@Override
public int getCodeIndex() { return 10; }
@Override
public int getValueIndex() { return 12; }
@Override
public int getSize() { return 16; }
}
class EventStruct64Bit implements EventStruct {
@Override
public int getTypeIndex() { return 16; }
@Override
public int getCodeIndex() { return 18; }
@Override
public int getValueIndex() { return 20; }
@Override
public int getSize() { return 24; }
}
/**
* EVENT_BUFFER_SIZE controls the maximum number of event lines that can be
* processed per device in one pulse. This value must be greater than the
* number of lines in the largest event including the terminating EV_SYN
* SYN_REPORT. However it should not be too large or a flood of events will
* prevent rendering from happening until the buffer is full.
*/
private static final int EVENT_BUFFER_SIZE = 1000;
private final ByteBuffer bb;
private final EventStruct eventStruct;
private int positionOfLastSync;
private int currentPosition;
private int mark;
LinuxEventBuffer(int osArchBits) {
eventStruct = osArchBits == 64 ? new EventStruct64Bit() : new EventStruct32Bit();
bb = ByteBuffer.allocate(eventStruct.getSize() * EVENT_BUFFER_SIZE);
bb.order(ByteOrder.nativeOrder());
}
int getEventSize() {
return eventStruct.getSize();
}
/**
* Adds a raw Linux event to the buffer. Blocks if the buffer is full.
* Checks whether this is a SYN SYN_REPORT event terminator.
*
* @param event A ByteBuffer containing the event to be added.
* @return true if the event was "SYN SYN_REPORT", false otherwise
* @throws InterruptedException if our thread was interrupted while waiting
* for the buffer to empty.
*/
synchronized boolean put(ByteBuffer event) throws
InterruptedException {
boolean isSync = event.getShort(eventStruct.getTypeIndex()) == 0
&& event.getInt(eventStruct.getValueIndex()) == 0;
while (bb.limit() - bb.position() < event.limit()) {
// Block if bb is full. This should be the
// only time this thread waits for anything
// except for more event lines.
if (MonocleSettings.settings.traceEventsVerbose) {
MonocleTrace.traceEvent(
"Event buffer %s is full, waiting for some space to become available",
bb);
// wait for half the space to be available, to avoid excessive context switching?
}
wait();
}
if (isSync) {
positionOfLastSync = bb.position();
}
bb.put(event);
if (MonocleSettings.settings.traceEventsVerbose) {
int index = bb.position() - eventStruct.getSize();
MonocleTrace.traceEvent("Read %s [index=%d]",
getEventDescription(index), index);
}
return isSync;
}
synchronized void startIteration() {
currentPosition = 0;
mark = 0;
if (MonocleSettings.settings.traceEventsVerbose) {
MonocleTrace.traceEvent("Processing %s [index=%d]", getEventDescription(), currentPosition);
}
}
synchronized void compact() {
positionOfLastSync -= currentPosition;
int newLimit = bb.position();
bb.position(currentPosition);
bb.limit(newLimit);
bb.compact();
if (MonocleSettings.settings.traceEventsVerbose) {
MonocleTrace.traceEvent("Compacted event buffer %s", bb);
}
// If put() is waiting for space in the buffer, wake it up
notifyAll();
}
/**
* Returns the type of the current event line. Call from the application
* thread.
*
* @return the type of the current event line
*/
synchronized short getEventType() {
return bb.getShort(currentPosition + eventStruct.getTypeIndex());
}
/**
* Returns the code of the current event line. Call from the application
* thread.
*
* @return the code of the event line
*/
short getEventCode() {
return bb.getShort(currentPosition + eventStruct.getCodeIndex());
}
/**
* Returns the value of the current event line. Call from the application
* thread.
*
* @return the value of the current event line
*/
synchronized int getEventValue() {
return bb.getInt(currentPosition + eventStruct.getValueIndex());
}
/**
* Returns a string describing the current event. Call from the application
* thread.
*
* @return a string describing the event
*/
synchronized String getEventDescription() {
return getEventDescription(currentPosition);
}
private synchronized String getEventDescription(int position) {
short type = bb.getShort(position + eventStruct.getTypeIndex());
short code = bb.getShort(position + eventStruct.getCodeIndex());
int value = bb.getInt(position + eventStruct.getValueIndex());
String typeStr = LinuxInput.typeToString(type);
return typeStr + " " + LinuxInput.codeToString(typeStr, code) + " " + value;
}
/**
* Advances to the next event line. Call from the application thread.
*/
synchronized void nextEvent() {
if (currentPosition > positionOfLastSync) {
throw new IllegalStateException("Cannot advance past the last" +
" EV_SYN EV_SYN_REPORT 0");
}
currentPosition += eventStruct.getSize();
if (MonocleSettings.settings.traceEventsVerbose && hasNextEvent()) {
MonocleTrace.traceEvent("Processing %s [index=%d]",
getEventDescription(), currentPosition);
}
}
/**
* Sets a mark on the buffer. A future call to reset() will return to this
* point.
*/
synchronized void mark() {
mark = currentPosition;
}
/**
* Returns iteration to the event set previously in a call to mark(), or to
* the beginning of the buffer if no call to mark() was made.
*/
synchronized void reset() {
currentPosition = mark;
}
/**
* Returns true iff another event line is available AND it is part of a
* complete event. Call from the application thread.
*/
synchronized boolean hasNextEvent() {
return currentPosition <= positionOfLastSync;
}
/**
* Returns true iff another event line is available. Call on the
* application thread.
*/
synchronized boolean hasData() {
return bb.position() != 0;
}
}