Signed-off-by: falkTX <falktx@falktx.com>pull/312/head
@@ -43,26 +43,7 @@ START_NAMESPACE_DISTRHO | |||||
*/ | */ | ||||
class ExternalWindow | class ExternalWindow | ||||
{ | { | ||||
struct PrivateData { | |||||
uintptr_t parentWindowHandle; | |||||
uintptr_t transientWinId; | |||||
uint width; | |||||
uint height; | |||||
double scaleFactor; | |||||
String title; | |||||
bool visible; | |||||
pid_t pid; | |||||
PrivateData() | |||||
: parentWindowHandle(0), | |||||
transientWinId(0), | |||||
width(1), | |||||
height(1), | |||||
scaleFactor(1.0), | |||||
title(), | |||||
visible(false), | |||||
pid(0) {} | |||||
} pData; | |||||
struct PrivateData; | |||||
public: | public: | ||||
/** | /** | ||||
@@ -82,9 +63,7 @@ public: | |||||
*/ | */ | ||||
virtual ~ExternalWindow() | virtual ~ExternalWindow() | ||||
{ | { | ||||
/* | |||||
terminateAndWaitForProcess(); | |||||
*/ | |||||
DISTRHO_SAFE_ASSERT(!pData.visible); | |||||
} | } | ||||
/* -------------------------------------------------------------------------------------------------------- | /* -------------------------------------------------------------------------------------------------------- | ||||
@@ -92,11 +71,17 @@ public: | |||||
virtual bool isRunning() const | virtual bool isRunning() const | ||||
{ | { | ||||
if (ext.inUse) | |||||
return ext.isRunning(); | |||||
return isVisible(); | return isVisible(); | ||||
} | } | ||||
virtual bool isQuiting() const | virtual bool isQuiting() const | ||||
{ | { | ||||
if (ext.inUse) | |||||
return ext.isQuiting; | |||||
return !isVisible(); | return !isVisible(); | ||||
} | } | ||||
@@ -113,6 +98,14 @@ public: | |||||
transientWindowChanged(winId); | transientWindowChanged(winId); | ||||
} | } | ||||
void close() | |||||
{ | |||||
hide(); | |||||
if (ext.inUse) | |||||
terminateAndWaitForExternalProcess(); | |||||
} | |||||
#if DISTRHO_PLUGIN_HAS_EMBED_UI | #if DISTRHO_PLUGIN_HAS_EMBED_UI | ||||
/** | /** | ||||
Whether this Window is embed into another (usually not DGL-controlled) Window. | Whether this Window is embed into another (usually not DGL-controlled) Window. | ||||
@@ -280,6 +273,25 @@ public: | |||||
virtual void focus() {} | virtual void focus() {} | ||||
protected: | protected: | ||||
/* -------------------------------------------------------------------------------------------------------- | |||||
* ExternalWindow special calls for running externals tools */ | |||||
bool startExternalProcess(const char* args[]) | |||||
{ | |||||
ext.inUse = true; | |||||
return ext.start(args); | |||||
} | |||||
void terminateAndWaitForExternalProcess() | |||||
{ | |||||
ext.isQuiting = true; | |||||
ext.terminateAndWait(); | |||||
} | |||||
/* -------------------------------------------------------------------------------------------------------- | |||||
* ExternalWindow specific callbacks */ | |||||
/** | /** | ||||
A function called when the window is resized. | A function called when the window is resized. | ||||
*/ | */ | ||||
@@ -309,101 +321,125 @@ protected: | |||||
return; (void)winId; | return; (void)winId; | ||||
} | } | ||||
/* | |||||
bool isRunning() noexcept | |||||
{ | |||||
if (pid <= 0) | |||||
return false; | |||||
private: | |||||
friend class PluginWindow; | |||||
friend class UI; | |||||
const pid_t p = ::waitpid(pid, nullptr, WNOHANG); | |||||
struct ExternalProcess { | |||||
bool inUse; | |||||
bool isQuiting; | |||||
mutable pid_t pid; | |||||
if (p == pid || (p == -1 && errno == ECHILD)) | |||||
{ | |||||
printf("NOTICE: Child process exited while idle\n"); | |||||
pid = 0; | |||||
return false; | |||||
} | |||||
ExternalProcess() | |||||
: inUse(false), | |||||
isQuiting(false), | |||||
pid(0) {} | |||||
return true; | |||||
} | |||||
bool isRunning() const noexcept | |||||
{ | |||||
if (pid <= 0) | |||||
return false; | |||||
*/ | |||||
const pid_t p = ::waitpid(pid, nullptr, WNOHANG); | |||||
protected: | |||||
/* | |||||
bool startExternalProcess(const char* args[]) | |||||
{ | |||||
terminateAndWaitForProcess(); | |||||
if (p == pid || (p == -1 && errno == ECHILD)) | |||||
{ | |||||
d_stdout("NOTICE: Child process exited while idle"); | |||||
pid = 0; | |||||
return false; | |||||
} | |||||
pid = vfork(); | |||||
return true; | |||||
} | |||||
switch (pid) | |||||
bool start(const char* args[]) | |||||
{ | { | ||||
case 0: | |||||
execvp(args[0], (char**)args); | |||||
_exit(1); | |||||
return false; | |||||
case -1: | |||||
printf("Could not start external ui\n"); | |||||
return false; | |||||
terminateAndWait(); | |||||
default: | |||||
return true; | |||||
} | |||||
} | |||||
pid = vfork(); | |||||
void terminateAndWaitForProcess() | |||||
{ | |||||
if (pid <= 0) | |||||
return; | |||||
switch (pid) | |||||
{ | |||||
case 0: | |||||
execvp(args[0], (char**)args); | |||||
_exit(1); | |||||
return false; | |||||
printf("Waiting for previous process to stop,,,\n"); | |||||
case -1: | |||||
d_stderr("Could not start external ui"); | |||||
return false; | |||||
bool sendTerm = true; | |||||
default: | |||||
return true; | |||||
} | |||||
} | |||||
for (pid_t p;;) | |||||
void terminateAndWait() | |||||
{ | { | ||||
p = ::waitpid(pid, nullptr, WNOHANG); | |||||
if (pid <= 0) | |||||
return; | |||||
d_stdout("Waiting for external process to stop,,,"); | |||||
bool sendTerm = true; | |||||
switch (p) | |||||
for (pid_t p;;) | |||||
{ | { | ||||
case 0: | |||||
if (sendTerm) | |||||
{ | |||||
sendTerm = false; | |||||
::kill(pid, SIGTERM); | |||||
} | |||||
break; | |||||
p = ::waitpid(pid, nullptr, WNOHANG); | |||||
case -1: | |||||
if (errno == ECHILD) | |||||
switch (p) | |||||
{ | { | ||||
printf("Done! (no such process)\n"); | |||||
pid = 0; | |||||
return; | |||||
case 0: | |||||
if (sendTerm) | |||||
{ | |||||
sendTerm = false; | |||||
::kill(pid, SIGTERM); | |||||
} | |||||
break; | |||||
case -1: | |||||
if (errno == ECHILD) | |||||
{ | |||||
d_stdout("Done! (no such process)"); | |||||
pid = 0; | |||||
return; | |||||
} | |||||
break; | |||||
default: | |||||
if (p == pid) | |||||
{ | |||||
d_stdout("Done! (clean wait)"); | |||||
pid = 0; | |||||
return; | |||||
} | |||||
break; | |||||
} | } | ||||
break; | |||||
default: | |||||
if (p == pid) | |||||
{ | |||||
printf("Done! (clean wait)\n"); | |||||
pid = 0; | |||||
return; | |||||
} | |||||
break; | |||||
// 5 msec | |||||
usleep(5*1000); | |||||
} | } | ||||
// 5 msec | |||||
usleep(5*1000); | |||||
} | } | ||||
} | |||||
*/ | |||||
} ext; | |||||
private: | |||||
friend class PluginWindow; | |||||
friend class UI; | |||||
struct PrivateData { | |||||
uintptr_t parentWindowHandle; | |||||
uintptr_t transientWinId; | |||||
uint width; | |||||
uint height; | |||||
double scaleFactor; | |||||
String title; | |||||
bool visible; | |||||
PrivateData() | |||||
: parentWindowHandle(0), | |||||
transientWinId(0), | |||||
width(1), | |||||
height(1), | |||||
scaleFactor(1.0), | |||||
title(), | |||||
visible(false) {} | |||||
} pData; | |||||
DISTRHO_DECLARE_NON_COPYABLE(ExternalWindow) | DISTRHO_DECLARE_NON_COPYABLE(ExternalWindow) | ||||
}; | }; | ||||
@@ -84,6 +84,9 @@ struct PluginApplication | |||||
d_msleep(30); | d_msleep(30); | ||||
idleCallback->idleCallback(); | idleCallback->idleCallback(); | ||||
} | } | ||||
if (! ui->isQuiting()) | |||||
ui->close(); | |||||
} | } | ||||
// these are not needed | // these are not needed | ||||
@@ -136,18 +139,13 @@ public: | |||||
uintptr_t getNativeWindowHandle() const noexcept { return ui->pData.parentWindowHandle; } | uintptr_t getNativeWindowHandle() const noexcept { return ui->pData.parentWindowHandle; } | ||||
// direct mappings | // direct mappings | ||||
bool isVisible() const noexcept { return ui->isVisible(); } | |||||
void close() { ui->close(); } | |||||
void focus() { ui->focus(); } | void focus() { ui->focus(); } | ||||
void show() { ui->show(); } | void show() { ui->show(); } | ||||
bool isVisible() const noexcept { return ui->isVisible(); } | |||||
void setTitle(const char* const title) { ui->setTitle(title); } | void setTitle(const char* const title) { ui->setTitle(title); } | ||||
void setVisible(const bool visible) { ui->setVisible(visible); } | void setVisible(const bool visible) { ui->setVisible(visible); } | ||||
// custom | |||||
void close() | |||||
{ | |||||
ui->hide(); | |||||
} | |||||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginWindow) | DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginWindow) | ||||
}; | }; | ||||
#else // DISTRHO_PLUGIN_HAS_EXTERNAL_UI | #else // DISTRHO_PLUGIN_HAS_EXTERNAL_UI | ||||
@@ -1,6 +1,6 @@ | |||||
/* | /* | ||||
* DISTRHO Plugin Framework (DPF) | * DISTRHO Plugin Framework (DPF) | ||||
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com> | |||||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||||
* | * | ||||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | * Permission to use, copy, modify, and/or distribute this software for any purpose with | ||||
* or without fee is hereby granted, provided that the above copyright notice and this | * or without fee is hereby granted, provided that the above copyright notice and this | ||||
@@ -14,6 +14,9 @@ | |||||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||
*/ | */ | ||||
// needed for IDE | |||||
#include "DistrhoPluginInfo.h" | |||||
#include "DistrhoUI.hpp" | #include "DistrhoUI.hpp" | ||||
// Extra includes for current path and fifo stuff | // Extra includes for current path and fifo stuff | ||||
@@ -46,13 +49,14 @@ static bool fileExists(const char* const filename) | |||||
static ssize_t | static ssize_t | ||||
writeRetry(int fd, const void* src, size_t size) | writeRetry(int fd, const void* src, size_t size) | ||||
{ | { | ||||
ssize_t error; | |||||
ssize_t error; | |||||
int attempts = 0; | |||||
do { | |||||
error = write(fd, src, size); | |||||
} while (error == -1 && (errno == EINTR || errno == EPIPE)); | |||||
do { | |||||
error = write(fd, src, size); | |||||
} while (error == -1 && (errno == EINTR || errno == EPIPE) && ++attempts < 5); | |||||
return error; | |||||
return error; | |||||
} | } | ||||
// ----------------------------------------------------------------------------------------------------------- | // ----------------------------------------------------------------------------------------------------------- | ||||
@@ -72,7 +76,7 @@ public: | |||||
fExternalScript.truncate(fExternalScript.rfind('/')); | fExternalScript.truncate(fExternalScript.rfind('/')); | ||||
} | } | ||||
fExternalScript += "/d_extui.sh"; | |||||
fExternalScript += "/ExternalLauncher.sh"; | |||||
d_stdout("External script = %s", fExternalScript.buffer()); | d_stdout("External script = %s", fExternalScript.buffer()); | ||||
} | } | ||||
@@ -105,6 +109,17 @@ protected: | |||||
/* -------------------------------------------------------------------------------------------------------- | /* -------------------------------------------------------------------------------------------------------- | ||||
* External Window overrides */ | * External Window overrides */ | ||||
/** | |||||
Keep-alive. | |||||
*/ | |||||
void uiIdle() override | |||||
{ | |||||
if (fFifo == -1) | |||||
return; | |||||
writeRetry(fFifo, "idle\n", 5); | |||||
} | |||||
/** | /** | ||||
Manage external process and IPC when UI is requested to be visible. | Manage external process and IPC when UI is requested to be visible. | ||||
*/ | */ | ||||
@@ -119,7 +134,7 @@ protected: | |||||
char winIdStr[24]; | char winIdStr[24]; | ||||
std::memset(winIdStr, 0, sizeof(winIdStr)); | std::memset(winIdStr, 0, sizeof(winIdStr)); | ||||
std::snprintf(winIdStr, 23, "%lu", getTransientWinId()); | |||||
std::snprintf(winIdStr, 23, "%lu", getTransientWindowId()); | |||||
const char* args[] = { | const char* args[] = { | ||||
fExternalScript.buffer(), | fExternalScript.buffer(), | ||||
@@ -145,12 +160,12 @@ protected: | |||||
DISTRHO_SAFE_ASSERT(writeRetry(fFifo, "quit\n", 5) == 5); | DISTRHO_SAFE_ASSERT(writeRetry(fFifo, "quit\n", 5) == 5); | ||||
fsync(fFifo); | fsync(fFifo); | ||||
} | } | ||||
close(fFifo); | |||||
::close(fFifo); | |||||
fFifo = -1; | fFifo = -1; | ||||
} | } | ||||
unlink(kFifoFilename); | unlink(kFifoFilename); | ||||
terminateAndWaitForProcess(); | |||||
terminateAndWaitForExternalProcess(); | |||||
} | } | ||||
UI::setVisible(yesNo); | UI::setVisible(yesNo); | ||||
@@ -20,13 +20,21 @@ fi | |||||
# Setup cancellation point for this script | # Setup cancellation point for this script | ||||
quitfn() { | quitfn() { | ||||
qdbus ${dbusRef} close 2>/dev/null | qdbus ${dbusRef} close 2>/dev/null | ||||
exit 0 | |||||
} | } | ||||
trap quitfn SIGINT | trap quitfn SIGINT | ||||
trap quitfn SIGTERM | trap quitfn SIGTERM | ||||
# Read Fifo for new values or a quit message | # Read Fifo for new values or a quit message | ||||
while read line <"${FIFO}"; do | |||||
while read -t 5 line < "${FIFO}"; do | |||||
if [ $? != 0 ]; then | |||||
echo "Timed out, closing" | |||||
break | |||||
fi | |||||
if echo "${line}" | grep -q "idle"; then | |||||
continue | |||||
fi | |||||
if echo "${line}" | grep -q "quit"; then | if echo "${line}" | grep -q "quit"; then | ||||
break | break | ||||
fi | fi | ||||