diff --git a/source/frontend/Makefile b/source/frontend/Makefile index 5db4d9c82..530f7abc2 100644 --- a/source/frontend/Makefile +++ b/source/frontend/Makefile @@ -12,6 +12,20 @@ include $(CWD)/Makefile.mk BINDIR := $(CWD)/../bin RESDIR := $(CWD)/../resources +ifeq ($(DEBUG),true) +OBJDIR := $(CWD)/../build/frontend/Debug +else +OBJDIR := $(CWD)/../build/frontend/Release +endif + +# --------------------------------------------------------------------------------------------------------------------- + +BUILD_CXX_FLAGS += -Iutils + +BUILD_CXX_FLAGS += -I../backend +BUILD_CXX_FLAGS += -I../includes +BUILD_CXX_FLAGS += -I../utils + # --------------------------------------------------------------------------------------------------------------------- ifeq ($(WINDOWS),true) @@ -38,6 +52,15 @@ endif TSs = $(patsubst %,translations/carla_%.ts,$(I18N_LANGUAGES)) QMs = $(patsubst %,translations/carla_%.qm,$(I18N_LANGUAGES)) +# --------------------------------------------------------------------------------------------------------------------- +# C++ files + +CPP_FILES = \ + carla_frontend.cpp \ + pluginlist/jackappdialog.cpp + +OBJS = $(CPP_FILES:%=$(OBJDIR)/%.o) + # --------------------------------------------------------------------------------------------------------------------- # Resources @@ -119,7 +142,7 @@ UIs += \ # --------------------------------------------------------------------------------------------------------------------- -all: $(QMs) $(RES) $(UIs) +all: $(BINDIR)/libcarla_frontend$(LIB_EXT) $(QMs) $(RES) $(UIs) # --------------------------------------------------------------------------------------------------------------------- @@ -149,8 +172,22 @@ $(BINDIR)/resources/zynaddsubfx-ui: ../native-plugins/resources/zynaddsubfx-ui # --------------------------------------------------------------------------------------------------------------------- +$(BINDIR)/libcarla_frontend$(LIB_EXT): $(OBJS) $(LIBS) + -@mkdir -p $(BINDIR) + @echo "Linking libcarla_frontend$(LIB_EXT)" + $(SILENT)$(CXX) $(OBJS) $(BUILD_CXX_FLAGS) $(QT5_LINK_FLAGS) $(SHARED) -o $@ + +$(OBJDIR)/%.cpp.o: %.cpp $(UIs) + -@mkdir -p $(shell dirname $@) + @echo "Compiling $<" + $(SILENT)$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ + +-include $(OBJS:%.o=%.d) + +# --------------------------------------------------------------------------------------------------------------------- + clean: - rm -rf $(UIs) $(RES) $(QMs) __pycache__ *.pyc + rm -rf $(BINDIR)/libcarla_frontend$(LIB_EXT) $(UIs) $(RES) $(QMs) __pycache__ *.pyc # old files rm -f ui_carla_add_jack.py rm -f ui_carla_database.py diff --git a/source/frontend/carla_frontend.cpp b/source/frontend/carla_frontend.cpp index 922044793..de2dc26ec 100644 --- a/source/frontend/carla_frontend.cpp +++ b/source/frontend/carla_frontend.cpp @@ -15,25 +15,9 @@ * For a full copy of the GNU General Public License see the doc/GPL.txt file. */ -#include "CarlaBackend.h" -#include "CarlaUtils.hpp" - -namespace CB = CARLA_BACKEND_NAMESPACE; - // ------------------------------------------------------------------------------------------------------------------- +// common files -struct WidgetResult { - union { - struct { - const char* command; - const char* name; - const char* labelSetup; - } jackappdialog; - }; -}; - -CARLA_PLUGIN_EXPORT void* carla_frontend_create_widget(void* parent, const char* widgetType); - -CARLA_PLUGIN_EXPORT void* carla_frontend_get_result(void* widget, const char* widgetType); +#include "utils/qsafesettings.cpp" // ------------------------------------------------------------------------------------------------------------------- diff --git a/source/frontend/carla_frontend.py b/source/frontend/carla_frontend.py new file mode 100644 index 000000000..864dd0aaf --- /dev/null +++ b/source/frontend/carla_frontend.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# Carla Backend utils +# Copyright (C) 2011-2022 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 +# published by the Free Software Foundation; either version 2 of +# the License, or any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# For a full copy of the GNU General Public License see the doc/GPL.txt file. + +# ------------------------------------------------------------------------------------------------------------ +# Imports (Global) + +# ------------------------------------------------------------------------------------------------------------ +# Imports (ctypes) + +from ctypes import ( + c_bool, c_char_p, c_double, c_int, c_uint, c_uint32, c_void_p, cast, + cdll, Structure, + CFUNCTYPE, POINTER +) + +from sip import voidptr + +# ------------------------------------------------------------------------------------------------------------ +# Imports (Custom) + +from carla_backend import ( + structToDict +) + +# --------------------------------------------------------------------------------------------------------------------- +# Convert a ctypes struct into a python dict, or None if null + +def structToDictOrNull(struct): + return structToDict(struct.contents) if struct else None + +# ------------------------------------------------------------------------------------------------------------ +# Carla Frontend API (C stuff) + +class JackApplicationDialogResults(Structure): + _fields_ = [ + ("command", c_char_p), + ("name", c_char_p), + ("labelSetup", c_char_p) + ] + +# ------------------------------------------------------------------------------------------------------------ +# Carla Frontend object using a DLL + +class CarlaFrontendLib(): + def __init__(self, filename): + self.lib = cdll.LoadLibrary(filename) + + self.lib.carla_frontend_createAndExecJackApplicationW.argtypes = (c_void_p, c_char_p) + self.lib.carla_frontend_createAndExecJackApplicationW.restype = POINTER(JackApplicationDialogResults) + + # -------------------------------------------------------------------------------------------------------- + + def createAndExecJackApplicationW(self, parent, projectFilename): + # FIXME cast(c_void_p, voidptr(parent)) + return structToDictOrNull(self.lib.carla_frontend_createAndExecJackApplicationW(None, projectFilename.encode("utf-8"))) + +# ------------------------------------------------------------------------------------------------------------ diff --git a/source/frontend/carla_host.py b/source/frontend/carla_host.py index 12ba51a44..33e9ff35c 100644 --- a/source/frontend/carla_host.py +++ b/source/frontend/carla_host.py @@ -55,13 +55,14 @@ import ui_carla_host from carla_app import * from carla_backend import * from carla_backend_qt import CarlaHostQtDLL, CarlaHostQtNull +from carla_frontend import CarlaFrontendLib from carla_shared import * from carla_settings import * from carla_utils import * from carla_widgets import * from patchcanvas import patchcanvas -from pluginlist import PluginDatabaseW +from pluginlist import PluginDatabaseW, JackApplicationW from widgets.digitalpeakmeter import DigitalPeakMeter from widgets.pixmapkeyboard import PixmapKeyboardHArea @@ -1235,16 +1236,16 @@ class HostWindow(QMainWindow): return (btype, ptype, filename, label, uniqueId, extraPtr) def showAddJackAppDialog(self): - dialog = JackApplicationW(self.fParentOrSelf, self.fProjectFilename) + ret = gCarla.felib.createAndExecJackApplicationW(self.fParentOrSelf, self.fProjectFilename) - if not dialog.exec_(): + if not ret: return if not self.host.is_engine_running(): QMessageBox.warning(self, self.tr("Warning"), self.tr("Cannot add new plugins while engine is stopped")) return - return dialog.getCommandAndFlags() + return ret @pyqtSlot() def slot_favoritePluginAdd(self): @@ -1351,16 +1352,16 @@ class HostWindow(QMainWindow): if data is None: return - filename, name, label = data - - if not filename: + if not data['command']: CustomMessageBox(self, QMessageBox.Critical, self.tr("Error"), self.tr("Cannot add jack application"), - self.tr("command is empty"), QMessageBox.Ok, QMessageBox.Ok) + self.tr("command is empty"), QMessageBox.Ok, QMessageBox.Ok) return - if not self.host.add_plugin(BINARY_NATIVE, PLUGIN_JACK, filename, name, label, 0, None, PLUGIN_OPTIONS_NULL): + if not self.host.add_plugin(BINARY_NATIVE, PLUGIN_JACK, + data['command'], data['name'], data['labelSetup'], + 0, None, PLUGIN_OPTIONS_NULL): CustomMessageBox(self, QMessageBox.Critical, self.tr("Error"), self.tr("Failed to load plugin"), - self.host.get_last_error(), QMessageBox.Ok, QMessageBox.Ok) + self.host.get_last_error(), QMessageBox.Ok, QMessageBox.Ok) # -------------------------------------------------------------------------------------------------------- # Plugins (macros) @@ -3234,6 +3235,7 @@ def initHost(initName, libPrefix, isControl, isPlugin, failError, HostClass = No libname = "libcarla_%s2.%s" % ("control" if isControl else "standalone", DLL_EXTENSION) libname = os.path.join(pathBinaries, libname) + felibname = os.path.join(pathBinaries, "libcarla_frontend.%s" % (DLL_EXTENSION)) utilsname = os.path.join(pathBinaries, "libcarla_utils.%s" % (DLL_EXTENSION)) # -------------------------------------------------------------------------------------------------------- @@ -3275,6 +3277,12 @@ def initHost(initName, libPrefix, isControl, isPlugin, failError, HostClass = No if not isControl: host.nsmOK = host.nsm_init(os.getpid(), initName) + # -------------------------------------------------------------------------------------------------------- + # Init frontend lib + + if not gCarla.nogui: + gCarla.felib = CarlaFrontendLib(felibname) + # -------------------------------------------------------------------------------------------------------- # Init utils diff --git a/source/frontend/carla_shared.py b/source/frontend/carla_shared.py index 784185a09..85767c70a 100644 --- a/source/frontend/carla_shared.py +++ b/source/frontend/carla_shared.py @@ -594,6 +594,7 @@ class CarlaObject(): self.gui = None # Host Window self.nogui = False # Skip UI self.term = False # Terminated by OS signal + self.felib = None # Frontend lib object self.utils = None # Utils object gCarla = CarlaObject() diff --git a/source/frontend/pluginlist/jackappdialog.cpp b/source/frontend/pluginlist/jackappdialog.cpp index c7cf5a3d3..e24c9d22d 100644 --- a/source/frontend/pluginlist/jackappdialog.cpp +++ b/source/frontend/pluginlist/jackappdialog.cpp @@ -38,8 +38,10 @@ # pragma GCC diagnostic pop #endif -#include "../utils/qsafesettings.hpp" -#include "../../includes/CarlaLibJackHints.h" +#include "qsafesettings.hpp" + +#include "CarlaLibJackHints.h" +#include "CarlaString.hpp" // -------------------------------------------------------------------------------------------------------------------- // Jack Application Dialog @@ -113,7 +115,7 @@ JackApplicationW::CommandAndFlags JackApplicationW::getCommandAndFlags() const } SessionManager smgr; - switch(self.ui.cb_session_mgr->currentIndex()) + switch (self.ui.cb_session_mgr->currentIndex()) { case UI_SESSION_LADISH: smgr = LIBJACK_SESSION_MANAGER_LADISH; @@ -238,6 +240,35 @@ void JackApplicationW::slot_saveSettings() settings.setValue("MidiOutMixdown", self.ui.cb_out_midi_mixdown->isChecked()); } +// -------------------------------------------------------------------------------------------------------------------- + +JackApplicationDialogResults* carla_frontend_createAndExecJackApplicationW(void* const parent, const char* const projectFilename) +{ + JackApplicationW gui(reinterpret_cast(parent), projectFilename); + + if (gui.exec()) + { + static JackApplicationDialogResults ret = {}; + static CarlaString retCommand; + static CarlaString retName; + static CarlaString retLabelSetup; + + const JackApplicationW::CommandAndFlags cafs = gui.getCommandAndFlags(); + retCommand = cafs.command.toUtf8().constData(); + retName = cafs.name.toUtf8().constData(); + retLabelSetup = cafs.labelSetup.toUtf8().constData(); + + ret.command = retCommand; + ret.name = retName; + ret.labelSetup = retLabelSetup; + + return &ret; + } + + return nullptr; +} + +#if 0 // -------------------------------------------------------------------------------------------------------------------- // Testing @@ -246,19 +277,17 @@ void JackApplicationW::slot_saveSettings() int main(int argc, char* argv[]) { QApplication app(argc, argv); - JackApplicationW gui(nullptr, ""); - gui.show(); - if (gui.exec()) + if (JackApplicationDialogResults* const res = carla_frontend_createAndExecJackApplicationW(nullptr, "")) { - auto cf = gui.getCommandAndFlags(); printf("Results:\n"); - printf("\tCommand: %s\n", cf.command.toUtf8().constData()); - printf("\tName: %s\n", cf.name.toUtf8().constData()); - printf("\tLabelSetup: %s\n", cf.labelSetup.toUtf8().constData()); + printf("\tCommand: %s\n", res->command); + printf("\tName: %s\n", res->name); + printf("\tLabelSetup: %s\n", res->labelSetup); } return 0; } +#endif // -------------------------------------------------------------------------------------------------------------------- diff --git a/source/frontend/pluginlist/jackappdialog.hpp b/source/frontend/pluginlist/jackappdialog.hpp index 317a10319..446b8eee1 100644 --- a/source/frontend/pluginlist/jackappdialog.hpp +++ b/source/frontend/pluginlist/jackappdialog.hpp @@ -35,6 +35,8 @@ #include "../utils/qcarlastring.hpp" +#include "CarlaDefines.h" + // -------------------------------------------------------------------------------------------------------------------- // Jack Application Dialog @@ -43,6 +45,8 @@ class JackApplicationW : public QDialog struct Self; Self& self; + // ---------------------------------------------------------------------------------------------------------------- + public: explicit JackApplicationW(QWidget* parent, const char* projectFilename); ~JackApplicationW() override; @@ -74,3 +78,17 @@ private slots: }; // -------------------------------------------------------------------------------------------------------------------- + +extern "C" { + +struct JackApplicationDialogResults { + const char* command; + const char* name; + const char* labelSetup; +}; + +CARLA_API JackApplicationDialogResults* carla_frontend_createAndExecJackApplicationW(void* parent, const char* projectFilename); + +} + +// -------------------------------------------------------------------------------------------------------------------- diff --git a/source/frontend/widgets/collapsablewidget.hpp b/source/frontend/widgets/collapsablewidget.hpp new file mode 100644 index 000000000..4b5f15be7 --- /dev/null +++ b/source/frontend/widgets/collapsablewidget.hpp @@ -0,0 +1,74 @@ +/* + * Carla plugin host + * Copyright (C) 2011-2022 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 + * published by the Free Software Foundation; either version 2 of + * the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For a full copy of the GNU General Public License see the doc/GPL.txt file. + */ + +#include "QtWidgets/qboxlayout.h" +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wdeprecated-copy-with-user-provided-copy" +# pragma clang diagnostic ignored "-Wdeprecated-register" +#elif defined(__GNUC__) && __GNUC__ >= 8 +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wclass-memaccess" +# pragma GCC diagnostic ignored "-Wdeprecated-copy" +#endif + +#include +#include + +#ifdef __clang__ +# pragma clang diagnostic pop +#elif defined(__GNUC__) && __GNUC__ >= 8 +# pragma GCC diagnostic pop +#endif + +#include "CarlaDefines.h" + +// -------------------------------------------------------------------------------------------------------------------- +// Jack Application Dialog + +class CollapsibleBox : public QFrame +{ + struct Self; + Self& self; + + // ---------------------------------------------------------------------------------------------------------------- + +public: + explicit CollapsibleBox(QWidget* parent); + ~CollapsibleBox() override; + + // ---------------------------------------------------------------------------------------------------------------- + // public methods + + QVBoxLayout* getContentLayout() const noexcept; + + // ---------------------------------------------------------------------------------------------------------------- + // private slots + +private slots: + void toolButtonPressed(bool toggled); +}; + +// -------------------------------------------------------------------------------------------------------------------- + +extern "C" { + +CARLA_API void* carla_frontend_createCollapsibleBox(void* parent); + +} + +// --------------------------------------------------------------------------------------------------------------------