Merge branch 'master' of https://github.com/tildearrow/furnace into k053260
This commit is contained in:
commit
a413868fcb
1
.gitmodules
vendored
1
.gitmodules
vendored
|
@ -1,6 +1,7 @@
|
|||
[submodule "extern/SDL"]
|
||||
path = extern/SDL
|
||||
url = https://github.com/libsdl-org/SDL.git
|
||||
branch = release-2.28.x
|
||||
[submodule "extern/libsndfile"]
|
||||
path = extern/libsndfile
|
||||
url = https://github.com/libsndfile/libsndfile.git
|
||||
|
|
|
@ -119,7 +119,7 @@ check out the [Releases](https://github.com/tildearrow/furnace/releases) page. a
|
|||
---
|
||||
# quick references
|
||||
|
||||
- **discussion**: see the [Discussions](https://github.com/tildearrow/furnace/discussions) section, or the [official Discord server](https://discord.gg/EfrwT2wq7z).
|
||||
- **discussion**: see the [Discussions](https://github.com/tildearrow/furnace/discussions) section, the [official Revolt](https://rvlt.gg/GRPS6tmc) or the [official Discord server](https://discord.gg/EfrwT2wq7z).
|
||||
- **help**: check out the [documentation](doc/README.md). it's incomplete though.
|
||||
|
||||
## packages
|
||||
|
|
|
@ -186,7 +186,7 @@ class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDe
|
|||
// Because on Chromebooks we show up as a dual-mode device, it will attempt to connect TRANSPORT_AUTO, which will use TRANSPORT_BREDR instead
|
||||
// of TRANSPORT_LE. Let's force ourselves to connect low energy.
|
||||
private BluetoothGatt connectGatt(boolean managed) {
|
||||
if (Build.VERSION.SDK_INT >= 23) {
|
||||
if (Build.VERSION.SDK_INT >= 23 /* Android 6.0 (M) */) {
|
||||
try {
|
||||
return mDevice.connectGatt(mManager.getContext(), managed, this, TRANSPORT_LE);
|
||||
} catch (Exception e) {
|
||||
|
|
|
@ -251,6 +251,8 @@ public class HIDDeviceManager {
|
|||
0x20d6, // PowerA
|
||||
0x24c6, // PowerA
|
||||
0x2c22, // Qanba
|
||||
0x2dc8, // 8BitDo
|
||||
0x9886, // ASTRO Gaming
|
||||
};
|
||||
|
||||
if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_VENDOR_SPEC &&
|
||||
|
@ -271,14 +273,16 @@ public class HIDDeviceManager {
|
|||
final int XB1_IFACE_SUBCLASS = 71;
|
||||
final int XB1_IFACE_PROTOCOL = 208;
|
||||
final int[] SUPPORTED_VENDORS = {
|
||||
0x044f, // Thrustmaster
|
||||
0x045e, // Microsoft
|
||||
0x0738, // Mad Catz
|
||||
0x0e6f, // PDP
|
||||
0x0f0d, // Hori
|
||||
0x10f5, // Turtle Beach
|
||||
0x1532, // Razer Wildcat
|
||||
0x20d6, // PowerA
|
||||
0x24c6, // PowerA
|
||||
0x2dc8, /* 8BitDo */
|
||||
0x2dc8, // 8BitDo
|
||||
0x2e24, // Hyperkin
|
||||
};
|
||||
|
||||
|
@ -353,13 +357,13 @@ public class HIDDeviceManager {
|
|||
private void initializeBluetooth() {
|
||||
Log.d(TAG, "Initializing Bluetooth");
|
||||
|
||||
if (Build.VERSION.SDK_INT <= 30 &&
|
||||
if (Build.VERSION.SDK_INT <= 30 /* Android 11.0 (R) */ &&
|
||||
mContext.getPackageManager().checkPermission(android.Manifest.permission.BLUETOOTH, mContext.getPackageName()) != PackageManager.PERMISSION_GRANTED) {
|
||||
Log.d(TAG, "Couldn't initialize Bluetooth, missing android.permission.BLUETOOTH");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE) || (Build.VERSION.SDK_INT < 18)) {
|
||||
if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE) || (Build.VERSION.SDK_INT < 18 /* Android 4.3 (JELLY_BEAN_MR2) */)) {
|
||||
Log.d(TAG, "Couldn't initialize Bluetooth, this version of Android does not support Bluetooth LE");
|
||||
return;
|
||||
}
|
||||
|
@ -573,7 +577,7 @@ public class HIDDeviceManager {
|
|||
try {
|
||||
final int FLAG_MUTABLE = 0x02000000; // PendingIntent.FLAG_MUTABLE, but don't require SDK 31
|
||||
int flags;
|
||||
if (Build.VERSION.SDK_INT >= 31) {
|
||||
if (Build.VERSION.SDK_INT >= 31 /* Android 12.0 (S) */) {
|
||||
flags = FLAG_MUTABLE;
|
||||
} else {
|
||||
flags = 0;
|
||||
|
|
|
@ -52,7 +52,7 @@ class HIDDeviceUSB implements HIDDevice {
|
|||
@Override
|
||||
public String getSerialNumber() {
|
||||
String result = null;
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
if (Build.VERSION.SDK_INT >= 21 /* Android 5.0 (LOLLIPOP) */) {
|
||||
try {
|
||||
result = mDevice.getSerialNumber();
|
||||
}
|
||||
|
@ -74,7 +74,7 @@ class HIDDeviceUSB implements HIDDevice {
|
|||
@Override
|
||||
public String getManufacturerName() {
|
||||
String result = null;
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
if (Build.VERSION.SDK_INT >= 21 /* Android 5.0 (LOLLIPOP) */) {
|
||||
result = mDevice.getManufacturerName();
|
||||
}
|
||||
if (result == null) {
|
||||
|
@ -86,7 +86,7 @@ class HIDDeviceUSB implements HIDDevice {
|
|||
@Override
|
||||
public String getProductName() {
|
||||
String result = null;
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
if (Build.VERSION.SDK_INT >= 21 /* Android 5.0 (LOLLIPOP) */) {
|
||||
result = mDevice.getProductName();
|
||||
}
|
||||
if (result == null) {
|
||||
|
|
|
@ -29,6 +29,7 @@ public class SDL {
|
|||
|
||||
// This function stores the current activity (SDL or not)
|
||||
public static void setContext(Context context) {
|
||||
SDLAudioManager.setContext(context);
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
|
|
|
@ -15,13 +15,9 @@ import android.content.pm.PackageManager;
|
|||
import android.content.res.Configuration;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.hardware.Sensor;
|
||||
import android.hardware.SensorEvent;
|
||||
import android.hardware.SensorEventListener;
|
||||
import android.hardware.SensorManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
|
@ -37,11 +33,8 @@ import android.view.Display;
|
|||
import android.view.Gravity;
|
||||
import android.view.InputDevice;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.PointerIcon;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.SurfaceView;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
|
@ -51,6 +44,7 @@ import android.view.inputmethod.EditorInfo;
|
|||
import android.view.inputmethod.InputConnection;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
|
@ -65,6 +59,9 @@ import java.util.Locale;
|
|||
*/
|
||||
public class SDLActivity extends Activity implements View.OnSystemUiVisibilityChangeListener {
|
||||
private static final String TAG = "SDL";
|
||||
private static final int SDL_MAJOR_VERSION = 2;
|
||||
private static final int SDL_MINOR_VERSION = 28;
|
||||
private static final int SDL_MICRO_VERSION = 0;
|
||||
/*
|
||||
// Display InputType.SOURCE/CLASS of events and devices
|
||||
//
|
||||
|
@ -96,7 +93,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
|
|||
|
||||
s2 = s_copy & InputDevice.SOURCE_ANY; // keep source only, no class;
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
if (Build.VERSION.SDK_INT >= 23) {
|
||||
tst = InputDevice.SOURCE_BLUETOOTH_STYLUS;
|
||||
if ((s & tst) == tst) src += " BLUETOOTH_STYLUS";
|
||||
s2 &= ~tst;
|
||||
|
@ -110,7 +107,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
|
|||
if ((s & tst) == tst) src += " GAMEPAD";
|
||||
s2 &= ~tst;
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
tst = InputDevice.SOURCE_HDMI;
|
||||
if ((s & tst) == tst) src += " HDMI";
|
||||
s2 &= ~tst;
|
||||
|
@ -149,7 +146,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
|
|||
if ((s & tst) == tst) src += " TOUCHSCREEN";
|
||||
s2 &= ~tst;
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
|
||||
if (Build.VERSION.SDK_INT >= 18) {
|
||||
tst = InputDevice.SOURCE_TOUCH_NAVIGATION;
|
||||
if ((s & tst) == tst) src += " TOUCH_NAVIGATION";
|
||||
s2 &= ~tst;
|
||||
|
@ -173,7 +170,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
|
|||
*/
|
||||
|
||||
public static boolean mIsResumedCalled, mHasFocus;
|
||||
public static final boolean mHasMultiWindow = (Build.VERSION.SDK_INT >= 24);
|
||||
public static final boolean mHasMultiWindow = (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */);
|
||||
|
||||
// Cursor types
|
||||
// private static final int SDL_SYSTEM_CURSOR_NONE = -1;
|
||||
|
@ -213,7 +210,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
|
|||
// Main components
|
||||
protected static SDLActivity mSingleton;
|
||||
protected static SDLSurface mSurface;
|
||||
protected static View mTextEdit;
|
||||
protected static DummyEdit mTextEdit;
|
||||
protected static boolean mScreenKeyboardShown;
|
||||
protected static ViewGroup mLayout;
|
||||
protected static SDLClipboardHandler mClipboardHandler;
|
||||
|
@ -227,9 +224,9 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
|
|||
|
||||
protected static SDLGenericMotionListener_API12 getMotionListener() {
|
||||
if (mMotionListener == null) {
|
||||
if (Build.VERSION.SDK_INT >= 26) {
|
||||
if (Build.VERSION.SDK_INT >= 26 /* Android 8.0 (O) */) {
|
||||
mMotionListener = new SDLGenericMotionListener_API26();
|
||||
} else if (Build.VERSION.SDK_INT >= 24) {
|
||||
} else if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
|
||||
mMotionListener = new SDLGenericMotionListener_API24();
|
||||
} else {
|
||||
mMotionListener = new SDLGenericMotionListener_API12();
|
||||
|
@ -315,6 +312,10 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
|
|||
mCurrentNativeState = NativeState.INIT;
|
||||
}
|
||||
|
||||
protected SDLSurface createSDLSurface(Context context) {
|
||||
return new SDLSurface(context);
|
||||
}
|
||||
|
||||
// Setup
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
|
@ -344,8 +345,18 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
|
|||
errorMsgBrokenLib = e.getMessage();
|
||||
}
|
||||
|
||||
if (mBrokenLibraries)
|
||||
{
|
||||
if (!mBrokenLibraries) {
|
||||
String expected_version = String.valueOf(SDL_MAJOR_VERSION) + "." +
|
||||
String.valueOf(SDL_MINOR_VERSION) + "." +
|
||||
String.valueOf(SDL_MICRO_VERSION);
|
||||
String version = nativeGetVersion();
|
||||
if (!version.equals(expected_version)) {
|
||||
mBrokenLibraries = true;
|
||||
errorMsgBrokenLib = "SDL C/Java version mismatch (expected " + expected_version + ", got " + version + ")";
|
||||
}
|
||||
}
|
||||
|
||||
if (mBrokenLibraries) {
|
||||
mSingleton = this;
|
||||
AlertDialog.Builder dlgAlert = new AlertDialog.Builder(this);
|
||||
dlgAlert.setMessage("An error occurred while trying to start the application. Please try again and/or reinstall."
|
||||
|
@ -382,7 +393,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
|
|||
mHIDDeviceManager = HIDDeviceManager.acquire(this);
|
||||
|
||||
// Set up the surface
|
||||
mSurface = new SDLSurface(getApplication());
|
||||
mSurface = createSDLSurface(this);
|
||||
|
||||
mLayout = new RelativeLayout(this);
|
||||
mLayout.addView(mSurface);
|
||||
|
@ -393,7 +404,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
|
|||
SDLActivity.onNativeOrientationChanged(mCurrentOrientation);
|
||||
|
||||
try {
|
||||
if (Build.VERSION.SDK_INT < 24) {
|
||||
if (Build.VERSION.SDK_INT < 24 /* Android 7.0 (N) */) {
|
||||
mCurrentLocale = getContext().getResources().getConfiguration().locale;
|
||||
} else {
|
||||
mCurrentLocale = getContext().getResources().getConfiguration().getLocales().get(0);
|
||||
|
@ -577,6 +588,8 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
|
|||
mHIDDeviceManager = null;
|
||||
}
|
||||
|
||||
SDLAudioManager.release(this);
|
||||
|
||||
if (SDLActivity.mBrokenLibraries) {
|
||||
super.onDestroy();
|
||||
return;
|
||||
|
@ -755,7 +768,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
|
|||
}
|
||||
break;
|
||||
case COMMAND_CHANGE_WINDOW_STYLE:
|
||||
if (Build.VERSION.SDK_INT >= 19) {
|
||||
if (Build.VERSION.SDK_INT >= 19 /* Android 4.4 (KITKAT) */) {
|
||||
if (context instanceof Activity) {
|
||||
Window window = ((Activity) context).getWindow();
|
||||
if (window != null) {
|
||||
|
@ -830,7 +843,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
|
|||
msg.obj = data;
|
||||
boolean result = commandHandler.sendMessage(msg);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 19) {
|
||||
if (Build.VERSION.SDK_INT >= 19 /* Android 4.4 (KITKAT) */) {
|
||||
if (command == COMMAND_CHANGE_WINDOW_STYLE) {
|
||||
// Ensure we don't return until the resize has actually happened,
|
||||
// or 500ms have passed.
|
||||
|
@ -886,6 +899,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
|
|||
}
|
||||
|
||||
// C functions we call
|
||||
public static native String nativeGetVersion();
|
||||
public static native int nativeSetupJNI();
|
||||
public static native int nativeRunMain(String library, String function, Object arguments);
|
||||
public static native void nativeLowMemory();
|
||||
|
@ -957,15 +971,18 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
|
|||
/* If set, hint "explicitly controls which UI orientations are allowed". */
|
||||
if (hint.contains("LandscapeRight") && hint.contains("LandscapeLeft")) {
|
||||
orientation_landscape = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE;
|
||||
} else if (hint.contains("LandscapeRight")) {
|
||||
orientation_landscape = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
|
||||
} else if (hint.contains("LandscapeLeft")) {
|
||||
orientation_landscape = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
|
||||
} else if (hint.contains("LandscapeRight")) {
|
||||
orientation_landscape = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
|
||||
}
|
||||
|
||||
if (hint.contains("Portrait") && hint.contains("PortraitUpsideDown")) {
|
||||
/* exact match to 'Portrait' to distinguish with PortraitUpsideDown */
|
||||
boolean contains_Portrait = hint.contains("Portrait ") || hint.endsWith("Portrait");
|
||||
|
||||
if (contains_Portrait && hint.contains("PortraitUpsideDown")) {
|
||||
orientation_portrait = ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT;
|
||||
} else if (hint.contains("Portrait")) {
|
||||
} else if (contains_Portrait) {
|
||||
orientation_portrait = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
|
||||
} else if (hint.contains("PortraitUpsideDown")) {
|
||||
orientation_portrait = ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
|
||||
|
@ -1078,7 +1095,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
|
|||
// thus SDK version 27. If we are in DeX mode and not API 27 or higher, as a result,
|
||||
// we should stick to relative mode.
|
||||
//
|
||||
if ((Build.VERSION.SDK_INT < 27) && isDeXMode()) {
|
||||
if (Build.VERSION.SDK_INT < 27 /* Android 8.1 (O_MR1) */ && isDeXMode()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1168,7 +1185,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
|
|||
* This method is called by SDL using JNI.
|
||||
*/
|
||||
public static boolean isDeXMode() {
|
||||
if (Build.VERSION.SDK_INT < 24) {
|
||||
if (Build.VERSION.SDK_INT < 24 /* Android 7.0 (N) */) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
|
@ -1220,8 +1237,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
|
|||
}
|
||||
|
||||
// This method is called by SDLControllerManager's API 26 Generic Motion Handler.
|
||||
public static View getContentView()
|
||||
{
|
||||
public static View getContentView() {
|
||||
return mLayout;
|
||||
}
|
||||
|
||||
|
@ -1292,6 +1308,77 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
|
|||
return event.isPrintingKey() || event.getKeyCode() == KeyEvent.KEYCODE_SPACE;
|
||||
}
|
||||
|
||||
public static boolean handleKeyEvent(View v, int keyCode, KeyEvent event, InputConnection ic) {
|
||||
int deviceId = event.getDeviceId();
|
||||
int source = event.getSource();
|
||||
|
||||
if (source == InputDevice.SOURCE_UNKNOWN) {
|
||||
InputDevice device = InputDevice.getDevice(deviceId);
|
||||
if (device != null) {
|
||||
source = device.getSources();
|
||||
}
|
||||
}
|
||||
|
||||
// if (event.getAction() == KeyEvent.ACTION_DOWN) {
|
||||
// Log.v("SDL", "key down: " + keyCode + ", deviceId = " + deviceId + ", source = " + source);
|
||||
// } else if (event.getAction() == KeyEvent.ACTION_UP) {
|
||||
// Log.v("SDL", "key up: " + keyCode + ", deviceId = " + deviceId + ", source = " + source);
|
||||
// }
|
||||
|
||||
// Dispatch the different events depending on where they come from
|
||||
// Some SOURCE_JOYSTICK, SOURCE_DPAD or SOURCE_GAMEPAD are also SOURCE_KEYBOARD
|
||||
// So, we try to process them as JOYSTICK/DPAD/GAMEPAD events first, if that fails we try them as KEYBOARD
|
||||
//
|
||||
// Furthermore, it's possible a game controller has SOURCE_KEYBOARD and
|
||||
// SOURCE_JOYSTICK, while its key events arrive from the keyboard source
|
||||
// So, retrieve the device itself and check all of its sources
|
||||
if (SDLControllerManager.isDeviceSDLJoystick(deviceId)) {
|
||||
// Note that we process events with specific key codes here
|
||||
if (event.getAction() == KeyEvent.ACTION_DOWN) {
|
||||
if (SDLControllerManager.onNativePadDown(deviceId, keyCode) == 0) {
|
||||
return true;
|
||||
}
|
||||
} else if (event.getAction() == KeyEvent.ACTION_UP) {
|
||||
if (SDLControllerManager.onNativePadUp(deviceId, keyCode) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((source & InputDevice.SOURCE_KEYBOARD) == InputDevice.SOURCE_KEYBOARD) {
|
||||
if (event.getAction() == KeyEvent.ACTION_DOWN) {
|
||||
if (isTextInputEvent(event)) {
|
||||
if (ic != null) {
|
||||
ic.commitText(String.valueOf((char) event.getUnicodeChar()), 1);
|
||||
} else {
|
||||
SDLInputConnection.nativeCommitText(String.valueOf((char) event.getUnicodeChar()), 1);
|
||||
}
|
||||
}
|
||||
onNativeKeyDown(keyCode);
|
||||
return true;
|
||||
} else if (event.getAction() == KeyEvent.ACTION_UP) {
|
||||
onNativeKeyUp(keyCode);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if ((source & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE) {
|
||||
// on some devices key events are sent for mouse BUTTON_BACK/FORWARD presses
|
||||
// they are ignored here because sending them as mouse input to SDL is messy
|
||||
if ((keyCode == KeyEvent.KEYCODE_BACK) || (keyCode == KeyEvent.KEYCODE_FORWARD)) {
|
||||
switch (event.getAction()) {
|
||||
case KeyEvent.ACTION_DOWN:
|
||||
case KeyEvent.ACTION_UP:
|
||||
// mark the event as handled or it will be handled by system
|
||||
// handling KEYCODE_BACK by system will call onBackPressed()
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called by SDL using JNI.
|
||||
*/
|
||||
|
@ -1535,7 +1622,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
|
|||
private final Runnable rehideSystemUi = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (Build.VERSION.SDK_INT >= 19) {
|
||||
if (Build.VERSION.SDK_INT >= 19 /* Android 4.4 (KITKAT) */) {
|
||||
int flags = View.SYSTEM_UI_FLAG_FULLSCREEN |
|
||||
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
|
||||
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY |
|
||||
|
@ -1588,7 +1675,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
|
|||
Bitmap bitmap = Bitmap.createBitmap(colors, width, height, Bitmap.Config.ARGB_8888);
|
||||
++mLastCursorID;
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 24) {
|
||||
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
|
||||
try {
|
||||
mCursors.put(mLastCursorID, PointerIcon.create(bitmap, hotSpotX, hotSpotY));
|
||||
} catch (Exception e) {
|
||||
|
@ -1604,7 +1691,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
|
|||
* This method is called by SDL using JNI.
|
||||
*/
|
||||
public static void destroyCustomCursor(int cursorID) {
|
||||
if (Build.VERSION.SDK_INT >= 24) {
|
||||
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
|
||||
try {
|
||||
mCursors.remove(cursorID);
|
||||
} catch (Exception e) {
|
||||
|
@ -1618,7 +1705,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
|
|||
*/
|
||||
public static boolean setCustomCursor(int cursorID) {
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 24) {
|
||||
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
|
||||
try {
|
||||
mSurface.setPointerIcon(mCursors.get(cursorID));
|
||||
} catch (Exception e) {
|
||||
|
@ -1673,7 +1760,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
|
|||
cursor_type = 1002; //PointerIcon.TYPE_HAND;
|
||||
break;
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= 24) {
|
||||
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
|
||||
try {
|
||||
mSurface.setPointerIcon(PointerIcon.getSystemIcon(SDL.getContext(), cursor_type));
|
||||
} catch (Exception e) {
|
||||
|
@ -1687,7 +1774,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
|
|||
* This method is called by SDL using JNI.
|
||||
*/
|
||||
public static void requestPermission(String permission, int requestCode) {
|
||||
if (Build.VERSION.SDK_INT < 23) {
|
||||
if (Build.VERSION.SDK_INT < 23 /* Android 6.0 (M) */) {
|
||||
nativePermissionResult(requestCode, true);
|
||||
return;
|
||||
}
|
||||
|
@ -1716,7 +1803,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
|
|||
i.setData(Uri.parse(url));
|
||||
|
||||
int flags = Intent.FLAG_ACTIVITY_NO_HISTORY | Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
if (Build.VERSION.SDK_INT >= 21 /* Android 5.0 (LOLLIPOP) */) {
|
||||
flags |= Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
|
||||
} else {
|
||||
flags |= Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET;
|
||||
|
@ -1809,455 +1896,6 @@ class SDLMain implements Runnable {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
SDLSurface. This is what we draw on, so we need to know when it's created
|
||||
in order to do anything useful.
|
||||
|
||||
Because of this, that's where we set up the SDL thread
|
||||
*/
|
||||
class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
|
||||
View.OnKeyListener, View.OnTouchListener, SensorEventListener {
|
||||
|
||||
// Sensors
|
||||
protected SensorManager mSensorManager;
|
||||
protected Display mDisplay;
|
||||
|
||||
// Keep track of the surface size to normalize touch events
|
||||
protected float mWidth, mHeight;
|
||||
|
||||
// Is SurfaceView ready for rendering
|
||||
public boolean mIsSurfaceReady;
|
||||
|
||||
// Startup
|
||||
public SDLSurface(Context context) {
|
||||
super(context);
|
||||
getHolder().addCallback(this);
|
||||
|
||||
setFocusable(true);
|
||||
setFocusableInTouchMode(true);
|
||||
requestFocus();
|
||||
setOnKeyListener(this);
|
||||
setOnTouchListener(this);
|
||||
|
||||
mDisplay = ((WindowManager)context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
|
||||
mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
|
||||
|
||||
setOnGenericMotionListener(SDLActivity.getMotionListener());
|
||||
|
||||
// Some arbitrary defaults to avoid a potential division by zero
|
||||
mWidth = 1.0f;
|
||||
mHeight = 1.0f;
|
||||
|
||||
mIsSurfaceReady = false;
|
||||
}
|
||||
|
||||
public void handlePause() {
|
||||
enableSensor(Sensor.TYPE_ACCELEROMETER, false);
|
||||
}
|
||||
|
||||
public void handleResume() {
|
||||
setFocusable(true);
|
||||
setFocusableInTouchMode(true);
|
||||
requestFocus();
|
||||
setOnKeyListener(this);
|
||||
setOnTouchListener(this);
|
||||
enableSensor(Sensor.TYPE_ACCELEROMETER, true);
|
||||
}
|
||||
|
||||
public Surface getNativeSurface() {
|
||||
return getHolder().getSurface();
|
||||
}
|
||||
|
||||
// Called when we have a valid drawing surface
|
||||
@Override
|
||||
public void surfaceCreated(SurfaceHolder holder) {
|
||||
Log.v("SDL", "surfaceCreated()");
|
||||
SDLActivity.onNativeSurfaceCreated();
|
||||
}
|
||||
|
||||
// Called when we lose the surface
|
||||
@Override
|
||||
public void surfaceDestroyed(SurfaceHolder holder) {
|
||||
Log.v("SDL", "surfaceDestroyed()");
|
||||
|
||||
// Transition to pause, if needed
|
||||
SDLActivity.mNextNativeState = SDLActivity.NativeState.PAUSED;
|
||||
SDLActivity.handleNativeState();
|
||||
|
||||
mIsSurfaceReady = false;
|
||||
SDLActivity.onNativeSurfaceDestroyed();
|
||||
}
|
||||
|
||||
// Called when the surface is resized
|
||||
@Override
|
||||
public void surfaceChanged(SurfaceHolder holder,
|
||||
int format, int width, int height) {
|
||||
Log.v("SDL", "surfaceChanged()");
|
||||
|
||||
if (SDLActivity.mSingleton == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
mWidth = width;
|
||||
mHeight = height;
|
||||
int nDeviceWidth = width;
|
||||
int nDeviceHeight = height;
|
||||
try
|
||||
{
|
||||
if (Build.VERSION.SDK_INT >= 17) {
|
||||
DisplayMetrics realMetrics = new DisplayMetrics();
|
||||
mDisplay.getRealMetrics( realMetrics );
|
||||
nDeviceWidth = realMetrics.widthPixels;
|
||||
nDeviceHeight = realMetrics.heightPixels;
|
||||
}
|
||||
} catch(Exception ignored) {
|
||||
}
|
||||
|
||||
synchronized(SDLActivity.getContext()) {
|
||||
// In case we're waiting on a size change after going fullscreen, send a notification.
|
||||
SDLActivity.getContext().notifyAll();
|
||||
}
|
||||
|
||||
Log.v("SDL", "Window size: " + width + "x" + height);
|
||||
Log.v("SDL", "Device size: " + nDeviceWidth + "x" + nDeviceHeight);
|
||||
SDLActivity.nativeSetScreenResolution(width, height, nDeviceWidth, nDeviceHeight, mDisplay.getRefreshRate());
|
||||
SDLActivity.onNativeResize();
|
||||
|
||||
// Prevent a screen distortion glitch,
|
||||
// for instance when the device is in Landscape and a Portrait App is resumed.
|
||||
boolean skip = false;
|
||||
int requestedOrientation = SDLActivity.mSingleton.getRequestedOrientation();
|
||||
|
||||
if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT || requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT) {
|
||||
if (mWidth > mHeight) {
|
||||
skip = true;
|
||||
}
|
||||
} else if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE || requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE) {
|
||||
if (mWidth < mHeight) {
|
||||
skip = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Special Patch for Square Resolution: Black Berry Passport
|
||||
if (skip) {
|
||||
double min = Math.min(mWidth, mHeight);
|
||||
double max = Math.max(mWidth, mHeight);
|
||||
|
||||
if (max / min < 1.20) {
|
||||
Log.v("SDL", "Don't skip on such aspect-ratio. Could be a square resolution.");
|
||||
skip = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Don't skip in MultiWindow.
|
||||
if (skip) {
|
||||
if (Build.VERSION.SDK_INT >= 24) {
|
||||
if (SDLActivity.mSingleton.isInMultiWindowMode()) {
|
||||
Log.v("SDL", "Don't skip in Multi-Window");
|
||||
skip = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (skip) {
|
||||
Log.v("SDL", "Skip .. Surface is not ready.");
|
||||
mIsSurfaceReady = false;
|
||||
return;
|
||||
}
|
||||
|
||||
/* If the surface has been previously destroyed by onNativeSurfaceDestroyed, recreate it here */
|
||||
SDLActivity.onNativeSurfaceChanged();
|
||||
|
||||
/* Surface is ready */
|
||||
mIsSurfaceReady = true;
|
||||
|
||||
SDLActivity.mNextNativeState = SDLActivity.NativeState.RESUMED;
|
||||
SDLActivity.handleNativeState();
|
||||
}
|
||||
|
||||
// Key events
|
||||
@Override
|
||||
public boolean onKey(View v, int keyCode, KeyEvent event) {
|
||||
|
||||
int deviceId = event.getDeviceId();
|
||||
int source = event.getSource();
|
||||
|
||||
if (source == InputDevice.SOURCE_UNKNOWN) {
|
||||
InputDevice device = InputDevice.getDevice(deviceId);
|
||||
if (device != null) {
|
||||
source = device.getSources();
|
||||
}
|
||||
}
|
||||
|
||||
// if (event.getAction() == KeyEvent.ACTION_DOWN) {
|
||||
// Log.v("SDL", "key down: " + keyCode + ", deviceId = " + deviceId + ", source = " + source);
|
||||
// } else if (event.getAction() == KeyEvent.ACTION_UP) {
|
||||
// Log.v("SDL", "key up: " + keyCode + ", deviceId = " + deviceId + ", source = " + source);
|
||||
// }
|
||||
|
||||
// Dispatch the different events depending on where they come from
|
||||
// Some SOURCE_JOYSTICK, SOURCE_DPAD or SOURCE_GAMEPAD are also SOURCE_KEYBOARD
|
||||
// So, we try to process them as JOYSTICK/DPAD/GAMEPAD events first, if that fails we try them as KEYBOARD
|
||||
//
|
||||
// Furthermore, it's possible a game controller has SOURCE_KEYBOARD and
|
||||
// SOURCE_JOYSTICK, while its key events arrive from the keyboard source
|
||||
// So, retrieve the device itself and check all of its sources
|
||||
if (SDLControllerManager.isDeviceSDLJoystick(deviceId)) {
|
||||
// Note that we process events with specific key codes here
|
||||
if (event.getAction() == KeyEvent.ACTION_DOWN) {
|
||||
if (SDLControllerManager.onNativePadDown(deviceId, keyCode) == 0) {
|
||||
return true;
|
||||
}
|
||||
} else if (event.getAction() == KeyEvent.ACTION_UP) {
|
||||
if (SDLControllerManager.onNativePadUp(deviceId, keyCode) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((source & InputDevice.SOURCE_KEYBOARD) == InputDevice.SOURCE_KEYBOARD) {
|
||||
if (event.getAction() == KeyEvent.ACTION_DOWN) {
|
||||
if (SDLActivity.isTextInputEvent(event)) {
|
||||
SDLInputConnection.nativeCommitText(String.valueOf((char) event.getUnicodeChar()), 1);
|
||||
}
|
||||
SDLActivity.onNativeKeyDown(keyCode);
|
||||
return true;
|
||||
} else if (event.getAction() == KeyEvent.ACTION_UP) {
|
||||
SDLActivity.onNativeKeyUp(keyCode);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if ((source & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE) {
|
||||
// on some devices key events are sent for mouse BUTTON_BACK/FORWARD presses
|
||||
// they are ignored here because sending them as mouse input to SDL is messy
|
||||
if ((keyCode == KeyEvent.KEYCODE_BACK) || (keyCode == KeyEvent.KEYCODE_FORWARD)) {
|
||||
switch (event.getAction()) {
|
||||
case KeyEvent.ACTION_DOWN:
|
||||
case KeyEvent.ACTION_UP:
|
||||
// mark the event as handled or it will be handled by system
|
||||
// handling KEYCODE_BACK by system will call onBackPressed()
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Touch events
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
/* Ref: http://developer.android.com/training/gestures/multi.html */
|
||||
int touchDevId = event.getDeviceId();
|
||||
final int pointerCount = event.getPointerCount();
|
||||
int action = event.getActionMasked();
|
||||
int pointerFingerId;
|
||||
int i = -1;
|
||||
float x,y,p;
|
||||
|
||||
/*
|
||||
* Prevent id to be -1, since it's used in SDL internal for synthetic events
|
||||
* Appears when using Android emulator, eg:
|
||||
* adb shell input mouse tap 100 100
|
||||
* adb shell input touchscreen tap 100 100
|
||||
*/
|
||||
if (touchDevId < 0) {
|
||||
touchDevId -= 1;
|
||||
}
|
||||
|
||||
// 12290 = Samsung DeX mode desktop mouse
|
||||
// 12290 = 0x3002 = 0x2002 | 0x1002 = SOURCE_MOUSE | SOURCE_TOUCHSCREEN
|
||||
// 0x2 = SOURCE_CLASS_POINTER
|
||||
if (event.getSource() == InputDevice.SOURCE_MOUSE || event.getSource() == (InputDevice.SOURCE_MOUSE | InputDevice.SOURCE_TOUCHSCREEN)) {
|
||||
int mouseButton = 1;
|
||||
try {
|
||||
Object object = event.getClass().getMethod("getButtonState").invoke(event);
|
||||
if (object != null) {
|
||||
mouseButton = (Integer) object;
|
||||
}
|
||||
} catch(Exception ignored) {
|
||||
}
|
||||
|
||||
// We need to check if we're in relative mouse mode and get the axis offset rather than the x/y values
|
||||
// if we are. We'll leverage our existing mouse motion listener
|
||||
SDLGenericMotionListener_API12 motionListener = SDLActivity.getMotionListener();
|
||||
x = motionListener.getEventX(event);
|
||||
y = motionListener.getEventY(event);
|
||||
|
||||
SDLActivity.onNativeMouse(mouseButton, action, x, y, motionListener.inRelativeMode());
|
||||
} else {
|
||||
switch(action) {
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
for (i = 0; i < pointerCount; i++) {
|
||||
pointerFingerId = event.getPointerId(i);
|
||||
x = event.getX(i) / mWidth;
|
||||
y = event.getY(i) / mHeight;
|
||||
p = event.getPressure(i);
|
||||
if (p > 1.0f) {
|
||||
// may be larger than 1.0f on some devices
|
||||
// see the documentation of getPressure(i)
|
||||
p = 1.0f;
|
||||
}
|
||||
SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p);
|
||||
}
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_UP:
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
// Primary pointer up/down, the index is always zero
|
||||
i = 0;
|
||||
/* fallthrough */
|
||||
case MotionEvent.ACTION_POINTER_UP:
|
||||
case MotionEvent.ACTION_POINTER_DOWN:
|
||||
// Non primary pointer up/down
|
||||
if (i == -1) {
|
||||
i = event.getActionIndex();
|
||||
}
|
||||
|
||||
pointerFingerId = event.getPointerId(i);
|
||||
x = event.getX(i) / mWidth;
|
||||
y = event.getY(i) / mHeight;
|
||||
p = event.getPressure(i);
|
||||
if (p > 1.0f) {
|
||||
// may be larger than 1.0f on some devices
|
||||
// see the documentation of getPressure(i)
|
||||
p = 1.0f;
|
||||
}
|
||||
SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p);
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
for (i = 0; i < pointerCount; i++) {
|
||||
pointerFingerId = event.getPointerId(i);
|
||||
x = event.getX(i) / mWidth;
|
||||
y = event.getY(i) / mHeight;
|
||||
p = event.getPressure(i);
|
||||
if (p > 1.0f) {
|
||||
// may be larger than 1.0f on some devices
|
||||
// see the documentation of getPressure(i)
|
||||
p = 1.0f;
|
||||
}
|
||||
SDLActivity.onNativeTouch(touchDevId, pointerFingerId, MotionEvent.ACTION_UP, x, y, p);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Sensor events
|
||||
public void enableSensor(int sensortype, boolean enabled) {
|
||||
// TODO: This uses getDefaultSensor - what if we have >1 accels?
|
||||
if (enabled) {
|
||||
mSensorManager.registerListener(this,
|
||||
mSensorManager.getDefaultSensor(sensortype),
|
||||
SensorManager.SENSOR_DELAY_GAME, null);
|
||||
} else {
|
||||
mSensorManager.unregisterListener(this,
|
||||
mSensorManager.getDefaultSensor(sensortype));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAccuracyChanged(Sensor sensor, int accuracy) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSensorChanged(SensorEvent event) {
|
||||
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
|
||||
|
||||
// Since we may have an orientation set, we won't receive onConfigurationChanged events.
|
||||
// We thus should check here.
|
||||
int newOrientation;
|
||||
|
||||
float x, y;
|
||||
switch (mDisplay.getRotation()) {
|
||||
case Surface.ROTATION_90:
|
||||
x = -event.values[1];
|
||||
y = event.values[0];
|
||||
newOrientation = SDLActivity.SDL_ORIENTATION_LANDSCAPE;
|
||||
break;
|
||||
case Surface.ROTATION_270:
|
||||
x = event.values[1];
|
||||
y = -event.values[0];
|
||||
newOrientation = SDLActivity.SDL_ORIENTATION_LANDSCAPE_FLIPPED;
|
||||
break;
|
||||
case Surface.ROTATION_180:
|
||||
x = -event.values[0];
|
||||
y = -event.values[1];
|
||||
newOrientation = SDLActivity.SDL_ORIENTATION_PORTRAIT_FLIPPED;
|
||||
break;
|
||||
case Surface.ROTATION_0:
|
||||
default:
|
||||
x = event.values[0];
|
||||
y = event.values[1];
|
||||
newOrientation = SDLActivity.SDL_ORIENTATION_PORTRAIT;
|
||||
break;
|
||||
}
|
||||
|
||||
if (newOrientation != SDLActivity.mCurrentOrientation) {
|
||||
SDLActivity.mCurrentOrientation = newOrientation;
|
||||
SDLActivity.onNativeOrientationChanged(newOrientation);
|
||||
}
|
||||
|
||||
SDLActivity.onNativeAccel(-x / SensorManager.GRAVITY_EARTH,
|
||||
y / SensorManager.GRAVITY_EARTH,
|
||||
event.values[2] / SensorManager.GRAVITY_EARTH);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Captured pointer events for API 26.
|
||||
public boolean onCapturedPointerEvent(MotionEvent event)
|
||||
{
|
||||
int action = event.getActionMasked();
|
||||
|
||||
float x, y;
|
||||
switch (action) {
|
||||
case MotionEvent.ACTION_SCROLL:
|
||||
x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
|
||||
y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
|
||||
SDLActivity.onNativeMouse(0, action, x, y, false);
|
||||
return true;
|
||||
|
||||
case MotionEvent.ACTION_HOVER_MOVE:
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
x = event.getX(0);
|
||||
y = event.getY(0);
|
||||
SDLActivity.onNativeMouse(0, action, x, y, true);
|
||||
return true;
|
||||
|
||||
case MotionEvent.ACTION_BUTTON_PRESS:
|
||||
case MotionEvent.ACTION_BUTTON_RELEASE:
|
||||
|
||||
// Change our action value to what SDL's code expects.
|
||||
if (action == MotionEvent.ACTION_BUTTON_PRESS) {
|
||||
action = MotionEvent.ACTION_DOWN;
|
||||
} else { /* MotionEvent.ACTION_BUTTON_RELEASE */
|
||||
action = MotionEvent.ACTION_UP;
|
||||
}
|
||||
|
||||
x = event.getX(0);
|
||||
y = event.getY(0);
|
||||
int button = event.getButtonState();
|
||||
|
||||
SDLActivity.onNativeMouse(button, action, x, y, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* This is a fake invisible editor view that receives the input and defines the
|
||||
* pan&scan region
|
||||
*/
|
||||
|
@ -2278,21 +1916,7 @@ class DummyEdit extends View implements View.OnKeyListener {
|
|||
|
||||
@Override
|
||||
public boolean onKey(View v, int keyCode, KeyEvent event) {
|
||||
/*
|
||||
* This handles the hardware keyboard input
|
||||
*/
|
||||
if (event.getAction() == KeyEvent.ACTION_DOWN) {
|
||||
if (SDLActivity.isTextInputEvent(event)) {
|
||||
ic.commitText(String.valueOf((char) event.getUnicodeChar()), 1);
|
||||
return true;
|
||||
}
|
||||
SDLActivity.onNativeKeyDown(keyCode);
|
||||
return true;
|
||||
} else if (event.getAction() == KeyEvent.ACTION_UP) {
|
||||
SDLActivity.onNativeKeyUp(keyCode);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return SDLActivity.handleKeyEvent(v, keyCode, event, ic);
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -2316,9 +1940,10 @@ class DummyEdit extends View implements View.OnKeyListener {
|
|||
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
|
||||
ic = new SDLInputConnection(this, true);
|
||||
|
||||
outAttrs.inputType = InputType.TYPE_CLASS_TEXT;
|
||||
outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI
|
||||
| EditorInfo.IME_FLAG_NO_FULLSCREEN /* API 11 */;
|
||||
outAttrs.inputType = InputType.TYPE_CLASS_TEXT |
|
||||
InputType.TYPE_TEXT_FLAG_MULTI_LINE;
|
||||
outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI |
|
||||
EditorInfo.IME_FLAG_NO_FULLSCREEN /* API 11 */;
|
||||
|
||||
return ic;
|
||||
}
|
||||
|
@ -2326,9 +1951,17 @@ class DummyEdit extends View implements View.OnKeyListener {
|
|||
|
||||
class SDLInputConnection extends BaseInputConnection {
|
||||
|
||||
protected EditText mEditText;
|
||||
protected String mCommittedText = "";
|
||||
|
||||
public SDLInputConnection(View targetView, boolean fullEditor) {
|
||||
super(targetView, fullEditor);
|
||||
mEditText = new EditText(SDL.getContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Editable getEditable() {
|
||||
return mEditText.getEditableText();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -2351,68 +1984,31 @@ class SDLInputConnection extends BaseInputConnection {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
return super.sendKeyEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean commitText(CharSequence text, int newCursorPosition) {
|
||||
|
||||
/* Generate backspaces for the text we're going to replace */
|
||||
final Editable content = getEditable();
|
||||
if (content != null) {
|
||||
int a = getComposingSpanStart(content);
|
||||
int b = getComposingSpanEnd(content);
|
||||
if (a == -1 || b == -1) {
|
||||
a = Selection.getSelectionStart(content);
|
||||
b = Selection.getSelectionEnd(content);
|
||||
if (!super.commitText(text, newCursorPosition)) {
|
||||
return false;
|
||||
}
|
||||
if (a < 0) a = 0;
|
||||
if (b < 0) b = 0;
|
||||
if (b < a) {
|
||||
int tmp = a;
|
||||
a = b;
|
||||
b = tmp;
|
||||
}
|
||||
int backspaces = (b - a);
|
||||
|
||||
for (int i = 0; i < backspaces; i++) {
|
||||
nativeGenerateScancodeForUnichar('\b');
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < text.length(); i++) {
|
||||
char c = text.charAt(i);
|
||||
if (c == '\n') {
|
||||
if (SDLActivity.onNativeSoftReturnKey()) {
|
||||
updateText();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
nativeGenerateScancodeForUnichar(c);
|
||||
}
|
||||
|
||||
SDLInputConnection.nativeCommitText(text.toString(), newCursorPosition);
|
||||
|
||||
return super.commitText(text, newCursorPosition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setComposingText(CharSequence text, int newCursorPosition) {
|
||||
|
||||
nativeSetComposingText(text.toString(), newCursorPosition);
|
||||
|
||||
return super.setComposingText(text, newCursorPosition);
|
||||
if (!super.setComposingText(text, newCursorPosition)) {
|
||||
return false;
|
||||
}
|
||||
updateText();
|
||||
return true;
|
||||
}
|
||||
|
||||
public static native void nativeCommitText(String text, int newCursorPosition);
|
||||
|
||||
public native void nativeGenerateScancodeForUnichar(char c);
|
||||
|
||||
public native void nativeSetComposingText(String text, int newCursorPosition);
|
||||
|
||||
@Override
|
||||
public boolean deleteSurroundingText(int beforeLength, int afterLength) {
|
||||
// Workaround to capture backspace key. Ref: http://stackoverflow.com/questions/14560344/android-backspace-in-webview-baseinputconnection
|
||||
if (Build.VERSION.SDK_INT <= 29 /* Android 10.0 (Q) */) {
|
||||
// Workaround to capture backspace key. Ref: http://stackoverflow.com/questions>/14560344/android-backspace-in-webview-baseinputconnection
|
||||
// and https://bugzilla.libsdl.org/show_bug.cgi?id=2265
|
||||
if (beforeLength > 0 && afterLength == 0) {
|
||||
// backspace(s)
|
||||
|
@ -2421,9 +2017,63 @@ class SDLInputConnection extends BaseInputConnection {
|
|||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.deleteSurroundingText(beforeLength, afterLength);
|
||||
}
|
||||
|
||||
if (!super.deleteSurroundingText(beforeLength, afterLength)) {
|
||||
return false;
|
||||
}
|
||||
updateText();
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void updateText() {
|
||||
final Editable content = getEditable();
|
||||
if (content == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String text = content.toString();
|
||||
int compareLength = Math.min(text.length(), mCommittedText.length());
|
||||
int matchLength, offset;
|
||||
|
||||
/* Backspace over characters that are no longer in the string */
|
||||
for (matchLength = 0; matchLength < compareLength; ) {
|
||||
int codePoint = mCommittedText.codePointAt(matchLength);
|
||||
if (codePoint != text.codePointAt(matchLength)) {
|
||||
break;
|
||||
}
|
||||
matchLength += Character.charCount(codePoint);
|
||||
}
|
||||
/* FIXME: This doesn't handle graphemes, like '🌬️' */
|
||||
for (offset = matchLength; offset < mCommittedText.length(); ) {
|
||||
int codePoint = mCommittedText.codePointAt(offset);
|
||||
nativeGenerateScancodeForUnichar('\b');
|
||||
offset += Character.charCount(codePoint);
|
||||
}
|
||||
|
||||
if (matchLength < text.length()) {
|
||||
String pendingText = text.subSequence(matchLength, text.length()).toString();
|
||||
for (offset = 0; offset < pendingText.length(); ) {
|
||||
int codePoint = pendingText.codePointAt(offset);
|
||||
if (codePoint == '\n') {
|
||||
if (SDLActivity.onNativeSoftReturnKey()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* Higher code points don't generate simulated scancodes */
|
||||
if (codePoint < 128) {
|
||||
nativeGenerateScancodeForUnichar((char)codePoint);
|
||||
}
|
||||
offset += Character.charCount(codePoint);
|
||||
}
|
||||
SDLInputConnection.nativeCommitText(pendingText, 0);
|
||||
}
|
||||
mCommittedText = text;
|
||||
}
|
||||
|
||||
public static native void nativeCommitText(String text, int newCursorPosition);
|
||||
|
||||
public static native void nativeGenerateScancodeForUnichar(char c);
|
||||
}
|
||||
|
||||
class SDLClipboardHandler implements
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
package org.libsdl.app;
|
||||
|
||||
import android.content.Context;
|
||||
import android.media.AudioDeviceCallback;
|
||||
import android.media.AudioDeviceInfo;
|
||||
import android.media.AudioFormat;
|
||||
import android.media.AudioManager;
|
||||
import android.media.AudioRecord;
|
||||
|
@ -8,16 +11,49 @@ import android.media.MediaRecorder;
|
|||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
public class SDLAudioManager
|
||||
{
|
||||
import java.util.Arrays;
|
||||
|
||||
public class SDLAudioManager {
|
||||
protected static final String TAG = "SDLAudio";
|
||||
|
||||
protected static AudioTrack mAudioTrack;
|
||||
protected static AudioRecord mAudioRecord;
|
||||
protected static Context mContext;
|
||||
|
||||
private static final int[] NO_DEVICES = {};
|
||||
|
||||
private static AudioDeviceCallback mAudioDeviceCallback;
|
||||
|
||||
public static void initialize() {
|
||||
mAudioTrack = null;
|
||||
mAudioRecord = null;
|
||||
mAudioDeviceCallback = null;
|
||||
|
||||
if(Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */)
|
||||
{
|
||||
mAudioDeviceCallback = new AudioDeviceCallback() {
|
||||
@Override
|
||||
public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
|
||||
Arrays.stream(addedDevices).forEach(deviceInfo -> addAudioDevice(deviceInfo.isSink(), deviceInfo.getId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) {
|
||||
Arrays.stream(removedDevices).forEach(deviceInfo -> removeAudioDevice(deviceInfo.isSink(), deviceInfo.getId()));
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static void setContext(Context context) {
|
||||
mContext = context;
|
||||
if (context != null) {
|
||||
registerAudioDeviceCallback();
|
||||
}
|
||||
}
|
||||
|
||||
public static void release(Context context) {
|
||||
unregisterAudioDeviceCallback(context);
|
||||
}
|
||||
|
||||
// Audio
|
||||
|
@ -35,7 +71,7 @@ public class SDLAudioManager
|
|||
}
|
||||
}
|
||||
|
||||
protected static int[] open(boolean isCapture, int sampleRate, int audioFormat, int desiredChannels, int desiredFrames) {
|
||||
protected static int[] open(boolean isCapture, int sampleRate, int audioFormat, int desiredChannels, int desiredFrames, int deviceId) {
|
||||
int channelConfig;
|
||||
int sampleSize;
|
||||
int frameSize;
|
||||
|
@ -43,14 +79,14 @@ public class SDLAudioManager
|
|||
Log.v(TAG, "Opening " + (isCapture ? "capture" : "playback") + ", requested " + desiredFrames + " frames of " + desiredChannels + " channel " + getAudioFormatString(audioFormat) + " audio at " + sampleRate + " Hz");
|
||||
|
||||
/* On older devices let's use known good settings */
|
||||
if (Build.VERSION.SDK_INT < 21) {
|
||||
if (Build.VERSION.SDK_INT < 21 /* Android 5.0 (LOLLIPOP) */) {
|
||||
if (desiredChannels > 2) {
|
||||
desiredChannels = 2;
|
||||
}
|
||||
}
|
||||
|
||||
/* AudioTrack has sample rate limitation of 48000 (fixed in 5.0.2) */
|
||||
if (Build.VERSION.SDK_INT < 22) {
|
||||
if (Build.VERSION.SDK_INT < 22 /* Android 5.1 (LOLLIPOP_MR1) */) {
|
||||
if (sampleRate < 8000) {
|
||||
sampleRate = 8000;
|
||||
} else if (sampleRate > 48000) {
|
||||
|
@ -59,7 +95,7 @@ public class SDLAudioManager
|
|||
}
|
||||
|
||||
if (audioFormat == AudioFormat.ENCODING_PCM_FLOAT) {
|
||||
int minSDKVersion = (isCapture ? 23 : 21);
|
||||
int minSDKVersion = (isCapture ? 23 /* Android 6.0 (M) */ : 21 /* Android 5.0 (LOLLIPOP) */);
|
||||
if (Build.VERSION.SDK_INT < minSDKVersion) {
|
||||
audioFormat = AudioFormat.ENCODING_PCM_16BIT;
|
||||
}
|
||||
|
@ -120,7 +156,7 @@ public class SDLAudioManager
|
|||
channelConfig = AudioFormat.CHANNEL_OUT_5POINT1 | AudioFormat.CHANNEL_OUT_BACK_CENTER;
|
||||
break;
|
||||
case 8:
|
||||
if (Build.VERSION.SDK_INT >= 23) {
|
||||
if (Build.VERSION.SDK_INT >= 23 /* Android 6.0 (M) */) {
|
||||
channelConfig = AudioFormat.CHANNEL_OUT_7POINT1_SURROUND;
|
||||
} else {
|
||||
Log.v(TAG, "Requested " + desiredChannels + " channels, getting 5.1 surround");
|
||||
|
@ -201,6 +237,10 @@ public class SDLAudioManager
|
|||
return null;
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */ && deviceId != 0) {
|
||||
mAudioRecord.setPreferredDevice(getOutputAudioDeviceInfo(deviceId));
|
||||
}
|
||||
|
||||
mAudioRecord.startRecording();
|
||||
}
|
||||
|
||||
|
@ -224,6 +264,10 @@ public class SDLAudioManager
|
|||
return null;
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */ && deviceId != 0) {
|
||||
mAudioTrack.setPreferredDevice(getInputAudioDeviceInfo(deviceId));
|
||||
}
|
||||
|
||||
mAudioTrack.play();
|
||||
}
|
||||
|
||||
|
@ -238,11 +282,73 @@ public class SDLAudioManager
|
|||
return results;
|
||||
}
|
||||
|
||||
private static AudioDeviceInfo getInputAudioDeviceInfo(int deviceId) {
|
||||
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
|
||||
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
|
||||
return Arrays.stream(audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS))
|
||||
.filter(deviceInfo -> deviceInfo.getId() == deviceId)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static AudioDeviceInfo getOutputAudioDeviceInfo(int deviceId) {
|
||||
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
|
||||
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
|
||||
return Arrays.stream(audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS))
|
||||
.filter(deviceInfo -> deviceInfo.getId() == deviceId)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static void registerAudioDeviceCallback() {
|
||||
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
|
||||
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
|
||||
audioManager.registerAudioDeviceCallback(mAudioDeviceCallback, null);
|
||||
}
|
||||
}
|
||||
|
||||
private static void unregisterAudioDeviceCallback(Context context) {
|
||||
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
|
||||
AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
|
||||
audioManager.unregisterAudioDeviceCallback(mAudioDeviceCallback);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called by SDL using JNI.
|
||||
*/
|
||||
public static int[] audioOpen(int sampleRate, int audioFormat, int desiredChannels, int desiredFrames) {
|
||||
return open(false, sampleRate, audioFormat, desiredChannels, desiredFrames);
|
||||
public static int[] getAudioOutputDevices() {
|
||||
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
|
||||
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
|
||||
return Arrays.stream(audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)).mapToInt(AudioDeviceInfo::getId).toArray();
|
||||
} else {
|
||||
return NO_DEVICES;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called by SDL using JNI.
|
||||
*/
|
||||
public static int[] getAudioInputDevices() {
|
||||
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
|
||||
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
|
||||
return Arrays.stream(audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)).mapToInt(AudioDeviceInfo::getId).toArray();
|
||||
} else {
|
||||
return NO_DEVICES;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called by SDL using JNI.
|
||||
*/
|
||||
public static int[] audioOpen(int sampleRate, int audioFormat, int desiredChannels, int desiredFrames, int deviceId) {
|
||||
return open(false, sampleRate, audioFormat, desiredChannels, desiredFrames, deviceId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -254,6 +360,11 @@ public class SDLAudioManager
|
|||
return;
|
||||
}
|
||||
|
||||
if (android.os.Build.VERSION.SDK_INT < 21 /* Android 5.0 (LOLLIPOP) */) {
|
||||
Log.e(TAG, "Attempted to make an incompatible audio call with uninitialized audio! (floating-point output is supported since Android 5.0 Lollipop)");
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < buffer.length;) {
|
||||
int result = mAudioTrack.write(buffer, i, buffer.length - i, AudioTrack.WRITE_BLOCKING);
|
||||
if (result > 0) {
|
||||
|
@ -326,18 +437,22 @@ public class SDLAudioManager
|
|||
/**
|
||||
* This method is called by SDL using JNI.
|
||||
*/
|
||||
public static int[] captureOpen(int sampleRate, int audioFormat, int desiredChannels, int desiredFrames) {
|
||||
return open(true, sampleRate, audioFormat, desiredChannels, desiredFrames);
|
||||
public static int[] captureOpen(int sampleRate, int audioFormat, int desiredChannels, int desiredFrames, int deviceId) {
|
||||
return open(true, sampleRate, audioFormat, desiredChannels, desiredFrames, deviceId);
|
||||
}
|
||||
|
||||
/** This method is called by SDL using JNI. */
|
||||
public static int captureReadFloatBuffer(float[] buffer, boolean blocking) {
|
||||
if (Build.VERSION.SDK_INT < 23 /* Android 6.0 (M) */) {
|
||||
return 0;
|
||||
} else {
|
||||
return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
|
||||
}
|
||||
}
|
||||
|
||||
/** This method is called by SDL using JNI. */
|
||||
public static int captureReadShortBuffer(short[] buffer, boolean blocking) {
|
||||
if (Build.VERSION.SDK_INT < 23) {
|
||||
if (Build.VERSION.SDK_INT < 23 /* Android 6.0 (M) */) {
|
||||
return mAudioRecord.read(buffer, 0, buffer.length);
|
||||
} else {
|
||||
return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
|
||||
|
@ -346,7 +461,7 @@ public class SDLAudioManager
|
|||
|
||||
/** This method is called by SDL using JNI. */
|
||||
public static int captureReadByteBuffer(byte[] buffer, boolean blocking) {
|
||||
if (Build.VERSION.SDK_INT < 23) {
|
||||
if (Build.VERSION.SDK_INT < 23 /* Android 6.0 (M) */) {
|
||||
return mAudioRecord.read(buffer, 0, buffer.length);
|
||||
} else {
|
||||
return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
|
||||
|
@ -391,4 +506,9 @@ public class SDLAudioManager
|
|||
}
|
||||
|
||||
public static native int nativeSetupJNI();
|
||||
|
||||
public static native void removeAudioDevice(boolean isCapture, int deviceId);
|
||||
|
||||
public static native void addAudioDevice(boolean isCapture, int deviceId);
|
||||
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ public class SDLControllerManager
|
|||
public static native int nativeAddJoystick(int device_id, String name, String desc,
|
||||
int vendor_id, int product_id,
|
||||
boolean is_accelerometer, int button_mask,
|
||||
int naxes, int nhats, int nballs);
|
||||
int naxes, int axis_mask, int nhats, int nballs);
|
||||
public static native int nativeRemoveJoystick(int device_id);
|
||||
public static native int nativeAddHaptic(int device_id, String name);
|
||||
public static native int nativeRemoveHaptic(int device_id);
|
||||
|
@ -42,7 +42,7 @@ public class SDLControllerManager
|
|||
|
||||
public static void initialize() {
|
||||
if (mJoystickHandler == null) {
|
||||
if (Build.VERSION.SDK_INT >= 19) {
|
||||
if (Build.VERSION.SDK_INT >= 19 /* Android 4.4 (KITKAT) */) {
|
||||
mJoystickHandler = new SDLJoystickHandler_API19();
|
||||
} else {
|
||||
mJoystickHandler = new SDLJoystickHandler_API16();
|
||||
|
@ -50,7 +50,7 @@ public class SDLControllerManager
|
|||
}
|
||||
|
||||
if (mHapticHandler == null) {
|
||||
if (Build.VERSION.SDK_INT >= 26) {
|
||||
if (Build.VERSION.SDK_INT >= 26 /* Android 8.0 (O) */) {
|
||||
mHapticHandler = new SDLHapticHandler_API26();
|
||||
} else {
|
||||
mHapticHandler = new SDLHapticHandler();
|
||||
|
@ -168,6 +168,32 @@ class SDLJoystickHandler_API16 extends SDLJoystickHandler {
|
|||
arg1Axis = MotionEvent.AXIS_GAS;
|
||||
}
|
||||
|
||||
// Make sure the AXIS_Z is sorted between AXIS_RY and AXIS_RZ.
|
||||
// This is because the usual pairing are:
|
||||
// - AXIS_X + AXIS_Y (left stick).
|
||||
// - AXIS_RX, AXIS_RY (sometimes the right stick, sometimes triggers).
|
||||
// - AXIS_Z, AXIS_RZ (sometimes the right stick, sometimes triggers).
|
||||
// This sorts the axes in the above order, which tends to be correct
|
||||
// for Xbox-ish game pads that have the right stick on RX/RY and the
|
||||
// triggers on Z/RZ.
|
||||
//
|
||||
// Gamepads that don't have AXIS_Z/AXIS_RZ but use
|
||||
// AXIS_LTRIGGER/AXIS_RTRIGGER are unaffected by this.
|
||||
//
|
||||
// References:
|
||||
// - https://developer.android.com/develop/ui/views/touch-and-input/game-controllers/controller-input
|
||||
// - https://www.kernel.org/doc/html/latest/input/gamepad.html
|
||||
if (arg0Axis == MotionEvent.AXIS_Z) {
|
||||
arg0Axis = MotionEvent.AXIS_RZ - 1;
|
||||
} else if (arg0Axis > MotionEvent.AXIS_Z && arg0Axis < MotionEvent.AXIS_RZ) {
|
||||
--arg0Axis;
|
||||
}
|
||||
if (arg1Axis == MotionEvent.AXIS_Z) {
|
||||
arg1Axis = MotionEvent.AXIS_RZ - 1;
|
||||
} else if (arg1Axis > MotionEvent.AXIS_Z && arg1Axis < MotionEvent.AXIS_RZ) {
|
||||
--arg1Axis;
|
||||
}
|
||||
|
||||
return arg0Axis - arg1Axis;
|
||||
}
|
||||
}
|
||||
|
@ -210,7 +236,7 @@ class SDLJoystickHandler_API16 extends SDLJoystickHandler {
|
|||
mJoysticks.add(joystick);
|
||||
SDLControllerManager.nativeAddJoystick(joystick.device_id, joystick.name, joystick.desc,
|
||||
getVendorId(joystickDevice), getProductId(joystickDevice), false,
|
||||
getButtonMask(joystickDevice), joystick.axes.size(), joystick.hats.size()/2, 0);
|
||||
getButtonMask(joystickDevice), joystick.axes.size(), getAxisMask(joystick.axes), joystick.hats.size()/2, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -291,6 +317,9 @@ class SDLJoystickHandler_API16 extends SDLJoystickHandler {
|
|||
public int getVendorId(InputDevice joystickDevice) {
|
||||
return 0;
|
||||
}
|
||||
public int getAxisMask(List<InputDevice.MotionRange> ranges) {
|
||||
return -1;
|
||||
}
|
||||
public int getButtonMask(InputDevice joystickDevice) {
|
||||
return -1;
|
||||
}
|
||||
|
@ -308,6 +337,43 @@ class SDLJoystickHandler_API19 extends SDLJoystickHandler_API16 {
|
|||
return joystickDevice.getVendorId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAxisMask(List<InputDevice.MotionRange> ranges) {
|
||||
// For compatibility, keep computing the axis mask like before,
|
||||
// only really distinguishing 2, 4 and 6 axes.
|
||||
int axis_mask = 0;
|
||||
if (ranges.size() >= 2) {
|
||||
// ((1 << SDL_GAMEPAD_AXIS_LEFTX) | (1 << SDL_GAMEPAD_AXIS_LEFTY))
|
||||
axis_mask |= 0x0003;
|
||||
}
|
||||
if (ranges.size() >= 4) {
|
||||
// ((1 << SDL_GAMEPAD_AXIS_RIGHTX) | (1 << SDL_GAMEPAD_AXIS_RIGHTY))
|
||||
axis_mask |= 0x000c;
|
||||
}
|
||||
if (ranges.size() >= 6) {
|
||||
// ((1 << SDL_GAMEPAD_AXIS_LEFT_TRIGGER) | (1 << SDL_GAMEPAD_AXIS_RIGHT_TRIGGER))
|
||||
axis_mask |= 0x0030;
|
||||
}
|
||||
// Also add an indicator bit for whether the sorting order has changed.
|
||||
// This serves to disable outdated gamecontrollerdb.txt mappings.
|
||||
boolean have_z = false;
|
||||
boolean have_past_z_before_rz = false;
|
||||
for (InputDevice.MotionRange range : ranges) {
|
||||
int axis = range.getAxis();
|
||||
if (axis == MotionEvent.AXIS_Z) {
|
||||
have_z = true;
|
||||
} else if (axis > MotionEvent.AXIS_Z && axis < MotionEvent.AXIS_RZ) {
|
||||
have_past_z_before_rz = true;
|
||||
}
|
||||
}
|
||||
if (have_z && have_past_z_before_rz) {
|
||||
// If both these exist, the compare() function changed sorting order.
|
||||
// Set a bit to indicate this fact.
|
||||
axis_mask |= 0x8000;
|
||||
}
|
||||
return axis_mask;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getButtonMask(InputDevice joystickDevice) {
|
||||
int button_mask = 0;
|
||||
|
@ -743,7 +809,7 @@ class SDLGenericMotionListener_API26 extends SDLGenericMotionListener_API24 {
|
|||
|
||||
@Override
|
||||
public boolean supportsRelativeMouse() {
|
||||
return (!SDLActivity.isDeXMode() || (Build.VERSION.SDK_INT >= 27));
|
||||
return (!SDLActivity.isDeXMode() || Build.VERSION.SDK_INT >= 27 /* Android 8.1 (O_MR1) */);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -753,7 +819,7 @@ class SDLGenericMotionListener_API26 extends SDLGenericMotionListener_API24 {
|
|||
|
||||
@Override
|
||||
public boolean setRelativeMouseEnabled(boolean enabled) {
|
||||
if (!SDLActivity.isDeXMode() || (Build.VERSION.SDK_INT >= 27)) {
|
||||
if (!SDLActivity.isDeXMode() || Build.VERSION.SDK_INT >= 27 /* Android 8.1 (O_MR1) */) {
|
||||
if (enabled) {
|
||||
SDLActivity.getContentView().requestPointerCapture();
|
||||
} else {
|
||||
|
|
405
android/app/src/main/java/org/libsdl/app/SDLSurface.java
Normal file
405
android/app/src/main/java/org/libsdl/app/SDLSurface.java
Normal file
|
@ -0,0 +1,405 @@
|
|||
package org.libsdl.app;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.hardware.Sensor;
|
||||
import android.hardware.SensorEvent;
|
||||
import android.hardware.SensorEventListener;
|
||||
import android.hardware.SensorManager;
|
||||
import android.os.Build;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
import android.view.Display;
|
||||
import android.view.InputDevice;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.SurfaceView;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
|
||||
|
||||
/**
|
||||
SDLSurface. This is what we draw on, so we need to know when it's created
|
||||
in order to do anything useful.
|
||||
|
||||
Because of this, that's where we set up the SDL thread
|
||||
*/
|
||||
public class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
|
||||
View.OnKeyListener, View.OnTouchListener, SensorEventListener {
|
||||
|
||||
// Sensors
|
||||
protected SensorManager mSensorManager;
|
||||
protected Display mDisplay;
|
||||
|
||||
// Keep track of the surface size to normalize touch events
|
||||
protected float mWidth, mHeight;
|
||||
|
||||
// Is SurfaceView ready for rendering
|
||||
public boolean mIsSurfaceReady;
|
||||
|
||||
// Startup
|
||||
public SDLSurface(Context context) {
|
||||
super(context);
|
||||
getHolder().addCallback(this);
|
||||
|
||||
setFocusable(true);
|
||||
setFocusableInTouchMode(true);
|
||||
requestFocus();
|
||||
setOnKeyListener(this);
|
||||
setOnTouchListener(this);
|
||||
|
||||
mDisplay = ((WindowManager)context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
|
||||
mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
|
||||
|
||||
setOnGenericMotionListener(SDLActivity.getMotionListener());
|
||||
|
||||
// Some arbitrary defaults to avoid a potential division by zero
|
||||
mWidth = 1.0f;
|
||||
mHeight = 1.0f;
|
||||
|
||||
mIsSurfaceReady = false;
|
||||
}
|
||||
|
||||
public void handlePause() {
|
||||
enableSensor(Sensor.TYPE_ACCELEROMETER, false);
|
||||
}
|
||||
|
||||
public void handleResume() {
|
||||
setFocusable(true);
|
||||
setFocusableInTouchMode(true);
|
||||
requestFocus();
|
||||
setOnKeyListener(this);
|
||||
setOnTouchListener(this);
|
||||
enableSensor(Sensor.TYPE_ACCELEROMETER, true);
|
||||
}
|
||||
|
||||
public Surface getNativeSurface() {
|
||||
return getHolder().getSurface();
|
||||
}
|
||||
|
||||
// Called when we have a valid drawing surface
|
||||
@Override
|
||||
public void surfaceCreated(SurfaceHolder holder) {
|
||||
Log.v("SDL", "surfaceCreated()");
|
||||
SDLActivity.onNativeSurfaceCreated();
|
||||
}
|
||||
|
||||
// Called when we lose the surface
|
||||
@Override
|
||||
public void surfaceDestroyed(SurfaceHolder holder) {
|
||||
Log.v("SDL", "surfaceDestroyed()");
|
||||
|
||||
// Transition to pause, if needed
|
||||
SDLActivity.mNextNativeState = SDLActivity.NativeState.PAUSED;
|
||||
SDLActivity.handleNativeState();
|
||||
|
||||
mIsSurfaceReady = false;
|
||||
SDLActivity.onNativeSurfaceDestroyed();
|
||||
}
|
||||
|
||||
// Called when the surface is resized
|
||||
@Override
|
||||
public void surfaceChanged(SurfaceHolder holder,
|
||||
int format, int width, int height) {
|
||||
Log.v("SDL", "surfaceChanged()");
|
||||
|
||||
if (SDLActivity.mSingleton == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
mWidth = width;
|
||||
mHeight = height;
|
||||
int nDeviceWidth = width;
|
||||
int nDeviceHeight = height;
|
||||
try
|
||||
{
|
||||
if (Build.VERSION.SDK_INT >= 17 /* Android 4.2 (JELLY_BEAN_MR1) */) {
|
||||
DisplayMetrics realMetrics = new DisplayMetrics();
|
||||
mDisplay.getRealMetrics( realMetrics );
|
||||
nDeviceWidth = realMetrics.widthPixels;
|
||||
nDeviceHeight = realMetrics.heightPixels;
|
||||
}
|
||||
} catch(Exception ignored) {
|
||||
}
|
||||
|
||||
synchronized(SDLActivity.getContext()) {
|
||||
// In case we're waiting on a size change after going fullscreen, send a notification.
|
||||
SDLActivity.getContext().notifyAll();
|
||||
}
|
||||
|
||||
Log.v("SDL", "Window size: " + width + "x" + height);
|
||||
Log.v("SDL", "Device size: " + nDeviceWidth + "x" + nDeviceHeight);
|
||||
SDLActivity.nativeSetScreenResolution(width, height, nDeviceWidth, nDeviceHeight, mDisplay.getRefreshRate());
|
||||
SDLActivity.onNativeResize();
|
||||
|
||||
// Prevent a screen distortion glitch,
|
||||
// for instance when the device is in Landscape and a Portrait App is resumed.
|
||||
boolean skip = false;
|
||||
int requestedOrientation = SDLActivity.mSingleton.getRequestedOrientation();
|
||||
|
||||
if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT || requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT) {
|
||||
if (mWidth > mHeight) {
|
||||
skip = true;
|
||||
}
|
||||
} else if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE || requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE) {
|
||||
if (mWidth < mHeight) {
|
||||
skip = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Special Patch for Square Resolution: Black Berry Passport
|
||||
if (skip) {
|
||||
double min = Math.min(mWidth, mHeight);
|
||||
double max = Math.max(mWidth, mHeight);
|
||||
|
||||
if (max / min < 1.20) {
|
||||
Log.v("SDL", "Don't skip on such aspect-ratio. Could be a square resolution.");
|
||||
skip = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Don't skip in MultiWindow.
|
||||
if (skip) {
|
||||
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
|
||||
if (SDLActivity.mSingleton.isInMultiWindowMode()) {
|
||||
Log.v("SDL", "Don't skip in Multi-Window");
|
||||
skip = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (skip) {
|
||||
Log.v("SDL", "Skip .. Surface is not ready.");
|
||||
mIsSurfaceReady = false;
|
||||
return;
|
||||
}
|
||||
|
||||
/* If the surface has been previously destroyed by onNativeSurfaceDestroyed, recreate it here */
|
||||
SDLActivity.onNativeSurfaceChanged();
|
||||
|
||||
/* Surface is ready */
|
||||
mIsSurfaceReady = true;
|
||||
|
||||
SDLActivity.mNextNativeState = SDLActivity.NativeState.RESUMED;
|
||||
SDLActivity.handleNativeState();
|
||||
}
|
||||
|
||||
// Key events
|
||||
@Override
|
||||
public boolean onKey(View v, int keyCode, KeyEvent event) {
|
||||
return SDLActivity.handleKeyEvent(v, keyCode, event, null);
|
||||
}
|
||||
|
||||
// Touch events
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
/* Ref: http://developer.android.com/training/gestures/multi.html */
|
||||
int touchDevId = event.getDeviceId();
|
||||
final int pointerCount = event.getPointerCount();
|
||||
int action = event.getActionMasked();
|
||||
int pointerFingerId;
|
||||
int i = -1;
|
||||
float x,y,p;
|
||||
|
||||
/*
|
||||
* Prevent id to be -1, since it's used in SDL internal for synthetic events
|
||||
* Appears when using Android emulator, eg:
|
||||
* adb shell input mouse tap 100 100
|
||||
* adb shell input touchscreen tap 100 100
|
||||
*/
|
||||
if (touchDevId < 0) {
|
||||
touchDevId -= 1;
|
||||
}
|
||||
|
||||
// 12290 = Samsung DeX mode desktop mouse
|
||||
// 12290 = 0x3002 = 0x2002 | 0x1002 = SOURCE_MOUSE | SOURCE_TOUCHSCREEN
|
||||
// 0x2 = SOURCE_CLASS_POINTER
|
||||
if (event.getSource() == InputDevice.SOURCE_MOUSE || event.getSource() == (InputDevice.SOURCE_MOUSE | InputDevice.SOURCE_TOUCHSCREEN)) {
|
||||
int mouseButton = 1;
|
||||
try {
|
||||
Object object = event.getClass().getMethod("getButtonState").invoke(event);
|
||||
if (object != null) {
|
||||
mouseButton = (Integer) object;
|
||||
}
|
||||
} catch(Exception ignored) {
|
||||
}
|
||||
|
||||
// We need to check if we're in relative mouse mode and get the axis offset rather than the x/y values
|
||||
// if we are. We'll leverage our existing mouse motion listener
|
||||
SDLGenericMotionListener_API12 motionListener = SDLActivity.getMotionListener();
|
||||
x = motionListener.getEventX(event);
|
||||
y = motionListener.getEventY(event);
|
||||
|
||||
SDLActivity.onNativeMouse(mouseButton, action, x, y, motionListener.inRelativeMode());
|
||||
} else {
|
||||
switch(action) {
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
for (i = 0; i < pointerCount; i++) {
|
||||
pointerFingerId = event.getPointerId(i);
|
||||
x = event.getX(i) / mWidth;
|
||||
y = event.getY(i) / mHeight;
|
||||
p = event.getPressure(i);
|
||||
if (p > 1.0f) {
|
||||
// may be larger than 1.0f on some devices
|
||||
// see the documentation of getPressure(i)
|
||||
p = 1.0f;
|
||||
}
|
||||
SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p);
|
||||
}
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_UP:
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
// Primary pointer up/down, the index is always zero
|
||||
i = 0;
|
||||
/* fallthrough */
|
||||
case MotionEvent.ACTION_POINTER_UP:
|
||||
case MotionEvent.ACTION_POINTER_DOWN:
|
||||
// Non primary pointer up/down
|
||||
if (i == -1) {
|
||||
i = event.getActionIndex();
|
||||
}
|
||||
|
||||
pointerFingerId = event.getPointerId(i);
|
||||
x = event.getX(i) / mWidth;
|
||||
y = event.getY(i) / mHeight;
|
||||
p = event.getPressure(i);
|
||||
if (p > 1.0f) {
|
||||
// may be larger than 1.0f on some devices
|
||||
// see the documentation of getPressure(i)
|
||||
p = 1.0f;
|
||||
}
|
||||
SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p);
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
for (i = 0; i < pointerCount; i++) {
|
||||
pointerFingerId = event.getPointerId(i);
|
||||
x = event.getX(i) / mWidth;
|
||||
y = event.getY(i) / mHeight;
|
||||
p = event.getPressure(i);
|
||||
if (p > 1.0f) {
|
||||
// may be larger than 1.0f on some devices
|
||||
// see the documentation of getPressure(i)
|
||||
p = 1.0f;
|
||||
}
|
||||
SDLActivity.onNativeTouch(touchDevId, pointerFingerId, MotionEvent.ACTION_UP, x, y, p);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Sensor events
|
||||
public void enableSensor(int sensortype, boolean enabled) {
|
||||
// TODO: This uses getDefaultSensor - what if we have >1 accels?
|
||||
if (enabled) {
|
||||
mSensorManager.registerListener(this,
|
||||
mSensorManager.getDefaultSensor(sensortype),
|
||||
SensorManager.SENSOR_DELAY_GAME, null);
|
||||
} else {
|
||||
mSensorManager.unregisterListener(this,
|
||||
mSensorManager.getDefaultSensor(sensortype));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAccuracyChanged(Sensor sensor, int accuracy) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSensorChanged(SensorEvent event) {
|
||||
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
|
||||
|
||||
// Since we may have an orientation set, we won't receive onConfigurationChanged events.
|
||||
// We thus should check here.
|
||||
int newOrientation;
|
||||
|
||||
float x, y;
|
||||
switch (mDisplay.getRotation()) {
|
||||
case Surface.ROTATION_90:
|
||||
x = -event.values[1];
|
||||
y = event.values[0];
|
||||
newOrientation = SDLActivity.SDL_ORIENTATION_LANDSCAPE;
|
||||
break;
|
||||
case Surface.ROTATION_270:
|
||||
x = event.values[1];
|
||||
y = -event.values[0];
|
||||
newOrientation = SDLActivity.SDL_ORIENTATION_LANDSCAPE_FLIPPED;
|
||||
break;
|
||||
case Surface.ROTATION_180:
|
||||
x = -event.values[0];
|
||||
y = -event.values[1];
|
||||
newOrientation = SDLActivity.SDL_ORIENTATION_PORTRAIT_FLIPPED;
|
||||
break;
|
||||
case Surface.ROTATION_0:
|
||||
default:
|
||||
x = event.values[0];
|
||||
y = event.values[1];
|
||||
newOrientation = SDLActivity.SDL_ORIENTATION_PORTRAIT;
|
||||
break;
|
||||
}
|
||||
|
||||
if (newOrientation != SDLActivity.mCurrentOrientation) {
|
||||
SDLActivity.mCurrentOrientation = newOrientation;
|
||||
SDLActivity.onNativeOrientationChanged(newOrientation);
|
||||
}
|
||||
|
||||
SDLActivity.onNativeAccel(-x / SensorManager.GRAVITY_EARTH,
|
||||
y / SensorManager.GRAVITY_EARTH,
|
||||
event.values[2] / SensorManager.GRAVITY_EARTH);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Captured pointer events for API 26.
|
||||
public boolean onCapturedPointerEvent(MotionEvent event)
|
||||
{
|
||||
int action = event.getActionMasked();
|
||||
|
||||
float x, y;
|
||||
switch (action) {
|
||||
case MotionEvent.ACTION_SCROLL:
|
||||
x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
|
||||
y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
|
||||
SDLActivity.onNativeMouse(0, action, x, y, false);
|
||||
return true;
|
||||
|
||||
case MotionEvent.ACTION_HOVER_MOVE:
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
x = event.getX(0);
|
||||
y = event.getY(0);
|
||||
SDLActivity.onNativeMouse(0, action, x, y, true);
|
||||
return true;
|
||||
|
||||
case MotionEvent.ACTION_BUTTON_PRESS:
|
||||
case MotionEvent.ACTION_BUTTON_RELEASE:
|
||||
|
||||
// Change our action value to what SDL's code expects.
|
||||
if (action == MotionEvent.ACTION_BUTTON_PRESS) {
|
||||
action = MotionEvent.ACTION_DOWN;
|
||||
} else { /* MotionEvent.ACTION_BUTTON_RELEASE */
|
||||
action = MotionEvent.ACTION_UP;
|
||||
}
|
||||
|
||||
x = event.getX(0);
|
||||
y = event.getY(0);
|
||||
int button = event.getButtonState();
|
||||
|
||||
SDLActivity.onNativeMouse(button, action, x, y, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -32,3 +32,6 @@ a sound and input chip developed by Atari for their 8-bit computers (Atari 400,
|
|||
- filtered output on channel 2 (I suggest you to set channel 4 volume to 0).
|
||||
- use for PWM effects (not automatic!).
|
||||
- bit 0: 15KHz mode.
|
||||
- 12xx: toggle two-tone mode.
|
||||
- when enabled, channel 2 modulates channel 1. I don't know how, but it does.
|
||||
- only on ASAP core.
|
||||
|
|
12
extern/SAASound/src/SAADevice.cpp
vendored
12
extern/SAASound/src/SAADevice.cpp
vendored
|
@ -316,27 +316,27 @@ void CSAADevice::_TickAndOutputStereo(unsigned int& left_mixed, unsigned int& ri
|
|||
m_Noise0.Tick();
|
||||
m_Noise1.Tick();
|
||||
m_Amp0.TickAndOutputStereo(temp_left, temp_right);
|
||||
oscBuf[0]->data[oscBuf[0]->needle++]=(temp_left+temp_right)<<3;
|
||||
oscBuf[0]->data[oscBuf[0]->needle++]=(temp_left+temp_right)<<5;
|
||||
accum_left += temp_left;
|
||||
accum_right += temp_right;
|
||||
m_Amp1.TickAndOutputStereo(temp_left, temp_right);
|
||||
oscBuf[1]->data[oscBuf[1]->needle++]=(temp_left+temp_right)<<3;
|
||||
oscBuf[1]->data[oscBuf[1]->needle++]=(temp_left+temp_right)<<5;
|
||||
accum_left += temp_left;
|
||||
accum_right += temp_right;
|
||||
m_Amp2.TickAndOutputStereo(temp_left, temp_right);
|
||||
oscBuf[2]->data[oscBuf[2]->needle++]=(temp_left+temp_right)<<3;
|
||||
oscBuf[2]->data[oscBuf[2]->needle++]=(temp_left+temp_right)<<5;
|
||||
accum_left += temp_left;
|
||||
accum_right += temp_right;
|
||||
m_Amp3.TickAndOutputStereo(temp_left, temp_right);
|
||||
oscBuf[3]->data[oscBuf[3]->needle++]=(temp_left+temp_right)<<3;
|
||||
oscBuf[3]->data[oscBuf[3]->needle++]=(temp_left+temp_right)<<5;
|
||||
accum_left += temp_left;
|
||||
accum_right += temp_right;
|
||||
m_Amp4.TickAndOutputStereo(temp_left, temp_right);
|
||||
oscBuf[4]->data[oscBuf[4]->needle++]=(temp_left+temp_right)<<3;
|
||||
oscBuf[4]->data[oscBuf[4]->needle++]=(temp_left+temp_right)<<5;
|
||||
accum_left += temp_left;
|
||||
accum_right += temp_right;
|
||||
m_Amp5.TickAndOutputStereo(temp_left, temp_right);
|
||||
oscBuf[5]->data[oscBuf[5]->needle++]=(temp_left+temp_right)<<3;
|
||||
oscBuf[5]->data[oscBuf[5]->needle++]=(temp_left+temp_right)<<5;
|
||||
accum_left += temp_left;
|
||||
accum_right += temp_right;
|
||||
}
|
||||
|
|
2
extern/SDL
vendored
2
extern/SDL
vendored
|
@ -1 +1 @@
|
|||
Subproject commit 55b03c7493a7abed33cf803d1380a40fa8af903f
|
||||
Subproject commit ffa78e6bead23e2ba3adf8ec2367ff2218d4343c
|
5
extern/igfd/ImGuiFileDialog.cpp
vendored
5
extern/igfd/ImGuiFileDialog.cpp
vendored
|
@ -1137,8 +1137,9 @@ namespace IGFD
|
|||
IGFD::FileManager::FileManager()
|
||||
{
|
||||
puFsRoot = std::string(1u, PATH_SEP);
|
||||
for (int i=0; i<4; i++) {
|
||||
puSortingDirection[i]=true;
|
||||
puSortingDirection[0]=true;
|
||||
for (int i=1; i<4; i++) {
|
||||
puSortingDirection[i]=false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5683,7 +5683,8 @@ SafeWriter* DivEngine::saveFur(bool notPrimary, bool newPatternFormat) {
|
|||
|
||||
SafeWriter* DivEngine::saveDMF(unsigned char version) {
|
||||
// fail if version is not supported
|
||||
if (version<24 || version>26) {
|
||||
if (version>26) version=26;
|
||||
if (version<24) {
|
||||
logE("cannot save in this version!");
|
||||
lastError="invalid version to save in! this is a bug!";
|
||||
return NULL;
|
||||
|
@ -5817,7 +5818,7 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
|
|||
w->writeC(curSubSong->timeBase);
|
||||
w->writeC(curSubSong->speeds.val[0]);
|
||||
w->writeC((curSubSong->speeds.len>=2)?curSubSong->speeds.val[1]:curSubSong->speeds.val[0]);
|
||||
w->writeC((intHz<=53)?1:0);
|
||||
w->writeC((intHz<=53)?0:1);
|
||||
w->writeC((intHz!=60 && intHz!=50));
|
||||
char customHz[4];
|
||||
memset(customHz,0,4);
|
||||
|
|
|
@ -76,7 +76,8 @@ void DivPlatformArcade::acquire_nuked(short** buf, size_t len) {
|
|||
}
|
||||
|
||||
for (int i=0; i<8; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=fm.ch_out[i]>>1;
|
||||
int chOut=(int16_t)fm.ch_out[i];
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(chOut<<1,-32768,32767);
|
||||
}
|
||||
|
||||
if (o[0]<-32768) o[0]=-32768;
|
||||
|
@ -111,7 +112,8 @@ void DivPlatformArcade::acquire_ymfm(short** buf, size_t len) {
|
|||
fm_ymfm->generate(&out_ymfm);
|
||||
|
||||
for (int i=0; i<8; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=(fme->debug_channel(i)->debug_output(0)+fme->debug_channel(i)->debug_output(1))>>1;
|
||||
int chOut=fme->debug_channel(i)->debug_output(0)+fme->debug_channel(i)->debug_output(1);
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(chOut,-32768,32767);
|
||||
}
|
||||
|
||||
os[0]=out_ymfm.data[0];
|
||||
|
|
|
@ -187,9 +187,9 @@ void DivPlatformAY8910::acquire(short** buf, size_t len) {
|
|||
buf[0][i]=ayBuf[0][0];
|
||||
buf[1][i]=buf[0][i];
|
||||
|
||||
oscBuf[0]->data[oscBuf[0]->needle++]=sunsoftVolTable[31-(ay->lastIndx&31)]<<2;
|
||||
oscBuf[1]->data[oscBuf[1]->needle++]=sunsoftVolTable[31-((ay->lastIndx>>5)&31)]<<2;
|
||||
oscBuf[2]->data[oscBuf[2]->needle++]=sunsoftVolTable[31-((ay->lastIndx>>10)&31)]<<2;
|
||||
oscBuf[0]->data[oscBuf[0]->needle++]=CLAMP(sunsoftVolTable[31-(ay->lastIndx&31)]<<3,-32768,32767);
|
||||
oscBuf[1]->data[oscBuf[1]->needle++]=CLAMP(sunsoftVolTable[31-((ay->lastIndx>>5)&31)]<<3,-32768,32767);
|
||||
oscBuf[2]->data[oscBuf[2]->needle++]=CLAMP(sunsoftVolTable[31-((ay->lastIndx>>10)&31)]<<3,-32768,32767);
|
||||
}
|
||||
} else {
|
||||
for (size_t i=0; i<len; i++) {
|
||||
|
@ -205,9 +205,9 @@ void DivPlatformAY8910::acquire(short** buf, size_t len) {
|
|||
buf[1][i]=buf[0][i];
|
||||
}
|
||||
|
||||
oscBuf[0]->data[oscBuf[0]->needle++]=ayBuf[0][0]<<1;
|
||||
oscBuf[1]->data[oscBuf[1]->needle++]=ayBuf[1][0]<<1;
|
||||
oscBuf[2]->data[oscBuf[2]->needle++]=ayBuf[2][0]<<1;
|
||||
oscBuf[0]->data[oscBuf[0]->needle++]=ayBuf[0][0]<<2;
|
||||
oscBuf[1]->data[oscBuf[1]->needle++]=ayBuf[1][0]<<2;
|
||||
oscBuf[2]->data[oscBuf[2]->needle++]=ayBuf[2][0]<<2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -186,9 +186,9 @@ void DivPlatformAY8930::acquire(short** buf, size_t len) {
|
|||
buf[1][i]=buf[0][i];
|
||||
}
|
||||
|
||||
oscBuf[0]->data[oscBuf[0]->needle++]=ayBuf[0][0]<<1;
|
||||
oscBuf[1]->data[oscBuf[1]->needle++]=ayBuf[1][0]<<1;
|
||||
oscBuf[2]->data[oscBuf[2]->needle++]=ayBuf[2][0]<<1;
|
||||
oscBuf[0]->data[oscBuf[0]->needle++]=ayBuf[0][0]<<2;
|
||||
oscBuf[1]->data[oscBuf[1]->needle++]=ayBuf[1][0]<<2;
|
||||
oscBuf[2]->data[oscBuf[2]->needle++]=ayBuf[2][0]<<2;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -55,7 +55,7 @@ void DivPlatformBubSysWSG::acquire(short** buf, size_t len) {
|
|||
chanOut=chan[i].waveROM[k005289.addr(i)]*(regPool[2+i]&0xf);
|
||||
out+=chanOut;
|
||||
if (writeOscBuf==0) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=chanOut<<6;
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=chanOut<<7;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -80,18 +80,18 @@ void DivPlatformC64::acquire(short** buf, size_t len) {
|
|||
sid_fp.clock(4,&buf[0][i]);
|
||||
if (++writeOscBuf>=4) {
|
||||
writeOscBuf=0;
|
||||
oscBuf[0]->data[oscBuf[0]->needle++]=(sid_fp.lastChanOut[0]-dcOff)>>6;
|
||||
oscBuf[1]->data[oscBuf[1]->needle++]=(sid_fp.lastChanOut[1]-dcOff)>>6;
|
||||
oscBuf[2]->data[oscBuf[2]->needle++]=(sid_fp.lastChanOut[2]-dcOff)>>6;
|
||||
oscBuf[0]->data[oscBuf[0]->needle++]=(sid_fp.lastChanOut[0]-dcOff)>>5;
|
||||
oscBuf[1]->data[oscBuf[1]->needle++]=(sid_fp.lastChanOut[1]-dcOff)>>5;
|
||||
oscBuf[2]->data[oscBuf[2]->needle++]=(sid_fp.lastChanOut[2]-dcOff)>>5;
|
||||
}
|
||||
} else {
|
||||
sid.clock();
|
||||
buf[0][i]=sid.output();
|
||||
if (++writeOscBuf>=16) {
|
||||
writeOscBuf=0;
|
||||
oscBuf[0]->data[oscBuf[0]->needle++]=(sid.last_chan_out[0]-dcOff)>>6;
|
||||
oscBuf[1]->data[oscBuf[1]->needle++]=(sid.last_chan_out[1]-dcOff)>>6;
|
||||
oscBuf[2]->data[oscBuf[2]->needle++]=(sid.last_chan_out[2]-dcOff)>>6;
|
||||
oscBuf[0]->data[oscBuf[0]->needle++]=(sid.last_chan_out[0]-dcOff)>>5;
|
||||
oscBuf[1]->data[oscBuf[1]->needle++]=(sid.last_chan_out[1]-dcOff)>>5;
|
||||
oscBuf[2]->data[oscBuf[2]->needle++]=(sid.last_chan_out[2]-dcOff)>>5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ void DivPlatformDummy::acquire(short** buf, size_t len) {
|
|||
if (chan[j].active) {
|
||||
if (!isMuted[j]) {
|
||||
chanOut=(((signed short)chan[j].pos)*chan[j].amp*chan[j].vol)>>12;
|
||||
oscBuf[j]->data[oscBuf[j]->needle++]=chanOut>>1;
|
||||
oscBuf[j]->data[oscBuf[j]->needle++]=chanOut<<1;
|
||||
out+=chanOut;
|
||||
} else {
|
||||
oscBuf[j]->data[oscBuf[j]->needle++]=0;
|
||||
|
|
|
@ -64,7 +64,7 @@ void DivPlatformFDS::acquire_puNES(short* buf, size_t len) {
|
|||
buf[i]=sample;
|
||||
if (++writeOscBuf>=32) {
|
||||
writeOscBuf=0;
|
||||
oscBuf->data[oscBuf->needle++]=sample;
|
||||
oscBuf->data[oscBuf->needle++]=sample*3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -80,7 +80,7 @@ void DivPlatformFDS::acquire_NSFPlay(short* buf, size_t len) {
|
|||
buf[i]=sample;
|
||||
if (++writeOscBuf>=32) {
|
||||
writeOscBuf=0;
|
||||
oscBuf->data[oscBuf->needle++]=sample;
|
||||
oscBuf->data[oscBuf->needle++]=sample*3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -71,8 +71,13 @@ void DivPlatformGA20::acquire(short** buf, size_t len) {
|
|||
delay=w.delay;
|
||||
}
|
||||
}
|
||||
short *buffer[4] = {&ga20Buf[0][h],&ga20Buf[1][h],&ga20Buf[2][h],&ga20Buf[3][h]};
|
||||
ga20.sound_stream_update(buffer, 1);
|
||||
short *buffer[4]={
|
||||
&ga20Buf[0][h],
|
||||
&ga20Buf[1][h],
|
||||
&ga20Buf[2][h],
|
||||
&ga20Buf[3][h]
|
||||
};
|
||||
ga20.sound_stream_update(buffer,1);
|
||||
buf[0][h]=(signed int)(ga20Buf[0][h]+ga20Buf[1][h]+ga20Buf[2][h]+ga20Buf[3][h])>>2;
|
||||
for (int i=0; i<4; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=ga20Buf[i][h]>>1;
|
||||
|
|
|
@ -74,7 +74,7 @@ void DivPlatformGB::acquire(short** buf, size_t len) {
|
|||
buf[1][i]=gb->apu_output.final_sample.right;
|
||||
|
||||
for (int i=0; i<4; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=(gb->apu_output.current_sample[i].left+gb->apu_output.current_sample[i].right)<<5;
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=(gb->apu_output.current_sample[i].left+gb->apu_output.current_sample[i].right)<<6;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -122,6 +122,8 @@ void DivPlatformGenesis::processDAC(int iRate) {
|
|||
urgentWrite(0x2a,(unsigned char)sample+0x80);
|
||||
chan[5].dacReady=false;
|
||||
}
|
||||
} else {
|
||||
urgentWrite(0x2a,0x80);
|
||||
}
|
||||
chan[5].dacPos++;
|
||||
if (!chan[5].dacDirection && (s->isLoopable() && chan[5].dacPos>=(unsigned int)s->loopEnd)) {
|
||||
|
@ -187,7 +189,7 @@ void DivPlatformGenesis::acquire_nuked(short** buf, size_t len) {
|
|||
oscBuf[5]->data[oscBuf[5]->needle++]=chan[5].dacOutput<<6;
|
||||
oscBuf[6]->data[oscBuf[6]->needle++]=chan[6].dacOutput<<6;
|
||||
} else {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=fm.dacdata<<6;
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=((fm.dacdata^0x100)-0x100)<<6;
|
||||
oscBuf[6]->data[oscBuf[6]->needle++]=0;
|
||||
}
|
||||
} else {
|
||||
|
@ -252,7 +254,7 @@ void DivPlatformGenesis::acquire_ymfm(short** buf, size_t len) {
|
|||
oscBuf[5]->data[oscBuf[5]->needle++]=chan[5].dacOutput<<6;
|
||||
oscBuf[6]->data[oscBuf[6]->needle++]=chan[6].dacOutput<<6;
|
||||
} else {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=fm_ymfm->debug_dac_data()<<6;
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=((fm_ymfm->debug_dac_data()^0x100)-0x100)<<6;
|
||||
oscBuf[6]->data[oscBuf[6]->needle++]=0;
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -85,9 +85,9 @@ void DivPlatformMMC5::acquire(short** buf, size_t len) {
|
|||
|
||||
if (++writeOscBuf>=32) {
|
||||
writeOscBuf=0;
|
||||
oscBuf[0]->data[oscBuf[0]->needle++]=isMuted[0]?0:((mmc5->S3.output*10)<<6);
|
||||
oscBuf[1]->data[oscBuf[1]->needle++]=isMuted[1]?0:((mmc5->S4.output*10)<<6);
|
||||
oscBuf[2]->data[oscBuf[2]->needle++]=isMuted[2]?0:((mmc5->pcm.output*2)<<5);
|
||||
oscBuf[0]->data[oscBuf[0]->needle++]=isMuted[0]?0:((mmc5->S3.output)<<11);
|
||||
oscBuf[1]->data[oscBuf[1]->needle++]=isMuted[1]?0:((mmc5->S4.output)<<11);
|
||||
oscBuf[2]->data[oscBuf[2]->needle++]=isMuted[2]?0:((mmc5->pcm.output)<<7);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -118,7 +118,7 @@ void DivPlatformN163::acquire(short** buf, size_t len) {
|
|||
buf[0][i]=out;
|
||||
|
||||
if (n163.voice_cycle()==0x78) for (int i=0; i<8; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=n163.voice_out(i)<<6;
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=n163.voice_out(i)<<7;
|
||||
}
|
||||
|
||||
// command queue
|
||||
|
|
|
@ -115,11 +115,11 @@ void DivPlatformNES::acquire_puNES(short** buf, size_t len) {
|
|||
buf[0][i]=sample;
|
||||
if (++writeOscBuf>=32) {
|
||||
writeOscBuf=0;
|
||||
oscBuf[0]->data[oscBuf[0]->needle++]=isMuted[0]?0:(nes->S1.output<<10);
|
||||
oscBuf[1]->data[oscBuf[1]->needle++]=isMuted[1]?0:(nes->S2.output<<10);
|
||||
oscBuf[2]->data[oscBuf[2]->needle++]=isMuted[2]?0:(nes->TR.output<<10);
|
||||
oscBuf[3]->data[oscBuf[3]->needle++]=isMuted[3]?0:(nes->NS.output<<10);
|
||||
oscBuf[4]->data[oscBuf[4]->needle++]=isMuted[4]?0:(nes->DMC.output<<7);
|
||||
oscBuf[0]->data[oscBuf[0]->needle++]=isMuted[0]?0:(nes->S1.output<<11);
|
||||
oscBuf[1]->data[oscBuf[1]->needle++]=isMuted[1]?0:(nes->S2.output<<11);
|
||||
oscBuf[2]->data[oscBuf[2]->needle++]=isMuted[2]?0:(nes->TR.output<<11);
|
||||
oscBuf[3]->data[oscBuf[3]->needle++]=isMuted[3]?0:(nes->NS.output<<11);
|
||||
oscBuf[4]->data[oscBuf[4]->needle++]=isMuted[4]?0:(nes->DMC.output<<8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -142,11 +142,11 @@ void DivPlatformNES::acquire_NSFPlay(short** buf, size_t len) {
|
|||
buf[0][i]=sample;
|
||||
if (++writeOscBuf>=32) {
|
||||
writeOscBuf=0;
|
||||
oscBuf[0]->data[oscBuf[0]->needle++]=nes1_NP->out[0]<<10;
|
||||
oscBuf[1]->data[oscBuf[1]->needle++]=nes1_NP->out[1]<<10;
|
||||
oscBuf[2]->data[oscBuf[2]->needle++]=nes2_NP->out[0]<<10;
|
||||
oscBuf[3]->data[oscBuf[3]->needle++]=nes2_NP->out[1]<<10;
|
||||
oscBuf[4]->data[oscBuf[4]->needle++]=nes2_NP->out[2]<<7;
|
||||
oscBuf[0]->data[oscBuf[0]->needle++]=nes1_NP->out[0]<<11;
|
||||
oscBuf[1]->data[oscBuf[1]->needle++]=nes1_NP->out[1]<<11;
|
||||
oscBuf[2]->data[oscBuf[2]->needle++]=nes2_NP->out[0]<<11;
|
||||
oscBuf[3]->data[oscBuf[3]->needle++]=nes2_NP->out[1]<<12;
|
||||
oscBuf[4]->data[oscBuf[4]->needle++]=nes2_NP->out[2]<<8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -220,45 +220,45 @@ void DivPlatformOPL::acquire_nuked(short** buf, size_t len) {
|
|||
if (fm.rhy&0x20) {
|
||||
for (int i=0; i<melodicChans+1; i++) {
|
||||
unsigned char ch=outChanMap[i];
|
||||
int chOut=0;
|
||||
if (ch==255) continue;
|
||||
oscBuf[i]->data[oscBuf[i]->needle]=0;
|
||||
if (fm.channel[i].out[0]!=NULL) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle]+=*fm.channel[ch].out[0];
|
||||
chOut+=*fm.channel[ch].out[0];
|
||||
}
|
||||
if (fm.channel[i].out[1]!=NULL) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle]+=*fm.channel[ch].out[1];
|
||||
chOut+=*fm.channel[ch].out[1];
|
||||
}
|
||||
if (fm.channel[i].out[2]!=NULL) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle]+=*fm.channel[ch].out[2];
|
||||
chOut+=*fm.channel[ch].out[2];
|
||||
}
|
||||
if (fm.channel[i].out[3]!=NULL) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle]+=*fm.channel[ch].out[3];
|
||||
chOut+=*fm.channel[ch].out[3];
|
||||
}
|
||||
oscBuf[i]->needle++;
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(chOut<<(i==melodicChans?1:2),-32768,32767);
|
||||
}
|
||||
// special
|
||||
oscBuf[melodicChans+1]->data[oscBuf[melodicChans+1]->needle++]=fm.slot[16].out*3;
|
||||
oscBuf[melodicChans+2]->data[oscBuf[melodicChans+2]->needle++]=fm.slot[14].out*3;
|
||||
oscBuf[melodicChans+3]->data[oscBuf[melodicChans+3]->needle++]=fm.slot[17].out*3;
|
||||
oscBuf[melodicChans+4]->data[oscBuf[melodicChans+4]->needle++]=fm.slot[13].out*3;
|
||||
oscBuf[melodicChans+1]->data[oscBuf[melodicChans+1]->needle++]=fm.slot[16].out*4;
|
||||
oscBuf[melodicChans+2]->data[oscBuf[melodicChans+2]->needle++]=fm.slot[14].out*4;
|
||||
oscBuf[melodicChans+3]->data[oscBuf[melodicChans+3]->needle++]=fm.slot[17].out*4;
|
||||
oscBuf[melodicChans+4]->data[oscBuf[melodicChans+4]->needle++]=fm.slot[13].out*4;
|
||||
} else {
|
||||
for (int i=0; i<chans; i++) {
|
||||
unsigned char ch=outChanMap[i];
|
||||
int chOut=0;
|
||||
if (ch==255) continue;
|
||||
oscBuf[i]->data[oscBuf[i]->needle]=0;
|
||||
if (fm.channel[i].out[0]!=NULL) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle]+=*fm.channel[ch].out[0];
|
||||
chOut+=*fm.channel[ch].out[0];
|
||||
}
|
||||
if (fm.channel[i].out[1]!=NULL) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle]+=*fm.channel[ch].out[1];
|
||||
chOut+=*fm.channel[ch].out[1];
|
||||
}
|
||||
if (fm.channel[i].out[2]!=NULL) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle]+=*fm.channel[ch].out[2];
|
||||
chOut+=*fm.channel[ch].out[2];
|
||||
}
|
||||
if (fm.channel[i].out[3]!=NULL) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle]+=*fm.channel[ch].out[3];
|
||||
chOut+=*fm.channel[ch].out[3];
|
||||
}
|
||||
oscBuf[i]->needle++;
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(chOut<<2,-32768,32767);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -68,7 +68,7 @@ void DivPlatformOPLL::acquire_nuked(short** buf, size_t len) {
|
|||
unsigned char nextOut=cycleMapOPLL[fm.cycles];
|
||||
if ((nextOut>=6 && properDrums) || !isMuted[nextOut]) {
|
||||
os+=(o[0]+o[1]);
|
||||
if (vrc7 || (fm.rm_enable&0x20)) oscBuf[nextOut]->data[oscBuf[nextOut]->needle++]=(o[0]+o[1])<<5;
|
||||
if (vrc7 || (fm.rm_enable&0x20)) oscBuf[nextOut]->data[oscBuf[nextOut]->needle++]=(o[0]+o[1])<<6;
|
||||
} else {
|
||||
if (vrc7 || (fm.rm_enable&0x20)) oscBuf[nextOut]->data[oscBuf[nextOut]->needle++]=0;
|
||||
}
|
||||
|
@ -76,7 +76,7 @@ void DivPlatformOPLL::acquire_nuked(short** buf, size_t len) {
|
|||
if (!(vrc7 || (fm.rm_enable&0x20))) for (int i=0; i<9; i++) {
|
||||
unsigned char ch=visMapOPLL[i];
|
||||
if ((i>=6 && properDrums) || !isMuted[ch]) {
|
||||
oscBuf[ch]->data[oscBuf[ch]->needle++]=(fm.output_ch[i])<<5;
|
||||
oscBuf[ch]->data[oscBuf[ch]->needle++]=(fm.output_ch[i])<<6;
|
||||
} else {
|
||||
oscBuf[ch]->data[oscBuf[ch]->needle++]=0;
|
||||
}
|
||||
|
@ -107,7 +107,6 @@ void DivPlatformOPLL::tick(bool sysTick) {
|
|||
rWrite(0x36,drumVol[0]);
|
||||
rWrite(0x37,drumVol[1]|(drumVol[4]<<4));
|
||||
rWrite(0x38,drumVol[3]|(drumVol[2]<<4));
|
||||
break;
|
||||
} else if (i<6 || !drums) {
|
||||
if (i<9) {
|
||||
rWrite(0x30+i,((15-VOL_SCALE_LOG_BROKEN(chan[i].outVol,15-chan[i].state.op[1].tl,15))&15)|(chan[i].state.opllPreset<<4));
|
||||
|
|
|
@ -35,6 +35,7 @@ const char* regCheatSheetPOKEY[]={
|
|||
"AUDF4", "6",
|
||||
"AUDC4", "7",
|
||||
"AUDCTL", "8",
|
||||
"SKCTL", "F",
|
||||
NULL
|
||||
};
|
||||
|
||||
|
@ -154,6 +155,11 @@ void DivPlatformPOKEY::tick(bool sysTick) {
|
|||
}
|
||||
}
|
||||
|
||||
if (skctlChanged) {
|
||||
skctlChanged=false;
|
||||
rWrite(15,skctl);
|
||||
}
|
||||
|
||||
for (int i=0; i<4; i++) {
|
||||
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,parent->song.linearPitch?chan[i].pitch:0,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,true,0,parent->song.linearPitch?chan[i].pitch2:0,chipClock,CHIP_DIVIDER);
|
||||
|
@ -320,6 +326,10 @@ int DivPlatformPOKEY::dispatch(DivCommand c) {
|
|||
audctl=c.value&0xff;
|
||||
audctlChanged=true;
|
||||
break;
|
||||
case DIV_CMD_STD_NOISE_FREQ:
|
||||
skctl=c.value?0x8b:0x03;
|
||||
skctlChanged=true;
|
||||
break;
|
||||
case DIV_CMD_NOTE_PORTA: {
|
||||
int destFreq=NOTE_PERIODIC(c.value2);
|
||||
bool return2=false;
|
||||
|
@ -385,6 +395,7 @@ void DivPlatformPOKEY::forceIns() {
|
|||
chan[i].freqChanged=true;
|
||||
}
|
||||
audctlChanged=true;
|
||||
skctlChanged=true;
|
||||
}
|
||||
|
||||
void* DivPlatformPOKEY::getChanState(int ch) {
|
||||
|
@ -408,7 +419,7 @@ unsigned char* DivPlatformPOKEY::getRegisterPool() {
|
|||
}
|
||||
|
||||
int DivPlatformPOKEY::getRegisterPoolSize() {
|
||||
return 9;
|
||||
return 16;
|
||||
}
|
||||
|
||||
void DivPlatformPOKEY::reset() {
|
||||
|
@ -430,6 +441,8 @@ void DivPlatformPOKEY::reset() {
|
|||
|
||||
audctl=0;
|
||||
audctlChanged=true;
|
||||
skctl=3;
|
||||
skctlChanged=true;
|
||||
}
|
||||
|
||||
bool DivPlatformPOKEY::keyOffAffectsArp(int ch) {
|
||||
|
|
|
@ -48,8 +48,8 @@ class DivPlatformPOKEY: public DivDispatch {
|
|||
QueuedWrite(unsigned char a, unsigned char v): addr(a), val(v) {}
|
||||
};
|
||||
std::queue<QueuedWrite> writes;
|
||||
unsigned char audctl;
|
||||
bool audctlChanged;
|
||||
unsigned char audctl, skctl;
|
||||
bool audctlChanged, skctlChanged;
|
||||
unsigned char oscBufDelay;
|
||||
PokeyState pokey;
|
||||
AltASAP::Pokey altASAP;
|
||||
|
|
|
@ -42,7 +42,7 @@ void DivPlatformPV1000::acquire(short** buf, size_t len) {
|
|||
short samp=d65010g031_sound_tick(&d65010g031,1);
|
||||
buf[0][h]=samp;
|
||||
for (int i=0; i<3; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=(d65010g031.out[i]);
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=d65010g031.out[i]<<1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -87,7 +87,7 @@ void DivPlatformSCC::acquire(short** buf, size_t len) {
|
|||
buf[0][h]=out;
|
||||
|
||||
for (int i=0; i<5; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=scc->voice_out(i)<<6;
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=scc->voice_out(i)<<7;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,9 +58,9 @@ void DivPlatformSM8521::acquire(short** buf, size_t len) {
|
|||
sm8521_sound_tick(&sm8521,8);
|
||||
buf[0][h]=sm8521.out<<6;
|
||||
for (int i=0; i<2; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=sm8521.sg[i].base.out<<5;
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=sm8521.sg[i].base.out<<7;
|
||||
}
|
||||
oscBuf[2]->data[oscBuf[2]->needle++]=sm8521.noise.base.out<<5;
|
||||
oscBuf[2]->data[oscBuf[2]->needle++]=sm8521.noise.base.out<<7;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -509,7 +509,7 @@ public:
|
|||
}
|
||||
|
||||
if (oscb!=NULL) {
|
||||
oscb[i]->data[oscb[i]->needle++]=oscbWrite>>1;
|
||||
oscb[i]->data[oscb[i]->needle++]=oscbWrite;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ static constexpr int MuteInit = 2;
|
|||
static constexpr int MuteSerialInput = 8;
|
||||
//just some magick value to match the audio level of mzpokeysnd
|
||||
static constexpr int16_t MAGICK_VOLUME_BOOSTER = 160;
|
||||
static constexpr int16_t MAGICK_OSC_VOLUME_BOOSTER = 2;
|
||||
static constexpr int16_t MAGICK_OSC_VOLUME_BOOSTER = 6;
|
||||
|
||||
struct PokeyBase
|
||||
{
|
||||
|
|
|
@ -87,7 +87,7 @@ void DivPlatformSwan::acquire(short** buf, size_t len) {
|
|||
buf[0][h]=samp[0];
|
||||
buf[1][h]=samp[1];
|
||||
for (int i=0; i<4; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=(ws->sample_cache[i][0]+ws->sample_cache[i][1])<<5;
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=(ws->sample_cache[i][0]+ws->sample_cache[i][1])<<6;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,7 +54,7 @@ void DivPlatformT6W28::acquire(short** buf, size_t len) {
|
|||
tempL=0;
|
||||
tempR=0;
|
||||
for (int i=0; i<4; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=(out[i][1].curValue+out[i][2].curValue)<<6;
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=(out[i][1].curValue+out[i][2].curValue)<<7;
|
||||
tempL+=out[i][1].curValue<<7;
|
||||
tempR+=out[i][2].curValue<<7;
|
||||
}
|
||||
|
|
|
@ -51,8 +51,8 @@ void DivPlatformTIA::acquire(short** buf, size_t len) {
|
|||
}
|
||||
if (++chanOscCounter>=114) {
|
||||
chanOscCounter=0;
|
||||
oscBuf[0]->data[oscBuf[0]->needle++]=tia.myChannelOut[0]>>1;
|
||||
oscBuf[1]->data[oscBuf[1]->needle++]=tia.myChannelOut[1]>>1;
|
||||
oscBuf[0]->data[oscBuf[0]->needle++]=tia.myChannelOut[0];
|
||||
oscBuf[1]->data[oscBuf[1]->needle++]=tia.myChannelOut[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -78,7 +78,7 @@ void DivPlatformTX81Z::acquire(short** buf, size_t len) {
|
|||
fm_ymfm->generate(&out_ymfm);
|
||||
|
||||
for (int i=0; i<8; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=(fme->debug_channel(i)->debug_output(0)+fme->debug_channel(i)->debug_output(1))>>1;
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(fme->debug_channel(i)->debug_output(0)+fme->debug_channel(i)->debug_output(1),-32768,32767);
|
||||
}
|
||||
|
||||
os[0]=out_ymfm.data[0];
|
||||
|
|
|
@ -107,7 +107,7 @@ void DivPlatformVB::acquire(short** buf, size_t len) {
|
|||
tempL=0;
|
||||
tempR=0;
|
||||
for (int i=0; i<6; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=(vb->last_output[i][0]+vb->last_output[i][1])*4;
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=(vb->last_output[i][0]+vb->last_output[i][1])*8;
|
||||
tempL+=vb->last_output[i][0];
|
||||
tempR+=vb->last_output[i][1];
|
||||
}
|
||||
|
|
|
@ -69,7 +69,7 @@ void DivPlatformVIC20::acquire(short** buf, size_t len) {
|
|||
vic_sound_machine_calculate_samples(vic,&samp,1,1,0,SAMP_DIVIDER);
|
||||
buf[0][h]=samp;
|
||||
for (int i=0; i<4; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=vic->ch[i].out?(vic->volume<<10):0;
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=vic->ch[i].out?(vic->volume<<11):0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -87,9 +87,9 @@ void DivPlatformVRC6::acquire(short** buf, size_t len) {
|
|||
if (++writeOscBuf>=32) {
|
||||
writeOscBuf=0;
|
||||
for (int i=0; i<2; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=vrc6.pulse_out(i)<<9;
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=vrc6.pulse_out(i)<<11;
|
||||
}
|
||||
oscBuf[2]->data[oscBuf[2]->needle++]=vrc6.sawtooth_out()<<9;
|
||||
oscBuf[2]->data[oscBuf[2]->needle++]=vrc6.sawtooth_out()<<10;
|
||||
}
|
||||
|
||||
// Command part
|
||||
|
|
|
@ -231,11 +231,11 @@ void DivPlatformYM2203::acquire_combo(short** buf, size_t len) {
|
|||
buf[0][h]=os;
|
||||
|
||||
for (int i=0; i<3; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=fm_nuked.ch_out[i]>>1;
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=fm_nuked.ch_out[i]<<1;
|
||||
}
|
||||
|
||||
for (int i=3; i<6; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=fmout.data[i-2]>>1;
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=fmout.data[i-2]<<1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -282,11 +282,11 @@ void DivPlatformYM2203::acquire_ymfm(short** buf, size_t len) {
|
|||
|
||||
|
||||
for (int i=0; i<3; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=(fmChan[i]->debug_output(0)+fmChan[i]->debug_output(1))>>1;
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=(fmChan[i]->debug_output(0)+fmChan[i]->debug_output(1))<<1;
|
||||
}
|
||||
|
||||
for (int i=3; i<6; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=fmout.data[i-2]>>1;
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=fmout.data[i-2]<<1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -402,12 +402,12 @@ void DivPlatformYM2608::acquire_combo(short** buf, size_t len) {
|
|||
|
||||
|
||||
for (int i=0; i<psgChanOffs; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=fm_nuked.ch_out[i]>>1;
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=fm_nuked.ch_out[i]<<1;
|
||||
}
|
||||
|
||||
ssge->get_last_out(ssgOut);
|
||||
for (int i=psgChanOffs; i<adpcmAChanOffs; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=ssgOut.data[i-psgChanOffs]>>1;
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=ssgOut.data[i-psgChanOffs]<<1;
|
||||
}
|
||||
|
||||
for (int i=adpcmAChanOffs; i<adpcmBChanOffs; i++) {
|
||||
|
@ -471,12 +471,12 @@ void DivPlatformYM2608::acquire_ymfm(short** buf, size_t len) {
|
|||
buf[1][h]=os[1];
|
||||
|
||||
for (int i=0; i<6; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=(fmChan[i]->debug_output(0)+fmChan[i]->debug_output(1))>>1;
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=(fmChan[i]->debug_output(0)+fmChan[i]->debug_output(1))<<1;
|
||||
}
|
||||
|
||||
ssge->get_last_out(ssgOut);
|
||||
for (int i=6; i<9; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=ssgOut.data[i-6]>>1;
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=ssgOut.data[i-6]<<1;
|
||||
}
|
||||
|
||||
for (int i=9; i<15; i++) {
|
||||
|
|
|
@ -333,12 +333,12 @@ void DivPlatformYM2610::acquire_combo(short** buf, size_t len) {
|
|||
|
||||
|
||||
for (int i=0; i<psgChanOffs; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=fm_nuked.ch_out[bchOffs[i]]>>1;
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=fm_nuked.ch_out[bchOffs[i]]<<1;
|
||||
}
|
||||
|
||||
ssge->get_last_out(ssgOut);
|
||||
for (int i=psgChanOffs; i<adpcmAChanOffs; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=ssgOut.data[i-psgChanOffs]>>1;
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=ssgOut.data[i-psgChanOffs]<<1;
|
||||
}
|
||||
|
||||
for (int i=adpcmAChanOffs; i<adpcmBChanOffs; i++) {
|
||||
|
@ -404,12 +404,12 @@ void DivPlatformYM2610::acquire_ymfm(short** buf, size_t len) {
|
|||
buf[1][h]=os[1];
|
||||
|
||||
for (int i=0; i<psgChanOffs; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=(fmChan[i]->debug_output(0)+fmChan[i]->debug_output(1))>>1;
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=(fmChan[i]->debug_output(0)+fmChan[i]->debug_output(1))<<1;
|
||||
}
|
||||
|
||||
ssge->get_last_out(ssgOut);
|
||||
for (int i=psgChanOffs; i<adpcmAChanOffs; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=ssgOut.data[i-psgChanOffs]>>1;
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=ssgOut.data[i-psgChanOffs]<<1;
|
||||
}
|
||||
|
||||
for (int i=adpcmAChanOffs; i<adpcmBChanOffs; i++) {
|
||||
|
|
|
@ -401,12 +401,12 @@ void DivPlatformYM2610B::acquire_combo(short** buf, size_t len) {
|
|||
|
||||
|
||||
for (int i=0; i<psgChanOffs; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=fm_nuked.ch_out[i]>>1;
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=fm_nuked.ch_out[i]<<1;
|
||||
}
|
||||
|
||||
ssge->get_last_out(ssgOut);
|
||||
for (int i=psgChanOffs; i<adpcmAChanOffs; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=ssgOut.data[i-psgChanOffs]>>1;
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=ssgOut.data[i-psgChanOffs]<<1;
|
||||
}
|
||||
|
||||
for (int i=adpcmAChanOffs; i<adpcmBChanOffs; i++) {
|
||||
|
@ -471,12 +471,12 @@ void DivPlatformYM2610B::acquire_ymfm(short** buf, size_t len) {
|
|||
|
||||
|
||||
for (int i=0; i<psgChanOffs; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=(fmChan[i]->debug_output(0)+fmChan[i]->debug_output(1))>>1;
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=(fmChan[i]->debug_output(0)+fmChan[i]->debug_output(1))<<1;
|
||||
}
|
||||
|
||||
ssge->get_last_out(ssgOut);
|
||||
for (int i=psgChanOffs; i<adpcmAChanOffs; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=ssgOut.data[i-psgChanOffs]>>1;
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=ssgOut.data[i-psgChanOffs]<<1;
|
||||
}
|
||||
|
||||
for (int i=adpcmAChanOffs; i<adpcmBChanOffs; i++) {
|
||||
|
|
|
@ -1188,6 +1188,11 @@ void DivEngine::nextRow() {
|
|||
nextSpeed=speeds.val[curSpeed];
|
||||
}
|
||||
|
||||
/*
|
||||
if (skipping) {
|
||||
ticks=1;
|
||||
}*/
|
||||
|
||||
// post row details
|
||||
for (int i=0; i<chans; i++) {
|
||||
DivPattern* pat=curPat[i].getPattern(curOrders->ord[i][curOrder],false);
|
||||
|
@ -1314,7 +1319,7 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
|
|||
subticks=tickMult;
|
||||
|
||||
if (stepPlay!=1) {
|
||||
tempoAccum+=curSubSong->virtualTempoN;
|
||||
tempoAccum+=(skipping && curSubSong->virtualTempoN<curSubSong->virtualTempoD)?curSubSong->virtualTempoD:curSubSong->virtualTempoN;
|
||||
while (tempoAccum>=curSubSong->virtualTempoD) {
|
||||
tempoAccum-=curSubSong->virtualTempoD;
|
||||
if (--ticks<=0) {
|
||||
|
@ -1564,8 +1569,10 @@ void DivEngine::runMidiClock(int totalCycles) {
|
|||
if (speedSum<1.0) speedSum=1.0;
|
||||
if (vD<1) vD=1;
|
||||
double bpm=((24.0*divider)/(timeBase*hl*speedSum))*(double)curSubSong->virtualTempoN/vD;
|
||||
if (bpm<1.0) bpm=1.0;
|
||||
int increment=got.rate*pow(2,MASTER_CLOCK_PREC)/(bpm);
|
||||
|
||||
midiClockCycles+=got.rate*pow(2,MASTER_CLOCK_PREC)/(bpm);
|
||||
midiClockCycles+=increment;
|
||||
midiClockDrift+=fmod(got.rate*pow(2,MASTER_CLOCK_PREC),(double)(bpm));
|
||||
if (midiClockDrift>=(bpm)) {
|
||||
midiClockDrift-=(bpm);
|
||||
|
|
|
@ -1169,6 +1169,7 @@ void DivEngine::registerSystems() {
|
|||
{
|
||||
{0x10, {DIV_CMD_WAVE, "10xx: Set waveform (0 to 7)"}},
|
||||
{0x11, {DIV_CMD_STD_NOISE_MODE, "11xx: Set AUDCTL"}},
|
||||
{0x12, {DIV_CMD_STD_NOISE_FREQ, "12xx: Toggle two-tone mode"}},
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "../ta-log.h"
|
||||
#include "imgui.h"
|
||||
#include "imgui_internal.h"
|
||||
#include "misc/cpp/imgui_stdlib.h"
|
||||
|
||||
#define FURNACE_FFT_SIZE 4096
|
||||
#define FURNACE_FFT_RATE 80.0
|
||||
|
@ -284,11 +285,37 @@ void FurnaceGUI::drawChanOsc() {
|
|||
ImGui::ColorPicker4("Color",(float*)&chanOscColor);
|
||||
}
|
||||
|
||||
if (ImGui::Button("OK")) {
|
||||
chanOscOptions=false;
|
||||
ImGui::Text("Text format:");
|
||||
ImGui::SameLine();
|
||||
ImGui::InputText("##TextFormat",&chanOscTextFormat);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
if (ImGui::BeginTooltip()) {
|
||||
ImGui::TextUnformatted(
|
||||
"format guide:\n"
|
||||
"- %c: channel name\n"
|
||||
"- %C: channel short name\n"
|
||||
"- %d: channel number (starting from 0)\n"
|
||||
"- %D: channel number (starting from 1)\n"
|
||||
"- %i: instrument name\n"
|
||||
"- %I: instrument number (decimal)\n"
|
||||
"- %x: instrument number (hex)\n"
|
||||
"- %s: chip name\n"
|
||||
"- %S: chip ID\n"
|
||||
"- %v: volume (decimal)\n"
|
||||
"- %V: volume (percentage)\n"
|
||||
"- %b: volume (hex)\n"
|
||||
"- %%: percent sign"
|
||||
);
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::ColorEdit4("Text color",(float*)&chanOscTextColor);
|
||||
|
||||
if (ImGui::Button("OK")) {
|
||||
chanOscOptions=false;
|
||||
}
|
||||
} else {
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding,ImVec2(0.0f,0.0f));
|
||||
float availY=ImGui::GetContentRegionAvail().y;
|
||||
if (ImGui::BeginTable("ChanOsc",chanOscCols,ImGuiTableFlags_Borders)) {
|
||||
|
@ -411,6 +438,7 @@ void FurnaceGUI::drawChanOsc() {
|
|||
y-=dcOff;
|
||||
if (y<-0.5f) y=-0.5f;
|
||||
if (y>0.5f) y=0.5f;
|
||||
y*=chanOscAmplify;
|
||||
waveform[i]=ImLerp(inRect.Min,inRect.Max,ImVec2(x,0.5f-y));
|
||||
}
|
||||
}
|
||||
|
@ -425,7 +453,86 @@ void FurnaceGUI::drawChanOsc() {
|
|||
color=chanOscGrad.get(xVal,1.0f-yVal);
|
||||
}
|
||||
ImGui::PushClipRect(inRect.Min,inRect.Max,false);
|
||||
|
||||
dl->AddPolyline(waveform,precision,color,ImDrawFlags_None,dpiScale);
|
||||
|
||||
if (!chanOscTextFormat.empty()) {
|
||||
String text;
|
||||
bool inFormat=false;
|
||||
|
||||
for (char i: chanOscTextFormat) {
|
||||
if (inFormat) {
|
||||
switch (i) {
|
||||
case 'c':
|
||||
text+=e->getChannelName(ch);
|
||||
break;
|
||||
case 'C':
|
||||
text+=e->getChannelShortName(ch);
|
||||
break;
|
||||
case 'd':
|
||||
text+=fmt::sprintf("%d",ch);
|
||||
break;
|
||||
case 'D':
|
||||
text+=fmt::sprintf("%d",ch+1);
|
||||
break;
|
||||
case 'i': {
|
||||
DivChannelState* chanState=e->getChanState(ch);
|
||||
if (chanState==NULL) break;
|
||||
DivInstrument* ins=e->getIns(chanState->lastIns);
|
||||
text+=ins->name;
|
||||
break;
|
||||
}
|
||||
case 'I': {
|
||||
DivChannelState* chanState=e->getChanState(ch);
|
||||
if (chanState==NULL) break;
|
||||
text+=fmt::sprintf("%d",chanState->lastIns);
|
||||
break;
|
||||
}
|
||||
case 'x': {
|
||||
DivChannelState* chanState=e->getChanState(ch);
|
||||
if (chanState==NULL) break;
|
||||
if (chanState->lastIns<0) {
|
||||
text+="??";
|
||||
} else {
|
||||
text+=fmt::sprintf("%.2X",chanState->lastIns);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 's': {
|
||||
text+=e->getSystemName(e->sysOfChan[ch]);
|
||||
break;
|
||||
}
|
||||
case 'S': {
|
||||
text+=fmt::sprintf("%d",e->dispatchOfChan[ch]);
|
||||
break;
|
||||
}
|
||||
case 'v':
|
||||
break;
|
||||
case 'V':
|
||||
break;
|
||||
case 'b':
|
||||
break;
|
||||
case '%':
|
||||
text+='%';
|
||||
break;
|
||||
default:
|
||||
text+='%';
|
||||
text+=i;
|
||||
break;
|
||||
}
|
||||
inFormat=false;
|
||||
} else {
|
||||
if (i=='%') {
|
||||
inFormat=true;
|
||||
} else {
|
||||
text+=i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dl->AddText(ImLerp(inRect.Min,inRect.Max,ImVec2(0.0f,0.0f)),ImGui::GetColorU32(chanOscTextColor),text.c_str());
|
||||
}
|
||||
|
||||
ImGui::PopClipRect();
|
||||
}
|
||||
}
|
||||
|
@ -438,6 +545,7 @@ void FurnaceGUI::drawChanOsc() {
|
|||
}
|
||||
ImGui::PopStyleVar();
|
||||
}
|
||||
}
|
||||
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_CHAN_OSC;
|
||||
ImGui::End();
|
||||
}
|
||||
|
|
|
@ -5994,13 +5994,22 @@ bool FurnaceGUI::init() {
|
|||
chanOscCols=e->getConfInt("chanOscCols",3);
|
||||
chanOscColorX=e->getConfInt("chanOscColorX",GUI_OSCREF_CENTER);
|
||||
chanOscColorY=e->getConfInt("chanOscColorY",GUI_OSCREF_CENTER);
|
||||
chanOscTextX=e->getConfFloat("chanOscTextX",0.0f);
|
||||
chanOscTextY=e->getConfFloat("chanOscTextY",0.0f);
|
||||
chanOscAmplify=e->getConfFloat("chanOscAmplify",0.95f);
|
||||
chanOscWindowSize=e->getConfFloat("chanOscWindowSize",20.0f);
|
||||
chanOscWaveCorr=e->getConfBool("chanOscWaveCorr",true);
|
||||
chanOscOptions=e->getConfBool("chanOscOptions",false);
|
||||
chanOscNormalize=e->getConfBool("chanOscNormalize",false);
|
||||
chanOscTextFormat=e->getConfString("chanOscTextFormat","%c");
|
||||
chanOscColor.x=e->getConfFloat("chanOscColorR",1.0f);
|
||||
chanOscColor.y=e->getConfFloat("chanOscColorG",1.0f);
|
||||
chanOscColor.z=e->getConfFloat("chanOscColorB",1.0f);
|
||||
chanOscColor.w=e->getConfFloat("chanOscColorA",1.0f);
|
||||
chanOscTextColor.x=e->getConfFloat("chanOscTextColorR",1.0f);
|
||||
chanOscTextColor.y=e->getConfFloat("chanOscTextColorG",1.0f);
|
||||
chanOscTextColor.z=e->getConfFloat("chanOscTextColorB",1.0f);
|
||||
chanOscTextColor.w=e->getConfFloat("chanOscTextColorA",0.75f);
|
||||
chanOscUseGrad=e->getConfBool("chanOscUseGrad",false);
|
||||
chanOscGrad.fromString(e->getConfString("chanOscGrad",""));
|
||||
chanOscGrad.render();
|
||||
|
@ -6487,13 +6496,22 @@ void FurnaceGUI::commitState() {
|
|||
e->setConf("chanOscCols",chanOscCols);
|
||||
e->setConf("chanOscColorX",chanOscColorX);
|
||||
e->setConf("chanOscColorY",chanOscColorY);
|
||||
e->setConf("chanOscTextX",chanOscTextX);
|
||||
e->setConf("chanOscTextY",chanOscTextY);
|
||||
e->setConf("chanOscAmplify",chanOscAmplify);
|
||||
e->setConf("chanOscWindowSize",chanOscWindowSize);
|
||||
e->setConf("chanOscWaveCorr",chanOscWaveCorr);
|
||||
e->setConf("chanOscOptions",chanOscOptions);
|
||||
e->setConf("chanOscNormalize",chanOscNormalize);
|
||||
e->setConf("chanOscTextFormat",chanOscTextFormat);
|
||||
e->setConf("chanOscColorR",chanOscColor.x);
|
||||
e->setConf("chanOscColorG",chanOscColor.y);
|
||||
e->setConf("chanOscColorB",chanOscColor.z);
|
||||
e->setConf("chanOscColorA",chanOscColor.w);
|
||||
e->setConf("chanOscTextColorR",chanOscTextColor.x);
|
||||
e->setConf("chanOscTextColorG",chanOscTextColor.y);
|
||||
e->setConf("chanOscTextColorB",chanOscTextColor.z);
|
||||
e->setConf("chanOscTextColorA",chanOscTextColor.w);
|
||||
e->setConf("chanOscUseGrad",chanOscUseGrad);
|
||||
e->setConf("chanOscGrad",chanOscGrad.toString());
|
||||
|
||||
|
@ -6915,11 +6933,17 @@ FurnaceGUI::FurnaceGUI():
|
|||
chanOscColorX(GUI_OSCREF_CENTER),
|
||||
chanOscColorY(GUI_OSCREF_CENTER),
|
||||
chanOscWindowSize(20.0f),
|
||||
chanOscTextX(0.0f),
|
||||
chanOscTextY(0.0f),
|
||||
chanOscAmplify(0.95f),
|
||||
chanOscWaveCorr(true),
|
||||
chanOscOptions(false),
|
||||
updateChanOscGradTex(true),
|
||||
chanOscUseGrad(false),
|
||||
chanOscNormalize(false),
|
||||
chanOscTextFormat("%c"),
|
||||
chanOscColor(1.0f,1.0f,1.0f,1.0f),
|
||||
chanOscTextColor(1.0f,1.0f,1.0f,0.75f),
|
||||
chanOscGrad(64,64),
|
||||
chanOscGradTex(NULL),
|
||||
followLog(true),
|
||||
|
|
|
@ -1948,9 +1948,10 @@ class FurnaceGUI {
|
|||
|
||||
// per-channel oscilloscope
|
||||
int chanOscCols, chanOscColorX, chanOscColorY;
|
||||
float chanOscWindowSize;
|
||||
bool chanOscWaveCorr, chanOscOptions, updateChanOscGradTex, chanOscUseGrad;
|
||||
ImVec4 chanOscColor;
|
||||
float chanOscWindowSize, chanOscTextX, chanOscTextY, chanOscAmplify;
|
||||
bool chanOscWaveCorr, chanOscOptions, updateChanOscGradTex, chanOscUseGrad, chanOscNormalize;
|
||||
String chanOscTextFormat;
|
||||
ImVec4 chanOscColor, chanOscTextColor;
|
||||
Gradient2D chanOscGrad;
|
||||
void* chanOscGradTex;
|
||||
float chanOscLP0[DIV_MAX_CHANS];
|
||||
|
|
Loading…
Reference in a new issue