applen: add namespace and remove tabs.

Signed-off-by: Andrea Odetti <mariofutire@gmail.com>
This commit is contained in:
Andrea Odetti 2021-02-25 16:25:09 +00:00
parent 56511affa2
commit 6066a52592
11 changed files with 988 additions and 938 deletions

View file

@ -3,224 +3,229 @@
#include <cfloat> #include <cfloat>
#include <memory> #include <memory>
ASCIIArt::Unicode::Unicode(const char * aC, Blocks aValues) namespace na2
: c(aC), values(aValues)
{ {
}
const int ASCIIArt::PPQ = 8 * (2 * 7) / 4; ASCIIArt::Unicode::Unicode(const char * aC, Blocks aValues)
: c(aC), values(aValues)
ASCIIArt::ASCIIArt() : myRows(0), myColumns(0)
{
myGlyphs.push_back(Unicode("\u2580", {PPQ, PPQ, 0, 0})); // top half
myGlyphs.push_back(Unicode("\u258C", {PPQ, 0, PPQ, 0})); // left half
myGlyphs.push_back(Unicode("\u2596", { 0, 0, PPQ, 0})); // lower left
myGlyphs.push_back(Unicode("\u2597", { 0, 0, 0, PPQ})); // lower right
myGlyphs.push_back(Unicode("\u2598", {PPQ, 0, 0, 0})); // top left
myGlyphs.push_back(Unicode("\u259A", {PPQ, 0, 0, PPQ})); // diagonal
myGlyphs.push_back(Unicode("\u259D", { 0, PPQ, 0, 0})); // top right
myBlocks.resize(128);
init(1, 1); // normal size
}
void ASCIIArt::init(const int rows, const int columns)
{
if (myRows != rows || myColumns != columns)
{ {
if (myColumns != columns) }
const int ASCIIArt::PPQ = 8 * (2 * 7) / 4;
ASCIIArt::ASCIIArt() : myRows(0), myColumns(0)
{
myGlyphs.push_back(Unicode("\u2580", {PPQ, PPQ, 0, 0})); // top half
myGlyphs.push_back(Unicode("\u258C", {PPQ, 0, PPQ, 0})); // left half
myGlyphs.push_back(Unicode("\u2596", { 0, 0, PPQ, 0})); // lower left
myGlyphs.push_back(Unicode("\u2597", { 0, 0, 0, PPQ})); // lower right
myGlyphs.push_back(Unicode("\u2598", {PPQ, 0, 0, 0})); // top left
myGlyphs.push_back(Unicode("\u259A", {PPQ, 0, 0, PPQ})); // diagonal
myGlyphs.push_back(Unicode("\u259D", { 0, PPQ, 0, 0})); // top right
myBlocks.resize(128);
init(1, 1); // normal size
}
void ASCIIArt::init(const int rows, const int columns)
{
if (myRows != rows || myColumns != columns)
{ {
myColumns = columns; if (myColumns != columns)
for (size_t i = 0; i < myBlocks.size(); ++i)
{ {
myBlocks[i] = decodeByte(i); myColumns = columns;
for (size_t i = 0; i < myBlocks.size(); ++i)
{
myBlocks[i] = decodeByte(i);
}
}
myRows = rows;
myValues.resize(boost::extents[myRows][myColumns]);
myChars.resize(boost::extents[rows][columns]);
}
}
void ASCIIArt::changeColumns(const int x)
{
int newColumns = myColumns + x;
newColumns = std::max(1, std::min(7, newColumns));
init(myRows, newColumns);
}
void ASCIIArt::changeRows(const int x)
{
int newRows = x > 0 ? myRows * 2 : myRows / 2;
newRows = std::max(1, std::min(4, newRows));
init(newRows, myColumns);
}
const ASCIIArt::array_char_t & ASCIIArt::getCharacters(const unsigned char * address)
{
const array_val_t & values = getQuadrantValues(address);
return getCharacters(values);
}
const ASCIIArt::array_val_t & ASCIIArt::getQuadrantValues(const unsigned char * address) const
{
std::fill(myValues.origin(), myValues.origin() + myValues.num_elements(), 0);
const int linesPerRow = 8 / myRows;
const int linesPerQuadrant = linesPerRow / 2;
// 8 lines per text character
for (size_t i = 0; i < 8; ++i)
{
const int offset = 0x0400 * i;
// group color bit is ignored
const unsigned char value = (*(address + offset)) & 0x7f;
const int row = i / linesPerRow;
const int lineInRow = i % linesPerRow;
const int quadrant = lineInRow / linesPerQuadrant;
const int base = quadrant * 2;
const std::vector<Blocks> & decoded = myBlocks[value];
for (size_t col = 0; col < myColumns; ++col)
{
Blocks & blocks = myValues[row][col];
blocks.add(base + 0, decoded[col][0] * myRows);
blocks.add(base + 1, decoded[col][1] * myRows);
} }
} }
myRows = rows; return myValues;
myValues.resize(boost::extents[myRows][myColumns]);
myChars.resize(boost::extents[rows][columns]);
}
}
void ASCIIArt::changeColumns(const int x)
{
int newColumns = myColumns + x;
newColumns = std::max(1, std::min(7, newColumns));
init(myRows, newColumns);
}
void ASCIIArt::changeRows(const int x)
{
int newRows = x > 0 ? myRows * 2 : myRows / 2;
newRows = std::max(1, std::min(4, newRows));
init(newRows, myColumns);
}
const ASCIIArt::array_char_t & ASCIIArt::getCharacters(const unsigned char * address)
{
const array_val_t & values = getQuadrantValues(address);
return getCharacters(values);
}
const ASCIIArt::array_val_t & ASCIIArt::getQuadrantValues(const unsigned char * address) const
{
std::fill(myValues.origin(), myValues.origin() + myValues.num_elements(), 0);
const int linesPerRow = 8 / myRows;
const int linesPerQuadrant = linesPerRow / 2;
// 8 lines per text character
for (size_t i = 0; i < 8; ++i)
{
const int offset = 0x0400 * i;
// group color bit is ignored
const unsigned char value = (*(address + offset)) & 0x7f;
const int row = i / linesPerRow;
const int lineInRow = i % linesPerRow;
const int quadrant = lineInRow / linesPerQuadrant;
const int base = quadrant * 2;
const std::vector<Blocks> & decoded = myBlocks[value];
for (size_t col = 0; col < myColumns; ++col)
{
Blocks & blocks = myValues[row][col];
blocks.add(base + 0, decoded[col][0] * myRows);
blocks.add(base + 1, decoded[col][1] * myRows);
}
} }
return myValues; std::vector<Blocks> ASCIIArt::decodeByte(const unsigned char value) const
}
std::vector<Blocks> ASCIIArt::decodeByte(const unsigned char value) const
{
const int each = myColumns * 4 * PPQ / (8 * 7);
int available = 7;
int col = 0;
int pos = 0; // left right
std::vector<Blocks> decoded(myColumns);
for (size_t j = 0; j < 7; ++j)
{ {
int to_allocate = each; const int each = myColumns * 4 * PPQ / (8 * 7);
do
int available = 7;
int col = 0;
int pos = 0; // left right
std::vector<Blocks> decoded(myColumns);
for (size_t j = 0; j < 7; ++j)
{ {
const int here = std::min(available, to_allocate); int to_allocate = each;
if (value & (1 << j)) do
{ {
decoded[col].add(pos, here); const int here = std::min(available, to_allocate);
} if (value & (1 << j))
to_allocate -= here; {
available -= here; decoded[col].add(pos, here);
if (available == 0) }
{ to_allocate -= here;
// new quadrant available -= here;
available = 7; if (available == 0)
++pos; {
if (pos == 2) // new quadrant
{ available = 7;
pos = 0; ++pos;
++col; if (pos == 2)
} {
pos = 0;
++col;
}
}
} }
while (to_allocate > 0);
} }
while (to_allocate > 0);
return decoded;
} }
return decoded; const ASCIIArt::array_char_t & ASCIIArt::getCharacters(const array_val_t & values)
}
const ASCIIArt::array_char_t & ASCIIArt::getCharacters(const array_val_t & values)
{
const int rows = values.shape()[0];
const int columns = values.shape()[1];
for (size_t i = 0; i < rows; ++i)
{ {
for (size_t j = 0; j < columns; ++j) const int rows = values.shape()[0];
const int columns = values.shape()[1];
for (size_t i = 0; i < rows; ++i)
{ {
myChars[i][j] = getCharacter(values[i][j]); for (size_t j = 0; j < columns; ++j)
}
}
return myChars;
}
const ASCIIArt::Character & ASCIIArt::getCharacter(const Blocks & values)
{
const int zip = values.value;
const std::unordered_map<int, Character>::const_iterator it = myAsciiPixels.find(zip);
if (it == myAsciiPixels.end())
{
Character & best = myAsciiPixels[zip];
best.error = DBL_MAX;
for (const Unicode & glyph: myGlyphs)
{
double foreground;
double background;
double error;
fit(values, glyph, foreground, background, error);
if (error < best.error)
{ {
best.error = error; myChars[i][j] = getCharacter(values[i][j]);
best.foreground = foreground;
best.background = background;
best.c = glyph.c;
} }
} }
return best; return myChars;
} }
else
const ASCIIArt::Character & ASCIIArt::getCharacter(const Blocks & values)
{ {
return it->second; const int zip = values.value;
const std::unordered_map<int, Character>::const_iterator it = myAsciiPixels.find(zip);
if (it == myAsciiPixels.end())
{
Character & best = myAsciiPixels[zip];
best.error = DBL_MAX;
for (const Unicode & glyph: myGlyphs)
{
double foreground;
double background;
double error;
fit(values, glyph, foreground, background, error);
if (error < best.error)
{
best.error = error;
best.foreground = foreground;
best.background = background;
best.c = glyph.c;
}
}
return best;
}
else
{
return it->second;
}
}
void ASCIIArt::fit(const Blocks & art, const Unicode & glyph,
double & foreground, double & background, double & error)
{
int num_fg = 0;
int num_bg = 0;
int den_fg = 0;
int den_bg = 0;
for (size_t i = 0; i < art.size(); ++i)
{
const double f = glyph.values[i];
const double b = PPQ - f;
num_fg += art[i] * f;
den_fg += f * f;
num_bg += art[i] * b;
den_bg += b * b;
}
// close formula to minimise the L2 norm of the difference
// of grey intensity
foreground = double(num_fg) / double(den_fg);
background = double(num_bg) / double(den_bg);
error = 0.0;
for (size_t i = 0; i < art.size(); ++i)
{
const double f = glyph.values[i];
const double b = PPQ - f;
const double g = foreground * f + background * b;
const double e = art[i] - g;
error += e * e;
}
} }
} }
void ASCIIArt::fit(const Blocks & art, const Unicode & glyph,
double & foreground, double & background, double & error)
{
int num_fg = 0;
int num_bg = 0;
int den_fg = 0;
int den_bg = 0;
for (size_t i = 0; i < art.size(); ++i)
{
const double f = glyph.values[i];
const double b = PPQ - f;
num_fg += art[i] * f;
den_fg += f * f;
num_bg += art[i] * b;
den_bg += b * b;
}
// close formula to minimise the L2 norm of the difference
// of grey intensity
foreground = double(num_fg) / double(den_fg);
background = double(num_bg) / double(den_bg);
error = 0.0;
for (size_t i = 0; i < art.size(); ++i)
{
const double f = glyph.values[i];
const double b = PPQ - f;
const double g = foreground * f + background * b;
const double e = art[i] - g;
error += e * e;
}
}

View file

@ -5,98 +5,102 @@
#include <initializer_list> #include <initializer_list>
#include <boost/multi_array.hpp> #include <boost/multi_array.hpp>
namespace na2
struct Blocks
{ {
Blocks() : value(0) { }
Blocks(std::initializer_list<int> q) struct Blocks
{ {
value = 0; Blocks() : value(0) { }
for (auto it = rbegin(q); it != rend(q); ++it)
Blocks(std::initializer_list<int> q)
{ {
value <<= 5; value = 0;
value += *it; for (auto it = rbegin(q); it != rend(q); ++it)
{
value <<= 5;
value += *it;
}
} }
}
void add(int i, int val) void add(int i, int val)
{ {
value += val << (i * 5); value += val << (i * 5);
} }
int operator[](int i) const int operator[](int i) const
{ {
const int val = (value >> (i * 5)) & 0b11111; const int val = (value >> (i * 5)) & 0b11111;
return val; return val;
} }
Blocks & operator=(const int val) Blocks & operator=(const int val)
{ {
value = val; value = val;
return *this; return *this;
} }
size_t size() const size_t size() const
{ {
return 4; return 4;
} }
int value; int value;
};
class ASCIIArt
{
public:
ASCIIArt();
struct Character
{
const char * c;
double foreground;
double background;
double error;
}; };
typedef boost::multi_array<Character, 2> array_char_t;
typedef boost::multi_array<Blocks, 2> array_val_t;
void init(const int rows, const int columns); class ASCIIArt
void changeColumns(const int x);
void changeRows(const int x);
const array_char_t & getCharacters(const unsigned char * address);
const array_char_t & getCharacters(const array_val_t & values);
const array_val_t & getQuadrantValues(const unsigned char * address) const;
std::vector<Blocks> decodeByte(const unsigned char value) const;
private:
static const int PPQ; // Pixels per Quadrant
std::unordered_map<int, Character> myAsciiPixels;
struct Unicode
{ {
Unicode(const char * aC, Blocks aValues); public:
ASCIIArt();
struct Character
{
const char * c;
double foreground;
double background;
double error;
};
typedef boost::multi_array<Character, 2> array_char_t;
typedef boost::multi_array<Blocks, 2> array_val_t;
void init(const int rows, const int columns);
void changeColumns(const int x);
void changeRows(const int x);
const array_char_t & getCharacters(const unsigned char * address);
const array_char_t & getCharacters(const array_val_t & values);
const array_val_t & getQuadrantValues(const unsigned char * address) const;
std::vector<Blocks> decodeByte(const unsigned char value) const;
private:
static const int PPQ; // Pixels per Quadrant
std::unordered_map<int, Character> myAsciiPixels;
struct Unicode
{
Unicode(const char * aC, Blocks aValues);
const char * c;
Blocks values; // foreground: top left - top right - bottom left - bottom right
};
int myRows;
int myColumns;
std::vector<Unicode> myGlyphs;
std::vector<std::vector<Blocks>> myBlocks;
mutable array_val_t myValues; // workspace
array_char_t myChars; // workspace
const Character & getCharacter(const Blocks & values);
static void fit(const Blocks & art, const Unicode & glyph,
double & foreground, double & background, double & error);
const char * c;
Blocks values; // foreground: top left - top right - bottom left - bottom right
}; };
int myRows; }
int myColumns;
std::vector<Unicode> myGlyphs;
std::vector<std::vector<Blocks>> myBlocks;
mutable array_val_t myValues; // workspace
array_char_t myChars; // workspace
const Character & getCharacter(const Blocks & values);
static void fit(const Blocks & art, const Unicode & glyph,
double & foreground, double & background, double & error);
};

View file

@ -6,22 +6,22 @@
namespace namespace
{ {
enum Color { enum Color {
BLACK, BLACK,
DEEP_RED, DEEP_RED,
DARK_BLUE, DARK_BLUE,
MAGENTA, MAGENTA,
DARK_GREEN, DARK_GREEN,
DARK_GRAY, DARK_GRAY,
BLUE, BLUE,
LIGHT_BLUE, LIGHT_BLUE,
BROWN, BROWN,
ORANGE, ORANGE,
LIGHT_GRAY, LIGHT_GRAY,
PINK, PINK,
GREEN, GREEN,
YELLOW, YELLOW,
AQUA, AQUA,
WHITE WHITE
}; };
// input 0..255 // input 0..255
@ -34,91 +34,96 @@ namespace
#define SETFRAMECOLOR(c, r, g, b) init_color(firstColor + Color::c, scaleRGB(r), scaleRGB(g), scaleRGB(b)); #define SETFRAMECOLOR(c, r, g, b) init_color(firstColor + Color::c, scaleRGB(r), scaleRGB(g), scaleRGB(b));
GraphicsColors::GraphicsColors(const int firstColor, const int firstPair, const int numberOfGreys) namespace na2
: myNumberOfGRColors(16), myNumberOfGreys(numberOfGreys)
{ {
has_colors();
start_color();
can_change_color();
SETFRAMECOLOR(BLACK, 0x00,0x00,0x00); // 0 GraphicsColors::GraphicsColors(const int firstColor, const int firstPair, const int numberOfGreys)
SETFRAMECOLOR(DEEP_RED, 0x9D,0x09,0x66); // 0xD0,0x00,0x30 -> Linards Tweaked 0x9D,0x09,0x66 : myNumberOfGRColors(16), myNumberOfGreys(numberOfGreys)
SETFRAMECOLOR(DARK_BLUE, 0x00,0x00,0x80); // 4 // not used
SETFRAMECOLOR(MAGENTA, 0xC7,0x34,0xFF); // FD Linards Tweaked 0xFF,0x00,0xFF -> 0xC7,0x34,0xFF
SETFRAMECOLOR(DARK_GREEN, 0x00,0x80,0x00); // 2 // not used
SETFRAMECOLOR(DARK_GRAY, 0x80,0x80,0x80); // F8
SETFRAMECOLOR(BLUE, 0x0D,0xA1,0xFF); // FC Linards Tweaked 0x00,0x00,0xFF -> 0x0D,0xA1,0xFF
SETFRAMECOLOR(LIGHT_BLUE,0xAA,0xAA,0xFF); // 0x60,0xA0,0xFF -> Linards Tweaked 0xAA,0xAA,0xFF
SETFRAMECOLOR(BROWN, 0x55,0x55,0x00); // 0x80,0x50,0x00 -> Linards Tweaked 0x55,0x55,0x00
SETFRAMECOLOR(ORANGE, 0xF2,0x5E,0x00); // 0xFF,0x80,0x00 -> Linards Tweaked 0xF2,0x5E,0x00
SETFRAMECOLOR(LIGHT_GRAY, 0xC0,0xC0,0xC0); // 7 // GR: COLOR=10
SETFRAMECOLOR(PINK, 0xFF,0x89,0xE5); // 0xFF,0x90,0x80 -> Linards Tweaked 0xFF,0x89,0xE5
SETFRAMECOLOR(GREEN, 0x38,0xCB,0x00); // FA Linards Tweaked 0x00,0xFF,0x00 -> 0x38,0xCB,0x00
SETFRAMECOLOR(YELLOW, 0xD5,0xD5,0x1A); // FB Linards Tweaked 0xFF,0xFF,0x00 -> 0xD5,0xD5,0x1A
SETFRAMECOLOR(AQUA, 0x62,0xF6,0x99); // 0x40,0xFF,0x90 -> Linards Tweaked 0x62,0xF6,0x99
SETFRAMECOLOR(WHITE, 0xFF,0xFF,0xFF); // FF
int baseColor = firstColor;
int basePair = firstPair;
myFirstGRPair = basePair;
for (size_t i = 0; i < myNumberOfGRColors; ++i)
{ {
const int fg = firstColor + i; has_colors();
for (size_t j = 0; j < myNumberOfGRColors; ++j) start_color();
{ can_change_color();
const int bg = firstColor + j;
const int pair = myFirstGRPair + i * myNumberOfGRColors + j;
init_pair(pair, fg, bg); SETFRAMECOLOR(BLACK, 0x00,0x00,0x00); // 0
SETFRAMECOLOR(DEEP_RED, 0x9D,0x09,0x66); // 0xD0,0x00,0x30 -> Linards Tweaked 0x9D,0x09,0x66
SETFRAMECOLOR(DARK_BLUE, 0x00,0x00,0x80); // 4 // not used
SETFRAMECOLOR(MAGENTA, 0xC7,0x34,0xFF); // FD Linards Tweaked 0xFF,0x00,0xFF -> 0xC7,0x34,0xFF
SETFRAMECOLOR(DARK_GREEN, 0x00,0x80,0x00); // 2 // not used
SETFRAMECOLOR(DARK_GRAY, 0x80,0x80,0x80); // F8
SETFRAMECOLOR(BLUE, 0x0D,0xA1,0xFF); // FC Linards Tweaked 0x00,0x00,0xFF -> 0x0D,0xA1,0xFF
SETFRAMECOLOR(LIGHT_BLUE,0xAA,0xAA,0xFF); // 0x60,0xA0,0xFF -> Linards Tweaked 0xAA,0xAA,0xFF
SETFRAMECOLOR(BROWN, 0x55,0x55,0x00); // 0x80,0x50,0x00 -> Linards Tweaked 0x55,0x55,0x00
SETFRAMECOLOR(ORANGE, 0xF2,0x5E,0x00); // 0xFF,0x80,0x00 -> Linards Tweaked 0xF2,0x5E,0x00
SETFRAMECOLOR(LIGHT_GRAY, 0xC0,0xC0,0xC0); // 7 // GR: COLOR=10
SETFRAMECOLOR(PINK, 0xFF,0x89,0xE5); // 0xFF,0x90,0x80 -> Linards Tweaked 0xFF,0x89,0xE5
SETFRAMECOLOR(GREEN, 0x38,0xCB,0x00); // FA Linards Tweaked 0x00,0xFF,0x00 -> 0x38,0xCB,0x00
SETFRAMECOLOR(YELLOW, 0xD5,0xD5,0x1A); // FB Linards Tweaked 0xFF,0xFF,0x00 -> 0xD5,0xD5,0x1A
SETFRAMECOLOR(AQUA, 0x62,0xF6,0x99); // 0x40,0xFF,0x90 -> Linards Tweaked 0x62,0xF6,0x99
SETFRAMECOLOR(WHITE, 0xFF,0xFF,0xFF); // FF
int baseColor = firstColor;
int basePair = firstPair;
myFirstGRPair = basePair;
for (size_t i = 0; i < myNumberOfGRColors; ++i)
{
const int fg = firstColor + i;
for (size_t j = 0; j < myNumberOfGRColors; ++j)
{
const int bg = firstColor + j;
const int pair = myFirstGRPair + i * myNumberOfGRColors + j;
init_pair(pair, fg, bg);
}
}
baseColor += myNumberOfGRColors;
basePair += myNumberOfGRColors * myNumberOfGRColors;
myFirstHGRPair = basePair;
for (size_t i = 0; i < myNumberOfGreys; ++i)
{
const int color = baseColor + i;
const int grey = 1000 * i / (myNumberOfGreys - 1);
init_color(color, grey, grey, grey);
}
for (size_t i = 0; i < myNumberOfGreys; ++i)
{
const int fg = baseColor + i;
for (size_t j = 0; j < myNumberOfGreys; ++j)
{
const int bg = baseColor + j;
const int pair = basePair + i * myNumberOfGreys + j;
init_pair(pair, fg, bg);
}
} }
} }
baseColor += myNumberOfGRColors; int GraphicsColors::getPair(int color) const
basePair += myNumberOfGRColors * myNumberOfGRColors;
myFirstHGRPair = basePair;
for (size_t i = 0; i < myNumberOfGreys; ++i)
{ {
const int color = baseColor + i; const int fg = color & 0x0f;
const int grey = 1000 * i / (myNumberOfGreys - 1); const int bg = color >> 4;
init_color(color, grey, grey, grey);
const int pair = myFirstGRPair + fg * myNumberOfGRColors + bg;
return pair;
} }
for (size_t i = 0; i < myNumberOfGreys; ++i) int GraphicsColors::getGrey(double foreground, double background) const
{ {
const int fg = baseColor + i; const int fg = std::nearbyint((myNumberOfGreys - 1) * foreground);
for (size_t j = 0; j < myNumberOfGreys; ++j) const int bg = std::nearbyint((myNumberOfGreys - 1) * background);
{
const int bg = baseColor + j;
const int pair = basePair + i * myNumberOfGreys + j;
init_pair(pair, fg, bg); const int basePair = myFirstHGRPair;
}
const int pair = basePair + fg * myNumberOfGreys + bg;
return pair;
} }
}
int GraphicsColors::getPair(int color) const
{
const int fg = color & 0x0f;
const int bg = color >> 4;
const int pair = myFirstGRPair + fg * myNumberOfGRColors + bg;
return pair;
}
int GraphicsColors::getGrey(double foreground, double background) const
{
const int fg = std::nearbyint((myNumberOfGreys - 1) * foreground);
const int bg = std::nearbyint((myNumberOfGreys - 1) * background);
const int basePair = myFirstHGRPair;
const int pair = basePair + fg * myNumberOfGreys + bg;
return pair;
} }

View file

@ -1,18 +1,23 @@
#pragma once #pragma once
class GraphicsColors namespace na2
{ {
public:
GraphicsColors(const int firstColor, const int firstPair, const int numberOfGreys);
int getPair(int color) const; class GraphicsColors
int getGrey(double foreground, double background) const; {
public:
GraphicsColors(const int firstColor, const int firstPair, const int numberOfGreys);
private: int getPair(int color) const;
int myFirstGRPair; int getGrey(double foreground, double background) const;
int myFirstHGRPair;
const int myNumberOfGRColors; private:
const int myNumberOfGreys; int myFirstGRPair;
int myFirstHGRPair;
}; const int myNumberOfGRColors;
const int myNumberOfGreys;
};
}

View file

@ -9,99 +9,104 @@
#include "Log.h" #include "Log.h"
EvDevPaddle::EvDevPaddle(const std::string & device) namespace na2
: myButtonCodes(2), myAxisCodes(2), myAxisMins(2), myAxisMaxs(2)
{ {
myFD = open(device.c_str(), O_RDONLY | O_NONBLOCK);
if (myFD > 0) EvDevPaddle::EvDevPaddle(const std::string & device)
: myButtonCodes(2), myAxisCodes(2), myAxisMins(2), myAxisMaxs(2)
{ {
libevdev * dev; myFD = open(device.c_str(), O_RDONLY | O_NONBLOCK);
int rc = libevdev_new_from_fd(myFD, &dev); if (myFD > 0)
if (rc < 0)
{ {
LogFileOutput("Input: failed to init libevdev (%s): %s\n", strerror(-rc), device.c_str()); libevdev * dev;
int rc = libevdev_new_from_fd(myFD, &dev);
if (rc < 0)
{
LogFileOutput("Input: failed to init libevdev (%s): %s\n", strerror(-rc), device.c_str());
}
else
{
myDev.reset(dev, libevdev_free);
myName = libevdev_get_name(dev);
myButtonCodes[0] = BTN_SOUTH;
myButtonCodes[1] = BTN_EAST;
myAxisCodes[0] = ABS_X;
myAxisCodes[1] = ABS_Y;
for (size_t i = 0; i < myAxisCodes.size(); ++i)
{
myAxisMins[i] = libevdev_get_abs_minimum(dev, myAxisCodes[i]);
myAxisMaxs[i] = libevdev_get_abs_maximum(dev, myAxisCodes[i]);
}
}
} }
else else
{ {
myDev.reset(dev, libevdev_free); LogFileOutput("Input: failed to open device (%s): %s\n", strerror(errno), device.c_str());
myName = libevdev_get_name(dev);
myButtonCodes[0] = BTN_SOUTH;
myButtonCodes[1] = BTN_EAST;
myAxisCodes[0] = ABS_X;
myAxisCodes[1] = ABS_Y;
for (size_t i = 0; i < myAxisCodes.size(); ++i)
{
myAxisMins[i] = libevdev_get_abs_minimum(dev, myAxisCodes[i]);
myAxisMaxs[i] = libevdev_get_abs_maximum(dev, myAxisCodes[i]);
}
} }
} }
else
{
LogFileOutput("Input: failed to open device (%s): %s\n", strerror(errno), device.c_str());
}
}
EvDevPaddle::~EvDevPaddle() EvDevPaddle::~EvDevPaddle()
{
if (myFD > 0)
{ {
close(myFD); if (myFD > 0)
{
close(myFD);
}
} }
}
int EvDevPaddle::poll() int EvDevPaddle::poll()
{
int counter = 0;
if (!myDev)
{ {
int counter = 0;
if (!myDev)
{
return counter;
}
input_event ev;
int rc = LIBEVDEV_READ_STATUS_SUCCESS;
do
{
if (rc == LIBEVDEV_READ_STATUS_SYNC)
rc = libevdev_next_event(myDev.get(), LIBEVDEV_READ_FLAG_SYNC, &ev);
else
rc = libevdev_next_event(myDev.get(), LIBEVDEV_READ_FLAG_NORMAL, &ev);
++counter;
} while (rc >= 0);
return counter; return counter;
} }
input_event ev; const std::string & EvDevPaddle::getName() const
int rc = LIBEVDEV_READ_STATUS_SUCCESS;
do
{ {
if (rc == LIBEVDEV_READ_STATUS_SYNC) return myName;
rc = libevdev_next_event(myDev.get(), LIBEVDEV_READ_FLAG_SYNC, &ev);
else
rc = libevdev_next_event(myDev.get(), LIBEVDEV_READ_FLAG_NORMAL, &ev);
++counter;
} while (rc >= 0);
return counter;
}
const std::string & EvDevPaddle::getName() const
{
return myName;
}
bool EvDevPaddle::getButton(int i) const
{
int value = 0;
if (myDev)
{
int rc = libevdev_fetch_event_value(myDev.get(), EV_KEY, myButtonCodes[i], &value);
} }
return value != 0;
}
double EvDevPaddle::getAxis(int i) const bool EvDevPaddle::getButton(int i) const
{
if (myDev)
{ {
int value = 0; int value = 0;
int rc = libevdev_fetch_event_value(myDev.get(), EV_ABS, myAxisCodes[i], &value); if (myDev)
const double axis = 2.0 * (value - myAxisMins[i]) / (myAxisMaxs[i] - myAxisMins[i]) - 1.0; {
return axis; int rc = libevdev_fetch_event_value(myDev.get(), EV_KEY, myButtonCodes[i], &value);
}
return value != 0;
} }
else
double EvDevPaddle::getAxis(int i) const
{ {
return 0; if (myDev)
{
int value = 0;
int rc = libevdev_fetch_event_value(myDev.get(), EV_ABS, myAxisCodes[i], &value);
const double axis = 2.0 * (value - myAxisMins[i]) / (myAxisMaxs[i] - myAxisMins[i]) - 1.0;
return axis;
}
else
{
return 0;
}
} }
} }

View file

@ -8,28 +8,33 @@
struct libevdev; struct libevdev;
struct input_event; struct input_event;
class EvDevPaddle : public Paddle namespace na2
{ {
public:
EvDevPaddle(const std::string & device);
~EvDevPaddle();
int poll(); class EvDevPaddle : public Paddle
{
public:
EvDevPaddle(const std::string & device);
~EvDevPaddle();
const std::string & getName() const; int poll();
bool getButton(int i) const override;
double getAxis(int i) const override;
private: const std::string & getName() const;
int myFD; bool getButton(int i) const override;
std::shared_ptr<libevdev> myDev; double getAxis(int i) const override;
void process(const input_event & ev); private:
int myFD;
std::shared_ptr<libevdev> myDev;
std::string myName; void process(const input_event & ev);
std::vector<unsigned int> myButtonCodes; std::string myName;
std::vector<unsigned int> myAxisCodes;
std::vector<int> myAxisMins; std::vector<unsigned int> myButtonCodes;
std::vector<int> myAxisMaxs; std::vector<unsigned int> myAxisCodes;
}; std::vector<int> myAxisMins;
std::vector<int> myAxisMaxs;
};
}

View file

@ -27,7 +27,7 @@
namespace namespace
{ {
bool ContinueExecution(const common2::EmulatorOptions & options, const std::shared_ptr<NFrame> & frame) bool ContinueExecution(const common2::EmulatorOptions & options, const std::shared_ptr<na2::NFrame> & frame)
{ {
const auto start = std::chrono::steady_clock::now(); const auto start = std::chrono::steady_clock::now();
@ -58,38 +58,38 @@ namespace
switch (key) switch (key)
{ {
case KEY_F(2): case KEY_F(2):
{
ResetMachineState();
break;
}
case 278: // Shift-F2
{
CtrlReset();
break;
}
case KEY_F(3):
{
return false;
}
case KEY_F(5):
{
CardManager & cardManager = GetCardMgr();
if (cardManager.QuerySlot(SLOT6) == CT_Disk2)
{ {
dynamic_cast<Disk2InterfaceCard*>(cardManager.GetObj(SLOT6))->DriveSwap(); ResetMachineState();
break;
}
case 278: // Shift-F2
{
CtrlReset();
break;
}
case KEY_F(3):
{
return false;
}
case KEY_F(5):
{
CardManager & cardManager = GetCardMgr();
if (cardManager.QuerySlot(SLOT6) == CT_Disk2)
{
dynamic_cast<Disk2InterfaceCard*>(cardManager.GetObj(SLOT6))->DriveSwap();
}
break;
} }
break;
}
case KEY_F(11): case KEY_F(11):
{ {
Snapshot_SaveState(); Snapshot_SaveState();
break; break;
} }
case KEY_F(12): case KEY_F(12):
{ {
Snapshot_LoadState(); Snapshot_LoadState();
break; break;
} }
} }
frame->ProcessEvDev(); frame->ProcessEvDev();
@ -112,7 +112,7 @@ namespace
const double coeff = exp(-0.000001 * nExecutionPeriodUsec); // 0.36 after 1 second const double coeff = exp(-0.000001 * nExecutionPeriodUsec); // 0.36 after 1 second
g_relativeSpeed = g_relativeSpeed * coeff + double(us) / double(nExecutionPeriodUsec) * (1.0 - coeff); na2::g_relativeSpeed = na2::g_relativeSpeed * coeff + double(us) / double(nExecutionPeriodUsec) * (1.0 - coeff);
if (!cardManager.GetDisk2CardMgr().IsConditionForFullSpeed()) if (!cardManager.GetDisk2CardMgr().IsConditionForFullSpeed())
{ {
@ -126,11 +126,11 @@ namespace
} }
else else
{ {
return !g_stop; return !na2::g_stop;
} }
} }
void EnterMessageLoop(const common2::EmulatorOptions & options, const std::shared_ptr<NFrame> & frame) void EnterMessageLoop(const common2::EmulatorOptions & options, const std::shared_ptr<na2::NFrame> & frame)
{ {
LogFileTimeUntilFirstKeyReadReset(); LogFileTimeUntilFirstKeyReadReset();
while (ContinueExecution(options, frame)) while (ContinueExecution(options, frame))
@ -147,10 +147,10 @@ namespace
if (!run) if (!run)
return 1; return 1;
std::shared_ptr<NFrame> frame(new NFrame(options.paddleDeviceName)); std::shared_ptr<na2::NFrame> frame(new na2::NFrame(options.paddleDeviceName));
SetFrame(frame); SetFrame(frame);
// does not seem to be a problem calling endwin() multiple times // does not seem to be a problem calling endwin() multiple times
std::atexit(NFrame::Cleanup); std::atexit(na2::NFrame::Cleanup);
if (options.log) if (options.log)
{ {
@ -162,7 +162,7 @@ namespace
g_nMemoryClearType = options.memclear; g_nMemoryClearType = options.memclear;
common2::Initialisation init; common2::Initialisation init;
SetCtrlCHandler(options.headless); na2::SetCtrlCHandler(options.headless);
applyOptions(options); applyOptions(options);
if (options.benchmark) if (options.benchmark)

View file

@ -12,357 +12,362 @@
#include <locale.h> #include <locale.h>
#include <stdlib.h> #include <stdlib.h>
struct NCurses namespace na2
{ {
NCurses()
struct NCurses
{ {
setlocale(LC_ALL, ""); NCurses()
initscr();
curs_set(0);
noecho();
cbreak();
set_escdelay(0);
// make sure this happens when ncurses is indeed initialised
colors.reset(new GraphicsColors(20, 20, 32));
}
~NCurses()
{
endwin();
colors.reset();
}
std::shared_ptr<GraphicsColors> colors;
};
NFrame::NFrame(const std::string & paddleDevice)
: myPaddleDevice(paddleDevice)
, myRows(-1)
, myColumns(-1)
{
// only initialise if actually used
// so we can run headless
}
void NFrame::Initialize()
{
LinuxFrame::Initialize();
myTextFlashCounter = 0;
myTextFlashState = 0;
myAsciiArt.reset(new ASCIIArt());
myPaddle.reset(new EvDevPaddle(myPaddleDevice));
Paddle::instance = myPaddle;
}
void NFrame::Destroy()
{
LinuxFrame::Destroy();
myTextFlashCounter = 0;
myTextFlashState = 0;
myFrame.reset();
myStatus.reset();
myAsciiArt.reset();
myPaddle.reset();
Paddle::instance.reset();
myNCurses.reset();
}
void NFrame::ProcessEvDev()
{
myPaddle->poll();
}
void NFrame::ChangeColumns(const int x)
{
myAsciiArt->changeColumns(x);
}
void NFrame::ChangeRows(const int x)
{
myAsciiArt->changeRows(x);
}
void NFrame::Init(int rows, int columns)
{
if (myRows != rows || myColumns != columns)
{
InitialiseNCurses();
if (columns < myColumns || rows < myRows)
{ {
werase(myStatus.get()); setlocale(LC_ALL, "");
wrefresh(myStatus.get()); initscr();
werase(myFrame.get());
wrefresh(myFrame.get()); curs_set(0);
noecho();
cbreak();
set_escdelay(0);
// make sure this happens when ncurses is indeed initialised
colors.reset(new GraphicsColors(20, 20, 32));
} }
~NCurses()
{
endwin();
colors.reset();
}
std::shared_ptr<GraphicsColors> colors;
};
myRows = rows; NFrame::NFrame(const std::string & paddleDevice)
myColumns = columns; : myPaddleDevice(paddleDevice)
, myRows(-1)
const int width = 1 + myColumns + 1; , myColumns(-1)
const int left = (COLS - width) / 2;
myFrame.reset(newwin(1 + myRows + 1, width, 0, left), delwin);
box(myFrame.get(), 0 , 0);
wtimeout(myFrame.get(), 0);
keypad(myFrame.get(), true);
wrefresh(myFrame.get());
myStatus.reset(newwin(4, width, 1 + myRows + 1, left), delwin);
box(myStatus.get(), 0 , 0);
wrefresh(myStatus.get());
}
}
WINDOW * NFrame::GetWindow()
{
return myFrame.get();
}
WINDOW * NFrame::GetStatus()
{
return myStatus.get();
}
void NFrame::InitialiseNCurses()
{
if (!myNCurses)
{ {
myNCurses.reset(new NCurses()); // only initialise if actually used
} // so we can run headless
}
void NFrame::VideoPresentScreen()
{
VideoUpdateFlash();
Video & video = GetVideo();
// see NTSC_SetVideoMode in NTSC.cpp
// we shoudl really use g_nTextPage and g_nHiresPage
const int displaypage2 = (video.VideoGetSWPAGE2() && !video.VideoGetSW80STORE()) ? 1 : 0;
myHiresBank1 = MemGetAuxPtr (0x2000 << displaypage2);
myHiresBank0 = MemGetMainPtr(0x2000 << displaypage2);
myTextBank1 = MemGetAuxPtr (0x400 << displaypage2);
myTextBank0 = MemGetMainPtr(0x400 << displaypage2);
typedef bool (NFrame::* VideoUpdateFuncPtr_t)(Video &, int, int, int, int, int);
VideoUpdateFuncPtr_t update = video.VideoGetSWTEXT()
? video.VideoGetSW80COL()
? &NFrame::Update80ColCell
: &NFrame::Update40ColCell
: video.VideoGetSWHIRES()
? (video.VideoGetSWDHIRES() && video.VideoGetSW80COL())
? &NFrame::UpdateDHiResCell
: &NFrame::UpdateHiResCell
: (video.VideoGetSWDHIRES() && video.VideoGetSW80COL())
? &NFrame::UpdateDLoResCell
: &NFrame::UpdateLoResCell;
int y = 0;
int ypixel = 0;
while (y < 20) {
int offset = ((y & 7) << 7) + ((y >> 3) * 40);
int x = 0;
int xpixel = 0;
while (x < 40) {
(this->*update)(video, x, y, xpixel, ypixel, offset + x);
++x;
xpixel += 14;
}
++y;
ypixel += 16;
} }
if (video.VideoGetSWMIXED()) void NFrame::Initialize()
update = video.VideoGetSW80COL() ? &NFrame::Update80ColCell
: &NFrame::Update40ColCell;
while (y < 24) {
int offset = ((y & 7) << 7) + ((y >> 3) * 40);
int x = 0;
int xpixel = 0;
while (x < 40) {
(this->*update)(video, x, y, xpixel, ypixel, offset + x);
++x;
xpixel += 14;
}
++y;
ypixel += 16;
}
wrefresh(myFrame.get());
}
void NFrame::VideoUpdateFlash()
{
++myTextFlashCounter;
if (myTextFlashCounter == 16) // Flash rate = 0.5 * 60 / 16 Hz (as we need 2 changes for a period)
{ {
LinuxFrame::Initialize();
myTextFlashCounter = 0; myTextFlashCounter = 0;
myTextFlashState = !myTextFlashState; myTextFlashState = 0;
myAsciiArt.reset(new ASCIIArt());
myPaddle.reset(new EvDevPaddle(myPaddleDevice));
Paddle::instance = myPaddle;
} }
}
chtype NFrame::MapCharacter(Video & video, BYTE ch) void NFrame::Destroy()
{
const char low = ch & 0x7f;
const char high = ch & 0x80;
chtype result = low;
const int code = low >> 5;
switch (code)
{ {
case 0: // 00 - 1F LinuxFrame::Destroy();
result += 0x40; // UPPERCASE myTextFlashCounter = 0;
break; myTextFlashState = 0;
case 1: // 20 - 3F myFrame.reset();
// SPECIAL CHARACTER myStatus.reset();
break; myAsciiArt.reset();
case 2: // 40 - 5F
// UPPERCASE myPaddle.reset();
break; Paddle::instance.reset();
case 3: // 60 - 7F
// LOWERCASE myNCurses.reset();
if (high == 0 && !video.VideoGetSWAltCharSet()) }
void NFrame::ProcessEvDev()
{
myPaddle->poll();
}
void NFrame::ChangeColumns(const int x)
{
myAsciiArt->changeColumns(x);
}
void NFrame::ChangeRows(const int x)
{
myAsciiArt->changeRows(x);
}
void NFrame::Init(int rows, int columns)
{
if (myRows != rows || myColumns != columns)
{ {
result -= 0x40; InitialiseNCurses();
} if (columns < myColumns || rows < myRows)
break;
}
if (result == 0x7f)
{
result = ACS_CKBOARD;
}
if (!high)
{
if (!video.VideoGetSWAltCharSet() && (low >= 0x40))
{
// result |= A_BLINK; // does not work on my terminal
if (myTextFlashState)
{ {
result |= A_REVERSE; werase(myStatus.get());
wrefresh(myStatus.get());
werase(myFrame.get());
wrefresh(myFrame.get());
} }
myRows = rows;
myColumns = columns;
const int width = 1 + myColumns + 1;
const int left = (COLS - width) / 2;
myFrame.reset(newwin(1 + myRows + 1, width, 0, left), delwin);
box(myFrame.get(), 0 , 0);
wtimeout(myFrame.get(), 0);
keypad(myFrame.get(), true);
wrefresh(myFrame.get());
myStatus.reset(newwin(4, width, 1 + myRows + 1, left), delwin);
box(myStatus.get(), 0 , 0);
wrefresh(myStatus.get());
}
}
WINDOW * NFrame::GetWindow()
{
return myFrame.get();
}
WINDOW * NFrame::GetStatus()
{
return myStatus.get();
}
void NFrame::InitialiseNCurses()
{
if (!myNCurses)
{
myNCurses.reset(new NCurses());
}
}
void NFrame::VideoPresentScreen()
{
VideoUpdateFlash();
Video & video = GetVideo();
// see NTSC_SetVideoMode in NTSC.cpp
// we shoudl really use g_nTextPage and g_nHiresPage
const int displaypage2 = (video.VideoGetSWPAGE2() && !video.VideoGetSW80STORE()) ? 1 : 0;
myHiresBank1 = MemGetAuxPtr (0x2000 << displaypage2);
myHiresBank0 = MemGetMainPtr(0x2000 << displaypage2);
myTextBank1 = MemGetAuxPtr (0x400 << displaypage2);
myTextBank0 = MemGetMainPtr(0x400 << displaypage2);
typedef bool (NFrame::* VideoUpdateFuncPtr_t)(Video &, int, int, int, int, int);
VideoUpdateFuncPtr_t update = video.VideoGetSWTEXT()
? video.VideoGetSW80COL()
? &NFrame::Update80ColCell
: &NFrame::Update40ColCell
: video.VideoGetSWHIRES()
? (video.VideoGetSWDHIRES() && video.VideoGetSW80COL())
? &NFrame::UpdateDHiResCell
: &NFrame::UpdateHiResCell
: (video.VideoGetSWDHIRES() && video.VideoGetSW80COL())
? &NFrame::UpdateDLoResCell
: &NFrame::UpdateLoResCell;
int y = 0;
int ypixel = 0;
while (y < 20) {
int offset = ((y & 7) << 7) + ((y >> 3) * 40);
int x = 0;
int xpixel = 0;
while (x < 40) {
(this->*update)(video, x, y, xpixel, ypixel, offset + x);
++x;
xpixel += 14;
}
++y;
ypixel += 16;
}
if (video.VideoGetSWMIXED())
update = video.VideoGetSW80COL() ? &NFrame::Update80ColCell
: &NFrame::Update40ColCell;
while (y < 24) {
int offset = ((y & 7) << 7) + ((y >> 3) * 40);
int x = 0;
int xpixel = 0;
while (x < 40) {
(this->*update)(video, x, y, xpixel, ypixel, offset + x);
++x;
xpixel += 14;
}
++y;
ypixel += 16;
}
wrefresh(myFrame.get());
}
void NFrame::VideoUpdateFlash()
{
++myTextFlashCounter;
if (myTextFlashCounter == 16) // Flash rate = 0.5 * 60 / 16 Hz (as we need 2 changes for a period)
{
myTextFlashCounter = 0;
myTextFlashState = !myTextFlashState;
}
}
chtype NFrame::MapCharacter(Video & video, BYTE ch)
{
const char low = ch & 0x7f;
const char high = ch & 0x80;
chtype result = low;
const int code = low >> 5;
switch (code)
{
case 0: // 00 - 1F
result += 0x40; // UPPERCASE
break;
case 1: // 20 - 3F
// SPECIAL CHARACTER
break;
case 2: // 40 - 5F
// UPPERCASE
break;
case 3: // 60 - 7F
// LOWERCASE
if (high == 0 && !video.VideoGetSWAltCharSet())
{
result -= 0x40;
}
break;
}
if (result == 0x7f)
{
result = ACS_CKBOARD;
}
if (!high)
{
if (!video.VideoGetSWAltCharSet() && (low >= 0x40))
{
// result |= A_BLINK; // does not work on my terminal
if (myTextFlashState)
{
result |= A_REVERSE;
}
}
else
{
result |= A_REVERSE;
}
}
return result;
}
bool NFrame::Update40ColCell(Video & video, int x, int y, int xpixel, int ypixel, int offset)
{
Init(24, 40);
myAsciiArt->init(1, 1);
BYTE ch = *(myTextBank0+offset);
const chtype ch2 = MapCharacter(video, ch);
mvwaddch(myFrame.get(), 1 + y, 1 + x, ch2);
return true;
}
bool NFrame::Update80ColCell(Video & video, int x, int y, int xpixel, int ypixel, int offset)
{
Init(24, 80);
myAsciiArt->init(1, 2);
BYTE ch1 = *(myTextBank1+offset);
BYTE ch2 = *(myTextBank0+offset);
WINDOW * win = myFrame.get();
const chtype ch12 = MapCharacter(video, ch1);
mvwaddch(win, 1 + y, 1 + 2 * x, ch12);
const chtype ch22 = MapCharacter(video, ch2);
mvwaddch(win, 1 + y, 1 + 2 * x + 1, ch22);
return true;
}
bool NFrame::UpdateLoResCell(Video &, int x, int y, int xpixel, int ypixel, int offset)
{
BYTE val = *(myTextBank0+offset);
const int pair = myNCurses->colors->getPair(val);
WINDOW * win = myFrame.get();
wcolor_set(win, pair, NULL);
if (myColumns == 40)
{
mvwaddstr(win, 1 + y, 1 + x, "\u2580");
} }
else else
{ {
result |= A_REVERSE; mvwaddstr(win, 1 + y, 1 + 2 * x, "\u2580\u2580");
} }
wcolor_set(win, 0, NULL);
return true;
} }
return result; bool NFrame::UpdateDLoResCell(Video &, int x, int y, int xpixel, int ypixel, int offset)
}
bool NFrame::Update40ColCell(Video & video, int x, int y, int xpixel, int ypixel, int offset)
{
Init(24, 40);
myAsciiArt->init(1, 1);
BYTE ch = *(myTextBank0+offset);
const chtype ch2 = MapCharacter(video, ch);
mvwaddch(myFrame.get(), 1 + y, 1 + x, ch2);
return true;
}
bool NFrame::Update80ColCell(Video & video, int x, int y, int xpixel, int ypixel, int offset)
{
Init(24, 80);
myAsciiArt->init(1, 2);
BYTE ch1 = *(myTextBank1+offset);
BYTE ch2 = *(myTextBank0+offset);
WINDOW * win = myFrame.get();
const chtype ch12 = MapCharacter(video, ch1);
mvwaddch(win, 1 + y, 1 + 2 * x, ch12);
const chtype ch22 = MapCharacter(video, ch2);
mvwaddch(win, 1 + y, 1 + 2 * x + 1, ch22);
return true;
}
bool NFrame::UpdateLoResCell(Video &, int x, int y, int xpixel, int ypixel, int offset)
{
BYTE val = *(myTextBank0+offset);
const int pair = myNCurses->colors->getPair(val);
WINDOW * win = myFrame.get();
wcolor_set(win, pair, NULL);
if (myColumns == 40)
{ {
mvwaddstr(win, 1 + y, 1 + x, "\u2580"); return true;
} }
else
bool NFrame::UpdateHiResCell(Video &, int x, int y, int xpixel, int ypixel, int offset)
{ {
mvwaddstr(win, 1 + y, 1 + 2 * x, "\u2580\u2580"); const BYTE * base = myHiresBank0 + offset;
}
wcolor_set(win, 0, NULL);
return true; const ASCIIArt::array_char_t & chs = myAsciiArt->getCharacters(base);
}
bool NFrame::UpdateDLoResCell(Video &, int x, int y, int xpixel, int ypixel, int offset) const auto shape = chs.shape();
{ const size_t rows = shape[0];
return true; const size_t cols = shape[1];
}
bool NFrame::UpdateHiResCell(Video &, int x, int y, int xpixel, int ypixel, int offset) Init(24 * rows, 40 * cols);
{ WINDOW * win = myFrame.get();
const BYTE * base = myHiresBank0 + offset;
const ASCIIArt::array_char_t & chs = myAsciiArt->getCharacters(base); const GraphicsColors & colors = *myNCurses->colors;
const auto shape = chs.shape(); for (size_t i = 0; i < rows; ++i)
const size_t rows = shape[0];
const size_t cols = shape[1];
Init(24 * rows, 40 * cols);
WINDOW * win = myFrame.get();
const GraphicsColors & colors = *myNCurses->colors;
for (size_t i = 0; i < rows; ++i)
{
for (size_t j = 0; j < cols; ++j)
{ {
const int pair = colors.getGrey(chs[i][j].foreground, chs[i][j].background); for (size_t j = 0; j < cols; ++j)
{
const int pair = colors.getGrey(chs[i][j].foreground, chs[i][j].background);
wcolor_set(win, pair, NULL); wcolor_set(win, pair, NULL);
mvwaddstr(win, 1 + rows * y + i, 1 + cols * x + j, chs[i][j].c); mvwaddstr(win, 1 + rows * y + i, 1 + cols * x + j, chs[i][j].c);
}
} }
wcolor_set(win, 0, NULL);
return true;
} }
wcolor_set(win, 0, NULL);
return true; bool NFrame::UpdateDHiResCell(Video &, int x, int y, int xpixel, int ypixel, int offset)
} {
return true;
}
bool NFrame::UpdateDHiResCell(Video &, int x, int y, int xpixel, int ypixel, int offset) void NFrame::Cleanup()
{ {
return true; GetFrame().Destroy();
} }
void NFrame::Cleanup() int NFrame::FrameMessageBox(LPCSTR lpText, LPCSTR lpCaption, UINT uType)
{ {
GetFrame().Destroy(); LogFileOutput("MessageBox:\n%s\n%s\n\n", lpCaption, lpText);
} return IDOK;
}
int NFrame::FrameMessageBox(LPCSTR lpText, LPCSTR lpCaption, UINT uType)
{
LogFileOutput("MessageBox:\n%s\n%s\n\n", lpCaption, lpText);
return IDOK;
} }

View file

@ -6,61 +6,66 @@
#include <string> #include <string>
#include <ncurses.h> #include <ncurses.h>
class ASCIIArt; namespace na2
class EvDevPaddle;
struct NCurses;
class NFrame : public common2::CommonFrame
{ {
public:
NFrame(const std::string & paddleDevice);
WINDOW * GetWindow(); class ASCIIArt;
WINDOW * GetStatus(); class EvDevPaddle;
struct NCurses;
void Initialize() override; class NFrame : public common2::CommonFrame
void Destroy() override; {
void VideoPresentScreen() override; public:
int FrameMessageBox(LPCSTR lpText, LPCSTR lpCaption, UINT uType) override; NFrame(const std::string & paddleDevice);
void ProcessEvDev(); WINDOW * GetWindow();
WINDOW * GetStatus();
void ChangeColumns(const int x); void Initialize() override;
void ChangeRows(const int x); void Destroy() override;
void VideoPresentScreen() override;
int FrameMessageBox(LPCSTR lpText, LPCSTR lpCaption, UINT uType) override;
void Init(int rows, int columns); void ProcessEvDev();
static void Cleanup(); void ChangeColumns(const int x);
void ChangeRows(const int x);
private: void Init(int rows, int columns);
const std::string myPaddleDevice; static void Cleanup();
int myRows;
int myColumns;
int myTextFlashCounter;
bool myTextFlashState;
std::shared_ptr<WINDOW> myFrame; private:
std::shared_ptr<WINDOW> myStatus;
std::shared_ptr<ASCIIArt> myAsciiArt;
std::shared_ptr<EvDevPaddle> myPaddle;
std::shared_ptr<NCurses> myNCurses;
LPBYTE myTextBank1; // Aux const std::string myPaddleDevice;
LPBYTE myTextBank0; // Main int myRows;
LPBYTE myHiresBank1; int myColumns;
LPBYTE myHiresBank0; int myTextFlashCounter;
bool myTextFlashState;
void VideoUpdateFlash(); std::shared_ptr<WINDOW> myFrame;
std::shared_ptr<WINDOW> myStatus;
std::shared_ptr<ASCIIArt> myAsciiArt;
std::shared_ptr<EvDevPaddle> myPaddle;
std::shared_ptr<NCurses> myNCurses;
chtype MapCharacter(Video & video, BYTE ch); LPBYTE myTextBank1; // Aux
LPBYTE myTextBank0; // Main
LPBYTE myHiresBank1;
LPBYTE myHiresBank0;
bool Update40ColCell(Video & video, int x, int y, int xpixel, int ypixel, int offset); void VideoUpdateFlash();
bool Update80ColCell(Video & video, int x, int y, int xpixel, int ypixel, int offset);
bool UpdateLoResCell(Video &, int x, int y, int xpixel, int ypixel, int offset);
bool UpdateDLoResCell(Video &, int x, int y, int xpixel, int ypixel, int offset);
bool UpdateHiResCell(Video &, int x, int y, int xpixel, int ypixel, int offset);
bool UpdateDHiResCell(Video &, int x, int y, int xpixel, int ypixel, int offset);
void InitialiseNCurses(); chtype MapCharacter(Video & video, BYTE ch);
};
bool Update40ColCell(Video & video, int x, int y, int xpixel, int ypixel, int offset);
bool Update80ColCell(Video & video, int x, int y, int xpixel, int ypixel, int offset);
bool UpdateLoResCell(Video &, int x, int y, int xpixel, int ypixel, int offset);
bool UpdateDLoResCell(Video &, int x, int y, int xpixel, int ypixel, int offset);
bool UpdateHiResCell(Video &, int x, int y, int xpixel, int ypixel, int offset);
bool UpdateDHiResCell(Video &, int x, int y, int xpixel, int ypixel, int offset);
void InitialiseNCurses();
};
}

View file

@ -25,100 +25,106 @@ namespace
void sig_handler_exit(int signo) void sig_handler_exit(int signo)
{ {
g_stop = true; na2::g_stop = true;
} }
} }
double g_relativeSpeed = 1.0;
bool g_stop = false;
void SetCtrlCHandler(const bool headless) namespace na2
{ {
if (headless)
{
signal(SIGINT, sig_handler_exit);
}
else
{
signal(SIGINT, sig_handler_pass);
// pass Ctrl-C to the emulator
}
}
int ProcessKeyboard(const std::shared_ptr<NFrame> & frame) double g_relativeSpeed = 1.0;
{ bool g_stop = false;
WINDOW * window = frame->GetWindow();
if (!window) void SetCtrlCHandler(const bool headless)
{ {
return ERR; if (headless)
}
const int inch = wgetch(window);
int ch = ERR;
switch (inch)
{
case ERR:
break;
case '\n':
ch = 0x0d; // ENTER
break;
case KEY_BACKSPACE:
case KEY_LEFT:
ch = 0x08;
break;
case KEY_RIGHT:
ch = 0x15;
break;
case KEY_UP:
ch = 0x0b;
break;
case KEY_DOWN:
ch = 0x0a;
break;
case 0x14a: // DEL
ch = 0x7f;
break;
case 543 ... 546: // Various values for Ctrl/Alt - Left on Ubuntu and Pi OS
frame->ChangeColumns(-1);
break;
case 558 ... 561: // Ctrl/Alt - Right
frame->ChangeColumns(+1);
break;
case 564 ... 567: // Ctrl/Alt - Up
frame->ChangeRows(-1);
break;
case 523 ... 526: // Ctrl/Alt - Down
frame->ChangeRows(+1);
break;
default:
if (inch < 0x80)
{ {
ch = inch; signal(SIGINT, sig_handler_exit);
// Standard for Apple II is Upper case }
if (ch >= 'A' && ch <= 'Z') else
{ {
ch += 'a' - 'A'; signal(SIGINT, sig_handler_pass);
} // pass Ctrl-C to the emulator
else if (ch >= 'a' && ch <= 'z')
{
ch -= 'a' - 'A';
}
} }
} }
if (ch != ERR) int ProcessKeyboard(const std::shared_ptr<NFrame> & frame)
{ {
addKeyToBuffer(ch); WINDOW * window = frame->GetWindow();
return ERR; if (!window)
} {
else return ERR;
{ }
// pass it back
return inch; const int inch = wgetch(window);
int ch = ERR;
switch (inch)
{
case ERR:
break;
case '\n':
ch = 0x0d; // ENTER
break;
case KEY_BACKSPACE:
case KEY_LEFT:
ch = 0x08;
break;
case KEY_RIGHT:
ch = 0x15;
break;
case KEY_UP:
ch = 0x0b;
break;
case KEY_DOWN:
ch = 0x0a;
break;
case 0x14a: // DEL
ch = 0x7f;
break;
case 543 ... 546: // Various values for Ctrl/Alt - Left on Ubuntu and Pi OS
frame->ChangeColumns(-1);
break;
case 558 ... 561: // Ctrl/Alt - Right
frame->ChangeColumns(+1);
break;
case 564 ... 567: // Ctrl/Alt - Up
frame->ChangeRows(-1);
break;
case 523 ... 526: // Ctrl/Alt - Down
frame->ChangeRows(+1);
break;
default:
if (inch < 0x80)
{
ch = inch;
// Standard for Apple II is Upper case
if (ch >= 'A' && ch <= 'Z')
{
ch += 'a' - 'A';
}
else if (ch >= 'a' && ch <= 'z')
{
ch -= 'a' - 'A';
}
}
}
if (ch != ERR)
{
addKeyToBuffer(ch);
return ERR;
}
else
{
// pass it back
return inch;
}
} }
} }
// Mockingboard // Mockingboard

View file

@ -2,11 +2,16 @@
#include <memory> #include <memory>
class NFrame; namespace na2
{
int ProcessKeyboard(const std::shared_ptr<NFrame> & frame); class NFrame;
void SetCtrlCHandler(const bool headless);
extern double g_relativeSpeed; int ProcessKeyboard(const std::shared_ptr<NFrame> & frame);
void SetCtrlCHandler(const bool headless);
extern bool g_stop; extern double g_relativeSpeed;
extern bool g_stop;
}