11
|
1 /*
|
|
2 * 510ConnectBot
|
|
3 * Copyright 2014 Carl Byington
|
|
4 *
|
|
5 * Licensed under the Apache License, Version 2.0 (the "License");
|
|
6 * you may not use this file except in compliance with the License.
|
|
7 * You may obtain a copy of the License at
|
|
8 *
|
|
9 * http://www.apache.org/licenses/LICENSE-2.0
|
|
10 *
|
|
11 * Unless required by applicable law or agreed to in writing, software
|
|
12 * distributed under the License is distributed on an "AS IS" BASIS,
|
|
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14 * See the License for the specific language governing permissions and
|
|
15 * limitations under the License.
|
|
16 */
|
|
17
|
|
18 package com.five_ten_sg.connectbot.transport;
|
|
19
|
|
20 import java.io.IOException;
|
|
21 import java.io.InputStream;
|
|
22 import java.io.OutputStream;
|
|
23 import java.net.Socket;
|
|
24 import java.net.SocketException;
|
13
|
25 import java.net.UnknownHostException;
|
54
|
26 import java.util.HashMap;
|
11
|
27 import java.util.List;
|
|
28 import java.util.Map;
|
13
|
29 import java.util.regex.Matcher;
|
|
30 import java.util.regex.Pattern;
|
11
|
31
|
12
|
32 import org.tn5250j.framework.tn5250.Screen5250;
|
|
33 import org.tn5250j.framework.tn5250.tnvt;
|
|
34
|
13
|
35 import com.five_ten_sg.connectbot.R;
|
11
|
36 import com.five_ten_sg.connectbot.bean.HostBean;
|
|
37 import com.five_ten_sg.connectbot.bean.PortForwardBean;
|
|
38 import com.five_ten_sg.connectbot.service.TerminalBridge;
|
29
|
39 import com.five_ten_sg.connectbot.service.TerminalKeyListener;
|
11
|
40 import com.five_ten_sg.connectbot.service.TerminalManager;
|
|
41 import com.five_ten_sg.connectbot.util.HostDatabase;
|
50
|
42 import com.five_ten_sg.connectbot.util.PreferenceConstants;
|
11
|
43 import android.content.Context;
|
|
44 import android.net.Uri;
|
13
|
45 import android.util.Log;
|
53
|
46 import android.view.KeyCharacterMap;
|
50
|
47 import android.view.KeyEvent;
|
|
48 import android.view.View;
|
29
|
49 import de.mud.terminal.vt320;
|
11
|
50
|
|
51
|
|
52 /**
|
|
53 * @author Carl Byington
|
|
54 *
|
|
55 */
|
|
56 public class TN5250 extends AbsTransport {
|
|
57 private static final String PROTOCOL = "tn5250";
|
|
58 private static final String TAG = "ConnectBot.tn5250";
|
|
59 private static final int DEFAULT_PORT = 23;
|
31
|
60
|
11
|
61 private Screen5250 screen52;
|
31
|
62 private tnvt handler = null;
|
|
63 private Socket socket;
|
|
64 private boolean connected = false;
|
11
|
65
|
12
|
66 static final Pattern hostmask;
|
|
67 static {
|
|
68 hostmask = Pattern.compile("^([0-9a-z.-]+)(:(\\d+))?$", Pattern.CASE_INSENSITIVE);
|
|
69 }
|
|
70
|
45
|
71
|
|
72 class vt320x5250 extends vt320 {
|
84
8f23b05a51f7
convert ctrl keys to virtual keys; use proper android home directory
Carl Byington <carl@five-ten-sg.com>
diff
changeset
|
73 private HashMap<Integer, Integer> controls;
|
53
|
74 private HashMap<Integer, String> mnemonics;
|
|
75
|
54
|
76 public vt320x5250() {
|
112
|
77 this(80, 24);
|
54
|
78 }
|
|
79
|
53
|
80 public vt320x5250(int width, int height) {
|
|
81 super(width, height);
|
84
8f23b05a51f7
convert ctrl keys to virtual keys; use proper android home directory
Carl Byington <carl@five-ten-sg.com>
diff
changeset
|
82 controls = new HashMap<Integer, Integer>();
|
166
|
83 controls.put(0x01, KEY_PAUSE); // ctrl-a -> [attn]
|
84
8f23b05a51f7
convert ctrl keys to virtual keys; use proper android home directory
Carl Byington <carl@five-ten-sg.com>
diff
changeset
|
84 controls.put(0x08, KEY_BACK_SPACE);
|
8f23b05a51f7
convert ctrl keys to virtual keys; use proper android home directory
Carl Byington <carl@five-ten-sg.com>
diff
changeset
|
85 controls.put(0x09, KEY_TAB);
|
8f23b05a51f7
convert ctrl keys to virtual keys; use proper android home directory
Carl Byington <carl@five-ten-sg.com>
diff
changeset
|
86 controls.put(0x0d, KEY_ENTER);
|
166
|
87 controls.put(0x12, KEY_ESCAPE); // ctrl-r -> [reset]
|
|
88 controls.put(0x13, KEY_SYSREQ); // ctrl-s -> [sysreq]
|
|
89 controls.put(0x1b, KEY_ESCAPE); // esc -> [reset]
|
53
|
90 mnemonics = new HashMap<Integer, String>();
|
|
91 mnemonics.put(KEY_PAUSE , "[attn]");
|
|
92 mnemonics.put(KEY_F1 , "[pf1]");
|
|
93 mnemonics.put(KEY_F2 , "[pf2]");
|
|
94 mnemonics.put(KEY_F3 , "[pf3]");
|
|
95 mnemonics.put(KEY_F4 , "[pf4]");
|
|
96 mnemonics.put(KEY_F5 , "[pf5]");
|
|
97 mnemonics.put(KEY_F6 , "[pf6]");
|
|
98 mnemonics.put(KEY_F7 , "[pf7]");
|
|
99 mnemonics.put(KEY_F8 , "[pf8]");
|
|
100 mnemonics.put(KEY_F9 , "[pf9]");
|
|
101 mnemonics.put(KEY_F10 , "[pf10]");
|
|
102 mnemonics.put(KEY_F11 , "[pf11]");
|
|
103 mnemonics.put(KEY_F12 , "[pf12]");
|
175
2a7199ad90be
send cursor movement caused by user keystrokes to the monitor
Carl Byington <carl@five-ten-sg.com>
diff
changeset
|
104 mnemonics.put(KEY_F13 , "[pf13]");
|
2a7199ad90be
send cursor movement caused by user keystrokes to the monitor
Carl Byington <carl@five-ten-sg.com>
diff
changeset
|
105 mnemonics.put(KEY_F14 , "[pf14]");
|
2a7199ad90be
send cursor movement caused by user keystrokes to the monitor
Carl Byington <carl@five-ten-sg.com>
diff
changeset
|
106 mnemonics.put(KEY_F15 , "[pf15]");
|
2a7199ad90be
send cursor movement caused by user keystrokes to the monitor
Carl Byington <carl@five-ten-sg.com>
diff
changeset
|
107 mnemonics.put(KEY_F16 , "[pf16]");
|
2a7199ad90be
send cursor movement caused by user keystrokes to the monitor
Carl Byington <carl@five-ten-sg.com>
diff
changeset
|
108 mnemonics.put(KEY_F17 , "[pf17]");
|
2a7199ad90be
send cursor movement caused by user keystrokes to the monitor
Carl Byington <carl@five-ten-sg.com>
diff
changeset
|
109 mnemonics.put(KEY_F18 , "[pf18]");
|
2a7199ad90be
send cursor movement caused by user keystrokes to the monitor
Carl Byington <carl@five-ten-sg.com>
diff
changeset
|
110 mnemonics.put(KEY_F19 , "[pf19]");
|
2a7199ad90be
send cursor movement caused by user keystrokes to the monitor
Carl Byington <carl@five-ten-sg.com>
diff
changeset
|
111 mnemonics.put(KEY_F20 , "[pf20]");
|
2a7199ad90be
send cursor movement caused by user keystrokes to the monitor
Carl Byington <carl@five-ten-sg.com>
diff
changeset
|
112 mnemonics.put(KEY_F21 , "[pf21]");
|
2a7199ad90be
send cursor movement caused by user keystrokes to the monitor
Carl Byington <carl@five-ten-sg.com>
diff
changeset
|
113 mnemonics.put(KEY_F22 , "[pf22]");
|
2a7199ad90be
send cursor movement caused by user keystrokes to the monitor
Carl Byington <carl@five-ten-sg.com>
diff
changeset
|
114 mnemonics.put(KEY_F23 , "[pf23]");
|
2a7199ad90be
send cursor movement caused by user keystrokes to the monitor
Carl Byington <carl@five-ten-sg.com>
diff
changeset
|
115 mnemonics.put(KEY_F24 , "[pf24]");
|
53
|
116 mnemonics.put(KEY_UP , "[up]");
|
|
117 mnemonics.put(KEY_DOWN , "[down]");
|
|
118 mnemonics.put(KEY_LEFT , "[left]");
|
|
119 mnemonics.put(KEY_RIGHT , "[right]");
|
|
120 mnemonics.put(KEY_PAGE_DOWN , "[pgdown]");
|
|
121 mnemonics.put(KEY_PAGE_UP , "[pgup]");
|
|
122 mnemonics.put(KEY_INSERT , "[insert]");
|
|
123 mnemonics.put(KEY_DELETE , "[delete]");
|
|
124 mnemonics.put(KEY_BACK_SPACE , "[backspace]");
|
|
125 mnemonics.put(KEY_HOME , "[home]");
|
|
126 mnemonics.put(KEY_END , "[end]");
|
|
127 mnemonics.put(KEY_NUM_LOCK , "");
|
|
128 mnemonics.put(KEY_CAPS_LOCK , "");
|
|
129 mnemonics.put(KEY_SHIFT , "");
|
|
130 mnemonics.put(KEY_CONTROL , "");
|
|
131 mnemonics.put(KEY_ALT , "");
|
57
|
132 mnemonics.put(KEY_ENTER , "[enter]");
|
53
|
133 mnemonics.put(KEY_NUMPAD0 , "0");
|
|
134 mnemonics.put(KEY_NUMPAD1 , "1");
|
|
135 mnemonics.put(KEY_NUMPAD2 , "2");
|
|
136 mnemonics.put(KEY_NUMPAD3 , "3");
|
|
137 mnemonics.put(KEY_NUMPAD4 , "4");
|
|
138 mnemonics.put(KEY_NUMPAD5 , "5");
|
|
139 mnemonics.put(KEY_NUMPAD6 , "6");
|
|
140 mnemonics.put(KEY_NUMPAD7 , "7");
|
|
141 mnemonics.put(KEY_NUMPAD8 , "8");
|
|
142 mnemonics.put(KEY_NUMPAD9 , "9");
|
|
143 mnemonics.put(KEY_DECIMAL , ".");
|
|
144 mnemonics.put(KEY_ADD , "+");
|
151
|
145 mnemonics.put(KEY_ESCAPE , "[reset]");
|
53
|
146 mnemonics.put(KEY_TAB , "[tab]");
|
166
|
147 mnemonics.put(KEY_SYSREQ , "[sysreq]");
|
53
|
148 }
|
|
149
|
45
|
150 @Override
|
|
151 public void debug(String s) {
|
|
152 Log.d(TAG, s);
|
|
153 }
|
53
|
154
|
69
|
155 // monitor injecting a field
|
|
156 @Override
|
104
|
157 public void setField(int l, int c, char [] data) {
|
|
158 screen52.setField(l, c, data);
|
69
|
159 }
|
|
160
|
175
2a7199ad90be
send cursor movement caused by user keystrokes to the monitor
Carl Byington <carl@five-ten-sg.com>
diff
changeset
|
161 // monitor simulating key depress
|
2a7199ad90be
send cursor movement caused by user keystrokes to the monitor
Carl Byington <carl@five-ten-sg.com>
diff
changeset
|
162 @Override
|
2a7199ad90be
send cursor movement caused by user keystrokes to the monitor
Carl Byington <carl@five-ten-sg.com>
diff
changeset
|
163 public void keyDepressed(int keyCode, char keyChar, int modifiers) {
|
2a7199ad90be
send cursor movement caused by user keystrokes to the monitor
Carl Byington <carl@five-ten-sg.com>
diff
changeset
|
164 if (mnemonics.containsKey(keyCode)) {
|
2a7199ad90be
send cursor movement caused by user keystrokes to the monitor
Carl Byington <carl@five-ten-sg.com>
diff
changeset
|
165 String s = mnemonics.get(keyCode);
|
2a7199ad90be
send cursor movement caused by user keystrokes to the monitor
Carl Byington <carl@five-ten-sg.com>
diff
changeset
|
166 if (s != "") screen52.sendKeys(s);
|
2a7199ad90be
send cursor movement caused by user keystrokes to the monitor
Carl Byington <carl@five-ten-sg.com>
diff
changeset
|
167 }
|
2a7199ad90be
send cursor movement caused by user keystrokes to the monitor
Carl Byington <carl@five-ten-sg.com>
diff
changeset
|
168 }
|
2a7199ad90be
send cursor movement caused by user keystrokes to the monitor
Carl Byington <carl@five-ten-sg.com>
diff
changeset
|
169
|
148
|
170 // terminal key listener found special key, send notification to monitor
|
|
171 @Override
|
|
172 public void monitorKey(boolean down) {
|
|
173 if (bridge.monitor != null) bridge.monitor.keyState(down);
|
|
174 }
|
|
175
|
69
|
176 // terminal key listener sending to local screen
|
45
|
177 @Override
|
|
178 public void write(byte[] b) {
|
144
|
179 screen52.sendKeys(new String(b));
|
229
|
180 if (bridge.monitor != null) bridge.monitor.testMoved();
|
144
|
181 }
|
|
182 @Override
|
|
183 public void write(int b) {
|
147
|
184 if (controls.containsKey(b)) keyPressed(controls.get(b), ' ', 0);
|
|
185 else screen52.sendKeys(new String(new byte[] {(byte)b}));
|
229
|
186 if (bridge.monitor != null) bridge.monitor.testMoved();
|
45
|
187 }
|
53
|
188 @Override
|
|
189 public void keyPressed(int keyCode, char keyChar, int modifiers) {
|
175
2a7199ad90be
send cursor movement caused by user keystrokes to the monitor
Carl Byington <carl@five-ten-sg.com>
diff
changeset
|
190 keyDepressed(keyCode, keyChar, modifiers);
|
229
|
191 if (bridge.monitor != null) bridge.monitor.testMoved();
|
53
|
192 }
|
175
2a7199ad90be
send cursor movement caused by user keystrokes to the monitor
Carl Byington <carl@five-ten-sg.com>
diff
changeset
|
193
|
53
|
194 // 5250 writing to the screen
|
69
|
195 // test for changed screen contents
|
|
196 @Override
|
|
197 public void testChanged() {
|
|
198 if (bridge.monitor != null) bridge.monitor.testChanged();
|
|
199 }
|
45
|
200 @Override
|
|
201 public void putChar(int c, int l, char ch, int attributes) {
|
|
202 if (bridge.monitor != null) bridge.monitor.screenChanged(l, c);
|
112
|
203
|
45
|
204 super.putChar(c, l, ch, attributes);
|
|
205 }
|
|
206 @Override
|
|
207 public void setCursorPosition(int c, int l) {
|
|
208 if (bridge.monitor != null) bridge.monitor.cursorMove(l, c);
|
112
|
209
|
45
|
210 super.setCursorPosition(c, l);
|
87
1bc2229562f8
convert ctrl keys to virtual keys; use proper android home directory
Carl Byington <carl@five-ten-sg.com>
diff
changeset
|
211 redraw();
|
45
|
212 }
|
|
213 };
|
|
214
|
|
215
|
11
|
216 public TN5250() {
|
112
|
217 super();
|
53
|
218 }
|
11
|
219
|
|
220
|
31
|
221 /**
|
|
222 * @return protocol part of the URI
|
|
223 */
|
112
|
224 public static String getProtocolName() {
|
|
225 return PROTOCOL;
|
|
226 }
|
11
|
227
|
31
|
228
|
|
229 /**
|
|
230 * Encode the current transport into a URI that can be passed via intent calls.
|
|
231 * @return URI to host
|
|
232 */
|
112
|
233 public Uri getUri(String input) {
|
12
|
234 Matcher matcher = hostmask.matcher(input);
|
|
235
|
|
236 if (!matcher.matches())
|
|
237 return null;
|
|
238
|
|
239 StringBuilder sb = new StringBuilder();
|
|
240 sb.append(PROTOCOL)
|
|
241 .append("://")
|
|
242 .append(matcher.group(1));
|
|
243 String portString = matcher.group(3);
|
|
244 int port = DEFAULT_PORT;
|
|
245
|
|
246 if (portString != null) {
|
|
247 try {
|
|
248 port = Integer.parseInt(portString);
|
|
249
|
|
250 if (port < 1 || port > 65535) {
|
|
251 port = DEFAULT_PORT;
|
|
252 }
|
|
253 }
|
|
254 catch (NumberFormatException nfe) {
|
|
255 // Keep the default port
|
|
256 }
|
|
257 }
|
|
258
|
|
259 if (port != DEFAULT_PORT) {
|
|
260 sb.append(':');
|
|
261 sb.append(port);
|
|
262 }
|
|
263
|
|
264 sb.append("/#")
|
|
265 .append(Uri.encode(input));
|
|
266 Uri uri = Uri.parse(sb.toString());
|
|
267 return uri;
|
112
|
268 }
|
11
|
269
|
31
|
270
|
112
|
271 /**
|
|
272 * Causes transport to connect to the target host. After connecting but before a
|
|
273 * session is started, must call back to {@link TerminalBridge#onConnected()}.
|
|
274 * After that call a session may be opened.
|
|
275 */
|
|
276 @Override
|
|
277 public void connect() {
|
30
|
278 screen52 = new Screen5250();
|
29
|
279 handler = new tnvt(screen52, true, false, bridge, manager);
|
32
|
280 screen52.setVT(handler);
|
|
281 screen52.setBuffer(buffer);
|
140
|
282 bridge.addFontSizeChangedListener(screen52);
|
121
|
283 connected = handler.connect(host, homeDirectory, buffer);
|
29
|
284 if (connected) bridge.onConnected();
|
11
|
285 }
|
|
286
|
|
287
|
|
288 /**
|
|
289 * Checks if read() will block. If there are no bytes remaining in
|
|
290 * the underlying transport, return true.
|
|
291 */
|
|
292 @Override
|
|
293 public boolean willBlock() {
|
29
|
294 // we don't use a relay thread between the transport and the vt320 buffer
|
|
295 return true;
|
11
|
296 }
|
|
297
|
31
|
298
|
11
|
299 /**
|
|
300 * Reads from the transport. Transport must support reading into a byte array
|
|
301 * <code>buffer</code> at the start of <code>offset</code> and a maximum of
|
|
302 * <code>length</code> bytes. If the remote host disconnects, throw an
|
|
303 * {@link IOException}.
|
|
304 * @param buffer byte buffer to store read bytes into
|
|
305 * @param offset where to start writing in the buffer
|
|
306 * @param length maximum number of bytes to read
|
|
307 * @return number of bytes read
|
|
308 * @throws IOException when remote host disconnects
|
|
309 */
|
|
310 public int read(byte[] buffer, int offset, int length) throws IOException {
|
29
|
311 // we don't use a relay thread between the transport and the vt320 buffer
|
11
|
312 return 0;
|
|
313 }
|
|
314
|
|
315
|
|
316 /**
|
|
317 * Writes to the transport. If the host is not yet connected, simply return without
|
|
318 * doing anything. An {@link IOException} should be thrown if there is an error after
|
|
319 * connection.
|
|
320 * @param buffer bytes to write to transport
|
|
321 * @throws IOException when there is a problem writing after connection
|
|
322 */
|
|
323 public void write(byte[] buffer) throws IOException {
|
|
324 }
|
|
325
|
31
|
326
|
11
|
327 /**
|
|
328 * Writes to the transport. See {@link #write(byte[])} for behavior details.
|
|
329 * @param c character to write to the transport
|
|
330 * @throws IOException when there is a problem writing after connection
|
|
331 */
|
|
332 public void write(int c) throws IOException {
|
|
333 }
|
|
334
|
31
|
335
|
11
|
336 /**
|
|
337 * Flushes the write commands to the transport.
|
|
338 * @throws IOException when there is a problem writing after connection
|
|
339 */
|
|
340 public void flush() throws IOException {
|
|
341 }
|
|
342
|
31
|
343
|
11
|
344 /**
|
32
|
345 * Closes the connection to the terminal.
|
11
|
346 */
|
|
347 public void close() {
|
13
|
348 handler.disconnect();
|
11
|
349 connected = false;
|
140
|
350 bridge.removeFontSizeChangedListener(screen52);
|
32
|
351 bridge.dispatchDisconnect(false);
|
11
|
352 }
|
|
353
|
31
|
354
|
11
|
355 /**
|
|
356 * Tells the transport what dimensions the display is currently
|
|
357 * @param columns columns of text
|
|
358 * @param rows rows of text
|
|
359 * @param width width in pixels
|
|
360 * @param height height in pixels
|
|
361 */
|
|
362 @Override
|
|
363 public void setDimensions(int columns, int rows, int width, int height) {
|
|
364 // do nothing
|
|
365 }
|
|
366
|
|
367
|
|
368 @Override
|
45
|
369 public vt320 getTransportBuffer() {
|
|
370 buffer = new vt320x5250();
|
47
|
371 return setupTransportBuffer();
|
45
|
372 }
|
|
373
|
|
374
|
|
375 @Override
|
11
|
376 public int getDefaultPort() {
|
|
377 return DEFAULT_PORT;
|
|
378 }
|
|
379
|
31
|
380
|
11
|
381 @Override
|
|
382 public boolean isConnected() {
|
|
383 return connected;
|
|
384 }
|
|
385
|
31
|
386
|
11
|
387 @Override
|
|
388 public boolean isSessionOpen() {
|
|
389 return connected;
|
|
390 }
|
|
391
|
31
|
392
|
11
|
393 @Override
|
|
394 public boolean isAuthenticated() {
|
|
395 return connected;
|
|
396 }
|
|
397
|
|
398
|
|
399 @Override
|
|
400 public String getDefaultNickname(String username, String hostname, int port) {
|
|
401 if (port == DEFAULT_PORT) {
|
|
402 return String.format("%s", hostname);
|
|
403 }
|
|
404 else {
|
|
405 return String.format("%s:%d", hostname, port);
|
|
406 }
|
|
407 }
|
|
408
|
31
|
409
|
11
|
410 @Override
|
|
411 public void getSelectionArgs(Uri uri, Map<String, String> selection) {
|
|
412 selection.put(HostDatabase.FIELD_HOST_PROTOCOL, PROTOCOL);
|
|
413 selection.put(HostDatabase.FIELD_HOST_NICKNAME, uri.getFragment());
|
|
414 selection.put(HostDatabase.FIELD_HOST_HOSTNAME, uri.getHost());
|
|
415 int port = uri.getPort();
|
|
416
|
147
|
417 if (port < 0) port = DEFAULT_PORT;
|
11
|
418
|
|
419 selection.put(HostDatabase.FIELD_HOST_PORT, Integer.toString(port));
|
|
420 }
|
|
421
|
|
422
|
|
423 @Override
|
|
424 public HostBean createHost(Uri uri) {
|
|
425 HostBean host = new HostBean();
|
|
426 host.setProtocol(PROTOCOL);
|
|
427 host.setHostname(uri.getHost());
|
|
428 int port = uri.getPort();
|
|
429
|
147
|
430 if (port < 0) port = DEFAULT_PORT;
|
11
|
431
|
|
432 host.setPort(port);
|
|
433 String nickname = uri.getFragment();
|
|
434
|
|
435 if (nickname == null || nickname.length() == 0) {
|
133
|
436 nickname = getDefaultNickname(host.getUsername(), host.getHostname(), host.getPort());
|
11
|
437 }
|
132
|
438 host.setNickname(nickname);
|
11
|
439
|
|
440 return host;
|
|
441 }
|
|
442
|
|
443
|
12
|
444 public String getFormatHint(Context context) {
|
11
|
445 return String.format("%s:%s",
|
|
446 context.getString(R.string.format_hostname),
|
|
447 context.getString(R.string.format_port));
|
|
448 }
|
|
449
|
|
450
|
|
451 @Override
|
|
452 public boolean usesNetwork() {
|
|
453 return true;
|
|
454 }
|
29
|
455
|
|
456
|
|
457 @Override
|
|
458 public boolean needsRelay() {
|
|
459 // we don't use a relay thread between the transport and the vt320 buffer
|
|
460 return false;
|
|
461 }
|
|
462
|
43
|
463 public TerminalKeyListener getTerminalKeyListener() {
|
145
|
464 return new TerminalKeyListener(manager, bridge, buffer, host.getEncoding());
|
43
|
465 }
|
|
466
|
11
|
467 }
|