mirror of
https://github.com/libretro/RetroArch.git
synced 2024-11-22 23:49:50 +00:00
OpenDingux: Add rumble support
This commit is contained in:
parent
0a3306a7b4
commit
e582cf9f1d
@ -1323,6 +1323,24 @@ ifeq ($(HAVE_WINRAWINPUT), 1)
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_LIBSHAKE), 1)
|
||||
ifeq ($(OSX), 1)
|
||||
DEFINES += -DHAVE_LIBSHAKE
|
||||
INCLUDE_DIRS += -I$(DEPS_DIR)/libShake/include
|
||||
OBJ += $(DEPS_DIR)/libShake/src/common/error.o \
|
||||
$(DEPS_DIR)/libShake/src/common/helpers.o \
|
||||
$(DEPS_DIR)/libShake/src/common/presets.o \
|
||||
$(DEPS_DIR)/libShake/src/osx/shake.o
|
||||
else ifeq ($(HAVE_UNIX), 1)
|
||||
DEFINES += -DHAVE_LIBSHAKE
|
||||
INCLUDE_DIRS += -I$(DEPS_DIR)/libShake/include
|
||||
OBJ += $(DEPS_DIR)/libShake/src/common/error.o \
|
||||
$(DEPS_DIR)/libShake/src/common/helpers.o \
|
||||
$(DEPS_DIR)/libShake/src/common/presets.o \
|
||||
$(DEPS_DIR)/libShake/src/linux/shake.o
|
||||
endif
|
||||
endif
|
||||
|
||||
# Companion UI
|
||||
|
||||
ifneq ($(findstring Win32,$(OS)),)
|
||||
|
@ -69,6 +69,7 @@ HAVE_ZLIB = 1
|
||||
HAVE_CONFIGFILE = 1
|
||||
HAVE_PATCH = 1
|
||||
HAVE_CHEATS = 1
|
||||
HAVE_LIBSHAKE = 1
|
||||
|
||||
OS = Linux
|
||||
TARGET = retroarch
|
||||
@ -149,27 +150,27 @@ SYMBOL_MAP := -Wl,-Map=output.map
|
||||
|
||||
$(TARGET): $(RARCH_OBJ)
|
||||
@$(if $(Q), $(shell echo echo LD $@),)
|
||||
$(LINK) -o $@ $(RARCH_OBJ) $(LIBS) $(LDFLAGS) $(LIBRARY_DIRS)
|
||||
$(Q)$(LINK) -o $@ $(RARCH_OBJ) $(LIBS) $(LDFLAGS) $(LIBRARY_DIRS)
|
||||
|
||||
$(OBJDIR)/%.o: %.c
|
||||
@mkdir -p $(dir $@)
|
||||
@$(if $(Q), $(shell echo echo CC $<),)
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) $(DEFINES) -c -o $@ $<
|
||||
$(Q)$(CC) $(CPPFLAGS) $(CFLAGS) $(DEFINES) -c -o $@ $<
|
||||
|
||||
$(OBJDIR)/%.o: %.cpp
|
||||
@mkdir -p $(dir $@)
|
||||
@$(if $(Q), $(shell echo echo CXX $<),)
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(DEFINES) -MMD -c -o $@ $<
|
||||
$(Q)$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(DEFINES) -MMD -c -o $@ $<
|
||||
|
||||
$(OBJDIR)/%.o: %.m
|
||||
@mkdir -p $(dir $@)
|
||||
@$(if $(Q), $(shell echo echo OBJC $<),)
|
||||
$(CXX) $(OBJCFLAGS) $(DEFINES) -MMD -c -o $@ $<
|
||||
$(Q)$(CXX) $(OBJCFLAGS) $(DEFINES) -MMD -c -o $@ $<
|
||||
|
||||
$(OBJDIR)/%.o: %.S $(HEADERS)
|
||||
@mkdir -p $(dir $@)
|
||||
@$(if $(Q), $(shell echo echo AS $<),)
|
||||
$(CC) $(CFLAGS) $(ASFLAGS) $(DEFINES) -c -o $@ $<
|
||||
$(Q)$(CC) $(CFLAGS) $(ASFLAGS) $(DEFINES) -c -o $@ $<
|
||||
|
||||
clean:
|
||||
rm -rf $(OBJDIR_BASE)
|
||||
|
@ -73,6 +73,7 @@ HAVE_CONFIGFILE = 1
|
||||
HAVE_PATCH = 1
|
||||
HAVE_CHEATS = 1
|
||||
HAVE_CHEEVOS = 0
|
||||
HAVE_LIBSHAKE = 1
|
||||
|
||||
OS = Linux
|
||||
TARGET = retroarch
|
||||
@ -150,27 +151,27 @@ SYMBOL_MAP := -Wl,-Map=output.map
|
||||
|
||||
$(TARGET): $(RARCH_OBJ)
|
||||
@$(if $(Q), $(shell echo echo LD $@),)
|
||||
$(LINK) -o $@ $(RARCH_OBJ) $(LIBS) $(LDFLAGS) $(LIBRARY_DIRS)
|
||||
$(Q)$(LINK) -o $@ $(RARCH_OBJ) $(LIBS) $(LDFLAGS) $(LIBRARY_DIRS)
|
||||
|
||||
$(OBJDIR)/%.o: %.c
|
||||
@mkdir -p $(dir $@)
|
||||
@$(if $(Q), $(shell echo echo CC $<),)
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) $(DEFINES) -c -o $@ $<
|
||||
$(Q)$(CC) $(CPPFLAGS) $(CFLAGS) $(DEFINES) -c -o $@ $<
|
||||
|
||||
$(OBJDIR)/%.o: %.cpp
|
||||
@mkdir -p $(dir $@)
|
||||
@$(if $(Q), $(shell echo echo CXX $<),)
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(DEFINES) -MMD -c -o $@ $<
|
||||
$(Q)$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(DEFINES) -MMD -c -o $@ $<
|
||||
|
||||
$(OBJDIR)/%.o: %.m
|
||||
@mkdir -p $(dir $@)
|
||||
@$(if $(Q), $(shell echo echo OBJC $<),)
|
||||
$(CXX) $(OBJCFLAGS) $(DEFINES) -MMD -c -o $@ $<
|
||||
$(Q)$(CXX) $(OBJCFLAGS) $(DEFINES) -MMD -c -o $@ $<
|
||||
|
||||
$(OBJDIR)/%.o: %.S $(HEADERS)
|
||||
@mkdir -p $(dir $@)
|
||||
@$(if $(Q), $(shell echo echo AS $<),)
|
||||
$(CC) $(CFLAGS) $(ASFLAGS) $(DEFINES) -c -o $@ $<
|
||||
$(Q)$(CC) $(CFLAGS) $(ASFLAGS) $(DEFINES) -c -o $@ $<
|
||||
|
||||
clean:
|
||||
rm -rf $(OBJDIR_BASE)
|
||||
|
@ -1291,6 +1291,12 @@ static const bool sustained_performance_mode = false;
|
||||
static const bool vibrate_on_keypress = false;
|
||||
static const bool enable_device_vibration = false;
|
||||
|
||||
/* Defines the strength of rumble effects
|
||||
* on OpenDingux devices */
|
||||
#if defined(DINGUX) && defined(HAVE_LIBSHAKE)
|
||||
#define DEFAULT_DINGUX_RUMBLE_GAIN 50
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_VULKAN
|
||||
#define DEFAULT_VULKAN_GPU_INDEX 0
|
||||
#endif
|
||||
|
@ -1864,6 +1864,9 @@ static struct config_uint_setting *populate_settings_uint(
|
||||
SETTING_UINT("input_hotkey_block_delay", &settings->uints.input_hotkey_block_delay, true, DEFAULT_INPUT_HOTKEY_BLOCK_DELAY, false);
|
||||
#ifdef GEKKO
|
||||
SETTING_UINT("input_mouse_scale", &settings->uints.input_mouse_scale, true, DEFAULT_MOUSE_SCALE, false);
|
||||
#endif
|
||||
#if defined(DINGUX) && defined(HAVE_LIBSHAKE)
|
||||
SETTING_UINT("input_dingux_rumble_gain", &settings->uints.input_dingux_rumble_gain, true, DEFAULT_DINGUX_RUMBLE_GAIN, false);
|
||||
#endif
|
||||
SETTING_UINT("audio_latency", &settings->uints.audio_latency, false, 0 /* TODO */, false);
|
||||
SETTING_UINT("audio_resampler_quality", &settings->uints.audio_resampler_quality, true, audio_resampler_quality_level, false);
|
||||
|
@ -155,6 +155,8 @@ typedef struct settings
|
||||
unsigned input_menu_toggle_gamepad_combo;
|
||||
unsigned input_keyboard_gamepad_mapping_type;
|
||||
unsigned input_poll_type_behavior;
|
||||
unsigned input_dingux_rumble_gain;
|
||||
|
||||
unsigned netplay_port;
|
||||
unsigned netplay_input_latency_frames_min;
|
||||
unsigned netplay_input_latency_frames_range;
|
||||
|
23
deps/libShake/LICENSE.txt
vendored
Normal file
23
deps/libShake/LICENSE.txt
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015-2019 Artur Rojek
|
||||
Copyright (c) 2015-2019 Joe Vargas
|
||||
|
||||
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.
|
||||
|
72
deps/libShake/Makefile
vendored
Normal file
72
deps/libShake/Makefile
vendored
Normal file
@ -0,0 +1,72 @@
|
||||
ifeq ($(PLATFORM), gcw0)
|
||||
CC := /opt/gcw0-toolchain/usr/bin/mipsel-linux-gcc
|
||||
STRIP := /opt/gcw0-toolchain/usr/bin/mipsel-linux-strip
|
||||
BACKEND := LINUX
|
||||
endif
|
||||
|
||||
ifndef BACKEND
|
||||
$(error Please specify BACKEND. Possible values: LINUX, OSX")
|
||||
endif
|
||||
|
||||
LIBNAME := libshake
|
||||
SOVERSION := 2
|
||||
|
||||
SRCDIRS := common
|
||||
|
||||
ifeq ($(BACKEND), LINUX)
|
||||
LIBEXT := .so
|
||||
SONAME := $(LIBNAME)$(LIBEXT).$(SOVERSION)
|
||||
PREFIX ?= /usr
|
||||
LDFLAGS :=-Wl,-soname,$(SONAME)
|
||||
SRCDIRS += linux
|
||||
else ifeq ($(BACKEND), OSX)
|
||||
LIBEXT := .dylib
|
||||
SONAME := $(LIBNAME).$(SOVERSION)$(LIBEXT)
|
||||
PREFIX ?= /usr/local
|
||||
LDFLAGS := -Wl,-framework,Cocoa -framework IOKit -framework CoreFoundation -framework ForceFeedback -install_name $(SONAME)
|
||||
SRCDIRS += osx
|
||||
endif
|
||||
|
||||
CC ?= gcc
|
||||
STRIP ?= strip
|
||||
TARGET ?= $(SONAME)
|
||||
SYSROOT := $(shell $(CC) --print-sysroot)
|
||||
MACHINE ?= $(shell $(CC) -dumpmachine)
|
||||
DESTDIR ?= $(SYSROOT)
|
||||
CFLAGS := -fPIC
|
||||
SRCDIR := src
|
||||
OBJDIR := obj/$(MACHINE)
|
||||
SRC := $(foreach dir,$(SRCDIRS),$(sort $(wildcard $(addprefix $(SRCDIR)/,$(dir))/*.c)))
|
||||
OBJ := $(patsubst $(SRCDIR)/%.c,$(OBJDIR)/%.o,$(SRC))
|
||||
|
||||
ifdef DEBUG
|
||||
CFLAGS += -ggdb -Wall -Werror -pedantic -std=c89
|
||||
else
|
||||
CFLAGS += -O2
|
||||
endif
|
||||
|
||||
.PHONY: all install-headers install-lib install clean
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
$(TARGET): $(OBJ)
|
||||
$(CC) $(LDFLAGS) -shared $(CFLAGS) $^ -o $@
|
||||
ifdef DO_STRIP
|
||||
$(STRIP) $@
|
||||
endif
|
||||
|
||||
$(OBJ): $(OBJDIR)/%.o: $(SRCDIR)/%.c
|
||||
mkdir -p $(@D)
|
||||
$(CC) -c $(CFLAGS) $< -o $@ -I include
|
||||
|
||||
install-headers:
|
||||
cp include/*.h $(DESTDIR)$(PREFIX)/include/
|
||||
|
||||
install-lib:
|
||||
cp $(TARGET) $(DESTDIR)$(PREFIX)/lib/
|
||||
ln -sf $(TARGET) $(DESTDIR)$(PREFIX)/lib/$(LIBNAME)$(LIBEXT)
|
||||
|
||||
install: $(TARGET) install-headers install-lib
|
||||
|
||||
clean:
|
||||
rm -Rf $(TARGET) $(OBJDIR)
|
44
deps/libShake/README.md
vendored
Normal file
44
deps/libShake/README.md
vendored
Normal file
@ -0,0 +1,44 @@
|
||||
libShake
|
||||
========
|
||||
|
||||
About
|
||||
-----
|
||||
libShake is a simple, cross-platform haptic library.
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
### Linux
|
||||
|
||||
```shell
|
||||
BACKEND=LINUX make install
|
||||
```
|
||||
|
||||
### OSX
|
||||
|
||||
```shell
|
||||
BACKEND=OSX make install
|
||||
```
|
||||
|
||||
Authors
|
||||
-------
|
||||
* Artur Rojek (zear)
|
||||
* Joe Vargas (jxv)
|
||||
|
||||
License
|
||||
-------
|
||||
This program is released under the terms of MIT License, also known as Expat License.
|
||||
|
||||
See [LICENSE.txt](LICENSE.txt) for more details.
|
||||
|
||||
Extras
|
||||
------
|
||||
Buildroot support is available for libShake. Package config can be found in [extras/buildroot-package/](extras/buildroot-package/).
|
||||
|
||||
IRC channel
|
||||
-----------
|
||||
Join our IRC chatroom at the following address:
|
||||
|
||||
`#libShake` on `chat.freenode.net`
|
||||
|
||||
More information about the Freenode network can be found on [Freenode](https://freenode.net/).
|
28
deps/libShake/examples/Makefile
vendored
Normal file
28
deps/libShake/examples/Makefile
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
ifeq ($(PLATFORM), gcw0)
|
||||
CC := /opt/gcw0-toolchain/usr/bin/mipsel-linux-gcc
|
||||
STRIP := /opt/gcw0-toolchain/usr/bin/mipsel-linux-strip
|
||||
endif
|
||||
|
||||
CC ?= gcc
|
||||
STRIP ?= strip
|
||||
TARGET ?= simple listDevices deviceTest
|
||||
LDFLAGS := -L.. -lshake
|
||||
CFLAGS := -fPIC -I../include
|
||||
SRCDIR := .
|
||||
|
||||
ifdef DEBUG
|
||||
CFLAGS += -ggdb -Wall -Werror -pedantic -std=c89
|
||||
else
|
||||
CFLAGS += -O2
|
||||
endif
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
$(TARGET):
|
||||
$(CC) $(CFLAGS) $(SRCDIR)/$@.c $(LDFLAGS) -o $@
|
||||
$(STRIP) $@
|
||||
|
||||
clean:
|
||||
rm -Rf $(TARGET)
|
333
deps/libShake/examples/deviceTest.c
vendored
Normal file
333
deps/libShake/examples/deviceTest.c
vendored
Normal file
@ -0,0 +1,333 @@
|
||||
#include <shake.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
Shake_Device *device;
|
||||
static int quit;
|
||||
|
||||
static void waitForKey(void)
|
||||
{
|
||||
printf("Press RETURN to continue");
|
||||
while(getchar() != '\n');
|
||||
}
|
||||
|
||||
static void listDevices(void)
|
||||
{
|
||||
int numDevices;
|
||||
int i;
|
||||
|
||||
numDevices = Shake_NumOfDevices();
|
||||
printf("Detected devices: %d\n\n", numDevices);
|
||||
|
||||
for (i = 0; i < numDevices; ++i)
|
||||
{
|
||||
Shake_Device *dev = (i == Shake_DeviceId(dev)) ? dev : Shake_Open(i);
|
||||
printf("#%2d: %s\n", Shake_DeviceId(dev), Shake_DeviceName(dev));
|
||||
|
||||
if (dev != device)
|
||||
Shake_Close(dev);
|
||||
}
|
||||
}
|
||||
|
||||
static void deviceInfo(void)
|
||||
{
|
||||
printf("\nDevice #%d\n", Shake_DeviceId(device));
|
||||
printf(" Name: %s\n", Shake_DeviceName(device));
|
||||
printf(" Adjustable gain: %s\n", Shake_QueryGainSupport(device) ? "yes" : "no");
|
||||
printf(" Adjustable autocenter: %s\n", Shake_QueryAutocenterSupport(device) ? "yes" : "no");
|
||||
printf(" Effect capacity: %d\n", Shake_DeviceEffectCapacity(device));
|
||||
printf(" Supported effects:\n");
|
||||
if (Shake_QueryEffectSupport(device, SHAKE_EFFECT_RUMBLE)) printf(" SHAKE_EFFECT_RUMBLE\n");
|
||||
if (Shake_QueryEffectSupport(device, SHAKE_EFFECT_PERIODIC))
|
||||
{
|
||||
printf(" SHAKE_EFFECT_PERIODIC\n");
|
||||
if (Shake_QueryWaveformSupport(device, SHAKE_PERIODIC_SQUARE)) printf(" * SHAKE_PERIODIC_SQUARE\n");
|
||||
if (Shake_QueryWaveformSupport(device, SHAKE_PERIODIC_TRIANGLE)) printf(" * SHAKE_PERIODIC_TRIANGLE\n");
|
||||
if (Shake_QueryWaveformSupport(device, SHAKE_PERIODIC_SINE)) printf(" * SHAKE_PERIODIC_SINE\n");
|
||||
if (Shake_QueryWaveformSupport(device, SHAKE_PERIODIC_SAW_UP)) printf(" * SHAKE_PERIODIC_SAW_UP\n");
|
||||
if (Shake_QueryWaveformSupport(device, SHAKE_PERIODIC_SAW_DOWN)) printf(" * SHAKE_PERIODIC_SAW_DOWN\n");
|
||||
if (Shake_QueryWaveformSupport(device, SHAKE_PERIODIC_CUSTOM)) printf(" * SHAKE_PERIODIC_CUSTOM\n");
|
||||
}
|
||||
if (Shake_QueryEffectSupport(device, SHAKE_EFFECT_CONSTANT)) printf(" SHAKE_EFFECT_CONSTANT\n");
|
||||
if (Shake_QueryEffectSupport(device, SHAKE_EFFECT_SPRING)) printf(" SHAKE_EFFECT_SPRING\n");
|
||||
if (Shake_QueryEffectSupport(device, SHAKE_EFFECT_FRICTION)) printf(" SHAKE_EFFECT_FRICTION\n");
|
||||
if (Shake_QueryEffectSupport(device, SHAKE_EFFECT_DAMPER)) printf(" SHAKE_EFFECT_DAMPER\n");
|
||||
if (Shake_QueryEffectSupport(device, SHAKE_EFFECT_INERTIA)) printf(" SHAKE_EFFECT_INERTIA\n");
|
||||
if (Shake_QueryEffectSupport(device, SHAKE_EFFECT_RAMP)) printf(" SHAKE_EFFECT_RAMP\n");
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static void testCapacity(void)
|
||||
{
|
||||
Shake_Effect effect;
|
||||
int capacity = Shake_DeviceEffectCapacity(device);
|
||||
int *id;
|
||||
int i;
|
||||
|
||||
id = malloc(capacity * sizeof(int));
|
||||
if (!id)
|
||||
{
|
||||
printf("Unable to allocate memory.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
printf("-Capacity-\n");
|
||||
printf("Reported capacity: %d\n", capacity);
|
||||
|
||||
for (i = 0; i < capacity; ++i)
|
||||
{
|
||||
Shake_SimplePeriodic(&effect, SHAKE_PERIODIC_SINE, 1.0, 0.0, 1.0, 0.0);
|
||||
id[i] = Shake_UploadEffect(device, &effect);
|
||||
printf("Uploaded #%d as #%d\n", i, id[i]);
|
||||
}
|
||||
|
||||
printf("-End-\n\n");
|
||||
|
||||
for (i = 0; i < capacity; ++i)
|
||||
{
|
||||
Shake_EraseEffect(device, id[i]);
|
||||
}
|
||||
|
||||
free(id);
|
||||
}
|
||||
|
||||
static void testEffectPlayback(void)
|
||||
{
|
||||
Shake_Effect effect;
|
||||
int id;
|
||||
|
||||
printf("-Effect playback-\n");
|
||||
Shake_SimplePeriodic(&effect, SHAKE_PERIODIC_SINE, 1.0, 0.0, 2.0, 0.0);
|
||||
id = Shake_UploadEffect(device, &effect);
|
||||
Shake_Play(device, id);
|
||||
printf("Playing (2 sec)\n");
|
||||
sleep(1);
|
||||
Shake_Stop(device, id);
|
||||
printf("Stopping (at 1 sec)\n");
|
||||
sleep(1);
|
||||
Shake_Play(device, id);
|
||||
printf("Replaying (2 sec)\n");
|
||||
sleep(2);
|
||||
printf("-End-\n\n");
|
||||
|
||||
Shake_EraseEffect(device, id);
|
||||
}
|
||||
|
||||
static void testEffectOrder(void)
|
||||
{
|
||||
Shake_Effect effect;
|
||||
int id[4];
|
||||
int shuffle[4];
|
||||
int i;
|
||||
|
||||
printf("-Effect order-\n");
|
||||
Shake_SimplePeriodic(&effect, SHAKE_PERIODIC_SINE, 1.0, 0.0, 1.0, 0.0);
|
||||
id[0] = Shake_UploadEffect(device, &effect);
|
||||
|
||||
Shake_SimplePeriodic(&effect, SHAKE_PERIODIC_SQUARE, 1.0, 0.0, 1.0, 0.0);
|
||||
id[1] = Shake_UploadEffect(device, &effect);
|
||||
|
||||
Shake_SimpleRumble(&effect, 1.0, 1.0, 1.0);
|
||||
id[2] = Shake_UploadEffect(device, &effect);
|
||||
|
||||
Shake_SimpleRumble(&effect, 0.8, 1.0, 1.0);
|
||||
id[3] = Shake_UploadEffect(device, &effect);
|
||||
|
||||
for (i = 0; i < 4; ++i)
|
||||
{
|
||||
int r;
|
||||
|
||||
while (1)
|
||||
{
|
||||
int repeat = 0;
|
||||
int j;
|
||||
|
||||
r = rand() % 4;
|
||||
for (j = 0; j < i; ++j)
|
||||
{
|
||||
if (r == shuffle[j])
|
||||
{
|
||||
repeat = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!repeat)
|
||||
break;
|
||||
}
|
||||
|
||||
shuffle[i] = r;
|
||||
}
|
||||
|
||||
for (i = 0; i < 4; ++i)
|
||||
{
|
||||
Shake_Play(device, id[shuffle[i]]);
|
||||
printf("Effect #%d\n", shuffle[i]);
|
||||
sleep(1);
|
||||
}
|
||||
printf("-End-\n\n");
|
||||
|
||||
for (i = 0; i < 4; ++i)
|
||||
{
|
||||
Shake_EraseEffect(device, id[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void testEffectUpdate(void)
|
||||
{
|
||||
Shake_Effect effect;
|
||||
int id;
|
||||
|
||||
printf("-Effect update-\n");
|
||||
Shake_SimplePeriodic(&effect, SHAKE_PERIODIC_SINE, 1.0, 0.0, 4.0, 0.0);
|
||||
id = Shake_UploadEffect(device, &effect);
|
||||
Shake_Play(device, id);
|
||||
printf("Original\n");
|
||||
sleep(2);
|
||||
effect.u.periodic.magnitude = 0x6000;
|
||||
effect.id = id;
|
||||
id = Shake_UploadEffect(device, &effect);
|
||||
printf("Updated\n");
|
||||
sleep(2);
|
||||
printf("-End-\n\n");
|
||||
|
||||
Shake_EraseEffect(device, id);
|
||||
}
|
||||
|
||||
static void testEffectMixing(void)
|
||||
{
|
||||
Shake_Effect effect;
|
||||
int id[3];
|
||||
int i;
|
||||
|
||||
printf("-Effect mixing-\n");
|
||||
Shake_SimplePeriodic(&effect, SHAKE_PERIODIC_SINE, 0.6, 0.0, 4.0, 0.0);
|
||||
id[0] = Shake_UploadEffect(device, &effect);
|
||||
Shake_SimplePeriodic(&effect, SHAKE_PERIODIC_SQUARE, 0.2, 0.0, 2.0, 0.0);
|
||||
id[1] = Shake_UploadEffect(device, &effect);
|
||||
Shake_SimplePeriodic(&effect, SHAKE_PERIODIC_SINE, 0.2, 0.0, 1.0, 0.0);
|
||||
id[2] = Shake_UploadEffect(device, &effect);
|
||||
|
||||
Shake_Play(device, id[0]);
|
||||
printf("Playing #1 (0.6 mag)\n");
|
||||
sleep(1);
|
||||
Shake_Play(device, id[1]);
|
||||
printf("Adding #2 (+0.2 mag)\n");
|
||||
sleep(1);
|
||||
Shake_Play(device, id[2]);
|
||||
printf("Adding #3 (+0.2 mag)\n");
|
||||
sleep(1);
|
||||
printf("Removing #2 and #3 (-0.4 mag)\n");
|
||||
sleep(1);
|
||||
printf("-End-\n\n");
|
||||
|
||||
for (i = 0; i < 3; ++i)
|
||||
{
|
||||
Shake_EraseEffect(device, id[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void menu(void)
|
||||
{
|
||||
int selection;
|
||||
|
||||
printf("Device #%d: %s\n\n", Shake_DeviceId(device), Shake_DeviceName(device));
|
||||
printf("Select test:\n");
|
||||
printf("1) Effect capacity\n2) Effect playback\n3) Effect order\n4) Effect update\n5) Effect mixing\n6) Play all above tests\n\nI) Device info\nL) List devices\nQ) Quit\n\n");
|
||||
printf("> ");
|
||||
|
||||
selection = getchar();
|
||||
|
||||
switch (selection)
|
||||
{
|
||||
case 'q':
|
||||
case 'Q':
|
||||
quit = 1;
|
||||
break;
|
||||
case 'i':
|
||||
case 'I':
|
||||
deviceInfo();
|
||||
waitForKey();
|
||||
break;
|
||||
case 'l':
|
||||
case 'L':
|
||||
listDevices();
|
||||
waitForKey();
|
||||
break;
|
||||
case '1':
|
||||
testCapacity();
|
||||
waitForKey();
|
||||
break;
|
||||
case '2':
|
||||
testEffectPlayback();
|
||||
waitForKey();
|
||||
break;
|
||||
case '3':
|
||||
testEffectOrder();
|
||||
waitForKey();
|
||||
break;
|
||||
case '4':
|
||||
testEffectUpdate();
|
||||
waitForKey();
|
||||
break;
|
||||
case '5':
|
||||
testEffectMixing();
|
||||
waitForKey();
|
||||
break;
|
||||
case '6':
|
||||
testCapacity();
|
||||
sleep(1);
|
||||
testEffectPlayback();
|
||||
sleep(1);
|
||||
testEffectOrder();
|
||||
sleep(1);
|
||||
testEffectUpdate();
|
||||
sleep(1);
|
||||
testEffectMixing();
|
||||
waitForKey();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
while(getchar() != '\n');
|
||||
printf("------\n");
|
||||
}
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
int deviceId = 0;
|
||||
|
||||
if (argc > 1)
|
||||
deviceId = atoi(argv[1]);
|
||||
|
||||
if (deviceId < 0)
|
||||
{
|
||||
printf("Device number must be greater than or equal to 0.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
srand(time(NULL));
|
||||
Shake_Init();
|
||||
|
||||
if (Shake_NumOfDevices() <= deviceId)
|
||||
{
|
||||
printf("Device #%d doesn't exist.\n", deviceId);
|
||||
return -1;
|
||||
}
|
||||
|
||||
device = Shake_Open(deviceId);
|
||||
|
||||
while (!quit)
|
||||
menu();
|
||||
|
||||
/* Cleanup. */
|
||||
Shake_Close(device);
|
||||
Shake_Quit();
|
||||
|
||||
return 0;
|
||||
}
|
51
deps/libShake/examples/listDevices.c
vendored
Normal file
51
deps/libShake/examples/listDevices.c
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
#include <shake.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
void deviceInfo(Shake_Device *device)
|
||||
{
|
||||
printf("\nDevice #%d\n", Shake_DeviceId(device));
|
||||
printf(" Name: %s\n", Shake_DeviceName(device));
|
||||
printf(" Adjustable gain: %s\n", Shake_QueryGainSupport(device) ? "yes" : "no");
|
||||
printf(" Adjustable autocenter: %s\n", Shake_QueryAutocenterSupport(device) ? "yes" : "no");
|
||||
printf(" Effect capacity: %d\n", Shake_DeviceEffectCapacity(device));
|
||||
printf(" Supported effects:\n");
|
||||
if (Shake_QueryEffectSupport(device, SHAKE_EFFECT_RUMBLE)) printf(" SHAKE_EFFECT_RUMBLE\n");
|
||||
if (Shake_QueryEffectSupport(device, SHAKE_EFFECT_PERIODIC))
|
||||
{
|
||||
printf(" SHAKE_EFFECT_PERIODIC\n");
|
||||
if (Shake_QueryWaveformSupport(device, SHAKE_PERIODIC_SQUARE)) printf(" * SHAKE_PERIODIC_SQUARE\n");
|
||||
if (Shake_QueryWaveformSupport(device, SHAKE_PERIODIC_TRIANGLE)) printf(" * SHAKE_PERIODIC_TRIANGLE\n");
|
||||
if (Shake_QueryWaveformSupport(device, SHAKE_PERIODIC_SINE)) printf(" * SHAKE_PERIODIC_SINE\n");
|
||||
if (Shake_QueryWaveformSupport(device, SHAKE_PERIODIC_SAW_UP)) printf(" * SHAKE_PERIODIC_SAW_UP\n");
|
||||
if (Shake_QueryWaveformSupport(device, SHAKE_PERIODIC_SAW_DOWN)) printf(" * SHAKE_PERIODIC_SAW_DOWN\n");
|
||||
if (Shake_QueryWaveformSupport(device, SHAKE_PERIODIC_CUSTOM)) printf(" * SHAKE_PERIODIC_CUSTOM\n");
|
||||
}
|
||||
if (Shake_QueryEffectSupport(device, SHAKE_EFFECT_CONSTANT)) printf(" SHAKE_EFFECT_CONSTANT\n");
|
||||
if (Shake_QueryEffectSupport(device, SHAKE_EFFECT_SPRING)) printf(" SHAKE_EFFECT_SPRING\n");
|
||||
if (Shake_QueryEffectSupport(device, SHAKE_EFFECT_FRICTION)) printf(" SHAKE_EFFECT_FRICTION\n");
|
||||
if (Shake_QueryEffectSupport(device, SHAKE_EFFECT_DAMPER)) printf(" SHAKE_EFFECT_DAMPER\n");
|
||||
if (Shake_QueryEffectSupport(device, SHAKE_EFFECT_INERTIA)) printf(" SHAKE_EFFECT_INERTIA\n");
|
||||
if (Shake_QueryEffectSupport(device, SHAKE_EFFECT_RAMP)) printf(" SHAKE_EFFECT_RAMP\n");
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
int numDevices;
|
||||
int i;
|
||||
|
||||
Shake_Init();
|
||||
numDevices = Shake_NumOfDevices();
|
||||
printf("Detected devices: %d\n", numDevices);
|
||||
|
||||
for (i = 0; i < numDevices; ++i)
|
||||
{
|
||||
Shake_Device *device = Shake_Open(i);
|
||||
deviceInfo(device);
|
||||
Shake_Close(device);
|
||||
}
|
||||
|
||||
Shake_Quit();
|
||||
|
||||
return 0;
|
||||
}
|
40
deps/libShake/examples/simple.c
vendored
Normal file
40
deps/libShake/examples/simple.c
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
#include <shake.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
Shake_Device *device;
|
||||
Shake_Effect effect;
|
||||
int id;
|
||||
|
||||
Shake_Init();
|
||||
|
||||
if (Shake_NumOfDevices() > 0)
|
||||
{
|
||||
device = Shake_Open(0);
|
||||
|
||||
Shake_InitEffect(&effect, SHAKE_EFFECT_PERIODIC);
|
||||
effect.u.periodic.waveform = SHAKE_PERIODIC_SINE;
|
||||
effect.u.periodic.period = 0.1*0x100;
|
||||
effect.u.periodic.magnitude = 0x6000;
|
||||
effect.u.periodic.envelope.attackLength = 0x100;
|
||||
effect.u.periodic.envelope.attackLevel = 0;
|
||||
effect.u.periodic.envelope.fadeLength = 0x100;
|
||||
effect.u.periodic.envelope.fadeLevel = 0;
|
||||
effect.direction = 0x4000;
|
||||
effect.length = 2000;
|
||||
effect.delay = 0;
|
||||
|
||||
id = Shake_UploadEffect(device, &effect);
|
||||
Shake_Play(device, id);
|
||||
|
||||
sleep(2);
|
||||
Shake_EraseEffect(device, id);
|
||||
Shake_Close(device);
|
||||
}
|
||||
|
||||
Shake_Quit();
|
||||
|
||||
return 0;
|
||||
}
|
6
deps/libShake/extras/buildroot-package/libshake/Config.in
vendored
Normal file
6
deps/libShake/extras/buildroot-package/libshake/Config.in
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
config BR2_PACKAGE_LIBSHAKE
|
||||
bool "libshake"
|
||||
help
|
||||
A simple, cross-platform haptic library.
|
||||
|
||||
https://github.com/zear/libShake
|
26
deps/libShake/extras/buildroot-package/libshake/libshake.mk
vendored
Normal file
26
deps/libShake/extras/buildroot-package/libshake/libshake.mk
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
#############################################################
|
||||
#
|
||||
# libshake
|
||||
#
|
||||
#############################################################
|
||||
LIBSHAKE_VERSION = master
|
||||
LIBSHAKE_SITE = $(call github,zear,libShake,$(LIBSHAKE_VERSION))
|
||||
LIBSHAKE_LICENSE = MIT
|
||||
LIBSHAKE_LICENSE_FILES = LICENSE.txt
|
||||
LIBSHAKE_INSTALL_STAGING = YES
|
||||
|
||||
LIBSHAKE_MAKE_ENV = CC="$(TARGET_CC)" PREFIX=/usr
|
||||
|
||||
define LIBSHAKE_BUILD_CMDS
|
||||
$(LIBSHAKE_MAKE_ENV) $(MAKE) -C $(@D) BACKEND=LINUX
|
||||
endef
|
||||
|
||||
define LIBSHAKE_INSTALL_STAGING_CMDS
|
||||
$(LIBSHAKE_MAKE_ENV) DESTDIR="$(STAGING_DIR)" $(MAKE) -C $(@D) BACKEND=LINUX install
|
||||
endef
|
||||
|
||||
define LIBSHAKE_INSTALL_TARGET_CMDS
|
||||
$(LIBSHAKE_MAKE_ENV) DESTDIR="$(TARGET_DIR)" $(MAKE) -C $(@D) BACKEND=LINUX install-lib
|
||||
endef
|
||||
|
||||
$(eval $(generic-package))
|
522
deps/libShake/include/shake.h
vendored
Normal file
522
deps/libShake/include/shake.h
vendored
Normal file
@ -0,0 +1,522 @@
|
||||
/** \file shake.h
|
||||
\brief libShake public header.
|
||||
*/
|
||||
|
||||
#ifndef _SHAKE_H_
|
||||
#define _SHAKE_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/** \def SHAKE_MAJOR_VERSION
|
||||
\brief Version of the program (major).
|
||||
*/
|
||||
#define SHAKE_MAJOR_VERSION 0
|
||||
|
||||
/** \def SHAKE_MINOR_VERSION
|
||||
\brief Version of the program (minor).
|
||||
*/
|
||||
#define SHAKE_MINOR_VERSION 3
|
||||
|
||||
/** \def SHAKE_PATCH_VERSION
|
||||
\brief Version of the program (patch).
|
||||
*/
|
||||
#define SHAKE_PATCH_VERSION 2
|
||||
|
||||
/** \def SHAKE_ENVELOPE_ATTACK_LENGTH_MAX
|
||||
\brief Maximum allowed value of Shake_Envelope attackLength
|
||||
\sa Shake_Envelope
|
||||
*/
|
||||
#define SHAKE_ENVELOPE_ATTACK_LENGTH_MAX 0x7FFF
|
||||
|
||||
/** \def SHAKE_ENVELOPE_FADE_LENGTH_MAX
|
||||
\brief Maximum allowed value of Shake_Envelope fadeLength
|
||||
\sa Shake_Envelope
|
||||
*/
|
||||
#define SHAKE_ENVELOPE_FADE_LENGTH_MAX 0x7FFF
|
||||
|
||||
/** \def SHAKE_ENVELOPE_ATTACK_LEVEL_MAX
|
||||
\brief Maximum allowed value of Shake_Envelope attackLevel
|
||||
\sa Shake_Envelope
|
||||
*/
|
||||
#define SHAKE_ENVELOPE_ATTACK_LEVEL_MAX 0x7FFF
|
||||
|
||||
/** \def SHAKE_ENVELOPE_FADE_LEVEL_MAX
|
||||
\brief Maximum allowed value of Shake_Envelope fadeLevel
|
||||
\sa Shake_Envelope
|
||||
*/
|
||||
#define SHAKE_ENVELOPE_FADE_LEVEL_MAX 0x7FFF
|
||||
|
||||
/** \def SHAKE_RUMBLE_STRONG_MAGNITUDE_MAX
|
||||
\brief Maximum allowed value of Shake_EffectRumble strongMagnitude
|
||||
\sa Shake_EffectRumble
|
||||
*/
|
||||
#define SHAKE_RUMBLE_STRONG_MAGNITUDE_MAX 0x7FFF
|
||||
|
||||
/** \def SHAKE_RUMBLE_WEAK_MAGNITUDE_MAX
|
||||
\brief Maximum allowed value of Shake_EffectRumble weakMagnitude
|
||||
\sa Shake_EffectRumble
|
||||
*/
|
||||
#define SHAKE_RUMBLE_WEAK_MAGNITUDE_MAX 0x7FFF
|
||||
|
||||
/** \def SHAKE_PERIODIC_PERIOD_MAX
|
||||
\brief Maximum allowed value of Shake_EffectPeriodic period
|
||||
\sa Shake_EffectPeriodic
|
||||
*/
|
||||
#define SHAKE_PERIODIC_PERIOD_MAX 0x7FFF
|
||||
|
||||
/** \def SHAKE_PERIODIC_MAGNITUDE_MIN
|
||||
\brief Minimum allowed value of Shake_EffectPeriodic magnitude
|
||||
\sa Shake_EffectPeriodic
|
||||
*/
|
||||
#define SHAKE_PERIODIC_MAGNITUDE_MIN (-0x8000)
|
||||
|
||||
/** \def SHAKE_PERIODIC_MAGNITUDE_MAX
|
||||
\brief Maximum allowed value of Shake_EffectPeriodic magnitude
|
||||
\sa Shake_EffectPeriodic
|
||||
*/
|
||||
#define SHAKE_PERIODIC_MAGNITUDE_MAX 0x7FFF
|
||||
|
||||
/** \def SHAKE_PERIODIC_OFFSET_MIN
|
||||
\brief Minimum allowed value of Shake_EffectPeriodic offset
|
||||
\sa Shake_EffectPeriodic
|
||||
*/
|
||||
#define SHAKE_PERIODIC_OFFSET_MIN (-0x8000)
|
||||
|
||||
/** \def SHAKE_PERIODIC_OFFSET_MAX
|
||||
\brief Maximum allowed value of Shake_EffectPeriodic offset
|
||||
\sa Shake_EffectPeriodic
|
||||
*/
|
||||
#define SHAKE_PERIODIC_OFFSET_MAX 0x7FFF
|
||||
|
||||
/** \def SHAKE_PERIODIC_PHASE_MAX
|
||||
\brief Maximum allowed value of Shake_EffectPeriodic phase
|
||||
\sa Shake_EffectPeriodic
|
||||
*/
|
||||
#define SHAKE_PERIODIC_PHASE_MAX 0x7FFF
|
||||
|
||||
/** \def SHAKE_CONSTANT_LEVEL_MIN
|
||||
\brief Minimum allowed value of Shake_EffectConstant level
|
||||
\sa Shake_EffectConstant
|
||||
*/
|
||||
#define SHAKE_CONSTANT_LEVEL_MIN (-0x8000)
|
||||
|
||||
/** \def SHAKE_CONSTANT_LEVEL_MAX
|
||||
\brief Maximum allowed value of Shake_EffectConstant level
|
||||
\sa Shake_EffectConstant
|
||||
*/
|
||||
#define SHAKE_CONSTANT_LEVEL_MAX 0x7FFF
|
||||
|
||||
/** \def SHAKE_RAMP_START_LEVEL_MIN
|
||||
\brief Minimum allowed value of Shake_EffectRamp startLevel
|
||||
\sa Shake_EffectRamp
|
||||
*/
|
||||
#define SHAKE_RAMP_START_LEVEL_MIN (-0x8000)
|
||||
|
||||
/** \def SHAKE_RAMP_START_LEVEL_MAX
|
||||
\brief Maximum allowed value of Shake_EffectRamp startLevel
|
||||
\sa Shake_EffectRamp
|
||||
*/
|
||||
#define SHAKE_RAMP_START_LEVEL_MAX 0x7FFF
|
||||
|
||||
/** \def SHAKE_RAMP_END_LEVEL_MIN
|
||||
\brief Minimum allowed value of Shake_EffectRamp endLevel
|
||||
\sa Shake_EffectRamp
|
||||
*/
|
||||
#define SHAKE_RAMP_END_LEVEL_MIN (-0x8000)
|
||||
|
||||
/** \def SHAKE_RAMP_END_LEVEL_MAX
|
||||
\brief Maximum allowed value of Shake_EffectRamp endLevel
|
||||
\sa Shake_EffectRamp
|
||||
*/
|
||||
#define SHAKE_RAMP_END_LEVEL_MAX 0x7FFF
|
||||
|
||||
/** \def SHAKE_EFFECT_ID_MIN
|
||||
\brief Minimum allowed value of Shake_Effect id
|
||||
\sa Shake_Effect
|
||||
*/
|
||||
#define SHAKE_EFFECT_ID_MIN (-0x0001)
|
||||
|
||||
/** \def SHAKE_EFFECT_DIRECTION_MAX
|
||||
\brief Maximum allowed value of Shake_Effect direction
|
||||
\sa Shake_Effect
|
||||
*/
|
||||
#define SHAKE_EFFECT_DIRECTION_MAX 0xFFFE
|
||||
|
||||
/** \def SHAKE_EFFECT_LENGTH_MAX
|
||||
\brief Maximum allowed value of Shake_Effect length
|
||||
\sa Shake_Effect
|
||||
*/
|
||||
#define SHAKE_EFFECT_LENGTH_MAX 0x7FFF
|
||||
|
||||
/** \def SHAKE_EFFECT_DELAY_MAX
|
||||
\brief Maximum allowed value of Shake_Effect delay
|
||||
\sa Shake_Effect
|
||||
*/
|
||||
#define SHAKE_EFFECT_DELAY_MAX 0x7FFF
|
||||
|
||||
/** \enum Shake_Status
|
||||
\brief Request status.
|
||||
*/
|
||||
typedef enum Shake_Status
|
||||
{
|
||||
SHAKE_ERROR = -1, /**< Error. */
|
||||
SHAKE_OK = 0 /**< Success. */
|
||||
} Shake_Status;
|
||||
|
||||
/** \enum Shake_Bool
|
||||
\brief Boolean type.
|
||||
*/
|
||||
typedef enum Shake_Bool
|
||||
{
|
||||
SHAKE_FALSE = 0, /**< False. */
|
||||
SHAKE_TRUE = 1 /**< True. */
|
||||
} Shake_Bool;
|
||||
|
||||
/** \enum Shake_ErrorCode
|
||||
\brief Information about the error origin.
|
||||
*/
|
||||
typedef enum Shake_ErrorCode
|
||||
{
|
||||
SHAKE_EC_UNSET, /**< No error triggered yet. */
|
||||
SHAKE_EC_SUPPORT, /**< Feature not supported. */
|
||||
SHAKE_EC_DEVICE, /**< Device related. */
|
||||
SHAKE_EC_EFFECT, /**< Effect related. */
|
||||
SHAKE_EC_QUERY, /**< Query related. */
|
||||
SHAKE_EC_ARG, /**< Invalid argument. */
|
||||
SHAKE_EC_TRANSFER /**< Device transfer related. */
|
||||
} Shake_ErrorCode;
|
||||
|
||||
/** \struct Shake_Device
|
||||
\brief Haptic device.
|
||||
*/
|
||||
struct Shake_Device;
|
||||
typedef struct Shake_Device Shake_Device;
|
||||
|
||||
/** \enum Shake_PeriodicWaveform
|
||||
\brief Periodic effect waveform.
|
||||
*/
|
||||
typedef enum Shake_PeriodicWaveform
|
||||
{
|
||||
SHAKE_PERIODIC_SQUARE = 0, /**< Square waveform. */
|
||||
SHAKE_PERIODIC_TRIANGLE, /**< Triangle waveform. */
|
||||
SHAKE_PERIODIC_SINE, /**< Sine waveform. */
|
||||
SHAKE_PERIODIC_SAW_UP, /**< Saw up waveform. */
|
||||
SHAKE_PERIODIC_SAW_DOWN, /**< Saw down waveform. */
|
||||
SHAKE_PERIODIC_CUSTOM, /**< Custom waveform. */
|
||||
SHAKE_PERIODIC_COUNT
|
||||
} Shake_PeriodicWaveform;
|
||||
|
||||
/** \enum Shake_EffectType
|
||||
\brief Effect type.
|
||||
*/
|
||||
typedef enum Shake_EffectType
|
||||
{
|
||||
SHAKE_EFFECT_RUMBLE = 0, /**< Rumble effect. */
|
||||
SHAKE_EFFECT_PERIODIC, /**< Periodic effect. */
|
||||
SHAKE_EFFECT_CONSTANT, /**< Constant effect. */
|
||||
SHAKE_EFFECT_SPRING, /**< Spring effect. <b>NOTE:</b> Currently not supported. */
|
||||
SHAKE_EFFECT_FRICTION, /**< Friction effect. <b>NOTE:</b> Currently not supported. */
|
||||
SHAKE_EFFECT_DAMPER, /**< Damper effect. <b>NOTE:</b> Currently not supported. */
|
||||
SHAKE_EFFECT_INERTIA, /**< Inertia effect. <b>NOTE:</b> Currently not supported. */
|
||||
SHAKE_EFFECT_RAMP, /**< Ramp effect. */
|
||||
SHAKE_EFFECT_COUNT
|
||||
} Shake_EffectType;
|
||||
|
||||
/** \struct Shake_Envelope
|
||||
\brief Effect envelope.
|
||||
*/
|
||||
typedef struct Shake_Envelope
|
||||
{
|
||||
uint16_t attackLength; /**< Envelope attack duration. */
|
||||
uint16_t attackLevel; /**< Envelope attack level. */
|
||||
uint16_t fadeLength; /**< Envelope fade duration. */
|
||||
uint16_t fadeLevel; /**< Envelope fade level. */
|
||||
} Shake_Envelope;
|
||||
|
||||
/** \struct Shake_EffectRumble
|
||||
\brief Rumble effect structure.
|
||||
|
||||
<b>NOTE:</b> On Linux and OSX backends this effect is internally emulated by averaging the motor values
|
||||
with a periodic effect and no direct control over individual motors is available.
|
||||
*/
|
||||
typedef struct Shake_EffectRumble
|
||||
{
|
||||
uint16_t strongMagnitude; /**< Magnitude of the heavy motor. */
|
||||
uint16_t weakMagnitude; /**< Magnitude of the light motor. */
|
||||
} Shake_EffectRumble;
|
||||
|
||||
/** \struct Shake_EffectPeriodic
|
||||
\brief Periodic effect structure.
|
||||
*/
|
||||
typedef struct Shake_EffectPeriodic
|
||||
{
|
||||
Shake_PeriodicWaveform waveform;/**< Effect waveform. */
|
||||
uint16_t period; /**< Period of the wave (in ms). */
|
||||
int16_t magnitude; /**< Peak value of the wave. */
|
||||
int16_t offset; /**< Mean value of the wave. */
|
||||
uint16_t phase; /**< Horizontal shift of the wave. */
|
||||
Shake_Envelope envelope; /**< Effect envelope. */
|
||||
} Shake_EffectPeriodic;
|
||||
|
||||
/** \struct Shake_EffectConstant
|
||||
\brief Constant effect structure.
|
||||
*/
|
||||
typedef struct Shake_EffectConstant
|
||||
{
|
||||
int16_t level; /**< Magnitude of the effect. */
|
||||
Shake_Envelope envelope; /**< Effect envelope. */
|
||||
} Shake_EffectConstant;
|
||||
|
||||
/** \struct Shake_EffectRamp
|
||||
\brief Ramp effect structure.
|
||||
*/
|
||||
typedef struct Shake_EffectRamp
|
||||
{
|
||||
int16_t startLevel; /**< Starting magnitude of the effect. */
|
||||
int16_t endLevel; /**< Ending magnitude of the effect. */
|
||||
Shake_Envelope envelope; /**< Effect envelope. */
|
||||
} Shake_EffectRamp;
|
||||
|
||||
/** \struct Shake_Effect
|
||||
\brief Effect structure.
|
||||
*/
|
||||
typedef struct Shake_Effect
|
||||
{
|
||||
Shake_EffectType type; /**< Effect type. */
|
||||
int16_t id; /**< Effect id. Value of -1 creates a new effect. Value of 0 or greater modifies existing effect in a device. */
|
||||
uint16_t direction; /**< Direction of the effect. */
|
||||
uint16_t length; /**< Duration of the effect (in ms). */
|
||||
uint16_t delay; /**< Delay before the effect starts playing (in ms). */
|
||||
union
|
||||
{
|
||||
Shake_EffectRumble rumble;
|
||||
Shake_EffectPeriodic periodic;
|
||||
Shake_EffectConstant constant;
|
||||
Shake_EffectRamp ramp;
|
||||
} u; /**< Effect type data container. */
|
||||
} Shake_Effect;
|
||||
|
||||
/** \fn Shake_Status Shake_Init(void)
|
||||
\brief Initializes libShake.
|
||||
\return On success, SHAKE_OK is returned.
|
||||
\return On error, SHAKE_ERROR is returned.
|
||||
*/
|
||||
Shake_Status Shake_Init(void);
|
||||
|
||||
/** \fn void Shake_Quit(void)
|
||||
\brief Uninitializes libShake.
|
||||
*/
|
||||
void Shake_Quit(void);
|
||||
|
||||
/** \fn int Shake_NumOfDevices(void)
|
||||
\brief Lists the number of haptic devices.
|
||||
\return On success, number of devices is returned.
|
||||
\return On error, SHAKE_ERROR is returned.
|
||||
*/
|
||||
int Shake_NumOfDevices(void);
|
||||
|
||||
/** \fn Shake_Device *Shake_Open(unsigned int id)
|
||||
\brief Opens a Shake device.
|
||||
\param id Device id.
|
||||
\return On success, pointer to Shake device is returned.
|
||||
\return On error, NULL is returned.
|
||||
*/
|
||||
Shake_Device *Shake_Open(unsigned int id);
|
||||
|
||||
/** \fn Shake_Status Shake_Close(Shake_Device *dev)
|
||||
\brief Closes a Shake device.
|
||||
\param dev Pointer to Shake device.
|
||||
\return On success, SHAKE_OK is returned.
|
||||
\return On error, SHAKE_ERROR is returned.
|
||||
*/
|
||||
Shake_Status Shake_Close(Shake_Device *dev);
|
||||
|
||||
/** \fn int Shake_DeviceId(Shake_Device *dev)
|
||||
\brief Lists id of a Shake device.
|
||||
\param dev Pointer to Shake device.
|
||||
\return On success, id of Shake device is returned.
|
||||
\return On error, SHAKE_ERROR is returned.
|
||||
*/
|
||||
int Shake_DeviceId(Shake_Device *dev);
|
||||
|
||||
/** \fn const char *Shake_DeviceName(Shake_Device *dev)
|
||||
\brief Lists name of a Shake device.
|
||||
\param dev Pointer to Shake device.
|
||||
\return On success, name of Shake device is returned.
|
||||
\return On error, NULL is returned.
|
||||
*/
|
||||
const char *Shake_DeviceName(Shake_Device *dev);
|
||||
|
||||
/** \fn int Shake_DeviceEffectCapacity(Shake_Device *dev)
|
||||
\brief Lists effect capacity of a Shake device.
|
||||
\param dev Pointer to Shake device.
|
||||
\return On success, capacity of Shake device is returned.
|
||||
\return On error, SHAKE_ERROR is returned.
|
||||
*/
|
||||
int Shake_DeviceEffectCapacity(Shake_Device *dev);
|
||||
|
||||
/** \fn Shake_Bool Shake_QueryEffectSupport(Shake_Device *dev, Shake_EffectType type)
|
||||
\brief Queries effect support of a Shake device.
|
||||
\param dev Pointer to Shake device.
|
||||
\param type Effect type to query about.
|
||||
\return SHAKE_TRUE if effect is supported.
|
||||
\return SHAKE_FALSE if effect is not supported.
|
||||
*/
|
||||
Shake_Bool Shake_QueryEffectSupport(Shake_Device *dev, Shake_EffectType type);
|
||||
|
||||
/** \fn Shake_Bool Shake_QueryWaveformSupport(Shake_Device *dev, Shake_PeriodicWaveform waveform)
|
||||
\brief Queries waveform support of a Shake device.
|
||||
\param dev Pointer to Shake device.
|
||||
\param waveform Waveform type to query about.
|
||||
\return SHAKE_TRUE if waveform is supported.
|
||||
\return SHAKE_FALSE if waveform is not supported.
|
||||
*/
|
||||
Shake_Bool Shake_QueryWaveformSupport(Shake_Device *dev, Shake_PeriodicWaveform waveform);
|
||||
|
||||
/** \fn Shake_Bool Shake_QueryGainSupport(Shake_Device *dev)
|
||||
\brief Queries gain adjustment support of a Shake device.
|
||||
\param dev Pointer to Shake device.
|
||||
\return SHAKE_TRUE if gain adjustment is supported.
|
||||
\return SHAKE_FALSE if gain adjustment is not supported.
|
||||
*/
|
||||
Shake_Bool Shake_QueryGainSupport(Shake_Device *dev);
|
||||
|
||||
/** \fn Shake_Bool Shake_QueryAutocenterSupport(Shake_Device *dev)
|
||||
\brief Queries autocenter adjustment support of a Shake device.
|
||||
\param dev Pointer to Shake device.
|
||||
\return SHAKE_TRUE if autocenter adjustment is supported.
|
||||
\return SHAKE_FALSE if autocenter adjustment is not supported.
|
||||
*/
|
||||
Shake_Bool Shake_QueryAutocenterSupport(Shake_Device *dev);
|
||||
|
||||
/** \fn Shake_Status Shake_SetGain(Shake_Device *dev, int gain)
|
||||
\brief Sets gain of a Shake device.
|
||||
\param dev Pointer to Shake device.
|
||||
\param gain [0-100] Value of a new gain level. Value of 100 means full strength of the device.
|
||||
\return On success, SHAKE_OK is returned.
|
||||
\return On error, SHAKE_ERROR is returned.
|
||||
*/
|
||||
Shake_Status Shake_SetGain(Shake_Device *dev, int gain);
|
||||
|
||||
/** \fn Shake_Status Shake_SetAutocenter(Shake_Device *dev, int autocenter)
|
||||
\brief Sets autocenter of a Shake device.
|
||||
\param dev Pointer to Shake device.
|
||||
\param autocenter [0-100] Value of a new autocenter level. Value of 0 means "autocenter disabled".
|
||||
\return On success, SHAKE_OK is returned.
|
||||
\return On error, SHAKE_ERROR is returned.
|
||||
*/
|
||||
Shake_Status Shake_SetAutocenter(Shake_Device *dev, int autocenter);
|
||||
|
||||
/** \fn Shake_Status Shake_InitEffect(Shake_Effect *effect, Shake_EffectType type)
|
||||
\brief Initializes an effect.
|
||||
\param effect Pointer to Effect struct.
|
||||
\param type Type of the effect.
|
||||
\return On success, SHAKE_OK is returned.
|
||||
\return On error, SHAKE_ERROR is returned.
|
||||
*/
|
||||
Shake_Status Shake_InitEffect(Shake_Effect *effect, Shake_EffectType type);
|
||||
|
||||
/** \fn int Shake_UploadEffect(Shake_Device *dev, Shake_Effect *effect)
|
||||
\brief Uploads an effect into a Shake device.
|
||||
\param dev Pointer to Shake device.
|
||||
\param effect Pointer to Effect struct.
|
||||
\return On success, id of the uploaded effect returned.
|
||||
\return On error, SHAKE_ERROR is returned.
|
||||
*/
|
||||
int Shake_UploadEffect(Shake_Device *dev, Shake_Effect *effect);
|
||||
|
||||
/** \fn Shake_Status Shake_EraseEffect(Shake_Device *dev, int id)
|
||||
\brief Erases effect from a Shake device.
|
||||
\param dev Pointer to Shake device.
|
||||
\param id Id of the effect to erase.
|
||||
\return On success, SHAKE_OK is returned.
|
||||
\return On error, SHAKE_ERROR is returned.
|
||||
*/
|
||||
Shake_Status Shake_EraseEffect(Shake_Device *dev, int id);
|
||||
|
||||
/** \fn Shake_Status Shake_Play(Shake_Device *dev, int id)
|
||||
\brief Starts playback of an effect.
|
||||
\param dev Pointer to Shake device.
|
||||
\param id Id of the effect to play.
|
||||
\return On success, SHAKE_OK is returned.
|
||||
\return On error, SHAKE_ERROR is returned.
|
||||
*/
|
||||
Shake_Status Shake_Play(Shake_Device *dev, int id);
|
||||
|
||||
/** \fn Shake_Status Shake_Stop(Shake_Device *dev, int id)
|
||||
\brief Stops playback of an effect.
|
||||
\param dev Pointer to Shake device.
|
||||
\param id Id of the effect to stop.
|
||||
\return On success, SHAKE_OK is returned.
|
||||
\return On error, SHAKE_ERROR is returned.
|
||||
*/
|
||||
Shake_Status Shake_Stop(Shake_Device *dev, int id);
|
||||
|
||||
/** \fn void Shake_SimpleRumble(Shake_Effect *effect, float strongPercent, float weakPercent, float secs)
|
||||
\brief Creates a simple Rumble effect.
|
||||
\param effect Pointer to Effect struct.
|
||||
\param strongPercent [0.0-1.0] Percentage of the strong motor magnitude.
|
||||
\param weakPercent [0.0-1.0] Percentage of the weak motor magnitude.
|
||||
\param secs Duration of the effect (in sec).
|
||||
|
||||
\sa Shake_Effect
|
||||
\sa Shake_EffectRumble
|
||||
*/
|
||||
void Shake_SimpleRumble(Shake_Effect *effect, float strongPercent, float weakPercent, float secs);
|
||||
|
||||
/** \fn Shake_SimplePeriodic(Shake_Effect *effect, Shake_PeriodicWaveform waveform, float forcePercent, float attackSecs, float sustainSecs, float fadeSecs)
|
||||
\brief Creates a simple Periodic effect.
|
||||
\param effect Pointer to Effect struct.
|
||||
\param waveform Waveform type.
|
||||
\param forcePercent [0.0-1.0] Percentage of the effect magnitude.
|
||||
\param attackSecs Duration of the effect attack (in sec).
|
||||
\param sustainSecs Duration of the effect sustain (in sec).
|
||||
\param fadeSecs Duration of the effect fade (in sec).
|
||||
|
||||
\sa Shake_Effect
|
||||
\sa Shake_EffectPeriodic
|
||||
*/
|
||||
void Shake_SimplePeriodic(Shake_Effect *effect, Shake_PeriodicWaveform waveform, float forcePercent, float attackSecs, float sustainSecs, float fadeSecs);
|
||||
|
||||
/** \fn void Shake_SimpleConstant(Shake_Effect *effect, float forcePercent, float attackSecs, float sustainSecs, float fadeSecs)
|
||||
\brief Creates a simple Constant effect.
|
||||
\param effect Pointer to Effect struct.
|
||||
\param forcePercent [0.0-1.0] Percentage of the effect magnitude.
|
||||
\param attackSecs Duration of the effect attack (in sec).
|
||||
\param sustainSecs Duration of the effect sustain (in sec).
|
||||
\param fadeSecs Duration of the effect fade (in sec).
|
||||
|
||||
\sa Shake_Effect
|
||||
\sa Shake_EffectConstant
|
||||
*/
|
||||
void Shake_SimpleConstant(Shake_Effect *effect, float forcePercent, float attackSecs, float sustainSecs, float fadeSecs);
|
||||
|
||||
/** \fn Shake_SimpleRamp(Shake_Effect *effect, float startForcePercent, float endForcePercent, float attackSecs, float sustainSecs, float fadeSecs)
|
||||
\brief Creates a simple Ramp effect.
|
||||
\param effect Pointer to Effect struct.
|
||||
\param startForcePercent [0.0-1.0] Percentage of the effect magnitude at start.
|
||||
\param endForcePercent [0.0-1.0] Percentage of the effect magnitude at end.
|
||||
\param attackSecs Duration of the effect attack (in sec).
|
||||
\param sustainSecs Duration of the effect sustain (in sec).
|
||||
\param fadeSecs Duration of the effect fade (in sec).
|
||||
|
||||
\sa Shake_Effect
|
||||
\sa Shake_EffectRamp
|
||||
*/
|
||||
void Shake_SimpleRamp(Shake_Effect *effect, float startForcePercent, float endForcePercent, float attackSecs, float sustainSecs, float fadeSecs);
|
||||
|
||||
/** \fn Shake_ErrorCode Shake_GetErrorCode(void)
|
||||
\brief Informs about the error type.
|
||||
\return Error code reflecting the last error type.
|
||||
*/
|
||||
Shake_ErrorCode Shake_GetErrorCode(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _SHAKE_H_ */
|
14
deps/libShake/src/common/error.c
vendored
Normal file
14
deps/libShake/src/common/error.c
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
#include "error.h"
|
||||
|
||||
Shake_ErrorCode errorCode = SHAKE_EC_UNSET;
|
||||
|
||||
Shake_Status Shake_EmitErrorCode(Shake_ErrorCode ec)
|
||||
{
|
||||
errorCode = ec;
|
||||
return SHAKE_ERROR;
|
||||
}
|
||||
|
||||
Shake_ErrorCode Shake_GetErrorCode(void)
|
||||
{
|
||||
return errorCode;
|
||||
}
|
9
deps/libShake/src/common/error.h
vendored
Normal file
9
deps/libShake/src/common/error.h
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
#ifndef _SHAKE_ERROR_H_
|
||||
#define _SHAKE_ERROR_H_
|
||||
|
||||
#include "shake.h"
|
||||
|
||||
Shake_Status Shake_EmitErrorCode(Shake_ErrorCode ec);
|
||||
Shake_ErrorCode Shake_GetErrorCode(void);
|
||||
|
||||
#endif
|
93
deps/libShake/src/common/helpers.c
vendored
Normal file
93
deps/libShake/src/common/helpers.c
vendored
Normal file
@ -0,0 +1,93 @@
|
||||
#include "helpers.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
/* Linked list */
|
||||
|
||||
ListElement *listElementPrepend(ListElement *head)
|
||||
{
|
||||
ListElement *newNode = malloc(sizeof(ListElement));
|
||||
if(!newNode)
|
||||
{
|
||||
return head;
|
||||
}
|
||||
|
||||
newNode->next = head;
|
||||
return newNode;
|
||||
|
||||
}
|
||||
|
||||
ListElement *listElementDelete(ListElement *head, ListElement *toDelNode, void(*itemDel)(void *item))
|
||||
{
|
||||
ListElement *prevNode = NULL;
|
||||
ListElement *curNode = head;
|
||||
|
||||
while(curNode)
|
||||
{
|
||||
if(curNode == toDelNode)
|
||||
{
|
||||
if(!prevNode)
|
||||
{
|
||||
head = curNode->next;
|
||||
}
|
||||
else
|
||||
{
|
||||
prevNode->next = curNode->next;
|
||||
}
|
||||
|
||||
itemDel(curNode->item);
|
||||
free(curNode);
|
||||
return head;
|
||||
}
|
||||
prevNode = curNode;
|
||||
curNode = curNode->next;
|
||||
}
|
||||
|
||||
return head;
|
||||
}
|
||||
|
||||
ListElement *listElementDeleteAll(ListElement *head, void(*itemDel)(void *item))
|
||||
{
|
||||
ListElement *curNode = head;
|
||||
|
||||
while(curNode)
|
||||
{
|
||||
ListElement *toDelNode = curNode;
|
||||
curNode = curNode->next;
|
||||
|
||||
itemDel(toDelNode->item);
|
||||
free(toDelNode);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ListElement *listElementGet(ListElement *head, unsigned int id)
|
||||
{
|
||||
ListElement *curNode = head;
|
||||
int i = 0;
|
||||
|
||||
while(curNode)
|
||||
{
|
||||
if (i == id)
|
||||
return curNode;
|
||||
|
||||
curNode = curNode->next;
|
||||
++i;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
unsigned int listLength(ListElement *head)
|
||||
{
|
||||
ListElement *curNode = head;
|
||||
unsigned int count = 0;
|
||||
|
||||
while (curNode)
|
||||
{
|
||||
curNode = curNode->next;
|
||||
++count;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
16
deps/libShake/src/common/helpers.h
vendored
Normal file
16
deps/libShake/src/common/helpers.h
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
#ifndef _HELPERS_H_
|
||||
#define _HELPERS_H_
|
||||
|
||||
typedef struct ListElement
|
||||
{
|
||||
struct ListElement *next;
|
||||
void *item;
|
||||
} ListElement;
|
||||
|
||||
ListElement *listElementPrepend(ListElement *head);
|
||||
ListElement *listElementDelete(ListElement *head, ListElement *toDelNode, void(*itemDel)());
|
||||
ListElement *listElementDeleteAll(ListElement *head, void(*itemDel)(void *item));
|
||||
ListElement *listElementGet(ListElement *head, unsigned int id);
|
||||
unsigned int listLength(ListElement *head);
|
||||
|
||||
#endif /* _HELPERS_H_ */
|
50
deps/libShake/src/common/presets.c
vendored
Normal file
50
deps/libShake/src/common/presets.c
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
#include "shake.h"
|
||||
#include "helpers.h"
|
||||
|
||||
void Shake_SimpleRumble(Shake_Effect *effect, float strongPercent, float weakPercent, float secs)
|
||||
{
|
||||
Shake_InitEffect(effect, SHAKE_EFFECT_RUMBLE);
|
||||
effect->u.rumble.strongMagnitude = SHAKE_RUMBLE_STRONG_MAGNITUDE_MAX * strongPercent;
|
||||
effect->u.rumble.weakMagnitude = SHAKE_RUMBLE_WEAK_MAGNITUDE_MAX * weakPercent;
|
||||
effect->length = 1000 * secs;
|
||||
effect->delay = 0;
|
||||
}
|
||||
|
||||
void Shake_SimplePeriodic(Shake_Effect *effect, Shake_PeriodicWaveform waveform, float forcePercent, float attackSecs, float sustainSecs, float fadeSecs)
|
||||
{
|
||||
Shake_InitEffect(effect, SHAKE_EFFECT_PERIODIC);
|
||||
effect->u.periodic.waveform = waveform;
|
||||
effect->u.periodic.period = 0.1*0x100;
|
||||
effect->u.periodic.magnitude = SHAKE_PERIODIC_MAGNITUDE_MAX * forcePercent;
|
||||
effect->u.periodic.envelope.attackLength = 1000 * attackSecs;
|
||||
effect->u.periodic.envelope.attackLevel = 0;
|
||||
effect->u.periodic.envelope.fadeLength = 1000 * fadeSecs;
|
||||
effect->u.periodic.envelope.fadeLevel = 0;
|
||||
effect->length = 1000 * (sustainSecs + attackSecs + fadeSecs);
|
||||
effect->delay = 0;
|
||||
}
|
||||
|
||||
void Shake_SimpleConstant(Shake_Effect *effect, float forcePercent, float attackSecs, float sustainSecs, float fadeSecs)
|
||||
{
|
||||
Shake_InitEffect(effect, SHAKE_EFFECT_CONSTANT);
|
||||
effect->u.constant.level = SHAKE_CONSTANT_LEVEL_MAX * forcePercent;
|
||||
effect->u.constant.envelope.attackLength = 1000 * attackSecs;
|
||||
effect->u.constant.envelope.attackLevel = 0;
|
||||
effect->u.constant.envelope.fadeLength = 1000 * fadeSecs;
|
||||
effect->u.constant.envelope.fadeLevel = 0;
|
||||
effect->length = 1000 * (sustainSecs + attackSecs + fadeSecs);
|
||||
effect->delay = 0;
|
||||
}
|
||||
|
||||
void Shake_SimpleRamp(Shake_Effect *effect, float startForcePercent, float endForcePercent, float attackSecs, float sustainSecs, float fadeSecs)
|
||||
{
|
||||
Shake_InitEffect(effect, SHAKE_EFFECT_RAMP);
|
||||
effect->u.ramp.startLevel = SHAKE_RAMP_START_LEVEL_MAX * startForcePercent;
|
||||
effect->u.ramp.endLevel = SHAKE_RAMP_END_LEVEL_MAX * endForcePercent;
|
||||
effect->u.ramp.envelope.attackLength = 1000 * attackSecs;
|
||||
effect->u.ramp.envelope.attackLevel = 0;
|
||||
effect->u.ramp.envelope.fadeLength = 1000 * fadeSecs;
|
||||
effect->u.ramp.envelope.fadeLevel = 0;
|
||||
effect->length = 1000 * (sustainSecs + attackSecs + fadeSecs);
|
||||
effect->delay = 0;
|
||||
}
|
422
deps/libShake/src/linux/shake.c
vendored
Normal file
422
deps/libShake/src/linux/shake.c
vendored
Normal file
@ -0,0 +1,422 @@
|
||||
/* libShake - a basic haptic library */
|
||||
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/input.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "shake.h"
|
||||
#include "shake_private.h"
|
||||
#include "../common/helpers.h"
|
||||
#include "../common/error.h"
|
||||
|
||||
#define SHAKE_TEST(x) ((x) ? SHAKE_TRUE : SHAKE_FALSE)
|
||||
|
||||
static ListElement *listHead;
|
||||
static unsigned int numOfDevices;
|
||||
|
||||
static int nameFilter(const struct dirent *entry)
|
||||
{
|
||||
const char filter[] = "event";
|
||||
return !strncmp(filter, entry->d_name, strlen(filter));
|
||||
}
|
||||
|
||||
static void itemDelete(void *item)
|
||||
{
|
||||
Shake_Device *dev = (Shake_Device *)item;
|
||||
|
||||
if (!dev)
|
||||
return;
|
||||
|
||||
Shake_Close(dev);
|
||||
|
||||
free(dev->node);
|
||||
free(dev);
|
||||
}
|
||||
|
||||
static Shake_Status query(Shake_Device *dev)
|
||||
{
|
||||
int size = sizeof(dev->features)/sizeof(unsigned long);
|
||||
int i;
|
||||
|
||||
if(!dev)
|
||||
return Shake_EmitErrorCode(SHAKE_EC_ARG);
|
||||
|
||||
if (ioctl(dev->fd, EVIOCGBIT(EV_FF, sizeof(dev->features)), dev->features) == -1)
|
||||
return Shake_EmitErrorCode(SHAKE_EC_QUERY);
|
||||
|
||||
for (i = 0; i < size; ++i)
|
||||
{
|
||||
if (dev->features[i])
|
||||
break;
|
||||
}
|
||||
|
||||
if (i >= size) /* Device doesn't support any force feedback effects. Ignore it. */
|
||||
return Shake_EmitErrorCode(SHAKE_EC_DEVICE);
|
||||
|
||||
if (ioctl(dev->fd, EVIOCGEFFECTS, &dev->capacity) == -1)
|
||||
return Shake_EmitErrorCode(SHAKE_EC_QUERY);
|
||||
|
||||
if (dev->capacity <= 0) /* Device doesn't support uploading effects. Ignore it. */
|
||||
return Shake_EmitErrorCode(SHAKE_EC_DEVICE);
|
||||
|
||||
if (ioctl(dev->fd, EVIOCGNAME(sizeof(dev->name)), dev->name) == -1) /* Get device name */
|
||||
{
|
||||
strncpy(dev->name, "Unknown", sizeof(dev->name));
|
||||
}
|
||||
|
||||
return SHAKE_OK;
|
||||
}
|
||||
|
||||
static Shake_Status probe(Shake_Device *dev)
|
||||
{
|
||||
int isHaptic;
|
||||
|
||||
if(!dev)
|
||||
return Shake_EmitErrorCode(SHAKE_EC_ARG);
|
||||
if (!dev->node)
|
||||
return Shake_EmitErrorCode(SHAKE_EC_DEVICE);
|
||||
|
||||
dev->fd = open(dev->node, O_RDWR);
|
||||
|
||||
if (!dev->fd)
|
||||
return SHAKE_ERROR;
|
||||
|
||||
isHaptic = !query(dev);
|
||||
dev->fd = close(dev->fd);
|
||||
|
||||
return isHaptic ? SHAKE_OK : SHAKE_ERROR;
|
||||
}
|
||||
|
||||
/* API implementation. */
|
||||
|
||||
Shake_Status Shake_Init(void)
|
||||
{
|
||||
struct dirent **nameList;
|
||||
int numOfEntries;
|
||||
|
||||
numOfDevices = 0;
|
||||
|
||||
numOfEntries = scandir(SHAKE_DIR_NODES, &nameList, nameFilter, alphasort);
|
||||
|
||||
if (numOfEntries < 0)
|
||||
{
|
||||
return Shake_EmitErrorCode(SHAKE_EC_DEVICE);
|
||||
}
|
||||
else
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < numOfEntries; ++i)
|
||||
{
|
||||
Shake_Device dev;
|
||||
|
||||
dev.node = malloc(strlen(SHAKE_DIR_NODES) + strlen("/") + strlen(nameList[i]->d_name) + 1);
|
||||
if (dev.node == NULL)
|
||||
{
|
||||
return Shake_EmitErrorCode(SHAKE_EC_DEVICE);
|
||||
}
|
||||
|
||||
strcpy(dev.node, SHAKE_DIR_NODES);
|
||||
strcat(dev.node, "/");
|
||||
strcat(dev.node, nameList[i]->d_name);
|
||||
|
||||
if (probe(&dev) == SHAKE_OK)
|
||||
{
|
||||
dev.id = numOfDevices;
|
||||
listHead = listElementPrepend(listHead);
|
||||
listHead->item = malloc(sizeof(Shake_Device));
|
||||
memcpy(listHead->item, &dev, sizeof(Shake_Device));
|
||||
++numOfDevices;
|
||||
}
|
||||
else
|
||||
{
|
||||
free(dev.node);
|
||||
}
|
||||
|
||||
free(nameList[i]);
|
||||
}
|
||||
|
||||
free(nameList);
|
||||
}
|
||||
|
||||
return SHAKE_OK;
|
||||
}
|
||||
|
||||
void Shake_Quit(void)
|
||||
{
|
||||
if (listHead != NULL)
|
||||
{
|
||||
listElementDeleteAll(listHead, itemDelete);
|
||||
}
|
||||
|
||||
listHead = NULL;
|
||||
numOfDevices = 0;
|
||||
}
|
||||
|
||||
int Shake_NumOfDevices(void)
|
||||
{
|
||||
return numOfDevices;
|
||||
}
|
||||
|
||||
Shake_Device *Shake_Open(unsigned int id)
|
||||
{
|
||||
Shake_Device *dev;
|
||||
ListElement *element;
|
||||
|
||||
if (id >= numOfDevices)
|
||||
{
|
||||
Shake_EmitErrorCode(SHAKE_EC_ARG);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
element = listElementGet(listHead, numOfDevices - 1 - id);
|
||||
dev = (Shake_Device *)element->item;
|
||||
|
||||
if(!dev || !dev->node)
|
||||
{
|
||||
Shake_EmitErrorCode(SHAKE_EC_DEVICE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dev->fd = open(dev->node, O_RDWR);
|
||||
if (!dev->fd)
|
||||
Shake_EmitErrorCode(SHAKE_EC_DEVICE);
|
||||
|
||||
return dev->fd ? dev : NULL;
|
||||
}
|
||||
|
||||
int Shake_DeviceId(Shake_Device *dev)
|
||||
{
|
||||
if (!dev)
|
||||
Shake_EmitErrorCode(SHAKE_EC_ARG);
|
||||
|
||||
return dev ? dev->id : SHAKE_ERROR;
|
||||
}
|
||||
|
||||
const char *Shake_DeviceName(Shake_Device *dev)
|
||||
{
|
||||
if (!dev)
|
||||
Shake_EmitErrorCode(SHAKE_EC_ARG);
|
||||
|
||||
return dev ? dev->name : NULL;
|
||||
}
|
||||
|
||||
int Shake_DeviceEffectCapacity(Shake_Device *dev)
|
||||
{
|
||||
if (!dev)
|
||||
Shake_EmitErrorCode(SHAKE_EC_ARG);
|
||||
|
||||
return dev ? dev->capacity : SHAKE_ERROR;
|
||||
}
|
||||
|
||||
Shake_Bool Shake_QueryEffectSupport(Shake_Device *dev, Shake_EffectType type)
|
||||
{
|
||||
/* Starts at a magic, non-zero number, FF_RUMBLE.
|
||||
Increments respectively to EffectType. */
|
||||
return SHAKE_TEST(test_bit(FF_RUMBLE + type, dev->features));
|
||||
}
|
||||
|
||||
Shake_Bool Shake_QueryWaveformSupport(Shake_Device *dev, Shake_PeriodicWaveform waveform)
|
||||
{
|
||||
/* Starts at a magic, non-zero number, FF_SQUARE.
|
||||
Increments respectively to PeriodicWaveform. */
|
||||
return SHAKE_TEST(test_bit(FF_SQUARE + waveform, dev->features));
|
||||
}
|
||||
|
||||
Shake_Bool Shake_QueryGainSupport(Shake_Device *dev)
|
||||
{
|
||||
return SHAKE_TEST(test_bit(FF_GAIN, dev->features));
|
||||
}
|
||||
|
||||
Shake_Bool Shake_QueryAutocenterSupport(Shake_Device *dev)
|
||||
{
|
||||
return SHAKE_TEST(test_bit(FF_AUTOCENTER, dev->features));
|
||||
}
|
||||
|
||||
Shake_Status Shake_SetGain(Shake_Device *dev, int gain)
|
||||
{
|
||||
struct input_event ie;
|
||||
|
||||
if (!dev)
|
||||
return Shake_EmitErrorCode(SHAKE_EC_ARG);
|
||||
|
||||
if (gain < 0)
|
||||
gain = 0;
|
||||
if (gain > 100)
|
||||
gain = 100;
|
||||
|
||||
ie.type = EV_FF;
|
||||
ie.code = FF_GAIN;
|
||||
ie.value = 0xFFFFUL * gain / 100;
|
||||
|
||||
if (write(dev->fd, &ie, sizeof(ie)) == -1)
|
||||
return Shake_EmitErrorCode(SHAKE_EC_TRANSFER);
|
||||
|
||||
return SHAKE_OK;
|
||||
}
|
||||
|
||||
Shake_Status Shake_SetAutocenter(Shake_Device *dev, int autocenter)
|
||||
{
|
||||
struct input_event ie;
|
||||
|
||||
if (!dev)
|
||||
return Shake_EmitErrorCode(SHAKE_EC_ARG);
|
||||
|
||||
if (autocenter < 0)
|
||||
autocenter = 0;
|
||||
if (autocenter > 100)
|
||||
autocenter = 100;
|
||||
|
||||
ie.type = EV_FF;
|
||||
ie.code = FF_AUTOCENTER;
|
||||
ie.value = 0xFFFFUL * autocenter / 100;
|
||||
|
||||
if (write(dev->fd, &ie, sizeof(ie)) == -1)
|
||||
return Shake_EmitErrorCode(SHAKE_EC_TRANSFER);
|
||||
|
||||
return SHAKE_OK;
|
||||
}
|
||||
|
||||
Shake_Status Shake_InitEffect(Shake_Effect *effect, Shake_EffectType type)
|
||||
{
|
||||
if (!effect || type >= SHAKE_EFFECT_COUNT)
|
||||
return Shake_EmitErrorCode(SHAKE_EC_ARG);
|
||||
|
||||
memset(effect, 0, sizeof(*effect));
|
||||
effect->type = type;
|
||||
effect->id = -1;
|
||||
|
||||
return SHAKE_OK;
|
||||
}
|
||||
|
||||
int Shake_UploadEffect(Shake_Device *dev, Shake_Effect *effect)
|
||||
{
|
||||
struct ff_effect e;
|
||||
|
||||
if (!dev || !effect || effect->id < -1)
|
||||
return Shake_EmitErrorCode(SHAKE_EC_ARG);
|
||||
|
||||
/* Common parameters. */
|
||||
e.id = effect->id;
|
||||
e.direction = effect->direction;
|
||||
e.trigger.button = 0;
|
||||
e.trigger.interval = 0;
|
||||
e.replay.delay = effect->delay;
|
||||
e.replay.length = effect->length;
|
||||
|
||||
/* Effect type specific parameters. */
|
||||
if(effect->type == SHAKE_EFFECT_RUMBLE)
|
||||
{
|
||||
e.type = FF_RUMBLE;
|
||||
e.u.rumble.strong_magnitude = effect->u.rumble.strongMagnitude;
|
||||
e.u.rumble.weak_magnitude = effect->u.rumble.weakMagnitude;
|
||||
}
|
||||
else if(effect->type == SHAKE_EFFECT_PERIODIC)
|
||||
{
|
||||
e.type = FF_PERIODIC;
|
||||
e.u.periodic.waveform = FF_SQUARE + effect->u.periodic.waveform;
|
||||
e.u.periodic.period = effect->u.periodic.period;
|
||||
e.u.periodic.magnitude = effect->u.periodic.magnitude;
|
||||
e.u.periodic.offset = effect->u.periodic.offset;
|
||||
e.u.periodic.phase = effect->u.periodic.phase;
|
||||
e.u.periodic.envelope.attack_length = effect->u.periodic.envelope.attackLength;
|
||||
e.u.periodic.envelope.attack_level = effect->u.periodic.envelope.attackLevel;
|
||||
e.u.periodic.envelope.fade_length = effect->u.periodic.envelope.fadeLength;
|
||||
e.u.periodic.envelope.fade_level = effect->u.periodic.envelope.fadeLevel;
|
||||
}
|
||||
else if(effect->type == SHAKE_EFFECT_CONSTANT)
|
||||
{
|
||||
e.type = FF_CONSTANT;
|
||||
e.u.constant.level = effect->u.constant.level;
|
||||
e.u.constant.envelope.attack_length = effect->u.constant.envelope.attackLength;
|
||||
e.u.constant.envelope.attack_level = effect->u.constant.envelope.attackLevel;
|
||||
e.u.constant.envelope.fade_length = effect->u.constant.envelope.fadeLength;
|
||||
e.u.constant.envelope.fade_level = effect->u.constant.envelope.fadeLevel;
|
||||
}
|
||||
else if(effect->type == SHAKE_EFFECT_RAMP)
|
||||
{
|
||||
e.type = FF_RAMP;
|
||||
e.u.ramp.start_level = effect->u.ramp.startLevel;
|
||||
e.u.ramp.end_level = effect->u.ramp.endLevel;
|
||||
e.u.ramp.envelope.attack_length = effect->u.ramp.envelope.attackLength;
|
||||
e.u.ramp.envelope.attack_level = effect->u.ramp.envelope.attackLevel;
|
||||
e.u.ramp.envelope.fade_length = effect->u.ramp.envelope.fadeLength;
|
||||
e.u.ramp.envelope.fade_level = effect->u.ramp.envelope.fadeLevel;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (effect->type >= SHAKE_EFFECT_COUNT ? Shake_EmitErrorCode(SHAKE_EC_ARG) : Shake_EmitErrorCode(SHAKE_EC_SUPPORT));
|
||||
}
|
||||
|
||||
if (ioctl(dev->fd, EVIOCSFF, &e) == -1)
|
||||
{
|
||||
return Shake_EmitErrorCode(SHAKE_EC_TRANSFER);
|
||||
}
|
||||
|
||||
return e.id;
|
||||
}
|
||||
|
||||
Shake_Status Shake_EraseEffect(Shake_Device *dev, int id)
|
||||
{
|
||||
if (!dev || id < 0)
|
||||
return Shake_EmitErrorCode(SHAKE_EC_ARG);
|
||||
|
||||
if (ioctl(dev->fd, EVIOCRMFF, id) == -1)
|
||||
{
|
||||
return Shake_EmitErrorCode(SHAKE_EC_TRANSFER);
|
||||
}
|
||||
|
||||
return SHAKE_OK;
|
||||
}
|
||||
|
||||
Shake_Status Shake_Play(Shake_Device *dev, int id)
|
||||
{
|
||||
struct input_event play;
|
||||
if (!dev || id < 0)
|
||||
return Shake_EmitErrorCode(SHAKE_EC_ARG);
|
||||
|
||||
play.type = EV_FF;
|
||||
play.code = id; /* the id we got when uploading the effect */
|
||||
play.value = FF_STATUS_PLAYING; /* play: FF_STATUS_PLAYING, stop: FF_STATUS_STOPPED */
|
||||
|
||||
if (write(dev->fd, (const void*) &play, sizeof(play)) == -1)
|
||||
return Shake_EmitErrorCode(SHAKE_EC_TRANSFER);
|
||||
|
||||
return SHAKE_OK;
|
||||
}
|
||||
|
||||
Shake_Status Shake_Stop(Shake_Device *dev, int id)
|
||||
{
|
||||
struct input_event stop;
|
||||
if (!dev || id < 0)
|
||||
return Shake_EmitErrorCode(SHAKE_EC_ARG);
|
||||
|
||||
stop.type = EV_FF;
|
||||
stop.code = id; /* the id we got when uploading the effect */
|
||||
stop.value = FF_STATUS_STOPPED;
|
||||
|
||||
if (write(dev->fd, (const void*) &stop, sizeof(stop)) == -1)
|
||||
return Shake_EmitErrorCode(SHAKE_EC_TRANSFER);
|
||||
|
||||
return SHAKE_OK;
|
||||
}
|
||||
|
||||
Shake_Status Shake_Close(Shake_Device *dev)
|
||||
{
|
||||
if (!dev)
|
||||
return Shake_EmitErrorCode(SHAKE_EC_ARG);
|
||||
|
||||
close(dev->fd);
|
||||
|
||||
return SHAKE_OK;
|
||||
}
|
30
deps/libShake/src/linux/shake_private.h
vendored
Normal file
30
deps/libShake/src/linux/shake_private.h
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
#ifndef _SHAKE_PRIVATE_H_
|
||||
#define _SHAKE_PRIVATE_H_
|
||||
|
||||
#include <dirent.h>
|
||||
#include <linux/input.h>
|
||||
#include "../common/helpers.h"
|
||||
|
||||
#define SHAKE_DIR_NODES "/dev/input"
|
||||
|
||||
#define BITS_PER_LONG (sizeof(long) * 8)
|
||||
#define OFF(x) ((x)%BITS_PER_LONG)
|
||||
#define BIT(x) (1UL<<OFF(x))
|
||||
#define LONG(x) ((x)/BITS_PER_LONG)
|
||||
#define test_bit(bit, array) ((array[LONG(bit)] >> OFF(bit)) & 1)
|
||||
#define BITS_TO_LONGS(x) \
|
||||
(((x) + 8 * sizeof (unsigned long) - 1) / (8 * sizeof (unsigned long)))
|
||||
|
||||
struct Shake_Device
|
||||
{
|
||||
char name[128];
|
||||
int id;
|
||||
int capacity; /* Number of effects the device can play at the same time */
|
||||
/* Platform dependent section */
|
||||
int fd;
|
||||
char *node;
|
||||
unsigned long features[BITS_TO_LONGS(FF_CNT)];
|
||||
|
||||
};
|
||||
|
||||
#endif /* _SHAKE_PRIVATE_H_ */
|
719
deps/libShake/src/osx/shake.c
vendored
Normal file
719
deps/libShake/src/osx/shake.c
vendored
Normal file
@ -0,0 +1,719 @@
|
||||
/* libShake - a basic haptic library */
|
||||
|
||||
#include <ForceFeedback/ForceFeedback.h>
|
||||
#include <ForceFeedback/ForceFeedbackConstants.h>
|
||||
#include <IOKit/IOKitLib.h>
|
||||
#include <IOKit/hid/IOHIDLib.h>
|
||||
#include <IOKit/hid/IOHIDKeys.h>
|
||||
|
||||
#include "shake.h"
|
||||
#include "./shake_private.h"
|
||||
#include "../common/helpers.h"
|
||||
#include "../common/error.h"
|
||||
|
||||
static ListElement *listHead;
|
||||
static unsigned int numOfDevices;
|
||||
|
||||
static int convertMagnitude(int magnitude)
|
||||
{
|
||||
return ((float)magnitude/0x7FFF) * FF_FFNOMINALMAX;
|
||||
}
|
||||
|
||||
static void devItemDelete(void *item)
|
||||
{
|
||||
Shake_Device *dev = (Shake_Device *)item;
|
||||
|
||||
if (!dev)
|
||||
return;
|
||||
|
||||
Shake_Close(dev);
|
||||
if (dev->service)
|
||||
IOObjectRelease(dev->service);
|
||||
free(dev);
|
||||
}
|
||||
|
||||
static void effectItemDelete(void *item)
|
||||
{
|
||||
EffectContainer *effect = (EffectContainer *)item;
|
||||
free(effect);
|
||||
}
|
||||
|
||||
static Shake_Status query(Shake_Device *dev)
|
||||
{
|
||||
HRESULT result;
|
||||
io_name_t deviceName;
|
||||
|
||||
if(!dev)
|
||||
return Shake_EmitErrorCode(SHAKE_EC_ARG);
|
||||
if (!dev->service)
|
||||
return Shake_EmitErrorCode(SHAKE_EC_DEVICE);
|
||||
|
||||
result = FFCreateDevice(dev->service, &dev->device);
|
||||
if (result != FF_OK)
|
||||
{
|
||||
return Shake_EmitErrorCode(SHAKE_EC_DEVICE);
|
||||
}
|
||||
|
||||
result = FFDeviceGetForceFeedbackCapabilities(dev->device, &dev->features);
|
||||
if (result != FF_OK)
|
||||
{
|
||||
return Shake_EmitErrorCode(SHAKE_EC_QUERY);
|
||||
}
|
||||
|
||||
if (!dev->features.supportedEffects) /* Device doesn't support any force feedback effects. Ignore it. */
|
||||
{
|
||||
return Shake_EmitErrorCode(SHAKE_EC_DEVICE);
|
||||
}
|
||||
|
||||
dev->capacity = dev->features.storageCapacity;
|
||||
|
||||
if (dev->capacity <= 0) /* Device doesn't support uploading effects. Ignore it. */
|
||||
return Shake_EmitErrorCode(SHAKE_EC_DEVICE);
|
||||
|
||||
IORegistryEntryGetName(dev->service, deviceName); /* Get device name */
|
||||
if (strlen((char *)deviceName))
|
||||
{
|
||||
strncpy(dev->name, (char *)deviceName, sizeof(dev->name));
|
||||
}
|
||||
else
|
||||
{
|
||||
strncpy(dev->name, "Unknown", sizeof(dev->name));
|
||||
}
|
||||
|
||||
if (FFReleaseDevice(dev->device) == FF_OK)
|
||||
{
|
||||
dev->device = 0;
|
||||
}
|
||||
|
||||
return SHAKE_OK;
|
||||
}
|
||||
|
||||
static Shake_Status probe(Shake_Device *dev)
|
||||
{
|
||||
if ((FFIsForceFeedback(dev->service)) != FF_OK)
|
||||
{
|
||||
return SHAKE_ERROR;
|
||||
}
|
||||
|
||||
if (query(dev))
|
||||
{
|
||||
return SHAKE_ERROR;
|
||||
}
|
||||
|
||||
return SHAKE_OK;
|
||||
}
|
||||
|
||||
/* API implementation. */
|
||||
|
||||
Shake_Status Shake_Init(void)
|
||||
{
|
||||
IOReturn ret;
|
||||
io_iterator_t iter;
|
||||
CFDictionaryRef match;
|
||||
io_service_t device;
|
||||
|
||||
match = IOServiceMatching(kIOHIDDeviceKey);
|
||||
|
||||
if (!match)
|
||||
{
|
||||
return Shake_EmitErrorCode(SHAKE_EC_DEVICE);
|
||||
}
|
||||
|
||||
ret = IOServiceGetMatchingServices(kIOMasterPortDefault, match, &iter);
|
||||
|
||||
if (ret != kIOReturnSuccess)
|
||||
{
|
||||
return Shake_EmitErrorCode(SHAKE_EC_DEVICE);
|
||||
}
|
||||
|
||||
if (!IOIteratorIsValid(iter))
|
||||
{
|
||||
return Shake_EmitErrorCode(SHAKE_EC_DEVICE);
|
||||
}
|
||||
|
||||
while ((device = IOIteratorNext(iter)) != IO_OBJECT_NULL)
|
||||
{
|
||||
Shake_Device dev;
|
||||
dev.service = device;
|
||||
dev.effectList = NULL;
|
||||
|
||||
if (probe(&dev) == SHAKE_OK)
|
||||
{
|
||||
dev.id = numOfDevices;
|
||||
listHead = listElementPrepend(listHead);
|
||||
listHead->item = malloc(sizeof(Shake_Device));
|
||||
memcpy(listHead->item, &dev, sizeof(Shake_Device));
|
||||
++numOfDevices;
|
||||
}
|
||||
else
|
||||
{
|
||||
IOObjectRelease(device);
|
||||
}
|
||||
}
|
||||
|
||||
IOObjectRelease(iter);
|
||||
|
||||
return SHAKE_OK;
|
||||
}
|
||||
|
||||
void Shake_Quit(void)
|
||||
{
|
||||
if (listHead != NULL)
|
||||
{
|
||||
listElementDeleteAll(listHead, devItemDelete);
|
||||
}
|
||||
}
|
||||
|
||||
int Shake_NumOfDevices(void)
|
||||
{
|
||||
return numOfDevices;
|
||||
}
|
||||
|
||||
Shake_Device *Shake_Open(unsigned int id)
|
||||
{
|
||||
HRESULT result;
|
||||
Shake_Device *dev;
|
||||
ListElement *element;
|
||||
|
||||
if (id >= numOfDevices)
|
||||
{
|
||||
Shake_EmitErrorCode(SHAKE_EC_ARG);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
element = listElementGet(listHead, numOfDevices - 1 - id);
|
||||
dev = (Shake_Device *)element->item;
|
||||
|
||||
if(!dev || !dev->service)
|
||||
{
|
||||
Shake_EmitErrorCode(SHAKE_EC_DEVICE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
result = FFCreateDevice(dev->service, &dev->device);
|
||||
|
||||
if (result != FF_OK)
|
||||
{
|
||||
Shake_EmitErrorCode(SHAKE_EC_DEVICE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
int Shake_DeviceId(Shake_Device *dev)
|
||||
{
|
||||
if (!dev)
|
||||
Shake_EmitErrorCode(SHAKE_EC_ARG);
|
||||
|
||||
return dev ? dev->id : SHAKE_ERROR;
|
||||
}
|
||||
|
||||
const char *Shake_DeviceName(Shake_Device *dev)
|
||||
{
|
||||
if (!dev)
|
||||
Shake_EmitErrorCode(SHAKE_EC_ARG);
|
||||
|
||||
return dev ? dev->name : NULL;
|
||||
}
|
||||
|
||||
int Shake_DeviceEffectCapacity(Shake_Device *dev)
|
||||
{
|
||||
if (!dev)
|
||||
Shake_EmitErrorCode(SHAKE_EC_ARG);
|
||||
|
||||
return dev ? dev->capacity : SHAKE_ERROR;
|
||||
}
|
||||
|
||||
Shake_Bool Shake_QueryEffectSupport(Shake_Device *dev, Shake_EffectType type)
|
||||
{
|
||||
FFCapabilitiesEffectType query;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case SHAKE_EFFECT_RUMBLE:
|
||||
/* Emulate EFFECT_RUMBLE with EFFECT_PERIODIC. */
|
||||
return Shake_QueryWaveformSupport(dev, SHAKE_PERIODIC_SINE) ? SHAKE_TRUE : SHAKE_FALSE;
|
||||
break;
|
||||
case SHAKE_EFFECT_PERIODIC:
|
||||
{
|
||||
Shake_PeriodicWaveform waveform;
|
||||
|
||||
for (waveform = SHAKE_PERIODIC_SQUARE; waveform < SHAKE_PERIODIC_COUNT; ++waveform)
|
||||
{
|
||||
if (Shake_QueryWaveformSupport(dev, waveform))
|
||||
return SHAKE_TRUE;
|
||||
}
|
||||
|
||||
return SHAKE_FALSE;
|
||||
}
|
||||
break;
|
||||
case SHAKE_EFFECT_CONSTANT:
|
||||
query = FFCAP_ET_CONSTANTFORCE;
|
||||
break;
|
||||
case SHAKE_EFFECT_SPRING:
|
||||
query = FFCAP_ET_SPRING;
|
||||
break;
|
||||
case SHAKE_EFFECT_FRICTION:
|
||||
query = FFCAP_ET_FRICTION;
|
||||
break;
|
||||
case SHAKE_EFFECT_DAMPER:
|
||||
query = FFCAP_ET_DAMPER;
|
||||
break;
|
||||
case SHAKE_EFFECT_INERTIA:
|
||||
query = FFCAP_ET_INERTIA;
|
||||
break;
|
||||
case SHAKE_EFFECT_RAMP:
|
||||
query = FFCAP_ET_RAMPFORCE;
|
||||
break;
|
||||
|
||||
default:
|
||||
return SHAKE_FALSE;
|
||||
}
|
||||
|
||||
return test_bit(query, dev->features.supportedEffects) ? SHAKE_TRUE : SHAKE_FALSE;
|
||||
}
|
||||
|
||||
Shake_Bool Shake_QueryWaveformSupport(Shake_Device *dev, Shake_PeriodicWaveform waveform)
|
||||
{
|
||||
FFCapabilitiesEffectType query;
|
||||
|
||||
switch (waveform)
|
||||
{
|
||||
case SHAKE_PERIODIC_SQUARE:
|
||||
query = FFCAP_ET_SQUARE;
|
||||
break;
|
||||
case SHAKE_PERIODIC_TRIANGLE:
|
||||
query = FFCAP_ET_TRIANGLE;
|
||||
break;
|
||||
case SHAKE_PERIODIC_SINE:
|
||||
query = FFCAP_ET_SINE;
|
||||
break;
|
||||
case SHAKE_PERIODIC_SAW_UP:
|
||||
query = FFCAP_ET_SAWTOOTHUP;
|
||||
break;
|
||||
case SHAKE_PERIODIC_SAW_DOWN:
|
||||
query = FFCAP_ET_SAWTOOTHDOWN;
|
||||
break;
|
||||
case SHAKE_PERIODIC_CUSTOM:
|
||||
query = FFCAP_ET_CUSTOMFORCE;
|
||||
break;
|
||||
|
||||
default:
|
||||
return SHAKE_FALSE;
|
||||
}
|
||||
|
||||
return test_bit(query, dev->features.supportedEffects) ? SHAKE_TRUE : SHAKE_FALSE;
|
||||
}
|
||||
|
||||
Shake_Bool Shake_QueryGainSupport(Shake_Device *dev)
|
||||
{
|
||||
HRESULT result;
|
||||
int value = 0; /* Unused for now. */
|
||||
|
||||
result = FFDeviceGetForceFeedbackProperty(dev->device, FFPROP_FFGAIN, &value, sizeof(value));
|
||||
|
||||
if (result == FF_OK)
|
||||
{
|
||||
return SHAKE_TRUE;
|
||||
}
|
||||
else if (result != FFERR_UNSUPPORTED)
|
||||
{
|
||||
Shake_EmitErrorCode(SHAKE_EC_QUERY);
|
||||
}
|
||||
|
||||
return SHAKE_FALSE;
|
||||
}
|
||||
|
||||
Shake_Bool Shake_QueryAutocenterSupport(Shake_Device *dev)
|
||||
{
|
||||
HRESULT result;
|
||||
int value = 0; /* Unused for now. */
|
||||
|
||||
result = FFDeviceGetForceFeedbackProperty(dev->device, FFPROP_AUTOCENTER, &value, sizeof(value));
|
||||
|
||||
if (result == FF_OK)
|
||||
{
|
||||
return SHAKE_TRUE;
|
||||
}
|
||||
else if (result != FFERR_UNSUPPORTED)
|
||||
{
|
||||
Shake_EmitErrorCode(SHAKE_EC_QUERY);
|
||||
}
|
||||
|
||||
return SHAKE_FALSE;
|
||||
}
|
||||
|
||||
Shake_Status Shake_SetGain(Shake_Device *dev, int gain)
|
||||
{
|
||||
if (!dev)
|
||||
return Shake_EmitErrorCode(SHAKE_EC_ARG);
|
||||
|
||||
if (gain < 0)
|
||||
gain = 0;
|
||||
if (gain > 100)
|
||||
gain = 100;
|
||||
|
||||
gain = ((float)gain/100) * FF_FFNOMINALMAX;
|
||||
|
||||
if (FFDeviceSetForceFeedbackProperty(dev->device, FFPROP_FFGAIN, &gain) != FF_OK)
|
||||
{
|
||||
return Shake_EmitErrorCode(SHAKE_EC_TRANSFER);
|
||||
}
|
||||
|
||||
return SHAKE_OK;
|
||||
}
|
||||
|
||||
Shake_Status Shake_SetAutocenter(Shake_Device *dev, int autocenter)
|
||||
{
|
||||
if (!dev)
|
||||
return Shake_EmitErrorCode(SHAKE_EC_ARG);
|
||||
|
||||
if (autocenter) /* OSX supports only OFF and ON values */
|
||||
{
|
||||
autocenter = 1;
|
||||
}
|
||||
|
||||
if (FFDeviceSetForceFeedbackProperty(dev->device, FFPROP_AUTOCENTER, &autocenter) != FF_OK)
|
||||
{
|
||||
return Shake_EmitErrorCode(SHAKE_EC_TRANSFER);
|
||||
}
|
||||
|
||||
return SHAKE_OK;
|
||||
}
|
||||
|
||||
Shake_Status Shake_InitEffect(Shake_Effect *effect, Shake_EffectType type)
|
||||
{
|
||||
if (!effect || type >= SHAKE_EFFECT_COUNT)
|
||||
return Shake_EmitErrorCode(SHAKE_EC_ARG);
|
||||
|
||||
memset(effect, 0, sizeof(*effect));
|
||||
effect->type = type;
|
||||
effect->id = -1;
|
||||
|
||||
return SHAKE_OK;
|
||||
}
|
||||
|
||||
int Shake_UploadEffect(Shake_Device *dev, Shake_Effect *effect)
|
||||
{
|
||||
HRESULT result;
|
||||
FFEFFECT e;
|
||||
CFUUIDRef effectType;
|
||||
EffectContainer *container = NULL;
|
||||
FFENVELOPE envelope;
|
||||
TypeSpecificParams typeParams;
|
||||
DWORD rgdwAxes[2];
|
||||
LONG rglDirection[2];
|
||||
|
||||
if (!dev || !effect || effect->id < SHAKE_EFFECT_ID_MIN)
|
||||
return Shake_EmitErrorCode(SHAKE_EC_ARG);
|
||||
|
||||
rgdwAxes[0] = FFJOFS_X;
|
||||
rgdwAxes[1] = FFJOFS_Y;
|
||||
rglDirection[0] = effect->direction;
|
||||
rglDirection[1] = 0;
|
||||
memset(&envelope, 0, sizeof(FFENVELOPE));
|
||||
|
||||
/* Common parameters. */
|
||||
memset(&e, 0, sizeof(FFEFFECT));
|
||||
e.dwSize = sizeof(FFEFFECT);
|
||||
e.dwFlags = FFEFF_POLAR | FFEFF_OBJECTOFFSETS;
|
||||
e.dwDuration = effect->length * 1000;
|
||||
e.dwSamplePeriod = 0;
|
||||
e.cAxes = 2;
|
||||
e.rgdwAxes = rgdwAxes;
|
||||
e.rglDirection = rglDirection;
|
||||
e.dwStartDelay = effect->delay;
|
||||
e.dwTriggerButton = FFEB_NOTRIGGER;
|
||||
e.dwTriggerRepeatInterval = 0;
|
||||
e.lpEnvelope = &envelope;
|
||||
e.lpEnvelope->dwSize = sizeof(FFENVELOPE);
|
||||
|
||||
e.dwGain = FF_FFNOMINALMAX;
|
||||
|
||||
/* Effect type specific parameters. */
|
||||
if(effect->type == SHAKE_EFFECT_RUMBLE)
|
||||
{
|
||||
/* Emulate EFFECT_RUMBLE with EFFECT_PERIODIC. */
|
||||
int magnitude;
|
||||
|
||||
/*
|
||||
* The magnitude is calculated as average of
|
||||
* 2/3 of strongMagnitude and 1/3 of weakMagnitude.
|
||||
* This follows the same ratios as in the Linux kernel.
|
||||
*/
|
||||
magnitude = effect->u.rumble.strongMagnitude/3 + effect->u.rumble.weakMagnitude/6;
|
||||
|
||||
if (magnitude > SHAKE_PERIODIC_MAGNITUDE_MAX)
|
||||
{
|
||||
magnitude = SHAKE_PERIODIC_MAGNITUDE_MAX;
|
||||
}
|
||||
|
||||
typeParams.pf.dwMagnitude = convertMagnitude(magnitude);
|
||||
typeParams.pf.lOffset = 0;
|
||||
typeParams.pf.dwPhase = 0;
|
||||
typeParams.pf.dwPeriod = 50 * 1000; /* Magic number from the Linux kernel implementation. */
|
||||
|
||||
effectType = kFFEffectType_Sine_ID;
|
||||
e.lpEnvelope->dwAttackTime = 0;
|
||||
e.lpEnvelope->dwAttackLevel = 0;
|
||||
e.lpEnvelope->dwFadeTime = 0;
|
||||
e.lpEnvelope->dwFadeLevel = 0;
|
||||
|
||||
e.cbTypeSpecificParams = sizeof(FFPERIODIC);
|
||||
e.lpvTypeSpecificParams = &typeParams.pf;
|
||||
}
|
||||
else if(effect->type == SHAKE_EFFECT_PERIODIC)
|
||||
{
|
||||
switch (effect->u.periodic.waveform)
|
||||
{
|
||||
case SHAKE_PERIODIC_SQUARE:
|
||||
effectType = kFFEffectType_Square_ID;
|
||||
break;
|
||||
case SHAKE_PERIODIC_TRIANGLE:
|
||||
effectType = kFFEffectType_Triangle_ID;
|
||||
break;
|
||||
case SHAKE_PERIODIC_SINE:
|
||||
effectType = kFFEffectType_Sine_ID;
|
||||
break;
|
||||
case SHAKE_PERIODIC_SAW_UP:
|
||||
effectType = kFFEffectType_SawtoothUp_ID;
|
||||
break;
|
||||
case SHAKE_PERIODIC_SAW_DOWN:
|
||||
effectType = kFFEffectType_SawtoothDown_ID;
|
||||
break;
|
||||
case SHAKE_PERIODIC_CUSTOM:
|
||||
effectType = kFFEffectType_CustomForce_ID;
|
||||
break;
|
||||
|
||||
default:
|
||||
return Shake_EmitErrorCode(SHAKE_EC_SUPPORT);
|
||||
}
|
||||
|
||||
typeParams.pf.dwMagnitude = convertMagnitude(effect->u.periodic.magnitude);
|
||||
typeParams.pf.lOffset = convertMagnitude(effect->u.periodic.offset);
|
||||
typeParams.pf.dwPhase = ((float)effect->u.periodic.phase/SHAKE_PERIODIC_PHASE_MAX) * OSX_PERIODIC_PHASE_MAX;
|
||||
typeParams.pf.dwPeriod = effect->u.periodic.period * 1000;
|
||||
|
||||
e.lpEnvelope->dwAttackTime = effect->u.periodic.envelope.attackLength * 1000;
|
||||
e.lpEnvelope->dwAttackLevel = effect->u.periodic.envelope.attackLevel;
|
||||
e.lpEnvelope->dwFadeTime = effect->u.periodic.envelope.fadeLength * 1000;
|
||||
e.lpEnvelope->dwFadeLevel = effect->u.periodic.envelope.fadeLevel;
|
||||
|
||||
e.cbTypeSpecificParams = sizeof(FFPERIODIC);
|
||||
e.lpvTypeSpecificParams = &typeParams.pf;
|
||||
}
|
||||
else if(effect->type == SHAKE_EFFECT_CONSTANT)
|
||||
{
|
||||
typeParams.cf.lMagnitude = convertMagnitude(effect->u.constant.level);
|
||||
|
||||
effectType = kFFEffectType_ConstantForce_ID;
|
||||
e.lpEnvelope->dwAttackTime = effect->u.constant.envelope.attackLength * 1000;
|
||||
e.lpEnvelope->dwAttackLevel = effect->u.constant.envelope.attackLevel;
|
||||
e.lpEnvelope->dwFadeTime = effect->u.constant.envelope.fadeLength * 1000;
|
||||
e.lpEnvelope->dwFadeLevel = effect->u.constant.envelope.fadeLevel;
|
||||
|
||||
e.cbTypeSpecificParams = sizeof(FFCONSTANTFORCE);
|
||||
e.lpvTypeSpecificParams = &typeParams.cf;
|
||||
}
|
||||
else if(effect->type == SHAKE_EFFECT_RAMP)
|
||||
{
|
||||
typeParams.rf.lStart = ((float)effect->u.ramp.startLevel/SHAKE_RAMP_START_LEVEL_MAX) * FF_FFNOMINALMAX;
|
||||
typeParams.rf.lEnd = ((float)effect->u.ramp.endLevel/SHAKE_RAMP_END_LEVEL_MAX) * FF_FFNOMINALMAX;
|
||||
|
||||
effectType = kFFEffectType_RampForce_ID;
|
||||
e.lpEnvelope->dwAttackTime = effect->u.ramp.envelope.attackLength * 1000;
|
||||
e.lpEnvelope->dwAttackLevel = effect->u.ramp.envelope.attackLevel;
|
||||
e.lpEnvelope->dwFadeTime = effect->u.ramp.envelope.fadeLength * 1000;
|
||||
e.lpEnvelope->dwFadeLevel = effect->u.ramp.envelope.fadeLevel;
|
||||
|
||||
e.cbTypeSpecificParams = sizeof(FFRAMPFORCE);
|
||||
e.lpvTypeSpecificParams = &typeParams.rf;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (effect->type >= SHAKE_EFFECT_COUNT ? Shake_EmitErrorCode(SHAKE_EC_ARG) : Shake_EmitErrorCode(SHAKE_EC_SUPPORT));
|
||||
}
|
||||
|
||||
if (effect->id == SHAKE_EFFECT_ID_MIN) /* Create a new effect. */
|
||||
{
|
||||
dev->effectList = listElementPrepend(dev->effectList);
|
||||
dev->effectList->item = malloc(sizeof(EffectContainer));
|
||||
container = dev->effectList->item;
|
||||
container->id = listLength(dev->effectList) - 1;
|
||||
container->effect = 0;
|
||||
|
||||
result = FFDeviceCreateEffect(dev->device, effectType, &e, &container->effect);
|
||||
|
||||
if ((unsigned int)result != FF_OK)
|
||||
{
|
||||
dev->effectList = listElementDelete(dev->effectList, dev->effectList, effectItemDelete);
|
||||
return Shake_EmitErrorCode(SHAKE_EC_TRANSFER);
|
||||
}
|
||||
}
|
||||
else /* Update existing effect. */
|
||||
{
|
||||
ListElement *node = dev->effectList;
|
||||
EffectContainer *item;
|
||||
|
||||
while (node)
|
||||
{
|
||||
item = (EffectContainer *)node->item;
|
||||
if (item->id == effect->id)
|
||||
{
|
||||
container = item;
|
||||
break;
|
||||
}
|
||||
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
if (container)
|
||||
{
|
||||
int flags = FFEP_AXES | FFEP_DIRECTION | FFEP_DURATION | FFEP_ENVELOPE | FFEP_GAIN | FFEP_SAMPLEPERIOD | FFEP_STARTDELAY | FFEP_TRIGGERBUTTON | FFEP_TYPESPECIFICPARAMS;
|
||||
|
||||
result = FFEffectSetParameters(container->effect, &e, flags);
|
||||
|
||||
if ((unsigned int)result != FF_OK)
|
||||
return Shake_EmitErrorCode(SHAKE_EC_TRANSFER);
|
||||
}
|
||||
}
|
||||
|
||||
return container ? container->id : Shake_EmitErrorCode(SHAKE_EC_EFFECT);
|
||||
}
|
||||
|
||||
Shake_Status Shake_EraseEffect(Shake_Device *dev, int id)
|
||||
{
|
||||
ListElement *node;
|
||||
EffectContainer *effect = NULL;
|
||||
|
||||
if(!dev || id < 0)
|
||||
return Shake_EmitErrorCode(SHAKE_EC_ARG);
|
||||
|
||||
node = dev->effectList;
|
||||
|
||||
while (node)
|
||||
{
|
||||
effect = (EffectContainer *)node->item;
|
||||
if (effect->id == id)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
if (!node || !effect)
|
||||
{
|
||||
return Shake_EmitErrorCode(SHAKE_EC_EFFECT);
|
||||
}
|
||||
|
||||
if (FFDeviceReleaseEffect(dev->device, effect->effect))
|
||||
{
|
||||
return Shake_EmitErrorCode(SHAKE_EC_TRANSFER);
|
||||
}
|
||||
|
||||
dev->effectList = listElementDelete(dev->effectList, node, effectItemDelete);
|
||||
|
||||
return SHAKE_OK;
|
||||
}
|
||||
|
||||
Shake_Status Shake_Play(Shake_Device *dev, int id)
|
||||
{
|
||||
ListElement *node;
|
||||
EffectContainer *effect = NULL;
|
||||
|
||||
if(!dev || id < 0)
|
||||
return Shake_EmitErrorCode(SHAKE_EC_ARG);
|
||||
|
||||
node = dev->effectList;
|
||||
|
||||
while (node)
|
||||
{
|
||||
effect = (EffectContainer *)node->item;
|
||||
if (effect->id == id)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
if (!node || !effect)
|
||||
{
|
||||
return Shake_EmitErrorCode(SHAKE_EC_EFFECT);
|
||||
}
|
||||
|
||||
if (FFEffectStart(effect->effect, 1, 0) != FF_OK)
|
||||
{
|
||||
return Shake_EmitErrorCode(SHAKE_EC_TRANSFER);
|
||||
}
|
||||
|
||||
return SHAKE_OK;
|
||||
}
|
||||
|
||||
Shake_Status Shake_Stop(Shake_Device *dev, int id)
|
||||
{
|
||||
ListElement *node;
|
||||
EffectContainer *effect = NULL;
|
||||
|
||||
if(!dev || id < 0)
|
||||
return Shake_EmitErrorCode(SHAKE_EC_ARG);
|
||||
|
||||
node = dev->effectList;
|
||||
|
||||
while (node)
|
||||
{
|
||||
effect = (EffectContainer *)node->item;
|
||||
if (effect->id == id)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
if (!node || !effect)
|
||||
{
|
||||
return Shake_EmitErrorCode(SHAKE_EC_EFFECT);
|
||||
}
|
||||
|
||||
if (FFEffectStop(effect->effect))
|
||||
{
|
||||
return Shake_EmitErrorCode(SHAKE_EC_TRANSFER);
|
||||
}
|
||||
|
||||
return SHAKE_OK;
|
||||
}
|
||||
|
||||
Shake_Status Shake_Close(Shake_Device *dev)
|
||||
{
|
||||
int effectLen;
|
||||
int i;
|
||||
|
||||
if (!dev)
|
||||
return Shake_EmitErrorCode(SHAKE_EC_ARG);
|
||||
if (!dev->device)
|
||||
return Shake_EmitErrorCode(SHAKE_EC_DEVICE);
|
||||
|
||||
effectLen = listLength(dev->effectList);
|
||||
|
||||
for (i = 0; i < effectLen; ++i)
|
||||
{
|
||||
EffectContainer *effect = (EffectContainer *)listElementGet(dev->effectList, i);
|
||||
if (FFDeviceReleaseEffect(dev->device, effect->effect))
|
||||
{
|
||||
return Shake_EmitErrorCode(SHAKE_EC_TRANSFER);
|
||||
}
|
||||
}
|
||||
|
||||
dev->effectList = listElementDeleteAll(dev->effectList, effectItemDelete);
|
||||
if (FFReleaseDevice(dev->device) != FF_OK)
|
||||
{
|
||||
return Shake_EmitErrorCode(SHAKE_EC_TRANSFER);
|
||||
}
|
||||
|
||||
dev->device = 0;
|
||||
|
||||
return SHAKE_OK;
|
||||
}
|
43
deps/libShake/src/osx/shake_private.h
vendored
Normal file
43
deps/libShake/src/osx/shake_private.h
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
#ifndef _SHAKE_PRIVATE_H_
|
||||
#define _SHAKE_PRIVATE_H_
|
||||
|
||||
#include <ForceFeedback/ForceFeedback.h>
|
||||
#include <IOKit/IOKitLib.h>
|
||||
#include "../common/helpers.h"
|
||||
|
||||
#define BITS_PER_LONG (sizeof(long) * 8)
|
||||
#define OFF(x) ((x)%BITS_PER_LONG)
|
||||
#define BIT(x) (1UL<<OFF(x))
|
||||
#define LONG(x) ((x)/BITS_PER_LONG)
|
||||
#define test_bit(bit, array) ((array >> OFF(bit)) & 1)
|
||||
#define BITS_TO_LONGS(x) \
|
||||
(((x) + 8 * sizeof (unsigned long) - 1) / (8 * sizeof (unsigned long)))
|
||||
|
||||
#define OSX_PERIODIC_PHASE_MAX 0x8C9F
|
||||
|
||||
typedef union TypeSpecificParams
|
||||
{
|
||||
FFPERIODIC pf;
|
||||
FFCONSTANTFORCE cf;
|
||||
FFRAMPFORCE rf;
|
||||
} TypeSpecificParams;
|
||||
|
||||
typedef struct EffectContainer
|
||||
{
|
||||
int id;
|
||||
FFEffectObjectReference effect;
|
||||
} EffectContainer;
|
||||
|
||||
struct Shake_Device
|
||||
{
|
||||
char name[128];
|
||||
int id;
|
||||
int capacity; /* Number of effects the device can play at the same time */
|
||||
/* Platform dependent section */
|
||||
io_service_t service;
|
||||
FFDeviceObjectReference device;
|
||||
ListElement *effectList;
|
||||
FFCAPABILITIES features;
|
||||
};
|
||||
|
||||
#endif /* _SHAKE_PRIVATE_H_ */
|
@ -778,6 +778,20 @@ INPUT
|
||||
#include "../input/drivers_joypad/udev_joypad.c"
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_LIBSHAKE)
|
||||
#if TARGET_OS_OSX
|
||||
#include "../deps/libShake/src/common/error.c"
|
||||
#include "../deps/libShake/src/common/helpers.c"
|
||||
#include "../deps/libShake/src/common/presets.c"
|
||||
#include "../deps/libShake/src/osx/shake.c"
|
||||
#elif defined(__linux__) || (defined(BSD) && !defined(__MACH__))
|
||||
#include "../deps/libShake/src/common/error.c"
|
||||
#include "../deps/libShake/src/common/helpers.c"
|
||||
#include "../deps/libShake/src/common/presets.c"
|
||||
#include "../deps/libShake/src/linux/shake.c"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*============================================================
|
||||
INPUT (HID)
|
||||
============================================================ */
|
||||
|
@ -26,15 +26,50 @@
|
||||
#include "../../tasks/tasks_internal.h"
|
||||
#include "../../verbosity.h"
|
||||
|
||||
#if defined(HAVE_LIBSHAKE)
|
||||
#include <shake.h>
|
||||
#include "../../configuration.h"
|
||||
#endif
|
||||
|
||||
/* Simple joypad driver designed to rationalise
|
||||
* the bizarre keyboard/gamepad hybrid setup
|
||||
* of OpenDingux devices */
|
||||
|
||||
#define SDL_DINGUX_JOYPAD_NAME "Dingux Gamepad"
|
||||
|
||||
#if defined(HAVE_LIBSHAKE)
|
||||
/* 5 ms period == 200 Hz
|
||||
* > Meissner's Corpuscle registers this
|
||||
* as 'fast' motion */
|
||||
#define SDL_DINGUX_RUMBLE_WEAK_PERIOD 5
|
||||
|
||||
/* 142 ms period ~= 7 Hz
|
||||
* > Merkel's Cells and Ruffini Ending register
|
||||
* this as 'slow' motion */
|
||||
#define SDL_DINGUX_RUMBLE_STRONG_PERIOD 142
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int id;
|
||||
uint16_t strength;
|
||||
Shake_Effect effect;
|
||||
bool active;
|
||||
} dingux_joypad_rumble_effect_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Shake_Device *device;
|
||||
dingux_joypad_rumble_effect_t weak;
|
||||
dingux_joypad_rumble_effect_t strong;
|
||||
} dingux_joypad_rumble_t;
|
||||
#endif
|
||||
|
||||
typedef struct
|
||||
{
|
||||
SDL_Joystick *device;
|
||||
#if defined(HAVE_LIBSHAKE)
|
||||
dingux_joypad_rumble_t rumble;
|
||||
#endif
|
||||
uint16_t pad_state;
|
||||
int16_t analog_state[2][2];
|
||||
unsigned num_axes;
|
||||
@ -47,6 +82,169 @@ extern uint64_t lifecycle_state;
|
||||
|
||||
static dingux_joypad_t dingux_joypad;
|
||||
|
||||
#if defined(HAVE_LIBSHAKE)
|
||||
static bool sdl_dingux_rumble_init(dingux_joypad_rumble_t *rumble)
|
||||
{
|
||||
settings_t *settings = config_get_ptr();
|
||||
unsigned rumble_gain = settings ? settings->uints.input_dingux_rumble_gain : 0;
|
||||
bool weak_uploaded = false;
|
||||
bool strong_uploaded = false;
|
||||
|
||||
/* If gain is zero, rumble is disabled
|
||||
* > No need to initialise device */
|
||||
if (rumble_gain == 0)
|
||||
goto error;
|
||||
|
||||
if (Shake_NumOfDevices() < 1)
|
||||
goto error;
|
||||
|
||||
/* Open shake device */
|
||||
rumble->device = Shake_Open(0);
|
||||
|
||||
if (!rumble->device)
|
||||
goto error;
|
||||
|
||||
/* Check whether shake device has the required
|
||||
* feature set */
|
||||
if (!Shake_QueryEffectSupport(rumble->device, SHAKE_EFFECT_PERIODIC) ||
|
||||
!Shake_QueryWaveformSupport(rumble->device, SHAKE_PERIODIC_SINE))
|
||||
goto error;
|
||||
|
||||
/* In most cases it is recommended to use SHAKE_EFFECT_PERIODIC
|
||||
* instead of SHAKE_EFFECT_RUMBLE. All devices that support
|
||||
* SHAKE_EFFECT_RUMBLE support SHAKE_EFFECT_PERIODIC (square,
|
||||
* triangle, sine) and vice versa */
|
||||
|
||||
/* Initialise weak rumble effect */
|
||||
if (Shake_InitEffect(&rumble->weak.effect, SHAKE_EFFECT_PERIODIC) != SHAKE_OK)
|
||||
goto error;
|
||||
|
||||
rumble->weak.effect.u.periodic.waveform = SHAKE_PERIODIC_SINE;
|
||||
rumble->weak.effect.u.periodic.period = SDL_DINGUX_RUMBLE_WEAK_PERIOD;
|
||||
rumble->weak.effect.u.periodic.magnitude = 0;
|
||||
rumble->weak.id = Shake_UploadEffect(rumble->device, &rumble->weak.effect);
|
||||
|
||||
if (rumble->weak.id == SHAKE_ERROR)
|
||||
goto error;
|
||||
weak_uploaded = true;
|
||||
|
||||
/* Initialise strong rumble effect */
|
||||
if (Shake_InitEffect(&rumble->strong.effect, SHAKE_EFFECT_PERIODIC) != SHAKE_OK)
|
||||
goto error;
|
||||
|
||||
rumble->strong.effect.u.periodic.waveform = SHAKE_PERIODIC_SINE;
|
||||
rumble->strong.effect.u.periodic.period = SDL_DINGUX_RUMBLE_STRONG_PERIOD;
|
||||
rumble->strong.effect.u.periodic.magnitude = 0;
|
||||
rumble->strong.id = Shake_UploadEffect(rumble->device, &rumble->strong.effect);
|
||||
|
||||
if (rumble->weak.id == SHAKE_ERROR)
|
||||
goto error;
|
||||
strong_uploaded = true;
|
||||
|
||||
/* Set gain, if supported */
|
||||
if (Shake_QueryGainSupport(rumble->device))
|
||||
if (Shake_SetGain(rumble->device, (int)rumble_gain) != SHAKE_OK)
|
||||
goto error;
|
||||
|
||||
return true;
|
||||
|
||||
error:
|
||||
if (rumble_gain != 0)
|
||||
RARCH_WARN("[libShake]: Input device does not support rumble effects.\n");
|
||||
|
||||
if (rumble->device)
|
||||
{
|
||||
if (weak_uploaded)
|
||||
Shake_EraseEffect(rumble->device, rumble->weak.id);
|
||||
|
||||
if (strong_uploaded)
|
||||
Shake_EraseEffect(rumble->device, rumble->strong.id);
|
||||
|
||||
Shake_Close(rumble->device);
|
||||
rumble->device = NULL;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool sdl_dingux_rumble_update(Shake_Device *device,
|
||||
dingux_joypad_rumble_effect_t *effect,
|
||||
uint16_t strength, uint16_t max_strength)
|
||||
{
|
||||
int id;
|
||||
|
||||
/* If strength is zero, halt rumble effect */
|
||||
if (strength == 0)
|
||||
{
|
||||
if (effect->active)
|
||||
{
|
||||
if (Shake_Stop(device, effect->id) == SHAKE_OK)
|
||||
{
|
||||
effect->active = false;
|
||||
effect->strength = 0;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* If strength is unchanged, do nothing */
|
||||
if (strength == effect->strength)
|
||||
return true;
|
||||
|
||||
/* Strength has changed - update effect */
|
||||
effect->effect.id = effect->id;
|
||||
effect->effect.u.periodic.magnitude = (max_strength * strength) / 0xFFFF;
|
||||
id = Shake_UploadEffect(device, &effect->effect);
|
||||
|
||||
if (id == SHAKE_ERROR)
|
||||
return false;
|
||||
|
||||
effect->id = id;
|
||||
|
||||
if (!effect->active)
|
||||
{
|
||||
if (Shake_Play(device, effect->id) == SHAKE_OK)
|
||||
{
|
||||
effect->active = true;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool sdl_dingux_joypad_set_rumble(unsigned pad,
|
||||
enum retro_rumble_effect effect, uint16_t strength)
|
||||
{
|
||||
dingux_joypad_t *joypad = (dingux_joypad_t*)&dingux_joypad;
|
||||
|
||||
if (!joypad->rumble.device)
|
||||
return false;
|
||||
|
||||
switch (effect)
|
||||
{
|
||||
case RETRO_RUMBLE_STRONG:
|
||||
return sdl_dingux_rumble_update(joypad->rumble.device,
|
||||
&joypad->rumble.strong, strength,
|
||||
SHAKE_RUMBLE_STRONG_MAGNITUDE_MAX);
|
||||
case RETRO_RUMBLE_WEAK:
|
||||
return sdl_dingux_rumble_update(joypad->rumble.device,
|
||||
&joypad->rumble.weak, strength,
|
||||
SHAKE_RUMBLE_WEAK_MAGNITUDE_MAX);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const char *sdl_dingux_joypad_name(unsigned port)
|
||||
{
|
||||
const char *joypad_name = NULL;
|
||||
@ -69,6 +267,11 @@ static void sdl_dingux_joypad_connect(void)
|
||||
if (joypad->device)
|
||||
joypad->num_axes = SDL_JoystickNumAxes(joypad->device);
|
||||
|
||||
#if defined(HAVE_LIBSHAKE)
|
||||
/* Configure rumble interface */
|
||||
sdl_dingux_rumble_init(&joypad->rumble);
|
||||
#endif
|
||||
|
||||
/* 'Register' joypad connection via
|
||||
* autoconfig task */
|
||||
input_autoconfigure_connect(
|
||||
@ -92,6 +295,22 @@ static void sdl_dingux_joypad_disconnect(void)
|
||||
if (joypad->connected)
|
||||
input_autoconfigure_disconnect(0, sdl_dingux_joypad.ident);
|
||||
|
||||
#if defined(HAVE_LIBSHAKE)
|
||||
if (joypad->rumble.device)
|
||||
{
|
||||
if (joypad->rumble.weak.active)
|
||||
Shake_Stop(joypad->rumble.device, joypad->rumble.weak.id);
|
||||
|
||||
if (joypad->rumble.strong.active)
|
||||
Shake_Stop(joypad->rumble.device, joypad->rumble.strong.id);
|
||||
|
||||
Shake_EraseEffect(joypad->rumble.device, joypad->rumble.weak.id);
|
||||
Shake_EraseEffect(joypad->rumble.device, joypad->rumble.strong.id);
|
||||
|
||||
Shake_Close(joypad->rumble.device);
|
||||
}
|
||||
#endif
|
||||
|
||||
memset(joypad, 0, sizeof(dingux_joypad_t));
|
||||
}
|
||||
|
||||
@ -108,6 +327,11 @@ static void sdl_dingux_joypad_destroy(void)
|
||||
/* Flush out all pending events */
|
||||
while (SDL_PollEvent(&event));
|
||||
|
||||
#if defined(HAVE_LIBSHAKE)
|
||||
/* De-initialise rumble interface */
|
||||
Shake_Quit();
|
||||
#endif
|
||||
|
||||
BIT64_CLEAR(lifecycle_state, RARCH_MENU_TOGGLE);
|
||||
}
|
||||
|
||||
@ -127,6 +351,11 @@ static void *sdl_dingux_joypad_init(void *data)
|
||||
else if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0)
|
||||
return NULL;
|
||||
|
||||
#if defined(HAVE_LIBSHAKE)
|
||||
/* Initialise rumble interface */
|
||||
Shake_Init();
|
||||
#endif
|
||||
|
||||
/* Connect joypad */
|
||||
sdl_dingux_joypad_connect();
|
||||
|
||||
@ -457,7 +686,11 @@ input_device_driver_t sdl_dingux_joypad = {
|
||||
sdl_dingux_joypad_get_buttons,
|
||||
sdl_dingux_joypad_axis,
|
||||
sdl_dingux_joypad_poll,
|
||||
NULL, /* set_rumble */
|
||||
#if defined(HAVE_LIBSHAKE)
|
||||
sdl_dingux_joypad_set_rumble,
|
||||
#else
|
||||
NULL,
|
||||
#endif
|
||||
sdl_dingux_joypad_name,
|
||||
"sdl_dingux",
|
||||
};
|
||||
|
@ -4780,6 +4780,12 @@ MSG_HASH(
|
||||
MENU_ENUM_LABEL_INPUT_HAPTIC_FEEDBACK_SETTINGS,
|
||||
"input_haptic_feedback_settings"
|
||||
)
|
||||
#if defined(DINGUX) && defined(HAVE_LIBSHAKE)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_INPUT_DINGUX_RUMBLE_GAIN,
|
||||
"input_dingux_rumble_gain"
|
||||
)
|
||||
#endif
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_INPUT_TURBO_MODE,
|
||||
"input_turbo_mode"
|
||||
|
@ -2173,6 +2173,16 @@ MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_ENABLE_DEVICE_VIBRATION,
|
||||
"Enable Device Vibration (For Supported Cores)"
|
||||
)
|
||||
#if defined(DINGUX) && defined(HAVE_LIBSHAKE)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_INPUT_DINGUX_RUMBLE_GAIN,
|
||||
"Vibration Strength (Restart)"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_INPUT_DINGUX_RUMBLE_GAIN,
|
||||
"Specifies the magnitude of haptic feedback effects."
|
||||
)
|
||||
#endif
|
||||
|
||||
/* Settings > Input > Menu Controls */
|
||||
|
||||
|
@ -331,6 +331,9 @@ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_input_turbo_period, MENU_
|
||||
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_input_duty_cycle, MENU_ENUM_SUBLABEL_INPUT_DUTY_CYCLE)
|
||||
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_input_turbo_mode, MENU_ENUM_SUBLABEL_INPUT_TURBO_MODE)
|
||||
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_input_turbo_default_button, MENU_ENUM_SUBLABEL_INPUT_TURBO_DEFAULT_BUTTON)
|
||||
#if defined(DINGUX) && defined(HAVE_LIBSHAKE)
|
||||
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_input_dingux_rumble_gain, MENU_ENUM_SUBLABEL_INPUT_DINGUX_RUMBLE_GAIN)
|
||||
#endif
|
||||
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_video_vertical_sync, MENU_ENUM_SUBLABEL_VIDEO_VSYNC)
|
||||
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_video_adaptive_vsync, MENU_ENUM_SUBLABEL_VIDEO_ADAPTIVE_VSYNC)
|
||||
DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_core_allow_rotate, MENU_ENUM_SUBLABEL_VIDEO_ALLOW_ROTATE)
|
||||
@ -3301,6 +3304,11 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs,
|
||||
case MENU_ENUM_LABEL_INPUT_TURBO_DEFAULT_BUTTON:
|
||||
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_input_turbo_default_button);
|
||||
break;
|
||||
#if defined(DINGUX) && defined(HAVE_LIBSHAKE)
|
||||
case MENU_ENUM_LABEL_INPUT_DINGUX_RUMBLE_GAIN:
|
||||
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_input_dingux_rumble_gain);
|
||||
break;
|
||||
#endif
|
||||
case MENU_ENUM_LABEL_INPUT_BIND_TIMEOUT:
|
||||
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_input_bind_timeout);
|
||||
break;
|
||||
|
@ -5202,14 +5202,31 @@ unsigned menu_displaylist_build_list(
|
||||
count++;
|
||||
break;
|
||||
case DISPLAYLIST_INPUT_HAPTIC_FEEDBACK_SETTINGS_LIST:
|
||||
if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list,
|
||||
MENU_ENUM_LABEL_VIBRATE_ON_KEYPRESS,
|
||||
PARSE_ONLY_BOOL, false) == 0)
|
||||
count++;
|
||||
if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list,
|
||||
MENU_ENUM_LABEL_ENABLE_DEVICE_VIBRATION,
|
||||
PARSE_ONLY_BOOL, false) == 0)
|
||||
count++;
|
||||
{
|
||||
settings_t *settings = config_get_ptr();
|
||||
const char *input_driver_id = settings->arrays.input_driver;
|
||||
const char *joypad_driver_id = settings->arrays.input_joypad_driver;
|
||||
|
||||
if (string_is_equal(input_driver_id, "android"))
|
||||
{
|
||||
if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list,
|
||||
MENU_ENUM_LABEL_VIBRATE_ON_KEYPRESS,
|
||||
PARSE_ONLY_BOOL, false) == 0)
|
||||
count++;
|
||||
if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list,
|
||||
MENU_ENUM_LABEL_ENABLE_DEVICE_VIBRATION,
|
||||
PARSE_ONLY_BOOL, false) == 0)
|
||||
count++;
|
||||
}
|
||||
|
||||
#if defined(DINGUX) && defined(HAVE_LIBSHAKE)
|
||||
if (string_is_equal(joypad_driver_id, "sdl_dingux"))
|
||||
if (MENU_DISPLAYLIST_PARSE_SETTINGS_ENUM(list,
|
||||
MENU_ENUM_LABEL_INPUT_DINGUX_RUMBLE_GAIN,
|
||||
PARSE_ONLY_UINT, false) == 0)
|
||||
count++;
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
case DISPLAYLIST_INPUT_HOTKEY_BINDS_LIST:
|
||||
{
|
||||
|
@ -11794,6 +11794,22 @@ static bool setting_append_list(
|
||||
SD_FLAG_NONE
|
||||
);
|
||||
|
||||
#if defined(DINGUX) && defined(HAVE_LIBSHAKE)
|
||||
CONFIG_UINT(
|
||||
list, list_info,
|
||||
&settings->uints.input_dingux_rumble_gain,
|
||||
MENU_ENUM_LABEL_INPUT_DINGUX_RUMBLE_GAIN,
|
||||
MENU_ENUM_LABEL_VALUE_INPUT_DINGUX_RUMBLE_GAIN,
|
||||
DEFAULT_DINGUX_RUMBLE_GAIN,
|
||||
&group_info,
|
||||
&subgroup_info,
|
||||
parent_group,
|
||||
general_write_handler,
|
||||
general_read_handler);
|
||||
(*list)[list_info->index - 1].ui_type = ST_UI_TYPE_UINT_COMBOBOX;
|
||||
(*list)[list_info->index - 1].action_ok = &setting_action_ok_uint;
|
||||
menu_settings_list_current_add_range(list, list_info, 0, 100, 5, true, true);
|
||||
#endif
|
||||
CONFIG_UINT(
|
||||
list, list_info,
|
||||
&settings->uints.input_poll_type_behavior,
|
||||
|
@ -960,6 +960,10 @@ enum msg_hash_enums
|
||||
MENU_ENUM_LABEL_VALUE_QUIT_ON_CLOSE_CONTENT_ENABLED,
|
||||
MENU_ENUM_LABEL_VALUE_QUIT_ON_CLOSE_CONTENT_CLI,
|
||||
|
||||
#if defined(DINGUX) && defined(HAVE_LIBSHAKE)
|
||||
MENU_LABEL(INPUT_DINGUX_RUMBLE_GAIN),
|
||||
#endif
|
||||
|
||||
/* Video */
|
||||
MENU_LABEL(CRT_SWITCH_RESOLUTION),
|
||||
MENU_LABEL(CRT_SWITCH_RESOLUTION_SUPER),
|
||||
|
@ -83,6 +83,7 @@ HAVE_OPENGL1=yes # OpenGL 1.1 support
|
||||
HAVE_MALI_FBDEV=no # Mali fbdev context support
|
||||
HAVE_VIVANTE_FBDEV=no # Vivante fbdev context support
|
||||
HAVE_OPENDINGUX_FBDEV=no # Opendingux fbdev context support
|
||||
HAVE_SDL_DINGUX=no # Opendingux SDL input/gfx driver support
|
||||
HAVE_OPENGLES=no # Use GLESv2 instead of desktop GL
|
||||
HAVE_OPENGLES3=no # OpenGLES3 support
|
||||
HAVE_OPENGLES3_1=no # OpenGLES3.1 support
|
||||
@ -189,4 +190,5 @@ C89_METAL=no
|
||||
HAVE_NETWORK_VIDEO=no
|
||||
HAVE_STEAM=no # Enable Steam build
|
||||
HAVE_ODROIDGO2=no # ODROID-GO Advance rotation support (requires librga)
|
||||
HAVE_LIBSHAKE=no # libShake haptic feedback support
|
||||
HAVE_GIT_VERSION=yes # Git version support
|
||||
|
Loading…
Reference in New Issue
Block a user