// 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 #include #include #include #include #include using std::auto_ptr; #include #include #include #include #include // Para depuracion #include using std::cerr; using std::endl; #if defined __unix__ || defined __linux__ #include #endif #include #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 #include #include #include #endif // BLASSIC_USE_SVGALIB #ifdef BLASSIC_USE_X #include #include #include #endif // BLASSIC_USE_X #ifdef BLASSIC_USE_WINDOWS #include #include #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 q; CriticalSection cs; }; #if 0 const size_t MAXKEYSYM= 65535; std::vector 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 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 (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 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 (screenwidth - x - 1); x= newx; } break; } } template 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 (screenwidth - y - 1); y= newy; } break; } } template 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 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 ( 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 (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 (~ 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 (xj); point [n].y= static_cast (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 (ch [i]) << ", "; } TRMESSAGE (tr, oss.str () ); } #endif char chinv [8]; for (int i= 0; i < 8; ++i) chinv [i]= static_cast (~ ch [i]); //for (int i= 0; i < 256; ++i) int iFrom= static_cast (from); int iTo= static_cast (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 (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 & 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 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 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 (c < 0 ? c - .5 : c + .5); out_y= static_cast (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 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 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 (c < 0 ? c - .5 : c + .5); out_y= static_cast (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 > visited; ColorTester test; public: Painter (ColorValue paint, ColorValue border) : paint (paint), border (border), visited (screenwidth, std::vector (screenheight) ) { } bool check (int x, int y) { if (! check_limit (x, y) ) return false; std::vector ::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 > visited; ColorTester test; public: Painter (ColorValue paint, ColorValue border) : paint (paint), border (border), visited (screenwidth, std::vector (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 > & visit, ColorTester & test) { if (! check_limit (x, y) ) return false; std::vector ::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 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 > visit (screenwidth, vector (screenheight) ); std::deque 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 > visited; std::deque processlist, nextlist; ColorTester test; public: Painter (ColorValue paint, ColorValue border) : paint (paint), border (border), visited (screenwidth, std::vector (screenheight) ) { } bool check (int x, int y) { if (! check_limit (x, y) ) return false; std::vector ::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 (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 (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 (c) ); unsigned char c1= static_cast (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 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 (params [0] ) % 16); } void do_SI () // 15 { setcolor (static_cast (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 (params [0] ) ); } void do_ETB () // 23 { // Ink mode, only CPC modes are allowed. graphics::setdrawmode (static_cast (params [0] ) % 4); } void do_CAN () // 24 { std::swap (foreground, background); } void do_EM () // 25 { int symbol= static_cast (params [0] ); // Avoid generate an error if out of range. if (symbol < symbol_after_is) return; unsigned char byte [8]; params.copy (reinterpret_cast (& byte [0] ), 8, 1); graphics::definesymbol (symbol, byte); } void do_SUB () // 26 { set (static_cast (params [0] ), static_cast (params [1] ), static_cast (params [2] ), static_cast (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 (params [0] ) % 16; int color= static_cast (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 (params [0] ); int yy= static_cast (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 (params [0] ) - 32; x= static_cast (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 (& 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 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 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 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 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 (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 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 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