1 /*******************************************************************************
2   Copyright (C) 2013 Jasem Mutlaq <mutlaqja@ikarustech.com>
3 
4  This library is free software; you can redistribute it and/or
5  modify it under the terms of the GNU Library General Public
6  License version 2 as published by the Free Software Foundation.
7 
8  This library is distributed in the hope that it will be useful,
9  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  Library General Public License for more details.
12 
13  You should have received a copy of the GNU Library General Public License
14  along with this library; see the file COPYING.LIB.  If not, write to
15  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
16  Boston, MA 02110-1301, USA.
17 *******************************************************************************/
18 
19 #include "indicontroller.h"
20 
21 #include <cstring>
22 
23 namespace INDI
24 {
Controller(DefaultDevice * cdevice)25 Controller::Controller(DefaultDevice *cdevice)
26 {
27     device = cdevice;
28 
29     JoystickSettingT      = nullptr;
30     JoystickSettingTP.ntp = 0;
31 
32     joystickCallbackFunc = joystickEvent;
33     axisCallbackFunc     = axisEvent;
34     buttonCallbackFunc   = buttonEvent;
35 }
36 
~Controller()37 Controller::~Controller()
38 {
39     for (int i = 0; i < JoystickSettingTP.ntp; i++)
40         free(JoystickSettingT[i].aux0);
41 
42     free(JoystickSettingT);
43 }
44 
mapController(const char * propertyName,const char * propertyLabel,ControllerType type,const char * initialValue)45 void Controller::mapController(const char *propertyName, const char *propertyLabel, ControllerType type,
46                                const char *initialValue)
47 {
48     if (JoystickSettingT == nullptr)
49         JoystickSettingT = static_cast<IText *>(malloc(sizeof(IText)));
50 
51     // Ignore duplicates
52     for (int i = 0; i < JoystickSettingTP.ntp; i++)
53     {
54         if (!strcmp(propertyName, JoystickSettingT[i].name))
55             return;
56     }
57 
58     IText *buf = static_cast<IText *>(realloc(JoystickSettingT, (JoystickSettingTP.ntp + 1) * sizeof(IText)));
59     if (buf == nullptr)
60     {
61         free (JoystickSettingT);
62         perror("Failed to allocate memory for joystick controls.");
63         return;
64     }
65     else
66         JoystickSettingT = buf;
67 
68     ControllerType *ctype = static_cast<ControllerType *>(malloc(sizeof(ControllerType)));
69     *ctype                = type;
70 
71     memset(JoystickSettingT + JoystickSettingTP.ntp, 0, sizeof(IText));
72     IUFillText(&JoystickSettingT[JoystickSettingTP.ntp], propertyName, propertyLabel, initialValue);
73 
74     JoystickSettingT[JoystickSettingTP.ntp++].aux0 = ctype;
75 
76     IUFillTextVector(&JoystickSettingTP, JoystickSettingT, JoystickSettingTP.ntp, device->getDeviceName(),
77                      "JOYSTICKSETTINGS", "Settings", "Joystick", IP_RW, 0, IPS_IDLE);
78 }
79 
clearMap()80 void Controller::clearMap()
81 {
82     for (int i = 0; i < JoystickSettingTP.ntp; i++)
83     {
84         free(JoystickSettingT[i].aux0);
85         free(JoystickSettingT[i].text);
86     }
87 
88     JoystickSettingTP.ntp = 0;
89     free(JoystickSettingT);
90     JoystickSettingT = nullptr;
91 }
92 
initProperties()93 bool Controller::initProperties()
94 {
95     IUFillSwitch(&UseJoystickS[0], "ENABLE", "Enable", ISS_OFF);
96     IUFillSwitch(&UseJoystickS[1], "DISABLE", "Disable", ISS_ON);
97     IUFillSwitchVector(&UseJoystickSP, UseJoystickS, 2, device->getDeviceName(), "USEJOYSTICK", "Joystick", OPTIONS_TAB,
98                        IP_RW, ISR_1OFMANY, 0, IPS_IDLE);
99 
100     IUFillText(&JoystickDeviceT[0], "SNOOP_JOYSTICK_DEVICE", "Device", "Joystick");
101     IUFillTextVector(&JoystickDeviceTP, JoystickDeviceT, 1, device->getDeviceName(), "SNOOP_JOYSTICK", "Snoop Joystick",
102                      OPTIONS_TAB, IP_RW, 60, IPS_IDLE);
103 
104     return true;
105 }
106 
ISGetProperties(const char * dev)107 void Controller::ISGetProperties(const char *dev)
108 {
109     if (dev != nullptr && strcmp(dev, device->getDeviceName()))
110         return;
111 
112     if (device->isConnected())
113     {
114         device->defineProperty(&UseJoystickSP);
115         device->defineProperty(&JoystickDeviceTP);
116 
117         if (JoystickSettingT && UseJoystickS[0].s == ISS_ON)
118             device->defineProperty(&JoystickSettingTP);
119     }
120 }
121 
updateProperties()122 bool Controller::updateProperties()
123 {
124     if (device->isConnected())
125     {
126         device->defineProperty(&UseJoystickSP);
127         device->defineProperty(&JoystickDeviceTP);
128 
129         if (JoystickSettingT && UseJoystickS[0].s == ISS_ON)
130             device->defineProperty(&JoystickSettingTP);
131     }
132     else
133     {
134         device->deleteProperty(UseJoystickSP.name);
135         device->deleteProperty(JoystickDeviceTP.name);
136         device->deleteProperty(JoystickSettingTP.name);
137     }
138 
139     return true;
140 }
141 
ISNewSwitch(const char * dev,const char * name,ISState * states,char * names[],int n)142 bool Controller::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
143 {
144     if (strcmp(dev, device->getDeviceName()) == 0)
145     {
146         // Enable joystick support
147         if (!strcmp(name, UseJoystickSP.name))
148         {
149             IUUpdateSwitch(&UseJoystickSP, states, names, n);
150 
151             UseJoystickSP.s = IPS_OK;
152 
153             if (UseJoystickSP.sp[0].s == ISS_ON)
154                 enableJoystick();
155             else
156                 disableJoystick();
157 
158             IDSetSwitch(&UseJoystickSP, nullptr);
159 
160             return true;
161         }
162     }
163 
164     return false;
165 }
166 
ISNewText(const char * dev,const char * name,char * texts[],char * names[],int n)167 bool Controller::ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
168 {
169     if (strcmp(dev, device->getDeviceName()) == 0)
170     {
171         if (!strcmp(name, "SNOOP_JOYSTICK"))
172         {
173             IUUpdateText(&JoystickDeviceTP, texts, names, n);
174             JoystickDeviceTP.s = IPS_IDLE;
175 
176             IDSetText(&JoystickDeviceTP, nullptr);
177 
178             if (UseJoystickSP.sp[0].s == ISS_ON)
179             {
180                 // Let's switch over to the new joystick.
181                 enableJoystick();
182             }
183 
184             return true;
185         }
186 
187         if (!strcmp(name, "JOYSTICKSETTINGS") && n <= JoystickSettingTP.ntp)
188         {
189             for (int i = 0; i < JoystickSettingTP.ntp; i++)
190             {
191                 IText *tp = IUFindText(&JoystickSettingTP, names[i]);
192                 if (tp)
193                 {
194                     ControllerType cType  = getControllerType(texts[i]);
195                     ControllerType myType = *(static_cast<ControllerType *>(JoystickSettingT[i].aux0));
196                     if (cType != myType)
197                     {
198                         JoystickSettingTP.s = IPS_ALERT;
199                         IDSetText(&JoystickSettingTP, nullptr);
200                         DEBUGFDEVICE(dev, INDI::Logger::DBG_ERROR, "Cannot change controller type to %s.", texts[i]);
201                         return false;
202                     }
203                 }
204             }
205 
206             IUUpdateText(&JoystickSettingTP, texts, names, n);
207 
208             for (int i = 0; i < n; i++)
209             {
210                 if (strstr(JoystickSettingT[i].text, "JOYSTICK_"))
211                     IDSnoopDevice(JoystickDeviceT[0].text, JoystickSettingT[i].text);
212             }
213 
214             JoystickSettingTP.s = IPS_OK;
215             IDSetText(&JoystickSettingTP, nullptr);
216             return true;
217         }
218     }
219 
220     return false;
221 }
222 
ISSnoopDevice(XMLEle * root)223 bool Controller::ISSnoopDevice(XMLEle *root)
224 {
225     XMLEle *ep = nullptr;
226     double mag = 0, angle = 0;
227 
228     // If joystick is disabled, do not process anything.
229     if (UseJoystickSP.sp[0].s == ISS_OFF)
230         return false;
231 
232     const char *propName = findXMLAttValu(root, "name");
233 
234     // Check axis
235     if (!strcmp("JOYSTICK_AXES", propName))
236     {
237         for (ep = nextXMLEle(root, 1); ep != nullptr; ep = nextXMLEle(root, 0))
238         {
239             const char *elemName = findXMLAttValu(ep, "name");
240             const char *setting  = getControllerSetting(elemName);
241             if (setting == nullptr)
242                 continue;
243 
244             mag = atof(pcdataXMLEle(ep));
245 
246             axisCallbackFunc(setting, mag, device);
247         }
248     }
249     // Check buttons
250     else if (!strcmp("JOYSTICK_BUTTONS", propName))
251     {
252         for (ep = nextXMLEle(root, 1); ep != nullptr; ep = nextXMLEle(root, 0))
253         {
254             const char *elemName = findXMLAttValu(ep, "name");
255             const char *setting  = getControllerSetting(elemName);
256 
257             if (setting == nullptr)
258                 continue;
259 
260             buttonCallbackFunc(setting, strcmp(pcdataXMLEle(ep), "Off") ? ISS_ON : ISS_OFF, device);
261         }
262     }
263     // Check joysticks
264     else if (strstr(propName, "JOYSTICK_"))
265     {
266         const char *setting = getControllerSetting(propName);
267 
268         // We don't have this here, so let's not process it
269         if (setting == nullptr)
270             return false;
271 
272         for (ep = nextXMLEle(root, 1); ep != nullptr; ep = nextXMLEle(root, 0))
273         {
274             if (!strcmp("JOYSTICK_MAGNITUDE", findXMLAttValu(ep, "name")))
275                 mag = atof(pcdataXMLEle(ep));
276             else if (!strcmp("JOYSTICK_ANGLE", findXMLAttValu(ep, "name")))
277                 angle = atof(pcdataXMLEle(ep));
278         }
279 
280         joystickCallbackFunc(setting, mag, angle, device);
281     }
282 
283     return false;
284 }
285 
saveConfigItems(FILE * fp)286 bool Controller::saveConfigItems(FILE *fp)
287 {
288     IUSaveConfigSwitch(fp, &UseJoystickSP);
289     IUSaveConfigText(fp, &JoystickSettingTP);
290     IUSaveConfigText(fp, &JoystickDeviceTP);
291 
292     return true;
293 }
294 
enableJoystick()295 void Controller::enableJoystick()
296 {
297     device->defineProperty(&JoystickSettingTP);
298 
299     for (int i = 0; i < JoystickSettingTP.ntp; i++)
300     {
301         if (strstr(JoystickSettingTP.tp[i].text, "JOYSTICK_"))
302             IDSnoopDevice(JoystickDeviceT[0].text, JoystickSettingTP.tp[i].text);
303     }
304 
305     IDSnoopDevice(JoystickDeviceT[0].text, "JOYSTICK_AXES");
306     IDSnoopDevice(JoystickDeviceT[0].text, "JOYSTICK_BUTTONS");
307 }
308 
disableJoystick()309 void Controller::disableJoystick()
310 {
311     device->deleteProperty(JoystickSettingTP.name);
312 }
313 
setJoystickCallback(joystickFunc JoystickCallback)314 void Controller::setJoystickCallback(joystickFunc JoystickCallback)
315 {
316     joystickCallbackFunc = JoystickCallback;
317 }
318 
setAxisCallback(axisFunc AxisCallback)319 void Controller::setAxisCallback(axisFunc AxisCallback)
320 {
321     axisCallbackFunc = AxisCallback;
322 }
323 
setButtonCallback(buttonFunc buttonCallback)324 void Controller::setButtonCallback(buttonFunc buttonCallback)
325 {
326     buttonCallbackFunc = buttonCallback;
327 }
328 
joystickEvent(const char * joystick_n,double mag,double angle,void * context)329 void Controller::joystickEvent(const char *joystick_n, double mag, double angle, void *context)
330 {
331     INDI_UNUSED(joystick_n);
332     INDI_UNUSED(mag);
333     INDI_UNUSED(angle);
334     INDI_UNUSED(context);
335 }
336 
axisEvent(const char * axis_n,int value,void * context)337 void Controller::axisEvent(const char *axis_n, int value, void *context)
338 {
339     INDI_UNUSED(axis_n);
340     INDI_UNUSED(value);
341     INDI_UNUSED(context);
342 }
343 
buttonEvent(const char * button_n,int button_value,void * context)344 void Controller::buttonEvent(const char *button_n, int button_value, void *context)
345 {
346     INDI_UNUSED(button_n);
347     INDI_UNUSED(button_value);
348     INDI_UNUSED(context);
349 }
350 
getControllerType(const char * name)351 Controller::ControllerType Controller::getControllerType(const char *name)
352 {
353     ControllerType targetType = CONTROLLER_UNKNOWN;
354 
355     if (strstr(name, "JOYSTICK_"))
356         targetType = CONTROLLER_JOYSTICK;
357     else if (strstr(name, "AXIS_"))
358         targetType = CONTROLLER_AXIS;
359     else if (strstr(name, "BUTTON_"))
360         targetType = CONTROLLER_BUTTON;
361 
362     return targetType;
363 }
364 
getControllerSetting(const char * name)365 const char *Controller::getControllerSetting(const char *name)
366 {
367     for (int i = 0; i < JoystickSettingTP.ntp; i++)
368         if (!strcmp(JoystickSettingT[i].text, name))
369             return JoystickSettingT[i].name;
370 
371     return nullptr;
372 }
373 }
374