From d49fee07507f8ca5398ba2fcd6f5ca9ea7f634bc Mon Sep 17 00:00:00 2001 From: Ben Kuper Date: Sat, 22 Jun 2024 19:53:50 +0200 Subject: [PATCH 01/12] Add support for types int64, double, T, F, Nil and Impulse This also updates the UnitTest to include those types --- modules/juce_osc/osc/juce_OSCArgument.cpp | 169 ++++++++++++++++++++++ modules/juce_osc/osc/juce_OSCArgument.h | 50 +++++++ modules/juce_osc/osc/juce_OSCMessage.cpp | 79 ++++++++-- modules/juce_osc/osc/juce_OSCMessage.h | 21 +++ modules/juce_osc/osc/juce_OSCReceiver.cpp | 18 +++ modules/juce_osc/osc/juce_OSCSender.cpp | 16 ++ modules/juce_osc/osc/juce_OSCTypes.cpp | 6 + modules/juce_osc/osc/juce_OSCTypes.h | 14 +- 8 files changed, 359 insertions(+), 14 deletions(-) diff --git a/modules/juce_osc/osc/juce_OSCArgument.cpp b/modules/juce_osc/osc/juce_OSCArgument.cpp index 9feee23f5360..79d4c8a5883c 100644 --- a/modules/juce_osc/osc/juce_OSCArgument.cpp +++ b/modules/juce_osc/osc/juce_OSCArgument.cpp @@ -36,10 +36,15 @@ namespace juce { OSCArgument::OSCArgument (int32 v) : type (OSCTypes::int32), intValue (v) {} +OSCArgument::OSCArgument (int64 v) : type (OSCTypes::int64), int64Value (v) {} OSCArgument::OSCArgument (float v) : type (OSCTypes::float32), floatValue (v) {} +OSCArgument::OSCArgument (double v) : type (OSCTypes::double64),doubleValue (v) {} OSCArgument::OSCArgument (const String& s) : type (OSCTypes::string), stringValue (s) {} OSCArgument::OSCArgument (MemoryBlock b) : type (OSCTypes::blob), blob (std::move (b)) {} OSCArgument::OSCArgument (OSCColour c) : type (OSCTypes::colour), intValue ((int32) c.toInt32()) {} +OSCArgument::OSCArgument(bool b) : type (b ? OSCTypes::T : OSCTypes::F) {} +OSCArgument::OSCArgument(OSCType t) : type (t) {} //for nil and impulse + //============================================================================== String OSCArgument::getString() const noexcept @@ -60,6 +65,15 @@ int32 OSCArgument::getInt32() const noexcept return 0; } +int64 OSCArgument::getInt64() const noexcept +{ + if (isInt64()) + return int64Value; + + jassertfalse; // you must check the type of an argument before attempting to get its value! + return 0; +} + float OSCArgument::getFloat32() const noexcept { if (isFloat32()) @@ -69,6 +83,15 @@ float OSCArgument::getFloat32() const noexcept return 0.0f; } +double OSCArgument::getDouble() const noexcept +{ + if (isDouble()) + return doubleValue; + + jassertfalse; // you must check the type of an argument before attempting to get its value! + return 0.0; +} + const MemoryBlock& OSCArgument::getBlob() const noexcept { // you must check the type of an argument before attempting to get its value! @@ -86,6 +109,15 @@ OSCColour OSCArgument::getColour() const noexcept return { 0, 0, 0, 0 }; } +bool OSCArgument::getBool() const noexcept +{ + if (isBool()) + return type == OSCTypes::T; + + jassertfalse; // you must check the type of an argument before attempting to get its value! + return false; +} + //============================================================================== //============================================================================== @@ -126,14 +158,40 @@ class OSCArgumentTests final : public UnitTest expect (arg.getType() == OSCTypes::int32); expect (arg.isInt32()); + expect (! arg.isInt64()); expect (! arg.isFloat32()); + expect (! arg.isDouble()); expect (! arg.isString()); expect (! arg.isBlob()); expect (! arg.isColour()); + expect (! arg.isNil()); + expect (! arg.isImpulse()); + expect (! arg.isBool()); expect (arg.getInt32() == value); } + beginTest("Int64"); + { + int64 value = 1234567890123456789; + + OSCArgument arg(value); + + expect(arg.getType() == OSCTypes::int64); + expect (! arg.isInt32()); + expect (arg.isInt64()); + expect (! arg.isFloat32()); + expect (! arg.isDouble()); + expect (! arg.isString()); + expect (! arg.isBlob()); + expect (! arg.isColour()); + expect (! arg.isNil()); + expect (! arg.isImpulse()); + expect (! arg.isBool()); + + expect(arg.getInt64() == value); + } + beginTest ("Float32"); { float value = 12345.6789f; @@ -142,14 +200,40 @@ class OSCArgumentTests final : public UnitTest expect (arg.getType() == OSCTypes::float32); expect (! arg.isInt32()); + expect (! arg.isInt64()); expect (arg.isFloat32()); + expect (! arg.isDouble()); expect (! arg.isString()); expect (! arg.isBlob()); expect (! arg.isColour()); + expect (! arg.isNil()); + expect (! arg.isImpulse()); + expect (! arg.isBool()); expectEquals (arg.getFloat32(), value); } + beginTest("Double"); + { + double value = 12345.6789; + + OSCArgument arg(value); + + expect(arg.getType() == OSCTypes::double64); + expect (! arg.isInt32()); + expect (! arg.isInt64()); + expect (! arg.isFloat32()); + expect (arg.isDouble()); + expect (! arg.isString()); + expect (! arg.isBlob()); + expect (! arg.isColour()); + expect (! arg.isNil()); + expect (! arg.isImpulse()); + expect (! arg.isBool()); + + expectEquals(arg.getDouble(), value); + } + beginTest ("String"); { String value = "Hello, World!"; @@ -157,10 +241,15 @@ class OSCArgumentTests final : public UnitTest expect (arg.getType() == OSCTypes::string); expect (! arg.isInt32()); + expect (! arg.isInt64()); expect (! arg.isFloat32()); + expect (! arg.isDouble()); expect (arg.isString()); expect (! arg.isBlob()); expect (! arg.isColour()); + expect (! arg.isNil()); + expect (! arg.isImpulse()); + expect (! arg.isBool()); expect (arg.getString() == value); } @@ -171,10 +260,15 @@ class OSCArgumentTests final : public UnitTest expect (arg.getType() == OSCTypes::string); expect (! arg.isInt32()); + expect (! arg.isInt64()); expect (! arg.isFloat32()); + expect (! arg.isDouble()); expect (arg.isString()); expect (! arg.isBlob()); expect (! arg.isColour()); + expect (! arg.isNil()); + expect (! arg.isImpulse()); + expect (! arg.isBool()); expect (arg.getString() == "Hello, World!"); } @@ -186,10 +280,15 @@ class OSCArgumentTests final : public UnitTest expect (arg.getType() == OSCTypes::blob); expect (! arg.isInt32()); + expect (! arg.isInt64()); expect (! arg.isFloat32()); + expect (! arg.isDouble()); expect (! arg.isString()); expect (arg.isBlob()); expect (! arg.isColour()); + expect (! arg.isNil()); + expect (! arg.isImpulse()); + expect (! arg.isBool()); expect (arg.getBlob() == blob); } @@ -209,15 +308,85 @@ class OSCArgumentTests final : public UnitTest expect (arg.getType() == OSCTypes::colour); expect (! arg.isInt32()); + expect (! arg.isInt64()); expect (! arg.isFloat32()); + expect (! arg.isDouble()); expect (! arg.isString()); expect (! arg.isBlob()); expect (arg.isColour()); + expect (! arg.isNil()); + expect (! arg.isImpulse()); + expect (! arg.isBool()); expect (arg.getColour().toInt32() == col.toInt32()); } } + beginTest ("Nil"); + { + OSCArgument arg (OSCTypes::nil); + + expect (arg.getType() == OSCTypes::nil); + expect (! arg.isInt32()); + expect (! arg.isInt64()); + expect (! arg.isFloat32()); + expect (! arg.isDouble()); + expect (! arg.isString()); + expect (! arg.isBlob()); + expect (! arg.isColour()); + expect (arg.isNil()); + expect (! arg.isImpulse()); + expect (! arg.isBool()); + } + + beginTest("Impulse"); + { + OSCArgument arg(OSCTypes::impulse); + + expect(arg.getType() == OSCTypes::impulse); + expect (! arg.isInt32()); + expect (! arg.isInt64()); + expect (! arg.isFloat32()); + expect (! arg.isDouble()); + expect (! arg.isString()); + expect (! arg.isBlob()); + expect (! arg.isColour()); + expect (! arg.isNil()); + expect (arg.isImpulse()); + expect (! arg.isBool()); + } + + beginTest("True"); + { + OSCArgument arg(OSCTypes::T); + expect (! arg.isInt32()); + expect (! arg.isInt64()); + expect (! arg.isFloat32()); + expect (! arg.isDouble()); + expect (! arg.isString()); + expect (! arg.isBlob()); + expect (! arg.isColour()); + expect (! arg.isNil()); + expect (! arg.isImpulse()); + expect (arg.isBool()); + } + + beginTest("False"); + { + OSCArgument arg(OSCTypes::F); + expect (! arg.isInt32()); + expect (! arg.isInt64()); + expect (! arg.isFloat32()); + expect (! arg.isDouble()); + expect (! arg.isString()); + expect (! arg.isBlob()); + expect (! arg.isColour()); + expect (! arg.isNil()); + expect (! arg.isImpulse()); + expect (arg.isBool()); + } + + beginTest ("Copy, move and assignment"); { { diff --git a/modules/juce_osc/osc/juce_OSCArgument.h b/modules/juce_osc/osc/juce_OSCArgument.h index 6b908464a61e..55287e2645ad 100644 --- a/modules/juce_osc/osc/juce_OSCArgument.h +++ b/modules/juce_osc/osc/juce_OSCArgument.h @@ -52,9 +52,15 @@ class JUCE_API OSCArgument /** Constructs an OSCArgument with type int32 and a given value. */ OSCArgument (int32 value); + /** Constructs an OSCArgument with type int64 and a given value. */ + OSCArgument(int64 value); + /** Constructs an OSCArgument with type float32 and a given value. */ OSCArgument (float value); + /** Constructs an OSCArgument with type double and a given value. */ + OSCArgument(double value); + /** Constructs an OSCArgument with type string and a given value */ OSCArgument (const String& value); @@ -69,6 +75,14 @@ class JUCE_API OSCArgument /** Constructs an OSCArgument with type colour and a given colour value */ OSCArgument (OSCColour colour); + /** Constructs an OSCArgument with type T or F depending on the given boolean value */ + OSCArgument(bool value); + + /** Constructs an OSCArgument with the given OSC type tag. + This constructor is intended for creating OSCArgument objects with type nil or impulse. + */ + OSCArgument(OSCType type); + /** Returns the type of the OSCArgument as an OSCType. OSCType is a char type, and its value will be the OSC type tag of the type. */ @@ -77,9 +91,15 @@ class JUCE_API OSCArgument /** Returns whether the type of the OSCArgument is int32. */ bool isInt32() const noexcept { return type == OSCTypes::int32; } + /** Returns whether the type of the OSCArgument is int64. */ + bool isInt64() const noexcept { return type == OSCTypes::int64; } + /** Returns whether the type of the OSCArgument is float. */ bool isFloat32() const noexcept { return type == OSCTypes::float32; } + /** Returns whether the type of the OSCArgument is double. */ + bool isDouble() const noexcept { return type == OSCTypes::double64; } + /** Returns whether the type of the OSCArgument is string. */ bool isString() const noexcept { return type == OSCTypes::string; } @@ -89,16 +109,35 @@ class JUCE_API OSCArgument /** Returns whether the type of the OSCArgument is colour. */ bool isColour() const noexcept { return type == OSCTypes::colour; } + /** Returns whether the type of the OSCArgument is nil. */ + bool isNil() const noexcept { return type == OSCTypes::nil; } + + /** Returns whether the type of the OSCArgument is impulse. */ + bool isImpulse() const noexcept { return type == OSCTypes::impulse; } + + /** Returns whether the type of the OSCArgument is T or F. */ + bool isBool() const noexcept { return type == OSCTypes::T || type == OSCTypes::F; } + /** Returns the value of the OSCArgument as an int32. If the type of the OSCArgument is not int32, the behaviour is undefined. */ int32 getInt32() const noexcept; + /** Returns the value of the OSCArgument as an int64. + If the type of the OSCArgument is not int64, the behaviour is undefined. + */ + int64 getInt64() const noexcept; + /** Returns the value of the OSCArgument as a float32. If the type of the OSCArgument is not float32, the behaviour is undefined. */ float getFloat32() const noexcept; + /** Returns the value of the OSCArgument as a double. + If the type of the OSCArgument is not double, the behaviour is undefined. + */ + double getDouble() const noexcept; + /** Returns the value of the OSCArgument as a string. If the type of the OSCArgument is not string, the behaviour is undefined. */ @@ -116,6 +155,11 @@ class JUCE_API OSCArgument */ OSCColour getColour() const noexcept; + /** Returns the value of the OSCArgument as a boolean. + If the type of the OSCArgument is not T or F, the behaviour is undefined. + */ + bool getBool() const noexcept { return type == OSCTypes::T; } + private: //============================================================================== OSCType type; @@ -126,6 +170,12 @@ class JUCE_API OSCArgument float floatValue; }; + union + { + int64 int64Value; + double doubleValue; + }; + String stringValue; MemoryBlock blob; }; diff --git a/modules/juce_osc/osc/juce_OSCMessage.cpp b/modules/juce_osc/osc/juce_OSCMessage.cpp index 47f0021cabc4..526955f60848 100644 --- a/modules/juce_osc/osc/juce_OSCMessage.cpp +++ b/modules/juce_osc/osc/juce_OSCMessage.cpp @@ -98,10 +98,15 @@ void OSCMessage::clear() //============================================================================== void OSCMessage::addInt32 (int32 value) { arguments.add (OSCArgument (value)); } +void OSCMessage::addInt64 (int64 value) { arguments.add (OSCArgument (value)); } void OSCMessage::addFloat32 (float value) { arguments.add (OSCArgument (value)); } +void OSCMessage::addDouble (double value) { arguments.add (OSCArgument (value)); } void OSCMessage::addString (const String& value) { arguments.add (OSCArgument (value)); } void OSCMessage::addBlob (MemoryBlock blob) { arguments.add (OSCArgument (std::move (blob))); } void OSCMessage::addColour (OSCColour colour) { arguments.add (OSCArgument (colour)); } +void OSCMessage::addNil() { arguments.add (OSCArgument (OSCTypes::nil)); } +void OSCMessage::addImpulse() { arguments.add (OSCArgument (OSCTypes::impulse)); } +void OSCMessage::addBool (bool value) { arguments.add (OSCArgument (value)); } void OSCMessage::addArgument (OSCArgument arg) { arguments.add (arg); } @@ -124,41 +129,72 @@ class OSCMessageTests final : public UnitTest expectEquals (msg.size(), 0); expect (msg.getAddressPattern().toString() == "/test/param0"); - const int numTestArgs = 5; + const int numTestArgs = 11; const int testInt = 42; + const int64 testInt64 = 64; const float testFloat = 3.14159f; + const double testDouble = 6.28; const String testString = "Hello, World!"; const OSCColour testColour = { 10, 20, 150, 200 }; + const bool testTrue = true; + const bool testFalse = false; const uint8 testBlobData[5] = { 0xBB, 0xCC, 0xDD, 0xEE, 0xFF }; const MemoryBlock testBlob (testBlobData, sizeof (testBlobData)); msg.addInt32 (testInt); + msg.addInt64 (testInt64); msg.addFloat32 (testFloat); + msg.addDouble (testDouble); msg.addString (testString); msg.addBlob (testBlob); msg.addColour (testColour); + msg.addNil (); + msg.addImpulse (); + msg.addBool (testTrue); + msg.addBool (testFalse); + expectEquals (msg.size(), numTestArgs); expectEquals (msg[0].getType(), OSCTypes::int32); - expectEquals (msg[1].getType(), OSCTypes::float32); - expectEquals (msg[2].getType(), OSCTypes::string); - expectEquals (msg[3].getType(), OSCTypes::blob); - expectEquals (msg[4].getType(), OSCTypes::colour); + expectEquals (msg[1].getType(), OSCTypes::int64); + expectEquals (msg[2].getType(), OSCTypes::float32); + expectEquals (msg[3].getType(), OSCTypes::double64); + expectEquals (msg[4].getType(), OSCTypes::string); + expectEquals (msg[5].getType(), OSCTypes::blob); + expectEquals (msg[6].getType(), OSCTypes::colour); + expectEquals (msg[7].getType(), OSCTypes::nil); + expectEquals (msg[8].getType(), OSCTypes::impulse); + expectEquals (msg[9].getType(), OSCTypes::T); + expectEquals (msg[10].getType(), OSCTypes::F); + expect (msg[0].isInt32()); - expect (msg[1].isFloat32()); - expect (msg[2].isString()); - expect (msg[3].isBlob()); - expect (msg[4].isColour()); + expect (msg[1].isInt64()); + expect (msg[2].isFloat32()); + expect (msg[3].isDouble()); + expect (msg[4].isString()); + expect (msg[5].isBlob()); + expect (msg[6].isColour()); + expect (msg[7].isNil()); + expect (msg[8].isImpulse()); + expect (msg[9].isBool()); + expect (msg[10].isBool()); + expectEquals (msg[0].getInt32(), testInt); - expectEquals (msg[1].getFloat32(), testFloat); - expectEquals (msg[2].getString(), testString); - expect (msg[3].getBlob() == testBlob); - expect (msg[4].getColour().toInt32() == testColour.toInt32()); + expectEquals (msg[1].getInt64(), testInt64); + expectEquals (msg[2].getFloat32(), testFloat); + expectEquals (msg[3].getDouble(), testDouble); + expectEquals (msg[4].getString(), testString); + expect (msg[5].getBlob() == testBlob); + expect (msg[6].getColour().toInt32() == testColour.toInt32()); + expectEquals (msg[9].getBool(), testTrue); + expectEquals (msg[10].getBool(), testFalse); + + expect (msg.begin() + numTestArgs == msg.end()); @@ -166,9 +202,15 @@ class OSCMessageTests final : public UnitTest expect (arg->isInt32()); expectEquals (arg->getInt32(), testInt); ++arg; + expect (arg->isInt64()); + expectEquals (arg->getInt64(), testInt64); + ++arg; expect (arg->isFloat32()); expectEquals (arg->getFloat32(), testFloat); ++arg; + expect (arg->isDouble()); + expectEquals (arg->getDouble(), testDouble); + ++arg; expect (arg->isString()); expectEquals (arg->getString(), testString); ++arg; @@ -178,6 +220,17 @@ class OSCMessageTests final : public UnitTest expect (arg->isColour()); expect (arg->getColour().toInt32() == testColour.toInt32()); ++arg; + expect (arg->isNil()); + ++arg; + expect(arg->isImpulse()); + ++arg; + expect(arg->isBool()); + expectEquals(arg->getBool(), testTrue); + ++arg; + expect(arg->isBool()); + expectEquals(arg->getBool(), testFalse); + ++arg; + expect (arg == msg.end()); } diff --git a/modules/juce_osc/osc/juce_OSCMessage.h b/modules/juce_osc/osc/juce_OSCMessage.h index f49e931c61a4..15aa78172f76 100644 --- a/modules/juce_osc/osc/juce_OSCMessage.h +++ b/modules/juce_osc/osc/juce_OSCMessage.h @@ -133,11 +133,21 @@ class JUCE_API OSCMessage */ void addInt32 (int32 value); + /** Creates a new OSCArgument of type int64 with the given value, + and adds it to the OSCMessage object. + */ + void addInt64(int64 value); + /** Creates a new OSCArgument of type float32 with the given value, and adds it to the OSCMessage object. */ void addFloat32 (float value); + /** Creates a new OSCArgument of type double with the given value, + and adds it to the OSCMessage object. + */ + void addDouble (double value); + /** Creates a new OSCArgument of type string with the given value, and adds it to the OSCMessage object. */ @@ -155,6 +165,17 @@ class JUCE_API OSCMessage */ void addColour (OSCColour colour); + /** Creates a new OSCArgument of type nil and adds it to the OSCMessage object. */ + void addNil(); + + /** Creates a new OSCArgument of type impulse and adds it to the OSCMessage object. */ + void addImpulse(); + + /** Creates a new OSCArgument of type T or F with the given value, + and adds it to the OSCMessage object. + */ + void addBool (bool value); + /** Adds the OSCArgument argument to the OSCMessage object. Note: This method will result in a copy of the OSCArgument object if it is passed diff --git a/modules/juce_osc/osc/juce_OSCReceiver.cpp b/modules/juce_osc/osc/juce_OSCReceiver.cpp index 545a289299cc..1e0c0323aa99 100644 --- a/modules/juce_osc/osc/juce_OSCReceiver.cpp +++ b/modules/juce_osc/osc/juce_OSCReceiver.cpp @@ -86,6 +86,12 @@ namespace return input.readIntBigEndian(); } + int64 readInt64() + { + checkBytesAvailable(8, "OSC input stream exhausted while reading int64"); + return input.readInt64BigEndian(); + } + uint64 readUint64() { checkBytesAvailable (8, "OSC input stream exhausted while reading uint64"); @@ -98,6 +104,12 @@ namespace return input.readFloatBigEndian(); } + double readDouble64() + { + checkBytesAvailable(8, "OSC input stream exhausted while reading double"); + return input.readDoubleBigEndian(); + } + String readString() { checkBytesAvailable (4, "OSC input stream exhausted while reading string"); @@ -189,10 +201,16 @@ namespace switch (type) { case OSCTypes::int32: return OSCArgument (readInt32()); + case OSCTypes::int64: return OSCArgument (readInt64()); case OSCTypes::float32: return OSCArgument (readFloat32()); + case OSCTypes::double64: return OSCArgument (readDouble64()); case OSCTypes::string: return OSCArgument (readString()); case OSCTypes::blob: return OSCArgument (readBlob()); case OSCTypes::colour: return OSCArgument (readColour()); + case OSCTypes::nil: return OSCArgument (OSCTypes::nil); + case OSCTypes::impulse: return OSCArgument (OSCTypes::impulse); + case OSCTypes::T: return OSCArgument (true); + case OSCTypes::F: return OSCArgument (false); default: // You supplied an invalid OSCType when calling readArgument! This should never happen. diff --git a/modules/juce_osc/osc/juce_OSCSender.cpp b/modules/juce_osc/osc/juce_OSCSender.cpp index 118adf824263..a87bca1957e1 100644 --- a/modules/juce_osc/osc/juce_OSCSender.cpp +++ b/modules/juce_osc/osc/juce_OSCSender.cpp @@ -62,6 +62,11 @@ namespace return output.writeIntBigEndian (value); } + bool writeInt64(int64 value) + { + return output.writeInt64BigEndian(value); + } + bool writeUint64 (uint64 value) { return output.writeInt64BigEndian (int64 (value)); @@ -72,6 +77,11 @@ namespace return output.writeFloatBigEndian (value); } + bool writeDouble(double value) + { + return output.writeDoubleBigEndian(value); + } + bool writeString (const String& value) { if (! output.writeString (value)) @@ -133,10 +143,16 @@ namespace switch (arg.getType()) { case OSCTypes::int32: return writeInt32 (arg.getInt32()); + case OSCTypes::int64: return writeInt64 (arg.getInt64()); case OSCTypes::float32: return writeFloat32 (arg.getFloat32()); + case OSCTypes::double64: return writeDouble (arg.getDouble()); case OSCTypes::string: return writeString (arg.getString()); case OSCTypes::blob: return writeBlob (arg.getBlob()); case OSCTypes::colour: return writeColour (arg.getColour()); + case OSCTypes::nil: return true; + case OSCTypes::impulse: return true; + case OSCTypes::T: return true; + case OSCTypes::F: return true; default: // In this very unlikely case you supplied an invalid OSCType! diff --git a/modules/juce_osc/osc/juce_OSCTypes.cpp b/modules/juce_osc/osc/juce_OSCTypes.cpp index 5a3405b1dc66..c9214fd2a7cc 100644 --- a/modules/juce_osc/osc/juce_OSCTypes.cpp +++ b/modules/juce_osc/osc/juce_OSCTypes.cpp @@ -36,10 +36,16 @@ namespace juce { const OSCType OSCTypes::int32 = 'i'; +const OSCType OSCTypes::int64 = 'h'; const OSCType OSCTypes::float32 = 'f'; +const OSCType OSCTypes::double64= 'd'; const OSCType OSCTypes::string = 's'; const OSCType OSCTypes::blob = 'b'; const OSCType OSCTypes::colour = 'r'; +const OSCType OSCTypes::nil = 'N'; +const OSCType OSCTypes::impulse = 'I'; +const OSCType OSCTypes::T = 'T'; +const OSCType OSCTypes::F = 'F'; uint32 OSCColour::toInt32() const { diff --git a/modules/juce_osc/osc/juce_OSCTypes.h b/modules/juce_osc/osc/juce_OSCTypes.h index 83dbfc791c52..9dab1346ab64 100644 --- a/modules/juce_osc/osc/juce_OSCTypes.h +++ b/modules/juce_osc/osc/juce_OSCTypes.h @@ -56,18 +56,30 @@ class JUCE_API OSCTypes { public: static const OSCType int32; + static const OSCType int64; static const OSCType float32; + static const OSCType double64; static const OSCType string; static const OSCType blob; static const OSCType colour; + static const OSCType nil; + static const OSCType impulse; + static const OSCType T; + static const OSCType F; static bool isSupportedType (OSCType type) noexcept { return type == OSCTypes::int32 + || type == OSCTypes::int64 || type == OSCTypes::float32 + || type == OSCTypes::double64 || type == OSCTypes::string || type == OSCTypes::blob - || type == OSCTypes::colour; + || type == OSCTypes::colour + || type == OSCTypes::nil + || type == OSCTypes::impulse + || type == OSCTypes::T + || type == OSCTypes::F; } }; From d2cf768b0a49261a36f0b319c390533fa1dce1cc Mon Sep 17 00:00:00 2001 From: Ben Kuper Date: Sat, 22 Jun 2024 20:09:54 +0200 Subject: [PATCH 02/12] Add IP and Port detection option in OSCReceiver --- modules/juce_osc/juce_osc.h | 6 ++++ modules/juce_osc/osc/juce_OSCMessage.cpp | 20 +++++++++++++ modules/juce_osc/osc/juce_OSCMessage.h | 15 ++++++++++ modules/juce_osc/osc/juce_OSCReceiver.cpp | 34 +++++++++++++++++++++-- 4 files changed, 72 insertions(+), 3 deletions(-) diff --git a/modules/juce_osc/juce_osc.h b/modules/juce_osc/juce_osc.h index 6195b449b231..d171c103557c 100644 --- a/modules/juce_osc/juce_osc.h +++ b/modules/juce_osc/juce_osc.h @@ -60,6 +60,12 @@ #pragma once #define JUCE_OSC_H_INCLUDED +/** Config: JUCE_IP_AND_PORT_DETECTION + If enabled, this will add remoteIP and remotePort variables to osc packets, corresponding to the sender's ip and port when receiving messages. +*/ +#ifndef JUCE_IP_AND_PORT_DETECTION +#define JUCE_IP_AND_PORT_DETECTION 0 +#endif #include #include diff --git a/modules/juce_osc/osc/juce_OSCMessage.cpp b/modules/juce_osc/osc/juce_OSCMessage.cpp index 47f0021cabc4..4a6da3d4d31d 100644 --- a/modules/juce_osc/osc/juce_OSCMessage.cpp +++ b/modules/juce_osc/osc/juce_OSCMessage.cpp @@ -50,6 +50,26 @@ OSCAddressPattern OSCMessage::getAddressPattern() const noexcept return addressPattern; } +#if JUCE_IP_AND_PORT_DETECTION +String OSCMessage::getSenderIPAddress() const noexcept +{ + return senderIPAddress; +} + +void OSCMessage::setSenderIPAddress(const String& ip) noexcept +{ + senderIPAddress = ip; +} + +int OSCMessage::getSenderPortNumber() const noexcept +{ + return senderPortNumber; +} +void OSCMessage::setSenderPortNumber(int port) noexcept +{ + senderPortNumber = port; +} +#endif //============================================================================== int OSCMessage::size() const noexcept { diff --git a/modules/juce_osc/osc/juce_OSCMessage.h b/modules/juce_osc/osc/juce_OSCMessage.h index f49e931c61a4..b07eac5c5a10 100644 --- a/modules/juce_osc/osc/juce_OSCMessage.h +++ b/modules/juce_osc/osc/juce_OSCMessage.h @@ -91,6 +91,16 @@ class JUCE_API OSCMessage /** Returns the address pattern of the OSCMessage. */ OSCAddressPattern getAddressPattern() const noexcept; +#if JUCE_IP_AND_PORT_DETECTION + /** Returns the sender's IP Address. */ + String getSenderIPAddress() const noexcept; + void setSenderIPAddress(const String& ip) noexcept; + + /** Returns the sender's port number. */ + int getSenderPortNumber() const noexcept; + void setSenderPortNumber(int port) noexcept; +#endif + /** Returns the number of OSCArgument objects that belong to this OSCMessage. */ int size() const noexcept; @@ -178,6 +188,11 @@ class JUCE_API OSCMessage //============================================================================== OSCAddressPattern addressPattern; Array arguments; + +#if JUCE_IP_AND_PORT_DETECTION + String senderIPAddress; + int senderPortNumber = 0; +#endif }; diff --git a/modules/juce_osc/osc/juce_OSCReceiver.cpp b/modules/juce_osc/osc/juce_OSCReceiver.cpp index 545a289299cc..f0510ba5d3c4 100644 --- a/modules/juce_osc/osc/juce_OSCReceiver.cpp +++ b/modules/juce_osc/osc/juce_OSCReceiver.cpp @@ -56,9 +56,18 @@ namespace @param sourceData the block of data to use as the stream's source @param sourceDataSize the number of bytes in the source data block */ + +#if JUCE_IP_AND_PORT_DETECTION + OSCInputStream (const void* sourceData, size_t sourceDataSize, const String& senderIPAddress, const int& senderPortNumber) : + input(sourceData, sourceDataSize, false), + senderIPAddress(senderIPAddress), + senderPortNumber(senderPortNumber) + {} +#else OSCInputStream (const void* sourceData, size_t sourceDataSize) : input (sourceData, sourceDataSize, false) {} +#endif //============================================================================== /** Returns a pointer to the source data block from which this stream is reading. */ @@ -273,6 +282,11 @@ namespace private: MemoryInputStream input; +#if JUCE_IP_AND_PORT_DETECTION + String senderIPAddress; + int senderPortNumber; +#endif + //============================================================================== void readPaddingZeros (size_t bytesRead) { @@ -427,10 +441,15 @@ struct OSCReceiver::Pimpl : private Thread, }; //============================================================================== - void handleBuffer (const char* data, size_t dataSize) +#if JUCE_IP_AND_PORT_DETECTION + void handleBuffer(const char* data, size_t dataSize, const String& senderIPAddress, const int& senderPortNumber) { - OSCInputStream inStream (data, dataSize); - + OSCInputStream inStream(data, dataSize, senderIPAddress, senderPortNumber); +#else + void handleBuffer(const char* data, size_t dataSize) + { + OSCInputStream inStream(data, dataSize); +#endif try { auto content = inStream.readElementWithKnownSize (dataSize); @@ -477,10 +496,19 @@ struct OSCReceiver::Pimpl : private Thread, if (ready == 0) continue; + +#if JUCE_IP_AND_PORT_DETECTION + String senderIPAddress = ""; + int senderPortNumber = 0; + auto bytesRead = (size_t) socket->read (oscBuffer.getData(), bufferSize, false, senderIPAddress, senderPortNumber); + if (bytesRead >= 4) + handleBuffer(oscBuffer.getData(), bytesRead, senderIPAddress, senderPortNumber); +#else auto bytesRead = (size_t) socket->read (oscBuffer.getData(), bufferSize, false); if (bytesRead >= 4) handleBuffer (oscBuffer.getData(), bytesRead); +#endif } } From 669ac2cc8f0496c6f31eea741d09b7b7d66044f8 Mon Sep 17 00:00:00 2001 From: Ben Kuper Date: Sat, 22 Jun 2024 20:11:46 +0200 Subject: [PATCH 03/12] added allow special chars in address --- modules/juce_osc/juce_osc.h | 8 ++++++++ modules/juce_osc/osc/juce_OSCAddress.cpp | 6 +++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/modules/juce_osc/juce_osc.h b/modules/juce_osc/juce_osc.h index d171c103557c..19eeb7665cbc 100644 --- a/modules/juce_osc/juce_osc.h +++ b/modules/juce_osc/juce_osc.h @@ -60,6 +60,14 @@ #pragma once #define JUCE_OSC_H_INCLUDED + +/** Config: JUCE_ALLOW_SPECIAL_CHARS_IN_ADDRESS + Enables the use of characters in adress that are not allowed by the OSC specifications (like spaces), but that are used + by some applications anyway (e.g. /my spaced/address) +*/ +#ifndef JUCE_ALLOW_SPECIAL_CHARS_IN_ADDRESS +#define JUCE_ALLOW_SPECIAL_CHARS_IN_ADDRESS 0 +#endif /** Config: JUCE_IP_AND_PORT_DETECTION If enabled, this will add remoteIP and remotePort variables to osc packets, corresponding to the sender's ip and port when receiving messages. */ diff --git a/modules/juce_osc/osc/juce_OSCAddress.cpp b/modules/juce_osc/osc/juce_OSCAddress.cpp index c13872cf7ece..99e1358de6d5 100644 --- a/modules/juce_osc/osc/juce_OSCAddress.cpp +++ b/modules/juce_osc/osc/juce_OSCAddress.cpp @@ -285,7 +285,11 @@ namespace static bool isDisallowedChar (juce_wchar c) noexcept { - return CharPointer_ASCII (Traits::getDisallowedChars()).indexOf (c, false) >= 0; +#if JUCE_ALLOW_SPECIAL_CHARS_IN_ADDRESS + return false; +#else + return CharPointer_ASCII( Traits::getDisallowedChars()).indexOf (c, false) >= 0; +#endif } static bool containsOnlyAllowedPrintableASCIIChars (const String& string) noexcept From 4f98d0ec2f6ad092d3d5a3a3036971f7308a48a9 Mon Sep 17 00:00:00 2001 From: Ben Kuper Date: Sat, 22 Jun 2024 20:12:10 +0200 Subject: [PATCH 04/12] added broadcast and binding options --- modules/juce_osc/juce_osc.h | 15 +++++++++++++++ modules/juce_osc/osc/juce_OSCReceiver.cpp | 11 ++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/modules/juce_osc/juce_osc.h b/modules/juce_osc/juce_osc.h index 19eeb7665cbc..28e5326b0969 100644 --- a/modules/juce_osc/juce_osc.h +++ b/modules/juce_osc/juce_osc.h @@ -68,6 +68,21 @@ #ifndef JUCE_ALLOW_SPECIAL_CHARS_IN_ADDRESS #define JUCE_ALLOW_SPECIAL_CHARS_IN_ADDRESS 0 #endif + +/** Config: JUCE_ENABLE_BROADCAST_BY_DEFAULT + Automatically enables broadcast on bound port in OSCReceiver +*/ +#ifndef JUCE_ENABLE_BROADCAST_BY_DEFAULT +#define JUCE_ENABLE_BROADCAST_BY_DEFAULT 0 +#endif + +/** Config: JUCE_EXCLUSIVE_BINDING_BY_DEFAULT + If enabled, this will make the binding of this port exclusive, so no other process can bind it. +*/ +#ifndef JUCE_EXCLUSIVE_BINDING_BY_DEFAULT +#define JUCE_EXCLUSIVE_BINDING_BY_DEFAULT 0 +#endif + /** Config: JUCE_IP_AND_PORT_DETECTION If enabled, this will add remoteIP and remotePort variables to osc packets, corresponding to the sender's ip and port when receiving messages. */ diff --git a/modules/juce_osc/osc/juce_OSCReceiver.cpp b/modules/juce_osc/osc/juce_OSCReceiver.cpp index f0510ba5d3c4..d6959dd2170b 100644 --- a/modules/juce_osc/osc/juce_OSCReceiver.cpp +++ b/modules/juce_osc/osc/juce_OSCReceiver.cpp @@ -354,7 +354,16 @@ struct OSCReceiver::Pimpl : private Thread, if (! disconnect()) return false; - socket.setOwned (new DatagramSocket (false)); +#if JUCE_ENABLE_BROADCAST_BY_DEFAULT + DatagramSocket* datagram = new DatagramSocket(true); +#else + DatagramSocket* datagram = new DatagramSocket(false); +#endif + socket.setOwned(datagram); + +#if JUCE_EXCLUSIVE_BINDING_BY_DEFAULT + socket->setEnablePortReuse(false); +#endif if (! socket->bindToPort (portNumber)) return false; From 7972d8f44e4a4a8fbdb7018d92c6b4320e56ea91 Mon Sep 17 00:00:00 2001 From: Ben Kuper Date: Sat, 22 Jun 2024 20:28:26 +0200 Subject: [PATCH 05/12] respect text alignment --- modules/juce_osc/osc/juce_OSCArgument.cpp | 4 ++-- modules/juce_osc/osc/juce_OSCArgument.h | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/modules/juce_osc/osc/juce_OSCArgument.cpp b/modules/juce_osc/osc/juce_OSCArgument.cpp index 79d4c8a5883c..94f0a4ff4dbe 100644 --- a/modules/juce_osc/osc/juce_OSCArgument.cpp +++ b/modules/juce_osc/osc/juce_OSCArgument.cpp @@ -42,8 +42,8 @@ OSCArgument::OSCArgument (double v) : type (OSCTypes::double64),doub OSCArgument::OSCArgument (const String& s) : type (OSCTypes::string), stringValue (s) {} OSCArgument::OSCArgument (MemoryBlock b) : type (OSCTypes::blob), blob (std::move (b)) {} OSCArgument::OSCArgument (OSCColour c) : type (OSCTypes::colour), intValue ((int32) c.toInt32()) {} -OSCArgument::OSCArgument(bool b) : type (b ? OSCTypes::T : OSCTypes::F) {} -OSCArgument::OSCArgument(OSCType t) : type (t) {} //for nil and impulse +OSCArgument::OSCArgument (bool b) : type (b ? OSCTypes::T : OSCTypes::F) {} +OSCArgument::OSCArgument (OSCType t) : type (t) {} //for nil and impulse //============================================================================== diff --git a/modules/juce_osc/osc/juce_OSCArgument.h b/modules/juce_osc/osc/juce_OSCArgument.h index 55287e2645ad..cf1b10edf42c 100644 --- a/modules/juce_osc/osc/juce_OSCArgument.h +++ b/modules/juce_osc/osc/juce_OSCArgument.h @@ -53,13 +53,13 @@ class JUCE_API OSCArgument OSCArgument (int32 value); /** Constructs an OSCArgument with type int64 and a given value. */ - OSCArgument(int64 value); + OSCArgument (int64 value); /** Constructs an OSCArgument with type float32 and a given value. */ OSCArgument (float value); /** Constructs an OSCArgument with type double and a given value. */ - OSCArgument(double value); + OSCArgument (double value); /** Constructs an OSCArgument with type string and a given value */ OSCArgument (const String& value); @@ -76,12 +76,12 @@ class JUCE_API OSCArgument OSCArgument (OSCColour colour); /** Constructs an OSCArgument with type T or F depending on the given boolean value */ - OSCArgument(bool value); + OSCArgument (bool value); /** Constructs an OSCArgument with the given OSC type tag. This constructor is intended for creating OSCArgument objects with type nil or impulse. */ - OSCArgument(OSCType type); + OSCArgument (OSCType type); /** Returns the type of the OSCArgument as an OSCType. OSCType is a char type, and its value will be the OSC type tag of the type. @@ -98,7 +98,7 @@ class JUCE_API OSCArgument bool isFloat32() const noexcept { return type == OSCTypes::float32; } /** Returns whether the type of the OSCArgument is double. */ - bool isDouble() const noexcept { return type == OSCTypes::double64; } + bool isDouble() const noexcept { return type == OSCTypes::double64; } /** Returns whether the type of the OSCArgument is string. */ bool isString() const noexcept { return type == OSCTypes::string; } @@ -116,7 +116,7 @@ class JUCE_API OSCArgument bool isImpulse() const noexcept { return type == OSCTypes::impulse; } /** Returns whether the type of the OSCArgument is T or F. */ - bool isBool() const noexcept { return type == OSCTypes::T || type == OSCTypes::F; } + bool isBool() const noexcept { return type == OSCTypes::T || type == OSCTypes::F; } /** Returns the value of the OSCArgument as an int32. If the type of the OSCArgument is not int32, the behaviour is undefined. From d1d27781b4ddf5d7af5210c55b6194f276303f92 Mon Sep 17 00:00:00 2001 From: Ben Kuper Date: Sat, 22 Jun 2024 22:25:31 +0200 Subject: [PATCH 06/12] Virtual functions in viewport to be fully overridable from child class --- .../juce_gui_basics/layout/juce_Viewport.h | 84 +++++++++---------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/modules/juce_gui_basics/layout/juce_Viewport.h b/modules/juce_gui_basics/layout/juce_Viewport.h index f87a19de604a..c572b8d3fb86 100644 --- a/modules/juce_gui_basics/layout/juce_Viewport.h +++ b/modules/juce_gui_basics/layout/juce_Viewport.h @@ -64,7 +64,7 @@ class JUCE_API Viewport : public Component, explicit Viewport (const String& componentName = String()); /** Destructor. */ - ~Viewport() override; + virtual ~Viewport() override; //============================================================================== /** Sets the component that this viewport will contain and scroll around. @@ -82,14 +82,14 @@ class JUCE_API Viewport : public Component, of the component @see getViewedComponent */ - void setViewedComponent (Component* newViewedComponent, + virtual void setViewedComponent (Component* newViewedComponent, bool deleteComponentWhenNoLongerNeeded = true); /** Returns the component that's currently being used inside the Viewport. @see setViewedComponent */ - Component* getViewedComponent() const noexcept { return contentComp.get(); } + virtual Component* getViewedComponent() const noexcept { return contentComp.get(); } //============================================================================== /** Changes the position of the viewed component. @@ -102,7 +102,7 @@ class JUCE_API Viewport : public Component, @see getViewPositionX, getViewPositionY, setViewPositionProportionately */ - void setViewPosition (int xPixelsOffset, int yPixelsOffset); + virtual void setViewPosition (int xPixelsOffset, int yPixelsOffset); /** Changes the position of the viewed component. @@ -114,7 +114,7 @@ class JUCE_API Viewport : public Component, @see getViewPositionX, getViewPositionY, setViewPositionProportionately */ - void setViewPosition (Point newPosition); + virtual void setViewPosition (Point newPosition); /** Changes the view position as a proportion of the distance it can move. @@ -123,7 +123,7 @@ class JUCE_API Viewport : public Component, to the right as it's possible to go whilst keeping the child component on-screen. */ - void setViewPositionProportionately (double proportionX, double proportionY); + virtual void setViewPositionProportionately (double proportionX, double proportionY); /** If the specified position is at the edges of the viewport, this method scrolls the viewport to bring that position nearer to the centre. @@ -140,51 +140,51 @@ class JUCE_API Viewport : public Component, to scroll by. @returns true if the viewport was scrolled */ - bool autoScroll (int mouseX, int mouseY, int distanceFromEdge, int maximumSpeed); + virtual bool autoScroll (int mouseX, int mouseY, int distanceFromEdge, int maximumSpeed); /** Returns the position within the child component of the top-left of its visible area. */ - Point getViewPosition() const noexcept { return lastVisibleArea.getPosition(); } + virtual Point getViewPosition() const noexcept { return lastVisibleArea.getPosition(); } /** Returns the visible area of the child component, relative to its top-left */ - Rectangle getViewArea() const noexcept { return lastVisibleArea; } + virtual Rectangle getViewArea() const noexcept { return lastVisibleArea; } /** Returns the position within the child component of the top-left of its visible area. @see getViewWidth, setViewPosition */ - int getViewPositionX() const noexcept { return lastVisibleArea.getX(); } + virtual int getViewPositionX() const noexcept { return lastVisibleArea.getX(); } /** Returns the position within the child component of the top-left of its visible area. @see getViewHeight, setViewPosition */ - int getViewPositionY() const noexcept { return lastVisibleArea.getY(); } + virtual int getViewPositionY() const noexcept { return lastVisibleArea.getY(); } /** Returns the width of the visible area of the child component. This may be less than the width of this Viewport if there's a vertical scrollbar or if the child component is itself smaller. */ - int getViewWidth() const noexcept { return lastVisibleArea.getWidth(); } + virtual int getViewWidth() const noexcept { return lastVisibleArea.getWidth(); } /** Returns the height of the visible area of the child component. This may be less than the height of this Viewport if there's a horizontal scrollbar or if the child component is itself smaller. */ - int getViewHeight() const noexcept { return lastVisibleArea.getHeight(); } + virtual int getViewHeight() const noexcept { return lastVisibleArea.getHeight(); } /** Returns the width available within this component for the contents. This will be the width of the viewport component minus the width of a vertical scrollbar (if visible). */ - int getMaximumVisibleWidth() const; + virtual int getMaximumVisibleWidth() const; /** Returns the height available within this component for the contents. This will be the height of the viewport component minus the space taken up by a horizontal scrollbar (if visible). */ - int getMaximumVisibleHeight() const; + virtual int getMaximumVisibleHeight() const; //============================================================================== /** Callback method that is called when the visible area changes. @@ -207,7 +207,7 @@ class JUCE_API Viewport : public Component, mouse-wheel scrolling even when there the scrollbars are hidden. When the scrollbars are visible, these parameters are ignored. */ - void setScrollBarsShown (bool showVerticalScrollbarIfNeeded, + virtual void setScrollBarsShown (bool showVerticalScrollbarIfNeeded, bool showHorizontalScrollbarIfNeeded, bool allowVerticalScrollingWithoutScrollbar = false, bool allowHorizontalScrollingWithoutScrollbar = false); @@ -222,63 +222,63 @@ class JUCE_API Viewport : public Component, will appear at the bottom of the view port's content (this is the default), otherwise it will be at the top. */ - void setScrollBarPosition (bool verticalScrollbarOnRight, + virtual void setScrollBarPosition (bool verticalScrollbarOnRight, bool horizontalScrollbarAtBottom); /** True if the vertical scrollbar will appear on the right side of the content */ - bool isVerticalScrollbarOnTheRight() const noexcept { return vScrollbarRight; } + virtual bool isVerticalScrollbarOnTheRight() const noexcept { return vScrollbarRight; } /** True if the horizontal scrollbar will appear at the bottom of the content */ - bool isHorizontalScrollbarAtBottom() const noexcept { return hScrollbarBottom; } + virtual bool isHorizontalScrollbarAtBottom() const noexcept { return hScrollbarBottom; } /** True if the vertical scrollbar is enabled. @see setScrollBarsShown */ - bool isVerticalScrollBarShown() const noexcept { return showVScrollbar; } + virtual bool isVerticalScrollBarShown() const noexcept { return showVScrollbar; } /** True if the horizontal scrollbar is enabled. @see setScrollBarsShown */ - bool isHorizontalScrollBarShown() const noexcept { return showHScrollbar; } + virtual bool isHorizontalScrollBarShown() const noexcept { return showHScrollbar; } /** Changes the width of the scrollbars. If this isn't specified, the default width from the LookAndFeel class will be used. @see LookAndFeel::getDefaultScrollbarWidth */ - void setScrollBarThickness (int thickness); + virtual void setScrollBarThickness (int thickness); /** Returns the thickness of the scrollbars. @see setScrollBarThickness */ - int getScrollBarThickness() const; + virtual int getScrollBarThickness() const; /** Changes the distance that a single-step click on a scrollbar button will move the viewport. */ - void setSingleStepSizes (int stepX, int stepY); + virtual void setSingleStepSizes (int stepX, int stepY); /** Returns a reference to the scrollbar component being used. Handy if you need to customise the bar somehow. */ - ScrollBar& getVerticalScrollBar() noexcept { return *verticalScrollBar; } + virtual ScrollBar& getVerticalScrollBar() noexcept { return *verticalScrollBar; } /** Returns a reference to the scrollbar component being used. Handy if you need to customise the bar somehow. */ - ScrollBar& getHorizontalScrollBar() noexcept { return *horizontalScrollBar; } + virtual ScrollBar& getHorizontalScrollBar() noexcept { return *horizontalScrollBar; } /** Re-instantiates the scrollbars, which is only really useful if you've overridden createScrollBarComponent(). */ - void recreateScrollbars(); + virtual void recreateScrollbars(); /** True if there's any off-screen content that could be scrolled vertically, or false if everything is currently visible. */ - bool canScrollVertically() const noexcept; + virtual bool canScrollVertically() const noexcept; /** True if there's any off-screen content that could be scrolled horizontally, or false if everything is currently visible. */ - bool canScrollHorizontally() const noexcept; + virtual bool canScrollHorizontally() const noexcept; /** Enables or disables drag-to-scroll functionality for mouse sources in the viewport. @@ -287,14 +287,14 @@ class JUCE_API Viewport : public Component, method. */ [[deprecated ("Use setScrollOnDragMode instead.")]] - void setScrollOnDragEnabled (bool shouldScrollOnDrag) + virtual void setScrollOnDragEnabled (bool shouldScrollOnDrag) { setScrollOnDragMode (shouldScrollOnDrag ? ScrollOnDragMode::all : ScrollOnDragMode::never); } /** Returns true if drag-to-scroll functionality is enabled for mouse input sources. */ [[deprecated ("Use getScrollOnDragMode instead.")]] - bool isScrollOnDragEnabled() const noexcept { return getScrollOnDragMode() == ScrollOnDragMode::all; } + virtual bool isScrollOnDragEnabled() const noexcept { return getScrollOnDragMode() == ScrollOnDragMode::all; } enum class ScrollOnDragMode { @@ -309,33 +309,33 @@ class JUCE_API Viewport : public Component, user is drag-scrolling, you can disable this with the Component::setViewportIgnoreDragFlag() method. */ - void setScrollOnDragMode (ScrollOnDragMode scrollOnDragMode); + virtual void setScrollOnDragMode (ScrollOnDragMode scrollOnDragMode); /** Returns the current scroll-on-drag mode. */ - ScrollOnDragMode getScrollOnDragMode() const { return scrollOnDragMode; } + virtual ScrollOnDragMode getScrollOnDragMode() const { return scrollOnDragMode; } /** Returns true if the user is currently dragging-to-scroll. @see setScrollOnDragEnabled */ - bool isCurrentlyScrollingOnDrag() const noexcept; + virtual bool isCurrentlyScrollingOnDrag() const noexcept; //============================================================================== /** @internal */ - void resized() override; + virtual void resized() override; /** @internal */ - void scrollBarMoved (ScrollBar*, double newRangeStart) override; + virtual void scrollBarMoved (ScrollBar*, double newRangeStart) override; /** @internal */ - void mouseWheelMove (const MouseEvent&, const MouseWheelDetails&) override; + virtual void mouseWheelMove (const MouseEvent&, const MouseWheelDetails&) override; /** @internal */ - void mouseDown (const MouseEvent& e) override; + virtual void mouseDown (const MouseEvent& e) override; /** @internal */ - bool keyPressed (const KeyPress&) override; + virtual bool keyPressed (const KeyPress&) override; /** @internal */ - void componentMovedOrResized (Component&, bool wasMoved, bool wasResized) override; + virtual void componentMovedOrResized (Component&, bool wasMoved, bool wasResized) override; /** @internal */ - void lookAndFeelChanged() override; + virtual void lookAndFeelChanged() override; /** @internal */ - bool useMouseWheelMoveIfNeeded (const MouseEvent&, const MouseWheelDetails&); + virtual bool useMouseWheelMoveIfNeeded (const MouseEvent&, const MouseWheelDetails&); /** @internal */ static bool respondsToKey (const KeyPress&); From 5bb41f0a467de787addc644065e3cbd3af5daf0c Mon Sep 17 00:00:00 2001 From: Ben Kuper Date: Sat, 22 Jun 2024 22:44:20 +0200 Subject: [PATCH 07/12] fix double declaration getBool() --- modules/juce_osc/osc/juce_OSCArgument.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/juce_osc/osc/juce_OSCArgument.h b/modules/juce_osc/osc/juce_OSCArgument.h index cf1b10edf42c..7b29e12e22cd 100644 --- a/modules/juce_osc/osc/juce_OSCArgument.h +++ b/modules/juce_osc/osc/juce_OSCArgument.h @@ -158,7 +158,7 @@ class JUCE_API OSCArgument /** Returns the value of the OSCArgument as a boolean. If the type of the OSCArgument is not T or F, the behaviour is undefined. */ - bool getBool() const noexcept { return type == OSCTypes::T; } + bool getBool() const noexcept; private: //============================================================================== From 747d788997aef3abf77ea643e1fe3e0598a6db3d Mon Sep 17 00:00:00 2001 From: Ben Kuper Date: Sun, 23 Jun 2024 10:24:44 +0200 Subject: [PATCH 08/12] add variant.isVoid() check in fromVar function --- modules/juce_core/javascript/juce_Javascript.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/juce_core/javascript/juce_Javascript.cpp b/modules/juce_core/javascript/juce_Javascript.cpp index bfd9a9eb6b51..045aeb048a08 100644 --- a/modules/juce_core/javascript/juce_Javascript.cpp +++ b/modules/juce_core/javascript/juce_Javascript.cpp @@ -60,6 +60,9 @@ struct VariantConverter { static choc::value::Value fromVar (const var& variant) { + if (variant.isVoid()) + return {}; + if (variant.isInt()) return choc::value::Value { (int) variant }; From fe16f518bdf4cb58b29bdf3dcf16142d4b05e213 Mon Sep 17 00:00:00 2001 From: Ben Kuper Date: Sun, 23 Jun 2024 10:27:55 +0200 Subject: [PATCH 09/12] restructure to avoid warning when using allow special chars option --- modules/juce_osc/osc/juce_OSCAddress.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/modules/juce_osc/osc/juce_OSCAddress.cpp b/modules/juce_osc/osc/juce_OSCAddress.cpp index 99e1358de6d5..8b6540ff89bc 100644 --- a/modules/juce_osc/osc/juce_OSCAddress.cpp +++ b/modules/juce_osc/osc/juce_OSCAddress.cpp @@ -283,14 +283,17 @@ namespace return c >= ' ' && c <= '~'; } - static bool isDisallowedChar (juce_wchar c) noexcept - { #if JUCE_ALLOW_SPECIAL_CHARS_IN_ADDRESS + static bool isDisallowedChar (juce_wchar) noexcept + { return false; + } #else - return CharPointer_ASCII( Traits::getDisallowedChars()).indexOf (c, false) >= 0; -#endif + static bool isDisallowedChar(juce_wchar c) noexcept + { + return CharPointer_ASCII(Traits::getDisallowedChars()).indexOf(c, false) >= 0; } +#endif static bool containsOnlyAllowedPrintableASCIIChars (const String& string) noexcept { From 10471b3b61689aeaaa6afcc39a19c866d2da72d1 Mon Sep 17 00:00:00 2001 From: Ben Kuper Date: Fri, 5 Jul 2024 02:24:05 +0200 Subject: [PATCH 10/12] Revert "add variant.isVoid() check in fromVar function" This reverts commit 747d788997aef3abf77ea643e1fe3e0598a6db3d. --- modules/juce_core/javascript/juce_Javascript.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/modules/juce_core/javascript/juce_Javascript.cpp b/modules/juce_core/javascript/juce_Javascript.cpp index 045aeb048a08..bfd9a9eb6b51 100644 --- a/modules/juce_core/javascript/juce_Javascript.cpp +++ b/modules/juce_core/javascript/juce_Javascript.cpp @@ -60,9 +60,6 @@ struct VariantConverter { static choc::value::Value fromVar (const var& variant) { - if (variant.isVoid()) - return {}; - if (variant.isInt()) return choc::value::Value { (int) variant }; From 9bf02b20c7d8a826bf3a03d6b8fa6366fa729a10 Mon Sep 17 00:00:00 2001 From: Ben Kuper Date: Wed, 9 Oct 2024 12:39:01 +0200 Subject: [PATCH 11/12] remove JS_GPN_ENUM_ONLY to the flags when retrieving properties. --- modules/juce_core/javascript/juce_Javascript.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/juce_core/javascript/juce_Javascript.cpp b/modules/juce_core/javascript/juce_Javascript.cpp index 3ffedae69fdf..c6479aec7d06 100644 --- a/modules/juce_core/javascript/juce_Javascript.cpp +++ b/modules/juce_core/javascript/juce_Javascript.cpp @@ -364,7 +364,7 @@ static var tryQuickJSToJuce (const qjs::QuickJSContext::ValuePtr& ptr, JSPropertyEnum* properties = nullptr; uint32_t numProps = 0; - if (JS_GetOwnPropertyNames (ptr.context, &properties, &numProps, obj.get(), JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY) != 0 + if (JS_GetOwnPropertyNames (ptr.context, &properties, &numProps, obj.get(), JS_GPN_STRING_MASK) != 0 || properties == nullptr) { return new DynamicObject; From 2d5ee0f4f8a2ddc60c292aac2001f45028582205 Mon Sep 17 00:00:00 2001 From: Ben Kuper Date: Thu, 17 Oct 2024 16:48:40 +0200 Subject: [PATCH 12/12] keep track of last callFunction time to avoid bad interrupt --- .../juce_core/javascript/juce_Javascript.cpp | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/modules/juce_core/javascript/juce_Javascript.cpp b/modules/juce_core/javascript/juce_Javascript.cpp index 59204510f0b9..6875caa7001b 100644 --- a/modules/juce_core/javascript/juce_Javascript.cpp +++ b/modules/juce_core/javascript/juce_Javascript.cpp @@ -692,12 +692,14 @@ class JavascriptEngine::Impl { shouldStop = false; - engine.setInterruptHandler ([this, maxExecTime, started = Time::getMillisecondCounterHiRes()]() + timeAtLastStart = Time::getMillisecondCounterHiRes(); + + engine.setInterruptHandler ([this, maxExecTime]() { if (shouldStop) return 1; - const auto elapsed = RelativeTime::milliseconds ((int64) (Time::getMillisecondCounterHiRes() - started)); + const auto elapsed = RelativeTime::milliseconds ((int64) (Time::getMillisecondCounterHiRes() - timeAtLastStart)); return elapsed > maxExecTime ? 1 : 0; }); @@ -723,8 +725,10 @@ class JavascriptEngine::Impl return result; } - var callFunction (const Identifier& function, const var::NativeFunctionArgs& args, Result* errorMessage) + var callFunction (const Identifier& function, const var::NativeFunctionArgs& args, Result* errorMessage, RelativeTime maxExecTime) { + timeAtLastStart = Time::getMillisecondCounterHiRes(); + auto* ctx = engine.getQuickJSContext(); const auto functionStr = function.toString(); @@ -771,6 +775,11 @@ class JavascriptEngine::Impl //============================================================================== detail::QuickJSWrapper engine; std::atomic shouldStop = false; + + /** This value stores the last time a execute, eval or callFunction has been started. + This allows to update the interrupt check when using callFunction() after execute() without having to always override the interrupt handler + */ + double timeAtLastStart = 0; }; //============================================================================== @@ -801,7 +810,7 @@ var JavascriptEngine::callFunction (const Identifier& function, const var::NativeFunctionArgs& args, Result* errorMessage) { - return impl->callFunction (function, args, errorMessage); + return impl->callFunction (function, args, errorMessage, maximumExecutionTime); } void JavascriptEngine::stop() noexcept