1 /*
2  *  Copyright (C) 2014-2020 Team Kodi (https://kodi.tv)
3  *
4  *  SPDX-License-Identifier: GPL-2.0-or-later
5  *  See LICENSE.md for more information.
6  */
7 
8 #include "FrontendBridge.h"
9 #include "LibretroEnvironment.h"
10 #include "LibretroTranslator.h"
11 #include "input/ButtonMapper.h"
12 #include "input/InputManager.h"
13 #include "client.h"
14 
15 #include <algorithm>
16 #include <assert.h>
17 #include <kodi/General.h>
18 
19 using namespace LIBRETRO;
20 
21 #define S16NE_FRAMESIZE  4 // int16 L + int16 R
22 
23 #define MAX_RUMBLE_STRENGTH  0xffff
24 
25 #ifndef CONSTRAIN
26   // Credit: https://stackoverflow.com/questions/8941262/constrain-function-port-from-arduino
27   #define CONSTRAIN(amt, low, high)  ((amt) < (low) ? (low) : ((amt) > (high) ? (high) : (amt)))
28 #endif
29 
LogFrontend(retro_log_level level,const char * fmt,...)30 void CFrontendBridge::LogFrontend(retro_log_level level, const char *fmt, ...)
31 {
32   AddonLog xbmcLevel;
33   switch (level)
34   {
35   case RETRO_LOG_DEBUG: xbmcLevel = ADDON_LOG_DEBUG; break;
36   case RETRO_LOG_INFO:  xbmcLevel = ADDON_LOG_INFO;  break;
37   case RETRO_LOG_WARN:  xbmcLevel = ADDON_LOG_ERROR; break;
38   case RETRO_LOG_ERROR: xbmcLevel = ADDON_LOG_ERROR; break;
39   default:              xbmcLevel = ADDON_LOG_ERROR; break;
40   }
41 
42   char buffer[16384];
43   va_list args;
44   va_start(args, fmt);
45   vsprintf(buffer, fmt, args);
46   va_end(args);
47 
48   kodi::Log(xbmcLevel, buffer);
49 }
50 
VideoRefresh(const void * data,unsigned int width,unsigned int height,size_t pitch)51 void CFrontendBridge::VideoRefresh(const void* data, unsigned int width, unsigned int height, size_t pitch)
52 {
53   if (data == RETRO_HW_FRAME_BUFFER_VALID)
54   {
55     CLibretroEnvironment::Get().Video().RenderHwFrame();
56   }
57   else if (data == nullptr)
58   {
59     // Libretro is sending a frame dupe command
60     CLibretroEnvironment::Get().Video().DupeFrame();
61   }
62   else
63   {
64     CLibretroEnvironment::Get().Video().AddFrame(static_cast<const uint8_t*>(data),
65                                                  static_cast<unsigned int>(pitch * height),
66                                                  width,
67                                                  height,
68                                                  CLibretroEnvironment::Get().GetVideoFormat(),
69                                                  CLibretroEnvironment::Get().GetVideoRotation());
70   }
71 }
72 
AudioFrame(int16_t left,int16_t right)73 void CFrontendBridge::AudioFrame(int16_t left, int16_t right)
74 {
75   CLibretroEnvironment::Get().Audio().AddFrame_S16NE(left, right);
76 }
77 
AudioFrames(const int16_t * data,size_t frames)78 size_t CFrontendBridge::AudioFrames(const int16_t* data, size_t frames)
79 {
80   CLibretroEnvironment::Get().Audio().AddFrames_S16NE(reinterpret_cast<const uint8_t*>(data),
81                                                       static_cast<unsigned int>(frames * S16NE_FRAMESIZE));
82 
83   return frames;
84 }
85 
InputPoll(void)86 void CFrontendBridge::InputPoll(void)
87 {
88   // Not needed
89 }
90 
InputState(unsigned int port,unsigned int device,unsigned int index,unsigned int id)91 int16_t CFrontendBridge::InputState(unsigned int port, unsigned int device, unsigned int index, unsigned int id)
92 {
93   int16_t inputState = 0;
94 
95   // According to libretro.h, device should already be masked, but just in case
96   device &= RETRO_DEVICE_MASK;
97 
98   switch (device)
99   {
100   case RETRO_DEVICE_JOYPAD:
101   case RETRO_DEVICE_KEYBOARD:
102     inputState = CInputManager::Get().ButtonState(device, port, id) ? 1 : 0;
103     break;
104 
105   case RETRO_DEVICE_MOUSE:
106   case RETRO_DEVICE_LIGHTGUN:
107     static_assert(RETRO_DEVICE_ID_MOUSE_X == RETRO_DEVICE_ID_LIGHTGUN_X, "RETRO_DEVICE_ID_MOUSE_X != RETRO_DEVICE_ID_LIGHTGUN_X");
108     static_assert(RETRO_DEVICE_ID_MOUSE_Y == RETRO_DEVICE_ID_LIGHTGUN_Y, "RETRO_DEVICE_ID_MOUSE_Y != RETRO_DEVICE_ID_LIGHTGUN_Y");
109 
110     switch (id)
111     {
112       case RETRO_DEVICE_ID_MOUSE_X:
113         inputState = CInputManager::Get().DeltaX(device, port);
114         break;
115       case RETRO_DEVICE_ID_MOUSE_Y:
116         inputState = CInputManager::Get().DeltaY(device, port);
117         break;
118       default:
119       {
120         inputState = CInputManager::Get().ButtonState(device, port, id) ? 1 : 0;
121         break;
122       }
123     }
124     break;
125 
126   case RETRO_DEVICE_ANALOG:
127   {
128     float value = 0.0f; // Axis value between -1 and 1
129 
130     if (index == RETRO_DEVICE_INDEX_ANALOG_BUTTON)
131     {
132       value = CInputManager::Get().AnalogButtonState(port, id);
133     }
134     else
135     {
136       float x, y;
137       if (CInputManager::Get().AnalogStickState(port, index, x, y))
138       {
139         if (id == RETRO_DEVICE_ID_ANALOG_X)
140         {
141           value = x;
142         }
143         else if (id == RETRO_DEVICE_ID_ANALOG_Y)
144         {
145           value = -y; // y axis is inverted
146         }
147       }
148     }
149 
150     const float normalized = (value + 1.0f) / 2.0f;
151     const int clamped = std::max(0, std::min(0xffff, static_cast<int>(normalized * 0xffff)));
152     inputState = clamped - 0x8000;
153     break;
154   }
155 
156   case RETRO_DEVICE_POINTER:
157   {
158     float x, y;
159     if (CInputManager::Get().AbsolutePointerState(port, index, x, y))
160     {
161       if (id == RETRO_DEVICE_ID_POINTER_X)
162       {
163         inputState = (int)(x * 0x7fff);
164       }
165       else if (id == RETRO_DEVICE_ID_POINTER_Y)
166       {
167         inputState = (int)(y * 0x7fff);
168       }
169       else if (id == RETRO_DEVICE_ID_POINTER_PRESSED)
170       {
171         inputState = 1;
172       }
173     }
174     break;
175   }
176 
177   default:
178     break;
179   }
180 
181   return inputState;
182 }
183 
HwGetCurrentFramebuffer(void)184 uintptr_t CFrontendBridge::HwGetCurrentFramebuffer(void)
185 {
186   if (!CLibretroEnvironment::Get().GetAddon())
187     return 0;
188 
189   return CLibretroEnvironment::Get().Video().GetHwFramebuffer();
190 }
191 
HwGetProcAddress(const char * sym)192 retro_proc_address_t CFrontendBridge::HwGetProcAddress(const char *sym)
193 {
194   if (!CLibretroEnvironment::Get().GetAddon())
195     return nullptr;
196 
197   return CLibretroEnvironment::Get().GetAddon()->HwGetProcAddress(sym);
198 }
199 
RumbleSetState(unsigned int port,retro_rumble_effect effect,uint16_t strength)200 bool CFrontendBridge::RumbleSetState(unsigned int port, retro_rumble_effect effect, uint16_t strength)
201 {
202   if (!CLibretroEnvironment::Get().GetAddon())
203     return false;
204 
205   std::string controllerId  = CInputManager::Get().ControllerID(port);
206   std::string address       = CInputManager::Get().GetAddress(port);
207   std::string libretroMotor = LibretroTranslator::GetMotorName(effect);
208   std::string featureName   = CButtonMapper::Get().GetControllerFeature(controllerId, libretroMotor);
209   float       magnitude     = static_cast<float>(strength) / MAX_RUMBLE_STRENGTH;
210 
211   if (controllerId.empty() || address.empty() || featureName.empty())
212     return false;
213 
214   game_input_event eventStruct;
215   eventStruct.type            = GAME_INPUT_EVENT_MOTOR;
216   eventStruct.controller_id   = controllerId.c_str();
217   eventStruct.port_address    = address.c_str();
218   eventStruct.port_type       = GAME_PORT_CONTROLLER;
219   eventStruct.feature_name    = featureName.c_str();
220   eventStruct.motor.magnitude = CONSTRAIN(magnitude, 0.0f, 1.0f);
221 
222   CLibretroEnvironment::Get().GetAddon()->KodiInputEvent(eventStruct);
223   return true;
224 }
225 
SensorSetState(unsigned port,retro_sensor_action action,unsigned rate)226 bool CFrontendBridge::SensorSetState(unsigned port, retro_sensor_action action, unsigned rate)
227 {
228   const bool bEnabled = (action == RETRO_SENSOR_ACCELEROMETER_ENABLE);
229 
230   CInputManager::Get().EnableAnalogSensors(port, bEnabled);
231 
232   return true;
233 }
234 
SensorGetInput(unsigned port,unsigned id)235 float CFrontendBridge::SensorGetInput(unsigned port, unsigned id)
236 {
237   float axisState = 0.0f;
238 
239   float x, y, z;
240   if (CInputManager::Get().AccelerometerState(port, x, y, z))
241   {
242     switch (id)
243     {
244     case RETRO_SENSOR_ACCELEROMETER_X:
245       axisState = x;
246       break;
247     case RETRO_SENSOR_ACCELEROMETER_Y:
248       axisState = y;
249       break;
250     case RETRO_SENSOR_ACCELEROMETER_Z:
251       axisState = z;
252       break;
253     default:
254       break;
255     }
256   }
257 
258   return axisState;
259 }
260 
StartCamera(void)261 bool CFrontendBridge::StartCamera(void)
262 {
263   return false; // Not implemented
264 }
265 
StopCamera(void)266 void CFrontendBridge::StopCamera(void)
267 {
268   // Not implemented
269 }
270 
PerfGetTimeUsec(void)271 retro_time_t CFrontendBridge::PerfGetTimeUsec(void)
272 {
273   return 0; // Not implemented
274 }
275 
PerfGetCounter(void)276 retro_perf_tick_t CFrontendBridge::PerfGetCounter(void)
277 {
278   return 0; // Not implemented
279 }
280 
PerfGetCpuFeatures(void)281 uint64_t CFrontendBridge::PerfGetCpuFeatures(void)
282 {
283   return 0; // Not implemented
284 }
285 
PerfLog(void)286 void CFrontendBridge::PerfLog(void)
287 {
288   // Not implemented
289 }
290 
PerfRegister(retro_perf_counter * counter)291 void CFrontendBridge::PerfRegister(retro_perf_counter *counter)
292 {
293   // Not implemented
294 }
295 
PerfStart(retro_perf_counter * counter)296 void CFrontendBridge::PerfStart(retro_perf_counter *counter)
297 {
298   // Not implemented
299 }
300 
PerfStop(retro_perf_counter * counter)301 void CFrontendBridge::PerfStop(retro_perf_counter *counter)
302 {
303   // Not implemented
304 }
305 
StartLocation(void)306 bool CFrontendBridge::StartLocation(void)
307 {
308   return false; // Not implemented
309 }
310 
StopLocation(void)311 void CFrontendBridge::StopLocation(void)
312 {
313   // Not implemented
314 }
315 
GetLocation(double * lat,double * lon,double * horiz_accuracy,double * vert_accuracy)316 bool CFrontendBridge::GetLocation(double *lat, double *lon, double *horiz_accuracy, double *vert_accuracy)
317 {
318   return false; // Not implemented
319 }
320 
SetLocationInterval(unsigned interval_ms,unsigned interval_distance)321 void CFrontendBridge::SetLocationInterval(unsigned interval_ms, unsigned interval_distance)
322 {
323   // Not implemented
324 }
325 
LocationInitialized(void)326 void CFrontendBridge::LocationInitialized(void)
327 {
328   // Not implemented
329 }
330 
LocationDeinitialized(void)331 void CFrontendBridge::LocationDeinitialized(void)
332 {
333   // Not implemented
334 }
335