Signed-off-by: falkTX <falktx@falktx.com>tags/v1.0
@@ -0,0 +1,3 @@ | |||||
/bin/ | |||||
/build/ | |||||
.kdev4/ |
@@ -0,0 +1,5 @@ | |||||
../../dpf/dgl/ | |||||
../../dpf/distrho/ | |||||
../../dpf-widgets/generic | |||||
../../dpf-widgets/opengl | |||||
/usr/include/carla |
@@ -15,22 +15,110 @@ | |||||
* For a full copy of the GNU General Public License see the LICENSE file. | * For a full copy of the GNU General Public License see the LICENSE file. | ||||
*/ | */ | ||||
#include "CarlaNativePlugin.h" | |||||
#include "DistrhoPlugin.hpp" | #include "DistrhoPlugin.hpp" | ||||
#include "DistrhoUI.hpp" | |||||
START_NAMESPACE_DISTRHO | START_NAMESPACE_DISTRHO | ||||
// ----------------------------------------------------------------------------------------------------------- | // ----------------------------------------------------------------------------------------------------------- | ||||
static uint32_t host_get_buffer_size(NativeHostHandle); | |||||
static double host_get_sample_rate(NativeHostHandle); | |||||
static bool host_is_offline(NativeHostHandle); | |||||
static const NativeTimeInfo* host_get_time_info(NativeHostHandle handle); | |||||
static bool host_write_midi_event(NativeHostHandle handle, const NativeMidiEvent* event); | |||||
static intptr_t host_dispatcher(NativeHostHandle handle, NativeHostDispatcherOpcode opcode, int32_t index, intptr_t value, void* ptr, float opt); | |||||
// ----------------------------------------------------------------------------------------------------------- | |||||
class IldaeilPlugin : public Plugin | class IldaeilPlugin : public Plugin | ||||
{ | { | ||||
const NativePluginDescriptor* fCarlaPluginDescriptor; | |||||
NativePluginHandle fCarlaPluginHandle; | |||||
NativeHostDescriptor fCarlaHostDescriptor; | |||||
CarlaHostHandle fCarlaHostHandle; | |||||
NativeTimeInfo fCarlaTimeInfo; | |||||
UI* fUI; | |||||
public: | public: | ||||
IldaeilPlugin() | IldaeilPlugin() | ||||
: Plugin(0, 0, 0) | |||||
: Plugin(0, 0, 0), | |||||
fCarlaPluginDescriptor(nullptr), | |||||
fCarlaPluginHandle(nullptr), | |||||
fCarlaHostHandle(nullptr), | |||||
fUI(nullptr) | |||||
{ | { | ||||
fCarlaPluginDescriptor = carla_get_native_rack_plugin(); | |||||
DISTRHO_SAFE_ASSERT_RETURN(fCarlaPluginDescriptor != nullptr,); | |||||
memset(&fCarlaHostDescriptor, 0, sizeof(fCarlaHostDescriptor)); | |||||
memset(&fCarlaTimeInfo, 0, sizeof(fCarlaTimeInfo)); | |||||
fCarlaHostDescriptor.handle = this; | |||||
fCarlaHostDescriptor.resourceDir = carla_get_library_folder(); | |||||
fCarlaHostDescriptor.uiName = "Ildaeil"; | |||||
fCarlaHostDescriptor.uiParentId = 0; | |||||
fCarlaHostDescriptor.get_buffer_size = host_get_buffer_size; | |||||
fCarlaHostDescriptor.get_sample_rate = host_get_sample_rate; | |||||
fCarlaHostDescriptor.is_offline = host_is_offline; | |||||
fCarlaHostDescriptor.get_time_info = host_get_time_info; | |||||
fCarlaHostDescriptor.write_midi_event = host_write_midi_event; | |||||
fCarlaHostDescriptor.ui_parameter_changed = nullptr; | |||||
fCarlaHostDescriptor.ui_midi_program_changed = nullptr; | |||||
fCarlaHostDescriptor.ui_custom_data_changed = nullptr; | |||||
fCarlaHostDescriptor.ui_closed = nullptr; | |||||
fCarlaHostDescriptor.ui_open_file = nullptr; | |||||
fCarlaHostDescriptor.ui_save_file = nullptr; | |||||
fCarlaHostDescriptor.dispatcher = host_dispatcher; | |||||
fCarlaPluginHandle = fCarlaPluginDescriptor->instantiate(&fCarlaHostDescriptor); | |||||
DISTRHO_SAFE_ASSERT_RETURN(fCarlaPluginHandle != nullptr,); | |||||
fCarlaHostHandle = carla_create_native_plugin_host_handle(fCarlaPluginDescriptor, fCarlaPluginHandle); | |||||
} | } | ||||
~IldaeilPlugin() override | ~IldaeilPlugin() override | ||||
{ | { | ||||
if (fCarlaHostHandle != nullptr) | |||||
{ | |||||
carla_host_handle_free(fCarlaHostHandle); | |||||
} | |||||
if (fCarlaPluginHandle != nullptr) | |||||
fCarlaPluginDescriptor->cleanup(fCarlaPluginHandle); | |||||
} | |||||
const NativeTimeInfo* getTimeInfo() | |||||
{ | |||||
const TimePosition& timePos(getTimePosition()); | |||||
fCarlaTimeInfo.playing = timePos.playing; | |||||
fCarlaTimeInfo.frame = timePos.frame; | |||||
fCarlaTimeInfo.bbt.valid = timePos.bbt.valid; | |||||
fCarlaTimeInfo.bbt.bar = timePos.bbt.bar; | |||||
fCarlaTimeInfo.bbt.beat = timePos.bbt.beat; | |||||
fCarlaTimeInfo.bbt.tick = timePos.bbt.tick; | |||||
fCarlaTimeInfo.bbt.barStartTick = timePos.bbt.barStartTick; | |||||
fCarlaTimeInfo.bbt.beatsPerBar = timePos.bbt.beatsPerBar; | |||||
fCarlaTimeInfo.bbt.beatType = timePos.bbt.beatType; | |||||
fCarlaTimeInfo.bbt.ticksPerBeat = timePos.bbt.ticksPerBeat; | |||||
fCarlaTimeInfo.bbt.beatsPerMinute = timePos.bbt.beatsPerMinute; | |||||
return &fCarlaTimeInfo; | |||||
} | |||||
void resizeUI(const uint width, const uint height) | |||||
{ | |||||
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,); | |||||
fUI->setSize(width, height); | |||||
} | } | ||||
protected: | protected: | ||||
@@ -43,7 +131,13 @@ protected: | |||||
*/ | */ | ||||
const char* getLabel() const override | const char* getLabel() const override | ||||
{ | { | ||||
return "Ildaeil"; | |||||
#if DISTRHO_PLUGIN_IS_SYNTH | |||||
return "IldaeilSynth"; | |||||
#elif DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | |||||
return "IldaeilMIDI"; | |||||
#else | |||||
return "IldaeilFX"; | |||||
#endif | |||||
} | } | ||||
/** | /** | ||||
@@ -51,7 +145,7 @@ protected: | |||||
*/ | */ | ||||
const char* getDescription() const override | const char* getDescription() const override | ||||
{ | { | ||||
return "..."; | |||||
return "Ildaeil is a mini-plugin host working as a plugin, allowing one-to-one plugin format reusage."; | |||||
} | } | ||||
/** | /** | ||||
@@ -111,17 +205,29 @@ protected: | |||||
/* -------------------------------------------------------------------------------------------------------- | /* -------------------------------------------------------------------------------------------------------- | ||||
* Process */ | * Process */ | ||||
/** | |||||
Run/process function for plugins without MIDI input. | |||||
*/ | |||||
void run(const float** inputs, float** outputs, uint32_t frames) override | |||||
void activate() override | |||||
{ | |||||
if (fCarlaPluginHandle != nullptr) | |||||
fCarlaPluginDescriptor->activate(fCarlaPluginHandle); | |||||
} | |||||
void deactivate() override | |||||
{ | { | ||||
// copy inputs over outputs if needed | |||||
if (outputs[0] != inputs[0]) | |||||
std::memcpy(outputs[0], inputs[0], sizeof(float)*frames); | |||||
if (fCarlaPluginHandle != nullptr) | |||||
fCarlaPluginDescriptor->deactivate(fCarlaPluginHandle); | |||||
} | |||||
if (outputs[1] != inputs[1]) | |||||
std::memcpy(outputs[1], inputs[1], sizeof(float)*frames); | |||||
void run(const float** inputs, float** outputs, uint32_t frames) override | |||||
{ | |||||
if (fCarlaPluginHandle != nullptr) | |||||
{ | |||||
fCarlaPluginDescriptor->process(fCarlaPluginHandle, (float**)inputs, outputs, frames, nullptr, 0); | |||||
} | |||||
else | |||||
{ | |||||
std::memset(outputs[0], 0, sizeof(float)*frames); | |||||
std::memset(outputs[1], 0, sizeof(float)*frames); | |||||
} | |||||
} | } | ||||
// ------------------------------------------------------------------------------------------------------- | // ------------------------------------------------------------------------------------------------------- | ||||
@@ -133,6 +239,48 @@ private: | |||||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(IldaeilPlugin) | DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(IldaeilPlugin) | ||||
}; | }; | ||||
// ----------------------------------------------------------------------------------------------------------- | |||||
static uint32_t host_get_buffer_size(const NativeHostHandle handle) | |||||
{ | |||||
return static_cast<IldaeilPlugin*>(handle)->getBufferSize(); | |||||
} | |||||
static double host_get_sample_rate(const NativeHostHandle handle) | |||||
{ | |||||
return static_cast<IldaeilPlugin*>(handle)->getSampleRate(); | |||||
} | |||||
static bool host_is_offline(NativeHostHandle) | |||||
{ | |||||
return false; | |||||
} | |||||
static const NativeTimeInfo* host_get_time_info(NativeHostHandle handle) | |||||
{ | |||||
return static_cast<IldaeilPlugin*>(handle)->getTimeInfo(); | |||||
} | |||||
static bool host_write_midi_event(NativeHostHandle handle, const NativeMidiEvent* event) | |||||
{ | |||||
return false; | |||||
} | |||||
static intptr_t host_dispatcher(NativeHostHandle handle, NativeHostDispatcherOpcode opcode, | |||||
int32_t index, intptr_t value, void* ptr, float opt) | |||||
{ | |||||
switch (opcode) | |||||
{ | |||||
case NATIVE_HOST_OPCODE_UI_RESIZE: | |||||
static_cast<IldaeilPlugin*>(handle)->resizeUI(index, value); | |||||
break; | |||||
default: | |||||
break; | |||||
} | |||||
return 0; | |||||
} | |||||
/* ------------------------------------------------------------------------------------------------------------ | /* ------------------------------------------------------------------------------------------------------------ | ||||
* Plugin entry point, called by DPF to create a new plugin instance. */ | * Plugin entry point, called by DPF to create a new plugin instance. */ | ||||
@@ -15,32 +15,105 @@ | |||||
* For a full copy of the GNU General Public License see the LICENSE file. | * For a full copy of the GNU General Public License see the LICENSE file. | ||||
*/ | */ | ||||
#include "CarlaNativePlugin.h" | |||||
#include <X11/Xlib.h> | |||||
#include <X11/Xutil.h> | |||||
#include "../FX/DistrhoPluginInfo.h" | |||||
#include "DistrhoUI.hpp" | #include "DistrhoUI.hpp" | ||||
#include "DistrhoPlugin.hpp" | |||||
#include "ResizeHandle.hpp" | #include "ResizeHandle.hpp" | ||||
START_NAMESPACE_DISTRHO | START_NAMESPACE_DISTRHO | ||||
class IldaeilPlugin : public Plugin | |||||
{ | |||||
public: | |||||
const NativePluginDescriptor* fCarlaPluginDescriptor; | |||||
NativePluginHandle fCarlaPluginHandle; | |||||
NativeHostDescriptor fCarlaHostDescriptor; | |||||
CarlaHostHandle fCarlaHostHandle; | |||||
UI* fUI; | |||||
// ... | |||||
}; | |||||
// ----------------------------------------------------------------------------------------------------------- | // ----------------------------------------------------------------------------------------------------------- | ||||
// shared resource pointer | |||||
// carla_juce_init(); | |||||
class IldaeilUI : public UI | class IldaeilUI : public UI | ||||
{ | { | ||||
void* fContext; | |||||
ResizeHandle fResizeHandle; | |||||
IldaeilPlugin* const fPlugin; | |||||
// ResizeHandle fResizeHandle; | |||||
uint fPluginCount; | |||||
uint fPluginSelected; | |||||
::Window fHostWindowLookingToResize; | |||||
public: | public: | ||||
IldaeilUI() | IldaeilUI() | ||||
: UI(1280, 720), | : UI(1280, 720), | ||||
fContext(getPluginInstancePointer()), | |||||
fResizeHandle(this) | |||||
fPlugin((IldaeilPlugin*)getPluginInstancePointer()), | |||||
// fResizeHandle(this), | |||||
fPluginCount(0), | |||||
fPluginSelected(0), | |||||
fHostWindowLookingToResize(0) | |||||
{ | { | ||||
using namespace CarlaBackend; | |||||
if (fPlugin == nullptr || fPlugin->fCarlaHostHandle == nullptr) | |||||
return; | |||||
const CarlaHostHandle handle = fPlugin->fCarlaHostHandle; | |||||
if (carla_get_current_plugin_count(handle) != 0) | |||||
{ | |||||
const CarlaPluginInfo* const info = carla_get_plugin_info(handle, 0); | |||||
if (info->hints & PLUGIN_HAS_CUSTOM_UI) // FIXME use PLUGIN_HAS_CUSTOM_EMBED_UI | |||||
{ | |||||
const uintptr_t winId = getWindow().getNativeWindowHandle(); | |||||
carla_embed_custom_ui(handle, 0, (void*)winId); | |||||
fHostWindowLookingToResize = (::Window)winId; | |||||
tryResizingToChildWindowContent(); | |||||
} | |||||
} | |||||
// start cache/lookup, maybe spawn thread for this? | |||||
fPluginCount = carla_get_cached_plugin_count(PLUGIN_LV2, nullptr); | |||||
for (uint i=0; i<fPluginCount; ++i) | |||||
carla_get_cached_plugin_info(PLUGIN_LV2, i); | |||||
} | } | ||||
~IldaeilUI() override | ~IldaeilUI() override | ||||
{ | { | ||||
if (fPlugin != nullptr) | |||||
{ | |||||
fPlugin->fUI = nullptr; | |||||
if (fPlugin->fCarlaHostHandle != nullptr) | |||||
carla_show_custom_ui(fPlugin->fCarlaHostHandle, 0, false); | |||||
} | |||||
} | } | ||||
void onImGuiDisplay() override | void onImGuiDisplay() override | ||||
{ | { | ||||
if (fPlugin == nullptr || fPlugin->fCarlaHostHandle == nullptr) | |||||
return; | |||||
const CarlaHostHandle handle = fPlugin->fCarlaHostHandle; | |||||
if (carla_get_current_plugin_count(handle) != 0) | |||||
return; | |||||
float width = getWidth(); | float width = getWidth(); | ||||
float height = getHeight(); | float height = getHeight(); | ||||
float margin = 20.0f; | float margin = 20.0f; | ||||
@@ -48,8 +121,99 @@ public: | |||||
ImGui::SetNextWindowPos(ImVec2(margin, margin)); | ImGui::SetNextWindowPos(ImVec2(margin, margin)); | ||||
ImGui::SetNextWindowSize(ImVec2(width - 2 * margin, height - 2 * margin)); | ImGui::SetNextWindowSize(ImVec2(width - 2 * margin, height - 2 * margin)); | ||||
if (ImGui::Begin("Plugin List")) | |||||
if (ImGui::Begin("Plugin List", nullptr, ImGuiWindowFlags_NoResize)) | |||||
{ | { | ||||
static char searchBuf[0xff] = "Search..."; | |||||
ImGui::InputText("", searchBuf, sizeof(searchBuf)-1, ImGuiInputTextFlags_CharsNoBlank|ImGuiInputTextFlags_AutoSelectAll); | |||||
using namespace CarlaBackend; | |||||
if (ImGui::Button("Load Plugin")) | |||||
{ | |||||
do { | |||||
const CarlaCachedPluginInfo* info = carla_get_cached_plugin_info(PLUGIN_LV2, fPluginSelected); | |||||
DISTRHO_SAFE_ASSERT_BREAK(info != nullptr); | |||||
const char* const slash = std::strchr(info->label, DISTRHO_OS_SEP); | |||||
DISTRHO_SAFE_ASSERT_BREAK(slash != nullptr); | |||||
d_stdout("Loading %s...", info->name); | |||||
if (carla_add_plugin(handle, BINARY_NATIVE, PLUGIN_LV2, nullptr, nullptr, | |||||
slash+1, 0, 0x0, PLUGIN_OPTIONS_NULL)) | |||||
{ | |||||
const CarlaPluginInfo* const info = carla_get_plugin_info(handle, 0); | |||||
if (info->hints & PLUGIN_HAS_CUSTOM_UI) // FIXME use PLUGIN_HAS_CUSTOM_EMBED_UI | |||||
{ | |||||
const uintptr_t winId = getWindow().getNativeWindowHandle(); | |||||
carla_embed_custom_ui(handle, 0, (void*)winId); | |||||
fHostWindowLookingToResize = (::Window)winId; | |||||
tryResizingToChildWindowContent(); | |||||
} | |||||
repaint(); | |||||
} | |||||
} while (false); | |||||
} | |||||
if (ImGui::BeginChild("pluginlistwindow")) | |||||
{ | |||||
if (ImGui::BeginTable("pluginlist", 3, ImGuiTableFlags_NoSavedSettings|ImGuiTableFlags_NoClip)) | |||||
{ | |||||
ImGui::TableSetupColumn("Name"); | |||||
ImGui::TableSetupColumn("Bundle"); | |||||
ImGui::TableSetupColumn("URI"); | |||||
ImGui::TableHeadersRow(); | |||||
const char* const search = searchBuf[0] != 0 && std::strcmp(searchBuf, "Search...") != 0 ? searchBuf : nullptr; | |||||
if (fPluginCount != 0) | |||||
{ | |||||
for (uint i=0; i<fPluginCount; ++i) | |||||
{ | |||||
const CarlaCachedPluginInfo* info = carla_get_cached_plugin_info(PLUGIN_LV2, i); | |||||
DISTRHO_SAFE_ASSERT_CONTINUE(info != nullptr); | |||||
#if DISTRHO_PLUGIN_IS_SYNTH | |||||
if (info->midiIns != 1 || info->audioOuts != 2) | |||||
continue; | |||||
#elif DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | |||||
if (info->midiIns != 1 || info->midiOuts != 1) | |||||
continue; | |||||
if (info->audioIns != 0 || info->audioOuts != 0) | |||||
continue; | |||||
#else | |||||
if (info->audioIns != 2 || info->audioOuts != 2) | |||||
continue; | |||||
#endif | |||||
const char* const slash = std::strchr(info->label, DISTRHO_OS_SEP); | |||||
DISTRHO_SAFE_ASSERT_CONTINUE(slash != nullptr); | |||||
if (search != nullptr && strcasestr(info->name, search) == nullptr) | |||||
continue; | |||||
bool selected = fPluginSelected == i; | |||||
ImGui::TableNextRow(); | |||||
ImGui::TableSetColumnIndex(0); | |||||
ImGui::Selectable(info->name, &selected); | |||||
ImGui::TableSetColumnIndex(1); | |||||
ImGui::Selectable(slash+1, &selected); | |||||
ImGui::TableSetColumnIndex(2); | |||||
ImGui::TextUnformatted(info->label, slash); | |||||
if (selected) | |||||
fPluginSelected = i; | |||||
} | |||||
} | |||||
ImGui::EndTable(); | |||||
} | |||||
ImGui::EndChild(); | |||||
} | |||||
} | } | ||||
ImGui::End(); | ImGui::End(); | ||||
@@ -57,6 +221,80 @@ public: | |||||
void uiIdle() override | void uiIdle() override | ||||
{ | { | ||||
if (fPlugin == nullptr || fPlugin->fCarlaHostHandle == nullptr) | |||||
return; | |||||
fPlugin->fCarlaPluginDescriptor->ui_idle(fPlugin->fCarlaPluginHandle); | |||||
if (fHostWindowLookingToResize == 0) | |||||
return; | |||||
tryResizingToChildWindowContent(); | |||||
} | |||||
private: | |||||
void tryResizingToChildWindowContent() | |||||
{ | |||||
if (::Display* const display = XOpenDisplay(nullptr)) | |||||
{ | |||||
if (const ::Window childWindow = getChildWindow(display, fHostWindowLookingToResize)) | |||||
{ | |||||
d_stdout("found child window"); | |||||
XSizeHints sizeHints; | |||||
memset(&sizeHints, 0, sizeof(sizeHints)); | |||||
if (XGetNormalHints(display, childWindow, &sizeHints)) | |||||
{ | |||||
int width = 0; | |||||
int height = 0; | |||||
if (sizeHints.flags & PSize) | |||||
{ | |||||
width = sizeHints.width; | |||||
height = sizeHints.height; | |||||
} | |||||
else if (sizeHints.flags & PBaseSize) | |||||
{ | |||||
width = sizeHints.base_width; | |||||
height = sizeHints.base_height; | |||||
} | |||||
else if (sizeHints.flags & PMinSize) | |||||
{ | |||||
width = sizeHints.min_width; | |||||
height = sizeHints.min_height; | |||||
} | |||||
d_stdout("child window bounds %u %u", width, height); | |||||
if (width > 1 && height > 1) | |||||
{ | |||||
fHostWindowLookingToResize = 0; | |||||
setSize(static_cast<uint>(width), static_cast<uint>(height)); | |||||
} | |||||
} | |||||
else | |||||
d_stdout("child window without bounds"); | |||||
} | |||||
XCloseDisplay(display); | |||||
} | |||||
} | |||||
::Window getChildWindow(::Display* const display, const ::Window hostWindow) const | |||||
{ | |||||
::Window rootWindow, parentWindow, ret = 0; | |||||
::Window* childWindows = nullptr; | |||||
uint numChildren = 0; | |||||
XQueryTree(display, hostWindow, &rootWindow, &parentWindow, &childWindows, &numChildren); | |||||
if (numChildren > 0 && childWindows != nullptr) | |||||
{ | |||||
ret = childWindows[0]; | |||||
XFree(childWindows); | |||||
} | |||||
return ret; | |||||
} | } | ||||
protected: | protected: | ||||
@@ -1,185 +0,0 @@ | |||||
/* | |||||
* Resize handle for DPF | |||||
* Copyright (C) 2021 Filipe Coelho <falktx@falktx.com> | |||||
* | |||||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||||
* or without fee is hereby granted, provided that the above copyright notice and this | |||||
* permission notice appear in all copies. | |||||
* | |||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||||
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||||
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||||
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
*/ | |||||
#pragma once | |||||
#include "TopLevelWidget.hpp" | |||||
#include "../dgl/Color.hpp" | |||||
START_NAMESPACE_DGL | |||||
/** Resize handle for DPF windows, will sit on bottom-right. */ | |||||
class ResizeHandle : public TopLevelWidget | |||||
{ | |||||
public: | |||||
/** Constructor for placing this handle on top of a window. */ | |||||
explicit ResizeHandle(Window& window) | |||||
: TopLevelWidget(window), | |||||
handleSize(16), | |||||
resizing(false) | |||||
{ | |||||
resetArea(); | |||||
} | |||||
/** Overloaded constructor, will fetch the window from an existing top-level widget. */ | |||||
explicit ResizeHandle(TopLevelWidget* const tlw) | |||||
: TopLevelWidget(tlw->getWindow()), | |||||
handleSize(16), | |||||
resizing(false) | |||||
{ | |||||
resetArea(); | |||||
} | |||||
/** Set the handle size, minimum 16. */ | |||||
void setHandleSize(const uint size) | |||||
{ | |||||
handleSize = std::max(16u, size); | |||||
resetArea(); | |||||
} | |||||
protected: | |||||
void onDisplay() override | |||||
{ | |||||
const GraphicsContext& context(getGraphicsContext()); | |||||
const double lineWidth = 1.0 * getScaleFactor(); | |||||
#ifdef DGL_OPENGL | |||||
// glUseProgram(0); | |||||
glMatrixMode(GL_MODELVIEW); | |||||
#endif | |||||
// draw white lines, 1px wide | |||||
Color(1.0f, 1.0f, 1.0f).setFor(context); | |||||
l1.draw(context, lineWidth); | |||||
l2.draw(context, lineWidth); | |||||
l3.draw(context, lineWidth); | |||||
// draw black lines, offset by 1px and 1px wide | |||||
Color(0.0f, 0.0f, 0.0f).setFor(context); | |||||
Line<double> l1b(l1), l2b(l2), l3b(l3); | |||||
l1b.moveBy(lineWidth, lineWidth); | |||||
l2b.moveBy(lineWidth, lineWidth); | |||||
l3b.moveBy(lineWidth, lineWidth); | |||||
l1b.draw(context, lineWidth); | |||||
l2b.draw(context, lineWidth); | |||||
l3b.draw(context, lineWidth); | |||||
} | |||||
bool onMouse(const MouseEvent& ev) override | |||||
{ | |||||
if (ev.button != 1) | |||||
return false; | |||||
if (ev.press && area.contains(ev.pos)) | |||||
{ | |||||
resizing = true; | |||||
resizingSize = Size<double>(getWidth(), getHeight()); | |||||
lastResizePoint = ev.pos; | |||||
return true; | |||||
} | |||||
if (resizing && ! ev.press) | |||||
{ | |||||
resizing = false; | |||||
return true; | |||||
} | |||||
return false; | |||||
} | |||||
bool onMotion(const MotionEvent& ev) override | |||||
{ | |||||
if (! resizing) | |||||
return false; | |||||
const Size<double> offset(ev.pos.getX() - lastResizePoint.getX(), | |||||
ev.pos.getY() - lastResizePoint.getY()); | |||||
resizingSize += offset; | |||||
lastResizePoint = ev.pos; | |||||
// TODO min width, min height | |||||
const uint minWidth = 16; | |||||
const uint minHeight = 16; | |||||
if (resizingSize.getWidth() < minWidth) | |||||
resizingSize.setWidth(minWidth); | |||||
if (resizingSize.getWidth() > 16384) | |||||
resizingSize.setWidth(16384); | |||||
if (resizingSize.getHeight() < minHeight) | |||||
resizingSize.setHeight(minHeight); | |||||
if (resizingSize.getHeight() > 16384) | |||||
resizingSize.setHeight(16384); | |||||
setSize(resizingSize.getWidth(), resizingSize.getHeight()); | |||||
return true; | |||||
} | |||||
void onResize(const ResizeEvent& ev) override | |||||
{ | |||||
TopLevelWidget::onResize(ev); | |||||
resetArea(); | |||||
} | |||||
private: | |||||
Rectangle<uint> area; | |||||
Line<double> l1, l2, l3; | |||||
uint handleSize; | |||||
// event handling state | |||||
bool resizing; | |||||
Point<double> lastResizePoint; | |||||
Size<double> resizingSize; | |||||
void resetArea() | |||||
{ | |||||
const double scaleFactor = getScaleFactor(); | |||||
const uint margin = 0.0 * scaleFactor; | |||||
const uint size = handleSize * scaleFactor; | |||||
area = Rectangle<uint>(getWidth() - size - margin, | |||||
getHeight() - size - margin, | |||||
size, size); | |||||
recreateLines(area.getX(), area.getY(), size); | |||||
} | |||||
void recreateLines(const uint x, const uint y, const uint size) | |||||
{ | |||||
uint linesize = size; | |||||
uint offset = 0; | |||||
// 1st line, full diagonal size | |||||
l1.setStartPos(x + size, y); | |||||
l1.setEndPos(x, y + size); | |||||
// 2nd line, bit more to the right and down, cropped | |||||
offset += size / 3; | |||||
linesize -= size / 3; | |||||
l2.setStartPos(x + linesize + offset, y + offset); | |||||
l2.setEndPos(x + offset, y + linesize + offset); | |||||
// 3rd line, even more right and down | |||||
offset += size / 3; | |||||
linesize -= size / 3; | |||||
l3.setStartPos(x + linesize + offset, y + offset); | |||||
l3.setEndPos(x + offset, y + linesize + offset); | |||||
} | |||||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ResizeHandle) | |||||
}; | |||||
END_NAMESPACE_DGL |
@@ -0,0 +1,6 @@ | |||||
../Common/ | |||||
../../dpf/dgl/ | |||||
../../dpf/distrho/ | |||||
../../dpf-widgets/generic | |||||
../../dpf-widgets/opengl | |||||
/usr/include/carla |
@@ -30,7 +30,7 @@ | |||||
#define DISTRHO_PLUGIN_WANT_MIDI_INPUT 0 | #define DISTRHO_PLUGIN_WANT_MIDI_INPUT 0 | ||||
#define DISTRHO_PLUGIN_WANT_MIDI_OUTPUT 0 | #define DISTRHO_PLUGIN_WANT_MIDI_OUTPUT 0 | ||||
#define DISTRHO_PLUGIN_WANT_DIRECT_ACCESS 1 | #define DISTRHO_PLUGIN_WANT_DIRECT_ACCESS 1 | ||||
#define DISTRHO_PLUGIN_WANT_DIRECT_ACCESS 1 | |||||
#define DISTRHO_PLUGIN_WANT_TIMEPOS 1 | |||||
#define DISTRHO_UI_USE_CUSTOM 1 | #define DISTRHO_UI_USE_CUSTOM 1 | ||||
#define DISTRHO_UI_USER_RESIZABLE 0 | #define DISTRHO_UI_USER_RESIZABLE 0 | ||||
#define DISTRHO_UI_CUSTOM_INCLUDE_PATH "DearImGui.hpp" | #define DISTRHO_UI_CUSTOM_INCLUDE_PATH "DearImGui.hpp" | ||||
@@ -25,8 +25,12 @@ FILES_UI = \ | |||||
include ../../dpf/Makefile.plugins.mk | include ../../dpf/Makefile.plugins.mk | ||||
BUILD_CXX_FLAGS += -I../Common | BUILD_CXX_FLAGS += -I../Common | ||||
BUILD_CXX_FLAGS += -I../../dpf-widgets/generic | |||||
BUILD_CXX_FLAGS += -I../../dpf-widgets/opengl | BUILD_CXX_FLAGS += -I../../dpf-widgets/opengl | ||||
BUILD_CXX_FLAGS += $(shell pkg-config --cflags carla-host-plugin carla-native-plugin carla-utils) | |||||
LINK_FLAGS += $(shell pkg-config --libs carla-host-plugin carla-native-plugin carla-utils) | |||||
# -------------------------------------------------------------- | # -------------------------------------------------------------- | ||||
# Enable all possible plugin types | # Enable all possible plugin types | ||||
@@ -29,7 +29,7 @@ | |||||
#define DISTRHO_PLUGIN_WANT_LATENCY 1 | #define DISTRHO_PLUGIN_WANT_LATENCY 1 | ||||
#define DISTRHO_PLUGIN_WANT_MIDI_INPUT 1 | #define DISTRHO_PLUGIN_WANT_MIDI_INPUT 1 | ||||
#define DISTRHO_PLUGIN_WANT_MIDI_OUTPUT 1 | #define DISTRHO_PLUGIN_WANT_MIDI_OUTPUT 1 | ||||
#define DISTRHO_PLUGIN_WANT_DIRECT_ACCESS 1 | |||||
#define DISTRHO_PLUGIN_WANT_TIMEPOS 1 | |||||
#define DISTRHO_PLUGIN_WANT_DIRECT_ACCESS 1 | #define DISTRHO_PLUGIN_WANT_DIRECT_ACCESS 1 | ||||
#define DISTRHO_UI_USE_CUSTOM 1 | #define DISTRHO_UI_USE_CUSTOM 1 | ||||
#define DISTRHO_UI_USER_RESIZABLE 0 | #define DISTRHO_UI_USER_RESIZABLE 0 | ||||
@@ -29,7 +29,7 @@ | |||||
#define DISTRHO_PLUGIN_WANT_LATENCY 1 | #define DISTRHO_PLUGIN_WANT_LATENCY 1 | ||||
#define DISTRHO_PLUGIN_WANT_MIDI_INPUT 1 | #define DISTRHO_PLUGIN_WANT_MIDI_INPUT 1 | ||||
#define DISTRHO_PLUGIN_WANT_MIDI_OUTPUT 0 | #define DISTRHO_PLUGIN_WANT_MIDI_OUTPUT 0 | ||||
#define DISTRHO_PLUGIN_WANT_DIRECT_ACCESS 1 | |||||
#define DISTRHO_PLUGIN_WANT_TIMEPOS 1 | |||||
#define DISTRHO_PLUGIN_WANT_DIRECT_ACCESS 1 | #define DISTRHO_PLUGIN_WANT_DIRECT_ACCESS 1 | ||||
#define DISTRHO_UI_USE_CUSTOM 1 | #define DISTRHO_UI_USE_CUSTOM 1 | ||||
#define DISTRHO_UI_USER_RESIZABLE 0 | #define DISTRHO_UI_USER_RESIZABLE 0 | ||||