/* * $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.console.spi; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Stack; import javax.naming.NameNotFoundException; import org.apache.log4j.Logger; import org.jnode.bootlog.BootLogInstance; import org.jnode.driver.ApiNotFoundException; import org.jnode.driver.Device; import org.jnode.driver.DeviceListener; import org.jnode.driver.DeviceManager; import org.jnode.driver.console.Console; import org.jnode.driver.console.ConsoleException; import org.jnode.driver.console.ConsoleManager; import org.jnode.driver.input.KeyboardAPI; import org.jnode.driver.input.KeyboardEvent; import org.jnode.driver.input.PointerAPI; import org.jnode.driver.input.PointerEvent; import org.jnode.naming.InitialNaming; import org.jnode.system.event.FocusEvent; /** * @author epr */ public abstract class AbstractConsoleManager implements ConsoleManager { /** * My logger */ protected final Logger log = Logger.getLogger(getClass()); /** * All registered consoles */ private final Map<String, Console> consoles = new HashMap<String, Console>(); /** * The current console */ private Console current; /** * The keyboard api i'm using */ private KeyboardAPI kbApi; /** * The pointer devices that I'm listener on */ private final LinkedList<Device> pointerDevs = new LinkedList<Device>(); /** * The device manager i'm using */ private final DeviceManager devMan; /** * The holder for the context console */ private static final InheritableThreadLocal<Console> contextConsole = new InheritableThreadLocal<Console>(); private AbstractConsoleManager parent; private final Map<Integer, Stack<Console>> stackMap = new HashMap<Integer, Stack<Console>>(); private Stack<Console> currentStack; /** * Initialize a new instance */ public AbstractConsoleManager() throws ConsoleException { try { devMan = InitialNaming.lookup(DeviceManager.NAME); openInput(devMan); } catch (NameNotFoundException ex) { throw new ConsoleException("DeviceManager not found", ex); } current = null; } protected final void initializeKeyboard(Device kbDev) { try { this.kbApi = kbDev.getAPI(KeyboardAPI.class); this.kbApi.addKeyboardListener(this); } catch (ApiNotFoundException ex) { BootLogInstance.get().error("KeyboardAPI not found", ex); } } protected KeyboardAPI getKeyboardApi() { return kbApi; } /** * Add a pointer device * * @param pDev */ protected final void addPointerDevice(Device pDev) { try { final PointerAPI pApi = pDev.getAPI(PointerAPI.class); pointerDevs.add(pDev); pApi.addPointerListener(this); } catch (ApiNotFoundException ex) { BootLogInstance.get().error("PointerAPI not found", ex); } } protected void openInput(DeviceManager devMan) { final Collection<Device> kbs = devMan.getDevicesByAPI(KeyboardAPI.class); if (!kbs.isEmpty()) { initializeKeyboard((Device) kbs.iterator().next()); } final Collection<Device> pointers = devMan.getDevicesByAPI(PointerAPI.class); for (Device dev : pointers) { addPointerDevice(dev); } devMan.addListener(new DevManListener()); } /** * Remove a pointer device * * @param pDev */ final void removePointer(Device pDev) { if (pointerDevs.remove(pDev)) { try { final PointerAPI pApi = pDev.getAPI(PointerAPI.class); pApi.removePointerListener(this); } catch (ApiNotFoundException ex) { BootLogInstance.get().error("PointerAPI not found", ex); } } } /** * Gets the console with the given index * * @param name * @return The console */ public Console getConsole(String name) { return (Console) consoles.get(name); } /** * Gets the console that "hosts" the current thread. * * @return Console */ public Console getContextConsole() { Console c = contextConsole.get(); if (c == null) { c = getFocus(); contextConsole.set(c); } return c; } /** * Gets the currently focused console. * * @return Console */ public Console getFocus() { return current; } /** * Focus the given console * * @param console */ public synchronized void focus(Console console) { if (this.current != null && this.current != console) { this.current.focusLost(new FocusEvent(FocusEvent.FOCUS_LOST)); } this.current = console; if (this.current != null) { current.focusGained(new FocusEvent(FocusEvent.FOCUS_GAINED)); } } public void printConsoles(PrintWriter pw) { ArrayList<Integer> list = new ArrayList<Integer>(); list.addAll(stackMap.keySet()); Collections.sort(list); for (Integer key : list) { pw.println("Screen of " + KeyEvent.getKeyText(key) + ':'); Stack<Console> stack = stackMap.get(key); int t_ind = stack.size(); for (int i = t_ind; i-- > 0;) { Console console = stack.get(i); String prefix = console == current ? " > " : i == t_ind - 1 ? " * " : " "; pw.println(prefix + console.getConsoleName()); } } } public Console getConsoleByAccelerator(int keyCode) { Stack<Console> stack = stackMap.get(keyCode); if (stack != null && !stack.empty()) { currentStack = stack; return stack.peek(); } return null; } protected void setAccelerator(Console console) { for (int i = 0; i < 12; i++) { final int keyCode = KeyEvent.VK_F1 + i; Stack<Console> stack = stackMap.get(keyCode); if (stack == null) { stack = new Stack<Console>(); } if (stack.empty()) { stackMap.put(keyCode, stack); stack.push(console); currentStack = stack; return; } } } protected void stackConsole(Console console) { if (currentStack != null) currentStack.push(console); } /** * Just keeping track of the One previous console will lead to lots of * problems. We need a stack, at least. Currently it is the client's * responsibility to choose the new console. * * @param console */ public void unregisterConsole(Console console) { log.debug("unregisterConsole(" + console.getConsoleName() + ')'); if (contextConsole.get() == console) { contextConsole.set(null); } if (current == console) { console.focusLost(new FocusEvent(FocusEvent.FOCUS_LOST)); } consoles.remove(console.getConsoleName()); if (currentStack != null && !currentStack.empty() && currentStack.peek() == console) { currentStack.pop(); if (!currentStack.empty()) { current = currentStack.peek(); focus(current); } else { Integer last_key = null; for (Iterator<Map.Entry<Integer, Stack<Console>>> it = stackMap.entrySet().iterator(); it.hasNext();) { Map.Entry<Integer, Stack<Console>> entry = it.next(); if (entry.getValue().equals(currentStack)) { last_key = entry.getKey(); it.remove(); break; } } if (!stackMap.isEmpty()) { Integer new_key = null; List<Integer> keys = new ArrayList<Integer>(stackMap.keySet()); Collections.sort(keys); if (last_key == null) { new_key = keys.get(0); } else { Collections.reverse(keys); for (Integer k : keys) { if (k < last_key) { new_key = k; break; } } if (new_key == null) { new_key = keys.get(keys.size() - 1); } } currentStack = stackMap.get(new_key); current = currentStack.peek(); focus(current); } } } if (current == console) { current = null; if (!consoles.isEmpty()) { focus(consoles.values().iterator().next()); } } if (parent != null && consoles.isEmpty()) { handleFocus(); } } public void registerConsole(Console console) { consoles.put(console.getConsoleName(), console); if (current == null) { current = console; current.focusGained(new FocusEvent(FocusEvent.FOCUS_GAINED)); } if (contextConsole.get() == null) { contextConsole.set(console); } } private void handleFocus() { if (consoles.isEmpty()) { if (parent != null) parent.handleFocus(); } else { Console c = getFocus(); if (c == null) c = consoles.values().iterator().next(); focus(c); } } /** * Close all consoles. */ public void closeAll() { for (Iterator<Console> i = consoles.values().iterator(); i.hasNext();) { Console console = (Console) i.next(); i.remove(); // remove from iterator before closing to avoid // concurrent modification console.close(); } } /** * @see org.jnode.driver.input.KeyboardListener#keyPressed(org.jnode.driver.input.KeyboardEvent) */ public void keyPressed(KeyboardEvent event) { if ((event.getModifiers() & InputEvent.ALT_DOWN_MASK) != 0) { final int keyCode = event.getKeyCode(); final Console c = getConsoleByAccelerator(keyCode); if (c != null) { focus(c); event.consume(); } } if (!event.isConsumed() && (current != null)) { current.keyPressed(event); } } /** * @see org.jnode.driver.input.KeyboardListener#keyReleased(org.jnode.driver.input.KeyboardEvent) */ public void keyReleased(KeyboardEvent event) { if (current != null) { current.keyReleased(event); } } /** * @see org.jnode.driver.input.PointerListener#pointerStateChanged(org.jnode.driver.input.PointerEvent) */ public void pointerStateChanged(PointerEvent event) { if (current != null) { current.pointerStateChanged(event); } } public Set<String> getConsoleNames() { return new HashSet<String>(consoles.keySet()); } public Collection<Console> getConsoles() { return new ArrayList<Console>(consoles.values()); } public AbstractConsoleManager getParent() { return parent; } public void setParent(ConsoleManager parent) { this.parent = (AbstractConsoleManager) parent; } void restack(final AbstractConsole console) { int accel = console.getAcceleratorKeyCode(); if (accel == 0) return; //remove console for (Iterator<Integer> iter = stackMap.keySet().iterator(); iter.hasNext();) { Integer key = iter.next(); if (key == accel) return; //no restack needed Stack<Console> stack = stackMap.get(key); if (stack.contains(console)) { stack.remove(console); if (stack.empty()) iter.remove(); break; } } //add the console to the specified screen Stack<Console> stack = stackMap.get(accel); if (stack == null) { stack = new Stack<Console>(); stackMap.put(accel, stack); } stack.push(console); } /** * This listener looks for registration of a keyboard device. * * @author epr */ class DevManListener implements DeviceListener { /** * @param device * @see org.jnode.driver.DeviceListener#deviceStarted(org.jnode.driver.Device) */ public void deviceStarted(Device device) { if (device.implementsAPI(KeyboardAPI.class)) { initializeKeyboard(device); } else if (device.implementsAPI(PointerAPI.class)) { addPointerDevice(device); } } /** * @param device * @see org.jnode.driver.DeviceListener#deviceStop(org.jnode.driver.Device) */ public void deviceStop(Device device) { if (device.implementsAPI(PointerAPI.class)) { removePointer(device); } } } }