mirror of
https://github.com/makarcz/vm6502.git
synced 2024-09-27 12:58:15 +00:00
465 lines
12 KiB
C++
465 lines
12 KiB
C++
|
|
||
|
#include "GraphDisp.h"
|
||
|
#include "MKGenException.h"
|
||
|
#include "system.h"
|
||
|
|
||
|
#if defined(WINDOWS)
|
||
|
#include "wtypes.h"
|
||
|
#endif
|
||
|
|
||
|
namespace MKBasic {
|
||
|
|
||
|
/*
|
||
|
*--------------------------------------------------------------------
|
||
|
* Method: GraphDisp()
|
||
|
* Purpose: Class default constructor.
|
||
|
* Arguments: n/a
|
||
|
* Returns: n/a
|
||
|
*--------------------------------------------------------------------
|
||
|
*/
|
||
|
GraphDisp::GraphDisp()
|
||
|
{
|
||
|
Initialize();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
*--------------------------------------------------------------------
|
||
|
* Method: GraphDisp()
|
||
|
* Purpose: Class custom constructor.
|
||
|
* Arguments: width, height - integer, graphical display dimensions
|
||
|
* (virtual dimensions as opposed to
|
||
|
* to actual dimensions GRAPHDISP_MAXW,
|
||
|
* GRAPHDISP_MAXH)
|
||
|
* Returns: n/a
|
||
|
*--------------------------------------------------------------------
|
||
|
*/
|
||
|
GraphDisp::GraphDisp(int width, int height)
|
||
|
{
|
||
|
mWidth = width;
|
||
|
mHeight = height;
|
||
|
Initialize();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
*--------------------------------------------------------------------
|
||
|
* Method: ~GraphDisp()
|
||
|
* Purpose: Class destructor.
|
||
|
* Arguments: n/a
|
||
|
* Returns: n/a
|
||
|
*--------------------------------------------------------------------
|
||
|
*/
|
||
|
GraphDisp::~GraphDisp()
|
||
|
{
|
||
|
mContLoop = false;
|
||
|
while (mMainLoopActive);
|
||
|
SDL_DestroyRenderer(mpRenderer);
|
||
|
SDL_DestroyWindow(mpWindow);
|
||
|
SDL_Quit();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
*--------------------------------------------------------------------
|
||
|
* Method: Initialize()
|
||
|
* Purpose: Initialize class members, objects.
|
||
|
* Arguments: n/a
|
||
|
* Returns: n/a
|
||
|
* Problems:
|
||
|
* SDL_CreateWindow with flags:
|
||
|
* SDL_WINDOW_SHOWN|SDL_WINDOW_BORDERLESS|SDL_WINDOW_RESIZABLE
|
||
|
* creates window with no title/icon/system buttons, but with
|
||
|
* a frame, which can be used to resize it. The problem is, when
|
||
|
* it is resized, it stops updating correctly.
|
||
|
*--------------------------------------------------------------------
|
||
|
*/
|
||
|
void GraphDisp::Initialize()
|
||
|
{
|
||
|
int desk_w, desk_h, winbd_top = 5, winbd_right = 5;
|
||
|
|
||
|
GetDesktopResolution(desk_w, desk_h);
|
||
|
// Available in version > 2.0.4
|
||
|
//SDL_GetWindowBordersSize(mpWindow, &winbd_top, NULL, NULL, &winbd_right);
|
||
|
mWinPosX = desk_w - GRAPHDISP_MAXW - winbd_right;
|
||
|
mWinPosY = winbd_top;
|
||
|
|
||
|
SDL_Init(SDL_INIT_VIDEO);
|
||
|
|
||
|
mpWindow = SDL_CreateWindow(
|
||
|
"GraphDisp",
|
||
|
mWinPosX,
|
||
|
mWinPosY,
|
||
|
GRAPHDISP_MAXW,
|
||
|
GRAPHDISP_MAXH,
|
||
|
SDL_WINDOW_SHOWN|SDL_WINDOW_BORDERLESS|SDL_WINDOW_RESIZABLE
|
||
|
);
|
||
|
|
||
|
if (NULL == mpWindow) {
|
||
|
throw MKGenException(SDL_GetError());
|
||
|
}
|
||
|
|
||
|
// Get window surface
|
||
|
mpSurface = SDL_GetWindowSurface(mpWindow);
|
||
|
|
||
|
// Create renderer for window
|
||
|
mpRenderer = SDL_CreateRenderer(mpWindow, -1, SDL_RENDERER_SOFTWARE);
|
||
|
//mpRenderer = SDL_GetRenderer(mpWindow);
|
||
|
|
||
|
if (NULL == mpRenderer) {
|
||
|
throw MKGenException(SDL_GetError());
|
||
|
}
|
||
|
|
||
|
Clear();
|
||
|
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
*--------------------------------------------------------------------
|
||
|
* Method: ClearScreen()
|
||
|
* Purpose: Clear the surface. Update screen.
|
||
|
* Arguments: n/a
|
||
|
* Returns: n/a
|
||
|
*--------------------------------------------------------------------
|
||
|
*/
|
||
|
void GraphDisp::ClearScreen()
|
||
|
{
|
||
|
Clear();
|
||
|
UpdateSurface();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
*--------------------------------------------------------------------
|
||
|
* Method: Clear()
|
||
|
* Purpose: Clear the surface.
|
||
|
* Arguments: n/a
|
||
|
* Returns: n/a
|
||
|
*--------------------------------------------------------------------
|
||
|
*/
|
||
|
void GraphDisp::Clear()
|
||
|
{
|
||
|
//Fill the surface with background color
|
||
|
SDL_FillRect(mpSurface, NULL, SDL_MapRGB(mpSurface->format,
|
||
|
mBgRgbR,
|
||
|
mBgRgbG,
|
||
|
mBgRgbB));
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
*--------------------------------------------------------------------
|
||
|
* Method: UpdateSurface()
|
||
|
* Purpose: Update window surface.
|
||
|
* Arguments: n/a
|
||
|
* Returns: n/a
|
||
|
*--------------------------------------------------------------------
|
||
|
*/
|
||
|
void GraphDisp::UpdateSurface()
|
||
|
{
|
||
|
//Update the surface
|
||
|
SDL_UpdateWindowSurface(mpWindow);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
*--------------------------------------------------------------------
|
||
|
* Method: Update()
|
||
|
* Purpose: Update window surface (public).
|
||
|
* Arguments: n/a
|
||
|
* Returns: n/a
|
||
|
*--------------------------------------------------------------------
|
||
|
*/
|
||
|
void GraphDisp::Update()
|
||
|
{
|
||
|
//Update the surface
|
||
|
UpdateSurface();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
*--------------------------------------------------------------------
|
||
|
* Method: RenderPixel()
|
||
|
* Purpose: Set or unset pixel (scaled) on graphics display
|
||
|
* surface.
|
||
|
* Arguments: x, y - integer, virtual pixel coordinates
|
||
|
* set - boolean, set or unset pixel
|
||
|
* Returns: n/a
|
||
|
*--------------------------------------------------------------------
|
||
|
*/
|
||
|
void GraphDisp::RenderPixel(int x, int y, bool set)
|
||
|
{
|
||
|
SDL_Rect rtf;
|
||
|
|
||
|
rtf.x = x * mPixelSizeX; rtf.y = y * mPixelSizeY;
|
||
|
rtf.w = mPixelSizeX;
|
||
|
rtf.h = mPixelSizeY;
|
||
|
|
||
|
int rgb_r = 0, rgb_g = 0, rgb_b = 0;
|
||
|
|
||
|
if (set) {
|
||
|
rgb_r = mFgRgbR;
|
||
|
rgb_g = mFgRgbG;
|
||
|
rgb_b = mFgRgbB;
|
||
|
} else {
|
||
|
rgb_r = mBgRgbR;
|
||
|
rgb_g = mBgRgbG;
|
||
|
rgb_b = mBgRgbB;
|
||
|
}
|
||
|
|
||
|
// set or unset pixel
|
||
|
SDL_FillRect(mpSurface, &rtf, SDL_MapRGB(mpSurface->format, rgb_r, rgb_g, rgb_b));
|
||
|
|
||
|
//Update the surface
|
||
|
SDL_UpdateWindowSurface(mpWindow);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
*--------------------------------------------------------------------
|
||
|
* Method: SetPixel()
|
||
|
* Purpose: Set pixel (scaled) on graphics display surface.
|
||
|
* Arguments: x, y - pixel coordinates
|
||
|
* Returns: n/a
|
||
|
*--------------------------------------------------------------------
|
||
|
*/
|
||
|
void GraphDisp::SetPixel(int x, int y)
|
||
|
{
|
||
|
RenderPixel(x, y, true);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
*--------------------------------------------------------------------
|
||
|
* Method: ErasePixel()
|
||
|
* Purpose: Unset (erase) pixel (paint with BG color) on graphics
|
||
|
* display surface.
|
||
|
* Arguments: x, y - pixel coordinates
|
||
|
* Returns: n/a
|
||
|
*--------------------------------------------------------------------
|
||
|
*/
|
||
|
void GraphDisp::ErasePixel(int x, int y)
|
||
|
{
|
||
|
RenderPixel(x, y, false);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
*--------------------------------------------------------------------
|
||
|
* Method: DrawLine()
|
||
|
* Purpose: Draw or erase line between specified points.
|
||
|
* Arguments: x1, y1 - coordinates of first point
|
||
|
* x2, y2 - coordinates of second point
|
||
|
* Returns: n/a
|
||
|
*--------------------------------------------------------------------
|
||
|
*/
|
||
|
void GraphDisp::DrawLine(int x1, int y1, int x2, int y2, bool draworerase)
|
||
|
{
|
||
|
int colR = mFgRgbR, colG = mFgRgbG, colB = mFgRgbB;
|
||
|
|
||
|
if (false == draworerase) {
|
||
|
colR = mBgRgbR;
|
||
|
colG = mBgRgbG;
|
||
|
colB = mBgRgbB;
|
||
|
}
|
||
|
|
||
|
SDL_SetRenderDrawColor(mpRenderer, colR, colG, colB, SDL_ALPHA_OPAQUE);
|
||
|
SDL_RenderSetLogicalSize(mpRenderer, mWidth, mHeight);
|
||
|
SDL_RenderSetScale(mpRenderer, mPixelSizeX, mPixelSizeY);
|
||
|
SDL_RenderDrawLine(mpRenderer, x1, y1, x2, y2);
|
||
|
SDL_RenderPresent(mpRenderer);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
*--------------------------------------------------------------------
|
||
|
* Method: DrawLine()
|
||
|
* Purpose: Draw line between specified points.
|
||
|
* Arguments: x1, y1 - coordinates of first point
|
||
|
* x2, y2 - coordinates of second point
|
||
|
* Returns: n/a
|
||
|
*--------------------------------------------------------------------
|
||
|
*/
|
||
|
void GraphDisp::DrawLine(int x1, int y1, int x2, int y2)
|
||
|
{
|
||
|
DrawLine(x1, y1, x2, y2, true);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
*--------------------------------------------------------------------
|
||
|
* Method: EraseLine()
|
||
|
* Purpose: Erase line between specified points (draw with BG color)
|
||
|
* Arguments: x1, y1 - coordinates of first point
|
||
|
* x2, y2 - coordinates of second point
|
||
|
* Returns: n/a
|
||
|
*--------------------------------------------------------------------
|
||
|
*/
|
||
|
void GraphDisp::EraseLine(int x1, int y1, int x2, int y2)
|
||
|
{
|
||
|
DrawLine(x1, y1, x2, y2, false);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
*--------------------------------------------------------------------
|
||
|
* Method: SetBgColor()
|
||
|
* Purpose: Set background color.
|
||
|
* Arguments: r, g, b - integer, red, green and blue intensities
|
||
|
* Returns: n/a
|
||
|
*--------------------------------------------------------------------
|
||
|
*/
|
||
|
void GraphDisp::SetBgColor(int r, int g, int b)
|
||
|
{
|
||
|
mBgRgbR = r;
|
||
|
mBgRgbG = g;
|
||
|
mBgRgbB = b;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
*--------------------------------------------------------------------
|
||
|
* Method: SetFgColor()
|
||
|
* Purpose: Set foreground color.
|
||
|
* Arguments: r, g, b - integer, red, green and blue intensities
|
||
|
* Returns: n/a
|
||
|
*--------------------------------------------------------------------
|
||
|
*/
|
||
|
void GraphDisp::SetFgColor(int r, int g, int b)
|
||
|
{
|
||
|
mFgRgbR = r;
|
||
|
mFgRgbG = g;
|
||
|
mFgRgbB = b;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
*--------------------------------------------------------------------
|
||
|
* Method: MainLoop()
|
||
|
* Purpose: The main loop to process SDL events and update window.
|
||
|
* This is a global function meant to run in a thread.
|
||
|
* Arguments: n/a
|
||
|
* Returns: n/a
|
||
|
*--------------------------------------------------------------------
|
||
|
*/
|
||
|
void MainLoop(GraphDisp *pgd)
|
||
|
{
|
||
|
pgd->mMainLoopActive = true;
|
||
|
while (pgd->mContLoop) {
|
||
|
pgd->ReadEvents();
|
||
|
pgd->Update();
|
||
|
}
|
||
|
pgd->mMainLoopActive = false;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
*--------------------------------------------------------------------
|
||
|
* Method: Start()
|
||
|
* Purpose: Starts MainLoop in a thread.
|
||
|
* Arguments: n/a
|
||
|
* Returns: n/a
|
||
|
*--------------------------------------------------------------------
|
||
|
*/
|
||
|
void GraphDisp::Start(GraphDisp *pgd)
|
||
|
{
|
||
|
mMainLoopThread = thread(MainLoop,pgd);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
*--------------------------------------------------------------------
|
||
|
* Method: Stop()
|
||
|
* Purpose: Stop MainLoop thread.
|
||
|
* Arguments: n/a
|
||
|
* Returns: n/a
|
||
|
*--------------------------------------------------------------------
|
||
|
*/
|
||
|
void GraphDisp::Stop()
|
||
|
{
|
||
|
mContLoop = false;
|
||
|
mMainLoopThread.join();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
*--------------------------------------------------------------------
|
||
|
* Method:
|
||
|
* Purpose:
|
||
|
* Arguments:
|
||
|
* Returns:
|
||
|
*--------------------------------------------------------------------
|
||
|
*/
|
||
|
bool GraphDisp::IsMainLoopActive()
|
||
|
{
|
||
|
return mMainLoopActive;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
*--------------------------------------------------------------------
|
||
|
* Method: ReadEvents()
|
||
|
* Purpose: Read events and perform actions when needed.
|
||
|
* Arguments: n/a
|
||
|
* Returns: n/a
|
||
|
* Problems:
|
||
|
* By blitting (copying) surface I wanted to avoid loosing already
|
||
|
* drawn pixels during windows resize, but this doesn't work.
|
||
|
*--------------------------------------------------------------------
|
||
|
*/
|
||
|
void GraphDisp::ReadEvents()
|
||
|
{
|
||
|
SDL_Event e;
|
||
|
SDL_Surface *pTmpSurface;
|
||
|
|
||
|
while (SDL_PollEvent(&e)) {
|
||
|
|
||
|
switch (e.type) {
|
||
|
|
||
|
case SDL_QUIT:
|
||
|
mContLoop = false;
|
||
|
break;
|
||
|
|
||
|
case SDL_WINDOWEVENT:
|
||
|
if (SDL_WINDOWEVENT_RESIZED == e.window.event) {
|
||
|
|
||
|
pTmpSurface = SDL_CreateRGBSurface(0,
|
||
|
mpSurface->w,
|
||
|
mpSurface->h,
|
||
|
mpSurface->format->BitsPerPixel,
|
||
|
mpSurface->format->Rmask,
|
||
|
mpSurface->format->Gmask,
|
||
|
mpSurface->format->Bmask,
|
||
|
mpSurface->format->Amask);
|
||
|
SDL_SetWindowSize(mpWindow, GRAPHDISP_MAXW, GRAPHDISP_MAXH);
|
||
|
mpSurface = SDL_GetWindowSurface(mpWindow);
|
||
|
SDL_SetWindowPosition(mpWindow, mWinPosX, mWinPosY);
|
||
|
SDL_SetSurfaceAlphaMod(pTmpSurface, 0);
|
||
|
SDL_BlitSurface(pTmpSurface, 0, mpSurface, 0);
|
||
|
UpdateSurface();
|
||
|
SDL_FreeSurface(pTmpSurface);
|
||
|
|
||
|
} else if (SDL_WINDOWEVENT_FOCUS_GAINED == e.window.event) {
|
||
|
|
||
|
SDL_RaiseWindow(mpWindow);
|
||
|
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
*--------------------------------------------------------------------
|
||
|
* Method:
|
||
|
* Purpose:
|
||
|
* Arguments:
|
||
|
* Returns:
|
||
|
*--------------------------------------------------------------------
|
||
|
*/
|
||
|
void GraphDisp::GetDesktopResolution(int& horizontal, int& vertical)
|
||
|
{
|
||
|
#if defined(WINDOWS)
|
||
|
RECT desktop;
|
||
|
// Get a handle to the desktop window
|
||
|
const HWND hDesktop = GetDesktopWindow();
|
||
|
// Get the size of screen to the variable desktop
|
||
|
GetWindowRect(hDesktop, &desktop);
|
||
|
// The top left corner will have coordinates (0,0)
|
||
|
// and the bottom right corner will have coordinates
|
||
|
// (horizontal, vertical)
|
||
|
horizontal = desktop.right;
|
||
|
vertical = desktop.bottom;
|
||
|
#else
|
||
|
horizontal = GRAPHDISP_MAXW;
|
||
|
vertical = GRAPHDISP_MAXH;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
} // namespace MKBasic
|