/*
* This file is part of lanterna (http://code.google.com/p/lanterna/).
*
* lanterna 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 3 of the License, or
* (at your option) any later version.
*
* This program 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 program. If not, see
* Here is an example of how to use this {@code TextGUIThread}: *
* {@code * MultiWindowTextGUI textGUI = new MultiWindowTextGUI(new SeparateTextGUIThread.Factory(), screen); * // ... add components ... * ((AsynchronousTextGUIThread)textGUI.getGUIThread()).start(); * // ... this thread will continue while the GUI runs on a separate thread ... * } ** @see TextGUIThread * @see SameTextGUIThread * @author Martin */ public class SeparateTextGUIThread extends AbstractTextGUIThread implements AsynchronousTextGUIThread { private volatile State state; private final Thread textGUIThread; private final CountDownLatch waitLatch; private SeparateTextGUIThread(TextGUI textGUI) { super(textGUI); this.waitLatch = new CountDownLatch(1); this.textGUIThread = new Thread("LanternaGUI") { @Override public void run() { mainGUILoop(); } }; state = State.CREATED; } @Override public void start() { textGUIThread.start(); state = State.STARTED; } @Override public void stop() { if(state != State.STARTED) { return; } state = State.STOPPING; } @Override public void waitForStop() throws InterruptedException { waitLatch.await(); } @Override public State getState() { return state; } @Override public Thread getThread() { return textGUIThread; } @Override public void invokeLater(Runnable runnable) throws IllegalStateException { if(state != State.STARTED) { throw new IllegalStateException("Cannot schedule " + runnable + " for execution on the TextGUIThread " + "because the thread is in " + state + " state"); } super.invokeLater(runnable); } private void mainGUILoop() { try { //Draw initial screen, after this only draw when the GUI is marked as invalid try { textGUI.updateScreen(); } catch(IOException e) { exceptionHandler.onIOException(e); } catch(RuntimeException e) { exceptionHandler.onRuntimeException(e); } while(state == State.STARTED) { try { if (!processEventsAndUpdate()) { try { Thread.sleep(1); } catch(InterruptedException ignored) {} } } catch(EOFException e) { stop(); break; //Break out quickly from the main loop } catch(IOException e) { if(exceptionHandler.onIOException(e)) { stop(); break; } } catch(RuntimeException e) { if(exceptionHandler.onRuntimeException(e)) { stop(); break; } } } } finally { state = State.STOPPED; waitLatch.countDown(); } } /** * Factory class for creating SeparateTextGUIThread objects */ public static class Factory implements TextGUIThreadFactory { @Override public TextGUIThread createTextGUIThread(TextGUI textGUI) { return new SeparateTextGUIThread(textGUI); } } }