1 module ws.x.draw; 2 3 version(Posix): 4 5 6 import 7 std.string, 8 std.algorithm, 9 std.math, 10 std.conv, 11 x11.X, 12 x11.Xlib, 13 x11.extensions.render, 14 x11.extensions.Xrender, 15 ws.draw, 16 ws.wm, 17 ws.bindings.xft, 18 ws.gui.point, 19 ws.x.font; 20 21 22 class Color { 23 24 ulong pix; 25 XftColor rgb; 26 long[4] rgba; 27 28 this(Display* dpy, int screen, long[4] values){ 29 Colormap cmap = DefaultColormap(dpy, screen); 30 Visual* vis = DefaultVisual(dpy, screen); 31 rgba = values; 32 auto name = "#%02x%02x%02x".format(values[0], values[1], values[2]); 33 if(!XftColorAllocName(dpy, vis, cmap, name.toStringz, &rgb)) 34 throw new Exception("Cannot allocate color " ~ name); 35 pix = rgb.pixel; 36 } 37 38 } 39 40 class Cur { 41 Cursor cursor; 42 this(Display* dpy, int shape){ 43 cursor = XCreateFontCursor(dpy, shape); 44 } 45 void destroy(Display* dpy){ 46 XFreeCursor(dpy, cursor); 47 } 48 } 49 50 class Icon { 51 Picture picture; 52 int[2] size; 53 void destroy(Display* dpy){ 54 XRenderFreePicture(dpy, picture); 55 } 56 } 57 58 class XDraw: DrawEmpty { 59 60 int[2] size; 61 Display* dpy; 62 int screen; 63 x11.X.Window window; 64 Visual* visual; 65 Drawable drawable; 66 XftDraw* xft; 67 GC gc; 68 69 Color color; 70 Color[long[4]] colors; 71 72 ws.x.font.Font font; 73 ws.x.font.Font[string] fonts; 74 75 XRectangle[] clipStack; 76 77 Picture frontBuffer; 78 79 this(ws.wm.Window window){ 80 this(wm.displayHandle, window.windowHandle); 81 } 82 83 this(Display* dpy, x11.X.Window window){ 84 XWindowAttributes wa; 85 XGetWindowAttributes(dpy, window, &wa); 86 this.dpy = dpy; 87 screen = DefaultScreen(dpy); 88 this.window = window; 89 this.size = [wa.width, wa.height]; 90 drawable = XCreatePixmap(dpy, window, size.w, size.h, wa.depth); 91 gc = XCreateGC(dpy, window, 0, null); 92 XSetLineAttributes(dpy, gc, 1, LineSolid, CapButt, JoinMiter); 93 xft = XftDrawCreate(dpy, drawable, wa.visual, wa.colormap); 94 visual = wa.visual; 95 auto format = XRenderFindVisualFormat(dpy, wa.visual); 96 XRenderPictureAttributes pa; 97 pa.subwindow_mode = IncludeInferiors; 98 frontBuffer = XRenderCreatePicture(dpy, drawable, format, CPSubwindowMode, &pa); 99 } 100 101 this(x11.X.Window root, Drawable drawable, Picture frontBuffer){ 102 dpy = wm.displayHandle; 103 screen = DefaultScreen(dpy); 104 XWindowAttributes wa; 105 XGetWindowAttributes(dpy, root, &wa); 106 this.drawable = drawable; 107 this.frontBuffer = frontBuffer; 108 xft = XftDrawCreate(dpy, drawable, wa.visual, wa.colormap); 109 } 110 111 ~this(){ 112 if(drawable) 113 XFreePixmap(dpy, drawable); 114 if(frontBuffer) 115 XRenderFreePicture(dpy, frontBuffer); 116 } 117 118 override int width(string text){ 119 return font.width(text); 120 } 121 122 override void resize(int[2] size){ 123 if(this.size == size) 124 return; 125 this.size = size; 126 if(drawable) 127 XFreePixmap(dpy, drawable); 128 drawable = XCreatePixmap(dpy, window, size.w, size.h, DefaultDepth(dpy, screen)); 129 auto format = XRenderFindVisualFormat(dpy, visual); 130 XRenderPictureAttributes pa; 131 pa.subwindow_mode = IncludeInferiors; 132 if(frontBuffer) 133 XRenderFreePicture(dpy, frontBuffer); 134 frontBuffer = XRenderCreatePicture(dpy, drawable, format, CPSubwindowMode, &pa); 135 XftDrawChange(xft, drawable); 136 } 137 138 override void destroy(){ 139 foreach(font; fonts) 140 font.destroy; 141 XFreePixmap(dpy, drawable); 142 XRenderFreePicture(dpy, frontBuffer); 143 drawable = None; 144 frontBuffer = None; 145 XftDrawDestroy(xft); 146 XFreeGC(dpy, gc); 147 } 148 149 override void setFont(string font, int size){ 150 font ~= ":size=%d".format(size); 151 if(font !in fonts) 152 fonts[font] = new ws.x.font.Font(dpy, screen, font); 153 this.font = fonts[font]; 154 } 155 156 override int fontHeight(){ 157 return font.h; 158 } 159 160 override void setColor(float[3] color){ 161 setColor([color[0], color[1], color[2], 1]); 162 } 163 164 override void setColor(float[4] color){ 165 long[4] values = [ 166 (color[0]*255).lround.max(0).min(255), 167 (color[1]*255).lround.max(0).min(255), 168 (color[2]*255).lround.max(0).min(255), 169 (color[3]*255).lround.max(0).min(255) 170 ]; 171 if(values !in colors) 172 colors[values] = new Color(dpy, screen, values); 173 this.color = colors[values]; 174 } 175 176 override void clip(int[2] pos, int[2] size){ 177 pos[1] = this.size[1] - pos[1] - size[1]; 178 if(clipStack.length){ 179 auto rect = clipStack[$-1]; 180 auto maxx = rect.x + rect.width; 181 auto maxy = rect.y + rect.height; 182 pos = [ 183 pos.x.max(rect.x).min(maxx), 184 pos.y.max(rect.y).min(maxy) 185 ]; 186 size = [ 187 (pos.x+size.w.min(rect.width)).min(maxx)-pos.x, 188 (pos.y+size.h.min(rect.height)).min(maxy)-pos.y 189 ]; 190 } 191 auto rect = XRectangle(cast(short)pos[0], cast(short)pos[1], cast(short)size[0], cast(short)size[1]); 192 XftDrawSetClipRectangles(xft, 0, 0, &rect, 1); 193 if(gc) 194 XSetClipRectangles(dpy, gc, 0, 0, &rect, 1, Unsorted); 195 clipStack ~= rect; 196 } 197 198 override void noclip(){ 199 clipStack = clipStack[0..$-1]; 200 if(clipStack.length){ 201 auto rect = clipStack[$-1]; 202 XftDrawSetClipRectangles(xft, 0, 0, &rect, 1); 203 if(gc) 204 XSetClipRectangles(dpy, gc, 0, 0, &rect, 1, Unsorted); 205 }else{ 206 if(gc) 207 XSetClipMask(dpy, gc, None); 208 XftDrawSetClip(xft, null); 209 } 210 } 211 212 override void rect(int[2] pos, int[2] size){ 213 auto a = this.color.rgba[3]/255.0; 214 XRenderColor color = { 215 (this.color.rgba[0]*255*a).to!ushort, 216 (this.color.rgba[1]*255*a).to!ushort, 217 (this.color.rgba[2]*255*a).to!ushort, 218 (this.color.rgba[3]*255).to!ushort 219 }; 220 XRenderFillRectangle(dpy, PictOpOver, frontBuffer, &color, pos.x, this.size.h-size.h-pos.y, size.w, size.h); 221 } 222 223 override void rectOutline(int[2] pos, int[2] size){ 224 XSetForeground(dpy, gc, color.pix); 225 XDrawRectangle(dpy, drawable, gc, pos.x, this.size.h-pos.y-size.h, size.w, size.h); 226 } 227 228 override void line(int[2] start, int[2] end){ 229 XSetForeground(dpy, gc, color.pix); 230 XDrawLine(dpy, drawable, gc, start.x, start.y, end.x, end.y); 231 } 232 233 override int text(int[2] pos, string text, double offset=-0.2){ 234 if(text.length){ 235 auto width = width(text); 236 auto fontHeight = font.h; 237 auto offsetRight = max(0.0,-offset)*fontHeight; 238 auto offsetLeft = max(0.0,offset-1)*fontHeight; 239 auto x = pos.x - min(1,max(0,offset))*width + offsetRight - offsetLeft; 240 auto y = this.size.h - pos.y - 2; 241 XftDrawStringUtf8(xft, &color.rgb, font.xfont, cast(int)x.lround, cast(int)y.lround, text.toStringz, cast(int)text.length); 242 return this.width(text); 243 } 244 return 0; 245 } 246 247 override int text(int[2] pos, int h, string text, double offset=-0.2){ 248 pos.y += ((h-font.h)/2.0).lround; 249 return this.text(pos, text, offset); 250 } 251 252 Icon icon(ubyte[] data, int[2] size){ 253 assert(data.length == size.w*size.h*4, "%s != %s*%s*4".format(data.length, size.w, size.h)); 254 auto res = new Icon; 255 256 auto img = XCreateImage( 257 dpy, 258 null, 259 32, 260 ZPixmap, 261 0, 262 cast(char*)data.ptr, 263 cast(uint)size.w, 264 cast(uint)size.h, 265 32, 266 0 267 ); 268 269 auto pixmap = XCreatePixmap(dpy, drawable, size.w, size.h, 32); 270 271 XRenderPictureAttributes attributes; 272 auto gc = XCreateGC(dpy, pixmap, 0, null); 273 XPutImage(dpy, pixmap, gc, img, 0, 0, 0, 0, size.w, size.h); 274 auto pictformat = XRenderFindStandardFormat(dpy, PictStandardARGB32); 275 res.picture = XRenderCreatePicture(dpy, pixmap, pictformat, 0, &attributes); 276 XRenderSetPictureFilter(dpy, res.picture, "best", null, 0); 277 XFreePixmap(dpy, pixmap); 278 279 res.size = size; 280 return res; 281 /+ 282 res.pixmap = XCreatePixmap(wm.displayHandle, window, DisplayWidth(wm.displayHandle, 0), DisplayHeight(wm.displayHandle, 0), DefaultDepth(wm.displayHandle, 0)); 283 res.picture = XRenderCreatePicture(wm.displayHandle, pixmap, format, 0, null); 284 XFreePixmap(wm.displayHandle, res.pixmap); 285 +/ 286 287 } 288 289 void icon(Icon icon, int x, int y, double scale, Picture alpha=None){ 290 XTransform xform = {[ 291 [XDoubleToFixed( 1 ), XDoubleToFixed( 0 ), XDoubleToFixed( 0 )], 292 [XDoubleToFixed( 0 ), XDoubleToFixed( 1 ), XDoubleToFixed( 0 )], 293 [XDoubleToFixed( 0 ), XDoubleToFixed( 0 ), XDoubleToFixed( scale )] 294 ]}; 295 XRenderSetPictureTransform(dpy, icon.picture, &xform); 296 XRenderComposite(dpy, PictOpOver, icon.picture, alpha, frontBuffer, 0, 0, 0, 0, x, y, (icon.size.w*scale).to!int, (icon.size.h*scale).to!int); 297 } 298 299 override void finishFrame(){ 300 XCopyArea(dpy, drawable, window, gc, 0, 0, size.w, size.h, 0, 0); 301 XSync(dpy, False); 302 } 303 304 } 305