From 45dc0a99c36564d8fb8761eabee99f88063657a7 Mon Sep 17 00:00:00 2001 From: LibretroAdmin Date: Wed, 28 Oct 2026 07:51:28 +0100 Subject: [PATCH] input: Dingux rumble through evdev and the output writer; drop libShake libShake was used by one driver, sdl_dingux, for its vibration motor, and linked on webOS for nothing. What it did there is a few evdev calls: find the node that plays a periodic sine, upload a sine per motor, play and stop it, set the gain. input/common/linux_ff.h does those with no allocation (libShake scanned /dev/input with scandir and malloc'd a node per entry) and none of its bugs (an open that failed was taken as success). The uploads and plays now go out from the shared output writer, as udev's and XInput's rumble do, so a motor's driver never holds the frame; the frontend's thread notes the strengths, both in one word, and returns. A core asking the same strength each frame costs no call at all. On MIPS (-O2, mips32) that path is 15 to 19 instructions with no stack frame, where it was about 32 with a frame and four spills. Every SDL Dingux target gets the motor now, not only the three that built libShake; one without a motor finds none and has no rumble, as before. The output writer makes no syscall for a wake while one is still on its way, which every driver on it gains from. xinput_rumble_test says a call did not wait by the write not being done yet, not by a time a loaded runner can overrun. samples/input/dingux_rumble runs the real driver against a stand-in /dev/input and SDL, with threads and without. --- .github/workflows/Linux-samples-input.yml | 17 + Makefile.common | 24 +- Makefile.dingux | 1 - Makefile.lfx000 | 1 - Makefile.miyoo | 1 - Makefile.retrofw | 1 - Makefile.rg350 | 1 - Makefile.rg350_odbeta | 1 - Makefile.rs90 | 1 - Makefile.webos | 1 - config.def.h | 2 +- deps/libShake/LICENSE.txt | 23 - deps/libShake/Makefile | 72 -- deps/libShake/README.md | 44 -- deps/libShake/examples/Makefile | 28 - deps/libShake/examples/deviceTest.c | 333 -------- deps/libShake/examples/listDevices.c | 51 -- deps/libShake/examples/simple.c | 40 - .../buildroot-package/libshake/Config.in | 6 - .../buildroot-package/libshake/libshake.mk | 26 - deps/libShake/include/shake.h | 522 ------------- deps/libShake/src/common/error.c | 14 - deps/libShake/src/common/error.h | 9 - deps/libShake/src/common/helpers.c | 93 --- deps/libShake/src/common/helpers.h | 16 - deps/libShake/src/common/presets.c | 50 -- deps/libShake/src/linux/shake.c | 422 ---------- deps/libShake/src/linux/shake_private.h | 30 - deps/libShake/src/osx/shake.c | 719 ------------------ deps/libShake/src/osx/shake_private.h | 43 -- griffin/griffin.c | 12 +- input/common/linux_ff.h | 169 ++++ input/common/output_writer.c | 18 +- input/common/output_writer.h | 3 +- input/drivers_joypad/sdl_dingux_joypad.c | 285 +++---- qb/config.params.sh | 1 - samples/input/dingux_rumble/Makefile | 46 ++ samples/input/dingux_rumble/SDL/SDL.h | 38 + .../input/dingux_rumble/dingux_rumble_test.c | 344 +++++++++ .../input/xinput_rumble/xinput_rumble_test.c | 18 +- 40 files changed, 758 insertions(+), 2768 deletions(-) delete mode 100644 deps/libShake/LICENSE.txt delete mode 100644 deps/libShake/Makefile delete mode 100644 deps/libShake/README.md delete mode 100644 deps/libShake/examples/Makefile delete mode 100644 deps/libShake/examples/deviceTest.c delete mode 100644 deps/libShake/examples/listDevices.c delete mode 100644 deps/libShake/examples/simple.c delete mode 100644 deps/libShake/extras/buildroot-package/libshake/Config.in delete mode 100644 deps/libShake/extras/buildroot-package/libshake/libshake.mk delete mode 100644 deps/libShake/include/shake.h delete mode 100644 deps/libShake/src/common/error.c delete mode 100644 deps/libShake/src/common/error.h delete mode 100644 deps/libShake/src/common/helpers.c delete mode 100644 deps/libShake/src/common/helpers.h delete mode 100644 deps/libShake/src/common/presets.c delete mode 100644 deps/libShake/src/linux/shake.c delete mode 100644 deps/libShake/src/linux/shake_private.h delete mode 100644 deps/libShake/src/osx/shake.c delete mode 100644 deps/libShake/src/osx/shake_private.h create mode 100644 input/common/linux_ff.h create mode 100644 samples/input/dingux_rumble/Makefile create mode 100644 samples/input/dingux_rumble/SDL/SDL.h create mode 100644 samples/input/dingux_rumble/dingux_rumble_test.c 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;