diff --git a/Makefile b/Makefile index 855e75f8a..29ac75658 100644 --- a/Makefile +++ b/Makefile @@ -725,7 +725,6 @@ endif ifeq ($(LINUX),true) ifeq ($(HAVE_X11),true) -ifeq ($(DEFAULT_QT),4) # Install vst plugin install -d $(DESTDIR)$(LIBDIR)/vst/carla.vst @@ -748,7 +747,6 @@ ifeq ($(DEFAULT_QT),4) rm -rf $(DESTDIR)$(LIBDIR)/vst/carla.vst/styles $(LINK) $(LIBDIR)/carla/styles $(DESTDIR)$(LIBDIR)/vst/carla.vst/styles endif -endif endif # -------------------------------------------------------------------------------------------------------------------- @@ -814,15 +812,11 @@ else @echo "LV2 plugin: $(ANS_NO) $(mZ)Not available for Windows$(mE)" endif ifeq ($(LINUX),true) -ifeq ($(DEFAULT_QT),4) ifeq ($(HAVE_X11),true) @echo "VST plugin: $(ANS_YES)" else # HAVE_X11 @echo "VST plugin: $(ANS_NO) $(mS)X11 missing$(mE)" endif -else # DEFAULT_QT - @echo "VST plugin: $(ANS_NO) $(mZ)Qt4 only$(mE)" -endif else # LINUX @echo "VST plugin: $(ANS_NO) $(mZ)Linux only$(mE)" endif diff --git a/source/backend/CarlaUtils.cpp b/source/backend/CarlaUtils.cpp index 7778b56f8..cd48c0a3a 100644 --- a/source/backend/CarlaUtils.cpp +++ b/source/backend/CarlaUtils.cpp @@ -33,6 +33,10 @@ # include "juce_audio_processors/juce_audio_processors.h" #endif +#ifdef HAVE_X11 +# include +#endif + #include "../native-plugins/_data.cpp" namespace CB = CarlaBackend; @@ -821,6 +825,61 @@ const char* carla_get_library_folder() // ------------------------------------------------------------------------------------------------------------------- +void carla_x11_reparent_window(uintptr_t winId1, uintptr_t winId2) +{ + carla_debug("carla_x11_reparent_window()"); + +#ifdef HAVE_X11 + if (::Display* const disp = XOpenDisplay(nullptr)) + { + XReparentWindow(disp, winId1, winId2, 0, 0); + XMapWindow(disp, winId1); + XCloseDisplay(disp); + } +#endif +} + +void carla_x11_move_window(uintptr_t winId, int x, int y) +{ +#ifdef HAVE_X11 + if (::Display* const disp = XOpenDisplay(nullptr)) + { + XMoveWindow(disp, winId, x, y); + XCloseDisplay(disp); + } +#endif +} + +int* carla_x11_get_window_pos(uintptr_t winId) +{ + carla_debug("carla_x11_get_window_pos()"); + + static int pos[2]; + +#ifdef HAVE_X11 + if (::Display* const disp = XOpenDisplay(nullptr)) + { + int x, y; + Window child; + XWindowAttributes xwa; + XTranslateCoordinates(disp, winId, XRootWindow(disp, 0), 0, 0, &x, &y, &child); + XGetWindowAttributes(disp, winId, &xwa); + XCloseDisplay(disp); + pos[0] = x - xwa.x; + pos[1] = y - xwa.y; + } + else +#endif + { + pos[0] = 0; + pos[1] = 0; + } + + return pos; +} + +// ------------------------------------------------------------------------------------------------------------------- + #include "CarlaPipeUtils.cpp" // ------------------------------------------------------------------------------------------------------------------- diff --git a/source/backend/CarlaUtils.h b/source/backend/CarlaUtils.h index 5c0f29665..4ee4ede18 100644 --- a/source/backend/CarlaUtils.h +++ b/source/backend/CarlaUtils.h @@ -234,6 +234,15 @@ CARLA_EXPORT const char* carla_get_library_filename(); */ CARLA_EXPORT const char* carla_get_library_folder(); +// ------------------------------------------------------------------------------------------------------------------- +// TESTING + +CARLA_EXPORT void carla_x11_reparent_window(uintptr_t winId1, uintptr_t winId2); + +CARLA_EXPORT void carla_x11_move_window(uintptr_t winId, int x, int y); + +CARLA_EXPORT int* carla_x11_get_window_pos(uintptr_t winId); + // ------------------------------------------------------------------------------------------------------------------- /** @} */ diff --git a/source/backend/Makefile b/source/backend/Makefile index ada8fd1ca..8bac9e24a 100644 --- a/source/backend/Makefile +++ b/source/backend/Makefile @@ -116,6 +116,10 @@ UTILS_LINK_FLAGS += $(JUCE_AUDIO_FORMATS_LIBS) UTILS_LINK_FLAGS += $(JUCE_CORE_LIBS) UTILS_LINK_FLAGS += $(LILV_LIBS) +ifeq ($(HAVE_X11),true) +UTILS_LINK_FLAGS += $(X11_LIBS) +endif + ifeq ($(MACOS),true) UTILS_LINK_FLAGS += $(JUCE_AUDIO_PROCESSORS_LIBS) UTILS_LINK_FLAGS += $(JUCE_DATA_STRUCTURES_LIBS) diff --git a/source/carla_utils.py b/source/carla_utils.py index 43053c3a0..0b5a9e1fc 100644 --- a/source/carla_utils.py +++ b/source/carla_utils.py @@ -215,6 +215,15 @@ class CarlaUtils(object): self.lib.carla_pipe_client_destroy.argtypes = [CarlaPipeClientHandle] self.lib.carla_pipe_client_destroy.restype = None + self.lib.carla_x11_reparent_window.argtypes = [c_uintptr, c_uintptr] + self.lib.carla_x11_reparent_window.restype = None + + self.lib.carla_x11_move_window.argtypes = [c_uintptr, c_int, c_int] + self.lib.carla_x11_move_window.restype = None + + self.lib.carla_x11_get_window_pos.argtypes = [c_uintptr] + self.lib.carla_x11_get_window_pos.restype = POINTER(c_int) + # use _putenv on windows if not WINDOWS: self.msvcrt = None @@ -314,4 +323,14 @@ class CarlaUtils(object): def pipe_client_destroy(self, handle): self.lib.carla_pipe_client_destroy(handle) + def x11_reparent_window(self, winId1, winId2): + self.lib.carla_x11_reparent_window(winId1, winId2) + + def x11_move_window(self, winId, x, y): + self.lib.carla_x11_move_window(winId, x, y) + + def x11_get_window_pos(self, winId): + data = self.lib.carla_x11_get_window_pos(winId) + return (int(data[0]), int(data[1])) + # ------------------------------------------------------------------------------------------------------------ diff --git a/source/native-plugins/resources/carla-plugin b/source/native-plugins/resources/carla-plugin index 46f89b22e..fad40d31d 100755 --- a/source/native-plugins/resources/carla-plugin +++ b/source/native-plugins/resources/carla-plugin @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Carla plugin host (plugin UI) -# Copyright (C) 2013-2014 Filipe Coelho +# 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 @@ -16,6 +16,20 @@ # # For a full copy of the GNU General Public License see the GPL.txt file +# ------------------------------------------------------------------------------------------------------------ +# Imports (Config) + +from carla_config import * + +# ------------------------------------------------------------------------------------------------------------ +# Imports (Global) + +if config_UseQt5: + from PyQt5.QtGui import QKeySequence + from PyQt5.QtWidgets import QHBoxLayout +else: + from PyQt4.QtGui import QHBoxLayout, QKeySequence + # ------------------------------------------------------------------------------------------------------------ # Imports (Custom Stuff) @@ -381,117 +395,160 @@ class CarlaMiniW(ExternalUI, HostWindow): print("unknown message: \"" + msg + "\"") # ------------------------------------------------------------------------------------------------------------ -# Embed plugin UI +# Embed Widget + +if LINUX and config_UseQt5: + from PyQt5.QtGui import QMouseEvent, QWindow -if LINUX and not config_UseQt5: - from PyQt4.QtGui import QHBoxLayout, QKeySequence, QX11EmbedWidget + class QEmbedWidget(QWidget): + def __init__(self): + QWidget.__init__(self) + self.setAttribute(Qt.WA_LayoutUsesWidgetRect) - class CarlaEmbedW(QX11EmbedWidget): - def __init__(self, host, winId): + self.fPos = (0, 0) + self.move(0, 0) + + self.fWinId = 0 + + def eventFilter(self, obj, ev): + if isinstance(ev, QMouseEvent): + pos = gCarla.utils.x11_get_window_pos(self.fWinId) + if self.fPos != pos: + self.fPos = pos + self.move(pos[0], pos[1]) + gCarla.utils.x11_move_window(self.fWinId, 0, 0) + return False + + def finalSetup(self, gui, winId): + self.fWinId = int(self.winId()) + gui.ui.menubar.installEventFilter(self) + gCarla.utils.x11_reparent_window(self.fWinId, winId) + self.show() + +elif LINUX and not config_UseQt5: + from PyQt4.QtGui import QX11EmbedWidget + + class QEmbedWidget(QX11EmbedWidget): + def __init__(self): QX11EmbedWidget.__init__(self) - self.host = host - self.fWinId = winId + def finalSetup(self, gui, winId): + self.embedInto(winId) + self.show() - self.fLayout = QVBoxLayout(self) - self.fLayout.setContentsMargins(0, 0, 0, 0) - self.fLayout.setSpacing(0) - self.setLayout(self.fLayout) +else: + class QEmbedWidget(object): + def __init__(self, winId, width, height): + print("Cannot use embed UI with this configuration") + raise Exception - self.gui = CarlaMiniW(host, self) - self.gui.hide() +# ------------------------------------------------------------------------------------------------------------ +# Embed plugin UI - self.gui.ui.act_file_quit.setEnabled(False) - self.gui.ui.act_file_quit.setVisible(False) +class CarlaEmbedW(QEmbedWidget): + def __init__(self, host, winId): + QEmbedWidget.__init__(self) + self.setFixedSize(740, 512) - self.fShortcutActions = [] - self.addShortcutActions(self.gui.ui.menu_File.actions()) - self.addShortcutActions(self.gui.ui.menu_Plugin.actions()) - self.addShortcutActions(self.gui.ui.menu_PluginMacros.actions()) - self.addShortcutActions(self.gui.ui.menu_Settings.actions()) - self.addShortcutActions(self.gui.ui.menu_Help.actions()) + self.host = host + self.fWinId = winId - if self.host.processMode == ENGINE_PROCESS_MODE_PATCHBAY: - self.addShortcutActions(self.gui.ui.menu_Canvas.actions()) - self.addShortcutActions(self.gui.ui.menu_Canvas_Zoom.actions()) + self.fLayout = QVBoxLayout(self) + self.fLayout.setContentsMargins(0, 0, 0, 0) + self.fLayout.setSpacing(0) + self.setLayout(self.fLayout) - self.addWidget(self.gui.ui.menubar) - self.addLine() - self.addWidget(self.gui.ui.toolBar) + self.gui = CarlaMiniW(host, self) + self.gui.hide() - if self.host.processMode == ENGINE_PROCESS_MODE_PATCHBAY: - self.addLine() + self.gui.ui.act_file_quit.setEnabled(False) + self.gui.ui.act_file_quit.setVisible(False) - self.addWidget(self.gui.centralWidget()) + self.fShortcutActions = [] + self.addShortcutActions(self.gui.ui.menu_File.actions()) + self.addShortcutActions(self.gui.ui.menu_Plugin.actions()) + self.addShortcutActions(self.gui.ui.menu_PluginMacros.actions()) + self.addShortcutActions(self.gui.ui.menu_Settings.actions()) + self.addShortcutActions(self.gui.ui.menu_Help.actions()) - self.setFixedSize(740, 512) - self.embedInto(winId) - self.show() + if self.host.processMode == ENGINE_PROCESS_MODE_PATCHBAY: + self.addShortcutActions(self.gui.ui.menu_Canvas.actions()) + self.addShortcutActions(self.gui.ui.menu_Canvas_Zoom.actions()) - def addShortcutActions(self, actions): - for action in actions: - if not action.shortcut().isEmpty(): - self.fShortcutActions.append(action) - - def addWidget(self, widget): - widget.setParent(self) - self.fLayout.addWidget(widget) - - def addLine(self): - line = QFrame(self) - line.setFrameShadow(QFrame.Sunken) - line.setFrameShape(QFrame.HLine) - line.setLineWidth(0) - line.setMidLineWidth(1) - self.fLayout.addWidget(line) - - def keyPressEvent(self, event): - modifiers = event.modifiers() - modifiersStr = "" - - if modifiers & Qt.ShiftModifier: - modifiersStr += "Shift+" - if modifiers & Qt.ControlModifier: - modifiersStr += "Ctrl+" - if modifiers & Qt.AltModifier: - modifiersStr += "Alt+" - if modifiers & Qt.MetaModifier: - modifiersStr += "Meta+" - - keyStr = QKeySequence(event.key()).toString() - keySeq = QKeySequence(modifiersStr + keyStr) - - for action in self.fShortcutActions: - if not action.isEnabled(): - continue - if keySeq.matches(action.shortcut()) != QKeySequence.ExactMatch: - continue - event.accept() - action.trigger() - return + self.addWidget(self.gui.ui.menubar) + self.addLine() + self.addWidget(self.gui.ui.toolBar) + + if self.host.processMode == ENGINE_PROCESS_MODE_PATCHBAY: + self.addLine() + + self.addWidget(self.gui.centralWidget()) + self.finalSetup(self.gui, winId) + + def addShortcutActions(self, actions): + for action in actions: + if not action.shortcut().isEmpty(): + self.fShortcutActions.append(action) + + def addWidget(self, widget): + widget.setParent(self) + self.fLayout.addWidget(widget) + + def addLine(self): + line = QFrame(self) + line.setFrameShadow(QFrame.Sunken) + line.setFrameShape(QFrame.HLine) + line.setLineWidth(0) + line.setMidLineWidth(1) + self.fLayout.addWidget(line) + + def keyPressEvent(self, event): + modifiers = event.modifiers() + modifiersStr = "" + + if modifiers & Qt.ShiftModifier: + modifiersStr += "Shift+" + if modifiers & Qt.ControlModifier: + modifiersStr += "Ctrl+" + if modifiers & Qt.AltModifier: + modifiersStr += "Alt+" + if modifiers & Qt.MetaModifier: + modifiersStr += "Meta+" + + keyStr = QKeySequence(event.key()).toString() + keySeq = QKeySequence(modifiersStr + keyStr) + + for action in self.fShortcutActions: + if not action.isEnabled(): + continue + if keySeq.matches(action.shortcut()) != QKeySequence.ExactMatch: + continue + event.accept() + action.trigger() + return - QX11EmbedWidget.keyPressEvent(self, event) + QEmbedWidget.keyPressEvent(self, event) - def showEvent(self, event): - QX11EmbedWidget.showEvent(self, event) + def showEvent(self, event): + QEmbedWidget.showEvent(self, event) - # set our gui as parent for all plugins UIs - winIdStr = "%x" % self.fWinId - self.host.set_engine_option(ENGINE_OPTION_FRONTEND_WIN_ID, 0, winIdStr) + # set our gui as parent for all plugins UIs + winIdStr = "%x" % self.fWinId + self.host.set_engine_option(ENGINE_OPTION_FRONTEND_WIN_ID, 0, winIdStr) - def hideEvent(self, event): - # disable parent - self.host.set_engine_option(ENGINE_OPTION_FRONTEND_WIN_ID, 0, "0") + def hideEvent(self, event): + # disable parent + self.host.set_engine_option(ENGINE_OPTION_FRONTEND_WIN_ID, 0, "0") - QX11EmbedWidget.hideEvent(self, event) + QEmbedWidget.hideEvent(self, event) - def closeEvent(self, event): - self.gui.close() - self.gui.closeExternalUI() - QX11EmbedWidget.closeEvent(self, event) + def closeEvent(self, event): + self.gui.close() + self.gui.closeExternalUI() + QEmbedWidget.closeEvent(self, event) - # there might be other qt windows open which will block carla-plugin from quitting - app.quit() + # there might be other qt windows open which will block carla-plugin from quitting + app.quit() # ------------------------------------------------------------------------------------------------------------ # Main @@ -526,7 +583,7 @@ if __name__ == '__main__': gCarla.utils.setenv("CARLA_PLUGIN_EMBED_WINID", "0") - if LINUX and winId != 0 and not config_UseQt5: + if LINUX and winId != 0: gui = CarlaEmbedW(host, winId) else: gui = CarlaMiniW(host) diff --git a/source/plugin/Makefile b/source/plugin/Makefile index f10fac399..04d3ec182 100644 --- a/source/plugin/Makefile +++ b/source/plugin/Makefile @@ -104,7 +104,6 @@ TARGETS = \ ifeq ($(LINUX),true) ifeq ($(HAVE_X11),true) -ifeq ($(DEFAULT_QT),4) TARGETS += \ $(BINDIR)/CarlaRack$(LIB_EXT) \ $(BINDIR)/CarlaRackFX$(LIB_EXT) \ @@ -113,7 +112,6 @@ TARGETS += \ $(BINDIR)/CarlaPatchbayFX$(LIB_EXT) endif endif -endif # ----------------------------------------------------------------------------------------------------------------------------