AppleWin/source/frontends/ncurses/nframe.cpp

369 lines
7.5 KiB
C++
Raw Normal View History

#include "StdAfx.h"
#include "frontends/ncurses/nframe.h"
#include "frontends/ncurses/colors.h"
#include "frontends/ncurses/asciiart.h"
#include "frontends/ncurses/evdevpaddle.h"
#include "Interface.h"
#include "Memory.h"
#include "Log.h"
#include <signal.h>
#include <locale.h>
#include <stdlib.h>
struct NCurses
{
NCurses()
{
setlocale(LC_ALL, "");
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());
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
{
mvwaddstr(win, 1 + y, 1 + 2 * x, "\u2580\u2580");
}
wcolor_set(win, 0, NULL);
return true;
}
bool NFrame::UpdateDLoResCell(Video &, int x, int y, int xpixel, int ypixel, int offset)
{
return true;
}
bool NFrame::UpdateHiResCell(Video &, int x, int y, int xpixel, int ypixel, int offset)
{
const BYTE * base = myHiresBank0 + offset;
const ASCIIArt::array_char_t & chs = myAsciiArt->getCharacters(base);
const auto shape = chs.shape();
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);
wcolor_set(win, pair, NULL);
mvwaddstr(win, 1 + rows * y + i, 1 + cols * x + j, chs[i][j].c);
}
}
wcolor_set(win, 0, NULL);
return true;
}
bool NFrame::UpdateDHiResCell(Video &, int x, int y, int xpixel, int ypixel, int offset)
{
return true;
}
void NFrame::Cleanup()
{
GetFrame().Destroy();
}
int NFrame::FrameMessageBox(LPCSTR lpText, LPCSTR lpCaption, UINT uType)
{
LogFileOutput("MessageBox:\n%s\n%s\n\n", lpCaption, lpText);
return IDOK;
}