10#include <sys/select.h>
11#include <sys/socket.h>
27#include <QCoreApplication>
29#include <QKeySequence>
40#define LOC QString("LIRC: ")
88 m_mainWindow(main_window),
89 m_lircdDevice(std::move(lircd_device)),
90 m_program(std::move(our_program)),
91 m_configFile(std::move(config_file)),
105 QObject::deleteLater();
110 QMutexLocker locker(&
m_lock);
126static QByteArray
get_ip(
const QString &h)
128 QByteArray hba = h.toLatin1();
129 struct in_addr sin_addr {};
130 if (inet_aton(hba.constData(), &sin_addr))
133 struct addrinfo hints {};
134 hints.ai_family = AF_INET;
135 hints.ai_socktype = SOCK_STREAM;
136 hints.ai_protocol = IPPROTO_TCP;
138 struct addrinfo *result =
nullptr;
139 int err = getaddrinfo(hba.constData(),
nullptr, &hints, &result);
142 LOG(VB_GENERAL, LOG_DEBUG,
143 QString(
"get_ip: %1").arg(gai_strerror(err)));
144 return QString(
"").toLatin1();
147 int addrlen = result->ai_addrlen;
150 freeaddrinfo(result);
151 return QString(
"").toLatin1();
154 if (result->ai_addr->sa_family != AF_INET)
156 freeaddrinfo(result);
157 return QString(
"").toLatin1();
160 sin_addr = ((
struct sockaddr_in*)(result->ai_addr))->sin_addr;
161 hba = QByteArray(inet_ntoa(sin_addr));
162 freeaddrinfo(result);
169 QMutexLocker locker(&
m_lock);
173 uint64_t vtype = (0 ==
m_retryCount) ? VB_GENERAL : VB_FILE;
175 int lircd_socket = -1;
179 struct sockaddr_un addr {};
180 addr.sun_family = AF_UNIX;
181 static constexpr int max_copy =
sizeof(addr.sun_path) - 1;
183 if (dev.size() > max_copy)
187 " is too long for the 'unix' socket API");
192 lircd_socket = socket(AF_UNIX, SOCK_STREAM, 0);
193 if (lircd_socket < 0)
195 LOG(vtype, LOG_ERR,
LOC + QString(
"Failed to open Unix socket '%1'")
201 strncpy(addr.sun_path, dev.constData(), max_copy);
203 int ret = ::connect(lircd_socket, (
struct sockaddr*) &addr,
209 QString(
"Failed to connect to Unix socket '%1'")
218 lircd_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
219 if (lircd_socket < 0)
221 LOG(vtype, LOG_ERR,
LOC + QString(
"Failed to open TCP socket '%1'")
233 port = (tmp[1].toUInt()) ? tmp[1].toUInt() : port;
235 QByteArray device =
get_ip(dev);
236 struct sockaddr_in addr {};
237 addr.sin_family = AF_INET;
238 addr.sin_port = htons(port);
240 if (!inet_aton(device.constData(), &addr.sin_addr))
242 LOG(vtype, LOG_ERR,
LOC + QString(
"Failed to parse IP address '%1'")
249 int ret = ::connect(lircd_socket, (
struct sockaddr*) &addr,
254 QString(
"Failed to connect TCP socket '%1'")
264 int flags = fcntl(lircd_socket, F_GETFD);
267 ret = fcntl(lircd_socket, F_SETFD, flags |
O_NONBLOCK);
270 LOG(VB_GENERAL, LOG_WARNING,
LOC +
271 QString(
"Failed set flags for socket '%1'")
278 ret = setsockopt(lircd_socket, SOL_SOCKET, SO_OOBINLINE, &i,
sizeof(i));
281 LOG(VB_GENERAL, LOG_WARNING,
LOC +
282 QString(
"Failed setting OOBINLINE option for socket '%1'")
286 ret = setsockopt(lircd_socket, SOL_SOCKET, SO_KEEPALIVE, &i,
sizeof(i));
289 LOG(VB_GENERAL, LOG_WARNING,
LOC +
290 QString(
"Failed setting KEEPALIVE option for socket '%1'")
312 QString(
"Failed to read config file '%1'").arg(
m_configFile));
320 LOG(VB_GENERAL, LOG_INFO,
LOC +
321 QString(
"Successfully initialized '%1' using '%2' config")
329 QMutexLocker locker(&
m_lock);
333 LOG(VB_GENERAL, LOG_ERR,
"start() called without lircd socket");
343 QMutexLocker locker(&
m_lock);
356 while ((0 == ret) && !code.empty())
358 QString lirctext = QString::fromStdString(code);
359 QString qtcode = QString::fromStdString(code);
360 qtcode.replace(
"ctrl-",
"ctrl+", Qt::CaseInsensitive);
361 qtcode.replace(
"alt-",
"alt+", Qt::CaseInsensitive);
362 qtcode.replace(
"shift-",
"shift+", Qt::CaseInsensitive);
363 qtcode.replace(
"meta-",
"meta+", Qt::CaseInsensitive);
364 QKeySequence a(qtcode);
371 QCoreApplication::postEvent(
374 (Qt::KeyboardModifiers)
376 QString(), lirctext));
379 std::vector<LircKeycodeEvent*> keyReleases;
381 for (
int i = 0; i < a.count(); i++)
383#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
385 Qt::KeyboardModifiers mod = Qt::NoModifier;
386 mod |= (Qt::SHIFT & keycode) ? Qt::ShiftModifier : Qt::NoModifier;
387 mod |= (Qt::META & keycode) ? Qt::MetaModifier : Qt::NoModifier;
388 mod |= (Qt::CTRL & keycode) ? Qt::ControlModifier: Qt::NoModifier;
389 mod |= (Qt::ALT & keycode) ? Qt::AltModifier : Qt::NoModifier;
391 keycode &= ~Qt::MODIFIER_MASK;
393 int keycode = a[i].key();
394 Qt::KeyboardModifiers mod = a[i].keyboardModifiers();
398 if (!mod && keycode <= 0xFFFF)
399 text = QString(QChar(keycode));
401 QCoreApplication::postEvent(
403 QEvent::KeyPress, keycode, mod, text, lirctext));
405 keyReleases.push_back(
407 QEvent::KeyRelease, keycode, mod, text, lirctext));
410 for (
int i = (
int)keyReleases.size() - 1; i>=0; i--)
411 QCoreApplication::postEvent(
m_mainWindow, keyReleases[i]);
422 LOG(VB_GENERAL, LOG_DEBUG,
LOC +
"run -- start");
428 std::this_thread::sleep_for(100ms);
432 QMutexLocker locker(&
m_lock);
436 LOG(VB_GENERAL, LOG_ERR,
LOC +
437 "Failed to reconnect, exiting LIRC thread.");
441 LOG(VB_FILE, LOG_WARNING,
LOC +
"EOF -- reconnecting");
454 std::this_thread::sleep_for(2s);
471 if (ret < 0 && errno != EINTR)
473 LOG(VB_GENERAL, LOG_ERR,
LOC +
"select() failed" +
ENO);
482 QList<QByteArray> codes =
GetCodes();
483 for (
const auto & code : std::as_const(codes))
487 LOG(VB_GENERAL, LOG_DEBUG,
LOC +
"run -- end");
494 QList<QByteArray> ret;
498 m_buf.resize(buf_size);
516 LOG(VB_GENERAL, LOG_NOTICE,
LOC +
"GetCodes -- EOF?");
521 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Could not read socket" +
ENO);
529 LOG(VB_GENERAL, LOG_NOTICE,
LOC +
"GetCodes -- eof?");
539 ret =
m_buf.split(
'\n');
540 if (
m_buf.endsWith(
'\n'))
546 m_buf = ret.takeLast();
551#include "moc_lirc.cpp"
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_readconfig(const struct lirc_state *state, const std::string &file, struct lirc_config **config, int(check)(std::string &s))
int lirc_deinit(struct lirc_state *state)
void lirc_freeconfig(struct lirc_config *config)
int lirc_code2char(const struct lirc_state *state, struct lirc_config *config, const std::string &code, std::string &string)
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=[])