applen: add namespace and remove tabs.
Signed-off-by: Andrea Odetti <mariofutire@gmail.com>
This commit is contained in:
parent
56511affa2
commit
6066a52592
11 changed files with 988 additions and 938 deletions
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -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);
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue