diff --git a/resources/ui/carla_host.ui b/resources/ui/carla_host.ui
index 610e46b56..90548960b 100644
--- a/resources/ui/carla_host.ui
+++ b/resources/ui/carla_host.ui
@@ -137,6 +137,52 @@
+
+
+ Logs
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 1
+
+ -
+
+
+
+ DejaVu Sans Mono
+
+
+
+ Qt::ScrollBarAlwaysOn
+
+
+ Qt::ScrollBarAlwaysOn
+
+
+ QPlainTextEdit::NoWrap
+
+
+ Loading...
+
+
+ Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse
+
+
+
+
+
diff --git a/source/backend/CarlaStandalone.cpp b/source/backend/CarlaStandalone.cpp
index 1aa694fa7..3429af09a 100644
--- a/source/backend/CarlaStandalone.cpp
+++ b/source/backend/CarlaStandalone.cpp
@@ -27,6 +27,10 @@
#include "CarlaBackendUtils.hpp"
#include "CarlaBase64Utils.hpp"
+#ifndef BUILD_BRIDGE
+# include "CarlaLogThread.hpp"
+#endif
+
#include "juce_audio_formats/juce_audio_formats.h"
#if defined(CARLA_OS_MAC) || defined(CARLA_OS_WIN)
@@ -49,6 +53,7 @@ struct CarlaBackendStandalone {
void* engineCallbackPtr;
#ifndef BUILD_BRIDGE
EngineOptions engineOptions;
+ CarlaLogThread logThread;
#endif
FileCallbackFunc fileCallback;
@@ -62,6 +67,7 @@ struct CarlaBackendStandalone {
engineCallbackPtr(nullptr),
#ifndef BUILD_BRIDGE
engineOptions(),
+ logThread(),
#endif
fileCallback(nullptr),
fileCallbackPtr(nullptr),
@@ -314,6 +320,7 @@ bool carla_engine_init(const char* driverName, const char* clientName)
{
#ifndef BUILD_BRIDGE
juce::initialiseJuce_GUI();
+ gStandalone.logThread.init();
#endif
gStandalone.lastError = "No error";
return true;
@@ -402,7 +409,9 @@ bool carla_engine_close()
#ifndef BUILD_BRIDGE
juce::shutdownJuce_GUI();
+ gStandalone.logThread.stop();
#endif
+
delete gStandalone.engine;
gStandalone.engine = nullptr;
@@ -436,12 +445,12 @@ void carla_set_engine_callback(EngineCallbackFunc func, void* ptr)
gStandalone.engineCallback = func;
gStandalone.engineCallbackPtr = ptr;
+#ifndef BUILD_BRIDGE
+ gStandalone.logThread.setCallback(func, ptr);
+#endif
+
if (gStandalone.engine != nullptr)
gStandalone.engine->setCallback(func, ptr);
-
-//#ifdef WANT_LOGS
-// gLogThread.setCallback(func, ptr);
-//#endif
}
#ifndef BUILD_BRIDGE
diff --git a/source/backend/CarlaUtils.cpp b/source/backend/CarlaUtils.cpp
index f651824bd..ff0d263c2 100644
--- a/source/backend/CarlaUtils.cpp
+++ b/source/backend/CarlaUtils.cpp
@@ -654,6 +654,16 @@ const char* carla_get_supported_file_extensions()
// -------------------------------------------------------------------------------------------------------------------
+void carla_fflush(bool err)
+{
+ std::fflush(err ? stderr : stdout);
+}
+
+void carla_fputs(bool err, const char* string)
+{
+ std::fputs(string, err ? stderr : stdout);
+}
+
void carla_set_process_name(const char* name)
{
carla_debug("carla_set_process_name(\"%s\")", name);
diff --git a/source/backend/CarlaUtils.h b/source/backend/CarlaUtils.h
index 4ee4ede18..48697a4f6 100644
--- a/source/backend/CarlaUtils.h
+++ b/source/backend/CarlaUtils.h
@@ -158,6 +158,16 @@ CARLA_EXPORT const CarlaCachedPluginInfo* carla_get_cached_plugin_info(PluginTyp
/* ------------------------------------------------------------------------------------------------------------
* set stuff */
+/*!
+ * Flush stdout or stderr.
+ */
+CARLA_EXPORT void carla_fflush(bool err);
+
+/*!
+ * Print the string @a string to stdout or stderr.
+ */
+CARLA_EXPORT void carla_fputs(bool err, const char* string);
+
/*!
* Set the current process name to @a name.
*/
diff --git a/source/carla_host.py b/source/carla_host.py
index 46bd3e117..b7107ec9a 100644
--- a/source/carla_host.py
+++ b/source/carla_host.py
@@ -68,6 +68,19 @@ CARLA_CLIENT_NAME = os.getenv("CARLA_CLIENT_NAME")
LADISH_APP_NAME = os.getenv("LADISH_APP_NAME")
NSM_URL = os.getenv("NSM_URL")
+# ------------------------------------------------------------------------------------------------------------
+# Carla Print class
+
+class CarlaPrint:
+ def __init__(self, err):
+ self.err = err
+
+ def flush(self):
+ gCarla.utils.fflush(self.err)
+
+ def write(self, string):
+ gCarla.utils.fputs(self.err, string)
+
# ------------------------------------------------------------------------------------------------------------
# Host Window
@@ -167,6 +180,7 @@ class HostWindow(QMainWindow):
self.ui.menu_Engine.setEnabled(False)
self.ui.menu_Engine.setVisible(False)
self.ui.menu_Engine.menuAction().setVisible(False)
+ self.ui.tabWidget.removeTab(2)
if self.host.isControl:
self.ui.act_file_new.setVisible(False)
@@ -443,6 +457,7 @@ class HostWindow(QMainWindow):
# ----------------------------------------------------------------------------------------------------
# Final setup
+ self.ui.text_logs.clear()
self.setProperWindowTitle()
# Qt needs this so it properly creates & resizes the canvas
@@ -613,8 +628,14 @@ class HostWindow(QMainWindow):
firstInit = self.fFirstEngineInit
self.fFirstEngineInit = False
+ self.ui.text_logs.appendPlainText("======= Starting engine =======")
+
+ if self.host.engine_init(audioDriver, self.fClientName):
+ self.ui.text_logs.appendPlainText("======= Engine started ========")
+ return
- if self.host.engine_init(audioDriver, self.fClientName) or firstInit:
+ elif firstInit:
+ self.ui.text_logs.appendPlainText("Failed to start engine on first try, ignored")
return
audioError = self.host.get_last_error()
@@ -626,6 +647,8 @@ class HostWindow(QMainWindow):
@pyqtSlot()
def slot_engineStop(self, forced = False):
+ self.ui.text_logs.appendPlainText("======= Stopping engine =======")
+
if self.fPluginCount == 0:
self.engineStopFinal()
return True
@@ -655,8 +678,11 @@ class HostWindow(QMainWindow):
if self.host.is_engine_running():
self.host.remove_all_plugins()
- if not self.host.engine_close():
- print(self.host.get_last_error())
+ if self.host.engine_close():
+ self.ui.text_logs.appendPlainText("======= Engine stopped ========")
+ else:
+ self.ui.text_logs.appendPlainText("Failed to stop engine, error was:")
+ self.ui.text_logs.appendPlainText(self.host.get_last_error())
if self.fCustomStopAction == 1:
self.close()
@@ -1601,7 +1627,9 @@ class HostWindow(QMainWindow):
if self.fCanvasWidth == 0 or self.fCanvasHeight == 0:
return
- if self.ui.tabWidget.currentIndex() == 1:
+ currentIndex = self.ui.tabWidget.currentIndex()
+
+ if currentIndex == 1:
width = self.ui.graphicsView.width()
height = self.ui.graphicsView.height()
else:
@@ -1609,7 +1637,7 @@ class HostWindow(QMainWindow):
self.ui.tabWidget.setCurrentIndex(1)
width = self.ui.graphicsView.width()
height = self.ui.graphicsView.height()
- self.ui.tabWidget.setCurrentIndex(0)
+ self.ui.tabWidget.setCurrentIndex(currentIndex)
self.ui.tabWidget.blockSignals(False)
self.ui.miniCanvasPreview.setViewSize(float(width)/self.fCanvasWidth, float(height)/self.fCanvasHeight)
@@ -1719,9 +1747,12 @@ class HostWindow(QMainWindow):
# --------------------------------------------------------------------------------------------------------
+ def fixLogText(self, text):
+ return text.replace("\x1b[30;1m", "").replace("\x1b[31m", "").replace("\x1b[0m", "")
+
@pyqtSlot(int, int, int, float, str)
def slot_handleDebugCallback(self, pluginId, value1, value2, value3, valueStr):
- print("DEBUG:", pluginId, value1, value2, value3, valueStr)
+ self.ui.text_logs.appendPlainText(self.fixLogText(valueStr))
@pyqtSlot(str)
def slot_handleInfoCallback(self, info):
@@ -2224,7 +2255,14 @@ def initHost(initName, libPrefix, isControl, isPlugin, failError, HostClass = No
gCarla.utils = CarlaUtils(os.path.join(pathBinaries, utilsname))
gCarla.utils.set_process_name(os.path.basename(initName))
- #gCarla.utils.set_locale_C()
+
+ try:
+ sys.stdout.flush()
+ except:
+ pass
+
+ sys.stdout = CarlaPrint(False)
+ sys.stderr = CarlaPrint(True)
# --------------------------------------------------------------------------------------------------------
# Done
diff --git a/source/carla_utils.py b/source/carla_utils.py
index 0b5a9e1fc..c7342954d 100644
--- a/source/carla_utils.py
+++ b/source/carla_utils.py
@@ -179,6 +179,12 @@ class CarlaUtils(object):
self.lib.carla_get_cached_plugin_info.argtypes = [c_enum, c_uint]
self.lib.carla_get_cached_plugin_info.restype = POINTER(CarlaCachedPluginInfo)
+ self.lib.carla_fflush.argtypes = [c_bool]
+ self.lib.carla_fflush.restype = None
+
+ self.lib.carla_fputs.argtypes = [c_bool, c_char_p]
+ self.lib.carla_fputs.restype = None
+
self.lib.carla_set_process_name.argtypes = [c_char_p]
self.lib.carla_set_process_name.restype = None
@@ -279,6 +285,12 @@ class CarlaUtils(object):
def get_cached_plugin_info(self, ptype, index):
return structToDict(self.lib.carla_get_cached_plugin_info(ptype, index).contents)
+ def fflush(self, err):
+ self.lib.carla_fflush(err)
+
+ def fputs(self, err, string):
+ self.lib.carla_fputs(err, string.encode("utf-8"))
+
def set_process_name(self, name):
self.lib.carla_set_process_name(name.encode("utf-8"))
diff --git a/source/utils/CarlaLogThread.hpp b/source/utils/CarlaLogThread.hpp
index 0223f6677..ce96f8c9e 100644
--- a/source/utils/CarlaLogThread.hpp
+++ b/source/utils/CarlaLogThread.hpp
@@ -1,6 +1,6 @@
/*
- * Carla Log thread
- * Copyright (C) 2013 Filipe Coelho
+ * Carla Log Thread
+ * Copyright (C) 2013-2016 Filipe Coelho
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -24,51 +24,67 @@
#include
-using CarlaBackend::CallbackFunc;
+using CarlaBackend::EngineCallbackFunc;
// -----------------------------------------------------------------------
// Log thread
-class CarlaLogThread : public CarlaThread
+class CarlaLogThread : private CarlaThread
{
public:
CarlaLogThread()
: CarlaThread("CarlaLogThread"),
+ fStdOut(-1),
+ fStdErr(-1),
fCallback(nullptr),
- fCallbackPtr(nullptr)
+ fCallbackPtr(nullptr) {}
+
+ ~CarlaLogThread()
+ {
+ stop();
+ }
+
+ void init()
{
- pipe(fPipe);
+ CARLA_SAFE_ASSERT_RETURN(pipe(fPipe) == 0,);
+ CARLA_SAFE_ASSERT_RETURN(fcntl(fPipe[0], F_SETFL, O_NONBLOCK) == 0,);
- fflush(stdout);
- fflush(stderr);
+ std::fflush(stdout);
+ std::fflush(stderr);
+
+ fStdOut = dup(STDOUT_FILENO);
+ fStdErr = dup(STDERR_FILENO);
- //fPipe[1] = ::dup(STDOUT_FILENO);
- //fPipe[1] = ::dup(STDERR_FILENO);
dup2(fPipe[1], STDOUT_FILENO);
dup2(fPipe[1], STDERR_FILENO);
- fcntl(fPipe[0], F_SETFL, O_NONBLOCK);
-
- start(2);
+ startThread();
}
- ~CarlaLogThread()
+ void stop()
{
- fCallback = nullptr;
- fCallbackPtr = nullptr;
+ if (fStdOut != -1)
+ return;
- stop(5000);
+ stopThread(5000);
- fflush(stdout);
- fflush(stderr);
+ std::fflush(stdout);
+ std::fflush(stderr);
close(fPipe[0]);
close(fPipe[1]);
+
+ dup2(fStdOut, STDOUT_FILENO);
+ dup2(fStdErr, STDERR_FILENO);
+ close(fStdOut);
+ close(fStdErr);
+ fStdOut = -1;
+ fStdErr = -1;
}
- void setCallback(CallbackFunc callback, void* callbackPtr)
+ void setCallback(EngineCallbackFunc callback, void* callbackPtr)
{
- CARLA_ASSERT(callback != nullptr);
+ CARLA_SAFE_ASSERT_RETURN(callback != nullptr,);
fCallback = callback;
fCallbackPtr = callbackPtr;
@@ -77,56 +93,62 @@ public:
protected:
void run()
{
- while (! shouldExit())
- {
- size_t r, lastRead;
- ssize_t r2; // to avoid sign/unsign conversions
+ CARLA_SAFE_ASSERT_RETURN(fCallback != nullptr,);
- static char bufTemp[1024+1] = { '\0' };
- static char bufRead[1024+1];
- static char bufSend[2048+1];
+ size_t k, bufTempPos;
+ ssize_t r, lastRead;
+ char bufTemp[1024+1];
+ char bufRead[1024+1];
+ char bufSend[2048+1];
- while ((r2 = read(fPipe[0], bufRead, sizeof(char)*1024)) > 0)
+ bufTemp[0] = '\0';
+ bufTempPos = 0;
+
+ while (! shouldThreadExit())
+ {
+ bufRead[0] = '\0';
+
+ while ((r = read(fPipe[0], bufRead, 1024)) > 0)
{
- r = static_cast(r2);
+ CARLA_SAFE_ASSERT_CONTINUE(r <= 1024);
bufRead[r] = '\0';
lastRead = 0;
- for (size_t i=0; i < r; ++i)
+ for (ssize_t i=0; i(i-lastRead);
- if (bufRead[i] == '\n')
+ if (bufTempPos != 0)
{
- std::strcpy(bufSend, bufTemp);
- std::strncat(bufSend, bufRead+lastRead, i-lastRead);
- bufSend[std::strlen(bufTemp)+i-lastRead] = '\0';
-
- lastRead = i;
- bufTemp[0] = '\0';
-
- if (fCallback != nullptr)
- {
- if (fOldBuffer.isNotEmpty())
- {
- fCallback(fCallbackPtr, CarlaBackend::CALLBACK_DEBUG, 0, 0, 0, 0.0f, fOldBuffer);
- fOldBuffer = nullptr;
- }
-
- fCallback(fCallbackPtr, CarlaBackend::CALLBACK_DEBUG, 0, 0, 0, 0.0f, bufSend);
- }
- else
- fOldBuffer += bufSend;
+ std::memcpy(bufSend, bufTemp, bufTempPos);
+ std::memcpy(bufSend+bufTempPos, bufRead+lastRead, k);
+ k += bufTempPos;
+ }
+ else
+ {
+ std::memcpy(bufSend, bufRead+lastRead, k);
}
- }
- CARLA_ASSERT(lastRead < r);
+ lastRead = i+1;
+ bufSend[k] = '\0';
+ bufTemp[0] = '\0';
+ bufTempPos = 0;
+
+ fCallback(fCallbackPtr, CarlaBackend::ENGINE_CALLBACK_DEBUG, 0, 0, 0, 0.0f, bufSend);
+ }
- if (lastRead > 0 && r > 0 && lastRead+1 < r)
+ if (lastRead > 0 && lastRead != r)
{
- std::strncpy(bufTemp, bufRead+lastRead, r-lastRead);
- bufTemp[r-lastRead] = '\0';
+ k = static_cast(r-lastRead);
+ std::memcpy(bufTemp, bufRead+lastRead, k);
+ bufTemp[k] = '\0';
+ bufTempPos = k;
}
}
@@ -135,13 +157,14 @@ protected:
}
private:
- int fPipe[2];
+ int fPipe[2];
+ int fStdOut;
+ int fStdErr;
- CallbackFunc fCallback;
- void* fCallbackPtr;
- CarlaString fOldBuffer;
+ EngineCallbackFunc fCallback;
+ void* fCallbackPtr;
- CARLA_PREVENT_HEAP_ALLOCATION
+ //CARLA_PREVENT_HEAP_ALLOCATION
CARLA_DECLARE_NON_COPY_CLASS(CarlaLogThread)
};