Loading...
Searching...
No Matches
ExecutionHelpers.hpp
1#pragma once
2#include <JS/Qml/QmlObjects.hpp>
3#include <Library/LibrarySettings.hpp>
4#include <score/application/ApplicationContext.hpp>
5#include <score/application/GUIApplicationContext.hpp>
6
7#include <ossia/detail/logger.hpp>
8#include <ossia-qt/invoke.hpp>
9#include <ossia-qt/qml_engine_functions.hpp>
10#include <JS/ConsolePanel.hpp>
11
12#include <QDir>
13#include <QQmlComponent>
14#include <QQmlEngine>
15#include <QStandardPaths>
16#include <QUrl>
17
18#if __has_include(<boost/hash2/xxh3.hpp>)
19#include <boost/hash2/xxh3.hpp>
20#include <boost/algorithm/hex.hpp>
21#endif
22
23namespace JS
24{
25
26static inline QString hashFileData(const QByteArray& str)
27{
28 QString hexName;
29#if __has_include(<boost/hash2/xxh3.hpp>)
30 boost::hash2::xxh3_128 hasher;
31 hasher.update(str.constData(), str.size());
32 const auto result = hasher.result();
33 std::string hexString;
34 boost::algorithm::hex(result.begin(), result.end(), std::back_inserter(hexString));
35
36 hexName.reserve(32);
37 hexName.push_back("-");
38 hexName.append(hexString.data());
39#endif
40 return hexName;
41}
42#include <QDir>
43#include <QFile>
44#include <QFileInfo>
45#include <QString>
46
47inline bool copyDirectoryRecursively(const QString& sourcePath, const QString& destPath)
48{
49 QDir sourceDir(sourcePath);
50 if(!sourceDir.exists())
51 {
52 return false;
53 }
54
55 QDir destDir(destPath);
56 // Create the destination directory if it doesn't exist
57 if(!destDir.exists() && !destDir.mkpath("."))
58 {
59 return false;
60 }
61
62 bool success = true;
63
64 // Get all files and directories, including hidden and system files, excluding "." and ".."
65 const QFileInfoList entries = sourceDir.entryInfoList(
66 QDir::NoDotAndDotDot | QDir::AllEntries | QDir::Hidden | QDir::System);
67
68 for(const QFileInfo& entryInfo : entries)
69 {
70 QString newDestPath = destDir.absoluteFilePath(entryInfo.fileName());
71
72 if(entryInfo.isDir())
73 {
74 // Recursively copy subdirectories
75 if(!copyDirectoryRecursively(entryInfo.absoluteFilePath(), newDestPath))
76 {
77 success = false;
78 }
79 }
80 else
81 {
82 // Overwrite existing files at the destination
83 if(QFile::exists(newDestPath))
84 {
85 QFile::remove(newDestPath);
86 }
87 // Copy the file
88 if(!QFile::copy(entryInfo.absoluteFilePath(), newDestPath))
89 {
90 success = false;
91 }
92 }
93 }
94
95 return success;
96}
97
98inline bool copyParentFolderContents(const QString& rootPath, const QString& dst)
99{
100 QFileInfo fileInfo(rootPath);
101
102 QString parentFolder = fileInfo.absolutePath();
103
104 return copyDirectoryRecursively(parentFolder, dst);
105}
106
107inline void loadJSObjectFromString(
108 const QString& rootPath, const QByteArray& str, QQmlComponent& comp, bool is_ui)
109{
110#if __has_include(<boost/hash2/xxh3.hpp>)
111 static const auto cache_path = QStandardPaths::writableLocation(QStandardPaths::CacheLocation);
112 QFile f{cache_path + "/Script" + hashFileData(str) + (is_ui ? ".ui.qml" : ".qml")};
113 if(f.open(QIODevice::ReadWrite))
114 {
115 // Only way to make sure we hit the QML cache
116 if(str != f.readAll())
117 {
118 f.resize(0);
119 f.reset();
120 f.write(str);
121 f.flush();
122 f.close();
123 }
124 copyParentFolderContents(rootPath, cache_path);
125
126 comp.loadUrl(QUrl::fromLocalFile(f.fileName()));
127 }
128 else
129#endif
130 {
131 comp.setData(str, QUrl::fromLocalFile(rootPath));
132 }
133}
134
135inline JS::Script* createJSObject(QQmlComponent& c, QQmlContext* context)
136{
137 const auto& errs = c.errors();
138 if(!errs.empty())
139 {
140 ossia::logger().error(
141 "Uncaught exception at line {} : {}", errs[0].line(),
142 errs[0].toString().toStdString());
143 return nullptr;
144 }
145 else
146 {
147 auto object = c.create(context);
148 auto obj = qobject_cast<JS::Script*>(object);
149 if(obj)
150 return obj;
151 delete object;
152 return nullptr;
153 }
154}
155
156inline JS::Script* createJSObject(
157 const QString& rootPath, const QString& val, QQmlEngine* engine,
158 QQmlContext* context)
159{
160 if(val.trimmed().startsWith("import"))
161 {
162 QQmlComponent c{engine};
163 loadJSObjectFromString(rootPath, val.toUtf8(), c, false);
164 return createJSObject(c, context);
165 }
166 else if(QFile::exists(val))
167 {
168 QQmlComponent c{engine, QUrl::fromLocalFile(val)};
169 return createJSObject(c, context);
170 }
171 return nullptr;
172}
173
174inline void setupExecFuncs(auto* self, QObject* context, ossia::qt::qml_engine_functions* m_execFuncs)
175{
176 QObject::connect(
177 m_execFuncs, &ossia::qt::qml_engine_functions::system, qApp,
178 [](const QString& code) {
179 std::thread{[code] { ::system(code.toStdString().c_str()); }}.detach();
180 }, Qt::QueuedConnection);
181
182 if(auto* js_panel = score::GUIAppContext().findPanel<JS::PanelDelegate>())
183 {
184 QObject::connect(
185 m_execFuncs, &ossia::qt::qml_engine_functions::exec, js_panel,
186 &JS::PanelDelegate::evaluate, Qt::QueuedConnection);
187
188 QObject::connect(
189 m_execFuncs, &ossia::qt::qml_engine_functions::compute, m_execFuncs,
190 [self, context, m_execFuncs, js_panel](const QString& code, const QString& cbname) {
191 // Exec thread
192
193 // Callback ran in UI thread
194 auto cb = [self
195 , context=QPointer{context}
196 , cur = QPointer{self->m_object}
197 , m_execFuncs
198 , cbname] (const QVariant& v) {
199 if(!self)
200 return;
201
202 // Go back to exec thread, we have to go through the normal engine exec ctx
203 ossia::qt::run_async(m_execFuncs, [self, context, cur, v, cbname] {
204 if(!context || !cur)
205 return;
206 if(self->m_object != cur)
207 return;
208
209 auto mo = self->m_object->metaObject();
210 for(int i = 0; i < mo->methodCount(); i++)
211 {
212 if(mo->method(i).name() == cbname)
213 {
214 mo->method(i).invoke(
215 self->m_object, Qt::DirectConnection, QGenericReturnArgument(),
216 QArgument<QVariant>{"v", v});
217 }
218 }
219 });
220 };
221
222 // Go to ui thread
223 ossia::qt::run_async(js_panel, [js_panel, code, cb]() {
224 js_panel->compute(code, cb); // This invokes cb
225 });
226 }, Qt::DirectConnection);
227 }
228}
229}
Definition ConsolePanel.hpp:40
Definition QmlObjects.hpp:845