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:
essej
2022-04-18 17:51:22 -04:00
parent 63e175fee6
commit 25bd5d8adb
3210 changed files with 1045392 additions and 0 deletions

View 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.

View 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);
}

View 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;
}

View 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);
}

View 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);
}
}

View 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);
}

View 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

View 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

View 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 {};
}
}

File diff suppressed because it is too large Load Diff

View 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

View 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

View 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

View 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

View 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, &param);
}
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

View 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

View 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

View 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

View 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

View 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

View 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

View 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, &param);
}
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

View 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

View 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

File diff suppressed because it is too large Load Diff

View 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

View 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

View 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

View 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

View 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

View 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

File diff suppressed because it is too large Load Diff

View 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

View 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

File diff suppressed because it is too large Load Diff

View 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

View 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

View 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

View 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) &currentModuleHandle,
(HMODULE*) &currentModuleHandle);
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