#include "Draw.h" #ifdef PLATFORM_X11 #define Time XTime #define Font XFont #define Display XDisplay #define Picture XPicture #ifndef flagNOGTK #include #include #include #endif #undef Picture #undef Time #undef Font #undef Display NAMESPACE_UPP #define LLOG(x) //LOG(x) #define LTIMING(x) //TIMING(x) XDisplay *Xdisplay; int Xscreenno; Visual *Xvisual; Window Xroot; Screen *Xscreen; Colormap Xcolormap; int Xheight; int Xwidth; int XheightMM; int XwidthMM; int Xdepth; dword Xblack; dword Xwhite; int Xconnection; byte *Xmapcolor; byte *Xunmapcolor; dword (*Xgetpixel)(int r, int g, int b); void StaticExitDraw_() { Draw::FreeFonts(); } EXITBLOCK { if(Xdisplay) { StaticExitDraw_(); // No CloseDisplay for now... XCloseDisplay(Xdisplay); LLOG("Xdisplay closed"); Xdisplay = NULL; } if(Xmapcolor) delete[] Xmapcolor; if(Xunmapcolor) delete[] Xunmapcolor; } void XError() { Panic("X11 error !"); } void XError(const char *s) { Panic(String("X11 error:") + s + " !"); } static int sAcs; bool sAllocColor(int xr, int xg, int xb) { XColor ce; ce.red = xr; ce.green = xg; ce.blue = xb; ce.flags = DoRed | DoGreen | DoBlue; if(!XAllocColor(Xdisplay, Xcolormap, &ce)) return false; sAcs++; return sAcs < 257; } void sAllocColors() { int r, g, b; for(r = 0; r < 2; r++) for(g = 0; g < 2; g++) for(b = 0; b < 2; b++) if(!sAllocColor(65535 * r, 65535 * g, 65535 * b)) return; for(r = 0; r < 3; r++) for(g = 0; g < 3; g++) for(b = 0; b < 3; b++) if((r == 1 || g == 1 || b == 1) && !sAllocColor((65535 * r) / 2, (65535 * g) / 2, (65535 * b) / 2)) return; for(r = 5; r >= 0; r--) for(g = 5; g >= 0; g--) for(b = 5; b >= 0; b--) if((r != 0 && r != 5 || g != 0 && g != 5 || b != 0 && b != 5) && !sAllocColor((65535 * r) / 5, (65535 * g) / 5, (65535 * b) / 5)) return; for(r = 1; r <= 11; r += 2) if(!sAllocColor((65535 * r) / 11, (65535 * r) / 11, (65535 * r) / 11)) return; for(int r = 255; r >= 0; r--) if(!sAllocColor(r << 8, r << 8, r << 8)) return; } dword GetPseudoColorPixel(int r, int g, int b) { return Xmapcolor[r * 11 / 255 * (24 * 12) + g * 23 / 255 * 12 + b * 11 / 255]; } static struct Xshift { dword mask; int bits; int shift; dword Do(int c) { return (c >> bits << shift) & mask; } } Xred, Xgreen, Xblue; Xshift CalcXShift(dword mask) { Xshift f; f.mask = mask; f.shift = 0; f.bits = 0; while((mask & 1) == 0) { mask >>= 1; f.shift++; } while((mask & 1) == 1) { mask >>= 1; f.bits++; } f.bits = 8 - f.bits; if(f.bits < 0) { f.shift += f.bits; f.bits = 0; } LLOG("xshift(" << FormatIntHex(mask) << "): mask = " << FormatIntHex(f.mask) << ", bits = " << f.bits << ", shift = " << f.shift); return f; } dword GetTrueColorPixel(int r, int g, int b) { return Xred.Do(r) | Xgreen.Do(g) | Xblue.Do(b); } inline int ssq(int x) { return x * x; } void InitX11Draw(XDisplay *display) { Xdisplay = display; if(!Xdisplay) { puts(NFormat("No X11 display, errno = %d, %s", errno, strerror(errno))); fflush(stdout); XError(); } int Xscreenno = DefaultScreen(Xdisplay); Xroot = RootWindow(Xdisplay, Xscreenno); Xscreen = ScreenOfDisplay(Xdisplay, Xscreenno); Xcolormap = DefaultColormap(Xdisplay, Xscreenno); // Xcolormap = (Colormap)GDK().gdk_x11_colormap_get_xcolormap(GDK().gdk_rgb_get_colormap()); Xheight = DisplayHeight(Xdisplay, Xscreenno); Xwidth = DisplayWidth(Xdisplay, Xscreenno); XheightMM = DisplayHeightMM(Xdisplay, Xscreenno); XwidthMM = DisplayWidthMM(Xdisplay, Xscreenno); LLOG("Xwidth = " << Xwidth << ", XwidthMM = " << XwidthMM); LLOG("Xheight = " << Xheight << ", XheightMM = " << XheightMM); Xdepth = DefaultDepth(Xdisplay, Xscreenno); Xblack = BlackPixel(Xdisplay, 0); Xwhite = WhitePixel(Xdisplay, 0); Xconnection = XConnectionNumber(Xdisplay); Xvisual = DefaultVisual(Xdisplay, Xscreenno); Visual *v = Xvisual; if(v->c_class == TrueColor) { Xred = CalcXShift(v->red_mask); Xgreen = CalcXShift(v->green_mask); Xblue = CalcXShift(v->blue_mask); Xgetpixel = GetTrueColorPixel; } else { sAllocColors(); int colorcount = max(1 << Xdepth, 256); Buffer cs(colorcount); int i; for(i = 0; i < colorcount; i++) cs[i].pixel = i; XQueryColors(Xdisplay, Xcolormap, cs, colorcount); Xunmapcolor = new byte[3 * colorcount]; for(i = 0; i < colorcount; i++) { Xunmapcolor[3 * i + 0] = cs[i].blue; Xunmapcolor[3 * i + 1] = cs[i].green; Xunmapcolor[3 * i + 2] = cs[i].red; } byte *cm = Xmapcolor = new byte[12 * 24 * 12]; for(int r = 0; r < 12; r++) for(int g = 0; g < 24; g++) for(int b = 0; b < 12; b++) { int mind = INT_MAX; int mini; for(int i = 0; i < colorcount; i++) { int d = ssq(r * 255 / 11 - (cs[i].red >> 8)) + ssq(g * 255 / 23 - (cs[i].green >> 8)) + ssq(b * 255 / 11 - (cs[i].blue >> 8)); if(d < mind) { mini = i; mind = d; } } *cm++ = mini; } Xgetpixel = GetPseudoColorPixel; } // XFree(v); Draw::SetStdFont(ScreenSans(12)); } void InitX11Draw(const char *dispname) { #ifdef flagNOGTK if(!dispname || !*dispname) { int f = Environment().Find("DISPLAY"); dispname = (f >= 0 ? ~Environment()[f] : ":0.0"); } InitX11Draw(XOpenDisplay(dispname)); #else MemoryIgnoreLeaksBlock __; const Vector& cmd = CommandLine(); char **argv = (char**) MemoryAllocPermanent(sizeof(char *) * cmd.GetCount()); for(int i = 0; i < cmd.GetCount(); i++) argv[i] = PermanentCopy(cmd[i]); int argc = cmd.GetCount(); gtk_init (&argc, &argv); GtkWidget *w = gtk_window_new(GTK_WINDOW_TOPLEVEL); GdkDisplay *d = gtk_widget_get_display(w); gdk_x11_display_get_xdisplay(d); InitX11Draw(gdk_x11_display_get_xdisplay(d)); gtk_widget_destroy(w); #endif } #ifdef PLATFORM_XFT void SetClip(GC gc, XftDraw *xftdraw, const Vector& cl) #else void SetClip(GC gc, const Vector& cl) #endif { DrawLock __; LTIMING("SetClip"); Buffer xr(cl.GetCount()); LLOG("SetClip"); for(int i = 0; i < cl.GetCount(); i++) { XRectangle& r = xr[i]; const Rect& cr = cl[i]; LLOG("[" << i << "] = " << cr); r.x = cr.left; r.y = cr.top; r.width = cr.Width(); r.height = cr.Height(); } XSetClipRectangles(Xdisplay, gc, 0, 0, xr, cl.GetCount(), Unsorted); #ifdef PLATFORM_XFT LLOG("XftDrawSetClipRectangles, # = " << cl.GetCount() << ", xftdraw = " << FormatIntHex(xftdraw)); XftDrawSetClipRectangles(xftdraw, 0, 0, xr, cl.GetCount()); LLOG("//XftDrawSetClipRectangles"); #endif } void Draw::CloneClip() { if(cloff.GetCount() > 1 && cloff.Top().clipi == cloff[cloff.GetCount() - 2].clipi) { cloff.Top().clipi = clip.GetCount(); Vector& c = clip.Add(); c <<= clip[clip.GetCount() - 2]; } } void Draw::SetForeground(Color color) { DrawLock __; LTIMING("SetForeground"); if(IsDrawing()) return; int p = GetXPixel(color.GetR(), color.GetG(), color.GetB()); if(p == foreground) return; LTIMING("XSetForeground"); LLOG("XSetForeground " << color); foreground = p; XSetForeground(Xdisplay, gc, foreground); } void Draw::SetClip() { DrawLock __; if(IsDrawing() || dw == Xroot) return; LTIMING("SetClip"); #ifdef PLATFORM_XFT UPP::SetClip(gc, xftdraw, clip.Top()); #else UPP::SetClip(gc, clip.Top()); #endif } void Draw::SetLineStyle(int width) { DrawLock __; if(IsDrawing()) return; if(width == linewidth) return; linewidth = width; if(IsNull(width)) width = 1; if(width < PEN_SOLID) { static const char dash[] = { 18, 6 }; static const char dot[] = { 3, 3 }; static const char dashdot[] = { 9, 6, 3, 6 }; static const char dashdotdot[] = { 9, 3, 3, 3, 3, 3 }; static struct { const char *dash; int len; } ds[] = { { dash, __countof(dash) }, { dot, __countof(dot) }, { dashdot, __countof(dashdot) }, { dashdotdot, __countof(dashdotdot) } }; int i = -(width - PEN_DASH); ASSERT(i >= 0 && i < 4); XSetDashes(Xdisplay, gc, 0, ds[i].dash, ds[i].len); } XSetLineAttributes(Xdisplay, gc, max(width, 1), width < PEN_SOLID ? LineOnOffDash : LineSolid, CapRound, JoinRound); } void Draw::Init() { DrawLock __; pageDots = pagePixels = Size(Xwidth, Xheight); pageMMs = Size(XwidthMM, XheightMM); nativeDpi = inchPixels = 254 * pagePixels / pageMMs / 10; sheetPixels = pagePixels; pageOffset = Point(0, 0); InitFonts(); cloff.Clear(); clip.Clear(); foreground = linewidth = Null; device = 0; device = 0; pixels = true; printer = aborted = backdraw = is_mono = false; } void Draw::Init(const Vector& _clip, Point _offset) { DrawLock __; Init(); clip.Add() <<= _clip; offset.Add(_offset); actual_offset = _offset; Cloff& f = cloff.Add(); f.offseti = 0; f.clipi = 0; SetClip(); } Draw::Draw() { DrawLock __; dw = None; gc = None; actual_offset = Point(0, 0); Init(); } void Draw::BeginNative() {} void Draw::EndNative() {} #ifdef PLATFORM_XFT Draw::Draw(Drawable _dw, GC _gc, XftDraw *_xftdraw, const Vector& _clip) #else Draw::Draw(Drawable _dw, GC _gc, const Vector& _clip) #endif { LLOG("Draw"); dw = _dw; gc = _gc; #ifdef PLATFORM_XFT xftdraw = _xftdraw; #endif Init(_clip); } Draw::~Draw() { } void BackDraw::Create(Draw& w, int cx, int cy) { DrawLock __; LLOG("Creating BackDraw " << cx << "x" << cy); Destroy(); size.cx = cx; size.cy = cy; dw = XCreatePixmap(Xdisplay, w.GetDrawable(), max(cx, 1), max(cy, 1), Xdepth); gc = XCreateGC(Xdisplay, dw, 0, 0); #ifdef PLATFORM_XFT xftdraw = XftDrawCreate(Xdisplay, (Drawable) dw, DefaultVisual(Xdisplay, Xscreenno), Xcolormap); #endif Vector clip; clip.Add(RectC(0, 0, cx, cy)); Init(clip, Point(0, 0)); backdraw = true; } void BackDraw::Put(Draw& w, int x, int y) { DrawLock __; LLOG("Putting BackDraw"); ASSERT(dw != None); XCopyArea(Xdisplay, dw, w.GetDrawable(), w.GetGC(), 0, 0, size.cx, size.cy, x + w.GetOffset().x, y + w.GetOffset().y); } void BackDraw::Destroy() { DrawLock __; if(dw != None) { #ifdef PLATFORM_XFT XftDrawDestroy(xftdraw); #endif XFreePixmap(Xdisplay, dw); XFreeGC(Xdisplay, gc); } } NilDraw::NilDraw() { DrawLock __; dw = Xroot; gc = XCreateGC(Xdisplay, Xroot, 0, 0); pixels = false; Init(Vector()); } NilDraw::~NilDraw() { DrawLock __; XFreeGC(Xdisplay, gc); } Draw& ScreenInfo() { return Single(); } END_UPP_NAMESPACE #endif