/* * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. * * * * * * * * * * * * * * * * * * * * */ 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 { public int getTypeIndex() { return 8; } public int getCodeIndex() { return 10; } public int getValueIndex() { return 12; } public int getSize() { return 16; } } class EventStruct64Bit implements EventStruct { public int getTypeIndex() { return 16; } public int getCodeIndex() { return 18; } public int getValueIndex() { return 20; } 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.getInt(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; } }