diff --git a/.github/workflows/Linux-samples-input.yml b/.github/workflows/Linux-samples-input.yml index e418f3d8e2..397d299a3f 100644 --- a/.github/workflows/Linux-samples-input.yml +++ b/.github/workflows/Linux-samples-input.yml @@ -1124,6 +1124,23 @@ jobs: timeout 120 ./udev_rumble_test make clean echo "[pass] udev_rumble_test" + - name: Build and run dingux_rumble_test (ASan + UBSan, then TSan) + shell: bash + working-directory: samples/input/dingux_rumble + run: | + set -eu + # The Dingux joypad driver's vibration motor, through evdev + # and the output writer: the first node that plays a sine is + # taken, the strengths and gain are written as they change and + # not again, a motor slow to take an upload does not hold the + # call, and stopping the driver lets the motor go. + make clean SANITIZER=address,undefined && make -j$(nproc) all SANITIZER=address,undefined + timeout 120 ./dingux_rumble_test + timeout 120 ./dingux_rumble_test_nothreads + make clean SANITIZER=thread && make -j$(nproc) all SANITIZER=thread + timeout 120 ./dingux_rumble_test + make clean + echo "[pass] dingux_rumble_test" - name: Build and run udev_player_lights_test (ASan + UBSan, then TSan) shell: bash working-directory: samples/input/udev_player_lights diff --git a/Makefile.common b/Makefile.common index e9d01451df..e85e685f9d 100644 --- a/Makefile.common +++ b/Makefile.common @@ -1481,29 +1481,17 @@ ifeq ($(HAVE_WINRAWINPUT), 1) endif endif +# The Dingux joypad driver's vibration motor is written from it too +ifeq ($(HAVE_SDL_DINGUX), 1) + HAVE_INPUT_OUTPUT_WRITER = 1 +endif + # The thread the joypad drivers write to their controllers from (udev, -# XInput, Windows raw input): one object, whichever of them is built. +# XInput, Windows raw input, Dingux): one object, whichever of them is built. ifeq ($(HAVE_INPUT_OUTPUT_WRITER), 1) OBJ += input/common/output_writer.o 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 diff --git a/Makefile.dingux b/Makefile.dingux index e3026848bb..c1b43241c5 100644 --- a/Makefile.dingux +++ b/Makefile.dingux @@ -101,7 +101,6 @@ HAVE_XMB = 1 HAVE_CONFIGFILE = 1 HAVE_PATCH = 1 HAVE_CHEATS = 1 -HAVE_LIBSHAKE = 1 HAVE_CORE_INFO_CACHE = 1 OS = Linux diff --git a/Makefile.lfx000 b/Makefile.lfx000 index 9aad3f1545..664fc84279 100644 --- a/Makefile.lfx000 +++ b/Makefile.lfx000 @@ -106,7 +106,6 @@ HAVE_CONFIGFILE = 1 HAVE_PATCH = 1 HAVE_CHEATS = 1 HAVE_CHEEVOS = 0 -HAVE_LIBSHAKE = 0 HAVE_CORE_INFO_CACHE = 1 #HAVE_TINYALSA = 1 HAVE_NEAREST_RESAMPLER = 1 diff --git a/Makefile.miyoo b/Makefile.miyoo index 5ac7b265f9..0d9dec2f7f 100644 --- a/Makefile.miyoo +++ b/Makefile.miyoo @@ -109,7 +109,6 @@ HAVE_PATCH = 1 HAVE_XDELTA = 0 # Disabled until we figure out how to include HAVE_CHEATS = 1 HAVE_CHEEVOS = 0 -HAVE_LIBSHAKE = 0 HAVE_CORE_INFO_CACHE = 1 #HAVE_TINYALSA = 1 HAVE_NEAREST_RESAMPLER = 1 diff --git a/Makefile.retrofw b/Makefile.retrofw index ca05bb0fbe..498271f1b5 100644 --- a/Makefile.retrofw +++ b/Makefile.retrofw @@ -114,7 +114,6 @@ HAVE_PATCH = 1 HAVE_XDELTA = 0 # disabled because isn't available (or we haven't figured out how to install it) HAVE_CHEATS = 1 HAVE_CHEEVOS = 0 -HAVE_LIBSHAKE = 0 HAVE_CORE_INFO_CACHE = 1 HAVE_OSS =1 diff --git a/Makefile.rg350 b/Makefile.rg350 index 87515c42ad..d91899b525 100644 --- a/Makefile.rg350 +++ b/Makefile.rg350 @@ -111,7 +111,6 @@ HAVE_CONFIGFILE = 1 HAVE_PATCH = 1 HAVE_CHEATS = 1 HAVE_CHEEVOS = 0 -HAVE_LIBSHAKE = 1 HAVE_CORE_INFO_CACHE = 1 OS = Linux diff --git a/Makefile.rg350_odbeta b/Makefile.rg350_odbeta index 6b977860d4..81dc7d0935 100644 --- a/Makefile.rg350_odbeta +++ b/Makefile.rg350_odbeta @@ -106,7 +106,6 @@ HAVE_CONFIGFILE = 1 HAVE_PATCH = 1 HAVE_CHEATS = 1 HAVE_CHEEVOS = 0 -HAVE_LIBSHAKE = 1 HAVE_TINYALSA = 1 HAVE_CORE_INFO_CACHE = 1 diff --git a/Makefile.rs90 b/Makefile.rs90 index 0449cde59e..f4aaa3aa33 100644 --- a/Makefile.rs90 +++ b/Makefile.rs90 @@ -109,7 +109,6 @@ HAVE_PATCH = 1 HAVE_XDELTA = 0 # Disabled until we figure out how to include HAVE_CHEATS = 1 HAVE_CHEEVOS = 0 -HAVE_LIBSHAKE = 0 HAVE_TINYALSA = 1 HAVE_NEAREST_RESAMPLER = 1 HAVE_CORE_INFO_CACHE = 1 diff --git a/Makefile.webos b/Makefile.webos index 78e35a7c2d..d1a24259d9 100644 --- a/Makefile.webos +++ b/Makefile.webos @@ -143,7 +143,6 @@ HAVE_CONFIGFILE = 1 HAVE_PATCH = 1 HAVE_CHEATS = 1 HAVE_CHEEVOS = 1 -HAVE_LIBSHAKE = 1 HAVE_UPDATE_ASSETS = 1 HAVE_UPDATE_CORES = 1 HAVE_UPDATE_CORE_INFO = 1 diff --git a/config.def.h b/config.def.h index 4fb3de364e..2822ba5731 100644 --- a/config.def.h +++ b/config.def.h @@ -2172,7 +2172,7 @@ /* Defines the strength of rumble effects * on OpenDingux devices */ -#if defined(DINGUX) && defined(HAVE_LIBSHAKE) +#if defined(DINGUX) && defined(__linux__) #define DEFAULT_RUMBLE_GAIN 50 #else #define DEFAULT_RUMBLE_GAIN 100 diff --git a/deps/libShake/LICENSE.txt b/deps/libShake/LICENSE.txt deleted file mode 100644 index c368c8e31d..0000000000 --- a/deps/libShake/LICENSE.txt +++ /dev/null @@ -1,23 +0,0 @@ -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. - diff --git a/deps/libShake/Makefile b/deps/libShake/Makefile deleted file mode 100644 index d7906c9eb1..0000000000 --- a/deps/libShake/Makefile +++ /dev/null @@ -1,72 +0,0 @@ -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) diff --git a/deps/libShake/README.md b/deps/libShake/README.md deleted file mode 100644 index 3792f179d8..0000000000 --- a/deps/libShake/README.md +++ /dev/null @@ -1,44 +0,0 @@ -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/). diff --git a/deps/libShake/examples/Makefile b/deps/libShake/examples/Makefile deleted file mode 100644 index 5d7abea5eb..0000000000 --- a/deps/libShake/examples/Makefile +++ /dev/null @@ -1,28 +0,0 @@ -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) diff --git a/deps/libShake/examples/deviceTest.c b/deps/libShake/examples/deviceTest.c deleted file mode 100644 index c7e0358d58..0000000000 --- a/deps/libShake/examples/deviceTest.c +++ /dev/null @@ -1,333 +0,0 @@ -#include -#include -#include -#include -#include - -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; -} diff --git a/deps/libShake/examples/listDevices.c b/deps/libShake/examples/listDevices.c deleted file mode 100644 index 67d8215a4a..0000000000 --- a/deps/libShake/examples/listDevices.c +++ /dev/null @@ -1,51 +0,0 @@ -#include -#include -#include - -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; -} diff --git a/deps/libShake/examples/simple.c b/deps/libShake/examples/simple.c deleted file mode 100644 index 29151eca70..0000000000 --- a/deps/libShake/examples/simple.c +++ /dev/null @@ -1,40 +0,0 @@ -#include -#include -#include - -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; -} diff --git a/deps/libShake/extras/buildroot-package/libshake/Config.in b/deps/libShake/extras/buildroot-package/libshake/Config.in deleted file mode 100644 index ad1a25d8f2..0000000000 --- a/deps/libShake/extras/buildroot-package/libshake/Config.in +++ /dev/null @@ -1,6 +0,0 @@ -config BR2_PACKAGE_LIBSHAKE - bool "libshake" - help - A simple, cross-platform haptic library. - - https://github.com/zear/libShake diff --git a/deps/libShake/extras/buildroot-package/libshake/libshake.mk b/deps/libShake/extras/buildroot-package/libshake/libshake.mk deleted file mode 100644 index ab70bd9f1e..0000000000 --- a/deps/libShake/extras/buildroot-package/libshake/libshake.mk +++ /dev/null @@ -1,26 +0,0 @@ -############################################################# -# -# 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)) diff --git a/deps/libShake/include/shake.h b/deps/libShake/include/shake.h deleted file mode 100644 index ff095e4afa..0000000000 --- a/deps/libShake/include/shake.h +++ /dev/null @@ -1,522 +0,0 @@ -/** \file shake.h - \brief libShake public header. -*/ - -#ifndef _SHAKE_H_ -#define _SHAKE_H_ - -#ifdef __cplusplus -extern "C" { -#endif - -#include - -/** \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. NOTE: Currently not supported. */ - SHAKE_EFFECT_FRICTION, /**< Friction effect. NOTE: Currently not supported. */ - SHAKE_EFFECT_DAMPER, /**< Damper effect. NOTE: Currently not supported. */ - SHAKE_EFFECT_INERTIA, /**< Inertia effect. NOTE: 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. - - NOTE: 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_ */ diff --git a/deps/libShake/src/common/error.c b/deps/libShake/src/common/error.c deleted file mode 100644 index 3b9d801b53..0000000000 --- a/deps/libShake/src/common/error.c +++ /dev/null @@ -1,14 +0,0 @@ -#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; -} diff --git a/deps/libShake/src/common/error.h b/deps/libShake/src/common/error.h deleted file mode 100644 index 6f2baf38ef..0000000000 --- a/deps/libShake/src/common/error.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef _SHAKE_ERROR_H_ -#define _SHAKE_ERROR_H_ - -#include "shake.h" - -Shake_Status Shake_EmitErrorCode(Shake_ErrorCode ec); -Shake_ErrorCode Shake_GetErrorCode(void); - -#endif diff --git a/deps/libShake/src/common/helpers.c b/deps/libShake/src/common/helpers.c deleted file mode 100644 index 50a3f0738a..0000000000 --- a/deps/libShake/src/common/helpers.c +++ /dev/null @@ -1,93 +0,0 @@ -#include "helpers.h" -#include - -/* 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; -} diff --git a/deps/libShake/src/common/helpers.h b/deps/libShake/src/common/helpers.h deleted file mode 100644 index b571a3f1f4..0000000000 --- a/deps/libShake/src/common/helpers.h +++ /dev/null @@ -1,16 +0,0 @@ -#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)(void *item)); -ListElement *listElementDeleteAll(ListElement *head, void(*itemDel)(void *item)); -ListElement *listElementGet(ListElement *head, unsigned int id); -unsigned int listLength(ListElement *head); - -#endif /* _HELPERS_H_ */ diff --git a/deps/libShake/src/common/presets.c b/deps/libShake/src/common/presets.c deleted file mode 100644 index 80370d998b..0000000000 --- a/deps/libShake/src/common/presets.c +++ /dev/null @@ -1,50 +0,0 @@ -#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; -} diff --git a/deps/libShake/src/linux/shake.c b/deps/libShake/src/linux/shake.c deleted file mode 100644 index e480287108..0000000000 --- a/deps/libShake/src/linux/shake.c +++ /dev/null @@ -1,422 +0,0 @@ -/* libShake - a basic haptic library */ - -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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; -} diff --git a/deps/libShake/src/linux/shake_private.h b/deps/libShake/src/linux/shake_private.h deleted file mode 100644 index 656fdc4647..0000000000 --- a/deps/libShake/src/linux/shake_private.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef _SHAKE_PRIVATE_H_ -#define _SHAKE_PRIVATE_H_ - -#include -#include -#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(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_ */ diff --git a/deps/libShake/src/osx/shake.c b/deps/libShake/src/osx/shake.c deleted file mode 100644 index 175afd8b67..0000000000 --- a/deps/libShake/src/osx/shake.c +++ /dev/null @@ -1,719 +0,0 @@ -/* libShake - a basic haptic library */ - -#include -#include -#include -#include -#include - -#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; -} diff --git a/deps/libShake/src/osx/shake_private.h b/deps/libShake/src/osx/shake_private.h deleted file mode 100644 index e96dec88ef..0000000000 --- a/deps/libShake/src/osx/shake_private.h +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef _SHAKE_PRIVATE_H_ -#define _SHAKE_PRIVATE_H_ - -#include -#include -#include "../common/helpers.h" - -#define BITS_PER_LONG (sizeof(long) * 8) -#define OFF(x) ((x)%BITS_PER_LONG) -#define BIT(x) (1UL<> 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_ */ diff --git a/griffin/griffin.c b/griffin/griffin.c index 2cf2c97c65..e375efe780 100644 --- a/griffin/griffin.c +++ b/griffin/griffin.c @@ -1037,7 +1037,8 @@ INPUT #endif /* the thread the joypad drivers write to their controllers from */ -#if defined(HAVE_XINPUT) || defined(HAVE_WINRAWINPUT) || defined(HAVE_UDEV) || defined(ANDROID) +#if defined(HAVE_XINPUT) || defined(HAVE_WINRAWINPUT) || defined(HAVE_UDEV) || defined(ANDROID) \ + || (defined(DINGUX) && defined(HAVE_SDL_DINGUX)) #include "../input/common/output_writer.c" #endif @@ -1056,15 +1057,6 @@ INPUT #include "../input/drivers_joypad/udev_joypad.c" #endif -#if defined(HAVE_LIBSHAKE) -#include "../deps/libShake/src/common/error.c" -#include "../deps/libShake/src/common/helpers.c" -#include "../deps/libShake/src/common/presets.c" -#if TARGET_OS_OSX -#include "../deps/libShake/src/osx/shake.c" -#elif defined(__linux__) || (defined(BSD) && !defined(__MACH__)) -#include "../deps/libShake/src/linux/shake.c" -#endif #endif #ifdef HAVE_TEST_DRIVERS diff --git a/input/common/linux_ff.h b/input/common/linux_ff.h new file mode 100644 index 0000000000..a9f06bfd15 --- /dev/null +++ b/input/common/linux_ff.h @@ -0,0 +1,169 @@ +/* RetroArch - A frontend for libretro. + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#ifndef _LINUX_FF_H +#define _LINUX_FF_H + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* A vibration motor behind an evdev node, driven with a periodic sine + * effect: what a handheld whose motor is not part of a gamepad (the + * OpenDingux ones) offers. Each call is a syscall to the motor's + * driver, so a joypad driver makes them from its output writer + * (output_writer.h), never on a frame. Nothing is allocated. */ + +#ifndef O_CLOEXEC +#define O_CLOEXEC 0 +#endif + +/* /dev/input/event0 to this, looked through for a motor */ +#define LINUX_FF_EVENT_NODES 32 +/* the largest magnitude of a periodic effect */ +#define LINUX_FF_MAGNITUDE_MAX 0x7FFF + +#define LINUX_FF_BITS_LONG (8 * sizeof(unsigned long)) + +struct linux_ff_motor +{ + int id; /* the uploaded effect, or -1 */ + uint16_t strength; /* the strength it was uploaded with */ + bool playing; +}; + +static INLINE void linux_ff_motor_init(struct linux_ff_motor *m) +{ + m->id = -1; + m->strength = 0; + m->playing = false; +} + +static INLINE bool linux_ff_has(const unsigned long *bits, unsigned bit) +{ + return (bits[bit / LINUX_FF_BITS_LONG] >> (bit % LINUX_FF_BITS_LONG)) & 1; +} + +/* The first event node that plays a periodic sine and holds @effects + * of them at once, open for writing; -1 for none. @gain: it takes + * FF_GAIN. */ +static INLINE int linux_ff_open_sine(int effects, bool *gain) +{ + unsigned n; + + *gain = false; + for (n = 0; n < LINUX_FF_EVENT_NODES; n++) + { + unsigned long ff[(FF_MAX + LINUX_FF_BITS_LONG) / LINUX_FF_BITS_LONG]; + char path[32]; + int capacity = 0; + int fd; + + snprintf(path, sizeof(path), "/dev/input/event%u", n); + if ((fd = open(path, O_RDWR | O_CLOEXEC)) < 0) + continue; + memset(ff, 0, sizeof(ff)); + if ( ioctl(fd, EVIOCGBIT(EV_FF, sizeof(ff)), ff) >= 0 + && linux_ff_has(ff, FF_PERIODIC) + && linux_ff_has(ff, FF_SINE) + && ioctl(fd, EVIOCGEFFECTS, &capacity) >= 0 + && capacity >= effects) + { + *gain = linux_ff_has(ff, FF_GAIN); + return fd; + } + close(fd); + } + return -1; +} + +static INLINE bool linux_ff_event(int fd, uint16_t code, int32_t value) +{ + struct input_event ie; + memset(&ie, 0, sizeof(ie)); + ie.type = EV_FF; + ie.code = code; + ie.value = value; + return write(fd, &ie, sizeof(ie)) == (ssize_t)sizeof(ie); +} + +/* @percent of the motor's strength, 0 to 100 */ +static INLINE bool linux_ff_gain(int fd, unsigned percent) +{ + if (percent > 100) + percent = 100; + return linux_ff_event(fd, FF_GAIN, (int32_t)(0xFFFFu * percent / 100)); +} + +/* Runs @m at @strength, 0 to 0xFFFF, as a sine of @period_ms. A new + * strength is uploaded over the effect, which the kernel changes as + * it plays; 0 stops it. False, and @m as it was, if a call failed. */ +static INLINE bool linux_ff_motor_set(int fd, struct linux_ff_motor *m, + uint16_t period_ms, uint16_t strength) +{ + if (!strength) + { + if (m->playing) + { + if (!linux_ff_event(fd, (uint16_t)m->id, 0)) + return false; + m->playing = false; + } + return true; + } + + if (m->id < 0 || strength != m->strength) + { + struct ff_effect e; + memset(&e, 0, sizeof(e)); + e.type = FF_PERIODIC; + e.id = (int16_t)m->id; + e.u.periodic.waveform = FF_SINE; + e.u.periodic.period = period_ms; + e.u.periodic.magnitude = (int16_t)( + (LINUX_FF_MAGNITUDE_MAX * (uint32_t)strength) / 0xFFFFu); + if (ioctl(fd, EVIOCSFF, &e) < 0) + return false; + m->id = e.id; + m->strength = strength; + } + + if (!m->playing) + { + if (!linux_ff_event(fd, (uint16_t)m->id, 1)) + return false; + m->playing = true; + } + return true; +} + +/* Stops @m and lets the kernel have its effect back */ +static INLINE void linux_ff_motor_erase(int fd, struct linux_ff_motor *m) +{ + if (m->id < 0) + return; + if (m->playing) + linux_ff_event(fd, (uint16_t)m->id, 0); + ioctl(fd, EVIOCRMFF, m->id); + linux_ff_motor_init(m); +} + +#endif diff --git a/input/common/output_writer.c b/input/common/output_writer.c index 40014a389e..4da36700a5 100644 --- a/input/common/output_writer.c +++ b/input/common/output_writer.c @@ -38,6 +38,9 @@ struct input_output_writer void *userdata; sthread_t *thread; retro_atomic_int_t quit; + /* a wake is on its way and the writer has yet to look: another + * needs no syscall */ + retro_atomic_int_t pending; #ifdef _WIN32 HANDLE wake; #else @@ -118,6 +121,8 @@ static void input_output_writer_thread(void *data) quit = true; } #endif + /* before the driver looks, so a wake from here on is not lost */ + retro_atomic_exchange_int(&writer->pending, 0); if (retro_atomic_load_acquire_int(&writer->quit)) quit = true; writer->cb(writer->userdata, quit); @@ -143,6 +148,7 @@ input_output_writer_t *input_output_writer_new_timed( writer->cb = cb; writer->userdata = userdata; retro_atomic_store_release_int(&writer->quit, 0); + retro_atomic_store_release_int(&writer->pending, 0); #ifdef _WIN32 if (!(writer->wake = CreateEventA(NULL, FALSE, FALSE, NULL))) @@ -175,10 +181,8 @@ input_output_writer_t *input_output_writer_new_timed( return writer; } -void input_output_writer_wake(input_output_writer_t *writer) +static void input_output_writer_signal(input_output_writer_t *writer) { - if (!writer) - return; #ifdef _WIN32 SetEvent(writer->wake); #else @@ -193,12 +197,18 @@ void input_output_writer_wake(input_output_writer_t *writer) #endif } +void input_output_writer_wake(input_output_writer_t *writer) +{ + if (writer && !retro_atomic_exchange_int(&writer->pending, 1)) + input_output_writer_signal(writer); +} + void input_output_writer_free(input_output_writer_t *writer) { if (!writer) return; retro_atomic_store_release_int(&writer->quit, 1); - input_output_writer_wake(writer); + input_output_writer_signal(writer); sthread_join(writer->thread); #ifdef _WIN32 CloseHandle(writer->wake); diff --git a/input/common/output_writer.h b/input/common/output_writer.h index a467c527a1..8736220de1 100644 --- a/input/common/output_writer.h +++ b/input/common/output_writer.h @@ -32,7 +32,8 @@ RETRO_BEGIN_DECLS * Nothing is queued here and nothing is passed through: a wake says * only that there is something to look at. Wakes that arrive while the * thread is writing fold into one, so a controller slow to answer is - * given the latest of what is wanted and not each value in turn. + * given the latest of what is wanted and not each value in turn; a + * wake while one is still on its way makes no syscall. * * There is no lock. The thread is woken through an event (Windows) or * a pipe. */ diff --git a/input/drivers_joypad/sdl_dingux_joypad.c b/input/drivers_joypad/sdl_dingux_joypad.c index 6ccbe06307..1ab23d9f75 100644 --- a/input/drivers_joypad/sdl_dingux_joypad.c +++ b/input/drivers_joypad/sdl_dingux_joypad.c @@ -26,10 +26,11 @@ #include "../../tasks/tasks_internal.h" #include "../../verbosity.h" -#if defined(HAVE_LIBSHAKE) -#include -#include "../../configuration.h" -#include "../../config.def.h" +#if defined(__linux__) +#define SDL_DINGUX_HAS_RUMBLE 1 +#include +#include "../common/linux_ff.h" +#include "../common/output_writer.h" #endif /* RS-90 devices: @@ -123,7 +124,7 @@ #define SDL_DINGUX_SDLK_MENU SDLK_HOME #endif -#if defined(HAVE_LIBSHAKE) +#if defined(SDL_DINGUX_HAS_RUMBLE) /* 5 ms period == 200 Hz * > Meissner's Corpuscle registers this * as 'fast' motion */ @@ -134,20 +135,21 @@ * this as 'slow' motion */ #define SDL_DINGUX_RUMBLE_STRONG_PERIOD 142 +/* The motor is written from the output writer's thread: the frontend's + * notes what it wants, the strong strength over the weak in one word, + * and the gain beside it. The motor's state is the writer's. */ 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; + input_output_writer_t *writer; + struct linux_ff_motor motor[2]; /* [RETRO_RUMBLE_STRONG], [RETRO_RUMBLE_WEAK] */ + int fd; + int gain; /* written, or -1 */ + uint32_t want; /* the frontend's last asked */ + bool has_gain; } dingux_joypad_rumble_t; + +static retro_atomic_int_t dingux_rumble_want; +static retro_atomic_int_t dingux_rumble_want_gain; #endif typedef struct @@ -155,7 +157,7 @@ typedef struct #if defined(SDL_DINGUX_HAS_ANALOG) SDL_Joystick *device; #endif -#if defined(HAVE_LIBSHAKE) +#if defined(SDL_DINGUX_HAS_RUMBLE) dingux_joypad_rumble_t rumble; #endif unsigned num_axes; @@ -169,171 +171,117 @@ typedef struct static dingux_joypad_t dingux_joypad; -#if defined(HAVE_LIBSHAKE) -static bool sdl_dingux_rumble_init(dingux_joypad_rumble_t *rumble) +#if defined(SDL_DINGUX_HAS_RUMBLE) +/* What is wanted of the motor, written if it differs from what was. On + * the writer's thread; on the frontend's only when there is no writer. + * @last: the motor is let go. */ +static void sdl_dingux_rumble_write(void *data, bool last) { - unsigned rumble_gain = input_config_get_rumble_gain(); - bool weak_uploaded = false; - bool strong_uploaded = false; + dingux_joypad_rumble_t *rumble = (dingux_joypad_rumble_t*)data; + uint32_t want; + int gain; - 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->strong.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: - RARCH_WARN("[libShake] Input device does not support rumble effects.\n"); - - if (rumble->device) + if (last) { - 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; + linux_ff_motor_erase(rumble->fd, &rumble->motor[RETRO_RUMBLE_STRONG]); + linux_ff_motor_erase(rumble->fd, &rumble->motor[RETRO_RUMBLE_WEAK]); + close(rumble->fd); + rumble->fd = -1; + return; } - return false; + gain = retro_atomic_load_acquire_int(&dingux_rumble_want_gain); + if ( rumble->has_gain + && gain != rumble->gain + && linux_ff_gain(rumble->fd, (unsigned)gain)) + rumble->gain = gain; + + want = (uint32_t)retro_atomic_load_acquire_int(&dingux_rumble_want); + linux_ff_motor_set(rumble->fd, &rumble->motor[RETRO_RUMBLE_STRONG], + SDL_DINGUX_RUMBLE_STRONG_PERIOD, (uint16_t)(want >> 16)); + linux_ff_motor_set(rumble->fd, &rumble->motor[RETRO_RUMBLE_WEAK], + SDL_DINGUX_RUMBLE_WEAK_PERIOD, (uint16_t)want); } -static bool sdl_dingux_rumble_update(Shake_Device *device, - dingux_joypad_rumble_effect_t *effect, - uint16_t strength, uint16_t max_strength) +static void sdl_dingux_rumble_wake(dingux_joypad_rumble_t *rumble) { - /* If strength is zero, halt rumble effect */ - if (strength == 0) - { - if (effect->active) - { - if (Shake_Stop(device, effect->id) != SHAKE_OK) - return false; - effect->active = false; - } + if (rumble->writer) + input_output_writer_wake(rumble->writer); + else + sdl_dingux_rumble_write(rumble, false); +} - return true; - } +static void sdl_dingux_rumble_init(dingux_joypad_rumble_t *rumble) +{ + rumble->fd = linux_ff_open_sine(2, &rumble->has_gain); + if (rumble->fd < 0) + return; + linux_ff_motor_init(&rumble->motor[RETRO_RUMBLE_STRONG]); + linux_ff_motor_init(&rumble->motor[RETRO_RUMBLE_WEAK]); + rumble->gain = -1; + rumble->want = 0; + retro_atomic_store_release_int(&dingux_rumble_want, 0); + retro_atomic_store_release_int(&dingux_rumble_want_gain, + (int)input_config_get_rumble_gain()); + rumble->writer = input_output_writer_new(sdl_dingux_rumble_write, rumble); + /* the gain, written as the motor is found */ + sdl_dingux_rumble_wake(rumble); +} - /* If strength has changed, update effect */ - if (strength != effect->strength) - { - int id; - - 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; - effect->strength = strength; - } - - /* If effect is currently idle, activate it */ - if (!effect->active) - { - if (Shake_Play(device, effect->id) != SHAKE_OK) - return false; - effect->active = true; - } - - return true; +static void sdl_dingux_rumble_deinit(dingux_joypad_rumble_t *rumble) +{ + if (rumble->fd < 0) + return; + if (rumble->writer) + input_output_writer_free(rumble->writer); + else + sdl_dingux_rumble_write(rumble, true); + rumble->writer = NULL; + rumble->fd = -1; } 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; + dingux_joypad_rumble_t *rumble = &dingux_joypad.rumble; + uint32_t want; - if ((pad != 0) || !joypad->rumble.device) + if (pad != 0 || rumble->fd < 0) 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: + want = (rumble->want & 0xFFFFu) | ((uint32_t)strength << 16); break; + case RETRO_RUMBLE_WEAK: + want = (rumble->want & 0xFFFF0000u) | strength; + break; + default: + return false; } - return false; + /* a core asking the same each frame costs nothing */ + if (want != rumble->want) + { + rumble->want = want; + retro_atomic_store_release_int(&dingux_rumble_want, (int)want); + sdl_dingux_rumble_wake(rumble); + } + return true; } static bool sdl_dingux_joypad_set_rumble_gain(unsigned pad, unsigned gain) { - dingux_joypad_t *joypad = (dingux_joypad_t*)&dingux_joypad; + dingux_joypad_rumble_t *rumble = &dingux_joypad.rumble; - if ((pad != 0) || !joypad->rumble.device) + if (pad != 0 || rumble->fd < 0 || !rumble->has_gain) return false; - - /* Gain is automatically capped by Shake_SetGain(), - * but do it explicitly here for clarity */ if (gain > 100) gain = 100; - - /* Set gain */ - if (Shake_QueryGainSupport(joypad->rumble.device)) - if (Shake_SetGain(joypad->rumble.device, (int)gain) == SHAKE_OK) - return true; - - return false; + retro_atomic_store_release_int(&dingux_rumble_want_gain, (int)gain); + sdl_dingux_rumble_wake(rumble); + return true; } #endif @@ -361,8 +309,7 @@ static void sdl_dingux_joypad_connect(void) joypad->num_axes = SDL_JoystickNumAxes(joypad->device); #endif -#if defined(HAVE_LIBSHAKE) - /* Configure rumble interface */ +#if defined(SDL_DINGUX_HAS_RUMBLE) sdl_dingux_rumble_init(&joypad->rumble); #endif @@ -391,23 +338,14 @@ 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); - } +#if defined(SDL_DINGUX_HAS_RUMBLE) + sdl_dingux_rumble_deinit(&joypad->rumble); #endif memset(joypad, 0, sizeof(dingux_joypad_t)); +#if defined(SDL_DINGUX_HAS_RUMBLE) + joypad->rumble.fd = -1; +#endif } static void sdl_dingux_joypad_destroy(void) @@ -420,11 +358,6 @@ 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 - #if defined(SDL_DINGUX_HAS_MENU_TOGGLE) input_driver_set_platform_menu_button(false); #endif @@ -436,6 +369,9 @@ static void *sdl_dingux_joypad_init(void *data) uint32_t sdl_subsystem_flags = SDL_WasInit(0); memset(joypad, 0, sizeof(dingux_joypad_t)); +#if defined(SDL_DINGUX_HAS_RUMBLE) + joypad->rumble.fd = -1; +#endif #if defined(SDL_DINGUX_HAS_MENU_TOGGLE) input_driver_set_platform_menu_button(false); #endif @@ -454,11 +390,6 @@ static void *sdl_dingux_joypad_init(void *data) } #endif -#if defined(HAVE_LIBSHAKE) - /* Initialise rumble interface */ - Shake_Init(); -#endif - /* Connect joypad */ sdl_dingux_joypad_connect(); @@ -782,7 +713,7 @@ input_device_driver_t sdl_dingux_joypad = { sdl_dingux_joypad_get_buttons, sdl_dingux_joypad_axis, sdl_dingux_joypad_poll, -#if defined(HAVE_LIBSHAKE) +#if defined(SDL_DINGUX_HAS_RUMBLE) sdl_dingux_joypad_set_rumble, sdl_dingux_joypad_set_rumble_gain, #else diff --git a/qb/config.params.sh b/qb/config.params.sh index 81bfb53d01..f7ed6ca666 100644 --- a/qb/config.params.sh +++ b/qb/config.params.sh @@ -213,7 +213,6 @@ HAVE_WGI=auto # Windows.Gaming.Input for Xbox pads on the HID drive HAVE_LIBUSB=auto # Libusb HID support C89_LIBUSB=no HAVE_BLISSBOX=auto # Blissbox support -HAVE_LIBSHAKE=no # libShake haptic feedback support HAVE_RPILED=auto # RPI led support HAVE_TEST_DRIVERS=yes # Test input driver diff --git a/samples/input/dingux_rumble/Makefile b/samples/input/dingux_rumble/Makefile new file mode 100644 index 0000000000..9f9e90cde8 --- /dev/null +++ b/samples/input/dingux_rumble/Makefile @@ -0,0 +1,46 @@ +TARGET := dingux_rumble_test + +# The real Dingux joypad driver, given a stand-in /dev/input and SDL: +# its vibration motor is found on the node that plays a sine and +# written from the driver's own thread. Built with threads and without. +# make && make check +REPO_ROOT := ../../.. +LRC := $(REPO_ROOT)/libretro-common + +CFLAGS += -Wall -Wno-unused -std=gnu99 -g1 -O1 \ + -DRARCH_INTERNAL -DDINGUX -DHAVE_SDL_DINGUX \ + -I. -I$(REPO_ROOT) -I$(REPO_ROOT)/input/include \ + -I$(REPO_ROOT)/gfx/include \ + -I$(LRC)/include -I$(REPO_ROOT)/deps + +ifneq ($(SANITIZER),) + CFLAGS += -fsanitize=$(SANITIZER) -fno-omit-frame-pointer + LDFLAGS += -fsanitize=$(SANITIZER) +endif + +COMMON := $(REPO_ROOT)/input/common/output_writer.c + +# The motor is evdev's, so Linux's: elsewhere there is nothing to +# test, and the targets say so and succeed. +ifeq ($(shell uname -s 2>/dev/null),Linux) +all: $(TARGET) $(TARGET)_nothreads + +$(TARGET): dingux_rumble_test.c $(REPO_ROOT)/input/drivers_joypad/sdl_dingux_joypad.c $(REPO_ROOT)/input/common/linux_ff.h + $(CC) $(CFLAGS) -DHAVE_THREADS $(LDFLAGS) -o $@ dingux_rumble_test.c \ + $(COMMON) $(LRC)/rthreads/rthreads.c -lpthread + +$(TARGET)_nothreads: dingux_rumble_test.c $(REPO_ROOT)/input/drivers_joypad/sdl_dingux_joypad.c $(REPO_ROOT)/input/common/linux_ff.h + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ dingux_rumble_test.c $(COMMON) + +check: all + ./$(TARGET) + ./$(TARGET)_nothreads +else +all check: + @echo "SKIP dingux_rumble_test: the Dingux motor is Linux's" +endif + +clean: + rm -f $(TARGET) $(TARGET)_nothreads + +.PHONY: all check clean diff --git a/samples/input/dingux_rumble/SDL/SDL.h b/samples/input/dingux_rumble/SDL/SDL.h new file mode 100644 index 0000000000..05ff78a582 --- /dev/null +++ b/samples/input/dingux_rumble/SDL/SDL.h @@ -0,0 +1,38 @@ +/* What of SDL 1.2 the Dingux joypad driver uses, stood in for: the + * test answers the calls, and builds where no SDL 1.2 is installed. */ +#ifndef DINGUX_RUMBLE_FAKE_SDL_H +#define DINGUX_RUMBLE_FAKE_SDL_H + +#include + +#define SDL_INIT_JOYSTICK 0x00000200 + +typedef enum +{ + SDLK_BACKSPACE = 8, SDLK_TAB = 9, SDLK_RETURN = 13, SDLK_ESCAPE = 27, + SDLK_SPACE = 32, SDLK_KP_PERIOD = 266, SDLK_KP_DIVIDE = 267, + SDLK_UP = 273, SDLK_DOWN = 274, SDLK_RIGHT = 275, SDLK_LEFT = 276, + SDLK_HOME = 278, SDLK_END = 279, SDLK_PAGEUP = 280, SDLK_PAGEDOWN = 281, + SDLK_RSHIFT = 303, SDLK_LSHIFT = 304, SDLK_RCTRL = 305, SDLK_LCTRL = 306, + SDLK_RALT = 307, SDLK_LALT = 308 +} SDLKey; + +enum { SDL_KEYDOWN = 2, SDL_KEYUP = 3 }; + +typedef struct { SDLKey sym; } SDL_keysym; +typedef struct { uint8_t type; SDL_keysym keysym; } SDL_KeyboardEvent; +typedef union { uint8_t type; SDL_KeyboardEvent key; } SDL_Event; +typedef struct SDL_Joystick SDL_Joystick; + +uint32_t SDL_WasInit(uint32_t flags); +int SDL_Init(uint32_t flags); +int SDL_InitSubSystem(uint32_t flags); +int SDL_NumJoysticks(void); +SDL_Joystick *SDL_JoystickOpen(int index); +int SDL_JoystickNumAxes(SDL_Joystick *joystick); +int16_t SDL_JoystickGetAxis(SDL_Joystick *joystick, int axis); +void SDL_JoystickUpdate(void); +void SDL_JoystickClose(SDL_Joystick *joystick); +int SDL_PollEvent(SDL_Event *event); + +#endif diff --git a/samples/input/dingux_rumble/dingux_rumble_test.c b/samples/input/dingux_rumble/dingux_rumble_test.c new file mode 100644 index 0000000000..64e680911c --- /dev/null +++ b/samples/input/dingux_rumble/dingux_rumble_test.c @@ -0,0 +1,344 @@ +/* The Dingux joypad driver's vibration motor, written through evdev + * from the output writer's thread. + * + * The real driver is included, with /dev/input stood in for: a few + * event nodes, of which one plays a periodic sine, and the uploads, + * plays and gains made to it noted. An upload can be made slow, as a + * motor's driver can be, to show the frontend's call does not wait. + * Built twice, with threads and without: without, the same writes are + * made on the caller's thread. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* ---- /dev/input, stood in for --------------------------------------- */ + +#define FAKE_FD 1000 +#define NODES 4 + +/* what each node is: 0 nothing, 1 rumble only, 2 a periodic sine with + * gain, 3 the same again; @motor_gain off makes 2 a motor without */ +static int node_kind[NODES] = { 0, 1, 2, 3 }; +static int motor_gain = 1; +static int opened[NODES]; /* open now */ +static int slow_ms; +/* written on the writer's thread, read on the test's: only through + * the atomics below */ +static int uploads, plays, stops, gains, erases; +static int last_period, last_magnitude, last_id_in, last_gain; +static int next_id = 3; /* the writer's thread's alone */ + +#define A_SET(v, x) __atomic_store_n(&(v), (x), __ATOMIC_SEQ_CST) +#define A_INC(v) __atomic_fetch_add(&(v), 1, __ATOMIC_SEQ_CST) +#define N(v) __atomic_load_n(&(v), __ATOMIC_SEQ_CST) + +static int node_of(int fd) +{ + int n = fd - FAKE_FD; + return (n >= 0 && n < NODES && N(opened[n])) ? n : -1; +} + +static int test_open(const char *path, int flags, ...) +{ + unsigned n; + (void)flags; + if (sscanf(path, "/dev/input/event%u", &n) != 1 || n >= NODES) + { + errno = ENOENT; + return -1; + } + A_SET(opened[n], 1); + return FAKE_FD + (int)n; +} + +static int test_close(int fd) +{ + int n = node_of(fd); + if (n < 0) + return close(fd); + A_SET(opened[n], 0); + return 0; +} + +static void set_bit_in(unsigned long *bits, unsigned bit) +{ + bits[bit / (8 * sizeof(long))] |= 1UL << (bit % (8 * sizeof(long))); +} + +static int test_ioctl(int fd, unsigned long req, uintptr_t arg) +{ + int n = node_of(fd); + if (n < 0) + { + errno = EBADF; + return -1; + } + if (req == EVIOCGBIT(EV_FF, sizeof(unsigned long) * ((FF_MAX + 8 * sizeof(long)) / (8 * sizeof(long))))) + { + unsigned long *bits = (unsigned long*)arg; + memset(bits, 0, sizeof(unsigned long) * ((FF_MAX + 8 * sizeof(long)) / (8 * sizeof(long)))); + if (node_kind[n] >= 1) + set_bit_in(bits, FF_RUMBLE); + if (node_kind[n] >= 2) + { + set_bit_in(bits, FF_PERIODIC); + set_bit_in(bits, FF_SINE); + if (motor_gain) + set_bit_in(bits, FF_GAIN); + } + return 0; + } + if (req == EVIOCGEFFECTS) + { + *(int*)arg = node_kind[n] ? 16 : 0; + return 0; + } + if (req == EVIOCSFF) + { + struct ff_effect *e = (struct ff_effect*)arg; + if (slow_ms) + { + struct timespec ts; + ts.tv_sec = slow_ms / 1000; + ts.tv_nsec = (slow_ms % 1000) * 1000000L; + nanosleep(&ts, NULL); + } + if (e->type != FF_PERIODIC || e->u.periodic.waveform != FF_SINE) + { + errno = EINVAL; + return -1; + } + A_SET(last_id_in, e->id); + if (e->id < 0) + e->id = (short)next_id++; + A_SET(last_period, e->u.periodic.period); + A_SET(last_magnitude, e->u.periodic.magnitude); + A_INC(uploads); + return 0; + } + if (req == EVIOCRMFF) + { + A_INC(erases); + return 0; + } + errno = ENOTTY; + return -1; +} + +static ssize_t test_write(int fd, const void *buf, size_t count) +{ + const struct input_event *ie = (const struct input_event*)buf; + if (node_of(fd) < 0) + return write(fd, buf, count); + if (count != sizeof(*ie) || ie->type != EV_FF) + { + errno = EINVAL; + return -1; + } + if (ie->code == FF_GAIN) + { + A_SET(last_gain, ie->value); + A_INC(gains); + } + else if (ie->value) + A_INC(plays); + else + A_INC(stops); + return (ssize_t)count; +} + +#define open(...) test_open(__VA_ARGS__) +#define close(fd) test_close((fd)) +#define write(fd, buf, count) test_write((fd), (buf), (count)) +#define ioctl(fd, req, arg) test_ioctl((fd), (unsigned long)(req), (uintptr_t)(arg)) + +#include "input/drivers_joypad/sdl_dingux_joypad.c" +#undef open +#undef close +#undef write +#undef ioctl + +/* ---- what the driver links against --------------------------------- */ + +static unsigned stub_gain = 50; +unsigned input_config_get_rumble_gain(void) { return stub_gain; } +void RARCH_LOG(const char *fmt, ...) { (void)fmt; } +void RARCH_WARN(const char *fmt, ...) { (void)fmt; } +void RARCH_ERR(const char *fmt, ...) { (void)fmt; } +bool input_autoconfigure_connect(const char *name, const char *display_name, + const char *phys, const char *driver, unsigned port, unsigned vid, + unsigned pid) +{ (void)name; (void)display_name; (void)phys; (void)driver; (void)port; (void)vid; (void)pid; return true; } +bool input_autoconfigure_disconnect(unsigned port, const char *name) +{ (void)port; (void)name; return true; } +void input_driver_set_platform_menu_button(bool v) { (void)v; } +void input_driver_set_platform_menu_pressed(bool v) { (void)v; } +bool input_driver_platform_menu_button(void) { return false; } + +uint32_t SDL_WasInit(uint32_t flags) { (void)flags; return SDL_INIT_JOYSTICK; } +int SDL_Init(uint32_t flags) { (void)flags; return 0; } +int SDL_InitSubSystem(uint32_t flags) { (void)flags; return 0; } +int SDL_NumJoysticks(void) { return 0; } +SDL_Joystick *SDL_JoystickOpen(int index) { (void)index; return NULL; } +int SDL_JoystickNumAxes(SDL_Joystick *j) { (void)j; return 0; } +int16_t SDL_JoystickGetAxis(SDL_Joystick *j, int axis) { (void)j; (void)axis; return 0; } +void SDL_JoystickUpdate(void) { } +void SDL_JoystickClose(SDL_Joystick *j) { (void)j; } +int SDL_PollEvent(SDL_Event *event) { (void)event; return 0; } + +/* ---- the test ------------------------------------------------------- */ + +static unsigned failures; +#define CHECK(cond, ...) do { \ + if (!(cond)) { printf(" FAIL "); printf(__VA_ARGS__); printf("\n"); failures++; } \ +} while (0) + +/* waits for @v to reach @want: at once where there is no writer */ +static void settle(int *v, int want) +{ + int i; + for (i = 0; i < 600 && N(*v) < want; i++) + usleep(5000); +} + +/* something that must not come is given this long to */ +static void quiet(void) { usleep(50000); } + +int main(void) +{ + int i, before, strong_id; + + /* the motor found: the first node that plays a sine, the others + * looked at and let go; its gain written as it is found */ + sdl_dingux_joypad_init(NULL); + CHECK(dingux_joypad.rumble.fd == FAKE_FD + 2, + "the motor was not found on the first node that plays a sine (%d)", + dingux_joypad.rumble.fd); + CHECK(!N(opened[0]) && !N(opened[1]) && N(opened[2]) && !N(opened[3]), + "the nodes looked at were not let go"); +#ifdef HAVE_THREADS + CHECK(dingux_joypad.rumble.writer != NULL, "the rumble writer did not start"); +#endif + settle(&gains, 1); + CHECK(N(gains) == 1 && N(last_gain) == 0xFFFF * 50 / 100, + "the gain was not written as the motor was found (%d, 0x%x)", + N(gains), N(last_gain)); + printf(" ok the motor is the first node that plays a sine, its gain written\n"); + + /* strong: a 7 Hz sine at full strength, uploaded then played */ + CHECK(sdl_dingux_joypad_set_rumble(0, RETRO_RUMBLE_STRONG, 0xFFFF), + "rumble was refused"); + settle(&plays, 1); + CHECK(N(uploads) == 1 && N(last_id_in) == -1 && N(last_period) == 142 + && N(last_magnitude) == 0x7FFF && N(plays) == 1, + "the strong motor was not uploaded and played (%d %d %d 0x%x %d)", + N(uploads), N(last_id_in), N(last_period), N(last_magnitude), N(plays)); + strong_id = dingux_joypad.rumble.motor[RETRO_RUMBLE_STRONG].id; + + /* weak: a 200 Hz sine at half */ + CHECK(sdl_dingux_joypad_set_rumble(0, RETRO_RUMBLE_WEAK, 0x8000), + "rumble was refused"); + settle(&plays, 2); + CHECK(N(uploads) == 2 && N(last_period) == 5 + && N(last_magnitude) == (0x7FFF * 0x8000) / 0xFFFF && N(plays) == 2, + "the weak motor was not uploaded and played"); + printf(" ok strong and weak: their sines uploaded at their strengths and played\n"); + + /* the same again, as a core asks each frame: nothing written */ + before = N(uploads) + N(plays) + N(stops); + for (i = 0; i < 100; i++) + { + sdl_dingux_joypad_set_rumble(0, RETRO_RUMBLE_STRONG, 0xFFFF); + sdl_dingux_joypad_set_rumble(0, RETRO_RUMBLE_WEAK, 0x8000); + } + quiet(); + CHECK(N(uploads) + N(plays) + N(stops) == before, + "an unchanged strength was written again"); + + /* a new strength while playing: uploaded over the same effect */ + sdl_dingux_joypad_set_rumble(0, RETRO_RUMBLE_STRONG, 0x4000); + settle(&uploads, 3); + quiet(); + CHECK(N(uploads) == 3 && N(last_id_in) == strong_id && N(plays) == 2, + "a new strength was not uploaded over the playing effect"); + + /* stopped, then started at the strength it had: a play, no upload */ + sdl_dingux_joypad_set_rumble(0, RETRO_RUMBLE_STRONG, 0); + settle(&stops, 1); + sdl_dingux_joypad_set_rumble(0, RETRO_RUMBLE_STRONG, 0x4000); + settle(&plays, 3); + quiet(); + CHECK(N(stops) == 1 && N(plays) == 3 && N(uploads) == 3, + "a stop and a restart at the same strength were not a stop and a play"); + printf(" ok unchanged strengths cost nothing; a change, a stop and a restart are written as they should be\n"); + + /* the gain changed */ + CHECK(sdl_dingux_joypad_set_rumble_gain(0, 100), "the gain was refused"); + settle(&gains, 2); + CHECK(N(last_gain) == 0xFFFF, "the new gain was not written (0x%x)", N(last_gain)); + CHECK(!sdl_dingux_joypad_set_rumble(1, RETRO_RUMBLE_STRONG, 0xFFFF), + "rumble was accepted for a second pad"); + +#ifdef HAVE_THREADS + /* a motor slow to take an upload: the call returns before it has */ + slow_ms = 300; + before = N(uploads); + sdl_dingux_joypad_set_rumble(0, RETRO_RUMBLE_WEAK, 0x1000); + CHECK(N(uploads) == before, "the call waited for a motor that takes 300 ms"); + settle(&uploads, before + 1); + CHECK(N(uploads) == before + 1 && N(last_magnitude) == (0x7FFF * 0x1000) / 0xFFFF, + "the strength did not arrive"); + slow_ms = 0; + printf(" ok a motor 300 ms slow to take an upload: the call returns at once\n"); +#endif + + /* the driver stopped: both stopped, given back, the node closed */ + sdl_dingux_joypad_destroy(); + CHECK(N(stops) == 3 && N(erases) == 2 && !N(opened[2]), + "the motor was not stopped and let go (%d %d %d)", + N(stops), N(erases), N(opened[2])); + CHECK(dingux_joypad.rumble.fd == -1, "the driver kept the motor's descriptor"); + CHECK(!sdl_dingux_joypad_set_rumble(0, RETRO_RUMBLE_STRONG, 0xFFFF), + "rumble was accepted with the driver stopped"); + printf(" ok stopping the driver stops both motors and lets the node go\n"); + + /* a motor without gain: none written, and the gain call refused */ + motor_gain = 0; + before = N(gains); + sdl_dingux_joypad_init(NULL); + quiet(); + CHECK(dingux_joypad.rumble.fd == FAKE_FD + 2 && N(gains) == before, + "a gain was written to a motor that has none"); + CHECK(!sdl_dingux_joypad_set_rumble_gain(0, 80), + "a gain was accepted for a motor that has none"); + sdl_dingux_joypad_destroy(); + + /* no motor at all */ + node_kind[2] = node_kind[3] = 1; + sdl_dingux_joypad_init(NULL); + CHECK(dingux_joypad.rumble.fd == -1, "a motor was found where none plays a sine"); + CHECK(!sdl_dingux_joypad_set_rumble(0, RETRO_RUMBLE_STRONG, 0xFFFF), + "rumble was accepted with no motor"); + for (i = 0; i < NODES; i++) + CHECK(!N(opened[i]), "node %d was left open", i); + sdl_dingux_joypad_destroy(); + printf(" ok a motor without gain, and none at all\n"); + + if (failures) + { + printf("FAIL dingux_rumble_test: %u\n", failures); + return 1; + } + printf("PASS dingux_rumble_test\n"); + return 0; +} diff --git a/samples/input/xinput_rumble/xinput_rumble_test.c b/samples/input/xinput_rumble/xinput_rumble_test.c index 730e13671b..84b6a0d099 100644 --- a/samples/input/xinput_rumble/xinput_rumble_test.c +++ b/samples/input/xinput_rumble/xinput_rumble_test.c @@ -83,17 +83,19 @@ int main(void) xinput_rumble_start(); CHECK(xinput_rumble_writer != NULL, "the rumble writer did not start"); - /* a controller slow to answer: the call returns at once, and the - * strength still arrives */ - slow_ms = 60; + /* a controller slow to answer: the call returns before it has, + * and the strength still arrives. Said by the write not being done + * yet as the call returns, not by a time, which a loaded machine + * can overrun. */ + slow_ms = 300; t0 = now_ms(); CHECK(xinput_joypad_rumble(1, RETRO_RUMBLE_STRONG, 0x8000), "rumble was refused"); took = now_ms() - t0; - CHECK(took < 20.0, "the call waited for a controller that takes 60 ms to answer"); + CHECK(N(calls) == 0, "the call waited for a controller that takes 300 ms to answer"); settle(1); CHECK(N(calls) == 1 && N(last_user) == 1 && N(last_left) == 0x8000 && N(last_right) == 0, "the strength did not arrive"); - printf(" ok a controller that takes 60 ms to answer: the call returns in %.2f ms and the strength still arrives\n", took); + printf(" ok a controller that takes 300 ms to answer: the call returns in %.2f ms and the strength still arrives\n", took); /* many strengths while the controller is busy: it is given the * latest, not each in turn */ @@ -102,11 +104,13 @@ int main(void) for (i = 1; i <= 20; i++) xinput_joypad_rumble(1, RETRO_RUMBLE_STRONG, (uint16_t)(0x1000 + i)); took = now_ms() - t0; + /* a call that waited would have seen the first write done */ + CHECK(N(calls) == before, "twenty calls waited for the controller"); settle(before + 1); - Sleep(200); + for (i = 0; i < 400 && N(last_left) != 0x1000 + 20; i++) + Sleep(5); CHECK(N(last_left) == 0x1000 + 20, "the last strength written is not the last one asked for"); CHECK(N(calls) - before < 20, "every strength was written one by one"); - CHECK(took < 20.0, "twenty calls waited for the controller"); printf(" ok twenty strengths in %.2f ms: %ld written, the last one last\n", took, (long)(N(calls) - before)); slow_ms = 0;