📄 graphics.cpp
字号:
// graphics.cpp
#ifdef __BORLANDC__
#pragma warn -8027
#endif
#include "graphics.h"
#include "sysvar.h"
#include "error.h"
#include "var.h"
//#include "cursor.h"
#include "key.h"
#include "util.h"
// Para depuracion
#include <iostream>
using std::cerr;
using std::endl;
#include <cassert>
#define ASSERT assert
#include <map>
#include <queue>
#include <cmath>
#ifdef BLASSIC_USE_SVGALIB
#include <unistd.h>
#include <sys/types.h>
#include <vga.h>
#include <vgagl.h>
char * font= NULL;
#endif
#ifdef BLASSIC_USE_X
#include <X11/Xlib.h>
#include <X11/Xutil.h>
Display * display= 0;
int screen;
Window window;
Pixmap pixmap;
bool pixmap_created= false;
GC gc, gcp;
XGCValues gcvalues, gcvaluesp;
XEvent x_event;
long eventusedmask= ExposureMask | KeyPressMask |
ButtonPressMask | ButtonReleaseMask |
PointerMotionMask | EnterWindowMask;
XColor xcBlack, xcBlue, xcGreen, xcCyan,
xcRed, xcMagenta, xcBrown, xcLightGrey,
xcDarkGrey, xcLightBlue, xcLightGreen, xcLightCyan,
xcLightRed, xcLightMagenta, xcYellow, xcWhite;
typedef XColor * pcolor;
#endif
#ifdef BLASSIC_USE_WINDOWS
#include <process.h>
#include <windows.h>
ATOM atomClass;
HANDLE hEvent;
HWND window;
HDC hdc;
HBITMAP pixmap;
HDC hdcPixmap;
HPEN xcBlack, xcBlue, xcGreen, xcCyan,
xcRed, xcMagenta, xcBrown, xcLightGrey,
xcDarkGrey, xcLightBlue, xcLightGreen, xcLightCyan,
xcLightRed, xcLightMagenta, xcYellow, xcWhite;
typedef HPEN * pcolor;
#endif
#if defined (BLASSIC_USE_WINDOWS) || defined (BLASSIC_USE_X)
pcolor pforeground, pbackground,
default_foreground, default_background;
#endif
#include <string>
#include <algorithm>
namespace {
bool fSynchro= false;
class CriticalSection {
public:
CriticalSection ()
{
#ifdef BLASSIC_USE_WINDOWS
InitializeCriticalSection (& cs);
#endif
}
~CriticalSection ()
{
#ifdef BLASSIC_USE_WINDOWS
DeleteCriticalSection (& cs);
#endif
}
void enter ()
{
#ifdef BLASSIC_USE_WINDOWS
EnterCriticalSection (& cs);
#endif
}
void leave ()
{
#ifdef BLASSIC_USE_WINDOWS
LeaveCriticalSection (& cs);
#endif
}
private:
#ifdef BLASSIC_USE_WINDOWS
CRITICAL_SECTION cs;
#endif
};
class CriticalLock {
public:
CriticalLock (CriticalSection & cs) :
cs (cs)
{
cs.enter ();
}
~CriticalLock ()
{
cs.leave ();
}
private:
CriticalSection & cs;
};
class QueueKey {
public:
QueueKey ()
{
}
void push (const std::string & str)
{
CriticalLock lock (cs);
q.push (str);
}
std::string pop ()
{
CriticalLock lock (cs);
std::string str= q.front ();
q.pop ();
return str;
}
bool empty ()
{
return q.empty ();
}
private:
std::queue <std::string> q;
CriticalSection cs;
};
QueueKey queuekey;
bool inited= false;
bool window_created= false;
bool opaquemode= true;
int xmousepos, ymousepos;
const int text_mode= 0, user_mode= -1;
int actualmode= text_mode;
inline void requiregraphics ()
{
if (actualmode == text_mode)
throw ErrNoGraphics;
}
#ifdef BLASSIC_USE_SVGALIB
bool svgalib= false;
#else
//const bool svgalib= false;
#endif
int screenwidth, screenheight;
int lastx, lasty;
#if defined BLASSIC_USE_X
static const int
drawmode_copy= GXcopy,
drawmode_xor= GXxor,
drawmode_and= GXand,
drawmode_or= GXor,
drawmode_invert= GXinvert;
#elif defined BLASSIC_USE_WINDOWS
static const int
drawmode_copy= R2_COPYPEN,
drawmode_xor= R2_XORPEN,
// Revisar los valores para and y or.
drawmode_and= R2_MASKPEN,
drawmode_or= R2_MERGEPEN,
drawmode_invert= R2_NOT;
#endif
int drawmode= drawmode_copy;
void reinit_pixmap ()
{
#ifdef BLASSIC_USE_WINDOWS
RECT r = { 0, 0, screenwidth, screenheight };
FillRect (hdcPixmap, & r, (HBRUSH) GetStockObject (WHITE_BRUSH) );
#endif
#ifdef BLASSIC_USE_X
XSetForeground (display, gcp, WhitePixel (display, screen) );
XFillRectangle (display, pixmap, gcp, 0, 0, screenwidth, screenheight);
XSetForeground (display, gcp, BlackPixel (display, screen) );
#endif
}
void reinit_window ()
{
#ifdef BLASSIC_USE_WINDOWS
//HDC hdc= GetDC (window);
BitBlt (hdc, 0, 0, screenwidth, screenheight, hdcPixmap, 0, 0, SRCCOPY);
//ReleaseDC (window, hdc);
#endif
#ifdef BLASSIC_USE_X
XSetFunction (display, gc, drawmode_copy);
XCopyArea (display, pixmap, window, gc,
0, 0, screenwidth, screenheight, 0, 0);
XSetForeground (display, gc, BlackPixel (display, screen) );
XSetForeground (display, gc, pforeground->pixel);
XFlush (display);
XSetFunction (display, gc, drawmode);
#endif
}
#ifdef BLASSIC_USE_WINDOWS
const UINT
WM_USER_CREATE_WINDOW= WM_USER + 3,
WM_USER_DESTROY_WINDOW= WM_USER + 4;
HANDLE hthread= NULL;
DWORD idthread= 0;
LRESULT APIENTRY windowproc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static int width, height;
switch (uMsg) {
case WM_SIZE:
width= LOWORD (lParam);
height= HIWORD (lParam);
return TRUE;
case WM_ERASEBKGND:
return TRUE;
case WM_PAINT:
{
//err << "WM_PAINT " << width << ", " << height << endl;
PAINTSTRUCT paintstruct;
HDC hdc= BeginPaint (hwnd, & paintstruct);
BitBlt (hdc, 0, 0, width, height, hdcPixmap, 0, 0, SRCCOPY);
EndPaint (hwnd, & paintstruct);
}
return FALSE;
case WM_KEYDOWN:
{
WORD k= (WORD) wParam;
//std::string str= string_from_virtual_key (k);
std::string str= string_from_key (k);
if (! str.empty () )
{
queuekey.push (str);
return TRUE;
}
}
return FALSE;
case WM_CHAR:
{
char c= (char) wParam;
queuekey.push (std::string (1, c) );
}
return TRUE;
case WM_MOUSEMOVE:
xmousepos= LOWORD (lParam);
ymousepos= HIWORD (lParam);
return TRUE;
case WM_LBUTTONDOWN:
queuekey.push (strCLICK);
return TRUE;
case WM_RBUTTONDOWN:
queuekey.push (strSCLICK);
return TRUE;
case WM_LBUTTONUP:
queuekey.push (strRELEASE);
return TRUE;
case WM_RBUTTONUP:
queuekey.push (strSRELEASE);
return TRUE;
case WM_DESTROY:
SetEvent (hEvent);
return TRUE;
default:
return DefWindowProc (hwnd, uMsg, wParam, lParam);
}
}
void thread_create_window (int width, int height)
{
HDC hdcscreen= CreateDC ("DISPLAY", NULL, NULL, NULL);
if (hdcscreen == NULL)
return;
pixmap= CreateCompatibleBitmap (hdcscreen, width, height);
if (pixmap == NULL)
{
DeleteDC (hdcscreen);
return;
}
hdcPixmap= CreateCompatibleDC (hdcscreen);
DeleteDC (hdcscreen);
if (hdcPixmap == NULL)
{
DeleteObject (pixmap);
pixmap= NULL;
return;
}
SelectObject (hdcPixmap, pixmap);
reinit_pixmap ();
window= CreateWindow (
LPCTSTR (atomClass),
"blassic",
WS_VISIBLE,
0, 0,
width + GetSystemMetrics (SM_CXDLGFRAME) * 2,
height + GetSystemMetrics (SM_CYDLGFRAME) * 2 +
GetSystemMetrics (SM_CYCAPTION),
NULL,
NULL,
GetModuleHandle (0),
NULL);
if (window)
{
SetActiveWindow (window);
hdc= GetDC (window);
window_created= true;
}
else
{
DeleteDC (hdcPixmap);
DeleteObject (pixmap);
pixmap= NULL;
}
}
// Testing new method of create and destroy windows.
struct ThreadParams {
int width, height;
};
unsigned WINAPI threadproc (void * arg)
{
MSG msg;
ThreadParams * tp= reinterpret_cast <ThreadParams *> (arg);
thread_create_window (tp->width, tp->height);
// Ensure the message queue exist before the main thread continues.
// Perhpas unnecesary with the new method, but...
PeekMessage (& msg, NULL, 0, UINT (-1), PM_NOREMOVE);
SetEvent (hEvent);
if (! window_created)
return 0;
while (GetMessage (& msg, NULL, 0, 0) )
{
switch (msg.message)
{
case WM_USER_CREATE_WINDOW:
//thread_create_window (msg.wParam, msg.lParam);
SetEvent (hEvent);
break;
case WM_USER_DESTROY_WINDOW:
if (DestroyWindow (window) == 0)
cerr << "Error al destruir: " <<
GetLastError () << endl;
break;
default:
TranslateMessage (& msg);
DispatchMessage (& msg);
}
}
return 0;
}
void create_thread (int width, int height)
{
ThreadParams tp= { width, height };
hthread= HANDLE (_beginthreadex (NULL, 0, threadproc,
& tp, 0, (unsigned int *) (& idthread) ) );
if (hthread == NULL)
{
cerr << "Error al crear thread para graficos" << endl;
throw ErrBlassicInternal;
}
WaitForSingleObject (hEvent, INFINITE);
if (! window_created)
{
WaitForSingleObject (hthread, INFINITE);
CloseHandle (hthread);
idthread= 0;
hthread= NULL;
}
}
void destroy_thread ()
{
if (idthread)
{
BOOL r= PostThreadMessage (idthread, WM_QUIT, 0, 0);
if (r == 0)
{
TerminateThread (hthread, 0);
}
else
{
WaitForSingleObject (hthread, INFINITE);
}
CloseHandle (hthread);
idthread= 0;
hthread= NULL;
}
}
#endif // WINDOWS
void create_window (int width, int height)
{
#ifdef BLASSIC_USE_WINDOWS
if (hthread == NULL)
{
create_thread (width, height);
if (hthread == NULL)
throw ErrBlassicInternal;
}
BOOL r= PostThreadMessage (idthread, WM_USER_CREATE_WINDOW,
WPARAM (width), LPARAM (height) );
if (r == 0)
{
cerr << "Error al comunicarse con thread de graficos. "
"GetLastError =" << GetLastError () <<
endl;
destroy_thread ();
throw ErrBlassicInternal;
}
WaitForSingleObject (hEvent, INFINITE);
if (! window_created)
{
cerr << "Error al crear ventana" << endl;
throw ErrBlassicInternal;
}
#endif
#ifdef BLASSIC_USE_X
#if 1
window= XCreateSimpleWindow (display,
RootWindow (display, screen),
0, 0, width, height,
5, BlackPixel (display, screen),
WhitePixel (display, screen) );
#else
int depth= 8;
window= XCreateWindow (display,
RootWindow (display, screen),
0, 0, width, height,
5,
depth,
InputOutput,
CopyFromParent,
0,
NULL);
#endif
window_created= true;
#if 1
int depth= DefaultDepth (display, DefaultScreen (display) );
#endif
pixmap= XCreatePixmap (display, window,
width, height, depth);
pixmap_created= true;
gc= XCreateGC (display, window, 0, & gcvalues);
gcp= XCreateGC (display, pixmap, 0, & gcvaluesp);
//XSetForeground (display, gcp, WhitePixel (display, screen) );
//XFillRectangle (display, pixmap, gcp, 0, 0, width, height);
//XSetForeground (display, gcp, BlackPixel (display, screen) );
reinit_pixmap ();
XSetStandardProperties (display, window,
"blassic", "blassic", None,
0, 0, NULL);
XSelectInput (display, window, eventusedmask);
XMapWindow (display, window);
graphics::idle ();
#endif
}
void destroy_window ()
{
#ifdef BLASSIC_USE_WINDOWS
PostThreadMessage (idthread, WM_USER_DESTROY_WINDOW, 0, 0);
WaitForSingleObject (hEvent, INFINITE);
DeleteDC (hdcPixmap);
DeleteObject (pixmap);
window= 0;
window_created= false;
destroy_thread ();
#endif
#ifdef BLASSIC_USE_X
XDestroyWindow (display, window);
window_created= false;
XFreePixmap (display, pixmap);
pixmap_created= false;
XFlush (display);
graphics::idle ();
#endif
}
#ifdef BLASSIC_USE_WINDOWS
struct assign_color {
pcolor pxc;
int r, g, b;
};
class assign_elem {
public:
void operator () (const assign_color & elem) const
{
* elem.pxc= CreatePen (PS_SOLID, 1,
RGB (elem.r, elem.g, elem.b) );
}
};
void init_wincolors ()
{
#ifdef __BORLANDC__
// With const Borland can't expand the for_each.
typedef assign_color const_assign_color;
#else
typedef const assign_color const_assign_color;
#endif
static const_assign_color table_colors []= {
#if 0
{ &xcBlack, 0, 0, 0 },
{ &xcBlue, 0, 0, 42 * 4 },
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -