libgui 3.00
libgui is a GUI library for the GameCube, Wii, and Wii U
 
Loading...
Searching...
No Matches
OgcInputDriver.cpp
1/****************************************************************************
2 * Platform Abstraction Layer (OGC driver)
3 * Daryl Borth 2026
4 * OgcInputDriver.cpp
5 *
6 * Wii/GameCube controller management and normalization
7 ***************************************************************************/
8
9#include <gccore.h>
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13#include <cmath>
14#include <ogcsys.h>
15#include <unistd.h>
16
17#include "OgcInputDriver.h"
18#include "../Platform.h"
19#include "OgcInputUtil.h"
20#include "../InputController.h"
21
22/****************************************************************************
23 * Hardware Mapping Helpers
24 * Translates raw libogc hardware bits to our generic UI masks
25 ***************************************************************************/
26static uint32_t MapPADToGeneric(uint32_t pad_btns) {
27 uint32_t mask = INPUT_BTN_NONE;
28 if (pad_btns & PAD_BUTTON_A) mask |= INPUT_BTN_A;
29 if (pad_btns & PAD_BUTTON_B) mask |= INPUT_BTN_B;
30 if (pad_btns & PAD_BUTTON_X) mask |= INPUT_BTN_X;
31 if (pad_btns & PAD_BUTTON_Y) mask |= INPUT_BTN_Y;
32 if (pad_btns & PAD_BUTTON_UP) mask |= INPUT_BTN_UP;
33 if (pad_btns & PAD_BUTTON_DOWN) mask |= INPUT_BTN_DOWN;
34 if (pad_btns & PAD_BUTTON_LEFT) mask |= INPUT_BTN_LEFT;
35 if (pad_btns & PAD_BUTTON_RIGHT) mask |= INPUT_BTN_RIGHT;
36 if (pad_btns & PAD_BUTTON_START) mask |= INPUT_BTN_PLUS;
37 if (pad_btns & PAD_TRIGGER_L) mask |= INPUT_TRIGGER_L;
38 if (pad_btns & PAD_TRIGGER_R) mask |= INPUT_TRIGGER_R;
39 if (pad_btns & PAD_TRIGGER_Z) mask |= INPUT_TRIGGER_ZR;
40 return mask;
41}
42
43OgcInputDriver::OgcInputDriver() {
44 for (int i = 0; i < 4; i++) {
45 rumbleRequest[i] = continuousRumble[i] = false;
46 menuRumbleFrames[i] = continuousRumbleCount[i] = silenceFrames[i] = menuRumbleGapFrames[i] = 0;
47 }
48}
49
50OgcInputDriver::~OgcInputDriver() {
51 shutdown();
52}
53
55 PAD_Init();
57 InitUserInputControllers();
58}
59
61 for (int i = 0; i < 4; i++) {
62 driveRumble(i, false);
63 PAD_ControlMotor(i, PAD_MOTOR_STOP);
64 rumbleRequest[i] = continuousRumble[i] = false;
65 menuRumbleFrames[i] = continuousRumbleCount[i] = silenceFrames[i] = menuRumbleGapFrames[i] = 0;
66 }
67}
68
69void OgcInputDriver::setRumble(int channel, bool rumble) {
70 if (channel >= 0 && channel < 4) {
71 rumbleRequest[channel] = rumble;
72 }
73}
74
75void OgcInputDriver::setContinuousRumble(int channel, bool continuous) {
76 if (channel >= 0 && channel < 4) continuousRumble[channel] = continuous;
77}
78
81
82 u32 activeGamecubePads = PAD_ScanPads();
83 bool allowRumble = isRumbleEnabled() && isSystemRumbleAllowed();
84
85 for(int i = 3; i >= 0; i--) {
86 InputPadData padData;
87
88 bool gamecubeActive = (activeGamecubePads & (1 << i)) != 0;
89
90 if(gamecubeActive) {
91 padData.hw_connected[INPUT_HW_GAMECUBE] = true;
92 padData.hw_buttons_d[INPUT_HW_GAMECUBE] = MapPADToGeneric(PAD_ButtonsDown(i));
93 padData.hw_buttons_h[INPUT_HW_GAMECUBE] = MapPADToGeneric(PAD_ButtonsHeld(i));
94 padData.hw_buttons_r[INPUT_HW_GAMECUBE] = MapPADToGeneric(PAD_ButtonsUp(i));
95 padData.hw_stickX[INPUT_HW_GAMECUBE] = OgcStickToFloat(PAD_StickX(i));
96 padData.hw_stickY[INPUT_HW_GAMECUBE] = OgcStickToFloat(PAD_StickY(i));
97 padData.hw_substickX[INPUT_HW_GAMECUBE] = OgcStickToFloat(PAD_SubStickX(i));
98 padData.hw_substickY[INPUT_HW_GAMECUBE] = OgcStickToFloat(PAD_SubStickY(i));
99 padData.hw_triggerL[INPUT_HW_GAMECUBE] = OgcClampf((float)PAD_TriggerL(i) / 255.0f, 0.0f, 1.0f);
100 padData.hw_triggerR[INPUT_HW_GAMECUBE] = OgcClampf((float)PAD_TriggerR(i) / 255.0f, 0.0f, 1.0f);
101 }
102
103 // Whatever else this console has (Wiimotes, ...)
104 readChannel(i, padData);
105
106 // Merge into unified aggregate state for UI Elements
107 for (uint32_t hw = 0; hw < INPUT_HW_MAX; hw++) {
108 if (!padData.hw_connected[hw])
109 continue;
110
111 padData.buttons_d |= padData.hw_buttons_d[hw];
112 padData.buttons_h |= padData.hw_buttons_h[hw];
113 padData.buttons_r |= padData.hw_buttons_r[hw];
114
115 if (std::abs(padData.hw_stickX[hw]) > std::abs(padData.stickX)) padData.stickX = padData.hw_stickX[hw];
116 if (std::abs(padData.hw_stickY[hw]) > std::abs(padData.stickY)) padData.stickY = padData.hw_stickY[hw];
117 if (std::abs(padData.hw_substickX[hw]) > std::abs(padData.substickX)) padData.substickX = padData.hw_substickX[hw];
118 if (std::abs(padData.hw_substickY[hw]) > std::abs(padData.substickY)) padData.substickY = padData.hw_substickY[hw];
119 if (std::abs(padData.hw_gforceX[hw]) > std::abs(padData.gforceX)) padData.gforceX = padData.hw_gforceX[hw];
120 if (std::abs(padData.hw_gforceY[hw]) > std::abs(padData.gforceY)) padData.gforceY = padData.hw_gforceY[hw];
121 if (std::abs(padData.hw_gforceZ[hw]) > std::abs(padData.gforceZ)) padData.gforceZ = padData.hw_gforceZ[hw];
122 if (std::abs(padData.hw_pitch[hw]) > std::abs(padData.pitch)) padData.pitch = padData.hw_pitch[hw];
123 if (std::abs(padData.hw_roll[hw]) > std::abs(padData.roll)) padData.roll = padData.hw_roll[hw];
124 if (std::abs(padData.hw_yaw[hw]) > std::abs(padData.yaw)) padData.yaw = padData.hw_yaw[hw];
125 }
126
127 // Push the finalized, merged payload to the controller abstraction
128 controller[i]->update(padData, platform->getVideo()->getDeltaTime());
129
130 // Menu (hover) rumble: a short tick with an enforced silent gap afterward
131 static constexpr int kMenuRumbleOnFrames = 2; // ~33ms motor-on burst
132 static constexpr int kMenuRumbleGapFrames = 6; // ~100ms enforced silence after a tick
133
134 if (rumbleRequest[i]) {
135 rumbleRequest[i] = false;
136 if (menuRumbleFrames[i] == 0 && menuRumbleGapFrames[i] == 0) {
137 menuRumbleFrames[i] = kMenuRumbleOnFrames;
138 }
139 }
140
141 bool menuWantRumble = menuRumbleFrames[i] > 0;
142
143 if (menuRumbleFrames[i] > 0) {
144 menuRumbleFrames[i]--;
145 if (menuRumbleFrames[i] == 0) menuRumbleGapFrames[i] = kMenuRumbleGapFrames;
146 } else if (menuRumbleGapFrames[i] > 0) {
147 menuRumbleGapFrames[i]--;
148 }
149
150 bool wantRumble = menuWantRumble || continuousRumble[i];
151 bool motorOn = false;
152
153 // Hardware safety: never drive the motor for more than ~70 consecutive
154 // frames; force a short rest (5 frames) before it can run again.
155 if (silenceFrames[i] > 0) {
156 silenceFrames[i]--;
157 continuousRumbleCount[i] = 0;
158 } else {
159 if (wantRumble && allowRumble) {
160 continuousRumbleCount[i]++;
161 if (continuousRumbleCount[i] > 70) {
162 silenceFrames[i] = 5;
163 continuousRumbleCount[i] = 0;
164 } else {
165 motorOn = true;
166 }
167 } else {
168 continuousRumbleCount[i] = 0;
169 }
170 }
171
172 driveRumble(i, motorOn);
173
174 if (gamecubeActive) {
175 PAD_ControlMotor(i, motorOn ? PAD_MOTOR_RUMBLE : PAD_MOTOR_STOP);
176 }
177 }
178}
bool isRumbleEnabled() const
Definition InputDriver.h:42
void update() override
Polls the hardware and dispatches InputPadData payloads to the UI.
void shutdown() override
Shuts the controller subsystem down.
virtual void driveRumble(int channel, bool on)
Drives the rumble motor of any controller other than the GameCube pad on channel; called every frame ...
void init() override
Initializes the controller subsystem.
virtual void readChannel(int channel, InputPadData &pad)
Called once per channel per frame: add the extra hardware's state for channel to pad (each device in ...
void setContinuousRumble(int channel, bool continuous) override
Holds a channel's motor on (or releases it) until changed. Drivers cap an unbroken run and insert a s...
void setRumble(int channel, bool rumble) override
Requests a rumble event on the specified controller channel.
virtual void scanHardware()
Called once per frame before the channels are read: poll the extra hardware.
virtual bool isSystemRumbleAllowed() const
False if the console's own settings disable controller rumble.
virtual void initHardware()
Extension points for a console with more input hardware than GameCube pads.
The universal input payload for UI elements.
Definition InputData.h:57
uint32_t buttons_d
Pressed this frame (down)
Definition InputData.h:61
float stickX
Left analog stick X, -1.0 to 1.0.
Definition InputData.h:64
float hw_roll[INPUT_HW_MAX]
Orientation roll, per profile.
Definition InputData.h:100
float gforceY
Accelerometer Y.
Definition InputData.h:69
uint32_t hw_buttons_d[INPUT_HW_MAX]
Buttons pressed this frame, per profile.
Definition InputData.h:86
float substickY
Right analog stick (C-Stick) Y, -1.0 to 1.0.
Definition InputData.h:67
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
float pitch
Controller orientation pitch (Wiimote/Nunchuk)
Definition InputData.h:71
bool hw_connected[INPUT_HW_MAX]
Profile is connected and contributing.
Definition InputData.h:85
float yaw
Controller orientation yaw.
Definition InputData.h:73
float gforceX
Accelerometer X (Wiimote/Nunchuk/GamePad)
Definition InputData.h:68
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 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 substickX
Right analog stick (C-Stick) X, -1.0 to 1.0.
Definition InputData.h:66
float hw_pitch[INPUT_HW_MAX]
Orientation pitch, per profile.
Definition InputData.h:99
float roll
Controller orientation roll.
Definition InputData.h:72
float stickY
Left analog stick Y, -1.0 to 1.0.
Definition InputData.h:65
uint32_t buttons_r
Released this frame.
Definition InputData.h:63
float hw_substickY[INPUT_HW_MAX]
Right stick Y, per profile.
Definition InputData.h:92
uint32_t buttons_h
Held this frame.
Definition InputData.h:62
uint32_t hw_buttons_r[INPUT_HW_MAX]
Buttons released this frame, per profile.
Definition InputData.h:88
float gforceZ
Accelerometer Z.
Definition InputData.h:70