libgui 3.00
libgui is a GUI library for the GameCube, Wii, and Wii U
 
Loading...
Searching...
No Matches
WiiInputDriver.cpp
1/****************************************************************************
2 * Platform Abstraction Layer (Wii driver)
3 * Daryl Borth 2009-2026
4 * WiiInputDriver.cpp
5 *
6 * Wiimote/extension management and normalization, the Wii U GamePad
7 * (libwiidrc), and the Wii-only parts of the GameCube driver's frame
8 ***************************************************************************/
9
10#include <gccore.h>
11#include <ogcsys.h>
12#include <wiiuse/wpad.h>
13
14#include "WiiInputDriver.h"
15#include "WiiPlatform.h"
16#include "wiidrc.h"
17#include "../OgcInputUtil.h"
18#include "../../Platform.h"
19#include "../../InputController.h"
20
21static void WiimotePowerButtonCallback(s32) { NotifyWiiShutdownRequested(); }
22
23WiiInputDriver::~WiiInputDriver() {
24 // The base destructor's own shutdown() can no longer reach driveRumble() below
25 shutdown();
26}
27
29 SYS_SetPowerCallback(NotifyWiiShutdownRequested);
30
31 WiiDRC_Init();
32 WPAD_Init();
33 WPAD_SetDataFormat(WPAD_CHAN_ALL, WPAD_FMT_BTNS_ACC_IR);
34 WPAD_SetVRes(WPAD_CHAN_ALL, platform->getVideo()->getScreenWidth(), platform->getVideo()->getScreenHeight());
35 WPAD_SetPowerButtonCallback(WiimotePowerButtonCallback);
36}
37
39 WPAD_ScanPads();
40 WiiDRC_ScanPads();
41}
42
44 return CONF_GetPadMotorMode() != 0;
45}
46
47void WiiInputDriver::driveRumble(int channel, bool on) {
48 WPAD_Rumble(channel, on ? 1 : 0);
49}
50
51/****************************************************************************
52 * Hardware Mapping Helpers
53 * Translates raw libogc hardware bits to our generic UI masks
54 ***************************************************************************/
55static uint32_t MapWiimoteToGeneric(uint32_t wpad_btns)
56{
57 uint32_t mask = INPUT_BTN_NONE;
58
59 if (wpad_btns & WPAD_BUTTON_A) mask |= INPUT_BTN_A;
60 if (wpad_btns & WPAD_BUTTON_B) mask |= INPUT_BTN_B;
61 if (wpad_btns & WPAD_BUTTON_1) mask |= INPUT_BTN_1;
62 if (wpad_btns & WPAD_BUTTON_2) mask |= INPUT_BTN_2;
63 if (wpad_btns & WPAD_BUTTON_UP) mask |= INPUT_BTN_UP;
64 if (wpad_btns & WPAD_BUTTON_DOWN) mask |= INPUT_BTN_DOWN;
65 if (wpad_btns & WPAD_BUTTON_LEFT) mask |= INPUT_BTN_LEFT;
66 if (wpad_btns & WPAD_BUTTON_RIGHT) mask |= INPUT_BTN_RIGHT;
67 if (wpad_btns & WPAD_BUTTON_PLUS) mask |= INPUT_BTN_PLUS;
68 if (wpad_btns & WPAD_BUTTON_MINUS) mask |= INPUT_BTN_MINUS;
69 if (wpad_btns & WPAD_BUTTON_HOME) mask |= INPUT_BTN_HOME;
70
71 return mask;
72}
73
74static uint32_t MapNunchukToGeneric(uint32_t wpad_btns)
75{
76 uint32_t mask = INPUT_BTN_NONE;
77
78 if (wpad_btns & WPAD_NUNCHUK_BUTTON_Z) mask |= INPUT_TRIGGER_ZL;
79 if (wpad_btns & WPAD_NUNCHUK_BUTTON_C) mask |= INPUT_TRIGGER_L;
80
81 return mask;
82}
83
84static uint32_t MapClassicToGeneric(uint32_t wpad_btns)
85{
86 uint32_t mask = INPUT_BTN_NONE;
87
88 // Classic Controller inputs (upper 16 bits)
89 if (wpad_btns & WPAD_CLASSIC_BUTTON_A) mask |= INPUT_BTN_A;
90 if (wpad_btns & WPAD_CLASSIC_BUTTON_B) mask |= INPUT_BTN_B;
91 if (wpad_btns & WPAD_CLASSIC_BUTTON_X) mask |= INPUT_BTN_X;
92 if (wpad_btns & WPAD_CLASSIC_BUTTON_Y) mask |= INPUT_BTN_Y;
93 if (wpad_btns & WPAD_CLASSIC_BUTTON_UP) mask |= INPUT_BTN_UP;
94 if (wpad_btns & WPAD_CLASSIC_BUTTON_DOWN) mask |= INPUT_BTN_DOWN;
95 if (wpad_btns & WPAD_CLASSIC_BUTTON_LEFT) mask |= INPUT_BTN_LEFT;
96 if (wpad_btns & WPAD_CLASSIC_BUTTON_RIGHT) mask |= INPUT_BTN_RIGHT;
97 if (wpad_btns & WPAD_CLASSIC_BUTTON_PLUS) mask |= INPUT_BTN_PLUS;
98 if (wpad_btns & WPAD_CLASSIC_BUTTON_MINUS) mask |= INPUT_BTN_MINUS;
99 if (wpad_btns & WPAD_CLASSIC_BUTTON_HOME) mask |= INPUT_BTN_HOME;
100 if (wpad_btns & WPAD_CLASSIC_BUTTON_FULL_L) mask |= INPUT_TRIGGER_L;
101 if (wpad_btns & WPAD_CLASSIC_BUTTON_FULL_R) mask |= INPUT_TRIGGER_R;
102 if (wpad_btns & WPAD_CLASSIC_BUTTON_ZL) mask |= INPUT_TRIGGER_ZL;
103 if (wpad_btns & WPAD_CLASSIC_BUTTON_ZR) mask |= INPUT_TRIGGER_ZR;
104
105 return mask;
106}
107
108static uint32_t MapWiiUGamepadToGeneric(uint32_t drc_btns) {
109 uint32_t mask = INPUT_BTN_NONE;
110 if (drc_btns & WIIDRC_BUTTON_A) mask |= INPUT_BTN_A;
111 if (drc_btns & WIIDRC_BUTTON_B) mask |= INPUT_BTN_B;
112 if (drc_btns & WIIDRC_BUTTON_X) mask |= INPUT_BTN_X;
113 if (drc_btns & WIIDRC_BUTTON_Y) mask |= INPUT_BTN_Y;
114 if (drc_btns & WIIDRC_BUTTON_UP) mask |= INPUT_BTN_UP;
115 if (drc_btns & WIIDRC_BUTTON_DOWN) mask |= INPUT_BTN_DOWN;
116 if (drc_btns & WIIDRC_BUTTON_LEFT) mask |= INPUT_BTN_LEFT;
117 if (drc_btns & WIIDRC_BUTTON_RIGHT) mask |= INPUT_BTN_RIGHT;
118 if (drc_btns & WIIDRC_BUTTON_PLUS) mask |= INPUT_BTN_PLUS;
119 if (drc_btns & WIIDRC_BUTTON_MINUS) mask |= INPUT_BTN_MINUS;
120 if (drc_btns & WIIDRC_BUTTON_HOME) mask |= INPUT_BTN_HOME;
121 if (drc_btns & WIIDRC_BUTTON_L) mask |= INPUT_TRIGGER_L;
122 if (drc_btns & WIIDRC_BUTTON_R) mask |= INPUT_TRIGGER_R;
123 if (drc_btns & WIIDRC_BUTTON_ZL) mask |= INPUT_TRIGGER_ZL;
124 if (drc_btns & WIIDRC_BUTTON_ZR) mask |= INPUT_TRIGGER_ZR;
125 return mask;
126}
127
128/****************************************************************************
129 * Analog Normalization Helpers
130 ***************************************************************************/
131static float NormalizeWPADAnalog(int pos, int min, int max, int center) {
132 if (min == max) return 0.0f;
133
134 // Handle broken 3rd party controller calibration data
135 if ((min >= center) || (max <= center)) {
136 min = 0; max = 64; center = 32; // Generic fallback
137 }
138 int offset = pos - center;
139 if (offset > 0) {
140 return OgcClampf((float)offset / (float)(max - center), 0.0f, 1.0f);
141 } else {
142 return OgcClampf((float)offset / (float)(center - min), -1.0f, 0.0f);
143 }
144}
145
146/****************************************************************************
147 * Per-channel state
148 ***************************************************************************/
150 readWiimote(channel, pad);
151
152 // The GamePad is one controller, on channel 0
153 if (channel == 0)
154 readGamePad(pad);
155}
156
157// The Wiimote and whatever is plugged into it (Nunchuk, Classic Controller, Wii U Pro Controller)
158void WiiInputDriver::readWiimote(int channel, InputPadData & padData) {
159 uint32_t exp_type = WPAD_EXP_NONE;
160
161 if (WPAD_Probe(channel, &exp_type) != WPAD_ERR_NONE)
162 return;
163
164 WPADData* wpad = WPAD_Data(channel);
165
166 // Always process base Wiimote
167 padData.hw_connected[INPUT_HW_WIIMOTE] = true;
168 padData.battery_level = wpad->battery_level;
169
170 padData.hw_gforceX[INPUT_HW_WIIMOTE] = wpad->gforce.x;
171 padData.hw_gforceY[INPUT_HW_WIIMOTE] = wpad->gforce.y;
172 padData.hw_gforceZ[INPUT_HW_WIIMOTE] = wpad->gforce.z;
173 padData.hw_pitch[INPUT_HW_WIIMOTE] = wpad->orient.pitch;
174 padData.hw_roll[INPUT_HW_WIIMOTE] = wpad->orient.roll;
175 padData.hw_yaw[INPUT_HW_WIIMOTE] = wpad->orient.yaw;
176
177 if (exp_type == WPAD_EXP_NONE) {
178 padData.hw_buttons_d[INPUT_HW_WIIMOTE] = MapWiimoteToGeneric(wpad->btns_d);
179 padData.hw_buttons_h[INPUT_HW_WIIMOTE] = MapWiimoteToGeneric(wpad->btns_h);
180 padData.hw_buttons_r[INPUT_HW_WIIMOTE] = MapWiimoteToGeneric(wpad->btns_u);
181
182 if (wpad->ir.valid) {
183 padData.validPointer = true;
184 padData.isTouch = false;
185 padData.cursor_x = wpad->ir.x;
186 padData.cursor_y = wpad->ir.y;
187 padData.cursor_angle = wpad->ir.angle;
188 }
189
190 controller[channel]->setSideways(getWiimoteOrientation() == WIIMOTE_ORIENTATION_HORIZONTAL);
191 }
192 else if (exp_type == WPAD_EXP_NUNCHUK) {
193 padData.hw_buttons_d[INPUT_HW_WIIMOTE] = MapWiimoteToGeneric(wpad->btns_d);
194 padData.hw_buttons_h[INPUT_HW_WIIMOTE] = MapWiimoteToGeneric(wpad->btns_h);
195 padData.hw_buttons_r[INPUT_HW_WIIMOTE] = MapWiimoteToGeneric(wpad->btns_u);
196
197 padData.hw_connected[INPUT_HW_NUNCHUK] = true;
198 padData.hw_buttons_d[INPUT_HW_NUNCHUK] = MapNunchukToGeneric(wpad->btns_d);
199 padData.hw_buttons_h[INPUT_HW_NUNCHUK] = MapNunchukToGeneric(wpad->btns_h);
200 padData.hw_buttons_r[INPUT_HW_NUNCHUK] = MapNunchukToGeneric(wpad->btns_u);
201 joystick_t* js = &wpad->exp.nunchuk.js;
202 padData.hw_stickX[INPUT_HW_NUNCHUK] = NormalizeWPADAnalog(js->pos.x, js->min.x, js->max.x, js->center.x);
203 padData.hw_stickY[INPUT_HW_NUNCHUK] = NormalizeWPADAnalog(js->pos.y, js->min.y, js->max.y, js->center.y);
204
205 padData.hw_gforceX[INPUT_HW_NUNCHUK] = wpad->exp.nunchuk.gforce.x;
206 padData.hw_gforceY[INPUT_HW_NUNCHUK] = wpad->exp.nunchuk.gforce.y;
207 padData.hw_gforceZ[INPUT_HW_NUNCHUK] = wpad->exp.nunchuk.gforce.z;
208 padData.hw_pitch[INPUT_HW_NUNCHUK] = wpad->exp.nunchuk.orient.pitch;
209 padData.hw_roll[INPUT_HW_NUNCHUK] = wpad->exp.nunchuk.orient.roll;
210 padData.hw_yaw[INPUT_HW_NUNCHUK] = wpad->exp.nunchuk.orient.yaw;
211
212 // The Wiimote still points at the sensor bar with a Nunchuk in the other hand
213 if (wpad->ir.valid) {
214 padData.validPointer = true;
215 padData.isTouch = false;
216 padData.cursor_x = wpad->ir.x;
217 padData.cursor_y = wpad->ir.y;
218 padData.cursor_angle = wpad->ir.angle;
219 }
220
221 controller[channel]->setSideways(false);
222 }
223 else if (exp_type == WPAD_EXP_CLASSIC) {
224 bool isWUPC = (wpad->exp.classic.type == 2);
225 int hw = isWUPC ? INPUT_HW_WUPC : INPUT_HW_CLASSIC;
226
227 padData.hw_connected[hw] = true;
228 padData.hw_buttons_d[hw] = MapClassicToGeneric(wpad->btns_d);
229 padData.hw_buttons_h[hw] = MapClassicToGeneric(wpad->btns_h);
230 padData.hw_buttons_r[hw] = MapClassicToGeneric(wpad->btns_u);
231
232 joystick_t* ljs = &wpad->exp.classic.ljs;
233 joystick_t* rjs = &wpad->exp.classic.rjs;
234 padData.hw_stickX[hw] = NormalizeWPADAnalog(ljs->pos.x, ljs->min.x, ljs->max.x, ljs->center.x);
235 padData.hw_stickY[hw] = NormalizeWPADAnalog(ljs->pos.y, ljs->min.y, ljs->max.y, ljs->center.y);
236 padData.hw_substickX[hw] = NormalizeWPADAnalog(rjs->pos.x, rjs->min.x, rjs->max.x, rjs->center.x);
237 padData.hw_substickY[hw] = NormalizeWPADAnalog(rjs->pos.y, rjs->min.y, rjs->max.y, rjs->center.y);
238 padData.hw_triggerL[hw] = OgcClampf(wpad->exp.classic.l_shoulder, 0.0f, 1.0f);
239 padData.hw_triggerR[hw] = OgcClampf(wpad->exp.classic.r_shoulder, 0.0f, 1.0f);
240 controller[channel]->setSideways(false);
241 }
242 else {
243 controller[channel]->setSideways(false);
244 }
245}
246
247// Wii U GamePad, read over the network by libwiidrc
248void WiiInputDriver::readGamePad(InputPadData & padData) {
249 if (!WiiDRC_Inited() || !WiiDRC_Connected())
250 return;
251
252 padData.hw_connected[INPUT_HW_DRC] = true;
253 padData.hw_buttons_d[INPUT_HW_DRC] = MapWiiUGamepadToGeneric(WiiDRC_ButtonsDown());
254 padData.hw_buttons_h[INPUT_HW_DRC] = MapWiiUGamepadToGeneric(WiiDRC_ButtonsHeld());
255 padData.hw_buttons_r[INPUT_HW_DRC] = MapWiiUGamepadToGeneric(WiiDRC_ButtonsUp());
256 padData.hw_stickX[INPUT_HW_DRC] = OgcStickToFloat(WiiDRC_lStickX());
257 padData.hw_stickY[INPUT_HW_DRC] = OgcStickToFloat(WiiDRC_lStickY());
258 padData.hw_substickX[INPUT_HW_DRC] = OgcStickToFloat(WiiDRC_rStickX());
259 padData.hw_substickY[INPUT_HW_DRC] = OgcStickToFloat(WiiDRC_rStickY());
260}
void setSideways(bool s)
Sets whether the Wiimote is held sideways; if so the d-pad and primary/secondary buttons are remapped...
int getWiimoteOrientation() const
Definition InputDriver.h:46
void shutdown() override
Shuts the controller subsystem down.
void driveRumble(int channel, bool on) override
Drives the rumble motor of any controller other than the GameCube pad on channel; called every frame ...
void readChannel(int channel, InputPadData &pad) override
Called once per channel per frame: add the extra hardware's state for channel to pad (each device in ...
void scanHardware() override
Called once per frame before the channels are read: poll the extra hardware.
void initHardware() override
Extension points for a console with more input hardware than GameCube pads.
bool isSystemRumbleAllowed() const override
False if the console's own settings disable controller rumble.
The universal input payload for UI elements.
Definition InputData.h:57
float hw_roll[INPUT_HW_MAX]
Orientation roll, per profile.
Definition InputData.h:100
uint32_t hw_buttons_d[INPUT_HW_MAX]
Buttons pressed this frame, per profile.
Definition InputData.h:86
float hw_gforceZ[INPUT_HW_MAX]
Accelerometer Z, per profile.
Definition InputData.h:98
float hw_triggerL[INPUT_HW_MAX]
Analog L trigger / shoulder, 0.0 - 1.0, per profile (GameCube, Classic, WUPC); 0 where the hardware h...
Definition InputData.h:93
float hw_triggerR[INPUT_HW_MAX]
Analog R trigger / shoulder, 0.0 - 1.0, per profile.
Definition InputData.h:94
float hw_stickY[INPUT_HW_MAX]
Left stick Y, per profile.
Definition InputData.h:90
float hw_substickX[INPUT_HW_MAX]
Right stick X, per profile.
Definition InputData.h:91
uint32_t battery_level
Controller battery level as reported by the backend.
Definition InputData.h:74
float cursor_x
Pointer X on the design canvas (Wiimote IR / GamePad touch)
Definition InputData.h:75
bool hw_connected[INPUT_HW_MAX]
Profile is connected and contributing.
Definition InputData.h:85
bool validPointer
True if the IR pointer or touch is active on screen.
Definition InputData.h:78
float cursor_angle
Pointer rotation (Wiimote roll)
Definition InputData.h:77
bool isTouch
True if input is direct touch (VPad) vs IR pointer.
Definition InputData.h:79
float hw_stickX[INPUT_HW_MAX]
Left stick X, per profile.
Definition InputData.h:89
float hw_gforceY[INPUT_HW_MAX]
Accelerometer Y, per profile.
Definition InputData.h:97
float cursor_y
Pointer Y on the design canvas.
Definition InputData.h:76
float hw_gforceX[INPUT_HW_MAX]
Accelerometer X, per profile.
Definition InputData.h:96
float hw_yaw[INPUT_HW_MAX]
Orientation yaw, per profile.
Definition InputData.h:101
uint32_t hw_buttons_h[INPUT_HW_MAX]
Buttons held this frame, per profile.
Definition InputData.h:87
float hw_pitch[INPUT_HW_MAX]
Orientation pitch, per profile.
Definition InputData.h:99
float hw_substickY[INPUT_HW_MAX]
Right stick Y, per profile.
Definition InputData.h:92
uint32_t hw_buttons_r[INPUT_HW_MAX]
Buttons released this frame, per profile.
Definition InputData.h:88