|
MythTV
0.26-pre
|
00001 00002 // Own header 00003 #include "lirc.h" 00004 00005 // C headers 00006 #include <cstdio> 00007 #include <cerrno> 00008 #include <cstdlib> 00009 00010 // C++ headers 00011 #include <algorithm> 00012 #include <vector> 00013 using namespace std; 00014 00015 // Qt headers 00016 #include <QCoreApplication> 00017 #include <QEvent> 00018 #include <QKeySequence> 00019 #include <QStringList> 00020 00021 // MythTV headers 00022 #include "mythdb.h" 00023 #include "mythsystem.h" 00024 #include "lircevent.h" 00025 #include "lirc_client.h" 00026 00027 #include <sys/types.h> 00028 #include <sys/socket.h> 00029 #include <sys/select.h> 00030 #include <sys/un.h> 00031 #include <netinet/in.h> 00032 #include <arpa/inet.h> 00033 #include <netdb.h> 00034 #include <unistd.h> 00035 #include <fcntl.h> 00036 #include <sys/wait.h> 00037 #include "mythlogging.h" 00038 00039 #define LOC QString("LIRC: ") 00040 00041 class LIRCPriv 00042 { 00043 public: 00044 LIRCPriv() : lircState(NULL), lircConfig(NULL) {} 00045 ~LIRCPriv() 00046 { 00047 if (lircState) 00048 { 00049 lirc_deinit(lircState); 00050 lircState = NULL; 00051 } 00052 if (lircConfig) 00053 { 00054 lirc_freeconfig(lircConfig); 00055 lircConfig = NULL; 00056 } 00057 } 00058 00059 struct lirc_state *lircState; 00060 struct lirc_config *lircConfig; 00061 }; 00062 00063 QMutex LIRC::lirclib_lock; 00064 00072 LIRC::LIRC(QObject *main_window, 00073 const QString &lircd_device, 00074 const QString &our_program, 00075 const QString &config_file) 00076 : MThread("LIRC"), 00077 lock(QMutex::Recursive), 00078 m_mainWindow(main_window), 00079 lircdDevice(lircd_device), 00080 program(our_program), 00081 configFile(config_file), 00082 doRun(false), 00083 buf_offset(0), 00084 eofCount(0), 00085 retryCount(0), 00086 d(new LIRCPriv()) 00087 { 00088 lircdDevice.detach(); 00089 program.detach(); 00090 configFile.detach(); 00091 buf.resize(0); 00092 } 00093 00094 LIRC::~LIRC() 00095 { 00096 TeardownAll(); 00097 } 00098 00099 void LIRC::deleteLater(void) 00100 { 00101 TeardownAll(); 00102 QObject::deleteLater(); 00103 } 00104 00105 void LIRC::TeardownAll(void) 00106 { 00107 QMutexLocker locker(&lock); 00108 if (doRun) 00109 { 00110 doRun = false; 00111 lock.unlock(); 00112 wait(); 00113 lock.lock(); 00114 } 00115 00116 if (d) 00117 { 00118 delete d; 00119 d = NULL; 00120 } 00121 } 00122 00123 static QByteArray get_ip(const QString &h) 00124 { 00125 QByteArray hba = h.toLatin1(); 00126 struct in_addr sin_addr; 00127 if (inet_aton(hba.constData(), &sin_addr)) 00128 return hba; 00129 00130 struct addrinfo hints; 00131 memset(&hints, 0, sizeof(hints)); 00132 hints.ai_family = AF_INET; 00133 hints.ai_socktype = SOCK_STREAM; 00134 hints.ai_protocol = IPPROTO_TCP; 00135 00136 struct addrinfo *result; 00137 int err = getaddrinfo(hba.constData(), NULL, &hints, &result); 00138 if (err) 00139 { 00140 LOG(VB_GENERAL, LOG_DEBUG, 00141 QString("get_ip: %1").arg(gai_strerror(err))); 00142 return QString("").toLatin1(); 00143 } 00144 00145 int addrlen = result->ai_addrlen; 00146 if (!addrlen) 00147 { 00148 freeaddrinfo(result); 00149 return QString("").toLatin1(); 00150 } 00151 00152 if (result->ai_addr->sa_family != AF_INET) 00153 { 00154 freeaddrinfo(result); 00155 return QString("").toLatin1(); 00156 } 00157 00158 sin_addr = ((struct sockaddr_in*)(result->ai_addr))->sin_addr; 00159 hba = QByteArray(inet_ntoa(sin_addr)); 00160 freeaddrinfo(result); 00161 00162 return hba; 00163 } 00164 00165 bool LIRC::Init(void) 00166 { 00167 QMutexLocker locker(&lock); 00168 if (d->lircState) 00169 return true; 00170 00171 uint64_t vtype = (0 == retryCount) ? VB_GENERAL : VB_FILE; 00172 00173 int lircd_socket = -1; 00174 if (lircdDevice.startsWith('/')) 00175 { 00176 // Connect the unix socket 00177 QByteArray dev = lircdDevice.toLocal8Bit(); 00178 if (dev.size() > 107) 00179 { 00180 LOG(vtype, LOG_ERR, LOC + 00181 QString("lircdDevice '%1'").arg(lircdDevice) + 00182 " is too long for the 'unix' socket API"); 00183 00184 return false; 00185 } 00186 00187 lircd_socket = socket(AF_UNIX, SOCK_STREAM, 0); 00188 if (lircd_socket < 0) 00189 { 00190 LOG(vtype, LOG_ERR, LOC + QString("Failed to open Unix socket '%1'") 00191 .arg(lircdDevice) + ENO); 00192 00193 return false; 00194 } 00195 00196 struct sockaddr_un addr; 00197 memset(&addr, 0, sizeof(sockaddr_un)); 00198 addr.sun_family = AF_UNIX; 00199 strncpy(addr.sun_path, dev.constData(),107); 00200 00201 int ret = ::connect(lircd_socket, (struct sockaddr*) &addr, 00202 sizeof(addr)); 00203 00204 if (ret < 0) 00205 { 00206 LOG(vtype, LOG_ERR, LOC + 00207 QString("Failed to connect to Unix socket '%1'") 00208 .arg(lircdDevice) + ENO); 00209 00210 close(lircd_socket); 00211 return false; 00212 } 00213 } 00214 else 00215 { 00216 lircd_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 00217 if (lircd_socket < 0) 00218 { 00219 LOG(vtype, LOG_ERR, LOC + QString("Failed to open TCP socket '%1'") 00220 .arg(lircdDevice) + ENO); 00221 00222 return false; 00223 } 00224 00225 QString dev = lircdDevice; 00226 uint port = 8765; 00227 QStringList tmp = lircdDevice.split(':'); 00228 if (2 == tmp.size()) 00229 { 00230 dev = tmp[0]; 00231 port = (tmp[1].toUInt()) ? tmp[1].toUInt() : port; 00232 } 00233 QByteArray device = get_ip(dev); 00234 struct sockaddr_in addr; 00235 memset(&addr, 0, sizeof(sockaddr_in)); 00236 addr.sin_family = AF_INET; 00237 addr.sin_port = htons(port); 00238 00239 if (!inet_aton(device.constData(), &addr.sin_addr)) 00240 { 00241 LOG(vtype, LOG_ERR, LOC + QString("Failed to parse IP address '%1'") 00242 .arg(dev)); 00243 00244 close(lircd_socket); 00245 return false; 00246 } 00247 00248 int ret = ::connect(lircd_socket, (struct sockaddr*) &addr, 00249 sizeof(addr)); 00250 if (ret < 0) 00251 { 00252 LOG(vtype, LOG_ERR, LOC + 00253 QString("Failed to connect TCP socket '%1'") 00254 .arg(lircdDevice) + ENO); 00255 00256 close(lircd_socket); 00257 return false; 00258 } 00259 00260 // On Linux, select() can indicate data when there isn't 00261 // any due to TCP checksum in-particular; to avoid getting 00262 // stuck on a read() call add the O_NONBLOCK flag. 00263 int flags = fcntl(lircd_socket, F_GETFD); 00264 if (flags >= 0) 00265 { 00266 ret = fcntl(lircd_socket, F_SETFD, flags | O_NONBLOCK); 00267 if (ret < 0) 00268 { 00269 LOG(VB_GENERAL, LOG_WARNING, LOC + 00270 QString("Failed set flags for socket '%1'") 00271 .arg(lircdDevice) + ENO); 00272 } 00273 } 00274 00275 // Attempt to inline out-of-band messages and keep the connection open.. 00276 int i = 1; 00277 setsockopt(lircd_socket, SOL_SOCKET, SO_OOBINLINE, &i, sizeof(i)); 00278 i = 1; 00279 setsockopt(lircd_socket, SOL_SOCKET, SO_KEEPALIVE, &i, sizeof(i)); 00280 } 00281 00282 d->lircState = lirc_init("/etc/lircrc", ".lircrc", "mythtv", NULL, 0); 00283 if (!d->lircState) 00284 { 00285 close(lircd_socket); 00286 return false; 00287 } 00288 d->lircState->lirc_lircd = lircd_socket; 00289 00290 // parse the config file 00291 if (!d->lircConfig) 00292 { 00293 QMutexLocker static_lock(&lirclib_lock); 00294 QByteArray cfg = configFile.toLocal8Bit(); 00295 if (lirc_readconfig(d->lircState, cfg.constData(), &d->lircConfig, NULL)) 00296 { 00297 LOG(vtype, LOG_ERR, LOC + 00298 QString("Failed to read config file '%1'").arg(configFile)); 00299 00300 lirc_deinit(d->lircState); 00301 d->lircState = NULL; 00302 return false; 00303 } 00304 } 00305 00306 LOG(VB_GENERAL, LOG_INFO, LOC + 00307 QString("Successfully initialized '%1' using '%2' config") 00308 .arg(lircdDevice).arg(configFile)); 00309 00310 return true; 00311 } 00312 00313 void LIRC::start(void) 00314 { 00315 QMutexLocker locker(&lock); 00316 00317 if (!d->lircState) 00318 { 00319 LOG(VB_GENERAL, LOG_ERR, "start() called without lircd socket"); 00320 return; 00321 } 00322 00323 doRun = true; 00324 MThread::start(); 00325 } 00326 00327 bool LIRC::IsDoRunSet(void) const 00328 { 00329 QMutexLocker locker(&lock); 00330 return doRun; 00331 } 00332 00333 void LIRC::Process(const QByteArray &data) 00334 { 00335 QMutexLocker static_lock(&lirclib_lock); 00336 00337 // lirc_code2char will make code point to a static datafer.. 00338 char *code = NULL; 00339 int ret = lirc_code2char( 00340 d->lircState, d->lircConfig, const_cast<char*>(data.constData()), &code); 00341 00342 while ((0 == ret) && code) 00343 { 00344 QString lirctext(code); 00345 QString qtcode = code; 00346 qtcode.replace("ctrl-", "ctrl+", Qt::CaseInsensitive); 00347 qtcode.replace("alt-", "alt+", Qt::CaseInsensitive); 00348 qtcode.replace("shift-", "shift+", Qt::CaseInsensitive); 00349 qtcode.replace("meta-", "meta+", Qt::CaseInsensitive); 00350 QKeySequence a(qtcode); 00351 00352 // Send a dummy keycode if we couldn't convert the key sequence. 00353 // This is done so the main code can output a warning for bad 00354 // mappings. 00355 if (!a.count()) 00356 { 00357 QCoreApplication::postEvent( 00358 m_mainWindow, new LircKeycodeEvent( 00359 QEvent::KeyPress, 0, 00360 (Qt::KeyboardModifiers) 00361 LircKeycodeEvent::kLIRCInvalidKeyCombo, 00362 QString(), lirctext)); 00363 } 00364 00365 vector<LircKeycodeEvent*> keyReleases; 00366 for (unsigned int i = 0; i < a.count(); i++) 00367 { 00368 int keycode = a[i]; 00369 Qt::KeyboardModifiers mod = Qt::NoModifier; 00370 mod |= (Qt::SHIFT & keycode) ? Qt::ShiftModifier : Qt::NoModifier; 00371 mod |= (Qt::META & keycode) ? Qt::MetaModifier : Qt::NoModifier; 00372 mod |= (Qt::CTRL & keycode) ? Qt::ControlModifier: Qt::NoModifier; 00373 mod |= (Qt::ALT & keycode) ? Qt::AltModifier : Qt::NoModifier; 00374 00375 keycode &= ~Qt::MODIFIER_MASK; 00376 00377 QString text = ""; 00378 if (!mod) 00379 text = QString(QChar(keycode)); 00380 00381 QCoreApplication::postEvent( 00382 m_mainWindow, new LircKeycodeEvent( 00383 QEvent::KeyPress, keycode, mod, text, lirctext)); 00384 00385 keyReleases.push_back( 00386 new LircKeycodeEvent( 00387 QEvent::KeyRelease, keycode, mod, text, lirctext)); 00388 } 00389 00390 for (int i = (int)keyReleases.size() - 1; i>=0; i--) 00391 QCoreApplication::postEvent(m_mainWindow, keyReleases[i]); 00392 00393 ret = lirc_code2char( 00394 d->lircState, d->lircConfig, const_cast<char*>(data.constData()), &code); 00395 } 00396 } 00397 00398 void LIRC::run(void) 00399 { 00400 RunProlog(); 00401 #if 0 00402 LOG(VB_GENERAL, LOG_DEBUG, LOC + "run -- start"); 00403 #endif 00404 /* Process all events read */ 00405 while (IsDoRunSet()) 00406 { 00407 if (eofCount && retryCount) 00408 usleep(100 * 1000); 00409 00410 if ((eofCount >= 10) || (!d->lircState)) 00411 { 00412 QMutexLocker locker(&lock); 00413 eofCount = 0; 00414 if (++retryCount > 1000) 00415 { 00416 LOG(VB_GENERAL, LOG_ERR, LOC + 00417 "Failed to reconnect, exiting LIRC thread."); 00418 doRun = false; 00419 continue; 00420 } 00421 LOG(VB_FILE, LOG_WARNING, LOC + "EOF -- reconnecting"); 00422 00423 lirc_deinit(d->lircState); 00424 d->lircState = NULL; 00425 00426 if (Init()) 00427 retryCount = 0; 00428 else 00429 sleep(2); // wait a while before we retry.. 00430 00431 continue; 00432 } 00433 00434 fd_set readfds; 00435 FD_ZERO(&readfds); 00436 FD_SET(d->lircState->lirc_lircd, &readfds); 00437 00438 // the maximum time select() should wait 00439 struct timeval timeout; 00440 timeout.tv_sec = 1; // 1 second 00441 timeout.tv_usec = 100 * 1000; // 100 ms 00442 00443 int ret = select(d->lircState->lirc_lircd + 1, &readfds, NULL, NULL, 00444 &timeout); 00445 00446 if (ret < 0 && errno != EINTR) 00447 { 00448 LOG(VB_GENERAL, LOG_ERR, LOC + "select() failed" + ENO); 00449 continue; 00450 } 00451 00452 // 0: Timer expired with no data, repeat select 00453 // -1: Iinterrupted while waiting, repeat select 00454 if (ret <= 0) 00455 continue; 00456 00457 QList<QByteArray> codes = GetCodes(); 00458 for (uint i = 0; i < (uint) codes.size(); i++) 00459 Process(codes[i]); 00460 } 00461 #if 0 00462 LOG(VB_GENERAL, LOG_DEBUG, LOC + "run -- end"); 00463 #endif 00464 RunEpilog(); 00465 } 00466 00467 QList<QByteArray> LIRC::GetCodes(void) 00468 { 00469 QList<QByteArray> ret; 00470 ssize_t len = -1; 00471 00472 uint buf_size = buf.size() + 128; 00473 buf.resize(buf_size); 00474 00475 while (true) 00476 { 00477 len = read(d->lircState->lirc_lircd, buf.data() + buf_offset, 128); 00478 if (len >= 0) 00479 break; 00480 00481 switch (errno) 00482 { 00483 case EINTR: 00484 continue; 00485 00486 case EAGAIN: 00487 return ret; 00488 00489 case ENOTCONN: // 107 (according to asm-generic/errno.h) 00490 if (!eofCount) 00491 LOG(VB_GENERAL, LOG_NOTICE, LOC + "GetCodes -- EOF?"); 00492 eofCount++; 00493 return ret; 00494 00495 default: 00496 LOG(VB_GENERAL, LOG_ERR, LOC + "Could not read socket" + ENO); 00497 return ret; 00498 } 00499 } 00500 00501 if (!len) 00502 { 00503 if (!eofCount) 00504 LOG(VB_GENERAL, LOG_NOTICE, LOC + "GetCodes -- eof?"); 00505 eofCount++; 00506 return ret; 00507 } 00508 00509 eofCount = 0; 00510 retryCount = 0; 00511 00512 buf_offset += len; 00513 buf.truncate(buf_offset); 00514 ret = buf.split('\n'); 00515 if (buf.endsWith('\n')) 00516 { 00517 buf_offset = 0; 00518 return ret; 00519 } 00520 00521 buf = ret.takeLast(); 00522 buf_offset = buf.size(); 00523 return ret; 00524 } 00525
1.7.6.1