1 // This code is D 1.0
2 
3 /**
4 	Part of my old D 1.0 game helper code that used SDL. I keep it compiling on new D compilers too, but it is not meant to be used in new projects.
5 */
6 module arsd.screen;
7 
8 import sdl.SDL;
9 import sdl.SDL_image;
10 import sdl.SDL_ttf;
11 import std.string;
12 
13 import std.stdio;
14 import std.format;
15 
16 import arsd.engine;
17 
18 version(D_Version2)
19 static import stdcstring = core.stdc.string;
20 else
21 static import stdcstring = std.c.string;
22 
23 version(none)
24 char[] fmt(...){
25     char[] o;
26     void putc(dchar c)
27     {
28     	o ~= c;
29     }
30 
31     std.format.doFormat(&putc, _arguments, _argptr);
32 
33 	return o;
34 }
35 
36 
37 extern(C){
38 	void glGetIntegerv(int, void*);
39 	void glMatrixMode(int);
40 	void glPushMatrix();
41 	void glLoadIdentity();
42 	void glOrtho(double, double, double, double, double, double);
43 	void glPopMatrix();
44 	void glEnable(int);
45 	void glDisable(int);
46 	void glClear(int);
47 	void glBegin(int);
48 	void glVertex2f(float, float);
49 	void glEnd();
50 	void glColor3b(ubyte, ubyte, ubyte);
51 	void glColor3i(int, int, int);
52 	void glColor3f(float, float, float);
53 	void glColor4f(float, float, float, float);
54 	void glTranslatef(float, float, float);
55 
56 	void glRotatef(float, float, float, float);
57 
58 	uint glGetError();
59 
60 	void glDeleteTextures(int, uint*);
61 
62 	char* gluErrorString(uint);
63 
64 	void glRasterPos2i(int, int);
65 	void glDrawPixels(int, int, uint, uint, void*);
66 	void glClearColor(float, float, float, float);
67 
68 
69 
70 	void glGenTextures(uint, uint*);
71 	void glBindTexture(int, int);
72 	void glTexParameteri(uint, uint, int);
73 	void glTexImage2D(int, int, int, int, int, int, int, int, void*);
74 
75 
76 	void glTexCoord2f(float, float);
77 	void glVertex2i(int, int);
78 	void glBlendFunc (int, int);
79 	void glViewport(int, int, int, int);
80 
81 	void glReadBuffer(uint);
82 	void glReadPixels(int, int, int, int, int, int, void*);
83 
84 
85 	const uint GL_FRONT = 0x0404;
86 
87 	const uint GL_BLEND = 0x0be2;
88 	const uint GL_SRC_ALPHA = 0x0302;
89 	const uint GL_ONE_MINUS_SRC_ALPHA = 0x0303;
90 
91 
92 	const uint GL_UNSIGNED_BYTE = 0x1401;
93 	const uint GL_RGB = 0x1907;
94 	const uint GL_BGRA = 0x80e1;
95 	const uint GL_RGBA = 0x1908;
96 	const uint GL_TEXTURE_2D =   0x0DE1;
97 	const uint GL_TEXTURE_MIN_FILTER = 0x2801;
98 	const uint GL_NEAREST = 0x2600;
99 	const uint GL_LINEAR = 0x2601;
100 	const uint GL_TEXTURE_MAG_FILTER = 0x2800;
101 
102 	const uint GL_NO_ERROR = 0;
103 
104 
105 
106 	const int GL_VIEWPORT = 0x0BA2;
107 	const int GL_MODELVIEW = 0x1700;
108 	const int GL_TEXTURE = 0x1702;
109 	const int GL_PROJECTION = 0x1701;
110 	const int GL_DEPTH_TEST = 0x0B71;
111 
112 	const int GL_COLOR_BUFFER_BIT = 0x00004000;
113 	const int GL_ACCUM_BUFFER_BIT = 0x00000200;
114 	const int GL_DEPTH_BUFFER_BIT = 0x00000100;
115 
116 	const int GL_POINTS = 0x0000;
117 	const int GL_LINES =  0x0001;
118 	const int GL_LINE_LOOP = 0x0002;
119 	const int GL_LINE_STRIP = 0x0003;
120 	const int GL_TRIANGLES = 0x0004;
121 	const int GL_TRIANGLE_STRIP = 5;
122 	const int GL_TRIANGLE_FAN = 6;
123 	const int GL_QUADS = 7;
124 	const int GL_QUAD_STRIP = 8;
125 	const int GL_POLYGON = 9;
126 
127 }
128 
129 public struct Point{
130 	int x;
131 	int y;
132 	Point opAddAssign(Point p){
133 		x += p.x;
134 		y += p.y;
135 		version(D_Version2)
136 			return this;
137 		else
138 			return *this;
139 	}
140 
141 	Point opAdd(Point p){
142 		Point a;
143 		a.x = x + p.x;
144 		a.y = y + p.y;
145 		return a;
146 	}
147 
148 	Point opSub(Point p){
149 		Point a;
150 		a.x = x - p.x;
151 		a.y = y - p.y;
152 		return a;
153 	}
154 }
155 
156 Point XY(int x, int y){
157 	Point p;
158 	p.x = x;
159 	p.y = y;
160 	return p;
161 }
162 
163 Point XY(float x, float y){
164 	Point p;
165 	p.x = cast(int)x;
166 	p.y = cast(int)y;
167 	return p;
168 }
169 
170 public struct Color{
171 	int r;
172 	int g;
173 	int b;
174 	int a;
175 
176 	uint toInt(){
177 		return r << 24 | g << 16 | b << 8 | a;
178 	}
179 
180 	void fromInt(uint i){
181 		r = i >> 24;
182 		g = (i >> 16) & 0x0ff;
183 		b = (i >> 8) & 0x0ff;
184 		a = i & 0x0ff;
185 	}
186 }
187 
188 Color white = {255, 255, 255, 255};
189 Color black = {0, 0, 0, 255};
190 
191 Color RGB(int r, int g, int b){
192 	Color c;
193 	c.r = r;
194 	c.g = g;
195 	c.b = b;
196 	c.a = 255;
197 	return c;
198 }
199 
200 Color RGBA(int r, int g, int b, int a){
201 	Color c;
202 	c.r = r;
203 	c.g = g;
204 	c.b = b;
205 	c.a = a;
206 	return c;
207 }
208 
209 Color XRGB(Color c, int alpha = -1){
210 	Color a;
211 	a.r = c.r ^ 255;
212 	a.g = c.g ^ 255;
213 	a.b = c.b ^ 255;
214 	if(alpha == -1)
215 		a.a = c.a;// ^ 255;
216 	else
217 		a.a = alpha;
218 	return a;
219 }
220 
221 class FontEngine{
222   public:
223 
224 	static FontEngine instance;
225 
226 	~this(){
227 		foreach(a; fonts)
228 			if(a != null)
229 				TTF_CloseFont(a);
230 		TTF_Quit();
231 	}
232 
233 	void loadFont(in char[] font, int size = 12, int index = 0){
234 		if(fonts[index] != null)
235 			freeFont(index);
236 		TTF_Font* temp;
237 		temp = TTF_OpenFont(std..string.toStringz(font), size);
238 		if(temp == null)
239 			throw new Exception("load font");
240 
241 		fonts[index] = temp;
242 	}
243 
244 	void freeFont(int index = 0){
245 		if(fonts[index] != null){
246 			TTF_CloseFont(fonts[index]);
247 			fonts[index] = null;
248 		}
249 	}
250 
251 	Image renderText(in char[] text, Color foreground = RGB(255,255,255), int font = 0){
252 		Image* a = immutableString(text) in cache[font];
253 		if(a !is null)
254 			return *a;
255 		SDL_Color f;
256 		f.r = cast(ubyte) foreground.r;
257 		f.g = cast(ubyte) foreground.g;
258 		f.b = cast(ubyte) foreground.b;
259 		f.unused = cast(ubyte) foreground.a;
260 
261 		SDL_Surface* s = TTF_RenderText_Blended(fonts[font], std..string.toStringz(text), f);
262 		Image i = new Image(s);
263 		cache[font][text]/*[font]*/ = i;
264 
265 		return i;
266 	}
267 
268 	int textHeight(in char[] text=" ",int font = 0){
269 		int w, h;
270 		TTF_SizeText(FontEngine.instance.fonts[font], std..string.toStringz(text), &w, &h);
271 		return h;
272 	}
273 
274 	void textSize(in char[] text, out int w, out int h, int font = 0){
275 		TTF_SizeText(fonts[font], std..string.toStringz(text), &w, &h);
276 	}
277   private:
278 	static this() {
279 		instance = new FontEngine;
280 	}
281 
282 	this(){
283 		if(TTF_Init() == -1)
284 			throw new Exception("TTF_Init");
285 
286 	}
287 
288 	TTF_Font*[8] fonts;
289 	Image[char[]][8] cache;
290 }
291 
292 interface Drawable{
293   public:
294 	void flip();
295 	int width();
296 	int height();
297 	int bpp();
298 	/*
299 	uint toGL();
300 	float texWidth();
301 	float texHeight();
302 	*/
303   protected:
304 	SDL_Surface* surface();
305 }
306 
307 int total = 0;
308 
309 
310 class Image : Drawable{
311   public:
312   	this(SDL_Surface* s){
313 		if(s == null)
314 			throw new Exception("Image");
315 		sur = s;
316 	}
317 
318 	/// Loads an image with the filename checking to see if it has already been loaded into the cache
319 	/// loads it as read-only
320 //	static Image load(char[] filename){
321 
322 //	}
323 
324 	this(char[] filename){
325 		sur = IMG_Load(std..string.toStringz(filename));
326 		if(sur == null)
327 			throw new Exception(immutableString("Load " ~ filename));
328 		name = filename;
329 	}
330 
331 
332 	void replace(char[] filename){
333 		if(t){
334 			glDeleteTextures(1, &tex);
335 			total--;
336 			writef("[%s]OpenGL texture destroyed %d. %d remain\n", name, tex, total);
337 			t = 0;
338 		}
339 		if(sur){
340 			SDL_FreeSurface(sur);
341 			sur = null;
342 		}
343 		sur = IMG_Load(std..string.toStringz(filename));
344 		if(sur == null)
345 			throw new Exception(immutableString("Load " ~ filename));
346 		name = filename;
347 	}
348 
349 
350 	// loads a slice of an image
351 	this(char[] filename, int x, int y, int wid, int hei){
352 	/*
353 		Image i = new Image(filename);
354 		this(wid, hei);
355 
356 		scope Painter p = new Painter(this);
357 		for(int a = 0; a < wid; a++)
358 		for(int b = 0; b < hei; b++)
359 			p.putpixel(XY(a, b), i.getPixel(XY(a + x, b + y)));
360 	*/
361 
362 		SDL_Surface* s1;
363 
364 		s1 = IMG_Load(std..string.toStringz(filename));
365 		if(s1 == null)
366 			throw new Exception(immutableString("Loading " ~ filename));
367 		scope(exit)
368 			SDL_FreeSurface(s1);
369 
370 
371 		sur = SDL_CreateRGBSurface(SDL_SWSURFACE, wid, hei, 32, 0xff0000, 0x00ff00, 0x0000ff, 0xff000000);
372 		if(sur == null)
373 			throw new Exception(immutableString("Create"));
374 
375 
376 		for(int b = 0; b < hei; b++){
377 			for(int a = 0; a < wid; a++){
378 			if(b+y >= s1.h || a+x >= s1.w){
379 				break;
380 			//	throw new Exception("eat my cum");
381 			}
382 				ubyte* wtf;
383 				if(s1.format.BitsPerPixel == 32){
384 					wtf = cast(ubyte*)(cast(ubyte*)s1.pixels + (b+y)*s1.pitch + (a+x) * 4);
385 				}
386 				else
387 				if(s1.format.BitsPerPixel == 24)
388 					wtf = cast(ubyte*)(cast(ubyte*)s1.pixels + (b+y)*s1.pitch + (a+x) * 3);
389 				else
390 					throw new Exception("fuck me in the ass");
391 
392 				ubyte* good = cast(ubyte*)(cast(ubyte*)sur.pixels + b*sur.pitch + a * 4);
393 
394 				good[0] = wtf[2];
395 				good[1] = wtf[1];
396 				good[2] = wtf[0];
397 				good[3] = wtf[3];
398 
399 			}
400 		}
401 
402 
403 /*
404 		SDL_Rect r;
405 		r.x = x;
406 		r.y = y;
407 		r.w = wid;
408 		r.h = hei;
409 
410 		SDL_Rect r2;
411 		r2.x = 0;
412 		r2.y = 0;
413 		r2.w = wid;
414 		r2.h = hei;
415 		if(SDL_BlitSurface(s1, &r, sur, &r2))
416 			throw new Exception("Blit");
417 */
418 	}
419 
420 	this(int wid, int hei){
421 		sur = SDL_CreateRGBSurface(SDL_SWSURFACE, wid, hei, 32, 0xff0000, 0x00ff00, 0x0000ff, 0xff000000);
422 		if(sur == null)
423 			throw new Exception("Create");
424 		t = false;
425 	}
426 
427 	~this(){
428 		if(t){
429 			glDeleteTextures(1, &tex);
430 			total--;
431 			writef("[%s]OpenGL texture destroyed %d. %d remain\n", name, tex, total);
432 		}
433 		if(sur)
434 			SDL_FreeSurface(sur);
435 	}
436 
437 	void flip(){
438 
439 	}
440 
441 	int width(){
442 		return surface.w;
443 	}
444 
445 	int height(){
446 		return surface.h;
447 	}
448 
449 	int bpp(){
450 		return sur.format.BitsPerPixel;
451 	}
452 
453 	Color getPixel(Point p){
454 		ubyte* bufp;
455 		Color a;
456 
457 		if(bpp == 32){
458 			bufp = cast(ubyte*)(cast(ubyte*)surface.pixels + p.y*surface.pitch + p.x * 4);
459 			a.a = bufp[3];
460 		        a.r = bufp[2];
461 		        a.g = bufp[1];
462         		a.b = bufp[0];
463 		}else{
464 			bufp = cast(ubyte*)(cast(ubyte*)surface.pixels + p.y*surface.pitch + p.x * 3);
465 		        a.a = 255;
466 			a.r = bufp[2];
467 		       	a.g = bufp[1];
468 	        	a.b = bufp[0];
469 		}
470 
471 		return a;
472 	}
473 
474 	uint toGL(){
475 		if(t)
476 			return tex;
477 		else{
478 			float[4] f;
479 			tex = SDL_GL_LoadTexture(surface, f.ptr);
480 			t = true;
481 			total++;
482 			texWidth = f[2];
483 			texHeight = f[3];
484 
485 //			total++;
486 //			writef("OpenGL texture created %d. %d exist\n", tex, total);
487 
488 			return tex;
489 		}
490 	}
491   protected:
492 	SDL_Surface* surface(){
493 		return sur;
494 	}
495   private:
496   	SDL_Surface* sur;
497 	uint tex;
498 	bool t;
499 	float texWidth;
500 	float texHeight;
501 	char[] name;
502 }
503 
504 bool useGL;
505 
506 class Screen : Drawable{
507   public:
508 	this(int xres = 1024, int yres = 768, int bpp = 24, bool oGL = false, bool fullScreen = false){//true){
509 //	oGL = false;
510 	oGL = true;
511 		if(!oGL)
512 			screen = SDL_SetVideoMode(xres, yres, bpp/*32*/, SDL_SWSURFACE);
513 		else{
514 			SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 8 );
515 			SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 8 );
516 			SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 8 );
517 			SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 24 );
518 			SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
519 			if(fullScreen){
520 			screen = SDL_SetVideoMode(xres, yres, 24, SDL_OPENGL| SDL_FULLSCREEN);
521 			}
522 			else
523 			screen = SDL_SetVideoMode(xres, yres, 0, SDL_OPENGL);
524 			//screen = SDL_SetVideoMode(xres, yres, bpp, SDL_OPENGL);
525 			if(screen is null)
526 				throw new Exception("screen");
527 
528    			glMatrixMode(GL_PROJECTION);
529    			glLoadIdentity();
530    			//glOrtho(0, 1000, yres, 0, 0, 1);
531    			glOrtho(0, xres, yres, 0, 0, 1);
532    			glMatrixMode(GL_MODELVIEW);
533 
534 			glDisable(GL_DEPTH_TEST);
535 
536 			glEnable(GL_TEXTURE_2D);
537 
538 
539 			glEnable (GL_BLEND); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
540 
541 
542 			glClearColor(0,0,0,0);
543 
544 //			glViewport(0,0,1024,1024);
545 		}
546 
547 		useGL = oGL;
548 
549 		if(screen == null)
550 			throw new Exception("screen");
551 
552 //		SDL_SetAlpha(screen, SDL_SRCALPHA | SDL_RLEACCEL, 128);
553 
554 		xr = xres;
555 		yr = yres;
556 	}
557 
558 	void switchSplitScreenMode(int player, int numberOfPlayers, bool horizontal){
559 			switch(numberOfPlayers){
560 				default: assert(0);
561 				case 1:
562 					return;
563 //					glViewport(0, 0, xr, yr);
564 				break;
565 				case 2:
566 					switch(player){
567 						default: assert(0);
568 						case 0:
569 							if(horizontal)
570 								glViewport(0, yr / 2, xr, yr / 2);
571 							else
572 								glViewport(0, 0, xr / 2, yr);
573 						break;
574 						case 1:
575 							if(horizontal)
576 								glViewport(0, 0, xr, yr / 2);
577 							else
578 								glViewport(xr / 2, 0, xr / 2, yr);
579 						break;
580 					}
581 				break;
582 				case 3:
583 				case 4:
584 					switch(player){
585 						default: assert(0);
586 					  case 0:
587 						glViewport(0, yr / 2, xr / 2, yr / 2);
588 					  break;
589 					  case 1:
590 						glViewport(xr / 2, yr / 2, xr / 2, yr / 2);
591 					  break;
592 					  case 2:
593 						glViewport(0, 0, xr / 2, yr / 2);
594 					  break;
595 					  case 3:
596 						glViewport(xr / 2, 0, xr / 2, yr / 2);
597 					  break;
598 					}
599 
600 				break;
601 			}
602 			glMatrixMode(GL_PROJECTION);
603 			glLoadIdentity();
604 			glOrtho(0, xr, yr, 0, 0, 1);
605 	}
606 
607 
608 	~this(){
609 		delete FontEngine.instance;
610 	}
611 
612 	Image screenshot(){
613 		if(!useGL)
614 			throw new Exception("Not yet implemented");
615 		Image image = new Image(xr, yr);
616 		glReadBuffer(GL_FRONT);
617 		glReadPixels(0, 0, xr, yr, GL_BGRA, GL_UNSIGNED_BYTE, image.sur.pixels);
618 
619 		Image temp = new Image(xr, yr);
620 
621 
622 	// FIXME
623 	version(Windows)
624 		return image;
625 
626 		// FIXME: this crashes on Windows
627 		for(int i = 0; i < yr; i++)
628 			stdcstring.memcpy(temp.sur.pixels + 4 * xr * i, image.sur.pixels + 4 * xr * (yr-1 - i), 4 * xr);
629 //        memcpy(image.sur.pixels, tem.psur.pixels, xres * yres * 4);
630 
631 		return temp;
632 	}
633 
634 
635 
636 
637 	void flip(){
638 		if(useGL)
639 			SDL_GL_SwapBuffers();
640 		else
641 			SDL_Flip(screen);
642 	}
643 
644 	int width(){
645 		return xr;
646 	}
647 
648 	int height(){
649 		return yr;
650 	}
651 
652 	int bpp(){
653 		return 124;
654 	}
655 	/*
656 	uint toGL(){
657 		throw new Error;
658 	}
659 	float texWidth(){
660 		return 1.0;
661 	}
662 	float texHeight(){
663 		return 1.0;
664 	}
665 */
666   protected:
667 	SDL_Surface* surface(){
668 		return screen;
669 	}
670 
671   private:
672 	SDL_Surface* screen;
673 	int xr;
674 	int yr;
675 }
676 
677 scope class Painter{
678   public:
679 	bool special;
680 	bool manualFlipped;
681 	Point translate;
682 	this(Painter p, Point t){
683 		s = p.s;
684 		special = true;
685 		translate = t;
686 	}
687 
688 	this(Drawable d){
689 	/+
690 		in {
691 			assert(!(s is null));
692 		}
693 	+/
694 		s = d;
695 		if(s is null)
696 			throw new Exception("christ what were you thinking");
697 
698 		if ( !(useGL
699 		&& s.bpp() == 124)
700 		&& SDL_MUSTLOCK(s.surface()) ) {
701 			if ( SDL_LockSurface(s.surface()) < 0 ) {
702 				throw new Exception("locking");
703 			}
704 			locked = true;
705 		}
706 	}
707 
708 	~this(){
709 		if(!manualFlipped){
710 		if(glbegin)
711 			endDrawingShapes();
712 		if(!special){
713 		if (locked){
714 			SDL_UnlockSurface(s.surface);
715 		}
716 		s.flip();
717 		}
718 		}
719 	}
720 
721 	void manualFlip(){
722 		if(glbegin)
723 			endDrawingShapes();
724 		if(!special){
725 		if (locked){
726 			SDL_UnlockSurface(s.surface);
727 		}
728 		s.flip();
729 		}
730 		manualFlipped = true;
731 	}
732 
733 	void setGLColor(Color color){
734 		if(useGL && s.bpp == 124){
735 			glColor4f(cast(float)color.r/255.0, cast(float)color.g/255.0, cast(float)color.b/255.0, cast(float)color.a / 255.0);
736 			return;
737 		}
738 	}
739 
740 	void putpixel(Point where, Color color){
741 		if(special) where += translate;
742 		if(glbegin)
743 			throw new Exception("Must end shape before doing anything else");
744 
745 //		if(color.a == 255)
746 //			return;
747 		int x = where.x;
748 		int y = where.y;
749 		if(x < 0 || x >= s.width || y < 0 || y >= s.height)
750 			return;
751 
752 
753 
754 		if(useGL && s.bpp == 124){
755 		//	y = 480 - y;
756 			glBegin(GL_POINTS);
757 			setGLColor(color);
758 			glVertex2f(cast(float)x, cast(float)y);
759 			glEnd();
760 			return;
761 		}
762 
763 
764 		ubyte *bufp;
765 
766 		if(s.bpp == 32){
767 			bufp = cast(ubyte*)(cast(ubyte*)s.surface.pixels + y*s.surface.pitch + x * 4);
768 				bufp[3] = cast(ubyte) color.a;
769 			        bufp[2] = cast(ubyte) color.r;
770 			        bufp[1] = cast(ubyte) color.g;
771         			bufp[0] = cast(ubyte) color.b;
772 		}else{
773 			bufp = cast(ubyte*)(cast(ubyte*)s.surface.pixels + y*s.surface.pitch + x * 3);
774 			if(color.a == 255){
775 			        bufp[2] = cast(ubyte) color.r;
776 		        	bufp[1] = cast(ubyte) color.g;
777 	        		bufp[0] = cast(ubyte) color.b;
778 			}
779 			else{
780 			        bufp[2] = cast(ubyte)(bufp[2] * (255-color.a) + (color.r * (color.a)) / 255);
781 			        bufp[1] = cast(ubyte)(bufp[1] * (255-color.a) + (color.g * (color.a)) / 255);
782 		        	bufp[0] = cast(ubyte)(bufp[0] * (255-color.a) + (color.b * (color.a)) / 255);
783 			}
784 		}
785 	}
786 
787 	void beginDrawingLines(){
788 		if(glbegin)
789 			throw new Exception("Can only draw one kind at a time");
790 		glbegin = true;
791 		if(useGL && s.bpp == 124)
792 			glBegin(GL_LINES);
793 	}
794 	void beginDrawingConnectedLines(){
795 		if(glbegin)
796 			throw new Exception("Can only draw one kind at a time");
797 		glbegin = true;
798 		if(useGL && s.bpp == 124)
799 			glBegin(GL_LINE_STRIP);
800 	}
801 	void beginDrawingPolygon(){
802 		if(glbegin)
803 			throw new Exception("Can only draw one kind at a time");
804 		glbegin = true;
805 		if(useGL && s.bpp == 124)
806 			glBegin(GL_POLYGON);
807 	}
808 	void beginDrawingTriangles(){
809 		if(glbegin)
810 			throw new Exception("Can only draw one kind at a time");
811 		glbegin = true;
812 		if(useGL && s.bpp == 124)
813 			glBegin(GL_TRIANGLES);
814 	}
815 	void beginDrawingBoxes(){
816 		if(glbegin)
817 			throw new Exception("Can only draw one kind at a time");
818 		glbegin = true;
819 		if(useGL && s.bpp == 124)
820 			glBegin(GL_QUADS);
821 	}
822 	void beginDrawingPoints(){
823 		if(glbegin)
824 			throw new Exception("Can only draw one kind at a time");
825 		glbegin = true;
826 		if(useGL && s.bpp == 124)
827 			glBegin(GL_POINTS);
828 	}
829 
830 	void endDrawingShapes(){
831 		if(!glbegin)
832 			return;
833 		glbegin = false;
834 		if(useGL && s.bpp == 124)
835 			glEnd();
836 	}
837 	void vertex(Point p){
838 		if(special) p += translate;
839 		if(!glbegin)
840 			throw new Exception("Can't use vertex without beginning first");
841 		if(useGL && s.bpp == 124)
842 			glVertex2i(p.x, p.y);
843 	}
844 	bool glbegin;
845 
846 	void drawImageRotated(Point where, Image i, float a, Color color = RGB(255,255,255)){
847 		if(i is null)
848 			return;
849 		glPushMatrix();
850 	//	glRotatef(a, cast(float)(where.x + 32) / s.width, cast(float)(where.y + 32) / s.height, 1);
851 		glTranslatef(where.x, where.y, 0);
852 		glRotatef(a, 0,0, 1);
853 		drawImage(XY(-i.width/2,-i.height/2), i, i.width, i.height, color);
854 		glPopMatrix();
855 	}
856 
857 	void drawImage(Point where, Image i, Color c){
858 		drawImage(where, i, 0, 0, c);
859 	}
860 
861 	void drawImage(Point where, Image i, int W = 0, int H = 0, Color c = RGBA(255,255,255,255)){
862 		if(i is null)
863 			return;
864 
865 		if(special) where += translate;
866 		if(glbegin)
867 			throw new Exception("Must end shape before doing anything else");
868 		if(useGL && s.bpp == 124){
869 			int x = where.x;
870 			int y = where.y;
871 			int w = W == 0 ? i.width : W;
872 			int h = H == 0 ? i.height : H;
873 
874 //			glColor4f(.5,.5,.5,1);
875 			setGLColor(c);
876 			glBindTexture(GL_TEXTURE_2D, i.toGL);
877 			glBegin(GL_QUADS);
878 				glTexCoord2f(0, 0); 			glVertex2i(x, y);
879 				glTexCoord2f(i.texWidth, 0); 		glVertex2i(x+w, y);
880 				glTexCoord2f(i.texWidth, i.texHeight); 	glVertex2i(x+w, y+h);
881 				glTexCoord2f(0, i.texHeight); 		glVertex2i(x, y+h);
882 			glEnd();
883 
884 			glBindTexture(GL_TEXTURE_2D, 0); // unbind the texture... I guess
885 				// I don't actually understand why that is needed
886 				// but without it, everything drawn after it is wrong (too light or dark)
887 			return;
888 		}
889 
890 		if((W == 0 && H == 0) || (i.width == W && i.height == H)){
891 		SDL_Rect r;
892 		r.x = cast(short)( where.x);
893 		r.y = cast(short)( where.y);
894 		r.w = cast(short)( i.width);
895 		r.h = cast(short)( i.height);
896 		if(locked)
897 			SDL_UnlockSurface(s.surface);
898 
899 		if(SDL_BlitSurface(i.surface, null, s.surface, &r) == -1)
900 			throw new Exception("blit");
901 
902 		if ( SDL_MUSTLOCK(s.surface) ) {
903 			if ( SDL_LockSurface(s.surface) < 0 ) {
904 				throw new Exception("lock");
905 			}
906 			locked = true;
907 		}
908 		} else { // quick and dirty scaling needed
909 			float dx = cast(float)i.width / cast(float)W;
910 			float dy = cast(float)i.height / cast(float)H;
911 			int X = where.x, Y = where.y;
912 
913 			for(float y = 0; y < i.height; y += dy){
914 				for(float x = 0; x < i.width; x += dx){
915 					putpixel(XY(X, Y), i.getPixel(XY(cast(int) x, cast(int) y)));
916 					X++;
917 				}
918 				X = where.x;
919 				Y++;
920 			}
921 
922 		}
923 
924 	}
925 
926 	void drawText(Point where, in char[] text, Color foreground = RGB(255,255,255), int font = 0){
927 		if(glbegin)
928 			throw new Exception("Must end shape before doing anything else");
929 
930 		if(useGL && s.bpp == 124){
931 			Image i = FontEngine.instance.renderText(text, RGB(255,255,255), font);
932 			drawImage(where, i, foreground);
933 		}else{
934 			Image i = FontEngine.instance.renderText(text, foreground, font);
935 			drawImage(where, i);
936 		}
937 	}
938 
939 	version(D_Version2) {
940 		import std.format;
941 		void drawTextf(T...)(Point where, T args) {
942 			char[] t;
943 			t.length = 80;
944 			int a = 0;
945 			void putc(dchar c){
946 				if(a == t.length)
947 					t.length = t.length + 80;
948 				t[a] = cast(char) c;
949 				a++;
950 			}
951 			formattedWrite(&putc, args);
952 			t.length = a;
953 
954 			drawText(where, t);
955 		}
956 	} else
957 	void drawTextf(Point where, ...){
958 		char[] t;
959 		t.length = 80;
960 		int a = 0;
961 		void putc(dchar c){
962 			if(a == t.length)
963 				t.length = t.length + 80;
964 			t[a] = cast(char) c;
965 			a++;
966 		}
967 		std.format.doFormat(&putc, _arguments, _argptr);
968 		t.length = a;
969 
970 		drawText(where, t);
971 	}
972 
973 	int wordLength(in char[] w, int font = 0){
974 		int a,b;
975 		FontEngine.instance.textSize(w, a, b, font);
976 		return a;
977 	}
978 
979 	int drawTextBoxed(Point where, char[] text, int width, int height, Color foreground = RGB(255,255,255), int font = 0){
980 		if(glbegin)
981 			throw new Exception("Must end shape before doing anything else");
982 
983 
984 		int xc;
985 		int yc = TTF_FontLineSkip(FontEngine.instance.fonts[font]);
986 
987 		int w = 0;
988 		int h = 0;
989 
990 		int l;
991 
992 char[] getWord(){
993 	int a = l;
994 	while(a < text.length && text[a] != ' ' && text[a] != '\n' && text[a] != '\t')
995 		a++;
996 	return text[l..a];
997 }
998 
999 int wordLength(in char[] w){
1000 	int a,b;
1001 	FontEngine.instance.textSize(w, a, b, font);
1002 	return a;
1003 }
1004 
1005 		Point ww = where;
1006 		while(l < text.length){
1007 			if(text[l] == '\n'){
1008 				l++;
1009 				goto newline;
1010 			}
1011 			if(wordLength(getWord()) + w > width){
1012 				goto newline;
1013 			}
1014 
1015 			if(!(w == 0 && text[l] == ' ')){
1016 				TTF_GlyphMetrics(FontEngine.instance.fonts[font], text[l], null,null,null,null,&xc);
1017 				drawText(ww, text[l..(l+1)], foreground, font);
1018 				w+=xc;
1019 				ww.x += xc;
1020 			}
1021 			l++;
1022 			if(w > (width - xc)){
1023 			newline:
1024 				w = 0;
1025 				h += yc;
1026 				ww.x = cast(short)(where.x);
1027 				ww.y += cast(short)(yc);
1028 
1029 				if(h > (height - yc))
1030 					break;
1031 			}
1032 		}
1033 		return l;
1034 	}
1035 
1036 	void drawTextCenteredHoriz(int top, char[] text, Color foreground, int font = 0){
1037 		Point where;
1038 		where.y = top;
1039 		int w, h;
1040 		TTF_SizeText(FontEngine.instance.fonts[font], std..string.toStringz(text), &w, &h);
1041 		where.x = (s.width - w) / 2;
1042 		drawText(where, text, foreground, font);
1043 	}
1044 
1045 	void line(Point start, Point end, Color color){
1046 		if(special){ start += translate; end += translate; }
1047 		if(glbegin)
1048 			throw new Exception("Must end shape before doing anything else");
1049 
1050 		if(useGL && s.bpp == 124){
1051 			setGLColor(color);
1052 			glBegin(GL_LINES);
1053 			glVertex2i(start.x, start.y);
1054 			glVertex2i(end.x, end.y);
1055 			glEnd();
1056 		}
1057 	}
1058 
1059 	void hline(Point start, int width, Color color){
1060 	if(useGL && s.bpp == 124){
1061 		line(start, XY(start.x + width, start.y), color);
1062 		return;
1063 	}
1064 		Point point = start;
1065 		for(int a = 0; a < width; a++){
1066 			putpixel(point, color);
1067 			point.x++;
1068 		}
1069 	}
1070 
1071 	void vline(Point start, int height, Color color){
1072 	if(useGL && s.bpp == 124){
1073 		line(start, XY(start.x, start.y + height), color);
1074 		return;
1075 	}
1076 
1077 		Point point = start;
1078 		for(int a = 0; a < height; a++){
1079 			putpixel(point, color);
1080 			point.y++;
1081 		}
1082 	}
1083 
1084 
1085 	void circle(Point center, int radius, Color color){
1086 		if(special) center += translate;
1087 		if(glbegin)
1088 			throw new Exception("Must end shape before doing anything else");
1089 
1090 	}
1091 
1092 	void arc(Point center, int radius, float start, float end, Color color){
1093 		if(special) center += translate;
1094 		if(glbegin)
1095 			throw new Exception("Must end shape before doing anything else");
1096 
1097 //		for(float a = start; a <= end; a+= (3.14159265358 / 50.0))
1098 //			putpixel((int)(cos(a) * (float)radius + center.x()),(int)( sin(a) * (float) radius + center.y()), color);
1099 	}
1100 
1101 	void box(Point upperLeft, Point lowerRight, Color color){
1102 		if(special) { upperLeft += translate; lowerRight += translate; }
1103 		if(glbegin)
1104 			throw new Exception("Must end shape before doing anything else");
1105 
1106 		if(useGL && s.bpp == 124){
1107 			int x1 = upperLeft.x;
1108 			int y1 = upperLeft.y;
1109 			int x2 = lowerRight.x;
1110 			int y2 = lowerRight.y;
1111 			glBegin(GL_QUADS);
1112 			//glColor3b(color.r, color.g, color.b);
1113 			setGLColor(color);
1114 			//glColor4f(1,1,1,1);
1115 			glVertex2i(x1, y1);
1116 			glVertex2i(x2, y1);
1117 			glVertex2i(x2, y2);
1118 			glVertex2i(x1, y2);
1119 			glEnd();
1120 			return;
1121 		}
1122 		SDL_Rect r;
1123 		r.x = cast(short) upperLeft.x;
1124 		r.y = cast(short) upperLeft.y;
1125 		r.w = cast(short) (lowerRight.x - upperLeft.x);
1126 		r.h = cast(short) (lowerRight.y - upperLeft.y);
1127 		if(s.bpp == 32)
1128 			SDL_FillRect(s.surface, &r, color.a << 24 | color.r << 16 | color.g << 8 | color.b);
1129 		else
1130 			SDL_FillRect(s.surface, &r, color.r << 16 | color.g << 8 | color.b);
1131 	}
1132 
1133 	void rect(Point upperLeft, Point lowerRight, Color color){
1134 		if(special) { upperLeft += translate; lowerRight += translate; }
1135 		if(glbegin)
1136 			throw new Exception("Must end shape before doing anything else");
1137 
1138 		if(useGL && s.bpp == 124){
1139 			int x1 = upperLeft.x;
1140 			int y1 = upperLeft.y;
1141 			int x2 = lowerRight.x;
1142 			int y2 = lowerRight.y;
1143 			glBegin(GL_LINE_LOOP);
1144 			//glColor3b(color.r, color.g, color.b);
1145 			setGLColor(color);
1146 			//glColor4f(1,1,1,1);
1147 			glVertex2i(x1, y1);
1148 			glVertex2i(x2+1, y1);
1149 			glVertex2i(x2, y2);
1150 			glVertex2i(x1, y2);
1151 			glEnd();
1152 			return;
1153 		}
1154 		/*
1155 		SDL_Rect r;
1156 		r.x = upperLeft.x;
1157 		r.y = upperLeft.y;
1158 		r.w = lowerRight.x - upperLeft.x;
1159 		r.h = lowerRight.y - upperLeft.y;
1160 		if(s.bpp == 32)
1161 			SDL_FillRect(s.surface, &r, color.a << 24 | color.r << 16 | color.g << 8 | color.b);
1162 		else
1163 			SDL_FillRect(s.surface, &r, color.r << 16 | color.g << 8 | color.b);
1164 		*/
1165 	}
1166 
1167 	void gbox(Point upperLeft, Point lowerRight, Color color1, Color color2, Color color3, Color color4){
1168 		if(special) { upperLeft += translate; lowerRight += translate; }
1169 		if(glbegin)
1170 			throw new Exception("Must end shape before doing anything else");
1171 
1172 		Color color = color1;
1173 		if(useGL && s.bpp == 124){
1174 			int x1 = upperLeft.x;
1175 			int y1 = upperLeft.y;
1176 			int x2 = lowerRight.x;
1177 			int y2 = lowerRight.y;
1178 			glBegin(GL_QUADS);
1179 			setGLColor(color1);
1180 			glVertex2i(x1, y1);
1181 			setGLColor(color2);
1182 			glVertex2i(x2, y1);
1183 			setGLColor(color4);
1184 			glVertex2i(x2, y2);
1185 			setGLColor(color3);
1186 			glVertex2i(x1, y2);
1187 			glEnd();
1188 		return;
1189 		}
1190 		SDL_Rect r;
1191 		r.x = cast(short) upperLeft.x;
1192 		r.y = cast(short) upperLeft.y;
1193 		r.w = cast(short) (lowerRight.x - upperLeft.x);
1194 		r.h = cast(short) (lowerRight.y - upperLeft.y);
1195 		if(s.bpp == 32)
1196 			SDL_FillRect(s.surface, &r, color.a << 24 | color.r << 16 | color.g << 8 | color.b);
1197 		else
1198 			SDL_FillRect(s.surface, &r, color.r << 16 | color.g << 8 | color.b);
1199 	}
1200 
1201 
1202 	void clear(){
1203 		if(useGL && s.bpp == 124){
1204 			glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_ACCUM_BUFFER_BIT);
1205 			return;
1206 		}
1207 		box(XY(0,0), XY(s.width, s.height), RGB(0,0,0));
1208 	}
1209 
1210 	void fill(Color color){
1211 		box(XY(0,0), XY(s.width, s.height), color);
1212 	}
1213 
1214 	void blend(Color color){
1215 		if(useGL && s.bpp == 124){
1216 			box(XY(0,0), XY(s.width, s.height), color);
1217 			return;
1218 		}
1219 
1220 		ubyte *bufp;
1221 
1222 		bufp = cast(ubyte*)s.surface.pixels;
1223 		for(int y = 0; y < s.height; y++)
1224 			for(int x = 0; x < s.width; x++){
1225 
1226 				bufp[2] = cast(ubyte)((bufp[2] * (255-color.a) + color.r * color.a) / 255);
1227 			        bufp[1] = cast(ubyte)((bufp[1] * (255-color.a) + color.g * color.a) / 255);
1228 		        	bufp[0] = cast(ubyte)((bufp[0] * (255-color.a) + color.b * color.a) / 255);
1229 
1230 				bufp += (s.bpp == 24 ? 3 : 4);
1231 			}
1232 	}
1233 
1234   private:
1235   	Drawable s;
1236 	bool locked;
1237 }
1238 
1239 
1240 
1241 
1242 
1243 
1244 
1245 int SDL_BlitSurface
1246 			(SDL_Surface *src, SDL_Rect *srcrect,
1247 			 SDL_Surface *dst, SDL_Rect *dstrect)
1248 {
1249 	return SDL_UpperBlit(src, srcrect, dst, dstrect);
1250 }
1251 
1252 bit SDL_MUSTLOCK(SDL_Surface *surface)
1253 {
1254 	return surface.offset || ((surface.flags &
1255 		(SDL_HWSURFACE | SDL_ASYNCBLIT | SDL_RLEACCEL)) != 0);
1256 }
1257 
1258 /* Quick utility function for texture creation */
1259 int power_of_two(int input)
1260 {
1261     int value = 1;
1262 
1263     while ( value < input ) {
1264         value <<= 1;
1265     }
1266     return value;
1267 }
1268 
1269 uint SDL_GL_LoadTexture(SDL_Surface *surface, float *texcoord)
1270 {
1271     uint texture;
1272     int w, h;
1273     SDL_Surface *image;
1274     SDL_Rect area;
1275     uint saved_flags;
1276     ubyte  saved_alpha;
1277 
1278     /* Use the surface width and height expanded to powers of 2 */
1279     w = power_of_two(surface.w);
1280     h = power_of_two(surface.h);
1281     texcoord[0] = 0.0f;         /* Min X */
1282     texcoord[1] = 0.0f;         /* Min Y */
1283     texcoord[2] = cast(float)surface.w / cast(float)w;  /* Max X */
1284     texcoord[3] = cast(float)surface.h / cast(float)h;  /* Max Y */
1285 
1286     image = SDL_CreateRGBSurface(
1287             SDL_SWSURFACE,
1288             w, h,
1289             32,
1290             0x000000FF,
1291             0x0000FF00,
1292             0x00FF0000,
1293             0xFF000000
1294                );
1295     if ( image == null) {
1296         throw new Exception("make image");
1297     }
1298 
1299 
1300     /* Save the alpha blending attributes */
1301     saved_flags = surface.flags&(SDL_SRCALPHA|SDL_RLEACCELOK);
1302     saved_alpha = surface.format.alpha;
1303     if ( (saved_flags & SDL_SRCALPHA) == SDL_SRCALPHA ) {
1304         SDL_SetAlpha(surface, 0, 0);
1305     }
1306 
1307     /* Copy the surface into the GL texture image */
1308     area.x = 0;
1309     area.y = 0;
1310     area.w = cast(ushort) surface.w;
1311     area.h = cast(ushort) surface.h;
1312     SDL_BlitSurface(surface, &area, image, &area);
1313 
1314     /* Restore the alpha blending attributes */
1315     if ( (saved_flags & SDL_SRCALPHA) == SDL_SRCALPHA ) {
1316         SDL_SetAlpha(surface, saved_flags, saved_alpha);
1317     }
1318 
1319     /* Create an OpenGL texture for the image */
1320     glGenTextures(1, &texture);
1321 
1322     glBindTexture(GL_TEXTURE_2D, texture);
1323     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1324     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1325 
1326     glTexImage2D(GL_TEXTURE_2D,
1327              0,
1328              GL_RGBA,
1329              w, h,
1330              0,
1331              GL_RGBA,
1332              GL_UNSIGNED_BYTE,
1333              image.pixels);
1334     SDL_FreeSurface(image); /* No longer needed */
1335 
1336 
1337 
1338     return texture;
1339 }
1340 
1341 
1342 
1343 
1344 
1345 
1346 		Color c1;
1347 		Color c2;
1348 		Color c3;
1349 		Color c4;
1350 static this(){
1351 	c1 = RGBA(0,0,255,160);
1352 	c2 = RGBA(0,0,255,160);
1353 	c3 = RGBA(0,0,255,160);
1354 	c4 = RGBA(0,0,0,160);
1355 }
1356 
1357 void drawHighlightBox(Painter p, Point where, int width, int height = 16){
1358 	p.gbox(where, where + XY(width, height), XRGB(c1, 128), XRGB(c2, 128), XRGB(c3, 128), XRGB(c4, 128));
1359 }
1360 
1361 // Real size is width + 8, height + 8. Size given if of the client area
1362 /*
1363 Point drawShadedRect(Painter p, Point where, int width, int height){
1364 	int x = where.x;
1365 	int y = where.y;
1366 
1367 	Color gray = RGB(128,128,128);
1368 
1369 	p.box(XY(x + 2, y), XY( x + 2 + width + 2, y + 4), gray);
1370 	p.box(XY(x + 2, y + height + 4), XY( x + 2 + width + 2, y + 4 + height + 4 ), gray);
1371 
1372 	p.box(XY(x, y + 2), XY(x + 4, y + 2 + height + 2), gray);
1373 	p.box(XY(x + 4 + width, y + 2), XY(x + 4 + width + 4, y + 2 + height + 2), gray);
1374 
1375 //	p.putpixel(XY(x + 1, y + 1), gray);
1376 //	p.putpixel(XY(x + 1, y + 4 + 3 + height), gray);
1377 //	p.putpixel(XY(x + 4 + width + 3, y + 1), gray);
1378 //	p.putpixel(XY(x + 4 + width + 3, y + 4 + 3 + height), gray);
1379 
1380 
1381 	p.hline(XY(x + 4, y + 1),              width + 2, white);
1382 	p.hline(XY(x + 4 - 2, y + 4 + height + 1), width + 2 + 1, white);
1383 
1384 	p.vline(XY(x + 1 - 1, y + 3),             height + 2, white);
1385 	p.vline(XY(x + 4 + width + 3, y + 3), height + 2, white);
1386 
1387 	p.gbox(XY(x + 4, y + 4), XY(x + width + 4, y + height + 4), c1, c2, c3, c4);
1388 
1389 	return XY(x + 4, y + 4);
1390 }
1391 */
1392 
1393 const int BORDER_WIDTH = 4;
1394 
1395 Point drawShadedRect(Painter p, Point where, int width, int height){
1396 	Color gray = RGB(128,128,128);
1397 
1398 	Point w;
1399 
1400 	// top section
1401 	w = where + XY( BORDER_WIDTH, 0);
1402 		p.box( 	w + XY(0, 				0 * BORDER_WIDTH / 4),
1403 			w + XY(width, 				1 * BORDER_WIDTH / 4),
1404 			gray);
1405 		p.box( 	w + XY(-BORDER_WIDTH / 2, 		1 * BORDER_WIDTH / 4),
1406 			w + XY(BORDER_WIDTH / 2 + width, 	3 * BORDER_WIDTH / 4),
1407 			white);
1408 		p.box( 	w + XY( -1 * BORDER_WIDTH / 4, 		3 * BORDER_WIDTH / 4),
1409 			w + XY( 1 * BORDER_WIDTH / 4 + width , 		4 * BORDER_WIDTH / 4),
1410 			black);
1411 	// bottom section
1412 	w = where + XY(BORDER_WIDTH, height + BORDER_WIDTH);
1413 		p.box( 	w + XY(-1 * BORDER_WIDTH / 4,		0 * BORDER_WIDTH / 4),
1414 			w + XY(1 * BORDER_WIDTH / 4 + width,			1 * BORDER_WIDTH / 4),
1415 			black);
1416 		p.box( 	w + XY(-BORDER_WIDTH / 2, 		1 * BORDER_WIDTH / 4),
1417 			w + XY(BORDER_WIDTH / 2 + width, 	3 * BORDER_WIDTH / 4),
1418 			white);
1419 		p.box( 	w + XY(-1 *BORDER_WIDTH / 4,		3 * BORDER_WIDTH / 4),
1420 			w + XY( 1 *BORDER_WIDTH / 4 + width, 	4 * BORDER_WIDTH / 4),
1421 			gray);
1422 
1423 	// left section
1424 	w = where + XY( 0, BORDER_WIDTH);
1425 		p.box( 	w + XY(0 * BORDER_WIDTH / 4, -1),
1426 			w + XY(1 * BORDER_WIDTH / 4, height + 1),
1427 			gray);
1428 		p.box( 	w + XY(1 * BORDER_WIDTH / 4, -BORDER_WIDTH / 2),
1429 			w + XY(3 * BORDER_WIDTH / 4, BORDER_WIDTH / 2 + height),
1430 			white);
1431 		p.box( 	w + XY(3 * BORDER_WIDTH / 4, 0),
1432 			w + XY(4 * BORDER_WIDTH / 4, height),
1433 			black);
1434 
1435 	// right section
1436 	w = where + XY( BORDER_WIDTH + width, BORDER_WIDTH);
1437 		p.box( 	w + XY(0 * BORDER_WIDTH / 4, 0),
1438 			w + XY(1 * BORDER_WIDTH / 4, height),
1439 			black);
1440 		p.box( 	w + XY(1 * BORDER_WIDTH / 4, -BORDER_WIDTH / 2),
1441 			w + XY(3 * BORDER_WIDTH / 4, BORDER_WIDTH / 2 + height),
1442 			white);
1443 		p.box( 	w + XY(3 * BORDER_WIDTH / 4, -1),
1444 			w + XY(4 * BORDER_WIDTH / 4, 1 + height),
1445 			gray);
1446 	w = where + XY(BORDER_WIDTH, BORDER_WIDTH);
1447 	p.gbox(w, w + XY(width, height), c1, c2, c3, c4);
1448 	return w;
1449 }
1450