--- /dev/null
+/*
+ Copyright 2020 Chris Tallon
+
+ This file is part of VOMP.
+
+ VOMP is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ VOMP is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with VOMP. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+#include <sstream>
+#include <algorithm>
+#include <stdexcept>
+
+#include "log.h"
+
+#include "inputlirc.h"
+
+const char* InputLIRC::myModName = "InputLIRC";
+
+bool InputLIRC::init()
+{
+ if (initted) return false;
+ initted = true;
+ log = Log::getInstance();
+ log->log(myModName, Log::DEBUG, "Starting InputLIRC");
+
+ if (!tcp.init())
+ {
+ log->log(myModName, Log::DEBUG, "TCP init error");
+ initted = false;
+ return false;
+ }
+
+ /* FIXME
+ * Hard code remote keys for now. Put lines like this in inputlirc.conf
+ * The 'THREE' is the VOMP key name, see input.h
+ * remotes["lirc-remote-name"]["lirc-button-name"] = THREE;
+ */
+ #include "inputlirc.conf"
+
+ return true;
+}
+
+void InputLIRC::shutdown()
+{
+ stop();
+
+ remotes.clear(); // FIXME ?
+
+ initted = false;
+}
+
+bool InputLIRC::start(const std::string& ip, USHORT port)
+{
+ // FIXME implement unix domain socket connection
+
+ bool tr = tcp.connect(ip, port); // NCONFIG
+ if (!tr)
+ {
+ log->log(myModName, Log::DEBUG, "InputLIRC TCP connect failed");
+ return false;
+ }
+
+ threadStartProtect.lock(); // Make sure listenThread is fully initted before start returns
+ listenThread = std::thread( [this]
+ {
+ threadStartProtect.lock();
+ threadStartProtect.unlock();
+ listenLoop();
+ });
+ threadStartProtect.unlock();
+
+ log->log(myModName, Log::DEBUG, "InputLIRC command client started");
+ return true;
+}
+
+void InputLIRC::stop()
+{
+ std::lock_guard<std::mutex> lg(threadStartProtect); // Also use it to protect against starting while stopping
+
+ if (!tcp.status()) return;
+
+ if (listenThread.joinable())
+ {
+ threadReqStop = true;
+ tcp.abortCall();
+ listenThread.join();
+ threadReqStop = false;
+ }
+
+ tcp.shutdown();
+}
+
+void InputLIRC::listenLoop()
+{
+ bool readSuccess;
+
+ while(1)
+ {
+ std::stringstream ss = tcp.readString(&readSuccess, 0);
+
+ if (threadReqStop) return;
+
+ if (!readSuccess)
+ {
+ if (!tcp.status())
+ {
+ log->log(myModName, Log::CRIT, "TCP status fail");
+ return;
+ } // return to stop this thread
+
+ continue;
+ }
+
+ log->log(myModName, Log::DEBUG, "got data: %s", ss.str().c_str());
+
+ std::string remoteName, remoteButton;
+ int repeatCount;
+ if (parse(ss.str(), remoteName, remoteButton, repeatCount))
+ log->log(myModName, Log::DEBUG, "Remote: '%s', Button: '%s', Count: '%i'", remoteName.c_str(), remoteButton.c_str(), repeatCount);
+ else
+ log->log(myModName, Log::ERR, "Parse error");
+
+ UCHAR button;
+ try
+ {
+ button = remotes.at(remoteName).at(remoteButton);
+ }
+ catch (std::exception& e)
+ {
+ log->log(myModName, Log::ERR, "Remote button not found");
+ continue;
+ }
+
+ if (repeatCount == 0) sendInputKey(button);
+ }
+}
+
+bool InputLIRC::parse(const std::string& input, std::string& remoteName, std::string& remoteButton, int& repeatCount)
+{
+ UINT pos{}, found{};
+ found = input.find(" ", pos);
+ if (found == std::string::npos) return false;
+ pos = found + 1; // pos at 00
+ found = input.find(" ", pos);
+ if (found == std::string::npos) return false;
+ std::string rcString = input.substr(pos, found - pos);
+ try { repeatCount = std::stoi(rcString, 0, 16); } catch (std::exception& e) { return false; }
+ pos = found + 1; // pos at KE
+ found = input.find(" ", pos);
+ if (found == std::string::npos)
+ {
+ remoteButton = input.substr(pos);
+ return true;
+ }
+ remoteButton = input.substr(pos, found - pos);
+ pos = found + 1;
+ if ((input.length() - pos) > 0)
+ remoteName = input.substr(pos);
+ return true;
+}
+
+
+// const char* InputLIRC::getHardCodedHardwareKeyNamesForVompKey(UCHAR /* vompKey */)
+// {
+// return "";
+// }
+
+std::string InputLIRC::getHardwareKeyName(HWC_TYPE /* hardwareKey */)
+{
+ std::string retval;
+ return retval;
+}
--- /dev/null
+/*
+ Copyright 2020 Chris Tallon
+
+ This file is part of VOMP.
+
+ VOMP is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ VOMP is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with VOMP. If not, see <https://www.gnu.org/licenses/>.
+*/
+
+#ifndef INPUTLIRC_H
+#define INPUTLIRC_H
+
+#include <thread>
+#include <mutex>
+#include <map>
+#include <string>
+
+#include "defines.h"
+#include "tcp.h"
+#include "input.h"
+
+class Log;
+
+class InputLIRC : public Input
+{
+ public:
+ bool init();
+ void shutdown();
+
+ bool start(const std::string& ip, USHORT port);
+ void stop();
+
+ void InitHWCListwithDefaults() {};
+ std::string getHardwareKeyName(HWC_TYPE hardwareKey);
+
+ private:
+ Log* log{};
+
+ static const char* myModName;
+ const char* modName() { return myModName; }
+
+ std::thread listenThread;
+ std::mutex threadStartProtect;
+ void listenLoop();
+ bool threadReqStop{};
+
+ bool parse(const std::string& input, std::string& remoteName, std::string& remoteButton, int& repeatCount);
+ UCHAR TranslateHWCFixed(HWC_TYPE code) { return static_cast<UCHAR>(code); };
+
+ bool initted{};
+ TCP tcp;
+ std::map<std::string, std::map<std::string, UCHAR>> remotes;
+};
+
+#endif
#include "inputcec.h"
#endif
#include "inputudp.h"
+#include "inputlirc.h"
#ifdef WIN32
#include "inputwin.h"
#endif
else
{ delete inputUDP; inputUDP = NULL; }
+ inputLirc = new InputLIRC();
+ ret = inputLirc->init();
+ if (ret)
+ oneOK = true;
+ else
+ { delete inputLirc; inputLirc = NULL; }
+
#ifdef WIN32
inputWin = new InputWin();
ret = inputWin->init();
if (inputUDP && inputUDP->start()) oneOK = true;
+
+ // FIXME
+ std::string lircIP;
+ USHORT lircPort = 8765;
+ // Override lircIP in inputlirc.conf2
+#include "inputlirc.conf2"
+ if (inputLirc && inputLirc->start(lircIP, lircPort)) oneOK = true;
+
+
if (!oneOK)
Log::getInstance()->log("InputMan", Log::CRIT, "InputMan could not start any input module");
if (inputLinux) inputLinux->stop();
#endif
if (inputUDP) inputUDP->stop();
+ if (inputLirc) inputLirc->stop();
}
void InputMan::shutdown()
inputUDP = NULL;
}
+ if (inputLirc)
+ {
+ Log::getInstance()->log("InputMan", Log::DEBUG, "Shutdown start - LIRC");
+ inputLirc->stop();
+ inputLirc->shutdown();
+ delete inputLirc;
+ inputLirc = NULL;
+ }
+
initted = false;
}
class InputCEC;
class InputWin;
class InputUDP;
-class InputLirc;
+class InputLIRC;
class InputMan: public AbstractOption
{
InputCEC* inputCEC{};
InputWin* inputWin{};
InputUDP* inputUDP{};
- InputLirc* inputLirc{};
+ InputLIRC* inputLirc{};
bool initted{};
};
const char* modName() { return myModName; }
bool initted{};
- UDP4 udp4;
+ UDP4 udp4; // FIXME UDP6 ?
Log* log{};
std::thread listenThread;
void listenLoop();
bool listenLoopStop{};
-#ifdef WIN32
+#ifdef WIN32 // FIXME move these into UDP4?
SOCKET quitPipe;
#else
int pfds[2];
vradiorec.o vaudioselector.o vscreensaver.o vopts.o \
wselectlist.o wjpeg.o wsymbol.o wbutton.o wtextbox.o \
woptionpane.o woptionbox.o wremoteconfig.o wtabbar.o led.o \
- inputman.o input.o inputudp.o vpicturebanner.o abstractoption.o \
- eventdispatcher.o vdrrequestpacket.o vdrresponsepacket.o \
- vvideolivetv.o vsleeptimer.o \
+ inputman.o input.o inputudp.o inputlirc.o vpicturebanner.o \
+ abstractoption.o eventdispatcher.o vdrrequestpacket.o \
+ vdrresponsepacket.o vvideolivetv.o vsleeptimer.o \
wprogressbar.o bitmap.o dvbsubtitles.o tfeed.o vteletextview.o \
teletextdecodervbiebu.o teletxt/txtfont.o movieinfo.o seriesinfo.o \
wmovieview.o wseriesview.o tvmedia.o wtvmedia.o wpictureview.o \
#include <sys/types.h> // socket connect getaddrinfo
#include <string.h> // memset
#include <errno.h> // errno var
+#include <stdlib.h> // malloc / free
+
+#include <sstream>
#include "defines.h"
#include "log.h"
#else
while (::read(abortPipe[0], waste, 10) > 0) ;
#endif
+
+ if (recStringBuf)
+ {
+ free(recStringBuf);
+ recStringBuf = NULL;
+ recStringBufStart = 0;
+ recStringBufUsed = 0;
+ }
}
bool TCP::status()
return true;
}
-bool TCP::read(void* dst, ULONG numBytes)
+bool TCP::read(void* dst, ULONG numBytes, int timeoutSec)
{
if (!connected) return false;
do
{
- if (abortCount == 5)
+ if (++abortCount == 1000)
{
- logger->log("TCP", Log::DEBUG, "abortCount = 5");
+ logger->log("TCP", Log::ALERT, "abortCount = 1000 - runaway error, or packet arrived in > 1000 pieces??");
return false;
}
if (abortPipe[0] > maxfd) maxfd = abortPipe[0];
#endif
- tv.tv_sec = 2;
- tv.tv_usec = 0;
-
- int selectResult = select(maxfd + 1, &readfds, NULL, NULL, &tv);
+ int selectResult;
+ if (timeoutSec)
+ {
+ tv.tv_sec = timeoutSec;
+ tv.tv_usec = 0;
+ selectResult = select(maxfd + 1, &readfds, NULL, NULL, &tv);
+ }
+ else
+ {
+ selectResult = select(maxfd + 1, &readfds, NULL, NULL, NULL);
+ }
if (selectResult == -1) { shutdown(); return false; }
if (selectResult == 0) return false;
#ifdef WIN32
- if (FD_ISSET(abortSocket, &readfds)) { logger->log("TCP", Log::DEBUG, "aborting..."); return false; }
+ if (FD_ISSET(abortSocket, &readfds)) { logger->log("TCP", Log::DEBUG, "Aborting..."); return false; }
#else
-
-
-
- if (FD_ISSET(abortPipe[0], &readfds)) { logger->log("TCP", Log::DEBUG, "B aborting..."); return false; }
+ if (FD_ISSET(abortPipe[0], &readfds)) { logger->log("TCP", Log::DEBUG, "Aborting..."); return false; }
#endif
int recvResult = recv(sockfd, pointer, numBytes - totalReceived, 0);
if (recvResult == -1) { shutdown(); return false; }
totalReceived += recvResult;
pointer += recvResult;
-
+
} while (totalReceived < numBytes);
return true;
}
+std::stringstream TCP::readString(bool* result, int timeoutSec)
+{
+ *result = false;
+
+ std::stringstream ss;
+
+ if (!connected) return ss;
+
+ fd_set readfds;
+ struct timeval tv;
+
+ int abortCount = 0;
+
+ if (!recStringBuf) recStringBuf = static_cast<char*>(malloc(recStringBufSize));
+ if (!recStringBuf) return ss;
+
+ // Absorb over-read from last time?
+ if (recStringBufUsed)
+ {
+ ss.write(&recStringBuf[recStringBufStart], recStringBufUsed);
+ recStringBufStart = 0;
+ recStringBufUsed = 0;
+ }
+
+ while(true)
+ {
+ if (++abortCount == 20)
+ {
+ logger->log("TCP", Log::DEBUG, "abortCount = 20");
+ return ss;
+ }
+
+ FD_ZERO(&readfds);
+ FD_SET(sockfd, &readfds);
+ int maxfd = sockfd; // WIN32 ignores
+
+#ifdef WIN32
+ FD_SET(abortSocket, &readfds);
+#else
+ FD_SET(abortPipe[0], &readfds);
+ if (abortPipe[0] > maxfd) maxfd = abortPipe[0];
+#endif
+
+ int selectResult;
+ if (timeoutSec)
+ {
+ tv.tv_sec = timeoutSec;
+ tv.tv_usec = 0;
+ selectResult = select(maxfd + 1, &readfds, NULL, NULL, &tv);
+ }
+ else
+ {
+ selectResult = select(maxfd + 1, &readfds, NULL, NULL, NULL);
+ }
+
+ if (selectResult == -1) { shutdown(); return ss; }
+ if (selectResult == 0) return ss;
+
+#ifdef WIN32
+ if (FD_ISSET(abortSocket, &readfds)) { logger->log("TCP", Log::DEBUG, "Aborting..."); return ss; }
+#else
+ if (FD_ISSET(abortPipe[0], &readfds)) { logger->log("TCP", Log::DEBUG, "Aborting..."); return ss; }
+#endif
+
+ int recvResult = recv(sockfd, &recStringBuf[recStringBufUsed], recStringBufSize - recStringBufUsed, 0);
+ if (recvResult == -1) { shutdown(); return ss; }
+ recStringBufUsed += recvResult;
+
+ // Do we have a full string?
+ for(int i = 0; i < recStringBufUsed; i++)
+ {
+ if (recStringBuf[i] == '\n')
+ {
+ // Found.
+ ss.write(recStringBuf, i);
+
+ if ((i + 1) != recStringBufUsed) // over read
+ {
+ i += 1; // Advance over \n
+ recStringBufStart = i;
+ recStringBufUsed -= i;
+ }
+ else
+ {
+ recStringBufUsed = 0;
+ }
+
+ *result = true;
+ return ss;
+ }
+ }
+ // no \n in buffer, go around
+ }
+}
+
MACAddress TCP::getMAC()
{
#ifndef WIN32
void abortCall(); // causes a read/connect call to immediately abort
bool connect(const std::string& ip, USHORT port);
- bool read(void* dest, ULONG numBytes);
+ bool read(void* dest, ULONG numBytes, int timeoutSec = 2); // Set timeoutSec to 0 for no timeout
+ std::stringstream readString(bool* result, int timeoutSec = 2);
bool write(void* src, ULONG numBytes);
bool status();
bool connected{};
std::mutex writeMutex;
+ char* recStringBuf{};
+ const int recStringBufSize{1024};
+ int recStringBufStart{};
+ int recStringBufUsed{};
+
#ifdef WIN32
SOCKET abortSocket;
#else