visualboyadvance-m/src/wx/sys.cpp

1330 lines
34 KiB
C++

#include "../common/ConfigManager.h"
#include "../common/SoundSDL.h"
#include "wxvbam.h"
#include "SDL.h"
#include <wx/ffile.h>
#include <wx/generic/prntdlgg.h>
#include <wx/print.h>
#include <wx/printdlg.h>
// These should probably be in vbamcore
int systemVerbose;
int systemFrameSkip;
int systemRedShift;
int systemGreenShift;
int systemBlueShift;
int systemColorDepth;
uint16_t systemColorMap16[0x10000];
uint32_t systemColorMap32[0x10000];
#define gs555(x) (x | (x << 5) | (x << 10))
uint16_t systemGbPalette[24] = {
gs555(0x1f), gs555(0x15), gs555(0x0c), 0,
gs555(0x1f), gs555(0x15), gs555(0x0c), 0,
gs555(0x1f), gs555(0x15), gs555(0x0c), 0,
gs555(0x1f), gs555(0x15), gs555(0x0c), 0,
gs555(0x1f), gs555(0x15), gs555(0x0c), 0,
gs555(0x1f), gs555(0x15), gs555(0x0c), 0
};
int RGB_LOW_BITS_MASK;
// these are local, though.
int joypress[4], autofire;
static int sensorx[4], sensory[4], sensorz[4];
bool pause_next;
bool turbo;
// and this is from MFC interface
bool soundBufferLow;
void systemMessage(int id, const char* fmt, ...)
{
(void)id; // unused params
static char* buf = NULL;
static int buflen = 80;
va_list args;
// auto-conversion of wxCharBuffer to const char * seems broken
// so save underlying wxCharBuffer (or create one of none is used)
wxCharBuffer _fmt(wxString(wxGetTranslation(wxString(fmt, wxConvLibc))).utf8_str());
if (!buf) {
buf = (char*)malloc(buflen);
if (!buf)
exit(1);
}
while (1) {
va_start(args, fmt);
int needsz = vsnprintf(buf, buflen, _fmt.data(), args);
va_end(args);
if (needsz < buflen)
break;
while (buflen <= needsz)
buflen *= 2;
free(buf);
buf = (char*)malloc(buflen);
if (!buf)
exit(1);
}
wxLogError(wxT("%s"), wxString(buf, wxConvLibc).c_str());
}
static int frames = 0;
void systemDrawScreen()
{
frames++;
MainFrame* mf = wxGetApp().frame;
mf->UpdateViewers();
// FIXME: Sm60FPS crap and sondBufferLow crap
GameArea* ga = mf->GetPanel();
#ifndef NO_FFMPEG
if (ga)
ga->AddFrame(pix);
#endif
if (ga && ga->panel)
ga->panel->DrawArea(&pix);
}
// record a game "movie"
// actually just game save state combined with a keystroke log
// doesn't work in GB "multiplayer" mode (only records default joypad)
//
// <name>.vmv = keystroke log; all values little-endian ints:
// <version>.32 = 1
// for every joypad change (init to 0) and once at end of movie {
// <timestamp>.32 = frames since start of movie
// <joypad>.32 = default joypad reading at that time
// }
// <name>.vm0 = saved state
wxFFile game_file;
bool game_recording, game_playback;
uint32_t game_frame;
uint32_t game_joypad;
void systemStartGameRecording(const wxString& fname)
{
GameArea* panel = wxGetApp().frame->GetPanel();
if (!panel || panel->game_type() == IMAGE_UNKNOWN || !panel->emusys->emuWriteState) {
wxLogError(_("No game in progress to record"));
return;
}
systemStopGamePlayback();
wxString fn = fname;
if (fn.size() < 4 || !wxString(fn.substr(fn.size() - 4)).IsSameAs(wxT(".vmv"), false))
fn.append(wxT(".vmv"));
uint32_t version = 1;
if (!game_file.Open(fn, wxT("wb")) || game_file.Write(&version, sizeof(version)) != sizeof(version)) {
wxLogError(_("Cannot open output file %s"), fname.c_str());
return;
}
fn[fn.size() - 1] = wxT('0');
if (!panel->emusys->emuWriteState(fn.mb_fn_str())) {
wxLogError(_("Error writing game recording"));
game_file.Close();
return;
}
game_frame = 0;
game_joypad = 0;
game_recording = true;
MainFrame* mf = wxGetApp().frame;
mf->cmd_enable &= ~(CMDEN_NGREC | CMDEN_GPLAY | CMDEN_NGPLAY);
mf->cmd_enable |= CMDEN_GREC;
mf->enable_menus();
}
void systemStopGameRecording()
{
if (!game_recording)
return;
if (game_file.Write(&game_frame, sizeof(game_frame)) != sizeof(game_frame) || game_file.Write(&game_joypad, sizeof(game_joypad)) != sizeof(game_joypad) || !game_file.Close())
wxLogError(_("Error writing game recording"));
game_recording = false;
MainFrame* mf = wxGetApp().frame;
mf->cmd_enable &= ~CMDEN_GREC;
mf->cmd_enable |= CMDEN_NGREC | CMDEN_NGPLAY;
mf->enable_menus();
}
uint32_t game_next_frame, game_next_joypad;
void systemStartGamePlayback(const wxString& fname)
{
GameArea* panel = wxGetApp().frame->GetPanel();
if (!panel || panel->game_type() == IMAGE_UNKNOWN || !panel->emusys->emuReadState) {
wxLogError(_("No game in progress to record"));
return;
}
if (game_recording) {
wxLogError(_("Cannot play game recording while recording"));
return;
}
systemStopGamePlayback();
wxString fn = fname;
if (fn.size() < 4 || !wxString(fn.substr(fn.size() - 4)).IsSameAs(wxT(".vmv"), false))
fn.append(wxT(".vmv"));
uint32_t version;
if (!game_file.Open(fn, wxT("rb")) || game_file.Read(&version, sizeof(version)) != sizeof(version) || wxUINT32_SWAP_ON_BE(version) != 1) {
wxLogError(_("Cannot open recording file %s"), fname.c_str());
return;
}
uint32_t gf, jp;
if (game_file.Read(&gf, sizeof(gf)) != sizeof(gf) || game_file.Read(&jp, sizeof(jp)) != sizeof(jp)) {
wxLogError(_("Error reading game recording"));
game_file.Close();
return;
}
game_next_frame = wxUINT32_SWAP_ON_BE(gf);
game_next_joypad = wxUINT32_SWAP_ON_BE(jp);
fn[fn.size() - 1] = wxT('0');
if (!panel->emusys->emuReadState(fn.mb_fn_str())) {
wxLogError(_("Error reading game recording"));
game_file.Close();
return;
}
game_frame = 0;
game_joypad = 0;
game_playback = true;
MainFrame* mf = wxGetApp().frame;
mf->cmd_enable &= ~(CMDEN_NGREC | CMDEN_GREC | CMDEN_NGPLAY);
mf->cmd_enable |= CMDEN_GPLAY;
mf->enable_menus();
}
void systemStopGamePlayback()
{
if (!game_playback)
return;
game_file.Close();
game_playback = false;
MainFrame* mf = wxGetApp().frame;
mf->cmd_enable &= ~CMDEN_GPLAY;
mf->cmd_enable |= CMDEN_NGREC | CMDEN_NGPLAY;
mf->enable_menus();
}
// updates the joystick data (done in background using wxSDLJoy)
bool systemReadJoypads()
{
return true;
}
// return information about the given joystick, -1 for default joystick
uint32_t systemReadJoypad(int joy)
{
if (joy < 0 || joy > 3)
joy = gopts.default_stick - 1;
uint32_t ret = joypress[joy];
if (turbo)
ret |= KEYM_SPEED;
uint32_t af = autofire;
if (ret & KEYM_AUTO_A) {
ret |= KEYM_A;
af |= KEYM_A;
}
if (ret & KEYM_AUTO_B) {
ret |= KEYM_B;
af |= KEYM_B;
}
static int autofire_trigger = 1;
static bool autofire_state = true;
uint32_t af_but = af & ret;
if (af_but) {
if (!autofire_state)
ret &= ~af_but;
if (!--autofire_trigger) {
autofire_trigger = gopts.autofire_rate;
autofire_state = !autofire_state;
}
} else {
autofire_state = true;
autofire_trigger = gopts.autofire_rate;
}
// disallow opposite directionals simultaneously
ret &= ~((ret & (KEYM_LEFT | KEYM_DOWN | KEYM_MOTION_DOWN | KEYM_MOTION_RIGHT)) >> 1);
ret &= REALKEY_MASK;
if (game_recording) {
uint32_t rret = ret & ~(KEYM_SPEED | KEYM_CAPTURE);
if (rret != game_joypad) {
game_joypad = rret;
uint32_t gf = wxUINT32_SWAP_ON_BE(game_frame);
uint32_t jp = wxUINT32_SWAP_ON_BE(game_joypad);
if (game_file.Write(&gf, sizeof(gf)) != sizeof(gf) || game_file.Write(&jp, sizeof(jp)) != sizeof(jp)) {
game_file.Close();
game_recording = false;
wxLogError(_("Error writing game recording"));
}
}
} else if (game_playback) {
while (game_frame >= game_next_frame) {
game_joypad = game_next_joypad;
uint32_t gf, jp;
if (game_file.Read(&gf, sizeof(gf)) != sizeof(gf) || game_file.Read(&jp, sizeof(jp)) != sizeof(jp)) {
game_file.Close();
game_playback = false;
wxString msg(_("Playback ended"));
systemScreenMessage(msg);
break;
}
game_next_frame = wxUINT32_SWAP_ON_BE(gf);
game_next_joypad = wxUINT32_SWAP_ON_BE(jp);
}
ret = game_joypad;
}
return ret;
}
void systemShowSpeed(int speed)
{
MainFrame* f = wxGetApp().frame;
wxString s;
s.Printf(_("%d%%(%d, %d fps)"), speed, systemFrameSkip, frames * speed / 100);
switch (showSpeed) {
case SS_NONE:
f->GetPanel()->osdstat.clear();
break;
case SS_PERCENT:
f->GetPanel()->osdstat.Printf(_("%d%%"), speed);
break;
case SS_DETAILED:
f->GetPanel()->osdstat = s;
break;
}
wxGetApp().frame->SetStatusText(s, 1);
frames = 0;
}
int systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED;
void system10Frames(int rate)
{
GameArea* panel = wxGetApp().frame->GetPanel();
int fs = frameSkip;
if (fs < 0) {
// I don't know why this algorithm isn't in common somewhere
// as is, I copied it from SDL
static uint32_t prevclock = 0;
static int speedadj = 0;
uint32_t t = systemGetClock();
if (!panel->was_paused && prevclock && (t - prevclock) != (uint32_t)(10000 / rate)) {
int speed = t == prevclock ? 100 * 10000 / rate - (t - prevclock) : 100;
// why 98??
if (speed >= 98)
speedadj++;
else if (speed < 80)
speedadj -= (90 - speed) / 5;
else
speedadj--;
if (speedadj >= 3) {
speedadj = 0;
if (systemFrameSkip > 0)
systemFrameSkip--;
} else if (speedadj <= -2) {
speedadj += 2;
if (systemFrameSkip < 9)
systemFrameSkip++;
}
}
prevclock = t;
panel->was_paused = false;
}
if (gopts.rewind_interval) {
if (!panel->rewind_time)
panel->rewind_time = gopts.rewind_interval * 6;
else if (!--panel->rewind_time)
panel->do_rewind = true;
}
if (--systemSaveUpdateCounter == SYSTEM_SAVE_NOT_UPDATED)
panel->SaveBattery();
else if (systemSaveUpdateCounter < SYSTEM_SAVE_NOT_UPDATED)
systemSaveUpdateCounter = SYSTEM_SAVE_NOT_UPDATED;
}
void systemFrame()
{
if (game_recording || game_playback)
game_frame++;
}
// technically, num is ignored in favor of finding the first
// available slot
void systemScreenCapture(int num)
{
GameArea* panel = wxGetApp().frame->GetPanel();
wxFileName fn = wxFileName(wxGetApp().frame->GetGamePath(gopts.scrshot_dir), wxEmptyString);
do {
wxString bfn;
bfn.Printf(wxT("%s%02d"), panel->game_name().c_str(),
num++);
if (captureFormat == 0)
bfn.append(wxT(".png"));
else // if(gopts.cap_format == 1)
bfn.append(wxT(".bmp"));
fn.SetFullName(bfn);
} while (fn.FileExists());
fn.Mkdir(0777, wxPATH_MKDIR_FULL);
if (captureFormat == 0)
panel->emusys->emuWritePNG(fn.GetFullPath().mb_fn_str());
else // if(gopts.cap_format == 1)
panel->emusys->emuWriteBMP(fn.GetFullPath().mb_fn_str());
wxString msg;
msg.Printf(_("Wrote snapshot %s"), fn.GetFullPath().c_str());
systemScreenMessage(msg);
}
void systemSaveOldest()
{
// I need to be implemented
}
void systemLoadRecent()
{
// I need to be implemented
}
uint32_t systemGetClock()
{
return wxGetApp().timer.Time();
}
void systemCartridgeRumble(bool) {}
static uint8_t sensorDarkness = 0xE8; // total darkness (including daylight on rainy days)
uint8_t systemGetSensorDarkness()
{
return sensorDarkness;
}
void systemUpdateSolarSensor()
{
uint8_t sun = 0x0; //sun = 0xE8 - 0xE8 (case 0 and default)
int level = sunBars / 10;
switch (level) {
case 1:
sun = 0xE8 - 0xE0;
break;
case 2:
sun = 0xE8 - 0xDA;
break;
case 3:
sun = 0xE8 - 0xD0;
break;
case 4:
sun = 0xE8 - 0xC8;
break;
case 5:
sun = 0xE8 - 0xC0;
break;
case 6:
sun = 0xE8 - 0xB0;
break;
case 7:
sun = 0xE8 - 0xA0;
break;
case 8:
sun = 0xE8 - 0x88;
break;
case 9:
sun = 0xE8 - 0x70;
break;
case 10:
sun = 0xE8 - 0x50;
break;
default:
break;
}
sensorDarkness = 0xE8 - sun;
}
void systemUpdateMotionSensor()
{
for (int i = 0; i < 4; i++) {
if (!sensorx[i])
sensorx[i] = 2047;
if (!sensory[i])
sensory[i] = 2047;
if (joypress[i] & KEYM_MOTION_LEFT) {
sunBars--;
if (sunBars < 1)
sunBars = 1;
sensorx[i] += 3;
if (sensorx[i] > 2197)
sensorx[i] = 2197;
if (sensorx[i] < 2047)
sensorx[i] = 2057;
} else if (joypress[i] & KEYM_MOTION_RIGHT) {
sunBars++;
if (sunBars > 100)
sunBars = 100;
sensorx[i] -= 3;
if (sensorx[i] < 1897)
sensorx[i] = 1897;
if (sensorx[i] > 2047)
sensorx[i] = 2037;
} else if (sensorx[i] > 2047) {
sensorx[i] -= 2;
if (sensorx[i] < 2047)
sensorx[i] = 2047;
} else {
sensorx[i] += 2;
if (sensorx[i] > 2047)
sensorx[i] = 2047;
}
if (joypress[i] & KEYM_MOTION_UP) {
sensory[i] += 3;
if (sensory[i] > 2197)
sensory[i] = 2197;
if (sensory[i] < 2047)
sensory[i] = 2057;
} else if (joypress[i] & KEYM_MOTION_DOWN) {
sensory[i] -= 3;
if (sensory[i] < 1897)
sensory[i] = 1897;
if (sensory[i] > 2047)
sensory[i] = 2037;
} else if (sensory[i] > 2047) {
sensory[i] -= 2;
if (sensory[i] < 2047)
sensory[i] = 2047;
} else {
sensory[i] += 2;
if (sensory[i] > 2047)
sensory[i] = 2047;
}
const int lowZ = -1800;
const int centerZ = 0;
const int highZ = 1800;
const int accelZ = 3;
if (joypress[i] & KEYM_MOTION_IN) {
sensorz[i] += accelZ;
if (sensorz[i] > highZ)
sensorz[i] = highZ;
if (sensorz[i] < centerZ)
sensorz[i] = centerZ + (accelZ * 300);
} else if (joypress[i] & KEYM_MOTION_OUT) {
sensorz[i] -= accelZ;
if (sensorz[i] < lowZ)
sensorz[i] = lowZ;
if (sensorz[i] > centerZ)
sensorz[i] = centerZ - (accelZ * 300);
} else if (sensorz[i] > centerZ) {
sensorz[i] -= (accelZ * 100);
if (sensorz[i] < centerZ)
sensorz[i] = centerZ;
} else {
sensorz[i] += (accelZ * 100);
if (sensorz[i] > centerZ)
sensorz[i] = centerZ;
}
}
systemUpdateSolarSensor();
}
int systemGetSensorX()
{
return sensorx[gopts.default_stick - 1];
}
int systemGetSensorY()
{
return sensory[gopts.default_stick - 1];
}
int systemGetSensorZ()
{
return sensorz[gopts.default_stick - 1] / 10;
}
class PrintDialog : public wxEvtHandler, public wxPrintout {
public:
PrintDialog(const uint16_t* data, int lines, bool cont);
~PrintDialog();
int ShowModal()
{
dlg->SetWindowStyle(wxCAPTION | wxRESIZE_BORDER);
if (gopts.keep_on_top)
dlg->SetWindowStyle(dlg->GetWindowStyle() | wxSTAY_ON_TOP);
else
dlg->SetWindowStyle(dlg->GetWindowStyle() & ~wxSTAY_ON_TOP);
CheckPointer(wxGetApp().frame);
return wxGetApp().frame->ShowModal(dlg);
}
private:
void DoSave(wxCommandEvent&);
void DoPrint(wxCommandEvent&);
void ChangeMag(wxCommandEvent&);
void ShowImg(wxPaintEvent&);
bool OnPrintPage(int pno);
void OnPreparePrinting();
bool HasPage(int pno) { return pno <= npw * nph; }
void GetPageInfo(int* minp, int* maxp, int* pfrom, int* pto)
{
*minp = 1;
*maxp = npw * nph;
*pfrom = 1;
*pto = 1;
}
wxDialog* dlg;
wxPanel* p;
wxImage img;
wxBitmap* bmp;
wxControlWithItems* mag;
static wxPrintData* printdata;
static wxPageSetupDialogData* pagedata;
wxRect margins;
int npw, nph;
DECLARE_CLASS(PrintDialog)
};
IMPLEMENT_CLASS(PrintDialog, wxEvtHandler)
PrintDialog::PrintDialog(const uint16_t* data, int lines, bool cont):
wxPrintout(wxGetApp().frame->GetPanel()->game_name() + wxT(" Printout")),
img(160, lines),
npw(1),
nph(1)
{
dlg = wxStaticCast(wxGetApp().frame->FindWindow(XRCID("GBPrinter")), wxDialog);
p = XRCCTRL(*dlg, "Preview", wxPanel);
wxScrolledWindow* pp = wxStaticCast(p->GetParent(), wxScrolledWindow);
wxSize sz(320, lines * 2);
p->SetSize(sz);
pp->SetVirtualSize(sz);
if (lines > 144)
sz.SetHeight(144 * 2);
pp->SetSizeHints(sz, sz); // keep sizer from messing with size
pp->SetSize(sz);
pp->SetScrollRate(1, 1);
// why is this so difficult?
// I want the display area to be 320x288, not the area w/ scrollbars
wxSize csz = pp->GetClientSize();
sz += sz - csz;
pp->SetSizeHints(sz, sz); // keep sizer from messing with size
pp->SetSize(sz);
dlg->Fit();
// what a waste. wxImage is brain-dead rgb24
data += 162; // top border
for (int y = 0; y < lines; y++) {
for (int x = 0; x < 160; x++) {
uint16_t d = *data++;
img.SetRGB(x, y, ((d >> 10) & 0x1f) << 3, ((d >> 5) & 0x1f) << 3,
(d & 0x1f) << 3);
}
data += 2; // rhs border
}
bmp = new wxBitmap(img.Scale(320, 2 * lines, wxIMAGE_QUALITY_HIGH));
p->Connect(wxEVT_PAINT, wxPaintEventHandler(PrintDialog::ShowImg),
NULL, this);
dlg->Connect(wxID_SAVE, wxEVT_COMMAND_BUTTON_CLICKED,
wxCommandEventHandler(PrintDialog::DoSave), NULL, this);
dlg->Connect(wxID_PRINT, wxEVT_COMMAND_BUTTON_CLICKED,
wxCommandEventHandler(PrintDialog::DoPrint), NULL, this);
dlg->Connect(XRCID("Magnification"), wxEVT_COMMAND_CHOICE_SELECTED,
wxCommandEventHandler(PrintDialog::ChangeMag), NULL, this);
mag = XRCCTRL(*dlg, "Magnification", wxControlWithItems);
mag->SetSelection(1);
wxWindow* w = dlg->FindWindow(cont ? wxID_OK : wxID_SAVE);
if (w)
w->SetFocus();
wxButton* cb = wxStaticCast(dlg->FindWindow(wxID_CANCEL), wxButton);
if (cb)
cb->SetLabel(_("&Discard"));
}
PrintDialog::~PrintDialog()
{
p->Disconnect(wxID_ANY, wxEVT_PAINT);
dlg->Disconnect(wxID_PRINT);
dlg->Disconnect(wxID_SAVE);
dlg->Disconnect(XRCID("Magnification"));
delete bmp;
}
void PrintDialog::ShowImg(wxPaintEvent& evt)
{
wxPaintDC dc(wxStaticCast(evt.GetEventObject(), wxWindow));
dc.DrawBitmap(*bmp, 0, 0);
}
void PrintDialog::ChangeMag(wxCommandEvent& evt)
{
(void)evt; // unused params
int m = mag->GetSelection() + 1;
wxScrolledWindow* pp = wxStaticCast(p->GetParent(), wxScrolledWindow);
wxSize sz(m * 160, m * img.GetHeight());
delete bmp;
bmp = new wxBitmap(img.Scale(sz.GetWidth(), sz.GetHeight(), wxIMAGE_QUALITY_HIGH));
p->SetSize(sz);
pp->SetVirtualSize(sz);
// pp->Refresh();
}
static wxString prsav_path;
void PrintDialog::DoSave(wxCommandEvent&)
{
wxString pats = _("Image files (*.bmp;*.jpg;*.png)|*.bmp;*.jpg;*.png|");
pats.append(wxALL_FILES);
wxString dn = wxGetApp().frame->GetPanel()->game_name();
if (captureFormat == 0)
dn.append(wxT(".png"));
else // if(gopts.cap_format == 1)
dn.append(wxT(".bmp"));
wxFileDialog fdlg(dlg, _("Save printer image to"), prsav_path, dn,
pats, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
int ret = fdlg.ShowModal();
prsav_path = fdlg.GetDirectory();
if (ret != wxID_OK)
return;
wxString of = fdlg.GetPath();
int m = mag->GetSelection() + 1;
wxImage scimg = img.Scale(m * 160, m * img.GetHeight(), wxIMAGE_QUALITY_HIGH);
if (scimg.SaveFile(of)) {
wxString msg;
msg.Printf(_("Wrote printer output to %s"), of.c_str());
systemScreenMessage(msg);
wxButton* cb = wxStaticCast(dlg->FindWindow(wxID_CANCEL), wxButton);
if (cb) {
cb->SetLabel(_("&Close"));
cb->SetFocus();
}
}
}
wxPrintData* PrintDialog::printdata = NULL;
wxPageSetupDialogData* PrintDialog::pagedata = NULL;
void PrintDialog::OnPreparePrinting()
{
margins = GetLogicalPageMarginsRect(*pagedata);
// strange... y is always negative
margins.y = -margins.y;
int w = bmp->GetWidth();
int h = bmp->GetHeight();
npw = (w + margins.width - 1) / margins.width;
nph = (h + margins.height - 1) / margins.height;
}
bool PrintDialog::OnPrintPage(int pno)
{
wxDC* dc = GetDC();
dc->SetClippingRegion(margins);
int xoff = (pno - 1) % npw;
int yoff = (pno - 1) / npw;
xoff *= margins.width;
yoff *= margins.height;
dc->DrawBitmap(*bmp, margins.x - xoff, margins.y - yoff);
return true;
}
void PrintDialog::DoPrint(wxCommandEvent&)
{
if (!printdata)
printdata = new wxPrintData;
if (!pagedata)
pagedata = new wxPageSetupDialogData(*printdata);
// wxGTK-2.8.12/gnome-2.18.8: can't set margins in page setup
// wxMAC: need to pop up 2 dialogs to get margins
// why even bother using standard dialogs, then?
// well, maybe under msw, where generic dialog may not even exist...
#ifndef __WXMSW__
wxGenericPageSetupDialog psdlg2(dlg, pagedata);
psdlg2.ShowModal();
*printdata = psdlg2.GetPageSetupDialogData().GetPrintData();
*pagedata = psdlg2.GetPageSetupDialogData();
#else
wxPageSetupDialog psdlg(dlg, pagedata);
psdlg.ShowModal();
*printdata = psdlg.GetPageSetupDialogData().GetPrintData();
*pagedata = psdlg.GetPageSetupDialogData();
#ifdef __WXMAC__
// FIXME: is this necessary? useful? functional?
wxMacPageMarginsDialog pmdlg(dlg, pagedata);
pmdlg.ShowModal();
*printdata = pmdlg.GetPageSetupDialogData().GetPrintData();
*pagedata = pmdlg.GetPageSetupDialogData();
#endif
#endif
wxPrintDialogData prdlg(*printdata);
wxPrinter printer(&prdlg);
if (printer.Print(dlg, this)) {
wxString msg = _("Printed");
systemScreenMessage(msg);
wxButton* cb = wxStaticCast(dlg->FindWindow(wxID_CANCEL), wxButton);
if (cb) {
cb->SetLabel(_("&Close"));
cb->SetFocus();
}
*printdata = printer.GetPrintDialogData().GetPrintData();
}
}
void systemGbPrint(uint8_t* data, int len, int pages, int feed, int pal, int cont)
{
(void)pages; // unused params
(void)cont; // unused params
ModalPause mp; // this might take a while, so signal a pause
GameArea* panel = wxGetApp().frame->GetPanel();
static uint16_t* accum_prdata;
static int accum_prdata_len = 0, accum_prdata_size = 0;
static uint16_t prdata[162 * 145] = { 0 };
int lines = len / 40;
uint16_t* out = prdata + 162; // 1-pix top border
for (int y = 0; y < lines / 8; y++) {
for (int x = 0; x < 160 / 8; x++) {
for (int k = 0; k < 8; k++) {
int a = *data++;
int b = *data++;
for (int j = 0, mask = 0x80; j < 8; j++, mask >>= 1) {
int c = ((a & mask) | ((b & mask) << 1)) >> (7 - j);
// the guide I read said 0 is on top, but if I do that,
// output seems reversed. So either 11 in pal means
// white, or 00 in c means bits 0-1 in pal.
// the guide I read also said 00 is white and 11 is black
// I'm going with "00 in c means bits 0-1 in pal"
if (pal != 0xe4) // 11 10 01 00
c = (pal & (3 << c * 2)) >> c * 2;
out[j + k * 162] = systemGbPalette[c];
}
}
out += 8;
}
out += 2 + 7 * 162; // 2-pix rhs border
}
// assume no bottom margin means "more coming"
// probably ought to make this time out somehow
// or at the very least dump when the game state changes
uint16_t* to_print = prdata;
if ((gopts.print_auto_page && !(feed & 15)) || accum_prdata_len) {
if (!accum_prdata_len)
accum_prdata_len = 162; // top border
accum_prdata_len += lines * 162;
if (accum_prdata_size < accum_prdata_len) {
if (!accum_prdata_size)
accum_prdata = (uint16_t*)calloc(accum_prdata_len, 2);
else
accum_prdata = (uint16_t*)realloc(accum_prdata, accum_prdata_len * 2);
accum_prdata_size = accum_prdata_len;
}
memcpy(accum_prdata + accum_prdata_len - lines * 162, prdata + 162,
lines * 162 * 2);
if (gopts.print_auto_page && !(feed & 15))
return;
to_print = accum_prdata;
lines = accum_prdata_len / 162 - 1;
accum_prdata_len = 0;
}
if (gopts.print_screen_cap) {
wxFileName fn = wxFileName(wxGetApp().frame->GetGamePath(gopts.scrshot_dir), wxEmptyString);
int num = 1;
do {
wxString bfn;
bfn.Printf(wxT("%s-print%02d"), panel->game_name().c_str(),
num++);
if (captureFormat == 0)
bfn.append(wxT(".png"));
else // if(gopts.cap_format == 1)
bfn.append(wxT(".bmp"));
fn.SetFullName(bfn);
} while (fn.FileExists());
fn.Mkdir(0777, wxPATH_MKDIR_FULL);
int d = systemColorDepth;
int rs = systemRedShift, bs = systemBlueShift, gs = systemGreenShift;
systemColorDepth = 16;
systemRedShift = 10;
systemGreenShift = 5;
systemBlueShift = 0;
wxString of = fn.GetFullPath();
bool ret = captureFormat == 0 ? utilWritePNGFile(of.mb_fn_str(), 160, lines, (uint8_t*)to_print) : utilWriteBMPFile(of.mb_fn_str(), 160, lines, (uint8_t*)to_print);
if (ret) {
wxString msg;
msg.Printf(_("Wrote printer output to %s"), of.c_str());
systemScreenMessage(msg);
}
systemColorDepth = d;
systemRedShift = rs;
systemGreenShift = gs;
systemBlueShift = bs;
return;
}
PrintDialog dlg(to_print, lines, !(feed & 15));
int ret = dlg.ShowModal();
if (ret == wxID_OK) {
accum_prdata_len = (lines + 1) * 162;
if (to_print != accum_prdata) {
if (accum_prdata_size < accum_prdata_len) {
if (!accum_prdata_size)
accum_prdata = (uint16_t*)calloc(accum_prdata_len, 2);
else
accum_prdata = (uint16_t*)realloc(accum_prdata, accum_prdata_len * 2);
accum_prdata_size = accum_prdata_len;
}
memcpy(accum_prdata, to_print, accum_prdata_len * 2);
}
}
}
void systemScreenMessage(const wxString& msg)
{
if (wxGetApp().frame && wxGetApp().frame->IsShown()) {
wxPuts(msg);
MainFrame* f = wxGetApp().frame;
GameArea* panel = f->GetPanel();
if (!panel->osdtext.empty())
f->PopStatusText();
f->PushStatusText(msg);
panel->osdtext = msg;
panel->osdtime = systemGetClock();
}
}
void systemScreenMessage(const char* msg)
{
systemScreenMessage(wxString(msg, wxConvLibc));
}
bool systemCanChangeSoundQuality()
{
#ifndef NO_FFMPEG
if (emulating) {
GameArea* panel = wxGetApp().frame->GetPanel();
if (panel)
return !panel->IsRecording();
}
#endif
return wxGetApp().IsMainLoopRunning();
}
bool systemPauseOnFrame()
{
if (pause_next) {
pause_next = false;
MainFrame* fr = wxGetApp().frame;
fr->GetPanel()->Pause();
return true;
}
return false;
}
void systemGbBorderOn()
{
GameArea* panel = wxGetApp().frame->GetPanel();
if (panel)
panel->AddBorder();
}
class SoundDriver;
SoundDriver* systemSoundInit()
{
soundShutdown();
switch (gopts.audio_api) {
case AUD_SDL:
return new SoundSDL();
#ifndef NO_OAL
case AUD_OPENAL:
return newOpenAL();
#endif
#ifdef __WXMSW__
case AUD_DIRECTSOUND:
return newDirectSound();
#ifndef NO_XAUDIO2
case AUD_XAUDIO2:
return newXAudio2_Output();
#endif
#ifndef NO_FAUDIO
case AUD_FAUDIO:
return newFAudio_Output();
#endif
#endif
default:
gopts.audio_api = 0;
}
return 0;
}
void systemOnWriteDataToSoundBuffer(const uint16_t* finalWave, int length)
{
(void)finalWave; // unused params
(void)length; // unused params
#ifndef NO_FFMPEG
GameArea* panel = wxGetApp().frame->GetPanel();
if (panel)
panel->AddFrame(finalWave, length);
#endif
}
void systemOnSoundShutdown()
{
}
extern int (*remoteSendFnc)(char*, int);
extern int (*remoteRecvFnc)(char*, int);
extern void (*remoteCleanUpFnc)();
#ifndef __WXMSW__
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/poll.h>
static wxString pty_slave;
static int pty_master = -1;
static int debugReadPty(char* buf, int len)
{
while (1) {
struct pollfd fd;
fd.fd = pty_master;
fd.events = POLLIN;
if (poll(&fd, 1, 200) != 0)
return read(pty_master, buf, len);
else
return -2; // try to let repaints & such run
}
}
static int debugWritePty(/* const */ char* buf, int len)
{
return write(pty_master, buf, len);
}
static void debugClosePty()
{
if (pty_master >= 0) {
close(pty_master);
pty_master = -1;
}
}
bool debugOpenPty()
{
if (pty_master >= 0) // should never happen
close(pty_master);
const char* slave_name;
if ((pty_master = posix_openpt(O_RDWR | O_NOCTTY)) < 0 || grantpt(pty_master) < 0 || unlockpt(pty_master) < 0 || !(slave_name = ptsname(pty_master))) {
wxLogError(_("Error opening pseudo tty: %s"), wxString(strerror(errno),
wxConvLibc)
.c_str());
if (pty_master >= 0) {
close(pty_master);
pty_master = -1;
}
return false;
}
pty_slave = wxString(slave_name, wxConvLibc);
remoteSendFnc = debugWritePty;
remoteRecvFnc = debugReadPty;
remoteCleanUpFnc = debugClosePty;
return true;
}
const wxString& debugGetSlavePty()
{
return pty_slave;
}
bool debugWaitPty()
{
if (pty_master < 0)
return false;
struct pollfd fd;
fd.fd = pty_master;
fd.events = POLLIN;
return poll(&fd, 1, 100) > 0;
}
#endif
#include <wx/socket.h>
static wxSocketServer* debug_server = NULL;
wxSocketBase* debug_remote = NULL;
static int debugReadSock(char* buf, int len)
{
debug_remote->SetFlags(wxSOCKET_BLOCK);
MainFrame* f = wxGetApp().frame;
// WaitForRead does not obey wxSOCKET_BLOCK
// It's hard to prevent menu command selection while GUI is active
// so this will likely fail if the user isn't careful
// the only fix in place is to prevent recursive idle loop
f->StartModal();
bool done = debug_remote->WaitForRead(0, 200);
f->StopModal();
if (!done)
return -2;
debug_remote->Read(buf, len);
if (debug_remote->Error())
return -1;
return debug_remote->LastCount();
}
static int debugWriteSock(char* buf, int len)
{
debug_remote->SetFlags(wxSOCKET_WAITALL | wxSOCKET_BLOCK);
debug_remote->Write(buf, len);
if (debug_remote->Error())
return -1;
return debug_remote->LastCount();
}
static void debugCloseSock()
{
delete debug_remote;
debug_remote = NULL;
delete debug_server;
debug_server = NULL;
}
bool debugStartListen(int port)
{
delete debug_server; // should never be necessary
delete debug_remote;
debug_remote = NULL;
wxIPV4address addr;
addr.Service(port);
addr.AnyAddress(); // probably ought to have a flag to select any/localhost
debug_server = new wxSocketServer(addr, wxSOCKET_REUSEADDR);
remoteSendFnc = debugWriteSock;
remoteRecvFnc = debugReadSock;
remoteCleanUpFnc = debugCloseSock;
if (debug_server->IsOk())
return true;
wxLogError(_("Error setting up server socket (%d)"), debug_server->LastError());
return false;
}
bool debugWaitSocket()
{
if (!debug_server)
return false;
if (debug_remote)
return true;
debug_server->WaitForAccept(0, 100);
debug_remote = debug_server->Accept(false);
return debug_remote != NULL;
}
void log(const char* defaultMsg, ...)
{
static FILE* out = NULL;
va_list valist;
char buf[2048];
va_start(valist, defaultMsg);
vsnprintf(buf, 2048, defaultMsg, valist);
va_end(valist);
wxGetApp().log.append(wxString(buf, wxConvLibc));
if (wxGetApp().IsMainLoopRunning()) {
LogDialog* d = wxGetApp().frame->logdlg;
if (d && d->IsShown()) {
d->Update();
}
systemScreenMessage(buf);
}
if (out == NULL) {
// FIXME: this should be an option
wxFileName trace_log(wxGetApp().GetConfigurationPath(), wxT("trace.log"));
out = fopen(trace_log.GetFullPath().utf8_str(), "w");
if (!out)
return;
}
va_start(valist, defaultMsg);
vfprintf(out, defaultMsg, valist);
va_end(valist);
}