1 #include "apitracecall.h"
4 #include "trace_model.hpp"
9 #define QT_USE_FAST_OPERATOR_PLUS
10 #include <QStringBuilder>
11 #include <QTextDocument>
13 const char * const styleSheet =
15 " font-weight:bold;\n"
16 // text shadow looks great but doesn't work well in qtwebkit 4.7
17 " /*text-shadow: 0px 2px 3px #555;*/\n"
18 " font-size: 1.2em;\n"
21 " border: 1px solid rgb(238,206,0);\n"
22 " border-radius: 4px;\n"
23 " background: yellow;\n"
25 " box-shadow: 0px 1px 3px dimgrey;\n"
26 " -webkit-transition: background 1s linear;\n"
29 " background: white;\n"
35 " border: 1px solid rgb(255,0,0);\n"
38 " border-radius: 4px;\n"
39 // also looks great but qtwebkit doesn't support it
40 //" background: #6fb2e5;\n"
41 //" box-shadow: 0 1px 5px #0061aa, inset 0 10px 20px #b6f9ff;\n"
42 //" -o-box-shadow: 0 1px 5px #0061aa, inset 0 10px 20px #b6f9ff;\n"
43 //" -webkit-box-shadow: 0 1px 5px #0061aa, inset 0 10px 20px #b6f9ff;\n"
44 //" -moz-box-shadow: 0 1px 5px #0061aa, inset 0 10px 20px #b6f9ff;\n"
47 ApiPointer::ApiPointer(unsigned long long val)
52 QString ApiPointer::toString() const
55 return QString("0x%1").arg(m_value, 0, 16);
57 return QLatin1String("NULL");
60 // Qt::convertFromPlainText doesn't do precisely what we want
62 plainTextToHTML(const QString & plain, bool multiLine)
67 for (int i = 0; i < plain.length(); ++i) {
68 if (plain[i] == QLatin1Char('\n')){
70 rich += QLatin1String("<br>\n");
72 rich += QLatin1String("\\n");
77 if (plain[i] == QLatin1Char('\t')){
79 rich += QChar(0x00a0U);
82 rich += QChar(0x00a0U);
86 rich += QLatin1String("\\t");
89 } else if (plain[i].isSpace()) {
90 rich += QChar(0x00a0U);
92 } else if (plain[i] == QLatin1Char('<')) {
93 rich += QLatin1String("<");
94 } else if (plain[i] == QLatin1Char('>')) {
95 rich += QLatin1String(">");
96 } else if (plain[i] == QLatin1Char('&')) {
97 rich += QLatin1String("&");
106 return QLatin1Literal("\"") + rich + QLatin1Literal("\"");
112 QString apiVariantToString(const QVariant &variant, bool multiLine)
114 if (variant.userType() == QVariant::Double) {
115 return QString::number(variant.toFloat());
117 if (variant.userType() == QVariant::ByteArray) {
118 if (variant.toByteArray().size() < 1024) {
119 int bytes = variant.toByteArray().size();
120 return QObject::tr("[binary data, size = %1 bytes]").arg(bytes);
122 float kb = variant.toByteArray().size()/1024.;
123 return QObject::tr("[binary data, size = %1 kb]").arg(kb);
127 if (variant.userType() == QVariant::String) {
128 return plainTextToHTML(variant.toString(), multiLine);
131 if (variant.userType() < QVariant::UserType) {
132 return variant.toString();
135 if (variant.canConvert<ApiPointer>()) {
136 return variant.value<ApiPointer>().toString();
138 if (variant.canConvert<ApiBitmask>()) {
139 return variant.value<ApiBitmask>().toString();
141 if (variant.canConvert<ApiStruct>()) {
142 return variant.value<ApiStruct>().toString();
144 if (variant.canConvert<ApiArray>()) {
145 return variant.value<ApiArray>().toString();
147 if (variant.canConvert<ApiEnum>()) {
148 return variant.value<ApiEnum>().toString();
154 ApiBitmask::ApiBitmask(const Trace::Bitmask *bitmask)
161 void ApiBitmask::init(const Trace::Bitmask *bitmask)
166 m_value = bitmask->value;
167 for (Trace::Bitmask::Signature::const_iterator it = bitmask->sig->begin();
168 it != bitmask->sig->end(); ++it) {
170 QPair<QString, unsigned long long> pair;
172 pair.first = QString::fromStdString(it->name);
173 pair.second = it->value;
179 QString ApiBitmask::toString() const
182 unsigned long long value = m_value;
184 for (Signature::const_iterator it = m_sig.begin();
185 value != 0 && it != m_sig.end(); ++it) {
186 Q_ASSERT(it->second);
187 if ((value & it->second) == it->second) {
189 str += QLatin1String(" | ");
192 value &= ~it->second;
196 if (value || first) {
198 str += QLatin1String(" | ");
200 str += QString::fromLatin1("0x%1").arg(value, 0, 16);
205 ApiStruct::ApiStruct(const Trace::Struct *s)
210 QString ApiStruct::toString() const
214 str += QLatin1String("{");
215 for (unsigned i = 0; i < m_members.count(); ++i) {
216 str += m_sig.memberNames[i] %
217 QLatin1Literal(" = ") %
218 apiVariantToString(m_members[i]);
219 if (i < m_members.count() - 1)
220 str += QLatin1String(", ");
222 str += QLatin1String("}");
227 void ApiStruct::init(const Trace::Struct *s)
232 m_sig.name = QString::fromStdString(s->sig->name);
233 for (unsigned i = 0; i < s->members.size(); ++i) {
235 m_sig.memberNames.append(
236 QString::fromStdString(s->sig->member_names[i]));
237 s->members[i]->visit(vis);
238 m_members.append(vis.variant());
242 void VariantVisitor::visit(Trace::Null *)
244 m_variant = QVariant::fromValue(ApiPointer(0));
247 void VariantVisitor::visit(Trace::Bool *node)
249 m_variant = QVariant(node->value);
252 void VariantVisitor::visit(Trace::SInt *node)
254 m_variant = QVariant(node->value);
257 void VariantVisitor::visit(Trace::UInt *node)
259 m_variant = QVariant(node->value);
262 void VariantVisitor::visit(Trace::Float *node)
264 m_variant = QVariant(node->value);
267 void VariantVisitor::visit(Trace::String *node)
269 m_variant = QVariant(QString::fromStdString(node->value));
272 void VariantVisitor::visit(Trace::Enum *e)
275 e->sig->second->visit(vis);
277 QVariant val = vis.variant();
279 m_variant = QVariant::fromValue(
280 ApiEnum(QString::fromStdString(e->sig->first), val));
283 void VariantVisitor::visit(Trace::Bitmask *bitmask)
285 m_variant = QVariant::fromValue(ApiBitmask(bitmask));
288 void VariantVisitor::visit(Trace::Struct *str)
290 m_variant = QVariant::fromValue(ApiStruct(str));
293 void VariantVisitor::visit(Trace::Array *array)
295 m_variant = QVariant::fromValue(ApiArray(array));
298 void VariantVisitor::visit(Trace::Blob *blob)
301 //FIXME: this is a nasty hack. Trace::Blob's can't
302 // delete the contents in the destructor because
303 // the data is being used by other calls. We piggy back
304 // on that assumption and don't deep copy the data. If
305 // Blob's will start deleting the data we will need to
306 // start deep copying it or switch to using something like
307 // Boost's shared_ptr or Qt's QSharedPointer to handle it
308 QByteArray barray = QByteArray::fromRawData(blob->buf, blob->size);
309 m_variant = QVariant(barray);
312 void VariantVisitor::visit(Trace::Pointer *ptr)
314 m_variant = QVariant::fromValue(ApiPointer(ptr->value));
317 ApiArray::ApiArray(const Trace::Array *arr)
322 ApiArray::ApiArray(const QList<QVariant> &vals)
327 QString ApiArray::toString() const
330 str += QLatin1String("[");
331 for(int i = 0; i < m_array.count(); ++i) {
332 const QVariant &var = m_array[i];
333 str += apiVariantToString(var);
334 if (i < m_array.count() - 1)
335 str += QLatin1String(", ");
337 str += QLatin1String("]");
342 void ApiArray::init(const Trace::Array *arr)
347 for (int i = 0; i < arr->values.size(); ++i) {
349 arr->values[i]->visit(vis);
351 m_array.append(vis.variant());
355 QStaticText ApiTraceCall::staticText() const
357 if (m_staticText && !m_staticText->text().isEmpty())
358 return *m_staticText;
360 QVariantList argValues = arguments();
362 QString richText = QString::fromLatin1(
363 "<span style=\"font-weight:bold\">%1</span>(").arg(m_name);
364 for (int i = 0; i < m_argNames.count(); ++i) {
365 richText += QLatin1String("<span style=\"color:#0000ff\">");
366 QString argText = apiVariantToString(argValues[i]);
368 //if arguments are really long (e.g. shader text), cut them
370 if (argText.length() > 40) {
371 QString shortened = argText.mid(0, 40);
372 shortened[argText.length() - 5] = '.';
373 shortened[argText.length() - 4] = '.';
374 shortened[argText.length() - 3] = '.';
375 shortened[argText.length() - 2] = argText[argText.length() - 2];
376 shortened[argText.length() - 1] = argText[argText.length() - 1];
377 richText += shortened;
381 richText += QLatin1String("</span>");
382 if (i < m_argNames.count() - 1)
383 richText += QLatin1String(", ");
385 richText += QLatin1String(")");
386 if (m_returnValue.isValid()) {
388 QLatin1Literal(" = ") %
389 QLatin1Literal("<span style=\"color:#0000ff\">") %
390 apiVariantToString(m_returnValue) %
391 QLatin1Literal("</span>");
395 m_staticText = new QStaticText(richText);
397 m_staticText->setText(richText);
399 opt.setWrapMode(QTextOption::NoWrap);
400 m_staticText->setTextOption(opt);
401 m_staticText->prepare();
403 return *m_staticText;
406 QString ApiTraceCall::toHtml() const
408 if (!m_richText.isEmpty())
411 m_richText = QLatin1String("<div class=\"call\">");
413 if (m_helpUrl.isEmpty()) {
414 m_richText += QString::fromLatin1(
415 "%1) <span class=\"callName\">%2</span>(")
419 m_richText += QString::fromLatin1(
420 "%1) <span class=\"callName\"><a href=\"%2\">%3</a></span>(")
422 .arg(m_helpUrl.toString())
426 QVariantList argValues = arguments();
427 for (int i = 0; i < m_argNames.count(); ++i) {
429 QLatin1String("<span class=\"arg-name\">") +
431 QLatin1String("</span>") +
432 QLatin1Literal(" = ") +
433 QLatin1Literal("<span class=\"arg-value\">") +
434 apiVariantToString(argValues[i], true) +
435 QLatin1Literal("</span>");
436 if (i < m_argNames.count() - 1)
437 m_richText += QLatin1String(", ");
439 m_richText += QLatin1String(")");
441 if (m_returnValue.isValid()) {
443 QLatin1String(" = ") +
444 QLatin1String("<span style=\"color:#0000ff\">") +
445 apiVariantToString(m_returnValue, true) +
446 QLatin1String("</span>");
448 m_richText += QLatin1String("</div>");
453 "<div class=\"error\">%1</div>")
455 m_richText += errorStr;
460 "<html><head><style type=\"text/css\" media=\"all\">"
461 "%1</style></head><body>%2</body></html>")
464 m_richText.squeeze();
466 //qDebug()<<m_richText;
470 QString ApiTraceCall::filterText() const
472 if (!m_filterText.isEmpty())
475 QVariantList argValues = arguments();
476 m_filterText = m_name + QLatin1Literal("(");
477 for (int i = 0; i < m_argNames.count(); ++i) {
478 m_filterText += m_argNames[i] +
479 QLatin1Literal(" = ") +
480 apiVariantToString(argValues[i]);
481 if (i < m_argNames.count() - 1)
482 m_filterText += QLatin1String(", ");
484 m_filterText += QLatin1String(")");
486 if (m_returnValue.isValid()) {
487 m_filterText += QLatin1Literal(" = ") +
488 apiVariantToString(m_returnValue);
490 m_filterText.squeeze();
494 QStaticText ApiTraceFrame::staticText() const
496 if (m_staticText && !m_staticText->text().isEmpty())
497 return *m_staticText;
501 //mark the frame if it uploads more than a meg a frame
502 if (m_binaryDataSize > (1024*1024)) {
505 "<span style=\"font-weight:bold;\">"
506 "Frame %1</span>"
507 "<span style=\"font-style:italic;\">"
508 " (%2MB)</span>")
510 .arg(double(m_binaryDataSize / (1024.*1024.)), 0, 'g', 2);
514 "<span style=\"font-weight:bold\">Frame %1</span>")
519 m_staticText = new QStaticText(richText);
522 opt.setWrapMode(QTextOption::NoWrap);
523 m_staticText->setTextOption(opt);
524 m_staticText->prepare();
526 return *m_staticText;
529 int ApiTraceCall::numChildren() const
534 int ApiTraceFrame::numChildren() const
536 return m_calls.count();
539 ApiTraceFrame::ApiTraceFrame()
540 : ApiTraceEvent(ApiTraceEvent::Frame),
546 ApiTraceCall::ApiTraceCall()
547 : ApiTraceEvent(ApiTraceEvent::Call),
548 m_hasBinaryData(false),
553 ApiTraceEvent::ApiTraceEvent()
554 : m_type(ApiTraceEvent::None),
559 ApiTraceEvent::ApiTraceEvent(Type t)
565 ApiTraceCall::~ApiTraceCall()
569 QVariantMap ApiTraceEvent::stateParameters() const
571 return m_state.parameters();
574 ApiTraceState ApiTraceEvent::state() const
579 void ApiTraceEvent::setState(const ApiTraceState &state)
584 bool ApiTraceCall::hasBinaryData() const
586 return m_hasBinaryData;
589 int ApiTraceCall::binaryDataIndex() const
591 return m_binaryDataIndex;
594 ApiTraceState::ApiTraceState()
598 ApiTraceState::ApiTraceState(const QVariantMap &parsedJson)
600 m_parameters = parsedJson[QLatin1String("parameters")].toMap();
601 QVariantMap attachedShaders =
602 parsedJson[QLatin1String("shaders")].toMap();
603 QVariantMap::const_iterator itr;
606 for (itr = attachedShaders.constBegin(); itr != attachedShaders.constEnd();
608 QString type = itr.key();
609 QString source = itr.value().toString();
610 m_shaderSources[type] = source;
613 QVariantMap textures =
614 parsedJson[QLatin1String("textures")].toMap();
615 for (itr = textures.constBegin(); itr != textures.constEnd(); ++itr) {
616 QVariantMap image = itr.value().toMap();
617 QSize size(image[QLatin1String("__width__")].toInt(),
618 image[QLatin1String("__height__")].toInt());
619 QString cls = image[QLatin1String("__class__")].toString();
620 QString type = image[QLatin1String("__type__")].toString();
622 image[QLatin1String("__normalized__")].toBool();
624 image[QLatin1String("__channels__")].toInt();
626 Q_ASSERT(type == QLatin1String("uint8"));
627 Q_ASSERT(normalized == true);
629 QByteArray dataArray =
630 image[QLatin1String("__data__")].toByteArray();
634 tex.setNumChannels(numChannels);
635 tex.setLabel(itr.key());
636 tex.contentsFromBase64(dataArray);
638 m_textures.append(tex);
642 parsedJson[QLatin1String("framebuffer")].toMap();
643 for (itr = fbos.constBegin(); itr != fbos.constEnd(); ++itr) {
644 QVariantMap buffer = itr.value().toMap();
645 QSize size(buffer[QLatin1String("__width__")].toInt(),
646 buffer[QLatin1String("__height__")].toInt());
647 QString cls = buffer[QLatin1String("__class__")].toString();
648 QString type = buffer[QLatin1String("__type__")].toString();
649 bool normalized = buffer[QLatin1String("__normalized__")].toBool();
650 int numChannels = buffer[QLatin1String("__channels__")].toInt();
652 Q_ASSERT(type == QLatin1String("uint8"));
653 Q_ASSERT(normalized == true);
655 QByteArray dataArray =
656 buffer[QLatin1String("__data__")].toByteArray();
660 fbo.setNumChannels(numChannels);
661 fbo.setType(itr.key());
662 fbo.contentsFromBase64(dataArray);
663 m_framebuffers.append(fbo);
667 QVariantMap ApiTraceState::parameters() const
672 QMap<QString, QString> ApiTraceState::shaderSources() const
674 return m_shaderSources;
677 bool ApiTraceState::isEmpty() const
679 return m_parameters.isEmpty();
682 QList<ApiTexture> ApiTraceState::textures() const
687 QList<ApiFramebuffer> ApiTraceState::framebuffers() const
689 return m_framebuffers;
692 QList<QVariant> ApiArray::values() const
697 int ApiTraceCall::index() const
702 QString ApiTraceCall::name() const
707 QStringList ApiTraceCall::argNames() const
712 QVariantList ApiTraceCall::arguments() const
714 if (m_editedValues.isEmpty())
717 return m_editedValues;
720 QVariant ApiTraceCall::returnValue() const
722 return m_returnValue;
725 QUrl ApiTraceCall::helpUrl() const
730 ApiTraceCall::ApiTraceCall(const Trace::Call *call)
731 : ApiTraceEvent(ApiTraceEvent::Call),
732 m_hasBinaryData(false),
735 m_name = QString::fromStdString(call->sig->name);
738 QString argumentsText;
739 for (int i = 0; i < call->sig->arg_names.size(); ++i) {
741 QString::fromStdString(call->sig->arg_names[i]);
744 VariantVisitor retVisitor;
745 call->ret->visit(retVisitor);
746 m_returnValue = retVisitor.variant();
748 for (int i = 0; i < call->args.size(); ++i) {
749 VariantVisitor argVisitor;
750 call->args[i]->visit(argVisitor);
751 m_argValues += argVisitor.variant();
752 if (m_argValues[i].type() == QVariant::ByteArray) {
753 m_hasBinaryData = true;
754 m_binaryDataIndex = i;
759 void ApiTraceCall::setHelpUrl(const QUrl &url)
764 void ApiTraceCall::setParentFrame(ApiTraceFrame *frame)
766 m_parentFrame = frame;
769 ApiTraceFrame * ApiTraceCall::parentFrame()const
771 return m_parentFrame;
774 ApiTraceEvent::~ApiTraceEvent()
779 void ApiTraceCall::revert()
781 setEditedValues(QVariantList());
784 ApiTrace * ApiTraceFrame::parentTrace() const
786 return m_parentTrace;
789 void ApiTraceFrame::setParentTrace(ApiTrace *trace)
791 m_parentTrace = trace;
794 QVariantList ApiTraceCall::originalValues() const
799 void ApiTraceCall::setEditedValues(const QVariantList &lst)
801 ApiTrace *trace = parentTrace();
803 m_editedValues = lst;
804 //lets regenerate data
805 m_richText = QString();
806 m_filterText = QString();
811 if (!lst.isEmpty()) {
812 trace->callEdited(this);
814 trace->callReverted(this);
819 QVariantList ApiTraceCall::editedValues() const
821 return m_editedValues;
824 bool ApiTraceCall::edited() const
826 return !m_editedValues.isEmpty();
829 ApiEnum::ApiEnum(const QString &name, const QVariant &val)
835 QString ApiEnum::toString() const
840 QVariant ApiEnum::value() const
845 QString ApiEnum::name() const
850 unsigned long long ApiBitmask::value() const
855 ApiBitmask::Signature ApiBitmask::signature() const
860 ApiStruct::Signature ApiStruct::signature() const
865 QList<QVariant> ApiStruct::values() const
870 unsigned long long ApiPointer::value() const
875 bool ApiTraceCall::hasError() const
877 return !m_error.isEmpty();
880 QString ApiTraceCall::error() const
885 void ApiTraceCall::setError(const QString &msg)
887 if (m_error != msg) {
888 ApiTrace *trace = parentTrace();
890 m_richText = QString();
892 trace->callError(this);
896 ApiTrace * ApiTraceCall::parentTrace() const
899 return m_parentFrame->parentTrace();
903 void ApiTraceFrame::addCall(ApiTraceCall *call)
905 m_calls.append(call);
906 if (call->hasBinaryData()) {
908 call->arguments()[call->binaryDataIndex()].toByteArray();
909 m_binaryDataSize += data.size();
913 QList<ApiTraceCall*> ApiTraceFrame::calls() const
918 ApiTraceCall * ApiTraceFrame::call(int idx) const
920 return m_calls.value(idx);
923 int ApiTraceFrame::callIndex(ApiTraceCall *call) const
925 return m_calls.indexOf(call);
928 bool ApiTraceFrame::isEmpty() const
930 return m_calls.isEmpty();
933 int ApiTraceFrame::binaryDataSize() const
935 return m_binaryDataSize;