| @@ -118,8 +118,8 @@ class LADSPAPluginInstance : public AudioPluginInstance | |||||
| { | { | ||||
| public: | public: | ||||
| LADSPAPluginInstance (const LADSPAModuleHandle::Ptr& m) | LADSPAPluginInstance (const LADSPAModuleHandle::Ptr& m) | ||||
| : plugin (nullptr), handle (nullptr), initialised (false), | |||||
| tempBuffer (1, 1), module (m) | |||||
| : module (m), plugin (nullptr), handle (nullptr), | |||||
| initialised (false), tempBuffer (1, 1) | |||||
| { | { | ||||
| ++insideLADSPACallback; | ++insideLADSPACallback; | ||||
| @@ -79,10 +79,11 @@ JUCE_API bool JUCE_CALLTYPE Process::isRunningUnderDebugger() | |||||
| return juce_isRunningUnderDebugger(); | return juce_isRunningUnderDebugger(); | ||||
| } | } | ||||
| static void swapUserAndEffectiveUser() | |||||
| static bool swapUserAndEffectiveUser() | |||||
| { | { | ||||
| (void) setreuid (geteuid(), getuid()); | |||||
| (void) setregid (getegid(), getgid()); | |||||
| int result1 = setreuid (geteuid(), getuid()); | |||||
| int result2 = setregid (getegid(), getgid()); | |||||
| return result1 == 0 && result2 == 0; | |||||
| } | } | ||||
| JUCE_API void JUCE_CALLTYPE Process::raisePrivilege() { if (geteuid() != 0 && getuid() == 0) swapUserAndEffectiveUser(); } | JUCE_API void JUCE_CALLTYPE Process::raisePrivilege() { if (geteuid() != 0 && getuid() == 0) swapUserAndEffectiveUser(); } | ||||
| @@ -94,24 +94,20 @@ private: | |||||
| //============================================================================== | //============================================================================== | ||||
| Value::Value() | |||||
| : value (new SimpleValueSource()) | |||||
| Value::Value() : value (new SimpleValueSource()) | |||||
| { | { | ||||
| } | } | ||||
| Value::Value (ValueSource* const v) | |||||
| : value (v) | |||||
| Value::Value (ValueSource* const v) : value (v) | |||||
| { | { | ||||
| jassert (v != nullptr); | jassert (v != nullptr); | ||||
| } | } | ||||
| Value::Value (const var& initialValue) | |||||
| : value (new SimpleValueSource (initialValue)) | |||||
| Value::Value (const var& initialValue) : value (new SimpleValueSource (initialValue)) | |||||
| { | { | ||||
| } | } | ||||
| Value::Value (const Value& other) | |||||
| : value (other.value) | |||||
| Value::Value (const Value& other) : value (other.value) | |||||
| { | { | ||||
| } | } | ||||
| @@ -123,12 +119,22 @@ Value& Value::operator= (const Value& other) | |||||
| #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS | #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS | ||||
| Value::Value (Value&& other) noexcept | Value::Value (Value&& other) noexcept | ||||
| : value (static_cast<ReferenceCountedObjectPtr<ValueSource>&&> (other.value)) | |||||
| { | { | ||||
| // moving a Value with listeners will lose those listeners, which | |||||
| // probably isn't what you wanted to happen! | |||||
| jassert (other.listeners.size() == 0); | |||||
| other.removeFromListenerList(); | |||||
| value = static_cast<ReferenceCountedObjectPtr<ValueSource>&&> (other.value); | |||||
| } | } | ||||
| Value& Value::operator= (Value&& other) noexcept | Value& Value::operator= (Value&& other) noexcept | ||||
| { | { | ||||
| // moving a Value with listeners will lose those listeners, which | |||||
| // probably isn't what you wanted to happen! | |||||
| jassert (other.listeners.size() == 0); | |||||
| other.removeFromListenerList(); | |||||
| value = static_cast<ReferenceCountedObjectPtr<ValueSource>&&> (other.value); | value = static_cast<ReferenceCountedObjectPtr<ValueSource>&&> (other.value); | ||||
| return *this; | return *this; | ||||
| } | } | ||||
| @@ -136,7 +142,12 @@ Value& Value::operator= (Value&& other) noexcept | |||||
| Value::~Value() | Value::~Value() | ||||
| { | { | ||||
| if (listeners.size() > 0) | |||||
| removeFromListenerList(); | |||||
| } | |||||
| void Value::removeFromListenerList() | |||||
| { | |||||
| if (listeners.size() > 0 && value != nullptr) // may be nullptr after a move operation | |||||
| value->valuesWithListeners.removeValue (this); | value->valuesWithListeners.removeValue (this); | ||||
| } | } | ||||
| @@ -217,6 +217,7 @@ private: | |||||
| ListenerList<Listener> listeners; | ListenerList<Listener> listeners; | ||||
| void callListeners(); | void callListeners(); | ||||
| void removeFromListenerList(); | |||||
| // This is disallowed to avoid confusion about whether it should | // This is disallowed to avoid confusion about whether it should | ||||
| // do a by-value or by-reference copy. | // do a by-value or by-reference copy. | ||||
| @@ -27,7 +27,7 @@ enum { magicMastSlaveConnectionHeader = 0x712baf04 }; | |||||
| static const char* startMessage = "__ipc_st"; | static const char* startMessage = "__ipc_st"; | ||||
| static const char* killMessage = "__ipc_k_"; | static const char* killMessage = "__ipc_k_"; | ||||
| static const char* pingMessage = "__ipc_p_"; | static const char* pingMessage = "__ipc_p_"; | ||||
| enum { specialMessageSize = 8 }; | |||||
| enum { specialMessageSize = 8, defaultTimeoutMs = 8000 }; | |||||
| static String getCommandLinePrefix (const String& commandLineUniqueID) | static String getCommandLinePrefix (const String& commandLineUniqueID) | ||||
| { | { | ||||
| @@ -40,7 +40,7 @@ static String getCommandLinePrefix (const String& commandLineUniqueID) | |||||
| struct ChildProcessPingThread : public Thread, | struct ChildProcessPingThread : public Thread, | ||||
| private AsyncUpdater | private AsyncUpdater | ||||
| { | { | ||||
| ChildProcessPingThread() : Thread ("IPC ping"), timeoutMs (8000) | |||||
| ChildProcessPingThread (int timeout) : Thread ("IPC ping"), timeoutMs (timeout) | |||||
| { | { | ||||
| pingReceived(); | pingReceived(); | ||||
| } | } | ||||
| @@ -84,8 +84,10 @@ private: | |||||
| struct ChildProcessMaster::Connection : public InterprocessConnection, | struct ChildProcessMaster::Connection : public InterprocessConnection, | ||||
| private ChildProcessPingThread | private ChildProcessPingThread | ||||
| { | { | ||||
| Connection (ChildProcessMaster& m, const String& pipeName) | |||||
| : InterprocessConnection (false, magicMastSlaveConnectionHeader), owner (m) | |||||
| Connection (ChildProcessMaster& m, const String& pipeName, int timeout) | |||||
| : InterprocessConnection (false, magicMastSlaveConnectionHeader), | |||||
| ChildProcessPingThread (timeout), | |||||
| owner (m) | |||||
| { | { | ||||
| if (createPipe (pipeName, timeoutMs)) | if (createPipe (pipeName, timeoutMs)) | ||||
| startThread (4); | startThread (4); | ||||
| @@ -140,7 +142,7 @@ bool ChildProcessMaster::sendMessageToSlave (const MemoryBlock& mb) | |||||
| return false; | return false; | ||||
| } | } | ||||
| bool ChildProcessMaster::launchSlaveProcess (const File& executable, const String& commandLineUniqueID) | |||||
| bool ChildProcessMaster::launchSlaveProcess (const File& executable, const String& commandLineUniqueID, int timeoutMs) | |||||
| { | { | ||||
| connection = nullptr; | connection = nullptr; | ||||
| jassert (childProcess.kill()); | jassert (childProcess.kill()); | ||||
| @@ -153,7 +155,7 @@ bool ChildProcessMaster::launchSlaveProcess (const File& executable, const Strin | |||||
| if (childProcess.start (args)) | if (childProcess.start (args)) | ||||
| { | { | ||||
| connection = new Connection (*this, pipeName); | |||||
| connection = new Connection (*this, pipeName, timeoutMs <= 0 ? defaultTimeoutMs : timeoutMs); | |||||
| if (connection->isConnected()) | if (connection->isConnected()) | ||||
| { | { | ||||
| @@ -171,8 +173,10 @@ bool ChildProcessMaster::launchSlaveProcess (const File& executable, const Strin | |||||
| struct ChildProcessSlave::Connection : public InterprocessConnection, | struct ChildProcessSlave::Connection : public InterprocessConnection, | ||||
| private ChildProcessPingThread | private ChildProcessPingThread | ||||
| { | { | ||||
| Connection (ChildProcessSlave& p, const String& pipeName) | |||||
| : InterprocessConnection (false, magicMastSlaveConnectionHeader), owner (p) | |||||
| Connection (ChildProcessSlave& p, const String& pipeName, int timeout) | |||||
| : InterprocessConnection (false, magicMastSlaveConnectionHeader), | |||||
| ChildProcessPingThread (timeout), | |||||
| owner (p) | |||||
| { | { | ||||
| connectToPipe (pipeName, timeoutMs); | connectToPipe (pipeName, timeoutMs); | ||||
| startThread (4); | startThread (4); | ||||
| @@ -237,7 +241,8 @@ bool ChildProcessSlave::sendMessageToMaster (const MemoryBlock& mb) | |||||
| } | } | ||||
| bool ChildProcessSlave::initialiseFromCommandLine (const String& commandLine, | bool ChildProcessSlave::initialiseFromCommandLine (const String& commandLine, | ||||
| const String& commandLineUniqueID) | |||||
| const String& commandLineUniqueID, | |||||
| int timeoutMs) | |||||
| { | { | ||||
| String prefix (getCommandLinePrefix (commandLineUniqueID)); | String prefix (getCommandLinePrefix (commandLineUniqueID)); | ||||
| @@ -248,7 +253,7 @@ bool ChildProcessSlave::initialiseFromCommandLine (const String& commandLine, | |||||
| if (pipeName.isNotEmpty()) | if (pipeName.isNotEmpty()) | ||||
| { | { | ||||
| connection = new Connection (*this, pipeName); | |||||
| connection = new Connection (*this, pipeName, timeoutMs <= 0 ? defaultTimeoutMs : timeoutMs); | |||||
| if (! connection->isConnected()) | if (! connection->isConnected()) | ||||
| connection = nullptr; | connection = nullptr; | ||||
| @@ -64,10 +64,16 @@ public: | |||||
| The commandLineUniqueID should be a short alphanumeric identifier (no spaces!) | The commandLineUniqueID should be a short alphanumeric identifier (no spaces!) | ||||
| that matches the string passed to ChildProcessMaster::launchSlaveProcess(). | that matches the string passed to ChildProcessMaster::launchSlaveProcess(). | ||||
| The timeoutMs parameter lets you specify how long the child process is allowed | |||||
| to run without receiving a ping from the master before the master is considered to | |||||
| have died, and handleConnectionLost() will be called. Passing <= 0 for this timeout | |||||
| makes it use a default value. | |||||
| Returns true if the command-line matches and the connection is made successfully. | Returns true if the command-line matches and the connection is made successfully. | ||||
| */ | */ | ||||
| bool initialiseFromCommandLine (const String& commandLine, | bool initialiseFromCommandLine (const String& commandLine, | ||||
| const String& commandLineUniqueID); | |||||
| const String& commandLineUniqueID, | |||||
| int timeoutMs = 0); | |||||
| //============================================================================== | //============================================================================== | ||||
| /** This will be called to deliver messages from the master process. | /** This will be called to deliver messages from the master process. | ||||
| @@ -141,11 +147,17 @@ public: | |||||
| that gets launched must respond by calling ChildProcessSlave::initialiseFromCommandLine() | that gets launched must respond by calling ChildProcessSlave::initialiseFromCommandLine() | ||||
| in its startup code, and must use a matching ID to commandLineUniqueID. | in its startup code, and must use a matching ID to commandLineUniqueID. | ||||
| The timeoutMs parameter lets you specify how long the child process is allowed | |||||
| to go without sending a ping before it is considered to have died and | |||||
| handleConnectionLost() will be called. Passing <= 0 for this timeout makes | |||||
| it use a default value. | |||||
| If this all works, the method returns true, and you can begin sending and | If this all works, the method returns true, and you can begin sending and | ||||
| receiving messages with the slave process. | receiving messages with the slave process. | ||||
| */ | */ | ||||
| bool launchSlaveProcess (const File& executableToLaunch, | bool launchSlaveProcess (const File& executableToLaunch, | ||||
| const String& commandLineUniqueID); | |||||
| const String& commandLineUniqueID, | |||||
| int timeoutMs = 0); | |||||
| /** This will be called to deliver a message from the slave process. | /** This will be called to deliver a message from the slave process. | ||||
| The call will probably be made on a background thread, so be careful with your thread-safety! | The call will probably be made on a background thread, so be careful with your thread-safety! | ||||
| @@ -589,6 +589,10 @@ namespace EdgeTableFillers | |||||
| filler[2].set (sourceColour); | filler[2].set (sourceColour); | ||||
| filler[3].set (sourceColour); | filler[3].set (sourceColour); | ||||
| } | } | ||||
| else | |||||
| { | |||||
| areRGBComponentsEqual = false; | |||||
| } | |||||
| } | } | ||||
| forcedinline void setEdgeTableYPos (const int y) noexcept | forcedinline void setEdgeTableYPos (const int y) noexcept | ||||
| @@ -3049,7 +3049,7 @@ void Desktop::Displays::findDisplays (float masterScale) | |||||
| d.userArea = d.totalArea = Rectangle<int> (screens[j].x_org, | d.userArea = d.totalArea = Rectangle<int> (screens[j].x_org, | ||||
| screens[j].y_org, | screens[j].y_org, | ||||
| screens[j].width, | screens[j].width, | ||||
| screens[j].height) * masterScale; | |||||
| screens[j].height) / masterScale; | |||||
| d.isMain = (index == 0); | d.isMain = (index == 0); | ||||
| d.scale = masterScale; | d.scale = masterScale; | ||||
| d.dpi = getDisplayDPI (0); // (all screens share the same DPI) | d.dpi = getDisplayDPI (0); // (all screens share the same DPI) | ||||
| @@ -1931,7 +1931,7 @@ bool TextEditor::deleteBackwards (bool moveInWholeWordSteps) | |||||
| if (moveInWholeWordSteps) | if (moveInWholeWordSteps) | ||||
| moveCaretTo (findWordBreakBefore (getCaretPosition()), true); | moveCaretTo (findWordBreakBefore (getCaretPosition()), true); | ||||
| else if (selection.isEmpty() && selection.getStart() > 0) | else if (selection.isEmpty() && selection.getStart() > 0) | ||||
| selection.setStart (selection.getEnd() - 1); | |||||
| selection = Range<int> (selection.getEnd() - 1, selection.getEnd()); | |||||
| cut(); | cut(); | ||||
| return true; | return true; | ||||
| @@ -1940,7 +1940,7 @@ bool TextEditor::deleteBackwards (bool moveInWholeWordSteps) | |||||
| bool TextEditor::deleteForwards (bool /*moveInWholeWordSteps*/) | bool TextEditor::deleteForwards (bool /*moveInWholeWordSteps*/) | ||||
| { | { | ||||
| if (selection.isEmpty() && selection.getStart() < getTotalNumChars()) | if (selection.isEmpty() && selection.getStart() < getTotalNumChars()) | ||||
| selection.setEnd (selection.getStart() + 1); | |||||
| selection = Range<int> (selection.getStart(), selection.getStart() + 1); | |||||
| cut(); | cut(); | ||||
| return true; | return true; | ||||