/* * $Id$ * * Copyright (C) 2003-2015 JNode.org * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library 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 Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; If not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ package org.jnode.driver.input; import java.nio.ByteBuffer; import java.nio.channels.ByteChannel; import java.util.ArrayList; import org.jnode.driver.Driver; import org.jnode.system.event.SystemEvent; import org.jnode.util.Queue; import org.jnode.util.QueueProcessor; import org.jnode.util.QueueProcessorThread; public abstract class AbstractInputDriver<E extends SystemEvent> extends Driver { private final ArrayList<SystemListener> listeners = new ArrayList<SystemListener>(); private QueueProcessorThread<E> eventQueueThread; private Queue<E> eventQueue = new Queue<E>(); private InputDaemon daemon; protected final void startDispatcher(String id) { this.eventQueue = new Queue<E>(); this.daemon = new InputDaemon(id + "-daemon"); this.daemon.start(); this.eventQueueThread = new QueueProcessorThread<E>(id + "-dispatcher", eventQueue, new SystemEventDispatcher()); this.eventQueueThread.start(); } protected final void stopDispatcher() { if (eventQueueThread != null) { eventQueueThread.stopProcessor(); } this.eventQueueThread = null; InputDaemon daemon = this.daemon; this.daemon = null; if (daemon != null) { daemon.setRunningState(false); daemon.interrupt(); } } class SystemEventDispatcher implements QueueProcessor<E> { /** * @see org.jnode.util.QueueProcessor#process(java.lang.Object) */ public void process(E event) throws Exception { for (SystemListener l : listeners) { sendEvent(l, event); if (event.isConsumed()) { break; } } } } protected abstract void sendEvent(SystemListener<E> l, E e); /** * Add a pointer listener * * @param l */ public final synchronized void addListener(SystemListener<E> l) { listeners.add(l); } /** * Remove a pointer listener * * @param l */ public final synchronized void removeListener(SystemListener<E> l) { listeners.remove(l); } /** * Claim to be the preferred listener. * The given listener must have been added by addKeyboardListener. * <b>This is not checked by a security manager</b> * * @param l */ protected final synchronized void setPreferredListener(SystemListener<E> l) { if (listeners.remove(l)) { listeners.add(0, l); } } protected abstract E handleScancode(byte b); /** * Gets the byte channel. This is implementation specific * * @return The byte channel */ protected abstract ByteChannel getChannel(); /** * InputDaemon that translates scancodes to SystemEvents and dispatches those events. */ class InputDaemon extends Thread { private boolean runningState; public InputDaemon(String name) { super(name); } public void run() { processChannel(); } /** * Read scancodes from the input channel and dispatch them as events. */ final void processChannel() { final ByteBuffer buf = ByteBuffer.allocate(1); final ByteChannel channel = getChannel(); setRunningState(true); while ((channel != null) && channel.isOpen() && isRunningState()) { try { buf.rewind(); if (channel.read(buf) != 1) { continue; } final byte scancode = buf.get(0); E event = handleScancode(scancode); if ((event != null) && !event.isConsumed()) { if (eventQueue.isClosed()) { // the queue is closed : it usually happen while JNode is halting // simply stop processing the events break; } eventQueue.add(event); } } catch (Throwable ex) { ex.printStackTrace(); } } } public synchronized boolean isRunningState() { return runningState; } public synchronized void setRunningState(boolean runningState) { this.runningState = runningState; } } }