View Javadoc

1   package org.lsst.ccs.demo.shell;
2   
3   import com.wittams.gritty.Questioner;
4   import com.wittams.gritty.RequestOrigin;
5   import com.wittams.gritty.ResizePanelDelegate;
6   import com.wittams.gritty.Tty;
7   import com.wittams.gritty.swing.GrittyTerminal;
8   import com.wittams.gritty.swing.TermPanel;
9   import java.awt.Dimension;
10  import java.io.IOException;
11  import java.io.InputStream;
12  import java.io.InterruptedIOException;
13  import java.io.OutputStream;
14  import java.util.concurrent.BlockingQueue;
15  import java.util.concurrent.LinkedBlockingQueue;
16  import javax.swing.JFrame;
17  import jline.Terminal;
18  import jline.console.ConsoleReader;
19  import org.lsst.ccs.command.CommandSet;
20  import org.lsst.ccs.command.CommandSetBuilder;
21  import org.lsst.ccs.command.demo.DemoCommands;
22  import org.lsst.ccs.shell.JLineShell;
23  import org.lsst.ccs.shell.JLineShell;
24  
25  /**
26   * This is an example of embedding a JLine compatible terminal inside a swing
27   * component. This could be used to make the command line tools available inside
28   * the JAS based console. It uses a terminal emulator called gritty which I found
29   * <a href="https://code.google.com/p/gritty/">here</a>. This does not seem very
30   * complete or well supported, but perhaps adequate to our needs.
31   * 
32   * //FIXME: Resizing not really working
33   * //FIXME: Whole application quits when window closed
34   * @author tonyj
35   */
36  public class SwingShell {
37  
38      private GrittyTerminal terminal;
39      private final JLineShell shell;
40      private final JFrame frame;
41  
42      public SwingShell(CommandSet userCommands, String title) throws IOException {
43          frame = new JFrame(title);
44          terminal = new GrittyTerminal();
45          TermPanel termPanel = terminal.getTermPanel();
46          frame.setContentPane(terminal);
47          frame.pack();
48          frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
49          frame.setLocationByPlatform(true);
50          termPanel.setVisible(true);
51          frame.setVisible(true);
52          frame.setResizable(true);
53  
54          termPanel.setResizePanelDelegate(new ResizePanelDelegate() {
55              @Override
56              public void resizedPanel(final Dimension pixelDimension, final RequestOrigin origin) {
57                  if (origin == RequestOrigin.Remote) {
58                      sizeFrameForTerm(frame);
59                  }
60              }
61          });
62          final TtyImpl tty = new TtyImpl();
63  
64          terminal.setTty(tty);
65          ConsoleReader reader = new ConsoleReader(tty.getInputStream(), tty.getOutputStream(), new SwingTerminal());
66          shell = new JLineShell(userCommands, reader);
67      }
68  
69      public void run() throws IOException {
70          terminal.start();
71          try {
72              shell.run();
73          } finally {
74              terminal.stop();
75              frame.setVisible(false);
76          }
77      }
78  
79      public static void main(String[] args) throws IOException {
80          CommandSetBuilder builder = new CommandSetBuilder();
81          SwingShell swingShell = new SwingShell(builder.buildCommandSet(new DemoCommands()),"Swing Shell");
82          swingShell.run();
83      }
84  
85      private void sizeFrameForTerm(final JFrame frame) {
86          Dimension d = terminal.getPreferredSize();
87  
88          d.width += frame.getWidth() - frame.getContentPane().getWidth();
89          d.height += frame.getHeight() - frame.getContentPane().getHeight();
90          frame.setSize(d);
91      }
92  
93      private static class SwingTerminal implements Terminal {
94  
95          @Override
96          public void init() throws Exception {
97          }
98  
99          @Override
100         public void restore() throws Exception {
101         }
102 
103         @Override
104         public void reset() throws Exception {
105         }
106 
107         @Override
108         public boolean isSupported() {
109             return true;
110         }
111 
112         @Override
113         public int getWidth() {
114             //FIXME: should be calculated based on size of window
115             return 80;
116         }
117 
118         @Override
119         public int getHeight() {
120             //FIXME: should be calculated based on size of window
121             return 100;
122         }
123 
124         @Override
125         public boolean isAnsiSupported() {
126             return false;
127         }
128 
129         @Override
130         public OutputStream wrapOutIfNeeded(OutputStream out) {
131             return out;
132         }
133 
134         @Override
135         public InputStream wrapInIfNeeded(InputStream in) throws IOException {
136             return in;
137         }
138 
139         @Override
140         public boolean hasWeirdWrap() {
141             return false;
142         }
143 
144         @Override
145         public boolean isEchoEnabled() {
146             return false;
147         }
148 
149         @Override
150         public void setEchoEnabled(boolean bln) {
151         }
152     }
153 
154     private class TtyImpl implements Tty {
155 
156         private final BlockingQueue<byte[]> inputQueue = new LinkedBlockingQueue<>();
157         private final BlockingQueue<byte[]> outputQueue = new LinkedBlockingQueue<>();
158         private final TtyInputStream inputStream;
159         private final TtyOutputStream outputStream;
160 
161         public TtyImpl() {
162             inputStream = new TtyInputStream();
163             outputStream = new TtyOutputStream();
164         }
165 
166         @Override
167         public boolean init(Questioner qstnr) {
168             return true;
169         }
170 
171         @Override
172         public void close() {
173         }
174 
175         @Override
176         public void resize(Dimension dmnsn, Dimension dmnsn1) {
177         }
178 
179         @Override
180         public String getName() {
181             return "Test";
182         }
183 
184         @Override
185         public int read(byte[] bytes, int i, int i1) throws IOException {
186             try {
187                 byte[] src = outputQueue.take();
188                 // FIXME: need to handle case where src does not fit in target
189                 System.arraycopy(src, 0, bytes, i, src.length);
190                 return src.length;
191             } catch (InterruptedException ex) {
192                 throw new InterruptedIOException();
193             }
194         }
195 
196         @Override
197         public void write(byte[] bytes) throws IOException {
198             try {
199                 inputQueue.put(bytes);
200             } catch (InterruptedException ex) {
201                 throw new InterruptedIOException();
202             }
203         }
204 
205         public TtyInputStream getInputStream() {
206             return inputStream;
207         }
208 
209         public TtyOutputStream getOutputStream() {
210             return outputStream;
211         }
212 
213         private class TtyInputStream extends InputStream {
214 
215             private byte[] currentBuffer;
216             private int pos;
217 
218             @Override
219             public int read() throws IOException {
220                 try {
221                     while (currentBuffer == null || currentBuffer.length <= pos) {
222                         currentBuffer = inputQueue.take();
223                         pos = 0;
224                     }
225                     return currentBuffer[pos++];
226                 } catch (InterruptedException interruptedException) {
227                     throw new InterruptedIOException();
228                 }
229             }
230 
231             @Override
232             public int read(byte[] b, int off, int len) throws IOException {
233                 try {
234                     if (currentBuffer == null || currentBuffer.length <= pos) {
235                         currentBuffer = inputQueue.take();
236                         pos = 0;
237                     }
238                     int actual = Math.min(len, currentBuffer.length - pos);
239                     System.arraycopy(currentBuffer, pos, b, off, actual);
240                     pos += actual;
241                     return actual;
242                 } catch (InterruptedException interruptedException) {
243                     throw new InterruptedIOException();
244                 }
245             }
246         }
247 
248         private class TtyOutputStream extends OutputStream {
249 
250             @Override
251             public void write(int b) throws IOException {
252                 try {
253                     byte[] buffer = new byte[1];
254                     buffer[0] = (byte) b;
255                     outputQueue.put(buffer);
256                     if (b == 0xa) {
257                         byte[] buffer2 = new byte[1];
258                         buffer2[0] = 0xd;
259                         outputQueue.put(buffer2);
260                     }
261                 } catch (InterruptedException interruptedException) {
262                     throw new InterruptedIOException();
263                 }
264             }
265         }
266     }
267 }