git subrepo clone --branch=sono6good https://github.com/essej/JUCE.git deps/juce
subrepo: subdir: "deps/juce" merged: "b13f9084e" upstream: origin: "https://github.com/essej/JUCE.git" branch: "sono6good" commit: "b13f9084e" git-subrepo: version: "0.4.3" origin: "https://github.com/ingydotnet/git-subrepo.git" commit: "2f68596"
This commit is contained in:
43
deps/juce/modules/juce_core/native/java/README.txt
vendored
Normal file
43
deps/juce/modules/juce_core/native/java/README.txt
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
The Java code in the module's native/java subfolders have been used to generate
|
||||
dex byte-code in various places in the JUCE framework. These are the steps
|
||||
required to re-generate the dex byte-code from any Java source code inside the
|
||||
native/java subfolders:
|
||||
|
||||
1. Create a new JUCE android project with the minimal sdk version which is
|
||||
required for the Java source code you wish to compile.
|
||||
|
||||
2. If you are creating byte-code for new .java files, move the new files into
|
||||
the native/javacore/app folder of the module, or create one if it doesn't
|
||||
exist. Remember that .java files need to be in nested sub-folders which
|
||||
resemble their package, i.e. a Java class com.rmsl.juce.HelloWorld.java should
|
||||
be in the module's native/javacore/app/com/rmsl/juce folder. If you wish to
|
||||
modify existing .java files in the JUCE modules then just rename native/java to
|
||||
native/javacore.
|
||||
|
||||
3. Build your project with Android Studio and run. The app will now use the
|
||||
source code in the folder created in step 2 so you can debug your Java code
|
||||
this way.
|
||||
|
||||
4. Once everything is working rebuild your app in release mode.
|
||||
|
||||
5. Go to your app's Builds/Android folder. Inside there you will find
|
||||
build/intermediates/javac/release_Release/compileRelease_ReleaseJavaWithJavac/classes.
|
||||
Inside of that folder, you will find all your Java byte-code compiled classes.
|
||||
Remove any classes that you are not interested in (typically you'll find
|
||||
Java.class and JuceApp.class which you will probably want to remove).
|
||||
|
||||
6. Inside of
|
||||
build/intermediates/javac/release_Release/compileRelease_ReleaseJavaWithJavac/classes
|
||||
execute the following dx command:
|
||||
|
||||
<path-to-your-android-sdk>/build-tools/<latest-build-tool-version>/dx --dex --verbose --min-sdk-version=<your-min-sdk-of-your-classes> --output /tmp/JavaDexByteCode.dex .
|
||||
|
||||
(Replace <your-min-sdk-of-your-classes> with the minimal sdk version you used in step 1.)
|
||||
|
||||
7. gzip the output:
|
||||
|
||||
gzip /tmp/JavaDexByteCode.dex
|
||||
|
||||
8. The output /tmp/JavaDexByteCode.dex.gz is now the byte code that can be
|
||||
included into JUCE. You can use the Projucer's BinaryData generator
|
||||
functionality to get this into a convenient char array like form.
|
80
deps/juce/modules/juce_core/native/java/app/com/rmsl/juce/FragmentOverlay.java
vendored
Normal file
80
deps/juce/modules/juce_core/native/java/app/com/rmsl/juce/FragmentOverlay.java
vendored
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
package com.rmsl.juce;
|
||||
|
||||
import android.app.DialogFragment;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
public class FragmentOverlay extends DialogFragment
|
||||
{
|
||||
@Override
|
||||
public void onCreate (Bundle state)
|
||||
{
|
||||
super.onCreate (state);
|
||||
cppThis = getArguments ().getLong ("cppThis");
|
||||
|
||||
if (cppThis != 0)
|
||||
onCreateNative (cppThis, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart ()
|
||||
{
|
||||
super.onStart ();
|
||||
|
||||
if (cppThis != 0)
|
||||
onStartNative (cppThis);
|
||||
}
|
||||
|
||||
public void onRequestPermissionsResult (int requestCode,
|
||||
String[] permissions,
|
||||
int[] grantResults)
|
||||
{
|
||||
if (cppThis != 0)
|
||||
onRequestPermissionsResultNative (cppThis, requestCode,
|
||||
permissions, grantResults);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult (int requestCode, int resultCode, Intent data)
|
||||
{
|
||||
if (cppThis != 0)
|
||||
onActivityResultNative (cppThis, requestCode, resultCode, data);
|
||||
}
|
||||
|
||||
public void close ()
|
||||
{
|
||||
cppThis = 0;
|
||||
dismiss ();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
private long cppThis = 0;
|
||||
|
||||
private native void onActivityResultNative (long myself, int requestCode, int resultCode, Intent data);
|
||||
private native void onCreateNative (long myself, Bundle state);
|
||||
private native void onStartNative (long myself);
|
||||
private native void onRequestPermissionsResultNative (long myself, int requestCode,
|
||||
String[] permissions, int[] grantResults);
|
||||
}
|
429
deps/juce/modules/juce_core/native/java/app/com/rmsl/juce/JuceHTTPStream.java
vendored
Normal file
429
deps/juce/modules/juce_core/native/java/app/com/rmsl/juce/JuceHTTPStream.java
vendored
Normal file
@ -0,0 +1,429 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
package com.rmsl.juce;
|
||||
|
||||
import java.lang.Runnable;
|
||||
import java.io.*;
|
||||
import java.net.URL;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.concurrent.atomic.*;
|
||||
|
||||
public class JuceHTTPStream
|
||||
{
|
||||
public JuceHTTPStream(String address, boolean isPostToUse, byte[] postDataToUse,
|
||||
String headersToUse, int timeOutMsToUse,
|
||||
int[] statusCodeToUse, StringBuffer responseHeadersToUse,
|
||||
int numRedirectsToFollowToUse, String httpRequestCmdToUse) throws IOException
|
||||
{
|
||||
isPost = isPostToUse;
|
||||
postData = postDataToUse;
|
||||
headers = headersToUse;
|
||||
timeOutMs = timeOutMsToUse;
|
||||
statusCode = statusCodeToUse;
|
||||
responseHeaders = responseHeadersToUse;
|
||||
totalLength = -1;
|
||||
numRedirectsToFollow = numRedirectsToFollowToUse;
|
||||
httpRequestCmd = httpRequestCmdToUse;
|
||||
|
||||
connection = createConnection(address, isPost, postData, headers, timeOutMs, httpRequestCmd);
|
||||
}
|
||||
|
||||
public static final JuceHTTPStream createHTTPStream(String address, boolean isPost, byte[] postData,
|
||||
String headers, int timeOutMs, int[] statusCode,
|
||||
StringBuffer responseHeaders, int numRedirectsToFollow,
|
||||
String httpRequestCmd)
|
||||
{
|
||||
// timeout parameter of zero for HttpUrlConnection is a blocking connect (negative value for juce::URL)
|
||||
if (timeOutMs < 0)
|
||||
timeOutMs = 0;
|
||||
else if (timeOutMs == 0)
|
||||
timeOutMs = 30000;
|
||||
|
||||
for (; ; )
|
||||
{
|
||||
try
|
||||
{
|
||||
JuceHTTPStream httpStream = new JuceHTTPStream(address, isPost, postData, headers,
|
||||
timeOutMs, statusCode, responseHeaders,
|
||||
numRedirectsToFollow, httpRequestCmd);
|
||||
|
||||
return httpStream;
|
||||
} catch (Throwable e)
|
||||
{
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private final HttpURLConnection createConnection(String address, boolean isPost, byte[] postData,
|
||||
String headers, int timeOutMs, String httpRequestCmdToUse) throws IOException
|
||||
{
|
||||
HttpURLConnection newConnection = (HttpURLConnection) (new URL(address).openConnection());
|
||||
|
||||
try
|
||||
{
|
||||
newConnection.setInstanceFollowRedirects(false);
|
||||
newConnection.setConnectTimeout(timeOutMs);
|
||||
newConnection.setReadTimeout(timeOutMs);
|
||||
|
||||
// headers - if not empty, this string is appended onto the headers that are used for the request. It must therefore be a valid set of HTML header directives, separated by newlines.
|
||||
// So convert headers string to an array, with an element for each line
|
||||
String headerLines[] = headers.split("\\n");
|
||||
|
||||
// Set request headers
|
||||
for (int i = 0; i < headerLines.length; ++i)
|
||||
{
|
||||
int pos = headerLines[i].indexOf(":");
|
||||
|
||||
if (pos > 0 && pos < headerLines[i].length())
|
||||
{
|
||||
String field = headerLines[i].substring(0, pos);
|
||||
String value = headerLines[i].substring(pos + 1);
|
||||
|
||||
if (value.length() > 0)
|
||||
newConnection.setRequestProperty(field, value);
|
||||
}
|
||||
}
|
||||
|
||||
newConnection.setRequestMethod(httpRequestCmd);
|
||||
|
||||
if (isPost)
|
||||
{
|
||||
newConnection.setDoOutput(true);
|
||||
|
||||
if (postData != null)
|
||||
{
|
||||
OutputStream out = newConnection.getOutputStream();
|
||||
out.write(postData);
|
||||
out.flush();
|
||||
}
|
||||
}
|
||||
|
||||
return newConnection;
|
||||
} catch (Throwable e)
|
||||
{
|
||||
newConnection.disconnect();
|
||||
throw new IOException("Connection error");
|
||||
}
|
||||
}
|
||||
|
||||
private final InputStream getCancellableStream(final boolean isInput) throws ExecutionException
|
||||
{
|
||||
synchronized (createFutureLock)
|
||||
{
|
||||
if (hasBeenCancelled.get())
|
||||
return null;
|
||||
|
||||
streamFuture = executor.submit(new Callable<BufferedInputStream>()
|
||||
{
|
||||
@Override
|
||||
public BufferedInputStream call() throws IOException
|
||||
{
|
||||
return new BufferedInputStream(isInput ? connection.getInputStream()
|
||||
: connection.getErrorStream());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return streamFuture.get();
|
||||
} catch (InterruptedException e)
|
||||
{
|
||||
return null;
|
||||
} catch (CancellationException e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public final boolean connect()
|
||||
{
|
||||
boolean result = false;
|
||||
int numFollowedRedirects = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
result = doConnect();
|
||||
|
||||
if (!result)
|
||||
return false;
|
||||
|
||||
if (++numFollowedRedirects > numRedirectsToFollow)
|
||||
break;
|
||||
|
||||
int status = statusCode[0];
|
||||
|
||||
if (status == 301 || status == 302 || status == 303 || status == 307)
|
||||
{
|
||||
// Assumes only one occurrence of "Location"
|
||||
int pos1 = responseHeaders.indexOf("Location:") + 10;
|
||||
int pos2 = responseHeaders.indexOf("\n", pos1);
|
||||
|
||||
if (pos2 > pos1)
|
||||
{
|
||||
String currentLocation = connection.getURL().toString();
|
||||
String newLocation = responseHeaders.substring(pos1, pos2);
|
||||
|
||||
try
|
||||
{
|
||||
// Handle newLocation whether it's absolute or relative
|
||||
URL baseUrl = new URL(currentLocation);
|
||||
URL newUrl = new URL(baseUrl, newLocation);
|
||||
String transformedNewLocation = newUrl.toString();
|
||||
|
||||
if (transformedNewLocation != currentLocation)
|
||||
{
|
||||
// Clear responseHeaders before next iteration
|
||||
responseHeaders.delete(0, responseHeaders.length());
|
||||
|
||||
synchronized (createStreamLock)
|
||||
{
|
||||
if (hasBeenCancelled.get())
|
||||
return false;
|
||||
|
||||
connection.disconnect();
|
||||
|
||||
try
|
||||
{
|
||||
connection = createConnection(transformedNewLocation, isPost,
|
||||
postData, headers, timeOutMs,
|
||||
httpRequestCmd);
|
||||
} catch (Throwable e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else
|
||||
{
|
||||
break;
|
||||
}
|
||||
} catch (Throwable e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
} else
|
||||
{
|
||||
break;
|
||||
}
|
||||
} else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private final boolean doConnect()
|
||||
{
|
||||
synchronized (createStreamLock)
|
||||
{
|
||||
if (hasBeenCancelled.get())
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
inputStream = getCancellableStream(true);
|
||||
} catch (ExecutionException e)
|
||||
{
|
||||
if (connection.getResponseCode() < 400)
|
||||
{
|
||||
statusCode[0] = connection.getResponseCode();
|
||||
connection.disconnect();
|
||||
return false;
|
||||
}
|
||||
} finally
|
||||
{
|
||||
statusCode[0] = connection.getResponseCode();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (statusCode[0] >= 400)
|
||||
inputStream = getCancellableStream(false);
|
||||
else
|
||||
inputStream = getCancellableStream(true);
|
||||
} catch (ExecutionException e)
|
||||
{
|
||||
}
|
||||
|
||||
for (java.util.Map.Entry<String, java.util.List<String>> entry : connection.getHeaderFields().entrySet())
|
||||
{
|
||||
if (entry.getKey() != null && entry.getValue() != null)
|
||||
{
|
||||
responseHeaders.append(entry.getKey() + ": "
|
||||
+ android.text.TextUtils.join(",", entry.getValue()) + "\n");
|
||||
|
||||
if (entry.getKey().compareTo("Content-Length") == 0)
|
||||
totalLength = Integer.decode(entry.getValue().get(0));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (IOException e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class DisconnectionRunnable implements Runnable
|
||||
{
|
||||
public DisconnectionRunnable(HttpURLConnection theConnection,
|
||||
InputStream theInputStream,
|
||||
ReentrantLock theCreateStreamLock,
|
||||
Object theCreateFutureLock,
|
||||
Future<BufferedInputStream> theStreamFuture)
|
||||
{
|
||||
connectionToDisconnect = theConnection;
|
||||
inputStream = theInputStream;
|
||||
createStreamLock = theCreateStreamLock;
|
||||
createFutureLock = theCreateFutureLock;
|
||||
streamFuture = theStreamFuture;
|
||||
}
|
||||
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!createStreamLock.tryLock())
|
||||
{
|
||||
synchronized (createFutureLock)
|
||||
{
|
||||
if (streamFuture != null)
|
||||
streamFuture.cancel(true);
|
||||
}
|
||||
|
||||
createStreamLock.lock();
|
||||
}
|
||||
|
||||
if (connectionToDisconnect != null)
|
||||
connectionToDisconnect.disconnect();
|
||||
|
||||
if (inputStream != null)
|
||||
inputStream.close();
|
||||
} catch (IOException e)
|
||||
{
|
||||
} finally
|
||||
{
|
||||
createStreamLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private HttpURLConnection connectionToDisconnect;
|
||||
private InputStream inputStream;
|
||||
private ReentrantLock createStreamLock;
|
||||
private Object createFutureLock;
|
||||
Future<BufferedInputStream> streamFuture;
|
||||
}
|
||||
|
||||
public final void release()
|
||||
{
|
||||
DisconnectionRunnable disconnectionRunnable = new DisconnectionRunnable(connection,
|
||||
inputStream,
|
||||
createStreamLock,
|
||||
createFutureLock,
|
||||
streamFuture);
|
||||
|
||||
synchronized (createStreamLock)
|
||||
{
|
||||
hasBeenCancelled.set(true);
|
||||
|
||||
connection = null;
|
||||
}
|
||||
|
||||
Thread disconnectionThread = new Thread(disconnectionRunnable);
|
||||
disconnectionThread.start();
|
||||
}
|
||||
|
||||
public final int read(byte[] buffer, int numBytes)
|
||||
{
|
||||
int num = 0;
|
||||
|
||||
try
|
||||
{
|
||||
synchronized (createStreamLock)
|
||||
{
|
||||
if (inputStream != null)
|
||||
num = inputStream.read(buffer, 0, numBytes);
|
||||
}
|
||||
} catch (IOException e)
|
||||
{
|
||||
}
|
||||
|
||||
if (num > 0)
|
||||
position += num;
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
public final long getPosition()
|
||||
{
|
||||
return position;
|
||||
}
|
||||
|
||||
public final long getTotalLength()
|
||||
{
|
||||
return totalLength;
|
||||
}
|
||||
|
||||
public final boolean isExhausted()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public final boolean setPosition(long newPos)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isPost;
|
||||
private byte[] postData;
|
||||
private String headers;
|
||||
private int timeOutMs;
|
||||
String httpRequestCmd;
|
||||
private HttpURLConnection connection;
|
||||
private int[] statusCode;
|
||||
private StringBuffer responseHeaders;
|
||||
private int totalLength;
|
||||
private int numRedirectsToFollow;
|
||||
private InputStream inputStream;
|
||||
private long position;
|
||||
private final ReentrantLock createStreamLock = new ReentrantLock();
|
||||
private final Object createFutureLock = new Object();
|
||||
private AtomicBoolean hasBeenCancelled = new AtomicBoolean();
|
||||
|
||||
private final ExecutorService executor = Executors.newCachedThreadPool(Executors.defaultThreadFactory());
|
||||
Future<BufferedInputStream> streamFuture;
|
||||
}
|
60
deps/juce/modules/juce_core/native/java/app/com/rmsl/juce/JuceInvocationHandler.java
vendored
Normal file
60
deps/juce/modules/juce_core/native/java/app/com/rmsl/juce/JuceInvocationHandler.java
vendored
Normal file
@ -0,0 +1,60 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
package com.rmsl.juce;
|
||||
|
||||
import java.lang.reflect.*;
|
||||
|
||||
public class JuceInvocationHandler implements InvocationHandler
|
||||
{
|
||||
public JuceInvocationHandler (long nativeContextRef)
|
||||
{
|
||||
nativeContext = nativeContextRef;
|
||||
}
|
||||
|
||||
public void clear()
|
||||
{
|
||||
nativeContext = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finalize()
|
||||
{
|
||||
if (nativeContext != 0)
|
||||
dispatchFinalize (nativeContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object invoke (Object proxy, Method method, Object[] args) throws Throwable
|
||||
{
|
||||
if (nativeContext != 0)
|
||||
return dispatchInvoke (nativeContext, proxy, method, args);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
private long nativeContext = 0;
|
||||
|
||||
private native void dispatchFinalize (long nativeContextRef);
|
||||
private native Object dispatchInvoke (long nativeContextRef, Object proxy, Method method, Object[] args);
|
||||
}
|
37
deps/juce/modules/juce_core/native/javacore/app/com/rmsl/juce/JuceApp.java
vendored
Normal file
37
deps/juce/modules/juce_core/native/javacore/app/com/rmsl/juce/JuceApp.java
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
package com.rmsl.juce;
|
||||
|
||||
import com.rmsl.juce.Java;
|
||||
|
||||
import android.app.Application;
|
||||
|
||||
public class JuceApp extends Application
|
||||
{
|
||||
@Override
|
||||
public void onCreate()
|
||||
{
|
||||
super.onCreate();
|
||||
Java.initialiseJUCE (this);
|
||||
}
|
||||
}
|
35
deps/juce/modules/juce_core/native/javacore/init/com/rmsl/juce/Java.java
vendored
Normal file
35
deps/juce/modules/juce_core/native/javacore/init/com/rmsl/juce/Java.java
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
package com.rmsl.juce;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
public class Java
|
||||
{
|
||||
static
|
||||
{
|
||||
System.loadLibrary ("juce_jni");
|
||||
}
|
||||
|
||||
public native static void initialiseJUCE (Context appContext);
|
||||
}
|
318
deps/juce/modules/juce_core/native/juce_BasicNativeHeaders.h
vendored
Normal file
318
deps/juce/modules/juce_core/native/juce_BasicNativeHeaders.h
vendored
Normal file
@ -0,0 +1,318 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#undef T
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_MAC || JUCE_IOS
|
||||
|
||||
#if JUCE_IOS
|
||||
#if JUCE_MODULE_AVAILABLE_juce_opengl && defined (__IPHONE_12_0) && __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_12_0
|
||||
#define GLES_SILENCE_DEPRECATION 1
|
||||
#endif
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <CoreData/CoreData.h>
|
||||
#import <MobileCoreServices/MobileCoreServices.h>
|
||||
#include <sys/fcntl.h>
|
||||
#else
|
||||
#if JUCE_MODULE_AVAILABLE_juce_opengl && defined (MAC_OS_X_VERSION_10_14) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14
|
||||
#define GL_SILENCE_DEPRECATION 1
|
||||
#endif
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#if (! defined MAC_OS_X_VERSION_10_12) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12
|
||||
#define NSEventModifierFlagCommand NSCommandKeyMask
|
||||
#define NSEventModifierFlagControl NSControlKeyMask
|
||||
#define NSEventModifierFlagHelp NSHelpKeyMask
|
||||
#define NSEventModifierFlagNumericPad NSNumericPadKeyMask
|
||||
#define NSEventModifierFlagOption NSAlternateKeyMask
|
||||
#define NSEventModifierFlagShift NSShiftKeyMask
|
||||
#define NSCompositingOperationSourceOver NSCompositeSourceOver
|
||||
#define NSEventMaskApplicationDefined NSApplicationDefinedMask
|
||||
#define NSEventTypeApplicationDefined NSApplicationDefined
|
||||
#define NSEventTypeCursorUpdate NSCursorUpdate
|
||||
#define NSEventTypeMouseMoved NSMouseMoved
|
||||
#define NSEventTypeLeftMouseDown NSLeftMouseDown
|
||||
#define NSEventTypeRightMouseDown NSRightMouseDown
|
||||
#define NSEventTypeOtherMouseDown NSOtherMouseDown
|
||||
#define NSEventTypeLeftMouseUp NSLeftMouseUp
|
||||
#define NSEventTypeRightMouseUp NSRightMouseUp
|
||||
#define NSEventTypeOtherMouseUp NSOtherMouseUp
|
||||
#define NSEventTypeLeftMouseDragged NSLeftMouseDragged
|
||||
#define NSEventTypeRightMouseDragged NSRightMouseDragged
|
||||
#define NSEventTypeOtherMouseDragged NSOtherMouseDragged
|
||||
#define NSEventTypeScrollWheel NSScrollWheel
|
||||
#define NSEventTypeKeyDown NSKeyDown
|
||||
#define NSEventTypeKeyUp NSKeyUp
|
||||
#define NSEventTypeFlagsChanged NSFlagsChanged
|
||||
#define NSEventMaskAny NSAnyEventMask
|
||||
#define NSWindowStyleMaskBorderless NSBorderlessWindowMask
|
||||
#define NSWindowStyleMaskClosable NSClosableWindowMask
|
||||
#define NSWindowStyleMaskFullScreen NSFullScreenWindowMask
|
||||
#define NSWindowStyleMaskMiniaturizable NSMiniaturizableWindowMask
|
||||
#define NSWindowStyleMaskResizable NSResizableWindowMask
|
||||
#define NSWindowStyleMaskTitled NSTitledWindowMask
|
||||
#define NSAlertStyleCritical NSCriticalAlertStyle
|
||||
#define NSControlSizeRegular NSRegularControlSize
|
||||
#define NSEventTypeMouseEntered NSMouseEntered
|
||||
#define NSEventTypeMouseExited NSMouseExited
|
||||
#define NSAlertStyleInformational NSInformationalAlertStyle
|
||||
#define NSEventTypeTabletPoint NSTabletPoint
|
||||
#define NSEventTypeTabletProximity NSTabletProximity
|
||||
#define NSEventTypeFlagsChanged NSFlagsChanged
|
||||
#define NSEventTypeAppKitDefined NSAppKitDefined
|
||||
#define NSEventTypeSystemDefined NSSystemDefined
|
||||
#define NSEventTypeApplicationDefined NSApplicationDefined
|
||||
#define NSEventTypePeriodic NSPeriodic
|
||||
#define NSEventTypeSmartMagnify NSEventTypeSmartMagnify
|
||||
#endif
|
||||
#import <CoreAudio/HostTime.h>
|
||||
#include <sys/dir.h>
|
||||
#endif
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <sys/mman.h>
|
||||
#include <fnmatch.h>
|
||||
#include <utime.h>
|
||||
#include <dlfcn.h>
|
||||
#include <ifaddrs.h>
|
||||
#include <net/if_dl.h>
|
||||
#include <mach/mach_time.h>
|
||||
#include <mach-o/dyld.h>
|
||||
#include <objc/runtime.h>
|
||||
#include <objc/objc.h>
|
||||
#include <objc/message.h>
|
||||
#include <poll.h>
|
||||
|
||||
//==============================================================================
|
||||
#elif JUCE_WINDOWS
|
||||
#if JUCE_MSVC
|
||||
#ifndef _CPPRTTI
|
||||
#error "You're compiling without RTTI enabled! This is needed for a lot of JUCE classes, please update your compiler settings!"
|
||||
#endif
|
||||
|
||||
#ifndef _CPPUNWIND
|
||||
#error "You're compiling without exceptions enabled! This is needed for a lot of JUCE classes, please update your compiler settings!"
|
||||
#endif
|
||||
|
||||
#pragma warning (push, 0) // disable all warnings whilst including system headers
|
||||
#endif
|
||||
|
||||
#define NOMINMAX
|
||||
|
||||
#define _WINSOCK_DEPRECATED_NO_WARNINGS 1
|
||||
#define STRICT 1
|
||||
#define WIN32_LEAN_AND_MEAN 1
|
||||
#if JUCE_MINGW
|
||||
#define _WIN32_WINNT 0x0600
|
||||
#else
|
||||
#define _WIN32_WINNT 0x0602
|
||||
#endif
|
||||
#define _UNICODE 1
|
||||
#define UNICODE 1
|
||||
#ifndef _WIN32_IE
|
||||
#define _WIN32_IE 0x0501
|
||||
#endif
|
||||
|
||||
#include <windows.h>
|
||||
#include <shellapi.h>
|
||||
#include <tchar.h>
|
||||
#include <stddef.h>
|
||||
#include <ctime>
|
||||
#include <wininet.h>
|
||||
#include <nb30.h>
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <iphlpapi.h>
|
||||
#include <mapi.h>
|
||||
#include <float.h>
|
||||
#include <process.h>
|
||||
#include <shlobj.h>
|
||||
#include <shlwapi.h>
|
||||
#include <mmsystem.h>
|
||||
#include <winioctl.h>
|
||||
|
||||
#if JUCE_MINGW
|
||||
#include <basetyps.h>
|
||||
#include <sys/time.h>
|
||||
#ifndef alloca
|
||||
#define alloca __builtin_alloca
|
||||
#endif
|
||||
#else
|
||||
#include <crtdbg.h>
|
||||
#include <comutil.h>
|
||||
#endif
|
||||
|
||||
#ifndef S_FALSE
|
||||
#define S_FALSE (1) // (apparently some obscure win32 dev environments don't define this)
|
||||
#endif
|
||||
|
||||
#undef PACKED
|
||||
|
||||
#if JUCE_MSVC
|
||||
#pragma warning (pop)
|
||||
#pragma warning (4: 4511 4512 4100)
|
||||
#endif
|
||||
|
||||
#if ! JUCE_MINGW && ! JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES
|
||||
#pragma comment (lib, "kernel32.lib")
|
||||
#pragma comment (lib, "user32.lib")
|
||||
#pragma comment (lib, "wininet.lib")
|
||||
#pragma comment (lib, "advapi32.lib")
|
||||
#pragma comment (lib, "ws2_32.lib")
|
||||
#pragma comment (lib, "version.lib")
|
||||
#pragma comment (lib, "shlwapi.lib")
|
||||
#pragma comment (lib, "winmm.lib")
|
||||
|
||||
#ifdef _NATIVE_WCHAR_T_DEFINED
|
||||
#ifdef _DEBUG
|
||||
#pragma comment (lib, "comsuppwd.lib")
|
||||
#else
|
||||
#pragma comment (lib, "comsuppw.lib")
|
||||
#endif
|
||||
#else
|
||||
#ifdef _DEBUG
|
||||
#pragma comment (lib, "comsuppd.lib")
|
||||
#else
|
||||
#pragma comment (lib, "comsupp.lib")
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Used with DynamicLibrary to simplify importing functions from a win32 DLL.
|
||||
|
||||
dll: the DynamicLibrary object
|
||||
functionName: function to import
|
||||
localFunctionName: name you want to use to actually call it (must be different)
|
||||
returnType: the return type
|
||||
params: list of params (bracketed)
|
||||
*/
|
||||
#define JUCE_LOAD_WINAPI_FUNCTION(dll, functionName, localFunctionName, returnType, params) \
|
||||
typedef returnType (WINAPI *type##localFunctionName) params; \
|
||||
type##localFunctionName localFunctionName = (type##localFunctionName) dll.getFunction (#functionName);
|
||||
|
||||
//==============================================================================
|
||||
#elif JUCE_LINUX
|
||||
#include <arpa/inet.h>
|
||||
#include <dlfcn.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <fnmatch.h>
|
||||
#include <net/if.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <pthread.h>
|
||||
#include <pwd.h>
|
||||
#include <sched.h>
|
||||
#include <signal.h>
|
||||
#include <stddef.h>
|
||||
#include <sys/dir.h>
|
||||
#include <sys/file.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/sysinfo.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/vfs.h>
|
||||
#include <sys/wait.h>
|
||||
#include <utime.h>
|
||||
#include <poll.h>
|
||||
|
||||
//==============================================================================
|
||||
#elif JUCE_BSD
|
||||
#include <arpa/inet.h>
|
||||
#include <dirent.h>
|
||||
#include <dlfcn.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <fnmatch.h>
|
||||
#include <ifaddrs.h>
|
||||
#include <langinfo.h>
|
||||
#include <net/if.h>
|
||||
#include <net/if_dl.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <pthread.h>
|
||||
#include <pwd.h>
|
||||
#include <sched.h>
|
||||
#include <signal.h>
|
||||
#include <stddef.h>
|
||||
#include <sys/file.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/user.h>
|
||||
#include <sys/wait.h>
|
||||
#include <utime.h>
|
||||
#include <poll.h>
|
||||
|
||||
//==============================================================================
|
||||
#elif JUCE_ANDROID
|
||||
#include <jni.h>
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
#include <sys/time.h>
|
||||
#include <utime.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <dlfcn.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/statfs.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/sysinfo.h>
|
||||
#include <sys/mman.h>
|
||||
#include <pwd.h>
|
||||
#include <dirent.h>
|
||||
#include <fnmatch.h>
|
||||
#include <sys/wait.h>
|
||||
#include <android/api-level.h>
|
||||
#include <poll.h>
|
||||
|
||||
// If you are getting include errors here, then you to re-build the Projucer
|
||||
// and re-save your .jucer file.
|
||||
#include <cpu-features.h>
|
||||
#endif
|
||||
|
||||
// Need to clear various moronic redefinitions made by system headers..
|
||||
#undef max
|
||||
#undef min
|
||||
#undef direct
|
||||
#undef check
|
691
deps/juce/modules/juce_core/native/juce_android_Files.cpp
vendored
Normal file
691
deps/juce/modules/juce_core/native/juce_android_Files.cpp
vendored
Normal file
@ -0,0 +1,691 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
METHOD (constructor, "<init>", "(Landroid/content/Context;Landroid/media/MediaScannerConnection$MediaScannerConnectionClient;)V") \
|
||||
METHOD (connect, "connect", "()V") \
|
||||
METHOD (disconnect, "disconnect", "()V") \
|
||||
METHOD (scanFile, "scanFile", "(Ljava/lang/String;Ljava/lang/String;)V") \
|
||||
|
||||
DECLARE_JNI_CLASS (MediaScannerConnection, "android/media/MediaScannerConnection")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
METHOD (query, "query", "(Landroid/net/Uri;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)Landroid/database/Cursor;") \
|
||||
METHOD (openInputStream, "openInputStream", "(Landroid/net/Uri;)Ljava/io/InputStream;") \
|
||||
METHOD (openOutputStream, "openOutputStream", "(Landroid/net/Uri;)Ljava/io/OutputStream;")
|
||||
|
||||
DECLARE_JNI_CLASS (ContentResolver, "android/content/ContentResolver")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
METHOD (moveToFirst, "moveToFirst", "()Z") \
|
||||
METHOD (getColumnIndex, "getColumnIndex", "(Ljava/lang/String;)I") \
|
||||
METHOD (getString, "getString", "(I)Ljava/lang/String;") \
|
||||
METHOD (close, "close", "()V") \
|
||||
|
||||
DECLARE_JNI_CLASS (AndroidCursor, "android/database/Cursor")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
STATICMETHOD (getExternalStorageDirectory, "getExternalStorageDirectory", "()Ljava/io/File;") \
|
||||
STATICMETHOD (getExternalStoragePublicDirectory, "getExternalStoragePublicDirectory", "(Ljava/lang/String;)Ljava/io/File;") \
|
||||
STATICMETHOD (getDataDirectory, "getDataDirectory", "()Ljava/io/File;")
|
||||
|
||||
DECLARE_JNI_CLASS (AndroidEnvironment, "android/os/Environment")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
METHOD (close, "close", "()V") \
|
||||
METHOD (flush, "flush", "()V") \
|
||||
METHOD (write, "write", "([BII)V")
|
||||
|
||||
DECLARE_JNI_CLASS (AndroidOutputStream, "java/io/OutputStream")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
FIELD (publicSourceDir, "publicSourceDir", "Ljava/lang/String;") \
|
||||
FIELD (dataDir, "dataDir", "Ljava/lang/String;")
|
||||
|
||||
DECLARE_JNI_CLASS (AndroidApplicationInfo, "android/content/pm/ApplicationInfo")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
//==============================================================================
|
||||
static File juceFile (LocalRef<jobject> obj)
|
||||
{
|
||||
auto* env = getEnv();
|
||||
|
||||
if (env->IsInstanceOf (obj.get(), JavaFile) != 0)
|
||||
return File (juceString (LocalRef<jstring> ((jstring) env->CallObjectMethod (obj.get(),
|
||||
JavaFile.getAbsolutePath))));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
static File getWellKnownFolder (const char* folderId)
|
||||
{
|
||||
auto* env = getEnv();
|
||||
auto fieldId = env->GetStaticFieldID (AndroidEnvironment, folderId, "Ljava/lang/String;");
|
||||
|
||||
if (fieldId == nullptr)
|
||||
{
|
||||
// unknown field in environment
|
||||
jassertfalse;
|
||||
return {};
|
||||
}
|
||||
|
||||
LocalRef<jobject> fieldValue (env->GetStaticObjectField (AndroidEnvironment, fieldId));
|
||||
|
||||
if (fieldValue == nullptr)
|
||||
return {};
|
||||
|
||||
LocalRef<jobject> downloadFolder (env->CallStaticObjectMethod (AndroidEnvironment,
|
||||
AndroidEnvironment.getExternalStoragePublicDirectory,
|
||||
fieldValue.get()));
|
||||
|
||||
return (downloadFolder ? juceFile (downloadFolder) : File());
|
||||
}
|
||||
|
||||
static LocalRef<jobject> urlToUri (const URL& url)
|
||||
{
|
||||
return LocalRef<jobject> (getEnv()->CallStaticObjectMethod (AndroidUri, AndroidUri.parse,
|
||||
javaString (url.toString (true)).get()));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
struct AndroidContentUriResolver
|
||||
{
|
||||
public:
|
||||
static LocalRef<jobject> getStreamForContentUri (const URL& url, bool inputStream)
|
||||
{
|
||||
// only use this method for content URIs
|
||||
jassert (url.getScheme() == "content");
|
||||
auto* env = getEnv();
|
||||
|
||||
LocalRef<jobject> contentResolver (env->CallObjectMethod (getAppContext().get(), AndroidContext.getContentResolver));
|
||||
|
||||
if (contentResolver)
|
||||
return LocalRef<jobject> ((env->CallObjectMethod (contentResolver.get(),
|
||||
inputStream ? ContentResolver.openInputStream
|
||||
: ContentResolver.openOutputStream,
|
||||
urlToUri (url).get())));
|
||||
|
||||
return LocalRef<jobject>();
|
||||
}
|
||||
|
||||
static File getLocalFileFromContentUri (const URL& url)
|
||||
{
|
||||
// only use this method for content URIs
|
||||
jassert (url.getScheme() == "content");
|
||||
|
||||
auto authority = url.getDomain();
|
||||
auto documentId = URL::removeEscapeChars (url.getSubPath().fromFirstOccurrenceOf ("/", false, false));
|
||||
auto tokens = StringArray::fromTokens (documentId, ":", "");
|
||||
|
||||
if (authority == "com.android.externalstorage.documents")
|
||||
{
|
||||
auto storageId = tokens[0];
|
||||
auto subpath = tokens[1];
|
||||
|
||||
auto storagePath = getStorageDevicePath (storageId);
|
||||
|
||||
if (storagePath != File())
|
||||
return storagePath.getChildFile (subpath);
|
||||
}
|
||||
else if (authority == "com.android.providers.downloads.documents")
|
||||
{
|
||||
auto type = tokens[0];
|
||||
auto downloadId = tokens[1];
|
||||
|
||||
if (type.equalsIgnoreCase ("raw"))
|
||||
{
|
||||
return File (downloadId);
|
||||
}
|
||||
else if (type.equalsIgnoreCase ("downloads"))
|
||||
{
|
||||
auto subDownloadPath = url.getSubPath().fromFirstOccurrenceOf ("tree/downloads", false, false);
|
||||
return File (getWellKnownFolder ("DIRECTORY_DOWNLOADS").getFullPathName() + "/" + subDownloadPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
return getLocalFileFromContentUri (URL ("content://downloads/public_downloads/" + documentId));
|
||||
}
|
||||
}
|
||||
else if (authority == "com.android.providers.media.documents" && documentId.isNotEmpty())
|
||||
{
|
||||
auto type = tokens[0];
|
||||
auto mediaId = tokens[1];
|
||||
|
||||
if (type == "image")
|
||||
type = "images";
|
||||
|
||||
return getCursorDataColumn (URL ("content://media/external/" + type + "/media"),
|
||||
"_id=?", StringArray {mediaId});
|
||||
}
|
||||
|
||||
return getCursorDataColumn (url);
|
||||
}
|
||||
|
||||
static String getFileNameFromContentUri (const URL& url)
|
||||
{
|
||||
auto uri = urlToUri (url);
|
||||
auto* env = getEnv();
|
||||
LocalRef<jobject> contentResolver (env->CallObjectMethod (getAppContext().get(), AndroidContext.getContentResolver));
|
||||
|
||||
if (contentResolver == nullptr)
|
||||
return {};
|
||||
|
||||
auto filename = getStringUsingDataColumn ("_display_name", env, uri, contentResolver);
|
||||
|
||||
// Fallback to "_data" column
|
||||
if (filename.isEmpty())
|
||||
{
|
||||
auto path = getStringUsingDataColumn ("_data", env, uri, contentResolver);
|
||||
filename = path.fromLastOccurrenceOf ("/", false, true);
|
||||
}
|
||||
|
||||
return filename;
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
static String getCursorDataColumn (const URL& url, const String& selection = {},
|
||||
const StringArray& selectionArgs = {})
|
||||
{
|
||||
auto uri = urlToUri (url);
|
||||
auto* env = getEnv();
|
||||
LocalRef<jobject> contentResolver (env->CallObjectMethod (getAppContext().get(), AndroidContext.getContentResolver));
|
||||
|
||||
if (contentResolver)
|
||||
{
|
||||
LocalRef<jstring> columnName (javaString ("_data"));
|
||||
LocalRef<jobjectArray> projection (env->NewObjectArray (1, JavaString, columnName.get()));
|
||||
|
||||
LocalRef<jobjectArray> args;
|
||||
|
||||
if (selection.isNotEmpty())
|
||||
{
|
||||
args = LocalRef<jobjectArray> (env->NewObjectArray (selectionArgs.size(), JavaString, javaString ("").get()));
|
||||
|
||||
for (int i = 0; i < selectionArgs.size(); ++i)
|
||||
env->SetObjectArrayElement (args.get(), i, javaString (selectionArgs[i]).get());
|
||||
}
|
||||
|
||||
LocalRef<jstring> jSelection (selection.isNotEmpty() ? javaString (selection) : LocalRef<jstring>());
|
||||
LocalRef<jobject> cursor (env->CallObjectMethod (contentResolver.get(), ContentResolver.query,
|
||||
uri.get(), projection.get(), jSelection.get(),
|
||||
args.get(), nullptr));
|
||||
|
||||
if (jniCheckHasExceptionOccurredAndClear())
|
||||
{
|
||||
// An exception has occurred, have you acquired RuntimePermission::readExternalStorage permission?
|
||||
jassertfalse;
|
||||
return {};
|
||||
}
|
||||
|
||||
if (cursor)
|
||||
{
|
||||
if (env->CallBooleanMethod (cursor.get(), AndroidCursor.moveToFirst) != 0)
|
||||
{
|
||||
auto columnIndex = env->CallIntMethod (cursor.get(), AndroidCursor.getColumnIndex, columnName.get());
|
||||
|
||||
if (columnIndex >= 0)
|
||||
{
|
||||
LocalRef<jstring> value ((jstring) env->CallObjectMethod (cursor.get(), AndroidCursor.getString, columnIndex));
|
||||
|
||||
if (value)
|
||||
return juceString (value.get());
|
||||
}
|
||||
}
|
||||
|
||||
env->CallVoidMethod (cursor.get(), AndroidCursor.close);
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static File getStorageDevicePath (const String& storageId)
|
||||
{
|
||||
// check for the primary alias
|
||||
if (storageId == "primary")
|
||||
return getPrimaryStorageDirectory();
|
||||
|
||||
auto storageDevices = getSecondaryStorageDirectories();
|
||||
|
||||
for (auto storageDevice : storageDevices)
|
||||
if (getStorageIdForMountPoint (storageDevice) == storageId)
|
||||
return storageDevice;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
static File getPrimaryStorageDirectory()
|
||||
{
|
||||
auto* env = getEnv();
|
||||
return juceFile (LocalRef<jobject> (env->CallStaticObjectMethod (AndroidEnvironment, AndroidEnvironment.getExternalStorageDirectory)));
|
||||
}
|
||||
|
||||
static Array<File> getSecondaryStorageDirectories()
|
||||
{
|
||||
Array<File> results;
|
||||
|
||||
if (getAndroidSDKVersion() >= 19)
|
||||
{
|
||||
auto* env = getEnv();
|
||||
static jmethodID m = (env->GetMethodID (AndroidContext, "getExternalFilesDirs",
|
||||
"(Ljava/lang/String;)[Ljava/io/File;"));
|
||||
if (m == nullptr)
|
||||
return {};
|
||||
|
||||
auto paths = convertFileArray (LocalRef<jobject> (env->CallObjectMethod (getAppContext().get(), m, nullptr)));
|
||||
|
||||
for (auto path : paths)
|
||||
results.add (getMountPointForFile (path));
|
||||
}
|
||||
else
|
||||
{
|
||||
// on older SDKs other external storages are located "next" to the primary
|
||||
// storage mount point
|
||||
auto mountFolder = getMountPointForFile (getPrimaryStorageDirectory())
|
||||
.getParentDirectory();
|
||||
|
||||
// don't include every folder. Only folders which are actually mountpoints
|
||||
juce_statStruct info;
|
||||
if (! juce_stat (mountFolder.getFullPathName(), info))
|
||||
return {};
|
||||
|
||||
auto rootFsDevice = info.st_dev;
|
||||
|
||||
for (const auto& iter : RangedDirectoryIterator (mountFolder, false, "*", File::findDirectories))
|
||||
{
|
||||
auto candidate = iter.getFile();
|
||||
|
||||
if (juce_stat (candidate.getFullPathName(), info)
|
||||
&& info.st_dev != rootFsDevice)
|
||||
results.add (candidate);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static String getStorageIdForMountPoint (const File& mountpoint)
|
||||
{
|
||||
// currently this seems to work fine, but something
|
||||
// more intelligent may be needed in the future
|
||||
return mountpoint.getFileName();
|
||||
}
|
||||
|
||||
static File getMountPointForFile (const File& file)
|
||||
{
|
||||
juce_statStruct info;
|
||||
|
||||
if (juce_stat (file.getFullPathName(), info))
|
||||
{
|
||||
auto dev = info.st_dev;
|
||||
File mountPoint = file;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
auto parent = mountPoint.getParentDirectory();
|
||||
|
||||
if (parent == mountPoint)
|
||||
break;
|
||||
|
||||
juce_stat (parent.getFullPathName(), info);
|
||||
|
||||
if (info.st_dev != dev)
|
||||
break;
|
||||
|
||||
mountPoint = parent;
|
||||
}
|
||||
|
||||
return mountPoint;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static Array<File> convertFileArray (LocalRef<jobject> obj)
|
||||
{
|
||||
auto* env = getEnv();
|
||||
int n = (int) env->GetArrayLength ((jobjectArray) obj.get());
|
||||
Array<File> files;
|
||||
|
||||
for (int i = 0; i < n; ++i)
|
||||
files.add (juceFile (LocalRef<jobject> (env->GetObjectArrayElement ((jobjectArray) obj.get(),
|
||||
(jsize) i))));
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static String getStringUsingDataColumn (const String& columnNameToUse, JNIEnv* env,
|
||||
const LocalRef<jobject>& uri,
|
||||
const LocalRef<jobject>& contentResolver)
|
||||
{
|
||||
LocalRef<jstring> columnName (javaString (columnNameToUse));
|
||||
LocalRef<jobjectArray> projection (env->NewObjectArray (1, JavaString, columnName.get()));
|
||||
|
||||
LocalRef<jobject> cursor (env->CallObjectMethod (contentResolver.get(), ContentResolver.query,
|
||||
uri.get(), projection.get(), nullptr,
|
||||
nullptr, nullptr));
|
||||
|
||||
if (jniCheckHasExceptionOccurredAndClear())
|
||||
{
|
||||
// An exception has occurred, have you acquired RuntimePermission::readExternalStorage permission?
|
||||
jassertfalse;
|
||||
return {};
|
||||
}
|
||||
|
||||
if (cursor == nullptr)
|
||||
return {};
|
||||
|
||||
String fileName;
|
||||
|
||||
if (env->CallBooleanMethod (cursor.get(), AndroidCursor.moveToFirst) != 0)
|
||||
{
|
||||
auto columnIndex = env->CallIntMethod (cursor.get(), AndroidCursor.getColumnIndex, columnName.get());
|
||||
|
||||
if (columnIndex >= 0)
|
||||
{
|
||||
LocalRef<jstring> value ((jstring) env->CallObjectMethod (cursor.get(), AndroidCursor.getString, columnIndex));
|
||||
|
||||
if (value)
|
||||
fileName = juceString (value.get());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
env->CallVoidMethod (cursor.get(), AndroidCursor.close);
|
||||
|
||||
return fileName;
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
struct AndroidContentUriOutputStream : public OutputStream
|
||||
{
|
||||
AndroidContentUriOutputStream (LocalRef<jobject>&& outputStream)
|
||||
: stream (outputStream)
|
||||
{
|
||||
}
|
||||
|
||||
~AndroidContentUriOutputStream() override
|
||||
{
|
||||
stream.callVoidMethod (AndroidOutputStream.close);
|
||||
}
|
||||
|
||||
void flush() override
|
||||
{
|
||||
stream.callVoidMethod (AndroidOutputStream.flush);
|
||||
}
|
||||
|
||||
bool setPosition (int64 newPos) override
|
||||
{
|
||||
return (newPos == pos);
|
||||
}
|
||||
|
||||
int64 getPosition() override
|
||||
{
|
||||
return pos;
|
||||
}
|
||||
|
||||
bool write (const void* dataToWrite, size_t numberOfBytes) override
|
||||
{
|
||||
if (numberOfBytes == 0)
|
||||
return true;
|
||||
|
||||
JNIEnv* env = getEnv();
|
||||
|
||||
jbyteArray javaArray = env->NewByteArray ((jsize) numberOfBytes);
|
||||
env->SetByteArrayRegion (javaArray, 0, (jsize) numberOfBytes, (const jbyte*) dataToWrite);
|
||||
|
||||
stream.callVoidMethod (AndroidOutputStream.write, javaArray, 0, (jint) numberOfBytes);
|
||||
env->DeleteLocalRef (javaArray);
|
||||
|
||||
pos += static_cast<int64> (numberOfBytes);
|
||||
return true;
|
||||
}
|
||||
|
||||
GlobalRef stream;
|
||||
int64 pos = 0;
|
||||
};
|
||||
|
||||
OutputStream* juce_CreateContentURIOutputStream (const URL& url)
|
||||
{
|
||||
auto stream = AndroidContentUriResolver::getStreamForContentUri (url, false);
|
||||
|
||||
return (stream.get() != nullptr ? new AndroidContentUriOutputStream (std::move (stream)) : nullptr);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class MediaScannerConnectionClient : public AndroidInterfaceImplementer
|
||||
{
|
||||
public:
|
||||
virtual void onMediaScannerConnected() = 0;
|
||||
virtual void onScanCompleted() = 0;
|
||||
|
||||
private:
|
||||
jobject invoke (jobject proxy, jobject method, jobjectArray args) override
|
||||
{
|
||||
auto* env = getEnv();
|
||||
|
||||
auto methodName = juceString ((jstring) env->CallObjectMethod (method, JavaMethod.getName));
|
||||
|
||||
if (methodName == "onMediaScannerConnected")
|
||||
{
|
||||
onMediaScannerConnected();
|
||||
return nullptr;
|
||||
}
|
||||
else if (methodName == "onScanCompleted")
|
||||
{
|
||||
onScanCompleted();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return AndroidInterfaceImplementer::invoke (proxy, method, args);
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
bool File::isOnCDRomDrive() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool File::isOnHardDisk() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool File::isOnRemovableDrive() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
String File::getVersion() const
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
static File getDocumentsDirectory()
|
||||
{
|
||||
auto* env = getEnv();
|
||||
|
||||
if (getAndroidSDKVersion() >= 19)
|
||||
return getWellKnownFolder ("DIRECTORY_DOCUMENTS");
|
||||
|
||||
return juceFile (LocalRef<jobject> (env->CallStaticObjectMethod (AndroidEnvironment, AndroidEnvironment.getDataDirectory)));
|
||||
}
|
||||
|
||||
static File getAppDataDir (bool dataDir)
|
||||
{
|
||||
auto* env = getEnv();
|
||||
|
||||
LocalRef<jobject> applicationInfo (env->CallObjectMethod (getAppContext().get(), AndroidContext.getApplicationInfo));
|
||||
LocalRef<jobject> jString (env->GetObjectField (applicationInfo.get(), dataDir ? AndroidApplicationInfo.dataDir : AndroidApplicationInfo.publicSourceDir));
|
||||
|
||||
return {juceString ((jstring) jString.get())};
|
||||
}
|
||||
|
||||
File File::getSpecialLocation (const SpecialLocationType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case userHomeDirectory:
|
||||
case userApplicationDataDirectory:
|
||||
case userDesktopDirectory:
|
||||
case commonApplicationDataDirectory:
|
||||
{
|
||||
static File appDataDir = getAppDataDir (true);
|
||||
return appDataDir;
|
||||
}
|
||||
|
||||
case userDocumentsDirectory:
|
||||
case commonDocumentsDirectory:
|
||||
{
|
||||
static auto docsDir = getDocumentsDirectory();
|
||||
return docsDir;
|
||||
}
|
||||
|
||||
case userPicturesDirectory:
|
||||
{
|
||||
static auto picturesDir = getWellKnownFolder ("DIRECTORY_PICTURES");
|
||||
return picturesDir;
|
||||
}
|
||||
|
||||
case userMusicDirectory:
|
||||
{
|
||||
static auto musicDir = getWellKnownFolder ("DIRECTORY_MUSIC");
|
||||
return musicDir;
|
||||
}
|
||||
case userMoviesDirectory:
|
||||
{
|
||||
static auto moviesDir = getWellKnownFolder ("DIRECTORY_MOVIES");
|
||||
return moviesDir;
|
||||
}
|
||||
|
||||
case globalApplicationsDirectory:
|
||||
return File ("/system/app");
|
||||
|
||||
case tempDirectory:
|
||||
{
|
||||
File tmp = getSpecialLocation (commonApplicationDataDirectory).getChildFile (".temp");
|
||||
tmp.createDirectory();
|
||||
return File (tmp.getFullPathName());
|
||||
}
|
||||
|
||||
case invokedExecutableFile:
|
||||
case currentExecutableFile:
|
||||
case currentApplicationFile:
|
||||
case hostApplicationPath:
|
||||
return getAppDataDir (false);
|
||||
|
||||
default:
|
||||
jassertfalse; // unknown type?
|
||||
break;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
bool File::moveToTrash() const
|
||||
{
|
||||
if (! exists())
|
||||
return true;
|
||||
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
|
||||
JUCE_API bool JUCE_CALLTYPE Process::openDocument (const String& fileName, const String&)
|
||||
{
|
||||
URL targetURL (fileName);
|
||||
auto* env = getEnv();
|
||||
|
||||
const LocalRef<jstring> action (javaString ("android.intent.action.VIEW"));
|
||||
LocalRef<jobject> intent (env->NewObject (AndroidIntent, AndroidIntent.constructWithUri, action.get(), urlToUri (targetURL).get()));
|
||||
|
||||
env->CallVoidMethod (getCurrentActivity(), AndroidContext.startActivity, intent.get());
|
||||
return true;
|
||||
}
|
||||
|
||||
void File::revealToUser() const
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class SingleMediaScanner : public MediaScannerConnectionClient
|
||||
{
|
||||
public:
|
||||
SingleMediaScanner (const String& filename)
|
||||
: msc (LocalRef<jobject> (getEnv()->NewObject (MediaScannerConnection,
|
||||
MediaScannerConnection.constructor,
|
||||
getAppContext().get(),
|
||||
CreateJavaInterface (this, "android/media/MediaScannerConnection$MediaScannerConnectionClient").get()))),
|
||||
file (filename)
|
||||
{
|
||||
getEnv()->CallVoidMethod (msc.get(), MediaScannerConnection.connect);
|
||||
}
|
||||
|
||||
void onMediaScannerConnected() override
|
||||
{
|
||||
auto* env = getEnv();
|
||||
|
||||
env->CallVoidMethod (msc.get(), MediaScannerConnection.scanFile, javaString (file).get(), 0);
|
||||
}
|
||||
|
||||
void onScanCompleted() override
|
||||
{
|
||||
getEnv()->CallVoidMethod (msc.get(), MediaScannerConnection.disconnect);
|
||||
}
|
||||
|
||||
private:
|
||||
GlobalRef msc;
|
||||
String file;
|
||||
};
|
||||
|
||||
void FileOutputStream::flushInternal()
|
||||
{
|
||||
if (fileHandle != nullptr)
|
||||
{
|
||||
if (fsync (getFD (fileHandle)) == -1)
|
||||
status = getResultForErrno();
|
||||
|
||||
// This stuff tells the OS to asynchronously update the metadata
|
||||
// that the OS has cached about the file - this metadata is used
|
||||
// when the device is acting as a USB drive, and unless it's explicitly
|
||||
// refreshed, it'll get out of step with the real file.
|
||||
new SingleMediaScanner (file.getFullPathName());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace juce
|
703
deps/juce/modules/juce_core/native/juce_android_JNIHelpers.cpp
vendored
Normal file
703
deps/juce/modules/juce_core/native/juce_android_JNIHelpers.cpp
vendored
Normal file
@ -0,0 +1,703 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
static const uint8 invocationHandleByteCode[] =
|
||||
{31,139,8,8,215,115,161,94,0,3,105,110,118,111,99,97,116,105,111,110,72,97,110,100,108,101,66,121,116,101,67,111,100,101,46,
|
||||
100,101,120,0,109,148,65,107,19,65,20,199,223,236,78,146,90,211,116,77,141,214,88,33,151,130,7,117,91,172,80,73,17,161,32,53,93,
|
||||
17,108,233,65,5,217,38,155,102,219,237,110,220,108,99,172,8,173,40,42,244,36,245,226,65,232,165,138,7,15,226,65,193,147,120,244,
|
||||
166,130,95,192,155,23,189,21,68,252,207,206,180,137,218,133,223,204,155,247,222,206,123,111,119,230,85,156,86,247,208,201,
|
||||
83,84,121,127,155,63,122,245,209,24,205,141,63,156,124,186,176,229,110,12,151,158,157,126,219,76,39,136,234,68,212,154,25,201,
|
||||
146,122,38,56,81,158,164,126,15,248,10,160,162,95,128,129,99,24,82,152,71,152,92,123,24,86,116,162,53,204,203,26,209,29,112,
|
||||
15,108,128,231,224,37,248,2,126,128,4,252,6,192,56,184,6,102,65,21,220,2,171,224,1,120,2,94,128,215,224,29,248,0,62,129,111,224,
|
||||
59,248,169,203,184,72,157,146,36,115,233,82,185,118,131,189,160,7,232,138,171,154,204,95,200,53,77,218,83,170,214,180,146,
|
||||
35,77,238,153,139,107,212,99,27,35,141,122,213,218,80,181,239,83,250,108,60,51,234,139,247,213,148,191,68,188,61,13,149,208,142,
|
||||
97,24,228,180,99,63,194,69,206,122,44,111,233,50,223,186,33,52,7,32,93,30,2,35,68,25,229,1,95,46,235,140,173,5,97,17,107,153,
|
||||
97,154,203,245,212,89,216,17,103,24,33,71,81,141,88,215,135,52,226,44,131,90,121,252,141,250,184,172,109,170,222,233,219,67,83,
|
||||
33,234,191,158,186,155,122,156,218,108,38,69,212,52,106,204,210,209,223,180,243,48,53,139,60,214,88,251,219,203,178,116,236,
|
||||
223,165,190,117,50,254,15,42,243,49,215,119,163,51,196,74,148,47,45,149,157,243,126,51,40,219,145,27,248,19,182,95,241,156,240,
|
||||
196,188,221,180,41,97,149,44,203,34,110,137,113,208,42,7,139,102,184,216,240,204,121,188,98,238,250,94,145,242,86,197,246,154,
|
||||
238,130,105,251,126,16,197,54,115,186,22,6,55,26,69,202,90,98,91,211,179,253,57,243,226,236,188,83,142,138,148,235,208,197,126,
|
||||
246,172,231,20,17,173,173,14,157,170,7,95,115,215,104,255,187,93,112,162,90,80,41,18,155,33,109,166,68,125,87,118,137,202,237,
|
||||
112,174,65,137,178,231,216,33,25,21,183,81,183,163,114,237,156,235,219,158,187,236,80,102,91,35,66,46,56,212,85,221,182,36,93,
|
||||
169,73,46,198,81,168,199,71,66,77,103,60,240,35,167,21,145,241,215,242,146,83,165,68,61,12,90,55,137,71,53,23,1,155,182,183,
|
||||
132,237,216,193,84,70,203,23,181,185,210,113,156,146,84,102,146,246,99,188,127,153,14,235,253,185,94,72,155,164,105,236,208,0,
|
||||
235,231,196,116,113,134,87,87,248,186,174,225,246,50,1,123,163,235,236,179,206,216,138,248,207,198,63,103,65,204,219,61,66,235,
|
||||
232,19,122,71,175,224,29,253,34,65,237,158,145,164,118,223,208,13,41,199,231,170,32,223,89,23,62,5,169,23,247,135,25,82,31,223,
|
||||
169,130,140,43,250,140,174,252,197,61,226,133,246,253,34,37,15,170,196,133,44,122,218,31,165,24,139,249,12,5,0,0,0,0};
|
||||
|
||||
//==============================================================================
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
STATICMETHOD (newProxyInstance, "newProxyInstance", "(Ljava/lang/ClassLoader;[Ljava/lang/Class;Ljava/lang/reflect/InvocationHandler;)Ljava/lang/Object;") \
|
||||
|
||||
DECLARE_JNI_CLASS (JavaProxy, "java/lang/reflect/Proxy")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
//==============================================================================
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
METHOD (constructor, "<init>", "(J)V") \
|
||||
METHOD (clear, "clear", "()V") \
|
||||
CALLBACK (juce_invokeImplementer, "dispatchInvoke", "(JLjava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;") \
|
||||
CALLBACK (juce_dispatchDelete, "dispatchFinalize", "(J)V")
|
||||
|
||||
DECLARE_JNI_CLASS_WITH_BYTECODE (JuceInvocationHandler, "com/rmsl/juce/JuceInvocationHandler", 10, invocationHandleByteCode, sizeof (invocationHandleByteCode))
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
METHOD (findClass, "findClass", "(Ljava/lang/String;)Ljava/lang/Class;") \
|
||||
STATICMETHOD (getSystemClassLoader, "getSystemClassLoader", "()Ljava/lang/ClassLoader;")
|
||||
|
||||
DECLARE_JNI_CLASS (JavaClassLoader, "java/lang/ClassLoader")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
METHOD (constructor, "<init>", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;)V")
|
||||
|
||||
DECLARE_JNI_CLASS (AndroidDexClassLoader, "dalvik/system/DexClassLoader")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
METHOD (constructor, "<init>", "(Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V")
|
||||
|
||||
DECLARE_JNI_CLASS_WITH_MIN_SDK (AndroidInMemoryDexClassLoader, "dalvik/system/InMemoryDexClassLoader", 26)
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
//==============================================================================
|
||||
struct SystemJavaClassComparator
|
||||
{
|
||||
static int compareElements (JNIClassBase* first, JNIClassBase* second)
|
||||
{
|
||||
auto isSysClassA = isSystemClass (first);
|
||||
auto isSysClassB = isSystemClass (second);
|
||||
|
||||
if ((! isSysClassA) && (! isSysClassB))
|
||||
{
|
||||
return DefaultElementComparator<bool>::compareElements (first != nullptr ? first->byteCode != nullptr : false,
|
||||
second != nullptr ? second->byteCode != nullptr : false);
|
||||
}
|
||||
|
||||
return DefaultElementComparator<bool>::compareElements (isSystemClass (first),
|
||||
isSystemClass (second));
|
||||
}
|
||||
|
||||
static bool isSystemClass (JNIClassBase* cls)
|
||||
{
|
||||
if (cls == nullptr)
|
||||
return false;
|
||||
|
||||
String path (cls->getClassPath());
|
||||
|
||||
return path.startsWith ("java/")
|
||||
|| path.startsWith ("android/")
|
||||
|| path.startsWith ("dalvik/");
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
JNIClassBase::JNIClassBase (const char* cp, int classMinSDK, const void* bc, size_t n)
|
||||
: classPath (cp), byteCode (bc), byteCodeSize (n), minSDK (classMinSDK), classRef (nullptr)
|
||||
{
|
||||
SystemJavaClassComparator comparator;
|
||||
|
||||
getClasses().addSorted (comparator, this);
|
||||
}
|
||||
|
||||
JNIClassBase::~JNIClassBase()
|
||||
{
|
||||
getClasses().removeFirstMatchingValue (this);
|
||||
}
|
||||
|
||||
Array<JNIClassBase*>& JNIClassBase::getClasses()
|
||||
{
|
||||
static Array<JNIClassBase*> classes;
|
||||
return classes;
|
||||
}
|
||||
|
||||
// Get code cache directory without yet having a context object
|
||||
static File getCodeCacheDirectory()
|
||||
{
|
||||
int pid = getpid();
|
||||
File cmdline("/proc/" + String(pid) + "/cmdline");
|
||||
|
||||
auto bundleId = cmdline.loadFileAsString().trimStart().trimEnd();
|
||||
|
||||
if (bundleId.isEmpty())
|
||||
return {};
|
||||
|
||||
return File("/data/data/" + bundleId + "/code_cache");
|
||||
}
|
||||
|
||||
void JNIClassBase::initialise (JNIEnv* env)
|
||||
{
|
||||
auto sdkVersion = getAndroidSDKVersion();
|
||||
|
||||
if (sdkVersion >= minSDK)
|
||||
{
|
||||
LocalRef<jstring> classNameAndPackage (javaString (String (classPath).replaceCharacter (L'/', L'.')));
|
||||
static Array<GlobalRef> byteCodeLoaders;
|
||||
|
||||
if (! SystemJavaClassComparator::isSystemClass(this))
|
||||
{
|
||||
LocalRef<jobject> defaultClassLoader (env->CallStaticObjectMethod (JavaClassLoader, JavaClassLoader.getSystemClassLoader));
|
||||
tryLoadingClassWithClassLoader (env, defaultClassLoader.get());
|
||||
|
||||
if (classRef == nullptr)
|
||||
{
|
||||
for (auto& byteCodeLoader : byteCodeLoaders)
|
||||
{
|
||||
tryLoadingClassWithClassLoader (env, byteCodeLoader.get());
|
||||
|
||||
if (classRef != nullptr)
|
||||
break;
|
||||
}
|
||||
|
||||
// fallback by trying to load the class from bytecode
|
||||
if (byteCode != nullptr)
|
||||
{
|
||||
LocalRef<jobject> byteCodeClassLoader;
|
||||
|
||||
MemoryOutputStream uncompressedByteCode;
|
||||
|
||||
{
|
||||
MemoryInputStream rawGZipData (byteCode, byteCodeSize, false);
|
||||
GZIPDecompressorInputStream gzipStream (&rawGZipData, false, GZIPDecompressorInputStream::gzipFormat);
|
||||
uncompressedByteCode.writeFromInputStream (gzipStream, -1);
|
||||
}
|
||||
|
||||
if (sdkVersion >= 26)
|
||||
{
|
||||
LocalRef<jbyteArray> byteArray (env->NewByteArray ((jsize) uncompressedByteCode.getDataSize()));
|
||||
jboolean isCopy;
|
||||
auto* dst = env->GetByteArrayElements (byteArray.get(), &isCopy);
|
||||
memcpy (dst, uncompressedByteCode.getData(), uncompressedByteCode.getDataSize());
|
||||
env->ReleaseByteArrayElements (byteArray.get(), dst, 0);
|
||||
|
||||
LocalRef<jobject> byteBuffer (env->CallStaticObjectMethod (JavaByteBuffer, JavaByteBuffer.wrap, byteArray.get()));
|
||||
|
||||
byteCodeClassLoader = LocalRef<jobject> (env->NewObject (AndroidInMemoryDexClassLoader,
|
||||
AndroidInMemoryDexClassLoader.constructor,
|
||||
byteBuffer.get(), defaultClassLoader.get()));
|
||||
}
|
||||
else if (uncompressedByteCode.getDataSize() >= 32)
|
||||
{
|
||||
auto codeCacheDir = getCodeCacheDirectory();
|
||||
|
||||
// The dex file has an embedded 20-byte long SHA-1 signature at offset 12
|
||||
auto fileName = String::toHexString ((char*)uncompressedByteCode.getData() + 12, 20, 0) + ".dex";
|
||||
auto dexFile = codeCacheDir.getChildFile (fileName);
|
||||
auto optimizedDirectory = codeCacheDir.getChildFile ("optimized_cache");
|
||||
optimizedDirectory.createDirectory();
|
||||
|
||||
if (dexFile.replaceWithData (uncompressedByteCode.getData(), uncompressedByteCode.getDataSize()))
|
||||
{
|
||||
byteCodeClassLoader = LocalRef<jobject> (env->NewObject (AndroidDexClassLoader,
|
||||
AndroidDexClassLoader.constructor,
|
||||
javaString (dexFile.getFullPathName()).get(),
|
||||
javaString (optimizedDirectory.getFullPathName()).get(),
|
||||
nullptr,
|
||||
defaultClassLoader.get()));
|
||||
}
|
||||
else
|
||||
{
|
||||
// can't write to cache folder
|
||||
jassertfalse;
|
||||
}
|
||||
}
|
||||
|
||||
if (byteCodeClassLoader != nullptr)
|
||||
{
|
||||
tryLoadingClassWithClassLoader (env, byteCodeClassLoader.get());
|
||||
byteCodeLoaders.add (GlobalRef(byteCodeClassLoader));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (classRef == nullptr)
|
||||
classRef = (jclass) env->NewGlobalRef (LocalRef<jobject> (env->FindClass (classPath)));
|
||||
|
||||
jassert (classRef != nullptr);
|
||||
initialiseFields (env);
|
||||
}
|
||||
}
|
||||
|
||||
void JNIClassBase::tryLoadingClassWithClassLoader (JNIEnv* env, jobject classLoader)
|
||||
{
|
||||
LocalRef<jstring> classNameAndPackage (javaString (String (classPath).replaceCharacter (L'/', L'.')));
|
||||
|
||||
// Android SDK <= 19 has a bug where the class loader might throw an exception but still return
|
||||
// a non-nullptr. So don't assign the result of this call to a jobject just yet...
|
||||
auto classObj = env->CallObjectMethod (classLoader, JavaClassLoader.findClass, classNameAndPackage.get());
|
||||
|
||||
if (jthrowable exception = env->ExceptionOccurred ())
|
||||
{
|
||||
env->ExceptionClear();
|
||||
classObj = nullptr;
|
||||
}
|
||||
|
||||
// later versions of Android don't throw at all, so re-check the object
|
||||
if (classObj != nullptr)
|
||||
classRef = (jclass) env->NewGlobalRef (LocalRef<jobject> (classObj));
|
||||
}
|
||||
|
||||
void JNIClassBase::release (JNIEnv* env)
|
||||
{
|
||||
if (classRef != nullptr)
|
||||
env->DeleteGlobalRef (classRef);
|
||||
}
|
||||
|
||||
void JNIClassBase::initialiseAllClasses (JNIEnv* env)
|
||||
{
|
||||
const Array<JNIClassBase*>& classes = getClasses();
|
||||
for (int i = classes.size(); --i >= 0;)
|
||||
classes.getUnchecked(i)->initialise (env);
|
||||
}
|
||||
|
||||
void JNIClassBase::releaseAllClasses (JNIEnv* env)
|
||||
{
|
||||
const Array<JNIClassBase*>& classes = getClasses();
|
||||
for (int i = classes.size(); --i >= 0;)
|
||||
classes.getUnchecked(i)->release (env);
|
||||
}
|
||||
|
||||
jmethodID JNIClassBase::resolveMethod (JNIEnv* env, const char* methodName, const char* params)
|
||||
{
|
||||
jmethodID m = env->GetMethodID (classRef, methodName, params);
|
||||
jassert (m != nullptr);
|
||||
return m;
|
||||
}
|
||||
|
||||
jmethodID JNIClassBase::resolveStaticMethod (JNIEnv* env, const char* methodName, const char* params)
|
||||
{
|
||||
jmethodID m = env->GetStaticMethodID (classRef, methodName, params);
|
||||
jassert (m != nullptr);
|
||||
return m;
|
||||
}
|
||||
|
||||
jfieldID JNIClassBase::resolveField (JNIEnv* env, const char* fieldName, const char* signature)
|
||||
{
|
||||
jfieldID f = env->GetFieldID (classRef, fieldName, signature);
|
||||
jassert (f != nullptr);
|
||||
return f;
|
||||
}
|
||||
|
||||
jfieldID JNIClassBase::resolveStaticField (JNIEnv* env, const char* fieldName, const char* signature)
|
||||
{
|
||||
jfieldID f = env->GetStaticFieldID (classRef, fieldName, signature);
|
||||
jassert (f != nullptr);
|
||||
return f;
|
||||
}
|
||||
|
||||
void JNIClassBase::resolveCallbacks (JNIEnv* env, const Array<JNINativeMethod>& nativeCallbacks)
|
||||
{
|
||||
if (nativeCallbacks.size() > 0) {
|
||||
//DBG(String::formatted("resolveCallbacks %d for %s", nativeCallbacks.size(), classPath));
|
||||
env->RegisterNatives (classRef, nativeCallbacks.begin(), (jint) nativeCallbacks.size());
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
LocalRef<jobject> CreateJavaInterface (AndroidInterfaceImplementer* implementer,
|
||||
const StringArray& interfaceNames,
|
||||
LocalRef<jobject> subclass)
|
||||
{
|
||||
auto* env = getEnv();
|
||||
|
||||
implementer->javaSubClass = GlobalRef (subclass);
|
||||
|
||||
// you need to override at least one interface
|
||||
jassert (interfaceNames.size() > 0);
|
||||
|
||||
auto classArray = LocalRef<jobject> (env->NewObjectArray (interfaceNames.size(), JavaClass, nullptr));
|
||||
LocalRef<jobject> classLoader;
|
||||
|
||||
for (auto i = 0; i < interfaceNames.size(); ++i)
|
||||
{
|
||||
auto aClass = LocalRef<jobject> (env->FindClass (interfaceNames[i].toRawUTF8()));
|
||||
|
||||
if (aClass != nullptr)
|
||||
{
|
||||
if (i == 0)
|
||||
classLoader = LocalRef<jobject> (env->CallObjectMethod (aClass, JavaClass.getClassLoader));
|
||||
|
||||
env->SetObjectArrayElement ((jobjectArray) classArray.get(), i, aClass);
|
||||
}
|
||||
else
|
||||
{
|
||||
// interface class not found
|
||||
jassertfalse;
|
||||
}
|
||||
}
|
||||
|
||||
auto invocationHandler = LocalRef<jobject> (env->NewObject (JuceInvocationHandler, JuceInvocationHandler.constructor,
|
||||
reinterpret_cast<jlong> (implementer)));
|
||||
|
||||
// CreateJavaInterface() is expected to be called just once for a given implementer
|
||||
jassert (implementer->invocationHandler == nullptr);
|
||||
|
||||
implementer->invocationHandler = GlobalRef (invocationHandler);
|
||||
|
||||
return LocalRef<jobject> (env->CallStaticObjectMethod (JavaProxy, JavaProxy.newProxyInstance,
|
||||
classLoader.get(), classArray.get(),
|
||||
invocationHandler.get()));
|
||||
}
|
||||
|
||||
LocalRef<jobject> CreateJavaInterface (AndroidInterfaceImplementer* implementer,
|
||||
const StringArray& interfaceNames)
|
||||
{
|
||||
return CreateJavaInterface (implementer, interfaceNames,
|
||||
LocalRef<jobject> (getEnv()->NewObject (JavaObject,
|
||||
JavaObject.constructor)));
|
||||
}
|
||||
|
||||
LocalRef<jobject> CreateJavaInterface (AndroidInterfaceImplementer* implementer,
|
||||
const String& interfaceName)
|
||||
{
|
||||
return CreateJavaInterface (implementer, StringArray (interfaceName));
|
||||
}
|
||||
|
||||
AndroidInterfaceImplementer::~AndroidInterfaceImplementer()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
void AndroidInterfaceImplementer::clear()
|
||||
{
|
||||
if (invocationHandler != nullptr)
|
||||
getEnv()->CallVoidMethod (invocationHandler,
|
||||
JuceInvocationHandler.clear);
|
||||
}
|
||||
|
||||
jobject AndroidInterfaceImplementer::invoke (jobject /*proxy*/, jobject method, jobjectArray args)
|
||||
{
|
||||
auto* env = getEnv();
|
||||
return env->CallObjectMethod (method, JavaMethod.invoke, javaSubClass.get(), args);
|
||||
}
|
||||
|
||||
jobject juce_invokeImplementer (JNIEnv*, jobject /*object*/, jlong host, jobject proxy,
|
||||
jobject method, jobjectArray args)
|
||||
{
|
||||
if (auto* myself = reinterpret_cast<AndroidInterfaceImplementer*> (host))
|
||||
return myself->invoke (proxy, method, args);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void juce_dispatchDelete (JNIEnv*, jobject /*object*/, jlong host)
|
||||
{
|
||||
if (auto* myself = reinterpret_cast<AndroidInterfaceImplementer*> (host))
|
||||
delete myself;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
jobject ActivityLifecycleCallbacks::invoke (jobject proxy, jobject method, jobjectArray args)
|
||||
{
|
||||
auto* env = getEnv();
|
||||
|
||||
auto methodName = juceString ((jstring) env->CallObjectMethod (method, JavaMethod.getName));
|
||||
|
||||
auto activity = env->GetArrayLength (args) > 0 ? env->GetObjectArrayElement (args, 0) : (jobject) nullptr;
|
||||
auto bundle = env->GetArrayLength (args) > 1 ? env->GetObjectArrayElement (args, 1) : (jobject) nullptr;
|
||||
|
||||
if (methodName == "onActivityPreCreated") { onActivityPreCreated (activity, bundle); return nullptr; }
|
||||
else if (methodName == "onActivityPreDestroyed") { onActivityPreDestroyed (activity); return nullptr; }
|
||||
else if (methodName == "onActivityPrePaused") { onActivityPrePaused (activity); return nullptr; }
|
||||
else if (methodName == "onActivityPreResumed") { onActivityPreResumed (activity); return nullptr; }
|
||||
else if (methodName == "onActivityPreSaveInstanceState") { onActivityPreSaveInstanceState (activity, bundle); return nullptr; }
|
||||
else if (methodName == "onActivityPreStarted") { onActivityPreStarted (activity); return nullptr; }
|
||||
else if (methodName == "onActivityPreStopped") { onActivityPreStopped (activity); return nullptr; }
|
||||
else if (methodName == "onActivityCreated") { onActivityCreated (activity, bundle); return nullptr; }
|
||||
else if (methodName == "onActivityDestroyed") { onActivityDestroyed (activity); return nullptr; }
|
||||
else if (methodName == "onActivityPaused") { onActivityPaused (activity); return nullptr; }
|
||||
else if (methodName == "onActivityResumed") { onActivityResumed (activity); return nullptr; }
|
||||
else if (methodName == "onActivitySaveInstanceState") { onActivitySaveInstanceState (activity, bundle); return nullptr; }
|
||||
else if (methodName == "onActivityStarted") { onActivityStarted (activity); return nullptr; }
|
||||
else if (methodName == "onActivityStopped") { onActivityStopped (activity); return nullptr; }
|
||||
else if (methodName == "onActivityPostCreated") { onActivityPostCreated (activity, bundle); return nullptr; }
|
||||
else if (methodName == "onActivityPostDestroyed") { onActivityPostDestroyed (activity); return nullptr; }
|
||||
else if (methodName == "onActivityPostPaused") { onActivityPostPaused (activity); return nullptr; }
|
||||
else if (methodName == "onActivityPostResumed") { onActivityPostResumed (activity); return nullptr; }
|
||||
else if (methodName == "onActivityPostSaveInstanceState") { onActivityPostSaveInstanceState (activity, bundle); return nullptr; }
|
||||
else if (methodName == "onActivityPostStarted") { onActivityPostStarted (activity); return nullptr; }
|
||||
else if (methodName == "onActivityPostStopped") { onActivityPostStopped (activity); return nullptr; }
|
||||
|
||||
return AndroidInterfaceImplementer::invoke (proxy, method, args);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
int getAndroidSDKVersion()
|
||||
{
|
||||
// this is used so often that we need to cache this
|
||||
static int sdkVersion = []
|
||||
{
|
||||
// don't use any jni helpers as they might not have been initialised yet
|
||||
// when this method is used
|
||||
auto* env = getEnv();
|
||||
|
||||
auto buildVersion = env->FindClass ("android/os/Build$VERSION");
|
||||
jassert (buildVersion != nullptr);
|
||||
|
||||
auto sdkVersionField = env->GetStaticFieldID (buildVersion, "SDK_INT", "I");
|
||||
jassert (sdkVersionField != nullptr);
|
||||
|
||||
return env->GetStaticIntField (buildVersion, sdkVersionField);
|
||||
}();
|
||||
|
||||
return sdkVersion;
|
||||
}
|
||||
|
||||
bool isPermissionDeclaredInManifest (const String& requestedPermission)
|
||||
{
|
||||
auto* env = getEnv();
|
||||
|
||||
LocalRef<jobject> pkgManager (env->CallObjectMethod (getAppContext().get(), AndroidContext.getPackageManager));
|
||||
LocalRef<jobject> pkgName (env->CallObjectMethod (getAppContext().get(), AndroidContext.getPackageName));
|
||||
LocalRef<jobject> pkgInfo (env->CallObjectMethod (pkgManager.get(), AndroidPackageManager.getPackageInfo,
|
||||
pkgName.get(), 0x00001000 /* PERMISSIONS */));
|
||||
|
||||
LocalRef<jobjectArray> permissions ((jobjectArray) env->GetObjectField (pkgInfo.get(), AndroidPackageInfo.requestedPermissions));
|
||||
int n = env->GetArrayLength (permissions);
|
||||
|
||||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
LocalRef<jstring> jstr ((jstring) env->GetObjectArrayElement (permissions, i));
|
||||
String permissionId (juceString (jstr));
|
||||
|
||||
if (permissionId == requestedPermission)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// This byte-code is generated from native/java/com/rmsl/juce/FragmentOverlay.java with min sdk version 16
|
||||
// See juce_core/native/java/README.txt on how to generate this byte-code.
|
||||
static const uint8 javaFragmentOverlay[] =
|
||||
{31,139,8,8,26,116,161,94,0,3,106,97,118,97,70,114,97,103,109,101,110,116,79,118,101,114,108,97,121,46,100,101,120,0,133,149,
|
||||
77,136,28,69,20,199,255,53,253,181,159,179,147,221,184,140,235,198,140,43,70,197,224,172,104,36,56,99,216,152,32,204,100,226,71,
|
||||
54,204,97,227,165,153,105,39,189,206,118,79,186,123,150,4,20,53,4,146,131,8,6,252,130,28,114,80,65,48,8,226,65,196,83,8,66,64,
|
||||
65,146,75,252,184,152,179,160,160,4,17,5,255,175,187,58,27,150,136,195,252,250,189,122,245,234,189,170,215,213,85,93,239,248,
|
||||
216,226,163,187,96,79,85,156,198,103,91,86,175,30,189,252,253,193,79,203,15,189,242,199,245,246,129,179,245,238,53,27,24,0,56,
|
||||
222,126,108,26,250,183,155,182,7,145,217,199,200,86,149,201,58,37,255,248,156,143,18,229,87,186,93,47,0,47,155,192,11,148,87,
|
||||
12,224,7,242,27,249,157,220,32,127,145,127,200,93,244,217,69,154,228,37,242,42,57,73,206,144,55,201,89,242,62,57,79,62,36,31,
|
||||
147,11,228,34,185,76,174,144,107,228,103,242,43,249,147,216,22,80,38,139,228,9,210,36,47,146,51,228,45,114,158,92,32,95,146,175,
|
||||
201,183,132,211,4,167,3,46,19,14,25,33,163,122,173,227,100,70,214,76,24,62,93,223,41,58,91,186,13,237,227,104,125,66,235,111,
|
||||
208,103,82,235,239,81,47,106,253,3,234,83,90,255,196,200,234,38,250,23,212,183,104,253,18,245,105,173,127,147,230,82,152,133,
|
||||
204,179,144,230,40,112,118,119,235,246,130,158,199,28,196,47,235,23,121,135,150,101,100,227,239,76,165,129,249,84,218,216,150,
|
||||
202,44,142,197,21,111,79,165,137,74,42,29,220,163,199,47,164,210,194,189,200,214,172,0,157,37,211,229,55,98,103,210,160,69,108,
|
||||
87,173,172,134,131,146,248,202,204,87,42,82,129,188,255,71,221,159,247,4,37,155,126,69,214,209,76,223,193,117,43,91,255,50,55,
|
||||
220,44,147,61,194,48,187,217,187,28,177,38,199,212,41,245,182,243,209,186,61,202,88,69,200,72,89,255,47,28,35,107,10,43,10,135,
|
||||
25,209,161,117,2,115,106,22,65,197,96,149,199,177,178,196,136,75,183,70,116,210,246,96,137,121,159,47,166,239,49,203,127,227,
|
||||
127,242,59,105,254,201,52,191,212,86,246,142,12,148,247,23,150,100,62,183,205,179,56,5,83,21,117,221,108,189,231,160,101,166,
|
||||
143,166,117,81,154,124,191,73,111,174,139,71,33,213,77,237,99,215,253,192,79,246,96,235,211,145,219,91,243,130,228,217,117,47,
|
||||
234,187,39,30,94,117,215,93,168,6,84,19,133,102,11,170,133,249,150,27,116,163,208,239,86,221,193,160,186,223,119,251,97,47,31,
|
||||
85,67,249,102,111,39,12,18,154,170,141,84,212,48,115,179,39,140,171,79,13,131,110,223,171,97,123,171,19,174,85,163,181,184,95,
|
||||
93,29,118,188,234,166,244,53,76,183,100,6,213,190,27,244,170,203,73,228,7,189,26,84,27,102,187,209,104,201,179,213,66,161,221,
|
||||
132,213,110,138,65,4,45,70,187,41,102,114,164,129,153,35,183,9,97,117,250,97,236,193,233,12,6,135,143,250,49,204,174,155,184,
|
||||
112,186,126,188,230,199,49,38,122,94,178,55,234,13,101,42,49,28,182,90,97,208,163,57,114,131,228,144,23,15,251,52,151,194,96,
|
||||
111,39,241,215,253,228,68,102,194,236,102,203,51,46,91,30,70,194,96,95,228,185,137,135,98,174,233,158,185,48,56,228,29,27,122,
|
||||
113,242,156,23,73,106,63,12,98,29,173,242,223,125,122,180,19,6,203,137,27,37,152,212,138,182,143,15,54,6,96,60,202,130,236,11,
|
||||
187,30,198,162,116,124,170,91,113,34,83,50,19,41,192,54,56,197,194,206,26,246,83,30,168,99,143,177,227,254,178,83,60,253,14,22,
|
||||
212,3,78,177,126,233,244,10,30,55,118,220,55,79,219,187,216,73,167,39,105,129,178,248,121,155,175,191,102,254,100,90,39,121,
|
||||
146,220,130,165,254,54,13,117,206,42,168,239,200,57,155,210,158,220,244,205,139,204,239,4,217,143,249,189,96,96,227,110,200,247,
|
||||
172,220,15,114,118,228,119,132,141,141,123,66,85,178,182,220,21,170,148,157,11,114,190,22,42,89,124,185,63,12,237,35,231,138,
|
||||
28,80,42,63,115,74,153,46,247,211,191,81,33,150,205,216,6,0,0,0,0};
|
||||
|
||||
//==============================================================================
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
METHOD (construct, "<init>", "()V") \
|
||||
METHOD (close, "close", "()V") \
|
||||
CALLBACK (FragmentOverlay::onActivityResultNative, "onActivityResultNative", "(JIILandroid/content/Intent;)V") \
|
||||
CALLBACK (FragmentOverlay::onCreateNative, "onCreateNative", "(JLandroid/os/Bundle;)V") \
|
||||
CALLBACK (FragmentOverlay::onStartNative, "onStartNative", "(J)V") \
|
||||
CALLBACK (FragmentOverlay::onRequestPermissionsResultNative, "onRequestPermissionsResultNative", "(JI[Ljava/lang/String;[I)V")
|
||||
|
||||
DECLARE_JNI_CLASS_WITH_BYTECODE (JuceFragmentOverlay, "com/rmsl/juce/FragmentOverlay", 16, javaFragmentOverlay, sizeof(javaFragmentOverlay))
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
METHOD (show, "show", "(Landroid/app/FragmentManager;Ljava/lang/String;)V")
|
||||
|
||||
DECLARE_JNI_CLASS (AndroidDialogFragment, "android/app/DialogFragment")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
//==============================================================================
|
||||
FragmentOverlay::FragmentOverlay()
|
||||
: native (LocalRef<jobject> (getEnv()->NewObject (JuceFragmentOverlay, JuceFragmentOverlay.construct)))
|
||||
{}
|
||||
|
||||
FragmentOverlay::~FragmentOverlay()
|
||||
{
|
||||
auto* env = getEnv();
|
||||
|
||||
env->CallVoidMethod (native.get(), JuceFragmentOverlay.close);
|
||||
}
|
||||
|
||||
void FragmentOverlay::open()
|
||||
{
|
||||
auto* env = getEnv();
|
||||
|
||||
LocalRef<jobject> bundle (env->NewObject (AndroidBundle, AndroidBundle.constructor));
|
||||
env->CallVoidMethod (bundle.get(), AndroidBundle.putLong, javaString ("cppThis").get(), (jlong) this);
|
||||
env->CallVoidMethod (native.get(), AndroidFragment.setArguments, bundle.get());
|
||||
|
||||
LocalRef<jobject> fm (env->CallObjectMethod (getCurrentActivity().get(), AndroidActivity.getFragmentManager));
|
||||
env->CallVoidMethod (native.get(), AndroidDialogFragment.show, fm.get(), javaString ("FragmentOverlay").get());
|
||||
}
|
||||
|
||||
void FragmentOverlay::onActivityResultNative (JNIEnv* env, jobject, jlong host,
|
||||
jint requestCode, jint resultCode, jobject data)
|
||||
{
|
||||
if (auto* myself = reinterpret_cast<FragmentOverlay*> (host))
|
||||
myself->onActivityResult (requestCode, resultCode, LocalRef<jobject> (env->NewLocalRef (data)));
|
||||
}
|
||||
|
||||
void FragmentOverlay::onCreateNative (JNIEnv* env, jobject, jlong host, jobject bundle)
|
||||
{
|
||||
if (auto* myself = reinterpret_cast<FragmentOverlay*> (host))
|
||||
myself->onCreated (LocalRef<jobject> (env->NewLocalRef (bundle)));
|
||||
}
|
||||
|
||||
void FragmentOverlay::onStartNative (JNIEnv*, jobject, jlong host)
|
||||
{
|
||||
if (auto* myself = reinterpret_cast<FragmentOverlay*> (host))
|
||||
myself->onStart();
|
||||
}
|
||||
|
||||
void FragmentOverlay::onRequestPermissionsResultNative (JNIEnv* env, jobject, jlong host, jint requestCode,
|
||||
jobjectArray jPermissions, jintArray jGrantResults)
|
||||
{
|
||||
if (auto* myself = reinterpret_cast<FragmentOverlay*> (host))
|
||||
{
|
||||
Array<int> grantResults;
|
||||
int n = (jGrantResults != nullptr ? env->GetArrayLength (jGrantResults) : 0);
|
||||
|
||||
if (n > 0)
|
||||
{
|
||||
auto* data = env->GetIntArrayElements (jGrantResults, nullptr);
|
||||
|
||||
for (int i = 0; i < n; ++i)
|
||||
grantResults.add (data[i]);
|
||||
|
||||
env->ReleaseIntArrayElements (jGrantResults, data, 0);
|
||||
}
|
||||
|
||||
myself->onRequestPermissionsResult (requestCode,
|
||||
javaStringArrayToJuce (LocalRef<jobjectArray> (jPermissions)),
|
||||
grantResults);
|
||||
}
|
||||
}
|
||||
|
||||
jobject FragmentOverlay::getNativeHandle()
|
||||
{
|
||||
return native.get();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class ActivityLauncher : public FragmentOverlay
|
||||
{
|
||||
public:
|
||||
ActivityLauncher (const LocalRef<jobject>& intentToUse,
|
||||
int requestCodeToUse,
|
||||
std::function<void (int, int, LocalRef<jobject>)> && callbackToUse)
|
||||
: intent (intentToUse), requestCode (requestCodeToUse), callback (std::move (callbackToUse))
|
||||
{}
|
||||
|
||||
void onStart() override
|
||||
{
|
||||
getEnv()->CallVoidMethod (getNativeHandle(), AndroidFragment.startActivityForResult,
|
||||
intent.get(), requestCode);
|
||||
}
|
||||
|
||||
void onActivityResult (int activityRequestCode, int resultCode, LocalRef<jobject> data) override
|
||||
{
|
||||
if (callback)
|
||||
callback (activityRequestCode, resultCode, std::move (data));
|
||||
|
||||
getEnv()->CallVoidMethod (getNativeHandle(), JuceFragmentOverlay.close);
|
||||
delete this;
|
||||
}
|
||||
|
||||
private:
|
||||
GlobalRef intent;
|
||||
int requestCode;
|
||||
std::function<void (int, int, LocalRef<jobject>)> callback;
|
||||
};
|
||||
|
||||
void startAndroidActivityForResult (const LocalRef<jobject>& intent, int requestCode,
|
||||
std::function<void (int, int, LocalRef<jobject>)> && callback)
|
||||
{
|
||||
auto* activityLauncher = new ActivityLauncher (intent, requestCode, std::move (callback));
|
||||
activityLauncher->open();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool androidHasSystemFeature (const String& property)
|
||||
{
|
||||
LocalRef<jobject> appContext (getAppContext());
|
||||
|
||||
if (appContext != nullptr)
|
||||
{
|
||||
auto* env = getEnv();
|
||||
|
||||
LocalRef<jobject> packageManager (env->CallObjectMethod (appContext.get(), AndroidContext.getPackageManager));
|
||||
|
||||
if (packageManager != nullptr)
|
||||
return env->CallBooleanMethod (packageManager.get(),
|
||||
AndroidPackageManager.hasSystemFeature,
|
||||
javaString (property).get()) != 0;
|
||||
}
|
||||
|
||||
// unable to get app's context
|
||||
jassertfalse;
|
||||
return false;
|
||||
}
|
||||
|
||||
String audioManagerGetProperty (const String& property)
|
||||
{
|
||||
if (getAndroidSDKVersion() >= 17)
|
||||
{
|
||||
auto* env = getEnv();
|
||||
LocalRef<jobject> audioManager (env->CallObjectMethod (getAppContext().get(), AndroidContext.getSystemService,
|
||||
javaString ("audio").get()));
|
||||
|
||||
if (audioManager != nullptr)
|
||||
{
|
||||
LocalRef<jstring> jProperty (javaString (property));
|
||||
|
||||
auto methodID = env->GetMethodID (AndroidAudioManager, "getProperty", "(Ljava/lang/String;)Ljava/lang/String;");
|
||||
|
||||
if (methodID != nullptr)
|
||||
return juceString (LocalRef<jstring> ((jstring) env->CallObjectMethod (audioManager.get(),
|
||||
methodID,
|
||||
javaString (property).get())));
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
1010
deps/juce/modules/juce_core/native/juce_android_JNIHelpers.h
vendored
Normal file
1010
deps/juce/modules/juce_core/native/juce_android_JNIHelpers.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
44
deps/juce/modules/juce_core/native/juce_android_Misc.cpp
vendored
Normal file
44
deps/juce/modules/juce_core/native/juce_android_Misc.cpp
vendored
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
void Logger::outputDebugString (const String& text)
|
||||
{
|
||||
char* data = text.toUTF8().getAddress();
|
||||
const size_t length = CharPointer_UTF8::getBytesRequiredFor (text.getCharPointer());
|
||||
const size_t chunkSize = 1023;
|
||||
|
||||
size_t position = 0;
|
||||
size_t numToRead = jmin (chunkSize, length);
|
||||
|
||||
while (numToRead > 0)
|
||||
{
|
||||
__android_log_print (ANDROID_LOG_INFO, "JUCE", "%s", data + position);
|
||||
|
||||
position += numToRead;
|
||||
numToRead = jmin (chunkSize, length - position);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace juce
|
662
deps/juce/modules/juce_core/native/juce_android_Network.cpp
vendored
Normal file
662
deps/juce/modules/juce_core/native/juce_android_Network.cpp
vendored
Normal file
@ -0,0 +1,662 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
// This byte-code is generated from native/java/com/rmsl/juce/JuceHTTPStream.java with min sdk version 16
|
||||
// See juce_core/native/java/README.txt on how to generate this byte-code.
|
||||
static const uint8 javaJuceHttpStream[] =
|
||||
{31,139,8,8,71,116,161,94,0,3,106,97,118,97,74,117,99,101,72,116,116,112,83,116,114,101,97,109,46,100,101,120,0,125,154,11,124,
|
||||
84,87,157,199,255,231,222,185,119,30,153,153,220,76,30,147,132,60,38,33,132,4,18,38,80,104,41,9,148,242,42,144,80,40,12,84,8,
|
||||
91,29,50,23,50,48,185,19,102,238,64,168,180,133,62,233,67,75,223,184,86,173,72,45,186,85,171,86,173,182,174,109,105,93,31,181,
|
||||
85,87,119,171,187,186,186,91,171,246,211,221,118,235,171,171,31,55,251,59,143,201,76,218,44,132,239,253,255,207,255,252,207,
|
||||
185,231,241,63,143,153,36,101,79,4,250,46,88,66,151,94,114,238,181,147,179,244,212,201,205,225,159,190,152,158,247,165,219,39,
|
||||
126,178,240,215,230,173,135,207,118,18,141,19,209,196,142,197,17,82,255,94,153,67,244,35,146,246,69,224,105,157,40,14,121,194,
|
||||
67,84,15,249,148,73,116,5,228,81,47,17,178,200,19,32,90,211,68,148,130,124,171,150,232,15,224,109,240,23,240,191,64,171,35,50,
|
||||
64,8,84,129,40,232,6,23,131,45,96,27,184,18,236,6,123,128,13,174,6,215,128,227,224,102,112,27,184,19,156,2,103,192,89,240,44,
|
||||
120,5,52,70,137,150,129,171,192,13,224,19,224,155,224,87,128,161,193,49,112,17,184,28,236,5,199,192,3,224,81,240,34,120,5,188,
|
||||
13,194,13,68,237,96,0,92,9,14,128,235,193,67,224,49,240,29,240,11,240,22,240,55,18,189,7,164,193,213,224,111,193,147,224,183,
|
||||
160,102,22,234,0,87,129,163,224,195,224,39,96,18,244,96,156,222,3,70,192,94,176,31,56,32,15,222,15,142,129,59,192,73,112,15,120,
|
||||
0,124,24,124,12,124,2,60,1,158,2,223,2,63,6,63,7,175,130,55,192,159,192,95,129,209,76,20,4,81,16,3,61,96,13,216,1,70,193,213,
|
||||
224,14,240,17,240,48,120,18,156,3,47,129,87,193,95,128,217,130,62,2,11,52,130,14,48,31,44,2,23,129,77,224,111,128,3,174,5,55,
|
||||
131,7,192,25,240,37,240,60,248,30,248,103,240,91,240,22,152,4,190,86,162,58,208,9,250,193,101,96,35,216,6,222,7,246,131,195,224,
|
||||
24,56,1,238,3,31,3,15,131,207,128,47,130,175,130,23,193,207,192,107,224,45,240,103,64,49,244,29,84,130,57,160,31,236,0,123,193,
|
||||
1,48,14,14,129,163,224,102,112,18,124,12,60,12,62,11,190,1,126,12,126,6,254,29,252,26,252,25,120,219,136,102,131,62,176,18,
|
||||
108,5,123,193,24,112,193,81,112,2,124,8,60,2,62,7,190,6,190,13,126,14,126,7,244,118,196,5,104,4,237,160,23,44,7,151,129,4,200,
|
||||
128,2,184,6,220,6,238,2,167,64,16,221,178,0,194,138,16,62,132,233,37,76,15,97,40,73,117,153,80,61,193,149,102,131,14,128,229,
|
||||
75,88,214,52,23,116,129,110,48,15,204,7,61,160,23,44,32,185,166,251,192,66,181,206,47,0,139,193,18,112,33,184,8,44,5,23,131,126,
|
||||
48,0,86,128,75,192,74,112,41,88,5,214,128,117,224,50,176,30,108,4,91,193,118,176,3,92,73,178,31,197,127,33,37,151,98,111,8,
|
||||
43,125,101,153,190,30,122,165,210,183,212,202,254,51,149,230,251,143,1,118,195,30,41,171,151,235,197,242,13,74,119,149,79,177,
|
||||
174,90,229,183,84,217,107,149,189,90,233,71,149,61,90,102,143,42,123,141,210,111,132,94,167,244,59,148,189,94,217,107,149,190,
|
||||
84,233,13,101,58,159,183,123,107,101,57,174,63,168,222,213,92,214,254,150,50,189,181,76,111,47,211,103,151,245,133,207,239,163,
|
||||
170,126,62,199,93,170,206,121,202,135,207,67,175,210,7,149,206,251,178,89,233,143,67,31,82,250,83,101,122,95,153,206,219,191,
|
||||
73,233,207,65,223,162,116,62,254,151,43,253,133,50,159,127,173,149,103,67,175,26,255,98,61,175,212,202,152,88,160,218,179,77,
|
||||
233,175,195,158,80,186,171,250,178,80,189,87,199,76,127,139,184,92,64,223,135,244,96,228,108,33,107,105,159,144,61,148,22,50,
|
||||
76,31,20,178,155,190,65,60,62,154,40,37,164,244,51,148,159,129,17,43,8,57,159,110,20,50,74,183,40,121,171,144,178,30,3,239,187,
|
||||
155,100,156,157,22,146,209,167,132,140,211,167,133,172,167,207,10,217,71,95,23,178,248,94,172,121,37,95,0,26,85,209,125,162,
|
||||
253,109,100,10,233,165,247,10,25,20,210,131,246,152,66,182,210,231,69,185,78,145,230,237,222,169,218,57,34,100,29,237,21,210,
|
||||
164,81,101,63,40,164,159,142,8,105,208,117,74,30,87,249,39,133,212,233,81,33,91,233,51,106,28,190,76,124,205,180,139,247,132,
|
||||
176,91,112,25,70,126,82,72,143,240,175,68,250,176,144,243,232,135,34,238,42,232,128,138,191,59,69,236,181,138,114,81,140,203,
|
||||
30,37,63,64,50,182,239,23,114,1,125,77,200,74,122,70,72,75,200,122,172,168,221,66,70,104,76,72,89,174,30,35,37,165,44,95,175,
|
||||
252,27,212,123,26,176,202,118,11,25,166,239,18,223,11,103,11,123,35,242,159,36,190,158,90,201,17,210,71,87,11,217,72,239,87,233,
|
||||
163,66,6,232,90,146,235,238,152,144,81,186,94,200,24,125,81,200,110,122,92,201,47,41,251,87,132,236,160,39,132,156,67,95,37,
|
||||
190,86,229,120,53,99,87,149,178,158,206,8,41,219,213,138,113,191,73,200,32,125,148,248,222,28,162,9,226,251,115,144,174,81,242,
|
||||
6,226,235,121,22,185,196,215,114,3,221,75,124,29,55,211,211,196,247,234,22,202,8,217,68,31,34,190,166,123,233,125,66,6,69,61,
|
||||
243,213,120,204,199,143,76,119,211,23,136,239,233,210,206,229,3,66,206,167,23,85,250,37,146,119,52,34,185,182,248,30,81,5,249,
|
||||
4,54,174,196,28,105,247,148,229,247,169,252,151,145,63,166,242,121,60,51,42,237,151,60,255,109,228,223,164,242,121,253,255,132,
|
||||
131,231,103,224,213,14,233,251,39,37,249,97,164,65,248,33,173,57,210,214,168,100,183,146,171,148,28,156,195,235,210,133,254,32,
|
||||
54,61,31,228,48,18,187,53,70,227,214,44,17,161,60,151,215,247,112,187,220,35,219,89,144,182,199,24,206,41,139,18,49,162,131,
|
||||
150,87,68,184,99,245,9,57,30,171,70,137,42,54,61,111,129,144,93,111,241,182,49,241,190,207,182,203,126,58,22,183,4,69,31,77,
|
||||
252,240,188,199,219,229,121,34,219,48,204,52,26,214,12,26,214,61,52,236,209,105,216,48,197,202,225,237,50,232,165,118,121,30,38,
|
||||
250,60,168,139,239,180,1,156,159,181,148,88,168,83,51,75,244,153,104,169,143,184,140,68,114,177,245,136,145,22,38,125,47,23,165,
|
||||
52,228,123,133,116,172,94,97,49,148,197,128,165,118,170,230,45,98,52,66,40,61,23,207,146,173,235,143,72,45,148,41,38,78,8,
|
||||
194,234,213,1,151,76,157,93,186,56,231,121,207,151,8,169,163,237,23,92,23,186,112,57,86,102,128,248,40,189,218,46,247,110,222,
|
||||
103,31,70,185,7,218,38,120,14,31,247,35,85,43,236,60,197,243,230,35,181,81,164,42,176,47,241,155,64,144,29,180,46,227,227,200,
|
||||
134,143,7,104,247,109,149,52,124,123,132,134,239,8,210,206,15,212,209,240,7,107,104,248,206,106,138,252,247,206,227,81,188,217,
|
||||
162,157,199,44,188,165,138,134,143,133,104,251,13,149,148,184,49,66,137,155,130,180,21,87,255,196,45,33,242,30,247,222,125,
|
||||
200,235,87,53,122,209,115,77,221,8,170,103,203,248,75,96,110,171,68,108,122,69,140,52,194,158,231,241,225,107,70,12,44,195,58,
|
||||
118,172,1,188,35,232,107,245,53,145,247,198,86,79,19,69,124,78,236,66,156,4,78,108,49,253,30,207,139,240,108,244,189,7,90,43,
|
||||
206,129,160,22,209,219,186,150,220,16,167,117,62,77,111,244,235,176,55,211,41,10,152,203,205,118,97,115,112,57,246,81,192,183,
|
||||
228,230,70,145,142,248,29,92,17,79,153,65,198,83,47,251,77,230,196,98,40,17,244,114,79,47,60,151,251,48,139,125,23,83,141,247,
|
||||
101,93,103,93,63,72,156,13,161,214,165,104,195,210,96,132,34,245,78,108,9,215,67,60,70,23,34,166,130,134,131,15,47,63,70,42,
|
||||
138,216,170,242,132,68,84,120,208,171,16,53,250,253,232,89,13,234,159,235,147,119,156,43,249,140,178,89,251,170,113,46,4,160,31,
|
||||
153,45,239,23,253,225,90,138,132,219,81,131,247,227,236,11,222,115,222,31,177,95,121,255,232,243,90,164,249,170,200,231,143,208,
|
||||
161,128,41,198,238,226,240,127,77,86,135,251,10,93,175,133,88,132,186,222,150,99,91,37,235,109,172,16,49,30,166,7,103,243,
|
||||
25,198,216,251,78,178,72,77,68,79,60,236,167,102,35,113,186,2,173,27,132,71,64,91,138,245,209,98,176,69,149,90,36,50,30,11,226,
|
||||
28,9,106,195,167,195,148,56,93,67,17,51,241,136,23,158,139,248,136,120,7,189,154,41,173,107,52,205,92,124,253,63,82,68,155,
|
||||
94,130,251,198,81,107,80,203,89,171,149,92,195,165,153,179,86,96,133,242,183,61,71,57,235,18,232,65,214,202,218,160,95,202,35,
|
||||
5,246,55,39,115,214,42,165,191,54,153,120,164,26,35,215,128,104,237,194,188,21,189,90,181,40,230,160,27,187,110,80,107,244,121,
|
||||
132,126,61,226,164,209,175,81,177,116,171,86,67,7,99,98,39,214,74,190,84,244,213,28,107,158,104,153,19,107,199,154,43,175,185,
|
||||
209,27,128,87,19,229,208,206,139,181,179,147,165,26,99,20,241,230,98,43,145,35,61,15,90,13,178,14,171,81,140,225,206,211,81,
|
||||
234,186,51,36,198,177,155,118,79,134,212,88,44,18,185,75,174,111,147,163,233,73,124,82,90,49,18,230,160,169,121,164,87,47,188,
|
||||
102,206,151,245,45,158,156,121,46,186,105,217,100,72,107,49,230,106,33,45,113,230,221,30,30,115,238,59,230,180,155,226,40,209,
|
||||
77,61,147,168,121,85,55,121,39,249,254,225,199,174,84,45,246,80,175,184,7,243,216,233,16,123,86,181,248,172,113,29,210,95,20,
|
||||
123,79,72,156,177,252,204,255,190,242,251,23,101,255,55,149,254,79,149,126,83,236,218,140,254,42,234,173,38,31,147,118,227,186,
|
||||
208,23,216,235,236,186,206,175,176,55,24,189,206,232,13,118,109,231,77,154,48,122,49,14,252,76,249,205,108,249,121,39,162,37,
|
||||
182,249,168,89,79,92,33,163,149,81,128,45,101,136,86,221,219,134,83,226,138,0,181,99,79,31,239,99,148,48,115,177,181,248,140,
|
||||
131,29,231,138,90,228,39,240,204,89,27,196,30,212,202,194,212,245,219,16,107,209,231,50,236,78,109,93,255,33,158,191,144,103,
|
||||
105,3,124,248,186,243,225,189,115,213,62,171,81,167,86,221,61,167,167,6,186,23,173,15,97,48,248,157,161,26,31,110,34,161,67,172,
|
||||
2,253,9,88,23,91,134,176,84,90,47,135,66,204,7,219,86,102,209,133,205,255,51,201,245,4,195,124,232,107,170,152,94,205,122,25,
|
||||
191,93,46,178,106,168,154,45,80,58,198,150,197,149,14,127,118,129,208,47,176,126,170,74,87,83,163,190,1,209,216,129,27,118,128,
|
||||
189,28,102,129,146,29,177,220,55,135,22,134,3,149,23,189,241,172,242,231,243,126,129,232,175,99,45,231,210,95,244,119,250,230,
|
||||
210,47,43,131,21,197,221,245,20,181,7,161,245,245,211,29,21,220,55,136,216,88,116,247,71,167,222,27,209,133,230,65,73,171,19,
|
||||
99,20,240,56,125,179,105,161,71,88,17,43,62,92,45,155,35,210,187,124,94,42,197,104,180,68,186,105,98,18,163,29,193,104,7,184,
|
||||
165,155,198,203,218,216,11,111,158,218,174,87,146,124,79,68,74,35,40,228,86,179,78,166,189,33,196,76,53,21,79,18,110,27,70,13,
|
||||
188,254,149,147,178,102,174,175,154,36,113,63,159,139,150,222,172,246,191,7,196,60,122,232,35,144,94,104,252,222,199,119,255,
|
||||
103,85,90,167,251,176,69,158,98,215,205,122,158,221,199,72,221,98,136,110,239,144,123,232,182,88,149,248,28,91,180,223,213,33,
|
||||
99,101,107,44,138,59,187,53,117,158,61,208,33,239,75,17,236,232,126,120,123,248,59,59,228,103,67,156,137,123,176,211,106,137,
|
||||
100,152,150,50,63,113,25,209,157,149,117,116,240,134,0,181,104,203,209,158,109,123,170,232,184,231,201,85,187,32,43,9,99,166,
|
||||
97,204,88,215,239,24,201,187,149,71,173,161,70,21,151,179,166,206,255,6,22,106,226,49,203,16,157,26,125,185,67,126,119,208,142,
|
||||
18,137,131,184,127,228,176,23,231,241,78,23,183,151,66,45,29,50,121,77,137,156,104,207,193,10,138,84,59,177,33,196,85,132,13,
|
||||
195,187,69,107,55,27,17,25,243,81,151,99,245,16,191,33,201,182,68,196,141,136,137,239,39,152,248,153,131,55,200,27,234,139,101,
|
||||
125,231,223,37,242,127,26,149,164,27,144,99,121,20,242,198,128,188,51,22,191,23,224,231,215,201,128,28,211,83,144,15,169,252,
|
||||
242,187,47,207,55,85,61,126,37,249,247,7,103,149,111,135,170,175,78,201,168,122,111,81,198,85,125,113,85,167,159,228,103,157,
|
||||
184,240,232,19,159,95,162,170,236,130,178,182,149,247,193,162,26,97,55,84,155,75,229,163,194,62,79,249,241,207,160,140,228,189,
|
||||
83,182,33,42,202,52,193,210,131,222,116,43,123,179,42,23,167,226,120,48,12,111,23,225,194,198,150,145,182,44,70,108,128,204,129,
|
||||
180,147,118,87,144,182,162,159,60,43,250,187,119,144,181,58,235,56,246,136,155,206,58,49,59,151,203,230,40,12,139,107,59,110,
|
||||
239,144,237,236,115,71,169,118,77,58,63,50,229,180,181,224,56,201,61,25,155,216,6,210,54,12,145,190,97,104,3,121,240,192,126,
|
||||
184,145,170,55,22,70,236,245,137,196,150,109,110,206,78,142,45,216,159,60,148,36,54,68,26,156,116,238,163,13,161,200,16,30,222,
|
||||
161,161,93,67,67,168,32,160,20,174,107,67,187,168,126,40,233,164,114,217,116,42,238,218,19,110,60,129,199,118,55,157,201,247,
|
||||
83,108,104,36,59,22,207,141,229,51,241,253,120,77,124,250,187,58,22,246,211,226,243,123,204,216,145,126,106,57,111,169,126,106,
|
||||
31,74,37,51,135,210,7,226,73,199,201,186,73,94,56,190,214,25,201,100,243,105,103,223,234,76,50,143,182,205,62,159,207,38,219,
|
||||
29,205,166,248,139,222,237,180,1,237,201,169,74,218,102,200,223,100,143,237,81,14,54,92,154,103,112,217,150,222,231,36,221,66,
|
||||
14,93,105,156,33,59,49,154,203,30,22,69,249,108,196,211,217,248,170,194,222,189,118,206,78,109,112,198,11,110,177,151,181,83,
|
||||
217,27,54,175,157,24,177,199,121,225,105,230,114,239,186,41,243,230,130,91,102,175,151,246,76,210,217,23,95,61,154,204,109,179,
|
||||
15,22,108,103,196,158,170,72,228,148,213,95,93,102,222,128,184,219,103,231,248,76,79,55,230,114,133,113,215,78,149,21,171,41,
|
||||
247,128,131,156,201,72,153,117,243,158,253,152,232,233,158,165,57,47,247,68,219,49,73,211,219,46,109,114,160,250,169,97,134,156,
|
||||
116,38,197,179,202,43,194,72,219,201,212,244,174,138,209,151,239,108,146,102,199,118,227,235,93,119,124,251,214,161,210,218,
|
||||
235,167,112,41,23,57,83,173,81,233,114,79,213,163,2,86,133,236,187,155,69,67,172,50,235,80,58,239,78,53,67,88,54,37,199,59,214,
|
||||
58,110,238,72,63,109,154,201,60,240,238,241,120,71,125,51,120,172,192,15,85,78,175,110,186,97,155,237,242,176,47,25,176,254,70,
|
||||
10,185,28,182,151,248,234,100,38,35,118,146,214,243,231,247,83,207,255,231,128,192,130,15,31,149,178,208,232,158,217,123,237,
|
||||
132,61,82,120,135,107,231,249,92,179,8,222,220,161,52,143,221,216,249,253,242,83,115,251,78,143,117,5,190,46,139,75,111,230,220,
|
||||
126,26,56,95,246,192,121,151,45,38,160,99,230,210,50,24,215,37,71,208,64,204,250,252,153,189,16,59,99,233,145,248,165,66,172,
|
||||
202,102,51,118,18,227,50,111,102,231,76,118,228,64,62,190,213,134,158,75,58,238,16,146,253,228,135,16,83,176,140,216,14,210,118,
|
||||
96,143,223,129,61,126,7,246,120,19,15,190,215,35,177,139,2,59,202,246,249,29,187,136,237,34,109,215,70,0,57,188,10,108,160,
|
||||
234,225,25,86,165,182,219,161,64,114,100,196,206,231,59,250,250,250,168,66,234,235,50,201,125,121,242,38,83,169,28,82,100,38,
|
||||
199,199,109,39,69,222,61,201,188,189,61,151,33,115,143,24,45,242,140,32,140,200,28,17,177,66,6,223,145,109,242,99,195,31,79,230,
|
||||
236,68,150,188,234,68,160,64,233,104,160,186,146,158,200,150,78,13,178,70,48,166,174,93,90,140,69,139,156,42,62,32,69,75,233,0,
|
||||
41,90,100,74,248,84,170,17,45,14,29,153,41,123,36,155,178,169,38,101,239,77,22,50,238,180,201,227,185,25,219,181,41,144,42,
|
||||
53,165,54,53,227,169,92,61,205,44,171,33,127,42,171,154,76,204,38,131,79,223,17,242,9,129,5,10,77,197,49,25,123,211,118,38,5,
|
||||
145,41,228,71,73,223,135,204,26,60,138,11,13,47,80,93,10,195,186,150,223,22,84,186,18,233,245,120,149,157,91,199,107,200,11,135,
|
||||
178,56,37,19,233,65,251,136,112,44,63,49,168,2,134,45,56,37,197,48,240,220,173,118,126,60,235,228,49,200,24,15,94,77,2,7,89,70,
|
||||
221,69,120,53,216,13,201,7,185,35,153,41,216,100,141,38,243,171,16,145,170,141,54,66,0,150,203,113,107,160,138,81,209,162,161,
|
||||
180,99,35,82,100,34,79,65,165,36,178,219,17,9,225,81,236,196,91,249,33,149,119,87,143,165,168,122,122,90,58,5,184,81,181,151,
|
||||
165,201,155,118,82,246,196,230,189,84,145,46,235,161,47,237,168,38,85,164,243,107,39,70,147,133,188,203,91,147,206,139,113,32,
|
||||
51,157,71,63,93,158,203,165,172,217,151,86,251,55,121,246,103,211,136,131,140,236,167,135,47,53,242,56,201,49,76,169,99,31,94,
|
||||
157,28,25,181,83,114,58,183,96,149,82,136,27,75,97,88,129,100,41,152,144,192,43,132,228,75,193,227,240,209,208,157,2,26,137,
|
||||
199,170,35,46,198,163,6,218,186,108,38,147,61,108,167,182,218,169,116,14,53,73,235,84,42,145,149,14,212,48,147,85,141,95,22,
|
||||
203,174,172,33,122,22,93,213,199,241,118,15,30,11,197,115,17,249,198,139,51,204,53,119,77,210,77,82,168,168,201,138,60,34,82,189,
|
||||
57,68,58,86,48,85,230,84,24,172,87,211,86,243,14,131,44,101,194,138,229,66,122,174,128,119,231,17,175,85,120,168,246,36,210,99,
|
||||
54,111,78,5,76,107,178,50,234,168,49,207,3,51,239,242,112,145,29,41,245,158,59,78,197,98,56,207,99,49,153,42,214,98,137,180,
|
||||
8,12,121,159,163,72,201,178,37,135,113,200,185,71,200,200,143,103,210,46,132,155,204,97,206,33,220,66,158,2,82,138,144,174,44,
|
||||
233,178,15,193,188,8,32,117,84,152,249,194,158,49,212,224,135,204,139,45,144,66,238,104,249,150,83,205,147,239,220,117,74,198,
|
||||
178,109,38,12,99,249,34,172,68,122,91,249,203,60,238,104,26,113,194,159,29,125,228,119,209,85,12,211,38,44,223,41,85,197,169,
|
||||
155,149,251,49,85,184,101,203,177,142,159,3,249,189,217,220,152,157,186,188,44,2,189,216,89,68,11,204,130,35,34,185,226,80,50,
|
||||
211,81,92,9,198,33,177,76,140,195,57,68,63,105,19,125,116,55,211,190,205,200,27,166,251,249,115,114,217,0,44,60,249,184,102,
|
||||
156,99,223,100,207,179,231,240,137,219,248,7,54,111,157,55,60,208,203,255,209,211,26,28,78,44,31,76,183,238,30,28,30,188,106,88,
|
||||
27,111,94,219,118,142,122,252,187,239,101,143,178,91,217,119,217,55,216,99,236,12,187,157,121,195,218,95,180,254,137,137,35,218,
|
||||
245,215,156,17,133,123,7,6,135,88,164,146,58,217,143,144,75,59,205,221,247,176,191,99,39,216,119,132,247,43,186,231,195,172,
|
||||
121,112,224,146,3,186,118,11,91,196,152,174,127,128,177,229,39,116,243,17,198,62,57,161,179,195,245,39,116,239,15,89,125,90,59,
|
||||
212,111,48,195,171,133,251,13,115,120,193,208,130,65,221,248,36,179,6,46,49,140,22,67,51,244,86,157,242,77,3,180,210,63,85,
|
||||
253,223,179,207,177,79,176,219,240,146,222,57,172,42,138,54,247,106,21,253,109,244,54,122,187,113,249,45,90,111,143,182,179,89,
|
||||
171,56,252,196,196,181,172,209,242,48,183,189,255,57,214,80,201,234,67,43,126,201,234,171,13,198,171,12,227,216,52,136,107,
|
||||
115,79,60,126,213,247,250,180,150,31,24,164,237,233,63,215,113,191,102,245,211,29,140,221,205,188,205,168,173,182,71,27,107,254,
|
||||
105,219,93,43,209,5,202,215,180,182,24,36,180,206,86,250,52,31,222,94,221,250,60,139,183,233,161,211,140,13,245,106,207,80,
|
||||
143,97,105,151,54,155,214,173,71,244,170,175,51,198,234,171,206,233,225,179,232,180,94,249,41,198,22,156,211,253,7,234,111,215,
|
||||
43,30,100,245,187,116,223,85,45,187,244,224,71,89,203,144,174,189,200,234,227,204,178,142,60,172,133,118,26,214,74,244,221,
|
||||
103,4,91,245,64,190,73,115,54,26,1,83,51,125,102,208,180,222,207,90,106,47,106,53,3,116,171,206,39,247,131,226,121,143,120,254,
|
||||
94,211,222,251,113,12,75,155,78,15,97,172,7,39,218,123,247,107,19,205,3,244,75,62,205,127,208,105,220,88,190,127,160,117,151,
|
||||
110,30,156,53,208,96,152,116,74,103,31,225,115,167,121,217,15,88,99,80,243,105,187,60,116,138,181,104,1,164,155,140,104,111,116,
|
||||
121,212,136,118,68,189,90,5,55,96,4,139,74,149,102,66,169,55,68,17,223,41,22,173,144,101,60,209,213,170,132,72,250,163,44,
|
||||
186,56,218,25,93,143,170,100,134,79,243,203,26,60,197,170,58,73,99,26,157,140,49,252,63,118,204,243,88,173,198,190,95,203,158,
|
||||
137,61,84,71,6,99,26,50,197,15,121,142,31,243,60,85,103,176,223,212,81,149,233,245,106,44,34,126,68,30,211,166,253,136,180,9,
|
||||
255,199,234,217,137,216,203,13,90,228,205,6,214,240,251,89,44,242,66,19,139,28,107,247,85,157,156,109,84,189,221,201,170,78,
|
||||
206,101,85,103,193,83,224,177,46,86,117,166,91,254,125,4,149,125,223,192,101,241,239,183,248,119,4,197,191,225,42,126,119,193,
|
||||
255,142,139,127,79,82,252,91,46,254,189,66,241,239,185,76,42,253,77,151,110,201,223,193,241,239,98,88,76,254,254,247,49,254,29,
|
||||
74,76,250,240,223,33,50,171,244,123,69,45,38,223,203,255,6,76,87,254,252,247,120,158,24,137,223,63,241,223,17,146,42,43,126,
|
||||
247,104,201,182,242,191,55,251,63,108,101,104,241,168,38,0,0,0,0};
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
METHOD (constructor, "<init>", "()V") \
|
||||
METHOD (toString, "toString", "()Ljava/lang/String;") \
|
||||
|
||||
DECLARE_JNI_CLASS (StringBuffer, "java/lang/StringBuffer")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
//==============================================================================
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
STATICMETHOD (createHTTPStream, "createHTTPStream", "(Ljava/lang/String;Z[BLjava/lang/String;I[ILjava/lang/StringBuffer;ILjava/lang/String;)Lcom/rmsl/juce/JuceHTTPStream;") \
|
||||
METHOD (connect, "connect", "()Z") \
|
||||
METHOD (release, "release", "()V") \
|
||||
METHOD (read, "read", "([BI)I") \
|
||||
METHOD (getPosition, "getPosition", "()J") \
|
||||
METHOD (getTotalLength, "getTotalLength", "()J") \
|
||||
METHOD (isExhausted, "isExhausted", "()Z") \
|
||||
METHOD (setPosition, "setPosition", "(J)Z") \
|
||||
|
||||
DECLARE_JNI_CLASS_WITH_BYTECODE (HTTPStream, "com/rmsl/juce/JuceHTTPStream", 16, javaJuceHttpStream, sizeof(javaJuceHttpStream))
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
//==============================================================================
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
METHOD (close, "close", "()V") \
|
||||
METHOD (read, "read", "([BII)I") \
|
||||
|
||||
DECLARE_JNI_CLASS (AndroidInputStream, "java/io/InputStream")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
//==============================================================================
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
METHOD (acquire, "acquire", "()V") \
|
||||
METHOD (release, "release", "()V") \
|
||||
|
||||
DECLARE_JNI_CLASS (AndroidMulticastLock, "android/net/wifi/WifiManager$MulticastLock")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
METHOD (createMulticastLock, "createMulticastLock", "(Ljava/lang/String;)Landroid/net/wifi/WifiManager$MulticastLock;") \
|
||||
|
||||
DECLARE_JNI_CLASS (AndroidWifiManager, "android/net/wifi/WifiManager")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
static LocalRef<jobject> getMulticastLock()
|
||||
{
|
||||
static LocalRef<jobject> multicastLock;
|
||||
static bool hasChecked = false;
|
||||
|
||||
if (! hasChecked)
|
||||
{
|
||||
hasChecked = true;
|
||||
|
||||
auto* env = getEnv();
|
||||
|
||||
LocalRef<jobject> wifiManager (env->CallObjectMethod (getAppContext().get(),
|
||||
AndroidContext.getSystemService,
|
||||
javaString ("wifi").get()));
|
||||
|
||||
if (wifiManager != nullptr)
|
||||
{
|
||||
multicastLock = LocalRef<jobject> (env->CallObjectMethod (wifiManager.get(),
|
||||
AndroidWifiManager.createMulticastLock,
|
||||
javaString ("JUCE_MulticastLock").get()));
|
||||
}
|
||||
}
|
||||
|
||||
return multicastLock;
|
||||
}
|
||||
|
||||
JUCE_API void JUCE_CALLTYPE acquireMulticastLock()
|
||||
{
|
||||
auto multicastLock = getMulticastLock();
|
||||
|
||||
if (multicastLock != nullptr)
|
||||
getEnv()->CallVoidMethod (multicastLock.get(), AndroidMulticastLock.acquire);
|
||||
}
|
||||
|
||||
JUCE_API void JUCE_CALLTYPE releaseMulticastLock()
|
||||
{
|
||||
auto multicastLock = getMulticastLock();
|
||||
|
||||
if (multicastLock != nullptr)
|
||||
getEnv()->CallVoidMethod (multicastLock.get(), AndroidMulticastLock.release);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void MACAddress::findAllAddresses (Array<MACAddress>& /*result*/)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
|
||||
JUCE_API bool JUCE_CALLTYPE Process::openEmailWithAttachments (const String& /*targetEmailAddress*/,
|
||||
const String& /*emailSubject*/,
|
||||
const String& /*bodyText*/,
|
||||
const StringArray& /*filesToAttach*/)
|
||||
{
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool URL::isLocalFile() const
|
||||
{
|
||||
if (getScheme() == "file")
|
||||
return true;
|
||||
|
||||
if (getScheme() == "content")
|
||||
{
|
||||
auto file = AndroidContentUriResolver::getLocalFileFromContentUri (*this);
|
||||
return (file != File());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
File URL::getLocalFile() const
|
||||
{
|
||||
if (getScheme() == "content")
|
||||
{
|
||||
auto path = AndroidContentUriResolver::getLocalFileFromContentUri (*this);
|
||||
|
||||
// This URL does not refer to a local file
|
||||
// Call URL::isLocalFile to first check if the URL
|
||||
// refers to a local file.
|
||||
jassert (path != File());
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
return fileFromFileSchemeURL (*this);
|
||||
}
|
||||
|
||||
String URL::getFileName() const
|
||||
{
|
||||
if (getScheme() == "content")
|
||||
return AndroidContentUriResolver::getFileNameFromContentUri (*this);
|
||||
|
||||
return toString (false).fromLastOccurrenceOf ("/", false, true);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class WebInputStream::Pimpl
|
||||
{
|
||||
public:
|
||||
enum { contentStreamCacheSize = 1024 };
|
||||
|
||||
Pimpl (WebInputStream&, const URL& urlToCopy, bool addParametersToBody)
|
||||
: url (urlToCopy),
|
||||
isContentURL (urlToCopy.getScheme() == "content"),
|
||||
addParametersToRequestBody (addParametersToBody),
|
||||
hasBodyDataToSend (addParametersToRequestBody || url.hasBodyDataToSend()),
|
||||
httpRequest (hasBodyDataToSend ? "POST" : "GET")
|
||||
{
|
||||
}
|
||||
|
||||
~Pimpl()
|
||||
{
|
||||
cancel();
|
||||
}
|
||||
|
||||
void cancel()
|
||||
{
|
||||
if (isContentURL)
|
||||
{
|
||||
stream.callVoidMethod (AndroidInputStream.close);
|
||||
return;
|
||||
}
|
||||
|
||||
const ScopedLock lock (createStreamLock);
|
||||
|
||||
if (stream != nullptr)
|
||||
{
|
||||
stream.callVoidMethod (HTTPStream.release);
|
||||
stream.clear();
|
||||
}
|
||||
|
||||
hasBeenCancelled = true;
|
||||
}
|
||||
|
||||
bool connect (WebInputStream::Listener* /*listener*/)
|
||||
{
|
||||
auto* env = getEnv();
|
||||
|
||||
if (isContentURL)
|
||||
{
|
||||
auto inputStream = AndroidContentUriResolver::getStreamForContentUri (url, true);
|
||||
|
||||
if (inputStream != nullptr)
|
||||
{
|
||||
stream = GlobalRef (inputStream);
|
||||
statusCode = 200;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
String address = url.toString (! addParametersToRequestBody);
|
||||
|
||||
if (! address.contains ("://"))
|
||||
address = "http://" + address;
|
||||
|
||||
MemoryBlock postData;
|
||||
|
||||
if (hasBodyDataToSend)
|
||||
WebInputStream::createHeadersAndPostData (url,
|
||||
headers,
|
||||
postData,
|
||||
addParametersToRequestBody);
|
||||
|
||||
jbyteArray postDataArray = nullptr;
|
||||
|
||||
if (! postData.isEmpty())
|
||||
{
|
||||
postDataArray = env->NewByteArray (static_cast<jsize> (postData.getSize()));
|
||||
env->SetByteArrayRegion (postDataArray, 0, static_cast<jsize> (postData.getSize()), (const jbyte*) postData.getData());
|
||||
}
|
||||
|
||||
LocalRef<jobject> responseHeaderBuffer (env->NewObject (StringBuffer, StringBuffer.constructor));
|
||||
|
||||
// Annoyingly, the android HTTP functions will choke on this call if you try to do it on the message
|
||||
// thread. You'll need to move your networking code to a background thread to keep it happy..
|
||||
jassert (Thread::getCurrentThread() != nullptr);
|
||||
|
||||
jintArray statusCodeArray = env->NewIntArray (1);
|
||||
jassert (statusCodeArray != nullptr);
|
||||
|
||||
{
|
||||
const ScopedLock lock (createStreamLock);
|
||||
|
||||
if (! hasBeenCancelled)
|
||||
stream = GlobalRef (LocalRef<jobject> (env->CallStaticObjectMethod (HTTPStream,
|
||||
HTTPStream.createHTTPStream,
|
||||
javaString (address).get(),
|
||||
(jboolean) addParametersToRequestBody,
|
||||
postDataArray,
|
||||
javaString (headers).get(),
|
||||
(jint) timeOutMs,
|
||||
statusCodeArray,
|
||||
responseHeaderBuffer.get(),
|
||||
(jint) numRedirectsToFollow,
|
||||
javaString (httpRequest).get())));
|
||||
}
|
||||
|
||||
if (stream != nullptr && ! stream.callBooleanMethod (HTTPStream.connect))
|
||||
stream.clear();
|
||||
|
||||
jint* const statusCodeElements = env->GetIntArrayElements (statusCodeArray, nullptr);
|
||||
statusCode = statusCodeElements[0];
|
||||
env->ReleaseIntArrayElements (statusCodeArray, statusCodeElements, 0);
|
||||
env->DeleteLocalRef (statusCodeArray);
|
||||
|
||||
if (postDataArray != nullptr)
|
||||
env->DeleteLocalRef (postDataArray);
|
||||
|
||||
if (stream != nullptr)
|
||||
{
|
||||
StringArray headerLines;
|
||||
|
||||
{
|
||||
LocalRef<jstring> headersString ((jstring) env->CallObjectMethod (responseHeaderBuffer.get(),
|
||||
StringBuffer.toString));
|
||||
headerLines.addLines (juceString (env, headersString));
|
||||
}
|
||||
|
||||
for (int i = 0; i < headerLines.size(); ++i)
|
||||
{
|
||||
const String& header = headerLines[i];
|
||||
const String key (header.upToFirstOccurrenceOf (": ", false, false));
|
||||
const String value (header.fromFirstOccurrenceOf (": ", false, false));
|
||||
const String previousValue (responseHeaders[key]);
|
||||
|
||||
responseHeaders.set (key, previousValue.isEmpty() ? value : (previousValue + "," + value));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// WebInputStream methods
|
||||
void withExtraHeaders (const String& extraHeaders)
|
||||
{
|
||||
if (! headers.endsWithChar ('\n') && headers.isNotEmpty())
|
||||
headers << "\r\n";
|
||||
|
||||
headers << extraHeaders;
|
||||
|
||||
if (! headers.endsWithChar ('\n') && headers.isNotEmpty())
|
||||
headers << "\r\n";
|
||||
}
|
||||
|
||||
void withCustomRequestCommand (const String& customRequestCommand) { httpRequest = customRequestCommand; }
|
||||
void withConnectionTimeout (int timeoutInMs) { timeOutMs = timeoutInMs; }
|
||||
void withNumRedirectsToFollow (int maxRedirectsToFollow) { numRedirectsToFollow = maxRedirectsToFollow; }
|
||||
StringPairArray getRequestHeaders() const { return WebInputStream::parseHttpHeaders (headers); }
|
||||
StringPairArray getResponseHeaders() const { return responseHeaders; }
|
||||
int getStatusCode() const { return statusCode; }
|
||||
|
||||
//==============================================================================
|
||||
bool isError() const { return stream == nullptr; }
|
||||
bool isExhausted() { return (isContentURL ? eofStreamReached : stream != nullptr && stream.callBooleanMethod (HTTPStream.isExhausted)); }
|
||||
int64 getTotalLength() { return (isContentURL ? -1 : (stream != nullptr ? stream.callLongMethod (HTTPStream.getTotalLength) : 0)); }
|
||||
int64 getPosition() { return (isContentURL ? readPosition : (stream != nullptr ? stream.callLongMethod (HTTPStream.getPosition) : 0)); }
|
||||
|
||||
//==============================================================================
|
||||
bool setPosition (int64 wantedPos)
|
||||
{
|
||||
if (isContentURL)
|
||||
{
|
||||
if (wantedPos < readPosition)
|
||||
return false;
|
||||
|
||||
auto bytesToSkip = wantedPos - readPosition;
|
||||
|
||||
if (bytesToSkip == 0)
|
||||
return true;
|
||||
|
||||
HeapBlock<char> buffer (bytesToSkip);
|
||||
|
||||
return (read (buffer.getData(), (int) bytesToSkip) > 0);
|
||||
}
|
||||
|
||||
return stream != nullptr && stream.callBooleanMethod (HTTPStream.setPosition, (jlong) wantedPos);
|
||||
}
|
||||
|
||||
int read (void* buffer, int bytesToRead)
|
||||
{
|
||||
jassert (buffer != nullptr && bytesToRead >= 0);
|
||||
|
||||
const ScopedLock lock (createStreamLock);
|
||||
|
||||
if (stream == nullptr)
|
||||
return 0;
|
||||
|
||||
JNIEnv* env = getEnv();
|
||||
|
||||
jbyteArray javaArray = env->NewByteArray (bytesToRead);
|
||||
|
||||
auto numBytes = (isContentURL ? stream.callIntMethod (AndroidInputStream.read, javaArray, 0, (jint) bytesToRead)
|
||||
: stream.callIntMethod (HTTPStream.read, javaArray, (jint) bytesToRead));
|
||||
|
||||
if (numBytes > 0)
|
||||
env->GetByteArrayRegion (javaArray, 0, numBytes, static_cast<jbyte*> (buffer));
|
||||
|
||||
env->DeleteLocalRef (javaArray);
|
||||
|
||||
readPosition += jmax (0, numBytes);
|
||||
|
||||
if (numBytes == -1)
|
||||
eofStreamReached = true;
|
||||
|
||||
return numBytes;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
int statusCode = 0;
|
||||
|
||||
private:
|
||||
const URL url;
|
||||
const bool isContentURL, addParametersToRequestBody, hasBodyDataToSend;
|
||||
bool eofStreamReached = false;
|
||||
int numRedirectsToFollow = 5, timeOutMs = 0;
|
||||
String httpRequest, headers;
|
||||
StringPairArray responseHeaders;
|
||||
CriticalSection createStreamLock;
|
||||
bool hasBeenCancelled = false;
|
||||
int readPosition = 0;
|
||||
|
||||
GlobalRef stream;
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl)
|
||||
};
|
||||
|
||||
std::unique_ptr<URL::DownloadTask> URL::downloadToFile (const File& targetLocation, const DownloadTaskOptions& options)
|
||||
{
|
||||
return URL::DownloadTask::createFallbackDownloader (*this, targetLocation, options);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
#if __ANDROID_API__ < 24 // Android support for getifadds was added in Android 7.0 (API 24) so the posix implementation does not apply
|
||||
|
||||
static IPAddress makeAddress (const sockaddr_in *addr_in)
|
||||
{
|
||||
if (addr_in->sin_addr.s_addr == INADDR_NONE)
|
||||
return {};
|
||||
|
||||
return IPAddress (ntohl (addr_in->sin_addr.s_addr));
|
||||
}
|
||||
|
||||
struct InterfaceInfo
|
||||
{
|
||||
IPAddress interfaceAddress, broadcastAddress;
|
||||
String name;
|
||||
};
|
||||
|
||||
static Array<InterfaceInfo> findIPAddresses (int dummySocket)
|
||||
{
|
||||
ifconf cfg;
|
||||
HeapBlock<char> buffer;
|
||||
int bufferSize = 1024;
|
||||
|
||||
do
|
||||
{
|
||||
bufferSize *= 2;
|
||||
buffer.calloc (bufferSize);
|
||||
|
||||
cfg.ifc_len = bufferSize;
|
||||
cfg.ifc_buf = buffer;
|
||||
|
||||
if (ioctl (dummySocket, SIOCGIFCONF, &cfg) < 0 && errno != EINVAL)
|
||||
return {};
|
||||
|
||||
} while (bufferSize < cfg.ifc_len + 2 * (int) (IFNAMSIZ + sizeof (struct sockaddr_in6)));
|
||||
|
||||
Array<InterfaceInfo> result;
|
||||
|
||||
for (size_t i = 0; i < (size_t) cfg.ifc_len / (size_t) sizeof (struct ifreq); ++i)
|
||||
{
|
||||
auto& item = cfg.ifc_req[i];
|
||||
|
||||
if (item.ifr_addr.sa_family == AF_INET)
|
||||
{
|
||||
InterfaceInfo info;
|
||||
info.interfaceAddress = makeAddress (reinterpret_cast<const sockaddr_in*> (&item.ifr_addr));
|
||||
|
||||
if (! info.interfaceAddress.isNull())
|
||||
{
|
||||
if (ioctl (dummySocket, SIOCGIFBRDADDR, &item) == 0)
|
||||
info.broadcastAddress = makeAddress (reinterpret_cast<const sockaddr_in*> (&item.ifr_broadaddr));
|
||||
info.name = item.ifr_name;
|
||||
|
||||
result.add (info);
|
||||
}
|
||||
}
|
||||
else if (item.ifr_addr.sa_family == AF_INET6)
|
||||
{
|
||||
// TODO: IPv6
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static Array<InterfaceInfo> findIPAddresses()
|
||||
{
|
||||
auto dummySocket = socket (AF_INET, SOCK_DGRAM, 0); // a dummy socket to execute the IO control
|
||||
|
||||
if (dummySocket < 0)
|
||||
return {};
|
||||
|
||||
auto result = findIPAddresses (dummySocket);
|
||||
::close (dummySocket);
|
||||
return result;
|
||||
}
|
||||
|
||||
void IPAddress::findAllAddresses (Array<IPAddress>& result, bool /*includeIPv6*/)
|
||||
{
|
||||
for (auto& a : findIPAddresses())
|
||||
result.add (a.interfaceAddress);
|
||||
}
|
||||
|
||||
void IPAddress::findAllInterfaceAddresses (Array<IPAddressInterfaceNamePair>& result, bool /* includeIPv6 */)
|
||||
{
|
||||
for (auto& a : findIPAddresses())
|
||||
result.add (IPAddressInterfaceNamePair(a.interfaceAddress, a.name));
|
||||
}
|
||||
|
||||
IPAddress IPAddress::getInterfaceBroadcastAddress (const IPAddress& address)
|
||||
{
|
||||
for (auto& a : findIPAddresses())
|
||||
if (a.interfaceAddress == address)
|
||||
return a.broadcastAddress;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
261
deps/juce/modules/juce_core/native/juce_android_RuntimePermissions.cpp
vendored
Normal file
261
deps/juce/modules/juce_core/native/juce_android_RuntimePermissions.cpp
vendored
Normal file
@ -0,0 +1,261 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
static String jucePermissionToAndroidPermission (RuntimePermissions::PermissionID permission)
|
||||
{
|
||||
switch (permission)
|
||||
{
|
||||
case RuntimePermissions::recordAudio: return "android.permission.RECORD_AUDIO";
|
||||
case RuntimePermissions::bluetoothMidi: return "android.permission.ACCESS_FINE_LOCATION";
|
||||
case RuntimePermissions::readExternalStorage: return "android.permission.READ_EXTERNAL_STORAGE";
|
||||
case RuntimePermissions::writeExternalStorage: return "android.permission.WRITE_EXTERNAL_STORAGE";
|
||||
case RuntimePermissions::camera: return "android.permission.CAMERA";
|
||||
}
|
||||
|
||||
// invalid permission
|
||||
jassertfalse;
|
||||
return {};
|
||||
}
|
||||
|
||||
static RuntimePermissions::PermissionID androidPermissionToJucePermission (const String& permission)
|
||||
{
|
||||
if (permission == "android.permission.RECORD_AUDIO") return RuntimePermissions::recordAudio;
|
||||
else if (permission == "android.permission.ACCESS_FINE_LOCATION") return RuntimePermissions::bluetoothMidi;
|
||||
else if (permission == "android.permission.READ_EXTERNAL_STORAGE") return RuntimePermissions::readExternalStorage;
|
||||
else if (permission == "android.permission.WRITE_EXTERNAL_STORAGE") return RuntimePermissions::writeExternalStorage;
|
||||
else if (permission == "android.permission.CAMERA") return RuntimePermissions::camera;
|
||||
|
||||
return static_cast<RuntimePermissions::PermissionID> (-1);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
struct PermissionsRequest
|
||||
{
|
||||
PermissionsRequest() {}
|
||||
|
||||
// using "= default" on the following method triggers an internal compiler error
|
||||
// in Android NDK 17
|
||||
PermissionsRequest (const PermissionsRequest& o)
|
||||
: callback (o.callback), permission (o.permission)
|
||||
{}
|
||||
|
||||
PermissionsRequest (PermissionsRequest&& o)
|
||||
: callback (std::move (o.callback)), permission (o.permission)
|
||||
{
|
||||
o.permission = static_cast<RuntimePermissions::PermissionID> (-1);
|
||||
}
|
||||
|
||||
PermissionsRequest (RuntimePermissions::Callback && callbackToUse,
|
||||
RuntimePermissions::PermissionID permissionToRequest)
|
||||
: callback (std::move (callbackToUse)), permission (permissionToRequest)
|
||||
{}
|
||||
|
||||
PermissionsRequest& operator= (const PermissionsRequest & o)
|
||||
{
|
||||
callback = o.callback;
|
||||
permission = o.permission;
|
||||
return *this;
|
||||
}
|
||||
|
||||
PermissionsRequest& operator= (PermissionsRequest && o)
|
||||
{
|
||||
callback = std::move (o.callback);
|
||||
permission = o.permission;
|
||||
return *this;
|
||||
}
|
||||
|
||||
RuntimePermissions::Callback callback;
|
||||
RuntimePermissions::PermissionID permission;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
struct PermissionsOverlay : FragmentOverlay
|
||||
{
|
||||
PermissionsOverlay (CriticalSection& cs) : overlayGuard (cs) {}
|
||||
~PermissionsOverlay() override = default;
|
||||
|
||||
struct PermissionResult
|
||||
{
|
||||
PermissionsRequest request;
|
||||
bool granted;
|
||||
};
|
||||
|
||||
void onStart() override { onRequestPermissionsResult (0, {}, {}); }
|
||||
|
||||
void onRequestPermissionsResult (int /*requestCode*/,
|
||||
const StringArray& permissions,
|
||||
const Array<int>& grantResults) override
|
||||
{
|
||||
std::vector<PermissionResult> results;
|
||||
|
||||
{
|
||||
ScopedLock lock (overlayGuard);
|
||||
|
||||
for (auto it = requests.begin(); it != requests.end();)
|
||||
{
|
||||
auto& request = *it;
|
||||
|
||||
if (RuntimePermissions::isGranted (request.permission))
|
||||
{
|
||||
results.push_back ({std::move (request), true});
|
||||
it = requests.erase (it);
|
||||
}
|
||||
else
|
||||
{
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
auto n = permissions.size();
|
||||
|
||||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
auto permission = androidPermissionToJucePermission (permissions[i]);
|
||||
auto granted = (grantResults.getReference (i) == 0);
|
||||
|
||||
for (auto it = requests.begin(); it != requests.end();)
|
||||
{
|
||||
auto& request = *it;
|
||||
|
||||
if (request.permission == permission)
|
||||
{
|
||||
results.push_back ({std::move (request), granted});
|
||||
it = requests.erase (it);
|
||||
}
|
||||
else
|
||||
{
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& result : results)
|
||||
if (result.request.callback)
|
||||
result.request.callback (result.granted);
|
||||
|
||||
{
|
||||
auto* env = getEnv();
|
||||
ScopedLock lock (overlayGuard);
|
||||
|
||||
if (requests.size() > 0)
|
||||
{
|
||||
auto &request = requests.front();
|
||||
|
||||
StringArray permissionsArray{
|
||||
jucePermissionToAndroidPermission (request.permission)};
|
||||
auto jPermissionsArray = juceStringArrayToJava (permissionsArray);
|
||||
|
||||
|
||||
auto requestPermissionsMethodID
|
||||
= env->GetMethodID(AndroidFragment, "requestPermissions", "([Ljava/lang/String;I)V");
|
||||
|
||||
// this code should only be reached for SDKs >= 23, so this method should be
|
||||
// be available
|
||||
jassert(requestPermissionsMethodID != nullptr);
|
||||
|
||||
env->CallVoidMethod (getNativeHandle(), requestPermissionsMethodID, jPermissionsArray.get (), 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
getSingleton() = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static std::unique_ptr<PermissionsOverlay>& getSingleton()
|
||||
{
|
||||
static std::unique_ptr<PermissionsOverlay> instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
CriticalSection& overlayGuard;
|
||||
std::vector<PermissionsRequest> requests;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
void RuntimePermissions::request (PermissionID permission, Callback callback)
|
||||
{
|
||||
auto requestedPermission = jucePermissionToAndroidPermission (permission);
|
||||
|
||||
if (! isPermissionDeclaredInManifest (requestedPermission))
|
||||
{
|
||||
// Error! If you want to be able to request this runtime permission, you
|
||||
// also need to declare it in your app's manifest. You can do so via
|
||||
// the Projucer. Otherwise this can't work.
|
||||
jassertfalse;
|
||||
|
||||
callback (false);
|
||||
return;
|
||||
}
|
||||
|
||||
auto alreadyGranted = isGranted (permission);
|
||||
|
||||
if (alreadyGranted || getAndroidSDKVersion() < 23)
|
||||
{
|
||||
callback (alreadyGranted);
|
||||
return;
|
||||
}
|
||||
|
||||
PermissionsRequest request (std::move (callback), permission);
|
||||
|
||||
static CriticalSection overlayGuard;
|
||||
ScopedLock lock (overlayGuard);
|
||||
|
||||
std::unique_ptr<PermissionsOverlay>& overlay = PermissionsOverlay::getSingleton();
|
||||
|
||||
bool alreadyOpen = true;
|
||||
|
||||
if (overlay == nullptr)
|
||||
{
|
||||
overlay.reset (new PermissionsOverlay (overlayGuard));
|
||||
alreadyOpen = false;
|
||||
}
|
||||
|
||||
overlay->requests.push_back (std::move (request));
|
||||
|
||||
if (! alreadyOpen)
|
||||
overlay->open();
|
||||
}
|
||||
|
||||
bool RuntimePermissions::isRequired (PermissionID /*permission*/)
|
||||
{
|
||||
return getAndroidSDKVersion() >= 23;
|
||||
}
|
||||
|
||||
bool RuntimePermissions::isGranted (PermissionID permission)
|
||||
{
|
||||
auto* env = getEnv();
|
||||
|
||||
auto requestedPermission = jucePermissionToAndroidPermission (permission);
|
||||
int result = env->CallIntMethod (getAppContext().get(), AndroidContext.checkCallingOrSelfPermission,
|
||||
javaString (requestedPermission).get());
|
||||
|
||||
|
||||
return result == 0 /* PERMISSION_GRANTED */;
|
||||
}
|
||||
|
||||
} // namespace juce
|
241
deps/juce/modules/juce_core/native/juce_android_SystemStats.cpp
vendored
Normal file
241
deps/juce/modules/juce_core/native/juce_android_SystemStats.cpp
vendored
Normal file
@ -0,0 +1,241 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
namespace AndroidStatsHelpers
|
||||
{
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
STATICMETHOD (getProperty, "getProperty", "(Ljava/lang/String;)Ljava/lang/String;")
|
||||
|
||||
DECLARE_JNI_CLASS (SystemClass, "java/lang/System")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
STATICMETHOD (getDefault, "getDefault", "()Ljava/util/Locale;") \
|
||||
METHOD (getCountry, "getCountry", "()Ljava/lang/String;") \
|
||||
METHOD (getLanguage, "getLanguage", "()Ljava/lang/String;")
|
||||
|
||||
DECLARE_JNI_CLASS (JavaLocale, "java/util/Locale")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
static String getSystemProperty (const String& name)
|
||||
{
|
||||
return juceString (LocalRef<jstring> ((jstring) getEnv()->CallStaticObjectMethod (SystemClass,
|
||||
SystemClass.getProperty,
|
||||
javaString (name).get())));
|
||||
}
|
||||
|
||||
static String getLocaleValue (bool isRegion)
|
||||
{
|
||||
auto* env = getEnv();
|
||||
LocalRef<jobject> locale (env->CallStaticObjectMethod (JavaLocale, JavaLocale.getDefault));
|
||||
|
||||
auto stringResult = isRegion ? env->CallObjectMethod (locale.get(), JavaLocale.getCountry)
|
||||
: env->CallObjectMethod (locale.get(), JavaLocale.getLanguage);
|
||||
|
||||
return juceString (LocalRef<jstring> ((jstring) stringResult));
|
||||
}
|
||||
|
||||
static String getAndroidOsBuildValue (const char* fieldName)
|
||||
{
|
||||
return juceString (LocalRef<jstring> ((jstring) getEnv()->GetStaticObjectField (
|
||||
AndroidBuild, getEnv()->GetStaticFieldID (AndroidBuild, fieldName, "Ljava/lang/String;"))));
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
SystemStats::OperatingSystemType SystemStats::getOperatingSystemType()
|
||||
{
|
||||
return Android;
|
||||
}
|
||||
|
||||
String SystemStats::getOperatingSystemName()
|
||||
{
|
||||
return "Android " + AndroidStatsHelpers::getSystemProperty ("os.version");
|
||||
}
|
||||
|
||||
String SystemStats::getDeviceDescription()
|
||||
{
|
||||
return AndroidStatsHelpers::getAndroidOsBuildValue ("MODEL")
|
||||
+ "-" + AndroidStatsHelpers::getAndroidOsBuildValue ("SERIAL");
|
||||
}
|
||||
|
||||
String SystemStats::getDeviceManufacturer()
|
||||
{
|
||||
return AndroidStatsHelpers::getAndroidOsBuildValue ("MANUFACTURER");
|
||||
}
|
||||
|
||||
bool SystemStats::isOperatingSystem64Bit()
|
||||
{
|
||||
#if JUCE_64BIT
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
String SystemStats::getCpuVendor()
|
||||
{
|
||||
return AndroidStatsHelpers::getSystemProperty ("os.arch");
|
||||
}
|
||||
|
||||
String SystemStats::getCpuModel()
|
||||
{
|
||||
return readPosixConfigFileValue ("/proc/cpuinfo", "Hardware");
|
||||
}
|
||||
|
||||
int SystemStats::getCpuSpeedInMegahertz()
|
||||
{
|
||||
int maxFreqKHz = 0;
|
||||
|
||||
for (int i = 0; i < getNumCpus(); ++i)
|
||||
{
|
||||
int freqKHz = File ("/sys/devices/system/cpu/cpu" + String(i) + "/cpufreq/cpuinfo_max_freq")
|
||||
.loadFileAsString()
|
||||
.getIntValue();
|
||||
|
||||
maxFreqKHz = jmax (freqKHz, maxFreqKHz);
|
||||
}
|
||||
|
||||
return maxFreqKHz / 1000;
|
||||
}
|
||||
|
||||
int SystemStats::getMemorySizeInMegabytes()
|
||||
{
|
||||
#if __ANDROID_API__ >= 9
|
||||
struct sysinfo sysi;
|
||||
|
||||
if (sysinfo (&sysi) == 0)
|
||||
return static_cast<int> ((sysi.totalram * sysi.mem_unit) / (1024 * 1024));
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SystemStats::getPageSize()
|
||||
{
|
||||
return static_cast<int> (sysconf (_SC_PAGESIZE));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
String SystemStats::getLogonName()
|
||||
{
|
||||
if (const char* user = getenv ("USER"))
|
||||
return CharPointer_UTF8 (user);
|
||||
|
||||
if (struct passwd* const pw = getpwuid (getuid()))
|
||||
return CharPointer_UTF8 (pw->pw_name);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
String SystemStats::getFullUserName()
|
||||
{
|
||||
return getLogonName();
|
||||
}
|
||||
|
||||
String SystemStats::getComputerName()
|
||||
{
|
||||
char name [256] = { 0 };
|
||||
if (gethostname (name, sizeof (name) - 1) == 0)
|
||||
return name;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
String SystemStats::getUserLanguage() { return AndroidStatsHelpers::getLocaleValue (false); }
|
||||
String SystemStats::getUserRegion() { return AndroidStatsHelpers::getLocaleValue (true); }
|
||||
String SystemStats::getDisplayLanguage() { return getUserLanguage() + "-" + getUserRegion(); }
|
||||
|
||||
//==============================================================================
|
||||
void CPUInformation::initialise() noexcept
|
||||
{
|
||||
numPhysicalCPUs = numLogicalCPUs = jmax ((int) 1, (int) android_getCpuCount());
|
||||
|
||||
auto cpuFamily = android_getCpuFamily();
|
||||
auto cpuFeatures = android_getCpuFeatures();
|
||||
|
||||
if (cpuFamily == ANDROID_CPU_FAMILY_X86 || cpuFamily == ANDROID_CPU_FAMILY_X86_64)
|
||||
{
|
||||
hasMMX = hasSSE = hasSSE2 = (cpuFamily == ANDROID_CPU_FAMILY_X86_64);
|
||||
|
||||
hasSSSE3 = ((cpuFeatures & ANDROID_CPU_X86_FEATURE_SSSE3) != 0);
|
||||
hasSSE41 = ((cpuFeatures & ANDROID_CPU_X86_FEATURE_SSE4_1) != 0);
|
||||
hasSSE42 = ((cpuFeatures & ANDROID_CPU_X86_FEATURE_SSE4_2) != 0);
|
||||
hasAVX = ((cpuFeatures & ANDROID_CPU_X86_FEATURE_AVX) != 0);
|
||||
hasAVX2 = ((cpuFeatures & ANDROID_CPU_X86_FEATURE_AVX2) != 0);
|
||||
|
||||
// Google does not distinguish between MMX, SSE, SSE2, SSE3 and SSSE3. So
|
||||
// I assume (and quick Google searches seem to confirm this) that there are
|
||||
// only devices out there that either support all of this or none of this.
|
||||
if (hasSSSE3)
|
||||
hasMMX = hasSSE = hasSSE2 = hasSSE3 = true;
|
||||
}
|
||||
else if (cpuFamily == ANDROID_CPU_FAMILY_ARM)
|
||||
{
|
||||
hasNeon = ((cpuFeatures & ANDROID_CPU_ARM_FEATURE_NEON) != 0);
|
||||
}
|
||||
else if (cpuFamily == ANDROID_CPU_FAMILY_ARM64)
|
||||
{
|
||||
// all arm 64-bit cpus have neon
|
||||
hasNeon = true;
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
uint32 juce_millisecondsSinceStartup() noexcept
|
||||
{
|
||||
timespec t;
|
||||
clock_gettime (CLOCK_MONOTONIC, &t);
|
||||
|
||||
return static_cast<uint32> (t.tv_sec) * 1000U + static_cast<uint32> (t.tv_nsec) / 1000000U;
|
||||
}
|
||||
|
||||
int64 Time::getHighResolutionTicks() noexcept
|
||||
{
|
||||
timespec t;
|
||||
clock_gettime (CLOCK_MONOTONIC, &t);
|
||||
|
||||
return (t.tv_sec * (int64) 1000000) + (t.tv_nsec / 1000);
|
||||
}
|
||||
|
||||
int64 Time::getHighResolutionTicksPerSecond() noexcept
|
||||
{
|
||||
return 1000000; // (microseconds)
|
||||
}
|
||||
|
||||
double Time::getMillisecondCounterHiRes() noexcept
|
||||
{
|
||||
return (double) getHighResolutionTicks() * 0.001;
|
||||
}
|
||||
|
||||
bool Time::setSystemTimeToThisTime() const
|
||||
{
|
||||
jassertfalse;
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace juce
|
395
deps/juce/modules/juce_core/native/juce_android_Threads.cpp
vendored
Normal file
395
deps/juce/modules/juce_core/native/juce_android_Threads.cpp
vendored
Normal file
@ -0,0 +1,395 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
/*
|
||||
Note that a lot of methods that you'd expect to find in this file actually
|
||||
live in juce_posix_SharedCode.h!
|
||||
*/
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
FIELD (activityInfo, "activityInfo", "Landroid/content/pm/ActivityInfo;")
|
||||
|
||||
DECLARE_JNI_CLASS (AndroidResolveInfo, "android/content/pm/ResolveInfo")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
//==============================================================================
|
||||
JavaVM* androidJNIJavaVM = nullptr;
|
||||
jobject androidApkContext = nullptr;
|
||||
|
||||
//==============================================================================
|
||||
JNIEnv* getEnv() noexcept
|
||||
{
|
||||
if (androidJNIJavaVM != nullptr)
|
||||
{
|
||||
JNIEnv* env;
|
||||
androidJNIJavaVM->AttachCurrentThread (&env, nullptr);
|
||||
|
||||
return env;
|
||||
}
|
||||
|
||||
// You did not call Thread::initialiseJUCE which must be called at least once in your apk
|
||||
// before using any JUCE APIs. The Projucer will automatically generate java code
|
||||
// which will invoke Thread::initialiseJUCE for you.
|
||||
jassertfalse;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void JNICALL juce_JavainitialiseJUCE (JNIEnv* env, jobject /*jclass*/, jobject context)
|
||||
{
|
||||
Thread::initialiseJUCE (env, context);
|
||||
}
|
||||
|
||||
extern "C" jint JNIEXPORT JNI_OnLoad (JavaVM* vm, void*)
|
||||
{
|
||||
// Huh? JNI_OnLoad was called two times!
|
||||
jassert (androidJNIJavaVM == nullptr);
|
||||
|
||||
androidJNIJavaVM = vm;
|
||||
|
||||
auto* env = getEnv();
|
||||
|
||||
// register the initialisation function
|
||||
auto juceJavaClass = env->FindClass("com/rmsl/juce/Java");
|
||||
|
||||
if (juceJavaClass != nullptr)
|
||||
{
|
||||
JNINativeMethod method {"initialiseJUCE", "(Landroid/content/Context;)V",
|
||||
reinterpret_cast<void*> (juce_JavainitialiseJUCE)};
|
||||
|
||||
auto status = env->RegisterNatives (juceJavaClass, &method, 1);
|
||||
jassert (status == 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// com.rmsl.juce.Java class not found. Apparently this project is a library
|
||||
// or was not generated by the Projucer. That's ok, the user will have to
|
||||
// call Thread::initialiseJUCE manually
|
||||
env->ExceptionClear();
|
||||
}
|
||||
|
||||
JNIClassBase::initialiseAllClasses (env);
|
||||
|
||||
return JNI_VERSION_1_2;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class JuceActivityWatcher : public ActivityLifecycleCallbacks
|
||||
{
|
||||
public:
|
||||
JuceActivityWatcher()
|
||||
{
|
||||
LocalRef<jobject> appContext (getAppContext());
|
||||
|
||||
if (appContext != nullptr)
|
||||
{
|
||||
auto* env = getEnv();
|
||||
|
||||
myself = GlobalRef (CreateJavaInterface (this, "android/app/Application$ActivityLifecycleCallbacks"));
|
||||
env->CallVoidMethod (appContext.get(), AndroidApplication.registerActivityLifecycleCallbacks, myself.get());
|
||||
}
|
||||
|
||||
checkActivityIsMain (androidApkContext);
|
||||
}
|
||||
|
||||
~JuceActivityWatcher() override
|
||||
{
|
||||
LocalRef<jobject> appContext (getAppContext());
|
||||
|
||||
if (appContext != nullptr && myself != nullptr)
|
||||
{
|
||||
auto* env = getEnv();
|
||||
|
||||
env->CallVoidMethod (appContext.get(), AndroidApplication.unregisterActivityLifecycleCallbacks, myself.get());
|
||||
clear();
|
||||
myself.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void onActivityStarted (jobject activity) override
|
||||
{
|
||||
auto* env = getEnv();
|
||||
|
||||
checkActivityIsMain (activity);
|
||||
|
||||
ScopedLock lock (currentActivityLock);
|
||||
|
||||
if (currentActivity != nullptr)
|
||||
{
|
||||
// see Clarification June 2001 in JNI reference for why this is
|
||||
// necessary
|
||||
LocalRef<jobject> localStorage (env->NewLocalRef (currentActivity));
|
||||
|
||||
if (env->IsSameObject (localStorage.get(), activity) != 0)
|
||||
return;
|
||||
|
||||
env->DeleteWeakGlobalRef (currentActivity);
|
||||
currentActivity = nullptr;
|
||||
}
|
||||
|
||||
if (activity != nullptr)
|
||||
currentActivity = env->NewWeakGlobalRef (activity);
|
||||
}
|
||||
|
||||
void onActivityStopped (jobject activity) override
|
||||
{
|
||||
auto* env = getEnv();
|
||||
|
||||
ScopedLock lock (currentActivityLock);
|
||||
|
||||
if (currentActivity != nullptr)
|
||||
{
|
||||
// important that the comparison happens in this order
|
||||
// to avoid race condition where the weak reference becomes null
|
||||
// just after the first check
|
||||
if (env->IsSameObject (currentActivity, activity) != 0
|
||||
|| env->IsSameObject (currentActivity, nullptr) != 0)
|
||||
{
|
||||
env->DeleteWeakGlobalRef (currentActivity);
|
||||
currentActivity = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LocalRef<jobject> getCurrent()
|
||||
{
|
||||
ScopedLock lock (currentActivityLock);
|
||||
return LocalRef<jobject> (getEnv()->NewLocalRef (currentActivity));
|
||||
}
|
||||
|
||||
LocalRef<jobject> getMain()
|
||||
{
|
||||
ScopedLock lock (currentActivityLock);
|
||||
return LocalRef<jobject> (getEnv()->NewLocalRef (mainActivity));
|
||||
}
|
||||
|
||||
static JuceActivityWatcher& getInstance()
|
||||
{
|
||||
static JuceActivityWatcher activityWatcher;
|
||||
return activityWatcher;
|
||||
}
|
||||
|
||||
private:
|
||||
void checkActivityIsMain (jobject context)
|
||||
{
|
||||
auto* env = getEnv();
|
||||
|
||||
ScopedLock lock (currentActivityLock);
|
||||
|
||||
if (mainActivity != nullptr)
|
||||
{
|
||||
if (env->IsSameObject (mainActivity, nullptr) != 0)
|
||||
{
|
||||
env->DeleteWeakGlobalRef (mainActivity);
|
||||
mainActivity = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (mainActivity == nullptr)
|
||||
{
|
||||
LocalRef<jobject> appContext (getAppContext());
|
||||
auto mainActivityPath = getMainActivityClassPath();
|
||||
|
||||
if (mainActivityPath.isNotEmpty())
|
||||
{
|
||||
auto clasz = env->GetObjectClass (context);
|
||||
auto activityPath = juceString (LocalRef<jstring> ((jstring) env->CallObjectMethod (clasz, JavaClass.getName)));
|
||||
|
||||
// This may be problematic for apps which use several activities with the same type. We just
|
||||
// assume that the very first activity of this type is the main one
|
||||
if (activityPath == mainActivityPath)
|
||||
mainActivity = env->NewWeakGlobalRef (context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static String getMainActivityClassPath()
|
||||
{
|
||||
static String mainActivityClassPath;
|
||||
|
||||
if (mainActivityClassPath.isEmpty())
|
||||
{
|
||||
LocalRef<jobject> appContext (getAppContext());
|
||||
|
||||
if (appContext != nullptr)
|
||||
{
|
||||
auto* env = getEnv();
|
||||
|
||||
LocalRef<jobject> pkgManager (env->CallObjectMethod (appContext.get(), AndroidContext.getPackageManager));
|
||||
LocalRef<jstring> pkgName ((jstring) env->CallObjectMethod (appContext.get(), AndroidContext.getPackageName));
|
||||
|
||||
LocalRef<jobject> intent (env->NewObject (AndroidIntent, AndroidIntent.constructWithString,
|
||||
javaString ("android.intent.action.MAIN").get()));
|
||||
|
||||
intent = LocalRef<jobject> (env->CallObjectMethod (intent.get(),
|
||||
AndroidIntent.setPackage,
|
||||
pkgName.get()));
|
||||
|
||||
LocalRef<jobject> resolveInfo (env->CallObjectMethod (pkgManager.get(), AndroidPackageManager.resolveActivity, intent.get(), 0));
|
||||
|
||||
if (resolveInfo != nullptr)
|
||||
{
|
||||
LocalRef<jobject> activityInfo (env->GetObjectField (resolveInfo.get(), AndroidResolveInfo.activityInfo));
|
||||
LocalRef<jstring> jName ((jstring) env->GetObjectField (activityInfo.get(), AndroidPackageItemInfo.name));
|
||||
LocalRef<jstring> jPackage ((jstring) env->GetObjectField (activityInfo.get(), AndroidPackageItemInfo.packageName));
|
||||
|
||||
mainActivityClassPath = juceString (jName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mainActivityClassPath;
|
||||
}
|
||||
|
||||
GlobalRef myself;
|
||||
CriticalSection currentActivityLock;
|
||||
jweak currentActivity = nullptr;
|
||||
jweak mainActivity = nullptr;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_MODULE_AVAILABLE_juce_events && JUCE_ANDROID
|
||||
void juce_juceEventsAndroidStartApp();
|
||||
#endif
|
||||
|
||||
void Thread::initialiseJUCE (void* jniEnv, void* context)
|
||||
{
|
||||
static CriticalSection cs;
|
||||
ScopedLock lock (cs);
|
||||
|
||||
// jniEnv and context should not be null!
|
||||
jassert (jniEnv != nullptr && context != nullptr);
|
||||
|
||||
auto* env = static_cast<JNIEnv*> (jniEnv);
|
||||
|
||||
if (androidJNIJavaVM == nullptr)
|
||||
{
|
||||
JavaVM* javaVM = nullptr;
|
||||
|
||||
auto status = env->GetJavaVM (&javaVM);
|
||||
jassert (status == 0 && javaVM != nullptr);
|
||||
|
||||
androidJNIJavaVM = javaVM;
|
||||
}
|
||||
|
||||
static bool firstCall = true;
|
||||
|
||||
if (firstCall)
|
||||
{
|
||||
firstCall = false;
|
||||
|
||||
// if we ever support unloading then this should probably be a weak reference
|
||||
androidApkContext = env->NewGlobalRef (static_cast<jobject> (context));
|
||||
JuceActivityWatcher::getInstance();
|
||||
|
||||
#if JUCE_MODULE_AVAILABLE_juce_events && JUCE_ANDROID
|
||||
juce_juceEventsAndroidStartApp();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
LocalRef<jobject> getAppContext() noexcept
|
||||
{
|
||||
auto* env = getEnv();
|
||||
auto context = androidApkContext;
|
||||
|
||||
// You did not call Thread::initialiseJUCE which must be called at least once in your apk
|
||||
// before using any JUCE APIs. The Projucer will automatically generate java code
|
||||
// which will invoke Thread::initialiseJUCE for you.
|
||||
jassert (env != nullptr && context != nullptr);
|
||||
|
||||
if (context == nullptr)
|
||||
return LocalRef<jobject>();
|
||||
|
||||
if (env->IsInstanceOf (context, AndroidApplication) != 0)
|
||||
return LocalRef<jobject> (env->NewLocalRef (context));
|
||||
|
||||
LocalRef<jobject> applicationContext (env->CallObjectMethod (context, AndroidContext.getApplicationContext));
|
||||
|
||||
if (applicationContext == nullptr)
|
||||
return LocalRef<jobject> (env->NewLocalRef (context));
|
||||
|
||||
return applicationContext;
|
||||
}
|
||||
|
||||
LocalRef<jobject> getCurrentActivity() noexcept
|
||||
{
|
||||
return JuceActivityWatcher::getInstance().getCurrent();
|
||||
}
|
||||
|
||||
LocalRef<jobject> getMainActivity() noexcept
|
||||
{
|
||||
return JuceActivityWatcher::getInstance().getMain();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// sets the process to 0=low priority, 1=normal, 2=high, 3=realtime
|
||||
JUCE_API void JUCE_CALLTYPE Process::setPriority (ProcessPriority prior)
|
||||
{
|
||||
// TODO
|
||||
|
||||
struct sched_param param;
|
||||
int policy, maxp, minp;
|
||||
|
||||
const int p = (int) prior;
|
||||
|
||||
if (p <= 1)
|
||||
policy = SCHED_OTHER;
|
||||
else
|
||||
policy = SCHED_RR;
|
||||
|
||||
minp = sched_get_priority_min (policy);
|
||||
maxp = sched_get_priority_max (policy);
|
||||
|
||||
if (p < 2)
|
||||
param.sched_priority = 0;
|
||||
else if (p == 2 )
|
||||
// Set to middle of lower realtime priority range
|
||||
param.sched_priority = minp + (maxp - minp) / 4;
|
||||
else
|
||||
// Set to middle of higher realtime priority range
|
||||
param.sched_priority = minp + (3 * (maxp - minp) / 4);
|
||||
|
||||
pthread_setschedparam (pthread_self(), policy, ¶m);
|
||||
}
|
||||
|
||||
JUCE_API bool JUCE_CALLTYPE juce_isRunningUnderDebugger() noexcept
|
||||
{
|
||||
StringArray lines;
|
||||
File ("/proc/self/status").readLines (lines);
|
||||
|
||||
for (int i = lines.size(); --i >= 0;) // (NB - it's important that this runs in reverse order)
|
||||
if (lines[i].upToFirstOccurrenceOf (":", false, false).trim().equalsIgnoreCase ("TracerPid"))
|
||||
return (lines[i].fromFirstOccurrenceOf (":", false, false).trim().getIntValue() > 0);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
JUCE_API void JUCE_CALLTYPE Process::raisePrivilege() {}
|
||||
JUCE_API void JUCE_CALLTYPE Process::lowerPrivilege() {}
|
||||
|
||||
|
||||
|
||||
} // namespace juce
|
656
deps/juce/modules/juce_core/native/juce_curl_Network.cpp
vendored
Normal file
656
deps/juce/modules/juce_core/native/juce_curl_Network.cpp
vendored
Normal file
@ -0,0 +1,656 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
struct CURLSymbols
|
||||
{
|
||||
CURL* (*curl_easy_init) (void);
|
||||
CURLcode (*curl_easy_setopt) (CURL *curl, CURLoption option, ...);
|
||||
void (*curl_easy_cleanup) (CURL *curl);
|
||||
CURLcode (*curl_easy_getinfo) (CURL *curl, CURLINFO info, ...);
|
||||
CURLMcode (*curl_multi_add_handle) (CURLM *multi_handle, CURL *curl_handle);
|
||||
CURLMcode (*curl_multi_cleanup) (CURLM *multi_handle);
|
||||
CURLMcode (*curl_multi_fdset) (CURLM *multi_handle, fd_set *read_fd_set, fd_set *write_fd_set, fd_set *exc_fd_set, int *max_fd);
|
||||
CURLMsg* (*curl_multi_info_read) (CURLM *multi_handle, int *msgs_in_queue);
|
||||
CURLM* (*curl_multi_init) (void);
|
||||
CURLMcode (*curl_multi_perform) (CURLM *multi_handle, int *running_handles);
|
||||
CURLMcode (*curl_multi_remove_handle) (CURLM *multi_handle, CURL *curl_handle);
|
||||
CURLMcode (*curl_multi_timeout) (CURLM *multi_handle, long *milliseconds);
|
||||
struct curl_slist* (*curl_slist_append) (struct curl_slist *, const char *);
|
||||
void (*curl_slist_free_all) (struct curl_slist *);
|
||||
curl_version_info_data* (*curl_version_info) (CURLversion);
|
||||
|
||||
static std::unique_ptr<CURLSymbols> create()
|
||||
{
|
||||
std::unique_ptr<CURLSymbols> symbols (new CURLSymbols);
|
||||
|
||||
#if JUCE_LOAD_CURL_SYMBOLS_LAZILY
|
||||
const ScopedLock sl (getLibcurlLock());
|
||||
#define JUCE_INIT_CURL_SYMBOL(name) if (! symbols->loadSymbol (symbols->name, #name)) return nullptr;
|
||||
#else
|
||||
#define JUCE_INIT_CURL_SYMBOL(name) symbols->name = ::name;
|
||||
#endif
|
||||
|
||||
JUCE_INIT_CURL_SYMBOL (curl_easy_init)
|
||||
JUCE_INIT_CURL_SYMBOL (curl_easy_setopt)
|
||||
JUCE_INIT_CURL_SYMBOL (curl_easy_cleanup)
|
||||
JUCE_INIT_CURL_SYMBOL (curl_easy_getinfo)
|
||||
JUCE_INIT_CURL_SYMBOL (curl_multi_add_handle)
|
||||
JUCE_INIT_CURL_SYMBOL (curl_multi_cleanup)
|
||||
JUCE_INIT_CURL_SYMBOL (curl_multi_fdset)
|
||||
JUCE_INIT_CURL_SYMBOL (curl_multi_info_read)
|
||||
JUCE_INIT_CURL_SYMBOL (curl_multi_init)
|
||||
JUCE_INIT_CURL_SYMBOL (curl_multi_perform)
|
||||
JUCE_INIT_CURL_SYMBOL (curl_multi_remove_handle)
|
||||
JUCE_INIT_CURL_SYMBOL (curl_multi_timeout)
|
||||
JUCE_INIT_CURL_SYMBOL (curl_slist_append)
|
||||
JUCE_INIT_CURL_SYMBOL (curl_slist_free_all)
|
||||
JUCE_INIT_CURL_SYMBOL (curl_version_info)
|
||||
|
||||
return symbols;
|
||||
}
|
||||
|
||||
// liburl's curl_multi_init calls curl_global_init which is not thread safe
|
||||
// so we need to get a lock during calls to curl_multi_init and curl_multi_cleanup
|
||||
static CriticalSection& getLibcurlLock() noexcept
|
||||
{
|
||||
static CriticalSection cs;
|
||||
return cs;
|
||||
}
|
||||
|
||||
private:
|
||||
CURLSymbols() = default;
|
||||
|
||||
#if JUCE_LOAD_CURL_SYMBOLS_LAZILY
|
||||
static DynamicLibrary& getLibcurl()
|
||||
{
|
||||
const ScopedLock sl (getLibcurlLock());
|
||||
static DynamicLibrary libcurl;
|
||||
|
||||
if (libcurl.getNativeHandle() == nullptr)
|
||||
for (auto libName : { "libcurl.so", "libcurl.so.4", "libcurl.so.3" })
|
||||
if (libcurl.open (libName))
|
||||
break;
|
||||
|
||||
return libcurl;
|
||||
}
|
||||
|
||||
template <typename FuncPtr>
|
||||
bool loadSymbol (FuncPtr& dst, const char* name)
|
||||
{
|
||||
dst = reinterpret_cast<FuncPtr> (getLibcurl().getFunction (name));
|
||||
return (dst != nullptr);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class WebInputStream::Pimpl
|
||||
{
|
||||
public:
|
||||
Pimpl (WebInputStream& ownerStream, const URL& urlToCopy, bool addParametersToBody)
|
||||
: owner (ownerStream),
|
||||
url (urlToCopy),
|
||||
addParametersToRequestBody (addParametersToBody),
|
||||
hasBodyDataToSend (url.hasBodyDataToSend() || addParametersToRequestBody),
|
||||
httpRequest (hasBodyDataToSend ? "POST" : "GET")
|
||||
{
|
||||
jassert (symbols); // Unable to load libcurl!
|
||||
|
||||
{
|
||||
const ScopedLock sl (CURLSymbols::getLibcurlLock());
|
||||
multi = symbols->curl_multi_init();
|
||||
}
|
||||
|
||||
if (multi != nullptr)
|
||||
{
|
||||
curl = symbols->curl_easy_init();
|
||||
|
||||
if (curl != nullptr)
|
||||
if (symbols->curl_multi_add_handle (multi, curl) == CURLM_OK)
|
||||
return;
|
||||
}
|
||||
|
||||
cleanup();
|
||||
}
|
||||
|
||||
~Pimpl()
|
||||
{
|
||||
cleanup();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// Input Stream overrides
|
||||
bool isError() const { return curl == nullptr || lastError != CURLE_OK; }
|
||||
bool isExhausted() { return (isError() || finished) && curlBuffer.isEmpty(); }
|
||||
int64 getPosition() { return streamPos; }
|
||||
int64 getTotalLength() { return contentLength; }
|
||||
|
||||
int read (void* buffer, int bytesToRead)
|
||||
{
|
||||
return readOrSkip (buffer, bytesToRead, false);
|
||||
}
|
||||
|
||||
bool setPosition (int64 wantedPos)
|
||||
{
|
||||
const int amountToSkip = static_cast<int> (wantedPos - getPosition());
|
||||
|
||||
if (amountToSkip < 0)
|
||||
return false;
|
||||
|
||||
if (amountToSkip == 0)
|
||||
return true;
|
||||
|
||||
const int actuallySkipped = readOrSkip (nullptr, amountToSkip, true);
|
||||
|
||||
return actuallySkipped == amountToSkip;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// WebInputStream methods
|
||||
void withExtraHeaders (const String& extraHeaders)
|
||||
{
|
||||
if (! requestHeaders.endsWithChar ('\n') && requestHeaders.isNotEmpty())
|
||||
requestHeaders << "\r\n";
|
||||
|
||||
requestHeaders << extraHeaders;
|
||||
|
||||
if (! requestHeaders.endsWithChar ('\n') && requestHeaders.isNotEmpty())
|
||||
requestHeaders << "\r\n";
|
||||
}
|
||||
|
||||
void withCustomRequestCommand (const String& customRequestCommand) { httpRequest = customRequestCommand; }
|
||||
void withConnectionTimeout (int timeoutInMs) { timeOutMs = timeoutInMs; }
|
||||
void withNumRedirectsToFollow (int maxRedirectsToFollow) { maxRedirects = maxRedirectsToFollow; }
|
||||
StringPairArray getRequestHeaders() const { return WebInputStream::parseHttpHeaders (requestHeaders); }
|
||||
StringPairArray getResponseHeaders() const { return WebInputStream::parseHttpHeaders (responseHeaders); }
|
||||
int getStatusCode() const { return statusCode; }
|
||||
|
||||
//==============================================================================
|
||||
void cleanup()
|
||||
{
|
||||
const ScopedLock lock (cleanupLock);
|
||||
const ScopedLock sl (CURLSymbols::getLibcurlLock());
|
||||
|
||||
if (curl != nullptr)
|
||||
{
|
||||
symbols->curl_multi_remove_handle (multi, curl);
|
||||
|
||||
if (headerList != nullptr)
|
||||
{
|
||||
symbols->curl_slist_free_all (headerList);
|
||||
headerList = nullptr;
|
||||
}
|
||||
|
||||
symbols->curl_easy_cleanup (curl);
|
||||
curl = nullptr;
|
||||
}
|
||||
|
||||
if (multi != nullptr)
|
||||
{
|
||||
symbols->curl_multi_cleanup (multi);
|
||||
multi = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void cancel()
|
||||
{
|
||||
cleanup();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool setOptions()
|
||||
{
|
||||
auto address = url.toString (! addParametersToRequestBody);
|
||||
|
||||
curl_version_info_data* data = symbols->curl_version_info (CURLVERSION_NOW);
|
||||
jassert (data != nullptr);
|
||||
|
||||
if (! requestHeaders.endsWithChar ('\n'))
|
||||
requestHeaders << "\r\n";
|
||||
|
||||
if (hasBodyDataToSend)
|
||||
WebInputStream::createHeadersAndPostData (url,
|
||||
requestHeaders,
|
||||
headersAndPostData,
|
||||
addParametersToRequestBody);
|
||||
|
||||
if (! requestHeaders.endsWithChar ('\n'))
|
||||
requestHeaders << "\r\n";
|
||||
|
||||
auto userAgent = String ("curl/") + data->version;
|
||||
|
||||
if (symbols->curl_easy_setopt (curl, CURLOPT_URL, address.toRawUTF8()) == CURLE_OK
|
||||
&& symbols->curl_easy_setopt (curl, CURLOPT_WRITEDATA, this) == CURLE_OK
|
||||
&& symbols->curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, StaticCurlWrite) == CURLE_OK
|
||||
&& symbols->curl_easy_setopt (curl, CURLOPT_NOSIGNAL, 1) == CURLE_OK
|
||||
&& symbols->curl_easy_setopt (curl, CURLOPT_MAXREDIRS, static_cast<long> (maxRedirects)) == CURLE_OK
|
||||
&& symbols->curl_easy_setopt (curl, CURLOPT_USERAGENT, userAgent.toRawUTF8()) == CURLE_OK
|
||||
&& symbols->curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, (maxRedirects > 0 ? 1 : 0)) == CURLE_OK)
|
||||
{
|
||||
if (hasBodyDataToSend)
|
||||
{
|
||||
if (symbols->curl_easy_setopt (curl, CURLOPT_READDATA, this) != CURLE_OK
|
||||
|| symbols->curl_easy_setopt (curl, CURLOPT_READFUNCTION, StaticCurlRead) != CURLE_OK)
|
||||
return false;
|
||||
|
||||
if (symbols->curl_easy_setopt (curl, CURLOPT_POST, 1) != CURLE_OK
|
||||
|| symbols->curl_easy_setopt (curl, CURLOPT_POSTFIELDSIZE_LARGE, static_cast<curl_off_t> (headersAndPostData.getSize())) != CURLE_OK)
|
||||
return false;
|
||||
}
|
||||
|
||||
// handle special http request commands
|
||||
const auto hasSpecialRequestCmd = hasBodyDataToSend ? (httpRequest != "POST") : (httpRequest != "GET");
|
||||
|
||||
if (hasSpecialRequestCmd)
|
||||
if (symbols->curl_easy_setopt (curl, CURLOPT_CUSTOMREQUEST, httpRequest.toRawUTF8()) != CURLE_OK)
|
||||
return false;
|
||||
|
||||
if (symbols->curl_easy_setopt (curl, CURLOPT_HEADERDATA, this) != CURLE_OK
|
||||
|| symbols->curl_easy_setopt (curl, CURLOPT_HEADERFUNCTION, StaticCurlHeader) != CURLE_OK)
|
||||
return false;
|
||||
|
||||
if (timeOutMs > 0)
|
||||
{
|
||||
auto timeOutSecs = ((long) timeOutMs + 999) / 1000;
|
||||
|
||||
if (symbols->curl_easy_setopt (curl, CURLOPT_CONNECTTIMEOUT, timeOutSecs) != CURLE_OK
|
||||
|| symbols->curl_easy_setopt (curl, CURLOPT_LOW_SPEED_LIMIT, 100) != CURLE_OK
|
||||
|| symbols->curl_easy_setopt (curl, CURLOPT_LOW_SPEED_TIME, timeOutSecs) != CURLE_OK)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool connect (WebInputStream::Listener* webInputListener)
|
||||
{
|
||||
{
|
||||
const ScopedLock lock (cleanupLock);
|
||||
|
||||
if (curl == nullptr)
|
||||
return false;
|
||||
|
||||
if (! setOptions())
|
||||
{
|
||||
cleanup();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (requestHeaders.isNotEmpty())
|
||||
{
|
||||
const StringArray headerLines = StringArray::fromLines (requestHeaders);
|
||||
|
||||
// fromLines will always return at least one line if the string is not empty
|
||||
jassert (headerLines.size() > 0);
|
||||
headerList = symbols->curl_slist_append (headerList, headerLines [0].toRawUTF8());
|
||||
|
||||
for (int i = 1; (i < headerLines.size() && headerList != nullptr); ++i)
|
||||
headerList = symbols->curl_slist_append (headerList, headerLines [i].toRawUTF8());
|
||||
|
||||
if (headerList == nullptr)
|
||||
{
|
||||
cleanup();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (symbols->curl_easy_setopt (curl, CURLOPT_HTTPHEADER, headerList) != CURLE_OK)
|
||||
{
|
||||
cleanup();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
listener = webInputListener;
|
||||
|
||||
if (hasBodyDataToSend)
|
||||
postBuffer = &headersAndPostData;
|
||||
|
||||
size_t lastPos = static_cast<size_t> (-1);
|
||||
|
||||
// step until either: 1) there is an error 2) the transaction is complete
|
||||
// or 3) data is in the in buffer
|
||||
while ((! finished) && curlBuffer.isEmpty())
|
||||
{
|
||||
{
|
||||
const ScopedLock lock (cleanupLock);
|
||||
|
||||
if (curl == nullptr)
|
||||
return false;
|
||||
}
|
||||
|
||||
singleStep();
|
||||
|
||||
// call callbacks if this is a post request
|
||||
if (hasBodyDataToSend && listener != nullptr && lastPos != postPosition)
|
||||
{
|
||||
lastPos = postPosition;
|
||||
|
||||
if (! listener->postDataSendProgress (owner, static_cast<int> (lastPos), static_cast<int> (headersAndPostData.getSize())))
|
||||
{
|
||||
// user has decided to abort the transaction
|
||||
cleanup();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const ScopedLock lock (cleanupLock);
|
||||
|
||||
if (curl == nullptr)
|
||||
return false;
|
||||
|
||||
long responseCode;
|
||||
if (symbols->curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &responseCode) == CURLE_OK)
|
||||
statusCode = static_cast<int> (responseCode);
|
||||
|
||||
// get content length size
|
||||
double curlLength;
|
||||
if (symbols->curl_easy_getinfo (curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &curlLength) == CURLE_OK)
|
||||
contentLength = static_cast<int64> (curlLength);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void finish()
|
||||
{
|
||||
const ScopedLock lock (cleanupLock);
|
||||
|
||||
if (curl == nullptr)
|
||||
return;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
int cnt = 0;
|
||||
|
||||
if (CURLMsg* msg = symbols->curl_multi_info_read (multi, &cnt))
|
||||
{
|
||||
if (msg->msg == CURLMSG_DONE && msg->easy_handle == curl)
|
||||
{
|
||||
lastError = msg->data.result; // this is the error that stopped our process from continuing
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
finished = true;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void singleStep()
|
||||
{
|
||||
if (lastError != CURLE_OK)
|
||||
return;
|
||||
|
||||
fd_set fdread, fdwrite, fdexcep;
|
||||
int maxfd = -1;
|
||||
long curl_timeo;
|
||||
|
||||
{
|
||||
const ScopedLock lock (cleanupLock);
|
||||
|
||||
if (multi == nullptr)
|
||||
return;
|
||||
|
||||
if ((lastError = (int) symbols->curl_multi_timeout (multi, &curl_timeo)) != CURLM_OK)
|
||||
return;
|
||||
}
|
||||
|
||||
// why 980? see http://curl.haxx.se/libcurl/c/curl_multi_timeout.html
|
||||
if (curl_timeo < 0)
|
||||
curl_timeo = 980;
|
||||
|
||||
struct timeval tv;
|
||||
tv.tv_sec = curl_timeo / 1000;
|
||||
tv.tv_usec = (curl_timeo % 1000) * 1000;
|
||||
|
||||
FD_ZERO (&fdread);
|
||||
FD_ZERO (&fdwrite);
|
||||
FD_ZERO (&fdexcep);
|
||||
|
||||
{
|
||||
const ScopedLock lock (cleanupLock);
|
||||
|
||||
if (multi == nullptr)
|
||||
return;
|
||||
|
||||
if ((lastError = (int) symbols->curl_multi_fdset (multi, &fdread, &fdwrite, &fdexcep, &maxfd)) != CURLM_OK)
|
||||
return;
|
||||
}
|
||||
|
||||
if (maxfd != -1)
|
||||
{
|
||||
if (select (maxfd + 1, &fdread, &fdwrite, &fdexcep, &tv) < 0)
|
||||
{
|
||||
lastError = -1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// if curl does not return any sockets for to wait on, then the doc says to wait 100 ms
|
||||
Thread::sleep (100);
|
||||
}
|
||||
|
||||
int still_running = 0;
|
||||
int curlRet;
|
||||
|
||||
{
|
||||
const ScopedLock lock (cleanupLock);
|
||||
|
||||
while ((curlRet = (int) symbols->curl_multi_perform (multi, &still_running)) == CURLM_CALL_MULTI_PERFORM)
|
||||
{}
|
||||
}
|
||||
|
||||
if ((lastError = curlRet) != CURLM_OK)
|
||||
return;
|
||||
|
||||
if (still_running <= 0)
|
||||
finish();
|
||||
}
|
||||
|
||||
int readOrSkip (void* buffer, int bytesToRead, bool skip)
|
||||
{
|
||||
if (bytesToRead <= 0)
|
||||
return 0;
|
||||
|
||||
size_t pos = 0;
|
||||
size_t len = static_cast<size_t> (bytesToRead);
|
||||
|
||||
while (len > 0)
|
||||
{
|
||||
size_t bufferBytes = curlBuffer.getSize();
|
||||
bool removeSection = true;
|
||||
|
||||
if (bufferBytes == 0)
|
||||
{
|
||||
// do not call curl again if we are finished
|
||||
{
|
||||
const ScopedLock lock (cleanupLock);
|
||||
|
||||
if (finished || curl == nullptr)
|
||||
return static_cast<int> (pos);
|
||||
}
|
||||
|
||||
skipBytes = skip ? len : 0;
|
||||
singleStep();
|
||||
|
||||
// update the amount that was read/skipped from curl
|
||||
bufferBytes = skip ? len - skipBytes : curlBuffer.getSize();
|
||||
removeSection = ! skip;
|
||||
}
|
||||
|
||||
// can we copy data from the internal buffer?
|
||||
if (bufferBytes > 0)
|
||||
{
|
||||
size_t max = jmin (len, bufferBytes);
|
||||
|
||||
if (! skip)
|
||||
memcpy (addBytesToPointer (buffer, pos), curlBuffer.getData(), max);
|
||||
|
||||
pos += max;
|
||||
streamPos += static_cast<int64> (max);
|
||||
len -= max;
|
||||
|
||||
if (removeSection)
|
||||
curlBuffer.removeSection (0, max);
|
||||
}
|
||||
}
|
||||
|
||||
return static_cast<int> (pos);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// CURL callbacks
|
||||
size_t curlWriteCallback (char* ptr, size_t size, size_t nmemb)
|
||||
{
|
||||
if (curl == nullptr || lastError != CURLE_OK)
|
||||
return 0;
|
||||
|
||||
const size_t len = size * nmemb;
|
||||
|
||||
// skip bytes if necessary
|
||||
size_t max = jmin (skipBytes, len);
|
||||
skipBytes -= max;
|
||||
|
||||
if (len > max)
|
||||
curlBuffer.append (ptr + max, len - max);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
size_t curlReadCallback (char* ptr, size_t size, size_t nmemb)
|
||||
{
|
||||
if (curl == nullptr || postBuffer == nullptr || lastError != CURLE_OK)
|
||||
return 0;
|
||||
|
||||
const size_t len = size * nmemb;
|
||||
|
||||
size_t max = jmin (postBuffer->getSize() - postPosition, len);
|
||||
memcpy (ptr, (char*)postBuffer->getData() + postPosition, max);
|
||||
postPosition += max;
|
||||
|
||||
return max;
|
||||
}
|
||||
|
||||
size_t curlHeaderCallback (char* ptr, size_t size, size_t nmemb)
|
||||
{
|
||||
if (curl == nullptr || lastError != CURLE_OK)
|
||||
return 0;
|
||||
|
||||
size_t len = size * nmemb;
|
||||
|
||||
String header (ptr, len);
|
||||
|
||||
if (! header.contains (":") && header.startsWithIgnoreCase ("HTTP/"))
|
||||
responseHeaders.clear();
|
||||
else
|
||||
responseHeaders += header;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
// Static method wrappers
|
||||
static size_t StaticCurlWrite (char* ptr, size_t size, size_t nmemb, void* userdata)
|
||||
{
|
||||
WebInputStream::Pimpl* wi = reinterpret_cast<WebInputStream::Pimpl*> (userdata);
|
||||
return wi->curlWriteCallback (ptr, size, nmemb);
|
||||
}
|
||||
|
||||
static size_t StaticCurlRead (char* ptr, size_t size, size_t nmemb, void* userdata)
|
||||
{
|
||||
WebInputStream::Pimpl* wi = reinterpret_cast<WebInputStream::Pimpl*> (userdata);
|
||||
return wi->curlReadCallback (ptr, size, nmemb);
|
||||
}
|
||||
|
||||
static size_t StaticCurlHeader (char* ptr, size_t size, size_t nmemb, void* userdata)
|
||||
{
|
||||
WebInputStream::Pimpl* wi = reinterpret_cast<WebInputStream::Pimpl*> (userdata);
|
||||
return wi->curlHeaderCallback (ptr, size, nmemb);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
WebInputStream& owner;
|
||||
const URL url;
|
||||
std::unique_ptr<CURLSymbols> symbols { CURLSymbols::create() };
|
||||
|
||||
//==============================================================================
|
||||
// curl stuff
|
||||
CURLM* multi = nullptr;
|
||||
CURL* curl = nullptr;
|
||||
struct curl_slist* headerList = nullptr;
|
||||
int lastError = CURLE_OK;
|
||||
|
||||
//==============================================================================
|
||||
// Options
|
||||
int timeOutMs = 0;
|
||||
int maxRedirects = 5;
|
||||
const bool addParametersToRequestBody, hasBodyDataToSend;
|
||||
String httpRequest;
|
||||
|
||||
//==============================================================================
|
||||
// internal buffers and buffer positions
|
||||
int64 contentLength = -1, streamPos = 0;
|
||||
MemoryBlock curlBuffer;
|
||||
MemoryBlock headersAndPostData;
|
||||
String responseHeaders, requestHeaders;
|
||||
int statusCode = -1;
|
||||
|
||||
//==============================================================================
|
||||
bool finished = false;
|
||||
size_t skipBytes = 0;
|
||||
|
||||
//==============================================================================
|
||||
// Http POST variables
|
||||
const MemoryBlock* postBuffer = nullptr;
|
||||
size_t postPosition = 0;
|
||||
|
||||
//==============================================================================
|
||||
WebInputStream::Listener* listener = nullptr;
|
||||
|
||||
//==============================================================================
|
||||
CriticalSection cleanupLock;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl)
|
||||
};
|
||||
|
||||
std::unique_ptr<URL::DownloadTask> URL::downloadToFile (const File& targetLocation, const DownloadTaskOptions& options)
|
||||
{
|
||||
return URL::DownloadTask::createFallbackDownloader (*this, targetLocation, options);
|
||||
}
|
||||
|
||||
} // namespace juce
|
108
deps/juce/modules/juce_core/native/juce_intel_SharedCode.h
vendored
Normal file
108
deps/juce/modules/juce_core/native/juce_intel_SharedCode.h
vendored
Normal file
@ -0,0 +1,108 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#if JUCE_INTEL && ! JUCE_NO_INLINE_ASM
|
||||
|
||||
namespace SystemStatsHelpers
|
||||
{
|
||||
|
||||
static void doCPUID (uint32& a, uint32& b, uint32& c, uint32& d, uint32 type)
|
||||
{
|
||||
uint32 la = a, lb = b, lc = c, ld = d;
|
||||
|
||||
#if JUCE_32BIT && defined (__pic__)
|
||||
asm ("mov %%ebx, %%edi\n"
|
||||
"cpuid\n"
|
||||
"xchg %%edi, %%ebx\n"
|
||||
: "=a" (la), "=D" (lb), "=c" (lc), "=d" (ld)
|
||||
: "a" (type), "c" (0));
|
||||
#else
|
||||
asm ("cpuid\n"
|
||||
: "=a" (la), "=b" (lb), "=c" (lc), "=d" (ld)
|
||||
: "a" (type), "c" (0));
|
||||
#endif
|
||||
|
||||
a = la; b = lb; c = lc; d = ld;
|
||||
}
|
||||
|
||||
static void getCPUInfo (bool& hasMMX,
|
||||
bool& hasSSE,
|
||||
bool& hasSSE2,
|
||||
bool& has3DNow,
|
||||
bool& hasSSE3,
|
||||
bool& hasSSSE3,
|
||||
bool& hasFMA3,
|
||||
bool& hasSSE41,
|
||||
bool& hasSSE42,
|
||||
bool& hasAVX,
|
||||
bool& hasFMA4,
|
||||
bool& hasAVX2,
|
||||
bool& hasAVX512F,
|
||||
bool& hasAVX512DQ,
|
||||
bool& hasAVX512IFMA,
|
||||
bool& hasAVX512PF,
|
||||
bool& hasAVX512ER,
|
||||
bool& hasAVX512CD,
|
||||
bool& hasAVX512BW,
|
||||
bool& hasAVX512VL,
|
||||
bool& hasAVX512VBMI,
|
||||
bool& hasAVX512VPOPCNTDQ)
|
||||
{
|
||||
uint32 a = 0, b = 0, d = 0, c = 0;
|
||||
SystemStatsHelpers::doCPUID (a, b, c, d, 1);
|
||||
|
||||
hasMMX = (d & (1u << 23)) != 0;
|
||||
hasSSE = (d & (1u << 25)) != 0;
|
||||
hasSSE2 = (d & (1u << 26)) != 0;
|
||||
has3DNow = (b & (1u << 31)) != 0;
|
||||
hasSSE3 = (c & (1u << 0)) != 0;
|
||||
hasSSSE3 = (c & (1u << 9)) != 0;
|
||||
hasFMA3 = (c & (1u << 12)) != 0;
|
||||
hasSSE41 = (c & (1u << 19)) != 0;
|
||||
hasSSE42 = (c & (1u << 20)) != 0;
|
||||
hasAVX = (c & (1u << 28)) != 0;
|
||||
|
||||
SystemStatsHelpers::doCPUID (a, b, c, d, 0x80000001);
|
||||
hasFMA4 = (c & (1u << 16)) != 0;
|
||||
|
||||
SystemStatsHelpers::doCPUID (a, b, c, d, 7);
|
||||
hasAVX2 = (b & (1u << 5)) != 0;
|
||||
hasAVX512F = (b & (1u << 16)) != 0;
|
||||
hasAVX512DQ = (b & (1u << 17)) != 0;
|
||||
hasAVX512IFMA = (b & (1u << 21)) != 0;
|
||||
hasAVX512PF = (b & (1u << 26)) != 0;
|
||||
hasAVX512ER = (b & (1u << 27)) != 0;
|
||||
hasAVX512CD = (b & (1u << 28)) != 0;
|
||||
hasAVX512BW = (b & (1u << 30)) != 0;
|
||||
hasAVX512VL = (b & (1u << 31)) != 0;
|
||||
hasAVX512VBMI = (c & (1u << 1)) != 0;
|
||||
hasAVX512VPOPCNTDQ = (c & (1u << 14)) != 0;
|
||||
}
|
||||
|
||||
} // namespace SystemStatsHelpers
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
143
deps/juce/modules/juce_core/native/juce_linux_CommonFile.cpp
vendored
Normal file
143
deps/juce/modules/juce_core/native/juce_linux_CommonFile.cpp
vendored
Normal file
@ -0,0 +1,143 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
bool File::copyInternal (const File& dest) const
|
||||
{
|
||||
FileInputStream in (*this);
|
||||
|
||||
if (dest.deleteFile())
|
||||
{
|
||||
{
|
||||
FileOutputStream out (dest);
|
||||
|
||||
if (out.failedToOpen())
|
||||
return false;
|
||||
|
||||
if (out.writeFromInputStream (in, -1) == getSize())
|
||||
return true;
|
||||
}
|
||||
|
||||
dest.deleteFile();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void File::findFileSystemRoots (Array<File>& destArray)
|
||||
{
|
||||
destArray.add (File ("/"));
|
||||
}
|
||||
|
||||
bool File::isHidden() const
|
||||
{
|
||||
return getFileName().startsWithChar ('.');
|
||||
}
|
||||
|
||||
bool File::isSymbolicLink() const
|
||||
{
|
||||
return getNativeLinkedTarget().isNotEmpty();
|
||||
}
|
||||
|
||||
String File::getNativeLinkedTarget() const
|
||||
{
|
||||
constexpr int bufferSize = 8194;
|
||||
HeapBlock<char> buffer (bufferSize);
|
||||
auto numBytes = (int) readlink (getFullPathName().toRawUTF8(), buffer, bufferSize - 2);
|
||||
return String::fromUTF8 (buffer, jmax (0, numBytes));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class DirectoryIterator::NativeIterator::Pimpl
|
||||
{
|
||||
public:
|
||||
Pimpl (const File& directory, const String& wc)
|
||||
: parentDir (File::addTrailingSeparator (directory.getFullPathName())),
|
||||
wildCard (wc), dir (opendir (directory.getFullPathName().toUTF8()))
|
||||
{
|
||||
}
|
||||
|
||||
~Pimpl()
|
||||
{
|
||||
if (dir != nullptr)
|
||||
closedir (dir);
|
||||
}
|
||||
|
||||
bool next (String& filenameFound,
|
||||
bool* const isDir, bool* const isHidden, int64* const fileSize,
|
||||
Time* const modTime, Time* const creationTime, bool* const isReadOnly)
|
||||
{
|
||||
if (dir != nullptr)
|
||||
{
|
||||
const char* wildcardUTF8 = nullptr;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
struct dirent* const de = readdir (dir);
|
||||
|
||||
if (de == nullptr)
|
||||
break;
|
||||
|
||||
if (wildcardUTF8 == nullptr)
|
||||
wildcardUTF8 = wildCard.toUTF8();
|
||||
|
||||
if (fnmatch (wildcardUTF8, de->d_name, FNM_CASEFOLD) == 0)
|
||||
{
|
||||
filenameFound = CharPointer_UTF8 (de->d_name);
|
||||
|
||||
updateStatInfoForFile (parentDir + filenameFound, isDir, fileSize, modTime, creationTime, isReadOnly);
|
||||
|
||||
if (isHidden != nullptr)
|
||||
*isHidden = filenameFound.startsWithChar ('.');
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
String parentDir, wildCard;
|
||||
DIR* dir;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (Pimpl)
|
||||
};
|
||||
|
||||
DirectoryIterator::NativeIterator::NativeIterator (const File& directory, const String& wildCardStr)
|
||||
: pimpl (new DirectoryIterator::NativeIterator::Pimpl (directory, wildCardStr))
|
||||
{
|
||||
}
|
||||
|
||||
DirectoryIterator::NativeIterator::~NativeIterator() {}
|
||||
|
||||
bool DirectoryIterator::NativeIterator::next (String& filenameFound,
|
||||
bool* isDir, bool* isHidden, int64* fileSize,
|
||||
Time* modTime, Time* creationTime, bool* isReadOnly)
|
||||
{
|
||||
return pimpl->next (filenameFound, isDir, isHidden, fileSize, modTime, creationTime, isReadOnly);
|
||||
}
|
||||
|
||||
} // namespace juce
|
248
deps/juce/modules/juce_core/native/juce_linux_Files.cpp
vendored
Normal file
248
deps/juce/modules/juce_core/native/juce_linux_Files.cpp
vendored
Normal file
@ -0,0 +1,248 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#if JUCE_BSD
|
||||
extern char** environ;
|
||||
#endif
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
enum
|
||||
{
|
||||
U_ISOFS_SUPER_MAGIC = 0x9660, // linux/iso_fs.h
|
||||
U_MSDOS_SUPER_MAGIC = 0x4d44, // linux/msdos_fs.h
|
||||
U_NFS_SUPER_MAGIC = 0x6969, // linux/nfs_fs.h
|
||||
U_SMB_SUPER_MAGIC = 0x517B // linux/smb_fs.h
|
||||
};
|
||||
|
||||
bool File::isOnCDRomDrive() const
|
||||
{
|
||||
struct statfs buf;
|
||||
|
||||
return statfs (getFullPathName().toUTF8(), &buf) == 0
|
||||
&& buf.f_type == (unsigned int) U_ISOFS_SUPER_MAGIC;
|
||||
}
|
||||
|
||||
bool File::isOnHardDisk() const
|
||||
{
|
||||
struct statfs buf;
|
||||
|
||||
if (statfs (getFullPathName().toUTF8(), &buf) == 0)
|
||||
{
|
||||
switch (buf.f_type)
|
||||
{
|
||||
case U_ISOFS_SUPER_MAGIC: // CD-ROM
|
||||
case U_MSDOS_SUPER_MAGIC: // Probably floppy (but could be mounted FAT filesystem)
|
||||
case U_NFS_SUPER_MAGIC: // Network NFS
|
||||
case U_SMB_SUPER_MAGIC: // Network Samba
|
||||
return false;
|
||||
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
// Assume so if this fails for some reason
|
||||
return true;
|
||||
}
|
||||
|
||||
bool File::isOnRemovableDrive() const
|
||||
{
|
||||
jassertfalse; // xxx not implemented for linux!
|
||||
return false;
|
||||
}
|
||||
|
||||
String File::getVersion() const
|
||||
{
|
||||
return {}; // xxx not yet implemented
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static File resolveXDGFolder (const char* const type, const char* const fallbackFolder)
|
||||
{
|
||||
StringArray confLines;
|
||||
File ("~/.config/user-dirs.dirs").readLines (confLines);
|
||||
|
||||
for (int i = 0; i < confLines.size(); ++i)
|
||||
{
|
||||
const String line (confLines[i].trimStart());
|
||||
|
||||
if (line.startsWith (type))
|
||||
{
|
||||
// eg. resolve XDG_MUSIC_DIR="$HOME/Music" to /home/user/Music
|
||||
const File f (line.replace ("$HOME", File ("~").getFullPathName())
|
||||
.fromFirstOccurrenceOf ("=", false, false)
|
||||
.trim().unquoted());
|
||||
|
||||
if (f.isDirectory())
|
||||
return f;
|
||||
}
|
||||
}
|
||||
|
||||
return File (fallbackFolder);
|
||||
}
|
||||
|
||||
const char* const* juce_argv = nullptr;
|
||||
int juce_argc = 0;
|
||||
|
||||
File File::getSpecialLocation (const SpecialLocationType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case userHomeDirectory:
|
||||
{
|
||||
if (const char* homeDir = getenv ("HOME"))
|
||||
return File (CharPointer_UTF8 (homeDir));
|
||||
|
||||
if (auto* pw = getpwuid (getuid()))
|
||||
return File (CharPointer_UTF8 (pw->pw_dir));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
case userDocumentsDirectory: return resolveXDGFolder ("XDG_DOCUMENTS_DIR", "~/Documents");
|
||||
case userMusicDirectory: return resolveXDGFolder ("XDG_MUSIC_DIR", "~/Music");
|
||||
case userMoviesDirectory: return resolveXDGFolder ("XDG_VIDEOS_DIR", "~/Videos");
|
||||
case userPicturesDirectory: return resolveXDGFolder ("XDG_PICTURES_DIR", "~/Pictures");
|
||||
case userDesktopDirectory: return resolveXDGFolder ("XDG_DESKTOP_DIR", "~/Desktop");
|
||||
case userApplicationDataDirectory: return resolveXDGFolder ("XDG_CONFIG_HOME", "~/.config");
|
||||
case commonDocumentsDirectory:
|
||||
case commonApplicationDataDirectory: return File ("/opt");
|
||||
case globalApplicationsDirectory: return File ("/usr");
|
||||
|
||||
case tempDirectory:
|
||||
{
|
||||
if (const char* tmpDir = getenv ("TMPDIR"))
|
||||
return File (CharPointer_UTF8 (tmpDir));
|
||||
|
||||
return File ("/tmp");
|
||||
}
|
||||
|
||||
case invokedExecutableFile:
|
||||
if (juce_argv != nullptr && juce_argc > 0)
|
||||
return File (CharPointer_UTF8 (juce_argv[0]));
|
||||
// Falls through
|
||||
JUCE_FALLTHROUGH
|
||||
|
||||
case currentExecutableFile:
|
||||
case currentApplicationFile:
|
||||
#if ! JUCE_STANDALONE_APPLICATION
|
||||
return juce_getExecutableFile();
|
||||
#endif
|
||||
// deliberate fall-through if this is not a shared-library
|
||||
JUCE_FALLTHROUGH
|
||||
|
||||
case hostApplicationPath:
|
||||
{
|
||||
#if JUCE_BSD
|
||||
return juce_getExecutableFile();
|
||||
#else
|
||||
const File f ("/proc/self/exe");
|
||||
return f.isSymbolicLink() ? f.getLinkedTarget() : juce_getExecutableFile();
|
||||
#endif
|
||||
}
|
||||
|
||||
default:
|
||||
jassertfalse; // unknown type?
|
||||
break;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool File::moveToTrash() const
|
||||
{
|
||||
if (! exists())
|
||||
return true;
|
||||
|
||||
File trashCan ("~/.Trash");
|
||||
|
||||
if (! trashCan.isDirectory())
|
||||
trashCan = "~/.local/share/Trash/files";
|
||||
|
||||
if (! trashCan.isDirectory())
|
||||
return false;
|
||||
|
||||
return moveFileTo (trashCan.getNonexistentChildFile (getFileNameWithoutExtension(),
|
||||
getFileExtension()));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static bool isFileExecutable (const String& filename)
|
||||
{
|
||||
juce_statStruct info;
|
||||
|
||||
return juce_stat (filename, info)
|
||||
&& S_ISREG (info.st_mode)
|
||||
&& access (filename.toUTF8(), X_OK) == 0;
|
||||
}
|
||||
|
||||
bool Process::openDocument (const String& fileName, const String& parameters)
|
||||
{
|
||||
const auto cmdString = [&]
|
||||
{
|
||||
if (fileName.startsWithIgnoreCase ("file:")
|
||||
|| File::createFileWithoutCheckingPath (fileName).isDirectory()
|
||||
|| ! isFileExecutable (fileName))
|
||||
{
|
||||
const auto singleCommand = fileName.trim().quoted();
|
||||
|
||||
StringArray cmdLines;
|
||||
|
||||
for (auto browserName : { "xdg-open", "/etc/alternatives/x-www-browser", "firefox", "mozilla",
|
||||
"google-chrome", "chromium-browser", "opera", "konqueror" })
|
||||
{
|
||||
cmdLines.add (String (browserName) + " " + singleCommand);
|
||||
}
|
||||
|
||||
return cmdLines.joinIntoString (" || ");
|
||||
}
|
||||
|
||||
return (fileName.replace (" ", "\\ ", false) + " " + parameters).trim();
|
||||
}();
|
||||
|
||||
const char* const argv[] = { "/bin/sh", "-c", cmdString.toUTF8(), nullptr };
|
||||
|
||||
const auto cpid = fork();
|
||||
|
||||
if (cpid == 0)
|
||||
{
|
||||
setsid();
|
||||
|
||||
// Child process
|
||||
execve (argv[0], (char**) argv, environ);
|
||||
exit (0);
|
||||
}
|
||||
|
||||
return cpid >= 0;
|
||||
}
|
||||
|
||||
void File::revealToUser() const
|
||||
{
|
||||
if (isDirectory())
|
||||
startAsProcess();
|
||||
else if (getParentDirectory().exists())
|
||||
getParentDirectory().startAsProcess();
|
||||
}
|
||||
|
||||
} // namespace juce
|
616
deps/juce/modules/juce_core/native/juce_linux_Network.cpp
vendored
Normal file
616
deps/juce/modules/juce_core/native/juce_linux_Network.cpp
vendored
Normal file
@ -0,0 +1,616 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
void MACAddress::findAllAddresses (Array<MACAddress>& result)
|
||||
{
|
||||
#if JUCE_BSD
|
||||
struct ifaddrs* addrs = nullptr;
|
||||
|
||||
if (getifaddrs (&addrs) != -1)
|
||||
{
|
||||
for (auto* i = addrs; i != nullptr; i = i->ifa_next)
|
||||
{
|
||||
if (i->ifa_addr->sa_family == AF_LINK)
|
||||
{
|
||||
struct sockaddr_dl* sdl = (struct sockaddr_dl*) i->ifa_addr;
|
||||
MACAddress ma ((const uint8*) (sdl->sdl_data + sdl->sdl_nlen));
|
||||
|
||||
if (! ma.isNull())
|
||||
result.addIfNotAlreadyThere (ma);
|
||||
}
|
||||
}
|
||||
|
||||
freeifaddrs (addrs);
|
||||
}
|
||||
#else
|
||||
auto s = socket (AF_INET, SOCK_DGRAM, 0);
|
||||
|
||||
if (s != -1)
|
||||
{
|
||||
struct ifaddrs* addrs = nullptr;
|
||||
|
||||
if (getifaddrs (&addrs) != -1)
|
||||
{
|
||||
for (auto* i = addrs; i != nullptr; i = i->ifa_next)
|
||||
{
|
||||
struct ifreq ifr;
|
||||
strcpy (ifr.ifr_name, i->ifa_name);
|
||||
ifr.ifr_addr.sa_family = AF_INET;
|
||||
|
||||
if (ioctl (s, SIOCGIFHWADDR, &ifr) == 0)
|
||||
{
|
||||
MACAddress ma ((const uint8*) ifr.ifr_hwaddr.sa_data);
|
||||
|
||||
if (! ma.isNull())
|
||||
result.addIfNotAlreadyThere (ma);
|
||||
}
|
||||
}
|
||||
|
||||
freeifaddrs (addrs);
|
||||
}
|
||||
|
||||
::close (s);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
bool JUCE_CALLTYPE Process::openEmailWithAttachments (const String& /* targetEmailAddress */,
|
||||
const String& /* emailSubject */,
|
||||
const String& /* bodyText */,
|
||||
const StringArray& /* filesToAttach */)
|
||||
{
|
||||
jassertfalse; // xxx todo
|
||||
return false;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
#if ! JUCE_USE_CURL
|
||||
class WebInputStream::Pimpl
|
||||
{
|
||||
public:
|
||||
Pimpl (WebInputStream& pimplOwner, const URL& urlToCopy, bool addParametersToBody)
|
||||
: owner (pimplOwner),
|
||||
url (urlToCopy),
|
||||
addParametersToRequestBody (addParametersToBody),
|
||||
hasBodyDataToSend (addParametersToRequestBody || url.hasBodyDataToSend()),
|
||||
httpRequestCmd (hasBodyDataToSend ? "POST" : "GET")
|
||||
{
|
||||
}
|
||||
|
||||
~Pimpl()
|
||||
{
|
||||
closeSocket();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// WebInputStream methods
|
||||
void withExtraHeaders (const String& extraHeaders)
|
||||
{
|
||||
if (! headers.endsWithChar ('\n') && headers.isNotEmpty())
|
||||
headers << "\r\n";
|
||||
|
||||
headers << extraHeaders;
|
||||
|
||||
if (! headers.endsWithChar ('\n') && headers.isNotEmpty())
|
||||
headers << "\r\n";
|
||||
}
|
||||
|
||||
void withCustomRequestCommand (const String& customRequestCommand) { httpRequestCmd = customRequestCommand; }
|
||||
void withConnectionTimeout (int timeoutInMs) { timeOutMs = timeoutInMs; }
|
||||
void withNumRedirectsToFollow (int maxRedirectsToFollow) { numRedirectsToFollow = maxRedirectsToFollow; }
|
||||
int getStatusCode() const { return statusCode; }
|
||||
StringPairArray getRequestHeaders() const { return WebInputStream::parseHttpHeaders (headers); }
|
||||
|
||||
StringPairArray getResponseHeaders() const
|
||||
{
|
||||
StringPairArray responseHeaders;
|
||||
|
||||
if (! isError())
|
||||
{
|
||||
for (int i = 0; i < headerLines.size(); ++i)
|
||||
{
|
||||
auto& headersEntry = headerLines[i];
|
||||
auto key = headersEntry.upToFirstOccurrenceOf (": ", false, false);
|
||||
auto value = headersEntry.fromFirstOccurrenceOf (": ", false, false);
|
||||
auto previousValue = responseHeaders[key];
|
||||
responseHeaders.set (key, previousValue.isEmpty() ? value : (previousValue + "," + value));
|
||||
}
|
||||
}
|
||||
|
||||
return responseHeaders;
|
||||
}
|
||||
|
||||
bool connect (WebInputStream::Listener* listener)
|
||||
{
|
||||
{
|
||||
const ScopedLock lock (createSocketLock);
|
||||
|
||||
if (hasBeenCancelled)
|
||||
return false;
|
||||
}
|
||||
|
||||
address = url.toString (! addParametersToRequestBody);
|
||||
statusCode = createConnection (listener, numRedirectsToFollow);
|
||||
|
||||
return statusCode != 0;
|
||||
}
|
||||
|
||||
void cancel()
|
||||
{
|
||||
const ScopedLock lock (createSocketLock);
|
||||
|
||||
hasBeenCancelled = true;
|
||||
statusCode = -1;
|
||||
finished = true;
|
||||
|
||||
closeSocket();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool isError() const { return socketHandle < 0; }
|
||||
bool isExhausted() { return finished; }
|
||||
int64 getPosition() { return position; }
|
||||
int64 getTotalLength() { return contentLength; }
|
||||
|
||||
int read (void* buffer, int bytesToRead)
|
||||
{
|
||||
if (finished || isError())
|
||||
return 0;
|
||||
|
||||
if (isChunked && ! readingChunk)
|
||||
{
|
||||
if (position >= chunkEnd)
|
||||
{
|
||||
const ScopedValueSetter<bool> setter (readingChunk, true, false);
|
||||
MemoryOutputStream chunkLengthBuffer;
|
||||
char c = 0;
|
||||
|
||||
if (chunkEnd > 0)
|
||||
{
|
||||
if (read (&c, 1) != 1 || c != '\r'
|
||||
|| read (&c, 1) != 1 || c != '\n')
|
||||
{
|
||||
finished = true;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
while (chunkLengthBuffer.getDataSize() < 512 && ! (finished || isError()))
|
||||
{
|
||||
if (read (&c, 1) != 1)
|
||||
{
|
||||
finished = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (c == '\r')
|
||||
continue;
|
||||
|
||||
if (c == '\n')
|
||||
break;
|
||||
|
||||
chunkLengthBuffer.writeByte (c);
|
||||
}
|
||||
|
||||
auto chunkSize = chunkLengthBuffer.toString().trimStart().getHexValue64();
|
||||
|
||||
if (chunkSize == 0)
|
||||
{
|
||||
finished = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
chunkEnd += chunkSize;
|
||||
}
|
||||
|
||||
if (bytesToRead > chunkEnd - position)
|
||||
bytesToRead = static_cast<int> (chunkEnd - position);
|
||||
}
|
||||
|
||||
pollfd pfd { socketHandle, POLLIN, 0 };
|
||||
|
||||
if (poll (&pfd, 1, timeOutMs) <= 0)
|
||||
return 0; // (timeout)
|
||||
|
||||
auto bytesRead = jmax (0, (int) recv (socketHandle, buffer, (size_t) bytesToRead, MSG_WAITALL));
|
||||
|
||||
if (bytesRead == 0)
|
||||
finished = true;
|
||||
|
||||
if (! readingChunk)
|
||||
position += bytesRead;
|
||||
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
bool setPosition (int64 wantedPos)
|
||||
{
|
||||
if (isError())
|
||||
return false;
|
||||
|
||||
if (wantedPos != position)
|
||||
{
|
||||
finished = false;
|
||||
|
||||
if (wantedPos < position)
|
||||
return false;
|
||||
|
||||
auto numBytesToSkip = wantedPos - position;
|
||||
auto skipBufferSize = (int) jmin (numBytesToSkip, (int64) 16384);
|
||||
HeapBlock<char> temp (skipBufferSize);
|
||||
|
||||
while (numBytesToSkip > 0 && ! isExhausted())
|
||||
numBytesToSkip -= read (temp, (int) jmin (numBytesToSkip, (int64) skipBufferSize));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
int statusCode = 0;
|
||||
|
||||
private:
|
||||
WebInputStream& owner;
|
||||
URL url;
|
||||
int socketHandle = -1, levelsOfRedirection = 0;
|
||||
StringArray headerLines;
|
||||
String address, headers;
|
||||
MemoryBlock postData;
|
||||
int64 contentLength = -1, position = 0;
|
||||
bool finished = false;
|
||||
const bool addParametersToRequestBody, hasBodyDataToSend;
|
||||
int timeOutMs = 0;
|
||||
int numRedirectsToFollow = 5;
|
||||
String httpRequestCmd;
|
||||
int64 chunkEnd = 0;
|
||||
bool isChunked = false, readingChunk = false;
|
||||
CriticalSection closeSocketLock, createSocketLock;
|
||||
bool hasBeenCancelled = false;
|
||||
|
||||
void closeSocket (bool resetLevelsOfRedirection = true)
|
||||
{
|
||||
const ScopedLock lock (closeSocketLock);
|
||||
|
||||
if (socketHandle >= 0)
|
||||
{
|
||||
::shutdown (socketHandle, SHUT_RDWR);
|
||||
::close (socketHandle);
|
||||
}
|
||||
|
||||
socketHandle = -1;
|
||||
|
||||
if (resetLevelsOfRedirection)
|
||||
levelsOfRedirection = 0;
|
||||
}
|
||||
|
||||
int createConnection (WebInputStream::Listener* listener, int numRedirects)
|
||||
{
|
||||
closeSocket (false);
|
||||
|
||||
if (hasBodyDataToSend)
|
||||
WebInputStream::createHeadersAndPostData (url,
|
||||
headers,
|
||||
postData,
|
||||
addParametersToRequestBody);
|
||||
|
||||
auto timeOutTime = Time::getMillisecondCounter();
|
||||
|
||||
if (timeOutMs == 0)
|
||||
timeOutMs = 30000;
|
||||
|
||||
if (timeOutMs < 0)
|
||||
timeOutTime = 0xffffffff;
|
||||
else
|
||||
timeOutTime += (uint32) timeOutMs;
|
||||
|
||||
String hostName, hostPath;
|
||||
int hostPort;
|
||||
|
||||
if (! decomposeURL (address, hostName, hostPath, hostPort))
|
||||
return 0;
|
||||
|
||||
String serverName, proxyName, proxyPath;
|
||||
int proxyPort = 0;
|
||||
int port = 0;
|
||||
|
||||
auto proxyURL = String::fromUTF8 (getenv ("http_proxy"));
|
||||
|
||||
if (proxyURL.startsWithIgnoreCase ("http://"))
|
||||
{
|
||||
if (! decomposeURL (proxyURL, proxyName, proxyPath, proxyPort))
|
||||
return 0;
|
||||
|
||||
serverName = proxyName;
|
||||
port = proxyPort;
|
||||
}
|
||||
else
|
||||
{
|
||||
serverName = hostName;
|
||||
port = hostPort;
|
||||
}
|
||||
|
||||
struct addrinfo hints;
|
||||
zerostruct (hints);
|
||||
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_flags = AI_NUMERICSERV;
|
||||
|
||||
struct addrinfo* result = nullptr;
|
||||
|
||||
if (getaddrinfo (serverName.toUTF8(), String (port).toUTF8(), &hints, &result) != 0 || result == nullptr)
|
||||
return 0;
|
||||
|
||||
{
|
||||
const ScopedLock lock (createSocketLock);
|
||||
|
||||
socketHandle = hasBeenCancelled ? -1
|
||||
: socket (result->ai_family, result->ai_socktype, 0);
|
||||
}
|
||||
|
||||
if (socketHandle == -1)
|
||||
{
|
||||
freeaddrinfo (result);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int receiveBufferSize = 16384;
|
||||
setsockopt (socketHandle, SOL_SOCKET, SO_RCVBUF, (char*) &receiveBufferSize, sizeof (receiveBufferSize));
|
||||
setsockopt (socketHandle, SOL_SOCKET, SO_KEEPALIVE, nullptr, 0);
|
||||
|
||||
#if JUCE_MAC
|
||||
setsockopt (socketHandle, SOL_SOCKET, SO_NOSIGPIPE, 0, 0);
|
||||
#endif
|
||||
|
||||
if (::connect (socketHandle, result->ai_addr, result->ai_addrlen) == -1)
|
||||
{
|
||||
closeSocket();
|
||||
freeaddrinfo (result);
|
||||
return 0;
|
||||
}
|
||||
|
||||
freeaddrinfo (result);
|
||||
|
||||
{
|
||||
const MemoryBlock requestHeader (createRequestHeader (hostName, hostPort, proxyName, proxyPort, hostPath, address,
|
||||
headers, postData, httpRequestCmd));
|
||||
|
||||
if (! sendHeader (socketHandle, requestHeader, timeOutTime, owner, listener))
|
||||
{
|
||||
closeSocket();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
auto responseHeader = readResponse (timeOutTime);
|
||||
position = 0;
|
||||
|
||||
if (responseHeader.isNotEmpty())
|
||||
{
|
||||
headerLines = StringArray::fromLines (responseHeader);
|
||||
|
||||
auto status = responseHeader.fromFirstOccurrenceOf (" ", false, false)
|
||||
.substring (0, 3).getIntValue();
|
||||
|
||||
auto location = findHeaderItem (headerLines, "Location:");
|
||||
|
||||
if (++levelsOfRedirection <= numRedirects
|
||||
&& status >= 300 && status < 400
|
||||
&& location.isNotEmpty() && location != address)
|
||||
{
|
||||
if (! (location.startsWithIgnoreCase ("http://")
|
||||
|| location.startsWithIgnoreCase ("https://")
|
||||
|| location.startsWithIgnoreCase ("ftp://")))
|
||||
{
|
||||
// The following is a bit dodgy. Ideally, we should do a proper transform of the relative URI to a target URI
|
||||
if (location.startsWithChar ('/'))
|
||||
location = URL (address).withNewSubPath (location).toString (true);
|
||||
else
|
||||
location = address + "/" + location;
|
||||
}
|
||||
|
||||
address = location;
|
||||
return createConnection (listener, numRedirects);
|
||||
}
|
||||
|
||||
auto contentLengthString = findHeaderItem (headerLines, "Content-Length:");
|
||||
|
||||
if (contentLengthString.isNotEmpty())
|
||||
contentLength = contentLengthString.getLargeIntValue();
|
||||
|
||||
isChunked = (findHeaderItem (headerLines, "Transfer-Encoding:") == "chunked");
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
closeSocket();
|
||||
return 0;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
String readResponse (uint32 timeOutTime)
|
||||
{
|
||||
int numConsecutiveLFs = 0;
|
||||
MemoryOutputStream buffer;
|
||||
|
||||
while (numConsecutiveLFs < 2
|
||||
&& buffer.getDataSize() < 32768
|
||||
&& Time::getMillisecondCounter() <= timeOutTime
|
||||
&& ! (finished || isError()))
|
||||
{
|
||||
char c = 0;
|
||||
|
||||
if (read (&c, 1) != 1)
|
||||
return {};
|
||||
|
||||
buffer.writeByte (c);
|
||||
|
||||
if (c == '\n')
|
||||
++numConsecutiveLFs;
|
||||
else if (c != '\r')
|
||||
numConsecutiveLFs = 0;
|
||||
}
|
||||
|
||||
auto header = buffer.toString().trimEnd();
|
||||
|
||||
if (header.startsWithIgnoreCase ("HTTP/"))
|
||||
return header;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
static void writeValueIfNotPresent (MemoryOutputStream& dest, const String& headers, const String& key, const String& value)
|
||||
{
|
||||
if (! headers.containsIgnoreCase (key))
|
||||
dest << "\r\n" << key << ' ' << value;
|
||||
}
|
||||
|
||||
static void writeHost (MemoryOutputStream& dest, const String& httpRequestCmd,
|
||||
const String& path, const String& host, int port)
|
||||
{
|
||||
dest << httpRequestCmd << ' ' << path << " HTTP/1.1\r\nHost: " << host;
|
||||
|
||||
/* HTTP spec 14.23 says that the port number must be included in the header if it is not 80 */
|
||||
if (port != 80)
|
||||
dest << ':' << port;
|
||||
}
|
||||
|
||||
static MemoryBlock createRequestHeader (const String& hostName, int hostPort,
|
||||
const String& proxyName, int proxyPort,
|
||||
const String& hostPath, const String& originalURL,
|
||||
const String& userHeaders, const MemoryBlock& postData,
|
||||
const String& httpRequestCmd)
|
||||
{
|
||||
MemoryOutputStream header;
|
||||
|
||||
if (proxyName.isEmpty())
|
||||
writeHost (header, httpRequestCmd, hostPath, hostName, hostPort);
|
||||
else
|
||||
writeHost (header, httpRequestCmd, originalURL, proxyName, proxyPort);
|
||||
|
||||
writeValueIfNotPresent (header, userHeaders, "User-Agent:", "JUCE/" JUCE_STRINGIFY(JUCE_MAJOR_VERSION)
|
||||
"." JUCE_STRINGIFY(JUCE_MINOR_VERSION)
|
||||
"." JUCE_STRINGIFY(JUCE_BUILDNUMBER));
|
||||
writeValueIfNotPresent (header, userHeaders, "Connection:", "close");
|
||||
|
||||
const auto postDataSize = postData.getSize();
|
||||
const auto hasPostData = postDataSize > 0;
|
||||
|
||||
if (hasPostData)
|
||||
writeValueIfNotPresent (header, userHeaders, "Content-Length:", String ((int) postDataSize));
|
||||
|
||||
if (userHeaders.isNotEmpty())
|
||||
header << "\r\n" << userHeaders;
|
||||
|
||||
header << "\r\n\r\n";
|
||||
|
||||
if (hasPostData)
|
||||
header << postData;
|
||||
|
||||
return header.getMemoryBlock();
|
||||
}
|
||||
|
||||
static bool sendHeader (int socketHandle, const MemoryBlock& requestHeader, uint32 timeOutTime,
|
||||
WebInputStream& pimplOwner, WebInputStream::Listener* listener)
|
||||
{
|
||||
size_t totalHeaderSent = 0;
|
||||
|
||||
while (totalHeaderSent < requestHeader.getSize())
|
||||
{
|
||||
if (Time::getMillisecondCounter() > timeOutTime)
|
||||
return false;
|
||||
|
||||
auto numToSend = jmin (1024, (int) (requestHeader.getSize() - totalHeaderSent));
|
||||
|
||||
if (send (socketHandle, static_cast<const char*> (requestHeader.getData()) + totalHeaderSent, (size_t) numToSend, 0) != numToSend)
|
||||
return false;
|
||||
|
||||
totalHeaderSent += (size_t) numToSend;
|
||||
|
||||
if (listener != nullptr && ! listener->postDataSendProgress (pimplOwner, (int) totalHeaderSent, (int) requestHeader.getSize()))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool decomposeURL (const String& url, String& host, String& path, int& port)
|
||||
{
|
||||
if (! url.startsWithIgnoreCase ("http://"))
|
||||
return false;
|
||||
|
||||
auto nextSlash = url.indexOfChar (7, '/');
|
||||
auto nextColon = url.indexOfChar (7, ':');
|
||||
|
||||
if (nextColon > nextSlash && nextSlash > 0)
|
||||
nextColon = -1;
|
||||
|
||||
if (nextColon >= 0)
|
||||
{
|
||||
host = url.substring (7, nextColon);
|
||||
|
||||
if (nextSlash >= 0)
|
||||
port = url.substring (nextColon + 1, nextSlash).getIntValue();
|
||||
else
|
||||
port = url.substring (nextColon + 1).getIntValue();
|
||||
}
|
||||
else
|
||||
{
|
||||
port = 80;
|
||||
|
||||
if (nextSlash >= 0)
|
||||
host = url.substring (7, nextSlash);
|
||||
else
|
||||
host = url.substring (7);
|
||||
}
|
||||
|
||||
if (nextSlash >= 0)
|
||||
path = url.substring (nextSlash);
|
||||
else
|
||||
path = "/";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static String findHeaderItem (const StringArray& lines, const String& itemName)
|
||||
{
|
||||
for (int i = 0; i < lines.size(); ++i)
|
||||
if (lines[i].startsWithIgnoreCase (itemName))
|
||||
return lines[i].substring (itemName.length()).trim();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl)
|
||||
};
|
||||
|
||||
std::unique_ptr<URL::DownloadTask> URL::downloadToFile (const File& targetLocation, const DownloadTaskOptions& options)
|
||||
{
|
||||
return URL::DownloadTask::createFallbackDownloader (*this, targetLocation, options);
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
367
deps/juce/modules/juce_core/native/juce_linux_SystemStats.cpp
vendored
Normal file
367
deps/juce/modules/juce_core/native/juce_linux_SystemStats.cpp
vendored
Normal file
@ -0,0 +1,367 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#if JUCE_BELA
|
||||
extern "C" int cobalt_thread_mode();
|
||||
#endif
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#if ! JUCE_BSD
|
||||
static String getCpuInfo (const char* key)
|
||||
{
|
||||
return readPosixConfigFileValue ("/proc/cpuinfo", key);
|
||||
}
|
||||
|
||||
static String getLocaleValue (nl_item key)
|
||||
{
|
||||
auto oldLocale = ::setlocale (LC_ALL, "");
|
||||
auto result = String::fromUTF8 (nl_langinfo (key));
|
||||
::setlocale (LC_ALL, oldLocale);
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
void Logger::outputDebugString (const String& text)
|
||||
{
|
||||
std::cerr << text << std::endl;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
SystemStats::OperatingSystemType SystemStats::getOperatingSystemType()
|
||||
{
|
||||
return Linux;
|
||||
}
|
||||
|
||||
String SystemStats::getOperatingSystemName()
|
||||
{
|
||||
return "Linux";
|
||||
}
|
||||
|
||||
bool SystemStats::isOperatingSystem64Bit()
|
||||
{
|
||||
#if JUCE_64BIT
|
||||
return true;
|
||||
#else
|
||||
//xxx not sure how to find this out?..
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
String SystemStats::getDeviceDescription()
|
||||
{
|
||||
#if JUCE_BSD
|
||||
int mib[] = {
|
||||
CTL_HW,
|
||||
HW_MACHINE
|
||||
};
|
||||
size_t machineDescriptionLength = 0;
|
||||
auto result = sysctl (mib, numElementsInArray (mib), nullptr, &machineDescriptionLength, nullptr, 0);
|
||||
|
||||
if (result != 0 || machineDescriptionLength == 0)
|
||||
return {};
|
||||
|
||||
MemoryBlock machineDescription { machineDescriptionLength };
|
||||
result = sysctl (mib, numElementsInArray (mib), machineDescription.getData(), &machineDescriptionLength, nullptr, 0);
|
||||
return String::fromUTF8 (result == 0 ? (char*) machineDescription.getData() : "");
|
||||
#else
|
||||
return getCpuInfo ("Hardware");
|
||||
#endif
|
||||
}
|
||||
|
||||
String SystemStats::getDeviceManufacturer()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
String SystemStats::getCpuVendor()
|
||||
{
|
||||
#if JUCE_BSD
|
||||
return {};
|
||||
#else
|
||||
auto v = getCpuInfo ("vendor_id");
|
||||
|
||||
if (v.isEmpty())
|
||||
v = getCpuInfo ("model name");
|
||||
|
||||
return v;
|
||||
#endif
|
||||
}
|
||||
|
||||
String SystemStats::getCpuModel()
|
||||
{
|
||||
#if JUCE_BSD
|
||||
int mib[] = {
|
||||
CTL_HW,
|
||||
HW_MODEL
|
||||
};
|
||||
size_t modelLength = 0;
|
||||
auto result = sysctl (mib, numElementsInArray (mib), nullptr, &modelLength, nullptr, 0);
|
||||
|
||||
if (result != 0 || modelLength == 0)
|
||||
return {};
|
||||
|
||||
MemoryBlock model { modelLength };
|
||||
result = sysctl (mib, numElementsInArray (mib), model.getData(), &modelLength, nullptr, 0);
|
||||
return String::fromUTF8 (result == 0 ? (char*) model.getData() : "");
|
||||
#else
|
||||
return getCpuInfo ("model name");
|
||||
#endif
|
||||
}
|
||||
|
||||
int SystemStats::getCpuSpeedInMegahertz()
|
||||
{
|
||||
#if JUCE_BSD
|
||||
int32 clockRate = 0;
|
||||
auto clockRateSize = sizeof (clockRate);
|
||||
auto result = sysctlbyname ("hw.clockrate", &clockRate, &clockRateSize, nullptr, 0);
|
||||
return result == 0 ? clockRate : 0;
|
||||
#else
|
||||
return roundToInt (getCpuInfo ("cpu MHz").getFloatValue());
|
||||
#endif
|
||||
}
|
||||
|
||||
int SystemStats::getMemorySizeInMegabytes()
|
||||
{
|
||||
#if JUCE_BSD
|
||||
int mib[] = {
|
||||
CTL_HW,
|
||||
HW_PHYSMEM
|
||||
};
|
||||
int64 memory = 0;
|
||||
auto memorySize = sizeof (memory);
|
||||
auto result = sysctl (mib, numElementsInArray (mib), &memory, &memorySize, nullptr, 0);
|
||||
return result == 0 ? (int) (memory / 1e6) : 0;
|
||||
#else
|
||||
struct sysinfo sysi;
|
||||
|
||||
if (sysinfo (&sysi) == 0)
|
||||
return (int) (sysi.totalram * sysi.mem_unit / (1024 * 1024));
|
||||
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
int SystemStats::getPageSize()
|
||||
{
|
||||
return (int) sysconf (_SC_PAGESIZE);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
String SystemStats::getLogonName()
|
||||
{
|
||||
if (auto user = getenv ("USER"))
|
||||
return String::fromUTF8 (user);
|
||||
|
||||
if (auto pw = getpwuid (getuid()))
|
||||
return String::fromUTF8 (pw->pw_name);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
String SystemStats::getFullUserName()
|
||||
{
|
||||
return getLogonName();
|
||||
}
|
||||
|
||||
String SystemStats::getComputerName()
|
||||
{
|
||||
char name[256] = {};
|
||||
|
||||
if (gethostname (name, sizeof (name) - 1) == 0)
|
||||
return name;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
String SystemStats::getUserLanguage()
|
||||
{
|
||||
#if JUCE_BSD
|
||||
if (auto langEnv = getenv ("LANG"))
|
||||
return String::fromUTF8 (langEnv).upToLastOccurrenceOf (".UTF-8", false, true);
|
||||
|
||||
return {};
|
||||
#else
|
||||
return getLocaleValue (_NL_IDENTIFICATION_LANGUAGE);
|
||||
#endif
|
||||
}
|
||||
|
||||
String SystemStats::getUserRegion()
|
||||
{
|
||||
#if JUCE_BSD
|
||||
return {};
|
||||
#else
|
||||
return getLocaleValue (_NL_IDENTIFICATION_TERRITORY);
|
||||
#endif
|
||||
}
|
||||
|
||||
String SystemStats::getDisplayLanguage()
|
||||
{
|
||||
auto result = getUserLanguage();
|
||||
auto region = getUserRegion();
|
||||
|
||||
if (region.isNotEmpty())
|
||||
result << "-" << region;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void CPUInformation::initialise() noexcept
|
||||
{
|
||||
#if JUCE_BSD
|
||||
#if JUCE_INTEL && ! JUCE_NO_INLINE_ASM
|
||||
SystemStatsHelpers::getCPUInfo (hasMMX,
|
||||
hasSSE,
|
||||
hasSSE2,
|
||||
has3DNow,
|
||||
hasSSE3,
|
||||
hasSSSE3,
|
||||
hasFMA3,
|
||||
hasSSE41,
|
||||
hasSSE42,
|
||||
hasAVX,
|
||||
hasFMA4,
|
||||
hasAVX2,
|
||||
hasAVX512F,
|
||||
hasAVX512DQ,
|
||||
hasAVX512IFMA,
|
||||
hasAVX512PF,
|
||||
hasAVX512ER,
|
||||
hasAVX512CD,
|
||||
hasAVX512BW,
|
||||
hasAVX512VL,
|
||||
hasAVX512VBMI,
|
||||
hasAVX512VPOPCNTDQ);
|
||||
#endif
|
||||
|
||||
numLogicalCPUs = numPhysicalCPUs = []
|
||||
{
|
||||
int mib[] = {
|
||||
CTL_HW,
|
||||
HW_NCPU
|
||||
};
|
||||
int32 numCPUs = 1;
|
||||
auto numCPUsSize = sizeof (numCPUs);
|
||||
auto result = sysctl (mib, numElementsInArray (mib), &numCPUs, &numCPUsSize, nullptr, 0);
|
||||
return result == 0 ? numCPUs : 1;
|
||||
}();
|
||||
#else
|
||||
auto flags = getCpuInfo ("flags");
|
||||
|
||||
hasMMX = flags.contains ("mmx");
|
||||
hasFMA3 = flags.contains ("fma");
|
||||
hasFMA4 = flags.contains ("fma4");
|
||||
hasSSE = flags.contains ("sse");
|
||||
hasSSE2 = flags.contains ("sse2");
|
||||
hasSSE3 = flags.contains ("sse3");
|
||||
has3DNow = flags.contains ("3dnow");
|
||||
hasSSSE3 = flags.contains ("ssse3");
|
||||
hasSSE41 = flags.contains ("sse4_1");
|
||||
hasSSE42 = flags.contains ("sse4_2");
|
||||
hasAVX = flags.contains ("avx");
|
||||
hasAVX2 = flags.contains ("avx2");
|
||||
hasAVX512F = flags.contains ("avx512f");
|
||||
hasAVX512BW = flags.contains ("avx512bw");
|
||||
hasAVX512CD = flags.contains ("avx512cd");
|
||||
hasAVX512DQ = flags.contains ("avx512dq");
|
||||
hasAVX512ER = flags.contains ("avx512er");
|
||||
hasAVX512IFMA = flags.contains ("avx512ifma");
|
||||
hasAVX512PF = flags.contains ("avx512pf");
|
||||
hasAVX512VBMI = flags.contains ("avx512vbmi");
|
||||
hasAVX512VL = flags.contains ("avx512vl");
|
||||
hasAVX512VPOPCNTDQ = flags.contains ("avx512_vpopcntdq");
|
||||
|
||||
numLogicalCPUs = getCpuInfo ("processor").getIntValue() + 1;
|
||||
|
||||
// Assume CPUs in all sockets have the same number of cores
|
||||
numPhysicalCPUs = getCpuInfo ("cpu cores").getIntValue() * (getCpuInfo ("physical id").getIntValue() + 1);
|
||||
|
||||
if (numPhysicalCPUs <= 0)
|
||||
numPhysicalCPUs = numLogicalCPUs;
|
||||
#endif
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
uint32 juce_millisecondsSinceStartup() noexcept
|
||||
{
|
||||
return (uint32) (Time::getHighResolutionTicks() / 1000);
|
||||
}
|
||||
|
||||
int64 Time::getHighResolutionTicks() noexcept
|
||||
{
|
||||
timespec t;
|
||||
|
||||
#if JUCE_BELA
|
||||
if (cobalt_thread_mode() == 0x200 /*XNRELAX*/)
|
||||
clock_gettime (CLOCK_MONOTONIC, &t);
|
||||
else
|
||||
__wrap_clock_gettime (CLOCK_MONOTONIC, &t);
|
||||
#else
|
||||
clock_gettime (CLOCK_MONOTONIC, &t);
|
||||
#endif
|
||||
|
||||
return (t.tv_sec * (int64) 1000000) + (t.tv_nsec / 1000);
|
||||
}
|
||||
|
||||
int64 Time::getHighResolutionTicksPerSecond() noexcept
|
||||
{
|
||||
return 1000000; // (microseconds)
|
||||
}
|
||||
|
||||
double Time::getMillisecondCounterHiRes() noexcept
|
||||
{
|
||||
return (double) getHighResolutionTicks() * 0.001;
|
||||
}
|
||||
|
||||
bool Time::setSystemTimeToThisTime() const
|
||||
{
|
||||
timeval t;
|
||||
t.tv_sec = decltype (timeval::tv_sec) (millisSinceEpoch / 1000);
|
||||
t.tv_usec = decltype (timeval::tv_usec) ((millisSinceEpoch - t.tv_sec * 1000) * 1000);
|
||||
|
||||
return settimeofday (&t, nullptr) == 0;
|
||||
}
|
||||
|
||||
JUCE_API bool JUCE_CALLTYPE juce_isRunningUnderDebugger() noexcept
|
||||
{
|
||||
#if JUCE_BSD
|
||||
int mib[] =
|
||||
{
|
||||
CTL_KERN,
|
||||
KERN_PROC,
|
||||
KERN_PROC_PID,
|
||||
::getpid()
|
||||
};
|
||||
struct kinfo_proc info;
|
||||
auto infoSize = sizeof (info);
|
||||
auto result = sysctl (mib, numElementsInArray (mib), &info, &infoSize, nullptr, 0);
|
||||
return result == 0 ? ((info.ki_flag & P_TRACED) != 0) : false;
|
||||
#else
|
||||
return readPosixConfigFileValue ("/proc/self/status", "TracerPid").getIntValue() > 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace juce
|
62
deps/juce/modules/juce_core/native/juce_linux_Threads.cpp
vendored
Normal file
62
deps/juce/modules/juce_core/native/juce_linux_Threads.cpp
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
/*
|
||||
Note that a lot of methods that you'd expect to find in this file actually
|
||||
live in juce_posix_SharedCode.h!
|
||||
*/
|
||||
|
||||
//==============================================================================
|
||||
JUCE_API void JUCE_CALLTYPE Process::setPriority (const ProcessPriority prior)
|
||||
{
|
||||
auto policy = (prior <= NormalPriority) ? SCHED_OTHER : SCHED_RR;
|
||||
auto minp = sched_get_priority_min (policy);
|
||||
auto maxp = sched_get_priority_max (policy);
|
||||
|
||||
struct sched_param param;
|
||||
|
||||
switch (prior)
|
||||
{
|
||||
case LowPriority:
|
||||
case NormalPriority: param.sched_priority = 0; break;
|
||||
case HighPriority: param.sched_priority = minp + (maxp - minp) / 4; break;
|
||||
case RealtimePriority: param.sched_priority = minp + (3 * (maxp - minp) / 4); break;
|
||||
default: jassertfalse; break;
|
||||
}
|
||||
|
||||
pthread_setschedparam (pthread_self(), policy, ¶m);
|
||||
}
|
||||
|
||||
static bool swapUserAndEffectiveUser()
|
||||
{
|
||||
auto result1 = setreuid (geteuid(), getuid());
|
||||
auto result2 = setregid (getegid(), getgid());
|
||||
return result1 == 0 && result2 == 0;
|
||||
}
|
||||
|
||||
JUCE_API void JUCE_CALLTYPE Process::raisePrivilege() { if (geteuid() != 0 && getuid() == 0) swapUserAndEffectiveUser(); }
|
||||
JUCE_API void JUCE_CALLTYPE Process::lowerPrivilege() { if (geteuid() == 0 && getuid() != 0) swapUserAndEffectiveUser(); }
|
||||
|
||||
} // namespace juce
|
64
deps/juce/modules/juce_core/native/juce_mac_CFHelpers.h
vendored
Normal file
64
deps/juce/modules/juce_core/native/juce_mac_CFHelpers.h
vendored
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
/* This file contains a few helper functions that are used internally but which
|
||||
need to be kept away from the public headers because they use obj-C symbols.
|
||||
*/
|
||||
namespace juce
|
||||
{
|
||||
|
||||
template <typename CFType>
|
||||
struct CFObjectDeleter
|
||||
{
|
||||
void operator() (CFType object) const noexcept
|
||||
{
|
||||
if (object != nullptr)
|
||||
CFRelease (object);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename CFType>
|
||||
using CFUniquePtr = std::unique_ptr<typename std::remove_pointer<CFType>::type, CFObjectDeleter<CFType>>;
|
||||
|
||||
template <typename CFType>
|
||||
struct CFObjectHolder
|
||||
{
|
||||
CFObjectHolder() = default;
|
||||
explicit CFObjectHolder (CFType obj) : object (obj) {}
|
||||
|
||||
CFObjectHolder (const CFObjectHolder&) = delete;
|
||||
CFObjectHolder (CFObjectHolder&&) = delete;
|
||||
|
||||
CFObjectHolder& operator= (const CFObjectHolder&) = delete;
|
||||
CFObjectHolder& operator= (CFObjectHolder&&) = delete;
|
||||
|
||||
~CFObjectHolder() noexcept
|
||||
{
|
||||
if (object != nullptr)
|
||||
CFRelease (object);
|
||||
}
|
||||
|
||||
// Public to facilitate passing the pointer address to functions
|
||||
CFType object = nullptr;
|
||||
};
|
||||
|
||||
} // namespace juce
|
521
deps/juce/modules/juce_core/native/juce_mac_Files.mm
vendored
Normal file
521
deps/juce/modules/juce_core/native/juce_mac_Files.mm
vendored
Normal file
@ -0,0 +1,521 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
/*
|
||||
Note that a lot of methods that you'd expect to find in this file actually
|
||||
live in juce_posix_SharedCode.h!
|
||||
*/
|
||||
|
||||
//==============================================================================
|
||||
bool File::copyInternal (const File& dest) const
|
||||
{
|
||||
JUCE_AUTORELEASEPOOL
|
||||
{
|
||||
NSFileManager* fm = [NSFileManager defaultManager];
|
||||
|
||||
return [fm fileExistsAtPath: juceStringToNS (fullPath)]
|
||||
&& [fm copyItemAtPath: juceStringToNS (fullPath)
|
||||
toPath: juceStringToNS (dest.getFullPathName())
|
||||
error: nil];
|
||||
}
|
||||
}
|
||||
|
||||
void File::findFileSystemRoots (Array<File>& destArray)
|
||||
{
|
||||
destArray.add (File ("/"));
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
namespace MacFileHelpers
|
||||
{
|
||||
static bool isFileOnDriveType (const File& f, const char* const* types)
|
||||
{
|
||||
struct statfs buf;
|
||||
|
||||
if (juce_doStatFS (f, buf))
|
||||
{
|
||||
const String type (buf.f_fstypename);
|
||||
|
||||
while (*types != nullptr)
|
||||
if (type.equalsIgnoreCase (*types++))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool isHiddenFile (const String& path)
|
||||
{
|
||||
#if JUCE_MAC
|
||||
JUCE_AUTORELEASEPOOL
|
||||
{
|
||||
NSNumber* hidden = nil;
|
||||
NSError* err = nil;
|
||||
|
||||
return [createNSURLFromFile (path) getResourceValue: &hidden forKey: NSURLIsHiddenKey error: &err]
|
||||
&& [hidden boolValue];
|
||||
}
|
||||
#else
|
||||
return File (path).getFileName().startsWithChar ('.');
|
||||
#endif
|
||||
}
|
||||
|
||||
#if JUCE_IOS
|
||||
static String getIOSSystemLocation (NSSearchPathDirectory type)
|
||||
{
|
||||
return nsStringToJuce ([NSSearchPathForDirectoriesInDomains (type, NSUserDomainMask, YES)
|
||||
objectAtIndex: 0]);
|
||||
}
|
||||
#else
|
||||
static bool launchExecutable (const String& pathAndArguments)
|
||||
{
|
||||
auto cpid = fork();
|
||||
|
||||
if (cpid == 0)
|
||||
{
|
||||
const char* const argv[4] = { "/bin/sh", "-c", pathAndArguments.toUTF8(), nullptr };
|
||||
|
||||
// Child process
|
||||
if (execve (argv[0], (char**) argv, nullptr) < 0)
|
||||
exit (0);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (cpid < 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool File::isOnCDRomDrive() const
|
||||
{
|
||||
static const char* const cdTypes[] = { "cd9660", "cdfs", "cddafs", "udf", nullptr };
|
||||
|
||||
return MacFileHelpers::isFileOnDriveType (*this, cdTypes);
|
||||
}
|
||||
|
||||
bool File::isOnHardDisk() const
|
||||
{
|
||||
static const char* const nonHDTypes[] = { "nfs", "smbfs", "ramfs", nullptr };
|
||||
|
||||
return ! (isOnCDRomDrive() || MacFileHelpers::isFileOnDriveType (*this, nonHDTypes));
|
||||
}
|
||||
|
||||
bool File::isOnRemovableDrive() const
|
||||
{
|
||||
#if JUCE_IOS
|
||||
return false; // xxx is this possible?
|
||||
#else
|
||||
JUCE_AUTORELEASEPOOL
|
||||
{
|
||||
BOOL removable = false;
|
||||
|
||||
[[NSWorkspace sharedWorkspace]
|
||||
getFileSystemInfoForPath: juceStringToNS (getFullPathName())
|
||||
isRemovable: &removable
|
||||
isWritable: nil
|
||||
isUnmountable: nil
|
||||
description: nil
|
||||
type: nil];
|
||||
|
||||
return removable;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool File::isHidden() const
|
||||
{
|
||||
return MacFileHelpers::isHiddenFile (getFullPathName());
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
const char* const* juce_argv = nullptr;
|
||||
int juce_argc = 0;
|
||||
|
||||
File File::getSpecialLocation (const SpecialLocationType type)
|
||||
{
|
||||
JUCE_AUTORELEASEPOOL
|
||||
{
|
||||
String resultPath;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case userHomeDirectory: resultPath = nsStringToJuce (NSHomeDirectory()); break;
|
||||
|
||||
#if JUCE_IOS
|
||||
case userDocumentsDirectory: resultPath = MacFileHelpers::getIOSSystemLocation (NSDocumentDirectory); break;
|
||||
case userDesktopDirectory: resultPath = MacFileHelpers::getIOSSystemLocation (NSDesktopDirectory); break;
|
||||
|
||||
case tempDirectory:
|
||||
{
|
||||
File tmp (MacFileHelpers::getIOSSystemLocation (NSCachesDirectory));
|
||||
tmp = tmp.getChildFile (juce_getExecutableFile().getFileNameWithoutExtension());
|
||||
tmp.createDirectory();
|
||||
return tmp.getFullPathName();
|
||||
}
|
||||
|
||||
#else
|
||||
case userDocumentsDirectory: resultPath = "~/Documents"; break;
|
||||
case userDesktopDirectory: resultPath = "~/Desktop"; break;
|
||||
|
||||
case tempDirectory:
|
||||
{
|
||||
File tmp ("~/Library/Caches/" + juce_getExecutableFile().getFileNameWithoutExtension());
|
||||
tmp.createDirectory();
|
||||
return File (tmp.getFullPathName());
|
||||
}
|
||||
#endif
|
||||
case userMusicDirectory: resultPath = "~/Music"; break;
|
||||
case userMoviesDirectory: resultPath = "~/Movies"; break;
|
||||
case userPicturesDirectory: resultPath = "~/Pictures"; break;
|
||||
case userApplicationDataDirectory: resultPath = "~/Library"; break;
|
||||
case commonApplicationDataDirectory: resultPath = "/Library"; break;
|
||||
case commonDocumentsDirectory: resultPath = "/Users/Shared"; break;
|
||||
case globalApplicationsDirectory: resultPath = "/Applications"; break;
|
||||
|
||||
case invokedExecutableFile:
|
||||
if (juce_argv != nullptr && juce_argc > 0)
|
||||
return File::getCurrentWorkingDirectory().getChildFile (CharPointer_UTF8 (juce_argv[0]));
|
||||
// deliberate fall-through...
|
||||
JUCE_FALLTHROUGH
|
||||
|
||||
case currentExecutableFile:
|
||||
return juce_getExecutableFile();
|
||||
|
||||
case currentApplicationFile:
|
||||
{
|
||||
const File exe (juce_getExecutableFile());
|
||||
const File parent (exe.getParentDirectory());
|
||||
|
||||
#if JUCE_IOS
|
||||
return parent;
|
||||
#else
|
||||
return parent.getFullPathName().endsWithIgnoreCase ("Contents/MacOS")
|
||||
? parent.getParentDirectory().getParentDirectory()
|
||||
: exe;
|
||||
#endif
|
||||
}
|
||||
|
||||
case hostApplicationPath:
|
||||
{
|
||||
unsigned int size = 8192;
|
||||
HeapBlock<char> buffer;
|
||||
buffer.calloc (size + 8);
|
||||
|
||||
_NSGetExecutablePath (buffer.get(), &size);
|
||||
return File (String::fromUTF8 (buffer, (int) size));
|
||||
}
|
||||
|
||||
default:
|
||||
jassertfalse; // unknown type?
|
||||
break;
|
||||
}
|
||||
|
||||
if (resultPath.isNotEmpty())
|
||||
return File (resultPath.convertToPrecomposedUnicode());
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
String File::getVersion() const
|
||||
{
|
||||
JUCE_AUTORELEASEPOOL
|
||||
{
|
||||
if (NSBundle* bundle = [NSBundle bundleWithPath: juceStringToNS (getFullPathName())])
|
||||
if (NSDictionary* info = [bundle infoDictionary])
|
||||
if (NSString* name = [info valueForKey: nsStringLiteral ("CFBundleShortVersionString")])
|
||||
return nsStringToJuce (name);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static NSString* getFileLink (const String& path)
|
||||
{
|
||||
return [[NSFileManager defaultManager] destinationOfSymbolicLinkAtPath: juceStringToNS (path) error: nil];
|
||||
}
|
||||
|
||||
bool File::isSymbolicLink() const
|
||||
{
|
||||
return getFileLink (fullPath) != nil;
|
||||
}
|
||||
|
||||
String File::getNativeLinkedTarget() const
|
||||
{
|
||||
if (NSString* dest = getFileLink (fullPath))
|
||||
return nsStringToJuce (dest);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool File::moveToTrash() const
|
||||
{
|
||||
if (! exists())
|
||||
return true;
|
||||
|
||||
JUCE_AUTORELEASEPOOL
|
||||
{
|
||||
#if JUCE_MAC || (JUCE_IOS && (defined (__IPHONE_11_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_11_0))
|
||||
if (@available (macOS 10.8, iOS 11.0, *))
|
||||
{
|
||||
NSError* error = nil;
|
||||
return [[NSFileManager defaultManager] trashItemAtURL: createNSURLFromFile (*this)
|
||||
resultingItemURL: nil
|
||||
error: &error];
|
||||
}
|
||||
#endif
|
||||
|
||||
#if JUCE_IOS
|
||||
return deleteFile();
|
||||
#else
|
||||
[[NSWorkspace sharedWorkspace] recycleURLs: [NSArray arrayWithObject: createNSURLFromFile (*this)]
|
||||
completionHandler: nil];
|
||||
|
||||
// recycleURLs is async, so we need to block until it has finished. We can't use a
|
||||
// run-loop here because it'd dispatch unexpected messages, so have to do this very
|
||||
// nasty bodge. But this is only needed for support of pre-10.8 versions.
|
||||
for (int retries = 100; --retries >= 0;)
|
||||
{
|
||||
if (! exists())
|
||||
return true;
|
||||
|
||||
Thread::sleep (5);
|
||||
}
|
||||
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class DirectoryIterator::NativeIterator::Pimpl
|
||||
{
|
||||
public:
|
||||
Pimpl (const File& directory, const String& wildcard)
|
||||
: parentDir (File::addTrailingSeparator (directory.getFullPathName())),
|
||||
wildCard (wildcard)
|
||||
{
|
||||
JUCE_AUTORELEASEPOOL
|
||||
{
|
||||
enumerator = [[[NSFileManager defaultManager] enumeratorAtPath: juceStringToNS (directory.getFullPathName())] retain];
|
||||
}
|
||||
}
|
||||
|
||||
~Pimpl()
|
||||
{
|
||||
[enumerator release];
|
||||
}
|
||||
|
||||
bool next (String& filenameFound,
|
||||
bool* const isDir, bool* const isHidden, int64* const fileSize,
|
||||
Time* const modTime, Time* const creationTime, bool* const isReadOnly)
|
||||
{
|
||||
JUCE_AUTORELEASEPOOL
|
||||
{
|
||||
const char* wildcardUTF8 = nullptr;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (enumerator == nil)
|
||||
return false;
|
||||
|
||||
NSString* file = [enumerator nextObject];
|
||||
|
||||
if (file == nil)
|
||||
return false;
|
||||
|
||||
[enumerator skipDescendents];
|
||||
filenameFound = nsStringToJuce (file).convertToPrecomposedUnicode();
|
||||
|
||||
if (wildcardUTF8 == nullptr)
|
||||
wildcardUTF8 = wildCard.toUTF8();
|
||||
|
||||
if (fnmatch (wildcardUTF8, filenameFound.toUTF8(), FNM_CASEFOLD) != 0)
|
||||
continue;
|
||||
|
||||
auto fullPath = parentDir + filenameFound;
|
||||
updateStatInfoForFile (fullPath, isDir, fileSize, modTime, creationTime, isReadOnly);
|
||||
|
||||
if (isHidden != nullptr)
|
||||
*isHidden = MacFileHelpers::isHiddenFile (fullPath);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
String parentDir, wildCard;
|
||||
NSDirectoryEnumerator* enumerator = nil;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (Pimpl)
|
||||
};
|
||||
|
||||
DirectoryIterator::NativeIterator::NativeIterator (const File& directory, const String& wildcard)
|
||||
: pimpl (new DirectoryIterator::NativeIterator::Pimpl (directory, wildcard))
|
||||
{
|
||||
}
|
||||
|
||||
DirectoryIterator::NativeIterator::~NativeIterator()
|
||||
{
|
||||
}
|
||||
|
||||
bool DirectoryIterator::NativeIterator::next (String& filenameFound,
|
||||
bool* const isDir, bool* const isHidden, int64* const fileSize,
|
||||
Time* const modTime, Time* const creationTime, bool* const isReadOnly)
|
||||
{
|
||||
return pimpl->next (filenameFound, isDir, isHidden, fileSize, modTime, creationTime, isReadOnly);
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
bool JUCE_CALLTYPE Process::openDocument (const String& fileName, const String& parameters)
|
||||
{
|
||||
JUCE_AUTORELEASEPOOL
|
||||
{
|
||||
NSString* fileNameAsNS (juceStringToNS (fileName));
|
||||
NSURL* filenameAsURL = File::createFileWithoutCheckingPath (fileName).exists() ? [NSURL fileURLWithPath: fileNameAsNS]
|
||||
: [NSURL URLWithString: fileNameAsNS];
|
||||
|
||||
#if JUCE_IOS
|
||||
ignoreUnused (parameters);
|
||||
|
||||
#if (! defined __IPHONE_OS_VERSION_MIN_REQUIRED) || (! defined __IPHONE_10_0) || (__IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_10_0)
|
||||
return [[UIApplication sharedApplication] openURL: filenameAsURL];
|
||||
#else
|
||||
[[UIApplication sharedApplication] openURL: filenameAsURL options: @{} completionHandler: nil];
|
||||
return true;
|
||||
#endif
|
||||
#else
|
||||
NSWorkspace* workspace = [NSWorkspace sharedWorkspace];
|
||||
|
||||
if (parameters.isEmpty())
|
||||
return [workspace openURL: filenameAsURL];
|
||||
|
||||
const File file (fileName);
|
||||
|
||||
if (file.isBundle())
|
||||
{
|
||||
StringArray params;
|
||||
params.addTokens (parameters, true);
|
||||
|
||||
NSMutableArray* paramArray = [[NSMutableArray new] autorelease];
|
||||
|
||||
for (int i = 0; i < params.size(); ++i)
|
||||
[paramArray addObject: juceStringToNS (params[i])];
|
||||
|
||||
#if (defined MAC_OS_X_VERSION_10_15) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_15
|
||||
auto config = [NSWorkspaceOpenConfiguration configuration];
|
||||
[config setCreatesNewApplicationInstance: YES];
|
||||
config.arguments = paramArray;
|
||||
|
||||
[workspace openApplicationAtURL: filenameAsURL
|
||||
configuration: config
|
||||
completionHandler: nil];
|
||||
return true;
|
||||
#else
|
||||
NSMutableDictionary* dict = [[NSMutableDictionary new] autorelease];
|
||||
|
||||
[dict setObject: paramArray
|
||||
forKey: nsStringLiteral ("NSWorkspaceLaunchConfigurationArguments")];
|
||||
|
||||
return [workspace launchApplicationAtURL: filenameAsURL
|
||||
options: NSWorkspaceLaunchDefault | NSWorkspaceLaunchNewInstance
|
||||
configuration: dict
|
||||
error: nil];
|
||||
#endif
|
||||
}
|
||||
|
||||
if (file.exists())
|
||||
return MacFileHelpers::launchExecutable ("\"" + fileName + "\" " + parameters);
|
||||
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void File::revealToUser() const
|
||||
{
|
||||
#if ! JUCE_IOS
|
||||
if (exists())
|
||||
[[NSWorkspace sharedWorkspace] selectFile: juceStringToNS (getFullPathName()) inFileViewerRootedAtPath: nsEmptyString()];
|
||||
else if (getParentDirectory().exists())
|
||||
getParentDirectory().revealToUser();
|
||||
#endif
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
OSType File::getMacOSType() const
|
||||
{
|
||||
JUCE_AUTORELEASEPOOL
|
||||
{
|
||||
NSDictionary* fileDict = [[NSFileManager defaultManager] attributesOfItemAtPath: juceStringToNS (getFullPathName()) error: nil];
|
||||
return [fileDict fileHFSTypeCode];
|
||||
}
|
||||
}
|
||||
|
||||
bool File::isBundle() const
|
||||
{
|
||||
#if JUCE_IOS
|
||||
return false; // xxx can't find a sensible way to do this without trying to open the bundle..
|
||||
#else
|
||||
JUCE_AUTORELEASEPOOL
|
||||
{
|
||||
return [[NSWorkspace sharedWorkspace] isFilePackageAtPath: juceStringToNS (getFullPathName())];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if JUCE_MAC
|
||||
void File::addToDock() const
|
||||
{
|
||||
// check that it's not already there...
|
||||
if (! juce_getOutputFromCommand ("defaults read com.apple.dock persistent-apps").containsIgnoreCase (getFullPathName()))
|
||||
{
|
||||
juce_runSystemCommand ("defaults write com.apple.dock persistent-apps -array-add \"<dict><key>tile-data</key><dict><key>file-data</key><dict><key>_CFURLString</key><string>"
|
||||
+ getFullPathName() + "</string><key>_CFURLStringType</key><integer>0</integer></dict></dict></dict>\"");
|
||||
|
||||
juce_runSystemCommand ("osascript -e \"tell application \\\"Dock\\\" to quit\"");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
File File::getContainerForSecurityApplicationGroupIdentifier (const String& appGroup)
|
||||
{
|
||||
if (auto* url = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier: juceStringToNS (appGroup)])
|
||||
return File (nsStringToJuce ([url path]));
|
||||
|
||||
return File();
|
||||
}
|
||||
|
||||
} // namespace juce
|
1164
deps/juce/modules/juce_core/native/juce_mac_Network.mm
vendored
Normal file
1164
deps/juce/modules/juce_core/native/juce_mac_Network.mm
vendored
Normal file
File diff suppressed because it is too large
Load Diff
436
deps/juce/modules/juce_core/native/juce_mac_ObjCHelpers.h
vendored
Normal file
436
deps/juce/modules/juce_core/native/juce_mac_ObjCHelpers.h
vendored
Normal file
@ -0,0 +1,436 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "juce_mac_CFHelpers.h"
|
||||
|
||||
/* This file contains a few helper functions that are used internally but which
|
||||
need to be kept away from the public headers because they use obj-C symbols.
|
||||
*/
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
inline Range<int> nsRangeToJuce (NSRange range)
|
||||
{
|
||||
return { (int) range.location, (int) (range.location + range.length) };
|
||||
}
|
||||
|
||||
inline NSRange juceRangeToNS (Range<int> range)
|
||||
{
|
||||
return NSMakeRange ((NSUInteger) range.getStart(), (NSUInteger) range.getLength());
|
||||
}
|
||||
|
||||
inline String nsStringToJuce (NSString* s)
|
||||
{
|
||||
return CharPointer_UTF8 ([s UTF8String]);
|
||||
}
|
||||
|
||||
inline NSString* juceStringToNS (const String& s)
|
||||
{
|
||||
return [NSString stringWithUTF8String: s.toUTF8()];
|
||||
}
|
||||
|
||||
inline NSString* nsStringLiteral (const char* const s) noexcept
|
||||
{
|
||||
return [NSString stringWithUTF8String: s];
|
||||
}
|
||||
|
||||
inline NSString* nsEmptyString() noexcept
|
||||
{
|
||||
return [NSString string];
|
||||
}
|
||||
|
||||
inline NSURL* createNSURLFromFile (const String& f)
|
||||
{
|
||||
return [NSURL fileURLWithPath: juceStringToNS (f)];
|
||||
}
|
||||
|
||||
inline NSURL* createNSURLFromFile (const File& f)
|
||||
{
|
||||
return createNSURLFromFile (f.getFullPathName());
|
||||
}
|
||||
|
||||
inline NSArray* createNSArrayFromStringArray (const StringArray& strings)
|
||||
{
|
||||
auto array = [[NSMutableArray alloc] init];
|
||||
|
||||
for (auto string: strings)
|
||||
[array addObject:juceStringToNS (string)];
|
||||
|
||||
return [array autorelease];
|
||||
}
|
||||
|
||||
inline NSArray* varArrayToNSArray (const var& varToParse);
|
||||
|
||||
inline NSDictionary* varObjectToNSDictionary (const var& varToParse)
|
||||
{
|
||||
auto dictionary = [NSMutableDictionary dictionary];
|
||||
|
||||
if (varToParse.isObject())
|
||||
{
|
||||
auto* dynamicObject = varToParse.getDynamicObject();
|
||||
|
||||
auto& properties = dynamicObject->getProperties();
|
||||
|
||||
for (int i = 0; i < properties.size(); ++i)
|
||||
{
|
||||
auto* keyString = juceStringToNS (properties.getName (i).toString());
|
||||
|
||||
const var& valueVar = properties.getValueAt (i);
|
||||
|
||||
if (valueVar.isObject())
|
||||
{
|
||||
auto* valueDictionary = varObjectToNSDictionary (valueVar);
|
||||
|
||||
[dictionary setObject: valueDictionary forKey: keyString];
|
||||
}
|
||||
else if (valueVar.isArray())
|
||||
{
|
||||
auto* valueArray = varArrayToNSArray (valueVar);
|
||||
|
||||
[dictionary setObject: valueArray forKey: keyString];
|
||||
}
|
||||
else
|
||||
{
|
||||
auto* valueString = juceStringToNS (valueVar.toString());
|
||||
|
||||
[dictionary setObject: valueString forKey: keyString];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
inline NSArray* varArrayToNSArray (const var& varToParse)
|
||||
{
|
||||
jassert (varToParse.isArray());
|
||||
|
||||
if (! varToParse.isArray())
|
||||
return nil;
|
||||
|
||||
const auto* varArray = varToParse.getArray();
|
||||
|
||||
auto array = [NSMutableArray arrayWithCapacity: (NSUInteger) varArray->size()];
|
||||
|
||||
for (const auto& aVar : *varArray)
|
||||
{
|
||||
if (aVar.isObject())
|
||||
{
|
||||
auto* valueDictionary = varObjectToNSDictionary (aVar);
|
||||
|
||||
[array addObject: valueDictionary];
|
||||
}
|
||||
else if (aVar.isArray())
|
||||
{
|
||||
auto* valueArray = varArrayToNSArray (aVar);
|
||||
|
||||
[array addObject: valueArray];
|
||||
}
|
||||
else
|
||||
{
|
||||
auto* valueString = juceStringToNS (aVar.toString());
|
||||
|
||||
[array addObject: valueString];
|
||||
}
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
var nsObjectToVar (NSObject* array);
|
||||
|
||||
inline var nsDictionaryToVar (NSDictionary* dictionary)
|
||||
{
|
||||
DynamicObject::Ptr dynamicObject (new DynamicObject());
|
||||
|
||||
for (NSString* key in dictionary)
|
||||
dynamicObject->setProperty (nsStringToJuce (key), nsObjectToVar (dictionary[key]));
|
||||
|
||||
return var (dynamicObject.get());
|
||||
}
|
||||
|
||||
inline var nsArrayToVar (NSArray* array)
|
||||
{
|
||||
Array<var> resultArray;
|
||||
|
||||
for (id value in array)
|
||||
resultArray.add (nsObjectToVar (value));
|
||||
|
||||
return var (resultArray);
|
||||
}
|
||||
|
||||
inline var nsObjectToVar (NSObject* obj)
|
||||
{
|
||||
if ([obj isKindOfClass: [NSString class]]) return nsStringToJuce ((NSString*) obj);
|
||||
else if ([obj isKindOfClass: [NSNumber class]]) return nsStringToJuce ([(NSNumber*) obj stringValue]);
|
||||
else if ([obj isKindOfClass: [NSDictionary class]]) return nsDictionaryToVar ((NSDictionary*) obj);
|
||||
else if ([obj isKindOfClass: [NSArray class]]) return nsArrayToVar ((NSArray*) obj);
|
||||
else
|
||||
{
|
||||
// Unsupported yet, add here!
|
||||
jassertfalse;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
#if JUCE_MAC
|
||||
template <typename RectangleType>
|
||||
NSRect makeNSRect (const RectangleType& r) noexcept
|
||||
{
|
||||
return NSMakeRect (static_cast<CGFloat> (r.getX()),
|
||||
static_cast<CGFloat> (r.getY()),
|
||||
static_cast<CGFloat> (r.getWidth()),
|
||||
static_cast<CGFloat> (r.getHeight()));
|
||||
}
|
||||
#endif
|
||||
|
||||
#if JUCE_INTEL
|
||||
template <typename T>
|
||||
struct NeedsStret
|
||||
{
|
||||
#if JUCE_32BIT
|
||||
static constexpr auto value = sizeof (T) > 8;
|
||||
#else
|
||||
static constexpr auto value = sizeof (T) > 16;
|
||||
#endif
|
||||
};
|
||||
|
||||
template <>
|
||||
struct NeedsStret<void> { static constexpr auto value = false; };
|
||||
|
||||
template <typename T, bool b = NeedsStret<T>::value>
|
||||
struct MetaSuperFn { static constexpr auto value = objc_msgSendSuper_stret; };
|
||||
|
||||
template <typename T>
|
||||
struct MetaSuperFn<T, false> { static constexpr auto value = objc_msgSendSuper; };
|
||||
#else
|
||||
template <typename>
|
||||
struct MetaSuperFn { static constexpr auto value = objc_msgSendSuper; };
|
||||
#endif
|
||||
|
||||
template <typename SuperType, typename ReturnType, typename... Params>
|
||||
inline ReturnType ObjCMsgSendSuper (id self, SEL sel, Params... params)
|
||||
{
|
||||
using SuperFn = ReturnType (*) (struct objc_super*, SEL, Params...);
|
||||
const auto fn = reinterpret_cast<SuperFn> (MetaSuperFn<ReturnType>::value);
|
||||
|
||||
objc_super s = { self, [SuperType class] };
|
||||
return fn (&s, sel, params...);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
struct NSObjectDeleter
|
||||
{
|
||||
void operator() (NSObject* object) const noexcept
|
||||
{
|
||||
if (object != nullptr)
|
||||
[object release];
|
||||
}
|
||||
};
|
||||
|
||||
template <typename NSType>
|
||||
using NSUniquePtr = std::unique_ptr<NSType, NSObjectDeleter>;
|
||||
|
||||
//==============================================================================
|
||||
template <typename Type>
|
||||
inline Type getIvar (id self, const char* name)
|
||||
{
|
||||
void* v = nullptr;
|
||||
object_getInstanceVariable (self, name, &v);
|
||||
return static_cast<Type> (v);
|
||||
}
|
||||
|
||||
template <typename SuperclassType>
|
||||
struct ObjCClass
|
||||
{
|
||||
ObjCClass (const char* nameRoot)
|
||||
: cls (objc_allocateClassPair ([SuperclassType class], getRandomisedName (nameRoot).toUTF8(), 0))
|
||||
{
|
||||
}
|
||||
|
||||
~ObjCClass()
|
||||
{
|
||||
auto kvoSubclassName = String ("NSKVONotifying_") + class_getName (cls);
|
||||
|
||||
if (objc_getClass (kvoSubclassName.toUTF8()) == nullptr)
|
||||
objc_disposeClassPair (cls);
|
||||
}
|
||||
|
||||
void registerClass()
|
||||
{
|
||||
objc_registerClassPair (cls);
|
||||
}
|
||||
|
||||
SuperclassType* createInstance() const
|
||||
{
|
||||
return class_createInstance (cls, 0);
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
void addIvar (const char* name)
|
||||
{
|
||||
BOOL b = class_addIvar (cls, name, sizeof (Type), (uint8_t) rint (log2 (sizeof (Type))), @encode (Type));
|
||||
jassert (b); ignoreUnused (b);
|
||||
}
|
||||
|
||||
template <typename FunctionType>
|
||||
void addMethod (SEL selector, FunctionType callbackFn, const char* signature)
|
||||
{
|
||||
BOOL b = class_addMethod (cls, selector, (IMP) callbackFn, signature);
|
||||
jassert (b); ignoreUnused (b);
|
||||
}
|
||||
|
||||
template <typename FunctionType>
|
||||
void addMethod (SEL selector, FunctionType callbackFn, const char* sig1, const char* sig2)
|
||||
{
|
||||
addMethod (selector, callbackFn, (String (sig1) + sig2).toUTF8());
|
||||
}
|
||||
|
||||
template <typename FunctionType>
|
||||
void addMethod (SEL selector, FunctionType callbackFn, const char* sig1, const char* sig2, const char* sig3)
|
||||
{
|
||||
addMethod (selector, callbackFn, (String (sig1) + sig2 + sig3).toUTF8());
|
||||
}
|
||||
|
||||
template <typename FunctionType>
|
||||
void addMethod (SEL selector, FunctionType callbackFn, const char* sig1, const char* sig2, const char* sig3, const char* sig4)
|
||||
{
|
||||
addMethod (selector, callbackFn, (String (sig1) + sig2 + sig3 + sig4).toUTF8());
|
||||
}
|
||||
|
||||
void addProtocol (Protocol* protocol)
|
||||
{
|
||||
BOOL b = class_addProtocol (cls, protocol);
|
||||
jassert (b); ignoreUnused (b);
|
||||
}
|
||||
|
||||
template <typename ReturnType, typename... Params>
|
||||
static ReturnType sendSuperclassMessage (id self, SEL sel, Params... params)
|
||||
{
|
||||
return ObjCMsgSendSuper<SuperclassType, ReturnType, Params...> (self, sel, params...);
|
||||
}
|
||||
|
||||
Class cls;
|
||||
|
||||
private:
|
||||
static String getRandomisedName (const char* root)
|
||||
{
|
||||
return root + String::toHexString (juce::Random::getSystemRandom().nextInt64());
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (ObjCClass)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
#ifndef DOXYGEN
|
||||
template <class JuceClass>
|
||||
struct ObjCLifetimeManagedClass : public ObjCClass<NSObject>
|
||||
{
|
||||
ObjCLifetimeManagedClass()
|
||||
: ObjCClass<NSObject> ("ObjCLifetimeManagedClass_")
|
||||
{
|
||||
addIvar<JuceClass*> ("cppObject");
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wundeclared-selector")
|
||||
addMethod (@selector (initWithJuceObject:), initWithJuceObject, "@@:@");
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
|
||||
addMethod (@selector (dealloc), dealloc, "v@:");
|
||||
|
||||
registerClass();
|
||||
}
|
||||
|
||||
static id initWithJuceObject (id _self, SEL, JuceClass* obj)
|
||||
{
|
||||
NSObject* self = sendSuperclassMessage<NSObject*> (_self, @selector (init));
|
||||
object_setInstanceVariable (self, "cppObject", obj);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
static void dealloc (id _self, SEL)
|
||||
{
|
||||
if (auto* obj = getIvar<JuceClass*> (_self, "cppObject"))
|
||||
{
|
||||
delete obj;
|
||||
object_setInstanceVariable (_self, "cppObject", nullptr);
|
||||
}
|
||||
|
||||
sendSuperclassMessage<void> (_self, @selector (dealloc));
|
||||
}
|
||||
|
||||
static ObjCLifetimeManagedClass objCLifetimeManagedClass;
|
||||
};
|
||||
|
||||
template <typename Class>
|
||||
ObjCLifetimeManagedClass<Class> ObjCLifetimeManagedClass<Class>::objCLifetimeManagedClass;
|
||||
#endif
|
||||
|
||||
// this will return an NSObject which takes ownership of the JUCE instance passed-in
|
||||
// This is useful to tie the life-time of a juce instance to the life-time of an NSObject
|
||||
template <typename Class>
|
||||
NSObject* createNSObjectFromJuceClass (Class* obj)
|
||||
{
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wobjc-method-access")
|
||||
return [ObjCLifetimeManagedClass<Class>::objCLifetimeManagedClass.createInstance() initWithJuceObject:obj];
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
}
|
||||
|
||||
// Get the JUCE class instance that was tied to the life-time of an NSObject with the
|
||||
// function above
|
||||
template <typename Class>
|
||||
Class* getJuceClassFromNSObject (NSObject* obj)
|
||||
{
|
||||
return obj != nullptr ? getIvar<Class*> (obj, "cppObject") : nullptr;
|
||||
}
|
||||
|
||||
template <typename ReturnT, class Class, typename... Params>
|
||||
ReturnT (^CreateObjCBlock(Class* object, ReturnT (Class::*fn)(Params...))) (Params...)
|
||||
{
|
||||
__block Class* _this = object;
|
||||
__block ReturnT (Class::*_fn)(Params...) = fn;
|
||||
|
||||
return [[^ReturnT (Params... params) { return (_this->*_fn) (params...); } copy] autorelease];
|
||||
}
|
||||
|
||||
template <typename BlockType>
|
||||
class ObjCBlock
|
||||
{
|
||||
public:
|
||||
ObjCBlock() { block = nullptr; }
|
||||
template <typename R, class C, typename... P>
|
||||
ObjCBlock (C* _this, R (C::*fn)(P...)) : block (CreateObjCBlock (_this, fn)) {}
|
||||
ObjCBlock (BlockType b) : block ([b copy]) {}
|
||||
ObjCBlock& operator= (const BlockType& other) { if (block != nullptr) { [block release]; } block = [other copy]; return *this; }
|
||||
bool operator== (const void* ptr) const { return ((const void*) block == ptr); }
|
||||
bool operator!= (const void* ptr) const { return ((const void*) block != ptr); }
|
||||
~ObjCBlock() { if (block != nullptr) [block release]; }
|
||||
|
||||
operator BlockType() { return block; }
|
||||
|
||||
private:
|
||||
BlockType block;
|
||||
};
|
||||
|
||||
} // namespace juce
|
105
deps/juce/modules/juce_core/native/juce_mac_Strings.mm
vendored
Normal file
105
deps/juce/modules/juce_core/native/juce_mac_Strings.mm
vendored
Normal file
@ -0,0 +1,105 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
String String::fromCFString (CFStringRef cfString)
|
||||
{
|
||||
if (cfString == nullptr)
|
||||
return {};
|
||||
|
||||
CFRange range = { 0, CFStringGetLength (cfString) };
|
||||
CFIndex bytesNeeded = 0;
|
||||
CFStringGetBytes (cfString, range, kCFStringEncodingUTF8, 0, false, nullptr, 0, &bytesNeeded);
|
||||
|
||||
HeapBlock<UInt8> utf8 (bytesNeeded + 1);
|
||||
CFStringGetBytes (cfString, range, kCFStringEncodingUTF8, 0, false, utf8, bytesNeeded + 1, nullptr);
|
||||
|
||||
return String (CharPointer_UTF8 ((const CharPointer_UTF8::CharType*) utf8.get()),
|
||||
CharPointer_UTF8 ((const CharPointer_UTF8::CharType*) utf8.get() + bytesNeeded));
|
||||
}
|
||||
|
||||
CFStringRef String::toCFString() const
|
||||
{
|
||||
const char* const utf8 = toRawUTF8();
|
||||
|
||||
if (CFStringRef result = CFStringCreateWithBytes (kCFAllocatorDefault, (const UInt8*) utf8,
|
||||
(CFIndex) strlen (utf8), kCFStringEncodingUTF8, false))
|
||||
return result;
|
||||
|
||||
// If CFStringCreateWithBytes fails, it probably means there was a UTF8 format
|
||||
// error, so we'll return an empty string rather than a null pointer.
|
||||
return String().toCFString();
|
||||
}
|
||||
|
||||
String String::convertToPrecomposedUnicode() const
|
||||
{
|
||||
#if JUCE_IOS
|
||||
JUCE_AUTORELEASEPOOL
|
||||
{
|
||||
return nsStringToJuce ([juceStringToNS (*this) precomposedStringWithCanonicalMapping]);
|
||||
}
|
||||
#else
|
||||
UnicodeMapping map;
|
||||
|
||||
map.unicodeEncoding = CreateTextEncoding (kTextEncodingUnicodeDefault,
|
||||
kUnicodeNoSubset,
|
||||
kTextEncodingDefaultFormat);
|
||||
|
||||
map.otherEncoding = CreateTextEncoding (kTextEncodingUnicodeDefault,
|
||||
kUnicodeCanonicalCompVariant,
|
||||
kTextEncodingDefaultFormat);
|
||||
|
||||
map.mappingVersion = kUnicodeUseLatestMapping;
|
||||
|
||||
UnicodeToTextInfo conversionInfo = {};
|
||||
String result;
|
||||
|
||||
if (CreateUnicodeToTextInfo (&map, &conversionInfo) == noErr)
|
||||
{
|
||||
const size_t bytesNeeded = CharPointer_UTF16::getBytesRequiredFor (getCharPointer());
|
||||
|
||||
HeapBlock<char> tempOut;
|
||||
tempOut.calloc (bytesNeeded + 4);
|
||||
|
||||
ByteCount bytesRead = 0;
|
||||
ByteCount outputBufferSize = 0;
|
||||
|
||||
if (ConvertFromUnicodeToText (conversionInfo,
|
||||
bytesNeeded, (ConstUniCharArrayPtr) toUTF16().getAddress(),
|
||||
kUnicodeDefaultDirectionMask,
|
||||
0, {}, {}, {},
|
||||
bytesNeeded, &bytesRead,
|
||||
&outputBufferSize, tempOut) == noErr)
|
||||
{
|
||||
result = String (CharPointer_UTF16 (reinterpret_cast<CharPointer_UTF16::CharType*> (tempOut.get())));
|
||||
}
|
||||
|
||||
DisposeUnicodeToTextInfo (&conversionInfo);
|
||||
}
|
||||
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace juce
|
348
deps/juce/modules/juce_core/native/juce_mac_SystemStats.mm
vendored
Normal file
348
deps/juce/modules/juce_core/native/juce_mac_SystemStats.mm
vendored
Normal file
@ -0,0 +1,348 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
ScopedAutoReleasePool::ScopedAutoReleasePool()
|
||||
{
|
||||
pool = [[NSAutoreleasePool alloc] init];
|
||||
}
|
||||
|
||||
ScopedAutoReleasePool::~ScopedAutoReleasePool()
|
||||
{
|
||||
[((NSAutoreleasePool*) pool) release];
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void Logger::outputDebugString (const String& text)
|
||||
{
|
||||
// Would prefer to use std::cerr here, but avoiding it for
|
||||
// the moment, due to clang JIT linkage problems.
|
||||
fputs (text.toRawUTF8(), stderr);
|
||||
fputs ("\n", stderr);
|
||||
fflush (stderr);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void CPUInformation::initialise() noexcept
|
||||
{
|
||||
#if JUCE_INTEL && ! JUCE_NO_INLINE_ASM
|
||||
SystemStatsHelpers::getCPUInfo (hasMMX,
|
||||
hasSSE,
|
||||
hasSSE2,
|
||||
has3DNow,
|
||||
hasSSE3,
|
||||
hasSSSE3,
|
||||
hasFMA3,
|
||||
hasSSE41,
|
||||
hasSSE42,
|
||||
hasAVX,
|
||||
hasFMA4,
|
||||
hasAVX2,
|
||||
hasAVX512F,
|
||||
hasAVX512DQ,
|
||||
hasAVX512IFMA,
|
||||
hasAVX512PF,
|
||||
hasAVX512ER,
|
||||
hasAVX512CD,
|
||||
hasAVX512BW,
|
||||
hasAVX512VL,
|
||||
hasAVX512VBMI,
|
||||
hasAVX512VPOPCNTDQ);
|
||||
#endif
|
||||
|
||||
numLogicalCPUs = (int) [[NSProcessInfo processInfo] activeProcessorCount];
|
||||
|
||||
unsigned int physicalcpu = 0;
|
||||
size_t len = sizeof (physicalcpu);
|
||||
|
||||
if (sysctlbyname ("hw.physicalcpu", &physicalcpu, &len, nullptr, 0) >= 0)
|
||||
numPhysicalCPUs = (int) physicalcpu;
|
||||
|
||||
if (numPhysicalCPUs <= 0)
|
||||
numPhysicalCPUs = numLogicalCPUs;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
#if ! JUCE_IOS
|
||||
static String getOSXVersion()
|
||||
{
|
||||
JUCE_AUTORELEASEPOOL
|
||||
{
|
||||
const String systemVersionPlist ("/System/Library/CoreServices/SystemVersion.plist");
|
||||
|
||||
#if (defined (MAC_OS_X_VERSION_10_13) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_13)
|
||||
NSError* error = nullptr;
|
||||
NSDictionary* dict = [NSDictionary dictionaryWithContentsOfURL: createNSURLFromFile (systemVersionPlist)
|
||||
error: &error];
|
||||
#else
|
||||
NSDictionary* dict = [NSDictionary dictionaryWithContentsOfFile: juceStringToNS (systemVersionPlist)];
|
||||
#endif
|
||||
|
||||
if (dict != nullptr)
|
||||
return nsStringToJuce ([dict objectForKey: nsStringLiteral ("ProductVersion")]);
|
||||
|
||||
jassertfalse;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
SystemStats::OperatingSystemType SystemStats::getOperatingSystemType()
|
||||
{
|
||||
#if JUCE_IOS
|
||||
return iOS;
|
||||
#else
|
||||
StringArray parts;
|
||||
parts.addTokens (getOSXVersion(), ".", StringRef());
|
||||
|
||||
const auto major = parts[0].getIntValue();
|
||||
const auto minor = parts[1].getIntValue();
|
||||
|
||||
switch (major)
|
||||
{
|
||||
case 10:
|
||||
{
|
||||
jassert (minor > 2);
|
||||
return (OperatingSystemType) (minor + MacOSX_10_7 - 7);
|
||||
}
|
||||
|
||||
case 11: return MacOS_11;
|
||||
case 12: return MacOS_12;
|
||||
}
|
||||
|
||||
return UnknownOS;
|
||||
#endif
|
||||
}
|
||||
|
||||
String SystemStats::getOperatingSystemName()
|
||||
{
|
||||
#if JUCE_IOS
|
||||
return "iOS " + nsStringToJuce ([[UIDevice currentDevice] systemVersion]);
|
||||
#else
|
||||
return "Mac OSX " + getOSXVersion();
|
||||
#endif
|
||||
}
|
||||
|
||||
String SystemStats::getDeviceDescription()
|
||||
{
|
||||
#if JUCE_IOS
|
||||
const char* name = "hw.machine";
|
||||
#else
|
||||
const char* name = "hw.model";
|
||||
#endif
|
||||
|
||||
size_t size;
|
||||
|
||||
if (sysctlbyname (name, nullptr, &size, nullptr, 0) >= 0)
|
||||
{
|
||||
HeapBlock<char> model (size);
|
||||
|
||||
if (sysctlbyname (name, model, &size, nullptr, 0) >= 0)
|
||||
{
|
||||
String description (model.get());
|
||||
|
||||
#if JUCE_IOS
|
||||
if (description == "x86_64") // running in the simulator
|
||||
{
|
||||
if (auto* userInfo = [[NSProcessInfo processInfo] environment])
|
||||
{
|
||||
if (auto* simDeviceName = [userInfo objectForKey: @"SIMULATOR_DEVICE_NAME"])
|
||||
return nsStringToJuce (simDeviceName);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return description;
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
String SystemStats::getDeviceManufacturer()
|
||||
{
|
||||
return "Apple";
|
||||
}
|
||||
|
||||
bool SystemStats::isOperatingSystem64Bit()
|
||||
{
|
||||
#if JUCE_IOS
|
||||
return false;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
int SystemStats::getMemorySizeInMegabytes()
|
||||
{
|
||||
uint64 mem = 0;
|
||||
size_t memSize = sizeof (mem);
|
||||
int mib[] = { CTL_HW, HW_MEMSIZE };
|
||||
sysctl (mib, 2, &mem, &memSize, nullptr, 0);
|
||||
return (int) (mem / (1024 * 1024));
|
||||
}
|
||||
|
||||
String SystemStats::getCpuVendor()
|
||||
{
|
||||
#if JUCE_INTEL && ! JUCE_NO_INLINE_ASM
|
||||
uint32 dummy = 0;
|
||||
uint32 vendor[4] = { 0 };
|
||||
|
||||
SystemStatsHelpers::doCPUID (dummy, vendor[0], vendor[2], vendor[1], 0);
|
||||
|
||||
return String (reinterpret_cast<const char*> (vendor), 12);
|
||||
#else
|
||||
return {};
|
||||
#endif
|
||||
}
|
||||
|
||||
String SystemStats::getCpuModel()
|
||||
{
|
||||
char name[65] = { 0 };
|
||||
size_t size = sizeof (name) - 1;
|
||||
|
||||
if (sysctlbyname ("machdep.cpu.brand_string", &name, &size, nullptr, 0) >= 0)
|
||||
return String (name);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
int SystemStats::getCpuSpeedInMegahertz()
|
||||
{
|
||||
uint64 speedHz = 0;
|
||||
size_t speedSize = sizeof (speedHz);
|
||||
int mib[] = { CTL_HW, HW_CPU_FREQ };
|
||||
sysctl (mib, 2, &speedHz, &speedSize, nullptr, 0);
|
||||
|
||||
#if JUCE_BIG_ENDIAN
|
||||
if (speedSize == 4)
|
||||
speedHz >>= 32;
|
||||
#endif
|
||||
|
||||
return (int) (speedHz / 1000000);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
String SystemStats::getLogonName()
|
||||
{
|
||||
return nsStringToJuce (NSUserName());
|
||||
}
|
||||
|
||||
String SystemStats::getFullUserName()
|
||||
{
|
||||
return nsStringToJuce (NSFullUserName());
|
||||
}
|
||||
|
||||
String SystemStats::getComputerName()
|
||||
{
|
||||
char name[256] = { 0 };
|
||||
if (gethostname (name, sizeof (name) - 1) == 0)
|
||||
return String (name).upToLastOccurrenceOf (".local", false, true);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
static String getLocaleValue (CFStringRef key)
|
||||
{
|
||||
CFUniquePtr<CFLocaleRef> cfLocale (CFLocaleCopyCurrent());
|
||||
const String result (String::fromCFString ((CFStringRef) CFLocaleGetValue (cfLocale.get(), key)));
|
||||
return result;
|
||||
}
|
||||
|
||||
String SystemStats::getUserLanguage() { return getLocaleValue (kCFLocaleLanguageCode); }
|
||||
String SystemStats::getUserRegion() { return getLocaleValue (kCFLocaleCountryCode); }
|
||||
|
||||
String SystemStats::getDisplayLanguage()
|
||||
{
|
||||
CFUniquePtr<CFArrayRef> cfPrefLangs (CFLocaleCopyPreferredLanguages());
|
||||
const String result (String::fromCFString ((CFStringRef) CFArrayGetValueAtIndex (cfPrefLangs.get(), 0)));
|
||||
return result;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/* NB: these are kept outside the HiResCounterInfo struct and initialised to 1 to avoid
|
||||
division-by-zero errors if some other static constructor calls us before this file's
|
||||
static constructors have had a chance to fill them in correctly..
|
||||
*/
|
||||
static uint64 hiResCounterNumerator = 0, hiResCounterDenominator = 1;
|
||||
|
||||
class HiResCounterInfo
|
||||
{
|
||||
public:
|
||||
HiResCounterInfo()
|
||||
{
|
||||
mach_timebase_info_data_t timebase;
|
||||
(void) mach_timebase_info (&timebase);
|
||||
|
||||
if (timebase.numer % 1000000 == 0)
|
||||
{
|
||||
hiResCounterNumerator = timebase.numer / 1000000;
|
||||
hiResCounterDenominator = timebase.denom;
|
||||
}
|
||||
else
|
||||
{
|
||||
hiResCounterNumerator = timebase.numer;
|
||||
hiResCounterDenominator = timebase.denom * (uint64) 1000000;
|
||||
}
|
||||
|
||||
highResTimerFrequency = (timebase.denom * (uint64) 1000000000) / timebase.numer;
|
||||
highResTimerToMillisecRatio = hiResCounterNumerator / (double) hiResCounterDenominator;
|
||||
}
|
||||
|
||||
uint32 millisecondsSinceStartup() const noexcept
|
||||
{
|
||||
return (uint32) ((mach_absolute_time() * hiResCounterNumerator) / hiResCounterDenominator);
|
||||
}
|
||||
|
||||
double getMillisecondCounterHiRes() const noexcept
|
||||
{
|
||||
return mach_absolute_time() * highResTimerToMillisecRatio;
|
||||
}
|
||||
|
||||
int64 highResTimerFrequency;
|
||||
|
||||
private:
|
||||
double highResTimerToMillisecRatio;
|
||||
};
|
||||
|
||||
static HiResCounterInfo hiResCounterInfo;
|
||||
|
||||
uint32 juce_millisecondsSinceStartup() noexcept { return hiResCounterInfo.millisecondsSinceStartup(); }
|
||||
double Time::getMillisecondCounterHiRes() noexcept { return hiResCounterInfo.getMillisecondCounterHiRes(); }
|
||||
int64 Time::getHighResolutionTicksPerSecond() noexcept { return hiResCounterInfo.highResTimerFrequency; }
|
||||
int64 Time::getHighResolutionTicks() noexcept { return (int64) mach_absolute_time(); }
|
||||
|
||||
bool Time::setSystemTimeToThisTime() const
|
||||
{
|
||||
jassertfalse;
|
||||
return false;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
int SystemStats::getPageSize()
|
||||
{
|
||||
return (int) NSPageSize();
|
||||
}
|
||||
|
||||
} // namespace juce
|
83
deps/juce/modules/juce_core/native/juce_mac_Threads.mm
vendored
Normal file
83
deps/juce/modules/juce_core/native/juce_mac_Threads.mm
vendored
Normal file
@ -0,0 +1,83 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
/*
|
||||
Note that a lot of methods that you'd expect to find in this file actually
|
||||
live in juce_posix_SharedCode.h!
|
||||
*/
|
||||
|
||||
#if JUCE_IOS
|
||||
bool isIOSAppActive = true;
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
JUCE_API bool JUCE_CALLTYPE Process::isForegroundProcess()
|
||||
{
|
||||
if (SystemStats::isRunningInAppExtensionSandbox())
|
||||
return true;
|
||||
|
||||
#if JUCE_MAC
|
||||
return [NSApp isActive];
|
||||
#else
|
||||
return isIOSAppActive;
|
||||
#endif
|
||||
}
|
||||
|
||||
JUCE_API void JUCE_CALLTYPE Process::makeForegroundProcess()
|
||||
{
|
||||
#if JUCE_MAC
|
||||
if (! SystemStats::isRunningInAppExtensionSandbox())
|
||||
[NSApp activateIgnoringOtherApps: YES];
|
||||
#endif
|
||||
}
|
||||
|
||||
JUCE_API void JUCE_CALLTYPE Process::hide()
|
||||
{
|
||||
if (! SystemStats::isRunningInAppExtensionSandbox())
|
||||
{
|
||||
#if JUCE_MAC
|
||||
[NSApp hide: nil];
|
||||
#elif JUCE_IOS
|
||||
[[UIApplication sharedApplication] performSelector: @selector(suspend)];
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
JUCE_API void JUCE_CALLTYPE Process::raisePrivilege() {}
|
||||
JUCE_API void JUCE_CALLTYPE Process::lowerPrivilege() {}
|
||||
|
||||
JUCE_API void JUCE_CALLTYPE Process::setPriority (ProcessPriority) {}
|
||||
|
||||
//==============================================================================
|
||||
JUCE_API bool JUCE_CALLTYPE juce_isRunningUnderDebugger() noexcept
|
||||
{
|
||||
struct kinfo_proc info;
|
||||
int m[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid() };
|
||||
size_t sz = sizeof (info);
|
||||
sysctl (m, 4, &info, &sz, nullptr, 0);
|
||||
return (info.kp_proc.p_flag & P_TRACED) != 0;
|
||||
}
|
||||
|
||||
} // namespace juce
|
150
deps/juce/modules/juce_core/native/juce_posix_IPAddress.h
vendored
Normal file
150
deps/juce/modules/juce_core/native/juce_posix_IPAddress.h
vendored
Normal file
@ -0,0 +1,150 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
struct InterfaceInfo
|
||||
{
|
||||
IPAddress interfaceAddress, broadcastAddress;
|
||||
String name;
|
||||
};
|
||||
|
||||
inline bool operator== (const InterfaceInfo& lhs, const InterfaceInfo& rhs)
|
||||
{
|
||||
return lhs.interfaceAddress == rhs.interfaceAddress
|
||||
&& lhs.broadcastAddress == rhs.broadcastAddress;
|
||||
}
|
||||
|
||||
#if ! JUCE_WASM
|
||||
static IPAddress makeAddress (const sockaddr_in6* addr_in)
|
||||
{
|
||||
if (addr_in == nullptr)
|
||||
return {};
|
||||
|
||||
auto addr = addr_in->sin6_addr;
|
||||
|
||||
IPAddressByteUnion temp;
|
||||
uint16 arr[8];
|
||||
|
||||
for (int i = 0; i < 8; ++i) // Swap bytes from network to host order
|
||||
{
|
||||
temp.split[0] = addr.s6_addr[i * 2 + 1];
|
||||
temp.split[1] = addr.s6_addr[i * 2];
|
||||
|
||||
arr[i] = temp.combined;
|
||||
}
|
||||
|
||||
return IPAddress (arr);
|
||||
}
|
||||
|
||||
static IPAddress makeAddress (const sockaddr_in* addr_in)
|
||||
{
|
||||
if (addr_in->sin_addr.s_addr == INADDR_NONE)
|
||||
return {};
|
||||
|
||||
return IPAddress (ntohl (addr_in->sin_addr.s_addr));
|
||||
}
|
||||
|
||||
bool populateInterfaceInfo (struct ifaddrs* ifa, InterfaceInfo& interfaceInfo)
|
||||
{
|
||||
if (ifa->ifa_addr != nullptr)
|
||||
{
|
||||
if (ifa->ifa_addr->sa_family == AF_INET)
|
||||
{
|
||||
auto interfaceAddressInfo = unalignedPointerCast<sockaddr_in*> (ifa->ifa_addr);
|
||||
auto broadcastAddressInfo = unalignedPointerCast<sockaddr_in*> (ifa->ifa_dstaddr);
|
||||
|
||||
if (interfaceAddressInfo->sin_addr.s_addr != INADDR_NONE)
|
||||
{
|
||||
interfaceInfo.interfaceAddress = makeAddress (interfaceAddressInfo);
|
||||
interfaceInfo.broadcastAddress = makeAddress (broadcastAddressInfo);
|
||||
interfaceInfo.name = ifa->ifa_name;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (ifa->ifa_addr->sa_family == AF_INET6)
|
||||
{
|
||||
interfaceInfo.interfaceAddress = makeAddress (unalignedPointerCast<sockaddr_in6*> (ifa->ifa_addr));
|
||||
interfaceInfo.broadcastAddress = makeAddress (unalignedPointerCast<sockaddr_in6*> (ifa->ifa_dstaddr));
|
||||
interfaceInfo.name = ifa->ifa_name;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
Array<InterfaceInfo> getAllInterfaceInfo()
|
||||
{
|
||||
Array<InterfaceInfo> interfaces;
|
||||
|
||||
#if JUCE_WASM
|
||||
// TODO
|
||||
#else
|
||||
struct ifaddrs* ifaddr = nullptr;
|
||||
|
||||
if (getifaddrs (&ifaddr) != -1)
|
||||
{
|
||||
for (auto* ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next)
|
||||
{
|
||||
InterfaceInfo i;
|
||||
|
||||
if (populateInterfaceInfo (ifa, i))
|
||||
interfaces.addIfNotAlreadyThere (i);
|
||||
}
|
||||
|
||||
freeifaddrs (ifaddr);
|
||||
}
|
||||
#endif
|
||||
|
||||
return interfaces;
|
||||
}
|
||||
}
|
||||
|
||||
void IPAddress::findAllAddresses (Array<IPAddress>& result, bool includeIPv6)
|
||||
{
|
||||
for (auto& i : getAllInterfaceInfo())
|
||||
if (includeIPv6 || ! i.interfaceAddress.isIPv6)
|
||||
result.addIfNotAlreadyThere (i.interfaceAddress);
|
||||
}
|
||||
|
||||
void IPAddress::findAllInterfaceAddresses (Array<IPAddressInterfaceNamePair>& result, bool includeIPv6)
|
||||
{
|
||||
for (auto& i : getAllInterfaceInfo())
|
||||
if (includeIPv6 || ! i.interfaceAddress.isIPv6)
|
||||
result.addIfNotAlreadyThere (IPAddressInterfaceNamePair(i.interfaceAddress, i.name));
|
||||
}
|
||||
|
||||
IPAddress IPAddress::getInterfaceBroadcastAddress (const IPAddress& interfaceAddress)
|
||||
{
|
||||
for (auto& i : getAllInterfaceInfo())
|
||||
if (i.interfaceAddress == interfaceAddress)
|
||||
return i.broadcastAddress;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace juce
|
248
deps/juce/modules/juce_core/native/juce_posix_NamedPipe.cpp
vendored
Normal file
248
deps/juce/modules/juce_core/native/juce_posix_NamedPipe.cpp
vendored
Normal file
@ -0,0 +1,248 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#if ! JUCE_WASM
|
||||
|
||||
class NamedPipe::Pimpl
|
||||
{
|
||||
public:
|
||||
Pimpl (const String& pipePath, bool createPipe)
|
||||
: pipeInName (pipePath + "_in"),
|
||||
pipeOutName (pipePath + "_out"),
|
||||
createdPipe (createPipe)
|
||||
{
|
||||
signal (SIGPIPE, signalHandler);
|
||||
juce_siginterrupt (SIGPIPE, 1);
|
||||
}
|
||||
|
||||
~Pimpl()
|
||||
{
|
||||
if (pipeIn != -1) ::close (pipeIn);
|
||||
if (pipeOut != -1) ::close (pipeOut);
|
||||
|
||||
if (createdPipe)
|
||||
{
|
||||
if (createdFifoIn) unlink (pipeInName.toUTF8());
|
||||
if (createdFifoOut) unlink (pipeOutName.toUTF8());
|
||||
}
|
||||
}
|
||||
|
||||
bool connect (int timeOutMilliseconds)
|
||||
{
|
||||
return openPipe (true, getTimeoutEnd (timeOutMilliseconds));
|
||||
}
|
||||
|
||||
int read (char* destBuffer, int maxBytesToRead, int timeOutMilliseconds)
|
||||
{
|
||||
auto timeoutEnd = getTimeoutEnd (timeOutMilliseconds);
|
||||
int bytesRead = 0;
|
||||
|
||||
while (bytesRead < maxBytesToRead)
|
||||
{
|
||||
auto bytesThisTime = maxBytesToRead - bytesRead;
|
||||
auto numRead = (int) ::read (pipeIn, destBuffer, (size_t) bytesThisTime);
|
||||
|
||||
if (numRead <= 0)
|
||||
{
|
||||
if (errno != EWOULDBLOCK || stopReadOperation.load() || hasExpired (timeoutEnd))
|
||||
return -1;
|
||||
|
||||
const int maxWaitingTime = 30;
|
||||
waitForInput (pipeIn, timeoutEnd == 0 ? maxWaitingTime
|
||||
: jmin (maxWaitingTime,
|
||||
(int) (timeoutEnd - Time::getMillisecondCounter())));
|
||||
continue;
|
||||
}
|
||||
|
||||
bytesRead += numRead;
|
||||
destBuffer += numRead;
|
||||
}
|
||||
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
int write (const char* sourceBuffer, int numBytesToWrite, int timeOutMilliseconds)
|
||||
{
|
||||
auto timeoutEnd = getTimeoutEnd (timeOutMilliseconds);
|
||||
|
||||
if (! openPipe (false, timeoutEnd))
|
||||
return -1;
|
||||
|
||||
int bytesWritten = 0;
|
||||
|
||||
while (bytesWritten < numBytesToWrite && ! hasExpired (timeoutEnd))
|
||||
{
|
||||
auto bytesThisTime = numBytesToWrite - bytesWritten;
|
||||
auto numWritten = (int) ::write (pipeOut, sourceBuffer, (size_t) bytesThisTime);
|
||||
|
||||
if (numWritten <= 0)
|
||||
return -1;
|
||||
|
||||
bytesWritten += numWritten;
|
||||
sourceBuffer += numWritten;
|
||||
}
|
||||
|
||||
return bytesWritten;
|
||||
}
|
||||
|
||||
static bool createFifo (const String& name, bool mustNotExist)
|
||||
{
|
||||
return mkfifo (name.toUTF8(), 0666) == 0 || ((! mustNotExist) && errno == EEXIST);
|
||||
}
|
||||
|
||||
bool createFifos (bool mustNotExist)
|
||||
{
|
||||
createdFifoIn = createFifo (pipeInName, mustNotExist);
|
||||
createdFifoOut = createFifo (pipeOutName, mustNotExist);
|
||||
|
||||
return createdFifoIn && createdFifoOut;
|
||||
}
|
||||
|
||||
const String pipeInName, pipeOutName;
|
||||
int pipeIn = -1, pipeOut = -1;
|
||||
bool createdFifoIn = false, createdFifoOut = false;
|
||||
|
||||
const bool createdPipe;
|
||||
std::atomic<bool> stopReadOperation { false };
|
||||
|
||||
private:
|
||||
static void signalHandler (int) {}
|
||||
|
||||
static uint32 getTimeoutEnd (int timeOutMilliseconds)
|
||||
{
|
||||
return timeOutMilliseconds >= 0 ? Time::getMillisecondCounter() + (uint32) timeOutMilliseconds : 0;
|
||||
}
|
||||
|
||||
static bool hasExpired (uint32 timeoutEnd)
|
||||
{
|
||||
return timeoutEnd != 0 && Time::getMillisecondCounter() >= timeoutEnd;
|
||||
}
|
||||
|
||||
int openPipe (const String& name, int flags, uint32 timeoutEnd)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
auto p = ::open (name.toUTF8(), flags);
|
||||
|
||||
if (p != -1 || hasExpired (timeoutEnd) || stopReadOperation.load())
|
||||
return p;
|
||||
|
||||
Thread::sleep (2);
|
||||
}
|
||||
}
|
||||
|
||||
bool openPipe (bool isInput, uint32 timeoutEnd)
|
||||
{
|
||||
auto& pipe = isInput ? pipeIn : pipeOut;
|
||||
int flags = (isInput ? O_RDWR : O_WRONLY) | O_NONBLOCK;
|
||||
|
||||
const String& pipeName = isInput ? (createdPipe ? pipeInName : pipeOutName)
|
||||
: (createdPipe ? pipeOutName : pipeInName);
|
||||
|
||||
if (pipe == -1)
|
||||
{
|
||||
pipe = openPipe (pipeName, flags, timeoutEnd);
|
||||
|
||||
if (pipe == -1)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void waitForInput (int handle, int timeoutMsecs) noexcept
|
||||
{
|
||||
pollfd pfd { handle, POLLIN, 0 };
|
||||
poll (&pfd, 1, timeoutMsecs);
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl)
|
||||
};
|
||||
|
||||
void NamedPipe::close()
|
||||
{
|
||||
{
|
||||
ScopedReadLock sl (lock);
|
||||
|
||||
if (pimpl != nullptr)
|
||||
{
|
||||
pimpl->stopReadOperation = true;
|
||||
|
||||
char buffer[1] = { 0 };
|
||||
ssize_t done = ::write (pimpl->pipeIn, buffer, 1);
|
||||
ignoreUnused (done);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
ScopedWriteLock sl (lock);
|
||||
pimpl.reset();
|
||||
}
|
||||
}
|
||||
|
||||
bool NamedPipe::openInternal (const String& pipeName, bool createPipe, bool mustNotExist)
|
||||
{
|
||||
#if JUCE_IOS
|
||||
pimpl.reset (new Pimpl (File::getSpecialLocation (File::tempDirectory)
|
||||
.getChildFile (File::createLegalFileName (pipeName)).getFullPathName(), createPipe));
|
||||
#else
|
||||
auto file = pipeName;
|
||||
|
||||
if (! File::isAbsolutePath (file))
|
||||
file = "/tmp/" + File::createLegalFileName (file);
|
||||
|
||||
pimpl.reset (new Pimpl (file, createPipe));
|
||||
#endif
|
||||
|
||||
if (createPipe && ! pimpl->createFifos (mustNotExist))
|
||||
{
|
||||
pimpl.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! pimpl->connect (200))
|
||||
{
|
||||
pimpl.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int NamedPipe::read (void* destBuffer, int maxBytesToRead, int timeOutMilliseconds)
|
||||
{
|
||||
ScopedReadLock sl (lock);
|
||||
return pimpl != nullptr ? pimpl->read (static_cast<char*> (destBuffer), maxBytesToRead, timeOutMilliseconds) : -1;
|
||||
}
|
||||
|
||||
int NamedPipe::write (const void* sourceBuffer, int numBytesToWrite, int timeOutMilliseconds)
|
||||
{
|
||||
ScopedReadLock sl (lock);
|
||||
return pimpl != nullptr ? pimpl->write (static_cast<const char*> (sourceBuffer), numBytesToWrite, timeOutMilliseconds) : -1;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
1395
deps/juce/modules/juce_core/native/juce_posix_SharedCode.h
vendored
Normal file
1395
deps/juce/modules/juce_core/native/juce_posix_SharedCode.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
87
deps/juce/modules/juce_core/native/juce_wasm_SystemStats.cpp
vendored
Normal file
87
deps/juce/modules/juce_core/native/juce_wasm_SystemStats.cpp
vendored
Normal file
@ -0,0 +1,87 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
void Logger::outputDebugString (const String& text)
|
||||
{
|
||||
std::cerr << text << std::endl;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
SystemStats::OperatingSystemType SystemStats::getOperatingSystemType() { return WASM; }
|
||||
String SystemStats::getOperatingSystemName() { return "WASM"; }
|
||||
bool SystemStats::isOperatingSystem64Bit() { return true; }
|
||||
String SystemStats::getDeviceDescription() { return "Web-browser"; }
|
||||
String SystemStats::getDeviceManufacturer() { return {}; }
|
||||
String SystemStats::getCpuVendor() { return {}; }
|
||||
String SystemStats::getCpuModel() { return {}; }
|
||||
int SystemStats::getCpuSpeedInMegahertz() { return 0; }
|
||||
int SystemStats::getMemorySizeInMegabytes() { return 0; }
|
||||
int SystemStats::getPageSize() { return 0; }
|
||||
String SystemStats::getLogonName() { return {}; }
|
||||
String SystemStats::getFullUserName() { return {}; }
|
||||
String SystemStats::getComputerName() { return {}; }
|
||||
String SystemStats::getUserLanguage() { return {}; }
|
||||
String SystemStats::getUserRegion() { return {}; }
|
||||
String SystemStats::getDisplayLanguage() { return {}; }
|
||||
|
||||
//==============================================================================
|
||||
void CPUInformation::initialise() noexcept
|
||||
{
|
||||
numLogicalCPUs = 1;
|
||||
numPhysicalCPUs = 1;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
uint32 juce_millisecondsSinceStartup() noexcept
|
||||
{
|
||||
return static_cast<uint32> (emscripten_get_now());
|
||||
}
|
||||
|
||||
int64 Time::getHighResolutionTicks() noexcept
|
||||
{
|
||||
return static_cast<int64> (emscripten_get_now() * 1000.0);
|
||||
}
|
||||
|
||||
int64 Time::getHighResolutionTicksPerSecond() noexcept
|
||||
{
|
||||
return 1000000; // (microseconds)
|
||||
}
|
||||
|
||||
double Time::getMillisecondCounterHiRes() noexcept
|
||||
{
|
||||
return emscripten_get_now();
|
||||
}
|
||||
|
||||
bool Time::setSystemTimeToThisTime() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
JUCE_API bool JUCE_CALLTYPE juce_isRunningUnderDebugger() noexcept
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace juce
|
230
deps/juce/modules/juce_core/native/juce_win32_ComSmartPtr.h
vendored
Normal file
230
deps/juce/modules/juce_core/native/juce_win32_ComSmartPtr.h
vendored
Normal file
@ -0,0 +1,230 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#if JUCE_MINGW || (! (defined (_MSC_VER) || defined (__uuidof)))
|
||||
#ifdef __uuidof
|
||||
#undef __uuidof
|
||||
#endif
|
||||
|
||||
template <typename Type> struct UUIDGetter { static CLSID get() { jassertfalse; return {}; } };
|
||||
#define __uuidof(x) UUIDGetter<x>::get()
|
||||
|
||||
template <>
|
||||
struct UUIDGetter<::IUnknown>
|
||||
{
|
||||
static CLSID get() { return { 0, 0, 0, { 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 } }; }
|
||||
};
|
||||
|
||||
#define JUCE_DECLARE_UUID_GETTER(name, uuid) \
|
||||
template <> struct UUIDGetter<name> { static CLSID get() { return uuidFromString (uuid); } };
|
||||
|
||||
#define JUCE_COMCLASS(name, guid) \
|
||||
struct name; \
|
||||
JUCE_DECLARE_UUID_GETTER (name, guid) \
|
||||
struct name
|
||||
|
||||
#else
|
||||
#define JUCE_DECLARE_UUID_GETTER(name, uuid)
|
||||
#define JUCE_COMCLASS(name, guid) struct __declspec (uuid (guid)) name
|
||||
#endif
|
||||
|
||||
#define JUCE_IUNKNOWNCLASS(name, guid) JUCE_COMCLASS(name, guid) : public IUnknown
|
||||
#define JUCE_COMRESULT HRESULT STDMETHODCALLTYPE
|
||||
#define JUCE_COMCALL virtual HRESULT STDMETHODCALLTYPE
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token")
|
||||
|
||||
inline GUID uuidFromString (const char* s) noexcept
|
||||
{
|
||||
uint32 ints[4] = {};
|
||||
|
||||
for (uint32 digitIndex = 0; digitIndex < 32;)
|
||||
{
|
||||
auto c = (uint32) *s++;
|
||||
uint32 digit;
|
||||
|
||||
if (c >= '0' && c <= '9') digit = c - '0';
|
||||
else if (c >= 'a' && c <= 'f') digit = c - 'a' + 10;
|
||||
else if (c >= 'A' && c <= 'F') digit = c - 'A' + 10;
|
||||
else if (c == '-') continue;
|
||||
else break;
|
||||
|
||||
ints[digitIndex / 8] |= (digit << 4 * (7 - (digitIndex & 7)));
|
||||
++digitIndex;
|
||||
}
|
||||
|
||||
return { ints[0],
|
||||
(uint16) (ints[1] >> 16),
|
||||
(uint16) ints[1],
|
||||
{ (uint8) (ints[2] >> 24), (uint8) (ints[2] >> 16), (uint8) (ints[2] >> 8), (uint8) ints[2],
|
||||
(uint8) (ints[3] >> 24), (uint8) (ints[3] >> 16), (uint8) (ints[3] >> 8), (uint8) ints[3] }};
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/** A simple COM smart pointer.
|
||||
|
||||
@tags{Core}
|
||||
*/
|
||||
template <class ComClass>
|
||||
class ComSmartPtr
|
||||
{
|
||||
public:
|
||||
ComSmartPtr() noexcept {}
|
||||
ComSmartPtr (ComClass* obj) : p (obj) { if (p) p->AddRef(); }
|
||||
ComSmartPtr (const ComSmartPtr& other) : p (other.p) { if (p) p->AddRef(); }
|
||||
~ComSmartPtr() { release(); }
|
||||
|
||||
operator ComClass*() const noexcept { return p; }
|
||||
ComClass& operator*() const noexcept { return *p; }
|
||||
ComClass* operator->() const noexcept { return p; }
|
||||
|
||||
ComSmartPtr& operator= (ComClass* const newP)
|
||||
{
|
||||
if (newP != nullptr) newP->AddRef();
|
||||
release();
|
||||
p = newP;
|
||||
return *this;
|
||||
}
|
||||
|
||||
ComSmartPtr& operator= (const ComSmartPtr& newP) { return operator= (newP.p); }
|
||||
|
||||
// Releases and nullifies this pointer and returns its address
|
||||
ComClass** resetAndGetPointerAddress()
|
||||
{
|
||||
release();
|
||||
p = nullptr;
|
||||
return &p;
|
||||
}
|
||||
|
||||
HRESULT CoCreateInstance (REFCLSID classUUID, DWORD dwClsContext = CLSCTX_INPROC_SERVER)
|
||||
{
|
||||
auto hr = ::CoCreateInstance (classUUID, nullptr, dwClsContext, __uuidof (ComClass), (void**) resetAndGetPointerAddress());
|
||||
jassert (hr != CO_E_NOTINITIALIZED); // You haven't called CoInitialize for the current thread!
|
||||
return hr;
|
||||
}
|
||||
|
||||
template <class OtherComClass>
|
||||
HRESULT QueryInterface (REFCLSID classUUID, ComSmartPtr<OtherComClass>& destObject) const
|
||||
{
|
||||
if (p == nullptr)
|
||||
return E_POINTER;
|
||||
|
||||
return p->QueryInterface (classUUID, (void**) destObject.resetAndGetPointerAddress());
|
||||
}
|
||||
|
||||
template <class OtherComClass>
|
||||
HRESULT QueryInterface (ComSmartPtr<OtherComClass>& destObject) const
|
||||
{
|
||||
return this->QueryInterface (__uuidof (OtherComClass), destObject);
|
||||
}
|
||||
|
||||
template <class OtherComClass>
|
||||
ComSmartPtr<OtherComClass> getInterface() const
|
||||
{
|
||||
ComSmartPtr<OtherComClass> destObject;
|
||||
|
||||
if (QueryInterface (destObject) == S_OK)
|
||||
return destObject;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
ComClass* p = nullptr;
|
||||
|
||||
void release() { if (p != nullptr) p->Release(); }
|
||||
|
||||
ComClass** operator&() noexcept; // private to avoid it being used accidentally
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
template <class First, class... ComClasses>
|
||||
class ComBaseClassHelperBase : public First, public ComClasses...
|
||||
{
|
||||
public:
|
||||
ComBaseClassHelperBase (unsigned int initialRefCount) : refCount (initialRefCount) {}
|
||||
virtual ~ComBaseClassHelperBase() = default;
|
||||
|
||||
ULONG STDMETHODCALLTYPE AddRef() { return ++refCount; }
|
||||
ULONG STDMETHODCALLTYPE Release() { auto r = --refCount; if (r == 0) delete this; return r; }
|
||||
|
||||
protected:
|
||||
ULONG refCount;
|
||||
|
||||
JUCE_COMRESULT QueryInterface (REFIID refId, void** result)
|
||||
{
|
||||
if (refId == __uuidof (IUnknown))
|
||||
return castToType<First> (result);
|
||||
|
||||
*result = nullptr;
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
template <class Type>
|
||||
JUCE_COMRESULT castToType (void** result)
|
||||
{
|
||||
this->AddRef();
|
||||
*result = dynamic_cast<Type*> (this);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
};
|
||||
|
||||
/** Handy base class for writing COM objects, providing ref-counting and a basic QueryInterface method.
|
||||
|
||||
@tags{Core}
|
||||
*/
|
||||
template <class... ComClasses>
|
||||
class ComBaseClassHelper : public ComBaseClassHelperBase<ComClasses...>
|
||||
{
|
||||
public:
|
||||
explicit ComBaseClassHelper (unsigned int initialRefCount = 1)
|
||||
: ComBaseClassHelperBase<ComClasses...> (initialRefCount) {}
|
||||
|
||||
JUCE_COMRESULT QueryInterface (REFIID refId, void** result)
|
||||
{
|
||||
const std::tuple<IID, void*> bases[]
|
||||
{
|
||||
std::make_tuple (__uuidof (ComClasses),
|
||||
static_cast<void*> (static_cast<ComClasses*> (this)))...
|
||||
};
|
||||
|
||||
for (const auto& base : bases)
|
||||
{
|
||||
if (refId == std::get<0> (base))
|
||||
{
|
||||
this->AddRef();
|
||||
*result = std::get<1> (base);
|
||||
return S_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return ComBaseClassHelperBase<ComClasses...>::QueryInterface (refId, result);
|
||||
}
|
||||
};
|
||||
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
|
||||
} // namespace juce
|
1234
deps/juce/modules/juce_core/native/juce_win32_Files.cpp
vendored
Normal file
1234
deps/juce/modules/juce_core/native/juce_win32_Files.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
681
deps/juce/modules/juce_core/native/juce_win32_Network.cpp
vendored
Normal file
681
deps/juce/modules/juce_core/native/juce_win32_Network.cpp
vendored
Normal file
@ -0,0 +1,681 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#ifndef INTERNET_FLAG_NEED_FILE
|
||||
#define INTERNET_FLAG_NEED_FILE 0x00000010
|
||||
#endif
|
||||
|
||||
#ifndef INTERNET_OPTION_DISABLE_AUTODIAL
|
||||
#define INTERNET_OPTION_DISABLE_AUTODIAL 70
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
class WebInputStream::Pimpl
|
||||
{
|
||||
public:
|
||||
Pimpl (WebInputStream& pimplOwner, const URL& urlToCopy, bool addParametersToBody)
|
||||
: owner (pimplOwner),
|
||||
url (urlToCopy),
|
||||
addParametersToRequestBody (addParametersToBody),
|
||||
hasBodyDataToSend (addParametersToRequestBody || url.hasBodyDataToSend()),
|
||||
httpRequestCmd (hasBodyDataToSend ? "POST" : "GET")
|
||||
{
|
||||
}
|
||||
|
||||
~Pimpl()
|
||||
{
|
||||
closeConnection();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// WebInputStream methods
|
||||
void withExtraHeaders (const String& extraHeaders)
|
||||
{
|
||||
if (! headers.endsWithChar ('\n') && headers.isNotEmpty())
|
||||
headers << "\r\n";
|
||||
|
||||
headers << extraHeaders;
|
||||
|
||||
if (! headers.endsWithChar ('\n') && headers.isNotEmpty())
|
||||
headers << "\r\n";
|
||||
}
|
||||
|
||||
void withCustomRequestCommand (const String& customRequestCommand) { httpRequestCmd = customRequestCommand; }
|
||||
void withConnectionTimeout (int timeoutInMs) { timeOutMs = timeoutInMs; }
|
||||
void withNumRedirectsToFollow (int maxRedirectsToFollow) { numRedirectsToFollow = maxRedirectsToFollow; }
|
||||
StringPairArray getRequestHeaders() const { return WebInputStream::parseHttpHeaders (headers); }
|
||||
StringPairArray getResponseHeaders() const { return responseHeaders; }
|
||||
int getStatusCode() const { return statusCode; }
|
||||
|
||||
//==============================================================================
|
||||
bool connect (WebInputStream::Listener* listener)
|
||||
{
|
||||
{
|
||||
const ScopedLock lock (createConnectionLock);
|
||||
|
||||
if (hasBeenCancelled)
|
||||
return false;
|
||||
}
|
||||
|
||||
auto address = url.toString (! addParametersToRequestBody);
|
||||
|
||||
while (numRedirectsToFollow-- >= 0)
|
||||
{
|
||||
createConnection (address, listener);
|
||||
|
||||
if (! isError())
|
||||
{
|
||||
DWORD bufferSizeBytes = 4096;
|
||||
StringPairArray dataHeaders;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
HeapBlock<char> buffer (bufferSizeBytes);
|
||||
|
||||
if (HttpQueryInfo (request, HTTP_QUERY_RAW_HEADERS_CRLF, buffer.getData(), &bufferSizeBytes, nullptr))
|
||||
{
|
||||
StringArray headersArray;
|
||||
headersArray.addLines (String (reinterpret_cast<const WCHAR*> (buffer.getData())));
|
||||
|
||||
for (int i = 0; i < headersArray.size(); ++i)
|
||||
{
|
||||
const String& header = headersArray[i];
|
||||
const String key (header.upToFirstOccurrenceOf (": ", false, false));
|
||||
const String value (header.fromFirstOccurrenceOf (": ", false, false));
|
||||
const String previousValue (dataHeaders[key]);
|
||||
dataHeaders.set (key, previousValue.isEmpty() ? value : (previousValue + "," + value));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
|
||||
return false;
|
||||
|
||||
bufferSizeBytes += 4096;
|
||||
}
|
||||
|
||||
DWORD status = 0;
|
||||
DWORD statusSize = sizeof (status);
|
||||
|
||||
if (HttpQueryInfo (request, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, &status, &statusSize, nullptr))
|
||||
{
|
||||
statusCode = (int) status;
|
||||
|
||||
if (numRedirectsToFollow >= 0
|
||||
&& (statusCode == 301 || statusCode == 302 || statusCode == 303 || statusCode == 307))
|
||||
{
|
||||
String newLocation (dataHeaders["Location"]);
|
||||
|
||||
// Check whether location is a relative URI - this is an incomplete test for relative path,
|
||||
// but we'll use it for now (valid protocols for this implementation are http, https & ftp)
|
||||
if (! (newLocation.startsWithIgnoreCase ("http://")
|
||||
|| newLocation.startsWithIgnoreCase ("https://")
|
||||
|| newLocation.startsWithIgnoreCase ("ftp://")))
|
||||
{
|
||||
if (newLocation.startsWithChar ('/'))
|
||||
newLocation = URL (address).withNewSubPath (newLocation).toString (true);
|
||||
else
|
||||
newLocation = address + "/" + newLocation;
|
||||
}
|
||||
|
||||
if (newLocation.isNotEmpty() && newLocation != address)
|
||||
{
|
||||
address = newLocation;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
responseHeaders.addArray (dataHeaders);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return (request != nullptr);
|
||||
}
|
||||
|
||||
bool isError() const { return request == nullptr; }
|
||||
bool isExhausted() { return finished; }
|
||||
int64 getPosition() { return position; }
|
||||
|
||||
int64 getTotalLength()
|
||||
{
|
||||
if (! isError())
|
||||
{
|
||||
DWORD index = 0, result = 0, size = sizeof (result);
|
||||
|
||||
if (HttpQueryInfo (request, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, &result, &size, &index))
|
||||
return (int64) result;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int read (void* buffer, int bytesToRead)
|
||||
{
|
||||
jassert (bytesToRead >= 0);
|
||||
|
||||
if (buffer == nullptr)
|
||||
{
|
||||
jassertfalse;
|
||||
return 0;
|
||||
}
|
||||
|
||||
DWORD bytesRead = 0;
|
||||
|
||||
if (! (finished || isError()))
|
||||
{
|
||||
InternetReadFile (request, buffer, (DWORD) bytesToRead, &bytesRead);
|
||||
position += bytesRead;
|
||||
|
||||
if (bytesRead == 0)
|
||||
finished = true;
|
||||
}
|
||||
|
||||
return (int) bytesRead;
|
||||
}
|
||||
|
||||
void cancel()
|
||||
{
|
||||
{
|
||||
const ScopedLock lock (createConnectionLock);
|
||||
|
||||
hasBeenCancelled = true;
|
||||
|
||||
closeConnection();
|
||||
}
|
||||
}
|
||||
|
||||
bool setPosition (int64 wantedPos)
|
||||
{
|
||||
if (isError())
|
||||
return false;
|
||||
|
||||
if (wantedPos != position)
|
||||
{
|
||||
finished = false;
|
||||
position = (int64) InternetSetFilePointer (request, (LONG) wantedPos, nullptr, FILE_BEGIN, 0);
|
||||
|
||||
if (position == wantedPos)
|
||||
return true;
|
||||
|
||||
if (wantedPos < position)
|
||||
return false;
|
||||
|
||||
int64 numBytesToSkip = wantedPos - position;
|
||||
auto skipBufferSize = (int) jmin (numBytesToSkip, (int64) 16384);
|
||||
HeapBlock<char> temp (skipBufferSize);
|
||||
|
||||
while (numBytesToSkip > 0 && ! isExhausted())
|
||||
numBytesToSkip -= read (temp, (int) jmin (numBytesToSkip, (int64) skipBufferSize));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int statusCode = 0;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
WebInputStream& owner;
|
||||
const URL url;
|
||||
HINTERNET connection = nullptr, request = nullptr;
|
||||
String headers;
|
||||
MemoryBlock postData;
|
||||
int64 position = 0;
|
||||
bool finished = false;
|
||||
const bool addParametersToRequestBody, hasBodyDataToSend;
|
||||
int timeOutMs = 0;
|
||||
String httpRequestCmd;
|
||||
int numRedirectsToFollow = 5;
|
||||
StringPairArray responseHeaders;
|
||||
CriticalSection createConnectionLock;
|
||||
bool hasBeenCancelled = false;
|
||||
|
||||
void closeConnection()
|
||||
{
|
||||
HINTERNET requestCopy = request;
|
||||
|
||||
request = nullptr;
|
||||
|
||||
if (requestCopy != nullptr)
|
||||
InternetCloseHandle (requestCopy);
|
||||
|
||||
if (connection != nullptr)
|
||||
{
|
||||
InternetCloseHandle (connection);
|
||||
connection = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void createConnection (const String& address, WebInputStream::Listener* listener)
|
||||
{
|
||||
static HINTERNET sessionHandle = InternetOpen (_T("juce"), INTERNET_OPEN_TYPE_PRECONFIG, nullptr, nullptr, 0);
|
||||
|
||||
closeConnection();
|
||||
|
||||
if (sessionHandle != nullptr)
|
||||
{
|
||||
// break up the url..
|
||||
const int fileNumChars = 65536;
|
||||
const int serverNumChars = 2048;
|
||||
const int usernameNumChars = 1024;
|
||||
const int passwordNumChars = 1024;
|
||||
HeapBlock<TCHAR> file (fileNumChars), server (serverNumChars),
|
||||
username (usernameNumChars), password (passwordNumChars);
|
||||
|
||||
URL_COMPONENTS uc = {};
|
||||
uc.dwStructSize = sizeof (uc);
|
||||
uc.lpszUrlPath = file;
|
||||
uc.dwUrlPathLength = fileNumChars;
|
||||
uc.lpszHostName = server;
|
||||
uc.dwHostNameLength = serverNumChars;
|
||||
uc.lpszUserName = username;
|
||||
uc.dwUserNameLength = usernameNumChars;
|
||||
uc.lpszPassword = password;
|
||||
uc.dwPasswordLength = passwordNumChars;
|
||||
|
||||
if (hasBodyDataToSend)
|
||||
WebInputStream::createHeadersAndPostData (url,
|
||||
headers,
|
||||
postData,
|
||||
addParametersToRequestBody);
|
||||
|
||||
if (InternetCrackUrl (address.toWideCharPointer(), 0, 0, &uc))
|
||||
openConnection (uc, sessionHandle, address, listener);
|
||||
}
|
||||
}
|
||||
|
||||
void openConnection (URL_COMPONENTS& uc, HINTERNET sessionHandle,
|
||||
const String& address,
|
||||
WebInputStream::Listener* listener)
|
||||
{
|
||||
int disable = 1;
|
||||
InternetSetOption (sessionHandle, INTERNET_OPTION_DISABLE_AUTODIAL, &disable, sizeof (disable));
|
||||
|
||||
if (timeOutMs == 0)
|
||||
timeOutMs = 30000;
|
||||
else if (timeOutMs < 0)
|
||||
timeOutMs = -1;
|
||||
|
||||
applyTimeout (sessionHandle, INTERNET_OPTION_CONNECT_TIMEOUT);
|
||||
applyTimeout (sessionHandle, INTERNET_OPTION_RECEIVE_TIMEOUT);
|
||||
applyTimeout (sessionHandle, INTERNET_OPTION_SEND_TIMEOUT);
|
||||
applyTimeout (sessionHandle, INTERNET_OPTION_DATA_RECEIVE_TIMEOUT);
|
||||
applyTimeout (sessionHandle, INTERNET_OPTION_DATA_SEND_TIMEOUT);
|
||||
|
||||
const bool isFtp = address.startsWithIgnoreCase ("ftp:");
|
||||
|
||||
{
|
||||
const ScopedLock lock (createConnectionLock);
|
||||
|
||||
connection = hasBeenCancelled ? nullptr
|
||||
: InternetConnect (sessionHandle,
|
||||
uc.lpszHostName, uc.nPort,
|
||||
uc.lpszUserName, uc.lpszPassword,
|
||||
isFtp ? (DWORD) INTERNET_SERVICE_FTP
|
||||
: (DWORD) INTERNET_SERVICE_HTTP,
|
||||
0, 0);
|
||||
}
|
||||
|
||||
if (connection != nullptr)
|
||||
{
|
||||
if (isFtp)
|
||||
request = FtpOpenFile (connection, uc.lpszUrlPath, GENERIC_READ,
|
||||
FTP_TRANSFER_TYPE_BINARY | INTERNET_FLAG_NEED_FILE, 0);
|
||||
else
|
||||
openHTTPConnection (uc, address, listener);
|
||||
}
|
||||
}
|
||||
|
||||
void applyTimeout (HINTERNET sessionHandle, const DWORD option)
|
||||
{
|
||||
InternetSetOption (sessionHandle, option, &timeOutMs, sizeof (timeOutMs));
|
||||
}
|
||||
|
||||
void sendHTTPRequest (INTERNET_BUFFERS& buffers, WebInputStream::Listener* listener)
|
||||
{
|
||||
if (! HttpSendRequestEx (request, &buffers, nullptr, HSR_INITIATE, 0))
|
||||
return;
|
||||
|
||||
int totalBytesSent = 0;
|
||||
|
||||
while (totalBytesSent < (int) postData.getSize())
|
||||
{
|
||||
auto bytesToSend = jmin (1024, (int) postData.getSize() - totalBytesSent);
|
||||
DWORD bytesSent = 0;
|
||||
|
||||
if (bytesToSend == 0
|
||||
|| ! InternetWriteFile (request, static_cast<const char*> (postData.getData()) + totalBytesSent,
|
||||
(DWORD) bytesToSend, &bytesSent))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
totalBytesSent += (int) bytesSent;
|
||||
|
||||
if (listener != nullptr
|
||||
&& ! listener->postDataSendProgress (owner, totalBytesSent, (int) postData.getSize()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void openHTTPConnection (URL_COMPONENTS& uc, const String& address, WebInputStream::Listener* listener)
|
||||
{
|
||||
const TCHAR* mimeTypes[] = { _T("*/*"), nullptr };
|
||||
|
||||
DWORD flags = INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_NO_COOKIES
|
||||
| INTERNET_FLAG_NO_AUTO_REDIRECT;
|
||||
|
||||
if (address.startsWithIgnoreCase ("https:"))
|
||||
flags |= INTERNET_FLAG_SECURE; // (this flag only seems necessary if the OS is running IE6 -
|
||||
// IE7 seems to automatically work out when it's https)
|
||||
|
||||
{
|
||||
const ScopedLock lock (createConnectionLock);
|
||||
|
||||
request = hasBeenCancelled ? nullptr
|
||||
: HttpOpenRequest (connection, httpRequestCmd.toWideCharPointer(),
|
||||
uc.lpszUrlPath, nullptr, nullptr, mimeTypes, flags, 0);
|
||||
}
|
||||
|
||||
if (request != nullptr)
|
||||
{
|
||||
INTERNET_BUFFERS buffers = {};
|
||||
buffers.dwStructSize = sizeof (INTERNET_BUFFERS);
|
||||
buffers.lpcszHeader = headers.toWideCharPointer();
|
||||
buffers.dwHeadersLength = (DWORD) headers.length();
|
||||
buffers.dwBufferTotal = (DWORD) postData.getSize();
|
||||
|
||||
auto sendRequestAndTryEnd = [this, &buffers, &listener]() -> bool
|
||||
{
|
||||
sendHTTPRequest (buffers, listener);
|
||||
|
||||
if (HttpEndRequest (request, nullptr, 0, 0))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
auto closed = sendRequestAndTryEnd();
|
||||
|
||||
// N.B. this is needed for some authenticated HTTP connections
|
||||
if (! closed && GetLastError() == ERROR_INTERNET_FORCE_RETRY)
|
||||
closed = sendRequestAndTryEnd();
|
||||
|
||||
if (closed)
|
||||
return;
|
||||
}
|
||||
|
||||
closeConnection();
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl)
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
struct GetAdaptersAddressesHelper
|
||||
{
|
||||
bool callGetAdaptersAddresses()
|
||||
{
|
||||
DynamicLibrary dll ("iphlpapi.dll");
|
||||
JUCE_LOAD_WINAPI_FUNCTION (dll, GetAdaptersAddresses, getAdaptersAddresses, DWORD, (ULONG, ULONG, PVOID, PIP_ADAPTER_ADDRESSES, PULONG))
|
||||
|
||||
if (getAdaptersAddresses == nullptr)
|
||||
return false;
|
||||
|
||||
adaptersAddresses.malloc (1);
|
||||
ULONG len = sizeof (IP_ADAPTER_ADDRESSES);
|
||||
|
||||
if (getAdaptersAddresses (AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, nullptr, adaptersAddresses, &len) == ERROR_BUFFER_OVERFLOW)
|
||||
adaptersAddresses.malloc (len, 1);
|
||||
|
||||
return getAdaptersAddresses (AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, nullptr, adaptersAddresses, &len) == NO_ERROR;
|
||||
}
|
||||
|
||||
HeapBlock<IP_ADAPTER_ADDRESSES> adaptersAddresses;
|
||||
};
|
||||
|
||||
namespace MACAddressHelpers
|
||||
{
|
||||
static void addAddress (Array<MACAddress>& result, const MACAddress& ma)
|
||||
{
|
||||
if (! ma.isNull())
|
||||
result.addIfNotAlreadyThere (ma);
|
||||
}
|
||||
|
||||
static void getViaGetAdaptersAddresses (Array<MACAddress>& result)
|
||||
{
|
||||
GetAdaptersAddressesHelper addressesHelper;
|
||||
|
||||
if (addressesHelper.callGetAdaptersAddresses())
|
||||
{
|
||||
for (PIP_ADAPTER_ADDRESSES adapter = addressesHelper.adaptersAddresses; adapter != nullptr; adapter = adapter->Next)
|
||||
{
|
||||
if (adapter->PhysicalAddressLength >= 6)
|
||||
addAddress (result, MACAddress (adapter->PhysicalAddress));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void getViaNetBios (Array<MACAddress>& result)
|
||||
{
|
||||
DynamicLibrary dll ("netapi32.dll");
|
||||
JUCE_LOAD_WINAPI_FUNCTION (dll, Netbios, NetbiosCall, UCHAR, (PNCB))
|
||||
|
||||
if (NetbiosCall != nullptr)
|
||||
{
|
||||
LANA_ENUM enums = {};
|
||||
|
||||
{
|
||||
NCB ncb = {};
|
||||
ncb.ncb_command = NCBENUM;
|
||||
ncb.ncb_buffer = (unsigned char*) &enums;
|
||||
ncb.ncb_length = sizeof (LANA_ENUM);
|
||||
NetbiosCall (&ncb);
|
||||
}
|
||||
|
||||
for (int i = 0; i < enums.length; ++i)
|
||||
{
|
||||
NCB ncb2 = {};
|
||||
ncb2.ncb_command = NCBRESET;
|
||||
ncb2.ncb_lana_num = enums.lana[i];
|
||||
|
||||
if (NetbiosCall (&ncb2) == 0)
|
||||
{
|
||||
NCB ncb = {};
|
||||
memcpy (ncb.ncb_callname, "* ", NCBNAMSZ);
|
||||
ncb.ncb_command = NCBASTAT;
|
||||
ncb.ncb_lana_num = enums.lana[i];
|
||||
|
||||
struct ASTAT
|
||||
{
|
||||
ADAPTER_STATUS adapt;
|
||||
NAME_BUFFER NameBuff [30];
|
||||
};
|
||||
|
||||
ASTAT astat;
|
||||
zerostruct (astat);
|
||||
ncb.ncb_buffer = (unsigned char*) &astat;
|
||||
ncb.ncb_length = sizeof (ASTAT);
|
||||
|
||||
if (NetbiosCall (&ncb) == 0 && astat.adapt.adapter_type == 0xfe)
|
||||
addAddress (result, MACAddress (astat.adapt.adapter_address));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void split (const sockaddr_in6* sa_in6, int off, uint8* split)
|
||||
{
|
||||
#if JUCE_MINGW
|
||||
split[0] = sa_in6->sin6_addr._S6_un._S6_u8[off + 1];
|
||||
split[1] = sa_in6->sin6_addr._S6_un._S6_u8[off];
|
||||
#else
|
||||
split[0] = sa_in6->sin6_addr.u.Byte[off + 1];
|
||||
split[1] = sa_in6->sin6_addr.u.Byte[off];
|
||||
#endif
|
||||
}
|
||||
|
||||
static IPAddress createAddress (const sockaddr_in6* sa_in6)
|
||||
{
|
||||
IPAddressByteUnion temp;
|
||||
uint16 arr[8];
|
||||
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
split (sa_in6, i * 2, temp.split);
|
||||
arr[i] = temp.combined;
|
||||
}
|
||||
|
||||
return IPAddress (arr);
|
||||
}
|
||||
|
||||
static IPAddress createAddress (const sockaddr_in* sa_in)
|
||||
{
|
||||
return IPAddress ((uint8*) &sa_in->sin_addr.s_addr, false);
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
static void findAddresses (Array<IPAddress>& result, bool includeIPv6, Type start)
|
||||
{
|
||||
for (auto addr = start; addr != nullptr; addr = addr->Next)
|
||||
{
|
||||
if (addr->Address.lpSockaddr->sa_family == AF_INET)
|
||||
result.addIfNotAlreadyThere (createAddress (unalignedPointerCast<sockaddr_in*> (addr->Address.lpSockaddr)));
|
||||
else if (addr->Address.lpSockaddr->sa_family == AF_INET6 && includeIPv6)
|
||||
result.addIfNotAlreadyThere (createAddress (unalignedPointerCast<sockaddr_in6*> (addr->Address.lpSockaddr)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MACAddress::findAllAddresses (Array<MACAddress>& result)
|
||||
{
|
||||
MACAddressHelpers::getViaGetAdaptersAddresses (result);
|
||||
MACAddressHelpers::getViaNetBios (result);
|
||||
}
|
||||
|
||||
void IPAddress::findAllAddresses (Array<IPAddress>& result, bool includeIPv6)
|
||||
{
|
||||
result.addIfNotAlreadyThere (IPAddress::local());
|
||||
|
||||
if (includeIPv6)
|
||||
result.addIfNotAlreadyThere (IPAddress::local (true));
|
||||
|
||||
GetAdaptersAddressesHelper addressesHelper;
|
||||
|
||||
if (addressesHelper.callGetAdaptersAddresses())
|
||||
{
|
||||
for (PIP_ADAPTER_ADDRESSES adapter = addressesHelper.adaptersAddresses; adapter != nullptr; adapter = adapter->Next)
|
||||
{
|
||||
MACAddressHelpers::findAddresses (result, includeIPv6, adapter->FirstUnicastAddress);
|
||||
MACAddressHelpers::findAddresses (result, includeIPv6, adapter->FirstAnycastAddress);
|
||||
MACAddressHelpers::findAddresses (result, includeIPv6, adapter->FirstMulticastAddress);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IPAddress::findAllInterfaceAddresses (Array<IPAddressInterfaceNamePair>& result, bool includeIPv6)
|
||||
{
|
||||
// TBD
|
||||
#if 0
|
||||
result.addIfNotAlreadyThere (IPAddress::local());
|
||||
|
||||
if (includeIPv6)
|
||||
result.addIfNotAlreadyThere (IPAddress::local (true));
|
||||
|
||||
GetAdaptersAddressesHelper addressesHelper;
|
||||
|
||||
if (addressesHelper.callGetAdaptersAddresses())
|
||||
{
|
||||
for (PIP_ADAPTER_ADDRESSES adapter = addressesHelper.adaptersAddresses; adapter != nullptr; adapter = adapter->Next)
|
||||
{
|
||||
MACAddressHelpers::findAddresses (result, includeIPv6, adapter->FirstUnicastAddress);
|
||||
MACAddressHelpers::findAddresses (result, includeIPv6, adapter->FirstAnycastAddress);
|
||||
MACAddressHelpers::findAddresses (result, includeIPv6, adapter->FirstMulticastAddress);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
IPAddress IPAddress::getInterfaceBroadcastAddress (const IPAddress&)
|
||||
{
|
||||
// TODO
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
bool JUCE_CALLTYPE Process::openEmailWithAttachments (const String& targetEmailAddress,
|
||||
const String& emailSubject,
|
||||
const String& bodyText,
|
||||
const StringArray& filesToAttach)
|
||||
{
|
||||
DynamicLibrary dll ("MAPI32.dll");
|
||||
JUCE_LOAD_WINAPI_FUNCTION (dll, MAPISendMail, mapiSendMail,
|
||||
ULONG, (LHANDLE, ULONG, lpMapiMessage, ::FLAGS, ULONG))
|
||||
|
||||
if (mapiSendMail == nullptr)
|
||||
return false;
|
||||
|
||||
MapiMessage message = {};
|
||||
message.lpszSubject = (LPSTR) emailSubject.toRawUTF8();
|
||||
message.lpszNoteText = (LPSTR) bodyText.toRawUTF8();
|
||||
|
||||
MapiRecipDesc recip = {};
|
||||
recip.ulRecipClass = MAPI_TO;
|
||||
String targetEmailAddress_ (targetEmailAddress);
|
||||
if (targetEmailAddress_.isEmpty())
|
||||
targetEmailAddress_ = " "; // (Windows Mail can't deal with a blank address)
|
||||
recip.lpszName = (LPSTR) targetEmailAddress_.toRawUTF8();
|
||||
message.nRecipCount = 1;
|
||||
message.lpRecips = &recip;
|
||||
|
||||
HeapBlock<MapiFileDesc> files;
|
||||
files.calloc (filesToAttach.size());
|
||||
|
||||
message.nFileCount = (ULONG) filesToAttach.size();
|
||||
message.lpFiles = files;
|
||||
|
||||
for (int i = 0; i < filesToAttach.size(); ++i)
|
||||
{
|
||||
files[i].nPosition = (ULONG) -1;
|
||||
files[i].lpszPathName = (LPSTR) filesToAttach[i].toRawUTF8();
|
||||
}
|
||||
|
||||
return mapiSendMail (0, 0, &message, MAPI_DIALOG | MAPI_LOGON_UI, 0) == SUCCESS_SUCCESS;
|
||||
}
|
||||
|
||||
std::unique_ptr<URL::DownloadTask> URL::downloadToFile (const File& targetLocation, const DownloadTaskOptions& options)
|
||||
{
|
||||
return URL::DownloadTask::createFallbackDownloader (*this, targetLocation, options);
|
||||
}
|
||||
|
||||
} // namespace juce
|
251
deps/juce/modules/juce_core/native/juce_win32_Registry.cpp
vendored
Normal file
251
deps/juce/modules/juce_core/native/juce_win32_Registry.cpp
vendored
Normal file
@ -0,0 +1,251 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
struct RegistryKeyWrapper
|
||||
{
|
||||
RegistryKeyWrapper (String name, bool createForWriting, DWORD wow64Flags)
|
||||
{
|
||||
if (HKEY rootKey = getRootKey (name))
|
||||
{
|
||||
name = name.substring (name.indexOfChar ('\\') + 1);
|
||||
|
||||
auto lastSlash = name.lastIndexOfChar ('\\');
|
||||
valueName = name.substring (lastSlash + 1);
|
||||
wideCharValueName = valueName.toWideCharPointer();
|
||||
|
||||
name = name.substring (0, lastSlash);
|
||||
auto wideCharName = name.toWideCharPointer();
|
||||
DWORD result;
|
||||
|
||||
if (createForWriting)
|
||||
RegCreateKeyEx (rootKey, wideCharName, 0, nullptr, REG_OPTION_NON_VOLATILE,
|
||||
KEY_WRITE | KEY_QUERY_VALUE | wow64Flags, nullptr, &key, &result);
|
||||
else
|
||||
RegOpenKeyEx (rootKey, wideCharName, 0, KEY_READ | wow64Flags, &key);
|
||||
}
|
||||
}
|
||||
|
||||
~RegistryKeyWrapper()
|
||||
{
|
||||
if (key != nullptr)
|
||||
RegCloseKey (key);
|
||||
}
|
||||
|
||||
static HKEY getRootKey (const String& name) noexcept
|
||||
{
|
||||
if (name.startsWithIgnoreCase ("HKEY_CURRENT_USER\\")) return HKEY_CURRENT_USER;
|
||||
if (name.startsWithIgnoreCase ("HKCU\\")) return HKEY_CURRENT_USER;
|
||||
if (name.startsWithIgnoreCase ("HKEY_LOCAL_MACHINE\\")) return HKEY_LOCAL_MACHINE;
|
||||
if (name.startsWithIgnoreCase ("HKLM\\")) return HKEY_LOCAL_MACHINE;
|
||||
if (name.startsWithIgnoreCase ("HKEY_CLASSES_ROOT\\")) return HKEY_CLASSES_ROOT;
|
||||
if (name.startsWithIgnoreCase ("HKCR\\")) return HKEY_CLASSES_ROOT;
|
||||
if (name.startsWithIgnoreCase ("HKEY_USERS\\")) return HKEY_USERS;
|
||||
if (name.startsWithIgnoreCase ("HKU\\")) return HKEY_USERS;
|
||||
|
||||
jassertfalse; // The name starts with an unknown root key (or maybe an old Win9x type)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static bool setValue (const String& regValuePath, const DWORD type,
|
||||
const void* data, size_t dataSize, const DWORD wow64Flags)
|
||||
{
|
||||
const RegistryKeyWrapper key (regValuePath, true, wow64Flags);
|
||||
|
||||
return key.key != nullptr
|
||||
&& RegSetValueEx (key.key, key.wideCharValueName, 0, type,
|
||||
reinterpret_cast<const BYTE*> (data),
|
||||
(DWORD) dataSize) == ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
static uint32 getBinaryValue (const String& regValuePath, MemoryBlock& result, DWORD wow64Flags)
|
||||
{
|
||||
const RegistryKeyWrapper key (regValuePath, false, wow64Flags);
|
||||
|
||||
if (key.key != nullptr)
|
||||
{
|
||||
for (unsigned long bufferSize = 1024; ; bufferSize *= 2)
|
||||
{
|
||||
result.setSize (bufferSize, false);
|
||||
DWORD type = REG_NONE;
|
||||
|
||||
auto err = RegQueryValueEx (key.key, key.wideCharValueName, nullptr, &type,
|
||||
(LPBYTE) result.getData(), &bufferSize);
|
||||
|
||||
if (err == ERROR_SUCCESS)
|
||||
{
|
||||
result.setSize (bufferSize, false);
|
||||
return type;
|
||||
}
|
||||
|
||||
if (err != ERROR_MORE_DATA)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return REG_NONE;
|
||||
}
|
||||
|
||||
static String getValue (const String& regValuePath, const String& defaultValue, DWORD wow64Flags)
|
||||
{
|
||||
MemoryBlock buffer;
|
||||
|
||||
switch (getBinaryValue (regValuePath, buffer, wow64Flags))
|
||||
{
|
||||
case REG_SZ: return static_cast<const WCHAR*> (buffer.getData());
|
||||
case REG_DWORD: return String ((int) *reinterpret_cast<const DWORD*> (buffer.getData()));
|
||||
default: break;
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
static bool keyExists (const String& regKeyPath, const DWORD wow64Flags)
|
||||
{
|
||||
return RegistryKeyWrapper (regKeyPath + "\\", false, wow64Flags).key != nullptr;
|
||||
}
|
||||
|
||||
static bool valueExists (const String& regValuePath, const DWORD wow64Flags)
|
||||
{
|
||||
const RegistryKeyWrapper key (regValuePath, false, wow64Flags);
|
||||
|
||||
if (key.key == nullptr)
|
||||
return false;
|
||||
|
||||
unsigned char buffer [512];
|
||||
unsigned long bufferSize = sizeof (buffer);
|
||||
DWORD type = 0;
|
||||
|
||||
auto result = RegQueryValueEx (key.key, key.wideCharValueName,
|
||||
nullptr, &type, buffer, &bufferSize);
|
||||
|
||||
return result == ERROR_SUCCESS || result == ERROR_MORE_DATA;
|
||||
}
|
||||
|
||||
HKEY key = nullptr;
|
||||
const wchar_t* wideCharValueName = nullptr;
|
||||
String valueName;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (RegistryKeyWrapper)
|
||||
};
|
||||
|
||||
uint32 JUCE_CALLTYPE WindowsRegistry::getBinaryValue (const String& regValuePath, MemoryBlock& result, WoW64Mode mode)
|
||||
{
|
||||
return RegistryKeyWrapper::getBinaryValue (regValuePath, result, (DWORD) mode);
|
||||
}
|
||||
|
||||
String JUCE_CALLTYPE WindowsRegistry::getValue (const String& regValuePath, const String& defaultValue, WoW64Mode mode)
|
||||
{
|
||||
return RegistryKeyWrapper::getValue (regValuePath, defaultValue, (DWORD) mode);
|
||||
}
|
||||
|
||||
bool JUCE_CALLTYPE WindowsRegistry::setValue (const String& regValuePath, const String& value, WoW64Mode mode)
|
||||
{
|
||||
return RegistryKeyWrapper::setValue (regValuePath, REG_SZ, value.toWideCharPointer(),
|
||||
CharPointer_UTF16::getBytesRequiredFor (value.getCharPointer()), mode);
|
||||
}
|
||||
|
||||
bool JUCE_CALLTYPE WindowsRegistry::setValue (const String& regValuePath, const uint32 value, WoW64Mode mode)
|
||||
{
|
||||
return RegistryKeyWrapper::setValue (regValuePath, REG_DWORD, &value, sizeof (value), (DWORD) mode);
|
||||
}
|
||||
|
||||
bool JUCE_CALLTYPE WindowsRegistry::setValue (const String& regValuePath, const uint64 value, WoW64Mode mode)
|
||||
{
|
||||
return RegistryKeyWrapper::setValue (regValuePath, REG_QWORD, &value, sizeof (value), (DWORD) mode);
|
||||
}
|
||||
|
||||
bool JUCE_CALLTYPE WindowsRegistry::setValue (const String& regValuePath, const MemoryBlock& value, WoW64Mode mode)
|
||||
{
|
||||
return RegistryKeyWrapper::setValue (regValuePath, REG_BINARY, value.getData(), value.getSize(), (DWORD) mode);
|
||||
}
|
||||
|
||||
bool JUCE_CALLTYPE WindowsRegistry::valueExists (const String& regValuePath, WoW64Mode mode)
|
||||
{
|
||||
return RegistryKeyWrapper::valueExists (regValuePath, (DWORD) mode);
|
||||
}
|
||||
|
||||
bool JUCE_CALLTYPE WindowsRegistry::keyExists (const String& regKeyPath, WoW64Mode mode)
|
||||
{
|
||||
return RegistryKeyWrapper::keyExists (regKeyPath, (DWORD) mode);
|
||||
}
|
||||
|
||||
bool JUCE_CALLTYPE WindowsRegistry::deleteValue (const String& regValuePath, WoW64Mode mode)
|
||||
{
|
||||
const RegistryKeyWrapper key (regValuePath, true, (DWORD) mode);
|
||||
|
||||
return key.key != nullptr && RegDeleteValue (key.key, key.wideCharValueName) == ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
static bool deleteKeyNonRecursive (const String& regKeyPath, WindowsRegistry::WoW64Mode mode)
|
||||
{
|
||||
const RegistryKeyWrapper key (regKeyPath, true, (DWORD) mode);
|
||||
|
||||
return key.key != nullptr && RegDeleteKey (key.key, key.wideCharValueName) == ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
bool JUCE_CALLTYPE WindowsRegistry::deleteKey (const String& regKeyPath, WoW64Mode mode)
|
||||
{
|
||||
if (deleteKeyNonRecursive (regKeyPath, mode))
|
||||
return true;
|
||||
|
||||
for (const RegistryKeyWrapper key (regKeyPath + "\\", false, (DWORD) mode);;)
|
||||
{
|
||||
wchar_t subKey[MAX_PATH + 1] = {};
|
||||
DWORD subKeySize = MAX_PATH;
|
||||
|
||||
if (RegEnumKeyEx (key.key, 0, subKey, &subKeySize, nullptr, nullptr, nullptr, nullptr) != ERROR_SUCCESS
|
||||
|| ! deleteKey (regKeyPath + "\\" + String (subKey), mode))
|
||||
break;
|
||||
}
|
||||
|
||||
return deleteKeyNonRecursive (regKeyPath, mode);
|
||||
}
|
||||
|
||||
bool JUCE_CALLTYPE WindowsRegistry::registerFileAssociation (const String& fileExtension,
|
||||
const String& symbolicDescription,
|
||||
const String& fullDescription,
|
||||
const File& targetExecutable,
|
||||
const int iconResourceNumber,
|
||||
const bool registerForCurrentUserOnly,
|
||||
WoW64Mode mode)
|
||||
{
|
||||
auto root = registerForCurrentUserOnly ? "HKEY_CURRENT_USER\\Software\\Classes\\"
|
||||
: "HKEY_CLASSES_ROOT\\";
|
||||
auto key = root + symbolicDescription;
|
||||
|
||||
return setValue (root + fileExtension + "\\", symbolicDescription, mode)
|
||||
&& setValue (key + "\\", fullDescription, mode)
|
||||
&& setValue (key + "\\shell\\open\\command\\", targetExecutable.getFullPathName() + " \"%1\"", mode)
|
||||
&& (iconResourceNumber == 0
|
||||
|| setValue (key + "\\DefaultIcon\\",
|
||||
targetExecutable.getFullPathName() + "," + String (iconResourceNumber)));
|
||||
}
|
||||
|
||||
// These methods are deprecated:
|
||||
String WindowsRegistry::getValueWow64 (const String& p, const String& defVal) { return getValue (p, defVal, WoW64_64bit); }
|
||||
bool WindowsRegistry::valueExistsWow64 (const String& p) { return valueExists (p, WoW64_64bit); }
|
||||
bool WindowsRegistry::keyExistsWow64 (const String& p) { return keyExists (p, WoW64_64bit); }
|
||||
|
||||
} // namespace juce
|
590
deps/juce/modules/juce_core/native/juce_win32_SystemStats.cpp
vendored
Normal file
590
deps/juce/modules/juce_core/native/juce_win32_SystemStats.cpp
vendored
Normal file
@ -0,0 +1,590 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#if JUCE_MSVC && ! defined (__INTEL_COMPILER)
|
||||
#pragma intrinsic (__cpuid)
|
||||
#pragma intrinsic (__rdtsc)
|
||||
#endif
|
||||
|
||||
void Logger::outputDebugString (const String& text)
|
||||
{
|
||||
OutputDebugString ((text + "\n").toWideCharPointer());
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
#ifdef JUCE_DLL_BUILD
|
||||
JUCE_API void* juceDLL_malloc (size_t sz) { return std::malloc (sz); }
|
||||
JUCE_API void juceDLL_free (void* block) { std::free (block); }
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
|
||||
#if JUCE_MINGW || JUCE_CLANG
|
||||
static void callCPUID (int result[4], uint32 type)
|
||||
{
|
||||
uint32 la = (uint32) result[0], lb = (uint32) result[1],
|
||||
lc = (uint32) result[2], ld = (uint32) result[3];
|
||||
|
||||
asm ("mov %%ebx, %%esi \n\t"
|
||||
"cpuid \n\t"
|
||||
"xchg %%esi, %%ebx"
|
||||
: "=a" (la), "=S" (lb), "=c" (lc), "=d" (ld) : "a" (type)
|
||||
#if JUCE_64BIT
|
||||
, "b" (lb), "c" (lc), "d" (ld)
|
||||
#endif
|
||||
);
|
||||
|
||||
result[0] = (int) la; result[1] = (int) lb;
|
||||
result[2] = (int) lc; result[3] = (int) ld;
|
||||
}
|
||||
#else
|
||||
static void callCPUID (int result[4], int infoType)
|
||||
{
|
||||
__cpuid (result, infoType);
|
||||
}
|
||||
#endif
|
||||
|
||||
String SystemStats::getCpuVendor()
|
||||
{
|
||||
int info[4] = { 0 };
|
||||
callCPUID (info, 0);
|
||||
|
||||
char v [12];
|
||||
memcpy (v, info + 1, 4);
|
||||
memcpy (v + 4, info + 3, 4);
|
||||
memcpy (v + 8, info + 2, 4);
|
||||
|
||||
return String (v, 12);
|
||||
}
|
||||
|
||||
String SystemStats::getCpuModel()
|
||||
{
|
||||
char name[65] = { 0 };
|
||||
int info[4] = { 0 };
|
||||
|
||||
callCPUID (info, 0x80000000);
|
||||
|
||||
const int numExtIDs = info[0];
|
||||
|
||||
if ((unsigned) numExtIDs < 0x80000004) // if brand string is unsupported
|
||||
return {};
|
||||
|
||||
callCPUID (info, 0x80000002);
|
||||
memcpy (name, info, sizeof (info));
|
||||
|
||||
callCPUID (info, 0x80000003);
|
||||
memcpy (name + 16, info, sizeof (info));
|
||||
|
||||
callCPUID (info, 0x80000004);
|
||||
memcpy (name + 32, info, sizeof (info));
|
||||
|
||||
return String (name).trim();
|
||||
}
|
||||
|
||||
static int findNumberOfPhysicalCores() noexcept
|
||||
{
|
||||
#if JUCE_MINGW
|
||||
// Not implemented in MinGW
|
||||
jassertfalse;
|
||||
|
||||
return 1;
|
||||
#else
|
||||
|
||||
int numPhysicalCores = 0;
|
||||
DWORD bufferSize = 0;
|
||||
GetLogicalProcessorInformation (nullptr, &bufferSize);
|
||||
|
||||
if (auto numBuffers = (size_t) (bufferSize / sizeof (SYSTEM_LOGICAL_PROCESSOR_INFORMATION)))
|
||||
{
|
||||
HeapBlock<SYSTEM_LOGICAL_PROCESSOR_INFORMATION> buffer (numBuffers);
|
||||
|
||||
if (GetLogicalProcessorInformation (buffer, &bufferSize))
|
||||
for (size_t i = 0; i < numBuffers; ++i)
|
||||
if (buffer[i].Relationship == RelationProcessorCore)
|
||||
++numPhysicalCores;
|
||||
}
|
||||
|
||||
return numPhysicalCores;
|
||||
#endif // JUCE_MINGW
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void CPUInformation::initialise() noexcept
|
||||
{
|
||||
int info[4] = { 0 };
|
||||
callCPUID (info, 1);
|
||||
|
||||
// NB: IsProcessorFeaturePresent doesn't work on XP
|
||||
hasMMX = (info[3] & (1 << 23)) != 0;
|
||||
hasSSE = (info[3] & (1 << 25)) != 0;
|
||||
hasSSE2 = (info[3] & (1 << 26)) != 0;
|
||||
hasSSE3 = (info[2] & (1 << 0)) != 0;
|
||||
hasAVX = (info[2] & (1 << 28)) != 0;
|
||||
hasFMA3 = (info[2] & (1 << 12)) != 0;
|
||||
hasSSSE3 = (info[2] & (1 << 9)) != 0;
|
||||
hasSSE41 = (info[2] & (1 << 19)) != 0;
|
||||
hasSSE42 = (info[2] & (1 << 20)) != 0;
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wshift-sign-overflow")
|
||||
has3DNow = (info[1] & (1 << 31)) != 0;
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
|
||||
callCPUID (info, 0x80000001);
|
||||
hasFMA4 = (info[2] & (1 << 16)) != 0;
|
||||
|
||||
callCPUID (info, 7);
|
||||
|
||||
hasAVX2 = ((unsigned int) info[1] & (1 << 5)) != 0;
|
||||
hasAVX512F = ((unsigned int) info[1] & (1u << 16)) != 0;
|
||||
hasAVX512DQ = ((unsigned int) info[1] & (1u << 17)) != 0;
|
||||
hasAVX512IFMA = ((unsigned int) info[1] & (1u << 21)) != 0;
|
||||
hasAVX512PF = ((unsigned int) info[1] & (1u << 26)) != 0;
|
||||
hasAVX512ER = ((unsigned int) info[1] & (1u << 27)) != 0;
|
||||
hasAVX512CD = ((unsigned int) info[1] & (1u << 28)) != 0;
|
||||
hasAVX512BW = ((unsigned int) info[1] & (1u << 30)) != 0;
|
||||
hasAVX512VL = ((unsigned int) info[1] & (1u << 31)) != 0;
|
||||
hasAVX512VBMI = ((unsigned int) info[2] & (1u << 1)) != 0;
|
||||
hasAVX512VPOPCNTDQ = ((unsigned int) info[2] & (1u << 14)) != 0;
|
||||
|
||||
SYSTEM_INFO systemInfo;
|
||||
GetNativeSystemInfo (&systemInfo);
|
||||
numLogicalCPUs = (int) systemInfo.dwNumberOfProcessors;
|
||||
numPhysicalCPUs = findNumberOfPhysicalCores();
|
||||
|
||||
if (numPhysicalCPUs <= 0)
|
||||
numPhysicalCPUs = numLogicalCPUs;
|
||||
}
|
||||
|
||||
#if JUCE_MSVC && JUCE_CHECK_MEMORY_LEAKS
|
||||
struct DebugFlagsInitialiser
|
||||
{
|
||||
DebugFlagsInitialiser()
|
||||
{
|
||||
_CrtSetDbgFlag (_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
|
||||
}
|
||||
};
|
||||
|
||||
static DebugFlagsInitialiser debugFlagsInitialiser;
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_MINGW
|
||||
static uint32 getWindowsVersion()
|
||||
{
|
||||
auto filename = _T("kernel32.dll");
|
||||
DWORD handle = 0;
|
||||
|
||||
if (auto size = GetFileVersionInfoSize (filename, &handle))
|
||||
{
|
||||
HeapBlock<char> data (size);
|
||||
|
||||
if (GetFileVersionInfo (filename, handle, size, data))
|
||||
{
|
||||
VS_FIXEDFILEINFO* info = nullptr;
|
||||
UINT verSize = 0;
|
||||
|
||||
if (VerQueryValue (data, (LPCTSTR) _T("\\"), (void**) &info, &verSize))
|
||||
if (size > 0 && info != nullptr && info->dwSignature == 0xfeef04bd)
|
||||
return (uint32) info->dwFileVersionMS;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
RTL_OSVERSIONINFOW getWindowsVersionInfo()
|
||||
{
|
||||
RTL_OSVERSIONINFOW versionInfo = {};
|
||||
|
||||
if (auto* moduleHandle = ::GetModuleHandleW (L"ntdll.dll"))
|
||||
{
|
||||
using RtlGetVersion = LONG (WINAPI*) (PRTL_OSVERSIONINFOW);
|
||||
|
||||
if (auto* rtlGetVersion = (RtlGetVersion) ::GetProcAddress (moduleHandle, "RtlGetVersion"))
|
||||
{
|
||||
versionInfo.dwOSVersionInfoSize = sizeof (versionInfo);
|
||||
LONG STATUS_SUCCESS = 0;
|
||||
|
||||
if (rtlGetVersion (&versionInfo) != STATUS_SUCCESS)
|
||||
versionInfo = {};
|
||||
}
|
||||
}
|
||||
|
||||
return versionInfo;
|
||||
}
|
||||
#endif
|
||||
|
||||
SystemStats::OperatingSystemType SystemStats::getOperatingSystemType()
|
||||
{
|
||||
#if JUCE_MINGW
|
||||
auto v = getWindowsVersion();
|
||||
auto major = (v >> 16) & 0xff;
|
||||
auto minor = (v >> 0) & 0xff;
|
||||
#else
|
||||
auto versionInfo = getWindowsVersionInfo();
|
||||
auto major = versionInfo.dwMajorVersion;
|
||||
auto minor = versionInfo.dwMinorVersion;
|
||||
#endif
|
||||
|
||||
jassert (major <= 10); // need to add support for new version!
|
||||
|
||||
if (major == 10) return Windows10;
|
||||
if (major == 6 && minor == 3) return Windows8_1;
|
||||
if (major == 6 && minor == 2) return Windows8_0;
|
||||
if (major == 6 && minor == 1) return Windows7;
|
||||
if (major == 6 && minor == 0) return WinVista;
|
||||
if (major == 5 && minor == 1) return WinXP;
|
||||
if (major == 5 && minor == 0) return Win2000;
|
||||
|
||||
jassertfalse;
|
||||
return UnknownOS;
|
||||
}
|
||||
|
||||
String SystemStats::getOperatingSystemName()
|
||||
{
|
||||
const char* name = "Unknown OS";
|
||||
|
||||
switch (getOperatingSystemType())
|
||||
{
|
||||
case Windows10: name = "Windows 10"; break;
|
||||
case Windows8_1: name = "Windows 8.1"; break;
|
||||
case Windows8_0: name = "Windows 8.0"; break;
|
||||
case Windows7: name = "Windows 7"; break;
|
||||
case WinVista: name = "Windows Vista"; break;
|
||||
case WinXP: name = "Windows XP"; break;
|
||||
case Win2000: name = "Windows 2000"; break;
|
||||
|
||||
case MacOSX: JUCE_FALLTHROUGH
|
||||
case Windows: JUCE_FALLTHROUGH
|
||||
case Linux: JUCE_FALLTHROUGH
|
||||
case Android: JUCE_FALLTHROUGH
|
||||
case iOS: JUCE_FALLTHROUGH
|
||||
|
||||
case MacOSX_10_7: JUCE_FALLTHROUGH
|
||||
case MacOSX_10_8: JUCE_FALLTHROUGH
|
||||
case MacOSX_10_9: JUCE_FALLTHROUGH
|
||||
case MacOSX_10_10: JUCE_FALLTHROUGH
|
||||
case MacOSX_10_11: JUCE_FALLTHROUGH
|
||||
case MacOSX_10_12: JUCE_FALLTHROUGH
|
||||
case MacOSX_10_13: JUCE_FALLTHROUGH
|
||||
case MacOSX_10_14: JUCE_FALLTHROUGH
|
||||
case MacOSX_10_15: JUCE_FALLTHROUGH
|
||||
case MacOS_11: JUCE_FALLTHROUGH
|
||||
case MacOS_12: JUCE_FALLTHROUGH
|
||||
|
||||
case UnknownOS: JUCE_FALLTHROUGH
|
||||
case WASM: JUCE_FALLTHROUGH
|
||||
default: jassertfalse; break; // !! new type of OS?
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
String SystemStats::getDeviceDescription()
|
||||
{
|
||||
#if WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP
|
||||
return "Windows (Desktop)";
|
||||
#elif WINAPI_FAMILY == WINAPI_FAMILY_PC_APP
|
||||
return "Windows (Store)";
|
||||
#elif WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP
|
||||
return "Windows (Phone)";
|
||||
#elif WINAPI_FAMILY == WINAPI_FAMILY_SYSTEM
|
||||
return "Windows (System)";
|
||||
#elif WINAPI_FAMILY == WINAPI_FAMILY_SERVER
|
||||
return "Windows (Server)";
|
||||
#else
|
||||
return "Windows";
|
||||
#endif
|
||||
}
|
||||
|
||||
String SystemStats::getDeviceManufacturer()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
bool SystemStats::isOperatingSystem64Bit()
|
||||
{
|
||||
#if JUCE_64BIT
|
||||
return true;
|
||||
#else
|
||||
typedef BOOL (WINAPI* LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
|
||||
|
||||
const auto moduleHandle = GetModuleHandleA ("kernel32");
|
||||
|
||||
if (moduleHandle == nullptr)
|
||||
{
|
||||
jassertfalse;
|
||||
return false;
|
||||
}
|
||||
|
||||
LPFN_ISWOW64PROCESS fnIsWow64Process
|
||||
= (LPFN_ISWOW64PROCESS) GetProcAddress (moduleHandle, "IsWow64Process");
|
||||
|
||||
BOOL isWow64 = FALSE;
|
||||
|
||||
return fnIsWow64Process != nullptr
|
||||
&& fnIsWow64Process (GetCurrentProcess(), &isWow64)
|
||||
&& isWow64 != FALSE;
|
||||
#endif
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
int SystemStats::getMemorySizeInMegabytes()
|
||||
{
|
||||
MEMORYSTATUSEX mem;
|
||||
mem.dwLength = sizeof (mem);
|
||||
GlobalMemoryStatusEx (&mem);
|
||||
return (int) (mem.ullTotalPhys / (1024 * 1024)) + 1;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
String SystemStats::getEnvironmentVariable (const String& name, const String& defaultValue)
|
||||
{
|
||||
auto len = GetEnvironmentVariableW (name.toWideCharPointer(), nullptr, 0);
|
||||
|
||||
if (len == 0)
|
||||
return String (defaultValue);
|
||||
|
||||
HeapBlock<WCHAR> buffer (len);
|
||||
len = GetEnvironmentVariableW (name.toWideCharPointer(), buffer, len);
|
||||
|
||||
return String (CharPointer_wchar_t (buffer),
|
||||
CharPointer_wchar_t (buffer + len));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
uint32 juce_millisecondsSinceStartup() noexcept
|
||||
{
|
||||
return (uint32) timeGetTime();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class HiResCounterHandler
|
||||
{
|
||||
public:
|
||||
HiResCounterHandler()
|
||||
: hiResTicksOffset (0)
|
||||
{
|
||||
// This macro allows you to override the default timer-period
|
||||
// used on Windows. By default this is set to 1, because that has
|
||||
// always been the value used in JUCE apps, and changing it could
|
||||
// affect the behaviour of existing code, but you may wish to make
|
||||
// it larger (or set it to 0 to use the system default) to make your
|
||||
// app less demanding on the CPU.
|
||||
// For more info, see win32 documentation about the timeBeginPeriod
|
||||
// function.
|
||||
#ifndef JUCE_WIN32_TIMER_PERIOD
|
||||
#define JUCE_WIN32_TIMER_PERIOD 1
|
||||
#endif
|
||||
|
||||
#if JUCE_WIN32_TIMER_PERIOD > 0
|
||||
auto res = timeBeginPeriod (JUCE_WIN32_TIMER_PERIOD);
|
||||
ignoreUnused (res);
|
||||
jassert (res == TIMERR_NOERROR);
|
||||
#endif
|
||||
|
||||
LARGE_INTEGER f;
|
||||
QueryPerformanceFrequency (&f);
|
||||
hiResTicksPerSecond = f.QuadPart;
|
||||
hiResTicksScaleFactor = 1000.0 / (double) hiResTicksPerSecond;
|
||||
}
|
||||
|
||||
inline int64 getHighResolutionTicks() noexcept
|
||||
{
|
||||
LARGE_INTEGER ticks;
|
||||
QueryPerformanceCounter (&ticks);
|
||||
return ticks.QuadPart + hiResTicksOffset;
|
||||
}
|
||||
|
||||
inline double getMillisecondCounterHiRes() noexcept
|
||||
{
|
||||
return (double) getHighResolutionTicks() * hiResTicksScaleFactor;
|
||||
}
|
||||
|
||||
int64 hiResTicksPerSecond, hiResTicksOffset;
|
||||
double hiResTicksScaleFactor;
|
||||
};
|
||||
|
||||
static HiResCounterHandler hiResCounterHandler;
|
||||
|
||||
int64 Time::getHighResolutionTicksPerSecond() noexcept { return hiResCounterHandler.hiResTicksPerSecond; }
|
||||
int64 Time::getHighResolutionTicks() noexcept { return hiResCounterHandler.getHighResolutionTicks(); }
|
||||
double Time::getMillisecondCounterHiRes() noexcept { return hiResCounterHandler.getMillisecondCounterHiRes(); }
|
||||
|
||||
//==============================================================================
|
||||
static int64 juce_getClockCycleCounter() noexcept
|
||||
{
|
||||
#if JUCE_MSVC
|
||||
// MS intrinsics version...
|
||||
return (int64) __rdtsc();
|
||||
|
||||
#elif JUCE_GCC || JUCE_CLANG
|
||||
// GNU inline asm version...
|
||||
unsigned int hi = 0, lo = 0;
|
||||
|
||||
__asm__ __volatile__ (
|
||||
"xor %%eax, %%eax \n\
|
||||
xor %%edx, %%edx \n\
|
||||
rdtsc \n\
|
||||
movl %%eax, %[lo] \n\
|
||||
movl %%edx, %[hi]"
|
||||
:
|
||||
: [hi] "m" (hi),
|
||||
[lo] "m" (lo)
|
||||
: "cc", "eax", "ebx", "ecx", "edx", "memory");
|
||||
|
||||
return (int64) ((((uint64) hi) << 32) | lo);
|
||||
#else
|
||||
#error "unknown compiler?"
|
||||
#endif
|
||||
}
|
||||
|
||||
int SystemStats::getCpuSpeedInMegahertz()
|
||||
{
|
||||
auto cycles = juce_getClockCycleCounter();
|
||||
auto millis = Time::getMillisecondCounter();
|
||||
int lastResult = 0;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
int n = 1000000;
|
||||
while (--n > 0) {}
|
||||
|
||||
auto millisElapsed = Time::getMillisecondCounter() - millis;
|
||||
auto cyclesNow = juce_getClockCycleCounter();
|
||||
|
||||
if (millisElapsed > 80)
|
||||
{
|
||||
auto newResult = (int) (((cyclesNow - cycles) / millisElapsed) / 1000);
|
||||
|
||||
if (millisElapsed > 500 || (lastResult == newResult && newResult > 100))
|
||||
return newResult;
|
||||
|
||||
lastResult = newResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
bool Time::setSystemTimeToThisTime() const
|
||||
{
|
||||
SYSTEMTIME st;
|
||||
|
||||
st.wDayOfWeek = 0;
|
||||
st.wYear = (WORD) getYear();
|
||||
st.wMonth = (WORD) (getMonth() + 1);
|
||||
st.wDay = (WORD) getDayOfMonth();
|
||||
st.wHour = (WORD) getHours();
|
||||
st.wMinute = (WORD) getMinutes();
|
||||
st.wSecond = (WORD) getSeconds();
|
||||
st.wMilliseconds = (WORD) (millisSinceEpoch % 1000);
|
||||
|
||||
// do this twice because of daylight saving conversion problems - the
|
||||
// first one sets it up, the second one kicks it in.
|
||||
// NB: the local variable is here to avoid analysers warning about having
|
||||
// two identical sub-expressions in the return statement
|
||||
auto firstCallToSetTimezone = SetLocalTime (&st) != 0;
|
||||
return firstCallToSetTimezone && SetLocalTime (&st) != 0;
|
||||
}
|
||||
|
||||
int SystemStats::getPageSize()
|
||||
{
|
||||
SYSTEM_INFO systemInfo;
|
||||
GetNativeSystemInfo (&systemInfo);
|
||||
|
||||
return (int) systemInfo.dwPageSize;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
String SystemStats::getLogonName()
|
||||
{
|
||||
TCHAR text [256] = { 0 };
|
||||
auto len = (DWORD) numElementsInArray (text) - 1;
|
||||
GetUserName (text, &len);
|
||||
return String (text, len);
|
||||
}
|
||||
|
||||
String SystemStats::getFullUserName()
|
||||
{
|
||||
return getLogonName();
|
||||
}
|
||||
|
||||
String SystemStats::getComputerName()
|
||||
{
|
||||
TCHAR text[128] = { 0 };
|
||||
auto len = (DWORD) numElementsInArray (text) - 1;
|
||||
GetComputerNameEx (ComputerNamePhysicalDnsHostname, text, &len);
|
||||
return String (text, len);
|
||||
}
|
||||
|
||||
static String getLocaleValue (LCID locale, LCTYPE key, const char* defaultValue)
|
||||
{
|
||||
TCHAR buffer [256] = { 0 };
|
||||
if (GetLocaleInfo (locale, key, buffer, 255) > 0)
|
||||
return buffer;
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
String SystemStats::getUserLanguage() { return getLocaleValue (LOCALE_USER_DEFAULT, LOCALE_SISO639LANGNAME, "en"); }
|
||||
String SystemStats::getUserRegion() { return getLocaleValue (LOCALE_USER_DEFAULT, LOCALE_SISO3166CTRYNAME, "US"); }
|
||||
|
||||
String SystemStats::getDisplayLanguage()
|
||||
{
|
||||
DynamicLibrary dll ("kernel32.dll");
|
||||
JUCE_LOAD_WINAPI_FUNCTION (dll,
|
||||
GetUserPreferredUILanguages,
|
||||
getUserPreferredUILanguages,
|
||||
BOOL,
|
||||
(DWORD, PULONG, PZZWSTR, PULONG))
|
||||
|
||||
constexpr auto defaultResult = "en";
|
||||
|
||||
if (getUserPreferredUILanguages == nullptr)
|
||||
return defaultResult;
|
||||
|
||||
ULONG numLanguages = 0;
|
||||
ULONG numCharsInLanguagesBuffer = 0;
|
||||
|
||||
// Retrieving the necessary buffer size for storing the list of languages
|
||||
if (! getUserPreferredUILanguages (MUI_LANGUAGE_NAME, &numLanguages, nullptr, &numCharsInLanguagesBuffer))
|
||||
return defaultResult;
|
||||
|
||||
std::vector<WCHAR> languagesBuffer (numCharsInLanguagesBuffer);
|
||||
const auto success = getUserPreferredUILanguages (MUI_LANGUAGE_NAME,
|
||||
&numLanguages,
|
||||
languagesBuffer.data(),
|
||||
&numCharsInLanguagesBuffer);
|
||||
|
||||
if (! success || numLanguages == 0)
|
||||
return defaultResult;
|
||||
|
||||
// The buffer contains a zero delimited list of languages, the first being
|
||||
// the currently displayed language.
|
||||
return languagesBuffer.data();
|
||||
}
|
||||
|
||||
} // namespace juce
|
582
deps/juce/modules/juce_core/native/juce_win32_Threads.cpp
vendored
Normal file
582
deps/juce/modules/juce_core/native/juce_win32_Threads.cpp
vendored
Normal file
@ -0,0 +1,582 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
The code included in this file is provided under the terms of the ISC license
|
||||
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
|
||||
To use, copy, modify, and/or distribute this software for any purpose with or
|
||||
without fee is hereby granted provided that the above copyright notice and
|
||||
this permission notice appear in all copies.
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
HWND juce_messageWindowHandle = nullptr; // (this is used by other parts of the codebase)
|
||||
|
||||
void* getUser32Function (const char* functionName)
|
||||
{
|
||||
HMODULE module = GetModuleHandleA ("user32.dll");
|
||||
|
||||
if (module != nullptr)
|
||||
return (void*) GetProcAddress (module, functionName);
|
||||
|
||||
jassertfalse;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
CriticalSection::CriticalSection() noexcept
|
||||
{
|
||||
// (just to check the MS haven't changed this structure and broken things...)
|
||||
static_assert (sizeof (CRITICAL_SECTION) <= sizeof (lock),
|
||||
"win32 lock array too small to hold CRITICAL_SECTION: please report this JUCE bug!");
|
||||
|
||||
InitializeCriticalSection ((CRITICAL_SECTION*) &lock);
|
||||
}
|
||||
|
||||
CriticalSection::~CriticalSection() noexcept { DeleteCriticalSection ((CRITICAL_SECTION*) &lock); }
|
||||
void CriticalSection::enter() const noexcept { EnterCriticalSection ((CRITICAL_SECTION*) &lock); }
|
||||
bool CriticalSection::tryEnter() const noexcept { return TryEnterCriticalSection ((CRITICAL_SECTION*) &lock) != FALSE; }
|
||||
void CriticalSection::exit() const noexcept { LeaveCriticalSection ((CRITICAL_SECTION*) &lock); }
|
||||
|
||||
|
||||
//==============================================================================
|
||||
void JUCE_API juce_threadEntryPoint (void*);
|
||||
|
||||
static unsigned int STDMETHODCALLTYPE threadEntryProc (void* userData)
|
||||
{
|
||||
if (juce_messageWindowHandle != nullptr)
|
||||
AttachThreadInput (GetWindowThreadProcessId (juce_messageWindowHandle, nullptr),
|
||||
GetCurrentThreadId(), TRUE);
|
||||
|
||||
juce_threadEntryPoint (userData);
|
||||
|
||||
_endthreadex (0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Thread::launchThread()
|
||||
{
|
||||
unsigned int newThreadId;
|
||||
threadHandle = (void*) _beginthreadex (nullptr, (unsigned int) threadStackSize,
|
||||
&threadEntryProc, this, 0, &newThreadId);
|
||||
threadId = (ThreadID) (pointer_sized_int) newThreadId;
|
||||
}
|
||||
|
||||
void Thread::closeThreadHandle()
|
||||
{
|
||||
CloseHandle ((HANDLE) threadHandle.get());
|
||||
threadId = nullptr;
|
||||
threadHandle = nullptr;
|
||||
}
|
||||
|
||||
void Thread::killThread()
|
||||
{
|
||||
if (threadHandle.get() != nullptr)
|
||||
{
|
||||
#if JUCE_DEBUG
|
||||
OutputDebugStringA ("** Warning - Forced thread termination **\n");
|
||||
#endif
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6258)
|
||||
TerminateThread (threadHandle.get(), 0);
|
||||
JUCE_END_IGNORE_WARNINGS_MSVC
|
||||
}
|
||||
}
|
||||
|
||||
void JUCE_CALLTYPE Thread::setCurrentThreadName (const String& name)
|
||||
{
|
||||
#if JUCE_DEBUG && JUCE_MSVC
|
||||
struct
|
||||
{
|
||||
DWORD dwType;
|
||||
LPCSTR szName;
|
||||
DWORD dwThreadID;
|
||||
DWORD dwFlags;
|
||||
} info;
|
||||
|
||||
info.dwType = 0x1000;
|
||||
info.szName = name.toUTF8();
|
||||
info.dwThreadID = GetCurrentThreadId();
|
||||
info.dwFlags = 0;
|
||||
|
||||
__try
|
||||
{
|
||||
RaiseException (0x406d1388 /*MS_VC_EXCEPTION*/, 0, sizeof (info) / sizeof (ULONG_PTR), (ULONG_PTR*) &info);
|
||||
}
|
||||
__except (GetExceptionCode() == EXCEPTION_NONCONTINUABLE_EXCEPTION ? EXCEPTION_EXECUTE_HANDLER
|
||||
: EXCEPTION_CONTINUE_EXECUTION)
|
||||
{
|
||||
OutputDebugStringA ("** Warning - Encountered noncontinuable exception **\n");
|
||||
}
|
||||
#else
|
||||
ignoreUnused (name);
|
||||
#endif
|
||||
}
|
||||
|
||||
Thread::ThreadID JUCE_CALLTYPE Thread::getCurrentThreadId()
|
||||
{
|
||||
return (ThreadID) (pointer_sized_int) GetCurrentThreadId();
|
||||
}
|
||||
|
||||
bool Thread::setThreadPriority (void* handle, int priority)
|
||||
{
|
||||
int pri = THREAD_PRIORITY_TIME_CRITICAL;
|
||||
|
||||
if (priority < 1) pri = THREAD_PRIORITY_IDLE;
|
||||
else if (priority < 2) pri = THREAD_PRIORITY_LOWEST;
|
||||
else if (priority < 5) pri = THREAD_PRIORITY_BELOW_NORMAL;
|
||||
else if (priority < 7) pri = THREAD_PRIORITY_NORMAL;
|
||||
else if (priority < 9) pri = THREAD_PRIORITY_ABOVE_NORMAL;
|
||||
else if (priority < 10) pri = THREAD_PRIORITY_HIGHEST;
|
||||
|
||||
if (handle == nullptr)
|
||||
handle = GetCurrentThread();
|
||||
|
||||
return SetThreadPriority (handle, pri) != FALSE;
|
||||
}
|
||||
|
||||
void JUCE_CALLTYPE Thread::setCurrentThreadAffinityMask (const uint32 affinityMask)
|
||||
{
|
||||
SetThreadAffinityMask (GetCurrentThread(), affinityMask);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
struct SleepEvent
|
||||
{
|
||||
SleepEvent() noexcept
|
||||
: handle (CreateEvent (nullptr, FALSE, FALSE,
|
||||
#if JUCE_DEBUG
|
||||
_T("JUCE Sleep Event")))
|
||||
#else
|
||||
nullptr))
|
||||
#endif
|
||||
{}
|
||||
|
||||
~SleepEvent() noexcept
|
||||
{
|
||||
CloseHandle (handle);
|
||||
handle = nullptr;
|
||||
}
|
||||
|
||||
HANDLE handle;
|
||||
};
|
||||
|
||||
static SleepEvent sleepEvent;
|
||||
|
||||
void JUCE_CALLTYPE Thread::sleep (const int millisecs)
|
||||
{
|
||||
jassert (millisecs >= 0);
|
||||
|
||||
if (millisecs >= 10 || sleepEvent.handle == nullptr)
|
||||
Sleep ((DWORD) millisecs);
|
||||
else
|
||||
// unlike Sleep() this is guaranteed to return to the current thread after
|
||||
// the time expires, so we'll use this for short waits, which are more likely
|
||||
// to need to be accurate
|
||||
WaitForSingleObject (sleepEvent.handle, (DWORD) millisecs);
|
||||
}
|
||||
|
||||
void Thread::yield()
|
||||
{
|
||||
Sleep (0);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static int lastProcessPriority = -1;
|
||||
|
||||
// called when the app gains focus because Windows does weird things to process priority
|
||||
// when you swap apps, and this forces an update when the app is brought to the front.
|
||||
void juce_repeatLastProcessPriority()
|
||||
{
|
||||
if (lastProcessPriority >= 0) // (avoid changing this if it's not been explicitly set by the app..)
|
||||
{
|
||||
DWORD p;
|
||||
|
||||
switch (lastProcessPriority)
|
||||
{
|
||||
case Process::LowPriority: p = IDLE_PRIORITY_CLASS; break;
|
||||
case Process::NormalPriority: p = NORMAL_PRIORITY_CLASS; break;
|
||||
case Process::HighPriority: p = HIGH_PRIORITY_CLASS; break;
|
||||
case Process::RealtimePriority: p = REALTIME_PRIORITY_CLASS; break;
|
||||
default: jassertfalse; return; // bad priority value
|
||||
}
|
||||
|
||||
SetPriorityClass (GetCurrentProcess(), p);
|
||||
}
|
||||
}
|
||||
|
||||
void JUCE_CALLTYPE Process::setPriority (ProcessPriority prior)
|
||||
{
|
||||
if (lastProcessPriority != (int) prior)
|
||||
{
|
||||
lastProcessPriority = (int) prior;
|
||||
juce_repeatLastProcessPriority();
|
||||
}
|
||||
}
|
||||
|
||||
JUCE_API bool JUCE_CALLTYPE juce_isRunningUnderDebugger() noexcept
|
||||
{
|
||||
return IsDebuggerPresent() != FALSE;
|
||||
}
|
||||
|
||||
static void* currentModuleHandle = nullptr;
|
||||
|
||||
void* JUCE_CALLTYPE Process::getCurrentModuleInstanceHandle() noexcept
|
||||
{
|
||||
if (currentModuleHandle == nullptr)
|
||||
{
|
||||
auto status = GetModuleHandleEx (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
|
||||
(LPCTSTR) ¤tModuleHandle,
|
||||
(HMODULE*) ¤tModuleHandle);
|
||||
|
||||
if (status == 0 || currentModuleHandle == nullptr)
|
||||
currentModuleHandle = GetModuleHandleA (nullptr);
|
||||
}
|
||||
|
||||
return currentModuleHandle;
|
||||
}
|
||||
|
||||
void JUCE_CALLTYPE Process::setCurrentModuleInstanceHandle (void* const newHandle) noexcept
|
||||
{
|
||||
currentModuleHandle = newHandle;
|
||||
}
|
||||
|
||||
void JUCE_CALLTYPE Process::raisePrivilege() {}
|
||||
void JUCE_CALLTYPE Process::lowerPrivilege() {}
|
||||
|
||||
void JUCE_CALLTYPE Process::terminate()
|
||||
{
|
||||
#if JUCE_MSVC && JUCE_CHECK_MEMORY_LEAKS
|
||||
_CrtDumpMemoryLeaks();
|
||||
#endif
|
||||
|
||||
// bullet in the head in case there's a problem shutting down..
|
||||
ExitProcess (1);
|
||||
}
|
||||
|
||||
bool juce_isRunningInWine()
|
||||
{
|
||||
HMODULE ntdll = GetModuleHandleA ("ntdll");
|
||||
return ntdll != nullptr && GetProcAddress (ntdll, "wine_get_version") != nullptr;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool DynamicLibrary::open (const String& name)
|
||||
{
|
||||
close();
|
||||
handle = LoadLibrary (name.toWideCharPointer());
|
||||
return handle != nullptr;
|
||||
}
|
||||
|
||||
void DynamicLibrary::close()
|
||||
{
|
||||
if (handle != nullptr)
|
||||
{
|
||||
FreeLibrary ((HMODULE) handle);
|
||||
handle = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void* DynamicLibrary::getFunction (const String& functionName) noexcept
|
||||
{
|
||||
return handle != nullptr ? (void*) GetProcAddress ((HMODULE) handle, functionName.toUTF8()) // (void* cast is required for mingw)
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class InterProcessLock::Pimpl
|
||||
{
|
||||
public:
|
||||
Pimpl (String nameIn, const int timeOutMillisecs)
|
||||
: handle (nullptr), refCount (1)
|
||||
{
|
||||
nameIn = nameIn.replaceCharacter ('\\', '/');
|
||||
handle = CreateMutexW (nullptr, TRUE, ("Global\\" + nameIn).toWideCharPointer());
|
||||
|
||||
// Not 100% sure why a global mutex sometimes can't be allocated, but if it fails, fall back to
|
||||
// a local one. (A local one also sometimes fails on other machines so neither type appears to be
|
||||
// universally reliable)
|
||||
if (handle == nullptr)
|
||||
handle = CreateMutexW (nullptr, TRUE, ("Local\\" + nameIn).toWideCharPointer());
|
||||
|
||||
if (handle != nullptr && GetLastError() == ERROR_ALREADY_EXISTS)
|
||||
{
|
||||
if (timeOutMillisecs == 0)
|
||||
{
|
||||
close();
|
||||
return;
|
||||
}
|
||||
|
||||
switch (WaitForSingleObject (handle, timeOutMillisecs < 0 ? INFINITE : (DWORD) timeOutMillisecs))
|
||||
{
|
||||
case WAIT_OBJECT_0:
|
||||
case WAIT_ABANDONED:
|
||||
break;
|
||||
|
||||
case WAIT_TIMEOUT:
|
||||
default:
|
||||
close();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~Pimpl()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
void close()
|
||||
{
|
||||
if (handle != nullptr)
|
||||
{
|
||||
ReleaseMutex (handle);
|
||||
CloseHandle (handle);
|
||||
handle = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
HANDLE handle;
|
||||
int refCount;
|
||||
};
|
||||
|
||||
InterProcessLock::InterProcessLock (const String& name_)
|
||||
: name (name_)
|
||||
{
|
||||
}
|
||||
|
||||
InterProcessLock::~InterProcessLock()
|
||||
{
|
||||
}
|
||||
|
||||
bool InterProcessLock::enter (const int timeOutMillisecs)
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
if (pimpl == nullptr)
|
||||
{
|
||||
pimpl.reset (new Pimpl (name, timeOutMillisecs));
|
||||
|
||||
if (pimpl->handle == nullptr)
|
||||
pimpl.reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
pimpl->refCount++;
|
||||
}
|
||||
|
||||
return pimpl != nullptr;
|
||||
}
|
||||
|
||||
void InterProcessLock::exit()
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
// Trying to release the lock too many times!
|
||||
jassert (pimpl != nullptr);
|
||||
|
||||
if (pimpl != nullptr && --(pimpl->refCount) == 0)
|
||||
pimpl.reset();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class ChildProcess::ActiveProcess
|
||||
{
|
||||
public:
|
||||
ActiveProcess (const String& command, int streamFlags)
|
||||
: ok (false), readPipe (nullptr), writePipe (nullptr)
|
||||
{
|
||||
SECURITY_ATTRIBUTES securityAtts = {};
|
||||
securityAtts.nLength = sizeof (securityAtts);
|
||||
securityAtts.bInheritHandle = TRUE;
|
||||
|
||||
if (CreatePipe (&readPipe, &writePipe, &securityAtts, 0)
|
||||
&& SetHandleInformation (readPipe, HANDLE_FLAG_INHERIT, 0))
|
||||
{
|
||||
STARTUPINFOW startupInfo = {};
|
||||
startupInfo.cb = sizeof (startupInfo);
|
||||
|
||||
startupInfo.hStdOutput = (streamFlags & wantStdOut) != 0 ? writePipe : nullptr;
|
||||
startupInfo.hStdError = (streamFlags & wantStdErr) != 0 ? writePipe : nullptr;
|
||||
startupInfo.dwFlags = STARTF_USESTDHANDLES;
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6335)
|
||||
ok = CreateProcess (nullptr, const_cast<LPWSTR> (command.toWideCharPointer()),
|
||||
nullptr, nullptr, TRUE, CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT,
|
||||
nullptr, nullptr, &startupInfo, &processInfo) != FALSE;
|
||||
JUCE_END_IGNORE_WARNINGS_MSVC
|
||||
}
|
||||
}
|
||||
|
||||
~ActiveProcess()
|
||||
{
|
||||
if (ok)
|
||||
{
|
||||
CloseHandle (processInfo.hThread);
|
||||
CloseHandle (processInfo.hProcess);
|
||||
}
|
||||
|
||||
if (readPipe != nullptr)
|
||||
CloseHandle (readPipe);
|
||||
|
||||
if (writePipe != nullptr)
|
||||
CloseHandle (writePipe);
|
||||
}
|
||||
|
||||
bool isRunning() const noexcept
|
||||
{
|
||||
return WaitForSingleObject (processInfo.hProcess, 0) != WAIT_OBJECT_0;
|
||||
}
|
||||
|
||||
int read (void* dest, int numNeeded) const noexcept
|
||||
{
|
||||
int total = 0;
|
||||
|
||||
while (ok && numNeeded > 0)
|
||||
{
|
||||
DWORD available = 0;
|
||||
|
||||
if (! PeekNamedPipe ((HANDLE) readPipe, nullptr, 0, nullptr, &available, nullptr))
|
||||
break;
|
||||
|
||||
const int numToDo = jmin ((int) available, numNeeded);
|
||||
|
||||
if (available == 0)
|
||||
{
|
||||
if (! isRunning())
|
||||
break;
|
||||
|
||||
Thread::sleep (1);
|
||||
}
|
||||
else
|
||||
{
|
||||
DWORD numRead = 0;
|
||||
if (! ReadFile ((HANDLE) readPipe, dest, (DWORD) numToDo, &numRead, nullptr))
|
||||
break;
|
||||
|
||||
total += (int) numRead;
|
||||
dest = addBytesToPointer (dest, numRead);
|
||||
numNeeded -= (int) numRead;
|
||||
}
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
bool killProcess() const noexcept
|
||||
{
|
||||
return TerminateProcess (processInfo.hProcess, 0) != FALSE;
|
||||
}
|
||||
|
||||
uint32 getExitCode() const noexcept
|
||||
{
|
||||
DWORD exitCode = 0;
|
||||
GetExitCodeProcess (processInfo.hProcess, &exitCode);
|
||||
return (uint32) exitCode;
|
||||
}
|
||||
|
||||
bool ok;
|
||||
|
||||
private:
|
||||
HANDLE readPipe, writePipe;
|
||||
PROCESS_INFORMATION processInfo;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ActiveProcess)
|
||||
};
|
||||
|
||||
bool ChildProcess::start (const String& command, int streamFlags)
|
||||
{
|
||||
activeProcess.reset (new ActiveProcess (command, streamFlags));
|
||||
|
||||
if (! activeProcess->ok)
|
||||
activeProcess = nullptr;
|
||||
|
||||
return activeProcess != nullptr;
|
||||
}
|
||||
|
||||
bool ChildProcess::start (const StringArray& args, int streamFlags)
|
||||
{
|
||||
String escaped;
|
||||
|
||||
for (int i = 0; i < args.size(); ++i)
|
||||
{
|
||||
String arg (args[i]);
|
||||
|
||||
// If there are spaces, surround it with quotes. If there are quotes,
|
||||
// replace them with \" so that CommandLineToArgv will correctly parse them.
|
||||
if (arg.containsAnyOf ("\" "))
|
||||
arg = arg.replace ("\"", "\\\"").quoted();
|
||||
|
||||
escaped << arg << ' ';
|
||||
}
|
||||
|
||||
return start (escaped.trim(), streamFlags);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
struct HighResolutionTimer::Pimpl
|
||||
{
|
||||
Pimpl (HighResolutionTimer& t) noexcept : owner (t)
|
||||
{
|
||||
}
|
||||
|
||||
~Pimpl()
|
||||
{
|
||||
jassert (periodMs == 0);
|
||||
}
|
||||
|
||||
void start (int newPeriod)
|
||||
{
|
||||
if (newPeriod != periodMs)
|
||||
{
|
||||
stop();
|
||||
periodMs = newPeriod;
|
||||
|
||||
TIMECAPS tc;
|
||||
if (timeGetDevCaps (&tc, sizeof (tc)) == TIMERR_NOERROR)
|
||||
{
|
||||
const int actualPeriod = jlimit ((int) tc.wPeriodMin, (int) tc.wPeriodMax, newPeriod);
|
||||
|
||||
timerID = timeSetEvent ((UINT) actualPeriod, tc.wPeriodMin, callbackFunction, (DWORD_PTR) this,
|
||||
TIME_PERIODIC | TIME_CALLBACK_FUNCTION | 0x100 /*TIME_KILL_SYNCHRONOUS*/);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void stop()
|
||||
{
|
||||
periodMs = 0;
|
||||
timeKillEvent (timerID);
|
||||
}
|
||||
|
||||
HighResolutionTimer& owner;
|
||||
int periodMs = 0;
|
||||
|
||||
private:
|
||||
unsigned int timerID;
|
||||
|
||||
static void STDMETHODCALLTYPE callbackFunction (UINT, UINT, DWORD_PTR userInfo, DWORD_PTR, DWORD_PTR)
|
||||
{
|
||||
if (Pimpl* const timer = reinterpret_cast<Pimpl*> (userInfo))
|
||||
if (timer->periodMs != 0)
|
||||
timer->owner.hiResTimerCallback();
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (Pimpl)
|
||||
};
|
||||
|
||||
} // namespace juce
|
Reference in New Issue
Block a user