/*
* Copyright 2010 jOpenRay, ILM Informatique
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package org.jopenray.adapter;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.io.InputStream;
import java.io.OutputStream;
import org.jopenray.operation.CopyOperation;
import org.jopenray.server.session.Session;
import org.jopenray.server.thinclient.DisplayMessage;
import org.jopenray.server.thinclient.InputListener;
import org.jopenray.server.thinclient.ThinClient;
import org.jopenray.util.VeraFont;
import com.jcraft.jcterm.Connection;
import com.jcraft.jcterm.Emulator;
import com.jcraft.jcterm.EmulatorVT100;
import com.jcraft.jcterm.JSchSession;
import com.jcraft.jcterm.Term;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelShell;
import com.jcraft.jsch.Proxy;
import com.jcraft.jsch.UserInfo;
public class SshAdapter implements InputListener, Term {
private OutputStream out;
private InputStream in;
private Emulator emulator;
private Connection connection;
private BufferedImage img;
private BufferedImage background;
private Graphics2D cursor_graphics;
private Graphics2D graphics;
private Color defaultbground = Color.black;
private Color defaultfground = Color.white;
private Color bground = Color.black;
private Color fground = Color.white;
private Font font;
private boolean bold = false;
private boolean underline = false;
private boolean reverse = false;
private int term_width = 80;
private int term_height = 24;
private int descent = 0;
private int x = 0;
private int y = 0;
private int char_width;
private int char_height;
private int line_space = -2;
private boolean antialiasing = true;
private final Object[] colors = { Color.black, new Color(189, 105, 130),
new Color(61, 241, 121), Color.yellow, new Color(105, 130, 189),
Color.magenta, Color.cyan, new Color(240, 240, 240) };
private ThinClient client;
public SshAdapter() {
configure();
background = new BufferedImage(char_width, char_height,
BufferedImage.TYPE_INT_RGB);
{
Graphics2D foog = (Graphics2D) (background.getGraphics());
foog.setColor(getBackGround());
foog.fillRect(0, 0, char_width, char_height);
foog.dispose();
}
}
private void configure() {
font = VeraFont.getMonoFont();
if (font == null) {
font = new Font("serif", Font.PLAIN, 14);
}
font = font.deriveFont(14f);
BufferedImage img = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
Graphics2D graphics = (Graphics2D) (img.getGraphics());
graphics.setFont(font);
{
FontMetrics fo = graphics.getFontMetrics();
descent = fo.getDescent();
char_width = (int) (fo.charWidth((char) '@'));
char_height = (int) (fo.getHeight()) + (line_space * 2) + 1;
descent += line_space;
}
img.flush();
graphics.dispose();
}
public void setSize(int w, int h) {
BufferedImage imgOrg = img;
if (graphics != null)
graphics.dispose();
int column = w / getCharWidth();
int row = h / getCharHeight();
term_width = column;
term_height = row;
if (emulator != null)
emulator.reset();
img = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
graphics = (Graphics2D) (img.getGraphics());
graphics.setFont(font);
clearArea(0, 0, w, h);
if (imgOrg != null) {
Shape clip = graphics.getClip();
graphics.setClip(0, 0, getTermWidth(), getTermHeight());
graphics.drawImage(imgOrg, 0, 0, null);
graphics.setClip(clip);
}
if (cursor_graphics != null)
cursor_graphics.dispose();
cursor_graphics = (Graphics2D) (img.getGraphics());
cursor_graphics.setColor(getForeGround());
cursor_graphics.setXORMode(getBackGround());
setAntiAliasing(antialiasing);
if (connection != null) {
connection.requestResize(this);
}
if (imgOrg != null) {
imgOrg.flush();
imgOrg = null;
}
}
private void repaint(int x, int y, int width, int height) {
if (img != null) {
if (y + height > img.getHeight()) {
height = img.getHeight() - y;
}
if (x + width > img.getWidth()) {
width = img.getWidth() - x;
}
System.err.println("SshAdapter.repaint() " + x + "," + y + " "
+ width + "x" + height);
// Thread.dumpStack();
try {
BufferedImage image = img.getSubimage(x, y, width, height);
this.client.getWriter().sendImage(image, x, y);
} catch (Exception e) {
System.err.println("SshAdapter.repaint() " + x + "," + y + " "
+ width + "x" + height);
// TODO: handle exception
e.printStackTrace();
}
} else {
System.out.println("SshAdapter.repaint() null img");
}
}
byte[] obuffer = new byte[3];
public void keyTyped(KeyEvent e) {
}
public int getTermWidth() {
return char_width * term_width;
}
public int getTermHeight() {
return char_height * term_height;
}
public int getCharWidth() {
return char_width;
}
public int getCharHeight() {
return char_height;
}
public int getColumnCount() {
return term_width;
}
public int getRowCount() {
return term_height;
}
public void clear() {
graphics.setColor(getBackGround());
graphics.fillRect(0, 0, char_width * term_width, char_height
* term_height);
graphics.setColor(getForeGround());
}
public void setCursor(int x, int y) {
// System.out.println("setCursor: "+x+","+y);
this.x = x;
this.y = y;
}
public void drawCursor() {
cursor_graphics.fillRect(x, y - char_height, char_width, char_height);
repaint(x, y - char_height, char_width, char_height);
}
public void redraw(int x, int y, int width, int height) {
repaint(x, y, width, height);
}
public void clearArea(int x1, int y1, int x2, int y2) {
// System.out.println("clear_area: "+x1+" "+y1+" "+x2+" "+y2);
graphics.setColor(getBackGround());
graphics.fillRect(x1, y1, x2 - x1, y2 - y1);
graphics.setColor(getForeGround());
}
public void scrollArea(int x, int y, int w, int h, int dx, int dy) {
System.err.println("scroll_area: " + x + " " + y + " " + w + " " + h
+ " " + dx + " " + dy);
graphics.copyArea(x, y, w, h, dx, dy);
// repaint(x + dx, y + dy, w, h);
DisplayMessage m = new DisplayMessage(this.client.getWriter());
m.addOperation(new CopyOperation(x + dx, y + dy, w, h, x, y));
this.client.getWriter().addMessage(m);
if (dy > 0) {
repaint(x, y, w, dy);
repaint(x, y + dy, dx, h - dy);
} else {
repaint(x, y, -dy, h + dy);
repaint(x, h + dy, w, -dy);
}
System.err.println("scroll_area: " + x + " " + y + " " + w + " " + h
+ " " + dx + " " + dy + " ok");
}
public void drawBytes(byte[] buf, int s, int len, int x, int y) {
// clear_area(x, y, x+len*char_width, y+char_height);
// graphics.setColor(getForeGround());
// System.out.println("drawString: "+x+","+y+" "+len+" "+new String(buf,
// s, len));
graphics.drawBytes(buf, s, len, x, y - descent);
if (bold)
graphics.drawBytes(buf, s, len, x + 1, y - descent);
if (underline) {
graphics.drawLine(x, y - 1, x + len * char_width, y - 1);
}
}
public void drawString(String str, int x, int y) {
// clear_area(x, y, x+str.length()*char_width, y+char_height);
// graphics.setColor(getForeGround());
graphics.drawString(str, x, y - descent);
if (bold) {
graphics.drawString(str, x + 1, y - descent);
}
if (underline) {
graphics.drawLine(x, y - 1, x + str.length() * char_width, y - 1);
}
}
public void beep() {
}
public void setLineSpace(int foo) {
this.line_space = foo;
}
public boolean getAntiAliasing() {
return antialiasing;
}
public void setAntiAliasing(boolean foo) {
if (graphics == null)
return;
antialiasing = foo;
Object mode = foo ? RenderingHints.VALUE_TEXT_ANTIALIAS_ON
: RenderingHints.VALUE_TEXT_ANTIALIAS_OFF;
RenderingHints hints = new RenderingHints(
RenderingHints.KEY_TEXT_ANTIALIASING, mode);
graphics.setRenderingHints(hints);
}
private Color toColor(Object o) {
if (o instanceof String) {
return java.awt.Color.getColor((String) o);
}
if (o instanceof java.awt.Color) {
return (java.awt.Color) o;
}
return Color.white;
}
public void setDefaultForeGround(Object f) {
defaultfground = toColor(f);
}
public void setDefaultBackGround(Object f) {
defaultbground = toColor(f);
}
public void setForeGround(Object f) {
fground = toColor(f);
graphics.setColor(getForeGround());
}
public void setBackGround(Object b) {
bground = toColor(b);
Graphics2D foog = (Graphics2D) (background.getGraphics());
foog.setColor(getBackGround());
foog.fillRect(0, 0, char_width, char_height);
foog.dispose();
}
private Color getForeGround() {
if (reverse)
return bground;
return fground;
}
private Color getBackGround() {
if (reverse)
return fground;
return bground;
}
public Object getColor(int index) {
if (colors == null || index < 0 || colors.length <= index)
return null;
return colors[index];
}
public void setBold() {
bold = true;
}
public void setUnderline() {
underline = true;
}
public void setReverse() {
reverse = true;
if (graphics != null)
graphics.setColor(getForeGround());
}
public void resetAllAttributes() {
bold = false;
underline = false;
reverse = false;
bground = defaultbground;
fground = defaultfground;
if (graphics != null) {
graphics.setColor(getForeGround());
}
}
@Override
public void keyPressed(int key, boolean shift, boolean ctrl, boolean alt,
boolean meta, boolean altGr) {
byte[] code = null;
switch (key) {
case KeyEvent.VK_CONTROL:
case KeyEvent.VK_SHIFT:
case KeyEvent.VK_ALT:
case KeyEvent.VK_CAPS_LOCK:
return;
case KeyEvent.VK_ENTER:
code = emulator.getCodeENTER();
break;
case KeyEvent.VK_UP:
code = emulator.getCodeUP();
break;
case KeyEvent.VK_DOWN:
code = emulator.getCodeDOWN();
break;
case KeyEvent.VK_RIGHT:
code = emulator.getCodeRIGHT();
break;
case KeyEvent.VK_LEFT:
code = emulator.getCodeLEFT();
break;
case KeyEvent.VK_F1:
code = emulator.getCodeF1();
break;
case KeyEvent.VK_F2:
code = emulator.getCodeF2();
break;
case KeyEvent.VK_F3:
code = emulator.getCodeF3();
break;
case KeyEvent.VK_F4:
code = emulator.getCodeF4();
break;
case KeyEvent.VK_F5:
code = emulator.getCodeF5();
break;
case KeyEvent.VK_F6:
code = emulator.getCodeF6();
break;
case KeyEvent.VK_F7:
code = emulator.getCodeF7();
break;
case KeyEvent.VK_F8:
code = emulator.getCodeF8();
break;
case KeyEvent.VK_F9:
code = emulator.getCodeF9();
break;
case KeyEvent.VK_F10:
code = emulator.getCodeF10();
break;
case KeyEvent.VK_TAB:
code = emulator.getCodeTAB();
break;
}
if (code != null) {
try {
out.write(code, 0, code.length);
out.flush();
} catch (Exception ee) {
}
return;
}
char keychar = getCharFromKeyCode(key, shift, ctrl, alt, meta);
System.out.println("Keypressed keychar:" + (int) keychar + " '"
+ keychar + "' from keycode:" + key);
if ((keychar & 0xff00) == 0) {
System.out.println("Sending keychar:" + keychar + " from keycode:"
+ key);
obuffer[0] = (byte) keychar;
try {
out.write(obuffer, 0, 1);
out.flush();
} catch (Exception ee) {
}
}
}
private char getCharFromKeyCode(int key, boolean shift, boolean ctrl,
boolean alt, boolean meta) {
char c = (char) key;
if (!shift) {
if (key >= KeyEvent.VK_A && key <= KeyEvent.VK_Z) {
// A -> a
c += 32;
}
}
return c;
}
@Override
public void keyReleased(int key) {
char keychar = getCharFromKeyCode(key, false, false, false, false);
if ((keychar & 0xff00) != 0) {
char[] foo = new char[1];
foo[0] = keychar;
try {
byte[] goo = new String(foo).getBytes("EUC-JP");
out.write(goo, 0, goo.length);
out.flush();
} catch (Exception eee) {
}
}
}
@Override
public void mouseMoved(int mouseX, int mouseY) {
// TODO Auto-generated method stub
}
@Override
public void mousePressed(int button, int mouseX, int mouseY) {
// TODO Auto-generated method stub
}
@Override
public void mouseReleased(int button, int mouseX, int mouseY) {
// TODO Auto-generated method stub
}
@Override
public void mouseWheelDown(int mouseX, int mouseY) {
// TODO Auto-generated method stub
}
@Override
public void mouseWheelUp(int mouseX, int mouseY) {
// TODO Auto-generated method stub
}
JSchSession jschsession;
Proxy proxy = null;
public void start(ThinClient thinClient, Session session) {
this.client = thinClient;
int port = 22;
try {
UserInfo ui = new UserInfo() {
@Override
public String getPassphrase() {
// TODO Auto-generated method stub
return null;
}
@Override
public String getPassword() {
// TODO Auto-generated method stub
return null;
}
@Override
public boolean promptPassphrase(String message) {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean promptPassword(String message) {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean promptYesNo(String message) {
return true;
}
@Override
public void showMessage(String message) {
// TODO Auto-generated method stub
}
};
jschsession = JSchSession.getSession(session.getLogin(), session
.getPassword(), session.getServer(), port, ui, proxy);
java.util.Properties config = new java.util.Properties();
config.put("compression.s2c", "none");
config.put("compression.c2s", "none");
jschsession.getSession().setConfig(config);
jschsession.getSession().rekey();
Channel channel = null;
OutputStream out = null;
InputStream in = null;
channel = jschsession.getSession().openChannel("shell");
out = channel.getOutputStream();
in = channel.getInputStream();
channel.connect();
final OutputStream fout = out;
final InputStream fin = in;
final Channel fchannel = channel;
connection = new Connection() {
public InputStream getInputStream() {
return fin;
}
public OutputStream getOutputStream() {
return fout;
}
public void requestResize(Term term) {
if (fchannel instanceof ChannelShell) {
int c = term.getColumnCount();
int r = term.getRowCount();
((ChannelShell) fchannel)
.setPtySize(c, r, c * term.getCharWidth(), r
* term.getCharHeight());
}
}
public void close() {
fchannel.disconnect();
}
};
setSize(thinClient.getScreenWidth(), thinClient.getScreenHeight());
start(connection);
} catch (Exception e) {
e.printStackTrace();
}
}
public void start(Connection connection) {
this.connection = connection;
in = connection.getInputStream();
out = connection.getOutputStream();
emulator = new EmulatorVT100(this, in);
emulator.reset();
emulator.start();
clear();
redraw(0, 0, getTermWidth(), getTermHeight());
}
public void stop() {
this.connection.close();
}
}