File: ControllerEmu.h

package info (click to toggle)
dolphin-emu 5.0%2Bdfsg-5
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 29,052 kB
  • sloc: cpp: 213,146; java: 6,252; asm: 2,277; xml: 1,998; ansic: 1,514; python: 462; sh: 279; pascal: 247; makefile: 124; perl: 97
file content (457 lines) | stat: -rw-r--r-- 11,129 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
// Copyright 2010 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.

#pragma once

#include <algorithm>
#include <cmath>
#include <memory>
#include <string>
#include <vector>

#include "Common/IniFile.h"
#include "Core/ConfigManager.h"
#include "InputCommon/GCPadStatus.h"
#include "InputCommon/ControllerInterface/ControllerInterface.h"

#define sign(x) ((x)?(x)<0?-1:1:0)

enum
{
	GROUP_TYPE_OTHER,
	GROUP_TYPE_STICK,
	GROUP_TYPE_MIXED_TRIGGERS,
	GROUP_TYPE_BUTTONS,
	GROUP_TYPE_FORCE,
	GROUP_TYPE_EXTENSION,
	GROUP_TYPE_TILT,
	GROUP_TYPE_CURSOR,
	GROUP_TYPE_TRIGGERS,
	GROUP_TYPE_SLIDER
};

enum
{
	SETTING_RADIUS,
	SETTING_DEADZONE,
};

const char* const named_directions[] =
{
	"Up",
	"Down",
	"Left",
	"Right"
};

class ControllerEmu
{
public:

	class ControlGroup
	{
	public:

		class Control
		{
		protected:
			Control(ControllerInterface::ControlReference* const _ref, const std::string& _name)
				: control_ref(_ref), name(_name) {}

		public:
			virtual ~Control() {}
			std::unique_ptr<ControllerInterface::ControlReference> const control_ref;
			const std::string name;

		};

		class Input : public Control
		{
		public:

			Input(const std::string& _name)
				: Control(new ControllerInterface::InputReference, _name) {}
		};

		class Output : public Control
		{
		public:

			Output(const std::string& _name)
				: Control(new ControllerInterface::OutputReference, _name) {}
		};

		class Setting
		{
		public:
			Setting(const std::string& _name, const ControlState def_value
				, const unsigned int _low = 0, const unsigned int _high = 100)
				: name(_name)
				, value(def_value)
				, default_value(def_value)
				, low(_low)
				, high(_high)
				, is_virtual(false)
				, is_iterate(false) {}

			virtual ~Setting()
			{
			}

			const std::string   name;
			ControlState        value;
			const ControlState  default_value;
			const unsigned int  low, high;
			bool                is_virtual;
			bool                is_iterate;

			virtual void SetValue(ControlState new_value)
			{
				value = new_value;
			}

			virtual ControlState GetValue()
			{
				return value;
			}
		};

		class BackgroundInputSetting : public Setting
		{
		public:
			BackgroundInputSetting(const std::string &_name) : Setting(_name, false)
			{
				is_virtual = true;
			}

			void SetValue(ControlState new_value) override
			{
				SConfig::GetInstance().m_BackgroundInput = !!new_value;
			}

			ControlState GetValue() override
			{
				return SConfig::GetInstance().m_BackgroundInput;
			}
		};

		class IterateUI : public Setting
		{
		public:
			IterateUI(const std::string &_name) : Setting(_name, false)
			{
				is_iterate = true;
			}
		};

		ControlGroup(const std::string& _name, const unsigned int _type = GROUP_TYPE_OTHER)
			: name(_name), ui_name(_name), type(_type) {}
		ControlGroup(const std::string& _name, const std::string& _ui_name, const unsigned int _type = GROUP_TYPE_OTHER)
			: name(_name), ui_name(_ui_name), type(_type) {}
		virtual ~ControlGroup() {}

		virtual void LoadConfig(IniFile::Section *sec, const std::string& defdev = "", const std::string& base = "" );
		virtual void SaveConfig(IniFile::Section *sec, const std::string& defdev = "", const std::string& base = "" );

		void SetControlExpression(int index, const std::string& expression);

		const std::string     name;
		const std::string     ui_name;
		const unsigned int    type;

		std::vector<std::unique_ptr<Control>> controls;
		std::vector<std::unique_ptr<Setting>> settings;

	};

	class AnalogStick : public ControlGroup
	{
	public:
		// The GameCube controller and Wiimote attachments have a different default radius
		AnalogStick(const char* const _name, ControlState default_radius);
		AnalogStick(const char* const _name, const char* const _ui_name, ControlState default_radius);

		void GetState(ControlState* const x, ControlState* const y)
		{
			ControlState yy = controls[0]->control_ref->State() - controls[1]->control_ref->State();
			ControlState xx = controls[3]->control_ref->State() - controls[2]->control_ref->State();

			ControlState radius = settings[SETTING_RADIUS]->value;
			ControlState deadzone = settings[SETTING_DEADZONE]->value;
			ControlState m = controls[4]->control_ref->State();

			ControlState ang = atan2(yy, xx);
			ControlState ang_sin = sin(ang);
			ControlState ang_cos = cos(ang);

			ControlState dist = sqrt(xx*xx + yy*yy);

			// dead zone code
			dist = std::max(0.0, dist - deadzone);
			dist /= (1 - deadzone);

			// radius
			dist *= radius;

			// The modifier halves the distance by 50%, which is useful
			// for keyboard controls.
			if (m)
				dist *= 0.5;

			yy = std::max(-1.0, std::min(1.0, ang_sin * dist));
			xx = std::max(-1.0, std::min(1.0, ang_cos * dist));

			*y = yy;
			*x = xx;
		}
	};

	class Buttons : public ControlGroup
	{
	public:
		Buttons(const std::string& _name);

		template <typename C>
		void GetState(C* const buttons, const C* bitmasks)
		{
			for (auto& control : controls)
			{
				if (control->control_ref->State() > settings[0]->value) // threshold
					*buttons |= *bitmasks;

				bitmasks++;
			}
		}

	};

	class MixedTriggers : public ControlGroup
	{
	public:
		MixedTriggers(const std::string& _name);

		void GetState(u16 *const digital, const u16* bitmasks, ControlState* analog)
		{
			const unsigned int trig_count = ((unsigned int) (controls.size() / 2));
			for (unsigned int i=0; i<trig_count; ++i,++bitmasks,++analog)
			{
				if (controls[i]->control_ref->State() > settings[0]->value) //threshold
				{
					*analog = 1.0;
					*digital |= *bitmasks;
				}
				else
				{
					*analog = controls[i+trig_count]->control_ref->State();
				}
			}
		}
	};

	class Triggers : public ControlGroup
	{
	public:
		Triggers(const std::string& _name);

		void GetState(ControlState* analog)
		{
			const unsigned int trig_count = ((unsigned int) (controls.size()));
			const ControlState deadzone = settings[0]->value;
			for (unsigned int i=0; i<trig_count; ++i,++analog)
				*analog = std::max(controls[i]->control_ref->State() - deadzone, 0.0) / (1 - deadzone);
		}
	};

	class Slider : public ControlGroup
	{
	public:
		Slider(const std::string& _name);

		void GetState(ControlState* const slider)
		{
			const ControlState deadzone = settings[0]->value;
			const ControlState state = controls[1]->control_ref->State() - controls[0]->control_ref->State();

			if (fabs(state) > deadzone)
				*slider = (state - (deadzone * sign(state))) / (1 - deadzone);
			else
				*slider = 0;
		}
	};

	class Force : public ControlGroup
	{
	public:
		Force(const std::string& _name);

		void GetState(ControlState* axis)
		{
			const ControlState deadzone = settings[0]->value;
			for (unsigned int i = 0; i < 6; i += 2)
			{
				ControlState tmpf = 0;
				const ControlState state = controls[i+1]->control_ref->State() - controls[i]->control_ref->State();
				if (fabs(state) > deadzone)
					tmpf = ((state - (deadzone * sign(state))) / (1 - deadzone));

				ControlState &ax = m_swing[i >> 1];
				*axis++ = (tmpf - ax);
				ax = tmpf;
			}
		}

	private:
		ControlState m_swing[3];
	};

	class Tilt : public ControlGroup
	{
	public:
		Tilt(const std::string& _name);

		void GetState(ControlState* const x, ControlState* const y, const bool step = true)
		{
			// this is all a mess

			ControlState yy = controls[0]->control_ref->State() - controls[1]->control_ref->State();
			ControlState xx = controls[3]->control_ref->State() - controls[2]->control_ref->State();

			ControlState deadzone = settings[0]->value;
			ControlState circle = settings[1]->value;
			auto const angle = settings[2]->value / 1.8;
			ControlState m = controls[4]->control_ref->State();

			// deadzone / circle stick code
			// this section might be all wrong, but its working good enough, I think

			ControlState ang = atan2(yy, xx);
			ControlState ang_sin = sin(ang);
			ControlState ang_cos = cos(ang);

			// the amt a full square stick would have at current angle
			ControlState square_full = std::min(ang_sin ? 1/fabs(ang_sin) : 2, ang_cos ? 1/fabs(ang_cos) : 2);

			// the amt a full stick would have that was (user setting circular) at current angle
			// I think this is more like a pointed circle rather than a rounded square like it should be
			ControlState stick_full = (square_full * (1 - circle)) + (circle);

			ControlState dist = sqrt(xx*xx + yy*yy);

			// dead zone code
			dist = std::max(0.0, dist - deadzone * stick_full);
			dist /= (1 - deadzone);

			// circle stick code
			ControlState amt = dist / stick_full;
			dist += (square_full - 1) * amt * circle;

			if (m)
				dist *= 0.5;

			yy = std::max(-1.0, std::min(1.0, ang_sin * dist));
			xx = std::max(-1.0, std::min(1.0, ang_cos * dist));

			// this is kinda silly here
			// gui being open will make this happen 2x as fast, o well

			// silly
			if (step)
			{
				if (xx > m_tilt[0])
					m_tilt[0] = std::min(m_tilt[0] + 0.1, xx);
				else if (xx < m_tilt[0])
					m_tilt[0] = std::max(m_tilt[0] - 0.1, xx);

				if (yy > m_tilt[1])
					m_tilt[1] = std::min(m_tilt[1] + 0.1, yy);
				else if (yy < m_tilt[1])
					m_tilt[1] = std::max(m_tilt[1] - 0.1, yy);
			}

			*y = m_tilt[1] * angle;
			*x = m_tilt[0] * angle;
		}

	private:
		ControlState m_tilt[2];
	};

	class Cursor : public ControlGroup
	{
	public:
		Cursor(const std::string& _name);

		void GetState(ControlState* const x, ControlState* const y, ControlState* const z, const bool adjusted = false)
		{
			const ControlState zz = controls[4]->control_ref->State() - controls[5]->control_ref->State();

			// silly being here
			if (zz > m_z)
				m_z = std::min(m_z + 0.1, zz);
			else if (zz < m_z)
				m_z = std::max(m_z - 0.1, zz);

			*z = m_z;

			// hide
			if (controls[6]->control_ref->State() > 0.5)
			{
				*x = 10000; *y = 0;
			}
			else
			{
				ControlState yy = controls[0]->control_ref->State() - controls[1]->control_ref->State();
				ControlState xx = controls[3]->control_ref->State() - controls[2]->control_ref->State();

				// adjust cursor according to settings
				if (adjusted)
				{
					xx *= (settings[1]->value * 2);
					yy *= (settings[2]->value * 2);
					yy += (settings[0]->value - 0.5);
				}

				*x = xx;
				*y = yy;
			}
		}

		ControlState m_z;
	};

	class Extension : public ControlGroup
	{
	public:
		Extension(const std::string& _name)
			: ControlGroup(_name, GROUP_TYPE_EXTENSION)
			, switch_extension(0)
			, active_extension(0) {}

		~Extension() {}

		void GetState(u8* const data);
		bool IsButtonPressed() const;

		std::vector<std::unique_ptr<ControllerEmu>> attachments;

		int switch_extension;
		int active_extension;
	};

	virtual ~ControllerEmu() {}

	virtual std::string GetName() const = 0;

	virtual void LoadDefaults(const ControllerInterface& ciface);

	virtual void LoadConfig(IniFile::Section *sec, const std::string& base = "");
	virtual void SaveConfig(IniFile::Section *sec, const std::string& base = "");
	void UpdateDefaultDevice();

	void UpdateReferences(ControllerInterface& devi);

	std::vector<std::unique_ptr<ControlGroup>> groups;

	ciface::Core::DeviceQualifier default_device;
};