AWT keyboard barely working

This commit is contained in:
Kevin Lamonte 2015-03-16 11:45:46 -04:00
parent 1ac2ccb131
commit 84614868e6
7 changed files with 528 additions and 64 deletions

View file

@ -30,10 +30,11 @@
<project name="jexer" basedir="." default="run">
<property name="src.dir" value="src"/>
<property name="build.dir" value="build"/>
<property name="classes.dir" value="${build.dir}/classes"/>
<property name="jar.dir" value="${build.dir}/jar"/>
<property name="src.dir" value="src"/>
<property name="resources.dir" value="resources"/>
<property name="build.dir" value="build"/>
<property name="classes.dir" value="${build.dir}/classes"/>
<property name="jar.dir" value="${build.dir}/jar"/>
<target name="clean">
<delete dir="${build.dir}"/>
@ -49,6 +50,7 @@
<mkdir dir="${jar.dir}"/>
<jar destfile="${jar.dir}/${ant.project.name}.jar"
basedir="${classes.dir}">
<fileset dir="${resources.dir}"/>
<manifest>
<attribute name="Main-Class" value="jexer.demos.Demo1"/>
</manifest>

View file

@ -0,0 +1,97 @@
Copyright (c) 2010 Dimitar Toshkov Zhekov,
with Reserved Font Name "Terminus Font".
Copyright (c) 2011 Tilman Blumenbach,
with Reserved Font Name "Terminus (TTF)".
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

Binary file not shown.

Binary file not shown.

View file

@ -39,18 +39,72 @@ import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.geom.Rectangle2D;
import java.io.InputStream;
/**
* This Screen implementation draws to a Java AWT Frame.
*/
public final class AWTScreen extends Screen {
private static Color MYBLACK;
private static Color MYRED;
private static Color MYGREEN;
private static Color MYYELLOW;
private static Color MYBLUE;
private static Color MYMAGENTA;
private static Color MYCYAN;
private static Color MYWHITE;
private static Color MYBOLD_BLACK;
private static Color MYBOLD_RED;
private static Color MYBOLD_GREEN;
private static Color MYBOLD_YELLOW;
private static Color MYBOLD_BLUE;
private static Color MYBOLD_MAGENTA;
private static Color MYBOLD_CYAN;
private static Color MYBOLD_WHITE;
private static boolean dosColors = false;
/**
* Setup AWT colors to match DOS color palette.
*/
private static void setDOSColors() {
if (dosColors) {
return;
}
MYBLACK = new Color(0x00, 0x00, 0x00);
MYRED = new Color(0xa8, 0x00, 0x00);
MYGREEN = new Color(0x00, 0xa8, 0x00);
MYYELLOW = new Color(0xa8, 0x54, 0x00);
MYBLUE = new Color(0x00, 0x00, 0xa8);
MYMAGENTA = new Color(0xa8, 0x00, 0xa8);
MYCYAN = new Color(0x00, 0xa8, 0xa8);
MYWHITE = new Color(0xa8, 0xa8, 0xa8);
MYBOLD_BLACK = new Color(0x54, 0x54, 0x54);
MYBOLD_RED = new Color(0xfc, 0x54, 0x54);
MYBOLD_GREEN = new Color(0x54, 0xfc, 0x54);
MYBOLD_YELLOW = new Color(0xfc, 0xfc, 0x54);
MYBOLD_BLUE = new Color(0x54, 0x54, 0xfc);
MYBOLD_MAGENTA = new Color(0xfc, 0x54, 0xfc);
MYBOLD_CYAN = new Color(0x54, 0xfc, 0xfc);
MYBOLD_WHITE = new Color(0xfc, 0xfc, 0xfc);
dosColors = true;
}
/**
* AWTFrame is our top-level hook into the AWT system.
*/
class AWTFrame extends Frame {
/**
* The terminus font resource filename.
*/
private static final String FONTFILE = "terminus-ttf-4.39/TerminusTTF-Bold-4.39.ttf";
/**
* The TUI Screen data.
*/
@ -66,41 +120,165 @@ public final class AWTScreen extends Screen {
*/
private int textHeight = 1;
/**
* Descent of a character cell.
*/
private int maxDescent = 0;
/**
* Top pixel value.
*/
private int top = 30;
/**
* Left pixel value.
*/
private int left = 30;
/**
* Convert a CellAttributes foreground color to an AWT Color.
*
* @param attr the text attributes
* @return the AWT Color
*/
private Color attrToForegroundColor(final CellAttributes attr) {
/*
* TODO:
* reverse
* blink
* underline
*/
if (attr.getBold()) {
if (attr.getForeColor().equals(jexer.bits.Color.BLACK)) {
return MYBOLD_BLACK;
} else if (attr.getForeColor().equals(jexer.bits.Color.RED)) {
return MYBOLD_RED;
} else if (attr.getForeColor().equals(jexer.bits.Color.BLUE)) {
return MYBOLD_BLUE;
} else if (attr.getForeColor().equals(jexer.bits.Color.GREEN)) {
return MYBOLD_GREEN;
} else if (attr.getForeColor().equals(jexer.bits.Color.YELLOW)) {
return MYBOLD_YELLOW;
} else if (attr.getForeColor().equals(jexer.bits.Color.CYAN)) {
return MYBOLD_CYAN;
} else if (attr.getForeColor().equals(jexer.bits.Color.MAGENTA)) {
return MYBOLD_MAGENTA;
} else if (attr.getForeColor().equals(jexer.bits.Color.WHITE)) {
return MYBOLD_WHITE;
}
} else {
if (attr.getForeColor().equals(jexer.bits.Color.BLACK)) {
return MYBLACK;
} else if (attr.getForeColor().equals(jexer.bits.Color.RED)) {
return MYRED;
} else if (attr.getForeColor().equals(jexer.bits.Color.BLUE)) {
return MYBLUE;
} else if (attr.getForeColor().equals(jexer.bits.Color.GREEN)) {
return MYGREEN;
} else if (attr.getForeColor().equals(jexer.bits.Color.YELLOW)) {
return MYYELLOW;
} else if (attr.getForeColor().equals(jexer.bits.Color.CYAN)) {
return MYCYAN;
} else if (attr.getForeColor().equals(jexer.bits.Color.MAGENTA)) {
return MYMAGENTA;
} else if (attr.getForeColor().equals(jexer.bits.Color.WHITE)) {
return MYWHITE;
}
}
throw new IllegalArgumentException("Invalid color: " + attr.getForeColor().getValue());
}
/**
* Convert a CellAttributes background color to an AWT Color.
*
* @param attr the text attributes
* @return the AWT Color
*/
private Color attrToBackgroundColor(final CellAttributes attr) {
/*
* TODO:
* reverse
* blink
* underline
*/
if (attr.getBackColor().equals(jexer.bits.Color.BLACK)) {
return MYBLACK;
} else if (attr.getBackColor().equals(jexer.bits.Color.RED)) {
return MYRED;
} else if (attr.getBackColor().equals(jexer.bits.Color.BLUE)) {
return MYBLUE;
} else if (attr.getBackColor().equals(jexer.bits.Color.GREEN)) {
return MYGREEN;
} else if (attr.getBackColor().equals(jexer.bits.Color.YELLOW)) {
return MYYELLOW;
} else if (attr.getBackColor().equals(jexer.bits.Color.CYAN)) {
return MYCYAN;
} else if (attr.getBackColor().equals(jexer.bits.Color.MAGENTA)) {
return MYMAGENTA;
} else if (attr.getBackColor().equals(jexer.bits.Color.WHITE)) {
return MYWHITE;
}
throw new IllegalArgumentException("Invalid color: " + attr.getBackColor().getValue());
}
/**
* Public constructor.
*
* @param screen the Screen that Backend talks to
*/
public AWTFrame() {
public AWTFrame(final AWTScreen screen) {
this.screen = screen;
setDOSColors();
setTitle("Jexer Application");
setBackground(java.awt.Color.black);
setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
setFont(new Font("Liberation Mono", Font.BOLD, 16));
// setFont(new Font("Liberation Mono", Font.BOLD, 16));
// setFont(new Font(Font.MONOSPACED, Font.PLAIN, 16));
setSize(100, 100);
try {
ClassLoader loader = Thread.currentThread().getContextClassLoader();
InputStream in = loader.getResourceAsStream(FONTFILE);
Font terminusRoot = Font.createFont(Font.TRUETYPE_FONT, in);
Font terminus = terminusRoot.deriveFont(Font.PLAIN, 22);
setFont(terminus);
} catch (Exception e) {
e.printStackTrace();
// setFont(new Font("Liberation Mono", Font.PLAIN, 24));
setFont(new Font(Font.MONOSPACED, Font.PLAIN, 24));
}
setVisible(true);
resizeToScreen();
}
/**
* Resize to font dimensions.
*/
public void resizeToScreen() {
Graphics gr = getGraphics();
FontMetrics fm = gr.getFontMetrics();
textWidth = fm.charWidth('m');
textHeight = fm.getHeight();
setSize((textWidth + 1) * screen.width + (2 * left),
(textHeight + 1) * screen.height + (2 * top));
Graphics gr = getGraphics();
FontMetrics fm = gr.getFontMetrics();
maxDescent = fm.getMaxDescent();
Rectangle2D bounds = fm.getMaxCharBounds(gr);
int leading = fm.getLeading();
textWidth = (int)Math.round(bounds.getWidth());
textHeight = (int)Math.round(bounds.getHeight()) - maxDescent;
// This also produces the same number, but works better for ugly
// monospace.
textHeight = fm.getMaxAscent() + maxDescent - leading;
System.err.printf("W: %d H: %d\n", textWidth, textHeight);
// Figure out the thickness of borders and use that to set the
// final size.
Insets insets = getInsets();
left = insets.left;
top = insets.top;
setSize(textWidth * screen.width + insets.left + insets.right,
textHeight * screen.height + insets.top + insets.bottom);
/*
System.err.printf("W: %d H: %d MD: %d L: %d\n", textWidth,
textHeight, maxDescent, leading);
*/
}
/**
@ -109,60 +287,27 @@ public final class AWTScreen extends Screen {
* @param gr the AWT Graphics context
*/
@Override
public void paint(Graphics gr) {
public void paint(final Graphics gr) {
for (int y = 0; y < screen.height; y++) {
for (int x = 0; x < screen.width; x++) {
Cell lCell = screen.logical[x][y];
Cell pCell = screen.physical[x][y];
int xPixel = x * (textWidth + 1) + left;
int yPixel = y * (textHeight + 1) + top - y;
int xPixel = x * textWidth + left;
int yPixel = y * textHeight + top;
if (!lCell.equals(pCell)) {
// Draw the background rectangle, then the foreground
// character.
if (lCell.getBackColor().equals(jexer.bits.Color.BLACK)) {
gr.setColor(Color.black);
} else if (lCell.getBackColor().equals(jexer.bits.Color.RED)) {
gr.setColor(Color.red);
} else if (lCell.getBackColor().equals(jexer.bits.Color.BLUE)) {
gr.setColor(Color.blue);
} else if (lCell.getBackColor().equals(jexer.bits.Color.GREEN)) {
gr.setColor(Color.green);
} else if (lCell.getBackColor().equals(jexer.bits.Color.YELLOW)) {
gr.setColor(Color.yellow);
} else if (lCell.getBackColor().equals(jexer.bits.Color.CYAN)) {
gr.setColor(Color.cyan);
} else if (lCell.getBackColor().equals(jexer.bits.Color.MAGENTA)) {
gr.setColor(Color.magenta);
} else if (lCell.getBackColor().equals(jexer.bits.Color.WHITE)) {
gr.setColor(Color.white);
}
gr.fillRect(xPixel, yPixel, textWidth + 1,
textHeight + 2);
if (lCell.getForeColor().equals(jexer.bits.Color.BLACK)) {
gr.setColor(Color.black);
} else if (lCell.getForeColor().equals(jexer.bits.Color.RED)) {
gr.setColor(Color.red);
} else if (lCell.getForeColor().equals(jexer.bits.Color.BLUE)) {
gr.setColor(Color.blue);
} else if (lCell.getForeColor().equals(jexer.bits.Color.GREEN)) {
gr.setColor(Color.green);
} else if (lCell.getForeColor().equals(jexer.bits.Color.YELLOW)) {
gr.setColor(Color.yellow);
} else if (lCell.getForeColor().equals(jexer.bits.Color.CYAN)) {
gr.setColor(Color.cyan);
} else if (lCell.getForeColor().equals(jexer.bits.Color.MAGENTA)) {
gr.setColor(Color.magenta);
} else if (lCell.getForeColor().equals(jexer.bits.Color.WHITE)) {
gr.setColor(Color.white);
}
gr.setColor(attrToBackgroundColor(lCell));
gr.fillRect(xPixel, yPixel, textWidth, textHeight);
gr.setColor(attrToForegroundColor(lCell));
char [] chars = new char[1];
chars[0] = lCell.getChar();
gr.drawChars(chars, 0, 1, xPixel,
yPixel + textHeight - 2);
yPixel + textHeight - maxDescent);
// Physical is always updated
physical[x][y].setTo(lCell);
@ -173,17 +318,15 @@ public final class AWTScreen extends Screen {
}
/**
* The raw AWT Frame.
* The raw AWT Frame. Note package private access.
*/
private AWTFrame frame;
AWTFrame frame;
/**
* Public constructor.
*/
public AWTScreen() {
frame = new AWTFrame();
frame.screen = this;
frame.resizeToScreen();
frame = new AWTFrame(this);
}
/**

View file

@ -30,6 +30,10 @@
*/
package jexer.io;
import java.awt.event.KeyListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseListener;
import java.awt.event.WindowListener;
import java.util.List;
import java.util.LinkedList;
@ -46,7 +50,12 @@ import static jexer.TKeypress.*;
/**
* This class reads keystrokes and mouse events from an AWT Frame.
*/
public final class AWTTerminal {
public final class AWTTerminal implements KeyListener {
/**
* The backend Screen.
*/
private AWTScreen screen;
/**
* The session information.
@ -106,15 +115,18 @@ public final class AWTTerminal {
/**
* Constructor sets up state for getEvent().
*
* @param screen the top-level AWT frame
* @param screen the top-level AWT frame
*/
public AWTTerminal(final AWTScreen screen) {
this.screen = screen;
mouse1 = false;
mouse2 = false;
mouse3 = false;
stopReaderThread = false;
sessionInfo = new TSessionInfo();
eventQueue = new LinkedList<TInputEvent>();
screen.frame.addKeyListener(this);
}
/**
@ -122,6 +134,7 @@ public final class AWTTerminal {
*/
public void shutdown() {
// System.err.println("=== shutdown() ==="); System.err.flush();
screen.frame.dispose();
}
/**
@ -160,4 +173,213 @@ public final class AWTTerminal {
}
}
/**
* Pass AWT keystrokes into the event queue.
*
* @param key keystroke received
*/
@Override
public void keyReleased(final KeyEvent key) {
// Ignore release events
}
/**
* Pass AWT keystrokes into the event queue.
*
* @param key keystroke received
*/
@Override
public void keyTyped(final KeyEvent key) {
// Ignore typed events
}
/**
* Pass AWT keystrokes into the event queue.
*
* @param key keystroke received
*/
@Override
public void keyPressed(final KeyEvent key) {
boolean alt = false;
boolean shift = false;
boolean ctrl = false;
char ch = ' ';
boolean isKey = false;
int fnKey = 0;
if (key.isActionKey()) {
isKey = true;
} else {
ch = key.getKeyChar();
}
alt = key.isAltDown();
ctrl = key.isControlDown();
shift = key.isShiftDown();
/*
System.err.printf("AWT Key: %s\n", key);
System.err.printf(" isKey: %s\n", isKey);
System.err.printf(" alt: %s\n", alt);
System.err.printf(" ctrl: %s\n", ctrl);
System.err.printf(" shift: %s\n", shift);
System.err.printf(" ch: %s\n", ch);
*/
// Special case: not return the bare modifier presses
switch (key.getKeyCode()) {
case KeyEvent.VK_ALT:
return;
case KeyEvent.VK_ALT_GRAPH:
return;
case KeyEvent.VK_CONTROL:
return;
case KeyEvent.VK_SHIFT:
return;
case KeyEvent.VK_META:
return;
default:
break;
}
TKeypress keypress = null;
if (isKey) {
switch (key.getKeyCode()) {
case KeyEvent.VK_F1:
keypress = new TKeypress(true, TKeypress.F1, ' ',
alt, ctrl, shift);
break;
case KeyEvent.VK_F2:
keypress = new TKeypress(true, TKeypress.F2, ' ',
alt, ctrl, shift);
break;
case KeyEvent.VK_F3:
keypress = new TKeypress(true, TKeypress.F3, ' ',
alt, ctrl, shift);
break;
case KeyEvent.VK_F4:
keypress = new TKeypress(true, TKeypress.F4, ' ',
alt, ctrl, shift);
break;
case KeyEvent.VK_F5:
keypress = new TKeypress(true, TKeypress.F5, ' ',
alt, ctrl, shift);
break;
case KeyEvent.VK_F6:
keypress = new TKeypress(true, TKeypress.F6, ' ',
alt, ctrl, shift);
break;
case KeyEvent.VK_F7:
keypress = new TKeypress(true, TKeypress.F7, ' ',
alt, ctrl, shift);
break;
case KeyEvent.VK_F8:
keypress = new TKeypress(true, TKeypress.F8, ' ',
alt, ctrl, shift);
break;
case KeyEvent.VK_F9:
keypress = new TKeypress(true, TKeypress.F9, ' ',
alt, ctrl, shift);
break;
case KeyEvent.VK_F10:
keypress = new TKeypress(true, TKeypress.F10, ' ',
alt, ctrl, shift);
break;
case KeyEvent.VK_F11:
keypress = new TKeypress(true, TKeypress.F11, ' ',
alt, ctrl, shift);
break;
case KeyEvent.VK_F12:
keypress = new TKeypress(true, TKeypress.F12, ' ',
alt, ctrl, shift);
break;
case KeyEvent.VK_HOME:
keypress = new TKeypress(true, TKeypress.HOME, ' ',
alt, ctrl, shift);
break;
case KeyEvent.VK_END:
keypress = new TKeypress(true, TKeypress.END, ' ',
alt, ctrl, shift);
break;
case KeyEvent.VK_PAGE_UP:
keypress = new TKeypress(true, TKeypress.PGUP, ' ',
alt, ctrl, shift);
break;
case KeyEvent.VK_PAGE_DOWN:
keypress = new TKeypress(true, TKeypress.PGDN, ' ',
alt, ctrl, shift);
break;
case KeyEvent.VK_INSERT:
keypress = new TKeypress(true, TKeypress.INS, ' ',
alt, ctrl, shift);
break;
case KeyEvent.VK_DELETE:
keypress = new TKeypress(true, TKeypress.F1, ' ',
alt, ctrl, shift);
break;
case KeyEvent.VK_RIGHT:
keypress = new TKeypress(true, TKeypress.RIGHT, ' ',
alt, ctrl, shift);
break;
case KeyEvent.VK_LEFT:
keypress = new TKeypress(true, TKeypress.LEFT, ' ',
alt, ctrl, shift);
break;
case KeyEvent.VK_UP:
keypress = new TKeypress(true, TKeypress.UP, ' ',
alt, ctrl, shift);
break;
case KeyEvent.VK_DOWN:
keypress = new TKeypress(true, TKeypress.DOWN, ' ',
alt, ctrl, shift);
break;
case KeyEvent.VK_TAB:
// Special case: distinguish TAB vs BTAB
if (shift) {
keypress = kbShiftTab;
} else {
keypress = kbTab;
}
break;
case KeyEvent.VK_ENTER:
keypress = new TKeypress(true, TKeypress.ENTER, ' ',
alt, ctrl, shift);
break;
case KeyEvent.VK_ESCAPE:
keypress = new TKeypress(true, TKeypress.ESC, ' ',
alt, ctrl, shift);
break;
case KeyEvent.VK_BACK_SPACE:
// Special case: return it as kbBackspace (Ctrl-H)
keypress = new TKeypress(false, 0, 'H', false, true, false);
break;
default:
// Unsupported, ignore
return;
}
}
if (keypress == null) {
switch (ch) {
case 0x08:
keypress = kbBackspace;
break;
case 0x0A:
keypress = kbEnter;
break;
case 0x0D:
keypress = kbEnter;
break;
default:
if (!alt && ctrl && !shift) {
ch = key.getKeyText(key.getKeyCode()).charAt(0);
}
// Not a special key, put it together
keypress = new TKeypress(false, 0, ch, alt, ctrl, shift);
}
}
// Save it and we are done.
synchronized (eventQueue) {
eventQueue.add(new TKeypressEvent(keypress));
}
}
}