aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPavel Pisa <pisa@cmp.felk.cvut.cz>2019-06-28 15:36:23 +0200
committerPavel Pisa <pisa@cmp.felk.cvut.cz>2019-06-28 15:37:26 +0200
commit8fc09b33240fa0b55bd0db22103db65861bc2d6b (patch)
tree80eb673212aaf6b6e056c3e895885dab78815e31
parentf51b0a34cdcfe70f57add0e36dd3b34e3dbc83db (diff)
downloadqtmips-8fc09b33240fa0b55bd0db22103db65861bc2d6b.tar.gz
qtmips-8fc09b33240fa0b55bd0db22103db65861bc2d6b.tar.bz2
qtmips-8fc09b33240fa0b55bd0db22103db65861bc2d6b.zip
Include QHtml5File source file to allow ELF file load under emscripten.
Signed-off-by: Pavel Pisa <pisa@cmp.felk.cvut.cz>
-rw-r--r--qtmips_gui/newdialog.cpp18
-rw-r--r--qtmips_gui/qhtml5file.h59
-rw-r--r--qtmips_gui/qhtml5file_html5.cpp200
-rw-r--r--qtmips_gui/qtmips_gui.pro6
4 files changed, 283 insertions, 0 deletions
diff --git a/qtmips_gui/newdialog.cpp b/qtmips_gui/newdialog.cpp
index 0e1c5dc..ef9dac3 100644
--- a/qtmips_gui/newdialog.cpp
+++ b/qtmips_gui/newdialog.cpp
@@ -37,6 +37,11 @@
#include "mainwindow.h"
#include "qtmipsexception.h"
+#ifdef __EMSCRIPTEN__
+#include <QFileInfo>
+#include "qhtml5file.h"
+#endif
+
NewDialog::NewDialog(QWidget *parent, QSettings *settings) : QDialog(parent) {
setWindowTitle("New machine");
@@ -146,6 +151,7 @@ void NewDialog::create_empty() {
void NewDialog::browse_elf() {
+#ifndef __EMSCRIPTEN__
QFileDialog elf_dialog(this);
elf_dialog.setFileMode(QFileDialog::ExistingFile);
if (elf_dialog.exec()) {
@@ -154,6 +160,18 @@ void NewDialog::browse_elf() {
config->set_elf(path);
}
// Elf shouldn't have any other effect so we skip config_gui here
+#else
+ QHtml5File::load("*", [&](const QByteArray &content, const QString &fileName) {
+ QFileInfo fi(fileName);
+ QString elf_name = fi.fileName();
+ QFile file(elf_name);
+ file.open(QIODevice::WriteOnly | QIODevice::Truncate);
+ file.write(content);
+ file.close();
+ ui->elf_file->setText(elf_name);
+ config->set_elf(elf_name);
+ });
+#endif
}
void NewDialog::elf_change(QString val) {
diff --git a/qtmips_gui/qhtml5file.h b/qtmips_gui/qhtml5file.h
new file mode 100644
index 0000000..dbe6587
--- /dev/null
+++ b/qtmips_gui/qhtml5file.h
@@ -0,0 +1,59 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QHTML5FILE_H
+#define QHTML5FILE_H
+
+#include <QtCore/QByteArray>
+#include <QtCore/QString>
+
+#include <functional>
+
+QT_BEGIN_NAMESPACE
+
+namespace QHtml5File {
+
+void load(const QString &accept, std::function<void(const QByteArray &, const QString &)> fileDataReady);
+void save(const QByteArray &contents, const QString &fileNameHint);
+
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/qtmips_gui/qhtml5file_html5.cpp b/qtmips_gui/qhtml5file_html5.cpp
new file mode 100644
index 0000000..60f26d7
--- /dev/null
+++ b/qtmips_gui/qhtml5file_html5.cpp
@@ -0,0 +1,200 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qhtml5file.h"
+
+#include <qdebug.h>
+
+#include <emscripten.h>
+#include <emscripten/html5.h>
+
+//
+// This file implements file load via HTML file input element and file save via browser download.
+//
+
+// Global user file data ready callback and C helper function. JavaScript will
+// call this function when the file data is ready; the helper then forwards
+// the call to the current handler function. This means there can be only one
+// file open in proress at a given time.
+std::function<void(char *, size_t, const char *)> g_qtFileDataReadyCallback;
+extern "C" EMSCRIPTEN_KEEPALIVE void qt_callFileDataReady(char *content, size_t contentSize, const char *fileName)
+{
+ if (g_qtFileDataReadyCallback == nullptr)
+ return;
+
+ g_qtFileDataReadyCallback(content, contentSize, fileName);
+ g_qtFileDataReadyCallback = nullptr;
+}
+
+namespace {
+ void loadFile(const char *accept, std::function<void(char *, size_t, const char *)> fileDataReady)
+ {
+ if (::g_qtFileDataReadyCallback)
+ puts("Warning: Concurrent loadFile() calls are not supported. Cancelling earlier call");
+
+ // Call qt_callFileDataReady to make sure the emscripten linker does not
+ // optimize it away, which may happen if the function is called from JavaScript
+ // only. Set g_qtFileDataReadyCallback to null to make it a a no-op.
+ ::g_qtFileDataReadyCallback = nullptr;
+ ::qt_callFileDataReady(nullptr, 0, nullptr);
+
+ ::g_qtFileDataReadyCallback = fileDataReady;
+ EM_ASM_({
+ const accept = UTF8ToString($0);
+
+ // Crate file file input which whil display the native file dialog
+ var fileElement = document.createElement("input");
+ document.body.appendChild(fileElement);
+ fileElement.type = "file";
+ fileElement.style = "display:none";
+ fileElement.accept = accept;
+ fileElement.onchange = function(event) {
+ const files = event.target.files;
+
+ // Read files
+ for (var i = 0; i < files.length; i++) {
+ const file = files[i];
+ var reader = new FileReader();
+ reader.onload = function() {
+ const name = file.name;
+ var contentArray = new Uint8Array(reader.result);
+ const contentSize = reader.result.byteLength;
+
+ // Copy the file file content to the C++ heap.
+ // Note: this could be simplified by passing the content as an
+ // "array" type to ccall and then let it copy to C++ memory.
+ // However, this built-in solution does not handle files larger
+ // than ~15M (Chrome). Instead, allocate memory manually and
+ // pass a pointer to the C++ side (which will free() it when done).
+
+ // TODO: consider slice()ing the file to read it picewise and
+ // then assembling it in a QByteArray on the C++ side.
+
+ const heapPointer = _malloc(contentSize);
+ const heapBytes = new Uint8Array(Module.HEAPU8.buffer, heapPointer, contentSize);
+ heapBytes.set(contentArray);
+
+ // Null out the first data copy to enable GC
+ reader = null;
+ contentArray = null;
+
+ // Call the C++ file data ready callback
+ ccall("qt_callFileDataReady", null,
+ ["number", "number", "string"], [heapPointer, contentSize, name]);
+ };
+ reader.readAsArrayBuffer(file);
+ }
+
+ // Clean up document
+ document.body.removeChild(fileElement);
+
+ }; // onchange callback
+
+ // Trigger file dialog open
+ fileElement.click();
+
+ }, accept);
+ }
+
+ void saveFile(const char *contentPointer, size_t contentLength, const char *fileNameHint)
+ {
+ EM_ASM_({
+ // Make the file contents and file name hint accessible to Javascript: convert
+ // the char * to a JavaScript string and create a subarray view into the C heap.
+ const contentPointer = $0;
+ const contentLength = $1;
+ const fileNameHint = UTF8ToString($2);
+ const fileContent = Module.HEAPU8.subarray(contentPointer, contentPointer + contentLength);
+
+ // Create a hidden download link and click it programatically
+ const fileblob = new Blob([fileContent], { type : "application/octet-stream" } );
+ var link = document.createElement("a");
+ document.body.appendChild(link);
+ link.download = fileNameHint;
+ link.href = window.URL.createObjectURL(fileblob);
+ link.style = "display:none";
+ link.click();
+ document.body.removeChild(link);
+
+ }, contentPointer, contentLength, fileNameHint);
+ }
+}
+
+/*!
+ \brief Read local file via file dialog.
+
+ Call this function to make the browser display an open-file dialog. This function
+ returns immediately, and \a fileDataReady is called when the user has selected a file
+ and the file contents has been read.
+
+ \a The accept argument specifies which file types to accept, and must follow the
+ <input type="file"> html standard formatting, for example ".png, .jpg, .jpeg".
+
+ This function is implemented on Qt for WebAssembly only. A nonfunctional cross-
+ platform stub is provided so that code that uses it can compile on all platforms.
+*/
+void QHtml5File::load(const QString &accept, std::function<void(const QByteArray &, const QString &)> fileDataReady)
+{
+ loadFile(accept.toUtf8().constData(), [=](char *content, size_t size, const char *fileName) {
+
+ // Copy file data into QByteArray and free buffer that was allocated
+ // on the JavaScript side. We could have used QByteArray::fromRawData()
+ // to avoid the copy here, but that would make memory management awkward.
+ QByteArray qtFileContent(content, size);
+ free(content);
+
+ // Call user-supplied data ready callback
+ fileDataReady(qtFileContent, QString::fromUtf8(fileName));
+ });
+}
+
+/*!
+ \brief Write local file via browser download
+
+ Call this function to make the browser start a file download. The file
+ will contains the given \a content, with a suggested \a fileNameHint.
+
+ This function is implemented on Qt for WebAssembly only. A nonfunctional cross-
+ platform stub is provided so that code that uses it can compile on all platforms.
+*/
+void QHtml5File::save(const QByteArray &content, const QString &fileNameHint)
+{
+ // Convert to C types and save
+ saveFile(content.constData(), content.size(), fileNameHint.toUtf8().constData());
+}
diff --git a/qtmips_gui/qtmips_gui.pro b/qtmips_gui/qtmips_gui.pro
index 5bedaa1..2e6287d 100644
--- a/qtmips_gui/qtmips_gui.pro
+++ b/qtmips_gui/qtmips_gui.pro
@@ -117,6 +117,12 @@ HEADERS += \
hinttabledelegate.h \
coreview/minimux.h
+wasm: SOURCES += \
+ qhtml5file_html5.cpp
+
+wasm: HEADERS += \
+ qhtml5file.h
+
FORMS += \
NewDialog.ui \
NewDialogCache.ui \