1 //
2 //  QtQuickFGCanvasItem.cxx
3 //
4 //  Copyright (C) 2017 James Turner <zakalawe@mac.com>
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Library General Public
8 // License as published by the Free Software Foundation; either
9 // version 2 of the License, or (at your option) any later version.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // Library General Public License for more details.
15 //
16 // You should have received a copy of the GNU Library General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
19 
20 #include "QtQuickFGCanvasItem.hxx"
21 
22 #include <QDebug>
23 #include <QSGSimpleTextureNode>
24 
25 #include <simgear/canvas/Canvas.hxx>
26 #include <simgear/canvas/events/MouseEvent.hxx>
27 #include <simgear/debug/logstream.hxx>
28 
29 #include <Main/globals.hxx>
30 #include <Scripting/NasalSys.hxx>
31 
32 namespace sc = simgear::canvas;
33 
34 namespace {
35 
36 class CanvasTexture : public QSGDynamicTexture
37 {
38 public:
CanvasTexture(sc::CanvasPtr canvas)39     CanvasTexture(sc::CanvasPtr canvas)
40     {
41         _manager = globals->get_subsystem<CanvasMgr>();
42         setHorizontalWrapMode(QSGTexture::ClampToEdge);
43         setVerticalWrapMode(QSGTexture::ClampToEdge);
44     }
45 
updateTexture()46     bool updateTexture() override
47     {
48         // no-op, canvas is updated by OSG before QtQuick rendering occurs
49         // this would change if we wanted QtQuick to drive Canvas
50         // updating, which we don't
51         // but do return true so QQ knows to repaint / re-cache
52         return true;
53     }
54 
hasMipmaps() const55     bool hasMipmaps() const override
56     {
57         return false;
58     }
59 
hasAlphaChannel() const60     bool hasAlphaChannel() const override
61     {
62         return false; // for now, can Canvas have an alpha channel?
63     }
64 
bind()65     void bind() override
66     {
67         glBindTexture(GL_TEXTURE_2D, _manager->getCanvasTexId(_canvas));
68     }
69 
textureId() const70     int textureId() const override
71     {
72         return  _manager->getCanvasTexId(_canvas);
73     }
74 
textureSize() const75     QSize textureSize() const override
76     {
77         SGPropertyNode* cprops = _canvas->getProps();
78         return QSize(cprops->getIntValue("value[0]"),
79                      cprops->getIntValue("value[1]"));
80     }
81 private:
82     CanvasMgr* _manager;
83     sc::CanvasPtr _canvas;
84 
85 };
86 
87 } // of anonymous namespace
88 
convertQtButtonToCanvas(Qt::MouseButton button)89 static int convertQtButtonToCanvas(Qt::MouseButton button)
90 {
91     switch (button) {
92     case Qt::LeftButton:    return 0;
93     case Qt::MiddleButton:  return 1;
94     case Qt::RightButton:   return 2;
95     default:
96         break;
97     }
98 
99     qWarning() << Q_FUNC_INFO << "unmapped Qt mouse button" << button;
100     return 0;
101 }
102 
QtQuickFGCanvasItem(QQuickItem * parent)103 QtQuickFGCanvasItem::QtQuickFGCanvasItem(QQuickItem *parent)
104 {
105 }
106 
~QtQuickFGCanvasItem()107 QtQuickFGCanvasItem::~QtQuickFGCanvasItem()
108 {
109     if ( _canvas ) {
110         _canvas->destroy();
111     }
112 }
113 
updatePaintNode(QSGNode * oldNode,QQuickItem::UpdatePaintNodeData *)114 QSGNode *QtQuickFGCanvasItem::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *)
115 {
116     QSGSimpleTextureNode* texNode = static_cast<QSGSimpleTextureNode*>(oldNode);
117     if (!texNode) {
118         texNode = new QSGSimpleTextureNode;
119 
120         CanvasTexture* texture = new CanvasTexture(_canvas);
121         texNode->setTexture(texture);
122     }
123 
124     // or should this simply be the geometry?
125     texNode->setRect(QRectF(0.0, 0.0, width(), height()));
126 
127     return texNode;
128 }
129 
convertMouseEvent(QMouseEvent * event)130 sc::MouseEventPtr QtQuickFGCanvasItem::convertMouseEvent(QMouseEvent* event)
131 {
132     sc::MouseEventPtr canvasEvent(new sc::MouseEvent);
133     canvasEvent->time = event->timestamp() / 1000.0;
134     canvasEvent->screen_pos.set(event->screenPos().x(),
135                                 event->screenPos().y());
136     canvasEvent->client_pos.set(event->pos().x(),
137                                 event->pos().y());
138 
139     // synthesise delta values
140     QPointF delta = event->screenPos() - _lastMouseEventScreenPosition;
141     canvasEvent->delta.set(delta.x(), delta.y());
142     _lastMouseEventScreenPosition = event->screenPos();
143 
144     return canvasEvent;
145 }
146 
mousePressEvent(QMouseEvent * event)147 void QtQuickFGCanvasItem::mousePressEvent(QMouseEvent *event)
148 {
149     sc::MouseEventPtr canvasEvent = convertMouseEvent(event);
150     canvasEvent->button = convertQtButtonToCanvas(event->button());
151     canvasEvent->type = sc::Event::MOUSE_DOWN;
152     _canvas->handleMouseEvent(canvasEvent);
153 }
154 
mouseReleaseEvent(QMouseEvent * event)155 void QtQuickFGCanvasItem::mouseReleaseEvent(QMouseEvent *event)
156 {
157     sc::MouseEventPtr canvasEvent = convertMouseEvent(event);
158     canvasEvent->button = convertQtButtonToCanvas(event->button());
159     canvasEvent->type = sc::Event::MOUSE_UP;
160     _canvas->handleMouseEvent(canvasEvent);
161 }
162 
mouseMoveEvent(QMouseEvent * event)163 void QtQuickFGCanvasItem::mouseMoveEvent(QMouseEvent *event)
164 {
165     sc::MouseEventPtr canvasEvent = convertMouseEvent(event);
166     if (event->buttons() == Qt::NoButton) {
167         canvasEvent->type = sc::Event::MOUSE_MOVE;
168     } else {
169         canvasEvent->button = convertQtButtonToCanvas(event->button());
170         canvasEvent->type = sc::Event::DRAG;
171     }
172 
173     _canvas->handleMouseEvent(canvasEvent);
174 }
175 
mouseDoubleClickEvent(QMouseEvent * event)176 void QtQuickFGCanvasItem::mouseDoubleClickEvent(QMouseEvent *event)
177 {
178     sc::MouseEventPtr canvasEvent = convertMouseEvent(event);
179     canvasEvent->button = convertQtButtonToCanvas(event->button());
180     canvasEvent->type = sc::Event::DBL_CLICK;
181     _canvas->handleMouseEvent(canvasEvent);
182 }
183 
184 
wheelEvent(QWheelEvent * event)185 void QtQuickFGCanvasItem::wheelEvent(QWheelEvent *event)
186 {
187     sc::MouseEventPtr canvasEvent(new sc::MouseEvent);
188     canvasEvent->time = event->timestamp() / 1000.0;
189     canvasEvent->type = sc::Event::WHEEL;
190 
191     // TODO - check if using pixelDelta is beneficial at all.
192     canvasEvent->delta.set(event->angleDelta().x(),
193                            event->angleDelta().y());
194 }
195 
geometryChanged(const QRectF & newGeometry,const QRectF & oldGeometry)196 void QtQuickFGCanvasItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
197 {
198     QQuickItem::geometryChanged(newGeometry, oldGeometry);
199     updateCanvasGeometry();
200 }
201 
setCanvas(QString canvas)202 void QtQuickFGCanvasItem::setCanvas(QString canvas)
203 {
204     if (_canvasName == canvas)
205         return;
206 
207     if (_canvas) {
208         // release it
209         _canvas->destroy();
210         _canvas.clear();
211     }
212     _canvasName = canvas;
213 
214     if (!_canvasName.isEmpty()) {
215         CanvasMgr* canvasManager = globals->get_subsystem<CanvasMgr>();
216         _canvas = canvasManager->createCanvas("");
217 
218         SGPropertyNode* cprops = _canvas->getProps();
219         cprops->setBoolValue("render-always", true);
220 
221         initCanvasNasalModules();
222         updateCanvasGeometry();
223     }
224 
225     emit canvasChanged(_canvasName);
226 }
227 
initCanvasNasalModules()228 void QtQuickFGCanvasItem::initCanvasNasalModules()
229 {
230 #if 0
231     SGPropertyNode *nasal = props->getNode("nasal");
232     if( !nasal )
233       return;
234 
235     FGNasalSys *nas = globals->get_subsystem<FGNasalSys>();
236     if( !nas )
237       SG_LOG( SG_GENERAL,
238               SG_ALERT,
239               "CanvasWidget: Failed to get nasal subsystem!" );
240 
241     const std::string file = std::string("__canvas:")
242                            + cprops->getStringValue("name");
243 
244     SGPropertyNode *load = nasal->getNode("load");
245     if( load )
246     {
247       const char *s = load->getStringValue();
248       nas->handleCommand(module.c_str(), file.c_str(), s, cprops);
249     }
250 #endif
251 }
252 
updateCanvasGeometry()253 void QtQuickFGCanvasItem::updateCanvasGeometry()
254 {
255     SGPropertyNode* cprops = _canvas->getProps();
256     cprops->setIntValue("size[0]", width());
257     cprops->setIntValue("size[1]", height());
258 }
259