/* Neutrino-GUI - DBoxII-Project Copyright (C) 2001 Steffen Hehn 'McClean' 2003 thegoodguy Copyright (C) 2008-2014,2016-2017 Stefan Seyfried Copyright (C) 2013-2014 martii License: GPL This program 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. This program 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 this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #ifdef KEYBOARD_INSTEAD_OF_REMOTE_CONTROL #include #endif /* KEYBOARD_INSTEAD_OF_REMOTE_CONTROL */ #include #include #include #include #include #include #include #include #include #include #include //#include #include #include #include #include #include //#define RCDEBUG #define ENABLE_REPEAT_CHECK typedef struct input_event t_input_event; #ifdef KEYBOARD_INSTEAD_OF_REMOTE_CONTROL static struct termio orig_termio; static bool saved_orig_termio = false; #endif /* KEYBOARD_INSTEAD_OF_REMOTE_CONTROL */ static bool input_stopped = false; static struct timespec devinput_mtime = { 0, 0 }; static unsigned int _start_ms = 0; static unsigned int _repeat_ms = 0; #ifdef RCDEBUG #define d_printf printf #else #define d_printf(...) #endif /********************************************************************************* * Constructor - opens rc-input device, selects rc-hardware and starts threads * *********************************************************************************/ CRCInput::CRCInput() { timerid= 1; repeatkeys = NULL; longPressAny = false; // pipe for internal event-queue // ----------------------------- if (pipe(fd_pipe_high_priority) < 0) { perror("fd_pipe_high_priority"); exit(-1); } fcntl(fd_pipe_high_priority[0], F_SETFL, O_NONBLOCK ); fcntl(fd_pipe_high_priority[1], F_SETFL, O_NONBLOCK ); if (pipe(fd_pipe_low_priority) < 0) { perror("fd_pipe_low_priority"); exit(-1); } fcntl(fd_pipe_low_priority[0], F_SETFL, O_NONBLOCK ); fcntl(fd_pipe_low_priority[1], F_SETFL, O_NONBLOCK ); // open event-library // ----------------------------- fd_event = 0; //network-setup struct sockaddr_un servaddr; int clilen; memset(&servaddr, 0, sizeof(struct sockaddr_un)); servaddr.sun_family = AF_UNIX; cstrncpy(servaddr.sun_path, NEUTRINO_UDS_NAME, sizeof(servaddr.sun_path)); clilen = sizeof(servaddr.sun_family) + strlen(servaddr.sun_path); unlink(NEUTRINO_UDS_NAME); //network-setup if ((fd_event = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { perror("[neutrino] socket\n"); } if ( bind(fd_event, (struct sockaddr*) &servaddr, clilen) <0 ) { perror("[neutrino] bind failed...\n"); exit(-1); } if (listen(fd_event, 25) !=0) { perror("[neutrino] listen failed...\n"); exit( -1 ); } repeat_block = repeat_block_generic = 0; checkdev(); open(); rc_last_key = KEY_MAX; firstKey = true; longPressEnd = 0; //select and setup remote control hardware set_rc_hw(); } bool CRCInput::checkdev() { /* stat()ing the directory is fast and cheap. If a device gets added or * removed, the mtime of /dev/input/ will change, which in turn * warrants a more thorough investigation */ struct stat st; if (stat("/dev/input/", &st) == 0) { if (st.st_mtim.tv_sec != devinput_mtime.tv_sec || st.st_mtim.tv_nsec != devinput_mtime.tv_nsec) { devinput_mtime.tv_sec = st.st_mtim.tv_sec; devinput_mtime.tv_nsec = st.st_mtim.tv_nsec; printf("[rcinput:%s] /dev/input mtime changed\n", __func__); return true; } return false; /* still the same... */ } printf("[rcinput:%s] stat /dev/input failed: %m\n", __func__); return true; /* need to check anyway... */ } bool CRCInput::checkpath(in_dev id) { for (std::vector::iterator it = indev.begin(); it != indev.end(); ++it) { if ((*it).path == id.path) { printf("[rcinput:%s] skipping already opened %s\n", __func__, id.path.c_str()); return true; } } return false; } /* if recheck == true, only not already opened devices are opened, if not, close then (re)open all */ void CRCInput::open(bool recheck) { if (recheck == false) close(); /* close() takes the lock, too... */ OpenThreads::ScopedLock m_lock(mutex); struct in_dev id; #if !HAVE_GENERIC_HARDWARE DIR *dir; dir = opendir("/dev/input"); if (! dir) { printf("[rcinput:%s] opendir failed: %m\n", __func__); return; } unsigned long evbit; struct dirent *dentry; while ((dentry = readdir(dir)) != NULL) { id.path = "/dev/input/" + std::string(dentry->d_name); /* hack: on hd2, the device is called "/dev/cs_ir", there are links in /dev/input: pointing to it nevis_ir and event0 (WTF???) so if nevis_ir points to cs_ir, accept it, even though it is a symlink... a better solution would be to simply mknod /dev/input/nevis_ir c 240 0, creating a second instance of /dev/cs_ir named /dev/input/nevis_ir (or to fix the driver to actually create a real event0 device via udev) Note: i'm deliberately not using event0, because this might be replaced by a "real" event0 device if e.g. an USB keyboard is plugged in*/ if (dentry->d_type == DT_LNK && id.path == "/dev/input/nevis_ir") { if (readLink(id.path) != "/dev/cs_ir") continue; } else if (dentry->d_type != DT_CHR) { d_printf("[rcinput:%s] skipping '%s'\n", __func__, dentry->d_name); continue; } d_printf("[rcinput:%s] considering '%s'\n", __func__, dentry->d_name); if (checkpath(id)) continue; id.fd = ::open(id.path.c_str(), O_RDWR|O_NONBLOCK|O_CLOEXEC); if (id.fd == -1) { printf("[rcinput:%s] open %s failed: %m\n", __func__, id.path.c_str()); continue; } if (ioctl(id.fd, EVIOCGBIT(0, EV_MAX), &evbit) < 0) { ::close(id.fd); /* not a proper input device, e.g. /dev/input/mice */ continue; } if ((evbit & (1 << EV_KEY)) == 0) { printf("[rcinput:%s] %s is bad; no EV_KEY support (0x%lx)\n", __func__, id.path.c_str(), evbit); ::close(id.fd); continue; } printf("[rcinput:%s] opened %s (fd %d) ev 0x%lx\n", __func__, id.path.c_str(), id.fd, evbit); indev.push_back(id); } closedir(dir); #endif setKeyRepeatDelay(0, 0); id.path = "/tmp/neutrino.input"; if (! checkpath(id)) { id.fd = ::open(id.path.c_str(), O_RDWR|O_NONBLOCK|O_CLOEXEC); if (id.fd == -1) { /* debug, because it only matters for HAVE_GENERIC_HARDWARE */ d_printf("[rcinput:%s] open %s failed: %m\n", __func__, id.path.c_str()); } else { printf("[rcinput:%s] opened %s (fd %d)\n", __func__, id.path.c_str(), id.fd); indev.push_back(id); } } //+++++++++++++++++++++++++++++++++++++++ #ifdef KEYBOARD_INSTEAD_OF_REMOTE_CONTROL fd_keyb = STDIN_FILENO; if (indev[0].fd < 0) indev[0].fd = fd_keyb; #else fd_keyb = 0; #endif /* KEYBOARD_INSTEAD_OF_REMOTE_CONTROL */ /* ::open("/dev/dbox/rc0", O_RDONLY); if (fd_keyb<0) { perror("/dev/stdin"); exit(-1); } */ #ifdef KEYBOARD_INSTEAD_OF_REMOTE_CONTROL ::fcntl(fd_keyb, F_SETFL, O_NONBLOCK); struct termio new_termio; ::ioctl(STDIN_FILENO, TCGETA, &orig_termio); saved_orig_termio = true; new_termio = orig_termio; new_termio.c_lflag &= ~ICANON; // new_termio.c_lflag &= ~(ICANON|ECHO); new_termio.c_cc[VMIN ] = 1; new_termio.c_cc[VTIME] = 0; ::ioctl(STDIN_FILENO, TCSETA, &new_termio); #else //fcntl(fd_keyb, F_SETFL, O_NONBLOCK ); //+++++++++++++++++++++++++++++++++++++++ #endif /* KEYBOARD_INSTEAD_OF_REMOTE_CONTROL */ calculateMaxFd(); } void CRCInput::close() { OpenThreads::ScopedLock m_lock(mutex); for (unsigned int i = 0; i < indev.size(); i++) ::close(indev[i].fd); indev.clear(); #ifdef KEYBOARD_INSTEAD_OF_REMOTE_CONTROL if (saved_orig_termio) { ::ioctl(STDIN_FILENO, TCSETA, &orig_termio); printf("Original terminal settings restored.\n"); } #else /* if(fd_keyb) { ::close(fd_keyb); } */ #endif /* KEYBOARD_INSTEAD_OF_REMOTE_CONTROL */ calculateMaxFd(); } void CRCInput::calculateMaxFd() { fd_max = fd_event; for (unsigned int i = 0; i < indev.size(); i++) if (indev[i].fd > fd_max) fd_max = indev[i].fd; if(fd_pipe_high_priority[0] > fd_max) fd_max = fd_pipe_high_priority[0]; if(fd_pipe_low_priority[0] > fd_max) fd_max = fd_pipe_low_priority[0]; } /************************************************************************** * Destructor - close the input-device * **************************************************************************/ CRCInput::~CRCInput() { close(); if(fd_pipe_high_priority[0]) ::close(fd_pipe_high_priority[0]); if(fd_pipe_high_priority[1]) ::close(fd_pipe_high_priority[1]); if(fd_pipe_low_priority[0]) ::close(fd_pipe_low_priority[0]); if(fd_pipe_low_priority[1]) ::close(fd_pipe_low_priority[1]); if(fd_event) ::close(fd_event); } /************************************************************************** * stopInput - stop reading rcin for plugins * **************************************************************************/ void CRCInput::stopInput(const bool ext) { if (isLocked()) return; input_stopped = true; close(); if (ext) postMsg(NeutrinoMessages::LOCK_RC_EXTERN, 0); } bool CRCInput::isLocked(void) { return input_stopped; } /************************************************************************** * restartInput - restart reading rcin after calling plugins * **************************************************************************/ void CRCInput::restartInput(const bool ext) { if (!isLocked()) return; close(); open(); input_stopped = false; if (ext) postMsg(NeutrinoMessages::UNLOCK_RC_EXTERN, 0); } #if 0 //never used int CRCInput::messageLoop( bool anyKeyCancels, int timeout ) { neutrino_msg_t msg; neutrino_msg_data_t data; int res = menu_return::RETURN_REPAINT; bool doLoop = true; if ( timeout == -1 ) timeout = g_settings.timing[SNeutrinoSettings::TIMING_MENU]; uint64_t timeoutEnd = CRCInput::calcTimeoutEnd(timeout); while (doLoop) { g_RCInput->getMsgAbsoluteTimeout( &msg, &data, &timeoutEnd ); if ( ( msg == CRCInput::RC_timeout ) || CNeutrinoApp::getInstance()->backKey(msg) || ( msg == CRCInput::RC_ok ) ) doLoop = false; else if (CNeutrinoApp::getInstance()->listModeKey(msg)) { // do nothing } else { int mr = CNeutrinoApp::getInstance()->handleMsg( msg, data ); if ( mr & messages_return::cancel_all ) { res = menu_return::RETURN_EXIT_ALL; doLoop = false; } else if ( mr & messages_return::unhandled ) { if ((msg <= CRCInput::RC_MaxRC) && (data == 0)) /* <- button pressed */ { if ( anyKeyCancels ) doLoop = false; else timeoutEnd = CRCInput::calcTimeoutEnd(timeout); } } } } return res; } #endif int CRCInput::addTimer(uint64_t Interval, bool oneshot, bool correct_time ) { uint64_t timeNow = time_monotonic_us(); timer _newtimer; if (!oneshot) _newtimer.interval = Interval; else _newtimer.interval = 0; _newtimer.id = timerid++; /* in theory, this uint32_t could overflow... */ /* ...and timerid == 0 is used as "no timer" in many places. */ if (timerid == 0) timerid++; if ( correct_time ) _newtimer.times_out = timeNow+ Interval; else _newtimer.times_out = Interval; _newtimer.correct_time = correct_time; //printf("adding timer %d (0x%" PRIx64 ", 0x%" PRIx64 ")\n", _newtimer.id, _newtimer.times_out, Interval); timer_mutex.lock(); std::vector::iterator e; for ( e= timers.begin(); e!= timers.end(); ++e ) if ( e->times_out> _newtimer.times_out ) break; timers.insert(e, _newtimer); timer_mutex.unlock(); return _newtimer.id; } void CRCInput::killTimer(uint32_t &id) { //printf("killing timer %d\n", id); if(id == 0) return; timer_mutex.lock(); std::vector::iterator e; for ( e= timers.begin(); e!= timers.end(); ++e ) if ( e->id == id ) { timers.erase(e); break; } id = 0; timer_mutex.unlock(); } int CRCInput::checkTimers() { int _id = 0; uint64_t timeNow = time_monotonic_us(); timer_mutex.lock(); std::vector::iterator e; for ( e= timers.begin(); e!= timers.end(); ++e ) if ( e->times_out< timeNow+ 2000 ) { //printf("timeout timer %d %llx %llx\n",e->id,e->times_out,timeNow ); _id = e->id; if ( e->interval != 0 ) { timer _newtimer; _newtimer.id = e->id; _newtimer.interval = e->interval; _newtimer.correct_time = e->correct_time; if ( _newtimer.correct_time ) _newtimer.times_out = timeNow + e->interval; else _newtimer.times_out = e->times_out + e->interval; timers.erase(e); for ( e= timers.begin(); e!= timers.end(); ++e ) if ( e->times_out> _newtimer.times_out ) break; timers.insert(e, _newtimer); } else timers.erase(e); break; } // else // printf("skipped timer %d %llx %llx\n",e->id,e->times_out, timeNow ); //printf("checkTimers: return %d\n", _id); timer_mutex.unlock(); return _id; } int64_t CRCInput::calcTimeoutEnd(const int _timeout_in_seconds) { uint64_t timeout_in_seconds = (_timeout_in_seconds == 0) ? INT_MAX : _timeout_in_seconds; return time_monotonic_us() + (timeout_in_seconds * 1000000); } int64_t CRCInput::calcTimeoutEnd_MS(const int timeout_in_milliseconds) { return time_monotonic_us() + (timeout_in_milliseconds * 1000); } void CRCInput::getMsgAbsoluteTimeout(neutrino_msg_t * msg, neutrino_msg_data_t * data, uint64_t *TimeoutEnd, bool bAllowRepeatLR) { uint64_t timeNow = time_monotonic_us(); uint64_t diff; if ( *TimeoutEnd < timeNow+ 100 ) diff = 100; // Minimum Differenz... else diff = ( *TimeoutEnd - timeNow ); //printf("CRCInput::getMsgAbsoluteTimeout diff %llx TimeoutEnd %llx now %llx\n", diff, *TimeoutEnd, timeNow); getMsg_us( msg, data, diff, bAllowRepeatLR ); } void CRCInput::getMsg(neutrino_msg_t * msg, neutrino_msg_data_t * data, int Timeout, bool bAllowRepeatLR) { getMsg_us(msg, data, (uint64_t) Timeout * 100 * 1000, bAllowRepeatLR); } void CRCInput::getMsg_ms(neutrino_msg_t * msg, neutrino_msg_data_t * data, int Timeout, bool bAllowRepeatLR) { getMsg_us(msg, data, (uint64_t) Timeout * 1000, bAllowRepeatLR); } uint32_t *CRCInput::setAllowRepeat(uint32_t *rk) { uint32_t *r = repeatkeys; repeatkeys = rk; return r; } bool checkLongPress(uint32_t key); // keybind_setup.cpp bool CRCInput::mayLongPress(uint32_t key, bool bAllowRepeatLR) { if (mayRepeat(key, bAllowRepeatLR)) return false; if (longPressAny) return true; return checkLongPress(key); } bool CRCInput::mayRepeat(uint32_t key, bool bAllowRepeatLR) { if((key == RC_up) || (key == RC_down) || (key == RC_plus) || (key == RC_minus) || (key == RC_page_down) || (key == RC_page_up) || ((bAllowRepeatLR) && ((key == RC_left ) || (key == RC_right)))) return true; if (repeatkeys) { uint32_t *k = repeatkeys; while (*k != RC_nokey) { if (*k == key) { return true; } k++; } } return false; } void CRCInput::getMsg_us(neutrino_msg_t * msg, neutrino_msg_data_t * data, uint64_t Timeout, bool bAllowRepeatLR) { static uint64_t last_keypress = 0ULL; //uint64_t getKeyBegin; //static __u16 rc_last_key = KEY_MAX; static __u16 rc_last_repeat_key = KEY_MAX; struct timeval tvselect; uint64_t InitialTimeout = Timeout; int64_t targetTimeout; int timer_id; fd_set rfds; *data = 0; /* reopen a missing input device * TODO: real hot-plugging, e.g. of keyboards and triggering this loop... * right now it is only run if some event is happening "by accident" */ if (!input_stopped) { if (checkdev()) open(true); } // wiederholung reinmachen - dass wirklich die ganze zeit bis timeout gewartet wird! uint64_t getKeyBegin = time_monotonic_us(); while(1) { timer_id = 0; if ( !timers.empty() ) { uint64_t t_n = time_monotonic_us(); if ( timers[0].times_out< t_n ) { timer_id = checkTimers(); *msg = NeutrinoMessages::EVT_TIMER; *data = timer_id; return; } else { targetTimeout = timers[0].times_out - t_n; if ( (uint64_t) targetTimeout> Timeout) targetTimeout= Timeout; else timer_id = timers[0].id; } } else targetTimeout= Timeout; tvselect.tv_sec = targetTimeout/1000000; tvselect.tv_usec = targetTimeout%1000000; FD_ZERO(&rfds); for (unsigned int i = 0; i < indev.size(); i++) { if (indev[i].fd != -1) FD_SET(indev[i].fd, &rfds); } #ifdef KEYBOARD_INSTEAD_OF_REMOTE_CONTROL if (true) #else if (fd_keyb> 0) #endif /* KEYBOARD_INSTEAD_OF_REMOTE_CONTROL */ FD_SET(fd_keyb, &rfds); FD_SET(fd_event, &rfds); FD_SET(fd_pipe_high_priority[0], &rfds); FD_SET(fd_pipe_low_priority[0], &rfds); int status = select(fd_max+1, &rfds, NULL, NULL, &tvselect); if ( status == -1 ) { perror("[neutrino - getMsg_us]: select returned "); // in case of an error return timeout...?! *msg = RC_timeout; *data = 0; return; } else if ( status == 0 ) // Timeout! { if ( timer_id != 0 ) { timer_id = checkTimers(); if ( timer_id != 0 ) { *msg = NeutrinoMessages::EVT_TIMER; *data = timer_id; return; } else continue; } else { *msg = RC_timeout; *data = 0; return; } } if(FD_ISSET(fd_pipe_high_priority[0], &rfds)) { struct event buf; read(fd_pipe_high_priority[0], &buf, sizeof(buf)); *msg = buf.msg; *data = buf.data; // printf("got event from high-pri pipe %x %x\n", *msg, *data ); return; } #ifdef KEYBOARD_INSTEAD_OF_REMOTE_CONTROL if (FD_ISSET(fd_keyb, &rfds)) { uint32_t trkey = 0; char key = 0; read(fd_keyb, &key, sizeof(key)); switch(key) { case 27: // <- Esc trkey = KEY_HOME; break; case 10: // <- Return case 'o': trkey = KEY_OK; break; case 'p': trkey = KEY_POWER; break; case 's': trkey = KEY_SETUP; break; case 'h': trkey = KEY_HELP; break; case 'i': trkey = KEY_UP; break; case 'm': trkey = KEY_DOWN; break; case 'j': trkey = KEY_LEFT; break; case 'k': trkey = KEY_RIGHT; break; case 'r': trkey = KEY_RED; break; case 'g': trkey = KEY_GREEN; break; case 'y': trkey = KEY_YELLOW; break; case 'b': trkey = KEY_BLUE; break; case '0': trkey = RC_0; break; case '1': trkey = RC_1; break; case '2': trkey = RC_2; break; case '3': trkey = RC_3; break; case '4': trkey = RC_4; break; case '5': trkey = RC_5; break; case '6': trkey = RC_6; break; case '7': trkey = RC_7; break; case '8': trkey = RC_8; break; case '9': trkey = RC_9; break; case '+': trkey = RC_plus; break; case '-': trkey = RC_minus; break; case 'a': trkey = KEY_A; break; case 'u': trkey = KEY_U; break; case '/': trkey = KEY_SLASH; break; case '\\': trkey = KEY_BACKSLASH; break; default: trkey = RC_nokey; } if (trkey != RC_nokey) { *msg = trkey; *data = 0; /* <- button pressed */ return; } } #else /* if(FD_ISSET(fd_keyb, &rfds)) { char key = 0; read(fd_keyb, &key, sizeof(key)); printf("keyboard: %d\n", rc_key); } */ #endif /* KEYBOARD_INSTEAD_OF_REMOTE_CONTROL */ if(FD_ISSET(fd_event, &rfds)) { //printf("[neutrino] event - accept!\n"); socklen_t clilen; struct sockaddr_in cliaddr; clilen = sizeof(cliaddr); int fd_eventclient = accept(fd_event, (struct sockaddr *) &cliaddr, &clilen); *msg = RC_nokey; //printf("[neutrino] network event - read!\n"); CEventServer::eventHead emsg; int read_bytes= recv(fd_eventclient, &emsg, sizeof(emsg), MSG_WAITALL); //printf("[neutrino] event read %d bytes - following %d bytes\n", read_bytes, emsg.dataSize ); if ( read_bytes == sizeof(emsg) ) { bool dont_delete_p = false; unsigned char* p; p= new unsigned char[ emsg.dataSize + 1 ]; if ( p!=NULL ) { read_bytes= recv(fd_eventclient, p, emsg.dataSize, MSG_WAITALL); //printf("[neutrino] eventbody read %d bytes - initiator %x\n", read_bytes, emsg.initiatorID ); #if 0 if ( emsg.initiatorID == CEventServer::INITID_CONTROLD ) { switch(emsg.eventID) { case CControldClient::EVT_VOLUMECHANGED : *msg = NeutrinoMessages::EVT_VOLCHANGED; *data = 0; break; case CControldClient::EVT_MUTECHANGED : *msg = NeutrinoMessages::EVT_MUTECHANGED; *data = (unsigned) p; dont_delete_p = true; break; case CControldClient::EVT_VCRCHANGED : *msg = NeutrinoMessages::EVT_VCRCHANGED; *data = *(int*) p; break; case CControldClient::EVT_MODECHANGED : *msg = NeutrinoMessages::EVT_MODECHANGED; *data = *(int*) p; break; default: printf("[neutrino] event INITID_CONTROLD - unknown eventID 0x%x\n", emsg.eventID ); } } else #endif if ( emsg.initiatorID == CEventServer::INITID_HTTPD ) { switch(emsg.eventID) { case NeutrinoMessages::SHUTDOWN : *msg = NeutrinoMessages::SHUTDOWN; *data = 0; break; case NeutrinoMessages::REBOOT : *msg = NeutrinoMessages::REBOOT; *data = 0; break; case NeutrinoMessages::RESTART : *msg = NeutrinoMessages::RESTART; *data = 0; break; case NeutrinoMessages::EVT_POPUP : *msg = NeutrinoMessages::EVT_POPUP; *data = (unsigned long) p; dont_delete_p = true; break; case NeutrinoMessages::EVT_EXTMSG : *msg = NeutrinoMessages::EVT_EXTMSG; *data = (unsigned long) p; dont_delete_p = true; break; case NeutrinoMessages::CHANGEMODE : // Change *msg = NeutrinoMessages::CHANGEMODE; *data = *(unsigned long*) p; break; case NeutrinoMessages::STANDBY_TOGGLE : *msg = NeutrinoMessages::STANDBY_TOGGLE; *data = 0; break; case NeutrinoMessages::STANDBY_ON : *msg = NeutrinoMessages::STANDBY_ON; *data = 0; break; case NeutrinoMessages::STANDBY_OFF : *msg = NeutrinoMessages::STANDBY_OFF; *data = 0; break; case NeutrinoMessages::EVT_START_PLUGIN : *msg = NeutrinoMessages::EVT_START_PLUGIN; *data = (unsigned long) p; dont_delete_p = true; break; case NeutrinoMessages::LOCK_RC : *msg = NeutrinoMessages::LOCK_RC; *data = 0; break; case NeutrinoMessages::UNLOCK_RC : *msg = NeutrinoMessages::UNLOCK_RC; *data = 0; break; case NeutrinoMessages::RELOAD_SETUP : *msg = NeutrinoMessages::RELOAD_SETUP; *data = 0; break; case NeutrinoMessages::EVT_HDMI_CEC_VIEW_ON: *msg = NeutrinoMessages::EVT_HDMI_CEC_VIEW_ON; *data = 0; break; case NeutrinoMessages::EVT_HDMI_CEC_STANDBY: *msg = NeutrinoMessages::EVT_HDMI_CEC_STANDBY; *data = 0; break; case NeutrinoMessages::EVT_SET_MUTE : *msg = NeutrinoMessages::EVT_SET_MUTE; *data = *(char*) p; break; case NeutrinoMessages::EVT_SET_VOLUME : *msg = NeutrinoMessages::EVT_SET_VOLUME; *data = *(char*) p; break; case NeutrinoMessages::RECORD_START : *msg = NeutrinoMessages::RECORD_START; *data = (unsigned long) p; dont_delete_p = true; break; case NeutrinoMessages::RECORD_STOP : *msg = NeutrinoMessages::RECORD_STOP; *data = (unsigned long) p; dont_delete_p = true; break; default: printf("[neutrino] event INITID_HTTPD - unknown eventID 0x%x\n", emsg.eventID ); } } else if ( emsg.initiatorID == CEventServer::INITID_SECTIONSD ) { //printf("[neutrino] event - from SECTIONSD %x %x\n", emsg.eventID, *(unsigned*) p); switch(emsg.eventID) { case CSectionsdClient::EVT_TIMESET: { printf("[neutrino] CSectionsdClient::EVT_TIMESET: timediff %" PRId64 "\n", *(int64_t*) p); /* compensate last_keypress for autorepeat / long press detection * I doubt this works correcty, if we had kernel 3.4+, using * EVIOCSCLOCKID ioctl would be better. * Still guessing the logic behind the condition... */ if ((int64_t)last_keypress > *(int64_t*)p) last_keypress += *(int64_t *)p; *msg = NeutrinoMessages::EVT_TIMESET; *data = (neutrino_msg_data_t) p; dont_delete_p = true; break; } case CSectionsdClient::EVT_GOT_CN_EPG: printf("[neutrino] CSectionsdClient::EVT_GOT_CN_EPG\n"); *msg = NeutrinoMessages::EVT_CURRENTNEXT_EPG; *data = (neutrino_msg_data_t) p; dont_delete_p = true; break; case CSectionsdClient::EVT_WRITE_SI_FINISHED: *msg = NeutrinoMessages::EVT_SI_FINISHED; *data = 0; break; case CSectionsdClient::EVT_EIT_COMPLETE: printf("[neutrino] CSectionsdClient::EVT_EIT_COMPLETE\n"); *msg = NeutrinoMessages::EVT_EIT_COMPLETE; *data = (neutrino_msg_data_t) p; dont_delete_p = true; break; #if 0 case CSectionsdClient::EVT_SERVICES_UPDATE: *msg = NeutrinoMessages::EVT_SERVICES_UPD; *data = 0; break; case CSectionsdClient::EVT_BOUQUETS_UPDATE: break; #endif case CSectionsdClient::EVT_RELOAD_XMLTV: *msg = NeutrinoMessages::EVT_RELOAD_XMLTV; *data = 0; break; default: printf("[neutrino] event INITID_SECTIONSD - unknown eventID 0x%x\n", emsg.eventID ); } } else if ( emsg.initiatorID == CEventServer::INITID_ZAPIT ) { //printf("[neutrino] event - from ZAPIT %x %x\n", emsg.eventID, *(unsigned*) p); switch(emsg.eventID) { case CZapitClient::EVT_RECORDMODE_ACTIVATED: *msg = NeutrinoMessages::EVT_RECORDMODE; *data = true; break; case CZapitClient::EVT_RECORDMODE_DEACTIVATED: *msg = NeutrinoMessages::EVT_RECORDMODE; *data = false; break; case CZapitClient::EVT_ZAP_COMPLETE: *msg = NeutrinoMessages::EVT_ZAP_COMPLETE; break; case CZapitClient::EVT_ZAP_FAILED: *msg = NeutrinoMessages::EVT_ZAP_FAILED; break; case CZapitClient::EVT_ZAP_SUB_FAILED: *msg = NeutrinoMessages::EVT_ZAP_SUB_FAILED; break; case CZapitClient::EVT_ZAP_COMPLETE_IS_NVOD: *msg = NeutrinoMessages::EVT_ZAP_ISNVOD; break; case CZapitClient::EVT_ZAP_SUB_COMPLETE: *msg = NeutrinoMessages::EVT_ZAP_SUB_COMPLETE; break; case CZapitClient::EVT_SCAN_COMPLETE: *msg = NeutrinoMessages::EVT_SCAN_COMPLETE; *data = 0; break; case CZapitClient::EVT_SCAN_NUM_TRANSPONDERS: *msg = NeutrinoMessages::EVT_SCAN_NUM_TRANSPONDERS; *data = *(unsigned*) p; break; case CZapitClient::EVT_SCAN_REPORT_NUM_SCANNED_TRANSPONDERS: *msg = NeutrinoMessages::EVT_SCAN_REPORT_NUM_SCANNED_TRANSPONDERS; *data = *(unsigned*) p; break; case CZapitClient::EVT_SCAN_FOUND_A_CHAN: *msg = NeutrinoMessages::EVT_SCAN_FOUND_A_CHAN; break; case CZapitClient::EVT_SCAN_SERVICENAME: *msg = NeutrinoMessages::EVT_SCAN_SERVICENAME; break; case CZapitClient::EVT_SCAN_FOUND_TV_CHAN: *msg = NeutrinoMessages::EVT_SCAN_FOUND_TV_CHAN; *data = *(unsigned*) p; break; case CZapitClient::EVT_SCAN_FOUND_RADIO_CHAN: *msg = NeutrinoMessages::EVT_SCAN_FOUND_RADIO_CHAN; *data = *(unsigned*) p; break; case CZapitClient::EVT_SCAN_FOUND_DATA_CHAN: *msg = NeutrinoMessages::EVT_SCAN_FOUND_DATA_CHAN; *data = *(unsigned*) p; break; case CZapitClient::EVT_SCAN_REPORT_FREQUENCYP: *msg = NeutrinoMessages::EVT_SCAN_REPORT_FREQUENCYP; *data = *(unsigned*) p; break; case CZapitClient::EVT_SCAN_NUM_CHANNELS: *msg = NeutrinoMessages::EVT_SCAN_NUM_CHANNELS; *data = *(unsigned*) p; break; case CZapitClient::EVT_SCAN_PROVIDER: *msg = NeutrinoMessages::EVT_SCAN_PROVIDER; break; case CZapitClient::EVT_SCAN_SATELLITE: *msg = NeutrinoMessages::EVT_SCAN_SATELLITE; break; case CZapitClient::EVT_BOUQUETS_CHANGED: *msg = NeutrinoMessages::EVT_BOUQUETSCHANGED; *data = 0; break; case CZapitClient::EVT_SERVICES_CHANGED: *msg = NeutrinoMessages::EVT_SERVICESCHANGED; *data = 0; break; case CZapitClient::EVT_ZAP_CA_CLEAR: *msg = NeutrinoMessages::EVT_ZAP_CA_CLEAR; *data = *(unsigned*) p; break; case CZapitClient::EVT_ZAP_CA_LOCK: *msg = NeutrinoMessages::EVT_ZAP_CA_LOCK; *data = *(unsigned*) p; break; case CZapitClient::EVT_ZAP_CA_FTA: *msg = NeutrinoMessages::EVT_ZAP_CA_FTA; *data = *(unsigned*) p; break; case CZapitClient::EVT_ZAP_CA_ID : *msg = NeutrinoMessages::EVT_ZAP_CA_ID; *data = *(unsigned*) p; break; case CZapitClient::EVT_SCAN_FAILED: *msg = NeutrinoMessages::EVT_SCAN_FAILED; *data = 0; break; case CZapitClient::EVT_ZAP_MOTOR: *msg = NeutrinoMessages::EVT_ZAP_MOTOR; *data = *(unsigned*) p; break; case CZapitClient::EVT_SDT_CHANGED: *msg = NeutrinoMessages::EVT_SERVICES_UPD; *data = 0; break; case CZapitClient::EVT_PMT_CHANGED: *msg = NeutrinoMessages::EVT_PMT_CHANGED; *data = (neutrino_msg_data_t) p; dont_delete_p = true; break; case CZapitClient::EVT_TUNE_COMPLETE: *msg = NeutrinoMessages::EVT_TUNE_COMPLETE; *data = (neutrino_msg_data_t) p; break; case CZapitClient::EVT_BACK_ZAP_COMPLETE: *msg = NeutrinoMessages::EVT_BACK_ZAP_COMPLETE; *data = (neutrino_msg_data_t) p; break; case CZapitClient::EVT_WEBTV_ZAP_COMPLETE: *msg = NeutrinoMessages::EVT_WEBTV_ZAP_COMPLETE; *data = (neutrino_msg_data_t) p; break; default: printf("[neutrino] event INITID_ZAPIT - unknown eventID 0x%x\n", emsg.eventID ); } if (((*msg) >= CRCInput::RC_WithData) && ((*msg) < CRCInput::RC_WithData + 0x10000000)) { *data = (neutrino_msg_data_t) p; dont_delete_p = true; } } else if ( emsg.initiatorID == CEventServer::INITID_TIMERD ) { /* if (emsg.eventID==CTimerdClient::EVT_ANNOUNCE_NEXTPROGRAM) { } if (emsg.eventID==CTimerdClient::EVT_NEXTPROGRAM) { *msg = NeutrinoMessages::EVT_NEXTPROGRAM; *data = (neutrino_msg_data_t) p; dont_delete_p = true; } */ switch(emsg.eventID) { case CTimerdClient::EVT_ANNOUNCE_RECORD : *msg = NeutrinoMessages::ANNOUNCE_RECORD; *data = (unsigned long) p; dont_delete_p = true; break; case CTimerdClient::EVT_ANNOUNCE_ZAPTO : *msg = NeutrinoMessages::ANNOUNCE_ZAPTO; *data = (neutrino_msg_data_t)p; dont_delete_p = true; break; case CTimerdClient::EVT_ANNOUNCE_SHUTDOWN : *msg = NeutrinoMessages::ANNOUNCE_SHUTDOWN; *data = 0; break; case CTimerdClient::EVT_ANNOUNCE_SLEEPTIMER : *msg = NeutrinoMessages::ANNOUNCE_SLEEPTIMER; *data = 0; break; case CTimerdClient::EVT_SLEEPTIMER : *msg = NeutrinoMessages::SLEEPTIMER; *data = 0; break; case CTimerdClient::EVT_RECORD_START : *msg = NeutrinoMessages::RECORD_START; *data = (unsigned long) p; dont_delete_p = true; break; case CTimerdClient::EVT_RECORD_STOP : *msg = NeutrinoMessages::RECORD_STOP; *data = (unsigned long) p; dont_delete_p = true; break; case CTimerdClient::EVT_ZAPTO : *msg = NeutrinoMessages::ZAPTO; *data = (unsigned long) p; dont_delete_p = true; break; case CTimerdClient::EVT_SHUTDOWN : *msg = NeutrinoMessages::SHUTDOWN; *data = 0; break; case CTimerdClient::EVT_STANDBY_ON : *msg = NeutrinoMessages::STANDBY_ON; *data = 0; break; case CTimerdClient::EVT_STANDBY_OFF : *msg = NeutrinoMessages::STANDBY_OFF; *data = 0; break; case CTimerdClient::EVT_REMIND : *msg = NeutrinoMessages::REMIND; *data = (unsigned long) p; dont_delete_p = true; break; case CTimerdClient::EVT_EXEC_PLUGIN : *msg = NeutrinoMessages::EVT_START_PLUGIN; *data = (unsigned long) p; dont_delete_p = true; break; default : printf("[neutrino] event INITID_TIMERD - unknown eventID 0x%x\n", emsg.eventID ); } } else if (emsg.initiatorID == CEventServer::INITID_NEUTRINO) { printf("CRCInput::getMsg_us: INITID_NEUTRINO: msg %x size %d data %p\n", (int) emsg.eventID, emsg.dataSize, p); if (emsg.eventID == NeutrinoMessages::EVT_HOTPLUG) { printf("EVT_HOTPLUG: [%s]\n", (char *) p); *msg = emsg.eventID; *data = (neutrino_msg_data_t) p; dont_delete_p = true; } #if 0 if ((emsg.eventID == NeutrinoMessages::EVT_RECORDING_ENDED) && (read_bytes == sizeof(stream2file_status2_t))) { *msg = NeutrinoMessages::EVT_RECORDING_ENDED; *data = (neutrino_msg_data_t) p; dont_delete_p = true; } #endif } else if (emsg.initiatorID == CEventServer::INITID_GENERIC_INPUT_EVENT_PROVIDER) { if (read_bytes == sizeof(int)) { *msg = *(int *)p; *data = emsg.eventID; } } else printf("[neutrino] event - unknown initiatorID 0x%x\n", emsg.initiatorID); if (!dont_delete_p) { delete[] p; p = NULL; } } } else { printf("[neutrino] event - read failed!\n"); } ::close(fd_eventclient); if ( *msg != RC_nokey ) { // raus hier :) //printf("[neutrino] event 0x%x\n", *msg); return; } } /* iterate backwards or the vector will be corrupted by the indev.erase(i) */ std::vector::iterator i = indev.end(); while (i != indev.begin()) { --i; if (((*i).fd != -1) && (FD_ISSET((*i).fd, &rfds))) { uint64_t now_pressed = 0; t_input_event ev; memset(&ev, 0, sizeof(ev)); /* we later check for ev.type = EV_SYN = 0x00, so set something invalid here... */ ev.type = EV_MAX; int ret = read((*i).fd, &ev, sizeof(t_input_event)); if (ret != sizeof(t_input_event)) { if (errno == ENODEV) { /* hot-unplugged? */ ::close((*i).fd); i = indev.erase(i); } continue; } if (ev.type == EV_SYN) continue; /* ignore... */ /* try to compensate for possible changes in wall clock * kernel ev.time default uses CLOCK_REALTIME, as does gettimeofday(). * so subtract gettimeofday() from ev.time and then add * CLOCK_MONOTONIC, which is supposed to not change with settimeofday. * Everything would be much easier if we could use the post-kernel 3.4 * EVIOCSCLOCKID ioctl :-) */ struct timespec t1; now_pressed = ev.time.tv_usec + ev.time.tv_sec * 1000000ULL; if (!clock_gettime(CLOCK_MONOTONIC, &t1)) { struct timeval t2; gettimeofday(&t2, NULL); now_pressed += t1.tv_sec * 1000000ULL + t1.tv_nsec / 1000; now_pressed -= (t2.tv_usec + t2.tv_sec * 1000000ULL); } SHTDCNT::getInstance()->resetSleepTimer(); #if HAVE_ARM_HARDWARE || HAVE_MIPS_HARDWARE if ((ev.code == 0 || ev.code == 1) && ev.value && firstKey) continue; #endif if (ev.value && firstKey) { firstKey = false; CTimerManager::getInstance()->cancelShutdownOnWakeup(); } uint32_t trkey = translate(ev.code); printf("key: %04x value %d, translate: %04x -%s-\n", ev.code, ev.value, trkey, getKeyName(trkey).c_str()); if (trkey == RC_nokey) continue; if (g_settings.longkeypress_duration > LONGKEYPRESS_OFF) { uint64_t longPressNow = time_monotonic_us(); if (ev.value == 0 && longPressEnd) { if (longPressNow < longPressEnd) { // Key was a potential long press, but wasn't pressed long enough longPressEnd = 0; ev.value = 1; } else { // Long-press, key released after time limit longPressEnd = 0; continue; } } else if (ev.value == 1 && mayLongPress(trkey, bAllowRepeatLR)) { // A long-press may start here. longPressEnd = longPressNow + 1000 * g_settings.longkeypress_duration; rc_last_key = KEY_MAX; continue; } else if (ev.value == 2 && longPressEnd) { if (longPressEnd < longPressNow) { // Key was pressed long enough. ev.value = 1; trkey |= RC_Repeat; } else { // Long-press, but key still not released. Skip. continue; } } } if (ev.value) { d_printf("rc_last_key %04x rc_last_repeat_key %04x\n\n", rc_last_key, rc_last_repeat_key); bool keyok = true; #if 0 uint64_t now_pressed; tv = ev.time; now_pressed = (uint64_t) tv.tv_usec + (uint64_t)((uint64_t) tv.tv_sec * (uint64_t) 1000000); #endif if (trkey == rc_last_key) { /* only allow selected keys to be repeated */ if (mayRepeat(trkey, bAllowRepeatLR) || (g_settings.shutdown_real_rcdelay && ((trkey == RC_standby) && #if HAVE_CST_HARDWARE (cs_get_revision() > 7) #else (g_info.hw_caps->can_shutdown) #endif ))) { #ifdef ENABLE_REPEAT_CHECK if (rc_last_repeat_key != trkey) { if ((now_pressed > last_keypress + repeat_block) || /* accept all keys after time discontinuity: */ (now_pressed < last_keypress)) rc_last_repeat_key = trkey; else keyok = false; } #endif } else keyok = false; } else rc_last_repeat_key = KEY_MAX; rc_last_key = trkey; if (keyok) { #ifdef ENABLE_REPEAT_CHECK if ((now_pressed > last_keypress + repeat_block_generic) || /* accept all keys after time discontinuity: */ (now_pressed < last_keypress)) rc_last_repeat_key = trkey; #endif { last_keypress = now_pressed; *msg = trkey; *data = 0; /* <- button pressed */ return; } } /*if keyok */ } /* if (ev.value) */ else { // clear rc_last_key on keyup event rc_last_key = KEY_MAX; if (trkey == RC_standby) { *msg = RC_standby; *data = 1; /* <- button released */ return; } } }/* if FDSET */ } /* for NUMBER_OF_EVENT_DEVICES */ if(FD_ISSET(fd_pipe_low_priority[0], &rfds)) { struct event buf; read(fd_pipe_low_priority[0], &buf, sizeof(buf)); *msg = buf.msg; *data = buf.data; // printf("got event from low-pri pipe %x %x\n", *msg, *data ); return; } if ( InitialTimeout == 0 ) { //nicht warten wenn kein key da ist *msg = RC_timeout; *data = 0; return; } else { //timeout neu kalkulieren int64_t getKeyNow = time_monotonic_us(); int64_t diff = (getKeyNow - getKeyBegin); if( Timeout <= (uint64_t) diff ) { *msg = RC_timeout; *data = 0; return; } else Timeout -= diff; } } } void CRCInput::postMsg(const neutrino_msg_t msg, const neutrino_msg_data_t data, const bool Priority) { // printf("postMsg %x %x %d\n", msg, data, Priority ); struct event buf; buf.msg = msg; buf.data = data; if (Priority) write(fd_pipe_high_priority[1], &buf, sizeof(buf)); else write(fd_pipe_low_priority[1], &buf, sizeof(buf)); } void CRCInput::clearRCMsg() { t_input_event ev; for (unsigned int i = 0; i < indev.size(); i++) { if (indev[i].fd != -1) { while (read(indev[i].fd, &ev, sizeof(t_input_event)) == sizeof(t_input_event)) ; } } rc_last_key = KEY_MAX; } /************************************************************************** * isNumeric - test if key is 0..9 * **************************************************************************/ bool CRCInput::isNumeric(const neutrino_msg_t key) { return ((key == RC_0) || ((key >= RC_1) && (key <= RC_9))); } /************************************************************************** * getNumericValue - return numeric value of the key or -1 * **************************************************************************/ int CRCInput::getNumericValue(const neutrino_msg_t key) { return ((key == RC_0) ? (int)0 : (((key >= RC_1) && (key <= RC_9)) ? (int)(key - RC_1 + 1) : (int)-1)); } /************************************************************************** * convertDigitToKey - return key representing digit or RC_nokey * **************************************************************************/ static const unsigned int digit_to_key[10] = {CRCInput::RC_0, CRCInput::RC_1, CRCInput::RC_2, CRCInput::RC_3, CRCInput::RC_4, CRCInput::RC_5, CRCInput::RC_6, CRCInput::RC_7, CRCInput::RC_8, CRCInput::RC_9}; unsigned int CRCInput::convertDigitToKey(const unsigned int digit) { return (digit < 10) ? digit_to_key[digit] : RC_nokey; } /************************************************************************** * getUnicodeValue - return unicode value of the key or \0 * **************************************************************************/ #define UNICODE_VALUE_SIZE 58 static const char unicode_value[UNICODE_VALUE_SIZE * 2] = "\0\0" "\0\0" "1\0" "2\0" "3\0" "4\0" "5\0" "6\0" "7\0" "8\0" "9\0" "0\0" "-\0" "=\0" "\0\0" "\0\0" "Q\0" "W\0" "E\0" "R\0" "T\0" "Y\0" "U\0" "I\0" "O\0" "P\0" "{\0" "}\0" "\0\0" "\0\0" "A\0" "S\0" "D\0" "F\0" "G\0" "H\0" "J\0" "K\0" "L\0" ";\0" "'\0" "\140\0" "\0\0" "\\\0" "Z\0" "X\0" "C\0" "V\0" "B\0" "N\0" "M\0" "\0\0" ".\0" "/\0" "\0\0" "\0\0" "\0\0" " "; const char *CRCInput::getUnicodeValue(const neutrino_msg_t key) { if (key < UNICODE_VALUE_SIZE) return unicode_value + key * 2; return ""; } /************************************************************************** * transforms the rc-key to const char * * **************************************************************************/ const char * CRCInput::getSpecialKeyName(const unsigned int key) { switch(key) { case RC_standby: return "standby"; case RC_home: return "home"; case RC_back: return "back"; case RC_setup: return "setup"; case RC_red: return "red button"; case RC_green: return "green button"; case RC_yellow: return "yellow button"; case RC_blue: return "blue button"; case RC_page_up: return "page up"; case RC_page_down: return "page down"; case RC_up: return "cursor up"; case RC_down: return "cursor down"; case RC_left: return "cursor left"; case RC_right: return "cursor right"; case RC_ok: return "ok"; case RC_plus: return "vol. inc"; case RC_minus: return "vol. dec"; case RC_spkr: return "mute"; case RC_help: return "help"; case RC_info: return "info"; case RC_topleft: return "topleft"; case RC_topright: return "topright"; case RC_audio: return "audio"; case RC_video: return "video"; case RC_tv: return "tv"; case RC_tv2: return "tv2"; case RC_radio: return "radio"; case RC_text: return "text"; case RC_epg: return "epg"; case RC_recall: return "recall"; case RC_favorites: return "favorites"; case RC_sat: return "sat"; case RC_sat2: return "sat2"; case RC_timeout: return "timeout"; case RC_play: return "play"; case RC_stop: return "stop"; case RC_forward: return "forward"; case RC_rewind: return "rewind"; case RC_timeshift: return "timeshift"; case RC_mode: return "mode"; case RC_record: return "record"; case RC_pause: return "pause"; case RC_games: return "games"; case RC_next: return "next"; case RC_prev: return "prev"; case RC_nokey: return "none"; case RC_power_on: return "power on"; case RC_power_off: return "power off"; case RC_standby_on: return "standby on"; case RC_standby_off: return "standby off"; case RC_mute_on: return "mute on"; case RC_mute_off: return "mute off"; case RC_analog_on: return "analog on"; case RC_analog_off: return "analog off"; case RC_www: return "www"; case RC_sub: return "sub"; case RC_pos: return "pos"; case RC_sleep: return "sleep"; case RC_nextsong: return "next song"; case RC_previoussong: return "previous song"; case RC_bookmarks: return "bookmarks"; case RC_program: return "program"; case RC_playpause: return "play / pause"; case RC_pvr: return "pvr"; case RC_touchpad_toggle: return "touchpad toggle"; case RC_vod: return "vod"; case RC_f1: return "f1"; case RC_f2: return "f2"; case RC_f3: return "f3"; case RC_f4: return "f4"; case RC_f5: return "f5"; case RC_f6: return "f6"; case RC_f7: return "f7"; case RC_f8: return "f8"; case RC_f9: return "f9"; case RC_f10: return "f10"; default: printf("unknown key: %d (0x%x) \n", key, key); return "unknown"; } } std::string CRCInput::getKeyName(const unsigned int key) { std::string res; if (key > RC_MaxRC) res = getKeyNameC(key); /* will only resolve RC_nokey or "unknown" */ else { res = (getKeyNameC(key & ~RC_Repeat)); if ((key & RC_Repeat) && res != "unknown") res += " (long)"; } return res; } const char *CRCInput::getKeyNameC(const unsigned int key) { const char *lunicode_value = getUnicodeValue(key); if (*lunicode_value) return lunicode_value; return getSpecialKeyName(key); } /************************************************************************** * transforms the rc-key to generic - internal use only! * **************************************************************************/ int CRCInput::translate(int code) { switch(code) { case KEY_EXIT: return RC_home; case 0x100: // FIXME -- needed? return RC_up; case 0x101: // FIXME -- needed? return RC_down; case KEY_CHANNELUP: return RC_page_up; case KEY_CHANNELDOWN: return RC_page_down; #if HAVE_ARM_HARDWARE || HAVE_MIPS_HARDWARE #if BOXMODEL_HD51 || BOXMODEL_BRE2ZE4K || BOXMODEL_H7 || BOXMODEL_E4HDULTRA || BOXMODEL_PROTEK4K || BOXMODEL_HD60 || BOXMODEL_HD61 || BOXMODEL_MULTIBOX || BOXMODEL_MULTIBOXSE case KEY_TV2: return RC_tv; #elif BOXMODEL_OSMIO4K || BOXMODEL_OSMIO4KPLUS case KEY_VIDEO: return RC_mode; #endif case KEY_SWITCHVIDEOMODE: return RC_mode; case KEY_FASTFORWARD: return RC_forward; case 0xb0: // vuplus timer key return RC_timer; #endif default: break; } if ((code >= 0) && (code <= KEY_MAX)) return code; return (int)RC_nokey; } void CRCInput::setKeyRepeatDelay(unsigned int start_ms, unsigned int repeat_ms) { if (start_ms == 0 && repeat_ms == 0) { start_ms = _start_ms; repeat_ms = _repeat_ms; } else { _start_ms = start_ms; _repeat_ms = repeat_ms; } /* iterate backwards or the vector will be corrupted by the indev.erase(i) */ std::vector::iterator it = indev.end(); while (it != indev.begin()) { --it; int fd = (*it).fd; std::string path = (*it).path; if (path == "/tmp/neutrino.input") continue; /* setting repeat rate does not work here */ #ifdef BOXMODEL_CST_HD1 /* this is ugly, but the driver does not support anything advanced... */ if (path == "/dev/input/nevis_ir") { d_printf("[rcinput:%s] %s(fd %d) using proprietary ioctl\n", __func__, path.c_str(), fd); ioctl(fd, IOC_IR_SET_F_DELAY, start_ms); ioctl(fd, IOC_IR_SET_X_DELAY, repeat_ms); continue; } #endif d_printf("[rcinput:%s] %s(fd %d) writing EV_REP (%d->%d)\n", __func__, path.c_str(), fd, start_ms, repeat_ms); /* if we have a good input device, we don't need the private ioctl above */ struct input_event ie; memset(&ie, 0, sizeof(ie)); ie.type = EV_REP; /* increase by 10 ms to trick the repeat checker code in the * rcinput loop into accepting the key event... */ ie.value = start_ms + 10; ie.code = REP_DELAY; if (write(fd, &ie, sizeof(ie)) == -1) { if (errno == ENODEV) { printf("[rcinput:%s] %s(fd %d) ENODEV??\n", __func__, path.c_str(), fd); /* hot-unplugged? */ ::close(fd); it = indev.erase(it); devinput_mtime.tv_sec = 0; /* force check */ continue; } printf("[rcinput:%s] %s(fd %d) write %s: %m\n", __func__, path.c_str(), fd, "REP_DELAY"); } ie.value = repeat_ms + 10; ie.code = REP_PERIOD; if (write(fd, &ie, sizeof(ie)) == -1) printf("[rcinput:%s] %s(fd %d) write %s: %m\n", __func__, path.c_str(), fd, "REP_PERIOD"); } } #ifdef IOC_IR_SET_PRI_PROTOCOL // hint: ir_protocol_t and other useful things are defined in cs_ir_generic.h void CRCInput::set_rc_hw(ir_protocol_t ir_protocol, unsigned int ir_address) { int ioctl_ret = -1; if (indev.empty()) { printf("[rcinput:%s] indev is empty!\n", __func__); return; } int fd = -1; for (std::vector::iterator it = indev.begin(); it != indev.end(); ++it) { if ((*it).path == "/dev/input/nevis_ir") { fd = (*it).fd; break; } } if (fd == -1) { printf("[rcinput:%s] no nevis_ir input device found??\n", __func__); return; } ioctl_ret = ::ioctl(fd, IOC_IR_SET_PRI_PROTOCOL, ir_protocol); if(ioctl_ret < 0) perror("IOC_IR_SET_PRI_PROTOCOL"); else printf("CRCInput::set_rc_hw: Set IOCTRL : IOC_IR_SET_PRI_PROTOCOL, %05X\n", ir_protocol); //bypass setting of IR Address with ir_address=0 if(ir_address > 0) { //fixme?: for now fd_rc[] is hardcoded to 0 since only fd_rc[0] is used at the moment ioctl_ret = ::ioctl(fd, IOC_IR_SET_PRI_ADDRESS, ir_address); if(ioctl_ret < 0) perror("IOC_IR_SET_PRI_ADDRESS"); else printf("CRCInput::set_rc_hw: Set IOCTRL : IOC_IR_SET_PRI_ADDRESS, %05X\n", ir_address); } } // hint: ir_protocol_t and other useful things are defined in cs_ir_generic.h void CRCInput::set_rc_hw(void) { ir_protocol_t ir_protocol = IR_PROTOCOL_UNKNOWN; unsigned int ir_address = 0x00; switch(g_settings.remote_control_hardware) { case RC_HW_COOLSTREAM: ir_protocol = IR_PROTOCOL_NECE; ir_address = 0xFF80; break; case RC_HW_DBOX: ir_protocol = IR_PROTOCOL_NRC17; ir_address = 0x00C5; break; case RC_HW_PHILIPS: ir_protocol = IR_PROTOCOL_RC5; ir_address = 0x000A; break; default: ir_protocol = IR_PROTOCOL_NECE; ir_address = 0xFF80; } set_rc_hw(ir_protocol, ir_address); } #else void CRCInput::set_rc_hw(void) { } #endif