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.

188 lines
5.5KB

  1. /*
  2. * DISTRHO Plugin Framework (DPF)
  3. * Copyright (C) 2012-2019 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. #include "DistrhoUI.hpp"
  17. // Extra includes for current path and fifo stuff
  18. #include <dlfcn.h>
  19. #include <fcntl.h>
  20. #include <sys/stat.h>
  21. #include <sys/types.h>
  22. START_NAMESPACE_DISTRHO
  23. // TODO: generate a random, not-yet-existing, filename
  24. const char* const kFifoFilename = "/tmp/dpf-fifo-test";
  25. // Helper to get current path of this plugin
  26. static const char* getCurrentPluginFilename()
  27. {
  28. Dl_info exeInfo;
  29. void* localSymbol = (void*)kFifoFilename;
  30. dladdr(localSymbol, &exeInfo);
  31. return exeInfo.dli_fname;
  32. }
  33. // Helper to check if a file exists
  34. static bool fileExists(const char* const filename)
  35. {
  36. return access(filename, F_OK) != -1;
  37. }
  38. // Helper function to keep trying to write until it succeeds or really errors out
  39. static ssize_t
  40. writeRetry(int fd, const void* src, size_t size)
  41. {
  42. ssize_t error;
  43. do {
  44. error = write(fd, src, size);
  45. } while (error == -1 && (errno == EINTR || errno == EPIPE));
  46. return error;
  47. }
  48. // -----------------------------------------------------------------------------------------------------------
  49. class ExternalExampleUI : public UI
  50. {
  51. public:
  52. ExternalExampleUI()
  53. : UI(405, 256),
  54. fFifo(-1),
  55. fValue(0.0f),
  56. fExternalScript(getNextBundlePath())
  57. {
  58. if (fExternalScript.isEmpty())
  59. {
  60. fExternalScript = getCurrentPluginFilename();
  61. fExternalScript.truncate(fExternalScript.rfind('/'));
  62. }
  63. fExternalScript += "/d_extui.sh";
  64. d_stdout("External script = %s", fExternalScript.buffer());
  65. }
  66. protected:
  67. /* --------------------------------------------------------------------------------------------------------
  68. * DSP/Plugin Callbacks */
  69. /**
  70. A parameter has changed on the plugin side.
  71. This is called by the host to inform the UI about parameter changes.
  72. */
  73. void parameterChanged(uint32_t index, float value) override
  74. {
  75. if (index != 0)
  76. return;
  77. fValue = value;
  78. if (fFifo == -1)
  79. return;
  80. // NOTE: This is a terrible way to pass values, also locale might get in the way...
  81. char valueStr[24];
  82. std::memset(valueStr, 0, sizeof(valueStr));
  83. std::snprintf(valueStr, 23, "%i\n", static_cast<int>(value + 0.5f));
  84. DISTRHO_SAFE_ASSERT(writeRetry(fFifo, valueStr, 24) == sizeof(valueStr));
  85. }
  86. /* --------------------------------------------------------------------------------------------------------
  87. * External Window overrides */
  88. /**
  89. Manage external process and IPC when UI is requested to be visible.
  90. */
  91. void setVisible(const bool yesNo) override
  92. {
  93. if (yesNo)
  94. {
  95. DISTRHO_SAFE_ASSERT_RETURN(fileExists(fExternalScript),);
  96. mkfifo(kFifoFilename, 0666);
  97. sync();
  98. char winIdStr[24];
  99. std::memset(winIdStr, 0, sizeof(winIdStr));
  100. std::snprintf(winIdStr, 23, "%lu", getTransientWinId());
  101. const char* args[] = {
  102. fExternalScript.buffer(),
  103. kFifoFilename,
  104. "--progressbar", "External UI example",
  105. "--title", getTitle(),
  106. nullptr,
  107. };
  108. DISTRHO_SAFE_ASSERT_RETURN(startExternalProcess(args),);
  109. // NOTE: this can lockup the current thread if the other side does not read the file!
  110. fFifo = open(kFifoFilename, O_WRONLY);
  111. DISTRHO_SAFE_ASSERT_RETURN(fFifo != -1,);
  112. parameterChanged(0, fValue);
  113. }
  114. else
  115. {
  116. if (fFifo != -1)
  117. {
  118. if (isRunning())
  119. {
  120. DISTRHO_SAFE_ASSERT(writeRetry(fFifo, "quit\n", 5) == 5);
  121. fsync(fFifo);
  122. }
  123. close(fFifo);
  124. fFifo = -1;
  125. }
  126. unlink(kFifoFilename);
  127. terminateAndWaitForProcess();
  128. }
  129. UI::setVisible(yesNo);
  130. }
  131. // -------------------------------------------------------------------------------------------------------
  132. private:
  133. // IPC Stuff
  134. int fFifo;
  135. // Current value, cached for when UI becomes visible
  136. float fValue;
  137. // Path to external ui script
  138. String fExternalScript;
  139. /**
  140. Set our UI class as non-copyable and add a leak detector just in case.
  141. */
  142. DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ExternalExampleUI)
  143. };
  144. /* ------------------------------------------------------------------------------------------------------------
  145. * UI entry point, called by DPF to create a new UI instance. */
  146. UI* createUI()
  147. {
  148. return new ExternalExampleUI();
  149. }
  150. // -----------------------------------------------------------------------------------------------------------
  151. END_NAMESPACE_DISTRHO