From 43fa10b12f7bc78f0a48b04c5441a11a88f32775 Mon Sep 17 00:00:00 2001 From: jules Date: Tue, 4 Nov 2014 17:36:59 +0000 Subject: [PATCH] More template cleanups --- examples/AudioAppExample/Source/Main.cpp | 4 +- .../AudioAppExample/Source/MainComponent.cpp | 5 +- .../Introjucer/JuceLibraryCode/BinaryData.cpp | 31 +- .../Introjucer/JuceLibraryCode/BinaryData.h | 6 +- .../jucer_OpenGLComponentTemplate.cpp | 77 ++ .../jucer_OpenglComponentTemplate.cpp | 18 +- .../Wizards/jucer_ProjectWizard_Animated.h | 2 +- .../Wizards/jucer_ProjectWizard_openGL.h | 3 +- .../box2d/Collision/b2collideedge.cpp | 698 +++++++++++++ .../box2d/Collision/b2collision.cpp | 249 +++++ .../wrapper/juce_activex_gluecode.cpp | 947 ++++++++++++++++++ .../windows/juce_ResizableWindow.cpp | 4 + .../utils/juce_OpenGLAppComponent.cpp | 11 + .../utils/juce_OpenGLAppComponent.h | 2 + 14 files changed, 2035 insertions(+), 22 deletions(-) create mode 100644 extras/Introjucer/Source/BinaryData/jucer_OpenGLComponentTemplate.cpp create mode 100644 modules/juce_box2d/box2d/Collision/b2collideedge.cpp create mode 100644 modules/juce_box2d/box2d/Collision/b2collision.cpp create mode 100644 modules/juce_browser_plugin_client/wrapper/juce_activex_gluecode.cpp diff --git a/examples/AudioAppExample/Source/Main.cpp b/examples/AudioAppExample/Source/Main.cpp index 20ac4673b0..dd48f969ca 100644 --- a/examples/AudioAppExample/Source/Main.cpp +++ b/examples/AudioAppExample/Source/Main.cpp @@ -24,7 +24,7 @@ public: bool moreThanOneInstanceAllowed() override { return true; } //============================================================================== - void initialise (const String& commandLine) override + void initialise (const String& /*commandLine*/) override { // This method is where you should put your application's initialisation code.. @@ -46,7 +46,7 @@ public: quit(); } - void anotherInstanceStarted (const String& commandLine) override + void anotherInstanceStarted (const String& /*commandLine*/) override { // When another instance of the app is launched while this one is running, // this method is invoked, and the commandLine parameter tells you what diff --git a/examples/AudioAppExample/Source/MainComponent.cpp b/examples/AudioAppExample/Source/MainComponent.cpp index a7a4b63c1c..0ba953b54e 100644 --- a/examples/AudioAppExample/Source/MainComponent.cpp +++ b/examples/AudioAppExample/Source/MainComponent.cpp @@ -22,7 +22,8 @@ public: phaseDelta (0.0f), frequency (5000.0f), amplitude (0.2f), - sampleRate (0.0) + sampleRate (0.0), + expectedSamplesPerBlock (0) { setSize (500, 400); @@ -39,6 +40,7 @@ public: void prepareToPlay (int samplesPerBlockExpected, double newSampleRate) override { sampleRate = newSampleRate; + expectedSamplesPerBlock = samplesPerBlockExpected; } /* This method generates the actual audio samples. @@ -140,6 +142,7 @@ private: float amplitude; double sampleRate; + int expectedSamplesPerBlock; Point lastMousePosition; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainContentComponent) diff --git a/extras/Introjucer/JuceLibraryCode/BinaryData.cpp b/extras/Introjucer/JuceLibraryCode/BinaryData.cpp index f8efb039ec..833620c2b2 100644 --- a/extras/Introjucer/JuceLibraryCode/BinaryData.cpp +++ b/extras/Introjucer/JuceLibraryCode/BinaryData.cpp @@ -44,7 +44,7 @@ static const unsigned char temp_binary_data_0[] = " void update() override\r\n" " {\r\n" " // This function is called at the frequency specified by the setFramesPerSecond() call\r\n" -" // in the constructor. You can use it\r\n" +" // in the constructor. You can use it to update counters, animate values, etc.\r\n" " }\r\n" "\r\n" " void paint (Graphics& g) override\r\n" @@ -111,8 +111,8 @@ static const unsigned char temp_binary_data_1[] = " {\r\n" " setSize (500, 400);\r\n" "\r\n" -" // specify the number of input and output channels needed\r\n" -" setAudioChannels (1, 1);\r\n" +" // specify the number of input and output channels that we want to open\r\n" +" setAudioChannels (2, 2);\r\n" " }\r\n" "\r\n" " ~MainContentComponent()\r\n" @@ -1344,15 +1344,26 @@ static const unsigned char temp_binary_data_20[] = " MainContentComponent()\r\n" " {\r\n" " setSize (500, 400);\r\n" -"\r\n" " }\r\n" "\r\n" " ~MainContentComponent()\r\n" " {\r\n" -" shutdownAudio();\r\n" +" shutdownOpenGL();\r\n" " }\r\n" "\r\n" -" void paint (Graphics& g)\r\n" +" void initialise() override\r\n" +" {\r\n" +" }\r\n" +"\r\n" +" void shutdown() override\r\n" +" {\r\n" +" }\r\n" +"\r\n" +" void render() override\r\n" +" {\r\n" +" }\r\n" +"\r\n" +" void paint (Graphics& g) override\r\n" " {\r\n" " // (Our component is opaque, so we must completely fill the background with a solid colour)\r\n" " g.fillAll (Colours::black);\r\n" @@ -1361,7 +1372,7 @@ static const unsigned char temp_binary_data_20[] = " // You can add your drawing code here!\r\n" " }\r\n" "\r\n" -" void resized()\r\n" +" void resized() override\r\n" " {\r\n" " // This is called when the MainContentComponent is resized.\r\n" " // If you add any child components, this is where you should\r\n" @@ -3627,8 +3638,8 @@ const char* getNamedResource (const char* resourceNameUTF8, int& numBytes) throw switch (hash) { - case 0x6cf2645e: numBytes = 1908; return jucer_AnimatedComponentTemplate_cpp; - case 0xafccbd3f: numBytes = 2977; return jucer_AudioComponentTemplate_cpp; + case 0x6cf2645e: numBytes = 1949; return jucer_AnimatedComponentTemplate_cpp; + case 0xafccbd3f: numBytes = 2991; return jucer_AudioComponentTemplate_cpp; case 0x27c5a93a: numBytes = 1180; return jucer_AudioPluginEditorTemplate_cpp; case 0x4d0721bf: numBytes = 1012; return jucer_AudioPluginEditorTemplate_h; case 0x51b49ac5: numBytes = 5039; return jucer_AudioPluginFilterTemplate_cpp; @@ -3647,7 +3658,7 @@ const char* getNamedResource (const char* resourceNameUTF8, int& numBytes) throw case 0x02a2a077: numBytes = 262; return jucer_NewCppFileTemplate_cpp; case 0x0842c43c: numBytes = 308; return jucer_NewCppFileTemplate_h; case 0x36e634a1: numBytes = 1626; return jucer_NewInlineComponentTemplate_h; - case 0x7fbac252: numBytes = 1679; return jucer_OpenGLComponentTemplate_cpp; + case 0x7fbac252: numBytes = 1834; return jucer_OpenGLComponentTemplate_cpp; case 0x44be9398: numBytes = 2922; return AudioPluginXCodeScript_txt; case 0x4a0cfd09: numBytes = 151; return background_tile_png; case 0x763d39dc: numBytes = 1050; return colourscheme_dark_xml; diff --git a/extras/Introjucer/JuceLibraryCode/BinaryData.h b/extras/Introjucer/JuceLibraryCode/BinaryData.h index 221cad38a9..66bafef40f 100644 --- a/extras/Introjucer/JuceLibraryCode/BinaryData.h +++ b/extras/Introjucer/JuceLibraryCode/BinaryData.h @@ -10,10 +10,10 @@ namespace BinaryData { extern const char* jucer_AnimatedComponentTemplate_cpp; - const int jucer_AnimatedComponentTemplate_cppSize = 1908; + const int jucer_AnimatedComponentTemplate_cppSize = 1949; extern const char* jucer_AudioComponentTemplate_cpp; - const int jucer_AudioComponentTemplate_cppSize = 2977; + const int jucer_AudioComponentTemplate_cppSize = 2991; extern const char* jucer_AudioPluginEditorTemplate_cpp; const int jucer_AudioPluginEditorTemplate_cppSize = 1180; @@ -70,7 +70,7 @@ namespace BinaryData const int jucer_NewInlineComponentTemplate_hSize = 1626; extern const char* jucer_OpenGLComponentTemplate_cpp; - const int jucer_OpenGLComponentTemplate_cppSize = 1679; + const int jucer_OpenGLComponentTemplate_cppSize = 1834; extern const char* AudioPluginXCodeScript_txt; const int AudioPluginXCodeScript_txtSize = 2922; diff --git a/extras/Introjucer/Source/BinaryData/jucer_OpenGLComponentTemplate.cpp b/extras/Introjucer/Source/BinaryData/jucer_OpenGLComponentTemplate.cpp new file mode 100644 index 0000000000..7701c4d1eb --- /dev/null +++ b/extras/Introjucer/Source/BinaryData/jucer_OpenGLComponentTemplate.cpp @@ -0,0 +1,77 @@ +/* + ============================================================================== + + This file was auto-generated! + + ============================================================================== +*/ + +#ifndef MAINCOMPONENT_H_INCLUDED +#define MAINCOMPONENT_H_INCLUDED + +INCLUDE_JUCE + +//============================================================================== +/* + This component lives inside our window, and this is where you should put all + your controls and content. +*/ +class MainContentComponent : public OpenGLAppComponent +{ +public: + //============================================================================== + MainContentComponent() + { + setSize (500, 400); + } + + ~MainContentComponent() + { + shutdownOpenGL(); + } + + void initialise() override + { + } + + void shutdown() override + { + } + + void render() override + { + } + + void paint (Graphics& g) override + { + // (Our component is opaque, so we must completely fill the background with a solid colour) + g.fillAll (Colours::black); + + + // You can add your drawing code here! + } + + void resized() override + { + // This is called when the MainContentComponent is resized. + // If you add any child components, this is where you should + // update their positions. + } + + +private: + //============================================================================== + + // private member variables + + + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainContentComponent) +}; + + +// (This function is called by the app startup code to create our main component) +Component* createMainContentComponent() { return new MainContentComponent(); } + + +#endif // MAINCOMPONENT_H_INCLUDED diff --git a/extras/Introjucer/Source/BinaryData/jucer_OpenglComponentTemplate.cpp b/extras/Introjucer/Source/BinaryData/jucer_OpenglComponentTemplate.cpp index d98bd07e55..7701c4d1eb 100644 --- a/extras/Introjucer/Source/BinaryData/jucer_OpenglComponentTemplate.cpp +++ b/extras/Introjucer/Source/BinaryData/jucer_OpenglComponentTemplate.cpp @@ -27,10 +27,22 @@ public: ~MainContentComponent() { - shutdownAudio(); + shutdownOpenGL(); } - void paint (Graphics& g) + void initialise() override + { + } + + void shutdown() override + { + } + + void render() override + { + } + + void paint (Graphics& g) override { // (Our component is opaque, so we must completely fill the background with a solid colour) g.fillAll (Colours::black); @@ -39,7 +51,7 @@ public: // You can add your drawing code here! } - void resized() + void resized() override { // This is called when the MainContentComponent is resized. // If you add any child components, this is where you should diff --git a/extras/Introjucer/Source/Wizards/jucer_ProjectWizard_Animated.h b/extras/Introjucer/Source/Wizards/jucer_ProjectWizard_Animated.h index 6a40c04af5..f3afed12cd 100644 --- a/extras/Introjucer/Source/Wizards/jucer_ProjectWizard_Animated.h +++ b/extras/Introjucer/Source/Wizards/jucer_ProjectWizard_Animated.h @@ -50,7 +50,7 @@ struct AnimatedAppWizard : public NewProjectWizard // create main window String windowCpp = project.getFileTemplate ("jucer_AnimatedComponentTemplate_cpp") - .replace ("INCLUDE_JUCE", CodeHelpers::createIncludeStatement (project.getAppIncludeFile(), contentCompCpp), false); + .replace ("INCLUDE_JUCE", CodeHelpers::createIncludeStatement (project.getAppIncludeFile(), contentCompCpp), false); if (! FileHelpers::overwriteFileWithNewDataIfDifferent (contentCompCpp, windowCpp)) failedFiles.add (contentCompCpp.getFullPathName()); diff --git a/extras/Introjucer/Source/Wizards/jucer_ProjectWizard_openGL.h b/extras/Introjucer/Source/Wizards/jucer_ProjectWizard_openGL.h index 3018942cbe..ba6ce71807 100644 --- a/extras/Introjucer/Source/Wizards/jucer_ProjectWizard_openGL.h +++ b/extras/Introjucer/Source/Wizards/jucer_ProjectWizard_openGL.h @@ -49,8 +49,7 @@ struct OpenGLAppWizard : public NewProjectWizard String appHeaders (CodeHelpers::createIncludeStatement (project.getAppIncludeFile(), mainCppFile)); // create main window - - String windowCpp = project.getFileTemplate ("jucer_OpenglComponentTemplate_cpp") + String windowCpp = project.getFileTemplate ("jucer_OpenGLComponentTemplate_cpp") .replace ("INCLUDE_JUCE", CodeHelpers::createIncludeStatement (project.getAppIncludeFile(), contentCompCpp), false); if (! FileHelpers::overwriteFileWithNewDataIfDifferent (contentCompCpp, windowCpp)) diff --git a/modules/juce_box2d/box2d/Collision/b2collideedge.cpp b/modules/juce_box2d/box2d/Collision/b2collideedge.cpp new file mode 100644 index 0000000000..12a1a733ae --- /dev/null +++ b/modules/juce_box2d/box2d/Collision/b2collideedge.cpp @@ -0,0 +1,698 @@ +/* + * Copyright (c) 2007-2009 Erin Catto http://www.box2d.org + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#include "b2Collision.h" +#include "Shapes/b2CircleShape.h" +#include "Shapes/b2EdgeShape.h" +#include "Shapes/b2PolygonShape.h" + + +// Compute contact points for edge versus circle. +// This accounts for edge connectivity. +void b2CollideEdgeAndCircle(b2Manifold* manifold, + const b2EdgeShape* edgeA, const b2Transform& xfA, + const b2CircleShape* circleB, const b2Transform& xfB) +{ + manifold->pointCount = 0; + + // Compute circle in frame of edge + b2Vec2 Q = b2MulT(xfA, b2Mul(xfB, circleB->m_p)); + + b2Vec2 A = edgeA->m_vertex1, B = edgeA->m_vertex2; + b2Vec2 e = B - A; + + // Barycentric coordinates + float32 u = b2Dot(e, B - Q); + float32 v = b2Dot(e, Q - A); + + float32 radius = edgeA->m_radius + circleB->m_radius; + + b2ContactFeature cf; + cf.indexB = 0; + cf.typeB = b2ContactFeature::e_vertex; + + // Region A + if (v <= 0.0f) + { + b2Vec2 P = A; + b2Vec2 d = Q - P; + float32 dd = b2Dot(d, d); + if (dd > radius * radius) + { + return; + } + + // Is there an edge connected to A? + if (edgeA->m_hasVertex0) + { + b2Vec2 A1 = edgeA->m_vertex0; + b2Vec2 B1 = A; + b2Vec2 e1 = B1 - A1; + float32 u1 = b2Dot(e1, B1 - Q); + + // Is the circle in Region AB of the previous edge? + if (u1 > 0.0f) + { + return; + } + } + + cf.indexA = 0; + cf.typeA = b2ContactFeature::e_vertex; + manifold->pointCount = 1; + manifold->type = b2Manifold::e_circles; + manifold->localNormal.SetZero(); + manifold->localPoint = P; + manifold->points[0].id.key = 0; + manifold->points[0].id.cf = cf; + manifold->points[0].localPoint = circleB->m_p; + return; + } + + // Region B + if (u <= 0.0f) + { + b2Vec2 P = B; + b2Vec2 d = Q - P; + float32 dd = b2Dot(d, d); + if (dd > radius * radius) + { + return; + } + + // Is there an edge connected to B? + if (edgeA->m_hasVertex3) + { + b2Vec2 B2 = edgeA->m_vertex3; + b2Vec2 A2 = B; + b2Vec2 e2 = B2 - A2; + float32 v2 = b2Dot(e2, Q - A2); + + // Is the circle in Region AB of the next edge? + if (v2 > 0.0f) + { + return; + } + } + + cf.indexA = 1; + cf.typeA = b2ContactFeature::e_vertex; + manifold->pointCount = 1; + manifold->type = b2Manifold::e_circles; + manifold->localNormal.SetZero(); + manifold->localPoint = P; + manifold->points[0].id.key = 0; + manifold->points[0].id.cf = cf; + manifold->points[0].localPoint = circleB->m_p; + return; + } + + // Region AB + float32 den = b2Dot(e, e); + b2Assert(den > 0.0f); + b2Vec2 P = (1.0f / den) * (u * A + v * B); + b2Vec2 d = Q - P; + float32 dd = b2Dot(d, d); + if (dd > radius * radius) + { + return; + } + + b2Vec2 n(-e.y, e.x); + if (b2Dot(n, Q - A) < 0.0f) + { + n.Set(-n.x, -n.y); + } + n.Normalize(); + + cf.indexA = 0; + cf.typeA = b2ContactFeature::e_face; + manifold->pointCount = 1; + manifold->type = b2Manifold::e_faceA; + manifold->localNormal = n; + manifold->localPoint = A; + manifold->points[0].id.key = 0; + manifold->points[0].id.cf = cf; + manifold->points[0].localPoint = circleB->m_p; +} + +// This structure is used to keep track of the best separating axis. +struct b2EPAxis +{ + enum Type + { + e_unknown, + e_edgeA, + e_edgeB + }; + + Type type; + int32 index; + float32 separation; +}; + +// This holds polygon B expressed in frame A. +struct b2TempPolygon +{ + b2Vec2 vertices[b2_maxPolygonVertices]; + b2Vec2 normals[b2_maxPolygonVertices]; + int32 count; +}; + +// Reference face used for clipping +struct b2ReferenceFace +{ + int32 i1, i2; + + b2Vec2 v1, v2; + + b2Vec2 normal; + + b2Vec2 sideNormal1; + float32 sideOffset1; + + b2Vec2 sideNormal2; + float32 sideOffset2; +}; + +// This class collides and edge and a polygon, taking into account edge adjacency. +struct b2EPCollider +{ + void Collide(b2Manifold* manifold, const b2EdgeShape* edgeA, const b2Transform& xfA, + const b2PolygonShape* polygonB, const b2Transform& xfB); + b2EPAxis ComputeEdgeSeparation(); + b2EPAxis ComputePolygonSeparation(); + + enum VertexType + { + e_isolated, + e_concave, + e_convex + }; + + b2TempPolygon m_polygonB; + + b2Transform m_xf; + b2Vec2 m_centroidB; + b2Vec2 m_v0, m_v1, m_v2, m_v3; + b2Vec2 m_normal0, m_normal1, m_normal2; + b2Vec2 m_normal; + VertexType m_type1, m_type2; + b2Vec2 m_lowerLimit, m_upperLimit; + float32 m_radius; + bool m_front; +}; + +// Algorithm: +// 1. Classify v1 and v2 +// 2. Classify polygon centroid as front or back +// 3. Flip normal if necessary +// 4. Initialize normal range to [-pi, pi] about face normal +// 5. Adjust normal range according to adjacent edges +// 6. Visit each separating axes, only accept axes within the range +// 7. Return if _any_ axis indicates separation +// 8. Clip +void b2EPCollider::Collide(b2Manifold* manifold, const b2EdgeShape* edgeA, const b2Transform& xfA, + const b2PolygonShape* polygonB, const b2Transform& xfB) +{ + m_xf = b2MulT(xfA, xfB); + + m_centroidB = b2Mul(m_xf, polygonB->m_centroid); + + m_v0 = edgeA->m_vertex0; + m_v1 = edgeA->m_vertex1; + m_v2 = edgeA->m_vertex2; + m_v3 = edgeA->m_vertex3; + + bool hasVertex0 = edgeA->m_hasVertex0; + bool hasVertex3 = edgeA->m_hasVertex3; + + b2Vec2 edge1 = m_v2 - m_v1; + edge1.Normalize(); + m_normal1.Set(edge1.y, -edge1.x); + float32 offset1 = b2Dot(m_normal1, m_centroidB - m_v1); + float32 offset0 = 0.0f, offset2 = 0.0f; + bool convex1 = false, convex2 = false; + + // Is there a preceding edge? + if (hasVertex0) + { + b2Vec2 edge0 = m_v1 - m_v0; + edge0.Normalize(); + m_normal0.Set(edge0.y, -edge0.x); + convex1 = b2Cross(edge0, edge1) >= 0.0f; + offset0 = b2Dot(m_normal0, m_centroidB - m_v0); + } + + // Is there a following edge? + if (hasVertex3) + { + b2Vec2 edge2 = m_v3 - m_v2; + edge2.Normalize(); + m_normal2.Set(edge2.y, -edge2.x); + convex2 = b2Cross(edge1, edge2) > 0.0f; + offset2 = b2Dot(m_normal2, m_centroidB - m_v2); + } + + // Determine front or back collision. Determine collision normal limits. + if (hasVertex0 && hasVertex3) + { + if (convex1 && convex2) + { + m_front = offset0 >= 0.0f || offset1 >= 0.0f || offset2 >= 0.0f; + if (m_front) + { + m_normal = m_normal1; + m_lowerLimit = m_normal0; + m_upperLimit = m_normal2; + } + else + { + m_normal = -m_normal1; + m_lowerLimit = -m_normal1; + m_upperLimit = -m_normal1; + } + } + else if (convex1) + { + m_front = offset0 >= 0.0f || (offset1 >= 0.0f && offset2 >= 0.0f); + if (m_front) + { + m_normal = m_normal1; + m_lowerLimit = m_normal0; + m_upperLimit = m_normal1; + } + else + { + m_normal = -m_normal1; + m_lowerLimit = -m_normal2; + m_upperLimit = -m_normal1; + } + } + else if (convex2) + { + m_front = offset2 >= 0.0f || (offset0 >= 0.0f && offset1 >= 0.0f); + if (m_front) + { + m_normal = m_normal1; + m_lowerLimit = m_normal1; + m_upperLimit = m_normal2; + } + else + { + m_normal = -m_normal1; + m_lowerLimit = -m_normal1; + m_upperLimit = -m_normal0; + } + } + else + { + m_front = offset0 >= 0.0f && offset1 >= 0.0f && offset2 >= 0.0f; + if (m_front) + { + m_normal = m_normal1; + m_lowerLimit = m_normal1; + m_upperLimit = m_normal1; + } + else + { + m_normal = -m_normal1; + m_lowerLimit = -m_normal2; + m_upperLimit = -m_normal0; + } + } + } + else if (hasVertex0) + { + if (convex1) + { + m_front = offset0 >= 0.0f || offset1 >= 0.0f; + if (m_front) + { + m_normal = m_normal1; + m_lowerLimit = m_normal0; + m_upperLimit = -m_normal1; + } + else + { + m_normal = -m_normal1; + m_lowerLimit = m_normal1; + m_upperLimit = -m_normal1; + } + } + else + { + m_front = offset0 >= 0.0f && offset1 >= 0.0f; + if (m_front) + { + m_normal = m_normal1; + m_lowerLimit = m_normal1; + m_upperLimit = -m_normal1; + } + else + { + m_normal = -m_normal1; + m_lowerLimit = m_normal1; + m_upperLimit = -m_normal0; + } + } + } + else if (hasVertex3) + { + if (convex2) + { + m_front = offset1 >= 0.0f || offset2 >= 0.0f; + if (m_front) + { + m_normal = m_normal1; + m_lowerLimit = -m_normal1; + m_upperLimit = m_normal2; + } + else + { + m_normal = -m_normal1; + m_lowerLimit = -m_normal1; + m_upperLimit = m_normal1; + } + } + else + { + m_front = offset1 >= 0.0f && offset2 >= 0.0f; + if (m_front) + { + m_normal = m_normal1; + m_lowerLimit = -m_normal1; + m_upperLimit = m_normal1; + } + else + { + m_normal = -m_normal1; + m_lowerLimit = -m_normal2; + m_upperLimit = m_normal1; + } + } + } + else + { + m_front = offset1 >= 0.0f; + if (m_front) + { + m_normal = m_normal1; + m_lowerLimit = -m_normal1; + m_upperLimit = -m_normal1; + } + else + { + m_normal = -m_normal1; + m_lowerLimit = m_normal1; + m_upperLimit = m_normal1; + } + } + + // Get polygonB in frameA + m_polygonB.count = polygonB->m_vertexCount; + for (int32 i = 0; i < polygonB->m_vertexCount; ++i) + { + m_polygonB.vertices[i] = b2Mul(m_xf, polygonB->m_vertices[i]); + m_polygonB.normals[i] = b2Mul(m_xf.q, polygonB->m_normals[i]); + } + + m_radius = 2.0f * b2_polygonRadius; + + manifold->pointCount = 0; + + b2EPAxis edgeAxis = ComputeEdgeSeparation(); + + // If no valid normal can be found than this edge should not collide. + if (edgeAxis.type == b2EPAxis::e_unknown) + { + return; + } + + if (edgeAxis.separation > m_radius) + { + return; + } + + b2EPAxis polygonAxis = ComputePolygonSeparation(); + if (polygonAxis.type != b2EPAxis::e_unknown && polygonAxis.separation > m_radius) + { + return; + } + + // Use hysteresis for jitter reduction. + const float32 k_relativeTol = 0.98f; + const float32 k_absoluteTol = 0.001f; + + b2EPAxis primaryAxis; + if (polygonAxis.type == b2EPAxis::e_unknown) + { + primaryAxis = edgeAxis; + } + else if (polygonAxis.separation > k_relativeTol * edgeAxis.separation + k_absoluteTol) + { + primaryAxis = polygonAxis; + } + else + { + primaryAxis = edgeAxis; + } + + b2ClipVertex ie[2]; + b2ReferenceFace rf; + if (primaryAxis.type == b2EPAxis::e_edgeA) + { + manifold->type = b2Manifold::e_faceA; + + // Search for the polygon normal that is most anti-parallel to the edge normal. + int32 bestIndex = 0; + float32 bestValue = b2Dot(m_normal, m_polygonB.normals[0]); + for (int32 i = 1; i < m_polygonB.count; ++i) + { + float32 value = b2Dot(m_normal, m_polygonB.normals[i]); + if (value < bestValue) + { + bestValue = value; + bestIndex = i; + } + } + + int32 i1 = bestIndex; + int32 i2 = i1 + 1 < m_polygonB.count ? i1 + 1 : 0; + + ie[0].v = m_polygonB.vertices[i1]; + ie[0].id.cf.indexA = 0; + ie[0].id.cf.indexB = (uint8) i1; + ie[0].id.cf.typeA = b2ContactFeature::e_face; + ie[0].id.cf.typeB = b2ContactFeature::e_vertex; + + ie[1].v = m_polygonB.vertices[i2]; + ie[1].id.cf.indexA = 0; + ie[1].id.cf.indexB = (uint8) i2; + ie[1].id.cf.typeA = b2ContactFeature::e_face; + ie[1].id.cf.typeB = b2ContactFeature::e_vertex; + + if (m_front) + { + rf.i1 = 0; + rf.i2 = 1; + rf.v1 = m_v1; + rf.v2 = m_v2; + rf.normal = m_normal1; + } + else + { + rf.i1 = 1; + rf.i2 = 0; + rf.v1 = m_v2; + rf.v2 = m_v1; + rf.normal = -m_normal1; + } + } + else + { + manifold->type = b2Manifold::e_faceB; + + ie[0].v = m_v1; + ie[0].id.cf.indexA = 0; + ie[0].id.cf.indexB = (uint8) primaryAxis.index; + ie[0].id.cf.typeA = b2ContactFeature::e_vertex; + ie[0].id.cf.typeB = b2ContactFeature::e_face; + + ie[1].v = m_v2; + ie[1].id.cf.indexA = 0; + ie[1].id.cf.indexB = (uint8) primaryAxis.index; + ie[1].id.cf.typeA = b2ContactFeature::e_vertex; + ie[1].id.cf.typeB = b2ContactFeature::e_face; + + rf.i1 = primaryAxis.index; + rf.i2 = rf.i1 + 1 < m_polygonB.count ? rf.i1 + 1 : 0; + rf.v1 = m_polygonB.vertices[rf.i1]; + rf.v2 = m_polygonB.vertices[rf.i2]; + rf.normal = m_polygonB.normals[rf.i1]; + } + + rf.sideNormal1.Set(rf.normal.y, -rf.normal.x); + rf.sideNormal2 = -rf.sideNormal1; + rf.sideOffset1 = b2Dot(rf.sideNormal1, rf.v1); + rf.sideOffset2 = b2Dot(rf.sideNormal2, rf.v2); + + // Clip incident edge against extruded edge1 side edges. + b2ClipVertex clipPoints1[2]; + b2ClipVertex clipPoints2[2]; + int32 np; + + // Clip to box side 1 + np = b2ClipSegmentToLine(clipPoints1, ie, rf.sideNormal1, rf.sideOffset1, rf.i1); + + if (np < b2_maxManifoldPoints) + { + return; + } + + // Clip to negative box side 1 + np = b2ClipSegmentToLine(clipPoints2, clipPoints1, rf.sideNormal2, rf.sideOffset2, rf.i2); + + if (np < b2_maxManifoldPoints) + { + return; + } + + // Now clipPoints2 contains the clipped points. + if (primaryAxis.type == b2EPAxis::e_edgeA) + { + manifold->localNormal = rf.normal; + manifold->localPoint = rf.v1; + } + else + { + manifold->localNormal = polygonB->m_normals[rf.i1]; + manifold->localPoint = polygonB->m_vertices[rf.i1]; + } + + int32 pointCount = 0; + for (int32 i = 0; i < b2_maxManifoldPoints; ++i) + { + float32 separation; + + separation = b2Dot(rf.normal, clipPoints2[i].v - rf.v1); + + if (separation <= m_radius) + { + b2ManifoldPoint* cp = manifold->points + pointCount; + + if (primaryAxis.type == b2EPAxis::e_edgeA) + { + cp->localPoint = b2MulT(m_xf, clipPoints2[i].v); + cp->id = clipPoints2[i].id; + } + else + { + cp->localPoint = clipPoints2[i].v; + cp->id.cf.typeA = clipPoints2[i].id.cf.typeB; + cp->id.cf.typeB = clipPoints2[i].id.cf.typeA; + cp->id.cf.indexA = clipPoints2[i].id.cf.indexB; + cp->id.cf.indexB = clipPoints2[i].id.cf.indexA; + } + + ++pointCount; + } + } + + manifold->pointCount = pointCount; +} + +b2EPAxis b2EPCollider::ComputeEdgeSeparation() +{ + b2EPAxis axis; + axis.type = b2EPAxis::e_edgeA; + axis.index = m_front ? 0 : 1; + axis.separation = FLT_MAX; + + for (int32 i = 0; i < m_polygonB.count; ++i) + { + float32 s = b2Dot(m_normal, m_polygonB.vertices[i] - m_v1); + if (s < axis.separation) + { + axis.separation = s; + } + } + + return axis; +} + +b2EPAxis b2EPCollider::ComputePolygonSeparation() +{ + b2EPAxis axis; + axis.type = b2EPAxis::e_unknown; + axis.index = -1; + axis.separation = -FLT_MAX; + + b2Vec2 perp(-m_normal.y, m_normal.x); + + for (int32 i = 0; i < m_polygonB.count; ++i) + { + b2Vec2 n = -m_polygonB.normals[i]; + + float32 s1 = b2Dot(n, m_polygonB.vertices[i] - m_v1); + float32 s2 = b2Dot(n, m_polygonB.vertices[i] - m_v2); + float32 s = b2Min(s1, s2); + + if (s > m_radius) + { + // No collision + axis.type = b2EPAxis::e_edgeB; + axis.index = i; + axis.separation = s; + return axis; + } + + // Adjacency + if (b2Dot(n, perp) >= 0.0f) + { + if (b2Dot(n - m_upperLimit, m_normal) < -b2_angularSlop) + { + continue; + } + } + else + { + if (b2Dot(n - m_lowerLimit, m_normal) < -b2_angularSlop) + { + continue; + } + } + + if (s > axis.separation) + { + axis.type = b2EPAxis::e_edgeB; + axis.index = i; + axis.separation = s; + } + } + + return axis; +} + +void b2CollideEdgeAndPolygon( b2Manifold* manifold, + const b2EdgeShape* edgeA, const b2Transform& xfA, + const b2PolygonShape* polygonB, const b2Transform& xfB) +{ + b2EPCollider collider; + collider.Collide(manifold, edgeA, xfA, polygonB, xfB); +} diff --git a/modules/juce_box2d/box2d/Collision/b2collision.cpp b/modules/juce_box2d/box2d/Collision/b2collision.cpp new file mode 100644 index 0000000000..529498f89d --- /dev/null +++ b/modules/juce_box2d/box2d/Collision/b2collision.cpp @@ -0,0 +1,249 @@ +/* +* Copyright (c) 2007-2009 Erin Catto http://www.box2d.org +* +* This software is provided 'as-is', without any express or implied +* warranty. In no event will the authors be held liable for any damages +* arising from the use of this software. +* Permission is granted to anyone to use this software for any purpose, +* including commercial applications, and to alter it and redistribute it +* freely, subject to the following restrictions: +* 1. The origin of this software must not be misrepresented; you must not +* claim that you wrote the original software. If you use this software +* in a product, an acknowledgment in the product documentation would be +* appreciated but is not required. +* 2. Altered source versions must be plainly marked as such, and must not be +* misrepresented as being the original software. +* 3. This notice may not be removed or altered from any source distribution. +*/ + +#include "b2Collision.h" +#include "b2Distance.h" + +void b2WorldManifold::Initialize(const b2Manifold* manifold, + const b2Transform& xfA, float32 radiusA, + const b2Transform& xfB, float32 radiusB) +{ + if (manifold->pointCount == 0) + { + return; + } + + switch (manifold->type) + { + case b2Manifold::e_circles: + { + normal.Set(1.0f, 0.0f); + b2Vec2 pointA = b2Mul(xfA, manifold->localPoint); + b2Vec2 pointB = b2Mul(xfB, manifold->points[0].localPoint); + if (b2DistanceSquared(pointA, pointB) > b2_epsilon * b2_epsilon) + { + normal = pointB - pointA; + normal.Normalize(); + } + + b2Vec2 cA = pointA + radiusA * normal; + b2Vec2 cB = pointB - radiusB * normal; + points[0] = 0.5f * (cA + cB); + } + break; + + case b2Manifold::e_faceA: + { + normal = b2Mul(xfA.q, manifold->localNormal); + b2Vec2 planePoint = b2Mul(xfA, manifold->localPoint); + + for (int32 i = 0; i < manifold->pointCount; ++i) + { + b2Vec2 clipPoint = b2Mul(xfB, manifold->points[i].localPoint); + b2Vec2 cA = clipPoint + (radiusA - b2Dot(clipPoint - planePoint, normal)) * normal; + b2Vec2 cB = clipPoint - radiusB * normal; + points[i] = 0.5f * (cA + cB); + } + } + break; + + case b2Manifold::e_faceB: + { + normal = b2Mul(xfB.q, manifold->localNormal); + b2Vec2 planePoint = b2Mul(xfB, manifold->localPoint); + + for (int32 i = 0; i < manifold->pointCount; ++i) + { + b2Vec2 clipPoint = b2Mul(xfA, manifold->points[i].localPoint); + b2Vec2 cB = clipPoint + (radiusB - b2Dot(clipPoint - planePoint, normal)) * normal; + b2Vec2 cA = clipPoint - radiusA * normal; + points[i] = 0.5f * (cA + cB); + } + + // Ensure normal points from A to B. + normal = -normal; + } + break; + } +} + +void b2GetPointStates(b2PointState state1[b2_maxManifoldPoints], b2PointState state2[b2_maxManifoldPoints], + const b2Manifold* manifold1, const b2Manifold* manifold2) +{ + for (int32 i = 0; i < b2_maxManifoldPoints; ++i) + { + state1[i] = b2_nullState; + state2[i] = b2_nullState; + } + + // Detect persists and removes. + for (int32 i = 0; i < manifold1->pointCount; ++i) + { + b2ContactID id = manifold1->points[i].id; + + state1[i] = b2_removeState; + + for (int32 j = 0; j < manifold2->pointCount; ++j) + { + if (manifold2->points[j].id.key == id.key) + { + state1[i] = b2_persistState; + break; + } + } + } + + // Detect persists and adds. + for (int32 i = 0; i < manifold2->pointCount; ++i) + { + b2ContactID id = manifold2->points[i].id; + + state2[i] = b2_addState; + + for (int32 j = 0; j < manifold1->pointCount; ++j) + { + if (manifold1->points[j].id.key == id.key) + { + state2[i] = b2_persistState; + break; + } + } + } +} + +// From Real-time Collision Detection, p179. +bool b2AABB::RayCast(b2RayCastOutput* output, const b2RayCastInput& input) const +{ + float32 tmin = -b2_maxFloat; + float32 tmax = b2_maxFloat; + + b2Vec2 p = input.p1; + b2Vec2 d = input.p2 - input.p1; + b2Vec2 absD = b2Abs(d); + + b2Vec2 normal; + + for (int32 i = 0; i < 2; ++i) + { + if (absD(i) < b2_epsilon) + { + // Parallel. + if (p(i) < lowerBound(i) || upperBound(i) < p(i)) + { + return false; + } + } + else + { + float32 inv_d = 1.0f / d(i); + float32 t1 = (lowerBound(i) - p(i)) * inv_d; + float32 t2 = (upperBound(i) - p(i)) * inv_d; + + // Sign of the normal vector. + float32 s = -1.0f; + + if (t1 > t2) + { + b2Swap(t1, t2); + s = 1.0f; + } + + // Push the min up + if (t1 > tmin) + { + normal.SetZero(); + normal(i) = s; + tmin = t1; + } + + // Pull the max down + tmax = b2Min(tmax, t2); + + if (tmin > tmax) + { + return false; + } + } + } + + // Does the ray start inside the box? + // Does the ray intersect beyond the max fraction? + if (tmin < 0.0f || input.maxFraction < tmin) + { + return false; + } + + // Intersection. + output->fraction = tmin; + output->normal = normal; + return true; +} + +// Sutherland-Hodgman clipping. +int32 b2ClipSegmentToLine(b2ClipVertex vOut[2], const b2ClipVertex vIn[2], + const b2Vec2& normal, float32 offset, int32 vertexIndexA) +{ + // Start with no output points + int32 numOut = 0; + + // Calculate the distance of end points to the line + float32 distance0 = b2Dot(normal, vIn[0].v) - offset; + float32 distance1 = b2Dot(normal, vIn[1].v) - offset; + + // If the points are behind the plane + if (distance0 <= 0.0f) vOut[numOut++] = vIn[0]; + if (distance1 <= 0.0f) vOut[numOut++] = vIn[1]; + + // If the points are on different sides of the plane + if (distance0 * distance1 < 0.0f) + { + // Find intersection point of edge and plane + float32 interp = distance0 / (distance0 - distance1); + vOut[numOut].v = vIn[0].v + interp * (vIn[1].v - vIn[0].v); + + // VertexA is hitting edgeB. + vOut[numOut].id.cf.indexA = (uint8) vertexIndexA; + vOut[numOut].id.cf.indexB = vIn[0].id.cf.indexB; + vOut[numOut].id.cf.typeA = b2ContactFeature::e_vertex; + vOut[numOut].id.cf.typeB = b2ContactFeature::e_face; + ++numOut; + } + + return numOut; +} + +bool b2TestOverlap( const b2Shape* shapeA, int32 indexA, + const b2Shape* shapeB, int32 indexB, + const b2Transform& xfA, const b2Transform& xfB) +{ + b2DistanceInput input; + input.proxyA.Set(shapeA, indexA); + input.proxyB.Set(shapeB, indexB); + input.transformA = xfA; + input.transformB = xfB; + input.useRadii = true; + + b2SimplexCache cache; + cache.count = 0; + + b2DistanceOutput output; + + b2Distance(&output, &cache, &input); + + return output.distance < 10.0f * b2_epsilon; +} diff --git a/modules/juce_browser_plugin_client/wrapper/juce_activex_gluecode.cpp b/modules/juce_browser_plugin_client/wrapper/juce_activex_gluecode.cpp new file mode 100644 index 0000000000..28235a1fdf --- /dev/null +++ b/modules/juce_browser_plugin_client/wrapper/juce_activex_gluecode.cpp @@ -0,0 +1,947 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 + + Details of these licenses can be found at: www.gnu.org/licenses + + JUCE is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.juce.com for more information. + + ============================================================================== +*/ + +//============================================================================== +/* + This file contains all the gubbins to create an ActiveX browser plugin that + wraps your BrowserPluginComponent object. + +*/ +//============================================================================== +#if _MSC_VER + +//============================================================================== +#include +#include +#include +#pragma warning (disable:4584) + +#include "../juce_browser_plugin.h" +using namespace juce; + +#include "juce_BrowserPluginComponent.h" + +#ifndef JuceBrowserPlugin_ActiveXCLSID +#error "For an activeX plugin, you need to define JuceBrowserPlugin_ActiveXCLSID in your BrowserPluginCharacteristics.h file!" +#endif + +//============================================================================== +#if JUCE_DEBUG +static int numDOWID = 0, numJuceSO = 0; +#endif + +#define log(a) DBG(a) + +// Cunning trick used to add functions to export list without messing about with .def files. +#define EXPORTED_FUNCTION comment(linker, "/EXPORT:" __FUNCTION__ "=" __FUNCDNAME__) + +//============================================================================== +static void juceVarToVariant (const var& v, VARIANT& dest); +static var variantTojuceVar (const VARIANT& v); + +//============================================================================== +// Takes care of the logic in invoking var methods from IDispatch callbacks. +class IDispatchHelper +{ +public: + IDispatchHelper() {} + + String getStringFromDISPID (const DISPID hash) const + { + return identifierNames [identifierIDs.indexOf (hash)]; + } + + DISPID getDISPIDForName (const String& name) + { + const int i = identifierNames.indexOf (String (name)); + + if (i >= 0) + return identifierIDs[i]; + + const DISPID newID = (DISPID) name.hashCode64(); + identifierNames.add (name); + identifierIDs.add (newID); + return newID; + } + + HRESULT doGetIDsOfNames (LPOLESTR* rgszNames, UINT cNames, DISPID* rgDispId) + { + for (unsigned int i = 0; i < cNames; ++i) + rgDispId[i] = getDISPIDForName (rgszNames[i]); + + return S_OK; + } + + HRESULT doInvoke (const var& v, + DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pDispParams, + VARIANT* pVarResult, EXCEPINFO* pExcepInfo, UINT* puArgErr) + { + const Identifier memberId (getStringFromDISPID (dispIdMember)); + + DynamicObject* const object = v.getDynamicObject(); + + if (memberId.toString().isEmpty() || object == nullptr) + return DISP_E_MEMBERNOTFOUND; + + if ((wFlags & DISPATCH_METHOD) != 0) + { + if (object->hasMethod (memberId)) + { + const int numArgs = pDispParams == nullptr ? 0 : pDispParams->cArgs; + var result; + + if (numArgs == 0) + { + result = v.call (memberId); + } + else + { + Array args; + for (int j = numArgs; --j >= 0;) + args.add (variantTojuceVar (pDispParams->rgvarg[j])); + + result = v.invoke (memberId, numArgs == 0 ? nullptr : args.getRawDataPointer(), numArgs); + } + + if (pVarResult != nullptr) + juceVarToVariant (result, *pVarResult); + + return S_OK; + } + } + else if ((wFlags & DISPATCH_PROPERTYGET) != 0) + { + if (object->hasProperty (memberId) && pVarResult != nullptr) + { + juceVarToVariant (object->getProperty (memberId), *pVarResult); + return S_OK; + } + } + else if ((wFlags & DISPATCH_PROPERTYPUT) != 0) + { + if (pDispParams != nullptr && pDispParams->cArgs > 0) + { + object->setProperty (memberId, variantTojuceVar (pDispParams->rgvarg[0])); + return S_OK; + } + } + + return DISP_E_MEMBERNOTFOUND; + } + +private: + Array identifierIDs; + StringArray identifierNames; + + JUCE_DECLARE_NON_COPYABLE (IDispatchHelper) +}; + +//============================================================================== +// Makes a var look like an IDispatch +class IDispatchWrappingDynamicObject : public IDispatch +{ +public: + IDispatchWrappingDynamicObject (const var& object_) + : object (object_), + refCount (1) + { + DBG ("num Juce wrapper objs: " + String (++numJuceSO)); + } + + virtual ~IDispatchWrappingDynamicObject() + { + DBG ("num Juce wrapper objs: " + String (--numJuceSO)); + } + + HRESULT __stdcall QueryInterface (REFIID id, void __RPC_FAR* __RPC_FAR* result) + { + if (id == IID_IUnknown) { AddRef(); *result = (IUnknown*) this; return S_OK; } + if (id == IID_IDispatch) { AddRef(); *result = (IDispatch*) this; return S_OK; } + + *result = 0; + return E_NOINTERFACE; + } + + ULONG __stdcall AddRef() { return ++refCount; } + ULONG __stdcall Release() { const int r = --refCount; if (r == 0) delete this; return r; } + + HRESULT __stdcall GetTypeInfoCount (UINT*) { return E_NOTIMPL; } + HRESULT __stdcall GetTypeInfo (UINT, LCID, ITypeInfo**) { return E_NOTIMPL; } + + HRESULT __stdcall GetIDsOfNames (REFIID riid, LPOLESTR* rgszNames, UINT cNames, + LCID lcid, DISPID* rgDispId) + { + return iDispatchHelper.doGetIDsOfNames (rgszNames, cNames, rgDispId); + } + + HRESULT __stdcall Invoke (DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, + DISPPARAMS* pDispParams, VARIANT* pVarResult, + EXCEPINFO* pExcepInfo, UINT* puArgErr) + { + return iDispatchHelper.doInvoke (object, dispIdMember, riid, lcid, wFlags, pDispParams, + pVarResult, pExcepInfo, puArgErr); + } + +private: + //============================================================================== + var object; + int refCount; + IDispatchHelper iDispatchHelper; + + JUCE_DECLARE_NON_COPYABLE (IDispatchWrappingDynamicObject) +}; + + +//============================================================================== +// Makes an IDispatch look like a var +class DynamicObjectWrappingIDispatch : public DynamicObject +{ +public: + DynamicObjectWrappingIDispatch (IDispatch* const source_) + : source (source_) + { + source->AddRef(); + log ("num IDispatch wrapper objs: " + String (++numDOWID)); + } + + ~DynamicObjectWrappingIDispatch() + { + source->Release(); + log ("num IDispatch wrapper objs: " + String (--numDOWID)); + } + + var getProperty (const Identifier& propertyName) const override + { + const String nameCopy (propertyName.toString()); + LPCOLESTR name = nameCopy.toUTF16(); + DISPID id = 0; + if (source->GetIDsOfNames (IID_NULL, (LPOLESTR*) &name, 1, 0, &id) == S_OK) + { + EXCEPINFO excepInfo; + DISPPARAMS params; + zerostruct (params); + UINT argError; + VARIANT result; + zerostruct (result); + + if (source->Invoke (id, IID_NULL, 0, DISPATCH_PROPERTYGET, + ¶ms, &result, &excepInfo, &argError) == S_OK) + { + var v (variantTojuceVar (result)); + VariantClear (&result); + return v; + } + } + + return var(); + } + + bool hasProperty (const Identifier& propertyName) const override + { + const String nameCopy (propertyName.toString()); + LPCOLESTR name = nameCopy.toUTF16(); + DISPID id = 0; + return source->GetIDsOfNames (IID_NULL, (LPOLESTR*) &name, 1, 0, &id) == S_OK; + } + + void setProperty (const Identifier& propertyName, const var& newValue) override + { + const String nameCopy (propertyName.toString()); + LPCOLESTR name = nameCopy.toUTF16(); + DISPID id = 0; + if (source->GetIDsOfNames (IID_NULL, (LPOLESTR*) &name, 1, 0, &id) == S_OK) + { + VARIANT param; + zerostruct (param); + juceVarToVariant (newValue, param); + + DISPPARAMS dispParams; + zerostruct (dispParams); + dispParams.cArgs = 1; + dispParams.rgvarg = ¶m; + EXCEPINFO excepInfo; + zerostruct (excepInfo); + + VARIANT result; + zerostruct (result); + UINT argError = 0; + + if (source->Invoke (id, IID_NULL, 0, DISPATCH_PROPERTYPUT, + &dispParams, &result, &excepInfo, &argError) == S_OK) + { + VariantClear (&result); + } + + VariantClear (¶m); + } + } + + void removeProperty (const Identifier& propertyName) override + { + setProperty (propertyName, var()); + } + + bool hasMethod (const Identifier& methodName) const override + { + const String nameCopy (methodName.toString()); + LPCOLESTR name = nameCopy.toUTF16(); + DISPID id = 0; + return source->GetIDsOfNames (IID_NULL, (LPOLESTR*) &name, 1, 0, &id) == S_OK; + } + + var invokeMethod (Identifier methodName, const var::NativeFunctionArgs& args) override + { + var returnValue; + const String nameCopy (methodName.toString()); + LPCOLESTR name = nameCopy.toUTF16(); + DISPID id = 0; + if (source->GetIDsOfNames (IID_NULL, (LPOLESTR*) &name, 1, 0, &id) == S_OK) + { + HeapBlock params; + params.calloc (args.numArguments + 1); + + for (int i = 0; i < args.numArguments; ++i) + juceVarToVariant (args.arguments[(args.numArguments - 1) - i], params[i]); + + DISPPARAMS dispParams; + zerostruct (dispParams); + dispParams.cArgs = args.numArguments; + dispParams.rgvarg = params; + + EXCEPINFO excepInfo; + zerostruct (excepInfo); + + VARIANT result; + zerostruct (result); + UINT argError = 0; + + if (source->Invoke (id, IID_NULL, 0, DISPATCH_METHOD, + &dispParams, &result, &excepInfo, &argError) == S_OK) + { + returnValue = variantTojuceVar (result); + VariantClear (&result); + } + } + + return returnValue; + } + +private: + IDispatch* const source; + + JUCE_DECLARE_NON_COPYABLE (DynamicObjectWrappingIDispatch) +}; + + +//============================================================================== +void juceVarToVariant (const var& v, VARIANT& dest) +{ + if (v.isVoid()) + { + dest.vt = VT_EMPTY; + } + else if (v.isInt()) + { + dest.vt = VT_INT; + dest.intVal = (int) v; + } + else if (v.isBool()) + { + dest.vt = VT_BOOL; + dest.boolVal = (int) v; + } + else if (v.isDouble()) + { + dest.vt = VT_R8; + dest.dblVal = (double) v; + } + else if (v.isString()) + { + dest.vt = VT_BSTR; + dest.bstrVal = SysAllocString (v.toString().toUTF16()); + } + else if (v.getDynamicObject() != nullptr) + { + dest.vt = VT_DISPATCH; + dest.pdispVal = new IDispatchWrappingDynamicObject (v); + } + else if (v.isMethod()) + { + dest.vt = VT_EMPTY; + } +} + +var variantTojuceVar (const VARIANT& v) +{ + if ((v.vt & VT_ARRAY) != 0) + { + //xxx + } + else + { + switch (v.vt & ~VT_BYREF) + { + case VT_VOID: + case VT_EMPTY: return var(); + case VT_I1: return var ((int) v.cVal); + case VT_I2: return var ((int) v.iVal); + case VT_I4: return var ((int) v.lVal); + case VT_I8: return var (String (v.llVal)); + case VT_UI1: return var ((int) v.bVal); + case VT_UI2: return var ((int) v.uiVal); + case VT_UI4: return var ((int) v.ulVal); + case VT_UI8: return var (String (v.ullVal)); + case VT_INT: return var ((int) v.intVal); + case VT_UINT: return var ((int) v.uintVal); + case VT_R4: return var ((double) v.fltVal); + case VT_R8: return var ((double) v.dblVal); + case VT_BSTR: return var (String (v.bstrVal)); + case VT_BOOL: return var (v.boolVal ? true : false); + case VT_DISPATCH: return var (new DynamicObjectWrappingIDispatch (v.pdispVal)); + default: break; + } + } + + return var(); +} + +//============================================================================== +// This acts as the embedded HWND +class AXBrowserPluginHolderComponent : public Component +{ +public: + AXBrowserPluginHolderComponent() + : parentHWND (0), + browser (nullptr) + { + setOpaque (true); + setWantsKeyboardFocus (false); + + addAndMakeVisible (child = createBrowserPlugin()); + jassert (child != nullptr); // You have to create one of these! + } + + ~AXBrowserPluginHolderComponent() + { + setWindow (nullptr); + child = nullptr; + } + + //============================================================================== + void paint (Graphics& g) override + { + if (child == nullptr || ! child->isOpaque()) + g.fillAll (Colours::white); + } + + void resized() override + { + if (child != nullptr) + child->setBounds (getLocalBounds()); + } + + var getObject() { return child->getJavascriptObject(); } + + void setWindow (IOleInPlaceSite* site) + { + if (browser != nullptr) + { + browser->Release(); + browser = nullptr; + } + + HWND newHWND = 0; + + if (site != nullptr) + { + site->GetWindow (&newHWND); + + IServiceProvider* sp = nullptr; + site->QueryInterface (IID_IServiceProvider, (void**) &sp); + + if (sp != nullptr) + { + sp->QueryService (IID_IWebBrowserApp, IID_IWebBrowser2, (void**) &browser); + sp->Release(); + } + } + + if (parentHWND != newHWND) + { + removeFromDesktop(); + setVisible (false); + + parentHWND = newHWND; + + if (parentHWND != 0) + { + addToDesktop (0); + + HWND ourHWND = (HWND) getWindowHandle(); + SetParent (ourHWND, parentHWND); + DWORD val = GetWindowLong (ourHWND, GWL_STYLE); + val = (val & ~WS_POPUP) | WS_CHILD; + SetWindowLong (ourHWND, GWL_STYLE, val); + + setVisible (true); + } + } + + if (site != nullptr) + site->OnInPlaceActivate(); + } + + String getBrowserURL() const + { + if (browser == nullptr) + return String::empty; + + BSTR url = nullptr; + browser->get_LocationURL (&url); + return URL::removeEscapeChars (url); + } + +private: + //============================================================================== + ScopedPointer child; + HWND parentHWND; + IWebBrowser2* browser; + + JUCE_DECLARE_NON_COPYABLE (AXBrowserPluginHolderComponent) +}; + +//============================================================================== +extern String browserVersionDesc; + +static String getExePath() +{ + TCHAR moduleFile [2048] = { 0 }; + GetModuleFileName (0, moduleFile, 2048); + return moduleFile; +} + +static String getExeVersion (const String& exeFileName, const String& fieldName) +{ + DWORD pointlessWin32Variable; + DWORD size = GetFileVersionInfoSize (exeFileName.toUTF16(), &pointlessWin32Variable); + + if (size > 0) + { + HeapBlock exeInfo; + exeInfo.calloc (size); + + if (GetFileVersionInfo (exeFileName.toUTF16(), 0, size, exeInfo)) + { + TCHAR* result = nullptr; + unsigned int resultLen = 0; + + // try the 1200 codepage (Unicode) + String queryStr ("\\StringFileInfo\\040904B0\\" + fieldName); + + if (! VerQueryValue (exeInfo, (LPTSTR) queryStr.toUTF16().getAddress(), (void**) &result, &resultLen)) + { + // try the 1252 codepage (Windows Multilingual) + queryStr = "\\StringFileInfo\\040904E4\\" + fieldName; + VerQueryValue (exeInfo, (LPTSTR) queryStr.toUTF16().getAddress(), (void**) &result, &resultLen); + } + + return String (result, resultLen); + } + } + + return String::empty; +} + +static int numActivePlugins = 0; + +class JuceActiveXObject : public IUnknown, + public IDispatch, + public IObjectWithSite, + public IObjectSafety, + public IOleInPlaceObject +{ +public: + JuceActiveXObject() + : site (nullptr), refCount (0) + { + log ("JuceActiveXObject"); + } + + ~JuceActiveXObject() + { + log ("~JuceActiveXObject"); + holderComp = nullptr; + } + + HRESULT __stdcall QueryInterface (REFIID id, void __RPC_FAR* __RPC_FAR* result) + { + if (id == IID_IUnknown) { AddRef(); *result = (IUnknown*) this; return S_OK; } + if (id == IID_IDispatch) { AddRef(); *result = (IDispatch*) this; return S_OK; } + if (id == IID_IObjectWithSite) { AddRef(); *result = (IObjectWithSite*) this; return S_OK; } + if (id == IID_IObjectSafety) { AddRef(); *result = (IObjectSafety*) this; return S_OK; } + if (id == IID_IOleInPlaceObject) { AddRef(); *result = (IOleInPlaceObject*) this; return S_OK; } + if (id == IID_IOleWindow) { AddRef(); *result = (IOleWindow*) (IOleInPlaceObject*) this; return S_OK; } + + *result = 0; + return E_NOINTERFACE; + } + + ULONG __stdcall AddRef() { return ++refCount; } + ULONG __stdcall Release() { const int r = --refCount; if (r == 0) delete this; return r; } + + HRESULT __stdcall GetTypeInfoCount (UINT* pctinfo) { return E_NOTIMPL; } + HRESULT __stdcall GetTypeInfo (UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo) { return E_NOTIMPL; } + + HRESULT __stdcall GetIDsOfNames (REFIID riid, LPOLESTR* rgszNames, UINT cNames, + LCID lcid, DISPID* rgDispId) + { + return iDispatchHelper.doGetIDsOfNames (rgszNames, cNames, rgDispId); + } + + HRESULT __stdcall Invoke (DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, + DISPPARAMS* pDispParams, VARIANT* pVarResult, + EXCEPINFO* pExcepInfo, UINT* puArgErr) + { + if (holderComp == nullptr) + return DISP_E_MEMBERNOTFOUND; + + return iDispatchHelper.doInvoke (holderComp->getObject(), + dispIdMember, riid, lcid, wFlags, pDispParams, + pVarResult, pExcepInfo, puArgErr); + } + + HRESULT __stdcall SetSite (IUnknown* newSite) + { + if (newSite != site) + { + if (site != nullptr) + site->Release(); + + site = newSite; + + if (site != nullptr) + { + site->AddRef(); + + IOleInPlaceSite* inPlaceSite = nullptr; + site->QueryInterface (IID_IOleInPlaceSite, (void**) &inPlaceSite); + + if (inPlaceSite != nullptr) + { + createHolderComp(); + + holderComp->setWindow (inPlaceSite); + inPlaceSite->Release(); + } + else + { + deleteHolderComp(); + } + } + else + { + deleteHolderComp(); + } + } + + return S_OK; + } + + void createHolderComp() + { + if (holderComp == nullptr) + { + if (numActivePlugins++ == 0) + { + log ("initialiseJuce_GUI()"); + initialiseJuce_GUI(); + + browserVersionDesc = "Internet Explorer " + getExeVersion (getExePath(), "FileVersion"); + } + + holderComp = new AXBrowserPluginHolderComponent(); + } + } + + void deleteHolderComp() + { + if (holderComp != nullptr) + { + holderComp = nullptr; + + if (--numActivePlugins == 0) + { + log ("shutdownJuce_GUI()"); + shutdownJuce_GUI(); + } + } + } + + HRESULT __stdcall GetSite (REFIID riid, void **ppvSite) + { + *ppvSite = site; + return S_OK; + } + + //============================================================================== + HRESULT __stdcall SetObjectRects (LPCRECT r, LPCRECT c) + { + if (holderComp != nullptr) + holderComp->setBounds (r->left, r->top, r->right - r->left, r->bottom - r->top); + + return S_OK; + } + + HRESULT __stdcall GetWindow (HWND* phwnd) + { + if (holderComp == nullptr) + return E_NOTIMPL; + + *phwnd = (HWND) holderComp->getWindowHandle(); + return S_OK; + } + + //============================================================================== + HRESULT __stdcall ContextSensitiveHelp (BOOL fEnterMode) { return E_NOTIMPL; } + HRESULT __stdcall InPlaceDeactivate() { return E_NOTIMPL; } + HRESULT __stdcall UIDeactivate() { return E_NOTIMPL; } + HRESULT __stdcall ReactivateAndUndo() { return E_NOTIMPL; } + + //============================================================================== + HRESULT __stdcall GetInterfaceSafetyOptions (REFIID riid, DWORD *pdwSupportedOptions, DWORD *pdwEnabledOptions) + { + *pdwSupportedOptions = *pdwEnabledOptions = INTERFACESAFE_FOR_UNTRUSTED_CALLER|INTERFACESAFE_FOR_UNTRUSTED_DATA; + return S_OK; + } + + HRESULT __stdcall SetInterfaceSafetyOptions (REFIID, DWORD, DWORD) { return S_OK; } + +private: + IUnknown* site; + int refCount; + ScopedPointer holderComp; + IDispatchHelper iDispatchHelper; + + JUCE_DECLARE_NON_COPYABLE (JuceActiveXObject) +}; + +//============================================================================== +class JuceActiveXObjectFactory : public IUnknown, + public IClassFactory +{ +public: + JuceActiveXObjectFactory() : refCount (0) {} + + HRESULT __stdcall QueryInterface (REFIID id, void __RPC_FAR* __RPC_FAR* result) + { + if (id == IID_IUnknown) { AddRef(); *result = (IUnknown*) this; return S_OK; } + if (id == IID_IClassFactory) { AddRef(); *result = (IClassFactory*) this; return S_OK; } + + *result = nullptr; + return E_NOINTERFACE; + } + + ULONG __stdcall AddRef() { return ++refCount; } + ULONG __stdcall Release() { const int r = --refCount; if (r == 0) delete this; return r; } + + HRESULT __stdcall CreateInstance (IUnknown* pUnkOuter, REFIID riid, void** ppvObject) + { + *ppvObject = nullptr; + + if (pUnkOuter != nullptr && riid != IID_IUnknown) + return CLASS_E_NOAGGREGATION; + + JuceActiveXObject* ax = new JuceActiveXObject(); + return ax->QueryInterface (riid, ppvObject); + } + + HRESULT __stdcall LockServer (BOOL /*fLock*/) { return S_OK; } + +private: + int refCount; + + JUCE_DECLARE_NON_COPYABLE (JuceActiveXObjectFactory) +}; + +//============================================================================== +String getActiveXBrowserURL (const BrowserPluginComponent* comp) +{ + if (AXBrowserPluginHolderComponent* ax = dynamic_cast (comp->getParentComponent())) + return ax->getBrowserURL(); + + return String(); +} + +//============================================================================== +extern "C" BOOL WINAPI DllMain (HANDLE instance, DWORD reason, LPVOID) +{ + #pragma EXPORTED_FUNCTION + + switch (reason) + { + case DLL_PROCESS_ATTACH: + log ("DLL_PROCESS_ATTACH"); + Process::setCurrentModuleInstanceHandle (instance); + break; + + case DLL_PROCESS_DETACH: + log ("DLL_PROCESS_DETACH"); + browserVersionDesc.clear(); + + // IE has a tendency to leak our objects, so although none of this should be + // necessary, it's best to make sure.. + jassert (numActivePlugins == 0); + shutdownJuce_GUI(); + break; + + default: + break; + } + + return TRUE; +} + +static String CLSIDToJuceString (REFCLSID clsid) +{ + LPWSTR s = nullptr; + StringFromIID (clsid, &s); + + if (s == nullptr) + return String::empty; + + const String result (s); + LPMALLOC malloc; + CoGetMalloc (1, &malloc); + if (malloc != nullptr) + { + malloc->Free (s); + malloc->Release(); + } + + return result.removeCharacters ("{}").trim(); +} + +STDAPI DllGetClassObject (REFCLSID rclsid, REFIID riid, LPVOID* ppv) +{ + #pragma EXPORTED_FUNCTION + + *ppv = nullptr; + + if (CLSIDToJuceString (rclsid).equalsIgnoreCase (String (JuceBrowserPlugin_ActiveXCLSID))) + { + JuceActiveXObjectFactory* afx = new JuceActiveXObjectFactory(); + if (afx->QueryInterface (riid, ppv) == S_OK) + return S_OK; + + delete afx; + } + + return CLASS_E_CLASSNOTAVAILABLE; +} + +STDAPI DllCanUnloadNow() +{ + #pragma EXPORTED_FUNCTION + return S_OK; +} + +//============================================================================== +static String makeLegalRegistryName (const String& s) +{ + return s.retainCharacters ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_."); +} + +static HRESULT doRegistration (const bool unregister) +{ + const String company (makeLegalRegistryName (JuceBrowserPlugin_Company)); + const String plugin (makeLegalRegistryName (JuceBrowserPlugin_Name)); + const String clsID ("{" + String (JuceBrowserPlugin_ActiveXCLSID).toUpperCase() + "}"); + const String root ("HKEY_CLASSES_ROOT\\"); + const String companyDotPlugin (company + "." + plugin); + const String companyDotPluginCur (companyDotPlugin + ".1"); + const String clsIDRoot (root + "CLSID\\" + clsID + "\\"); + const String dllPath (File::getSpecialLocation (File::currentApplicationFile).getFullPathName()); + + StringPairArray settings; + settings.set (root + companyDotPluginCur + "\\", JuceBrowserPlugin_Name); + settings.set (root + companyDotPluginCur + "\\CLSID\\", clsID); + settings.set (root + companyDotPlugin + "\\", JuceBrowserPlugin_Name); + settings.set (root + companyDotPlugin + "\\CLSID\\", clsID); + settings.set (root + companyDotPlugin + "\\CurVer\\", companyDotPluginCur); + settings.set (clsIDRoot, JuceBrowserPlugin_Name); + settings.set (clsIDRoot + "Implemented Categories\\{7DD95801-9882-11CF-9FA9-00AA006C42C4}\\", String::empty); + settings.set (clsIDRoot + "Implemented Categories\\{7DD95802-9882-11CF-9FA9-00AA006C42C4}\\", String::empty); + settings.set (clsIDRoot + "ProgID\\", companyDotPluginCur); + settings.set (clsIDRoot + "VersionIndependentProgID\\", companyDotPlugin); + settings.set (clsIDRoot + "Programmable\\", String::empty); + settings.set (clsIDRoot + "InProcServer32\\", dllPath); + settings.set (clsIDRoot + "InProcServer32\\ThreadingModel", "Apartment"); + settings.set (clsIDRoot + "Control\\", String::empty); + settings.set (clsIDRoot + "Insertable\\", String::empty); + settings.set (clsIDRoot + "ToolboxBitmap32\\", dllPath + ", 101"); + settings.set (clsIDRoot + "TypeLib\\", ""); + settings.set (clsIDRoot + "Version\\", JuceBrowserPlugin_Version); + + if (unregister) + { + for (int i = 0; i < settings.getAllKeys().size(); ++i) + WindowsRegistry::deleteValue (settings.getAllKeys()[i]); + + WindowsRegistry::deleteKey (root + companyDotPluginCur); + WindowsRegistry::deleteKey (root + companyDotPlugin); + WindowsRegistry::deleteKey (clsIDRoot); + + if (WindowsRegistry::valueExists (clsIDRoot + "InProcServer32")) + return SELFREG_E_CLASS; + } + else + { + WindowsRegistry::deleteKey (clsIDRoot); + + for (int i = 0; i < settings.getAllKeys().size(); ++i) + WindowsRegistry::setValue (settings.getAllKeys()[i], + settings [settings.getAllKeys()[i]]); + + // check whether the registration actually worked - if not, we probably don't have + // enough privileges to write to the registry.. + if (WindowsRegistry::getValue (clsIDRoot + "InProcServer32\\") != dllPath) + return SELFREG_E_CLASS; + } + + return S_OK; +} + +STDAPI DllRegisterServer() +{ + #pragma EXPORTED_FUNCTION + return doRegistration (false); +} + +STDAPI DllUnregisterServer() +{ + #pragma EXPORTED_FUNCTION + return doRegistration (true); +} + +#endif diff --git a/modules/juce_gui_basics/windows/juce_ResizableWindow.cpp b/modules/juce_gui_basics/windows/juce_ResizableWindow.cpp index f30defc08b..0338cc48ac 100644 --- a/modules/juce_gui_basics/windows/juce_ResizableWindow.cpp +++ b/modules/juce_gui_basics/windows/juce_ResizableWindow.cpp @@ -558,6 +558,10 @@ bool ResizableWindow::restoreWindowStateFromString (const String& s) } updateLastPosIfNotFullScreen(); + + if (fs) + setBoundsConstrained (newPos); + setFullScreen (fs); if (! fs) diff --git a/modules/juce_opengl/utils/juce_OpenGLAppComponent.cpp b/modules/juce_opengl/utils/juce_OpenGLAppComponent.cpp index d7069ae838..724537e265 100644 --- a/modules/juce_opengl/utils/juce_OpenGLAppComponent.cpp +++ b/modules/juce_opengl/utils/juce_OpenGLAppComponent.cpp @@ -31,6 +31,17 @@ OpenGLAppComponent::OpenGLAppComponent() : frameCounter (0) } OpenGLAppComponent::~OpenGLAppComponent() +{ + // Before your subclass's destructor has completed, you must call + // shutdownOpenGL() to release the GL context. (Otherwise there's + // a danger that it may invoke a GL callback on your class while + // it's in the process of being deleted. + jassert (! openGLContext.isAttached()); + + shutdownOpenGL(); +} + +void OpenGLAppComponent::shutdownOpenGL() { openGLContext.detach(); } diff --git a/modules/juce_opengl/utils/juce_OpenGLAppComponent.h b/modules/juce_opengl/utils/juce_OpenGLAppComponent.h index 112a1ceb65..858ea617b0 100644 --- a/modules/juce_opengl/utils/juce_OpenGLAppComponent.h +++ b/modules/juce_opengl/utils/juce_OpenGLAppComponent.h @@ -46,6 +46,8 @@ public: */ int getFrameCounter() const noexcept { return frameCounter; } + void shutdownOpenGL(); + /** Implement this method to set up any GL objects that you need for rendering. The GL context will be active when this method is called. */