/* * 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 <http://www.gnu.org/licenses/>. * * Copyright (C) 2010-2017 Martin Berglund */ package com.googlecode.lanterna.gui2; import java.io.EOFException; import java.io.IOException; import java.util.concurrent.CountDownLatch; /** * Default implementation of TextGUIThread, this class runs the GUI event processing on a dedicated thread. The GUI * needs to be explicitly started in order for the event processing loop to begin, so you must call {@code start()} * for this. The GUI thread will stop if {@code stop()} is called, the input stream returns EOF or an exception is * thrown from inside the event handling loop. * <p> * Here is an example of how to use this {@code TextGUIThread}: * <pre> * {@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 ... * } * </pre> * @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); } } }