diff options
Diffstat (limited to 'graphics.cpp')
-rw-r--r-- | graphics.cpp | 8110 |
1 files changed, 8110 insertions, 0 deletions
diff --git a/graphics.cpp b/graphics.cpp new file mode 100644 index 0000000..e84423b --- /dev/null +++ b/graphics.cpp @@ -0,0 +1,8110 @@ +// graphics.cpp +// Revision 24-apr-2009 + +#ifdef __BORLANDC__ +#pragma warn -8027 +#endif + +#include "graphics.h" +#include "sysvar.h" +#include "error.h" +#include "var.h" +#include "key.h" +#include "charset.h" + +#include "util.h" +using util::touch; +using util::to_string; + +#include "trace.h" + +#include <string> +#include <vector> +#include <algorithm> +#include <sstream> +#include <iomanip> + +#include <memory> +using std::auto_ptr; + +#include <map> +#include <queue> + +#include <string.h> +#include <limits.h> +#include <math.h> + +// Para depuracion +#include <iostream> +using std::cerr; +using std::endl; +#if defined __unix__ || defined __linux__ +#include <unistd.h> +#endif +#include <cassert> +#define ASSERT assert + + +// This is controlled with the configure option --disable-graphics +#ifndef BLASSIC_CONFIG_NO_GRAPHICS + +#if defined BLASSIC_USE_WINDOWS || defined BLASSIC_USE_X || \ + defined BLASSIC_USE_SVGALIB + +// If configure is not used you can comment the following line +// to disable graphics. + +#define BLASSIC_HAS_GRAPHICS + +#endif + +#endif + + +#ifdef BLASSIC_HAS_GRAPHICS + +#ifdef BLASSIC_USE_SVGALIB + +#include <unistd.h> +#include <sys/types.h> +#include <vga.h> +#include <vgagl.h> + +#endif +// BLASSIC_USE_SVGALIB + +#ifdef BLASSIC_USE_X + +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/keysym.h> + +#endif +// BLASSIC_USE_X + +#ifdef BLASSIC_USE_WINDOWS + +#include <process.h> +#include <windows.h> +#undef min +#undef max + +#if defined __CYGWIN32__ || defined __CYGWIN__ +// This macros are from Anders Norlander, modified to add +// the cast to start_proc. +/* Macro uses args se we can cast proc to LPTHREAD_START_ROUTINE + in order to avoid warings because of return type */ +#define _beginthreadex(security, stack_size, start_proc, arg, flags, pid) \ +CreateThread (security, stack_size, (LPTHREAD_START_ROUTINE) start_proc, \ + arg, flags, (LPDWORD) pid) +#define _endthreadex ExitThread +#endif + +// Use Polyline to draw a point. +#define USE_POLY + +#endif +// BLASSIC_USE_WINDOWS + +#endif +// BLASSIC_HAS_GRAPHICS + +namespace sysvar= blassic::sysvar; + + +// Character set + +charset::chardataset charset::data; + +const charset::chardataset * charset::default_charset= + & charset::default_data; + +namespace { + +#ifndef BLASSIC_HAS_GRAPHICS + +void no_graphics_support () +{ + if (showdebuginfo () ) + cerr << "This version of Blassic was compiled " + "without graphics support" << endl; + throw ErrFunctionCall; +} + +#endif +// BLASSIC_HAS_GRAPHICS + +#ifdef BLASSIC_HAS_GRAPHICS + +#ifdef BLASSIC_USE_SVGALIB + +char * font= NULL; + +#endif +// BLASSIC_USE_SVGALIB + +#ifdef BLASSIC_USE_X + +Display * display= 0; +XIM xim= 0; +XIC xic= 0; +int screen; +Window window; +Pixmap pixmap; +bool pixmap_created= false; +GC gc, gcp; +XGCValues gcvalues, gcvaluesp; + +//XEvent x_event; + +long eventusedmask= StructureNotifyMask | ExposureMask | + KeyPressMask | KeyReleaseMask | + ButtonPressMask | ButtonReleaseMask | + PointerMotionMask | EnterWindowMask; +long eventusedmaskactual; + +typedef XColor color_t; + +typedef unsigned long ColorValue; + +#endif +// BLASSIC_USE_X + +#ifdef BLASSIC_USE_WINDOWS + +ATOM atomClass; +HANDLE hEvent; +HWND window; +HDC hdc= 0; +HBITMAP pixmap; +HDC hdcPixmap= 0; +typedef HPEN color_t; + +typedef COLORREF ColorValue; + +#endif +// BLASSIC_USE_WINDOWS + +#if defined (BLASSIC_USE_WINDOWS) || defined (BLASSIC_USE_X) + +color_t xcBlack, xcBlue, xcGreen, xcCyan, + xcRed, xcMagenta, xcBrown, xcLightGrey, + xcDarkGrey, xcLightBlue, xcLightGreen, xcLightCyan, + xcLightRed, xcLightMagenta, xcYellow, xcWhite; + +typedef color_t * pcolor; + +pcolor pforeground, pbackground, + //default_foreground= & xcBlack, default_background= & xcWhite, + activecolor= NULL; + +int default_pen, default_paper; + +int graphics_pen, graphics_paper; + +#endif +// defined BLASSIC_USE_WINDOWS || defined BLASSIC_USE_X + +std::string default_title ("blassic"); + +bool fSynchro= false; + +#ifdef BLASSIC_USE_WINDOWS + +class CriticalSection { +public: + CriticalSection () + { + InitializeCriticalSection (& cs); + } + ~CriticalSection () + { + DeleteCriticalSection (& cs); + } + void enter () + { + EnterCriticalSection (& cs); + } + void leave () + { + LeaveCriticalSection (& cs); + } +private: + CRITICAL_SECTION cs; +}; + +class CriticalLock { +public: + CriticalLock (CriticalSection & cs) : + cs (cs) + { + cs.enter (); + } + ~CriticalLock () + { + cs.leave (); + } +private: + CriticalSection & cs; +}; + +#else + +// Empty implementation, not using threads. + +class CriticalSection { }; + +class CriticalLock { +public: + CriticalLock (CriticalSection &) + { } +}; + +#endif + +class QueueKey { +public: + QueueKey () + { + } + void push (const std::string & str) + { + CriticalLock lock (cs); + touch (lock); + q.push (str); + } + std::string pop () + { + CriticalLock lock (cs); + touch (lock); + std::string str= q.front (); + q.pop (); + return str; + } + bool empty () + { + return q.empty (); + } + void erase () + { + CriticalLock lock (cs); + touch (lock); + while (! q.empty () ) + q.pop (); + } +private: + std::queue <std::string> q; + CriticalSection cs; +}; + +#if 0 +const size_t MAXKEYSYM= 65535; +std::vector <bool> keypressedmap (MAXKEYSYM); +#endif + +const size_t MAXINKEYCODE= 79; + +#ifdef BLASSIC_USE_WINDOWS + +#if 0 +unsigned int presscode [MAXINKEYCODE + 1]= { +}; +#endif + +#elif defined BLASSIC_USE_X + +#if 0 +const unsigned int KEYSYMUNUSED= 1; +unsigned int presscode [MAXINKEYCODE + 1]= { + XK_Up, // 0 + XK_Right, // 1 + XK_Down, // 2 + XK_KP_9, // 3 + XK_KP_6, // 4 + XK_KP_3, // 5 + XK_Execute, // 6 + XK_KP_Decimal, // 7 + XK_Left, // 8 + KEYSYMUNUSED, // 9 "Copy" key + XK_KP_7, // 10 + XK_KP_8, // 11 + XK_KP_5, // 12 + XK_KP_1, // 13 + XK_KP_2, // 14 + XK_KP_0, // 15 + XK_Delete, // 16 + KEYSYMUNUSED, // 17 + XK_Return, // 18 + KEYSYMUNUSED, // 19 + XK_KP_4, // 20 + XK_Shift_L, // 21 + KEYSYMUNUSED, // 22 + XK_Control_L, // 23 + KEYSYMUNUSED, // 24 + KEYSYMUNUSED, // 25 + KEYSYMUNUSED, // 26 + XK_P, // 27 + KEYSYMUNUSED, // 28 + KEYSYMUNUSED, // 29 + KEYSYMUNUSED, // 30 + KEYSYMUNUSED, // 31 + XK_0, // 32 + XK_9, // 33 + XK_O, // 34 + XK_I, // 35 + XK_L, // 36 + XK_K, // 37 + XK_M, // 38 + KEYSYMUNUSED, // 39 + XK_8, // 40 + XK_7, // 41 + XK_U, // 42 + XK_Y, // 43 + XK_H, // 44 + XK_J, // 45 + XK_N, // 46 + KEYSYMUNUSED, // 47 + XK_6, // 48 + XK_5, // 49 + XK_R, // 50 + XK_T, // 51 + XK_G, // 52 + XK_F, // 53 + XK_B, // 54 + XK_V, // 55 + XK_4, // 56 + XK_3, // 57 + XK_E, // 58 + XK_W, // 59 + XK_S, // 60 + XK_D, // 61 + XK_C, // 62 + XK_X, // 63 + XK_1, // 64 + XK_2, // 65 + XK_Escape, // 66 + XK_Q, // 67 + XK_Tab, // 68 + XK_A, // 69 + XK_Caps_Lock, // 70 + XK_Z, // 71 + KEYSYMUNUSED, // 72 + KEYSYMUNUSED, // 73 + KEYSYMUNUSED, // 74 + KEYSYMUNUSED, // 75 + KEYSYMUNUSED, // 76 + KEYSYMUNUSED, // 77 + KEYSYMUNUSED, // 78 + XK_BackSpace, // 79 +}; + +inline void keysymtoupper (KeySym & ks) +{ + KeySym discard; + // Convert to upper case. + XConvertCase (ks, & discard, & ks); +} + +void set_pressed (KeySym ks) +{ + keysymtoupper (ks); + if (ks >= 0 && ks <= MAXKEYSYM) + keypressedmap [ks]= 1; +} + +void reset_pressed (KeySym ks) +{ + keysymtoupper (ks); + if (ks >= 0 && ks <= MAXKEYSYM) + keypressedmap [ks]= 0; +} + +#endif + +#endif + +#ifdef BLASSIC_USE_X +const unsigned int MAXKEYCODE= 255; +#elif defined BLASSIC_USE_WINDOWS +const unsigned int MAXKEYCODE= 511; +#endif + +std::vector <bool> keycode_pressed (MAXKEYCODE + 1); + +#ifdef BLASSIC_USE_X + +const unsigned int NOUS= 0xFF; + +const unsigned int inkeytocode [MAXINKEYCODE + 1]={ + // The key symbols indicated are the corresponding + // to a spanish keyboard. + 0x62, // 0 Up + 0x66, // 1 Right + 0x68, // 2 Down + 0x51, // 3 Numeric 9 + 0x55, // 4 Numeric 6 + 0x59, // 5 Numeric 3 + 0x6C, // 6 Intro + 0x5B, // 7 Numeric . + 0x64, // 8 Left + 0x00, // 9 Copy + 0x4F, // 10 Numeric 7 + 0x50, // 11 Numeric 8 + 0x54, // 12 Numeric 5 + 0x57, // 13 Numeric 1 + 0x58, // 14 Numeric 2 + 0x5A, // 15 Numeric 0 + 0x6B, // 16 Delete + 0x23, // 17 + + 0x24, // 18 Return + 0x33, // 19 0x53, // 20 Numeric 4 + 0x3E, // 21 Shift + 0x5E, // 22 \ in the CPC, <> + 0x6D, // 23 Control + 0x15, // 24 0x14, // 25 ' + 0x22, // 26 `[ + 0x21, // 27 P + 0x30, // 28 '{ + 0x2F, // 29 0x3D, // 30 - + 0x3C, // 31 . + 0x13, // 32 0 + 0x12, // 33 9 + 0x20, // 34 O + 0x1F, // 35 I + 0x2E, // 36 L + 0x2D, // 37 K + 0x3A, // 38 M + 0x3B, // 39 , + 0x11, // 40 8 + 0x10, // 41 7 + 0x1E, // 42 U + 0x1D, // 43 Y + 0x2B, // 44 H + 0x2C, // 45 J + 0x39, // 46 N + 0x41, // 47 space + 0x0F, // 48 6 + 0x0E, // 49 5 + 0x1B, // 50 R + 0x1C, // 51 T + 0x2A, // 52 G + 0x29, // 53 F + 0x38, // 54 B + 0x37, // 55 V + 0x0D, // 56 4 + 0x0C, // 57 3 + 0x1A, // 58 E + 0x19, // 59 W + 0x27, // 60 S + 0x28, // 61 D + 0x36, // 62 C + 0x35, // 63 X + 0x0A, // 64 1 + 0x0B, // 65 2 + 0x09, // 66 Escape + 0x18, // 67 Q + 0x17, // 68 Tab + 0x26, // 69 A + 0x42, // 70 Caps lock + 0x34, // 71 Z + NOUS, // 72 joystick on cpc, unasigned + NOUS, // 73 joystick on cpc, unasigned + NOUS, // 74 joystick on cpc, unasigned + NOUS, // 75 joystick on cpc, unasigned + NOUS, // 76 joystick on cpc, unasigned + NOUS, // 77 joystick on cpc, unasigned + NOUS, // 78 Inexistent + 0x16, // 79 Backspace +}; + +#elif defined BLASSIC_USE_WINDOWS + +const unsigned int NOUS= 0x1FF; + +const unsigned int inkeytocode [MAXINKEYCODE + 1]={ + // The key symbols indicated are the corresponding + // to a spanish keyboard. + 0x008, // 0 Up + 0x00D, // 1 Right + 0x068, // 2 Down + 0x049, // 3 Numeric 9 + 0x04d, // 4 Numeric 6 + 0x051, // 5 Numeric 3 + 0x11C, // 6 Intro + 0x05B, // 7 Numeric . + 0x064, // 8 Left + 0x000, // 9 Copy + 0x047, // 10 Numeric 7 + 0x048, // 11 Numeric 8 + 0x04C, // 12 Numeric 5 + 0x04F, // 13 Numeric 1 + 0x050, // 14 Numeric 2 + 0x052, // 15 Numeric 0 + 0x153, // 16 Delete + 0x01B, // 17 + + 0x01C, // 18 Return + 0x02B, // 19 0x04B, // 20 Numeric 4 + 0x02A, // 21 Shift + 0x056, // 22 \ on the CPC, <> + 0x01D, // 23 Control + 0x00D, // 24 0x00C, // 25 ' + 0x01A, // 26 `[ + 0x019, // 27 P + 0x028, // 28 '{ + 0x027, // 29 0x035, // 30 - + 0x034, // 31 . + 0x00B, // 32 0 + 0x00A, // 33 9 + 0x018, // 34 O + 0x017, // 35 I + 0x026, // 36 L + 0x025, // 37 K + 0x032, // 38 M + 0x033, // 39 , + 0x009, // 40 8 + 0x008, // 41 7 + 0x016, // 42 U + 0x015, // 43 Y + 0x023, // 44 H + 0x024, // 45 J + 0x031, // 46 N + 0x039, // 47 space + 0x007, // 48 6 + 0x006, // 49 5 + 0x013, // 50 R + 0x014, // 51 T + 0x022, // 52 G + 0x021, // 53 F + 0x030, // 54 B + 0x02F, // 55 V + 0x005, // 56 4 + 0x004, // 57 3 + 0x012, // 58 E + 0x011, // 59 W + 0x01F, // 60 S + 0x020, // 61 D + 0x02E, // 62 C + 0x02D, // 63 X + 0x002, // 64 1 + 0x003, // 65 2 + 0x001, // 66 Escape + 0x010, // 67 Q + 0x00F, // 68 Tab + 0x01E, // 69 A + 0x03A, // 70 Caps lock + 0x02C, // 71 Z + NOUS, // 72 joystick on cpc, unasigned + NOUS, // 73 joystick on cpc, unasigned + NOUS, // 74 joystick on cpc, unasigned + NOUS, // 75 joystick on cpc, unasigned + NOUS, // 76 joystick on cpc, unasigned + NOUS, // 77 joystick on cpc, unasigned + NOUS, // 78 Inexistent + 0x00E, // 79 Backspace +}; + +#endif + +void keycode_press (unsigned int keycode) +{ + //std::cerr << "Pressed " << std::hex << std::setfill ('0') << + // std::setw (3) << keycode << std::endl; + if (keycode <= MAXKEYCODE) + keycode_pressed [keycode]= true; +} + +void keycode_release (unsigned int keycode) +{ + //std::cerr << "Released " << std::hex << std::setfill ('0') << + // std::setw (3) << keycode << std::endl; + if (keycode <= MAXKEYCODE) + keycode_pressed [keycode]= false; +} + +int keypressed (int keynum) +{ + if (keynum < 0 || keynum > static_cast <int> (MAXINKEYCODE) ) + return -1; + graphics::idle (); + //if (! keypressedmap [presscode [keynum] ] ) + // return -1; + + const int shiftpressed= 32, ctrlpressed= 128; + + #ifdef BLASSIC_USE_X + const unsigned int + shift_left= 0x32, shift_right= 0x3E, + control_left= 0x25, control_right= 0x6D; + #else + const unsigned int + shift_left= 0x2A, shift_right= 0x36, + control_left= 0x1D, control_right= 0x11D; + #endif + + bool shifted= keycode_pressed [shift_left] || + keycode_pressed [shift_right]; + bool controled= keycode_pressed [control_left] || + keycode_pressed [control_right]; + + bool pressed; + switch (keynum) { + case 21: + pressed= shifted; + break; + case 23: + pressed= controled; + break; + default: + pressed= keycode_pressed [inkeytocode [keynum] ]; + } + if (! pressed) + return -1; + int r= 0; + //if (keypressedmap [XK_Shift_L] || keypressedmap [XK_Shift_R] ) + if (shifted) + r|= shiftpressed; + //if (keypressedmap [XK_Control_L] || keypressedmap [XK_Control_R] ) + if (controled) + r|= ctrlpressed; + + return r; +} + +QueueKey queuekey; + +//#endif +//// BLASSIC_HAS_GRAPHICS + +bool inited= false; +bool window_created= false; +bool opaquemode= true; + +int xmousepos, ymousepos; + +const int text_mode= 0, user_mode= -1; + +bool graphics_mode_active= false; +int actualmode= text_mode; + +int screenwidth, screenheight; +int realwidth, realheight; +int originx= 0, originy= 0; + +bool limited= false; +int limit_minx, limit_miny, limit_maxx, limit_maxy; + +enum RotateMode { RotateNone, Rotate90 }; +RotateMode rotate= RotateNone; + +template <class C> +inline void do_rotate (C & x, C & y) +{ + switch (rotate) + { + case RotateNone: + // Nothing to do. + break; + case Rotate90: + { + //int newx= y; + C newx= y; + //y= screenheight - x; + y= static_cast <C> (screenwidth - x - 1); + x= newx; + } + break; + } +} + +template <class C> +inline void do_unrotate (C & x, C & y) +{ + switch (rotate) + { + case RotateNone: + // Nothing to do. + break; + case Rotate90: + { + //int newy= x; + C newy= x; + x= static_cast <C> (screenwidth - y - 1); + y= newy; + } + break; + } +} + +template <class C> +inline void do_rotate_rel (C & x, C & y) +{ + switch (rotate) + { + case RotateNone: + // Nothing to do. + break; + case Rotate90: + { + std::swap (x, y); + } + break; + } +} + +enum TransformType { TransformIdentity, TransformInvertY }; + +TransformType activetransform= TransformIdentity; + +inline void transform_x (int & x) +{ + x+= originx; +} + +inline int transform_inverse_x (int x) +{ + return x - originx; +} + +inline void adjust_y (int & y) +{ + switch (activetransform) + { + case TransformIdentity: + break; // Nothing to do + case TransformInvertY: + y= screenheight - 1 - y; + break; + } +} + +inline void transform_y (int & y) +{ + y+= originy; + adjust_y (y); +} + +inline int transform_inverse_y (int y) +{ + switch (activetransform) + { + case TransformIdentity: + break; // Nothing to do + case TransformInvertY: + y= screenheight - 1 - y; + break; + } + return y - originy; +} + +inline void set_origin (int x, int y) +{ + originx= x; + //adjust_y (y); + originy= y; +} + +void clear_limits () +{ + limited= false; +} + +void set_limits (int minx, int maxx, int miny, int maxy) +{ + limited= true; + if (minx > maxx) + std::swap (minx, maxx); + limit_minx= minx; + limit_maxx= maxx; + adjust_y (miny); + adjust_y (maxy); + if (miny > maxy) + std::swap (miny, maxy); + limit_miny= miny; + limit_maxy= maxy; + if (limit_minx <= 0 && limit_maxx >= screenwidth - 1 && + limit_miny <= 0 && limit_maxy >= screenheight - 1) + { + limited= false; + } +} + +inline bool check_limit (int x, int y) +{ + if (x < 0 || y < 0 || x >= screenwidth || y >= screenheight) + return false; + return (! limited) || (x >= limit_minx && x <= limit_maxx && + y >= limit_miny && y <= limit_maxy); +} + +#ifdef BLASSIC_USE_SVGALIB + +bool svgalib= false; + +#else + +//const bool svgalib= false; + +#endif + +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 + +//#ifdef BLASSIC_HAS_GRAPHICS + +int drawmode= drawmode_copy; + +// Numeric draw modes: +// 0: normal copy mode. +// 1: XOR +// 2: AND +// 3: OR +// 0 to 3 are Amstrad CPC modes. +// 4: INVERT, NOT. + +static int drawmodesbynumber []= { drawmode_copy, drawmode_xor, + drawmode_and, drawmode_or, drawmode_invert }; + +int getdrawmode (int mode) +{ + if (mode < 0 || size_t (mode) >= util::dim_array (drawmodesbynumber) ) + throw ErrFunctionCall; + return drawmodesbynumber [mode]; +} + +#ifdef BLASSIC_USE_WINDOWS + +static int bitbltmodesbynumber []= { SRCCOPY, SRCINVERT, + SRCAND, SRCPAINT, DSTINVERT }; + +int getbitbltmode (int mode) +{ + if (mode < 0 || + size_t (mode) >= util::dim_array (bitbltmodesbynumber) ) + { + throw ErrFunctionCall; + } + return bitbltmodesbynumber [mode]; +} + + +#endif +// BLASSIC_USE_WINDOWS + +//#endif +//// BLASSIC_HAS_GRAPHICS + +const int BASIC_COLORS= 16; + +const int LancelotsFavouriteColour= 0x0204FB; +// http://mindprod.com/unmainnaming.html + +bool colors_inited= false; + +struct ColorRGB { + int r; + int g; + int b; +}; + +const ColorRGB assignRGB []= { + { 0, 0, 0 }, + { 0, 0, 0xA8 }, + { 0, 0xA8, 0 }, + { 0, 0xA8, 0xA8 }, + { 0xA8, 0, 0 }, + { 0xA8, 0, 0xA8 }, + { 0xA8, 0x54, 0 }, + { 0xA8, 0xA8, 0xA8 }, + + { 0x54, 0x54, 0x54 }, + { 0x54, 0x54, 0xFF }, + { 0x54, 0xFF, 0x54 }, + { 0x54, 0xFF, 0xFF }, + { 0xFF, 0x54, 0x54 }, + { 0xFF, 0x54, 0xFF }, + { 0xFF, 0xFF, 0x54 }, + { 0xFF, 0xFF, 0xFF } +}; + +//#ifdef BLASSIC_HAS_GRAPHICS + +struct ColorInUse { + pcolor pc; + ColorRGB rgb; +}; + +#ifdef BLASSIC_USE_X + +ColorValue getColorValue (const ColorInUse & c) +{ + return c.pc->pixel; +} + +#elif defined BLASSIC_USE_WINDOWS + +ColorValue getColorValue (const ColorInUse & c) +{ + return RGB (c.rgb.r, c.rgb.g, c.rgb.b); +} + +#endif + +typedef std::map <int, ColorInUse> definedcolor_t; + +definedcolor_t definedcolor; + +ColorInUse tablecolors []= +{ + { &xcBlack, { 0, 0, 0} }, + { &xcBlue, { 0, 0, 0} }, + { &xcGreen, { 0, 0, 0} }, + { &xcCyan, { 0, 0, 0} }, + { &xcRed, { 0, 0, 0} }, + { &xcMagenta, { 0, 0, 0} }, + { &xcBrown, { 0, 0, 0} }, + { &xcLightGrey, { 0, 0, 0} }, + + { &xcDarkGrey, { 0, 0, 0} }, + { &xcLightBlue, { 0, 0, 0} }, + { &xcLightGreen, { 0, 0, 0} }, + { &xcLightCyan, { 0, 0, 0} }, + { &xcLightRed, { 0, 0, 0} }, + { &xcLightMagenta, { 0, 0, 0} }, + { &xcYellow, { 0, 0, 0} }, + { &xcWhite, { 0, 0, 0} } +}; + +inline ColorInUse & mapcolor (int color) +{ + if (color >= 0 && color < BASIC_COLORS) + return tablecolors [color]; + definedcolor_t::iterator it= definedcolor.find (color); + if (it != definedcolor.end () ) + return it->second; + return tablecolors [0]; +} + +inline ColorInUse & mapnewcolor (int color) +{ + if (color >= 0 && color < BASIC_COLORS) + return tablecolors [color]; + definedcolor_t::iterator it= definedcolor.find (color); + if (it != definedcolor.end () ) + return it->second; + ColorInUse n= { new color_t, { 0, 0, 0} }; + return definedcolor.insert (std::make_pair (color, n) ).first->second; +} + +void setink (int inknum, const ColorRGB & rgb) +{ + ColorInUse & ciu= mapnewcolor (inknum); + + #ifdef BLASSIC_USE_WINDOWS + + ASSERT (hdcPixmap); + COLORREF newcolor= + GetNearestColor (hdcPixmap, RGB (rgb.r, rgb.g, rgb.b) ); + ciu.rgb.r= GetRValue (newcolor); + ciu.rgb.g= GetGValue (newcolor); + ciu.rgb.b= GetBValue (newcolor); + HPEN newpen= CreatePen (PS_SOLID, 1, newcolor); + if (ciu.pc == pforeground) + { + //SelectObject (hdc, * ciu.pc); + //SelectObject (hdcPixmap, * ciu.pc); + SelectObject (hdc, newpen); + SelectObject (hdcPixmap, newpen); + } + if (* ciu.pc != NULL) + DeleteObject (* ciu.pc); + * ciu.pc= newpen; + + #elif defined BLASSIC_USE_X + + ciu.rgb= rgb; + ASSERT (display); + Colormap cm= DefaultColormap (display, screen); + XColor xc; + std::ostringstream namecolor; + namecolor << "rgb:" << std::hex << std::setfill ('0') << + std::setw (2) << rgb.r << '/' << + std::setw (2) << rgb.g << '/' << + std::setw (2) << rgb.b; + XColor newpen; + XAllocNamedColor (display, cm, + namecolor.str ().c_str (), & newpen, & xc); + if (ciu.pc == pforeground) + { + //XSetForeground (display, gcp, ciu.pc->pixel); + //XSetForeground (display, gc, ciu.pc->pixel); + XSetForeground (display, gcp, newpen.pixel); + XSetForeground (display, gc, newpen.pixel); + } + // Not sure if previous color needs to be freed and why. + * ciu.pc= newpen; + + #endif +} + +void setcpcink (int inknum, int cpccolor) +{ + // These rgb values are taken from screen captures + // of the WinAPE2 Amstrad CPC emulator. + static const ColorRGB cpctable []= { + { 0, 0, 0 }, // Black + { 0, 0, 96 }, // Blue + { 0, 0, 255 }, // Bright blue + { 96, 0, 0 }, // Red + { 96, 0, 96 }, // Magenta + { 96, 0, 255 }, // Mauve + { 255, 0, 0 }, // Bright red + { 255, 0, 96 }, // Purple + { 255, 0, 255 }, // Bright magenta + { 0, 103, 0 }, // Green + { 0, 103, 96 }, // Cyan + { 0, 103, 255 }, // Sky blue + { 96, 103, 0 }, // Yellow + { 96, 103, 96 }, // White + { 96, 103, 255 }, // Pastel blue + { 255, 103, 0 }, // Orange + { 255, 103, 96 }, // Pink + { 255, 103, 255 }, // Pastel magenta + { 0, 255, 0 }, // Bright green + { 0, 255, 96 }, // Sea green + { 0, 255, 255 }, // Bright cyan + { 96, 255, 0 }, // Lime green + { 96, 255, 96 }, // Pastel green + { 96, 255, 255 }, // Pastel cyan + { 255, 255, 0 }, // Bright yellow + { 255, 255, 96 }, // Pastel yellow + { 255, 255, 255 }, // Brigth white + }; + ASSERT (cpccolor >= 0 && + cpccolor < static_cast <int> ( util::dim_array (cpctable) ) ); + const ColorRGB & rgb= cpctable [cpccolor]; + + setink (inknum, rgb); +} + +void cpc_default_inks () +{ + static const int default_ink []= + { 1, 24, 20, 6, 26, 0, 2, 8, 10, 12, 14, 16, 18, 22, 1, 16 }; + // The last two are blinking on the CPC, we use the first color. + for (int i= 0; i < int (util::dim_array (default_ink) ); ++i) + //for (int i= 0; i < 16; ++i) + setcpcink (i, default_ink [i] ); +} + +void spectrum_inks () +{ + // Taken from a screen capture of the Spectaculator Spectrum Emulator. + static const ColorRGB spectrumtable []= + { + { 0, 0, 0 }, // Black + { 0, 0, 207 }, // Blue + { 207, 0, 0 }, // Red + { 207, 0, 207 }, // Magenta + { 0, 200, 0 }, // Green + { 0, 200, 207 }, // Cyan + { 207, 200, 0 }, // Yellow + { 207, 200, 207 }, // White + { 0, 0, 0 }, // Black bright + { 0, 0, 255 }, // Blue bright + { 255, 0, 0 }, // Red bright + { 255, 0, 255 }, // Magenta bright + { 0, 248, 0 }, // Green bright + { 0, 248, 255 }, // Cyan bright + { 255, 248, 0 }, // Yellow bright + { 255, 248, 255 }, // White bright + }; + for (int i= 0; i < 16; ++i) + setink (i, spectrumtable [i] ); +} + +enum Inkset { InkStandard, InkCpc, InkSpectrum } inkset= InkStandard; + +void init_colors () +{ + TRACEFUNC (tr, "init_colors"); + + ASSERT (sizeof (assignRGB) / sizeof (ColorRGB) == BASIC_COLORS); + ASSERT (sizeof (tablecolors) / sizeof (ColorInUse) == BASIC_COLORS); + + switch (inkset) + { + case InkStandard: + for (int i= 0; i < BASIC_COLORS; ++i) + { + const ColorRGB & rgb= assignRGB [i]; + setink (i, rgb); + } + break; + case InkCpc: + cpc_default_inks (); + break; + case InkSpectrum: + spectrum_inks (); + break; + } + colors_inited= true; + pforeground= mapcolor (default_pen).pc; + pbackground= mapcolor (default_paper).pc; +} + +void reinit_pixmap () +{ + #ifdef BLASSIC_USE_WINDOWS + + //RECT r = { 0, 0, screenwidth, screenheight }; + RECT r = { 0, 0, realwidth, realheight }; + FillRect (hdcPixmap, & r, (HBRUSH) GetStockObject (WHITE_BRUSH) ); + + #elif defined BLASSIC_USE_X + + XSetForeground (display, gcp, WhitePixel (display, screen) ); + XFillRectangle (display, pixmap, gcp, 0, 0, realwidth, realheight); + XSetForeground (display, gcp, BlackPixel (display, screen) ); + + #endif +} + +void reinit_window () +{ + #ifdef BLASSIC_USE_WINDOWS + + //BitBlt (hdc, 0, 0, screenwidth, screenheight, hdcPixmap, + BitBlt (hdc, 0, 0, realwidth, realheight, hdcPixmap, + 0, 0, SRCCOPY); + + #elif defined BLASSIC_USE_X + + XSetFunction (display, gc, drawmode_copy); + XCopyArea (display, pixmap, window, gc, + //0, 0, screenwidth, screenheight, 0, 0); + 0, 0, realwidth, realheight, 0, 0); + //XSetForeground (display, gc, BlackPixel (display, screen) ); + //XSetForeground (display, gc, pforeground->pixel); + XFlush (display); + XSetFunction (display, gc, drawmode); + + #endif +} + +void reinit_window (int x, int y, int width, int height) +{ + #ifdef BLASSIC_USE_WINDOWS + + BitBlt (hdc, x, y, width, height, hdcPixmap, + x, y, SRCCOPY); + + #elif defined BLASSIC_USE_X + + XSetFunction (display, gc, drawmode_copy); + XCopyArea (display, pixmap, window, gc, + x, y, width, height, x, y); + //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; + +inline unsigned int getkeycode (LPARAM lParam) +{ + return (lParam & 0x001FF0000) >> 16; +} + +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: + case WM_SYSKEYDOWN: + keycode_press (getkeycode (lParam) ); + { + WORD k= (WORD) wParam; + //std::string str= string_from_virtual_key (k); + switch (rotate) + { + case RotateNone: + // Nothing to do. + break; + case Rotate90: + switch (k) + { + case VK_LEFT: + k= VK_UP; break; + case VK_UP: + k= VK_RIGHT; break; + case VK_RIGHT: + k= VK_DOWN; break; + case VK_DOWN: + k= VK_LEFT; break; + } + break; + } + std::string str= string_from_key (k); + if (! str.empty () ) + { + queuekey.push (str); + return TRUE; + } + } + return FALSE; + case WM_KEYUP: + keycode_release (getkeycode (lParam) ); + return FALSE; +// case WM_SYSKEYDOWN: +// keycode_press (getkeycode (lParam) ); +// return FALSE; + case WM_SYSKEYUP: + keycode_release (getkeycode (lParam) ); + 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); + do_unrotate (xmousepos, ymousepos); + 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); + } +} + +class ProtectWindow { + HWND hwnd; +public: + ProtectWindow (HWND hwnd) : hwnd (hwnd) { } + ~ProtectWindow () + { + if (hwnd) + DestroyWindow (hwnd); + } + void release () + { + hwnd= 0; + } +}; + +class ProtectPixmap { + HBITMAP & pixmap; + bool active; +public: + ProtectPixmap (HBITMAP & pixmap) : pixmap (pixmap), active (true) { } + ~ProtectPixmap () + { + if (active) + { + DeleteObject (pixmap); + pixmap= NULL; + } + } + void release () + { + active= false; + } +}; + +void thread_create_window (int width, int height) +{ + window= CreateWindow ( + LPCTSTR (atomClass), + default_title.c_str (), + /*WS_VISIBLE | */ WS_SYSMENU | WS_MINIMIZEBOX, + 0, 0, + width + GetSystemMetrics (SM_CXDLGFRAME) * 2, + height + GetSystemMetrics (SM_CYDLGFRAME) * 2 + + GetSystemMetrics (SM_CYCAPTION), + NULL, + NULL, + GetModuleHandle (0), + 0); + if (window) + { + ProtectWindow pw (window); + hdc= GetDC (window); + if (hdc == NULL) + return; + pixmap= CreateCompatibleBitmap (hdc, width, height); + if (pixmap == NULL) + return; + ProtectPixmap pp (pixmap); + hdcPixmap= CreateCompatibleDC (hdc); + if (hdcPixmap == NULL) + return; + SelectObject (hdcPixmap, pixmap); + init_colors (); + reinit_pixmap (); + window_created= true; + ShowWindow (window, SW_SHOWNORMAL); + //SetActiveWindow (window); + SetForegroundWindow (window); + pw.release (); + pp.release (); + } +} + +// 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 destroying: " << + 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) + { + if (showdebuginfo () ) + cerr << "Error creating graphics thread" << 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) +{ + TRACEFUNC (tr, "create_window"); + + #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 communicating with graphics thread" + "GetLastError =" << GetLastError () << + endl; + destroy_thread (); + throw ErrBlassicInternal; + } + WaitForSingleObject (hEvent, INFINITE); + if (! window_created) + { + if (showdebuginfo () ) + cerr << "Error creating window" << endl; + throw ErrBlassicInternal; + } + + #elif defined 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 0 + int depth= DefaultDepth (display, DefaultScreen (display) ); + #else + unsigned int depth; + { + XWindowAttributes attr; + XGetWindowAttributes (display, window, & attr); + depth= attr.depth; + } + #endif + pixmap= XCreatePixmap (display, window, + width, height, depth); + pixmap_created= true; + + gc= XCreateGC (display, window, 0, & gcvalues); + gcp= XCreateGC (display, pixmap, 0, & gcvaluesp); + init_colors (); + reinit_pixmap (); + XSetStandardProperties (display, window, + default_title.c_str (), + default_title.c_str (), + None, + 0, 0, NULL); + + eventusedmaskactual= eventusedmask; + + if (xim) + { + XIMStyle input_style= XIMPreeditNothing | XIMStatusNothing; + xic= XCreateIC (xim, + XNInputStyle, input_style, + XNClientWindow, window, + XNFocusWindow, window, + NULL); + } + if (xic != NULL) + { + TRMESSAGE (tr, "XIC created"); + + long filterevents; + if (XGetICValues (xic, + XNFilterEvents, & filterevents, + NULL) + == NULL); + { + eventusedmaskactual |= filterevents; + } + } + else + { + TRMESSAGE (tr, "XIC not created"); + } + + XSelectInput (display, window, eventusedmaskactual); + XMapWindow (display, window); + + // Wait for window mapping. + { + XEvent e; + do + { + XNextEvent (display, & e); + } while (e.type != MapNotify); + } + + 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 (); + + #elif defined BLASSIC_USE_X + + XDestroyWindow (display, window); + window_created= false; + XFreePixmap (display, pixmap); + pixmap_created= false; + XFlush (display); + graphics::idle (); + + #endif +} + +#endif +// BLASSIC_HAS_GRAPHICS + +inline void requiregraphics () +{ + #ifndef BLASSIC_HAS_GRAPHICS + + no_graphics_support (); + + #else + + //if (actualmode == text_mode) + if (! graphics_mode_active) + throw ErrNoGraphics; + + #endif +} + +std::string program_name; + +#ifdef BLASSIC_USE_X + +std::string getDISPLAY () +{ + const char * strdisplay= getenv ("DISPLAY"); + if (strdisplay == NULL) + return std::string (); + else + return strdisplay; +} + +std::string last_display; + +#endif + +void initialize_graphics () +{ + TRACEFUNC (tr, "initialize_graphics"); + // Does the real initialization of the graphics system. + // It will not be called until a graphics mode ise + // established. + + #ifdef BLASSIC_HAS_GRAPHICS + + ASSERT (! inited); + + const bool showfailinfo= showdebuginfo (); + + #ifdef BLASSIC_USE_SVGALIB + + if (geteuid () == 0) { + std::string prog (program_name); + std::string::size_type l= prog.size (); + if (l > 3 && prog.substr (l - 3) == "vga") { + vga_init (); + inited= true; + svgalib= true; + return; + } + else + if (getuid () != 0) + seteuid (getuid () ); + } + #endif + + #ifdef BLASSIC_USE_X + + const char * strDisplay; + static const char WITHOUT_GRAPHICS []= + ", running without graphics support."; + + if ( (strDisplay= getenv ("DISPLAY") ) != NULL && + strDisplay [0] != '\0') + { + TRMESSAGE (tr, std::string ("Opening ") + strDisplay); + display= XOpenDisplay (0); + if (display) + { + last_display= strDisplay; + TRMESSAGE (tr, "Display opened"); + inited= true; + XSetLocaleModifiers (""); // Testing. + XSetLocaleModifiers ("@im=none"); // Testing. + xim= XOpenIM (display, NULL, NULL, NULL); + if (xim != NULL) + { + TRMESSAGE (tr, "XIM opened"); + } + screen= DefaultScreen (display); + //init_xcolors (); + //init_colors (); + } + else + { + static const char ERROR_OPEN []= + "Error opening DISPLAY '"; + TRMESSAGE (tr, std::string (ERROR_OPEN) + + strDisplay+ '\''); + if (showfailinfo) + cerr << ERROR_OPEN << + strDisplay << '\'' << + WITHOUT_GRAPHICS << + endl; + } + } + else + { + const char * const message= strDisplay ? "Empty" : "No"; + static const char DISPLAY []= " DISPLAY value"; + TRMESSAGE (tr, std::string (message) + DISPLAY); + if (showfailinfo) + cerr << message << DISPLAY << WITHOUT_GRAPHICS << + endl; + } + + #elif defined BLASSIC_USE_WINDOWS + + WNDCLASS wndclass; + wndclass.style= CS_NOCLOSE | CS_OWNDC; + wndclass.lpfnWndProc= windowproc; + wndclass.cbClsExtra= 0; + wndclass.cbWndExtra= 0; + wndclass.hInstance= GetModuleHandle (0); + wndclass.hIcon= 0; + wndclass.hCursor= LoadCursor (NULL, IDC_ARROW); + wndclass.hbrBackground= HBRUSH (GetStockObject (WHITE_BRUSH) ); + wndclass.lpszMenuName= 0; + wndclass.lpszClassName= program_name.c_str (); + atomClass= RegisterClass (& wndclass); + if (atomClass == 0) + { + if (showfailinfo) + cerr << "Error registering class" << endl; + } + else + { + inited= true; + //init_wincolors (); + //init_colors (); + } + hEvent= CreateEvent (NULL, FALSE, FALSE, NULL); + // Event automatic, initial nonsignaled + //create_thread (); + + #endif + + #else + // No BLASSIC_HAS_GRAPHICS + + #endif +} + +void destroy_text_windows (); + +void check_initialized () +{ + #ifdef BLASSIC_HAS_GRAPHICS + + if (! inited) + initialize_graphics (); + else + { + #ifdef BLASSIC_USE_X + if (last_display != getDISPLAY () ) + { + destroy_text_windows (); + graphics::setmode (text_mode); + graphics::uninitialize (); + initialize_graphics (); + } + #endif + } + + #endif +} + +} // namespace + +void graphics::initialize (const char * progname) +{ + TRACEFUNC (tr, "graphics::initialize"); + + // Default symbol after and charset initialization: + symbolafter (0); + + // Now complete initialization is not done here. + #if 1 + + program_name= progname; + + #else + + #ifdef BLASSIC_HAS_GRAPHICS + + ASSERT (! inited); + + const bool showfailinfo= showdebuginfo (); + + #ifdef BLASSIC_USE_SVGALIB + + if (geteuid () == 0) { + std::string prog (progname); + std::string::size_type l= prog.size (); + if (l > 3 && prog.substr (l - 3) == "vga") { + vga_init (); + inited= true; + svgalib= true; + return; + } + else + if (getuid () != 0) + seteuid (getuid () ); + } + #endif + + #ifdef BLASSIC_USE_X + + touch (progname); + const char * strDisplay; + static const char WITHOUT_GRAPHICS []= + ", running without graphics support."; + + if ( (strDisplay= getenv ("DISPLAY") ) != NULL && + strDisplay [0] != '\0') + { + TRMESSAGE (tr, std::string ("Opening ") + strDisplay); + display= XOpenDisplay (0); + if (display) + { + TRMESSAGE (tr, "Display opened"); + inited= true; + XSetLocaleModifiers (""); // Testing. + XSetLocaleModifiers ("@im=none"); // Testing. + xim= XOpenIM (display, NULL, NULL, NULL); + if (xim != NULL) + { + TRMESSAGE (tr, "XIM opened"); + } + screen= DefaultScreen (display); + //init_xcolors (); + //init_colors (); + } + else + { + static const char ERROR_OPEN []= + "Error opening DISPLAY '"; + TRMESSAGE (tr, std::string (ERROR_OPEN) + + strDisplay+ '\''); + if (showfailinfo) + cerr << ERROR_OPEN << + strDisplay << '\'' << + WITHOUT_GRAPHICS << + endl; + } + } + else + { + const char * const message= strDisplay ? "Empty" : "No"; + static const char DISPLAY []= " DISPLAY value"; + TRMESSAGE (tr, std::string (message) + DISPLAY); + if (showfailinfo) + cerr << message << DISPLAY << WITHOUT_GRAPHICS << + endl; + } + + #elif defined BLASSIC_USE_WINDOWS + + WNDCLASS wndclass; + wndclass.style= CS_NOCLOSE | CS_OWNDC; + wndclass.lpfnWndProc= windowproc; + wndclass.cbClsExtra= 0; + wndclass.cbWndExtra= 0; + wndclass.hInstance= GetModuleHandle (0); + wndclass.hIcon= 0; + wndclass.hCursor= LoadCursor (NULL, IDC_ARROW); + wndclass.hbrBackground= HBRUSH (GetStockObject (WHITE_BRUSH) ); + wndclass.lpszMenuName= 0; + wndclass.lpszClassName= progname; + atomClass= RegisterClass (& wndclass); + if (atomClass == 0) + { + if (showfailinfo) + cerr << "Error registering class" << endl; + } + else + { + inited= true; + //init_wincolors (); + //init_colors (); + } + hEvent= CreateEvent (NULL, FALSE, FALSE, NULL); + // Event automatic, initial nonsignaled + //create_thread (); + + #endif + + #else + // No BLASSIC_HAS_GRAPHICS + + touch (progname); + + #endif + + #endif +} + +void graphics::uninitialize () +{ + TRACEFUNC (tr, "graphics::uninitialize"); + + #ifdef BLASSIC_HAS_GRAPHICS + + if (! inited) return; + + //if (actualmode != 0) + if (graphics_mode_active) + setmode (0); + + #ifdef BLASSIC_USE_SVGA + #if 0 + if (svgalib) + //vga_setmode (TEXT); + setmode (0); + #endif + #endif + + #ifdef BLASSIC_USE_X + + if (display) + { + clear_images (); + TRMESSAGE (tr, "closing display"); + //if (window_created) + // destroy_window (); + if (xic) + XDestroyIC (xic); + if (xim) + XCloseIM (xim); + XCloseDisplay (display); + TRMESSAGE (tr, "display is closed"); + display= 0; + } + + #elif defined BLASSIC_USE_WINDOWS + + //destroy_thread (); + //if (window_created) + // destroy_window (); + + if (atomClass) + UnregisterClass (LPCTSTR (atomClass), + GetModuleHandle (0) ); + #endif + + inited= false; + + #endif + // BLASSIC_HAS_GRAPHICS +} + +void graphics::origin (int x, int y) +{ + TRACEFUNC (tr, "graphics::origin"); + + #ifdef BLASSIC_HAS_GRAPHICS + + set_origin (x, y); + + #else + + touch (x, y); + no_graphics_support (); + + #endif +} + +void graphics::limits (int minx, int maxx, int miny, int maxy) +{ + TRACEFUNC (tr, "graphics::limits"); + + #ifdef BLASSIC_HAS_GRAPHICS + + set_limits (minx, maxx, miny, maxy); + + #else + + touch (minx, maxx, miny, maxy); + no_graphics_support (); + + #endif +} + +void graphics::ink (int inknum, int cpccolor) +{ + requiregraphics (); + + // Check not needed, is done in setcpcink. + //if (cpccolor < 0 || cpccolor > 26) + // throw ErrFunctionCall; + + #ifdef BLASSIC_HAS_GRAPHICS + + setcpcink (inknum, cpccolor); + + #else + + touch (inknum, cpccolor); + + #endif +} + +void graphics::ink (int inknum, int r, int g, int b) +{ + requiregraphics (); + + #ifdef BLASSIC_HAS_GRAPHICS + + ColorRGB rgb= { r, g, b }; + setink (inknum, rgb); + + #else + + touch (inknum, r, g, b); + + #endif +} + +void graphics::clearink () +{ + requiregraphics (); + + #ifdef BLASSIC_HAS_GRAPHICS + + init_colors (); + + #endif +} + +namespace { + +#ifdef BLASSIC_HAS_GRAPHICS + +#ifdef BLASSIC_USE_X + +void keypress (XKeyPressedEvent & xk) +{ + TRACEFUNC (tr, "keypress"); + + KeySym ks= 0; + const int STRBUFSIZE= 500; // Value used in xterm. + char buffer [STRBUFSIZE]; + int r; + if (xic != NULL) + { + Status status; + r= XmbLookupString (xic, & xk, buffer, STRBUFSIZE, + & ks, & status); + } + else + { + r= XLookupString (& xk, buffer, STRBUFSIZE - 1, + & ks, NULL); + } + + // Change cursor keys if rotated. + switch (rotate) + { + case RotateNone: + // Nothing to do. + break; + case Rotate90: + switch (ks) + { + case XK_Left: + ks= XK_Up; break; + case XK_Up: + ks= XK_Right; break; + case XK_Right: + ks= XK_Down; break; + case XK_Down: + ks= XK_Left; break; + + case XK_KP_Left: + ks= XK_KP_Up; break; + case XK_KP_Up: + ks= XK_KP_Right; break; + case XK_KP_Right: + ks= XK_KP_Down; break; + case XK_KP_Down: + ks= XK_KP_Left; break; + } + break; + } + #ifndef NDEBUG + { + std::ostringstream oss; + //oss << std::hex << std::setw (4) << ks; + oss << std::hex << std::setw (4) << xk.keycode; + //if (r > 0) + oss << " CHAR: " << buffer [0]; + TRMESSAGE (tr, oss.str () ); + } + #endif + //set_pressed (ks); + + #if 0 + std::string str= string_from_key (ks); + #else + + // I don't know why XmbLookupString does not return + // the Euro sign as string, this is a dirty solution. + std::string str; + #ifdef XK_EuroSign + if (ks == XK_EuroSign) + { + const char eurochar (164); + str= std::string (1, eurochar); + } + else + + #endif + str= string_from_key (ks); + #endif + + if (! str.empty () ) + { + TRMESSAGE (tr, std::string ("key") + str); + queuekey.push (str); + } + else + if (r > 0) + { + TRMESSAGE (tr, std::string ("key: ") + + std::string (buffer, r) ); + queuekey.push (std::string (buffer, r) ); + } +} + +void keyrelease (XKeyReleasedEvent & xk) +{ + KeySym ks= 0; + const int STRBUFSIZE= 500; // Value used in xterm. + char buffer [STRBUFSIZE]; + int r; + if (xic == NULL) + { + r= XLookupString (& xk, buffer, STRBUFSIZE - 1, + & ks, NULL); + } + + // Change cursor keys if rotated. + switch (rotate) + { + case RotateNone: + // Nothing to do. + break; + case Rotate90: + switch (ks) + { + case XK_Left: + ks= XK_Up; break; + case XK_Up: + ks= XK_Right; break; + case XK_Right: + ks= XK_Down; break; + case XK_Down: + ks= XK_Left; break; + + case XK_KP_Left: + ks= XK_KP_Up; break; + case XK_KP_Up: + ks= XK_KP_Right; break; + case XK_KP_Right: + ks= XK_KP_Down; break; + case XK_KP_Down: + ks= XK_KP_Left; break; + } + break; + } + //reset_pressed (ks); +} + +void process_event (XEvent & x_event, bool & do_copy) +{ + // Set the state of pressed keys before checking XFilterEvent. + switch (x_event.type) + { + case KeyPress: + keycode_press (x_event.xkey.keycode); + break; + case KeyRelease: + keycode_release (x_event.xkey.keycode); + break; + } + + if (XFilterEvent (& x_event, window) ) + return; + + switch (x_event.type) + { + case Expose: + do_copy= true; + break; + case KeyPress: + keypress (x_event.xkey); + break; + case KeyRelease: + keyrelease (x_event.xkey); + break; + case ButtonPress: + { + XButtonEvent & xbpe= x_event.xbutton; + //cerr << "ButtonPress event, button=" << + // xbpe.button << + // endl; + switch (xbpe.button) + { + case 1: + queuekey.push (strCLICK); + break; + case 3: + queuekey.push (strSCLICK); + break; + default: + ; + } + } + break; + case ButtonRelease: + { + XButtonEvent & xbpe= x_event.xbutton; + //cerr << "ButtonRelease event, button=" << + // xbpe.button << + // endl; + switch (xbpe.button) + { + case 1: + queuekey.push (strRELEASE); + break; + case 3: + queuekey.push (strSRELEASE); + break; + default: + ; + } + } + break; + case MotionNotify: + { + XMotionEvent & xme= x_event.xmotion; + //cerr << "MotionNotify event " << + // xme.x << ", " << xme.y << + // endl; + xmousepos= xme.x; + ymousepos= xme.y; + do_unrotate (xmousepos, ymousepos); + } + break; + case EnterNotify: + { + XCrossingEvent & xce= x_event.xcrossing; + //cerr << "EnterNotify event" << endl; + xmousepos= xce.x; + ymousepos= xce.y; + do_unrotate (xmousepos, ymousepos); + } + break; + default: + //cerr << "Another event." << endl; + ; + } // switch type of event +} + +void wait_X_event () +{ + ASSERT (window_created); + + XEvent x_event; + bool do_copy= false; + XWindowEvent (display, window, eventusedmaskactual, & x_event); + process_event (x_event, do_copy); + if (do_copy && pixmap_created) + reinit_window (); +} + +#endif +// BLASSIC_USE_X + +#endif +// BLASSIC_HAS_GRAPHICS + +} // namespace + +//void graphics::idle () +void blassic::idle () +{ + #ifdef BLASSIC_HAS_GRAPHICS + + if (! window_created) + return; + + #ifdef BLASSIC_USE_X + + XEvent x_event; + bool do_copy= false; + while (XCheckWindowEvent (display, window, + eventusedmaskactual, & x_event) ) + { + process_event (x_event, do_copy); + } // while + if (do_copy && pixmap_created) + { + #if 0 + XCopyArea (display, pixmap, window, gc, + 0, 0, screenwidth, screenheight, 0, 0); + XFlush (display); + #else + //XSetFunction (display, gc, drawmode_copy); + reinit_window (); + //XSetFunction (display, gc, drawmode); + #endif + //cerr << "Copied." << endl; + } + + #endif + + #ifdef BLASSIC_USE_WINDOWS + //UpdateWindow (window); + //Sleep (0); + #endif + + #endif + // BLASSIC_HAS_GRAPHICS +} + +#ifdef BLASSIC_HAS_GRAPHICS + +namespace { + +void setactivecolor (pcolor pxc) +{ + activecolor= pxc; + + #ifdef BLASSIC_USE_X + + XSetForeground (display, gcp, pxc->pixel); + XSetForeground (display, gc, pxc->pixel); + + #elif defined BLASSIC_USE_WINDOWS + + SelectObject (hdc, * pxc); + SelectObject (hdcPixmap, *pxc); + + #endif +} + +void textscroll () +{ + #ifdef BLASSIC_USE_SVGALIB + if (svgalib) + { + // PENDIENTE + return; + } + #endif + + #ifdef BLASSIC_USE_X + + int h= screenheight - 8; + //unsigned long white= WhitePixel (display, screen), + // black= BlackPixel (display, screen); + + XSetFunction (display, gcp, drawmode_copy); + XCopyArea (display, pixmap, pixmap, gcp, + 0, 8, screenwidth, h, 0, 0); + setactivecolor (pbackground); + XFillRectangle (display, pixmap, gcp, + 0, h, screenwidth, 8); + setactivecolor (pforeground); + XSetFunction (display, gcp, drawmode); + + if (! fSynchro) + reinit_window (); + + #elif defined BLASSIC_USE_WINDOWS + + RECT r = { 0, screenheight - 8, screenwidth, screenheight }; + + int h= screenheight - 8; + BitBlt (hdcPixmap, 0, 0, screenwidth, h, + hdcPixmap, 0, 8, SRCCOPY); + //HBRUSH hbrush= (HBRUSH) GetStockObject (WHITE_BRUSH); + LOGPEN logpen; + GetObject (* pbackground, sizeof (LOGPEN), & logpen); + HBRUSH hbrush= CreateSolidBrush (logpen.lopnColor); + FillRect (hdcPixmap, & r, hbrush); + DeleteObject (hbrush); + if (! fSynchro) + reinit_window (); + + #endif + +} + +void do_fill_rectangle (int x1, int y1, int x2, int y2, bool limitable) +{ + using std::min; + using std::max; + if (limitable && limited) + { + x1= max (x1, limit_minx); + y1= max (y1, limit_miny); + x2= min (x2, limit_maxx); + y2= min (y2, limit_maxy); + if (x1 > limit_maxx || x2 < limit_minx || + y1 > limit_maxy || y2 < limit_miny) + return; + } + + do_rotate (x1, y1); + do_rotate (x2, y2); + if (x1 > x2) + std::swap (x1, x2); + if (y1 > y2) + std::swap (y1, y2); + + #ifdef BLASSIC_USE_WINDOWS + + RECT r = { x1, y1, x2 + 1, y2 + 1 }; + LOGPEN logpen; + //GetObject (* pforeground, sizeof (LOGPEN), & logpen); + GetObject (* activecolor, sizeof (LOGPEN), & logpen); + HBRUSH hbrush= CreateSolidBrush (logpen.lopnColor); + FillRect (hdcPixmap, & r, hbrush); + if (! fSynchro) + FillRect (hdc, & r, hbrush); + DeleteObject (hbrush); + + #elif defined BLASSIC_USE_X + + //int w= std::abs (x2 - x1) + 1; + //int h= std::abs (y2 - y1) + 1; + int w= x2 - x1 + 1; + int h= y2 - y1 + 1; + XFillRectangle (display, pixmap, gcp, + x1, y1, w, h); + if (! fSynchro) + XFillRectangle (display, window, gc, + x1, y1, w, h); + + #endif +} + +#ifdef BLASSIC_USE_WINDOWS + +// Define in windows the struct used in X for XDrawPoints. +struct XPoint { + short x, y; +}; + +#endif + +inline void do_plot (int x, int y) +{ + if (! check_limit (x, y) ) + return; + + do_rotate (x, y); + + #ifdef BLASSIC_USE_SVGALIB + + if (svgalib) + { + vga_drawpixel (x, y); + return; + } + + #endif + + #ifdef BLASSIC_USE_X + + if (! fSynchro) + XDrawPoint (display, window, gc, x, y); + XDrawPoint (display, pixmap, gcp, x, y); + + #elif defined BLASSIC_USE_WINDOWS + + #ifdef USE_POLY + POINT p [2]= { {x, y}, {x + 1, y} }; + #endif + + if (! fSynchro) + { + #ifdef USE_POLY + Polyline (hdc, p, 2); + #else + MoveToEx (hdc, x, y, 0); + LineTo (hdc, x + 1, y); + #endif + } + #ifdef USE_POLY + Polyline (hdcPixmap, p, 2); + #else + MoveToEx (hdcPixmap, x, y, 0); + LineTo (hdcPixmap, x + 1, y); + #endif + + #endif +} + +// This are now not used, every text window has his own +//int tcol, trow; + +int maxtcol= 40, maxtrow= 25; + +const int MAXZOOMTEXTY= 4; +const int MAXZOOMTEXTX= 4; +//const int MAXZOOMTEXT= std::max (MAXZOOMTEXTX, MAXZOOMTEXTY); +// Borland C++ can't evaluate this at compile time. +const int MAXZOOMTEXT= 4; + +// Default values are needed by the initialization of windowzero. +int zoomtextx= 1, zoomtexty= 1; +//int zoomtextxrot= 1, zoomtextyrot= 1; +int charwidth= 8, charheight= 8; +//int charwidthrot= 9, charheightrot= 8; + +#ifdef BLASSIC_USE_WINDOWS + +// Length of each segment: all are 2 points. +DWORD poly_points [64 * MAXZOOMTEXT]; +bool init_poly_points () +{ + std::fill_n (poly_points, util::dim_array (poly_points), 2); + return true; +} +bool poly_points_inited= init_poly_points (); + +#endif + +inline void do_plot_points (XPoint point [], int npoints, bool limitable) +{ + ASSERT (npoints < 64 * zoomtexty); + + if (rotate != RotateNone) + for (int i= 0; i < npoints; ++i) + do_rotate (point [i].x, point [i].y); + + #ifdef BLASSIC_USE_X + + if (zoomtextx == 1) + { + XDrawPoints (display, pixmap, gcp, + point, npoints, CoordModeOrigin); + if (! fSynchro) + XDrawPoints (display, window, gc, + point, npoints, CoordModeOrigin); + } + else + { + // 64 points each char * max zoomtexty + XSegment seg [64 * MAXZOOMTEXT]; + int inc= zoomtextx - 1; + int xinc= 0, yinc= 0; + switch (rotate) + { + case RotateNone: + xinc= inc; break; + case Rotate90: + yinc= -inc; break; + } + for (int i= 0; i < npoints; ++i) + { + int xpos= point [i].x; + int ypos= point [i].y; + seg [i].x1= xpos; + seg [i].y1= ypos; + //xpos+= zoomtextx - 1; + xpos+= xinc; + ypos+= yinc; + if (limitable && limited) + { + xpos= std::min (xpos, limit_maxx); + ypos= std::max (ypos, limit_miny); + } + seg [i].x2= xpos; + seg [i].y2= ypos; + } + XDrawSegments (display, pixmap, gcp, seg, npoints); + if (! fSynchro) + XDrawSegments (display, window, gc, seg, npoints); + } + + #endif + + #ifdef BLASSIC_USE_WINDOWS + + // The PolyPolyline is a bit faster than doing each + // point separately. + // 64 * 2 points by segment * max zoomtexty + static POINT p [64 * 2 * MAXZOOMTEXT]; + // 64 points each char * max zoomtexty + + int inc= zoomtextx; + //if (zoomtextx > 1) + // ++inc; + int xinc= 0, yinc= 0; + switch (rotate) + { + case RotateNone: + xinc= inc; break; + case Rotate90: + yinc= -inc; break; + } + for (int i= 0; i < npoints; ++i) + { + int xpos= point [i].x; + int ypos= point [i].y; + p [2 * i].x= xpos; + p [2 * i].y= ypos; + //xpos+= zoomtextxrot; + //if (zoomtextxrot > 1) ++xpos; + xpos+= xinc; + ypos+= yinc; + if (limitable && limited) + { + xpos= std::min (xpos, limit_maxx); + ypos= std::max (ypos, limit_miny); + } + p [2 * i + 1].x= xpos; + p [2 * i + 1].y= ypos; + } + if (! fSynchro) + PolyPolyline (hdc, p, poly_points, npoints); + PolyPolyline (hdcPixmap, p, poly_points, npoints); + + #endif +} + +void printxy (int x, int y, unsigned char ch, + bool limitable, bool inverse= false, bool underline= false) +{ + TRACEFUNC (tr, "printxy"); + + static unsigned char mask [8]= { 128, 64, 32, 16, 8, 4, 2, 1 }; + + //charset::chardata & data= charset::data [ch]; + + charset::chardata data; + memcpy (data, charset::data [ch], sizeof (charset::chardata) ); + if (underline) + data [7]= 255; + + XPoint point [64 * MAXZOOMTEXT]; // 64 pixels * max zoom height + int n= 0, npoints= 0; + for (int i= 0, yi= y; i < 8; ++i, yi+= zoomtexty) + { + unsigned char c= data [i]; + if (inverse) + c= static_cast <unsigned char> (~ c); + + // Little optimization: + if (c == 0) + continue; + + for (int j= 0, xj= x; j < 8; ++j, xj+= zoomtextx) + { + if (c & mask [j] ) + { + for (int z= 0; z < zoomtexty; ++z) + { + int y= yi + z; + if (! limitable || + check_limit (xj, y) ) + { + point [n].x= static_cast + <short> (xj); + point [n].y= static_cast + <short> (y); + ++n; + } + } + ++npoints; + } + } + } + if (npoints < 64) + { + if (opaquemode) + { + setactivecolor (pbackground); + do_fill_rectangle (x, y, + x + charwidth - 1, y + charheight - 1, + limitable); + } + if (n > 0) + { + setactivecolor (pforeground); + do_plot_points (point, n, limitable); + } + else + setactivecolor (pforeground); + } + else + { + setactivecolor (pforeground); + do_fill_rectangle (x, y, + x + charwidth - 1, y + charheight - 1, + limitable); + } +} + +inline void print (int col, int row, unsigned char ch, + bool inverse, bool underline= false) +{ + printxy (col * charwidth, row * charheight, ch, false, + inverse, underline); +} + +void setmaxtext () +{ + charwidth= 8 * zoomtextx; + charheight= 8 * zoomtexty; + //charwidthrot= charwidth; + //charheightrot= charheight; + //do_rotate_rel (charwidthrot, charheightrot); + + //maxtcol= screenwidth / charwidthrot; + //maxtrow= screenheight / charheightrot; + maxtcol= screenwidth / charwidth; + maxtrow= screenheight / charheight; + + #if 0 + switch (rotate) + { + case RotateNone: + break; + case Rotate90: + std::swap (maxtcol, maxtrow); + break; + } + #endif +} + +void recreate_windows (); + +void set_mode (int width, int height, int mode, int charx= 1, int chary= 1) +{ + TRACEFUNC (tr, "set_mode"); + + ASSERT (charx >= 1 && charx <= MAXZOOMTEXTX); + ASSERT (chary >= 1 && chary <= MAXZOOMTEXTY); + + std::ostringstream oss; + oss << "Width " << short (width) << ", height " << short (height); + TRMESSAGE (tr, oss.str () ); + + if (mode != 0 && (width <= 0 || height <= 0) ) + throw ErrImproperArgument; + + if (mode != text_mode) + { + check_initialized (); + if (! inited) + { + if (showdebuginfo () ) + cerr << "Graphics system not initialized" << + endl; + throw ErrFunctionCall; + } + } + + sysvar::set16 (sysvar::GraphicsWidth, short (width) ); + sysvar::set16 (sysvar::GraphicsHeight, short (height) ); + screenwidth= width; + screenheight= height; + switch (sysvar::get (sysvar::GraphRotate) ) + { + case 0: + rotate= RotateNone; + // Nothing to do. + break; + case 1: + rotate= Rotate90; + // Rotate 90 degrees. + std::swap (width, height); + break; + default: + throw ErrFunctionCall; + } + realwidth= width; + realheight= height; + activetransform= TransformIdentity; + set_origin (0, 0); + fSynchro= false; + zoomtextx= charx; + zoomtexty= chary; + //zoomtextxrot= zoomtextx; + //zoomtextyrot= zoomtextyrot; + //do_rotate_rel (zoomtextxrot, zoomtextyrot); + clear_limits (); + + #ifdef BLASSIC_USE_SVGALIB + + if (svgalib) { + if (mode == user_mode) + throw ErrFunctionCall; + if (actualmode != text_mode) + { + free (font); + } + vga_setmode (mode); + if (mode != text_mode) { + setmaxtext (); + gl_setcontextvga (mode); + //font= new char [256 * 8 * 8 * BYTESPERPIXEL]; + font= (char *) + malloc (2 * 256 * 8 * 8 * BYTESPERPIXEL); + gl_expandfont (8, 8, 15, gl_font8x8, font); + gl_setfont (8, 8, font); + //cout << "Listo" << endl; + graphics_mode_active= true; + } + else + graphics_mode_active= false; + actualmode= mode; + return; + } + + #endif + + if (mode != actualmode || mode == user_mode) + { + { + std::ostringstream oss; + oss << "Changing mode from " << actualmode << + " to " << mode; + TRMESSAGE (tr, oss.str () ); + } + if (window_created) + destroy_window (); + if (mode != text_mode) + { + create_window (width, height); + graphics_mode_active= true; + } + else + graphics_mode_active= false; + } + else + { + if (mode != text_mode) + { + //graphics::setcolor (0); + graphics::setdrawmode (0); + reinit_pixmap (); + reinit_window (); + } + + #ifdef _Windows + else + if (actualmode != text_mode) + { + // REVISAR: ESTO NO PARECE VALIDO. + destroy_thread (); + graphics_mode_active= false; + } + #endif + } + + actualmode= mode; + + if (mode != text_mode) + { + //if (! colors_inited) + // init_colors (); + setmaxtext (); + //tcol= trow= 0; + lastx= lasty= 0; + + #if defined (BLASSIC_USE_WINDOWS) || defined (BLASSIC_USE_X) + //pforeground= default_foreground; + //pbackground= default_background; + #endif + + recreate_windows (); + } +} + +} // namespace + +#endif + +void graphics::cls () +{ + TRACEFUNC (tr, "graphics::cls"); + + requiregraphics (); + + #ifdef BLASSIC_HAS_GRAPHICS + + #ifdef BLASSIC_USE_SVGALIB + + if (svgalib) + { + // PENDIENTE + return; + } + + #endif + + //tcol= trow= 0; + //reinit_pixmap (); + + #if 0 + + int x1, y1, width, height; + if (limited) + { + TRMESSAGE (tr, "lmited"); + x1= limit_minx; width= limit_maxx - limit_minx + 1; + y1= limit_miny; height= limit_maxy - limit_miny + 1; + } + else + { + TRMESSAGE (tr, "unlimited"); + x1= 0; width= screenwidth; + y1= 0; height= screenheight; + } + + #ifdef BLASSIC_USE_WINDOWS + + //RECT r= { 0, 0, screenwidth, screenheight }; + RECT r= { x1, y1, x1 + width + 1, y1 + height + 1 }; + LOGPEN logpen; + GetObject (* pbackground, sizeof (LOGPEN), & logpen); + HBRUSH hbrush= CreateSolidBrush (logpen.lopnColor); + if (! fSynchro) + FillRect (hdc, & r, hbrush); + FillRect (hdcPixmap, & r, hbrush); + DeleteObject (hbrush); + + #elif defined BLASSIC_USE_X + + setactivecolor (pbackground); + XSetFunction (display, gcp, drawmode_copy); + XFillRectangle (display, pixmap, gcp, + //0, 0, screenwidth, screenheight); + x1, y1, width, height); + XSetFunction (display, gcp, drawmode); + if (! fSynchro) + { + XSetFunction (display, gc, drawmode_copy); + XFillRectangle (display, window, gc, + //0, 0, screenwidth, screenheight); + x1, y1, width, height); + XSetFunction (display, gc, drawmode); + // Inserted an idle call because without it + // the window sometimes is not updated. + graphics::idle (); + } + setactivecolor (pforeground); + + #endif + + #else + + // Limit control is done by do_fill_rectangle. + setactivecolor (pbackground); + do_fill_rectangle (0, 0, screenwidth - 1, screenheight - 1, true); + setactivecolor (pforeground); + + #endif + + #endif + // BLASSIC_HAS_GRAPHICS +} + +void graphics::setmode (int width, int height, bool inverty, + int zoomx, int zoomy) +{ + TRACEFUNC (tr, "graphics::setmode"); + + #ifndef BLASSIC_HAS_GRAPHICS + + touch (width, height, inverty, zoomx, zoomy); + no_graphics_support (); + + #else + + #if 0 + if (! inited) + { + if (showdebuginfo () ) + cerr << "Graphics system not initialized" << endl; + throw ErrFunctionCall; + } + #endif + + if (zoomx < 1 || zoomx > MAXZOOMTEXTX) + throw ErrImproperArgument; + if (zoomy < 1 || zoomy > MAXZOOMTEXTY) + throw ErrImproperArgument; + + inkset= InkStandard; + //default_foreground= & xcBlack; + //default_background= & xcWhite; + default_pen= 0; + default_paper= 15; + ::set_mode (width, height, user_mode, zoomx, zoomy); + if (inverty) + activetransform= TransformInvertY; + + #endif +} + +void graphics::setmode (int mode) +{ + TRACEFUNC (tr, "graphics::setmode"); + + #ifndef BLASSIC_HAS_GRAPHICS + + no_graphics_support (); + touch (mode); + + #else + + #if 0 + if (! inited && mode != text_mode) + { + if (showdebuginfo () ) + cerr << "Graphics system not initialized" << endl; + throw ErrFunctionCall; + } + #endif + + int width, height; + switch (mode) + { + case 1: + width= 320; height= 200; break; + case 2: + width= 640; height= 200; break; + case 5: + width= 320; height= 200; break; + case 10: + width= 640; height= 480; break; + case 11: + width= 800; height= 600; break; + default: + width= 0; height= 0; + } + if (mode != 0 && width == 0) + { + if (showdebuginfo () ) + cerr << "Invalid mode number " << mode << endl; + throw ErrFunctionCall; + } + + //default_foreground= & xcBlack; + //default_background= & xcWhite; + if (mode != text_mode) + { + inkset= InkStandard; + default_pen= 0; + default_paper= 15; + } + ::set_mode (width, height, mode); + + #endif +} + +#ifdef BLASSIC_HAS_GRAPHICS + +namespace { + +struct SpecialMode { + std::string name; + Inkset inkset; + int pen; + int paper; + int width; + int height; + TransformType transform; + int zoomx; + int zoomy; + SpecialMode (const std::string & name, Inkset inkset, + int pen, int paper, int width, int height, + TransformType transform, int zoomx, int zoomy) : + name (name), inkset (inkset), + pen (pen), paper (paper), width (width), height (height), + transform (transform), zoomx (zoomx), zoomy (zoomy) + { } +}; + +const SpecialMode specialmodes []= { + SpecialMode ("spectrum", InkSpectrum, 0, 7, 256, 192, + TransformInvertY, 1, 1), + SpecialMode ("cpc0", InkCpc, 1, 0, 640, 400, + TransformInvertY, 4, 2), + SpecialMode ("cpc1", InkCpc, 1, 0, 640, 400, + TransformInvertY, 2, 2), + SpecialMode ("cpc2", InkCpc, 1, 0, 640, 400, + TransformInvertY, 1, 2), + SpecialMode ("pcw", InkStandard, 0, 15, 720, 248, + TransformIdentity, 1, 1), + SpecialMode ("pcw2", InkStandard, 0, 15, 720, 496, + TransformIdentity, 1, 2), +}; + +} // namespace + +#endif +// BLASSIC_HAS_GRAPHICS + +void graphics::setmode (const std::string & mode) +{ + TRACEFUNC (tr, "graphics::setmode (string)"); + TRMESSAGE (tr, "mode: " + mode); + + #ifndef BLASSIC_HAS_GRAPHICS + + no_graphics_support (); + touch (mode); + + #else + + #if 0 + if (! inited) + throw ErrFunctionCall; + #endif + + for (size_t i= 0; i < util::dim_array (specialmodes); ++i) + { + const SpecialMode & m= specialmodes [i]; + if (m.name == mode) + { + inkset= m.inkset; + default_pen= m.pen; + default_paper= m.paper; + ::set_mode (m.width, m.height, user_mode, + m.zoomx, m.zoomy); + activetransform= m.transform; + // Spectrum is an special case. + if (mode == "spectrum") + { + set_origin (0, 16); + set_limits (0, 256, 16, 192); + } + return; + } + } + + TRMESSAGE (tr, "Invalid mode"); + if (showdebuginfo () ) + cerr << '\'' << mode << "' is not a valid graphics mode" << + endl; + throw ErrFunctionCall; + + #endif +} + +bool graphics::ingraphicsmode () +{ + #ifdef BLASSIC_HAS_GRAPHICS + + //return actualmode != text_mode; + return graphics_mode_active; + + #else + + return false; + + #endif +} + +void graphics::setcolor (int color) +{ + requiregraphics (); + + #ifdef BLASSIC_HAS_GRAPHICS + + #ifdef BLASSIC_USE_SVGALIB + + if (svgalib) { + vga_setcolor (color); + return; + } + + #endif + + graphics_pen= color; + pcolor pxc= mapcolor (color).pc; + + setactivecolor (pxc); + + #if defined (BLASSIC_USE_WINDOWS) || defined (BLASSIC_USE_X) + pforeground= pxc; + #endif + + #else + // No BLASSIC_HAS_GRAPHICS + + touch (color); + + #endif +} + +int graphics::getcolor () +{ + #ifdef BLASSIC_HAS_GRAPHICS + + return graphics_pen; + + #else + + throw ErrFunctionCall; + + #endif +} + +void graphics::setbackground (int color) +{ + //if (! inited) return; + + #ifdef BLASSIC_HAS_GRAPHICS + + graphics_paper= color; + pcolor pxc= mapcolor (color).pc; + pbackground= pxc; + + #else + + touch (color); + + #endif +} + +int graphics::getbackground () +{ + #ifdef BLASSIC_HAS_GRAPHICS + + return graphics_paper; + + #else + + return 0; + + #endif +} + +void graphics::settransparent (int transpmode) +{ + #ifdef BLASSIC_HAS_GRAPHICS + + if (! inited) + return; + + opaquemode= ! (transpmode & 1); + + #else + + touch (transpmode); + + #endif +} + +void graphics::setdrawmode (int mode) +{ + #ifdef BLASSIC_HAS_GRAPHICS + + if (! inited) + return; + + #if 0 + // Draw modes: + // 0: normal copy mode. + // 1: XOR + // 2: AND + // 3: OR + // 0 to 3 are Amstrad CPC modes. + // 4: INVERT, NOT. + static int modes []= { drawmode_copy, drawmode_xor, + drawmode_and, drawmode_or, drawmode_invert }; + + if (mode < 0 || size_t (mode) >= util::dim_array (modes) ) + return; + drawmode= modes [mode]; + #else + + drawmode= getdrawmode (mode); + + #endif + + #ifdef BLASSIC_USE_X + + XSetFunction (display, gc, drawmode); + XSetFunction (display, gcp, drawmode); + + #elif defined BLASSIC_USE_WINDOWS + + //HDC hdc= GetDC (window); + SetROP2 (hdc, drawmode); + //ReleaseDC (window, hdc); + SetROP2 (hdcPixmap, drawmode); + + #endif + + #else + // No graphics. + + touch (mode); + + #endif +} + +#ifdef BLASSIC_HAS_GRAPHICS + +namespace { + +void do_line_unmasked (int x, int y) +{ + int prevx= lastx, prevy= lasty; + lastx= x; lasty= y; + + transform_x (x); transform_x (prevx); + transform_y (y); transform_y (prevy); + + do_rotate (prevx, prevy); + do_rotate (x, y); + + setactivecolor (pforeground); + + #ifdef BLASSIC_USE_SVGALIB + if (svgalib) + { + vga_drawline (prevx, prevy, x, y); + return; + } + #endif + + #ifdef BLASSIC_USE_X + + if (! fSynchro) + XDrawLine (display, window, gc, prevx, prevy, x, y); + XDrawLine (display, pixmap, gcp, prevx, prevy, x, y); + XFlush (display); + + #elif defined BLASSIC_USE_WINDOWS + + if (! fSynchro) + { + MoveToEx (hdc, prevx, prevy, 0); + LineTo (hdc, x, y); + LineTo (hdc, x + 1, y); // Last point + } + + MoveToEx (hdcPixmap, prevx, prevy, 0); + LineTo (hdcPixmap, x, y); + LineTo (hdcPixmap, x + 1, y); // Last point + + #endif +} + +const unsigned char maskvaluedefault= '\xFF'; +unsigned char maskvalue= maskvaluedefault; +bool maskdrawfirst= true; +unsigned maskpos= 0; +unsigned char auxmask []= { 1, 2, 4, 8, 16, 32, 64, 128 }; + +inline void impl_plot_mask (int x, int y) +{ + if (maskvalue == maskvaluedefault) + do_plot (x, y); + else + { + if (maskvalue & auxmask [maskpos] ) + do_plot (x, y); + if (++maskpos == 8) maskpos= 0; + } +} + +void do_line_mask (int x, int y) +{ + int prevx= lastx, prevy= lasty; + lastx= x; lasty= y; + + transform_x (prevx); transform_x (x); + transform_y (prevy); transform_y (y); + + setactivecolor (pforeground); + + int px= x - prevx; + int py= y - prevy; + int d1x= px < 0 ? -1 : px > 0 ? 1 : 0; + int d1y= py < 0 ? -1 : py > 0 ? 1 : 0; + int d2x= d1x; + int d2y= 0; + int m= abs (px); + int n= abs (py); + if (m <= n) + { + d2x= 0; + d2y= d1y; + m= abs (py); + n= abs (px); + } + int s= m / 2; + for (int i= 0; i <= m; ++i) + { + if (i != 0 || maskdrawfirst) + { + //if (maskvalue & auxmask [maskpos] ) + // do_plot (prevx, prevy); + //if (++maskpos == 8) maskpos= 0; + impl_plot_mask (prevx, prevy); + } + s+= n; + if (s >= m) + { + s-= m; + prevx+= d1x; + prevy+= d1y; + } + else + { + prevx+= d2x; + prevy+= d2y; + } + } +} + +inline void do_line (int x, int y) +{ + // When limited we use line masked to simplify line unmasked. + if (! limited && (maskvalue == maskvaluedefault && maskdrawfirst) ) + do_line_unmasked (x, y); + else + do_line_mask (x, y); +} + + // -------------------------------- + // drawarc and auxiliary functions. + // -------------------------------- + +// Mimic the Spectrum DRAW instruction, even with values +// of angle greater than 2 * pi. + +// Workaround for a problem on gcc. +double zero= 0.0; // Don't make this var const! + +inline int signum (double d) +{ + return d < zero ? -1 : d > zero ? 1 : 0; +} + +inline int roundint (double d) +{ + double frac= modf (d, & d); + if (frac > 0.5) + ++d; + else + if (frac < -0.5) + --d; + return int (d); +} + +inline void do_drawarcsegment (int xd, int yd, + int & coords_x, int & coords_y, bool & firstpoint) +{ + // This function is used instead of the other line + // draw functions to ensure exact compatibility with + // the Spectrum and because of first point of line + // treatement. + + int b= abs (yd); + int c= abs (xd); + int d= signum (yd); + int e= signum (xd); + //std::cerr << "do_drawarcsegment " << c << ' ' << b << ' ' << + // e << ' ' << d << std::endl; + int h, l, hvx, hvy; + if (c >= b) + { + h= c; l= b; hvx= e; hvy= 0; + } + else + { + if (b == 0) + return; + h= b; l= c; hvx= 0; hvy= d; + } + b= h; + int a= h / 2; + for (int i= 0; i < b; ++i) + { + a+= l; + if (a < h) + { + // Horizontal or vertical step. + coords_x+= hvx; + coords_y+= hvy; + } + else + { + // Diagonal step. + a-= h; + coords_x+= e; + coords_y+= d; + } + if (firstpoint) + firstpoint= false; + else + { + int x= coords_x; int y= coords_y; + transform_x (x); + transform_y (y); + impl_plot_mask (x, y); + } + } +} + +void do_drawarc (int x, int y, double g) +{ + // The original algorithm uses the Spectrum cooordinates, + // then the angle is inverted when using non inverted + // coordinates. + // To an explanation of the algorithm used, see "The Complete + // Spectrum ROM Disassembly" or some other disassembly of the + // Spectrum ROM (the book has more complete comments). + + switch (activetransform) + { + case TransformIdentity: + g= -g; + break; + case TransformInvertY: + break; // Nothing to do + } + + setactivecolor (pforeground); + + // If drawing of first point is active, no special treatement + // for the first point by marking as if not were the first. + bool firstpoint= ! maskdrawfirst; + + int xend= lastx + x, yend= lasty + y; + + double sing2= sin (g / 2); + double z; + if (sing2 != 0 && (z= fabs ( (abs (x) + abs (y) ) / sing2) ) >= 1) + { + int a= roundint (fabs (g * sqrt (z) / 2) ); + if (a <= 255) + { + a= (a & 0xFC) + 4; + if (a >= 256) + a= 252; + } + else + a= 252; + + double singa= sin (g / a); + double cosga= cos (g / a); + double w= sin (g / (2.0 * a) ) / sing2; + double f= g / 2 - g / (2.0 * a); + double sinf= sin (f); + double cosf= cos (f); + double un= y * w * sinf + x * w * cosf; + double vn= y * w * cosf - x * w * sinf; + + double uv= fabs (un) + fabs (vn); + if (uv >= 1) + { + double xn= lastx; + double yn= lasty; + a= a - 1; + while (a > 0) + { + xn= xn + un; + yn= yn + vn; + do_drawarcsegment ( + roundint (xn - lastx), + roundint (yn - lasty), + lastx, lasty, firstpoint); + a= a - 1; + if (a > 0) + { + double un1= un; + un= un1 * cosga - vn * singa; + vn= un1 * singa + vn * cosga; + } + } + } + } + // Draw the last segment (can be the entire arc). + do_drawarcsegment (xend - lastx, yend - lasty, + lastx, lasty, firstpoint); +} + +#ifdef BLASSIC_USE_X + +inline void do_rotate_in_char (int & x, int & y) +{ + switch (rotate) + { + case RotateNone: + break; + case Rotate90: + #if 1 + int newx= y; + y= charwidth - x - 1; + x= newx; + #else + int newy= x; + x= charwidth - y - 1; + y= newy; + #endif + break; + } +} + +inline bool load_char_image (int x, int y, unsigned char (& ch) [8] ) +{ + TRACEFUNC (tr, "load_char_image"); + + class ImageGuard { + public: + ImageGuard (XImage * img) : + img (img) + { } + ~ImageGuard () + { + XDestroyImage (img); + } + private: + XImage * img; + }; + + int width= charwidth, height= charheight; + do_rotate_rel (width, height); + do_rotate (x, y); + switch (rotate) + { + case RotateNone: + break; + case Rotate90: + y-= height - 1; + break; + } + TRMESSAGE (tr, "at " + to_string (x) + ", " + to_string (y) ); + + XImage * img= XGetImage (display, pixmap, x, y, + width, height, AllPlanes, XYPixmap); + if (img == NULL) + throw ErrNoGraphics; // Not a good idea, provisional. + ImageGuard guard (img); + unsigned long back= XGetPixel (img, 0, 0); + bool fFore= false; + unsigned long fore= 0; + for (int i= 0, ipos= 0; i < 8; ++i, ipos+= zoomtexty) + for (int j= 0, jpos= 0; j < 8; ++j, jpos+= zoomtextx) + { + int rjpos= jpos, ripos= ipos; + do_rotate_in_char (rjpos, ripos); + unsigned long c= XGetPixel (img, rjpos, ripos); + bool bit; + if (c == back) + { + bit= false; + } + else + { + if (! fFore) + { + fFore= true; + fore= c; + } + if (c != fore) + return false; + bit= true; + } + #if 1 + // Quitado provisionalmente + if (zoomtextx > 1 || zoomtexty > 1) + { + int zx= zoomtextx, zy= zoomtexty; + //do_rotate_rel (zx, zy); + const int iend= ipos + zy; + const int jend= jpos + zx; + for (int ii= ipos; ii < iend; ++ii) + for (int jj= jpos; jj < jend; ++jj) + { + int rj= jj, ri= ii; + do_rotate_in_char (rj, ri); + if (XGetPixel (img, rj, ri) + != c) + return false; + } + } + #endif + ch [i]<<= 1; + ch [i]|= bit; + } + //XDestroyImage (img); + return true; +} + +#elif defined BLASSIC_USE_WINDOWS + +inline bool load_char_image (int x, int y, unsigned char (& ch) [8] ) +{ + //COLORREF back= GetPixel (hdcPixmap, x, y); + int rx= x, ry= y; + do_rotate (rx, ry); + COLORREF back= GetPixel (hdcPixmap, rx, ry); + bool fFore= false; + COLORREF fore= 0; + for (int i= 0, ipos= y; i < 8; ++i, ipos+= zoomtexty) + for (int j= 0, jpos= x; j < 8; ++j, jpos+= zoomtextx) + { + //COLORREF c= GetPixel (hdcPixmap, jpos, ipos); + int rjpos= jpos, ripos= ipos; + do_rotate (rjpos, ripos); + COLORREF c= GetPixel (hdcPixmap, rjpos, ripos); + bool bit; + if (c == back) + bit= false; + else + { + if (! fFore) + { + fFore= true; + fore= c; + } + if (c != fore) + //return std::string (); + return false; + bit= true; + } + if (zoomtextx > 1 || zoomtexty > 1) + { + const int iend= ipos + zoomtexty; + const int jend= jpos + zoomtextx; + for (int ii= ipos; ii < iend; ++ii) + for (int jj= jpos; jj < jend; ++jj) + { + int rj= jj, ri= ii; + do_rotate (rj, ri); + if (GetPixel (hdcPixmap, + rj, ri) != c) + return false; + } + } + ch [i]<<= 1; + ch [i]|= bit; + } + return true; +} + +#else + +inline bool load_char_image (int, int, unsigned char (&) [8] ) +{ + return false; +} + +#endif + +std::string copychrat (int x, int y, BlChar from, BlChar to) +{ + TRACEFUNC (tr, "copychrat"); + { + std::ostringstream oss; + oss << "at " << x << ", " << y; + TRMESSAGE (tr, oss.str () ); + } + + unsigned char ch [8]= {0}; + + //if (x < 0 || x > screenwidth - 8 || y < 0 || y > screenheight - 8) + if (x < 0 || x > screenwidth - charwidth || + y < 0 || y > screenheight - charheight) + return std::string (); + + if (! load_char_image (x, y, ch) ) + return std::string (); + + #ifndef NDEBUG + { + std::ostringstream oss; + oss << "Char: " << std::hex; + for (int i= 0; i < 8; ++i) + { + oss << std::setw (2) << std::setfill ('0') << + static_cast <unsigned int> (ch [i]) << ", "; + } + TRMESSAGE (tr, oss.str () ); + } + #endif + + char chinv [8]; + for (int i= 0; i < 8; ++i) + chinv [i]= static_cast <unsigned char> (~ ch [i]); + + //for (int i= 0; i < 256; ++i) + int iFrom= static_cast <int> (from); + int iTo= static_cast <int> (to) + 1; + for (int i= iFrom; i < iTo; ++i) + { + if (memcmp (charset::data [i], ch, 8) == 0 || + memcmp (charset::data [i], chinv, 8) == 0) + { + return std::string (1, static_cast <char> (i) ); + } + } + + return std::string (); +} + +class ColorTester { + #ifdef BLASSIC_USE_X + XImage * img; + #define TYPETESTER 2 + #endif +public: + ColorTester () + { + #ifdef BLASSIC_USE_X + #if TYPETESTER == 1 + img= XGetImage (display, pixmap, + 0, 0, 1, 1, + AllPlanes, XYPixmap); + #else + int width= screenwidth, height= screenheight; + do_rotate_rel (width, height); + img= XGetImage (display, pixmap, + 0, 0, width, height, + AllPlanes, XYPixmap); + #endif + if (img == NULL) + throw ErrNoGraphics; + #endif + } + ~ColorTester () + { + #ifdef BLASSIC_USE_X + XDestroyImage (img); + #endif + } + ColorValue operator () (int x, int y) + { + do_rotate (x, y); + #ifdef BLASSIC_USE_X + #if TYPETESTER == 1 + XGetSubImage (display, pixmap, x, y, 1, 1, + AllPlanes,XYPixmap, + img, 0, 0); + return XGetPixel (img, 0, 0); + #else + return XGetPixel (img, x, y); + #endif + #elif defined BLASSIC_USE_WINDOWS + return GetPixel (hdcPixmap, x, y); + #endif + } +}; + +ColorValue do_test_color (int x, int y) +{ + do_rotate (x, y); + + #ifdef BLASSIC_USE_X + + XImage * img= XGetImage (display, pixmap, x, y, 1, 1, + AllPlanes, XYPixmap); + if (img == NULL) + throw ErrNoGraphics; // Not a good idea, provisional. + unsigned long color= XGetPixel (img, 0, 0); + XDestroyImage (img); + return color; + + #else + + return GetPixel (hdcPixmap, x, y); + + #endif +} + +int do_test (int x, int y) +{ + //int x= lastx; + //int y= lasty; + //transform_x (x); + //transform_y (y); + + if (x < 0 || x >= screenwidth || y < 0 || y >= screenheight) + return 0; + + do_rotate (x, y); + + #ifdef BLASSIC_USE_X + + XImage * img= XGetImage (display, pixmap, x, y, 1, 1, + AllPlanes, XYPixmap); + if (img == NULL) + throw ErrNoGraphics; // Not a good idea, provisional. + unsigned long color= XGetPixel (img, 0, 0); + XDestroyImage (img); + for (int i= 0; i < 16; ++i) + if (mapcolor (i).pc->pixel == color) + return i; + for (definedcolor_t::iterator it= definedcolor.begin (); + it != definedcolor.end (); ++it) + { + if (it->second.pc->pixel == color) + return it->first; + } + return -1; + + #elif defined BLASSIC_USE_WINDOWS + + COLORREF color= GetPixel (hdcPixmap, x, y); + for (int i= 0; i < 16; ++i) + { + //const ColorRGB & c= assignRGB [i]; + const ColorRGB & c= mapcolor (i).rgb; + if (RGB (c.r, c.g, c.b) == color) + //if (GetNearestColor (hdcPixmap, RGB (c.r, c.g, c.b) ) == + // color) + return i; + } + for (definedcolor_t::iterator it= definedcolor.begin (); + it != definedcolor.end (); ++it) + { + const ColorRGB & c= it->second.rgb; + if (RGB (c.r, c.g, c.b) == color) + return it->first; + } + return -1; + + #else + + return -1; + + #endif +} + +int test () +{ + int x= lastx; + int y= lasty; + transform_x (x); + transform_y (y); + return do_test (x, y); +} + +} // namespace + +#endif +//BLASSIC_HAS_GRAPHICS + +void graphics::line (int x, int y) +{ + requiregraphics (); + + #ifdef BLASSIC_HAS_GRAPHICS + + do_line (x, y); + + #else + + touch (x, y); + + #endif +} + +void graphics::liner (int x, int y) +{ + requiregraphics (); + + #ifdef BLASSIC_HAS_GRAPHICS + + line (lastx + x, lasty + y); + + #else + + touch (x, y); + + #endif +} + +void graphics::drawarc (int x, int y, double g) +{ + requiregraphics (); + + #ifdef BLASSIC_HAS_GRAPHICS + + do_drawarc (x, y, g); + + #else + + touch (x, y, g); + + #endif +} + +void graphics::rectangle (Point org, Point dest) +{ + TRACEFUNC (tr, "graphics::rectangle"); + + requiregraphics (); + + #ifdef BLASSIC_HAS_GRAPHICS + + int x1= org.x; + int y1= org.y; + int x2= dest.x; + int y2= dest.y; + lastx= x2; lasty= y2; + + transform_x (x1); transform_x (x2); + transform_y (y1); transform_y (y2); + + do_rotate (x1, y1); + do_rotate (x2, y2); + + if (x1 > x2) std::swap (x1, x2); + if (y1 > y2) std::swap (y1, y2); + + #ifdef BLASSIC_USE_WINDOWS + + HBRUSH hold= (HBRUSH) SelectObject (hdcPixmap, GetStockObject (NULL_BRUSH) ); + Rectangle (hdcPixmap, x1, y1, x2 + 1, y2 + 1); + SelectObject (hdc, hold); + if (! fSynchro) + { + + hold= (HBRUSH) SelectObject (hdc, GetStockObject (NULL_BRUSH) ); + Rectangle (hdc, x1, y1, x2 + 1, y2 + 1); + SelectObject (hdc, hold); + } + + #elif defined BLASSIC_USE_X + + XDrawRectangle (display, pixmap, gcp, + x1, y1, x2 - x1, y2 - y1); + if (! fSynchro) + XDrawRectangle (display, window, gc, + x1, y1, x2 - x1, y2 - y1); + + #endif + + #else + // No BLASSIC_HAS_GRAPHICS + + touch (org, dest); + + #endif +} + +void graphics::rectanglefilled (Point org, Point dest) +{ + TRACEFUNC (tr, "graphics::rectanglefilled"); + + requiregraphics (); + + #ifdef BLASSIC_HAS_GRAPHICS + + int x1= org.x; + int y1= org.y; + int x2= dest.x; + int y2= dest.y; + lastx= x2; lasty= y2; + + transform_x (x1); transform_x (x2); + transform_y (y1); transform_y (y2); + + if (x1 > x2) std::swap (x1, x2); + if (y1 > y2) std::swap (y1, y2); + do_fill_rectangle (x1, y1, x2, y2, true); + + #else + // No BLASSIC_HAS_GRAPHICS + + touch (org, dest); + + #endif +} + +void graphics::zxplot (Point p) +{ + TRACEFUNC (tr, "graphics::zxplot"); + + requiregraphics (); + + #ifdef BLASSIC_HAS_GRAPHICS + + int x= p.x / 2 + 1; + int y= 21 - p.y / 2; + p.x*= 4; + p.y*= 4; + Point p2 (p); + p2.x+= 3; + p2.y+= 3; + rectanglefilled (p, p2); + gotoxy (BlChannel (0), x, y); + + #else + // No BLASSIC_HAS_GRAPHICS + + touch (p); + + #endif +} + +void graphics::zxunplot (Point p) +{ + TRACEFUNC (tr, "graphics::zxunplot"); + + requiregraphics (); + + #ifdef BLASSIC_HAS_GRAPHICS + + setactivecolor (pbackground); + zxplot (p); + setactivecolor (pforeground); + + #else + // No BLASSIC_HAS_GRAPHICS + + touch (p); + + #endif +} + + +void graphics::move (int x, int y) +{ + requiregraphics (); + + #ifdef BLASSIC_HAS_GRAPHICS + + lastx= x; lasty= y; + + #else + + touch (x, y); + + #endif +} + +void graphics::mover (int x, int y) +{ + requiregraphics (); + + #ifdef BLASSIC_HAS_GRAPHICS + + lastx+= x; lasty+= y; + + #else + + touch (x, y); + + #endif +} + +void graphics::plot (int x, int y) +{ + requiregraphics (); + + #ifdef BLASSIC_HAS_GRAPHICS + + lastx= x; lasty= y; + transform_x (x); + transform_y (y); + setactivecolor (pforeground); + do_plot (x, y); + + #else + + touch (x, y); + + #endif +} + +void graphics::plotr (int x, int y) +{ + #ifdef BLASSIC_HAS_GRAPHICS + + // No need to check here, plot will do + plot (lastx + x, lasty + y); + + #else + + touch (x, y); + no_graphics_support (); + + #endif +} + +int graphics::test (int x, int y, bool relative) +{ + #ifdef BLASSIC_HAS_GRAPHICS + + // Check for graphics mode is done when calling move. + if (relative) + { + x+= lastx; + y+= lasty; + } + move (x, y); + + return ::test (); + + #else + + no_graphics_support (); + touch (x, y, relative); + throw ErrBlassicInternal; // Make the compiler happy. + + #endif +} + +namespace { + +void line_to_point (const graphics::Point & p) +{ + graphics::line (p.x, p.y); +} + +} // namespace + +void graphics::plot (std::vector <Point> & points) +{ + requiregraphics (); + + #ifdef BLASSIC_HAS_GRAPHICS + + ASSERT (points.size () != 0); + lastx= points [0].x; + lasty= points [0].y; + std::for_each (points.begin () + 1, points.end (), line_to_point); + + #else + + // Must not come here, requiregraphics must throw. + ASSERT (false); + touch (points); + + #endif +} + +//#define DEBUG_CIRCLE + +#ifdef DEBUG_CIRCLE + +#include <unistd.h> + +namespace { inline void pausec () { usleep (100000); } } + +#else + +namespace { inline void pausec () { } } + +#endif + +void graphics::circle (int x, int y, int radius) +{ + requiregraphics (); + + #ifdef BLASSIC_HAS_GRAPHICS + + setactivecolor (pforeground); + + lastx= x + radius; + lasty= y; + + transform_x (x); + transform_y (y); + + if (radius == 0) + { + impl_plot_mask (x, y); + return; + } + + // sq2_2 is sin (pi/4) + #ifdef M_SQRT2 + static const double sq2_2= M_SQRT2 / 2.0; + #else + static const double sq2_2= sqrt (2.0) / 2.0; + #endif + //int r= int (radius * sq2_2 + .5) + 1; + int r= int (radius * sq2_2 + 1.0); + int rr= int (sqrt (radius * radius - r * r) + .5); + int dim= r; + if (rr >= r) ++dim; + + #ifdef DEBUG_CIRCLE + cerr << "Circle: " << radius << ", " << r << endl; + #endif + + util::auto_buffer <int> p (dim); + + // Bresenham algorithm. + for (int i= 0, j= radius, d= 1 - radius; i < dim; ++i) + { + p [i]= j; + if (d < 0) + d+= 2 * i + 3; + else + { + d+= 2 * (i - j) + 5; + --j; + } + } + + rr= p [r - 1] - 1; + ASSERT (rr <= dim - 1); + + // The first point in each quadrant is plotted independently. + // In the first quadrant is omitted, we plot it at the end. + #ifdef DEBUG_CIRCLE + graphics::setcolor (4); + #endif + for (int j= 1; j < r; ++j) + { + //do_line (x + p [j], y - j); + impl_plot_mask (x + p [j], y - j); + pausec (); + } + #ifdef DEBUG_CIRCLE + graphics::setcolor (0); + #endif + for (int i= rr; i > 0; --i) + { + //do_line (x + i, y - p [i] ); + impl_plot_mask (x + i, y - p [i] ); + pausec (); + } + + //do_line (x, y - radius); + #ifdef DEBUG_CIRCLE + graphics::setcolor (4); + #endif + impl_plot_mask (x, y - radius); + for (int i= 1; i < r; ++i) + { + //do_line (x - i, y - p [i] ); + impl_plot_mask (x - i, y - p [i] ); + pausec (); + } + #ifdef DEBUG_CIRCLE + graphics::setcolor (0); + #endif + for (int j= rr; j > 0; --j) + { + //do_line (x - p [j], y - j); + impl_plot_mask (x - p [j], y - j); + pausec (); + } + + //do_line (x - radius, y); + #ifdef DEBUG_CIRCLE + graphics::setcolor (4); + #endif + impl_plot_mask (x - radius, y); + for (int j= 1; j < r; ++j) + { + //do_line (x - p [j], y + j); + impl_plot_mask (x - p [j], y + j); + pausec (); + } + #ifdef DEBUG_CIRCLE + graphics::setcolor (0); + #endif + for (int i= rr; i > 0; --i) + { + //do_line (x - i, y + p [i] ); + impl_plot_mask (x - i, y + p [i] ); + pausec (); + } + + //do_line (x, y + radius); + impl_plot_mask (x, y + radius); + #ifdef DEBUG_CIRCLE + graphics::setcolor (4); + #endif + for (int i= 1; i < r; ++i) + { + //do_line (x + i, y + p [i] ); + impl_plot_mask (x + i, y + p [i] ); + pausec (); + } + #ifdef DEBUG_CIRCLE + graphics::setcolor (0); + #endif + for (int j= rr; j > 0; --j) + { + //do_line (x + p [j], y + j); + impl_plot_mask (x + p [j], y + j); + pausec (); + } + //do_line (x + radius, y); + #ifdef DEBUG_CIRCLE + graphics::setcolor (4); + #endif + impl_plot_mask (x + radius, y); + + #else + // BLASSIC_HAS_GRAPHICS + + touch (x, y, radius); + + #endif +} + +#ifdef BLASSIC_HAS_GRAPHICS + +namespace { + +inline void get_point_on_arc (int r, BlNumber a, int & out_x, int & out_y) +{ + BlNumber s= sin (a) * r, c= cos (a) * r; + out_x= static_cast <int> (c < 0 ? c - .5 : c + .5); + out_y= static_cast <int> (s < 0 ? s - .5 : s + .5); +} + +inline int get_quadrant (int x, int y) +{ + if (x >= 0) + { + if (y >= 0) + return 0; + else + return 3; + } + else + { + if (y > 0) + return 1; + else + return 2; + } +} + +} // namespace + +#endif +// BLASSIC_HAS_GRAPHICS + +void graphics::arccircle (int x, int y, int radius, + BlNumber arcbeg, BlNumber arcend) +{ + /* + The code for this function and his auxiliarys + is taken from the Allegro library. + Many thanks. + */ + + requiregraphics (); + + #ifdef BLASSIC_HAS_GRAPHICS + + setactivecolor (pforeground); + + //#define DEBUG_ARCCIRCLE + + #ifdef DEBUG_ARCCIRCLE + cerr << "arccircle: " << x << ", " << y << ", " << radius << ", " << + arcbeg << ", " << arcend << endl; + #endif + + lastx= x + radius; + lasty= y; + transform_x (x); + transform_y (y); + + int px, py; // Current position and auxiliary. + get_point_on_arc (radius, arcend, px, py); + const int ex= px, ey= py; // End position. + get_point_on_arc (radius, arcbeg, px, py); + const int sx= px, sy= py; // Start position. + // Current position start at start position. + + const int sq= get_quadrant (sx, sy); // Start quadrant. + // Calculate end quadrant, considering that end point + // must be after start point. + int q= get_quadrant (ex, ey); + if (sq > q) + // Quadrant end must be greater or equal. + q+= 4; + else if (sq == q && arcbeg > arcend) + // If equal, consider the angle. + q+= 4; + const int qe= q; + q= sq; // Current cuadrant. + + #ifdef DEBUG_ARCCIRCLE + cerr << "Quadrant from " << sq << " to " << qe << endl; + #endif + + // Direction of movement. + int dy = ( ( (q + 1) & 2) == 0) ? 1 : -1; + int dx= ( (q & 2) == 0) ? -1 : 1; + + const int rr= radius * radius; + int xx= px * px; + int yy= py * py - rr; + + while (true) + { + // Change quadrant when needed, adjusting directions. + if ( (q & 1) == 0) + { + if (px == 0) + { + if (qe == q) + break; + ++q; + dy= -dy; + } + } + else + { + if (py == 0) + { + if (qe == q) + break; + ++q; + dx= -dx; + } + } + // If we are in the end quadrant, check if at the end position. + if (qe == q) + { + int det= 0; + if (dy > 0) + { + if (py >= ey) + ++det; + } + else + { + if (py <= ey) + ++det; + } + if (dx > 0) + { + if (px >= ex) + ++det; + } + else + { + if (px <= ex) + ++det; + } + if (det == 2) + break; + } + + impl_plot_mask (x + px, y - py); + + int xx_new= (px + dx) * (px + dx); + int yy_new= (py + dy) * (py + dy) - rr; + int rr1= xx_new + yy; + int rr2= xx_new + yy_new; + int rr3= xx + yy_new; + if (rr1 < 0) rr1= -rr1; + if (rr2 < 0) rr2= -rr2; + if (rr3 < 0) rr3= -rr3; + if (rr3 >= std::min (rr1, rr2) ) + { + px+= dx; + xx= xx_new; + } + if (rr1 > std::min (rr2, rr3) ) + { + py+= dy; + yy= yy_new; + } + } + if (px != sx || py != sy || sq == qe) + impl_plot_mask (x + px, y - py); + + #else + // No BLASSIC_HAS_GRAPHICS + + touch (x, y, radius, arcbeg, arcend); + + #endif +} + +void graphics::ellipse (int ox, int oy, int rx, int ry) +{ + // Based on "A fast Bresenham type algorithm + // for drawing ellipses", by John Kennedy. + + //#define DEBUG_ELLIPSE + + #ifdef DEBUG_ELLIPSE + cerr << "Ellipse: " << rx << ", " << ry << endl; + #endif + + requiregraphics (); + + #ifdef BLASSIC_HAS_GRAPHICS + + setactivecolor (pforeground); + lastx= ox+ rx; + lasty= oy; + + transform_x (ox); + transform_y (oy); + + const int ry2= ry * ry; + const int rx2= rx * rx; + //const int dimy= int (ry2 / sqrt (ry2 + rx2) + 0.5); + const int dimy= int (ry2 / sqrt (ry2 + rx2) ) + 1; + + #ifdef DEBUG_ELLIPSE + cerr << "Dimy: " << dimy << endl; + #endif + + util::auto_buffer <int> py (dimy); + //#define SIMPLER + #ifndef SIMPLER + const int ry2_2= ry2 * 2; + const int rx2_2= rx2 * 2; + int xchange= ry2 * (1 - 2 * rx); + int ychange= rx2; + int ellipseerror= 0; + for (int y= 0, x= rx; y < dimy; ++y) + { + py [y]= x; + if (x == 0) // Needed for little rx + continue; + ellipseerror+= ychange; + ychange+= rx2_2; + if (2 * ellipseerror + xchange > 0) + { + --x; + ellipseerror+= xchange; + xchange+= ry2_2; + } + } + #else + for (int y= 0; y < dimy; ++y) + py [y]= int (rx * sqrt (ry * ry - y * y) / ry + 0.5); + #endif + + int aux= dimy > 0 ? py [dimy - 1] : rx; + const int dimx= aux < 0 ? 0 : aux; + + #ifdef DEBUG_ELLIPSE + cerr << "Dimx: " << dimx << endl; + #endif + + util::auto_buffer <int> px (dimx); + #ifndef SIMPLER + xchange= ry2; + ychange= rx2 * (1 - 2 * ry); + ellipseerror= 0; + for (int x= 0, y= ry; x < dimx; ++x) + { + px [x]= y; + if (y == 0) // Needed for little ry + continue; + ellipseerror+= xchange; + xchange+= ry2_2; + if (2 * ellipseerror + ychange > 0) + { + --y; + ellipseerror+= ychange; + ychange+= rx2_2; + } + } + #else + for (int x= 0; x < dimx; ++x) + px [x]= int (ry * sqrt (rx * rx - x * x) / rx + 0.5); + #endif + + for (int y= 1; y < dimy; ++y) + impl_plot_mask (ox + py [y], oy - y); + for (int x= dimx - 1; x > 0; --x) + impl_plot_mask (ox + x, oy - px [x] ); + impl_plot_mask (ox, oy - ry); + for (int x= 1; x < dimx; ++x) + impl_plot_mask (ox - x, oy - px [x] ); + for (int y= dimy - 1; y > 0; --y) + impl_plot_mask (ox - py [y], oy - y); + impl_plot_mask (ox - rx, oy); + for (int y= 1; y < dimy; ++y) + impl_plot_mask (ox - py [y], oy + y); + for (int x= dimx - 1; x > 0; --x) + impl_plot_mask (ox - x, oy + px [x] ); + impl_plot_mask (ox, oy + ry); + for (int x= 1; x < dimx; ++x) + impl_plot_mask (ox + x, oy + px [x] ); + for (int y= dimy - 1; y > 0; --y) + impl_plot_mask (ox + py [y], oy + y); + impl_plot_mask (ox + rx, oy); + + #else + // No BLASSIC_HAS_GRAPHICS + + touch (ox, oy, rx, ry); + + #endif +} + +#ifdef BLASSIC_HAS_GRAPHICS + +namespace { + +inline void get_point_on_arc (int rx, int ry, BlNumber a, + int & out_x, int & out_y) +{ + BlNumber s= sin (a) * ry, c= cos (a) * rx; + out_x= static_cast <int> (c < 0 ? c - .5 : c + .5); + out_y= static_cast <int> (s < 0 ? s - .5 : s + .5); +} + +} // namespace + +#endif +// BLASSIC_HAS_GRAPHICS + +void graphics::arcellipse (int ox, int oy, int rx, int ry, + BlNumber arcbeg, BlNumber arcend) +{ + /* + Adapted from the arccircle algorithm, + not optimal but works. + */ + //#define DEBUG_ARCELLIPSE + + requiregraphics (); + + #ifdef BLASSIC_HAS_GRAPHICS + + setactivecolor (pforeground); + lastx= ox+ rx; + lasty= oy; + + transform_x (ox); + transform_y (oy); + + int px, py; // Current position and auxiliary + get_point_on_arc (rx, ry, arcend, px, py); + const int ex= px, ey= py; // End position + get_point_on_arc (rx, ry, arcbeg, px, py); + const int sx= px, sy= py; // Start position + + const int sq= get_quadrant (sx, sy); // Start quadrant. + // Calculate end quadrant, considering that end point + // must be after start point. + int q= get_quadrant (ex, ey); + if (sq > q) + // Quadrant end must be greater or equal. + q+= 4; + else if (sq == q && arcbeg > arcend) + // If equal, consider the angle. + q+= 4; + const int qe= q; + q= sq; // Current cuadrant. + + #ifdef DEBUG_ARCELLIPSE + cerr << "Arc llipse from: " << arcbeg << " to " << arcend << endl; + cerr << "Quadrant from " << sq << " to " << qe << endl; + #endif + + // Direction of movement. + int dy = ( ( (q + 1) & 2) == 0) ? 1 : -1; + int dx= ( (q & 2) == 0) ? -1 : 1; + + const int rx2= rx * rx; + const int ry2= ry * ry; + const int rxy2= rx2 * ry2; + + while (true) + { + // Change quadrant when needed, adjusting directions. + if ( (q & 1) == 0) + { + // Take care that in very eccentric ellipses + // can be in 0 before the extreme point. + if (px == 0 && abs (py) == ry) + { + if (qe == q) + break; + ++q; + dy= -dy; + } + } + else + { + if (py == 0 && abs (px) == rx) + { + if (qe == q) + break; + ++q; + dx= -dx; + } + } + // If we are in the end quadrant, check if at the end position. + if (qe == q) + { + int det= 0; + if (dy > 0) + { + if (py >= ey) + ++det; + } + else + { + if (py <= ey) + ++det; + } + if (dx > 0) + { + if (px >= ex) + ++det; + } + else + { + if (px <= ex) + ++det; + } + if (det == 2) + break; + } + + impl_plot_mask (ox + px, oy - py); + + int rr1= ry2 * (px + dx) * (px + dx) + + rx2 * py * py - rxy2; + int rr2= ry2 * (px + dx) * (px + dx) + + rx2 * (py + dy) * (py + dy) - rxy2; + int rr3= ry2 * px * px + + rx2 * (py + dy) * (py + dy) - rxy2; + if (rr1 < 0) rr1= -rr1; + if (rr2 < 0) rr2= -rr2; + if (rr3 < 0) rr3= -rr3; + if (rr3 >= std::min (rr1, rr2) ) + { + px+= dx; + //xx= xx_new; + } + if (rr1 > std::min (rr2, rr3) ) + { + py+= dy; + //yy= yy_new; + } + } + if (px != sx || py != sy || sq == qe) + impl_plot_mask (ox + px, oy - py); + + #else + // No BLASSIC_HAS_GRAPHICS + + touch (ox, oy, rx, ry, arcbeg, arcend); + + #endif +} + +#ifdef BLASSIC_HAS_GRAPHICS + +namespace { + +#if 0 + +class Painter { + ColorValue paint, border; + std::vector <std::vector <bool> > visited; + ColorTester test; +public: + Painter (ColorValue paint, ColorValue border) : + paint (paint), + border (border), + visited (screenwidth, std::vector <bool> (screenheight) ) + { + } + + bool check (int x, int y) + { + if (! check_limit (x, y) ) + return false; + std::vector <bool>::reference v= visited [x] [y]; + if (v) + return false; + v= true; + ColorValue c= test (x, y); + if (c == border || c == paint) + return false; + return true; + } + +void do_paint_it (int x, int y/*, int xd, int yd*/) +{ + //if (! check_limit (x, y) ) + // return; + //int c= do_test (x, y); + //if (c == borderattr || c == paintattr) + // return; + + do_plot (x, y); + //visited [x] [y]= true; + + //ColorValue c; + //if (xd != -1) + #if 0 + for (int i=1; /*check_limit (x - i, y)*/ i < 2; ++i) + { + if (visited [x - i] [y] ) + break; + visited [x - i] [y]= true; + //c= do_test_color (x - i, y); + c= test (x - i, y); + if (c != border && c != paint) + do_paint_it (x - i, y, 1, 0); + else + break; + } + #else + if (check (x - 1, y) ) + do_paint_it (x - 1, y/*, 1, 0*/); + #endif + //if (xd != 1) + #if 0 + for (int i= 1; /*check_limit (x + i, y)*/ i < 2; ++i) + { + if (visited [x + i] [y] ) + break; + visited [x + i] [y]= true; + //c= do_test_color (x + i, y); + c= test (x + i, y); + if (c != border && c != paint) + do_paint_it (x + i, y, -1, 0); + else + break; + } + #else + if (check (x + 1, y) ) + do_paint_it (x + 1, y/*, 1, 0*/); + #endif + //if (yd != -1) + #if 0 + for (int i= 1; /*check_limit (x, y - i)*/ i < 2; ++i) + { + if (visited [x] [y - i] ) + break; + visited [x] [y - i]= true; + //c= do_test_color (x, y - i); + c= test (x, y - i); + if (c != border && c != paint) + do_paint_it (x, y - i, 0, 1); + else + break; + } + #else + if (check (x, y - 1) ) + do_paint_it (x, y - 1/*, 0, 1*/); + #endif + //if (yd != 1) + #if 0 + for (int i= 1; /*check_limit (x, y + i)*/ i < 2; ++i) + { + if (visited [x] [y + i] ) + break; + visited [x] [y + i]= true; + //c= do_test_color (x, y + i); + c= test (x, y + i); + if (c != border && c != paint) + do_paint_it (x, y + i, 0, -1); + else + break; + } + #else + if (check (x, y + 1) ) + do_paint_it (x, y + 1/*, 0, -1*/); + #endif +} + + void do_paint (int x, int y) + { + if (check (x, y) ) + do_paint_it (x, y/*, 0, 0*/); + } + +}; + +#elif 0 + +class Painter { + ColorValue paint, border; + std::vector <std::vector <bool> > visited; + ColorTester test; +public: + Painter (ColorValue paint, ColorValue border) : + paint (paint), + border (border), + visited (screenwidth, std::vector <bool> (screenheight) ) + { + } + +//void paintN (int x, int y); +//void paintS (int x, int y); +//void paintE (int x, int y); +//void paintW (int x, int y); + +void do_paint (int x, int y) +{ + if (! check_limit (x, y) ) + return; + //ColorValue c= do_test_color (x, y); + ColorValue c= test (x, y); + if (c == paint || c == border) + return; + do_plot (x, y); + visited [x] [y]= true; + paintN (x, y - 1); + paintS (x, y + 1); + paintE (x + 1, y); + paintW (x - 1, y); +} + +void paintN (int x, int y) +{ + if (! check_limit (x, y) ) + return; + if (visited [x] [y] ) + return; + visited [x] [y]= true; + //ColorValue c= do_test_color (x, y); + ColorValue c= test (x, y); + if (c == paint || c == border) + return; + do_plot (x, y); + paintN (x, y - 1); + paintE (x + 1, y); + paintW (x - 1, y); +} + +void paintS (int x, int y) +{ + if (! check_limit (x, y) ) + return; + if (visited [x] [y] ) + return; + visited [x] [y]= true; + //ColorValue c= do_test_color (x, y); + ColorValue c= test (x, y); + if (c == paint || c == border) + return; + do_plot (x, y); + paintS (x, y + 1); + paintE (x + 1, y); + paintW (x - 1, y); +} + +void paintE (int x, int y) +{ + if (! check_limit (x, y) ) + return; + if (visited [x] [y] ) + return; + visited [x] [y]= true; + //ColorValue c= do_test_color (x, y); + ColorValue c= test (x, y); + if (c == paint || c == border) + return; + do_plot (x, y); + paintN (x, y - 1); + paintS (x, y + 1); + paintE (x + 1, y); +} + +void paintW (int x, int y) +{ + if (! check_limit (x, y) ) + return; + if (visited [x] [y] ) + return; + visited [x] [y]= true; + //ColorValue c= do_test_color (x, y); + ColorValue c= test (x, y); + if (c == paint || c == border) + return; + do_plot (x, y); + paintN (x, y - 1); + paintS (x, y + 1); + paintW (x - 1, y); +} + +}; + +#else + +struct PPoint { + int x; + int y; + PPoint () { } + PPoint (int x, int y) : x (x), y (y) { } + //PPoint (const PPoint & p) : x (p.x), y (p.y) { } + bool check (ColorValue paint, ColorValue border, + std::vector <std::vector <bool> > & visit, + ColorTester & test) + { + if (! check_limit (x, y) ) + return false; + std::vector <bool>::reference v= visit [x] [y]; + if (v) + return false; + v= true; + //ColorValue c= do_test_color (x, y); + ColorValue c= test (x, y); + if (c == paint || c == border) + return false; + return true; + } + void plot () { do_plot (x, y); } +}; + +#if 0 + +class Painter { + ColorValue paint, border; + ColorTester test; + struct seg { + int y, xl, xr, dy; + seg () { } + seg (int y, int xl, int xr, int dy) : + y (y), xl (xl), xr (xr), dy (dy) + { } + }; + std::deque <seg> stack; + bool check (int x, int y) + { + if (! check_limit (x, y) ) + return false; + ColorValue c= test (x, y); + if (c == paint || c == border) + return false; + return true; + } +public: + Painter (ColorValue paint, ColorValue border) : + paint (paint), + border (border) + { + } + +void do_paint (int x, int y) +{ + seg s; + int l, x1, x2, dy; + if (! check_limit (x, y) ) + return; + ColorValue ov; + ov= test (x, y); + if (ov == paint || ov == border) + return; + stack.push_back (seg (y, x, x, 1) ); + stack.push_back (seg (y + 1, x, x, -1) ); + while (! stack.empty () ) + { + s= stack.back (); + stack.pop_back (); + y= s.y + s.dy; x1= s.xl; x2= s.xr; dy= s.dy; + for (x= x1; check (x, y); --x) + do_plot (x, y); + if (x >= x1) + goto skip; + l= x + 1; + if (l < x1) + stack.push_back (seg (y, l, x1 - 1, -dy) ); + x= x1 + 1; + do { + for ( ; check (x, y); ++x) + do_plot (x, y); + stack.push_back (seg (y, l, x - 1, dy) ); + if (x > x2 + 1) + stack.push_back (seg (y, x2 + 1, x - 1, -dy) ); + skip: + for (x++; x <= x2 && ! test (x, y); ++x) + continue; + l= x; + } while (x <= x2); + } +} + +}; + + +#elif 0 + +class Painter { + ColorValue paint, border; + ColorTester test; +public: + Painter (ColorValue paint, ColorValue border) : + paint (paint), + border (border) + { + } + +void do_paint (int x, int y) +{ + using std::vector; + vector <vector <bool> > + visit (screenwidth, vector <bool> (screenheight) ); + std::deque <PPoint> stack; + PPoint pt, pn; + stack.push_back (PPoint (x, y) ); + while (! stack.empty () ) + { + pt= stack.back (); + stack.pop_back (); + + pt.plot (); + pn= PPoint (pt.x, pt.y + 1); + if (pn.check (paint, border, visit, test) ) + stack.push_back (pn); + + pn= PPoint (pt.x, pt.y - 1); + if (pn.check (paint, border, visit, test) ) + stack.push_back (pn); + + pn= PPoint (pt.x + 1, pt.y); + if (pn.check (paint, border, visit, test) ) + stack.push_back (pn); + + pn= PPoint (pt.x - 1, pt.y); + if (pn.check (paint, border, visit, test) ) + stack.push_back (pn); + } +} + +}; + +#else + +class Painter { + ColorValue paint, border; + std::vector <std::vector <bool> > visited; + std::deque <PPoint> processlist, nextlist; + ColorTester test; +public: + Painter (ColorValue paint, ColorValue border) : + paint (paint), + border (border), + visited (screenwidth, std::vector <bool> (screenheight) ) + { + } + bool check (int x, int y) + { + if (! check_limit (x, y) ) + return false; + std::vector <bool>::reference v= visited [x] [y]; + if (v) + return false; + v= true; + ColorValue c= test (x, y); + if (c == paint || c == border) + return false; + return true; + } + void gf (PPoint p) + { + //if (! p.check (paint, border, visited, test) ) + if (! check (p.x, p.y) ) + return; + p.plot (); + nextlist.push_back ( PPoint (p.x, p.y - 1) ); + nextlist.push_back ( PPoint (p.x, p.y + 1) ); + nextlist.push_back ( PPoint (p.x - 1, p.y) ); + nextlist.push_back ( PPoint (p.x + 1, p.y) ); + + } + void do_paint (int x, int y) + { + processlist.push_back (PPoint (x, y) ); + while (! processlist.empty () ) + { + nextlist.clear (); + while (! processlist.empty () ) + { + PPoint cp= processlist.back (); + processlist.pop_back (); + gf (cp); + } + processlist= nextlist; + } + } +}; + +#endif + +#endif + +} // namespace + +#endif +// BLASSIC_HAS_GRAPHICS + +void graphics::paint (int x, int y, int paintattr, int borderattr) +{ + requiregraphics (); + + #ifdef BLASSIC_HAS_GRAPHICS + + transform_x (x); + transform_y (y); + + ColorValue paint= getColorValue (mapcolor (paintattr) ); + ColorValue border= getColorValue (mapcolor (borderattr) ); + + setactivecolor (mapcolor (paintattr).pc); + //do_paint (x, y /*, 0, 0*/, paint, border); + Painter p (paint, border); + p.do_paint (x, y /*, 0, 0*/ ); + setactivecolor (pforeground); + + #else + // No BLASSIC_HAS_GRAPHICS + + touch (x, y, paintattr, borderattr); + + #endif +} + +void graphics::mask (int m) +{ + requiregraphics (); + + #ifdef BLASSIC_HAS_GRAPHICS + + maskvalue= static_cast <unsigned char> (m); + maskpos= 0; + + #else + // No BLASSIC_HAS_GRAPHICS + + touch (m); + + #endif +} + +void graphics::maskdrawfirstpoint (bool f) +{ + requiregraphics (); + + #ifdef BLASSIC_HAS_GRAPHICS + + maskdrawfirst= f; + + #else + // No BLASSIC_HAS_GRAPHICS + + touch (f); + + #endif +} + +#ifdef BLASSIC_HAS_GRAPHICS + +namespace { + +char skipblank (const char * & s) +{ + char c; + while ( (c= * s) == ' ' || c == '\t') + ++s; + return c; +} + +int getnum (const char * & s) +{ + int r= 0; + char c; + if (! isdigit (* s) ) + { + if (showdebuginfo () ) + cerr << "Inavlid number in DRAW string" << endl; + throw ErrSyntax; + } + while ( isdigit (c= * s) ) + { + r*= 10; + r+= c - '0'; + ++s; + } + //cerr << r; + return r; +} + +void drawsyntaxerror () +{ + if (showdebuginfo () ) + cerr << "Invalid syntax in DRAW string" << endl; + throw ErrSyntax; +} + +enum TypeMove { MoveAbs, MovePos, MoveNeg }; + +TypeMove gettypemove (const char * & s) +{ + TypeMove typemove= MoveAbs; + char c= skipblank (s); + switch (c) + { + case '+': + //cerr << '+'; + typemove= MovePos; + ++s; + skipblank (s); + break; + case '-': + //cerr << '-'; + typemove= MoveNeg; + ++s; + skipblank (s); + break; + } + return typemove; +} + +inline void adjust (int & value, TypeMove t, int last) +{ + switch (t) + { + case MoveAbs: + break; + case MovePos: + value= last + value; + break; + case MoveNeg: + value= last - value; + break; + } +} + +} // namespace + +#endif +// BLASSIC_HAS_GRAPHICS + +void graphics::draw (const std::string & str) +{ + requiregraphics (); + + #ifdef BLASSIC_HAS_GRAPHICS + + const char * s= str.c_str (); + char c; + int i; + while ( (c= skipblank (s) ) != '\0') + { + //cerr << c; + ++s; + bool nopaint= false; + if (c == 'B') + { + c= skipblank (s); + //cerr << c; + ++s; + nopaint= true; + } + switch (c) + { + case 'M': + { + TypeMove mx= gettypemove (s); + int x= getnum (s); + adjust (x, mx, lastx); + c= skipblank (s); + if (c != ',') + //throw ErrSyntax; + drawsyntaxerror (); + //cerr << ','; + ++s; + TypeMove my= gettypemove (s); + int y= getnum (s); + adjust (y, my, lasty); + if (nopaint) + graphics::move (x, y); + else + graphics::line (x, y); + } + break; + case 'U': + skipblank (s); + i= getnum (s); + if (nopaint) + graphics::move (lastx, lasty - i); + else + graphics::line (lastx, lasty - i); + break; + case 'D': + skipblank (s); + i= getnum (s); + if (nopaint) + graphics::move (lastx, lasty + i); + else + graphics::line (lastx, lasty + i); + break; + case 'R': + skipblank (s); + i= getnum (s); + if (nopaint) + graphics::move (lastx + i, lasty); + else + graphics::line (lastx + i, lasty); + break; + case 'L': + skipblank (s); + i= getnum (s); + if (nopaint) + graphics::move (lastx - i, lasty); + else + graphics::line (lastx - i, lasty); + break; + case 'E': + skipblank (s); + i= getnum (s); + if (nopaint) + graphics::move (lastx + i, lasty - i); + else + graphics::line (lastx + i, lasty - i); + break; + case 'F': + skipblank (s); + i= getnum (s); + if (nopaint) + graphics::move (lastx + i, lasty + i); + else + graphics::line (lastx + i, lasty + i); + break; + case 'G': + skipblank (s); + i= getnum (s); + if (nopaint) + graphics::move (lastx - i, lasty + i); + else + graphics::line (lastx - i, lasty + i); + break; + case 'H': + skipblank (s); + i= getnum (s); + if (nopaint) + graphics::move (lastx - i, lasty - i); + else + graphics::line (lastx - i, lasty - i); + break; + case 'C': + c= skipblank (s); + if (! isdigit (c) ) + //throw ErrSyntax; + drawsyntaxerror (); + graphics::setcolor (c - '0'); + ++s; + break; + case 'X': + { + std::string x; + while ( (c= *s) != ';' && c != '\0') + { + x+= c; + ++s; + } + if (c != ';') + //throw ErrSyntax; + drawsyntaxerror (); + ++s; + if (typeofvar (x) != VarString) + throw ErrMismatch; + std::string xx= evaluatevarstring (x); + draw (xx); + } + break; + case ';': + break; + default: + //throw ErrSyntax; + drawsyntaxerror (); + } + } + //cerr << endl; + + #else + // No BLASSIC_HAS_GRAPHICS + + touch (str); + + #endif +} + +graphics::Point graphics::getlast () +{ + #ifdef BLASSIC_HAS_GRAPHICS + + return Point (lastx, lasty); + + #else + + return Point (0, 0); + + #endif +} + +std::string graphics::inkey () +{ + #ifdef BLASSIC_HAS_GRAPHICS + + idle (); + if (queuekey.empty () ) + return std::string (); + + #if 0 + + std::string str= queuekey.front (); + queuekey.pop (); + return str; + + #else + + return queuekey.pop (); + + #endif + + #else + // No BLASSIC_HAS_GRAPHICS + + ASSERT (false); + throw ErrBlassicInternal; // Make the compiler happy. + + #endif +} + +bool graphics::pollin () +{ + #ifdef BLASSIC_HAS_GRAPHICS + + idle (); + return ! queuekey.empty (); + + #else + + throw ErrBlassicInternal; // Make the compiler happy. + + #endif +} + +std::string graphics::getkey () +{ + #ifdef BLASSIC_HAS_GRAPHICS + + #ifdef BLASSIC_USE_X + + while (queuekey.empty () ) + wait_X_event (); + return queuekey.pop (); + + #else + + for (;;) + { + idle (); + if (! queuekey.empty () ) + return queuekey.pop (); + + // Reduces cpu usage. + #ifdef BLASSIC_USE_WINDOWS + Sleep (0); + #endif + } + + #endif + + #else + // No BLASSIC_HAS_GRAPHICS + + ASSERT (false); // Must not came here. + throw ErrBlassicInternal; // Make the compiler happy. + + #endif +} + +int graphics::keypressed (int keynum) +{ + requiregraphics (); + + #ifdef BLASSIC_HAS_GRAPHICS + + #if 0 + + if (keynum < 0 || keynum > static_cast <int> (MAXINKEYCODE) ) + return -1; + idle (); + if (! keypressedmap [presscode [keynum] ] ) + return -1; + int r= 0; + const int shiftpressed= 32, ctrlpressed= 128; + + #ifdef BLASSIC_USE_X + if (keypressedmap [XK_Shift_L] || keypressedmap [XK_Shift_R] ) + r|= shiftpressed; + if (keypressedmap [XK_Control_L] || keypressedmap [XK_Control_R] ) + r|= ctrlpressed; + #endif + + return r; + + #else + return ::keypressed (keynum); + #endif + + #else + // No graphics. + + touch (keynum); + ASSERT (false); // Must not came here. + throw ErrBlassicInternal; // Make the compiler happy. + + #endif +} + +namespace { + +int symbol_after_is; + +inline bool iscontrolchar (char c) +{ + //return iscntrl (static_cast <unsigned char> (c) ); + unsigned char c1= static_cast <unsigned char> (c); + return c1 < 32; +} + +} // namespace + +#ifdef BLASSIC_HAS_GRAPHICS + +namespace { + +class BlWindow { + size_t collecting_params; + std::string params; + unsigned char actual_control; + struct DefControlChar { + size_t nparams; + bool force; + void (BlWindow::* action) (); + DefControlChar (size_t nparams, bool force, + void (BlWindow::* action) () ) : + nparams (nparams), + force (force), + action (action) + { } + // Default constructor required by the initialization + // of the escape map (I don't want to use insert + // instead of [ ] ). + DefControlChar () + { } + }; + static const DefControlChar control []; + typedef std::map <char, DefControlChar> escape_t; + static const escape_t escape; + static escape_t init_escape (); + void ignore () + { /* Nothing to do */ } + void do_SOH () // 1 + { + do_charout (params [0] ); + } + void do_STX () // 2 + { + cursor_visible= false; + } + void do_ETX () // 3 + { + cursor_visible= true; + } + void do_ENQ () // 5 + { + tag_charout (params [0] ); + } + void do_BEL () // 7 + { + graphics::ring (); + } + void do_BS () // 8 + { + --x; + } + void do_TAB () // 9 + { + ++x; + } + void do_LF () // 10 + { + //x= 0; + ++y; + } + void do_VT () // 11 + { + --y; + } + void do_FF () // 12 + { + cls (); + } + void do_CR () // 13 + { + x= 0; + } + void do_SO () // 14 + { + setbackground (static_cast <unsigned char> (params [0] ) % 16); + } + void do_SI () // 15 + { + setcolor (static_cast <unsigned char> (params [0] ) % 16 ); + } + void do_DLE () // 16 + { + clear_rectangle (x, x, y, y); + } + void do_DC1 () // 17 + { + clear_from_left (); + } + void do_DC2 () // 18 + { + clear_to_right (); + } + void do_DC3 () // 19 + { + clear_from_begin (); + } + void do_DC4 () // 20 + { + clear_to_end (); + } + void do_SYN () // 22 + { + graphics::settransparent + (static_cast <unsigned char> (params [0] ) ); + } + void do_ETB () // 23 + { + // Ink mode, only CPC modes are allowed. + graphics::setdrawmode + (static_cast <unsigned char> (params [0] ) % 4); + } + void do_CAN () // 24 + { + std::swap (foreground, background); + } + void do_EM () // 25 + { + int symbol= static_cast <unsigned char> (params [0] ); + // Avoid generate an error if out of range. + if (symbol < symbol_after_is) + return; + unsigned char byte [8]; + params.copy (reinterpret_cast <char *> (& byte [0] ), 8, 1); + graphics::definesymbol (symbol, byte); + } + void do_SUB () // 26 + { + set (static_cast <unsigned char> (params [0] ), + static_cast <unsigned char> (params [1] ), + static_cast <unsigned char> (params [2] ), + static_cast <unsigned char> (params [3] ) ); + } + void do_ESC () // 27 + { + char c= params [0]; + escape_t::const_iterator it= escape.find (c); + if (it == escape.end () ) + { + do_charout (c); + return; + } + const DefControlChar & defcontrol= it->second; + if (defcontrol.nparams == 0) + { + if (defcontrol.force) + forcelegalposition (); + (this->* defcontrol.action) (); + return; + } + collecting_params= defcontrol.nparams; + // All escapes used have non-control codes, + // then we can use the character code + // without confusion. + ASSERT (! iscontrolchar (c) ); + + actual_control= c; + params.erase (); + } + void do_FS () // 28 + { + int ink= static_cast <unsigned char> (params [0] ) % 16; + int color= static_cast <unsigned char> (params [1] ) % 32; + if (color > 26) + return; + // Third parameter (flashing ink) ignored. + graphics::ink (ink, color); + } + void do_RS () // 30 + { + x= 0; y= 0; + } + void do_US () // 31 + { + int xx= static_cast <unsigned char> (params [0] ); + int yy= static_cast <unsigned char> (params [1] ); + if (xx == 0 || yy == 0) + return; + x= xx - 1; y= yy - 1; + } + void do_ESC_A () + { + if (y > 0) + --y; + } + void do_ESC_B () + { + if (y < height - 1) + ++y; + } + void do_ESC_C () + { + if (x < width - 1) + ++x; + } + void do_ESC_D () + { + if (x > 0) + --x; + } + void do_ESC_E () + { + clear_rectangle (0, width - 1, 0, height - 1); + } + void do_ESC_H () + { + x= 0; y= 0; + } + void do_ESC_I () + { + --y; + } + void do_ESC_J () + { + clear_to_end (); + } + void do_ESC_K () + { + clear_to_right (); + } + void do_ESC_L () + { + textscrollinverse (y); + } + void do_ESC_M () + { + textscroll (y); + } + void do_ESC_N () + { + deletechar (); + } + void do_ESC_Y () + { + y= static_cast <unsigned char> (params [0] ) - 32; + x= static_cast <unsigned char> (params [1] ) - 32; + if (y > height - 1) + y= height - 1; + if (x > width - 1) + x= width - 1; + } + void do_ESC_d () + { + clear_from_begin (); + } + void do_ESC_e () + { + cursor_visible= true; + } + void do_ESC_f () + { + cursor_visible= false; + } + void do_ESC_j () + { + savex= x; savey= y; + } + void do_ESC_k () + { + x= savex; y= savey; + } + void do_ESC_l () + { + clear_rectangle (0, width - 1, y, y); + } + void do_ESC_o () + { + clear_from_left (); + } + void do_ESC_p () + { + inverse= true; + //std::swap (foreground, background); + } + void do_ESC_q () + { + inverse= false; + } + void do_ESC_r () + { + underline= true; + } + void do_ESC_u () + { + underline= false; + } + void do_ESC_x () + { + // set mode 24 x 80 + setdefault (); + cls (); + set (0, 79, 0, 23); + } + void do_ESC_y () + { + // unset mode 24 x 80 + setdefault (); + cls (); + } +public: + BlWindow () : + collecting_params (0), + fTag (false), + cursor_visible (true), + inverse (false), + bright (false), + underline (false) + { + setdefault (); + defaultcolors (); + } + BlWindow (int x1, int x2, int y1, int y2) : + collecting_params (0), + fTag (false), + cursor_visible (true), + inverse (false), + bright (false), + underline (false) + { + set (x1, x2, y1, y2); + defaultcolors (); + } + void setdefault () + { + TRACEFUNC (tr, "BlWindow::setdefault"); + + set (0, maxtcol - 1, 0, maxtrow - 1); + } + void set (int x1, int x2, int y1, int y2) + { + TRACEFUNC (tr, "BlWindow::set"); + + if (x1 < 0 || x2 < 0 || y1 < 0 || y2 < 0) + { + TRMESSAGE (tr, "Invalid window values"); + throw ErrImproperArgument; + } + if (x1 > x2) std::swap (x1, x2); + if (y1 > y2) std::swap (y1, y2); + if (x1 >= maxtcol) x1= maxtcol - 1; + if (x2 >= maxtcol) x2= maxtcol - 1; + if (y1 >= maxtrow) y1= maxtrow - 1; + if (y2 >= maxtrow) y2= maxtrow - 1; + orgx= x1; orgy= y1; + width= x2 - x1 + 1; + height= y2 - y1 + 1; + x= y= 0; + } + void defaultcolors () + { + pen= default_pen; + paper= default_paper; + foreground= mapcolor (default_pen).pc; + background= mapcolor (default_paper).pc; + } + void setinverse (bool active) { inverse= active; } + bool getinverse () { return inverse; } + void setbright (bool active) + { + bool previous= bright; + bright= active; + // PENDIENTE + if (bright != previous) + { + int color= pen; + if (color >= 0 && color <= 7) + { + if (bright) + color+= 8; + foreground= mapcolor (color).pc; + } + color= paper; + if (color >= 0 && color <= 7) + { + if (bright) + color+= 8; + background= mapcolor (color).pc; + } + } + } + bool getbright () { return bright; } + int getwidth () const { return width; } + int getxpos () const { return x; } + int getypos () const { return y; } + void gotoxy (int x, int y) + { + this->x= x; this->y= y; + } + void forcelegalposition () + { + if (x >= width) + { x= 0; ++y; } + if (x < 0) + { x= width - 1; --y; } + if (y < 0) + { + y= 0; + textscrollinverse (0); + } + if (y >= height) + { + textscroll (0); + y= height - 1; + } + } + void tab () + { + forcelegalposition (); + int zone= sysvar::get16 (sysvar::Zone); + if (zone == 0) + zone= 8; + if (x >= (width / zone) * zone) + { + //cerr << "Fin de linea" << endl; + int yy= orgy + y; + for ( ; x < width; ++x) + print (orgx + x, yy, ' ', inverse, underline); + x= 0; + ++y; + } + else + { + int yy= orgy + y; + do { + print (orgx + x, yy, ' ', inverse, underline); + ++x; + } while (x % zone); + } + } + void tab (size_t n) + { + forcelegalposition (); + int col= n; + if (x > col) + { + do { + charout (' '); + } while (x < width); + } + int maxpos= std::min (col, width); + while (x < maxpos) + charout (' '); + x= col; + } + void setcolor (int color) + { + pen= color; + if (bright && color >=0 && color <= 7) + color+= 8; + foreground= mapcolor (color).pc; + } + int getcolor () + { + return pen; + } + void setbackground (int color) + { + paper= color; + if (bright && color >=0 && color <= 7) + color+= 8; + background= mapcolor (color).pc; + } + int getbackground () + { + return paper; + } + void movecharforward () + { + forcelegalposition (); + ++x; + } + void movecharforward (size_t n) + { + for ( ; n > 0; --n) + movecharforward (); + } + void movecharback () + { + forcelegalposition (); + --x; + } + void movecharback (size_t n) + { + for ( ; n > 0; --n) + movecharback (); + } + void movecharup () + { + forcelegalposition (); + --y; + } + void movecharup (size_t n) + { + for ( ; n > 0; --n) + movecharup (); + } + void movechardown () + { + forcelegalposition (); + ++y; + } + void movechardown (size_t n) + { + for ( ; n > 0; --n) + movechardown (); + } + void clear_from_left () + { + clear_rectangle (0, x, y, y); + } + void clear_to_right () + { + clear_rectangle (x, width - 1, y, y); + } + void clear_from_begin () + { + if (y > 0) + clear_rectangle (0, width - 1, 0, y - 1); + clear_from_left (); + } + void clear_to_end () + { + clear_to_right (); + if (y < height - 1) + clear_rectangle (0, width - 1, y + 1, height - 1); + } + void cls () + { + x= y= 0; + clear_rectangle (0, width - 1, 0, height - 1); + } + void clear_rectangle (int left, int right, int top, int bottom) + { + #if 1 + + int x1= (orgx + left) * charwidth; + int x2= (orgx + right + 1) * charwidth; + int y1= (orgy + top) * charheight; + int y2= (orgy + bottom + 1) * charheight; + setactivecolor (background); + do_fill_rectangle (x1, y1, x2 - 1, y2 - 1, false); + setactivecolor (pforeground); + + #else + + int x1= (orgx + left) * charwidth; + int w= (right - left + 1) * charwidth; + int y1= (orgy + top) * charheight; + int h= (bottom - top + 1) * charheight; + + #ifdef BLASSIC_USE_WINDOWS + + RECT r= { x1, y1, x1 + w, y1 + h }; + LOGPEN logpen; + GetObject (* background, sizeof (LOGPEN), & logpen); + HBRUSH hbrush= CreateSolidBrush (logpen.lopnColor); + if (! fSynchro) + FillRect (hdc, & r, hbrush); + FillRect (hdcPixmap, & r, hbrush); + DeleteObject (hbrush); + + #endif + + #ifdef BLASSIC_USE_X + + setactivecolor (background); + XSetFunction (display, gcp, drawmode_copy); + XFillRectangle (display, pixmap, gcp, + x1, y1, w, h); + XSetFunction (display, gcp, drawmode); + if (! fSynchro) + { + XSetFunction (display, gc, drawmode_copy); + XFillRectangle (display, window, gc, + x1, y1, w, h); + XSetFunction (display, gc, drawmode); + // Inserted an idle call because without it + // the window sometimes is not updated. + graphics::idle (); + } + setactivecolor (pforeground); + + #endif + + #endif + } + void deletechar () + { + int x1= (orgx + x) * charwidth; + int y1= (orgy + y) * charheight; + int w= (width - x - 1) * charwidth; + int h= charheight; + + #ifdef BLASSIC_USE_X + + XSetFunction (display, gcp, drawmode_copy); + XCopyArea (display, pixmap, pixmap, gcp, + x1 + charwidth, y1, w, h, x1, y1); + setactivecolor (background); + XFillRectangle (display, pixmap, gcp, + x1 + w, y1, charwidth, h); + if (! fSynchro) + reinit_window (x1, y1, w + charwidth, h); + setactivecolor (foreground); + + #elif defined BLASSIC_USE_WINDOWS + + RECT r= { x1 + w, y1, x1 + w + charwidth, y1 + h }; + BitBlt (hdcPixmap, x1, y1, w, h, + hdcPixmap, x1 + charwidth, y1, SRCCOPY); + LOGPEN logpen; + GetObject (* background, sizeof (LOGPEN), & logpen); + HBRUSH hbrush= CreateSolidBrush (logpen.lopnColor); + FillRect (hdcPixmap, & r, hbrush); + DeleteObject (hbrush); + if (! fSynchro) + reinit_window (x1, y1, w + charwidth, h); + + #endif + } + void textscroll (int fromline) + { + int x1= orgx * charwidth; + int y1= (orgy + fromline) * charheight; + int w= width * charwidth; + int h= (height - 1 - fromline) * charheight; + int x2= x1 + w; + int y2= y1 + h; + + // Reinit rectangle. This are not rotated because + // do_fill_rectangle will do it. + int x1reinit= x1; + int y1reinit= y1; + int x2reinit= x1 + w; + int y2reinit= y1 + h + charheight; + + int x1fill= x1; + int y1fill= y1 + h; + int x2fill= x1 + w - 1; + int y2fill= y1 + h + charheight - 1; + + do_rotate (x1, y1); + do_rotate (x2, y2); + if (x1 > x2) + std::swap (x1, x2); + if (y1 > y2) + std::swap (y1, y2); + do_rotate_rel (w, h); + + int x1src= x1; + int y1src= y1; + switch (rotate) + { + case RotateNone: + y1src+= charheight; + break; + case Rotate90: + x1src+= charheight; + break; + } + + do_rotate (x1reinit, y1reinit); + do_rotate (x2reinit, y2reinit); + if (x1reinit > x2reinit) + std::swap (x1reinit, x2reinit); + if (y1reinit > y2reinit) + std::swap (y1reinit, y2reinit); + + // Operate only in the pixmap, when finished update + // the visible window if not in synchro mode. + + // Do the scrolling. + + #ifdef BLASSIC_USE_X + + XSetFunction (display, gcp, drawmode_copy); + XCopyArea (display, pixmap, pixmap, gcp, + x1src, y1src, w, h, x1, y1); + + #if 0 + setactivecolor (background); + XFillRectangle (display, pixmap, gcp, + x1, y1 + h, w, charheight); + #endif + + XSetFunction (display, gcp, drawmode); + + #elif defined BLASSIC_USE_WINDOWS + + BitBlt (hdcPixmap, x1, y1, w, h, + hdcPixmap, x1src, y1src, SRCCOPY); + + #if 0 + LOGPEN logpen; + GetObject (* background, sizeof (LOGPEN), & logpen); + HBRUSH hbrush= CreateSolidBrush (logpen.lopnColor); + RECT r = { x1, y1 + h, x1 + w, y1 + h + charheight}; + FillRect (hdcPixmap, & r, hbrush); + DeleteObject (hbrush); + #endif + + #endif + + // Fill the new line with the background color. + setactivecolor (background); + do_fill_rectangle (x1fill, y1fill, x2fill, y2fill, false); + setactivecolor (foreground); + + // Test. + //#ifdef BLASSIC_USE_X + #if 0 + + // Generate a expose event for the scrolled area. + XExposeEvent event; + event.type= Expose; + event.display= display; + event.window= window; + event.x= x1; + event.y= y1; + event.width= w; + event.height= h; + event.count= 0; + XSendEvent (display, window, False, 0, + reinterpret_cast <XEvent *> (& event) ); + + #else + + // And update window if not in synchro mode. + if (! fSynchro) + //reinit_window (x1, y1, w, h + charheight); + reinit_window (x1reinit, y1reinit, + x2reinit - x1reinit, y2reinit - y1reinit); + + #endif + } + void textscrollinverse (int fromline) + { + int x1= orgx * charwidth; + int y1= (orgy + fromline) * charheight; + int w= width * charwidth; + int h= (height - 1 - fromline) * charheight; + + do_rotate (x1, y1); + do_rotate (w, h); + + #ifdef BLASSIC_USE_X + + XSetFunction (display, gcp, drawmode_copy); + XCopyArea (display, pixmap, pixmap, gcp, + x1, y1, w, h, x1, y1 + charheight); + setactivecolor (background); + XFillRectangle (display, pixmap, gcp, + x1, y1, w, charheight); + XSetFunction (display, gcp, drawmode); + + if (! fSynchro) + reinit_window (x1, y1, w, h + charheight); + setactivecolor (foreground); + + #elif defined BLASSIC_USE_WINDOWS + + RECT r = { x1, y1, x1 + w, y1 + charheight}; + BitBlt (hdcPixmap, x1, y1 + charheight, w, h, + hdcPixmap, x1, y1, SRCCOPY); + LOGPEN logpen; + GetObject (* background, sizeof (LOGPEN), & logpen); + HBRUSH hbrush= CreateSolidBrush (logpen.lopnColor); + FillRect (hdcPixmap, & r, hbrush); + DeleteObject (hbrush); + if (! fSynchro) + reinit_window (x1, y1, w, h + charheight); + + #endif + } + void scroll (int n) + { + forcelegalposition (); + if (n < 0) + { + for ( ; n < 0; ++n) + textscrollinverse (0); + gotoxy (0, 0); + } + else + { + for ( ; n > 0; --n) + textscroll (0); + gotoxy (0, height - 1); + } + } + void tag_charout (char c) + { + int x= lastx, y= lasty; + lastx+= charwidth; + transform_x (x); + transform_y (y); + printxy (x, y, c, true); + } + void charout (char c) + { + TRACEFUNC (tr, "BlWindow::charout"); + + if (fTag) + { + tag_charout (c); + return; + } + if (collecting_params) + { + TRMESSAGE (tr, "collecting params"); + params+= c; + if (--collecting_params == 0) + { + const DefControlChar & defcontrol= + actual_control < 32 ? + control [actual_control] : + escape.find (actual_control)->second; + if (defcontrol.force) + forcelegalposition (); + (this->* defcontrol.action) (); + } + return; + } + if (iscontrolchar (c) ) + { + TRMESSAGE (tr, "Is control char"); + actual_control= c; + params.erase (); + const DefControlChar & defcontrol= + control [actual_control]; + if (defcontrol.nparams > 0) + collecting_params= defcontrol.nparams; + else + { + if (defcontrol.force) + forcelegalposition (); + (this->* defcontrol.action) (); + } + return; + } + forcelegalposition (); + pcolor foresave= pforeground; + pforeground= foreground; + pcolor backsave= pbackground; + pbackground= background; + #if 0 + switch (c) + { + case '\n': + x= 0; + ++y; + break; + case '\b': + --x; + break; + case '\r': + x= 0; + break; + case '\t': + if (x >= (width / zone) * zone) + { + //cerr << "Fin de linea" << endl; + int yy= orgy + y; + for ( ; x < width; ++x) + print (orgx + x, yy, ' ', + inverse, underline); + x= 0; + ++y; + } + else + { + int yy= orgy + y; + do { + print (orgx + x, yy, ' ', + inverse, underline); + ++x; + } while (x % zone); + } + break; + default: + #endif + print (orgx + x, orgy + y, c, inverse, underline); + ++x; + #if 0 + } + #endif + pforeground= foresave; + pbackground= backsave; + } + void do_charout (char c) + { + pcolor foresave= pforeground; + pforeground= foreground; + pcolor backsave= pbackground; + pbackground= background; + print (orgx + x, orgy + y, c, inverse, underline); + pforeground= foresave; + pbackground= backsave; + ++x; + } + void invertcursor () + { + forcelegalposition (); + if (! cursor_visible) + return; + int x1= (orgx + x) * charwidth; + int y1= (orgy + y) * charheight; + int x1ini= x1; + int y1ini= y1 + charheight - 2; + int x1end= x1 + charwidth; + int y1end= y1 + charheight; + do_rotate (x1, y1); + do_rotate (x1ini, y1ini); + do_rotate (x1end, y1end); + if (x1ini > x1end) + std::swap (x1ini, x1end); + if (y1ini > y1end) + std::swap (y1ini, y1end); + + #ifdef BLASSIC_USE_X + + XSetFunction (display, gc, drawmode_invert); + XSetFunction (display, gcp, drawmode_invert); + XFillRectangle (display, window, gc, + x1ini, y1ini, x1end - x1ini, y1end - y1ini); + XFillRectangle (display, pixmap, gcp, + x1ini, y1ini, x1end - x1ini, y1end - y1ini); + XSetFunction (display, gc, drawmode); + XSetFunction (display, gcp, drawmode); + + #elif defined BLASSIC_USE_WINDOWS + + HBRUSH hbrush= (HBRUSH) GetStockObject (BLACK_BRUSH); + HDC ahdc [2]= { hdc, hdcPixmap }; + for (size_t i= 0; i < 2; ++i) + { + HDC hdc= ahdc [i]; + SetROP2 (hdc, drawmode_invert); + HBRUSH hold= (HBRUSH) SelectObject (hdc, hbrush); + //Rectangle (hdc, x1, y1 + 6, x1 + 8, y1 + 8); + Rectangle (hdc, x1ini, y1ini, x1end, y1end); + SelectObject (hdc, hold); + SetROP2 (hdc, drawmode); + } + + #endif + } + std::string copychr (BlChar from, BlChar to) + { + // I don't tested yet if that is done in the cpc + forcelegalposition (); + + int x1= (orgx + x) * charwidth; + int y1= (orgy + y) * charheight; + return copychrat (x1, y1, from , to); + } + void tag () + { + fTag= true; + } + void tagoff () + { + fTag= false; + } + bool istagactive () + { + return fTag; + } +private: + int orgx, orgy, width, height; + pcolor foreground; + pcolor background; + int x, y, savex, savey; + bool fTag; + bool cursor_visible; + bool inverse; + bool bright; + bool underline; + int pen; + int paper; +}; + +const BlWindow::DefControlChar BlWindow::control [32]= { + BlWindow::DefControlChar (0, false, & BlWindow::ignore), // NUL + BlWindow::DefControlChar (1, true, & BlWindow::do_SOH), // SOH + BlWindow::DefControlChar (0, false, & BlWindow::do_STX), // STX + BlWindow::DefControlChar (0, false, & BlWindow::do_ETX), // ETX + BlWindow::DefControlChar (1, false, & BlWindow::ignore), // EOT + BlWindow::DefControlChar (1, false, & BlWindow::do_ENQ), // ENQ + BlWindow::DefControlChar (0, false, & BlWindow::ignore), // ACK + BlWindow::DefControlChar (0, false, & BlWindow::do_BEL), // BEL + BlWindow::DefControlChar (0, true, & BlWindow::do_BS ), // BS + BlWindow::DefControlChar (0, true, & BlWindow::do_TAB), // TAB + BlWindow::DefControlChar (0, true, & BlWindow::do_LF ), // LF + BlWindow::DefControlChar (0, true, & BlWindow::do_VT ), // VT + BlWindow::DefControlChar (0, false, & BlWindow::do_FF ), // FF + BlWindow::DefControlChar (0, true, & BlWindow::do_CR ), // CR + BlWindow::DefControlChar (1, false, & BlWindow::do_SO ), // SO + BlWindow::DefControlChar (1, false, & BlWindow::do_SI ), // SI + BlWindow::DefControlChar (0, true, & BlWindow::do_DLE), // DLE + BlWindow::DefControlChar (0, true, & BlWindow::do_DC1), // DC1 + BlWindow::DefControlChar (0, true, & BlWindow::do_DC2), // DC2 + BlWindow::DefControlChar (0, true, & BlWindow::do_DC3), // DC3 + BlWindow::DefControlChar (0, true, & BlWindow::do_DC4), // DC4 + BlWindow::DefControlChar (0, false, & BlWindow::ignore), // NAK + BlWindow::DefControlChar (1, false, & BlWindow::do_SYN), // SYN + BlWindow::DefControlChar (1, false, & BlWindow::do_ETB), // ETB + BlWindow::DefControlChar (0, false, & BlWindow::do_CAN), // CAN + BlWindow::DefControlChar (9, false, & BlWindow::do_EM ), // EM + BlWindow::DefControlChar (4, false, & BlWindow::ignore), // SUB + BlWindow::DefControlChar (1, false, & BlWindow::do_ESC), // ESC + BlWindow::DefControlChar (3, false, & BlWindow::do_FS ), // FS + BlWindow::DefControlChar (2, false, & BlWindow::ignore), // GS + BlWindow::DefControlChar (0, false, & BlWindow::do_RS ), // RS + BlWindow::DefControlChar (2, false, & BlWindow::do_US ), // US +}; + +BlWindow::escape_t BlWindow::init_escape () +{ + escape_t aux; + DefControlChar ignore0 (0, false, & BlWindow::ignore); + DefControlChar ignore1 (1, false, & BlWindow::ignore); + aux ['0']= ignore0; // Status line inactive. + aux ['1']= ignore0; // Status line active. + aux ['2']= ignore1; // Select national character set + aux ['3']= ignore1; // Set mode + aux ['A']= DefControlChar (0, true, & BlWindow::do_ESC_A); + aux ['B']= DefControlChar (0, true, & BlWindow::do_ESC_B); + aux ['C']= DefControlChar (0, true, & BlWindow::do_ESC_C); + aux ['D']= DefControlChar (0, true, & BlWindow::do_ESC_D); + aux ['E']= DefControlChar (0, false, & BlWindow::do_ESC_E); + aux ['H']= DefControlChar (0, false, & BlWindow::do_ESC_H); + aux ['I']= DefControlChar (0, true, & BlWindow::do_ESC_I); + aux ['J']= DefControlChar (0, true, & BlWindow::do_ESC_J); + aux ['K']= DefControlChar (0, true, & BlWindow::do_ESC_K); + aux ['L']= DefControlChar (0, true, & BlWindow::do_ESC_L); + aux ['M']= DefControlChar (0, true, & BlWindow::do_ESC_M); + aux ['N']= DefControlChar (0, true, & BlWindow::do_ESC_N); + aux ['Y']= DefControlChar (2, false, & BlWindow::do_ESC_Y); + aux ['d']= DefControlChar (0, true, & BlWindow::do_ESC_d); + aux ['e']= DefControlChar (0, false, & BlWindow::do_ESC_e); + aux ['f']= DefControlChar (0, false, & BlWindow::do_ESC_f); + aux ['j']= DefControlChar (0, false, & BlWindow::do_ESC_j); + aux ['k']= DefControlChar (0, false, & BlWindow::do_ESC_k); + aux ['l']= DefControlChar (0, true, & BlWindow::do_ESC_l); + aux ['o']= DefControlChar (0, true, & BlWindow::do_ESC_o); + aux ['p']= DefControlChar (0, false, & BlWindow::do_ESC_p); + aux ['q']= DefControlChar (0, false, & BlWindow::do_ESC_q); + aux ['r']= DefControlChar (0, false, & BlWindow::do_ESC_r); + aux ['u']= DefControlChar (0, false, & BlWindow::do_ESC_u); + aux ['x']= DefControlChar (0, false, & BlWindow::do_ESC_x); + aux ['y']= DefControlChar (0, false, & BlWindow::do_ESC_y); + return aux; +} + +const BlWindow::escape_t BlWindow::escape= BlWindow::init_escape (); + +BlWindow windowzero; + +//typedef std::map <BlChannel, BlWindow *> MapWindow; + +// Map encapsultated to check access. Windows must be accessed only +// throug file, then accesing a window that not exists is a internal +// error. Also destruction of windows is now granted. + +class MapWindow { + typedef std::map <BlChannel, BlWindow *> map_type; + map_type mw; +public: + typedef map_type::iterator iterator; + typedef map_type::const_iterator const_iterator; + typedef map_type::value_type value_type; + typedef map_type::key_type key_type; + typedef map_type::mapped_type mapped_type; + + ~MapWindow (); + iterator begin (); + iterator end (); + const_iterator begin () const; + const_iterator end () const; + // operators [] are checked, fail if the key not exist. + mapped_type operator [] (key_type n) const; + iterator find (key_type n); + static void killwindowifnotzero (const value_type & vt); + void clear (); + void insert (const value_type & val); + void erase (iterator pos); +}; + +MapWindow::~MapWindow () +{ + // Ensure window destruction on exit. + clear (); +} + +MapWindow::iterator MapWindow::begin () +{ + return mw.begin (); +} + +MapWindow::iterator MapWindow::end () +{ + return mw.end (); +} + +MapWindow::const_iterator MapWindow::begin () const +{ + return mw.begin (); +} + +MapWindow::const_iterator MapWindow::end () const +{ + return mw.end (); +} + +MapWindow::mapped_type MapWindow::operator [] (key_type n) const +{ + const_iterator it= mw.find (n); + if (it == end () ) + { + if (showdebuginfo () ) + cerr << "Trying to access to window " << + n << " that not exists" << endl; + throw ErrBlassicInternal; + } + return it->second; +} + +MapWindow::iterator MapWindow::find (key_type n) +{ + return mw.find (n); +} + +void MapWindow::killwindowifnotzero (const value_type & vt) +{ + if (vt.first != BlChannel (0) ) + delete vt.second; +} + +void MapWindow::clear () +{ + std::for_each (begin (), end (), + & MapWindow::killwindowifnotzero); + mw.clear (); +} + +void MapWindow::insert (const value_type & val) +{ + // Fail if already exist. + std::pair <iterator, bool> r= mw.insert (val); + if (! r.second) + { + if (showdebuginfo () ) + cerr << "Trying to create window " << + val.first << " that already exists" << + endl; + throw ErrBlassicInternal; + } +} + +void MapWindow::erase (iterator pos) +{ + // Unchecked, actually. + mw.erase (pos); +} + + +MapWindow mapwindow; + +void destroy_text_windows () +{ + mapwindow.clear (); +} + +void recreate_windows () +{ + TRACEFUNC (tr, "recreate_windows"); + + windowzero.setdefault (); + windowzero.defaultcolors (); + windowzero.cls (); + + //std::for_each (mapwindow.begin (), mapwindow.end (), + // killwindowifnotzero); + //mapwindow.clear (); + destroy_text_windows (); + + //mapwindow [0]= & windowzero; + mapwindow.insert (std::make_pair (BlChannel (0), & windowzero) ); +} + +#if 0 +inline void do_charout (char c) +{ + switch (c) + { + case '\n': + tcol= 0; + if (++trow >= maxtrow) + { + textscroll (); + trow= maxtrow - 1; + } + return; + case '\r': + tcol= 0; + return; + case '\t': + { + int zone= sysvar::get16 (sysvar::Zone); + if (zone == 0) + zone= 8; + if (tcol >= (maxtcol / zone) * zone) + { + //cerr << "Fin de linea" << endl; + for ( ; tcol < maxtcol; ++tcol) + print (tcol, trow, ' ', false); + tcol= 0; + if (++trow >= maxtrow) + { + textscroll (); + trow= maxtrow - 1; + } + } + else + { + do { + print (tcol, trow, ' ', false); + ++tcol; + } while (tcol % zone); + } + return; + } + } + print (tcol, trow, c, false); + if (++tcol >= maxtcol) + { + tcol= 0; + if (++trow >= maxtrow) + { + textscroll (); + trow= maxtrow - 1; + } + } +} +#endif + +} // namespace + +#endif +// BLASSIC_HAS_GRAPHICS + +void graphics::setcolor (BlChannel ch, int color) +{ + #ifdef BLASSIC_HAS_GRAPHICS + + mapwindow [ch]->setcolor (color); + + #else + + touch (ch, color); + no_graphics_support (); + + #endif +} + +int graphics::getcolor (BlChannel ch) +{ + #ifdef BLASSIC_HAS_GRAPHICS + + return mapwindow [ch]->getcolor (); + + #else + + touch (ch); + no_graphics_support (); + throw ErrBlassicInternal; // Make the compiler happy + + #endif +} + +void graphics::setbackground (BlChannel ch, int color) +{ + #ifdef BLASSIC_HAS_GRAPHICS + + mapwindow [ch]->setbackground (color); + + #else + + touch (ch, color); + no_graphics_support (); + + #endif +} + +int graphics::getbackground (BlChannel ch) +{ + #ifdef BLASSIC_HAS_GRAPHICS + + return mapwindow [ch]->getbackground (); + + #else + + touch (ch); + no_graphics_support (); + throw ErrBlassicInternal; + + #endif +} + +void graphics::cls (BlChannel n) +{ + #ifdef BLASSIC_HAS_GRAPHICS + + mapwindow [n]->cls (); + + #else + + touch (n); + no_graphics_support (); + + #endif +} + +void graphics::definewindow (BlChannel n, int x1, int x2, int y1, int y2) +{ + requiregraphics (); + + #ifdef BLASSIC_HAS_GRAPHICS + + --x1; --x2; --y1; --y2; + MapWindow::iterator it= mapwindow.find (n); + if (it != mapwindow.end () ) + it->second->set (x1, x2, y1, y2); + else + { + //mapwindow [n]= new BlWindow (x1, x2, y1, y2); + + // auto_ptr protects the new window in case + // that mapwindows.insert throws. + auto_ptr <BlWindow> pwin (new BlWindow (x1, x2, y1, y2) ); + mapwindow.insert (std::make_pair (n, pwin.get () ) ); + pwin.release (); + } + + #else + + touch (n, x1, x2, y1, y2); + + #endif +} + +void graphics::undefinewindow (BlChannel n) +{ + if (n == 0) + return; + + #ifdef BLASSIC_HAS_GRAPHICS + + MapWindow::iterator it= mapwindow.find (n); + if (it != mapwindow.end () ) + { + delete it->second; + mapwindow.erase (it); + } + + #else + + ASSERT (false); + no_graphics_support (); + + #endif +} + +#if 0 +size_t graphics::getlinewidth () +{ + #ifdef BLASSIC_HAS_GRAPHICS + + #if 0 + return maxtcol; + #else + return windowzero.getwidth (); + #endif + + #else + + return 0; + + #endif +} +#endif + +size_t graphics::getlinewidth (BlChannel ch) +{ + #ifdef BLASSIC_HAS_GRAPHICS + + return mapwindow [ch]->getwidth (); + + #else + + touch (ch); + no_graphics_support (); + return 0; // Make the compiler happy + + #endif +} + +#if 0 +void graphics::charout (char c) +{ + #ifdef BLASSIC_HAS_GRAPHICS + + //do_charout (c); + windowzero.charout (c); + //idle (); + + #else + + touch (c); + + #endif +} + +void graphics::stringout (const std::string & str) +{ + #ifdef BLASSIC_HAS_GRAPHICS + + for (std::string::size_type i= 0, l= str.size (); i < l; ++i) + windowzero.charout (str [i]); + + #else + + touch (str); + + #endif +} +#endif + +void graphics::charout (BlChannel ch, char c) +{ + #ifdef BLASSIC_HAS_GRAPHICS + + mapwindow [ch]->charout (c); + + #else + + touch (ch, c); + + #endif +} + +void graphics::stringout (BlChannel ch, const std::string & str) +{ + TRACEFUNC (tr, "graphics::stringout"); + TRMESSAGE (tr, "Channel: " + to_string (ch) ); + TRMESSAGE (tr, "String: " + str); + + #ifdef BLASSIC_HAS_GRAPHICS + + BlWindow * pwin= mapwindow [ch]; + if (pwin == NULL) + throw ErrBlassicInternal; + for (std::string::size_type i= 0, l= str.size (); i < l; ++i) + pwin->charout (str [i]); + + #else + + touch (ch, str); + + #endif +} + +std::string graphics::copychr (BlChannel ch, BlChar from, BlChar to) +{ + #ifdef BLASSIC_HAS_GRAPHICS + + BlWindow * pwin= mapwindow [ch]; + return pwin->copychr (from, to); + + #else + + touch (ch, from, to); + ASSERT (false); + throw ErrBlassicInternal; + + #endif +} + +std::string graphics::screenchr (int x, int y) +{ + requiregraphics (); + + #ifdef BLASSIC_HAS_GRAPHICS + + return copychrat (x * charwidth, y * charheight, 0, 255); + + #else + + touch (x, y); + ASSERT (false); + throw ErrBlassicInternal; + + #endif +} + +#if 0 +void graphics::gotoxy (int x, int y) +{ + #ifdef BLASSIC_HAS_GRAPHICS + + trow= y; + tcol= x; + + #else + + touch (x, y); + + #endif +} +#endif + +void graphics::gotoxy (BlChannel ch, int x, int y) +{ + TRACEFUNC (tr, "graphics::gotoxy"); + TRMESSAGE (tr, "Channel: " + to_string (ch) + ", x= " + + to_string (x) + ", y= " + to_string (y) ); + + #ifdef BLASSIC_HAS_GRAPHICS + + mapwindow [ch]->gotoxy (x, y); + + #else + + touch (ch, x, y); + + #endif +} + +#if 0 +void graphics::tab (size_t n) +{ + #ifdef BLASSIC_HAS_GRAPHICS + + int col (n - 1); + if (tcol >= col) + { + do { + do_charout (' '); + } while (tcol > 0); + } + if (col >= maxtcol) + throw ErrFunctionCall; + do { + do_charout (' '); + } while (tcol < col); + + #else + // No BLASSIC_HAS_GRAPHICS + + touch (n); + + #endif +} +#endif + +void graphics::tab (BlChannel ch) +{ + #ifdef BLASSIC_HAS_GRAPHICS + + mapwindow [ch]->tab (); + + #else + + touch (ch); + + #endif +} + +void graphics::tab (BlChannel ch, size_t n) +{ + #ifdef BLASSIC_HAS_GRAPHICS + + mapwindow [ch]->tab (n); + + #else + + touch (ch, n); + + #endif +} + +#if 0 +void graphics::movecharforward (size_t n) +{ + #ifdef BLASSIC_HAS_GRAPHICS + + windowzero.movecharforward (n); + + #else + + touch (n); + + #endif +} +#endif + +void graphics::movecharforward (BlChannel ch, size_t n) +{ + #ifdef BLASSIC_HAS_GRAPHICS + + mapwindow [ch]->movecharforward (n); + + #else + + touch (ch, n); + + #endif +} + +#if 0 +void graphics::movecharback (size_t n) +{ + #ifdef BLASSIC_HAS_GRAPHICS + + windowzero.movecharback (n); + + #else + + touch (n); + + #endif +} +#endif + +void graphics::movecharback (BlChannel ch, size_t n) +{ + #ifdef BLASSIC_HAS_GRAPHICS + + mapwindow [ch]->movecharback (n); + + #else + + touch (ch, n); + + #endif +} + +#if 0 +void graphics::movecharup (size_t n) +{ + #ifdef BLASSIC_HAS_GRAPHICS + + windowzero.movecharup (n); + + #else + + touch (n); + + #endif +} +#endif + +void graphics::movecharup (BlChannel ch, size_t n) +{ + #ifdef BLASSIC_HAS_GRAPHICS + + mapwindow [ch]->movecharup (n); + + #else + + touch (ch, n); + + #endif +} + +#if 0 +void graphics::movechardown (size_t n) +{ + #ifdef BLASSIC_HAS_GRAPHICS + + windowzero.movechardown (n); + + #else + + touch (n); + + #endif +} +#endif + +void graphics::movechardown (BlChannel ch, size_t n) +{ + #ifdef BLASSIC_HAS_GRAPHICS + + mapwindow [ch]->movechardown (n); + + #else + + touch (ch, n); + + #endif +} + +void graphics::scroll (BlChannel ch, int n) +{ + #ifdef BLASSIC_HAS_GRAPHICS + + mapwindow [ch]->scroll (n); + + #else + + touch (ch, n); + + #endif +} + +void graphics::symbolafter (int symbol) +{ + if (symbol < 0 || symbol > 256) + throw ErrFunctionCall; + memcpy (charset::data, * charset::default_charset, + sizeof (charset::chardataset) ); + symbol_after_is= symbol; +} + +void graphics::definesymbol (int symbol, const unsigned char (& byte) [8] ) +{ + if (symbol < 0 || symbol > 255) + throw ErrFunctionCall; + if (symbol < symbol_after_is) + throw ErrImproperArgument; + memcpy (charset::data + symbol, byte, sizeof (byte) ); +} + +void graphics::synchronize (bool mode) +{ + TRACEFUNC (tr, "graphics::synchronize"); + + #ifdef BLASSIC_HAS_GRAPHICS + + bool previous= fSynchro; + TRMESSAGE (tr, "Was " + to_string (previous) ); + + fSynchro= mode; + + if (previous == true && mode == false && ingraphicsmode () ) + reinit_window (); + + TRMESSAGE (tr, "Set to " + to_string (mode) ); + + #else + + touch (mode); + + #endif +} + +void graphics::synchronize () +{ + requiregraphics (); + + #ifdef BLASSIC_HAS_GRAPHICS + + reinit_window (); + idle (); + + #endif +} + +#ifdef BLASSIC_HAS_GRAPHICS + +namespace { + +size_t synchrosaved= 0; +bool fSynchroSaved= false; + +} // namespace + +#endif +// BLASSIC_HAS_GRAPHICS + +void graphics::synchronize_suspend () +{ + TRACEFUNC (tr, "graphics::synchronize_suspend"); + + #ifdef BLASSIC_HAS_GRAPHICS + + if (synchrosaved++ > 0) + { + if (showdebuginfo () ) + cerr << "synchronize_suspend called several times" << + endl; + } + else + fSynchroSaved= fSynchro; + + //if (fSynchro) + // reinit_window (); + //fSynchro= false; + synchronize (false); + + #endif +} + +void graphics::synchronize_restart () +{ + TRACEFUNC (tr, "graphics::synchronize_restart"); + + #ifdef BLASSIC_HAS_GRAPHICS + + if (synchrosaved == 0) + { + if (showdebuginfo () ) + cerr << "uexpected call to synchronize_restart" << + endl; + } + else + { + if (--synchrosaved == 0) + //fSynchro= fSynchroSaved; + synchronize (fSynchroSaved); + } + + #endif +} + +int graphics::xmouse () +{ + #ifdef BLASSIC_HAS_GRAPHICS + + return xmousepos; + + #else + + return 0; + + #endif +} + +int graphics::ymouse () +{ + #ifdef BLASSIC_HAS_GRAPHICS + + return ymousepos; + + #else + + return 0; + + #endif +} + +int graphics::xpos () +{ + #ifdef BLASSIC_HAS_GRAPHICS + + return lastx; + + #else + + return 0; + + #endif +} + +int graphics::xpos (BlChannel ch) +{ + #ifdef BLASSIC_HAS_GRAPHICS + + return mapwindow [ch]->getxpos (); + + #else + + touch (ch); + throw ErrFunctionCall; + + #endif +} + +int graphics::ypos () +{ + #ifdef BLASSIC_HAS_GRAPHICS + + return lasty; + + #else + + return 0; + + #endif +} + +int graphics::ypos (BlChannel ch) +{ + #ifdef BLASSIC_HAS_GRAPHICS + + return mapwindow [ch]->getypos (); + + #else + + touch (ch); + throw ErrFunctionCall; + + #endif +} + +void graphics::tag (BlChannel ch) +{ + #ifdef BLASSIC_HAS_GRAPHICS + + mapwindow [ch]->tag (); + + #else + + touch (ch); + + #endif +} + +void graphics::tagoff (BlChannel ch) +{ + #ifdef BLASSIC_HAS_GRAPHICS + + mapwindow [ch]->tagoff (); + + #else + + touch (ch); + + #endif +} + +bool graphics::istagactive (BlChannel ch) +{ + #ifdef BLASSIC_HAS_GRAPHICS + + return mapwindow [ch]->istagactive (); + + #else + + touch (ch); + throw ErrFunctionCall; + + #endif +} + +#if 0 +void graphics::showcursor () +{ + #ifdef BLASSIC_HAS_GRAPHICS + windowzero.invertcursor (); + #endif +} + +void graphics::hidecursor () +{ + #ifdef BLASSIC_HAS_GRAPHICS + windowzero.invertcursor (); + #endif +} +#endif + +void graphics::showcursor (BlChannel ch) +{ + #ifdef BLASSIC_HAS_GRAPHICS + + mapwindow [ch]->invertcursor (); + + #else + + touch (ch); + + #endif +} + +void graphics::hidecursor (BlChannel ch) +{ + #ifdef BLASSIC_HAS_GRAPHICS + + mapwindow [ch]->invertcursor (); + + #else + + touch (ch); + + #endif +} + +void graphics::inverse (BlChannel ch, bool active) +{ + #ifdef BLASSIC_HAS_GRAPHICS + + mapwindow [ch]->setinverse (active); + + #else + + touch (ch, active); + + #endif +} + +bool graphics::getinverse (BlChannel ch) +{ + #ifdef BLASSIC_HAS_GRAPHICS + + return mapwindow [ch]->getinverse (); + + #else + + touch (ch); + throw ErrFunctionCall; + + #endif +} + +void graphics::bright (BlChannel ch, bool active) +{ + #ifdef BLASSIC_HAS_GRAPHICS + + mapwindow [ch]->setbright (active); + + #else + + touch (ch, active); + + #endif +} + +bool graphics::getbright (BlChannel ch) +{ + #ifdef BLASSIC_HAS_GRAPHICS + + return mapwindow [ch]->getbright (); + + #else + + touch (ch); + throw ErrFunctionCall; + + #endif +} + +void graphics::clean_input () +{ + TRACEFUNC (tr, "graphics::clean_input"); + + #ifdef BLASSIC_HAS_GRAPHICS + + graphics::idle (); + queuekey.erase (); + + #endif +} + +void graphics::ring () +{ + #ifdef BLASSIC_HAS_GRAPHICS + + #ifdef BLASSIC_USE_X + + XBell (display, 100); + + #elif defined BLASSIC_USE_WINDOWS + + MessageBeep (MB_ICONEXCLAMATION); + + #endif + + #endif +} + +void graphics::set_title (const std::string & title) +{ + #ifdef BLASSIC_HAS_GRAPHICS + + #ifdef BLASSIC_USE_WINDOWS + + SetWindowText (window, title.c_str () ); + + #elif defined BLASSIC_USE_X + + XmbSetWMProperties (display, window, title.c_str (), title.c_str (), + NULL, 0, NULL, NULL, NULL); + + #endif + + #else + // No BLASSIC_HAS_GRAPHICS + + touch (title); + + #endif +} + +void graphics::set_default_title (const std::string & title) +{ + #ifdef BLASSIC_HAS_GRAPHICS + + default_title= title; + + #else + + touch (title); + + #endif +} + +//********************************************************* +// Graphics get and put. +//********************************************************* + +#ifdef BLASSIC_HAS_GRAPHICS + +namespace { + +class Image { +public: + Image (int x1, int y1, int x2, int y2); + ~Image (); + void put (int x, int y, int mode); +private: + int width; + int height; + + #ifdef BLASSIC_USE_X + + XImage * img; + + #elif defined BLASSIC_USE_WINDOWS + + HBITMAP img; + HDC hdcImg; + + #endif +}; + +Image::Image (int x1, int y1, int x2, int y2) +{ + // Adjust coordinates to current rotate mode. + do_rotate (x1, y1); + do_rotate (x2, y2); + if (x1 > x2) std::swap (x1, x2); + if (y1 > y2) std::swap (y1, y2); + if (x1 >= screenwidth || y1 >= screenheight) + throw ErrFunctionCall; + if (x2 >= screenwidth) + x2= screenwidth - 1; + if (y2 >= screenheight) + y2= screenheight - 1; + + width= x2 - x1 + 1; + height= y2 - y1 + 1; + + #ifdef BLASSIC_USE_X + + img= XGetImage (display, pixmap, x1, y1, width, height, + AllPlanes, XYPixmap); + if (img == NULL) + throw ErrFunctionCall; + + #elif defined BLASSIC_USE_WINDOWS + + img= CreateCompatibleBitmap (hdcPixmap, width, height); + if (img == NULL) + throw ErrFunctionCall; + hdcImg= CreateCompatibleDC (hdcPixmap); + if (hdcImg == NULL) + { + DeleteObject (img); + throw ErrFunctionCall; + } + HGDIOBJ hgdiobj= SelectObject (hdcImg, img); + if (hgdiobj == NULL || + hgdiobj == reinterpret_cast <HGDIOBJ> (GDI_ERROR) ) + { + DeleteDC (hdcImg); + DeleteObject (img); + throw ErrFunctionCall; + } + + if (BitBlt (hdcImg, 0, 0, width, height, hdcPixmap, x1, y1, SRCCOPY) + == 0) + { + DeleteDC (hdcImg); + DeleteObject (img); + throw ErrFunctionCall; + } + + #endif +} + +Image::~Image () +{ + #ifdef BLASSIC_USE_X + + XDestroyImage (img); + + #elif defined BLASSIC_USE_WINDOWS + + DeleteDC (hdcImg); + DeleteObject (img); + + #endif +} + +void Image::put (int x, int y, int mode) +{ + TRACEFUNC (tr, "Image::put"); + + TRMESSAGE (tr, "at " + to_string (x) + ',' + to_string (y) ); + + // Adjust coordinates to current rotate mode. + do_rotate (x, y); + switch (rotate) + { + case RotateNone: + // Nothing to do. + break; + case Rotate90: + y-= width - 1; + break; + } + if (x >= screenwidth || y >= screenheight) + throw ErrFunctionCall; + int maxwidth= screenwidth - x + 1; + int maxheight= screenheight - y + 1; + int w= std::min (width, maxwidth); + int h= std::min (height, maxheight); + + TRMESSAGE (tr, "At " + to_string (x) + ',' + to_string (y) ); + + #ifdef BLASSIC_USE_X + + int modeused= getdrawmode (mode); + + XSetFunction (display, gcp, modeused); + XPutImage (display, pixmap, gcp, img, + 0, 0, x, y, w, h); + XSetFunction (display, gcp, drawmode); + + #elif defined BLASSIC_USE_WINDOWS + + int modeused= getbitbltmode (mode); + + if (BitBlt (hdcPixmap, x, y, w, h, hdcImg, 0, 0, modeused) == 0) + throw ErrFunctionCall; + + #endif + + if (! fSynchro) + reinit_window (x, y, width, height); +} + +typedef std::map <std::string, Image *> imagemap_t; +imagemap_t imagemap; + +} // namespace + +#endif +// BLASSIC_HAS_GRAPHICS + +void graphics::get_image (int x1, int y1, int x2, int y2, + const std::string & name) +{ + requiregraphics (); + + #ifdef BLASSIC_HAS_GRAPHICS + + imagemap_t::iterator it= imagemap.find (name); + if (it != imagemap.end () ) + { + delete it->second; + imagemap.erase (it); + } + + auto_ptr <Image> pimg (new Image (x1, y1, x2, y2) ); + imagemap [name]= pimg.get (); + pimg.release (); + + #else + // No BLASSIC_HAS_GRAPHICS + + touch (x1, y1, x2, y2, name); + + #endif +} + +void graphics::put_image (int x, int y, const std::string & name, int mode) +{ + requiregraphics (); + + #ifdef BLASSIC_HAS_GRAPHICS + + imagemap_t::iterator it= imagemap.find (name); + if (it != imagemap.end () ) + { + it->second->put (x, y, mode); + } + else + throw ErrFunctionCall; + + #else + + touch (x, y, name, mode); + + #endif + // BLASSIC_HAS_GRAPHICS +} + +void graphics::clear_images () +{ + #ifdef BLASSIC_HAS_GRAPHICS + + for (imagemap_t::iterator it= imagemap.begin (); + it != imagemap.end (); + ++it) + { + delete it->second; + } + imagemap.clear (); + + #endif + // BLASSIC_HAS_GRAPHICS +} + +// Fin de graphics.cpp |