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