cpp-sdl2
C++ header-only SDL2 wrapper
haptic.hpp
Go to the documentation of this file.
1 #pragma once
2 
3 #include <SDL_haptic.h>
4 #include <algorithm>
5 
6 namespace sdl
7 {
8 class Haptic
9 {
11  SDL_Haptic* haptic_ = nullptr;
12 
14  using effect_sdlid = int;
15 
17  using effect_list = std::vector<effect_sdlid>;
18 
21 
22 public:
24  SDL_Haptic* ptr() const { return haptic_; }
25 
28  {
29  static constexpr effect_list::size_type invalid_index =
30  std::numeric_limits<effect_list::size_type>::max();
31  effect_list::size_type index_ = invalid_index;
32  Haptic* owner_ = nullptr;
33  friend class Haptic;
34 
35  void move_from(InstalledEffect& other) {}
36 
37  public:
38  InstalledEffect(effect_list::size_type index, Haptic* owner) : index_{index}, owner_{owner}
39  {
40  }
41 
42  InstalledEffect() = default;
45 
46  InstalledEffect(InstalledEffect&& other) noexcept { *this = std::move(other); }
47 
49  {
50  if (index_ != other.index_)
51  {
52  index_ = other.index_;
53  owner_ = other.owner_;
54 
55  other.index_ = invalid_index;
56  other.owner_ = nullptr;
57  }
58  return *this;
59  }
60 
62  {
63  if (owner_ && index_ != invalid_index)
64  {
65  const effect_sdlid my_real_id = owner_->get_effect_sdlid(*this);
66  SDL_HapticDestroyEffect(owner_->ptr(), my_real_id);
67 
68  owner_->remove_effect(my_real_id);
69  }
70  }
71 
72  void run(uint32_t iterations = 1) { owner_->run_effect(*this, iterations); }
73  };
74 
75 #if _MSC_VER >= 1910
76 #pragma warning(push)
77 #pragma warning(disable : 26495)
78 #endif
79 
81  union Effect
82  {
83  // This is analog to how we handle sdl events: this
84  // definition is copied from SDL_haptic.h
85  // note : It has been planned by SDL developers to change the `type`
86  // type in 2.1.0.
87  Uint16 type;
88  SDL_HapticConstant constant;
89  SDL_HapticPeriodic periodic;
90  SDL_HapticCondition condition;
91  SDL_HapticRamp ramp;
92  SDL_HapticLeftRight leftright;
93  SDL_HapticCustom custom;
94 
96  operator SDL_HapticEffect*() const
97  {
98  // "I solemnly swear that I am up to no good."
99  return (SDL_HapticEffect*)(this);
100  }
101 
104  {
105  // Hopefully this will prevent the library to work when SDL will
106  // decide to "fix" the "oops, the 'type' variable doesn't have
107  // enough bits" problem they encouterd with 2.0 (this fix is
108  // scheduled to be in 2.1 since it's an API/ABI breakage)
109  static_assert(
110  sizeof(Effect::type) == sizeof(SDL_HapticEffect::type),
111  "please compare the layout between SDL_HapticEffect and "
112  "sdl::Haptic::Effect");
113  static_assert(
114  sizeof(Effect) == sizeof(SDL_HapticEffect),
115  "please compare the layout between SDL_HapticEffect and "
116  "sdl::Haptic::Effect");
117 
118  // be sure to fully zero initialize the enclosed effect
119  SDL_memset(this, 0, sizeof(Effect));
120  }
121  };
122 
123 #if _MSC_VER >= 1910
124 #pragma warning(pop)
125 #endif
126 
128  Haptic() {}
129 
131  Haptic(int haptic_index) : haptic_{SDL_HapticOpen(haptic_index)}
132  {
133  if (!haptic_)
134  {
135  throw Exception("SDL_HapticOpen");
136  }
137  }
138 
140  Haptic(SDL_Joystick* joystick) : haptic_{SDL_HapticOpenFromJoystick(joystick)}
141  {
142  if (!haptic_)
143  {
144  throw Exception("SDL_HapticOpenFromJoystick");
145  }
146  }
147 
150  {
151  if (haptic_)
152  {
153  SDL_HapticClose(haptic_);
154  }
155  }
156 
157  // not copyable
158  Haptic(Haptic const&) = delete;
159  Haptic& operator=(Haptic const&) = delete;
160 
162  Haptic(Haptic&& other) noexcept { *this = std::move(other); }
163 
165  Haptic& operator=(Haptic&& other) noexcept
166  {
167  if (haptic_ != other.haptic_)
168  {
169  haptic_ = other.haptic_;
170  my_effects = std::move(other.my_effects);
171  other.haptic_ = nullptr;
172  }
173  return *this;
174  }
175 
177  bool valid() const { return haptic_ != nullptr; }
178 
180  unsigned int get_capabilities() const
181  {
182  if (!valid()) // we're not an opened device... So we should decide what
183  // to do here...
184  return 0U;
185 
186  const auto capabilities = SDL_HapticQuery(haptic_);
187 
188  if (!capabilities)
189  {
190  // couldn't get the capabilities of this haptic device.
191  throw Exception("SDL_HapticQuery");
192  }
193 
194  return capabilities;
195  }
196 
198  bool is_capable_of(int haptic_flag) const
199  {
200  const auto caps = get_capabilities();
201  return (haptic_flag & caps) != 0;
202  }
203 
206  {
207  // We need to be able to play the haptic effect
208  if (!is_capable_of(e.type)) return {};
209 
210  // maybe we should have another RAII wrapper around the registered
211  // haptic effects
212  const effect_sdlid raw_sdl_id = SDL_HapticNewEffect(haptic_, e);
213 
214  if (raw_sdl_id < 0) throw Exception("SDL_HapticNewEffect");
215 
216  my_effects.push_back(raw_sdl_id);
217  return {my_effects.size() - 1, this};
218  }
219 
221  effect_list::size_type registered_effect_count() const { return my_effects.size(); }
222 
225  {
226  return my_effects.at(h.index_);
227  }
228 
231  {
232  for (size_t i = 0, nb_effects = my_effects.size(); i < nb_effects; ++i)
233  {
234  if (my_effects[i] == e) my_effects[i] = -1;
235  }
236  }
237 
239  void run_effect(InstalledEffect const& h, uint32_t iterations = 1) const
240  {
241  const effect_sdlid e = get_effect_sdlid(h);
242  if (e >= 0 && SDL_HapticRunEffect(haptic_, get_effect_sdlid(h), iterations) < 0)
243  {
244  throw Exception("SDL_HapticRunEffect");
245  }
246  }
247 
249  bool is_effect_compatible(Effect const& e) const { return is_capable_of(e.type); }
250 };
251 } // namespace sdl
effect_list::size_type registered_effect_count() const
Get the number of effects installed.
Definition: haptic.hpp:221
SDL_HapticRamp ramp
Condition effect.
Definition: haptic.hpp:91
InstalledEffect(effect_list::size_type index, Haptic *owner)
Definition: haptic.hpp:38
SDL_HapticCondition condition
Periodic effect.
Definition: haptic.hpp:90
InstalledEffect & operator=(InstalledEffect const &)
Effect defintion.
Definition: haptic.hpp:81
Haptic & operator=(Haptic &&other) noexcept
move assign opeartor
Definition: haptic.hpp:165
Installed effect handle.
Definition: haptic.hpp:27
InstalledEffect & operator=(InstalledEffect &&other) noexcept
Definition: haptic.hpp:48
Haptic(int haptic_index)
Open haptic device from index.
Definition: haptic.hpp:131
Haptic(Haptic &&other) noexcept
move ctor
Definition: haptic.hpp:162
void run(uint32_t iterations=1)
Definition: haptic.hpp:72
SDL_HapticCustom custom
Left/Right effect.
Definition: haptic.hpp:93
Haptic()
Uninitialized dummy haptic device.
Definition: haptic.hpp:128
InstalledEffect(InstalledEffect &&other) noexcept
Definition: haptic.hpp:46
std::vector< effect_sdlid > effect_list
Installed effect storage type.
Definition: haptic.hpp:17
Haptic(SDL_Joystick *joystick)
Open haptic device from joystick pointer.
Definition: haptic.hpp:140
SDL_HapticConstant constant
Effect type.
Definition: haptic.hpp:88
Effect()
Construct the effect. Fill it with zeroes.
Definition: haptic.hpp:103
effect_sdlid get_effect_sdlid(InstalledEffect const &h) const
Get the SDL assigned ID (an integer) to the effect.
Definition: haptic.hpp:224
static constexpr effect_list::size_type invalid_index
Definition: haptic.hpp:29
effect_list::size_type index_
Definition: haptic.hpp:31
Define to deactivate exception support.
Definition: color.hpp:7
void remove_effect(effect_sdlid e)
Deactiave the effect. This only set the registered id in question to -1.
Definition: haptic.hpp:230
bool is_effect_compatible(Effect const &e) const
Check if you can safely attemp to install the effect ont he haptic device.
Definition: haptic.hpp:249
SDL_Haptic * ptr() const
The the C pointer.
Definition: haptic.hpp:24
void run_effect(InstalledEffect const &h, uint32_t iterations=1) const
Run an effect, if said effect is valid.
Definition: haptic.hpp:239
unsigned int get_capabilities() const
Get a bitflag that describe the device capabilities.
Definition: haptic.hpp:180
void move_from(InstalledEffect &other)
Definition: haptic.hpp:35
SDL_HapticPeriodic periodic
Constant effect.
Definition: haptic.hpp:89
int effect_sdlid
Type of an installed "effect" for SDL.
Definition: haptic.hpp:14
SDL_Haptic * haptic_
Pointer to C sdl haptic device.
Definition: haptic.hpp:11
effect_list my_effects
Installed effect storage.
Definition: haptic.hpp:20
Define to deactivate exception support.
Definition: exception.hpp:21
bool valid() const
Return true if the device is correctly opened.
Definition: haptic.hpp:177
SDL_HapticLeftRight leftright
Ramp effect.
Definition: haptic.hpp:92
InstalledEffect new_effect(Effect const &e)
Install the effect.
Definition: haptic.hpp:205
~Haptic()
close the haptic device automatically
Definition: haptic.hpp:149
bool is_capable_of(int haptic_flag) const
Check the SDL_HAPTIC_ flag agianst the device capabilities.
Definition: haptic.hpp:198