15const int VuoConsole::maxLogs = 500;
20VuoConsole::VuoConsole(QObject *parent) :
23 areModelUpdatesPaused =
false;
24 oldLogsDeletedWhilePaused = 0;
44 startListeningInternal({LogStream::STDOUT, LogStream::STDERR});
50void VuoConsole::startListeningInternal(
const vector<LogStream> &streams)
59 dispatch_queue_t queue = dispatch_queue_create(
"org.vuo.editor.console", NULL);
61 for (LogStream stream : streams)
63 int origFd = (stream == LogStream::STDOUT ? STDOUT_FILENO : STDERR_FILENO);
64 const char *name = (stream == LogStream::STDOUT ?
"stdout" :
"stderr");
68 int copyFd = dup(origFd);
71 VUserLog(
"Console can't show logs from %s (dup failed): %s", name, strerror(errno));
77 int ret = pipe(readWriteFds);
80 VUserLog(
"Console can't show logs from %s (pipe failed): %s", name, strerror(errno));
86 int writeFd = dup2(readWriteFds[1], origFd);
89 VUserLog(
"Console can't show logs from %s (dup2 failed): %s", name, strerror(errno));
91 close(readWriteFds[0]);
92 close(readWriteFds[1]);
98 close(readWriteFds[1]);
102 int readFd = readWriteFds[0];
103 dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, readFd, 0, queue);
105 dispatch_source_set_event_handler(source, ^{
106 size_t estimated = dispatch_source_get_data(source);
107 char *buffer = (
char *)calloc(estimated + 1,
sizeof(
char));
111 read(readFd, buffer, estimated);
114 write(copyFd, buffer, estimated);
117 singleton->parseLogs(buffer, stream);
121 dispatch_resume(source);
132void VuoConsole::parseLogs(
char *buffer, LogStream stream)
134 QString bufferStr(buffer);
137 bufferStr.prepend(partialLog[stream]);
138 partialLog[stream] =
"";
140 bufferStr.replace(QRegExp(
"\\033\[[0-9;]+m"),
"");
142 QString separator =
"\n";
143 QStringList newLogs = bufferStr.split(separator, QString::SkipEmptyParts);
145 if (bufferStr.back() != separator)
146 partialLog[stream] = newLogs.takeLast();
148 if (! newLogs.isEmpty())
155void VuoConsole::appendLogs(QStringList newLogs)
157 logs.append(newLogs);
159 int oldLogsDeleted = 0;
160 if (logs.size() > maxLogs)
162 oldLogsDeleted = logs.size() - maxLogs;
163 logs.erase(logs.begin(), logs.begin() + logs.size() - maxLogs);
166 updateModel(oldLogsDeleted);
188 singleton->showInternal(screenMate);
194void VuoConsole::showInternal(QWidget *screenMate)
199 connect(window, &VuoConsoleWindow::destroyed,
this, [
this](){ window =
nullptr; });
207 window->activateWindow();
216void VuoConsole::updateModel(
int oldLogsDeleted)
220 if (! areModelUpdatesPaused)
227 oldLogsDeletedWhilePaused += oldLogsDeleted;
235void VuoConsole::pauseModelUpdates(
void)
237 areModelUpdatesPaused =
true;
243void VuoConsole::resumeModelUpdates(
void)
245 areModelUpdatesPaused =
false;
246 int oldLogsDeleted = oldLogsDeletedWhilePaused;
247 oldLogsDeletedWhilePaused = 0;
248 updateModel(oldLogsDeleted);
258 QFileDialog dialog(window, Qt::Sheet);
259 dialog.setAcceptMode(QFileDialog::AcceptSave);
260 dialog.setDefaultSuffix(
"log");
262 QString timestamp = QDateTime::currentDateTime().toString(
"yyyy-MM-dd hh-mm-ss");
263 QString userName = QString::fromStdString(
static_cast<VuoEditor *
>(qApp)->getUserName());
264 dialog.selectFile(QString(
"Vuo %1 %2.log").arg(timestamp).arg(userName));
266 if (dialog.exec() == QDialog::Accepted)
268 QString path = dialog.selectedFiles()[0];
270 if (file.open(QIODevice::WriteOnly))
272 QTextStream stream(&file);
274 QStringList logsCopy = window->
getModel();
275 for (QString log : logsCopy)
276 stream << log << endl;
279 VUserLog(
"Couldn't open file for writing: %s", path.toStdString().c_str());
282 resumeModelUpdates();
293 QTextStream stream(&copiedText);
295 QStringList logsCopy = window->
getModel();
298 stream <<
"```" << endl;
300 if (! selectedIndices.isEmpty())
301 for (QVariant index : selectedIndices)
302 stream << logsCopy.at(index.toInt()) << endl;
304 for (QString log : logsCopy)
305 stream << log << endl;
307 stream <<
"```" << endl;
309 QClipboard *clipboard = QApplication::clipboard();
310 clipboard->setText(copiedText);
312 resumeModelUpdates();