HomeForumSourceResearchGuide
Sign in to contribute to source. how it works
Component ui.IOLayer by barry
expand copy to clipboardexpand
data WindowEventData {
	int button_id
	int x
	int y
	int exd1
	int exd2
}

data DropEventData {
	int x
	int y
	char path[]
}

interface UIPlaneLib {
	
	event ready()
	
	event mouseUp(WindowEventData wed)
	event mouseDown(WindowEventData wed)
	event mouseMove(WindowEventData wed)
	event mouseWheel(WindowEventData wed)
	event keyDown(WindowEventData wed)
	event keyUp(WindowEventData wed)
	event resize(WindowEventData wed)
	event move(WindowEventData wed)
	event drop(DropEventData ded)
	event close()
	
	event post_shutdown()
	
	int makeWindow()

	void addPoint(int phandle, int x, int y, byte r, byte g, byte b, byte a)
	void addLine(int phandle, int sx, int sy, int ex, int ey, int thickness, byte r, byte g, byte b, byte a, bool antiAlias)
	void addCurve(int phandle, Point points[], int isteps, int thickness, byte r, byte g, byte b, byte a, bool antiAlias)
	void addRect(int phandle, int x, int y, int w, int h, byte r, byte g, byte b, byte a)
	void addEllipse(int phandle, int x, int y, int w, int h, byte r, byte g, byte b, byte a)
	void addEllipseOutline(int phandle, int x, int y, int w, int h, int thickness, byte r, byte g, byte b, byte a)
	void addArc(int phandle, int x, int y, int w, int h, int start, int end, int lineWidth, byte r, byte g, byte b, byte a, bool antiAlias)
	void addPie(int phandle, int x, int y, int w, int h, int start, int end, bool chord, byte r, byte g, byte b, byte a)
	void addPolygon(int phandle, Point points[], byte r, byte g, byte b, byte a)
	void addPolygonOutline(int phandle, Point points[], int thickness, byte r, byte g, byte b, byte a)
	void addPolygonBezier(int phandle, Point points[], int isteps, byte r, byte g, byte b, byte a)
	void addPolygonBezierOutline(int phandle, Point points[], int isteps, int thickness, byte r, byte g, byte b, byte a)
	void addBitmap(int phandle, PixelMap pixels, Rect subRect, int x, int y, int scaledWidth, int scaledHeight, int rotation)
	void addBitmapYUV(int phandle, PixelMapYUV pixels, Rect subRect, int x, int y, int scaledWidth, int scaledHeight, int rotation)
	void addTextWith(int phandle, int fontHandle, int x, int y, int rotation, char text[], byte r, byte g, byte b, byte a)
	
	void pushSurface(int phandle, int x, int y, int w, int h, int sx, int sy, byte a)
	void popSurface(int phandle)
	
	void setSize(int phandle, int x, int y)
	void setPosition(int phandle, int x, int y)
	void setVisible(int phandle, bool b)
	void setResizable(int phandle, bool b)
	void setFullScreen(int phandle, bool b)
	void setTitle(int phandle, char t[])
	void setIcon(int phandle, PixelMap p)
	void setCursor(int phandle, byte type, Cursor c)
	
	void commitBuffer(int phandle)

	PixelMap getPixels(int phandle)
	
	void getResolution(int phandle, WH wh)
	
	void setBackgroundColor(int phandle, byte r, byte g, byte b, byte a)
	
	void maximiseWindow(int phandle)
	void minimiseWindow(int phandle)
	
	void getMaximisedScreenRect(int phandle, Rect r)
	
	void closeWindow(int phandle)
	
	bool initMediaLayer()
	void runSystemLoop()
	bool runOneSystemLoop()
	void shutdown()
	
	// -- fonts --
	int loadFont(char path[], int size)
	int getTextWidth(int phandle, char text[])
	void getFontMetrics(int phandle, FontMetrics metrics)
	char[] getFontName(int phandle)
	bool isFontFixedWidth(int phandle)
	void getTextBitmapWith(int phandle, char text[], PixelMap pixels, byte r, byte g, byte b, byte a)
	void unloadFont(int fd)
	}

component provides IOLayer, IOWindow(Destructor), Font(Destructor) requires native UIPlaneLib lib, io.Output out, data.IntUtil iu
	{
	static bool initDone = false
	static Mutex qlock = new Mutex()
	
	implementation IOWindow {
		
		int platformHandle
		Mutex windowStateLock = new Mutex()
		
		eventsink LibEvents(EventData ed)
			{
			if (ed.type == UIPlaneLib.[mouseUp])
				{
				WindowEventData wed = ed.details
				emitevent mouseUp(new MouseEvent(ed.type, wed.button_id, wed.x, wed.y, wed.exd1))
				}
				else if (ed.type == UIPlaneLib.[mouseDown])
				{
				WindowEventData wed = ed.details
				emitevent mouseDown(new MouseEvent(ed.type, wed.button_id, wed.x, wed.y))
				}
				else if (ed.type == UIPlaneLib.[mouseMove])
				{
				WindowEventData wed = ed.details
				emitevent mouseMove(new MouseEvent(ed.type, wed.button_id, wed.x, wed.y))
				}
				else if (ed.type == UIPlaneLib.[mouseWheel])
				{
				WindowEventData wed = ed.details
				emitevent mouseWheel(new MouseEvent(ed.type, wed.button_id, wed.x, wed.y, wed.exd1, wed.exd2))
				}
				else if (ed.type == UIPlaneLib.[keyDown])
				{
				WindowEventData wed = ed.details
				emitevent keyDown(new KeyEvent(wed.button_id))
				}
				else if (ed.type == UIPlaneLib.[keyUp])
				{
				WindowEventData wed = ed.details
				emitevent keyUp(new KeyEvent(wed.button_id))
				}
				else if (ed.type == UIPlaneLib.[resize])
				{
				WindowEventData wed = ed.details
				emitevent resizeWindow(new WH(wed.x, wed.y))
				}
				else if (ed.type == UIPlaneLib.[move])
				{
				WindowEventData wed = ed.details
				emitevent moveWindow(new Point(wed.x, wed.y))
				}
				else if (ed.type == UIPlaneLib.[drop])
				{
				DropEventData wed = ed.details
				emitevent fileDrop(new DropEvent(wed.x, wed.y, wed.path))
				}
				else if (ed.type == UIPlaneLib.[close])
				{
				emitevent closeWindow()
				}
			}
		
		IOWindow:IOWindow()
			{
			mutex(qlock)
				{
				if (!initDone)
					{
					throw new Exception("IOLayer must be initialised before windows are created")
					}
				}
			
			platformHandle = lib.makeWindow()
			if (platformHandle == 0) throw new Exception("Platform window creation failed")
			
			sinkevent LibEvents(lib)
			}
		
		void IOWindow:setVisible(bool b)
			{
			mutex(windowStateLock)
				{
				lib.setVisible(platformHandle, b)
				}
			}
		
		void IOWindow:setResizable(bool v)
			{
			mutex(windowStateLock)
				{
				lib.setResizable(platformHandle, v)
				}
			}
		
		void IOWindow:setFullScreen(bool v)
			{
			mutex(windowStateLock)
				{
				lib.setFullScreen(platformHandle, v)
				}
			}
		
		WH IOWindow:getResolution()
			{
			mutex(windowStateLock)
				{
				WH wh = new WH()
				lib.getResolution(platformHandle, wh)
				
				return wh
				}
			}
		
		void IOWindow:setTitle(char title[])
			{
			mutex(windowStateLock)
				{
				lib.setTitle(platformHandle, title)
				}
			}
		
		void IOWindow:setIcon(store PixelMap p)
			{
			mutex(windowStateLock)
				{
				lib.setIcon(platformHandle, p)
				}
			}
		
		void IOWindow:setCursor(byte cursorType, opt Cursor c)
			{
			mutex(windowStateLock)
				{
				lib.setCursor(platformHandle, cursorType, c)
				}
			}
		
		void IOWindow:close()
			{
			mutex(windowStateLock)
				{
				if (platformHandle != 0)
					{
					lib.closeWindow(platformHandle)
					platformHandle = 0
					}
				}
			}
		
		void IOWindow:paint()
			{
			mutex(windowStateLock)
				{
				lib.commitBuffer(platformHandle)
				}
			}
		
		void IOWindow:rect(Rect2D r)
			{
			mutex(windowStateLock)
				{
				lib.addRect(platformHandle, r.x, r.y, r.width, r.height, r.color.r, r.color.g, r.color.b, r.color.a)
				}
			}
		
		void IOWindow:rectOutline(Rect2D r, opt int thickness)
			{
			if (!(isset thickness)) thickness = 1

			int offset = thickness / 2

			line(new Line2D(r.x - offset, r.y, (r.x + r.width) + offset - 1, r.y, r.color), thickness)
			line(new Line2D(r.x, r.y, r.x, (r.y + r.height) + offset - 1, r.color), thickness)
			line(new Line2D((r.x + r.width) - 1, r.y, (r.x + r.width) - 1, (r.y + r.height) + offset - 1, r.color), thickness)
			line(new Line2D(r.x, (r.y + r.height) - 1, (r.x + r.width) - 1, (r.y + r.height) - 1, r.color), thickness)
			}
		
		void IOWindow:roundedRect(Rect2D r, int xRadius, int yRadius)
			{
			mutex(windowStateLock)
				{
				//top-left corner
				pie(new Arc2D((r.x + xRadius), r.y + yRadius, xRadius, yRadius, 270, 360, r.color))
				//top-right corner
				pie(new Arc2D((r.x + r.width - xRadius), r.y + yRadius, xRadius, yRadius, 0, 90, r.color))
				//bottom-right corner
				pie(new Arc2D((r.x + r.width - xRadius), (r.y + r.height - yRadius), xRadius, yRadius, 90, 180, r.color))
				//bottom-left corner
				pie(new Arc2D((r.x + xRadius), (r.y + r.height - yRadius), xRadius, yRadius, 180, 270, r.color))

				//middle
				rect(new Rect2D(r.x + xRadius, r.y, r.width - xRadius - xRadius, r.height, r.color))

				//left rect
				rect(new Rect2D(r.x, r.y + yRadius, xRadius, r.height - yRadius - yRadius, r.color))

				//right rect
				rect(new Rect2D(r.x + r.width - xRadius, r.y + yRadius, xRadius, r.height - yRadius - yRadius, r.color))
				}
			}
		
		void IOWindow:roundedRectOutline(Rect2D r, int xRadius, int yRadius, opt int thickness)
			{
			if (!(isset thickness)) thickness = 1

			int offset = thickness / 2
			int md = thickness % 2

			if (thickness <= 1)
				{
				line(new Line2D((r.x + xRadius), r.y, (r.x + r.width - xRadius), r.y, r.color), thickness)
				line(new Line2D(r.x, r.y + yRadius, r.x, (r.y + r.height - yRadius), r.color), thickness)
				line(new Line2D((r.x + r.width) - 1, r.y + yRadius, (r.x + r.width) - 1, (r.y + r.height - yRadius), r.color), thickness)
				line(new Line2D((r.x + xRadius), (r.y + r.height) - 2, (r.x + r.width - xRadius), (r.y + r.height) - 2, r.color), thickness)
				}
				else
				{
				//using "curves" for lines here seems to give us the best anti-alias matched position effect, relative to our corner arcs

				//top
				curve(new Curve2D(new Point[](new Point(r.x+xRadius, r.y), new Point(r.x+(r.width/2), r.y), new Point(r.x+r.width-xRadius, r.y)), r.color), 2, thickness)

				//left
				curve(new Curve2D(new Point[](new Point(r.x, r.y+yRadius), new Point(r.x, r.y+(r.height/2)), new Point(r.x, r.y + r.height - yRadius)), r.color), 2, thickness)

				//right
				curve(new Curve2D(new Point[](new Point(r.x+r.width-1, r.y+yRadius), new Point(r.x+r.width-1, r.y+(r.height/2)), new Point(r.x+r.width-1, r.y + r.height - yRadius)), r.color), 2, thickness)

				//bottom
				curve(new Curve2D(new Point[](new Point(r.x+xRadius, r.y+r.height-offset), new Point(r.x+(r.width/2), r.y+r.height-offset), new Point(r.x+r.width-xRadius, r.y+r.height-offset)), r.color), 2, thickness)
				}

			if (thickness <= 1)
				{
				Arc2D arc = null

				//top-left corner
				arc = new Arc2D((r.x + xRadius), r.y + yRadius, xRadius, yRadius, 270, 360, r.color)
				lib.addArc(platformHandle, arc.x, arc.y, arc.xRadius, arc.yRadius, arc.start, arc.end, thickness, r.color.r, r.color.g, r.color.b, r.color.a, false)
				//top-right corner
				arc = new Arc2D((r.x + r.width - xRadius)-1, r.y + yRadius, xRadius, yRadius, 0, 90, r.color)
				lib.addArc(platformHandle, arc.x, arc.y, arc.xRadius, arc.yRadius, arc.start, arc.end, thickness, r.color.r, r.color.g, r.color.b, r.color.a, false)
				//bottom-right corner
				arc = new Arc2D((r.x + r.width - xRadius)-1, (r.y + r.height - yRadius) - 2, xRadius, yRadius, 90, 180, r.color)
				lib.addArc(platformHandle, arc.x, arc.y, arc.xRadius, arc.yRadius, arc.start, arc.end, thickness, r.color.r, r.color.g, r.color.b, r.color.a, false)
				//bottom-left corner
				arc = new Arc2D((r.x + xRadius), (r.y + r.height - yRadius) - 2, xRadius, yRadius, 180, 270, r.color)
				lib.addArc(platformHandle, arc.x, arc.y, arc.xRadius, arc.yRadius, arc.start, arc.end, thickness, r.color.r, r.color.g, r.color.b, r.color.a, false)
				}
				else
				{
				//top-left corner
				arcOutline(new Arc2D((r.x + xRadius), r.y + yRadius, xRadius, yRadius, 270, 360, r.color), thickness)
				//top-right corner
				arcOutline(new Arc2D((r.x + r.width - xRadius)-1, r.y + yRadius, xRadius, yRadius, 0, 90, r.color), thickness)
				//bottom-right corner
				arcOutline(new Arc2D((r.x + r.width - xRadius)-1, (r.y + r.height - yRadius) - 2, xRadius, yRadius, 90, 180, r.color), thickness)
				//bottom-left corner
				arcOutline(new Arc2D((r.x + xRadius), (r.y + r.height - yRadius) - 2, xRadius, yRadius, 180, 270, r.color), thickness)
				}
			}
		
		void IOWindow:ellipse(Ellipse2D r)
			{
			mutex(windowStateLock)
				{
				lib.addEllipse(platformHandle, r.x, r.y, r.xRadius, r.yRadius, r.color.r, r.color.g, r.color.b, r.color.a)
				}
			}
		
		void IOWindow:ellipseOutline(Ellipse2D r, opt int thickness)
			{
			if (!(isset thickness)) thickness = 1

			mutex(windowStateLock)
				{
				lib.addEllipseOutline(platformHandle, r.x, r.y, r.xRadius, r.yRadius, thickness, r.color.r, r.color.g, r.color.b, r.color.a)
				}
			}
		
		void IOWindow:arc(Arc2D r)
			{
			mutex(windowStateLock)
				{
				lib.addPie(platformHandle, r.x, r.y, r.xRadius, r.yRadius, r.start, r.end, true, r.color.r, r.color.g, r.color.b, r.color.a)
				}
			}
		
		void IOWindow:arcOutline(Arc2D r, opt int lineWidth)
			{
			if (!(isset lineWidth)) lineWidth = 1

			mutex(windowStateLock)
				{
				lib.addArc(platformHandle, r.x, r.y, r.xRadius, r.yRadius, r.start, r.end, lineWidth, r.color.r, r.color.g, r.color.b, r.color.a, true)
				}
			}
		
		void IOWindow:pie(Arc2D r)
			{
			mutex(windowStateLock)
				{
				lib.addPie(platformHandle, r.x, r.y, r.xRadius, r.yRadius, r.start, r.end, false, r.color.r, r.color.g, r.color.b, r.color.a)
				}
			}
		
		void IOWindow:polygon(Polygon2D r)
			{
			mutex(windowStateLock)
				{
				lib.addPolygon(platformHandle, r.points, r.color.r, r.color.g, r.color.b, r.color.a)
				}
			}
		
		void IOWindow:polygonOutline(Polygon2D r)
			{
			int thickness = 1

			mutex(windowStateLock)
				{
				lib.addPolygonOutline(platformHandle, r.points, thickness, r.color.r, r.color.g, r.color.b, r.color.a)
				}
			}
		
		void IOWindow:polygonBezier(Polygon2D r, int isteps)
			{
			mutex(windowStateLock)
				{
				lib.addPolygonBezier(platformHandle, r.points, isteps, r.color.r, r.color.g, r.color.b, r.color.a)
				}
			}
		
		void IOWindow:line(Line2D l, opt int thickness)
			{
			if (!(isset thickness)) thickness = 1

			mutex(windowStateLock)
				{
				lib.addLine(platformHandle, l.sx, l.sy, l.ex, l.ey, thickness, l.color.r, l.color.g, l.color.b, l.color.a, false)
				}
			}
		
		void IOWindow:curve(Curve2D l, int isteps, opt int thickness)
			{
			if (!(isset thickness)) thickness = 1

			mutex(windowStateLock)
				{
				lib.addCurve(platformHandle, l.points, isteps, thickness, l.color.r, l.color.g, l.color.b, l.color.a, false)
				}
			}
		
		void IOWindow:point(Point2D p)
			{
			mutex(windowStateLock)
				{
				lib.addPoint(platformHandle, p.x, p.y, p.color.r, p.color.g, p.color.b, p.color.a)
				}
			}
		
		void IOWindow:pixels(PixelMap map, int x, int y, opt int scaledWidth, int scaledHeight, int rotation, Rect subRect)
			{
			if (!(isset scaledWidth)) scaledWidth = map.size.width
			if (!(isset scaledHeight)) scaledHeight = map.size.height

			mutex(windowStateLock)
				{
				lib.addBitmap(platformHandle, map, subRect, x, y, scaledWidth, scaledHeight, rotation)
				}
			}
		
		void IOWindow:pixelsYUV(PixelMapYUV map, int x, int y, opt int scaledWidth, int scaledHeight, int rotation, Rect subRect)
			{
			if (!(isset scaledWidth)) scaledWidth = map.size.width
			if (!(isset scaledHeight)) scaledHeight = map.size.height

			mutex(windowStateLock)
				{
				lib.addBitmapYUV(platformHandle, map, subRect, x, y, scaledWidth, scaledHeight, rotation)
				}
			}
		
		void IOWindow:text(Point2D origin, Font f, char text[], opt int rotation)
			{
			//check if f is implemented by this component and if so we use its internal font handle; otherwise we fall back on Font.getBitmap() (which is slow)
			mutex(windowStateLock)
				{
				if (implements f)
					{
					lib.addTextWith(platformHandle, f.platformHandle, origin.x, origin.y, rotation, text, origin.color.r, origin.color.g, origin.color.b, origin.color.a)
					}
					else
					{
					PixelMap px = f.getPixels(text, origin.color)
					lib.addBitmap(platformHandle, px, null, origin.x, origin.y, px.size.width, px.size.height, rotation)
					}
				}
			}
		
		void IOWindow:pushSurface(Rect rect, int xscr, int yscr, byte alpha)
			{
			mutex(windowStateLock)
				{
				lib.pushSurface(platformHandle, rect.x, rect.y, rect.width, rect.height, xscr, yscr, alpha)
				}
			}
		
		void IOWindow:popSurface()
			{
			mutex(windowStateLock)
				{
				lib.popSurface(platformHandle)
				}
			}
		
		void IOWindow:setPosition(int x, int y)
			{
			mutex(windowStateLock)
				{
				lib.setPosition(platformHandle, x, y)
				}
			}
		
		void IOWindow:setSize(int x, int y)
			{
			mutex(windowStateLock)
				{
				lib.setSize(platformHandle, x, y)
				}
			}
		
		void IOWindow:setBackground(store Color c)
			{
			mutex(windowStateLock)
				{
				lib.setBackgroundColor(platformHandle, c.r, c.g, c.b, c.a)
				}
			}
		
		PixelMap IOWindow:getPixels()
			{
			return lib.getPixels(platformHandle)
			}
		
		void Destructor:destroy()
			{
			//if we're being destroyed, we need to free the window handle in the UI framework, otherwise it will have a bad reference
			close()
			}
		}
	
	implementation IOLayer {
		
		eventsink LibSysEvents(EventData ed)
			{
			if (ed.type == UIPlaneLib.[ready])
				{
				emitevent ready()
				}
				else if (ed.type == UIPlaneLib.[post_shutdown])
				{
				emitevent recvShutdown()
				}
			}
		
		IOLayer:IOLayer()
			{
			mutex(qlock)
				{
				if (initDone)
					{
					throw new Exception("initialisation has already been done")
					}
					else
					{
					initDone = true
					}
				}
			
			sinkevent LibSysEvents(lib)
			
			if (!lib.initMediaLayer())
				{
				throw new Exception("initialisation failed")
				}
			}
		
		void IOLayer:run()
			{
			mutex(qlock)
				{
				if (!initDone)
					{
					throw new Exception("system must first be initialised via init()")
					}
				}
			
			lib.runSystemLoop()
			}
		
		bool IOLayer:loop()
			{
			return !lib.runOneSystemLoop()
			}
		
		void IOLayer:shutdown()
			{
			lib.shutdown()
			
			initDone = false
			}
		
		}
	
	implementation Font{
		int platformHandle
		int fontSize
		char source[]
		
		Font:Font(char path[], int size)
			{
			fontSize = size
			source = path
			platformHandle = lib.loadFont(path, size)
			
			if (platformHandle == 0) throw new Exception("Failed to load font $path")
			}
		
		int Font:getSize()
			{
			return fontSize
			}
		
		char[] Font:getSource()
			{
			return source
			}
		
		FontMetrics Font:getFontMetrics()
			{
			FontMetrics result = new FontMetrics()
			lib.getFontMetrics(platformHandle, result)
			return result
			}
		
		char[] Font:getFontName()
			{
			return lib.getFontName(platformHandle)
			}
		
		bool Font:isFixedCharacterWidth()
			{
			return lib.isFontFixedWidth(platformHandle)
			}
		
		int Font:getTextWidth(char text[])
			{
			return lib.getTextWidth(platformHandle, text)
			}
		
		PixelMap Font:getPixels(char string[], Color textColor)
			{
			PixelMap result = new PixelMap(new WH())
			lib.getTextBitmapWith(platformHandle, string, result, textColor.r, textColor.g, textColor.b, textColor.a)
			return result
			}
		
		void Destructor:destroy()
			{
			lib.unloadFont(platformHandle)
			}
		}
	}
Linked auxiliary files
fonts/LiberationMono.ttf
fonts/SourceSansPro.ttf
Revision history
To propose a new revision to this entity, use dana source put -uc your/new/version.dn -n ui.IOLayer -m "reason for update" -u yourUsername
Version 4 (this version) by barry
Notes for this version: Adds YUV pixel map support
Version 3 by barry
Version 2 by barry
Version 1 by barry