10#include <sys/select.h>
11#include <sys/socket.h>
27#include <QCoreApplication>
29#include <QKeySequence>
40#define LOC QString("LIRC: ")
87 m_mainWindow(main_window),
88 m_lircdDevice(
std::move(lircd_device)),
89 m_program(
std::move(our_program)),
90 m_configFile(
std::move(config_file)),
104 QObject::deleteLater();
109 QMutexLocker locker(&
m_lock);
125static QByteArray
get_ip(
const QString &h)
127 QByteArray hba = h.toLatin1();
128 struct in_addr sin_addr {};
129 if (inet_aton(hba.constData(), &sin_addr))
132 struct addrinfo hints {};
133 hints.ai_family = AF_INET;
134 hints.ai_socktype = SOCK_STREAM;
135 hints.ai_protocol = IPPROTO_TCP;
137 struct addrinfo *result =
nullptr;
138 int err = getaddrinfo(hba.constData(),
nullptr, &hints, &result);
141 LOG(VB_GENERAL, LOG_DEBUG,
142 QString(
"get_ip: %1").arg(gai_strerror(err)));
143 return QString(
"").toLatin1();
146 int addrlen = result->ai_addrlen;
149 freeaddrinfo(result);
150 return QString(
"").toLatin1();
153 if (result->ai_addr->sa_family != AF_INET)
155 freeaddrinfo(result);
156 return QString(
"").toLatin1();
159 sin_addr = ((
struct sockaddr_in*)(result->ai_addr))->sin_addr;
160 hba = QByteArray(inet_ntoa(sin_addr));
161 freeaddrinfo(result);
168 QMutexLocker locker(&
m_lock);
172 uint64_t vtype = (0 ==
m_retryCount) ? VB_GENERAL : VB_FILE;
174 int lircd_socket = -1;
178 struct sockaddr_un addr {};
179 addr.sun_family = AF_UNIX;
180 static constexpr int max_copy =
sizeof(addr.sun_path) - 1;
182 if (dev.size() > max_copy)
186 " is too long for the 'unix' socket API");
191 lircd_socket = socket(AF_UNIX, SOCK_STREAM, 0);
192 if (lircd_socket < 0)
194 LOG(vtype, LOG_ERR,
LOC + QString(
"Failed to open Unix socket '%1'")
200 strncpy(addr.sun_path, dev.constData(), max_copy);
202 int ret = ::connect(lircd_socket, (
struct sockaddr*) &addr,
208 QString(
"Failed to connect to Unix socket '%1'")
217 lircd_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
218 if (lircd_socket < 0)
220 LOG(vtype, LOG_ERR,
LOC + QString(
"Failed to open TCP socket '%1'")
232 port = (
tmp[1].toUInt()) ?
tmp[1].toUInt() : port;
234 QByteArray device =
get_ip(dev);
235 struct sockaddr_in addr {};
236 addr.sin_family = AF_INET;
237 addr.sin_port = htons(port);
239 if (!inet_aton(device.constData(), &addr.sin_addr))
241 LOG(vtype, LOG_ERR,
LOC + QString(
"Failed to parse IP address '%1'")
248 int ret = ::connect(lircd_socket, (
struct sockaddr*) &addr,
253 QString(
"Failed to connect TCP socket '%1'")
263 int flags = fcntl(lircd_socket, F_GETFD);
266 ret = fcntl(lircd_socket, F_SETFD, flags |
O_NONBLOCK);
269 LOG(VB_GENERAL, LOG_WARNING,
LOC +
270 QString(
"Failed set flags for socket '%1'")
277 ret = setsockopt(lircd_socket, SOL_SOCKET, SO_OOBINLINE, &i,
sizeof(i));
280 LOG(VB_GENERAL, LOG_WARNING,
LOC +
281 QString(
"Failed setting OOBINLINE option for socket '%1'")
285 ret = setsockopt(lircd_socket, SOL_SOCKET, SO_KEEPALIVE, &i,
sizeof(i));
288 LOG(VB_GENERAL, LOG_WARNING,
LOC +
289 QString(
"Failed setting KEEPALIVE option for socket '%1'")
311 QString(
"Failed to read config file '%1'").arg(
m_configFile));
319 LOG(VB_GENERAL, LOG_INFO,
LOC +
320 QString(
"Successfully initialized '%1' using '%2' config")
328 QMutexLocker locker(&
m_lock);
332 LOG(VB_GENERAL, LOG_ERR,
"start() called without lircd socket");
342 QMutexLocker locker(&
m_lock);
351 char *code =
nullptr;
355 while ((0 == ret) && code)
357 QString lirctext(code);
358 QString qtcode = code;
359 qtcode.replace(
"ctrl-",
"ctrl+", Qt::CaseInsensitive);
360 qtcode.replace(
"alt-",
"alt+", Qt::CaseInsensitive);
361 qtcode.replace(
"shift-",
"shift+", Qt::CaseInsensitive);
362 qtcode.replace(
"meta-",
"meta+", Qt::CaseInsensitive);
363 QKeySequence a(qtcode);
370 QCoreApplication::postEvent(
373 (Qt::KeyboardModifiers)
375 QString(), lirctext));
378 std::vector<LircKeycodeEvent*> keyReleases;
380 for (
int i = 0; i < a.count(); i++)
382#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
384 Qt::KeyboardModifiers mod = Qt::NoModifier;
385 mod |= (Qt::SHIFT & keycode) ? Qt::ShiftModifier : Qt::NoModifier;
386 mod |= (Qt::META & keycode) ? Qt::MetaModifier : Qt::NoModifier;
387 mod |= (Qt::CTRL & keycode) ? Qt::ControlModifier: Qt::NoModifier;
388 mod |= (Qt::ALT & keycode) ? Qt::AltModifier : Qt::NoModifier;
390 keycode &= ~Qt::MODIFIER_MASK;
392 int keycode = a[i].key();
393 Qt::KeyboardModifiers mod = a[i].keyboardModifiers();
398 text = QString(QChar(keycode));
400 QCoreApplication::postEvent(
402 QEvent::KeyPress, keycode, mod, text, lirctext));
404 keyReleases.push_back(
406 QEvent::KeyRelease, keycode, mod, text, lirctext));
409 for (
int i = (
int)keyReleases.size() - 1; i>=0; i--)
410 QCoreApplication::postEvent(
m_mainWindow, keyReleases[i]);
421 LOG(VB_GENERAL, LOG_DEBUG,
LOC +
"run -- start");
427 std::this_thread::sleep_for(100ms);
431 QMutexLocker locker(&
m_lock);
435 LOG(VB_GENERAL, LOG_ERR,
LOC +
436 "Failed to reconnect, exiting LIRC thread.");
440 LOG(VB_FILE, LOG_WARNING,
LOC +
"EOF -- reconnecting");
449 std::this_thread::sleep_for(2s);
464 if (ret < 0 && errno != EINTR)
466 LOG(VB_GENERAL, LOG_ERR,
LOC +
"select() failed" +
ENO);
475 QList<QByteArray> codes =
GetCodes();
476 for (
const auto & code : std::as_const(codes))
480 LOG(VB_GENERAL, LOG_DEBUG,
LOC +
"run -- end");
487 QList<QByteArray> ret;
491 m_buf.resize(buf_size);
509 LOG(VB_GENERAL, LOG_NOTICE,
LOC +
"GetCodes -- EOF?");
514 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Could not read socket" +
ENO);
522 LOG(VB_GENERAL, LOG_NOTICE,
LOC +
"GetCodes -- eof?");
532 ret =
m_buf.split(
'\n');
533 if (
m_buf.endsWith(
'\n'))
539 m_buf = ret.takeLast();
struct lirc_state * m_lircState
struct lirc_config * m_lircConfig
void Process(const QByteArray &data)
QString m_lircdDevice
device on which to receive lircd data
bool IsDoRunSet(void) const
static QMutex s_lirclibLock
QString m_configFile
file containing LIRC->key mappings
QObject * m_mainWindow
window to send key events to
void run(void) override
Runs the Qt event loop unless we have a QRunnable, in which case we run the runnable run instead.
QList< QByteArray > GetCodes(void)
LIRC(QObject *main_window, QString lircd_device, QString our_program, QString config_file)
virtual void deleteLater(void)
static const unsigned kLIRCInvalidKeyCombo
This is a wrapper around QThread that does several additional things.
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
bool wait(std::chrono::milliseconds time=std::chrono::milliseconds::max())
Wait for the MThread to exit, with a maximum timeout.
__darwin_suseconds_t __suseconds_t
static const iso6937table * d
static QByteArray get_ip(const QString &h)
static constexpr __suseconds_t k100Milliseconds
int lirc_code2char(const struct lirc_state *state, struct lirc_config *config, const char *code, char **string)
int lirc_deinit(struct lirc_state *state)
void lirc_freeconfig(struct lirc_config *config)
int lirc_readconfig(const struct lirc_state *state, const char *file, struct lirc_config **config, int(check)(char *s))
struct lirc_state * lirc_init(const char *lircrc_root_file, const char *lircrc_user_file, const char *prog, const char *lircd, int verbose)
static bool VERBOSE_LEVEL_CHECK(uint64_t mask, LogLevel_t level)
#define ENO
This can be appended to the LOG args with "+".
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
def read(device=None, features=[])