Add 'src/jexer/' from commit 'cf01c92f5809a0732409e280fb0f32f27393618d'
[fanfix.git] / src / jexer / backend / MultiBackend.java
1 /*
2 * Jexer - Java Text User Interface
3 *
4 * The MIT License (MIT)
5 *
6 * Copyright (C) 2019 Kevin Lamonte
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the "Software"),
10 * to deal in the Software without restriction, including without limitation
11 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 * and/or sell copies of the Software, and to permit persons to whom the
13 * Software is furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 * DEALINGS IN THE SOFTWARE.
25 *
26 * @author Kevin Lamonte [kevin.lamonte@gmail.com]
27 * @version 1
28 */
29 package jexer.backend;
30
31 import java.util.ArrayList;
32 import java.util.List;
33
34 import jexer.event.TCommandEvent;
35 import jexer.event.TInputEvent;
36 import static jexer.TCommand.*;
37
38 /**
39 * MultiBackend mirrors its I/O to several backends.
40 */
41 public class MultiBackend implements Backend {
42
43 // ------------------------------------------------------------------------
44 // Variables --------------------------------------------------------------
45 // ------------------------------------------------------------------------
46
47 /**
48 * The screen to use.
49 */
50 private MultiScreen multiScreen;
51
52 /**
53 * The list of backends to use.
54 */
55 private List<Backend> backends = new ArrayList<Backend>();
56
57 /**
58 * The SessionInfo to return.
59 */
60 private SessionInfo sessionInfo;
61
62 // ------------------------------------------------------------------------
63 // Constructors -----------------------------------------------------------
64 // ------------------------------------------------------------------------
65
66 /**
67 * Public constructor requires one backend. Note that this backend's
68 * screen will be replaced with a MultiScreen.
69 *
70 * @param backend the backend to add
71 */
72 public MultiBackend(final Backend backend) {
73 backends.add(backend);
74 if (backend instanceof TWindowBackend) {
75 multiScreen = new MultiScreen(((TWindowBackend) backend).getOtherScreen());
76 } else {
77 multiScreen = new MultiScreen(backend.getScreen());
78 }
79 if (backend instanceof GenericBackend) {
80 ((GenericBackend) backend).abortOnDisconnect = false;
81 }
82 sessionInfo = backend.getSessionInfo();
83 }
84
85 // ------------------------------------------------------------------------
86 // Backend ----------------------------------------------------------------
87 // ------------------------------------------------------------------------
88
89 /**
90 * Getter for sessionInfo.
91 *
92 * @return the SessionInfo
93 */
94 public SessionInfo getSessionInfo() {
95 return sessionInfo;
96 }
97
98 /**
99 * Getter for screen.
100 *
101 * @return the Screen
102 */
103 public Screen getScreen() {
104 return multiScreen;
105 }
106
107 /**
108 * Subclasses must provide an implementation that syncs the logical
109 * screen to the physical device.
110 */
111 public void flushScreen() {
112 for (Backend backend: backends) {
113 backend.flushScreen();
114 }
115 }
116
117 /**
118 * Check if there are events in the queue.
119 *
120 * @return if true, getEvents() has something to return to the application
121 */
122 public boolean hasEvents() {
123 if (backends.size() == 0) {
124 return true;
125 }
126 for (Backend backend: backends) {
127 if (backend.hasEvents()) {
128 return true;
129 }
130 }
131 return false;
132 }
133
134 /**
135 * Subclasses must provide an implementation to get keyboard, mouse, and
136 * screen resize events.
137 *
138 * @param queue list to append new events to
139 */
140 public void getEvents(List<TInputEvent> queue) {
141 List<Backend> backendsToRemove = null;
142 for (Backend backend: backends) {
143 if (backend.hasEvents()) {
144 backend.getEvents(queue);
145
146 // This default backend assumes a single user, and if that
147 // user becomes disconnected we should terminate the
148 // application.
149 if (queue.size() > 0) {
150 TInputEvent event = queue.get(queue.size() - 1);
151 if (event instanceof TCommandEvent) {
152 TCommandEvent command = (TCommandEvent) event;
153 if (command.equals(cmBackendDisconnect)) {
154 if (backendsToRemove == null) {
155 backendsToRemove = new ArrayList<Backend>();
156 }
157 backendsToRemove.add(backend);
158 }
159 }
160 }
161 }
162 }
163 if (backendsToRemove != null) {
164 for (Backend backend: backendsToRemove) {
165 multiScreen.removeScreen(backend.getScreen());
166 backends.remove(backend);
167 backend.shutdown();
168 }
169 }
170 if (backends.size() == 0) {
171 queue.add(new TCommandEvent(cmAbort));
172 }
173 }
174
175 /**
176 * Subclasses must provide an implementation that closes sockets,
177 * restores console, etc.
178 */
179 public void shutdown() {
180 for (Backend backend: backends) {
181 backend.shutdown();
182 }
183 }
184
185 /**
186 * Subclasses must provide an implementation that sets the window title.
187 *
188 * @param title the new title
189 */
190 public void setTitle(final String title) {
191 for (Backend backend: backends) {
192 backend.setTitle(title);
193 }
194 }
195
196 /**
197 * Set listener to a different Object.
198 *
199 * @param listener the new listening object that run() wakes up on new
200 * input
201 */
202 public void setListener(final Object listener) {
203 for (Backend backend: backends) {
204 backend.setListener(listener);
205 }
206 }
207
208 /**
209 * Reload backend options from System properties.
210 */
211 public void reloadOptions() {
212 for (Backend backend: backends) {
213 backend.reloadOptions();
214 }
215 }
216
217 // ------------------------------------------------------------------------
218 // MultiBackend -----------------------------------------------------------
219 // ------------------------------------------------------------------------
220
221 /**
222 * Add a backend to the list.
223 *
224 * @param backend the backend to add
225 */
226 public void addBackend(final Backend backend) {
227 backends.add(backend);
228 if (backend instanceof TWindowBackend) {
229 multiScreen.addScreen(((TWindowBackend) backend).getOtherScreen());
230 } else {
231 multiScreen.addScreen(backend.getScreen());
232 }
233 if (backend instanceof GenericBackend) {
234 ((GenericBackend) backend).abortOnDisconnect = false;
235 }
236 }
237
238 /**
239 * Remove a backend from the list.
240 *
241 * @param backend the backend to remove
242 */
243 public void removeBackend(final Backend backend) {
244 if (backends.size() > 1) {
245 if (backend instanceof TWindowBackend) {
246 multiScreen.removeScreen(((TWindowBackend) backend).getOtherScreen());
247 } else {
248 multiScreen.removeScreen(backend.getScreen());
249 }
250 backends.remove(backend);
251 }
252 }
253
254 }