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.

264 lines
7.3KB

  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. #define MPV_TEST
  20. // #define KDE_FIFO_TEST
  21. #ifdef KDE_FIFO_TEST
  22. // Extra includes for current path and fifo stuff
  23. #include <dlfcn.h>
  24. #include <fcntl.h>
  25. #include <sys/stat.h>
  26. #include <sys/types.h>
  27. #endif
  28. START_NAMESPACE_DISTRHO
  29. #ifdef KDE_FIFO_TEST
  30. // TODO: generate a random, not-yet-existing, filename
  31. const char* const kFifoFilename = "/tmp/dpf-fifo-test";
  32. // Helper to get current path of this plugin
  33. static const char* getCurrentPluginFilename()
  34. {
  35. Dl_info exeInfo;
  36. void* localSymbol = (void*)kFifoFilename;
  37. dladdr(localSymbol, &exeInfo);
  38. return exeInfo.dli_fname;
  39. }
  40. // Helper to check if a file exists
  41. static bool fileExists(const char* const filename)
  42. {
  43. return access(filename, F_OK) != -1;
  44. }
  45. // Helper function to keep trying to write until it succeeds or really errors out
  46. static ssize_t
  47. writeRetry(int fd, const void* src, size_t size)
  48. {
  49. ssize_t error;
  50. int attempts = 0;
  51. do {
  52. error = write(fd, src, size);
  53. } while (error == -1 && (errno == EINTR || errno == EPIPE) && ++attempts < 5);
  54. return error;
  55. }
  56. #endif
  57. // -----------------------------------------------------------------------------------------------------------
  58. class ExternalExampleUI : public UI
  59. {
  60. public:
  61. ExternalExampleUI()
  62. : UI(405, 256),
  63. #ifdef KDE_FIFO_TEST
  64. fFifo(-1),
  65. fExternalScript(getNextBundlePath()),
  66. #endif
  67. fValue(0.0f)
  68. {
  69. #ifdef KDE_FIFO_TEST
  70. if (fExternalScript.isEmpty())
  71. {
  72. fExternalScript = getCurrentPluginFilename();
  73. fExternalScript.truncate(fExternalScript.rfind('/'));
  74. }
  75. fExternalScript += "/ExternalLauncher.sh";
  76. d_stdout("External script = %s", fExternalScript.buffer());
  77. #endif
  78. if (isVisible() || isEmbed())
  79. visibilityChanged(true);
  80. }
  81. ~ExternalExampleUI()
  82. {
  83. if (isEmbed())
  84. terminateAndWaitForExternalProcess();
  85. }
  86. protected:
  87. /* --------------------------------------------------------------------------------------------------------
  88. * DSP/Plugin Callbacks */
  89. /**
  90. A parameter has changed on the plugin side.
  91. This is called by the host to inform the UI about parameter changes.
  92. */
  93. void parameterChanged(uint32_t index, float value) override
  94. {
  95. if (index != 0)
  96. return;
  97. fValue = value;
  98. #ifdef KDE_FIFO_TEST
  99. if (fFifo == -1)
  100. return;
  101. // NOTE: This is a terrible way to pass values, also locale might get in the way...
  102. char valueStr[24];
  103. std::memset(valueStr, 0, sizeof(valueStr));
  104. std::snprintf(valueStr, 23, "%i\n", static_cast<int>(value + 0.5f));
  105. DISTRHO_SAFE_ASSERT(writeRetry(fFifo, valueStr, 24) == sizeof(valueStr));
  106. #endif
  107. }
  108. /* --------------------------------------------------------------------------------------------------------
  109. * External Window overrides */
  110. /**
  111. Keep-alive.
  112. */
  113. void uiIdle() override
  114. {
  115. #ifdef KDE_FIFO_TEST
  116. if (fFifo == -1)
  117. return;
  118. writeRetry(fFifo, "idle\n", 5);
  119. #endif
  120. }
  121. /**
  122. Manage external process and IPC when UI is requested to be visible.
  123. */
  124. void visibilityChanged(const bool visible) override
  125. {
  126. #ifdef KDE_FIFO_TEST
  127. if (visible)
  128. {
  129. DISTRHO_SAFE_ASSERT_RETURN(fileExists(fExternalScript),);
  130. mkfifo(kFifoFilename, 0666);
  131. sync();
  132. char winIdStr[24];
  133. std::memset(winIdStr, 0, sizeof(winIdStr));
  134. std::snprintf(winIdStr, 23, "%lu", getTransientWindowId());
  135. const char* args[] = {
  136. fExternalScript.buffer(),
  137. kFifoFilename,
  138. "--progressbar", "External UI example",
  139. "--title", getTitle(),
  140. nullptr,
  141. };
  142. DISTRHO_SAFE_ASSERT_RETURN(startExternalProcess(args),);
  143. // NOTE: this can lockup the current thread if the other side does not read the file!
  144. fFifo = open(kFifoFilename, O_WRONLY);
  145. DISTRHO_SAFE_ASSERT_RETURN(fFifo != -1,);
  146. parameterChanged(0, fValue);
  147. }
  148. else
  149. {
  150. if (fFifo != -1)
  151. {
  152. if (isRunning())
  153. {
  154. DISTRHO_SAFE_ASSERT(writeRetry(fFifo, "quit\n", 5) == 5);
  155. fsync(fFifo);
  156. }
  157. ::close(fFifo);
  158. fFifo = -1;
  159. }
  160. unlink(kFifoFilename);
  161. terminateAndWaitForExternalProcess();
  162. }
  163. #endif
  164. #ifdef MPV_TEST
  165. if (visible)
  166. {
  167. const char* const file = "/home/falktx/Videos/HD/"; // TODO make this a state file?
  168. if (isEmbed())
  169. {
  170. char winIdStr[64];
  171. snprintf(winIdStr, sizeof(winIdStr), "--wid=%lu", getParentWindowHandle());
  172. const char* args[] = {
  173. "mpv",
  174. "--ao=jack",
  175. winIdStr,
  176. file,
  177. nullptr
  178. };
  179. unsetenv("LD_LIBRARY_PATH");
  180. startExternalProcess(args);
  181. }
  182. else
  183. {
  184. const char* args[] = {
  185. "mpv",
  186. "--ao=jack",
  187. file,
  188. nullptr
  189. };
  190. startExternalProcess(args);
  191. }
  192. }
  193. else
  194. {
  195. terminateAndWaitForExternalProcess();
  196. }
  197. #endif
  198. }
  199. // -------------------------------------------------------------------------------------------------------
  200. private:
  201. #ifdef KDE_FIFO_TEST
  202. // IPC Stuff
  203. int fFifo;
  204. // Path to external ui script
  205. String fExternalScript;
  206. #endif
  207. // Current value, cached for when UI becomes visible
  208. float fValue;
  209. /**
  210. Set our UI class as non-copyable and add a leak detector just in case.
  211. */
  212. DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ExternalExampleUI)
  213. };
  214. /* ------------------------------------------------------------------------------------------------------------
  215. * UI entry point, called by DPF to create a new UI instance. */
  216. UI* createUI()
  217. {
  218. return new ExternalExampleUI();
  219. }
  220. // -----------------------------------------------------------------------------------------------------------
  221. END_NAMESPACE_DISTRHO