Mercurial > 510Connectbot
view src/com/five_ten_sg/connectbot/transport/TN5250.java @ 244:32737a428805
add host preference for fixed screen size
author | Carl Byington <carl@five-ten-sg.com> |
---|---|
date | Mon, 14 Jul 2014 11:07:11 -0700 |
parents | 594101a0876a |
children | 8a6c7c7ffe0d |
line wrap: on
line source
/* * 510ConnectBot * Copyright 2014 Carl Byington * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.five_ten_sg.connectbot.transport; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.net.SocketException; import java.net.UnknownHostException; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.tn5250j.framework.tn5250.Screen5250; import org.tn5250j.framework.tn5250.tnvt; import com.five_ten_sg.connectbot.R; import com.five_ten_sg.connectbot.bean.HostBean; import com.five_ten_sg.connectbot.bean.PortForwardBean; import com.five_ten_sg.connectbot.service.TerminalBridge; import com.five_ten_sg.connectbot.service.TerminalKeyListener; import com.five_ten_sg.connectbot.service.TerminalManager; import com.five_ten_sg.connectbot.util.HostDatabase; import com.five_ten_sg.connectbot.util.PreferenceConstants; import android.content.Context; import android.net.Uri; import android.util.Log; import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.View; import de.mud.terminal.vt320; /** * @author Carl Byington * */ public class TN5250 extends AbsTransport { private static final String PROTOCOL = "tn5250"; private static final String TAG = "ConnectBot.tn5250"; private static final int DEFAULT_PORT = 23; private Screen5250 screen52; private tnvt handler = null; private Socket socket; private boolean connected = false; static final Pattern hostmask; static { hostmask = Pattern.compile("^([0-9a-z.-]+)(:(\\d+))?$", Pattern.CASE_INSENSITIVE); } class vt320x5250 extends vt320 { private HashMap<Integer, Integer> controls; private HashMap<Integer, String> mnemonics; public vt320x5250() { this(80, 24); } public vt320x5250(int width, int height) { super(width, height); controls = new HashMap<Integer, Integer>(); controls.put(0x01, KEY_PAUSE); // ctrl-a -> [attn] controls.put(0x08, KEY_BACK_SPACE); controls.put(0x09, KEY_TAB); controls.put(0x0d, KEY_ENTER); controls.put(0x12, KEY_ESCAPE); // ctrl-r -> [reset] controls.put(0x13, KEY_SYSREQ); // ctrl-s -> [sysreq] controls.put(0x1b, KEY_ESCAPE); // esc -> [reset] mnemonics = new HashMap<Integer, String>(); mnemonics.put(KEY_PAUSE , "[attn]"); mnemonics.put(KEY_F1 , "[pf1]"); mnemonics.put(KEY_F2 , "[pf2]"); mnemonics.put(KEY_F3 , "[pf3]"); mnemonics.put(KEY_F4 , "[pf4]"); mnemonics.put(KEY_F5 , "[pf5]"); mnemonics.put(KEY_F6 , "[pf6]"); mnemonics.put(KEY_F7 , "[pf7]"); mnemonics.put(KEY_F8 , "[pf8]"); mnemonics.put(KEY_F9 , "[pf9]"); mnemonics.put(KEY_F10 , "[pf10]"); mnemonics.put(KEY_F11 , "[pf11]"); mnemonics.put(KEY_F12 , "[pf12]"); mnemonics.put(KEY_F13 , "[pf13]"); mnemonics.put(KEY_F14 , "[pf14]"); mnemonics.put(KEY_F15 , "[pf15]"); mnemonics.put(KEY_F16 , "[pf16]"); mnemonics.put(KEY_F17 , "[pf17]"); mnemonics.put(KEY_F18 , "[pf18]"); mnemonics.put(KEY_F19 , "[pf19]"); mnemonics.put(KEY_F20 , "[pf20]"); mnemonics.put(KEY_F21 , "[pf21]"); mnemonics.put(KEY_F22 , "[pf22]"); mnemonics.put(KEY_F23 , "[pf23]"); mnemonics.put(KEY_F24 , "[pf24]"); mnemonics.put(KEY_UP , "[up]"); mnemonics.put(KEY_DOWN , "[down]"); mnemonics.put(KEY_LEFT , "[left]"); mnemonics.put(KEY_RIGHT , "[right]"); mnemonics.put(KEY_PAGE_DOWN , "[pgdown]"); mnemonics.put(KEY_PAGE_UP , "[pgup]"); mnemonics.put(KEY_INSERT , "[insert]"); mnemonics.put(KEY_DELETE , "[delete]"); mnemonics.put(KEY_BACK_SPACE , "[backspace]"); mnemonics.put(KEY_HOME , "[home]"); mnemonics.put(KEY_END , "[end]"); mnemonics.put(KEY_NUM_LOCK , ""); mnemonics.put(KEY_CAPS_LOCK , ""); mnemonics.put(KEY_SHIFT , ""); mnemonics.put(KEY_CONTROL , ""); mnemonics.put(KEY_ALT , ""); mnemonics.put(KEY_ENTER , "[enter]"); mnemonics.put(KEY_NUMPAD0 , "0"); mnemonics.put(KEY_NUMPAD1 , "1"); mnemonics.put(KEY_NUMPAD2 , "2"); mnemonics.put(KEY_NUMPAD3 , "3"); mnemonics.put(KEY_NUMPAD4 , "4"); mnemonics.put(KEY_NUMPAD5 , "5"); mnemonics.put(KEY_NUMPAD6 , "6"); mnemonics.put(KEY_NUMPAD7 , "7"); mnemonics.put(KEY_NUMPAD8 , "8"); mnemonics.put(KEY_NUMPAD9 , "9"); mnemonics.put(KEY_DECIMAL , "."); mnemonics.put(KEY_ADD , "+"); mnemonics.put(KEY_ESCAPE , "[reset]"); mnemonics.put(KEY_TAB , "[tab]"); mnemonics.put(KEY_SYSREQ , "[sysreq]"); } @Override public void debug(String s) { Log.d(TAG, s); } // monitor injecting a field @Override public void setField(int l, int c, char [] data) { screen52.setField(l, c, data); } // monitor simulating key depress @Override public void keyDepressed(int keyCode, char keyChar, int modifiers) { if (mnemonics.containsKey(keyCode)) { String s = mnemonics.get(keyCode); if (s != "") screen52.sendKeys(s); } } // terminal key listener found special key, send notification to monitor @Override public void monitorKey(boolean down) { if (bridge.monitor != null) bridge.monitor.keyState(down); } // terminal key listener sending to local screen @Override public void write(byte[] b) { screen52.sendKeys(new String(b)); if (bridge.monitor != null) bridge.monitor.testMoved(); } @Override public void write(int b) { if (controls.containsKey(b)) keyPressed(controls.get(b), ' ', 0); else screen52.sendKeys(new String(new byte[] {(byte)b})); if (bridge.monitor != null) bridge.monitor.testMoved(); } @Override public void keyPressed(int keyCode, char keyChar, int modifiers) { keyDepressed(keyCode, keyChar, modifiers); if (bridge.monitor != null) bridge.monitor.testMoved(); } // 5250 writing to the screen // test for changed screen contents @Override public void testChanged() { if (bridge.monitor != null) bridge.monitor.testChanged(); } @Override public void putChar(int c, int l, char ch, int attributes) { if (bridge.monitor != null) bridge.monitor.screenChanged(l, c); super.putChar(c, l, ch, attributes); } @Override public void setCursorPosition(int c, int l) { if (bridge.monitor != null) bridge.monitor.cursorMove(l, c); super.setCursorPosition(c, l); redraw(); } }; public TN5250() { super(); } /** * @return protocol part of the URI */ public static String getProtocolName() { return PROTOCOL; } /** * Encode the current transport into a URI that can be passed via intent calls. * @return URI to host */ public Uri getUri(String input) { Matcher matcher = hostmask.matcher(input); if (!matcher.matches()) return null; StringBuilder sb = new StringBuilder(); sb.append(PROTOCOL) .append("://") .append(matcher.group(1)); String portString = matcher.group(3); int port = DEFAULT_PORT; if (portString != null) { try { port = Integer.parseInt(portString); if (port < 1 || port > 65535) { port = DEFAULT_PORT; } } catch (NumberFormatException nfe) { // Keep the default port } } if (port != DEFAULT_PORT) { sb.append(':'); sb.append(port); } sb.append("/#") .append(Uri.encode(input)); Uri uri = Uri.parse(sb.toString()); return uri; } /** * Causes transport to connect to the target host. After connecting but before a * session is started, must call back to {@link TerminalBridge#onConnected()}. * After that call a session may be opened. */ @Override public void connect() { screen52 = new Screen5250(); handler = new tnvt(screen52, true, false, bridge, manager); screen52.setVT(handler); screen52.setBuffer(buffer); bridge.addFontSizeChangedListener(screen52); connected = handler.connect(host, homeDirectory, buffer); if (connected) bridge.onConnected(); } /** * Checks if read() will block. If there are no bytes remaining in * the underlying transport, return true. */ @Override public boolean willBlock() { // we don't use a relay thread between the transport and the vt320 buffer return true; } /** * Reads from the transport. Transport must support reading into a byte array * <code>buffer</code> at the start of <code>offset</code> and a maximum of * <code>length</code> bytes. If the remote host disconnects, throw an * {@link IOException}. * @param buffer byte buffer to store read bytes into * @param offset where to start writing in the buffer * @param length maximum number of bytes to read * @return number of bytes read * @throws IOException when remote host disconnects */ public int read(byte[] buffer, int offset, int length) throws IOException { // we don't use a relay thread between the transport and the vt320 buffer return 0; } /** * Writes to the transport. If the host is not yet connected, simply return without * doing anything. An {@link IOException} should be thrown if there is an error after * connection. * @param buffer bytes to write to transport * @throws IOException when there is a problem writing after connection */ public void write(byte[] buffer) throws IOException { } /** * Writes to the transport. See {@link #write(byte[])} for behavior details. * @param c character to write to the transport * @throws IOException when there is a problem writing after connection */ public void write(int c) throws IOException { } /** * Flushes the write commands to the transport. * @throws IOException when there is a problem writing after connection */ public void flush() throws IOException { } /** * Closes the connection to the terminal. */ public void close() { handler.disconnect(); connected = false; bridge.removeFontSizeChangedListener(screen52); bridge.dispatchDisconnect(false); } /** * Tells the transport what dimensions the display is currently * @param columns columns of text * @param rows rows of text * @param width width in pixels * @param height height in pixels */ @Override public void setDimensions(int columns, int rows, int width, int height) { // do nothing } @Override public vt320 getTransportBuffer() { buffer = new vt320x5250(); return setupTransportBuffer(); } @Override public int getDefaultPort() { return DEFAULT_PORT; } @Override public boolean isConnected() { return connected; } @Override public boolean isSessionOpen() { return connected; } @Override public boolean isAuthenticated() { return connected; } @Override public String getDefaultNickname(String username, String hostname, int port) { if (port == DEFAULT_PORT) { return String.format("%s", hostname); } else { return String.format("%s:%d", hostname, port); } } @Override public void getSelectionArgs(Uri uri, Map<String, String> selection) { selection.put(HostDatabase.FIELD_HOST_PROTOCOL, PROTOCOL); selection.put(HostDatabase.FIELD_HOST_NICKNAME, uri.getFragment()); selection.put(HostDatabase.FIELD_HOST_HOSTNAME, uri.getHost()); int port = uri.getPort(); if (port < 0) port = DEFAULT_PORT; selection.put(HostDatabase.FIELD_HOST_PORT, Integer.toString(port)); } @Override public HostBean createHost(Uri uri) { HostBean host = new HostBean(); host.setProtocol(PROTOCOL); host.setHostname(uri.getHost()); int port = uri.getPort(); if (port < 0) port = DEFAULT_PORT; host.setPort(port); String nickname = uri.getFragment(); if (nickname == null || nickname.length() == 0) { nickname = getDefaultNickname(host.getUsername(), host.getHostname(), host.getPort()); } host.setNickname(nickname); return host; } public String getFormatHint(Context context) { return String.format("%s:%s", context.getString(R.string.format_hostname), context.getString(R.string.format_port)); } @Override public boolean usesNetwork() { return true; } @Override public boolean needsRelay() { // we don't use a relay thread between the transport and the vt320 buffer return false; } public TerminalKeyListener getTerminalKeyListener() { return new TerminalKeyListener(manager, bridge, buffer, host.getEncoding()); } }