ALLEGRO_HAPTIC or force feedback for Allegro
beoran

Since I found out that both Linux and my cheap USB joystick support force feedback, I'm interested in developing support for force feedback, or for haptic devices for short, also in Allegro. Now, on the PC platform, and certainly on Linux, almost no game uses haptic effects since no one knows how to program them. So support for haptic devices in Allegro would be nice. Also, SDL2 will also support haptic effects, so that's another reason I don't want Allegro to fall behind.

Anyway, I started work on this, but Thomas suggested that I first post the API I suggest here, so we can discuss and improve it. So please read this proposal for allegro5/haptic.h and tell me what you like, dislike and find unclear about it. Without more ado, here is the content of the file (also attached for use at your leasure).

#SelectExpand
1/* ______ ___ ___ 2 * /\ _ \ /_ \ /_ \ 3 * \ \ \L\ \//\ \ //\ \ __ __ _ __ ___ 4 * \ \ __ \ \ \ \ \ \ \ /'__`\ /'_ `/`'__/ __`\ 5 * \ \ /\ \ _\ _ _\ _/\ __//\ \L\ \ \ //\ \L\ \ 6 * \ _\ _/____/____\ ____\ ____ \ _\\ ____/ 7 * /_//_//____//____//____//___L\ /_/ /___/ 8 * /____/ 9 * _/__/ 10 * 11 * Haptic (that is, force feedback) routines for Allegro. 12 * By Beoran (beoran@gmail.com), 2013. 13 * 14 * See readme.txt for copyright information. 15 */ 16 17#ifndef __al_included_allegro5_haptic_h 18#define __al_included_allegro5_haptic_h 19 20#include "allegro5/base.h" 21#include "allegro5/events.h" 22#include "allegro5/mouse.h" 23#include "allegro5/joystick.h" 24 25#ifdef __cplusplus 26 extern "C" { 27#endif 28 29/* Enum: ALLEGRO_HAPTIC_CONSTANTS 30 */ 31enum ALLEGRO_HAPTIC_CONSTANTS { 32 ALLEGRO_HAPTIC_RUMBLE = 1 << 0, 33 ALLEGRO_HAPTIC_PERIODIC = 1 << 1, 34 ALLEGRO_HAPTIC_CONSTANT = 1 << 2, 35 ALLEGRO_HAPTIC_SPRING = 1 << 3, 36 ALLEGRO_HAPTIC_FRICTION = 1 << 4, 37 ALLEGRO_HAPTIC_DAMPER = 1 << 5, 38 ALLEGRO_HAPTIC_INERTIA = 1 << 6, 39 ALLEGRO_HAPTIC_RAMP = 1 << 7, 40 ALLEGRO_HAPTIC_SQUARE = 1 << 8, 41 ALLEGRO_HAPTIC_TRIANGLE = 1 << 9, 42 ALLEGRO_HAPTIC_SINE = 1 << 10, 43 ALLEGRO_HAPTIC_SAW_UP = 1 << 11, 44 ALLEGRO_HAPTIC_SAW_DOWN = 1 << 12, 45 ALLEGRO_HAPTIC_CUSTOM = 1 << 13, 46 ALLEGRO_HAPTIC_GAIN = 1 << 14, 47 ALLEGRO_HAPTIC_ANGLE = 1 << 15, 48 ALLEGRO_HAPTIC_RADIUS = 1 << 16, 49 ALLEGRO_HAPRIC_AZIMUTH = 1 << 17, 50}; 51 52 53 54/* Type: ALLEGRO_HAPTIC 55 */ 56typedef struct ALLEGRO_HAPTIC ALLEGRO_HAPTIC; 57 58/* Direction of a haptic effect. Angle is a value between 0 and 2*M_PI. 59 * An angle 0 means oriented towards the user, M_PI is away from the user 60 * (towards the screen). Angle is only supported if the device 61capabilities include 62 * ALLEGRO_HAPTIC_ANGLE. 63 * Radius (if supported ) is the distance of the effect from the user 64 * as a value between 0 and 1. Normally it is 0. Radius is only 65supported if the 66 * device capabilities include ALLEGRO_HAPTIC_RADIUS . 67 * Azimuth is the angle of elevation, between -M_PI and M_PI. 0 points to the 68 * horizontal plane, -M_PI points down, and M_PI points up. 69 * Azimuth is only supported if the device capabilities include 70 * ALLEGRO_HAPTIC_AZIMUTH. 71 * 72 */ 73struct ALLEGRO_HAPTIC_DIRECTION { 74 double angle; 75 double radius; 76 double azimuth; 77}; 78 79/* In all of the following structs, the doubles that express duration represent 80 * time in seconds. The double that represent levels of intensity are 81between 0.0 82 * and 1.0 that mean no effect and full 100% effect. */ 83 84/* Delay to start the replay and duration of the replay, expressed in 85seconds. */ 86struct ALLEGRO_HAPTIC_REPLAY { 87 double length; 88 double delay; 89}; 90 91/* Envelope of the effect. */ 92struct ALLEGRO_HAPTIC_ENVELOPE { 93 double attack_length; 94 double attack_level; 95 double fade_length; 96 double fade_level; 97}; 98 99/* Constant effect. Level is between 0.0 and 1.0. */ 100struct ALLEGRO_HAPTIC_CONSTANT_EFFECT { 101 double level; 102 struct ALLEGRO_HAPTIC_ENVELOPE envelope; 103}; 104 105/* Ramp effect. Both start_level and end level are between 0.0 and 1.0. */ 106struct ALLEGRO_HAPTIC_RAMP_EFFECT { 107 double start_level; 108 double end_level; 109 struct ALLEGRO_HAPTIC_ENVELOPE envelope; 110}; 111 112/* Condition effect. */ 113struct ALLEGRO_HAPTIC_CONDITION_EFFECT { 114 double right_saturation; 115 double left_saturation; 116 double right_coeff; 117 double left_coeff; 118 double deadband; 119 double center; 120}; 121 122/* Periodic (wave) effect. */ 123struct ALLEGRO_HAPTIC_PERIODIC_EFFECT { 124 int waveform; 125 double period; 126 double magnitude; 127 double offset; 128 double phase; 129 130 struct ALLEGRO_HAPTIC_ENVELOPE envelope; 131 int custom_len; 132 double *custom_data; 133}; 134 135/* Simple rumble effect with a magnitude between 0.0 and 1.0 for both 136 the strong and the weak rumble motors in the haptic device. */ 137struct ALLEGRO_HAPTIC_RUMBLE_EFFECT { 138 double strong_magnitude; 139 double weak_magnitude; 140}; 141 142union ALLEGRO_HAPTIC_EFFECT_UNION { 143 struct ALLEGRO_HAPTIC_CONSTANT_EFFECT constant; 144 struct ALLEGRO_HAPTIC_RAMP_EFFECT ramp; 145 struct ALLEGRO_HAPTIC_PERIODIC_EFFECT periodic; 146 struct ALLEGRO_HAPTIC_CONDITION_EFFECT condition; 147 struct ALLEGRO_HAPTIC_RUMBLE_EFFECT rumble; 148}; 149 150/* Type: ALLEGRO_HAPTIC_EFFECT. This neeeds to be filled in and uploaded to 151 * the haptic device before it can be played back. 152 */ 153struct ALLEGRO_HAPTIC_EFFECT { 154 int type; 155 int id; 156 struct ALLEGRO_HAPTIC_DIRECTION direction; 157 struct ALLEGRO_HAPTIC_REPLAY replay; 158 union ALLEGRO_HAPTIC_EFFECT_UNION data; 159}; 160 161 162/* Type: ALLEGRO_HAPTIC_EFFECT 163 */ 164typedef struct ALLEGRO_HAPTIC_EFFECT ALLEGRO_HAPTIC_EFFECT; 165 166 167 168/* Installs the haptic (force feedback) device subsystem. */ 169AL_FUNC(bool, al_install_haptic , (void)); 170/* Uninstalls the haptic device subsystem. */ 171AL_FUNC(void, al_uninstall_haptic , (void)); 172/* Returns true if the haptic device subsystem is installed, false if not. */ 173AL_FUNC(bool, al_is_haptic_installed , (void)); 174/* Checks if no new haptic devices became availabe and reconfigues the haptic 175 *subsystem. */ 176AL_FUNC(bool, al_reconfigure_haptic , (void)); 177 178/* Gets the amount of available haptic devices.*/ 179AL_FUNC(int, al_get_num_haptics , (void)); 180 181/* Opens and initializes the haptic device and returns a pointer 182 * to a device handle, or NULL on error. */ 183AL_FUNC(ALLEGRO_HAPTIC *, al_get_haptic , (int hapticn)); 184/* Closes the haptic device. */ 185AL_FUNC(void, al_release_haptic , (ALLEGRO_HAPTIC *)); 186/* Returns true if the haptic device can currently be used, false if not.*/ 187AL_FUNC(bool, al_get_haptic_active , (ALLEGRO_HAPTIC *)); 188/* Gets a string that describes the name of the haptic device.*/ 189AL_FUNC(const char*, al_get_haptic_name , (ALLEGRO_HAPTIC *)); 190 191/* Returns an integer with or'ed values from ALLEGRO_HAPTIC_CONSTANTS, that if 192 set indicate that the haptic device supports the given feature. */ 193AL_FUNC(int, al_get_haptic_capabilities , (ALLEGRO_HAPTIC *)); 194 195/* Sets the gain of the haptic device if supported. Gain is much like 196volume for sound, 197 it is as if every effect's intensity is multiplied by it. Gain is a 198value between 199 0.0 and 1.0. Returns true if set sucessfully, false if not.*/ 200AL_FUNC(bool, al_set_haptic_gain , (ALLEGRO_HAPTIC 201*, double gain)); 202/* Returns the current gain of the device. */ 203AL_FUNC(double, al_get_haptic_gain , (ALLEGRO_HAPTIC *)); 204 205/* Returns true if the mouse has haptic capabilities, false if not.*/ 206AL_FUNC(bool, al_is_mouse_haptic , (ALLEGRO_MOUSE *)); 207 208/* Returns true if the joystick has haptic capabilities, false if not.*/ 209AL_FUNC(bool, al_is_joystick_haptic , (ALLEGRO_JOYSTICK *)); 210/* If the mouse has haptic capabilities, returns the associated haptic 211device handle. 212 * Otherwise returns NULL;*/ 213AL_FUNC(ALLEGRO_HAPTIC *, al_get_haptic_from_mouse , (ALLEGRO_MOUSE *)); 214/* If the mouse has haptic capabilities, returns the associated haptic 215device handle. 216 * Otherwise returns NULL;*/ 217AL_FUNC(ALLEGRO_HAPTIC *, al_get_haptic_from_joystick, (ALLEGRO_JOYSTICK *)); 218 219/* Returns the maximum amount of haptic effects that can be uploaded 220to the device. */ 221AL_FUNC(int, al_get_num_haptic_effects , (ALLEGRO_HAPTIC *)); 222 223/* Returns true if the haptic device can play the haptic effect as 224given, false if not. */ 225AL_FUNC(bool, al_is_haptic_effect_ok , (ALLEGRO_HAPTIC 226*, ALLEGRO_HAPTIC_EFFECT *)); 227 228/* Uploads the haptic effect to the device. In play_id, an integer is 229stored that is 230 a reference to be used to control playback of the effect.*/ 231AL_FUNC(bool, al_upload_haptic_effect , (ALLEGRO_HAPTIC 232*, ALLEGRO_HAPTIC_EFFECT *, int * play_id)); 233 234/* Plays back a previously uploaded haptic effect on this device. */ 235AL_FUNC(bool, al_play_haptic_effect , (ALLEGRO_HAPTIC 236*, int play_id, int loop)); 237 238/* Stops playing a haptic effect on this device. */ 239AL_FUNC(bool, al_stop_haptic_effect , (ALLEGRO_HAPTIC 240*, int play_id)); 241/* Stops playing all haptic effects on this device. */ 242AL_FUNC(bool, al_stop_all_haptic_effects , (ALLEGRO_HAPTIC 243*, int play_id)); 244 245/* Returns true if the haptic effect is playing on the device false if 246not or if stopped. */ 247AL_FUNC(bool, al_is_haptic_effect_playing, (ALLEGRO_HAPTIC 248*, int play_id)); 249 250/* Uploads a simple rumble effect to the haptic device and starts 251playback immediately. 252 */ 253AL_FUNC(bool, al_rumble_haptic , (ALLEGRO_HAPTIC 254*, double intensity, double duration, int * play_id)); 255 256/* Event source for haptic device events. 257 *(XXX: currently no events are planned to be emitted, but that may change. )*/ 258AL_FUNC(ALLEGRO_EVENT_SOURCE *, al_get_haptic_event_source, (void)); 259 260 261 262 263#ifdef __cplusplus 264} 265#endif 266 267#endif

Trent Gamblin

One "danger" with these things is always that you write for one platform and then other platforms do things completely differently. You end up with a "biased" api. If you haven't already, I think you should look at Windows and Mac OS X and see how things are done there (even if you can't write drivers for those.) Then find a common set of functions and an API that is not targetted at a single platform.

NOTE: You may have already done this. I see a lot of very specific information here and that made me wonder if this wasn't just a thin wrapper over Linux apis.

beoran

Actually I'm guilty as charged there. :(

I don't have access to windows or mac OS-X, so to get some ideas of who to structure the API, I had to take a look at the SDL 2 sources (which are under the same license as Allegro, so no problem there). They too use a Linux-ish API as the smallest common denominator for most platforms . So I went for that with some nuances, such as using doubles to get device independent value for time , and relative values for intensity in there.

But I also know from SDL2 that it's possible to fit Windows and OS-X into this Linux-ish API, so I thought it would be OK. But if you have better ideas, then please do make suggestions.

I'll probably only be able to supply a Linux driver anyway, so I'd love to hear from people who want to implement te Windows or OS-X, or Iphone, or Android drivers for this and I'd like those people to tell me how we could modify this proposal so it will make it easy for them to write their drivers.

Trent Gamblin
beoran said:

But I also know from SDL2 that it's possible to fit Windows and OS-X into this Linux-ish API, so I thought it would be OK.

If that's the case then it's probably OK. BTW, you don't need a computer running Windows or OS X to look at the APIs. MSDN and http://developer.apple.com have documentation online.

beoran

Well yes, but I don' t use SDL exactly because I like the Allegro API better, so their API doesn't have to be the end-all be-all. If possible I'd like an API that is easier to use than SDL.

It's true, I should RTFM, but since I don't have a chance to test the other platforms, I doubt I would learn much more from RTFM than from the SDL code. I'd rather listen to the opinion of people who had hands on experience with those API's. Or people who are willing to gain this experience.

To be blunt, Trent, would you be willing to do the Windows or OSX port of this? You can get a USB joystick that supports rumble cheaply and easily enough, and then you can try to make it work using the Windows or OSX API. Like that you'll get valuable hands-on experience and we'll be able to decide how too proceed with ALLEGRO_HAPTICS much better.

Trent Gamblin

I can do Windows and OS X, but I may not have a lot of time to invest in them. I'll do my best. I guess I'll have a look on MSDN first and see what's available.

beoran

Thanks a lot! I know you are busy with your game project, but I think for an action RPG, rumble effects certainly will be an extra selling point. Even if you can't write the whole driver, your explorations will hopefully shed some light on the right approach.

I'll share with you the whole patch of all other changes I made versus Allegro git so far while I was working on the Linux driver, if and when you need it, so feel free to request it whenever.

Trent Gamblin

Branches are cheap in git right? Is there any reason we can't create a branch for developing this (to anyone who knows)?

beoran, these two pages hold most of the key information on force feedback with DirectInput:

http://msdn.microsoft.com/en-us/library/windows/desktop/microsoft.directx_sdk.idirectinputdevice8.idirectinputdevice8.createeffect(v=vs.85).aspx
http://msdn.microsoft.com/en-us/library/windows/desktop/microsoft.directx_sdk.idirectinputeffect.idirectinputeffect.setparameters(v=vs.85).aspx

Maybe you can tell me what out of this is common between your header and this. We can then try and make something that works well with stuff in common from them. For things that are in one but not the other, we can optionally drop them or keep them with fallbacks to something close where not supported.

EDIT2: After reading over your API fully and comparing it to DirectInput, it's mostly the same. I think there are a few types of effect that are missing in DirectInput and I didn't see anything about replays (if there is no replay in DirectInput I think we could put the onus on the user to replay effects, I'm not sure about this though.)

One thing I think that should be done is to somehow merge this with the joystick (and mouse?) apis somewhat. Like perhaps we could have a function like ALLEGRO_JOYSTICK *al_get_haptic_joystick and maybe the reverse.

beoran

OK, I'll set up my allegro branch at github for this, it might be tomorrow or Monday though before I get this done.

I quicly read the info linked too and it seems Directinput supports a few effects that Linux doesn't, and generally formats it's parameters differently, and is somewhat more convoluted. Especially when it comes to the position of the effect. Linux only supports an angle, (or 2 carthesian axes for a condition effect, IIRC), but DI supports much more. My idea was to only support spherical coordinates , and let the divers translate those as needed.

The Directinput API does allow on to upload, play and stop a sample, but it doesn't give you a "play id" like Linux does, you have to use the effect object, which means you can't reuse the object for different replays. This means that it may be easier to implement the API if we drop the play id and always use an ALLEGRO_HAPTIC_EFFECT * to control playback and stopping:

#SelectExpand
1/* Uploads the haptic effect to the device. */ 2AL_FUNC(bool, al_upload_haptic_effect , (ALLEGRO_HAPTIC*, ALLEGRO_HAPTIC_EFFECT *)); 3 4/* Plays back a previously uploaded haptic effect on this device. 5If the effect want uploaded yet, this will automaticallly call al_upload_haptic_effect to try and upload the effect. 6*/ 7AL_FUNC(bool, al_play_haptic_effect , (ALLEGRO_HAPTIC*, ALLEGRO_HAPTIC_EFFECT * effect, int loop)); 8 9/* Stops playing all haptic effects on this device. */ 10AL_FUNC(bool, al_stop_all_haptic_effects , (ALLEGRO_HAPTIC 11*, ALLEGRO_HAPTIC_EFFECT * effect)); 12 13/* Returns true if the haptic effect is playing on the device false if 14not or if stopped. */ 15AL_FUNC(bool, al_is_haptic_effect_playing, (ALLEGRO_HAPTIC 16*, ALLEGRO_HAPTIC_EFFECT * effect)); 17 18/* Fills in the haptic effect with a a simple rumble effect, uploads it to the haptic device and starts playback immediately. 19 */ 20AL_FUNC(bool, al_rumble_haptic , (ALLEGRO_HAPTIC 21*, ALLEGRO_HAPTIC_EFFECT * effect, double intensity, double duration, ); 22 23/* Copies a haptic effectin such a way that dest can be uploaded, played, and stoppped independently from src. */ 24AL_FUNC(bool, al_copy_haptic_effect(ALLEGRO_HAPTIC_EFFECT * dest, ALLEGRO_HAPTIC_EFFECT * src);

What you say? :)

Trent Gamblin

Looks ok. DirectInput doesn't have a "rumble" haptic, which one do you think is closest?

(probably just an oversight but does this need the effect parameter? AL_FUNC(bool, al_stop_all_haptic_effects , (ALLEGRO_HAPTIC
11*, ALLEGRO_HAPTIC_EFFECT * effect));)

beoran

Rumble is just a very simple "rumble" effect, normally you replace this by a periodic sine wave (or other supported wave) of the required amplitude. Actually, Linux supports rumble because the driver does an internal translation for you. I still want to have a "rumble" for the simples use case where all you want is a bit of vibration in the joystick.

Oh, and for integrating ALLEGRO_HAPTIC devices with joystick and mouse, I think
al_get_haptic_from_mouse and al_get_haptic_from_joystick should be enough for now. Normally I'd imagine you first install the joysticks and then enable the haptic effects on them. We could probably put that in the docs too and perhaps even require this, although I still haven't figured out yet how to make this work with the pluggable joysticks feature.

And yes, that was an oversight. The proper signature is of course
AL_FUNC(bool, al_stop_all_haptic_effects , (ALLEGRO_HAPTIC *));

Trent Gamblin

I think the ALLEGRO_HAPTIC for any joystick should behave exactly like the ALLEGRO_JOYSTICKs. If it's unplugged or one is plugged in the current state remains valid. Those devices become usable/unusable once you call al_reconfigure_joysticks. I guess with mice or other devices we just not support hotplugging at least at this time.

And I agree that having a simple "rumble" effect is a good idea.

beoran

Yes, I see the point about al_reconfigure_joysticks. Perhhaps that's what al_reconfigure_haptics will do on the side of the haptic devices then?

Oh, and it went easier than I thought, I set up a copy of the Allegro development branch on Github. You need the "haptics" branch specifically, get it here:

https://github.com/beoran/allegro/tree/haptic

Ah git... It's sometimes a bit difficult to use, but in functionality, it's "king"!

Trent Gamblin

Depends. Besides gamepads, what other haptic devices are there that will be supported? DirectInput only supports haptics on gamepads. On Linux we could do something like we do for joysticks already, but I don't know if hot pluggable haptic devices are as important as joysticks. Do other haptic devices on Linux get /dev/input/js* devices or something else? If so they'd probably be treated like joysticks with no buttons or whatever, and al_reconfigure_joysticks would take care of them automatically.

Checking out the code now.

EDIT: Couldn't complete the checkout. Got this error:

warning: remote HEAD refers to nonexistent ref, unable to checkout.

beoran

In linux, any input device can have haptic capabilities. So it's not linked to /dev/input/js0 at all, really. At least in theory, linux can have haptic keyboards, haptic trackerballs, haptic mise, haptic tablets, haptic touch screens, etc.

Another reason why haptic devices may not be integrated in joystics is on mobile platforms. Iphones and android can vibrate the whole phone, which isn't connected to the input. I though this should probably be modeled in Allegro as a haptic device too.

And the code isn't in compiling state, sorry for that. I just blasted it there quickly so we could "git" going. :p

Edit: to solve your git problem, after clning the repository, go into the seemingly empty cloned allegro subdir and do

git checkout 653125e2e3c80953d153c0a3f34326e940e8cadc
git checkout -b haptic

To get the repository in working state.

Trent Gamblin

Ok, but the error is from git. I can't clone the repository.

beoran

I tested it, if you do git clone and then get that message, you should find a seemingly empty allegro directory. That means the cloning actually worked, just the checkout didn't.

Necause git's not able to find the right branch (it expects master but that doesn't exist), it checks out nothing and leaves the allegro directory seemingly empty. So you must manually check out the latest revision and tell it that is the haptic branch.

Peter Wang

(I only looked at this briefly so far)

You provide a filled-in effect structure to al_upload_haptic_effect, but then you must provide the same address to al_play_haptic_effect? I think the previous design was cleaner, where the ALLEGRO_HAPTIC object owns the effect after uploading, and returns a handle for you to work with.

Trent Gamblin

Yeah that makes sense to me. I just couldn't articulate it.

Gideon Weems
beoran said:

Iphones and android can vibrate the whole phone, which isn't connected to the input. I though this should probably be modeled in Allegro as a haptic device too.

I agree.

beoran

OK, I'll reinstate the int handle based API. Or should I use a ALLEGRO_HAPTIC_PLAYBACK * , a bit like the sound API?

Peter Wang

Maybe ALLEGRO_HAPTIC_EFFECT_ID (not a pointer) if we were to match the audio API.

Do we need the stuff to enumerate/reconfigure/get/release individual ALLEGRO_HAPTIC objects? I'm assuming that haptic devices could be a component of some other objects, mainly ALLEGRO_JOYSTICK. For vibrating the whole phone, we might use the ALLEGRO_DISPLAY as the parent (or introduce something different). This might simplify the API a bit.

beoran

I'll take another look at the audio API and try to see how I can match that best with an ALLEGRO_HAPTIC_EFFECT_ID.

As for simplifying the API, unfortunately, some haptic devices are not connected to another device such as a display, screen or a joystick. Think of the PS2 Rez "Trance Vibrator" or other similar haptic devices. We have to think of everything...

Peter Wang

We have joy sticks.

beoran

Ah yes, joy sticks like these ones too:
http://therionorteline.files.wordpress.com/2012/09/usb-vibrator.jpg?w=1092
;)

True, we could model such a no-input haptic device as a joystick (that does not return events), but I feel that that doesn't make much sense now if that makes sense. Also mice and keyboards can be haptic as well.

However, perhaps a few joystick wrapper functions could be useful, like say
al_joystick_upload_haptic_effect, al_joystick_upload_play_effect, etc?

Peter Wang

Jokes aside, al_get_joystick_num_sticks/axes/buttons can return zero as well, once we update the documentation. How many zero-input devices are there anyway? (not counting gimmicks which only draw power from the USB port) I assume the Rez trance vibrator, specifically, is just another game controller to the OS.

Haptic keyboard/mouse can be handled as you had already.

beoran

Well, I can't say for all operating systems, but the Rez presents itself to Linux in the driver I know of as a device separate from the input, with a separate API. This may be a reason, though to disregard it for now.

However, I have to admit that for all other haptic devices I know of on Linux, the haptic capabilities are a part of the properties of a generic input device. Also, it's probably true that it will be easier for the people who use the API if they can get the haptic device handle directly from the desired device. So, I'll follow your suggestion.

I'll change the proposed API so haptic capabilities will be linked to existing input devices. And for the rare case of a haptic device that has no input capabilities, we'll make a virtual joystick that has no axes, or perhaps a al_joystic_is_pure_haptic(). Also this makes it possible to link the coordinate system of the haptic device to the axes the joystick actually has. It does mean that we have to modify the Linux joystick driver to scan more than just /dev/input/js* to get the "virtual joysticks".

So the way to get a haptic device handle will then be the following:

ALLEGRO_HAPTIC * al_get_haptic_from_joystick(ALLEGRO_JOYSTICK *);
ALLEGRO_HAPTIC * al_get_haptic_from_keyboard(ALLEGRO_KEYBOARD *);
ALLEGRO_HAPTIC * al_get_haptic_from_display(ALLEGRO_DISPLAY *);
ALLEGRO_HAPTIC * al_get_haptic_from_mouse(ALLEGRO_MOUSE *);
ALLEGRO_HAPTIC * al_get_haptic_from_touch_input(ALLEGRO_TOUCH_INPUT *);

Hmm, almost makes me wish we had some kind of generic ALLEGRO_INPUT_DEVICE...

Edit: here is the adjusted proposal for haptic.h, also with ALLEGRO_HAPTIC_EFFECT_ID to simplify play and stop.

#SelectExpand
1/* ______ ___ ___ 2 * /\ _ \ /_ \ /_ \ 3 * \ \ \L\ \//\ \ //\ \ __ __ _ __ ___ 4 * \ \ __ \ \ \ \ \ \ \ /'__`\ /'_ `/`'__/ __`\ 5 * \ \ /\ \ _\ _ _\ _/\ __//\ \L\ \ \ //\ \L\ \ 6 * \ _\ _/____/____\ ____\ ____ \ _\\ ____/ 7 * /_//_//____//____//____//___L\ /_/ /___/ 8 * /____/ 9 * _/__/ 10 * 11 * Haptic (that is, force feedback) routines for Allegro. 12 * By Beoran (beoran@gmail.com), 2013. 13 * 14 * See readme.txt for copyright information. 15 */ 16 17#ifndef __al_included_allegro5_haptic_h 18#define __al_included_allegro5_haptic_h 19 20#include "allegro5/base.h" 21#include "allegro5/events.h" 22#include "allegro5/mouse.h" 23#include "allegro5/joystick.h" 24 25#ifdef __cplusplus 26 extern "C" { 27#endif 28 29/* Enum: ALLEGRO_HAPTIC_CONSTANTS 30 */ 31enum ALLEGRO_HAPTIC_CONSTANTS { 32 ALLEGRO_HAPTIC_RUMBLE = 1 << 0, 33 ALLEGRO_HAPTIC_PERIODIC = 1 << 1, 34 ALLEGRO_HAPTIC_CONSTANT = 1 << 2, 35 ALLEGRO_HAPTIC_SPRING = 1 << 3, 36 ALLEGRO_HAPTIC_FRICTION = 1 << 4, 37 ALLEGRO_HAPTIC_DAMPER = 1 << 5, 38 ALLEGRO_HAPTIC_INERTIA = 1 << 6, 39 ALLEGRO_HAPTIC_RAMP = 1 << 7, 40 ALLEGRO_HAPTIC_SQUARE = 1 << 8, 41 ALLEGRO_HAPTIC_TRIANGLE = 1 << 9, 42 ALLEGRO_HAPTIC_SINE = 1 << 10, 43 ALLEGRO_HAPTIC_SAW_UP = 1 << 11, 44 ALLEGRO_HAPTIC_SAW_DOWN = 1 << 12, 45 ALLEGRO_HAPTIC_CUSTOM = 1 << 13, 46 ALLEGRO_HAPTIC_GAIN = 1 << 14, 47 ALLEGRO_HAPTIC_ANGLE = 1 << 15, 48 ALLEGRO_HAPTIC_RADIUS = 1 << 16, 49 ALLEGRO_HAPRIC_AZIMUTH = 1 << 17, 50}; 51 52 53 54/* Type: ALLEGRO_HAPTIC 55 */ 56typedef struct ALLEGRO_HAPTIC ALLEGRO_HAPTIC; 57 58/* Direction of a haptic effect. Angle is a value between 0 and 2*M_PI. 59 * An angle 0 means oriented towards the user, M_PI is away from the user 60 * (towards the screen). Angle is only supported if the device capabilities include 61 * ALLEGRO_HAPTIC_ANGLE. 62 * Radius (if supported ) is the distance of the effect from the user 63 * as a value between 0 and 1. Normally it is 0. Radius is only supported if the 64 * device capabilities include ALLEGRO_HAPTIC_RADIUS . 65 * Azimuth is the angle of elevation, between -M_PI and M_PI. 0 points to the 66 * horizontal plane, -M_PI points down, and M_PI points up. 67 * Azimuth is only supported if the device capabilities include 68 * ALLEGRO_HAPTIC_AZIMUTH. 69 * 70 */ 71struct ALLEGRO_HAPTIC_DIRECTION { 72 double angle; 73 double radius; 74 double azimuth; 75}; 76 77/* In all of the following structs, the doubles that express duration represent 78 * time in seconds. The double that represent levels of intensity are between 0.0 79 * and 1.0 that mean no effect and full 100% effect. */ 80 81/* Delay to start the replay and duration of the replay, expressed in seconds. */ 82struct ALLEGRO_HAPTIC_REPLAY { 83 double length; 84 double delay; 85}; 86 87/* Envelope of the effect. */ 88struct ALLEGRO_HAPTIC_ENVELOPE { 89 double attack_length; 90 double attack_level; 91 double fade_length; 92 double fade_level; 93}; 94 95/* Constant effect. Level is between 0.0 and 1.0. */ 96struct ALLEGRO_HAPTIC_CONSTANT_EFFECT { 97 double level; 98 struct ALLEGRO_HAPTIC_ENVELOPE envelope; 99}; 100 101/* Ramp effect. Both start_level and end level are between 0.0 and 1.0. */ 102struct ALLEGRO_HAPTIC_RAMP_EFFECT { 103 double start_level; 104 double end_level; 105 struct ALLEGRO_HAPTIC_ENVELOPE envelope; 106}; 107 108/* Condition effect. */ 109struct ALLEGRO_HAPTIC_CONDITION_EFFECT { 110 double right_saturation; 111 double left_saturation; 112 double right_coeff; 113 double left_coeff; 114 double deadband; 115 double center; 116}; 117 118/* Periodic (wave) effect. */ 119struct ALLEGRO_HAPTIC_PERIODIC_EFFECT { 120 int waveform; 121 double period; 122 double magnitude; 123 double offset; 124 double phase; 125 126 struct ALLEGRO_HAPTIC_ENVELOPE envelope; 127 int custom_len; 128 double *custom_data; 129}; 130 131/* Simple rumble effect with a magnitude between 0.0 and 1.0 for both 132 the strong and the weak rumble motors in the haptic device. */ 133struct ALLEGRO_HAPTIC_RUMBLE_EFFECT { 134 double strong_magnitude; 135 double weak_magnitude; 136}; 137 138union ALLEGRO_HAPTIC_EFFECT_UNION { 139 struct ALLEGRO_HAPTIC_CONSTANT_EFFECT constant; 140 struct ALLEGRO_HAPTIC_RAMP_EFFECT ramp; 141 struct ALLEGRO_HAPTIC_PERIODIC_EFFECT periodic; 142 struct ALLEGRO_HAPTIC_CONDITION_EFFECT condition; 143 struct ALLEGRO_HAPTIC_RUMBLE_EFFECT rumble; 144}; 145 146/* Type: ALLEGRO_HAPTIC_EFFECT. This neeeds to be filled in and uploaded to 147 * the haptic device before it can be played back. 148 */ 149struct ALLEGRO_HAPTIC_EFFECT { 150 int type; 151 int id; 152 struct ALLEGRO_HAPTIC_DIRECTION direction; 153 struct ALLEGRO_HAPTIC_REPLAY replay; 154 union ALLEGRO_HAPTIC_EFFECT_UNION data; 155}; 156 157 158/* Type: ALLEGRO_HAPTIC_EFFECT 159 */ 160typedef struct ALLEGRO_HAPTIC_EFFECT ALLEGRO_HAPTIC_EFFECT; 161 162 163 164/* Type: ALLEGRO_HAPTIC_EFFECT_ID 165 */ 166typedef struct ALLEGRO_HAPTIC_EFFECT_ID ALLEGRO_HAPTIC_EFFECT_ID; 167 168struct ALLEGRO_HAPTIC_EFFECT_ID { 169 ALLEGRO_HAPTIC * _haptic; 170 int _id; 171}; 172 173 174 175/* Installs the haptic (force feedback) device subsystem. */ 176AL_FUNC(bool, al_install_haptic , (void)); 177/* Uninstalls the haptic device subsystem. */ 178AL_FUNC(void, al_uninstall_haptic , (void)); 179/* Returns true if the haptic device subsystem is installed, false if not. */ 180AL_FUNC(bool, al_is_haptic_installed , (void)); 181 182/* Returns true if the mouse has haptic capabilities, false if not.*/ 183AL_FUNC(bool, al_is_mouse_haptic , (ALLEGRO_MOUSE *)); 184/* Returns true if the joystick has haptic capabilities, false if not.*/ 185AL_FUNC(bool, al_is_joystick_haptic , (ALLEGRO_JOYSTICK *)); 186/* Returns true if the keyboard has haptic capabilities, false if not.*/ 187AL_FUNC(bool, al_is_keyboard_haptic , (ALLEGRO_KEYBOARD *)); 188/* Returns true if the display has haptic capabilities, false if not.*/ 189AL_FUNC(bool, al_is_display_haptic , (ALLEGRO_DISPLAY *)); 190/* Returns true if the touch input has haptic capabilities, false if not.*/ 191AL_FUNC(bool, al_is_touch_input_haptic , (ALLEGRO_TOUCH_INPUT *)); 192 193 194/* If the mouse has haptic capabilities, returns the associated haptic device handle. 195 * Otherwise returns NULL. */ 196AL_FUNC(ALLEGRO_HAPTIC *, al_get_haptic_from_mouse , (ALLEGRO_MOUSE *)); 197/* If the mouse has haptic capabilities, returns the associated haptic device handle. 198 * Otherwise returns NULL. */ 199AL_FUNC(ALLEGRO_HAPTIC *, al_get_haptic_from_joystick , (ALLEGRO_JOYSTICK *)); 200/* If the keyboard has haptic capabilities, returns the associated haptic device handle. 201 * Otherwise returns NULL. */ 202AL_FUNC(ALLEGRO_HAPTIC *, al_get_haptic_from_keyboard , (ALLEGRO_DISPLAY *)); 203/* If the display has haptic capabilities, returns the associated haptic device handle. 204 * Otherwise returns NULL. */ 205AL_FUNC(ALLEGRO_HAPTIC *, al_get_haptic_from_display , (ALLEGRO_DISPLAY *)); 206/* If the touch input has haptic capabilities, returns the associated haptic 207 * device handle. Otherwise returns NULL. */ 208AL_FUNC(ALLEGRO_HAPTIC *, al_get_haptic_from_touch_input, (ALLEGRO_TOUCH_INPUT *)); 209 210 211 212 213/* Returns true if the haptic device can currently be used, false if not.*/ 214AL_FUNC(bool, al_get_haptic_active , (ALLEGRO_HAPTIC *)); 215 216/* Returns an integer with or'ed values from ALLEGRO_HAPTIC_CONSTANTS, that if 217 set indicate that the haptic device supports the given feature. */ 218AL_FUNC(int, al_get_haptic_capabilities , (ALLEGRO_HAPTIC *)); 219 220/* Sets the gain of the haptic device if supported. Gain is much like volume for sound, 221 it is as if every effect's intensity is multiplied by it. Gain is a value between 222 0.0 and 1.0. Returns true if set sucessfully, false if not.*/ 223AL_FUNC(bool, al_set_haptic_gain , (ALLEGRO_HAPTIC *, double gain)); 224/* Returns the current gain of the device. */ 225AL_FUNC(double, al_get_haptic_gain , (ALLEGRO_HAPTIC *)); 226 227 228/* Returns the maximum amount of haptic effects that can be uploaded to the device. */ 229AL_FUNC(int, al_get_num_haptic_effects , (ALLEGRO_HAPTIC *)); 230 231/* Returns true if the haptic device can play the haptic effect as given, false if not. */ 232AL_FUNC(bool, al_is_haptic_effect_ok , (ALLEGRO_HAPTIC *, ALLEGRO_HAPTIC_EFFECT *)); 233 234/* Uploads the haptic effect to the device. In play_id, a handle is stored that is 235 a reference to be used to control playback of the effect. */ 236AL_FUNC(bool, al_upload_haptic_effect , (ALLEGRO_HAPTIC *, ALLEGRO_HAPTIC_EFFECT *, ALLEGRO_HAPTIC_EFFECT_ID * play_id)); 237 238/* Plays back a previously uploaded haptic effect. */ 239AL_FUNC(bool, al_play_haptic_effect , (ALLEGRO_HAPTIC_EFFECT_ID *, int loop)); 240 241/* Uploads and immediately plays back the haptic effect to the device. */ 242AL_FUNC(bool, al_upload_and_play_haptic_effect , (ALLEGRO_HAPTIC *, ALLEGRO_HAPTIC_EFFECT *, int loop, ALLEGRO_HAPTIC_EFFECT_ID * play_id)); 243 244/* Stops playing a haptic effect . */ 245AL_FUNC(bool, al_stop_haptic_effect , (ALLEGRO_HAPTIC_EFFECT_ID *)); 246/* Stops playing all haptic effects on this device. */ 247AL_FUNC(bool, al_stop_all_haptic_effects , (ALLEGRO_HAPTIC *)); 248 249/* Returns true if the haptic effect is playing or false if not or if stopped. */ 250AL_FUNC(bool, al_is_haptic_effect_playing, (ALLEGRO_HAPTIC_EFFECT_ID *)); 251 252/* Uploads a simple rumble effect to the haptic device and starts playback immediately. 253 */ 254AL_FUNC(bool, al_rumble_haptic, (ALLEGRO_HAPTIC *, ALLEGRO_HAPTIC_EFFECT *, double intensity, ALLEGRO_HAPTIC_EFFECT_ID *));

Edit 2: I checked the X Windows mouse, display, and keyboard drivers, and the problem with them is that there is not trace to befound of the the underlying /dev/input/eventXX device. So there's no easy way to enable haptic support for them if any. The Linux joystick driver does have a fd of the input device that can be used to send haptic effects to, but all in all, it seems that the API I propose above could only be implemented relatively easily on on Linux for joysticks. For all other devices we'd have to scan all input devices and try to match them with the corresponding X Windows input... That seems convoluted. :/

Peter Wang

We never access the underlying device files, as ostensibly X is still a network aware system, and keyboard and mouse could be on another machine. So we would probably wait the necessary support appears in X, or whatever replaces it.

(We could actually support joystick via XInput, but AFAIK no one really does so.)

beoran

OK, that's fair enough. I can live with the idea that on X, we'll only have haptic joysticks for the time being, and then only on Linux. In theory SDL 2 is more capable than this, but I can see in practice, the API above will suffice for a first implemenation of haptics on Allegro. GLFW, for example also has a similar limitation of haptics only for joysticks. On other platforms, non-joystick haptics (like Vibrate on Android) may still be defined using al_get_haptic_from_display, etc.

With the current driver joystick support on other *BSD/*nix systems does not work anyway. And I've been reading around a bit and it looks like on Linux we'll probably have also to support either Wayland or Mir, or both (or maybe nether and system nr 3 will come along). Those systems just boil down that we'll have to use /dev/input/eventXX for everything input related anyway. When that happens, we can enable haptic support for all these devices.

I'll start implementing the API above for Linux. I will have to refactor the Linux joystick driver a bit though since now the haptic driver will need to read the joysticks driver's internal details. Probably the same will be needed on other platforms too. But that's not a fundamental problem.

Thread #612754. Printed from Allegro.cc