DISTRHO Plugin Framework
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

229 lines
7.0KB

  1. /*
  2. * DISTRHO Plugin Framework (DPF)
  3. * Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
  4. *
  5. * Permission to use, copy, modify, and/or distribute this software for any purpose with
  6. * or without fee is hereby granted, provided that the above copyright notice and this
  7. * permission notice appear in all copies.
  8. *
  9. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
  10. * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
  11. * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
  12. * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
  13. * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
  14. * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15. */
  16. // needed for IDE
  17. #include "DistrhoPluginInfo.h"
  18. #include "DistrhoUI.hpp"
  19. #if defined(DISTRHO_OS_MAC)
  20. #elif defined(DISTRHO_OS_WINDOWS)
  21. #else
  22. # include <sys/types.h>
  23. # include <X11/Xatom.h>
  24. # include <X11/Xlib.h>
  25. # include <X11/Xutil.h>
  26. # define X11Key_Escape 9
  27. #endif
  28. START_NAMESPACE_DISTRHO
  29. // -----------------------------------------------------------------------------------------------------------
  30. class EmbedExternalExampleUI : public UI
  31. {
  32. #if defined(DISTRHO_OS_MAC)
  33. #elif defined(DISTRHO_OS_WINDOWS)
  34. #else
  35. ::Display* fDisplay;
  36. ::Window fWindow;
  37. #endif
  38. public:
  39. EmbedExternalExampleUI()
  40. : UI(512, 256),
  41. fDisplay(nullptr),
  42. fWindow(0),
  43. fValue(0.0f)
  44. {
  45. #if defined(DISTRHO_OS_MAC)
  46. #elif defined(DISTRHO_OS_WINDOWS)
  47. #else
  48. fDisplay = XOpenDisplay(nullptr);
  49. DISTRHO_SAFE_ASSERT_RETURN(fDisplay != nullptr,);
  50. const int screen = DefaultScreen(fDisplay);
  51. const ::Window root = RootWindow(fDisplay, screen);
  52. const ::Window parent = isEmbed() ? (::Window)getParentWindowHandle() : root;
  53. XSetWindowAttributes attr = {};
  54. attr.event_mask = KeyPressMask|KeyReleaseMask;
  55. fWindow = XCreateWindow(fDisplay, parent,
  56. 0, 0, getWidth(), getHeight(),
  57. 0, DefaultDepth(fDisplay, screen), InputOutput, DefaultVisual(fDisplay, screen),
  58. CWColormap | CWEventMask, &attr);
  59. DISTRHO_SAFE_ASSERT_RETURN(fWindow != 0,);
  60. XSizeHints sizeHints = {};
  61. sizeHints.flags = PMinSize;
  62. sizeHints.min_width = getWidth();
  63. sizeHints.min_height = getHeight();
  64. XSetNormalHints(fDisplay, fWindow, &sizeHints);
  65. XStoreName(fDisplay, fWindow, getTitle());
  66. if (parent == root)
  67. {
  68. // grab Esc key for auto-close
  69. XGrabKey(fDisplay, X11Key_Escape, AnyModifier, fWindow, 1, GrabModeAsync, GrabModeAsync);
  70. // destroy window on close
  71. Atom wmDelete = XInternAtom(fDisplay, "WM_DELETE_WINDOW", True);
  72. XSetWMProtocols(fDisplay, fWindow, &wmDelete, 1);
  73. // set pid WM hint
  74. const pid_t pid = getpid();
  75. const Atom _nwp = XInternAtom(fDisplay, "_NET_WM_PID", False);
  76. XChangeProperty(fDisplay, fWindow, _nwp, XA_CARDINAL, 32, PropModeReplace, (const uchar*)&pid, 1);
  77. // set the window to both dialog and normal to produce a decorated floating dialog
  78. // order is important: DIALOG needs to come before NORMAL
  79. const Atom _wt = XInternAtom(fDisplay, "_NET_WM_WINDOW_TYPE", False);
  80. const Atom _wts[2] = {
  81. XInternAtom(fDisplay, "_NET_WM_WINDOW_TYPE_DIALOG", False),
  82. XInternAtom(fDisplay, "_NET_WM_WINDOW_TYPE_NORMAL", False)
  83. };
  84. XChangeProperty(fDisplay, fWindow, _wt, XA_ATOM, 32, PropModeReplace, (const uchar*)&_wts, 2);
  85. }
  86. #endif
  87. }
  88. ~EmbedExternalExampleUI()
  89. {
  90. if (fDisplay == nullptr)
  91. return;
  92. if (fWindow != 0)
  93. XDestroyWindow(fDisplay, fWindow);
  94. XCloseDisplay(fDisplay);
  95. }
  96. protected:
  97. /* --------------------------------------------------------------------------------------------------------
  98. * DSP/Plugin Callbacks */
  99. /**
  100. A parameter has changed on the plugin side.
  101. This is called by the host to inform the UI about parameter changes.
  102. */
  103. void parameterChanged(uint32_t index, float value) override
  104. {
  105. if (index != 0)
  106. return;
  107. fValue = value;
  108. }
  109. /* --------------------------------------------------------------------------------------------------------
  110. * External Window overrides */
  111. void titleChanged(const char* const title) override
  112. {
  113. d_stdout("visibilityChanged %s", title);
  114. #if defined(DISTRHO_OS_MAC)
  115. #elif defined(DISTRHO_OS_WINDOWS)
  116. #else
  117. DISTRHO_SAFE_ASSERT_RETURN(fWindow != 0,);
  118. XStoreName(fDisplay, fWindow, title);
  119. #endif
  120. }
  121. void transientWindowChanged(const uintptr_t winId) override
  122. {
  123. d_stdout("transientWindowChanged %lu", winId);
  124. #if defined(DISTRHO_OS_MAC)
  125. #elif defined(DISTRHO_OS_WINDOWS)
  126. #else
  127. DISTRHO_SAFE_ASSERT_RETURN(fWindow != 0,);
  128. XSetTransientForHint(fDisplay, fWindow, (::Window)winId);
  129. #endif
  130. }
  131. void visibilityChanged(const bool visible) override
  132. {
  133. d_stdout("visibilityChanged %d", visible);
  134. #if defined(DISTRHO_OS_MAC)
  135. #elif defined(DISTRHO_OS_WINDOWS)
  136. #else
  137. DISTRHO_SAFE_ASSERT_RETURN(fWindow != 0,);
  138. if (visible)
  139. XMapRaised(fDisplay, fWindow);
  140. else
  141. XUnmapWindow(fDisplay, fWindow);
  142. #endif
  143. }
  144. void uiIdle() override
  145. {
  146. // d_stdout("uiIdle");
  147. #if defined(DISTRHO_OS_MAC)
  148. #elif defined(DISTRHO_OS_WINDOWS)
  149. #else
  150. if (fDisplay == nullptr)
  151. return;
  152. for (XEvent event; XPending(fDisplay) > 0;)
  153. {
  154. XNextEvent(fDisplay, &event);
  155. if (! isVisible())
  156. continue;
  157. switch (event.type)
  158. {
  159. case ClientMessage:
  160. if (char* const type = XGetAtomName(fDisplay, event.xclient.message_type))
  161. {
  162. if (std::strcmp(type, "WM_PROTOCOLS") == 0)
  163. hide();
  164. }
  165. break;
  166. case KeyRelease:
  167. if (event.xkey.keycode == X11Key_Escape)
  168. hide();
  169. break;
  170. }
  171. }
  172. #endif
  173. }
  174. // -------------------------------------------------------------------------------------------------------
  175. private:
  176. // Current value, cached for when UI becomes visible
  177. float fValue;
  178. /**
  179. Set our UI class as non-copyable and add a leak detector just in case.
  180. */
  181. DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(EmbedExternalExampleUI)
  182. };
  183. /* ------------------------------------------------------------------------------------------------------------
  184. * UI entry point, called by DPF to create a new UI instance. */
  185. UI* createUI()
  186. {
  187. return new EmbedExternalExampleUI();
  188. }
  189. // -----------------------------------------------------------------------------------------------------------
  190. END_NAMESPACE_DISTRHO