aboutsummaryrefslogtreecommitdiffstats
path: root/src/org
diff options
context:
space:
mode:
authorKenny Root <kenny@the-b.org>2010-03-13 06:24:39 +0000
committerKenny Root <kenny@the-b.org>2010-03-13 06:24:39 +0000
commit515c3eab54db468865985299471a6785e3aebb28 (patch)
treed69348532e23a9fdfd064498a690de936d401708 /src/org
parent90632bd675a923e3ae22873547a5a936e485bd3f (diff)
downloadconnectbot-515c3eab54db468865985299471a6785e3aebb28.tar.gz
connectbot-515c3eab54db468865985299471a6785e3aebb28.tar.bz2
connectbot-515c3eab54db468865985299471a6785e3aebb28.zip
Refactor keyboard management into separate class
git-svn-id: https://connectbot.googlecode.com/svn/trunk/connectbot@490 df292f66-193f-0410-a5fc-6d59da041ff2
Diffstat (limited to 'src/org')
-rw-r--r--src/org/connectbot/ConsoleActivity.java3
-rw-r--r--src/org/connectbot/TerminalView.java27
-rw-r--r--src/org/connectbot/service/TerminalBridge.java472
-rw-r--r--src/org/connectbot/service/TerminalKeyListener.java486
-rw-r--r--src/org/connectbot/service/TerminalManager.java10
5 files changed, 554 insertions, 444 deletions
diff --git a/src/org/connectbot/ConsoleActivity.java b/src/org/connectbot/ConsoleActivity.java
index 234ef43..9f61105 100644
--- a/src/org/connectbot/ConsoleActivity.java
+++ b/src/org/connectbot/ConsoleActivity.java
@@ -1032,6 +1032,8 @@ public class ConsoleActivity extends Activity {
bound.setResizeAllowed(false);
else
bound.setResizeAllowed(true);
+
+ bound.hardKeyboardHidden = (newConfig.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES);
}
}
@@ -1044,7 +1046,6 @@ public class ConsoleActivity extends Activity {
private int addNewTerminalView(TerminalBridge bridge) {
// let them know about our prompt handler services
bridge.promptHelper.setHandler(promptHandler);
- bridge.refreshKeymode();
// inflate each terminal view
RelativeLayout view = (RelativeLayout)inflater.inflate(R.layout.item_terminal, flip, false);
diff --git a/src/org/connectbot/TerminalView.java b/src/org/connectbot/TerminalView.java
index 4e1fefc..35a3c56 100644
--- a/src/org/connectbot/TerminalView.java
+++ b/src/org/connectbot/TerminalView.java
@@ -20,6 +20,7 @@ package org.connectbot;
import org.connectbot.bean.SelectionArea;
import org.connectbot.service.FontSizeChangedListener;
import org.connectbot.service.TerminalBridge;
+import org.connectbot.service.TerminalKeyListener;
import android.app.Activity;
import android.content.Context;
@@ -110,7 +111,7 @@ public class TerminalView extends View implements FontSizeChangedListener {
bridge.addFontSizeChangedListener(this);
// connect our view up to the bridge
- setOnKeyListener(bridge);
+ setOnKeyListener(bridge.getKeyHandler());
}
public void destroy() {
@@ -149,14 +150,18 @@ public class TerminalView extends View implements FontSizeChangedListener {
// also draw cursor if visible
if (bridge.buffer.isCursorVisible()) {
int cursorColumn = bridge.buffer.getCursorColumn();
- int columns = bridge.buffer.getColumns();
+ final int cursorRow = bridge.buffer.getCursorRow();
+
+ final int columns = bridge.buffer.getColumns();
if (cursorColumn == columns)
cursorColumn = columns - 1;
+ if (cursorColumn < 0 || cursorRow < 0)
+ return;
+
int currentAttribute = bridge.buffer.getAttributes(
- cursorColumn,
- bridge.buffer.getCursorRow());
+ cursorColumn, cursorRow);
boolean onWideCharacter = (currentAttribute & VDUBuffer.FULLWIDTH) != 0;
int x = cursorColumn * bridge.charWidth;
@@ -176,21 +181,21 @@ public class TerminalView extends View implements FontSizeChangedListener {
// Make sure we scale our decorations to the correct size.
canvas.concat(scaleMatrix);
- int metaState = bridge.getMetaState();
+ int metaState = bridge.getKeyHandler().getMetaState();
- if ((metaState & TerminalBridge.META_SHIFT_ON) != 0)
+ if ((metaState & TerminalKeyListener.META_SHIFT_ON) != 0)
canvas.drawPath(shiftCursor, cursorStrokePaint);
- else if ((metaState & TerminalBridge.META_SHIFT_LOCK) != 0)
+ else if ((metaState & TerminalKeyListener.META_SHIFT_LOCK) != 0)
canvas.drawPath(shiftCursor, cursorPaint);
- if ((metaState & TerminalBridge.META_ALT_ON) != 0)
+ if ((metaState & TerminalKeyListener.META_ALT_ON) != 0)
canvas.drawPath(altCursor, cursorStrokePaint);
- else if ((metaState & TerminalBridge.META_ALT_LOCK) != 0)
+ else if ((metaState & TerminalKeyListener.META_ALT_LOCK) != 0)
canvas.drawPath(altCursor, cursorPaint);
- if ((metaState & TerminalBridge.META_CTRL_ON) != 0)
+ if ((metaState & TerminalKeyListener.META_CTRL_ON) != 0)
canvas.drawPath(ctrlCursor, cursorStrokePaint);
- else if ((metaState & TerminalBridge.META_CTRL_LOCK) != 0)
+ else if ((metaState & TerminalKeyListener.META_CTRL_LOCK) != 0)
canvas.drawPath(ctrlCursor, cursorPaint);
// Restore previous clip region
diff --git a/src/org/connectbot/service/TerminalBridge.java b/src/org/connectbot/service/TerminalBridge.java
index 9a2a946..2c1c1e1 100644
--- a/src/org/connectbot/service/TerminalBridge.java
+++ b/src/org/connectbot/service/TerminalBridge.java
@@ -32,10 +32,8 @@ import org.connectbot.bean.SelectionArea;
import org.connectbot.transport.AbsTransport;
import org.connectbot.transport.TransportFactory;
import org.connectbot.util.HostDatabase;
-import org.connectbot.util.PreferenceConstants;
import android.content.Context;
-import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -45,10 +43,6 @@ import android.graphics.Bitmap.Config;
import android.graphics.Paint.FontMetrics;
import android.text.ClipboardManager;
import android.util.Log;
-import android.view.KeyCharacterMap;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.View.OnKeyListener;
import de.mud.terminal.VDUBuffer;
import de.mud.terminal.VDUDisplay;
import de.mud.terminal.vt320;
@@ -63,10 +57,11 @@ import de.mud.terminal.vt320;
* This class also provides SSH hostkey verification prompting, and password
* prompting.
*/
-public class TerminalBridge implements VDUDisplay, OnKeyListener {
+public class TerminalBridge implements VDUDisplay {
public final static String TAG = "ConnectBot.TerminalBridge";
public final static int DEFAULT_FONT_SIZE = 10;
+ private final static int FONT_SIZE_STEP = 2;
public Integer[] color;
@@ -77,7 +72,7 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener {
public HostBean host;
- private AbsTransport transport;
+ /* package */ AbsTransport transport;
final Paint defaultPaint;
@@ -92,26 +87,6 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener {
private TerminalView parent = null;
private final Canvas canvas = new Canvas();
- private int metaState = 0;
-
- public final static int META_CTRL_ON = 0x01;
- public final static int META_CTRL_LOCK = 0x02;
- public final static int META_ALT_ON = 0x04;
- public final static int META_ALT_LOCK = 0x08;
- public final static int META_SHIFT_ON = 0x10;
- public final static int META_SHIFT_LOCK = 0x20;
- public final static int META_SLASH = 0x40;
- public final static int META_TAB = 0x80;
-
- // The bit mask of momentary and lock states for each
- public final static int META_CTRL_MASK = META_CTRL_ON | META_CTRL_LOCK;
- public final static int META_ALT_MASK = META_ALT_ON | META_ALT_LOCK;
- public final static int META_SHIFT_MASK = META_SHIFT_ON | META_SHIFT_LOCK;
-
- // All the transient key codes
- public final static int META_TRANSIENT = META_CTRL_ON | META_ALT_ON
- | META_SHIFT_ON;
-
private boolean disconnected = false;
private boolean awaitingClose = false;
@@ -119,16 +94,12 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener {
private int columns;
private int rows;
- private String keymode = null;
-
- private boolean hardKeyboard = false;
+ /* package */ final TerminalKeyListener keyListener;
private boolean selectingForCopy = false;
private final SelectionArea selectionArea;
private ClipboardManager clipboard;
- protected KeyCharacterMap keymap = KeyCharacterMap.load(KeyCharacterMap.BUILT_IN_KEYBOARD);
-
public int charWidth = -1;
public int charHeight = -1;
private int charTop = -1;
@@ -179,6 +150,8 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener {
fontSizeChangedListeners = new LinkedList<FontSizeChangedListener>();
transport = null;
+
+ keyListener = new TerminalKeyListener(manager, this, buffer, host);
}
/**
@@ -187,7 +160,6 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener {
* and password authentication.
*/
public TerminalBridge(final TerminalManager manager, final HostBean host) throws IOException {
-
this.manager = manager;
this.host = host;
@@ -270,8 +242,7 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener {
selectionArea = new SelectionArea();
- hardKeyboard = (manager.res.getConfiguration().keyboard
- == Configuration.KEYBOARD_QWERTY);
+ keyListener = new TerminalKeyListener(manager, this, buffer, host);
}
public PromptHelper getPromptHelper() {
@@ -482,394 +453,6 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener {
}
}
- public void refreshKeymode() {
- keymode = manager.getKeyMode();
- }
-
- /**
- * Handle onKey() events coming down from a {@link TerminalView} above us.
- * Modify the keys to make more sense to a host then pass it to the transport.
- */
- public boolean onKey(View v, int keyCode, KeyEvent event) {
- try {
-
- boolean hardKeyboardHidden =
- manager.res.getConfiguration().hardKeyboardHidden ==
- Configuration.HARDKEYBOARDHIDDEN_YES;
-
- // Ignore all key-up events except for the special keys
- if (event.getAction() == KeyEvent.ACTION_UP) {
- // There's nothing here for virtual keyboard users.
- if (!hardKeyboard || (hardKeyboard && hardKeyboardHidden))
- return false;
-
- // skip keys if we aren't connected yet or have been disconnected
- if (disconnected || transport == null)
- return false;
-
- if (PreferenceConstants.KEYMODE_RIGHT.equals(keymode)) {
- if (keyCode == KeyEvent.KEYCODE_ALT_RIGHT
- && (metaState & META_SLASH) != 0) {
- metaState &= ~(META_SLASH | META_TRANSIENT);
- transport.write('/');
- return true;
- } else if (keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT
- && (metaState & META_TAB) != 0) {
- metaState &= ~(META_TAB | META_TRANSIENT);
- transport.write(0x09);
- return true;
- }
- } else if (PreferenceConstants.KEYMODE_LEFT.equals(keymode)) {
- if (keyCode == KeyEvent.KEYCODE_ALT_LEFT
- && (metaState & META_SLASH) != 0) {
- metaState &= ~(META_SLASH | META_TRANSIENT);
- transport.write('/');
- return true;
- } else if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT
- && (metaState & META_TAB) != 0) {
- metaState &= ~(META_TAB | META_TRANSIENT);
- transport.write(0x09);
- return true;
- }
- }
-
- return false;
- }
-
- // check for terminal resizing keys
- if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
- forcedSize = false;
- setFontSize(fontSize + 2);
- return true;
- } else if(keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
- forcedSize = false;
- setFontSize(fontSize - 2);
- return true;
- }
-
- // skip keys if we aren't connected yet or have been disconnected
- if (disconnected || transport == null)
- return false;
-
- // if we're in scrollback, scroll to bottom of window on input
- if (buffer.windowBase != buffer.screenBase)
- buffer.setWindowBase(buffer.screenBase);
-
- boolean printing = (keymap.isPrintingKey(keyCode) || keyCode == KeyEvent.KEYCODE_SPACE);
-
- // otherwise pass through to existing session
- // print normal keys
- if (printing) {
- int curMetaState = event.getMetaState();
-
- metaState &= ~(META_SLASH | META_TAB);
-
- if ((metaState & META_SHIFT_MASK) != 0) {
- curMetaState |= KeyEvent.META_SHIFT_ON;
- metaState &= ~META_SHIFT_ON;
- redraw();
- }
-
- if ((metaState & META_ALT_MASK) != 0) {
- curMetaState |= KeyEvent.META_ALT_ON;
- metaState &= ~META_ALT_ON;
- redraw();
- }
-
- int key = keymap.get(keyCode, curMetaState);
-
- if ((metaState & META_CTRL_MASK) != 0) {
- metaState &= ~META_CTRL_ON;
- redraw();
-
- if ((!hardKeyboard || (hardKeyboard && hardKeyboardHidden))
- && sendFunctionKey(keyCode))
- return true;
-
- // Support CTRL-a through CTRL-z
- if (key >= 0x61 && key <= 0x7A)
- key -= 0x60;
- // Support CTRL-A through CTRL-_
- else if (key >= 0x41 && key <= 0x5F)
- key -= 0x40;
- else if (key == 0x20)
- key = 0x00;
- else if (key == 0x3F)
- key = 0x7F;
- }
-
- // handle pressing f-keys
- if ((hardKeyboard && !hardKeyboardHidden)
- && (curMetaState & KeyEvent.META_SHIFT_ON) != 0
- && sendFunctionKey(keyCode))
- return true;
-
- if (key < 0x80)
- transport.write(key);
- else
- // TODO write encoding routine that doesn't allocate each time
- transport.write(new String(Character.toChars(key))
- .getBytes(host.getEncoding()));
-
- return true;
- }
-
- if (keyCode == KeyEvent.KEYCODE_UNKNOWN &&
- event.getAction() == KeyEvent.ACTION_MULTIPLE) {
- byte[] input = event.getCharacters().getBytes(host.getEncoding());
- transport.write(input);
- return true;
- }
-
- // try handling keymode shortcuts
- if (hardKeyboard && !hardKeyboardHidden &&
- event.getRepeatCount() == 0) {
- if (PreferenceConstants.KEYMODE_RIGHT.equals(keymode)) {
- switch (keyCode) {
- case KeyEvent.KEYCODE_ALT_RIGHT:
- metaState |= META_SLASH;
- return true;
- case KeyEvent.KEYCODE_SHIFT_RIGHT:
- metaState |= META_TAB;
- return true;
- case KeyEvent.KEYCODE_SHIFT_LEFT:
- metaPress(META_SHIFT_ON);
- return true;
- case KeyEvent.KEYCODE_ALT_LEFT:
- metaPress(META_ALT_ON);
- return true;
- }
- } else if (PreferenceConstants.KEYMODE_LEFT.equals(keymode)) {
- switch (keyCode) {
- case KeyEvent.KEYCODE_ALT_LEFT:
- metaState |= META_SLASH;
- return true;
- case KeyEvent.KEYCODE_SHIFT_LEFT:
- metaState |= META_TAB;
- return true;
- case KeyEvent.KEYCODE_SHIFT_RIGHT:
- metaPress(META_SHIFT_ON);
- return true;
- case KeyEvent.KEYCODE_ALT_RIGHT:
- metaPress(META_ALT_ON);
- return true;
- }
- } else {
- switch (keyCode) {
- case KeyEvent.KEYCODE_ALT_LEFT:
- case KeyEvent.KEYCODE_ALT_RIGHT:
- metaPress(META_ALT_ON);
- return true;
- case KeyEvent.KEYCODE_SHIFT_LEFT:
- case KeyEvent.KEYCODE_SHIFT_RIGHT:
- metaPress(META_SHIFT_ON);
- return true;
- }
- }
- }
-
- // look for special chars
- switch(keyCode) {
- case KeyEvent.KEYCODE_CAMERA:
-
- // check to see which shortcut the camera button triggers
- String camera = manager.prefs.getString(
- PreferenceConstants.CAMERA,
- PreferenceConstants.CAMERA_CTRLA_SPACE);
- if(PreferenceConstants.CAMERA_CTRLA_SPACE.equals(camera)) {
- transport.write(0x01);
- transport.write(' ');
- } else if(PreferenceConstants.CAMERA_CTRLA.equals(camera)) {
- transport.write(0x01);
- } else if(PreferenceConstants.CAMERA_ESC.equals(camera)) {
- ((vt320)buffer).keyTyped(vt320.KEY_ESCAPE, ' ', 0);
- }
-
- break;
-
- case KeyEvent.KEYCODE_DEL:
- ((vt320) buffer).keyPressed(vt320.KEY_BACK_SPACE, ' ',
- getStateForBuffer());
- metaState &= ~META_TRANSIENT;
- return true;
- case KeyEvent.KEYCODE_ENTER:
- ((vt320)buffer).keyTyped(vt320.KEY_ENTER, ' ', 0);
- metaState &= ~META_TRANSIENT;
- return true;
-
- case KeyEvent.KEYCODE_DPAD_LEFT:
- if (selectingForCopy) {
- selectionArea.decrementColumn();
- redraw();
- } else {
- ((vt320) buffer).keyPressed(vt320.KEY_LEFT, ' ',
- getStateForBuffer());
- metaState &= ~META_TRANSIENT;
- tryKeyVibrate();
- }
- return true;
-
- case KeyEvent.KEYCODE_DPAD_UP:
- if (selectingForCopy) {
- selectionArea.decrementRow();
- redraw();
- } else {
- ((vt320) buffer).keyPressed(vt320.KEY_UP, ' ',
- getStateForBuffer());
- metaState &= ~META_TRANSIENT;
- tryKeyVibrate();
- }
- return true;
-
- case KeyEvent.KEYCODE_DPAD_DOWN:
- if (selectingForCopy) {
- selectionArea.incrementRow();
- redraw();
- } else {
- ((vt320) buffer).keyPressed(vt320.KEY_DOWN, ' ',
- getStateForBuffer());
- metaState &= ~META_TRANSIENT;
- tryKeyVibrate();
- }
- return true;
-
- case KeyEvent.KEYCODE_DPAD_RIGHT:
- if (selectingForCopy) {
- selectionArea.incrementColumn();
- redraw();
- } else {
- ((vt320) buffer).keyPressed(vt320.KEY_RIGHT, ' ',
- getStateForBuffer());
- metaState &= ~META_TRANSIENT;
- tryKeyVibrate();
- }
- return true;
-
- case KeyEvent.KEYCODE_DPAD_CENTER:
- if (selectingForCopy) {
- if (selectionArea.isSelectingOrigin())
- selectionArea.finishSelectingOrigin();
- else {
- if (parent != null && clipboard != null) {
- // copy selected area to clipboard
- String copiedText = selectionArea.copyFrom(buffer);
-
- clipboard.setText(copiedText);
- parent.notifyUser(parent.getContext().getString(
- R.string.console_copy_done,
- copiedText.length()));
-
- selectingForCopy = false;
- selectionArea.reset();
- }
- }
- } else {
- if ((metaState & META_CTRL_ON) != 0) {
- ((vt320)buffer).keyTyped(vt320.KEY_ESCAPE, ' ', 0);
- metaState &= ~META_CTRL_ON;
- } else
- metaState |= META_CTRL_ON;
- }
-
- redraw();
-
- return true;
- }
-
- } catch (IOException e) {
- Log.e(TAG, "Problem while trying to handle an onKey() event", e);
- try {
- transport.flush();
- } catch (IOException ioe) {
- Log.d(TAG, "Our transport was closed, dispatching disconnect event");
- dispatchDisconnect(false);
- }
- } catch (NullPointerException npe) {
- Log.d(TAG, "Input before connection established ignored.");
- return true;
- }
-
- return false;
- }
-
- /**
- * @param key
- * @return successful
- */
- private boolean sendFunctionKey(int keyCode) {
- switch (keyCode) {
- case KeyEvent.KEYCODE_1:
- ((vt320) buffer).keyPressed(vt320.KEY_F1, ' ', 0);
- return true;
- case KeyEvent.KEYCODE_2:
- ((vt320) buffer).keyPressed(vt320.KEY_F2, ' ', 0);
- return true;
- case KeyEvent.KEYCODE_3:
- ((vt320) buffer).keyPressed(vt320.KEY_F3, ' ', 0);
- return true;
- case KeyEvent.KEYCODE_4:
- ((vt320) buffer).keyPressed(vt320.KEY_F4, ' ', 0);
- return true;
- case KeyEvent.KEYCODE_5:
- ((vt320) buffer).keyPressed(vt320.KEY_F5, ' ', 0);
- return true;
- case KeyEvent.KEYCODE_6:
- ((vt320) buffer).keyPressed(vt320.KEY_F6, ' ', 0);
- return true;
- case KeyEvent.KEYCODE_7:
- ((vt320) buffer).keyPressed(vt320.KEY_F7, ' ', 0);
- return true;
- case KeyEvent.KEYCODE_8:
- ((vt320) buffer).keyPressed(vt320.KEY_F8, ' ', 0);
- return true;
- case KeyEvent.KEYCODE_9:
- ((vt320) buffer).keyPressed(vt320.KEY_F9, ' ', 0);
- return true;
- case KeyEvent.KEYCODE_0:
- ((vt320) buffer).keyPressed(vt320.KEY_F10, ' ', 0);
- return true;
- default:
- return false;
- }
- }
-
- /**
- * Handle meta key presses where the key can be locked on.
- * <p>
- * 1st press: next key to have meta state<br />
- * 2nd press: meta state is locked on<br />
- * 3rd press: disable meta state
- *
- * @param code
- */
- private void metaPress(int code) {
- if ((metaState & (code << 1)) != 0) {
- metaState &= ~(code << 1);
- } else if ((metaState & code) != 0) {
- metaState &= ~code;
- metaState |= code << 1;
- } else
- metaState |= code;
- redraw();
- }
-
- public int getMetaState() {
- return metaState;
- }
-
- private int getStateForBuffer() {
- int bufferState = 0;
-
- if ((metaState & META_CTRL_MASK) != 0)
- bufferState |= vt320.KEY_CONTROL;
- if ((metaState & META_SHIFT_MASK) != 0)
- bufferState |= vt320.KEY_SHIFT;
- if ((metaState & META_ALT_MASK) != 0)
- bufferState |= vt320.KEY_ALT;
-
- return bufferState;
- }
-
public void setSelectingForCopy(boolean selectingForCopy) {
this.selectingForCopy = selectingForCopy;
}
@@ -890,7 +473,7 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener {
* Request a different font size. Will make call to parentChanged() to make
* sure we resize PTY if needed.
*/
- private final void setFontSize(float size) {
+ /* package */ final void setFontSize(float size) {
if (size <= 0.0)
return;
@@ -915,6 +498,8 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener {
host.setFontSize((int) fontSize);
manager.hostdb.updateFontSize(host);
+
+ forcedSize = false;
}
/**
@@ -950,14 +535,15 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener {
}
this.parent = parent;
- int width = parent.getWidth();
- int height = parent.getHeight();
+ final int width = parent.getWidth();
+ final int height = parent.getHeight();
// Something has gone wrong with our layout; we're 0 width or height!
if (width <= 0 || height <= 0)
return;
clipboard = (ClipboardManager) parent.getContext().getSystemService(Context.CLIPBOARD_SERVICE);
+ keyListener.setClipboardManager(clipboard);
if (!forcedSize) {
// recalculate buffer size
@@ -1207,10 +793,10 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener {
if (direction > 0)
size -= step;
- forcedSize = true;
this.columns = cols;
this.rows = rows;
setFontSize(size);
+ forcedSize = true;
}
private int fontSizeCompare(float size, int cols, int rows, int width, int height) {
@@ -1359,4 +945,34 @@ public class TerminalBridge implements VDUDisplay, OnKeyListener {
public boolean isUsingNetwork() {
return transport.usesNetwork();
}
+
+ /**
+ * @return
+ */
+ public TerminalKeyListener getKeyHandler() {
+ return keyListener;
+ }
+
+ /**
+ *
+ */
+ public void resetScrollPosition() {
+ // if we're in scrollback, scroll to bottom of window on input
+ if (buffer.windowBase != buffer.screenBase)
+ buffer.setWindowBase(buffer.screenBase);
+ }
+
+ /**
+ *
+ */
+ public void increaseFontSize() {
+ setFontSize(fontSize + FONT_SIZE_STEP);
+ }
+
+ /**
+ *
+ */
+ public void decreaseFontSize() {
+ setFontSize(fontSize - FONT_SIZE_STEP);
+ }
}
diff --git a/src/org/connectbot/service/TerminalKeyListener.java b/src/org/connectbot/service/TerminalKeyListener.java
new file mode 100644
index 0000000..b63dab7
--- /dev/null
+++ b/src/org/connectbot/service/TerminalKeyListener.java
@@ -0,0 +1,486 @@
+/**
+ *
+ */
+package org.connectbot.service;
+
+import java.io.IOException;
+
+import org.connectbot.TerminalView;
+import org.connectbot.bean.HostBean;
+import org.connectbot.bean.SelectionArea;
+import org.connectbot.util.PreferenceConstants;
+
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
+import android.content.res.Configuration;
+import android.preference.PreferenceManager;
+import android.text.ClipboardManager;
+import android.util.Log;
+import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.View.OnKeyListener;
+import de.mud.terminal.VDUBuffer;
+import de.mud.terminal.vt320;
+
+/**
+ * @author kenny
+ *
+ */
+public class TerminalKeyListener implements OnKeyListener, OnSharedPreferenceChangeListener {
+ private static final String TAG = "ConnectBot.OnKeyListener";
+
+ public final static int META_CTRL_ON = 0x01;
+ public final static int META_CTRL_LOCK = 0x02;
+ public final static int META_ALT_ON = 0x04;
+ public final static int META_ALT_LOCK = 0x08;
+ public final static int META_SHIFT_ON = 0x10;
+ public final static int META_SHIFT_LOCK = 0x20;
+ public final static int META_SLASH = 0x40;
+ public final static int META_TAB = 0x80;
+
+ // The bit mask of momentary and lock states for each
+ public final static int META_CTRL_MASK = META_CTRL_ON | META_CTRL_LOCK;
+ public final static int META_ALT_MASK = META_ALT_ON | META_ALT_LOCK;
+ public final static int META_SHIFT_MASK = META_SHIFT_ON | META_SHIFT_LOCK;
+
+ // All the transient key codes
+ public final static int META_TRANSIENT = META_CTRL_ON | META_ALT_ON
+ | META_SHIFT_ON;
+
+ private final TerminalManager manager;
+ private final TerminalBridge bridge;
+ private final VDUBuffer buffer;
+ private final HostBean host;
+
+ protected KeyCharacterMap keymap = KeyCharacterMap.load(KeyCharacterMap.BUILT_IN_KEYBOARD);
+
+ private String keymode = null;
+ private boolean hardKeyboard = false;
+
+ private int metaState = 0;
+
+ private ClipboardManager clipboard = null;
+ private boolean selectingForCopy = false;
+ private final SelectionArea selectionArea;
+
+ private final SharedPreferences prefs;
+
+ public TerminalKeyListener(TerminalManager manager,
+ TerminalBridge bridge,
+ VDUBuffer buffer,
+ HostBean host) {
+ this.manager = manager;
+ this.bridge = bridge;
+ this.buffer = buffer;
+ this.host = host;
+
+ selectionArea = new SelectionArea();
+
+ prefs = PreferenceManager.getDefaultSharedPreferences(manager);
+ prefs.registerOnSharedPreferenceChangeListener(this);
+
+ hardKeyboard = (manager.res.getConfiguration().keyboard
+ == Configuration.KEYBOARD_QWERTY);
+
+ updateKeymode();
+ }
+
+ /**
+ * Handle onKey() events coming down from a {@link TerminalView} above us.
+ * Modify the keys to make more sense to a host then pass it to the transport.
+ */
+ public boolean onKey(View v, int keyCode, KeyEvent event) {
+ try {
+ final boolean hardKeyboardHidden = manager.hardKeyboardHidden;
+
+ // Ignore all key-up events except for the special keys
+ if (event.getAction() == KeyEvent.ACTION_UP) {
+ // There's nothing here for virtual keyboard users.
+ if (!hardKeyboard || (hardKeyboard && hardKeyboardHidden))
+ return false;
+
+ // skip keys if we aren't connected yet or have been disconnected
+ if (bridge.isDisconnected() || bridge.transport == null)
+ return false;
+
+ if (PreferenceConstants.KEYMODE_RIGHT.equals(keymode)) {
+ if (keyCode == KeyEvent.KEYCODE_ALT_RIGHT
+ && (metaState & META_SLASH) != 0) {
+ metaState &= ~(META_SLASH | META_TRANSIENT);
+ bridge.transport.write('/');
+ return true;
+ } else if (keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT
+ && (metaState & META_TAB) != 0) {
+ metaState &= ~(META_TAB | META_TRANSIENT);
+ bridge.transport.write(0x09);
+ return true;
+ }
+ } else if (PreferenceConstants.KEYMODE_LEFT.equals(keymode)) {
+ if (keyCode == KeyEvent.KEYCODE_ALT_LEFT
+ && (metaState & META_SLASH) != 0) {
+ metaState &= ~(META_SLASH | META_TRANSIENT);
+ bridge.transport.write('/');
+ return true;
+ } else if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT
+ && (metaState & META_TAB) != 0) {
+ metaState &= ~(META_TAB | META_TRANSIENT);
+ bridge.transport.write(0x09);
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ // check for terminal resizing keys
+ if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
+ bridge.increaseFontSize();
+ return true;
+ } else if(keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
+ bridge.decreaseFontSize();
+ return true;
+ }
+
+ // skip keys if we aren't connected yet or have been disconnected
+ if (bridge.isDisconnected() || bridge.transport == null)
+ return false;
+
+ bridge.resetScrollPosition();
+
+ boolean printing = (keymap.isPrintingKey(keyCode) || keyCode == KeyEvent.KEYCODE_SPACE);
+
+ // otherwise pass through to existing session
+ // print normal keys
+ if (printing) {
+ int curMetaState = event.getMetaState();
+
+ metaState &= ~(META_SLASH | META_TAB);
+
+ if ((metaState & META_SHIFT_MASK) != 0) {
+ curMetaState |= KeyEvent.META_SHIFT_ON;
+ metaState &= ~META_SHIFT_ON;
+ bridge.redraw();
+ }
+
+ if ((metaState & META_ALT_MASK) != 0) {
+ curMetaState |= KeyEvent.META_ALT_ON;
+ metaState &= ~META_ALT_ON;
+ bridge.redraw();
+ }
+
+ int key = keymap.get(keyCode, curMetaState);
+
+ if ((metaState & META_CTRL_MASK) != 0) {
+ metaState &= ~META_CTRL_ON;
+ bridge.redraw();
+
+ if ((!hardKeyboard || (hardKeyboard && hardKeyboardHidden))
+ && sendFunctionKey(keyCode))
+ return true;
+
+ // Support CTRL-a through CTRL-z
+ if (key >= 0x61 && key <= 0x7A)
+ key -= 0x60;
+ // Support CTRL-A through CTRL-_
+ else if (key >= 0x41 && key <= 0x5F)
+ key -= 0x40;
+ else if (key == 0x20)
+ key = 0x00;
+ else if (key == 0x3F)
+ key = 0x7F;
+ }
+
+ // handle pressing f-keys
+ if ((hardKeyboard && !hardKeyboardHidden)
+ && (curMetaState & KeyEvent.META_SHIFT_ON) != 0
+ && sendFunctionKey(keyCode))
+ return true;
+
+ if (key < 0x80)
+ bridge.transport.write(key);
+ else
+ // TODO write encoding routine that doesn't allocate each time
+ bridge.transport.write(new String(Character.toChars(key))
+ .getBytes(host.getEncoding()));
+
+ return true;
+ }
+
+ if (keyCode == KeyEvent.KEYCODE_UNKNOWN &&
+ event.getAction() == KeyEvent.ACTION_MULTIPLE) {
+ byte[] input = event.getCharacters().getBytes(host.getEncoding());
+ bridge.transport.write(input);
+ return true;
+ }
+
+ // try handling keymode shortcuts
+ if (hardKeyboard && !hardKeyboardHidden &&
+ event.getRepeatCount() == 0) {
+ if (PreferenceConstants.KEYMODE_RIGHT.equals(keymode)) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_ALT_RIGHT:
+ metaState |= META_SLASH;
+ return true;
+ case KeyEvent.KEYCODE_SHIFT_RIGHT:
+ metaState |= META_TAB;
+ return true;
+ case KeyEvent.KEYCODE_SHIFT_LEFT:
+ metaPress(META_SHIFT_ON);
+ return true;
+ case KeyEvent.KEYCODE_ALT_LEFT:
+ metaPress(META_ALT_ON);
+ return true;
+ }
+ } else if (PreferenceConstants.KEYMODE_LEFT.equals(keymode)) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_ALT_LEFT:
+ metaState |= META_SLASH;
+ return true;
+ case KeyEvent.KEYCODE_SHIFT_LEFT:
+ metaState |= META_TAB;
+ return true;
+ case KeyEvent.KEYCODE_SHIFT_RIGHT:
+ metaPress(META_SHIFT_ON);
+ return true;
+ case KeyEvent.KEYCODE_ALT_RIGHT:
+ metaPress(META_ALT_ON);
+ return true;
+ }
+ } else {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_ALT_LEFT:
+ case KeyEvent.KEYCODE_ALT_RIGHT:
+ metaPress(META_ALT_ON);
+ return true;
+ case KeyEvent.KEYCODE_SHIFT_LEFT:
+ case KeyEvent.KEYCODE_SHIFT_RIGHT:
+ metaPress(META_SHIFT_ON);
+ return true;
+ }
+ }
+ }
+
+ // look for special chars
+ switch(keyCode) {
+ case KeyEvent.KEYCODE_CAMERA:
+
+ // check to see which shortcut the camera button triggers
+ String camera = manager.prefs.getString(
+ PreferenceConstants.CAMERA,
+ PreferenceConstants.CAMERA_CTRLA_SPACE);
+ if(PreferenceConstants.CAMERA_CTRLA_SPACE.equals(camera)) {
+ bridge.transport.write(0x01);
+ bridge.transport.write(' ');
+ } else if(PreferenceConstants.CAMERA_CTRLA.equals(camera)) {
+ bridge.transport.write(0x01);
+ } else if(PreferenceConstants.CAMERA_ESC.equals(camera)) {
+ ((vt320)buffer).keyTyped(vt320.KEY_ESCAPE, ' ', 0);
+ }
+
+ break;
+
+ case KeyEvent.KEYCODE_DEL:
+ ((vt320) buffer).keyPressed(vt320.KEY_BACK_SPACE, ' ',
+ getStateForBuffer());
+ metaState &= ~META_TRANSIENT;
+ return true;
+ case KeyEvent.KEYCODE_ENTER:
+ ((vt320)buffer).keyTyped(vt320.KEY_ENTER, ' ', 0);
+ metaState &= ~META_TRANSIENT;
+ return true;
+
+ case KeyEvent.KEYCODE_DPAD_LEFT:
+ if (selectingForCopy) {
+ selectionArea.decrementColumn();
+ bridge.redraw();
+ } else {
+ ((vt320) buffer).keyPressed(vt320.KEY_LEFT, ' ',
+ getStateForBuffer());
+ metaState &= ~META_TRANSIENT;
+ bridge.tryKeyVibrate();
+ }
+ return true;
+
+ case KeyEvent.KEYCODE_DPAD_UP:
+ if (selectingForCopy) {
+ selectionArea.decrementRow();
+ bridge.redraw();
+ } else {
+ ((vt320) buffer).keyPressed(vt320.KEY_UP, ' ',
+ getStateForBuffer());
+ metaState &= ~META_TRANSIENT;
+ bridge.tryKeyVibrate();
+ }
+ return true;
+
+ case KeyEvent.KEYCODE_DPAD_DOWN:
+ if (selectingForCopy) {
+ selectionArea.incrementRow();
+ bridge.redraw();
+ } else {
+ ((vt320) buffer).keyPressed(vt320.KEY_DOWN, ' ',
+ getStateForBuffer());
+ metaState &= ~META_TRANSIENT;
+ bridge.tryKeyVibrate();
+ }
+ return true;
+
+ case KeyEvent.KEYCODE_DPAD_RIGHT:
+ if (selectingForCopy) {
+ selectionArea.incrementColumn();
+ bridge.redraw();
+ } else {
+ ((vt320) buffer).keyPressed(vt320.KEY_RIGHT, ' ',
+ getStateForBuffer());
+ metaState &= ~META_TRANSIENT;
+ bridge.tryKeyVibrate();
+ }
+ return true;
+
+ case KeyEvent.KEYCODE_DPAD_CENTER:
+ if (selectingForCopy) {
+ if (selectionArea.isSelectingOrigin())
+ selectionArea.finishSelectingOrigin();
+ else {
+ if (clipboard != null) {
+ // copy selected area to clipboard
+ String copiedText = selectionArea.copyFrom(buffer);
+
+ clipboard.setText(copiedText);
+ // XXX STOPSHIP
+// manager.notifyUser(manager.getString(
+// R.string.console_copy_done,
+// copiedText.length()));
+
+ selectingForCopy = false;
+ selectionArea.reset();
+ }
+ }
+ } else {
+ if ((metaState & META_CTRL_ON) != 0) {
+ ((vt320)buffer).keyTyped(vt320.KEY_ESCAPE, ' ', 0);
+ metaState &= ~META_CTRL_ON;
+ } else
+ metaState |= META_CTRL_ON;
+ }
+
+ bridge.redraw();
+
+ return true;
+ }
+
+ } catch (IOException e) {
+ Log.e(TAG, "Problem while trying to handle an onKey() event", e);
+ try {
+ bridge.transport.flush();
+ } catch (IOException ioe) {
+ Log.d(TAG, "Our transport was closed, dispatching disconnect event");
+ bridge.dispatchDisconnect(false);
+ }
+ } catch (NullPointerException npe) {
+ Log.d(TAG, "Input before connection established ignored.");
+ return true;
+ }
+
+ return false;
+ }
+
+
+ /**
+ * @param key
+ * @return successful
+ */
+ private boolean sendFunctionKey(int keyCode) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_1:
+ ((vt320) buffer).keyPressed(vt320.KEY_F1, ' ', 0);
+ return true;
+ case KeyEvent.KEYCODE_2:
+ ((vt320) buffer).keyPressed(vt320.KEY_F2, ' ', 0);
+ return true;
+ case KeyEvent.KEYCODE_3:
+ ((vt320) buffer).keyPressed(vt320.KEY_F3, ' ', 0);
+ return true;
+ case KeyEvent.KEYCODE_4:
+ ((vt320) buffer).keyPressed(vt320.KEY_F4, ' ', 0);
+ return true;
+ case KeyEvent.KEYCODE_5:
+ ((vt320) buffer).keyPressed(vt320.KEY_F5, ' ', 0);
+ return true;
+ case KeyEvent.KEYCODE_6:
+ ((vt320) buffer).keyPressed(vt320.KEY_F6, ' ', 0);
+ return true;
+ case KeyEvent.KEYCODE_7:
+ ((vt320) buffer).keyPressed(vt320.KEY_F7, ' ', 0);
+ return true;
+ case KeyEvent.KEYCODE_8:
+ ((vt320) buffer).keyPressed(vt320.KEY_F8, ' ', 0);
+ return true;
+ case KeyEvent.KEYCODE_9:
+ ((vt320) buffer).keyPressed(vt320.KEY_F9, ' ', 0);
+ return true;
+ case KeyEvent.KEYCODE_0:
+ ((vt320) buffer).keyPressed(vt320.KEY_F10, ' ', 0);
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Handle meta key presses where the key can be locked on.
+ * <p>
+ * 1st press: next key to have meta state<br />
+ * 2nd press: meta state is locked on<br />
+ * 3rd press: disable meta state
+ *
+ * @param code
+ */
+ private void metaPress(int code) {
+ if ((metaState & (code << 1)) != 0) {
+ metaState &= ~(code << 1);
+ } else if ((metaState & code) != 0) {
+ metaState &= ~code;
+ metaState |= code << 1;
+ } else
+ metaState |= code;
+ bridge.redraw();
+ }
+
+ public void setTerminalKeyMode(String keymode) {
+ this.keymode = keymode;
+ }
+
+ private int getStateForBuffer() {
+ int bufferState = 0;
+
+ if ((metaState & META_CTRL_MASK) != 0)
+ bufferState |= vt320.KEY_CONTROL;
+ if ((metaState & META_SHIFT_MASK) != 0)
+ bufferState |= vt320.KEY_SHIFT;
+ if ((metaState & META_ALT_MASK) != 0)
+ bufferState |= vt320.KEY_ALT;
+
+ return bufferState;
+ }
+
+ public int getMetaState() {
+ return metaState;
+ }
+
+ public void setClipboardManager(ClipboardManager clipboard) {
+ this.clipboard = clipboard;
+ }
+
+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
+ String key) {
+ if (PreferenceConstants.KEYMODE.equals(key)) {
+ updateKeymode();
+ }
+ }
+
+ private void updateKeymode() {
+ keymode = prefs.getString(PreferenceConstants.KEYMODE, PreferenceConstants.KEYMODE_RIGHT);
+ }
+}
diff --git a/src/org/connectbot/service/TerminalManager.java b/src/org/connectbot/service/TerminalManager.java
index 0f302f7..c378b14 100644
--- a/src/org/connectbot/service/TerminalManager.java
+++ b/src/org/connectbot/service/TerminalManager.java
@@ -45,6 +45,7 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.content.res.AssetFileDescriptor;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.media.AudioManager;
import android.media.MediaPlayer;
@@ -115,6 +116,8 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen
protected List<WeakReference<TerminalBridge>> mPendingReconnect
= new LinkedList<WeakReference<TerminalBridge>>();
+ public boolean hardKeyboardHidden;
+
@Override
public void onCreate() {
Log.i(TAG, "Starting background service");
@@ -152,6 +155,9 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen
wantBellVibration = prefs.getBoolean(PreferenceConstants.BELL_VIBRATE, true);
enableMediaPlayer();
+ hardKeyboardHidden = (res.getConfiguration().hardKeyboardHidden ==
+ Configuration.HARDKEYBOARDHIDDEN_YES);
+
final boolean lockingWifi = prefs.getBoolean(PreferenceConstants.WIFI_LOCK, true);
connectivityManager = new ConnectivityReceiver(this, lockingWifi);
@@ -259,10 +265,6 @@ public class TerminalManager extends Service implements BridgeDisconnectedListen
return scrollback;
}
- public String getKeyMode() {
- return prefs.getString(PreferenceConstants.KEYMODE, PreferenceConstants.KEYMODE_RIGHT); // "Use right-side keys"
- }
-
/**
* Open a new connection by reading parameters from the given URI. Follows
* format specified by an individual transport.