aboutsummaryrefslogtreecommitdiffstats
path: root/graphics.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'graphics.cpp')
-rw-r--r--graphics.cpp8110
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
Un proyecto texto-plano.xyz