diff --git a/Makefile.emscripten b/Makefile.emscripten new file mode 100644 index 0000000000..c1e913419c --- /dev/null +++ b/Makefile.emscripten @@ -0,0 +1,159 @@ +TARGET = retroarch.js + +OBJ = frontend/frontend_emscripten.o \ + retroarch.o \ + file.o \ + file_path.o \ + driver.o \ + conf/config_file.o \ + settings.o \ + hash.o \ + dynamic.o \ + dynamic_dummy.o \ + message.o \ + rewind.o \ + movie.o \ + gfx/gfx_common.o \ + input/input_common.o \ + input/rwebinput_input.o \ + core_options.o \ + patch.o \ + compat/compat.o \ + compat/rxml/rxml.o \ + screenshot.o \ + cheats.o \ + audio/utils.o \ + audio/rwebaudio.o \ + input/overlay.o \ + fifo_buffer.o \ + gfx/scaler/scaler.o \ + gfx/scaler/pixconv.o \ + gfx/scaler/scaler_int.o \ + gfx/scaler/filter.o \ + gfx/state_tracker.o \ + gfx/shader_parse.o \ + gfx/fonts/fonts.o \ + gfx/fonts/bitmapfont.o \ + gfx/image.o \ + audio/resampler.o \ + audio/sinc.o \ + audio/null.o \ + performance.o + +HAVE_OPENGL = 1 +HAVE_RGUI = 1 +HAVE_SDL = 0 +HAVE_ZLIB = 1 +HAVE_FBO = 1 +WANT_MINIZ = 1 +MEMORY = 67108864 +LTO = 0 +FAST_DOUBLES = 1 + +ifneq ($(NATIVE_ZLIB),) + WANT_MINIZ = 0 +endif + +libretro = libretro_emscripten.bc + +LIBS = -lm +DEFINES = -DHAVE_SCREENSHOTS -DHAVE_NULLAUDIO -DHAVE_BSV_MOVIE -DPACKAGE_VERSION=\"0.9.9.3\" +LDFLAGS = -L. -s TOTAL_MEMORY=$(MEMORY) --js-library emscripten/library_rwebaudio.js --js-library emscripten/library_rwebinput.js + +ifeq ($(PERF_TEST), 1) + DEFINES += -DPERF_TEST +endif + +ifeq ($(HAVE_RGUI), 1) + DEFINES += -DHAVE_RGUI + OBJ += frontend/menu/menu_common.o frontend/menu/rgui.o frontend/menu/history.o +endif + +ifeq ($(HAVE_SDL), 1) + OBJ += input/sdl_input.o + LIBS += -lSDL + DEFINES += -ISDL -DHAVE_SDL +endif + +ifeq ($(HAVE_OPENGL), 1) + OBJ += gfx/gl.o gfx/math/matrix.o gfx/fonts/gl_font.o gfx/fonts/gl_raster_font.o gfx/gfx_context.o gfx/context/emscriptenegl_ctx.o gfx/shader_glsl.o gfx/glsym/rglgen.o gfx/glsym/glsym_es2.o + DEFINES += -DHAVE_OPENGL -DHAVE_OPENGLES -DHAVE_OPENGLES2 -DHAVE_EGL -DHAVE_OVERLAY -DHAVE_GLSL +endif + +ifeq ($(HAVE_ZLIB), 1) + OBJ += gfx/rpng/rpng.o file_extract.o + DEFINES += -DHAVE_ZLIB + ifeq ($(WANT_MINIZ), 1) + OBJ += deps/miniz/miniz.o + DEFINES += -DWANT_MINIZ + else + LIBS += -lz + DEFINES += -DHAVE_ZLIB_DEFLATE + endif +endif + +ifeq ($(HAVE_FBO), 1) + DEFINES += -DHAVE_FBO +endif + +ifneq ($(V), 1) + Q := @ +endif + +ifeq ($(DEBUG), 1) + LDFLAGS += -O0 -g + CFLAGS += -O0 -g +else + LDFLAGS += -O2 + # WARNING: some optimizations can break some cores (ex: LTO breaks tyrquake) + ifeq ($(FAST_DOUBLES), 1) + LDFLAGS += -s DOUBLE_MODE=0 + endif + ifeq ($(LTO), 1) + LDFLAGS += --llvm-lto 3 + endif + CFLAGS += -O2 +endif + +CFLAGS += -Wall -Wno-unused-result -Wno-unused-variable -I. -std=gnu99 + +all: $(TARGET) + +$(TARGET): $(OBJ) + @$(if $(Q), $(shell echo echo LD $@),) + $(Q)$(LD) -o $@ $(OBJ) $(libretro) $(LIBS) $(LDFLAGS) + +%.o: %.c + @$(if $(Q), $(shell echo echo CC $<),) + $(Q)$(CC) $(CFLAGS) $(DEFINES) -c -o $@ $< + +%.o: %.cpp + @$(if $(Q), $(shell echo echo CXX $<),) + $(Q)$(CXX) $(CXXFLAGS) $(DEFINES) -c -o $@ $< + +clean: + rm -f *.o + rm -f deps/miniz/*.o + rm -f frontend/*.o + rm -f frontend/menu/*.o + rm -f audio/*.o + rm -f audio/xaudio-c/*.o + rm -f compat/*.o + rm -f compat/rxml/*.o + rm -f conf/*.o + rm -f gfx/scaler/*.o + rm -f gfx/*.o + rm -f gfx/d3d9/*.o + rm -f gfx/context/*.o + rm -f gfx/math/*.o + rm -f gfx/fonts/*.o + rm -f gfx/py_state/*.o + rm -f gfx/rpng/*.o + rm -f gfx/glsym/*.o + rm -f record/*.o + rm -f input/*.o + rm -f tools/*.o + rm -f $(TARGET) + +.PHONY: all clean + diff --git a/Makefile.win b/Makefile.win index 71036ccf47..020d5478eb 100644 --- a/Makefile.win +++ b/Makefile.win @@ -17,6 +17,7 @@ OBJ = frontend/frontend.o \ movie.o \ gfx/gfx_common.o \ input/input_common.o \ + input/autoconf/builtin_win.o \ core_options.o \ patch.o \ compat/compat.o \ @@ -44,7 +45,8 @@ JOBJ := conf/config_file.o \ compat/compat.o \ file_path.o \ tools/input_common_joyconfig.o \ - input/dinput.o + input/dinput.o \ + input/winxinput_joypad.o CC = gcc CXX = g++ @@ -62,6 +64,9 @@ HAVE_STDIN_CMD = 1 HAVE_THREADS = 1 HAVE_RGUI = 1 DYNAMIC = 1 +HAVE_WIN32GUI = 1 + +HAVE_WINXINPUT = 1 ifeq ($(SLIM),) HAVE_SDL = 1 @@ -78,7 +83,7 @@ endif libretro ?= -lretro LIBS = -lm -DEFINES = -I. -DHAVE_SCREENSHOTS -DHAVE_BSV_MOVIE +DEFINES = -I. -DHAVE_SCREENSHOTS -DHAVE_BSV_MOVIE -DHAVE_BUILTIN_AUTOCONFIG LDFLAGS = -L. -static-libgcc ifeq ($(TDM_GCC),) @@ -100,6 +105,10 @@ ifeq ($(HAVE_RGUI), 1) OBJ += frontend/menu/menu_common.o frontend/menu/rgui.o frontend/menu/history.o endif +ifeq ($(HAVE_WIN32GUI), 1) + DEFINES += -DHAVE_WIN32GUI +endif + ifeq ($(HAVE_SDL), 1) OBJ += gfx/sdl_gfx.o input/sdl_input.o input/sdl_joypad.o audio/sdl_audio.o JOBJ += input/sdl_joypad.o @@ -212,6 +221,11 @@ ifeq ($(HAVE_PYTHON), 1) OBJ += gfx/py_state/py_state.o endif +ifeq ($(HAVE_WINXINPUT), 1) + DEFINES += -DHAVE_WINXINPUT + OBJ += input/winxinput_joypad.o +endif + ifeq ($(HAVE_DINPUT), 1) LIBS += -ldinput8 -ldxguid -lole32 DEFINES += -DHAVE_DINPUT diff --git a/android/native/jni/Android.mk b/android/native/jni/Android.mk index 7a2e6b963c..063c0aeb2e 100644 --- a/android/native/jni/Android.mk +++ b/android/native/jni/Android.mk @@ -48,7 +48,7 @@ ifeq ($(PERF_TEST), 1) LOCAL_CFLAGS += -DPERF_TEST endif -LOCAL_CFLAGS += -Wall -pthread -Wno-unused-function -O3 -fno-stack-protector -funroll-loops -DNDEBUG -DRARCH_MOBILE -DHAVE_GRIFFIN -DANDROID -DHAVE_DYNAMIC -DHAVE_OPENGL -DHAVE_FBO -DHAVE_OVERLAY -DHAVE_OPENGLES -DHAVE_VID_CONTEXT -DHAVE_OPENGLES2 -DGLSL_DEBUG -DHAVE_GLSL -DHAVE_RGUI -DHAVE_SCREENSHOTS -DWANT_MINIZ -DHAVE_ZLIB -DINLINE=inline -DLSB_FIRST -DHAVE_THREADS -D__LIBRETRO__ -DRARCH_PERFORMANCE_MODE -std=gnu99 -I../../../deps/miniz +LOCAL_CFLAGS += -Wall -pthread -Wno-unused-function -O3 -fno-stack-protector -funroll-loops -DNDEBUG -DRARCH_MOBILE -DHAVE_GRIFFIN -DANDROID -DHAVE_DYNAMIC -DHAVE_OPENGL -DHAVE_FBO -DHAVE_OVERLAY -DHAVE_OPENGLES -DHAVE_VID_CONTEXT -DHAVE_OPENGLES2 -DGLSL_DEBUG -DHAVE_GLSL -DHAVE_RGUI -DHAVE_SCREENSHOTS -DWANT_MINIZ -DHAVE_ZLIB -DINLINE=inline -DLSB_FIRST -DHAVE_THREADS -D__LIBRETRO__ -std=gnu99 -I../../../deps/miniz LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -landroid -lEGL -lGLESv2 $(LOGGER_LDLIBS) -ldl diff --git a/android/native/jni/input_android.c b/android/native/jni/input_android.c index a6410eaf40..625f172d77 100644 --- a/android/native/jni/input_android.c +++ b/android/native/jni/input_android.c @@ -1855,7 +1855,7 @@ static int16_t android_input_state(void *data, const struct retro_keybind **bind static bool android_input_key_pressed(void *data, int key) { - return ((g_extern.lifecycle_state | driver.overlay_state) & (1ULL << key)); + return ((g_extern.lifecycle_state | driver.overlay_state.buttons) & (1ULL << key)); } static void android_input_free_input(void *data) diff --git a/android/phoenix/AndroidManifest.xml b/android/phoenix/AndroidManifest.xml index c44b3a1e1e..051ccabc81 100644 --- a/android/phoenix/AndroidManifest.xml +++ b/android/phoenix/AndroidManifest.xml @@ -1,6 +1,6 @@ @@ -29,6 +29,7 @@ + diff --git a/android/phoenix/res/xml/prefs.xml b/android/phoenix/res/xml/prefs.xml index 4d696d8411..94c6e5b452 100644 --- a/android/phoenix/res/xml/prefs.xml +++ b/android/phoenix/res/xml/prefs.xml @@ -43,6 +43,7 @@ @@ -123,17 +124,25 @@ - + + + + + + + + + + + = 17) { + int buffersize = getLowLatencyBufferSize(); + + boolean lowLatency = hasLowLatencyAudio(); + Log.i(TAG, "Audio is low latency: " + (lowLatency ? "yes" : "no")); + + if (lowLatency && !prefs.getBoolean("audio_high_latency", false)) { + config.setInt("audio_latency", 64); + config.setInt("audio_block_frames", buffersize); + } else { + config.setInt("audio_latency", prefs.getBoolean( + "audio_high_latency", false) ? 160 : 64); + config.setInt("audio_block_frames", 0); + } + } else { + config.setInt("audio_latency", + prefs.getBoolean("audio_high_latency", false) ? 160 : 64); + } + config.setBoolean("audio_enable", prefs.getBoolean("audio_enable", true)); config.setBoolean("video_smooth", @@ -286,10 +411,12 @@ public class MainMenuActivity extends PreferenceActivity { "0"))); config.setDouble("video_refresh_rate", - MainMenuActivity.getRefreshRate()); + getRefreshRate()); config.setBoolean("video_threaded", prefs.getBoolean("video_threaded", true)); + // Refactor these weird values - 'full', 'auto', 'square', whatever - + // go by what we have in RGUI - makes maintaining state easier too String aspect = prefs.getString("video_aspect_ratio", "auto"); if (aspect.equals("full")) { config.setBoolean("video_force_aspect", false); @@ -317,9 +444,10 @@ public class MainMenuActivity extends PreferenceActivity { && new File(shaderPath).exists()); boolean useOverlay = prefs.getBoolean("input_overlay_enable", true); + config.setBoolean("input_overlay_enable", useOverlay); // Not used by RetroArch directly. if (useOverlay) { String overlayPath = prefs - .getString("input_overlay", (MainMenuActivity.getInstance() + .getString("input_overlay", (getInstance() .getApplicationInfo().dataDir) + "/overlays/snes-landscape.cfg"); config.setString("input_overlay", overlayPath); @@ -328,7 +456,6 @@ public class MainMenuActivity extends PreferenceActivity { } else { config.setString("input_overlay", ""); } - config.setString( "savefile_directory", prefs.getBoolean("savefile_directory_enable", false) ? prefs @@ -345,7 +472,7 @@ public class MainMenuActivity extends PreferenceActivity { config.setBoolean("video_font_enable", prefs.getBoolean("video_font_enable", true)); - config.setString("game_history_path", MainMenuActivity.getInstance() + config.setString("game_history_path", getInstance() .getApplicationInfo().dataDir + "/retroarch-history.txt"); for (int i = 1; i <= 4; i++) { @@ -359,11 +486,10 @@ public class MainMenuActivity extends PreferenceActivity { } } - String confPath = getDefaultConfigPath(); try { - config.write(new File(confPath)); + config.write(new File(path)); } catch (IOException e) { - Log.e(TAG, "Failed to save config file to: " + confPath); + Log.e(TAG, "Failed to save config file to: " + path); } } @@ -499,20 +625,28 @@ public class MainMenuActivity extends PreferenceActivity { dialog.show(); } + + public static SharedPreferences getPreferences() { + return PreferenceManager.getDefaultSharedPreferences(getInstance().getBaseContext()); + } public void setModule(String core_path, String core_name) { + updateConfigFile(); + libretro_path = core_path; libretro_name = core_name; - File libretro_path_file = new File(core_path); - setCoreTitle((libretro_path_file.isDirectory() == true) ? "No core" - : core_name); - SharedPreferences prefs = PreferenceManager - .getDefaultSharedPreferences(getBaseContext()); + SharedPreferences prefs = getPreferences(); SharedPreferences.Editor edit = prefs.edit(); edit.putString("libretro_path", libretro_path); edit.putString("libretro_name", libretro_name); edit.commit(); + + if (usePerCoreConfig()) + refreshPreferenceScreen(); + else { + setCoreTitle(libretro_name); // this still needs to be applied + } } public void setCoreTitle(String core_name) { @@ -538,8 +672,7 @@ public class MainMenuActivity extends PreferenceActivity { @Override public void onClick(DialogInterface dialog, int which) { - SharedPreferences prefs = PreferenceManager - .getDefaultSharedPreferences(getBaseContext()); + SharedPreferences prefs = getPreferences(); SharedPreferences.Editor edit = prefs .edit(); edit.putString("video_refresh_rate", Double @@ -562,8 +695,7 @@ public class MainMenuActivity extends PreferenceActivity { @Override public void onClick(DialogInterface dialog, int which) { - SharedPreferences prefs = PreferenceManager - .getDefaultSharedPreferences(getBaseContext()); + SharedPreferences prefs = getPreferences(); SharedPreferences.Editor edit = prefs .edit(); edit.putBoolean("input_overlay_enable", @@ -585,8 +717,7 @@ public class MainMenuActivity extends PreferenceActivity { @Override public void onClick(DialogInterface dialog, int which) { - SharedPreferences prefs = PreferenceManager - .getDefaultSharedPreferences(getBaseContext()); + SharedPreferences prefs = getPreferences(); SharedPreferences.Editor edit = prefs .edit(); edit.putBoolean("input_overlay_enable", @@ -598,6 +729,27 @@ public class MainMenuActivity extends PreferenceActivity { }); alert.show(); retval = true; + } else if (android.os.Build.MODEL.equals("R800x")) { + AlertDialog.Builder alert = new AlertDialog.Builder(this) + .setTitle("Xperia Play detected") + .setMessage(message) + .setPositiveButton("OK", + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, + int which) { + SharedPreferences prefs = getPreferences(); + SharedPreferences.Editor edit = prefs + .edit(); + edit.putBoolean("input_overlay_enable", + false); + edit.putBoolean("input_autodetect_enable", + true); + edit.commit(); + } + }); + alert.show(); + retval = true; } else if (android.os.Build.ID.equals("JSS15J")) { AlertDialog.Builder alert = new AlertDialog.Builder(this) .setTitle("Nexus 7 2013 detected") @@ -607,8 +759,7 @@ public class MainMenuActivity extends PreferenceActivity { @Override public void onClick(DialogInterface dialog, int which) { - SharedPreferences prefs = PreferenceManager - .getDefaultSharedPreferences(getBaseContext()); + SharedPreferences prefs = getPreferences(); SharedPreferences.Editor edit = prefs .edit(); edit.putString("video_refresh_rate", Double @@ -626,6 +777,8 @@ public class MainMenuActivity extends PreferenceActivity { "Device either not detected in list or doesn't have any optimal settings in our database.", Toast.LENGTH_SHORT).show(); } + + refreshPreferenceScreen(); return retval; } @@ -668,7 +821,7 @@ public class MainMenuActivity extends PreferenceActivity { myIntent.putExtra("ROM", data.getStringExtra("PATH")); myIntent.putExtra("LIBRETRO", libretro_path); myIntent.putExtra("CONFIGFILE", - MainMenuActivity.getDefaultConfigPath()); + getDefaultConfigPath()); myIntent.putExtra("IME", current_ime); startActivity(myIntent); } diff --git a/android/phoenix/src/org/retroarch/browser/ROMActivity.java b/android/phoenix/src/org/retroarch/browser/ROMActivity.java index ed6cf81412..27a9e084b2 100644 --- a/android/phoenix/src/org/retroarch/browser/ROMActivity.java +++ b/android/phoenix/src/org/retroarch/browser/ROMActivity.java @@ -4,13 +4,12 @@ import java.io.File; import android.content.SharedPreferences; import android.os.Bundle; -import android.preference.PreferenceManager; public class ROMActivity extends DirectoryActivity { @Override public void onCreate(Bundle savedInstanceState) { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext()); + SharedPreferences prefs = MainMenuActivity.getPreferences(); String startPath = prefs.getString("rgui_browser_directory", ""); if (!startPath.isEmpty() && new File(startPath).exists()) super.setStartDirectory(startPath); diff --git a/android/phoenix/src/org/retroarch/browser/RefreshRateSetOS.java b/android/phoenix/src/org/retroarch/browser/RefreshRateSetOS.java index b9479c80eb..cbef30d588 100644 --- a/android/phoenix/src/org/retroarch/browser/RefreshRateSetOS.java +++ b/android/phoenix/src/org/retroarch/browser/RefreshRateSetOS.java @@ -4,7 +4,6 @@ import android.app.Activity; import android.content.Context; import android.content.SharedPreferences; import android.os.Bundle; -import android.preference.PreferenceManager; import android.view.Display; import android.view.WindowManager; import android.widget.Toast; @@ -18,7 +17,7 @@ public class RefreshRateSetOS extends Activity { final WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE); final Display display = wm.getDefaultDisplay(); double rate = display.getRefreshRate(); - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext()); + SharedPreferences prefs = MainMenuActivity.getPreferences(); SharedPreferences.Editor edit = prefs.edit(); edit.putString("video_refresh_rate", Double.valueOf(rate).toString()); edit.commit(); diff --git a/android/phoenix/src/org/retroarch/browser/ReportIME.java b/android/phoenix/src/org/retroarch/browser/ReportIME.java index 1a85b393ee..c8f5d463b4 100644 --- a/android/phoenix/src/org/retroarch/browser/ReportIME.java +++ b/android/phoenix/src/org/retroarch/browser/ReportIME.java @@ -2,6 +2,7 @@ package org.retroarch.browser; import android.app.Activity; import android.app.AlertDialog; +import android.content.DialogInterface; import android.os.Bundle; import android.provider.Settings; @@ -9,7 +10,27 @@ public class ReportIME extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - String current_ime = Settings.Secure.getString(getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD); - new AlertDialog.Builder(this).setMessage(current_ime).setNeutralButton("Close", null).show(); + String current_ime = Settings.Secure.getString(getContentResolver(), + Settings.Secure.DEFAULT_INPUT_METHOD); + + final Activity ctx = this; + AlertDialog.Builder dialog = new AlertDialog.Builder(this) + .setMessage(current_ime) + .setNeutralButton("Close", + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, + int which) { + ctx.finish(); + } + }).setCancelable(true) + .setOnCancelListener(new DialogInterface.OnCancelListener() { + @Override + public void onCancel(DialogInterface dialog) { + ctx.finish(); + } + }); + + dialog.show(); } } diff --git a/apple/OSX/en.lproj/InputBinder.xib b/apple/OSX/en.lproj/InputBinder.xib new file mode 100644 index 0000000000..1dc324d900 --- /dev/null +++ b/apple/OSX/en.lproj/InputBinder.xib @@ -0,0 +1,384 @@ + + + + 1080 + 12E55 + 3084 + 1187.39 + 626.00 + + com.apple.InterfaceBuilder.CocoaPlugin + 3084 + + + IBNSLayoutConstraint + NSButton + NSButtonCell + NSCustomObject + NSTextField + NSTextFieldCell + NSView + NSWindowTemplate + + + com.apple.InterfaceBuilder.CocoaPlugin + + + PluginDependencyRecalculationVersion + + + + + NSWindowController + + + FirstResponder + + + NSApplication + + + 15 + 2 + {{196, 240}, {480, 109}} + 544735232 + Window + RAInputBinder + + + + + 256 + + + + 268 + {{17, 72}, {446, 17}} + + + + _NS:1535 + YES + + 68157504 + 138413056 + Press a Key, Joystick Button, or move a Joystick Axis + + LucidaGrande + 13 + 1044 + + _NS:1535 + + + 6 + System + controlColor + + 3 + MC42NjY2NjY2NjY3AA + + + + 6 + System + controlTextColor + + 3 + MAA + + + + NO + + + + 268 + {{384, 13}, {82, 32}} + + + _NS:9 + YES + + 67108864 + 134217728 + Cancel + + _NS:9 + + -2038284288 + 129 + + + Gw + 200 + 25 + + NO + + + {480, 109} + + + + + {{0, 0}, {2560, 1418}} + {10000000000000, 10000000000000} + YES + + + + + + + window + + + + 3 + + + + goAway: + + + + 20 + + + + + + 0 + + + + + + -2 + + + File's Owner + + + -1 + + + First Responder + + + -3 + + + Application + + + 1 + + + + + + + + 2 + + + + + 4 + 0 + + 4 + 1 + + 20 + + 1000 + + 8 + 29 + 3 + + + + 6 + 0 + + 6 + 1 + + 20 + + 1000 + + 8 + 29 + 3 + + + + 3 + 0 + + 3 + 1 + + 20 + + 1000 + + 8 + 29 + 3 + + + + 6 + 0 + + 6 + 1 + + 20 + + 1000 + + 8 + 29 + 3 + + + + 5 + 0 + + 5 + 1 + + 20 + + 1000 + + 8 + 29 + 3 + + + + + + + + 4 + + + + + + + + 5 + + + + + 9 + + + + + 10 + + + + + 11 + + + + + 12 + + + + + + + + 13 + + + + + 14 + + + + + 15 + + + + + + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + {{357, 418}, {480, 270}} + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + + + + + + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + + + + + 20 + + + + + NSLayoutConstraint + NSObject + + IBProjectSource + ./Classes/NSLayoutConstraint.h + + + + RAInputBinder + NSWindow + + IBProjectSource + ./Classes/RAInputBinder.h + + + + + 0 + IBCocoaFramework + YES + 3 + YES + + diff --git a/apple/OSX/en.lproj/MainMenu.xib b/apple/OSX/en.lproj/MainMenu.xib index 4ebd04b3f9..908154abc8 100644 --- a/apple/OSX/en.lproj/MainMenu.xib +++ b/apple/OSX/en.lproj/MainMenu.xib @@ -2,9 +2,9 @@ 1080 - 12D78 + 12E55 3084 - 1187.37 + 1187.39 626.00 com.apple.InterfaceBuilder.CocoaPlugin @@ -297,6 +297,28 @@ + + + Go + + 2147483647 + + + submenuAction: + + Go + + + + Cores Directory + + 2147483647 + + + + + + Window @@ -397,11 +419,9 @@ - + 256 {480, 360} - - {{0, 0}, {2560, 1418}} {10000000000000, 10000000000000} @@ -420,7 +440,7 @@ - + 256 @@ -428,7 +448,6 @@ 268 {{17, 72}, {242, 17}} - _NS:1535 YES @@ -470,7 +489,6 @@ 268 {{20, 45}, {239, 26}} - _NS:9 1 @@ -499,7 +517,7 @@ 274 - {13, 0} + {15, 0} _NS:24 @@ -508,7 +526,7 @@ YES - 10 + 12 10 1000 @@ -559,7 +577,7 @@ 19 tableViewAction: - -765427712 + -767524864 @@ -578,7 +596,6 @@ 268 {{180, 13}, {82, 32}} - _NS:9 YES @@ -599,8 +616,6 @@ {276, 89} - - _NS:21 @@ -787,6 +802,14 @@ 584 + + + showCoresDirectory: + + + + 588 + @@ -823,6 +846,7 @@ + @@ -1339,6 +1363,27 @@ + + 585 + + + + + + + + 586 + + + + + + + + 587 + + + @@ -1417,6 +1462,9 @@ com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin @@ -1428,7 +1476,7 @@ - 584 + 588 @@ -1443,6 +1491,39 @@ RetroArch_OSX NSObject + + id + id + id + + + + coreWasChosen: + id + + + showCoresDirectory: + id + + + showPreferences: + id + + + + NSWindow + NSWindow + + + + _coreSelectSheet + NSWindow + + + window + NSWindow + + IBProjectSource ./Classes/RetroArch_OSX.h diff --git a/apple/OSX/Settings.xib b/apple/OSX/en.lproj/Settings.xib similarity index 52% rename from apple/OSX/Settings.xib rename to apple/OSX/en.lproj/Settings.xib index 1d5c744237..03a3147273 100644 --- a/apple/OSX/Settings.xib +++ b/apple/OSX/en.lproj/Settings.xib @@ -2,9 +2,9 @@ 1080 - 12D78 + 12E55 3084 - 1187.37 + 1187.39 626.00 com.apple.InterfaceBuilder.CocoaPlugin @@ -36,7 +36,7 @@ - NSObject + NSWindowController FirstResponder @@ -45,11 +45,11 @@ NSApplication - 15 + 3 2 - {{196, 240}, {671, 597}} + {{196, 240}, {759, 597}} 544735232 - Window + RetroArch Settings NSWindow @@ -69,10 +69,10 @@ 256 - {148, 514} + {148, 555} - + _NS:13 YES NO @@ -164,7 +164,7 @@ MC41AA - 17 + 30 104857600 @@ -176,7 +176,7 @@ 1 - {{1, 1}, {148, 514}} + {{1, 1}, {148, 555}} @@ -213,7 +213,7 @@ 0.99404761904761907 - {{20, 61}, {150, 516}} + {{20, 20}, {150, 557}} @@ -222,35 +222,11 @@ - QSAAAEEgAABBmAAAQZgAAA + QSAAAEEgAABCAAAAQgAAAA 0.25 4 1 - - - 268 - {{582, 13}, {75, 32}} - - - _NS:9 - YES - - 67108864 - 134217728 - Close - - _NS:9 - - -2038284288 - 129 - - - 200 - 25 - - NO - 268 @@ -263,7 +239,7 @@ 256 - {471, 498} + {559, 539} @@ -272,25 +248,27 @@ NO YES - + 256 - {471, 17} - + {559, 17} + - + - - + + -2147483392 {{224, 0}, {16, 17}} + + _NS:18 title - 101 + 276 16 1000 @@ -320,7 +298,7 @@ accessory - 364 + 277 40 1000 @@ -350,7 +328,7 @@ 2 - 17 + 30 306216960 @@ -361,9 +339,10 @@ 0 1 NO + 4 - {{1, 17}, {471, 498}} + {{1, 17}, {559, 539}} @@ -383,29 +362,29 @@ NO _doScroller: - 0.99799599198396793 + 0.9721706864564007 -2147483392 - {{1, 499}, {471, 16}} + {{1, 541}, {486, 15}} - + _NS:60 NO 1 _doScroller: - 0.82055749128919864 + 0.55290102389078499 - + 2304 - {{1, 0}, {471, 17}} + {{1, 0}, {559, 17}} @@ -413,24 +392,26 @@ 4 + - {{178, 61}, {473, 516}} + {{178, 20}, {561, 557}} - + _NS:9 133682 - - QSAAAEEgAABBmAAAQZgAAA + + + QSAAAEEgAABCAAAAQgAAAA 0.25 4 1 - {671, 597} + {759, 597} @@ -445,6 +426,22 @@ + + + window + + + + 422 + + + + delegate + + + + 356 + dataSource @@ -477,14 +474,6 @@ 104 - - - close: - - - - 122 - _outline @@ -493,45 +482,6 @@ 180 - - - textField - - - 274 - - - - 266 - {145, 17} - - - {250, 750} - YES - - 67108928 - 272631808 - Table View Cell - - - - 6 - System - controlColor - - - - - NO - - - {{1, 1}, {145, 17}} - - - - - 108 - dataSource @@ -558,7 +508,7 @@ 266 - {101, 17} + {{0, 7}, {276, 17}} {250, 750} @@ -566,22 +516,43 @@ 67108928 272631808 - Table View Cell + - + + 6 + System + controlColor + + NO - {{1, 1}, {101, 17}} + {{1, 1}, {276, 30}} 168 + + + value: stringValue + + + + + + value: stringValue + value + stringValue + 2 + + + 319 + textField @@ -592,15 +563,15 @@ 266 - {364, 17} + {{0, 7}, {277, 17}} - + {250, 750} YES - 67108928 - 272631808 - Table View Cell + 337641537 + 272631872 + String @@ -609,13 +580,339 @@ NO - {{105, 1}, {364, 17}} + {{280, 1}, {277, 30}} 172 + + + value: stringValue + + + + + + value: stringValue + value + stringValue + 2 + + + 240 + + + + textField + + + 274 + + + + 266 + {{0, 7}, {277, 17}} + + + {250, 750} + YES + + 337641537 + 272631872 + Int + + + + + + NO + + + {{280, 65}, {277, 30}} + + + + + 227 + + + + value: numericValue + + + + + + value: numericValue + value + numericValue + 2 + + + 241 + + + + value: booleanValue + + + 268 + {{1, 6}, {61, 18}} + + + _NS:9 + YES + + -2080374784 + 268435456 + + + _NS:9 + + 1211912448 + 2 + + NSImage + NSSwitch + + + NSSwitch + + + + 200 + 25 + + NO + + + + 274 + + + + {{280, 33}, {277, 30}} + + + + + + value: booleanValue + value + booleanValue + 2 + + + 255 + + + + textField + + + 274 + + + + 266 + {{0, 7}, {233, 17}} + + + {250, 750} + YES + + 337641537 + 272631872 + + + + + + + NO + + + + 268 + {{232, -2}, {48, 32}} + + + _NS:9 + YES + + 67108864 + 134217728 + ... + + _NS:9 + + -2038284288 + 129 + + + 200 + 25 + + NO + + + {{280, 97}, {277, 30}} + + + + + 298 + + + + doBrowse: + + + + 312 + + + + value: stringValue + + + + + + value: stringValue + value + stringValue + 2 + + + 314 + + + + textField + + + 274 + + + + 266 + {{0, 7}, {145, 17}} + + + {250, 750} + YES + + 67108928 + 272631808 + + + + + + + NO + + + {{1, 1}, {145, 30}} + + + + + 326 + + + + value: stringValue + + + + + + value: stringValue + value + stringValue + 2 + + + 327 + + + + doGetBind: + + + 274 + + + + 268 + {{232, -2}, {48, 32}} + + + _NS:9 + YES + + 67108864 + 134217728 + ... + + _NS:9 + + -2038284288 + 129 + + + 200 + 25 + + NO + + + + 266 + {{0, 7}, {233, 17}} + + + {250, 750} + YES + + 67108928 + 272631872 + + + + + + + NO + + + {{280, 129}, {277, 30}} + + + + + 564 + + + + value: stringValue + + + + + + value: stringValue + value + stringValue + 2 + + + 578 + @@ -655,30 +952,12 @@ 2 - - - - - 6 - 0 - - 6 - 1 - - 20 - - 1000 - - 8 - 29 - 3 - 4 0 - + 4 1 @@ -690,39 +969,7 @@ 29 3 - - - 3 - 0 - - 4 - 1 - - 20 - - 1000 - - 6 - 24 - 3 - - - - 4 - 0 - - 4 - 1 - - 0.0 - - 1000 - - 6 - 24 - 2 - - + 6 0 @@ -738,6 +985,22 @@ 29 3 + + + 5 + 0 + + 6 + 1 + + 8 + + 1000 + + 6 + 24 + 3 + 5 @@ -770,28 +1033,12 @@ 29 3 - - - 5 + + + 4 0 - 6 - 1 - - 8 - - 1000 - - 6 - 24 - 3 - - - - 3 - 0 - - 3 + 4 1 20 @@ -818,6 +1065,23 @@ 29 3 + + + 3 + 0 + + 3 + 1 + + 20 + + 1000 + + 8 + 29 + 3 + + @@ -854,7 +1118,7 @@ - + @@ -868,88 +1132,6 @@ - - 105 - - - - - - 10 - 0 - - 10 - 1 - - 0.0 - - 1000 - - 5 - 22 - 2 - - - - 6 - 0 - - 6 - 1 - - 3 - - 1000 - - 8 - 29 - 3 - - - - 5 - 0 - - 5 - 1 - - 3 - - 1000 - - 8 - 29 - 3 - - - - - - 106 - - - - - - - - 107 - - - - - 116 - - - - - - - - 117 - - - 148 @@ -989,20 +1171,6 @@ - - 154 - - - - - - - - - 155 - - - 156 @@ -1013,23 +1181,23 @@ - - - 10 + + + 6 0 - - 10 + + 6 1 - 0.0 + 3 1000 - 5 - 22 - 2 + 8 + 29 + 3 - + 5 0 @@ -1045,6 +1213,22 @@ 29 3 + + + 10 + 0 + + 10 + 1 + + 0.0 + + 1000 + + 5 + 22 + 2 + @@ -1062,27 +1246,511 @@ - 169 - + 320 + - - - + + + 10 + 0 + + 10 + 1 + + 0.0 + + 1000 + + 5 + 22 + 2 + + + + 5 + 0 + + 5 + 1 + + 3 + + 1000 + + 8 + 29 + 3 + + + 6 0 - + 6 1 3 1000 - + 8 29 3 - + + + + + + 324 + + + + + + + + 325 + + + + + 179 + + + + + 353 + + + + + 276 + + + + + 272 + + + + + 271 + + + + + 267 + + + + + 354 + + + + + 273 + + + + + 268 + + + + + 317 + + + + + 281 + + + + + 280 + + + + + 323 + + + + + 322 + + + + + 321 + + + + + 154 + + + + + + + + + + + + + 328 + + + + + 5 + 0 + + 6 + 1 + + 8 + + 1000 + + 6 + 24 + 3 + + + + 6 + 0 + + 6 + 1 + + 3 + + 1000 + + 8 + 29 + 3 + + + + 10 + 0 + + 10 + 1 + + 0.0 + + 1000 + + 5 + 22 + 2 + + + + 5 + 0 + + 5 + 1 + + 3 + + 1000 + + 8 + 29 + 3 + + + + 10 + 0 + + 10 + 1 + + 0.0 + + 1000 + + 6 + 24 + 2 + + + + + + + + 335 + + + + + + 7 + 0 + + 0 + 1 + + 36 + + 1000 + + 3 + 9 + 1 + + + + + + 345 + + + + + 336 + + + + + 242 + + + + + + 10 + 0 + + 10 + 1 + + 0.0 + + 1000 + + 5 + 22 + 2 + + + + 5 + 0 + + 5 + 1 + + 3 + + 1000 + + 8 + 29 + 3 + + + + + + 249 + + + + + + 7 + 0 + + 0 + 1 + + 57 + + 1000 + + 3 + 9 + 1 + + + + + + 291 + + + + + 290 + + + + + 292 + + + + + 250 + + + + + 295 + + + + + 5 + 0 + + 6 + 1 + + 8 + + 1000 + + 6 + 24 + 3 + + + + 6 + 0 + + 6 + 1 + + 3 + + 1000 + + 8 + 29 + 3 + + + + 10 + 0 + + 10 + 1 + + 0.0 + + 1000 + + 5 + 22 + 2 + + + + 10 + 0 + + 10 + 1 + + 0.0 + + 1000 + + 6 + 24 + 2 + + + + 5 + 0 + + 5 + 1 + + 3 + + 1000 + + 8 + 29 + 3 + + + + + + + + 296 + + + + + + + + 297 + + + + + 303 + + + + + + 7 + 0 + + 0 + 1 + + 36 + + 1000 + + 3 + 9 + 1 + + + + + + 309 + + + + + 304 + + + + + 169 + + + + 5 0 @@ -1098,7 +1766,7 @@ 29 3 - + 10 0 @@ -1114,6 +1782,22 @@ 22 2 + + + 6 + 0 + + 6 + 1 + + 3 + + 1000 + + 8 + 29 + 3 + @@ -1125,105 +1809,177 @@ + + 289 + + + + + 288 + + + + + 287 + + + 171 - 179 - - + 224 + + + + + + 6 + 0 + + 6 + 1 + + 3 + + 1000 + + 8 + 29 + 3 + + + + 10 + 0 + + 10 + 1 + + 0.0 + + 1000 + + 5 + 22 + 2 + + + + 5 + 0 + + 5 + 1 + + 3 + + 1000 + + 8 + 29 + 3 + + + - 195 - - + 225 + + + + + - 196 - - + 294 + + - 197 - - + 293 + + - 198 - - + 285 + + - 199 - - + 226 + + - 200 - - + 155 + + - 201 - - + 311 + + - 202 - - + 300 + + - 203 - - + 476 + + - 204 - - + 547 + + - 205 - - + 548 + + - 206 - - + 561 + + - 207 - - + 565 + + + + + - 208 - - + 566 + + - 209 - - + 574 + + - 210 - - + 575 + + - 211 - - + 576 + + - 212 - - + 572 + + @@ -1234,18 +1990,6 @@ com.apple.InterfaceBuilder.CocoaPlugin {{357, 418}, {480, 270}} - - - - - - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin @@ -1261,58 +2005,146 @@ + + + + com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin + RASettingCell - - + + + com.apple.InterfaceBuilder.CocoaPlugin + RALabelSetting com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin + RASettingCell - - - + + + com.apple.InterfaceBuilder.CocoaPlugin + RAStringSetting com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - + + - - + - - - + + - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin + RASettingCell + + + + + + com.apple.InterfaceBuilder.CocoaPlugin + RANumericSetting + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + RASettingCell + + + + + com.apple.InterfaceBuilder.CocoaPlugin + RABooleanSetting + + + + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + RASettingCell + + + + + + + + com.apple.InterfaceBuilder.CocoaPlugin + RAPathSetting + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + + + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + RASettingCell + + + + + + com.apple.InterfaceBuilder.CocoaPlugin + RALabelSetting + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + RASettingCell + + + + + + + + + com.apple.InterfaceBuilder.CocoaPlugin + RABindSetting + + + + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin @@ -1322,16 +2154,26 @@ com.apple.InterfaceBuilder.CocoaPlugin - + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin com.apple.InterfaceBuilder.CocoaPlugin - 212 + 581 @@ -1344,25 +2186,37 @@ - RASettingsDelegate - NSObject - - close: - id + RASettingCell + NSTableCellView + + numericValue + NSNumber - - close: - - close: - id + + numericValue + + numericValue + NSNumber + + IBProjectSource + ./Classes/RASettingCell.h + + + + RASettingsDelegate + NSObject + NSWindow NSOutlineView NSTableView - NSWindow + + _inputWindow + NSWindow + _outline NSOutlineView @@ -1371,10 +2225,6 @@ _table NSTableView - - _window - NSWindow - IBProjectSource @@ -1387,6 +2237,10 @@ IBCocoaFramework YES 3 + + NSSwitch + {15, 15} + YES diff --git a/apple/OSX/hid_pad.c b/apple/OSX/hid_pad.c index 33bb819b9e..39cee0feed 100644 --- a/apple/OSX/hid_pad.c +++ b/apple/OSX/hid_pad.c @@ -14,14 +14,44 @@ */ #include -#include "../RetroArch/apple_input.h" +#include "apple/common/apple_input.h" // NOTE: I pieced this together through trial and error, any corrections are welcome static IOHIDManagerRef g_hid_manager; -static uint32_t g_num_pads; +static uint32_t g_pad_slots; -static void hid_input_callback(void* inContext, IOReturn inResult, void* inSender, IOHIDValueRef inIOHIDValueRef) +#define HID_ISSET(t, x) (t & (1 << x)) +#define HID_SET(t, x) { t |= (1 << x); } +#define HID_CLEAR(t, x) { t &= ~(1 << x); } + +// Set the LEDs on PS3 controllers, if slot >= MAX_PADS the LEDs will be cleared +static void osx_pad_set_leds(IOHIDDeviceRef device, uint32_t slot) +{ + char buffer[1024]; + + CFStringRef device_name = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey)); + if (device_name) + { + CFStringGetCString(device_name, buffer, 1024, kCFStringEncodingUTF8); + + if (strncmp(buffer, "PLAYSTATION(R)3 Controller", 1024) == 0) + { + static uint8_t report_buffer[] = { + 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x27, + 0x10, 0x00, 0x32, 0xff, 0x27, 0x10, 0x00, 0x32, 0xff, 0x27, 0x10, 0x00, + 0x32, 0xff, 0x27, 0x10, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + report_buffer[10] = (slot >= MAX_PADS) ? 0 : (1 << (slot + 1)); + + IOHIDDeviceSetReport(device, kIOHIDReportTypeOutput, 0x01, report_buffer, sizeof(report_buffer)); + } + } +} + +static void hid_device_input_callback(void* inContext, IOReturn inResult, void* inSender, IOHIDValueRef inIOHIDValueRef) { IOHIDElementRef element = IOHIDValueGetElement(inIOHIDValueRef); IOHIDDeviceRef device = IOHIDElementGetDevice(element); @@ -82,25 +112,49 @@ static void hid_input_callback(void* inContext, IOReturn inResult, void* inSende } } -static void hid_device_attached(void* inContext, IOReturn inResult, void* inSender, IOHIDDeviceRef inDevice) +static void hid_device_removed(void* inContext, IOReturn inResult, void* inSender) { - void* context = 0; + IOHIDDeviceRef inDevice = (IOHIDDeviceRef)inSender; if (IOHIDDeviceConformsTo(inDevice, kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick)) { - if (g_num_pads > 4) - return; - context = (void*)(g_num_pads++); + uint32_t pad_index = (uint32_t)inContext; + + if (pad_index < MAX_PADS) + { + HID_CLEAR(g_pad_slots, pad_index); + + g_current_input_data.pad_buttons[pad_index] = 0; + memset(g_current_input_data.pad_axis[pad_index], 0, sizeof(g_current_input_data.pad_axis)); + } } - IOHIDDeviceOpen(inDevice, kIOHIDOptionsTypeNone); - IOHIDDeviceScheduleWithRunLoop(inDevice, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); - IOHIDDeviceRegisterInputValueCallback(inDevice, hid_input_callback, context); + osx_pad_set_leds(inDevice, MAX_PADS); + IOHIDDeviceClose(inDevice, kIOHIDOptionsTypeNone); } -static void hid_device_removed(void* inContext, IOReturn inResult, void* inSender, IOHIDDeviceRef inDevice) +static void hid_manager_device_attached(void* inContext, IOReturn inResult, void* inSender, IOHIDDeviceRef inDevice) { - IOHIDDeviceClose(inDevice, kIOHIDOptionsTypeNone); + uint32_t pad_index = 0; + if (IOHIDDeviceConformsTo(inDevice, kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick)) + { + if ((g_pad_slots & 0xF) == 0xF) + return; + + for (pad_index = 0; pad_index != MAX_PADS; pad_index ++) + if (!HID_ISSET(g_pad_slots, pad_index)) + { + HID_SET(g_pad_slots, pad_index); + break; + } + } + + IOHIDDeviceOpen(inDevice, kIOHIDOptionsTypeNone); + IOHIDDeviceScheduleWithRunLoop(inDevice, CFRunLoopGetCurrent(), kCFRunLoopCommonModes); + IOHIDDeviceRegisterInputValueCallback(inDevice, hid_device_input_callback, (void*)pad_index); + IOHIDDeviceRegisterRemovalCallback(inDevice, hid_device_removed, (void*)pad_index); + + osx_pad_set_leds(inDevice, pad_index); } static CFMutableDictionaryRef build_matching_dictionary(uint32_t page, uint32_t use) @@ -137,9 +191,8 @@ void osx_pad_init() IOHIDManagerSetDeviceMatchingMultiple(g_hid_manager, matcher); CFRelease(matcher); - IOHIDManagerRegisterDeviceMatchingCallback(g_hid_manager, hid_device_attached, 0); - IOHIDManagerRegisterDeviceRemovalCallback(g_hid_manager, hid_device_removed, 0); - IOHIDManagerScheduleWithRunLoop(g_hid_manager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + IOHIDManagerRegisterDeviceMatchingCallback(g_hid_manager, hid_manager_device_attached, 0); + IOHIDManagerScheduleWithRunLoop(g_hid_manager, CFRunLoopGetMain(), kCFRunLoopCommonModes); IOHIDManagerOpen(g_hid_manager, kIOHIDOptionsTypeNone); } @@ -150,7 +203,7 @@ void osx_pad_quit() if (g_hid_manager) { IOHIDManagerClose(g_hid_manager, kIOHIDOptionsTypeNone); - IOHIDManagerUnscheduleFromRunLoop(g_hid_manager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + IOHIDManagerUnscheduleFromRunLoop(g_hid_manager, CFRunLoopGetCurrent(), kCFRunLoopCommonModes); CFRelease(g_hid_manager); } diff --git a/apple/OSX/platform.h b/apple/OSX/platform.h index b35cf774af..b01f6526f5 100644 --- a/apple/OSX/platform.h +++ b/apple/OSX/platform.h @@ -37,6 +37,10 @@ - (void)loadingCore:(RAModuleInfo*)core withFile:(const char*)file; - (void)unloadingCore:(RAModuleInfo*)core; +@property (strong, nonatomic) NSString* configDirectory; // e.g. /var/mobile/Documents/.RetroArch +@property (strong, nonatomic) NSString* globalConfigFile; // e.g. /var/mobile/Documents/.RetroArch/retroarch.cfg +@property (strong, nonatomic) NSString* coreDirectory; // e.g. /Applications/RetroArch.app/modules + @end #endif diff --git a/apple/OSX/platform.m b/apple/OSX/platform.m index 09580ca852..f911432227 100644 --- a/apple/OSX/platform.m +++ b/apple/OSX/platform.m @@ -18,15 +18,7 @@ #import "RetroArch_Apple.h" #include "rarch_wrapper.h" -#include "../RetroArch/apple_input.h" - -// If USE_XATTR is defined any loaded file will get a com.RetroArch.Core extended attribute -// specifying which core was used to load. -//#define USE_XATTR - -#if defined(USE_XATTR) -#include "sys/xattr.h" -#endif +#include "apple/common/apple_input.h" #include "file.h" @@ -66,6 +58,11 @@ apple_platform = self; _loaded = true; + NSArray* paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES); + self.configDirectory = [paths[0] stringByAppendingPathComponent:@"RetroArch"]; + self.globalConfigFile = [NSString stringWithFormat:@"%@/retroarch.cfg", self.configDirectory]; + self.coreDirectory = [NSBundle.mainBundle.bundlePath stringByAppendingPathComponent:@"Contents/Resources/modules"]; + [window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary]; RAGameView.get.frame = [window.contentView bounds]; @@ -76,16 +73,16 @@ // Create core select list NSComboBox* cb = (NSComboBox*)[_coreSelectSheet.contentView viewWithTag:1]; - for (RAModuleInfo* i in RAModuleInfo.getModules) + for (RAModuleInfo* i in apple_get_modules()) [cb addItemWithObjectValue:i]; if (cb.numberOfItems) [cb selectItemAtIndex:0]; else - apple_display_alert(@"No libretro cores were found.", @"RetroArch"); + apple_display_alert(@"No libretro cores were found.\nSelect \"Go->Cores Directory\" from the menu and place libretro dylib files there.", @"RetroArch"); // Run RGUI if needed - if (!_wantReload) + if (!_wantReload || apple_argv) apple_run_core(nil, 0); else [self chooseCore]; @@ -162,23 +159,6 @@ - (void)chooseCore { -#ifdef USE_XATTR - char stored_name[PATH_MAX]; - if (getxattr(_file.UTF8String, "com.RetroArch.Core", stored_name, PATH_MAX, 0, 0) > 0) - { - for (RAModuleInfo* i in RAModuleInfo.getModules) - { - const char* core_name = i.path.lastPathComponent.UTF8String; - if (strcmp(core_name, stored_name) == 0) - { - _core = i; - [self runCore]; - return; - } - } - } -#endif - [NSApplication.sharedApplication beginSheet:_coreSelectSheet modalForWindow:window modalDelegate:nil didEndSelector:nil contextInfo:nil]; [NSApplication.sharedApplication runModalForWindow:_coreSelectSheet]; } @@ -202,14 +182,7 @@ - (void)loadingCore:(RAModuleInfo*)core withFile:(const char*)file { if (file) - { - [NSDocumentController.sharedDocumentController noteNewRecentDocumentURL:[NSURL fileURLWithPath:[NSString stringWithUTF8String:file]]]; - -#ifdef USE_XATTR - const char* core_name = core.path.lastPathComponent.UTF8String; - setxattr(file, "com.RetroArch.Core", core_name, strlen(core_name) + 1, 0, 0); -#endif - } + [NSDocumentController.sharedDocumentController noteNewRecentDocumentURL:[NSURL fileURLWithPath:@(file)]]; } - (void)unloadingCore:(RAModuleInfo*)core @@ -227,21 +200,16 @@ _wantReload = false; } -- (NSString*)retroarchConfigPath -{ - NSArray* paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES); - return [paths[0] stringByAppendingPathComponent:@"RetroArch"]; -} - -- (NSString*)corePath -{ - return [NSBundle.mainBundle.bundlePath stringByAppendingPathComponent:@"Contents/Resources/modules"]; -} - #pragma mark Menus +- (IBAction)showCoresDirectory:(id)sender +{ + [[NSWorkspace sharedWorkspace] openFile:self.coreDirectory]; +} + - (IBAction)showPreferences:(id)sender { - [[[NSWindowController alloc] initWithWindowNibName:@"Settings"] window]; + NSWindowController* wc = [[NSWindowController alloc] initWithWindowNibName:@"Settings"]; + [NSApp runModalForWindow:wc.window]; } - (IBAction)basicEvent:(id)sender @@ -259,6 +227,23 @@ int main(int argc, char *argv[]) { + uint32_t current_argc = 0; + + for (int i = 0; i != argc; i ++) + { + if (strcmp(argv[i], "--") == 0) + { + current_argc = 1; + apple_argv = malloc(sizeof(char*) * (argc + 1)); + memset(apple_argv, 0, sizeof(char*) * (argc + 1)); + apple_argv[0] = argv[0]; + } + else if (current_argc) + { + apple_argv[current_argc ++] = argv[i]; + } + } + return NSApplicationMain(argc, (const char **) argv); } diff --git a/apple/OSX/settings.m b/apple/OSX/settings.m index 4bebfb07ec..66ce2bf1cd 100644 --- a/apple/OSX/settings.m +++ b/apple/OSX/settings.m @@ -14,16 +14,272 @@ */ #import -#import "../RetroArch/RetroArch_Apple.h" -#include "../RetroArch/setting_data.h" +#import "apple/common/RetroArch_Apple.h" +#include "apple/common/setting_data.h" +#include "apple/common/apple_input.h" + +#include "driver.h" +#include "input/input_common.h" + +struct settings fake_settings; +struct global fake_extern; + +static const void* associated_name_tag = (void*)&associated_name_tag; + +#define BINDFOR(s) (*(struct retro_keybind*)(&s)->value) + +static const char* key_name_for_id(uint32_t hidkey) +{ + for (int i = 0; apple_key_name_map[i].hid_id; i ++) + if (apple_key_name_map[i].hid_id == hidkey) + return apple_key_name_map[i].keyname; + + return "nul"; +} + +static uint32_t key_id_for_name(const char* name) +{ + for (int i = 0; apple_key_name_map[i].hid_id; i ++) + if (strcmp(name, apple_key_name_map[i].keyname) == 0) + return apple_key_name_map[i].hid_id; + + return 0; +} + +#define key_name_for_rk(X) key_name_for_id(input_translate_rk_to_keysym(X)) +#define key_rk_for_name(X) input_translate_keysym_to_rk(key_id_for_name(X)) + +static const char* get_input_config_key(const rarch_setting_t* setting, const char* type) +{ + static char buffer[32]; + if (setting->input_player) + snprintf(buffer, 32, "input_player%d_%s%c%s", setting->input_player, setting->name, type ? '_' : '\0', type); + else + snprintf(buffer, 32, "input_%s%c%s", setting->name, type ? '_' : '\0', type); + return buffer; +} + +static const char* get_button_name(const rarch_setting_t* setting) +{ + static char buffer[32]; + + if (BINDFOR(*setting).joykey == NO_BTN) + return "nul"; + + snprintf(buffer, 32, "%lld", BINDFOR(*setting).joykey); + return buffer; +} + +static const char* get_axis_name(const rarch_setting_t* setting) +{ + static char buffer[32]; + + uint32_t joyaxis = BINDFOR(*setting).joyaxis; + + if (AXIS_NEG_GET(joyaxis) != AXIS_DIR_NONE) + snprintf(buffer, 8, "-%d", AXIS_NEG_GET(joyaxis)); + else if (AXIS_POS_GET(joyaxis) != AXIS_DIR_NONE) + snprintf(buffer, 8, "+%d", AXIS_POS_GET(joyaxis)); + else + return "nul"; + + return buffer; +} + +@interface RANumberFormatter : NSNumberFormatter +@end + +@implementation RANumberFormatter +- (id)initWithFloatSupport:(bool)allowFloat minimum:(double)min maximum:(double)max +{ + self = [super init]; + self.allowsFloats = allowFloat; + self.maximumFractionDigits = 10; + + if (min || max) + { + self.minimum = @(min); + self.maximum = @(max); + } + + return self; +} + +- (BOOL)isPartialStringValid:(NSString*)partialString newEditingString:(NSString**)newString errorDescription:(NSString**)error +{ + bool hasDot = false; + + if (partialString.length) + for (int i = 0; i != partialString.length; i ++) + { + unichar ch = [partialString characterAtIndex:i]; + + if (i == 0 && (!self.minimum || self.minimum.intValue < 0) && ch == '-') + continue; + else if (self.allowsFloats && !hasDot && ch == '.') + hasDot = true; + else if (!isdigit(ch)) + return NO; + } + + return YES; +} +@end + + +@interface RAInputBinder : NSWindow +@end + +@implementation RAInputBinder + +- (IBAction)goAway:(id)sender +{ + [NSApp endSheet:self]; + [self orderOut:nil]; +} + +// Stop the annoying sound when pressing a key +- (void)keyDown:(NSEvent*)theEvent +{ +} + +@end + +@interface RASettingCell : NSTableCellView +@property (nonatomic) const rarch_setting_t* setting; + +@property (nonatomic) NSString* stringValue; +@property (nonatomic) IBOutlet NSNumber* numericValue; +@property (nonatomic) bool booleanValue; + +@property (nonatomic) NSTimer* bindTimer; +@end + +@implementation RASettingCell +- (void)setSetting:(const rarch_setting_t *)aSetting +{ + _setting = aSetting; + + if (!_setting) + return; + + if (aSetting->type == ST_INT || aSetting->type == ST_FLOAT) + { + self.textField.formatter = [[RANumberFormatter alloc] initWithFloatSupport:aSetting->type == ST_FLOAT + minimum:aSetting->min + maximum:aSetting->max]; + } + else + self.textField.formatter = nil; + + // Set value + switch (aSetting->type) + { + case ST_INT: self.numericValue = @(*(int*)aSetting->value); break; + case ST_FLOAT: self.numericValue = @(*(float*)aSetting->value); break; + case ST_STRING: self.stringValue = @((const char*)aSetting->value); break; + case ST_PATH: self.stringValue = @((const char*)aSetting->value); break; + case ST_BOOL: self.booleanValue = *(bool*)aSetting->value; break; + case ST_BIND: [self updateInputString]; break; + default: break; + } +} + +- (IBAction)doBrowse:(id)sender +{ + NSOpenPanel* panel = [NSOpenPanel new]; + [panel runModal]; + + if (panel.URLs.count == 1) + self.stringValue = panel.URL.path; +} + +- (void)setNumericValue:(NSNumber *)numericValue +{ + _numericValue = numericValue; + + if (_setting && _setting->type == ST_INT) + *(int*)_setting->value = _numericValue.intValue; + else if (_setting && _setting->type == ST_FLOAT) + *(float*)_setting->value = _numericValue.floatValue; +} + +- (void)setBooleanValue:(bool)booleanValue +{ + _booleanValue = booleanValue; + + if (_setting && _setting->type == ST_BOOL) + *(bool*)_setting->value = _booleanValue; +} + +- (void)setStringValue:(NSString *)stringValue +{ + _stringValue = stringValue ? stringValue : @""; + + if (_setting && (_setting->type == ST_STRING || _setting->type == ST_PATH)) + strlcpy(_setting->value, _stringValue.UTF8String, _setting->size); +} + +// Input Binding +- (void)updateInputString +{ + self.stringValue = [NSString stringWithFormat:@"[KB:%s] [JS:%s] [AX:%s]", key_name_for_rk(BINDFOR(*_setting).key), + get_button_name(_setting), + get_axis_name(_setting)]; +} + +- (void)dismissBinder +{ + [self.bindTimer invalidate]; + self.bindTimer = nil; + + [self updateInputString]; + + [(id)self.window.attachedSheet goAway:nil]; +} + +- (void)checkBind:(NSTimer*)send +{ + int32_t value = 0; + + if ((value = apple_input_find_any_key())) + BINDFOR(*_setting).key = input_translate_keysym_to_rk(value); + else if ((value = apple_input_find_any_button(0)) >= 0) + BINDFOR(*_setting).joykey = value; + else if ((value = apple_input_find_any_axis(0))) + BINDFOR(*_setting).joyaxis = (value > 0) ? AXIS_POS(value - 1) : AXIS_NEG(value - 1); + else + return; + + [self dismissBinder]; +} + +- (IBAction)doGetBind:(id)sender +{ + static NSWindowController* controller; + if (!controller) + controller = [[NSWindowController alloc] initWithWindowNibName:@"InputBinder"]; + + self.bindTimer = [NSTimer timerWithTimeInterval:.1f target:self selector:@selector(checkBind:) userInfo:nil repeats:YES]; + [[NSRunLoop currentRunLoop] addTimer:self.bindTimer forMode:NSModalPanelRunLoopMode]; + + [NSApp beginSheet:controller.window modalForWindow:self.window modalDelegate:nil didEndSelector:nil contextInfo:nil]; +} + +@end + +@protocol RASettingView +@property const rarch_setting_t* setting; +@end @interface RASettingsDelegate : NSObject + NSOutlineViewDataSource, NSOutlineViewDelegate, + NSWindowDelegate> @end @implementation RASettingsDelegate { - NSWindow IBOutlet* _window; + NSWindow IBOutlet* _inputWindow; NSTableView IBOutlet* _table; NSOutlineView IBOutlet* _outline; @@ -33,10 +289,15 @@ - (void)awakeFromNib { + apple_enter_stasis(); + NSMutableArray* thisGroup = nil; NSMutableArray* thisSubGroup = nil; _settings = [NSMutableArray array]; - + + memcpy(&fake_settings, &g_settings, sizeof(struct settings)); + memcpy(&fake_extern, &g_extern, sizeof(struct global)); + for (int i = 0; setting_data[i].type; i ++) { switch (setting_data[i].type) @@ -44,7 +305,7 @@ case ST_GROUP: { thisGroup = [NSMutableArray array]; - objc_setAssociatedObject(thisGroup, "NAME", [NSString stringWithFormat:@"%s", setting_data[i].name], OBJC_ASSOCIATION_RETAIN_NONATOMIC); + objc_setAssociatedObject(thisGroup, associated_name_tag, [NSString stringWithFormat:@"%s", setting_data[i].name], OBJC_ASSOCIATION_RETAIN_NONATOMIC); break; } @@ -58,7 +319,7 @@ case ST_SUB_GROUP: { thisSubGroup = [NSMutableArray array]; - objc_setAssociatedObject(thisSubGroup, "NAME", [NSString stringWithFormat:@"%s", setting_data[i].name], OBJC_ASSOCIATION_RETAIN_NONATOMIC); + objc_setAssociatedObject(thisSubGroup, associated_name_tag, [NSString stringWithFormat:@"%s", setting_data[i].name], OBJC_ASSOCIATION_RETAIN_NONATOMIC); break; } @@ -77,49 +338,81 @@ } } - [NSApplication.sharedApplication beginSheet:_window modalForWindow:RetroArch_OSX.get->window modalDelegate:nil didEndSelector:nil contextInfo:nil]; - [NSApplication.sharedApplication runModalForWindow:_window]; + [self load]; } -- (IBAction)close:(id)sender +- (void)load { - [NSApplication.sharedApplication stopModal]; - [NSApplication.sharedApplication endSheet:_window returnCode:0]; - [_window orderOut:nil]; + config_file_t* conf = config_file_new(apple_platform.globalConfigFile.UTF8String); + + for (int i = 0; setting_data[i].type; i ++) + { + switch (setting_data[i].type) + { + case ST_BOOL: config_get_bool (conf, setting_data[i].name, (bool*)setting_data[i].value); break; + case ST_INT: config_get_int (conf, setting_data[i].name, (int*)setting_data[i].value); break; + case ST_FLOAT: config_get_float (conf, setting_data[i].name, (float*)setting_data[i].value); break; + case ST_PATH: config_get_array (conf, setting_data[i].name, (char*)setting_data[i].value, setting_data[i].size); break; + case ST_STRING: config_get_array (conf, setting_data[i].name, (char*)setting_data[i].value, setting_data[i].size); break; + + case ST_BIND: + { + input_config_parse_key (conf, "input_player1", setting_data[i].name, setting_data[i].value); + input_config_parse_joy_button(conf, "input_player1", setting_data[i].name, setting_data[i].value); + input_config_parse_joy_axis (conf, "input_player1", setting_data[i].name, setting_data[i].value); + break; + } + + case ST_HEX: break; + default: break; + } + } + config_file_free(conf); } +- (void)windowWillClose:(NSNotification *)notification +{ + config_file_t* conf = config_file_new(apple_platform.globalConfigFile.UTF8String); + conf = conf ? conf : config_file_new(0); + + for (int i = 0; setting_data[i].type; i ++) + { + switch (setting_data[i].type) + { + case ST_BOOL: config_set_bool (conf, setting_data[i].name, * (bool*)setting_data[i].value); break; + case ST_INT: config_set_int (conf, setting_data[i].name, * (int*)setting_data[i].value); break; + case ST_FLOAT: config_set_float (conf, setting_data[i].name, *(float*)setting_data[i].value); break; + case ST_PATH: config_set_string(conf, setting_data[i].name, (char*)setting_data[i].value); break; + case ST_STRING: config_set_string(conf, setting_data[i].name, (char*)setting_data[i].value); break; + + case ST_BIND: + { + config_set_string(conf, get_input_config_key(&setting_data[i], 0 ), key_name_for_rk(BINDFOR(setting_data[i]).key)); + config_set_string(conf, get_input_config_key(&setting_data[i], "btn" ), get_button_name(&setting_data[i])); + config_set_string(conf, get_input_config_key(&setting_data[i], "axis"), get_axis_name(&setting_data[i])); + break; + } + + case ST_HEX: break; + default: break; + } + } + config_file_write(conf, apple_platform.globalConfigFile.UTF8String); + config_file_free(conf); + + apple_exit_stasis(true); + + [NSApp stopModal]; +} #pragma mark View Builders - (NSView*)labelAccessoryFor:(NSString*)text onTable:(NSTableView*)table { - NSTextField* result = [table makeViewWithIdentifier:@"label" owner:self]; - if (result == nil) - { - result = [NSTextField new]; - result.bordered = NO; - result.drawsBackground = NO; - result.identifier = @"label"; - } - + RASettingCell* result = [table makeViewWithIdentifier:@"RALabelSetting" owner:nil]; result.stringValue = text; return result; } -- (NSView*)booleanAccessoryFor:(const rarch_setting_t*)setting onTable:(NSTableView*)table -{ - NSButton* result = [table makeViewWithIdentifier:@"boolean" owner:self]; - - if (!result) - { - result = [NSButton new]; - result.buttonType = NSSwitchButton; - result.title = @""; - } - - result.state = *(bool*)setting->value; - return result; -} - #pragma mark Section Table - (NSInteger)numberOfRowsInTableView:(NSTableView*)view { @@ -128,7 +421,7 @@ - (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row { - return [self labelAccessoryFor:objc_getAssociatedObject(_settings[row], "NAME") onTable:tableView]; + return [self labelAccessoryFor:objc_getAssociatedObject(_settings[row], associated_name_tag) onTable:tableView]; } - (void)tableViewSelectionDidChange:(NSNotification *)aNotification @@ -153,12 +446,16 @@ return [item isKindOfClass:[NSArray class]]; } +- (BOOL)validateProposedFirstResponder:(NSResponder *)responder forEvent:(NSEvent *)event { + return YES; +} + - (NSView *)outlineView:(NSOutlineView *)outlineView viewForTableColumn:(NSTableColumn *)tableColumn item:(id)item { if ([item isKindOfClass:[NSArray class]]) { if ([tableColumn.identifier isEqualToString:@"title"]) - return [self labelAccessoryFor:objc_getAssociatedObject(item, "NAME") onTable:outlineView]; + return [self labelAccessoryFor:objc_getAssociatedObject(item, associated_name_tag) onTable:outlineView]; else return [self labelAccessoryFor:[NSString stringWithFormat:@"%d items", (int)[item count]] onTable:outlineView]; } @@ -167,25 +464,22 @@ const rarch_setting_t* setting = &setting_data[[item intValue]]; if ([tableColumn.identifier isEqualToString:@"title"]) - return [self labelAccessoryFor:[NSString stringWithFormat:@"%s", setting->short_description] onTable:outlineView]; // < The outlineView will fill the value + return [self labelAccessoryFor:@(setting->short_description) onTable:outlineView]; else if([tableColumn.identifier isEqualToString:@"accessory"]) { + RASettingCell* s = nil; switch (setting->type) { - case ST_BOOL: return [self booleanAccessoryFor:setting onTable:outlineView]; - - case ST_PATH: - case ST_STRING: - return [self labelAccessoryFor:[NSString stringWithFormat:@"%s", (const char*)setting->value] onTable:outlineView]; - - case ST_INT: - return [self labelAccessoryFor:[NSString stringWithFormat:@"%d", *(int*)setting->value] onTable:outlineView]; - - case ST_FLOAT: - return [self labelAccessoryFor:[NSString stringWithFormat:@"%f", *(float*)setting->value] onTable:outlineView]; - - default: abort(); + case ST_BOOL: s = [outlineView makeViewWithIdentifier:@"RABooleanSetting" owner:nil]; break; + case ST_INT: s = [outlineView makeViewWithIdentifier:@"RANumericSetting" owner:nil]; break; + case ST_FLOAT: s = [outlineView makeViewWithIdentifier:@"RANumericSetting" owner:nil]; break; + case ST_PATH: s = [outlineView makeViewWithIdentifier:@"RAPathSetting" owner:nil]; break; + case ST_STRING: s = [outlineView makeViewWithIdentifier:@"RAStringSetting" owner:nil]; break; + case ST_BIND: s = [outlineView makeViewWithIdentifier:@"RABindSetting" owner:nil]; break; + default: break; } + s.setting = setting; + return s; } } diff --git a/apple/RetroArch_OSX.xcodeproj/project.pbxproj b/apple/RetroArch_OSX.xcodeproj/project.pbxproj index 6a1101970c..c70055652d 100644 --- a/apple/RetroArch_OSX.xcodeproj/project.pbxproj +++ b/apple/RetroArch_OSX.xcodeproj/project.pbxproj @@ -8,10 +8,11 @@ /* Begin PBXBuildFile section */ 9620F663178FD4D3001B3B81 /* settings.m in Sources */ = {isa = PBXBuildFile; fileRef = 9620F662178FD4D3001B3B81 /* settings.m */; }; - 9620F6651790004F001B3B81 /* Settings.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9620F6641790004F001B3B81 /* Settings.xib */; }; 962EE0E2178B3DF6004224FF /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 962EE0E1178B3DF6004224FF /* IOKit.framework */; }; 96355CE31788E72A0010DBFA /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 96355CE21788E72A0010DBFA /* Cocoa.framework */; }; 9646869817BBC14E00C5EA69 /* platform.m in Sources */ = {isa = PBXBuildFile; fileRef = 9646869617BBC14E00C5EA69 /* platform.m */; }; + 964DE7C117D84B34001CBB6C /* InputBinder.xib in Resources */ = {isa = PBXBuildFile; fileRef = 964DE7C317D84B34001CBB6C /* InputBinder.xib */; }; + 964DE7C417D84B57001CBB6C /* Settings.xib in Resources */ = {isa = PBXBuildFile; fileRef = 964DE7C617D84B57001CBB6C /* Settings.xib */; }; 967894931788ECDB00D6CA69 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 9678948F1788ECDB00D6CA69 /* InfoPlist.strings */; }; 967894941788ECDB00D6CA69 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 967894911788ECDB00D6CA69 /* MainMenu.xib */; }; 967894961788ED1100D6CA69 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 967894951788ED1100D6CA69 /* main.m */; }; @@ -29,7 +30,6 @@ /* Begin PBXFileReference section */ 9620F662178FD4D3001B3B81 /* settings.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = settings.m; path = OSX/settings.m; sourceTree = SOURCE_ROOT; }; - 9620F6641790004F001B3B81 /* Settings.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = Settings.xib; path = OSX/Settings.xib; sourceTree = ""; }; 962EE0E1178B3DF6004224FF /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = ../../../../../../../System/Library/Frameworks/IOKit.framework; sourceTree = ""; }; 96355CDF1788E72A0010DBFA /* RetroArch.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = RetroArch.app; sourceTree = BUILT_PRODUCTS_DIR; }; 96355CE21788E72A0010DBFA /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; @@ -38,6 +38,8 @@ 96355CE71788E72A0010DBFA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 9646869617BBC14E00C5EA69 /* platform.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = platform.m; path = OSX/platform.m; sourceTree = SOURCE_ROOT; }; 9646869717BBC14E00C5EA69 /* platform.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = platform.h; path = OSX/platform.h; sourceTree = SOURCE_ROOT; }; + 964DE7C217D84B34001CBB6C /* en */ = {isa = PBXFileReference; lastKnownFileType = file; name = en; path = en.lproj/InputBinder.xib; sourceTree = ""; }; + 964DE7C517D84B57001CBB6C /* en */ = {isa = PBXFileReference; lastKnownFileType = file; name = en; path = en.lproj/Settings.xib; sourceTree = ""; }; 9678948D1788ECCA00D6CA69 /* RetroArch-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "RetroArch-Info.plist"; path = "OSX/RetroArch-Info.plist"; sourceTree = SOURCE_ROOT; }; 967894901788ECDB00D6CA69 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = OSX/en.lproj/InfoPlist.strings; sourceTree = SOURCE_ROOT; }; 967894921788ECDB00D6CA69 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = OSX/en.lproj/MainMenu.xib; sourceTree = SOURCE_ROOT; }; @@ -78,7 +80,7 @@ isa = PBXGroup; children = ( 962EE0E1178B3DF6004224FF /* IOKit.framework */, - 96355CE81788E72A0010DBFA /* RetroArch */, + 96355CE81788E72A0010DBFA /* common */, 967894E7178A2E9B00D6CA69 /* Assets */, 96355CE11788E72A0010DBFA /* Frameworks */, 96355CE01788E72A0010DBFA /* Products */, @@ -109,7 +111,7 @@ name = Frameworks; sourceTree = ""; }; - 96355CE81788E72A0010DBFA /* RetroArch */ = { + 96355CE81788E72A0010DBFA /* common */ = { isa = PBXGroup; children = ( 9646869617BBC14E00C5EA69 /* platform.m */, @@ -124,7 +126,7 @@ 9678949C1788F02600D6CA69 /* utility.m */, 967894951788ED1100D6CA69 /* main.m */, ); - path = RetroArch; + path = common; sourceTree = ""; }; 96355CE91788E72A0010DBFA /* Supporting Files */ = { @@ -143,7 +145,8 @@ 967894E8178A2EB400D6CA69 /* modules */, 967894911788ECDB00D6CA69 /* MainMenu.xib */, C15874EE178F2094001171D4 /* RetroArch.icns */, - 9620F6641790004F001B3B81 /* Settings.xib */, + 964DE7C617D84B57001CBB6C /* Settings.xib */, + 964DE7C317D84B34001CBB6C /* InputBinder.xib */, ); name = Assets; sourceTree = ""; @@ -202,7 +205,8 @@ 967894941788ECDB00D6CA69 /* MainMenu.xib in Resources */, 967894E9178A2EB400D6CA69 /* modules in Resources */, C15874EF178F2094001171D4 /* RetroArch.icns in Resources */, - 9620F6651790004F001B3B81 /* Settings.xib in Resources */, + 964DE7C417D84B57001CBB6C /* Settings.xib in Resources */, + 964DE7C117D84B34001CBB6C /* InputBinder.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -226,6 +230,24 @@ /* End PBXSourcesBuildPhase section */ /* Begin PBXVariantGroup section */ + 964DE7C317D84B34001CBB6C /* InputBinder.xib */ = { + isa = PBXVariantGroup; + children = ( + 964DE7C217D84B34001CBB6C /* en */, + ); + name = InputBinder.xib; + path = OSX; + sourceTree = ""; + }; + 964DE7C617D84B57001CBB6C /* Settings.xib */ = { + isa = PBXVariantGroup; + children = ( + 964DE7C517D84B57001CBB6C /* en */, + ); + name = Settings.xib; + path = OSX; + sourceTree = ""; + }; 9678948F1788ECDB00D6CA69 /* InfoPlist.strings */ = { isa = PBXVariantGroup; children = ( @@ -303,7 +325,6 @@ "-DINLINE=inline", "-DLSB_FIRST", "-D__LIBRETRO__", - "-DRARCH_PERFORMANCE_MODE", "-DWANT_RPNG", "-DHAVE_COREAUDIO", "-DHAVE_DYNAMIC", @@ -349,23 +370,22 @@ "-DHAVE_RARCH_MAIN_WRAP", "-DHAVE_GRIFFIN", "-DHAVE_RGUI", - "-DIOS", + "-DOSX", "-DHAVE_OPENGL", "-DHAVE_FBO", "-DHAVE_VID_CONTEXT", - "-DHAVE_OPENGLES2", "-DHAVE_GLSL", "-DINLINE=inline", "-DLSB_FIRST", "-D__LIBRETRO__", - "-DRARCH_PERFORMANCE_MODE", - "-DRARCH_MOBILE", + "-DWANT_RPNG", "-DHAVE_COREAUDIO", "-DHAVE_DYNAMIC", "-DHAVE_OVERLAY", "-DHAVE_ZLIB", "-DWANT_MINIZ", "-DSINC_LOWER_QUALITY", + "-DHAVE_NETPLAY", ); PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = ""; diff --git a/apple/RetroArch_iOS.xcodeproj/project.pbxproj b/apple/RetroArch_iOS.xcodeproj/project.pbxproj index f94d43b805..a603b05390 100644 --- a/apple/RetroArch_iOS.xcodeproj/project.pbxproj +++ b/apple/RetroArch_iOS.xcodeproj/project.pbxproj @@ -120,7 +120,7 @@ 96AFAE1A16C1D4EA009DE44C = { isa = PBXGroup; children = ( - 96AFAE3316C1D4EA009DE44C /* RetroArch */, + 96AFAE3316C1D4EA009DE44C /* common */, 96AFAE9C16C1D976009DE44C /* core */, 966B9CB716E41E7A005B61E1 /* Assets */, 96AFAE2816C1D4EA009DE44C /* Frameworks */, @@ -151,7 +151,7 @@ name = Frameworks; sourceTree = ""; }; - 96AFAE3316C1D4EA009DE44C /* RetroArch */ = { + 96AFAE3316C1D4EA009DE44C /* common */ = { isa = PBXGroup; children = ( 9646869417BBBEAE00C5EA69 /* platform.m */, @@ -167,7 +167,7 @@ 96355CD11788CF190010DBFA /* RetroArch_Apple.h */, 96337E81176AC6E5004685F3 /* utility.m */, ); - path = RetroArch; + path = common; sourceTree = ""; }; 96AFAE3416C1D4EA009DE44C /* Supporting Files */ = { @@ -367,7 +367,6 @@ "-DLSB_FIRST", "-DHAVE_THREAD", "-D__LIBRETRO__", - "-DRARCH_PERFORMANCE_MODE", "-DRARCH_MOBILE", "-std=gnu99", "-DHAVE_COREAUDIO", @@ -417,7 +416,6 @@ "-DLSB_FIRST", "-DHAVE_THREAD", "-D__LIBRETRO__", - "-DRARCH_PERFORMANCE_MODE", "-DRARCH_MOBILE", "-std=gnu99", "-DHAVE_COREAUDIO", @@ -458,7 +456,6 @@ "-DINLINE=inline", "-DLSB_FIRST", "-D__LIBRETRO__", - "-DRARCH_PERFORMANCE_MODE", "-DRARCH_MOBILE", "-DHAVE_COREAUDIO", "-DHAVE_DYNAMIC", @@ -501,7 +498,6 @@ "-DINLINE=inline", "-DLSB_FIRST", "-D__LIBRETRO__", - "-DRARCH_PERFORMANCE_MODE", "-DRARCH_MOBILE", "-DHAVE_COREAUDIO", "-DHAVE_DYNAMIC", diff --git a/apple/RetroArch/RAGameView.m b/apple/common/RAGameView.m similarity index 81% rename from apple/RetroArch/RAGameView.m rename to apple/common/RAGameView.m index 4b9c592f33..8fec390435 100644 --- a/apple/RetroArch/RAGameView.m +++ b/apple/common/RAGameView.m @@ -17,6 +17,7 @@ #include "rarch_wrapper.h" #include "general.h" +#include "gfx/gfx_common.h" #ifdef IOS @@ -33,6 +34,7 @@ static UIView* g_pause_indicator_view; #include "apple_input.h" +static bool g_has_went_fullscreen; static RAGameView* g_instance; static NSOpenGLContext* g_context; static NSOpenGLPixelFormat* g_format; @@ -312,6 +314,73 @@ void *apple_get_proc_address(const char *symbol_name) #endif } +void apple_update_window_title(void) +{ + static char buf[128]; + bool got_text = gfx_get_fps(buf, sizeof(buf), false); +#ifdef OSX + static const char* const text = buf; // < Can't access buf directly in the block + + if (got_text) + { + // NOTE: This could go bad if buf is updated again before this completes. + // If it poses a problem it should be changed to dispatch_sync. + dispatch_async(dispatch_get_main_queue(), ^ + { + g_view.window.title = @(text); + }); + } +#endif +} + +bool apple_game_view_has_focus(void) +{ +#ifdef OSX + return [NSApp isActive]; +#else + return true; +#endif +} + +bool apple_set_video_mode(unsigned width, unsigned height, bool fullscreen) +{ + __block bool result = true; + +#ifdef OSX + dispatch_sync(dispatch_get_main_queue(), + ^{ + // TODO: Sceen mode support + + if (fullscreen && !g_has_went_fullscreen) + { + if (g_settings.video.monitor_index >= [NSScreen screens].count) + { + apple_display_alert(@"Could not go fullscreen: Monitor index out of range.", nil); + result = false; + return; + } + + [g_view enterFullScreenMode:[NSScreen screens][g_settings.video.monitor_index] withOptions:nil]; + [NSCursor hide]; + } + else if (!fullscreen && g_has_went_fullscreen) + { + [g_view exitFullScreenModeWithOptions:nil]; + [g_view.window makeFirstResponder:g_view]; + [NSCursor unhide]; + } + + g_has_went_fullscreen = fullscreen; + if (!g_has_went_fullscreen) + [g_view.window setContentSize:NSMakeSize(width, height)]; + }); +#endif + + // TODO: Maybe iOS users should be apple to show/hide the status bar here? + + return result; +} + #ifdef IOS void apple_bind_game_view_fbo(void) { diff --git a/apple/RetroArch/RAModuleInfo.h b/apple/common/RAModuleInfo.h similarity index 71% rename from apple/RetroArch/RAModuleInfo.h rename to apple/common/RAModuleInfo.h index 063143a5cb..39173ec473 100644 --- a/apple/RetroArch/RAModuleInfo.h +++ b/apple/common/RAModuleInfo.h @@ -22,22 +22,22 @@ #include "conf/config_file.h" #include "core_info.h" +extern NSArray* apple_get_modules(); + @interface RAModuleInfo : NSObject -@property (strong) NSString* path; +@property NSString* path; // e.g. /path/to/corename_libretro.dylib +@property NSString* baseName; // e.g. corename_libretro @property core_info_t* info; @property config_file_t* data; -@property (strong) NSString* description; -@property (strong) NSString* customConfigPath; +@property NSString* description; // Friendly name from config file, else just the filename +@property NSString* customConfigFile; // Path where custom config file would reside +@property NSString* configFile; // Path to effective config file -+ (NSArray*)getModules; - (bool)supportsFileAtPath:(NSString*)path; -+ (NSString*)globalConfigPath; - - (void)createCustomConfig; - (void)deleteCustomConfig; - (bool)hasCustomConfig; -- (NSString*)configPath; @end diff --git a/apple/RetroArch/RAModuleInfo.m b/apple/common/RAModuleInfo.m similarity index 82% rename from apple/RetroArch/RAModuleInfo.m rename to apple/common/RAModuleInfo.m index bd148684bb..985cfcb9c7 100644 --- a/apple/RetroArch/RAModuleInfo.m +++ b/apple/common/RAModuleInfo.m @@ -22,30 +22,29 @@ static NSMutableArray* moduleList; static core_info_list_t* coreList; -@implementation RAModuleInfo -+ (NSArray*)getModules +NSArray* apple_get_modules() { if (!moduleList) { - coreList = get_core_info_list(apple_platform.corePath.UTF8String); + coreList = get_core_info_list(apple_platform.coreDirectory.UTF8String); if (!coreList) return nil; - - moduleList = [NSMutableArray arrayWithCapacity:coreList->count]; + moduleList = [NSMutableArray arrayWithCapacity:coreList->count]; + for (int i = 0; coreList && i < coreList->count; i ++) { core_info_t* core = &coreList->list[i]; RAModuleInfo* newInfo = [RAModuleInfo new]; - newInfo.path = [NSString stringWithUTF8String:core->path]; + newInfo.path = @(core->path); + newInfo.baseName = newInfo.path.lastPathComponent.stringByDeletingPathExtension; newInfo.info = core; newInfo.data = core->data; - newInfo.description = [NSString stringWithUTF8String:core->display_name]; - - NSString* baseName = newInfo.path.lastPathComponent.stringByDeletingPathExtension; - newInfo.customConfigPath = [NSString stringWithFormat:@"%@/%@.cfg", apple_platform.retroarchConfigPath, baseName]; + newInfo.description = @(core->display_name); + newInfo.customConfigFile = [NSString stringWithFormat:@"%@/%@.cfg", apple_platform.configDirectory, newInfo.baseName]; + newInfo.configFile = newInfo.hasCustomConfig ? newInfo.customConfigFile : apple_platform.globalConfigFile; [moduleList addObject:newInfo]; } @@ -59,9 +58,7 @@ static core_info_list_t* coreList; return moduleList; } -- (void)dealloc -{ -} +@implementation RAModuleInfo - (id)copyWithZone:(NSZone *)zone { @@ -73,35 +70,21 @@ static core_info_list_t* coreList; return does_core_support_file(self.info, path.UTF8String); } -+ (NSString*)globalConfigPath -{ - static NSString* path; - if (!path) - path = [NSString stringWithFormat:@"%@/retroarch.cfg", apple_platform.retroarchConfigPath]; - - return path; -} - - (void)createCustomConfig { if (!self.hasCustomConfig) - [NSFileManager.defaultManager copyItemAtPath:RAModuleInfo.globalConfigPath toPath:self.customConfigPath error:nil]; + [NSFileManager.defaultManager copyItemAtPath:apple_platform.globalConfigFile toPath:self.customConfigFile error:nil]; } - (void)deleteCustomConfig { if (self.hasCustomConfig) - [NSFileManager.defaultManager removeItemAtPath:self.customConfigPath error:nil]; + [NSFileManager.defaultManager removeItemAtPath:self.customConfigFile error:nil]; } - (bool)hasCustomConfig { - return path_file_exists(self.customConfigPath.UTF8String); -} - -- (NSString*)configPath -{ - return self.hasCustomConfig ? self.customConfigPath : RAModuleInfo.globalConfigPath; + return path_file_exists(self.customConfigFile.UTF8String); } @end @@ -148,7 +131,7 @@ static NSString* build_string_pair(NSString* stringA, NSString* stringB) for (int i = 0; i < firmwareCount; i ++) { NSString* path = objc_get_value_from_config(_data.data, [NSString stringWithFormat:@"firmware%d_path", i + 1], @"Unspecified"); - path = [path stringByReplacingOccurrencesOfString:@"%sysdir%" withString:RetroArch_iOS.get.systemDirectory]; + path = [path stringByReplacingOccurrencesOfString:@"%sysdir%" withString:[RetroArch_iOS get].systemDirectory]; [firmwareSection addObject:build_string_pair(objc_get_value_from_config(_data.data, [NSString stringWithFormat:@"firmware%d_desc", i + 1], @"Unspecified"), path)]; } diff --git a/apple/RetroArch/RetroArch_Apple.h b/apple/common/RetroArch_Apple.h similarity index 85% rename from apple/RetroArch/RetroArch_Apple.h rename to apple/common/RetroArch_Apple.h index 90dccbb0da..b8616ae78e 100644 --- a/apple/RetroArch/RetroArch_Apple.h +++ b/apple/common/RetroArch_Apple.h @@ -28,8 +28,9 @@ - (void)loadingCore:(RAModuleInfo*)core withFile:(const char*)file; - (void)unloadingCore:(RAModuleInfo*)core; -- (NSString*)retroarchConfigPath; // < This returns the directory that contains retroarch.cfg and other custom configs -- (NSString*)corePath; +- (NSString*)configDirectory; // < This returns the directory that contains retroarch.cfg and other custom configs +- (NSString*)globalConfigFile; // < This is the full path to retroarch.cfg +- (NSString*)coreDirectory; // < This is the default path to where libretro cores are installed @end #ifdef IOS @@ -38,6 +39,7 @@ #import "../OSX/platform.h" #endif +extern char** apple_argv; extern bool apple_is_paused; extern bool apple_is_running; extern bool apple_use_tv_mode; @@ -53,7 +55,7 @@ extern void apple_event_show_rgui(void* userdata); extern void apple_refresh_config(); extern void apple_enter_stasis(); -extern void apple_exit_stasis(); +extern void apple_exit_stasis(bool reload_config); extern void apple_run_core(RAModuleInfo* core, const char* file); // utility.m diff --git a/apple/RetroArch/apple_input.c b/apple/common/apple_input.c similarity index 72% rename from apple/RetroArch/apple_input.c rename to apple/common/apple_input.c index 07a1bdecfa..152acb13a0 100644 --- a/apple/RetroArch/apple_input.c +++ b/apple/common/apple_input.c @@ -22,14 +22,14 @@ #include "general.h" #include "driver.h" +#include "keycode.inc" + extern const rarch_joypad_driver_t apple_joypad; static const rarch_joypad_driver_t* const g_joydriver = &apple_joypad; apple_input_data_t g_current_input_data; apple_input_data_t g_polled_input_data; -static const struct rarch_key_map rarch_key_map_hidusage[]; - #ifdef OSX // Taken from https://github.com/depp/keycode, check keycode.h for license const unsigned char MAC_NATIVE_TO_HID[128] = { 4, 22, 7, 9, 11, 10, 29, 27, 6, 25,255, 5, 20, 26, 8, 21, @@ -101,6 +101,42 @@ void apple_input_handle_key_event(unsigned keycode, bool down) g_current_input_data.keys[keycode] = down; } + +int32_t apple_input_find_any_key() +{ + for (int i = 0; apple_key_name_map[i].hid_id; i++) + if (g_current_input_data.keys[apple_key_name_map[i].hid_id]) + return apple_key_name_map[i].hid_id; + + return 0; +} + +int32_t apple_input_find_any_button(uint32_t port) +{ + uint32_t buttons = g_current_input_data.pad_buttons[port] | + ((port == 0) ? apple_input_get_icade_buttons() : 0); + + if (g_current_input_data.pad_buttons[port]) + for (int i = 0; i != 32; i ++) + if (buttons & (1 << i)) + return i; + + return -1; +} + +int32_t apple_input_find_any_axis(uint32_t port) +{ + for (int i = 0; i < 4; i++) + { + int16_t value = g_current_input_data.pad_axis[port][i]; + + if (abs(value) > 0x4000) + return (value < 0) ? -(i + 1) : i + 1; + } + + return 0; +} + // Game thread interface static bool apple_key_pressed(enum retro_key key) { @@ -118,7 +154,7 @@ static bool apple_is_pressed(unsigned port_num, const struct retro_keybind *bind // Exported input driver static void *apple_input_init(void) { - input_init_keyboard_lut(rarch_key_map_hidusage); + input_init_keyboard_lut(apple_key_map_hidusage); memset(&g_polled_input_data, 0, sizeof(g_polled_input_data)); return (void*)-1; } @@ -273,150 +309,3 @@ const input_driver_t input_apple = { apple_input_set_keybinds, "apple_input", }; - - -// Key table -#include "keycode.h" -static const struct rarch_key_map rarch_key_map_hidusage[] = { - { KEY_Delete, RETROK_BACKSPACE }, - { KEY_Tab, RETROK_TAB }, -// RETROK_CLEAR }, - { KEY_Enter, RETROK_RETURN }, - { KEY_Pause, RETROK_PAUSE }, - { KEY_Escape, RETROK_ESCAPE }, - { KEY_Space, RETROK_SPACE }, -// RETROK_EXCLAIM }, -// RETROK_QUOTEDBL }, -// RETROK_HASH }, -// RETROK_DOLLAR }, -// RETROK_AMPERSAND }, - { KEY_Quote, RETROK_QUOTE }, -// RETROK_LEFTPAREN }, -// RETROK_RIGHTPAREN }, -// RETROK_ASTERISK }, -// RETROK_PLUS }, - { KEY_Comma, RETROK_COMMA }, - { KEY_Minus, RETROK_MINUS }, - { KEY_Period, RETROK_PERIOD }, - { KEY_Slash, RETROK_SLASH }, - { KEY_0, RETROK_0 }, - { KEY_1, RETROK_1 }, - { KEY_2, RETROK_2 }, - { KEY_3, RETROK_3 }, - { KEY_4, RETROK_4 }, - { KEY_5, RETROK_5 }, - { KEY_6, RETROK_6 }, - { KEY_7, RETROK_7 }, - { KEY_8, RETROK_8 }, - { KEY_9, RETROK_9 }, -// RETROK_COLON }, - { KEY_Semicolon, RETROK_SEMICOLON }, -// RETROK_LESS }, - { KEY_Equals, RETROK_EQUALS }, -// RETROK_GREATER }, -// RETROK_QUESTION }, -// RETROK_AT }, - { KEY_LeftBracket, RETROK_LEFTBRACKET }, - { KEY_Backslash, RETROK_BACKSLASH }, - { KEY_RightBracket, RETROK_RIGHTBRACKET }, -// RETROK_CARET }, -// RETROK_UNDERSCORE }, - { KEY_Grave, RETROK_BACKQUOTE }, - { KEY_A, RETROK_a }, - { KEY_B, RETROK_b }, - { KEY_C, RETROK_c }, - { KEY_D, RETROK_d }, - { KEY_E, RETROK_e }, - { KEY_F, RETROK_f }, - { KEY_G, RETROK_g }, - { KEY_H, RETROK_h }, - { KEY_I, RETROK_i }, - { KEY_J, RETROK_j }, - { KEY_K, RETROK_k }, - { KEY_L, RETROK_l }, - { KEY_M, RETROK_m }, - { KEY_N, RETROK_n }, - { KEY_O, RETROK_o }, - { KEY_P, RETROK_p }, - { KEY_Q, RETROK_q }, - { KEY_R, RETROK_r }, - { KEY_S, RETROK_s }, - { KEY_T, RETROK_t }, - { KEY_U, RETROK_u }, - { KEY_V, RETROK_v }, - { KEY_W, RETROK_w }, - { KEY_X, RETROK_x }, - { KEY_Y, RETROK_y }, - { KEY_Z, RETROK_z }, - { KEY_DeleteForward, RETROK_DELETE }, - - { KP_0, RETROK_KP0 }, - { KP_1, RETROK_KP1 }, - { KP_2, RETROK_KP2 }, - { KP_3, RETROK_KP3 }, - { KP_4, RETROK_KP4 }, - { KP_5, RETROK_KP5 }, - { KP_6, RETROK_KP6 }, - { KP_7, RETROK_KP7 }, - { KP_8, RETROK_KP8 }, - { KP_9, RETROK_KP9 }, - { KP_Point, RETROK_KP_PERIOD }, - { KP_Divide, RETROK_KP_DIVIDE }, - { KP_Multiply, RETROK_KP_MULTIPLY }, - { KP_Subtract, RETROK_KP_MINUS }, - { KP_Add, RETROK_KP_PLUS }, - { KP_Enter, RETROK_KP_ENTER }, - { KP_Equals, RETROK_KP_EQUALS }, - - { KEY_Up, RETROK_UP }, - { KEY_Down, RETROK_DOWN }, - { KEY_Right, RETROK_RIGHT }, - { KEY_Left, RETROK_LEFT }, - { KEY_Insert, RETROK_INSERT }, - { KEY_Home, RETROK_HOME }, - { KEY_End, RETROK_END }, - { KEY_PageUp, RETROK_PAGEUP }, - { KEY_PageDown, RETROK_PAGEDOWN }, - - { KEY_F1, RETROK_F1 }, - { KEY_F2, RETROK_F2 }, - { KEY_F3, RETROK_F3 }, - { KEY_F4, RETROK_F4 }, - { KEY_F5, RETROK_F5 }, - { KEY_F6, RETROK_F6 }, - { KEY_F7, RETROK_F7 }, - { KEY_F8, RETROK_F8 }, - { KEY_F9, RETROK_F9 }, - { KEY_F10, RETROK_F10 }, - { KEY_F11, RETROK_F11 }, - { KEY_F12, RETROK_F12 }, - { KEY_F13, RETROK_F13 }, - { KEY_F14, RETROK_F14 }, - { KEY_F15, RETROK_F15 }, - -// RETROK_NUMLOCK }, - { KEY_CapsLock, RETROK_CAPSLOCK }, -// RETROK_SCROLLOCK }, - { KEY_RightShift, RETROK_RSHIFT }, - { KEY_LeftShift, RETROK_LSHIFT }, - { KEY_RightControl, RETROK_RCTRL }, - { KEY_LeftControl, RETROK_LCTRL }, - { KEY_RightAlt, RETROK_RALT }, - { KEY_LeftAlt, RETROK_LALT }, - { KEY_RightGUI, RETROK_RMETA }, - { KEY_LeftGUI, RETROK_RMETA }, -// RETROK_LSUPER }, -// RETROK_RSUPER }, -// RETROK_MODE }, -// RETROK_COMPOSE }, - -// RETROK_HELP }, - { KEY_PrintScreen, RETROK_PRINT }, -// RETROK_SYSREQ }, -// RETROK_BREAK }, - { KEY_Menu, RETROK_MENU }, -// RETROK_POWER }, -// RETROK_EURO }, -// RETROK_UNDO }, - { 0, RETROK_UNKNOWN } -}; diff --git a/apple/RetroArch/apple_input.h b/apple/common/apple_input.h similarity index 90% rename from apple/RetroArch/apple_input.h rename to apple/common/apple_input.h index 4a1ee13788..8d95746597 100644 --- a/apple/RetroArch/apple_input.h +++ b/apple/common/apple_input.h @@ -50,4 +50,8 @@ void apple_input_enable_icade(bool on); uint32_t apple_input_get_icade_buttons(); void apple_input_handle_key_event(unsigned keycode, bool down); +extern int32_t apple_input_find_any_key(); +extern int32_t apple_input_find_any_button(uint32_t port); +extern int32_t apple_input_find_any_axis(uint32_t port); + #endif diff --git a/apple/RetroArch/apple_joypad.c b/apple/common/apple_joypad.c similarity index 89% rename from apple/RetroArch/apple_joypad.c rename to apple/common/apple_joypad.c index 76a8dc2853..0f5807441e 100644 --- a/apple/RetroArch/apple_joypad.c +++ b/apple/common/apple_joypad.c @@ -17,12 +17,12 @@ #include "general.h" #ifdef IOS -#include "../iOS/input/BTStack/btdynamic.c" -#include "../iOS/input/BTStack/wiimote.c" -#include "../iOS/input/BTStack/btpad.c" -#include "../iOS/input/BTStack/btpad_ps3.c" -#include "../iOS/input/BTStack/btpad_wii.c" -#include "../iOS/input/BTStack/btpad_queue.c" +#include "apple/iOS/bluetooth/btdynamic.c" +#include "apple/iOS/bluetooth/wiimote.c" +#include "apple/iOS/bluetooth/btpad.c" +#include "apple/iOS/bluetooth/btpad_ps3.c" +#include "apple/iOS/bluetooth/btpad_wii.c" +#include "apple/iOS/bluetooth/btpad_queue.c" #elif defined(OSX) #include "../OSX/hid_pad.c" #endif diff --git a/apple/RetroArch/keycode.h b/apple/common/keycode.h similarity index 91% rename from apple/RetroArch/keycode.h rename to apple/common/keycode.h index e24fe023d4..f0b8a1219a 100644 --- a/apple/RetroArch/keycode.h +++ b/apple/common/keycode.h @@ -153,4 +153,21 @@ enum { KEY_RightAlt = 230, KEY_RightGUI = 231 }; + +// + +#include "input/input_common.h" // < For rarch_key_map + +struct apple_key_name_map_entry +{ + const char* const keyname; + const uint32_t hid_id; +}; + +extern const struct apple_key_name_map_entry apple_key_name_map[]; +extern const struct rarch_key_map apple_key_map_hidusage[]; + + +const char* apple_keycode_hidusage_to_name(uint32_t hid_usage); + #endif diff --git a/apple/common/keycode.inc b/apple/common/keycode.inc new file mode 100644 index 0000000000..f9d7363113 --- /dev/null +++ b/apple/common/keycode.inc @@ -0,0 +1,222 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2013 - Jason Fetters + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include "keycode.h" + +const struct apple_key_name_map_entry apple_key_name_map[] = +{ + { "left", KEY_Left }, { "right", KEY_Right }, + { "up", KEY_Up }, { "down", KEY_Down }, + { "enter", KEY_Enter }, { "kp_enter", KP_Enter }, + { "space", KEY_Space }, { "tab", KEY_Tab }, + { "shift", KEY_LeftShift }, { "rshift", KEY_RightShift }, + { "ctrl", KEY_LeftControl }, { "alt", KEY_LeftAlt }, + { "escape", KEY_Escape }, { "backspace", KEY_DeleteForward }, + { "backquote", KEY_Grave }, { "pause", KEY_Pause }, + + { "f1", KEY_F1 }, { "f2", KEY_F2 }, + { "f3", KEY_F3 }, { "f4", KEY_F4 }, + { "f5", KEY_F5 }, { "f6", KEY_F6 }, + { "f7", KEY_F7 }, { "f8", KEY_F8 }, + { "f9", KEY_F9 }, { "f10", KEY_F10 }, + { "f11", KEY_F11 }, { "f12", KEY_F12 }, + + { "num0", KEY_0 }, { "num1", KEY_1 }, + { "num2", KEY_2 }, { "num3", KEY_3 }, + { "num4", KEY_4 }, { "num5", KEY_5 }, + { "num6", KEY_6 }, { "num7", KEY_7 }, + { "num8", KEY_8 }, { "num9", KEY_9 }, + + { "insert", KEY_Insert }, { "del", KEY_DeleteForward }, + { "home", KEY_Home }, { "end", KEY_End }, + { "pageup", KEY_PageUp }, { "pagedown", KEY_PageDown }, + + { "add", KP_Add }, { "subtract", KP_Subtract }, + { "multiply", KP_Multiply }, { "divide", KP_Divide }, + { "keypad0", KP_0 }, { "keypad1", KP_1 }, + { "keypad2", KP_2 }, { "keypad3", KP_3 }, + { "keypad4", KP_4 }, { "keypad5", KP_5 }, + { "keypad6", KP_6 }, { "keypad7", KP_7 }, + { "keypad8", KP_8 }, { "keypad9", KP_9 }, + + { "period", KEY_Period }, { "capslock", KEY_CapsLock }, + { "numlock", KP_NumLock }, { "print_screen", KEY_PrintScreen }, + { "scroll_lock", KEY_ScrollLock }, + + { "a", KEY_A }, { "b", KEY_B }, { "c", KEY_C }, { "d", KEY_D }, + { "e", KEY_E }, { "f", KEY_F }, { "g", KEY_G }, { "h", KEY_H }, + { "i", KEY_I }, { "j", KEY_J }, { "k", KEY_K }, { "l", KEY_L }, + { "m", KEY_M }, { "n", KEY_N }, { "o", KEY_O }, { "p", KEY_P }, + { "q", KEY_Q }, { "r", KEY_R }, { "s", KEY_S }, { "t", KEY_T }, + { "u", KEY_U }, { "v", KEY_V }, { "w", KEY_W }, { "x", KEY_X }, + { "y", KEY_Y }, { "z", KEY_Z }, + + { "nul", 0x00}, +}; + +const struct rarch_key_map apple_key_map_hidusage[] = +{ + { KEY_Delete, RETROK_BACKSPACE }, + { KEY_Tab, RETROK_TAB }, +// RETROK_CLEAR }, + { KEY_Enter, RETROK_RETURN }, + { KEY_Pause, RETROK_PAUSE }, + { KEY_Escape, RETROK_ESCAPE }, + { KEY_Space, RETROK_SPACE }, +// RETROK_EXCLAIM }, +// RETROK_QUOTEDBL }, +// RETROK_HASH }, +// RETROK_DOLLAR }, +// RETROK_AMPERSAND }, + { KEY_Quote, RETROK_QUOTE }, +// RETROK_LEFTPAREN }, +// RETROK_RIGHTPAREN }, +// RETROK_ASTERISK }, +// RETROK_PLUS }, + { KEY_Comma, RETROK_COMMA }, + { KEY_Minus, RETROK_MINUS }, + { KEY_Period, RETROK_PERIOD }, + { KEY_Slash, RETROK_SLASH }, + { KEY_0, RETROK_0 }, + { KEY_1, RETROK_1 }, + { KEY_2, RETROK_2 }, + { KEY_3, RETROK_3 }, + { KEY_4, RETROK_4 }, + { KEY_5, RETROK_5 }, + { KEY_6, RETROK_6 }, + { KEY_7, RETROK_7 }, + { KEY_8, RETROK_8 }, + { KEY_9, RETROK_9 }, +// RETROK_COLON }, + { KEY_Semicolon, RETROK_SEMICOLON }, +// RETROK_LESS }, + { KEY_Equals, RETROK_EQUALS }, +// RETROK_GREATER }, +// RETROK_QUESTION }, +// RETROK_AT }, + { KEY_LeftBracket, RETROK_LEFTBRACKET }, + { KEY_Backslash, RETROK_BACKSLASH }, + { KEY_RightBracket, RETROK_RIGHTBRACKET }, +// RETROK_CARET }, +// RETROK_UNDERSCORE }, + { KEY_Grave, RETROK_BACKQUOTE }, + { KEY_A, RETROK_a }, + { KEY_B, RETROK_b }, + { KEY_C, RETROK_c }, + { KEY_D, RETROK_d }, + { KEY_E, RETROK_e }, + { KEY_F, RETROK_f }, + { KEY_G, RETROK_g }, + { KEY_H, RETROK_h }, + { KEY_I, RETROK_i }, + { KEY_J, RETROK_j }, + { KEY_K, RETROK_k }, + { KEY_L, RETROK_l }, + { KEY_M, RETROK_m }, + { KEY_N, RETROK_n }, + { KEY_O, RETROK_o }, + { KEY_P, RETROK_p }, + { KEY_Q, RETROK_q }, + { KEY_R, RETROK_r }, + { KEY_S, RETROK_s }, + { KEY_T, RETROK_t }, + { KEY_U, RETROK_u }, + { KEY_V, RETROK_v }, + { KEY_W, RETROK_w }, + { KEY_X, RETROK_x }, + { KEY_Y, RETROK_y }, + { KEY_Z, RETROK_z }, + { KEY_DeleteForward, RETROK_DELETE }, + + { KP_0, RETROK_KP0 }, + { KP_1, RETROK_KP1 }, + { KP_2, RETROK_KP2 }, + { KP_3, RETROK_KP3 }, + { KP_4, RETROK_KP4 }, + { KP_5, RETROK_KP5 }, + { KP_6, RETROK_KP6 }, + { KP_7, RETROK_KP7 }, + { KP_8, RETROK_KP8 }, + { KP_9, RETROK_KP9 }, + { KP_Point, RETROK_KP_PERIOD }, + { KP_Divide, RETROK_KP_DIVIDE }, + { KP_Multiply, RETROK_KP_MULTIPLY }, + { KP_Subtract, RETROK_KP_MINUS }, + { KP_Add, RETROK_KP_PLUS }, + { KP_Enter, RETROK_KP_ENTER }, + { KP_Equals, RETROK_KP_EQUALS }, + + { KEY_Up, RETROK_UP }, + { KEY_Down, RETROK_DOWN }, + { KEY_Right, RETROK_RIGHT }, + { KEY_Left, RETROK_LEFT }, + { KEY_Insert, RETROK_INSERT }, + { KEY_Home, RETROK_HOME }, + { KEY_End, RETROK_END }, + { KEY_PageUp, RETROK_PAGEUP }, + { KEY_PageDown, RETROK_PAGEDOWN }, + + { KEY_F1, RETROK_F1 }, + { KEY_F2, RETROK_F2 }, + { KEY_F3, RETROK_F3 }, + { KEY_F4, RETROK_F4 }, + { KEY_F5, RETROK_F5 }, + { KEY_F6, RETROK_F6 }, + { KEY_F7, RETROK_F7 }, + { KEY_F8, RETROK_F8 }, + { KEY_F9, RETROK_F9 }, + { KEY_F10, RETROK_F10 }, + { KEY_F11, RETROK_F11 }, + { KEY_F12, RETROK_F12 }, + { KEY_F13, RETROK_F13 }, + { KEY_F14, RETROK_F14 }, + { KEY_F15, RETROK_F15 }, + +// RETROK_NUMLOCK }, + { KEY_CapsLock, RETROK_CAPSLOCK }, +// RETROK_SCROLLOCK }, + { KEY_RightShift, RETROK_RSHIFT }, + { KEY_LeftShift, RETROK_LSHIFT }, + { KEY_RightControl, RETROK_RCTRL }, + { KEY_LeftControl, RETROK_LCTRL }, + { KEY_RightAlt, RETROK_RALT }, + { KEY_LeftAlt, RETROK_LALT }, + { KEY_RightGUI, RETROK_RMETA }, + { KEY_LeftGUI, RETROK_RMETA }, +// RETROK_LSUPER }, +// RETROK_RSUPER }, +// RETROK_MODE }, +// RETROK_COMPOSE }, + +// RETROK_HELP }, + { KEY_PrintScreen, RETROK_PRINT }, +// RETROK_SYSREQ }, +// RETROK_BREAK }, + { KEY_Menu, RETROK_MENU }, +// RETROK_POWER }, +// RETROK_EURO }, +// RETROK_UNDO }, + { 0, RETROK_UNKNOWN } +}; + + +const char* apple_keycode_hidusage_to_name(uint32_t hid_usage) +{ + for (int i = 0; apple_key_name_map[i].hid_id; i ++) + if (apple_key_name_map[i].hid_id == hid_usage) + return apple_key_name_map[i].keyname; + + return "nul"; +} diff --git a/apple/RetroArch/main.m b/apple/common/main.m similarity index 80% rename from apple/RetroArch/main.m rename to apple/common/main.m index 4ba48c1933..211f6f7f30 100644 --- a/apple/RetroArch/main.m +++ b/apple/common/main.m @@ -23,7 +23,7 @@ #include "file.h" -//#define HAVE_DEBUG_FILELOG +char** apple_argv; id apple_platform; @@ -71,8 +71,6 @@ pthread_mutex_t stasis_mutex = PTHREAD_MUTEX_INITIALIZER; static void event_stasis(void* userdata) { - // HACK: uninit_drivers is the nuclear option; uninit_audio would be better but will - // crash when resuming. uninit_drivers(); pthread_mutex_lock(&stasis_mutex); pthread_mutex_unlock(&stasis_mutex); @@ -88,8 +86,14 @@ void apple_enter_stasis() } } -void apple_exit_stasis() +void apple_exit_stasis(bool reload_config) { + if (reload_config) + { + objc_clear_config_hack(); + config_load(); + } + if (apple_is_running) pthread_mutex_unlock(&stasis_mutex); } @@ -125,37 +129,38 @@ void apple_run_core(RAModuleInfo* core, const char* file) apple_core = core; apple_is_running = true; - + static char config_path[PATH_MAX]; static char core_path[PATH_MAX]; static char file_path[PATH_MAX]; - - static const char* argv[] = { "retroarch", "-c", config_path, "-L", core_path, file_path, 0 }; - if (apple_core) - strlcpy(config_path, apple_core.configPath.UTF8String, sizeof(config_path)); - else - strlcpy(config_path, RAModuleInfo.globalConfigPath.UTF8String, sizeof(config_path)); + if (!apple_argv) + { + NSString* config_to_use = apple_core ? apple_core.configFile : apple_platform.globalConfigFile; + strlcpy(config_path, config_to_use.UTF8String, sizeof(config_path)); + + static const char* const argv_game[] = { "retroarch", "-c", config_path, "-L", core_path, file_path, 0 }; + static const char* const argv_menu[] = { "retroarch", "-c", config_path, "--menu", 0 }; - if (file && core) - { - argv[3] = "-L"; - argv[4] = core_path; - strlcpy(core_path, apple_core.path.UTF8String, sizeof(core_path)); - strlcpy(file_path, file, sizeof(file_path)); - } - else - { - argv[3] = "--menu"; - argv[4] = 0; + if (file && core) + { + strlcpy(core_path, apple_core.path.UTF8String, sizeof(core_path)); + strlcpy(file_path, file, sizeof(file_path)); + } + + apple_argv = (char**)((file && core) ? argv_game : argv_menu); } - if (pthread_create(&apple_retro_thread, 0, rarch_main_spring, argv)) + if (pthread_create(&apple_retro_thread, 0, rarch_main_spring, apple_argv)) { + apple_argv = 0; + apple_rarch_exited((void*)1); return; } + apple_argv = 0; + pthread_detach(apple_retro_thread); } } diff --git a/apple/RetroArch/rarch_wrapper.h b/apple/common/rarch_wrapper.h similarity index 86% rename from apple/RetroArch/rarch_wrapper.h rename to apple/common/rarch_wrapper.h index b92cf5eb83..92bce93c5e 100644 --- a/apple/RetroArch/rarch_wrapper.h +++ b/apple/common/rarch_wrapper.h @@ -23,12 +23,15 @@ char* ios_get_rarch_system_directory(); // These functions should only be called as arguments to dispatch_sync void apple_rarch_exited (void* result); -// These functions must only be called in gfx/context/ioseagl_ctx.c +// These functions must only be called in gfx/context/apple_gl_context.c bool apple_init_game_view(void); void apple_destroy_game_view(void); +bool apple_set_video_mode(unsigned width, unsigned height, bool fullscreen); void apple_flip_game_view(void); void apple_set_game_view_sync(unsigned interval); void apple_get_game_view_size(unsigned *width, unsigned *height); +void apple_update_window_title(void); +bool apple_game_view_has_focus(void); void *apple_get_proc_address(const char *symbol_name); #ifdef IOS diff --git a/apple/RetroArch/setting_data.h b/apple/common/setting_data.h similarity index 60% rename from apple/RetroArch/setting_data.h rename to apple/common/setting_data.h index e7f8246dc5..208fb8ea73 100644 --- a/apple/RetroArch/setting_data.h +++ b/apple/common/setting_data.h @@ -18,8 +18,8 @@ #include "general.h" -enum setting_type { ST_NONE, ST_BOOL, ST_INT, ST_FLOAT, ST_PATH, ST_STRING, ST_HEX, ST_GROUP, ST_SUB_GROUP, - ST_END_GROUP, ST_END_SUB_GROUP }; +enum setting_type { ST_NONE, ST_BOOL, ST_INT, ST_FLOAT, ST_PATH, ST_STRING, ST_HEX, ST_BIND, + ST_GROUP, ST_SUB_GROUP, ST_END_GROUP, ST_END_SUB_GROUP }; typedef struct { @@ -28,36 +28,43 @@ typedef struct const char* name; void* value; + uint32_t size; const char* short_description; - const char* long_description; - - const char** values; uint32_t input_player; + double min; double max; bool allow_blank; } rarch_setting_t; -#define START_GROUP(NAME) { ST_GROUP, NAME, 0, 0, 0, 0, 0, 0.0, 0.0, false }, -#define END_GROUP() { ST_END_GROUP, 0, 0, 0, 0, 0, 0, 0.0, 0.0, false }, -#define START_SUB_GROUP(NAME) { ST_SUB_GROUP, NAME, 0, 0, 0, 0, 0, 0.0, 0.0, false }, -#define END_SUB_GROUP() { ST_END_SUB_GROUP, 0, 0, 0, 0, 0, 0, 0.0, 0.0, false }, -#define START_GROUP(NAME) { ST_GROUP, NAME, 0, 0, 0, 0, 0, 0.0, 0.0, false }, -#define END_GROUP() { ST_END_GROUP, 0, 0, 0, 0, 0, 0, 0.0, 0.0, false }, -#define CONFIG_BOOL(TARGET, NAME, SHORT) { ST_BOOL, NAME, &TARGET, SHORT, 0, 0, 0, 0.0, 0.0, false }, -#define CONFIG_INT(TARGET, NAME, SHORT) { ST_INT, NAME, &TARGET, SHORT, 0, 0, 0, 0.0, 0.0, false }, -#define CONFIG_FLOAT(TARGET, NAME, SHORT) { ST_FLOAT, NAME, &TARGET, SHORT, 0, 0, 0, 0.0, 0.0, false }, -#define CONFIG_PATH(TARGET, NAME, SHORT) { ST_PATH, NAME, &TARGET, SHORT, 0, 0, 0, 0.0, 0.0, false }, -#define CONFIG_STRING(TARGET, NAME, SHORT) { ST_STRING, NAME, &TARGET, SHORT, 0, 0, 0, 0.0, 0.0, false }, -#define CONFIG_HEX(TARGET, NAME, SHORT) { ST_HEX, NAME, &TARGET, SHORT, 0, 0, 0, 0.0, 0.0, false }, +extern struct settings fake_settings; +extern struct global fake_extern; + +// HACK +#define g_settings fake_settings +#define g_extern fake_extern + +#define START_GROUP(NAME) { ST_GROUP, NAME }, +#define END_GROUP() { ST_END_GROUP }, +#define START_SUB_GROUP(NAME) { ST_SUB_GROUP, NAME }, +#define END_SUB_GROUP() { ST_END_SUB_GROUP }, +#define CONFIG_BOOL(TARGET, NAME, SHORT) { ST_BOOL, NAME, &TARGET, sizeof(TARGET), SHORT }, +#define CONFIG_INT(TARGET, NAME, SHORT) { ST_INT, NAME, &TARGET, sizeof(TARGET), SHORT }, +#define CONFIG_FLOAT(TARGET, NAME, SHORT) { ST_FLOAT, NAME, &TARGET, sizeof(TARGET), SHORT }, +#define CONFIG_PATH(TARGET, NAME, SHORT) { ST_PATH, NAME, &TARGET, sizeof(TARGET), SHORT }, +#define CONFIG_STRING(TARGET, NAME, SHORT) { ST_STRING, NAME, &TARGET, sizeof(TARGET), SHORT }, +#define CONFIG_HEX(TARGET, NAME, SHORT) { ST_HEX, NAME, &TARGET, sizeof(TARGET), SHORT }, + +#define CONFIG_BIND(TARGET, PLAYER, NAME, SHORT) { ST_BIND, NAME, &TARGET, sizeof(TARGET), SHORT, PLAYER }, const rarch_setting_t setting_data[] = { /***********/ /* DRIVERS */ /***********/ +#if 0 START_GROUP("Drivers") START_SUB_GROUP("Drivers") CONFIG_STRING(g_settings.video.driver, "video_driver", "Video Driver") @@ -67,6 +74,7 @@ const rarch_setting_t setting_data[] = CONFIG_STRING(g_settings.input.joypad_driver, "input_joypad_driver", "Joypad Driver") END_SUB_GROUP() END_GROUP() +#endif /*********/ /* PATHS */ @@ -92,7 +100,6 @@ const rarch_setting_t setting_data[] = END_GROUP() - /*************/ /* EMULATION */ /*************/ @@ -113,6 +120,82 @@ const rarch_setting_t setting_data[] = END_SUB_GROUP() END_GROUP() + /*********/ + /* VIDEO */ + /*********/ + START_GROUP("Video") + START_SUB_GROUP("Monitor") + CONFIG_INT(g_settings.video.monitor_index, "video_monitor_index", "Monitor Index") + CONFIG_BOOL(g_settings.video.fullscreen, "video_fullscreen", "Use Fullscreen mode") // if (!g_extern.force_fullscreen) + CONFIG_BOOL(g_settings.video.windowed_fullscreen, "video_windowed_fullscreen", "Windowed Fullscreen Mode") + CONFIG_INT(g_settings.video.fullscreen_x, "video_fullscreen_x", "Fullscreen Width") + CONFIG_INT(g_settings.video.fullscreen_y, "video_fullscreen_y", "Fullscreen Height") + CONFIG_FLOAT(g_settings.video.refresh_rate, "video_refresh_rate", "Refresh Rate") + END_SUB_GROUP() + +#if 0 + /* Video: Window Manager */ + START_SUB_GROUP("Window Manager") + CONFIG_BOOL(g_settings.video.disable_composition, "video_disable_composition", "Disable WM Composition") + END_SUB_GROUP() +#endif + + START_SUB_GROUP("Aspect") + CONFIG_BOOL(g_settings.video.force_aspect, "video_force_aspect", "Force aspect ratio") + CONFIG_FLOAT(g_settings.video.aspect_ratio, "video_aspect_ratio", "Aspect Ratio") + CONFIG_BOOL(g_settings.video.aspect_ratio_auto, "video_aspect_ratio_auto", "Use Auto Aspect Ratio") + CONFIG_INT(g_settings.video.aspect_ratio_idx, "aspect_ratio_index", "Aspect Ratio Index") + END_SUB_GROUP() + + START_SUB_GROUP("Scaling") + CONFIG_FLOAT(g_settings.video.xscale, "video_xscale", "X Scale") + CONFIG_FLOAT(g_settings.video.yscale, "video_yscale", "Y Scale") + CONFIG_BOOL(g_settings.video.scale_integer, "video_scale_integer", "Force integer scaling") + + CONFIG_INT(g_extern.console.screen.viewports.custom_vp.x, "custom_viewport_x", "Custom Viewport X") + CONFIG_INT(g_extern.console.screen.viewports.custom_vp.y, "custom_viewport_y", "Custom Viewport Y") + CONFIG_INT(g_extern.console.screen.viewports.custom_vp.width, "custom_viewport_width", "Custom Viewport Width") + CONFIG_INT(g_extern.console.screen.viewports.custom_vp.height, "custom_viewport_height", "Custom Viewport Height") + + CONFIG_BOOL(g_settings.video.smooth, "video_smooth", "Use bilinear filtering") + END_SUB_GROUP() + + START_SUB_GROUP("Shader") + CONFIG_BOOL(g_settings.video.shader_enable, "video_shader_enable", "Enable Shaders") + CONFIG_PATH(g_settings.video.shader_dir, "video_shader_dir", "Shader Directory") + CONFIG_PATH(g_settings.video.shader_path, "video_shader", "Shader") + END_SUB_GROUP() + + START_SUB_GROUP("Sync") + CONFIG_BOOL(g_settings.video.threaded, "video_threaded", "Use threaded video") + CONFIG_BOOL(g_settings.video.vsync, "video_vsync", "Use VSync") + CONFIG_BOOL(g_settings.video.hard_sync, "video_hard_sync", "Use OpenGL Hard Sync") + CONFIG_INT(g_settings.video.hard_sync_frames, "video_hard_sync_frames", "Number of Hard Sync frames") // 0 - 3 + END_SUB_GROUP() + + START_SUB_GROUP("Misc") + CONFIG_BOOL(g_settings.video.post_filter_record, "video_post_filter_record", "Post filter record") + CONFIG_BOOL(g_settings.video.gpu_record, "video_gpu_record", "GPU Record") + CONFIG_BOOL(g_settings.video.gpu_screenshot, "video_gpu_screenshot", "GPU Screenshot") + CONFIG_BOOL(g_settings.video.allow_rotate, "video_allow_rotate", "Allow rotation") + CONFIG_BOOL(g_settings.video.crop_overscan, "video_crop_overscan", "Crop Overscan") + + #ifdef HAVE_DYLIB + CONFIG_PATH(g_settings.video.filter_path, "video_filter", "Software filter"), + #endif + END_SUB_GROUP() + + START_SUB_GROUP("Messages") + CONFIG_PATH(g_settings.video.font_path, "video_font_path", "Font Path") + CONFIG_FLOAT(g_settings.video.font_size, "video_font_size", "Font Size") + CONFIG_BOOL(g_settings.video.font_enable, "video_font_enable", "Font Enable") + CONFIG_BOOL(g_settings.video.font_scale, "video_font_scale", "Font Scale") + CONFIG_FLOAT(g_settings.video.msg_pos_x, "video_message_pos_x", "Message X Position") + CONFIG_FLOAT(g_settings.video.msg_pos_y, "video_message_pos_y", "Message Y Position") + /* message color */ + END_SUB_GROUP() + END_GROUP() + /*********/ /* AUDIO */ /*********/ @@ -162,8 +245,8 @@ const rarch_setting_t setting_data[] = /* Input: Overlay */ #ifdef HAVE_OVERLAY CONFIG_PATH(g_settings.input.overlay, "input_overlay", "Input Overlay") - CONFIG_FLOAT(g_settings.input.overlay_opacity, "overlay_opacity", "Overlay Opacity") - CONFIG_FLOAT(g_settings.input.overlay_scale, "overlay_scale", "Overlay Scale") + CONFIG_FLOAT(g_settings.input.overlay_opacity, "input_overlay_opacity", "Overlay Opacity") + CONFIG_FLOAT(g_settings.input.overlay_scale, "input_overlay_scale", "Overlay Scale") #endif /* Input: Android */ @@ -175,79 +258,67 @@ const rarch_setting_t setting_data[] = CONFIG_INT(g_settings.input.icade_profile[3], "input_autodetect_icade_profile_pad4", "iCade 4") #endif END_SUB_GROUP() - END_GROUP() - /*********/ - /* VIDEO */ - /*********/ - START_GROUP("Video") - START_SUB_GROUP("Monitor") - CONFIG_INT(g_settings.video.monitor_index, "video_monitor_index", "Monitor Index") - CONFIG_BOOL(g_settings.video.fullscreen, "video_fullscreen", "Use Fullscreen mode") // if (!g_extern.force_fullscreen) - CONFIG_BOOL(g_settings.video.windowed_fullscreen, "video_windowed_fullscreen", "Windowed Fullscreen Mode") - CONFIG_INT(g_settings.video.fullscreen_x, "video_fullscreen_x", "Fullscreen Width") - CONFIG_INT(g_settings.video.fullscreen_y, "video_fullscreen_y", "Fullscreen Height") - CONFIG_FLOAT(g_settings.video.refresh_rate, "video_refresh_rate", "Refresh Rate") - END_SUB_GROUP() - - /* Video: Window Manager */ - START_SUB_GROUP("Window Manager") - CONFIG_BOOL(g_settings.video.disable_composition, "video_disable_composition", "Disable WM Composition") + START_SUB_GROUP("Meta Keys") + CONFIG_BIND(g_settings.input.binds[0][RARCH_FAST_FORWARD_KEY], 0, "toggle_fast_forward", "Fast forward toggle") + CONFIG_BIND(g_settings.input.binds[0][RARCH_FAST_FORWARD_HOLD_KEY], 0, "hold_fast_forward", "Fast forward hold") + CONFIG_BIND(g_settings.input.binds[0][RARCH_LOAD_STATE_KEY], 0, "load_state", "Load state") + CONFIG_BIND(g_settings.input.binds[0][RARCH_SAVE_STATE_KEY], 0, "save_state", "Save state") + CONFIG_BIND(g_settings.input.binds[0][RARCH_FULLSCREEN_TOGGLE_KEY], 0, "toggle_fullscreen", "Fullscreen toggle") + CONFIG_BIND(g_settings.input.binds[0][RARCH_QUIT_KEY], 0, "exit_emulator", "Quit RetroArch") + CONFIG_BIND(g_settings.input.binds[0][RARCH_STATE_SLOT_PLUS], 0, "state_slot_increase", "Savestate slot +") + CONFIG_BIND(g_settings.input.binds[0][RARCH_STATE_SLOT_MINUS], 0, "state_slot_decrease", "Savestate slot -") + CONFIG_BIND(g_settings.input.binds[0][RARCH_REWIND], 0, "rewind", "Rewind") + CONFIG_BIND(g_settings.input.binds[0][RARCH_MOVIE_RECORD_TOGGLE], 0, "movie_record_toggle", "Movie record toggle") + CONFIG_BIND(g_settings.input.binds[0][RARCH_PAUSE_TOGGLE], 0, "pause_toggle", "Pause toggle") + CONFIG_BIND(g_settings.input.binds[0][RARCH_FRAMEADVANCE], 0, "frame_advance", "Frameadvance") + CONFIG_BIND(g_settings.input.binds[0][RARCH_RESET], 0, "reset", "Reset game") + CONFIG_BIND(g_settings.input.binds[0][RARCH_SHADER_NEXT], 0, "shader_next", "Next shader") + CONFIG_BIND(g_settings.input.binds[0][RARCH_SHADER_PREV], 0, "shader_prev", "Previous shader") + CONFIG_BIND(g_settings.input.binds[0][RARCH_CHEAT_INDEX_PLUS], 0, "cheat_index_plus", "Cheat index +") + CONFIG_BIND(g_settings.input.binds[0][RARCH_CHEAT_INDEX_MINUS], 0, "cheat_index_minus", "Cheat index -") + CONFIG_BIND(g_settings.input.binds[0][RARCH_CHEAT_TOGGLE], 0, "cheat_toggle", "Cheat toggle") + CONFIG_BIND(g_settings.input.binds[0][RARCH_SCREENSHOT], 0, "screenshot", "Take screenshot") + CONFIG_BIND(g_settings.input.binds[0][RARCH_DSP_CONFIG], 0, "dsp_config", "DSP config") + CONFIG_BIND(g_settings.input.binds[0][RARCH_MUTE], 0, "audio_mute", "Audio mute toggle") + CONFIG_BIND(g_settings.input.binds[0][RARCH_NETPLAY_FLIP], 0, "netplay_flip_players", "Netplay flip players") + CONFIG_BIND(g_settings.input.binds[0][RARCH_SLOWMOTION], 0, "slowmotion", "Slow motion") + CONFIG_BIND(g_settings.input.binds[0][RARCH_ENABLE_HOTKEY], 0, "enable_hotkey", "Enable hotkeys") + CONFIG_BIND(g_settings.input.binds[0][RARCH_VOLUME_UP], 0, "volume_up", "Volume +") + CONFIG_BIND(g_settings.input.binds[0][RARCH_VOLUME_DOWN], 0, "volume_down", "Volume -") + CONFIG_BIND(g_settings.input.binds[0][RARCH_OVERLAY_NEXT], 0, "overlay_next", "Overlay next") + CONFIG_BIND(g_settings.input.binds[0][RARCH_DISK_EJECT_TOGGLE], 0, "disk_eject_toggle", "Disk eject toggle") + CONFIG_BIND(g_settings.input.binds[0][RARCH_DISK_NEXT], 0, "disk_next", "Disk next") + CONFIG_BIND(g_settings.input.binds[0][RARCH_GRAB_MOUSE_TOGGLE], 0, "grab_mouse_toggle", "Grab mouse toggle") + CONFIG_BIND(g_settings.input.binds[0][RARCH_MENU_TOGGLE], 0, "menu_toggle", "RGUI menu toggle") END_SUB_GROUP() - START_SUB_GROUP("Aspect") - CONFIG_BOOL(g_settings.video.force_aspect, "video_force_aspect", "Force aspect ratio") - CONFIG_FLOAT(g_settings.video.aspect_ratio, "video_aspect_ratio", "Aspect Ratio") - CONFIG_INT(g_settings.video.aspect_ratio_idx, "aspect_ratio_index", "Aspect Ratio Index") - CONFIG_BOOL(g_settings.video.aspect_ratio_auto, "video_aspect_ratio_auto", "Use Auto Aspect Ratio") - END_SUB_GROUP() - - START_SUB_GROUP("Scaling") - CONFIG_FLOAT(g_settings.video.xscale, "video_xscale", "X Scale") - CONFIG_FLOAT(g_settings.video.yscale, "video_yscale", "Y Scale") - CONFIG_BOOL(g_settings.video.scale_integer, "video_scale_integer", "Force integer scaling") - - CONFIG_INT(g_extern.console.screen.viewports.custom_vp.x, "custom_viewport_x", "Custom Viewport X") - CONFIG_INT(g_extern.console.screen.viewports.custom_vp.y, "custom_viewport_y", "Custom Viewport Y") - CONFIG_INT(g_extern.console.screen.viewports.custom_vp.width, "custom_viewport_width", "Custom Viewport Width") - CONFIG_INT(g_extern.console.screen.viewports.custom_vp.height, "custom_viewport_height", "Custom Viewport Height") - - CONFIG_BOOL(g_settings.video.smooth, "video_smooth", "Use bilinear filtering") - END_SUB_GROUP() - - START_SUB_GROUP("Shader") - CONFIG_BOOL(g_settings.video.shader_enable, "video_shader_enable", "Enable Shaders") - CONFIG_PATH(g_settings.video.shader_dir, "video_shader_dir", "Shader Directory") - CONFIG_PATH(g_settings.video.shader_path, "video_shader", "Shader") - END_SUB_GROUP() - - START_SUB_GROUP("Sync") - CONFIG_BOOL(g_settings.video.threaded, "video_threaded", "Use threaded video") - CONFIG_BOOL(g_settings.video.vsync, "video_vsync", "Use VSync") - CONFIG_BOOL(g_settings.video.hard_sync, "video_hard_sync", "Use OpenGL Hard Sync") - CONFIG_INT(g_settings.video.hard_sync_frames, "video_hard_sync_frames", "Number of Hard Sync frames") // 0 - 3 - END_SUB_GROUP() - - START_SUB_GROUP("Misc") - CONFIG_BOOL(g_settings.video.post_filter_record, "video_post_filter_record", "Post filter record") - CONFIG_BOOL(g_settings.video.gpu_record, "video_gpu_record", "GPU Record") - CONFIG_BOOL(g_settings.video.gpu_screenshot, "video_gpu_screenshot", "GPU Screenshot") - CONFIG_BOOL(g_settings.video.allow_rotate, "video_allow_rotate", "Allow rotation") - CONFIG_BOOL(g_settings.video.crop_overscan, "video_crop_overscan", "Crop Overscan") - - #ifdef HAVE_DYLIB - CONFIG_PATH(g_settings.video.filter_path, "video_filter", "Software filter"), - #endif - END_SUB_GROUP() - - START_SUB_GROUP("Messages") - CONFIG_PATH(g_settings.video.font_path, "video_font_path", "Font Path") - CONFIG_FLOAT(g_settings.video.font_size, "video_font_size", "Font Size") - CONFIG_BOOL(g_settings.video.font_enable, "video_font_enable", "Font Enable") - CONFIG_BOOL(g_settings.video.font_scale, "video_font_scale", "Font Scale") - CONFIG_FLOAT(g_settings.video.msg_pos_x, "video_message_pos_x", "Message X Position") - CONFIG_FLOAT(g_settings.video.msg_pos_y, "video_message_pos_y", "Message Y Position") - /* message color */ + START_SUB_GROUP("Player 1") + CONFIG_BIND(g_settings.input.binds[0][RETRO_DEVICE_ID_JOYPAD_B], 1, "b", "B button (down)") + CONFIG_BIND(g_settings.input.binds[0][RETRO_DEVICE_ID_JOYPAD_Y], 1, "y", "Y button (left)") + CONFIG_BIND(g_settings.input.binds[0][RETRO_DEVICE_ID_JOYPAD_SELECT],1, "select", "Select button") + CONFIG_BIND(g_settings.input.binds[0][RETRO_DEVICE_ID_JOYPAD_START], 1, "start", "Start button") + CONFIG_BIND(g_settings.input.binds[0][RETRO_DEVICE_ID_JOYPAD_UP], 1, "up", "Up D-pad") + CONFIG_BIND(g_settings.input.binds[0][RETRO_DEVICE_ID_JOYPAD_DOWN], 1, "down", "Down D-pad") + CONFIG_BIND(g_settings.input.binds[0][RETRO_DEVICE_ID_JOYPAD_LEFT], 1, "left", "Left D-pad") + CONFIG_BIND(g_settings.input.binds[0][RETRO_DEVICE_ID_JOYPAD_RIGHT], 1, "right", "Right D-pad") + CONFIG_BIND(g_settings.input.binds[0][RETRO_DEVICE_ID_JOYPAD_A], 1, "a", "A button (right)") + CONFIG_BIND(g_settings.input.binds[0][RETRO_DEVICE_ID_JOYPAD_X], 1, "x", "X button (top)") + CONFIG_BIND(g_settings.input.binds[0][RETRO_DEVICE_ID_JOYPAD_L], 1, "l", "L button (left shoulder)") + CONFIG_BIND(g_settings.input.binds[0][RETRO_DEVICE_ID_JOYPAD_R], 1, "r", "R button (right shoulder)") + CONFIG_BIND(g_settings.input.binds[0][RETRO_DEVICE_ID_JOYPAD_L2], 1, "l2", "L2 button (left shoulder #2)") + CONFIG_BIND(g_settings.input.binds[0][RETRO_DEVICE_ID_JOYPAD_R2], 1, "r2", "R2 button (right shoulder #2)") + CONFIG_BIND(g_settings.input.binds[0][RETRO_DEVICE_ID_JOYPAD_L3], 1, "l3", "L3 button (left analog button)") + CONFIG_BIND(g_settings.input.binds[0][RETRO_DEVICE_ID_JOYPAD_R3], 1, "r3", "R3 button (right analog button)") + CONFIG_BIND(g_settings.input.binds[0][RARCH_TURBO_ENABLE], 1, "turbo", "Turbo enable") + CONFIG_BIND(g_settings.input.binds[0][RARCH_ANALOG_LEFT_X_PLUS], 1, "l_x_plus", "Left analog X+ (right)") + CONFIG_BIND(g_settings.input.binds[0][RARCH_ANALOG_LEFT_X_MINUS], 1, "l_x_minus", "Left analog X- (left)") + CONFIG_BIND(g_settings.input.binds[0][RARCH_ANALOG_LEFT_Y_PLUS], 1, "l_y_plus", "Left analog Y+ (down)") + CONFIG_BIND(g_settings.input.binds[0][RARCH_ANALOG_LEFT_Y_MINUS], 1, "l_y_minus", "Left analog Y- (up)") + CONFIG_BIND(g_settings.input.binds[0][RARCH_ANALOG_RIGHT_X_PLUS], 1, "r_x_plus", "Right analog X+ (right)") + CONFIG_BIND(g_settings.input.binds[0][RARCH_ANALOG_RIGHT_X_MINUS], 1, "r_x_minus", "Right analog X- (left)") + CONFIG_BIND(g_settings.input.binds[0][RARCH_ANALOG_RIGHT_Y_PLUS], 1, "r_y_plus", "Right analog Y+ (down)") + CONFIG_BIND(g_settings.input.binds[0][RARCH_ANALOG_RIGHT_Y_MINUS], 1, "r_y_minus", "Right analog Y- (up)") END_SUB_GROUP() END_GROUP() @@ -266,4 +337,11 @@ const rarch_setting_t setting_data[] = { 0 } }; +// HACK +#undef g_settings +#undef g_extern + +// Keyboard +#include "keycode.h" + #endif \ No newline at end of file diff --git a/apple/RetroArch/utility.m b/apple/common/utility.m similarity index 96% rename from apple/RetroArch/utility.m rename to apple/common/utility.m index 0836e9fdec..1c8ffb8608 100644 --- a/apple/RetroArch/utility.m +++ b/apple/common/utility.m @@ -57,7 +57,7 @@ NSString* objc_get_value_from_config(config_file_t* config, NSString* name, NSSt if (config) config_get_string(config, [name UTF8String], &data); - NSString* result = data ? [NSString stringWithUTF8String:data] : defaultValue; + NSString* result = data ? @(data) : defaultValue; free(data); return result; } @@ -110,6 +110,11 @@ char* ios_get_rarch_system_directory() return self.sections[indexPath.section][indexPath.row + 1]; } +- (void)reset +{ + self.sections = [NSMutableArray array]; + [self.tableView reloadData]; +} @end #endif diff --git a/apple/iOS/RALogView.m b/apple/iOS/RALogView.m index d384515334..af802dceca 100644 --- a/apple/iOS/RALogView.m +++ b/apple/iOS/RALogView.m @@ -34,7 +34,7 @@ void ios_add_log_message(const char* format, ...) va_start(args, format); vsnprintf(buffer, 512, format, args); va_end(args); - [g_messages addObject:[NSString stringWithUTF8String: buffer]]; + [g_messages addObject:@(buffer)]; pthread_mutex_unlock(&g_lock); } diff --git a/apple/iOS/input/BTStack/btdynamic.c b/apple/iOS/bluetooth/btdynamic.c similarity index 98% rename from apple/iOS/input/BTStack/btdynamic.c rename to apple/iOS/bluetooth/btdynamic.c index 9dfa1a95b6..bfdcde2857 100644 --- a/apple/iOS/input/BTStack/btdynamic.c +++ b/apple/iOS/bluetooth/btdynamic.c @@ -17,7 +17,7 @@ #include #include -#include "../../../RetroArch/rarch_wrapper.h" +#include "apple/common/rarch_wrapper.h" #define BUILDING_BTDYNAMIC #include "btdynamic.h" diff --git a/apple/iOS/input/BTStack/btdynamic.h b/apple/iOS/bluetooth/btdynamic.h similarity index 100% rename from apple/iOS/input/BTStack/btdynamic.h rename to apple/iOS/bluetooth/btdynamic.h diff --git a/apple/iOS/input/BTStack/btpad.c b/apple/iOS/bluetooth/btpad.c similarity index 99% rename from apple/iOS/input/BTStack/btpad.c rename to apple/iOS/bluetooth/btpad.c index babb0ba3fc..0a6335df3e 100644 --- a/apple/iOS/input/BTStack/btpad.c +++ b/apple/iOS/bluetooth/btpad.c @@ -20,7 +20,7 @@ #include #include -#include "../../../RetroArch/rarch_wrapper.h" +#include "apple/common/rarch_wrapper.h" #include "btdynamic.h" #include "btpad.h" #include "btpad_queue.h" diff --git a/apple/iOS/input/BTStack/btpad.h b/apple/iOS/bluetooth/btpad.h similarity index 100% rename from apple/iOS/input/BTStack/btpad.h rename to apple/iOS/bluetooth/btpad.h diff --git a/apple/iOS/input/BTStack/btpad_ps3.c b/apple/iOS/bluetooth/btpad_ps3.c similarity index 97% rename from apple/iOS/input/BTStack/btpad_ps3.c rename to apple/iOS/bluetooth/btpad_ps3.c index e2079ef6ea..d681358e62 100644 --- a/apple/iOS/input/BTStack/btpad_ps3.c +++ b/apple/iOS/bluetooth/btpad_ps3.c @@ -18,7 +18,7 @@ #include #include "boolean.h" -#include "../../../RetroArch/rarch_wrapper.h" +#include "apple/common/rarch_wrapper.h" #include "btdynamic.h" #include "btpad.h" @@ -37,7 +37,7 @@ struct btpad_ps3_data static void btpad_ps3_send_control(struct btpad_ps3_data* device) { - // TODO: Can this be modified to turn of motion tracking? + // TODO: Can this be modified to turn off motion tracking? static uint8_t report_buffer[] = { 0x52, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, diff --git a/apple/iOS/input/BTStack/btpad_queue.c b/apple/iOS/bluetooth/btpad_queue.c similarity index 100% rename from apple/iOS/input/BTStack/btpad_queue.c rename to apple/iOS/bluetooth/btpad_queue.c diff --git a/apple/iOS/input/BTStack/btpad_queue.h b/apple/iOS/bluetooth/btpad_queue.h similarity index 100% rename from apple/iOS/input/BTStack/btpad_queue.h rename to apple/iOS/bluetooth/btpad_queue.h diff --git a/apple/iOS/input/BTStack/btpad_wii.c b/apple/iOS/bluetooth/btpad_wii.c similarity index 98% rename from apple/iOS/input/BTStack/btpad_wii.c rename to apple/iOS/bluetooth/btpad_wii.c index 8f4bbc569c..c8475fb4ab 100644 --- a/apple/iOS/input/BTStack/btpad_wii.c +++ b/apple/iOS/bluetooth/btpad_wii.c @@ -18,7 +18,7 @@ #include #include "boolean.h" -#include "../../../RetroArch/rarch_wrapper.h" +#include "apple/common/rarch_wrapper.h" #include "btdynamic.h" #include "btpad.h" diff --git a/apple/iOS/input/BTStack/btstack/btstack.h b/apple/iOS/bluetooth/btstack/btstack.h similarity index 100% rename from apple/iOS/input/BTStack/btstack/btstack.h rename to apple/iOS/bluetooth/btstack/btstack.h diff --git a/apple/iOS/input/BTStack/btstack/hci_cmds.h b/apple/iOS/bluetooth/btstack/hci_cmds.h similarity index 100% rename from apple/iOS/input/BTStack/btstack/hci_cmds.h rename to apple/iOS/bluetooth/btstack/hci_cmds.h diff --git a/apple/iOS/input/BTStack/btstack/linked_list.h b/apple/iOS/bluetooth/btstack/linked_list.h similarity index 100% rename from apple/iOS/input/BTStack/btstack/linked_list.h rename to apple/iOS/bluetooth/btstack/linked_list.h diff --git a/apple/iOS/input/BTStack/btstack/run_loop.h b/apple/iOS/bluetooth/btstack/run_loop.h similarity index 100% rename from apple/iOS/input/BTStack/btstack/run_loop.h rename to apple/iOS/bluetooth/btstack/run_loop.h diff --git a/apple/iOS/input/BTStack/btstack/sdp_util.h b/apple/iOS/bluetooth/btstack/sdp_util.h similarity index 100% rename from apple/iOS/input/BTStack/btstack/sdp_util.h rename to apple/iOS/bluetooth/btstack/sdp_util.h diff --git a/apple/iOS/input/BTStack/btstack/utils.h b/apple/iOS/bluetooth/btstack/utils.h similarity index 100% rename from apple/iOS/input/BTStack/btstack/utils.h rename to apple/iOS/bluetooth/btstack/utils.h diff --git a/apple/iOS/input/BTStack/wiimote.c b/apple/iOS/bluetooth/wiimote.c similarity index 100% rename from apple/iOS/input/BTStack/wiimote.c rename to apple/iOS/bluetooth/wiimote.c diff --git a/apple/iOS/input/BTStack/wiimote.h b/apple/iOS/bluetooth/wiimote.h similarity index 100% rename from apple/iOS/input/BTStack/wiimote.h rename to apple/iOS/bluetooth/wiimote.h diff --git a/apple/iOS/browser.m b/apple/iOS/browser.m index 62b2808745..c0aa8410e2 100644 --- a/apple/iOS/browser.m +++ b/apple/iOS/browser.m @@ -16,7 +16,7 @@ #include #include -#import "../RetroArch/RetroArch_Apple.h" +#import "apple/common/RetroArch_Apple.h" #import "views.h" #include "conf/config_file.h" @@ -29,50 +29,47 @@ { NSString* _path; NSMutableArray* _sectionNames; + id _delegate; } -+ (id)directoryListAtBrowseRoot -{ - NSString* rootPath = RetroArch_iOS.get.documentsDirectory; - NSString* ragPath = [rootPath stringByAppendingPathComponent:@"RetroArchGames"]; - RADirectoryList* list = [RADirectoryList directoryListForPath:path_is_directory(ragPath.UTF8String) ? ragPath : rootPath]; - return list; -} -+ (id)directoryListForPath:(NSString*)path -{ - // NOTE: Don't remove or ignore this abstraction, this function will be expanded when cover art comes back. - return [[RADirectoryList alloc] initWithPath:path]; -} - -- (id)initWithPath:(NSString*)path +- (id)initWithPath:(NSString*)path delegate:(id)delegate { _path = path; + _delegate = delegate; self = [super initWithStyle:UITableViewStylePlain]; self.title = path.lastPathComponent; self.hidesHeaders = YES; - NSMutableArray *toolbarButtons = [[NSMutableArray alloc] initWithCapacity:3]; + NSMutableArray *toolbarButtons = [[NSMutableArray alloc] initWithCapacity:3]; - UIBarButtonItem *refreshButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh target:self action:@selector(refresh)]; - refreshButton.style = UIBarButtonItemStyleBordered; - [toolbarButtons addObject:refreshButton]; + UIBarButtonItem *refreshButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh target:self action:@selector(refresh)]; + refreshButton.style = UIBarButtonItemStyleBordered; + [toolbarButtons addObject:refreshButton]; - UIBarButtonItem *flexibleSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:self action:nil]; - [toolbarButtons addObject:flexibleSpace]; + UIBarButtonItem *flexibleSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:self action:nil]; + [toolbarButtons addObject:flexibleSpace]; - UIBarButtonItem *newFolderButton = [[UIBarButtonItem alloc] initWithTitle:@"New Folder" style:UIBarButtonItemStyleBordered target:self action:@selector(createNewFolder)]; - [toolbarButtons addObject:newFolderButton]; + UIBarButtonItem *newFolderButton = [[UIBarButtonItem alloc] initWithTitle:@"New Folder" style:UIBarButtonItemStyleBordered target:self action:@selector(createNewFolder)]; + [toolbarButtons addObject:newFolderButton]; - [[[RetroArch_iOS get] toolbar] setItems:toolbarButtons]; - [self setToolbarItems:toolbarButtons]; - - [self refresh]; + [[[RetroArch_iOS get] toolbar] setItems:toolbarButtons]; + [self setToolbarItems:toolbarButtons]; return self; } +- (void)viewWillAppear:(BOOL)animated +{ + [self refresh]; +} + +- (void)viewDidDisappear:(BOOL)animated +{ + [self reset]; +} + - (void)refresh { static const char sectionNames[28] = { '/', '#', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', @@ -104,7 +101,7 @@ section = contents->elems[i].attr.b ? 0 : section; RADirectoryItem* item = RADirectoryItem.new; - item.path = [NSString stringWithUTF8String:contents->elems[i].data]; + item.path = @(contents->elems[i].data); item.isDirectory = contents->elems[i].attr.b; [sectionLists[section] addObject:item]; } @@ -127,37 +124,27 @@ - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { - RADirectoryItem* path = (RADirectoryItem*)[self itemForIndexPath:indexPath]; - - if(path.isDirectory) - [[RetroArch_iOS get] pushViewController:[RADirectoryList directoryListForPath:path.path] animated:YES]; - else - { - if (access(_path.UTF8String, R_OK | W_OK | X_OK)) - apple_display_alert(@"The directory containing the selected file has limited permissions. This may " - "prevent zipped games from loading, and will cause some cores to not function.", 0); - - [[RetroArch_iOS get] pushViewController:[[RAModuleList alloc] initWithGame:path.path] animated:YES]; - } + [_delegate directoryList:self itemWasSelected:[self itemForIndexPath:indexPath]]; } - (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { - RADirectoryItem* path = (RADirectoryItem*)[self itemForIndexPath:indexPath]; - static NSString *CellIdentifier = @"path"; + static NSString* const cell_types[2] = { @"file", @"folder" }; + static NSString* const icon_types[2] = { @"ic_file", @"ic_dir" }; + static const UITableViewCellAccessoryType accessory_types[2] = { UITableViewCellAccessoryDetailDisclosureButton, + UITableViewCellAccessoryDisclosureIndicator }; + RADirectoryItem* path = [self itemForIndexPath:indexPath]; + uint32_t type_id = path.isDirectory ? 1 : 0; - UITableViewCell* cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier]; - cell = (cell != nil) ? cell : [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]; - cell.textLabel.text = [path.path lastPathComponent]; - cell.accessoryType = (path.isDirectory) ? UITableViewCellAccessoryDisclosureIndicator : UITableViewCellAccessoryNone; - - if (path.isDirectory) { - cell.imageView.image = [UIImage imageNamed:@"ic_dir"]; - cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; - } else { - cell.imageView.image = [UIImage imageNamed:@"ic_file"]; - cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton; + UITableViewCell* cell = [self.tableView dequeueReusableCellWithIdentifier:cell_types[type_id]]; + if (!cell) + { + cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cell_types[type_id]]; + cell.imageView.image = [UIImage imageNamed:icon_types[type_id]]; + cell.accessoryType = accessory_types[type_id]; } + + cell.textLabel.text = [path.path lastPathComponent]; return cell; } @@ -224,26 +211,25 @@ @implementation RAModuleList { - NSString* _game; + id _delegate; } -- (id)initWithGame:(NSString*)path +- (id)initWithGame:(NSString*)path delegate:(id)delegate { self = [super initWithStyle:UITableViewStyleGrouped]; - [self setTitle:[path lastPathComponent]]; - - _game = path; + [self setTitle:path ? [path lastPathComponent] : @"Cores"]; + _delegate = delegate; // Load the modules with their data - NSArray* moduleList = [RAModuleInfo getModules]; + NSArray* moduleList = apple_get_modules(); NSMutableArray* supported = [NSMutableArray arrayWithObject:@"Suggested Cores"]; NSMutableArray* other = [NSMutableArray arrayWithObject:@"Other Cores"]; for (RAModuleInfo* i in moduleList) { - if ([i supportsFileAtPath:_game]) [supported addObject:i]; - else [other addObject:i]; + if (path && [i supportsFileAtPath:path]) [supported addObject:i]; + else [other addObject:i]; } if (supported.count > 1) @@ -257,7 +243,7 @@ - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { - apple_run_core((RAModuleInfo*)[self itemForIndexPath:indexPath], _game.UTF8String); + [_delegate moduleList:self itemWasSelected:[self itemForIndexPath:indexPath]]; } - (void)infoButtonTapped:(id)sender @@ -363,21 +349,8 @@ NSString *directoryPath = [currentDirectoryPath stringByAppendingPathComponent:cell.textLabel.text]; NSString *newPath = [directoryPath stringByAppendingPathComponent:fileName]; - BOOL didMove = [[NSFileManager defaultManager] moveItemAtPath:selectedFilePath toPath:newPath error:nil]; - - if (didMove) { - NSArray *viewsControllers = [[self presentingViewController] childViewControllers]; - - // Searches for RADirectoryList instance and call the refresh method - for (int i = 0; i < viewsControllers.count; i++) { - if ([viewsControllers[i] isKindOfClass:[RADirectoryList class]]) { - [viewsControllers[i] refresh]; - break; - } - } - } else { + if (![[NSFileManager defaultManager] moveItemAtPath:selectedFilePath toPath:newPath error:nil]) apple_display_alert(@"It was not possible to move the file", 0); - } [self dismissViewController]; } diff --git a/apple/iOS/platform.h b/apple/iOS/platform.h index d7a55f4668..d8675259c6 100644 --- a/apple/iOS/platform.h +++ b/apple/iOS/platform.h @@ -17,22 +17,28 @@ #ifndef __RARCH_IOS_PLATFORM_H #define __RARCH_IOS_PLATFORM_H +#include "views.h" + @interface RAGameView : UIViewController + (RAGameView*)get; - (void)openPauseMenu; - (void)closePauseMenu; @end -@interface RetroArch_iOS : UINavigationController +@interface RetroArch_iOS : UINavigationController + (RetroArch_iOS*)get; - (void)loadingCore:(RAModuleInfo*)core withFile:(const char*)file; - (void)unloadingCore:(RAModuleInfo*)core; -- (NSString*)retroarchConfigPath; - (void)refreshSystemConfig; +@property (strong, nonatomic) NSString* configDirectory; // e.g. /var/mobile/Documents/.RetroArch +@property (strong, nonatomic) NSString* globalConfigFile; // e.g. /var/mobile/Documents/.RetroArch/retroarch.cfg +@property (strong, nonatomic) NSString* coreDirectory; // e.g. /Applications/RetroArch.app/modules + @property (strong, nonatomic) NSString* documentsDirectory; // e.g. /var/mobile/Documents @property (strong, nonatomic) NSString* systemDirectory; // e.g. /var/mobile/Documents/.RetroArch @property (strong, nonatomic) NSString* systemConfigPath; // e.g. /var/mobile/Documents/.RetroArch/frontend.cfg diff --git a/apple/iOS/platform.m b/apple/iOS/platform.m index cfe1d8b391..06e0c349b8 100644 --- a/apple/iOS/platform.m +++ b/apple/iOS/platform.m @@ -19,12 +19,12 @@ #import "RetroArch_Apple.h" #include "rarch_wrapper.h" -#include "../RetroArch/apple_input.h" +#include "apple/common/apple_input.h" #import "views.h" -#include "input/BTStack/btpad.h" -#include "input/BTStack/btdynamic.h" -#include "input/BTStack/btpad.h" +#include "bluetooth/btpad.h" +#include "bluetooth/btdynamic.h" +#include "bluetooth/btpad.h" #include "file.h" @@ -85,6 +85,7 @@ static void handle_touch_event(NSArray* touches) @implementation RetroArch_iOS { UIWindow* _window; + NSString* _path; bool _isGameTop, _isRomList; uint32_t _settingMenusInBackStack; @@ -111,28 +112,27 @@ static void handle_touch_event(NSArray* touches) self.documentsDirectory = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents"]; self.systemDirectory = [self.documentsDirectory stringByAppendingPathComponent:@".RetroArch"]; self.systemConfigPath = [self.systemDirectory stringByAppendingPathComponent:@"frontend.cfg"]; + + self.configDirectory = self.systemDirectory; + self.globalConfigFile = [NSString stringWithFormat:@"%@/retroarch.cfg", self.configDirectory]; + self.coreDirectory = [NSBundle.mainBundle.bundlePath stringByAppendingPathComponent:@"modules"]; if (!path_make_and_check_directory(self.documentsDirectory.UTF8String, 0755, R_OK | W_OK | X_OK)) apple_display_alert([NSString stringWithFormat:@"Failed to create or access base directory: %@", self.documentsDirectory], 0); else if (!path_make_and_check_directory(self.systemDirectory.UTF8String, 0755, R_OK | W_OK | X_OK)) apple_display_alert([NSString stringWithFormat:@"Failed to create or access system directory: %@", self.systemDirectory], 0); else - { - [self pushViewController:[RADirectoryList directoryListAtBrowseRoot] animated:YES]; - [self refreshSystemConfig]; - - if (apple_use_tv_mode) - apple_run_core(nil, 0); - } + [self beginBrowsingForFile]; + // Warn if there are no cores present - if ([RAModuleInfo getModules].count == 0) + if (apple_get_modules().count == 0) apple_display_alert(@"No libretro cores were found. You will not be able to play any games.", 0); } - (void)applicationDidBecomeActive:(UIApplication *)application { - apple_exit_stasis(); + apple_exit_stasis(false); } - (void)applicationWillResignActive:(UIApplication *)application @@ -140,6 +140,45 @@ static void handle_touch_event(NSArray* touches) apple_enter_stasis(); } +#pragma mark Frontend Browsing Logic +- (void)beginBrowsingForFile +{ + NSString* rootPath = RetroArch_iOS.get.documentsDirectory; + NSString* ragPath = [rootPath stringByAppendingPathComponent:@"RetroArchGames"]; + NSString* target = path_is_directory(ragPath.UTF8String) ? ragPath : rootPath; + + [self pushViewController:[[RADirectoryList alloc] initWithPath:target delegate:self] animated:YES]; + + [self refreshSystemConfig]; + if (apple_use_tv_mode) + apple_run_core(nil, 0); + +} + +- (bool)directoryList:(id)list itemWasSelected:(RADirectoryItem*)path +{ + if(path.isDirectory) + [[RetroArch_iOS get] pushViewController:[[RADirectoryList alloc] initWithPath:path.path delegate:self] animated:YES]; + else + { + _path = path.path; + + if (access([path.path stringByDeletingLastPathComponent].UTF8String, R_OK | W_OK | X_OK)) + apple_display_alert(@"The directory containing the selected file has limited permissions. This may " + "prevent zipped games from loading, and will cause some cores to not function.", 0); + + [[RetroArch_iOS get] pushViewController:[[RAModuleList alloc] initWithGame:path.path delegate:self] animated:YES]; + } + + return true; +} + +- (bool)moduleList:(id)list itemWasSelected:(RAModuleInfo*)module +{ + apple_run_core(module, _path.UTF8String); + return true; +} + // UINavigationControllerDelegate - (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated { @@ -217,16 +256,6 @@ static void handle_touch_event(NSArray* touches) btpad_set_inquiry_state(true); } -- (NSString*)retroarchConfigPath -{ - return self.systemDirectory; -} - -- (NSString*)corePath -{ - return [NSBundle.mainBundle.bundlePath stringByAppendingPathComponent:@"modules"]; -} - #pragma mark FRONTEND CONFIG - (void)refreshSystemConfig { @@ -259,7 +288,7 @@ static void handle_touch_event(NSArray* touches) ios_set_bluetooth_mode(objc_get_value_from_config(conf, @"ios_btmode", @"keyboard")); bool val; - apple_use_tv_mode = config_get_bool(conf, "ios_tv_mode", & val) && val; + apple_use_tv_mode = config_get_bool(conf, "ios_tv_mode", &val) && val; config_file_free(conf); } diff --git a/apple/iOS/settings.m b/apple/iOS/settings.m index 54c860eaeb..11d3b73509 100644 --- a/apple/iOS/settings.m +++ b/apple/iOS/settings.m @@ -13,13 +13,13 @@ * If not, see . */ -#import "../RetroArch/RetroArch_Apple.h" +#import "apple/common/RetroArch_Apple.h" #import "views.h" -#include "../RetroArch/apple_input.h" -#include "../RetroArch/keycode.h" -#include "input/BTStack/btdynamic.h" -#include "input/BTStack/btpad.h" +#include "apple/common/apple_input.h" +#include "apple/common/keycode.h" +#include "bluetooth/btdynamic.h" +#include "bluetooth/btpad.h" enum SettingTypes { @@ -275,7 +275,7 @@ static NSArray* build_input_port_group(config_file_t* config, uint32_t player) - (id)initWithModule:(RAModuleInfo*)module { _module = module; - _configPath = _module ? _module.configPath : RAModuleInfo.globalConfigPath; + _configPath = _module ? _module.configFile : apple_platform.globalConfigFile; config_file_t* config = config_file_new([_configPath UTF8String]); @@ -308,7 +308,7 @@ static NSArray* build_input_port_group(config_file_t* config, uint32_t player) [NSArray arrayWithObjects:@"Input", subpath_setting(config, @"input_overlay", @"Input Overlay", @"", overlay_path, @"cfg"), - range_setting(config, @"overlay_opacity", @"Overlay Opacity", @"1.0", 0.0, 1.0), + range_setting(config, @"input_overlay_opacity", @"Overlay Opacity", @"1.0", 0.0, 1.0), group_setting(@"System Keys", [NSArray arrayWithObjects: // TODO: Many of these strings will be cut off on an iPhone [NSArray arrayWithObjects:@"System Keys", @@ -408,7 +408,7 @@ static void bluetooth_option_changed(RASettingData* setting) [modules addObject:@"Cores"]; [modules addObject:custom_action(@"Global Core Config", nil, nil, 0)]; - NSArray* moduleList = [RAModuleInfo getModules]; + NSArray* moduleList = apple_get_modules(); for (RAModuleInfo* i in moduleList) { [modules addObject:custom_action(i.description, nil, i, reload_core_config_state)]; @@ -840,97 +840,18 @@ static void bluetooth_option_changed(RASettingData* setting) - (void)checkInput { - // Keyboard - static const struct - { - const char* const keyname; - const uint32_t hid_id; - } ios_key_name_map[] = { - { "left", KEY_Left }, { "right", KEY_Right }, - { "up", KEY_Up }, { "down", KEY_Down }, - { "enter", KEY_Enter }, { "kp_enter", KP_Enter }, - { "space", KEY_Space }, { "tab", KEY_Tab }, - { "shift", KEY_LeftShift }, { "rshift", KEY_RightShift }, - { "ctrl", KEY_LeftControl }, { "alt", KEY_LeftAlt }, - { "escape", KEY_Escape }, { "backspace", KEY_DeleteForward }, - { "backquote", KEY_Grave }, { "pause", KEY_Pause }, + int32_t value = 0; - { "f1", KEY_F1 }, { "f2", KEY_F2 }, - { "f3", KEY_F3 }, { "f4", KEY_F4 }, - { "f5", KEY_F5 }, { "f6", KEY_F6 }, - { "f7", KEY_F7 }, { "f8", KEY_F8 }, - { "f9", KEY_F9 }, { "f10", KEY_F10 }, - { "f11", KEY_F11 }, { "f12", KEY_F12 }, - - { "num0", KEY_0 }, { "num1", KEY_1 }, - { "num2", KEY_2 }, { "num3", KEY_3 }, - { "num4", KEY_4 }, { "num5", KEY_5 }, - { "num6", KEY_6 }, { "num7", KEY_7 }, - { "num8", KEY_8 }, { "num9", KEY_9 }, + if ((value = apple_input_find_any_key())) + _value->value = @(apple_keycode_hidusage_to_name(value)); + else if ((value = apple_input_find_any_button(0)) >= 0) + _value->button_bind = [NSString stringWithFormat:@"%d", value]; + else if ((value = apple_input_find_any_axis(0))) + _value->axis_bind = [NSString stringWithFormat:@"%s%d", (value > 0) ? "+" : "-", value - 1]; + else + return; - { "insert", KEY_Insert }, { "del", KEY_DeleteForward }, - { "home", KEY_Home }, { "end", KEY_End }, - { "pageup", KEY_PageUp }, { "pagedown", KEY_PageDown }, - - { "add", KP_Add }, { "subtract", KP_Subtract }, - { "multiply", KP_Multiply }, { "divide", KP_Divide }, - { "keypad0", KP_0 }, { "keypad1", KP_1 }, - { "keypad2", KP_2 }, { "keypad3", KP_3 }, - { "keypad4", KP_4 }, { "keypad5", KP_5 }, - { "keypad6", KP_6 }, { "keypad7", KP_7 }, - { "keypad8", KP_8 }, { "keypad9", KP_9 }, - - { "period", KEY_Period }, { "capslock", KEY_CapsLock }, - { "numlock", KP_NumLock }, { "print_screen", KEY_PrintScreen }, - { "scroll_lock", KEY_ScrollLock }, - - { "a", KEY_A }, { "b", KEY_B }, { "c", KEY_C }, { "d", KEY_D }, - { "e", KEY_E }, { "f", KEY_F }, { "g", KEY_G }, { "h", KEY_H }, - { "i", KEY_I }, { "j", KEY_J }, { "k", KEY_K }, { "l", KEY_L }, - { "m", KEY_M }, { "n", KEY_N }, { "o", KEY_O }, { "p", KEY_P }, - { "q", KEY_Q }, { "r", KEY_R }, { "s", KEY_S }, { "t", KEY_T }, - { "u", KEY_U }, { "v", KEY_V }, { "w", KEY_W }, { "x", KEY_X }, - { "y", KEY_Y }, { "z", KEY_Z }, - - { "nul", 0x00}, - }; - - for (int i = 0; ios_key_name_map[i].hid_id; i++) - { - if (g_current_input_data.keys[ios_key_name_map[i].hid_id]) - { - _value->value = [NSString stringWithUTF8String:ios_key_name_map[i].keyname]; - [self finish]; - return; - } - } - - // Pad Buttons - uint32_t buttons = g_current_input_data.pad_buttons[_value->player] | - ((_value->player == 0) ? apple_input_get_icade_buttons() : 0); - - for (int i = 0; buttons && i < sizeof(buttons) * 8; i++) - { - if (buttons & (1 << i)) - { - _value->button_bind = [NSString stringWithFormat:@"%d", i]; - [self finish]; - return; - } - } - - // Pad Axis - for (int i = 0; i < 4; i++) - { - int16_t value = g_current_input_data.pad_axis[_value->player][i]; - - if (abs(value) > 0x1000) - { - _value->axis_bind = [NSString stringWithFormat:@"%s%d", (value > 0x1000) ? "+" : "-", i]; - [self finish]; - break; - } - } + [self finish]; } @end diff --git a/apple/iOS/views.h b/apple/iOS/views.h index 5b058b2ede..b2b5b887f5 100644 --- a/apple/iOS/views.h +++ b/apple/iOS/views.h @@ -29,6 +29,7 @@ - (id)initWithStyle:(UITableViewStyle)style; - (id)itemForIndexPath:(NSIndexPath*)indexPath; +- (void)reset; @end // browser.m @@ -38,17 +39,22 @@ @end // browser.m +@protocol RADirectoryListDelegate +- (bool)directoryList:(id)list itemWasSelected:(RADirectoryItem*)path; +@end + @interface RADirectoryList : RATableViewController @property (nonatomic, weak) RADirectoryItem *selectedItem; - -+ (id)directoryListAtBrowseRoot; -+ (id)directoryListForPath:(NSString*)path; -- (id)initWithPath:(NSString*)path; +- (id)initWithPath:(NSString*)path delegate:(id)delegate; @end // browser.m +@protocol RAModuleListDelegate +- (bool)moduleList:(id)list itemWasSelected:(RAModuleInfo*)module; +@end + @interface RAModuleList : RATableViewController -- (id)initWithGame:(NSString*)path; +- (id)initWithGame:(NSString*)path delegate:(id)delegate; @end // browser.m diff --git a/audio/coreaudio.c b/audio/coreaudio.c index b396e6a1e3..4d71eb4b8a 100644 --- a/audio/coreaudio.c +++ b/audio/coreaudio.c @@ -22,6 +22,10 @@ #include "../boolean.h" #include +#ifdef OSX +#include +#endif + #include #include #include @@ -93,6 +97,48 @@ static OSStatus audio_write_cb(void *userdata, AudioUnitRenderActionFlags *actio return noErr; } +#ifdef OSX +static void choose_output_device(coreaudio_t *dev, const char* device) +{ + AudioObjectPropertyAddress propaddr = + { + kAudioHardwarePropertyDevices, + kAudioObjectPropertyScopeGlobal, + kAudioObjectPropertyElementMaster + }; + + UInt32 size = 0; + + if (AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &propaddr, 0, 0, &size) != noErr) + return; + + UInt32 deviceCount = size / sizeof(AudioDeviceID); + AudioDeviceID *devices = malloc(size); + + if (!devices || AudioObjectGetPropertyData(kAudioObjectSystemObject, &propaddr, 0, 0, &size, devices) != noErr) + goto done; + + propaddr.mScope = kAudioDevicePropertyScopeOutput; + propaddr.mSelector = kAudioDevicePropertyDeviceName; + size = 1024; + + for (unsigned i = 0; i < deviceCount; i ++) + { + char device_name[1024]; + device_name[0] = 0; + + if (AudioObjectGetPropertyData(devices[i], &propaddr, 0, 0, &size, device_name) == noErr && strcmp(device_name, device) == 0) + { + AudioUnitSetProperty(dev->dev, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &devices[i], sizeof(AudioDeviceID)); + goto done; + } + } + +done: + free(devices); +} +#endif + static void *coreaudio_init(const char *device, unsigned rate, unsigned latency) { (void)device; @@ -121,6 +167,11 @@ static void *coreaudio_init(const char *device, unsigned rate, unsigned latency) if (AudioComponentInstanceNew(comp, &dev->dev) != noErr) goto error; +#ifdef OSX + if (device) + choose_output_device(dev, device); +#endif + dev->dev_alive = true; // Set audio format diff --git a/audio/opensl.c b/audio/opensl.c index 068e42a7a2..beffc5456b 100644 --- a/audio/opensl.c +++ b/audio/opensl.c @@ -115,6 +115,8 @@ static void *sl_init(const char *device, unsigned rate, unsigned latency) if (!sl) goto error; + RARCH_LOG("[SLES]: Requested audio latency: %d ms.", latency); + GOTO_IF_FAIL(slCreateEngine(&sl->engine_object, 0, NULL, 0, NULL, NULL)); GOTO_IF_FAIL(SLObjectItf_Realize(sl->engine_object, SL_BOOLEAN_FALSE)); GOTO_IF_FAIL(SLObjectItf_GetInterface(sl->engine_object, SL_IID_ENGINE, &sl->engine)); @@ -122,7 +124,11 @@ static void *sl_init(const char *device, unsigned rate, unsigned latency) GOTO_IF_FAIL(SLEngineItf_CreateOutputMix(sl->engine, &sl->output_mix, 0, NULL, NULL)); GOTO_IF_FAIL(SLObjectItf_Realize(sl->output_mix, SL_BOOLEAN_FALSE)); - sl->buf_size = next_pow2(32 * latency); + if (g_settings.audio.block_frames) + sl->buf_size = g_settings.audio.block_frames * 4; + else + sl->buf_size = next_pow2(32 * latency); + sl->buf_count = (latency * 4 * out_rate + 500) / 1000; sl->buf_count = (sl->buf_count + sl->buf_size / 2) / sl->buf_size; @@ -137,7 +143,7 @@ static void *sl_init(const char *device, unsigned rate, unsigned latency) for (unsigned i = 0; i < sl->buf_count; i++) sl->buffer[i] = sl->buffer_chunk + i * sl->buf_size; - RARCH_LOG("[SLES] : Setting audio latency: Block size = %u, Blocks = %u, Total = %u ...\n", + RARCH_LOG("[SLES]: Setting audio latency: Block size = %u, Blocks = %u, Total = %u ...\n", sl->buf_size, sl->buf_count, sl->buf_size * sl->buf_count); fmt_pcm.formatType = SL_DATAFORMAT_PCM; diff --git a/audio/rwebaudio.c b/audio/rwebaudio.c new file mode 100644 index 0000000000..7139a88de4 --- /dev/null +++ b/audio/rwebaudio.c @@ -0,0 +1,90 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2013 - Michael Lelli + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include "../driver.h" +#include "../general.h" + +#include "../emscripten/RWebAudio.h" + +static void ra_free(void *data) +{ + RWebAudioFree(); +} + +static void *ra_init(const char *device, unsigned rate, unsigned latency) +{ + (void)device; + (void)rate; + void *data = RWebAudioInit(latency); + if (data) + g_settings.audio.out_rate = RWebAudioSampleRate(); + return data; +} + +static ssize_t ra_write(void *data, const void *buf, size_t size) +{ + (void)data; + return RWebAudioWrite(buf, size); +} + +static bool ra_stop(void *data) +{ + (void)data; + return RWebAudioStop(); +} + +static void ra_set_nonblock_state(void *data, bool state) +{ + (void)data; + RWebAudioSetNonblockState(state); +} + +static bool ra_start(void *data) +{ + (void)data; + return RWebAudioStart(); +} + +static bool ra_use_float(void *data) +{ + (void)data; + return true; +} + +static size_t ra_write_avail(void *data) +{ + (void)data; + return RWebAudioWriteAvail(); +} + +static size_t ra_buffer_size(void *data) +{ + (void)data; + return RWebAudioBufferSize(); +} + +const audio_driver_t audio_rwebaudio = { + ra_init, + ra_write, + ra_stop, + ra_start, + ra_set_nonblock_state, + ra_free, + ra_use_float, + "rwebaudio", + ra_write_avail, + ra_buffer_size, +}; + diff --git a/blackberry-qnx/qnx_input.c b/blackberry-qnx/qnx_input.c index d6f6985886..52957a6222 100644 --- a/blackberry-qnx/qnx_input.c +++ b/blackberry-qnx/qnx_input.c @@ -627,7 +627,7 @@ static int16_t qnx_input_state(void *data, const struct retro_keybind **retro_ke static bool qnx_input_key_pressed(void *data, int key) { - return ((g_extern.lifecycle_state | driver.overlay_state ) & (1ULL << key)); + return ((g_extern.lifecycle_state | driver.overlay_state.buttons ) & (1ULL << key)); } static void qnx_input_free_input(void *data) diff --git a/conf/config_file.c b/conf/config_file.c index 8fcbd92ceb..05327d6cc3 100644 --- a/conf/config_file.c +++ b/conf/config_file.c @@ -355,6 +355,54 @@ static config_file_t *config_file_new_internal(const char *path, unsigned depth) return conf; } +config_file_t *config_file_new_from_string(const char *from_string) +{ + struct config_file *conf = (struct config_file*)calloc(1, sizeof(*conf)); + if (!conf) + return NULL; + + if (!from_string) + return conf; + + conf->path = NULL; + conf->include_depth = 0; + + struct string_list *lines = string_split(from_string, "\n"); + if (!lines) + return conf; + + for (size_t i = 0; i < lines->size; i++) + { + struct config_entry_list *list = (struct config_entry_list*)calloc(1, sizeof(*list)); + + char* line = lines->elems[i].data; + + if (line) + { + if (parse_line(conf, list, line)) + { + if (conf->entries) + { + conf->tail->next = list; + conf->tail = list; + } + else + { + conf->entries = list; + conf->tail = list; + } + } + } + + if (list != conf->tail) + free(list); + } + + string_list_free(lines); + + return conf; +} + config_file_t *config_file_new(const char *path) { return config_file_new_internal(path, 0); diff --git a/conf/config_file.h b/conf/config_file.h index 8ae7554181..ad366e84ca 100644 --- a/conf/config_file.h +++ b/conf/config_file.h @@ -41,6 +41,8 @@ typedef struct config_file config_file_t; // Loads a config file. Returns NULL if file doesn't exist. // NULL path will create an empty config file. config_file_t *config_file_new(const char *path); +// Load a config file from a string. +config_file_t *config_file_new_from_string(const char *from_string); // Frees config file. void config_file_free(config_file_t *conf); diff --git a/config.def.h b/config.def.h index a677cc2778..da09d29909 100644 --- a/config.def.h +++ b/config.def.h @@ -63,6 +63,7 @@ enum AUDIO_PS3, AUDIO_XENON360, AUDIO_WII, + AUDIO_RWEBAUDIO, AUDIO_NULL, INPUT_ANDROID, @@ -77,6 +78,7 @@ enum INPUT_LINUXRAW, INPUT_APPLE, INPUT_QNX, + INPUT_RWEBINPUT, INPUT_NULL }; @@ -130,6 +132,8 @@ enum #define AUDIO_DEFAULT_DRIVER AUDIO_SL #elif defined(HAVE_DSOUND) #define AUDIO_DEFAULT_DRIVER AUDIO_DSOUND +#elif defined(EMSCRIPTEN) +#define AUDIO_DEFAULT_DRIVER AUDIO_RWEBAUDIO #elif defined(HAVE_SDL) #define AUDIO_DEFAULT_DRIVER AUDIO_SDL #elif defined(HAVE_XAUDIO) @@ -152,6 +156,8 @@ enum #define INPUT_DEFAULT_DRIVER INPUT_ANDROID #elif defined(_WIN32) #define INPUT_DEFAULT_DRIVER INPUT_DINPUT +#elif defined(EMSCRIPTEN) +#define INPUT_DEFAULT_DRIVER INPUT_RWEBINPUT #elif defined(HAVE_SDL) #define INPUT_DEFAULT_DRIVER INPUT_SDL #elif defined(__CELLOS_LV2__) @@ -215,6 +221,14 @@ static const bool hard_sync = false; // 2: Etc ... static const unsigned hard_sync_frames = 0; +// Inserts a black frame inbetween frames. +// Useful for 120 Hz monitors who want to play 60 Hz material with eliminated ghosting. video_refresh_rate should still be configured as if it is a 60 Hz monitor (divide refresh rate by 2). +static bool black_frame_insertion = false; + +// Uses a custom swap interval for VSync. +// Set this to effectively halve monitor refresh rate. +static unsigned swap_interval = 1; + // Threaded video. Will possibly increase performance significantly at cost of worse synchronization and latency. static const bool video_threaded = false; diff --git a/driver.c b/driver.c index b9795818af..20273357bd 100644 --- a/driver.c +++ b/driver.c @@ -84,6 +84,9 @@ static const audio_driver_t *audio_drivers[] = { #ifdef GEKKO &audio_gx, #endif +#ifdef EMSCRIPTEN + &audio_rwebaudio, +#endif #ifdef HAVE_NULLAUDIO &audio_null, #endif @@ -165,6 +168,9 @@ static const input_driver_t *input_drivers[] = { #ifdef __BLACKBERRY_QNX__ &input_qnx, #endif +#ifdef EMSCRIPTEN + &input_rwebinput, +#endif #ifdef HAVE_NULLINPUT &input_null, #endif @@ -1058,7 +1064,7 @@ void uninit_video_input(void) { input_overlay_free(driver.overlay); driver.overlay = NULL; - driver.overlay_state = 0; + memset(&driver.overlay_state, 0, sizeof(driver.overlay_state)); } #endif diff --git a/driver.h b/driver.h index bc4cdd7d1a..5a30c74d8c 100644 --- a/driver.h +++ b/driver.h @@ -19,7 +19,7 @@ #include #include "boolean.h" -#include "libretro.h" +#include "libretro_private.h" #include #include #include "msvc/msvc_compat.h" @@ -462,7 +462,7 @@ typedef struct driver #ifdef HAVE_OVERLAY input_overlay_t *overlay; - uint64_t overlay_state; + input_overlay_state_t overlay_state; #endif // Interface for "poking". @@ -511,6 +511,7 @@ extern const audio_driver_t audio_coreaudio; extern const audio_driver_t audio_xenon360; extern const audio_driver_t audio_ps3; extern const audio_driver_t audio_gx; +extern const audio_driver_t audio_rwebaudio; extern const audio_driver_t audio_null; extern const video_driver_t video_gl; extern const video_driver_t video_psp1; @@ -536,6 +537,7 @@ extern const input_driver_t input_xinput; extern const input_driver_t input_linuxraw; extern const input_driver_t input_apple; extern const input_driver_t input_qnx; +extern const input_driver_t input_rwebinput; extern const input_driver_t input_null; #include "driver_funcs.h" diff --git a/driver_funcs.h b/driver_funcs.h index 69319e2104..56237a9cd6 100644 --- a/driver_funcs.h +++ b/driver_funcs.h @@ -56,7 +56,7 @@ static inline bool input_key_pressed_func(int key) bool ret = driver.input->key_pressed(driver.input_data, key); #ifdef HAVE_OVERLAY - ret |= driver.overlay_state & (UINT64_C(1) << key); + ret |= driver.overlay_state.buttons & (UINT64_C(1) << key); #endif #ifdef HAVE_COMMAND diff --git a/dynamic.c b/dynamic.c index 401b1f4e46..2f789ee5d6 100644 --- a/dynamic.c +++ b/dynamic.c @@ -30,7 +30,7 @@ #endif #include "boolean.h" -#include "libretro.h" +#include "libretro_private.h" #include "dynamic_dummy.h" #ifdef NEED_DYNAMIC @@ -93,8 +93,6 @@ unsigned (*pretro_get_region)(void); void *(*pretro_get_memory_data)(unsigned); size_t (*pretro_get_memory_size)(unsigned); -static bool environment_cb(unsigned cmd, void *data); - #ifdef HAVE_DYNAMIC #if defined(__APPLE__) #define DYNAMIC_EXT "dylib" @@ -397,7 +395,7 @@ void init_libretro_sym(bool dummy) load_symbols(dummy); - pretro_set_environment(environment_cb); + pretro_set_environment(rarch_environment_cb); } void uninit_libretro_sym(void) @@ -471,7 +469,7 @@ void dylib_close(dylib_t lib) } #endif -static bool environment_cb(unsigned cmd, void *data) +bool rarch_environment_cb(unsigned cmd, void *data) { switch (cmd) { @@ -758,6 +756,41 @@ static bool environment_cb(unsigned cmd, void *data) break; } + case RETRO_ENVIRONMENT_SET_LIBRETRO_PATH: + RARCH_LOG("Environ (Private) SET_LIBRETRO_PATH.\n"); + + if (path_file_exists((const char*)data)) + strlcpy(g_settings.libretro, (const char*)data, sizeof(g_settings.libretro)); + else + return false; + break; + + case RETRO_ENVIRONMENT_EXEC: + case RETRO_ENVIRONMENT_EXEC_ESCAPE: + + if (data) + strlcpy(g_extern.fullpath, (const char*)data, sizeof(g_extern.fullpath)); + else + *g_extern.fullpath = '\0'; + +#if !defined( HAVE_DYNAMIC) && defined(RARCH_CONSOLE) + g_extern.lifecycle_mode_state &= ~(1ULL << MODE_GAME); + g_extern.lifecycle_mode_state |= (1ULL << MODE_EXITSPAWN); + g_extern.lifecycle_mode_state |= (1ULL << MODE_EXITSPAWN_START_GAME); +#elif defined(HAVE_DYNAMIC) + g_extern.lifecycle_mode_state |= (1ULL << MODE_LOAD_GAME); +#endif + + if (cmd == RETRO_ENVIRONMENT_EXEC_ESCAPE) + { + RARCH_LOG("Environ (Private) EXEC_ESCAPE.\n"); + g_extern.exec = true; + } + else + RARCH_LOG("Environ (Private) EXEC.\n"); + + break; + default: RARCH_LOG("Environ UNSUPPORTED (#%u).\n", cmd); return false; diff --git a/dynamic.h b/dynamic.h index 6efa2722fb..198f81e39e 100644 --- a/dynamic.h +++ b/dynamic.h @@ -101,6 +101,8 @@ extern unsigned (*pretro_get_region)(void); extern void *(*pretro_get_memory_data)(unsigned); extern size_t (*pretro_get_memory_size)(unsigned); +extern bool rarch_environment_cb(unsigned cmd, void *data); + #ifdef __cplusplus } #endif diff --git a/emscripten/RWebAudio.h b/emscripten/RWebAudio.h new file mode 100644 index 0000000000..6911753923 --- /dev/null +++ b/emscripten/RWebAudio.h @@ -0,0 +1,28 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2013 - Michael Lelli + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include +#include +#include + +unsigned RWebAudioSampleRate(void); +void *RWebAudioInit(unsigned latency); +ssize_t RWebAudioWrite(const void *buf, size_t size); +bool RWebAudioStop(void); +bool RWebAudioStart(void); +void RWebAudioSetNonblockState(bool state); +void RWebAudioFree(void); +size_t RWebAudioWriteAvail(void); +size_t RWebAudioBufferSize(void); diff --git a/emscripten/RWebInput.h b/emscripten/RWebInput.h new file mode 100644 index 0000000000..103cc7a0cd --- /dev/null +++ b/emscripten/RWebInput.h @@ -0,0 +1,29 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2013 - Michael Lelli + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include + +typedef struct rwebinput_state +{ + char keys[32]; + int mouse_x; + int mouse_y; + char mouse_l; + char mouse_r; +} rwebinput_state_t; + +int RWebInputInit(void); +rwebinput_state_t *RWebInputPoll(int context); +void RWebInputDestroy(int context); diff --git a/emscripten/library_rwebaudio.js b/emscripten/library_rwebaudio.js new file mode 100644 index 0000000000..4e7594ec45 --- /dev/null +++ b/emscripten/library_rwebaudio.js @@ -0,0 +1,166 @@ +//"use strict"; + +var LibraryRWebAudio = { + $RA__deps: ['$Browser', 'usleep'], + $RA: { + BUFFER_SIZE: 256, + + context: null, + buffers: [], + numBuffers: 0, + bufIndex: 0, + bufOffset: 0, + startTime: 0, + nonblock: false, + currentTimeWorkaround: false, + + setStartTime: function() { + if (RA.context.currentTime) { + RA.startTime = window['performance']['now']() - RA.context.currentTime * 1000; + var time1 = RA.context.currentTime; + _usleep(50); + if (time1 === RA.context.currentTime) { + RA.currentTimeWorkaround = true; + if (RA.startTime === 0) throw 'startTime is 0'; + } + Module["resumeMainLoop"](); + } else window['setTimeout'](RA.setStartTime, 0); + }, + + getCurrentPerfTime: function() { + if (!RA.currentTimeWorkaround) return RA.context.currentTime; + else if (RA.startTime) return (window['performance']['now']() - RA.startTime) / 1000; + else throw 'getCurrentPerfTime() called before start time set'; + }, + + process: function(queueBuffers) { + var currentTime = RA.getCurrentPerfTime(); + for (var i = 0; i < RA.bufIndex; i++) { + if (RA.buffers[i].endTime < currentTime) { + var buf = RA.buffers.splice(i, 1); + RA.buffers[RA.numBuffers - 1] = buf[0]; + i--; + RA.bufIndex--; + } + } + }, + + fillBuffer: function(buf, samples) { + var count = 0; + var leftBuffer = RA.buffers[RA.bufIndex].getChannelData(0); + var rightBuffer = RA.buffers[RA.bufIndex].getChannelData(1); + while (samples && RA.bufOffset !== RA.BUFFER_SIZE) { + leftBuffer[RA.bufOffset] = {{{ makeGetValue('buf', 'count * 8', 'float') }}}; + rightBuffer[RA.bufOffset] = {{{ makeGetValue('buf', 'count * 8 + 4', 'float') }}}; + RA.bufOffset++; + count++; + samples--; + } + + return count; + }, + + queueAudio: function() { + var index = RA.bufIndex; + + var startTime; + if (RA.bufIndex) startTime = RA.buffers[RA.bufIndex - 1].endTime; + else startTime = RA.context.currentTime; + RA.buffers[index].endTime = startTime + RA.buffers[index].duration; + + var bufferSource = RA.context.createBufferSource(); + bufferSource.buffer = RA.buffers[index]; + bufferSource.connect(RA.context.destination); + bufferSource.start(startTime); + + RA.bufIndex++; + RA.bufOffset = 0; + }, + + block: function() { + do { + RA.process(); + } while (RA.bufIndex === RA.numBuffers - 1); + } + }, + + RWebAudioInit: function(latency) { + var ac = window['AudioContext'] || window['webkitAudioContext']; + + if (!ac) return 0; + + RA.context = new ac(); + + RA.numBuffers = ((latency * RA.context.sampleRate) / (1000 * RA.BUFFER_SIZE))|0; + if (RA.numBuffers < 2) RA.numBuffers = 2; + + for (var i = 0; i < RA.numBuffers; i++) RA.buffers[i] = RA.context.createBuffer(2, RA.BUFFER_SIZE, RA.context.sampleRate); + + RA.nonblock = false; + RA.startTime = 0; + // chrome hack to get currentTime running + RA.context.createGain(); + window['setTimeout'](RA.setStartTime, 0); + Module["pauseMainLoop"](); + return 1; + }, + + RWebAudioSampleRate: function() { + return RA.context.sampleRate; + }, + + RWebAudioWrite: function (buf, size) { + RA.process(); + var samples = size / 8; + var count = 0; + + while (samples) { + var fill = RA.fillBuffer(buf, samples); + samples -= fill; + count += fill; + buf += fill * 8; + + if (RA.bufOffset === RA.BUFFER_SIZE) { + if (RA.bufIndex === RA.numBuffers - 1) { + if (RA.nonblock) break; + else RA.block(); + } + RA.queueAudio(); + } + } + + return count * 8; + }, + + RWebAudioStop: function() { + RA.bufIndex = 0; + RA.bufOffset = 0; + return true; + }, + + RWebAudioStart: function() { + return true; + }, + + RWebAudioSetNonblockState: function(state) { + RA.nonblock = state; + }, + + RWebAudioFree: function() { + RA.bufIndex = 0; + RA.bufOffset = 0; + return; + }, + + RWebAudioBufferSize: function() { + return RA.numBuffers * RA.BUFFER_SIZE + RA.BUFFER_SIZE; + }, + + RWebAudioWriteAvail: function() { + RA.process(); + return ((RA.numBuffers - RA.bufIndex) * RA.BUFFER_SIZE - RA.bufOffset) * 8; + } +}; + +autoAddDeps(LibraryRWebAudio, '$RA'); +mergeInto(LibraryManager.library, LibraryRWebAudio); diff --git a/emscripten/library_rwebinput.js b/emscripten/library_rwebinput.js new file mode 100644 index 0000000000..7f45a747d8 --- /dev/null +++ b/emscripten/library_rwebinput.js @@ -0,0 +1,108 @@ +//"use strict"; + +var LibraryRWebInput = { + $RI__deps: ['$Browser'], + $RI: { + temp: null, + contexts: [], + + eventHandler: function(event) { + var i; + switch (event.type) { + case 'mousemove': + var x = event['movementX'] || event['mozMovementX'] || event['webkitMovementX']; + var y = event['movementY'] || event['mozMovementY'] || event['webkitMovementY']; + for (i = 0; i < RI.contexts.length; i++) { + var oldX = {{{ makeGetValue('RI.contexts[i].state', '32', 'i32') }}}; + var oldY = {{{ makeGetValue('RI.contexts[i].state', '36', 'i32') }}}; + x += oldX; + y += oldY; + {{{ makeSetValue('RI.contexts[i].state', '32', 'x', 'i32') }}}; + {{{ makeSetValue('RI.contexts[i].state', '36', 'y', 'i32') }}}; + } + break; + case 'mouseup': + case 'mousedown': + var value; + var offset; + if (event.button === 0) offset = 40; + else if (event.button === 2) offset = 41; + else break; + if (event.type === 'mouseup') value = 0; + else value = 1; + for (i = 0; i < RI.contexts.length; i++) { + {{{ makeSetValue('RI.contexts[i].state', 'offset', 'value', 'i8') }}}; + } + break; + case 'keyup': + case 'keydown': + var key = event.keyCode; + var offset = key >> 3; + var bit = 1 << (key & 7); + if (offset >= 32) throw 'key code error! bad code: ' + key; + for (i = 0; i < RI.contexts.length; i++) { + var value = {{{ makeGetValue('RI.contexts[i].state', 'offset', 'i8') }}}; + if (event.type === 'keyup') value &= ~bit; + else value |= bit; + {{{ makeSetValue('RI.contexts[i].state', 'offset', 'value', 'i8') }}}; + } + event.preventDefault(); + break; + case 'blur': + case 'visibilitychange': + for (i = 0; i < RI.contexts.length; i++) { + _memset(RI.contexts[i].state, 0, 42); + } + break; + } + } + }, + + RWebInputInit: function(latency) { + if (RI.contexts.length === 0) { + document.addEventListener('keyup', RI.eventHandler, false); + document.addEventListener('keydown', RI.eventHandler, false); + document.addEventListener('mousemove', RI.eventHandler, false); + document.addEventListener('mouseup', RI.eventHandler, false); + document.addEventListener('mousedown', RI.eventHandler, false); + document.addEventListener('blur', RI.eventHandler, false); + document.addEventListener('onvisbilitychange', RI.eventHandler, false); + } + if (RI.temp === null) RI.temp = _malloc(42); + + var s = _malloc(42); + _memset(s, 0, 42); + RI.contexts.push({ + state: s + }); + return RI.contexts.length; + }, + + RWebInputPoll: function(context) { + context -= 1; + var state = RI.contexts[context].state; + _memcpy(RI.temp, state, 42); + // reset mouse movements + {{{ makeSetValue('RI.contexts[context].state', '32', '0', 'i32') }}}; + {{{ makeSetValue('RI.contexts[context].state', '36', '0', 'i32') }}}; + return RI.temp; + }, + + RWebInputDestroy: function (context) { + if (context === RI.contexts.length) { + RI.contexts.pop(); + if (RI.contexts.length === 0) { + document.removeEventListener('keyup', RI.eventHandler, false); + document.removeEventListener('keydown', RI.eventHandler, false); + document.removeEventListener('mousemove', RI.eventHandler, false); + document.removeEventListener('mouseup', RI.eventHandler, false); + document.removeEventListener('mousedown', RI.eventHandler, false); + document.removeEventListener('blur', RI.eventHandler, false); + document.removeEventListener('onvisbilitychange', RI.eventHandler, false); + } + } + } +}; + +autoAddDeps(LibraryRWebInput, '$RI'); +mergeInto(LibraryManager.library, LibraryRWebInput); diff --git a/file_ext.h b/file_ext.h index bdfddeea3a..54bfb79cf5 100644 --- a/file_ext.h +++ b/file_ext.h @@ -42,6 +42,8 @@ #define EXT_EXECUTABLES "dol|DOL" #define SALAMANDER_FILE "boot.dol" #define DEFAULT_EXE_EXT ".dol" +#elif defined(EMSCRIPTEN) +#define EXT_EXECUTABLES "" #endif #endif diff --git a/frontend/frontend.c b/frontend/frontend.c index 4a9093d4e1..4102b07fbb 100644 --- a/frontend/frontend.c +++ b/frontend/frontend.c @@ -63,9 +63,7 @@ static bool libretro_install_core(const char *path_prefix, return false; } - strlcpy(g_settings.libretro, new_path, - sizeof(g_settings.libretro)); - + rarch_environment_cb(RETRO_ENVIRONMENT_SET_LIBRETRO_PATH, (void*)new_path); return true; } diff --git a/frontend/frontend_context.c b/frontend/frontend_context.c index 24fa1dd161..3072732b4c 100644 --- a/frontend/frontend_context.c +++ b/frontend/frontend_context.c @@ -36,11 +36,12 @@ static const frontend_ctx_driver_t *frontend_ctx_drivers[] = { #if defined(IOS) || defined(OSX) &frontend_ctx_apple, #endif + NULL // zero length array is not valid }; const frontend_ctx_driver_t *frontend_ctx_find_driver(const char *ident) { - for (unsigned i = 0; i < sizeof(frontend_ctx_drivers) / sizeof(frontend_ctx_drivers[0]); i++) + for (unsigned i = 0; frontend_ctx_drivers[i]; i++) { if (strcmp(frontend_ctx_drivers[i]->ident, ident) == 0) return frontend_ctx_drivers[i]; @@ -51,7 +52,7 @@ const frontend_ctx_driver_t *frontend_ctx_find_driver(const char *ident) const frontend_ctx_driver_t *frontend_ctx_init_first(void) { - for (unsigned i = 0; i < sizeof(frontend_ctx_drivers) / sizeof(frontend_ctx_drivers[0]); i++) + for (unsigned i = 0; frontend_ctx_drivers[i]; i++) return frontend_ctx_drivers[i]; return NULL; diff --git a/frontend/frontend_emscripten.c b/frontend/frontend_emscripten.c new file mode 100644 index 0000000000..1941e49984 --- /dev/null +++ b/frontend/frontend_emscripten.c @@ -0,0 +1,147 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2013 - Hans-Kristian Arntzen + * Copyright (C) 2011-2013 - Daniel De Matteis + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include +#include "../general.h" +#include "../conf/config_file.h" +#include "../file.h" +#include "../emscripten/RWebAudio.h" + +#ifdef HAVE_RGUI +#include "../frontend/menu/rgui.h" +#endif + +#if defined(HAVE_RGUI) || defined(HAVE_RMENU) || defined(HAVE_RMENU_XUI) +#define HAVE_MENU +#else +#undef HAVE_MENU +#endif + +static bool menuloop; + +static void endloop(void) +{ + g_extern.system.shutdown = false; + menu_free(); + + if (g_extern.config_save_on_exit && *g_extern.config_path) + config_save_file(g_extern.config_path); + + if (g_extern.main_is_init) + rarch_main_deinit(); + + rarch_deinit_msg_queue(); + +#ifdef PERF_TEST + rarch_perf_log(); +#endif + + rarch_main_clear_state(); + + exit(0); +} + +static void mainloop(void) +{ + if (g_extern.system.shutdown) + { + endloop(); + } + else if (menuloop) + { + if (!menu_iterate()) + { + menuloop = false; + driver_set_nonblock_state(driver.nonblock_state); + + if (driver.audio_data && !audio_start_func()) + { + RARCH_ERR("Failed to resume audio driver. Will continue without audio.\n"); + g_extern.audio_active = false; + } + + g_extern.lifecycle_mode_state &= ~(1ULL << MODE_MENU); + } + } + else if (g_extern.lifecycle_mode_state & (1ULL << MODE_LOAD_GAME)) + { + load_menu_game_prepare(); + + // If ROM load fails, we exit RetroArch. On console it might make more sense to go back to menu though ... + if (load_menu_game()) + g_extern.lifecycle_mode_state |= (1ULL << MODE_GAME); + else + { +#ifdef RARCH_CONSOLE + g_extern.lifecycle_mode_state |= (1ULL << MODE_MENU); +#else + return; +#endif + } + + g_extern.lifecycle_mode_state &= ~(1ULL << MODE_LOAD_GAME); + } + else if (g_extern.lifecycle_mode_state & (1ULL << MODE_GAME)) + { + bool r; + if (g_extern.is_paused && !g_extern.is_oneshot) + r = rarch_main_idle_iterate(); + else + r = rarch_main_iterate(); + if (!r) + g_extern.lifecycle_mode_state &= ~(1ULL << MODE_GAME); + } + else if (g_extern.lifecycle_mode_state & (1ULL << MODE_MENU)) + { + g_extern.lifecycle_mode_state |= 1ULL << MODE_MENU_PREINIT; + // Menu should always run with vsync on. + video_set_nonblock_state_func(false); + + if (driver.audio_data) + audio_stop_func(); + + menuloop = true; + } + else + { + g_extern.system.shutdown = true; + } +} + +int main(int argc, char *argv[]) +{ + emscripten_set_canvas_size(800, 600); + + rarch_main_clear_state(); + rarch_init_msg_queue(); + + int init_ret; + if ((init_ret = rarch_main_init(argc, argv))) return init_ret; + +#ifdef HAVE_MENU + menu_init(); + g_extern.lifecycle_mode_state |= 1ULL << MODE_GAME; + + // If we started a ROM directly from command line, + // push it to ROM history. + if (!g_extern.libretro_dummy) + menu_rom_history_push_current(); +#endif + + emscripten_set_main_loop(mainloop, g_settings.video.vsync ? 0 : INT_MAX, 1); + + return 0; +} diff --git a/frontend/menu/menu_common.c b/frontend/menu/menu_common.c index 69c66818a8..582daeb3ff 100644 --- a/frontend/menu/menu_common.c +++ b/frontend/menu/menu_common.c @@ -444,27 +444,18 @@ void load_menu_game_history(unsigned game_index) rom_history_get_index(rgui->history, game_index, &path, &core_path, &core_name); - strlcpy(g_settings.libretro, core_path, sizeof(g_settings.libretro)); + rarch_environment_cb(RETRO_ENVIRONMENT_SET_LIBRETRO_PATH, (void*)core_path); if (path) - { rgui->load_no_rom = false; - strlcpy(g_extern.fullpath, path, sizeof(g_extern.fullpath)); - } else - { rgui->load_no_rom = true; - *g_extern.fullpath = '\0'; - } -#if !defined( HAVE_DYNAMIC) && defined(RARCH_CONSOLE) - g_extern.lifecycle_mode_state &= ~(1ULL << MODE_GAME); - g_extern.lifecycle_mode_state |= (1ULL << MODE_EXITSPAWN); - g_extern.lifecycle_mode_state |= (1ULL << MODE_EXITSPAWN_START_GAME); -#elif defined(HAVE_DYNAMIC) + rarch_environment_cb(RETRO_ENVIRONMENT_EXEC, (void*)path); + +#if defined(HAVE_DYNAMIC) libretro_free_system_info(&rgui->info); libretro_get_system_info(g_settings.libretro, &rgui->info, NULL); - g_extern.lifecycle_mode_state |= (1ULL << MODE_LOAD_GAME); #endif } @@ -713,7 +704,7 @@ static uint64_t rgui_input(void) #ifdef HAVE_OVERLAY for (unsigned i = 0; i < DEVICE_NAV_LAST; i++) - input_state |= driver.overlay_state & menu_nav_binds[0][i].joykey ? (1ULL << i) : 0; + input_state |= driver.overlay_state.buttons & menu_nav_binds[0][i].joykey ? (1ULL << i) : 0; #endif #else static const int maps[] = { @@ -734,7 +725,7 @@ static uint64_t rgui_input(void) input_state |= input_input_state_func(binds, 0, RETRO_DEVICE_JOYPAD, 0, maps[i + 0]) ? (1ULL << maps[i + 1]) : 0; #ifdef HAVE_OVERLAY - input_state |= (driver.overlay_state & (UINT64_C(1) << maps[i + 0])) ? (1ULL << maps[i + 1]) : 0; + input_state |= (driver.overlay_state.buttons & (UINT64_C(1) << maps[i + 0])) ? (1ULL << maps[i + 1]) : 0; #endif } diff --git a/frontend/menu/menu_common.h b/frontend/menu/menu_common.h index a3293785ed..4b465bd718 100644 --- a/frontend/menu/menu_common.h +++ b/frontend/menu/menu_common.h @@ -118,6 +118,11 @@ typedef enum RGUI_SETTINGS_VIDEO_VSYNC, RGUI_SETTINGS_VIDEO_HARD_SYNC, RGUI_SETTINGS_VIDEO_HARD_SYNC_FRAMES, + RGUI_SETTINGS_VIDEO_BLACK_FRAME_INSERTION, + RGUI_SETTINGS_VIDEO_SWAP_INTERVAL, + RGUI_SETTINGS_VIDEO_WINDOW_SCALE_X, + RGUI_SETTINGS_VIDEO_WINDOW_SCALE_Y, + RGUI_SETTINGS_VIDEO_CROP_OVERSCAN, RGUI_SETTINGS_VIDEO_REFRESH_RATE_AUTO, RGUI_SETTINGS_VIDEO_OPTIONS_LAST, #ifdef HAVE_SHADER_MANAGER diff --git a/frontend/menu/rgui.c b/frontend/menu/rgui.c index 3edeec1b08..754d24b63e 100644 --- a/frontend/menu/rgui.c +++ b/frontend/menu/rgui.c @@ -418,6 +418,10 @@ static void render_text(rgui_handle_t *rgui) rgui->selection_ptr - TERM_HEIGHT / 2 : 0; size_t end = rgui->selection_ptr + TERM_HEIGHT <= rgui->selection_buf->size ? rgui->selection_ptr + TERM_HEIGHT : rgui->selection_buf->size; + + // Do not scroll if all items are visible. + if (rgui->selection_buf->size <= TERM_HEIGHT) + begin = 0; if (end - begin > TERM_HEIGHT) end = begin + TERM_HEIGHT; @@ -598,6 +602,21 @@ static void render_text(rgui_handle_t *rgui) case RGUI_SETTINGS_VIDEO_HARD_SYNC: strlcpy(type_str, g_settings.video.hard_sync ? "ON" : "OFF", sizeof(type_str)); break; + case RGUI_SETTINGS_VIDEO_BLACK_FRAME_INSERTION: + strlcpy(type_str, g_settings.video.black_frame_insertion ? "ON" : "OFF", sizeof(type_str)); + break; + case RGUI_SETTINGS_VIDEO_SWAP_INTERVAL: + snprintf(type_str, sizeof(type_str), "%u", g_settings.video.swap_interval); + break; + case RGUI_SETTINGS_VIDEO_WINDOW_SCALE_X: + snprintf(type_str, sizeof(type_str), "%.1fx", g_settings.video.xscale); + break; + case RGUI_SETTINGS_VIDEO_WINDOW_SCALE_Y: + snprintf(type_str, sizeof(type_str), "%.1fx", g_settings.video.yscale); + break; + case RGUI_SETTINGS_VIDEO_CROP_OVERSCAN: + strlcpy(type_str, g_settings.video.crop_overscan ? "ON" : "OFF", sizeof(type_str)); + break; case RGUI_SETTINGS_VIDEO_HARD_SYNC_FRAMES: snprintf(type_str, sizeof(type_str), "%u", g_settings.video.hard_sync_frames); break; @@ -1500,13 +1519,20 @@ static void rgui_settings_video_options_populate_entries(rgui_handle_t *rgui) rgui_list_push(rgui->selection_buf, "Integer Scale", RGUI_SETTINGS_VIDEO_INTEGER_SCALE, 0); rgui_list_push(rgui->selection_buf, "Aspect Ratio", RGUI_SETTINGS_VIDEO_ASPECT_RATIO, 0); rgui_list_push(rgui->selection_buf, "Custom Ratio", RGUI_SETTINGS_CUSTOM_VIEWPORT, 0); -#ifndef RARCH_PERFORMANCE_MODE +#if !defined(RARCH_CONSOLE) && !defined(RARCH_MOBILE) rgui_list_push(rgui->selection_buf, "Toggle Fullscreen", RGUI_SETTINGS_TOGGLE_FULLSCREEN, 0); #endif rgui_list_push(rgui->selection_buf, "Rotation", RGUI_SETTINGS_VIDEO_ROTATION, 0); rgui_list_push(rgui->selection_buf, "VSync", RGUI_SETTINGS_VIDEO_VSYNC, 0); rgui_list_push(rgui->selection_buf, "Hard GPU Sync", RGUI_SETTINGS_VIDEO_HARD_SYNC, 0); rgui_list_push(rgui->selection_buf, "Hard GPU Sync Frames", RGUI_SETTINGS_VIDEO_HARD_SYNC_FRAMES, 0); + rgui_list_push(rgui->selection_buf, "Black Frame Insertion", RGUI_SETTINGS_VIDEO_BLACK_FRAME_INSERTION, 0); + rgui_list_push(rgui->selection_buf, "VSync Swap Interval", RGUI_SETTINGS_VIDEO_SWAP_INTERVAL, 0); +#if !defined(RARCH_CONSOLE) && !defined(RARCH_MOBILE) + rgui_list_push(rgui->selection_buf, "Windowed Scale (X)", RGUI_SETTINGS_VIDEO_WINDOW_SCALE_X, 0); + rgui_list_push(rgui->selection_buf, "Windowed Scale (Y)", RGUI_SETTINGS_VIDEO_WINDOW_SCALE_Y, 0); +#endif + rgui_list_push(rgui->selection_buf, "Crop Overscan (reload)", RGUI_SETTINGS_VIDEO_CROP_OVERSCAN, 0); rgui_list_push(rgui->selection_buf, "Estimated Monitor FPS", RGUI_SETTINGS_VIDEO_REFRESH_RATE_AUTO, 0); } @@ -1854,12 +1880,10 @@ static int video_option_toggle_setting(rgui_handle_t *rgui, unsigned setting, rg driver.video_poke->set_aspect_ratio(driver.video_data, g_settings.video.aspect_ratio_idx); break; -#ifndef RARCH_PERFORMANCE_MODE case RGUI_SETTINGS_TOGGLE_FULLSCREEN: if (action == RGUI_ACTION_OK) rarch_set_fullscreen(!g_settings.video.fullscreen); break; -#endif #ifdef GEKKO case RGUI_SETTINGS_VIDEO_RESOLUTION: @@ -1936,6 +1960,105 @@ static int video_option_toggle_setting(rgui_handle_t *rgui, unsigned setting, rg } break; + case RGUI_SETTINGS_VIDEO_BLACK_FRAME_INSERTION: + switch (action) + { + case RGUI_ACTION_START: + g_settings.video.black_frame_insertion = false; + break; + + case RGUI_ACTION_LEFT: + case RGUI_ACTION_RIGHT: + case RGUI_ACTION_OK: + g_settings.video.black_frame_insertion = !g_settings.video.black_frame_insertion; + break; + + default: + break; + } + break; + + case RGUI_SETTINGS_VIDEO_CROP_OVERSCAN: + switch (action) + { + case RGUI_ACTION_START: + g_settings.video.crop_overscan = true; + break; + + case RGUI_ACTION_LEFT: + case RGUI_ACTION_RIGHT: + case RGUI_ACTION_OK: + g_settings.video.crop_overscan = !g_settings.video.crop_overscan; + break; + + default: + break; + } + break; + + case RGUI_SETTINGS_VIDEO_WINDOW_SCALE_X: + case RGUI_SETTINGS_VIDEO_WINDOW_SCALE_Y: + { + float *scale = setting == RGUI_SETTINGS_VIDEO_WINDOW_SCALE_X ? &g_settings.video.xscale : &g_settings.video.yscale; + float old_scale = *scale; + + switch (action) + { + case RGUI_ACTION_START: + *scale = 3.0f; + break; + + case RGUI_ACTION_LEFT: + *scale -= 1.0f; + break; + + case RGUI_ACTION_RIGHT: + *scale += 1.0f; + break; + + default: + break; + } + + *scale = roundf(*scale); + *scale = max(*scale, 1.0f); + + if (old_scale != *scale && !g_settings.video.fullscreen) + rarch_set_fullscreen(g_settings.video.fullscreen); // Reinit video driver. + + break; + } + + case RGUI_SETTINGS_VIDEO_SWAP_INTERVAL: + { + unsigned old = g_settings.video.swap_interval; + switch (action) + { + case RGUI_ACTION_START: + g_settings.video.swap_interval = 1; + break; + + case RGUI_ACTION_LEFT: + g_settings.video.swap_interval--; + break; + + case RGUI_ACTION_RIGHT: + case RGUI_ACTION_OK: + g_settings.video.swap_interval++; + break; + + default: + break; + } + + g_settings.video.swap_interval = min(g_settings.video.swap_interval, 4); + g_settings.video.swap_interval = max(g_settings.video.swap_interval, 1); + if (old != g_settings.video.swap_interval && driver.video && driver.video_data) + video_set_nonblock_state_func(false); // This will update the current swap interval. Since we're in RGUI now, always apply VSync. + + break; + } + case RGUI_SETTINGS_VIDEO_HARD_SYNC_FRAMES: switch (action) { @@ -2654,7 +2777,8 @@ int rgui_iterate(rgui_handle_t *rgui) // Core selection on non-console just updates directory listing. // Will take affect on new ROM load. #elif defined(GEKKO) && defined(HW_RVL) - strlcpy(g_settings.libretro, path, sizeof(g_settings.libretro)); // Is this supposed to be here? + rarch_environment_cb(RETRO_ENVIRONMENT_SET_LIBRETRO_PATH, (void*)path); + fill_pathname_join(g_extern.fullpath, default_paths.core_dir, SALAMANDER_FILE, sizeof(g_extern.fullpath)); g_extern.lifecycle_mode_state &= ~(1ULL << MODE_GAME); diff --git a/frontend/menu/rmenu.c b/frontend/menu/rmenu.c index 691232bad3..19aad8e7f3 100644 --- a/frontend/menu/rmenu.c +++ b/frontend/menu/rmenu.c @@ -460,7 +460,6 @@ static int select_file(void *data, uint64_t input) char extensions[128]; char comment[128]; char path[PATH_MAX]; - bool ret = true; bool pop_menu_stack = false; font_params_t font_parms = {0}; @@ -495,7 +494,7 @@ static int select_file(void *data, uint64_t input) if (input & (1ULL << DEVICE_NAV_B)) { if (filebrowser_iterate(rgui->browser, FILEBROWSER_ACTION_PATH_ISDIR)) - ret = filebrowser_iterate(rgui->browser, FILEBROWSER_ACTION_OK); + filebrowser_iterate(rgui->browser, FILEBROWSER_ACTION_OK); else { strlcpy(path, rgui->browser->current_dir.path, sizeof(path)); @@ -560,7 +559,7 @@ static int select_file(void *data, uint64_t input) true, menu_texture->width, menu_texture->height, 1.0f); break; case LIBRETRO_CHOICE: - strlcpy(g_settings.libretro, path, sizeof(g_settings.libretro)); + rarch_environment_cb(RETRO_ENVIRONMENT_SET_LIBRETRO_PATH, (void*)path); g_extern.lifecycle_mode_state |= (1ULL << MODE_EXITSPAWN); return -1; } diff --git a/frontend/menu/rmenu_xui.cpp b/frontend/menu/rmenu_xui.cpp index 8b41252f44..815d7ca2fa 100644 --- a/frontend/menu/rmenu_xui.cpp +++ b/frontend/menu/rmenu_xui.cpp @@ -1222,8 +1222,11 @@ HRESULT CRetroArchCoreBrowser::OnNotifyPress( HXUIOBJ hObjPressed, BOOL& bHandle wcstombs(str_buffer, (const wchar_t *)XuiListGetText(m_menulist, index), sizeof(str_buffer)); if(path_file_exists(rgui->browser->list->elems[index].data)) { - snprintf(g_settings.libretro, sizeof(g_settings.libretro), "%s\\%s", - rgui->browser->current_dir.directory_path, str_buffer); + struct retro_variable var; + var.key = "core_path"; + snprintf(var.value, sizeof(var.value), "%s\\%s", rgui->browser->current_dir.directory_path, str_buffer); + rarch_environment_cb(RETRO_ENVIRONMENT_SET_LIBRETRO_PATH, &var); + g_extern.lifecycle_mode_state |= (1ULL << MODE_EXITSPAWN); process_input_ret = -1; } diff --git a/frontend/platform/platform_apple.c b/frontend/platform/platform_apple.c index 1f6234bd82..a35ec8a1f2 100644 --- a/frontend/platform/platform_apple.c +++ b/frontend/platform/platform_apple.c @@ -15,7 +15,7 @@ #include #include -#include "../../apple/RetroArch/rarch_wrapper.h" +#include "../../apple/common/rarch_wrapper.h" #include "../frontend_context.h" diff --git a/frontend/platform/platform_gx.c b/frontend/platform/platform_gx.c index 102514d0ab..2851e55b38 100644 --- a/frontend/platform/platform_gx.c +++ b/frontend/platform/platform_gx.c @@ -18,7 +18,7 @@ #include #include "../../driver.h" #include "../../general.h" -#include "../../libretro.h" +#include "../../libretro_private.h" #include "../../console/rarch_console.h" #include "../../file.h" @@ -367,7 +367,11 @@ static int system_process_args(int argc, char *argv[], void *args) // a big hack: sometimes salamander doesn't save the new core it loads on first boot, // so we make sure g_settings.libretro is set here if (!g_settings.libretro[0] && argc >= 1 && strrchr(argv[0], '/')) - strlcpy(g_settings.libretro, strrchr(argv[0], '/') + 1, sizeof(g_settings.libretro)); + { + char path[PATH_MAX]; + strlcpy(path, strrchr(argv[0], '/') + 1, sizeof(path)); + rarch_environment_cb(RETRO_ENVIRONMENT_SET_LIBRETRO_PATH, path); + } if (argc > 2 && argv[1] != NULL && argv[2] != NULL) { diff --git a/frontend/platform/platform_qnx.c b/frontend/platform/platform_qnx.c index 1f2e4f3080..d0864d7a0d 100644 --- a/frontend/platform/platform_qnx.c +++ b/frontend/platform/platform_qnx.c @@ -20,6 +20,8 @@ #include "../../boolean.h" #include #include +#include "../../dynamic.h" +#include "../../libretro_private.h" static void get_environment_settings(int argc, char *argv[], void *args) { @@ -28,7 +30,8 @@ static void get_environment_settings(int argc, char *argv[], void *args) /* FIXME - should this apply for both BB10 and PB? */ #if defined(__QNX__) && !defined(HAVE_BB10) - strlcpy(g_settings.libretro, "app/native/lib", sizeof(g_settings.libretro)); + rarch_environment_cb(RETRO_ENVIRONMENT_SET_LIBRETRO_PATH, (void*)"app/native/lib"); + strlcpy(g_extern.config_path, "app/native/retroarch.cfg", sizeof(g_extern.config_path)); strlcpy(g_settings.video.shader_dir, "app/native/shaders_glsl", sizeof(g_settings.video.shader_dir)); #endif diff --git a/general.h b/general.h index 702b8928f8..0d187b688b 100644 --- a/general.h +++ b/general.h @@ -159,6 +159,8 @@ struct settings unsigned fullscreen_y; bool vsync; bool hard_sync; + bool black_frame_insertion; + unsigned swap_interval; unsigned hard_sync_frames; bool smooth; bool force_aspect; @@ -201,6 +203,7 @@ struct settings char driver[32]; bool enable; unsigned out_rate; + unsigned block_frames; float in_rate; char device[PATH_MAX]; unsigned latency; @@ -471,6 +474,8 @@ struct global msg_queue_t *msg_queue; + bool exec; + // Rewind support. state_manager_t *state_manager; void *state_buf; diff --git a/gfx/context/apple_gl_ctx.c b/gfx/context/apple_gl_ctx.c index d8db42630f..fb7e6333aa 100644 --- a/gfx/context/apple_gl_ctx.c +++ b/gfx/context/apple_gl_ctx.c @@ -25,7 +25,7 @@ #include "../shader_glsl.h" #endif -#include "../../apple/RetroArch/rarch_wrapper.h" +#include "../../apple/common/rarch_wrapper.h" static bool gfx_ctx_bind_api(enum gfx_ctx_api api, unsigned major, unsigned minor) { @@ -38,22 +38,6 @@ static bool gfx_ctx_bind_api(enum gfx_ctx_api api, unsigned major, unsigned mino #endif } -static bool gfx_ctx_set_video_mode( - unsigned width, unsigned height, - bool fullscreen) -{ - (void)width; - (void)height; - (void)fullscreen; - return true; -} - -static void gfx_ctx_update_window_title(void) -{ - char buf[128]; - gfx_get_fps(buf, sizeof(buf), false); -} - static void gfx_ctx_check_window(bool *quit, bool *resize, unsigned *width, unsigned *height, unsigned frame_count) { @@ -77,16 +61,6 @@ static void gfx_ctx_set_resize(unsigned width, unsigned height) (void)height; } -static bool gfx_ctx_has_focus(void) -{ - return true; -} - -static void gfx_ctx_swap_buffers(void) -{ - apple_flip_game_view(); -} - static void gfx_ctx_input_driver(const input_driver_t **input, void **input_data) { *input = NULL; @@ -105,13 +79,13 @@ const gfx_ctx_driver_t gfx_ctx_apple = { apple_destroy_game_view, gfx_ctx_bind_api, apple_set_game_view_sync, - gfx_ctx_set_video_mode, + apple_set_video_mode, apple_get_game_view_size, NULL, - gfx_ctx_update_window_title, + apple_update_window_title, gfx_ctx_check_window, gfx_ctx_set_resize, - gfx_ctx_has_focus, + apple_game_view_has_focus, apple_flip_game_view, gfx_ctx_input_driver, gfx_ctx_get_proc_address, diff --git a/gfx/context/drm_egl_ctx.c b/gfx/context/drm_egl_ctx.c index 67bda39585..995a9301c2 100644 --- a/gfx/context/drm_egl_ctx.c +++ b/gfx/context/drm_egl_ctx.c @@ -93,6 +93,8 @@ static void sighandler(int sig) static void gfx_ctx_swap_interval(unsigned interval) { g_interval = interval; + if (interval > 1) + RARCH_WARN("[KMS/EGL]: Swap intervals > 1 currently not supported. Will use swap interval of 1.\n"); } static void gfx_ctx_check_window(bool *quit, diff --git a/gfx/context/emscriptenegl_ctx.c b/gfx/context/emscriptenegl_ctx.c new file mode 100644 index 0000000000..f9b89a42ca --- /dev/null +++ b/gfx/context/emscriptenegl_ctx.c @@ -0,0 +1,275 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2013 - Hans-Kristian Arntzen + * Copyright (C) 2012 - Michael Lelli + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +// VideoCore context, for Rasperry Pi. + +#include "../../driver.h" +#include "../gfx_context.h" +#include "../gl_common.h" +#include "../gfx_common.h" + +#ifdef HAVE_CONFIG_H +#include "../../config.h" +#endif + +#include +#include + +#include +#include +#include + +static EGLContext g_egl_ctx; +static EGLSurface g_egl_surf; +static EGLDisplay g_egl_dpy; +static EGLConfig g_config; + +static bool g_inited; + +static unsigned g_fb_width; +static unsigned g_fb_height; + +static void gfx_ctx_swap_interval(unsigned interval) +{ + // no way to control vsync in WebGL + (void)interval; +} + +static void gfx_ctx_check_window(bool *quit, + bool *resize, unsigned *width, unsigned *height, unsigned frame_count) +{ + (void)frame_count; + int iWidth, iHeight, isFullscreen; + + emscripten_get_canvas_size(&iWidth, &iHeight, &isFullscreen); + *width = (unsigned) iWidth; + *height = (unsigned) iHeight; + + if (*width != g_fb_width || *height != g_fb_height) + *resize = true; + else + *resize = false; + + g_fb_width = (unsigned) iWidth; + g_fb_height = (unsigned) iHeight; + *quit = false; +} + +static void gfx_ctx_swap_buffers(void) +{ + // no-op in emscripten, no way to force swap/wait for vsync in browsers + //eglSwapBuffers(g_egl_dpy, g_egl_surf); +} + +static void gfx_ctx_set_resize(unsigned width, unsigned height) +{ + (void)width; + (void)height; +} + +static void gfx_ctx_update_window_title(void) +{ + char buf[128]; + if (gfx_get_fps(buf, sizeof(buf), false)) + RARCH_LOG("%s\n", buf); +} + +static void gfx_ctx_get_video_size(unsigned *width, unsigned *height) +{ + *width = g_fb_width; + *height = g_fb_height; +} + +static void gfx_ctx_destroy(void); + +static bool gfx_ctx_init(void) +{ + EGLint width; + EGLint height; + + RARCH_LOG("[EMSCRIPTEN/EGL]: Initializing...\n"); + if (g_inited) + { + RARCH_LOG("[EMSCRIPTEN/EGL]: Attempted to re-initialize driver.\n"); + return true; + } + + EGLint num_config; + + static const EGLint attribute_list[] = + { + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_NONE + }; + + static const EGLint context_attributes[] = + { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + + // get an EGL display connection + g_egl_dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (!g_egl_dpy) + goto error; + + // initialize the EGL display connection + if (!eglInitialize(g_egl_dpy, NULL, NULL)) + goto error; + + // get an appropriate EGL frame buffer configuration + if (!eglChooseConfig(g_egl_dpy, attribute_list, &g_config, 1, &num_config)) + goto error; + + // create an EGL rendering context + g_egl_ctx = eglCreateContext(g_egl_dpy, g_config, EGL_NO_CONTEXT, context_attributes); + if (!g_egl_ctx) + goto error; + + // create an EGL window surface + g_egl_surf = eglCreateWindowSurface(g_egl_dpy, g_config, 0, NULL); + if (!g_egl_surf) + goto error; + + // connect the context to the surface + if (!eglMakeCurrent(g_egl_dpy, g_egl_surf, g_egl_surf, g_egl_ctx)) + goto error; + + eglQuerySurface(g_egl_dpy, g_egl_surf, EGL_WIDTH, &width); + eglQuerySurface(g_egl_dpy, g_egl_surf, EGL_HEIGHT, &height); + g_fb_width = width; + g_fb_height = height; + RARCH_LOG("[EMSCRIPTEN/EGL]: Dimensions: %ux%u\n", width, height); + + return true; + +error: + gfx_ctx_destroy(); + return false; +} + +static bool gfx_ctx_set_video_mode( + unsigned width, unsigned height, + bool fullscreen) +{ + if (g_inited) + return false; + + g_inited = true; + return true; +} + +static bool gfx_ctx_bind_api(enum gfx_ctx_api api, unsigned major, unsigned minor) +{ + (void)major; + (void)minor; + switch (api) + { + case GFX_CTX_OPENGL_ES_API: + return eglBindAPI(EGL_OPENGL_ES_API); + default: + return false; + } +} + +static void gfx_ctx_destroy(void) +{ + if (g_egl_dpy) + { + eglMakeCurrent(g_egl_dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + + if (g_egl_ctx) + { + eglDestroyContext(g_egl_dpy, g_egl_ctx); + } + + if (g_egl_surf) + { + eglDestroySurface(g_egl_dpy, g_egl_surf); + } + + eglTerminate(g_egl_dpy); + } + + g_egl_ctx = NULL; + g_egl_surf = NULL; + g_egl_dpy = NULL; + g_config = 0; + g_inited = false; +} + +static void gfx_ctx_input_driver(const input_driver_t **input, void **input_data) +{ + *input = NULL; + + void *rwebinput = input_rwebinput.init(); + + if (rwebinput) + { + *input = &input_rwebinput; + *input_data = rwebinput; + } +} + +static bool gfx_ctx_has_focus(void) +{ + return g_inited; +} + +static gfx_ctx_proc_t gfx_ctx_get_proc_address(const char *symbol) +{ + return eglGetProcAddress(symbol); +} + +static float gfx_ctx_translate_aspect(unsigned width, unsigned height) +{ + return (float)width / height; +} + +static bool gfx_ctx_init_egl_image_buffer(const video_info_t *video) +{ + return false; +} + +static bool gfx_ctx_write_egl_image(const void *frame, unsigned width, unsigned height, unsigned pitch, bool rgb32, unsigned index, void **image_handle) +{ + return false; +} + +const gfx_ctx_driver_t gfx_ctx_emscripten = { + gfx_ctx_init, + gfx_ctx_destroy, + gfx_ctx_bind_api, + gfx_ctx_swap_interval, + gfx_ctx_set_video_mode, + gfx_ctx_get_video_size, + gfx_ctx_translate_aspect, + gfx_ctx_update_window_title, + gfx_ctx_check_window, + gfx_ctx_set_resize, + gfx_ctx_has_focus, + gfx_ctx_swap_buffers, + gfx_ctx_input_driver, + gfx_ctx_get_proc_address, + gfx_ctx_init_egl_image_buffer, + gfx_ctx_write_egl_image, + NULL, + "emscripten", +}; diff --git a/gfx/context/wgl_ctx.c b/gfx/context/wgl_ctx.c index cb925263ea..e8667ff66e 100644 --- a/gfx/context/wgl_ctx.c +++ b/gfx/context/wgl_ctx.c @@ -469,6 +469,7 @@ static bool gfx_ctx_set_video_mode( if (!g_hwnd) goto error; +#ifdef HAVE_WIN32GUI if (!fullscreen) { SetMenu(g_hwnd, LoadMenu(GetModuleHandle(NULL), MAKEINTRESOURCE(IDR_MENU))); @@ -477,6 +478,7 @@ static bool gfx_ctx_set_video_mode( unsigned menu_height = rcTemp.top + rect.top; // rect.top is negative after AdjustWindowRect(). SetWindowPos(g_hwnd, NULL, 0, 0, width, height + menu_height, SWP_NOMOVE); } +#endif if (!fullscreen || windowed_full) { diff --git a/gfx/d3d9/d3d9.cpp b/gfx/d3d9/d3d9.cpp index c4de8cf9c1..a59aa5647c 100644 --- a/gfx/d3d9/d3d9.cpp +++ b/gfx/d3d9/d3d9.cpp @@ -145,7 +145,20 @@ void D3DVideo::make_d3dpp(const video_info_t &info, D3DPRESENT_PARAMETERS &d3dpp d3dpp.Windowed = g_settings.video.windowed_fullscreen || !info.fullscreen; - d3dpp.PresentationInterval = info.vsync ? D3DPRESENT_INTERVAL_ONE : D3DPRESENT_INTERVAL_IMMEDIATE; + if (info.vsync) + { + switch (g_settings.video.swap_interval) + { + default: + case 1: d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_ONE; break; + case 2: d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_TWO; break; + case 3: d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_THREE; break; + case 4: d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_FOUR; break; + } + } + else + d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; + d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; d3dpp.hDeviceWindow = hWnd; d3dpp.BackBufferCount = 2; @@ -614,6 +627,17 @@ bool D3DVideo::frame(const void *frame, dev->SetViewport(&screen_vp); dev->Clear(0, 0, D3DCLEAR_TARGET, 0, 1, 0); + // Insert black frame first, so we can screenshot, etc. + if (g_settings.video.black_frame_insertion) + { + if (dev->Present(nullptr, nullptr, nullptr, nullptr) != D3D_OK) + { + needs_restore = true; + return true; + } + dev->Clear(0, 0, D3DCLEAR_TARGET, 0, 1, 0); + } + if (!chain->render(frame, width, height, pitch, rotation)) { RARCH_ERR("[D3D9]: Failed to render scene.\n"); diff --git a/gfx/gfx_context.c b/gfx/gfx_context.c index 4d5c45c30e..67d771d665 100644 --- a/gfx/gfx_context.c +++ b/gfx/gfx_context.c @@ -13,6 +13,7 @@ * If not, see . */ +#include "../general.h" #include "gfx_context.h" #include "../general.h" #include @@ -52,6 +53,9 @@ static const gfx_ctx_driver_t *gfx_ctx_drivers[] = { #if defined(IOS) || defined(OSX) //< Don't use __APPLE__ as it breaks basic SDL builds &gfx_ctx_apple, #endif +#ifdef EMSCRIPTEN + &gfx_ctx_emscripten, +#endif }; const gfx_ctx_driver_t *gfx_ctx_find_driver(const char *ident) diff --git a/gfx/gfx_context.h b/gfx/gfx_context.h index c8614799a5..da5c682792 100644 --- a/gfx/gfx_context.h +++ b/gfx/gfx_context.h @@ -108,6 +108,7 @@ extern const gfx_ctx_driver_t gfx_ctx_wgl; extern const gfx_ctx_driver_t gfx_ctx_videocore; extern const gfx_ctx_driver_t gfx_ctx_bbqnx; extern const gfx_ctx_driver_t gfx_ctx_apple; +extern const gfx_ctx_driver_t gfx_ctx_emscripten; extern const gfx_ctx_driver_t gfx_ctx_null; const gfx_ctx_driver_t *gfx_ctx_find_driver(const char *ident); // Finds driver with ident. Does not initialize. diff --git a/gfx/gl.c b/gfx/gl.c index 7a62910a78..0a0e03887f 100644 --- a/gfx/gl.c +++ b/gfx/gl.c @@ -1402,18 +1402,7 @@ static bool gl_frame(void *data, const void *frame, unsigned width, unsigned hei glBindTexture(GL_TEXTURE_2D, gl->texture[gl->tex_index]); #ifdef HAVE_FBO - // Data is already on GPU :) Have to reset some state however incase core changed it. - if (gl->hw_render_fbo_init) - { - gl_update_input_size(gl, width, height, pitch, false); - - if (!gl->fbo_inited) - { - gl_bind_backbuffer(); - gl_set_viewport(gl, gl->win_width, gl->win_height, false, true); - } - } - else + if (!gl->hw_render_fbo_init) #endif { gl_update_input_size(gl, width, height, pitch, true); @@ -1430,6 +1419,13 @@ static bool gl_frame(void *data, const void *frame, unsigned width, unsigned hei #ifdef HAVE_FBO if (gl->hw_render_fbo_init) { + gl_update_input_size(gl, width, height, pitch, false); + if (!gl->fbo_inited) + { + gl_bind_backbuffer(); + gl_set_viewport(gl, gl->win_width, gl->win_height, false, true); + } + #ifndef HAVE_OPENGLES if (!gl->core_context) glEnable(GL_TEXTURE_2D); @@ -1454,6 +1450,11 @@ static bool gl_frame(void *data, const void *frame, unsigned width, unsigned hei memcpy(tex_info.coord, gl->tex_coords, sizeof(gl->tex_coords)); glClear(GL_COLOR_BUFFER_BIT); + if (g_settings.video.black_frame_insertion) + { + context_swap_buffers_func(); + glClear(GL_COLOR_BUFFER_BIT); + } if (gl->shader) gl->shader->set_params(width, height, @@ -1623,7 +1624,7 @@ static void gl_set_nonblock_state(void *data, bool state) gl_t *gl = (gl_t*)data; (void)gl; - context_swap_interval_func(state ? 0 : 1); + context_swap_interval_func(state ? 0 : g_settings.video.swap_interval); } static bool resolve_extensions(gl_t *gl) @@ -1945,7 +1946,7 @@ static void *gl_init(const video_info_t *video, const input_driver_t **input, vo context_get_video_size_func(&gl->full_x, &gl->full_y); RARCH_LOG("Detecting screen resolution %ux%u.\n", gl->full_x, gl->full_y); - context_swap_interval_func(video->vsync ? 1 : 0); + context_swap_interval_func(video->vsync ? g_settings.video.swap_interval : 0); unsigned win_width = video->width; unsigned win_height = video->height; diff --git a/gfx/vg.c b/gfx/vg.c index bbfc3a8264..1297cf4063 100644 --- a/gfx/vg.c +++ b/gfx/vg.c @@ -87,7 +87,7 @@ static void *vg_init(const video_info_t *video, const input_driver_t **input, vo if (!vg) return NULL; - vg->driver = gfx_ctx_init_first(GFX_CTX_OPENVG_API); + vg->driver = gfx_ctx_init_first(GFX_CTX_OPENVG_API, 0, 0); if (!vg->driver) { diff --git a/griffin/griffin.c b/griffin/griffin.c index cbb9cc1f0e..5ff04b6ea9 100644 --- a/griffin/griffin.c +++ b/griffin/griffin.c @@ -298,8 +298,8 @@ INPUT #include "../android/native/jni/input_autodetect.c" #include "../android/native/jni/input_android.c" #elif defined(IOS) || defined(OSX) -#include "../apple/RetroArch/apple_input.c" -#include "../apple/RetroArch/apple_joypad.c" +#include "../apple/common/apple_input.c" +#include "../apple/common/apple_joypad.c" #elif defined(__BLACKBERRY_QNX__) #include "../blackberry-qnx/qnx_input.c" #endif diff --git a/input/autoconf/builtin.h b/input/autoconf/builtin.h new file mode 100644 index 0000000000..be4d9f1d4e --- /dev/null +++ b/input/autoconf/builtin.h @@ -0,0 +1,24 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2013 - pinumbernumber + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#ifndef AUTOCONF_BUILTIN_H__ +#define AUTOCONF_BUILTIN_H__ + +#include "../input_common.h" +#define DECL_BTN(btn, bind) "input_" #btn "_btn = " #bind "\n" +#define DECL_AXIS(axis, bind) "input_" #axis "_axis = " #bind "\n" + +#endif + diff --git a/input/autoconf/builtin_win.c b/input/autoconf/builtin_win.c new file mode 100644 index 0000000000..b359dcc102 --- /dev/null +++ b/input/autoconf/builtin_win.c @@ -0,0 +1,92 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2013 - pinumbernumber + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include "builtin.h" + +#define XINPUT_DEFAULT_BINDS \ +DECL_BTN(a, 1) \ +DECL_BTN(b, 0) \ +DECL_BTN(x, 3) \ +DECL_BTN(y, 2) \ +DECL_BTN(start, 6) \ +DECL_BTN(select, 7) \ +DECL_BTN(up, h0up) \ +DECL_BTN(down, h0down) \ +DECL_BTN(left, h0left) \ +DECL_BTN(right, h0right) \ +DECL_BTN(l, 4) \ +DECL_BTN(r, 5) \ +DECL_BTN(l3, 8 )\ +DECL_BTN(r3, 9) \ +DECL_BTN(menu_toggle, 10) \ +DECL_AXIS(l2, +4) \ +DECL_AXIS(r2, +5) \ +DECL_AXIS(l_x_plus, +0) \ +DECL_AXIS(l_x_minus, -0) \ +DECL_AXIS(l_y_plus, -1) \ +DECL_AXIS(l_y_minus, +1) \ +DECL_AXIS(r_x_plus, +2) \ +DECL_AXIS(r_x_minus, -2) \ +DECL_AXIS(r_y_plus, -3) \ +DECL_AXIS(r_y_minus, +3) + +// Some hardcoded autoconfig information. Will be used for pads with no autoconfig cfg files. +const char* const input_builtin_autoconfs[] = +{ + "input_device = \"XInput Controller (Player 1)\" \n" + "input_driver = \"winxinput\" \n" + XINPUT_DEFAULT_BINDS, + + "input_device = \"XInput Controller (Player 2)\" \n" + "input_driver = \"winxinput\" \n" + XINPUT_DEFAULT_BINDS, + + "input_device = \"XInput Controller (Player 3)\" \n" + "input_driver = \"winxinput\" \n" + XINPUT_DEFAULT_BINDS, + + "input_device = \"XInput Controller (Player 4)\" \n" + "input_driver = \"winxinput\" \n" + XINPUT_DEFAULT_BINDS, + + "input_device = \"Dual Trigger 3-in-1\" \n" + "input_driver = \"dinput\" \n" + DECL_BTN(a, 2) + DECL_BTN(b, 1) + DECL_BTN(x, 3) + DECL_BTN(y, 0) + DECL_BTN(start, 9) + DECL_BTN(select, 8) + DECL_BTN(up, h0up) + DECL_BTN(down, h0down) + DECL_BTN(left, h0left) + DECL_BTN(right, h0right) + DECL_BTN(l, 4) + DECL_BTN(r, 5) + DECL_BTN(l2, 6) + DECL_BTN(r2, 7) + DECL_BTN(l3, 10) + DECL_BTN(r3, 11) + DECL_AXIS(l_x_plus, +0) + DECL_AXIS(l_x_minus, -0) + DECL_AXIS(l_y_plus, +1) + DECL_AXIS(l_y_minus, -1) + DECL_AXIS(r_x_plus, +2) + DECL_AXIS(r_x_minus, -2) + DECL_AXIS(r_y_plus, +5) + DECL_AXIS(r_y_minus, -5), + + NULL +}; diff --git a/input/dinput.c b/input/dinput.c index 024f323ba9..b9a366d71f 100644 --- a/input/dinput.c +++ b/input/dinput.c @@ -49,6 +49,7 @@ struct dinput_joypad { LPDIRECTINPUTDEVICE8 joypad; DIJOYSTATE2 joy_state; + char* joy_name; }; static unsigned g_joypad_cnt; @@ -68,12 +69,6 @@ static bool dinput_init_context(void) if (g_ctx) return true; - if (driver.display_type != RARCH_DISPLAY_WIN32) - { - RARCH_ERR("Cannot open DInput as no Win32 window is present.\n"); - return false; - } - CoInitialize(NULL); // Who said we shouldn't have same call signature in a COM API? <_< @@ -351,6 +346,11 @@ const input_driver_t input_dinput = { dinput_grab_mouse, }; +// Keep track of which pad indexes are 360 controllers +// not static, will be read in winxinput_joypad.c +// -1 = not xbox pad, otherwise 0..3 +int g_xbox_pad_indexes[MAX_PLAYERS]; + static void dinput_joypad_destroy(void) { for (unsigned i = 0; i < MAX_PLAYERS; i++) @@ -360,6 +360,10 @@ static void dinput_joypad_destroy(void) IDirectInputDevice8_Unacquire(g_pads[i].joypad); IDirectInputDevice8_Release(g_pads[i].joypad); } + + free(g_pads[i].joy_name); + g_pads[i].joy_name = NULL; + } g_joypad_cnt = 0; @@ -386,6 +390,54 @@ static BOOL CALLBACK enum_axes_cb(const DIDEVICEOBJECTINSTANCE *inst, void *p) return DIENUM_CONTINUE; } +// TODO: Use a better way of detecting dual XInput/DInput pads. This current method +// will not work correctly for third-party controllers or future MS pads (Xbox One?). +// An example of this is provided in the DX SDK, which advises "Enum each PNP device +// using WMI and check each device ID to see if it contains "IG_"". Unfortunately the +// example code is a horrible unsightly mess. +static const char* const XINPUT_PAD_NAMES[] = +{ + "XBOX 360 For Windows", + "Controller (Gamepad for Xbox 360)", + "Controller (XBOX 360 For Windows)", + "Controller (Xbox 360 Wireless Receiver for Windows)", + "Controller (Xbox wireless receiver for windows)", + "XBOX 360 For Windows (Controller)", + "Xbox 360 Wireless Receiver", + "Xbox 360 Wireless Controller", + "Xbox Receiver for Windows (Wireless Controller)", + "Xbox wireless receiver for windows (Controller)", + "Gamepad F310 (Controller)", + "Controller (Gamepad F310)", + "Wireless Gamepad F710 (Controller)", + "Controller (Batarang wired controller (XBOX))", + "Afterglow Gamepad for Xbox 360 (Controller)" + "Controller (Rumble Gamepad F510)", + "Controller (Wireless Gamepad F710)", + "Controller (Xbox 360 Wireless Receiver for Windows)", + "Controller (Xbox wireless receiver for windows)", + "Controller (XBOX360 GAMEPAD)", + "MadCatz GamePad", + NULL +}; + +static bool name_is_360_pad(const char* name) +{ + for (unsigned i = 0; ; ++i) + { + const char* t = XINPUT_PAD_NAMES[i]; + if (t == NULL) + return false; + else if (strcasecmp(name, t) == 0) + return true; + } +} + +// Forward declaration +static const char *dinput_joypad_name(unsigned pad); + +static int g_last_xbox_pad_index; + static BOOL CALLBACK enum_joypad_cb(const DIDEVICEINSTANCE *inst, void *p) { (void)p; @@ -399,7 +451,22 @@ static BOOL CALLBACK enum_joypad_cb(const DIDEVICEINSTANCE *inst, void *p) #else if (FAILED(IDirectInput8_CreateDevice(g_ctx, &inst->guidInstance, pad, NULL))) #endif - return DIENUM_CONTINUE; + return DIENUM_CONTINUE; + + g_pads[g_joypad_cnt].joy_name = strdup(inst->tszProductName); + +#ifdef HAVE_WINXINPUT + bool is_360_pad = name_is_360_pad(inst->tszProductName); + + if (is_360_pad) + { + if (g_last_xbox_pad_index < 4) + g_xbox_pad_indexes[g_joypad_cnt] = g_last_xbox_pad_index; + ++g_last_xbox_pad_index; + + goto enum_iteration_done; + } +#endif IDirectInputDevice8_SetDataFormat(*pad, &c_dfDIJoystick2); IDirectInputDevice8_SetCooperativeLevel(*pad, (HWND)driver.video_window, @@ -407,9 +474,17 @@ static BOOL CALLBACK enum_joypad_cb(const DIDEVICEINSTANCE *inst, void *p) IDirectInputDevice8_EnumObjects(*pad, enum_axes_cb, *pad, DIDFT_ABSAXIS); + +#ifdef HAVE_WINXINPUT + if (!is_360_pad) +#endif + { + strlcpy(g_settings.input.device_names[g_joypad_cnt], dinput_joypad_name(g_joypad_cnt), sizeof(g_settings.input.device_names[g_joypad_cnt])); + input_config_autoconfigure_joypad(g_joypad_cnt, dinput_joypad_name(g_joypad_cnt), dinput_joypad.ident); + } +enum_iteration_done: g_joypad_cnt++; - return DIENUM_CONTINUE; } @@ -417,6 +492,14 @@ static bool dinput_joypad_init(void) { if (!dinput_init_context()) return false; + + g_last_xbox_pad_index = 0; + + for (unsigned i = 0; i < MAX_PLAYERS; ++i) + { + g_xbox_pad_indexes[i] = -1; + g_pads[i].joy_name = NULL; + } RARCH_LOG("Enumerating DInput joypads ...\n"); IDirectInput8_EnumDevices(g_ctx, DI8DEVCLASS_GAMECTRL, @@ -524,7 +607,7 @@ static void dinput_joypad_poll(void) { struct dinput_joypad *pad = &g_pads[i]; - if (pad->joypad) + if ((pad->joypad) && (g_xbox_pad_indexes[i] == -1)) { memset(&pad->joy_state, 0, sizeof(pad->joy_state)); @@ -555,10 +638,13 @@ static bool dinput_joypad_query_pad(unsigned pad) return pad < MAX_PLAYERS && g_pads[pad].joypad; } + + static const char *dinput_joypad_name(unsigned pad) { - (void)pad; - // FIXME + if (pad < MAX_PLAYERS) + return g_pads[pad].joy_name; + return NULL; } diff --git a/input/input_common.c b/input/input_common.c index 12be6eaf76..0e7418bfb8 100644 --- a/input/input_common.c +++ b/input/input_common.c @@ -16,6 +16,7 @@ #include "input_common.h" #include #include +#include #include "../general.h" #include "../driver.h" @@ -41,6 +42,9 @@ static const rarch_joypad_driver_t *joypad_drivers[] = { #ifndef IS_RETROLAUNCH +#ifdef HAVE_WINXINPUT + &winxinput_joypad, +#endif #ifdef HAVE_DINPUT &dinput_joypad, #endif @@ -512,6 +516,96 @@ const struct rarch_key_map rarch_key_map_dinput[] = { }; #endif +#ifdef EMSCRIPTEN +const struct rarch_key_map rarch_key_map_rwebinput[] = { + { 37, RETROK_LEFT }, + { 39, RETROK_RIGHT }, + { 38, RETROK_UP }, + { 40, RETROK_DOWN }, + { 13, RETROK_RETURN }, + { 9, RETROK_TAB }, + { 45, RETROK_INSERT }, + { 46, RETROK_DELETE }, + { 16, RETROK_RSHIFT }, + { 16, RETROK_LSHIFT }, + { 17, RETROK_LCTRL }, + { 35, RETROK_END }, + { 36, RETROK_HOME }, + { 34, RETROK_PAGEDOWN }, + { 33, RETROK_PAGEUP }, + { 18, RETROK_LALT }, + { 32, RETROK_SPACE }, + { 27, RETROK_ESCAPE }, + { 8, RETROK_BACKSPACE }, + { 13, RETROK_KP_ENTER }, + { 107, RETROK_KP_PLUS }, + { 109, RETROK_KP_MINUS }, + { 106, RETROK_KP_MULTIPLY }, + { 111, RETROK_KP_DIVIDE }, + { 192, RETROK_BACKQUOTE }, + { 19, RETROK_PAUSE }, + { 96, RETROK_KP0 }, + { 97, RETROK_KP1 }, + { 98, RETROK_KP2 }, + { 99, RETROK_KP3 }, + { 100, RETROK_KP4 }, + { 101, RETROK_KP5 }, + { 102, RETROK_KP6 }, + { 103, RETROK_KP7 }, + { 104, RETROK_KP8 }, + { 105, RETROK_KP9 }, + { 48, RETROK_0 }, + { 49, RETROK_1 }, + { 50, RETROK_2 }, + { 51, RETROK_3 }, + { 52, RETROK_4 }, + { 53, RETROK_5 }, + { 54, RETROK_6 }, + { 55, RETROK_7 }, + { 56, RETROK_8 }, + { 57, RETROK_9 }, + { 112, RETROK_F1 }, + { 113, RETROK_F2 }, + { 114, RETROK_F3 }, + { 115, RETROK_F4 }, + { 116, RETROK_F5 }, + { 117, RETROK_F6 }, + { 118, RETROK_F7 }, + { 119, RETROK_F8 }, + { 120, RETROK_F9 }, + { 121, RETROK_F10 }, + { 122, RETROK_F11 }, + { 123, RETROK_F12 }, + { 65, RETROK_a }, + { 66, RETROK_b }, + { 67, RETROK_c }, + { 68, RETROK_d }, + { 69, RETROK_e }, + { 70, RETROK_f }, + { 71, RETROK_g }, + { 72, RETROK_h }, + { 73, RETROK_i }, + { 74, RETROK_j }, + { 75, RETROK_k }, + { 76, RETROK_l }, + { 77, RETROK_m }, + { 78, RETROK_n }, + { 79, RETROK_o }, + { 80, RETROK_p }, + { 81, RETROK_q }, + { 82, RETROK_r }, + { 83, RETROK_s }, + { 84, RETROK_t }, + { 85, RETROK_u }, + { 86, RETROK_v }, + { 87, RETROK_w }, + { 88, RETROK_x }, + { 89, RETROK_y }, + { 90, RETROK_z }, + { 0, RETROK_UNKNOWN }, +}; +#endif + static enum retro_key rarch_keysym_lut[RETROK_LAST]; void input_init_keyboard_lut(const struct rarch_key_map *map) @@ -807,6 +901,38 @@ static void input_autoconfigure_joypad_conf(config_file_t *conf, struct retro_ke } } +static bool input_try_autoconfigure_joypad_from_conf(config_file_t *conf, unsigned index, const char *name, const char *driver, bool block_osd_spam) +{ + if (!conf) + return false; + + char ident[1024]; + char input_driver[1024]; + + *ident = *input_driver = '\0'; + + config_get_array(conf, "input_device", ident, sizeof(ident)); + config_get_array(conf, "input_driver", input_driver, sizeof(input_driver)); + + if (!strcmp(ident, name) && !strcmp(driver, input_driver)) + { + g_settings.input.autoconfigured[index] = true; + input_autoconfigure_joypad_conf(conf, g_settings.input.autoconf_binds[index]); + + char msg[512]; + snprintf(msg, sizeof(msg), "Joypad port #%u (%s) configured.", + index, name); + + if (!block_osd_spam) + msg_queue_push(g_extern.msg_queue, msg, 0, 60); + RARCH_LOG("%s\n", msg); + + return true; + } + + return false; +} + void input_config_autoconfigure_joypad(unsigned index, const char *name, const char *driver) { if (!g_settings.input.autodetect_enable) @@ -826,47 +952,41 @@ void input_config_autoconfigure_joypad(unsigned index, const char *name, const c if (!name) return; - if (!*g_settings.input.autoconfig_dir) - return; + // false = load from both cfg files and internal + bool internal_only = !*g_settings.input.autoconfig_dir; - struct string_list *list = dir_list_new(g_settings.input.autoconfig_dir, "cfg", false); - if (!list) - return; - - char ident[1024]; - char input_driver[1024]; - for (size_t i = 0; i < list->size; i++) +#ifdef HAVE_BUILTIN_AUTOCONFIG + // First internal + for (size_t i = 0; input_builtin_autoconfs[i]; i++) { - *ident = *input_driver = '\0'; - - config_file_t *conf = config_file_new(list->elems[i].data); - if (!conf) - continue; - - config_get_array(conf, "input_device", ident, sizeof(ident)); - config_get_array(conf, "input_driver", input_driver, sizeof(input_driver)); - - if (!strcmp(ident, name) && !strcmp(driver, input_driver)) - { - g_settings.input.autoconfigured[index] = true; - input_autoconfigure_joypad_conf(conf, g_settings.input.autoconf_binds[index]); - - char msg[512]; - snprintf(msg, sizeof(msg), "Joypad port #%u (%s) configured.", - index, name); - - if (!block_osd_spam) - msg_queue_push(g_extern.msg_queue, msg, 0, 60); - RARCH_LOG("%s\n", msg); - - config_file_free(conf); + config_file_t *conf = config_file_new_from_string(input_builtin_autoconfs[i]); + bool success = input_try_autoconfigure_joypad_from_conf(conf, index, name, driver, block_osd_spam); + config_file_free(conf); + if (success) break; - } - else - config_file_free(conf); } - - string_list_free(list); +#endif + + // Now try files + if (!internal_only) + { + struct string_list *list = dir_list_new(g_settings.input.autoconfig_dir, "cfg", false); + if (!list) + return; + + for (size_t i = 0; i < list->size; i++) + { + config_file_t *conf = config_file_new(list->elems[i].data); + if (!conf) + continue; + bool success = input_try_autoconfigure_joypad_from_conf(conf, index, name, driver, block_osd_spam); + config_file_free(conf); + if (success) + break; + } + + string_list_free(list); + } } #else void input_config_autoconfigure_joypad(unsigned index, const char *name, const char *driver) diff --git a/input/input_common.h b/input/input_common.h index 2ce912e98a..96d641dbdc 100644 --- a/input/input_common.h +++ b/input/input_common.h @@ -93,6 +93,7 @@ const char *input_joypad_name(const rarch_joypad_driver_t *driver, unsigned joyp extern const rarch_joypad_driver_t dinput_joypad; extern const rarch_joypad_driver_t linuxraw_joypad; +extern const rarch_joypad_driver_t winxinput_joypad; // Named as such to avoid confusion with xb1/360 port code extern const rarch_joypad_driver_t sdl_joypad; @@ -105,6 +106,7 @@ struct rarch_key_map extern const struct rarch_key_map rarch_key_map_x11[]; extern const struct rarch_key_map rarch_key_map_sdl[]; extern const struct rarch_key_map rarch_key_map_dinput[]; +extern const struct rarch_key_map rarch_key_map_rwebinput[]; void input_init_keyboard_lut(const struct rarch_key_map *map); enum retro_key input_translate_keysym_to_rk(unsigned sym); @@ -134,6 +136,8 @@ struct input_key_map }; extern const struct input_key_map input_config_key_map[]; +extern const char* const input_builtin_autoconfs[]; + const char *input_config_get_prefix(unsigned player, bool meta); void input_config_parse_key(config_file_t *conf, const char *prefix, const char *btn, diff --git a/input/overlay.c b/input/overlay.c index 9696fa5767..3854ea11a0 100644 --- a/input/overlay.c +++ b/input/overlay.c @@ -31,6 +31,13 @@ enum overlay_hitbox OVERLAY_HITBOX_RECT }; +enum overlay_type +{ + OVERLAY_TYPE_BUTTONS = 0, + OVERLAY_TYPE_ANALOG_LEFT, + OVERLAY_TYPE_ANALOG_RIGHT +}; + struct overlay_desc { float x; @@ -39,7 +46,9 @@ struct overlay_desc enum overlay_hitbox hitbox; float range_x, range_y; + enum overlay_type type; uint64_t key_mask; + float analog_saturate_pct; unsigned next_index; char next_index_name[64]; @@ -234,14 +243,23 @@ static bool input_overlay_load_desc(config_file_t *conf, struct overlay_desc *de char *key = list->elems[0].data; char *save; desc->key_mask = 0; - for (const char *tmp = strtok_r(key, "|", &save); tmp; tmp = strtok_r(NULL, "|", &save)) - desc->key_mask |= UINT64_C(1) << input_str_to_bind(tmp); - if (desc->key_mask & (UINT64_C(1) << RARCH_OVERLAY_NEXT)) + if (strcmp(key, "analog_left") == 0) + desc->type = OVERLAY_TYPE_ANALOG_LEFT; + else if (strcmp(key, "analog_right") == 0) + desc->type = OVERLAY_TYPE_ANALOG_RIGHT; + else { - char overlay_target_key[64]; - snprintf(overlay_target_key, sizeof(overlay_target_key), "overlay%u_desc%u_next_target", ol_index, desc_index); - config_get_array(conf, overlay_target_key, desc->next_index_name, sizeof(desc->next_index_name)); + desc->type = OVERLAY_TYPE_BUTTONS; + for (const char *tmp = strtok_r(key, "|", &save); tmp; tmp = strtok_r(NULL, "|", &save)) + desc->key_mask |= UINT64_C(1) << input_str_to_bind(tmp); + + if (desc->key_mask & (UINT64_C(1) << RARCH_OVERLAY_NEXT)) + { + char overlay_target_key[64]; + snprintf(overlay_target_key, sizeof(overlay_target_key), "overlay%u_desc%u_next_target", ol_index, desc_index); + config_get_array(conf, overlay_target_key, desc->next_index_name, sizeof(desc->next_index_name)); + } } desc->x = strtod(x, NULL) / width; @@ -258,6 +276,21 @@ static bool input_overlay_load_desc(config_file_t *conf, struct overlay_desc *de goto end; } + if (desc->type != OVERLAY_TYPE_BUTTONS) + { + if (desc->hitbox != OVERLAY_HITBOX_RADIAL) + { + RARCH_ERR("[Overlay]: Analog hitbox type must be \"radial\".\n"); + ret = false; + goto end; + } + + char overlay_analog_saturate_key[64]; + snprintf(overlay_analog_saturate_key, sizeof(overlay_analog_saturate_key), "overlay%u_desc%u_saturate_pct", ol_index, desc_index); + if (!config_get_float(conf, overlay_analog_saturate_key, &desc->analog_saturate_pct)) + desc->analog_saturate_pct = 1.0f; + } + desc->range_x = strtod(list->elems[4].data, NULL) / width; desc->range_y = strtod(list->elems[5].data, NULL) / height; @@ -530,12 +563,14 @@ static bool inside_hitbox(const struct overlay_desc *desc, float x, float y) } } -uint64_t input_overlay_poll(input_overlay_t *ol, int16_t norm_x, int16_t norm_y) +void input_overlay_poll(input_overlay_t *ol, input_overlay_state_t *out, int16_t norm_x, int16_t norm_y) { + memset(out, 0, sizeof(*out)); + if (!ol->enable) { ol->blocked = false; - return 0; + return; } // norm_x and norm_y is in [-0x7fff, 0x7fff] range, like RETRO_DEVICE_POINTER. @@ -547,25 +582,40 @@ uint64_t input_overlay_poll(input_overlay_t *ol, int16_t norm_x, int16_t norm_y) x /= ol->active->mod_w; y /= ol->active->mod_h; - uint64_t state = 0; for (size_t i = 0; i < ol->active->size; i++) { - if (inside_hitbox(&ol->active->descs[i], x, y)) + if (!inside_hitbox(&ol->active->descs[i], x, y)) + continue; + + if (ol->active->descs[i].type == OVERLAY_TYPE_BUTTONS) { uint64_t mask = ol->active->descs[i].key_mask; - state |= mask; + out->buttons |= mask; if (mask & (UINT64_C(1) << RARCH_OVERLAY_NEXT)) ol->next_index = ol->active->descs[i].next_index; } + else + { + float x_val = (x - ol->active->descs[i].x) / ol->active->descs[i].range_x / ol->active->descs[i].analog_saturate_pct; + float y_val = (y - ol->active->descs[i].y) / ol->active->descs[i].range_y / ol->active->descs[i].analog_saturate_pct; + + if (fabs(x_val) > 1.0f) + x_val = (x_val > 0.0f) ? 1.0f : -1.0f; + + if (fabs(y_val) > 1.0f) + y_val = (y_val > 0.0f) ? 1.0f : -1.0f; + + unsigned int base = (ol->active->descs[i].type == OVERLAY_TYPE_ANALOG_RIGHT) ? 2 : 0; + out->analog[base + 0] = x_val * 32767.0f; + out->analog[base + 1] = y_val * 32767.0f; + } } - if (!state) + if (!out->buttons) ol->blocked = false; else if (ol->blocked) - state = 0; - - return state; + memset(out, 0, sizeof(*out)); } void input_overlay_poll_clear(input_overlay_t *ol) diff --git a/input/overlay.h b/input/overlay.h index c5f08491cb..399cb896cf 100644 --- a/input/overlay.h +++ b/input/overlay.h @@ -30,6 +30,12 @@ extern "C" { // This interface requires that the video driver has support for the overlay interface. typedef struct input_overlay input_overlay_t; +typedef struct input_overlay_state +{ + uint64_t buttons; // This is a bitmask of (1 << key_bind_id). + int16_t analog[4]; // Left X, Left Y, Right X, Right Y +} input_overlay_state_t; + input_overlay_t *input_overlay_new(const char *overlay); void input_overlay_free(input_overlay_t *ol); @@ -38,8 +44,7 @@ void input_overlay_enable(input_overlay_t *ol, bool enable); bool input_overlay_full_screen(input_overlay_t *ol); // norm_x and norm_y are the result of input_translate_coord_viewport(). -// Resulting state is a bitmask of (1 << key_bind_id). -uint64_t input_overlay_poll(input_overlay_t *ol, int16_t norm_x, int16_t norm_y); +void input_overlay_poll(input_overlay_t *ol, input_overlay_state_t *out, int16_t norm_x, int16_t norm_y); // Call when there is nothing to poll. Allows overlay to clear certain state. void input_overlay_poll_clear(input_overlay_t *ol); diff --git a/input/rwebinput_input.c b/input/rwebinput_input.c new file mode 100644 index 0000000000..1a65eec7d3 --- /dev/null +++ b/input/rwebinput_input.c @@ -0,0 +1,149 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2013 - Michael Lelli + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include "input_common.h" + +#include "../driver.h" + +#include "../boolean.h" +#include "../general.h" + +#include "../emscripten/RWebInput.h" + +static bool uninited = false; + +typedef struct rwebinput_input +{ + rwebinput_state_t state; + int context; +} rwebinput_input_t; + +static void *rwebinput_input_init(void) +{ + rwebinput_input_t *rwebinput = (rwebinput_input_t*)calloc(1, sizeof(*rwebinput)); + if (!rwebinput) + return NULL; + + rwebinput->context = RWebInputInit(); + if (!rwebinput->context) + { + free(rwebinput); + return NULL; + } + + input_init_keyboard_lut(rarch_key_map_rwebinput); + + return rwebinput; +} + +static bool rwebinput_key_pressed(rwebinput_input_t *rwebinput, int key) +{ + if (key >= RETROK_LAST) + return false; + + unsigned sym = input_translate_rk_to_keysym((enum retro_key)key); + bool ret = rwebinput->state.keys[sym >> 3] & (1 << (sym & 7)); + return ret; +} + +static bool rwebinput_is_pressed(rwebinput_input_t *rwebinput, const struct retro_keybind *binds, unsigned id) +{ + if (id < RARCH_BIND_LIST_END) + { + const struct retro_keybind *bind = &binds[id]; + return bind->valid && rwebinput_key_pressed(rwebinput, binds[id].key); + } + else + return false; +} + +static bool rwebinput_bind_button_pressed(void *data, int key) +{ + rwebinput_input_t *rwebinput = (rwebinput_input_t*)data; + return rwebinput_is_pressed(rwebinput, g_settings.input.binds[0], key); +} + +static int16_t rwebinput_mouse_state(rwebinput_input_t *rwebinput, unsigned id) +{ + switch (id) + { + case RETRO_DEVICE_ID_MOUSE_X: + return (int16_t) rwebinput->state.mouse_x; + case RETRO_DEVICE_ID_MOUSE_Y: + return (int16_t) rwebinput->state.mouse_y; + case RETRO_DEVICE_ID_MOUSE_LEFT: + return rwebinput->state.mouse_l; + case RETRO_DEVICE_ID_MOUSE_RIGHT: + return rwebinput->state.mouse_r; + default: + return 0; + } +} + +static int16_t rwebinput_input_state(void *data, const struct retro_keybind **binds, unsigned port, unsigned device, unsigned index, unsigned id) +{ + rwebinput_input_t *rwebinput = (rwebinput_input_t*)data; + + switch (device) + { + case RETRO_DEVICE_JOYPAD: + return rwebinput_is_pressed(rwebinput, binds[port], id); + + case RETRO_DEVICE_KEYBOARD: + return rwebinput_key_pressed(rwebinput, id); + + case RETRO_DEVICE_MOUSE: + return rwebinput_mouse_state(rwebinput, id); + + default: + return 0; + } +} + +static void rwebinput_input_free(void *data) +{ + rwebinput_input_t *rwebinput = (rwebinput_input_t*)data; + uninited = true; + + RWebInputDestroy(rwebinput->context); + + free(data); +} + +static void rwebinput_input_poll(void *data) +{ + rwebinput_input_t *rwebinput = (rwebinput_input_t*)data; + + rwebinput_state_t *state = RWebInputPoll(rwebinput->context); + memcpy(&rwebinput->state, state, sizeof(rwebinput->state)); +} + +static void rwebinput_grab_mouse(void *data, bool state) +{ + (void)data; + (void)state; +} + +const input_driver_t input_rwebinput = { + rwebinput_input_init, + rwebinput_input_poll, + rwebinput_input_state, + rwebinput_bind_button_pressed, + rwebinput_input_free, + NULL, + "rwebinput", + rwebinput_grab_mouse, +}; + diff --git a/input/winxinput_joypad.c b/input/winxinput_joypad.c new file mode 100644 index 0000000000..535e8b62ea --- /dev/null +++ b/input/winxinput_joypad.c @@ -0,0 +1,373 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2013 - pinumbernumber + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +// Support 360 controllers on Windows. +// Said controllers do show under DInput but they have limitations in this mode; +// The triggers are combined rather than seperate and it is not possible to use +// the guide button. + +// Some wrappers for other controllers also simulate xinput (as it is easier to implement) +// so this may be useful for those also. +#include "input_common.h" + +#include "../general.h" +#include "../boolean.h" + +#include +#include +#include + +// Check the definitions do not already exist. +// Official and mingw xinput headers have different include guards +#if ((!_XINPUT_H_) && (!__WINE_XINPUT_H)) + +#define XINPUT_GAMEPAD_DPAD_UP 0x0001 +#define XINPUT_GAMEPAD_DPAD_DOWN 0x0002 +#define XINPUT_GAMEPAD_DPAD_LEFT 0x0004 +#define XINPUT_GAMEPAD_DPAD_RIGHT 0x0008 +#define XINPUT_GAMEPAD_START 0x0010 +#define XINPUT_GAMEPAD_BACK 0x0020 +#define XINPUT_GAMEPAD_LEFT_THUMB 0x0040 +#define XINPUT_GAMEPAD_RIGHT_THUMB 0x0080 +#define XINPUT_GAMEPAD_LEFT_SHOULDER 0x0100 +#define XINPUT_GAMEPAD_RIGHT_SHOULDER 0x0200 +#define XINPUT_GAMEPAD_A 0x1000 +#define XINPUT_GAMEPAD_B 0x2000 +#define XINPUT_GAMEPAD_X 0x4000 +#define XINPUT_GAMEPAD_Y 0x8000 + +typedef struct +{ + uint16_t wButtons; + uint8_t bLeftTrigger; + uint8_t bRightTrigger; + int16_t sThumbLX; + int16_t sThumbLY; + int16_t sThumbRX; + int16_t sThumbRY; +} XINPUT_GAMEPAD; + +typedef struct +{ + uint32_t dwPacketNumber; + XINPUT_GAMEPAD Gamepad; +} XINPUT_STATE; + +#endif + +// Guide constant is not officially documented +#define XINPUT_GAMEPAD_GUIDE 0x0400 + +#ifndef ERROR_DEVICE_NOT_CONNECTED +#define ERROR_DEVICE_NOT_CONNECTED 1167 +#endif + +#ifndef HAVE_DINPUT +#error Cannot compile xinput without dinput. +#endif + +// Due to 360 pads showing up under both XI and DI, and since we are going +// to have to pass through unhandled joypad numbers to DI, a slightly ugly +// hack is required here. dinput_joypad_init will fill this. +// For each pad index, the appropriate entry will be set to -1 if it is not +// a 360 pad, or the correct XInput player number (0..3 inclusive) if it is. +extern int g_xbox_pad_indexes[MAX_PLAYERS]; + +// For xinput1_3.dll +static HINSTANCE g_winxinput_dll; + +// Function pointer, to be assigned with GetProcAddress +typedef uint32_t (__stdcall *XInputGetStateEx_t)(uint32_t, XINPUT_STATE*); +static XInputGetStateEx_t g_XInputGetStateEx; + +// Guide button may or may not be available +static bool g_winxinput_guide_button_supported; + +typedef struct +{ + XINPUT_STATE xstate; + bool connected; +} winxinput_joypad_state; + +static winxinput_joypad_state g_winxinput_states[4]; + +static inline int pad_index_to_xplayer_index(unsigned pad) +{ + return g_xbox_pad_indexes[pad]; +} + +// Generic "XInput" instead of "Xbox 360", because there are +// some other non-xbox third party PC controllers. +static const char* const XBOX_CONTROLLER_NAMES[4] = +{ + "XInput Controller (Player 1)", + "XInput Controller (Player 2)", + "XInput Controller (Player 3)", + "XInput Controller (Player 4)" +}; + +const char* winxinput_joypad_name (unsigned pad) +{ + int xplayer = pad_index_to_xplayer_index(pad); + + if (xplayer < 0) + return dinput_joypad.name(pad); + else + // TODO: Different name if disconnected? + return XBOX_CONTROLLER_NAMES[xplayer]; +} + + + +static bool winxinput_joypad_init(void) +{ + g_winxinput_dll = NULL; + + // Find the correct path to load the DLL from. + // Usually this will be from the system directory, + // but occasionally a user may wish to use a third-party + // wrapper DLL (such as x360ce); support these by checking + // the working directory first. + + // No need to check for existance as we will be checking LoadLibrary's + // success anyway. + + // Note: Windows 8 ships with 1.4 but there doesn't + // seem to be any compelling reason to use it. + const char* DLL_NAME = "xinput1_3.dll"; + g_winxinput_dll = LoadLibrary(DLL_NAME); // Using dylib_* complicates building joyconfig. + if (!g_winxinput_dll) + { + // Loading from working dir failed, try to load from system. + char dll_path[MAX_PATH]; + GetSystemDirectory(dll_path, sizeof(dll_path)); + strlcat(dll_path, "\\", 1); + strlcat(dll_path, DLL_NAME, sizeof(DLL_NAME)); + g_winxinput_dll = LoadLibrary(dll_path); + + if (!g_winxinput_dll) + { + RARCH_ERR("Failed to load xinput1_3.dll, ensure DirectX and controller drivers are up to date.\n"); + return false; // DLL does not exist or is invalid + } + } + + // If we get here then an xinput DLL is correctly loaded. + // First try to load ordinal 100 (XInputGetStateEx). + g_XInputGetStateEx = (XInputGetStateEx_t) GetProcAddress(g_winxinput_dll, (const char*)100); + g_winxinput_guide_button_supported = true; + + if (!g_XInputGetStateEx) + { + // no ordinal 100. (Presumably a wrapper.) Load the ordinary + // XInputGetState, at the cost of losing guide button support. + g_winxinput_guide_button_supported = false; + g_XInputGetStateEx = (XInputGetStateEx_t) GetProcAddress(g_winxinput_dll, "XInputGetState"); + if (!g_XInputGetStateEx) + { + RARCH_ERR("Failed to init XInput: xinput1_3.dll is invalid or corrupt.\n"); + return false; // DLL was loaded but did not contain the correct function. + } + RARCH_WARN("XInput: No guide button support.\n"); + } + + // Zero out the states + for (unsigned i = 0; i < 4; ++i) + memset(&g_winxinput_states[i], 0, sizeof(winxinput_joypad_state)); + + // Do a dummy poll to check which controllers are connected. + XINPUT_STATE dummy_state; + for (unsigned i = 0; i < 4; ++i) + { + g_winxinput_states[i].connected = !(g_XInputGetStateEx(i, &dummy_state) == ERROR_DEVICE_NOT_CONNECTED); + if (g_winxinput_states[i].connected) + RARCH_LOG("Found XInput controller, player #%u\n", i); + } + + if ((!g_winxinput_states[0].connected) && + (!g_winxinput_states[1].connected) && + (!g_winxinput_states[2].connected) && + (!g_winxinput_states[3].connected)) + return false; + + // We're going to have to be buddies with dinput if we want to be able + // to use XI and non-XI controllers together. + if (!dinput_joypad.init()) + return false; + + for (unsigned autoconf_pad = 0; autoconf_pad < MAX_PLAYERS; autoconf_pad++) + { + if (pad_index_to_xplayer_index(autoconf_pad) > -1) + { + strlcpy(g_settings.input.device_names[autoconf_pad], winxinput_joypad_name(autoconf_pad), sizeof(g_settings.input.device_names[autoconf_pad])); + input_config_autoconfigure_joypad(autoconf_pad, winxinput_joypad_name(autoconf_pad), winxinput_joypad.ident); + } + } + + return true; + +} + +static bool winxinput_joypad_query_pad(unsigned pad) +{ + int xplayer = pad_index_to_xplayer_index(pad); + if (xplayer > -1) + return g_winxinput_states[xplayer].connected; + else + return dinput_joypad.query_pad(pad); +} + +static void winxinput_joypad_destroy(void) +{ + for (unsigned i = 0; i < 4; ++i) + memset(&g_winxinput_states[i], 0, sizeof(winxinput_joypad_state)); + + FreeLibrary(g_winxinput_dll); + g_winxinput_dll = NULL; + g_XInputGetStateEx = NULL; + + dinput_joypad.destroy(); +} + +// Buttons are provided by XInput as bits of a uint16. +// Map from rarch button index (0..10) to a mask to bitwise-& the buttons against. +// dpad is handled seperately. +static const uint16_t button_index_to_bitmap_code[] = { + XINPUT_GAMEPAD_A, + XINPUT_GAMEPAD_B, + XINPUT_GAMEPAD_X, + XINPUT_GAMEPAD_Y, + XINPUT_GAMEPAD_LEFT_SHOULDER, + XINPUT_GAMEPAD_RIGHT_SHOULDER, + XINPUT_GAMEPAD_START, + XINPUT_GAMEPAD_BACK, + XINPUT_GAMEPAD_LEFT_THUMB, + XINPUT_GAMEPAD_RIGHT_THUMB, + XINPUT_GAMEPAD_GUIDE +}; + +static bool winxinput_joypad_button (unsigned port_num, uint16_t joykey) +{ + if (joykey == NO_BTN) + return false; + + int xplayer = pad_index_to_xplayer_index(port_num); + if (xplayer == -1) + return dinput_joypad.button(port_num, joykey); + + if (!(g_winxinput_states[xplayer].connected)) + return false; + + //return false; + + uint16_t btn_word = g_winxinput_states[xplayer].xstate.Gamepad.wButtons; + + if (GET_HAT_DIR(joykey)) + { + switch (GET_HAT_DIR(joykey)) + { + case HAT_UP_MASK: return btn_word & XINPUT_GAMEPAD_DPAD_UP; + case HAT_DOWN_MASK: return btn_word & XINPUT_GAMEPAD_DPAD_DOWN; + case HAT_LEFT_MASK: return btn_word & XINPUT_GAMEPAD_DPAD_LEFT; + case HAT_RIGHT_MASK: return btn_word & XINPUT_GAMEPAD_DPAD_RIGHT; + } + return false; // hat requested and no hat button down + } + else + { + // non-hat button + unsigned num_buttons = g_winxinput_guide_button_supported ? 11 : 10; + + if (joykey < num_buttons) + return btn_word & button_index_to_bitmap_code[joykey]; + } + return false; +} + +static int16_t winxinput_joypad_axis (unsigned port_num, uint32_t joyaxis) +{ + if (joyaxis == AXIS_NONE) + return 0; + + int xplayer = pad_index_to_xplayer_index(port_num); + + if (xplayer == -1) + return dinput_joypad.axis(port_num, joyaxis); + + if (!(g_winxinput_states[xplayer].connected)) + return false; + + int16_t val = 0; + int axis = -1; + + bool is_neg = false; + bool is_pos = false; + + if (AXIS_NEG_GET(joyaxis) <= 3) // triggers (axes 4,5) cannot be negative + { + axis = AXIS_NEG_GET(joyaxis); + is_neg = true; + } + else if (AXIS_POS_GET(joyaxis) <= 5) + { + axis = AXIS_POS_GET(joyaxis); + is_pos = true; + } + + XINPUT_GAMEPAD* pad = &(g_winxinput_states[xplayer].xstate.Gamepad); + + switch (axis) + { + case 0: val = pad->sThumbLX; break; + case 1: val = pad->sThumbLY; break; + case 2: val = pad->sThumbRX; break; + case 3: val = pad->sThumbRY; break; + + case 4: val = pad->bLeftTrigger * 32767 / 255; break; // map 0..255 to 0..32767 + case 5: val = pad->bRightTrigger * 32767 / 255; break; + } + + if (is_neg && val > 0) + val = 0; + else if (is_pos && val < 0) + val = 0; + + // Clamp to avoid overflow error + if (val == -32768) + val = -32767; + + return val; +} + +static void winxinput_joypad_poll(void) +{ + for (unsigned i = 0; i < 4; ++i) + if (g_winxinput_states[i].connected) + if (g_XInputGetStateEx(i, &(g_winxinput_states[i].xstate)) == ERROR_DEVICE_NOT_CONNECTED) + g_winxinput_states[i].connected = false; + + dinput_joypad.poll(); +} + +const rarch_joypad_driver_t winxinput_joypad = { + winxinput_joypad_init, + winxinput_joypad_query_pad, + winxinput_joypad_destroy, + winxinput_joypad_button, + winxinput_joypad_axis, + winxinput_joypad_poll, + winxinput_joypad_name, + "winxinput", +}; diff --git a/libretro-test/Makefile b/libretro-test/Makefile index 15853aaeeb..1998f05f22 100644 --- a/libretro-test/Makefile +++ b/libretro-test/Makefile @@ -1,4 +1,8 @@ +ifneq ($(EMSCRIPTEN),) + platform = emscripten +endif + ifeq ($(platform),) platform = unix ifeq ($(shell uname -a),) @@ -32,6 +36,10 @@ else ifeq ($(platform), qnx) TARGET := $(TARGET_NAME)_libretro_qnx.so fpic := -fPIC SHARED := -shared -Wl,--version-script=link.T -Wl,--no-undefined +else ifeq ($(platform), emscripten) + TARGET := $(TARGET_NAME)_libretro_emscripten.so + fpic := -fPIC + SHARED := -shared -Wl,--version-script=link.T -Wl,--no-undefined else CC = gcc TARGET := $(TARGET_NAME)_retro.dll diff --git a/libretro.h b/libretro.h index 72050e93d5..7b767fe6eb 100755 --- a/libretro.h +++ b/libretro.h @@ -357,6 +357,8 @@ enum retro_mod // If set, this call is not part of the public libretro API yet. It can change or be removed at any time. #define RETRO_ENVIRONMENT_EXPERIMENTAL 0x10000 +// Environment callback to be used internally in frontend. +#define RETRO_ENVIRONMENT_PRIVATE 0x20000 // Environment commands. #define RETRO_ENVIRONMENT_SET_ROTATION 1 // const unsigned * -- @@ -445,7 +447,7 @@ enum retro_mod // If HW rendering is used, pass only RETRO_HW_FRAME_BUFFER_VALID or NULL to retro_video_refresh_t. #define RETRO_ENVIRONMENT_GET_VARIABLE 15 // struct retro_variable * -- - // Interface to aquire user-defined information from environment + // Interface to acquire user-defined information from environment // that cannot feasibly be supported in a multi-system way. // 'key' should be set to a key which has already been set by SET_VARIABLES. // 'data' will be set to a value or NULL. @@ -509,7 +511,7 @@ enum retro_mod // Lets the core know how much time has passed since last invocation of retro_run(). // The frontend can tamper with the timing to fake fast-forward, slow-motion, frame stepping, etc. // In this case the delta time will use the reference value in frame_time_callback.. - + // Notifies libretro that audio data should be written. typedef void (*retro_audio_callback_t)(void); diff --git a/libretro_private.h b/libretro_private.h new file mode 100644 index 0000000000..a393412911 --- /dev/null +++ b/libretro_private.h @@ -0,0 +1,45 @@ +/* Copyright (C) 2010-2013 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this libretro API header (libretro_private.h). + * --------------------------------------------------------------------------------------- + * + * Permission is hereby granted, free of charge, + * to any person obtaining a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef LIBRETRO_PRIVATE_H__ +#define LIBRETRO_PRIVATE_H__ + +// Private additions to libretro. No API/ABI stability guaranteed. + +#include "libretro.h" + +#define RETRO_ENVIRONMENT_SET_LIBRETRO_PATH (RETRO_ENVIRONMENT_PRIVATE | 0) + // const char * -- + // Sets the absolute path for the libretro core pointed to. RETRO_ENVIRONMENT_EXEC will use the last libretro core set with this call. + // Returns false if file for absolute path could not be found. +#define RETRO_ENVIRONMENT_EXEC (RETRO_ENVIRONMENT_PRIVATE | 1) + // const char * -- + // Requests that this core is deinitialized, and a new core is loaded. + // The libretro core used is set with SET_LIBRETRO_PATH, and path to game is passed in _EXEC. NULL means no game. +#define RETRO_ENVIRONMENT_EXEC_ESCAPE (RETRO_ENVIRONMENT_PRIVATE | 2) + // const char * -- + // Requests that this core is deinitialized, and a new core is loaded. It also escapes the main loop the core is currently + // bound to. + // The libretro core used is set with SET_LIBRETRO_PATH, and path to game is passed in _EXEC. NULL means no game. + +#endif + diff --git a/msvc/msvc-2010/RetroArch-msvc2010.sln b/msvc/msvc-2010/RetroArch-msvc2010.sln new file mode 100644 index 0000000000..6bb4b9c199 --- /dev/null +++ b/msvc/msvc-2010/RetroArch-msvc2010.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual C++ Express 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RetroArch-msvc2010", "RetroArch-msvc2010.vcxproj", "{27FF7CE1-4059-4AA1-8062-FD529560FA54}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {27FF7CE1-4059-4AA1-8062-FD529560FA54}.Debug|Win32.ActiveCfg = Debug|Win32 + {27FF7CE1-4059-4AA1-8062-FD529560FA54}.Debug|Win32.Build.0 = Debug|Win32 + {27FF7CE1-4059-4AA1-8062-FD529560FA54}.Release|Win32.ActiveCfg = Release|Win32 + {27FF7CE1-4059-4AA1-8062-FD529560FA54}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/msvc/msvc-2010/RetroArch-msvc2010.vcxproj b/msvc/msvc-2010/RetroArch-msvc2010.vcxproj index 17c46dec6d..4b037e80ea 100644 --- a/msvc/msvc-2010/RetroArch-msvc2010.vcxproj +++ b/msvc/msvc-2010/RetroArch-msvc2010.vcxproj @@ -88,7 +88,7 @@ Level3 Disabled - WIN32;HAVE_WIN32_D3D9;HAVE_CG;HAVE_GLSL;HAVE_FBO;HAVE_ZLIB;WANT_MINIZ;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_SCREENSHOTS;HAVE_BSV_MOVIE;HAVE_DINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETPLAY;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC + WIN32;HAVE_WIN32_D3D9;HAVE_CG;HAVE_GLSL;HAVE_FBO;HAVE_ZLIB;WANT_MINIZ;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_SCREENSHOTS;HAVE_BSV_MOVIE;HAVE_BUILTIN_AUTOCONFIG;HAVE_DINPUT;HAVE_WINXINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETPLAY;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\;$(CG_INC_PATH);%(AdditionalIncludeDirectories) MultiThreadedDebug CompileAsCpp @@ -108,7 +108,7 @@ Level3 Disabled - WIN32;HAVE_WIN32_D3D9;HAVE_CG;HAVE_GLSL;HAVE_FBO;HAVE_ZLIB;WANT_MINIZ;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_SCREENSHOTS;HAVE_BSV_MOVIE;HAVE_DINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETPLAY;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC + WIN32;HAVE_WIN32_D3D9;HAVE_CG;HAVE_GLSL;HAVE_FBO;HAVE_ZLIB;WANT_MINIZ;_DEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_SCREENSHOTS;HAVE_BSV_MOVIE;HAVE_BUILTIN_AUTOCONFIG;HAVE_DINPUT;HAVE_WINXINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETPLAY;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\;$(CG_INC_PATH);%(AdditionalIncludeDirectories) MultiThreadedDebug CompileAsCpp @@ -130,7 +130,7 @@ MaxSpeed true true - WIN32;HAVE_WIN32_D3D9;HAVE_CG;HAVE_GLSL;HAVE_FBO;HAVE_ZLIB;WANT_MINIZ;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_SCREENSHOTS;HAVE_BSV_MOVIE;HAVE_DINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETPLAY;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC + WIN32;HAVE_WIN32_D3D9;HAVE_CG;HAVE_GLSL;HAVE_FBO;HAVE_ZLIB;WANT_MINIZ;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_SCREENSHOTS;HAVE_BSV_MOVIE;HAVE_BUILTIN_AUTOCONFIG;HAVE_DINPUT;HAVE_WINXINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETPLAY;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;_CRT_SECURE_NO_WARNINGS;__SSE__;__i686__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\;$(CG_INC_PATH);%(AdditionalIncludeDirectories) MultiThreaded CompileAsCpp @@ -154,7 +154,7 @@ MaxSpeed true true - WIN32;HAVE_WIN32_D3D9;HAVE_CG;HAVE_GLSL;HAVE_FBO;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_SCREENSHOTS;HAVE_BSV_MOVIE;HAVE_DINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETPLAY;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;HAVE_ZLIB;WANT_MINIZ;_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC + WIN32;HAVE_WIN32_D3D9;HAVE_CG;HAVE_GLSL;HAVE_FBO;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);HAVE_SCREENSHOTS;HAVE_BSV_MOVIE;HAVE_BUILTIN_AUTOCONFIG;HAVE_DINPUT;HAVE_WINXINPUT;HAVE_XAUDIO;HAVE_DSOUND;HAVE_OPENGL;HAVE_DYLIB;HAVE_NETPLAY;HAVE_NETWORK_CMD;HAVE_COMMAND;HAVE_STDIN_CMD;HAVE_THREADS;HAVE_DYNAMIC;HAVE_ZLIB;WANT_MINIZ;_CRT_SECURE_NO_WARNINGS;__SSE__;__SSE2__;__x86_64__;HAVE_OVERLAY;HAVE_RGUI;HAVE_GL_SYNC $(MSBuildProjectDirectory);$(MSBuildProjectDirectory)\..\..\;$(CG_INC_PATH);%(AdditionalIncludeDirectories) MultiThreaded CompileAsCpp @@ -250,8 +250,12 @@ + + + + diff --git a/performance.c b/performance.c index 7829fe265d..faad97135d 100644 --- a/performance.c +++ b/performance.c @@ -58,6 +58,10 @@ #include #endif +#ifdef EMSCRIPTEN +#include +#endif + #ifdef PERF_TEST #define MAX_COUNTERS 64 static struct rarch_perf_counter *perf_counters[MAX_COUNTERS]; @@ -145,6 +149,8 @@ rarch_time_t rarch_get_time_usec(void) if (clock_gettime(CLOCK_MONOTONIC, &tv) < 0) return 0; return tv.tv_sec * INT64_C(1000000) + (tv.tv_nsec + 500) / 1000; +#elif defined(EMSCRIPTEN) + return emscripten_get_now() * 1000; #else #error "Your platform does not have a timer function implemented in rarch_get_time_usec(). Cannot continue." #endif diff --git a/retroarch-mingw-build.sh b/retroarch-mingw-build.sh index 3f9640a4f4..1f6135a03c 100755 --- a/retroarch-mingw-build.sh +++ b/retroarch-mingw-build.sh @@ -83,7 +83,7 @@ do_build() BUILDTYPE="$3" if [ ! -d "$RetroArch_DIR" ]; then - git clone git://github.com/Themaister/RetroArch.git "$RetroArch_DIR" + git clone git://github.com/libretro/RetroArch.git "$RetroArch_DIR" cd "$RetroArch_DIR" else cd "$RetroArch_DIR" diff --git a/retroarch.c b/retroarch.c index 8c1ceed1cd..2c119948ff 100644 --- a/retroarch.c +++ b/retroarch.c @@ -45,10 +45,6 @@ #include "msvc/msvc_compat.h" #endif -#if defined(RARCH_CONSOLE) && !defined(RARCH_PERFORMANCE_MODE) -#define RARCH_PERFORMANCE_MODE -#endif - // To avoid continous switching if we hold the button down, we require that the button must go from pressed, // unpressed back to pressed to be able to toggle between then. static void check_fast_forward_button(void) @@ -465,7 +461,7 @@ size_t audio_sample_batch(const int16_t *data, size_t frames) #ifdef HAVE_OVERLAY static inline void input_poll_overlay(void) { - driver.overlay_state = 0; + memset(&driver.overlay_state, 0, sizeof(driver.overlay_state)); unsigned device = input_overlay_full_screen(driver.overlay) ? RARCH_DEVICE_POINTER_SCREEN : RETRO_DEVICE_POINTER; @@ -480,7 +476,15 @@ static inline void input_poll_overlay(void) int16_t y = input_input_state_func(NULL, 0, device, i, RETRO_DEVICE_ID_POINTER_Y); - driver.overlay_state |= input_overlay_poll(driver.overlay, x, y); + input_overlay_state_t polled_data; + input_overlay_poll(driver.overlay, &polled_data, x, y); + + driver.overlay_state.buttons |= polled_data.buttons; + + for (unsigned j = 0; j < 4; j ++) + if (driver.overlay_state.analog[j] == 0) + driver.overlay_state.analog[j] = polled_data.analog[j]; + polled = true; } @@ -547,7 +551,13 @@ static int16_t input_state(unsigned port, unsigned device, unsigned index, unsig #ifdef HAVE_OVERLAY if (device == RETRO_DEVICE_JOYPAD && port == 0) - res |= driver.overlay_state & (UINT64_C(1) << id) ? 1 : 0; + res |= driver.overlay_state.buttons & (UINT64_C(1) << id) ? 1 : 0; + else if (device == RETRO_DEVICE_ANALOG && port == 0) + { + unsigned base = (index == RETRO_DEVICE_INDEX_ANALOG_RIGHT) ? 2 : 0; + base += (id == RETRO_DEVICE_ID_ANALOG_Y) ? 1 : 0; + res += driver.overlay_state.analog[base]; + } #endif // Don't allow turbo for D-pad. @@ -1935,7 +1945,6 @@ static void check_savestates(bool immutable) } } -#if !defined(RARCH_PERFORMANCE_MODE) void rarch_set_fullscreen(bool fullscreen) { g_settings.video.fullscreen = fullscreen; @@ -1966,7 +1975,6 @@ static bool check_fullscreen(void) was_pressed = pressed; return toggle; } -#endif void rarch_state_slot_increase(void) { @@ -2193,7 +2201,6 @@ static void check_movie(void) } #endif -#if !defined(RARCH_PERFORMANCE_MODE) static void check_pause(void) { static bool old_state = false; @@ -2266,7 +2273,6 @@ static void check_oneshot(void) g_extern.is_oneshot |= new_rewind_state && !old_rewind_state; old_rewind_state = new_rewind_state; } -#endif void rarch_game_reset(void) { @@ -2565,7 +2571,6 @@ static void check_dsp_config(void) } #endif -#if !defined(RARCH_PERFORMANCE_MODE) static void check_mute(void) { if (!g_extern.audio_active) @@ -2628,7 +2633,6 @@ static void check_volume(void) g_extern.audio_data.volume_gain = db_to_gain(g_extern.audio_data.volume_db); } -#endif #ifdef HAVE_NETPLAY static void check_netplay_flip(void) @@ -2700,10 +2704,8 @@ static void do_state_checks(void) #if defined(HAVE_SCREENSHOTS) && !defined(_XBOX) check_screenshot(); #endif -#if !defined(RARCH_PERFORMANCE_MODE) check_mute(); check_volume(); -#endif check_turbo(); @@ -2719,7 +2721,6 @@ static void do_state_checks(void) if (!g_extern.netplay) { #endif -#if !defined(RARCH_PERFORMANCE_MODE) check_pause(); check_oneshot(); @@ -2728,7 +2729,6 @@ static void do_state_checks(void) if (g_extern.is_paused && !g_extern.is_oneshot) return; -#endif check_fast_forward_button(); @@ -2759,9 +2759,7 @@ static void do_state_checks(void) else { check_netplay_flip(); -#if !defined(RARCH_PERFORMANCE_MODE) check_fullscreen(); -#endif } #endif } @@ -3059,6 +3057,12 @@ bool rarch_main_iterate(void) if (check_enter_rgui()) return false; // Enter menu, don't exit. + if (g_extern.exec) + { + g_extern.exec = false; + return false; + } + #ifdef HAVE_COMMAND if (driver.command) rarch_cmd_pre_frame(driver.command); diff --git a/retroarch.cfg b/retroarch.cfg index dafd3b0847..29494888c1 100644 --- a/retroarch.cfg +++ b/retroarch.cfg @@ -89,6 +89,11 @@ # Maximum is 3. # video_hard_sync_frames = 0 +# Inserts a black frame inbetween frames. +# Useful for 120 Hz monitors who want to play 60 Hz material with eliminated ghosting. +# video_refresh_rate should still be configured as if it is a 60 Hz monitor (divide refresh rate by 2). +# video_black_frame_insertion = false + # Use threaded video driver. Using this might improve performance at possible cost of latency and more video stuttering. # video_threaded = false @@ -115,7 +120,7 @@ # Forces cropping of overscanned frames. # Exact behavior of this option is implementation specific. -# video_crop_overscan = false +# video_crop_overscan = true # Path to shader. Shader can be either Cg, CGP (Cg preset) or XML/GLSL format if support is enabled. # video_shader = "/path/to/shader.{cg,cgp,shader}" diff --git a/settings.c b/settings.c index 4b9dd32237..4cad7763cd 100644 --- a/settings.c +++ b/settings.c @@ -67,6 +67,8 @@ const char *config_get_default_audio(void) return "ps3"; case AUDIO_WII: return "gx"; + case AUDIO_RWEBAUDIO: + return "rwebaudio"; case AUDIO_NULL: return "null"; default: @@ -137,6 +139,8 @@ const char *config_get_default_input(void) return "apple_input"; case INPUT_QNX: return "qnx_input"; + case INPUT_RWEBINPUT: + return "rwebinput"; case INPUT_NULL: return "null"; default: @@ -168,6 +172,8 @@ void config_set_defaults(void) g_settings.video.vsync = vsync; g_settings.video.hard_sync = hard_sync; g_settings.video.hard_sync_frames = hard_sync_frames; + g_settings.video.black_frame_insertion = black_frame_insertion; + g_settings.video.swap_interval = swap_interval; g_settings.video.threaded = video_threaded; g_settings.video.smooth = video_smooth; g_settings.video.force_aspect = force_aspect; @@ -196,6 +202,7 @@ void config_set_defaults(void) g_settings.audio.enable = audio_enable; g_settings.audio.out_rate = out_rate; + g_settings.audio.block_frames = 0; g_settings.audio.in_rate = out_rate; if (audio_device) strlcpy(g_settings.audio.device, audio_device, sizeof(g_settings.audio.device)); @@ -490,6 +497,10 @@ bool config_load_file(const char *path) if (g_settings.video.hard_sync_frames > 3) g_settings.video.hard_sync_frames = 3; + CONFIG_GET_BOOL(video.black_frame_insertion, "video_black_frame_insertion"); + CONFIG_GET_INT(video.swap_interval, "video_swap_interval"); + g_settings.video.swap_interval = max(g_settings.video.swap_interval, 1); + g_settings.video.swap_interval = min(g_settings.video.swap_interval, 4); CONFIG_GET_BOOL(video.threaded, "video_threaded"); CONFIG_GET_BOOL(video.smooth, "video_smooth"); CONFIG_GET_BOOL(video.force_aspect, "video_force_aspect"); @@ -628,6 +639,7 @@ bool config_load_file(const char *path) // Audio settings. CONFIG_GET_BOOL(audio.enable, "audio_enable"); CONFIG_GET_INT(audio.out_rate, "audio_out_rate"); + CONFIG_GET_INT(audio.block_frames, "audio_block_frames"); CONFIG_GET_STRING(audio.device, "audio_device"); CONFIG_GET_INT(audio.latency, "audio_latency"); CONFIG_GET_BOOL(audio.sync, "audio_sync"); @@ -968,12 +980,17 @@ bool config_save_file(const char *path) config_set_string(conf, "video_shader", g_settings.video.shader_path); config_set_bool(conf, "video_shader_enable", g_settings.video.shader_enable); config_set_float(conf, "video_aspect_ratio", g_settings.video.aspect_ratio); + config_set_float(conf, "video_xscale", g_settings.video.xscale); + config_set_float(conf, "video_yscale", g_settings.video.yscale); + config_set_bool(conf, "video_crop_overscan", g_settings.video.crop_overscan); config_set_bool(conf, "video_scale_integer", g_settings.video.scale_integer); config_set_bool(conf, "video_smooth", g_settings.video.smooth); config_set_float(conf, "video_refresh_rate", g_settings.video.refresh_rate); config_set_bool(conf, "video_vsync", g_settings.video.vsync); config_set_bool(conf, "video_hard_sync", g_settings.video.hard_sync); config_set_int(conf, "video_hard_sync_frames", g_settings.video.hard_sync_frames); + config_set_bool(conf, "video_black_frame_insertion", g_settings.video.black_frame_insertion); + config_set_int(conf, "video_swap_interval", g_settings.video.swap_interval); config_set_int(conf, "aspect_ratio_index", g_settings.video.aspect_ratio_idx); config_set_string(conf, "audio_device", g_settings.audio.device); config_set_bool(conf, "audio_rate_control", g_settings.audio.rate_control); @@ -1080,6 +1097,8 @@ bool config_save_file(const char *path) config_set_int(conf, cfg, g_settings.input.dpad_emulation[i]); snprintf(cfg, sizeof(cfg), "input_device_p%u", i + 1); config_set_int(conf, cfg, g_settings.input.device[i]); + snprintf(cfg, sizeof(cfg), "input_player%u_joypad_index", i + 1); + config_set_int(conf, cfg, g_settings.input.joypad_map[i]); } config_file_write(conf, path);