# HG changeset patch # User daveb # Date 1281947382 -34200 # Node ID 98980cee8c5be7550d63d023f9f6007861383190 # Parent cdd4fc728d9480c81f8fc5eea37c558d34264225 More work trying to support OpenGL as a renderer diff -r cdd4fc728d94 -r 98980cee8c5b doodle/gtk/cairo_canvas.d --- a/doodle/gtk/cairo_canvas.d Mon Aug 16 17:23:09 2010 +0930 +++ b/doodle/gtk/cairo_canvas.d Mon Aug 16 17:59:42 2010 +0930 @@ -35,7 +35,7 @@ import std.stdio; } -final class Canvas : Table, private IViewport { +final class CairoCanvas : Table, private IViewport { this(in Layer[] layers, IEventHandler eventHandler, IGrid grid, in double pixelsPerMillimetre) { super(3, 3, 0); diff -r cdd4fc728d94 -r 98980cee8c5b doodle/gtk/opengl.d --- a/doodle/gtk/opengl.d Mon Aug 16 17:23:09 2010 +0930 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,15 +0,0 @@ -module doodle.gtk.opengl; - -public { - import doodle.tk.renderer; -} - -/* -final class OpenGLRenderer : Renderer { - - // Drawing overrides: - - void setLineStyle(LineStyle style) { - } -} -*/ diff -r cdd4fc728d94 -r 98980cee8c5b doodle/gtk/opengl_canvas.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doodle/gtk/opengl_canvas.d Mon Aug 16 17:59:42 2010 +0930 @@ -0,0 +1,570 @@ +module doodle.gtk.opengl_canvas; + +public { + import doodle.dia.icanvas; + import doodle.gtk.events; +} + +private { + import doodle.core.logging; + import doodle.tk.screen_model; + import doodle.dia.layer_stack; + import doodle.gtk.cairo_renderer; + + import cairo.Surface; + import cairo.Context; + + import gtk.Widget; + import gtk.Toolbar; + import gtk.Table; + import gtk.HRuler; + import gtk.VRuler; + import gtk.Range; + import gtk.HScrollbar; + import gtk.VScrollbar; + import gtk.DrawingArea; + import gtk.Adjustment; + + import gdk.Drawable; + + import gtkc.gtk; + import gtkc.gtktypes; + //import gtkc.gdktypes; + + import std.math; + import std.stdio; +} + +final class OpenGLCanvas : Table, private IViewport { + this(in Layer[] layers, IEventHandler eventHandler, IGrid grid, in double pixelsPerMillimetre) { + super(3, 3, 0); + + _damageScreen = Rectangle.DEFAULT; + + _eventHandler = eventHandler; + _grid = grid; + _pixelsPerMillimetre = pixelsPerMillimetre; + + _layerStack = new LayerStack(layers); + + // Create our child widgets and register callbacks + + _hRuler = new HRuler; + attach(_hRuler, + 1, 2, + 0, 1, + AttachOptions.FILL | AttachOptions.EXPAND, AttachOptions.SHRINK, + 0, 0); + _hRuler.setMetric(MetricType.PIXELS); + + _vRuler = new VRuler; + attach(_vRuler, + 0, 1, + 1, 2, + AttachOptions.SHRINK, AttachOptions.FILL | AttachOptions.EXPAND, + 0, 0); + _vRuler.setMetric(MetricType.PIXELS); + + _drawingArea = new GLDrawingArea; + _drawingArea.addOnRealize(&onRealize); + _drawingArea.addOnConfigure(&onConfigure); + _drawingArea.addOnExpose(&onExpose); + _drawingArea.addOnButtonPress(&onButtonPress); + _drawingArea.addOnButtonRelease(&onButtonRelease); + _drawingArea.addOnKeyPress(&onKeyPressEvent); + _drawingArea.addOnKeyRelease(&onKeyReleaseEvent); + _drawingArea.addOnMotionNotify(&onMotionNotify); + _drawingArea.addOnScroll(&onScroll); + _drawingArea.addOnEnterNotify(&onEnterNotify); + _drawingArea.addOnLeaveNotify(&onLeaveNotify); + + _drawingArea.addOnFocusIn(&onFocusIn); + _drawingArea.addOnFocusOut(&onFocusOut); + _drawingArea.addOnMoveFocus(&onMoveFocus); + _drawingArea.addOnGrabBroken(&onGrabBroken); + _drawingArea.addOnGrabFocus(&onGrabFocus); + _drawingArea.addOnGrabNotify(&onGrabNotify); + // addOnPopupMenu + // addOnQueryTooltip + // addOnSelection* + _drawingArea.setEvents(EventMask.EXPOSURE_MASK | + EventMask.POINTER_MOTION_MASK | + EventMask.POINTER_MOTION_HINT_MASK | + EventMask.BUTTON_MOTION_MASK | + EventMask.BUTTON_PRESS_MASK | + EventMask.BUTTON_RELEASE_MASK | + EventMask.KEY_PRESS_MASK | + EventMask.KEY_RELEASE_MASK | + EventMask.ENTER_NOTIFY_MASK | + EventMask.LEAVE_NOTIFY_MASK | + EventMask.FOCUS_CHANGE_MASK | + EventMask.SCROLL_MASK); + + _drawingArea.setCanFocus(true); + + attach(_drawingArea, + 1, 2, + 1, 2, + AttachOptions.FILL | AttachOptions.EXPAND, AttachOptions.FILL | AttachOptions.EXPAND, + 0, 0); + + // value, lower, upper, step-inc, page-inc, page-size + // Give the adjustments dummy values until we receive a configure + _hAdjustment = new Adjustment(0.0, 0.0, 1.0, 0.2, 0.5, 0.5); + _hAdjustment.addOnValueChanged(&onAdjustmentValueChanged); + _hScrollbar = new HScrollbar(_hAdjustment); + _hScrollbar.setInverted(false); + attach(_hScrollbar, + 1, 2, + 2, 3, + AttachOptions.FILL | AttachOptions.EXPAND, + AttachOptions.SHRINK, + 0, 0); + + _vAdjustment = new Adjustment(0.0, 0.0, 1.0, 0.2, 0.5, 0.5); + _vAdjustment.addOnValueChanged(&onAdjustmentValueChanged); + _vScrollbar = new VScrollbar(_vAdjustment); + _vScrollbar.setInverted(true); + attach(_vScrollbar, + 2, 3, + 1, 2, + AttachOptions.SHRINK, + AttachOptions.FILL | AttachOptions.EXPAND, + 0, 0); + + _cursors = [ + Cursor.DEFAULT : CursorType.ARROW, + Cursor.HAND : CursorType.HAND1, + Cursor.CROSSHAIR : CursorType.CROSSHAIR, + Cursor.PENCIL : CursorType.PENCIL + ]; + } + + protected { // XXX the compiler complains about unimplemented methods if this is private + + // IViewport overrides: + + void zoomRelative(in Point screenDatum, in double factor) { + _screenModel.zoomRelative(factor, screenDatum); + consolidateBounds; + updateAdjustments; + updateRulers; + _grid.zoomChanged(_screenModel.zoom); + queueDraw; + } + + void panRelative(in Vector screenDisplacement) { + _screenModel.panRelativeScreen(screenDisplacement); + consolidateBounds; + updateAdjustments; + updateRulers; + queueDraw; + } + + void setCursor(in Cursor cursor) { + _drawingArea.setCursor(new gdk.Cursor.Cursor(_cursors[cursor])); + } + + void damageModel(in Rectangle area) { + _damageScreen = _damageScreen | _screenModel.modelToScreen(area); + } + + void damageScreen(in Rectangle area) { + _damageScreen = _damageScreen | area; + } + } + + private { + + void initialiseBounds(in Rectangle viewBoundsScreen) { + Rectangle layerBounds = _layerStack.bounds; + Rectangle paddedLayerBounds = growCentre(layerBounds, 2 * layerBounds.size); + _screenModel = new ScreenModel(0.25 * _pixelsPerMillimetre, paddedLayerBounds, viewBoundsScreen); + _grid.zoomChanged(_screenModel.zoom); + + updateAdjustments; + updateRulers; + } + + void consolidateBounds() { + Rectangle layerBounds = _layerStack.bounds; + Rectangle paddedLayerBounds = growCentre(layerBounds, 2 * layerBounds.size); + _screenModel.consolidateCanvasBounds(paddedLayerBounds); + + updateAdjustments; + updateRulers; + } + + bool onConfigure(GdkEventConfigure * event, Widget widget) { + assert(widget is _drawingArea); + assert(event); + + trace("onConfigure %sx%s", event.width, event.height); + + Rectangle viewBoundsScreen = Rectangle(Point(0.0, 0.0), Vector(cast(double)event.width, cast(double)event.height)); + if (_screenModel is null) { initialiseBounds(viewBoundsScreen); } + else { consolidateBounds; } + + return true; + } + + bool onExpose(GdkEventExpose * event, Widget widget) { + assert(widget is _drawingArea); + assert(event); + + trace("onExpose %sx%s", event.area.width, event.area.height); + + /+ + Drawable dr = _drawingArea.getWindow; + + int width, height; + dr.getSize(width, height); + //trace("Got expose %dx%d\n", width, height); + + scope modelCr = new Context(dr); + scope screenCr = new Context(dr); + + Rectangle screenDamage = + event is null ? _screenModel.viewBoundsScreen : + Rectangle(_screenModel.viewBoundsScreen.position + Vector(cast(double)event.area.x, _screenModel.viewBoundsScreen.h - cast(double)(event.area.y + event.area.height)), + Vector(cast(double)event.area.width, cast(double)event.area.height)); + + Rectangle modelDamage = _screenModel.screenToModel(screenDamage); + + modelCr.save; screenCr.save; { + { + // Setup model context and clip + modelCr.translate(0.0, _screenModel.viewBoundsScreen.h); + modelCr.scale(_screenModel.zoom, -_screenModel.zoom); + + immutable Point viewLeftBottom = _screenModel.screenToModel(Point(0.0, 0.0)); + modelCr.translate(-viewLeftBottom.x, -viewLeftBottom.y); + + modelCr.rectangle(modelDamage.x0, modelDamage.y0, modelDamage.w, modelDamage.h); + modelCr.clip; + } + + { + // Setup screen context and clip + screenCr.translate(0.0, _screenModel.viewBoundsScreen.h); + screenCr.scale(1.0, -1.0); + + screenCr.rectangle(screenDamage.x0, screenDamage.y0, screenDamage.w, screenDamage.h); + screenCr.clip; + } + + screenCr.save; { + // Fill the background with light grey + screenCr.setSourceRgba(0.9, 0.9, 0.9, 1.0); + screenCr.rectangle(screenDamage.x0, screenDamage.y0, screenDamage.w, screenDamage.h); + screenCr.fill; + } screenCr.restore; + + _layerStack.draw(screenDamage, new CairoRenderer(screenCr), + modelDamage, new CairoRenderer(modelCr)); + } screenCr.restore; modelCr.restore; + + return true; + +/ + + return false; + } + + bool onButtonPress(GdkEventButton * event, Widget widget) { + assert(widget is _drawingArea); + _eventHandler.handleButtonPress(this, makeButtonEvent(event, _screenModel)); + fixDamage; + return true; + } + + bool onButtonRelease(GdkEventButton * event, Widget widget) { + assert(widget is _drawingArea); + _eventHandler.handleButtonRelease(this, makeButtonEvent(event, _screenModel)); + fixDamage; + return true; + } + + bool onKeyPressEvent(GdkEventKey * event, Widget widget) { + assert(widget is _drawingArea); + _eventHandler.handleKeyPress(this, makeKeyEvent(event, _screenModel)); + fixDamage; + return true; + } + + bool onKeyReleaseEvent(GdkEventKey * event, Widget widget) { + assert(widget is _drawingArea); + _eventHandler.handleKeyRelease(this, makeKeyEvent(event, _screenModel)); + fixDamage; + return true; + } + + bool onMotionNotify(GdkEventMotion * event, Widget widget) { + assert(widget is _drawingArea); + + // Pass the events on to the rulers so that they update + gtk_widget_event(_hRuler.getWidgetStruct(), cast(GdkEvent *)event); + gtk_widget_event(_vRuler.getWidgetStruct(), cast(GdkEvent *)event); + + _eventHandler.handleMotion(this, makeMotionEvent(event, _screenModel)); + fixDamage; + return true; + } + + bool onScroll(GdkEventScroll * event, Widget widget) { + assert(widget is _drawingArea); + _eventHandler.handleScroll(this, makeScrollEvent(event, _screenModel)); + fixDamage; + return true; + } + + bool onEnterNotify(GdkEventCrossing * event, Widget widget) { + trace("1"); + assert(widget is _drawingArea); + trace("2"); + _eventHandler.handleEnter(this, makeCrossingEvent(event, _screenModel)); + trace("3"); + fixDamage; + trace("4"); + return true; + } + + bool onLeaveNotify(GdkEventCrossing * event, Widget widget) { + assert(widget is _drawingArea); + _eventHandler.handleLeave(this, makeCrossingEvent(event, _screenModel)); + fixDamage; + return true; + } + + bool onFocusIn(GdkEventFocus * event, Widget widget) { + trace("onFocusIn"); + return true; + } + + bool onFocusOut(GdkEventFocus * event, Widget widget) { + trace("onFocusOut"); + return true; + } + + void onMoveFocus(GtkDirectionType direction, Widget widget) { + trace("onMoveFocus"); + } + + bool onGrabBroken(gdk.Event.Event event, Widget widget) { + trace("onGrabBroken"); + return true; + } + + void onGrabFocus(Widget widget) { + //trace("onGrabFocus"); + } + + void onGrabNotify(gboolean what, Widget widget){ + trace("onGrabNotify"); + } + + void onAdjustmentValueChanged(Adjustment adjustment) { + GtkAdjustment * hGtkAdjustment = _hAdjustment.getAdjustmentStruct; + GtkAdjustment * vGtkAdjustment = _vAdjustment.getAdjustmentStruct; + + Point oldViewLeftBottom = _screenModel.screenToModel(Point(0.0, 0.0)); + Point newViewLeftBottom = Point(gtk_adjustment_get_value(hGtkAdjustment), + gtk_adjustment_get_value(vGtkAdjustment)); + + _screenModel.panRelativeModel(newViewLeftBottom - oldViewLeftBottom); + + updateRulers; + queueDraw; + } + + void updateRulers() { + immutable Point viewLeftBottom = _screenModel.screenToModel(_screenModel.viewBoundsScreen.corner0); + immutable Point viewRightTop = _screenModel.screenToModel(_screenModel.viewBoundsScreen.corner1); + + // Define these just to obtain the position + // below so we can preserve it + double lower, upper, position, maxSize; + + _hRuler.getRange(lower, upper, position, maxSize); + _hRuler.setRange(viewLeftBottom.x, + viewRightTop.x, + position, + _screenModel.zoom * 50.0); + + _vRuler.getRange(lower, upper, position, maxSize); + _vRuler.setRange(viewRightTop.y, + viewLeftBottom.y, + position, + _screenModel.zoom * 50.0); + } + + void updateAdjustments() { + immutable Point viewLeftBottom = _screenModel.screenToModel(Point(0.0, 0.0)); + immutable Point viewRightTop = _screenModel.screenToModel(_screenModel.viewBoundsScreen.corner1); + + // Adjust the canvas size if necessary + _screenModel.canvasAccommodate(Rectangle(viewLeftBottom, viewRightTop)); + + Rectangle viewBoundsModel = _screenModel.viewBoundsModel; + + // Update the adjustments + + GtkAdjustment * hGtkAdjustment = _hAdjustment.getAdjustmentStruct; + GtkAdjustment * vGtkAdjustment = _vAdjustment.getAdjustmentStruct; + + gtk_adjustment_set_lower(hGtkAdjustment, _screenModel.canvasBoundsModel.x0); + gtk_adjustment_set_upper(hGtkAdjustment, _screenModel.canvasBoundsModel.x1); + gtk_adjustment_set_value(hGtkAdjustment, viewLeftBottom.x); + gtk_adjustment_set_step_increment(hGtkAdjustment, _screenModel.canvasBoundsModel.w / 16.0); + gtk_adjustment_set_page_increment(hGtkAdjustment, _screenModel.canvasBoundsModel.w / 4.0); + gtk_adjustment_set_page_size(hGtkAdjustment, viewBoundsModel.w); + + gtk_adjustment_set_lower(vGtkAdjustment, _screenModel.canvasBoundsModel.y0); + gtk_adjustment_set_upper(vGtkAdjustment, _screenModel.canvasBoundsModel.y1); + gtk_adjustment_set_value(vGtkAdjustment, viewLeftBottom.y); + gtk_adjustment_set_step_increment(vGtkAdjustment, _screenModel.canvasBoundsModel.h / 16.0); + gtk_adjustment_set_page_increment(vGtkAdjustment, _screenModel.canvasBoundsModel.h / 4.0); + gtk_adjustment_set_page_size(vGtkAdjustment, viewBoundsModel.h); + + _hAdjustment.changed; + _hAdjustment.valueChanged; + _vAdjustment.changed; + _vAdjustment.valueChanged; + } + + void fixDamage() { + if (_damageScreen.valid) { + int x, y, w, h; + _damageScreen.getQuantised(x, y, w, h); + _drawingArea.queueDrawArea(x, cast(int)_screenModel.viewBoundsScreen.h - (y + h), w, h); + _damageScreen = Rectangle.DEFAULT; + } + } + + void onRealize(Widget widget) { + assert(widget is _drawingArea); + _drawingArea.grabFocus(); + } + + IEventHandler _eventHandler; + IGrid _grid; + double _pixelsPerMillimetre; + LayerStack _layerStack; + + // Child widgets: + HRuler _hRuler; + VRuler _vRuler; + DrawingArea _drawingArea; + Adjustment _hAdjustment; + HScrollbar _hScrollbar; + Adjustment _vAdjustment; + VScrollbar _vScrollbar; + + Rectangle _damageScreen; + ScreenModel _screenModel; + + immutable CursorType[Cursor] _cursors; + } +} + +private import gtk.DrawingArea; +private import glgtk.GLCapability; +private import glgdk.GLDrawable; +private import glgdk.GLConfig; +private import glgdk.GLContext; +private import gtkglc.glgdktypes; + +private import gtkglc.gl; +private import gtkglc.glu; + +private import gdk.Event; + +private import gtk.Widget; + +private import gtk.Main; +private import gtk.MainWindow; + +class GLDrawingArea : DrawingArea { + GLfloat width; + GLfloat height; + + /** need to include the mixin to add GL capabilities to this widget */ + mixin GLCapability; + + /** + * Construct a simple DrawingArea and sets the GLCapabilities + */ + this() { + setGLCapability(); // set the GL capabilities for this widget + } + + /** + * put any gl initializations here + * returns true to consume the event + */ + bool initGL() { + trace("initGL"); + + resizeGL(null); + return true; + } + + /** + * This method is called every time the window must be paint or repaint + * This is where you put the OpenGL call to draw something. + * This method call be called directly by the application without an event object + * to force redrawing of the scene. + * returns true to consume the event + */ + bool drawGL(GdkEventExpose* event = null) { + trace("drawGL"); + + glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glLoadIdentity (); + + gluLookAt(0, 0, 10, 0, 0, 0, 0, 1,0); //Set the camera position + + //Just Draw a tri-colored triangle + glBegin(GL_TRIANGLES); + glColor3f(1.0f,0.0f,0.0f); + glVertex3f( 0.0f, 1.0f, 0.0f); + glColor3f(0.0f,1.0f,0.0f); + glVertex3f(-1.0f,-1.0f, 0.0f); + glColor3f(0.0f,0.0f,1.0f); + glVertex3f( 1.0f,-1.0f, 0.0f); + glEnd(); + + return true; + } + + /** + * This method is called when the window is resized + * returns true to consume the event + */ + bool resizeGL(GdkEventConfigure* event = null) { + trace("resizeGL"); + + if (event == null) { + width = getWidth(); + height = getHeight(); + } + else { + width = event.width; + height = event.height; + } + + trace("Size %sx%s", width, height); + + glViewport(0, 0, cast(int)width, cast(int)height); //Adjust the viewport according to new window dimensions + + /* + * Update the projection Matrix accoding to the new dimension + * and reset the OpenGL state to MODELVIEW + */ + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluPerspective(20, width/height, 0.1, 10); + glMatrixMode(GL_MODELVIEW); + + return false; + } +} diff -r cdd4fc728d94 -r 98980cee8c5b doodle/gtk/opengl_renderer.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doodle/gtk/opengl_renderer.d Mon Aug 16 17:59:42 2010 +0930 @@ -0,0 +1,80 @@ +module doodle.gtk.opengl; + +public { + import doodle.tk.renderer; +} + +private { + import gtkglc.gl; + import gtkglc.glu; +} + +final class OpenGLRenderer : Renderer { + void setLineStyle(LineStyle style) { + GLint factor; + GLushort pattern; + + switch (style) { + case LineStyle.SOLID: + factor = 1; + pattern = 0xffff; + break; + case LineStyle.DASHED: + factor = 1; + pattern = 0x0f0f; + break; + case LineStyle.DOTTED: + factor = 1; + pattern = 0xefef; + break; + default: + assert(0); + } + glLineStipple(factor, pattern); + } + + void setLineWidth(in double width) { + glLineWidth(width); // FIXME must take scale into account + } + + void setColor(in Color color) { + glColor4d(color.r, color.g, color.b, color.a); + } + + void translate(in Point p) { + glTranslated(p.x, p.y, 0.0); + } + + void scale(in double s) { + glScaled(s, s, 1.0); + } + + void pushState() { + glPushAttrib(0); + } + + void popState() { + glPopAttrib(); + } + + void drawRectangle(in Rectangle rectangle, bool fill) { + glBegin(fill ? GL_QUADS : GL_LINE_LOOP); + glVertex2d(rectangle.x0, rectangle.y0); + glVertex2d(rectangle.x0, rectangle.y1); + glVertex2d(rectangle.x1, rectangle.y1); + glVertex2d(rectangle.x1, rectangle.y0); + glEnd(); + } + + void drawEllipse(in Rectangle rectangle, bool fill); + void drawSegment(in Segment segment); + void drawHLine(in double y, in double x0, in double x1); + void drawVLine(in double x, in double y0, in double y1); + void drawPoly(in Point[] points, bool fill); + + void setFontFace(in FontFace face); + void setFontSize(in double size); + void drawText(in string text); + + void measureText(in string text, out Rectangle logicalBounds, out Rectangle totalBounds) const; +} diff -r cdd4fc728d94 -r 98980cee8c5b doodle/main/prog/doodler.d --- a/doodle/main/prog/doodler.d Mon Aug 16 17:23:09 2010 +0930 +++ b/doodle/main/prog/doodler.d Mon Aug 16 17:59:42 2010 +0930 @@ -15,8 +15,10 @@ import doodle.fig.tools; - import doodle.gtk.cairo_canvas; import doodle.gtk.palette; + //import doodle.gtk.cairo_canvas; + import doodle.gtk.opengl_canvas; + import glgdk.GLdInit; import gtk.Main; import gtk.MainWindow; @@ -28,6 +30,7 @@ final class TopLevel : private IToolStackObserver { this(string[] args) { Main.init(args); + GLdInit.init(args); auto window = new MainWindow("Doodle"); auto vbox = new VBox(false, 0); @@ -56,7 +59,8 @@ // assume the screen has PPI of 120.0 immutable millimetersPerInch = 25.4; immutable pixelsPerMillimetre = 120.0 / millimetersPerInch; - auto canvas = new Canvas(layers, toolLayer, gridLayer, pixelsPerMillimetre); + //auto canvas = new CairoCanvas(layers, toolLayer, gridLayer, pixelsPerMillimetre); + auto canvas = new OpenGLCanvas(layers, toolLayer, gridLayer, pixelsPerMillimetre); vbox.packStart(canvas, true, true, 0);