Commit | Line | Data |
---|---|---|
a3b510ab NR |
1 | /* |
2 | * This file is part of lanterna (http://code.google.com/p/lanterna/). | |
3 | * | |
4 | * lanterna is free software: you can redistribute it and/or modify | |
5 | * it under the terms of the GNU Lesser General Public License as published by | |
6 | * the Free Software Foundation, either version 3 of the License, or | |
7 | * (at your option) any later version. | |
8 | * | |
9 | * This program is distributed in the hope that it will be useful, | |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | * GNU Lesser General Public License for more details. | |
13 | * | |
14 | * You should have received a copy of the GNU Lesser General Public License | |
15 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
16 | * | |
17 | * Copyright (C) 2010-2015 Martin | |
18 | */ | |
19 | package com.googlecode.lanterna.gui2; | |
20 | ||
21 | import java.io.EOFException; | |
22 | import java.io.IOException; | |
23 | import java.util.concurrent.CountDownLatch; | |
24 | ||
25 | /** | |
26 | * Default implementation of TextGUIThread, this class runs the GUI event processing on a dedicated thread. The GUI | |
27 | * needs to be explicitly started in order for the event processing loop to begin, so you must call {@code start()} | |
28 | * for this. The GUI thread will stop if {@code stop()} is called, the input stream returns EOF or an exception is | |
29 | * thrown from inside the event handling loop. | |
30 | * <p> | |
31 | * Here is an example of how to use this {@code TextGUIThread}: | |
32 | * <pre> | |
33 | * {@code | |
34 | * MultiWindowTextGUI textGUI = new MultiWindowTextGUI(new SeparateTextGUIThread.Factory(), screen); | |
35 | * // ... add components ... | |
36 | * ((AsynchronousTextGUIThread)textGUI.getGUIThread()).start(); | |
37 | * // ... this thread will continue while the GUI runs on a separate thread ... | |
38 | * } | |
39 | * </pre> | |
40 | * @see TextGUIThread | |
41 | * @see SameTextGUIThread | |
42 | * @author Martin | |
43 | */ | |
44 | public class SeparateTextGUIThread extends AbstractTextGUIThread implements AsynchronousTextGUIThread { | |
45 | private volatile State state; | |
46 | private final Thread textGUIThread; | |
47 | private final CountDownLatch waitLatch; | |
48 | ||
49 | private SeparateTextGUIThread(TextGUI textGUI) { | |
50 | super(textGUI); | |
51 | this.waitLatch = new CountDownLatch(1); | |
52 | this.textGUIThread = new Thread("LanternaGUI") { | |
53 | @Override | |
54 | public void run() { | |
55 | mainGUILoop(); | |
56 | } | |
57 | }; | |
58 | state = State.CREATED; | |
59 | } | |
60 | ||
61 | @Override | |
62 | public void start() { | |
63 | textGUIThread.start(); | |
64 | state = State.STARTED; | |
65 | } | |
66 | ||
67 | @Override | |
68 | public void stop() { | |
69 | if(state != State.STARTED) { | |
70 | return; | |
71 | } | |
72 | ||
73 | state = State.STOPPING; | |
74 | } | |
75 | ||
76 | @Override | |
77 | public void waitForStop() throws InterruptedException { | |
78 | waitLatch.await(); | |
79 | } | |
80 | ||
81 | @Override | |
82 | public State getState() { | |
83 | return state; | |
84 | } | |
85 | ||
86 | @Override | |
87 | public Thread getThread() { | |
88 | return textGUIThread; | |
89 | } | |
90 | ||
91 | @Override | |
92 | public void invokeLater(Runnable runnable) throws IllegalStateException { | |
93 | if(state != State.STARTED) { | |
94 | throw new IllegalStateException("Cannot schedule " + runnable + " for execution on the TextGUIThread " + | |
95 | "because the thread is in " + state + " state"); | |
96 | } | |
97 | super.invokeLater(runnable); | |
98 | } | |
99 | ||
100 | private void mainGUILoop() { | |
101 | try { | |
102 | //Draw initial screen, after this only draw when the GUI is marked as invalid | |
103 | try { | |
104 | textGUI.updateScreen(); | |
105 | } | |
106 | catch(IOException e) { | |
107 | exceptionHandler.onIOException(e); | |
108 | } | |
109 | catch(RuntimeException e) { | |
110 | exceptionHandler.onRuntimeException(e); | |
111 | } | |
112 | while(state == State.STARTED) { | |
113 | try { | |
114 | if (!processEventsAndUpdate()) { | |
115 | try { | |
116 | Thread.sleep(1); | |
117 | } | |
118 | catch(InterruptedException ignored) {} | |
119 | } | |
120 | } | |
121 | catch(EOFException e) { | |
122 | stop(); | |
123 | break; //Break out quickly from the main loop | |
124 | } | |
125 | catch(IOException e) { | |
126 | if(exceptionHandler.onIOException(e)) { | |
127 | stop(); | |
128 | break; | |
129 | } | |
130 | } | |
131 | catch(RuntimeException e) { | |
132 | if(exceptionHandler.onRuntimeException(e)) { | |
133 | stop(); | |
134 | break; | |
135 | } | |
136 | } | |
137 | } | |
138 | } | |
139 | finally { | |
140 | state = State.STOPPED; | |
141 | waitLatch.countDown(); | |
142 | } | |
143 | } | |
144 | ||
145 | ||
146 | /** | |
147 | * Factory class for creating SeparateTextGUIThread objects | |
148 | */ | |
149 | public static class Factory implements TextGUIThreadFactory { | |
150 | @Override | |
151 | public TextGUIThread createTextGUIThread(TextGUI textGUI) { | |
152 | return new SeparateTextGUIThread(textGUI); | |
153 | } | |
154 | } | |
155 | } |