fix macOS build (following Projucer changes made in Windows, which removed /Applications/JUCE/modules from its headers). move JUCE headers under source control, so that Windows and macOS can both build against same version of JUCE. remove AUv3 target (I think it's an iOS thing, so it will never work with this macOS fluidsynth dylib).

This commit is contained in:
Alex Birch
2018-06-17 13:34:53 +01:00
parent a2be47c887
commit dff4d13a1d
1563 changed files with 601601 additions and 3466 deletions

View File

@ -0,0 +1,169 @@
$$CameraApi21
//==============================================================================
public class CameraDeviceStateCallback extends CameraDevice.StateCallback
{
private native void cameraDeviceStateClosed (long host, CameraDevice camera);
private native void cameraDeviceStateDisconnected (long host, CameraDevice camera);
private native void cameraDeviceStateError (long host, CameraDevice camera, int error);
private native void cameraDeviceStateOpened (long host, CameraDevice camera);
CameraDeviceStateCallback (long hostToUse)
{
host = hostToUse;
}
@Override
public void onClosed (CameraDevice camera)
{
cameraDeviceStateClosed (host, camera);
}
@Override
public void onDisconnected (CameraDevice camera)
{
cameraDeviceStateDisconnected (host, camera);
}
@Override
public void onError (CameraDevice camera, int error)
{
cameraDeviceStateError (host, camera, error);
}
@Override
public void onOpened (CameraDevice camera)
{
cameraDeviceStateOpened (host, camera);
}
private long host;
}
//==============================================================================
public class CameraCaptureSessionStateCallback extends CameraCaptureSession.StateCallback
{
private native void cameraCaptureSessionActive (long host, CameraCaptureSession session);
private native void cameraCaptureSessionClosed (long host, CameraCaptureSession session);
private native void cameraCaptureSessionConfigureFailed (long host, CameraCaptureSession session);
private native void cameraCaptureSessionConfigured (long host, CameraCaptureSession session);
private native void cameraCaptureSessionReady (long host, CameraCaptureSession session);
CameraCaptureSessionStateCallback (long hostToUse)
{
host = hostToUse;
}
@Override
public void onActive (CameraCaptureSession session)
{
cameraCaptureSessionActive (host, session);
}
@Override
public void onClosed (CameraCaptureSession session)
{
cameraCaptureSessionClosed (host, session);
}
@Override
public void onConfigureFailed (CameraCaptureSession session)
{
cameraCaptureSessionConfigureFailed (host, session);
}
@Override
public void onConfigured (CameraCaptureSession session)
{
cameraCaptureSessionConfigured (host, session);
}
@Override
public void onReady (CameraCaptureSession session)
{
cameraCaptureSessionReady (host, session);
}
private long host;
}
//==============================================================================
public class CameraCaptureSessionCaptureCallback extends CameraCaptureSession.CaptureCallback
{
private native void cameraCaptureSessionCaptureCompleted (long host, boolean isPreview, CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result);
private native void cameraCaptureSessionCaptureFailed (long host, boolean isPreview, CameraCaptureSession session, CaptureRequest request, CaptureFailure failure);
private native void cameraCaptureSessionCaptureProgressed (long host, boolean isPreview, CameraCaptureSession session, CaptureRequest request, CaptureResult partialResult);
private native void cameraCaptureSessionCaptureStarted (long host, boolean isPreview, CameraCaptureSession session, CaptureRequest request, long timestamp, long frameNumber);
private native void cameraCaptureSessionCaptureSequenceAborted (long host, boolean isPreview, CameraCaptureSession session, int sequenceId);
private native void cameraCaptureSessionCaptureSequenceCompleted (long host, boolean isPreview, CameraCaptureSession session, int sequenceId, long frameNumber);
CameraCaptureSessionCaptureCallback (long hostToUse, boolean shouldBePreview)
{
host = hostToUse;
preview = shouldBePreview;
}
@Override
public void onCaptureCompleted (CameraCaptureSession session, CaptureRequest request,
TotalCaptureResult result)
{
cameraCaptureSessionCaptureCompleted (host, preview, session, request, result);
}
@Override
public void onCaptureFailed (CameraCaptureSession session, CaptureRequest request, CaptureFailure failure)
{
cameraCaptureSessionCaptureFailed (host, preview, session, request, failure);
}
@Override
public void onCaptureProgressed (CameraCaptureSession session, CaptureRequest request,
CaptureResult partialResult)
{
cameraCaptureSessionCaptureProgressed (host, preview, session, request, partialResult);
}
@Override
public void onCaptureSequenceAborted (CameraCaptureSession session, int sequenceId)
{
cameraCaptureSessionCaptureSequenceAborted (host, preview, session, sequenceId);
}
@Override
public void onCaptureSequenceCompleted (CameraCaptureSession session, int sequenceId, long frameNumber)
{
cameraCaptureSessionCaptureSequenceCompleted (host, preview, session, sequenceId, frameNumber);
}
@Override
public void onCaptureStarted (CameraCaptureSession session, CaptureRequest request, long timestamp,
long frameNumber)
{
cameraCaptureSessionCaptureStarted (host, preview, session, request, timestamp, frameNumber);
}
private long host;
private boolean preview;
}
//==============================================================================
public class JuceOrientationEventListener extends OrientationEventListener
{
private native void deviceOrientationChanged (long host, int orientation);
public JuceOrientationEventListener (long hostToUse, Context context, int rate)
{
super (context, rate);
host = hostToUse;
}
@Override
public void onOrientationChanged (int orientation)
{
deviceOrientationChanged (host, orientation);
}
private long host;
}
CameraApi21$$

View File

@ -0,0 +1,999 @@
//==============================================================================
public class BluetoothManager extends ScanCallback
{
BluetoothManager()
{
}
public String[] getMidiBluetoothAddresses()
{
return bluetoothMidiDevices.toArray (new String[bluetoothMidiDevices.size()]);
}
public String getHumanReadableStringForBluetoothAddress (String address)
{
BluetoothDevice btDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice (address);
return btDevice.getName();
}
public int getBluetoothDeviceStatus (String address)
{
return getAndroidMidiDeviceManager().getBluetoothDeviceStatus (address);
}
public void startStopScan (boolean shouldStart)
{
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (bluetoothAdapter == null)
{
Log.d ("JUCE", "BluetoothManager error: could not get default Bluetooth adapter");
return;
}
BluetoothLeScanner bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner();
if (bluetoothLeScanner == null)
{
Log.d ("JUCE", "BluetoothManager error: could not get Bluetooth LE scanner");
return;
}
if (shouldStart)
{
ScanFilter.Builder scanFilterBuilder = new ScanFilter.Builder();
scanFilterBuilder.setServiceUuid (ParcelUuid.fromString (bluetoothLEMidiServiceUUID));
ScanSettings.Builder scanSettingsBuilder = new ScanSettings.Builder();
scanSettingsBuilder.setCallbackType (ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
.setScanMode (ScanSettings.SCAN_MODE_LOW_POWER)
.setScanMode (ScanSettings.MATCH_MODE_STICKY);
bluetoothLeScanner.startScan (Arrays.asList (scanFilterBuilder.build()),
scanSettingsBuilder.build(),
this);
}
else
{
bluetoothLeScanner.stopScan (this);
}
}
public boolean pairBluetoothMidiDevice(String address)
{
BluetoothDevice btDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice (address);
if (btDevice == null)
{
Log.d ("JUCE", "failed to create buletooth device from address");
return false;
}
return getAndroidMidiDeviceManager().pairBluetoothDevice (btDevice);
}
public void unpairBluetoothMidiDevice (String address)
{
getAndroidMidiDeviceManager().unpairBluetoothDevice (address);
}
public void onScanFailed (int errorCode)
{
}
public void onScanResult (int callbackType, ScanResult result)
{
if (callbackType == ScanSettings.CALLBACK_TYPE_ALL_MATCHES
|| callbackType == ScanSettings.CALLBACK_TYPE_FIRST_MATCH)
{
BluetoothDevice device = result.getDevice();
if (device != null)
bluetoothMidiDevices.add (device.getAddress());
}
if (callbackType == ScanSettings.CALLBACK_TYPE_MATCH_LOST)
{
Log.d ("JUCE", "ScanSettings.CALLBACK_TYPE_MATCH_LOST");
BluetoothDevice device = result.getDevice();
if (device != null)
{
bluetoothMidiDevices.remove (device.getAddress());
unpairBluetoothMidiDevice (device.getAddress());
}
}
}
public void onBatchScanResults (List<ScanResult> results)
{
for (ScanResult result : results)
onScanResult (ScanSettings.CALLBACK_TYPE_ALL_MATCHES, result);
}
private BluetoothLeScanner scanner;
private static final String bluetoothLEMidiServiceUUID = "03B80E5A-EDE8-4B33-A751-6CE34EC4C700";
private HashSet<String> bluetoothMidiDevices = new HashSet<String>();
}
public static class JuceMidiInputPort extends MidiReceiver implements JuceMidiPort
{
private native void handleReceive (long host, byte[] msg, int offset, int count, long timestamp);
public JuceMidiInputPort (MidiDeviceManager mm, MidiOutputPort actualPort, MidiPortPath portPathToUse, long hostToUse)
{
owner = mm;
androidPort = actualPort;
portPath = portPathToUse;
juceHost = hostToUse;
isConnected = false;
}
@Override
protected void finalize() throws Throwable
{
close();
super.finalize();
}
@Override
public boolean isInputPort()
{
return true;
}
@Override
public void start()
{
if (owner != null && androidPort != null && ! isConnected) {
androidPort.connect(this);
isConnected = true;
}
}
@Override
public void stop()
{
if (owner != null && androidPort != null && isConnected) {
androidPort.disconnect(this);
isConnected = false;
}
}
@Override
public void close()
{
if (androidPort != null) {
try {
androidPort.close();
} catch (IOException exception) {
Log.d("JUCE", "IO Exception while closing port");
}
}
if (owner != null)
owner.removePort (portPath);
owner = null;
androidPort = null;
}
@Override
public void onSend (byte[] msg, int offset, int count, long timestamp)
{
if (count > 0)
handleReceive (juceHost, msg, offset, count, timestamp);
}
@Override
public void onFlush()
{}
@Override
public void sendMidi (byte[] msg, int offset, int count)
{
}
MidiDeviceManager owner;
MidiOutputPort androidPort;
MidiPortPath portPath;
long juceHost;
boolean isConnected;
}
public static class JuceMidiOutputPort implements JuceMidiPort
{
public JuceMidiOutputPort (MidiDeviceManager mm, MidiInputPort actualPort, MidiPortPath portPathToUse)
{
owner = mm;
androidPort = actualPort;
portPath = portPathToUse;
}
@Override
protected void finalize() throws Throwable
{
close();
super.finalize();
}
@Override
public boolean isInputPort()
{
return false;
}
@Override
public void start()
{
}
@Override
public void stop()
{
}
@Override
public void sendMidi (byte[] msg, int offset, int count)
{
if (androidPort != null)
{
try {
androidPort.send(msg, offset, count);
} catch (IOException exception)
{
Log.d ("JUCE", "send midi had IO exception");
}
}
}
@Override
public void close()
{
if (androidPort != null) {
try {
androidPort.close();
} catch (IOException exception) {
Log.d("JUCE", "IO Exception while closing port");
}
}
if (owner != null)
owner.removePort (portPath);
owner = null;
androidPort = null;
}
MidiDeviceManager owner;
MidiInputPort androidPort;
MidiPortPath portPath;
}
private static class MidiPortPath extends Object
{
public MidiPortPath (int deviceIdToUse, boolean direction, int androidIndex)
{
deviceId = deviceIdToUse;
isInput = direction;
portIndex = androidIndex;
}
public int deviceId;
public int portIndex;
public boolean isInput;
@Override
public int hashCode()
{
Integer i = new Integer ((deviceId * 128) + (portIndex < 128 ? portIndex : 127));
return i.hashCode() * (isInput ? -1 : 1);
}
@Override
public boolean equals (Object obj)
{
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
MidiPortPath other = (MidiPortPath) obj;
return (portIndex == other.portIndex && isInput == other.isInput && deviceId == other.deviceId);
}
}
//==============================================================================
public class MidiDeviceManager extends MidiManager.DeviceCallback implements MidiManager.OnDeviceOpenedListener
{
//==============================================================================
private class DummyBluetoothGattCallback extends BluetoothGattCallback
{
public DummyBluetoothGattCallback (MidiDeviceManager mm)
{
super();
owner = mm;
}
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState)
{
if (newState == BluetoothProfile.STATE_CONNECTED)
{
gatt.requestConnectionPriority(BluetoothGatt.CONNECTION_PRIORITY_HIGH);
owner.pairBluetoothDeviceStepTwo (gatt.getDevice());
}
}
public void onServicesDiscovered(BluetoothGatt gatt, int status) {}
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {}
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {}
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {}
public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {}
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {}
public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {}
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {}
public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {}
private MidiDeviceManager owner;
}
//==============================================================================
private class MidiDeviceOpenTask extends java.util.TimerTask
{
public MidiDeviceOpenTask (MidiDeviceManager deviceManager, MidiDevice device, BluetoothGatt gattToUse)
{
owner = deviceManager;
midiDevice = device;
btGatt = gattToUse;
}
@Override
public boolean cancel()
{
synchronized (MidiDeviceOpenTask.class)
{
owner = null;
boolean retval = super.cancel();
if (btGatt != null)
{
btGatt.disconnect();
btGatt.close();
btGatt = null;
}
if (midiDevice != null)
{
try
{
midiDevice.close();
}
catch (IOException e)
{}
midiDevice = null;
}
return retval;
}
}
public String getBluetoothAddress()
{
synchronized (MidiDeviceOpenTask.class)
{
if (midiDevice != null)
{
MidiDeviceInfo info = midiDevice.getInfo();
if (info.getType() == MidiDeviceInfo.TYPE_BLUETOOTH)
{
BluetoothDevice btDevice = (BluetoothDevice) info.getProperties().get (info.PROPERTY_BLUETOOTH_DEVICE);
if (btDevice != null)
return btDevice.getAddress();
}
}
}
return "";
}
public BluetoothGatt getGatt() { return btGatt; }
public int getID()
{
return midiDevice.getInfo().getId();
}
@Override
public void run()
{
synchronized (MidiDeviceOpenTask.class)
{
if (owner != null && midiDevice != null)
owner.onDeviceOpenedDelayed (midiDevice);
}
}
private MidiDeviceManager owner;
private MidiDevice midiDevice;
private BluetoothGatt btGatt;
}
//==============================================================================
public MidiDeviceManager()
{
manager = (MidiManager) getSystemService (MIDI_SERVICE);
if (manager == null)
{
Log.d ("JUCE", "MidiDeviceManager error: could not get MidiManager system service");
return;
}
openPorts = new HashMap<MidiPortPath, WeakReference<JuceMidiPort>> ();
midiDevices = new ArrayList<Pair<MidiDevice,BluetoothGatt>>();
openTasks = new HashMap<Integer, MidiDeviceOpenTask>();
btDevicesPairing = new HashMap<String, BluetoothGatt>();
MidiDeviceInfo[] foundDevices = manager.getDevices();
for (MidiDeviceInfo info : foundDevices)
onDeviceAdded (info);
manager.registerDeviceCallback (this, null);
}
protected void finalize() throws Throwable
{
manager.unregisterDeviceCallback (this);
synchronized (MidiDeviceManager.class)
{
btDevicesPairing.clear();
for (Integer deviceID : openTasks.keySet())
openTasks.get (deviceID).cancel();
openTasks = null;
}
for (MidiPortPath key : openPorts.keySet())
openPorts.get (key).get().close();
openPorts = null;
for (Pair<MidiDevice, BluetoothGatt> device : midiDevices)
{
if (device.second != null)
{
device.second.disconnect();
device.second.close();
}
device.first.close();
}
midiDevices.clear();
super.finalize();
}
public String[] getJuceAndroidMidiInputDevices()
{
return getJuceAndroidMidiDevices (MidiDeviceInfo.PortInfo.TYPE_OUTPUT);
}
public String[] getJuceAndroidMidiOutputDevices()
{
return getJuceAndroidMidiDevices (MidiDeviceInfo.PortInfo.TYPE_INPUT);
}
private String[] getJuceAndroidMidiDevices (int portType)
{
// only update the list when JUCE asks for a new list
synchronized (MidiDeviceManager.class)
{
deviceInfos = getDeviceInfos();
}
ArrayList<String> portNames = new ArrayList<String>();
int index = 0;
for (MidiPortPath portInfo = getPortPathForJuceIndex (portType, index); portInfo != null; portInfo = getPortPathForJuceIndex (portType, ++index))
portNames.add (getPortName (portInfo));
String[] names = new String[portNames.size()];
return portNames.toArray (names);
}
private JuceMidiPort openMidiPortWithJuceIndex (int index, long host, boolean isInput)
{
synchronized (MidiDeviceManager.class)
{
int portTypeToFind = (isInput ? MidiDeviceInfo.PortInfo.TYPE_OUTPUT : MidiDeviceInfo.PortInfo.TYPE_INPUT);
MidiPortPath portInfo = getPortPathForJuceIndex (portTypeToFind, index);
if (portInfo != null)
{
// ports must be opened exclusively!
if (openPorts.containsKey (portInfo))
return null;
Pair<MidiDevice,BluetoothGatt> devicePair = getMidiDevicePairForId (portInfo.deviceId);
if (devicePair != null)
{
MidiDevice device = devicePair.first;
if (device != null)
{
JuceMidiPort juceMidiPort = null;
if (isInput)
{
MidiOutputPort outputPort = device.openOutputPort(portInfo.portIndex);
if (outputPort != null)
juceMidiPort = new JuceMidiInputPort(this, outputPort, portInfo, host);
}
else
{
MidiInputPort inputPort = device.openInputPort(portInfo.portIndex);
if (inputPort != null)
juceMidiPort = new JuceMidiOutputPort(this, inputPort, portInfo);
}
if (juceMidiPort != null)
{
openPorts.put(portInfo, new WeakReference<JuceMidiPort>(juceMidiPort));
return juceMidiPort;
}
}
}
}
}
return null;
}
public JuceMidiPort openMidiInputPortWithJuceIndex (int index, long host)
{
return openMidiPortWithJuceIndex (index, host, true);
}
public JuceMidiPort openMidiOutputPortWithJuceIndex (int index)
{
return openMidiPortWithJuceIndex (index, 0, false);
}
/* 0: unpaired, 1: paired, 2: pairing */
public int getBluetoothDeviceStatus (String address)
{
synchronized (MidiDeviceManager.class)
{
if (! address.isEmpty())
{
if (findMidiDeviceForBluetoothAddress (address) != null)
return 1;
if (btDevicesPairing.containsKey (address))
return 2;
if (findOpenTaskForBluetoothAddress (address) != null)
return 2;
}
}
return 0;
}
public boolean pairBluetoothDevice (BluetoothDevice btDevice)
{
String btAddress = btDevice.getAddress();
if (btAddress.isEmpty())
return false;
synchronized (MidiDeviceManager.class)
{
if (getBluetoothDeviceStatus (btAddress) != 0)
return false;
btDevicesPairing.put (btDevice.getAddress(), null);
BluetoothGatt gatt = btDevice.connectGatt (getApplicationContext(), true, new DummyBluetoothGattCallback (this));
if (gatt != null)
{
btDevicesPairing.put (btDevice.getAddress(), gatt);
}
else
{
pairBluetoothDeviceStepTwo (btDevice);
}
}
return true;
}
public void pairBluetoothDeviceStepTwo (BluetoothDevice btDevice)
{
manager.openBluetoothDevice(btDevice, this, null);
}
public void unpairBluetoothDevice (String address)
{
if (address.isEmpty())
return;
synchronized (MidiDeviceManager.class)
{
if (btDevicesPairing.containsKey (address))
{
BluetoothGatt gatt = btDevicesPairing.get (address);
if (gatt != null)
{
gatt.disconnect();
gatt.close();
}
btDevicesPairing.remove (address);
}
MidiDeviceOpenTask openTask = findOpenTaskForBluetoothAddress (address);
if (openTask != null)
{
int deviceID = openTask.getID();
openTask.cancel();
openTasks.remove (deviceID);
}
Pair<MidiDevice, BluetoothGatt> midiDevicePair = findMidiDeviceForBluetoothAddress (address);
if (midiDevicePair != null)
{
MidiDevice midiDevice = midiDevicePair.first;
onDeviceRemoved (midiDevice.getInfo());
try {
midiDevice.close();
}
catch (IOException exception)
{
Log.d ("JUCE", "IOException while closing midi device");
}
}
}
}
private Pair<MidiDevice, BluetoothGatt> findMidiDeviceForBluetoothAddress (String address)
{
for (Pair<MidiDevice,BluetoothGatt> midiDevice : midiDevices)
{
MidiDeviceInfo info = midiDevice.first.getInfo();
if (info.getType() == MidiDeviceInfo.TYPE_BLUETOOTH)
{
BluetoothDevice btDevice = (BluetoothDevice) info.getProperties().get (info.PROPERTY_BLUETOOTH_DEVICE);
if (btDevice != null && btDevice.getAddress().equals (address))
return midiDevice;
}
}
return null;
}
private MidiDeviceOpenTask findOpenTaskForBluetoothAddress (String address)
{
for (Integer deviceID : openTasks.keySet())
{
MidiDeviceOpenTask openTask = openTasks.get (deviceID);
if (openTask.getBluetoothAddress().equals (address))
return openTask;
}
return null;
}
public void removePort (MidiPortPath path)
{
openPorts.remove (path);
}
public String getInputPortNameForJuceIndex (int index)
{
MidiPortPath portInfo = getPortPathForJuceIndex (MidiDeviceInfo.PortInfo.TYPE_OUTPUT, index);
if (portInfo != null)
return getPortName (portInfo);
return "";
}
public String getOutputPortNameForJuceIndex (int index)
{
MidiPortPath portInfo = getPortPathForJuceIndex (MidiDeviceInfo.PortInfo.TYPE_INPUT, index);
if (portInfo != null)
return getPortName (portInfo);
return "";
}
public void onDeviceAdded (MidiDeviceInfo info)
{
// only add standard midi devices
if (info.getType() == info.TYPE_BLUETOOTH)
return;
manager.openDevice (info, this, null);
}
public void onDeviceRemoved (MidiDeviceInfo info)
{
synchronized (MidiDeviceManager.class)
{
Pair<MidiDevice, BluetoothGatt> devicePair = getMidiDevicePairForId (info.getId());
if (devicePair != null)
{
MidiDevice midiDevice = devicePair.first;
BluetoothGatt gatt = devicePair.second;
// close all ports that use this device
boolean removedPort = true;
while (removedPort == true)
{
removedPort = false;
for (MidiPortPath key : openPorts.keySet())
{
if (key.deviceId == info.getId())
{
openPorts.get(key).get().close();
removedPort = true;
break;
}
}
}
if (gatt != null)
{
gatt.disconnect();
gatt.close();
}
midiDevices.remove (devicePair);
}
}
}
public void onDeviceStatusChanged (MidiDeviceStatus status)
{
}
@Override
public void onDeviceOpened (MidiDevice theDevice)
{
synchronized (MidiDeviceManager.class)
{
MidiDeviceInfo info = theDevice.getInfo();
int deviceID = info.getId();
BluetoothGatt gatt = null;
boolean isBluetooth = false;
if (! openTasks.containsKey (deviceID))
{
if (info.getType() == MidiDeviceInfo.TYPE_BLUETOOTH)
{
isBluetooth = true;
BluetoothDevice btDevice = (BluetoothDevice) info.getProperties().get (info.PROPERTY_BLUETOOTH_DEVICE);
if (btDevice != null)
{
String btAddress = btDevice.getAddress();
if (btDevicesPairing.containsKey (btAddress))
{
gatt = btDevicesPairing.get (btAddress);
btDevicesPairing.remove (btAddress);
}
else
{
// unpair was called in the mean time
try
{
Pair<MidiDevice, BluetoothGatt> midiDevicePair = findMidiDeviceForBluetoothAddress (btDevice.getAddress());
if (midiDevicePair != null)
{
gatt = midiDevicePair.second;
if (gatt != null)
{
gatt.disconnect();
gatt.close();
}
}
theDevice.close();
}
catch (IOException e)
{}
return;
}
}
}
MidiDeviceOpenTask openTask = new MidiDeviceOpenTask (this, theDevice, gatt);
openTasks.put (deviceID, openTask);
new java.util.Timer().schedule (openTask, (isBluetooth ? 2000 : 100));
}
}
}
public void onDeviceOpenedDelayed (MidiDevice theDevice)
{
synchronized (MidiDeviceManager.class)
{
int deviceID = theDevice.getInfo().getId();
if (openTasks.containsKey (deviceID))
{
if (! midiDevices.contains(theDevice))
{
BluetoothGatt gatt = openTasks.get (deviceID).getGatt();
openTasks.remove (deviceID);
midiDevices.add (new Pair<MidiDevice,BluetoothGatt> (theDevice, gatt));
}
}
else
{
// unpair was called in the mean time
MidiDeviceInfo info = theDevice.getInfo();
BluetoothDevice btDevice = (BluetoothDevice) info.getProperties().get (info.PROPERTY_BLUETOOTH_DEVICE);
if (btDevice != null)
{
String btAddress = btDevice.getAddress();
Pair<MidiDevice, BluetoothGatt> midiDevicePair = findMidiDeviceForBluetoothAddress (btDevice.getAddress());
if (midiDevicePair != null)
{
BluetoothGatt gatt = midiDevicePair.second;
if (gatt != null)
{
gatt.disconnect();
gatt.close();
}
}
}
try
{
theDevice.close();
}
catch (IOException e)
{}
}
}
}
public String getPortName(MidiPortPath path)
{
int portTypeToFind = (path.isInput ? MidiDeviceInfo.PortInfo.TYPE_INPUT : MidiDeviceInfo.PortInfo.TYPE_OUTPUT);
synchronized (MidiDeviceManager.class)
{
for (MidiDeviceInfo info : deviceInfos)
{
int localIndex = 0;
if (info.getId() == path.deviceId)
{
for (MidiDeviceInfo.PortInfo portInfo : info.getPorts())
{
int portType = portInfo.getType();
if (portType == portTypeToFind)
{
int portIndex = portInfo.getPortNumber();
if (portIndex == path.portIndex)
{
String portName = portInfo.getName();
if (portName.isEmpty())
portName = (String) info.getProperties().get(info.PROPERTY_NAME);
return portName;
}
}
}
}
}
}
return "";
}
public MidiPortPath getPortPathForJuceIndex (int portType, int juceIndex)
{
int portIdx = 0;
for (MidiDeviceInfo info : deviceInfos)
{
for (MidiDeviceInfo.PortInfo portInfo : info.getPorts())
{
if (portInfo.getType() == portType)
{
if (portIdx == juceIndex)
return new MidiPortPath (info.getId(),
(portType == MidiDeviceInfo.PortInfo.TYPE_INPUT),
portInfo.getPortNumber());
portIdx++;
}
}
}
return null;
}
private MidiDeviceInfo[] getDeviceInfos()
{
synchronized (MidiDeviceManager.class)
{
MidiDeviceInfo[] infos = new MidiDeviceInfo[midiDevices.size()];
int idx = 0;
for (Pair<MidiDevice,BluetoothGatt> midiDevice : midiDevices)
infos[idx++] = midiDevice.first.getInfo();
return infos;
}
}
private Pair<MidiDevice, BluetoothGatt> getMidiDevicePairForId (int deviceId)
{
synchronized (MidiDeviceManager.class)
{
for (Pair<MidiDevice,BluetoothGatt> midiDevice : midiDevices)
if (midiDevice.first.getInfo().getId() == deviceId)
return midiDevice;
}
return null;
}
private MidiManager manager;
private HashMap<String, BluetoothGatt> btDevicesPairing;
private HashMap<Integer, MidiDeviceOpenTask> openTasks;
private ArrayList<Pair<MidiDevice, BluetoothGatt>> midiDevices;
private MidiDeviceInfo[] deviceInfos;
private HashMap<MidiPortPath, WeakReference<JuceMidiPort>> openPorts;
}
public MidiDeviceManager getAndroidMidiDeviceManager()
{
if (getSystemService (MIDI_SERVICE) == null)
return null;
synchronized (JuceAppActivity.class)
{
if (midiDeviceManager == null)
midiDeviceManager = new MidiDeviceManager();
}
return midiDeviceManager;
}
public BluetoothManager getAndroidBluetoothManager()
{
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
if (adapter == null)
return null;
if (adapter.getBluetoothLeScanner() == null)
return null;
synchronized (JuceAppActivity.class)
{
if (bluetoothManager == null)
bluetoothManager = new BluetoothManager();
}
return bluetoothManager;
}

View File

@ -0,0 +1,85 @@
//==============================================================================
public class BluetoothManager
{
BluetoothManager()
{
}
public String[] getMidiBluetoothAddresses()
{
String[] bluetoothAddresses = new String[0];
return bluetoothAddresses;
}
public String getHumanReadableStringForBluetoothAddress (String address)
{
return address;
}
public int getBluetoothDeviceStatus (String address)
{
return 0;
}
public void startStopScan (boolean shouldStart)
{
}
public boolean pairBluetoothMidiDevice(String address)
{
return false;
}
public void unpairBluetoothMidiDevice (String address)
{
}
}
//==============================================================================
public class MidiDeviceManager
{
public MidiDeviceManager()
{
}
public String[] getJuceAndroidMidiInputDevices()
{
return new String[0];
}
public String[] getJuceAndroidMidiOutputDevices()
{
return new String[0];
}
public JuceMidiPort openMidiInputPortWithJuceIndex (int index, long host)
{
return null;
}
public JuceMidiPort openMidiOutputPortWithJuceIndex (int index)
{
return null;
}
public String getInputPortNameForJuceIndex (int index)
{
return "";
}
public String getOutputPortNameForJuceIndex (int index)
{
return "";
}
}
public MidiDeviceManager getAndroidMidiDeviceManager()
{
return null;
}
public BluetoothManager getAndroidBluetoothManager()
{
return null;
}

View File

@ -0,0 +1,12 @@
@Override
public void onRequestPermissionsResult (int permissionID, String permissions[], int[] grantResults)
{
boolean permissionsGranted = (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED);
if (! permissionsGranted)
Log.d ("JUCE", "onRequestPermissionsResult: runtime permission was DENIED: " + getAndroidPermissionName (permissionID));
Long ptrToCallback = permissionCallbackPtrMap.get (permissionID);
permissionCallbackPtrMap.remove (permissionID);
androidRuntimePermissionsCallback (permissionsGranted, ptrToCallback);
}

View File

@ -0,0 +1,138 @@
package com.juce;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.res.AssetFileDescriptor;
import android.content.res.Resources;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
import android.os.FileObserver;
import android.os.ParcelFileDescriptor;
import java.lang.String;
public final class SharingContentProvider extends ContentProvider
{
private Object lock = new Object();
private native void contentSharerFileObserverEvent (long host, int event, String path);
private native Cursor contentSharerQuery (Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder);
private native void contentSharerCursorClosed (long host);
private native AssetFileDescriptor contentSharerOpenFile (Uri uri, String mode);
private native String[] contentSharerGetStreamTypes (Uri uri, String mimeTypeFilter);
public final class ProviderFileObserver extends FileObserver
{
public ProviderFileObserver (long hostToUse, String path, int mask)
{
super (path, mask);
host = hostToUse;
}
public void onEvent (int event, String path)
{
contentSharerFileObserverEvent (host, event, path);
}
private long host;
}
public final class ProviderCursor extends MatrixCursor
{
ProviderCursor (long hostToUse, String[] columnNames)
{
super (columnNames);
host = hostToUse;
}
@Override
public void close()
{
super.close();
contentSharerCursorClosed (host);
}
private long host;
}
@Override
public boolean onCreate()
{
return true;
}
@Override
public Cursor query (Uri url, String[] projection, String selection,
String[] selectionArgs, String sortOrder)
{
synchronized (lock)
{
return contentSharerQuery (url, projection, selection, selectionArgs, sortOrder);
}
}
@Override
public Uri insert (Uri uri, ContentValues values)
{
return null;
}
@Override
public int update (Uri uri, ContentValues values, String selection,
String[] selectionArgs)
{
return 0;
}
@Override
public int delete (Uri uri, String selection, String[] selectionArgs)
{
return 0;
}
@Override
public String getType (Uri uri)
{
return null;
}
@Override
public AssetFileDescriptor openAssetFile (Uri uri, String mode)
{
synchronized (lock)
{
return contentSharerOpenFile (uri, mode);
}
}
@Override
public ParcelFileDescriptor openFile (Uri uri, String mode)
{
synchronized (lock)
{
AssetFileDescriptor result = contentSharerOpenFile (uri, mode);
if (result != null)
return result.getParcelFileDescriptor();
return null;
}
}
$$ContentProviderApi11
@Override
public String[] getStreamTypes (Uri uri, String mimeTypeFilter)
{
synchronized (lock)
{
return contentSharerGetStreamTypes (uri, mimeTypeFilter);
}
}
ContentProviderApi11$$
}

View File

@ -0,0 +1,69 @@
$$WebViewNativeApi23 private native void webViewReceivedError (long host, WebView view, WebResourceRequest request, WebResourceError error);WebViewNativeApi23$$
$$WebViewNativeApi21 private native void webViewReceivedHttpError (long host, WebView view, WebResourceRequest request, WebResourceResponse errorResponse);WebViewNativeApi21$$
$$WebViewApi1_10
@Override
public void onPageStarted (WebView view, String url, Bitmap favicon)
{
if (host != 0)
webViewPageLoadStarted (host, view, url);
}
WebViewApi1_10$$
$$WebViewApi11_20
@Override
public WebResourceResponse shouldInterceptRequest (WebView view, String url)
{
synchronized (hostLock)
{
if (host != 0)
{
boolean shouldLoad = webViewPageLoadStarted (host, view, url);
if (shouldLoad)
return null;
}
}
return new WebResourceResponse ("text/html", null, null);
}
WebViewApi11_20$$
$$WebViewApi21
@Override
public WebResourceResponse shouldInterceptRequest (WebView view, WebResourceRequest request)
{
synchronized (hostLock)
{
if (host != 0)
{
boolean shouldLoad = webViewPageLoadStarted (host, view, request.getUrl().toString());
if (shouldLoad)
return null;
}
}
return new WebResourceResponse ("text/html", null, null);
}
WebViewApi21$$
$$WebViewApi23
@Override
public void onReceivedError (WebView view, WebResourceRequest request, WebResourceError error)
{
if (host == 0)
return;
webViewReceivedError (host, view, request, error);
}
@Override
public void onReceivedHttpError (WebView view, WebResourceRequest request, WebResourceResponse errorResponse)
{
if (host == 0)
return;
webViewReceivedHttpError (host, view, request, errorResponse);
}
WebViewApi23$$

View File

@ -0,0 +1,971 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
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.android.vending.billing;
/**
* InAppBillingService is the service that provides in-app billing version 3 and beyond.
* This service provides the following features:
* 1. Provides a new API to get details of in-app items published for the app including
* price, type, title and description.
* 2. The purchase flow is synchronous and purchase information is available immediately
* after it completes.
* 3. Purchase information of in-app purchases is maintained within the Google Play system
* till the purchase is consumed.
* 4. An API to consume a purchase of an inapp item. All purchases of one-time
* in-app items are consumable and thereafter can be purchased again.
* 5. An API to get current purchases of the user immediately. This will not contain any
* consumed purchases.
*
* All calls will give a response code with the following possible values
* RESULT_OK = 0 - success
* RESULT_USER_CANCELED = 1 - User pressed back or canceled a dialog
* RESULT_SERVICE_UNAVAILABLE = 2 - The network connection is down
* RESULT_BILLING_UNAVAILABLE = 3 - This billing API version is not supported for the type requested
* RESULT_ITEM_UNAVAILABLE = 4 - Requested SKU is not available for purchase
* RESULT_DEVELOPER_ERROR = 5 - Invalid arguments provided to the API
* RESULT_ERROR = 6 - Fatal error during the API action
* RESULT_ITEM_ALREADY_OWNED = 7 - Failure to purchase since item is already owned
* RESULT_ITEM_NOT_OWNED = 8 - Failure to consume since item is not owned
*/
public interface IInAppBillingService extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.android.vending.billing.IInAppBillingService
{
private static final java.lang.String DESCRIPTOR = "com.android.vending.billing.IInAppBillingService";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.android.vending.billing.IInAppBillingService interface,
* generating a proxy if needed.
*/
public static com.android.vending.billing.IInAppBillingService asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.android.vending.billing.IInAppBillingService))) {
return ((com.android.vending.billing.IInAppBillingService)iin);
}
return new com.android.vending.billing.IInAppBillingService.Stub.Proxy(obj);
}
@Override public android.os.IBinder asBinder()
{
return this;
}
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_isBillingSupported:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
java.lang.String _arg1;
_arg1 = data.readString();
java.lang.String _arg2;
_arg2 = data.readString();
int _result = this.isBillingSupported(_arg0, _arg1, _arg2);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
case TRANSACTION_getSkuDetails:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
java.lang.String _arg1;
_arg1 = data.readString();
java.lang.String _arg2;
_arg2 = data.readString();
android.os.Bundle _arg3;
if ((0!=data.readInt())) {
_arg3 = android.os.Bundle.CREATOR.createFromParcel(data);
}
else {
_arg3 = null;
}
android.os.Bundle _result = this.getSkuDetails(_arg0, _arg1, _arg2, _arg3);
reply.writeNoException();
if ((_result!=null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
}
else {
reply.writeInt(0);
}
return true;
}
case TRANSACTION_getBuyIntent:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
java.lang.String _arg1;
_arg1 = data.readString();
java.lang.String _arg2;
_arg2 = data.readString();
java.lang.String _arg3;
_arg3 = data.readString();
java.lang.String _arg4;
_arg4 = data.readString();
android.os.Bundle _result = this.getBuyIntent(_arg0, _arg1, _arg2, _arg3, _arg4);
reply.writeNoException();
if ((_result!=null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
}
else {
reply.writeInt(0);
}
return true;
}
case TRANSACTION_getPurchases:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
java.lang.String _arg1;
_arg1 = data.readString();
java.lang.String _arg2;
_arg2 = data.readString();
java.lang.String _arg3;
_arg3 = data.readString();
android.os.Bundle _result = this.getPurchases(_arg0, _arg1, _arg2, _arg3);
reply.writeNoException();
if ((_result!=null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
}
else {
reply.writeInt(0);
}
return true;
}
case TRANSACTION_consumePurchase:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
java.lang.String _arg1;
_arg1 = data.readString();
java.lang.String _arg2;
_arg2 = data.readString();
int _result = this.consumePurchase(_arg0, _arg1, _arg2);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
case TRANSACTION_stub:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
java.lang.String _arg1;
_arg1 = data.readString();
java.lang.String _arg2;
_arg2 = data.readString();
int _result = this.stub(_arg0, _arg1, _arg2);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
case TRANSACTION_getBuyIntentToReplaceSkus:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
java.lang.String _arg1;
_arg1 = data.readString();
java.util.List<java.lang.String> _arg2;
_arg2 = data.createStringArrayList();
java.lang.String _arg3;
_arg3 = data.readString();
java.lang.String _arg4;
_arg4 = data.readString();
java.lang.String _arg5;
_arg5 = data.readString();
android.os.Bundle _result = this.getBuyIntentToReplaceSkus(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
reply.writeNoException();
if ((_result!=null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
}
else {
reply.writeInt(0);
}
return true;
}
case TRANSACTION_getBuyIntentExtraParams:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
java.lang.String _arg1;
_arg1 = data.readString();
java.lang.String _arg2;
_arg2 = data.readString();
java.lang.String _arg3;
_arg3 = data.readString();
java.lang.String _arg4;
_arg4 = data.readString();
android.os.Bundle _arg5;
if ((0!=data.readInt())) {
_arg5 = android.os.Bundle.CREATOR.createFromParcel(data);
}
else {
_arg5 = null;
}
android.os.Bundle _result = this.getBuyIntentExtraParams(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
reply.writeNoException();
if ((_result!=null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
}
else {
reply.writeInt(0);
}
return true;
}
case TRANSACTION_getPurchaseHistory:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
java.lang.String _arg1;
_arg1 = data.readString();
java.lang.String _arg2;
_arg2 = data.readString();
java.lang.String _arg3;
_arg3 = data.readString();
android.os.Bundle _arg4;
if ((0!=data.readInt())) {
_arg4 = android.os.Bundle.CREATOR.createFromParcel(data);
}
else {
_arg4 = null;
}
android.os.Bundle _result = this.getPurchaseHistory(_arg0, _arg1, _arg2, _arg3, _arg4);
reply.writeNoException();
if ((_result!=null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
}
else {
reply.writeInt(0);
}
return true;
}
case TRANSACTION_isBillingSupportedExtraParams:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
java.lang.String _arg1;
_arg1 = data.readString();
java.lang.String _arg2;
_arg2 = data.readString();
android.os.Bundle _arg3;
if ((0!=data.readInt())) {
_arg3 = android.os.Bundle.CREATOR.createFromParcel(data);
}
else {
_arg3 = null;
}
int _result = this.isBillingSupportedExtraParams(_arg0, _arg1, _arg2, _arg3);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.android.vending.billing.IInAppBillingService
{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
@Override public android.os.IBinder asBinder()
{
return mRemote;
}
public java.lang.String getInterfaceDescriptor()
{
return DESCRIPTOR;
}
@Override public int isBillingSupported(int apiVersion, java.lang.String packageName, java.lang.String type) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(apiVersion);
_data.writeString(packageName);
_data.writeString(type);
mRemote.transact(Stub.TRANSACTION_isBillingSupported, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
/**
* Provides details of a list of SKUs
* Given a list of SKUs of a valid type in the skusBundle, this returns a bundle
* with a list JSON strings containing the productId, price, title and description.
* This API can be called with a maximum of 20 SKUs.
* @param apiVersion billing API version that the app is using
* @param packageName the package name of the calling app
* @param type of the in-app items ("inapp" for one-time purchases
* and "subs" for subscriptions)
* @param skusBundle bundle containing a StringArrayList of SKUs with key "ITEM_ID_LIST"
* @return Bundle containing the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response codes
* on failures.
* "DETAILS_LIST" with a StringArrayList containing purchase information
* in JSON format similar to:
* '{ "productId" : "exampleSku",
* "type" : "inapp",
* "price" : "$5.00",
* "price_currency": "USD",
* "price_amount_micros": 5000000,
* "title : "Example Title",
* "description" : "This is an example description" }'
*/
@Override public android.os.Bundle getSkuDetails(int apiVersion, java.lang.String packageName, java.lang.String type, android.os.Bundle skusBundle) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
android.os.Bundle _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(apiVersion);
_data.writeString(packageName);
_data.writeString(type);
if ((skusBundle!=null)) {
_data.writeInt(1);
skusBundle.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_getSkuDetails, _data, _reply, 0);
_reply.readException();
if ((0!=_reply.readInt())) {
_result = android.os.Bundle.CREATOR.createFromParcel(_reply);
}
else {
_result = null;
}
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
/**
* Returns a pending intent to launch the purchase flow for an in-app item by providing a SKU,
* the type, a unique purchase token and an optional developer payload.
* @param apiVersion billing API version that the app is using
* @param packageName package name of the calling app
* @param sku the SKU of the in-app item as published in the developer console
* @param type of the in-app item being purchased ("inapp" for one-time purchases
* and "subs" for subscriptions)
* @param developerPayload optional argument to be sent back with the purchase information
* @return Bundle containing the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response codes
* on failures.
* "BUY_INTENT" - PendingIntent to start the purchase flow
*
* The Pending intent should be launched with startIntentSenderForResult. When purchase flow
* has completed, the onActivityResult() will give a resultCode of OK or CANCELED.
* If the purchase is successful, the result data will contain the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response
* codes on failures.
* "INAPP_PURCHASE_DATA" - String in JSON format similar to
* '{"orderId":"12999763169054705758.1371079406387615",
* "packageName":"com.example.app",
* "productId":"exampleSku",
* "purchaseTime":1345678900000,
* "purchaseToken" : "122333444455555",
* "developerPayload":"example developer payload" }'
* "INAPP_DATA_SIGNATURE" - String containing the signature of the purchase data that
* was signed with the private key of the developer
*/
@Override public android.os.Bundle getBuyIntent(int apiVersion, java.lang.String packageName, java.lang.String sku, java.lang.String type, java.lang.String developerPayload) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
android.os.Bundle _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(apiVersion);
_data.writeString(packageName);
_data.writeString(sku);
_data.writeString(type);
_data.writeString(developerPayload);
mRemote.transact(Stub.TRANSACTION_getBuyIntent, _data, _reply, 0);
_reply.readException();
if ((0!=_reply.readInt())) {
_result = android.os.Bundle.CREATOR.createFromParcel(_reply);
}
else {
_result = null;
}
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
/**
* Returns the current SKUs owned by the user of the type and package name specified along with
* purchase information and a signature of the data to be validated.
* This will return all SKUs that have been purchased in V3 and managed items purchased using
* V1 and V2 that have not been consumed.
* @param apiVersion billing API version that the app is using
* @param packageName package name of the calling app
* @param type of the in-app items being requested ("inapp" for one-time purchases
* and "subs" for subscriptions)
* @param continuationToken to be set as null for the first call, if the number of owned
* skus are too many, a continuationToken is returned in the response bundle.
* This method can be called again with the continuation token to get the next set of
* owned skus.
* @return Bundle containing the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response codes
on failures.
* "INAPP_PURCHASE_ITEM_LIST" - StringArrayList containing the list of SKUs
* "INAPP_PURCHASE_DATA_LIST" - StringArrayList containing the purchase information
* "INAPP_DATA_SIGNATURE_LIST"- StringArrayList containing the signatures
* of the purchase information
* "INAPP_CONTINUATION_TOKEN" - String containing a continuation token for the
* next set of in-app purchases. Only set if the
* user has more owned skus than the current list.
*/
@Override public android.os.Bundle getPurchases(int apiVersion, java.lang.String packageName, java.lang.String type, java.lang.String continuationToken) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
android.os.Bundle _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(apiVersion);
_data.writeString(packageName);
_data.writeString(type);
_data.writeString(continuationToken);
mRemote.transact(Stub.TRANSACTION_getPurchases, _data, _reply, 0);
_reply.readException();
if ((0!=_reply.readInt())) {
_result = android.os.Bundle.CREATOR.createFromParcel(_reply);
}
else {
_result = null;
}
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public int consumePurchase(int apiVersion, java.lang.String packageName, java.lang.String purchaseToken) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(apiVersion);
_data.writeString(packageName);
_data.writeString(purchaseToken);
mRemote.transact(Stub.TRANSACTION_consumePurchase, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public int stub(int apiVersion, java.lang.String packageName, java.lang.String type) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(apiVersion);
_data.writeString(packageName);
_data.writeString(type);
mRemote.transact(Stub.TRANSACTION_stub, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
/**
* Returns a pending intent to launch the purchase flow for upgrading or downgrading a
* subscription. The existing owned SKU(s) should be provided along with the new SKU that
* the user is upgrading or downgrading to.
* @param apiVersion billing API version that the app is using, must be 5 or later
* @param packageName package name of the calling app
* @param oldSkus the SKU(s) that the user is upgrading or downgrading from,
* if null or empty this method will behave like {@link #getBuyIntent}
* @param newSku the SKU that the user is upgrading or downgrading to
* @param type of the item being purchased, currently must be "subs"
* @param developerPayload optional argument to be sent back with the purchase information
* @return Bundle containing the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response codes
* on failures.
* "BUY_INTENT" - PendingIntent to start the purchase flow
*
* The Pending intent should be launched with startIntentSenderForResult. When purchase flow
* has completed, the onActivityResult() will give a resultCode of OK or CANCELED.
* If the purchase is successful, the result data will contain the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response
* codes on failures.
* "INAPP_PURCHASE_DATA" - String in JSON format similar to
* '{"orderId":"12999763169054705758.1371079406387615",
* "packageName":"com.example.app",
* "productId":"exampleSku",
* "purchaseTime":1345678900000,
* "purchaseToken" : "122333444455555",
* "developerPayload":"example developer payload" }'
* "INAPP_DATA_SIGNATURE" - String containing the signature of the purchase data that
* was signed with the private key of the developer
*/
@Override public android.os.Bundle getBuyIntentToReplaceSkus(int apiVersion, java.lang.String packageName, java.util.List<java.lang.String> oldSkus, java.lang.String newSku, java.lang.String type, java.lang.String developerPayload) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
android.os.Bundle _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(apiVersion);
_data.writeString(packageName);
_data.writeStringList(oldSkus);
_data.writeString(newSku);
_data.writeString(type);
_data.writeString(developerPayload);
mRemote.transact(Stub.TRANSACTION_getBuyIntentToReplaceSkus, _data, _reply, 0);
_reply.readException();
if ((0!=_reply.readInt())) {
_result = android.os.Bundle.CREATOR.createFromParcel(_reply);
}
else {
_result = null;
}
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
/**
* Returns a pending intent to launch the purchase flow for an in-app item. This method is
* a variant of the {@link #getBuyIntent} method and takes an additional {@code extraParams}
* parameter. This parameter is a Bundle of optional keys and values that affect the
* operation of the method.
* @param apiVersion billing API version that the app is using, must be 6 or later
* @param packageName package name of the calling app
* @param sku the SKU of the in-app item as published in the developer console
* @param type of the in-app item being purchased ("inapp" for one-time purchases
* and "subs" for subscriptions)
* @param developerPayload optional argument to be sent back with the purchase information
* @extraParams a Bundle with the following optional keys:
* "skusToReplace" - List<String> - an optional list of SKUs that the user is
* upgrading or downgrading from.
* Pass this field if the purchase is upgrading or downgrading
* existing subscriptions.
* The specified SKUs are replaced with the SKUs that the user is
* purchasing. Google Play replaces the specified SKUs at the start of
* the next billing cycle.
* "replaceSkusProration" - Boolean - whether the user should be credited for any unused
* subscription time on the SKUs they are upgrading or downgrading.
* If you set this field to true, Google Play swaps out the old SKUs
* and credits the user with the unused value of their subscription
* time on a pro-rated basis.
* Google Play applies this credit to the new subscription, and does
* not begin billing the user for the new subscription until after
* the credit is used up.
* If you set this field to false, the user does not receive credit for
* any unused subscription time and the recurrence date does not
* change.
* Default value is true. Ignored if you do not pass skusToReplace.
* "accountId" - String - an optional obfuscated string that is uniquely
* associated with the user's account in your app.
* If you pass this value, Google Play can use it to detect irregular
* activity, such as many devices making purchases on the same
* account in a short period of time.
* Do not use the developer ID or the user's Google ID for this field.
* In addition, this field should not contain the user's ID in
* cleartext.
* We recommend that you use a one-way hash to generate a string from
* the user's ID, and store the hashed string in this field.
* "vr" - Boolean - an optional flag indicating whether the returned intent
* should start a VR purchase flow. The apiVersion must also be 7 or
* later to use this flag.
*/
@Override public android.os.Bundle getBuyIntentExtraParams(int apiVersion, java.lang.String packageName, java.lang.String sku, java.lang.String type, java.lang.String developerPayload, android.os.Bundle extraParams) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
android.os.Bundle _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(apiVersion);
_data.writeString(packageName);
_data.writeString(sku);
_data.writeString(type);
_data.writeString(developerPayload);
if ((extraParams!=null)) {
_data.writeInt(1);
extraParams.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_getBuyIntentExtraParams, _data, _reply, 0);
_reply.readException();
if ((0!=_reply.readInt())) {
_result = android.os.Bundle.CREATOR.createFromParcel(_reply);
}
else {
_result = null;
}
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
/**
* Returns the most recent purchase made by the user for each SKU, even if that purchase is
* expired, canceled, or consumed.
* @param apiVersion billing API version that the app is using, must be 6 or later
* @param packageName package name of the calling app
* @param type of the in-app items being requested ("inapp" for one-time purchases
* and "subs" for subscriptions)
* @param continuationToken to be set as null for the first call, if the number of owned
* skus is too large, a continuationToken is returned in the response bundle.
* This method can be called again with the continuation token to get the next set of
* owned skus.
* @param extraParams a Bundle with extra params that would be appended into http request
* query string. Not used at this moment. Reserved for future functionality.
* @return Bundle containing the following key-value pairs
* "RESPONSE_CODE" with int value: RESULT_OK(0) if success,
* {@link IabHelper#BILLING_RESPONSE_RESULT_*} response codes on failures.
*
* "INAPP_PURCHASE_ITEM_LIST" - ArrayList<String> containing the list of SKUs
* "INAPP_PURCHASE_DATA_LIST" - ArrayList<String> containing the purchase information
* "INAPP_DATA_SIGNATURE_LIST"- ArrayList<String> containing the signatures
* of the purchase information
* "INAPP_CONTINUATION_TOKEN" - String containing a continuation token for the
* next set of in-app purchases. Only set if the
* user has more owned skus than the current list.
*/
@Override public android.os.Bundle getPurchaseHistory(int apiVersion, java.lang.String packageName, java.lang.String type, java.lang.String continuationToken, android.os.Bundle extraParams) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
android.os.Bundle _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(apiVersion);
_data.writeString(packageName);
_data.writeString(type);
_data.writeString(continuationToken);
if ((extraParams!=null)) {
_data.writeInt(1);
extraParams.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_getPurchaseHistory, _data, _reply, 0);
_reply.readException();
if ((0!=_reply.readInt())) {
_result = android.os.Bundle.CREATOR.createFromParcel(_reply);
}
else {
_result = null;
}
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public int isBillingSupportedExtraParams(int apiVersion, java.lang.String packageName, java.lang.String type, android.os.Bundle extraParams) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(apiVersion);
_data.writeString(packageName);
_data.writeString(type);
if ((extraParams!=null)) {
_data.writeInt(1);
extraParams.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_isBillingSupportedExtraParams, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_isBillingSupported = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_getSkuDetails = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_getBuyIntent = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
static final int TRANSACTION_getPurchases = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
static final int TRANSACTION_consumePurchase = (android.os.IBinder.FIRST_CALL_TRANSACTION + 4);
static final int TRANSACTION_stub = (android.os.IBinder.FIRST_CALL_TRANSACTION + 5);
static final int TRANSACTION_getBuyIntentToReplaceSkus = (android.os.IBinder.FIRST_CALL_TRANSACTION + 6);
static final int TRANSACTION_getBuyIntentExtraParams = (android.os.IBinder.FIRST_CALL_TRANSACTION + 7);
static final int TRANSACTION_getPurchaseHistory = (android.os.IBinder.FIRST_CALL_TRANSACTION + 8);
static final int TRANSACTION_isBillingSupportedExtraParams = (android.os.IBinder.FIRST_CALL_TRANSACTION + 9);
}
public int isBillingSupported(int apiVersion, java.lang.String packageName, java.lang.String type) throws android.os.RemoteException;
/**
* Provides details of a list of SKUs
* Given a list of SKUs of a valid type in the skusBundle, this returns a bundle
* with a list JSON strings containing the productId, price, title and description.
* This API can be called with a maximum of 20 SKUs.
* @param apiVersion billing API version that the app is using
* @param packageName the package name of the calling app
* @param type of the in-app items ("inapp" for one-time purchases
* and "subs" for subscriptions)
* @param skusBundle bundle containing a StringArrayList of SKUs with key "ITEM_ID_LIST"
* @return Bundle containing the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response codes
* on failures.
* "DETAILS_LIST" with a StringArrayList containing purchase information
* in JSON format similar to:
* '{ "productId" : "exampleSku",
* "type" : "inapp",
* "price" : "$5.00",
* "price_currency": "USD",
* "price_amount_micros": 5000000,
* "title : "Example Title",
* "description" : "This is an example description" }'
*/
public android.os.Bundle getSkuDetails(int apiVersion, java.lang.String packageName, java.lang.String type, android.os.Bundle skusBundle) throws android.os.RemoteException;
/**
* Returns a pending intent to launch the purchase flow for an in-app item by providing a SKU,
* the type, a unique purchase token and an optional developer payload.
* @param apiVersion billing API version that the app is using
* @param packageName package name of the calling app
* @param sku the SKU of the in-app item as published in the developer console
* @param type of the in-app item being purchased ("inapp" for one-time purchases
* and "subs" for subscriptions)
* @param developerPayload optional argument to be sent back with the purchase information
* @return Bundle containing the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response codes
* on failures.
* "BUY_INTENT" - PendingIntent to start the purchase flow
*
* The Pending intent should be launched with startIntentSenderForResult. When purchase flow
* has completed, the onActivityResult() will give a resultCode of OK or CANCELED.
* If the purchase is successful, the result data will contain the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response
* codes on failures.
* "INAPP_PURCHASE_DATA" - String in JSON format similar to
* '{"orderId":"12999763169054705758.1371079406387615",
* "packageName":"com.example.app",
* "productId":"exampleSku",
* "purchaseTime":1345678900000,
* "purchaseToken" : "122333444455555",
* "developerPayload":"example developer payload" }'
* "INAPP_DATA_SIGNATURE" - String containing the signature of the purchase data that
* was signed with the private key of the developer
*/
public android.os.Bundle getBuyIntent(int apiVersion, java.lang.String packageName, java.lang.String sku, java.lang.String type, java.lang.String developerPayload) throws android.os.RemoteException;
/**
* Returns the current SKUs owned by the user of the type and package name specified along with
* purchase information and a signature of the data to be validated.
* This will return all SKUs that have been purchased in V3 and managed items purchased using
* V1 and V2 that have not been consumed.
* @param apiVersion billing API version that the app is using
* @param packageName package name of the calling app
* @param type of the in-app items being requested ("inapp" for one-time purchases
* and "subs" for subscriptions)
* @param continuationToken to be set as null for the first call, if the number of owned
* skus are too many, a continuationToken is returned in the response bundle.
* This method can be called again with the continuation token to get the next set of
* owned skus.
* @return Bundle containing the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response codes
on failures.
* "INAPP_PURCHASE_ITEM_LIST" - StringArrayList containing the list of SKUs
* "INAPP_PURCHASE_DATA_LIST" - StringArrayList containing the purchase information
* "INAPP_DATA_SIGNATURE_LIST"- StringArrayList containing the signatures
* of the purchase information
* "INAPP_CONTINUATION_TOKEN" - String containing a continuation token for the
* next set of in-app purchases. Only set if the
* user has more owned skus than the current list.
*/
public android.os.Bundle getPurchases(int apiVersion, java.lang.String packageName, java.lang.String type, java.lang.String continuationToken) throws android.os.RemoteException;
public int consumePurchase(int apiVersion, java.lang.String packageName, java.lang.String purchaseToken) throws android.os.RemoteException;
public int stub(int apiVersion, java.lang.String packageName, java.lang.String type) throws android.os.RemoteException;
/**
* Returns a pending intent to launch the purchase flow for upgrading or downgrading a
* subscription. The existing owned SKU(s) should be provided along with the new SKU that
* the user is upgrading or downgrading to.
* @param apiVersion billing API version that the app is using, must be 5 or later
* @param packageName package name of the calling app
* @param oldSkus the SKU(s) that the user is upgrading or downgrading from,
* if null or empty this method will behave like {@link #getBuyIntent}
* @param newSku the SKU that the user is upgrading or downgrading to
* @param type of the item being purchased, currently must be "subs"
* @param developerPayload optional argument to be sent back with the purchase information
* @return Bundle containing the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response codes
* on failures.
* "BUY_INTENT" - PendingIntent to start the purchase flow
*
* The Pending intent should be launched with startIntentSenderForResult. When purchase flow
* has completed, the onActivityResult() will give a resultCode of OK or CANCELED.
* If the purchase is successful, the result data will contain the following key-value pairs
* "RESPONSE_CODE" with int value, RESULT_OK(0) if success, appropriate response
* codes on failures.
* "INAPP_PURCHASE_DATA" - String in JSON format similar to
* '{"orderId":"12999763169054705758.1371079406387615",
* "packageName":"com.example.app",
* "productId":"exampleSku",
* "purchaseTime":1345678900000,
* "purchaseToken" : "122333444455555",
* "developerPayload":"example developer payload" }'
* "INAPP_DATA_SIGNATURE" - String containing the signature of the purchase data that
* was signed with the private key of the developer
*/
public android.os.Bundle getBuyIntentToReplaceSkus(int apiVersion, java.lang.String packageName, java.util.List<java.lang.String> oldSkus, java.lang.String newSku, java.lang.String type, java.lang.String developerPayload) throws android.os.RemoteException;
/**
* Returns a pending intent to launch the purchase flow for an in-app item. This method is
* a variant of the {@link #getBuyIntent} method and takes an additional {@code extraParams}
* parameter. This parameter is a Bundle of optional keys and values that affect the
* operation of the method.
* @param apiVersion billing API version that the app is using, must be 6 or later
* @param packageName package name of the calling app
* @param sku the SKU of the in-app item as published in the developer console
* @param type of the in-app item being purchased ("inapp" for one-time purchases
* and "subs" for subscriptions)
* @param developerPayload optional argument to be sent back with the purchase information
* @extraParams a Bundle with the following optional keys:
* "skusToReplace" - List<String> - an optional list of SKUs that the user is
* upgrading or downgrading from.
* Pass this field if the purchase is upgrading or downgrading
* existing subscriptions.
* The specified SKUs are replaced with the SKUs that the user is
* purchasing. Google Play replaces the specified SKUs at the start of
* the next billing cycle.
* "replaceSkusProration" - Boolean - whether the user should be credited for any unused
* subscription time on the SKUs they are upgrading or downgrading.
* If you set this field to true, Google Play swaps out the old SKUs
* and credits the user with the unused value of their subscription
* time on a pro-rated basis.
* Google Play applies this credit to the new subscription, and does
* not begin billing the user for the new subscription until after
* the credit is used up.
* If you set this field to false, the user does not receive credit for
* any unused subscription time and the recurrence date does not
* change.
* Default value is true. Ignored if you do not pass skusToReplace.
* "accountId" - String - an optional obfuscated string that is uniquely
* associated with the user's account in your app.
* If you pass this value, Google Play can use it to detect irregular
* activity, such as many devices making purchases on the same
* account in a short period of time.
* Do not use the developer ID or the user's Google ID for this field.
* In addition, this field should not contain the user's ID in
* cleartext.
* We recommend that you use a one-way hash to generate a string from
* the user's ID, and store the hashed string in this field.
* "vr" - Boolean - an optional flag indicating whether the returned intent
* should start a VR purchase flow. The apiVersion must also be 7 or
* later to use this flag.
*/
public android.os.Bundle getBuyIntentExtraParams(int apiVersion, java.lang.String packageName, java.lang.String sku, java.lang.String type, java.lang.String developerPayload, android.os.Bundle extraParams) throws android.os.RemoteException;
/**
* Returns the most recent purchase made by the user for each SKU, even if that purchase is
* expired, canceled, or consumed.
* @param apiVersion billing API version that the app is using, must be 6 or later
* @param packageName package name of the calling app
* @param type of the in-app items being requested ("inapp" for one-time purchases
* and "subs" for subscriptions)
* @param continuationToken to be set as null for the first call, if the number of owned
* skus is too large, a continuationToken is returned in the response bundle.
* This method can be called again with the continuation token to get the next set of
* owned skus.
* @param extraParams a Bundle with extra params that would be appended into http request
* query string. Not used at this moment. Reserved for future functionality.
* @return Bundle containing the following key-value pairs
* "RESPONSE_CODE" with int value: RESULT_OK(0) if success,
* {@link IabHelper#BILLING_RESPONSE_RESULT_*} response codes on failures.
*
* "INAPP_PURCHASE_ITEM_LIST" - ArrayList<String> containing the list of SKUs
* "INAPP_PURCHASE_DATA_LIST" - ArrayList<String> containing the purchase information
* "INAPP_DATA_SIGNATURE_LIST"- ArrayList<String> containing the signatures
* of the purchase information
* "INAPP_CONTINUATION_TOKEN" - String containing a continuation token for the
* next set of in-app purchases. Only set if the
* user has more owned skus than the current list.
*/
public android.os.Bundle getPurchaseHistory(int apiVersion, java.lang.String packageName, java.lang.String type, java.lang.String continuationToken, android.os.Bundle extraParams) throws android.os.RemoteException;
public int isBillingSupportedExtraParams(int apiVersion, java.lang.String packageName, java.lang.String type, android.os.Bundle extraParams) throws android.os.RemoteException;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,16 @@
package com.juce;
import com.google.firebase.iid.*;
public final class JuceFirebaseInstanceIdService extends FirebaseInstanceIdService
{
private native void firebaseInstanceIdTokenRefreshed (String token);
@Override
public void onTokenRefresh()
{
String token = FirebaseInstanceId.getInstance().getToken();
firebaseInstanceIdTokenRefreshed (token);
}
}

View File

@ -0,0 +1,35 @@
package com.juce;
import com.google.firebase.messaging.*;
public final class JuceFirebaseMessagingService extends FirebaseMessagingService
{
private native void firebaseRemoteMessageReceived (RemoteMessage message);
private native void firebaseRemoteMessagesDeleted();
private native void firebaseRemoteMessageSent (String messageId);
private native void firebaseRemoteMessageSendError (String messageId, String error);
@Override
public void onMessageReceived (RemoteMessage message)
{
firebaseRemoteMessageReceived (message);
}
@Override
public void onDeletedMessages()
{
firebaseRemoteMessagesDeleted();
}
@Override
public void onMessageSent (String messageId)
{
firebaseRemoteMessageSent (messageId);
}
@Override
public void onSendError (String messageId, Exception e)
{
firebaseRemoteMessageSendError (messageId, e.toString());
}
}

View File

@ -0,0 +1,295 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
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
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <CoreData/CoreData.h>
#import <MobileCoreServices/MobileCoreServices.h>
#include <sys/fcntl.h>
#else
#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
#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>
//==============================================================================
#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 0x0501
#else
#define _WIN32_WINNT 0x0602
#endif
#define _UNICODE 1
#define UNICODE 1
#ifndef _WIN32_IE
#define _WIN32_IE 0x0500
#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_MSVC && ! 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>
//==============================================================================
#elif JUCE_BSD
#include <arpa/inet.h>
#include <dirent.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/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/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <utime.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>
// 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,625 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
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) \
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) \
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) \
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) \
STATICMETHOD (getExternalStorageDirectory, "getExternalStorageDirectory", "()Ljava/io/File;") \
STATICMETHOD (getExternalStoragePublicDirectory, "getExternalStoragePublicDirectory", "(Ljava/lang/String;)Ljava/io/File;") \
DECLARE_JNI_CLASS (AndroidEnvironment, "android/os/Environment");
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
METHOD (close, "close", "()V") \
METHOD (flush, "flush", "()V") \
METHOD (write, "write", "([BII)V")
DECLARE_JNI_CLASS (AndroidOutputStream, "java/io/OutputStream");
#undef JNI_CLASS_MEMBERS
//==============================================================================
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 (android.activity.callObjectMethod (JuceAppActivity.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 ("Download").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 (android.activity.callObjectMethod (JuceAppActivity.getContentResolver));
if (contentResolver == 0)
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 (android.activity.callObjectMethod (JuceAppActivity.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 (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 getWellKnownFolder (const String& folderId)
{
auto* env = getEnv();
LocalRef<jobject> downloadFolder (env->CallStaticObjectMethod (AndroidEnvironment,
AndroidEnvironment.getExternalStoragePublicDirectory,
javaString (folderId).get()));
return (downloadFolder ? juceFile (downloadFolder) : File());
}
//==============================================================================
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 (getSDKVersion() >= 19)
{
auto* env = getEnv();
static jmethodID m = (env->GetMethodID (JuceAppActivity, "getExternalFilesDirs",
"(Ljava/lang/String;)[Ljava/io/File;"));
if (m == 0)
return {};
auto paths = convertFileArray (LocalRef<jobject> (android.activity.callObjectMethod (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;
DirectoryIterator iter (mountFolder, false, "*", File::findDirectories);
while (iter.next())
{
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 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 int getSDKVersion()
{
static int sdkVersion
= getEnv()->CallStaticIntMethod (JuceAppActivity,
JuceAppActivity.getAndroidSDKVersion);
return sdkVersion;
}
static LocalRef<jobject> urlToUri (const URL& url)
{
return LocalRef<jobject> (getEnv()->CallStaticObjectMethod (AndroidUri, AndroidUri.parse,
javaString (url.toString (true)).get()));
}
//==============================================================================
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 (cursor == 0)
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()
{
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() != 0 ? new AndroidContentUriOutputStream (static_cast<LocalRef<jobject>&&> (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 getSpecialFile (jmethodID type)
{
return File (juceString (LocalRef<jstring> ((jstring) getEnv()->CallStaticObjectMethod (JuceAppActivity, type))));
}
File File::getSpecialLocation (const SpecialLocationType type)
{
switch (type)
{
case userHomeDirectory:
case userApplicationDataDirectory:
case userDesktopDirectory:
case commonApplicationDataDirectory:
return File (android.appDataDir);
case userDocumentsDirectory:
case commonDocumentsDirectory: return getSpecialFile (JuceAppActivity.getDocumentsFolder);
case userPicturesDirectory: return getSpecialFile (JuceAppActivity.getPicturesFolder);
case userMusicDirectory: return getSpecialFile (JuceAppActivity.getMusicFolder);
case userMoviesDirectory: return getSpecialFile (JuceAppActivity.getMoviesFolder);
case globalApplicationsDirectory:
return File ("/system/app");
case tempDirectory:
{
File tmp = File (android.appDataDir).getChildFile (".temp");
tmp.createDirectory();
return File (tmp.getFullPathName());
}
case invokedExecutableFile:
case currentExecutableFile:
case currentApplicationFile:
case hostApplicationPath:
return juce_getExecutableFile();
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&)
{
const LocalRef<jstring> t (javaString (fileName));
android.activity.callVoidMethod (JuceAppActivity.launchURL, t.get());
return true;
}
void File::revealToUser() const
{
}
//==============================================================================
class SingleMediaScanner : public MediaScannerConnectionClient
{
public:
SingleMediaScanner (const String& filename)
: msc (getEnv()->NewObject (MediaScannerConnection,
MediaScannerConnection.constructor,
android.activity.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 != 0)
{
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,781 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
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 ! (defined (JUCE_ANDROID_ACTIVITY_CLASSNAME) && defined (JUCE_ANDROID_ACTIVITY_CLASSPATH))
#error "The JUCE_ANDROID_ACTIVITY_CLASSNAME and JUCE_ANDROID_ACTIVITY_CLASSPATH macros must be set!"
#endif
//==============================================================================
extern JNIEnv* getEnv() noexcept;
// You should rarely need to use this function. Only if you expect callbacks
// on a java thread which you did not create yourself.
extern void setEnv (JNIEnv* env) noexcept;
/* @internal */
extern JNIEnv* attachAndroidJNI() noexcept;
//==============================================================================
class GlobalRef
{
public:
inline GlobalRef() noexcept : obj (0) {}
inline explicit GlobalRef (jobject o) : obj (retain (o)) {}
inline GlobalRef (const GlobalRef& other) : obj (retain (other.obj)) {}
inline GlobalRef (GlobalRef && other) noexcept : obj (0) { std::swap (other.obj, obj); }
~GlobalRef() { clear(); }
inline void clear()
{
if (obj != 0)
{
getEnv()->DeleteGlobalRef (obj);
obj = 0;
}
}
inline GlobalRef& operator= (const GlobalRef& other)
{
jobject newObj = retain (other.obj);
clear();
obj = newObj;
return *this;
}
inline GlobalRef& operator= (GlobalRef&& other)
{
clear();
std::swap (obj, other.obj);
return *this;
}
//==============================================================================
inline operator jobject() const noexcept { return obj; }
inline jobject get() const noexcept { return obj; }
//==============================================================================
#define DECLARE_CALL_TYPE_METHOD(returnType, typeName) \
returnType call##typeName##Method (jmethodID methodID, ... ) const \
{ \
va_list args; \
va_start (args, methodID); \
returnType result = getEnv()->Call##typeName##MethodV (obj, methodID, args); \
va_end (args); \
return result; \
}
DECLARE_CALL_TYPE_METHOD (jobject, Object)
DECLARE_CALL_TYPE_METHOD (jboolean, Boolean)
DECLARE_CALL_TYPE_METHOD (jbyte, Byte)
DECLARE_CALL_TYPE_METHOD (jchar, Char)
DECLARE_CALL_TYPE_METHOD (jshort, Short)
DECLARE_CALL_TYPE_METHOD (jint, Int)
DECLARE_CALL_TYPE_METHOD (jlong, Long)
DECLARE_CALL_TYPE_METHOD (jfloat, Float)
DECLARE_CALL_TYPE_METHOD (jdouble, Double)
#undef DECLARE_CALL_TYPE_METHOD
void callVoidMethod (jmethodID methodID, ... ) const
{
va_list args;
va_start (args, methodID);
getEnv()->CallVoidMethodV (obj, methodID, args);
va_end (args);
}
private:
//==============================================================================
jobject obj = 0;
static inline jobject retain (jobject obj)
{
return obj == 0 ? 0 : getEnv()->NewGlobalRef (obj);
}
};
//==============================================================================
template <typename JavaType>
class LocalRef
{
public:
explicit inline LocalRef() noexcept : obj (0) {}
explicit inline LocalRef (JavaType o) noexcept : obj (o) {}
inline LocalRef (const LocalRef& other) noexcept : obj (retain (other.obj)) {}
inline LocalRef (LocalRef&& other) noexcept : obj (0) { std::swap (obj, other.obj); }
~LocalRef() { clear(); }
void clear()
{
if (obj != 0)
{
getEnv()->DeleteLocalRef (obj);
obj = 0;
}
}
LocalRef& operator= (const LocalRef& other)
{
JavaType newObj = retain (other.obj);
clear();
obj = newObj;
return *this;
}
LocalRef& operator= (LocalRef&& other)
{
clear();
std::swap (other.obj, obj);
return *this;
}
inline operator JavaType() const noexcept { return obj; }
inline JavaType get() const noexcept { return obj; }
private:
JavaType obj;
static JavaType retain (JavaType obj)
{
return obj == 0 ? 0 : (JavaType) getEnv()->NewLocalRef (obj);
}
};
//==============================================================================
class JNIClassBase
{
public:
explicit JNIClassBase (const char* classPath);
virtual ~JNIClassBase();
inline operator jclass() const noexcept { return classRef; }
static void initialiseAllClasses (JNIEnv*);
static void releaseAllClasses (JNIEnv*);
protected:
virtual void initialiseFields (JNIEnv*) = 0;
jmethodID resolveMethod (JNIEnv*, const char* methodName, const char* params);
jmethodID resolveStaticMethod (JNIEnv*, const char* methodName, const char* params);
jfieldID resolveField (JNIEnv*, const char* fieldName, const char* signature);
jfieldID resolveStaticField (JNIEnv*, const char* fieldName, const char* signature);
private:
const char* const classPath;
jclass classRef;
static Array<JNIClassBase*>& getClasses();
void initialise (JNIEnv*);
void release (JNIEnv*);
JUCE_DECLARE_NON_COPYABLE (JNIClassBase)
};
//==============================================================================
#define CREATE_JNI_METHOD(methodID, stringName, params) methodID = resolveMethod (env, stringName, params);
#define CREATE_JNI_STATICMETHOD(methodID, stringName, params) methodID = resolveStaticMethod (env, stringName, params);
#define CREATE_JNI_FIELD(fieldID, stringName, signature) fieldID = resolveField (env, stringName, signature);
#define CREATE_JNI_STATICFIELD(fieldID, stringName, signature) fieldID = resolveStaticField (env, stringName, signature);
#define DECLARE_JNI_METHOD(methodID, stringName, params) jmethodID methodID;
#define DECLARE_JNI_FIELD(fieldID, stringName, signature) jfieldID fieldID;
#define DECLARE_JNI_CLASS(CppClassName, javaPath) \
class CppClassName ## _Class : public JNIClassBase \
{ \
public: \
CppClassName ## _Class() : JNIClassBase (javaPath) {} \
\
void initialiseFields (JNIEnv* env) \
{ \
ignoreUnused (env); \
JNI_CLASS_MEMBERS (CREATE_JNI_METHOD, CREATE_JNI_STATICMETHOD, CREATE_JNI_FIELD, CREATE_JNI_STATICFIELD); \
} \
\
JNI_CLASS_MEMBERS (DECLARE_JNI_METHOD, DECLARE_JNI_METHOD, DECLARE_JNI_FIELD, DECLARE_JNI_FIELD) \
}; \
static CppClassName ## _Class CppClassName;
//==============================================================================
#if defined (__arm__)
#define JUCE_ARM_SOFT_FLOAT_ABI __attribute__ ((pcs("aapcs")))
#else
#define JUCE_ARM_SOFT_FLOAT_ABI
#endif
#define JUCE_JNI_CALLBACK(className, methodName, returnType, params) \
extern "C" __attribute__ ((visibility("default"))) JUCE_ARM_SOFT_FLOAT_ABI returnType JUCE_JOIN_MACRO (JUCE_JOIN_MACRO (Java_, className), _ ## methodName) params
//==============================================================================
class AndroidSystem
{
public:
AndroidSystem();
void initialise (JNIEnv*, jobject activity, jstring appFile, jstring appDataDir);
void shutdown (JNIEnv*);
//==============================================================================
GlobalRef activity;
String appFile, appDataDir;
int screenWidth, screenHeight, dpi;
};
extern AndroidSystem android;
//==============================================================================
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
METHOD (createNewView, "createNewView", "(ZJ)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$ComponentPeerView;") \
METHOD (deleteView, "deleteView", "(L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$ComponentPeerView;)V") \
METHOD (createNativeSurfaceView, "createNativeSurfaceView", "(J)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$NativeSurfaceView;") \
METHOD (finish, "finish", "()V") \
METHOD (getWindowManager, "getWindowManager", "()Landroid/view/WindowManager;") \
METHOD (setRequestedOrientation, "setRequestedOrientation", "(I)V") \
METHOD (getClipboardContent, "getClipboardContent", "()Ljava/lang/String;") \
METHOD (setClipboardContent, "setClipboardContent", "(Ljava/lang/String;)V") \
METHOD (excludeClipRegion, "excludeClipRegion", "(Landroid/graphics/Canvas;FFFF)V") \
METHOD (renderGlyph, "renderGlyph", "(CCLandroid/graphics/Paint;Landroid/graphics/Matrix;Landroid/graphics/Rect;)[I") \
STATICMETHOD (createHTTPStream, "createHTTPStream", "(Ljava/lang/String;Z[BLjava/lang/String;I[ILjava/lang/StringBuffer;ILjava/lang/String;)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$HTTPStream;") \
METHOD (launchURL, "launchURL", "(Ljava/lang/String;)V") \
METHOD (showMessageBox, "showMessageBox", "(Ljava/lang/String;Ljava/lang/String;J)V") \
METHOD (showOkCancelBox, "showOkCancelBox", "(Ljava/lang/String;Ljava/lang/String;JLjava/lang/String;Ljava/lang/String;)V") \
METHOD (showYesNoCancelBox, "showYesNoCancelBox", "(Ljava/lang/String;Ljava/lang/String;J)V") \
STATICMETHOD (getLocaleValue, "getLocaleValue", "(Z)Ljava/lang/String;") \
STATICMETHOD (getDocumentsFolder, "getDocumentsFolder", "()Ljava/lang/String;") \
STATICMETHOD (getPicturesFolder, "getPicturesFolder", "()Ljava/lang/String;") \
STATICMETHOD (getMusicFolder, "getMusicFolder", "()Ljava/lang/String;") \
STATICMETHOD (getDownloadsFolder, "getDownloadsFolder", "()Ljava/lang/String;") \
STATICMETHOD (getMoviesFolder, "getMoviesFolder", "()Ljava/lang/String;") \
METHOD (getTypeFaceFromAsset, "getTypeFaceFromAsset", "(Ljava/lang/String;)Landroid/graphics/Typeface;") \
METHOD (getTypeFaceFromByteArray, "getTypeFaceFromByteArray", "([B)Landroid/graphics/Typeface;") \
METHOD (setScreenSaver, "setScreenSaver", "(Z)V") \
METHOD (getScreenSaver, "getScreenSaver", "()Z") \
METHOD (getAndroidMidiDeviceManager, "getAndroidMidiDeviceManager", "()L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$MidiDeviceManager;") \
METHOD (getAndroidBluetoothManager, "getAndroidBluetoothManager", "()L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$BluetoothManager;") \
STATICMETHOD (getAndroidSDKVersion, "getAndroidSDKVersion", "()I") \
METHOD (audioManagerGetProperty, "audioManagerGetProperty", "(Ljava/lang/String;)Ljava/lang/String;") \
METHOD (hasSystemFeature, "hasSystemFeature", "(Ljava/lang/String;)Z" ) \
METHOD (requestRuntimePermission, "requestRuntimePermission", "(IJ)V" ) \
METHOD (isPermissionGranted, "isPermissionGranted", "(I)Z" ) \
METHOD (isPermissionDeclaredInManifest, "isPermissionDeclaredInManifest", "(I)Z" ) \
METHOD (getAssets, "getAssets", "()Landroid/content/res/AssetManager;") \
METHOD (getSystemService, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;") \
METHOD (getPackageManager, "getPackageManager", "()Landroid/content/pm/PackageManager;") \
METHOD (getPackageName, "getPackageName", "()Ljava/lang/String;") \
METHOD (getResources, "getResources", "()Landroid/content/res/Resources;") \
METHOD (createInvocationHandler, "createInvocationHandler", "(J)Ljava/lang/reflect/InvocationHandler;") \
METHOD (invocationHandlerContextDeleted, "invocationHandlerContextDeleted", "(Ljava/lang/reflect/InvocationHandler;)V") \
METHOD (bindService, "bindService", "(Landroid/content/Intent;Landroid/content/ServiceConnection;I)Z") \
METHOD (unbindService, "unbindService", "(Landroid/content/ServiceConnection;)V") \
METHOD (startIntentSenderForResult, "startIntentSenderForResult", "(Landroid/content/IntentSender;ILandroid/content/Intent;III)V") \
METHOD (moveTaskToBack, "moveTaskToBack", "(Z)Z") \
METHOD (startActivity, "startActivity", "(Landroid/content/Intent;)V") \
METHOD (startActivityForResult, "startActivityForResult", "(Landroid/content/Intent;I)V") \
METHOD (getContentResolver, "getContentResolver", "()Landroid/content/ContentResolver;") \
METHOD (addAppPausedResumedListener, "addAppPausedResumedListener", "(L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$AppPausedResumedListener;J)V") \
METHOD (removeAppPausedResumedListener, "removeAppPausedResumedListener", "(L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$AppPausedResumedListener;J)V")
DECLARE_JNI_CLASS (JuceAppActivity, JUCE_ANDROID_ACTIVITY_CLASSPATH);
#undef JNI_CLASS_MEMBERS
//==============================================================================
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
STATICMETHOD (createBitmap, "createBitmap", "(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;") \
STATICMETHOD (createBitmapFrom, "createBitmap", "(Landroid/graphics/Bitmap;IIIILandroid/graphics/Matrix;Z)Landroid/graphics/Bitmap;") \
METHOD (compress, "compress", "(Landroid/graphics/Bitmap$CompressFormat;ILjava/io/OutputStream;)Z") \
METHOD (getHeight, "getHeight", "()I") \
METHOD (getWidth, "getWidth", "()I") \
METHOD (recycle, "recycle", "()V") \
METHOD (setPixel, "setPixel", "(III)V")
DECLARE_JNI_CLASS (AndroidBitmap, "android/graphics/Bitmap");
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
STATICMETHOD (valueOf, "valueOf", "(Ljava/lang/String;)Landroid/graphics/Bitmap$Config;")
DECLARE_JNI_CLASS (AndroidBitmapConfig, "android/graphics/Bitmap$Config");
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
STATICMETHOD (decodeByteArray, "decodeByteArray", "([BII)Landroid/graphics/Bitmap;")
DECLARE_JNI_CLASS (AndroidBitmapFactory, "android/graphics/BitmapFactory");
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
STATICMETHOD (dumpReferenceTables, "dumpReferenceTables", "()V")
DECLARE_JNI_CLASS (AndroidDebug, "android/os/Debug");
#undef JNI_CLASS_MEMBERS
#define JUCE_LOG_JNI_REFERENCES_TABLE getEnv()->CallStaticVoidMethod (AndroidDebug, AndroidDebug.dumpReferenceTables);
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
METHOD (getRotation, "getRotation", "()I")
DECLARE_JNI_CLASS (AndroidDisplay, "android/view/Display");
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
METHOD (constructor, "<init>", "()V") \
METHOD (constructorWithLooper, "<init>", "(Landroid/os/Looper;)V") \
METHOD (post, "post", "(Ljava/lang/Runnable;)Z") \
METHOD (postDelayed, "postDelayed", "(Ljava/lang/Runnable;J)Z") \
DECLARE_JNI_CLASS (AndroidHandler, "android/os/Handler");
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
METHOD (constructor, "<init>", "(Ljava/lang/String;)V") \
METHOD (getLooper, "getLooper", "()Landroid/os/Looper;") \
METHOD (join, "join", "()V") \
METHOD (quitSafely, "quitSafely", "()Z") \
METHOD (start, "start", "()V")
DECLARE_JNI_CLASS (AndroidHandlerThread, "android/os/HandlerThread");
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
STATICMETHOD (createChooser, "createChooser", "(Landroid/content/Intent;Ljava/lang/CharSequence;)Landroid/content/Intent;") \
METHOD (addCategory, "addCategory", "(Ljava/lang/String;)Landroid/content/Intent;") \
METHOD (constructor, "<init>", "()V") \
METHOD (constructorWithContextAndClass, "<init>", "(Landroid/content/Context;Ljava/lang/Class;)V") \
METHOD (constructWithString, "<init>", "(Ljava/lang/String;)V") \
METHOD (getAction, "getAction", "()Ljava/lang/String;") \
METHOD (getCategories, "getCategories", "()Ljava/util/Set;") \
METHOD (getData, "getData", "()Landroid/net/Uri;") \
METHOD (getExtras, "getExtras", "()Landroid/os/Bundle;") \
METHOD (getIntExtra, "getIntExtra", "(Ljava/lang/String;I)I") \
METHOD (getStringExtra, "getStringExtra", "(Ljava/lang/String;)Ljava/lang/String;") \
METHOD (putExtra, "putExtra", "(Ljava/lang/String;Ljava/lang/CharSequence;)Landroid/content/Intent;") \
METHOD (putExtras, "putExtras", "(Landroid/os/Bundle;)Landroid/content/Intent;") \
METHOD (putExtraString, "putExtra", "(Ljava/lang/String;Ljava/lang/String;)Landroid/content/Intent;") \
METHOD (putExtraStrings, "putExtra", "(Ljava/lang/String;[Ljava/lang/String;)Landroid/content/Intent;") \
METHOD (putExtraParcelable, "putExtra", "(Ljava/lang/String;Landroid/os/Parcelable;)Landroid/content/Intent;") \
METHOD (putParcelableArrayListExtra, "putParcelableArrayListExtra", "(Ljava/lang/String;Ljava/util/ArrayList;)Landroid/content/Intent;") \
METHOD (setAction, "setAction", "(Ljava/lang/String;)Landroid/content/Intent;") \
METHOD (setFlags, "setFlags", "(I)Landroid/content/Intent;") \
METHOD (setPackage, "setPackage", "(Ljava/lang/String;)Landroid/content/Intent;") \
METHOD (setType, "setType", "(Ljava/lang/String;)Landroid/content/Intent;") \
DECLARE_JNI_CLASS (AndroidIntent, "android/content/Intent");
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
METHOD (constructor, "<init>", "()V") \
METHOD (postRotate, "postRotate", "(FFF)Z") \
METHOD (postScale, "postScale", "(FFFF)Z") \
METHOD (postTranslate, "postTranslate", "(FF)Z") \
METHOD (setValues, "setValues", "([F)V")
DECLARE_JNI_CLASS (AndroidMatrix, "android/graphics/Matrix");
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
METHOD (getPackageInfo, "getPackageInfo", "(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;")
DECLARE_JNI_CLASS (AndroidPackageManager, "android/content/pm/PackageManager");
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
METHOD (constructor, "<init>", "(I)V") \
METHOD (setColor, "setColor", "(I)V") \
METHOD (setAlpha, "setAlpha", "(I)V") \
METHOD (setTypeface, "setTypeface", "(Landroid/graphics/Typeface;)Landroid/graphics/Typeface;") \
METHOD (ascent, "ascent", "()F") \
METHOD (descent, "descent", "()F") \
METHOD (setTextSize, "setTextSize", "(F)V") \
METHOD (getTextWidths, "getTextWidths", "(Ljava/lang/String;[F)I") \
METHOD (setTextScaleX, "setTextScaleX", "(F)V") \
METHOD (getTextPath, "getTextPath", "(Ljava/lang/String;IIFFLandroid/graphics/Path;)V") \
METHOD (setShader, "setShader", "(Landroid/graphics/Shader;)Landroid/graphics/Shader;") \
DECLARE_JNI_CLASS (AndroidPaint, "android/graphics/Paint");
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
STATICMETHOD (getActivity, "getActivity", "(Landroid/content/Context;ILandroid/content/Intent;I)Landroid/app/PendingIntent;") \
METHOD (getIntentSender, "getIntentSender", "()Landroid/content/IntentSender;")
DECLARE_JNI_CLASS (AndroidPendingIntent, "android/app/PendingIntent");
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
METHOD (toString, "toString", "()Ljava/lang/String;")
DECLARE_JNI_CLASS (AndroidRange, "android/util/Range");
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
METHOD (constructor, "<init>", "(IIII)V") \
FIELD (left, "left", "I") \
FIELD (right, "right", "I") \
FIELD (top, "top", "I") \
FIELD (bottom, "bottom", "I") \
DECLARE_JNI_CLASS (AndroidRect, "android/graphics/Rect");
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
METHOD (getIdentifier, "getIdentifier", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I") \
METHOD (openRawResourceFd, "openRawResourceFd", "(I)Landroid/content/res/AssetFileDescriptor;")
DECLARE_JNI_CLASS (AndroidResources, "android/content/res/Resources")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
METHOD (getHeight, "getHeight", "()I") \
METHOD (getWidth, "getWidth", "()I")
DECLARE_JNI_CLASS (AndroidSize, "android/util/Size");
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
STATICMETHOD (parse, "parse", "(Ljava/lang/String;)Landroid/net/Uri;") \
METHOD (toString, "toString", "()Ljava/lang/String;")
DECLARE_JNI_CLASS (AndroidUri, "android/net/Uri");
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
METHOD (layout, "layout", "(IIII)V") \
METHOD (getLeft, "getLeft", "()I") \
METHOD (getTop, "getTop", "()I") \
METHOD (getWidth, "getWidth", "()I") \
METHOD (getHeight, "getHeight", "()I") \
METHOD (getLocationOnScreen, "getLocationOnScreen", "([I)V") \
METHOD (getParent, "getParent", "()Landroid/view/ViewParent;") \
METHOD (bringToFront, "bringToFront", "()V") \
METHOD (requestFocus, "requestFocus", "()Z") \
METHOD (hasFocus, "hasFocus", "()Z") \
METHOD (invalidate, "invalidate", "(IIII)V") \
METHOD (setVisibility, "setVisibility", "(I)V")
DECLARE_JNI_CLASS (AndroidView, "android/view/View");
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
METHOD (addView, "addView", "(Landroid/view/View;)V") \
METHOD (removeView, "removeView", "(Landroid/view/View;)V")
DECLARE_JNI_CLASS (AndroidViewGroup, "android/view/ViewGroup")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
METHOD (getDefaultDisplay, "getDefaultDisplay", "()Landroid/view/Display;")
DECLARE_JNI_CLASS (AndroidWindowManager, "android/view/WindowManager");
#undef JNI_CLASS_MEMBERS
//==============================================================================
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
METHOD (constructor, "<init>", "(I)V") \
METHOD (add, "add", "(Ljava/lang/Object;)Z") \
METHOD (iterator, "iterator", "()Ljava/util/Iterator;") \
METHOD (get, "get", "(I)Ljava/lang/Object;") \
METHOD (size, "size", "()I")
DECLARE_JNI_CLASS (JavaArrayList, "java/util/ArrayList");
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
STATICMETHOD (valueOf, "valueOf", "(Z)Ljava/lang/Boolean;") \
METHOD (booleanValue, "booleanValue", "()Z")
DECLARE_JNI_CLASS (JavaBoolean, "java/lang/Boolean");
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
METHOD (constructor, "<init>", "()V") \
METHOD (containsKey, "containsKey", "(Ljava/lang/String;)Z") \
METHOD (get, "get", "(Ljava/lang/String;)Ljava/lang/Object;") \
METHOD (getBoolean, "getBoolean", "(Ljava/lang/String;)Z") \
METHOD (getBundle, "getBundle", "(Ljava/lang/String;)Landroid/os/Bundle;") \
METHOD (getCharSequence, "getCharSequence", "(Ljava/lang/String;)Ljava/lang/CharSequence;") \
METHOD (getInt, "getInt", "(Ljava/lang/String;)I") \
METHOD (getLong, "getLong", "(Ljava/lang/String;)J") \
METHOD (getLongArray, "getLongArray", "(Ljava/lang/String;)[J") \
METHOD (getParcelable, "getParcelable", "(Ljava/lang/String;)Landroid/os/Parcelable;") \
METHOD (getString, "getString", "(Ljava/lang/String;)Ljava/lang/String;") \
METHOD (getStringArrayList, "getStringArrayList", "(Ljava/lang/String;)Ljava/util/ArrayList;") \
METHOD (keySet, "keySet", "()Ljava/util/Set;") \
METHOD (putBoolean, "putBoolean", "(Ljava/lang/String;Z)V") \
METHOD (putBundle, "putBundle", "(Ljava/lang/String;Landroid/os/Bundle;)V") \
METHOD (putInt, "putInt", "(Ljava/lang/String;I)V") \
METHOD (putLong, "putLong", "(Ljava/lang/String;J)V") \
METHOD (putLongArray, "putLongArray", "(Ljava/lang/String;[J)V") \
METHOD (putString, "putString", "(Ljava/lang/String;Ljava/lang/String;)V") \
METHOD (putStringArrayList, "putStringArrayList", "(Ljava/lang/String;Ljava/util/ArrayList;)V")
DECLARE_JNI_CLASS (JavaBundle, "android/os/Bundle");
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
METHOD (get, "get", "([B)Ljava/nio/ByteBuffer;") \
METHOD (remaining, "remaining", "()I")
DECLARE_JNI_CLASS (JavaByteBuffer, "java/nio/ByteBuffer");
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
METHOD (toString, "toString", "()Ljava/lang/String;")
DECLARE_JNI_CLASS (JavaCharSequence, "java/lang/CharSequence");
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
STATICMETHOD (forName, "forName", "(Ljava/lang/String;)Ljava/lang/Class;") \
METHOD (getName, "getName", "()Ljava/lang/String;") \
METHOD (getModifiers, "getModifiers", "()I") \
METHOD (isAnnotation, "isAnnotation", "()Z") \
METHOD (isAnonymousClass, "isAnonymousClass", "()Z") \
METHOD (isArray, "isArray", "()Z") \
METHOD (isEnum, "isEnum", "()Z") \
METHOD (isInterface, "isInterface", "()Z") \
METHOD (isLocalClass, "isLocalClass", "()Z") \
METHOD (isMemberClass, "isMemberClass", "()Z") \
METHOD (isPrimitive, "isPrimitive", "()Z") \
METHOD (isSynthetic, "isSynthetic", "()Z") \
METHOD (getComponentType, "getComponentType", "()Ljava/lang/Class;") \
METHOD (getSuperclass, "getSuperclass", "()Ljava/lang/Class;") \
METHOD (getClassLoader, "getClassLoader", "()Ljava/lang/ClassLoader;") \
DECLARE_JNI_CLASS (JavaClass, "java/lang/Class");
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
METHOD (toString, "toString", "()Ljava/lang/String;")
DECLARE_JNI_CLASS (JavaEnum, "java/lang/Enum");
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
METHOD (constructor, "<init>", "(Ljava/lang/String;)V") \
METHOD (getAbsolutePath, "getAbsolutePath", "()Ljava/lang/String;") \
METHOD (length, "length", "()J")
DECLARE_JNI_CLASS (JavaFile, "java/io/File");
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
METHOD (constructor, "<init>", "(Ljava/lang/String;)V") \
METHOD (close, "close", "()V") \
METHOD (read, "read", "([B)I")
DECLARE_JNI_CLASS (JavaFileInputStream, "java/io/FileInputStream");
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
METHOD (constructor, "<init>", "(Ljava/lang/String;)V") \
METHOD (close, "close", "()V") \
METHOD (write, "write", "([BII)V")
DECLARE_JNI_CLASS (JavaFileOutputStream, "java/io/FileOutputStream");
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
METHOD (constructor, "<init>", "()V") \
METHOD (constructorWithCapacity, "<init>", "(I)V")
DECLARE_JNI_CLASS (JavaHashMap, "java/util/HashMap");
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
STATICMETHOD (parseInt, "parseInt", "(Ljava/lang/String;I)I") \
STATICMETHOD (valueOf, "valueOf", "(I)Ljava/lang/Integer;") \
METHOD (intValue, "intValue", "()I")
DECLARE_JNI_CLASS (JavaInteger, "java/lang/Integer");
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
METHOD (hasNext, "hasNext", "()Z") \
METHOD (next, "next", "()Ljava/lang/Object;")
DECLARE_JNI_CLASS (JavaIterator, "java/util/Iterator");
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
METHOD (get, "get", "(I)Ljava/lang/Object;") \
METHOD (size, "size", "()I")
DECLARE_JNI_CLASS (JavaList, "java/util/List");
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
METHOD (constructor, "<init>", "(J)V")
DECLARE_JNI_CLASS (JavaLong, "java/lang/Long");
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
METHOD (get, "get", "(Ljava/lang/Object;)Ljava/lang/Object;") \
METHOD (keySet, "keySet", "()Ljava/util/Set;") \
METHOD (put, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;")
DECLARE_JNI_CLASS (JavaMap, "java/util/Map");
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
METHOD (getName, "getName", "()Ljava/lang/String;") \
METHOD (getModifiers, "getModifiers", "()I") \
METHOD (getParameterTypes, "getParameterTypes", "()[Ljava/lang/Class;") \
METHOD (getReturnType, "getReturnType", "()Ljava/lang/Class;") \
METHOD (invoke, "invoke", "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;") \
METHOD (hashCode, "hashCode", "()I") \
METHOD (equals, "equals", "(Ljava/lang/Object;)Z") \
DECLARE_JNI_CLASS (JavaMethod, "java/lang/reflect/Method");
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
METHOD (constructor, "<init>", "()V") \
METHOD (getClass, "getClass", "()Ljava/lang/Class;") \
METHOD (toString, "toString", "()Ljava/lang/String;")
DECLARE_JNI_CLASS (JavaObject, "java/lang/Object");
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
METHOD (contains, "contains", "(Ljava/lang/Object;)Z") \
METHOD (iterator, "iterator", "()Ljava/util/Iterator;") \
METHOD (size, "size", "()I")
DECLARE_JNI_CLASS (JavaSet, "java/util/Set");
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
METHOD (concat, "concat", "(Ljava/lang/String;)Ljava/lang/String;") \
METHOD (getBytes, "getBytes", "()[B")
DECLARE_JNI_CLASS (JavaString, "java/lang/String");
#undef JNI_CLASS_MEMBERS
//==============================================================================
namespace
{
inline String juceString (JNIEnv* env, jstring s)
{
if (s == 0)
return {};
const char* const utf8 = env->GetStringUTFChars (s, nullptr);
CharPointer_UTF8 utf8CP (utf8);
const String result (utf8CP);
env->ReleaseStringUTFChars (s, utf8);
return result;
}
inline String juceString (jstring s)
{
return juceString (getEnv(), s);
}
inline LocalRef<jstring> javaString (const String& s)
{
return LocalRef<jstring> (getEnv()->NewStringUTF (s.toUTF8()));
}
inline LocalRef<jstring> javaStringFromChar (const juce_wchar c)
{
char utf8[8] = { 0 };
CharPointer_UTF8 (utf8).write (c);
return LocalRef<jstring> (getEnv()->NewStringUTF (utf8));
}
inline LocalRef<jobjectArray> juceStringArrayToJava (const StringArray& juceArray)
{
auto* env = getEnv();
LocalRef<jobjectArray> result (env->NewObjectArray ((jsize) juceArray.size(),
JavaString,
javaString ("").get()));
for (int i = 0; i < juceArray.size(); ++i)
env->SetObjectArrayElement (result, i, javaString (juceArray [i]).get());
return result;
}
inline StringArray javaStringArrayToJuce (const LocalRef<jobjectArray>& javaArray)
{
if (javaArray.get() == nullptr)
return {};
auto* env = getEnv();
StringArray result;
for (int i = 0; i < env->GetArrayLength (javaArray.get()); ++i)
{
LocalRef<jstring> javaString ((jstring) env->GetObjectArrayElement (javaArray.get(), i));
result.add (juceString (javaString.get()));
}
return result;
}
}
//==============================================================================
class AndroidInterfaceImplementer;
// This function takes ownership of the implementer. When the returned GlobalRef
// goes out of scope (and no other Java routine has a reference on the return-value)
// then the implementer will be deleted as well.
LocalRef<jobject> CreateJavaInterface (AndroidInterfaceImplementer* implementer,
const StringArray& interfaceNames,
LocalRef<jobject> subclass);
//==============================================================================
jobject juce_invokeImplementer (JNIEnv*, jlong, jobject, jobject, jobjectArray);
void juce_dispatchDelete (JNIEnv*, jlong);
//==============================================================================
class AndroidInterfaceImplementer
{
protected:
virtual ~AndroidInterfaceImplementer();
virtual jobject invoke (jobject proxy, jobject method, jobjectArray args);
//==============================================================================
friend LocalRef<jobject> CreateJavaInterface (AndroidInterfaceImplementer*, const StringArray&, LocalRef<jobject>);
friend jobject juce_invokeImplementer (JNIEnv*, jlong, jobject, jobject, jobjectArray);
friend void juce_dispatchDelete (JNIEnv*, jlong);
private:
GlobalRef javaSubClass;
GlobalRef invocationHandler;
};
LocalRef<jobject> CreateJavaInterface (AndroidInterfaceImplementer* implementer,
const StringArray& interfaceNames);
LocalRef<jobject> CreateJavaInterface (AndroidInterfaceImplementer* implementer,
const String& interfaceName);
} // namespace juce

View File

@ -0,0 +1,31 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
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)
{
__android_log_print (ANDROID_LOG_INFO, "JUCE", "%s", text.toUTF8().getAddress());
}
} // namespace juce

View File

@ -0,0 +1,403 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
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) \
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) \
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 (HTTPStream, JUCE_ANDROID_ACTIVITY_CLASSPATH "$HTTPStream");
#undef JNI_CLASS_MEMBERS
//==============================================================================
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
METHOD (close, "close", "()V") \
METHOD (read, "read", "([BII)I") \
DECLARE_JNI_CLASS (AndroidInputStream, "java/io/InputStream");
#undef JNI_CLASS_MEMBERS
//==============================================================================
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 shouldBePost)
: url (urlToCopy),
isContentURL (urlToCopy.getScheme() == "content"),
isPost (shouldBePost),
httpRequest (isPost ? "POST" : "GET")
{}
~Pimpl()
{
cancel();
}
void cancel()
{
if (isContentURL)
{
stream.callVoidMethod (AndroidInputStream.close);
return;
}
const ScopedLock lock (createStreamLock);
if (stream != 0)
{
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 (! isPost);
if (! address.contains ("://"))
address = "http://" + address;
MemoryBlock postData;
if (isPost)
WebInputStream::createHeadersAndPostData (url, headers, postData);
jbyteArray postDataArray = 0;
if (postData.getSize() > 0)
{
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 != 0);
{
const ScopedLock lock (createStreamLock);
if (! hasBeenCancelled)
stream = GlobalRef (LocalRef<jobject> (env->CallStaticObjectMethod (JuceAppActivity,
JuceAppActivity.createHTTPStream,
javaString (address).get(),
(jboolean) isPost,
postDataArray,
javaString (headers).get(),
(jint) timeOutMs,
statusCodeArray,
responseHeaderBuffer.get(),
(jint) numRedirectsToFollow,
javaString (httpRequest).get())));
}
if (stream != 0 && ! stream.callBooleanMethod (HTTPStream.connect))
stream.clear();
jint* const statusCodeElements = env->GetIntArrayElements (statusCodeArray, 0);
statusCode = statusCodeElements[0];
env->ReleaseIntArrayElements (statusCodeArray, statusCodeElements, 0);
env->DeleteLocalRef (statusCodeArray);
if (postDataArray != 0)
env->DeleteLocalRef (postDataArray);
if (stream != 0)
{
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;
bool isContentURL, isPost, 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)
};
URL::DownloadTask* URL::downloadToFile (const File& targetLocation, String extraHeaders, DownloadTask::Listener* listener, bool shouldUsePost)
{
return URL::DownloadTask::createFallbackDownloader (*this, targetLocation, extraHeaders, listener, shouldUsePost);
}
//==============================================================================
static void addAddress (const sockaddr_in* addr_in, Array<IPAddress>& result)
{
in_addr_t addr = addr_in->sin_addr.s_addr;
if (addr != INADDR_NONE)
result.addIfNotAlreadyThere (IPAddress (ntohl (addr)));
}
static void findIPAddresses (int sock, Array<IPAddress>& result)
{
ifconf cfg;
HeapBlock<char> buffer;
int bufferSize = 1024;
do
{
bufferSize *= 2;
buffer.calloc (bufferSize);
cfg.ifc_len = bufferSize;
cfg.ifc_buf = buffer;
if (ioctl (sock, SIOCGIFCONF, &cfg) < 0 && errno != EINVAL)
return;
} while (bufferSize < cfg.ifc_len + 2 * (int) (IFNAMSIZ + sizeof (struct sockaddr_in6)));
#if JUCE_MAC || JUCE_IOS
while (cfg.ifc_len >= (int) (IFNAMSIZ + sizeof (struct sockaddr_in)))
{
if (cfg.ifc_req->ifr_addr.sa_family == AF_INET) // Skip non-internet addresses
addAddress ((const sockaddr_in*) &cfg.ifc_req->ifr_addr, result);
cfg.ifc_len -= IFNAMSIZ + cfg.ifc_req->ifr_addr.sa_len;
cfg.ifc_buf += IFNAMSIZ + cfg.ifc_req->ifr_addr.sa_len;
}
#else
for (size_t i = 0; i < (size_t) cfg.ifc_len / (size_t) sizeof (struct ifreq); ++i)
{
const ifreq& item = cfg.ifc_req[i];
if (item.ifr_addr.sa_family == AF_INET)
addAddress ((const sockaddr_in*) &item.ifr_addr, result);
}
#endif
}
void IPAddress::findAllAddresses (Array<IPAddress>& result, bool /*includeIPv6*/)
{
const int sock = socket (AF_INET, SOCK_DGRAM, 0); // a dummy socket to execute the IO control
if (sock >= 0)
{
findIPAddresses (sock, result);
::close (sock);
}
}
} // namespace juce

View File

@ -0,0 +1,88 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
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 void handleAndroidCallback (bool permissionWasGranted, RuntimePermissions::Callback* callbackPtr)
{
if (callbackPtr == nullptr)
{
// got a nullptr passed in from java! this should never happen...
jassertfalse;
return;
}
std::unique_ptr<RuntimePermissions::Callback> uptr (callbackPtr);
if (RuntimePermissions::Callback callbackObj = *uptr)
callbackObj (permissionWasGranted);
}
JUCE_JNI_CALLBACK (JUCE_ANDROID_ACTIVITY_CLASSNAME,
androidRuntimePermissionsCallback,
void, (JNIEnv* env, jobject, jboolean permissionsGranted, jlong callbackPtr))
{
setEnv (env);
handleAndroidCallback (permissionsGranted != 0,
reinterpret_cast<RuntimePermissions::Callback*> (callbackPtr));
}
void RuntimePermissions::request (PermissionID permission, Callback callback)
{
if (! android.activity.callBooleanMethod (JuceAppActivity.isPermissionDeclaredInManifest, (jint) permission))
{
// 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;
}
if (JUCE_ANDROID_API_VERSION < 23)
{
// There is no runtime permission system on API level below 23. As long as the
// permission is in the manifest (seems to be the case), we can simply ask Android
// if the app has the permission, and then directly call through to the callback.
callback (isGranted (permission));
return;
}
// we need to move the callback object to the heap so Java can keep track of the pointer
// and asynchronously pass it back to us (to be called and then deleted)
Callback* callbackPtr = new Callback (std::move (callback));
android.activity.callVoidMethod (JuceAppActivity.requestRuntimePermission, permission, (jlong) callbackPtr);
}
bool RuntimePermissions::isRequired (PermissionID /*permission*/)
{
return JUCE_ANDROID_API_VERSION >= 23;
}
bool RuntimePermissions::isGranted (PermissionID permission)
{
return android.activity.callBooleanMethod (JuceAppActivity.isPermissionGranted, permission);
}
} // namespace juce

View File

@ -0,0 +1,539 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
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) \
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
JNIClassBase::JNIClassBase (const char* cp) : classPath (cp), classRef (0)
{
getClasses().add (this);
}
JNIClassBase::~JNIClassBase()
{
getClasses().removeFirstMatchingValue (this);
}
Array<JNIClassBase*>& JNIClassBase::getClasses()
{
static Array<JNIClassBase*> classes;
return classes;
}
void JNIClassBase::initialise (JNIEnv* env)
{
classRef = (jclass) env->NewGlobalRef (LocalRef<jobject> (env->FindClass (classPath)));
jassert (classRef != 0);
initialiseFields (env);
}
void JNIClassBase::release (JNIEnv* env)
{
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 != 0);
return m;
}
jmethodID JNIClassBase::resolveStaticMethod (JNIEnv* env, const char* methodName, const char* params)
{
jmethodID m = env->GetStaticMethodID (classRef, methodName, params);
jassert (m != 0);
return m;
}
jfieldID JNIClassBase::resolveField (JNIEnv* env, const char* fieldName, const char* signature)
{
jfieldID f = env->GetFieldID (classRef, fieldName, signature);
jassert (f != 0);
return f;
}
jfieldID JNIClassBase::resolveStaticField (JNIEnv* env, const char* fieldName, const char* signature)
{
jfieldID f = env->GetStaticFieldID (classRef, fieldName, signature);
jassert (f != 0);
return f;
}
//==============================================================================
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->CallObjectMethod (android.activity,
JuceAppActivity.createInvocationHandler,
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()
{
if (invocationHandler != nullptr)
getEnv()->CallVoidMethod (android.activity,
JuceAppActivity.invocationHandlerContextDeleted,
invocationHandler.get());
}
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* env, jlong thisPtr, jobject proxy, jobject method, jobjectArray args)
{
setEnv (env);
return reinterpret_cast<AndroidInterfaceImplementer*> (thisPtr)->invoke (proxy, method, args);
}
void juce_dispatchDelete (JNIEnv* env, jlong thisPtr)
{
setEnv (env);
delete reinterpret_cast<AndroidInterfaceImplementer*> (thisPtr);
}
JUCE_JNI_CALLBACK (JUCE_JOIN_MACRO (JUCE_ANDROID_ACTIVITY_CLASSNAME, _00024NativeInvocationHandler), dispatchInvoke,
jobject, (JNIEnv* env, jobject /*object*/, jlong thisPtr, jobject proxy, jobject method, jobjectArray args))
{
return juce_invokeImplementer (env, thisPtr, proxy, method, args);
}
JUCE_JNI_CALLBACK (JUCE_JOIN_MACRO (JUCE_ANDROID_ACTIVITY_CLASSNAME, _00024NativeInvocationHandler), dispatchFinalize,
void, (JNIEnv* env, jobject /*object*/, jlong thisPtr))
{
juce_dispatchDelete (env, thisPtr);
}
//==============================================================================
JavaVM* androidJNIJavaVM = nullptr;
class JniEnvThreadHolder
{
public:
static JniEnvThreadHolder& getInstance() noexcept
{
// You cann only use JNI functions AFTER JNI_OnLoad was called
jassert (androidJNIJavaVM != nullptr);
try
{
if (instance == nullptr)
instance = new JniEnvThreadHolder;
}
catch (...)
{
jassertfalse;
std::terminate();
}
return *instance;
}
static JNIEnv* getEnv() { return reinterpret_cast<JNIEnv*> (pthread_getspecific (getInstance().threadKey)); }
static void setEnv (JNIEnv* env)
{
// env must not be a nullptr
jassert (env != nullptr);
#if JUCE_DEBUG
JNIEnv* oldenv = reinterpret_cast<JNIEnv*> (pthread_getspecific (getInstance().threadKey));
// This thread is already attached to the JavaVM and you trying to attach
// it to a different instance of the VM.
jassert (oldenv == nullptr || oldenv == env);
#endif
pthread_setspecific (getInstance().threadKey, env);
}
private:
pthread_key_t threadKey;
static void threadDetach (void* p)
{
if (JNIEnv* env = reinterpret_cast<JNIEnv*> (p))
{
ignoreUnused (env);
androidJNIJavaVM->DetachCurrentThread();
}
}
JniEnvThreadHolder()
{
pthread_key_create (&threadKey, threadDetach);
}
static JniEnvThreadHolder* instance;
};
JniEnvThreadHolder* JniEnvThreadHolder::instance = nullptr;
//==============================================================================
JNIEnv* attachAndroidJNI() noexcept
{
auto* env = JniEnvThreadHolder::getEnv();
if (env == nullptr)
{
androidJNIJavaVM->AttachCurrentThread (&env, nullptr);
setEnv (env);
}
return env;
}
JNIEnv* getEnv() noexcept
{
auto* env = JniEnvThreadHolder::getEnv();
// You are trying to use a JUCE function on a thread that was not created by JUCE.
// You need to first call setEnv on this thread before using JUCE
jassert (env != nullptr);
return env;
}
void setEnv (JNIEnv* env) noexcept { JniEnvThreadHolder::setEnv (env); }
extern "C" jint JNI_OnLoad (JavaVM* vm, void*)
{
// Huh? JNI_OnLoad was called two times!
jassert (androidJNIJavaVM == nullptr);
androidJNIJavaVM = vm;
return JNI_VERSION_1_2;
}
//==============================================================================
AndroidSystem::AndroidSystem() : screenWidth (0), screenHeight (0), dpi (160)
{
}
void AndroidSystem::initialise (JNIEnv* env, jobject act, jstring file, jstring dataDir)
{
setEnv (env);
screenWidth = screenHeight = 0;
dpi = 160;
JNIClassBase::initialiseAllClasses (env);
activity = GlobalRef (act);
appFile = juceString (env, file);
appDataDir = juceString (env, dataDir);
}
void AndroidSystem::shutdown (JNIEnv* env)
{
activity.clear();
JNIClassBase::releaseAllClasses (env);
}
AndroidSystem android;
//==============================================================================
namespace AndroidStatsHelpers
{
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
STATICMETHOD (getProperty, "getProperty", "(Ljava/lang/String;)Ljava/lang/String;")
DECLARE_JNI_CLASS (SystemClass, "java/lang/System");
#undef JNI_CLASS_MEMBERS
static inline String getSystemProperty (const String& name)
{
return juceString (LocalRef<jstring> ((jstring) getEnv()->CallStaticObjectMethod (SystemClass,
SystemClass.getProperty,
javaString (name).get())));
}
static inline String getLocaleValue (bool isRegion)
{
return juceString (LocalRef<jstring> ((jstring) getEnv()->CallStaticObjectMethod (JuceAppActivity,
JuceAppActivity.getLocaleValue,
isRegion)));
}
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD)
DECLARE_JNI_CLASS (BuildClass, "android/os/Build");
#undef JNI_CLASS_MEMBERS
static inline String getAndroidOsBuildValue (const char* fieldName)
{
return juceString (LocalRef<jstring> ((jstring) getEnv()->GetStaticObjectField (
BuildClass, getEnv()->GetStaticFieldID (BuildClass, 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::getCpuSpeedInMegaherz()
{
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 getHighResolutionTicks() * 0.001;
}
bool Time::setSystemTimeToThisTime() const
{
jassertfalse;
return false;
}
} // namespace juce

View File

@ -0,0 +1,77 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
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!
*/
//==============================================================================
// 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,559 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
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
{
class WebInputStream::Pimpl
{
public:
Pimpl (WebInputStream& ownerStream, const URL& urlToCopy, bool shouldUsePost)
: owner (ownerStream), url (urlToCopy), isPost (shouldUsePost),
httpRequest (isPost ? "POST" : "GET")
{
multi = curl_multi_init();
if (multi != nullptr)
{
curl = curl_easy_init();
if (curl != nullptr)
if (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.getSize() == 0; }
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);
if (curl != nullptr)
{
curl_multi_remove_handle (multi, curl);
if (headerList != nullptr)
{
curl_slist_free_all (headerList);
headerList = nullptr;
}
curl_easy_cleanup (curl);
curl = nullptr;
}
if (multi != nullptr)
{
curl_multi_cleanup (multi);
multi = nullptr;
}
}
void cancel()
{
cleanup();
}
//==============================================================================
bool setOptions()
{
auto address = url.toString (! isPost);
curl_version_info_data* data = curl_version_info (CURLVERSION_NOW);
jassert (data != nullptr);
if (! requestHeaders.endsWithChar ('\n'))
requestHeaders << "\r\n";
if (isPost)
WebInputStream::createHeadersAndPostData (url, requestHeaders, headersAndPostData);
if (! requestHeaders.endsWithChar ('\n'))
requestHeaders << "\r\n";
auto userAgent = String ("curl/") + data->version;
if (curl_easy_setopt (curl, CURLOPT_URL, address.toRawUTF8()) == CURLE_OK
&& curl_easy_setopt (curl, CURLOPT_WRITEDATA, this) == CURLE_OK
&& curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, StaticCurlWrite) == CURLE_OK
&& curl_easy_setopt (curl, CURLOPT_NOSIGNAL, 1) == CURLE_OK
&& curl_easy_setopt (curl, CURLOPT_MAXREDIRS, static_cast<long> (maxRedirects)) == CURLE_OK
&& curl_easy_setopt (curl, CURLOPT_USERAGENT, userAgent.toRawUTF8()) == CURLE_OK
&& curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, (maxRedirects > 0 ? 1 : 0)) == CURLE_OK)
{
if (isPost)
{
if (curl_easy_setopt (curl, CURLOPT_READDATA, this) != CURLE_OK
|| curl_easy_setopt (curl, CURLOPT_READFUNCTION, StaticCurlRead) != CURLE_OK)
return false;
if (curl_easy_setopt (curl, CURLOPT_POST, 1) != CURLE_OK
|| curl_easy_setopt (curl, CURLOPT_POSTFIELDSIZE_LARGE, static_cast<curl_off_t> (headersAndPostData.getSize())) != CURLE_OK)
return false;
}
// handle special http request commands
bool hasSpecialRequestCmd = isPost ? (httpRequest != "POST") : (httpRequest != "GET");
if (hasSpecialRequestCmd)
if (curl_easy_setopt (curl, CURLOPT_CUSTOMREQUEST, httpRequest.toRawUTF8()) != CURLE_OK)
return false;
if (curl_easy_setopt (curl, CURLOPT_HEADERDATA, this) != CURLE_OK
|| curl_easy_setopt (curl, CURLOPT_HEADERFUNCTION, StaticCurlHeader) != CURLE_OK)
return false;
if (timeOutMs > 0)
{
auto timeOutSecs = ((long) timeOutMs + 999) / 1000;
if (curl_easy_setopt (curl, CURLOPT_CONNECTTIMEOUT, timeOutSecs) != CURLE_OK
|| curl_easy_setopt (curl, CURLOPT_LOW_SPEED_LIMIT, 100) != CURLE_OK
|| 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 = curl_slist_append (headerList, headerLines [0].toRawUTF8());
for (int i = 1; (i < headerLines.size() && headerList != nullptr); ++i)
headerList = curl_slist_append (headerList, headerLines [i].toRawUTF8());
if (headerList == nullptr)
{
cleanup();
return false;
}
if (curl_easy_setopt (curl, CURLOPT_HTTPHEADER, headerList) != CURLE_OK)
{
cleanup();
return false;
}
}
}
listener = webInputListener;
if (isPost)
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.getSize() == 0)
{
{
const ScopedLock lock (cleanupLock);
if (curl == nullptr)
return false;
}
singleStep();
// call callbacks if this is a post request
if (isPost && 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 (curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &responseCode) == CURLE_OK)
statusCode = static_cast<int> (responseCode);
// get content length size
double curlLength;
if (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 = 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) 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) 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) 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;
//==============================================================================
// 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 isPost;
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)
};
URL::DownloadTask* URL::downloadToFile (const File& targetLocation, String extraHeaders, DownloadTask::Listener* listener, bool shouldUsePost)
{
return URL::DownloadTask::createFallbackDownloader (*this, targetLocation, extraHeaders, listener, shouldUsePost);
}
} // namespace juce

View File

@ -0,0 +1,142 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
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
{
HeapBlock<char> buffer (8194);
const int numBytes = (int) readlink (getFullPathName().toRawUTF8(), buffer, 8192);
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,236 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
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
{
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 == (short) 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]));
// deliberate fall-through...
case currentExecutableFile:
case currentApplicationFile:
#if ! JUCE_STANDALONE_APPLICATION
return juce_getExecutableFile();
#endif
// deliberate fall-through if this is not a shared-library
case hostApplicationPath:
{
const File f ("/proc/self/exe");
return f.isSymbolicLink() ? f.getLinkedTarget() : juce_getExecutableFile();
}
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)
{
String cmdString (fileName.replace (" ", "\\ ",false));
cmdString << " " << parameters;
if (/*URL::isProbablyAWebsiteURL (fileName)
||*/ cmdString.startsWithIgnoreCase ("file:")
/*|| URL::isProbablyAnEmailAddress (fileName)*/
|| File::createFileWithoutCheckingPath (fileName).isDirectory()
|| ! isFileExecutable (fileName))
{
// create a command that tries to launch a bunch of likely browsers
static const char* const browserNames[] = { "xdg-open", "/etc/alternatives/x-www-browser", "firefox", "mozilla",
"google-chrome", "chromium-browser", "opera", "konqueror" };
StringArray cmdLines;
for (int i = 0; i < numElementsInArray (browserNames); ++i)
cmdLines.add (String (browserNames[i]) + " " + cmdString.trim().quoted());
cmdString = cmdLines.joinIntoString (" || ");
}
const char* const argv[4] = { "/bin/sh", "-c", cmdString.toUTF8(), 0 };
const int 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,585 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
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)
{
const int s = socket (AF_INET, SOCK_DGRAM, 0);
if (s != -1)
{
struct ifaddrs* addrs = nullptr;
if (getifaddrs (&addrs) != -1)
{
for (struct ifaddrs* 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);
}
}
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, const bool shouldUsePost)
: owner (pimplOwner), url (urlToCopy),
isPost (shouldUsePost), httpRequestCmd (shouldUsePost ? "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; }
StringPairArray getRequestHeaders() const { return WebInputStream::parseHttpHeaders (headers); }
StringPairArray getResponseHeaders() const
{
StringPairArray responseHeaders;
if (! isError())
{
for (int i = 0; i < headerLines.size(); ++i)
{
const String& headersEntry = headerLines[i];
const String key (headersEntry.upToFirstOccurrenceOf (": ", false, false));
const String value (headersEntry.fromFirstOccurrenceOf (": ", false, false));
const String previousValue (responseHeaders [key]);
responseHeaders.set (key, previousValue.isEmpty() ? value : (previousValue + "," + value));
}
}
return responseHeaders;
}
int getStatusCode() const { return statusCode; }
bool connect (WebInputStream::Listener* listener)
{
{
const ScopedLock lock (createSocketLock);
if (hasBeenCancelled)
return false;
}
address = url.toString (! isPost);
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);
}
const int64 chunkSize = chunkLengthBuffer.toString().trimStart().getHexValue64();
if (chunkSize == 0)
{
finished = true;
return 0;
}
chunkEnd += chunkSize;
}
if (bytesToRead > chunkEnd - position)
bytesToRead = static_cast<int> (chunkEnd - position);
}
fd_set readbits;
FD_ZERO (&readbits);
FD_SET (socketHandle, &readbits);
struct timeval tv;
tv.tv_sec = jmax (1, timeOutMs / 1000);
tv.tv_usec = 0;
if (select (socketHandle + 1, &readbits, 0, 0, &tv) <= 0)
return 0; // (timeout)
const int 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;
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;
URL url;
int socketHandle = -1, levelsOfRedirection = 0;
StringArray headerLines;
String address, headers;
MemoryBlock postData;
int64 contentLength = -1, position = 0;
bool finished = false;
const bool isPost;
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, const int numRedirects)
{
closeSocket (false);
if (isPost)
WebInputStream::createHeadersAndPostData (url, headers, postData);
uint32 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;
const String proxyURL (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 == 0)
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, 0, 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, isPost, httpRequestCmd));
if (! sendHeader (socketHandle, requestHeader, timeOutTime, owner, listener))
{
closeSocket();
return 0;
}
}
String responseHeader (readResponse (timeOutTime));
position = 0;
if (responseHeader.isNotEmpty())
{
headerLines = StringArray::fromLines (responseHeader);
const int status = responseHeader.fromFirstOccurrenceOf (" ", false, false)
.substring (0, 3).getIntValue();
String 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);
}
String contentLengthString (findHeaderItem (headerLines, "Content-Length:"));
if (contentLengthString.isNotEmpty())
contentLength = contentLengthString.getLargeIntValue();
isChunked = (findHeaderItem (headerLines, "Transfer-Encoding:") == "chunked");
return status;
}
closeSocket();
return 0;
}
//==============================================================================
String readResponse (const 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;
}
const String 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, const int hostPort,
const String& proxyName, const int proxyPort,
const String& hostPath, const String& originalURL,
const String& userHeaders, const MemoryBlock& postData,
const bool isPost, 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");
if (isPost)
writeValueIfNotPresent (header, userHeaders, "Content-Length:", String ((int) postData.getSize()));
if (userHeaders.isNotEmpty())
header << "\r\n" << userHeaders;
header << "\r\n\r\n";
if (isPost)
header << postData;
return header.getMemoryBlock();
}
static bool sendHeader (int socketHandle, const MemoryBlock& requestHeader, const uint32 timeOutTime,
WebInputStream& pimplOwner, WebInputStream::Listener* listener)
{
size_t totalHeaderSent = 0;
while (totalHeaderSent < requestHeader.getSize())
{
if (Time::getMillisecondCounter() > timeOutTime)
return false;
const int 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;
const int nextSlash = url.indexOfChar (7, '/');
int 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)
};
URL::DownloadTask* URL::downloadToFile (const File& targetLocation, String extraHeaders, DownloadTask::Listener* listener, bool shouldUsePost)
{
return URL::DownloadTask::createFallbackDownloader (*this, targetLocation, extraHeaders, listener, shouldUsePost);
}
#endif
} // namespace juce

View File

@ -0,0 +1,211 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
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 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
}
//==============================================================================
static inline String getCpuInfo (const char* key)
{
return readPosixConfigFileValue ("/proc/cpuinfo", key);
}
String SystemStats::getDeviceDescription()
{
return getCpuInfo ("Hardware");
}
String SystemStats::getDeviceManufacturer()
{
return {};
}
String SystemStats::getCpuVendor()
{
auto v = getCpuInfo ("vendor_id");
if (v.isEmpty())
v = getCpuInfo ("model name");
return v;
}
String SystemStats::getCpuModel()
{
return getCpuInfo ("model name");
}
int SystemStats::getCpuSpeedInMegaherz()
{
return roundToInt (getCpuInfo ("cpu MHz").getFloatValue());
}
int SystemStats::getMemorySizeInMegabytes()
{
struct sysinfo sysi;
if (sysinfo (&sysi) == 0)
return (int) (sysi.totalram * sysi.mem_unit / (1024 * 1024));
return 0;
}
int SystemStats::getPageSize()
{
return (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 {};
}
static String getLocaleValue (nl_item key)
{
const char* oldLocale = ::setlocale (LC_ALL, "");
String result (String::fromUTF8 (nl_langinfo (key)));
::setlocale (LC_ALL, oldLocale);
return result;
}
String SystemStats::getUserLanguage() { return getLocaleValue (_NL_IDENTIFICATION_LANGUAGE); }
String SystemStats::getUserRegion() { return getLocaleValue (_NL_IDENTIFICATION_TERRITORY); }
String SystemStats::getDisplayLanguage() { return getUserLanguage() + "-" + getUserRegion(); }
//==============================================================================
void CPUInformation::initialise() noexcept
{
auto flags = getCpuInfo ("flags");
hasMMX = flags.contains ("mmx");
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");
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;
}
//==============================================================================
uint32 juce_millisecondsSinceStartup() noexcept
{
timespec t;
clock_gettime (CLOCK_MONOTONIC, &t);
return (uint32) (t.tv_sec * 1000 + t.tv_nsec / 1000000);
}
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 getHighResolutionTicks() * 0.001;
}
bool Time::setSystemTimeToThisTime() const
{
timeval t;
t.tv_sec = millisSinceEpoch / 1000;
t.tv_usec = (millisSinceEpoch - t.tv_sec * 1000) * 1000;
return settimeofday (&t, 0) == 0;
}
JUCE_API bool JUCE_CALLTYPE juce_isRunningUnderDebugger() noexcept
{
#if JUCE_BSD
return 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) 2017 - ROLI Ltd.
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)
{
const int policy = (prior <= NormalPriority) ? SCHED_OTHER : SCHED_RR;
const int minp = sched_get_priority_min (policy);
const int 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()
{
int result1 = setreuid (geteuid(), getuid());
int result2 = setregid (getegid(), getgid());
return result1 == 0 && result2 == 0;
}
JUCE_API void JUCE_CALLTYPE Process::raisePrivilege() { if (geteuid() != 0 && getuid() == 0) swapUserAndEffectiveUser(); }
JUCE_API void JUCE_CALLTYPE Process::lowerPrivilege() { if (geteuid() == 0 && getuid() != 0) swapUserAndEffectiveUser(); }
} // namespace juce

View File

@ -0,0 +1,35 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
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 hack is a workaround for a bug (?) in Apple's 10.11 SDK headers
// which cause some configurations of Clang to throw out a spurious error..
#if JUCE_PROJUCER_LIVE_BUILD && (defined (__APPLE_CPP__) || defined(__APPLE_CC__))
#include <CoreFoundation/CFAvailability.h>
#undef CF_OPTIONS
#define CF_OPTIONS(_type, _name) _type _name; enum
// This is a workaround for the Xcode 9 version of NSUUID.h causing some errors
// in the live-build engine.
#define _Nullable
#define _Nonnull
#endif

View File

@ -0,0 +1,513 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
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)]
#if defined (MAC_OS_X_VERSION_10_6) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
&& [fm copyItemAtPath: juceStringToNS (fullPath)
toPath: juceStringToNS (dest.getFullPathName())
error: nil];
#else
&& [fm copyPath: juceStringToNS (fullPath)
toPath: juceStringToNS (dest.getFullPathName())
handler: nil];
#endif
}
}
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 != 0)
if (type.equalsIgnoreCase (*types++))
return true;
}
return false;
}
static bool isHiddenFile (const String& path)
{
#if defined (MAC_OS_X_VERSION_10_6) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6
JUCE_AUTORELEASEPOOL
{
NSNumber* hidden = nil;
NSError* err = nil;
return [createNSURLFromFile (path) getResourceValue: &hidden forKey: NSURLIsHiddenKey error: &err]
&& [hidden boolValue];
}
#elif JUCE_IOS
return File (path).getFileName().startsWithChar ('.');
#else
FSRef ref;
LSItemInfoRecord info;
return FSPathMakeRefWithOptions ((const UInt8*) path.toRawUTF8(), kFSPathMakeRefDoNotFollowLeafSymlink, &ref, 0) == noErr
&& LSCopyItemInfoForRef (&ref, kLSRequestBasicFlagsOnly, &info) == noErr
&& (info.flags & kLSItemInfoIsInvisible) != 0;
#endif
}
#if JUCE_IOS
static String getIOSSystemLocation (NSSearchPathDirectory type)
{
return nsStringToJuce ([NSSearchPathForDirectoriesInDomains (type, NSUserDomainMask, YES)
objectAtIndex: 0]);
}
#else
static bool launchExecutable (const String& pathAndArguments)
{
const char* const argv[4] = { "/bin/sh", "-c", pathAndArguments.toUTF8(), 0 };
const int cpid = fork();
if (cpid == 0)
{
// Child process
if (execve (argv[0], (char**) argv, 0) < 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 (String (juce_argv[0]));
// deliberate fall-through...
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 (defined (__IPHONE_11_0) && __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_11_0) \
|| (defined (MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_8)
NSError* error = nil;
return [[NSFileManager defaultManager] trashItemAtURL: createNSURLFromFile (*this)
resultingItemURL: nil
error: &error];
#elif 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 ([NSURL URLWithString: fileNameAsNS]);
if (filenameAsURL == nil)
filenameAsURL = [NSURL fileURLWithPath: 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())
// NB: the length check here is because of strange failures involving long filenames,
// probably due to filesystem name length limitations..
return (fileName.length() < 1024 && [workspace openFile: juceStringToNS (fileName)])
|| [workspace openURL: filenameAsURL];
const File file (fileName);
if (file.isBundle())
{
StringArray params;
params.addTokens (parameters, true);
NSMutableArray* paramArray = [[[NSMutableArray alloc] init] autorelease];
for (int i = 0; i < params.size(); ++i)
[paramArray addObject: juceStringToNS (params[i])];
NSMutableDictionary* dict = [[[NSMutableDictionary alloc] init] autorelease];
[dict setObject: paramArray
forKey: nsStringLiteral ("NSWorkspaceLaunchConfigurationArguments")];
return [workspace launchApplicationAtURL: filenameAsURL
options: NSWorkspaceLaunchDefault | NSWorkspaceLaunchNewInstance
configuration: dict
error: nil];
}
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
} // namespace juce

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,105 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
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 == 0)
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 = 0;
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, 0, 0, 0,
bytesNeeded, &bytesRead,
&outputBufferSize, tempOut) == noErr)
{
result = String (CharPointer_UTF16 ((CharPointer_UTF16::CharType*) tempOut.get()));
}
DisposeUnicodeToTextInfo (&conversionInfo);
}
return result;
#endif
}
} // namespace juce

View File

@ -0,0 +1,325 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
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);
}
//==============================================================================
namespace SystemStatsHelpers
{
#if JUCE_INTEL && ! JUCE_NO_INLINE_ASM
static void doCPUID (uint32& a, uint32& b, uint32& c, uint32& d, uint32 type)
{
uint32 la = a, lb = b, lc = c, ld = d;
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
);
a = la; b = lb; c = lc; d = ld;
}
#endif
}
//==============================================================================
void CPUInformation::initialise() noexcept
{
#if JUCE_INTEL && ! JUCE_NO_INLINE_ASM
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;
hasSSE41 = (c & (1u << 19)) != 0;
hasSSE42 = (c & (1u << 20)) != 0;
hasAVX = (c & (1u << 28)) != 0;
SystemStatsHelpers::doCPUID (a, b, c, d, 7);
hasAVX2 = (b & (1u << 5)) != 0;
#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
{
NSDictionary* dict = [NSDictionary dictionaryWithContentsOfFile:
nsStringLiteral ("/System/Library/CoreServices/SystemVersion.plist")];
return nsStringToJuce ([dict objectForKey: nsStringLiteral ("ProductVersion")]);
}
}
#endif
SystemStats::OperatingSystemType SystemStats::getOperatingSystemType()
{
#if JUCE_IOS
return iOS;
#else
StringArray parts;
parts.addTokens (getOSXVersion(), ".", StringRef());
jassert (parts[0].getIntValue() == 10);
const int major = parts[1].getIntValue();
jassert (major > 2);
return (OperatingSystemType) (major + MacOSX_10_4 - 4);
#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
return nsStringToJuce ([[UIDevice currentDevice] model]);
#else
size_t size;
if (sysctlbyname ("hw.model", nullptr, &size, nullptr, 0) >= 0)
{
HeapBlock<char> model (size);
if (sysctlbyname ("hw.model", model, &size, nullptr, 0) >= 0)
return model.get();
}
return {};
#endif
}
String SystemStats::getDeviceManufacturer()
{
return "Apple";
}
bool SystemStats::isOperatingSystem64Bit()
{
#if JUCE_IOS
return false;
#elif JUCE_64BIT
return true;
#else
return getOperatingSystemType() >= MacOSX_10_6;
#endif
}
int SystemStats::getMemorySizeInMegabytes()
{
uint64 mem = 0;
size_t memSize = sizeof (mem);
int mib[] = { CTL_HW, HW_MEMSIZE };
sysctl (mib, 2, &mem, &memSize, 0, 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::getCpuSpeedInMegaherz()
{
uint64 speedHz = 0;
size_t speedSize = sizeof (speedHz);
int mib[] = { CTL_HW, HW_CPU_FREQ };
sysctl (mib, 2, &speedHz, &speedSize, 0, 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)
{
CFLocaleRef cfLocale = CFLocaleCopyCurrent();
const String result (String::fromCFString ((CFStringRef) CFLocaleGetValue (cfLocale, key)));
CFRelease (cfLocale);
return result;
}
String SystemStats::getUserLanguage() { return getLocaleValue (kCFLocaleLanguageCode); }
String SystemStats::getUserRegion() { return getLocaleValue (kCFLocaleCountryCode); }
String SystemStats::getDisplayLanguage()
{
CFArrayRef cfPrefLangs = CFLocaleCopyPreferredLanguages();
const String result (String::fromCFString ((CFStringRef) CFArrayGetValueAtIndex (cfPrefLangs, 0)));
CFRelease (cfPrefLangs);
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,93 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
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()
{
jassertfalse;
}
JUCE_API void JUCE_CALLTYPE Process::lowerPrivilege()
{
jassertfalse;
}
JUCE_API void JUCE_CALLTYPE Process::setPriority (ProcessPriority)
{
// xxx
}
//==============================================================================
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, 0, 0);
return (info.kp_proc.p_flag & P_TRACED) != 0;
}
} // namespace juce

View File

@ -0,0 +1,437 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
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
{
//==============================================================================
static inline String nsStringToJuce (NSString* s)
{
return CharPointer_UTF8 ([s UTF8String]);
}
static inline NSString* juceStringToNS (const String& s)
{
return [NSString stringWithUTF8String: s.toUTF8()];
}
static inline NSString* nsStringLiteral (const char* const s) noexcept
{
return [NSString stringWithUTF8String: s];
}
static inline NSString* nsEmptyString() noexcept
{
return [NSString string];
}
static inline NSURL* createNSURLFromFile (const String& f)
{
return [NSURL fileURLWithPath: juceStringToNS (f)];
}
static inline NSURL* createNSURLFromFile (const File& f)
{
return createNSURLFromFile (f.getFullPathName());
}
static inline NSArray* createNSArrayFromStringArray (const StringArray& strings)
{
auto* array = [[NSMutableArray alloc] init];
for (auto string: strings)
[array addObject:juceStringToNS (string)];
return [array autorelease];
}
static NSArray* varArrayToNSArray (const var& varToParse);
static 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;
}
static 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;
}
static var nsArrayToVar (NSArray* array);
static var nsDictionaryToVar (NSDictionary* dictionary)
{
DynamicObject::Ptr dynamicObject = new DynamicObject();
for (NSString* key in dictionary)
{
const auto keyString = nsStringToJuce (key);
id value = dictionary[key];
if ([value isKindOfClass: [NSString class]])
dynamicObject->setProperty (keyString, nsStringToJuce ((NSString*) value));
else if ([value isKindOfClass: [NSNumber class]])
dynamicObject->setProperty (keyString, nsStringToJuce ([(NSNumber*) value stringValue]));
else if ([value isKindOfClass: [NSDictionary class]])
dynamicObject->setProperty (keyString, nsDictionaryToVar ((NSDictionary*) value));
else if ([value isKindOfClass: [NSArray class]])
dynamicObject->setProperty (keyString, nsArrayToVar ((NSArray*) value));
else
jassertfalse; // Unsupported yet, add here!
}
return var (dynamicObject.get());
}
static var nsArrayToVar (NSArray* array)
{
Array<var> resultArray;
for (id value in array)
{
if ([value isKindOfClass: [NSString class]])
resultArray.add (var (nsStringToJuce ((NSString*) value)));
else if ([value isKindOfClass: [NSNumber class]])
resultArray.add (var (nsStringToJuce ([(NSNumber*) value stringValue])));
else if ([value isKindOfClass: [NSDictionary class]])
resultArray.add (nsDictionaryToVar ((NSDictionary*) value));
else if ([value isKindOfClass: [NSArray class]])
resultArray.add (nsArrayToVar ((NSArray*) value));
else
jassertfalse; // Unsupported yet, add here!
}
return var (resultArray);
}
#if JUCE_MAC
template <typename RectangleType>
static 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_MAC || JUCE_IOS
// This is necessary as on iOS builds, some arguments may be passed on registers
// depending on the argument type. The re-cast objc_msgSendSuper to a function
// take the same arguments as the target method.
template <typename ReturnValue, typename... Params>
static inline ReturnValue ObjCMsgSendSuper (struct objc_super* s, SEL sel, Params... params)
{
typedef ReturnValue (*SuperFn)(struct objc_super*, SEL, Params...);
SuperFn fn = reinterpret_cast<SuperFn> (objc_msgSendSuper);
return fn (s, sel, params...);
}
// These hacks are a workaround for newer Xcode builds which by default prevent calls to these objc functions..
typedef id (*MsgSendSuperFn) (struct objc_super*, SEL, ...);
static inline MsgSendSuperFn getMsgSendSuperFn() noexcept { return (MsgSendSuperFn) (void*) objc_msgSendSuper; }
#if ! JUCE_IOS
typedef double (*MsgSendFPRetFn) (id, SEL op, ...);
static inline MsgSendFPRetFn getMsgSendFPRetFn() noexcept { return (MsgSendFPRetFn) (void*) objc_msgSend_fpret; }
#endif
#endif
//==============================================================================
template <typename ObjectType>
struct NSObjectRetainer
{
inline NSObjectRetainer (ObjectType* o) : object (o) { [object retain]; }
inline ~NSObjectRetainer() { [object release]; }
ObjectType* object;
};
//==============================================================================
struct NSObjectDeleter
{
void operator()(NSObject* object) const
{
[object release];
}
};
//==============================================================================
template <typename SuperclassType>
struct ObjCClass
{
ObjCClass (const char* nameRoot)
: cls (objc_allocateClassPair ([SuperclassType class], getRandomisedName (nameRoot).toUTF8(), 0))
{
}
~ObjCClass()
{
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);
}
#if JUCE_MAC || JUCE_IOS
static id sendSuperclassMessage (id self, SEL selector)
{
objc_super s = { self, [SuperclassType class] };
return getMsgSendSuperFn() (&s, selector);
}
#endif
template <typename Type>
static Type getIvar (id self, const char* name)
{
void* v = nullptr;
object_getInstanceVariable (self, name, &v);
return static_cast<Type> (v);
}
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");
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
addMethod (@selector (initWithJuceObject:), initWithJuceObject, "@@:@");
#pragma clang diagnostic pop
addMethod (@selector (dealloc), dealloc, "v@:");
registerClass();
}
static id initWithJuceObject (id _self, SEL, JuceClass* obj)
{
NSObject* self = _self;
objc_super s = { self, [NSObject class] };
self = ObjCMsgSendSuper<NSObject*> (&s, @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);
}
objc_super s = { _self, [NSObject class] };
ObjCMsgSendSuper<void> (&s, @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)
{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-method-access"
return [ObjCLifetimeManagedClass<Class>::objCLifetimeManagedClass.createInstance() initWithJuceObject:obj];
#pragma clang diagnostic pop
}
// 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 ? ObjCLifetimeManagedClass<Class>:: template 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,229 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
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
{
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());
}
}
int read (char* destBuffer, int maxBytesToRead, int timeOutMilliseconds)
{
auto timeoutEnd = getTimeoutEnd (timeOutMilliseconds);
if (pipeIn == -1)
{
pipeIn = openPipe (createdPipe ? pipeInName : pipeOutName, O_RDWR | O_NONBLOCK, timeoutEnd);
if (pipeIn == -1)
return -1;
}
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 (pipeOut == -1)
{
pipeOut = openPipe (createdPipe ? pipeOutName : pipeInName, O_WRONLY, timeoutEnd);
if (pipeOut == -1)
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);
}
}
static void waitForInput (int handle, int timeoutMsecs) noexcept
{
struct timeval timeout;
timeout.tv_sec = timeoutMsecs / 1000;
timeout.tv_usec = (timeoutMsecs % 1000) * 1000;
fd_set rset;
FD_ZERO (&rset);
FD_SET (handle, &rset);
select (handle + 1, &rset, nullptr, 0, &timeout);
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl)
};
void NamedPipe::close()
{
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;
}
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;
}
} // namespace juce

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,198 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
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
inline GUID uuidFromString (const char* s) noexcept
{
uint32 ints[4] = {};
for (uint32 digitIndex = 0; digitIndex < 32;)
{
auto c = *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, 0, 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);
}
private:
ComClass* p = nullptr;
void release() { if (p != nullptr) p->Release(); }
ComClass** operator&() noexcept; // private to avoid it being used accidentally
};
//==============================================================================
#define JUCE_COMRESULT HRESULT __stdcall
//==============================================================================
template <class ComClass>
class ComBaseClassHelperBase : public ComClass
{
public:
ComBaseClassHelperBase (unsigned int initialRefCount) : refCount (initialRefCount) {}
virtual ~ComBaseClassHelperBase() {}
ULONG __stdcall AddRef() { return ++refCount; }
ULONG __stdcall 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<IUnknown> (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 ComClass>
class ComBaseClassHelper : public ComBaseClassHelperBase<ComClass>
{
public:
ComBaseClassHelper (unsigned int initialRefCount = 1) : ComBaseClassHelperBase<ComClass> (initialRefCount) {}
~ComBaseClassHelper() {}
JUCE_COMRESULT QueryInterface (REFIID refId, void** result)
{
if (refId == __uuidof (ComClass))
return this->template castToType<ComClass> (result);
return ComBaseClassHelperBase<ComClass>::QueryInterface (refId, result);
}
};
} // namespace juce

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,667 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
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 shouldBePost)
: statusCode (0), owner (pimplOwner), url (urlToCopy), isPost (shouldBePost),
httpRequestCmd (isPost ? "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;
}
String address = url.toString (! isPost);
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, 0))
{
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, 0))
{
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 != 0);
}
bool isError() const { return request == 0; }
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 (buffer != nullptr && bytesToRead >= 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, 0, 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;
private:
//==============================================================================
WebInputStream& owner;
const URL url;
HINTERNET connection = 0, request = 0;
String headers;
MemoryBlock postData;
int64 position = 0;
bool finished = false;
const bool isPost;
int timeOutMs = 0;
String httpRequestCmd;
int numRedirectsToFollow = 5;
StringPairArray responseHeaders;
CriticalSection createConnectionLock;
bool hasBeenCancelled = false;
void closeConnection()
{
HINTERNET requestCopy = request;
request = 0;
if (requestCopy != 0)
InternetCloseHandle (requestCopy);
if (connection != 0)
{
InternetCloseHandle (connection);
connection = 0;
}
}
void createConnection (const String& address, WebInputStream::Listener* listener)
{
static HINTERNET sessionHandle = InternetOpen (_T("juce"), INTERNET_OPEN_TYPE_PRECONFIG, 0, 0, 0);
closeConnection();
if (sessionHandle != 0)
{
// 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 = { 0 };
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 (isPost)
WebInputStream::createHeadersAndPostData (url, headers, postData);
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 ? 0
: InternetConnect (sessionHandle,
uc.lpszHostName, uc.nPort,
uc.lpszUserName, uc.lpszPassword,
isFtp ? (DWORD) INTERNET_SERVICE_FTP
: (DWORD) INTERNET_SERVICE_HTTP,
0, 0);
}
if (connection != 0)
{
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 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 ? 0
: HttpOpenRequest (connection, httpRequestCmd.toWideCharPointer(),
uc.lpszUrlPath, 0, 0, mimeTypes, flags, 0);
}
if (request != 0)
{
INTERNET_BUFFERS buffers = { 0 };
buffers.dwStructSize = sizeof (INTERNET_BUFFERS);
buffers.lpcszHeader = headers.toWideCharPointer();
buffers.dwHeadersLength = (DWORD) headers.length();
buffers.dwBufferTotal = (DWORD) postData.getSize();
if (HttpSendRequestEx (request, &buffers, 0, HSR_INITIATE, 0))
{
int bytesSent = 0;
for (;;)
{
const int bytesToDo = jmin (1024, (int) postData.getSize() - bytesSent);
DWORD bytesDone = 0;
if (bytesToDo > 0
&& ! InternetWriteFile (request,
static_cast<const char*> (postData.getData()) + bytesSent,
(DWORD) bytesToDo, &bytesDone))
{
break;
}
if (bytesToDo == 0 || (int) bytesDone < bytesToDo)
{
if (HttpEndRequest (request, 0, 0, 0))
return;
break;
}
bytesSent += bytesDone;
if (listener != nullptr
&& ! listener->postDataSendProgress (owner, bytesSent, (int) postData.getSize()))
break;
}
}
}
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, NULL, adaptersAddresses, &len) == ERROR_BUFFER_OVERFLOW)
adaptersAddresses.malloc (len, 1);
return getAdaptersAddresses (AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, NULL, 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 != 0)
{
LANA_ENUM enums = { 0 };
{
NCB ncb = { 0 };
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 = { 0 };
ncb2.ncb_command = NCBRESET;
ncb2.ncb_lana_num = enums.lana[i];
if (NetbiosCall (&ncb2) == 0)
{
NCB ncb = { 0 };
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
}
}
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)
{
PIP_ADAPTER_UNICAST_ADDRESS pUnicast = nullptr;
for (pUnicast = adapter->FirstUnicastAddress; pUnicast != nullptr; pUnicast = pUnicast->Next)
{
if (pUnicast->Address.lpSockaddr->sa_family == AF_INET)
{
const sockaddr_in* sa_in = (sockaddr_in*)pUnicast->Address.lpSockaddr;
IPAddress ip ((uint8*)&sa_in->sin_addr.s_addr, false);
result.addIfNotAlreadyThere (ip);
}
else if (pUnicast->Address.lpSockaddr->sa_family == AF_INET6 && includeIPv6)
{
const sockaddr_in6* sa_in6 = (sockaddr_in6*)pUnicast->Address.lpSockaddr;
ByteUnion temp;
uint16 arr[8];
for (int i = 0; i < 8; ++i)
{
MACAddressHelpers::split (sa_in6, i * 2, temp.split);
arr[i] = temp.combined;
}
IPAddress ip (arr);
result.addIfNotAlreadyThere (ip);
}
}
PIP_ADAPTER_ANYCAST_ADDRESS pAnycast = nullptr;
for (pAnycast = adapter->FirstAnycastAddress; pAnycast != nullptr; pAnycast = pAnycast->Next)
{
if (pAnycast->Address.lpSockaddr->sa_family == AF_INET)
{
const sockaddr_in* sa_in = (sockaddr_in*)pAnycast->Address.lpSockaddr;
IPAddress ip ((uint8*)&sa_in->sin_addr.s_addr, false);
result.addIfNotAlreadyThere (ip);
}
else if (pAnycast->Address.lpSockaddr->sa_family == AF_INET6 && includeIPv6)
{
const sockaddr_in6* sa_in6 = (sockaddr_in6*)pAnycast->Address.lpSockaddr;
ByteUnion temp;
uint16 arr[8];
for (int i = 0; i < 8; ++i)
{
MACAddressHelpers::split (sa_in6, i * 2, temp.split);
arr[i] = temp.combined;
}
IPAddress ip (arr);
result.addIfNotAlreadyThere (ip);
}
}
PIP_ADAPTER_MULTICAST_ADDRESS pMulticast = nullptr;
for (pMulticast = adapter->FirstMulticastAddress; pMulticast != nullptr; pMulticast = pMulticast->Next)
{
if (pMulticast->Address.lpSockaddr->sa_family == AF_INET)
{
const sockaddr_in* sa_in = (sockaddr_in*)pMulticast->Address.lpSockaddr;
IPAddress ip ((uint8*)&sa_in->sin_addr.s_addr, false);
result.addIfNotAlreadyThere (ip);
}
else if (pMulticast->Address.lpSockaddr->sa_family == AF_INET6 && includeIPv6)
{
const sockaddr_in6* sa_in6 = (sockaddr_in6*)pMulticast->Address.lpSockaddr;
ByteUnion temp;
uint16 arr[8];
for (int i = 0; i < 8; ++i)
{
MACAddressHelpers::split (sa_in6, i * 2, temp.split);
arr[i] = temp.combined;
}
IPAddress ip (arr);
result.addIfNotAlreadyThere (ip);
}
}
}
}
}
//==============================================================================
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 = { 0 };
message.lpszSubject = (LPSTR) emailSubject.toRawUTF8();
message.lpszNoteText = (LPSTR) bodyText.toRawUTF8();
MapiRecipDesc recip = { 0 };
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;
}
URL::DownloadTask* URL::downloadToFile (const File& targetLocation, String extraHeaders, DownloadTask::Listener* listener, bool shouldUsePost)
{
return URL::DownloadTask::createFallbackDownloader (*this, targetLocation, extraHeaders, listener, shouldUsePost);
}
} // namespace juce

View File

@ -0,0 +1,251 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
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, 0, REG_OPTION_NON_VOLATILE,
KEY_WRITE | KEY_QUERY_VALUE | wow64Flags, 0, &key, &result);
else
RegOpenKeyEx (rootKey, wideCharName, 0, KEY_READ | wow64Flags, &key);
}
}
~RegistryKeyWrapper()
{
if (key != 0)
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 0;
}
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 != 0
&& 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 != 0)
{
for (unsigned long bufferSize = 1024; ; bufferSize *= 2)
{
result.setSize (bufferSize, false);
DWORD type = REG_NONE;
auto err = RegQueryValueEx (key.key, key.wideCharValueName, 0, &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& regValuePath, const DWORD wow64Flags)
{
return RegistryKeyWrapper (regValuePath, false, wow64Flags).key != 0;
}
static bool valueExists (const String& regValuePath, const DWORD wow64Flags)
{
const RegistryKeyWrapper key (regValuePath, false, wow64Flags);
if (key.key == 0)
return false;
unsigned char buffer [512];
unsigned long bufferSize = sizeof (buffer);
DWORD type = 0;
auto result = RegQueryValueEx (key.key, key.wideCharValueName,
0, &type, buffer, &bufferSize);
return result == ERROR_SUCCESS || result == ERROR_MORE_DATA;
}
HKEY key = 0;
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& regValuePath, WoW64Mode mode)
{
return RegistryKeyWrapper::keyExists (regValuePath, (DWORD) mode);
}
bool JUCE_CALLTYPE WindowsRegistry::deleteValue (const String& regValuePath, WoW64Mode mode)
{
const RegistryKeyWrapper key (regValuePath, true, (DWORD) mode);
return key.key != 0 && 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 != 0 && 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,499 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
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
#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
static void callCPUID (int result[4], uint32 type)
{
uint32 la = result[0], lb = result[1], lc = result[2], ld = 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] = la; result[1] = lb; result[2] = lc; result[3] = ld;
}
#else
static void callCPUID (int result[4], int infoType)
{
#if JUCE_PROJUCER_LIVE_BUILD
std::fill (result, result + 4, 0);
#else
__cpuid (result, infoType);
#endif
}
#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;
hasSSSE3 = (info[2] & (1 << 9)) != 0;
hasSSE41 = (info[2] & (1 << 19)) != 0;
hasSSE42 = (info[2] & (1 << 20)) != 0;
has3DNow = (info[1] & (1 << 31)) != 0;
callCPUID (info, 7);
hasAVX2 = (info[1] & (1 << 5)) != 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
//==============================================================================
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;
}
SystemStats::OperatingSystemType SystemStats::getOperatingSystemType()
{
auto v = getWindowsVersion();
auto major = (v >> 16);
jassert (major <= 10); // need to add support for new version!
if (major == 10) return Windows10;
if (v == 0x00060003) return Windows8_1;
if (v == 0x00060002) return Windows8_0;
if (v == 0x00060001) return Windows7;
if (v == 0x00060000) return WinVista;
if (v == 0x00050000) return Win2000;
if (major == 5) return WinXP;
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;
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);
LPFN_ISWOW64PROCESS fnIsWow64Process
= (LPFN_ISWOW64PROCESS) GetProcAddress (GetModuleHandleA ("kernel32"), "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)
{
DWORD len = GetEnvironmentVariableW (name.toWideCharPointer(), nullptr, 0);
if (GetLastError() == ERROR_ENVVAR_NOT_FOUND)
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
const MMRESULT res = timeBeginPeriod (JUCE_WIN32_TIMER_PERIOD);
ignoreUnused (res);
jassert (res == TIMERR_NOERROR);
#endif
LARGE_INTEGER f;
QueryPerformanceFrequency (&f);
hiResTicksPerSecond = f.QuadPart;
hiResTicksScaleFactor = 1000.0 / hiResTicksPerSecond;
}
inline int64 getHighResolutionTicks() noexcept
{
LARGE_INTEGER ticks;
QueryPerformanceCounter (&ticks);
return ticks.QuadPart + hiResTicksOffset;
}
inline double getMillisecondCounterHiRes() noexcept
{
return 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::getCpuSpeedInMegaherz()
{
const int64 cycles = juce_getClockCycleCounter();
const uint32 millis = Time::getMillisecondCounter();
int lastResult = 0;
for (;;)
{
int n = 1000000;
while (--n > 0) {}
const uint32 millisElapsed = Time::getMillisecondCounter() - millis;
const int64 cyclesNow = juce_getClockCycleCounter();
if (millisElapsed > 80)
{
const int 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
bool 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 };
DWORD len = (DWORD) numElementsInArray (text) - 1;
GetUserName (text, &len);
return String (text, len);
}
String SystemStats::getFullUserName()
{
return getLogonName();
}
String SystemStats::getComputerName()
{
TCHAR text[128] = { 0 };
DWORD len = (DWORD) numElementsInArray (text) - 1;
GetComputerName (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, GetUserDefaultUILanguage, getUserDefaultUILanguage, LANGID, (void))
if (getUserDefaultUILanguage == nullptr)
return "en";
const DWORD langID = MAKELCID (getUserDefaultUILanguage(), SORT_DEFAULT);
String mainLang (getLocaleValue (langID, LOCALE_SISO639LANGNAME, "en"));
String region (getLocaleValue (langID, LOCALE_SISO3166CTRYNAME, nullptr));
if (region.isNotEmpty())
mainLang << '-' << region;
return mainLang;
}
} // namespace juce

View File

@ -0,0 +1,591 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
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 = 0; // (this is used by other parts of the codebase)
void* getUser32Function (const char* functionName)
{
HMODULE module = GetModuleHandleA ("user32.dll");
jassert (module != 0);
return (void*) GetProcAddress (module, functionName);
}
//==============================================================================
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); }
//==============================================================================
WaitableEvent::WaitableEvent (const bool manualReset) noexcept
: handle (CreateEvent (0, manualReset ? TRUE : FALSE, FALSE, 0)) {}
WaitableEvent::~WaitableEvent() noexcept { CloseHandle (handle); }
void WaitableEvent::signal() const noexcept { SetEvent (handle); }
void WaitableEvent::reset() const noexcept { ResetEvent (handle); }
bool WaitableEvent::wait (const int timeOutMs) const noexcept
{
return WaitForSingleObject (handle, (DWORD) timeOutMs) == WAIT_OBJECT_0;
}
//==============================================================================
void JUCE_API juce_threadEntryPoint (void*);
static unsigned int __stdcall threadEntryProc (void* userData)
{
if (juce_messageWindowHandle != 0)
AttachThreadInput (GetWindowThreadProcessId (juce_messageWindowHandle, 0),
GetCurrentThreadId(), TRUE);
juce_threadEntryPoint (userData);
_endthreadex (0);
return 0;
}
void Thread::launchThread()
{
unsigned int newThreadId;
threadHandle = (void*) _beginthreadex (0, (unsigned int) threadStackSize,
&threadEntryProc, this, 0, &newThreadId);
threadId = (ThreadID) (pointer_sized_int) newThreadId;
}
void Thread::closeThreadHandle()
{
CloseHandle ((HANDLE) threadHandle.get());
threadId = 0;
threadHandle = 0;
}
void Thread::killThread()
{
if (threadHandle.get() != 0)
{
#if JUCE_DEBUG
OutputDebugStringA ("** Warning - Forced thread termination **\n");
#endif
TerminateThread (threadHandle.get(), 0);
}
}
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 (EXCEPTION_CONTINUE_EXECUTION)
{}
#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 == 0)
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 = 0;
}
HANDLE handle;
};
static SleepEvent sleepEvent;
void JUCE_CALLTYPE Thread::sleep (const int millisecs)
{
jassert (millisecs >= 0);
if (millisecs >= 10 || sleepEvent.handle == 0)
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()
{
jassertfalse; // xxx not implemented
}
void JUCE_CALLTYPE Process::lowerPrivilege()
{
jassertfalse; // xxx not implemented
}
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 != 0 && 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 name, const int timeOutMillisecs)
: handle (0), refCount (1)
{
name = name.replaceCharacter ('\\', '/');
handle = CreateMutexW (0, TRUE, ("Global\\" + name).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 == 0)
handle = CreateMutexW (0, TRUE, ("Local\\" + name).toWideCharPointer());
if (handle != 0 && GetLastError() == ERROR_ALREADY_EXISTS)
{
if (timeOutMillisecs == 0)
{
close();
return;
}
switch (WaitForSingleObject (handle, timeOutMillisecs < 0 ? INFINITE : timeOutMillisecs))
{
case WAIT_OBJECT_0:
case WAIT_ABANDONED:
break;
case WAIT_TIMEOUT:
default:
close();
break;
}
}
}
~Pimpl()
{
close();
}
void close()
{
if (handle != 0)
{
ReleaseMutex (handle);
CloseHandle (handle);
handle = 0;
}
}
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 == 0)
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 (0), writePipe (0)
{
SECURITY_ATTRIBUTES securityAtts = { 0 };
securityAtts.nLength = sizeof (securityAtts);
securityAtts.bInheritHandle = TRUE;
if (CreatePipe (&readPipe, &writePipe, &securityAtts, 0)
&& SetHandleInformation (readPipe, HANDLE_FLAG_INHERIT, 0))
{
STARTUPINFOW startupInfo = { 0 };
startupInfo.cb = sizeof (startupInfo);
startupInfo.hStdOutput = (streamFlags & wantStdOut) != 0 ? writePipe : 0;
startupInfo.hStdError = (streamFlags & wantStdErr) != 0 ? writePipe : 0;
startupInfo.dwFlags = STARTF_USESTDHANDLES;
ok = CreateProcess (nullptr, const_cast<LPWSTR> (command.toWideCharPointer()),
nullptr, nullptr, TRUE, CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT,
nullptr, nullptr, &startupInfo, &processInfo) != FALSE;
}
}
~ActiveProcess()
{
if (ok)
{
CloseHandle (processInfo.hThread);
CloseHandle (processInfo.hProcess);
}
if (readPipe != 0)
CloseHandle (readPipe);
if (writePipe != 0)
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::yield();
}
else
{
DWORD numRead = 0;
if (! ReadFile ((HANDLE) readPipe, dest, numToDo, &numRead, nullptr))
break;
total += numRead;
dest = addBytesToPointer (dest, numRead);
numNeeded -= 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 (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 __stdcall 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