/* * Juce Plugin Window Helper * Copyright (C) 2013-2021 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. */ #ifndef JUCE_PLUGIN_WINDOW_HPP_INCLUDED #define JUCE_PLUGIN_WINDOW_HPP_INCLUDED #include "CarlaJuceUtils.hpp" #include "CarlaVstUtils.hpp" #include "AppConfig.h" #include "juce_gui_basics/juce_gui_basics.h" #if defined(CARLA_OS_LINUX) && defined(HAVE_X11) # include #elif defined(CARLA_OS_MAC) # define Component CocoaComponent # define MemoryBlock CocoaMemoryBlock # define Point CocoaPoint # import # undef Component # undef MemoryBlock # undef Point #endif // ----------------------------------------------------------------------- namespace juce { class JucePluginWindow : public DialogWindow { public: JucePluginWindow(const uintptr_t parentId, const bool isStandalone, AEffect* const vstEffect) : DialogWindow("JucePluginWindow", Colour(50, 50, 200), true, false), fIsStandalone(isStandalone), fClosed(false), fShown(false), fTransientId(parentId), fLastKeyIndex(0), fLastKeyValue(0), fLastModifiers(), fVstEffect(vstEffect) { setVisible(false); setOpaque(true); setResizable(false, false); setUsingNativeTitleBar(true); } void show(Component* const comp) { fClosed = false; fShown = true; centreWithSize(comp->getWidth(), comp->getHeight()); setContentNonOwned(comp, true); if (! isOnDesktop()) addToDesktop(); #ifndef CARLA_OS_LINUX setAlwaysOnTop(true); #endif setTransient(); setVisible(true); toFront(true); #ifndef CARLA_OS_LINUX postCommandMessage(0); #endif } bool keyPressed (const KeyPress& key) override { if (DialogWindow::keyPressed (key)) return true; if (fVstEffect != nullptr) { const int juceKeyCode = key.getKeyCode(); int vstKeyIndex = 0; int vstKeyValue = 0; do { /**/ if (juceKeyCode == KeyPress::spaceKey) vstKeyValue = 7; else if (juceKeyCode == KeyPress::escapeKey) vstKeyValue = 6; else if (juceKeyCode == KeyPress::returnKey) vstKeyValue = 19; // NOTE: 4 is '\r' while 19 is '\n' else if (juceKeyCode == KeyPress::tabKey) vstKeyValue = 2; else if (juceKeyCode == KeyPress::deleteKey) vstKeyValue = 22; else if (juceKeyCode == KeyPress::backspaceKey) vstKeyValue = 1; else if (juceKeyCode == KeyPress::insertKey) vstKeyValue = 21; else if (juceKeyCode == KeyPress::upKey) vstKeyValue = 12; else if (juceKeyCode == KeyPress::downKey) vstKeyValue = 14; else if (juceKeyCode == KeyPress::leftKey) vstKeyValue = 11; else if (juceKeyCode == KeyPress::rightKey) vstKeyValue = 13; else if (juceKeyCode == KeyPress::pageUpKey) vstKeyValue = 15; else if (juceKeyCode == KeyPress::pageDownKey) vstKeyValue = 16; else if (juceKeyCode == KeyPress::homeKey) vstKeyValue = 10; else if (juceKeyCode == KeyPress::endKey) vstKeyValue = 9; else if (juceKeyCode == KeyPress::F1Key) vstKeyValue = 40; else if (juceKeyCode == KeyPress::F2Key) vstKeyValue = 41; else if (juceKeyCode == KeyPress::F3Key) vstKeyValue = 42; else if (juceKeyCode == KeyPress::F4Key) vstKeyValue = 43; else if (juceKeyCode == KeyPress::F5Key) vstKeyValue = 44; else if (juceKeyCode == KeyPress::F6Key) vstKeyValue = 45; else if (juceKeyCode == KeyPress::F7Key) vstKeyValue = 46; else if (juceKeyCode == KeyPress::F8Key) vstKeyValue = 47; else if (juceKeyCode == KeyPress::F9Key) vstKeyValue = 48; else if (juceKeyCode == KeyPress::F10Key) vstKeyValue = 49; else if (juceKeyCode == KeyPress::F11Key) vstKeyValue = 50; else if (juceKeyCode == KeyPress::F12Key) vstKeyValue = 51; else if (juceKeyCode == KeyPress::F13Key) break; else if (juceKeyCode == KeyPress::F14Key) break; else if (juceKeyCode == KeyPress::F15Key) break; else if (juceKeyCode == KeyPress::F16Key) break; else if (juceKeyCode == KeyPress::F17Key) break; else if (juceKeyCode == KeyPress::F18Key) break; else if (juceKeyCode == KeyPress::F19Key) break; else if (juceKeyCode == KeyPress::F20Key) break; else if (juceKeyCode == KeyPress::F21Key) break; else if (juceKeyCode == KeyPress::F22Key) break; else if (juceKeyCode == KeyPress::F23Key) break; else if (juceKeyCode == KeyPress::F24Key) break; else if (juceKeyCode == KeyPress::F25Key) break; else if (juceKeyCode == KeyPress::F26Key) break; else if (juceKeyCode == KeyPress::F27Key) break; else if (juceKeyCode == KeyPress::F28Key) break; else if (juceKeyCode == KeyPress::F29Key) break; else if (juceKeyCode == KeyPress::F30Key) break; else if (juceKeyCode == KeyPress::F31Key) break; else if (juceKeyCode == KeyPress::F32Key) break; else if (juceKeyCode == KeyPress::F33Key) break; else if (juceKeyCode == KeyPress::F34Key) break; else if (juceKeyCode == KeyPress::F35Key) break; else if (juceKeyCode == KeyPress::numberPad0) vstKeyValue = 24; else if (juceKeyCode == KeyPress::numberPad1) vstKeyValue = 25; else if (juceKeyCode == KeyPress::numberPad2) vstKeyValue = 26; else if (juceKeyCode == KeyPress::numberPad3) vstKeyValue = 27; else if (juceKeyCode == KeyPress::numberPad4) vstKeyValue = 28; else if (juceKeyCode == KeyPress::numberPad5) vstKeyValue = 29; else if (juceKeyCode == KeyPress::numberPad6) vstKeyValue = 30; else if (juceKeyCode == KeyPress::numberPad7) vstKeyValue = 31; else if (juceKeyCode == KeyPress::numberPad8) vstKeyValue = 32; else if (juceKeyCode == KeyPress::numberPad9) vstKeyValue = 33; else if (juceKeyCode == KeyPress::numberPadAdd) vstKeyValue = 35; else if (juceKeyCode == KeyPress::numberPadSubtract) vstKeyValue = 37; else if (juceKeyCode == KeyPress::numberPadMultiply) vstKeyValue = 34; else if (juceKeyCode == KeyPress::numberPadDivide) vstKeyValue = 39; else if (juceKeyCode == KeyPress::numberPadSeparator) vstKeyValue = 36; else if (juceKeyCode == KeyPress::numberPadDecimalPoint) vstKeyValue = 38; else if (juceKeyCode == KeyPress::numberPadEquals) vstKeyValue = 57; else if (juceKeyCode == KeyPress::numberPadDelete) break; else if (juceKeyCode == KeyPress::playKey) break; else if (juceKeyCode == KeyPress::stopKey) break; else if (juceKeyCode == KeyPress::fastForwardKey) break; else if (juceKeyCode == KeyPress::rewindKey) break; else vstKeyIndex = juceKeyCode; } while (false); fLastKeyIndex = vstKeyIndex; fLastKeyValue = vstKeyValue; return fVstEffect->dispatcher (fVstEffect, effEditKeyDown, vstKeyIndex, vstKeyValue, nullptr, 0.0f) != 0; } if (Component* const comp = getContentComponent()) return comp->keyPressed (key); return false; } bool keyStateChanged (bool isKeyDown) override { if (DialogWindow::keyStateChanged (isKeyDown)) return true; if (fVstEffect != nullptr && (fLastKeyIndex != 0 || fLastKeyValue != 0) && ! isKeyDown) { const int vstKeyIndex = fLastKeyIndex; const int vstKeyValue = fLastKeyValue; fLastKeyIndex = fLastKeyValue = 0; return fVstEffect->dispatcher (fVstEffect, effEditKeyUp, vstKeyIndex, vstKeyValue, nullptr, 0.0f) != 0; } if (Component* const comp = getContentComponent()) return comp->keyStateChanged (isKeyDown); return false; } void modifierKeysChanged (const ModifierKeys& modifiers) override { DialogWindow::modifierKeysChanged (modifiers); if (fVstEffect != nullptr) { const int oldRawFlags = fLastModifiers.getRawFlags(); const int newRawFlags = modifiers.getRawFlags(); if ((oldRawFlags & ModifierKeys::shiftModifier) != (newRawFlags & ModifierKeys::shiftModifier)) { fVstEffect->dispatcher (fVstEffect, (newRawFlags & ModifierKeys::shiftModifier) ? effEditKeyDown : effEditKeyUp, 0, 54, nullptr, 0.0f); } if ((oldRawFlags & ModifierKeys::ctrlModifier) != (newRawFlags & ModifierKeys::ctrlModifier)) { fVstEffect->dispatcher (fVstEffect, (newRawFlags & ModifierKeys::ctrlModifier) ? effEditKeyDown : effEditKeyUp, 0, 55, nullptr, 0.0f); } if ((oldRawFlags & ModifierKeys::altModifier) != (newRawFlags & ModifierKeys::altModifier)) { fVstEffect->dispatcher (fVstEffect, (newRawFlags & ModifierKeys::altModifier) ? effEditKeyDown : effEditKeyUp, 0, 56, nullptr, 0.0f); } if ((oldRawFlags & ModifierKeys::popupMenuClickModifier) != (newRawFlags & ModifierKeys::popupMenuClickModifier)) { fVstEffect->dispatcher (fVstEffect, (newRawFlags & ModifierKeys::popupMenuClickModifier) ? effEditKeyDown : effEditKeyUp, 0, 58, nullptr, 0.0f); } } fLastModifiers = modifiers; } void hide() { setVisible(false); if (isOnDesktop()) removeFromDesktop(); clearContentComponent(); } bool wasClosedByUser() const noexcept { return fClosed; } protected: void closeButtonPressed() override { fClosed = true; } bool escapeKeyPressed() override { fClosed = true; return true; } int getDesktopWindowStyleFlags() const override { int wflags = 0; wflags |= ComponentPeer::windowHasCloseButton; wflags |= ComponentPeer::windowHasDropShadow; wflags |= ComponentPeer::windowHasTitleBar; if (fIsStandalone) wflags |= ComponentPeer::windowAppearsOnTaskbar; return wflags; } #ifndef CARLA_OS_LINUX void handleCommandMessage(const int comamndId) override { CARLA_SAFE_ASSERT_RETURN(comamndId == 0,); if (fShown) { fShown = false; setAlwaysOnTop(false); } } #endif private: const bool fIsStandalone; volatile bool fClosed; bool fShown; const uintptr_t fTransientId; int fLastKeyIndex, fLastKeyValue; ModifierKeys fLastModifiers; AEffect* const fVstEffect; void setTransient() { if (fTransientId == 0) return; #if defined(CARLA_OS_LINUX) && defined(HAVE_X11) Display* const display = XWindowSystem::getInstance()->getDisplay(); CARLA_SAFE_ASSERT_RETURN(display != nullptr,); ::Window window = (::Window)getWindowHandle(); CARLA_SAFE_ASSERT_RETURN(window != 0,); XSetTransientForHint(display, window, static_cast<::Window>(fTransientId)); #endif #ifdef CARLA_OS_MAC NSView* const view = (NSView*)getWindowHandle(); CARLA_SAFE_ASSERT_RETURN(view != nullptr,); NSWindow* const window = [view window]; CARLA_SAFE_ASSERT_RETURN(window != nullptr,); NSWindow* const parentWindow = [NSApp windowWithWindowNumber:fTransientId]; CARLA_SAFE_ASSERT_RETURN(parentWindow != nullptr,); [parentWindow addChildWindow:window ordered:NSWindowAbove]; #endif #ifdef CARLA_OS_WIN const HWND window = (HWND)getWindowHandle(); CARLA_SAFE_ASSERT_RETURN(window != nullptr,); SetWindowLongPtr(window, GWLP_HWNDPARENT, static_cast(fTransientId)); #endif } CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(JucePluginWindow) }; } // namespace juce using juce::JucePluginWindow; // ----------------------------------------------------------------------- #endif // JUCE_PLUGIN_WINDOW_HPP_INCLUDED