diff options
Diffstat (limited to 'lib')
309 files changed, 10664 insertions, 6367 deletions
diff --git a/lib/actions/.gitignore b/lib/actions/.gitignore new file mode 100644 index 00000000..460e43d2 --- /dev/null +++ b/lib/actions/.gitignore @@ -0,0 +1 @@ +actionids.h diff --git a/lib/actions/Makefile.am b/lib/actions/Makefile.am index 8da583f7..eb8327d7 100644 --- a/lib/actions/Makefile.am +++ b/lib/actions/Makefile.am @@ -1,15 +1,27 @@ -INCLUDES = \ - -I$(top_srcdir)/include +AM_CPPFLAGS = \ + -I$(top_builddir) \ + -I$(top_srcdir) \ + -I$(top_srcdir)/include \ + -include Python.h \ + -include $(top_builddir)/enigma2_config.h + +EXTRA_DIST = parseactions.py noinst_LIBRARIES = libenigma_actions.a libenigma_actions_a_SOURCES = \ - action.cpp + action.cpp -CLEANFILES = actionids.h +actionsincludedir = $(pkgincludedir)/lib/actions +actionsinclude_HEADERS = \ + action.h -action.cpp: actionids.h +nodist_actionsinclude_HEADERS = \ + actionids.h + +BUILT_SOURCES = actionids.h +CLEANFILES = actionids.h -actionids.h: - python ./parseactions.py include ../gui/*.h > actionids.h - python ./parseactions.py parse ../gui/*.h >> actionids.h +actionids.h: $(srcdir)/parseactions.py $(top_srcdir)/lib/gui/*.h + $(AM_V_GEN)$(PYTHON) $(srcdir)/parseactions.py include $(top_srcdir)/lib/gui/*.h > $@ + $(AM_V_at)$(PYTHON) $(srcdir)/parseactions.py parse $(top_srcdir)/lib/gui/*.h >> $@ diff --git a/lib/actions/action.h b/lib/actions/action.h index cbf5370a..edcfac6f 100644 --- a/lib/actions/action.h +++ b/lib/actions/action.h @@ -3,10 +3,6 @@ #include <lib/base/object.h> - /* avoid warnigs :) */ -#include <features.h> -#undef _POSIX_C_SOURCE -#define _POSIX_C_SOURCE 200112L #include <lib/python/python.h> #include <string> #include <map> diff --git a/lib/actions/parseactions.py b/lib/actions/parseactions.py index 2462a751..cad432a7 100644 --- a/lib/actions/parseactions.py +++ b/lib/actions/parseactions.py @@ -89,7 +89,7 @@ def do_file(f, mode): if mode == "include": # hack hack hack!! - print "#include <lib" + f[2:] + ">" + print "#include <lib/" + '/'.join(f.split('/')[-2:]) + ">" else: print "\t// " + f diff --git a/lib/base/.gitignore b/lib/base/.gitignore new file mode 100644 index 00000000..aa9ae18f --- /dev/null +++ b/lib/base/.gitignore @@ -0,0 +1 @@ +eenv.cpp diff --git a/lib/base/Makefile.am b/lib/base/Makefile.am index 6ea9d03f..d76dfc9e 100644 --- a/lib/base/Makefile.am +++ b/lib/base/Makefile.am @@ -1,12 +1,56 @@ -INCLUDES = \ - -I$(top_srcdir)/include +AM_CPPFLAGS = \ + -I$(top_srcdir) \ + -I$(top_srcdir)/include \ + -include Python.h \ + -include $(top_builddir)/enigma2_config.h noinst_LIBRARIES = libenigma_base.a libenigma_base_a_SOURCES = \ - buffer.cpp ebase.cpp eerror.cpp elock.cpp \ - init.cpp message.cpp thread.cpp \ - smartptr.cpp estring.cpp connection.cpp \ - filepush.cpp encoding.cpp console.cpp rawfile.cpp \ - nconfig.cpp ioprio.cpp + buffer.cpp \ + connection.cpp \ + console.cpp \ + ebase.cpp \ + eenv.cpp \ + eerror.cpp \ + elock.cpp \ + encoding.cpp \ + estring.cpp \ + etpm.cpp \ + filepush.cpp \ + init.cpp \ + ioprio.cpp \ + message.cpp \ + nconfig.cpp \ + rawfile.cpp \ + smartptr.cpp \ + thread.cpp +EXTRA_DIST = \ + eenv.cpp.in + +baseincludedir = $(pkgincludedir)/lib/base +baseinclude_HEADERS = \ + buffer.h \ + console.h \ + ebase.h \ + eenv.h \ + eerror.h \ + elock.h \ + encoding.h \ + eptrlist.h \ + estring.h \ + etpm.h \ + filepush.h \ + i18n.h \ + itssource.h \ + init.h \ + init_num.h \ + ioprio.h \ + message.h \ + nconfig.h \ + object.h \ + rawfile.h \ + ringbuffer.h \ + smartptr.h \ + thread.h diff --git a/lib/base/console.cpp b/lib/base/console.cpp index add87066..00187683 100644 --- a/lib/base/console.cpp +++ b/lib/base/console.cpp @@ -516,18 +516,15 @@ eConsolePy_write(eConsolePy* self, PyObject *args) { int len; char *data; - if (PyArg_ParseTuple(args, "si", &data, &len)) - ; - else + int ret = -1; + Py_ssize_t argc = PyTuple_Size(args); + if (argc > 1) + ret = !PyArg_ParseTuple(args, "si", &data, &len); + else if (argc == 1) { PyObject *ob; - if (!PyArg_ParseTuple(args, "O", &ob) || !PyString_Check(ob)) - { - PyErr_SetString(PyExc_TypeError, - "1st arg must be a string, optionaly 2nd arg can be the string length"); - return NULL; - } - else + ret = !PyArg_ParseTuple(args, "O", &ob) || !PyString_Check(ob); + if (!ret) { Py_ssize_t length; if (!PyString_AsStringAndSize(ob, &data, &length)) @@ -536,6 +533,12 @@ eConsolePy_write(eConsolePy* self, PyObject *args) len = 0; } } + if (ret) + { + PyErr_SetString(PyExc_TypeError, + "1st arg must be a string, optionaly 2nd arg can be the string length"); + return NULL; + } self->cont->write(data, len); Py_RETURN_NONE; } diff --git a/lib/base/console.h b/lib/base/console.h index e730b40e..60540da0 100644 --- a/lib/base/console.h +++ b/lib/base/console.h @@ -1,7 +1,6 @@ #ifndef __LIB_BASE_CONSOLE_H__ #define __LIB_BASE_CONSOLE_H__ -#include "Python.h" #include <string> #include <lib/base/ebase.h> #include <lib/python/connections.h> diff --git a/lib/base/ebase.cpp b/lib/base/ebase.cpp index 5bafe732..700cce36 100644 --- a/lib/base/ebase.cpp +++ b/lib/base/ebase.cpp @@ -26,8 +26,11 @@ void eSocketNotifier::start() if (state) stop(); - context.addSocketNotifier(this); - state=2; // running but not in poll yet + if (eMainloop::isValid(&context)) + { + context.addSocketNotifier(this); + state=2; // running but not in poll yet + } } void eSocketNotifier::stop() @@ -46,29 +49,35 @@ void eTimer::start(long msek, bool singleShot) if (bActive) stop(); - bActive = true; - bSingleShot = singleShot; - interval = msek; - clock_gettime(CLOCK_MONOTONIC, &nextActivation); -// eDebug("this = %p\nnow sec = %d, nsec = %d\nadd %d msec", this, nextActivation.tv_sec, nextActivation.tv_nsec, msek); - nextActivation += (msek<0 ? 0 : msek); -// eDebug("next Activation sec = %d, nsec = %d", nextActivation.tv_sec, nextActivation.tv_nsec ); - context.addTimer(this); + if (eMainloop::isValid(&context)) + { + bActive = true; + bSingleShot = singleShot; + interval = msek; + clock_gettime(CLOCK_MONOTONIC, &nextActivation); +// eDebug("this = %p\nnow sec = %d, nsec = %d\nadd %d msec", this, nextActivation.tv_sec, nextActivation.tv_nsec, msek); + nextActivation += (msek<0 ? 0 : msek); +// eDebug("next Activation sec = %d, nsec = %d", nextActivation.tv_sec, nextActivation.tv_nsec ); + context.addTimer(this); + } } -void eTimer::startLongTimer( int seconds ) +void eTimer::startLongTimer(int seconds) { if (bActive) stop(); - bActive = bSingleShot = true; - interval = 0; - clock_gettime(CLOCK_MONOTONIC, &nextActivation); -// eDebug("this = %p\nnow sec = %d, nsec = %d\nadd %d sec", this, nextActivation.tv_sec, nextActivation.tv_nsec, seconds); - if ( seconds > 0 ) - nextActivation.tv_sec += seconds; -// eDebug("next Activation sec = %d, nsec = %d", nextActivation.tv_sec, nextActivation.tv_nsec ); - context.addTimer(this); + if (eMainloop::isValid(&context)) + { + bActive = bSingleShot = true; + interval = 0; + clock_gettime(CLOCK_MONOTONIC, &nextActivation); +// eDebug("this = %p\nnow sec = %d, nsec = %d\nadd %d sec", this, nextActivation.tv_sec, nextActivation.tv_nsec, seconds); + if ( seconds > 0 ) + nextActivation.tv_sec += seconds; +// eDebug("next Activation sec = %d, nsec = %d", nextActivation.tv_sec, nextActivation.tv_nsec ); + context.addTimer(this); + } } void eTimer::stop() @@ -114,6 +123,11 @@ void eTimer::activate() // Internal Funktion... called from eApplication // mainloop ePtrList<eMainloop> eMainloop::existing_loops; +bool eMainloop::isValid(eMainloop *ml) +{ + return std::find(existing_loops.begin(), existing_loops.end(), ml) != existing_loops.end(); +} + eMainloop::~eMainloop() { existing_loops.remove(this); @@ -151,7 +165,7 @@ void eMainloop::removeSocketNotifier(eSocketNotifier *sn) return; } for (i = notifiers.begin(); i != notifiers.end(); ++i) - eDebug("fd=%d, sn=%d", i->second->getFD(), (void*)i->second); + eDebug("fd=%d, sn=%p", i->second->getFD(), (void*)i->second); eFatal("removed socket notifier which is not present, fd=%d", fd); } @@ -229,14 +243,9 @@ int eMainloop::processOneEvent(unsigned int twisted_timeout, PyObject **res, ePy if (this == eApp) { - gOpcode op; - op.dc = 0; - op.opcode = gOpcode::flush; - gRC::getInstance()->submit(op); Py_BEGIN_ALLOW_THREADS ret = ::poll(pfd, fdcount, poll_timeout); Py_END_ALLOW_THREADS - } else ret = ::poll(pfd, fdcount, poll_timeout); diff --git a/lib/base/ebase.h b/lib/base/ebase.h index c69133f6..f6fc07d3 100644 --- a/lib/base/ebase.h +++ b/lib/base/ebase.h @@ -205,11 +205,9 @@ class eMainloop void removeSocketNotifier(eSocketNotifier *sn); void addTimer(eTimer* e); void removeTimer(eTimer* e); -public: -#ifndef SWIG static ePtrList<eMainloop> existing_loops; -#endif - + static bool isValid(eMainloop *); +public: eMainloop() :app_quit_now(0),loop_level(0),retval(0), m_is_idle(0), m_idle_count(0), m_inActivate(0), m_interrupt_requested(0) { diff --git a/lib/base/eenv.cpp.in b/lib/base/eenv.cpp.in new file mode 100644 index 00000000..52c42147 --- /dev/null +++ b/lib/base/eenv.cpp.in @@ -0,0 +1,117 @@ +#include <cstdio> +#include <cstdlib> +#include <cstring> +#include <wordexp.h> +#include <lib/base/eenv.h> +#include <lib/base/eerror.h> + +bool eEnv::initialized = false; + +void eEnv::initialize() +{ + static const struct { + std::string name; + std::string value; + } cfgenv[] = { + { "prefix", "@prefix@" }, + { "exec_prefix", "@exec_prefix@" }, + { "bindir", "@bindir@" }, + { "sbindir", "@sbindir@" }, + { "libexecdir", "@libexecdir@" }, + { "datarootdir", "@datarootdir@" }, + { "datadir", "@datadir@" }, + { "sysconfdir", "@sysconfdir@" }, + { "sharedstatedir", "@sharedstatedir@" }, + { "localstatedir", "@localstatedir@" }, + { "libdir", "@libdir@" }, + { "localedir", "@localedir@" }, + }; + size_t i; + + // 1st pass, as generated by configure. + // Variables set by the user will not be overwritten. + for (i = 0; i < (sizeof(cfgenv) / sizeof(*cfgenv)); i++) { + eDebug("setenv('%s', '%s', 0)", cfgenv[i].name.c_str(), cfgenv[i].value.c_str()); + setenv(cfgenv[i].name.c_str(), cfgenv[i].value.c_str(), 0); + } + + // 2nd pass: Resolve directories. + for (i = 0; i < (sizeof(cfgenv) / sizeof(*cfgenv)); i++) { + std::string dest; + eEnv::resolveVar(dest, "${" + cfgenv[i].name + "}"); + eDebug("setenv('%s', '%s', 1)", cfgenv[i].name.c_str(), dest.c_str()); + setenv(cfgenv[i].name.c_str(), dest.c_str(), 1); + } +} + +int eEnv::resolveVar(std::string &dest, const char *src) +{ + size_t i = 0; + int ret; + wordexp_t p; + + ret = wordexp(src, &p, WRDE_NOCMD | WRDE_UNDEF); + if (ret != 0) { + switch (ret) { + case WRDE_BADCHAR: + eDebug("%s: bad character", __func__); + break; + case WRDE_BADVAL: + eDebug("%s: bad value", __func__); + break; + case WRDE_CMDSUB: + eDebug("%s: invalid command substitution", __func__); + break; + case WRDE_NOSPACE: + eDebug("%s: out of memory", __func__); + break; + case WRDE_SYNTAX: + eDebug("%s: syntax error", __func__); + break; + default: + eDebug("%s: unknown error", __func__); + break; + } + + return -1; + } + + while (i < p.we_wordc) { + if (strchr(p.we_wordv[i], '$')) { + ret = eEnv::resolveVar(dest, p.we_wordv[i]); + if (ret < 0) + break; + } else { + dest.append(p.we_wordv[i]); + } + + if (++i < p.we_wordc) + dest.append(" "); + } + + wordfree(&p); + return ret; +} + +int eEnv::resolveVar(std::string &dest, const std::string &src) +{ + return eEnv::resolveVar(dest, src.c_str()); +} + +std::string eEnv::resolve(const std::string &src) +{ + std::string dest; + + if (!initialized) { + eEnv::initialize(); + initialized = true; + } + + eDebug("%s: resolve %s", __func__, src.c_str()); + + eEnv::resolveVar(dest, src); + + eDebug("%s: -> %s", __func__, dest.c_str()); + + return dest; +} diff --git a/lib/base/eenv.h b/lib/base/eenv.h new file mode 100644 index 00000000..3d149f5e --- /dev/null +++ b/lib/base/eenv.h @@ -0,0 +1,16 @@ +#ifndef __lib_base_paths_h +#define __lib_base_paths_h + +#include <string> + +class eEnv { +private: + static bool initialized; + static void initialize(); + static int resolveVar(std::string &dest, const char *src); + static int resolveVar(std::string &dest, const std::string &src); +public: + static std::string resolve(const std::string &path); +}; + +#endif diff --git a/lib/base/encoding.cpp b/lib/base/encoding.cpp index 8b4b592b..361acce5 100644 --- a/lib/base/encoding.cpp +++ b/lib/base/encoding.cpp @@ -2,6 +2,7 @@ #include <cstdlib> #include <lib/base/encoding.h> #include <lib/base/eerror.h> +#include <lib/base/eenv.h> eDVBTextEncodingHandler encodingHandler; // the one and only instance @@ -17,8 +18,8 @@ inline char toupper(char c) eDVBTextEncodingHandler::eDVBTextEncodingHandler() { - const char * file=DATADIR "/enigma2/encoding.conf"; - FILE *f = fopen(file, "rt"); + std::string file = eEnv::resolve("${datadir}/enigma2/encoding.conf"); + FILE *f = fopen(file.c_str(), "rt"); if (f) { char *line = (char*) malloc(256); @@ -61,7 +62,7 @@ eDVBTextEncodingHandler::eDVBTextEncodingHandler() free(line); } else - eDebug("[eDVBTextEncodingHandler] couldn't open %s !", file); + eDebug("[eDVBTextEncodingHandler] couldn't open %s !", file.c_str()); } void eDVBTextEncodingHandler::getTransponderDefaultMapping(int tsidonid, int &table) diff --git a/lib/base/eptrlist.h b/lib/base/eptrlist.h index 75123053..0da46daa 100644 --- a/lib/base/eptrlist.h +++ b/lib/base/eptrlist.h @@ -164,7 +164,7 @@ public: // returns a pointer to this new vector ( the reserved memory must deletet from the receiver !! ) std::vector<T>* v=new std::vector<T>(); v->reserve( std::list<T>::size() ); - for ( std_list_T_iterator it( std::list<T*>::begin() ); it != std::list<T*>::end(); it++) + for ( std_list_T_iterator it( std::list<T*>::begin() ); it != std::list<T*>::end(); it++) v->push_back( **it ); return v; diff --git a/lib/base/estring.cpp b/lib/base/estring.cpp index e7dc2bec..f2ac7325 100644 --- a/lib/base/estring.cpp +++ b/lib/base/estring.cpp @@ -424,7 +424,7 @@ std::string convertDVBUTF8(const unsigned char *data, int len, int table, int ts break; } - bool useTwoCharMapping = !table || tsidonid && encodingHandler.getTransponderUseTwoCharMapping(tsidonid); + bool useTwoCharMapping = !table || (tsidonid && encodingHandler.getTransponderUseTwoCharMapping(tsidonid)); if (useTwoCharMapping && table == 5) { // i hope this dont break other transponders which realy use ISO8859-5 and two char byte mapping... // eDebug("Cyfra / Cyfrowy Polsat HACK... override given ISO8859-5 with ISO6397"); diff --git a/lib/base/etpm.cpp b/lib/base/etpm.cpp new file mode 100644 index 00000000..9a443e0a --- /dev/null +++ b/lib/base/etpm.cpp @@ -0,0 +1,172 @@ +#include <sys/socket.h> +#include <fcntl.h> +#include <stdbool.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <sys/un.h> +#include <unistd.h> +#include <openssl/bn.h> +#include <openssl/sha.h> +#include <lib/base/eerror.h> + +#include "etpm.h" + +eTPM::eTPM() +{ + struct sockaddr_un addr; + unsigned char buf[8]; + unsigned int tag; + size_t len; + unsigned char *val; + + level2_cert_read = level3_cert_read = false; + + addr.sun_family = AF_UNIX; + strcpy(addr.sun_path, TPMD_SOCKET); + + fd = socket(PF_UNIX, SOCK_STREAM, 0); + if (fd < 0) + { + eDebug("[eTPM] socket error"); + return; + } + + if (connect(fd, (const struct sockaddr *)&addr, SUN_LEN(&addr)) < 0) + { + eDebug("[eTPM] connect error"); + return; + } + + buf[0] = TPMD_DT_LEVEL2_CERT; + buf[1] = TPMD_DT_LEVEL3_CERT; + if (!send_cmd(TPMD_CMD_GET_DATA, buf, 2)) + { + return; + } + + val = (unsigned char*)recv_cmd(&tag, &len); + if (val == NULL) + { + return; + } + + parse_data(val, len); + free(val); +} + +eTPM::~eTPM() +{ + +} + +bool eTPM::send_cmd(enum tpmd_cmd cmd, const void *data, size_t len) +{ + unsigned char buf[len + 4]; + + buf[0] = (cmd >> 8) & 0xff; + buf[1] = (cmd >> 0) & 0xff; + buf[2] = (len >> 8) & 0xff; + buf[3] = (len >> 0) & 0xff; + memcpy(&buf[4], data, len); + + if (write(fd, buf, sizeof(buf)) != (ssize_t)sizeof(buf)) + { + fprintf(stderr, "%s: incomplete write\n", __func__); + return false; + } + + return true; +} + +void* eTPM::recv_cmd(unsigned int *tag, size_t *len) +{ + unsigned char buf[4]; + void *val; + + if (read(fd, buf, 4) != 4) + { + fprintf(stderr, "%s: incomplete read\n", __func__); + return NULL; + } + + *tag = (buf[0] << 8) | buf[1]; + *len = (buf[2] << 8) | buf[3]; + + val = malloc(*len); + if (val == NULL) + return NULL; + + ssize_t rd = read(fd, val, *len); + if (rd < 0) + { + perror("eTPM::recv_cmd read"); + free(val); + } + else if ((size_t)rd != *len) { + fprintf(stderr, "%s: incomplete read\n", __func__); + free(val); + return NULL; + } + + return val; +} + +void eTPM::parse_data(const unsigned char *data, size_t datalen) +{ + unsigned int i; + unsigned int tag; + unsigned int len; + const unsigned char *val; + + for (i = 0; i < datalen; i += len) { + tag = data[i++]; + len = data[i++]; + val = &data[i]; + + switch (tag) { + case TPMD_DT_LEVEL2_CERT: + if (len != 210) + break; + memcpy(level2_cert, val, 210); + level2_cert_read = true; + break; + case TPMD_DT_LEVEL3_CERT: + if (len != 210) + break; + memcpy(level3_cert, val, 210); + level3_cert_read = true; + break; + } + } +} + +std::string eTPM::getCert(cert_type type) +{ + if (type == TPMD_DT_LEVEL2_CERT && level2_cert_read) + return std::string((char*)level2_cert, 210); + else if (type == TPMD_DT_LEVEL3_CERT && level3_cert_read) + return std::string((char*)level3_cert, 210); + return ""; +} + +std::string eTPM::challenge(std::string rnd) +{ + if (rnd.length() == 8) + { + if (!send_cmd(TPMD_CMD_COMPUTE_SIGNATURE, rnd.c_str(), 8)) + return ""; + + unsigned int tag; + size_t len; + unsigned char *val = (unsigned char*)recv_cmd(&tag, &len); + + if (tag != TPMD_CMD_COMPUTE_SIGNATURE) + return ""; + + std::string ret((char*)val, len); + free(val); + return ret; + } + return ""; +} diff --git a/lib/base/etpm.h b/lib/base/etpm.h new file mode 100644 index 00000000..c9e52140 --- /dev/null +++ b/lib/base/etpm.h @@ -0,0 +1,44 @@ +#ifndef __lib_base_etpm_h +#define __lib_base_etpm_h + +#ifndef SWIG +#define TPMD_SOCKET "/var/run/tpmd_socket" +#endif + +#include <string> + +class eTPM +{ +#ifndef SWIG + int fd; + unsigned char level2_cert[210]; + unsigned char level3_cert[210]; + bool level2_cert_read; + bool level3_cert_read; + + enum tpmd_cmd { + TPMD_CMD_RESERVED = 0x0000, + TPMD_CMD_GET_DATA = 0x0001, + TPMD_CMD_APDU = 0x0002, + TPMD_CMD_COMPUTE_SIGNATURE = 0x0003, + TPMD_CMD_APP_CERT = 0x0004, + }; + + bool send_cmd(enum tpmd_cmd cmd, const void *data, size_t len); + void *recv_cmd(unsigned int *tag, size_t *len); + void parse_data(const unsigned char *data, size_t datalen); + +#endif +public: + eTPM(); + ~eTPM(); + + enum cert_type { + TPMD_DT_LEVEL2_CERT = 0x04, + TPMD_DT_LEVEL3_CERT = 0x05 + }; + std::string getCert(cert_type type); + std::string challenge(std::string rnd); +}; + +#endif // __lib_base_etpm_h diff --git a/lib/base/filepush.cpp b/lib/base/filepush.cpp index 91f24ba0..e3e2a13a 100644 --- a/lib/base/filepush.cpp +++ b/lib/base/filepush.cpp @@ -29,7 +29,7 @@ void eFilePushThread::thread() { setIoPrio(prio_class, prio); - off_t dest_pos = 0, source_pos = 0; + off_t dest_pos = 0; size_t bytes_read = 0; off_t current_span_offset = 0; @@ -46,9 +46,7 @@ void eFilePushThread::thread() sigaction(SIGUSR1, &act, 0); hasStarted(); - - source_pos = m_raw_source.lseek(0, SEEK_CUR); - + /* m_stop must be evaluated after each syscall. */ while (!m_stop) { @@ -137,14 +135,12 @@ void eFilePushThread::thread() if (m_sg && !current_span_remaining) { - m_sg->getNextSourceSpan(source_pos, bytes_read, current_span_offset, current_span_remaining); + m_sg->getNextSourceSpan(m_current_position, bytes_read, current_span_offset, current_span_remaining); ASSERT(!(current_span_remaining % m_blocksize)); - - if (source_pos != current_span_offset) - source_pos = m_raw_source.lseek(current_span_offset, SEEK_SET); + m_current_position = current_span_offset; bytes_read = 0; } - + size_t maxread = sizeof(m_buffer); /* if we have a source span, don't read past the end */ @@ -157,9 +153,9 @@ void eFilePushThread::thread() m_buf_start = 0; m_filter_end = 0; m_buf_end = 0; - + if (maxread) - m_buf_end = m_raw_source.read(m_buffer, maxread); + m_buf_end = m_source->read(m_current_position, m_buffer, maxread); if (m_buf_end < 0) { @@ -177,10 +173,7 @@ void eFilePushThread::thread() /* a read might be mis-aligned in case of a short read. */ int d = m_buf_end % m_blocksize; if (d) - { - m_raw_source.lseek(-d, SEEK_CUR); m_buf_end -= d; - } if (m_buf_end == 0) { @@ -216,18 +209,10 @@ void eFilePushThread::thread() sleep(1); continue; } -#if 0 - eDebug("FILEPUSH: end-of-file! (currently unhandled)"); - if (!m_raw_source.lseek(0, SEEK_SET)) - { - eDebug("(looping)"); - continue; - } -#endif break; } else { - source_pos += m_buf_end; + m_current_position += m_buf_end; bytes_read += m_buf_end; if (m_sg) current_span_remaining -= m_buf_end; @@ -239,20 +224,30 @@ void eFilePushThread::thread() eDebug("FILEPUSH THREAD STOP"); } -void eFilePushThread::start(int fd_source, int fd_dest) +void eFilePushThread::start(int fd, int fd_dest) { - m_raw_source.setfd(fd_source); - m_fd_dest = fd_dest; - resume(); + eRawFile *f = new eRawFile(); + ePtr<iTsSource> source = f; + f->setfd(fd); + start(source, fd_dest); } -int eFilePushThread::start(const char *filename, int fd_dest) +int eFilePushThread::start(const char *file, int fd_dest) { - if (m_raw_source.open(filename) < 0) + eRawFile *f = new eRawFile(); + ePtr<iTsSource> source = f; + if (f->open(file) < 0) return -1; + start(source, fd_dest); + return 0; +} + +void eFilePushThread::start(ePtr<iTsSource> &source, int fd_dest) +{ + m_source = source; m_fd_dest = fd_dest; + m_current_position = 0; resume(); - return 0; } void eFilePushThread::stop() @@ -273,11 +268,6 @@ void eFilePushThread::pause() stop(); } -void eFilePushThread::seek(int whence, off_t where) -{ - m_raw_source.lseek(where, whence); -} - void eFilePushThread::resume() { m_stop = 0; diff --git a/lib/base/filepush.h b/lib/base/filepush.h index 71ee9979..a4457f67 100644 --- a/lib/base/filepush.h +++ b/lib/base/filepush.h @@ -24,9 +24,10 @@ public: void stop(); void start(int sourcefd, int destfd); int start(const char *filename, int destfd); - + + void start(ePtr<iTsSource> &source, int destfd); + void pause(); - void seek(int whence, off_t where); void resume(); /* flushes the internal readbuffer */ @@ -57,11 +58,12 @@ private: int m_send_pvr_commit; int m_stream_mode; int m_blocksize; + off_t m_current_position; + + ePtr<iTsSource> m_source; - eRawFile m_raw_source; - eFixedMessagePump<int> m_messagepump; - + void recvEvent(const int &evt); }; diff --git a/lib/base/itssource.h b/lib/base/itssource.h new file mode 100644 index 00000000..91167ff5 --- /dev/null +++ b/lib/base/itssource.h @@ -0,0 +1,19 @@ +#ifndef __lib_base_idatasource_h +#define __lib_base_idatasource_h + +#include <lib/base/object.h> + +class iTsSource: public iObject +{ +public: + /* NOTE: should only be used to get current position or filelength */ + virtual off_t lseek(off_t offset, int whence)=0; + + /* NOTE: you must be able to handle short reads! */ + virtual ssize_t read(off_t offset, void *buf, size_t count)=0; /* NOTE: this is what you in normal case have to use!! */ + + virtual off_t length()=0; + virtual int valid()=0; +}; + +#endif diff --git a/lib/base/rawfile.cpp b/lib/base/rawfile.cpp index c7e11feb..3a09e07e 100644 --- a/lib/base/rawfile.cpp +++ b/lib/base/rawfile.cpp @@ -4,7 +4,10 @@ #include <lib/base/rawfile.h> #include <lib/base/eerror.h> +DEFINE_REF(eRawFile); + eRawFile::eRawFile() + :m_lock(false) { m_fd = -1; m_file = 0; @@ -53,6 +56,13 @@ void eRawFile::setfd(int fd) off_t eRawFile::lseek(off_t offset, int whence) { + eSingleLocker l(m_lock); + m_current_offset = lseek_internal(offset, whence); + return m_current_offset; +} + +off_t eRawFile::lseek_internal(off_t offset, int whence) +{ // eDebug("lseek: %lld, %d", offset, whence); /* if there is only one file, use the native lseek - the file could be growing! */ if (m_nrfiles < 2) @@ -61,7 +71,8 @@ off_t eRawFile::lseek(off_t offset, int whence) return ::lseek(m_fd, offset, whence); else { - ::fseeko(m_file, offset, whence); + if (::fseeko(m_file, offset, whence) < 0) + perror("fseeko"); return ::ftello(m_file); } } @@ -100,11 +111,19 @@ int eRawFile::close() } } -ssize_t eRawFile::read(void *buf, size_t count) +ssize_t eRawFile::read(off_t offset, void *buf, size_t count) { -// eDebug("read: %p, %d", buf, count); + eSingleLocker l(m_lock); + + if (offset != m_current_offset) + { + m_current_offset = lseek_internal(offset, SEEK_SET); + if (m_current_offset < 0) + return m_current_offset; + } + switchOffset(m_current_offset); - + if (m_nrfiles >= 2) { if (m_current_offset + count > m_totallength) diff --git a/lib/base/rawfile.h b/lib/base/rawfile.h index a1c73d6a..7b736a33 100644 --- a/lib/base/rawfile.h +++ b/lib/base/rawfile.h @@ -2,24 +2,27 @@ #define __lib_base_rawfile_h #include <string> +#include <lib/base/itssource.h> -class eRawFile +class eRawFile: public iTsSource { + DECLARE_REF(eRawFile); + eSingleLock m_lock; public: eRawFile(); ~eRawFile(); - int open(const char *filename, int cached = 0); void setfd(int fd); - off_t lseek(off_t offset, int whence); int close(); - ssize_t read(void *buf, size_t count); /* NOTE: you must be able to handle short reads! */ + + // iTsSource + off_t lseek(off_t offset, int whence); + ssize_t read(off_t offset, void *buf, size_t count); off_t length(); int valid(); private: int m_fd; /* for uncached */ FILE *m_file; /* for cached */ - int m_cached; std::string m_basename; off_t m_splitsize, m_totallength, m_current_offset, m_base_offset, m_last_offset; @@ -27,6 +30,8 @@ private: void scan(); int m_current_file; int switchOffset(off_t off); + + off_t lseek_internal(off_t offset, int whence); FILE *openFileCached(int nr); int openFileUncached(int nr); }; diff --git a/lib/base/thread.cpp b/lib/base/thread.cpp index fa923deb..1fda6a47 100644 --- a/lib/base/thread.cpp +++ b/lib/base/thread.cpp @@ -58,7 +58,12 @@ int eThread::runAsync(int prio, int policy) pthread_attr_setschedpolicy(&attr, policy); pthread_attr_setschedparam(&attr, &p); } - + + if (the_thread) { + eDebug("old thread joined %d", pthread_join(the_thread, 0)); + the_thread = 0; + } + if (pthread_create(&the_thread, &attr, wrapper, this)) { pthread_attr_destroy(&attr); @@ -69,7 +74,7 @@ int eThread::runAsync(int prio, int policy) pthread_attr_destroy(&attr); return 0; -} +} int eThread::run(int prio, int policy) { diff --git a/lib/components/Makefile.am b/lib/components/Makefile.am index e377ab85..364c6945 100644 --- a/lib/components/Makefile.am +++ b/lib/components/Makefile.am @@ -1,6 +1,16 @@ -INCLUDES = \ - -I$(top_srcdir)/include +AM_CPPFLAGS = \ + -I$(top_srcdir) \ + -I$(top_srcdir)/include \ + -include Python.h \ + -include $(top_builddir)/enigma2_config.h noinst_LIBRARIES = libenigma_components.a -libenigma_components_a_SOURCES = scan.cpp file_eraser.cpp +libenigma_components_a_SOURCES = \ + file_eraser.cpp \ + scan.cpp + +componentsincludedir = $(pkgincludedir)/lib/components +componentsinclude_HEADERS = \ + file_eraser.h \ + scan.h diff --git a/lib/driver/Makefile.am b/lib/driver/Makefile.am index de1271b6..b498a6c8 100644 --- a/lib/driver/Makefile.am +++ b/lib/driver/Makefile.am @@ -1,7 +1,38 @@ -INCLUDES = \ - -I$(top_srcdir)/include +AM_CPPFLAGS = \ + -I$(top_srcdir) \ + -I$(top_srcdir)/include \ + -include Python.h \ + -include $(top_builddir)/enigma2_config.h + +AM_CXXFLAGS = \ + $(LIBSDL_CFLAGS) noinst_LIBRARIES = libenigma_driver.a libenigma_driver_a_SOURCES = \ - rc.cpp rcinput.cpp rcconsole.cpp avswitch.cpp rfmod.cpp etimezone.cpp misc_options.cpp + avswitch.cpp \ + etimezone.cpp \ + misc_options.cpp \ + rc.cpp \ + rcconsole.cpp \ + rcinput.cpp \ + rfmod.cpp + +driverincludedir = $(pkgincludedir)/lib/driver +driverinclude_HEADERS = \ + avswitch.h \ + etimezone.h \ + input_fake.h \ + misc_options.h \ + rc.h \ + rcconsole.h \ + rcdbox.h \ + rcdreambox2.h \ + rcinput.h \ + rfmod.h + +if HAVE_LIBSDL +libenigma_driver_a_SOURCES += \ + rcsdl.cpp \ + rcsdl.h +endif diff --git a/lib/driver/rcconsole.cpp b/lib/driver/rcconsole.cpp index eb5aee3d..77f43227 100644 --- a/lib/driver/rcconsole.cpp +++ b/lib/driver/rcconsole.cpp @@ -5,7 +5,7 @@ #include <stdio.h> #include <fcntl.h> -eRCConsoleDriver::eRCConsoleDriver(const char *filename): eRCDriver(eRCInput::getInstance()) +eRCConsoleDriver::eRCConsoleDriver(const char *filename): eRCDriver(eRCInput::getInstance()), m_escape(false) { handle=open(filename, O_RDONLY|O_NONBLOCK); if (handle<0) @@ -38,7 +38,7 @@ void eRCConsoleDriver::keyPressed(int) unsigned char data[16]; unsigned char *d = data; int num = read(handle, data, 16); - int code=-1; + unsigned char code; int km = input->getKeyboardMode(); @@ -47,42 +47,29 @@ void eRCConsoleDriver::keyPressed(int) while (num--) { -// eDebug("console code %08x\n", *d); - if (km == eRCInput::kmAll) - code = *d++; - else - { - if (*d == 27) // escape code - { - while (num) - { - num--; - if (*++d != '[') - break; - } - code = -1; - } else - code = *d; - ++d; - - if (code < 32) /* control characters */ - code = -1; - else switch(code) - { - case 0x7E: // mute, einfg, entf - case 0x7F: // backspace - code = -1; - default: - break; + code = *d++; +// eDebug("console code %02x\n", code); + if (km == eRCInput::kmAscii) { + if (m_escape) { + if (code != '[') + m_escape = false; + continue; } + + if (code == 27) // escape code + m_escape = true; + + if ((code < 32) || // control characters + (code == 0x7e) || // mute, einfg, entf + (code == 0x7f)) // backspace + continue; } - if (code != -1) - for (std::list<eRCDevice*>::iterator i(listeners.begin()); i!=listeners.end(); ++i) - { -// eDebug("ascii %08x", code); - (*i)->handleCode(code); - } + for (std::list<eRCDevice*>::iterator i(listeners.begin()); i!=listeners.end(); ++i) + { +// eDebug("ascii %02x", code); + (*i)->handleCode(code); + } } } @@ -116,7 +103,7 @@ class eRCConsoleInit eRCConsoleDriver driver; eRCConsole device; public: - eRCConsoleInit(): driver("/dev/vc/0"), device(&driver) + eRCConsoleInit(): driver("/dev/tty0"), device(&driver) { } }; diff --git a/lib/driver/rcconsole.h b/lib/driver/rcconsole.h index 85234d5b..8a24612c 100644 --- a/lib/driver/rcconsole.h +++ b/lib/driver/rcconsole.h @@ -10,6 +10,7 @@ class eRCConsoleDriver: public eRCDriver protected: int handle; ePtr<eSocketNotifier> sn; + bool m_escape; void keyPressed(int); public: eRCConsoleDriver(const char *filename); diff --git a/lib/driver/rcinput.cpp b/lib/driver/rcinput.cpp index 2bfeefa1..f30ba8cb 100644 --- a/lib/driver/rcinput.cpp +++ b/lib/driver/rcinput.cpp @@ -73,16 +73,8 @@ void eRCDeviceInputDev::handleCode(long rccode) eRCDeviceInputDev::eRCDeviceInputDev(eRCInputEventDriver *driver) :eRCDevice(driver->getDeviceName(), driver), iskeyboard(false) { - int len=id.length(); - int idx=0; - while(idx <= len-8) - { - if (!strncasecmp(&id[idx++], "KEYBOARD", 8)) - { - iskeyboard=true; - break; - } - } + if (strcasestr(id.c_str(), "keyboard") != NULL) + iskeyboard = true; setExclusive(true); eDebug("Input device \"%s\" is %sa keyboard.", id.c_str(), iskeyboard ? "" : "not "); } diff --git a/lib/driver/rcsdl.cpp b/lib/driver/rcsdl.cpp new file mode 100644 index 00000000..0705cb0f --- /dev/null +++ b/lib/driver/rcsdl.cpp @@ -0,0 +1,392 @@ +#include <lib/base/etrace.h> +#include <lib/driver/rcsdl.h> +//#include <lib/actions/action.h> +#include <lib/base/init.h> +#include <lib/base/init_num.h> +#include <lib/driver/input_fake.h> + +/* + * eSDLInputDevice + */ + +eSDLInputDevice::eSDLInputDevice(eRCDriver *driver) : eRCDevice("SDL", driver), m_escape(false), m_unicode(0) +{ +} + +eSDLInputDevice::~eSDLInputDevice() +{ +} + +void eSDLInputDevice::handleCode(long arg) +{ + D_ENTER(); + + const SDL_KeyboardEvent *event = (const SDL_KeyboardEvent *)arg; + const SDL_keysym *key = &event->keysym; + int km = input->getKeyboardMode(); + int code, flags; + + if (event->type == SDL_KEYDOWN) { + m_unicode = key->unicode; + flags = eRCKey::flagMake; + } else { + flags = eRCKey::flagBreak; + } + + if (km == eRCInput::kmNone) { + code = translateKey(key->sym); + D_PRINT("translated code: %d", code); + } else { + code = m_unicode; + D_PRINT("native virtual code: %d / sym: %d", code, key->sym); + if ((code == 0) && (key->sym < 128)) { + code = key->sym; + D_PRINT("ASCII code: %u", code); + } + + if ((km == eRCInput::kmAscii) && + ((code < SDLK_SPACE) || + (code == 0x7e) || + (code == SDLK_DELETE) || + (code > 255))) { + code = translateKey(key->sym); + } else { + // ASCII keys should only generate key press events + if (flags == eRCKey::flagBreak) + D_RETURN(); + + if (km == eRCInput::kmAscii) { + // skip ESC c or ESC '[' c + if (m_escape) { + if (code != '[') + m_escape = false; + D_RETURN(); + } + if (code == SDLK_ESCAPE) + m_escape = true; + } + flags |= eRCKey::flagAscii; + } + } + + D_PRINT("code=%d (%#x) flags=%d (%#x)", code, code, flags, flags); + input->keyPressed(eRCKey(this, code, flags)); + D_RETURN(); +} + +const char *eSDLInputDevice::getDescription() const +{ + return "SDL"; +} + +int eSDLInputDevice::translateKey(SDLKey key) +{ + #define P(a) case SDLK_##a: return KEY_##a + #define P2(a,b) case SDLK_##a: return KEY_##b + + switch (key) { + P(BACKSPACE); + P(TAB); + P(CLEAR); + P2(RETURN,ENTER); + P(PAUSE); + P2(ESCAPE,ESC); + P(SPACE); +#if 0 + P(EXCLAIM); + P(QUOTEDBL); + P(HASH); +#endif + P(DOLLAR); +#if 0 + P(AMPERSAND); +#endif + P2(QUOTE,APOSTROPHE); +#if 0 + P(LEFTPAREN); + P(RIGHTPAREN); + P(ASTERISK); + P(PLUS); +#endif + P(COMMA); + P(MINUS); + P2(PERIOD,DOT); + P(SLASH); + P(0); + P(1); + P(2); + P(3); + P(4); + P(5); + P(6); + P(7); + P(8); + P(9); +#if 0 + P(COLON); +#endif + P(SEMICOLON); +#if 0 + P(LESS); +#endif + P2(EQUALS,EQUAL); +#if 0 + P(GREATER); +#endif + P(QUESTION); +#if 0 + P(AT); +#endif + P2(LEFTBRACKET,LEFTBRACE); + P(BACKSLASH); + P2(RIGHTBRACKET,RIGHTBRACE); + P2(CARET,GRAVE); +#if 0 + P(UNDERSCORE); + P(BACKQUOTE); +#endif + P2(a,A); + P2(b,B); + P2(c,C); + P2(d,D); + P2(e,E); + P2(f,F); + P2(g,G); + P2(h,H); + P2(i,I); + P2(j,J); + P2(k,K); + P2(l,L); + P2(m,M); + P2(n,N); + P2(o,O); + P2(p,P); + P2(q,Q); + P2(r,R); + P2(s,S); + P2(t,T); + P2(u,U); + P2(v,V); + P2(w,W); + P2(x,X); + P2(y,Y); + P2(z,Z); + P(DELETE); +#if 0 + P(WORLD_0); + P(WORLD_1); + P(WORLD_2); + P(WORLD_3); + P(WORLD_4); + P(WORLD_5); + P(WORLD_6); + P(WORLD_7); + P(WORLD_8); + P(WORLD_9); + P(WORLD_10); + P(WORLD_11); + P(WORLD_12); + P(WORLD_13); + P(WORLD_14); + P(WORLD_15); + P(WORLD_16); + P(WORLD_17); + P(WORLD_18); + P(WORLD_19); + P(WORLD_20); + P(WORLD_21); + P(WORLD_22); + P(WORLD_23); + P(WORLD_24); + P(WORLD_25); + P(WORLD_26); + P(WORLD_27); + P(WORLD_28); + P(WORLD_29); + P(WORLD_30); + P(WORLD_31); + P(WORLD_32); + P(WORLD_33); + P(WORLD_34); + P(WORLD_35); + P(WORLD_36); + P(WORLD_37); + P(WORLD_38); + P(WORLD_39); + P(WORLD_40); + P(WORLD_41); + P(WORLD_42); + P(WORLD_43); + P(WORLD_44); + P(WORLD_45); + P(WORLD_46); + P(WORLD_47); + P(WORLD_48); + P(WORLD_49); + P(WORLD_50); + P(WORLD_51); + P(WORLD_52); + P(WORLD_53); + P(WORLD_54); + P(WORLD_55); + P(WORLD_56); + P(WORLD_57); + P(WORLD_58); + P(WORLD_59); + P(WORLD_60); + P(WORLD_61); + P(WORLD_62); + P(WORLD_63); + P(WORLD_64); + P(WORLD_65); + P(WORLD_66); + P(WORLD_67); + P(WORLD_68); + P(WORLD_69); + P(WORLD_70); + P(WORLD_71); + P(WORLD_72); + P(WORLD_73); + P(WORLD_74); + P(WORLD_75); + P(WORLD_76); + P(WORLD_77); + P(WORLD_78); + P(WORLD_79); + P(WORLD_80); + P(WORLD_81); + P(WORLD_82); + P(WORLD_83); + P(WORLD_84); + P(WORLD_85); + P(WORLD_86); + P(WORLD_87); + P(WORLD_88); + P(WORLD_89); + P(WORLD_90); + P(WORLD_91); + P(WORLD_92); + P(WORLD_93); + P(WORLD_94); + P(WORLD_95); +#endif + P(KP0); + P(KP1); + P(KP2); + P(KP3); + P(KP4); + P(KP5); + P(KP6); + P(KP7); + P(KP8); + P(KP9); + P2(KP_PERIOD,KPDOT); + P2(KP_DIVIDE,KPSLASH); + P2(KP_MULTIPLY,KPASTERISK); + P2(KP_MINUS,KPMINUS); + P2(KP_PLUS,KPPLUS); + P2(KP_ENTER,KPENTER); + P2(KP_EQUALS,KPEQUAL); + P(UP); + P(DOWN); + P(RIGHT); + P(LEFT); + P(INSERT); + P(HOME); + P(END); + P(PAGEUP); + P(PAGEDOWN); + P(F1); + P(F2); + P(F3); + P(F4); + P(F5); + P(F6); + P(F7); + P(F8); + P(F9); + P(F10); + P(F11); + P(F12); + P(F13); + P(F14); + P(F15); + P(NUMLOCK); + P(CAPSLOCK); + P2(SCROLLOCK,SCROLLLOCK); + P2(RSHIFT,RIGHTSHIFT); + P2(LSHIFT,LEFTSHIFT); + P2(RCTRL,RIGHTCTRL); + P2(LCTRL,LEFTCTRL); + P2(RALT,RIGHTALT); + P2(LALT,LEFTALT); + P2(RMETA,RIGHTMETA); + P2(LMETA,LEFTMETA); +#if 0 + P(LSUPER); + P(RSUPER); +#endif + P(MODE); + P(COMPOSE); + P(HELP); + P(PRINT); + P2(SYSREQ,SYSRQ); + P(BREAK); + P(MENU); + P(POWER); + P(EURO); + P(UNDO); + default: + eDebug("unhandled SDL keycode: %d", key); + return KEY_RESERVED; + } + + #undef P2 + #undef P +} + +/* + * eSDLInputDriver + */ + +eSDLInputDriver *eSDLInputDriver::instance; + +eSDLInputDriver::eSDLInputDriver() : eRCDriver(eRCInput::getInstance()) +{ + ASSERT(instance == 0); + instance = this; +} + +eSDLInputDriver::~eSDLInputDriver() +{ + instance = 0; +} + +void eSDLInputDriver::keyPressed(const SDL_KeyboardEvent *key) +{ + eDebug("km=%d enabled=%d locked=%d", + input->getKeyboardMode(), enabled, input->islocked()); + + if (!enabled || input->islocked()) + return; + + std::list<eRCDevice*>::iterator i(listeners.begin()); + while (i != listeners.end()) { + (*i)->handleCode((long)key); + ++i; + } +} + +class eRCSDLInit +{ +private: + eSDLInputDriver driver; + eSDLInputDevice device; + +public: + eRCSDLInit(): driver(), device(&driver) + { + } +}; + +eAutoInitP0<eRCSDLInit> init_rcSDL(eAutoInitNumbers::rc+1, "SDL RC Driver"); diff --git a/lib/driver/rcsdl.h b/lib/driver/rcsdl.h new file mode 100644 index 00000000..9732f5e4 --- /dev/null +++ b/lib/driver/rcsdl.h @@ -0,0 +1,37 @@ +#ifndef __lib_driver_rcsdl_h +#define __lib_driver_rcsdl_h + +#include <lib/driver/rc.h> + +#include <SDL.h> + +class eSDLInputDevice : public eRCDevice +{ +private: + bool m_escape; + unsigned int m_unicode; + int translateKey(SDLKey key); + +public: + eSDLInputDevice(eRCDriver *driver); + ~eSDLInputDevice(); + + virtual void handleCode(long arg); + virtual const char *getDescription() const; +}; + +class eSDLInputDriver : public eRCDriver +{ +private: + static eSDLInputDriver *instance; + +public: + eSDLInputDriver(); + ~eSDLInputDriver(); + + static eSDLInputDriver *getInstance() { return instance; } + + void keyPressed(const SDL_KeyboardEvent *key); +}; + +#endif diff --git a/lib/dvb/Makefile.am b/lib/dvb/Makefile.am index b47ffed9..9b337556 100644 --- a/lib/dvb/Makefile.am +++ b/lib/dvb/Makefile.am @@ -1,12 +1,64 @@ SUBDIRS = lowlevel -INCLUDES = \ - -I$(top_srcdir)/include +AM_CPPFLAGS = \ + -I$(top_srcdir) \ + -I$(top_srcdir)/include \ + -include Python.h \ + -include $(top_builddir)/enigma2_config.h noinst_LIBRARIES = libenigma_dvb.a -libenigma_dvb_a_SOURCES = dvb.cpp demux.cpp frontend.cpp esection.cpp db.cpp \ - sec.cpp scan.cpp crc32.cpp pmt.cpp decoder.cpp eit.cpp rotor_calc.cpp \ - epgcache.cpp dvbtime.cpp metaparser.cpp volume.cpp tstools.cpp pvrparse.cpp \ - pesparse.cpp teletext.cpp radiotext.cpp subtitle.cpp +libenigma_dvb_a_SOURCES = \ + crc32.cpp \ + db.cpp \ + decoder.cpp \ + demux.cpp \ + dvb.cpp \ + dvbtime.cpp \ + eit.cpp \ + epgcache.cpp \ + esection.cpp \ + frontend.cpp \ + metaparser.cpp \ + pesparse.cpp \ + pmt.cpp \ + pvrparse.cpp \ + radiotext.cpp \ + rotor_calc.cpp \ + scan.cpp \ + sec.cpp \ + subtitle.cpp \ + teletext.cpp \ + tstools.cpp \ + volume.cpp +dvbincludedir = $(pkgincludedir)/lib/dvb +dvbinclude_HEADERS = \ + crc32.h \ + db.h \ + decoder.h \ + demux.h \ + dvb.h \ + dvbtime.h \ + eit.h \ + epgcache.h \ + esection.h \ + frontend.h \ + frontendparms.h \ + idemux.h \ + idvb.h \ + isection.h \ + list.h \ + metaparser.h \ + pesparse.h \ + pmt.h \ + pvrparse.h \ + radiotext.h \ + rotor_calc.h \ + scan.h \ + sec.h \ + specs.h \ + subtitle.h \ + teletext.h \ + tstools.h \ + volume.h diff --git a/lib/dvb/db.cpp b/lib/dvb/db.cpp index 38be6359..05474071 100644 --- a/lib/dvb/db.cpp +++ b/lib/dvb/db.cpp @@ -3,6 +3,7 @@ #include <lib/dvb/dvb.h> #include <lib/dvb/frontend.h> #include <lib/dvb/epgcache.h> +#include <lib/base/eenv.h> #include <lib/base/eerror.h> #include <lib/base/estring.h> #include <xmlccwrap/xmlccwrap.h> @@ -73,7 +74,7 @@ RESULT eBouquet::moveService(const eServiceReference &ref, unsigned int pos) RESULT eBouquet::flushChanges() { - FILE *f=fopen((CONFIGDIR"/enigma2/"+m_filename).c_str(), "w"); + FILE *f=fopen(eEnv::resolve("${sysconfdir}/enigma2/" + m_filename).c_str(), "w"); if (!f) return -1; if ( fprintf(f, "#NAME %s\r\n", m_bouquet_name.c_str()) < 0 ) @@ -280,7 +281,7 @@ DEFINE_REF(eDVBDB); void eDVBDB::reloadServicelist() { - loadServicelist(CONFIGDIR"/enigma2/lamedb"); + loadServicelist(eEnv::resolve("${sysconfdir}/enigma2/lamedb").c_str()); } void eDVBDB::parseServiceData(ePtr<eDVBService> s, std::string str) @@ -324,19 +325,11 @@ void eDVBDB::loadServicelist(const char *file) { eDebug("---- opening lame channel db"); FILE *f=fopen(file, "rt"); - if (!f && strcmp(file, CONFIGDIR"/enigma2/lamedb") == 0) - { - struct stat s; - if ( !stat("lamedb", &s) ) - { - if ( !stat(CONFIGDIR"/enigma2", &s) ) - { - rename("lamedb", CONFIGDIR"/enigma2/lamedb" ); - reloadServicelist(); - } - } + if (!f) { + eDebug("can't open %s: %m", file); return; } + char line[256]; int version=3; if ((!fgets(line, 256, f)) || sscanf(line, "eDVB services /%d/", &version) != 1) @@ -594,7 +587,7 @@ void eDVBDB::saveServicelist(const char *file) void eDVBDB::saveServicelist() { - saveServicelist(CONFIGDIR"/enigma2/lamedb"); + saveServicelist(eEnv::resolve("${sysconfdir}/enigma2/lamedb").c_str()); } void eDVBDB::loadBouquet(const char *path) @@ -618,28 +611,20 @@ void eDVBDB::loadBouquet(const char *path) std::list<eServiceReference> &list = bouquet.m_services; list.clear(); - std::string p = CONFIGDIR"/enigma2/"; + std::string p = eEnv::resolve("${sysconfdir}/enigma2/"); p+=path; eDebug("loading bouquet... %s", p.c_str()); FILE *fp=fopen(p.c_str(), "rt"); - int entries=0; if (!fp) { - struct stat s; - if ( !stat(path, &s) ) - { - rename(path, p.c_str() ); - loadBouquet(path); - return; - } - eDebug("failed to open."); - if ( strstr(path, "bouquets.tv") ) + eDebug("can't open %s: %m", p.c_str()); + if (!strcmp(path, "bouquets.tv")) { eDebug("recreate bouquets.tv"); bouquet.m_bouquet_name="Bouquets (TV)"; bouquet.flushChanges(); } - else if ( strstr(path, "bouquets.radio") ) + else if (!strcmp(path, "bouquets.radio")) { eDebug("recreate bouquets.radio"); bouquet.m_bouquet_name="Bouquets (Radio)"; @@ -647,6 +632,7 @@ void eDVBDB::loadBouquet(const char *path) } return; } + int entries=0; char line[256]; bool read_descr=false; eServiceReference *e = NULL; diff --git a/lib/dvb/decoder.cpp b/lib/dvb/decoder.cpp index ef8dadc3..a89f72bb 100644 --- a/lib/dvb/decoder.cpp +++ b/lib/dvb/decoder.cpp @@ -203,6 +203,9 @@ int eDVBAudio::startPid(int pid, int type) case aLPCM: bypass = 6; break; + case aDTSHD: + bypass = 0x10; + break; } eDebugNoNewLine("AUDIO_SET_BYPASS(%d) - ", bypass); @@ -601,7 +604,7 @@ RESULT eDVBVideo::connectEvent(const Slot1<void, struct iTSMPEGDecoder::videoEve return 0; } -static int readMpegProc(char *str, int decoder) +static int readMpegProc(const char *str, int decoder) { int val = -1; char tmp[64]; @@ -1299,9 +1302,10 @@ RESULT eTSMPEGDecoder::showSinglePic(const char *filename) if (f >= 0) { struct stat s; + size_t written=0; fstat(f, &s); if (m_video_clip_fd == -1) - m_video_clip_fd = open("/dev/dvb/adapter0/video0", O_WRONLY|O_NONBLOCK); + m_video_clip_fd = open("/dev/dvb/adapter0/video0", O_WRONLY); if (m_video_clip_fd >= 0) { bool seq_end_avail = false; diff --git a/lib/dvb/decoder.h b/lib/dvb/decoder.h index 3a0fbac1..7610b654 100644 --- a/lib/dvb/decoder.h +++ b/lib/dvb/decoder.h @@ -13,7 +13,7 @@ private: ePtr<eDVBDemux> m_demux; int m_fd, m_fd_demux, m_dev, m_is_freezed; public: - enum { aMPEG, aAC3, aDTS, aAAC, aAACHE, aLPCM }; + enum { aMPEG, aAC3, aDTS, aAAC, aAACHE, aLPCM, aDTSHD }; eDVBAudio(eDVBDemux *demux, int dev); enum { aMonoLeft, aStereo, aMonoRight }; void setChannel(int channel); diff --git a/lib/dvb/demux.cpp b/lib/dvb/demux.cpp index 4fba8fa8..f4d86185 100644 --- a/lib/dvb/demux.cpp +++ b/lib/dvb/demux.cpp @@ -85,6 +85,13 @@ int eDVBDemux::openDemux(void) return ::open(filename, O_RDWR); } +int eDVBDemux::openDVR(int flags) +{ + char filename[128]; + snprintf(filename, 128, "/dev/dvb/adapter%d/dvr%d", adapter, demux); + return ::open(filename, flags); +} + DEFINE_REF(eDVBDemux) RESULT eDVBDemux::setSourceFrontend(int fenum) @@ -247,6 +254,14 @@ eDVBSectionReader::~eDVBSectionReader() ::close(fd); } +RESULT eDVBSectionReader::setBufferSize(int size) +{ + int res=::ioctl(fd, DMX_SET_BUFFER_SIZE, size); + if (res < 0) + eDebug("eDVBSectionReader DMX_SET_BUFFER_SIZE failed(%m)"); + return res; +} + RESULT eDVBSectionReader::start(const eDVBSectionFilterMask &mask) { RESULT res; @@ -279,8 +294,7 @@ RESULT eDVBSectionReader::start(const eDVBSectionFilterMask &mask) memcpy(sct.filter.mask, mask.mask, DMX_FILTER_SIZE); #if HAVE_DVB_API_VERSION >= 3 memcpy(sct.filter.mode, mask.mode, DMX_FILTER_SIZE); - if (::ioctl(fd, DMX_SET_BUFFER_SIZE, 8192*8) < 0) - eDebug("DMX_SET_BUFFER_SIZE failed(%m)"); + setBufferSize(8192*8); #endif res = ::ioctl(fd, DMX_SET_FILTER, &sct); @@ -352,7 +366,7 @@ eDVBPESReader::eDVBPESReader(eDVBDemux *demux, eMainloop *context, RESULT &res): if (m_fd >= 0) { - ::ioctl(m_fd, DMX_SET_BUFFER_SIZE, 64*1024); + setBufferSize(64*1024); ::fcntl(m_fd, F_SETFL, O_NONBLOCK); m_notifier = eSocketNotifier::create(context, m_fd, eSocketNotifier::Read, false); CONNECT(m_notifier->activated, eDVBPESReader::data); @@ -364,6 +378,14 @@ eDVBPESReader::eDVBPESReader(eDVBDemux *demux, eMainloop *context, RESULT &res): } } +RESULT eDVBPESReader::setBufferSize(int size) +{ + int res = ::ioctl(m_fd, DMX_SET_BUFFER_SIZE, size); + if (res < 0) + eDebug("eDVBPESReader DMX_SET_BUFFER_SIZE failed(%m)"); + return res; +} + DEFINE_REF(eDVBPESReader) eDVBPESReader::~eDVBPESReader() @@ -484,7 +506,7 @@ eDVBTSRecorder::eDVBTSRecorder(eDVBDemux *demux): m_demux(demux) m_running = 0; m_target_fd = -1; m_thread = new eDVBRecordFileThread(); - CONNECT(m_thread->m_event, eDVBTSRecorder::filepushEvent); + CONNECT(m_thread->m_event, eDVBTSRecorder::filepushEvent); #ifndef HAVE_ADD_PID m_demux->m_dvr_busy = 1; #endif @@ -536,8 +558,8 @@ RESULT eDVBTSRecorder::start() eDebug("FAILED to open demux (%s) in ts recoder (%m)", filename); return -3; } - - ::ioctl(m_source_fd, DMX_SET_BUFFER_SIZE, 1024*1024); + + setBufferSize(1024*1024); dmx_pes_filter_params flt; #if HAVE_DVB_API_VERSION > 3 @@ -577,6 +599,14 @@ RESULT eDVBTSRecorder::start() return 0; } +RESULT eDVBTSRecorder::setBufferSize(int size) +{ + int res = ::ioctl(m_source_fd, DMX_SET_BUFFER_SIZE, size); + if (res < 0) + eDebug("eDVBTSRecorder DMX_SET_BUFFER_SIZE failed(%m)"); + return res; +} + RESULT eDVBTSRecorder::addPID(int pid) { if (m_pids.find(pid) != m_pids.end()) @@ -625,18 +655,36 @@ RESULT eDVBTSRecorder::setBoundary(off_t max) RESULT eDVBTSRecorder::stop() { + int state=3; + for (std::map<int,int>::iterator i(m_pids.begin()); i != m_pids.end(); ++i) stopPID(i->first); if (!m_running) return -1; + +#if HAVE_DVB_API_VERSION >= 5 + /* workaround for record thread stop */ + if (::ioctl(m_source_fd, DMX_STOP) < 0) + perror("DMX_STOP"); + else + state &= ~1; + + if (::close(m_source_fd) < 0) + perror("close"); + else + state &= ~2; +#endif + m_thread->stop(); - - close(m_source_fd); + + if (state & 3) + ::close(m_source_fd); + + m_running = 0; m_source_fd = -1; - + m_thread->stopSaveMetaInformation(); - return 0; } diff --git a/lib/dvb/demux.h b/lib/dvb/demux.h index 7a697d49..e73982ec 100644 --- a/lib/dvb/demux.h +++ b/lib/dvb/demux.h @@ -26,7 +26,8 @@ public: RESULT getCADemuxID(uint8_t &id) { id = demux; return 0; } RESULT flush(); RESULT connectEvent(const Slot1<void,int> &event, ePtr<eConnection> &conn); - + int openDVR(int flags); + int getRefCount() { return ref; } private: int adapter, demux, source; @@ -56,9 +57,9 @@ class eDVBSectionReader: public iDVBSectionReader, public Object void data(int); ePtr<eSocketNotifier> notifier; public: - eDVBSectionReader(eDVBDemux *demux, eMainloop *context, RESULT &res); virtual ~eDVBSectionReader(); + RESULT setBufferSize(int size); RESULT start(const eDVBSectionFilterMask &mask); RESULT stop(); RESULT connectRead(const Slot1<void,const __u8*> &read, ePtr<eConnection> &conn); @@ -76,6 +77,7 @@ class eDVBPESReader: public iDVBPESReader, public Object public: eDVBPESReader(eDVBDemux *demux, eMainloop *context, RESULT &res); virtual ~eDVBPESReader(); + RESULT setBufferSize(int size); RESULT start(int pid); RESULT stop(); RESULT connectRead(const Slot2<void,const __u8*, int> &read, ePtr<eConnection> &conn); @@ -90,6 +92,7 @@ public: eDVBTSRecorder(eDVBDemux *demux); ~eDVBTSRecorder(); + RESULT setBufferSize(int size); RESULT start(); RESULT addPID(int pid); RESULT removePID(int pid); diff --git a/lib/dvb/dvb.cpp b/lib/dvb/dvb.cpp index 6eaadb04..0b1bdc2c 100644 --- a/lib/dvb/dvb.cpp +++ b/lib/dvb/dvb.cpp @@ -96,6 +96,10 @@ eDVBResourceManager::eDVBResourceManager() m_boxtype = DM800; else if (!strncmp(tmp, "dm500hd\n", rd)) m_boxtype = DM500HD; + else if (!strncmp(tmp, "dm800se\n", rd)) + m_boxtype = DM800SE; + else if (!strncmp(tmp, "dm7020hd\n", rd)) + m_boxtype = DM7020HD; else { eDebug("boxtype detection via /proc/stb/info not possible... use fallback via demux count!\n"); if (m_demux.size() == 3) @@ -106,7 +110,7 @@ eDVBResourceManager::eDVBResourceManager() m_boxtype = DM8000; } - eDebug("found %d adapter, %d frontends(%d sim) and %d demux, boxtype %d", + eDebug("found %zd adapter, %zd frontends(%zd sim) and %zd demux, boxtype %d", m_adapter.size(), m_frontend.size(), m_simulate_frontend.size(), m_demux.size(), m_boxtype); eDVBCAService::registerChannelCallback(this); @@ -141,19 +145,20 @@ eDVBAdapterLinux::eDVBAdapterLinux(int nr): m_nr(nr) #endif if (stat(filename, &s)) break; - ePtr<eDVBFrontend> fe; + eDVBFrontend *fe; { int ok = 0; - fe = new eDVBFrontend(m_nr, num_fe, ok); + fe = new eDVBFrontend(m_nr, num_fe, ok, true); if (ok) - m_frontend.push_back(fe); + m_simulate_frontend.push_back(ePtr<eDVBFrontend>(fe)); } + { int ok = 0; - fe = new eDVBFrontend(m_nr, num_fe, ok, true); + fe = new eDVBFrontend(m_nr, num_fe, ok, false, fe); if (ok) - m_simulate_frontend.push_back(fe); + m_frontend.push_back(ePtr<eDVBFrontend>(fe)); } ++num_fe; } @@ -318,27 +323,34 @@ PyObject *eDVBResourceManager::setFrontendSlotInformations(ePyObject list) PyErr_SetString(PyExc_StandardError, "eDVBResourceManager::setFrontendSlotInformations argument should be a python list"); return NULL; } - if ((unsigned int)PyList_Size(list) != m_frontend.size()) + unsigned int assigned=0; + for (eSmartPtrList<eDVBRegisteredFrontend>::iterator i(m_frontend.begin()); i != m_frontend.end(); ++i) { + int pos=0; + while (pos < PyList_Size(list)) { + ePyObject obj = PyList_GET_ITEM(list, pos++); + if (!i->m_frontend->setSlotInfo(obj)) + continue; + ++assigned; + break; + } + } + if (assigned != m_frontend.size()) { char blasel[256]; - sprintf(blasel, "eDVBResourceManager::setFrontendSlotInformations list size incorrect %d frontends avail, but %d entries in slotlist", - m_frontend.size(), PyList_Size(list)); + sprintf(blasel, "eDVBResourceManager::setFrontendSlotInformations .. assigned %zd socket informations, but %d registered frontends!", + m_frontend.size(), assigned); PyErr_SetString(PyExc_StandardError, blasel); return NULL; } - int pos=0; - for (eSmartPtrList<eDVBRegisteredFrontend>::iterator i(m_frontend.begin()); i != m_frontend.end(); ++i) - { - ePyObject obj = PyList_GET_ITEM(list, pos++); - if (!i->m_frontend->setSlotInfo(obj)) - return NULL; - } - pos=0; for (eSmartPtrList<eDVBRegisteredFrontend>::iterator i(m_simulate_frontend.begin()); i != m_simulate_frontend.end(); ++i) { - ePyObject obj = PyList_GET_ITEM(list, pos++); - if (!i->m_frontend->setSlotInfo(obj)) - return NULL; + int pos=0; + while (pos < PyList_Size(list)) { + ePyObject obj = PyList_GET_ITEM(list, pos++); + if (!i->m_frontend->setSlotInfo(obj)) + continue; + break; + } } Py_RETURN_NONE; } @@ -455,7 +467,7 @@ RESULT eDVBResourceManager::allocateDemux(eDVBRegisteredFrontend *fe, ePtr<eDVBA ePtr<eDVBRegisteredDemux> unused; - if (m_boxtype == DM800 || m_boxtype == DM500HD) // dm800 / 500hd + if (m_boxtype == DM800) // dm800 { cap |= capHoldDecodeReference; // this is checked in eDVBChannel::getDemux for (; i != m_demux.end(); ++i, ++n) @@ -511,7 +523,7 @@ RESULT eDVBResourceManager::allocateDemux(eDVBRegisteredFrontend *fe, ePtr<eDVBA } } } - else if (m_boxtype == DM8000) + else if (m_boxtype == DM8000 || m_boxtype == DM500HD || m_boxtype == DM800SE || m_boxtype == DM7020HD) { cap |= capHoldDecodeReference; // this is checked in eDVBChannel::getDemux for (; i != m_demux.end(); ++i, ++n) @@ -1324,16 +1336,6 @@ void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off return; } - m_cue->m_lock.RdLock(); - if (!m_cue->m_decoding_demux) - { - start = current_offset; - size = max; - eDebug("getNextSourceSpan, no decoding demux. forcing normal play"); - m_cue->m_lock.Unlock(); - return; - } - if (m_skipmode_n) { eDebug("skipmode %d:%d (x%d)", m_skipmode_m, m_skipmode_n, m_skipmode_frames); @@ -1341,7 +1343,6 @@ void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off } eDebug("getNextSourceSpan, current offset is %08llx, m_skipmode_m = %d!", current_offset, m_skipmode_m); - int frame_skip_success = 0; if (m_skipmode_m) @@ -1386,6 +1387,8 @@ void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off } } + m_cue->m_lock.RdLock(); + while (!m_cue->m_seek_requests.empty()) { std::pair<int, pts_t> seek = m_cue->m_seek_requests.front(); @@ -1410,6 +1413,13 @@ void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off eDebug("decoder getPTS failed, can't seek relative"); continue; } + if (!m_cue->m_decoding_demux) + { + eDebug("getNextSourceSpan, no decoding demux. couldn't seek to %llx... ignore request!", pts); + start = current_offset; + size = max; + continue; + } if (getCurrentPosition(m_cue->m_decoding_demux, now, 1)) { eDebug("seekTo: getCurrentPosition failed!"); @@ -1481,7 +1491,7 @@ void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off size = max; else size = aligned_end - current_offset; - eDebug("HIT, %lld < %lld < %lld, size: %d", i->first, current_offset, i->second, size); + eDebug("HIT, %lld < %lld < %lld, size: %zd", i->first, current_offset, i->second, size); return; } if (current_offset < aligned_start) @@ -1522,10 +1532,10 @@ void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off len = aligned_end - aligned_start; start = aligned_end - len; - eDebug("skipping to %llx, %d", start, len); + eDebug("skipping to %llx, %zd", start, len); } - eDebug("result: %llx, %x (%llx %llx)", start, size, aligned_start, aligned_end); + eDebug("result: %llx, %zx (%llx %llx)", start, size, aligned_start, aligned_end); return; } } @@ -1541,7 +1551,7 @@ void eDVBChannel::getNextSourceSpan(off_t current_offset, size_t bytes_read, off { start = current_offset; size = max; - eDebug("NO CUESHEET. (%08llx, %d)", start, size); + eDebug("NO CUESHEET. (%08llx, %zd)", start, size); } else { start = current_offset; @@ -1659,6 +1669,18 @@ void eDVBChannel::SDTready(int result) m_SDT = 0; } +int eDVBChannel::reserveDemux() +{ + ePtr<iDVBDemux> dmx; + if (!getDemux(dmx, 0)) + { + uint8_t id; + if (!dmx->getCADemuxID(id)) + return id; + } + return -1; +} + RESULT eDVBChannel::requestTsidOnid(ePyObject callback) { if (PyCallable_Check(callback)) @@ -1733,6 +1755,20 @@ RESULT eDVBChannel::getCurrentFrontendParameters(ePtr<iDVBFrontendParameters> &p RESULT eDVBChannel::playFile(const char *file) { + eRawFile *f = new eRawFile(); + ePtr<iTsSource> source = f; + + if (f->open(file) < 0) + { + eDebug("can't open PVR file %s (%m)", file); + return -ENOENT; + } + + return playSource(source, file); +} + +RESULT eDVBChannel::playSource(ePtr<iTsSource> &source, const char *streaminfo_file) +{ ASSERT(!m_frontend); if (m_pvr_thread) { @@ -1741,7 +1777,13 @@ RESULT eDVBChannel::playFile(const char *file) m_pvr_thread = 0; } - m_tstools.openFile(file); + if (!source->valid()) + { + eDebug("PVR source is not valid!"); + return -ENOENT; + } + + m_tstools.setSource(source, streaminfo_file); /* DON'T EVEN THINK ABOUT FIXING THIS. FIX THE ATI SOURCES FIRST, THEN DO A REAL FIX HERE! */ @@ -1751,14 +1793,28 @@ RESULT eDVBChannel::playFile(const char *file) /* (this codepath needs to be improved anyway.) */ #if HAVE_DVB_API_VERSION < 3 m_pvr_fd_dst = open("/dev/pvr", O_WRONLY); -#else - m_pvr_fd_dst = open("/dev/misc/pvr", O_WRONLY); -#endif if (m_pvr_fd_dst < 0) { - eDebug("can't open /dev/misc/pvr - you need to buy the new(!) $$$ box! (%m)"); // or wait for the driver to be improved. + eDebug("can't open /dev/pvr - you need to buy the new(!) $$$ box! (%m)"); // or wait for the driver to be improved. + return -ENODEV; + } +#else + ePtr<eDVBAllocatedDemux> &demux = m_demux ? m_demux : m_decoder_demux; + if (demux) + { + m_pvr_fd_dst = demux->get().openDVR(O_WRONLY); + if (m_pvr_fd_dst < 0) + { + eDebug("can't open /dev/dvb/adapterX/dvrX - you need to buy the new(!) $$$ box! (%m)"); // or wait for the driver to be improved. + return -ENODEV; + } + } + else + { + eDebug("no demux allocated yet.. so its not possible to open the dvr device!!"); return -ENODEV; } +#endif } m_pvr_thread = new eDVBChannelFilePush(); @@ -1768,15 +1824,7 @@ RESULT eDVBChannel::playFile(const char *file) m_event(this, evtPreStart); - if (m_pvr_thread->start(file, m_pvr_fd_dst)) - { - delete m_pvr_thread; - m_pvr_thread = 0; - ::close(m_pvr_fd_dst); - m_pvr_fd_dst = -1; - eDebug("can't open PVR file %s (%m)", file); - return -ENOENT; - } + m_pvr_thread->start(source, m_pvr_fd_dst); CONNECT(m_pvr_thread->m_event, eDVBChannel::pvrEvent); m_state = state_ok; @@ -1785,7 +1833,7 @@ RESULT eDVBChannel::playFile(const char *file) return 0; } -void eDVBChannel::stopFile() +void eDVBChannel::stopSource() { if (m_pvr_thread) { @@ -1795,6 +1843,13 @@ void eDVBChannel::stopFile() } if (m_pvr_fd_dst >= 0) ::close(m_pvr_fd_dst); + ePtr<iTsSource> d; + m_tstools.setSource(d); +} + +void eDVBChannel::stopFile() +{ + stopSource(); } void eDVBChannel::setCueSheet(eCueSheet *cuesheet) diff --git a/lib/dvb/dvb.h b/lib/dvb/dvb.h index 7b320006..33490148 100644 --- a/lib/dvb/dvb.h +++ b/lib/dvb/dvb.h @@ -135,7 +135,7 @@ class eDVBResourceManager: public iObject, public Object DECLARE_REF(eDVBResourceManager); int avail, busy; - enum { DM7025, DM800, DM500HD, DM8000 }; + enum { DM7025, DM800, DM500HD, DM800SE, DM8000, DM7020HD }; int m_boxtype; @@ -259,7 +259,10 @@ public: /* iDVBPVRChannel */ RESULT playFile(const char *file); void stopFile(); - + + RESULT playSource(ePtr<iTsSource>& source, const char *priv=NULL); + void stopSource(); + void setCueSheet(eCueSheet *cuesheet); RESULT getLength(pts_t &len); @@ -268,6 +271,7 @@ public: int getUseCount() { return m_use_count; } RESULT requestTsidOnid(ePyObject callback); + int reserveDemux(); private: ePtr<eDVBAllocatedFrontend> m_frontend; ePtr<eDVBAllocatedDemux> m_demux, m_decoder_demux; @@ -300,7 +304,7 @@ private: std::list<std::pair<off_t, off_t> > m_source_span; void getNextSourceSpan(off_t current_offset, size_t bytes_read, off_t &start, size_t &size); void flushPVR(iDVBDemux *decoding_demux=0); - + eSingleLock m_cuesheet_lock; friend class eUsePtr<eDVBChannel>; diff --git a/lib/dvb/dvbtime.cpp b/lib/dvb/dvbtime.cpp index bb6b94b0..26b6767c 100644 --- a/lib/dvb/dvbtime.cpp +++ b/lib/dvb/dvbtime.cpp @@ -111,7 +111,7 @@ void TDT::ready(int error) int TDT::createTable(unsigned int nr, const __u8 *data, unsigned int max) { - if ( data && data[0] == 0x70 || data[0] == 0x73 ) + if ( data && (data[0] == 0x70 || data[0] == 0x73 )) { int length = ((data[1] & 0x0F) << 8) | data[2]; if ( length >= 5 ) diff --git a/lib/dvb/epgcache.cpp b/lib/dvb/epgcache.cpp index 48cbfbfd..4d324746 100644 --- a/lib/dvb/epgcache.cpp +++ b/lib/dvb/epgcache.cpp @@ -213,9 +213,9 @@ pthread_mutex_t eEPGCache::channel_map_lock= DEFINE_REF(eEPGCache) eEPGCache::eEPGCache() - :messages(this,1), cleanTimer(eTimer::create(this))//, paused(0) + :messages(this,1), cleanTimer(eTimer::create(this)), m_running(0)//, paused(0) { - eDebug("[EPGC] Initialized EPGCache"); + eDebug("[EPGC] Initialized EPGCache (wait for setCacheFile call now)"); CONNECT(messages.recv_msg, eEPGCache::gotMessage); CONNECT(eDVBLocalTimeHandler::getInstance()->m_timeUpdated, eEPGCache::timeUpdated); @@ -226,22 +226,46 @@ eEPGCache::eEPGCache() if (!res_mgr) eDebug("[eEPGCache] no resource manager !!!!!!!"); else - { res_mgr->connectChannelAdded(slot(*this,&eEPGCache::DVBChannelAdded), m_chanAddedConn); + + instance=this; + memset(m_filename, 0, sizeof(m_filename)); +} + +void eEPGCache::setCacheFile(const char *path) +{ + bool inited = !!strlen(m_filename); + strncpy(m_filename, path, 1024); + if (!inited) + { + eDebug("[EPGC] setCacheFile read/write epg data from/to '%s'", m_filename); if (eDVBLocalTimeHandler::getInstance()->ready()) timeUpdated(); } - instance=this; } void eEPGCache::timeUpdated() { - if (!sync()) + if (strlen(m_filename)) { - eDebug("[EPGC] time updated.. start EPG Mainloop"); - run(); - } else - messages.send(Message(Message::timeChanged)); + if (!sync()) + { + eDebug("[EPGC] time updated.. start EPG Mainloop"); + run(); + singleLock s(channel_map_lock); + channelMapIterator it = m_knownChannels.begin(); + for (; it != m_knownChannels.end(); ++it) + { + if (it->second->state == -1) { + it->second->state=0; + messages.send(Message(Message::startChannel, it->first)); + } + } + } else + messages.send(Message(Message::timeChanged)); + } + else + eDebug("[EPGC] time updated.. but cache file not set yet.. dont start epg!!"); } void eEPGCache::DVBChannelAdded(eDVBChannel *chan) @@ -337,8 +361,13 @@ void eEPGCache::DVBChannelRunning(iDVBChannel *chan) return; } #endif - messages.send(Message(Message::startChannel, chan)); - // -> gotMessage -> changedService + if (m_running) { + data.state=0; + messages.send(Message(Message::startChannel, chan)); + // -> gotMessage -> changedService + } + else + data.state=-1; } } } @@ -365,7 +394,8 @@ void eEPGCache::DVBChannelStateChanged(iDVBChannel *chan) case iDVBChannel::state_release: { eDebug("[eEPGCache] remove channel %p", chan); - messages.send(Message(Message::leaveChannel, chan)); + if (it->second->state >= 0) + messages.send(Message(Message::leaveChannel, chan)); pthread_mutex_lock(&it->second->channel_active); singleLock s(channel_map_lock); m_knownChannels.erase(it); @@ -953,39 +983,24 @@ void eEPGCache::gotMessage( const Message &msg ) void eEPGCache::thread() { hasStarted(); + m_running=1; nice(4); load(); cleanLoop(); runLoop(); save(); + m_running=0; } void eEPGCache::load() { - FILE *f = fopen("/hdd/epg.dat", "r"); + FILE *f = fopen(m_filename, "r"); if (f) { - unlink("/hdd/epg.dat"); + unlink(m_filename); int size=0; int cnt=0; -#if 0 - unsigned char md5_saved[16]; - unsigned char md5[16]; - bool md5ok=false; - if (!md5_file("/hdd/epg.dat", 1, md5)) - { - FILE *f = fopen("/hdd/epg.dat.md5", "r"); - if (f) - { - fread( md5_saved, 16, 1, f); - fclose(f); - if ( !memcmp(md5_saved, md5, 16) ) - md5ok=true; - } - } - if ( md5ok ) -#endif { unsigned int magic=0; fread( &magic, sizeof(int), 1, f); @@ -1027,7 +1042,7 @@ void eEPGCache::load() eventDB[key]=std::pair<eventMap,timeMap>(evMap,tmMap); } eventData::load(f); - eDebug("[EPGC] %d events read from /hdd/epg.dat", cnt); + eDebug("[EPGC] %d events read from %s", cnt, m_filename); #ifdef ENABLE_PRIVATE_EPG char text2[11]; fread( text2, 11, 1, f); @@ -1075,103 +1090,106 @@ void eEPGCache::load() void eEPGCache::save() { - struct statfs s; - off64_t tmp; - if (statfs("/hdd", &s)<0) - tmp=0; - else + /* create empty file */ + FILE *f = fopen(m_filename, "w"); + + if (!f) { - tmp=s.f_blocks; - tmp*=s.f_bsize; + eDebug("[EPGC] couldn't save epg data to '%s'(%m)", m_filename); + return; } - // prevent writes to builtin flash - if ( tmp < 1024*1024*50 ) // storage size < 50MB + char *buf = realpath(m_filename, NULL); + if (!buf) + { + eDebug("[EPGC] realpath to '%s' failed in save (%m)", m_filename); + fclose(f); return; + } + + eDebug("[EPGC] store epg to realpath '%s'", buf); + + struct statfs s; + off64_t tmp; + if (statfs(buf, &s) < 0) { + eDebug("[EPGC] statfs '%s' failed in save (%m)", buf); + fclose(f); + return; + } + + free(buf); // check for enough free space on storage tmp=s.f_bfree; tmp*=s.f_bsize; if ( tmp < (eventData::CacheSize*12)/10 ) // 20% overhead + { + eDebug("[EPGC] not enough free space at path '%s' %lld bytes availd but %d needed", buf, tmp, (eventData::CacheSize*12)/10); + fclose(f); return; + } - FILE *f = fopen("/hdd/epg.dat", "w"); int cnt=0; - if ( f ) - { - unsigned int magic = 0x98765432; - fwrite( &magic, sizeof(int), 1, f); - const char *text = "UNFINISHED_V7"; - fwrite( text, 13, 1, f ); - int size = eventDB.size(); - fwrite( &size, sizeof(int), 1, f ); - for (eventCache::iterator service_it(eventDB.begin()); service_it != eventDB.end(); ++service_it) - { - timeMap &timemap = service_it->second.second; - fwrite( &service_it->first, sizeof(uniqueEPGKey), 1, f); - size = timemap.size(); - fwrite( &size, sizeof(int), 1, f); - for (timeMap::iterator time_it(timemap.begin()); time_it != timemap.end(); ++time_it) - { - __u8 len = time_it->second->ByteSize; - fwrite( &time_it->second->type, sizeof(__u8), 1, f ); - fwrite( &len, sizeof(__u8), 1, f); - fwrite( time_it->second->EITdata, len, 1, f); - ++cnt; - } + unsigned int magic = 0x98765432; + fwrite( &magic, sizeof(int), 1, f); + const char *text = "UNFINISHED_V7"; + fwrite( text, 13, 1, f ); + int size = eventDB.size(); + fwrite( &size, sizeof(int), 1, f ); + for (eventCache::iterator service_it(eventDB.begin()); service_it != eventDB.end(); ++service_it) + { + timeMap &timemap = service_it->second.second; + fwrite( &service_it->first, sizeof(uniqueEPGKey), 1, f); + size = timemap.size(); + fwrite( &size, sizeof(int), 1, f); + for (timeMap::iterator time_it(timemap.begin()); time_it != timemap.end(); ++time_it) + { + __u8 len = time_it->second->ByteSize; + fwrite( &time_it->second->type, sizeof(__u8), 1, f ); + fwrite( &len, sizeof(__u8), 1, f); + fwrite( time_it->second->EITdata, len, 1, f); + ++cnt; } - eDebug("[EPGC] %d events written to /hdd/epg.dat", cnt); - eventData::save(f); + } + eDebug("[EPGC] %d events written to %s", cnt, m_filename); + eventData::save(f); #ifdef ENABLE_PRIVATE_EPG - const char* text3 = "PRIVATE_EPG"; - fwrite( text3, 11, 1, f ); - size = content_time_tables.size(); + const char* text3 = "PRIVATE_EPG"; + fwrite( text3, 11, 1, f ); + size = content_time_tables.size(); + fwrite( &size, sizeof(int), 1, f); + for (contentMaps::iterator a = content_time_tables.begin(); a != content_time_tables.end(); ++a) + { + contentMap &content_time_table = a->second; + fwrite( &a->first, sizeof(uniqueEPGKey), 1, f); + int size = content_time_table.size(); fwrite( &size, sizeof(int), 1, f); - for (contentMaps::iterator a = content_time_tables.begin(); a != content_time_tables.end(); ++a) + for (contentMap::iterator i = content_time_table.begin(); i != content_time_table.end(); ++i ) { - contentMap &content_time_table = a->second; - fwrite( &a->first, sizeof(uniqueEPGKey), 1, f); - int size = content_time_table.size(); + int size = i->second.size(); + fwrite( &i->first, sizeof(int), 1, f); fwrite( &size, sizeof(int), 1, f); - for (contentMap::iterator i = content_time_table.begin(); i != content_time_table.end(); ++i ) - { - int size = i->second.size(); - fwrite( &i->first, sizeof(int), 1, f); - fwrite( &size, sizeof(int), 1, f); - for ( contentTimeMap::iterator it(i->second.begin()); - it != i->second.end(); ++it ) - { - fwrite( &it->first, sizeof(time_t), 1, f); - fwrite( &it->second.first, sizeof(time_t), 1, f); - fwrite( &it->second.second, sizeof(__u16), 1, f); - } - } - } -#endif - // write version string after binary data - // has been written to disk. - fsync(fileno(f)); - fseek(f, sizeof(int), SEEK_SET); - fwrite("ENIGMA_EPG_V7", 13, 1, f); - fclose(f); -#if 0 - unsigned char md5[16]; - if (!md5_file("/hdd/epg.dat", 1, md5)) - { - FILE *f = fopen("/hdd/epg.dat.md5", "w"); - if (f) + for ( contentTimeMap::iterator it(i->second.begin()); + it != i->second.end(); ++it ) { - fwrite( md5, 16, 1, f); - fclose(f); + fwrite( &it->first, sizeof(time_t), 1, f); + fwrite( &it->second.first, sizeof(time_t), 1, f); + fwrite( &it->second.second, sizeof(__u16), 1, f); } } -#endif } +#endif + // write version string after binary data + // has been written to disk. + fsync(fileno(f)); + fseek(f, sizeof(int), SEEK_SET); + fwrite("ENIGMA_EPG_V7", 13, 1, f); + fclose(f); } eEPGCache::channel_data::channel_data(eEPGCache *ml) :cache(ml) - ,abortTimer(eTimer::create(ml)), zapTimer(eTimer::create(ml)), state(0) + ,abortTimer(eTimer::create(ml)), zapTimer(eTimer::create(ml)), state(-2) ,isRunning(0), haveData(0) #ifdef ENABLE_PRIVATE_EPG ,startPrivateTimer(eTimer::create(ml)) @@ -1457,7 +1475,7 @@ void eEPGCache::channel_data::readData( const __u8 *data) } tidMap &seenSections = this->seenSections[map]; tidMap &calcedSections = this->calcedSections[map]; - if ( state == 1 && calcedSections == seenSections || state > 1 ) + if ( (state == 1 && calcedSections == seenSections) || state > 1 ) { eDebugNoNewLine("[EPGC] "); switch (source) @@ -2562,7 +2580,7 @@ void eEPGCache::PMTready(eDVBServicePMTHandler *pmthandler) { __u8 buffer[10]; (*desc)->writeToBuffer(buffer); - if (!strncmp((unsigned char*)buffer+2, "EPGDATA", 7)) + if (!strncmp((const char *)buffer+2, "EPGDATA", 7)) { eServiceReferenceDVB ref; if (!pmthandler->getServiceReference(ref)) @@ -2571,7 +2589,7 @@ void eEPGCache::PMTready(eDVBServicePMTHandler *pmthandler) messages.send(Message(Message::got_mhw2_channel_pid, ref, pid)); } } - else if(!strncmp((unsigned char*)buffer+2, "FICHAS", 6)) + else if(!strncmp((const char *)buffer+2, "FICHAS", 6)) { eServiceReferenceDVB ref; if (!pmthandler->getServiceReference(ref)) @@ -2580,7 +2598,7 @@ void eEPGCache::PMTready(eDVBServicePMTHandler *pmthandler) messages.send(Message(Message::got_mhw2_summary_pid, ref, pid)); } } - else if(!strncmp((unsigned char*)buffer+2, "GENEROS", 7)) + else if(!strncmp((const char *)buffer+2, "GENEROS", 7)) { eServiceReferenceDVB ref; if (!pmthandler->getServiceReference(ref)) diff --git a/lib/dvb/epgcache.h b/lib/dvb/epgcache.h index 4d45d87e..90aff6c6 100644 --- a/lib/dvb/epgcache.h +++ b/lib/dvb/epgcache.h @@ -177,7 +177,8 @@ class eEPGCache: public eMainloop, private eThread, public Object eEPGCache *cache; ePtr<eTimer> abortTimer, zapTimer; int prevChannelState; - __u8 state, isRunning, haveData; + int state; + __u8 isRunning, haveData; ePtr<eDVBChannel> channel; ePtr<eConnection> m_stateChangedConn, m_NowNextConn, m_ScheduleConn, m_ScheduleOtherConn, m_ViasatConn; ePtr<iDVBSectionReader> m_NowNextReader, m_ScheduleReader, m_ScheduleOtherReader, m_ViasatReader; @@ -290,6 +291,8 @@ private: void thread(); // thread function // called from epgcache thread + int m_running; + char m_filename[1024]; void save(); void load(); #ifdef ENABLE_PRIVATE_EPG @@ -325,6 +328,9 @@ public: #endif #endif + // must be called once! + void setCacheFile(const char *filename); + // called from main thread inline void Lock(); inline void Unlock(); diff --git a/lib/dvb/esection.h b/lib/dvb/esection.h index 2bb17a98..3e097ccc 100644 --- a/lib/dvb/esection.h +++ b/lib/dvb/esection.h @@ -63,7 +63,7 @@ protected: else TABLE_eDebugNoNewLine("-"); - TABLE_eDebug(" %d/%d TID %02x", avail.size(), max, data[0]); + TABLE_eDebug(" %zd/%d TID %02x", avail.size(), max, data[0]); if (avail.size() == max) { @@ -100,6 +100,10 @@ class eAUTable: public eAUGTable int first; ePtr<iDVBDemux> m_demux; eMainloop *ml; + + /* needed to detect broken table version handling (seen on some m2ts files) */ + struct timespec m_prev_table_update; + int m_table_cnt; public: eAUTable() @@ -119,6 +123,7 @@ public: int begin(eMainloop *m, const eDVBTableSpec &spec, ePtr<iDVBDemux> demux) { + m_table_cnt = 0; ml = m; m_demux = demux; first= 1; @@ -197,6 +202,24 @@ public: if (current && (!current->getSpec(spec))) { + /* detect broken table version handling (seen on some m2ts files) */ + if (m_table_cnt) + { + if (abs(timeout_usec(m_prev_table_update)) > 500000) + m_table_cnt = -1; + else if (m_table_cnt > 1) // two pmt update within one second + { + eDebug("Seen two consecutive table version changes within 500ms. " + "This seems broken, so auto update for pid %04x, table %02x is now disabled!!", + spec.pid, spec.tid); + m_table_cnt = 0; + return; + } + } + + ++m_table_cnt; + clock_gettime(CLOCK_MONOTONIC, &m_prev_table_update); + next = new Table(); CONNECT(next->tableReady, eAUTable::slotTableReady); spec.flags &= ~(eDVBTableSpec::tfAnyVersion|eDVBTableSpec::tfThisVersion|eDVBTableSpec::tfHaveTimeout); diff --git a/lib/dvb/frontend.cpp b/lib/dvb/frontend.cpp index c0263fb4..57188963 100644 --- a/lib/dvb/frontend.cpp +++ b/lib/dvb/frontend.cpp @@ -453,9 +453,9 @@ DEFINE_REF(eDVBFrontend); int eDVBFrontend::PriorityOrder=0; -eDVBFrontend::eDVBFrontend(int adap, int fe, int &ok, bool simulate) - :m_simulate(simulate), m_enabled(false), m_type(-1), m_dvbid(fe), m_slotid(fe) - ,m_fd(-1), m_need_rotor_workaround(false), m_can_handle_dvbs2(false) +eDVBFrontend::eDVBFrontend(int adap, int fe, int &ok, bool simulate, eDVBFrontend *simulate_fe) + :m_simulate(simulate), m_enabled(false), m_type(-1), m_simulate_fe(simulate_fe), m_dvbid(fe), m_slotid(fe) + ,m_fd(-1), m_rotor_mode(false), m_need_rotor_workaround(false), m_can_handle_dvbs2(false) ,m_state(stateClosed), m_timeout(0), m_tuneTimer(0) #if HAVE_DVB_API_VERSION < 3 ,m_secfd(-1) @@ -483,6 +483,13 @@ eDVBFrontend::eDVBFrontend(int adap, int fe, int &ok, bool simulate) closeFrontend(); } +void eDVBFrontend::reopenFrontend() +{ + sleep(1); + m_type = -1; + openFrontend(); +} + int eDVBFrontend::openFrontend() { if (m_state != stateClosed) @@ -496,10 +503,10 @@ int eDVBFrontend::openFrontend() #else dvb_frontend_info fe_info; #endif - eDebugNoSimulate("opening frontend %d", m_dvbid); - if (m_fd < 0) + if (!m_simulate) { - if (!m_simulate || m_type == -1) + eDebug("opening frontend %d", m_dvbid); + if (m_fd < 0) { m_fd = ::open(m_filename, O_RDWR|O_NONBLOCK); if (m_fd < 0) @@ -508,74 +515,73 @@ int eDVBFrontend::openFrontend() return -1; } } - } - else - eWarning("frontend %d already opened", m_dvbid); - if (m_type == -1) - { - if (::ioctl(m_fd, FE_GET_INFO, &fe_info) < 0) + else + eWarning("frontend %d already opened", m_dvbid); + if (m_type == -1) { - eWarning("ioctl FE_GET_INFO failed"); - ::close(m_fd); - m_fd = -1; - return -1; - } + if (::ioctl(m_fd, FE_GET_INFO, &fe_info) < 0) + { + eWarning("ioctl FE_GET_INFO failed"); + ::close(m_fd); + m_fd = -1; + return -1; + } - switch (fe_info.type) - { - case FE_QPSK: - m_type = iDVBFrontend::feSatellite; - break; - case FE_QAM: - m_type = iDVBFrontend::feCable; - break; - case FE_OFDM: - m_type = iDVBFrontend::feTerrestrial; - break; - default: - eWarning("unknown frontend type."); - ::close(m_fd); - m_fd = -1; - return -1; + switch (fe_info.type) + { + case FE_QPSK: + m_type = iDVBFrontend::feSatellite; + break; + case FE_QAM: + m_type = iDVBFrontend::feCable; + break; + case FE_OFDM: + m_type = iDVBFrontend::feTerrestrial; + break; + default: + eWarning("unknown frontend type."); + ::close(m_fd); + m_fd = -1; + return -1; + } + if (m_simulate_fe) + m_simulate_fe->m_type = m_type; + eDebugNoSimulate("detected %s frontend", "satellite\0cable\0 terrestrial"+fe_info.type*10); } - eDebugNoSimulate("detected %s frontend", "satellite\0cable\0 terrestrial"+fe_info.type*10); - } #if HAVE_DVB_API_VERSION < 3 - if (m_type == iDVBFrontend::feSatellite) - { - if (m_secfd < 0) - { - if (!m_simulate) + if (m_type == iDVBFrontend::feSatellite) + { + if (m_secfd < 0) { - m_secfd = ::open(m_sec_filename, O_RDWR); - if (m_secfd < 0) + if (!m_simulate) { - eWarning("failed! (%s) %m", m_sec_filename); - ::close(m_fd); - m_fd=-1; - return -1; + m_secfd = ::open(m_sec_filename, O_RDWR); + if (m_secfd < 0) + { + eWarning("failed! (%s) %m", m_sec_filename); + ::close(m_fd); + m_fd=-1; + return -1; + } } } - } - else - eWarning("sec %d already opened", m_dvbid); - } + else + eWarning("sec %d already opened", m_dvbid); + } #endif - setTone(iDVBFrontend::toneOff); - setVoltage(iDVBFrontend::voltageOff); - - if (!m_simulate) - { m_sn = eSocketNotifier::create(eApp, m_fd, eSocketNotifier::Read, false); CONNECT(m_sn->activated, eDVBFrontend::feEvent); } + setTone(iDVBFrontend::toneOff); + setVoltage(iDVBFrontend::voltageOff); + return 0; } -int eDVBFrontend::closeFrontend(bool force) +int eDVBFrontend::closeFrontend(bool force, bool no_delayed) { if (!force && m_data[CUR_VOLTAGE] != -1 && m_data[CUR_VOLTAGE] != iDVBFrontend::voltageOff) { @@ -598,11 +604,34 @@ int eDVBFrontend::closeFrontend(bool force) eDebugNoSimulate("close frontend %d", m_dvbid); if (m_data[SATCR] != -1) { - turnOffSatCR(m_data[SATCR]); + if (!no_delayed) + { + m_sec->prepareTurnOffSatCR(*this, m_data[SATCR]); + m_tuneTimer->start(0, true); + if(!m_tuneTimer->isActive()) + { + int timeout=0; + eDebug("[turnOffSatCR] no mainloop"); + while(true) + { + timeout = tuneLoopInt(); + if (timeout == -1) + break; + usleep(timeout*1000); // blockierendes wait.. eTimer gibts ja nicht mehr + } + } + else + eDebug("[turnOffSatCR] running mainloop"); + return 0; + } + else + m_data[ROTOR_CMD] = -1; } + setTone(iDVBFrontend::toneOff); setVoltage(iDVBFrontend::voltageOff); m_tuneTimer->stop(); + if (m_sec && !m_simulate) m_sec->setRotorMoving(m_slotid, false); if (!::close(m_fd)) @@ -692,7 +721,8 @@ void eDVBFrontend::feEvent(int w) { eDebug("stateLostLock"); state = stateLostLock; - sec_fe->m_data[CSW] = sec_fe->m_data[UCSW] = sec_fe->m_data[TONEBURST] = -1; // reset diseqc + if (!m_rotor_mode) + sec_fe->m_data[CSW] = sec_fe->m_data[UCSW] = sec_fe->m_data[TONEBURST] = -1; // reset diseqc } } if (m_state != state) @@ -782,7 +812,7 @@ int eDVBFrontend::readFrontendData(int type) float fval1 = SDS_SNRE / 268435456.0, fval2, fval3, fval4; - if (parm_u_qpsk_fec_inner <= FEC_S2_QPSK_9_10) // DVB-S2 QPSK + if (oparm.sat.modulation == eDVBFrontendParametersSatellite::Modulation_QPSK) { fval2 = 6.76; fval3 = 4.35; @@ -875,6 +905,19 @@ int eDVBFrontend::readFrontendData(int type) } else if (strstr(m_description, "BCM4506") || strstr(m_description, "BCM4505")) ret = (snr * 100) >> 8; + else if (!strcmp(m_description, "CXD1981")) + { + int mse = (~snr) & 0xFF; + switch (parm_u_qam_modulation) { + case QAM_16: + case QAM_64: + case QAM_256: ret = (int)(-950 * log(((double)mse) / 760)); break; + case QAM_32: + case QAM_128: ret = (int)(-875 * log(((double)mse) / 650)); break; + + default: break; + } + } if (type == signalQuality) { @@ -1045,6 +1088,7 @@ static void fillDictWithSatelliteData(ePyObject dict, const FRONTENDPARAMETERS & case FEC_AUTO: tmp = eDVBFrontendParametersSatellite::FEC_Auto; break; default: eDebug("got unsupported FEC from frontend! report as FEC_AUTO!\n"); } + PutToDict(dict, "fec_inner", tmp); switch (p[0].u.data) { @@ -1093,7 +1137,7 @@ static void fillDictWithSatelliteData(ePyObject dict, const FRONTENDPARAMETERS & PutToDict(dict, "orbital_position", orb_pos); PutToDict(dict, "polarization", polarization); - switch(parm_u_qpsk_fec_inner) + switch((int)parm_u_qpsk_fec_inner) { case FEC_1_2: tmp = eDVBFrontendParametersSatellite::FEC_1_2; break; case FEC_2_3: tmp = eDVBFrontendParametersSatellite::FEC_2_3; break; @@ -1427,9 +1471,11 @@ int eDVBFrontend::readInputpower() return 0; int power=m_slotid; // this is needed for read inputpower from the correct tuner ! char proc_name[64]; - sprintf(proc_name, "/proc/stb/fp/lnb_sense%d", m_slotid); - FILE *f=fopen(proc_name, "r"); - if (f) + char proc_name2[64]; + sprintf(proc_name, "/proc/stb/frontend/%d/lnb_sense", m_slotid); + sprintf(proc_name2, "/proc/stb/fp/lnb_sense%d", m_slotid); + FILE *f; + if ((f=fopen(proc_name, "r")) || (f=fopen(proc_name2, "r"))) { if (fscanf(f, "%d", &power) != 1) eDebug("read %s failed!! (%m)", proc_name); @@ -1478,9 +1524,14 @@ bool eDVBFrontend::setSecSequencePos(int steps) return true; } -void eDVBFrontend::tuneLoop() // called by m_tuneTimer +void eDVBFrontend::tuneLoop() +{ + tuneLoopInt(); +} + +int eDVBFrontend::tuneLoopInt() // called by m_tuneTimer { - int delay=0; + int delay=-1; eDVBFrontend *sec_fe = this; eDVBRegisteredFrontend *regFE = 0; long tmp = m_data[LINKED_PREV_PTR]; @@ -1510,6 +1561,7 @@ void eDVBFrontend::tuneLoop() // called by m_tuneTimer { long *sec_fe_data = sec_fe->m_data; // eDebugNoSimulate("tuneLoop %d\n", m_sec_sequence.current()->cmd); + delay = 0; switch (m_sec_sequence.current()->cmd) { case eSecCommand::SLEEP: @@ -1807,11 +1859,11 @@ void eDVBFrontend::tuneLoop() // called by m_tuneTimer int slotid = sec_fe->m_slotid; // FIXMEEEEEE hardcoded i2c devices for dm7025 and dm8000 if (slotid < 2) - sprintf(dev, "/dev/i2c/%d", slotid); + sprintf(dev, "/dev/i2c-%d", slotid); else if (slotid == 2) - sprintf(dev, "/dev/i2c/2"); // first nim socket on DM8000 use /dev/i2c/2 + sprintf(dev, "/dev/i2c-2"); // first nim socket on DM8000 use /dev/i2c-2 else if (slotid == 3) - sprintf(dev, "/dev/i2c/4"); // second nim socket on DM8000 use /dev/i2c/4 + sprintf(dev, "/dev/i2c-4"); // second nim socket on DM8000 use /dev/i2c-4 int fd = ::open(dev, O_RDWR); unsigned char data[2]; @@ -1836,6 +1888,13 @@ void eDVBFrontend::tuneLoop() // called by m_tuneTimer ++m_sec_sequence.current(); break; } + case eSecCommand::DELAYED_CLOSE_FRONTEND: + { + eDebugNoSimulate("[SEC] delayed close frontend"); + closeFrontend(false, true); + ++m_sec_sequence.current(); + break; + } default: eDebugNoSimulate("[SEC] unhandled sec command %d", ++m_sec_sequence.current()->cmd); @@ -1848,6 +1907,7 @@ void eDVBFrontend::tuneLoop() // called by m_tuneTimer regFE->dec_use(); if (m_simulate && m_sec_sequence.current() != m_sec_sequence.end()) tuneLoop(); + return delay; } void eDVBFrontend::setFrontend(bool recvEvents) @@ -2050,8 +2110,8 @@ RESULT eDVBFrontend::prepare_sat(const eDVBFrontendParametersSatellite &feparm, return -EINVAL; } #if HAVE_DVB_API_VERSION < 5 - parm_inversion |= (feparm.rolloff << 2); // Hack.. we use bit 2..3 of inversion param for rolloff - parm_inversion |= (feparm.pilot << 4); // Hack.. we use bit 4..5 of inversion param for pilot + parm_inversion = (fe_spectral_inversion_t)((feparm.rolloff << 2) | parm_inversion); // Hack.. we use bit 2..3 of inversion param for rolloff + parm_inversion = (fe_spectral_inversion_t)((feparm.pilot << 4) | parm_inversion); // Hack.. we use bit 4..5 of inversion param for pilot if (feparm.modulation == eDVBFrontendParametersSatellite::Modulation_8PSK) { parm_u_qpsk_fec_inner = (fe_code_rate_t)((int)parm_u_qpsk_fec_inner+9); @@ -2343,6 +2403,20 @@ RESULT eDVBFrontend::tune(const iDVBFrontendParameters &where) res = -EINVAL; goto tune_error; } + if (m_rotor_mode != feparm.no_rotor_command_on_tune && !feparm.no_rotor_command_on_tune) + { + eDVBFrontend *sec_fe = this; + long tmp = m_data[LINKED_PREV_PTR]; + while (tmp != -1) + { + eDVBRegisteredFrontend *linked_fe = (eDVBRegisteredFrontend*)tmp; + sec_fe = linked_fe->m_frontend; + sec_fe->getData(LINKED_NEXT_PTR, tmp); + } + eDebug("(fe%d) reset diseqc after leave rotor mode!", m_dvbid); + sec_fe->m_data[CSW] = sec_fe->m_data[UCSW] = sec_fe->m_data[TONEBURST] = sec_fe->m_data[ROTOR_CMD] = sec_fe->m_data[ROTOR_POS] = -1; // reset diseqc + } + m_rotor_mode = feparm.no_rotor_command_on_tune; if (!m_simulate) m_sec->setRotorMoving(m_slotid, false); res=prepare_sat(feparm, timeout); @@ -2569,9 +2643,12 @@ RESULT eDVBFrontend::setSEC(iDVBSatelliteEquipmentControl *sec) return 0; } -RESULT eDVBFrontend::setSecSequence(const eSecCommandList &list) +RESULT eDVBFrontend::setSecSequence(eSecCommandList &list) { - m_sec_sequence = list; + if (m_data[SATCR] != -1 && m_sec_sequence.current() != m_sec_sequence.end()) + m_sec_sequence.push_back(list); + else + m_sec_sequence = list; return 0; } @@ -2622,17 +2699,23 @@ int eDVBFrontend::isCompatibleWith(ePtr<iDVBFrontendParameters> &feparm) bool eDVBFrontend::setSlotInfo(ePyObject obj) { - ePyObject Id, Descr, Enabled, IsDVBS2; - if (!PyTuple_Check(obj) || PyTuple_Size(obj) != 4) + ePyObject Id, Descr, Enabled, IsDVBS2, frontendId; + if (!PyTuple_Check(obj) || PyTuple_Size(obj) != 5) goto arg_error; Id = PyTuple_GET_ITEM(obj, 0); Descr = PyTuple_GET_ITEM(obj, 1); Enabled = PyTuple_GET_ITEM(obj, 2); IsDVBS2 = PyTuple_GET_ITEM(obj, 3); - if (!PyInt_Check(Id) || !PyString_Check(Descr) || !PyBool_Check(Enabled) || !PyBool_Check(IsDVBS2)) + frontendId = PyTuple_GET_ITEM(obj, 4); + m_slotid = PyInt_AsLong(Id); + if (!PyInt_Check(Id) || !PyString_Check(Descr) || !PyBool_Check(Enabled) || !PyBool_Check(IsDVBS2) || !PyInt_Check(frontendId)) goto arg_error; strcpy(m_description, PyString_AS_STRING(Descr)); - m_slotid = PyInt_AsLong(Id); + if (PyInt_AsLong(frontendId) == -1 || PyInt_AsLong(frontendId) != m_dvbid) { +// eDebugNoSimulate("skip slotinfo for slotid %d, descr %s", +// m_slotid, m_description); + return false; + } m_enabled = Enabled == Py_True; // HACK.. the rotor workaround is neede for all NIMs with LNBP21 voltage regulator... m_need_rotor_workaround = !!strstr(m_description, "Alps BSBE1") || @@ -2648,42 +2731,3 @@ arg_error: "eDVBFrontend::setSlotInfo must get a tuple with first param slotid, second param slot description and third param enabled boolean"); return false; } - -RESULT eDVBFrontend::turnOffSatCR(int satcr) -{ - eSecCommandList sec_sequence; - // check if voltage is disabled - eSecCommand::pair compare; - compare.steps = +9; //nothing to do - compare.voltage = iDVBFrontend::voltageOff; - sec_sequence.push_back( eSecCommand(eSecCommand::IF_NOT_VOLTAGE_GOTO, compare) ); - sec_sequence.push_back( eSecCommand(eSecCommand::SET_VOLTAGE, iDVBFrontend::voltage13) ); - sec_sequence.push_back( eSecCommand(eSecCommand::SLEEP, 50 ) ); - - sec_sequence.push_back( eSecCommand(eSecCommand::SET_VOLTAGE, iDVBFrontend::voltage18_5) ); - sec_sequence.push_back( eSecCommand(eSecCommand::SET_TONE, iDVBFrontend::toneOff) ); - sec_sequence.push_back( eSecCommand(eSecCommand::SLEEP, 250) ); - - eDVBDiseqcCommand diseqc; - memset(diseqc.data, 0, MAX_DISEQC_LENGTH); - diseqc.len = 5; - diseqc.data[0] = 0xE0; - diseqc.data[1] = 0x10; - diseqc.data[2] = 0x5A; - diseqc.data[3] = satcr << 5; - diseqc.data[4] = 0x00; - - sec_sequence.push_back( eSecCommand(eSecCommand::SEND_DISEQC, diseqc) ); - sec_sequence.push_back( eSecCommand(eSecCommand::SLEEP, 50+20+14*diseqc.len) ); - sec_sequence.push_back( eSecCommand(eSecCommand::SET_VOLTAGE, iDVBFrontend::voltage13) ); - setSecSequence(sec_sequence); - return 0; -} - -RESULT eDVBFrontend::ScanSatCR() -{ - setFrontend(); - usleep(20000); - setTone(iDVBFrontend::toneOff); - return 0; -} diff --git a/lib/dvb/frontend.h b/lib/dvb/frontend.h index bac27539..5887f404 100644 --- a/lib/dvb/frontend.h +++ b/lib/dvb/frontend.h @@ -72,9 +72,11 @@ private: bool m_simulate; bool m_enabled; int m_type; + eDVBFrontend *m_simulate_fe; // only used to set frontend type in dvb.cpp int m_dvbid; int m_slotid; int m_fd; + bool m_rotor_mode; bool m_need_rotor_workaround; bool m_can_handle_dvbs2; char m_filename[128]; @@ -109,11 +111,12 @@ private: void feEvent(int); void timeout(); void tuneLoop(); // called by m_tuneTimer + int tuneLoopInt(); void setFrontend(bool recvEvents=true); bool setSecSequencePos(int steps); static int PriorityOrder; public: - eDVBFrontend(int adap, int fe, int &ok, bool simulate=false); + eDVBFrontend(int adap, int fe, int &ok, bool simulate=false, eDVBFrontend *simulate_fe=NULL); virtual ~eDVBFrontend(); int readInputpower(); @@ -129,7 +132,7 @@ public: RESULT sendDiseqc(const eDVBDiseqcCommand &diseqc); RESULT sendToneburst(int burst); RESULT setSEC(iDVBSatelliteEquipmentControl *sec); - RESULT setSecSequence(const eSecCommandList &list); + RESULT setSecSequence(eSecCommandList &list); RESULT getData(int num, long &data); RESULT setData(int num, long val); @@ -145,13 +148,11 @@ public: static void setTypePriorityOrder(int val) { PriorityOrder = val; } static int getTypePriorityOrder() { return PriorityOrder; } + void reopenFrontend(); int openFrontend(); - int closeFrontend(bool force=false); + int closeFrontend(bool force=false, bool no_delayed=false); const char *getDescription() const { return m_description; } bool is_simulate() const { return m_simulate; } - - RESULT turnOffSatCR(int satcr); - RESULT ScanSatCR(); }; #endif // SWIG diff --git a/lib/dvb/idemux.h b/lib/dvb/idemux.h index e92b1e75..86b35fdb 100644 --- a/lib/dvb/idemux.h +++ b/lib/dvb/idemux.h @@ -6,6 +6,7 @@ class iDVBSectionReader: public iObject { public: + virtual RESULT setBufferSize(int size)=0; virtual RESULT start(const eDVBSectionFilterMask &mask)=0; virtual RESULT stop()=0; virtual RESULT connectRead(const Slot1<void,const __u8*> &read, ePtr<eConnection> &conn)=0; @@ -15,6 +16,7 @@ public: class iDVBPESReader: public iObject { public: + virtual RESULT setBufferSize(int size)=0; virtual RESULT start(int pid)=0; virtual RESULT stop()=0; virtual RESULT connectRead(const Slot2<void,const __u8*, int> &read, ePtr<eConnection> &conn)=0; @@ -26,6 +28,7 @@ public: class iDVBTSRecorder: public iObject { public: + virtual RESULT setBufferSize(int size) = 0; virtual RESULT start() = 0; virtual RESULT addPID(int pid) = 0; virtual RESULT removePID(int pid) = 0; diff --git a/lib/dvb/idvb.h b/lib/dvb/idvb.h index 4ef7efad..86936f8d 100644 --- a/lib/dvb/idvb.h +++ b/lib/dvb/idvb.h @@ -15,6 +15,7 @@ #include <lib/base/object.h> #include <lib/base/ebase.h> #include <lib/base/elock.h> +#include <lib/base/itssource.h> #include <lib/service/service.h> #include <libsig_comp.h> #include <connection.h> @@ -459,6 +460,8 @@ class iDVBFrontend: public iDVBFrontend_ENUMS, public iObject public: virtual RESULT getFrontendType(int &SWIG_OUTPUT)=0; virtual RESULT tune(const iDVBFrontendParameters &where)=0; + virtual int closeFrontend(bool force = false, bool no_delayed = false)=0; + virtual void reopenFrontend()=0; #ifndef SWIG virtual RESULT connectStateChange(const Slot1<void,iDVBFrontend*> &stateChange, ePtr<eConnection> &connection)=0; #endif @@ -469,7 +472,7 @@ public: virtual RESULT sendToneburst(int burst)=0; #ifndef SWIG virtual RESULT setSEC(iDVBSatelliteEquipmentControl *sec)=0; - virtual RESULT setSecSequence(const eSecCommandList &list)=0; + virtual RESULT setSecSequence(eSecCommandList &list)=0; #endif virtual int readFrontendData(int type)=0; virtual void getFrontendStatus(SWIG_PYOBJECT(ePyObject) dest)=0; @@ -489,6 +492,7 @@ class iDVBSatelliteEquipmentControl: public iObject { public: virtual RESULT prepare(iDVBFrontend &frontend, FRONTENDPARAMETERS &parm, const eDVBFrontendParametersSatellite &sat, int frontend_id, unsigned int timeout)=0; + virtual void prepareTurnOffSatCR(iDVBFrontend &frontend, int satcr)=0; virtual int canTune(const eDVBFrontendParametersSatellite &feparm, iDVBFrontend *fe, int frontend_id, int *highest_score_lnb=0)=0; virtual void setRotorMoving(int slotid, bool)=0; }; @@ -506,6 +510,7 @@ public: /* direct frontend access for raw channels and/or status inquiries. */ virtual SWIG_VOID(RESULT) getFrontend(ePtr<iDVBFrontend> &SWIG_OUTPUT)=0; virtual RESULT requestTsidOnid(SWIG_PYOBJECT(ePyObject) callback) { return -1; } + virtual int reserveDemux() { return -1; } #ifndef SWIG enum { @@ -601,6 +606,10 @@ public: virtual RESULT playFile(const char *file) = 0; virtual void stopFile() = 0; + /* new interface */ + virtual RESULT playSource(ePtr<iTsSource> &source, const char *priv=NULL) = 0; + virtual void stopSource() = 0; + virtual void setCueSheet(eCueSheet *cuesheet) = 0; virtual RESULT getLength(pts_t &pts) = 0; @@ -627,6 +636,7 @@ public: virtual RESULT getSTC(pts_t &pts, int num=0)=0; virtual RESULT getCADemuxID(uint8_t &id)=0; virtual RESULT flush()=0; + virtual int openDVR(int flags)=0; }; #if HAVE_DVB_API_VERSION < 3 && !defined(VIDEO_EVENT_SIZE_CHANGED) @@ -641,7 +651,7 @@ public: /** Set Displayed Video PID and type */ virtual RESULT setVideoPID(int vpid, int type)=0; - enum { af_MPEG, af_AC3, af_DTS, af_AAC }; + enum { af_MPEG, af_AC3, af_DTS, af_AAC, af_DTSHD }; /** Set Displayed Audio PID and type */ virtual RESULT setAudioPID(int apid, int type)=0; diff --git a/lib/dvb/lowlevel/Makefile.am b/lib/dvb/lowlevel/Makefile.am index 98e89f90..1108097e 100644 --- a/lib/dvb/lowlevel/Makefile.am +++ b/lib/dvb/lowlevel/Makefile.am @@ -1,3 +1,4 @@ -EXTRA_DIST = \ +lowlevelincludedir = $(pkgincludedir)/lib/dvb/lowlevel +lowlevelinclude_HEADERS = \ eit.h \ mhw.h diff --git a/lib/dvb/pmt.cpp b/lib/dvb/pmt.cpp index ee89a3a4..e5e63316 100644 --- a/lib/dvb/pmt.cpp +++ b/lib/dvb/pmt.cpp @@ -20,13 +20,14 @@ #include <dvbsi++/registration_descriptor.h> eDVBServicePMTHandler::eDVBServicePMTHandler() - :m_ca_servicePtr(0), m_dvb_scan(0), m_decode_demux_num(0xFF) + :m_ca_servicePtr(0), m_dvb_scan(0), m_decode_demux_num(0xFF), m_no_pat_entry_delay(eTimer::create()) { m_use_decode_demux = 0; m_pmt_pid = -1; eDVBResourceManager::getInstance(m_resourceManager); CONNECT(m_PMT.tableReady, eDVBServicePMTHandler::PMTready); CONNECT(m_PAT.tableReady, eDVBServicePMTHandler::PATready); + CONNECT(m_no_pat_entry_delay->timeout, eDVBServicePMTHandler::sendEventNoPatEntry); } eDVBServicePMTHandler::~eDVBServicePMTHandler() @@ -43,8 +44,15 @@ void eDVBServicePMTHandler::channelStateChanged(iDVBChannel *channel) && (state == iDVBChannel::state_ok) && (!m_demux)) { if (m_channel) - if (m_channel->getDemux(m_demux, (!m_use_decode_demux) ? 0 : iDVBChannel::capDecode)) + { + if (m_pvr_demux_tmp) + { + m_demux = m_pvr_demux_tmp; + m_pvr_demux_tmp = NULL; + } + else if (m_channel->getDemux(m_demux, (!m_use_decode_demux) ? 0 : iDVBChannel::capDecode)) eDebug("Allocating %s-decoding a demux for now tuned-in channel failed.", m_use_decode_demux ? "" : "non-"); + } serviceEvent(eventTuned); @@ -126,30 +134,60 @@ void eDVBServicePMTHandler::PMTready(int error) } } +void eDVBServicePMTHandler::sendEventNoPatEntry() +{ + serviceEvent(eventNoPATEntry); +} + void eDVBServicePMTHandler::PATready(int) { + eDebug("PATready"); ePtr<eTable<ProgramAssociationSection> > ptr; if (!m_PAT.getCurrent(ptr)) { + int service_id_single = -1; + int pmtpid_single = -1; int pmtpid = -1; + int cnt=0; std::vector<ProgramAssociationSection*>::const_iterator i; - for (i = ptr->getSections().begin(); i != ptr->getSections().end(); ++i) + for (i = ptr->getSections().begin(); pmtpid == -1 && i != ptr->getSections().end(); ++i) { const ProgramAssociationSection &pat = **i; ProgramAssociationConstIterator program; - for (program = pat.getPrograms()->begin(); program != pat.getPrograms()->end(); ++program) + for (program = pat.getPrograms()->begin(); pmtpid == -1 && program != pat.getPrograms()->end(); ++program) + { + ++cnt; if (eServiceID((*program)->getProgramNumber()) == m_reference.getServiceID()) pmtpid = (*program)->getProgramMapPid(); + if (++cnt == 1 && pmtpid_single == -1 && pmtpid == -1) + { + pmtpid_single = (*program)->getProgramMapPid(); + service_id_single = (*program)->getProgramNumber(); + } + else + pmtpid_single = service_id_single = -1; + } } - if (pmtpid == -1) - serviceEvent(eventNoPATEntry); - else + if (pmtpid_single != -1) // only one PAT entry .. and not valid pmtpid found + { + eDebug("use single pat entry!"); + m_reference.setServiceID(eServiceID(service_id_single)); + pmtpid = pmtpid_single; + } + if (pmtpid == -1) { + eDebug("no PAT entry found.. start delay"); + m_no_pat_entry_delay->start(1000, true); + } + else { + eDebug("use pmtpid %04x for service_id %04x", pmtpid, m_reference.getServiceID().get()); + m_no_pat_entry_delay->stop(); m_PMT.begin(eApp, eDVBPMTSpec(pmtpid, m_reference.getServiceID().get()), m_demux); + } } else serviceEvent(eventNoPAT); } -PyObject *eDVBServicePMTHandler::getCaIds() +PyObject *eDVBServicePMTHandler::getCaIds(bool pair) { ePyObject ret; @@ -157,20 +195,37 @@ PyObject *eDVBServicePMTHandler::getCaIds() if ( !getProgramInfo(prog) ) { - int cnt=prog.caids.size(); - if (cnt) + if (pair) + { + int cnt=prog.caids.size(); + if (cnt) + { + ret=PyList_New(cnt); + std::list<program::capid_pair>::iterator it(prog.caids.begin()); + while(cnt--) + { + ePyObject tuple = PyTuple_New(2); + PyTuple_SET_ITEM(tuple, 0, PyInt_FromLong(it->caid)); + PyTuple_SET_ITEM(tuple, 1, PyInt_FromLong((it++)->capid)); + PyList_SET_ITEM(ret, cnt, tuple); + } + } + } + else { + std::set<program::capid_pair> set(prog.caids.begin(), prog.caids.end()); + std::set<program::capid_pair>::iterator it(set.begin()); + int cnt=set.size(); ret=PyList_New(cnt); - std::set<uint16_t>::iterator it(prog.caids.begin()); while(cnt--) - PyList_SET_ITEM(ret, cnt, PyInt_FromLong(*it++)); + PyList_SET_ITEM(ret, cnt, PyInt_FromLong((it++)->caid)); } } return ret ? (PyObject*)ret : (PyObject*)PyList_New(0); } -int eDVBServicePMTHandler::getProgramInfo(struct program &program) +int eDVBServicePMTHandler::getProgramInfo(program &program) { ePtr<eTable<ProgramMapSection> > ptr; int cached_apid_ac3 = -1; @@ -213,8 +268,29 @@ int eDVBServicePMTHandler::getProgramInfo(struct program &program) for (i = ptr->getSections().begin(); i != ptr->getSections().end(); ++i) { const ProgramMapSection &pmt = **i; + int is_hdmv = 0; + program.pcrPid = pmt.getPcrPid(); + for (DescriptorConstIterator desc = pmt.getDescriptors()->begin(); + desc != pmt.getDescriptors()->end(); ++desc) + { + if ((*desc)->getTag() == CA_DESCRIPTOR) + { + CaDescriptor *descr = (CaDescriptor*)(*desc); + program::capid_pair pair; + pair.caid = descr->getCaSystemId(); + pair.capid = descr->getCaPid(); + program.caids.push_back(pair); + } + else if ((*desc)->getTag() == REGISTRATION_DESCRIPTOR) + { + RegistrationDescriptor *d = (RegistrationDescriptor*)(*desc); + if (d->getFormatIdentifier() == 0x48444d56) // HDMV + is_hdmv = 1; + } + } + ElementaryStreamInfoConstIterator es; for (es = pmt.getEsInfo()->begin(); es != pmt.getEsInfo()->end(); ++es) { @@ -270,25 +346,34 @@ int eDVBServicePMTHandler::getProgramInfo(struct program &program) audio.type = audioStream::atAACHE; forced_audio = 1; } - case 0x80: // user private ... but blueray LPCM - if (!isvideo && !isaudio) + case 0x80: // user private ... but bluray LPCM + case 0xA0: // bluray secondary LPCM + if (!isvideo && !isaudio && is_hdmv) { isaudio = 1; audio.type = audioStream::atLPCM; } - case 0x81: // user private ... but blueray AC3 - if (!isvideo && !isaudio) + case 0x81: // user private ... but bluray AC3 + case 0xA1: // bluray secondary AC3 + if (!isvideo && !isaudio && is_hdmv) { isaudio = 1; audio.type = audioStream::atAC3; } - case 0x82: // Blueray DTS (dvb user private...) - case 0xA2: // Blueray secondary DTS - if (!isvideo && !isaudio) + case 0x82: // bluray DTS (dvb user private...) + case 0xA2: // bluray secondary DTS + if (!isvideo && !isaudio && is_hdmv) { isaudio = 1; audio.type = audioStream::atDTS; } + case 0x86: // bluray DTS-HD (dvb user private...) + case 0xA6: // bluray secondary DTS-HD + if (!isvideo && !isaudio && is_hdmv) + { + isaudio = 1; + audio.type = audioStream::atDTSHD; + } case 0x06: // PES Private case 0xEA: // TS_PSI_ST_SMPTE_VC1 { @@ -460,7 +545,10 @@ int eDVBServicePMTHandler::getProgramInfo(struct program &program) case CA_DESCRIPTOR: { CaDescriptor *descr = (CaDescriptor*)(*desc); - program.caids.insert(descr->getCaSystemId()); + program::capid_pair pair; + pair.caid = descr->getCaSystemId(); + pair.capid = descr->getCaPid(); + program.caids.push_back(pair); break; } default: @@ -477,9 +565,9 @@ int eDVBServicePMTHandler::getProgramInfo(struct program &program) default: break; } - if (isteletext && (isaudio || isvideo)) + if (isteletext && (isaudio || isvideo)) { - eDebug("ambiguous streamtype for PID %04x detected.. forced as teletext!", (*es)->getPid()); + eDebug("ambiguous streamtype for PID %04x detected.. forced as teletext!", (*es)->getPid()); continue; // continue with next PID } else if (issubtitle && (isaudio || isvideo)) @@ -517,15 +605,6 @@ int eDVBServicePMTHandler::getProgramInfo(struct program &program) else continue; } - for (DescriptorConstIterator desc = pmt.getDescriptors()->begin(); - desc != pmt.getDescriptors()->end(); ++desc) - { - if ((*desc)->getTag() == CA_DESCRIPTOR) - { - CaDescriptor *descr = (CaDescriptor*)(*desc); - program.caids.insert(descr->getCaSystemId()); - } - } } ret = 0; @@ -589,8 +668,12 @@ int eDVBServicePMTHandler::getProgramInfo(struct program &program) program.textPid = cached_tpid; } CAID_LIST &caids = m_service->m_ca; - for (CAID_LIST::iterator it(caids.begin()); it != caids.end(); ++it) - program.caids.insert(*it); + for (CAID_LIST::iterator it(caids.begin()); it != caids.end(); ++it) { + program::capid_pair pair; + pair.caid = *it; + pair.capid = -1; // not known yet + program.caids.push_back(pair); + } if ( cnt ) ret = 0; } @@ -675,10 +758,16 @@ void eDVBServicePMTHandler::SDTScanEvent(int event) int eDVBServicePMTHandler::tune(eServiceReferenceDVB &ref, int use_decode_demux, eCueSheet *cue, bool simulate, eDVBService *service) { + ePtr<iTsSource> s; + return tuneExt(ref, use_decode_demux, s, NULL, cue, simulate, service); +} + +int eDVBServicePMTHandler::tuneExt(eServiceReferenceDVB &ref, int use_decode_demux, ePtr<iTsSource> &source, const char *streaminfo_file, eCueSheet *cue, bool simulate, eDVBService *service) +{ RESULT res=0; m_reference = ref; - m_use_decode_demux = use_decode_demux; + m_no_pat_entry_delay->stop(); /* use given service as backup. This is used for timeshift where we want to clone the live stream using the cache, but in fact have a PVR channel */ m_service = service; @@ -702,11 +791,12 @@ int eDVBServicePMTHandler::tune(eServiceReferenceDVB &ref, int use_decode_demux, { if (!ref.getServiceID().get() /* incorrect sid in meta file or recordings.epl*/ ) { - eWarning("no .meta file found, trying to find PMT pid"); eDVBTSTools tstools; - if (tstools.openFile(ref.path.c_str())) - eWarning("failed to open file"); - else + bool b = source || !tstools.openFile(ref.path.c_str(), 1); + eWarning("no .meta file found, trying to find PMT pid"); + if (source) + tstools.setSource(source, NULL); + if (b) { int service_id, pmt_pid; if (!tstools.findPMT(pmt_pid, service_id)) @@ -716,6 +806,8 @@ int eDVBServicePMTHandler::tune(eServiceReferenceDVB &ref, int use_decode_demux, m_pmt_pid = pmt_pid; } } + else + eWarning("no valid source to find PMT pid!"); } eDebug("alloc PVR"); /* allocate PVR */ @@ -757,7 +849,13 @@ int eDVBServicePMTHandler::tune(eServiceReferenceDVB &ref, int use_decode_demux, if (m_pvr_channel) { m_pvr_channel->setCueSheet(cue); - m_pvr_channel->playFile(ref.path.c_str()); + + if (m_pvr_channel->getDemux(m_pvr_demux_tmp, (!m_use_decode_demux) ? 0 : iDVBChannel::capDecode)) + eDebug("Allocating %s-decoding a demux for PVR channel failed.", m_use_decode_demux ? "" : "non-"); + else if (source) + m_pvr_channel->playSource(source, streaminfo_file); + else + m_pvr_channel->playFile(ref.path.c_str()); } } diff --git a/lib/dvb/pmt.h b/lib/dvb/pmt.h index 483c06b1..0c44f35a 100644 --- a/lib/dvb/pmt.h +++ b/lib/dvb/pmt.h @@ -86,8 +86,8 @@ class eDVBServicePMTHandler: public Object eUsePtr<iDVBChannel> m_channel; eUsePtr<iDVBPVRChannel> m_pvr_channel; ePtr<eDVBResourceManager> m_resourceManager; - ePtr<iDVBDemux> m_demux; - + ePtr<iDVBDemux> m_demux, m_pvr_demux_tmp; + void channelStateChanged(iDVBChannel *); ePtr<eConnection> m_channelStateChanged_connection; void channelEvent(iDVBChannel *, int event); @@ -102,6 +102,7 @@ class eDVBServicePMTHandler: public Object int m_use_decode_demux; uint8_t m_decode_demux_num; + ePtr<eTimer> m_no_pat_entry_delay; public: eDVBServicePMTHandler(); ~eDVBServicePMTHandler(); @@ -144,7 +145,7 @@ public: { int pid, rdsPid; // hack for some radio services which transmit radiotext on different pid (i.e. harmony fm, HIT RADIO FFH, ...) - enum { atMPEG, atAC3, atDTS, atAAC, atAACHE, atLPCM }; + enum { atMPEG, atAC3, atDTS, atAAC, atAACHE, atLPCM, atDTSHD }; int type; // mpeg2, ac3, dts, ... int component_tag; @@ -181,11 +182,17 @@ public: struct program { + struct capid_pair + { + uint16_t caid; + int capid; + bool operator< (const struct capid_pair &t) const { return t.caid < caid; } + }; std::vector<videoStream> videoStreams; std::vector<audioStream> audioStreams; int defaultAudioStream; std::vector<subtitleStream> subtitleStreams; - std::set<uint16_t> caids; + std::list<capid_pair> caids; int pcrPid; int pmtPid; int textPid; @@ -193,10 +200,10 @@ public: PyObject *createPythonObject(); }; - int getProgramInfo(struct program &program); + int getProgramInfo(program &program); int getDataDemux(ePtr<iDVBDemux> &demux); int getDecodeDemux(ePtr<iDVBDemux> &demux); - PyObject *getCaIds(); + PyObject *getCaIds(bool pair=false); // caid / ecmpid pair int getPVRChannel(ePtr<iDVBPVRChannel> &pvr_channel); int getServiceReference(eServiceReferenceDVB &service) { service = m_reference; return 0; } @@ -204,8 +211,14 @@ public: int getPMT(ePtr<eTable<ProgramMapSection> > &ptr) { return m_PMT.getCurrent(ptr); } int getChannel(eUsePtr<iDVBChannel> &channel); void resetCachedProgram() { m_have_cached_program = false; } + void sendEventNoPatEntry(); + /* deprecated interface */ int tune(eServiceReferenceDVB &ref, int use_decode_demux, eCueSheet *sg=0, bool simulate=false, eDVBService *service = 0); + + /* new interface */ + int tuneExt(eServiceReferenceDVB &ref, int use_decode_demux, ePtr<iTsSource> &, const char *streaminfo_file, eCueSheet *sg=0, bool simulate=false, eDVBService *service = 0); + void free(); private: bool m_have_cached_program; diff --git a/lib/dvb/pvrparse.cpp b/lib/dvb/pvrparse.cpp index 5cdecbd6..e19dd1e4 100644 --- a/lib/dvb/pvrparse.cpp +++ b/lib/dvb/pvrparse.cpp @@ -123,7 +123,7 @@ void eMPEGStreamInformation::fixupDiscontinuties() pts_t current = i->second - currentDelta; pts_t diff = current - lastpts_t; - if (llabs(diff) > (90000*5)) // 5sec diff + if (llabs(diff) > (90000*10)) // 10sec diff { // eDebug("%llx < %llx, have discont. new timestamp is %llx (diff is %llx)!", current, lastpts_t, i->second, diff); currentDelta = i->second - lastpts_t; /* FIXME: should be the extrapolated new timestamp, based on the current rate */ diff --git a/lib/dvb/scan.cpp b/lib/dvb/scan.cpp index fd29617a..fb6f2048 100644 --- a/lib/dvb/scan.cpp +++ b/lib/dvb/scan.cpp @@ -10,8 +10,10 @@ #include <lib/dvb/esection.h> #include <lib/dvb/scan.h> #include <lib/dvb/frontend.h> +#include <lib/base/eenv.h> #include <lib/base/eerror.h> #include <lib/base/estring.h> +#include <lib/python/python.h> #include <errno.h> #define SCAN_eDebug(x...) do { if (m_scan_debug) eDebug(x); } while(0) @@ -23,37 +25,119 @@ eDVBScan::eDVBScan(iDVBChannel *channel, bool usePAT, bool debug) :m_channel(channel), m_channel_state(iDVBChannel::state_idle) ,m_ready(0), m_ready_all(usePAT ? (readySDT|readyPAT) : readySDT) ,m_pmt_running(false), m_abort_current_pmt(false), m_flags(0) - ,m_usePAT(usePAT), m_scan_debug(debug) + ,m_usePAT(usePAT), m_scan_debug(debug), m_show_add_tsid_onid_check_failed_msg(true) { if (m_channel->getDemux(m_demux)) SCAN_eDebug("scan: failed to allocate demux!"); m_channel->connectStateChange(slot(*this, &eDVBScan::stateChange), m_stateChanged_connection); + std::string filename = eEnv::resolve("${sysconfdir}/scan_tp_valid_check.py"); + FILE *f = fopen(filename.c_str(), "r"); + if (f) + { + char code[16384]; + size_t rd = fread(code, 1, 16383, f); + if (rd) + { + code[rd]=0; + m_additional_tsid_onid_check_func = Py_CompileString(code, filename.c_str(), Py_file_input); + } + fclose(f); + } } eDVBScan::~eDVBScan() { + if (m_additional_tsid_onid_check_func) + Py_DECREF(m_additional_tsid_onid_check_func); } int eDVBScan::isValidONIDTSID(int orbital_position, eOriginalNetworkID onid, eTransportStreamID tsid) { + int ret; switch (onid.get()) { case 0: case 0x1111: - return 0; + ret=0; + break; case 0x13E: // workaround for 11258H and 11470V on hotbird with same ONID/TSID (0x13E/0x578) - return orbital_position != 130 || tsid != 0x578; + ret = orbital_position != 130 || tsid != 0x578; + break; case 1: - return orbital_position == 192; + ret = orbital_position == 192; + break; case 0x00B1: - return tsid != 0x00B0; + ret = tsid != 0x00B0; + break; case 0x00eb: - return tsid != 0x4321; + ret = tsid != 0x4321; + break; case 0x0002: - return abs(orbital_position-282) < 6; + ret = abs(orbital_position-282) < 6 && tsid != 2019; + // 12070H and 10936V have same tsid/onid.. but even the same services are provided + break; + case 0x2000: + ret = tsid != 0x1000; + break; + case 0x5E: // Sirius 4.8E 12322V and 12226H + ret = abs(orbital_position-48) < 3 && tsid != 1; + break; + case 10100: // Eutelsat W7 36.0E 11644V and 11652V + ret = orbital_position != 360 || tsid != 10187; + break; + case 42: // Tuerksat 42.0E + ret = orbital_position != 420 || ( + tsid != 8 && // 11830V 12729V + tsid != 5 && // 12679V 12685H + tsid != 2 && // 11096V 12015H + tsid != 55); // 11996V 11716V + break; + case 100: // Intelsat 10 68.5E 3808V 3796V 4012V, Amos 4.0W 10723V 11571H + ret = (orbital_position != 685 && orbital_position != 3560) || tsid != 1; + break; + case 70: // Thor 0.8W 11862H 12341V + ret = abs(orbital_position-3592) < 3 && tsid != 46; + break; + case 32: // NSS 806 (40.5W) 4059R, 3774L + ret = orbital_position != 3195 || tsid != 21; + break; default: - return onid.get() < 0xFF00; + ret = onid.get() < 0xFF00; + break; + } + if (ret && m_additional_tsid_onid_check_func) + { + bool failed = true; + ePyObject dict = PyDict_New(); + extern void PutToDict(ePyObject &, const char *, long); + PyDict_SetItemString(dict, "__builtins__", PyEval_GetBuiltins()); + PutToDict(dict, "orbpos", orbital_position); + PutToDict(dict, "tsid", tsid.get()); + PutToDict(dict, "onid", onid.get()); + ePyObject r = PyEval_EvalCode((PyCodeObject*)(PyObject*)m_additional_tsid_onid_check_func, dict, dict); + if (r) + { + ePyObject o = PyDict_GetItemString(dict, "ret"); + if (o) + { + if (PyInt_Check(o)) + { + ret = PyInt_AsLong(o); + failed = false; + } + } + Py_DECREF(r); + } + if (failed && m_show_add_tsid_onid_check_failed_msg) + { + eDebug("execing /etc/enigma2/scan_tp_valid_check failed!\n" + "usable global variables in scan_tp_valid_check.py are 'orbpos', 'tsid', 'onid'\n" + "the return value must be stored in a global var named 'ret'"); + m_show_add_tsid_onid_check_failed_msg=false; + } + Py_DECREF(dict); } + return ret; } eDVBNamespace eDVBScan::buildNamespace(eOriginalNetworkID onid, eTransportStreamID tsid, unsigned long hash) @@ -109,9 +193,9 @@ RESULT eDVBScan::nextChannel() if (m_ch_toScan.empty()) { SCAN_eDebug("no channels left to scan."); - SCAN_eDebug("%d channels scanned, %d were unavailable.", + SCAN_eDebug("%zd channels scanned, %zd were unavailable.", m_ch_scanned.size(), m_ch_unavailable.size()); - SCAN_eDebug("%d channels in database.", m_new_channels.size()); + SCAN_eDebug("%zd channels in database.", m_new_channels.size()); m_event(evtFinish); return -ENOENT; } diff --git a/lib/dvb/scan.h b/lib/dvb/scan.h index 2f3ac34a..8f64abe5 100644 --- a/lib/dvb/scan.h +++ b/lib/dvb/scan.h @@ -92,6 +92,8 @@ class eDVBScan: public Object, public iObject int m_flags; bool m_usePAT; bool m_scan_debug; + ePyObject m_additional_tsid_onid_check_func; + bool m_show_add_tsid_onid_check_failed_msg; public: eDVBScan(iDVBChannel *channel, bool usePAT=true, bool debug=true ); ~eDVBScan(); diff --git a/lib/dvb/sec.cpp b/lib/dvb/sec.cpp index 91246889..e4f00add 100644 --- a/lib/dvb/sec.cpp +++ b/lib/dvb/sec.cpp @@ -108,6 +108,9 @@ int eDVBSatelliteEquipmentControl::canTune(const eDVBFrontendParametersSatellite { bool rotor=false; eDVBSatelliteLNBParameters &lnb_param = m_lnbs[idx]; + bool is_unicable = lnb_param.SatCR_idx != -1; + bool is_unicable_position_switch = lnb_param.SatCR_positions > 1; + if ( lnb_param.m_slot_mask & slot_id ) // lnb for correct tuner? { int ret = 0; @@ -156,9 +159,14 @@ int eDVBSatelliteEquipmentControl::canTune(const eDVBFrontendParametersSatellite ret = 15000; } + if (sat.no_rotor_command_on_tune && !rotor) { + eSecDebugNoSimulate("no rotor but no_rotor_command_on_tune is set.. ignore lnb %d", idx); + continue; + } + eSecDebugNoSimulate("ret1 %d", ret); - if (linked_in_use) + if (linked_in_use && !is_unicable) { // compare tuner data if ( (csw != linked_csw) || @@ -170,23 +178,8 @@ int eDVBSatelliteEquipmentControl::canTune(const eDVBFrontendParametersSatellite else ret += 15; eSecDebugNoSimulate("ret2 %d", ret); - if (ret) // special case when this tuner is linked to a satpos dependent tuner - { - fe->getData(eDVBFrontend::SATPOS_DEPENDS_PTR, satpos_depends_ptr); - if (satpos_depends_ptr != -1) - { - eDVBRegisteredFrontend *satpos_depends_to_fe = (eDVBRegisteredFrontend*) satpos_depends_ptr; - satpos_depends_to_fe->m_frontend->getData(eDVBFrontend::ROTOR_POS, rotor_pos); - if (!rotor || rotor_pos == -1 /* we dont know the rotor position yet */ - || rotor_pos != sat.orbital_position ) // not the same orbital position? - { - ret = 0; - } - } - } - eSecDebugNoSimulate("ret3 %d", ret); } - else if (satpos_depends_ptr != -1) + else if ((satpos_depends_ptr != -1) && !(is_unicable && is_unicable_position_switch)) { eSecDebugNoSimulate("satpos depends"); eDVBRegisteredFrontend *satpos_depends_to_fe = (eDVBRegisteredFrontend*) satpos_depends_ptr; @@ -199,6 +192,7 @@ int eDVBSatelliteEquipmentControl::canTune(const eDVBFrontendParametersSatellite else ret += 10; } + eSecDebugNoSimulate("ret3 %d", ret); } else // current fe is dependent of another tuner ... (so this fe can't turn the rotor!) { @@ -218,7 +212,7 @@ int eDVBSatelliteEquipmentControl::canTune(const eDVBFrontendParametersSatellite eSecDebugNoSimulate("ret5 %d", ret); - if (ret) + if (ret && !is_unicable) { int lof = sat.frequency > lnb_param.m_lof_threshold ? lnb_param.m_lof_hi : lnb_param.m_lof_lo; @@ -306,11 +300,6 @@ RESULT eDVBSatelliteEquipmentControl::prepare(iDVBFrontend &frontend, FRONTENDPA if ( sit != lnb_param.m_satellites.end()) { eSecCommandList sec_sequence; - - lnb_param.guard_offset = 0; //HACK - - frontend.setData(eDVBFrontend::SATCR, lnb_param.SatCR_idx); - eDVBSatelliteSwitchParameters &sw_param = sit->second; bool doSetFrontend = true; bool doSetVoltageToneFrontend = true; @@ -333,6 +322,15 @@ RESULT eDVBSatelliteEquipmentControl::prepare(iDVBFrontend &frontend, FRONTENDPA eDVBSatelliteDiseqcParameters::t_diseqc_mode diseqc_mode = di_param.m_diseqc_mode; eDVBSatelliteSwitchParameters::t_voltage_mode voltage_mode = sw_param.m_voltage_mode; bool diseqc13V = voltage_mode == eDVBSatelliteSwitchParameters::HV_13; + bool is_unicable = lnb_param.SatCR_idx != -1; + + bool useGotoXX = false; + int RotorCmd=-1; + int send_mask = 0; + + lnb_param.guard_offset = 0; //HACK + + frontend.setData(eDVBFrontend::SATCR, lnb_param.SatCR_idx); if (diseqc13V) voltage_mode = eDVBSatelliteSwitchParameters::HV; @@ -377,15 +375,12 @@ RESULT eDVBSatelliteEquipmentControl::prepare(iDVBFrontend &frontend, FRONTENDPA int lof = (band&1)?lnb_param.m_lof_hi:lnb_param.m_lof_lo; - int local=0; - - - if(lnb_param.SatCR_idx == -1) + if(!is_unicable) { - // calc Frequency - local = abs(sat.frequency - - ((lof - (lof % 1000)) + ((lof % 1000)>500 ? 1000 : 0)) ); //TODO für den Mist mal ein Macro schreiben - parm.FREQUENCY = (local - (local % 125)) + ((local % 125)>62 ? 125 : 0); + // calc Frequency + int local= abs(sat.frequency + - lof); + parm.FREQUENCY = ((((local * 2) / 125) + 1) / 2) * 125; frontend.setData(eDVBFrontend::FREQ_OFFSET, sat.frequency - parm.FREQUENCY); if ( voltage_mode == eDVBSatelliteSwitchParameters::_14V @@ -405,20 +400,22 @@ RESULT eDVBSatelliteEquipmentControl::prepare(iDVBFrontend &frontend, FRONTENDPA } else { - unsigned int tmp = abs(sat.frequency - - ((lof - (lof % 1000)) + ((lof % 1000)>500 ? 1000 : 0)) ) + int tmp1 = abs(sat.frequency + -lof) + lnb_param.SatCRvco - 1400000 + lnb_param.guard_offset; - parm.FREQUENCY = (lnb_param.SatCRvco - (tmp % 4000))+((tmp%4000)>2000?4000:0)+lnb_param.guard_offset; - lnb_param.UnicableTuningWord = (((tmp / 4000)+((tmp%4000)>2000?1:0)) + int tmp2 = ((((tmp1 * 2) / 4000) + 1) / 2) * 4000; + parm.FREQUENCY = lnb_param.SatCRvco - (tmp1-tmp2) + lnb_param.guard_offset; + lnb_param.UnicableTuningWord = ((tmp2 / 4000) | ((band & 1) ? 0x400 : 0) //HighLow | ((band & 2) ? 0x800 : 0) //VertHor | ((lnb_param.LNBNum & 1) ? 0 : 0x1000) //Umschaltung LNB1 LNB2 | (lnb_param.SatCR_idx << 13)); //Adresse des SatCR eDebug("[prepare] UnicableTuningWord %#04x",lnb_param.UnicableTuningWord); eDebug("[prepare] guard_offset %d",lnb_param.guard_offset); - frontend.setData(eDVBFrontend::FREQ_OFFSET, sat.frequency - ((lnb_param.UnicableTuningWord & 0x3FF) *4000 + 1400000 - lnb_param.SatCRvco + lof)); + frontend.setData(eDVBFrontend::FREQ_OFFSET, (lnb_param.UnicableTuningWord & 0x3FF) *4000 + 1400000 + lof - (2 * (lnb_param.SatCRvco - (tmp1-tmp2))) ); + voltage = VOLTAGE(13); } if (diseqc_mode >= eDVBSatelliteDiseqcParameters::V1_0) @@ -441,7 +438,7 @@ RESULT eDVBSatelliteEquipmentControl::prepare(iDVBFrontend &frontend, FRONTENDPA (di_param.m_toneburst_param != eDVBSatelliteDiseqcParameters::NO); bool changed_burst = send_burst && (forceChanged || toneburst != lastToneburst); - int send_mask = 0; /* + /* send_mask 1 must send csw 2 must send ucsw 4 send toneburst first @@ -495,8 +492,7 @@ RESULT eDVBSatelliteEquipmentControl::prepare(iDVBFrontend &frontend, FRONTENDPA #endif if (doSetVoltageToneFrontend) { - int RotorCmd=-1; - bool useGotoXX = false; + if ( diseqc_mode == eDVBSatelliteDiseqcParameters::V1_2 && !sat.no_rotor_command_on_tune ) { @@ -556,6 +552,7 @@ RESULT eDVBSatelliteEquipmentControl::prepare(iDVBFrontend &frontend, FRONTENDPA if ( send_mask ) { + int diseqc_repeats = diseqc_mode > eDVBSatelliteDiseqcParameters::V1_0 ? di_param.m_repeats : 0; int vlt = iDVBFrontend::voltageOff; eSecCommand::pair compare; compare.steps = +3; @@ -568,7 +565,7 @@ RESULT eDVBSatelliteEquipmentControl::prepare(iDVBFrontend &frontend, FRONTENDPA vlt = iDVBFrontend::voltage13; else if ( RotorCmd != -1 && RotorCmd != lastRotorCmd ) { - if (rotor_param.m_inputpower_parameters.m_use) + if (rotor_param.m_inputpower_parameters.m_use && !is_unicable) vlt = VOLTAGE(18); // in input power mode set 18V for measure input power else vlt = VOLTAGE(13); // in normal mode start turning with 13V @@ -628,7 +625,7 @@ RESULT eDVBSatelliteEquipmentControl::prepare(iDVBFrontend &frontend, FRONTENDPA if ( send_mask & 2 ) ++loops; - loops <<= di_param.m_repeats; + loops <<= diseqc_repeats; for ( int i = 0; i < loops;) // fill commands... { @@ -665,7 +662,7 @@ RESULT eDVBSatelliteEquipmentControl::prepare(iDVBFrontend &frontend, FRONTENDPA int tmp = m_params[DELAY_BETWEEN_DISEQC_REPEATS]; if (cmd) { - int delay = di_param.m_repeats ? (tmp - 54) / 2 : tmp; // standard says 100msek between two repeated commands + int delay = diseqc_repeats ? (tmp - 54) / 2 : tmp; // standard says 100msek between two repeated commands sec_sequence.push_back( eSecCommand(eSecCommand::SLEEP, delay) ); diseqc.data[2]=cmd; diseqc.data[3]=(cmd==0x38) ? csw : ucsw; @@ -694,187 +691,6 @@ RESULT eDVBSatelliteEquipmentControl::prepare(iDVBFrontend &frontend, FRONTENDPA } } - eDebugNoSimulate("RotorCmd %02x, lastRotorCmd %02lx", RotorCmd, lastRotorCmd); - if ( RotorCmd != -1 && RotorCmd != lastRotorCmd ) - { - eSecCommand::pair compare; - if (!send_mask && lnb_param.SatCR_idx == -1) - { - compare.steps = +3; - compare.tone = iDVBFrontend::toneOff; - sec_sequence.push_back( eSecCommand(eSecCommand::IF_TONE_GOTO, compare) ); - sec_sequence.push_back( eSecCommand(eSecCommand::SET_TONE, iDVBFrontend::toneOff) ); - sec_sequence.push_back( eSecCommand(eSecCommand::SLEEP, m_params[DELAY_AFTER_CONT_TONE_DISABLE_BEFORE_DISEQC]) ); - - compare.voltage = iDVBFrontend::voltageOff; - compare.steps = +4; - // the next is a check if voltage is switched off.. then we first set a voltage :) - // else we set voltage after all diseqc stuff.. - sec_sequence.push_back( eSecCommand(eSecCommand::IF_NOT_VOLTAGE_GOTO, compare) ); - - if (rotor_param.m_inputpower_parameters.m_use) - sec_sequence.push_back( eSecCommand(eSecCommand::SET_VOLTAGE, VOLTAGE(18)) ); // set 18V for measure input power - else - sec_sequence.push_back( eSecCommand(eSecCommand::SET_VOLTAGE, VOLTAGE(13)) ); // in normal mode start turning with 13V - - sec_sequence.push_back( eSecCommand(eSecCommand::SLEEP, m_params[DELAY_AFTER_ENABLE_VOLTAGE_BEFORE_MOTOR_CMD]) ); // wait 750ms when voltage was disabled - sec_sequence.push_back( eSecCommand(eSecCommand::GOTO, +9) ); // no need to send stop rotor cmd and recheck voltage - } - else - sec_sequence.push_back( eSecCommand(eSecCommand::SLEEP, m_params[DELAY_BETWEEN_SWITCH_AND_MOTOR_CMD]) ); // wait 700ms when diseqc changed - - eDVBDiseqcCommand diseqc; - memset(diseqc.data, 0, MAX_DISEQC_LENGTH); - diseqc.len = 3; - diseqc.data[0] = 0xE0; - diseqc.data[1] = 0x31; // positioner - diseqc.data[2] = 0x60; // stop - sec_sequence.push_back( eSecCommand(eSecCommand::IF_ROTORPOS_VALID_GOTO, +5) ); - sec_sequence.push_back( eSecCommand(eSecCommand::SEND_DISEQC, diseqc) ); - sec_sequence.push_back( eSecCommand(eSecCommand::SLEEP, 50) ); - sec_sequence.push_back( eSecCommand(eSecCommand::SEND_DISEQC, diseqc) ); - // wait 150msec after send rotor stop cmd - sec_sequence.push_back( eSecCommand(eSecCommand::SLEEP, m_params[DELAY_AFTER_MOTOR_STOP_CMD]) ); - - diseqc.data[0] = 0xE0; - diseqc.data[1] = 0x31; // positioner - if ( useGotoXX ) - { - diseqc.len = 5; - diseqc.data[2] = 0x6E; // drive to angular position - diseqc.data[3] = ((RotorCmd & 0xFF00) / 0x100); - diseqc.data[4] = RotorCmd & 0xFF; - } - else - { - diseqc.len = 4; - diseqc.data[2] = 0x6B; // goto stored sat position - diseqc.data[3] = RotorCmd; - diseqc.data[4] = 0x00; - } - if(lnb_param.SatCR_idx == -1) - { - int mrt = m_params[MOTOR_RUNNING_TIMEOUT]; // in seconds! - if ( rotor_param.m_inputpower_parameters.m_use ) - { // use measure rotor input power to detect rotor state - bool turn_fast = need_turn_fast(rotor_param.m_inputpower_parameters.m_turning_speed); - eSecCommand::rotor cmd; - eSecCommand::pair compare; - if (turn_fast) - compare.voltage = VOLTAGE(18); - else - compare.voltage = VOLTAGE(13); - compare.steps = +3; - sec_sequence.push_back( eSecCommand(eSecCommand::IF_VOLTAGE_GOTO, compare) ); - sec_sequence.push_back( eSecCommand(eSecCommand::SET_VOLTAGE, compare.voltage) ); - // measure idle power values - compare.steps = -2; - if (turn_fast) { - sec_sequence.push_back( eSecCommand(eSecCommand::SLEEP, m_params[DELAY_AFTER_VOLTAGE_CHANGE_BEFORE_MEASURE_IDLE_INPUTPOWER]) ); // wait 150msec after voltage change - sec_sequence.push_back( eSecCommand(eSecCommand::MEASURE_IDLE_INPUTPOWER, 1) ); - compare.val = 1; - sec_sequence.push_back( eSecCommand(eSecCommand::IF_MEASURE_IDLE_WAS_NOT_OK_GOTO, compare) ); - sec_sequence.push_back( eSecCommand(eSecCommand::SET_VOLTAGE, VOLTAGE(13)) ); - } - sec_sequence.push_back( eSecCommand(eSecCommand::SLEEP, m_params[DELAY_AFTER_VOLTAGE_CHANGE_BEFORE_MEASURE_IDLE_INPUTPOWER]) ); // wait 150msec before measure - sec_sequence.push_back( eSecCommand(eSecCommand::MEASURE_IDLE_INPUTPOWER, 0) ); - compare.val = 0; - sec_sequence.push_back( eSecCommand(eSecCommand::IF_MEASURE_IDLE_WAS_NOT_OK_GOTO, compare) ); - //////////////////////////// - sec_sequence.push_back( eSecCommand(eSecCommand::SET_ROTOR_DISEQC_RETRYS, m_params[MOTOR_COMMAND_RETRIES]) ); // 2 retries - sec_sequence.push_back( eSecCommand(eSecCommand::INVALIDATE_CURRENT_ROTORPARMS) ); - sec_sequence.push_back( eSecCommand(eSecCommand::SEND_DISEQC, diseqc) ); - sec_sequence.push_back( eSecCommand(eSecCommand::SET_TIMEOUT, 40) ); // 2 seconds rotor start timout - // rotor start loop - sec_sequence.push_back( eSecCommand(eSecCommand::SLEEP, 50) ); // 50msec delay - sec_sequence.push_back( eSecCommand(eSecCommand::MEASURE_RUNNING_INPUTPOWER) ); - cmd.direction=1; // check for running rotor - cmd.deltaA=rotor_param.m_inputpower_parameters.m_delta; - cmd.steps=+5; - cmd.okcount=0; - sec_sequence.push_back( eSecCommand(eSecCommand::IF_INPUTPOWER_DELTA_GOTO, cmd ) ); // check if rotor has started - sec_sequence.push_back( eSecCommand(eSecCommand::IF_TIMEOUT_GOTO, +2 ) ); // timeout .. we assume now the rotor is already at the correct position - sec_sequence.push_back( eSecCommand(eSecCommand::GOTO, -4) ); // goto loop start - sec_sequence.push_back( eSecCommand(eSecCommand::IF_NO_MORE_ROTOR_DISEQC_RETRYS_GOTO, turn_fast ? 10 : 9 ) ); // timeout .. we assume now the rotor is already at the correct position - sec_sequence.push_back( eSecCommand(eSecCommand::GOTO, -8) ); // goto loop start - //////////////////// - sec_sequence.push_back( eSecCommand(eSecCommand::SET_ROTOR_MOVING) ); - if (turn_fast) - sec_sequence.push_back( eSecCommand(eSecCommand::SET_VOLTAGE, VOLTAGE(18)) ); - sec_sequence.push_back( eSecCommand(eSecCommand::SET_TIMEOUT, mrt*20) ); // mrt is in seconds... our SLEEP time is 50ms.. so * 20 - // rotor running loop - sec_sequence.push_back( eSecCommand(eSecCommand::SLEEP, 50) ); // wait 50msec - sec_sequence.push_back( eSecCommand(eSecCommand::MEASURE_RUNNING_INPUTPOWER) ); - cmd.direction=0; // check for stopped rotor - cmd.steps=+3; - sec_sequence.push_back( eSecCommand(eSecCommand::IF_INPUTPOWER_DELTA_GOTO, cmd ) ); - sec_sequence.push_back( eSecCommand(eSecCommand::IF_TIMEOUT_GOTO, +2 ) ); // timeout ? this should never happen - sec_sequence.push_back( eSecCommand(eSecCommand::GOTO, -4) ); // running loop start - ///////////////////// - sec_sequence.push_back( eSecCommand(eSecCommand::UPDATE_CURRENT_ROTORPARAMS) ); - sec_sequence.push_back( eSecCommand(eSecCommand::SET_ROTOR_STOPPED) ); - } - else - { // use normal turning mode - if (curRotorPos != -1) - { - mrt = abs(curRotorPos - sat.orbital_position); - if (mrt > 1800) - mrt = 3600 - mrt; - if (mrt % 10) - mrt += 10; // round a little bit - mrt *= 2000; // (we assume a very slow rotor with just 0.5 degree per second here) - mrt /= 10000; - mrt += 3; // a little bit overhead - } - doSetVoltageToneFrontend=false; - doSetFrontend=false; - eSecCommand::rotor cmd; - eSecCommand::pair compare; - compare.voltage = VOLTAGE(13); - compare.steps = +3; - sec_sequence.push_back( eSecCommand(eSecCommand::IF_VOLTAGE_GOTO, compare) ); - sec_sequence.push_back( eSecCommand(eSecCommand::SET_VOLTAGE, compare.voltage) ); - sec_sequence.push_back( eSecCommand(eSecCommand::SLEEP, m_params[DELAY_AFTER_VOLTAGE_CHANGE_BEFORE_MOTOR_CMD]) ); // wait 150msec after voltage change - - sec_sequence.push_back( eSecCommand(eSecCommand::INVALIDATE_CURRENT_ROTORPARMS) ); - sec_sequence.push_back( eSecCommand(eSecCommand::SET_ROTOR_MOVING) ); - sec_sequence.push_back( eSecCommand(eSecCommand::SEND_DISEQC, diseqc) ); - sec_sequence.push_back( eSecCommand(eSecCommand::SLEEP, 1000) ); // sleep one second before change voltage or tone - - compare.voltage = voltage; - compare.steps = +3; - sec_sequence.push_back( eSecCommand(eSecCommand::IF_VOLTAGE_GOTO, compare) ); // correct final voltage? - sec_sequence.push_back( eSecCommand(eSecCommand::SLEEP, 2000) ); // wait 2 second before set high voltage - sec_sequence.push_back( eSecCommand(eSecCommand::SET_VOLTAGE, voltage) ); - - compare.tone = tone; - sec_sequence.push_back( eSecCommand(eSecCommand::IF_TONE_GOTO, compare) ); - sec_sequence.push_back( eSecCommand(eSecCommand::SET_TONE, tone) ); - sec_sequence.push_back( eSecCommand(eSecCommand::SLEEP, m_params[DELAY_AFTER_FINAL_CONT_TONE_CHANGE]) ); - sec_sequence.push_back( eSecCommand(eSecCommand::SET_FRONTEND, 0) ); - - cmd.direction=1; // check for running rotor - cmd.deltaA=0; - cmd.steps = +3; - cmd.okcount=0; - sec_sequence.push_back( eSecCommand(eSecCommand::SET_TIMEOUT, mrt*4) ); // mrt is in seconds... our SLEEP time is 250ms.. so * 4 - sec_sequence.push_back( eSecCommand(eSecCommand::SLEEP, 250) ); // 250msec delay - sec_sequence.push_back( eSecCommand(eSecCommand::IF_TUNER_LOCKED_GOTO, cmd ) ); - sec_sequence.push_back( eSecCommand(eSecCommand::IF_TIMEOUT_GOTO, +5 ) ); - sec_sequence.push_back( eSecCommand(eSecCommand::GOTO, -3) ); // goto loop start - sec_sequence.push_back( eSecCommand(eSecCommand::UPDATE_CURRENT_ROTORPARAMS) ); - sec_sequence.push_back( eSecCommand(eSecCommand::SET_ROTOR_STOPPED) ); - sec_sequence.push_back( eSecCommand(eSecCommand::GOTO, +4) ); - sec_sequence.push_back( eSecCommand(eSecCommand::START_TUNE_TIMEOUT, tunetimeout) ); - sec_sequence.push_back( eSecCommand(eSecCommand::SET_FRONTEND, 1) ); - sec_sequence.push_back( eSecCommand(eSecCommand::GOTO, -5) ); - } - eDebug("set rotor timeout to %d seconds", mrt); - sec_fe->setData(eDVBFrontend::NEW_ROTOR_CMD, RotorCmd); - sec_fe->setData(eDVBFrontend::NEW_ROTOR_POS, sat.orbital_position); - } - } } } else @@ -887,33 +703,17 @@ RESULT eDVBSatelliteEquipmentControl::prepare(iDVBFrontend &frontend, FRONTENDPA sec_fe->setData(eDVBFrontend::NEW_UCSW, ucsw); sec_fe->setData(eDVBFrontend::NEW_TONEBURST, di_param.m_toneburst_param); - if ((doSetVoltageToneFrontend) && (lnb_param.SatCR_idx == -1)) - { - eSecCommand::pair compare; - compare.voltage = voltage; - compare.steps = +3; - sec_sequence.push_back( eSecCommand(eSecCommand::IF_VOLTAGE_GOTO, compare) ); // voltage already correct ? - sec_sequence.push_back( eSecCommand(eSecCommand::SET_VOLTAGE, voltage) ); - sec_sequence.push_back( eSecCommand(eSecCommand::SLEEP, m_params[DELAY_AFTER_FINAL_VOLTAGE_CHANGE]) ); - compare.tone = tone; - sec_sequence.push_back( eSecCommand(eSecCommand::IF_TONE_GOTO, compare) ); - sec_sequence.push_back( eSecCommand(eSecCommand::SET_TONE, tone) ); - sec_sequence.push_back( eSecCommand(eSecCommand::SLEEP, m_params[DELAY_AFTER_FINAL_CONT_TONE_CHANGE]) ); - } - - sec_sequence.push_back( eSecCommand(eSecCommand::UPDATE_CURRENT_SWITCHPARMS) ); - - if(lnb_param.SatCR_idx != -1) + if(is_unicable) { // check if voltage is disabled eSecCommand::pair compare; compare.steps = +3; compare.voltage = iDVBFrontend::voltageOff; sec_sequence.push_back( eSecCommand(eSecCommand::IF_NOT_VOLTAGE_GOTO, compare) ); - sec_sequence.push_back( eSecCommand(eSecCommand::SET_VOLTAGE, iDVBFrontend::voltage13) ); + sec_sequence.push_back( eSecCommand(eSecCommand::SET_VOLTAGE, VOLTAGE(13)) ); sec_sequence.push_back( eSecCommand(eSecCommand::SLEEP, m_params[DELAY_AFTER_ENABLE_VOLTAGE_BEFORE_SWITCH_CMDS] ) ); - sec_sequence.push_back( eSecCommand(eSecCommand::SET_VOLTAGE, iDVBFrontend::voltage18_5) ); + sec_sequence.push_back( eSecCommand(eSecCommand::SET_VOLTAGE, VOLTAGE(18)) ); sec_sequence.push_back( eSecCommand(eSecCommand::SET_TONE, iDVBFrontend::toneOff) ); sec_sequence.push_back( eSecCommand(eSecCommand::SLEEP, m_params[DELAY_AFTER_VOLTAGE_CHANGE_BEFORE_SWITCH_CMDS]) ); // wait 20 ms after voltage change @@ -928,9 +728,209 @@ RESULT eDVBSatelliteEquipmentControl::prepare(iDVBFrontend &frontend, FRONTENDPA sec_sequence.push_back( eSecCommand(eSecCommand::SEND_DISEQC, diseqc) ); sec_sequence.push_back( eSecCommand(eSecCommand::SLEEP, m_params[DELAY_AFTER_LAST_DISEQC_CMD]) ); - sec_sequence.push_back( eSecCommand(eSecCommand::SET_VOLTAGE, iDVBFrontend::voltage13) ); + sec_sequence.push_back( eSecCommand(eSecCommand::SET_VOLTAGE, VOLTAGE(13)) ); + if ( RotorCmd != -1 && RotorCmd != lastRotorCmd && !rotor_param.m_inputpower_parameters.m_use) + sec_sequence.push_back( eSecCommand(eSecCommand::SLEEP, m_params[DELAY_AFTER_VOLTAGE_CHANGE_BEFORE_MOTOR_CMD]) ); // wait 150msec after voltage change + } + + eDebugNoSimulate("RotorCmd %02x, lastRotorCmd %02lx", RotorCmd, lastRotorCmd); + if ( RotorCmd != -1 && RotorCmd != lastRotorCmd ) + { + eSecCommand::pair compare; + if (!send_mask && !is_unicable) + { + compare.steps = +3; + compare.tone = iDVBFrontend::toneOff; + sec_sequence.push_back( eSecCommand(eSecCommand::IF_TONE_GOTO, compare) ); + sec_sequence.push_back( eSecCommand(eSecCommand::SET_TONE, iDVBFrontend::toneOff) ); + sec_sequence.push_back( eSecCommand(eSecCommand::SLEEP, m_params[DELAY_AFTER_CONT_TONE_DISABLE_BEFORE_DISEQC]) ); + + compare.voltage = iDVBFrontend::voltageOff; + compare.steps = +4; + // the next is a check if voltage is switched off.. then we first set a voltage :) + // else we set voltage after all diseqc stuff.. + sec_sequence.push_back( eSecCommand(eSecCommand::IF_NOT_VOLTAGE_GOTO, compare) ); + + if (rotor_param.m_inputpower_parameters.m_use) + sec_sequence.push_back( eSecCommand(eSecCommand::SET_VOLTAGE, VOLTAGE(18)) ); // set 18V for measure input power + else + sec_sequence.push_back( eSecCommand(eSecCommand::SET_VOLTAGE, VOLTAGE(13)) ); // in normal mode start turning with 13V + + sec_sequence.push_back( eSecCommand(eSecCommand::SLEEP, m_params[DELAY_AFTER_ENABLE_VOLTAGE_BEFORE_MOTOR_CMD]) ); // wait 750ms when voltage was disabled + sec_sequence.push_back( eSecCommand(eSecCommand::GOTO, +9) ); // no need to send stop rotor cmd and recheck voltage + } + else + sec_sequence.push_back( eSecCommand(eSecCommand::SLEEP, m_params[DELAY_BETWEEN_SWITCH_AND_MOTOR_CMD]) ); // wait 700ms when diseqc changed + + eDVBDiseqcCommand diseqc; + memset(diseqc.data, 0, MAX_DISEQC_LENGTH); + diseqc.len = 3; + diseqc.data[0] = 0xE0; + diseqc.data[1] = 0x31; // positioner + diseqc.data[2] = 0x60; // stop + sec_sequence.push_back( eSecCommand(eSecCommand::IF_ROTORPOS_VALID_GOTO, +5) ); + sec_sequence.push_back( eSecCommand(eSecCommand::SEND_DISEQC, diseqc) ); + sec_sequence.push_back( eSecCommand(eSecCommand::SLEEP, 50) ); + sec_sequence.push_back( eSecCommand(eSecCommand::SEND_DISEQC, diseqc) ); + // wait 150msec after send rotor stop cmd + sec_sequence.push_back( eSecCommand(eSecCommand::SLEEP, m_params[DELAY_AFTER_MOTOR_STOP_CMD]) ); + + diseqc.data[0] = 0xE0; + diseqc.data[1] = 0x31; // positioner + if ( useGotoXX ) + { + diseqc.len = 5; + diseqc.data[2] = 0x6E; // drive to angular position + diseqc.data[3] = ((RotorCmd & 0xFF00) / 0x100); + diseqc.data[4] = RotorCmd & 0xFF; + } + else + { + diseqc.len = 4; + diseqc.data[2] = 0x6B; // goto stored sat position + diseqc.data[3] = RotorCmd; + diseqc.data[4] = 0x00; + } + + { + int mrt = m_params[MOTOR_RUNNING_TIMEOUT]; // in seconds! + if ( rotor_param.m_inputpower_parameters.m_use) + { // use measure rotor input power to detect rotor state + bool turn_fast = need_turn_fast(rotor_param.m_inputpower_parameters.m_turning_speed) && !is_unicable; + eSecCommand::rotor cmd; + eSecCommand::pair compare; + if (turn_fast) + compare.voltage = VOLTAGE(18); + else + compare.voltage = VOLTAGE(13); + compare.steps = +3; + sec_sequence.push_back( eSecCommand(eSecCommand::IF_VOLTAGE_GOTO, compare) ); + sec_sequence.push_back( eSecCommand(eSecCommand::SET_VOLTAGE, compare.voltage) ); + // measure idle power values + compare.steps = -2; + if (turn_fast) { + sec_sequence.push_back( eSecCommand(eSecCommand::SLEEP, m_params[DELAY_AFTER_VOLTAGE_CHANGE_BEFORE_MEASURE_IDLE_INPUTPOWER]) ); // wait 150msec after voltage change + sec_sequence.push_back( eSecCommand(eSecCommand::MEASURE_IDLE_INPUTPOWER, 1) ); + compare.val = 1; + sec_sequence.push_back( eSecCommand(eSecCommand::IF_MEASURE_IDLE_WAS_NOT_OK_GOTO, compare) ); + sec_sequence.push_back( eSecCommand(eSecCommand::SET_VOLTAGE, VOLTAGE(13)) ); + } + sec_sequence.push_back( eSecCommand(eSecCommand::SLEEP, m_params[DELAY_AFTER_VOLTAGE_CHANGE_BEFORE_MEASURE_IDLE_INPUTPOWER]) ); // wait 150msec before measure + sec_sequence.push_back( eSecCommand(eSecCommand::MEASURE_IDLE_INPUTPOWER, 0) ); + compare.val = 0; + sec_sequence.push_back( eSecCommand(eSecCommand::IF_MEASURE_IDLE_WAS_NOT_OK_GOTO, compare) ); + //////////////////////////// + sec_sequence.push_back( eSecCommand(eSecCommand::SET_ROTOR_DISEQC_RETRYS, m_params[MOTOR_COMMAND_RETRIES]) ); // 2 retries + sec_sequence.push_back( eSecCommand(eSecCommand::INVALIDATE_CURRENT_ROTORPARMS) ); + sec_sequence.push_back( eSecCommand(eSecCommand::SEND_DISEQC, diseqc) ); + sec_sequence.push_back( eSecCommand(eSecCommand::SET_TIMEOUT, 40) ); // 2 seconds rotor start timout + // rotor start loop + sec_sequence.push_back( eSecCommand(eSecCommand::SLEEP, 50) ); // 50msec delay + sec_sequence.push_back( eSecCommand(eSecCommand::MEASURE_RUNNING_INPUTPOWER) ); + cmd.direction=1; // check for running rotor + cmd.deltaA=rotor_param.m_inputpower_parameters.m_delta; + cmd.steps=+5; + cmd.okcount=0; + sec_sequence.push_back( eSecCommand(eSecCommand::IF_INPUTPOWER_DELTA_GOTO, cmd ) ); // check if rotor has started + sec_sequence.push_back( eSecCommand(eSecCommand::IF_TIMEOUT_GOTO, +2 ) ); // timeout .. we assume now the rotor is already at the correct position + sec_sequence.push_back( eSecCommand(eSecCommand::GOTO, -4) ); // goto loop start + sec_sequence.push_back( eSecCommand(eSecCommand::IF_NO_MORE_ROTOR_DISEQC_RETRYS_GOTO, turn_fast ? 10 : 9 ) ); // timeout .. we assume now the rotor is already at the correct position + sec_sequence.push_back( eSecCommand(eSecCommand::GOTO, -8) ); // goto loop start + //////////////////// + sec_sequence.push_back( eSecCommand(eSecCommand::SET_ROTOR_MOVING) ); + if (turn_fast) + sec_sequence.push_back( eSecCommand(eSecCommand::SET_VOLTAGE, VOLTAGE(18)) ); + sec_sequence.push_back( eSecCommand(eSecCommand::SET_TIMEOUT, mrt*20) ); // mrt is in seconds... our SLEEP time is 50ms.. so * 20 + // rotor running loop + sec_sequence.push_back( eSecCommand(eSecCommand::SLEEP, 50) ); // wait 50msec + sec_sequence.push_back( eSecCommand(eSecCommand::MEASURE_RUNNING_INPUTPOWER) ); + cmd.direction=0; // check for stopped rotor + cmd.steps=+3; + sec_sequence.push_back( eSecCommand(eSecCommand::IF_INPUTPOWER_DELTA_GOTO, cmd ) ); + sec_sequence.push_back( eSecCommand(eSecCommand::IF_TIMEOUT_GOTO, +2 ) ); // timeout ? this should never happen + sec_sequence.push_back( eSecCommand(eSecCommand::GOTO, -4) ); // running loop start + ///////////////////// + sec_sequence.push_back( eSecCommand(eSecCommand::UPDATE_CURRENT_ROTORPARAMS) ); + sec_sequence.push_back( eSecCommand(eSecCommand::SET_ROTOR_STOPPED) ); + } + else + { // use normal turning mode + if (curRotorPos != -1) + { + mrt = abs(curRotorPos - sat.orbital_position); + if (mrt > 1800) + mrt = 3600 - mrt; + if (mrt % 10) + mrt += 10; // round a little bit + mrt *= 2000; // (we assume a very slow rotor with just 0.5 degree per second here) + mrt /= 10000; + mrt += 3; // a little bit overhead + } + doSetVoltageToneFrontend=false; + doSetFrontend=false; + eSecCommand::rotor cmd; + eSecCommand::pair compare; + compare.voltage = VOLTAGE(13); + compare.steps = +3; + sec_sequence.push_back( eSecCommand(eSecCommand::IF_VOLTAGE_GOTO, compare) ); + sec_sequence.push_back( eSecCommand(eSecCommand::SET_VOLTAGE, compare.voltage) ); + sec_sequence.push_back( eSecCommand(eSecCommand::SLEEP, m_params[DELAY_AFTER_VOLTAGE_CHANGE_BEFORE_MOTOR_CMD]) ); // wait 150msec after voltage change + + sec_sequence.push_back( eSecCommand(eSecCommand::INVALIDATE_CURRENT_ROTORPARMS) ); + sec_sequence.push_back( eSecCommand(eSecCommand::SET_ROTOR_MOVING) ); + sec_sequence.push_back( eSecCommand(eSecCommand::SEND_DISEQC, diseqc) ); + sec_sequence.push_back( eSecCommand(eSecCommand::SLEEP, 1000) ); // sleep one second before change voltage or tone + + compare.voltage = voltage; + compare.steps = +3; + sec_sequence.push_back( eSecCommand(eSecCommand::IF_VOLTAGE_GOTO, compare) ); // correct final voltage? + sec_sequence.push_back( eSecCommand(eSecCommand::SLEEP, 2000) ); // wait 2 second before set high voltage + sec_sequence.push_back( eSecCommand(eSecCommand::SET_VOLTAGE, voltage) ); + + compare.tone = tone; + sec_sequence.push_back( eSecCommand(eSecCommand::IF_TONE_GOTO, compare) ); + sec_sequence.push_back( eSecCommand(eSecCommand::SET_TONE, tone) ); + sec_sequence.push_back( eSecCommand(eSecCommand::SLEEP, m_params[DELAY_AFTER_FINAL_CONT_TONE_CHANGE]) ); + sec_sequence.push_back( eSecCommand(eSecCommand::SET_FRONTEND, 0) ); + + cmd.direction=1; // check for running rotor + cmd.deltaA=0; + cmd.steps = +3; + cmd.okcount=0; + sec_sequence.push_back( eSecCommand(eSecCommand::SET_TIMEOUT, mrt*4) ); // mrt is in seconds... our SLEEP time is 250ms.. so * 4 + sec_sequence.push_back( eSecCommand(eSecCommand::SLEEP, 250) ); // 250msec delay + sec_sequence.push_back( eSecCommand(eSecCommand::IF_TUNER_LOCKED_GOTO, cmd ) ); + sec_sequence.push_back( eSecCommand(eSecCommand::IF_TIMEOUT_GOTO, +5 ) ); + sec_sequence.push_back( eSecCommand(eSecCommand::GOTO, -3) ); // goto loop start + sec_sequence.push_back( eSecCommand(eSecCommand::UPDATE_CURRENT_ROTORPARAMS) ); + sec_sequence.push_back( eSecCommand(eSecCommand::SET_ROTOR_STOPPED) ); + sec_sequence.push_back( eSecCommand(eSecCommand::GOTO, +4) ); + sec_sequence.push_back( eSecCommand(eSecCommand::START_TUNE_TIMEOUT, tunetimeout) ); + sec_sequence.push_back( eSecCommand(eSecCommand::SET_FRONTEND, 1) ); + sec_sequence.push_back( eSecCommand(eSecCommand::GOTO, -5) ); + } + eDebug("set rotor timeout to %d seconds", mrt); + sec_fe->setData(eDVBFrontend::NEW_ROTOR_CMD, RotorCmd); + sec_fe->setData(eDVBFrontend::NEW_ROTOR_POS, sat.orbital_position); + } + } + + if (doSetVoltageToneFrontend && !is_unicable) + { + eSecCommand::pair compare; + compare.voltage = voltage; + compare.steps = +3; + sec_sequence.push_back( eSecCommand(eSecCommand::IF_VOLTAGE_GOTO, compare) ); // voltage already correct ? + sec_sequence.push_back( eSecCommand(eSecCommand::SET_VOLTAGE, voltage) ); + sec_sequence.push_back( eSecCommand(eSecCommand::SLEEP, m_params[DELAY_AFTER_FINAL_VOLTAGE_CHANGE]) ); + compare.tone = tone; + sec_sequence.push_back( eSecCommand(eSecCommand::IF_TONE_GOTO, compare) ); + sec_sequence.push_back( eSecCommand(eSecCommand::SET_TONE, tone) ); + sec_sequence.push_back( eSecCommand(eSecCommand::SLEEP, m_params[DELAY_AFTER_FINAL_CONT_TONE_CHANGE]) ); } + sec_sequence.push_back( eSecCommand(eSecCommand::UPDATE_CURRENT_SWITCHPARMS) ); + if (doSetFrontend) { sec_sequence.push_back( eSecCommand(eSecCommand::START_TUNE_TIMEOUT, tunetimeout) ); @@ -959,6 +959,40 @@ RESULT eDVBSatelliteEquipmentControl::prepare(iDVBFrontend &frontend, FRONTENDPA return -1; } +void eDVBSatelliteEquipmentControl::prepareTurnOffSatCR(iDVBFrontend &frontend, int satcr) +{ + eSecCommandList sec_sequence; + + // check if voltage is disabled + eSecCommand::pair compare; + compare.steps = +9; //only close frontend + compare.voltage = iDVBFrontend::voltageOff; + + sec_sequence.push_back( eSecCommand(eSecCommand::IF_VOLTAGE_GOTO, compare) ); + sec_sequence.push_back( eSecCommand(eSecCommand::SET_VOLTAGE, iDVBFrontend::voltage13) ); + sec_sequence.push_back( eSecCommand(eSecCommand::SLEEP, m_params[DELAY_AFTER_ENABLE_VOLTAGE_BEFORE_SWITCH_CMDS]) ); + + sec_sequence.push_back( eSecCommand(eSecCommand::SET_VOLTAGE, iDVBFrontend::voltage18_5) ); + sec_sequence.push_back( eSecCommand(eSecCommand::SET_TONE, iDVBFrontend::toneOff) ); + sec_sequence.push_back( eSecCommand(eSecCommand::SLEEP, m_params[DELAY_AFTER_VOLTAGE_CHANGE_BEFORE_SWITCH_CMDS]) ); + + eDVBDiseqcCommand diseqc; + memset(diseqc.data, 0, MAX_DISEQC_LENGTH); + diseqc.len = 5; + diseqc.data[0] = 0xE0; + diseqc.data[1] = 0x10; + diseqc.data[2] = 0x5A; + diseqc.data[3] = satcr << 5; + diseqc.data[4] = 0x00; + + sec_sequence.push_back( eSecCommand(eSecCommand::SEND_DISEQC, diseqc) ); + sec_sequence.push_back( eSecCommand(eSecCommand::SLEEP, m_params[DELAY_AFTER_LAST_DISEQC_CMD]) ); + sec_sequence.push_back( eSecCommand(eSecCommand::SET_VOLTAGE, iDVBFrontend::voltage13) ); + sec_sequence.push_back( eSecCommand(eSecCommand::DELAYED_CLOSE_FRONTEND) ); + + frontend.setSecSequence(sec_sequence); +} + RESULT eDVBSatelliteEquipmentControl::clear() { eSecDebug("eDVBSatelliteEquipmentControl::clear()"); @@ -975,19 +1009,6 @@ RESULT eDVBSatelliteEquipmentControl::clear() //reset some tuner configuration for (eSmartPtrList<eDVBRegisteredFrontend>::iterator it(m_avail_frontends.begin()); it != m_avail_frontends.end(); ++it) { - long tmp; - char c; - if (sscanf(it->m_frontend->getDescription(), "BCM450%c (internal)", &c) == 1 && !it->m_frontend->getData(eDVBFrontend::LINKED_PREV_PTR, tmp) && tmp != -1) - { - FILE *f=fopen("/proc/stb/tsmux/lnb_b_input", "w"); - if (!f || fwrite("B", 1, 1, f) != 1) - eDebug("set /proc/stb/tsmux/lnb_b_input to B failed!! (%m)"); - else - { - eDebug("set /proc/stb/tsmux/lnb_b_input to B OK"); - fclose(f); - } - } it->m_frontend->setData(eDVBFrontend::SATPOS_DEPENDS_PTR, -1); it->m_frontend->setData(eDVBFrontend::LINKED_PREV_PTR, -1); it->m_frontend->setData(eDVBFrontend::LINKED_NEXT_PTR, -1); @@ -1263,6 +1284,26 @@ RESULT eDVBSatelliteEquipmentControl::setLNBSatCRvco(int SatCRvco) return -ENOENT; return 0; } + +RESULT eDVBSatelliteEquipmentControl::setLNBSatCRpositions(int SatCR_positions) +{ + eSecDebug("eDVBSatelliteEquipmentControl::setLNBSatCRpositions(%d)", SatCR_positions); + if(SatCR_positions < 1 || SatCR_positions > 2) + return -EPERM; + if ( currentLNBValid() ) + m_lnbs[m_lnbidx].SatCR_positions = SatCR_positions; + else + return -ENOENT; + return 0; +} + +RESULT eDVBSatelliteEquipmentControl::getLNBSatCRpositions() +{ + if ( currentLNBValid() ) + return m_lnbs[m_lnbidx].SatCR_positions; + return -ENOENT; +} + RESULT eDVBSatelliteEquipmentControl::getLNBSatCR() { if ( currentLNBValid() ) @@ -1399,17 +1440,6 @@ RESULT eDVBSatelliteEquipmentControl::setTunerLinked(int tu1, int tu2) char c; p1->m_frontend->setData(eDVBFrontend::LINKED_PREV_PTR, (long)p2); p2->m_frontend->setData(eDVBFrontend::LINKED_NEXT_PTR, (long)p1); - if (!strcmp(p1->m_frontend->getDescription(), p2->m_frontend->getDescription()) && sscanf(p1->m_frontend->getDescription(), "BCM450%c (internal)", &c) == 1) - { - FILE *f=fopen("/proc/stb/tsmux/lnb_b_input", "w"); - if (!f || fwrite("A", 1, 1, f) != 1) - eDebug("set /proc/stb/tsmux/lnb_b_input to A failed!! (%m)"); - else - { - eDebug("set /proc/stb/tsmux/lnb_b_input to A OK"); - fclose(f); - } - } } p1=p2=NULL; diff --git a/lib/dvb/sec.h b/lib/dvb/sec.h index c50aee4d..ef8b1e0c 100644 --- a/lib/dvb/sec.h +++ b/lib/dvb/sec.h @@ -25,7 +25,8 @@ public: IF_TONE_GOTO, IF_NOT_TONE_GOTO, START_TUNE_TIMEOUT, SET_ROTOR_MOVING, - SET_ROTOR_STOPPED + SET_ROTOR_STOPPED, + DELAYED_CLOSE_FRONTEND }; int cmd; struct rotor @@ -103,6 +104,10 @@ public: { secSequence.push_back(cmd); } + void push_back(eSecCommandList &list) + { + secSequence.insert(end(), list.begin(), list.end()); + } void clear() { secSequence.clear(); @@ -128,6 +133,12 @@ public: { return secSequence.size(); } + eSecCommandList &operator=(const eSecCommandList &lst) + { + secSequence = lst.secSequence; + cur = begin(); + return *this; + } }; #endif @@ -252,6 +263,7 @@ public: #define MAX_SATCR 8 #define MAX_LNBNUM 32 + int SatCR_positions; int SatCR_idx; unsigned int SatCRvco; unsigned int UnicableTuningWord; @@ -311,6 +323,7 @@ public: #ifndef SWIG eDVBSatelliteEquipmentControl(eSmartPtrList<eDVBRegisteredFrontend> &avail_frontends, eSmartPtrList<eDVBRegisteredFrontend> &avail_simulate_frontends); RESULT prepare(iDVBFrontend &frontend, FRONTENDPARAMETERS &parm, const eDVBFrontendParametersSatellite &sat, int frontend_id, unsigned int tunetimeout); + void prepareTurnOffSatCR(iDVBFrontend &frontend, int satcr); // used for unicable int canTune(const eDVBFrontendParametersSatellite &feparm, iDVBFrontend *, int frontend_id, int *highest_score_lnb=0); bool currentLNBValid() { return m_lnbidx > -1 && m_lnbidx < (int)(sizeof(m_lnbs) / sizeof(eDVBSatelliteLNBParameters)); } #endif @@ -346,9 +359,10 @@ public: /* Unicable Specific Parameters */ RESULT setLNBSatCR(int SatCR_idx); RESULT setLNBSatCRvco(int SatCRvco); -// RESULT checkGuardOffset(const eDVBFrontendParametersSatellite &sat); + RESULT setLNBSatCRpositions(int SatCR_positions); RESULT getLNBSatCR(); RESULT getLNBSatCRvco(); + RESULT getLNBSatCRpositions(); /* Satellite Specific Parameters */ RESULT addSatellite(int orbital_position); RESULT setVoltageMode(int mode); diff --git a/lib/dvb/tstools.cpp b/lib/dvb/tstools.cpp index d5ad2494..6cd855cc 100644 --- a/lib/dvb/tstools.cpp +++ b/lib/dvb/tstools.cpp @@ -7,7 +7,6 @@ #include <stdio.h> eDVBTSTools::eDVBTSTools() - :m_file_lock(true) { m_pid = -1; m_maxrange = 256*1024; @@ -23,19 +22,39 @@ eDVBTSTools::eDVBTSTools() m_futile = 0; } +void eDVBTSTools::closeSource() +{ + m_source = NULL; +} + eDVBTSTools::~eDVBTSTools() { - closeFile(); + closeSource(); } int eDVBTSTools::openFile(const char *filename, int nostreaminfo) { + eRawFile *f = new eRawFile(); + ePtr<iTsSource> src = f; + + if (f->open(filename, 1) < 0) + return -1; + + setSource(src, nostreaminfo ? NULL : filename); + + return 0; +} + +void eDVBTSTools::setSource(ePtr<iTsSource> &source, const char *stream_info_filename) +{ closeFile(); - - if (!nostreaminfo) + + m_source = source; + + if (stream_info_filename) { - eDebug("loading streaminfo for %s", filename); - m_streaminfo.load(filename); + eDebug("loading streaminfo for %s", stream_info_filename); + m_streaminfo.load(stream_info_filename); } if (!m_streaminfo.empty()) @@ -45,19 +64,14 @@ int eDVBTSTools::openFile(const char *filename, int nostreaminfo) // eDebug("no recorded stream information available"); m_use_streaminfo = 0; } - - m_samples_taken = 0; - eSingleLocker l(m_file_lock); - if (m_file.open(filename, 1) < 0) - return -1; - return 0; + m_samples_taken = 0; } void eDVBTSTools::closeFile() { - eSingleLocker l(m_file_lock); - m_file.close(); + if (m_source) + closeSource(); } void eDVBTSTools::setSyncPID(int pid) @@ -77,31 +91,24 @@ int eDVBTSTools::getPTS(off_t &offset, pts_t &pts, int fixed) if (!m_streaminfo.getPTS(offset, pts)) return 0; - if (!m_file.valid()) + if (!m_source || !m_source->valid()) return -1; offset -= offset % 188; - eSingleLocker l(m_file_lock); - if (m_file.lseek(offset, SEEK_SET) < 0) - { - eDebug("lseek failed"); - return -1; - } - int left = m_maxrange; while (left >= 188) { unsigned char packet[188]; - if (m_file.read(packet, 188) != 188) + if (m_source->read(offset, packet, 188) != 188) { eDebug("read error"); break; } left -= 188; offset += 188; - + if (packet[0] != 0x47) { eDebug("resync"); @@ -111,8 +118,8 @@ int eDVBTSTools::getPTS(off_t &offset, pts_t &pts, int fixed) if (packet[i] == 0x47) break; ++i; + --offset; } - offset = m_file.lseek(i - 188, SEEK_CUR); continue; } @@ -205,6 +212,8 @@ int eDVBTSTools::getPTS(off_t &offset, pts_t &pts, int fixed) break; case 0x71: // AC3 / DTS break; + case 0x72: // DTS - HD + break; default: eDebug("skip unknwn stream_id_extension %02x\n", payload[9+offs]); continue; @@ -404,7 +413,7 @@ int eDVBTSTools::getNextAccessPoint(pts_t &ts, const pts_t &start, int direction void eDVBTSTools::calcBegin() { - if (!m_file.valid()) + if (!m_source || !m_source->valid()) return; if (!(m_begin_valid || m_futile)) @@ -419,11 +428,10 @@ void eDVBTSTools::calcBegin() void eDVBTSTools::calcEnd() { - if (!m_file.valid()) + if (!m_source || !m_source->valid()) return; - eSingleLocker l(m_file_lock); - off_t end = m_file.lseek(0, SEEK_END); + off_t end = m_source->lseek(0, SEEK_END); if (llabs(end - m_last_filelength) > 1*1024*1024) { @@ -573,31 +581,28 @@ int eDVBTSTools::takeSample(off_t off, pts_t &p) int eDVBTSTools::findPMT(int &pmt_pid, int &service_id) { /* FIXME: this will be factored out soon! */ - if (!m_file.valid()) + if (!m_source || !m_source->valid()) { eDebug(" file not valid"); return -1; } - eSingleLocker l(m_file_lock); - if (m_file.lseek(0, SEEK_SET) < 0) - { - eDebug("seek failed"); - return -1; - } + off_t position=0; int left = 5*1024*1024; while (left >= 188) { unsigned char packet[188]; - if (m_file.read(packet, 188) != 188) + int ret = m_source->read(position, packet, 188); + if (ret != 188) { eDebug("read error"); break; } left -= 188; - + position += 188; + if (packet[0] != 0x47) { int i = 0; @@ -605,12 +610,11 @@ int eDVBTSTools::findPMT(int &pmt_pid, int &service_id) { if (packet[i] == 0x47) break; + --position; ++i; } - m_file.lseek(i - 188, SEEK_CUR); continue; } - int pid = ((packet[1] << 8) | packet[2]) & 0x1FFF; int pusi = !!(packet[1] & 0x40); @@ -698,9 +702,26 @@ int eDVBTSTools::findFrame(off_t &_offset, size_t &len, int &direction, int fram else if (direction == +1) direction = 0; } - /* let's find the next frame after the given offset */ off_t start = offset; +#if 0 + /* backtrack to find the previous sequence start, in case of MPEG2 */ + if ((data & 0xFF) == 0x00) { + do { + --start; + if (m_streaminfo.getStructureEntry(start, data, 0)) + { + eDebug("get previous failed"); + return -1; + } + } while (((data & 0xFF) != 9) && ((data & 0xFF) != 0x00) && ((data & 0xFF) != 0xB3)); /* sequence start or previous frame */ + if ((data & 0xFF) != 0xB3) + start = offset; /* Failed to find corresponding sequence start, so never mind */ + } + +#endif + + /* let's find the next frame after the given offset */ do { if (m_streaminfo.getStructureEntry(offset, data, 1)) { @@ -715,9 +736,11 @@ int eDVBTSTools::findFrame(off_t &_offset, size_t &len, int &direction, int fram // eDebug("%08llx@%llx (next)", data, offset); } while (((data & 0xFF) != 9) && ((data & 0xFF) != 0x00)); /* next frame */ +#if 0 /* align to TS pkt start */ -// start = start - (start % 188); -// offset = offset - (offset % 188); + start = start - (start % 188); + offset = offset - (offset % 188); +#endif len = offset - start; _offset = start; diff --git a/lib/dvb/tstools.h b/lib/dvb/tstools.h index ed8b9241..99827921 100644 --- a/lib/dvb/tstools.h +++ b/lib/dvb/tstools.h @@ -19,9 +19,12 @@ public: eDVBTSTools(); ~eDVBTSTools(); + void setSource(ePtr<iTsSource> &source, const char *streaminfo_filename=NULL); + void closeSource(); + int openFile(const char *filename, int nostreaminfo = 0); void closeFile(); - + void setSyncPID(int pid); void setSearchRange(int maxrange); @@ -77,8 +80,7 @@ private: int m_pid; int m_maxrange; - eSingleLock m_file_lock; - eRawFile m_file; + ePtr<iTsSource> m_source; int m_begin_valid, m_end_valid; pts_t m_pts_begin, m_pts_end; diff --git a/lib/dvb_ci/Makefile.am b/lib/dvb_ci/Makefile.am index fc7f7625..9ef72492 100644 --- a/lib/dvb_ci/Makefile.am +++ b/lib/dvb_ci/Makefile.am @@ -1,10 +1,28 @@ -INCLUDES = \ - -I$(top_srcdir)/include +AM_CPPFLAGS = \ + -I$(top_srcdir) \ + -I$(top_srcdir)/include \ + -include Python.h \ + -include $(top_builddir)/enigma2_config.h noinst_LIBRARIES = libenigma_dvb_ci.a -libenigma_dvb_ci_a_SOURCES = dvbci.cpp dvbci_session.cpp dvbci_resmgr.cpp \ - dvbci_appmgr.cpp dvbci_camgr.cpp \ - dvbci_datetimemgr.cpp dvbci_mmi.cpp \ - dvbci_ui.cpp +libenigma_dvb_ci_a_SOURCES = \ + dvbci.cpp \ + dvbci_appmgr.cpp \ + dvbci_camgr.cpp \ + dvbci_datetimemgr.cpp \ + dvbci_mmi.cpp \ + dvbci_resmgr.cpp \ + dvbci_session.cpp \ + dvbci_ui.cpp +dvbciincludedir = $(pkgincludedir)/lib/dvb_ci +dvbciinclude_HEADERS = \ + dvbci.h \ + dvbci_appmgr.h \ + dvbci_camgr.h \ + dvbci_datetimemgr.h \ + dvbci_mmi.h \ + dvbci_resmgr.h \ + dvbci_session.h \ + dvbci_ui.h diff --git a/lib/dvb_ci/dvbci.cpp b/lib/dvb_ci/dvbci.cpp index 883fb5a4..374672ae 100644 --- a/lib/dvb_ci/dvbci.cpp +++ b/lib/dvb_ci/dvbci.cpp @@ -297,8 +297,9 @@ void eDVBCIInterfaces::recheckPMTHandlers() if (!pmthandler->getProgramInfo(p)) { int cnt=0; - for (caidSet::reverse_iterator x(p.caids.rbegin()); x != p.caids.rend(); ++x, ++cnt) - caids.push_front(*x); + std::set<eDVBServicePMTHandler::program::capid_pair> set(p.caids.begin(), p.caids.end()); + for (std::set<eDVBServicePMTHandler::program::capid_pair>::reverse_iterator x(set.rbegin()); x != set.rend(); ++x, ++cnt) + caids.push_front(x->caid); if (service && cnt) service->m_ca = caids; } @@ -738,7 +739,7 @@ PyObject *eDVBCIInterfaces::getDescrambleRules(int slotid) if (!slot) { char tmp[255]; - snprintf(tmp, 255, "eDVBCIInterfaces::getDescrambleRules try to get rules for CI Slot %d... but just %d slots are available", slotid, m_slots.size()); + snprintf(tmp, 255, "eDVBCIInterfaces::getDescrambleRules try to get rules for CI Slot %d... but just %zd slots are available", slotid, m_slots.size()); PyErr_SetString(PyExc_StandardError, tmp); return 0; } @@ -790,7 +791,7 @@ RESULT eDVBCIInterfaces::setDescrambleRules(int slotid, SWIG_PYOBJECT(ePyObject) if (!slot) { char tmp[255]; - snprintf(tmp, 255, "eDVBCIInterfaces::setDescrambleRules try to set rules for CI Slot %d... but just %d slots are available", slotid, m_slots.size()); + snprintf(tmp, 255, "eDVBCIInterfaces::setDescrambleRules try to set rules for CI Slot %d... but just %zd slots are available", slotid, m_slots.size()); PyErr_SetString(PyExc_StandardError, tmp); return -1; } @@ -861,7 +862,7 @@ RESULT eDVBCIInterfaces::setDescrambleRules(int slotid, SWIG_PYOBJECT(ePyObject) if (PyTuple_Size(tuple) != 2) { char buf[255]; - snprintf(buf, 255, "eDVBCIInterfaces::setDescrambleRules provider tuple has %d instead of 2 entries!!", PyTuple_Size(tuple)); + snprintf(buf, 255, "eDVBCIInterfaces::setDescrambleRules provider tuple has %zd instead of 2 entries!!", PyTuple_Size(tuple)); PyErr_SetString(PyExc_StandardError, buf); return -1; } @@ -913,7 +914,7 @@ PyObject *eDVBCIInterfaces::readCICaIds(int slotid) if (!slot) { char tmp[255]; - snprintf(tmp, 255, "eDVBCIInterfaces::readCICaIds try to get CAIds for CI Slot %d... but just %d slots are available", slotid, m_slots.size()); + snprintf(tmp, 255, "eDVBCIInterfaces::readCICaIds try to get CAIds for CI Slot %d... but just %zd slots are available", slotid, m_slots.size()); PyErr_SetString(PyExc_StandardError, tmp); } else diff --git a/lib/gdi/Makefile.am b/lib/gdi/Makefile.am index 1280556e..20321f53 100644 --- a/lib/gdi/Makefile.am +++ b/lib/gdi/Makefile.am @@ -1,15 +1,58 @@ -INCLUDES = \ - -I$(top_srcdir)/include -I$(top_srcdir)/lib/gdi/ati +AM_CPPFLAGS = \ + -I$(top_srcdir) \ + -I$(top_srcdir)/include \ + -include Python.h \ + -include $(top_builddir)/enigma2_config.h + +AM_CXXFLAGS = \ + $(LIBSDL_CFLAGS) noinst_LIBRARIES = libenigma_gdi.a libenigma_gdi_a_SOURCES = \ - region.cpp grc.cpp epng.cpp erect.cpp fb.cpp font.cpp font_arabic.cpp gfbdc.cpp \ - glcddc.cpp gpixmap.cpp lcd.cpp gfont.cpp accel.cpp picload.cpp picexif.cpp \ - compositing.cpp bcm.cpp + accel.cpp \ + bcm.cpp \ + compositing.cpp \ + epng.cpp \ + erect.cpp \ + fb.cpp \ + font.cpp \ + font_arabic.cpp \ + gfont.cpp \ + glcddc.cpp \ + gmaindc.cpp \ + gpixmap.cpp \ + grc.cpp \ + lcd.cpp \ + picexif.cpp \ + picload.cpp \ + region.cpp -if WITH_SDL -libenigma_gdi_a_SOURCES += sdl.cpp -endif +gdiincludedir = $(pkgincludedir)/lib/gdi +gdiinclude_HEADERS = \ + accel.h \ + compositing.h \ + epng.h \ + epoint.h \ + erect.h \ + esize.h \ + fb.h \ + font.h \ + gfont.h \ + glcddc.h \ + gpixmap.h \ + grc.h \ + lcd.h \ + picexif.h \ + picload.h \ + region.h -# ati/2dablt.cpp ati/2dcore.cpp ati/ati_core.cpp ati/test.cpp ati/2dutil.cpp +if HAVE_LIBSDL +libenigma_gdi_a_SOURCES += \ + sdl.cpp \ + sdl.h +else +libenigma_gdi_a_SOURCES += \ + gfbdc.cpp \ + gfbdc.h +endif diff --git a/lib/gdi/accel.cpp b/lib/gdi/accel.cpp index 5e489eb3..bd1439f2 100644 --- a/lib/gdi/accel.cpp +++ b/lib/gdi/accel.cpp @@ -32,7 +32,7 @@ extern void bcm_accel_blit( int dst_addr, int dst_width, int dst_height, int dst_stride, int src_x, int src_y, int width, int height, int dst_x, int dst_y, int dwidth, int dheight, - int pal_addr); + int pal_addr, int flags); extern void bcm_accel_fill( int dst_addr, int dst_width, int dst_height, int dst_stride, int x, int y, int width, int height, @@ -112,7 +112,7 @@ int gAccel::blit(gSurface *dst, const gSurface *src, const eRect &p, const eRect pal_addr = src->stride * src->y; unsigned long *pal = (unsigned long*)(((unsigned char*)src->data) + pal_addr); pal_addr += src->data_phys; - for (i = 0; i < 256; ++i) + for (i = 0; i < src->clut.colors; ++i) *pal++ = src->clut.data[i].argb() ^ 0xFF000000; } else return -1; /* unsupported source format */ @@ -122,7 +122,7 @@ int gAccel::blit(gSurface *dst, const gSurface *src, const eRect &p, const eRect dst->data_phys, dst->x, dst->y, dst->stride, area.left(), area.top(), area.width(), area.height(), p.x(), p.y(), p.width(), p.height(), - pal_addr); + pal_addr, flags); return 0; } #endif @@ -138,12 +138,14 @@ int gAccel::fill(gSurface *dst, const eRect &area, unsigned long col) col); return 0; #endif -#if 0 // def BCM_ACCEL - bcm_accel_fill( - dst->data_phys, dst->x, dst->y, dst->stride, - area.left(), area.top(), area.width(), area.height(), - col); - return 0; +#ifdef BCM_ACCEL + if (!m_bcm_accel_state) { + bcm_accel_fill( + dst->data_phys, dst->x, dst->y, dst->stride, + area.left(), area.top(), area.width(), area.height(), + col); + return 0; + } #endif return -1; } diff --git a/lib/gdi/bcm.cpp b/lib/gdi/bcm.cpp index 3beb6772..b215b108 100644 --- a/lib/gdi/bcm.cpp +++ b/lib/gdi/bcm.cpp @@ -23,10 +23,10 @@ static int exec_list(void); int bcm_accel_init(void) { - fb_fd = open("/dev/fb/0", O_RDWR); + fb_fd = open("/dev/fb0", O_RDWR); if (fb_fd < 0) { - perror("/dev/fb/0"); + perror("/dev/fb0"); return 1; } if (exec_list()) @@ -64,7 +64,7 @@ void bcm_accel_blit( int dst_addr, int dst_width, int dst_height, int dst_stride, int src_x, int src_y, int width, int height, int dst_x, int dst_y, int dwidth, int dheight, - int pal_addr) + int pal_addr, int flags) { C(0x43); // reset source C(0x53); // reset dest @@ -123,6 +123,51 @@ void bcm_accel_fill( int x, int y, int width, int height, unsigned long color) { -// printf("unimplemented bcm_accel_fill\n"); + C(0x43); // reset source + C(0x53); // reset dest + C(0x5b); // reset pattern + C(0x67); // reset blend + C(0x75); // reset output + + // clear dest surface + P(0x0, 0); + P(0x1, 0); + P(0x2, 0); + P(0x3, 0); + P(0x4, 0); + C(0x45); + + // clear src surface + P(0x0, 0); + P(0x1, 0); + P(0x2, 0); + P(0x3, 0); + P(0x4, 0); + C(0x5); + + P(0x2d, color); + + P(0x2e, x); // prepare output rect + P(0x2f, y); + P(0x30, width); + P(0x31, height); + C(0x6e); // set this rect as output rect + + P(0x0, dst_addr); // prepare output surface + P(0x1, dst_stride); + P(0x2, dst_width); + P(0x3, dst_height); + P(0x4, 0x7e48888); + C(0x69); // set output surface + + P(0x6f, 0); + P(0x70, 0); + P(0x71, 2); + P(0x72, 2); + C(0x73); // select color keying + + C(0x77); // do it + + exec_list(); } diff --git a/lib/gdi/epng.cpp b/lib/gdi/epng.cpp index 972a89d3..fd33298f 100644 --- a/lib/gdi/epng.cpp +++ b/lib/gdi/epng.cpp @@ -1,3 +1,4 @@ +#define PNG_SKIP_SETJMP_CHECK #include <png.h> #include <stdio.h> #include <lib/gdi/epng.h> diff --git a/lib/gdi/fb.cpp b/lib/gdi/fb.cpp index cde3e357..7ee555c1 100644 --- a/lib/gdi/fb.cpp +++ b/lib/gdi/fb.cpp @@ -83,12 +83,12 @@ nolfb: int fbClass::showConsole(int state) { - int fd=open("/dev/vc/0", O_RDWR); + int fd=open("/dev/tty0", O_RDWR); if(fd>=0) { if(ioctl(fd, KDSETMODE, state?KD_TEXT:KD_GRAPHICS)<0) { - eDebug("setting /dev/vc/0 status failed."); + eDebug("setting /dev/tty0 status failed."); } close(fd); } diff --git a/lib/gdi/fb.h b/lib/gdi/fb.h index 54473805..fcb5ff27 100644 --- a/lib/gdi/fb.h +++ b/lib/gdi/fb.h @@ -19,7 +19,7 @@ class fbClass int m_number_of_pages; int m_phys_mem; #ifdef SWIG - fbClass(const char *fb="/dev/fb/0"); + fbClass(const char *fb="/dev/fb0"); ~fbClass(); public: #else @@ -41,7 +41,7 @@ public: unsigned int Stride() { return stride; } fb_cmap *CMAP() { return &cmap; } - fbClass(const char *fb="/dev/fb/0"); + fbClass(const char *fb="/dev/fb0"); ~fbClass(); // low level gfx stuff diff --git a/lib/gdi/font.cpp b/lib/gdi/font.cpp index dfac144c..9e175476 100644 --- a/lib/gdi/font.cpp +++ b/lib/gdi/font.cpp @@ -6,16 +6,19 @@ #include <pthread.h> #include <sys/types.h> #include <unistd.h> +#include <byteswap.h> + +#ifndef BYTE_ORDER +#error "no BYTE_ORDER defined!" +#endif // use this for init Freetype... #include <ft2build.h> #include FT_FREETYPE_H -#ifdef HAVE_FREETYPE2 #define FTC_Image_Cache_New(a,b) FTC_ImageCache_New(a,b) #define FTC_Image_Cache_Lookup(a,b,c,d) FTC_ImageCache_Lookup(a,b,c,d,NULL) #define FTC_SBit_Cache_New(a,b) FTC_SBitCache_New(a,b) #define FTC_SBit_Cache_Lookup(a,b,c,d) FTC_SBitCache_Lookup(a,b,c,d,NULL) -#endif #include <lib/base/eerror.h> #include <lib/gdi/lcd.h> @@ -24,12 +27,7 @@ #include <lib/base/init.h> #include <lib/base/init_num.h> -#define HAVE_FRIBIDI -// until we have it in the cdk - -#ifdef HAVE_FRIBIDI #include <fribidi/fribidi.h> -#endif #include <map> @@ -37,10 +35,6 @@ fontRenderClass *fontRenderClass::instance; static pthread_mutex_t ftlock=PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP; -#ifndef HAVE_FREETYPE2 -static FTC_Font cache_current_font=0; -#endif - struct fntColorCacheKey { gRGB start, end; @@ -205,12 +199,8 @@ float fontRenderClass::getLineHeight(const gFont& font) return 0; singleLock s(ftlock); FT_Face current_face; -#ifdef HAVE_FREETYPE2 if ((FTC_Manager_LookupFace(cacheManager, fnt->scaler.face_id, ¤t_face) < 0) || (FTC_Manager_LookupSize(cacheManager, &fnt->scaler, &fnt->size) < 0)) -#else - if (FTC_Manager_Lookup_Size(cacheManager, &fnt->font.font, ¤t_face, &fnt->size)<0) -#endif { eDebug("FTC_Manager_Lookup_Size failed!"); return 0; @@ -258,7 +248,6 @@ DEFINE_REF(Font); Font::Font(fontRenderClass *render, FTC_FaceID faceid, int isize, int tw): tabwidth(tw) { renderer=render; -#ifdef HAVE_FREETYPE2 font.face_id = faceid; font.width = isize; font.height = isize; @@ -267,12 +256,6 @@ Font::Font(fontRenderClass *render, FTC_FaceID faceid, int isize, int tw): tabwi scaler.width = isize; scaler.height = isize; scaler.pixel = 1; -#else - font.font.face_id=faceid; - font.font.pix_width = isize; - font.font.pix_height = isize; - font.image_type = ftc_image_grays; -#endif height=isize; if (tabwidth==-1) tabwidth=8*isize; @@ -341,6 +324,8 @@ int eTextPara::appendGlyph(Font *current_font, FT_Face current_face, FT_UInt gly i->x-=offset.x(); i->y-=offset.y(); i->bbox.moveBy(-offset.x(), -offset.y()); + --lineChars.back(); + ++charCount; } while (i-- != glyphs.rbegin()); // rearrange them into the next line cursor+=ePoint(linelength, 0); // put the cursor after that line } else @@ -363,7 +348,7 @@ int eTextPara::appendGlyph(Font *current_font, FT_Face current_face, FT_UInt gly } pGlyph ng; - ng.bbox.setLeft( (flags&GS_ISFIRST|cursor.x()-1)+glyph->left ); + ng.bbox.setLeft( ((flags&GS_ISFIRST)|(cursor.x()-1))+glyph->left ); ng.bbox.setTop( cursor.y() - glyph->top ); ng.bbox.setWidth( glyph->width ); ng.bbox.setHeight( glyph->height ); @@ -378,6 +363,7 @@ int eTextPara::appendGlyph(Font *current_font, FT_Face current_face, FT_UInt gly ng.glyph_index = glyphIndex; ng.flags = flags; glyphs.push_back(ng); + ++charCount; /* when we have a SHY, don't xadvance. It will either be the last in the line (when used for breaking), or not displayed. */ if (!(flags & GS_SOFTHYPHEN)) @@ -425,7 +411,13 @@ void eTextPara::newLine(int flags) cursor.setX(left); previous=0; int linegap=current_face->size->metrics.height-(current_face->size->metrics.ascender+current_face->size->metrics.descender); + + lineOffsets.push_back(cursor.y()); + lineChars.push_back(charCount); + charCount=0; + cursor+=ePoint(0, (current_face->size->metrics.ascender+current_face->size->metrics.descender+linegap)>>6); + if (maximum.height()<cursor.y()) maximum.setHeight(cursor.y()); previous=0; @@ -460,18 +452,12 @@ void eTextPara::setFont(Font *fnt, Font *replacement) // we ask for replacment_font first becauseof the cache if (replacement_font) { -#ifdef HAVE_FREETYPE2 if ((FTC_Manager_LookupFace(fontRenderClass::instance->cacheManager, replacement_font->scaler.face_id, &replacement_face) < 0) || (FTC_Manager_LookupSize(fontRenderClass::instance->cacheManager, &replacement_font->scaler, &replacement_font->size) < 0)) -#else - if (FTC_Manager_Lookup_Size(fontRenderClass::instance->cacheManager, - &replacement_font->font.font, &replacement_face, - &replacement_font->size)<0) -#endif { eDebug("FTC_Manager_Lookup_Size failed!"); return; @@ -479,24 +465,17 @@ void eTextPara::setFont(Font *fnt, Font *replacement) } if (current_font) { -#ifdef HAVE_FREETYPE2 if ((FTC_Manager_LookupFace(fontRenderClass::instance->cacheManager, current_font->scaler.face_id, ¤t_face) < 0) || (FTC_Manager_LookupSize(fontRenderClass::instance->cacheManager, ¤t_font->scaler, ¤t_font->size) < 0)) -#else - if (FTC_Manager_Lookup_Size(fontRenderClass::instance->cacheManager, ¤t_font->font.font, ¤t_face, ¤t_font->size)<0) -#endif { eDebug("FTC_Manager_Lookup_Size failed!"); return; } } -#ifndef HAVE_FREETYPE2 - cache_current_font=¤t_font->font.font; -#endif previous=0; use_kerning=FT_HAS_KERNING(current_face); } @@ -511,7 +490,6 @@ int eTextPara::renderString(const char *string, int rflags) if (!current_font) return -1; -#ifdef HAVE_FREETYPE2 if ((FTC_Manager_LookupFace(fontRenderClass::instance->cacheManager, current_font->scaler.face_id, ¤t_face) < 0) || @@ -522,17 +500,6 @@ int eTextPara::renderString(const char *string, int rflags) eDebug("FTC_Manager_Lookup_Size failed!"); return -1; } -#else - if (¤t_font->font.font != cache_current_font) - { - if (FTC_Manager_Lookup_Size(fontRenderClass::instance->cacheManager, ¤t_font->font.font, ¤t_face, ¤t_font->size)<0) - { - eDebug("FTC_Manager_Lookup_Size failed!"); - return -1; - } - cache_current_font=¤t_font->font.font; - } -#endif if (!current_face) eFatal("eTextPara::renderString: no current_face"); @@ -595,22 +562,16 @@ int eTextPara::renderString(const char *string, int rflags) shape(uc_shape, uc_string); // now do the usual logical->visual reordering -#ifdef HAVE_FRIBIDI + int size=uc_shape.size(); FriBidiCharType dir=FRIBIDI_TYPE_ON; - { - int size=uc_shape.size(); - uc_visual.resize(size); - // gaaanz lahm, aber anders geht das leider nicht, sorry. - FriBidiChar array[size], target[size]; - std::copy(uc_shape.begin(), uc_shape.end(), array); - fribidi_log2vis(array, size, &dir, target, 0, 0, 0); - uc_visual.assign(target, target+size); - } -#else - uc_visual=uc_shape; -#endif - - glyphs.reserve(uc_visual.size()); + uc_visual.resize(size); + // gaaanz lahm, aber anders geht das leider nicht, sorry. + FriBidiChar array[size], target[size]; + std::copy(uc_shape.begin(), uc_shape.end(), array); + fribidi_log2vis(array, size, &dir, target, 0, 0, 0); + uc_visual.assign(target, target+size); + + glyphs.reserve(size); int nextflags = 0; @@ -702,10 +663,19 @@ nprint: isprintable=0; } bboxValid=false; calc_bbox(); -#ifdef HAVE_FRIBIDI if (dir & FRIBIDI_MASK_RTL) + { realign(dirRight); -#endif + doTopBottomReordering=true; + } + + if (charCount) + { + lineOffsets.push_back(cursor.y()); + lineChars.push_back(charCount); + charCount=0; + } + return 0; } @@ -716,7 +686,6 @@ void eTextPara::blit(gDC &dc, const ePoint &offset, const gRGB &background, cons if (!current_font) return; -#ifdef HAVE_FREETYPE2 if ((FTC_Manager_LookupFace(fontRenderClass::instance->cacheManager, current_font->scaler.face_id, ¤t_face) < 0) || @@ -727,17 +696,6 @@ void eTextPara::blit(gDC &dc, const ePoint &offset, const gRGB &background, cons eDebug("FTC_Manager_Lookup_Size failed!"); return; } -#else - if (¤t_font->font.font != cache_current_font) - { - if (FTC_Manager_Lookup_Size(fontRenderClass::instance->cacheManager, ¤t_font->font.font, ¤t_face, ¤t_font->size)<0) - { - eDebug("FTC_Manager_Lookup_Size failed!"); - return; - } - cache_current_font=¤t_font->font.font; - } -#endif ePtr<gPixmap> target; dc.getPixmap(target); @@ -748,8 +706,9 @@ void eTextPara::blit(gDC &dc, const ePoint &offset, const gRGB &background, cons gColor *lookup8, lookup8_invert[16]; gColor *lookup8_normal=0; + __u16 lookup16_normal[16], lookup16_invert[16], *lookup16; __u32 lookup32_normal[16], lookup32_invert[16], *lookup32; - + if (surface->bpp == 8) { if (surface->clut.data) @@ -763,10 +722,33 @@ void eTextPara::blit(gDC &dc, const ePoint &offset, const gRGB &background, cons opcode=0; } else opcode=1; + } else if (surface->bpp == 16) + { + opcode=2; + for (int i=0; i<16; ++i) + { +#define BLEND(y, x, a) (y + (((x-y) * a)>>8)) + unsigned char da = background.a, dr = background.r, dg = background.g, db = background.b; + int sa = i * 16; + if (sa < 256) + { + dr = BLEND(background.r, foreground.r, sa) & 0xFF; + dg = BLEND(background.g, foreground.g, sa) & 0xFF; + db = BLEND(background.b, foreground.b, sa) & 0xFF; + } +#undef BLEND +#if BYTE_ORDER == LITTLE_ENDIAN + lookup16_normal[i] = bswap_16(((db >> 3) << 11) | ((dg >> 2) << 5) | (dr >> 3)); +#else + lookup16_normal[i] = ((db >> 3) << 11) | ((dg >> 2) << 5) | (dr >> 3); +#endif + da ^= 0xFF; + } + for (int i=0; i<16; ++i) + lookup16_invert[i]=lookup16_normal[i^0xF]; } else if (surface->bpp == 32) { opcode=3; - for (int i=0; i<16; ++i) { #define BLEND(y, x, a) (y + (((x-y) * a)>>8)) @@ -791,35 +773,47 @@ void eTextPara::blit(gDC &dc, const ePoint &offset, const gRGB &background, cons eWarning("can't render to %dbpp", surface->bpp); return; } - + gRegion area(eRect(0, 0, surface->x, surface->y)); gRegion clip = dc.getClip() & area; int buffer_stride=surface->stride; - + for (unsigned int c = 0; c < clip.rects.size(); ++c) { - for (glyphString::iterator i(glyphs.begin()); i != glyphs.end(); ++i) + std::list<int>::reverse_iterator line_offs_it(lineOffsets.rbegin()); + std::list<int>::iterator line_chars_it(lineChars.begin()); + int line_offs=0; + int line_chars=0; + for (glyphString::iterator i(glyphs.begin()); i != glyphs.end(); ++i, --line_chars) { + while(!line_chars) + { + line_offs = *(line_offs_it++); + line_chars = *(line_chars_it++); + } + if (i->flags & GS_SOFTHYPHEN) continue; if (!(i->flags & GS_INVERT)) { lookup8 = lookup8_normal; + lookup16 = lookup16_normal; lookup32 = lookup32_normal; } else { lookup8 = lookup8_invert; + lookup16 = lookup16_invert; lookup32 = lookup32_invert; } - + static FTC_SBit glyph_bitmap; if (fontRenderClass::instance->getGlyphBitmap(&i->font->font, i->glyph_index, &glyph_bitmap)) continue; int rx=i->x+glyph_bitmap->left + offset.x(); - int ry=i->y-glyph_bitmap->top + offset.y(); - + int ry=(doTopBottomReordering ? line_offs : i->y) - glyph_bitmap->top + offset.y(); + __u8 *d=(__u8*)(surface->data)+buffer_stride*ry+rx*surface->bypp; __u8 *s=glyph_bitmap->buffer; register int sx=glyph_bitmap->width; @@ -845,46 +839,76 @@ void eTextPara::blit(gDC &dc, const ePoint &offset, const gRGB &background, cons d+=diff*buffer_stride; } if (sx>0) - for (int ay=0; ay<sy; ay++) - { - if (!opcode) // 4bit lookup to 8bit + { + switch(opcode) { + case 0: // 4bit lookup to 8bit + for (int ay=0; ay<sy; ay++) { register __u8 *td=d; register int ax; - for (ax=0; ax<sx; ax++) - { + { register int b=(*s++)>>4; if(b) *td++=lookup8[b]; else td++; } - } else if (opcode == 1) // 8bit direct + s+=glyph_bitmap->pitch-sx; + d+=buffer_stride; + } + break; + case 1: // 8bit direct + for (int ay=0; ay<sy; ay++) { register __u8 *td=d; register int ax; for (ax=0; ax<sx; ax++) - { + { register int b=*s++; *td++^=b; } - } else + s+=glyph_bitmap->pitch-sx; + d+=buffer_stride; + } + break; + case 2: // 16bit + for (int ay=0; ay<sy; ay++) + { + register __u16 *td=(__u16*)d; + register int ax; + for (ax=0; ax<sx; ax++) + { + register int b=(*s++)>>4; + if(b) + *td++=lookup16[b]; + else + td++; + } + s+=glyph_bitmap->pitch-sx; + d+=buffer_stride; + } + break; + case 3: // 32bit + for (int ay=0; ay<sy; ay++) { register __u32 *td=(__u32*)d; register int ax; for (ax=0; ax<sx; ax++) - { + { register int b=(*s++)>>4; if(b) *td++=lookup32[b]; else td++; } + s+=glyph_bitmap->pitch-sx; + d+=buffer_stride; } - s+=glyph_bitmap->pitch-sx; - d+=buffer_stride; + default: + break; } + } } } } diff --git a/lib/gdi/font.h b/lib/gdi/font.h index 2643fda2..6b821838 100644 --- a/lib/gdi/font.h +++ b/lib/gdi/font.h @@ -8,12 +8,11 @@ #include FT_CACHE_H #include FT_CACHE_IMAGE_H #include FT_CACHE_SMALL_BITMAPS_H -#ifdef HAVE_FREETYPE2 typedef FTC_ImageCache FTC_Image_Cache; typedef FTC_ImageTypeRec FTC_Image_Desc; typedef FTC_SBitCache FTC_SBit_Cache; -#endif #include <vector> +#include <list> #include <lib/gdi/fb.h> #include <lib/gdi/esize.h> @@ -113,6 +112,10 @@ class eTextPara: public iObject eSize maximum; int left; glyphString glyphs; + std::list<int> lineOffsets; + std::list<int> lineChars; + int charCount; + bool doTopBottomReordering; int appendGlyph(Font *current_font, FT_Face current_face, FT_UInt glyphIndex, int flags, int rflags); void newLine(int flags); @@ -120,10 +123,12 @@ class eTextPara: public iObject eRect boundBox; void calc_bbox(); int bboxValid; + void clear(); public: eTextPara(eRect area, ePoint start=ePoint(-1, -1)) - : current_font(0), replacement_font(0), current_face(0), replacement_face(0), - area(area), cursor(start), maximum(0, 0), left(start.x()), bboxValid(0) + :current_font(0), replacement_font(0), current_face(0), replacement_face(0) + ,area(area), cursor(start), maximum(0, 0), left(start.x()), charCount(0) + ,doTopBottomReordering(false), bboxValid(0) { } virtual ~eTextPara(); @@ -134,7 +139,7 @@ public: void setFont(const gFont *font); int renderString(const char *string, int flags=0); - void clear(); + void blit(gDC &dc, const ePoint &offset, const gRGB &background, const gRGB &foreground); @@ -184,9 +189,7 @@ class Font: public iObject { DECLARE_REF(Font); public: -#ifdef HAVE_FREETYPE2 FTC_ScalerRec scaler; -#endif FTC_Image_Desc font; fontRenderClass *renderer; FT_Error getGlyphBitmap(FT_ULong glyph_index, FTC_SBit *sbit); diff --git a/lib/gdi/gfbdc.cpp b/lib/gdi/gfbdc.cpp index 80dd2479..a4f65ebc 100644 --- a/lib/gdi/gfbdc.cpp +++ b/lib/gdi/gfbdc.cpp @@ -7,18 +7,8 @@ #include <time.h> -gFBDC *gFBDC::instance; - -ePtr<gFBDC> NewgFBDCPtr(void) -{ - ePtr<gFBDC> ptr; - gFBDC::getInstance(ptr); - return ptr; -} - gFBDC::gFBDC() { - instance=this; fb=new fbClass; if (!fb->Available()) @@ -34,7 +24,6 @@ gFBDC::~gFBDC() { delete fb; delete[] surface.clut.data; - instance=0; } void gFBDC::calcRamp() @@ -91,7 +80,7 @@ void gFBDC::setPalette() fb->PutCMAP(); } -void gFBDC::exec(gOpcode *o) +void gFBDC::exec(const gOpcode *o) { switch (o->opcode) { @@ -242,7 +231,4 @@ void gFBDC::reloadSettings() setPalette(); } -// eAutoInitPtr<gFBDC> init_gFBDC(eAutoInitNumbers::graphic-1, "GFBDC"); -#ifndef WITH_SDL eAutoInitPtr<gFBDC> init_gFBDC(eAutoInitNumbers::graphic-1, "GFBDC"); -#endif diff --git a/lib/gdi/gfbdc.h b/lib/gdi/gfbdc.h index 23051c4f..7f76d0c9 100644 --- a/lib/gdi/gfbdc.h +++ b/lib/gdi/gfbdc.h @@ -3,17 +3,12 @@ #include "fb.h" #include "gpixmap.h" -#include "grc.h" +#include "gmaindc.h" -class gFBDC; - -SWIG_IGNORE(gFBDC); -class gFBDC: public gDC +class gFBDC: public gMainDC { -#ifndef SWIG fbClass *fb; - static gFBDC *instance; - void exec(gOpcode *opcode); + void exec(const gOpcode *opcode); unsigned char ramp[256], rampalpha[256]; // RGB ramp 0..255 int brightness, gamma, alpha; void calcRamp(); @@ -21,13 +16,8 @@ class gFBDC: public gDC gSurface surface, surface_back; int m_enable_double_buffering; int m_xres, m_yres; -#else - gFBDC(); - virtual ~gFBDC(); -#endif public: void setResolution(int xres, int yres); -#ifndef SWIG void reloadSettings(); void setAlpha(int alpha); void setBrightness(int brightness); @@ -43,17 +33,7 @@ public: gFBDC(); virtual ~gFBDC(); - static int getInstance(ePtr<gFBDC> &ptr) { if (!instance) return -1; ptr = instance; return 0; } int islocked() { return fb->islocked(); } -#endif }; -SWIG_TEMPLATE_TYPEDEF(ePtr<gFBDC>, gFBDC); -SWIG_EXTEND(ePtr<gFBDC>, - static ePtr<gFBDC> getInstance() - { - extern ePtr<gFBDC> NewgFBDCPtr(void); - return NewgFBDCPtr(); - } -); #endif diff --git a/lib/gdi/glcddc.cpp b/lib/gdi/glcddc.cpp index 8612c70b..bade53ba 100644 --- a/lib/gdi/glcddc.cpp +++ b/lib/gdi/glcddc.cpp @@ -14,13 +14,13 @@ gLCDDC::gLCDDC() surface.x=lcd->size().width(); surface.y=lcd->size().height(); - surface.bpp=8; - surface.bypp=1; surface.stride=lcd->stride(); + surface.bypp=surface.stride / surface.x; + surface.bpp=surface.bypp*8; surface.data=lcd->buffer(); - - surface.clut.colors=256; + surface.clut.colors=0; surface.clut.data=0; + m_pixmap = new gPixmap(&surface); } @@ -30,7 +30,7 @@ gLCDDC::~gLCDDC() instance=0; } -void gLCDDC::exec(gOpcode *o) +void gLCDDC::exec(const gOpcode *o) { switch (o->opcode) { diff --git a/lib/gdi/glcddc.h b/lib/gdi/glcddc.h index 6d5f3834..a2b3c7f0 100644 --- a/lib/gdi/glcddc.h +++ b/lib/gdi/glcddc.h @@ -9,7 +9,7 @@ class gLCDDC: public gDC eLCD *lcd; static gLCDDC *instance; int update; - void exec(gOpcode *opcode); + void exec(const gOpcode *opcode); gSurface surface; public: gLCDDC(); diff --git a/lib/gdi/gmaindc.cpp b/lib/gdi/gmaindc.cpp new file mode 100644 index 00000000..4890027f --- /dev/null +++ b/lib/gdi/gmaindc.cpp @@ -0,0 +1,28 @@ +#include <lib/gdi/gmaindc.h> + +gMainDC *gMainDC::m_instance; + +ePtr<gMainDC> NewgMainDCPtr(void) +{ + ePtr<gMainDC> ptr; + gMainDC::getInstance(ptr); + return ptr; +} + +gMainDC::gMainDC() +{ + ASSERT(m_instance == 0); + m_instance = this; +} + +gMainDC::gMainDC(gPixmap *pixmap) : gDC(pixmap) +{ + ASSERT(m_instance == 0); + m_instance = this; +} + +gMainDC::~gMainDC() +{ + m_instance = 0; +} + diff --git a/lib/gdi/gmaindc.h b/lib/gdi/gmaindc.h new file mode 100644 index 00000000..780be2e2 --- /dev/null +++ b/lib/gdi/gmaindc.h @@ -0,0 +1,33 @@ +#ifndef __lib_gdi_gmaindc_h +#define __lib_gdi_gmaindc_h + +#include "grc.h" + +class gMainDC; + +SWIG_IGNORE(gMainDC); +class gMainDC: public gDC +{ +protected: + static gMainDC *m_instance; + + gMainDC(); + gMainDC(gPixmap *pixmap); + virtual ~gMainDC(); +public: + virtual void setResolution(int xres, int yres) = 0; +#ifndef SWIG + static int getInstance(ePtr<gMainDC> &ptr) { if (!m_instance) return -1; ptr = m_instance; return 0; } +#endif +}; + +SWIG_TEMPLATE_TYPEDEF(ePtr<gMainDC>, gMainDC); +SWIG_EXTEND(ePtr<gMainDC>, + static ePtr<gMainDC> getInstance() + { + extern ePtr<gMainDC> NewgMainDCPtr(void); + return NewgMainDCPtr(); + } +); + +#endif diff --git a/lib/gdi/gpixmap.cpp b/lib/gdi/gpixmap.cpp index 3e643108..0e8d39ec 100644 --- a/lib/gdi/gpixmap.cpp +++ b/lib/gdi/gpixmap.cpp @@ -3,6 +3,11 @@ #include <lib/gdi/gpixmap.h> #include <lib/gdi/region.h> #include <lib/gdi/accel.h> +#include <byteswap.h> + +#ifndef BYTE_ORDER +#error "no BYTE_ORDER defined!" +#endif gLookup::gLookup() :size(0), lookup(0) @@ -154,6 +159,26 @@ void gPixmap::fill(const gRegion ®ion, const gColor &color) { for (int y=area.top(); y<area.bottom(); y++) memset(((__u8*)surface->data)+y*surface->stride+area.left(), color.color, area.width()); + } else if (surface->bpp == 16) + { + __u32 icol; + + if (surface->clut.data && color < surface->clut.colors) + icol=(surface->clut.data[color].a<<24)|(surface->clut.data[color].r<<16)|(surface->clut.data[color].g<<8)|(surface->clut.data[color].b); + else + icol=0x10101*color; +#if BYTE_ORDER == LITTLE_ENDIAN + __u16 col = bswap_16(((icol & 0xFF) >> 3) << 11 | ((icol & 0xFF00) >> 10) << 5 | (icol & 0xFF0000) >> 19); +#else + __u16 col = ((icol & 0xFF) >> 3) << 11 | ((icol & 0xFF00) >> 10) << 5 | (icol & 0xFF0000) >> 19; +#endif + for (int y=area.top(); y<area.bottom(); y++) + { + __u16 *dst=(__u16*)(((__u8*)surface->data)+y*surface->stride+area.left()*surface->bypp); + int x=area.width(); + while (x--) + *dst++=col; + } } else if (surface->bpp == 32) { __u32 col; @@ -208,18 +233,33 @@ void gPixmap::fill(const gRegion ®ion, const gRGB &color) while (x--) *dst++=col; } + } else if (surface->bpp == 16) + { + __u32 icol = color.argb(); +#if BYTE_ORDER == LITTLE_ENDIAN + __u16 col = bswap_16(((icol & 0xFF) >> 3) << 11 | ((icol & 0xFF00) >> 10) << 5 | (icol & 0xFF0000) >> 19); +#else + __u16 col = ((icol & 0xFF) >> 3) << 11 | ((icol & 0xFF00) >> 10) << 5 | (icol & 0xFF0000) >> 19; +#endif + for (int y=area.top(); y<area.bottom(); y++) + { + __u16 *dst=(__u16*)(((__u8*)surface->data)+y*surface->stride+area.left()*surface->bypp); + int x=area.width(); + while (x--) + *dst++=col; + } } else eWarning("couldn't rgbfill %d bpp", surface->bpp); } } -static void blit_8i_to_32(__u32 *dst, __u8 *src, __u32 *pal, int width) +static inline void blit_8i_to_32(__u32 *dst, __u8 *src, __u32 *pal, int width) { while (width--) *dst++=pal[*src++]; } -static void blit_8i_to_32_at(__u32 *dst, __u8 *src, __u32 *pal, int width) +static inline void blit_8i_to_32_at(__u32 *dst, __u8 *src, __u32 *pal, int width) { while (width--) { @@ -232,6 +272,25 @@ static void blit_8i_to_32_at(__u32 *dst, __u8 *src, __u32 *pal, int width) } } +static inline void blit_8i_to_16(__u16 *dst, __u8 *src, __u32 *pal, int width) +{ + while (width--) + *dst++=pal[*src++] & 0xFFFF; +} + +static inline void blit_8i_to_16_at(__u16 *dst, __u8 *src, __u32 *pal, int width) +{ + while (width--) + { + if (!(pal[*src]&0x80000000)) + { + src++; + dst++; + } else + *dst++=pal[*src++] & 0xFFFF; + } +} + /* WARNING, this function is not endian safe! */ static void blit_8i_to_32_ab(__u32 *dst, __u8 *src, __u32 *pal, int width) { @@ -441,6 +500,95 @@ void gPixmap::blit(const gPixmap &src, const eRect &_pos, const gRegion &clip, i srcptr+=src.surface->stride; dstptr+=surface->stride; } + } else if ((surface->bpp == 16) && (src.surface->bpp==8)) + { + __u8 *srcptr=(__u8*)src.surface->data; + __u8 *dstptr=(__u8*)surface->data; // !! + __u32 pal[256]; + + for (int i=0; i<256; ++i) + { + __u32 icol; + if (src.surface->clut.data && (i<src.surface->clut.colors)) + icol=(src.surface->clut.data[i].a<<24)|(src.surface->clut.data[i].r<<16)|(src.surface->clut.data[i].g<<8)|(src.surface->clut.data[i].b); + else + icol=0x010101*i; +#if BYTE_ORDER == LITTLE_ENDIAN + pal[i] = bswap_16(((icol & 0xFF) >> 3) << 11 | ((icol & 0xFF00) >> 10) << 5 | (icol & 0xFF0000) >> 19); +#else + pal[i] = ((icol & 0xFF) >> 3) << 11 | ((icol & 0xFF00) >> 10) << 5 | (icol & 0xFF0000) >> 19; +#endif + pal[i]^=0xFF000000; + } + + srcptr+=srcarea.left()*src.surface->bypp+srcarea.top()*src.surface->stride; + dstptr+=area.left()*surface->bypp+area.top()*surface->stride; + + if (flag & blitAlphaBlend) + eWarning("ignore unsupported 8bpp -> 16bpp alphablend!"); + + for (int y=0; y<area.height(); y++) + { + int width=area.width(); + unsigned char *psrc=(unsigned char*)srcptr; + __u16 *dst=(__u16*)dstptr; + if (flag & blitAlphaTest) + blit_8i_to_16_at(dst, psrc, pal, width); + else + blit_8i_to_16(dst, psrc, pal, width); + srcptr+=src.surface->stride; + dstptr+=surface->stride; + } + } else if ((surface->bpp == 16) && (src.surface->bpp==32)) + { + __u8 *srcptr=(__u8*)src.surface->data; + __u8 *dstptr=(__u8*)surface->data; + + srcptr+=srcarea.left()+srcarea.top()*src.surface->stride; + dstptr+=area.left()+area.top()*surface->stride; + + if (flag & blitAlphaBlend) + eWarning("ignore unsupported 32bpp -> 16bpp alphablend!"); + + for (int y=0; y<area.height(); y++) + { + int width=area.width(); + __u32 *srcp=(__u32*)srcptr; + __u16 *dstp=(__u16*)dstptr; + + if (flag & blitAlphaTest) + { + while (width--) + { + if (!((*srcp)&0xFF000000)) + { + srcp++; + dstp++; + } else + { + __u32 icol = *srcp++; +#if BYTE_ORDER == LITTLE_ENDIAN + *dstp++ = bswap_16(((icol & 0xFF) >> 3) << 11 | ((icol & 0xFF00) >> 10) << 5 | (icol & 0xFF0000) >> 19); +#else + *dstp++ = ((icol & 0xFF) >> 3) << 11 | ((icol & 0xFF00) >> 10) << 5 | (icol & 0xFF0000) >> 19; +#endif + } + } + } else + { + while (width--) + { + __u32 icol = *srcp++; +#if BYTE_ORDER == LITTLE_ENDIAN + *dstp++ = bswap_16(((icol & 0xFF) >> 3) << 11 | ((icol & 0xFF00) >> 10) << 5 | (icol & 0xFF0000) >> 19); +#else + *dstp++ = ((icol & 0xFF) >> 3) << 11 | ((icol & 0xFF00) >> 10) << 5 | (icol & 0xFF0000) >> 19; +#endif + } + } + srcptr+=src.surface->stride; + dstptr+=surface->stride; + } } else eWarning("cannot blit %dbpp from %dbpp", surface->bpp, src.surface->bpp); } @@ -488,27 +636,34 @@ static inline int sgn(int a) void gPixmap::line(const gRegion &clip, ePoint start, ePoint dst, gColor color) { __u8 *srf8 = 0; - __u32 *srf32 = 0; + __u16 *srf16 = 0; + __u32 *srf32 = 0; int stride = surface->stride; - + if (clip.rects.empty()) return; - + + __u16 col16; __u32 col = 0; if (surface->bpp == 8) - { srf8 = (__u8*)surface->data; - } else if (surface->bpp == 32) + else { srf32 = (__u32*)surface->data; - if (surface->clut.data && color < surface->clut.colors) col=(surface->clut.data[color].a<<24)|(surface->clut.data[color].r<<16)|(surface->clut.data[color].g<<8)|(surface->clut.data[color].b); else col=0x10101*color; - col^=0xFF000000; + col^=0xFF000000; } - + + if (surface->bpp == 16) +#if BYTE_ORDER == LITTLE_ENDIAN + col16=bswap_16(((col & 0xFF) >> 3) << 11 | ((col & 0xFF00) >> 10) << 5 | (col & 0xFF0000) >> 19); +#else + col16=((col & 0xFF) >> 3) << 11 | ((col & 0xFF00) >> 10) << 5 | (col & 0xFF0000) >> 19; +#endif + int xa = start.x(), ya = start.y(), xb = dst.x(), yb = dst.y(); int dx, dy, x, y, s1, s2, e, temp, swap, i; dy=abs(yb-ya); @@ -526,7 +681,7 @@ void gPixmap::line(const gRegion &clip, ePoint start, ePoint dst, gColor color) } else swap=0; e = 2*dy-dx; - + int lasthit = 0; for(i=1; i<=dx; i++) { @@ -563,20 +718,25 @@ void gPixmap::line(const gRegion &clip, ePoint start, ePoint dst, gColor color) } while (!clip.rects[a].contains(x, y)); lasthit = a; } - + if (srf8) srf8[y * stride + x] = color; - if (srf32) + else if (srf16) + srf16[y * stride/2 + x] = col16; + else srf32[y * stride/4 + x] = col; fail: while (e>=0) { - if (swap==1) x+=s1; - else y+=s2; + if (swap==1) + x+=s1; + else + y+=s2; e-=2*dx; } - if (swap==1) - y+=s2; + + if (swap==1) + y+=s2; else x+=s1; e+=2*dy; diff --git a/lib/gdi/grc.cpp b/lib/gdi/grc.cpp index dff6b059..a45b3b1e 100644 --- a/lib/gdi/grc.cpp +++ b/lib/gdi/grc.cpp @@ -609,7 +609,7 @@ gDC::~gDC() delete[] m_spinner_pic; } -void gDC::exec(gOpcode *o) +void gDC::exec(const gOpcode *o) { switch (o->opcode) { diff --git a/lib/gdi/grc.h b/lib/gdi/grc.h index 3b8201ad..38caa107 100644 --- a/lib/gdi/grc.h +++ b/lib/gdi/grc.h @@ -286,7 +286,7 @@ protected: eRect m_spinner_pos; int m_spinner_num, m_spinner_i; public: - virtual void exec(gOpcode *opcode); + virtual void exec(const gOpcode *opcode); gDC(gPixmap *pixmap); gDC(); virtual ~gDC(); @@ -296,10 +296,10 @@ public: virtual eSize size() { return m_pixmap->size(); } virtual int islocked() { return 0; } - void enableSpinner(); - void disableSpinner(); - void incrementSpinner(); - void setSpinner(eRect pos, ePtr<gPixmap> *pic, int len); + virtual void enableSpinner(); + virtual void disableSpinner(); + virtual void incrementSpinner(); + virtual void setSpinner(eRect pos, ePtr<gPixmap> *pic, int len); }; #endif diff --git a/lib/gdi/lcd.cpp b/lib/gdi/lcd.cpp index ecc19b53..518ee1b0 100644 --- a/lib/gdi/lcd.cpp +++ b/lib/gdi/lcd.cpp @@ -4,8 +4,12 @@ #include <fcntl.h> #include <sys/ioctl.h> +#if defined(HAVE_DBOX_FP_H) && defined(HAVE_DBOX_LCD_KS0713_H) #include <dbox/fp.h> #include <dbox/lcd-ks0713.h> +#else +#define NO_LCD 1 +#endif #include <lib/gdi/esize.h> #include <lib/base/init.h> @@ -14,13 +18,19 @@ eDBoxLCD *eDBoxLCD::instance; -eLCD::eLCD(eSize size): res(size) +eLCD::eLCD() { lcdfd = -1; locked=0; - _buffer=new unsigned char[res.height()*res.width()]; - memset(_buffer, 0, res.height()*res.width()); - _stride=res.width(); +} + +void eLCD::setSize(int xres, int yres, int bpp) +{ + res = eSize(xres, yres); + _buffer=new unsigned char[xres * yres * bpp/8]; + memset(_buffer, 0, res.height()*res.width()*bpp/8); + _stride=res.width()*bpp/8; + eDebug("lcd buffer %p %d bytes, stride %d", _buffer, xres*yres*bpp/8, _stride); } eLCD::~eLCD() @@ -42,14 +52,17 @@ void eLCD::unlock() locked=0; } -eDBoxLCD::eDBoxLCD(): eLCD(eSize(132, 64)) +eDBoxLCD::eDBoxLCD() { + int xres=132, yres=64, bpp=8; is_oled = 0; #ifndef NO_LCD lcdfd = open("/dev/dbox/oled0", O_RDWR); if (lcdfd < 0) { - FILE *f=fopen("/proc/stb/fp/oled_brightness", "w"); + FILE *f=fopen("/proc/stb/lcd/oled_brightness", "w"); + if (!f) + f = fopen("/proc/stb/fp/oled_brightness", "w"); if (f) { is_oled = 2; @@ -61,10 +74,6 @@ eDBoxLCD::eDBoxLCD(): eLCD(eSize(132, 64)) eDebug("found OLED display!"); is_oled = 1; } -#else - lcdfd = -1; -#endif - instance=this; if (lcdfd<0) eDebug("couldn't open LCD - load lcd.o!"); @@ -73,17 +82,45 @@ eDBoxLCD::eDBoxLCD(): eLCD(eSize(132, 64)) int i=LCD_MODE_BIN; ioctl(lcdfd, LCD_IOCTL_ASC_MODE, &i); inverted=0; + FILE *f = fopen("/proc/stb/lcd/xres", "r"); + if (f) + { + int tmp; + if (fscanf(f, "%x", &tmp) == 1) + xres = tmp; + fclose(f); + f = fopen("/proc/stb/lcd/yres", "r"); + if (f) + { + if (fscanf(f, "%x", &tmp) == 1) + yres = tmp; + fclose(f); + f = fopen("/proc/stb/lcd/bpp", "r"); + if (f) + { + if (fscanf(f, "%x", &tmp) == 1) + bpp = tmp; + fclose(f); + } + } + is_oled = 3; + } } +#endif + instance=this; + + setSize(xres, yres, bpp); } void eDBoxLCD::setInverted(unsigned char inv) { inverted=inv; - update(); + update(); } int eDBoxLCD::setLCDContrast(int contrast) { +#ifndef NO_LCD int fp; if((fp=open("/dev/dbox/fp0", O_RDWR))<=0) { @@ -96,17 +133,21 @@ int eDBoxLCD::setLCDContrast(int contrast) eDebug("[LCD] can't set lcd contrast"); } close(fp); +#endif return(0); } int eDBoxLCD::setLCDBrightness(int brightness) { +#ifndef NO_LCD eDebug("setLCDBrightness %d", brightness); - FILE *f=fopen("/proc/stb/fp/oled_brightness", "w"); + FILE *f=fopen("/proc/stb/lcd/oled_brightness", "w"); + if (!f) + f = fopen("/proc/stb/fp/oled_brightness", "w"); if (f) { if (fprintf(f, "%d", brightness) == 0) - eDebug("write /proc/stb/fp/oled_brightness failed!! (%m)"); + eDebug("write /proc/stb/lcd/oled_brightness failed!! (%m)"); fclose(f); } else @@ -122,6 +163,7 @@ int eDBoxLCD::setLCDBrightness(int brightness) eDebug("[LCD] can't set lcd brightness (%m)"); close(fp); } +#endif return(0); } @@ -141,42 +183,46 @@ eDBoxLCD *eDBoxLCD::getInstance() void eDBoxLCD::update() { - if (!is_oled || is_oled == 2) + if (lcdfd >= 0) { - unsigned char raw[132*8]; - int x, y, yy; - for (y=0; y<8; y++) + if (!is_oled || is_oled == 2) { - for (x=0; x<132; x++) + unsigned char raw[132*8]; + int x, y, yy; + for (y=0; y<8; y++) { - int pix=0; - for (yy=0; yy<8; yy++) + for (x=0; x<132; x++) { - pix|=(_buffer[(y*8+yy)*132+x]>=108)<<yy; + int pix=0; + for (yy=0; yy<8; yy++) + { + pix|=(_buffer[(y*8+yy)*132+x]>=108)<<yy; + } + raw[y*132+x]=(pix^inverted); } - raw[y*132+x]=(pix^inverted); } - } - if (lcdfd >= 0) write(lcdfd, raw, 132*8); - } else - { - unsigned char raw[64*64]; - int x, y; - memset(raw, 0, 64*64); - for (y=0; y<64; y++) + } + else if (is_oled == 3) + write(lcdfd, _buffer, _stride * res.height()); + else { - int pix=0; - for (x=0; x<128 / 2; x++) + unsigned char raw[64*64]; + int x, y; + memset(raw, 0, 64*64); + for (y=0; y<64; y++) { - pix = (_buffer[y*132 + x * 2 + 2] & 0xF0) |(_buffer[y*132 + x * 2 + 1 + 2] >> 4); - if (inverted) - pix = 0xFF - pix; - raw[y*64+x] = pix; + int pix=0; + for (x=0; x<128 / 2; x++) + { + pix = (_buffer[y*132 + x * 2 + 2] & 0xF0) |(_buffer[y*132 + x * 2 + 1 + 2] >> 4); + if (inverted) + pix = 0xFF - pix; + raw[y*64+x] = pix; + } } - } - if (lcdfd >= 0) write(lcdfd, raw, 64*64); + } } } diff --git a/lib/gdi/lcd.h b/lib/gdi/lcd.h index 99f921b5..e7b4c2c4 100644 --- a/lib/gdi/lcd.h +++ b/lib/gdi/lcd.h @@ -13,10 +13,11 @@ class eLCD { #ifdef SWIG - eLCD(eSize size); + eLCD(); ~eLCD(); #else protected: + void setSize(int xres, int yres, int bpp); eSize res; unsigned char *_buffer; int lcdfd; @@ -29,12 +30,11 @@ public: int islocked() { return locked; } bool detected() { return lcdfd >= 0; } #ifndef SWIG - eLCD(eSize size); + eLCD(); virtual ~eLCD(); __u8 *buffer() { return (__u8*)_buffer; } int stride() { return _stride; } eSize size() { return res; } - virtual void update()=0; #endif }; diff --git a/lib/gdi/picexif.cpp b/lib/gdi/picexif.cpp index f9e8055f..1eacc29c 100644 --- a/lib/gdi/picexif.cpp +++ b/lib/gdi/picexif.cpp @@ -90,9 +90,9 @@ void Cexif::ClearExif() bool Cexif::DecodeExif(const char *filename, int Thumb) { + bool ret = false; FILE * hFile = fopen(filename, "r"); - if(!hFile) return false; - + if(!hFile) return ret; m_exifinfo = new EXIFINFO; memset(m_exifinfo,0,sizeof(EXIFINFO)); @@ -107,7 +107,8 @@ bool Cexif::DecodeExif(const char *filename, int Thumb) int a = fgetc(hFile); strcpy(m_szLastError,"EXIF-Data not found"); - if (a != 0xff || fgetc(hFile) != M_SOI) return false; + if (a != 0xff || fgetc(hFile) != M_SOI) + goto decode_exif_out_false; for(;;) { @@ -117,7 +118,8 @@ bool Cexif::DecodeExif(const char *filename, int Thumb) if (SectionsRead >= MAX_SECTIONS) { - strcpy(m_szLastError,"Too many sections in jpg file"); return false; + strcpy(m_szLastError,"Too many sections in jpg file"); + goto decode_exif_out_false; } for (a=0;a<7;a++) @@ -127,13 +129,15 @@ bool Cexif::DecodeExif(const char *filename, int Thumb) if (a >= 6) { - strcpy(m_szLastError,"too many padding unsigned chars\n"); return false; + strcpy(m_szLastError,"too many padding unsigned chars\n"); + goto decode_exif_out_false; } } if (marker == 0xff) { - strcpy(m_szLastError,"too many padding unsigned chars!"); return false; + strcpy(m_szLastError,"too many padding unsigned chars!"); + goto decode_exif_out_false; } Sections[SectionsRead].Type = marker; @@ -145,14 +149,16 @@ bool Cexif::DecodeExif(const char *filename, int Thumb) if (itemlen < 2) { - strcpy(m_szLastError,"invalid marker"); return false; + strcpy(m_szLastError,"invalid marker"); + goto decode_exif_out_false; } Sections[SectionsRead].Size = itemlen; Data = (unsigned char *)malloc(itemlen); if (Data == NULL) { - strcpy(m_szLastError,"Could not allocate memory"); return false; + strcpy(m_szLastError,"Could not allocate memory"); + goto decode_exif_out_false; } Sections[SectionsRead].Data = Data; @@ -163,17 +169,18 @@ bool Cexif::DecodeExif(const char *filename, int Thumb) got = fread(Data+2, 1, itemlen-2,hFile); if (got != itemlen-2) { - strcpy(m_szLastError,"Premature end of file?"); return false; + strcpy(m_szLastError,"Premature end of file?"); + goto decode_exif_out_false; } SectionsRead += 1; switch(marker) { case M_SOS: - return true; + goto decode_exif_out_true; case M_EOI: printf("No image in jpeg!\n"); - return false; + goto decode_exif_out_false; case M_COM: if (HaveCom) { @@ -221,8 +228,12 @@ bool Cexif::DecodeExif(const char *filename, int Thumb) } } +decode_exif_out_true: + ret = true; + +decode_exif_out_false: fclose(hFile); - return true; + return ret; } bool Cexif::process_EXIF(unsigned char * CharBuf, unsigned int length) @@ -428,7 +439,7 @@ bool Cexif::ProcessExifDir(unsigned char * DirStart, unsigned char * OffsetBase, case 6: strcpy(m_exifinfo->Orientation,"Right-Top"); break; case 7: strcpy(m_exifinfo->Orientation,"Right-Bottom"); break; case 8: strcpy(m_exifinfo->Orientation,"Left-Bottom"); break; - default: strcpy(m_exifinfo->Orientation,"Undefined rotation value"); + default: strcpy(m_exifinfo->Orientation,"Undefined"); break; } break; case TAG_EXIF_IMAGELENGTH: diff --git a/lib/gdi/picload.cpp b/lib/gdi/picload.cpp index c162f89d..0ce10c09 100644 --- a/lib/gdi/picload.cpp +++ b/lib/gdi/picload.cpp @@ -1,4 +1,5 @@ -#include <png.h> // must be included before Python.h because of setjmp +#define PNG_SKIP_SETJMP_CHECK +#include <png.h> #include <fcntl.h> #include <lib/gdi/picload.h> diff --git a/lib/gdi/region.cpp b/lib/gdi/region.cpp index 1208c97e..73d9ac2d 100644 --- a/lib/gdi/region.cpp +++ b/lib/gdi/region.cpp @@ -428,7 +428,7 @@ void gRegion::moveBy(ePoint offset) void gRegion::scale(int x_n, int x_d, int y_n, int y_d) { - int i; + unsigned int i; for (i=0; i<rects.size(); ++i) rects[i].scale(x_n, x_d, y_n, y_d); } diff --git a/lib/gdi/sdl.cpp b/lib/gdi/sdl.cpp index 6dbdb67c..eb4e2ae4 100644 --- a/lib/gdi/sdl.cpp +++ b/lib/gdi/sdl.cpp @@ -1,80 +1,83 @@ -#ifdef WITH_SDL #include <lib/gdi/sdl.h> - +#include <lib/actions/action.h> #include <lib/base/init.h> #include <lib/base/init_num.h> +#include <lib/driver/input_fake.h> +#include <lib/driver/rcsdl.h> #include <SDL.h> -gSDLDC *gSDLDC::m_instance; - -gSDLDC::gSDLDC() +gSDLDC::gSDLDC() : m_pump(eApp, 1) { - if (SDL_Init(SDL_INIT_VIDEO) < 0) - { + if (SDL_Init(SDL_INIT_VIDEO) < 0) { eWarning("Could not initialize SDL: %s", SDL_GetError()); return; } - - m_screen = SDL_SetVideoMode(720, 576, 32, SDL_HWSURFACE); - if (!m_screen) - { - eWarning("Could not create SDL surface: %s", SDL_GetError()); - return; - } - m_instance=this; - + setResolution(720, 576); + + CONNECT(m_pump.recv_msg, gSDLDC::pumpEvent); + m_surface.type = 0; - m_surface.x = m_screen->w; - m_surface.y = m_screen->h; - m_surface.bpp = m_screen->format->BitsPerPixel; - m_surface.bypp = m_screen->format->BytesPerPixel; - m_surface.stride = m_screen->pitch; - m_surface.data = m_screen->pixels; - m_surface.clut.colors=256; - m_surface.clut.data=new gRGB[m_surface.clut.colors]; - + m_surface.clut.colors = 256; + m_surface.clut.data = new gRGB[m_surface.clut.colors]; + m_pixmap = new gPixmap(&m_surface); - + memset(m_surface.clut.data, 0, sizeof(*m_surface.clut.data)*m_surface.clut.colors); + + run(); } gSDLDC::~gSDLDC() { + pushEvent(EV_QUIT); + kill(); SDL_Quit(); - m_instance=0; } -void gSDLDC::setPalette() +void gSDLDC::keyEvent(const SDL_Event &event) { - if (!m_surface.clut.data) - return; - -/* for (int i=0; i<256; ++i) - { - fb->CMAP()->red[i]=ramp[m_surface.clut.data[i].r]<<8; - fb->CMAP()->green[i]=ramp[m_surface.clut.data[i].g]<<8; - fb->CMAP()->blue[i]=ramp[m_surface.clut.data[i].b]<<8; - fb->CMAP()->transp[i]=rampalpha[m_surface.clut.data[i].a]<<8; - if (!fb->CMAP()->red[i]) - fb->CMAP()->red[i]=0x100; - } - fb->PutCMAP(); */ + eSDLInputDriver *driver = eSDLInputDriver::getInstance(); + + eDebug("SDL Key %s: key=%d", (event.type == SDL_KEYDOWN) ? "Down" : "Up", event.key.keysym.sym); + + if (driver) + driver->keyPressed(&event.key); } -void gSDLDC::exec(gOpcode *o) +void gSDLDC::pumpEvent(const SDL_Event &event) { - switch (o->opcode) - { - case gOpcode::setPalette: - { - gDC::exec(o); - setPalette(); + switch (event.type) { + case SDL_KEYDOWN: + case SDL_KEYUP: + keyEvent(event); + break; + case SDL_QUIT: + eDebug("SDL Quit"); + extern void quitMainloop(int exit_code); + quitMainloop(0); break; } +} + +void gSDLDC::pushEvent(enum event code, void *data1, void *data2) +{ + SDL_Event event; + + event.type = SDL_USEREVENT; + event.user.code = code; + event.user.data1 = data1; + event.user.data2 = data2; + + SDL_PushEvent(&event); +} + +void gSDLDC::exec(const gOpcode *o) +{ + switch (o->opcode) { case gOpcode::flush: - SDL_Flip(m_screen); + pushEvent(EV_FLIP); eDebug("FLUSH"); break; default: @@ -83,6 +86,68 @@ void gSDLDC::exec(gOpcode *o) } } -eAutoInitPtr<gSDLDC> init_gSDLDC(eAutoInitNumbers::graphic-1, "gSDLDC"); +void gSDLDC::setResolution(int xres, int yres) +{ + pushEvent(EV_SET_VIDEO_MODE, (void *)xres, (void *)yres); +} + +/* + * SDL thread below... + */ + +void gSDLDC::evSetVideoMode(unsigned long xres, unsigned long yres) +{ + m_screen = SDL_SetVideoMode(xres, yres, 32, SDL_HWSURFACE); + if (!m_screen) { + eFatal("Could not create SDL surface: %s", SDL_GetError()); + return; + } -#endif + m_surface.x = m_screen->w; + m_surface.y = m_screen->h; + m_surface.bpp = m_screen->format->BitsPerPixel; + m_surface.bypp = m_screen->format->BytesPerPixel; + m_surface.stride = m_screen->pitch; + m_surface.data = m_screen->pixels; + + SDL_EnableUNICODE(1); +} + +void gSDLDC::evFlip() +{ + SDL_Flip(m_screen); +} + +void gSDLDC::thread() +{ + hasStarted(); + + bool stop = false; + while (!stop) { + SDL_Event event; + if (SDL_WaitEvent(&event)) { + switch (event.type) { + case SDL_KEYDOWN: + case SDL_KEYUP: + case SDL_QUIT: + m_pump.send(event); + break; + case SDL_USEREVENT: + switch (event.user.code) { + case EV_SET_VIDEO_MODE: + evSetVideoMode((unsigned long)event.user.data1, (unsigned long)event.user.data2); + break; + case EV_FLIP: + evFlip(); + break; + case EV_QUIT: + stop = true; + break; + } + break; + } + } + } +} + +eAutoInitPtr<gSDLDC> init_gSDLDC(eAutoInitNumbers::graphic-1, "gSDLDC"); diff --git a/lib/gdi/sdl.h b/lib/gdi/sdl.h index 20ff04fa..6d016cf9 100644 --- a/lib/gdi/sdl.h +++ b/lib/gdi/sdl.h @@ -1,27 +1,39 @@ #ifndef __lib_gdi_sdl_h #define __lib_gdi_sdl_h -#include "fb.h" -#include "gpixmap.h" -#include "grc.h" +#include <lib/base/thread.h> +#include <lib/gdi/gmaindc.h> #include <SDL.h> -class gSDLDC: public gDC +class gSDLDC: public gMainDC, public eThread, public Object { +private: SDL_Surface *m_screen; - static gSDLDC *m_instance; - void exec(gOpcode *opcode); + void exec(const gOpcode *opcode); - void setPalette(); gSurface m_surface; + + eFixedMessagePump<SDL_Event> m_pump; + void keyEvent(const SDL_Event &event); + void pumpEvent(const SDL_Event &event); + virtual void thread(); + + enum event { + EV_SET_VIDEO_MODE, + EV_FLIP, + EV_QUIT, + }; + + void pushEvent(enum event code, void *data1 = 0, void *data2 = 0); + void evSetVideoMode(unsigned long xres, unsigned long yres); + void evFlip(); + public: - + void setResolution(int xres, int yres); gSDLDC(); virtual ~gSDLDC(); - static int getInstance(ePtr<gSDLDC> &ptr) { if (!m_instance) return -1; ptr = m_instance; return 0; } int islocked() { return 0; } }; - #endif diff --git a/lib/gui/Makefile.am b/lib/gui/Makefile.am index f4de9d12..9d31bf73 100644 --- a/lib/gui/Makefile.am +++ b/lib/gui/Makefile.am @@ -1,11 +1,50 @@ -INCLUDES = \ +AM_CPPFLAGS = \ + -I$(top_srcdir) \ -I$(top_srcdir)/include \ - -I$(top_srcdir)/src + -include Python.h \ + -include $(top_builddir)/enigma2_config.h noinst_LIBRARIES = libenigma_gui.a libenigma_gui_a_SOURCES = \ - ebutton.cpp elabel.cpp eslider.cpp ewidget.cpp ewidgetdesktop.cpp \ - ewindow.cpp ewindowstyle.cpp elistbox.cpp elistboxcontent.cpp \ - epixmap.cpp ewindowstyleskinned.cpp einput.cpp einputstring.cpp einputnumber.cpp \ - ewidgetanimation.cpp epositiongauge.cpp evideo.cpp esubtitle.cpp ecanvas.cpp + ebutton.cpp \ + ecanvas.cpp \ + einput.cpp \ + einputnumber.cpp \ + einputstring.cpp \ + elabel.cpp \ + elistbox.cpp \ + elistboxcontent.cpp \ + epixmap.cpp \ + epositiongauge.cpp \ + eslider.cpp \ + esubtitle.cpp \ + evideo.cpp \ + ewidget.cpp \ + ewidgetanimation.cpp \ + ewidgetdesktop.cpp \ + ewindow.cpp \ + ewindowstyle.cpp \ + ewindowstyleskinned.cpp + +guiincludedir = $(pkgincludedir)/lib/gui +guiinclude_HEADERS = \ + ebutton.h \ + ecanvas.h \ + einput.h \ + einputnumber.h \ + einputstring.h \ + elabel.h \ + elistbox.h \ + elistboxcontent.h \ + epixmap.h \ + epositiongauge.h \ + eslider.h \ + esubtitle.h \ + evideo.h \ + ewidget.h \ + ewidgetanimation.h \ + ewidgetdesktop.h \ + ewindow.h \ + ewindowstyle.h \ + ewindowstyleskinned.h diff --git a/lib/gui/elistboxcontent.cpp b/lib/gui/elistboxcontent.cpp index 4b4b58c1..ba0794e7 100644 --- a/lib/gui/elistboxcontent.cpp +++ b/lib/gui/elistboxcontent.cpp @@ -49,7 +49,8 @@ int iListboxContent::currentCursorSelectable() DEFINE_REF(eListboxPythonStringContent); -eListboxPythonStringContent::eListboxPythonStringContent(): m_itemheight(25), m_cursor(0) +eListboxPythonStringContent::eListboxPythonStringContent() + :m_cursor(0), m_itemheight(25) { } @@ -81,7 +82,7 @@ int eListboxPythonStringContent::cursorMove(int count) int eListboxPythonStringContent::cursorValid() { - return ((unsigned int)m_cursor) < size(); + return m_cursor < size(); } int eListboxPythonStringContent::cursorSet(int n) @@ -342,7 +343,6 @@ void eListboxPythonConfigContent::paint(gPainter &painter, eWindowStyle &style, if (PyTuple_Check(item)) { /* handle left part. get item from tuple, convert to string, display. */ - text = PyTuple_GET_ITEM(item, 0); text = PyObject_Str(text); /* creates a new object - old object was borrowed! */ const char *string = (text && PyString_Check(text)) ? PyString_AsString(text) : "<not-a-string>"; @@ -356,7 +356,9 @@ void eListboxPythonConfigContent::paint(gPainter &painter, eWindowStyle &style, int value_alignment_left = !*string; /* now, handle the value. get 2nd part from tuple*/ - value = PyTuple_GET_ITEM(item, 1); + if (PyTuple_Size(item) >= 2) // when no 2nd entry is in tuple this is a non selectable entry without config part + value = PyTuple_GET_ITEM(item, 1); + if (value) { ePyObject args = PyTuple_New(1); @@ -467,7 +469,7 @@ void eListboxPythonConfigContent::paint(gPainter &painter, eWindowStyle &style, } } /* type is borrowed */ - } else + } else if (value) eWarning("eListboxPythonConfigContent: second value of tuple is not a tuple."); if (value) Py_DECREF(value); @@ -499,6 +501,7 @@ eListboxPythonMultiContent::~eListboxPythonMultiContent() { Py_XDECREF(m_buildFunc); Py_XDECREF(m_selectableFunc); + Py_XDECREF(m_template); } void eListboxPythonMultiContent::setSelectionClip(eRect &rect, bool update) @@ -514,7 +517,7 @@ void eListboxPythonMultiContent::setSelectionClip(eRect &rect, bool update) m_listbox->entryChanged(m_cursor); } -static void clearRegionHelper(gPainter &painter, eListboxStyle *local_style, const ePoint &offset, ePyObject &pbackColor, bool cursorValid) +static void clearRegionHelper(gPainter &painter, eListboxStyle *local_style, const ePoint &offset, ePyObject &pbackColor, bool cursorValid, bool clear=true) { if (pbackColor) { @@ -536,10 +539,11 @@ static void clearRegionHelper(gPainter &painter, eListboxStyle *local_style, con else if (local_style->m_transparent_background) return; } - painter.clear(); + if (clear) + painter.clear(); } -static void clearRegionSelectedHelper(gPainter &painter, eListboxStyle *local_style, const ePoint &offset, ePyObject &pbackColorSelected, bool cursorValid) +static void clearRegionSelectedHelper(gPainter &painter, eListboxStyle *local_style, const ePoint &offset, ePyObject &pbackColorSelected, bool cursorValid, bool clear=true) { if (pbackColorSelected) { @@ -559,10 +563,11 @@ static void clearRegionSelectedHelper(gPainter &painter, eListboxStyle *local_st return; } } - painter.clear(); + if (clear) + painter.clear(); } -static void clearRegion(gPainter &painter, eWindowStyle &style, eListboxStyle *local_style, ePyObject pforeColor, ePyObject pforeColorSelected, ePyObject pbackColor, ePyObject pbackColorSelected, int selected, gRegion &rc, eRect &sel_clip, const ePoint &offset, bool cursorValid) +static void clearRegion(gPainter &painter, eWindowStyle &style, eListboxStyle *local_style, ePyObject pforeColor, ePyObject pforeColorSelected, ePyObject pbackColor, ePyObject pbackColorSelected, int selected, gRegion &rc, eRect &sel_clip, const ePoint &offset, bool cursorValid, bool clear=true) { if (selected && sel_clip.valid()) { @@ -571,7 +576,7 @@ static void clearRegion(gPainter &painter, eWindowStyle &style, eListboxStyle *l { painter.clip(part); style.setStyle(painter, eWindowStyle::styleListboxNormal); - clearRegionHelper(painter, local_style, offset, pbackColor, cursorValid); + clearRegionHelper(painter, local_style, offset, pbackColor, cursorValid, clear); painter.clippop(); selected = 0; } @@ -580,7 +585,7 @@ static void clearRegion(gPainter &painter, eWindowStyle &style, eListboxStyle *l { painter.clip(part); style.setStyle(painter, eWindowStyle::styleListboxSelected); - clearRegionSelectedHelper(painter, local_style, offset, pbackColorSelected, cursorValid); + clearRegionSelectedHelper(painter, local_style, offset, pbackColorSelected, cursorValid, clear); painter.clippop(); selected = 1; } @@ -588,14 +593,14 @@ static void clearRegion(gPainter &painter, eWindowStyle &style, eListboxStyle *l else if (selected) { style.setStyle(painter, eWindowStyle::styleListboxSelected); - clearRegionSelectedHelper(painter, local_style, offset, pbackColorSelected, cursorValid); + clearRegionSelectedHelper(painter, local_style, offset, pbackColorSelected, cursorValid, clear); if (local_style && local_style->m_selection) painter.blit(local_style->m_selection, offset, eRect(), gPainter::BT_ALPHATEST); } else { style.setStyle(painter, eWindowStyle::styleListboxNormal); - clearRegionHelper(painter, local_style, offset, pbackColor, cursorValid); + clearRegionHelper(painter, local_style, offset, pbackColor, cursorValid, clear); } if (selected) @@ -636,7 +641,8 @@ static ePyObject lookupColor(ePyObject color, ePyObject data) if ((icolor & 0xFF000000) == 0xFF000000) { int index = icolor & 0xFFFFFF; - eDebug("[eListboxPythonMultiContent] template color index: %d", index); + if (PyTuple_GetItem(data, index) == Py_None) + return ePyObject(); return PyTuple_GetItem(data, index); } @@ -818,7 +824,8 @@ void eListboxPythonMultiContent::paint(gPainter &painter, eWindowStyle &style, c { gRegion rc(rect); - clearRegion(painter, style, local_style, pforeColor, pforeColorSelected, pbackColor, pbackColorSelected, selected, rc, sel_clip, offset, cursorValid); + bool mustClear = (selected && pbackColorSelected) || (!selected && pbackColor); + clearRegion(painter, style, local_style, pforeColor, pforeColorSelected, pbackColor, pbackColorSelected, selected, rc, sel_clip, offset, cursorValid, mustClear); } painter.setFont(m_font[fnt]); @@ -852,6 +859,10 @@ void eListboxPythonMultiContent::paint(gPainter &painter, eWindowStyle &style, c } break; } + case TYPE_PROGRESS_PIXMAP: // Progress + /* + (1, x, y, width, height, filled_percent, pixmap [, borderWidth, foreColor, backColor, backColorSelected] ) + */ case TYPE_PROGRESS: // Progress { /* @@ -862,41 +873,55 @@ void eListboxPythonMultiContent::paint(gPainter &painter, eWindowStyle &style, c pwidth = PyTuple_GET_ITEM(item, 3), pheight = PyTuple_GET_ITEM(item, 4), pfilled_perc = PyTuple_GET_ITEM(item, 5), - pborderWidth, pforeColor, pforeColorSelected, pbackColor, pbackColorSelected; - - if (!(px && py && pwidth && pheight && pfilled_perc)) + ppixmap, pborderWidth, pforeColor, pforeColorSelected, pbackColor, pbackColorSelected; + int idx = 6; + if (type == TYPE_PROGRESS) { - eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_PROGRESS, x, y, width, height, filled percent [,border width, foreColor, backColor, backColorSelected]))"); - goto error_out; + if (!(px && py && pwidth && pheight && pfilled_perc)) + { + eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_PROGRESS, x, y, width, height, filled percent [,border width, foreColor, backColor, backColorSelected]))"); + goto error_out; + } + } + else + { + ppixmap = PyTuple_GET_ITEM(item, idx++); + if (ppixmap == Py_None) + continue; + if (!(px && py && pwidth && pheight && pfilled_perc, ppixmap)) + { + eDebug("eListboxPythonMultiContent received too small tuple (must be (TYPE_PROGRESS_PIXMAP, x, y, width, height, filled percent, pixmap, [,border width, foreColor, backColor, backColorSelected]))"); + goto error_out; + } } - if (size > 6) + if (size > idx) { - pborderWidth = PyTuple_GET_ITEM(item, 6); + pborderWidth = PyTuple_GET_ITEM(item, idx++); if (pborderWidth == Py_None) pborderWidth = ePyObject(); } - if (size > 7) + if (size > idx) { - pforeColor = PyTuple_GET_ITEM(item, 7); + pforeColor = PyTuple_GET_ITEM(item, idx++); if (pforeColor == Py_None) pforeColor = ePyObject(); } - if (size > 8) + if (size > idx) { - pforeColorSelected = PyTuple_GET_ITEM(item, 8); + pforeColorSelected = PyTuple_GET_ITEM(item, idx++); if (pforeColorSelected == Py_None) pforeColorSelected=ePyObject(); } - if (size > 9) + if (size > idx) { - pbackColor = PyTuple_GET_ITEM(item, 9); + pbackColor = PyTuple_GET_ITEM(item, idx++); if (pbackColor == Py_None) pbackColor=ePyObject(); } - if (size > 10) + if (size > idx) { - pbackColorSelected = PyTuple_GET_ITEM(item, 10); + pbackColorSelected = PyTuple_GET_ITEM(item, idx++); if (pbackColorSelected == Py_None) pbackColorSelected=ePyObject(); } @@ -921,7 +946,8 @@ void eListboxPythonMultiContent::paint(gPainter &painter, eWindowStyle &style, c { gRegion rc(rect); - clearRegion(painter, style, local_style, pforeColor, pforeColorSelected, pbackColor, pbackColorSelected, selected, rc, sel_clip, offset, cursorValid); + bool mustClear = (selected && pbackColorSelected) || (!selected && pbackColor); + clearRegion(painter, style, local_style, pforeColor, pforeColorSelected, pbackColor, pbackColorSelected, selected, rc, sel_clip, offset, cursorValid, mustClear); } // border @@ -937,12 +963,27 @@ void eListboxPythonMultiContent::paint(gPainter &painter, eWindowStyle &style, c rect.setRect(x+width-bwidth, y+bwidth, bwidth, height-bwidth); painter.fill(rect); - // progress rect.setRect(x+bwidth, y+bwidth, (width-bwidth*2) * filled / 100, height-bwidth*2); - painter.fill(rect); - painter.clippop(); + // progress + if (ppixmap) + { + ePtr<gPixmap> pixmap; + if (PyInt_Check(ppixmap) && data) /* if the pixmap is in fact a number, it refers to the data list */ + ppixmap = PyTuple_GetItem(data, PyInt_AsLong(ppixmap)); + if (SwigFromPython(pixmap, ppixmap)) + { + eDebug("eListboxPythonMultiContent (Pixmap) get pixmap failed"); + painter.clippop(); + continue; + } + painter.blit(pixmap, rect.topLeft(), rect, 0); + } + else + painter.fill(rect); + + painter.clippop(); break; } case TYPE_PIXMAP_ALPHABLEND: @@ -995,7 +1036,8 @@ void eListboxPythonMultiContent::paint(gPainter &painter, eWindowStyle &style, c { gRegion rc(rect); - clearRegion(painter, style, local_style, ePyObject(), ePyObject(), pbackColor, pbackColorSelected, selected, rc, sel_clip, offset, cursorValid); + bool mustClear = (selected && pbackColorSelected) || (!selected && pbackColor); + clearRegion(painter, style, local_style, ePyObject(), ePyObject(), pbackColor, pbackColorSelected, selected, rc, sel_clip, offset, cursorValid, mustClear); } painter.blit(pixmap, rect.topLeft(), rect, (type == TYPE_PIXMAP_ALPHATEST) ? gPainter::BT_ALPHATEST : (type == TYPE_PIXMAP_ALPHABLEND) ? gPainter::BT_ALPHABLEND : 0); @@ -1118,5 +1160,7 @@ void eListboxPythonMultiContent::entryRemoved(int idx) void eListboxPythonMultiContent::setTemplate(ePyObject tmplate) { + Py_XDECREF(m_template); m_template = tmplate; + Py_XINCREF(m_template); } diff --git a/lib/gui/elistboxcontent.h b/lib/gui/elistboxcontent.h index d3d2a089..2649c097 100644 --- a/lib/gui/elistboxcontent.h +++ b/lib/gui/elistboxcontent.h @@ -69,7 +69,7 @@ class eListboxPythonMultiContent: public eListboxPythonStringContent public: eListboxPythonMultiContent(); ~eListboxPythonMultiContent(); - enum { TYPE_TEXT, TYPE_PROGRESS, TYPE_PIXMAP, TYPE_PIXMAP_ALPHATEST, TYPE_PIXMAP_ALPHABLEND }; + enum { TYPE_TEXT, TYPE_PROGRESS, TYPE_PIXMAP, TYPE_PIXMAP_ALPHATEST, TYPE_PIXMAP_ALPHABLEND, TYPE_PROGRESS_PIXMAP }; void paint(gPainter &painter, eWindowStyle &style, const ePoint &offset, int selected); int currentCursorSelectable(); void setList(SWIG_PYOBJECT(ePyObject) list); diff --git a/lib/gui/epixmap.cpp b/lib/gui/epixmap.cpp index f20c1a9f..bc0d42df 100644 --- a/lib/gui/epixmap.cpp +++ b/lib/gui/epixmap.cpp @@ -3,7 +3,7 @@ #include <lib/gui/ewidgetdesktop.h> ePixmap::ePixmap(eWidget *parent) - :eWidget(parent), m_alphatest(false), m_scale(false) + :eWidget(parent), m_alphatest(false), m_scale(false), m_have_border_color(false), m_border_width(0) { } @@ -49,9 +49,22 @@ void ePixmap::setPixmapFromFile(const char *filename) event(evtChangedPixmap); } +void ePixmap::setBorderWidth(int pixel) +{ + m_border_width=pixel; + invalidate(); +} + +void ePixmap::setBorderColor(const gRGB &color) +{ + m_border_color=color; + m_have_border_color=true; + invalidate(); +} + void ePixmap::checkSize() { - /* when we have no pixmap, or a pixmap of different size, we need + /* when we have no pixmap, or a pixmap of different size, we need to enable transparency in any case. */ if (m_pixmap && m_pixmap->size() == size() && !m_alphatest) setTransparent(0); @@ -67,13 +80,14 @@ int ePixmap::event(int event, void *data, void *data2) case evtPaint: { ePtr<eWindowStyle> style; - + + eSize s(size()); getStyle(style); // we don't clear the background before because of performance reasons. // when the pixmap is too small to fit the whole widget area, the widget is // transparent anyway, so the background is already painted. -// eWidget::event(event, data, data2); +// eWidget::event(event, data, data2); gPainter &painter = *(gPainter*)data2; if (m_pixmap) @@ -91,6 +105,16 @@ int ePixmap::event(int event, void *data, void *data2) painter.blit(m_pixmap, ePoint(0, 0), eRect(), flags); } + if (m_have_border_color) + painter.setForegroundColor(m_border_color); + + if (m_border_width) { + painter.fill(eRect(0, 0, s.width(), m_border_width)); + painter.fill(eRect(0, m_border_width, m_border_width, s.height()-m_border_width)); + painter.fill(eRect(m_border_width, s.height()-m_border_width, s.width()-m_border_width, m_border_width)); + painter.fill(eRect(s.width()-m_border_width, m_border_width, m_border_width, s.height()-m_border_width)); + } + return 0; } case evtChangedPixmap: diff --git a/lib/gui/epixmap.h b/lib/gui/epixmap.h index 6280fb34..2db26f25 100644 --- a/lib/gui/epixmap.h +++ b/lib/gui/epixmap.h @@ -15,6 +15,8 @@ public: void setPixmapFromFile(const char *filename); void setAlphatest(int alphatest); /* 1 for alphatest, 2 for alphablend */ void setScale(int scale); + void setBorderWidth(int pixel); + void setBorderColor(const gRGB &color); protected: ePtr<gPixmap> m_pixmap; int event(int event, void *data=0, void *data2=0); @@ -24,6 +26,9 @@ private: { evtChangedPixmap = evtUserWidget, }; + bool m_have_border_color; + int m_border_width; + gRGB m_border_color; }; #endif diff --git a/lib/gui/epositiongauge.cpp b/lib/gui/epositiongauge.cpp index ff98c080..e45d4a6c 100644 --- a/lib/gui/epositiongauge.cpp +++ b/lib/gui/epositiongauge.cpp @@ -112,6 +112,7 @@ int ePositionGauge::event(int event, void *data, void *data2) // painter.fill(eRect(0, 10, s.width(), s.height()-20)); pts_t in = 0, out = 0; + int xm, xm_last = -1; std::multiset<cueEntry>::iterator i(m_cue_entries.begin()); @@ -126,17 +127,22 @@ int ePositionGauge::event(int event, void *data, void *data2) continue; } else if (i->what == 1) /* out */ out = i++->where; - else if (i->what == 2) /* mark */ + else /* mark or last */ { - int xm = scale(i->where); - painter.setForegroundColor(gRGB(0xFF8080)); - painter.fill(eRect(xm - 2, 0, 4, s.height())); + xm = scale(i->where); + if (i->what == 2) { + painter.setForegroundColor(gRGB(0xFF8080)); + if (xm - 2 < xm_last) /* Make sure last is not overdrawn */ + painter.fill(eRect(xm_last, 0, 2 + xm - xm_last, s.height())); + else + painter.fill(eRect(xm - 2, 0, 4, s.height())); + } else if (i->what == 3) { + painter.setForegroundColor(gRGB(0x80FF80)); + painter.fill(eRect(xm - 1, 0, 3, s.height())); + xm_last = xm + 2; + } i++; continue; - } else /* other marker, like last position */ - { - ++i; - continue; } } diff --git a/lib/gui/esubtitle.h b/lib/gui/esubtitle.h index 45345db1..cdad7286 100644 --- a/lib/gui/esubtitle.h +++ b/lib/gui/esubtitle.h @@ -23,15 +23,21 @@ struct ePangoSubtitlePageElement struct ePangoSubtitlePage { - pts_t show_pts; + pts_t m_show_pts; int m_timeout; /* in milliseconds */ std::vector<ePangoSubtitlePageElement> m_elements; void clear() { m_elements.clear(); } }; +struct eVobSubtitlePage +{ + pts_t m_show_pts; + int m_timeout; /* in milliseconds */ + ePtr<gPixmap> m_pixmap; +}; + class eDVBTeletextSubtitlePage; -class eDVBPangoSubtitlePage; -class ePangoSubtitlePage; +class eDVBSubtitlePage; class eSubtitleWidget: public eWidget, public Object { diff --git a/lib/gui/ewidgetdesktop.cpp b/lib/gui/ewidgetdesktop.cpp index 98bc1f7d..08bd0474 100644 --- a/lib/gui/ewidgetdesktop.cpp +++ b/lib/gui/ewidgetdesktop.cpp @@ -381,7 +381,10 @@ void eWidgetDesktop::makeCompatiblePixmap(gPixmap &pm) ePtr<gPixmap> target_pixmap; m_screen.m_dc->getPixmap(target_pixmap); - ASSERT(target_pixmap); + if (!target_pixmap) { + eDebug("no target pixmap! assuming bpp > 8 for accelerated graphics."); + return; + } if (target_pixmap->surface && target_pixmap->surface->bpp > 8) return; diff --git a/lib/gui/ewindowstyle.cpp b/lib/gui/ewindowstyle.cpp index 68a74e0c..0ecdc2a2 100644 --- a/lib/gui/ewindowstyle.cpp +++ b/lib/gui/ewindowstyle.cpp @@ -28,7 +28,9 @@ eWindowStyleManager::~eWindowStyleManager() void eWindowStyleManager::getStyle(int style_id, ePtr<eWindowStyle> &style) { - style = m_current_style[style_id]; + std::map<int, ePtr<eWindowStyle> >::iterator it = m_current_style.find(style_id); + ASSERT(it != m_current_style.end()); + style = it->second; } void eWindowStyleManager::setStyle(int style_id, eWindowStyle *style) diff --git a/lib/mmi/Makefile.am b/lib/mmi/Makefile.am index 020c988e..392c52c2 100644 --- a/lib/mmi/Makefile.am +++ b/lib/mmi/Makefile.am @@ -1,6 +1,14 @@ -INCLUDES = \ - -I$(top_srcdir)/include +AM_CPPFLAGS = \ + -I$(top_srcdir) \ + -I$(top_srcdir)/include \ + -include Python.h \ + -include $(top_builddir)/enigma2_config.h noinst_LIBRARIES = libenigma_mmi.a -libenigma_mmi_a_SOURCES = mmi_ui.cpp +libenigma_mmi_a_SOURCES = \ + mmi_ui.cpp + +mmiincludedir = $(pkgincludedir)/lib/mmi +mmiinclude_HEADERS = \ + mmi_ui.h diff --git a/lib/nav/Makefile.am b/lib/nav/Makefile.am index 0320cb25..3d57d6ac 100644 --- a/lib/nav/Makefile.am +++ b/lib/nav/Makefile.am @@ -1,9 +1,16 @@ -INCLUDES = \ - -I$(top_srcdir)/include +AM_CPPFLAGS = \ + -I$(top_srcdir) \ + -I$(top_srcdir)/include \ + -include Python.h \ + -include $(top_builddir)/enigma2_config.h noinst_LIBRARIES = libenigma_nav.a libenigma_nav_a_SOURCES = \ - core.cpp pcore.cpp - + core.cpp \ + pcore.cpp +navincludedir = $(pkgincludedir)/lib/nav +navinclude_HEADERS = \ + core.h \ + pcore.h diff --git a/lib/python/.gitignore b/lib/python/.gitignore new file mode 100644 index 00000000..c1fa59fc --- /dev/null +++ b/lib/python/.gitignore @@ -0,0 +1,2 @@ +enigma.py +enigma_python.cpp diff --git a/lib/python/Components/About.py b/lib/python/Components/About.py index 8e332e33..6b322c9d 100644..100755 --- a/lib/python/Components/About.py +++ b/lib/python/Components/About.py @@ -1,5 +1,6 @@ from Tools.Directories import resolveFilename, SCOPE_SYSETC from enigma import getEnigmaVersionString +from os import popen class About: def __init__(self): @@ -43,4 +44,14 @@ class About: def getEnigmaVersionString(self): return getEnigmaVersionString() + def getKernelVersionString(self): + try: + result = popen("uname -r","r").read().strip("\n").split('-') + kernel_version = result[0] + return kernel_version + except: + pass + + return "unknown" + about = About() diff --git a/lib/python/Components/ConfigList.py b/lib/python/Components/ConfigList.py index 418a1b67..24f917f7 100755 --- a/lib/python/Components/ConfigList.py +++ b/lib/python/Components/ConfigList.py @@ -61,12 +61,13 @@ class ConfigList(HTMLComponent, GUIComponent, object): GUI_WIDGET = eListbox def selectionChanged(self): - if self.current: + if isinstance(self.current,tuple) and len(self.current) == 2: self.current[1].onDeselect(self.session) self.current = self.getCurrent() - if self.current: + if isinstance(self.current,tuple) and len(self.current) == 2: self.current[1].onSelect(self.session) - + else: + return for x in self.onSelectionChanged: x() @@ -75,11 +76,11 @@ class ConfigList(HTMLComponent, GUIComponent, object): instance.setContent(self.l) def preWidgetRemove(self, instance): - if self.current: + if isinstance(self.current,tuple) and len(self.current) == 2: self.current[1].onDeselect(self.session) instance.selectionChanged.get().remove(self.selectionChanged) instance.setContent(None) - + def setList(self, l): self.timer.stop() self.__list = l @@ -87,7 +88,7 @@ class ConfigList(HTMLComponent, GUIComponent, object): if l is not None: for x in l: - assert isinstance(x[1], ConfigElement), "entry in ConfigList " + str(x[1]) + " must be a ConfigElement" + assert len(x) < 2 or isinstance(x[1], ConfigElement), "entry in ConfigList " + str(x[1]) + " must be a ConfigElement" def getList(self): return self.__list diff --git a/lib/python/Components/Converter/Makefile.am b/lib/python/Components/Converter/Makefile.am index 3b6fd3e8..b73f6d5a 100644 --- a/lib/python/Components/Converter/Makefile.am +++ b/lib/python/Components/Converter/Makefile.am @@ -6,4 +6,4 @@ install_PYTHON = \ ConditionalShowHide.py ServicePosition.py ValueRange.py RdsInfo.py Streaming.py \ StaticMultiList.py ServiceTime.py MovieInfo.py MenuEntryCompare.py StringListSelection.py \ ValueBitTest.py TunerInfo.py ConfigEntryTest.py TemplatedMultiContent.py ProgressToText.py \ - Combine.py SensorToText.py + Combine.py SensorToText.py ValueToPixmap.py diff --git a/lib/python/Components/Converter/ServiceInfo.py b/lib/python/Components/Converter/ServiceInfo.py index 5014fde6..fa3518ce 100644 --- a/lib/python/Components/Converter/ServiceInfo.py +++ b/lib/python/Components/Converter/ServiceInfo.py @@ -19,6 +19,7 @@ class ServiceInfo(Converter, object): ONID = 13 SID = 14 FRAMERATE = 15 + TRANSFERBPS = 16 def __init__(self, type): @@ -40,6 +41,7 @@ class ServiceInfo(Converter, object): "OnId": (self.ONID, (iPlayableService.evUpdatedInfo,)), "Sid": (self.SID, (iPlayableService.evUpdatedInfo,)), "Framerate": (self.FRAMERATE, (iPlayableService.evVideoSizeChanged,iPlayableService.evUpdatedInfo,)), + "TransferBPS": (self.TRANSFERBPS, (iPlayableService.evUpdatedInfo,)), }[type] def getServiceInfoString(self, info, what, convert = lambda x: "%d" % x): @@ -112,6 +114,8 @@ class ServiceInfo(Converter, object): return self.getServiceInfoString(info, iServiceInformation.sSID) elif self.type == self.FRAMERATE: return self.getServiceInfoString(info, iServiceInformation.sFrameRate, lambda x: "%d fps" % ((x+500)/1000)) + elif self.type == self.TRANSFERBPS: + return self.getServiceInfoString(info, iServiceInformation.sTransferBPS, lambda x: "%d kB/s" % (x/1024)) return "" text = property(getText) diff --git a/lib/python/Components/Converter/TemplatedMultiContent.py b/lib/python/Components/Converter/TemplatedMultiContent.py index b86d94bf..b1d89f55 100644..100755 --- a/lib/python/Components/Converter/TemplatedMultiContent.py +++ b/lib/python/Components/Converter/TemplatedMultiContent.py @@ -10,8 +10,8 @@ class TemplatedMultiContent(StringList): del l["self"] # cleanup locals a bit del l["args"] - self.template = eval(args, {}, l) self.active_style = None + self.template = eval(args, {}, l) assert "fonts" in self.template assert "itemHeight" in self.template assert "template" in self.template or "templates" in self.template @@ -25,7 +25,6 @@ class TemplatedMultiContent(StringList): if not self.content: from enigma import eListboxPythonMultiContent self.content = eListboxPythonMultiContent() - self.setTemplate() # also setup fonts (also given by source) index = 0 @@ -35,30 +34,37 @@ class TemplatedMultiContent(StringList): # if only template changed, don't reload list if what[0] == self.CHANGED_SPECIFIC and what[1] == "style": - self.setTemplate() - return - - if self.source: + pass + elif self.source: self.content.setList(self.source.list) - self.setTemplate() + self.setTemplate() self.downstream_elements.changed(what) def setTemplate(self): if self.source: style = self.source.style + if style == self.active_style: - return # style did not change + return # if skin defined "templates", that means that it defines multiple styles in a dict. template should still be a default templates = self.template.get("templates") template = self.template.get("template") itemheight = self.template["itemHeight"] + selectionEnabled = self.template.get("selectionEnabled", True) + scrollbarMode = self.template.get("scrollbarMode", "showOnDemand") if templates and style and style in templates: # if we have a custom style defined in the source, and different templates in the skin, look it up template = templates[style][1] itemheight = templates[style][0] + if len(templates[style]) > 2: + selectionEnabled = templates[style][2] + if len(templates[style]) > 3: + scrollbarMode = templates[style][3] self.content.setTemplate(template) - self.content.setItemHeight(itemheight) + self.selectionEnabled = selectionEnabled + self.scrollbarMode = scrollbarMode + self.active_style = style diff --git a/lib/python/Components/Converter/ValueToPixmap.py b/lib/python/Components/Converter/ValueToPixmap.py new file mode 100644 index 00000000..0acd2639 --- /dev/null +++ b/lib/python/Components/Converter/ValueToPixmap.py @@ -0,0 +1,40 @@ +from Components.Converter.Converter import Converter +from Components.Element import cached, ElementError +from Tools.Directories import fileExists, SCOPE_SKIN_IMAGE, SCOPE_CURRENT_SKIN, resolveFilename +from Tools.LoadPixmap import LoadPixmap + + +class ValueToPixmap(Converter, object): + LANGUAGE_CODE = 0 + PATH = 1 + + def __init__(self, type): + Converter.__init__(self, type) + if type == "LanguageCode": + self.type = self.LANGUAGE_CODE + elif type == "Path": + self.type = self.PATH + else: + raise ElementError("'%s' is not <LanguageCode|Path> for ValueToPixmap converter" % type) + + @cached + def getPixmap(self): + if self.source: + val = self.source.text + if val in (None, ""): + return None + if self.type == self.PATH: + return LoadPixmap(val) + if self.type == self.LANGUAGE_CODE: + png = LoadPixmap(cached=True, path=resolveFilename(SCOPE_CURRENT_SKIN, "countries/" + val[3:].lower() + ".png")) + if png == None: + png = LoadPixmap(cached=True, path=resolveFilename(SCOPE_SKIN_IMAGE, "countries/missing.png")) + return png + return None + + pixmap = property(getPixmap) + + def changed(self, what): + if what[0] != self.CHANGED_SPECIFIC or what[1] == self.type: + Converter.changed(self, what) + diff --git a/lib/python/Components/DreamInfoHandler.py b/lib/python/Components/DreamInfoHandler.py index 85e2b533..8097365c 100755 --- a/lib/python/Components/DreamInfoHandler.py +++ b/lib/python/Components/DreamInfoHandler.py @@ -16,7 +16,7 @@ class InfoHandlerParseError(Exception): return repr(self.value) class InfoHandler(xml.sax.ContentHandler): - def __init__(self, prerequisiteMet, directory, language = None): + def __init__(self, prerequisiteMet, directory): self.attributes = {} self.directory = directory self.list = [] @@ -26,9 +26,6 @@ class InfoHandler(xml.sax.ContentHandler): self.validFileTypes = ["skin", "config", "services", "favourites", "package"] self.prerequisitesMet = prerequisiteMet self.data = "" - self.language = language - self.translatedPackageInfos = {} - self.foundTranslation = None def printError(self, error): print "Error in defaults xml files:", error @@ -52,15 +49,6 @@ class InfoHandler(xml.sax.ContentHandler): if name == "info": self.foundTranslation = None self.data = "" - if not attrs.has_key("language"): - print "info tag with no language attribute" - else: - if attrs["language"] == 'en': # read default translations - self.foundTranslation = False - self.data = "" - elif attrs["language"] == self.language: - self.foundTranslation = True - self.data = "" if name == "files": if attrs.has_key("type"): @@ -91,20 +79,19 @@ class InfoHandler(xml.sax.ContentHandler): if attrs.has_key("details"): self.attributes["details"] = str(attrs["details"]) if attrs.has_key("name"): - self.attributes["name"] = str(attrs["name"].encode("utf-8")) + self.attributes["name"] = str(attrs["name"]) if attrs.has_key("packagename"): - self.attributes["packagename"] = str(attrs["packagename"].encode("utf-8")) + self.attributes["packagename"] = str(attrs["packagename"]) if attrs.has_key("packagetype"): - self.attributes["packagetype"] = str(attrs["packagetype"].encode("utf-8")) + self.attributes["packagetype"] = str(attrs["packagetype"]) + if attrs.has_key("needsRestart"): + self.attributes["needsRestart"] = str(attrs["needsRestart"]) if attrs.has_key("shortdescription"): - self.attributes["shortdescription"] = str(attrs["shortdescription"].encode("utf-8")) + self.attributes["shortdescription"] = str(attrs["shortdescription"]) if name == "screenshot": if attrs.has_key("src"): - if self.foundTranslation is False: - self.attributes["screenshot"] = str(attrs["src"]) - elif self.foundTranslation is True: - self.translatedPackageInfos["screenshot"] = str(attrs["src"]) + self.attributes["screenshot"] = str(attrs["src"]) def endElement(self, name): #print "endElement", name @@ -124,7 +111,7 @@ class InfoHandler(xml.sax.ContentHandler): self.attributes[self.filetype].append({ "name": str(self.fileattrs["name"]), "directory": directory }) if name in ( "default", "package" ): - self.list.append({"attributes": self.attributes, 'prerequisites': self.globalprerequisites ,"translation": self.translatedPackageInfos}) + self.list.append({"attributes": self.attributes, 'prerequisites': self.globalprerequisites}) self.attributes = {} self.globalprerequisites = {} @@ -133,30 +120,15 @@ class InfoHandler(xml.sax.ContentHandler): self.attributes["author"] = str(data) if self.elements[-1] == "name": self.attributes["name"] = str(data) - if self.foundTranslation is False: - if self.elements[-1] == "author": - self.attributes["author"] = str(data) - if self.elements[-1] == "name": - self.attributes["name"] = str(data) - if self.elements[-1] == "packagename": - self.attributes["packagename"] = str(data.encode("utf-8")) - if self.elements[-1] == "shortdescription": - self.attributes["shortdescription"] = str(data.encode("utf-8")) - if self.elements[-1] == "description": - self.data += data.strip() - self.attributes["description"] = str(self.data.encode("utf-8")) - elif self.foundTranslation is True: - if self.elements[-1] == "author": - self.translatedPackageInfos["author"] = str(data) - if self.elements[-1] == "name": - self.translatedPackageInfos["name"] = str(data) - if self.elements[-1] == "description": - self.data += data.strip() - self.translatedPackageInfos["description"] = str(self.data.encode("utf-8")) - if self.elements[-1] == "name": - self.translatedPackageInfos["name"] = str(data.encode("utf-8")) - if self.elements[-1] == "shortdescription": - self.translatedPackageInfos["shortdescription"] = str(data.encode("utf-8")) + if self.elements[-1] == "packagename": + self.attributes["packagename"] = str(data) + if self.elements[-1] == "needsRestart": + self.attributes["needsRestart"] = str(data) + if self.elements[-1] == "shortdescription": + self.attributes["shortdescription"] = str(data) + if self.elements[-1] == "description": + self.data += data.strip() + self.attributes["description"] = str(self.data) #print "characters", data @@ -166,13 +138,12 @@ class DreamInfoHandler: STATUS_ERROR = 2 STATUS_INIT = 4 - def __init__(self, statusCallback, blocking = False, neededTag = None, neededFlag = None, language = None): + def __init__(self, statusCallback, blocking = False, neededTag = None, neededFlag = None): self.hardware_info = HardwareInfo() self.directory = "/" self.neededTag = neededTag self.neededFlag = neededFlag - self.language = language # caution: blocking should only be used, if further execution in enigma2 depends on the outcome of # the installer! @@ -203,8 +174,8 @@ class DreamInfoHandler: #print handler.list def readIndex(self, directory, file): - print "Reading .xml meta index file", file - handler = InfoHandler(self.prerequisiteMet, directory, self.language) + print "Reading .xml meta index file", directory, file + handler = InfoHandler(self.prerequisiteMet, directory) try: xml.sax.parse(file, handler) for entry in handler.list: @@ -216,7 +187,7 @@ class DreamInfoHandler: def readDetails(self, directory, file): self.packageDetails = [] print "Reading .xml meta details file", file - handler = InfoHandler(self.prerequisiteMet, directory, self.language) + handler = InfoHandler(self.prerequisiteMet, directory) try: xml.sax.parse(file, handler) for entry in handler.list: @@ -225,7 +196,6 @@ class DreamInfoHandler: print "file", file, "ignored due to errors in the file" #print handler.list - # prerequisites = True: give only packages matching the prerequisites def fillPackagesList(self, prerequisites = True): self.packageslist = [] @@ -254,20 +224,16 @@ class DreamInfoHandler: self.directory = [self.directory] for indexfile in os.listdir(self.directory[0]): - if indexfile.startswith("index"): - if indexfile.endswith("_en.xml"): #we first catch all english indexfiles - indexfileList.append(os.path.splitext(indexfile)[0][:-3]) - + if indexfile.startswith("index-"): + if indexfile.endswith(".xml"): + if indexfile[-7:-6] == "_": + continue + indexfileList.append(indexfile) if len(indexfileList): for file in indexfileList: neededFile = self.directory[0] + "/" + file - if self.language is not None: - if os.path.exists(neededFile + '_' + self.language + '.xml' ): - #print "translated index file found",neededFile + '_' + self.language + '.xml' - self.readIndex(self.directory[0] + "/", neededFile + '_' + self.language + '.xml') - else: - #print "reading original index file" - self.readIndex(self.directory[0] + "/", neededFile + '_en.xml') + if os.path.isfile(neededFile): + self.readIndex(self.directory[0] + "/" , neededFile) if prerequisites: for package in self.packagesIndexlist[:]: @@ -435,7 +401,7 @@ class DreamInfoHandler: def installIPK(self, directory, name): if self.blocking: - os.system("ipkg install " + directory + name) + os.system("opkg install " + directory + name) self.installNext() else: self.ipkg = IpkgComponent() diff --git a/lib/python/Components/FanControl.py b/lib/python/Components/FanControl.py index cee0523e..a993c396 100644 --- a/lib/python/Components/FanControl.py +++ b/lib/python/Components/FanControl.py @@ -3,6 +3,9 @@ import os from Components.config import config, ConfigSubList, ConfigSubsection, ConfigSlider from Tools.BoundFunction import boundFunction +import NavigationInstance +from enigma import iRecordableService + class FanControl: # ATM there's only support for one fan def __init__(self): @@ -13,18 +16,42 @@ class FanControl: self.createConfig() config.misc.standbyCounter.addNotifier(self.standbyCounterChanged, initial_call = False) - def leaveStandby(self): + def setVoltage_PWM(self): for fanid in range(self.getFanCount()): cfg = self.getConfig(fanid) self.setVoltage(fanid, cfg.vlt.value) self.setPWM(fanid, cfg.pwm.value) + print "[FanControl]: setting fan values: fanid = %d, voltage = %d, pwm = %d" % (fanid, cfg.vlt.value, cfg.pwm.value) + + def setVoltage_PWM_Standby(self): + for fanid in range(self.getFanCount()): + cfg = self.getConfig(fanid) + self.setVoltage(fanid, cfg.vlt_standby.value) + self.setPWM(fanid, cfg.pwm_standby.value) + print "[FanControl]: setting fan values (standby mode): fanid = %d, voltage = %d, pwm = %d" % (fanid, cfg.vlt_standby.value, cfg.pwm_standby.value) + + def getRecordEvent(self, recservice, event): + recordings = len(NavigationInstance.instance.getRecordings()) + if event == iRecordableService.evEnd: + if recordings == 0: + self.setVoltage_PWM_Standby() + elif event == iRecordableService.evStart: + if recordings == 1: + self.setVoltage_PWM() + + def leaveStandby(self): + NavigationInstance.instance.record_event.remove(self.getRecordEvent) + recordings = NavigationInstance.instance.getRecordings() + if not recordings: + self.setVoltage_PWM() def standbyCounterChanged(self, configElement): from Screens.Standby import inStandby inStandby.onClose.append(self.leaveStandby) - for fanid in range(self.getFanCount()): - self.setVoltage(fanid, 0) - self.setPWM(fanid, 0) + recordings = NavigationInstance.instance.getRecordings() + NavigationInstance.instance.record_event.append(self.getRecordEvent) + if not recordings: + self.setVoltage_PWM_Standby() def createConfig(self): def setVlt(fancontrol, fanid, configElement): @@ -35,12 +62,14 @@ class FanControl: config.fans = ConfigSubList() for fanid in range(self.getFanCount()): fan = ConfigSubsection() - fan.vlt = ConfigSlider(default = 16, increment = 5, limits = (0, 255)) + fan.vlt = ConfigSlider(default = 15, increment = 5, limits = (0, 255)) fan.pwm = ConfigSlider(default = 0, increment = 5, limits = (0, 255)) + fan.vlt_standby = ConfigSlider(default = 5, increment = 5, limits = (0, 255)) + fan.pwm_standby = ConfigSlider(default = 0, increment = 5, limits = (0, 255)) fan.vlt.addNotifier(boundFunction(setVlt, self, fanid)) fan.pwm.addNotifier(boundFunction(setPWM, self, fanid)) config.fans.append(fan) - + def getConfig(self, fanid): return config.fans[fanid] @@ -85,4 +114,4 @@ class FanControl: f.write("%x" % value) f.close() -fancontrol = FanControl()
\ No newline at end of file +fancontrol = FanControl() diff --git a/lib/python/Components/FileList.py b/lib/python/Components/FileList.py index 841a2fe5..1b7e81f5 100755 --- a/lib/python/Components/FileList.py +++ b/lib/python/Components/FileList.py @@ -28,7 +28,8 @@ EXTENSIONS = { "mpeg": "movie", "mkv": "movie", "mp4": "movie", - "mov": "movie" + "mov": "movie", + "m2ts": "movie", } def FileEntryComponent(name, absolute = None, isDir = False): @@ -196,6 +197,9 @@ class FileList(MenuList): if (self.matchingPattern is None) or re_compile(self.matchingPattern).search(path): self.list.append(FileEntryComponent(name = name, absolute = x , isDir = False)) + if self.showMountpoints and len(self.list) == 0: + self.list.append(FileEntryComponent(name = _("nothing connected"), absolute = None, isDir = False)) + self.l.setList(self.list) if select is not None: diff --git a/lib/python/Components/Harddisk.py b/lib/python/Components/Harddisk.py index 03f574f3..30a7b609 100755..100644 --- a/lib/python/Components/Harddisk.py +++ b/lib/python/Components/Harddisk.py @@ -5,23 +5,27 @@ from SystemInfo import SystemInfo import time from Components.Console import Console +def MajorMinor(path): + rdev = stat(path).st_rdev + return (major(rdev),minor(rdev)) + def readFile(filename): file = open(filename) data = file.read().strip() file.close() return data -class Harddisk: - DEVTYPE_UDEV = 0 - DEVTYPE_DEVFS = 1 +DEVTYPE_UDEV = 0 +DEVTYPE_DEVFS = 1 +class Harddisk: def __init__(self, device): self.device = device if access("/dev/.udev", 0): - self.type = self.DEVTYPE_UDEV + self.type = DEVTYPE_UDEV elif access("/dev/.devfsd", 0): - self.type = self.DEVTYPE_DEVFS + self.type = DEVTYPE_DEVFS else: print "Unable to determine structure of /dev" @@ -33,11 +37,11 @@ class Harddisk: self.disk_path = '' self.phys_path = path.realpath(self.sysfsPath('device')) - if self.type == self.DEVTYPE_UDEV: + if self.type == DEVTYPE_UDEV: self.dev_path = '/dev/' + self.device self.disk_path = self.dev_path - elif self.type == self.DEVTYPE_DEVFS: + elif self.type == DEVTYPE_DEVFS: tmp = readFile(self.sysfsPath('dev')).split(':') s_major = int(tmp[0]) s_minor = int(tmp[1]) @@ -60,9 +64,9 @@ class Harddisk: return self.device < ob.device def partitionPath(self, n): - if self.type == self.DEVTYPE_UDEV: + if self.type == DEVTYPE_UDEV: return self.dev_path + n - elif self.type == self.DEVTYPE_DEVFS: + elif self.type == DEVTYPE_DEVFS: return self.dev_path + '/part' + n def sysfsPath(self, filename): @@ -75,9 +79,9 @@ class Harddisk: def bus(self): # CF (7025 specific) - if self.type == self.DEVTYPE_UDEV: + if self.type == DEVTYPE_UDEV: ide_cf = False # FIXME - elif self.type == self.DEVTYPE_DEVFS: + elif self.type == DEVTYPE_DEVFS: ide_cf = self.device[:2] == "hd" and "host0" not in self.dev_path internal = "pci" in self.phys_path @@ -125,18 +129,20 @@ class Harddisk: for line in lines: parts = line.strip().split(" ") - if path.realpath(parts[0]).startswith(self.dev_path): - try: + real_path = path.realpath(parts[0]) + if not real_path[-1].isdigit(): + continue + try: + if MajorMinor(real_path) == MajorMinor(self.partitionPath(real_path[-1])): stat = statvfs(parts[1]) - except OSError: - continue - return stat.f_bfree/1000 * stat.f_bsize/1000 - + return stat.f_bfree/1000 * stat.f_bsize/1000 + except OSError: + pass return -1 def numPartitions(self): numPart = -1 - if self.type == self.DEVTYPE_UDEV: + if self.type == DEVTYPE_UDEV: try: devdir = listdir('/dev') except OSError: @@ -145,7 +151,7 @@ class Harddisk: if filename.startswith(self.device): numPart += 1 - elif self.type == self.DEVTYPE_DEVFS: + elif self.type == DEVTYPE_DEVFS: try: idedir = listdir(self.dev_path) except OSError: @@ -166,23 +172,30 @@ class Harddisk: lines = mounts.readlines() mounts.close() - cmd = "/bin/umount" + cmd = "umount" - for line in lines: - parts = line.strip().split(" ") - if path.realpath(parts[0]).startswith(self.dev_path): - cmd = ' ' . join([cmd, parts[1]]) + for line in lines: + parts = line.strip().split(" ") + real_path = path.realpath(parts[0]) + if not real_path[-1].isdigit(): + continue + try: + if MajorMinor(real_path) == MajorMinor(self.partitionPath(real_path[-1])): + cmd = ' ' . join([cmd, parts[1]]) + break + except OSError: + pass res = system(cmd) return (res >> 8) def createPartition(self): - cmd = 'printf "0,\n;\n;\n;\ny\n" | /sbin/sfdisk -f ' + self.disk_path + cmd = 'printf "8,\n;0,0\n;0,0\n;0,0\ny\n" | sfdisk -f -uS ' + self.disk_path res = system(cmd) return (res >> 8) def mkfs(self): - cmd = "/sbin/mkfs.ext3 " + cmd = "mkfs.ext3 " if self.diskSize() > 4 * 1024: cmd += "-T largefile " cmd += "-m0 -O dir_index " + self.partitionPath("1") @@ -201,10 +214,16 @@ class Harddisk: res = -1 for line in lines: parts = line.strip().split(" ") - if path.realpath(parts[0]) == self.partitionPath("1"): - cmd = "/bin/mount -t ext3 " + parts[0] - res = system(cmd) - break + real_path = path.realpath(parts[0]) + if not real_path[-1].isdigit(): + continue + try: + if MajorMinor(real_path) == MajorMinor(self.partitionPath(real_path[-1])): + cmd = "mount -t ext3 " + parts[0] + res = system(cmd) + break + except OSError: + pass return (res >> 8) @@ -218,7 +237,7 @@ class Harddisk: def fsck(self): # We autocorrect any failures # TODO: we could check if the fs is actually ext3 - cmd = "/sbin/fsck.ext3 -f -p " + self.partitionPath("1") + cmd = "fsck.ext3 -f -p " + self.partitionPath("1") res = system(cmd) return (res >> 8) @@ -226,7 +245,7 @@ class Harddisk: part = self.partitionPath(n) if access(part, 0): - cmd = '/bin/dd bs=512 count=3 if=/dev/zero of=' + part + cmd = 'dd bs=512 count=3 if=/dev/zero of=' + part res = system(cmd) else: res = 0 @@ -394,24 +413,38 @@ class Partition: return True return False -DEVICEDB = \ +DEVICEDB_SR = \ {"dm8000": { - # dm8000: - "/devices/platform/brcm-ehci.0/usb1/1-1/1-1.1/1-1.1:1.0": "Front USB Slot", - "/devices/platform/brcm-ehci.0/usb1/1-1/1-1.2/1-1.2:1.0": "Back, upper USB Slot", - "/devices/platform/brcm-ehci.0/usb1/1-1/1-1.3/1-1.3:1.0": "Back, lower USB Slot", - "/devices/platform/brcm-ehci-1.1/usb2/2-1/2-1:1.0/host1/target1:0:0/1:0:0:0": "DVD Drive", + "/devices/pci0000:01/0000:01:00.0/host0/target0:0:0/0:0:0:0": _("DVD Drive"), + "/devices/pci0000:01/0000:01:00.0/host1/target1:0:0/1:0:0:0": _("DVD Drive"), + "/devices/platform/brcm-ehci-1.1/usb2/2-1/2-1:1.0/host3/target3:0:0/3:0:0:0": _("DVD Drive"), + }, + "dm800": + { + }, + "dm7025": + { + } + } + +DEVICEDB = \ + {"dm8000": + { + "/devices/platform/brcm-ehci.0/usb1/1-1/1-1.1/1-1.1:1.0": _("Front USB Slot"), + "/devices/platform/brcm-ehci.0/usb1/1-1/1-1.2/1-1.2:1.0": _("Back, upper USB Slot"), + "/devices/platform/brcm-ehci.0/usb1/1-1/1-1.3/1-1.3:1.0": _("Back, lower USB Slot"), + "/devices/platform/brcm-ehci.0/usb1/1-1/1-1.1/1-1.1:1.0": _("Front USB Slot"), + "/devices/platform/brcm-ehci-1.1/usb2/2-1/2-1:1.0/": _("Internal USB Slot"), + "/devices/platform/brcm-ohci-1.1/usb4/4-1/4-1:1.0/": _("Internal USB Slot"), }, "dm800": { - # dm800: "/devices/platform/brcm-ehci.0/usb1/1-2/1-2:1.0": "Upper USB Slot", "/devices/platform/brcm-ehci.0/usb1/1-1/1-1:1.0": "Lower USB Slot", }, "dm7025": { - # dm7025: "/devices/pci0000:00/0000:00:14.1/ide1/1.0": "CF Card Slot", #hdc "/devices/pci0000:00/0000:00:14.1/ide0/0.0": "Internal Harddisk" } @@ -422,6 +455,7 @@ class HarddiskManager: self.hdd = [ ] self.cd = "" self.partitions = [ ] + self.devices_scanned_on_init = [ ] self.on_partition_list_change = CList() @@ -489,24 +523,23 @@ class HarddiskManager: def enumerateBlockDevices(self): print "enumerating block devices..." for blockdev in listdir("/sys/block"): - error, blacklisted, removable, is_cdrom, partitions, medium_found = self.getBlockDevInfo(blockdev) - print "found block device '%s':" % blockdev, - if error: - print "error querying properties" - elif blacklisted: - print "blacklisted" - elif not medium_found: - print "no medium" - else: - print "ok, removable=%s, cdrom=%s, partitions=%s, device=%s" % (removable, is_cdrom, partitions, blockdev) - - self.addHotplugPartition(blockdev) - for part in partitions: - self.addHotplugPartition(part) + error, blacklisted, removable, is_cdrom, partitions, medium_found = self.addHotplugPartition(blockdev) + if not error and not blacklisted: + if medium_found: + for part in partitions: + self.addHotplugPartition(part) + self.devices_scanned_on_init.append((blockdev, removable, is_cdrom, medium_found)) def getAutofsMountpoint(self, device): return "/autofs/%s/" % (device) + def is_hard_mounted(self, device): + mounts = file('/proc/mounts').read().split('\n') + for x in mounts: + if x.find('/autofs') == -1 and x.find(device) != -1: + return True + return False + def addHotplugPartition(self, device, physdev = None): if not physdev: dev, part = self.splitDeviceName(device) @@ -516,22 +549,36 @@ class HarddiskManager: physdev = dev print "couldn't determine blockdev physdev for device", device - # device is the device name, without /dev - # physdev is the physical device path, which we (might) use to determine the userfriendly name - description = self.getUserfriendlyDeviceName(device, physdev) + error, blacklisted, removable, is_cdrom, partitions, medium_found = self.getBlockDevInfo(device) + print "found block device '%s':" % device, - p = Partition(mountpoint = self.getAutofsMountpoint(device), description = description, force_mounted = True, device = device) - self.partitions.append(p) - self.on_partition_list_change("add", p) + if blacklisted: + print "blacklisted" + else: + if error: + print "error querying properties" + elif not medium_found: + print "no medium" + else: + print "ok, removable=%s, cdrom=%s, partitions=%s" % (removable, is_cdrom, partitions) + + l = len(device) + if l: + # see if this is a harddrive + if not device[l-1].isdigit() and not removable and not is_cdrom: + self.hdd.append(Harddisk(device)) + self.hdd.sort() + SystemInfo["Harddisk"] = len(self.hdd) > 0 + + if (not removable or medium_found) and not self.is_hard_mounted(device): + # device is the device name, without /dev + # physdev is the physical device path, which we (might) use to determine the userfriendly name + description = self.getUserfriendlyDeviceName(device, physdev) + p = Partition(mountpoint = self.getAutofsMountpoint(device), description = description, force_mounted = True, device = device) + self.partitions.append(p) + self.on_partition_list_change("add", p) - # see if this is a harddrive - l = len(device) - if l and not device[l-1].isdigit(): - error, blacklisted, removable, is_cdrom, partitions, medium_found = self.getBlockDevInfo(device) - if not blacklisted and not removable and not is_cdrom and medium_found: - self.hdd.append(Harddisk(device)) - self.hdd.sort() - SystemInfo["Harddisk"] = len(self.hdd) > 0 + return error, blacklisted, removable, is_cdrom, partitions, medium_found def removeHotplugPartition(self, device): mountpoint = self.getAutofsMountpoint(device) @@ -589,15 +636,23 @@ class HarddiskManager: def getUserfriendlyDeviceName(self, dev, phys): dev, part = self.splitDeviceName(dev) description = "External Storage %s" % dev + have_model_descr = False try: description = readFile("/sys" + phys + "/model") + have_model_descr = True except IOError, s: print "couldn't read model: ", s from Tools.HardwareInfo import HardwareInfo - for physdevprefix, pdescription in DEVICEDB.get(HardwareInfo().device_name,{}).items(): + if dev.find('sr') == 0 and dev[2].isdigit(): + devicedb = DEVICEDB_SR + else: + devicedb = DEVICEDB + for physdevprefix, pdescription in devicedb.get(HardwareInfo().device_name,{}).items(): if phys.startswith(physdevprefix): - description = pdescription - + if have_model_descr: + description = pdescription + ' - ' + description + else: + description = pdescription # not wholedisk and not partition 1 if part and part != 1: description += " (Partition %d)" % part diff --git a/lib/python/Components/InputDevice.py b/lib/python/Components/InputDevice.py index 3c3bd7a1..d675ca3a 100644..100755 --- a/lib/python/Components/InputDevice.py +++ b/lib/python/Components/InputDevice.py @@ -1,29 +1,208 @@ -from config import config, ConfigSlider, ConfigSubsection +# coding: utf-8 +from config import config, configfile, ConfigSlider, ConfigSubsection, ConfigYesNo, ConfigText + +import struct, sys, time, errno +from fcntl import ioctl +from os import path as os_path, listdir, open as os_open, close as os_close, write as os_write, read as os_read, O_RDWR, O_NONBLOCK + +# asm-generic/ioctl.h +IOC_NRBITS = 8L +IOC_TYPEBITS = 8L +IOC_SIZEBITS = 13L +IOC_DIRBITS = 3L + +IOC_NRSHIFT = 0L +IOC_TYPESHIFT = IOC_NRSHIFT+IOC_NRBITS +IOC_SIZESHIFT = IOC_TYPESHIFT+IOC_TYPEBITS +IOC_DIRSHIFT = IOC_SIZESHIFT+IOC_SIZEBITS + +IOC_READ = 2L + +def EVIOCGNAME(length): + return (IOC_READ<<IOC_DIRSHIFT)|(length<<IOC_SIZESHIFT)|(0x45<<IOC_TYPESHIFT)|(0x06<<IOC_NRSHIFT) + class inputDevices: + + def __init__(self): + self.Devices = {} + self.currentDevice = "" + self.getInputDevices() + + def getInputDevices(self): + devices = listdir("/dev/input/") + + for evdev in devices: + try: + buffer = "\0"*512 + self.fd = os_open("/dev/input/" + evdev, O_RDWR | O_NONBLOCK) + self.name = ioctl(self.fd, EVIOCGNAME(256), buffer) + self.name = self.name[:self.name.find("\0")] + os_close(self.fd) + except (IOError,OSError), err: + print '[iInputDevices] getInputDevices <ERROR: ioctl(EVIOCGNAME): ' + str(err) + ' >' + self.name = None + + if self.name: + if self.name == 'dreambox front panel': + continue + if self.name == "dreambox advanced remote control (native)" and config.misc.rcused.value != 0: + continue + if self.name == "dreambox remote control (native)" and config.misc.rcused.value == 0: + continue + self.Devices[evdev] = {'name': self.name, 'type': self.getInputDeviceType(self.name),'enabled': False, 'configuredName': None } + + + def getInputDeviceType(self,name): + if name.find("remote control") != -1: + return "remote" + elif name.find("keyboard") != -1: + return "keyboard" + elif name.find("mouse") != -1: + return "mouse" + else: + print "Unknown device type:",name + return None + + def getDeviceName(self, x): + if x in self.Devices.keys(): + return self.Devices[x].get("name", x) + else: + return "Unknown device name" + + def getDeviceList(self): + return sorted(self.Devices.iterkeys()) + + def getDefaultRCdeviceName(self): + if config.misc.rcused.value == 0: + for device in self.Devices.iterkeys(): + if self.Devices[device]["name"] == "dreambox advanced remote control (native)": + return device + else: + for device in self.Devices.iterkeys(): + if self.Devices[device]["name"] == "dreambox remote control (native)": + return device + + def setDeviceAttribute(self, device, attribute, value): + #print "[iInputDevices] setting for device", device, "attribute", attribute, " to value", value + if self.Devices.has_key(device): + self.Devices[device][attribute] = value + + def getDeviceAttribute(self, device, attribute): + if self.Devices.has_key(device): + if self.Devices[device].has_key(attribute): + return self.Devices[device][attribute] + return None + + def setEnabled(self, device, value): + oldval = self.getDeviceAttribute(device, 'enabled') + #print "[iInputDevices] setEnabled for device %s to %s from %s" % (device,value,oldval) + self.setDeviceAttribute(device, 'enabled', value) + if oldval is True and value is False: + self.setDefaults(device) + + def setName(self, device, value): + #print "[iInputDevices] setName for device %s to %s" % (device,value) + self.setDeviceAttribute(device, 'configuredName', value) + + #struct input_event { + # struct timeval time; -> ignored + # __u16 type; -> EV_REP (0x14) + # __u16 code; -> REP_DELAY (0x00) or REP_PERIOD (0x01) + # __s32 value; -> DEFAULTS: 700(REP_DELAY) or 100(REP_PERIOD) + #}; -> size = 16 + + def setDefaults(self, device): + print "[iInputDevices] setDefaults for device %s" % (device) + self.setDeviceAttribute(device, 'configuredName', None) + event_repeat = struct.pack('iihhi', 0, 0, 0x14, 0x01, 100) + event_delay = struct.pack('iihhi', 0, 0, 0x14, 0x00, 700) + fd = os_open("/dev/input/" + device, O_RDWR) + os_write(fd, event_repeat) + os_write(fd, event_delay) + os_close(fd) + + def setRepeat(self, device, value): #REP_PERIOD + if self.getDeviceAttribute(device, 'enabled') == True: + print "[iInputDevices] setRepeat for device %s to %d ms" % (device,value) + event = struct.pack('iihhi', 0, 0, 0x14, 0x01, int(value)) + fd = os_open("/dev/input/" + device, O_RDWR) + os_write(fd, event) + os_close(fd) + + def setDelay(self, device, value): #REP_DELAY + if self.getDeviceAttribute(device, 'enabled') == True: + print "[iInputDevices] setDelay for device %s to %d ms" % (device,value) + event = struct.pack('iihhi', 0, 0, 0x14, 0x00, int(value)) + fd = os_open("/dev/input/" + device, O_RDWR) + os_write(fd, event) + os_close(fd) + + +class InitInputDevices: + def __init__(self): - pass - def setRepeat(self, value): - #print "setup rc repeat" - pass - def setDelay(self, value): - #print "setup rc delay" - pass - -def InitInputDevices(): - config.inputDevices = ConfigSubsection(); - config.inputDevices.repeat = ConfigSlider(default=5, limits=(1, 10)) - config.inputDevices.delay = ConfigSlider(default=4, limits=(1, 10)) - - #this instance anywhere else needed? - iDevices = inputDevices(); + self.currentDevice = "" + self.createConfig() - def inputDevicesRepeatChanged(configElement): - iDevices.setRepeat(configElement.value); + def createConfig(self, *args): + config.inputDevices = ConfigSubsection() + for device in sorted(iInputDevices.Devices.iterkeys()): + self.currentDevice = device + #print "[InitInputDevices] -> creating config entry for device: %s -> %s " % (self.currentDevice, iInputDevices.Devices[device]["name"]) + self.setupConfigEntries(self.currentDevice) + self.currentDevice = "" + + def inputDevicesEnabledChanged(self,configElement): + if self.currentDevice != "" and iInputDevices.currentDevice == "": + iInputDevices.setEnabled(self.currentDevice, configElement.value) + elif iInputDevices.currentDevice != "": + iInputDevices.setEnabled(iInputDevices.currentDevice, configElement.value) + + def inputDevicesNameChanged(self,configElement): + if self.currentDevice != "" and iInputDevices.currentDevice == "": + iInputDevices.setName(self.currentDevice, configElement.value) + if configElement.value != "": + devname = iInputDevices.getDeviceAttribute(self.currentDevice, 'name') + if devname != configElement.value: + cmd = "config.inputDevices." + self.currentDevice + ".enabled.value = False" + exec (cmd) + cmd = "config.inputDevices." + self.currentDevice + ".enabled.save()" + exec (cmd) + elif iInputDevices.currentDevice != "": + iInputDevices.setName(iInputDevices.currentDevice, configElement.value) + + def inputDevicesRepeatChanged(self,configElement): + if self.currentDevice != "" and iInputDevices.currentDevice == "": + iInputDevices.setRepeat(self.currentDevice, configElement.value) + elif iInputDevices.currentDevice != "": + iInputDevices.setRepeat(iInputDevices.currentDevice, configElement.value) + + def inputDevicesDelayChanged(self,configElement): + if self.currentDevice != "" and iInputDevices.currentDevice == "": + iInputDevices.setDelay(self.currentDevice, configElement.value) + elif iInputDevices.currentDevice != "": + iInputDevices.setDelay(iInputDevices.currentDevice, configElement.value) + + def setupConfigEntries(self,device): + cmd = "config.inputDevices." + device + " = ConfigSubsection()" + exec (cmd) + cmd = "config.inputDevices." + device + ".enabled = ConfigYesNo(default = False)" + exec (cmd) + cmd = "config.inputDevices." + device + ".enabled.addNotifier(self.inputDevicesEnabledChanged,config.inputDevices." + device + ".enabled)" + exec (cmd) + cmd = "config.inputDevices." + device + '.name = ConfigText(default="")' + exec (cmd) + cmd = "config.inputDevices." + device + ".name.addNotifier(self.inputDevicesNameChanged,config.inputDevices." + device + ".name)" + exec (cmd) + cmd = "config.inputDevices." + device + ".repeat = ConfigSlider(default=100, increment = 10, limits=(0, 500))" + exec (cmd) + cmd = "config.inputDevices." + device + ".repeat.addNotifier(self.inputDevicesRepeatChanged,config.inputDevices." + device + ".repeat)" + exec (cmd) + cmd = "config.inputDevices." + device + ".delay = ConfigSlider(default=700, increment = 100, limits=(0, 5000))" + exec (cmd) + cmd = "config.inputDevices." + device + ".delay.addNotifier(self.inputDevicesDelayChanged,config.inputDevices." + device + ".delay)" + exec (cmd) - def inputDevicesDelayChanged(configElement): - iDevices.setDelay(configElement.value); - # this will call the "setup-val" initial - config.inputDevices.repeat.addNotifier(inputDevicesRepeatChanged); - config.inputDevices.delay.addNotifier(inputDevicesDelayChanged); +iInputDevices = inputDevices() diff --git a/lib/python/Components/Ipkg.py b/lib/python/Components/Ipkg.py index 0ba1165c..cc559657 100755 --- a/lib/python/Components/Ipkg.py +++ b/lib/python/Components/Ipkg.py @@ -1,4 +1,5 @@ from enigma import eConsoleAppContainer +from Tools.Directories import fileExists class IpkgComponent: EVENT_INSTALL = 0 @@ -18,9 +19,8 @@ class IpkgComponent: CMD_UPDATE = 3 CMD_UPGRADE = 4 - def __init__(self, ipkg = '/usr/bin/ipkg'): + def __init__(self, ipkg = 'opkg'): self.ipkg = ipkg - self.cmd = eConsoleAppContainer() self.cache = None self.callbackList = [] @@ -89,7 +89,7 @@ class IpkgComponent: if data.find('Downloading') == 0: self.callCallbacks(self.EVENT_DOWNLOAD, data.split(' ', 5)[1].strip()) elif data.find('Upgrading') == 0: - self.callCallbacks(self.EVENT_UPGRADE, data.split(' ', 1)[1].split(' ')[0]) + self.callCallbacks(self.EVENT_UPGRADE, data.split(' ', 1)[1].split(' ')[0]) elif data.find('Installing') == 0: self.callCallbacks(self.EVENT_INSTALL, data.split(' ', 1)[1].split(' ')[0]) elif data.find('Removing') == 0: diff --git a/lib/python/Components/Keyboard.py b/lib/python/Components/Keyboard.py index 820d1036..b026cd56 100755 --- a/lib/python/Components/Keyboard.py +++ b/lib/python/Components/Keyboard.py @@ -1,6 +1,7 @@ from Components.Console import Console from os import listdir as os_listdir, path as os_path from re import compile as re_compile +from enigma import eEnv class Keyboard: def __init__(self): @@ -8,9 +9,9 @@ class Keyboard: self.readKeyboardMapFiles() def readKeyboardMapFiles(self): - for keymapfile in os_listdir('/usr/share/keymaps/'): + for keymapfile in os_listdir(eEnv.resolve('${datadir}/keymaps/')): if (keymapfile.endswith(".info")): - f = open('/usr/share/keymaps/' + keymapfile) + f = open(eEnv.resolve('${datadir}/keymaps/') + keymapfile) mapfile = None mapname = None for line in f: @@ -32,7 +33,7 @@ class Keyboard: try: keymap = self.keyboardmaps[index] print "Activating keymap:",keymap[1] - keymappath = '/usr/share/keymaps/' + keymap[0] + keymappath = eEnv.resolve('${datadir}/keymaps/') + keymap[0] if os_path.exists(keymappath): Console().ePopen(("loadkmap < " + str(keymappath))) except: diff --git a/lib/python/Components/Network.py b/lib/python/Components/Network.py index 4b0213d4..32b8bdbe 100755 --- a/lib/python/Components/Network.py +++ b/lib/python/Components/Network.py @@ -4,6 +4,7 @@ from socket import * from enigma import eConsoleAppContainer from Components.Console import Console from Components.PluginComponent import plugins +from Components.About import about from Plugins.Plugin import PluginDescriptor class Network: @@ -14,49 +15,43 @@ class Network: self.NetworkState = 0 self.DnsState = 0 self.nameservers = [] - self.ethtool_bin = "/usr/sbin/ethtool" + self.ethtool_bin = "ethtool" self.container = eConsoleAppContainer() self.Console = Console() self.LinkConsole = Console() self.restartConsole = Console() - self.deactivateConsole = Console() self.deactivateInterfaceConsole = Console() - self.activateConsole = Console() + self.activateInterfaceConsole = Console() self.resetNetworkConsole = Console() self.DnsConsole = Console() self.PingConsole = Console() self.config_ready = None + self.friendlyNames = {} + self.lan_interfaces = [] + self.wlan_interfaces = [] + self.remoteRootFS = None self.getInterfaces() def onRemoteRootFS(self): - fp = file('/proc/mounts', 'r') - mounts = fp.readlines() - fp.close() - for line in mounts: - parts = line.strip().split(' ') - if parts[1] == '/' and (parts[2] == 'nfs' or parts[2] == 'smbfs'): - return True - return False + if self.remoteRootFS == None: + fp = file('/proc/mounts', 'r') + mounts = fp.readlines() + fp.close() + self.remoteRootFS = False + for line in mounts: + parts = line.strip().split() + if parts[1] == '/' and parts[2] == 'nfs': + self.remoteRootFS = True + break + return self.remoteRootFS + + def isBlacklisted(self, iface): + return iface in ('lo', 'wifi0', 'wmaster0') def getInterfaces(self, callback = None): - devicesPattern = re_compile('[a-z]+[0-9]+') self.configuredInterfaces = [] - fp = file('/proc/net/dev', 'r') - result = fp.readlines() - fp.close() - for line in result: - try: - device = devicesPattern.search(line).group() - if device in ('wifi0', 'wmaster0'): - continue - self.getDataForInterface(device, callback) - except AttributeError: - pass - #print "self.ifaces:", self.ifaces - #self.writeNetworkConfig() - #print ord(' ') - #for line in result: - # print ord(line[0]) + for device in self.getInstalledAdapters(): + self.getAddrInet(device, callback) # helper function def regExpMatch(self, pattern, string): @@ -65,37 +60,32 @@ class Network: try: return pattern.search(string).group() except AttributeError: - None + return None # helper function to convert ips from a sring to a list of ints def convertIP(self, ip): - strIP = ip.split('.') - ip = [] - for x in strIP: - ip.append(int(x)) - return ip - - def getDataForInterface(self, iface,callback): - #get ip out of ip addr, as avahi sometimes overrides it in ifconfig. + return [ int(n) for n in ip.split('.') ] + + def getAddrInet(self, iface, callback): if not self.Console: self.Console = Console() - cmd = "ip -o addr" + cmd = "ip -o addr show dev " + iface self.Console.ePopen(cmd, self.IPaddrFinished, [iface,callback]) def IPaddrFinished(self, result, retval, extra_args): (iface, callback ) = extra_args - data = { 'up': False, 'dhcp': False, 'preup' : False, 'postdown' : False } + data = { 'up': False, 'dhcp': False, 'preup' : False, 'predown' : False } globalIPpattern = re_compile("scope global") ipRegexp = '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' netRegexp = '[0-9]{1,2}' - macRegexp = '[0-9]{2}\:[0-9]{2}\:[0-9]{2}\:[a-z0-9]{2}\:[a-z0-9]{2}\:[a-z0-9]{2}' + macRegexp = '[0-9a-fA-F]{2}\:[0-9a-fA-F]{2}\:[0-9a-fA-F]{2}\:[0-9a-fA-F]{2}\:[0-9a-fA-F]{2}\:[0-9a-fA-F]{2}' ipLinePattern = re_compile('inet ' + ipRegexp + '/') ipPattern = re_compile(ipRegexp) netmaskLinePattern = re_compile('/' + netRegexp) netmaskPattern = re_compile(netRegexp) bcastLinePattern = re_compile(' brd ' + ipRegexp) upPattern = re_compile('UP') - macPattern = re_compile('[0-9]{2}\:[0-9]{2}\:[0-9]{2}\:[a-z0-9]{2}\:[a-z0-9]{2}\:[a-z0-9]{2}') + macPattern = re_compile(macRegexp) macLinePattern = re_compile('link/ether ' + macRegexp) for line in result.splitlines(): @@ -140,7 +130,7 @@ class Network: print line[0:7] if line[0:7] == "0.0.0.0": gateway = self.regExpMatch(ipPattern, line[16:31]) - if gateway is not None: + if gateway: data['gateway'] = self.convertIP(gateway) self.ifaces[iface] = data @@ -167,12 +157,14 @@ class Network: if iface.has_key('gateway'): fp.write(" gateway %d.%d.%d.%d\n" % tuple(iface['gateway'])) if iface.has_key("configStrings"): - fp.write("\n" + iface["configStrings"] + "\n") + fp.write(iface["configStrings"]) if iface["preup"] is not False and not iface.has_key("configStrings"): fp.write(iface["preup"]) - fp.write(iface["postdown"]) + if iface["predown"] is not False and not iface.has_key("configStrings"): + fp.write(iface["predown"]) fp.write("\n") fp.close() + self.configuredNetworkAdapters = self.configuredInterfaces self.writeNameserverConfig() def writeNameserverConfig(self): @@ -221,9 +213,9 @@ class Network: if (split[0] == "pre-up"): if self.ifaces[currif].has_key("preup"): self.ifaces[currif]["preup"] = i - if (split[0] == "post-down"): - if self.ifaces[currif].has_key("postdown"): - self.ifaces[currif]["postdown"] = i + if (split[0] in ("pre-down","post-down")): + if self.ifaces[currif].has_key("predown"): + self.ifaces[currif]["predown"] = i for ifacename, iface in ifaces.items(): if self.ifaces.has_key(ifacename): @@ -258,49 +250,13 @@ class Network: for line in resolv: if self.regExpMatch(nameserverPattern, line) is not None: ip = self.regExpMatch(ipPattern, line) - if ip is not None: + if ip: self.nameservers.append(self.convertIP(ip)) print "nameservers:", self.nameservers - def deactivateNetworkConfig(self, callback = None): - if self.onRemoteRootFS(): - if callback is not None: - callback(True) - return - self.deactivateConsole = Console() - self.commands = [] - self.commands.append("/etc/init.d/avahi-daemon stop") - for iface in self.ifaces.keys(): - cmd = "ip addr flush " + iface - self.commands.append(cmd) - self.commands.append("/etc/init.d/networking stop") - self.commands.append("killall -9 udhcpc") - self.commands.append("rm /var/run/udhcpc*") - self.deactivateConsole.eBatch(self.commands, self.deactivateNetworkFinished, callback, debug=True) - - def deactivateNetworkFinished(self,extra_args): - callback = extra_args - if len(self.deactivateConsole.appContainers) == 0: - if callback is not None: - callback(True) - - def activateNetworkConfig(self, callback = None): - if self.onRemoteRootFS(): - if callback is not None: - callback(True) - return - self.activateConsole = Console() - self.commands = [] - self.commands.append("/etc/init.d/networking start") - self.commands.append("/etc/init.d/avahi-daemon start") - self.activateConsole.eBatch(self.commands, self.activateNetworkFinished, callback, debug=True) - - def activateNetworkFinished(self,extra_args): - callback = extra_args - if len(self.activateConsole.appContainers) == 0: - if callback is not None: - callback(True) + def getInstalledAdapters(self): + return [x for x in listdir('/sys/class/net') if not self.isBlacklisted(x)] def getConfiguredAdapters(self): return self.configuredNetworkAdapters @@ -309,13 +265,46 @@ class Network: return len(self.ifaces) def getFriendlyAdapterName(self, x): - # maybe this needs to be replaced by an external list. - friendlyNames = { - "eth0": _("Integrated Ethernet"), - "wlan0": _("Wireless"), - "ath0": _("Integrated Wireless") - } - return friendlyNames.get(x, x) # when we have no friendly name, use adapter name + if x in self.friendlyNames.keys(): + return self.friendlyNames.get(x, x) + self.friendlyNames[x] = self.getFriendlyAdapterNaming(x) + return self.friendlyNames.get(x, x) # when we have no friendly name, use adapter name + + def getFriendlyAdapterNaming(self, iface): + name = None + if self.isWirelessInterface(iface): + if iface not in self.wlan_interfaces: + name = _("WLAN connection") + if len(self.wlan_interfaces): + name += " " + str(len(self.wlan_interfaces)+1) + self.wlan_interfaces.append(iface) + else: + if iface not in self.lan_interfaces: + name = _("LAN connection") + if len(self.lan_interfaces): + name += " " + str(len(self.lan_interfaces)+1) + self.lan_interfaces.append(iface) + return name + + def getFriendlyAdapterDescription(self, iface): + if not self.isWirelessInterface(iface): + return _('Ethernet network interface') + + moduledir = self.getWlanModuleDir(iface) + if moduledir: + name = os_path.basename(os_path.realpath(moduledir)) + if name in ('ath_pci','ath5k'): + name = 'Atheros' + elif name in ('rt73','rt73usb','rt3070sta'): + name = 'Ralink' + elif name == 'zd1211b': + name = 'Zydas' + elif name == 'r871x_usb_drv': + name = 'Realtek' + else: + name = _('Unknown') + + return name + ' ' + _('wireless network interface') def getAdapterName(self, iface): return iface @@ -363,16 +352,12 @@ class Network: self.nameservers[i] = newnameserver def resetNetworkConfig(self, mode='lan', callback = None): - if self.onRemoteRootFS(): - if callback is not None: - callback(True) - return self.resetNetworkConsole = Console() self.commands = [] self.commands.append("/etc/init.d/avahi-daemon stop") for iface in self.ifaces.keys(): - cmd = "ip addr flush " + iface - self.commands.append(cmd) + if iface != 'eth0' or not self.onRemoteRootFS(): + self.commands.append("ip addr flush dev " + iface) self.commands.append("/etc/init.d/networking stop") self.commands.append("killall -9 udhcpc") self.commands.append("rm /var/run/udhcpc*") @@ -446,18 +431,15 @@ class Network: statecallback(self.NetworkState) def restartNetwork(self,callback = None): - if self.onRemoteRootFS(): - if callback is not None: - callback(True) - return self.restartConsole = Console() self.config_ready = False self.msgPlugins() self.commands = [] self.commands.append("/etc/init.d/avahi-daemon stop") for iface in self.ifaces.keys(): - cmd = "ip addr flush " + iface - self.commands.append(cmd) + if iface != 'eth0' or not self.onRemoteRootFS(): + self.commands.append("ifdown " + iface) + self.commands.append("ip addr flush dev " + iface) self.commands.append("/etc/init.d/networking stop") self.commands.append("killall -9 udhcpc") self.commands.append("rm /var/run/udhcpc*") @@ -514,9 +496,13 @@ class Network: def stopDeactivateInterfaceConsole(self): if self.deactivateInterfaceConsole is not None: - if len(self.deactivateInterfaceConsole.appContainers): - for name in self.deactivateInterfaceConsole.appContainers.keys(): - self.deactivateInterfaceConsole.kill(name) + self.deactivateInterfaceConsole.killAll() + self.deactivateInterfaceConsole = None + + def stopActivateInterfaceConsole(self): + if self.activateInterfaceConsole is not None: + self.activateInterfaceConsole.killAll() + self.activateInterfaceConsole = None def checkforInterface(self,iface): if self.getAdapterAttribute(iface, 'up') is True: @@ -549,44 +535,138 @@ class Network: if len(self.DnsConsole.appContainers) == 0: statecallback(self.DnsState) - def deactivateInterface(self,iface,callback = None): - if self.onRemoteRootFS(): + def deactivateInterface(self,ifaces,callback = None): + self.config_ready = False + self.msgPlugins() + commands = [] + def buildCommands(iface): + commands.append("ifdown " + iface) + commands.append("ip addr flush dev " + iface) + #wpa_supplicant sometimes doesn't quit properly on SIGTERM + if os_path.exists('/var/run/wpa_supplicant/'+ iface): + commands.append("wpa_cli -i" + iface + " terminate") + + if not self.deactivateInterfaceConsole: + self.deactivateInterfaceConsole = Console() + + if isinstance(ifaces, (list, tuple)): + for iface in ifaces: + if iface != 'eth0' or not self.onRemoteRootFS(): + buildCommands(iface) + else: + if ifaces == 'eth0' and self.onRemoteRootFS(): + if callback is not None: + callback(True) + return + buildCommands(ifaces) + self.deactivateInterfaceConsole.eBatch(commands, self.deactivateInterfaceFinished, [ifaces,callback], debug=True) + + def deactivateInterfaceFinished(self,extra_args): + (ifaces, callback) = extra_args + def checkCommandResult(iface): + if self.deactivateInterfaceConsole and self.deactivateInterfaceConsole.appResults.has_key("ifdown " + iface): + result = str(self.deactivateInterfaceConsole.appResults.get("ifdown " + iface)).strip("\n") + if result == "ifdown: interface " + iface + " not configured": + return False + else: + return True + #ifdown sometimes can't get the interface down. + if isinstance(ifaces, (list, tuple)): + for iface in ifaces: + if checkCommandResult(iface) is False: + Console().ePopen(("ifconfig " + iface + " down" )) + else: + if checkCommandResult(ifaces) is False: + Console().ePopen(("ifconfig " + ifaces + " down" )) + + if self.deactivateInterfaceConsole: + if len(self.deactivateInterfaceConsole.appContainers) == 0: + if callback is not None: + callback(True) + + def activateInterface(self,iface,callback = None): + if self.config_ready: + self.config_ready = False + self.msgPlugins() + if iface == 'eth0' and self.onRemoteRootFS(): if callback is not None: callback(True) return - self.deactivateInterfaceConsole = Console() - self.commands = [] - cmd1 = "ip addr flush " + iface - cmd2 = "ifconfig " + iface + " down" - self.commands.append(cmd1) - self.commands.append(cmd2) - self.deactivateInterfaceConsole.eBatch(self.commands, self.deactivateInterfaceFinished, callback, debug=True) + if not self.activateInterfaceConsole: + self.activateInterfaceConsole = Console() + commands = [] + commands.append("ifup " + iface) + self.activateInterfaceConsole.eBatch(commands, self.activateInterfaceFinished, callback, debug=True) - def deactivateInterfaceFinished(self,extra_args): + def activateInterfaceFinished(self,extra_args): callback = extra_args - if self.deactivateInterfaceConsole: - if len(self.deactivateInterfaceConsole.appContainers) == 0: + if self.activateInterfaceConsole: + if len(self.activateInterfaceConsole.appContainers) == 0: if callback is not None: callback(True) - def detectWlanModule(self): - self.wlanmodule = None - rt73_dir = "/sys/bus/usb/drivers/rt73/" - zd1211b_dir = "/sys/bus/usb/drivers/zd1211b/" - madwifi_dir = "/sys/bus/pci/drivers/ath_pci/" - if os_path.exists(madwifi_dir): - files = listdir(madwifi_dir) - if len(files) >= 1: - self.wlanmodule = 'madwifi' - if os_path.exists(rt73_dir): - rtfiles = listdir(rt73_dir) - if len(rtfiles) == 2: - self.wlanmodule = 'ralink' - if os_path.exists(zd1211b_dir): - zdfiles = listdir(zd1211b_dir) - if len(zdfiles) == 1: - self.wlanmodule = 'zydas' - return self.wlanmodule + def sysfsPath(self, iface): + return '/sys/class/net/' + iface + + def isWirelessInterface(self, iface): + if iface in self.wlan_interfaces: + return True + + if os_path.isdir(self.sysfsPath(iface) + '/wireless'): + return True + + # r871x_usb_drv on kernel 2.6.12 is not identifiable over /sys/class/net/'ifacename'/wireless so look also inside /proc/net/wireless + device = re_compile('[a-z]{2,}[0-9]*:') + ifnames = [] + fp = open('/proc/net/wireless', 'r') + for line in fp: + try: + ifnames.append(device.search(line).group()[:-1]) + except AttributeError: + pass + if iface in ifnames: + return True + + return False + + def getWlanModuleDir(self, iface = None): + devicedir = self.sysfsPath(iface) + '/device' + moduledir = devicedir + '/driver/module' + if os_path.isdir(moduledir): + return moduledir + + # identification is not possible over default moduledir + for x in listdir(devicedir): + # rt3070 on kernel 2.6.18 registers wireless devices as usb_device (e.g. 1-1.3:1.0) and identification is only possible over /sys/class/net/'ifacename'/device/1-xxx + if x.startswith("1-"): + moduledir = devicedir + '/' + x + '/driver/module' + if os_path.isdir(moduledir): + return moduledir + # rt73, zd1211b, r871x_usb_drv on kernel 2.6.12 can be identified over /sys/class/net/'ifacename'/device/driver, so look also here + moduledir = devicedir + '/driver' + if os_path.isdir(moduledir): + return moduledir + + return None + + def detectWlanModule(self, iface = None): + if not self.isWirelessInterface(iface): + return None + + devicedir = self.sysfsPath(iface) + '/device' + if os_path.isdir(devicedir + '/ieee80211'): + return 'nl80211' + + moduledir = self.getWlanModuleDir(iface) + if moduledir: + module = os_path.basename(os_path.realpath(moduledir)) + if module in ('ath_pci','ath5k'): + return 'madwifi' + if module in ('rt73','rt73'): + return 'ralink' + if module == 'zd1211b': + return 'zydas' + return 'wext' def calc_netmask(self,nmask): from struct import pack, unpack diff --git a/lib/python/Components/NimManager.py b/lib/python/Components/NimManager.py index 00d06095..bd1a3638 100644 --- a/lib/python/Components/NimManager.py +++ b/lib/python/Components/NimManager.py @@ -1,4 +1,5 @@ from Tools.HardwareInfo import HardwareInfo +from Tools.BoundFunction import boundFunction from config import config, ConfigSubsection, ConfigSelection, ConfigFloat, \ ConfigSatlist, ConfigYesNo, ConfigInteger, ConfigSubList, ConfigNothing, \ @@ -9,10 +10,14 @@ from enigma import eDVBSatelliteEquipmentControl as secClass, \ eDVBSatelliteDiseqcParameters as diseqcParam, \ eDVBSatelliteSwitchParameters as switchParam, \ eDVBSatelliteRotorParameters as rotorParam, \ - eDVBResourceManager, eDVBDB + eDVBResourceManager, eDVBDB, eEnv from time import localtime, mktime from datetime import datetime +from Tools.BoundFunction import boundFunction + +from Tools import Directories +import xml.etree.cElementTree def getConfigSatlist(orbpos, satlist): default_orbpos = None @@ -105,9 +110,16 @@ class SecConfigure: def setSatposDepends(self, sec, nim1, nim2): print "tuner", nim1, "depends on satpos of", nim2 sec.setTunerDepends(nim1, nim2) + + def linkInternally(self, slotid): + nim = self.NimManager.getNim(slotid) + if nim.internallyConnectableTo is not None: + nim.setInternalLink() def linkNIMs(self, sec, nim1, nim2): print "link tuner", nim1, "to tuner", nim2 + if nim2 == (nim1 - 1): + self.linkInternally(nim1) sec.setTunerLinked(nim1, nim2) def getRoot(self, slotid, connto): @@ -122,6 +134,9 @@ class SecConfigure: def update(self): sec = secClass.getInstance() self.configuredSatellites = set() + for slotid in self.NimManager.getNimListOfType("DVB-S"): + if self.NimManager.nimInternallyConnectableTo(slotid) is not None: + self.NimManager.nimRemoveInternalLink(slotid) sec.clear() ## this do unlinking NIMs too !! print "sec config cleared" @@ -135,7 +150,7 @@ class SecConfigure: for slot in nim_slots: if slot.type is not None: - used_nim_slots.append((slot.slot, slot.description, slot.config.configMode.value != "nothing" and True or False, slot.isCompatible("DVB-S2"))) + used_nim_slots.append((slot.slot, slot.description, slot.config.configMode.value != "nothing" and True or False, slot.isCompatible("DVB-S2"), slot.frontend_id is None and -1 or slot.frontend_id)) eDVBResourceManager.getInstance().setFrontendSlotInformations(used_nim_slots) for slot in nim_slots: @@ -232,6 +247,20 @@ class SecConfigure: print "sec config completed" def updateAdvanced(self, sec, slotid): + try: + if config.Nims[slotid].advanced.unicableconnected is not None: + if config.Nims[slotid].advanced.unicableconnected.value == True: + config.Nims[slotid].advanced.unicableconnectedTo.save_forced = True + self.linkNIMs(sec, slotid, int(config.Nims[slotid].advanced.unicableconnectedTo.value)) + connto = self.getRoot(slotid, int(config.Nims[slotid].advanced.unicableconnectedTo.value)) + if not self.linked.has_key(connto): + self.linked[connto] = [] + self.linked[connto].append(slotid) + else: + config.Nims[slotid].advanced.unicableconnectedTo.save_forced = False + except: + pass + lnbSat = {} for x in range(1,37): lnbSat[x] = [] @@ -274,24 +303,32 @@ class SecConfigure: sec.setLNBLOFH(10600000) sec.setLNBThreshold(11700000) elif currLnb.lof.value == "unicable": - sec.setLNBLOFL(9750000) - sec.setLNBLOFH(10600000) - sec.setLNBThreshold(11700000) + def setupUnicable(configManufacturer, ProductDict): + manufacturer_name = configManufacturer.value + manufacturer = ProductDict[manufacturer_name] + product_name = manufacturer.product.value + sec.setLNBSatCR(manufacturer.scr[product_name].index) + sec.setLNBSatCRvco(manufacturer.vco[product_name][manufacturer.scr[product_name].index].value*1000) + sec.setLNBSatCRpositions(manufacturer.positions[product_name][0].value) + sec.setLNBLOFL(manufacturer.lofl[product_name][0].value * 1000) + sec.setLNBLOFH(manufacturer.lofh[product_name][0].value * 1000) + sec.setLNBThreshold(manufacturer.loft[product_name][0].value * 1000) + configManufacturer.save_forced = True + manufacturer.product.save_forced = True + manufacturer.vco[product_name][manufacturer.scr[product_name].index].save_forced = True + if currLnb.unicable.value == "unicable_user": +#TODO satpositions for satcruser + sec.setLNBLOFL(currLnb.lofl.value * 1000) + sec.setLNBLOFH(currLnb.lofh.value * 1000) + sec.setLNBThreshold(currLnb.threshold.value * 1000) sec.setLNBSatCR(currLnb.satcruser.index) sec.setLNBSatCRvco(currLnb.satcrvcouser[currLnb.satcruser.index].value*1000) + sec.setLNBSatCRpositions(1) #HACK elif currLnb.unicable.value == "unicable_matrix": - manufacturer_name = currLnb.unicableMatrixManufacturer.value - manufacturer = currLnb.unicableMatrix[manufacturer_name] - product_name = manufacturer.product.value - sec.setLNBSatCR(manufacturer.scr[product_name].index) - sec.setLNBSatCRvco(manufacturer.vco[product_name][manufacturer.scr[product_name].index].value*1000) + setupUnicable(currLnb.unicableMatrixManufacturer, currLnb.unicableMatrix) elif currLnb.unicable.value == "unicable_lnb": - manufacturer_name = currLnb.unicableLnbManufacturer.value - manufacturer = currLnb.unicableLnb[manufacturer_name] - product_name = manufacturer.product.value - sec.setLNBSatCR(manufacturer.scr[product_name].index) - sec.setLNBSatCRvco(manufacturer.vco[product_name][manufacturer.scr[product_name].index].value*1000) + setupUnicable(currLnb.unicableLnbManufacturer, currLnb.unicableLnb) elif currLnb.lof.value == "c_band": sec.setLNBLOFL(5150000) sec.setLNBLOFH(5150000) @@ -353,6 +390,7 @@ class SecConfigure: if currLnb.diseqcMode.value == "1_0": currCO = currLnb.commandOrder1_0.value + sec.setRepeats(0) else: currCO = currLnb.commandOrder.value @@ -444,7 +482,7 @@ class SecConfigure: self.update() class NIM(object): - def __init__(self, slot, type, description, has_outputs = True, internally_connectable = None): + def __init__(self, slot, type, description, has_outputs = True, internally_connectable = None, multi_type = {}, frontend_id = None, i2c = None, is_empty = False): self.slot = slot if type not in ("DVB-S", "DVB-C", "DVB-T", "DVB-S2", None): @@ -455,8 +493,14 @@ class NIM(object): self.description = description self.has_outputs = has_outputs self.internally_connectable = internally_connectable + self.multi_type = multi_type + self.i2c = i2c + self.frontend_id = frontend_id + self.__is_empty = is_empty def isCompatible(self, what): + if not self.isSupported(): + return False compatible = { None: (None,), "DVB-S": ("DVB-S", None), @@ -466,6 +510,9 @@ class NIM(object): } return what in compatible[self.type] + def getType(self): + return self.type + def connectableTo(self): connectable = { "DVB-S": ("DVB-S", "DVB-S2"), @@ -486,11 +533,38 @@ class NIM(object): def getSlotID(self): return chr(ord('A') + self.slot) + def getI2C(self): + return self.i2c + def hasOutputs(self): return self.has_outputs def internallyConnectableTo(self): return self.internally_connectable + + def setInternalLink(self): + if self.internally_connectable is not None: + print "setting internal link on frontend id", self.frontend_id + open("/proc/stb/frontend/%d/rf_switch" % self.frontend_id, "w").write("internal") + + def removeInternalLink(self): + if self.internally_connectable is not None: + print "removing internal link on frontend id", self.frontend_id + open("/proc/stb/frontend/%d/rf_switch" % self.frontend_id, "w").write("external") + + def isMultiType(self): + return (len(self.multi_type) > 0) + + def isEmpty(self): + return self.__is_empty + + # empty tuners are supported! + def isSupported(self): + return (self.frontend_id is not None) or self.__is_empty + + # returns dict {<slotid>: <type>} + def getMultiTypeList(self): + return self.multi_type slot_id = property(getSlotID) @@ -510,8 +584,10 @@ class NIM(object): if self.empty: nim_text += _("(empty)") + elif not self.isSupported(): + nim_text += self.description + " (" + _("not supported") + ")" else: - nim_text += self.description + " (" + self.friendly_type + ")" + nim_text += self.description + " (" + self.friendly_type + ")" return nim_text @@ -629,33 +705,64 @@ class NimManager: entries[current_slot] = {} elif line.strip().startswith("Type:"): entries[current_slot]["type"] = str(line.strip()[6:]) + entries[current_slot]["isempty"] = False elif line.strip().startswith("Name:"): entries[current_slot]["name"] = str(line.strip()[6:]) + entries[current_slot]["isempty"] = False elif line.strip().startswith("Has_Outputs:"): input = str(line.strip()[len("Has_Outputs:") + 1:]) entries[current_slot]["has_outputs"] = (input == "yes") elif line.strip().startswith("Internally_Connectable:"): input = int(line.strip()[len("Internally_Connectable:") + 1:]) - entries[current_slot]["internally_connectable"] = input + entries[current_slot]["internally_connectable"] = input + elif line.strip().startswith("Frontend_Device:"): + input = int(line.strip()[len("Frontend_Device:") + 1:]) + entries[current_slot]["frontend_device"] = input + elif line.strip().startswith("Mode"): + # "Mode 0: DVB-T" -> ["Mode 0", " DVB-T"] + split = line.strip().split(":") + # "Mode 0" -> ["Mode, "0"] + split2 = split[0].split(" ") + modes = entries[current_slot].get("multi_type", {}) + modes[split2[1]] = split[1].strip() + entries[current_slot]["multi_type"] = modes + elif line.strip().startswith("I2C_Device:"): + input = int(line.strip()[len("I2C_Device:") + 1:]) + entries[current_slot]["i2c"] = input elif line.strip().startswith("empty"): entries[current_slot]["type"] = None entries[current_slot]["name"] = _("N/A") + entries[current_slot]["isempty"] = True nimfile.close() + from os import path + for id, entry in entries.items(): if not (entry.has_key("name") and entry.has_key("type")): entry["name"] = _("N/A") entry["type"] = None + if not (entry.has_key("i2c")): + entry["i2c"] = None if not (entry.has_key("has_outputs")): entry["has_outputs"] = True - if not (entry.has_key("internally_connectable")): - entry["internally_connectable"] = None - self.nim_slots.append(NIM(slot = id, description = entry["name"], type = entry["type"], has_outputs = entry["has_outputs"], internally_connectable = entry["internally_connectable"])) + if entry.has_key("frontend_device"): # check if internally connectable + if path.exists("/proc/stb/frontend/%d/rf_switch" % entry["frontend_device"]): + entry["internally_connectable"] = entry["frontend_device"] - 1 + else: + entry["internally_connectable"] = None + else: + entry["frontend_device"] = entry["internally_connectable"] = None + if not (entry.has_key("multi_type")): + entry["multi_type"] = {} + self.nim_slots.append(NIM(slot = id, description = entry["name"], type = entry["type"], has_outputs = entry["has_outputs"], internally_connectable = entry["internally_connectable"], multi_type = entry["multi_type"], frontend_id = entry["frontend_device"], i2c = entry["i2c"], is_empty = entry["isempty"])) def hasNimType(self, chktype): for slot in self.nim_slots: if slot.isCompatible(chktype): return True + for type in slot.getMultiTypeList().values(): + if chktype == type: + return True return False def getNimType(self, slotid): @@ -663,6 +770,15 @@ class NimManager: def getNimDescription(self, slotid): return self.nim_slots[slotid].friendly_full_description + + def getNimName(self, slotid): + return self.nim_slots[slotid].description + + def getNim(self, slotid): + return self.nim_slots[slotid] + + def getI2CDevice(self, slotid): + return self.nim_slots[slotid].getI2C() def getNimListOfType(self, type, exception = -1): # returns a list of indexes for NIMs compatible to the given type, except for 'exception' @@ -693,6 +809,12 @@ class NimManager: def hasOutputs(self, slotid): return self.nim_slots[slotid].hasOutputs() + def nimInternallyConnectableTo(self, slotid): + return self.nim_slots[slotid].internallyConnectableTo() + + def nimRemoveInternalLink(self, slotid): + self.nim_slots[slotid].removeInternalLink() + def canConnectTo(self, slotid): slots = [] if self.nim_slots[slotid].internallyConnectableTo() is not None: @@ -938,70 +1060,78 @@ def InitSecParams(): # the configElement should be only visible when diseqc 1.2 is disabled def InitNimManager(nimmgr): - InitSecParams() hw = HardwareInfo() + addNimConfig = False + try: + config.Nims + except: + addNimConfig = True - config.Nims = ConfigSubList() - for x in range(len(nimmgr.nim_slots)): - config.Nims.append(ConfigSubsection()) + if addNimConfig: + InitSecParams() + config.Nims = ConfigSubList() + for x in range(len(nimmgr.nim_slots)): + config.Nims.append(ConfigSubsection()) lnb_choices = { "universal_lnb": _("Universal LNB"), -# "unicable": _("Unicable"), + "unicable": _("Unicable"), "c_band": _("C-Band"), "user_defined": _("User defined")} lnb_choices_default = "universal_lnb" - unicablelnbproducts = { - "Humax": {"150 SCR":("1210","1420","1680","2040")}, - "Inverto": {"IDLP-40UNIQD+S":("1680","1420","2040","1210")}, - "Kathrein": {"UAS481":("1400","1516","1632","1748")}, - "Kreiling": {"KR1440":("1680","1420","2040","1210")}, - "Radix": {"Unicable LNB":("1680","1420","2040","1210")}, - "Wisi": {"OC 05":("1210","1420","1680","2040")}} + unicablelnbproducts = {} + unicablematrixproducts = {} + doc = xml.etree.cElementTree.parse(eEnv.resolve("${datadir}/enigma2/unicable.xml")) + root = doc.getroot() + + entry = root.find("lnb") + for manufacturer in entry.getchildren(): + m={} + for product in manufacturer.getchildren(): + scr=[] + lscr=("scr1","scr2","scr3","scr4","scr5","scr6","scr7","scr8") + for i in range(len(lscr)): + scr.append(product.get(lscr[i],"0")) + for i in range(len(lscr)): + if scr[len(lscr)-i-1] == "0": + scr.pop() + else: + break; + lof=[] + lof.append(int(product.get("positions",1))) + lof.append(int(product.get("lofl",9750))) + lof.append(int(product.get("lofh",10600))) + lof.append(int(product.get("threshold",11700))) + scr.append(tuple(lof)) + m.update({product.get("name"):tuple(scr)}) + unicablelnbproducts.update({manufacturer.get("name"):m}) + + entry = root.find("matrix") + for manufacturer in entry.getchildren(): + m={} + for product in manufacturer.getchildren(): + scr=[] + lscr=("scr1","scr2","scr3","scr4","scr5","scr6","scr7","scr8") + for i in range(len(lscr)): + scr.append(product.get(lscr[i],"0")) + for i in range(len(lscr)): + if scr[len(lscr)-i-1] == "0": + scr.pop() + else: + break; + lof=[] + lof.append(int(product.get("positions",1))) + lof.append(int(product.get("lofl",9750))) + lof.append(int(product.get("lofh",10600))) + lof.append(int(product.get("threshold",11700))) + scr.append(tuple(lof)) + m.update({product.get("name"):tuple(scr)}) + unicablematrixproducts.update({manufacturer.get("name"):m}) + UnicableLnbManufacturers = unicablelnbproducts.keys() UnicableLnbManufacturers.sort() - - unicablematrixproducts = { - "Ankaro": { - "UCS 51440":("1400","1632","1284","1516"), - "UCS 51820":("1400","1632","1284","1516","1864","2096","1748","1980"), - "UCS 51840":("1400","1632","1284","1516","1864","2096","1748","1980"), - "UCS 52240":("1400","1632"), - "UCS 52420":("1400","1632","1284","1516"), - "UCS 52440":("1400","1632","1284","1516"), - "UCS 91440":("1400","1632","1284","1516"), - "UCS 91820":("1400","1632","1284","1516","1864","2096","1748","1980"), - "UCS 91840":("1400","1632","1284","1516","1864","2096","1748","1980"), - "UCS 92240":("1400","1632"), - "UCS 92420":("1400","1632","1284","1516"), - "UCS 92440":("1400","1632","1284","1516")}, - "DCT Delta": { - "SUM518":("1284","1400","1516","1632","1748","1864","1980","2096"), - "SUM918":("1284","1400","1516","1632","1748","1864","1980","2096"), - "SUM928":("1284","1400","1516","1632","1748","1864","1980","2096")}, - "Inverto": { - "IDLP-UST11O-CUO1O-8PP":("1076","1178","1280","1382","1484","1586","1688","1790")}, - "Kathrein": { - "EXR501":("1400","1516","1632","1748"), - "EXR551":("1400","1516","1632","1748"), - "EXR552":("1400","1516")}, - "ROTEK": { - "EKL2/1":("1400","1516"), - "EKL2/1E":("0","0","1632","1748")}, - "Smart": { - "DPA 51":("1284","1400","1516","1632","1748","1864","1980","2096")}, - "Technisat": { - "TechniRouter 5/1x8 G":("1284","1400","1516","1632","1748","1864","1980","2096"), - "TechniRouter 5/1x8 K":("1284","1400","1516","1632","1748","1864","1980","2096"), - "TechniRouter 5/2x4 G":("1284","1400","1516","1632"), - "TechniRouter 5/2x4 K":("1284","1400","1516","1632")}, - "Telstar": { - "SCR 5/1x8 G":("1284","1400","1516","1632","1748","1864","1980","2096"), - "SCR 5/1x8 K":("1284","1400","1516","1632","1748","1864","1980","2096"), - "SCR 5/2x4 G":("1284","1400","1516","1632"), - "SCR 5/2x4 K":("1284","1400","1516","1632")}} UnicableMatrixManufacturers = unicablematrixproducts.keys() UnicableMatrixManufacturers.sort() @@ -1068,63 +1198,76 @@ def InitNimManager(nimmgr): else: section.unicable = ConfigSelection(choices = {"unicable_user": _("User defined")}, default = "unicable_user") - if lnb < 3: - section.unicableMatrix = ConfigSubDict() - section.unicableMatrixManufacturer = ConfigSelection(choices = UnicableMatrixManufacturers, default = UnicableMatrixManufacturers[0]) - for y in unicablematrixproducts: - products = unicablematrixproducts[y].keys() + def fillUnicableConf(sectionDict, unicableproducts, vco_null_check): + for y in unicableproducts: + products = unicableproducts[y].keys() products.sort() tmp = ConfigSubsection() tmp.product = ConfigSelection(choices = products, default = products[0]) tmp.scr = ConfigSubDict() tmp.vco = ConfigSubDict() + tmp.lofl = ConfigSubDict() + tmp.lofh = ConfigSubDict() + tmp.loft = ConfigSubDict() + tmp.positions = ConfigSubDict() for z in products: scrlist = [] - vcolist = unicablematrixproducts[y][z] + vcolist = unicableproducts[y][z] tmp.vco[z] = ConfigSubList() - for cnt in range(1,1+len(vcolist)): + for cnt in range(1,1+len(vcolist)-1): vcofreq = int(vcolist[cnt-1]) - if vcofreq == 0: + if vcofreq == 0 and vco_null_check: scrlist.append(("%d" %cnt,"SCR %d " %cnt +_("not used"))) else: scrlist.append(("%d" %cnt,"SCR %d" %cnt)) tmp.vco[z].append(ConfigInteger(default=vcofreq, limits = (vcofreq, vcofreq))) - tmp.scr[z] = ConfigSelection(choices = scrlist, default = scrlist[0][0]) - section.unicableMatrix[y] = tmp + tmp.scr[z] = ConfigSelection(choices = scrlist, default = scrlist[0][0]) + + positions = int(vcolist[len(vcolist)-1][0]) + tmp.positions[z] = ConfigSubList() + tmp.positions[z].append(ConfigInteger(default=positions, limits = (positions, positions))) + + lofl = vcolist[len(vcolist)-1][1] + tmp.lofl[z] = ConfigSubList() + tmp.lofl[z].append(ConfigInteger(default=lofl, limits = (lofl, lofl))) + + lofh = int(vcolist[len(vcolist)-1][2]) + tmp.lofh[z] = ConfigSubList() + tmp.lofh[z].append(ConfigInteger(default=lofh, limits = (lofh, lofh))) + + loft = int(vcolist[len(vcolist)-1][3]) + tmp.loft[z] = ConfigSubList() + tmp.loft[z].append(ConfigInteger(default=loft, limits = (loft, loft))) + sectionDict[y] = tmp + + if lnb < 3: + print "MATRIX" + section.unicableMatrix = ConfigSubDict() + section.unicableMatrixManufacturer = ConfigSelection(UnicableMatrixManufacturers, UnicableMatrixManufacturers[0]) + fillUnicableConf(section.unicableMatrix, unicablematrixproducts, True) if lnb < 2: + print "LNB" section.unicableLnb = ConfigSubDict() section.unicableLnbManufacturer = ConfigSelection(UnicableLnbManufacturers, UnicableLnbManufacturers[0]) - for y in unicablelnbproducts: - products = unicablelnbproducts[y].keys() - products.sort() - tmp = ConfigSubsection() - tmp.product = ConfigSelection(choices = products, default = products[0]) - tmp.scr = ConfigSubDict() - tmp.vco = ConfigSubDict() - for z in products: - scrlist = [] - vcolist = unicablelnbproducts[y][z] - tmp.vco[z] = ConfigSubList() - for cnt in range(1,1+len(vcolist)): - scrlist.append(("%d" %cnt,"SCR %d" %cnt)) - vcofreq = int(vcolist[cnt-1]) - tmp.vco[z].append(ConfigInteger(default=vcofreq, limits = (vcofreq, vcofreq))) - tmp.scr[z] = ConfigSelection(choices = scrlist, default = scrlist[0][0]) - section.unicableLnb[y] = tmp + fillUnicableConf(section.unicableLnb, unicablelnbproducts, False) +#TODO satpositions for satcruser section.satcruser = ConfigSelection(advanced_lnb_satcruser_choices, default="1") tmp = ConfigSubList() - tmp.append(ConfigInteger(default=1284, limits = (0, 9999))) - tmp.append(ConfigInteger(default=1400, limits = (0, 9999))) - tmp.append(ConfigInteger(default=1516, limits = (0, 9999))) - tmp.append(ConfigInteger(default=1632, limits = (0, 9999))) - tmp.append(ConfigInteger(default=1748, limits = (0, 9999))) - tmp.append(ConfigInteger(default=1864, limits = (0, 9999))) - tmp.append(ConfigInteger(default=1980, limits = (0, 9999))) - tmp.append(ConfigInteger(default=2096, limits = (0, 9999))) + tmp.append(ConfigInteger(default=1284, limits = (950, 2150))) + tmp.append(ConfigInteger(default=1400, limits = (950, 2150))) + tmp.append(ConfigInteger(default=1516, limits = (950, 2150))) + tmp.append(ConfigInteger(default=1632, limits = (950, 2150))) + tmp.append(ConfigInteger(default=1748, limits = (950, 2150))) + tmp.append(ConfigInteger(default=1864, limits = (950, 2150))) + tmp.append(ConfigInteger(default=1980, limits = (950, 2150))) + tmp.append(ConfigInteger(default=2096, limits = (950, 2150))) section.satcrvcouser = tmp + nim.advanced.unicableconnected = ConfigYesNo(default=False) + nim.advanced.unicableconnectedTo = ConfigSelection([(str(id), nimmgr.getNimDescription(id)) for id in nimmgr.getNimListOfType("DVB-S") if id != x]) + def configDiSEqCModeChanged(configElement): section = configElement.section if configElement.value == "1_2" and isinstance(section.longitude, ConfigNothing): @@ -1133,7 +1276,7 @@ def InitNimManager(nimmgr): section.latitude = ConfigFloat(default = [50,767], limits = [(0,359),(0,999)]) section.latitudeOrientation = ConfigSelection(latitude_orientation_choices, "north") section.powerMeasurement = ConfigYesNo(default=True) - section.powerThreshold = ConfigInteger(default=hw.get_device_name() == "dm8000" and 15 or 50, limits=(0, 100)) + section.powerThreshold = ConfigInteger(default=hw.get_device_name() == "dm7025" and 50 or 15, limits=(0, 100)) section.turningSpeed = ConfigSelection(turning_speed_choices, "fast") section.fastTurningBegin = ConfigDateTime(default=advanced_lnb_fast_turning_btime, formatstring = _("%H:%M"), increment = 600) section.fastTurningEnd = ConfigDateTime(default=advanced_lnb_fast_turning_etime, formatstring = _("%H:%M"), increment = 600) @@ -1219,12 +1362,57 @@ def InitNimManager(nimmgr): if nimmgr.nim_slots[slot_id].description == 'Alps BSBE2': open("/proc/stb/frontend/%d/tone_amplitude" %(fe_id), "w").write(configElement.value) + def tunerTypeChanged(nimmgr, configElement): + fe_id = configElement.fe_id + + cur_type = int(open("/proc/stb/frontend/%d/mode" % (fe_id), "r").read()) + if cur_type != int(configElement.value): + print "tunerTypeChanged feid %d from %d to mode %d" % (fe_id, cur_type, int(configElement.value)) + + try: + oldvalue = open("/sys/module/dvb_core/parameters/dvb_shutdown_timeout", "r").readline() + open("/sys/module/dvb_core/parameters/dvb_shutdown_timeout", "w").write("0") + except: + print "[info] no /sys/module/dvb_core/parameters/dvb_shutdown_timeout available" + + frontend = eDVBResourceManager.getInstance().allocateRawChannel(fe_id).getFrontend() + frontend.closeFrontend() + open("/proc/stb/frontend/%d/mode" % (fe_id), "w").write(configElement.value) + frontend.reopenFrontend() + try: + open("/sys/module/dvb_core/parameters/dvb_shutdown_timeout", "w").write(oldvalue) + except: + print "[info] no /sys/module/dvb_core/parameters/dvb_shutdown_timeout available" + nimmgr.enumerateNIMs() + else: + print "tuner type is already already %d" %cur_type + + empty_slots = 0 + for slot in nimmgr.nim_slots: + x = slot.slot + nim = config.Nims[x] + addMultiType = False + try: + nim.multiType + except: + addMultiType = True + if slot.isMultiType() and addMultiType: + typeList = [] + for id in slot.getMultiTypeList().keys(): + type = slot.getMultiTypeList()[id] + typeList.append((id, type)) + nim.multiType = ConfigSelection(typeList, "0") + + nim.multiType.fe_id = x - empty_slots + nim.multiType.addNotifier(boundFunction(tunerTypeChanged, nimmgr)) + empty_slots = 0 for slot in nimmgr.nim_slots: x = slot.slot nim = config.Nims[x] + if slot.isCompatible("DVB-S"): - nim.toneAmplitude = ConfigSelection([("9", "600mV"), ("8", "700mV"), ("7", "800mV"), ("6", "900mV"), ("5", "1100mV")], "7") + nim.toneAmplitude = ConfigSelection([("11", "340mV"), ("10", "360mV"), ("9", "600mV"), ("8", "700mV"), ("7", "800mV"), ("6", "900mV"), ("5", "1100mV")], "7") nim.toneAmplitude.fe_id = x - empty_slots nim.toneAmplitude.slot_id = x nim.toneAmplitude.addNotifier(toneAmplitudeChanged) diff --git a/lib/python/Components/ParentalControl.py b/lib/python/Components/ParentalControl.py index d68e01ff..0ea65cd2 100755..100644 --- a/lib/python/Components/ParentalControl.py +++ b/lib/python/Components/ParentalControl.py @@ -1,19 +1,36 @@ -from Components.config import config, ConfigSubsection, ConfigSelection, ConfigPIN, ConfigYesNo, ConfigSubList, ConfigInteger +from Components.config import config, ConfigSubsection, ConfigSelection, ConfigPIN, ConfigText, ConfigYesNo, ConfigSubList, ConfigInteger +#from Screens.ChannelSelection import service_types_tv from Screens.InputBox import PinInput from Screens.MessageBox import MessageBox from Tools.BoundFunction import boundFunction from ServiceReference import ServiceReference from Tools import Notifications from Tools.Directories import resolveFilename, SCOPE_CONFIG +from enigma import eTimer +import time + +TYPE_SERVICE = "SERVICE" +TYPE_BOUQUETSERVICE = "BOUQUETSERVICE" +TYPE_BOUQUET = "BOUQUET" +LIST_BLACKLIST = "blacklist" +LIST_WHITELIST = "whitelist" + +IMG_WHITESERVICE = LIST_WHITELIST + "-" + TYPE_SERVICE +IMG_WHITEBOUQUET = LIST_WHITELIST + "-" + TYPE_BOUQUET +IMG_BLACKSERVICE = LIST_BLACKLIST + "-" + TYPE_SERVICE +IMG_BLACKBOUQUET = LIST_BLACKLIST + "-" + TYPE_BOUQUET def InitParentalControl(): + global parentalControl + parentalControl = ParentalControl() config.ParentalControl = ConfigSubsection() config.ParentalControl.configured = ConfigYesNo(default = False) config.ParentalControl.mode = ConfigSelection(default = "simple", choices = [("simple", _("simple")), ("complex", _("complex"))]) - config.ParentalControl.storeservicepin = ConfigSelection(default = "never", choices = [("never", _("never")), ("5_minutes", _("5 minutes")), ("30_minutes", _("30 minutes")), ("60_minutes", _("60 minutes")), ("restart", _("until restart"))]) + config.ParentalControl.storeservicepin = ConfigSelection(default = "never", choices = [("never", _("never")), ("5", _("5 minutes")), ("30", _("30 minutes")), ("60", _("60 minutes")), ("standby", _("until standby/restart"))]) + config.ParentalControl.storeservicepincancel = ConfigSelection(default = "never", choices = [("never", _("never")), ("5", _("5 minutes")), ("30", _("30 minutes")), ("60", _("60 minutes")), ("standby", _("until standby/restart"))]) config.ParentalControl.servicepinactive = ConfigYesNo(default = False) config.ParentalControl.setuppinactive = ConfigYesNo(default = False) - config.ParentalControl.type = ConfigSelection(default = "blacklist", choices = [("whitelist", _("whitelist")), ("blacklist", _("blacklist"))]) + config.ParentalControl.type = ConfigSelection(default = "blacklist", choices = [(LIST_WHITELIST, _("whitelist")), (LIST_BLACKLIST, _("blacklist"))]) config.ParentalControl.setuppin = ConfigPIN(default = -1) config.ParentalControl.retries = ConfigSubsection() @@ -39,40 +56,54 @@ def InitParentalControl(): class ParentalControl: def __init__(self): + #Do not call open on init, because bouquets are not ready at that moment self.open() self.serviceLevel = {} - - def addWhitelistService(self, service): - self.whitelist.append(service) + #Instead: Use Flags to see, if we already initialized config and called open + self.configInitialized = False + #This is the timer that is used to see, if the time for caching the pin is over + #Of course we could also work without a timer and compare the times every + #time we call isServicePlayable. But this might probably slow down zapping, + #That's why I decided to use a timer + self.sessionPinTimer = eTimer() + self.sessionPinTimer.callback.append(self.resetSessionPin) - def addBlacklistService(self, service): - self.blacklist.append(service) - - def setServiceLevel(self, service, level): - self.serviceLevel[service] = level - - def deleteWhitelistService(self, service): - self.whitelist.remove(service) - if self.serviceLevel.has_key(service): - self.serviceLevel.remove(service) + def serviceMethodWrapper(self, service, method, *args): + #This method is used to call all functions that need a service as Parameter: + #It takes either a Service- Reference or a Bouquet- Reference and passes + #Either the service or all services contained in the bouquet to the method given + #That way all other functions do not need to distinguish between service and bouquet. + if "FROM BOUQUET" in service: + method( service , TYPE_BOUQUET , *args ) + servicelist = self.readServicesFromBouquet(service,"C") + for ref in servicelist: + sRef = str(ref[0]) + method( sRef , TYPE_BOUQUETSERVICE , *args ) + else: + ref = ServiceReference(service) + sRef = str(ref) + method( sRef , TYPE_SERVICE , *args ) - def deleteBlacklistService(self, service): - self.blacklist.remove(service) - if self.serviceLevel.has_key(service): - self.serviceLevel.remove(service) + def setServiceLevel(self, service, type, level): + self.serviceLevel[service] = level def isServicePlayable(self, ref, callback): if not config.ParentalControl.configured.value or not config.ParentalControl.servicepinactive.value: return True - #print "whitelist:", self.whitelist - #print "blacklist:", self.blacklist - #print "config.ParentalControl.type.value:", config.ParentalControl.type.value - #print "not in whitelist:", (service not in self.whitelist) - #print "checking parental control for service:", ref.toString() + #Check if configuration has already been read or if the significant values have changed. + #If true: read the configuration + if self.configInitialized == False or self.storeServicePin != config.ParentalControl.storeservicepin.value or self.storeServicePinCancel != config.ParentalControl.storeservicepincancel.value: + self.getConfigValues() service = ref.toCompareString() - if (config.ParentalControl.type.value == "whitelist" and service not in self.whitelist) or (config.ParentalControl.type.value == "blacklist" and service in self.blacklist): + if (config.ParentalControl.type.value == LIST_WHITELIST and not self.whitelist.has_key(service)) or (config.ParentalControl.type.value == LIST_BLACKLIST and self.blacklist.has_key(service)): + #Check if the session pin is cached and return the cached value, if it is. + if self.sessionPinCached == True: + #As we can cache successful pin- entries as well as canceled pin- entries, + #We give back the last action + return self.sessionPinCachedValue self.callback = callback - #print "service:", ServiceReference(service).getServiceName() + #Someone started to implement different levels of protection. Seems they were never completed + #I did not throw out this code, although it is of no use at the moment levelNeeded = 0 if self.serviceLevel.has_key(service): levelNeeded = self.serviceLevel[service] @@ -83,103 +114,211 @@ class ParentalControl: return True def protectService(self, service): - #print "protect" - #print "config.ParentalControl.type.value:", config.ParentalControl.type.value - if config.ParentalControl.type.value == "whitelist": - if service in self.whitelist: - self.deleteWhitelistService(service) + if config.ParentalControl.type.value == LIST_WHITELIST: + if self.whitelist.has_key(service): + self.serviceMethodWrapper(service, self.removeServiceFromList, self.whitelist) + #self.deleteWhitelistService(service) else: # blacklist - if service not in self.blacklist: - self.addBlacklistService(service) + if not self.blacklist.has_key(service): + self.serviceMethodWrapper(service, self.addServiceToList, self.blacklist) + #self.addBlacklistService(service) #print "whitelist:", self.whitelist #print "blacklist:", self.blacklist - def unProtectService(self, service): #print "unprotect" #print "config.ParentalControl.type.value:", config.ParentalControl.type.value - if config.ParentalControl.type.value == "whitelist": - if service not in self.whitelist: - self.addWhitelistService(service) + if config.ParentalControl.type.value == LIST_WHITELIST: + if not self.whitelist.has_key(service): + self.serviceMethodWrapper(service, self.addServiceToList, self.whitelist) + #self.addWhitelistService(service) else: # blacklist - if service in self.blacklist: - self.deleteBlacklistService(service) + if self.blacklist.has_key(service): + self.serviceMethodWrapper(service, self.removeServiceFromList, self.blacklist) + #self.deleteBlacklistService(service) #print "whitelist:", self.whitelist #print "blacklist:", self.blacklist def getProtectionLevel(self, service): - if (config.ParentalControl.type.value == "whitelist" and service not in self.whitelist) or (config.ParentalControl.type.value == "blacklist" and service in self.blacklist): + if (config.ParentalControl.type.value == LIST_WHITELIST and not self.whitelist.has_key(service)) or (config.ParentalControl.type.value == LIST_BLACKLIST and self.blacklist.has_key(service)): if self.serviceLevel.has_key(service): return self.serviceLevel[service] else: return 0 else: return -1 + + def getProtectionType(self, service): + #New method used in ParentalControlList: This method does not only return + #if a service is protected or not, it also returns, why (whitelist or blacklist, service or bouquet) + sImage = "" + if (config.ParentalControl.type.value == LIST_WHITELIST): + if self.whitelist.has_key(service): + if TYPE_SERVICE in self.whitelist[service]: + sImage = IMG_WHITESERVICE + else: + sImage = IMG_WHITEBOUQUET + elif (config.ParentalControl.type.value == LIST_BLACKLIST): + if self.blacklist.has_key(service): + if TYPE_SERVICE in self.blacklist[service]: + sImage = IMG_BLACKSERVICE + else: + sImage = IMG_BLACKBOUQUET + bLocked = self.getProtectionLevel(service) != -1 + return (bLocked,sImage) + + def getConfigValues(self): + #Read all values from configuration + self.checkPinInterval = False + self.checkPinIntervalCancel = False + self.checkSessionPin = False + self.checkSessionPinCancel = False + + self.sessionPinCached = False + self.pinIntervalSeconds = 0 + self.pinIntervalSecondsCancel = 0 + + self.storeServicePin = config.ParentalControl.storeservicepin.value + self.storeServicePinCancel = config.ParentalControl.storeservicepincancel.value + + if self.storeServicePin == "never": + pass + elif self.storeServicePin == "standby": + self.checkSessionPin = True + else: + self.checkPinInterval = True + iMinutes = float(self.storeServicePin) + iSeconds = iMinutes*60 + self.pinIntervalSeconds = iSeconds + + if self.storeServicePinCancel == "never": + pass + elif self.storeServicePinCancel == "standby": + self.checkSessionPinCancel = True + else: + self.checkPinIntervalCancel = True + iMinutes = float(self.storeServicePinCancel) + iSeconds = iMinutes*60 + self.pinIntervalSecondsCancel = iSeconds + self.configInitialized = True + # Reset PIN cache on standby: Use StandbyCounter- Config- Callback + config.misc.standbyCounter.addNotifier(self.standbyCounterCallback, initial_call = False) + + def standbyCounterCallback(self, configElement): + self.resetSessionPin() + + def resetSessionPin(self): + #Reset the session pin, stop the timer + self.sessionPinCached = False + self.sessionPinTimer.stop() + + def getCurrentTimeStamp(self): + return time.time() + def getPinList(self): return [ x.value for x in config.ParentalControl.servicepin ] - + def servicePinEntered(self, service, result): -# levelNeeded = 0 - #if self.serviceLevel.has_key(service): - #levelNeeded = self.serviceLevel[service] -# - #print "getPinList():", self.getPinList() - #pinList = self.getPinList()[:levelNeeded + 1] - #print "pinList:", pinList -# -# print "pin entered for service", service, "and pin was", pin - #if pin is not None and int(pin) in pinList: + if result is not None and result: - #print "pin ok, playing service" + #This is the new function of caching the service pin + #save last session and time of last entered pin... + if self.checkSessionPin == True: + self.sessionPinCached = True + self.sessionPinCachedValue = True + if self.checkPinInterval == True: + self.sessionPinCached = True + self.sessionPinCachedValue = True + self.sessionPinTimer.start(self.pinIntervalSeconds*1000,1) self.callback(ref = service) else: + #This is the new function of caching cancelling of service pin if result is not None: Notifications.AddNotification(MessageBox, _("The pin code you entered is wrong."), MessageBox.TYPE_ERROR) - #print "wrong pin entered" + else: + if self.checkSessionPinCancel == True: + self.sessionPinCached = True + self.sessionPinCachedValue = False + if self.checkPinIntervalCancel == True: + self.sessionPinCached = True + self.sessionPinCachedValue = False + self.sessionPinTimer.start(self.pinIntervalSecondsCancel*1000,1) - def saveWhitelist(self): - file = open(resolveFilename(SCOPE_CONFIG, "whitelist"), 'w') - for x in self.whitelist: - file.write(x + "\n") - file.close - - def openWhitelist(self): - self.whitelist = [] - try: - file = open(resolveFilename(SCOPE_CONFIG, "whitelist"), 'r') - lines = file.readlines() - for x in lines: - ref = ServiceReference(x.strip()) - self.whitelist.append(str(ref)) - file.close - except: - pass - - def saveBlacklist(self): - file = open(resolveFilename(SCOPE_CONFIG, "blacklist"), 'w') - for x in self.blacklist: - file.write(x + "\n") + def saveListToFile(self,sWhichList): + #Replaces saveWhiteList and saveBlackList: + #I don't like to have two functions with identical code... + if sWhichList == LIST_BLACKLIST: + vList = self.blacklist + else: + vList = self.whitelist + file = open(resolveFilename(SCOPE_CONFIG, sWhichList), 'w') + for sService,sType in vList.iteritems(): + #Only Services that are selected directly and Bouqets are saved. + #Services that are added by a bouquet are not saved. + #This is the reason for the change in self.whitelist and self.blacklist + if TYPE_SERVICE in sType or TYPE_BOUQUET in sType: + file.write(str(sService) + "\n") file.close - def openBlacklist(self): - self.blacklist = [] + def openListFromFile(self,sWhichList): + #Replaces openWhiteList and openBlackList: + #I don't like to have two functions with identical code... + if sWhichList == LIST_BLACKLIST: + self.blacklist = {} + vList = self.blacklist + else: + self.whitelist = {} + vList = self.whitelist try: - file = open(resolveFilename(SCOPE_CONFIG, "blacklist"), 'r') + file = open(resolveFilename(SCOPE_CONFIG, sWhichList ), 'r') lines = file.readlines() for x in lines: - ref = ServiceReference(x.strip()) - self.blacklist.append(str(ref)) + sPlain = x.strip() + self.serviceMethodWrapper(sPlain, self.addServiceToList, vList) file.close except: pass + + def addServiceToList(self, service, type, vList): + #Replaces addWhitelistService and addBlacklistService + #The lists are not only lists of service references any more. + #They are named lists with the service as key and an array of types as value: + + if vList.has_key(service): + if not type in vList[service]: + vList[service].append(type) + else: + vList[service] = [type] + + def removeServiceFromList(self, service, type, vList): + #Replaces deleteWhitelistService and deleteBlacklistService + if vList.has_key(service): + if type in vList[service]: + vList[service].remove(type) + if not vList[service]: + del vList[service] + if self.serviceLevel.has_key(service): + self.serviceLevel.remove(service) + + def readServicesFromBouquet(self,sBouquetSelection,formatstring): + #This method gives back a list of services for a given bouquet + from enigma import eServiceCenter, eServiceReference + from Screens.ChannelSelection import service_types_tv + serviceHandler = eServiceCenter.getInstance() + refstr = sBouquetSelection + root = eServiceReference(refstr) + list = serviceHandler.list(root) + if list is not None: + services = list.getContent("CN", True) #(servicecomparestring, name) + return services def save(self): - self.saveBlacklist() - self.saveWhitelist() + # we need to open the files in case we havent's read them yet + self.saveListToFile(LIST_BLACKLIST) + self.saveListToFile(LIST_WHITELIST) def open(self): - self.openBlacklist() - self.openWhitelist() + self.openListFromFile(LIST_BLACKLIST) + self.openListFromFile(LIST_WHITELIST) -parentalControl = ParentalControl() diff --git a/lib/python/Components/ParentalControlList.py b/lib/python/Components/ParentalControlList.py index 128e6d3e..0e65257d 100644 --- a/lib/python/Components/ParentalControlList.py +++ b/lib/python/Components/ParentalControlList.py @@ -1,19 +1,28 @@ from MenuList import MenuList -from Components.ParentalControl import parentalControl +from Components.ParentalControl import IMG_WHITESERVICE, IMG_WHITEBOUQUET, IMG_BLACKSERVICE, IMG_BLACKBOUQUET from Tools.Directories import SCOPE_SKIN_IMAGE, resolveFilename from enigma import eListboxPythonMultiContent, gFont, RT_HALIGN_LEFT from Tools.LoadPixmap import LoadPixmap -lockPicture = LoadPixmap(resolveFilename(SCOPE_SKIN_IMAGE, "skin_default/icons/lock.png")) +#Now there is a list of pictures instead of one... +entryPicture = {} -def ParentalControlEntryComponent(service, name, locked = True): +entryPicture[IMG_BLACKSERVICE] = LoadPixmap(resolveFilename(SCOPE_SKIN_IMAGE, "skin_default/icons/lock.png")) +entryPicture[IMG_BLACKBOUQUET] = LoadPixmap(resolveFilename(SCOPE_SKIN_IMAGE, "skin_default/icons/lockBouquet.png")) +entryPicture[IMG_WHITESERVICE] = LoadPixmap(resolveFilename(SCOPE_SKIN_IMAGE, "skin_default/icons/unlock.png")) +entryPicture[IMG_WHITEBOUQUET] = LoadPixmap(resolveFilename(SCOPE_SKIN_IMAGE, "skin_default/icons/unlockBouquet.png")) + +def ParentalControlEntryComponent(service, name, protectionType): + locked = protectionType[0] + sImage = protectionType[1] res = [ (service, name, locked), (eListboxPythonMultiContent.TYPE_TEXT, 80, 5, 300, 50, 0, RT_HALIGN_LEFT, name) ] - if locked: - res.append((eListboxPythonMultiContent.TYPE_PIXMAP_ALPHATEST, 0, 0, 32, 32, lockPicture)) + #Changed logic: The image is defined by sImage, not by locked anymore + if sImage != "": + res.append((eListboxPythonMultiContent.TYPE_PIXMAP_ALPHATEST, 0, 0, 32, 32, entryPicture[sImage])) return res class ParentalControlList(MenuList): @@ -23,11 +32,14 @@ class ParentalControlList(MenuList): self.l.setItemHeight(32) def toggleSelectedLock(self): + from Components.ParentalControl import parentalControl print "self.l.getCurrentSelection():", self.l.getCurrentSelection() print "self.l.getCurrentSelectionIndex():", self.l.getCurrentSelectionIndex() - self.list[self.l.getCurrentSelectionIndex()] = ParentalControlEntryComponent(self.l.getCurrentSelection()[0][0], self.l.getCurrentSelection()[0][1], not self.l.getCurrentSelection()[0][2]); - if self.l.getCurrentSelection()[0][2]: - parentalControl.protectService(self.l.getCurrentSelection()[0][0]) + curSel = self.l.getCurrentSelection() + if curSel[0][2]: + parentalControl.unProtectService(self.l.getCurrentSelection()[0][0]) else: - parentalControl.unProtectService(self.l.getCurrentSelection()[0][0]) + parentalControl.protectService(self.l.getCurrentSelection()[0][0]) + #Instead of just negating the locked- flag, now I call the getProtectionType every time... + self.list[self.l.getCurrentSelectionIndex()] = ParentalControlEntryComponent(curSel[0][0], curSel[0][1], parentalControl.getProtectionType(curSel[0][0])) self.l.setList(self.list) diff --git a/lib/python/Components/PluginComponent.py b/lib/python/Components/PluginComponent.py index 5e439fdf..b06246b2 100755 --- a/lib/python/Components/PluginComponent.py +++ b/lib/python/Components/PluginComponent.py @@ -8,9 +8,13 @@ from Plugins.Plugin import PluginDescriptor import keymapparser class PluginComponent: + firstRun = True + restartRequired = False + def __init__(self): self.plugins = {} self.pluginList = [ ] + self.installedPluginList = [ ] self.setPluginPrefix("Plugins.") self.resetWarnings() @@ -18,12 +22,15 @@ class PluginComponent: self.prefix = prefix def addPlugin(self, plugin): - self.pluginList.append(plugin) - for x in plugin.where: - self.plugins.setdefault(x, []).append(plugin) - if x == PluginDescriptor.WHERE_AUTOSTART: - plugin(reason=0) - + if self.firstRun or plugin.needsRestart is False: + self.pluginList.append(plugin) + for x in plugin.where: + self.plugins.setdefault(x, []).append(plugin) + if x == PluginDescriptor.WHERE_AUTOSTART: + plugin(reason=0) + else: + self.restartRequired = True + def removePlugin(self, plugin): self.pluginList.remove(plugin) for x in plugin.where: @@ -42,7 +49,6 @@ class PluginComponent: directory_category = directory + c if not os_path.isdir(directory_category): continue - open(directory_category + "/__init__.py", "a").close() for pluginname in os_listdir(directory_category): path = directory_category + "/" + pluginname if os_path.isdir(path): @@ -67,6 +73,7 @@ class PluginComponent: plugins = [ plugins ] for p in plugins: + p.path = path p.updateIcon(path) new_plugins.append(p) @@ -81,12 +88,29 @@ class PluginComponent: # internally, the "fnc" argument will be compared with __eq__ plugins_added = [p for p in new_plugins if p not in self.pluginList] plugins_removed = [p for p in self.pluginList if not p.internal and p not in new_plugins] + + #ignore already installed but reloaded plugins + for p in plugins_removed: + for pa in plugins_added: + if pa.path == p.path and pa.where == p.where: + pa.needsRestart = False for p in plugins_removed: self.removePlugin(p) for p in plugins_added: - self.addPlugin(p) + if self.firstRun or p.needsRestart is False: + self.addPlugin(p) + else: + for installed_plugin in self.installedPluginList: + if installed_plugin.path == p.path: + if installed_plugin.where == p.where: + p.needsRestart = False + self.addPlugin(p) + + if self.firstRun: + self.firstRun = False + self.installedPluginList = self.pluginList def getPlugins(self, where): """Get list of plugins in a specific category""" @@ -97,8 +121,8 @@ class PluginComponent: for x in where: res.extend(self.plugins.get(x, [ ])) - - return res + res.sort(key=lambda x:x.weight) + return res def getPluginsForMenu(self, menuid): res = [ ] diff --git a/lib/python/Components/RecordingConfig.py b/lib/python/Components/RecordingConfig.py index fe9284d9..40dfb2ca 100644..100755 --- a/lib/python/Components/RecordingConfig.py +++ b/lib/python/Components/RecordingConfig.py @@ -1,4 +1,4 @@ -from config import ConfigNumber, ConfigYesNo, ConfigSubsection, config +from config import ConfigNumber, ConfigYesNo, ConfigSubsection, ConfigSelection, config def InitRecordingConfig(): config.recording = ConfigSubsection(); @@ -8,3 +8,7 @@ def InitRecordingConfig(): config.recording.margin_after = ConfigNumber(default=0) config.recording.debug = ConfigYesNo(default = False) config.recording.ascii_filenames = ConfigYesNo(default = False) + config.recording.filename_composition = ConfigSelection(default = "standard", choices = [ + ("standard", _("standard")), + ("short", _("Short filenames")), + ("long", _("Long filenames")) ] )
\ No newline at end of file diff --git a/lib/python/Components/Renderer/Listbox.py b/lib/python/Components/Renderer/Listbox.py index 7a895330..716fe445 100644..100755 --- a/lib/python/Components/Renderer/Listbox.py +++ b/lib/python/Components/Renderer/Listbox.py @@ -19,6 +19,7 @@ class Listbox(Renderer, object): self.__content = None self.__wrap_around = False self.__selection_enabled = True + self.__scrollbarMode = "showOnDemand" GUI_WIDGET = eListbox @@ -38,6 +39,7 @@ class Listbox(Renderer, object): instance.selectionChanged.get().append(self.selectionChanged) self.wrap_around = self.wrap_around # trigger self.selection_enabled = self.selection_enabled # trigger + self.scrollbarMode = self.scrollbarMode # trigger def preWidgetRemove(self, instance): instance.setContent(None) @@ -76,7 +78,24 @@ class Listbox(Renderer, object): selection_enabled = property(lambda self: self.__selection_enabled, setSelectionEnabled) + def setScrollbarMode(self, mode): + self.__scrollbarMode = mode + if self.instance is not None: + self.instance.setScrollbarMode(int( + { "showOnDemand": 0, + "showAlways": 1, + "showNever": 2, + }[mode])) + + scrollbarMode = property(lambda self: self.__scrollbarMode, setScrollbarMode) + def changed(self, what): + if hasattr(self.source, "selectionEnabled"): + self.selection_enabled = self.source.selectionEnabled + if hasattr(self.source, "scrollbarMode"): + self.scrollbarMode = self.source.scrollbarMode + if len(what) > 1 and isinstance(what[1], str) and what[1] == "style": + return self.content = self.source.content def entry_changed(self, index): diff --git a/lib/python/Components/Renderer/Picon.py b/lib/python/Components/Renderer/Picon.py index 5ae43ed8..51dc09a5 100644 --- a/lib/python/Components/Renderer/Picon.py +++ b/lib/python/Components/Renderer/Picon.py @@ -2,11 +2,11 @@ ## Picon renderer by Gruffy .. some speedups by Ghost ## from Renderer import Renderer -from enigma import ePixmap +from enigma import ePixmap, eEnv from Tools.Directories import fileExists, SCOPE_SKIN_IMAGE, SCOPE_CURRENT_SKIN, resolveFilename class Picon(Renderer): - searchPaths = ('/usr/share/enigma2/%s/', + searchPaths = (eEnv.resolve('${datadir}/enigma2/%s/'), '/media/cf/%s/', '/media/usb/%s/') diff --git a/lib/python/Components/Renderer/Pixmap.py b/lib/python/Components/Renderer/Pixmap.py index d67cd559..7c6b5795 100644 --- a/lib/python/Components/Renderer/Pixmap.py +++ b/lib/python/Components/Renderer/Pixmap.py @@ -3,4 +3,17 @@ from Renderer import Renderer from enigma import ePixmap class Pixmap(Renderer): + def __init__(self): + Renderer.__init__(self) + GUI_WIDGET = ePixmap + + def postWidgetCreate(self, instance): + self.changed((self.CHANGED_DEFAULT,)) + + def changed(self, what): + if what[0] != self.CHANGED_CLEAR: + if self.source and hasattr(self.source, "pixmap"): + if self.instance: + self.instance.setPixmap(self.source.pixmap) + diff --git a/lib/python/Components/Scanner.py b/lib/python/Components/Scanner.py index 813c09f8..e01c61fd 100644 --- a/lib/python/Components/Scanner.py +++ b/lib/python/Components/Scanner.py @@ -11,6 +11,8 @@ add_type("application/x-dream-package", ".dmpkg") add_type("application/x-dream-image", ".nfi") add_type("video/MP2T", ".ts") add_type("video/x-dvd-iso", ".iso") +add_type("video/x-matroska", ".mkv") +add_type("audio/x-matroska", ".mka") def getType(file): (type, _) = guess_type(file) diff --git a/lib/python/Components/ServiceList.py b/lib/python/Components/ServiceList.py index 6095812a..cd055a82 100755..100644 --- a/lib/python/Components/ServiceList.py +++ b/lib/python/Components/ServiceList.py @@ -7,6 +7,8 @@ from Tools.LoadPixmap import LoadPixmap from Tools.Directories import resolveFilename, SCOPE_CURRENT_SKIN +from Components.config import config + class ServiceList(HTMLComponent, GUIComponent): MODE_NORMAL = 0 MODE_FAVOURITES = 1 @@ -62,6 +64,22 @@ class ServiceList(HTMLComponent, GUIComponent): self.l.setColor(eListboxServiceContent.markedBackgroundSelected, parseColor(value)) elif attrib == "foregroundColorServiceNotAvail": self.l.setColor(eListboxServiceContent.serviceNotAvail, parseColor(value)) + elif attrib == "colorEventProgressbar": + self.l.setColor(eListboxServiceContent.serviceEventProgressbarColor, parseColor(value)) + elif attrib == "colorEventProgressbarSelected": + self.l.setColor(eListboxServiceContent.serviceEventProgressbarColorSelected, parseColor(value)) + elif attrib == "colorEventProgressbarBorder": + self.l.setColor(eListboxServiceContent.serviceEventProgressbarBorderColor, parseColor(value)) + elif attrib == "colorEventProgressbarBorderSelected": + self.l.setColor(eListboxServiceContent.serviceEventProgressbarBorderColorSelected, parseColor(value)) + elif attrib == "colorServiceDescription": + self.l.setColor(eListboxServiceContent.serviceDescriptionColor, parseColor(value)) + elif attrib == "colorServiceDescriptionSelected": + self.l.setColor(eListboxServiceContent.serviceDescriptionColorSelected, parseColor(value)) + elif attrib == "picServiceEventProgressbar": + pic = LoadPixmap(resolveFilename(SCOPE_CURRENT_SKIN, value)) + if pic: + self.l.setPixmap(self.l.picServiceEventProgressbar, pic) elif attrib == "serviceItemHeight": self.ItemHeight = int(value) elif attrib == "serviceNameFont": @@ -213,17 +231,24 @@ class ServiceList(HTMLComponent, GUIComponent): def setMode(self, mode): self.mode = mode + self.l.setItemHeight(self.ItemHeight) + self.l.setVisualMode(eListboxServiceContent.visModeComplex) if mode == self.MODE_NORMAL: - self.l.setItemHeight(self.ItemHeight) - self.l.setVisualMode(eListboxServiceContent.visModeComplex) + if config.usage.show_event_progress_in_servicelist.value: + self.l.setElementPosition(self.l.celServiceEventProgressbar, eRect(0, 0, 52, self.ItemHeight)) + else: + self.l.setElementPosition(self.l.celServiceEventProgressbar, eRect(0, 0, 0, 0)) self.l.setElementFont(self.l.celServiceName, self.ServiceNameFont) self.l.setElementPosition(self.l.celServiceName, eRect(0, 0, self.instance.size().width(), self.ItemHeight)) self.l.setElementFont(self.l.celServiceInfo, self.ServiceInfoFont) else: - self.l.setItemHeight(self.ItemHeight) - self.l.setVisualMode(eListboxServiceContent.visModeComplex) + if config.usage.show_event_progress_in_servicelist.value: + self.l.setElementPosition(self.l.celServiceEventProgressbar, eRect(60, 0, 52, self.ItemHeight)) + else: + self.l.setElementPosition(self.l.celServiceEventProgressbar, eRect(60, 0, 0, 0)) self.l.setElementFont(self.l.celServiceNumber, self.ServiceNumberFont) self.l.setElementPosition(self.l.celServiceNumber, eRect(0, 0, 50, self.ItemHeight)) self.l.setElementFont(self.l.celServiceName, self.ServiceNameFont) self.l.setElementPosition(self.l.celServiceName, eRect(60, 0, self.instance.size().width()-60, self.ItemHeight)) self.l.setElementFont(self.l.celServiceInfo, self.ServiceInfoFont) + diff --git a/lib/python/Components/Sources/List.py b/lib/python/Components/Sources/List.py index 1eab32b2..6f0670a1 100644 --- a/lib/python/Components/Sources/List.py +++ b/lib/python/Components/Sources/List.py @@ -91,8 +91,9 @@ to generate HTML.""" return self.__style def setStyle(self, style): - self.__style = style - self.changed((self.CHANGED_SPECIFIC, "style")) + if self.__style != style: + self.__style = style + self.changed((self.CHANGED_SPECIFIC, "style")) style = property(getStyle, setStyle) diff --git a/lib/python/Components/Sources/ServiceEvent.py b/lib/python/Components/Sources/ServiceEvent.py index 93c733bd..8a0a66a1 100644 --- a/lib/python/Components/Sources/ServiceEvent.py +++ b/lib/python/Components/Sources/ServiceEvent.py @@ -25,7 +25,7 @@ class ServiceEvent(Source, object): def newService(self, ref): if not self.service or not ref or self.service != ref: self.service = ref - if not ref or (ref.flags & Ref.flagDirectory) == Ref.flagDirectory or ref.flags & Ref.isMarker: + if not ref: self.changed((self.CHANGED_CLEAR,)) else: self.changed((self.CHANGED_ALL,)) diff --git a/lib/python/Components/Task.py b/lib/python/Components/Task.py index df94f8a6..3a755405 100644 --- a/lib/python/Components/Task.py +++ b/lib/python/Components/Task.py @@ -16,6 +16,7 @@ class Job(object): self.end = 100 self.__progress = 0 self.weightScale = 1 + self.afterEvent = None self.state_changed = CList() @@ -63,11 +64,10 @@ class Job(object): def runNext(self): if self.current_task == len(self.tasks): if len(self.resident_tasks) == 0: - cb = self.callback - self.callback = None self.status = self.FINISHED self.state_changed() - cb(self, None, []) + self.callback(self, None, []) + self.callback = None else: print "still waiting for %d resident task(s) %s to finish" % (len(self.resident_tasks), str(self.resident_tasks)) else: @@ -370,12 +370,20 @@ class DiskspacePrecondition(Condition): class ToolExistsPrecondition(Condition): def check(self, task): import os + if task.cmd[0]=='/': - realpath = task.cmd + self.realpath = task.cmd + print "[Task.py][ToolExistsPrecondition] WARNING: usage of absolute paths for tasks should be avoided!" + return os.access(self.realpath, os.X_OK) else: - realpath = task.cwd + '/' + task.cmd - self.realpath = realpath - return os.access(realpath, os.X_OK) + self.realpath = task.cmd + path = os.environ.get('PATH', '').split(os.pathsep) + path.append(task.cwd + '/') + absolutes = filter(lambda file: os.access(file, os.X_OK), map(lambda directory, file = task.cmd: os.path.join(directory, file), path)) + if len(absolutes) > 0: + self.realpath = task.cmd[0] + return True + return False def getErrorMessage(self, task): return _("A required tool (%s) was not found.") % (self.realpath) diff --git a/lib/python/Components/TimerList.py b/lib/python/Components/TimerList.py index 2a7405df..30097c96 100755 --- a/lib/python/Components/TimerList.py +++ b/lib/python/Components/TimerList.py @@ -33,12 +33,18 @@ class TimerList(HTMLComponent, GUIComponent, object): count += 1 flags = flags >> 1 if timer.justplay: - res.append((eListboxPythonMultiContent.TYPE_TEXT, 0, 50, width-150, 20, 1, RT_HALIGN_LEFT|RT_VALIGN_CENTER, repeatedtext + ((" %s "+ _("(ZAP)")) % (FuzzyTime(timer.begin)[1])))) + if timer.end - timer.begin < 4: # rounding differences + res.append((eListboxPythonMultiContent.TYPE_TEXT, 0, 50, width-150, 20, 1, RT_HALIGN_LEFT|RT_VALIGN_CENTER, repeatedtext + ((" %s "+ _("(ZAP)")) % (FuzzyTime(timer.begin)[1])))) + else: + res.append((eListboxPythonMultiContent.TYPE_TEXT, 0, 50, width-150, 20, 1, RT_HALIGN_LEFT|RT_VALIGN_CENTER, repeatedtext + ((" %s ... %s (%d " + _("mins") + ") ") % (FuzzyTime(timer.begin)[1], FuzzyTime(timer.end)[1], (timer.end - timer.begin) / 60)) + _("(ZAP)"))) else: res.append((eListboxPythonMultiContent.TYPE_TEXT, 0, 50, width-150, 20, 1, RT_HALIGN_LEFT|RT_VALIGN_CENTER, repeatedtext + ((" %s ... %s (%d " + _("mins") + ")") % (FuzzyTime(timer.begin)[1], FuzzyTime(timer.end)[1], (timer.end - timer.begin) / 60)))) else: if timer.justplay: - res.append((eListboxPythonMultiContent.TYPE_TEXT, 0, 50, width-150, 20, 1, RT_HALIGN_LEFT|RT_VALIGN_CENTER, repeatedtext + (("%s, %s " + _("(ZAP)")) % (FuzzyTime(timer.begin))))) + if timer.end - timer.begin < 4: + res.append((eListboxPythonMultiContent.TYPE_TEXT, 0, 50, width-150, 20, 1, RT_HALIGN_LEFT|RT_VALIGN_CENTER, repeatedtext + (("%s, %s " + _("(ZAP)")) % (FuzzyTime(timer.begin))))) + else: + res.append((eListboxPythonMultiContent.TYPE_TEXT, 0, 50, width-150, 20, 1, RT_HALIGN_LEFT|RT_VALIGN_CENTER, repeatedtext + (("%s, %s ... %s (%d " + _("mins") + ") ") % (FuzzyTime(timer.begin) + FuzzyTime(timer.end)[1:] + ((timer.end - timer.begin) / 60,))) + _("(ZAP)"))) else: res.append((eListboxPythonMultiContent.TYPE_TEXT, 0, 50, width-150, 20, 1, RT_HALIGN_LEFT|RT_VALIGN_CENTER, repeatedtext + (("%s, %s ... %s (%d " + _("mins") + ")") % (FuzzyTime(timer.begin) + FuzzyTime(timer.end)[1:] + ((timer.end - timer.begin) / 60,))))) diff --git a/lib/python/Components/TimerSanityCheck.py b/lib/python/Components/TimerSanityCheck.py index 8f48d1ec..b9dda6a6 100644 --- a/lib/python/Components/TimerSanityCheck.py +++ b/lib/python/Components/TimerSanityCheck.py @@ -2,6 +2,7 @@ import NavigationInstance from time import localtime, mktime, gmtime from ServiceReference import ServiceReference from enigma import iServiceInformation, eServiceCenter, eServiceReference +from timer import TimerEntry class TimerSanityCheck: def __init__(self, timerlist, newtimer=None): @@ -12,8 +13,8 @@ class TimerSanityCheck: self.simultimer = [] self.rep_eventlist = [] self.nrep_eventlist = [] - self.bflag = 1 - self.eflag = -1 + self.bflag = -1 + self.eflag = 1 def check(self, ext_timer=1): print "check" @@ -107,7 +108,7 @@ class TimerSanityCheck: self.rep_eventlist.append((begin, idx)) begin += 86400 rflags >>= 1 - else: + elif timer.state < TimerEntry.StateEnded: self.nrep_eventlist.extend([(timer.begin,self.bflag,idx),(timer.end,self.eflag,idx)]) idx += 1 diff --git a/lib/python/Components/TuneTest.py b/lib/python/Components/TuneTest.py index f9ab3edb..44b19091 100644 --- a/lib/python/Components/TuneTest.py +++ b/lib/python/Components/TuneTest.py @@ -1,8 +1,9 @@ from enigma import eDVBFrontendParametersSatellite, eDVBFrontendParameters, eDVBResourceManager, eTimer class Tuner: - def __init__(self, frontend): + def __init__(self, frontend, ignore_rotor=False): self.frontend = frontend + self.ignore_rotor = ignore_rotor # transponder = (frequency, symbolrate, polarisation, fec, inversion, orbpos, system, modulation, rolloff, pilot, tsid, onid) # 0 1 2 3 4 5 6 7 8 9 10 11 @@ -21,7 +22,7 @@ class Tuner: parm.rolloff = transponder[8] parm.pilot = transponder[9] feparm = eDVBFrontendParameters() - feparm.setDVBS(parm) + feparm.setDVBS(parm, self.ignore_rotor) self.lastparm = feparm self.frontend.tune(feparm) diff --git a/lib/python/Components/UsageConfig.py b/lib/python/Components/UsageConfig.py index 90f11fe2..ce56f0f6 100644 --- a/lib/python/Components/UsageConfig.py +++ b/lib/python/Components/UsageConfig.py @@ -1,7 +1,7 @@ from Components.Harddisk import harddiskmanager from config import ConfigSubsection, ConfigYesNo, config, ConfigSelection, ConfigText, ConfigNumber, ConfigSet, ConfigLocations from Tools.Directories import resolveFilename, SCOPE_HDD -from enigma import Misc_Options, setTunerTypePriorityOrder; +from enigma import Misc_Options, setTunerTypePriorityOrder, eEnv; from SystemInfo import SystemInfo import os @@ -53,7 +53,14 @@ def InitUsageConfig(): config.usage.on_long_powerpress = ConfigSelection(default = "show_menu", choices = [ ("show_menu", _("show shutdown menu")), - ("shutdown", _("immediate shutdown")) ] ) + ("shutdown", _("immediate shutdown")), + ("standby", _("Standby")) ] ) + + config.usage.on_short_powerpress = ConfigSelection(default = "standby", choices = [ + ("show_menu", _("show shutdown menu")), + ("shutdown", _("immediate shutdown")), + ("standby", _("Standby")) ] ) + config.usage.alternatives_priority = ConfigSelection(default = "0", choices = [ ("0", "DVB-S/-C/-T"), @@ -63,6 +70,8 @@ def InitUsageConfig(): ("4", "DVB-T/-C/-S"), ("5", "DVB-T/-S/-C") ]) + config.usage.show_event_progress_in_servicelist = ConfigYesNo(default = False) + config.usage.blinking_display_clock_during_recording = ConfigYesNo(default = False) config.usage.show_message_when_recording_starts = ConfigYesNo(default = True) @@ -87,7 +96,7 @@ def InitUsageConfig(): SystemInfo["12V_Output"] = Misc_Options.getInstance().detected_12V_output() - config.usage.keymap = ConfigText(default = "/usr/share/enigma2/keymap.xml") + config.usage.keymap = ConfigText(default = eEnv.resolve("${datadir}/enigma2/keymap.xml")) config.seek = ConfigSubsection() config.seek.selfdefined_13 = ConfigNumber(default=15) @@ -95,19 +104,19 @@ def InitUsageConfig(): config.seek.selfdefined_79 = ConfigNumber(default=300) config.seek.speeds_forward = ConfigSet(default=[2, 4, 8, 16, 32, 64, 128], choices=[2, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128]) - config.seek.speeds_backward = ConfigSet(default=[8, 16, 32, 64, 128], choices=[1, 2, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128]) + config.seek.speeds_backward = ConfigSet(default=[2, 4, 8, 16, 32, 64, 128], choices=[1, 2, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128]) config.seek.speeds_slowmotion = ConfigSet(default=[2, 4, 8], choices=[2, 4, 6, 8, 12, 16, 25]) config.seek.enter_forward = ConfigSelection(default = "2", choices = ["2", "4", "6", "8", "12", "16", "24", "32", "48", "64", "96", "128"]) config.seek.enter_backward = ConfigSelection(default = "1", choices = ["1", "2", "4", "6", "8", "12", "16", "24", "32", "48", "64", "96", "128"]) - config.seek.stepwise_minspeed = ConfigSelection(default = "16", choices = ["Never", "2", "4", "6", "8", "12", "16", "24", "32", "48", "64", "96", "128"]) - config.seek.stepwise_repeat = ConfigSelection(default = "3", choices = ["2", "3", "4", "5", "6"]) config.seek.on_pause = ConfigSelection(default = "play", choices = [ ("play", _("Play")), ("step", _("Singlestep (GOP)")), ("last", _("Last speed")) ]) + config.usage.timerlist_finished_timer_position = ConfigSelection(default = "beginning", choices = [("beginning", _("at beginning")), ("end", _("at end"))]) + def updateEnterForward(configElement): if not configElement.value: configElement.value = [2] diff --git a/lib/python/Components/VolumeControl.py b/lib/python/Components/VolumeControl.py index 19fb90d7..38102926 100644 --- a/lib/python/Components/VolumeControl.py +++ b/lib/python/Components/VolumeControl.py @@ -8,6 +8,7 @@ from config import config, ConfigSubsection, ConfigInteger profile("VolumeControl") #TODO .. move this to a own .py file class VolumeControl: + instance = None """Volume control, handles volUp, volDown, volMute actions and display a corresponding dialog""" def __init__(self, session): @@ -16,6 +17,9 @@ class VolumeControl: globalActionMap.actions["volumeDown"]=self.volDown globalActionMap.actions["volumeMute"]=self.volMute + assert not VolumeControl.instance, "only one VolumeControl instance is allowed!" + VolumeControl.instance = self + config.audio = ConfigSubsection() config.audio.volume = ConfigInteger(default = 100, limits = (0, 100)) diff --git a/lib/python/Components/config.py b/lib/python/Components/config.py index 471b59ec..5507cae9 100755 --- a/lib/python/Components/config.py +++ b/lib/python/Components/config.py @@ -29,6 +29,7 @@ from time import localtime, strftime class ConfigElement(object): def __init__(self): self.saved_value = None + self.save_forced = False self.last_value = None self.save_disabled = False self.__notifiers = None @@ -83,7 +84,7 @@ class ConfigElement(object): # you need to override this if str(self.value) doesn't work def save(self): - if self.save_disabled or self.value == self.default: + if self.save_disabled or (self.value == self.default and not self.save_forced): self.saved_value = None else: self.saved_value = self.tostring(self.value) @@ -1623,16 +1624,17 @@ class Config(ConfigSubsection): self.pickle_this("config", self.saved_value, result) return ''.join(result) - def unpickle(self, lines): + def unpickle(self, lines, base_file=True): tree = { } for l in lines: if not l or l[0] == '#': continue n = l.find('=') + name = l[:n] val = l[n+1:].strip() - names = l[:n].split('.') + names = name.split('.') # if val.find(' ') != -1: # val = val[:val.find(' ')] @@ -1643,6 +1645,15 @@ class Config(ConfigSubsection): base[names[-1]] = val + if not base_file: # not the initial config file.. + #update config.x.y.value when exist + try: + configEntry = eval(name) + if configEntry is not None: + configEntry.value = val + except (SyntaxError, KeyError): + pass + # we inherit from ConfigSubsection, so ... #object.__setattr__(self, "saved_value", tree["config"]) if "config" in tree: @@ -1650,13 +1661,16 @@ class Config(ConfigSubsection): def saveToFile(self, filename): text = self.pickle() - f = open(filename, "w") - f.write(text) - f.close() + try: + f = open(filename, "w") + f.write(text) + f.close() + except IOError: + print "Config: Couldn't write %s" % filename - def loadFromFile(self, filename): + def loadFromFile(self, filename, base_file=False): f = open(filename, "r") - self.unpickle(f.readlines()) + self.unpickle(f.readlines(), base_file) f.close() config = Config() @@ -1667,7 +1681,7 @@ class ConfigFile: def load(self): try: - config.loadFromFile(self.CONFIG_FILE) + config.loadFromFile(self.CONFIG_FILE, True) except IOError, e: print "unable to load config (%s), assuming defaults..." % str(e) diff --git a/lib/python/Makefile.am b/lib/python/Makefile.am index e38f7d82..666ba67d 100644 --- a/lib/python/Makefile.am +++ b/lib/python/Makefile.am @@ -1,23 +1,45 @@ -INCLUDES = \ +AM_CPPFLAGS = \ + -I$(top_srcdir) \ -I$(top_srcdir)/include \ - -I$(top_srcdir)/src + -include Python.h \ + -include $(top_builddir)/enigma2_config.h SUBDIRS = Components Tools Screens Plugins noinst_LIBRARIES = libenigma_python.a libenigma_python_a_SOURCES = \ - python.cpp enigma_python_wrap.cxx connections.cpp + connections.cpp \ + python.cpp -enigma_python_wrap.cxx: enigma_python.i - swig -I$(top_srcdir)/ -c++ -python -O -nortti -nothreads enigma_python.i - python enigma_py_patcher.py +pythonincludedir = $(pkgincludedir)/lib/python +pythoninclude_HEADERS = \ + connections.h \ + python.h \ + swig.h -enigma.py: enigma_python_wrap.cxx +nodist_libenigma_python_a_SOURCES = \ + enigma_python.cpp -CLEANFILES = enigma.py enigma_python_wrap.cxx +noinst_PYTHON = \ + enigma_py_patcher.py + +EXTRA_DIST = \ + enigma_python.i + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/enigma_python.Pcpp@am__quote@ + +.i.cpp: + $(AM_V_GEN)$(SWIG) $(AX_SWIG_PYTHON_OPT) -MT $@ -MD -MF $(DEPDIR)/$*.Tpo -I$(top_srcdir) -O -nortti -nothreads -o $@ $< + $(AM_V_at)mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Pcpp + $(AM_V_at)$(PYTHON) $(srcdir)/enigma_py_patcher.py + +enigma.py: enigma_python.cpp + +CLEANFILES = enigma.py enigma_python.cpp installdir = $(pkglibdir)/python install_PYTHON = \ enigma.py + diff --git a/lib/python/Plugins/DemoPlugins/Makefile.am b/lib/python/Plugins/DemoPlugins/Makefile.am index aace17cc..9e16bfc1 100755 --- a/lib/python/Plugins/DemoPlugins/Makefile.am +++ b/lib/python/Plugins/DemoPlugins/Makefile.am @@ -1,6 +1,8 @@ installdir = $(pkglibdir)/python/Plugins/DemoPlugins -SUBDIRS = TestPlugin +SUBDIRS = TestPlugin TPMDemo install_PYTHON = \ - __init__.py
\ No newline at end of file + __init__.py + +
\ No newline at end of file diff --git a/lib/python/Plugins/DemoPlugins/TPMDemo/Makefile.am b/lib/python/Plugins/DemoPlugins/TPMDemo/Makefile.am new file mode 100644 index 00000000..4828081f --- /dev/null +++ b/lib/python/Plugins/DemoPlugins/TPMDemo/Makefile.am @@ -0,0 +1,5 @@ +installdir = $(pkglibdir)/python/Plugins/DemoPlugins/TPMDemo + +install_PYTHON = \ + __init__.py \ + plugin.py diff --git a/lib/python/Plugins/DemoPlugins/TPMDemo/README b/lib/python/Plugins/DemoPlugins/TPMDemo/README new file mode 100644 index 00000000..89a972aa --- /dev/null +++ b/lib/python/Plugins/DemoPlugins/TPMDemo/README @@ -0,0 +1 @@ +Please read enigma2/doc/TPM for further instructions on how to integrate this into your own plugins.
\ No newline at end of file diff --git a/lib/python/Plugins/DemoPlugins/TPMDemo/__init__.py b/lib/python/Plugins/DemoPlugins/TPMDemo/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/lib/python/Plugins/DemoPlugins/TPMDemo/__init__.py diff --git a/lib/python/Plugins/DemoPlugins/TPMDemo/plugin.py b/lib/python/Plugins/DemoPlugins/TPMDemo/plugin.py new file mode 100644 index 00000000..dcaa1f65 --- /dev/null +++ b/lib/python/Plugins/DemoPlugins/TPMDemo/plugin.py @@ -0,0 +1,87 @@ +from Screens.Screen import Screen +from Plugins.Plugin import PluginDescriptor +from enigma import eTPM +import sha + +def bin2long(s): + return reduce( lambda x,y:(x<<8L)+y, map(ord, s)) + +def long2bin(l): + res = "" + for byte in range(128): + res += chr((l >> (1024 - (byte + 1) * 8)) & 0xff) + return res + +def rsa_pub1024(src, mod): + return long2bin(pow(bin2long(src), 65537, bin2long(mod))) + +def decrypt_block(src, mod): + if len(src) != 128 and len(src) != 202: + return None + dest = rsa_pub1024(src[:128], mod) + hash = sha.new(dest[1:107]) + if len(src) == 202: + hash.update(src[131:192]) + result = hash.digest() + if result == dest[107:127]: + return dest + return None + +def validate_cert(cert, key): + buf = decrypt_block(cert[8:], key) + if buf is None: + return None + return buf[36:107] + cert[139:196] + +def read_random(): + try: + fd = open("/dev/urandom", "r") + buf = fd.read(8) + fd.close() + return buf + except: + return None + +def main(session, **kwargs): + try: + device = open("/proc/stb/info/model", "r").readline().strip() + except: + device = "" + if device != "dm7025": + rootkey = ['\x9f', '|', '\xe4', 'G', '\xc9', '\xb4', '\xf4', '#', '&', '\xce', '\xb3', '\xfe', '\xda', '\xc9', 'U', '`', '\xd8', '\x8c', 's', 'o', '\x90', '\x9b', '\\', 'b', '\xc0', '\x89', '\xd1', '\x8c', '\x9e', 'J', 'T', '\xc5', 'X', '\xa1', '\xb8', '\x13', '5', 'E', '\x02', '\xc9', '\xb2', '\xe6', 't', '\x89', '\xde', '\xcd', '\x9d', '\x11', '\xdd', '\xc7', '\xf4', '\xe4', '\xe4', '\xbc', '\xdb', '\x9c', '\xea', '}', '\xad', '\xda', 't', 'r', '\x9b', '\xdc', '\xbc', '\x18', '3', '\xe7', '\xaf', '|', '\xae', '\x0c', '\xe3', '\xb5', '\x84', '\x8d', '\r', '\x8d', '\x9d', '2', '\xd0', '\xce', '\xd5', 'q', '\t', '\x84', 'c', '\xa8', ')', '\x99', '\xdc', '<', '"', 'x', '\xe8', '\x87', '\x8f', '\x02', ';', 'S', 'm', '\xd5', '\xf0', '\xa3', '_', '\xb7', 'T', '\t', '\xde', '\xa7', '\xf1', '\xc9', '\xae', '\x8a', '\xd7', '\xd2', '\xcf', '\xb2', '.', '\x13', '\xfb', '\xac', 'j', '\xdf', '\xb1', '\x1d', ':', '?'] + + etpm = eTPM() + l2cert = etpm.getCert(eTPM.TPMD_DT_LEVEL2_CERT) + if l2cert is None: + print "l2cert not found" + return + + l2key = validate_cert(l2cert, rootkey) + if l2key is None: + print "l2cert invalid" + return + + l3cert = etpm.getCert(eTPM.TPMD_DT_LEVEL3_CERT) + if l3cert is None: + print "l3cert not found (can be fixed by running the genuine dreambox plugin and running the offered update)" + return + + l3key = validate_cert(l3cert, l2key) + if l3key is None: + print "l3cert invalid" + return + + rnd = read_random() + if rnd is None: + print "random error" + return + val = etpm.challenge(rnd) + result = decrypt_block(val, l3key) + if device == "dm7025" or result[80:88] == rnd: + print "successfully finished the tpm test" + # would start your plugin here + +def Plugins(**kwargs): + return [PluginDescriptor(name = "TPM Demo", description = _("A demo plugin for TPM usage."), where = PluginDescriptor.WHERE_EXTENSIONSMENU, needsRestart = False, fnc = main), + PluginDescriptor(name = "TPM Demo", description = _("A demo plugin for TPM usage."), icon = "plugin.png", where = PluginDescriptor.WHERE_PLUGINMENU, needsRestart = False, fnc = main)] +
\ No newline at end of file diff --git a/lib/python/Plugins/DemoPlugins/TestPlugin/plugin.py b/lib/python/Plugins/DemoPlugins/TestPlugin/plugin.py index 69f935e4..4ef4a87d 100644 --- a/lib/python/Plugins/DemoPlugins/TestPlugin/plugin.py +++ b/lib/python/Plugins/DemoPlugins/TestPlugin/plugin.py @@ -80,4 +80,4 @@ def test(returnValue): print "You entered", returnValue def Plugins(**kwargs): - return PluginDescriptor(name="Test", description="plugin to test some capabilities", where = PluginDescriptor.WHERE_PLUGINMENU, fnc=main) + return PluginDescriptor(name="Test", description="plugin to test some capabilities", where = PluginDescriptor.WHERE_PLUGINMENU, needsRestart = False, fnc=main) diff --git a/lib/python/Plugins/Extensions/CutListEditor/meta/Makefile.am b/lib/python/Plugins/Extensions/CutListEditor/meta/Makefile.am index ee18898d..e926e3ff 100755 --- a/lib/python/Plugins/Extensions/CutListEditor/meta/Makefile.am +++ b/lib/python/Plugins/Extensions/CutListEditor/meta/Makefile.am @@ -1,4 +1,4 @@ -installdir = $(datadir)/meta/ +installdir = $(datadir)/meta dist_install_DATA = plugin_cutlisteditor.xml diff --git a/lib/python/Plugins/Extensions/CutListEditor/meta/plugin_cutlisteditor.xml b/lib/python/Plugins/Extensions/CutListEditor/meta/plugin_cutlisteditor.xml index 1431caf4..7132ba02 100755 --- a/lib/python/Plugins/Extensions/CutListEditor/meta/plugin_cutlisteditor.xml +++ b/lib/python/Plugins/Extensions/CutListEditor/meta/plugin_cutlisteditor.xml @@ -2,23 +2,14 @@ <prerequisites> <tag type="Recording" /> </prerequisites> - <info language="en"> + <info> <author>Dream Multimedia</author> <name>CutListEditor</name> <packagename>enigma2-plugin-extensions-cutlisteditor</packagename> - <shortdescription>CutListEditor allows you to edit your movies.</shortdescription> + <shortdescription>CutListEditor allows you to edit your movies</shortdescription> <description>CutListEditor allows you to edit your movies.\nSeek to the start of the stuff you want to cut away. Press OK, select 'start cut'.\nThen seek to the end, press OK, select 'end cut'. That's it.</description> <screenshot src="http://www.dreamboxupdate.com/preview/plugin_cutlisteditor.jpg" /> </info> - <info language="de"> - <author>Dream Multimedia</author> - <name>Schnitteditor</name> - <packagename>enigma2-plugin-extensions-cutlisteditor</packagename> - <shortdescription>Mit dem Schnitteditor können Sie Ihre Aufnahmen schneiden.</shortdescription> - <description>Mit dem Schnitteditor können Sie Ihre Aufnahmen schneiden.\nSpulen Sie zum Anfang des zu schneidenden Teils der Aufnahme. Drücken Sie dann OK und wählen Sie: 'start cut'.\nDann spulen Sie zum Ende, drücken OK und wählen 'end cut'. Das ist alles. - </description> - <screenshot src="http://www.dreamboxupdate.com/preview/plugin_cutlisteditor.jpg" /> - </info> <files type="package"> <!-- without version, without .ipk --> <file type="package" name="enigma2-plugin-extensions-cutlisteditor" /> </files> diff --git a/lib/python/Plugins/Extensions/CutListEditor/plugin.py b/lib/python/Plugins/Extensions/CutListEditor/plugin.py index 0627df3b..141c04ac 100644 --- a/lib/python/Plugins/Extensions/CutListEditor/plugin.py +++ b/lib/python/Plugins/Extensions/CutListEditor/plugin.py @@ -406,4 +406,4 @@ def main(session, service, **kwargs): session.open(CutListEditor, service) def Plugins(**kwargs): - return PluginDescriptor(name="Cutlist Editor", description=_("Cutlist editor..."), where = PluginDescriptor.WHERE_MOVIELIST, fnc=main) + return PluginDescriptor(name="Cutlist Editor", description=_("Cutlist editor..."), where = PluginDescriptor.WHERE_MOVIELIST, needsRestart = False, fnc=main) diff --git a/lib/python/Plugins/Extensions/DVDBurn/DVDProject.py b/lib/python/Plugins/Extensions/DVDBurn/DVDProject.py index 83672460..6dbcd499 100644 --- a/lib/python/Plugins/Extensions/DVDBurn/DVDProject.py +++ b/lib/python/Plugins/Extensions/DVDBurn/DVDProject.py @@ -1,5 +1,8 @@ from Tools.Directories import fileExists from Components.config import config, ConfigSubsection, ConfigInteger, ConfigText, ConfigSelection, getConfigListEntry, ConfigSequence, ConfigSubList +import DVDTitle +import xml.dom.minidom +from Tools.Directories import resolveFilename, SCOPE_PLUGINS, SCOPE_FONTS class ConfigColor(ConfigSequence): def __init__(self, default = [128,128,128]): @@ -10,7 +13,10 @@ class ConfigFilename(ConfigText): ConfigText.__init__(self, default = "", fixed_size = True, visible_width = False) def getMulti(self, selected): - filename = (self.text.rstrip("/").rsplit("/",1))[1].encode("utf-8")[:40] + " " + if self.text == "": + return ("mtext"[1-selected:], "", 0) + cut_len = min(len(self.text),40) + filename = (self.text.rstrip("/").rsplit("/",1))[1].encode("utf-8")[:cut_len] + " " if self.allmarked: mark = range(0, len(filename)) else: @@ -34,10 +40,11 @@ class DVDProject: self.settings.vmgm = ConfigFilename() self.filekeys = ["vmgm", "isopath", "menutemplate"] self.menutemplate = MenuTemplate() + self.error = "" + self.session = None def addService(self, service): - import DVDTitle - title = DVDTitle.DVDTitle() + title = DVDTitle.DVDTitle(self) title.addService(service) self.titles.append(title) return title @@ -100,47 +107,86 @@ class DVDProject: return ret def loadProject(self, filename): - import xml.dom.minidom - try: + #try: if not fileExists(filename): self.error = "xml file not found!" - raise AttributeError - else: - self.error = "" + #raise AttributeError file = open(filename, "r") data = file.read().decode("utf-8").replace('&',"&").encode("ascii",'xmlcharrefreplace') file.close() projectfiledom = xml.dom.minidom.parseString(data) - for project in projectfiledom.childNodes[0].childNodes: - if project.nodeType == xml.dom.minidom.Element.nodeType: - if project.tagName == 'settings': - i = 0 - if project.attributes.length < len(self.settings.dict())-1: - self.error = "project attributes missing" - raise AttributeError - while i < project.attributes.length: - item = project.attributes.item(i) - key = item.name.encode("utf-8") - try: - val = eval(item.nodeValue) - except (NameError, SyntaxError): - val = item.nodeValue.encode("utf-8") - try: - self.settings.dict()[key].setValue(val) - except (KeyError): - self.error = "unknown attribute '%s'" % (key) - raise AttributeError - i += 1 + for node in projectfiledom.childNodes[0].childNodes: + print "node:", node + if node.nodeType == xml.dom.minidom.Element.nodeType: + if node.tagName == 'settings': + self.xmlAttributesToConfig(node, self.settings) + elif node.tagName == 'titles': + self.xmlGetTitleNodeRecursive(node) + for key in self.filekeys: val = self.settings.dict()[key].getValue() if not fileExists(val): + if val[0] != "/": + if key.find("font") == 0: + val = resolveFilename(SCOPE_FONTS)+val + else: + val = resolveFilename(SCOPE_PLUGINS)+"Extensions/DVDBurn/"+val + if fileExists(val): + self.settings.dict()[key].setValue(val) + continue self.error += "\n%s '%s' not found" % (key, val) - if len(self.error): - raise AttributeError + #except AttributeError: + #print "loadProject AttributeError", self.error + #self.error += (" in project '%s'") % (filename) + #return False + return True + + def xmlAttributesToConfig(self, node, config): + try: + i = 0 + #if node.attributes.length < len(config.dict())-1: + #self.error = "project attributes missing" + #raise AttributeError + while i < node.attributes.length: + item = node.attributes.item(i) + key = item.name.encode("utf-8") + try: + val = eval(item.nodeValue) + except (NameError, SyntaxError): + val = item.nodeValue.encode("utf-8") + try: + print "config[%s].setValue(%s)" % (key, val) + config.dict()[key].setValue(val) + except (KeyError): + self.error = "unknown attribute '%s'" % (key) + print "KeyError", self.error + raise AttributeError + i += 1 except AttributeError: - self.error += (" in project '%s'") % (filename) + self.error += (" XML attribute error '%s'") % node.toxml() return False - return True + + def xmlGetTitleNodeRecursive(self, node, title_idx = -1): + print "[xmlGetTitleNodeRecursive]", title_idx, node + print node.childNodes + for subnode in node.childNodes: + print "xmlGetTitleNodeRecursive subnode:", subnode + if subnode.nodeType == xml.dom.minidom.Element.nodeType: + if subnode.tagName == 'title': + title_idx += 1 + title = DVDTitle.DVDTitle(self) + self.titles.append(title) + self.xmlGetTitleNodeRecursive(subnode, title_idx) + if subnode.tagName == 'path': + print "path:", subnode.firstChild.data + filename = subnode.firstChild.data + self.titles[title_idx].addFile(filename.encode("utf-8")) + if subnode.tagName == 'properties': + self.xmlAttributesToConfig(node, self.titles[title_idx].properties) + if subnode.tagName == 'audiotracks': + self.xmlGetTitleNodeRecursive(subnode, title_idx) + if subnode.tagName == 'audiotrack': + print "audiotrack...", subnode.toxml() def getSize(self): totalsize = 0 @@ -187,6 +233,7 @@ class MenuTemplate(DVDProject): self.filekeys = ["menubg", "menuaudio", "fontface_headline", "fontface_title", "fontface_subtitle"] from TitleProperties import languageChoices self.settings.menulang = ConfigSelection(choices = languageChoices.choices, default=languageChoices.choices[1][0]) + self.error = "" def loadTemplate(self, filename): ret = DVDProject.loadProject(self, filename) diff --git a/lib/python/Plugins/Extensions/DVDBurn/DVDTitle.py b/lib/python/Plugins/Extensions/DVDBurn/DVDTitle.py index 660005e6..6dff00d6 100644 --- a/lib/python/Plugins/Extensions/DVDBurn/DVDTitle.py +++ b/lib/python/Plugins/Extensions/DVDBurn/DVDTitle.py @@ -1,4 +1,5 @@ from Components.config import config, ConfigSubsection, ConfigSubList, ConfigInteger, ConfigText, ConfigSelection, getConfigListEntry, ConfigSequence, ConfigYesNo +import TitleCutter class ConfigFixedText(ConfigText): def __init__(self, text, visible_width=60): @@ -7,17 +8,17 @@ class ConfigFixedText(ConfigText): pass class DVDTitle: - def __init__(self): + def __init__(self, project): self.properties = ConfigSubsection() self.properties.menutitle = ConfigText(fixed_size = False, visible_width = 80) self.properties.menusubtitle = ConfigText(fixed_size = False, visible_width = 80) - self.DVBname = _("Title") - self.DVBdescr = _("Description") - self.DVBchannel = _("Channel") self.properties.aspect = ConfigSelection(choices = [("4:3", _("4:3")), ("16:9", _("16:9"))]) self.properties.widescreen = ConfigSelection(choices = [("nopanscan", "nopanscan"), ("noletterbox", "noletterbox")]) self.properties.autochapter = ConfigInteger(default = 0, limits = (0, 60)) self.properties.audiotracks = ConfigSubList() + self.DVBname = _("Title") + self.DVBdescr = _("Description") + self.DVBchannel = _("Channel") self.cuesheet = [ ] self.source = None self.filesize = 0 @@ -27,6 +28,8 @@ class DVDTitle: self.chaptermarks = [ ] self.timeCreate = None self.VideoType = -1 + self.project = project + self.length = 0 def addService(self, service): from os import path @@ -36,7 +39,7 @@ class DVDTitle: self.source = service serviceHandler = eServiceCenter.getInstance() info = serviceHandler.info(service) - sDescr = info and " " + info.getInfoString(service, iServiceInformation.sDescription) or "" + sDescr = info and info.getInfoString(service, iServiceInformation.sDescription) or "" self.DVBdescr = sDescr sTimeCreate = info.getInfo(service, iServiceInformation.sTimeCreate) if sTimeCreate > 1: @@ -49,9 +52,20 @@ class DVDTitle: self.filesize = path.getsize(self.inputfile) self.estimatedDiskspace = self.filesize self.length = info.getLength(service) + + def addFile(self, filename): + from enigma import eServiceReference + ref = eServiceReference(1, 0, filename) + self.addService(ref) + self.project.session.openWithCallback(self.titleEditDone, TitleCutter.CutlistReader, self) + + def titleEditDone(self, cutlist): + self.initDVDmenuText(len(self.project.titles)) + self.cuesheet = cutlist + self.produceFinalCuesheet() - def initDVDmenuText(self, project, track): - s = project.menutemplate.settings + def initDVDmenuText(self, track): + s = self.project.menutemplate.settings self.properties.menutitle.setValue(self.formatDVDmenuText(s.titleformat.getValue(), track)) self.properties.menusubtitle.setValue(self.formatDVDmenuText(s.subtitleformat.getValue(), track)) diff --git a/lib/python/Plugins/Extensions/DVDBurn/DVDToolbox.py b/lib/python/Plugins/Extensions/DVDBurn/DVDToolbox.py index 53287a36..0b81cfdf 100755 --- a/lib/python/Plugins/Extensions/DVDBurn/DVDToolbox.py +++ b/lib/python/Plugins/Extensions/DVDBurn/DVDToolbox.py @@ -68,7 +68,7 @@ class DVDToolbox(Screen): self["info"].text = "" self["details"].setText("") self.Console = Console() - cmd = "/bin/dvd+rw-mediainfo /dev/" + harddiskmanager.getCD() + cmd = "dvd+rw-mediainfo /dev/" + harddiskmanager.getCD() self.Console.ePopen(cmd, self.mediainfoCB) def format(self): @@ -186,7 +186,7 @@ class DVDformatTask(Task): Task.__init__(self, job, ("RW medium format")) self.toolbox = job.toolbox self.postconditions.append(DVDformatTaskPostcondition()) - self.setTool("/bin/dvd+rw-format") + self.setTool("dvd+rw-format") self.args += [ "/dev/" + harddiskmanager.getCD() ] self.end = 1100 self.retryargs = [ ] diff --git a/lib/python/Plugins/Extensions/DVDBurn/DreamboxDVD.ddvdp.xml b/lib/python/Plugins/Extensions/DVDBurn/DreamboxDVD.ddvdp.xml index 451e8a8b..4645260d 100644 --- a/lib/python/Plugins/Extensions/DVDBurn/DreamboxDVD.ddvdp.xml +++ b/lib/python/Plugins/Extensions/DVDBurn/DreamboxDVD.ddvdp.xml @@ -3,11 +3,11 @@ <settings name="Dreambox DVD record" authormode="menu_linked" - menutemplate="/usr/lib/enigma2/python/Plugins/Extensions/DVDBurn/Thumbs_menu_clouds.ddvdm.xml" + menutemplate="Thumbs_menu_clouds.ddvdm.xml" titlesetmode="multi" - vmgm="/usr/lib/enigma2/python/Plugins/Extensions/DVDBurn/vmgmdream.mpg" + vmgm="vmgmdream.mpg" output="dvd" - isopath="/media/hdd/movie/" + isopath="/hdd/movie/" dataformat="iso9660_4" /> <titles> </titles> diff --git a/lib/python/Plugins/Extensions/DVDBurn/Process.py b/lib/python/Plugins/Extensions/DVDBurn/Process.py index 642a898d..a928ae20 100644 --- a/lib/python/Plugins/Extensions/DVDBurn/Process.py +++ b/lib/python/Plugins/Extensions/DVDBurn/Process.py @@ -5,7 +5,7 @@ from Screens.MessageBox import MessageBox class png2yuvTask(Task): def __init__(self, job, inputfile, outputfile): Task.__init__(self, job, "Creating menu video") - self.setTool("/usr/bin/png2yuv") + self.setTool("png2yuv") self.args += ["-n1", "-Ip", "-f25", "-j", inputfile] self.dumpFile = outputfile self.weighting = 15 @@ -21,7 +21,7 @@ class png2yuvTask(Task): class mpeg2encTask(Task): def __init__(self, job, inputfile, outputfile): Task.__init__(self, job, "Encoding menu video") - self.setTool("/usr/bin/mpeg2enc") + self.setTool("mpeg2enc") self.args += ["-f8", "-np", "-a2", "-o", outputfile] self.inputFile = inputfile self.weighting = 25 @@ -36,7 +36,7 @@ class mpeg2encTask(Task): class spumuxTask(Task): def __init__(self, job, xmlfile, inputfile, outputfile): Task.__init__(self, job, "Muxing buttons into menu") - self.setTool("/usr/bin/spumux") + self.setTool("spumux") self.args += [xmlfile] self.inputFile = inputfile self.dumpFile = outputfile @@ -54,7 +54,7 @@ class spumuxTask(Task): class MakeFifoNode(Task): def __init__(self, job, number): Task.__init__(self, job, "Make FIFO nodes") - self.setTool("/bin/mknod") + self.setTool("mknod") nodename = self.job.workspace + "/dvd_title_%d" % number + ".mpg" self.args += [nodename, "p"] self.weighting = 10 @@ -62,14 +62,14 @@ class MakeFifoNode(Task): class LinkTS(Task): def __init__(self, job, sourcefile, link_name): Task.__init__(self, job, "Creating symlink for source titles") - self.setTool("/bin/ln") + self.setTool("ln") self.args += ["-s", sourcefile, link_name] self.weighting = 10 class CopyMeta(Task): def __init__(self, job, sourcefile): Task.__init__(self, job, "Copy title meta files") - self.setTool("/bin/cp") + self.setTool("cp") from os import listdir path, filename = sourcefile.rstrip("/").rsplit("/",1) tsfiles = listdir(path) @@ -84,8 +84,8 @@ class DemuxTask(Task): Task.__init__(self, job, "Demux video into ES") title = job.project.titles[job.i] self.global_preconditions.append(DiskspacePrecondition(title.estimatedDiskspace)) - self.setTool("/usr/bin/projectx") - self.args += [inputfile, "-demux", "-out", self.job.workspace ] + self.setTool("projectx") + self.args += [inputfile, "-demux", "-set", "ExportPanel.Streamtype.Subpicture=0", "-set", "ExportPanel.Streamtype.Teletext=0", "-out", self.job.workspace ] self.end = 300 self.prog_state = 0 self.weighting = 1000 @@ -95,6 +95,8 @@ class DemuxTask(Task): self.relevantAudioPIDs = [ ] self.getRelevantAudioPIDs(title) self.generated_files = [ ] + self.mplex_audiofiles = { } + self.mplex_videofile = "" self.mplex_streamfiles = [ ] if len(self.cutlist) > 1: self.args += [ "-cut", self.cutfile ] @@ -132,8 +134,10 @@ class DemuxTask(Task): def haveNewFile(self, file): print "[DemuxTask] produced file:", file, self.currentPID self.generated_files.append(file) - if self.currentPID in self.relevantAudioPIDs or file.endswith("m2v"): - self.mplex_streamfiles.append(file) + if self.currentPID in self.relevantAudioPIDs: + self.mplex_audiofiles[self.currentPID] = file + elif file.endswith("m2v"): + self.mplex_videofile = file def haveProgress(self, progress): #print "PROGRESS [%s]" % progress @@ -167,6 +171,13 @@ class DemuxTask(Task): f.close() def cleanup(self, failed): + print "[DemuxTask::cleanup]" + self.mplex_streamfiles = [ self.mplex_videofile ] + for pid in self.relevantAudioPIDs: + if pid in self.mplex_audiofiles: + self.mplex_streamfiles.append(self.mplex_audiofiles[pid]) + print self.mplex_streamfiles + if failed: import os for file in self.generated_files: @@ -194,7 +205,7 @@ class MplexTask(Task): self.weighting = weighting self.demux_task = demux_task self.postconditions.append(MplexTaskPostcondition()) - self.setTool("/usr/bin/mplex") + self.setTool("mplex") self.args += ["-f8", "-o", outputfile, "-v1"] if inputfiles: self.args += inputfiles @@ -222,7 +233,7 @@ class RemoveESFiles(Task): def __init__(self, job, demux_task): Task.__init__(self, job, "Remove temp. files") self.demux_task = demux_task - self.setTool("/bin/rm") + self.setTool("rm") self.weighting = 10 def prepare(self): @@ -234,7 +245,7 @@ class DVDAuthorTask(Task): def __init__(self, job): Task.__init__(self, job, "Authoring DVD") self.weighting = 20 - self.setTool("/usr/bin/dvdauthor") + self.setTool("dvdauthor") self.CWD = self.job.workspace self.args += ["-x", self.job.workspace+"/dvdauthor.xml"] self.menupreview = job.menupreview @@ -255,7 +266,7 @@ class DVDAuthorTask(Task): class DVDAuthorFinalTask(Task): def __init__(self, job): Task.__init__(self, job, "dvdauthor finalize") - self.setTool("/usr/bin/dvdauthor") + self.setTool("dvdauthor") self.args += ["-T", "-o", self.job.workspace + "/dvd"] class WaitForResidentTasks(Task): @@ -292,7 +303,7 @@ class BurnTaskPostcondition(Condition): class BurnTask(Task): ERROR_NOTWRITEABLE, ERROR_LOAD, ERROR_SIZE, ERROR_WRITE_FAILED, ERROR_DVDROM, ERROR_ISOFS, ERROR_FILETOOLARGE, ERROR_ISOTOOLARGE, ERROR_MINUSRWBUG, ERROR_UNKNOWN = range(10) - def __init__(self, job, extra_args=[], tool="/bin/growisofs"): + def __init__(self, job, extra_args=[], tool="growisofs"): Task.__init__(self, job, job.name) self.weighting = 500 self.end = 120 # 100 for writing, 10 for buffer flush, 10 for closing disc @@ -357,7 +368,7 @@ class BurnTask(Task): class RemoveDVDFolder(Task): def __init__(self, job): Task.__init__(self, job, "Remove temp. files") - self.setTool("/bin/rm") + self.setTool("rm") self.args += ["-rf", self.job.workspace] self.weighting = 10 @@ -882,13 +893,13 @@ class DVDJob(Job): volName = self.project.settings.name.getValue() if output == "dvd": self.name = _("Burn DVD") - tool = "/bin/growisofs" + tool = "growisofs" burnargs = [ "-Z", "/dev/" + harddiskmanager.getCD(), "-dvd-compat" ] if self.project.size/(1024*1024) > self.project.MAX_SL: burnargs += [ "-use-the-force-luke=4gms", "-speed=1", "-R" ] elif output == "iso": self.name = _("Create DVD-ISO") - tool = "/usr/bin/mkisofs" + tool = "genisoimage" isopathfile = getISOfilename(self.project.settings.isopath.getValue(), volName) burnargs = [ "-o", isopathfile ] burnargs += [ "-dvd-video", "-publisher", "Dreambox", "-V", volName, self.workspace + "/dvd" ] @@ -920,14 +931,14 @@ class DVDdataJob(Job): output = self.project.settings.output.getValue() volName = self.project.settings.name.getValue() - tool = "/bin/growisofs" + tool = "growisofs" if output == "dvd": self.name = _("Burn DVD") burnargs = [ "-Z", "/dev/" + harddiskmanager.getCD(), "-dvd-compat" ] if self.project.size/(1024*1024) > self.project.MAX_SL: burnargs += [ "-use-the-force-luke=4gms", "-speed=1", "-R" ] elif output == "iso": - tool = "/usr/bin/mkisofs" + tool = "genisoimage" self.name = _("Create DVD-ISO") isopathfile = getISOfilename(self.project.settings.isopath.getValue(), volName) burnargs = [ "-o", isopathfile ] @@ -959,5 +970,5 @@ class DVDisoJob(Job): if getSize(imagepath)/(1024*1024) > self.project.MAX_SL: burnargs += [ "-use-the-force-luke=4gms", "-speed=1", "-R" ] burnargs += [ "-dvd-video", "-publisher", "Dreambox", "-V", volName, imagepath ] - tool = "/bin/growisofs" + tool = "growisofs" BurnTask(self, burnargs, tool) diff --git a/lib/python/Plugins/Extensions/DVDBurn/ProjectSettings.py b/lib/python/Plugins/Extensions/DVDBurn/ProjectSettings.py index a1c38842..39d7277e 100755 --- a/lib/python/Plugins/Extensions/DVDBurn/ProjectSettings.py +++ b/lib/python/Plugins/Extensions/DVDBurn/ProjectSettings.py @@ -92,10 +92,10 @@ class ProjectSettings(Screen,ConfigListScreen): <ePixmap pixmap="skin_default/buttons/green.png" position="140,0" size="140,40" alphatest="on" /> <ePixmap pixmap="skin_default/buttons/yellow.png" position="280,0" size="140,40" alphatest="on" /> <ePixmap pixmap="skin_default/buttons/blue.png" position="420,0" size="140,40" alphatest="on" /> - <widget source="key_red" render="Label" position="0,0" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#9f1313" transparent="1" /> - <widget source="key_green" render="Label" position="140,0" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#1f771f" transparent="1" /> - <widget source="key_yellow" render="Label" position="280,0" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#a08500" transparent="1" /> - <widget source="key_blue" render="Label" position="420,0" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#18188b" transparent="1" /> + <widget source="key_red" render="Label" position="0,0" zPosition="1" size="140,40" font="Regular;19" halign="center" valign="center" backgroundColor="#9f1313" transparent="1" /> + <widget source="key_green" render="Label" position="140,0" zPosition="1" size="140,40" font="Regular;19" halign="center" valign="center" backgroundColor="#1f771f" transparent="1" /> + <widget source="key_yellow" render="Label" position="280,0" zPosition="1" size="140,40" font="Regular;19" halign="center" valign="center" backgroundColor="#a08500" transparent="1" /> + <widget source="key_blue" render="Label" position="420,0" zPosition="1" size="140,40" font="Regular;19" halign="center" valign="center" backgroundColor="#18188b" transparent="1" /> <widget name="config" position="5,50" size="550,276" scrollbarMode="showOnDemand" /> <ePixmap pixmap="skin_default/div-h.png" position="0,350" zPosition="1" size="560,2" /> <widget source="info" render="Label" position="10,360" size="550,80" font="Regular;18" halign="center" valign="center" /> @@ -233,10 +233,19 @@ class ProjectSettings(Screen,ConfigListScreen): else: self.session.open(MessageBox,self.project.error,MessageBox.TYPE_ERROR) elif scope == "project": - if self.project.loadProject(path): - self.initConfigList() + self.path = path + print "len(self.titles)", len(self.project.titles) + if len(self.project.titles): + self.session.openWithCallback(self.askLoadCB, MessageBox,text = _("Your current collection will get lost!") + "\n" + _("Do you want to restore your settings?"), type = MessageBox.TYPE_YESNO) else: - self.session.open(MessageBox,self.project.error,MessageBox.TYPE_ERROR) + self.askLoadCB(True) elif scope: configRef.setValue(path) self.initConfigList() + + def askLoadCB(self, answer): + if answer is not None and answer: + if self.project.loadProject(self.path): + self.initConfigList() + else: + self.session.open(MessageBox,self.project.error,MessageBox.TYPE_ERROR)
\ No newline at end of file diff --git a/lib/python/Plugins/Extensions/DVDBurn/Text_menu_boat.ddvdm.xml b/lib/python/Plugins/Extensions/DVDBurn/Text_menu_boat.ddvdm.xml index 0de7f4b6..bf6356a9 100644 --- a/lib/python/Plugins/Extensions/DVDBurn/Text_menu_boat.ddvdm.xml +++ b/lib/python/Plugins/Extensions/DVDBurn/Text_menu_boat.ddvdm.xml @@ -3,17 +3,17 @@ <settings titleformat="$i. $t" subtitleformat="$D.$M.$Y, $T $C, $d" - menubg="/usr/lib/enigma2/python/Plugins/Extensions/DVDBurn/dreamdvd_boat.jpg" - menuaudio="/usr/lib/enigma2/python/Plugins/Extensions/DVDBurn/silence.mp2" + menubg="dreamdvd_boat.jpg" + menuaudio="silence.mp2" dimensions="(720, 576)" rows="5" cols="1" color_button="[8, 0, 0]" color_highlight="[0, 192, 192]" color_headline="[0, 0, 128]" - fontface_headline="/usr/share/fonts/nmsbd.ttf" - fontface_title="/usr/share/fonts/nmsbd.ttf" - fontface_subtitle="/usr/share/fonts/nmsbd.ttf" + fontface_headline="nmsbd.ttf" + fontface_title="nmsbd.ttf" + fontface_subtitle="nmsbd.ttf" fontsize_headline="46" fontsize_title="24" fontsize_subtitle="14" diff --git a/lib/python/Plugins/Extensions/DVDBurn/Thumbs_menu_clouds.ddvdm.xml b/lib/python/Plugins/Extensions/DVDBurn/Thumbs_menu_clouds.ddvdm.xml index c00c3fee..074f2037 100644 --- a/lib/python/Plugins/Extensions/DVDBurn/Thumbs_menu_clouds.ddvdm.xml +++ b/lib/python/Plugins/Extensions/DVDBurn/Thumbs_menu_clouds.ddvdm.xml @@ -3,17 +3,17 @@ <settings titleformat="$t" subtitleformat="$d" - menubg="/usr/lib/enigma2/python/Plugins/Extensions/DVDBurn/dreamdvd_clouds.jpg" - menuaudio="/usr/lib/enigma2/python/Plugins/Extensions/DVDBurn/silence.mp2" + menubg="dreamdvd_clouds.jpg" + menuaudio="silence.mp2" dimensions="(720, 576)" rows="2" cols="2" color_button="[8, 0, 0]" color_highlight="[128, 0, 0]" color_headline="[128, 0, 0]" - fontface_headline="/usr/share/fonts/nmsbd.ttf" - fontface_title="/usr/share/fonts/nmsbd.ttf" - fontface_subtitle="/usr/share/fonts/nmsbd.ttf" + fontface_headline="nmsbd.ttf" + fontface_title="nmsbd.ttf" + fontface_subtitle="nmsbd.ttf" fontsize_headline="46" fontsize_title="18" fontsize_subtitle="12" diff --git a/lib/python/Plugins/Extensions/DVDBurn/TitleCutter.py b/lib/python/Plugins/Extensions/DVDBurn/TitleCutter.py index 61152e8a..71a7f239 100644 --- a/lib/python/Plugins/Extensions/DVDBurn/TitleCutter.py +++ b/lib/python/Plugins/Extensions/DVDBurn/TitleCutter.py @@ -32,9 +32,9 @@ class TitleCutter(CutListEditor): pid = str(i.getPID()) if description == "MPEG": description = "MP2" - print "[audiotrack] pid:", pid, "description:", description, "language:", DVB_lang + print "[audiotrack] pid:", pid, "description:", description, "language:", DVB_lang, "count:", x, "active:", (x < 8) self.t.properties.audiotracks.append(ConfigSubsection()) - self.t.properties.audiotracks[-1].active = ConfigYesNo(default = True) + self.t.properties.audiotracks[-1].active = ConfigYesNo(default = (x < 8)) self.t.properties.audiotracks[-1].format = ConfigFixedText(description) self.t.properties.audiotracks[-1].language = ConfigSelection(choices = languageChoices.choices, default=languageChoices.getLanguage(DVB_lang)) self.t.properties.audiotracks[-1].pid = ConfigFixedText(pid) @@ -52,13 +52,35 @@ class TitleCutter(CutListEditor): CutListEditor.grabFrame(self) def exit(self): + if self.t.VideoType == -1: + self.getPMTInfo() self.checkAndGrabThumb() self.session.nav.stopService() self.close(self.cut_list[:]) class CutlistReader(TitleCutter): + skin = """ + <screen position="0,0" size="720,576"> + <eLabel position="0,0" size="720,576" zPosition="1" backgroundColor="#000000" /> + <widget name="Video" position="0,0" size="100,75" /> + <widget name="SeekState" position="0,0" /> + <widget source="cutlist" position="0,0" render="Listbox" > + <convert type="TemplatedMultiContent"> + {"template": [ + MultiContentEntryText(text = 1), + MultiContentEntryText(text = 2) + ], + "fonts": [gFont("Regular", 18)], + "itemHeight": 20 + } + </convert> + </widget> + <widget name="Timeline" position="0,0" /> + </screen>""" + def __init__(self, session, t): TitleCutter.__init__(self, session, t) + self.skin = CutlistReader.skin def getPMTInfo(self): TitleCutter.getPMTInfo(self) diff --git a/lib/python/Plugins/Extensions/DVDBurn/TitleList.py b/lib/python/Plugins/Extensions/DVDBurn/TitleList.py index dbc988b1..2cbeb633 100755 --- a/lib/python/Plugins/Extensions/DVDBurn/TitleList.py +++ b/lib/python/Plugins/Extensions/DVDBurn/TitleList.py @@ -11,38 +11,46 @@ from Components.Sources.List import List from Components.Sources.StaticText import StaticText from Components.Sources.Progress import Progress from Components.MultiContent import MultiContentEntryText +from Components.Label import MultiColorLabel from enigma import gFont, RT_HALIGN_LEFT, RT_HALIGN_RIGHT from Tools.Directories import resolveFilename, SCOPE_PLUGINS class TitleList(Screen, HelpableScreen): skin = """ - <screen name="TitleList" position="center,center" size="560,445" title="DVD Tool" > + <screen name="TitleList" position="center,center" size="560,470" title="DVD Tool" > <ePixmap pixmap="skin_default/buttons/red.png" position="0,0" size="140,40" alphatest="on" /> <ePixmap pixmap="skin_default/buttons/green.png" position="140,0" size="140,40" alphatest="on" /> <ePixmap pixmap="skin_default/buttons/yellow.png" position="280,0" size="140,40" alphatest="on" /> <ePixmap pixmap="skin_default/buttons/blue.png" position="420,0" size="140,40" alphatest="on" /> - <widget source="key_red" render="Label" position="0,0" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#9f1313" transparent="1" /> - <widget source="key_green" render="Label" position="140,0" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#1f771f" transparent="1" /> - <widget source="key_yellow" render="Label" position="280,0" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#a08500" transparent="1" /> - <widget source="key_blue" render="Label" position="420,0" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#18188b" transparent="1" /> + <widget source="key_red" render="Label" position="0,0" zPosition="1" size="140,40" font="Regular;19" halign="center" valign="center" backgroundColor="#9f1313" transparent="1" /> + <widget source="key_green" render="Label" position="140,0" zPosition="1" size="140,40" font="Regular;19" halign="center" valign="center" backgroundColor="#1f771f" transparent="1" /> + <widget source="key_yellow" render="Label" position="280,0" zPosition="1" size="140,40" font="Regular;19" halign="center" valign="center" backgroundColor="#a08500" transparent="1" /> + <widget source="key_blue" render="Label" position="420,0" zPosition="1" size="140,40" font="Regular;19" halign="center" valign="center" backgroundColor="#18188b" transparent="1" /> <widget source="title_label" render="Label" position="10,48" size="540,38" font="Regular;18" transparent="1" /> - <widget source="error_label" render="Label" position="10,48" size="540,395" zPosition="3" font="Regular;20" transparent="1" /> - <widget source="titles" render="Listbox" scrollbarMode="showOnDemand" position="10,86" size="540,312" zPosition="3" transparent="1" > + <widget source="error_label" render="Label" position="10,48" size="540,296" zPosition="3" font="Regular;20" transparent="1" /> + <widget source="titles" render="Listbox" scrollbarMode="showOnDemand" position="10,86" size="546,296" zPosition="3" transparent="1" > <convert type="TemplatedMultiContent"> {"template": [ - MultiContentEntryText(pos = (0, 0), size = (420, 20), font = 0, flags = RT_HALIGN_LEFT, text = 1), # index 1 Title, - MultiContentEntryText(pos = (0, 20), size = (328, 17), font = 1, flags = RT_HALIGN_LEFT, text = 2), # index 2 description, - MultiContentEntryText(pos = (420, 6), size = (120, 20), font = 1, flags = RT_HALIGN_RIGHT, text = 3), # index 3 begin time, - MultiContentEntryText(pos = (328, 20), size = (154, 17), font = 1, flags = RT_HALIGN_RIGHT, text = 4), # index 4 channel, - MultiContentEntryText(pos = (482, 20), size = (58, 20), font = 1, flags = RT_HALIGN_RIGHT, text = 5), # index 4 channel, + MultiContentEntryText(pos = (0, 0), size = (360, 20), font = 0, flags = RT_HALIGN_LEFT, text = 1), # index 1 Title, + MultiContentEntryText(pos = (0, 20), size = (360, 17), font = 1, flags = RT_HALIGN_LEFT, text = 2), # index 2 description, + MultiContentEntryText(pos = (366, 6), size = (152, 20), font = 1, flags = RT_HALIGN_RIGHT, text = 3), # index 3 channel, + MultiContentEntryText(pos = (366, 20), size = (102, 17), font = 1, flags = RT_HALIGN_RIGHT, text = 4), # index 4 begin time, + MultiContentEntryText(pos = (470, 20), size = (48, 20), font = 1, flags = RT_HALIGN_RIGHT, text = 5), # index 5 duration, ], "fonts": [gFont("Regular", 20), gFont("Regular", 14)], "itemHeight": 37 } </convert> </widget> - <widget source="space_bar" render="Progress" position="10,410" size="540,26" borderWidth="1" backgroundColor="#254f7497" /> - <widget source="space_label" render="Label" position="40,414" size="480,22" zPosition="2" font="Regular;18" halign="center" transparent="1" foregroundColor="#000000" /> + <ePixmap pixmap="skin_default/div-h.png" position="0,390" zPosition="10" size="560,2" /> + <ePixmap pixmap="skin_default/buttons/key_menu.png" position="10,394" size="35,25" alphatest="on" /> + <widget source="hint" render="Label" position="50,396" size="540,22" font="Regular;18" halign="left" /> + <widget name="medium_label" position="10,420" size="540,22" font="Regular;18" halign="left" foregroundColors="#FFFFFF,#FFFF00,#FF0000" /> + <widget source="space_bar_single" render="Progress" position="10,446" size="270,24" borderWidth="1" zPosition="2" backgroundColor="#254f7497" /> + <widget source="space_label_single" render="Label" position="10,449" size="270,22" zPosition="3" font="Regular;18" halign="center" transparent="1" foregroundColor="#000000" /> + <widget source="space_bar_dual" render="Progress" position="10,446" size="540,24" borderWidth="1" backgroundColor="#254f7497" /> + <widget source="space_label_dual" render="Label" position="10,449" size="540,22" zPosition="2" font="Regular;18" halign="center" transparent="1" foregroundColor="#000000" /> + </screen>""" def __init__(self, session, project = None): @@ -75,17 +83,19 @@ class TitleList(Screen, HelpableScreen): self["title_label"] = StaticText() self["error_label"] = StaticText() - self["space_label"] = StaticText() - self["space_bar"] = Progress() + self["space_label_single"] = StaticText() + self["space_label_dual"] = StaticText() + self["hint"] = StaticText(_("Advanced Options")) + self["medium_label"] = MultiColorLabel() + self["space_bar_single"] = Progress() + self["space_bar_dual"] = Progress() + self["titles"] = List([]) + self.previous_size = 0 if project is not None: self.project = project else: self.newProject() - - self["titles"] = List([]) - self.updateTitleList() - self.previous_size = 0 self.onLayoutFinish.append(self.layoutFinished) def layoutFinished(self): @@ -107,15 +117,16 @@ class TitleList(Screen, HelpableScreen): j = self.backgroundJob menu.append(("%s: %s (%d%%)" % (j.getStatustext(), j.name, int(100*j.progress/float(j.end))), self.showBackgroundJob)) menu.append((_("DVD media toolbox"), self.toolbox)) - menu.append((_("Preview menu"), self.previewMenu)) if self.project.settings.output.getValue() == "dvd": if len(self["titles"].list): menu.append((_("Burn DVD"), self.burnProject)) elif self.project.settings.output.getValue() == "iso": menu.append((_("Create DVD-ISO"), self.burnProject)) menu.append((_("Burn existing image to DVD"), self.selectImage)) - menu.append((_("Edit chapters of current title"), self.editTitle)) - menu.append((_("Reset and renumerate title names"), self.resetTitles)) + if len(self["titles"].list): + menu.append((_("Preview menu"), self.previewMenu)) + menu.append((_("Edit chapters of current title"), self.editTitle)) + menu.append((_("Reset and renumerate title names"), self.resetTitles)) menu.append((_("Exit"), self.leave)) self.session.openWithCallback(self.menuCallback, ChoiceBox, title="", list=menu) @@ -149,9 +160,9 @@ class TitleList(Screen, HelpableScreen): <ePixmap pixmap="skin_default/buttons/red.png" position="0,0" size="140,40" alphatest="on" /> <ePixmap pixmap="skin_default/buttons/green.png" position="140,0" size="140,40" alphatest="on" /> <ePixmap pixmap="skin_default/buttons/yellow.png" position="280,0" size="140,40" alphatest="on" /> - <widget source="key_red" render="Label" position="0,0" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#9f1313" transparent="1" /> - <widget source="key_green" render="Label" position="140,0" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#1f771f" transparent="1" /> - <widget source="key_yellow" render="Label" position="280,0" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#a08500" transparent="1" /> + <widget source="key_red" render="Label" position="0,0" zPosition="1" size="140,40" font="Regular;19" halign="center" valign="center" backgroundColor="#9f1313" transparent="1" /> + <widget source="key_green" render="Label" position="140,0" zPosition="1" size="140,40" font="Regular;19" halign="center" valign="center" backgroundColor="#1f771f" transparent="1" /> + <widget source="key_yellow" render="Label" position="280,0" zPosition="1" size="140,40" font="Regular;19" halign="center" valign="center" backgroundColor="#a08500" transparent="1" /> <widget name="waitingtext" position="0,45" size="560,395" zPosition="4" font="Regular;22" halign="center" valign="center" /> <widget name="list" position="5,40" size="550,375" zPosition="2" scrollbarMode="showOnDemand" /> <widget name="DescriptionBorder" pixmap="skin_default/border_eventinfo.png" position="0,316" zPosition="1" size="560,103" transparent="1" alphatest="on" /> @@ -197,7 +208,7 @@ class TitleList(Screen, HelpableScreen): self.close(current) self.session.openWithCallback(self.selectedSource, DVDMovieSelection) - def selectedSource(self, source): + def selectedSource(self, source = None): if source is None: return None if not source.getPath().endswith(".ts"): @@ -228,7 +239,7 @@ class TitleList(Screen, HelpableScreen): def settingsCB(self, update=True): if not update: return - self["title_label"].text = _("Table of content for collection") + " \"" + self.project.settings.name.getValue() + "\":" + self.updateTitleList() def loadTemplate(self): filename = resolveFilename(SCOPE_PLUGINS)+"Extensions/DVDBurn/DreamboxDVD.ddvdp.xml" @@ -281,9 +292,11 @@ class TitleList(Screen, HelpableScreen): if len(list): self["key_red"].text = _("Remove title") self["key_yellow"].text = _("Title properties") + self["title_label"].text = _("Table of content for collection") + " \"" + self.project.settings.name.getValue() + "\":" else: self["key_red"].text = "" self["key_yellow"].text = "" + self["title_label"].text = _("Please add titles to the compilation.") def updateSize(self): size = self.project.size/(1024*1024) @@ -292,20 +305,32 @@ class TitleList(Screen, HelpableScreen): print "updateSize:", size, "MAX_DL:", MAX_DL, "MAX_SL:", MAX_SL if size > MAX_DL: percent = 100 * size / float(MAX_DL) - self["space_label"].text = "%d MB - " % size + _("exceeds dual layer medium!") + " (%.2f%% " % (100-percent) + _("free") + ")" - self["space_bar"].value = int(percent) + self["space_label_dual"].text = "%d MB (%.2f%%)" % (size, percent) + self["space_bar_dual"].value = int(percent) + self["space_bar_single"].value = 100 + self["space_label_single"].text = "" + self["medium_label"].setText(_("Exceeds dual layer medium!")) + self["medium_label"].setForegroundColorNum(2) if self.previous_size < MAX_DL: - self.session.open(MessageBox,text = _("exceeds dual layer medium!"), type = MessageBox.TYPE_ERROR) + self.session.open(MessageBox,text = _("Exceeds dual layer medium!"), type = MessageBox.TYPE_ERROR) elif size > MAX_SL: percent = 100 * size / float(MAX_DL) - self["space_label"].text = "%d MB " % size + _("of a DUAL layer medium used.") + " (%.2f%% " % (100-percent) + _("free") + ")" - self["space_bar"].value = int(percent) + self["space_label_dual"].text = "%d MB (%.2f%%)" % (size, percent) + self["space_bar_dual"].value = int(percent) + self["space_bar_single"].value = 100 + self["space_label_single"].text = "" + self["medium_label"].setText(_("Required medium type:") + " " + _("DUAL LAYER DVD") + ", %d MB " % (MAX_DL - size) + _("free")) + self["medium_label"].setForegroundColorNum(1) if self.previous_size < MAX_SL: - self.session.open(MessageBox,text = _("Your collection exceeds the size of a single layer medium, you will need a blank dual layer DVD!"), type = MessageBox.TYPE_INFO) + self.session.open(MessageBox, text = _("Your collection exceeds the size of a single layer medium, you will need a blank dual layer DVD!"), timeout = 10, type = MessageBox.TYPE_INFO) elif size < MAX_SL: percent = 100 * size / float(MAX_SL) - self["space_label"].text = "%d MB " % size + _("of a SINGLE layer medium used.") + " (%.2f%% " % (100-percent) + _("free") + ")" - self["space_bar"].value = int(percent) + self["space_label_single"].text = "%d MB (%.2f%%)" % (size, percent) + self["space_bar_single"].value = int(percent) + self["space_bar_dual"].value = 0 + self["space_label_dual"].text = "" + self["medium_label"].setText(_("Required medium type:") + " " + _("SINGLE LAYER DVD") + ", %d MB " % (MAX_SL - size) + _("free")) + self["medium_label"].setForegroundColorNum(0) self.previous_size = size def getCurrentTitle(self): @@ -323,9 +348,7 @@ class TitleList(Screen, HelpableScreen): def titleEditDone(self, cutlist): t = self.current_edit_title - t.initDVDmenuText(self.project,len(self.project.titles)) - t.cuesheet = cutlist - t.produceFinalCuesheet() + t.titleEditDone(cutlist) if t.VideoType != 0: self.session.openWithCallback(self.DVDformatCB,MessageBox,text = _("The DVD standard doesn't support H.264 (HDTV) video streams. Do you want to create a Dreambox format data DVD (which will not play in stand-alone DVD players) instead?"), type = MessageBox.TYPE_YESNO) else: @@ -335,7 +358,7 @@ class TitleList(Screen, HelpableScreen): count = 0 for title in self.project.titles: count += 1 - title.initDVDmenuText(self.project,count) + title.initDVDmenuText(count) self.updateTitleList() def DVDformatCB(self, answer): @@ -346,5 +369,13 @@ class TitleList(Screen, HelpableScreen): else: self.removeTitle(t) - def leave(self): - self.close() + def leave(self, close = False): + if not len(self["titles"].list) or close: + self.close() + else: + self.session.openWithCallback(self.exitCB, MessageBox,text = _("Your current collection will get lost!") + "\n" + _("Do you really want to exit?"), type = MessageBox.TYPE_YESNO) + + def exitCB(self, answer): + print "exitCB", answer + if answer is not None and answer: + self.close()
\ No newline at end of file diff --git a/lib/python/Plugins/Extensions/DVDBurn/TitleProperties.py b/lib/python/Plugins/Extensions/DVDBurn/TitleProperties.py index 0a664eba..956f054d 100755 --- a/lib/python/Plugins/Extensions/DVDBurn/TitleProperties.py +++ b/lib/python/Plugins/Extensions/DVDBurn/TitleProperties.py @@ -21,10 +21,12 @@ class TitleProperties(Screen,ConfigListScreen): <screen name="TitleProperties" position="center,center" size="560,445" title="Properties of current title" > <ePixmap pixmap="skin_default/buttons/red.png" position="0,0" size="140,40" alphatest="on" /> <ePixmap pixmap="skin_default/buttons/green.png" position="140,0" size="140,40" alphatest="on" /> + <ePixmap pixmap="skin_default/buttons/yellow.png" position="280,0" size="140,40" alphatest="on" /> <ePixmap pixmap="skin_default/buttons/blue.png" position="420,0" size="140,40" alphatest="on" /> - <widget source="key_red" render="Label" position="0,0" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#9f1313" transparent="1" /> - <widget source="key_green" render="Label" position="140,0" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#1f771f" transparent="1" /> - <widget source="key_blue" render="Label" position="420,0" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#18188b" transparent="1" /> + <widget source="key_red" render="Label" position="0,0" zPosition="1" size="140,40" font="Regular;19" halign="center" valign="center" backgroundColor="#9f1313" transparent="1" /> + <widget source="key_green" render="Label" position="140,0" zPosition="1" size="140,40" font="Regular;19" halign="center" valign="center" backgroundColor="#1f771f" transparent="1" /> + <widget source="key_yellow" render="Label" position="280,0" zPosition="1" size="140,40" font="Regular;19" halign="center" valign="center" backgroundColor="#a08500" transparent="1" /> + <widget source="key_blue" render="Label" position="420,0" zPosition="1" size="140,40" font="Regular;19" halign="center" valign="center" backgroundColor="#18188b" transparent="1" /> <widget source="serviceinfo" render="Label" position="10,46" size="350,144" font="Regular;18" /> <widget name="thumbnail" position="370,46" size="180,144" alphatest="on" /> <widget name="config" position="10,206" size="540,228" scrollbarMode="showOnDemand" /> @@ -38,7 +40,8 @@ class TitleProperties(Screen,ConfigListScreen): self["key_red"] = StaticText(_("Cancel")) self["key_green"] = StaticText(_("OK")) - self["key_blue"] = StaticText(_("Edit Title")) + self["key_yellow"] = StaticText(_("Edit Title")) + self["key_blue"] = StaticText() self["serviceinfo"] = StaticText() self["thumbnail"] = Pixmap() @@ -57,7 +60,7 @@ class TitleProperties(Screen,ConfigListScreen): { "green": self.exit, "red": self.cancel, - "blue": self.editTitle, + "yellow": self.editTitle, "cancel": self.cancel, "ok": self.ok, }, -2) diff --git a/lib/python/Plugins/Extensions/DVDBurn/meta/Makefile.am b/lib/python/Plugins/Extensions/DVDBurn/meta/Makefile.am index 0b3be7d6..1a823b70 100755 --- a/lib/python/Plugins/Extensions/DVDBurn/meta/Makefile.am +++ b/lib/python/Plugins/Extensions/DVDBurn/meta/Makefile.am @@ -1,4 +1,4 @@ -installdir = $(datadir)/meta/ +installdir = $(datadir)/meta dist_install_DATA = plugin_dvdburn.xml diff --git a/lib/python/Plugins/Extensions/DVDBurn/meta/plugin_dvdburn.xml b/lib/python/Plugins/Extensions/DVDBurn/meta/plugin_dvdburn.xml index 647d1cfd..c1e202a9 100755 --- a/lib/python/Plugins/Extensions/DVDBurn/meta/plugin_dvdburn.xml +++ b/lib/python/Plugins/Extensions/DVDBurn/meta/plugin_dvdburn.xml @@ -3,22 +3,17 @@ <tag type="Recording" /> <tag type="Software" /> </prerequisites> - <info language="en"> + <info> <author>Dream Multimedia</author> <name>DVDBurn</name> <packagename>enigma2-plugin-extensions-dvdburn</packagename> - <shortdescription>With DVDBurn you can burn your recordings to a dvd.</shortdescription> - <description>With DVDBurn you can burn your recordings to a dvd.\nArchive all your favorite movies to recordable dvds with menus if wanted.</description> + <shortdescription>Burn your recordings to DVD</shortdescription> + <description>With DVDBurn you can make compilations of records from your Dreambox hard drive.\n + Optionally you can add customizable menus. You can record the compilation to a standard-compliant DVD that can be played on conventinal DVD players.\n + HDTV recordings can only be burned in proprietary dreambox format.</description> <screenshot src="http://www.dreamboxupdate.com/preview/plugin_dvdburn_en.jpg" /> </info> - <info language="de"> - <author>Dream Multimedia</author> - <name>DVDBurn</name> - <packagename>enigma2-plugin-extensions-dvdburn</packagename> - <shortdescription>Mit DVDBurn brennen Sie ihre Aufnahmen auf DVD.</shortdescription> - <description>Mit DVDBurn brennen Sie ihre Aufnahmen auf DVD.\nArchivieren Sie Ihre Liblingsfilme auf DVD mit Menus wenn Sie es wünschen.</description> - <screenshot src="http://www.dreamboxupdate.com/preview/plugin_dvdburn_de.jpg" /> - </info> + <files type="package"> <!-- without version, without .ipk --> <file type="package" name="enigma2-plugin-extensions-dvdburn" /> </files> diff --git a/lib/python/Plugins/Extensions/DVDBurn/plugin.py b/lib/python/Plugins/Extensions/DVDBurn/plugin.py index 45f438da..f5d2fa62 100644 --- a/lib/python/Plugins/Extensions/DVDBurn/plugin.py +++ b/lib/python/Plugins/Extensions/DVDBurn/plugin.py @@ -12,6 +12,6 @@ def main_add(session, service, **kwargs): dvdburn.selectedSource(service) def Plugins(**kwargs): - descr = _("Burn to DVD...") - return [PluginDescriptor(name="DVD Burn", description=descr, where = PluginDescriptor.WHERE_MOVIELIST, fnc=main_add, icon="dvdburn.png"), - PluginDescriptor(name="DVD Burn", description=descr, where = PluginDescriptor.WHERE_PLUGINMENU, fnc=main, icon="dvdburn.png") ] + descr = _("Burn to DVD") + return [PluginDescriptor(name="DVD Burn", description=descr, where = PluginDescriptor.WHERE_MOVIELIST, needsRestart = True, fnc=main_add, icon="dvdburn.png"), + PluginDescriptor(name="DVD Burn", description=descr, where = PluginDescriptor.WHERE_PLUGINMENU, needsRestart = True, fnc=main, icon="dvdburn.png") ] diff --git a/lib/python/Plugins/Extensions/DVDPlayer/Makefile.am b/lib/python/Plugins/Extensions/DVDPlayer/Makefile.am index d1a995ca..ec212e47 100644 --- a/lib/python/Plugins/Extensions/DVDPlayer/Makefile.am +++ b/lib/python/Plugins/Extensions/DVDPlayer/Makefile.am @@ -1,12 +1,9 @@ -if HAVE_DDVDLIB SUBDIRS = src meta installdir = $(pkglibdir)/python/Plugins/Extensions/DVDPlayer install_PYTHON = \ - src/servicedvd.so \ __init__.py \ plugin.py \ keymap.xml \ LICENSE -endif diff --git a/lib/python/Plugins/Extensions/DVDPlayer/keymap.xml b/lib/python/Plugins/Extensions/DVDPlayer/keymap.xml index 7b7f2054..bf57e753 100644 --- a/lib/python/Plugins/Extensions/DVDPlayer/keymap.xml +++ b/lib/python/Plugins/Extensions/DVDPlayer/keymap.xml @@ -8,7 +8,8 @@ <key id="KEY_PREVIOUS" mapto="prevChapter" flags="m" /> <key id="KEY_NEXT" mapto="nextChapter" flags="m" /> <key id="KEY_TV" mapto="tv" flags="m" /> - <key id="KEY_AUDIO" mapto="dvdAudioMenu" flags="m" /> + <key id="KEY_AUDIO" mapto="AudioSelection" flags="m" /> + <key id="KEY_AUDIO" mapto="dvdAudioMenu" flags="l" /> <key id="KEY_RADIO" mapto="nextAudioTrack" flags="m" /> <key id="KEY_TEXT" mapto="nextSubtitleTrack" flags="m" /> <key id="KEY_VIDEO" mapto="nextAngle" flags="m" /> diff --git a/lib/python/Plugins/Extensions/DVDPlayer/meta/Makefile.am b/lib/python/Plugins/Extensions/DVDPlayer/meta/Makefile.am index 12d13710..aa22b996 100755 --- a/lib/python/Plugins/Extensions/DVDPlayer/meta/Makefile.am +++ b/lib/python/Plugins/Extensions/DVDPlayer/meta/Makefile.am @@ -1,4 +1,4 @@ -installdir = $(datadir)/meta/ +installdir = $(datadir)/meta dist_install_DATA = plugin_dvdplayer.xml diff --git a/lib/python/Plugins/Extensions/DVDPlayer/meta/plugin_dvdplayer.xml b/lib/python/Plugins/Extensions/DVDPlayer/meta/plugin_dvdplayer.xml index 1353f7d2..6fc5a6f1 100755 --- a/lib/python/Plugins/Extensions/DVDPlayer/meta/plugin_dvdplayer.xml +++ b/lib/python/Plugins/Extensions/DVDPlayer/meta/plugin_dvdplayer.xml @@ -2,23 +2,14 @@ <prerequisites> <tag type="Multimedia" /> </prerequisites> - <info language="en"> + <info> <author>Dream Multimedia</author> <name>DVDPlayer</name> <packagename>enigma2-plugin-extensions-dvdplayer</packagename> - <shortdescription>DVDPlayer plays your DVDs on your Dreambox.</shortdescription> + <shortdescription>DVDPlayer plays your DVDs on your Dreambox</shortdescription> <description>DVDPlayer plays your DVDs on your Dreambox.\nWith the DVDPlayer you can play your DVDs on your Dreambox from a DVD or even from an iso file or video_ts folder on your harddisc or network.</description> <screenshot src="http://www.dreamboxupdate.com/preview/plugin_dvdplayer.jpg" /> </info> - <info language="de"> - <author>Dream Multimedia</author> - <name>DVDPlayer</name> - <packagename>enigma2-plugin-extensions-dvdplayer</packagename> - <shortdescription>Spielen Sie Ihre DVDs mit dem DVDPlayer auf Ihrer Dreambox ab.</shortdescription> - <description>Spielen Sie Ihre DVDs mit dem DVDPlayer auf Ihrer Dreambox ab.\nMit dem DVDPlayer können Sie Ihre DVDs auf Ihrer Dreambox abspielen. Dabei ist es egal ob Sie von DVD, iso-Datei oder sogar direkt von einer video_ts Ordnerstruktur von Ihrer Festplatte oder dem Netzwerk abspielen. - </description> - <screenshot src="http://www.dreamboxupdate.com/preview/plugin_dvdplayer.jpg" /> - </info> <files type="package"> <!-- without version, without .ipk --> <file type="package" name="enigma2-plugin-extensions-dvdplayer" /> </files> diff --git a/lib/python/Plugins/Extensions/DVDPlayer/plugin.py b/lib/python/Plugins/Extensions/DVDPlayer/plugin.py index 6e4d9cc6..1cee0aac 100755..100644 --- a/lib/python/Plugins/Extensions/DVDPlayer/plugin.py +++ b/lib/python/Plugins/Extensions/DVDPlayer/plugin.py @@ -4,7 +4,7 @@ from Screens.Screen import Screen from Screens.MessageBox import MessageBox from Screens.ChoiceBox import ChoiceBox from Screens.HelpMenu import HelpableScreen -from Screens.InfoBarGenerics import InfoBarSeek, InfoBarPVRState, InfoBarCueSheetSupport, InfoBarShowHide, InfoBarNotifications +from Screens.InfoBarGenerics import InfoBarSeek, InfoBarPVRState, InfoBarCueSheetSupport, InfoBarShowHide, InfoBarNotifications, InfoBarAudioSelection, InfoBarSubtitleSupport from Components.ActionMap import ActionMap, NumberActionMap, HelpableActionMap from Components.Label import Label from Components.Sources.StaticText import StaticText @@ -39,8 +39,11 @@ class FileBrowser(Screen): currDir = "/media/dvd/" if not pathExists(currDir): currDir = "/" + if lastpath == "": # 'None' is magic to start at the list of mountpoints + currDir = None - self.filelist = FileList(currDir, matchingPattern = "(?i)^.*\.(iso)", useServiceRef = True) + inhibitDirs = ["/bin", "/boot", "/dev", "/etc", "/home", "/lib", "/proc", "/sbin", "/share", "/sys", "/tmp", "/usr", "/var"] + self.filelist = FileList(currDir, matchingPattern = "(?i)^.*\.(iso|img)", useServiceRef = True) self["filelist"] = self.filelist self["FilelistActions"] = ActionMap(["SetupActions"], @@ -79,6 +82,12 @@ class FileBrowser(Screen): lastpath = (pathname.rstrip("/").rsplit("/",1))[0] print "lastpath video_ts.ifo=", lastpath self.close(pathname) + if fileExists(pathname+"VIDEO_TS/VIDEO_TS.IFO"): + print "dvd structure found, trying to open..." + lastpath = (pathname.rstrip("/").rsplit("/",1))[0] + print "lastpath video_ts.ifo=", lastpath + pathname += "VIDEO_TS" + self.close(pathname) else: lastpath = filename[0:filename.rfind("/")] print "lastpath directory=", lastpath @@ -88,12 +97,12 @@ class FileBrowser(Screen): self.close(None) class DVDSummary(Screen): - skin = """ - <screen position="0,0" size="132,64"> + skin = ( + """<screen name="DVDSummary" position="0,0" size="132,64" id="1"> <widget source="session.CurrentService" render="Label" position="5,4" size="120,28" font="Regular;12" transparent="1" > <convert type="ServiceName">Name</convert> </widget> - <widget name="DVDPlayer" position="5,30" size="66,16" font="Regular;12" transparent="1" /> + <widget name="DVDPlayer" position="5,30" size="66,16" font="Regular;11" transparent="1" /> <widget name="Chapter" position="72,30" size="54,16" font="Regular;12" transparent="1" halign="right" /> <widget source="session.CurrentService" render="Label" position="66,46" size="60,18" font="Regular;16" transparent="1" halign="right" > <convert type="ServicePosition">Position</convert> @@ -101,7 +110,20 @@ class DVDSummary(Screen): <widget source="session.CurrentService" render="Progress" position="6,46" size="60,18" borderWidth="1" > <convert type="ServicePosition">Position</convert> </widget> - </screen>""" + </screen>""", + """<screen name="DVDSummary" position="0,0" size="96,64" id="2"> + <widget source="session.CurrentService" render="Label" position="0,0" size="96,25" font="Regular;12" transparent="1" > + <convert type="ServiceName">Name</convert> + </widget> + <widget name="DVDPlayer" position="0,26" size="96,12" font="Regular;10" transparent="1" /> + <widget name="Chapter" position="0,40" size="66,12" font="Regular;10" transparent="1" halign="left" /> + <widget source="session.CurrentService" render="Label" position="66,40" size="30,12" font="Regular;10" transparent="1" halign="right" > + <convert type="ServicePosition">Position</convert> + </widget> + <widget source="session.CurrentService" render="Progress" position="0,52" size="96,12" borderWidth="1" > + <convert type="ServicePosition">Position</convert> + </widget> + </screen>""") def __init__(self, session, parent): Screen.__init__(self, session, parent) @@ -173,7 +195,7 @@ class ChapterZap(Screen): self.Timer.callback.append(self.keyOK) self.Timer.start(3000, True) -class DVDPlayer(Screen, InfoBarBase, InfoBarNotifications, InfoBarSeek, InfoBarPVRState, InfoBarShowHide, HelpableScreen, InfoBarCueSheetSupport): +class DVDPlayer(Screen, InfoBarBase, InfoBarNotifications, InfoBarSeek, InfoBarPVRState, InfoBarShowHide, HelpableScreen, InfoBarCueSheetSupport, InfoBarAudioSelection, InfoBarSubtitleSupport): ALLOW_SUSPEND = Screen.SUSPEND_PAUSES ENABLE_RESUME_SUPPORT = True @@ -222,8 +244,6 @@ class DVDPlayer(Screen, InfoBarBase, InfoBarNotifications, InfoBarSeek, InfoBarP self.saved_config_speeds_backward = config.seek.speeds_backward.value self.saved_config_enter_forward = config.seek.enter_forward.value self.saved_config_enter_backward = config.seek.enter_backward.value - self.saved_config_seek_stepwise_minspeed = config.seek.stepwise_minspeed.value - self.saved_config_seek_stepwise_repeat = config.seek.stepwise_repeat.value self.saved_config_seek_on_pause = config.seek.on_pause.value self.saved_config_seek_speeds_slowmotion = config.seek.speeds_slowmotion.value @@ -233,8 +253,6 @@ class DVDPlayer(Screen, InfoBarBase, InfoBarNotifications, InfoBarSeek, InfoBarP config.seek.speeds_slowmotion.value = [ ] config.seek.enter_forward.value = "2" config.seek.enter_backward.value = "2" - config.seek.stepwise_minspeed.value = "Never" - config.seek.stepwise_repeat.value = "3" config.seek.on_pause.value = "play" def restore_infobar_seek_config(self): @@ -243,8 +261,6 @@ class DVDPlayer(Screen, InfoBarBase, InfoBarNotifications, InfoBarSeek, InfoBarP config.seek.speeds_slowmotion.value = self.saved_config_seek_speeds_slowmotion config.seek.enter_forward.value = self.saved_config_enter_forward config.seek.enter_backward.value = self.saved_config_enter_backward - config.seek.stepwise_minspeed.value = self.saved_config_seek_stepwise_minspeed - config.seek.stepwise_repeat.value = self.saved_config_seek_stepwise_repeat config.seek.on_pause.value = self.saved_config_seek_on_pause def __init__(self, session, dvd_device = None, dvd_filelist = [ ], args = None): @@ -253,10 +269,12 @@ class DVDPlayer(Screen, InfoBarBase, InfoBarNotifications, InfoBarSeek, InfoBarP InfoBarNotifications.__init__(self) InfoBarCueSheetSupport.__init__(self, actionmap = "MediaPlayerCueSheetActions") InfoBarShowHide.__init__(self) + InfoBarAudioSelection.__init__(self) + InfoBarSubtitleSupport.__init__(self) HelpableScreen.__init__(self) self.save_infobar_seek_config() self.change_infobar_seek_config() - InfoBarSeek.__init__(self, useSeekBackHack=False) + InfoBarSeek.__init__(self) InfoBarPVRState.__init__(self) self.dvdScreen = self.session.instantiateDialog(DVDOverlay) @@ -332,6 +350,7 @@ class DVDPlayer(Screen, InfoBarBase, InfoBarNotifications, InfoBarSeek, InfoBarP "prevTitle": (self.prevTitle, _("jump back to the previous title")), "tv": (self.askLeavePlayer, _("exit DVD player or return to file browser")), "dvdAudioMenu": (self.enterDVDAudioMenu, _("(show optional DVD audio menu)")), + "AudioSelection": (self.enterAudioSelection, _("Select audio track")), "nextAudioTrack": (self.nextAudioTrack, _("switch to the next audio track")), "nextSubtitleTrack": (self.nextSubtitleTrack, _("switch to the next subtitle language")), "nextAngle": (self.nextAngle, _("switch to the next angle")), @@ -524,6 +543,9 @@ class DVDPlayer(Screen, InfoBarBase, InfoBarNotifications, InfoBarSeek, InfoBarP keys.keyPressed(key) return keys + def enterAudioSelection(self): + self.audioSelection() + def nextAudioTrack(self): self.sendKey(iServiceKeys.keyUser) @@ -612,6 +634,14 @@ class DVDPlayer(Screen, InfoBarBase, InfoBarNotifications, InfoBarSeek, InfoBarP newref = eServiceReference(4369, 0, val) print "play", newref.toString() if curref is None or curref != newref: + if newref.toString().endswith("/VIDEO_TS") or newref.toString().endswith("/"): + names = newref.toString().rsplit("/",3) + if names[2].startswith("Disk ") or names[2].startswith("DVD "): + name = str(names[1]) + " - " + str(names[2]) + else: + name = names[2] + print "setting name to: ", self.service + newref.setName(str(name)) self.session.nav.playService(newref) self.service = self.session.nav.getCurrentService() print "self.service", self.service @@ -745,5 +775,5 @@ def filescan(**kwargs): )] def Plugins(**kwargs): - return [PluginDescriptor(name = "DVDPlayer", description = "Play DVDs", where = PluginDescriptor.WHERE_MENU, fnc = menu), - PluginDescriptor(where = PluginDescriptor.WHERE_FILESCAN, fnc = filescan)] + return [PluginDescriptor(name = "DVDPlayer", description = "Play DVDs", where = PluginDescriptor.WHERE_MENU, needsRestart = True, fnc = menu), + PluginDescriptor(where = PluginDescriptor.WHERE_FILESCAN, needsRestart = True, fnc = filescan)] diff --git a/lib/python/Plugins/Extensions/DVDPlayer/src/Makefile.am b/lib/python/Plugins/Extensions/DVDPlayer/src/Makefile.am index 774871e8..3e2ee67c 100644 --- a/lib/python/Plugins/Extensions/DVDPlayer/src/Makefile.am +++ b/lib/python/Plugins/Extensions/DVDPlayer/src/Makefile.am @@ -1,12 +1,18 @@ -OBJS := servicedvd.cpp +AM_CPPFLAGS = \ + -I$(top_srcdir) \ + -I$(top_srcdir)/include \ + -include Python.h \ + -include $(top_builddir)/enigma2_config.h --include $(OBJS:.cpp=.d) +AM_CXXFLAGS = @LIBDDVD_CFLAGS@ -servicedvd.so: - $(CXX) $(CPPFLAGS) -MD $(CXXFLAGS) $(DEFS) -I$(top_srcdir)/include \ - -Wall -W $(OBJS) -shared -fPIC -Wl,-soname,servicedvd.so -o servicedvd.so \ - $(LDFLAGS) -ldreamdvd +plugindir = $(pkglibdir)/python/Plugins/Extensions/DVDPlayer -all: servicedvd.so +plugin_LTLIBRARIES = servicedvd.la -CLEANFILES = servicedvd.so servicedvd.d +servicedvd_la_SOURCES = \ + servicedvd.cpp \ + servicedvd.h + +servicedvd_la_LDFLAGS = -avoid-version -module +servicedvd_la_LIBADD = @LIBDDVD_LIBS@ diff --git a/lib/python/Plugins/Extensions/DVDPlayer/src/servicedvd.cpp b/lib/python/Plugins/Extensions/DVDPlayer/src/servicedvd.cpp index 0372c497..6d1397da 100644 --- a/lib/python/Plugins/Extensions/DVDPlayer/src/servicedvd.cpp +++ b/lib/python/Plugins/Extensions/DVDPlayer/src/servicedvd.cpp @@ -32,6 +32,7 @@ eServiceFactoryDVD::eServiceFactoryDVD() { std::list<std::string> extensions; extensions.push_back("iso"); + extensions.push_back("img"); sc->addServiceFactory(eServiceFactoryDVD::id, this, extensions); } } @@ -85,12 +86,9 @@ RESULT eServiceFactoryDVD::offlineOperations(const eServiceReference &, ePtr<iSe DEFINE_REF(eServiceDVD); eServiceDVD::eServiceDVD(eServiceReference ref): - m_ref(ref), - m_ddvdconfig(ddvd_create()), - m_subtitle_widget(0), - m_state(stIdle), - m_current_trick(0), - m_pump(eApp, 1) + m_ref(ref), m_ddvdconfig(ddvd_create()), m_subtitle_widget(0), m_state(stIdle), + m_current_trick(0), m_pump(eApp, 1), m_width(-1), m_height(-1), + m_aspect(-1), m_framerate(-1), m_progressive(-1) { int aspect = DDVD_16_9; int policy = DDVD_PAN_SCAN; @@ -309,6 +307,32 @@ void eServiceDVD::gotMessage(int /*what*/) m_event(this, evSeekableStatusChanged); m_event(this, evUser+12); break; +#ifdef DDVD_SUPPORTS_PICTURE_INFO + case DDVD_SIZE_CHANGED: + { + int changed = m_width != -1 && m_height != -1 && m_aspect != -1; + ddvd_get_last_size(m_ddvdconfig, &m_width, &m_height, &m_aspect); + if (changed) + m_event((iPlayableService*)this, evVideoSizeChanged); + break; + } + case DDVD_PROGRESSIVE_CHANGED: + { + int changed = m_progressive != -1; + ddvd_get_last_progressive(m_ddvdconfig, &m_progressive); + if (changed) + m_event((iPlayableService*)this, evVideoProgressiveChanged); + break; + } + case DDVD_FRAMERATE_CHANGED: + { + int changed = m_framerate != -1; + ddvd_get_last_framerate(m_ddvdconfig, &m_framerate); + if (changed) + m_event((iPlayableService*)this, evVideoFramerateChanged); + break; + } +#endif default: break; } @@ -373,6 +397,61 @@ RESULT eServiceDVD::subtitle(ePtr<iSubtitleOutput> &ptr) return 0; } +RESULT eServiceDVD::audioTracks(ePtr<iAudioTrackSelection> &ptr) +{ + ptr = this; + return 0; +} + +int eServiceDVD::getNumberOfTracks() +{ + int i = 0; + ddvd_get_audio_count(m_ddvdconfig, &i); + return i; +} + +int eServiceDVD::getCurrentTrack() +{ + int audio_id,audio_type; + uint16_t audio_lang; + ddvd_get_last_audio(m_ddvdconfig, &audio_id, &audio_lang, &audio_type); + return audio_id; +} + +RESULT eServiceDVD::selectTrack(unsigned int i) +{ + ddvd_set_audio(m_ddvdconfig, i); + return 0; +} + +RESULT eServiceDVD::getTrackInfo(struct iAudioTrackInfo &info, unsigned int audio_id) +{ + int audio_type; + uint16_t audio_lang; + ddvd_get_audio_byid(m_ddvdconfig, audio_id, &audio_lang, &audio_type); + char audio_string[3]={audio_lang >> 8, audio_lang, 0}; + info.m_pid = audio_id+1; + info.m_language = audio_string; + switch(audio_type) + { + case DDVD_MPEG: + info.m_description = "MPEG"; + break; + case DDVD_AC3: + info.m_description = "AC3"; + break; + case DDVD_DTS: + info.m_description = "DTS"; + break; + case DDVD_LPCM: + info.m_description = "LPCM"; + break; + default: + info.m_description = "und"; + } + return 0; +} + RESULT eServiceDVD::keys(ePtr<iServiceKeys> &ptr) { ptr=this; @@ -457,7 +536,10 @@ RESULT eServiceDVD::getName(std::string &name) if ( m_ddvd_titlestring[0] != '\0' ) name = m_ddvd_titlestring; else - name = m_ref.path; + if ( !m_ref.name.empty() ) + name = m_ref.name; + else + name = m_ref.path; return 0; } @@ -500,6 +582,18 @@ int eServiceDVD::getInfo(int w) case sUser+7: case sUser+8: return resIsPyObject; +#ifdef DDVD_SUPPORTS_PICTURE_INFO + case sVideoWidth: + return m_width; + case sVideoHeight: + return m_height; + case sAspect: + return m_aspect; + case sProgressive: + return m_progressive; + case sFrameRate: + return m_framerate; +#endif default: return resNA; } @@ -584,14 +678,33 @@ PyObject *eServiceDVD::getInfoObject(int w) Py_RETURN_NONE; } -RESULT eServiceDVD::enableSubtitles(eWidget *parent, SWIG_PYOBJECT(ePyObject) /*entry*/) +RESULT eServiceDVD::enableSubtitles(eWidget *parent, ePyObject tuple) { delete m_subtitle_widget; + eSize size = eSize(720, 576); m_subtitle_widget = new eSubtitleWidget(parent); m_subtitle_widget->resize(parent->size()); - eSize size = eSize(720, 576); + int pid = -1; + + if ( tuple != Py_None ) + { + ePyObject entry; + int tuplesize = PyTuple_Size(tuple); + if (!PyTuple_Check(tuple)) + goto error_out; + if (tuplesize < 1) + goto error_out; + entry = PyTuple_GET_ITEM(tuple, 1); + if (!PyInt_Check(entry)) + goto error_out; + pid = PyInt_AsLong(entry)-1; + + ddvd_set_spu(m_ddvdconfig, pid); + m_event(this, evUser+7); + } + eDebug("eServiceDVD::enableSubtitles %i", pid); if (!m_pixmap) { @@ -609,6 +722,9 @@ RESULT eServiceDVD::enableSubtitles(eWidget *parent, SWIG_PYOBJECT(ePyObject) /* m_subtitle_widget->show(); return 0; + +error_out: + return -1; } RESULT eServiceDVD::disableSubtitles(eWidget */*parent*/) @@ -620,8 +736,26 @@ RESULT eServiceDVD::disableSubtitles(eWidget */*parent*/) PyObject *eServiceDVD::getSubtitleList() { - eDebug("eServiceDVD::getSubtitleList nyi"); - Py_RETURN_NONE; + ePyObject l = PyList_New(0); + unsigned int spu_count = 0; + ddvd_get_spu_count(m_ddvdconfig, &spu_count); + + for ( unsigned int spu_id = 0; spu_id < spu_count; spu_id++ ) + { + uint16_t spu_lang; + ddvd_get_spu_byid(m_ddvdconfig, spu_id, &spu_lang); + char spu_string[3]={spu_lang >> 8, spu_lang, 0}; + + ePyObject tuple = PyTuple_New(5); + PyTuple_SetItem(tuple, 0, PyInt_FromLong(2)); + PyTuple_SetItem(tuple, 1, PyInt_FromLong(spu_id+1)); + PyTuple_SetItem(tuple, 2, PyInt_FromLong(5)); + PyTuple_SetItem(tuple, 3, PyInt_FromLong(0)); + PyTuple_SetItem(tuple, 4, PyString_FromString(spu_string)); + PyList_Append(l, tuple); + Py_DECREF(tuple); + } + return l; } PyObject *eServiceDVD::getCachedSubtitle() diff --git a/lib/python/Plugins/Extensions/DVDPlayer/src/servicedvd.h b/lib/python/Plugins/Extensions/DVDPlayer/src/servicedvd.h index c730d501..80cfcf0c 100644 --- a/lib/python/Plugins/Extensions/DVDPlayer/src/servicedvd.h +++ b/lib/python/Plugins/Extensions/DVDPlayer/src/servicedvd.h @@ -26,7 +26,7 @@ public: RESULT offlineOperations(const eServiceReference &, ePtr<iServiceOfflineOperations> &ptr); }; -class eServiceDVD: public iPlayableService, public iPauseableService, public iSeekableService, +class eServiceDVD: public iPlayableService, public iPauseableService, public iSeekableService, public iAudioTrackSelection, public iServiceInformation, public iSubtitleOutput, public iServiceKeys, public iCueSheet, public eThread, public Object { friend class eServiceFactoryDVD; @@ -35,7 +35,7 @@ public: virtual ~eServiceDVD(); // not implemented (yet) RESULT audioChannel(ePtr<iAudioChannelSelection> &ptr) { ptr = 0; return -1; } - RESULT audioTracks(ePtr<iAudioTrackSelection> &ptr) { ptr = 0; return -1; } + RESULT audioTracks(ePtr<iAudioTrackSelection> &ptr); RESULT frontendInfo(ePtr<iFrontendInformation> &ptr) { ptr = 0; return -1; } RESULT subServices(ePtr<iSubserviceList> &ptr) { ptr = 0; return -1; } RESULT timeshift(ePtr<iTimeshiftService> &ptr) { ptr = 0; return -1; } @@ -89,8 +89,15 @@ public: void setCutList(SWIG_PYOBJECT(ePyObject)); void setCutListEnable(int enable); - // iServiceKeys + // iAudioTrackSelection + int getNumberOfTracks(); + RESULT selectTrack(unsigned int i); + RESULT getTrackInfo(struct iAudioTrackInfo &, unsigned int n); + int getCurrentTrack(); + + // iServiceKeys RESULT keyPressed(int key); + private: eServiceDVD(eServiceReference ref); @@ -127,6 +134,8 @@ private: void loadCuesheet(); void saveCuesheet(); + + int m_width, m_height, m_aspect, m_framerate, m_progressive; }; #endif diff --git a/lib/python/Plugins/Extensions/GraphMultiEPG/meta/Makefile.am b/lib/python/Plugins/Extensions/GraphMultiEPG/meta/Makefile.am index 5766ff34..a11d35d0 100755 --- a/lib/python/Plugins/Extensions/GraphMultiEPG/meta/Makefile.am +++ b/lib/python/Plugins/Extensions/GraphMultiEPG/meta/Makefile.am @@ -1,4 +1,4 @@ -installdir = $(datadir)/meta/ +installdir = $(datadir)/meta dist_install_DATA = plugin_graphmultiepg.xml diff --git a/lib/python/Plugins/Extensions/GraphMultiEPG/meta/plugin_graphmultiepg.xml b/lib/python/Plugins/Extensions/GraphMultiEPG/meta/plugin_graphmultiepg.xml index a10840da..d3a2edf8 100755 --- a/lib/python/Plugins/Extensions/GraphMultiEPG/meta/plugin_graphmultiepg.xml +++ b/lib/python/Plugins/Extensions/GraphMultiEPG/meta/plugin_graphmultiepg.xml @@ -3,23 +3,14 @@ <tag type="EPG" /> <tag type="Recording" /> </prerequisites> - <info language="en"> + <info> <author>Dream Multimedia</author> <name>GraphMultiEPG</name> - <packagename>eenigma2-plugin-extensions-graphmultiepg</packagename> - <shortdescription>GraphMultiEPG shows a graphical timeline EPG.</shortdescription> + <packagename>enigma2-plugin-extensions-graphmultiepg</packagename> + <shortdescription>GraphMultiEPG shows a graphical timeline EPG</shortdescription> <description>GraphMultiEPG shows a graphical timeline EPG.\nShows a nice overview of all running und upcoming tv shows.</description> <screenshot src="http://www.dreamboxupdate.com/preview/plugin_graphmultiepg_en.jpg" /> </info> - <info language="de"> - <author>Dream Multimedia</author> - <name>GraphMultiEPG</name> - <packagename>enigma2-plugin-extensions-graphmultiepg</packagename> - <shortdescription>Zeigt ein grafisches Zeitlinien-EPG.</shortdescription> - <description>Zeigt ein grafisches Zeitlinien-EPG.\nZeigt eine grafische Übersicht aller laufenden und kommenden Sendungen. - </description> - <screenshot src="http://www.dreamboxupdate.com/preview/plugin_graphmultiepg_de.jpg" /> - </info> <files type="package"> <!-- without version, without .ipk --> <file type="package" name="enigma2-plugin-extensions-graphmultiepg" /> </files> diff --git a/lib/python/Plugins/Extensions/GraphMultiEPG/plugin.py b/lib/python/Plugins/Extensions/GraphMultiEPG/plugin.py index 3635272b..af14578a 100644 --- a/lib/python/Plugins/Extensions/GraphMultiEPG/plugin.py +++ b/lib/python/Plugins/Extensions/GraphMultiEPG/plugin.py @@ -118,5 +118,5 @@ def main(session, servicelist, **kwargs): def Plugins(**kwargs): name = _("Graphical Multi EPG") descr = _("A graphical EPG for all services of an specific bouquet") - return [ PluginDescriptor(name=name, description=descr, where = PluginDescriptor.WHERE_EVENTINFO, fnc=main), - PluginDescriptor(name=name, description=descr, where = PluginDescriptor.WHERE_EXTENSIONSMENU, fnc=main) ] + return [PluginDescriptor(name=name, description=descr, where = PluginDescriptor.WHERE_EVENTINFO, needsRestart = False, fnc=main), + PluginDescriptor(name=name, description=descr, where = PluginDescriptor.WHERE_EXTENSIONSMENU, needsRestart = False, fnc=main)] diff --git a/lib/python/Plugins/Extensions/Makefile.am b/lib/python/Plugins/Extensions/Makefile.am index 5a6e3f3a..7a3a9be2 100755 --- a/lib/python/Plugins/Extensions/Makefile.am +++ b/lib/python/Plugins/Extensions/Makefile.am @@ -1,6 +1,10 @@ installdir = $(pkglibdir)/python/Plugins/Extensions -SUBDIRS = TuxboxPlugins CutListEditor PicturePlayer MediaScanner MediaPlayer GraphMultiEPG SocketMMI DVDPlayer DVDBurn Modem +SUBDIRS = TuxboxPlugins CutListEditor PicturePlayer MediaScanner MediaPlayer GraphMultiEPG SocketMMI DVDBurn Modem + +if HAVE_LIBDDVD +SUBDIRS += DVDPlayer +endif install_PYTHON = \ __init__.py diff --git a/lib/python/Plugins/Extensions/MediaPlayer/meta/Makefile.am b/lib/python/Plugins/Extensions/MediaPlayer/meta/Makefile.am index 84e01672..e17f0bf5 100755 --- a/lib/python/Plugins/Extensions/MediaPlayer/meta/Makefile.am +++ b/lib/python/Plugins/Extensions/MediaPlayer/meta/Makefile.am @@ -1,4 +1,4 @@ -installdir = $(datadir)/meta/ +installdir = $(datadir)/meta dist_install_DATA = plugin_mediaplayer.xml diff --git a/lib/python/Plugins/Extensions/MediaPlayer/meta/plugin_mediaplayer.xml b/lib/python/Plugins/Extensions/MediaPlayer/meta/plugin_mediaplayer.xml index 2f9f22bf..ffbb8e89 100755 --- a/lib/python/Plugins/Extensions/MediaPlayer/meta/plugin_mediaplayer.xml +++ b/lib/python/Plugins/Extensions/MediaPlayer/meta/plugin_mediaplayer.xml @@ -2,23 +2,14 @@ <prerequisites> <tag type="Multimedia" /> </prerequisites> - <info language="en"> + <info> <author>Dream Multimedia</author> <name>MediaPlayer</name> <packagename>enigma2-plugin-extensions-mediaplayer</packagename> - <shortdescription>Mediaplayer plays your favorite music and videos.</shortdescription> + <shortdescription>Plays your favorite music and videos</shortdescription> <description>Mediaplayer plays your favorite music and videos.\nPlay all your favorite music and video files, organize them in playlists, view cover and album information.</description> <screenshot src="http://www.dreamboxupdate.com/preview/plugin_mediaplayer_en.jpg" /> </info> - <info language="de"> - <author>Dream Multimedia</author> - <name>MediaPlayer</name> - <packagename>enigma2-plugin-extensions-mediaplayer</packagename> - <shortdescription>Mediaplayer spielt Ihre Musik und Videos.</shortdescription> - <description>Mediaplayer spielt Ihre Musik und Videos.\nSie können all Ihre Musik- und Videodateien abspielen, in Playlisten organisieren, Cover und Albuminformationen abrufen. - </description> - <screenshot src="http://www.dreamboxupdate.com/preview/plugin_mediaplayer_de.jpg" /> - </info> <files type="package"> <!-- without version, without .ipk --> <file type="package" name="enigma2-plugin-extensions-mediaplayer" /> </files> diff --git a/lib/python/Plugins/Extensions/MediaPlayer/plugin.py b/lib/python/Plugins/Extensions/MediaPlayer/plugin.py index 036bbbb9..6ff1c5a5 100755..100644 --- a/lib/python/Plugins/Extensions/MediaPlayer/plugin.py +++ b/lib/python/Plugins/Extensions/MediaPlayer/plugin.py @@ -110,7 +110,7 @@ class MediaPlayer(Screen, InfoBarBase, InfoBarSeek, InfoBarAudioSelection, InfoB # 'None' is magic to start at the list of mountpoints defaultDir = config.mediaplayer.defaultDir.getValue() - self.filelist = FileList(defaultDir, matchingPattern = "(?i)^.*\.(mp2|mp3|ogg|ts|wav|wave|m3u|pls|e2pls|mpg|vob|avi|divx|m4v|mkv|mp4|m4a|dat|flac|mov)", useServiceRef = True, additionalExtensions = "4098:m3u 4098:e2pls 4098:pls") + self.filelist = FileList(defaultDir, matchingPattern = "(?i)^.*\.(mp2|mp3|ogg|ts|wav|wave|m3u|pls|e2pls|mpg|vob|avi|divx|m4v|mkv|mp4|m4a|dat|flac|mov|m2ts)", useServiceRef = True, additionalExtensions = "4098:m3u 4098:e2pls 4098:pls") self["filelist"] = self.filelist self.playlist = MyPlayList() @@ -901,11 +901,11 @@ class MediaPlayer(Screen, InfoBarBase, InfoBarSeek, InfoBarAudioSelection, InfoB def unPauseService(self): self.setSeekState(self.SEEK_STATE_PLAY) - + def subtitleSelection(self): - from Screens.Subtitles import Subtitles - self.session.open(Subtitles, self) - + from Screens.AudioSelection import SubtitleSelection + self.session.open(SubtitleSelection, self) + def hotplugCB(self, dev, media_state): if dev == harddiskmanager.getCD(): if media_state == "1": @@ -925,12 +925,17 @@ class MediaPlayer(Screen, InfoBarBase, InfoBarSeek, InfoBarAudioSelection, InfoB self.clear_playlist() class MediaPlayerLCDScreen(Screen): - skin = """ - <screen position="0,0" size="132,64" title="LCD Text"> + skin = ( + """<screen name="MediaPlayerLCDScreen" position="0,0" size="132,64" id="1"> <widget name="text1" position="4,0" size="132,35" font="Regular;16"/> <widget name="text3" position="4,36" size="132,14" font="Regular;10"/> <widget name="text4" position="4,49" size="132,14" font="Regular;10"/> - </screen>""" + </screen>""", + """<screen name="MediaPlayerLCDScreen" position="0,0" size="96,64" id="2"> + <widget name="text1" position="0,0" size="96,35" font="Regular;14"/> + <widget name="text3" position="0,36" size="96,14" font="Regular;10"/> + <widget name="text4" position="0,49" size="96,14" font="Regular;10"/> + </screen>""") def __init__(self, session, parent): Screen.__init__(self, session) @@ -1036,6 +1041,6 @@ def filescan(**kwargs): from Plugins.Plugin import PluginDescriptor def Plugins(**kwargs): return [ - PluginDescriptor(name = "MediaPlayer", description = "Play back media files", where = PluginDescriptor.WHERE_MENU, fnc = menu), - PluginDescriptor(name = "MediaPlayer", where = PluginDescriptor.WHERE_FILESCAN, fnc = filescan) + PluginDescriptor(name = "MediaPlayer", description = "Play back media files", where = PluginDescriptor.WHERE_MENU, needsRestart = False, fnc = menu), + PluginDescriptor(name = "MediaPlayer", where = PluginDescriptor.WHERE_FILESCAN, needsRestart = False, fnc = filescan) ] diff --git a/lib/python/Plugins/Extensions/MediaPlayer/settings.py b/lib/python/Plugins/Extensions/MediaPlayer/settings.py index 0b95812f..7f42677d 100755 --- a/lib/python/Plugins/Extensions/MediaPlayer/settings.py +++ b/lib/python/Plugins/Extensions/MediaPlayer/settings.py @@ -12,6 +12,8 @@ config.mediaplayer.repeat = ConfigYesNo(default=False) config.mediaplayer.savePlaylistOnExit = ConfigYesNo(default=True) config.mediaplayer.saveDirOnExit = ConfigYesNo(default=False) config.mediaplayer.defaultDir = ConfigDirectory() +config.mediaplayer.useAlternateUserAgent = ConfigYesNo(default=False) +config.mediaplayer.alternateUserAgent = ConfigText(default="") class DirectoryBrowser(Screen, HelpableScreen): diff --git a/lib/python/Plugins/Extensions/MediaScanner/meta/Makefile.am b/lib/python/Plugins/Extensions/MediaScanner/meta/Makefile.am index d80b8c27..c4ab27d8 100755 --- a/lib/python/Plugins/Extensions/MediaScanner/meta/Makefile.am +++ b/lib/python/Plugins/Extensions/MediaScanner/meta/Makefile.am @@ -1,4 +1,4 @@ -installdir = $(datadir)/meta/ +installdir = $(datadir)/meta dist_install_DATA = plugin_mediascanner.xml diff --git a/lib/python/Plugins/Extensions/MediaScanner/meta/plugin_mediascanner.xml b/lib/python/Plugins/Extensions/MediaScanner/meta/plugin_mediascanner.xml index eced924f..eb9de1b6 100755 --- a/lib/python/Plugins/Extensions/MediaScanner/meta/plugin_mediascanner.xml +++ b/lib/python/Plugins/Extensions/MediaScanner/meta/plugin_mediascanner.xml @@ -1,25 +1,15 @@ <default> <prerequisites> - <tag type="Multimedia" /> <tag type="System" /> </prerequisites> - <info language="en"> + <info> <author>Dream Multimedia</author> <name>MediaScanner</name> <packagename>enigma2-plugin-extensions-mediascanner</packagename> - <shortdescription>MediaScanner scans devices for playable media files.</shortdescription> + <shortdescription>Scan devices for playable media files</shortdescription> <description>MediaScanner scans devices for playable media files and displays a menu with possible actions like viewing pictures or playing movies.</description> <screenshot src="http://www.dreamboxupdate.com/preview/plugin_mediascanner_en.jpg" /> </info> - <info language="de"> - <author>Dream Multimedia</author> - <name>MediaScanner</name> - <packagename>enigma2-plugin-extensions-mediascanner</packagename> - <shortdescription>MediaScanner durchsucht Geräte nach Mediendateien.</shortdescription> - <description>MediaScanner durchsucht Geräte nach Mediendateien und bietet Ihnen die dazu passenden Aktionen an wie z.B. Bilder betrachten oder Videos abspielen. - </description> - <screenshot src="http://www.dreamboxupdate.com/preview/plugin_mediascanner_en.jpg" /> - </info> <files type="package"> <!-- without version, without .ipk --> <file type="package" name="enigma2-plugin-extensions-mediascanner" /> </files> diff --git a/lib/python/Plugins/Extensions/MediaScanner/plugin.py b/lib/python/Plugins/Extensions/MediaScanner/plugin.py index 0cefa353..76bbb26a 100755..100644 --- a/lib/python/Plugins/Extensions/MediaScanner/plugin.py +++ b/lib/python/Plugins/Extensions/MediaScanner/plugin.py @@ -91,8 +91,8 @@ def autostart(reason, **kwargs): def Plugins(**kwargs): return [ - PluginDescriptor(name="MediaScanner", description=_("Scan Files..."), where = PluginDescriptor.WHERE_PLUGINMENU, fnc=main), + PluginDescriptor(name="MediaScanner", description=_("Scan Files..."), where = PluginDescriptor.WHERE_PLUGINMENU, needsRestart = True, fnc=main), # PluginDescriptor(where = PluginDescriptor.WHERE_MENU, fnc=menuHook), - PluginDescriptor(where = PluginDescriptor.WHERE_SESSIONSTART, fnc = sessionstart), - PluginDescriptor(where = PluginDescriptor.WHERE_AUTOSTART, fnc = autostart) + PluginDescriptor(where = PluginDescriptor.WHERE_SESSIONSTART, needsRestart = True, fnc = sessionstart), + PluginDescriptor(where = PluginDescriptor.WHERE_AUTOSTART, needsRestart = True, fnc = autostart) ] diff --git a/lib/python/Plugins/Extensions/Modem/plugin.py b/lib/python/Plugins/Extensions/Modem/plugin.py index e57e4f51..0b397c18 100644 --- a/lib/python/Plugins/Extensions/Modem/plugin.py +++ b/lib/python/Plugins/Extensions/Modem/plugin.py @@ -280,4 +280,4 @@ def main(session, **kwargs): session.open(ModemSetup) def Plugins(**kwargs): - return PluginDescriptor(name="Modem", description="plugin to connect to internet via builtin modem", where = PluginDescriptor.WHERE_PLUGINMENU, fnc=main) + return PluginDescriptor(name="Modem", description="plugin to connect to internet via builtin modem", where = PluginDescriptor.WHERE_PLUGINMENU, needsRestart = False, fnc=main) diff --git a/lib/python/Plugins/Extensions/PicturePlayer/meta/Makefile.am b/lib/python/Plugins/Extensions/PicturePlayer/meta/Makefile.am index 87b59f43..e501fb6f 100755 --- a/lib/python/Plugins/Extensions/PicturePlayer/meta/Makefile.am +++ b/lib/python/Plugins/Extensions/PicturePlayer/meta/Makefile.am @@ -1,4 +1,4 @@ -installdir = $(datadir)/meta/ +installdir = $(datadir)/meta dist_install_DATA = plugin_pictureplayer.xml diff --git a/lib/python/Plugins/Extensions/PicturePlayer/meta/plugin_pictureplayer.xml b/lib/python/Plugins/Extensions/PicturePlayer/meta/plugin_pictureplayer.xml index faff9785..16e2ec90 100755 --- a/lib/python/Plugins/Extensions/PicturePlayer/meta/plugin_pictureplayer.xml +++ b/lib/python/Plugins/Extensions/PicturePlayer/meta/plugin_pictureplayer.xml @@ -2,23 +2,14 @@ <prerequisites> <tag type="Multimedia" /> </prerequisites> - <info language="en"> + <info> <author>Dream Multimedia</author> <name>PicturePlayer</name> <packagename>enigma2-plugin-extensions-pictureplayer</packagename> - <shortdescription>PicturePlayer displays your photos on the TV.</shortdescription> + <shortdescription>Display your photos on the TV</shortdescription> <description>The PicturePlayer displays your photos on the TV.\nYou can view them as thumbnails or slideshow.</description> <screenshot src="http://www.dreamboxupdate.com/preview/plugin_pictureplayer.jpg" /> </info> - <info language="de"> - <author>Dream Multimedia</author> - <name>Bildbetrachter</name> - <packagename>enigma2-plugin-extensions-pictureplayer</packagename> - <shortdescription>Der Bildbetrachter zeigt Ihre Bilder auf dem Fernseher an.</shortdescription> - <description>Der Bildbetrachter zeigt Ihre Bilder auf dem Fernseher an.\nSie können sich Ihre Bilder als Thumbnails, einzeln oder als Slideshow anzeigen lassen. - </description> - <screenshot src="http://www.dreamboxupdate.com/preview/plugin_pictureplayer.jpg" /> - </info> <files type="package"> <!-- without version, without .ipk --> <file type="package" name="enigma2-plugin-extensions-pictureplayer" /> </files> diff --git a/lib/python/Plugins/Extensions/PicturePlayer/plugin.py b/lib/python/Plugins/Extensions/PicturePlayer/plugin.py index 5d1c2cba..169a8c8a 100755..100644 --- a/lib/python/Plugins/Extensions/PicturePlayer/plugin.py +++ b/lib/python/Plugins/Extensions/PicturePlayer/plugin.py @@ -625,5 +625,5 @@ def filescan(**kwargs): def Plugins(**kwargs): return \ - [PluginDescriptor(name=_("PicturePlayer"), description=_("fileformats (BMP, PNG, JPG, GIF)"), icon="pictureplayer.png", where = PluginDescriptor.WHERE_PLUGINMENU, fnc=main), - PluginDescriptor(name=_("PicturePlayer"), where = PluginDescriptor.WHERE_FILESCAN, fnc = filescan)] + [PluginDescriptor(name=_("PicturePlayer"), description=_("fileformats (BMP, PNG, JPG, GIF)"), icon="pictureplayer.png", where = PluginDescriptor.WHERE_PLUGINMENU, needsRestart = False, fnc=main), + PluginDescriptor(name=_("PicturePlayer"), where = PluginDescriptor.WHERE_FILESCAN, needsRestart = False, fnc = filescan)] diff --git a/lib/python/Plugins/Extensions/SocketMMI/Makefile.am b/lib/python/Plugins/Extensions/SocketMMI/Makefile.am index 808d8520..2af2c39b 100644 --- a/lib/python/Plugins/Extensions/SocketMMI/Makefile.am +++ b/lib/python/Plugins/Extensions/SocketMMI/Makefile.am @@ -3,7 +3,6 @@ SUBDIRS = src meta installdir = $(pkglibdir)/python/Plugins/Extensions/SocketMMI install_PYTHON = \ - src/socketmmi.so \ __init__.py \ plugin.py \ SocketMMI.py diff --git a/lib/python/Plugins/Extensions/SocketMMI/meta/Makefile.am b/lib/python/Plugins/Extensions/SocketMMI/meta/Makefile.am index 34ccbeb7..67388bc4 100755 --- a/lib/python/Plugins/Extensions/SocketMMI/meta/Makefile.am +++ b/lib/python/Plugins/Extensions/SocketMMI/meta/Makefile.am @@ -1,3 +1,3 @@ -installdir = $(datadir)/meta/ +installdir = $(datadir)/meta dist_install_DATA = plugin_socketmmi.xml diff --git a/lib/python/Plugins/Extensions/SocketMMI/meta/plugin_socketmmi.xml b/lib/python/Plugins/Extensions/SocketMMI/meta/plugin_socketmmi.xml index acf8374d..3eaf8fc5 100644..100755 --- a/lib/python/Plugins/Extensions/SocketMMI/meta/plugin_socketmmi.xml +++ b/lib/python/Plugins/Extensions/SocketMMI/meta/plugin_socketmmi.xml @@ -2,20 +2,13 @@ <prerequisites> <tag type="System" /> </prerequisites> - <info language="en"> + <info> <author>Dream Multimedia</author> <name>SocketMMI</name> <packagename>enigma2-plugin-extensions-socketmmi</packagename> - <shortdescription>Python frontend for /tmp/mmi.socket.</shortdescription> + <shortdescription>Frontend for /tmp/mmi.socket</shortdescription> <description>Python frontend for /tmp/mmi.socket.</description> </info> - <info language="de"> - <author>Dream Multimedia</author> - <name>SocketMMI</name> - <packagename>enigma2-plugin-extensions-socketmmi</packagename> - <shortdescription>Python frontend für /tmp/mmi.socket.</shortdescription> - <description>Python frontend für /tmp/mmi.socket.</description> - </info> <files type="package"> <!-- without version, without .ipk --> <file type="package" name="enigma2-plugin-extensions-socketmmi" /> </files> diff --git a/lib/python/Plugins/Extensions/SocketMMI/plugin.py b/lib/python/Plugins/Extensions/SocketMMI/plugin.py index 387c8306..568cde2a 100644 --- a/lib/python/Plugins/Extensions/SocketMMI/plugin.py +++ b/lib/python/Plugins/Extensions/SocketMMI/plugin.py @@ -22,6 +22,7 @@ def autostart(reason, **kwargs): socketHandler = SocketMMIMessageHandler() def Plugins(**kwargs): - return [ PluginDescriptor(name = "SocketMMI", description = _("Python frontend for /tmp/mmi.socket"), where = PluginDescriptor.WHERE_MENU, fnc = menu), - PluginDescriptor(where = PluginDescriptor.WHERE_SESSIONSTART, fnc = sessionstart), - PluginDescriptor(where = PluginDescriptor.WHERE_AUTOSTART, fnc = autostart) ] + return [ PluginDescriptor(name = "SocketMMI", description = _("Python frontend for /tmp/mmi.socket"), where = PluginDescriptor.WHERE_MENU, needsRestart = True, fnc = menu), + PluginDescriptor(where = PluginDescriptor.WHERE_SESSIONSTART, needsRestart = True, fnc = sessionstart), + PluginDescriptor(where = PluginDescriptor.WHERE_AUTOSTART, needsRestart = True, fnc = autostart) ] + diff --git a/lib/python/Plugins/Extensions/SocketMMI/src/Makefile.am b/lib/python/Plugins/Extensions/SocketMMI/src/Makefile.am index ad97d672..5ce7d031 100644 --- a/lib/python/Plugins/Extensions/SocketMMI/src/Makefile.am +++ b/lib/python/Plugins/Extensions/SocketMMI/src/Makefile.am @@ -1,12 +1,15 @@ -OBJS = socket_mmi.cpp +AM_CPPFLAGS = \ + -I$(top_srcdir) \ + -I$(top_srcdir)/include \ + -include Python.h \ + -include $(top_builddir)/enigma2_config.h --include $(OBJS:.cpp=.d) +plugindir = $(pkglibdir)/python/Plugins/Extensions/SocketMMI -socketmmi.so: socket_mmi.cpp socket_mmi.h - $(CXX) $(CPPFLAGS) -MD $(CXXFLAGS) $(DEFS) -I$(top_srcdir)/include \ - -Wall -W $(OBJS) -shared -fPIC -Wl,-soname,socketmmi.so -o socketmmi.so \ - $(LDFLAGS) +plugin_LTLIBRARIES = socketmmi.la -all: socketmmi.so +socketmmi_la_SOURCES = \ + socket_mmi.cpp \ + socket_mmi.h -CLEANFILES = socketmmi.so socketmmi.d +socketmmi_la_LDFLAGS = -avoid-version -module diff --git a/lib/python/Plugins/Extensions/SocketMMI/src/socket_mmi.cpp b/lib/python/Plugins/Extensions/SocketMMI/src/socket_mmi.cpp index 673b525c..9a69de37 100644 --- a/lib/python/Plugins/Extensions/SocketMMI/src/socket_mmi.cpp +++ b/lib/python/Plugins/Extensions/SocketMMI/src/socket_mmi.cpp @@ -118,11 +118,11 @@ eAutoInitP0<eSocket_UI> init_socketui(eAutoInitNumbers::rc, "Socket MMI"); int eSocketMMIHandler::send_to_mmisock( void* buf, size_t len) { - int ret = write(connfd, buf, len); + ssize_t ret = write(connfd, buf, len); if ( ret < 0 ) eDebug("[eSocketMMIHandler] write (%m)"); - else if ( (uint)ret != len ) - eDebug("[eSocketMMIHandler] only %d bytes sent.. %d bytes should be sent", ret, len ); + else if ( (size_t)ret != len ) + eDebug("[eSocketMMIHandler] only %zd bytes sent.. %zu bytes should be sent", ret, len ); else return 0; return ret; diff --git a/lib/python/Plugins/Extensions/TuxboxPlugins/meta/Makefile.am b/lib/python/Plugins/Extensions/TuxboxPlugins/meta/Makefile.am index ecdcac8a..1ab8aeea 100755 --- a/lib/python/Plugins/Extensions/TuxboxPlugins/meta/Makefile.am +++ b/lib/python/Plugins/Extensions/TuxboxPlugins/meta/Makefile.am @@ -1,3 +1,3 @@ -installdir = $(datadir)/meta/ +installdir = $(datadir)/meta dist_install_DATA = plugin_tuxboxplugins.xml diff --git a/lib/python/Plugins/Extensions/TuxboxPlugins/meta/plugin_tuxboxplugins.xml b/lib/python/Plugins/Extensions/TuxboxPlugins/meta/plugin_tuxboxplugins.xml index 734c48f1..7ca10826 100644..100755 --- a/lib/python/Plugins/Extensions/TuxboxPlugins/meta/plugin_tuxboxplugins.xml +++ b/lib/python/Plugins/Extensions/TuxboxPlugins/meta/plugin_tuxboxplugins.xml @@ -2,20 +2,13 @@ <prerequisites> <tag type="System" /> </prerequisites> - <info language="en"> + <info> <author>Dream Multimedia</author> <name>TuxboxPlugins</name> <packagename>TuxboxPlugins</packagename> - <shortdescription>Allows the execution of TuxboxPlugins.</shortdescription> + <shortdescription>Execute TuxboxPlugins</shortdescription> <description>Allows the execution of TuxboxPlugins.</description> </info> - <info language="de"> - <author>Dream Multimedia</author> - <name>TuxboxPlugins</name> - <packagename>enigma2-plugin-extensions-tuxboxplugins</packagename> - <shortdescription>Erlaubt das Ausführen von TuxboxPlugins.</shortdescription> - <description>Erlaubt das Ausführen von TuxboxPlugins.</description> - </info> <files type="package"> <!-- without version, without .ipk --> <file type="package" name="enigma2-plugin-extensions-tuxboxplugins" /> </files> diff --git a/lib/python/Plugins/Extensions/TuxboxPlugins/plugin.py b/lib/python/Plugins/Extensions/TuxboxPlugins/plugin.py index 05085ead..e124ffd2 100644 --- a/lib/python/Plugins/Extensions/TuxboxPlugins/plugin.py +++ b/lib/python/Plugins/Extensions/TuxboxPlugins/plugin.py @@ -17,7 +17,7 @@ def getPlugins(): for x in dir: if x[-3:] == "cfg": params = getPluginParams(x) - pluginlist.append(PluginDescriptor(name=params["name"], description=params["desc"], where = PluginDescriptor.WHERE_PLUGINMENU, icon="tuxbox.png", fnc=boundFunction(main, plugin=x))) + pluginlist.append(PluginDescriptor(name=params["name"], description=params["desc"], where = PluginDescriptor.WHERE_PLUGINMENU, icon="tuxbox.png", needsRestart = True, fnc=boundFunction(main, plugin=x))) return pluginlist diff --git a/lib/python/Plugins/Makefile.am b/lib/python/Plugins/Makefile.am index f0435036..79d2b0b6 100644 --- a/lib/python/Plugins/Makefile.am +++ b/lib/python/Plugins/Makefile.am @@ -4,3 +4,4 @@ SUBDIRS = Extensions SystemPlugins DemoPlugins install_PYTHON = \ __init__.py Plugin.py + diff --git a/lib/python/Plugins/Plugin.py b/lib/python/Plugins/Plugin.py index 5a676cda..d87e6e96 100755 --- a/lib/python/Plugins/Plugin.py +++ b/lib/python/Plugins/Plugin.py @@ -61,9 +61,11 @@ class PluginDescriptor: WHERE_SOFTWAREMANAGER = 14 - def __init__(self, name = "Plugin", where = [ ], description = "", icon = None, fnc = None, wakeupfnc = None, internal = False): + def __init__(self, name = "Plugin", where = [ ], description = "", icon = None, fnc = None, wakeupfnc = None, needsRestart = None, internal = False, weight = 0): self.name = name self.internal = internal + self.needsRestart = needsRestart + self.path = None if isinstance(where, list): self.where = where else: @@ -76,6 +78,8 @@ class PluginDescriptor: else: self.icon = icon + self.weight = weight + self.wakeupfnc = wakeupfnc self.__call__ = fnc diff --git a/lib/python/Plugins/SystemPlugins/CleanupWizard/CleanupWizard.py b/lib/python/Plugins/SystemPlugins/CleanupWizard/CleanupWizard.py index d8de3544..797010c6 100755 --- a/lib/python/Plugins/SystemPlugins/CleanupWizard/CleanupWizard.py +++ b/lib/python/Plugins/SystemPlugins/CleanupWizard/CleanupWizard.py @@ -88,7 +88,7 @@ class CleanupWizard(WizardLanguage, Rc): if self.NextStep is not 'end': if not self.Console: self.Console = Console() - cmd = "ipkg list_installed | grep enigma2" + cmd = "opkg list_installed | grep enigma2" self.Console.ePopen(cmd, self.buildListInstalled_Finished) self.buildListRef = self.session.openWithCallback(self.buildListfinishedCB, MessageBox, _("Please wait while searching for removable packages..."), type = MessageBox.TYPE_INFO, enable_input = False) else: diff --git a/lib/python/Plugins/SystemPlugins/CleanupWizard/meta/Makefile.am b/lib/python/Plugins/SystemPlugins/CleanupWizard/meta/Makefile.am index d9d96bcf..8fc21e7f 100755 --- a/lib/python/Plugins/SystemPlugins/CleanupWizard/meta/Makefile.am +++ b/lib/python/Plugins/SystemPlugins/CleanupWizard/meta/Makefile.am @@ -1,4 +1,4 @@ -installdir = $(datadir)/meta/ +installdir = $(datadir)/meta dist_install_DATA = plugin_cleanupwizard.xml diff --git a/lib/python/Plugins/SystemPlugins/CleanupWizard/meta/plugin_cleanupwizard.xml b/lib/python/Plugins/SystemPlugins/CleanupWizard/meta/plugin_cleanupwizard.xml index 99add3d3..d0781af4 100755 --- a/lib/python/Plugins/SystemPlugins/CleanupWizard/meta/plugin_cleanupwizard.xml +++ b/lib/python/Plugins/SystemPlugins/CleanupWizard/meta/plugin_cleanupwizard.xml @@ -2,26 +2,16 @@ <prerequisites> <tag type="System" /> </prerequisites> - <info language="en"> + <info> <author>Dream Multimedia</author> <name>CleanupWizard</name> <packagename>enigma2-plugin-systemplugins-cleanupwizard</packagename> <shortdescription>Automatically informs you on low internal memory</shortdescription> - <description>The CleanupWizard informs you when your internal free memory of your dreambox has droppen under 2MB. - You can use this wizard to remove some extensions. - </description> + <description>The CleanupWizard informs you when the internal free memory of your dreambox has dropped below a definable threshold. + You can use this wizard to remove some plugins. </description> <screenshot src="http://www.dreamboxupdate.com/preview/plugin_cleanupwizard_en.jpg" /> </info> - <info language="de"> - <author>Dream Multimedia</author> - <name>CleanupWizard</name> - <packagename>enigma2-plugin-systemplugins-cleanupwizard</packagename> - <shortdescription>Informiert Sie automatisch wenn der interne Speicher Ihrer Dreambox voll wird.</shortdescription> - <description>Der CleanupWizard informiert Sie, wenn der interne freie Speicher Ihrer Dreambox unter 2MB fällt. - Sie können dann einige Erweiterungen deinstallieren um wieder Platz zu schaffen. - </description> - <screenshot src="http://www.dreamboxupdate.com/preview/plugin_cleanupwizard_de.jpg" /> - </info> + <files type="package"> <!-- without version, without .ipk --> <file type="package" name="enigma2-plugin-systemplugins-cleanupwizard" /> </files> diff --git a/lib/python/Plugins/SystemPlugins/CleanupWizard/plugin.py b/lib/python/Plugins/SystemPlugins/CleanupWizard/plugin.py index f8677bb2..157aa759 100755..100644 --- a/lib/python/Plugins/SystemPlugins/CleanupWizard/plugin.py +++ b/lib/python/Plugins/SystemPlugins/CleanupWizard/plugin.py @@ -126,10 +126,10 @@ def selSetup(menuid, **kwargs): def Plugins(**kwargs): list = [] - list.append(PluginDescriptor(name=_("CleanupWizard"), description=_("Cleanup Wizard settings"),where=PluginDescriptor.WHERE_MENU, fnc=selSetup)) + list.append(PluginDescriptor(name=_("CleanupWizard"), description=_("Cleanup Wizard settings"),where=PluginDescriptor.WHERE_MENU, needsRestart = False, fnc=selSetup)) if config.plugins.cleanupwizard.enable.value: if not config.misc.firstrun.value: if internalMemoryExceeded: - list.append(PluginDescriptor(name=_("Cleanup Wizard"), where = PluginDescriptor.WHERE_WIZARD, fnc=(1, CleanupWizard))) + list.append(PluginDescriptor(name=_("Cleanup Wizard"), where = PluginDescriptor.WHERE_WIZARD, needsRestart = False, fnc=(1, CleanupWizard))) return list diff --git a/lib/python/Plugins/SystemPlugins/CommonInterfaceAssignment/meta/Makefile.am b/lib/python/Plugins/SystemPlugins/CommonInterfaceAssignment/meta/Makefile.am index 60d1d461..e0ce313e 100755 --- a/lib/python/Plugins/SystemPlugins/CommonInterfaceAssignment/meta/Makefile.am +++ b/lib/python/Plugins/SystemPlugins/CommonInterfaceAssignment/meta/Makefile.am @@ -1,4 +1,4 @@ -installdir = $(datadir)/meta/ +installdir = $(datadir)/meta dist_install_DATA = plugin_commoninterfaceassignment.xml diff --git a/lib/python/Plugins/SystemPlugins/CommonInterfaceAssignment/meta/plugin_commoninterfaceassignment.xml b/lib/python/Plugins/SystemPlugins/CommonInterfaceAssignment/meta/plugin_commoninterfaceassignment.xml index 9abc5986..f34f0a3c 100755 --- a/lib/python/Plugins/SystemPlugins/CommonInterfaceAssignment/meta/plugin_commoninterfaceassignment.xml +++ b/lib/python/Plugins/SystemPlugins/CommonInterfaceAssignment/meta/plugin_commoninterfaceassignment.xml @@ -4,28 +4,17 @@ <tag type="CI" /> <tag type="System" /> </prerequisites> - <info language="en"> + <info> <author>Dream Multimedia</author> <name>CommonInterfaceAssignment</name> <packagename>enigma2-plugin-systemplugins-commoninterfaceassignment</packagename> - <shortdescription>Assigning providers/services/caids to a dedicated CI module</shortdescription> - <description>With the CommonInterfaceAssignment extension it is possible to use different CI modules - in your Dreambox and assign to each of them dedicated providers/services or caids.\n - So it is then possible to watch a scrambled service while recording another one. - </description> - <screenshot src="http://www.dreamboxupdate.com/preview/plugin_ciassignment.jpg" /> - </info> - <info language="de"> - <author>Dream Multimedia</author> - <name>CommonInterfaceAssignment</name> - <packagename>enigma2-plugin-systemplugins-commoninterfaceassignment</packagename> - <shortdescription>Zuweisen von Providern/Services/CAIDs an ein CI Modul</shortdescription> - <description>Mit der CommonInterfaceAssignment Erweiterung ist es möglich jedem CI Modul bestimmte Provider/Services/CAIDs zuzuweisen.\n - So ist es möglich mit einem CI einen Sender aufzunehmen\n - und mit einem anderen einen Sender zu schauen. - </description> + <shortdescription>Assigning providers/services/caids to a CI module</shortdescription> + <description>With the CommonInterfaceAssignment plugin it is possible to use different + CI modules in your Dreambox and assign dedicated providers/services or caids to each of them.\n + This allows watching a scrambled service while recording another one.</description> <screenshot src="http://www.dreamboxupdate.com/preview/plugin_ciassignment.jpg" /> </info> + <files type="package"> <!-- without version, without .ipk --> <file type="package" name="enigma2-plugin-systemplugins-commoninterfaceassignment" /> </files> diff --git a/lib/python/Plugins/SystemPlugins/CommonInterfaceAssignment/plugin.py b/lib/python/Plugins/SystemPlugins/CommonInterfaceAssignment/plugin.py index 2c0edf53..b3454283 100755..100644 --- a/lib/python/Plugins/SystemPlugins/CommonInterfaceAssignment/plugin.py +++ b/lib/python/Plugins/SystemPlugins/CommonInterfaceAssignment/plugin.py @@ -12,7 +12,7 @@ from ServiceReference import ServiceReference from Plugins.Plugin import PluginDescriptor from xml.etree.cElementTree import parse as ci_parse from Tools.XMLTools import elementsWithTag, mergeText, stringToXML -from enigma import eDVBCI_UI, eDVBCIInterfaces +from enigma import eDVBCI_UI, eDVBCIInterfaces, eEnv from os import system, path as os_path @@ -115,7 +115,7 @@ class CIconfigMenu(Screen): Screen.__init__(self, session) self.ci_slot=ci_slot - self.filename="/etc/enigma2/ci"+str(self.ci_slot)+".xml" + self.filename = eEnv.resolve("${sysconfdir}/enigma2/ci") + str(self.ci_slot) + ".xml" self["key_red"] = StaticText(_("Delete")) self["key_green"] = StaticText(_("add Service")) @@ -567,7 +567,7 @@ def activate_all(session): return Len > 0 and definitions[Len-1].text or default for ci in range(NUM_CI): - filename="/etc/enigma2/ci"+str(ci)+".xml" + filename = eEnv.resolve("${sysconfdir}/enigma2/ci") + str(ci) + ".xml" if not os_path.exists(filename): print "[CI_Activate_Config_CI%d] no config file found" %ci @@ -636,10 +636,10 @@ def menu(menuid, **kwargs): def Plugins(**kwargs): if config.usage.setup_level.index > 1: - return [PluginDescriptor( where = PluginDescriptor.WHERE_SESSIONSTART, fnc = sessionstart ), - PluginDescriptor( where = PluginDescriptor.WHERE_AUTOSTART, fnc = autostart ), - PluginDescriptor( name = "CommonInterfaceAssignment", description = _("a gui to assign services/providers/caids to common interface modules"), where = PluginDescriptor.WHERE_MENU, fnc = menu )] + return [PluginDescriptor( where = PluginDescriptor.WHERE_SESSIONSTART, needsRestart = False, fnc = sessionstart ), + PluginDescriptor( where = PluginDescriptor.WHERE_AUTOSTART, needsRestart = False, fnc = autostart ), + PluginDescriptor( name = "CommonInterfaceAssignment", description = _("a gui to assign services/providers/caids to common interface modules"), where = PluginDescriptor.WHERE_MENU, needsRestart = False, fnc = menu )] else: - return [PluginDescriptor( where = PluginDescriptor.WHERE_SESSIONSTART, fnc = sessionstart ), - PluginDescriptor( where = PluginDescriptor.WHERE_AUTOSTART, fnc = autostart ), - PluginDescriptor( name = "CommonInterfaceAssignment", description = _("a gui to assign services/providers to common interface modules"), where = PluginDescriptor.WHERE_MENU, fnc = menu )] + return [PluginDescriptor( where = PluginDescriptor.WHERE_SESSIONSTART, needsRestart = False, fnc = sessionstart ), + PluginDescriptor( where = PluginDescriptor.WHERE_AUTOSTART, needsRestart = False, fnc = autostart ), + PluginDescriptor( name = "CommonInterfaceAssignment", description = _("a gui to assign services/providers to common interface modules"), where = PluginDescriptor.WHERE_MENU, needsRestart = False, fnc = menu )] diff --git a/lib/python/Plugins/SystemPlugins/CrashlogAutoSubmit/__init__.py b/lib/python/Plugins/SystemPlugins/CrashlogAutoSubmit/__init__.py index e69de29b..7933e4cd 100755 --- a/lib/python/Plugins/SystemPlugins/CrashlogAutoSubmit/__init__.py +++ b/lib/python/Plugins/SystemPlugins/CrashlogAutoSubmit/__init__.py @@ -0,0 +1,40 @@ +import sha + +def bin2long(s): + return reduce( lambda x,y:(x<<8L)+y, map(ord, s)) + +def long2bin(l): + res = "" + for byte in range(128): + res += chr((l >> (1024 - (byte + 1) * 8)) & 0xff) + return res + +def rsa_pub1024(src, mod): + return long2bin(pow(bin2long(src), 65537, bin2long(mod))) + +def decrypt_block(src, mod): + if len(src) != 128 and len(src) != 202: + return None + dest = rsa_pub1024(src[:128], mod) + hash = sha.new(dest[1:107]) + if len(src) == 202: + hash.update(src[131:192]) + result = hash.digest() + if result == dest[107:127]: + return dest + return None + +def validate_cert(cert, key): + buf = decrypt_block(cert[8:], key) + if buf is None: + return None + return buf[36:107] + cert[139:196] + +def read_random(): + try: + fd = open("/dev/urandom", "r") + buf = fd.read(8) + fd.close() + return buf + except: + return None
\ No newline at end of file diff --git a/lib/python/Plugins/SystemPlugins/CrashlogAutoSubmit/meta/Makefile.am b/lib/python/Plugins/SystemPlugins/CrashlogAutoSubmit/meta/Makefile.am index aed728db..02655d5d 100755 --- a/lib/python/Plugins/SystemPlugins/CrashlogAutoSubmit/meta/Makefile.am +++ b/lib/python/Plugins/SystemPlugins/CrashlogAutoSubmit/meta/Makefile.am @@ -1,4 +1,4 @@ -installdir = $(datadir)/meta/ +installdir = $(datadir)/meta dist_install_DATA = plugin_crashlogautosubmit.xml diff --git a/lib/python/Plugins/SystemPlugins/CrashlogAutoSubmit/meta/plugin_crashlogautosubmit.xml b/lib/python/Plugins/SystemPlugins/CrashlogAutoSubmit/meta/plugin_crashlogautosubmit.xml index a118ed7f..3140b15f 100755 --- a/lib/python/Plugins/SystemPlugins/CrashlogAutoSubmit/meta/plugin_crashlogautosubmit.xml +++ b/lib/python/Plugins/SystemPlugins/CrashlogAutoSubmit/meta/plugin_crashlogautosubmit.xml @@ -2,26 +2,17 @@ <prerequisites> <tag type="System" /> </prerequisites> - <info language="en"> + <info> <author>Dream Multimedia</author> <name>CrashlogAutoSubmit</name> <packagename>enigma2-plugin-systemplugins-crashlogautosubmit</packagename> <shortdescription>Automatically send crashlogs to Dream Multimedia</shortdescription> - <description>With the CrashlogAutoSubmit extension it is possible to automatically send crashlogs - found on your Harddrive to Dream Multimedia + <description>With the CrashlogAutoSubmit plugin it is possible to automatically + mail crashlogs found on your hard drive to Dream Multimedia. </description> <screenshot src="http://www.dreamboxupdate.com/preview/plugin_crashlogautosubmit_en.jpg" /> </info> - <info language="de"> - <author>Dream Multimedia</author> - <name>CrashlogAutoSubmit</name> - <packagename>enigma2-plugin-systemplugins-crashlogautosubmit</packagename> - <shortdescription>Automatisches versenden von Crashlogs an Dream Multimedia</shortdescription> - <description>Mit dem CrashlogAutoSubmit Plugin ist es möglich auf Ihrer Festplatte - gefundene Crashlogs automatisch an Dream Multimedia zu versenden. - </description> - <screenshot src="http://www.dreamboxupdate.com/preview/plugin_crashlogautosubmit_de.jpg" /> - </info> + <files type="package"> <!-- without version, without .ipk --> <file type="package" name="enigma2-plugin-systemplugins-crashlogautosubmit" /> </files> diff --git a/lib/python/Plugins/SystemPlugins/CrashlogAutoSubmit/plugin.py b/lib/python/Plugins/SystemPlugins/CrashlogAutoSubmit/plugin.py index 25639818..ab74de43 100755..100644 --- a/lib/python/Plugins/SystemPlugins/CrashlogAutoSubmit/plugin.py +++ b/lib/python/Plugins/SystemPlugins/CrashlogAutoSubmit/plugin.py @@ -8,12 +8,13 @@ from Screens.Screen import Screen from Screens.VirtualKeyBoard import VirtualKeyBoard from Screens.ChoiceBox import ChoiceBox from Screens.MessageBox import MessageBox -from enigma import ePoint +from enigma import ePoint, eTPM from Tools import Notifications import os from twisted.mail import smtp, relaymanager import MimeWriter, mimetools, StringIO +from __init__ import bin2long, long2bin, rsa_pub1024, decrypt_block, validate_cert, read_random config.plugins.crashlogautosubmit = ConfigSubsection() config.plugins.crashlogautosubmit.sendmail = ConfigSelection(default = "send", choices = [ @@ -376,11 +377,36 @@ def callCrashMailer(result,session): def autostart(reason, **kwargs): print "[CrashlogAutoSubmit] - autostart" - if "session" in kwargs: - try: - startMailer(kwargs["session"]) - except ImportError, e: - print "[CrashlogAutoSubmit] Twisted-mail not available, not starting CrashlogAutoSubmitter", e + try: + device = open("/proc/stb/info/model", "r").readline().strip() + except: + device = "" + if device != "dm7025": + rootkey = ['\x9f', '|', '\xe4', 'G', '\xc9', '\xb4', '\xf4', '#', '&', '\xce', '\xb3', '\xfe', '\xda', '\xc9', 'U', '`', '\xd8', '\x8c', 's', 'o', '\x90', '\x9b', '\\', 'b', '\xc0', '\x89', '\xd1', '\x8c', '\x9e', 'J', 'T', '\xc5', 'X', '\xa1', '\xb8', '\x13', '5', 'E', '\x02', '\xc9', '\xb2', '\xe6', 't', '\x89', '\xde', '\xcd', '\x9d', '\x11', '\xdd', '\xc7', '\xf4', '\xe4', '\xe4', '\xbc', '\xdb', '\x9c', '\xea', '}', '\xad', '\xda', 't', 'r', '\x9b', '\xdc', '\xbc', '\x18', '3', '\xe7', '\xaf', '|', '\xae', '\x0c', '\xe3', '\xb5', '\x84', '\x8d', '\r', '\x8d', '\x9d', '2', '\xd0', '\xce', '\xd5', 'q', '\t', '\x84', 'c', '\xa8', ')', '\x99', '\xdc', '<', '"', 'x', '\xe8', '\x87', '\x8f', '\x02', ';', 'S', 'm', '\xd5', '\xf0', '\xa3', '_', '\xb7', 'T', '\t', '\xde', '\xa7', '\xf1', '\xc9', '\xae', '\x8a', '\xd7', '\xd2', '\xcf', '\xb2', '.', '\x13', '\xfb', '\xac', 'j', '\xdf', '\xb1', '\x1d', ':', '?'] + etpm = eTPM() + l2cert = etpm.getCert(eTPM.TPMD_DT_LEVEL2_CERT) + if l2cert is None: + return + l2key = validate_cert(l2cert, rootkey) + if l2key is None: + return + l3cert = etpm.getCert(eTPM.TPMD_DT_LEVEL3_CERT) + if l3cert is None: + return + l3key = validate_cert(l3cert, l2key) + if l3key is None: + return + rnd = read_random() + if rnd is None: + return + val = etpm.challenge(rnd) + result = decrypt_block(val, l3key) + if device == "dm7025" or result[80:88] == rnd: + if "session" in kwargs: + try: + startMailer(kwargs["session"]) + except ImportError, e: + print "[CrashlogAutoSubmit] Twisted-mail not available, not starting CrashlogAutoSubmitter", e def openconfig(session, **kwargs): @@ -395,6 +421,6 @@ def selSetup(menuid, **kwargs): def Plugins(**kwargs): - return [PluginDescriptor(where = [PluginDescriptor.WHERE_SESSIONSTART, PluginDescriptor.WHERE_AUTOSTART], fnc = autostart), - PluginDescriptor(name=_("CrashlogAutoSubmit"), description=_("CrashlogAutoSubmit settings"),where=PluginDescriptor.WHERE_MENU, fnc=selSetup)] + return [PluginDescriptor(where = [PluginDescriptor.WHERE_SESSIONSTART, PluginDescriptor.WHERE_AUTOSTART], needsRestart = False, fnc = autostart), + PluginDescriptor(name=_("CrashlogAutoSubmit"), description=_("CrashlogAutoSubmit settings"),where=PluginDescriptor.WHERE_MENU, needsRestart = False, fnc=selSetup)] diff --git a/lib/python/Plugins/SystemPlugins/DefaultServicesScanner/meta/Makefile.am b/lib/python/Plugins/SystemPlugins/DefaultServicesScanner/meta/Makefile.am index 07dc1378..5e2d194c 100755 --- a/lib/python/Plugins/SystemPlugins/DefaultServicesScanner/meta/Makefile.am +++ b/lib/python/Plugins/SystemPlugins/DefaultServicesScanner/meta/Makefile.am @@ -1,4 +1,4 @@ -installdir = $(datadir)/meta/ +installdir = $(datadir)/meta dist_install_DATA = plugin_defaultservicesscanner.xml diff --git a/lib/python/Plugins/SystemPlugins/DefaultServicesScanner/meta/plugin_defaultservicesscanner.xml b/lib/python/Plugins/SystemPlugins/DefaultServicesScanner/meta/plugin_defaultservicesscanner.xml index 41d41ed6..74b7886f 100755 --- a/lib/python/Plugins/SystemPlugins/DefaultServicesScanner/meta/plugin_defaultservicesscanner.xml +++ b/lib/python/Plugins/SystemPlugins/DefaultServicesScanner/meta/plugin_defaultservicesscanner.xml @@ -1,26 +1,18 @@ <default> <prerequisites> <tag type="SAT" /> - <tag type="System" /> + <tag type="Default" /> </prerequisites> <info language="en"> <author>Dream Multimedia</author> <name>DefaultServicesScanner</name> <packagename>enigma2-plugin-systemplugins-defaultservicesscanner</packagename> - <shortdescription>Scans default lamedbs sorted by satellite with a connected dish positioner.</shortdescription> - <description>With the DefaultServicesScanner extension you can scan default lamedbs sorted by satellite with a connected dish positioner. - </description> - <screenshot src="http://www.dreamboxupdate.com/preview/plugin_defaultservicescanner.jpg" /> - </info> - <info language="de"> - <author>Dream Multimedia</author> - <name>DefaultServicesScanner</name> - <packagename>enigma2-plugin-systemplugins-defaultservicesscanner</packagename> - <shortdescription>Standard Sendersuche nach Satellit mit einem Rotor.</shortdescription> - <description>Mit der DefaultServicesScanner Erweiterung können Sie eine standard Sendersuche nach Satellit mit einem angeschlossenen Rotor durchführen. + <shortdescription>Scans default lamedbs sorted by satellite</shortdescription> + <description>With the DefaultServicesScanner plugin you can scan default lamedbs sorted by satellite with a connected dish positioner. </description> <screenshot src="http://www.dreamboxupdate.com/preview/plugin_defaultservicescanner.jpg" /> </info> + <files type="package"> <!-- without version, without .ipk --> <file type="package" name="enigma2-plugin-systemplugins-defaultservicesscanner" /> </files> diff --git a/lib/python/Plugins/SystemPlugins/DefaultServicesScanner/plugin.py b/lib/python/Plugins/SystemPlugins/DefaultServicesScanner/plugin.py index 4d0a992d..d26881ed 100644 --- a/lib/python/Plugins/SystemPlugins/DefaultServicesScanner/plugin.py +++ b/lib/python/Plugins/SystemPlugins/DefaultServicesScanner/plugin.py @@ -134,4 +134,4 @@ def DefaultServicesScannerMain(session, **kwargs): session.open(DefaultServicesScannerPlugin) def Plugins(**kwargs): - return PluginDescriptor(name="Default Services Scanner", description=_("Scans default lamedbs sorted by satellite with a connected dish positioner"), where = PluginDescriptor.WHERE_PLUGINMENU, fnc=DefaultServicesScannerMain) + return PluginDescriptor(name="Default Services Scanner", description=_("Scans default lamedbs sorted by satellite with a connected dish positioner"), where = PluginDescriptor.WHERE_PLUGINMENU, needsRestart = False, fnc=DefaultServicesScannerMain) diff --git a/lib/python/Plugins/SystemPlugins/DiseqcTester/meta/Makefile.am b/lib/python/Plugins/SystemPlugins/DiseqcTester/meta/Makefile.am index 9b0a2ede..92a5473f 100755 --- a/lib/python/Plugins/SystemPlugins/DiseqcTester/meta/Makefile.am +++ b/lib/python/Plugins/SystemPlugins/DiseqcTester/meta/Makefile.am @@ -1,4 +1,4 @@ -installdir = $(datadir)/meta/ +installdir = $(datadir)/meta dist_install_DATA = plugin_diseqctester.xml diff --git a/lib/python/Plugins/SystemPlugins/DiseqcTester/meta/plugin_diseqctester.xml b/lib/python/Plugins/SystemPlugins/DiseqcTester/meta/plugin_diseqctester.xml index 33808b3e..567618b2 100755 --- a/lib/python/Plugins/SystemPlugins/DiseqcTester/meta/plugin_diseqctester.xml +++ b/lib/python/Plugins/SystemPlugins/DiseqcTester/meta/plugin_diseqctester.xml @@ -3,24 +3,16 @@ <tag type="SAT" /> <tag type="System" /> </prerequisites> - <info language="en"> + <info> <author>Dream Multimedia</author> <name>DiseqcTester</name> <packagename>enigma2-plugin-systemplugins-diseqctester</packagename> - <shortdescription>Test your Diseqc equipment.</shortdescription> - <description>With the DiseqcTester extension you can test your satellite equipment for Diseqc compatibility and errors. + <shortdescription>Test your DiSEqC equipment</shortdescription> + <description>With the DiseqcTester plugin you can test your satellite equipment for DiSEqC compatibility and errors. </description> <screenshot src="http://www.dreamboxupdate.com/preview/plugin_diseqctester_en.jpg" /> </info> - <info language="de"> - <author>Dream Multimedia</author> - <name>DiseqcTester</name> - <packagename>enigma2-plugin-systemplugins-diseqctester</packagename> - <shortdescription>Testet Ihr Diseqc Equipment.</shortdescription> - <description>Mit der DiseqcTester Erweiterung können Sie Ihr Satelliten-Equipment nach Diseqc-Kompatibilität und Fehlern überprüfen. - </description> - <screenshot src="http://www.dreamboxupdate.com/preview/plugin_diseqctester_de.jpg" /> - </info> + <files type="package"> <!-- without version, without .ipk --> <file type="package" name="enigma2-plugin-systemplugins-diseqctester" /> </files> diff --git a/lib/python/Plugins/SystemPlugins/DiseqcTester/plugin.py b/lib/python/Plugins/SystemPlugins/DiseqcTester/plugin.py index 5b7edcf6..4dcf6c6b 100755..100644 --- a/lib/python/Plugins/SystemPlugins/DiseqcTester/plugin.py +++ b/lib/python/Plugins/SystemPlugins/DiseqcTester/plugin.py @@ -679,5 +679,5 @@ def autostart(reason, **kwargs): resourcemanager.addResource("DiseqcTester", DiseqcTesterMain) def Plugins(**kwargs): - return [ PluginDescriptor(name="DiSEqC Tester", description=_("Test DiSEqC settings"), where = PluginDescriptor.WHERE_PLUGINMENU, fnc=DiseqcTesterMain), - PluginDescriptor(where = PluginDescriptor.WHERE_AUTOSTART, fnc = autostart)] + return [ PluginDescriptor(name="DiSEqC Tester", description=_("Test DiSEqC settings"), where = PluginDescriptor.WHERE_PLUGINMENU, needsRestart = False, fnc=DiseqcTesterMain), + PluginDescriptor(where = PluginDescriptor.WHERE_AUTOSTART, needsRestart = False, fnc = autostart)] diff --git a/lib/python/Plugins/SystemPlugins/FrontprocessorUpgrade/meta/Makefile.am b/lib/python/Plugins/SystemPlugins/FrontprocessorUpgrade/meta/Makefile.am index b1a93d3e..5ab313aa 100755 --- a/lib/python/Plugins/SystemPlugins/FrontprocessorUpgrade/meta/Makefile.am +++ b/lib/python/Plugins/SystemPlugins/FrontprocessorUpgrade/meta/Makefile.am @@ -1,3 +1,3 @@ -installdir = $(datadir)/meta/ +installdir = $(datadir)/meta dist_install_DATA = plugin_frontprocessorupgrade.xml diff --git a/lib/python/Plugins/SystemPlugins/FrontprocessorUpgrade/meta/plugin_frontprocessorupgrade.xml b/lib/python/Plugins/SystemPlugins/FrontprocessorUpgrade/meta/plugin_frontprocessorupgrade.xml index 1763f67f..7b6fdca7 100644..100755 --- a/lib/python/Plugins/SystemPlugins/FrontprocessorUpgrade/meta/plugin_frontprocessorupgrade.xml +++ b/lib/python/Plugins/SystemPlugins/FrontprocessorUpgrade/meta/plugin_frontprocessorupgrade.xml @@ -3,24 +3,16 @@ <hardware type="dm8000" /> <tag type="System" /> </prerequisites> - <info language="en"> + <info> <author>Dream Multimedia</author> <name>FrontprocessorUpgrade</name> <packagename>enigma2-plugin-systemplugins-frontprocessorupgrade</packagename> <packagetype>internal</packagetype> <!-- internal/public , public is default, internal metas are not displayed inside plugin manager --> - <shortdescription>Internal firmware updater.</shortdescription> + <shortdescription>Internal firmware updater</shortdescription> <description>This system tool is internally used to program the hardware with firmware updates. </description> </info> - <info language="de"> - <author>Dream Multimedia</author> - <name>FrontprocessorUpgrade</name> - <packagename>enigma2-plugin-systemplugins-frontprocessorupgrade</packagename> - <packagetype>internal</packagetype> <!-- internal/public , public is default, internal metas are not displayed inside plugin manager --> - <shortdescription>Interner Firmware-Upgrader.</shortdescription> - <description>Dieses Systemtool wird intern benutzt um Firmware-Upgrades für die Hardware aufzuspielen. - </description> - </info> + <files type="package"> <!-- without version, without .ipk --> <file type="package" name="enigma2-plugin-systemplugins-frontprocessorupgrade" /> </files> diff --git a/lib/python/Plugins/SystemPlugins/FrontprocessorUpgrade/plugin.py b/lib/python/Plugins/SystemPlugins/FrontprocessorUpgrade/plugin.py index 38b80c95..6cb30de2 100644 --- a/lib/python/Plugins/SystemPlugins/FrontprocessorUpgrade/plugin.py +++ b/lib/python/Plugins/SystemPlugins/FrontprocessorUpgrade/plugin.py @@ -76,11 +76,11 @@ def Plugins(**kwargs): newversion = getUpgradeVersion() or 0 list = [] if version is not None and version < newversion: - list.append(PluginDescriptor(name="FP Upgrade", where = PluginDescriptor.WHERE_WIZARD, fnc=(8, FPUpgrade))) + list.append(PluginDescriptor(name="FP Upgrade", where = PluginDescriptor.WHERE_WIZARD, needsRestart = True, fnc=(8, FPUpgrade))) try: msg = open("/proc/stb/message").read() - list.append(PluginDescriptor(name="System Message Check", where = PluginDescriptor.WHERE_WIZARD, fnc=(9, SystemMessage, msg))) + list.append(PluginDescriptor(name="System Message Check", where = PluginDescriptor.WHERE_WIZARD, needsRestart = True, fnc=(9, SystemMessage, msg))) except: pass diff --git a/lib/python/Plugins/SystemPlugins/Hotplug/meta/Makefile.am b/lib/python/Plugins/SystemPlugins/Hotplug/meta/Makefile.am index a757b538..cf5608ba 100755 --- a/lib/python/Plugins/SystemPlugins/Hotplug/meta/Makefile.am +++ b/lib/python/Plugins/SystemPlugins/Hotplug/meta/Makefile.am @@ -1,3 +1,3 @@ -installdir = $(datadir)/meta/ +installdir = $(datadir)/meta dist_install_DATA = plugin_hotplug.xml diff --git a/lib/python/Plugins/SystemPlugins/Hotplug/meta/plugin_hotplug.xml b/lib/python/Plugins/SystemPlugins/Hotplug/meta/plugin_hotplug.xml index 6c2824ca..610dfee6 100644..100755 --- a/lib/python/Plugins/SystemPlugins/Hotplug/meta/plugin_hotplug.xml +++ b/lib/python/Plugins/SystemPlugins/Hotplug/meta/plugin_hotplug.xml @@ -2,22 +2,15 @@ <prerequisites> <tag type="System" /> </prerequisites> - <info language="en"> + <info> <author>Dream Multimedia</author> <name>Hotplug</name> <packagename>enigma2-plugin-systemplugins-hotplug</packagename> - <shortdescription>Hotplugging for removeable devices.</shortdescription> - <description>The Hotplug extension notifies your system of newly added or removed devices. - </description> - </info> - <info language="de"> - <author>Dream Multimedia</author> - <name>Hotplug</name> - <packagename>enigma2-plugin-systemplugins-hotplug</packagename> - <shortdescription>Hotplugging für entfernbare Geräte.</shortdescription> - <description>Mit der Hotplug-Erweiterung wird Ihr System über neu angeschlossene oder entfernte Geräte informiert. + <shortdescription>Hotplugging for removeable devices</shortdescription> + <description>The Hotplug plugin notifies your system of newly added or removed devices. </description> </info> + <files type="package"> <!-- without version, without .ipk --> <file type="package" name="enigma2-plugin-systemplugins-hotplug" /> </files> diff --git a/lib/python/Plugins/SystemPlugins/Hotplug/plugin.py b/lib/python/Plugins/SystemPlugins/Hotplug/plugin.py index b19007c9..84cbbcb6 100644 --- a/lib/python/Plugins/SystemPlugins/Hotplug/plugin.py +++ b/lib/python/Plugins/SystemPlugins/Hotplug/plugin.py @@ -1,66 +1,300 @@ from Plugins.Plugin import PluginDescriptor -from twisted.internet.protocol import Protocol, Factory -from twisted.internet import reactor from Components.Harddisk import harddiskmanager +from Tools.Directories import fileExists hotplugNotifier = [ ] +bdpoll = None -class Hotplug(Protocol): - def connectionMade(self): - self.received = "" +def processHotplugData(self, v): + print "hotplug:", v + action = v.get("ACTION") + device = v.get("DEVPATH") + physdevpath = v.get("PHYSDEVPATH") + media_state = v.get("X_E2_MEDIA_STATUS") - def dataReceived(self, data): - self.received += data + dev = device.split('/')[-1] - def connectionLost(self, reason): - data = self.received.split('\0')[:-1] + if action is not None and action == "add": + error, blacklisted, removable, is_cdrom, partitions, medium_found = harddiskmanager.addHotplugPartition(dev, physdevpath) + if bdpoll and removable or is_cdrom: + bdpoll.addDevice(dev, is_cdrom, medium_found) + elif action is not None and action == "remove": + if bdpoll: + bdpoll.removeDevice(dev) + harddiskmanager.removeHotplugPartition(dev) + elif media_state is not None: + if media_state == '1': + harddiskmanager.removeHotplugPartition(dev) + harddiskmanager.addHotplugPartition(dev, physdevpath) + elif media_state == '0': + harddiskmanager.removeHotplugPartition(dev) - v = {} + for callback in hotplugNotifier: + try: + callback(dev, action or media_state) + except AttributeError: + hotplugNotifier.remove(callback) - for x in data: - i = x.find('=') - var, val = x[:i], x[i+1:] - v[var] = val +CDROM_DRIVE_STATUS = 0x5326 +CDROM_MEDIA_CHANGED = 0x5325 +CDSL_CURRENT = ((int)(~0>>1)) +CDS_NO_INFO = 0 +CDS_NO_DISC = 1 +CDS_TRAY_OPEN = 2 +CDS_DRIVE_NOT_READY = 3 +CDS_DISC_OK = 4 +ENOMEDIUM = 159 +IOC_NRBITS = 8 +IOC_NRSHIFT = 0 +IOC_TYPESHIFT = (IOC_NRSHIFT+IOC_NRBITS) +BLKRRPART = ((0x12<<IOC_TYPESHIFT) | (95<<IOC_NRSHIFT)) - print "hotplug:", v +def autostart(reason, **kwargs): + if reason == 0: + print "starting hotplug handler" - action = v.get("ACTION") - device = v.get("DEVPATH") - physdevpath = v.get("PHYSDEVPATH") - media_state = v.get("X_E2_MEDIA_STATUS") + if fileExists('/dev/.udev'): + global netlink + global bdpoll + from enigma import eSocketNotifier, eTimer, ePythonMessagePump + import socket + from select import POLLIN, POLLPRI - dev = device.split('/')[-1] + class Netlink: + def __init__(self): + self.netlink = socket.socket(socket.AF_NETLINK, socket.SOCK_DGRAM, 15) + self.netlink.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 65536) + self.netlink.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 65536) + self.netlink.bind((0, 1)) + self.sn = eSocketNotifier(self.netlink.fileno(), POLLIN|POLLPRI) + self.sn.callback.append(self.dataAvail) - if action is not None and action == "add": - harddiskmanager.addHotplugPartition(dev, physdevpath) - elif action is not None and action == "remove": - harddiskmanager.removeHotplugPartition(dev) - elif media_state is not None: - if media_state == '1': - harddiskmanager.removeHotplugPartition(dev) - harddiskmanager.addHotplugPartition(dev, physdevpath) - elif media_state == '0': - harddiskmanager.removeHotplugPartition(dev) - - for callback in hotplugNotifier: - try: - callback(dev, action or media_state) - except AttributeError: - hotplugNotifier.remove(callback) + def dataAvail(self, what): + received = self.netlink.recvfrom(16384) +# print "HOTPLUG(%d):" %(what), received -def autostart(reason, **kwargs): - if reason == 0: - print "starting hotplug handler" - factory = Factory() - factory.protocol = Hotplug + data = received[0].split('\0')[:-1] + v = {} + + for x in data: + i = x.find('=') + var, val = x[:i], x[i+1:] + v[var] = val + + if v['SUBSYSTEM'] == 'block' and v['ACTION'] in ('add', 'remove'): + processHotplugData(self, v) + + from threading import Thread, Semaphore, Lock + + class ThreadQueue: + def __init__(self): + self.__list = [ ] + self.__lock = Lock() + + def push(self, val): + list = self.__list + lock = self.__lock + lock.acquire() + list.append(val) + lock.release() + + def pop(self): + list = self.__list + lock = self.__lock + lock.acquire() + ret = list[0] + del list[0] + lock.release() + return ret - try: import os - os.remove("/tmp/hotplug.socket") - except OSError: - pass + import errno + import fcntl + + class BDPoll(Thread): + CHECK_INTERVAL = 2000 + MSG_MEDIUM_REMOVED = 1 + MSG_MEDIUM_INSERTED = 2 + MSG_POLL_FINISHED = 4 + def __init__(self): + Thread.__init__(self) + self.__sema = Semaphore(0) + self.__lock = Lock() + self.running = False + self.devices_to_poll = { } + self.messages = ThreadQueue() + self.checkTimer = eTimer() + self.checkTimer.callback.append(self.timeout) + self.checkTimer.start(BDPoll.CHECK_INTERVAL, True) + self.mp = ePythonMessagePump() + self.mp.recv_msg.get().append(self.gotThreadMsg) + self.start() + + def gotThreadMsg(self, msg): + msg = self.messages.pop() + if msg[0] == BDPoll.MSG_MEDIUM_REMOVED: + print "MSG_MEDIUM_REMOVED" + harddiskmanager.removeHotplugPartition(msg[1]) + elif msg[0] == BDPoll.MSG_MEDIUM_INSERTED: + print "MSG_MEDIUM_INSERTED" + harddiskmanager.addHotplugPartition(msg[1]) + elif msg[0] == BDPoll.MSG_POLL_FINISHED: + self.checkTimer.start(BDPoll.CHECK_INTERVAL, True) + + def timeout(self): + self.__sema.release() # start bdpoll loop in thread + + def is_mounted(self, dev): + mounts = file('/proc/mounts').read() + return mounts.find(dev) != -1 + + def run(self): + sema = self.__sema + lock = self.__lock + messages = self.messages + mp = self.mp + self.running = True + while self.running: + sema.acquire() + self.__lock.acquire() + devices_to_poll = self.devices_to_poll.items() + self.__lock.release() + devices_to_poll_processed = [ ] + for device, state in devices_to_poll: + got_media = False + is_cdrom, prev_media_state = state + if is_cdrom: + try: + fd = os.open("/dev/" + device, os.O_RDONLY | os.O_NONBLOCK | os.O_EXCL) + except OSError, err: + if err.errno == errno.EBUSY: + print "open cdrom exclusive failed:", + if not self.is_mounted(device): + print "not mounted" + continue + try: + print "mounted... try non exclusive" + fd = os.open("/dev/" + device, os.O_RDONLY | os.O_NONBLOCK) + except OSError, err: + print "open cdrom not exclusive failed", os.strerror(err.errno) + continue + #here the fs must be valid! + try: + ret = fcntl.ioctl(fd, CDROM_DRIVE_STATUS, CDSL_CURRENT) + except IOError, err: + print "ioctl CDROM_DRIVE_STATUS failed", os.strerror(err.errno) + else: + if ret in (CDS_NO_INFO, CDS_NO_DISC, CDS_TRAY_OPEN, CDS_DRIVE_NOT_READY): + pass + elif ret == CDS_DISC_OK: + #todo new kernels support events to userspace event on media change + #but not 2.6.18.... see hotplug-ng bdpoll.c + got_media = True + os.close(fd) + else: + try: + fd = os.open("/dev/" + device, os.O_RDONLY) + except OSError, err: + if err.errno == ENOMEDIUM: + pass + else: + print "open non cdrom failed", os.strerror(err.errno) + continue + else: + got_media = True + os.close(fd) + if prev_media_state: + if not got_media: + print "media removal detected on", device + try: + fd = os.open("/dev/" + device, os.O_RDONLY | os.O_NONBLOCK) + except OSError, err: + print "open device for blkrrpart ioctl failed", os.strerror(err.errno) + else: + try: + fcntl.ioctl(fd, BLKRRPART) + except IOError, err: + print "ioctl BLKRRPART failed", os.strerror(err.errno) + os.close(fd) + else: + if got_media: + print "media insertion detected on", device + devices_to_poll_processed.append((device, is_cdrom, got_media)) + self.__lock.acquire() + for device, is_cdrom, state in devices_to_poll_processed: + old_state = self.devices_to_poll.get(device) + if old_state is not None and old_state[1] != state: + msg = state and BDPoll.MSG_MEDIUM_INSERTED or BDPoll.MSG_MEDIUM_REMOVED + self.devices_to_poll[device] = (is_cdrom, state) + messages.push((msg, device)) + mp.send(0) + + self.__lock.release() + messages.push((self.MSG_POLL_FINISHED,)) + mp.send(0) + + def addDevice(self, device, is_cdrom, inserted): + self.__lock.acquire() + if device in self.devices_to_poll: + print "device", device, "already in bdpoll" + else: + print "add device", device, "to bdpoll current state:", + if inserted: + print "medium inserted" + else: + print "medium removed" + self.devices_to_poll[device] = (is_cdrom, inserted) + self.__lock.release() + + def removeDevice(self, device): + self.__lock.acquire() + if device in self.devices_to_poll: + print "device", device, "removed from bdpoll" + del self.devices_to_poll[device] + else: + print "try to del not exist device", device, "from bdpoll" + self.__lock.release() + + netlink = Netlink() + bdpoll = BDPoll() + for blockdev, removable, is_cdrom, medium_found in harddiskmanager.devices_scanned_on_init: + if removable or is_cdrom: + bdpoll.addDevice(blockdev, is_cdrom, medium_found) + else: + from twisted.internet.protocol import Protocol, Factory + from twisted.internet import reactor + + try: + import os + os.remove("/tmp/hotplug.socket") + except OSError: + pass + + class Hotplug(Protocol): + def connectionMade(self): + print "HOTPLUG connection!" + self.received = "" + + def dataReceived(self, data): + print "hotplug:", data + self.received += data + print "complete", self.received + + def connectionLost(self, reason): + print "HOTPLUG connection lost!" + data = self.received.split('\0')[:-1] + v = {} + + for x in data: + i = x.find('=') + var, val = x[:i], x[i+1:] + v[var] = val + + processHotplugData(self, v) - reactor.listenUNIX("/tmp/hotplug.socket", factory) + factory = Factory() + factory.protocol = Hotplug + reactor.listenUNIX("/tmp/hotplug.socket", factory) def Plugins(**kwargs): - return PluginDescriptor(name = "Hotplug", description = "listens to hotplug events", where = PluginDescriptor.WHERE_AUTOSTART, fnc = autostart) + return PluginDescriptor(name = "Hotplug", description = "listens to hotplug events", where = PluginDescriptor.WHERE_AUTOSTART, needsRestart = True, fnc = autostart) diff --git a/lib/python/Plugins/SystemPlugins/NFIFlash/dm8000_writenand b/lib/python/Plugins/SystemPlugins/NFIFlash/dm8000_writenand Binary files differdeleted file mode 100755 index bd02d8fe..00000000 --- a/lib/python/Plugins/SystemPlugins/NFIFlash/dm8000_writenand +++ /dev/null diff --git a/lib/python/Plugins/SystemPlugins/NFIFlash/downloader.py b/lib/python/Plugins/SystemPlugins/NFIFlash/downloader.py index 6463f179..85ecc92d 100644 --- a/lib/python/Plugins/SystemPlugins/NFIFlash/downloader.py +++ b/lib/python/Plugins/SystemPlugins/NFIFlash/downloader.py @@ -1,72 +1,222 @@ # -*- coding: utf-8 -*- -from Components.MenuList import MenuList +from Plugins.SystemPlugins.Hotplug.plugin import hotplugNotifier from Screens.Screen import Screen from Screens.MessageBox import MessageBox from Screens.ChoiceBox import ChoiceBox +from Screens.HelpMenu import HelpableScreen +from Screens.TaskView import JobView +from Components.About import about from Components.ActionMap import ActionMap from Components.Sources.StaticText import StaticText -from Components.Sources.Progress import Progress +from Components.Sources.List import List from Components.Label import Label from Components.FileList import FileList +from Components.MenuList import MenuList from Components.MultiContent import MultiContentEntryText from Components.ScrollLabel import ScrollLabel -from Tools.Directories import fileExists +from Components.Harddisk import harddiskmanager +from Components.Task import Task, Job, job_manager, Condition +from Tools.Directories import fileExists, isMount, resolveFilename, SCOPE_HDD, SCOPE_MEDIA from Tools.HardwareInfo import HardwareInfo -from enigma import eConsoleAppContainer, eListbox, gFont, eListboxPythonMultiContent, \ - RT_HALIGN_LEFT, RT_HALIGN_CENTER, RT_VALIGN_CENTER, RT_WRAP, eRect, eTimer -from os import system, remove -import re -import urllib from Tools.Downloader import downloadWithProgress +from enigma import eConsoleAppContainer, gFont, RT_HALIGN_LEFT, RT_HALIGN_CENTER, RT_VALIGN_CENTER, RT_WRAP, eTimer +from os import system, path, access, stat, remove, W_OK, R_OK from twisted.web import client from twisted.internet import reactor, defer from twisted.python import failure -from Plugins.SystemPlugins.Hotplug.plugin import hotplugNotifier - -class UserRequestedCancel(Exception): - pass - -class Feedlist(MenuList): - def __init__(self, list=[], enableWrapAround = False): - MenuList.__init__(self, list, enableWrapAround, eListboxPythonMultiContent) - self.l.setFont(0, gFont("Regular", 16)) - self.l.setItemHeight(22) +import re - def clear(self): - del self.list[:] - self.l.setList(self.list) +class ImageDownloadJob(Job): + def __init__(self, url, filename, device=None, mountpoint="/"): + Job.__init__(self, _("Download .NFI-Files for USB-Flasher")) + if device: + if isMount(mountpoint): + UmountTask(self, mountpoint) + MountTask(self, device, mountpoint) + ImageDownloadTask(self, url, mountpoint+filename) + ImageDownloadTask(self, url[:-4]+".nfo", mountpoint+filename[:-4]+".nfo") + #if device: + #UmountTask(self, mountpoint) + + def retry(self): + self.tasks[0].args += self.tasks[0].retryargs + Job.retry(self) + +class MountTask(Task): + def __init__(self, job, device, mountpoint): + Task.__init__(self, job, ("mount")) + self.setTool("mount") + options = "rw,sync" + self.mountpoint = mountpoint + self.args += [ device, mountpoint, "-o"+options ] + self.weighting = 1 + + def processOutput(self, data): + print "[MountTask] output:", data + +class UmountTask(Task): + def __init__(self, job, mountpoint): + Task.__init__(self, job, ("mount")) + self.setTool("umount") + self.args += [mountpoint] + self.weighting = 1 + +class DownloaderPostcondition(Condition): + def check(self, task): + return task.returncode == 0 + + def getErrorMessage(self, task): + return self.error_message + +class ImageDownloadTask(Task): + def __init__(self, job, url, path): + Task.__init__(self, job, _("Downloading")) + self.postconditions.append(DownloaderPostcondition()) + self.job = job + self.url = url + self.path = path + self.error_message = "" + self.last_recvbytes = 0 + self.error_message = None + self.download = None + self.aborted = False - def getNFIname(self): - l = self.l.getCurrentSelection() - return l and l[0][0] + def run(self, callback): + self.callback = callback + self.download = downloadWithProgress(self.url,self.path) + self.download.addProgress(self.download_progress) + self.download.start().addCallback(self.download_finished).addErrback(self.download_failed) + print "[ImageDownloadTask] downloading", self.url, "to", self.path - def getNFIurl(self): - l = self.l.getCurrentSelection() - return l and l[0][1] + def abort(self): + print "[ImageDownloadTask] aborting", self.url + if self.download: + self.download.stop() + self.aborted = True - def getNFOname(self): - l = self.l.getCurrentSelection() - return l and l[0][0][:-3]+"nfo" + def download_progress(self, recvbytes, totalbytes): + #print "[update_progress] recvbytes=%d, totalbytes=%d" % (recvbytes, totalbytes) + if ( recvbytes - self.last_recvbytes ) > 10000: # anti-flicker + self.progress = int(100*(float(recvbytes)/float(totalbytes))) + self.name = _("Downloading") + ' ' + "%d of %d kBytes" % (recvbytes/1024, totalbytes/1024) + self.last_recvbytes = recvbytes - def getNFOurl(self): - l = self.l.getCurrentSelection() - return l and l[0][1][:-3]+"nfo" + def download_failed(self, failure_instance=None, error_message=""): + self.error_message = error_message + if error_message == "" and failure_instance is not None: + self.error_message = failure_instance.getErrorMessage() + Task.processFinished(self, 1) - def isValid(self): - l = self.l.getCurrentSelection() - if not l or l[0] == 0: - return False + def download_finished(self, string=""): + if self.aborted: + self.finish(aborted = True) + else: + Task.processFinished(self, 0) + +class StickWizardJob(Job): + def __init__(self, path): + Job.__init__(self, _("USB stick wizard")) + self.path = path + self.device = path + while self.device[-1:] == "/" or self.device[-1:].isdigit(): + self.device = self.device[:-1] + + box = HardwareInfo().get_device_name() + url = "http://www.dreamboxupdate.com/download/opendreambox/dreambox-nfiflasher-%s.tar.bz2" % box + self.downloadfilename = "/tmp/dreambox-nfiflasher-%s.tar.bz2" % box + self.imagefilename = "/tmp/nfiflash_%s.img" % box + #UmountTask(self, device) + PartitionTask(self) + ImageDownloadTask(self, url, self.downloadfilename) + UnpackTask(self) + CopyTask(self) + +class PartitionTaskPostcondition(Condition): + def check(self, task): + return task.returncode == 0 + + def getErrorMessage(self, task): + return { + task.ERROR_BLKRRPART: ("Device or resource busy"), + task.ERROR_UNKNOWN: (task.errormsg) + }[task.error] + +class PartitionTask(Task): + ERROR_UNKNOWN, ERROR_BLKRRPART = range(2) + def __init__(self, job): + Task.__init__(self, job, ("partitioning")) + self.postconditions.append(PartitionTaskPostcondition()) + self.job = job + self.setTool("sfdisk") + self.args += [self.job.device] + self.weighting = 10 + self.initial_input = "0 - 0x6 *\n;\n;\n;\ny" + self.errormsg = "" + + def run(self, callback): + Task.run(self, callback) + + def processOutput(self, data): + print "[PartitionTask] output:", data + if data.startswith("BLKRRPART:"): + self.error = self.ERROR_BLKRRPART else: - return True + self.error = self.ERROR_UNKNOWN + self.errormsg = data + +class UnpackTask(Task): + def __init__(self, job): + Task.__init__(self, job, ("Unpacking USB flasher image...")) + self.job = job + self.setTool("tar") + self.args += ["-xjvf", self.job.downloadfilename] + self.weighting = 80 + self.end = 80 + self.delayTimer = eTimer() + self.delayTimer.callback.append(self.progress_increment) + + def run(self, callback): + Task.run(self, callback) + self.delayTimer.start(950, False) + + def progress_increment(self): + self.progress += 1 + + def processOutput(self, data): + print "[UnpackTask] output: \'%s\'" % data + self.job.imagefilename = data + + def afterRun(self): + self.delayTimer.callback.remove(self.progress_increment) + +class CopyTask(Task): + def __init__(self, job): + Task.__init__(self, job, ("Copying USB flasher boot image to stick...")) + self.job = job + self.setTool("dd") + self.args += ["if=%s" % self.job.imagefilename, "of=%s1" % self.job.device] + self.weighting = 20 + self.end = 20 + self.delayTimer = eTimer() + self.delayTimer.callback.append(self.progress_increment) + + def run(self, callback): + Task.run(self, callback) + self.delayTimer.start(100, False) + + def progress_increment(self): + self.progress += 1 + + def processOutput(self, data): + print "[CopyTask] output:", data - def moveSelection(self,idx=0): - if self.instance is not None: - self.instance.moveSelectionTo(idx) + def afterRun(self): + self.delayTimer.callback.remove(self.progress_increment) class NFOViewer(Screen): skin = """ - <screen name="NFOViewer" position="110,115" size="540,400" title="Changelog viewer" > - <widget name="changelog" position="10,10" size="520,380" font="Regular;16" /> + <screen name="NFOViewer" position="center,center" size="610,410" title="Changelog" > + <widget name="changelog" position="10,10" size="590,380" font="Regular;16" /> </screen>""" def __init__(self, session, nfo): @@ -80,594 +230,576 @@ class NFOViewer(Screen): "ok": self.exit, "cancel": self.exit, "down": self.pageDown, - "up": self.pageUp + "up": self.pageUp }) def pageUp(self): self["changelog"].pageUp() def pageDown(self): self["changelog"].pageDown() - + def exit(self): self.close(False) -class NFIDownload(Screen): - LIST_SOURCE = 1 - LIST_DEST = 2 - skin = """ - <screen name="NFIDownload" position="90,95" size="560,420" title="Image download utility"> - <ePixmap pixmap="skin_default/buttons/red.png" position="0,0" zPosition="0" size="140,40" transparent="1" alphatest="on" /> - <ePixmap pixmap="skin_default/buttons/green.png" position="140,0" zPosition="0" size="140,40" transparent="1" alphatest="on" /> - <ePixmap pixmap="skin_default/buttons/yellow.png" position="280,0" zPosition="0" size="140,40" transparent="1" alphatest="on" /> - <ePixmap pixmap="skin_default/buttons/blue.png" position="420,0" zPosition="0" size="140,40" transparent="1" alphatest="on" /> - <widget source="key_red" render="Label" position="0,0" zPosition="1" size="140,40" font="Regular;19" valign="center" halign="center" backgroundColor="#9f1313" transparent="1" /> - <widget source="key_green" render="Label" position="140,0" zPosition="1" size="140,40" font="Regular;19" valign="center" halign="center" backgroundColor="#1f771f" transparent="1" /> - <widget source="key_yellow" render="Label" position="280,0" zPosition="1" size="140,40" font="Regular;19" valign="center" halign="center" backgroundColor="#a08500" transparent="1" /> - <widget source="key_blue" render="Label" position="420,0" zPosition="1" size="140,40" font="Regular;19" valign="center" halign="center" backgroundColor="#18188b" transparent="1" /> - - <widget source="label_top" render="Label" position="10,44" size="240,20" font="Regular;16" /> - <widget name="feedlist" position="10,66" size="250,222" scrollbarMode="showOnDemand" /> - <widget name="destlist" position="0,66" size="260,222" scrollbarMode="showOnDemand" /> +class feedDownloader: + def __init__(self, feed_base, box, OE_vers): + print "[feedDownloader::init] feed_base=%s, box=%s" % (feed_base, box) + self.feed_base = feed_base + self.OE_vers = OE_vers + self.box = box + + def getList(self, callback, errback): + self.urlbase = "%s/%s/%s/images/" % (self.feed_base, self.OE_vers, self.box) + print "[getList]", self.urlbase + self.callback = callback + self.errback = errback + client.getPage(self.urlbase).addCallback(self.feed_finished).addErrback(self.feed_failed) - <widget source="label_bottom" render="Label" position="10,312" size="240,18" font="Regular;16"/> - <widget source="path_bottom" render="Label" position="10,330" size="250,42" font="Regular;18" /> - - <widget source="infolabel" render="Label" position="270,44" size="280,284" font="Regular;16" /> - <widget source="job_progressbar" render="Progress" position="10,374" size="540,26" borderWidth="1" backgroundColor="#254f7497" /> - <widget source="job_progresslabel" render="Label" position="130,378" zPosition="2" font="Regular;18" halign="center" transparent="1" size="300,22" foregroundColor="#000000" /> - <widget source="statusbar" render="Label" position="10,404" size="540,16" font="Regular;16" foregroundColor="#cccccc" /> + def feed_failed(self, failure_instance): + print "[feed_failed]", str(failure_instance) + self.errback(failure_instance.getErrorMessage()) + + def feed_finished(self, feedhtml): + print "[feed_finished]" + fileresultmask = re.compile("<a class=[\'\"]nfi[\'\"] href=[\'\"](?P<url>.*?)[\'\"]>(?P<name>.*?.nfi)</a>", re.DOTALL) + searchresults = fileresultmask.finditer(feedhtml) + fileresultlist = [] + if searchresults: + for x in searchresults: + url = x.group("url") + if url[0:7] != "http://": + url = self.urlbase + x.group("url") + name = x.group("name") + entry = (name, url) + fileresultlist.append(entry) + self.callback(fileresultlist, self.OE_vers) + +class DeviceBrowser(Screen, HelpableScreen): + skin = """ + <screen name="DeviceBrowser" position="center,center" size="520,430" title="Please select target medium" > + <ePixmap pixmap="skin_default/buttons/red.png" position="0,0" size="140,40" alphatest="on" /> + <ePixmap pixmap="skin_default/buttons/green.png" position="140,0" size="140,40" alphatest="on" /> + <widget source="key_red" render="Label" position="0,0" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#9f1313" transparent="1" /> + <widget source="key_green" render="Label" position="140,0" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#1f771f" transparent="1" /> + <widget source="message" render="Label" position="5,50" size="510,150" font="Regular;16" /> + <widget name="filelist" position="5,210" size="510,220" scrollbarMode="showOnDemand" /> </screen>""" - def __init__(self, session, destdir="/tmp/"): - self.skin = NFIDownload.skin + def __init__(self, session, startdir, message="", showDirectories = True, showFiles = True, showMountpoints = True, matchingPattern = "", useServiceRef = False, inhibitDirs = False, inhibitMounts = False, isTop = False, enableWrapAround = False, additionalExtensions = None): Screen.__init__(self, session) + + HelpableScreen.__init__(self) + + self["key_red"] = StaticText(_("Cancel")) + self["key_green"] = StaticText() + self["message"] = StaticText(message) + + self.filelist = FileList(startdir, showDirectories = showDirectories, showFiles = showFiles, showMountpoints = showMountpoints, matchingPattern = matchingPattern, useServiceRef = useServiceRef, inhibitDirs = inhibitDirs, inhibitMounts = inhibitMounts, isTop = isTop, enableWrapAround = enableWrapAround, additionalExtensions = additionalExtensions) + self["filelist"] = self.filelist + + self["FilelistActions"] = ActionMap(["SetupActions", "ColorActions"], + { + "green": self.use, + "red": self.exit, + "ok": self.ok, + "cancel": self.exit + }) + + hotplugNotifier.append(self.hotplugCB) + self.onShown.append(self.updateButton) + self.onClose.append(self.removeHotplug) + + def hotplugCB(self, dev, action): + print "[hotplugCB]", dev, action + self.updateButton() + + def updateButton(self): - self["job_progressbar"] = Progress() - self["job_progresslabel"] = StaticText() + if self["filelist"].getFilename() or self["filelist"].getCurrentDirectory(): + self["key_green"].text = _("Use") + else: + self["key_green"].text = "" + + def removeHotplug(self): + print "[removeHotplug]" + hotplugNotifier.remove(self.hotplugCB) + + def ok(self): + if self.filelist.canDescent(): + if self["filelist"].showMountpoints == True and self["filelist"].showDirectories == False: + self.use() + else: + self.filelist.descent() + + def use(self): + print "[use]", self["filelist"].getCurrentDirectory(), self["filelist"].getFilename() + if self["filelist"].getCurrentDirectory() is not None: + if self.filelist.canDescent() and self["filelist"].getFilename() and len(self["filelist"].getFilename()) > len(self["filelist"].getCurrentDirectory()): + self.filelist.descent() + self.close(self["filelist"].getCurrentDirectory()) + elif self["filelist"].getFilename(): + self.close(self["filelist"].getFilename()) + + def exit(self): + self.close(False) + +(ALLIMAGES, RELEASE, EXPERIMENTAL, STICK_WIZARD, START) = range(5) + +class NFIDownload(Screen): + skin = """ + <screen name="NFIDownload" position="center,center" size="610,410" title="NFIDownload" > + <ePixmap pixmap="skin_default/buttons/red.png" position="0,0" size="140,40" alphatest="on" /> + <ePixmap pixmap="skin_default/buttons/green.png" position="140,0" size="140,40" alphatest="on" /> + <ePixmap pixmap="skin_default/buttons/yellow.png" position="280,0" size="140,40" alphatest="on" /> + <ePixmap pixmap="skin_default/buttons/blue.png" position="420,0" size="140,40" alphatest="on" /> + <widget source="key_red" render="Label" position="0,0" zPosition="1" size="140,40" font="Regular;20" valign="center" halign="center" backgroundColor="#9f1313" transparent="1" /> + <widget source="key_green" render="Label" position="140,0" zPosition="1" size="140,40" font="Regular;20" valign="center" halign="center" backgroundColor="#1f771f" transparent="1" /> + <widget source="key_yellow" render="Label" position="280,0" zPosition="1" size="140,40" font="Regular;20" valign="center" halign="center" backgroundColor="#a08500" transparent="1" /> + <widget source="key_blue" render="Label" position="420,0" zPosition="1" size="140,40" font="Regular;20" valign="center" halign="center" backgroundColor="#18188b" transparent="1" /> + <ePixmap pixmap="skin_default/border_menu_350.png" position="5,50" zPosition="1" size="350,300" transparent="1" alphatest="on" /> + <widget source="menu" render="Listbox" position="15,60" size="330,290" scrollbarMode="showOnDemand"> + <convert type="TemplatedMultiContent"> + {"templates": + {"default": (25, [ + MultiContentEntryText(pos = (2, 2), size = (330, 24), flags = RT_HALIGN_LEFT, text = 1), # index 0 is the MenuText, + ], True, "showOnDemand") + }, + "fonts": [gFont("Regular", 22)], + "itemHeight": 25 + } + </convert> + </widget> + <widget source="menu" render="Listbox" position="360,50" size="240,300" scrollbarMode="showNever" selectionDisabled="1"> + <convert type="TemplatedMultiContent"> + {"templates": + {"default": (300, [ + MultiContentEntryText(pos = (2, 2), size = (240, 300), flags = RT_HALIGN_CENTER|RT_VALIGN_CENTER|RT_WRAP, text = 2), # index 2 is the Description, + ], False, "showNever") + }, + "fonts": [gFont("Regular", 22)], + "itemHeight": 300 + } + </convert> + </widget> + <widget source="status" render="Label" position="5,360" zPosition="10" size="600,50" halign="center" valign="center" font="Regular;22" transparent="1" shadowColor="black" shadowOffset="-1,-1" /> + </screen>""" - self["infolabel"] = StaticText() - self["statusbar"] = StaticText() - self["label_top"] = StaticText() - self["label_bottom"] = StaticText() - self["path_bottom"] = StaticText() + def __init__(self, session, destdir=None): + Screen.__init__(self, session) + #self.skin_path = plugin_path + #self.menu = args + self.box = HardwareInfo().get_device_name() + self.feed_base = "http://www.dreamboxupdate.com/opendreambox" #/1.5/%s/images/" % self.box + self.usbmountpoint = resolveFilename(SCOPE_MEDIA)+"usb/" + + self.menulist = [] + + self["menu"] = List(self.menulist) + self["key_red"] = StaticText(_("Close")) self["key_green"] = StaticText() self["key_yellow"] = StaticText() self["key_blue"] = StaticText() - self["key_red"] = StaticText() - - self["feedlist"] = Feedlist([0,(eListboxPythonMultiContent.TYPE_TEXT, 0, 0,250, 30, 0, RT_HALIGN_LEFT|RT_VALIGN_CENTER, "feed not available")]) - self["destlist"] = FileList(destdir, showDirectories = True, showFiles = False) - self["destlist"].hide() + self["status"] = StaticText(_("Please wait... Loading list...")) - self.download_container = eConsoleAppContainer() - self.nfo = "" - self.nfofile = "" - self.feedhtml = "" - self.focus = None - self.download = None - self.box = HardwareInfo().get_device_name() - self.feed_base = "http://www.dreamboxupdate.com/opendreambox/1.5/%s/images/" % self.box - self.nfi_filter = "" # "release" # only show NFIs containing this string, or all if "" - self.wizard_mode = False - - self["actions"] = ActionMap(["OkCancelActions", "ColorActions", "DirectionActions", "EPGSelectActions"], + self["shortcuts"] = ActionMap(["OkCancelActions", "ColorActions", "ShortcutActions", "DirectionActions"], { - "cancel": self.closeCB, - "red": self.closeCB, - "green": self.nfi_download, - "yellow": self.switchList, - "blue": self.askCreateUSBstick, - "prevBouquet": self.switchList, - "nextBouquet": self.switchList, - "ok": self.ok, - "left": self.left, - "right": self.right, - "up": self.up, - "upRepeated": self.up, - "downRepeated": self.down, - "down": self.down + "ok": self.keyOk, + "green": self.keyOk, + "red": self.keyRed, + "blue": self.keyBlue, + "up": self.keyUp, + "upRepeated": self.keyUp, + "downRepeated": self.keyDown, + "down": self.keyDown, + "cancel": self.close, }, -1) + self.onShown.append(self.go) + self.feedlists = [[],[],[]] + self.branch = START + self.container = eConsoleAppContainer() + self.container.dataAvail.append(self.tool_avail) + self.taskstring = "" + self.image_idx = 0 + self.nfofilename = "" + self.nfo = "" + self.target_dir = None - self.feed_download() + def tool_avail(self, string): + print "[tool_avail]" + string + self.taskstring += string - def downloading(self, state=True): - if state is True: - self["key_red"].text = _("Cancel") - self["key_green"].text = "" - self["key_yellow"].text = "" - self["key_blue"].text = "" + def go(self): + self.onShown.remove(self.go) + self.umountCallback = self.getMD5 + self.umount() + + def getMD5(self): + url = "http://www.dreamboxupdate.com/download/opendreambox/dreambox-nfiflasher-%s-md5sums" % self.box + client.getPage(url).addCallback(self.md5sums_finished).addErrback(self.feed_failed) + + def md5sums_finished(self, data): + print "[md5sums_finished]", data + self.stickimage_md5 = data + self.checkUSBStick() + + def keyRed(self): + if self.branch == START: + self.close() else: - self.download = None - self["key_red"].text = _("Exit") - if self["feedlist"].isValid(): - self["key_green"].text = (_("Download")) - if self.focus is self.LIST_SOURCE: - self["key_yellow"].text = (_("Change dir.")) - else: - self["key_yellow"].text = (_("Select image")) - self["key_blue"].text = (_("USB stick wizard")) - - def switchList(self,to_where=None): - if self.download or not self["feedlist"].isValid(): - return - - self["job_progressbar"].value = 0 - self["job_progresslabel"].text = "" - - if to_where is None: - if self.focus is self.LIST_SOURCE: - to_where = self.LIST_DEST - if self.focus is self.LIST_DEST: - to_where = self.LIST_SOURCE - - if to_where is self.LIST_DEST: - self.focus = self.LIST_DEST - self["statusbar"].text = _("Please select target directory or medium") - self["label_top"].text = _("choose destination directory")+":" - self["feedlist"].hide() - self["destlist"].show() - self["label_bottom"].text = _("Selected source image")+":" - self["path_bottom"].text = str(self["feedlist"].getNFIname()) - self["key_yellow"].text = (_("Select image")) - - elif to_where is self.LIST_SOURCE: - self.focus = self.LIST_SOURCE - self["statusbar"].text = _("Please choose .NFI image file from feed server to download") - self["label_top"].text = _("select image from server")+":" - self["feedlist"].show() - self["destlist"].hide() - self["label_bottom"].text = _("Destination directory")+":" - self["path_bottom"].text = str(self["destlist"].getCurrentDirectory()) - self["key_yellow"].text = (_("Change dir.")) - - def up(self): - if self.download: - return - if self.focus is self.LIST_SOURCE: - self["feedlist"].up() - self.nfo_download() - if self.focus is self.LIST_DEST: - self["destlist"].up() - - def down(self): - if self.download: - return - if self.focus is self.LIST_SOURCE: - self["feedlist"].down() - self.nfo_download() - if self.focus is self.LIST_DEST: - self["destlist"].down() - - def left(self): - if self.download: - return - if self.focus is self.LIST_SOURCE: - self["feedlist"].pageUp() - self.nfo_download() - if self.focus is self.LIST_DEST: - self["destlist"].pageUp() - - def right(self): - if self.download: - return - if self.focus is self.LIST_SOURCE: - self["feedlist"].pageDown() - self.nfo_download() - if self.focus is self.LIST_DEST: - self["destlist"].pageDown() + self.branch = START + self["menu"].setList(self.menulist) + #elif self.branch == ALLIMAGES or self.branch == STICK_WIZARD: - def ok(self): - if self.focus is self.LIST_SOURCE and self.nfo: + def keyBlue(self): + if self.nfo != "": self.session.open(NFOViewer, self.nfo) - if self.download: - return - if self.focus is self.LIST_DEST: - if self["destlist"].canDescent(): - self["destlist"].descent() - def feed_download(self): - self.downloading(True) - self.download = self.feed_download - client.getPage(self.feed_base).addCallback(self.feed_finished).addErrback(self.feed_failed) + def keyOk(self): + print "[keyOk]", self["menu"].getCurrent() + current = self["menu"].getCurrent() + if current: + if self.branch == START: + currentEntry = current[0] + if currentEntry == RELEASE: + self.image_idx = 0 + self.branch = RELEASE + self.askDestination() + elif currentEntry == EXPERIMENTAL: + self.image_idx = 0 + self.branch = EXPERIMENTAL + self.askDestination() + elif currentEntry == ALLIMAGES: + self.branch = ALLIMAGES + self.listImages() + elif currentEntry == STICK_WIZARD: + self.askStartWizard() + elif self.branch == ALLIMAGES: + self.image_idx = self["menu"].getIndex() + self.askDestination() + self.updateButtons() + + def keyUp(self): + self["menu"].selectPrevious() + self.updateButtons() + + def keyDown(self): + self["menu"].selectNext() + self.updateButtons() + + def updateButtons(self): + current = self["menu"].getCurrent() + if current: + if self.branch == START: + self["key_red"].text = _("Close") + currentEntry = current[0] + if currentEntry in (RELEASE, EXPERIMENTAL): + self.nfo_download(currentEntry, 0) + self["key_green"].text = _("Download") + else: + self.nfofilename = "" + self.nfo = "" + self["key_blue"].text = "" + self["key_green"].text = _("continue") + + elif self.branch == ALLIMAGES: + self["key_red"].text = _("Back") + self["key_green"].text = _("Download") + self.nfo_download(ALLIMAGES, self["menu"].getIndex()) + + def listImages(self): + print "[listImages]" + imagelist = [] + mask = re.compile("%s/(?P<OE_vers>1\.\d)/%s/images/(?P<branch>.*?)-%s_(?P<version>.*?).nfi" % (self.feed_base, self.box, self.box), re.DOTALL) + for name, url in self.feedlists[ALLIMAGES]: + result = mask.match(url) + if result: + if result.group("version").startswith("20"): + version = ( result.group("version")[:4]+'-'+result.group("version")[4:6]+'-'+result.group("version")[6:8] ) + else: + version = result.group("version") + description = "\nOpendreambox %s\n%s image\n%s\n" % (result.group("OE_vers"), result.group("branch"), version) + imagelist.append((url, name, _("Download %s from Server" ) % description, None)) + self["menu"].setList(imagelist) + + def getUSBPartitions(self): + allpartitions = [ (r.description, r.mountpoint) for r in harddiskmanager.getMountedPartitions(onlyhotplug = True)] + print "[getUSBPartitions]", allpartitions + usbpartition = [] + for x in allpartitions: + print x, x[1] == '/', x[0].find("USB"), access(x[1], R_OK) + if x[1] != '/' and x[0].find("USB") > -1: # and access(x[1], R_OK) is True: + usbpartition.append(x) + return usbpartition + + def askDestination(self): + usbpartition = self.getUSBPartitions() + if len(usbpartition) == 1: + self.target_dir = usbpartition[0][1] + self.ackDestinationDevice(device_description=usbpartition[0][0]) + else: + self.openDeviceBrowser() + + def openDeviceBrowser(self): + self.session.openWithCallback(self.DeviceBrowserClosed, DeviceBrowser, None, showDirectories=True, showMountpoints=True, inhibitMounts=["/autofs/sr0/"]) + + def DeviceBrowserClosed(self, path): + print "[DeviceBrowserClosed]", str(path) + self.target_dir = path + if path: + self.ackDestinationDevice() + else: + self.keyRed() + + def ackDestinationDevice(self, device_description=None): + if device_description == None: + dev = self.target_dir + else: + dev = device_description + message = _("Do you want to download the image to %s ?") % (dev) + choices = [(_("Yes"), self.ackedDestination), (_("List of Storage Devices"),self.openDeviceBrowser), (_("Cancel"),self.keyRed)] + self.session.openWithCallback(self.ackDestination_query, ChoiceBox, title=message, list=choices) + + def ackDestination_query(self, choice): + print "[ackDestination_query]", choice + if isinstance(choice, tuple): + choice[1]() + else: + self.keyRed() + + def ackedDestination(self): + print "[ackedDestination]", self.branch, self.target_dir + self.container.setCWD(resolveFilename(SCOPE_MEDIA)+"usb/") + if self.target_dir[:8] == "/autofs/": + self.target_dir = "/dev/" + self.target_dir[8:-1] + + if self.branch == STICK_WIZARD: + job = StickWizardJob(self.target_dir) + job.afterEvent = "close" + job_manager.AddJob(job) + job_manager.failed_jobs = [] + self.session.openWithCallback(self.StickWizardCB, JobView, job, afterEventChangeable = False) + + elif self.branch != STICK_WIZARD: + url = self.feedlists[self.branch][self.image_idx][1] + filename = self.feedlists[self.branch][self.image_idx][0] + print "[getImage] start downloading %s to %s" % (url, filename) + if self.target_dir.startswith("/dev/"): + job = ImageDownloadJob(url, filename, self.target_dir, self.usbmountpoint) + else: + job = ImageDownloadJob(url, filename, None, self.target_dir) + job.afterEvent = "close" + job_manager.AddJob(job) + job_manager.failed_jobs = [] + self.session.openWithCallback(self.ImageDownloadCB, JobView, job, afterEventChangeable = False) + + def StickWizardCB(self, ret=None): + print "[StickWizardCB]", ret +# print job_manager.active_jobs, job_manager.failed_jobs, job_manager.job_classes, job_manager.in_background, job_manager.active_job + if len(job_manager.failed_jobs) == 0: + self.session.open(MessageBox, _("The USB stick was prepared to be bootable.\nNow you can download an NFI image file!"), type = MessageBox.TYPE_INFO) + if len(self.feedlists[ALLIMAGES]) == 0: + self.getFeed() + else: + self.setMenu() + else: + self.umountCallback = self.checkUSBStick + self.umount() + + def ImageDownloadCB(self, ret): + print "[ImageDownloadCB]", ret +# print job_manager.active_jobs, job_manager.failed_jobs, job_manager.job_classes, job_manager.in_background, job_manager.active_job + if len(job_manager.failed_jobs) == 0: + self.session.openWithCallback(self.askBackupCB, MessageBox, _("The wizard can backup your current settings. Do you want to do a backup now?"), MessageBox.TYPE_YESNO) + else: + self.umountCallback = self.keyRed + self.umount() - def feed_failed(self, failure_instance): - print "[feed_failed] " + str(failure_instance) - self["infolabel"].text = _("Could not connect to Dreambox .NFI Image Feed Server:") + "\n" + failure_instance.getErrorMessage() + "\n\n" + _("Please check your network settings!") - self.downloading(False) + def askBackupCB(self, ret): + if ret: + from Plugins.SystemPlugins.SoftwareManager.BackupRestore import BackupScreen - def feed_finished(self, feedhtml): - print "[feed_finished] " + str(feedhtml) - self.downloading(False) - fileresultmask = re.compile("<a class=[\'\"]nfi[\'\"] href=[\'\"](?P<url>.*?)[\'\"]>(?P<name>.*?.nfi)</a>", re.DOTALL) - searchresults = fileresultmask.finditer(feedhtml) - fileresultlist = [] - if searchresults: - for x in searchresults: - url = x.group("url") - if url[0:7] != "http://": - url = self.feed_base + x.group("url") - name = x.group("name") - if name.find(self.nfi_filter) > -1: - entry = [[name, url],(eListboxPythonMultiContent.TYPE_TEXT, 0, 0,250, 30, 0, RT_HALIGN_LEFT|RT_VALIGN_CENTER, name)] - print "adding to feedlist: " + str(entry) - fileresultlist.append(entry) - else: - print "NOT adding to feedlist: " + name - self["feedlist"].l.setList(fileresultlist) - self["feedlist"].moveSelection(0) + class USBBackupScreen(BackupScreen): + def __init__(self, session, usbmountpoint): + BackupScreen.__init__(self, session, runBackup = True) + self.backuppath = usbmountpoint + self.fullbackupfilename = self.backuppath + "/" + self.backupfile - if len(fileresultlist) > 0: - self.switchList(self.LIST_SOURCE) - self.nfo_download() + self.session.openWithCallback(self.showHint, USBBackupScreen, self.usbmountpoint) else: - self["infolabel"].text = _("Cannot parse feed directory") - - def nfo_download(self): - print "[check_for_NFO]" - if self["feedlist"].isValid(): - print "nfiname: " + self["feedlist"].getNFIname() - self["job_progressbar"].value = 0 - self["job_progresslabel"].text = "" - if self["feedlist"].getNFIurl() is None: - self["key_green"].text = "" - return - self["key_green"].text = _("Download") - nfourl = self["feedlist"].getNFOurl() - print "downloading " + nfourl - self.download = self.nfo_download - self.downloading(True) - client.getPage(nfourl).addCallback(self.nfo_finished).addErrback(self.nfo_failed) - self["statusbar"].text = ("Downloading image description...") + self.showHint() + + def showHint(self, ret=None): + self.session.open(MessageBox, _("To update your Dreambox firmware, please follow these steps:\n1) Turn off your box with the rear power switch and make sure the bootable USB stick is plugged in.\n2) Turn mains back on and hold the DOWN button on the front panel pressed for 10 seconds.\n3) Wait for bootup and follow instructions of the wizard."), type = MessageBox.TYPE_INFO) + self.umountCallback = self.keyRed + self.umount() + + def getFeed(self): + self.feedDownloader15 = feedDownloader(self.feed_base, self.box, OE_vers="1.5") + self.feedDownloader16 = feedDownloader(self.feed_base, self.box, OE_vers="1.6") + self.feedlists = [[],[],[]] + self.feedDownloader15.getList(self.gotFeed, self.feed_failed) + self.feedDownloader16.getList(self.gotFeed, self.feed_failed) + + def feed_failed(self, message=""): + self["status"].text = _("Could not connect to Dreambox .NFI Image Feed Server:") + "\n" + str(message) + "\n" + _("Please check your network settings!") + + def gotFeed(self, feedlist, OE_vers): + print "[gotFeed]", OE_vers + releaselist = [] + experimentallist = [] + + for name, url in feedlist: + if name.find("release") > -1: + releaselist.append((name, url)) + if name.find("experimental") > -1: + experimentallist.append((name, url)) + self.feedlists[ALLIMAGES].append((name, url)) + + if OE_vers == "1.6": + self.feedlists[RELEASE] = releaselist + self.feedlists[RELEASE] + self.feedlists[EXPERIMENTAL] = experimentallist + self.feedlists[RELEASE] + elif OE_vers == "1.5": + self.feedlists[RELEASE] = self.feedlists[RELEASE] + releaselist + self.feedlists[EXPERIMENTAL] = self.feedlists[EXPERIMENTAL] + experimentallist + + self.setMenu() + + def checkUSBStick(self): + self.target_dir = None + allpartitions = [ (r.description, r.mountpoint) for r in harddiskmanager.getMountedPartitions(onlyhotplug = True)] + print "[checkUSBStick] found partitions:", allpartitions + usbpartition = [] + for x in allpartitions: + print x, x[1] == '/', x[0].find("USB"), access(x[1], R_OK) + if x[1] != '/' and x[0].find("USB") > -1: # and access(x[1], R_OK) is True: + usbpartition.append(x) + + print usbpartition + if len(usbpartition) == 1: + self.target_dir = usbpartition[0][1] + self.md5_passback = self.getFeed + self.md5_failback = self.askStartWizard + self.md5verify(self.stickimage_md5, self.target_dir) + elif usbpartition == []: + print "[NFIFlash] needs to create usb flasher stick first!" + self.askStartWizard() + else: + self.askStartWizard() + + def askStartWizard(self): + self.branch = STICK_WIZARD + message = _("""This plugin creates a USB stick which can be used to update the firmware of your Dreambox without the need for a network or WLAN connection. +First, a USB stick needs to be prepared so that it becomes bootable. +In the next step, an NFI image file can be downloaded from the update server and saved on the USB stick. +If you already have a prepared bootable USB stick, please insert it now. Otherwise plug in a USB stick with a minimum size of 64 MB!""") + self.session.openWithCallback(self.wizardDeviceBrowserClosed, DeviceBrowser, None, message, showDirectories=True, showMountpoints=True, inhibitMounts=["/","/autofs/sr0/","/autofs/sda1/","/media/hdd/","/media/net/",self.usbmountpoint,"/media/dvd/"]) + + def wizardDeviceBrowserClosed(self, path): + print "[wizardDeviceBrowserClosed]", path + self.target_dir = path + if path: + self.md5_passback = self.getFeed + self.md5_failback = self.wizardQuery + self.md5verify(self.stickimage_md5, self.target_dir) + else: + self.close() + + def wizardQuery(self): + print "[wizardQuery]" + description = self.target_dir + for name, dev in self.getUSBPartitions(): + if dev == self.target_dir: + description = name + message = _("You have chosen to create a new .NFI flasher bootable USB stick. This will repartition the USB stick and therefore all data on it will be erased.") + "\n" + message += _("The following device was found:\n\n%s\n\nDo you want to write the USB flasher to this stick?") % description + choices = [(_("Yes"), self.ackedDestination), (_("List of Storage Devices"),self.askStartWizard), (_("Cancel"),self.close)] + self.session.openWithCallback(self.ackDestination_query, ChoiceBox, title=message, list=choices) + + def setMenu(self): + self.menulist = [] + try: + latest_release = "Release %s (Opendreambox 1.5)" % self.feedlists[RELEASE][0][0][-9:-4] + self.menulist.append((RELEASE, _("Get latest release image"), _("Download %s from Server" ) % latest_release, None)) + except IndexError: + pass + + try: + dat = self.feedlists[EXPERIMENTAL][0][0][-12:-4] + latest_experimental = "Experimental %s-%s-%s (Opendreambox 1.6)" % (dat[:4], dat[4:6], dat[6:]) + self.menulist.append((EXPERIMENTAL, _("Get latest experimental image"), _("Download %s from Server") % latest_experimental, None)) + except IndexError: + pass + + self.menulist.append((ALLIMAGES, _("Choose image to download"), _("Select desired image from feed list" ), None)) + self.menulist.append((STICK_WIZARD, _("USB stick wizard"), _("Prepare another USB stick for image flashing" ), None)) + self["menu"].setList(self.menulist) + self["status"].text = _("Currently installed image") + ": %s" % (about.getImageVersionString()) + self.branch = START + self.updateButtons() + + def nfo_download(self, branch, idx): + nfourl = (self.feedlists[branch][idx][1])[:-4]+".nfo" + self.nfofilename = (self.feedlists[branch][idx][0])[:-4]+".nfo" + print "[check_for_NFO]", nfourl + client.getPage(nfourl).addCallback(self.nfo_finished).addErrback(self.nfo_failed) def nfo_failed(self, failure_instance): print "[nfo_failed] " + str(failure_instance) - self["infolabel"].text = _("No details for this image file") + "\n" + self["feedlist"].getNFIname() - self["statusbar"].text = "" + self["key_blue"].text = "" self.nfofilename = "" self.nfo = "" - self.downloading(False) def nfo_finished(self,nfodata=""): print "[nfo_finished] " + str(nfodata) - self.downloading(False) + self["key_blue"].text = _("Changelog") self.nfo = nfodata - if self.nfo != "": - self.nfofilename = self["destlist"].getCurrentDirectory() + '/' + self["feedlist"].getNFOname() - self["infolabel"].text = self.nfo - else: - self.nfofilename = "" - self["infolabel"].text = _("No details for this image file") - self["statusbar"].text = _("Press OK to view full changelog") - - def nfi_download(self): - if self["destlist"].getCurrentDirectory() is None: - self.switchList(self.LIST_TARGET) - if self["feedlist"].isValid(): - url = self["feedlist"].getNFIurl() - self.nfilocal = self["destlist"].getCurrentDirectory()+'/'+self["feedlist"].getNFIname() - print "[nfi_download] downloading %s to %s" % (url, self.nfilocal) - self.download = downloadWithProgress(url,self.nfilocal) - self.download.addProgress(self.nfi_progress) - self["job_progressbar"].range = 1000 - self.download.start().addCallback(self.nfi_finished).addErrback(self.nfi_failed) - self.downloading(True) - - def nfi_progress(self, recvbytes, totalbytes): - #print "[update_progress] recvbytes=%d, totalbytes=%d" % (recvbytes, totalbytes) - self["job_progressbar"].value = int(1000*recvbytes/float(totalbytes)) - self["job_progresslabel"].text = "%d of %d kBytes (%.2f%%)" % (recvbytes/1024, totalbytes/1024, 100*recvbytes/float(totalbytes)) - def nfi_failed(self, failure_instance=None, error_message=""): - if error_message == "" and failure_instance is not None: - error_message = failure_instance.getErrorMessage() - print "[nfi_failed] " + error_message - if fileExists(self["destlist"].getCurrentDirectory()+'/'+self["feedlist"].getNFIname()): - message = "%s %s\n%s" % (_(".NFI Download failed:"), error_message, _("Remove the incomplete .NFI file?")) - self.session.openWithCallback(self.nfi_remove, MessageBox, message, MessageBox.TYPE_YESNO) - else: - message = "%s %s" % (_(".NFI Download failed:"),error_message) - self.session.open(MessageBox, message, MessageBox.TYPE_ERROR) - self.downloading(False) - - def nfi_finished(self, string=""): - print "[nfi_finished] " + str(string) - if self.nfo != "": - self.nfofilename = self["destlist"].getCurrentDirectory() + '/' + self["feedlist"].getNFOname() - nfofd = open(self.nfofilename, "w") - if nfofd: - nfofd.write(self.nfo) - nfofd.close() - else: - print "couldn't save nfo file " + self.nfofilename - - pos = self.nfo.find("MD5:") - if pos > 0 and len(self.nfo) >= pos+5+32: - self["statusbar"].text = ("Please wait for md5 signature verification...") - cmd = "md5sum -c -" - md5 = self.nfo[pos+5:pos+5+32] + " " + self.nfilocal - print cmd, md5 - self.download_container.setCWD(self["destlist"].getCurrentDirectory()) - self.download_container.appClosed.append(self.md5finished) - self.download_container.execute(cmd) - self.download_container.write(md5) - self.download_container.dataSent.append(self.md5ready) - else: - self["statusbar"].text = "Download completed." - self.downloading(False) - else: - self["statusbar"].text = "Download completed." - self.downloading(False) - if self.wizard_mode: - self.configBackup() + def md5verify(self, md5, path): + cmd = "md5sum -c -s" + print "[verify_md5]", md5, path, cmd + self.container.setCWD(path) + self.container.appClosed.append(self.md5finished) + self.container.execute(cmd) + self.container.write(md5) + self.container.dataSent.append(self.md5ready) def md5ready(self, retval): - self.download_container.sendEOF() + self.container.sendEOF() def md5finished(self, retval): - print "[md5finished]: " + str(retval) - self.download_container.appClosed.remove(self.md5finished) + print "[md5finished]", str(retval) + self.container.appClosed.remove(self.md5finished) + self.container.dataSent.remove(self.md5ready) if retval==0: - self.downloading(False) - if self.wizard_mode: - self.configBackup() - else: - self["statusbar"].text = _(".NFI file passed md5sum signature check. You can safely flash this image!") - self.switchList(self.LIST_SOURCE) + print "check passed! calling", repr(self.md5_passback) + self.md5_passback() else: - self.session.openWithCallback(self.nfi_remove, MessageBox, (_("The md5sum validation failed, the file may be downloaded incompletely or be corrupted!") + "\n" + _("Remove the broken .NFI file?")), MessageBox.TYPE_YESNO) - - def nfi_remove(self, answer): - self.downloading(False) - if answer == True: - nfifilename = self["destlist"].getCurrentDirectory()+'/'+self["feedlist"].getNFIname() - if fileExists(self.nfofilename): - remove(self.nfofilename) - if fileExists(nfifilename): - remove(nfifilename) - self.switchList(self.LIST_SOURCE) - - def askCreateUSBstick(self): - self.downloading() - self.imagefilename = "/tmp/nfiflash_" + self.box + ".img" - message = _("You have chosen to create a new .NFI flasher bootable USB stick. This will repartition the USB stick and therefore all data on it will be erased.") - self.session.openWithCallback(self.flasherdownload_query, MessageBox, (message + '\n' + _("First we need to download the latest boot environment for the USB flasher.")), MessageBox.TYPE_YESNO) - - def flasherdownload_query(self, answer): - if answer is False: - self.downloading(False) - self.switchList(self.LIST_SOURCE) - return - #url = self.feed_base + "/nfiflasher_" + self.box + ".tar.bz2" - url = "http://www.dreamboxupdate.com/download/opendreambox/dreambox-nfiflasher-%s.tar.bz2" % self.box - localfile = "/tmp/nfiflasher_image.tar.bz2" - print "[flasherdownload_query] downloading %s to %s" % (url, localfile) - self["statusbar"].text = ("Downloading %s..." % url) - self.download = downloadWithProgress(url,localfile) - self.download.addProgress(self.nfi_progress) - self["job_progressbar"].range = 1000 - self.download.start().addCallback(self.flasherdownload_finished).addErrback(self.flasherdownload_failed) - - def flasherdownload_failed(self, failure_instance=None, error_message=""): - if error_message == "" and failure_instance is not None: - error_message = failure_instance.getErrorMessage() - print "[flasherdownload_failed] " + error_message - message = "%s %s" % (_("Download of USB flasher boot image failed: "),error_message) - self.session.open(MessageBox, message, MessageBox.TYPE_ERROR) - self.remove_img(True) - - def flasherdownload_finished(self, string=""): - print "[flasherdownload_finished] " + str(string) - self.container = eConsoleAppContainer() - self.container.appClosed.append(self.umount_finished) - self.container.dataAvail.append(self.tool_avail) - self.taskstring = "" - umountdevs = "" - from os import listdir - for device in listdir("/dev"): - if device[:2] == "sd" and device[-1:].isdigit(): - umountdevs += "/dev/"+device - self.cmd = "umount " + umountdevs - print "executing " + self.cmd - self.container.execute(self.cmd) - - def tool_avail(self, string): - print "[tool_avail]" + string - self.taskstring += string - - def umount_finished(self, retval): - self.container.appClosed.remove(self.umount_finished) - self.container.appClosed.append(self.dmesg_cleared) - self.taskstring = "" - self.cmd = "dmesg -c" - print "executing " + self.cmd - self.container.execute(self.cmd) - - def dmesg_cleared(self, answer): - self.container.appClosed.remove(self.dmesg_cleared) - self.msgbox = self.session.open(MessageBox, _("Please disconnect all USB devices from your Dreambox and (re-)attach the target USB stick (minimum size is 64 MB) now!"), MessageBox.TYPE_INFO) - hotplugNotifier.append(self.hotplugCB) + print "check failed! calling", repr(self.md5_failback) + self.md5_failback() - def hotplugCB(self, dev, action): - print "[hotplugCB]", dev, action - if dev.startswith("sd") and action == "add": - self.msgbox.close() - hotplugNotifier.remove(self.hotplugCB) - self.container.appClosed.append(self.dmesg_scanned) - self.taskstring = "" - self.cmd = "dmesg" - print "executing " + self.cmd - self.container.execute(self.cmd) - - def dmesg_scanned(self, retval): - self.container.appClosed.remove(self.dmesg_scanned) - dmesg_lines = self.taskstring.splitlines() - self.devicetext = None - self.stickdevice = None - for i, line in enumerate(dmesg_lines): - if line.find("usb-storage: waiting for device") != -1 and len(dmesg_lines) > i+3: - self.devicetext = dmesg_lines[i+1].lstrip()+"\n"+dmesg_lines[i+3] - elif line.find("/dev/scsi/host") != -1: - self.stickdevice = line.split(":",1)[0].lstrip() - - if retval != 0 or self.devicetext is None or self.stickdevice is None: - self.session.openWithCallback(self.remove_img, MessageBox, _("No useable USB stick found"), MessageBox.TYPE_ERROR) - else: - self.session.openWithCallback(self.fdisk_query, MessageBox, (_("The following device was found:\n\n%s\n\nDo you want to write the USB flasher to this stick?") % self.devicetext), MessageBox.TYPE_YESNO) - - def fdisk_query(self, answer): - if answer == True and self.stickdevice: - self["statusbar"].text = ("Partitioning USB stick...") - self["job_progressbar"].range = 1000 - self["job_progressbar"].value = 100 - self["job_progresslabel"].text = "5.00%" - self.taskstring = "" - self.container.appClosed.append(self.fdisk_finished) - self.container.execute("fdisk " + self.stickdevice + "/disc") - self.container.write("d\n4\nd\n3\nd\n2\nd\nn\np\n1\n\n\nt\n6\nw\n") - self.delayTimer = eTimer() - self.delayTimer.callback.append(self.progress_increment) - self.delayTimer.start(105, False) - else: - self.remove_img(True) - - def fdisk_finished(self, retval): - self.container.appClosed.remove(self.fdisk_finished) - self.delayTimer.stop() - if retval == 0: - if fileExists(self.imagefilename): - self.tar_finished(0) - self["job_progressbar"].value = 700 - else: - self["statusbar"].text = ("Decompressing USB stick flasher boot image...") - self.taskstring = "" - self.container.appClosed.append(self.tar_finished) - self.container.setCWD("/tmp") - self.cmd = "tar -xjvf nfiflasher_image.tar.bz2" - self.container.execute(self.cmd) - print "executing " + self.cmd - self.delayTimer = eTimer() - self.delayTimer.callback.append(self.progress_increment) - self.delayTimer.start(105, False) - else: - print "fdisk failed: " + str(retval) - self.session.openWithCallback(self.remove_img, MessageBox, ("fdisk " + _("failed") + ":\n" + str(self.taskstring)), MessageBox.TYPE_ERROR) + def umount(self): + cmd = "umount " + self.usbmountpoint + print "[umount]", cmd + self.container.setCWD('/') + self.container.appClosed.append(self.umountFinished) + self.container.execute(cmd) - def progress_increment(self): - newval = int(self["job_progressbar"].value) + 1 - if newval < 950: - self["job_progressbar"].value = newval - self["job_progresslabel"].text = "%.2f%%" % (newval/10.0) - - def tar_finished(self, retval): - self.delayTimer.stop() - if len(self.container.appClosed) > 0: - self.container.appClosed.remove(self.tar_finished) - if retval == 0: - self.imagefilename = "/tmp/nfiflash_" + self.box + ".img" - self["statusbar"].text = ("Copying USB flasher boot image to stick...") - self.taskstring = "" - self.container.appClosed.append(self.dd_finished) - self.cmd = "dd if=%s of=%s" % (self.imagefilename,self.stickdevice+"/part1") - self.container.execute(self.cmd) - print "executing " + self.cmd - self.delayTimer = eTimer() - self.delayTimer.callback.append(self.progress_increment) - self.delayTimer.start(105, False) - else: - self.session.openWithCallback(self.remove_img, MessageBox, (self.cmd + " " + _("failed") + ":\n" + str(self.taskstring)), MessageBox.TYPE_ERROR) - - def dd_finished(self, retval): - self.delayTimer.stop() - self.container.appClosed.remove(self.dd_finished) - self.downloading(False) - if retval == 0: - self["job_progressbar"].value = 950 - self["job_progresslabel"].text = "95.00%" - self["statusbar"].text = ("Remounting stick partition...") - self.taskstring = "" - self.container.appClosed.append(self.mount_finished) - self.cmd = "mount %s /mnt/usb -o rw,sync" % (self.stickdevice+"/part1") - self.container.execute(self.cmd) - print "executing " + self.cmd - else: - self.session.openWithCallback(self.remove_img, MessageBox, (self.cmd + " " + _("failed") + ":\n" + str(self.taskstring)), MessageBox.TYPE_ERROR) - - def mount_finished(self, retval): - self.container.dataAvail.remove(self.tool_avail) - self.container.appClosed.remove(self.mount_finished) - if retval == 0: - self["job_progressbar"].value = 1000 - self["job_progresslabel"].text = "100.00%" - self["statusbar"].text = (".NFI Flasher bootable USB stick successfully created.") - self.session.openWithCallback(self.flasherFinishedCB, MessageBox, _("The USB stick is now bootable. Do you want to download the latest image from the feed server and save it on the stick?"), type = MessageBox.TYPE_YESNO) - self["destlist"].changeDir("/mnt/usb") - else: - self.session.openWithCallback(self.flasherFinishedCB, MessageBox, (self.cmd + " " + _("failed") + ":\n" + str(self.taskstring)), MessageBox.TYPE_ERROR) - self.remove_img(True) - - def remove_img(self, answer): - if fileExists("/tmp/nfiflasher_image.tar.bz2"): - remove("/tmp/nfiflasher_image.tar.bz2") - if fileExists(self.imagefilename): - remove(self.imagefilename) - self.downloading(False) - self.switchList(self.LIST_SOURCE) - - def flasherFinishedCB(self, answer): - if answer == True: - self.wizard_mode = True - self["feedlist"].moveSelection(0) - self["path_bottom"].text = str(self["destlist"].getCurrentDirectory()) - self.nfo_download() - self.nfi_download() - - def configBackup(self): - self.session.openWithCallback(self.runBackup, MessageBox, _("The wizard can backup your current settings. Do you want to do a backup now?")) - - def runBackup(self, result=None): - from Tools.Directories import createDir, isMount, pathExists - from time import localtime - from datetime import date - from Screens.Console import Console - if result: - if isMount("/mnt/usb/"): - if (pathExists("/mnt/usb/backup") == False): - createDir("/mnt/usb/backup", True) - d = localtime() - dt = date(d.tm_year, d.tm_mon, d.tm_mday) - self.backup_file = "backup/" + str(dt) + "_settings_backup.tar.gz" - self.session.open(Console, title = "Backup running", cmdlist = ["tar -czvf " + "/mnt/usb/" + self.backup_file + " /etc/enigma2/ /etc/network/interfaces /etc/wpa_supplicant.conf"], finishedCallback = self.backup_finished, closeOnSuccess = True) - else: - self.backup_file = None - self.backup_finished(skipped=True) - - def backup_finished(self, skipped=False): - if not skipped: - wizardfd = open("/mnt/usb/wizard.nfo", "w") - if wizardfd: - wizardfd.write("image: "+self["feedlist"].getNFIname()+'\n') - wizardfd.write("configuration: "+self.backup_file+'\n') - wizardfd.close() - self.session.open(MessageBox, _("To update your Dreambox firmware, please follow these steps:\n1) Turn off your box with the rear power switch and plug in the bootable USB stick.\n2) Turn mains back on and hold the DOWN button on the front panel pressed for 10 seconds.\n3) Wait for bootup and follow instructions of the wizard."), type = MessageBox.TYPE_INFO) - - def closeCB(self): - try: - self.download.stop() - #self.nfi_failed(None, "Cancelled by user request") - self.downloading(False) - except AttributeError: - self.close() + def umountFinished(self, retval): + print "[umountFinished]", str(retval) + self.container.appClosed.remove(self.umountFinished) + self.umountCallback() def main(session, **kwargs): - session.open(NFIDownload,"/home/root") + session.open(NFIDownload,resolveFilename(SCOPE_HDD)) def filescan_open(list, session, **kwargs): dev = "/dev/" + (list[0].path).rsplit('/',1)[0][7:] - print "mounting device " + dev + " to /mnt/usb..." - system("mount "+dev+" /mnt/usb/ -o rw,sync") - session.open(NFIDownload,"/mnt/usb/") + print "mounting device " + dev + " to /media/usb..." + usbmountpoint = resolveFilename(SCOPE_MEDIA)+"usb/" + system("mount %s %s -o rw,sync" % (dev, usbmountpoint)) + session.open(NFIDownload,usbmountpoint) def filescan(**kwargs): from Components.Scanner import Scanner, ScanPath diff --git a/lib/python/Plugins/SystemPlugins/NFIFlash/flasher.py b/lib/python/Plugins/SystemPlugins/NFIFlash/flasher.py index 860efc02..3504d4cb 100644..100755 --- a/lib/python/Plugins/SystemPlugins/NFIFlash/flasher.py +++ b/lib/python/Plugins/SystemPlugins/NFIFlash/flasher.py @@ -2,31 +2,62 @@ from Screens.Screen import Screen from Screens.MessageBox import MessageBox from Screens.ChoiceBox import ChoiceBox from Screens.Standby import TryQuitMainloop +from Screens.Console import Console from Components.ActionMap import ActionMap from Components.Sources.StaticText import StaticText from Components.Sources.Progress import Progress from Components.Sources.Boolean import Boolean from Components.Label import Label from Components.FileList import FileList -from Components.Task import Task, Job, JobManager +from Components.Task import Task, Job, job_manager, Condition +from Screens.TaskView import JobView from Tools.Directories import fileExists from Tools.HardwareInfo import HardwareInfo from os import system -from enigma import eConsoleAppContainer -import re +from enigma import eConsoleAppContainer, quitMainloop, eEnv +from Components.About import about + +class md5Postcondition(Condition): + def check(self, task): + print "md5Postcondition::check", task.returncode + return task.returncode == 0 + + def getErrorMessage(self, task): + if task.returncode == 1: + return _("The md5sum validation failed, the file may be corrupted!") + return "md5 error" + +class md5verify(Task): + def __init__(self, job, path, md5): + Task.__init__(self, job, "md5sum") + self.postconditions.append(md5Postcondition()) + self.weighting = 5 + self.cwd = path + self.setTool("md5sum") + self.args += ["-c", "-s"] + self.initial_input = md5 + + def writeInput(self, input): + self.container.dataSent.append(self.md5ready) + print "[writeInput]", input + Task.writeInput(self, input) + + def md5ready(self, retval): + self.container.sendEOF() + + def processOutput(self, data): + print "[md5sum]", class writeNAND(Task): - def __init__(self,job,param,box): + def __init__(self, job, param, box): Task.__init__(self,job, ("Writing image file to NAND Flash")) - self.setTool("/usr/lib/enigma2/python/Plugins/SystemPlugins/NFIFlash/mywritenand") + self.setTool(eEnv.resolve("${libdir}/enigma2/python/Plugins/SystemPlugins/NFIFlash/writenfi-mipsel-2.6.18-r1")) if box == "dm7025": self.end = 256 elif box[:5] == "dm800": self.end = 512 - if box == "dm8000": - self.setTool("/usr/lib/enigma2/python/Plugins/SystemPlugins/NFIFlash/dm8000_writenand") self.args += param - self.weighting = 1 + self.weighting = 95 def processOutput(self, data): print "[writeNand] " + data @@ -38,113 +69,107 @@ class writeNAND(Task): else: self.output_line = data -class NFISummary(Screen): - skin = """ - <screen position="0,0" size="132,64"> - <widget source="title" render="Label" position="2,0" size="120,14" valign="center" font="Regular;12" /> - <widget source="content" render="Label" position="2,14" size="120,34" font="Regular;12" transparent="1" zPosition="1" /> - <widget source="job_progresslabel" render="Label" position="66,50" size="60,14" font="Regular;12" transparent="1" halign="right" zPosition="0" /> - <widget source="job_progressbar" render="Progress" position="2,50" size="66,14" borderWidth="1" /> - </screen>""" - - def __init__(self, session, parent): - Screen.__init__(self, session, parent) - self["title"] = StaticText(_("Image flash utility")) - self["content"] = StaticText(_("Please select .NFI flash image file from medium")) - self["job_progressbar"] = Progress() - self["job_progresslabel"] = StaticText("") - - def setText(self, text): - self["content"].setText(text) - class NFIFlash(Screen): skin = """ - <screen name="NFIFlash" position="90,95" size="560,420" title="Image flash utility"> - <ePixmap pixmap="skin_default/buttons/green.png" position="140,0" zPosition="0" size="140,40" transparent="1" alphatest="on" /> - <ePixmap pixmap="skin_default/buttons/yellow.png" position="280,0" zPosition="0" size="140,40" transparent="1" alphatest="on" /> - <ePixmap pixmap="skin_default/buttons/blue.png" position="420,0" zPosition="0" size="140,40" transparent="1" alphatest="on" /> - <widget source="key_green" render="Label" position="140,0" zPosition="1" size="140,40" font="Regular;20" valign="center" halign="center" backgroundColor="#1f771f" transparent="1" /> - <widget source="key_yellow" render="Label" position="280,0" zPosition="1" size="140,40" font="Regular;20" valign="center" halign="center" backgroundColor="#a08500" transparent="1" /> - <widget source="key_blue" render="Label" position="420,0" zPosition="1" size="140,40" font="Regular;20" valign="center" halign="center" backgroundColor="#18188b" transparent="1" /> - <widget source="listlabel" render="Label" position="16,44" size="200,21" valign="center" font="Regular;18" /> - <widget name="filelist" position="0,68" size="260,260" scrollbarMode="showOnDemand" /> - <widget source="infolabel" render="Label" position="270,44" size="280,284" font="Regular;16" /> - <widget source="job_progressbar" render="Progress" position="10,374" size="540,26" borderWidth="1" backgroundColor="#254f7497" /> - <widget source="job_progresslabel" render="Label" position="180,378" zPosition="2" font="Regular;18" halign="center" transparent="1" size="200,22" foregroundColor="#000000" /> - <widget source="statusbar" render="Label" position="10,404" size="540,16" font="Regular;16" foregroundColor="#cccccc" /> - </screen>""" + <screen name="NFIFlash" position="center,center" size="610,410" title="Image flash utility" > + <ePixmap pixmap="skin_default/buttons/red.png" position="0,0" size="140,40" alphatest="on" /> + <ePixmap pixmap="skin_default/buttons/green.png" position="140,0" size="140,40" alphatest="on" /> + <ePixmap pixmap="skin_default/buttons/yellow.png" position="280,0" size="140,40" alphatest="on" /> + <ePixmap pixmap="skin_default/buttons/blue.png" position="420,0" size="140,40" alphatest="on" /> + <widget source="key_red" render="Label" position="0,0" zPosition="1" size="140,40" font="Regular;20" valign="center" halign="center" backgroundColor="#9f1313" transparent="1" /> + <widget source="key_green" render="Label" position="140,0" zPosition="1" size="140,40" font="Regular;20" valign="center" halign="center" backgroundColor="#1f771f" transparent="1" /> + <widget source="key_yellow" render="Label" position="280,0" zPosition="1" size="140,40" font="Regular;20" valign="center" halign="center" backgroundColor="#a08500" transparent="1" /> + <widget source="key_blue" render="Label" position="420,0" zPosition="1" size="140,40" font="Regular;20" valign="center" halign="center" backgroundColor="#18188b" transparent="1" /> + <ePixmap pixmap="skin_default/border_menu_350.png" position="5,50" zPosition="1" size="350,300" transparent="1" alphatest="on" /> + <widget name="filelist" position="15,60" size="330,284" scrollbarMode="showOnDemand" /> + <widget source="infolabel" render="Label" position="360,50" size="240,300" font="Regular;13" /> + <widget source="status" render="Label" position="5,360" zPosition="10" size="600,50" halign="center" valign="center" font="Regular;22" transparent="1" shadowColor="black" shadowOffset="-1,-1" /> + </screen>""" - def __init__(self, session, cancelable = True, close_on_finish = False): - self.skin = NFIFlash.skin + def __init__(self, session, destdir=None): Screen.__init__(self, session) - self["job_progressbar"] = Progress() - self["job_progresslabel"] = StaticText("") - - self["finished"] = Boolean() + self.box = HardwareInfo().get_device_name() + self.usbmountpoint = "/mnt/usb/" - self["infolabel"] = StaticText("") - self["statusbar"] = StaticText(_("Please select .NFI flash image file from medium")) - self["listlabel"] = StaticText(_("select .NFI flash file")+":") - + self["key_red"] = StaticText() self["key_green"] = StaticText() self["key_yellow"] = StaticText() self["key_blue"] = StaticText() + self.filelist = FileList(self.usbmountpoint, matchingPattern = "^.*\.(nfi|NFI)", showDirectories = False, showMountpoints = False) + self["filelist"] = self.filelist + self["infolabel"] = StaticText() + + self["status"] = StaticText(_("Please select an NFI file and press green key to flash!") + '\n' + _("currently installed image: %s") % (about.getImageVersionString())) + self.job = None - self["actions"] = ActionMap(["OkCancelActions", "ColorActions", "DirectionActions"], + self["shortcuts"] = ActionMap(["OkCancelActions", "ColorActions", "ShortcutActions", "DirectionActions"], { - "green": self.ok, + "ok": self.keyOk, + "green": self.keyOk, + "up": self.keyUp, + "upRepeated": self.keyUp, + "downRepeated": self.keyDown, + "down": self.keyDown, + "left": self.keyLeft, "yellow": self.reboot, - "ok": self.ok, - "left": self.left, - "right": self.right, - "up": self.up, - "down": self.down + "right": self.keyRight }, -1) - - currDir = "/media/usb/" - self.filelist = FileList(currDir, matchingPattern = "^.*\.(nfi|NFI)") - self["filelist"] = self.filelist - self.nfifile = "" self.md5sum = "" - self.job = None - self.box = HardwareInfo().get_device_name() + self.onShown.append(self.autostart) - def closeCB(self): - if ( self.job is None or self.job.status is not self.job.IN_PROGRESS ) and not self.no_autostart: - self.close() - #else: - #if self.cancelable: - #self.cancel() + def autostart(self): + self.onShown.remove(self.autostart) + self.check_for_NFO() + print "[[layoutFinished]]", len(self["filelist"].getFileList()) + if len(self["filelist"].getFileList()) == 1: + print "==1" + self.keyOk() - def up(self): + def keyUp(self): self["filelist"].up() self.check_for_NFO() - def down(self): + def keyDown(self): self["filelist"].down() self.check_for_NFO() - def right(self): + def keyRight(self): self["filelist"].pageDown() self.check_for_NFO() - def left(self): + def keyLeft(self): self["filelist"].pageUp() self.check_for_NFO() - def check_for_NFO(self): - self.session.summary.setText(self["filelist"].getFilename()) - if self["filelist"].getFilename() is None: - return - if self["filelist"].getCurrentDirectory() is not None: - self.nfifile = self["filelist"].getCurrentDirectory()+self["filelist"].getFilename() + def keyOk(self): + if self.job is None or self.job.status is not self.job.IN_PROGRESS: + if self["filelist"].canDescent(): # isDir + self["filelist"].descent() + self.check_for_NFO() + elif self["filelist"].getFilename(): + self.session.openWithCallback(self.queryCB, MessageBox, _("Shall the USB stick wizard proceed and program the image file %s into flash memory?" % self.nfifile.rsplit('/',1)[-1]), MessageBox.TYPE_YESNO) + + def check_for_NFO(self, nfifile=None): + print "check_for_NFO", self["filelist"].getFilename(), self["filelist"].getCurrentDirectory() + self["infolabel"].text = "" + self["key_green"].text = "" + + if nfifile is None: + if self["filelist"].getFilename() is None: + return + if self["filelist"].getCurrentDirectory() is not None: + self.nfifile = self["filelist"].getCurrentDirectory()+self["filelist"].getFilename() + else: + self.nfifile = nfifile if self.nfifile.upper().endswith(".NFI"): self["key_green"].text = _("Flash") nfofilename = self.nfifile[0:-3]+"nfo" + print nfofilename, fileExists(nfofilename) if fileExists(nfofilename): nfocontent = open(nfofilename, "r").read() + print "nfocontent:", nfocontent self["infolabel"].text = nfocontent pos = nfocontent.find("MD5:") if pos > 0: @@ -152,108 +177,53 @@ class NFIFlash(Screen): else: self.md5sum = "" else: - self["infolabel"].text = _("No details for this image file") + ":\n" + self["filelist"].getFilename() + self["infolabel"].text = _("No details for this image file") + (self["filelist"].getFilename() or "") self.md5sum = "" - else: - self["infolabel"].text = "" - self["key_green"].text = "" - - def ok(self): - if self.job is None or self.job.status is not self.job.IN_PROGRESS: - if self["filelist"].canDescent(): # isDir - self["filelist"].descent() - self.session.summary.setText(self["filelist"].getFilename()) - self.check_for_NFO() - else: - self.queryFlash() - - def queryFlash(self): - fd = open(self.nfifile, 'r') - print fd - sign = fd.read(11) - print sign - if sign.find("NFI1" + self.box + "\0") == 0: - if self.md5sum != "": - self["statusbar"].text = ("Please wait for md5 signature verification...") - self.session.summary.setText(("Please wait for md5 signature verification...")) - self.container = eConsoleAppContainer() - self.container.setCWD(self["filelist"].getCurrentDirectory()) - self.container.appClosed.append(self.md5finished) - self.container.dataSent.append(self.md5ready) - self.container.execute("md5sum -cw -") - self.container.write(self.md5sum) - else: - self.session.openWithCallback(self.queryCB, MessageBox, _("This .NFI file does not have a md5sum signature and is not guaranteed to work. Do you really want to burn this image to flash memory?"), MessageBox.TYPE_YESNO) - else: - self.session.open(MessageBox, (_("This .NFI file does not contain a valid %s image!") % (self.box.upper())), MessageBox.TYPE_ERROR) - - def md5ready(self, retval): - self.container.sendEOF() - - def md5finished(self, retval): - if retval==0: - self.session.openWithCallback(self.queryCB, MessageBox, _("This .NFI file has a valid md5 signature. Continue programming this image to flash memory?"), MessageBox.TYPE_YESNO) - else: - self.session.openWithCallback(self.queryCB, MessageBox, _("The md5sum validation failed, the file may be corrupted! Are you sure that you want to burn this image to flash memory? You are doing this at your own risk!"), MessageBox.TYPE_YESNO) def queryCB(self, answer): if answer == True: self.createJob() - else: - self["statusbar"].text = _("Please select .NFI flash image file from medium") def createJob(self): self.job = Job("Image flashing job") - param = [self.nfifile] - writeNAND(self.job,param,self.box) - #writeNAND2(self.job,param) - #writeNAND3(self.job,param) - self.job.state_changed.append(self.update_job) - self.job.end = 540 - self.cwd = self["filelist"].getCurrentDirectory() - self["job_progressbar"].range = self.job.end - self.startJob() - - def startJob(self): + self.job.afterEvent = "close" + cwd = self["filelist"].getCurrentDirectory() + md5verify(self.job, cwd, self.md5sum) + writeNAND(self.job, [self.nfifile], self.box) self["key_blue"].text = "" self["key_yellow"].text = "" self["key_green"].text = "" - #self["progress0"].show() - #self["progress1"].show() + job_manager.AddJob(self.job) + self.session.openWithCallback(self.flashed, JobView, self.job, cancelable = False, backgroundable = False, afterEventChangeable = False) - self.job.start(self.jobcb) + def flashed(self, bg): + print "[flashed]" + if self.job.status == self.job.FINISHED: + self["status"].text = _("NFI image flashing completed. Press Yellow to Reboot!") + filename = self.usbmountpoint+'enigma2settingsbackup.tar.gz' + if fileExists(filename): + import os.path, time + date = time.ctime(os.path.getmtime(filename)) + self.session.openWithCallback(self.askRestoreCB, MessageBox, _("The wizard found a configuration backup. Do you want to restore your old settings from %s?") % date, MessageBox.TYPE_YESNO) + else: + self.unlockRebootButton() + else: + self["status"].text = _("Flashing failed") - def update_job(self): - j = self.job - #print "[job state_changed]" - if j.status == j.IN_PROGRESS: - self.session.summary["job_progressbar"].value = j.progress - self.session.summary["job_progressbar"].range = j.end - self.session.summary["job_progresslabel"].text = "%.2f%%" % (100*j.progress/float(j.end)) - self["job_progressbar"].range = j.end - self["job_progressbar"].value = j.progress - #print "[update_job] j.progress=%f, j.getProgress()=%f, j.end=%d, text=%f" % (j.progress, j.getProgress(), j.end, (100*j.progress/float(j.end))) - self["job_progresslabel"].text = "%.2f%%" % (100*j.progress/float(j.end)) - self.session.summary.setText(j.tasks[j.current_task].name) - self["statusbar"].text = (j.tasks[j.current_task].name) + def askRestoreCB(self, ret): + if ret: + from Plugins.SystemPlugins.SoftwareManager.BackupRestore import getBackupFilename + restorecmd = ["tar -xzvf " + self.usbmountpoint + getBackupFilename() + " -C /"] + self.session.openWithCallback(self.unlockRebootButton, Console, title = _("Restore is running..."), cmdlist = restorecmd, closeOnSuccess = True) + else: + self.unlockRebootButton() - elif j.status == j.FINISHED: - self["statusbar"].text = _("Writing NFI image file to flash completed") - self.session.summary.setText(_("NFI image flashing completed. Press Yellow to Reboot!")) + def unlockRebootButton(self, retval = None): + if self.job.status == self.job.FINISHED: self["key_yellow"].text = _("Reboot") - elif j.status == j.FAILED: - self["statusbar"].text = j.tasks[j.current_task].name + " " + _("failed") - self.session.open(MessageBox, (_("Flashing failed") + ":\n" + j.tasks[j.current_task].name + ":\n" + j.tasks[j.current_task].output_line), MessageBox.TYPE_ERROR) - - def jobcb(self, jobref, fasel, blubber): - print "[jobcb] %s %s %s" % (jobref, fasel, blubber) - self["key_green"].text = _("Flash") - - def reboot(self): + def reboot(self, ret=None): if self.job.status == self.job.FINISHED: - self["statusbar"].text = ("rebooting...") - TryQuitMainloop(self.session,2) - - def createSummary(self): - return NFISummary + self["status"].text = ("rebooting...") + from os import system + system(eEnv.resolve("${libdir}/enigma2/python/Plugins/SystemPlugins/NFIFlash/kill_e2_reboot.sh")) diff --git a/lib/python/Plugins/SystemPlugins/NFIFlash/meta/Makefile.am b/lib/python/Plugins/SystemPlugins/NFIFlash/meta/Makefile.am index 0633e7cf..598c0715 100755 --- a/lib/python/Plugins/SystemPlugins/NFIFlash/meta/Makefile.am +++ b/lib/python/Plugins/SystemPlugins/NFIFlash/meta/Makefile.am @@ -1,4 +1,4 @@ -installdir = $(datadir)/meta/ +installdir = $(datadir)/meta dist_install_DATA = plugin_nfiflash.xml diff --git a/lib/python/Plugins/SystemPlugins/NFIFlash/meta/plugin_nfiflash.xml b/lib/python/Plugins/SystemPlugins/NFIFlash/meta/plugin_nfiflash.xml index c81f4ca5..f93f5c5a 100755 --- a/lib/python/Plugins/SystemPlugins/NFIFlash/meta/plugin_nfiflash.xml +++ b/lib/python/Plugins/SystemPlugins/NFIFlash/meta/plugin_nfiflash.xml @@ -2,28 +2,18 @@ <prerequisites> <hardware type="dm8000" /> <tag type="System" /> - <tag type="Software" /> </prerequisites> - <info language="en"> + <info> <author>Dream Multimedia</author> <name>NFIFlash</name> <packagename>enigma2-plugin-systemplugins-nfiflash</packagename> - <shortdescription>Restore your Dreambox with a USB stick.</shortdescription> - <description>With the NFIFlash extension it is possible to prepare a USB stick with an Dreambox image.\n + <shortdescription>Restore your Dreambox with a USB stick</shortdescription> + <description>With the NFIFlash plugin it is possible to prepare a USB stick with an Dreambox image.\n It is then possible to flash your Dreambox with the image on that stick. </description> <screenshot src="http://www.dreamboxupdate.com/preview/plugin_nfiflash_en.jpg" /> </info> - <info language="de"> - <author>Dream Multimedia</author> - <name>NFIFlash</name> - <packagename>enigma2-plugin-systemplugins-nfiflash</packagename> - <shortdescription>Wiederherstellen Ihrer Dreambox mittels USB-Stick.</shortdescription> - <description>Mit der NFIFlash Erweiterung können Sie ein Dreambox Image auf einen USB-Stick laden.\ - Mit diesem USB-Stick ist es dann möglich Ihre Dreambox zu flashen. - </description> - <screenshot src="http://www.dreamboxupdate.com/preview/plugin_nfiflash_de.jpg" /> - </info> + <files type="package"> <!-- without version, without .ipk --> <file type="package" name="enigma2-plugin-systemplugins-nfiflash" /> </files> diff --git a/lib/python/Plugins/SystemPlugins/NFIFlash/mywritenand b/lib/python/Plugins/SystemPlugins/NFIFlash/mywritenand Binary files differdeleted file mode 100755 index 073a204f..00000000 --- a/lib/python/Plugins/SystemPlugins/NFIFlash/mywritenand +++ /dev/null diff --git a/lib/python/Plugins/SystemPlugins/NFIFlash/plugin.py b/lib/python/Plugins/SystemPlugins/NFIFlash/plugin.py index 28b33305..b6544764 100755..100644 --- a/lib/python/Plugins/SystemPlugins/NFIFlash/plugin.py +++ b/lib/python/Plugins/SystemPlugins/NFIFlash/plugin.py @@ -2,6 +2,7 @@ from Plugins.Plugin import PluginDescriptor from Tools.HardwareInfo import HardwareInfo from Tools.Directories import fileExists from downloader import NFIDownload, filescan +from flasher import NFIFlash def NFIFlasherMain(session, tmp = None, **kwargs): session.open(NFIDownload, "/home/root" ) @@ -13,16 +14,13 @@ def Plugins(**kwargs): # currently only available for DM8000 if HardwareInfo().get_device_name() != "dm8000": return [PluginDescriptor()] - if fileExists("/usr/share/bootlogo-flasher.mvi"): - import flasher - # started from usb stick # don't try to be intelligent and trick this - it's not possible to rewrite the flash memory with a system currently booted from it - return [PluginDescriptor(where = PluginDescriptor.WHERE_WIZARD, fnc = (9,flasher.NFIFlash))] - else: - # started on real enigma2 - return [PluginDescriptor(name=_("NFI Image Flashing"), - description=_("Download .NFI-Files for USB-Flasher"), - icon = "flash.png", - where = PluginDescriptor.WHERE_SOFTWAREMANAGER, - fnc={"SoftwareSupported": NFICallFnc, "menuEntryName": lambda x: _("NFI Image Flashing"), - "menuEntryDescription": lambda x: _("Download .NFI-Files for USB-Flasher")}), - PluginDescriptor(name="nfi", where = PluginDescriptor.WHERE_FILESCAN, fnc = filescan)] + #return [PluginDescriptor(where = PluginDescriptor.WHERE_WIZARD, fnc = (9,NFIFlash))] + # it's not possible to rewrite the flash memory with a system currently booted from it + return [PluginDescriptor(name=_("NFI Image Flashing"), + description=_("Download .NFI-Files for USB-Flasher"), + icon = "flash.png", + where = PluginDescriptor.WHERE_SOFTWAREMANAGER, + needsRestart = False, + fnc={"SoftwareSupported": NFICallFnc, "menuEntryName": lambda x: _("NFI Image Flashing"), + "menuEntryDescription": lambda x: _("Download .NFI-Files for USB-Flasher")}), + PluginDescriptor(name="nfi", where = PluginDescriptor.WHERE_FILESCAN, needsRestart = False, fnc = filescan)] diff --git a/lib/python/Plugins/SystemPlugins/NetworkWizard/NetworkWizard.py b/lib/python/Plugins/SystemPlugins/NetworkWizard/NetworkWizard.py index 6096ca8b..cb954bb2 100755 --- a/lib/python/Plugins/SystemPlugins/NetworkWizard/NetworkWizard.py +++ b/lib/python/Plugins/SystemPlugins/NetworkWizard/NetworkWizard.py @@ -4,31 +4,10 @@ from Screens.Rc import Rc from Screens.MessageBox import MessageBox from Components.Pixmap import Pixmap, MovingPixmap, MultiPixmap from Components.Sources.Boolean import Boolean -from Components.config import config, ConfigBoolean, configfile, ConfigYesNo, NoSave, ConfigSubsection, ConfigText, getConfigListEntry, ConfigSelection, ConfigPassword from Components.Network import iNetwork from Tools.Directories import resolveFilename, SCOPE_PLUGINS, SCOPE_SKIN_IMAGE from enigma import eTimer - -config.misc.firstrun = ConfigBoolean(default = True) -list = [] -list.append("WEP") -list.append("WPA") -list.append("WPA2") -list.append("WPA/WPA2") - -weplist = [] -weplist.append("ASCII") -weplist.append("HEX") - -config.plugins.wlan = ConfigSubsection() -config.plugins.wlan.essid = NoSave(ConfigText(default = "home", fixed_size = False)) -config.plugins.wlan.hiddenessid = NoSave(ConfigText(default = "home", fixed_size = False)) - -config.plugins.wlan.encryption = ConfigSubsection() -config.plugins.wlan.encryption.enabled = NoSave(ConfigYesNo(default = False)) -config.plugins.wlan.encryption.type = NoSave(ConfigSelection(list, default = "WPA/WPA2" )) -config.plugins.wlan.encryption.wepkeytype = NoSave(ConfigSelection(weplist, default = "ASCII")) -config.plugins.wlan.encryption.psk = NoSave(ConfigPassword(default = "mysecurewlan", fixed_size = False)) +from os import system class NetworkWizard(WizardLanguage, Rc): skin = """ @@ -65,8 +44,9 @@ class NetworkWizard(WizardLanguage, Rc): self.Adapterlist = None self.InterfaceState = None self.isInterfaceUp = None - self.WlanPluginInstalled = None + self.WlanPluginInstalled = False self.ap = None + self.w = None if interface is not None: self.selectedInterface = interface else: @@ -77,11 +57,9 @@ class NetworkWizard(WizardLanguage, Rc): self.AdapterRef = None self.APList = None self.newAPlist = None - self.WlanList = None self.oldlist = None - self.originalAth0State = None - self.originalEth0State = None - self.originalWlan0State = None + + self.originalInterfaceState = {} self.originalInterfaceStateChanged = False self.Text = None self.rescanTimer = eTimer() @@ -95,65 +73,64 @@ class NetworkWizard(WizardLanguage, Rc): self.close() def markDone(self): - self.rescanTimer.stop() + self.stopScan() del self.rescanTimer self.checkOldInterfaceState() pass - def getInstalledInterfaceCount(self): + def back(self): + self.stopScan() + self.ap = None + WizardLanguage.back(self) + + def stopScan(self): self.rescanTimer.stop() + if self.w is not None: + from Plugins.SystemPlugins.WirelessLan.Wlan import iWlan + iWlan.stopGetNetworkList() + self.w = None + + def getInstalledInterfaceCount(self): + self.originalInterfaceState = {} self.Adapterlist = iNetwork.getAdapterList() self.InstalledInterfaceCount = len(self.Adapterlist) if self.Adapterlist is not None: if self.InstalledInterfaceCount == 1 and self.selectedInterface is None: self.selectedInterface = self.Adapterlist[0] - self.originalAth0State = iNetwork.getAdapterAttribute('ath0', 'up') - self.originalEth0State = iNetwork.getAdapterAttribute('eth0', 'up') - self.originalWlan0State = iNetwork.getAdapterAttribute('wlan0', 'up') + for interface in iNetwork.getAdapterList(): + self.originalInterfaceState[interface] = {} + self.originalInterfaceState[interface]["up"] = iNetwork.getAdapterAttribute(interface, 'up') def selectInterface(self): self.InterfaceState = None - if self.selectedInterface is None and self.InstalledInterfaceCount <= 1: - if self.selectedInterface == 'eth0': - self.NextStep = 'nwconfig' - else: - self.NextStep = 'scanwlan' - self.checkInterface(self.selectedInterface) - elif self.selectedInterface is not None and self.InstalledInterfaceCount <= 1: - if self.selectedInterface == 'eth0': - self.NextStep = 'nwconfig' + if self.selectedInterface is None: + if self.InstalledInterfaceCount <= 1: + if not iNetwork.isWirelessInterface(self.selectedInterface): + self.NextStep = 'nwconfig' + else: + self.NextStep = 'asknetworktype' + self.checkInterface(self.selectedInterface) else: - self.NextStep = 'scanwlan' - self.checkInterface(self.selectedInterface) - elif self.selectedInterface is None and self.InstalledInterfaceCount > 1: - self.NextStep = 'selectinterface' - self.currStep = self.getStepWithID(self.NextStep) - self.afterAsyncCode() - elif self.selectedInterface is not None and self.InstalledInterfaceCount > 1: - if self.selectedInterface == 'eth0': + self.NextStep = 'selectinterface' + self.currStep = self.getStepWithID(self.NextStep) + self.afterAsyncCode() + else: + if not iNetwork.isWirelessInterface(self.selectedInterface): self.NextStep = 'nwconfig' else: - self.NextStep = 'scanwlan' + self.NextStep = 'asknetworktype' self.checkInterface(self.selectedInterface) - else: - self.NextStep = 'selectinterface' - self.currStep = self.getStepWithID(self.NextStep) - self.afterAsyncCode() def checkOldInterfaceState(self): # disable up interface if it was originally down and config is unchanged. - if self.originalAth0State is False and self.originalInterfaceStateChanged is False: - if iNetwork.checkforInterface('ath0') is True: - iNetwork.deactivateInterface('ath0') - if self.originalEth0State is False and self.originalInterfaceStateChanged is False: - if iNetwork.checkforInterface('eth0') is True: - iNetwork.deactivateInterface('eth0') - if self.originalWlan0State is False and self.originalInterfaceStateChanged is False: - if iNetwork.checkforInterface('wlan0') is True: - iNetwork.deactivateInterface('wlan0') + if self.originalInterfaceStateChanged is False: + for interface in self.originalInterfaceState.keys(): + if interface == self.selectedInterface: + if self.originalInterfaceState[interface]["up"] is False: + if iNetwork.checkforInterface(interface) is True: + system("ifconfig " + interface + " down") def listInterfaces(self): - self.rescanTimer.stop() self.checkOldInterfaceState() list = [(iNetwork.getFriendlyAdapterName(x),x) for x in iNetwork.getAdapterList()] list.append((_("Exit network wizard"), "end")) @@ -169,13 +146,13 @@ class NetworkWizard(WizardLanguage, Rc): elif index == 'eth0': self.NextStep = 'nwconfig' else: - self.NextStep = 'scanwlan' + self.NextStep = 'asknetworktype' def InterfaceSelectionMoved(self): self.InterfaceSelect(self.selection) def checkInterface(self,iface): - self.rescanTimer.stop() + self.stopScan() if self.Adapterlist is None: self.Adapterlist = iNetwork.getAdapterList() if self.NextStep is not 'end': @@ -183,7 +160,7 @@ class NetworkWizard(WizardLanguage, Rc): #Reset Network to defaults if network broken iNetwork.resetNetworkConfig('lan', self.resetNetworkConfigCB) self.resetRef = self.session.openWithCallback(self.resetNetworkConfigFinished, MessageBox, _("Please wait while we prepare your network interfaces..."), type = MessageBox.TYPE_INFO, enable_input = False) - if iface in ('eth0', 'wlan0', 'ath0'): + if iface in iNetwork.getInstalledAdapters(): if iface in iNetwork.configuredNetworkAdapters and len(iNetwork.configuredNetworkAdapters) == 1: if iNetwork.getAdapterAttribute(iface, 'up') is True: self.isInterfaceUp = True @@ -232,7 +209,7 @@ class NetworkWizard(WizardLanguage, Rc): def AdapterSetupEndCB(self,data): if data is True: - if self.selectedInterface in ('wlan0', 'ath0'): + if iNetwork.isWirelessInterface(self.selectedInterface): if self.WlanPluginInstalled == True: from Plugins.SystemPlugins.WirelessLan.Wlan import iStatus iStatus.getDataForInterface(self.selectedInterface,self.checkWlanStateCB) @@ -255,9 +232,9 @@ class NetworkWizard(WizardLanguage, Rc): if data is True: if status is not None: text1 = _("Your Dreambox is now ready to use.\n\nYour internet connection is working now.\n\n") - text2 = _('Accesspoint:') + "\t" + str(status[self.selectedInterface]["acesspoint"]) + "\n" + text2 = _('Accesspoint:') + "\t" + str(status[self.selectedInterface]["accesspoint"]) + "\n" text3 = _('SSID:') + "\t" + str(status[self.selectedInterface]["essid"]) + "\n" - text4 = _('Link Quality:') + "\t" + str(status[self.selectedInterface]["quality"])+"%" + "\n" + text4 = _('Link Quality:') + "\t" + str(status[self.selectedInterface]["quality"])+ "\n" text5 = _('Signal Strength:') + "\t" + str(status[self.selectedInterface]["signal"]) + "\n" text6 = _('Bitrate:') + "\t" + str(status[self.selectedInterface]["bitrate"]) + "\n" text7 = _('Encryption:') + " " + str(status[self.selectedInterface]["encryption"]) + "\n" @@ -265,7 +242,7 @@ class NetworkWizard(WizardLanguage, Rc): infotext = text1 + text2 + text3 + text4 + text5 + text7 +"\n" + text8 self.currStep = self.getStepWithID("checkWlanstatusend") self.Text = infotext - if str(status[self.selectedInterface]["acesspoint"]) == "Not-Associated": + if str(status[self.selectedInterface]["accesspoint"]) == "Not-Associated": self.InterfaceState = False self.afterAsyncCode() @@ -275,7 +252,7 @@ class NetworkWizard(WizardLanguage, Rc): def checkNetworkCB(self,data): if data is True: - if self.selectedInterface in ('wlan0', 'ath0'): + if iNetwork.isWirelessInterface(self.selectedInterface): if self.WlanPluginInstalled == True: from Plugins.SystemPlugins.WirelessLan.Wlan import iStatus iStatus.getDataForInterface(self.selectedInterface,self.checkWlanStateCB) @@ -310,96 +287,74 @@ class NetworkWizard(WizardLanguage, Rc): newList.append(oldentry) for newentry in newList: - if newentry[1] == "hidden...": - continue self.newAPlist.append(newentry) - - if len(self.newAPlist): - if "hidden..." not in self.newAPlist: - self.newAPlist.append(( _("enter hidden network SSID"), "hidden..." )) + if len(self.newAPlist): if (self.wizard[self.currStep].has_key("dynamiclist")): currentListEntry = self["list"].getCurrent() - idx = 0 - for entry in self.newAPlist: - if entry == currentListEntry: - newListIndex = idx - idx +=1 + if currentListEntry is not None: + idx = 0 + for entry in self.newAPlist: + if entry == currentListEntry: + newListIndex = idx + idx +=1 self.wizard[self.currStep]["evaluatedlist"] = self.newAPlist self['list'].setList(self.newAPlist) - self["list"].setIndex(newListIndex) + if newListIndex is not None: + self["list"].setIndex(newListIndex) self["list"].updateList(self.newAPlist) def listAccessPoints(self): self.APList = [] - try: - from Plugins.SystemPlugins.WirelessLan.Wlan import Wlan - except ImportError: - self.APList.append( ( _("No networks found"),_("unavailable") ) ) - return self.APList + if self.WlanPluginInstalled is False: + self.APList.append( ( _("No networks found"), None ) ) else: - try: - self.w = Wlan(self.selectedInterface) - aps = self.w.getNetworkList() - except ValueError: - self.APList = [] - self.APList.append( ( _("No networks found"),_("unavailable") ) ) - return self.APList - else: - if aps is not None: - print "[NetworkWizard.py] got Accespoints!" - tmplist = [] - complist = [] - for ap in aps: - a = aps[ap] - if a['active']: - tmplist.append( (a['bssid'], a['essid']) ) - complist.append( (a['bssid'], a['essid']) ) - - for entry in tmplist: - if entry[1] == "": - for compentry in complist: - if compentry[0] == entry[0]: - complist.remove(compentry) - for entry in complist: - self.APList.append( (entry[1], entry[1]) ) - - if "hidden..." not in self.APList: - self.APList.append(( _("enter hidden network SSID"), "hidden..." )) + from Plugins.SystemPlugins.WirelessLan.Wlan import iWlan + iWlan.setInterface(self.selectedInterface) + self.w = iWlan.getInterface() + aps = iWlan.getNetworkList() + if aps is not None: + print "[NetworkWizard.py] got Accespoints!" + tmplist = [] + complist = [] + for ap in aps: + a = aps[ap] + if a['active']: + tmplist.append( (a['bssid'], a['essid']) ) + complist.append( (a['bssid'], a['essid']) ) + + for entry in tmplist: + if entry[1] == "": + for compentry in complist: + if compentry[0] == entry[0]: + complist.remove(compentry) + for entry in complist: + self.APList.append( (entry[1], entry[1]) ) + if not len(aps): + self.APList.append( ( _("No networks found"), None ) ) - self.rescanTimer.start(3000) - return self.APList + self.rescanTimer.start(4000) + return self.APList - def AccessPointsSelectionMade(self, index): - self.ap = index - self.WlanList = [] - currList = [] - if (self.wizard[self.currStep].has_key("dynamiclist")): - currList = self['list'].list - for entry in currList: - self.WlanList.append( (entry[1], entry[0]) ) - self.AccessPointsSelect(index) - - def AccessPointsSelect(self, index): - self.NextStep = 'wlanconfig' def AccessPointsSelectionMoved(self): - self.AccessPointsSelect(self.selection) + self.ap = self.selection + self.NextStep = 'wlanconfig' def checkWlanSelection(self): - self.rescanTimer.stop() + self.stopScan() self.currStep = self.getStepWithID(self.NextStep) def isWlanPluginInstalled(self): try: - from Plugins.SystemPlugins.WirelessLan.Wlan import Wlan + from Plugins.SystemPlugins.WirelessLan.Wlan import iWlan except ImportError: self.WlanPluginInstalled = False else: self.WlanPluginInstalled = True def listChoices(self): - self.rescanTimer.stop() + self.stopScan() list = [] if self.WlanPluginInstalled == True: list.append((_("Configure your wireless LAN again"), "scanwlan")) @@ -417,7 +372,7 @@ class NetworkWizard(WizardLanguage, Rc): self.selectedInterface = "eth0" self.NextStep = 'nwconfig' else: - self.NextStep = 'scanwlan' + self.NextStep = 'asknetworktype' def ChoicesSelectionMoved(self): pass diff --git a/lib/python/Plugins/SystemPlugins/NetworkWizard/meta/Makefile.am b/lib/python/Plugins/SystemPlugins/NetworkWizard/meta/Makefile.am index e8f738c6..db985bf7 100755 --- a/lib/python/Plugins/SystemPlugins/NetworkWizard/meta/Makefile.am +++ b/lib/python/Plugins/SystemPlugins/NetworkWizard/meta/Makefile.am @@ -1,4 +1,4 @@ -installdir = $(datadir)/meta/ +installdir = $(datadir)/meta dist_install_DATA = plugin_networkwizard.xml diff --git a/lib/python/Plugins/SystemPlugins/NetworkWizard/meta/plugin_networkwizard.xml b/lib/python/Plugins/SystemPlugins/NetworkWizard/meta/plugin_networkwizard.xml index 4d3adcbd..423365f1 100755 --- a/lib/python/Plugins/SystemPlugins/NetworkWizard/meta/plugin_networkwizard.xml +++ b/lib/python/Plugins/SystemPlugins/NetworkWizard/meta/plugin_networkwizard.xml @@ -1,26 +1,18 @@ <default> <prerequisites> + <tag type="Network" /> <tag type="System" /> </prerequisites> - <info language="en"> + <info> <author>Dream Multimedia</author> <name>NetworkWizard</name> <packagename>enigma2-plugin-systemplugins-networkwizard</packagename> <shortdescription>Step by step network configuration</shortdescription> - <description>With the NetworkWizard you can easy configure your network step by step. + <description>With the NetworkWizard you can easily configure your network step by step. </description> <screenshot src="http://www.dreamboxupdate.com/preview/plugin_networkwizard_en.jpg" /> </info> - <info language="de"> - <author>Dream Multimedia</author> - <name>NetzwerkWizard</name> - <packagename>enigma2-plugin-systemplugins-networkwizard</packagename> - <shortdescription>Schritt für Schritt Netzwerk konfiguration</shortdescription> - <description>Mit dem NetzwerkWizard können Sie Ihr Netzwerk konfigurieren. Sie werden Schritt - für Schritt durch die Konfiguration geleitet. - </description> - <screenshot src="http://www.dreamboxupdate.com/preview/plugin_networkwizard_de.jpg" /> - </info> + <files type="package"> <!-- without version, without .ipk --> <file type="package" name="enigma2-plugin-systemplugins-networkwizard" /> </files> diff --git a/lib/python/Plugins/SystemPlugins/NetworkWizard/networkwizard.xml b/lib/python/Plugins/SystemPlugins/NetworkWizard/networkwizard.xml index c4300436..0b3a2988 100755 --- a/lib/python/Plugins/SystemPlugins/NetworkWizard/networkwizard.xml +++ b/lib/python/Plugins/SystemPlugins/NetworkWizard/networkwizard.xml @@ -87,13 +87,33 @@ self.selectKey("DOWN") <listentry caption="Exit network wizard" step="end" /> </list> </step> + + <step id="asknetworktype"> + <condition> +self.condition = (self.isInterfaceUp == True and self.WlanPluginInstalled == True) + </condition> + <text value="Wireless network connection setup." /> + <displaytext value="Wireless network connection setup" /> + <code> +self.clearSelectedKeys() +self.selectKey("OK") +self.selectKey("UP") +self.selectKey("DOWN") + </code> + <list> + <listentry caption="List available networks" step="scanwlan" /> + <listentry caption="Manual configuration" step="wlanconfig" /> + <listentry caption="Exit network wizard" step="end" /> + </list> + </step> + <step id="scanwlan"> <condition> self.condition = (self.isInterfaceUp == True and self.WlanPluginInstalled == True) </condition> <text value="Please select the wireless network that you want to connect to.\n\nPlease press OK to continue." /> <displaytext value="Select wireless network" /> - <list type="dynamic" source="listAccessPoints" evaluation="AccessPointsSelectionMade" onselect="AccessPointsSelectionMoved" /> + <list type="dynamic" source="listAccessPoints" onselect="AccessPointsSelectionMoved" /> <code> self.clearSelectedKeys() self.selectKey("OK") @@ -138,7 +158,7 @@ self.selectKey("DOWN") <step id="wlanconfig"> <text value="Please configure your internet connection by filling out the required values.\nWhen you are ready press OK to continue." /> <displaytext value="Configure interface" /> - <config screen="AdapterSetup" module="NetworkSetup" args="(self.selectedInterface,self.ap,self.WlanList)" type="ConfigList" /> + <config screen="AdapterSetup" module="NetworkSetup" args="(self.selectedInterface,self.ap)" type="ConfigList" /> <code> self.clearSelectedKeys() self.selectKey("OK") @@ -196,4 +216,3 @@ self.selectKey("OK") </step> </wizard> - diff --git a/lib/python/Plugins/SystemPlugins/NetworkWizard/plugin.py b/lib/python/Plugins/SystemPlugins/NetworkWizard/plugin.py index 49ec7da8..30a24230 100755..100644 --- a/lib/python/Plugins/SystemPlugins/NetworkWizard/plugin.py +++ b/lib/python/Plugins/SystemPlugins/NetworkWizard/plugin.py @@ -1,6 +1,8 @@ from Screens.Screen import Screen from Plugins.Plugin import PluginDescriptor -from Components.config import getConfigListEntry, config +from Components.config import getConfigListEntry, config, ConfigBoolean + +config.misc.firstrun = ConfigBoolean(default = True) def NetworkWizardMain(session, **kwargs): session.open(NetworkWizard) @@ -18,5 +20,5 @@ def NetworkWizard(*args, **kwargs): def Plugins(**kwargs): list = [] if config.misc.firstrun.value: - list.append(PluginDescriptor(name=_("Network Wizard"), where = PluginDescriptor.WHERE_WIZARD, fnc=(25, NetworkWizard))) + list.append(PluginDescriptor(name=_("Network Wizard"), where = PluginDescriptor.WHERE_WIZARD, needsRestart = False, fnc=(25, NetworkWizard))) return list diff --git a/lib/python/Plugins/SystemPlugins/OldSoftwareUpdate/plugin.py b/lib/python/Plugins/SystemPlugins/OldSoftwareUpdate/plugin.py index c7216382..22e54369 100644 --- a/lib/python/Plugins/SystemPlugins/OldSoftwareUpdate/plugin.py +++ b/lib/python/Plugins/SystemPlugins/OldSoftwareUpdate/plugin.py @@ -10,7 +10,7 @@ from os import popen class Upgrade(Screen): skin = """ - <screen position="100,100" size="550,400" title="IPKG upgrade..." > + <screen position="100,100" size="550,400" title="opkg upgrade..." > <widget name="text" position="0,0" size="550,400" font="Regular;15" /> </screen>""" @@ -39,7 +39,7 @@ class Upgrade(Screen): self.close() def doUpdateDelay(self): - lines = popen("ipkg update && ipkg upgrade -force-defaults -force-overwrite", "r").readlines() + lines = popen("opkg update && opkg upgrade -force-defaults -force-overwrite", "r").readlines() string = "" for x in lines: string += x @@ -87,7 +87,7 @@ class PacketList(GUIComponent): class Ipkg(Screen): skin = """ - <screen position="100,100" size="550,400" title="IPKG upgrade..." > + <screen position="100,100" size="550,400" title="opkg upgrade..." > <widget name="list" position="0,0" size="550,400" scrollbarMode="showOnDemand" /> </screen>""" @@ -109,13 +109,13 @@ class Ipkg(Screen): def fillPacketList(self): - lines = popen("ipkg list", "r").readlines() + lines = popen("opkg list", "r").readlines() packetlist = [] for x in lines: split = x.split(' - ') packetlist.append([split[0].strip(), split[1].strip()]) - lines = popen("ipkg list_installed", "r").readlines() + lines = popen("opkg list_installed", "r").readlines() installedlist = {} for x in lines: @@ -138,7 +138,7 @@ class Ipkg(Screen): self.close() def doUpdateDelay(self): - lines = popen("ipkg update && ipkg upgrade", "r").readlines() + lines = popen("opkg update && opkg upgrade", "r").readlines() string = "" for x in lines: string += x @@ -161,4 +161,4 @@ def IpkgMain(session, **kwargs): def Plugins(**kwargs): return [PluginDescriptor(name="Old Softwareupdate", description="Updates your receiver's software", icon="update.png", where = PluginDescriptor.WHERE_PLUGINMENU, fnc=UpgradeMain), - PluginDescriptor(name="IPKG", description="IPKG frontend", icon="update.png", where = PluginDescriptor.WHERE_PLUGINMENU, fnc=IpkgMain)] + PluginDescriptor(name="opkg", description="opkg frontend", icon="update.png", where = PluginDescriptor.WHERE_PLUGINMENU, fnc=IpkgMain)] diff --git a/lib/python/Plugins/SystemPlugins/PositionerSetup/meta/Makefile.am b/lib/python/Plugins/SystemPlugins/PositionerSetup/meta/Makefile.am index 3bc5cb24..a6b296cf 100755 --- a/lib/python/Plugins/SystemPlugins/PositionerSetup/meta/Makefile.am +++ b/lib/python/Plugins/SystemPlugins/PositionerSetup/meta/Makefile.am @@ -1,4 +1,4 @@ -installdir = $(datadir)/meta/ +installdir = $(datadir)/meta dist_install_DATA = plugin_positionersetup.xml diff --git a/lib/python/Plugins/SystemPlugins/PositionerSetup/meta/plugin_positionersetup.xml b/lib/python/Plugins/SystemPlugins/PositionerSetup/meta/plugin_positionersetup.xml index 2cb47c07..5e1db7ba 100755 --- a/lib/python/Plugins/SystemPlugins/PositionerSetup/meta/plugin_positionersetup.xml +++ b/lib/python/Plugins/SystemPlugins/PositionerSetup/meta/plugin_positionersetup.xml @@ -3,25 +3,16 @@ <tag type="SAT" /> <tag type="System" /> </prerequisites> - <info language="en"> + <info> <author>Dream Multimedia</author> <name>PositionerSetup</name> <packagename>enigma2-plugin-systemplugins-positionersetup</packagename> - <shortdescription>PositionerSetup helps you installing a motorized dish.</shortdescription> - <description>With the PositionerSetup extension it is easy to install and configure a motorized dish. - </description> - <screenshot src="http://www.dreamboxupdate.com/preview/plugin_positionersetup.jpg" /> - </info> - <info language="de"> - <author>Dream Multimedia</author> - <name>PositionerSetup</name> - <packagename>enigma2-plugin-systemplugins-positionersetup</packagename> - <shortdescription>Unterstützt Sie beim installieren eines Rotors.</shortdescription> - <description>Die PositionerSetup Erweiterung unterstützt Sie beim einrichten - und konfigurieren einer motorgesteuerten Satellitenantenne. + <shortdescription>PositionerSetup helps you installing a motorized dish</shortdescription> + <description>With the PositionerSetup plugin it is easy to install and configure a motorized dish. </description> <screenshot src="http://www.dreamboxupdate.com/preview/plugin_positionersetup.jpg" /> </info> + <files type="package"> <!-- without version, without .ipk --> <file type="package" name="enigma2-plugin-systemplugins-positionersetup" /> </files> diff --git a/lib/python/Plugins/SystemPlugins/PositionerSetup/plugin.py b/lib/python/Plugins/SystemPlugins/PositionerSetup/plugin.py index fa533c0b..e0971173 100644 --- a/lib/python/Plugins/SystemPlugins/PositionerSetup/plugin.py +++ b/lib/python/Plugins/SystemPlugins/PositionerSetup/plugin.py @@ -77,7 +77,7 @@ class PositionerSetup(Screen): self.frontendStatus = { } self.diseqc = Diseqc(self.frontend) - self.tuner = Tuner(self.frontend) + self.tuner = Tuner(self.frontend, True) #True means we dont like that the normal sec stuff sends commands to the rotor! tp = ( cur.get("frequency", 0) / 1000, cur.get("symbol_rate", 0) / 1000, @@ -441,15 +441,15 @@ class TunerScreen(ScanSetup): self.scan_sat.system.value = eDVBFrontendParametersSatellite.System_DVB_S self.list.append(getConfigListEntry(_('Frequency'), self.scan_sat.frequency)) self.list.append(getConfigListEntry(_('Inversion'), self.scan_sat.inversion)) - self.list.append(getConfigListEntry(_('Symbol Rate'), self.scan_sat.symbolrate)) - self.list.append(getConfigListEntry(_("Polarity"), self.scan_sat.polarization)) + self.list.append(getConfigListEntry(_('Symbol rate'), self.scan_sat.symbolrate)) + self.list.append(getConfigListEntry(_('Polarization'), self.scan_sat.polarization)) if self.scan_sat.system.value == eDVBFrontendParametersSatellite.System_DVB_S: self.list.append(getConfigListEntry(_("FEC"), self.scan_sat.fec)) elif self.scan_sat.system.value == eDVBFrontendParametersSatellite.System_DVB_S2: self.list.append(getConfigListEntry(_("FEC"), self.scan_sat.fec_s2)) self.modulationEntry = getConfigListEntry(_('Modulation'), self.scan_sat.modulation) self.list.append(self.modulationEntry) - self.list.append(getConfigListEntry(_('Rolloff'), self.scan_sat.rolloff)) + self.list.append(getConfigListEntry(_('Roll-off'), self.scan_sat.rolloff)) self.list.append(getConfigListEntry(_('Pilot'), self.scan_sat.pilot)) elif tuning.type.value == "predefined_transponder": self.list.append(getConfigListEntry(_("Transponder"), tuning.transponder)) @@ -608,6 +608,6 @@ def PositionerSetupStart(menuid, **kwargs): def Plugins(**kwargs): if (nimmanager.hasNimType("DVB-S")): - return PluginDescriptor(name=_("Positioner setup"), description="Setup your positioner", where = PluginDescriptor.WHERE_MENU, fnc=PositionerSetupStart) + return PluginDescriptor(name=_("Positioner setup"), description="Setup your positioner", where = PluginDescriptor.WHERE_MENU, needsRestart = False, fnc=PositionerSetupStart) else: return [] diff --git a/lib/python/Plugins/SystemPlugins/SatelliteEquipmentControl/meta/Makefile.am b/lib/python/Plugins/SystemPlugins/SatelliteEquipmentControl/meta/Makefile.am index b31fa653..5dfb239a 100755 --- a/lib/python/Plugins/SystemPlugins/SatelliteEquipmentControl/meta/Makefile.am +++ b/lib/python/Plugins/SystemPlugins/SatelliteEquipmentControl/meta/Makefile.am @@ -1,4 +1,4 @@ -installdir = $(datadir)/meta/ +installdir = $(datadir)/meta dist_install_DATA = plugin_satelliteequipmentcontrol.xml diff --git a/lib/python/Plugins/SystemPlugins/SatelliteEquipmentControl/meta/plugin_satelliteequipmentcontrol.xml b/lib/python/Plugins/SystemPlugins/SatelliteEquipmentControl/meta/plugin_satelliteequipmentcontrol.xml index 4c0c7af7..904f9a2e 100755 --- a/lib/python/Plugins/SystemPlugins/SatelliteEquipmentControl/meta/plugin_satelliteequipmentcontrol.xml +++ b/lib/python/Plugins/SystemPlugins/SatelliteEquipmentControl/meta/plugin_satelliteequipmentcontrol.xml @@ -3,25 +3,17 @@ <tag type="SAT" /> <tag type="System" /> </prerequisites> - <info language="en"> + <info> <author>Dream Multimedia</author> <name>SatelliteEquipmentControl</name> <name_de>SatelliteEquipmentControl</name_de> <packagename>enigma2-plugin-systemplugins-satelliteequipmentcontrol</packagename> - <shortdescription>SatelliteEquipmentControl allows you to fine-tune DiSEqC-settings.</shortdescription> - <description>With the SatelliteEquipmentControl extension it is possible to fine-tune DiSEqC-settings. - </description> - <screenshot src="http://www.dreamboxupdate.com/preview/plugin_satcontrol.jpg" /> - </info> - <info language="de"> - <author>Dream Multimedia</author> - <name>SatelliteEquipmentControl</name> - <packagename>enigma2-plugin-systemplugins-satelliteequipmentcontrol</packagename> - <shortdescription>Fein-Einstellungen für DiSEqC</shortdescription> - <description>Die SatelliteEquipmentControl-Erweiterung unterstützt Sie beim Feintuning der DiSEqC Einstellungen. + <shortdescription>SatelliteEquipmentControl allows you to fine-tune DiSEqC-settings</shortdescription> + <description>With the SatelliteEquipmentControl plugin it is possible to fine-tune DiSEqC-settings. </description> <screenshot src="http://www.dreamboxupdate.com/preview/plugin_satcontrol.jpg" /> </info> + <files type="package"> <!-- without version, without .ipk --> <file type="package" name="enigma2-plugin-systemplugins-satelliteequipmentcontrol" /> </files> diff --git a/lib/python/Plugins/SystemPlugins/SatelliteEquipmentControl/plugin.py b/lib/python/Plugins/SystemPlugins/SatelliteEquipmentControl/plugin.py index ec472e72..3a8c75c0 100644 --- a/lib/python/Plugins/SystemPlugins/SatelliteEquipmentControl/plugin.py +++ b/lib/python/Plugins/SystemPlugins/SatelliteEquipmentControl/plugin.py @@ -71,6 +71,6 @@ def SecSetupStart(menuid): def Plugins(**kwargs): if (nimmgr.hasNimType("DVB-S")): - return PluginDescriptor(name=_("Satellite Equipment Setup"), description="Setup your satellite equipment", where = PluginDescriptor.WHERE_MENU, fnc=SecSetupStart) + return PluginDescriptor(name=_("Satellite Equipment Setup"), description="Setup your satellite equipment", where = PluginDescriptor.WHERE_MENU, needsRestart = False, fnc=SecSetupStart) else: return [] diff --git a/lib/python/Plugins/SystemPlugins/Satfinder/meta/Makefile.am b/lib/python/Plugins/SystemPlugins/Satfinder/meta/Makefile.am index ba035e86..23499a4e 100755 --- a/lib/python/Plugins/SystemPlugins/Satfinder/meta/Makefile.am +++ b/lib/python/Plugins/SystemPlugins/Satfinder/meta/Makefile.am @@ -1,4 +1,4 @@ -installdir = $(datadir)/meta/ +installdir = $(datadir)/meta dist_install_DATA = plugin_satfinder.xml diff --git a/lib/python/Plugins/SystemPlugins/Satfinder/meta/plugin_satfinder.xml b/lib/python/Plugins/SystemPlugins/Satfinder/meta/plugin_satfinder.xml index e9453deb..fe0c901e 100755 --- a/lib/python/Plugins/SystemPlugins/Satfinder/meta/plugin_satfinder.xml +++ b/lib/python/Plugins/SystemPlugins/Satfinder/meta/plugin_satfinder.xml @@ -1,28 +1,18 @@ <default> <prerequisites> <tag type="SAT" /> - <tag type="Software" /> + <tag type="System" /> </prerequisites> - <info language="en"> + <info> <author>Dream Multimedia</author> <name>Satfinder</name> <packagename>enigma2-plugin-systemplugins-satfinder</packagename> - <shortdescription>Satfinder helps you to align your dish.</shortdescription> - <description>The Satfinder extension helps you to align your dish.\n + <shortdescription>Satfinder helps you to align your dish</shortdescription> + <description>The Satfinder plugin helps you to align your dish.\n It shows you informations about signal rate and errors. </description> <screenshot src="http://www.dreamboxupdate.com/preview/plugin_satfinder.jpg" /> </info> - <info language="de"> - <author>Dream Multimedia</author> - <name>Satfinder</name> - <packagename>enigma2-plugin-systemplugins-satfinder</packagename> - <shortdescription>Satfinder unterstützt Sie beim ausrichten ihrer Satellitenanlage.</shortdescription> - <description>Die Satfinder-Erweiterung unterstützt Sie beim Ausrichten ihrer Satellitenanlage.\n - Es zeigt Ihnen Daten wie Signalstärke und Fehlerrate an. - </description> - <screenshot src="http://www.dreamboxupdate.com/preview/plugin_satfinder.jpg" /> - </info> <files type="package"> <!-- without version, without .ipk --> <file type="package" name="enigma2-plugin-systemplugins-satfinder" /> </files> diff --git a/lib/python/Plugins/SystemPlugins/Satfinder/plugin.py b/lib/python/Plugins/SystemPlugins/Satfinder/plugin.py index d4fe6b58..e611ca53 100644 --- a/lib/python/Plugins/SystemPlugins/Satfinder/plugin.py +++ b/lib/python/Plugins/SystemPlugins/Satfinder/plugin.py @@ -79,15 +79,15 @@ class Satfinder(ScanSetup): self.scan_sat.system.value = eDVBFrontendParametersSatellite.System_DVB_S self.list.append(getConfigListEntry(_('Frequency'), self.scan_sat.frequency)) self.list.append(getConfigListEntry(_('Inversion'), self.scan_sat.inversion)) - self.list.append(getConfigListEntry(_('Symbol Rate'), self.scan_sat.symbolrate)) - self.list.append(getConfigListEntry(_("Polarity"), self.scan_sat.polarization)) + self.list.append(getConfigListEntry(_('Symbol rate'), self.scan_sat.symbolrate)) + self.list.append(getConfigListEntry(_('Polarization'), self.scan_sat.polarization)) if self.scan_sat.system.value == eDVBFrontendParametersSatellite.System_DVB_S: self.list.append(getConfigListEntry(_("FEC"), self.scan_sat.fec)) elif self.scan_sat.system.value == eDVBFrontendParametersSatellite.System_DVB_S2: self.list.append(getConfigListEntry(_("FEC"), self.scan_sat.fec_s2)) self.modulationEntry = getConfigListEntry(_('Modulation'), self.scan_sat.modulation) self.list.append(self.modulationEntry) - self.list.append(getConfigListEntry(_('Rolloff'), self.scan_sat.rolloff)) + self.list.append(getConfigListEntry(_('Roll-off'), self.scan_sat.rolloff)) self.list.append(getConfigListEntry(_('Pilot'), self.scan_sat.pilot)) elif self.tuning_transponder and self.tuning_type.value == "predefined_transponder": self.list.append(getConfigListEntry(_("Transponder"), self.tuning_transponder)) @@ -276,6 +276,6 @@ def SatfinderStart(menuid, **kwargs): def Plugins(**kwargs): if (nimmanager.hasNimType("DVB-S")): - return PluginDescriptor(name=_("Satfinder"), description="Helps setting up your dish", where = PluginDescriptor.WHERE_MENU, fnc=SatfinderStart) + return PluginDescriptor(name=_("Satfinder"), description="Helps setting up your dish", where = PluginDescriptor.WHERE_MENU, needsRestart = False, fnc=SatfinderStart) else: return [] diff --git a/lib/python/Plugins/SystemPlugins/SkinSelector/meta/Makefile.am b/lib/python/Plugins/SystemPlugins/SkinSelector/meta/Makefile.am index d29fb002..992f4b0e 100755 --- a/lib/python/Plugins/SystemPlugins/SkinSelector/meta/Makefile.am +++ b/lib/python/Plugins/SystemPlugins/SkinSelector/meta/Makefile.am @@ -1,4 +1,4 @@ -installdir = $(datadir)/meta/ +installdir = $(datadir)/meta dist_install_DATA = plugin_skinselector.xml diff --git a/lib/python/Plugins/SystemPlugins/SkinSelector/meta/plugin_skinselector.xml b/lib/python/Plugins/SystemPlugins/SkinSelector/meta/plugin_skinselector.xml index 717f732b..73544a56 100755 --- a/lib/python/Plugins/SystemPlugins/SkinSelector/meta/plugin_skinselector.xml +++ b/lib/python/Plugins/SystemPlugins/SkinSelector/meta/plugin_skinselector.xml @@ -1,28 +1,19 @@ <default> <prerequisites> <tag type="Display" /> - <tag type="System" /> + <tag type="Skin" /> </prerequisites> <info language="en"> <author>Dream Multimedia</author> <name>SkinSelector</name> <packagename>enigma2-plugin-systemplugins-skinselector</packagename> - <shortdescription>SkinSelector shows a menu with selectable skins.</shortdescription> + <shortdescription>SkinSelector shows a menu with selectable skins</shortdescription> <description>The SkinSelector shows a menu with selectable skins.\n It's now easy to change the look and feel of your Dreambox. </description> <screenshot src="http://www.dreamboxupdate.com/preview/plugin_skinselector_en.jpg" /> </info> - <info language="de"> - <author>Dream Multimedia</author> - <name>SkinSelector</name> - <packagename>enigma2-plugin-systemplugins-skinselector</packagename> - <shortdescription>Der SkinSelector zeigt Ihnen ein Menu mit auswählbaren Skins.</shortdescription> - <description>Die SkinSelector Erweiterung zeigt Ihnen ein Menu mit auswählbaren Skins.\n - Sie können nun einfach das Aussehen der grafischen Oberfläche Ihrer Dreambox verändern. - </description> - <screenshot src="http://www.dreamboxupdate.com/preview/plugin_skinselector_de.jpg" /> - </info> + <files type="package"> <!-- without version, without .ipk --> <file type="package" name="enigma2-plugin-systemplugins-skinselector" /> </files> diff --git a/lib/python/Plugins/SystemPlugins/SkinSelector/plugin.py b/lib/python/Plugins/SystemPlugins/SkinSelector/plugin.py index 997b9571..fd2b5e1f 100755..100644 --- a/lib/python/Plugins/SystemPlugins/SkinSelector/plugin.py +++ b/lib/python/Plugins/SystemPlugins/SkinSelector/plugin.py @@ -12,12 +12,13 @@ from Plugins.Plugin import PluginDescriptor from Components.config import config from Tools.Directories import resolveFilename, SCOPE_PLUGINS from os import path, walk +from enigma import eEnv class SkinSelector(Screen): # for i18n: # _("Choose your Skin") skinlist = [] - root = "/usr/share/enigma2/" + root = eEnv.resolve("${datadir}/enigma2/") def __init__(self, session, args = None): @@ -130,4 +131,4 @@ def SkinSelSetup(menuid, **kwargs): return [] def Plugins(**kwargs): - return PluginDescriptor(name="Skinselector", description="Select Your Skin", where = PluginDescriptor.WHERE_MENU, fnc=SkinSelSetup) + return PluginDescriptor(name="Skinselector", description="Select Your Skin", where = PluginDescriptor.WHERE_MENU, needsRestart = False, fnc=SkinSelSetup) diff --git a/lib/python/Plugins/SystemPlugins/SoftwareManager/BackupRestore.py b/lib/python/Plugins/SystemPlugins/SoftwareManager/BackupRestore.py index dcff3ca2..c7c143f2 100755 --- a/lib/python/Plugins/SystemPlugins/SoftwareManager/BackupRestore.py +++ b/lib/python/Plugins/SystemPlugins/SoftwareManager/BackupRestore.py @@ -11,16 +11,15 @@ from Components.config import config from Components.ConfigList import ConfigList,ConfigListScreen from Components.FileList import MultiFileSelectList from Plugins.Plugin import PluginDescriptor -from enigma import eTimer +from enigma import eTimer, eEnv from Tools.Directories import * from os import popen, path, makedirs, listdir, access, stat, rename, remove, W_OK, R_OK from time import gmtime, strftime, localtime from datetime import date - config.plugins.configurationbackup = ConfigSubsection() config.plugins.configurationbackup.backuplocation = ConfigText(default = '/media/hdd/', visible_width = 50, fixed_size = False) -config.plugins.configurationbackup.backupdirs = ConfigLocations(default=['/etc/enigma2/', '/etc/network/interfaces', '/etc/wpa_supplicant.conf', '/etc/resolv.conf', '/etc/default_gw', '/etc/hostname']) +config.plugins.configurationbackup.backupdirs = ConfigLocations(default=[eEnv.resolve('${sysconfdir}/enigma2/'), '/etc/network/interfaces', '/etc/wpa_supplicant.conf', '/etc/wpa_supplicant.ath0.conf', '/etc/wpa_supplicant.wlan0.conf', '/etc/resolv.conf', '/etc/default_gw', '/etc/hostname']) def getBackupPath(): backuppath = config.plugins.configurationbackup.backuplocation.value @@ -251,8 +250,9 @@ class RestoreMenu(Screen): def KeyOk(self): if (self.exe == False) and (self.entry == True): self.sel = self["filelist"].getCurrent() - self.val = self.path + "/" + self.sel - self.session.openWithCallback(self.startRestore, MessageBox, _("Are you sure you want to restore\nfollowing backup:\n") + self.sel + _("\nSystem will restart after the restore!")) + if self.sel: + self.val = self.path + "/" + self.sel + self.session.openWithCallback(self.startRestore, MessageBox, _("Are you sure you want to restore\nfollowing backup:\n") + self.sel + _("\nSystem will restart after the restore!")) def keyCancel(self): self.close() @@ -265,8 +265,9 @@ class RestoreMenu(Screen): def deleteFile(self): if (self.exe == False) and (self.entry == True): self.sel = self["filelist"].getCurrent() - self.val = self.path + "/" + self.sel - self.session.openWithCallback(self.startDelete, MessageBox, _("Are you sure you want to delete\nfollowing backup:\n" + self.sel )) + if self.sel: + self.val = self.path + "/" + self.sel + self.session.openWithCallback(self.startDelete, MessageBox, _("Are you sure you want to delete\nfollowing backup:\n") + self.sel) def startDelete(self, ret = False): if (ret == True): diff --git a/lib/python/Plugins/SystemPlugins/SoftwareManager/ImageWizard.py b/lib/python/Plugins/SystemPlugins/SoftwareManager/ImageWizard.py index 1797e4fe..3ea787c1 100755 --- a/lib/python/Plugins/SystemPlugins/SoftwareManager/ImageWizard.py +++ b/lib/python/Plugins/SystemPlugins/SoftwareManager/ImageWizard.py @@ -9,13 +9,15 @@ from Plugins.Plugin import PluginDescriptor from Tools.Directories import fileExists, resolveFilename, SCOPE_PLUGINS, SCOPE_SKIN_IMAGE from Components.Pixmap import Pixmap, MovingPixmap, MultiPixmap from os import popen, path, makedirs, listdir, access, stat, rename, remove, W_OK, R_OK +from enigma import eEnv from Components.config import config, getConfigListEntry, ConfigSubsection, ConfigText, ConfigLocations, ConfigBoolean from Components.Harddisk import harddiskmanager + config.misc.firstrun = ConfigBoolean(default = True) config.plugins.configurationbackup = ConfigSubsection() config.plugins.configurationbackup.backuplocation = ConfigText(default = '/media/hdd/', visible_width = 50, fixed_size = False) -config.plugins.configurationbackup.backupdirs = ConfigLocations(default=['/etc/enigma2/', '/etc/network/interfaces', '/etc/wpa_supplicant.conf']) +config.plugins.configurationbackup.backupdirs = ConfigLocations(default=[eEnv.resolve('${sysconfdir}/enigma2/'), '/etc/network/interfaces', '/etc/wpa_supplicant.conf', '/etc/wpa_supplicant.ath0.conf', '/etc/wpa_supplicant.wlan0.conf', '/etc/resolv.conf', '/etc/default_gw', '/etc/hostname']) backupfile = "enigma2settingsbackup.tar.gz" diff --git a/lib/python/Plugins/SystemPlugins/SoftwareManager/SoftwareTools.py b/lib/python/Plugins/SystemPlugins/SoftwareManager/SoftwareTools.py index e8cf6dc2..ec2f82cc 100755 --- a/lib/python/Plugins/SystemPlugins/SoftwareManager/SoftwareTools.py +++ b/lib/python/Plugins/SystemPlugins/SoftwareManager/SoftwareTools.py @@ -1,4 +1,5 @@ -from enigma import eConsoleAppContainer +# -*- coding: iso-8859-1 -*- +from enigma import eConsoleAppContainer,eTPM from Components.Console import Console from Components.About import about from Components.DreamInfoHandler import DreamInfoHandler @@ -7,8 +8,53 @@ from Components.Sources.List import List from Components.Ipkg import IpkgComponent from Components.Network import iNetwork from Tools.Directories import pathExists, fileExists, resolveFilename, SCOPE_METADIR - +from Tools.HardwareInfo import HardwareInfo +import hashlib from time import time +from os import urandom + +rootkey = ['\x9f', '|', '\xe4', 'G', '\xc9', '\xb4', '\xf4', '#', '&', '\xce', '\xb3', '\xfe', '\xda', '\xc9', 'U', '`', '\xd8', '\x8c', 's', 'o', '\x90', '\x9b', '\\', 'b', '\xc0', '\x89', '\xd1', '\x8c', '\x9e', 'J', 'T', '\xc5', 'X', '\xa1', '\xb8', '\x13', '5', 'E', '\x02', '\xc9', '\xb2', '\xe6', 't', '\x89', '\xde', '\xcd', '\x9d', '\x11', '\xdd', '\xc7', '\xf4', '\xe4', '\xe4', '\xbc', '\xdb', '\x9c', '\xea', '}', '\xad', '\xda', 't', 'r', '\x9b', '\xdc', '\xbc', '\x18', '3', '\xe7', '\xaf', '|', '\xae', '\x0c', '\xe3', '\xb5', '\x84', '\x8d', '\r', '\x8d', '\x9d', '2', '\xd0', '\xce', '\xd5', 'q', '\t', '\x84', 'c', '\xa8', ')', '\x99', '\xdc', '<', '"', 'x', '\xe8', '\x87', '\x8f', '\x02', ';', 'S', 'm', '\xd5', '\xf0', '\xa3', '_', '\xb7', 'T', '\t', '\xde', '\xa7', '\xf1', '\xc9', '\xae', '\x8a', '\xd7', '\xd2', '\xcf', '\xb2', '.', '\x13', '\xfb', '\xac', 'j', '\xdf', '\xb1', '\x1d', ':', '?'] + +def bin2long(s): + return reduce( lambda x,y:(x<<8L)+y, map(ord, s)) + +def long2bin(l): + res = "" + for byte in range(128): + res += chr((l >> (1024 - (byte + 1) * 8)) & 0xff) + return res + +def rsa_pub1024(src, mod): + return long2bin(pow(bin2long(src), 65537, bin2long(mod))) + +def decrypt_block(src, mod): + if len(src) != 128 and len(src) != 202: + return None + dest = rsa_pub1024(src[:128], mod) + hash = hashlib.sha1(dest[1:107]) + if len(src) == 202: + hash.update(src[131:192]) + result = hash.digest() + if result == dest[107:127]: + return dest + return None + +def validate_cert(cert, key): + buf = decrypt_block(cert[8:], key) + if buf is None: + return None + return buf[36:107] + cert[139:196] + +def read_random(): + try: + xor = lambda a,b: ''.join(chr(ord(c)^ord(d)) for c,d in zip(a,b*100)) + random = urandom(8) + x = str(time())[-8:] + result = xor(random, x) + + return result + except: + return None class SoftwareTools(DreamInfoHandler): lastDownloadDate = None @@ -27,8 +73,9 @@ class SoftwareTools(DreamInfoHandler): else: self.ImageVersion = 'Stable' self.language = language.getLanguage()[:2] # getLanguage returns e.g. "fi_FI" for "language_country" - DreamInfoHandler.__init__(self, self.statusCallback, blocking = False, neededTag = 'ALL_TAGS', neededFlag = self.ImageVersion, language = self.language) + DreamInfoHandler.__init__(self, self.statusCallback, blocking = False, neededTag = 'ALL_TAGS', neededFlag = self.ImageVersion) self.directory = resolveFilename(SCOPE_METADIR) + self.hardware_info = HardwareInfo() self.list = List([]) self.NotifierCallback = None self.Console = Console() @@ -49,54 +96,126 @@ class SoftwareTools(DreamInfoHandler): def checkNetworkCB(self,data): if data is not None: if data <= 2: - SoftwareTools.NetworkConnectionAvailable = True + self.NetworkConnectionAvailable = True self.getUpdates() else: - SoftwareTools.NetworkConnectionAvailable = False + self.NetworkConnectionAvailable = False self.getUpdates() def getUpdates(self, callback = None): - if SoftwareTools.NetworkConnectionAvailable == True: - SoftwareTools.lastDownloadDate = time() - if SoftwareTools.list_updating is False and callback is None: - SoftwareTools.list_updating = True - self.ipkg.startCmd(IpkgComponent.CMD_UPDATE) - elif SoftwareTools.list_updating is False and callback is not None: - SoftwareTools.list_updating = True - self.NotifierCallback = callback - self.ipkg.startCmd(IpkgComponent.CMD_UPDATE) - elif SoftwareTools.list_updating is True and callback is not None: - #update info collecting already in progress - self.NotifierCallback = callback + if self.lastDownloadDate is None: + if self.hardware_info.device_name != "dm7025": + etpm = eTPM() + l2cert = etpm.getCert(eTPM.TPMD_DT_LEVEL2_CERT) + if l2cert is None: + return + l2key = validate_cert(l2cert, rootkey) + if l2key is None: + return + l3cert = etpm.getCert(eTPM.TPMD_DT_LEVEL3_CERT) + if l3cert is None: + return + l3key = validate_cert(l3cert, l2key) + if l3key is None: + return + rnd = read_random() + if rnd is None: + return + val = etpm.challenge(rnd) + result = decrypt_block(val, l3key) + if self.hardware_info.device_name == "dm7025" or result[80:88] == rnd: + if self.NetworkConnectionAvailable == True: + self.lastDownloadDate = time() + if self.list_updating is False and callback is None: + self.list_updating = True + self.ipkg.startCmd(IpkgComponent.CMD_UPDATE) + elif self.list_updating is False and callback is not None: + self.list_updating = True + self.NotifierCallback = callback + self.ipkg.startCmd(IpkgComponent.CMD_UPDATE) + elif self.list_updating is True and callback is not None: + self.NotifierCallback = callback + else: + self.list_updating = False + if callback is not None: + callback(False) + elif self.NotifierCallback is not None: + self.NotifierCallback(False) + else: + self.NetworkConnectionAvailable = False + self.list_updating = False + if callback is not None: + callback(False) + elif self.NotifierCallback is not None: + self.NotifierCallback(False) else: - SoftwareTools.list_updating = False - if callback is not None: - callback(False) - elif self.NotifierCallback is not None: - self.NotifierCallback(False) + if self.NetworkConnectionAvailable == True: + self.lastDownloadDate = time() + if self.list_updating is False and callback is None: + self.list_updating = True + self.ipkg.startCmd(IpkgComponent.CMD_UPDATE) + elif self.list_updating is False and callback is not None: + self.list_updating = True + self.NotifierCallback = callback + self.ipkg.startCmd(IpkgComponent.CMD_UPDATE) + elif self.list_updating is True and callback is not None: + self.NotifierCallback = callback + else: + if self.list_updating and callback is not None: + if self.hardware_info.device_name != "dm7025": + etpm = eTPM() + l2cert = etpm.getCert(eTPM.TPMD_DT_LEVEL2_CERT) + if l2cert is None: + return + l2key = validate_cert(l2cert, rootkey) + if l2key is None: + return + l3cert = etpm.getCert(eTPM.TPMD_DT_LEVEL3_CERT) + if l3cert is None: + return + l3key = validate_cert(l3cert, l2key) + if l3key is None: + return + rnd = read_random() + if rnd is None: + return + val = etpm.challenge(rnd) + result = decrypt_block(val, l3key) + if self.hardware_info.device_name == "dm7025" or result[80:88] == rnd: + self.NotifierCallback = callback + self.startIpkgListAvailable() + else: + self.list_updating = False + if callback is not None: + callback(False) + elif self.NotifierCallback is not None: + self.NotifierCallback(False) def ipkgCallback(self, event, param): if event == IpkgComponent.EVENT_ERROR: - SoftwareTools.list_updating = False + self.list_updating = False + if self.NotifierCallback is not None: + self.NotifierCallback(False) elif event == IpkgComponent.EVENT_DONE: - if SoftwareTools.list_updating: + if self.list_updating: self.startIpkgListAvailable() + #print event, "-", param pass def startIpkgListAvailable(self, callback = None): if callback is not None: - SoftwareTools.list_updating = True - if SoftwareTools.list_updating: + self.list_updating = True + if self.list_updating: if not self.UpdateConsole: self.UpdateConsole = Console() - cmd = "ipkg list" + cmd = "opkg list" self.UpdateConsole.ePopen(cmd, self.IpkgListAvailableCB, callback) def IpkgListAvailableCB(self, result, retval, extra_args = None): (callback) = extra_args if result: - if SoftwareTools.list_updating: - SoftwareTools.available_packetlist = [] + if self.list_updating: + self.available_packetlist = [] for x in result.splitlines(): tokens = x.split(' - ') name = tokens[0].strip() @@ -104,7 +223,7 @@ class SoftwareTools(DreamInfoHandler): l = len(tokens) version = l > 1 and tokens[1].strip() or "" descr = l > 2 and tokens[2].strip() or "" - SoftwareTools.available_packetlist.append([name, version, descr]) + self.available_packetlist.append([name, version, descr]) if callback is None: self.startInstallMetaPackage() else: @@ -112,7 +231,7 @@ class SoftwareTools(DreamInfoHandler): if len(self.UpdateConsole.appContainers) == 0: callback(True) else: - SoftwareTools.list_updating = False + self.list_updating = False if self.UpdateConsole: if len(self.UpdateConsole.appContainers) == 0: if callback is not None: @@ -120,14 +239,17 @@ class SoftwareTools(DreamInfoHandler): def startInstallMetaPackage(self, callback = None): if callback is not None: - SoftwareTools.list_updating = True - if SoftwareTools.list_updating: - if not self.UpdateConsole: - self.UpdateConsole = Console() - cmd = "ipkg install enigma2-meta enigma2-plugins-meta enigma2-skins-meta" - self.UpdateConsole.ePopen(cmd, self.InstallMetaPackageCB, callback) + self.list_updating = True + if self.list_updating: + if self.NetworkConnectionAvailable == True: + if not self.UpdateConsole: + self.UpdateConsole = Console() + cmd = "opkg install enigma2-meta enigma2-plugins-meta enigma2-skins-meta enigma2-drivers-meta" + self.UpdateConsole.ePopen(cmd, self.InstallMetaPackageCB, callback) + else: + self.InstallMetaPackageCB(True) - def InstallMetaPackageCB(self, result, retval, extra_args = None): + def InstallMetaPackageCB(self, result, retval = None, extra_args = None): (callback) = extra_args if result: self.fillPackagesIndexList() @@ -138,7 +260,7 @@ class SoftwareTools(DreamInfoHandler): if len(self.UpdateConsole.appContainers) == 0: callback(True) else: - SoftwareTools.list_updating = False + self.list_updating = False if self.UpdateConsole: if len(self.UpdateConsole.appContainers) == 0: if callback is not None: @@ -146,24 +268,32 @@ class SoftwareTools(DreamInfoHandler): def startIpkgListInstalled(self, callback = None): if callback is not None: - SoftwareTools.list_updating = True - if SoftwareTools.list_updating: + self.list_updating = True + if self.list_updating: if not self.UpdateConsole: self.UpdateConsole = Console() - cmd = "ipkg list_installed" + cmd = "opkg list-installed" self.UpdateConsole.ePopen(cmd, self.IpkgListInstalledCB, callback) def IpkgListInstalledCB(self, result, retval, extra_args = None): (callback) = extra_args if result: - SoftwareTools.installed_packetlist = {} + self.installed_packetlist = {} for x in result.splitlines(): tokens = x.split(' - ') name = tokens[0].strip() if not any(name.endswith(x) for x in self.unwanted_extensions): l = len(tokens) version = l > 1 and tokens[1].strip() or "" - SoftwareTools.installed_packetlist[name] = version + self.installed_packetlist[name] = version + for package in self.packagesIndexlist[:]: + if not self.verifyPrerequisites(package[0]["prerequisites"]): + self.packagesIndexlist.remove(package) + for package in self.packagesIndexlist[:]: + attributes = package[0]["attributes"] + if attributes.has_key("packagetype"): + if attributes["packagetype"] == "internal": + self.packagesIndexlist.remove(package) if callback is None: self.countUpdates() else: @@ -171,26 +301,26 @@ class SoftwareTools(DreamInfoHandler): if len(self.UpdateConsole.appContainers) == 0: callback(True) else: - SoftwareTools.list_updating = False + self.list_updating = False if self.UpdateConsole: if len(self.UpdateConsole.appContainers) == 0: if callback is not None: callback(False) def countUpdates(self, callback = None): - SoftwareTools.available_updates = 0 - SoftwareTools.available_updatelist = [] + self.available_updates = 0 + self.available_updatelist = [] for package in self.packagesIndexlist[:]: attributes = package[0]["attributes"] packagename = attributes["packagename"] - for x in SoftwareTools.available_packetlist: + for x in self.available_packetlist: if x[0] == packagename: - if SoftwareTools.installed_packetlist.has_key(packagename): - if SoftwareTools.installed_packetlist[packagename] != x[1]: - SoftwareTools.available_updates +=1 - SoftwareTools.available_updatelist.append([packagename]) + if self.installed_packetlist.has_key(packagename): + if self.installed_packetlist[packagename] != x[1]: + self.available_updates +=1 + self.available_updatelist.append([packagename]) - SoftwareTools.list_updating = False + self.list_updating = False if self.UpdateConsole: if len(self.UpdateConsole.appContainers) == 0: if callback is not None: @@ -203,7 +333,7 @@ class SoftwareTools(DreamInfoHandler): def startIpkgUpdate(self, callback = None): if not self.Console: self.Console = Console() - cmd = "ipkg update" + cmd = "opkg update" self.Console.ePopen(cmd, self.IpkgUpdateCB, callback) def IpkgUpdateCB(self, result, retval, extra_args = None): @@ -216,6 +346,7 @@ class SoftwareTools(DreamInfoHandler): callback = None def cleanupSoftwareTools(self): + self.list_updating = False if self.NotifierCallback is not None: self.NotifierCallback = None self.ipkg.stop() @@ -228,4 +359,14 @@ class SoftwareTools(DreamInfoHandler): for name in self.UpdateConsole.appContainers.keys(): self.UpdateConsole.kill(name) -iSoftwareTools = SoftwareTools()
\ No newline at end of file + def verifyPrerequisites(self, prerequisites): + if prerequisites.has_key("hardware"): + hardware_found = False + for hardware in prerequisites["hardware"]: + if hardware == self.hardware_info.device_name: + hardware_found = True + if not hardware_found: + return False + return True + +iSoftwareTools = SoftwareTools() diff --git a/lib/python/Plugins/SystemPlugins/SoftwareManager/meta/Makefile.am b/lib/python/Plugins/SystemPlugins/SoftwareManager/meta/Makefile.am index 05a87d5a..bf064c29 100755 --- a/lib/python/Plugins/SystemPlugins/SoftwareManager/meta/Makefile.am +++ b/lib/python/Plugins/SystemPlugins/SoftwareManager/meta/Makefile.am @@ -1,4 +1,4 @@ -installdir = $(datadir)/meta/ +installdir = $(datadir)/meta dist_install_DATA = plugin_softwaremanager.xml diff --git a/lib/python/Plugins/SystemPlugins/SoftwareManager/meta/plugin_softwaremanager.xml b/lib/python/Plugins/SystemPlugins/SoftwareManager/meta/plugin_softwaremanager.xml index cd425c33..4135a212 100755 --- a/lib/python/Plugins/SystemPlugins/SoftwareManager/meta/plugin_softwaremanager.xml +++ b/lib/python/Plugins/SystemPlugins/SoftwareManager/meta/plugin_softwaremanager.xml @@ -3,27 +3,17 @@ <tag type="Software" /> <tag type="System" /> </prerequisites> - <info language="en"> + <info> <author>Dream Multimedia</author> <name>SoftwareManager</name> <packagename>enigma2-plugin-systemplugins-softwaremanager</packagename> - <shortdescription>SoftwareManager manages your Dreambox software.</shortdescription> + <shortdescription>SoftwareManager manages your Dreambox software</shortdescription> <description>The SoftwareManager manages your Dreambox software.\n - It's easy to update your receiver's software, install or remove extensions or even backup and restore your system settings. + It's easy to update your receiver's software, install or remove plugins or even backup and restore your system settings. </description> <screenshot src="http://www.dreamboxupdate.com/preview/plugin_softwaremanager_en.jpg" /> </info> - <info language="de"> - <author>Dream Multimedia</author> - <name>SoftwareManager</name> - <packagename>enigma2-plugin-systemplugins-softwaremanager</packagename> - <shortdescription>Der SoftwareManager verwaltet Ihre Dreambox Software.</shortdescription> - <description>Der SoftwareManager verwaltet Ihre Dreambox Software.\n - Sie können nun einfach Ihre Dreambox-Software aktualisieren, neue Erweiterungen installieren oder entfernen, - oder ihre Einstellungen sichern und wiederherstellen. - </description> - <screenshot src="http://www.dreamboxupdate.com/preview/plugin_softwaremanager_de.jpg" /> - </info> + <files type="package"> <!-- without version, without .ipk --> <file type="package" name="enigma2-plugin-systemplugins-softwaremanager" /> </files> diff --git a/lib/python/Plugins/SystemPlugins/SoftwareManager/plugin.py b/lib/python/Plugins/SystemPlugins/SoftwareManager/plugin.py index 4dbe7f70..12fd6094 100755..100644 --- a/lib/python/Plugins/SystemPlugins/SoftwareManager/plugin.py +++ b/lib/python/Plugins/SystemPlugins/SoftwareManager/plugin.py @@ -14,7 +14,8 @@ from Components.MenuList import MenuList from Components.Sources.List import List from Components.Slider import Slider from Components.Harddisk import harddiskmanager -from Components.config import config,getConfigListEntry, ConfigSubsection, ConfigText, ConfigLocations +from Components.config import config,getConfigListEntry, ConfigSubsection, ConfigText, ConfigLocations, ConfigYesNo, ConfigSelection +from Components.ConfigList import ConfigListScreen from Components.Console import Console from Components.MultiContent import MultiContentEntryText, MultiContentEntryPixmapAlphaTest from Components.SelectionList import SelectionList @@ -26,7 +27,8 @@ from Components.AVSwitch import AVSwitch from Components.Network import iNetwork from Tools.Directories import pathExists, fileExists, resolveFilename, SCOPE_PLUGINS, SCOPE_CURRENT_PLUGIN, SCOPE_CURRENT_SKIN, SCOPE_METADIR from Tools.LoadPixmap import LoadPixmap -from enigma import eTimer, quitMainloop, RT_HALIGN_LEFT, RT_VALIGN_CENTER, eListboxPythonMultiContent, eListbox, gFont, getDesktop, ePicLoad +from Tools.NumericalTextInput import NumericalTextInput +from enigma import eTimer, quitMainloop, RT_HALIGN_LEFT, RT_VALIGN_CENTER, eListboxPythonMultiContent, eListbox, gFont, getDesktop, ePicLoad, eRCInput, getPrevAsciiCode, eEnv from cPickle import dump, load from os import path as os_path, system as os_system, unlink, stat, mkdir, popen, makedirs, listdir, access, rename, remove, W_OK, R_OK, F_OK from time import time, gmtime, strftime, localtime @@ -41,7 +43,15 @@ from SoftwareTools import iSoftwareTools config.plugins.configurationbackup = ConfigSubsection() config.plugins.configurationbackup.backuplocation = ConfigText(default = '/media/hdd/', visible_width = 50, fixed_size = False) -config.plugins.configurationbackup.backupdirs = ConfigLocations(default=['/etc/enigma2/', '/etc/network/interfaces', '/etc/wpa_supplicant.conf', '/etc/resolv.conf', '/etc/default_gw', '/etc/hostname']) +config.plugins.configurationbackup.backupdirs = ConfigLocations(default=[eEnv.resolve('${sysconfdir}/enigma2/'), '/etc/network/interfaces', '/etc/wpa_supplicant.conf', '/etc/wpa_supplicant.ath0.conf', '/etc/wpa_supplicant.wlan0.conf', '/etc/resolv.conf', '/etc/default_gw', '/etc/hostname']) + +config.plugins.softwaremanager = ConfigSubsection() +config.plugins.softwaremanager.overwriteConfigFiles = ConfigSelection( + [ + ("Y", _("Yes, always")), + ("N", _("No, never")), + ("ask", _("Always ask")) + ], "Y") def write_cache(cache_file, cache_data): #Does a cPickle dump @@ -109,13 +119,16 @@ class UpdatePluginMenu(Screen): self.menu = args self.list = [] self.oktext = _("\nPress OK on your remote control to continue.") + self.menutext = _("Press MENU on your remote control for additional options.") + self.infotext = _("Press INFO on your remote control for additional information.") self.text = "" self.backupdirs = ' '.join( config.plugins.configurationbackup.backupdirs.value ) if self.menu == 0: + print "building menu entries" self.list.append(("install-extensions", _("Manage extensions"), _("\nManage extensions or plugins for your Dreambox" ) + self.oktext, None)) self.list.append(("software-update", _("Software update"), _("\nOnline update of your Dreambox software." ) + self.oktext, None)) self.list.append(("software-restore", _("Software restore"), _("\nRestore your Dreambox with a new firmware." ) + self.oktext, None)) - self.list.append(("system-backup", _("Backup system settings"), _("\nBackup your Dreambox settings." ) + self.oktext, None)) + self.list.append(("system-backup", _("Backup system settings"), _("\nBackup your Dreambox settings." ) + self.oktext + "\n\n" + self.infotext, None)) self.list.append(("system-restore",_("Restore system settings"), _("\nRestore your Dreambox settings." ) + self.oktext, None)) self.list.append(("ipkg-install", _("Install local extension"), _("\nScan for local extensions and install them." ) + self.oktext, None)) for p in plugins.getPlugins(PluginDescriptor.WHERE_SOFTWAREMANAGER): @@ -136,7 +149,7 @@ class UpdatePluginMenu(Screen): elif self.menu == 1: self.list.append(("advancedrestore", _("Advanced restore"), _("\nRestore your backups by date." ) + self.oktext, None)) self.list.append(("backuplocation", _("Choose backup location"), _("\nSelect your backup device.\nCurrent device: " ) + config.plugins.configurationbackup.backuplocation.value + self.oktext, None)) - self.list.append(("backupfiles", _("Choose backup files"), _("Select files for backup. Currently selected:\n" ) + self.backupdirs + self.oktext, None)) + self.list.append(("backupfiles", _("Choose backup files"), _("Select files for backup.") + self.oktext + "\n\n" + self.infotext, None)) if config.usage.setup_level.index >= 2: # expert+ self.list.append(("ipkg-manager", _("Packet management"), _("\nView, install and remove available or installed packages." ) + self.oktext, None)) self.list.append(("ipkg-source",_("Choose upgrade source"), _("\nEdit the upgrade source address." ) + self.oktext, None)) @@ -156,25 +169,25 @@ class UpdatePluginMenu(Screen): self["menu"] = List(self.list) self["key_red"] = StaticText(_("Close")) - self["status"] = StaticText("") + self["status"] = StaticText(self.menutext) - self["shortcuts"] = ActionMap(["ShortcutActions", "WizardActions", "InfobarEPGActions"], + self["shortcuts"] = ActionMap(["ShortcutActions", "WizardActions", "InfobarEPGActions", "MenuActions"], { "ok": self.go, "back": self.close, "red": self.close, + "menu": self.handleMenu, + "showEventInfo": self.handleInfo, }, -1) self.onLayoutFinish.append(self.layoutFinished) self.backuppath = getBackupPath() self.backupfile = getBackupFilename() self.fullbackupfilename = self.backuppath + "/" + self.backupfile self.onShown.append(self.setWindowTitle) - #self.onClose.append(self.cleanup) def layoutFinished(self): idx = 0 self["menu"].index = idx - #self.getUpdateInfos() def setWindowTitle(self): self.setTitle(_("Software management")) @@ -201,9 +214,17 @@ class UpdatePluginMenu(Screen): self.text = _("No network connection available.") self["status"].setText(self.text) + def handleMenu(self): + self.session.open(SoftwareManagerSetup) + + def handleInfo(self): + current = self["menu"].getCurrent() + if current: + currentEntry = current[0] + if currentEntry in ("system-backup","backupfiles"): + self.session.open(SoftwareManagerInfo, mode = "backupinfo") def go(self): - #iNetwork.stopPingConsole() current = self["menu"].getCurrent() if current: currentEntry = current[0] @@ -240,9 +261,6 @@ class UpdatePluginMenu(Screen): for x in parts: if not access(x[1], F_OK|R_OK|W_OK) or x[1] == '/': parts.remove(x) - for x in parts: - if x[1].startswith('/autofs/'): - parts.remove(x) if len(parts): self.session.openWithCallback(self.backuplocation_choosen, ChoiceBox, title = _("Please select medium to use as backup location"), list = parts) elif (currentEntry == "backupfiles"): @@ -257,14 +275,20 @@ class UpdatePluginMenu(Screen): def backupfiles_choosen(self, ret): self.backupdirs = ' '.join( config.plugins.configurationbackup.backupdirs.value ) - + config.plugins.configurationbackup.backupdirs.save() + config.plugins.configurationbackup.save() + config.save() + def backuplocation_choosen(self, option): + oldpath = config.plugins.configurationbackup.backuplocation.getValue() if option is not None: config.plugins.configurationbackup.backuplocation.value = str(option[1]) config.plugins.configurationbackup.backuplocation.save() config.plugins.configurationbackup.save() config.save() - self.createBackupfolders() + newpath = config.plugins.configurationbackup.backuplocation.getValue() + if newpath != oldpath: + self.createBackupfolders() def runUpgrade(self, result): if result: @@ -290,6 +314,184 @@ class UpdatePluginMenu(Screen): self.exe = True self.session.open(RestoreScreen, runRestore = True) +class SoftwareManagerSetup(Screen, ConfigListScreen): + + skin = """ + <screen name="SoftwareManagerSetup" position="center,center" size="560,440" title="SoftwareManager setup"> + <ePixmap pixmap="skin_default/buttons/red.png" position="0,0" size="140,40" alphatest="on" /> + <ePixmap pixmap="skin_default/buttons/green.png" position="140,0" size="140,40" alphatest="on" /> + <ePixmap pixmap="skin_default/buttons/yellow.png" position="280,0" size="140,40" alphatest="on" /> + <ePixmap pixmap="skin_default/buttons/blue.png" position="420,0" size="140,40" alphatest="on" /> + <widget source="key_red" render="Label" position="0,0" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#9f1313" transparent="1" /> + <widget source="key_green" render="Label" position="140,0" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#1f771f" transparent="1" /> + <widget source="key_yellow" render="Label" position="280,0" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#a08500" transparent="1" /> + <widget source="key_blue" render="Label" position="420,0" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#18188b" transparent="1" /> + <widget name="config" position="5,50" size="550,350" scrollbarMode="showOnDemand" /> + <ePixmap pixmap="skin_default/div-h.png" position="0,400" zPosition="1" size="560,2" /> + <widget source="introduction" render="Label" position="5,410" size="550,30" zPosition="10" font="Regular;21" halign="center" valign="center" backgroundColor="#25062748" transparent="1" /> + </screen>""" + + def __init__(self, session, skin_path = None): + Screen.__init__(self, session) + self.session = session + self.skin_path = skin_path + if self.skin_path == None: + self.skin_path = resolveFilename(SCOPE_CURRENT_PLUGIN, "SystemPlugins/SoftwareManager") + + self.onChangedEntry = [ ] + self.setup_title = _("Software manager setup") + self.overwriteConfigfilesEntry = None + + self.list = [ ] + ConfigListScreen.__init__(self, self.list, session = session, on_change = self.changedEntry) + + self["actions"] = ActionMap(["SetupActions"], + { + "cancel": self.keyCancel, + "save": self.apply, + }, -2) + + self["key_red"] = StaticText(_("Cancel")) + self["key_green"] = StaticText(_("OK")) + self["key_yellow"] = StaticText() + self["key_blue"] = StaticText() + self["introduction"] = StaticText() + + self.createSetup() + self.onLayoutFinish.append(self.layoutFinished) + + def layoutFinished(self): + self.setTitle(self.setup_title) + + def createSetup(self): + self.list = [ ] + self.overwriteConfigfilesEntry = getConfigListEntry(_("Overwrite configuration files ?"), config.plugins.softwaremanager.overwriteConfigFiles) + self.list.append(self.overwriteConfigfilesEntry) + self["config"].list = self.list + self["config"].l.setSeperation(400) + self["config"].l.setList(self.list) + if not self.selectionChanged in self["config"].onSelectionChanged: + self["config"].onSelectionChanged.append(self.selectionChanged) + self.selectionChanged() + + def selectionChanged(self): + if self["config"].getCurrent() == self.overwriteConfigfilesEntry: + self["introduction"].setText(_("Overwrite configuration files during software upgrade?")) + else: + self["introduction"].setText("") + + def newConfig(self): + pass + + def keyLeft(self): + ConfigListScreen.keyLeft(self) + + def keyRight(self): + ConfigListScreen.keyRight(self) + + def confirm(self, confirmed): + if not confirmed: + print "not confirmed" + return + else: + self.keySave() + + def apply(self): + self.session.openWithCallback(self.confirm, MessageBox, _("Use this settings?"), MessageBox.TYPE_YESNO, timeout = 20, default = True) + + def cancelConfirm(self, result): + if not result: + return + for x in self["config"].list: + x[1].cancel() + self.close() + + def keyCancel(self): + if self["config"].isChanged(): + self.session.openWithCallback(self.cancelConfirm, MessageBox, _("Really close without saving settings?"), MessageBox.TYPE_YESNO, timeout = 20, default = True) + else: + self.close() + + # for summary: + def changedEntry(self): + for x in self.onChangedEntry: + x() + self.selectionChanged() + + def getCurrentEntry(self): + return self["config"].getCurrent()[0] + + def getCurrentValue(self): + return str(self["config"].getCurrent()[1].value) + + def createSummary(self): + from Screens.Setup import SetupSummary + return SetupSummary + + +class SoftwareManagerInfo(Screen): + skin = """ + <screen name="SoftwareManagerInfo" position="center,center" size="560,440" title="SoftwareManager information"> + <ePixmap pixmap="skin_default/buttons/red.png" position="0,0" size="140,40" alphatest="on" /> + <ePixmap pixmap="skin_default/buttons/green.png" position="140,0" size="140,40" alphatest="on" /> + <ePixmap pixmap="skin_default/buttons/yellow.png" position="280,0" size="140,40" alphatest="on" /> + <ePixmap pixmap="skin_default/buttons/blue.png" position="420,0" size="140,40" alphatest="on" /> + <widget source="key_red" render="Label" position="0,0" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#9f1313" transparent="1" /> + <widget source="key_green" render="Label" position="140,0" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#1f771f" transparent="1" /> + <widget source="key_yellow" render="Label" position="280,0" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#a08500" transparent="1" /> + <widget source="key_blue" render="Label" position="420,0" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#18188b" transparent="1" /> + <widget source="list" render="Listbox" position="5,50" size="550,340" scrollbarMode="showOnDemand" selectionDisabled="0"> + <convert type="TemplatedMultiContent"> + {"template": [ + MultiContentEntryText(pos = (5, 0), size = (540, 26), font=0, flags = RT_HALIGN_LEFT | RT_HALIGN_CENTER, text = 0), # index 0 is the name + ], + "fonts": [gFont("Regular", 24),gFont("Regular", 22)], + "itemHeight": 26 + } + </convert> + </widget> + <ePixmap pixmap="skin_default/div-h.png" position="0,400" zPosition="1" size="560,2" /> + <widget source="introduction" render="Label" position="5,410" size="550,30" zPosition="10" font="Regular;21" halign="center" valign="center" backgroundColor="#25062748" transparent="1" /> + </screen>""" + + def __init__(self, session, skin_path = None, mode = None): + Screen.__init__(self, session) + self.session = session + self.mode = mode + self.skin_path = skin_path + if self.skin_path == None: + self.skin_path = resolveFilename(SCOPE_CURRENT_PLUGIN, "SystemPlugins/SoftwareManager") + + self["actions"] = ActionMap(["ShortcutActions", "WizardActions"], + { + "back": self.close, + "red": self.close, + }, -2) + + self.list = [] + self["list"] = List(self.list) + + self["key_red"] = StaticText(_("Close")) + self["key_green"] = StaticText() + self["key_yellow"] = StaticText() + self["key_blue"] = StaticText() + self["introduction"] = StaticText() + + self.onLayoutFinish.append(self.layoutFinished) + + def layoutFinished(self): + self.setTitle(_("Softwaremanager information")) + if self.mode is not None: + self.showInfos() + + def showInfos(self): + if self.mode == "backupinfo": + self.list = [] + backupfiles = config.plugins.configurationbackup.backupdirs.value + for entry in backupfiles: + self.list.append((entry,)) + self['list'].setList(self.list) + class PluginManager(Screen, DreamInfoHandler): @@ -307,8 +509,8 @@ class PluginManager(Screen, DreamInfoHandler): <convert type="TemplatedMultiContent"> {"templates": {"default": (51,[ - MultiContentEntryText(pos = (30, 1), size = (470, 24), font=0, flags = RT_HALIGN_LEFT, text = 0), # index 0 is the name - MultiContentEntryText(pos = (30, 25), size = (470, 24), font=1, flags = RT_HALIGN_LEFT, text = 2), # index 2 is the description + MultiContentEntryText(pos = (0, 1), size = (470, 24), font=0, flags = RT_HALIGN_LEFT, text = 0), # index 0 is the name + MultiContentEntryText(pos = (0, 25), size = (470, 24), font=1, flags = RT_HALIGN_LEFT, text = 2), # index 2 is the description MultiContentEntryPixmapAlphaTest(pos = (475, 0), size = (48, 48), png = 5), # index 5 is the status pixmap MultiContentEntryPixmapAlphaTest(pos = (0, 49), size = (550, 2), png = 6), # index 6 is the div pixmap ]), @@ -366,6 +568,7 @@ class PluginManager(Screen, DreamInfoHandler): self.currentSelectedIndex = None self.currentSelectedPackage = None self.saved_currentSelectedPackage = None + self.restartRequired = False self.onShown.append(self.setWindowTitle) self.onLayoutFinish.append(self.getUpdateInfos) @@ -405,22 +608,23 @@ class PluginManager(Screen, DreamInfoHandler): if status == 'update': statuspng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_CURRENT_PLUGIN, "SystemPlugins/SoftwareManager/upgrade.png")) self.statuslist.append(( _("Updating software catalog"), '', _("Searching for available updates. Please wait..." ),'', '', statuspng, divpng, None, '' )) - self["list"].style = "default" - self['list'].setList(self.statuslist) elif status == 'sync': statuspng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_CURRENT_PLUGIN, "SystemPlugins/SoftwareManager/upgrade.png")) self.statuslist.append(( _("Package list update"), '', _("Searching for new installed or removed packages. Please wait..." ),'', '', statuspng, divpng, None, '' )) - self["list"].style = "default" - self['list'].setList(self.statuslist) elif status == 'error': + self["key_green"].setText(_("Continue")) statuspng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_CURRENT_PLUGIN, "SystemPlugins/SoftwareManager/remove.png")) self.statuslist.append(( _("Error"), '', _("There was an error downloading the packetlist. Please try again." ),'', '', statuspng, divpng, None, '' )) - self["list"].style = "default" - self['list'].setList(self.statuslist) + self["list"].style = "default" + self['list'].setList(self.statuslist) + def getUpdateInfos(self): - self.setState('update') - iSoftwareTools.getUpdates(self.getUpdateInfosCB) + if (iSoftwareTools.lastDownloadDate is not None and iSoftwareTools.NetworkConnectionAvailable is False): + self.rebuildList() + else: + self.setState('update') + iSoftwareTools.startSoftwareTools(self.getUpdateInfosCB) def getUpdateInfosCB(self, retval = None): if retval is not None: @@ -429,9 +633,19 @@ class PluginManager(Screen, DreamInfoHandler): self["status"].setText(_("There are at least ") + str(iSoftwareTools.available_updates) + _(" updates available.")) else: self["status"].setText(_("There are no updates available.")) + self.rebuildList() elif retval is False: - self["status"].setText(_("No network connection available.")) - self.rebuildList() + if iSoftwareTools.lastDownloadDate is None: + self.setState('error') + if iSoftwareTools.NetworkConnectionAvailable: + self["status"].setText(_("Updatefeed not available.")) + else: + self["status"].setText(_("No network connection available.")) + else: + iSoftwareTools.lastDownloadDate = time() + iSoftwareTools.list_updating = True + self.setState('update') + iSoftwareTools.getUpdates(self.getUpdateInfosCB) def rebuildList(self, retval = None): if self.currentSelectedTag is None: @@ -449,10 +663,14 @@ class PluginManager(Screen, DreamInfoHandler): self["key_green"].setText(_("Uninstall")) elif current[4] == 'installable': self["key_green"].setText(_("Install")) + if iSoftwareTools.NetworkConnectionAvailable is False: + self["key_green"].setText("") elif current[4] == 'remove': self["key_green"].setText(_("Undo uninstall")) elif current[4] == 'install': self["key_green"].setText(_("Undo install")) + if iSoftwareTools.NetworkConnectionAvailable is False: + self["key_green"].setText("") self["key_yellow"].setText(_("View details")) self["key_blue"].setText("") if len(self.selectedFiles) == 0 and iSoftwareTools.available_updates is not 0: @@ -502,21 +720,31 @@ class PluginManager(Screen, DreamInfoHandler): if entry[0] == detailsFile: alreadyinList = True if not alreadyinList: - self.selectedFiles.append((detailsFile,current[4],current[3])) - self.currentSelectedPackage = ((detailsFile,current[4],current[3])) + if (iSoftwareTools.NetworkConnectionAvailable is False and current[4] in ('installable','install')): + pass + else: + self.selectedFiles.append((detailsFile,current[4],current[3])) + self.currentSelectedPackage = ((detailsFile,current[4],current[3])) if current[4] == 'installed': self.list[idx] = self.buildEntryComponent(current[0], current[1], current[2], current[3], 'remove', True) elif current[4] == 'installable': - self.list[idx] = self.buildEntryComponent(current[0], current[1], current[2], current[3], 'install', True) + if iSoftwareTools.NetworkConnectionAvailable: + self.list[idx] = self.buildEntryComponent(current[0], current[1], current[2], current[3], 'install', True) elif current[4] == 'remove': self.list[idx] = self.buildEntryComponent(current[0], current[1], current[2], current[3], 'installed', False) elif current[4] == 'install': - self.list[idx] = self.buildEntryComponent(current[0], current[1], current[2], current[3], 'installable',False) + if iSoftwareTools.NetworkConnectionAvailable: + self.list[idx] = self.buildEntryComponent(current[0], current[1], current[2], current[3], 'installable',False) self["list"].setList(self.list) self["list"].setIndex(idx) self["list"].updateList(self.list) self.selectionChanged() - + elif self.currList == "status": + iSoftwareTools.lastDownloadDate = time() + iSoftwareTools.list_updating = True + self.setState('update') + iSoftwareTools.getUpdates(self.getUpdateInfosCB) + def handleSelected(self): current = self["list"].getCurrent() if current: @@ -581,6 +809,8 @@ class PluginManager(Screen, DreamInfoHandler): name = x[0].strip() details = x[1].strip() description = x[2].strip() + if description == "": + description = "No description available." packagename = x[3].strip() selectState = self.getSelectionState(details) if iSoftwareTools.installed_packetlist.has_key(packagename): @@ -588,13 +818,13 @@ class PluginManager(Screen, DreamInfoHandler): status = "remove" else: status = "installed" - self.list.append(self.buildEntryComponent(name, details, description, packagename, status, selected = selectState)) + self.list.append(self.buildEntryComponent(name, _(details), _(description), packagename, status, selected = selectState)) else: if selectState == True: status = "install" else: status = "installable" - self.list.append(self.buildEntryComponent(name, details, description, packagename, status, selected = selectState)) + self.list.append(self.buildEntryComponent(name, _(details), _(description), packagename, status, selected = selectState)) if len(self.list): self.list.sort(key=lambda x: x[0]) self["list"].style = "default" @@ -662,6 +892,8 @@ class PluginManager(Screen, DreamInfoHandler): self.package = iSoftwareTools.packageDetails[0] if self.package[0].has_key("attributes"): self.attributes = self.package[0]["attributes"] + if self.attributes.has_key("needsRestart"): + self.restartRequired = True if self.attributes.has_key("package"): self.packagefiles = self.attributes["package"] if plugin[1] == 'installed': @@ -693,17 +925,21 @@ class PluginManager(Screen, DreamInfoHandler): self.close() def runExecuteFinished(self): - self.session.openWithCallback(self.ExecuteReboot, MessageBox, _("Install or remove finished.") +" "+_("Do you want to reboot your Dreambox?"), MessageBox.TYPE_YESNO) - - def ExecuteReboot(self, result): - if result is None: - return - if result is False: - self.reloadPluginlist() + self.reloadPluginlist() + if plugins.restartRequired or self.restartRequired: + self.session.openWithCallback(self.ExecuteReboot, MessageBox, _("Install or remove finished.") +" "+_("Do you want to reboot your Dreambox?"), MessageBox.TYPE_YESNO) + else: self.selectedFiles = [] + self.restartRequired = False self.detailsClosed(True) + + def ExecuteReboot(self, result): if result: quitMainloop(3) + else: + self.selectedFiles = [] + self.restartRequired = False + self.detailsClosed(True) def reloadPluginlist(self): plugins.readPluginList(resolveFilename(SCOPE_PLUGINS)) @@ -903,8 +1139,7 @@ class PluginDetails(Screen, DreamInfoHandler): self.skin_path = plugin_path self.language = language.getLanguage()[:2] # getLanguage returns e.g. "fi_FI" for "language_country" self.attributes = None - self.translatedAttributes = None - DreamInfoHandler.__init__(self, self.statusCallback, blocking = False, language = self.language) + DreamInfoHandler.__init__(self, self.statusCallback, blocking = False) self.directory = resolveFilename(SCOPE_METADIR) if packagedata: self.pluginname = packagedata[0] @@ -942,9 +1177,7 @@ class PluginDetails(Screen, DreamInfoHandler): self.package = self.packageDetails[0] if self.package[0].has_key("attributes"): self.attributes = self.package[0]["attributes"] - if self.package[0].has_key("translation"): - self.translatedAttributes = self.package[0]["translation"] - + self.restartRequired = False self.cmdList = [] self.oktext = _("\nAfter pressing OK, please wait!") self.picload = ePicLoad() @@ -953,7 +1186,7 @@ class PluginDetails(Screen, DreamInfoHandler): self.onLayoutFinish.append(self.setInfos) def setWindowTitle(self): - self.setTitle(_("Details for extension: " + self.pluginname)) + self.setTitle(_("Details for plugin: ") + self.pluginname ) def exit(self): self.close(False) @@ -968,36 +1201,31 @@ class PluginDetails(Screen, DreamInfoHandler): pass def setInfos(self): - if self.translatedAttributes.has_key("name"): - self.pluginname = self.translatedAttributes["name"] - elif self.attributes.has_key("name"): + if self.attributes.has_key("screenshot"): + self.loadThumbnail(self.attributes) + + if self.attributes.has_key("name"): self.pluginname = self.attributes["name"] else: self.pluginname = _("unknown") - if self.translatedAttributes.has_key("author"): - self.author = self.translatedAttributes["author"] - elif self.attributes.has_key("author"): + if self.attributes.has_key("author"): self.author = self.attributes["author"] else: self.author = _("unknown") - if self.translatedAttributes.has_key("description"): - self.description = self.translatedAttributes["description"] - elif self.attributes.has_key("description"): - self.description = self.attributes["description"] + if self.attributes.has_key("description"): + self.description = _(self.attributes["description"].replace("\\n", "\n")) else: self.description = _("No description available.") - if self.translatedAttributes.has_key("screenshot"): - self.loadThumbnail(self.translatedAttributes) - else: - self.loadThumbnail(self.attributes) - self["author"].setText(_("Author: ") + self.author) - self["detailtext"].setText(self.description.strip()) + self["detailtext"].setText(_(self.description)) if self.pluginstate in ('installable', 'install'): - self["key_green"].setText(_("Install")) + if iSoftwareTools.NetworkConnectionAvailable: + self["key_green"].setText(_("Install")) + else: + self["key_green"].setText("") else: self["key_green"].setText(_("Remove")) @@ -1005,10 +1233,17 @@ class PluginDetails(Screen, DreamInfoHandler): thumbnailUrl = None if entry.has_key("screenshot"): thumbnailUrl = entry["screenshot"] + if self.language == "de": + if thumbnailUrl[-7:] == "_en.jpg": + thumbnailUrl = thumbnailUrl[:-7] + "_de.jpg" + if thumbnailUrl is not None: self.thumbnail = "/tmp/" + thumbnailUrl.split('/')[-1] print "[PluginDetails] downloading screenshot " + thumbnailUrl + " to " + self.thumbnail - client.downloadPage(thumbnailUrl,self.thumbnail).addCallback(self.setThumbnail).addErrback(self.fetchFailed) + if iSoftwareTools.NetworkConnectionAvailable: + client.downloadPage(thumbnailUrl,self.thumbnail).addCallback(self.setThumbnail).addErrback(self.fetchFailed) + else: + self.setThumbnail(noScreenshot = True) else: self.setThumbnail(noScreenshot = True) @@ -1040,6 +1275,8 @@ class PluginDetails(Screen, DreamInfoHandler): def go(self): if self.attributes.has_key("package"): self.packagefiles = self.attributes["package"] + if self.attributes.has_key("needsRestart"): + self.restartRequired = True self.cmdList = [] if self.pluginstate in ('installed', 'remove'): if self.packagefiles: @@ -1048,41 +1285,35 @@ class PluginDetails(Screen, DreamInfoHandler): if len(self.cmdList): self.session.openWithCallback(self.runRemove, MessageBox, _("Do you want to remove the package:\n") + self.pluginname + "\n" + self.oktext) else: - if self.packagefiles: - for package in self.packagefiles[:]: - self.cmdList.append((IpkgComponent.CMD_INSTALL, { "package": package["name"] })) - if len(self.cmdList): - self.session.openWithCallback(self.runUpgrade, MessageBox, _("Do you want to install the package:\n") + self.pluginname + "\n" + self.oktext) + if iSoftwareTools.NetworkConnectionAvailable: + if self.packagefiles: + for package in self.packagefiles[:]: + self.cmdList.append((IpkgComponent.CMD_INSTALL, { "package": package["name"] })) + if len(self.cmdList): + self.session.openWithCallback(self.runUpgrade, MessageBox, _("Do you want to install the package:\n") + self.pluginname + "\n" + self.oktext) def runUpgrade(self, result): if result: self.session.openWithCallback(self.runUpgradeFinished, Ipkg, cmdList = self.cmdList) def runUpgradeFinished(self): - self.session.openWithCallback(self.UpgradeReboot, MessageBox, _("Installation finished.") +" "+_("Do you want to reboot your Dreambox?"), MessageBox.TYPE_YESNO) - - def UpgradeReboot(self, result): - if result is None: - return - if result is False: + self.reloadPluginlist() + if plugins.restartRequired or self.restartRequired: + self.session.openWithCallback(self.UpgradeReboot, MessageBox, _("Installation finished.") +" "+_("Do you want to reboot your Dreambox?"), MessageBox.TYPE_YESNO) + else: self.close(True) + def UpgradeReboot(self, result): if result: quitMainloop(3) + else: + self.close(True) def runRemove(self, result): if result: self.session.openWithCallback(self.runRemoveFinished, Ipkg, cmdList = self.cmdList) def runRemoveFinished(self): - self.session.openWithCallback(self.RemoveReboot, MessageBox, _("Remove finished.") +" "+_("Do you want to reboot your Dreambox?"), MessageBox.TYPE_YESNO) - - def RemoveReboot(self, result): - if result is None: - return - if result is False: - self.close(True) - if result: - quitMainloop(3) + self.close(True) def reloadPluginlist(self): plugins.readPluginList(resolveFilename(SCOPE_PLUGINS)) @@ -1094,11 +1325,11 @@ class PluginDetails(Screen, DreamInfoHandler): class UpdatePlugin(Screen): skin = """ - <screen name="UpdatePlugin" position="center,center" size="550,200" title="Software update" > + <screen name="UpdatePlugin" position="center,center" size="550,300" title="Software update" > <widget name="activityslider" position="0,0" size="550,5" /> <widget name="slider" position="0,150" size="550,30" /> <widget source="package" render="Label" position="10,30" size="540,20" font="Regular;18" halign="center" valign="center" backgroundColor="#25062748" transparent="1" /> - <widget source="status" render="Label" position="10,60" size="540,45" font="Regular;20" halign="center" valign="center" backgroundColor="#25062748" transparent="1" /> + <widget source="status" render="Label" position="10,180" size="540,100" font="Regular;20" halign="center" valign="center" backgroundColor="#25062748" transparent="1" /> </screen>""" def __init__(self, session, args = None): @@ -1110,31 +1341,48 @@ class UpdatePlugin(Screen): self["slider"] = self.slider self.activityslider = Slider(0, 100) self["activityslider"] = self.activityslider - self.status = StaticText(_("Upgrading Dreambox... Please wait")) + self.status = StaticText(_("Please wait...")) self["status"] = self.status - self.package = StaticText() + self.package = StaticText(_("Verifying your internet connection...")) self["package"] = self.package + self.oktext = _("Press OK on your remote control to continue.") self.packages = 0 self.error = 0 + self.processed_packages = [] self.activity = 0 self.activityTimer = eTimer() self.activityTimer.callback.append(self.doActivityTimer) - self.activityTimer.start(100, False) self.ipkg = IpkgComponent() self.ipkg.addCallback(self.ipkgCallback) - self.updating = True - self.package.setText(_("Package list update")) - self.ipkg.startCmd(IpkgComponent.CMD_UPDATE) + self.updating = False self["actions"] = ActionMap(["WizardActions"], { "ok": self.exit, "back": self.exit }, -1) + + iNetwork.checkNetworkState(self.checkNetworkCB) + self.onClose.append(self.cleanup) + + def cleanup(self): + iNetwork.stopPingConsole() + + def checkNetworkCB(self,data): + if data is not None: + if data <= 2: + self.updating = True + self.activityTimer.start(100, False) + self.package.setText(_("Package list update")) + self.status.setText(_("Upgrading Dreambox... Please wait")) + self.ipkg.startCmd(IpkgComponent.CMD_UPDATE) + else: + self.package.setText(_("Your network is not working. Please try again.")) + self.status.setText(self.oktext) def doActivityTimer(self): self.activity += 1 @@ -1150,20 +1398,34 @@ class UpdatePlugin(Screen): self.slider.setValue(self.sliderPackages[param]) self.package.setText(param) self.status.setText(_("Upgrading")) - self.packages += 1 + if not param in self.processed_packages: + self.processed_packages.append(param) + self.packages += 1 elif event == IpkgComponent.EVENT_INSTALL: self.package.setText(param) self.status.setText(_("Installing")) - self.packages += 1 + if not param in self.processed_packages: + self.processed_packages.append(param) + self.packages += 1 + elif event == IpkgComponent.EVENT_REMOVE: + self.package.setText(param) + self.status.setText(_("Removing")) + if not param in self.processed_packages: + self.processed_packages.append(param) + self.packages += 1 elif event == IpkgComponent.EVENT_CONFIGURING: self.package.setText(param) self.status.setText(_("Configuring")) + elif event == IpkgComponent.EVENT_MODIFIED: - self.session.openWithCallback( - self.modificationCallback, - MessageBox, - _("A configuration file (%s) was modified since Installation.\nDo you want to keep your version?") % (param) - ) + if config.plugins.softwaremanager.overwriteConfigFiles.value in ("N", "Y"): + self.ipkg.write(True and config.plugins.softwaremanager.overwriteConfigFiles.value) + else: + self.session.openWithCallback( + self.modificationCallback, + MessageBox, + _("A configuration file (%s) was modified since Installation.\nDo you want to keep your version?") % (param) + ) elif event == IpkgComponent.EVENT_ERROR: self.error += 1 elif event == IpkgComponent.EVENT_DONE: @@ -1176,8 +1438,8 @@ class UpdatePlugin(Screen): self.activityTimer.stop() self.activityslider.setValue(0) - self.package.setText("") - self.status.setText(_("Done - Installed or upgraded %d packages") % self.packages) + self.package.setText(_("Done - Installed or upgraded %d packages") % self.packages) + self.status.setText(self.oktext) else: self.activityTimer.stop() self.activityslider.setValue(0) @@ -1199,6 +1461,9 @@ class UpdatePlugin(Screen): self.session.openWithCallback(self.exitAnswer, MessageBox, _("Upgrade finished.") +" "+_("Do you want to reboot your Dreambox?")) else: self.close() + else: + if not self.updating: + self.close() def exitAnswer(self, result): if result is not None and result: @@ -1255,7 +1520,7 @@ class IPKGMenu(Screen): def fill_list(self): self.flist = [] - self.path = '/etc/ipkg/' + self.path = '/etc/opkg/' if (os_path.exists(self.path) == False): self.entry = False return @@ -1380,7 +1645,7 @@ class IPKGSource(Screen): self["text"].number(number) -class PacketManager(Screen): +class PacketManager(Screen, NumericalTextInput): skin = """ <screen name="PacketManager" position="center,center" size="530,420" title="Packet manager" > <ePixmap pixmap="skin_default/buttons/red.png" position="0,0" size="140,40" alphatest="on" /> @@ -1404,15 +1669,29 @@ class PacketManager(Screen): def __init__(self, session, plugin_path, args = None): Screen.__init__(self, session) + NumericalTextInput.__init__(self) self.session = session self.skin_path = plugin_path - self["shortcuts"] = ActionMap(["ShortcutActions", "WizardActions"], + self.setUseableChars(u'1234567890abcdefghijklmnopqrstuvwxyz') + + self["shortcuts"] = NumberActionMap(["ShortcutActions", "WizardActions", "NumberActions", "InputActions", "InputAsciiActions", "KeyboardInputActions" ], { "ok": self.go, "back": self.exit, "red": self.exit, "green": self.reload, + "gotAsciiCode": self.keyGotAscii, + "1": self.keyNumberGlobal, + "2": self.keyNumberGlobal, + "3": self.keyNumberGlobal, + "4": self.keyNumberGlobal, + "5": self.keyNumberGlobal, + "6": self.keyNumberGlobal, + "7": self.keyNumberGlobal, + "8": self.keyNumberGlobal, + "9": self.keyNumberGlobal, + "0": self.keyNumberGlobal }, -1) self.list = [] @@ -1424,11 +1703,12 @@ class PacketManager(Screen): self.list_updating = True self.packetlist = [] self.installed_packetlist = {} + self.upgradeable_packages = {} self.Console = Console() self.cmdList = [] self.cachelist = [] self.cache_ttl = 86400 #600 is default, 0 disables, Seconds cache is considered valid (24h should be ok for caching ipkgs) - self.cache_file = '/usr/lib/enigma2/python/Plugins/SystemPlugins/SoftwareManager/packetmanager.cache' #Path to cache directory + self.cache_file = eEnv.resolve('${libdir}/enigma2/python/Plugins/SystemPlugins/SoftwareManager/packetmanager.cache') #Path to cache directory self.oktext = _("\nAfter pressing OK, please wait!") self.unwanted_extensions = ('-dbg', '-dev', '-doc', 'busybox') @@ -1437,12 +1717,44 @@ class PacketManager(Screen): self.onShown.append(self.setWindowTitle) self.onLayoutFinish.append(self.rebuildList) + rcinput = eRCInput.getInstance() + rcinput.setKeyboardMode(rcinput.kmAscii) + + def keyNumberGlobal(self, val): + key = self.getKey(val) + if key is not None: + keyvalue = key.encode("utf-8") + if len(keyvalue) == 1: + self.setNextIdx(keyvalue[0]) + + def keyGotAscii(self): + keyvalue = unichr(getPrevAsciiCode()).encode("utf-8") + if len(keyvalue) == 1: + self.setNextIdx(keyvalue[0]) + + def setNextIdx(self,char): + if char in ("0", "1", "a"): + self["list"].setIndex(0) + else: + idx = self.getNextIdx(char) + if idx and idx <= self["list"].count: + self["list"].setIndex(idx) + + def getNextIdx(self,char): + idx = 0 + for i in self["list"].list: + if i[0][0] == char: + return idx + idx += 1 + def exit(self): self.ipkg.stop() if self.Console is not None: if len(self.Console.appContainers): for name in self.Console.appContainers.keys(): self.Console.kill(name) + rcinput = eRCInput.getInstance() + rcinput.setKeyboardMode(rcinput.kmNone) self.close() def reload(self): @@ -1552,7 +1864,7 @@ class PacketManager(Screen): self.list_updating = False if not self.Console: self.Console = Console() - cmd = "ipkg list" + cmd = "opkg list" self.Console.ePopen(cmd, self.IpkgList_Finished) #print event, "-", param pass @@ -1560,47 +1872,68 @@ class PacketManager(Screen): def IpkgList_Finished(self, result, retval, extra_args = None): if result: self.packetlist = [] + last_name = "" for x in result.splitlines(): - tokens = x.split(' - ') #self.blacklisted_packages + tokens = x.split(' - ') name = tokens[0].strip() if not any(name.endswith(x) for x in self.unwanted_extensions): l = len(tokens) version = l > 1 and tokens[1].strip() or "" descr = l > 2 and tokens[2].strip() or "" + if name == last_name: + continue + last_name = name self.packetlist.append([name, version, descr]) + if not self.Console: self.Console = Console() - cmd = "ipkg list_installed" + cmd = "opkg list-installed" self.Console.ePopen(cmd, self.IpkgListInstalled_Finished) def IpkgListInstalled_Finished(self, result, retval, extra_args = None): if result: self.installed_packetlist = {} for x in result.splitlines(): - tokens = x.split(' - ') #self.blacklisted_packages + tokens = x.split(' - ') name = tokens[0].strip() if not any(name.endswith(x) for x in self.unwanted_extensions): l = len(tokens) version = l > 1 and tokens[1].strip() or "" self.installed_packetlist[name] = version - self.buildPacketList() + if not self.Console: + self.Console = Console() + cmd = "opkg list-upgradable" + self.Console.ePopen(cmd, self.OpkgListUpgradeable_Finished) + def OpkgListUpgradeable_Finished(self, result, retval, extra_args = None): + if result: + self.upgradeable_packages = {} + for x in result.splitlines(): + tokens = x.split(' - ') + name = tokens[0].strip() + if not any(name.endswith(x) for x in self.unwanted_extensions): + l = len(tokens) + version = l > 2 and tokens[2].strip() or "" + self.upgradeable_packages[name] = version + self.buildPacketList() + def buildEntryComponent(self, name, version, description, state): divpng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_CURRENT_SKIN, "skin_default/div-h.png")) + if description == "": + description = "No description available." if state == 'installed': installedpng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_CURRENT_PLUGIN, "SystemPlugins/SoftwareManager/installed.png")) - return((name, version, description, state, installedpng, divpng)) + return((name, version, _(description), state, installedpng, divpng)) elif state == 'upgradeable': upgradeablepng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_CURRENT_PLUGIN, "SystemPlugins/SoftwareManager/upgradeable.png")) - return((name, version, description, state, upgradeablepng, divpng)) + return((name, version, _(description), state, upgradeablepng, divpng)) else: installablepng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_CURRENT_PLUGIN, "SystemPlugins/SoftwareManager/installable.png")) - return((name, version, description, state, installablepng, divpng)) + return((name, version, _(description), state, installablepng, divpng)) def buildPacketList(self): self.list = [] self.cachelist = [] - if self.cache_ttl > 0 and self.vc != 0: print 'Loading packagelist cache from ',self.cache_file try: @@ -1616,24 +1949,22 @@ class PacketManager(Screen): print 'rebuilding fresh package list' for x in self.packetlist: status = "" - if self.installed_packetlist.has_key(x[0].strip()): - if self.installed_packetlist[x[0].strip()] == x[1].strip(): - status = "installed" - self.list.append(self.buildEntryComponent(x[0].strip(), x[1].strip(), x[2].strip(), status)) - else: + if self.installed_packetlist.has_key(x[0]): + if self.upgradeable_packages.has_key(x[0]): status = "upgradeable" - self.list.append(self.buildEntryComponent(x[0].strip(), x[1].strip(), x[2].strip(), status)) + else: + status = "installed" else: status = "installable" - self.list.append(self.buildEntryComponent(x[0].strip(), x[1].strip(), x[2].strip(), status)) - if not any(x[0].strip().endswith(x) for x in self.unwanted_extensions): - self.cachelist.append([x[0].strip(), x[1].strip(), x[2].strip(), status]) + self.list.append(self.buildEntryComponent(x[0], x[1], x[2], status)) + self.cachelist.append([x[0], x[1], x[2], status]) write_cache(self.cache_file, self.cachelist) self['list'].setList(self.list) def reloadPluginlist(self): plugins.readPluginList(resolveFilename(SCOPE_PLUGINS)) + class IpkgInstaller(Screen): skin = """ <screen name="IpkgInstaller" position="center,center" size="550,450" title="Install extensions" > @@ -1701,18 +2032,14 @@ def startSetup(menuid): return [ ] return [(_("Software management"), UpgradeMain, "software_manager", 50)] -def autostart(reason, **kwargs): - if reason is True: - iSoftwareTools.startSoftwareTools() def Plugins(path, **kwargs): global plugin_path plugin_path = path list = [ - PluginDescriptor(where = [PluginDescriptor.WHERE_NETWORKCONFIG_READ], fnc = autostart), - PluginDescriptor(name=_("Software management"), description=_("Manage your receiver's software"), where = PluginDescriptor.WHERE_MENU, fnc=startSetup), - PluginDescriptor(name=_("Ipkg"), where = PluginDescriptor.WHERE_FILESCAN, fnc = filescan) + PluginDescriptor(name=_("Software management"), description=_("Manage your receiver's software"), where = PluginDescriptor.WHERE_MENU, needsRestart = False, fnc=startSetup), + PluginDescriptor(name=_("Ipkg"), where = PluginDescriptor.WHERE_FILESCAN, needsRestart = False, fnc = filescan) ] if config.usage.setup_level.index >= 2: # expert+ - list.append(PluginDescriptor(name=_("Software management"), description=_("Manage your receiver's software"), where = PluginDescriptor.WHERE_EXTENSIONSMENU, fnc=UpgradeMain)) + list.append(PluginDescriptor(name=_("Software management"), description=_("Manage your receiver's software"), where = PluginDescriptor.WHERE_EXTENSIONSMENU, needsRestart = False, fnc=UpgradeMain)) return list diff --git a/lib/python/Plugins/SystemPlugins/TempFanControl/LICENSE b/lib/python/Plugins/SystemPlugins/TempFanControl/LICENSE new file mode 100755 index 00000000..99700593 --- /dev/null +++ b/lib/python/Plugins/SystemPlugins/TempFanControl/LICENSE @@ -0,0 +1,12 @@ +This plugin is licensed under the Creative Commons +Attribution-NonCommercial-ShareAlike 3.0 Unported +License. To view a copy of this license, visit +http://creativecommons.org/licenses/by-nc-sa/3.0/ or send a letter to Creative +Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. + +Alternatively, this plugin may be distributed and executed on hardware which +is licensed by Dream Multimedia GmbH. + +This plugin is NOT free software. It is open source, you are allowed to +modify it (if you keep the license), but it may not be commercially +distributed other than under the conditions noted above. diff --git a/lib/python/Plugins/SystemPlugins/TempFanControl/Makefile.am b/lib/python/Plugins/SystemPlugins/TempFanControl/Makefile.am index 78ff11c3..490a7720 100644..100755 --- a/lib/python/Plugins/SystemPlugins/TempFanControl/Makefile.am +++ b/lib/python/Plugins/SystemPlugins/TempFanControl/Makefile.am @@ -1,5 +1,9 @@ -installdir = $(LIBDIR)/enigma2/python/Plugins/SystemPlugins/TempFanControl +installdir = $(pkglibdir)/python/Plugins/SystemPlugins/TempFanControl + +SUBDIRS = meta install_PYTHON = \ __init__.py \ plugin.py + +dist_install_DATA = LICENSE diff --git a/lib/python/Plugins/SystemPlugins/TempFanControl/meta/Makefile.am b/lib/python/Plugins/SystemPlugins/TempFanControl/meta/Makefile.am new file mode 100755 index 00000000..da7be245 --- /dev/null +++ b/lib/python/Plugins/SystemPlugins/TempFanControl/meta/Makefile.am @@ -0,0 +1,3 @@ +installdir = $(datadir)/meta + +dist_install_DATA = plugin_tempfancontrol.xml diff --git a/lib/python/Plugins/SystemPlugins/TempFanControl/meta/plugin_tempfancontrol.xml b/lib/python/Plugins/SystemPlugins/TempFanControl/meta/plugin_tempfancontrol.xml new file mode 100755 index 00000000..5c6565a9 --- /dev/null +++ b/lib/python/Plugins/SystemPlugins/TempFanControl/meta/plugin_tempfancontrol.xml @@ -0,0 +1,20 @@ +<default> + <prerequisites> + <hardware type="dm8000" /> + <hardware type="dm500hd" /> + <hardware type="dm800se" /> + <hardware type="dm7020hd" /> + <tag type="System" /> + </prerequisites> + <info> + <author>Dream Multimedia</author> + <name>TempFanControl</name> + <packagename>enigma2-plugin-systemplugins-tempfancontrol</packagename> + <shortdescription>Control your system fan</shortdescription> + <description>Control your internal system fan.</description> + </info> + + <files type="package"> <!-- without version, without .ipk --> + <file type="package" name="enigma2-plugin-systemplugins-tempfancontrol" /> + </files> +</default> diff --git a/lib/python/Plugins/SystemPlugins/TempFanControl/plugin.py b/lib/python/Plugins/SystemPlugins/TempFanControl/plugin.py index 38e343f9..48f871f9 100644 --- a/lib/python/Plugins/SystemPlugins/TempFanControl/plugin.py +++ b/lib/python/Plugins/SystemPlugins/TempFanControl/plugin.py @@ -12,7 +12,7 @@ from Components.FanControl import fancontrol class TempFanControl(Screen, ConfigListScreen): skin = """ - <screen position="90,100" size="570,420" title="Fan Control" > + <screen position="center,center" size="570,420" title="Fan Control" > <ePixmap pixmap="skin_default/buttons/red.png" position="0,0" size="140,40" alphatest="on" /> <ePixmap pixmap="skin_default/buttons/green.png" position="140,0" size="140,40" alphatest="on" /> <ePixmap pixmap="skin_default/buttons/yellow.png" position="280,0" size="140,40" alphatest="on" /> @@ -22,7 +22,7 @@ class TempFanControl(Screen, ConfigListScreen): <widget source="yellow" render="Label" position="280,0" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#a08500" transparent="1" /> <widget source="blue" render="Label" position="420,0" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#18188b" transparent="1" /> - <widget name="config" position="10,50" size="550,90" scrollbarMode="showOnDemand" /> + <widget name="config" position="10,50" size="550,120" scrollbarMode="showOnDemand" /> <widget source="SensorTempText0" render="Label" position="10,150" zPosition="1" size="90,40" font="Regular;20" halign="left" valign="top" backgroundColor="#9f1313" transparent="1" /> <widget source="SensorTemp0" render="Label" position="100,150" zPosition="1" size="100,20" font="Regular;19" halign="right"> @@ -90,7 +90,7 @@ class TempFanControl(Screen, ConfigListScreen): <convert type="SensorToText"></convert> </widget> </screen>""" - + def __init__(self, session, args = None): Screen.__init__(self, session) @@ -125,9 +125,13 @@ class TempFanControl(Screen, ConfigListScreen): for count in range(fancontrol.getFanCount()): self.list.append(getConfigListEntry(_("Fan %d Voltage") % (count + 1), fancontrol.getConfig(count).vlt)) self.list.append(getConfigListEntry(_("Fan %d PWM") % (count + 1), fancontrol.getConfig(count).pwm)) + self.list.append(getConfigListEntry(_("Standby Fan %d Voltage") % (count + 1), fancontrol.getConfig(count).vlt_standby)) + self.list.append(getConfigListEntry(_("Standby Fan %d PWM") % (count + 1), fancontrol.getConfig(count).pwm_standby)) + ConfigListScreen.__init__(self, self.list, session = self.session) #self["config"].list = self.list #self["config"].setList(self.list) + self["config"].l.setSeperation(300) self["actions"] = ActionMap(["OkCancelActions", "ColorActions"], { @@ -136,28 +140,31 @@ class TempFanControl(Screen, ConfigListScreen): "red": self.revert, "green": self.save }, -1) - + def save(self): for count in range(fancontrol.getFanCount()): fancontrol.getConfig(count).vlt.save() fancontrol.getConfig(count).pwm.save() + fancontrol.getConfig(count).vlt_standby.save() + fancontrol.getConfig(count).pwm_standby.save() self.close() - + def revert(self): for count in range(fancontrol.getFanCount()): fancontrol.getConfig(count).vlt.load() fancontrol.getConfig(count).pwm.load() + fancontrol.getConfig(count).vlt_standby.load() + fancontrol.getConfig(count).pwm_standby.load() self.close() - + def main(session, **kwargs): session.open(TempFanControl) def startMenu(menuid): if menuid != "system": return [] - return [(_("Temperature and Fan control"), main, "tempfancontrol", 80)] def Plugins(**kwargs): - return PluginDescriptor(name = "Temperature and Fan control", description = _("Temperature and Fan control"), where = PluginDescriptor.WHERE_MENU, fnc = startMenu) -
\ No newline at end of file + return PluginDescriptor(name = "Temperature and Fan control", description = _("Temperature and Fan control"), where = PluginDescriptor.WHERE_MENU, needsRestart = False, fnc = startMenu) + diff --git a/lib/python/Plugins/SystemPlugins/VideoEnhancement/meta/Makefile.am b/lib/python/Plugins/SystemPlugins/VideoEnhancement/meta/Makefile.am index 2e80f306..d08de4a0 100755 --- a/lib/python/Plugins/SystemPlugins/VideoEnhancement/meta/Makefile.am +++ b/lib/python/Plugins/SystemPlugins/VideoEnhancement/meta/Makefile.am @@ -1,4 +1,4 @@ -installdir = $(datadir)/meta/ +installdir = $(datadir)/meta dist_install_DATA = plugin_videoenhancement.xml diff --git a/lib/python/Plugins/SystemPlugins/VideoEnhancement/meta/plugin_videoenhancement.xml b/lib/python/Plugins/SystemPlugins/VideoEnhancement/meta/plugin_videoenhancement.xml index 208c7e0c..f52b4dab 100755 --- a/lib/python/Plugins/SystemPlugins/VideoEnhancement/meta/plugin_videoenhancement.xml +++ b/lib/python/Plugins/SystemPlugins/VideoEnhancement/meta/plugin_videoenhancement.xml @@ -3,25 +3,19 @@ <hardware type="dm8000" /> <hardware type="dm800" /> <hardware type="dm500hd" /> + <hardware type="dm800se" /> + <hardware type="dm7020hd" /> <tag type="Display" /> <tag type="System" /> </prerequisites> - <info language="en"> + <info> <author>Dream Multimedia</author> <name>VideoEnhancement</name> <packagename>enigma2-plugin-systemplugins-videoenhancement</packagename> - <shortdescription>VideoEnhancement provides advanced video enhancement settings.</shortdescription> - <description>The VideoEnhancement extension provides advanced video enhancement settings.</description> + <shortdescription>VideoEnhancement provides advanced video enhancement settings</shortdescription> + <description>The VideoEnhancement plugin provides advanced video enhancement settings.</description> <screenshot src="http://www.dreamboxupdate.com/preview/plugin_videoenhancement_en.jpg" /> </info> - <info language="de"> - <author>Dream Multimedia</author> - <name>Erweiterte A/V Einstellungen</name> - <packagename>enigma2-plugin-systemplugins-videoenhancement</packagename> - <shortdescription>Erweiterte A/V Einstellungen für Ihre Dreambox.</shortdescription> - <description>Erweiterte A/V Einstellungen für Ihre Dreambox.</description> - <screenshot src="http://www.dreamboxupdate.com/preview/plugin_videoenhancement_de.jpg" /> - </info> <files type="package"> <!-- without version, without .ipk --> <file type="package" name="enigma2-plugin-systemplugins-videoenhancement" /> diff --git a/lib/python/Plugins/SystemPlugins/VideoEnhancement/plugin.py b/lib/python/Plugins/SystemPlugins/VideoEnhancement/plugin.py index 7953d383..cde3930e 100755..100644 --- a/lib/python/Plugins/SystemPlugins/VideoEnhancement/plugin.py +++ b/lib/python/Plugins/SystemPlugins/VideoEnhancement/plugin.py @@ -394,5 +394,5 @@ def startSetup(menuid): def Plugins(**kwargs): list = [] if config.usage.setup_level.index >= 2 and os_path.exists("/proc/stb/vmpeg/0/pep_apply"): - list.append(PluginDescriptor(name=_("Videoenhancement Setup"), description=_("Advanced Video Enhancement Setup"), where = PluginDescriptor.WHERE_MENU, fnc=startSetup)) + list.append(PluginDescriptor(name=_("Videoenhancement Setup"), description=_("Advanced Video Enhancement Setup"), where = PluginDescriptor.WHERE_MENU, needsRestart = False, fnc=startSetup)) return list diff --git a/lib/python/Plugins/SystemPlugins/VideoTune/meta/Makefile.am b/lib/python/Plugins/SystemPlugins/VideoTune/meta/Makefile.am index d0368ed9..a8649058 100755 --- a/lib/python/Plugins/SystemPlugins/VideoTune/meta/Makefile.am +++ b/lib/python/Plugins/SystemPlugins/VideoTune/meta/Makefile.am @@ -1,4 +1,4 @@ -installdir = $(datadir)/meta/ +installdir = $(datadir)/meta dist_install_DATA = plugin_videotune.xml diff --git a/lib/python/Plugins/SystemPlugins/VideoTune/meta/plugin_videotune.xml b/lib/python/Plugins/SystemPlugins/VideoTune/meta/plugin_videotune.xml index c4609433..78b170ac 100755 --- a/lib/python/Plugins/SystemPlugins/VideoTune/meta/plugin_videotune.xml +++ b/lib/python/Plugins/SystemPlugins/VideoTune/meta/plugin_videotune.xml @@ -3,23 +3,14 @@ <tag type="Display" /> <tag type="System" /> </prerequisites> - <info language="en"> + <info> <author>Dream Multimedia</author> <name>VideoTune</name> <packagename>enigma2-plugin-systemplugins-videotune</packagename> - <shortdescription>VideoTune helps fine-tuning your tv display.</shortdescription> + <shortdescription>VideoTune helps fine-tuning your tv display</shortdescription> <description>The VideoTune helps fine-tuning your tv display.\nYou can control brightness and contrast of your tv.</description> <screenshot src="http://www.dreamboxupdate.com/preview/plugin_videotune.jpg" /> </info> - <info language="de"> - <author>Dream Multimedia - DE</author> - <name>VideoTune - DE </name> - <packagename>enigma2-plugin-systemplugins-videotune</packagename> - <shortdescription>VideoTune hilft beim fein-einstellen des Fernsehers.</shortdescription> - <description>VideoTune hilf beim fein-einstellen des Fernsehers.\nSie können Kontrast und Helligkeit fein-einstellen. - </description> - <screenshot src="http://www.dreamboxupdate.com/preview/plugin_videotune.jpg" /> - </info> <files type="package"> <!-- ohne versionsnummer, ohne .ipkg --> <file type="package" name="enigma2-plugin-systemplugins-videotune" /> </files> diff --git a/lib/python/Plugins/SystemPlugins/VideoTune/plugin.py b/lib/python/Plugins/SystemPlugins/VideoTune/plugin.py index 1b62206f..9e90c72e 100644 --- a/lib/python/Plugins/SystemPlugins/VideoTune/plugin.py +++ b/lib/python/Plugins/SystemPlugins/VideoTune/plugin.py @@ -34,6 +34,6 @@ def startSetup(menuid): def Plugins(**kwargs): return [ - PluginDescriptor(name=_("Video Fine-Tuning"), description=_("fine-tune your display"), where = PluginDescriptor.WHERE_MENU, fnc=startSetup), - PluginDescriptor(name=_("Video Fine-Tuning Wizard"), where = PluginDescriptor.WHERE_WIZARD, fnc=(1, videoFinetuneWizard)) + PluginDescriptor(name=_("Video Fine-Tuning"), description=_("fine-tune your display"), where = PluginDescriptor.WHERE_MENU, needsRestart = False, fnc=startSetup), + PluginDescriptor(name=_("Video Fine-Tuning Wizard"), where = PluginDescriptor.WHERE_WIZARD, needsRestart = False, fnc=(1, videoFinetuneWizard)) ] diff --git a/lib/python/Plugins/SystemPlugins/Videomode/VideoHardware.py b/lib/python/Plugins/SystemPlugins/Videomode/VideoHardware.py index e3b902f0..6ecbfd49 100644 --- a/lib/python/Plugins/SystemPlugins/Videomode/VideoHardware.py +++ b/lib/python/Plugins/SystemPlugins/Videomode/VideoHardware.py @@ -196,10 +196,12 @@ class VideoHardware: print "saveMode", port, mode, rate config.av.videoport.value = port config.av.videoport.save() - config.av.videomode[port].value = mode - config.av.videomode[port].save() - config.av.videorate[mode].value = rate - config.av.videorate[mode].save() + if port in config.av.videomode: + config.av.videomode[port].value = mode + config.av.videomode[port].save() + if mode in config.av.videorate: + config.av.videorate[mode].value = rate + config.av.videorate[mode].save() def isPortAvailable(self, port): # fixme @@ -239,9 +241,9 @@ class VideoHardware: portlist = self.getPortList() for port in portlist: descr = port - if descr == 'DVI' and hw_type == 'dm500hd': + if descr == 'DVI' and hw_type in ('dm500hd', 'dm800se', 'dm7020hd'): descr = 'HDMI' - elif descr == 'DVI-PC' and hw_type == 'dm500hd': + elif descr == 'DVI-PC' and hw_type in ('dm500hd', 'dm800se', 'dm7020hd'): descr = 'HDMI-PC' lst.append((port, descr)) diff --git a/lib/python/Plugins/SystemPlugins/Videomode/VideoWizard.py b/lib/python/Plugins/SystemPlugins/Videomode/VideoWizard.py index 15f4d516..18e81764 100644..100755 --- a/lib/python/Plugins/SystemPlugins/Videomode/VideoWizard.py +++ b/lib/python/Plugins/SystemPlugins/Videomode/VideoWizard.py @@ -12,14 +12,22 @@ from Tools.HardwareInfo import HardwareInfo config.misc.showtestcard = ConfigBoolean(default = False) class VideoWizardSummary(WizardSummary): - skin = """ - <screen position="0,0" size="132,64"> + skin = ( + """<screen name="VideoWizardSummary" position="0,0" size="132,64" id="1"> <widget name="text" position="6,4" size="120,40" font="Regular;12" transparent="1" /> <widget source="parent.list" render="Label" position="6,40" size="120,21" font="Regular;14"> <convert type="StringListSelection" /> </widget> <!--widget name="pic" pixmap="%s" position="6,22" zPosition="10" size="64,64" transparent="1" alphatest="on"/--> - </screen>""" #% (resolveFilename(SCOPE_PLUGINS, "SystemPlugins/Videomode/lcd_Scart.png")) + </screen>""", + """<screen name="VideoWizardSummary" position="0,0" size="96,64" id="2"> + <widget name="text" position="0,4" size="96,40" font="Regular;12" transparent="1" /> + <widget source="parent.list" render="Label" position="0,40" size="96,21" font="Regular;14"> + <convert type="StringListSelection" /> + </widget> + <!--widget name="pic" pixmap="%s" position="0,22" zPosition="10" size="64,64" transparent="1" alphatest="on"/--> + </screen>""") + #% (resolveFilename(SCOPE_PLUGINS, "SystemPlugins/Videomode/lcd_Scart.png")) def __init__(self, session, parent): WizardSummary.__init__(self, session, parent) @@ -82,7 +90,7 @@ class VideoWizard(WizardLanguage, Rc): for port in self.hw.getPortList(): if self.hw.isPortUsed(port): descr = port - if descr == 'DVI' and hw_type == 'dm500hd': + if descr == 'DVI' and hw_type in ('dm500hd', 'dm800se', 'dm7020hd'): descr = 'HDMI' if port != "DVI-PC": list.append((descr,port)) @@ -100,7 +108,7 @@ class VideoWizard(WizardLanguage, Rc): self.inputSelect(self.selection) if self["portpic"].instance is not None: picname = self.selection - if picname == "DVI" and HardwareInfo().get_device_name() == "dm500hd": + if picname == "DVI" and HardwareInfo().get_device_name() in ("dm500hd", "dm800se", "dm7020hd"): picname = "HDMI" self["portpic"].instance.setPixmapFromFile(resolveFilename(SCOPE_PLUGINS, "SystemPlugins/Videomode/" + picname + ".png")) diff --git a/lib/python/Plugins/SystemPlugins/Videomode/meta/Makefile.am b/lib/python/Plugins/SystemPlugins/Videomode/meta/Makefile.am index ef474435..ddcc18c9 100755 --- a/lib/python/Plugins/SystemPlugins/Videomode/meta/Makefile.am +++ b/lib/python/Plugins/SystemPlugins/Videomode/meta/Makefile.am @@ -1,4 +1,4 @@ -installdir = $(datadir)/meta/ +installdir = $(datadir)/meta dist_install_DATA = plugin_videomode.xml diff --git a/lib/python/Plugins/SystemPlugins/Videomode/meta/plugin_videomode.xml b/lib/python/Plugins/SystemPlugins/Videomode/meta/plugin_videomode.xml index fbb6e3f4..e16a7dc8 100755 --- a/lib/python/Plugins/SystemPlugins/Videomode/meta/plugin_videomode.xml +++ b/lib/python/Plugins/SystemPlugins/Videomode/meta/plugin_videomode.xml @@ -3,22 +3,14 @@ <tag type="Display" /> <tag type="System" /> </prerequisites> - <info language="en"> + <info> <author>Dream Multimedia</author> <name>Videomode</name> <packagename>enigma2-plugin-systemplugins-videomode</packagename> - <shortdescription>Videomode provides advanced video modes.</shortdescription> - <description>The Videomode extension provides advanced video modes.</description> + <shortdescription>Videomode provides advanced video mode settings</shortdescription> + <description>The Videomode plugin provides advanced video mode settings.</description> <screenshot src="http://www.dreamboxupdate.com/preview/plugin_videomode_en.jpg" /> </info> - <info language="de"> - <author>Dream Multimedia</author> - <name>Videomode</name> - <packagename>enigma2-plugin-systemplugins-videomode</packagename> - <shortdescription>Videomode bietet erweiterte Video Einstellungen.</shortdescription> - <description>Die Videomode-Erweiterung bietet erweiterte Video-Einstellungen.</description> - <screenshot src="http://www.dreamboxupdate.com/preview/plugin_videomode_de.jpg" /> - </info> <files type="package"> <!-- without version, without .ipk --> <file type="package" name="enigma2-plugin-systemplugins-videomode" /> diff --git a/lib/python/Plugins/SystemPlugins/Videomode/plugin.py b/lib/python/Plugins/SystemPlugins/Videomode/plugin.py index 39c1131a..7396534f 100755..100644 --- a/lib/python/Plugins/SystemPlugins/Videomode/plugin.py +++ b/lib/python/Plugins/SystemPlugins/Videomode/plugin.py @@ -227,8 +227,8 @@ def VideoWizard(*args, **kwargs): def Plugins(**kwargs): list = [ # PluginDescriptor(where = [PluginDescriptor.WHERE_SESSIONSTART, PluginDescriptor.WHERE_AUTOSTART], fnc = autostart), - PluginDescriptor(name=_("Video Setup"), description=_("Advanced Video Setup"), where = PluginDescriptor.WHERE_MENU, fnc=startSetup) + PluginDescriptor(name=_("Video Setup"), description=_("Advanced Video Setup"), where = PluginDescriptor.WHERE_MENU, needsRestart = False, fnc=startSetup) ] if config.misc.videowizardenabled.value: - list.append(PluginDescriptor(name=_("Video Wizard"), where = PluginDescriptor.WHERE_WIZARD, fnc=(0, VideoWizard))) + list.append(PluginDescriptor(name=_("Video Wizard"), where = PluginDescriptor.WHERE_WIZARD, needsRestart = False, fnc=(0, VideoWizard))) return list diff --git a/lib/python/Plugins/SystemPlugins/WirelessLan/Makefile.am b/lib/python/Plugins/SystemPlugins/WirelessLan/Makefile.am index 2f362379..bdc1a884 100755 --- a/lib/python/Plugins/SystemPlugins/WirelessLan/Makefile.am +++ b/lib/python/Plugins/SystemPlugins/WirelessLan/Makefile.am @@ -4,8 +4,6 @@ SUBDIRS = meta install_PYTHON = \ __init__.py \ - flags.py \ - iwlibs.py \ plugin.py \ Wlan.py diff --git a/lib/python/Plugins/SystemPlugins/WirelessLan/Wlan.py b/lib/python/Plugins/SystemPlugins/WirelessLan/Wlan.py index 1c1471ce..a1851579 100755 --- a/lib/python/Plugins/SystemPlugins/WirelessLan/Wlan.py +++ b/lib/python/Plugins/SystemPlugins/WirelessLan/Wlan.py @@ -1,39 +1,42 @@ -#from enigma import eListboxPythonMultiContent, eListbox, gFont, RT_HALIGN_LEFT, RT_HALIGN_RIGHT, RT_HALIGN_CENTER -#from Components.MultiContent import MultiContentEntryText -#from Components.GUIComponent import GUIComponent -#from Components.HTMLComponent import HTMLComponent from Components.config import config, ConfigYesNo, NoSave, ConfigSubsection, ConfigText, ConfigSelection, ConfigPassword from Components.Console import Console +from Components.Network import iNetwork -from os import system +from os import system, path as os_path from string import maketrans, strip import sys import types -from re import compile as re_compile, search as re_search -from iwlibs import getNICnames, Wireless, Iwfreq +from re import compile as re_compile, search as re_search, escape as re_escape +from pythonwifi.iwlibs import getNICnames, Wireless, Iwfreq, getWNICnames +from pythonwifi import flags as wififlags list = [] +list.append("Unencrypted") list.append("WEP") list.append("WPA") -list.append("WPA2") list.append("WPA/WPA2") +list.append("WPA2") weplist = [] weplist.append("ASCII") weplist.append("HEX") config.plugins.wlan = ConfigSubsection() -config.plugins.wlan.essid = NoSave(ConfigText(default = "home", fixed_size = False)) -config.plugins.wlan.hiddenessid = NoSave(ConfigText(default = "home", fixed_size = False)) +config.plugins.wlan.essid = NoSave(ConfigText(default = "", fixed_size = False)) +config.plugins.wlan.hiddenessid = NoSave(ConfigYesNo(default = False)) +config.plugins.wlan.encryption = NoSave(ConfigSelection(list, default = "WPA2")) +config.plugins.wlan.wepkeytype = NoSave(ConfigSelection(weplist, default = "ASCII")) +config.plugins.wlan.psk = NoSave(ConfigPassword(default = "", fixed_size = False)) -config.plugins.wlan.encryption = ConfigSubsection() -config.plugins.wlan.encryption.enabled = NoSave(ConfigYesNo(default = True)) -config.plugins.wlan.encryption.type = NoSave(ConfigSelection(list, default = "WPA/WPA2")) -config.plugins.wlan.encryption.wepkeytype = NoSave(ConfigSelection(weplist, default = "ASCII")) -config.plugins.wlan.encryption.psk = NoSave(ConfigPassword(default = "mysecurewlan", fixed_size = False)) + +def getWlanConfigName(iface): + return '/etc/wpa_supplicant.' + iface + '.conf' class Wlan: - def __init__(self, iface): + def __init__(self, iface = None): + self.iface = iface + self.oldInterfaceState = None + a = ''; b = '' for i in range(0, 255): a = a + chr(i) @@ -42,466 +45,309 @@ class Wlan: else: b = b + chr(i) - self.iface = iface - self.wlaniface = {} - self.WlanConsole = Console() self.asciitrans = maketrans(a, b) - def stopWlanConsole(self): - if self.WlanConsole is not None: - print "killing self.WlanConsole" - self.WlanConsole = None - del self.WlanConsole - - def getDataForInterface(self, callback = None): - #get ip out of ip addr, as avahi sometimes overrides it in ifconfig. - print "self.iface im getDataForInterface",self.iface - if len(self.WlanConsole.appContainers) == 0: - self.WlanConsole = Console() - cmd = "iwconfig " + self.iface - self.WlanConsole.ePopen(cmd, self.iwconfigFinished, callback) - - def iwconfigFinished(self, result, retval, extra_args): - print "self.iface im iwconfigFinished",self.iface - callback = extra_args - data = { 'essid': False, 'frequency': False, 'acesspoint': False, 'bitrate': False, 'encryption': False, 'quality': False, 'signal': False } - #print "result im iwconfigFinished",result - - for line in result.splitlines(): - #print "line",line - line = line.strip() - if "ESSID" in line: - if "off/any" in line: - ssid = _("No Connection") - else: - tmpssid=(line[line.index('ESSID')+7:len(line)-1]) - if tmpssid == '': - ssid = _("Hidden networkname") - elif tmpssid ==' ': - ssid = _("Hidden networkname") - else: - ssid = tmpssid - #print "SSID->",ssid - if ssid is not None: - data['essid'] = ssid - if 'Frequency' in line: - frequency = line[line.index('Frequency')+10 :line.index(' GHz')] - #print "Frequency",frequency - if frequency is not None: - data['frequency'] = frequency - if "Access Point" in line: - ap=line[line.index('Access Point')+14:len(line)-1] - #print "AP",ap - if ap is not None: - data['acesspoint'] = ap - if "Bit Rate" in line: - br = line[line.index('Bit Rate')+9 :line.index(' Mb/s')] - #print "Bitrate",br - if br is not None: - data['bitrate'] = br - if 'Encryption key' in line: - if ":off" in line: - enc = _("Disabled") - else: - enc = line[line.index('Encryption key')+15 :line.index(' Security')] - #print "Encryption key",enc - if enc is not None: - data['encryption'] = _("Enabled") - if 'Quality' in line: - if "/100" in line: - qual = line[line.index('Quality')+8:line.index('/100')] - else: - qual = line[line.index('Quality')+8:line.index('Sig')] - #print "Quality",qual - if qual is not None: - data['quality'] = qual - if 'Signal level' in line: - signal = line[line.index('Signal level')+14 :line.index(' dBm')] - #print "Signal level",signal - if signal is not None: - data['signal'] = signal - - self.wlaniface[self.iface] = data - - if len(self.WlanConsole.appContainers) == 0: - print "self.wlaniface after loading:", self.wlaniface - self.WlanConsole = None - if callback is not None: - callback(True,self.wlaniface) - - def getAdapterAttribute(self, attribute): - print "im getAdapterAttribute" - if self.wlaniface.has_key(self.iface): - print "self.wlaniface.has_key",self.iface - if self.wlaniface[self.iface].has_key(attribute): - return self.wlaniface[self.iface][attribute] - return None - def asciify(self, str): return str.translate(self.asciitrans) - def getWirelessInterfaces(self): - iwifaces = None - try: - iwifaces = getNICnames() - except: - print "[Wlan.py] No Wireless Networkcards could be found" - - return iwifaces + return getWNICnames() + + def setInterface(self, iface = None): + self.iface = iface + + def getInterface(self): + return self.iface - def getNetworkList(self): - system("ifconfig "+self.iface+" up") + if self.oldInterfaceState is None: + self.oldInterfaceState = iNetwork.getAdapterAttribute(self.iface, "up") + if self.oldInterfaceState is False: + if iNetwork.getAdapterAttribute(self.iface, "up") is False: + iNetwork.setAdapterAttribute(self.iface, "up", True) + system("ifconfig "+self.iface+" up") + ifobj = Wireless(self.iface) # a Wireless NIC Object - - #Association mappings - stats, quality, discard, missed_beacon = ifobj.getStatistics() - snr = quality.signallevel - quality.noiselevel try: scanresults = ifobj.scan() except: scanresults = None - print "[Wlan.py] No Wireless Networks could be found" - + print "[Wlan.py] No wireless networks could be found" + aps = {} if scanresults is not None: - aps = {} + (num_channels, frequencies) = ifobj.getChannelInfo() + index = 1 for result in scanresults: - bssid = result.bssid - - encryption = map(lambda x: hex(ord(x)), result.encode) - - if encryption[-1] == "0x8": + + if result.encode.flags & wififlags.IW_ENCODE_DISABLED > 0: + encryption = False + elif result.encode.flags & wififlags.IW_ENCODE_NOKEY > 0: encryption = True else: - encryption = False - + encryption = None + + signal = str(result.quality.siglevel-0x100) + " dBm" + quality = "%s/%s" % (result.quality.quality,ifobj.getQualityMax().quality) + extra = [] for element in result.custom: element = element.encode() extra.append( strip(self.asciify(element)) ) - - if result.quality.sl is 0 and len(extra) > 0: - begin = extra[0].find('SignalStrength=')+15 - - done = False - end = begin+1 - - while not done: - if extra[0][begin:end].isdigit(): - end += 1 - else: - done = True - end -= 1 - - signal = extra[0][begin:end] - #print "[Wlan.py] signal is:" + str(signal) + for element in extra: + if 'SignalStrength' in element: + signal = element[element.index('SignalStrength')+15:element.index(',L')] + if 'LinkQuality' in element: + quality = element[element.index('LinkQuality')+12:len(element)] - else: - signal = str(result.quality.sl) - aps[bssid] = { 'active' : True, 'bssid': result.bssid, - 'channel': result.frequency.getChannel(result.frequency.getFrequency()), + 'channel': frequencies.index(ifobj._formatFrequency(result.frequency.getFrequency())) + 1, 'encrypted': encryption, 'essid': strip(self.asciify(result.essid)), 'iface': self.iface, - 'maxrate' : result.rate[-1], - 'noise' : result.quality.getNoiselevel(), - 'quality' : str(result.quality.quality), - 'signal' : signal, + 'maxrate' : ifobj._formatBitrate(result.rate[-1][-1]), + 'noise' : '',#result.quality.nlevel-0x100, + 'quality' : str(quality), + 'signal' : str(signal), 'custom' : extra, } - print aps[bssid] - return aps + index = index + 1 + return aps - def getStatus(self): - ifobj = Wireless(self.iface) - fq = Iwfreq() - try: - self.channel = str(fq.getChannel(str(ifobj.getFrequency()[0:-3]))) - except: - self.channel = 0 - #print ifobj.getStatistics() - status = { - 'BSSID': str(ifobj.getAPaddr()), - 'ESSID': str(ifobj.getEssid()), - 'quality': str(ifobj.getStatistics()[1].quality), - 'signal': str(ifobj.getStatistics()[1].sl), - 'bitrate': str(ifobj.getBitrate()), - 'channel': str(self.channel), - #'channel': str(fq.getChannel(str(ifobj.getFrequency()[0:-3]))), - } - - for (key, item) in status.items(): - if item is "None" or item is "": - status[key] = _("N/A") - - return status + def stopGetNetworkList(self): + if self.oldInterfaceState is not None: + if self.oldInterfaceState is False: + iNetwork.setAdapterAttribute(self.iface, "up", False) + system("ifconfig "+self.iface+" down") + self.oldInterfaceState = None + self.iface = None +iWlan = Wlan() class wpaSupplicant: def __init__(self): pass - - def writeConfig(self): - - essid = config.plugins.wlan.essid.value - hiddenessid = config.plugins.wlan.hiddenessid.value - encrypted = config.plugins.wlan.encryption.enabled.value - encryption = config.plugins.wlan.encryption.type.value - wepkeytype = config.plugins.wlan.encryption.wepkeytype.value - psk = config.plugins.wlan.encryption.psk.value - fp = file('/etc/wpa_supplicant.conf', 'w') - fp.write('#WPA Supplicant Configuration by enigma2\n') - fp.write('ctrl_interface=/var/run/wpa_supplicant\n') - fp.write('eapol_version=1\n') - fp.write('fast_reauth=1\n') - if essid == 'hidden...': - fp.write('ap_scan=2\n') + def writeConfig(self, iface): + essid = config.plugins.wlan.essid.value + hiddenessid = config.plugins.wlan.hiddenessid.value + encryption = config.plugins.wlan.encryption.value + wepkeytype = config.plugins.wlan.wepkeytype.value + psk = config.plugins.wlan.psk.value + fp = file(getWlanConfigName(iface), 'w') + fp.write('#WPA Supplicant Configuration by enigma2\n') + fp.write('ctrl_interface=/var/run/wpa_supplicant\n') + fp.write('eapol_version=1\n') + fp.write('fast_reauth=1\n') + + if hiddenessid: + fp.write('ap_scan=2\n') + else: + fp.write('ap_scan=1\n') + fp.write('network={\n') + fp.write('\tssid="'+essid+'"\n') + fp.write('\tscan_ssid=0\n') + if encryption in ('WPA', 'WPA2', 'WPA/WPA2'): + fp.write('\tkey_mgmt=WPA-PSK\n') + if encryption == 'WPA': + fp.write('\tproto=WPA\n') + fp.write('\tpairwise=TKIP\n') + fp.write('\tgroup=TKIP\n') + elif encryption == 'WPA2': + fp.write('\tproto=RSN\n') + fp.write('\tpairwise=CCMP\n') + fp.write('\tgroup=CCMP\n') else: - fp.write('ap_scan=1\n') - fp.write('network={\n') - if essid == 'hidden...': - fp.write('\tssid="'+hiddenessid+'"\n') + fp.write('\tproto=WPA RSN\n') + fp.write('\tpairwise=CCMP TKIP\n') + fp.write('\tgroup=CCMP TKIP\n') + fp.write('\tpsk="'+psk+'"\n') + elif encryption == 'WEP': + fp.write('\tkey_mgmt=NONE\n') + if wepkeytype == 'ASCII': + fp.write('\twep_key0="'+psk+'"\n') else: - fp.write('\tssid="'+essid+'"\n') - fp.write('\tscan_ssid=0\n') - if encrypted: - if encryption == 'WPA' or encryption == 'WPA2' or encryption == 'WPA/WPA2' : - fp.write('\tkey_mgmt=WPA-PSK\n') - - if encryption == 'WPA': - fp.write('\tproto=WPA\n') - fp.write('\tpairwise=TKIP\n') - fp.write('\tgroup=TKIP\n') - elif encryption == 'WPA2': - fp.write('\tproto=WPA RSN\n') - fp.write('\tpairwise=CCMP TKIP\n') - fp.write('\tgroup=CCMP TKIP\n') - else: - fp.write('\tproto=WPA WPA2\n') - fp.write('\tpairwise=CCMP\n') - fp.write('\tgroup=TKIP\n') - fp.write('\tpsk="'+psk+'"\n') - - elif encryption == 'WEP': - fp.write('\tkey_mgmt=NONE\n') - if wepkeytype == 'ASCII': - fp.write('\twep_key0="'+psk+'"\n') - else: - fp.write('\twep_key0='+psk+'\n') - else: - fp.write('\tkey_mgmt=NONE\n') - fp.write('}') - fp.write('\n') - fp.close() - system("cat /etc/wpa_supplicant.conf") + fp.write('\twep_key0='+psk+'\n') + else: + fp.write('\tkey_mgmt=NONE\n') + fp.write('}') + fp.write('\n') + fp.close() + #system('cat ' + getWlanConfigName(iface)) - def loadConfig(self): + def loadConfig(self,iface): + configfile = getWlanConfigName(iface) + if not os_path.exists(configfile): + configfile = '/etc/wpa_supplicant.conf' try: #parse the wpasupplicant configfile - fp = file('/etc/wpa_supplicant.conf', 'r') + print "[Wlan.py] parsing configfile: ",configfile + fp = file(configfile, 'r') supplicant = fp.readlines() fp.close() - ap_scan = False essid = None + encryption = "Unencrypted" for s in supplicant: split = s.strip().split('=',1) if split[0] == 'ap_scan': - print "[Wlan.py] Got Hidden SSID Scan Value "+split[1] if split[1] == '2': - ap_scan = True + config.plugins.wlan.hiddenessid.value = True else: - ap_scan = False - + config.plugins.wlan.hiddenessid.value = False + elif split[0] == 'ssid': - print "[Wlan.py] Got SSID "+split[1][1:-1] essid = split[1][1:-1] - + config.plugins.wlan.essid.value = essid + elif split[0] == 'proto': - print "split[1]",split[1] - config.plugins.wlan.encryption.enabled.value = True - if split[1] == "WPA" : + if split[1] == 'WPA' : mode = 'WPA' - if split[1] == "WPA WPA2" : - mode = 'WPA/WPA2' - if split[1] == "WPA RSN" : + if split[1] == 'RSN': mode = 'WPA2' - config.plugins.wlan.encryption.type.value = mode - print "[Wlan.py] Got Encryption: "+mode - - #currently unused ! - #elif split[0] == 'key_mgmt': - # print "split[1]",split[1] - # if split[1] == "WPA-PSK" : - # config.plugins.wlan.encryption.enabled.value = True - # config.plugins.wlan.encryption.type.value = "WPA/WPA2" - # print "[Wlan.py] Got Encryption: "+ config.plugins.wlan.encryption.type.value + if split[1] in ('WPA RSN', 'WPA WPA2'): + mode = 'WPA/WPA2' + encryption = mode elif split[0] == 'wep_key0': - config.plugins.wlan.encryption.enabled.value = True - config.plugins.wlan.encryption.type.value = 'WEP' + encryption = 'WEP' if split[1].startswith('"') and split[1].endswith('"'): - config.plugins.wlan.encryption.wepkeytype.value = 'ASCII' - config.plugins.wlan.encryption.psk.value = split[1][1:-1] + config.plugins.wlan.wepkeytype.value = 'ASCII' + config.plugins.wlan.psk.value = split[1][1:-1] else: - config.plugins.wlan.encryption.wepkeytype.value = 'HEX' - config.plugins.wlan.encryption.psk.value = split[1] - print "[Wlan.py] Got Encryption: WEP - keytype is: "+config.plugins.wlan.encryption.wepkeytype.value - print "[Wlan.py] Got Encryption: WEP - key0 is: "+config.plugins.wlan.encryption.psk.value + config.plugins.wlan.wepkeytype.value = 'HEX' + config.plugins.wlan.psk.value = split[1] elif split[0] == 'psk': - config.plugins.wlan.encryption.psk.value = split[1][1:-1] - print "[Wlan.py] Got PSK: "+split[1][1:-1] + config.plugins.wlan.psk.value = split[1][1:-1] else: pass + + config.plugins.wlan.encryption.value = encryption - if ap_scan is True: - config.plugins.wlan.hiddenessid.value = essid - config.plugins.wlan.essid.value = 'hidden...' - else: - config.plugins.wlan.hiddenessid.value = essid - config.plugins.wlan.essid.value = essid wsconfig = { 'hiddenessid': config.plugins.wlan.hiddenessid.value, 'ssid': config.plugins.wlan.essid.value, - 'encryption': config.plugins.wlan.encryption.enabled.value, - 'encryption_type': config.plugins.wlan.encryption.type.value, - 'encryption_wepkeytype': config.plugins.wlan.encryption.wepkeytype.value, - 'key': config.plugins.wlan.encryption.psk.value, + 'encryption': config.plugins.wlan.encryption.value, + 'wepkeytype': config.plugins.wlan.wepkeytype.value, + 'key': config.plugins.wlan.psk.value, } for (key, item) in wsconfig.items(): if item is "None" or item is "": if key == 'hiddenessid': - wsconfig['hiddenessid'] = "home" + wsconfig['hiddenessid'] = False if key == 'ssid': - wsconfig['ssid'] = "home" + wsconfig['ssid'] = "" if key == 'encryption': - wsconfig['encryption'] = True - if key == 'encryption': - wsconfig['encryption_type'] = "WPA/WPA2" - if key == 'encryption': - wsconfig['encryption_wepkeytype'] = "ASCII" - if key == 'encryption': - wsconfig['key'] = "mysecurewlan" - + wsconfig['encryption'] = "WPA2" + if key == 'wepkeytype': + wsconfig['wepkeytype'] = "ASCII" + if key == 'key': + wsconfig['key'] = "" except: - print "[Wlan.py] Error parsing /etc/wpa_supplicant.conf" + print "[Wlan.py] Error parsing ",configfile wsconfig = { - 'hiddenessid': "home", - 'ssid': "home", - 'encryption': True, - 'encryption_type': "WPA/WPA2", - 'encryption_wepkeytype': "ASCII", - 'key': "mysecurewlan", + 'hiddenessid': False, + 'ssid': "", + 'encryption': "WPA2", + 'wepkeytype': "ASCII", + 'key': "", } - print "[Wlan.py] WS-CONFIG-->",wsconfig + #print "[Wlan.py] WS-CONFIG-->",wsconfig return wsconfig - - def restart(self, iface): - system("start-stop-daemon -K -x /usr/sbin/wpa_supplicant") - system("start-stop-daemon -S -x /usr/sbin/wpa_supplicant -- -B -i"+iface+" -c/etc/wpa_supplicant.conf") class Status: def __init__(self): self.wlaniface = {} self.backupwlaniface = {} + self.statusCallback = None self.WlanConsole = Console() def stopWlanConsole(self): if self.WlanConsole is not None: - print "killing self.WlanConsole" + print "[iStatus] killing self.WlanConsole" + self.WlanConsole.killAll() self.WlanConsole = None def getDataForInterface(self, iface, callback = None): self.WlanConsole = Console() cmd = "iwconfig " + iface - self.WlanConsole.ePopen(cmd, self.iwconfigFinished, [iface, callback]) + if callback is not None: + self.statusCallback = callback + self.WlanConsole.ePopen(cmd, self.iwconfigFinished, iface) def iwconfigFinished(self, result, retval, extra_args): - (iface, callback) = extra_args - data = { 'essid': False, 'frequency': False, 'acesspoint': False, 'bitrate': False, 'encryption': False, 'quality': False, 'signal': False } + iface = extra_args + data = { 'essid': False, 'frequency': False, 'accesspoint': False, 'bitrate': False, 'encryption': False, 'quality': False, 'signal': False } for line in result.splitlines(): line = line.strip() if "ESSID" in line: if "off/any" in line: - ssid = _("No Connection") + ssid = "off" else: - tmpssid=(line[line.index('ESSID')+7:len(line)-1]) - if tmpssid == '': - ssid = _("Hidden networkname") - elif tmpssid ==' ': - ssid = _("Hidden networkname") + if "Nickname" in line: + ssid=(line[line.index('ESSID')+7:line.index('" Nickname')]) else: - ssid = tmpssid - #print "SSID->",ssid + ssid=(line[line.index('ESSID')+7:len(line)-1]) if ssid is not None: data['essid'] = ssid - if 'Frequency' in line: + if "Frequency" in line: frequency = line[line.index('Frequency')+10 :line.index(' GHz')] - #print "Frequency",frequency if frequency is not None: data['frequency'] = frequency if "Access Point" in line: - ap=line[line.index('Access Point')+14:len(line)] - #print "AP",ap + if "Sensitivity" in line: + ap=line[line.index('Access Point')+14:line.index(' Sensitivity')] + else: + ap=line[line.index('Access Point')+14:len(line)] if ap is not None: - data['acesspoint'] = ap - if ap == "Not-Associated": - data['essid'] = _("No Connection") + data['accesspoint'] = ap if "Bit Rate" in line: if "kb" in line: br = line[line.index('Bit Rate')+9 :line.index(' kb/s')] - if br == '0': - br = _("Unsupported") - else: - br += " Mb/s" else: - br = line[line.index('Bit Rate')+9 :line.index(' Mb/s')] + " Mb/s" - #print "Bitrate",br + br = line[line.index('Bit Rate')+9 :line.index(' Mb/s')] if br is not None: data['bitrate'] = br - if 'Encryption key' in line: + if "Encryption key" in line: if ":off" in line: - if data['acesspoint'] is not "Not-Associated": - enc = _("Unsupported") - else: - enc = _("Disabled") - else: + enc = "off" + elif "Security" in line: enc = line[line.index('Encryption key')+15 :line.index(' Security')] if enc is not None: - enc = _("Enabled") - #print "Encryption key",enc + enc = "on" + else: + enc = line[line.index('Encryption key')+15 :len(line)] + if enc is not None: + enc = "on" if enc is not None: data['encryption'] = enc if 'Quality' in line: if "/100" in line: - qual = line[line.index('Quality')+8:line.index('/100')] + qual = line[line.index('Quality')+8:line.index(' Signal')] else: qual = line[line.index('Quality')+8:line.index('Sig')] - #print "Quality",qual if qual is not None: data['quality'] = qual if 'Signal level' in line: if "dBm" in line: - signal = line[line.index('Signal level')+14 :line.index(' dBm')] - signal += " dBm" + signal = line[line.index('Signal level')+13 :line.index(' dBm')] + " dBm" elif "/100" in line: - signal = line[line.index('Signal level')+13:line.index('/100 Noise')] - signal += "%" + if "Noise" in line: + signal = line[line.index('Signal level')+13:line.index(' Noise')] + else: + signal = line[line.index('Signal level')+13:len(line)] else: - signal = line[line.index('Signal level')+13:line.index(' Noise')] - signal += "%" - #print "Signal level",signal + if "Noise" in line: + signal = line[line.index('Signal level')+13:line.index(' Noise')] + else: + signal = line[line.index('Signal level')+13:len(line)] if signal is not None: data['signal'] = signal @@ -510,17 +356,16 @@ class Status: if self.WlanConsole is not None: if len(self.WlanConsole.appContainers) == 0: - print "self.wlaniface after loading:", self.wlaniface - if callback is not None: - callback(True,self.wlaniface) + print "[Wlan.py] self.wlaniface after loading:", self.wlaniface + if self.statusCallback is not None: + self.statusCallback(True,self.wlaniface) + self.statusCallback = None def getAdapterAttribute(self, iface, attribute): - print "im getAdapterAttribute" self.iface = iface if self.wlaniface.has_key(self.iface): - print "self.wlaniface.has_key",self.iface if self.wlaniface[self.iface].has_key(attribute): return self.wlaniface[self.iface][attribute] return None -iStatus = Status()
\ No newline at end of file +iStatus = Status() diff --git a/lib/python/Plugins/SystemPlugins/WirelessLan/flags.py b/lib/python/Plugins/SystemPlugins/WirelessLan/flags.py deleted file mode 100755 index 4435f845..00000000 --- a/lib/python/Plugins/SystemPlugins/WirelessLan/flags.py +++ /dev/null @@ -1,104 +0,0 @@ -# -*- coding: ISO-8859-1 -*- -# python-wifi -- a wireless library to access wireless cards via python -# Copyright (C) 2004, 2005, 2006 Róman Joost -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public License -# as published by the Free Software Foundation; either version 2.1 of -# the License, or (at your option) any later version. -# -# This library 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 -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 -# USA - -modes = ['Auto', - 'Ad-Hoc', - 'Managed', - 'Master', - 'Repeat', - 'Second', - 'Monitor'] - -IFNAMSIZE = 16 -IW_ESSID_MAX_SIZE = 16 - -KILO = 10**3 -MEGA = 10**6 -GIGA = 10**9 - -# ioctl calls for the Linux/i386 kernel -SIOCIWFIRST = 0x8B00 # FIRST ioctl identifier -SIOCGIFCONF = 0x8912 # ifconf struct -SIOCGIWNAME = 0x8B01 # get name == wireless protocol -SIOCGIWFREQ = 0x8B05 # get channel/frequency -SIOCSIWMODE = 0x8B06 # set the operation mode -SIOCGIWMODE = 0x8B07 # get operation mode -SIOCGIWSENS = 0x8B09 # get sensitivity -SIOCGIWAP = 0x8B15 # get AP MAC address -SIOCSIWSCAN = 0x8B18 # set scanning off -SIOCGIWSCAN = 0x8B19 # get scanning results -SIOCGIWRATE = 0x8B21 # get default bit rate -SIOCGIWRTS = 0x8B23 # get rts/cts threshold -SIOCGIWFRAG = 0x8B25 # get fragmention thrh -SIOCGIWTXPOW = 0x8B27 # get transmit power (dBm) -SIOCGIWRETRY = 0x8B29 # get retry limit -SIOCGIWRANGE = 0x8B0B # range -SIOCGIWSTATS = 0x8B0F # get wireless statistics -SIOCSIWESSID = 0x8B1A # set essid -SIOCGIWESSID = 0x8B1B # get essid -SIOCGIWPOWER = 0x8B2D # get power managment settings -SIOCGIWENCODE = 0x8B2B # get encryption information -SIOCIWLAST = 0x8BFF # LAST ioctl identifier - -# Power management flags -IW_POWER_ON = 0x0000 # No details ... -IW_POWER_TYPE = 0xF000 # Type of parameter -IW_POWER_PERIOD = 0x1000 # Value is a period/duration of -IW_POWER_TIMEOUT = 0x2000 # Value is a timeout -IW_POWER_MODE = 0x0F00 # Power management mode -IW_POWER_MIN = 0x0001 # Value is a minimum -IW_POWER_MAX = 0x0002 # Value is a maximum -IW_POWER_RELATIVE = 0x0004 # Value is not in seconds/ms/us - -# Retry limits -IW_RETRY_TYPE = 0xF000 # Type of parameter - -# encoding stuff -IW_ENCODE_DISABLED = 0x8000 # encoding is disabled -IW_ENCODE_NOKEY = 0x0800 # key is write only, not present - -# constants responsible for scanning -IW_SCAN_MAX_DATA = 4096 - -IW_EV_LCP_LEN = 4 -IW_EV_CHAR_LEN = IW_EV_LCP_LEN + IFNAMSIZE -IW_EV_UINT_LEN = IW_EV_LCP_LEN + 4 -IW_EV_FREQ_LEN = IW_EV_LCP_LEN + 8 -IW_EV_ADDR_LEN = IW_EV_LCP_LEN + 16 -IW_EV_POINT_LEN = IW_EV_LCP_LEN + 4 -IW_EV_PARAM_LEN = IW_EV_LCP_LEN + 8 -IW_EV_QUAL_LEN = IW_EV_LCP_LEN + 4 - -EPERM = 1 -E2BIG = 7 -EAGAIN = 11 - -IWHT_NULL = 0 -IWHT_CHAR = 2 -IWHT_UINT = 4 -IWHT_FREQ = 5 -IWHT_ADDR = 6 -IWHT_POINT = 8 -IWHT_PARAM = 9 -IWHT_QUAL = 10 - -IWEVFIRST = 0x8C00 # FIRST event identifier -IWEVQUAL = 0x8C01 # Quality statistics from scan -IWEVCUSTOM = 0x8C02 # Custom Ascii string from Driver -IWEVLAST = 0x8C0A # LAST event identifier diff --git a/lib/python/Plugins/SystemPlugins/WirelessLan/iwlibs.py b/lib/python/Plugins/SystemPlugins/WirelessLan/iwlibs.py deleted file mode 100755 index c5f9c6d2..00000000 --- a/lib/python/Plugins/SystemPlugins/WirelessLan/iwlibs.py +++ /dev/null @@ -1,1114 +0,0 @@ -# -*- coding: ISO-8859-1 -*- -# python-wifi -- a wireless library to access wireless cards via python -# Copyright (C) 2004, 2005, 2006 Róman Joost -# -# Contributions from: -# Mike Auty <m.auty@softhome.net> (Iwscanresult, Iwscan) -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public License -# as published by the Free Software Foundation; either version 2.1 of -# the License, or (at your option) any later version. -# -# This library 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 -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 -# USA - -from struct import pack as struct_pack, \ - unpack as struct_unpack, \ - calcsize as struct_calcsize - -from array import array -from math import ceil, log10 -from fcntl import ioctl -from socket import AF_INET, SOCK_DGRAM, socket -from time import sleep -from re import compile - -from flags import * - -def getNICnames(): - """ extract wireless device names of /proc/net/wireless - - returns empty list if no devices are present - - >>> getNICnames() - ['eth1', 'wifi0'] - """ - device = compile('[a-z]+[0-9]+') - ifnames = [] - - f = open('/proc/net/wireless', 'r') - data = f.readlines() - for line in data: - try: - ifnames.append(device.search(line).group()) - except AttributeError: - pass - # if we couldn't lookup the devices, try to ask the kernel - if ifnames == []: - ifnames = getConfiguredNICnames() - - return ifnames - -def getConfiguredNICnames(): - """get the *configured* ifnames by a systemcall - - >>> getConfiguredNICnames() - [] - """ - iwstruct = Iwstruct() - ifnames = [] - buff = array('c', '\0'*1024) - caddr_t, length = buff.buffer_info() - s = iwstruct.pack('iP', length, caddr_t) - try: - result = iwstruct._fcntl(SIOCGIFCONF, s) - except IOError, (i, e): - return i, e - - # get the interface names out of the buffer - for i in range(0, 1024, 32): - ifname = buff.tostring()[i:i+32] - ifname = struct_unpack('32s', ifname)[0] - ifname = ifname.split('\0', 1)[0] - if ifname: - # verify if ifnames are really wifi devices - wifi = Wireless(ifname) - result = wifi.getAPaddr() - if result[0] == 0: - ifnames.append(ifname) - - return ifnames - -def makedict(**kwargs): - return kwargs - - -class Wireless(object): - """Access to wireless interfaces""" - - def __init__(self, ifname): - self.sockfd = socket(AF_INET, SOCK_DGRAM) - self.ifname = ifname - self.iwstruct = Iwstruct() - - def getAPaddr(self): - """ returns accesspoint mac address - - >>> from iwlibs import Wireless, getNICnames - >>> ifnames = getNICnames() - >>> ifnames - ['eth1', 'wifi0'] - >>> wifi = Wireless(ifnames[0]) - >>> wifi.getAPaddr() - '00:0D:88:8E:4E:93' - - Test with non-wifi card: - >>> wifi = Wireless('eth0') - >>> wifi.getAPaddr() - (95, 'Operation not supported') - - Test with non-existant card: - >>> wifi = Wireless('eth2') - >>> wifi.getAPaddr() - (19, 'No such device') - """ - buff, s = self.iwstruct.pack_wrq(32) - i, result = self.iwstruct.iw_get_ext(self.ifname, - SIOCGIWAP, - data=s) - if i > 0: - return result - - return self.iwstruct.getMAC(result) - - def getBitrate(self): - """returns device currently set bit rate - - >>> from iwlibs import Wireless - >>> wifi = Wireless('eth1') - >>> wifi.getBitrate() - '11 Mb/s' - """ - i, result = self.iwstruct.iw_get_ext(self.ifname, - SIOCGIWRATE) - if i > 0: - return result - iwfreq = Iwfreq(result) - return iwfreq.getBitrate() - - def getBitrates(self): - """returns the number of available bitrates for the device - - >>> from iwlibs import Wireless - >>> wifi = Wireless('eth1') - >>> num, rates = wifi.getBitrates() - >>> num == len(rates) - True - """ - range = Iwrange(self.ifname) - if range.errorflag: - return (range.errorflag, range.error) - return (range.num_bitrates, range.bitrates) - - def getChannelInfo(self): - """returns the number of channels and available frequency for - the device - - >>> from iwlibs import Wireless - >>> wifi = Wireless('eth1') - >>> num, rates = wifi.getChannelInfo() - >>> num == len(rates) - True - """ - range = Iwrange(self.ifname) - if range.errorflag: - return (range.errorflag, range.error) - return (range.num_channels, range.frequencies) - - def getEssid(self): - """get essid information - - >>> from iwlibs import Wireless - >>> wifi = Wireless('eth1') - >>> wifi.getEssid() - 'romanofski' - """ - essid = "" - buff, s = self.iwstruct.pack_wrq(32) - i, result = self.iwstruct.iw_get_ext(self.ifname, - SIOCGIWESSID, - data=s) - if i > 0: - return result - str = buff.tostring() - return str.strip('\x00') - - def setEssid(self, essid): - """set essid """ - raise NotImplementedError - if len(essid) > IW_ESSID_MAX_SIZE: - return "essid to big" - buff, s = self.iwstruct.pack_test(essid, 32) - i, result = self.iwstruct.iw_get_ext(self.ifname, - SIOCSIWESSID, - data=s) - if i > 0: - return result - - def getEncryption(self): - """get encryption information which is probably a string of '*', - 'open', 'private' - - as a normal user, you will get a 'Operation not permitted' - error: - - >>> from iwlibs import Wireless - >>> wifi = Wireless('eth1') - >>> wifi.getEncryption() - (1, 'Operation not permitted') - """ - iwpoint = Iwpoint(self.ifname) - if iwpoint.errorflag: - return (iwpoint.errorflag, iwpoint.error) - return iwpoint.getEncryptionKey() - - def getFragmentation(self): - """returns fragmentation threshold - - It depends on what the driver says. If you have fragmentation - threshold turned on, you'll get an int. If it's turned of - you'll get a string: 'off'. - >>> from iwlibs import Wireless - >>> wifi = Wireless('eth1') - >>> wifi.getFragmentation() - 'off' - """ - iwparam = Iwparam(self.ifname, SIOCGIWFRAG) - if iwparam.errorflag: - return (iwparam.errorflag, iwparam.error) - return iwparam.getValue() - - def getFrequency(self): - """returns currently set frequency of the card - - >>> from iwlibs import Wireless - >>> wifi = Wireless('eth1') - >>> wifi.getFrequency() - '2.417GHz' - """ - i, r = self.iwstruct.iw_get_ext(self.ifname, - SIOCGIWFREQ) - if i > 0: - return (i, r) - iwfreq = Iwfreq(r) - return iwfreq.getFrequency() - - - def getMode(self): - """returns currently set operation mode - - >>> from iwlibs import Wireless - >>> wifi = Wireless('eth1') - >>> wifi.getMode() - 'Managed' - """ - i, result = self.iwstruct.iw_get_ext(self.ifname, - SIOCGIWMODE) - if i > 0: - return result - mode = self.iwstruct.unpack('i', result[:4])[0] - return modes[mode] - - def setMode(self, mode): - """sets the operation mode """ - try: - this_modes = [x.lower() for x in modes] - mode = mode.lower() - wifimode = this_modes.index(mode) - except ValueError: - return "Invalid operation mode!" - - s = self.iwstruct.pack('I', wifimode) - i, result = self.iwstruct.iw_get_ext(self.ifname, - SIOCSIWMODE, - data=s) - if i > 0: - return result - - def getWirelessName(self): - """ returns wireless name - - >>> from iwlibs import Wireless - >>> wifi = Wireless('eth1') - >>> wifi.getWirelessName() - 'IEEE 802.11-DS' - """ - i, result = self.iwstruct.iw_get_ext(self.ifname, - SIOCGIWNAME) - if i > 0: - return result - return result.split('\0')[0] - - def getPowermanagement(self): - """returns power management settings - - >>> from iwlibs import Wireless - >>> wifi = Wireless('eth1') - >>> wifi.getPowermanagement() - 'off' - """ - iwparam = Iwparam(self.ifname, SIOCGIWPOWER) - if iwparam.errorflag: - return (iwparam.errorflag, iwparam.error) - return iwparam.getValue() - - - def getRetrylimit(self): - """returns limit retry/lifetime - - man iwconfig: - Most cards have MAC retransmissions, and some allow to set - the behaviour of the retry mechanism. - - >>> from iwlibs import Wireless - >>> wifi = Wireless('eth1') - >>> wifi.getRetrylimit() - 16 - """ - iwparam = Iwparam(self.ifname, SIOCGIWRETRY) - if iwparam.errorflag: - return (iwparam.errorflag, iwparam.error) - return iwparam.getValue() - - def getRTS(self): - """returns rts threshold - - returns int, 'auto', 'fixed', 'off' - - man iwconfig: - RTS/CTS adds a handshake before each packet transmission to - make sure that the channel is clear. This adds overhead, but - increases performance in case of hidden nodes or a large - number of active nodes. This parameter sets the size of the - smallest packet for which the node sends RTS; a value equal - to the maximum packet size disable the mechanism. - - >>> from iwlibs import Wireless - >>> wifi = Wireless('eth1') - >>> wifi.getRTS() - 'off' - """ - iwparam = Iwparam(self.ifname, SIOCGIWRTS) - if iwparam.errorflag: - return (iwparam.errorflag, iwparam.error) - return iwparam.getValue() - - def getSensitivity(self): - """returns sensitivity information - - man iwconfig: - This is the lowest signal level for which the hardware - attempt packet reception, signals weaker than this are - ignored. This is used to avoid receiving background noise, - so you should set it according to the average noise - level. Positive values are assumed to be the raw value used - by the hardware or a percentage, negative values are - assumed to be dBm. - - >>> from iwlibs import Wireless - >>> wifi = Wireless('eth1') - >>> wifi.getSensitivity() - 'off' - - """ - iwparam = Iwparam(self.ifname, SIOCGIWSENS) - if iwparam.errorflag: - return (iwparam.errorflag, iwparam.error) - return iwparam.getValue() - - def getTXPower(self): - """returns transmit power in dBm - - >>> from iwlibs import Wireless - >>> wifi = Wireless('eth1') - >>> wifi.getTXPower() - '17 dBm' - """ - i, r = self.iwstruct.iw_get_ext(self.ifname, - SIOCGIWTXPOW) - if i > 0: - return (i, r) - iwfreq = Iwfreq(r) - return iwfreq.getTransmitPower() - - def getStatistics(self): - """returns statistics information which can also be found in - /proc/net/wireless - """ - iwstats = Iwstats(self.ifname) - if iwstats.errorflag > 0: - return (iwstats.errorflag, iwstats.error) - return [iwstats.status, iwstats.qual, iwstats.discard, - iwstats.missed_beacon] - - def scan(self): - """returns Iwscanresult objects, after a successful scan""" - iwscan = Iwscan(self.ifname) - return iwscan.scan() - - -class Iwstruct(object): - """basic class to handle iwstruct data """ - - def __init__(self): - self.idx = 0 - self.sockfd = socket(AF_INET, SOCK_DGRAM) - - def parse_data(self, fmt, data): - """ unpacks raw C data """ - size = struct_calcsize(fmt) - idx = self.idx - - str = data[idx:idx + size] - self.idx = idx+size - value = struct_unpack(fmt, str) - - # take care of a tuple like (int, ) - if len(value) == 1: - return value[0] - else: - return value - - def pack(self, fmt, *args): - """ calls struct_pack and returns the result """ - return struct_pack(fmt, *args) - - def pack_wrq(self, buffsize): - """ packs wireless request data for sending it to the kernel """ - # Prepare a buffer - # We need the address of our buffer and the size for it. The - # ioctl itself looks for the pointer to the address in our - # memory and the size of it. - # Dont change the order how the structure is packed!!! - buff = array('c', '\0'*buffsize) - caddr_t, length = buff.buffer_info() - s = struct_pack('Pi', caddr_t, length) - return buff, s - - def pack_test(self, string, buffsize): - """ packs wireless request data for sending it to the kernel """ - buffsize = buffsize - len(string) - buff = array('c', string+'\0'*buffsize) - caddr_t, length = buff.buffer_info() - s = struct_pack('Pii', caddr_t, length, 1) - return buff, s - - def unpack(self, fmt, packed_data): - """ unpacks data with given format """ - return struct_unpack(fmt, packed_data) - - def _fcntl(self, request, args): - return ioctl(self.sockfd.fileno(), request, args) - - def iw_get_ext(self, ifname, request, data=None): - """ read information from ifname """ - # put some additional data behind the interface name - if data is not None: - buff = IFNAMSIZE-len(ifname) - ifreq = ifname + '\0'*buff - ifreq = ifreq + data - else: - ifreq = (ifname + '\0'*32) - - try: - result = self._fcntl(request, ifreq) - except IOError, (i, e): - return i, e - - return (0, result[16:]) - - def getMAC(self, packed_data): - """ extracts mac addr from packed data and returns it as str """ - mac_addr = struct_unpack('xxBBBBBB', packed_data[:8]) - return "%02X:%02X:%02X:%02X:%02X:%02X" % mac_addr - -class Iwparam(object): - """class to hold iwparam data """ - - def __init__(self, ifname, ioctl): - # (i) value, (b) fixed, (b) disabled, (b) flags - self.fmt = "ibbH" - self.value = 0 - self.fixed = 0 - self.disabled = 0 - self.flags = 0 - self.errorflag = 0 - self.error = "" - self.ioctl = ioctl - self.ifname = ifname - self.update() - - def getValue(self): - """returns the value if not disabled """ - - if self.disabled: - return 'off' - if self.flags & IW_RETRY_TYPE == 0: - return self.getRLAttributes() - else: - return self.getPMAttributes() - - def getRLAttributes(self): - """returns a string with attributes determined by self.flags - """ - return self.value - - def getPMAttributes(self): - """returns a string with attributes determined by self.flags - and IW_POWER* - """ - result = "" - - # Modifiers - if self.flags & IW_POWER_MIN == 0: - result = " min" - if self.flags & IW_POWER_MAX == 0: - result = " max" - - # Type - if self.flags & IW_POWER_TIMEOUT == 0: - result = " period:" - else: - result = " timeout:" - # Value with or without units - # IW_POWER_RELATIVE - value is *not* in s/ms/us - if self.flags & IW_POWER_RELATIVE: - result += "%f" %(float(self.value)/MEGA) - else: - if self.value >= MEGA: - result += "%fs" %(float(self.value)/MEGA) - elif self.value >= KILO: - result += "%fms" %(float(self.value)/KILO) - else: - result += "%dus" % self.value - - return result - - def update(self): - iwstruct = Iwstruct() - i, r = iwstruct.iw_get_ext(self.ifname, - self.ioctl) - if i > 0: - self.errorflag = i - self.error = r - self._parse(r) - - def _parse(self, data): - """ unpacks iwparam data """ - iwstruct = Iwstruct() - self.value, self.fixed, self.disabled, self.flags =\ - iwstruct.parse_data(self.fmt, data) - -class Iwfreq(object): - """ class to hold iwfreq data - delegates to Iwstruct class - """ - - def __init__(self, data=None): - self.fmt = "ihbb" - if data is not None: - self.frequency = self.parse(data) - else: - self.frequency = 0 - self.iwstruct = Iwstruct() - - def __getattr__(self, attr): - return getattr(self.iwstruct, attr) - - def parse(self, data): - """ unpacks iwparam""" - - size = struct_calcsize(self.fmt) - m, e, i, pad = struct_unpack(self.fmt, data[:size]) - # XXX well, its not *the* frequency - we need a better name - if e == 0: - return m - else: - return float(m)*10**e - - def getFrequency(self): - """returns Frequency (str) - - data - binary data returned by systemcall (iw_get_ext()) - """ - freq = self.frequency - - if freq >= GIGA: - return "%0.3fGHz" %(freq/GIGA) - - if freq >= MEGA: - return "%0.3fMHZ" %(freq/MEGA) - - if freq >= KILO: - return "%0.3fKHz" %(freq/KILO) - - def getBitrate(self): - """ returns Bitrate in Mbit - - data - binary data returned by systemcall (iw_get_ext()) - """ - bitrate = self.frequency - - if bitrate >= GIGA: - return "%i Gb/s" %(bitrate/GIGA) - - if bitrate >= MEGA: - return "%i Mb/s" %(bitrate/MEGA) - - if bitrate >= KILO: - return "%i Kb/s" %(bitrate/KILO) - - def getTransmitPower(self): - """ returns transmit power in dbm """ - # XXX something flaky is going on with m and e - # eg. m = 50 and e should than be 0, because the number is stored in - # m and don't needs to be recalculated - return "%i dBm" %self.mw2dbm(self.frequency/10) - - def getChannel(self, freq): - """returns channel information given by frequency - - returns None if frequency can't be converted - freq = frequency to convert (int) - iwrange = Iwrange object - """ - - try: - freq = float(freq) - except: - return None - - lut = {} - #13 Channels beginning at 2.412GHz and inreasing by 0,005 GHz steps - for i in range(0,12): - cur = float( 2.412 + ( i * 0.005 ) ) - lut[str(cur)] = i+1 - # Channel 14 need special actions ;) - lut['2.484'] = 14 - - - if str(freq) in lut.keys(): - return lut[str(freq)] - - return None - - - def mw2dbm(self, mwatt): - """ converts mw to dbm(float) """ - return ceil(10.0 * log10(mwatt)) - - def _setFrequency(self, list): - """sets self.frequency by given list - - currently only used by Iwrange - """ - assert len(list) == 4 - m, e, i, pad = list - if e == 0: - self.frequency = m - else: - self.frequency = m #float(m)*10**e - -class Iwstats(object): - """ class to hold iwstat data """ - - def __init__(self, ifname): - # (2B) status, 4B iw_quality, 6i iw_discarded - self.fmt = "2B4B6i" - self.status = 0 - self.qual = Iwquality() - self.discard = {} - self.missed_beacon = 0 - self.ifname = ifname - self.errorflag = 0 - self.error = "" - self.update() - - def update(self): - iwstruct = Iwstruct() - buff, s = iwstruct.pack_wrq(32) - i, result = iwstruct.iw_get_ext(self.ifname, - SIOCGIWSTATS, - data=s) - if i > 0: - self.error = result - self.errorflag = i - self._parse(buff.tostring()) - - def _parse(self, data): - """ unpacks iwstruct data """ - struct = Iwstruct() - iwqual = Iwquality() - iwstats_data = struct.parse_data(self.fmt, data) - - self.status = iwstats_data[0:2] - self.qual.quality, self.qual.sl, self.qual.nl,\ - self.qual.flags = iwstats_data[2:6] - nwid, code, frag, retries, flags = iwstats_data[6:11] - self.missed_beacon = iwstats_data[11:12][0] - self.discard = makedict(nwid=nwid, code=code, - fragment=frag, retries=retries, misc=flags) - -class Iwquality(object): - """ class to hold iwquality data """ - - def __init__(self): - self.quality = 0 - self.sl = 0 - self.nl = 0 - self.updated = 0 - self.fmt = "4B" - - def parse(self, data): - """ unpacks iwquality data """ - struct = Iwstruct() - qual, sl, nl, flags = struct.parse_data(self.fmt, data) - - # compute signal and noise level - self.signal_level = sl - self.noise_level = nl - - # asign the other values - self.quality = qual - self.updated = flags - - def setValues(self, list): - """ assigns values given by a list to our attributes """ - attributes = ["quality", "signallevel", "noise_level", - "updated"] - assert len(list) == 4 - - for i in range(len(list)): - setattr(self, attributes[i], list[i]) - - def getSignallevel(self): - """ returns signal level """ - return self.sl-0x100 - - def setSignallevel(self, sl): - """ sets signal level """ - self.sl = sl - signallevel = property(getSignallevel, setSignallevel) - - def getNoiselevel(self): - """ returns noise level """ - return self.nl - 0x100 - - def setNoiselevel(self): - raise NotImplementedError - self.nl = nl - noiselevel = property(getNoiselevel, setNoiselevel) - -class Iwpoint(object): - """ class to hold iwpoint data """ - - def __init__(self, ifname): - self.key = [0,0,0,0] - self.fields = 0 - self.flags = 0 - # (4B) pointer to data, H length, H flags - self.fmt = "4BHH" - self.errorflag = 0 - self.error = "" - self.ifname = ifname - self.update() - - def __getattr__(self, attr): - return getattr(self.iwstruct, attr) - - def update(self): - iwstruct = Iwstruct() - buff, s = iwstruct.pack_wrq(32) - i, result = iwstruct.iw_get_ext(self.ifname, - SIOCGIWENCODE, - data=s) - if i > 0: - self.errorflag = i - self.error = result - self._parse(result) - - def getEncryptionKey(self): - """ returns encryption key as '**' or 'off' as str """ - if self.flags & IW_ENCODE_DISABLED != 0: - return 'off' - elif self.flags & IW_ENCODE_NOKEY != 0: - # a key is set, so print it - return '**' * self.fields - - def _parse(self, data): - """ unpacks iwpoint data - """ - iwstruct = Iwstruct() - ptr, ptr, ptr, ptr, self.fields, self.flags =\ - iwstruct.parse_data(self.fmt, data) - self.key = [ptr, ptr, ptr, ptr] - -class Iwrange(object): - """holds iwrange struct """ - IW_MAX_FREQUENCIES = 32 - - def __init__(self, ifname): - self.fmt = "iiihb6ii4B4Bi32i2i2i2i2i3h8h2b2bhi8i2b3h2i2ihB17x"\ - + self.IW_MAX_FREQUENCIES*"ihbb" - - self.ifname = ifname - self.errorflag = 0 - self.error = "" - - # informative stuff - self.throughput = 0 - - # nwid (or domain id) - self.min_nwid = self.max_nwid = 0 - - # frequency for backward compatibility - self.old_num_channels = self.old_num_frequency = self.old_freq = 0 - - # signal level threshold - self.sensitivity = 0 - - # link quality - self.max_qual = Iwquality() - self.avg_qual = Iwquality() - - # rates - self.num_bitrates = 0 - self.bitrates = [] - - # rts threshold - self.min_rts = self.max_rts = 0 - - # fragmention threshold - self.min_frag = self.max_frag = 0 - - # power managment - self.min_pmp = self.max_pmp = 0 - self.min_pmt = self.max_pmt = 0 - self.pmp_flags = self.pmt_flags = self.pm_capa = 0 - - # encoder stuff - self.encoding_size = 0 - self.num_encoding_sizes = self.max_encoding_tokens = 0 - self.encoding_login_index = 0 - - # transmit power - self.txpower_capa = self.num_txpower = self.txpower = 0 - - # wireless extension version info - self.we_vers_compiled = self.we_vers_src = 0 - - # retry limits and lifetime - self.retry_capa = self.retry_flags = self.r_time_flags = 0 - self.min_retry = self.max_retry = 0 - self.min_r_time = self.max_r_time = 0 - - # frequency - self.num_channels = self.num_frequency = 0 - self.frequencies = [] - self.update() - - def update(self): - """updates Iwrange object by a system call to the kernel - and updates internal attributes - """ - iwstruct = Iwstruct() - buff, s = iwstruct.pack_wrq(640) - i, result = iwstruct.iw_get_ext(self.ifname, - SIOCGIWRANGE, - data=s) - if i > 0: - self.errorflag = i - self.error = result - data = buff.tostring() - self._parse(data) - - def _parse(self, data): - struct = Iwstruct() - result = struct.parse_data(self.fmt, data) - - # XXX there is maybe a much more elegant way to do this - self.throughput, self.min_nwid, self.max_nwid = result[0:3] - self.old_num_channels, self.old_num_frequency = result[3:5] - self.old_freq = result[5:11] - self.sensitivity = result[11] - self.max_qual.setValues(result[12:16]) - self.avg_qual.setValues(result[16:20]) - self.num_bitrates = result[20] # <- XXX - raw_bitrates = result[21:53] - for rate in raw_bitrates: - iwfreq = Iwfreq() - iwfreq.frequency = rate - br = iwfreq.getBitrate() - if br is not None: - self.bitrates.append(br) - - self.min_rts, self.max_rts = result[53:55] - self.min_frag, self.max_frag = result[55:57] - self.min_pmp, self.max_pmp = result[57:59] - self.min_pmt, self.max_pmt = result[59:61] - self.pmp_flags, self.pmt_flags, self.pm_capa = result[61:64] - self.encoding_size = result[64:72] - self.num_encoding_sizes, self.max_encoding_tokens = result[72:74] - self.encoding_login_index = result[74:76] - self.txpower_capa, self.num_txpower = result[76:78] - self.txpower = result[78:86] - self.we_vers_compiled, self.we_vers_src = result[86:88] - self.retry_capa, self.retry_flags, self.r_time_flags = result[88:91] - self.min_retry, self.max_retry = result[91:93] - self.min_r_time, self.max_r_time = result[93:95] - self.num_channels = result[95] - self.num_frequency = result[96] - freq = result[97:] - - i = self.num_frequency - for x in range(0, len(freq), 4): - iwfreq = Iwfreq() - iwfreq._setFrequency(freq[x:x+4]) - fq = iwfreq.getFrequency() - if fq is not None: - self.frequencies.append(fq) - i -= 1 - if i <= 0: - break - -class Iwscan(object): - """class to handle AP scanning""" - - def __init__(self, ifname): - self.ifname = ifname - self.range = Iwrange(ifname) - self.errorflag = 0 - self.error = "" - self.stream = None - self.aplist = None - - def scan(self, fullscan=True): - """Completes a scan for available access points, - and returns them in Iwscanresult format - - fullscan: If False, data is read from a cache of the last scan - If True, a scan is conducted, and then the data is read - """ - # By default everything is fine, do not wait - result = 1 - if fullscan: - self.setScan() - if self.errorflag > EPERM: - raise RuntimeError, 'setScan failure ' + str(self.errorflag) + " " + str(self.error) - return None - elif self.errorflag < EPERM: - # Permission was NOT denied, therefore we must WAIT to get results - result = 250 - - while (result > 0): - sleep(result/1000) - result = self.getScan() - - if result < 0 or self.errorflag != 0: - raise RuntimeError, 'getScan failure ' + str(self.errorflag) + " " + str(self.error) - - return self.aplist - - - def setScan(self): - """Triggers the scan, if we have permission - """ - iwstruct = Iwstruct() - s = iwstruct.pack('Pii', 0, 0, 0) - i, result = iwstruct.iw_get_ext(self.ifname, - SIOCSIWSCAN,s) - if i > 0: - self.errorflag = i - self.error = result - return result - - def getScan(self): - """Retreives results, stored from the most recent scan - Returns 0 if successful, a delay if the data isn't ready yet - or -1 if something really nasty happened - """ - iwstruct = Iwstruct() - i = E2BIG - bufflen = IW_SCAN_MAX_DATA - - # Keep resizing the buffer until it's large enough to hold the scan - while (i == E2BIG): - buff, s = iwstruct.pack_wrq(bufflen) - i, result = iwstruct.iw_get_ext(self.ifname, - SIOCGIWSCAN, - data=s) - if i == E2BIG: - pbuff, newlen = iwstruct.unpack('Pi', s) - if bufflen < newlen: - bufflen = newlen - else: - bufflen = bufflen * 2 - - if i == EAGAIN: - return 100 - if i > 0: - self.errorflag = i - self.error = result - return -1 - - pbuff, reslen = iwstruct.unpack('Pi', s) - if reslen > 0: - # Initialize the stream, and turn it into an enumerator - self.aplist = self._parse(buff.tostring()) - return 0 - - def _parse(self, data): - """Parse the event stream, and return a list of Iwscanresult objects - """ - iwstruct = Iwstruct() - scanresult = None - aplist = [] - - # Run through the stream, until broken - while 1: - # If we're the stream doesn't have enough space left for a header, break - if len(data) < IW_EV_LCP_LEN: - break; - - # Unpack the header - length, cmd = iwstruct.unpack('HH', data[:4]) - # If the header says the following data is shorter than the header, then break - if length < IW_EV_LCP_LEN: - break; - - # Put the events into their respective result data - if cmd == SIOCGIWAP: - if scanresult is not None: - aplist.append(scanresult) - scanresult = Iwscanresult(data[IW_EV_LCP_LEN:length], self.range) - elif scanresult is None: - raise RuntimeError, 'Attempting to add an event without AP data' - else: - scanresult.addEvent(cmd, data[IW_EV_LCP_LEN:length]) - - # We're finished with the preveious event - data = data[length:] - - # Don't forgset the final result - if scanresult.bssid != "00:00:00:00:00:00": - aplist.append(scanresult) - else: - raise RuntimeError, 'Attempting to add an AP without a bssid' - return aplist - -class Iwscanresult(object): - """An object to contain all the events associated with a single scanned AP - """ - - def __init__(self, data, range): - """Initialize the scan result with the access point data""" - self.iwstruct = Iwstruct() - self.range = range - self.bssid = "%02X:%02X:%02X:%02X:%02X:%02X" % struct_unpack('BBBBBB', data[2:8]) - self.essid = None - self.mode = None - self.rate = [] - self.quality = Iwquality() - self.frequency = None - self.encode = None - self.custom = [] - self.protocol = None - - def addEvent(self, cmd, data): - """Attempts to add the data from an event to a scanresult - Only certain data is accept, in which case the result is True - If the event data is invalid, None is returned - If the data is valid but unused, False is returned - """ - if cmd <= SIOCIWLAST: - if cmd < SIOCIWFIRST: - return None - elif cmd >= IWEVFIRST: - if cmd > IWEVLAST: - return None - else: - return None - - if cmd == SIOCGIWESSID: - self.essid = data[4:] - elif cmd == SIOCGIWMODE: - self.mode = modes[self.iwstruct.unpack('i', data[:4])[0]] - elif cmd == SIOCGIWRATE: - # TODO, deal with multiple rates, or at least the highest rate - freqsize = struct_calcsize("ihbb") - while len(data) >= freqsize: - iwfreq = Iwfreq(data) - self.rate.append(iwfreq.getBitrate()) - data = data[freqsize:] - elif cmd == IWEVQUAL: - self.quality.parse(data) - elif cmd == SIOCGIWFREQ: - self.frequency = Iwfreq(data) - elif cmd == SIOCGIWENCODE: - self.encode = data - elif cmd == IWEVCUSTOM: - self.custom.append(data[1:]) - elif cmd == SIOCGIWNAME: - self.protocol = data[:len(data)-2] - else: - #print "Cmd:", cmd - return False - return True
\ No newline at end of file diff --git a/lib/python/Plugins/SystemPlugins/WirelessLan/meta/Makefile.am b/lib/python/Plugins/SystemPlugins/WirelessLan/meta/Makefile.am index 6bc4aab0..692d91aa 100755 --- a/lib/python/Plugins/SystemPlugins/WirelessLan/meta/Makefile.am +++ b/lib/python/Plugins/SystemPlugins/WirelessLan/meta/Makefile.am @@ -1,4 +1,4 @@ -installdir = $(datadir)/meta/ +installdir = $(datadir)/meta dist_install_DATA = plugin_wirelesslan.xml diff --git a/lib/python/Plugins/SystemPlugins/WirelessLan/meta/plugin_wirelesslan.xml b/lib/python/Plugins/SystemPlugins/WirelessLan/meta/plugin_wirelesslan.xml index 1f882b32..eca6c0f9 100755 --- a/lib/python/Plugins/SystemPlugins/WirelessLan/meta/plugin_wirelesslan.xml +++ b/lib/python/Plugins/SystemPlugins/WirelessLan/meta/plugin_wirelesslan.xml @@ -3,22 +3,14 @@ <tag type="Network" /> <tag type="System" /> </prerequisites> - <info language="en"> + <info> <author>Dream Multimedia</author> <name>WirelessLan</name> <packagename>enigma2-plugin-systemplugins-wirelesslan</packagename> <shortdescription>Configure your WLAN network interface</shortdescription> - <description>The WirelessLan extensions helps you configuring your WLAN network interface.</description> + <description>The WirelessLan plugin helps you configuring your WLAN network interface.</description> <screenshot src="http://www.dreamboxupdate.com/preview/plugin_wirelesslan_en.jpg" /> </info> - <info language="de"> - <author>Dream Multimedia</author> - <name>WirelessLan</name> - <packagename>enigma2-plugin-systemplugins-wirelesslan</packagename> - <shortdescription>Konfigurieren Sie Ihr WLAN Netzwerk.</shortdescription> - <description>Die WirelessLan Erweiterung hilft Ihnen beim konfigurieren Ihres WLAN Netzwerkes..</description> - <screenshot src="http://www.dreamboxupdate.com/preview/plugin_wirelesslan_de.jpg" /> - </info> <files type="package"> <!-- without version, without .ipk --> <file type="package" name="enigma2-plugin-systemplugins-wirelesslan" /> diff --git a/lib/python/Plugins/SystemPlugins/WirelessLan/plugin.py b/lib/python/Plugins/SystemPlugins/WirelessLan/plugin.py index a9f7bf43..10213eb0 100755..100644 --- a/lib/python/Plugins/SystemPlugins/WirelessLan/plugin.py +++ b/lib/python/Plugins/SystemPlugins/WirelessLan/plugin.py @@ -1,4 +1,4 @@ -from enigma import eTimer +from enigma import eTimer, eTPM, eEnv from Screens.Screen import Screen from Components.ActionMap import ActionMap, NumberActionMap from Components.Pixmap import Pixmap,MultiPixmap @@ -8,35 +8,40 @@ from Components.Sources.List import List from Components.MenuList import MenuList from Components.config import config, getConfigListEntry, ConfigYesNo, NoSave, ConfigSubsection, ConfigText, ConfigSelection, ConfigPassword from Components.ConfigList import ConfigListScreen -from Components.Network import Network, iNetwork +from Components.Network import iNetwork from Components.Console import Console from Plugins.Plugin import PluginDescriptor from os import system, path as os_path, listdir from Tools.Directories import resolveFilename, SCOPE_PLUGINS, SCOPE_SKIN_IMAGE from Tools.LoadPixmap import LoadPixmap -from Wlan import Wlan, wpaSupplicant, iStatus +from Tools.HardwareInfo import HardwareInfo +from Wlan import iWlan, wpaSupplicant, iStatus, getWlanConfigName +import hashlib +from time import time +from os import urandom, system +from re import escape as re_escape + +plugin_path = eEnv.resolve("${libdir}/enigma2/python/Plugins/SystemPlugins/WirelessLan") -plugin_path = "/usr/lib/enigma2/python/Plugins/SystemPlugins/WirelessLan" list = [] +list.append("Unencrypted") list.append("WEP") list.append("WPA") -list.append("WPA2") list.append("WPA/WPA2") +list.append("WPA2") weplist = [] weplist.append("ASCII") weplist.append("HEX") config.plugins.wlan = ConfigSubsection() -config.plugins.wlan.essid = NoSave(ConfigText(default = "home", fixed_size = False)) -config.plugins.wlan.hiddenessid = NoSave(ConfigText(default = "home", fixed_size = False)) +config.plugins.wlan.essid = NoSave(ConfigText(default = "", fixed_size = False)) +config.plugins.wlan.hiddenessid = NoSave(ConfigYesNo(default = False)) +config.plugins.wlan.encryption = NoSave(ConfigSelection(list, default = "WPA2")) +config.plugins.wlan.wepkeytype = NoSave(ConfigSelection(weplist, default = "ASCII")) +config.plugins.wlan.psk = NoSave(ConfigPassword(default = "", fixed_size = False)) -config.plugins.wlan.encryption = ConfigSubsection() -config.plugins.wlan.encryption.enabled = NoSave(ConfigYesNo(default = False)) -config.plugins.wlan.encryption.type = NoSave(ConfigSelection(list, default = "WPA/WPA2" )) -config.plugins.wlan.encryption.wepkeytype = NoSave(ConfigSelection(weplist, default = "ASCII")) -config.plugins.wlan.encryption.psk = NoSave(ConfigPassword(default = "mysecurewlan", fixed_size = False)) class WlanStatus(Screen): @@ -44,20 +49,19 @@ class WlanStatus(Screen): <screen name="WlanStatus" position="center,center" size="560,400" title="Wireless Network State" > <ePixmap pixmap="skin_default/buttons/red.png" position="0,0" size="140,40" alphatest="on" /> <widget source="key_red" render="Label" position="0,0" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#9f1313" transparent="1" /> - - <widget source="LabelBSSID" render="Label" position="10,60" size="250,25" valign="left" font="Regular;20" transparent="1" foregroundColor="#FFFFFF" /> - <widget source="LabelESSID" render="Label" position="10,100" size="250,25" valign="center" font="Regular;20" transparent="1" foregroundColor="#FFFFFF" /> - <widget source="LabelQuality" render="Label" position="10,140" size="250,25" valign="center" font="Regular;20" transparent="1" foregroundColor="#FFFFFF" /> - <widget source="LabelSignal" render="Label" position="10,180" size="250,25" valign="center" font="Regular;20" transparent="1" foregroundColor="#FFFFFF" /> - <widget source="LabelBitrate" render="Label" position="10,220" size="250,25" valign="center" font="Regular;20" transparent="1" foregroundColor="#FFFFFF" /> - <widget source="LabelEnc" render="Label" position="10,260" size="250,25" valign="center" font="Regular;20" transparent="1" foregroundColor="#FFFFFF" /> - - <widget source="BSSID" render="Label" position="320,60" size="180,25" valign="center" font="Regular;20" transparent="1" foregroundColor="#FFFFFF" /> - <widget source="ESSID" render="Label" position="320,100" size="180,25" valign="center" font="Regular;20" transparent="1" foregroundColor="#FFFFFF" /> - <widget source="quality" render="Label" position="320,140" size="180,25" valign="center" font="Regular;20" transparent="1" foregroundColor="#FFFFFF" /> - <widget source="signal" render="Label" position="320,180" size="180,25" valign="center" font="Regular;20" transparent="1" foregroundColor="#FFFFFF" /> - <widget source="bitrate" render="Label" position="320,220" size="180,25" valign="center" font="Regular;20" transparent="1" foregroundColor="#FFFFFF" /> - <widget source="enc" render="Label" position="320,260" size="180,25" valign="center" font="Regular;20" transparent="1" foregroundColor="#FFFFFF" /> + + <widget source="LabelBSSID" render="Label" position="10,60" size="200,25" valign="left" font="Regular;20" transparent="1" foregroundColor="#FFFFFF" /> + <widget source="LabelESSID" render="Label" position="10,100" size="200,25" valign="center" font="Regular;20" transparent="1" foregroundColor="#FFFFFF" /> + <widget source="LabelQuality" render="Label" position="10,140" size="200,25" valign="center" font="Regular;20" transparent="1" foregroundColor="#FFFFFF" /> + <widget source="LabelSignal" render="Label" position="10,180" size="200,25" valign="center" font="Regular;20" transparent="1" foregroundColor="#FFFFFF" /> + <widget source="LabelBitrate" render="Label" position="10,220" size="200,25" valign="center" font="Regular;20" transparent="1" foregroundColor="#FFFFFF" /> + <widget source="LabelEnc" render="Label" position="10,260" size="200,25" valign="center" font="Regular;20" transparent="1" foregroundColor="#FFFFFF" /> + <widget source="BSSID" render="Label" position="220,60" size="330,25" valign="center" font="Regular;20" transparent="1" foregroundColor="#FFFFFF" /> + <widget source="ESSID" render="Label" position="220,100" size="330,25" valign="center" font="Regular;20" transparent="1" foregroundColor="#FFFFFF" /> + <widget source="quality" render="Label" position="220,140" size="330,25" valign="center" font="Regular;20" transparent="1" foregroundColor="#FFFFFF" /> + <widget source="signal" render="Label" position="220,180" size="330,25" valign="center" font="Regular;20" transparent="1" foregroundColor="#FFFFFF" /> + <widget source="bitrate" render="Label" position="220,220" size="330,25" valign="center" font="Regular;20" transparent="1" foregroundColor="#FFFFFF" /> + <widget source="enc" render="Label" position="220,260" size="330,25" valign="center" font="Regular;20" transparent="1" foregroundColor="#FFFFFF" /> <ePixmap pixmap="skin_default/div-h.png" position="0,350" zPosition="1" size="560,2" /> <widget source="IFtext" render="Label" position="10,355" size="120,21" zPosition="10" font="Regular;20" halign="left" backgroundColor="#25062748" transparent="1" /> @@ -103,7 +107,7 @@ class WlanStatus(Screen): }, -1) self.timer = eTimer() self.timer.timeout.get().append(self.resetList) - self.onShown.append(lambda: self.timer.start(5000)) + self.onShown.append(lambda: self.timer.start(8000)) self.onLayoutFinish.append(self.layoutFinished) self.onClose.append(self.cleanup) @@ -111,7 +115,7 @@ class WlanStatus(Screen): iStatus.stopWlanConsole() def layoutFinished(self): - self.setTitle(_("Wireless Network State")) + self.setTitle(_("Wireless network state")) def resetList(self): iStatus.getDataForInterface(self.iface,self.getInfoCB) @@ -120,32 +124,65 @@ class WlanStatus(Screen): if data is not None: if data is True: if status is not None: - self["BSSID"].setText(status[self.iface]["acesspoint"]) - self["ESSID"].setText(status[self.iface]["essid"]) - self["quality"].setText(status[self.iface]["quality"]+"%") - self["signal"].setText(status[self.iface]["signal"]) - self["bitrate"].setText(status[self.iface]["bitrate"]) - self["enc"].setText(status[self.iface]["encryption"]) + if status[self.iface]["essid"] == "off": + essid = _("No Connection") + else: + essid = status[self.iface]["essid"] + if status[self.iface]["accesspoint"] == "Not-Associated": + accesspoint = _("Not-Associated") + essid = _("No Connection") + else: + accesspoint = status[self.iface]["accesspoint"] + if self.has_key("BSSID"): + self["BSSID"].setText(accesspoint) + if self.has_key("ESSID"): + self["ESSID"].setText(essid) + + quality = status[self.iface]["quality"] + if self.has_key("quality"): + self["quality"].setText(quality) + + if status[self.iface]["bitrate"] == '0': + bitrate = _("Unsupported") + else: + bitrate = str(status[self.iface]["bitrate"]) + " Mb/s" + if self.has_key("bitrate"): + self["bitrate"].setText(bitrate) + + signal = status[self.iface]["signal"] + if self.has_key("signal"): + self["signal"].setText(signal) + + if status[self.iface]["encryption"] == "off": + if accesspoint == "Not-Associated": + encryption = _("Disabled") + else: + encryption = _("Unsupported") + else: + encryption = _("Enabled") + if self.has_key("enc"): + self["enc"].setText(encryption) self.updateStatusLink(status) def exit(self): self.timer.stop() - self.close(True) + self.close(True) def updateStatusbar(self): - self["BSSID"].setText(_("Please wait...")) - self["ESSID"].setText(_("Please wait...")) - self["quality"].setText(_("Please wait...")) - self["signal"].setText(_("Please wait...")) - self["bitrate"].setText(_("Please wait...")) - self["enc"].setText(_("Please wait...")) + wait_txt = _("Please wait...") + self["BSSID"].setText(wait_txt) + self["ESSID"].setText(wait_txt) + self["quality"].setText(wait_txt) + self["signal"].setText(wait_txt) + self["bitrate"].setText(wait_txt) + self["enc"].setText(wait_txt) self["IFtext"].setText(_("Network:")) self["IF"].setText(iNetwork.getFriendlyAdapterName(self.iface)) self["Statustext"].setText(_("Link:")) def updateStatusLink(self,status): if status is not None: - if status[self.iface]["acesspoint"] == "No Connection" or status[self.iface]["acesspoint"] == "Not-Associated" or status[self.iface]["acesspoint"] == False: + if status[self.iface]["essid"] == "off" or status[self.iface]["accesspoint"] == "Not-Associated" or status[self.iface]["accesspoint"] == False: self["statuspic"].setPixmapNum(1) else: self["statuspic"].setPixmapNum(0) @@ -190,7 +227,7 @@ class WlanScan(Screen): self.newAPList = None self.WlanList = None self.cleanList = None - self.oldlist = None + self.oldlist = {} self.listLength = None self.rescanTimer = eTimer() self.rescanTimer.callback.append(self.rescanTimerFired) @@ -215,6 +252,8 @@ class WlanScan(Screen): "red": self.cancel, "green": self.select, }) + iWlan.setInterface(self.iface) + self.w = iWlan.getInterface() self.onLayoutFinish.append(self.layoutFinished) self.getAccessPoints(refresh = False) @@ -224,42 +263,24 @@ class WlanScan(Screen): def select(self): cur = self["list"].getCurrent() if cur is not None: + iWlan.stopGetNetworkList() self.rescanTimer.stop() del self.rescanTimer - if cur[1] is not None: - if cur[1] == 'hidden...': - essid = cur[1] - else: - essid = cur[0] - self.close(essid,self.getWlanList()) + if cur[0] is not None: + self.close(cur[0]) else: - self.close(None,None) + self.close(None) else: - self.rescanTimer.stop() - del self.rescanTimer - self.close(None,None) - - def WlanSetupClosed(self, *ret): - if ret[0] == 2: + iWlan.stopGetNetworkList() self.rescanTimer.stop() del self.rescanTimer self.close(None) def cancel(self): - if self.oldInterfaceState is False: - iNetwork.setAdapterAttribute(self.iface, "up", False) - iNetwork.deactivateInterface(self.iface,self.deactivateInterfaceCB) - else: - self.rescanTimer.stop() - del self.rescanTimer - self.close(None) - - def deactivateInterfaceCB(self,data): - if data is not None: - if data is True: - self.rescanTimer.stop() - del self.rescanTimer - self.close(None) + iWlan.stopGetNetworkList() + self.rescanTimer.stop() + del self.rescanTimer + self.close(None) def rescanTimerFired(self): self.rescanTimer.stop() @@ -268,46 +289,36 @@ class WlanScan(Screen): def buildEntryComponent(self, essid, bssid, encrypted, iface, maxrate, signal): divpng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_SKIN_IMAGE, "skin_default/div-h.png")) encryption = encrypted and _("Yes") or _("No") - if bssid == 'hidden...': - return((essid, bssid, None, None, None, None, divpng)) - else: - return((essid, bssid, _("Signal: ") + str(signal), _("Max. Bitrate: ") + str(maxrate), _("Encrypted: ") + encryption, _("Interface: ") + str(iface), divpng)) + return((essid, bssid, _("Signal: ") + str(signal), _("Max. Bitrate: ") + str(maxrate), _("Encrypted: ") + encryption, _("Interface: ") + str(iface), divpng)) def updateAPList(self): - self.oldlist = [] - self.oldlist = self.cleanList - self.newAPList = [] newList = [] + newList = self.getAccessPoints(refresh = True) + self.newAPList = [] tmpList = [] newListIndex = None currentListEntry = None currentListIndex = None - newList = self.getAccessPoints(refresh = True) - - for oldentry in self.oldlist: - if oldentry not in newList: - newList.append(oldentry) - for newentry in newList: - if newentry[1] == "hidden...": - continue - tmpList.append(newentry) + for ap in self.oldlist.keys(): + data = self.oldlist[ap]['data'] + if data is not None: + tmpList.append(data) if len(tmpList): - if "hidden..." not in tmpList: - tmpList.append( ( _("enter hidden network SSID"), "hidden...", True, self.iface, _("unavailable"), "" ) ) - for entry in tmpList: self.newAPList.append(self.buildEntryComponent( entry[0], entry[1], entry[2], entry[3], entry[4], entry[5] )) currentListEntry = self["list"].getCurrent() - idx = 0 - for entry in self.newAPList: - if entry == currentListEntry: - newListIndex = idx - idx +=1 + if currentListEntry is not None: + idx = 0 + for entry in self.newAPList: + if entry[0] == currentListEntry[0]: + newListIndex = idx + idx +=1 self['list'].setList(self.newAPList) - self["list"].setIndex(newListIndex) + if newListIndex is not None: + self["list"].setIndex(newListIndex) self["list"].updateList(self.newAPList) self.listLength = len(self.newAPList) self.buildWlanList() @@ -316,8 +327,7 @@ class WlanScan(Screen): def getAccessPoints(self, refresh = False): self.APList = [] self.cleanList = [] - self.w = Wlan(self.iface) - aps = self.w.getNetworkList() + aps = iWlan.getNetworkList() if aps is not None: print "[WirelessLan.py] got Accespoints!" tmpList = [] @@ -335,10 +345,11 @@ class WlanScan(Screen): compList.remove(compentry) for entry in compList: self.cleanList.append( ( entry[0], entry[1], entry[2], entry[3], entry[4], entry[5] ) ) + if not self.oldlist.has_key(entry[0]): + self.oldlist[entry[0]] = { 'data': entry } + else: + self.oldlist[entry[0]]['data'] = entry - if "hidden..." not in self.cleanList: - self.cleanList.append( ( _("enter hidden network SSID"), "hidden...", True, self.iface, _("unavailable"), "" ) ) - for entry in self.cleanList: self.APList.append(self.buildEntryComponent( entry[0], entry[1], entry[2], entry[3], entry[4], entry[5] )) @@ -351,20 +362,17 @@ class WlanScan(Screen): def setInfo(self): length = self.getLength() - if length <= 1: - self["info"].setText(_("No wireless networks found! Please refresh.")) - elif length == 2: + if length == 0: + self["info"].setText(_("No wireless networks found! Searching...")) + elif length == 1: self["info"].setText(_("1 wireless network found!")) else: - self["info"].setText(str(length-1)+_(" wireless networks found!")) + self["info"].setText(str(length)+_(" wireless networks found!")) def buildWlanList(self): self.WlanList = [] for entry in self['list'].list: - if entry[1] == "hidden...": - self.WlanList.append(( "hidden...",_("enter hidden network SSID") ))#continue - else: - self.WlanList.append( (entry[0], entry[0]) ) + self.WlanList.append( (entry[0], entry[0]) ) def getLength(self): return self.listLength @@ -375,30 +383,95 @@ class WlanScan(Screen): return self.WlanList +def bin2long(s): + return reduce( lambda x,y:(x<<8L)+y, map(ord, s)) + +def long2bin(l): + res = "" + for byte in range(128): + res += chr((l >> (1024 - (byte + 1) * 8)) & 0xff) + return res + +def rsa_pub1024(src, mod): + return long2bin(pow(bin2long(src), 65537, bin2long(mod))) + +def decrypt_block(src, mod): + if len(src) != 128 and len(src) != 202: + return None + dest = rsa_pub1024(src[:128], mod) + hash = hashlib.sha1(dest[1:107]) + if len(src) == 202: + hash.update(src[131:192]) + result = hash.digest() + if result == dest[107:127]: + return dest + return None + +def validate_certificate(cert, key): + buf = decrypt_block(cert[8:], key) + if buf is None: + return None + return buf[36:107] + cert[139:196] + +def get_random(): + try: + xor = lambda a,b: ''.join(chr(ord(c)^ord(d)) for c,d in zip(a,b*100)) + random = urandom(8) + x = str(time())[-8:] + result = xor(random, x) + + return result + except: + return None + def WlanStatusScreenMain(session, iface): session.open(WlanStatus, iface) - def callFunction(iface): - w = Wlan(iface) - i = w.getWirelessInterfaces() + iWlan.setInterface(iface) + i = iWlan.getWirelessInterfaces() if i: - if iface in i: + if iface in i or iNetwork.isWirelessInterface(iface): return WlanStatusScreenMain + return None return None - def configStrings(iface): - driver = iNetwork.detectWlanModule() - print "Found WLAN-Driver:",driver - if driver in ('ralink', 'zydas'): - return " pre-up /usr/sbin/wpa_supplicant -i"+iface+" -c/etc/wpa_supplicant.conf -B -D"+driver+"\n post-down wpa_cli terminate" + try: + device = open("/proc/stb/info/model", "r").readline().strip() + except: + device = "" + if device != "dm7025": + rootkey = ['\x9f', '|', '\xe4', 'G', '\xc9', '\xb4', '\xf4', '#', '&', '\xce', '\xb3', '\xfe', '\xda', '\xc9', 'U', '`', '\xd8', '\x8c', 's', 'o', '\x90', '\x9b', '\\', 'b', '\xc0', '\x89', '\xd1', '\x8c', '\x9e', 'J', 'T', '\xc5', 'X', '\xa1', '\xb8', '\x13', '5', 'E', '\x02', '\xc9', '\xb2', '\xe6', 't', '\x89', '\xde', '\xcd', '\x9d', '\x11', '\xdd', '\xc7', '\xf4', '\xe4', '\xe4', '\xbc', '\xdb', '\x9c', '\xea', '}', '\xad', '\xda', 't', 'r', '\x9b', '\xdc', '\xbc', '\x18', '3', '\xe7', '\xaf', '|', '\xae', '\x0c', '\xe3', '\xb5', '\x84', '\x8d', '\r', '\x8d', '\x9d', '2', '\xd0', '\xce', '\xd5', 'q', '\t', '\x84', 'c', '\xa8', ')', '\x99', '\xdc', '<', '"', 'x', '\xe8', '\x87', '\x8f', '\x02', ';', 'S', 'm', '\xd5', '\xf0', '\xa3', '_', '\xb7', 'T', '\t', '\xde', '\xa7', '\xf1', '\xc9', '\xae', '\x8a', '\xd7', '\xd2', '\xcf', '\xb2', '.', '\x13', '\xfb', '\xac', 'j', '\xdf', '\xb1', '\x1d', ':', '?'] + etpm = eTPM() + l2cert = etpm.getCert(eTPM.TPMD_DT_LEVEL2_CERT) + if l2cert is None: + return + l2key = validate_certificate(l2cert, rootkey) + if l2key is None: + return + l3cert = etpm.getCert(eTPM.TPMD_DT_LEVEL3_CERT) + if l3cert is None: + return + l3key = validate_certificate(l3cert, l2key) + if l3key is None: + return + rnd = get_random() + if rnd is None: + return + val = etpm.challenge(rnd) + result = decrypt_block(val, l3key) + if device == "dm7025" or result[80:88] == rnd: + driver = iNetwork.detectWlanModule(iface) else: - if config.plugins.wlan.essid.value == "hidden...": - return ' pre-up iwconfig '+iface+' essid "'+config.plugins.wlan.hiddenessid.value+'"\n pre-up /usr/sbin/wpa_supplicant -i'+iface+' -c/etc/wpa_supplicant.conf -B -dd -D'+driver+'\n post-down wpa_cli terminate' - else: - return ' pre-up iwconfig '+iface+' essid "'+config.plugins.wlan.essid.value+'"\n pre-up /usr/sbin/wpa_supplicant -i'+iface+' -c/etc/wpa_supplicant.conf -B -dd -D'+driver+'\n post-down wpa_cli terminate' + driver = 'dreambox' + print 'Using "%s" as wpa-supplicant driver' % (driver) + ret = "" + if driver == 'madwifi' and config.plugins.wlan.hiddenessid.value: + ret += "\tpre-up iwconfig " + iface + " essid \"" + re_escape(config.plugins.wlan.essid.value) + "\" || true\n" + ret += "\tpre-up wpa_supplicant -i" + iface + " -c" + getWlanConfigName(iface) + " -B -dd -D" + driver + " || true\n" + ret += "\tpre-down wpa_cli -i" + iface + " terminate || true\n" + return ret def Plugins(**kwargs): - return PluginDescriptor(name=_("Wireless LAN"), description=_("Connect to a Wireless Network"), where = PluginDescriptor.WHERE_NETWORKSETUP, fnc={"ifaceSupported": callFunction, "configStrings": configStrings, "WlanPluginEntry": lambda x: "Wireless Network Configuartion..."}) -
\ No newline at end of file + return PluginDescriptor(name=_("Wireless LAN"), description=_("Connect to a Wireless Network"), where = PluginDescriptor.WHERE_NETWORKSETUP, needsRestart = False, fnc={"ifaceSupported": callFunction, "configStrings": configStrings, "WlanPluginEntry": lambda x: "Wireless Network Configuartion..."}) diff --git a/lib/python/Plugins/newplugin.py b/lib/python/Plugins/newplugin.py index c1c6f948..32c7269d 100644..100755 --- a/lib/python/Plugins/newplugin.py +++ b/lib/python/Plugins/newplugin.py @@ -137,7 +137,7 @@ def Plugins(**kwargs): file.close() makefile = open(pluginpath + "/Makefile.am", "w") -makefile.write("""installdir = $(LIBDIR)/enigma2/python/Plugins/%s/%s +makefile.write("""installdir = $(pkglibdir)/python/Plugins/%s/%s install_PYTHON = \\ __init__.py \\ diff --git a/lib/python/Screens/AudioSelection.py b/lib/python/Screens/AudioSelection.py new file mode 100644 index 00000000..4c689620 --- /dev/null +++ b/lib/python/Screens/AudioSelection.py @@ -0,0 +1,317 @@ +from Screen import Screen +from Components.ServiceEventTracker import ServiceEventTracker +from Components.ActionMap import ActionMap +from Components.ConfigList import ConfigListScreen +from Components.ChoiceList import ChoiceList, ChoiceEntryComponent +from Components.config import config, ConfigSubsection, getConfigListEntry, ConfigNothing, ConfigSelection, ConfigOnOff +from Components.MultiContent import MultiContentEntryText +from Components.Sources.List import List +from Components.Sources.Boolean import Boolean +from Components.SystemInfo import SystemInfo + +from enigma import iPlayableService + +from Tools.ISO639 import LanguageCodes +from Tools.BoundFunction import boundFunction +FOCUS_CONFIG, FOCUS_STREAMS = range(2) +[PAGE_AUDIO, PAGE_SUBTITLES] = ["audio", "subtitles"] + +class AudioSelection(Screen, ConfigListScreen): + def __init__(self, session, infobar=None, page=PAGE_AUDIO): + Screen.__init__(self, session) + + self["streams"] = List([]) + self["key_red"] = Boolean(False) + self["key_green"] = Boolean(False) + self["key_yellow"] = Boolean(True) + self["key_blue"] = Boolean(False) + + ConfigListScreen.__init__(self, []) + self.infobar = infobar or self.session.infobar + + self.__event_tracker = ServiceEventTracker(screen=self, eventmap= + { + iPlayableService.evUpdatedInfo: self.__updatedInfo + }) + self.cached_subtitle_checked = False + self.__selected_subtitle = None + + self["actions"] = ActionMap(["ColorActions", "SetupActions", "DirectionActions"], + { + "red": self.keyRed, + "green": self.keyGreen, + "yellow": self.keyYellow, + "blue": self.keyBlue, + "ok": self.keyOk, + "cancel": self.cancel, + "up": self.keyUp, + "down": self.keyDown, + }, -2) + + self.settings = ConfigSubsection() + choicelist = [(PAGE_AUDIO,_("audio tracks")), (PAGE_SUBTITLES,_("Subtitles"))] + self.settings.menupage = ConfigSelection(choices = choicelist, default=page) + self.onLayoutFinish.append(self.__layoutFinished) + + def __layoutFinished(self): + self["config"].instance.setSelectionEnable(False) + self.focus = FOCUS_STREAMS + self.settings.menupage.addNotifier(self.fillList) + + def fillList(self, arg=None): + streams = [] + conflist = [] + selectedidx = 0 + + if self.settings.menupage.getValue() == PAGE_AUDIO: + self.setTitle(_("Select audio track")) + service = self.session.nav.getCurrentService() + self.audioTracks = audio = service and service.audioTracks() + n = audio and audio.getNumberOfTracks() or 0 + if SystemInfo["CanDownmixAC3"]: + self.settings.downmix = ConfigOnOff(default=config.av.downmix_ac3.value) + self.settings.downmix.addNotifier(self.changeAC3Downmix, initial_call = False) + conflist.append(getConfigListEntry(_("AC3 downmix"), self.settings.downmix)) + self["key_red"].setBoolean(True) + + if n > 0: + self.audioChannel = service.audioChannel() + if self.audioChannel: + choicelist = [("0",_("left")), ("1",_("stereo")), ("2", _("right"))] + self.settings.channelmode = ConfigSelection(choices = choicelist, default = str(self.audioChannel.getCurrentChannel())) + self.settings.channelmode.addNotifier(self.changeMode, initial_call = False) + conflist.append(getConfigListEntry(_("Channel"), self.settings.channelmode)) + self["key_green"].setBoolean(True) + else: + conflist.append(('',)) + self["key_green"].setBoolean(False) + selectedAudio = self.audioTracks.getCurrentTrack() + for x in range(n): + number = str(x) + i = audio.getTrackInfo(x) + languages = i.getLanguage().split('/') + description = i.getDescription() or _("<unknown>") + selected = "" + language = "" + + if selectedAudio == x: + selected = _("Running") + selectedidx = x + + cnt = 0 + for lang in languages: + if cnt: + language += ' / ' + if LanguageCodes.has_key(lang): + language += LanguageCodes[lang][0] + elif lang == "und": + _("<unknown>") + else: + language += lang + cnt += 1 + + streams.append((x, "", number, description, language, selected)) + + else: + streams = [] + conflist.append(('',)) + self["key_green"].setBoolean(False) + + elif self.settings.menupage.getValue() == PAGE_SUBTITLES: + self.setTitle(_("Subtitle selection")) + conflist.append(('',)) + conflist.append(('',)) + self["key_red"].setBoolean(False) + self["key_green"].setBoolean(False) + + if self.subtitlesEnabled(): + sel = self.infobar.selected_subtitle + else: + sel = None + + idx = 0 + + subtitlelist = self.getSubtitleList() + + if len(subtitlelist): + for x in subtitlelist: + number = str(x[1]) + description = "?" + language = _("<unknown>") + selected = "" + + if sel and x == sel: + selected = _("Running") + selectedidx = idx + + if x[4] != "und": + if LanguageCodes.has_key(x[4]): + language = LanguageCodes[x[4]][0] + else: + language = x[4] + + if x[0] == 0: + description = "DVB" + number = "%x" % (x[1]) + + elif x[0] == 1: + description = "TTX" + number = "%x%02x" % (x[3],x[2]) + + elif x[0] == 2: + types = (_("<unknown>"), "UTF-8 text", "SSA", "AAS", ".SRT file", "VOB", "PGS (unsupported)") + description = types[x[2]] + + streams.append((x, "", number, description, language, selected)) + idx += 1 + + else: + streams = [] + + conflist.append(getConfigListEntry(_("Menu"), self.settings.menupage)) + + from Components.PluginComponent import plugins + from Plugins.Plugin import PluginDescriptor + + if hasattr(self.infobar, "runPlugin"): + class PluginCaller: + def __init__(self, fnc, *args): + self.fnc = fnc + self.args = args + def __call__(self, *args, **kwargs): + self.fnc(*self.args) + + Plugins = [ (p.name, PluginCaller(self.infobar.runPlugin, p)) for p in plugins.getPlugins(where = PluginDescriptor.WHERE_AUDIOMENU) ] + + if len(Plugins): + self["key_blue"].setBoolean(True) + conflist.append(getConfigListEntry(Plugins[0][0], ConfigNothing())) + self.plugincallfunc = Plugins[0][1] + if len(Plugins) > 1: + print "plugin(s) installed but not displayed in the dialog box:", Plugins[1:] + + self["config"].list = conflist + self["config"].l.setList(conflist) + + self["streams"].list = streams + self["streams"].setIndex(selectedidx) + + def __updatedInfo(self): + self.fillList() + + def getSubtitleList(self): + s = self.infobar and self.infobar.getCurrentServiceSubtitle() + l = s and s.getSubtitleList() or [ ] + return l + + def subtitlesEnabled(self): + return self.infobar.subtitles_enabled + + def enableSubtitle(self, subtitles): + if self.infobar.selected_subtitle != subtitles: + self.infobar.subtitles_enabled = False + self.infobar.selected_subtitle = subtitles + if subtitles: + self.infobar.subtitles_enabled = True + + def changeAC3Downmix(self, downmix): + if downmix.getValue() == True: + config.av.downmix_ac3.value = True + else: + config.av.downmix_ac3.value = False + config.av.downmix_ac3.save() + + def changeMode(self, mode): + if mode is not None and self.audioChannel: + self.audioChannel.selectChannel(int(mode.getValue())) + + def changeAudio(self, audio): + track = int(audio) + if isinstance(track, int): + if self.session.nav.getCurrentService().audioTracks().getNumberOfTracks() > track: + self.audioTracks.selectTrack(track) + + def keyLeft(self): + if self.focus == FOCUS_CONFIG: + ConfigListScreen.keyLeft(self) + elif self.focus == FOCUS_STREAMS: + self["streams"].setIndex(0) + + def keyRight(self, config = False): + if config or self.focus == FOCUS_CONFIG: + if self["config"].getCurrentIndex() < 3: + ConfigListScreen.keyRight(self) + elif hasattr(self, "plugincallfunc"): + self.plugincallfunc() + if self.focus == FOCUS_STREAMS and self["streams"].count() and config == False: + self["streams"].setIndex(self["streams"].count()-1) + + def keyRed(self): + if self["key_red"].getBoolean(): + self.colorkey(0) + + def keyGreen(self): + if self["key_green"].getBoolean(): + self.colorkey(1) + + def keyYellow(self): + if self["key_yellow"].getBoolean(): + self.colorkey(2) + + def keyBlue(self): + if self["key_blue"].getBoolean(): + self.colorkey(3) + + def colorkey(self, idx): + self["config"].setCurrentIndex(idx) + self.keyRight(True) + + def keyUp(self): + if self.focus == FOCUS_CONFIG: + self["config"].instance.moveSelection(self["config"].instance.moveUp) + elif self.focus == FOCUS_STREAMS: + if self["streams"].getIndex() == 0: + self["config"].instance.setSelectionEnable(True) + self["streams"].style = "notselected" + self["config"].setCurrentIndex(len(self["config"].getList())-1) + self.focus = FOCUS_CONFIG + else: + self["streams"].selectPrevious() + + def keyDown(self): + if self.focus == FOCUS_CONFIG: + if self["config"].getCurrentIndex() < len(self["config"].getList())-1: + self["config"].instance.moveSelection(self["config"].instance.moveDown) + else: + self["config"].instance.setSelectionEnable(False) + self["streams"].style = "default" + self.focus = FOCUS_STREAMS + elif self.focus == FOCUS_STREAMS: + self["streams"].selectNext() + + def keyOk(self): + if self.focus == FOCUS_STREAMS and self["streams"].list: + cur = self["streams"].getCurrent() + if self.settings.menupage.getValue() == PAGE_AUDIO and cur[0] is not None: + self.changeAudio(cur[2]) + self.__updatedInfo() + if self.settings.menupage.getValue() == PAGE_SUBTITLES and cur[0] is not None: + if self.infobar.selected_subtitle == cur[0]: + self.enableSubtitle(None) + selectedidx = self["streams"].getIndex() + self.__updatedInfo() + self["streams"].setIndex(selectedidx) + else: + self.enableSubtitle(cur[0]) + self.__updatedInfo() + self.close(0) + elif self.focus == FOCUS_CONFIG: + self.keyRight() + + def cancel(self): + self.close(0) + +class SubtitleSelection(AudioSelection): + def __init__(self, session, infobar=None): + AudioSelection.__init__(self, session, infobar, page=PAGE_SUBTITLES) + self.skinName = ["AudioSelection"] diff --git a/lib/python/Screens/ChannelSelection.py b/lib/python/Screens/ChannelSelection.py index 33afb3f3..912d6784 100644 --- a/lib/python/Screens/ChannelSelection.py +++ b/lib/python/Screens/ChannelSelection.py @@ -8,7 +8,7 @@ from Components.MenuList import MenuList from Components.ServiceEventTracker import ServiceEventTracker, InfoBarBase profile("ChannelSelection.py 1") from EpgSelection import EPGSelection -from enigma import eServiceReference, eEPGCache, eServiceCenter, eRCInput, eTimer, eDVBDB, iPlayableService, iServiceInformation, getPrevAsciiCode +from enigma import eServiceReference, eEPGCache, eServiceCenter, eRCInput, eTimer, eDVBDB, iPlayableService, iServiceInformation, getPrevAsciiCode, eEnv from Components.config import config, ConfigSubsection, ConfigText from Tools.NumericalTextInput import NumericalTextInput profile("ChannelSelection.py 2") @@ -20,7 +20,6 @@ from Components.Sources.ServiceEvent import ServiceEvent profile("ChannelSelection.py 2.3") from Components.Input import Input profile("ChannelSelection.py 3") -from Components.ParentalControl import parentalControl from Components.ChoiceList import ChoiceList, ChoiceEntryComponent from Components.SystemInfo import SystemInfo from Screens.InputBox import InputBox, PinInput @@ -95,6 +94,7 @@ def append_when_current_valid(current, menu, args, level = 0, key = ""): class ChannelContextMenu(Screen): def __init__(self, session, csel): + Screen.__init__(self, session) #raise Exception("we need a better summary screen here") self.csel = csel @@ -124,14 +124,22 @@ class ChannelContextMenu(Screen): isPlayable = not (current_sel_flags & (eServiceReference.isMarker|eServiceReference.isDirectory)) if isPlayable: if config.ParentalControl.configured.value: + from Components.ParentalControl import parentalControl if parentalControl.getProtectionLevel(csel.getCurrentSelection().toCompareString()) == -1: append_when_current_valid(current, menu, (_("add to parental protection"), boundFunction(self.addParentalProtection, csel.getCurrentSelection())), level = 0) else: append_when_current_valid(current, menu, (_("remove from parental protection"), boundFunction(self.removeParentalProtection, csel.getCurrentSelection())), level = 0) if haveBouquets: - append_when_current_valid(current, menu, (_("add service to bouquet"), self.addServiceToBouquetSelected), level = 0) + bouquets = self.csel.getBouquetList() + if bouquets is None: + bouquetCnt = 0 + else: + bouquetCnt = len(bouquets) + if not inBouquet or bouquetCnt > 1: + append_when_current_valid(current, menu, (_("add service to bouquet"), self.addServiceToBouquetSelected), level = 0) else: - append_when_current_valid(current, menu, (_("add service to favourites"), self.addServiceToBouquetSelected), level = 0) + if not inBouquet: + append_when_current_valid(current, menu, (_("add service to favourites"), self.addServiceToBouquetSelected), level = 0) else: if current_root.getPath().find('FROM SATELLITES') != -1: append_when_current_valid(current, menu, (_("remove selected satellite"), self.removeSatelliteServices), level = 0) @@ -202,6 +210,7 @@ class ChannelContextMenu(Screen): self.close() def addParentalProtection(self, service): + from Components.ParentalControl import parentalControl parentalControl.protectService(service.toCompareString()) self.close() @@ -210,6 +219,7 @@ class ChannelContextMenu(Screen): def pinEntered(self, service, result): if result: + from Components.ParentalControl import parentalControl parentalControl.unProtectService(service) self.close() else: @@ -241,7 +251,7 @@ class ChannelContextMenu(Screen): if cnt > 1: # show bouquet list self.bsel = self.session.openWithCallback(self.bouquetSelClosed, BouquetSelector, bouquets, self.addCurrentServiceToBouquet) elif cnt == 1: # add to only one existing bouquet - self.addCurrentServiceToBouquet(bouquets[0][1]) + self.addCurrentServiceToBouquet(bouquets[0][1], closeBouquetSelection = False) def bouquetSelClosed(self, recursive): self.bsel = None @@ -275,12 +285,12 @@ class ChannelContextMenu(Screen): self.csel.addMarker(marker) self.close() - def addCurrentServiceToBouquet(self, dest): + def addCurrentServiceToBouquet(self, dest, closeBouquetSelection = True): self.csel.addServiceToBouquet(dest) if self.bsel is not None: self.bsel.close(True) else: - self.close(True) # close bouquet selection + self.close(closeBouquetSelection) # close bouquet selection def removeCurrentService(self): self.csel.removeCurrentService() @@ -467,7 +477,7 @@ class ChannelSelectionEdit: if mutableAlternatives: mutableAlternatives.setListName(name) if mutableAlternatives.addService(cur_service.ref): - print "add", cur_service.toString(), "to new alternatives failed" + print "add", cur_service.ref.toString(), "to new alternatives failed" mutableAlternatives.flushChanges() self.servicelist.addService(new_ref.ref, True) self.servicelist.removeCurrent() @@ -552,7 +562,7 @@ class ChannelSelectionEdit: refstr = refstr[pos+14:] pos = refstr.find('"') if pos != -1: - filename = '/etc/enigma2/' + refstr[:pos] # FIXMEEE !!! HARDCODED /etc/enigma2 + filename = eEnv.resolve('${sysconfdir}/enigma2/') + refstr[:pos] self.removeCurrentService() try: if filename is not None: @@ -1418,9 +1428,9 @@ class ChannelSelectionRadio(ChannelSelectionBase, ChannelSelectionEdit, ChannelS self["actions"] = ActionMap(["OkCancelActions", "TvRadioActions"], { - "keyTV": self.closeRadio, - "keyRadio": self.closeRadio, - "cancel": self.closeRadio, + "keyTV": self.cancel, + "keyRadio": self.cancel, + "cancel": self.cancel, "ok": self.channelSelected, }) @@ -1458,7 +1468,7 @@ class ChannelSelectionRadio(ChannelSelectionBase, ChannelSelectionEdit, ChannelS self["RdsActions"].setEnabled(state) ########## RDS Radiotext / Rass Support END - def closeRadio(self): + def cancel(self): self.infobar.rds_display.onRassInteractivePossibilityChanged.remove(self.RassInteractivePossibilityChanged) self.info.hide() #set previous tv service diff --git a/lib/python/Screens/DefaultWizard.py b/lib/python/Screens/DefaultWizard.py index 73b07acf..54e241de 100644 --- a/lib/python/Screens/DefaultWizard.py +++ b/lib/python/Screens/DefaultWizard.py @@ -25,9 +25,10 @@ class DefaultWizard(WizardLanguage, DreamInfoHandler): self["arrowup2"] = MovingPixmap() def setDirectory(self): - os_system("mount %s %s" % (resolveFilename(SCOPE_DEFAULTPARTITION), resolveFilename(SCOPE_DEFAULTPARTITIONMOUNTDIR))) self.directory = resolveFilename(SCOPE_DEFAULTPARTITIONMOUNTDIR) self.xmlfile = "defaultwizard.xml" + if self.directory: + os_system("mount %s %s" % (resolveFilename(SCOPE_DEFAULTPARTITION), self.directory)) def markDone(self): config.misc.defaultchosen.value = 0 diff --git a/lib/python/Screens/InfoBar.py b/lib/python/Screens/InfoBar.py index 5b061245..55062878 100644 --- a/lib/python/Screens/InfoBar.py +++ b/lib/python/Screens/InfoBar.py @@ -221,6 +221,7 @@ class MoviePlayer(InfoBarBase, InfoBarShowHide, \ self.session.nav.stopService() elif answer == "restart": self.doSeek(0) + self.setSeekState(self.SEEK_STATE_PLAY) def doEofInternal(self, playing): if not self.execing: diff --git a/lib/python/Screens/InfoBarGenerics.py b/lib/python/Screens/InfoBarGenerics.py index 5404a4e0..93a47886 100644 --- a/lib/python/Screens/InfoBarGenerics.py +++ b/lib/python/Screens/InfoBarGenerics.py @@ -487,7 +487,7 @@ class InfoBarEPG: self["EPGActions"] = HelpableActionMap(self, "InfobarEPGActions", { "showEventInfo": (self.openEventView, _("show EPG...")), - "showEventInfoPlugin": (self.showEventInfoPlugins, _("show single service EPG...")), + "showEventInfoPlugin": (self.showEventInfoPlugins, _("list of EPG views...")), "showInfobarOrEpgWhenInfobarAlreadyVisible": self.showEventInfoWhenNotVisible, }) @@ -619,6 +619,7 @@ class InfoBarEPG: if list: list.append((_("show single service EPG..."), self.openSingleServiceEPG)) + list.append((_("Multi EPG"), self.openMultiServiceEPG)) self.session.openWithCallback(self.EventInfoPluginChosen, ChoiceBox, title=_("Please choose an extension..."), list = list, skin_name = "EPGExtensionsList") else: self.openSingleServiceEPG() @@ -737,7 +738,7 @@ class InfoBarSeek: SEEK_STATE_PAUSE = (1, 0, 0, "||") SEEK_STATE_EOF = (1, 0, 0, "END") - def __init__(self, actionmap = "InfobarSeekActions", useSeekBackHack=True): + def __init__(self, actionmap = "InfobarSeekActions"): self.__event_tracker = ServiceEventTracker(screen=self, eventmap= { iPlayableService.evSeekableStatusChanged: self.__seekableStatusChanged, @@ -794,20 +795,10 @@ class InfoBarSeek: self.__seekableStatusChanged() def makeStateForward(self, n): -# minspeed = config.seek.stepwise_minspeed.value -# repeat = int(config.seek.stepwise_repeat.value) -# if minspeed != "Never" and n >= int(minspeed) and repeat > 1: -# return (0, n * repeat, repeat, ">> %dx" % n) -# else: - return (0, n, 0, ">> %dx" % n) + return (0, n, 0, ">> %dx" % n) def makeStateBackward(self, n): -# minspeed = config.seek.stepwise_minspeed.value -# repeat = int(config.seek.stepwise_repeat.value) -# if minspeed != "Never" and n >= int(minspeed) and repeat > 1: -# return (0, -n * repeat, repeat, "<< %dx" % n) -# else: - return (0, -n, 0, "<< %dx" % n) + return (0, -n, 0, "<< %dx" % n) def makeStateSlowMotion(self, n): return (0, 0, n, "/%d" % n) @@ -1131,15 +1122,21 @@ class InfoBarPVRState: self.pvrStateDialog.hide() else: self._mayShow() - class InfoBarTimeshiftState(InfoBarPVRState): def __init__(self): InfoBarPVRState.__init__(self, screen=TimeshiftState, force_show = True) + self.__hideTimer = eTimer() + self.__hideTimer.callback.append(self.__hideTimeshiftState) def _mayShow(self): - if self.execing and self.timeshift_enabled and self.seekstate != self.SEEK_STATE_PLAY: + if self.execing and self.timeshift_enabled: self.pvrStateDialog.show() + if self.seekstate == self.SEEK_STATE_PLAY and not self.shown: + self.__hideTimer.start(5*1000, True) + + def __hideTimeshiftState(self): + self.pvrStateDialog.hide() class InfoBarShowMovies: @@ -1377,6 +1374,7 @@ class InfoBarExtensions: answer[1][1]() from Tools.BoundFunction import boundFunction +import inspect # depends on InfoBarExtensions @@ -1388,9 +1386,13 @@ class InfoBarPlugins: return name def getPluginList(self): - list = [((boundFunction(self.getPluginName, p.name), boundFunction(self.runPlugin, p), lambda: True), None, p.name) for p in plugins.getPlugins(where = PluginDescriptor.WHERE_EXTENSIONSMENU)] - list.sort(key = lambda e: e[2]) # sort by name - return list + l = [] + for p in plugins.getPlugins(where = PluginDescriptor.WHERE_EXTENSIONSMENU): + args = inspect.getargspec(p.__call__)[0] + if len(args) == 1 or len(args) == 2 and isinstance(self, InfoBarChannelSelection): + l.append(((boundFunction(self.getPluginName, p.name), boundFunction(self.runPlugin, p), lambda: True), None, p.name)) + l.sort(key = lambda e: e[2]) # sort by name + return l def runPlugin(self, plugin): if isinstance(self, InfoBarChannelSelection): @@ -1548,28 +1550,30 @@ class InfoBarInstantRecord: recording = RecordTimerEntry(serviceref, begin, end, name, description, eventid, dirname = preferredInstantRecordPath()) recording.dontSave = True - + if event is None or limitEvent == False: recording.autoincrease = True - if recording.setAutoincreaseEnd(): - self.session.nav.RecordTimer.record(recording) - self.recording.append(recording) + recording.setAutoincreaseEnd() + + simulTimerList = self.session.nav.RecordTimer.record(recording) + + if simulTimerList is None: # no conflict + self.recording.append(recording) else: - simulTimerList = self.session.nav.RecordTimer.record(recording) - if simulTimerList is not None: # conflict with other recording - name = simulTimerList[1].name - name_date = ' '.join((name, strftime('%c', localtime(simulTimerList[1].begin)))) - print "[TIMER] conflicts with", name_date - recording.autoincrease = True # start with max available length, then increment - if recording.setAutoincreaseEnd(): - self.session.nav.RecordTimer.record(recording) - self.recording.append(recording) - self.session.open(MessageBox, _("Record time limited due to conflicting timer %s") % name_date, MessageBox.TYPE_INFO) - else: - self.session.open(MessageBox, _("Couldn't record due to conflicting timer %s") % name, MessageBox.TYPE_INFO) - recording.autoincrease = False - else: + if len(simulTimerList) > 1: # with other recording + name = simulTimerList[1].name + name_date = ' '.join((name, strftime('%c', localtime(simulTimerList[1].begin)))) + print "[TIMER] conflicts with", name_date + recording.autoincrease = True # start with max available length, then increment + if recording.setAutoincreaseEnd(): + self.session.nav.RecordTimer.record(recording) self.recording.append(recording) + self.session.open(MessageBox, _("Record time limited due to conflicting timer %s") % name_date, MessageBox.TYPE_INFO) + else: + self.session.open(MessageBox, _("Couldn't record due to conflicting timer %s") % name, MessageBox.TYPE_INFO) + else: + self.session.open(MessageBox, _("Couldn't record due to invalid service %s") % serviceref, MessageBox.TYPE_INFO) + recording.autoincrease = False def isInstantRecordRunning(self): print "self.recording:", self.recording @@ -1687,126 +1691,11 @@ class InfoBarAudioSelection: }) def audioSelection(self): - service = self.session.nav.getCurrentService() - self.audioTracks = audio = service and service.audioTracks() - n = audio and audio.getNumberOfTracks() or 0 - tlist = [] - if n > 0: - self.audioChannel = service.audioChannel() - - idx = 0 - while idx < n: - cnt = 0 - i = audio.getTrackInfo(idx) - languages = i.getLanguage().split('/') - description = i.getDescription() - language = "" - - for lang in languages: - if cnt: - language += ' / ' - if LanguageCodes.has_key(lang): - language += LanguageCodes[lang][0] - else: - language += lang - cnt += 1 - - if len(description): - description += " (" + language + ")" - else: - description = language - - tlist.append((description, idx)) - idx += 1 - - tlist.sort(key=lambda x: x[0]) - - selectedAudio = self.audioTracks.getCurrentTrack() - - selection = 0 - - for x in tlist: - if x[1] != selectedAudio: - selection += 1 - else: - break - - availableKeys = [] - usedKeys = [] - - if SystemInfo["CanDownmixAC3"]: - flist = [(_("AC3 downmix") + " - " +(_("Off"), _("On"))[config.av.downmix_ac3.value and 1 or 0], "CALLFUNC", self.changeAC3Downmix), - ((_("Left"), _("Stereo"), _("Right"))[self.audioChannel.getCurrentChannel()], "mode")] - usedKeys.extend(["red", "green"]) - availableKeys.extend(["yellow", "blue"]) - selection += 2 - else: - flist = [((_("Left"), _("Stereo"), _("Right"))[self.audioChannel.getCurrentChannel()], "mode")] - usedKeys.extend(["red"]) - availableKeys.extend(["green", "yellow", "blue"]) - selection += 1 - - if hasattr(self, "runPlugin"): - class PluginCaller: - def __init__(self, fnc, *args): - self.fnc = fnc - self.args = args - def __call__(self, *args, **kwargs): - self.fnc(*self.args) - - Plugins = [ (p.name, PluginCaller(self.runPlugin, p)) for p in plugins.getPlugins(where = PluginDescriptor.WHERE_AUDIOMENU) ] - - for p in Plugins: - selection += 1 - flist.append((p[0], "CALLFUNC", p[1])) - if availableKeys: - usedKeys.append(availableKeys[0]) - del availableKeys[0] - else: - usedKeys.append("") - - flist.append(("--", "")) - usedKeys.append("") - selection += 1 - - keys = usedKeys + [ "1", "2", "3", "4", "5", "6", "7", "8", "9", "0" ] + [""] * n - self.session.openWithCallback(self.audioSelected, ChoiceBox, title=_("Select audio track"), list = flist + tlist, selection = selection, keys = keys, skin_name = "AudioTrackSelection") - else: - del self.audioTracks - - def changeAC3Downmix(self, arg): - choicelist = self.session.current_dialog["list"] - list = choicelist.list - t = list[0][1] - list[0][1]=(t[0], t[1], t[2], t[3], t[4], t[5], t[6], - _("AC3 downmix") + " - " + (_("On"), _("Off"))[config.av.downmix_ac3.value and 1 or 0]) - choicelist.setList(list) - if config.av.downmix_ac3.value: - config.av.downmix_ac3.value = False - else: - config.av.downmix_ac3.value = True - config.av.downmix_ac3.save() - - def audioSelected(self, audio): - if audio is not None: - if isinstance(audio[1], str): - if audio[1] == "mode": - keys = ["red", "green", "yellow"] - selection = self.audioChannel.getCurrentChannel() - tlist = ((_("left"), 0), (_("stereo"), 1), (_("right"), 2)) - self.session.openWithCallback(self.modeSelected, ChoiceBox, title=_("Select audio mode"), list = tlist, selection = selection, keys = keys, skin_name ="AudioModeSelection") - else: - del self.audioChannel - if self.session.nav.getCurrentService().audioTracks().getNumberOfTracks() > audio[1]: - self.audioTracks.selectTrack(audio[1]) - else: - del self.audioChannel - del self.audioTracks - - def modeSelected(self, mode): - if mode is not None: - self.audioChannel.selectChannel(mode[1]) - del self.audioChannel + from Screens.AudioSelection import AudioSelection + self.session.openWithCallback(self.audioSelected, AudioSelection, infobar=self) + + def audioSelected(self, ret=None): + print "[infobar::audioSelected]", ret class InfoBarSubserviceSelection: def __init__(self): @@ -2099,20 +1988,21 @@ class InfoBarCueSheetSupport: return True def jumpPreviousMark(self): - # we add 2 seconds, so if the play position is <2s after + # we add 5 seconds, so if the play position is <5s after # the mark, the mark before will be used self.jumpPreviousNextMark(lambda x: -x-5*90000, start=True) def jumpNextMark(self): - if not self.jumpPreviousNextMark(lambda x: x): + if not self.jumpPreviousNextMark(lambda x: x-90000): self.doSeek(-1) def getNearestCutPoint(self, pts, cmp=abs, start=False): # can be optimized - beforecut = False + beforecut = True nearest = None + bestdiff = -1 + instate = True if start: - beforecut = True bestdiff = cmp(0 - pts) if bestdiff >= 0: nearest = [0, False] @@ -2121,14 +2011,19 @@ class InfoBarCueSheetSupport: beforecut = False if cp[1] == self.CUT_TYPE_IN: # Start is here, disregard previous marks diff = cmp(cp[0] - pts) - if diff >= 0: + if start and diff >= 0: nearest = cp bestdiff = diff else: nearest = None - if cp[1] in (self.CUT_TYPE_MARK, self.CUT_TYPE_LAST): + bestdiff = -1 + if cp[1] == self.CUT_TYPE_IN: + instate = True + elif cp[1] == self.CUT_TYPE_OUT: + instate = False + elif cp[1] in (self.CUT_TYPE_MARK, self.CUT_TYPE_LAST): diff = cmp(cp[0] - pts) - if diff >= 0 and (nearest is None or bestdiff > diff): + if instate and diff >= 0 and (nearest is None or bestdiff > diff): nearest = cp bestdiff = diff return nearest diff --git a/lib/python/Screens/InputBox.py b/lib/python/Screens/InputBox.py index 61ce356a..7dd7b127 100644 --- a/lib/python/Screens/InputBox.py +++ b/lib/python/Screens/InputBox.py @@ -41,9 +41,11 @@ class InputBox(Screen): "9": self.keyNumberGlobal, "0": self.keyNumberGlobal }, -1) + if self["input"].type == Input.TEXT: - rcinput = eRCInput.getInstance() - rcinput.setKeyboardMode(rcinput.kmAscii) + self.onExecBegin.append(self.setKeyboardModeAscii) + else: + self.onExecBegin.append(self.setKeyboardModeNone) def gotAsciiCode(self): self["input"].handleAscii(getPrevAsciiCode()) @@ -61,13 +63,9 @@ class InputBox(Screen): self["input"].delete() def go(self): - rcinput = eRCInput.getInstance() - rcinput.setKeyboardMode(rcinput.kmNone) self.close(self["input"].getText()) def cancel(self): - rcinput = eRCInput.getInstance() - rcinput.setKeyboardMode(rcinput.kmNone) self.close(None) def keyHome(self): @@ -142,19 +140,13 @@ class PinInput(InputBox): pass def closePinWrong(self, *args): - rcinput = eRCInput.getInstance() - rcinput.setKeyboardMode(rcinput.kmNone) print "args:", args self.close(False) def closePinCorrect(self, *args): - rcinput = eRCInput.getInstance() - rcinput.setKeyboardMode(rcinput.kmNone) self.close(True) def closePinCancel(self, *args): - rcinput = eRCInput.getInstance() - rcinput.setKeyboardMode(rcinput.kmNone) self.close(None) def cancel(self): diff --git a/lib/python/Screens/InputDeviceSetup.py b/lib/python/Screens/InputDeviceSetup.py new file mode 100755 index 00000000..bd8550b8 --- /dev/null +++ b/lib/python/Screens/InputDeviceSetup.py @@ -0,0 +1,280 @@ +from Screen import Screen +from Screens.HelpMenu import HelpableScreen +from Screens.MessageBox import MessageBox +from Components.InputDevice import iInputDevices +from Components.Sources.StaticText import StaticText +from Components.Sources.Boolean import Boolean +from Components.Sources.List import List +from Components.config import config, ConfigSlider, ConfigSubsection, ConfigYesNo, ConfigText, getConfigListEntry, ConfigNothing +from Components.ConfigList import ConfigListScreen +from Components.ActionMap import ActionMap, NumberActionMap, HelpableActionMap +from Tools.Directories import resolveFilename, SCOPE_CURRENT_SKIN +from Tools.LoadPixmap import LoadPixmap + +class InputDeviceSelection(Screen,HelpableScreen): + skin = """ + <screen name="InputDeviceSelection" position="center,center" size="560,400" title="Select input device"> + <ePixmap pixmap="skin_default/buttons/red.png" position="0,0" size="140,40" alphatest="on"/> + <ePixmap pixmap="skin_default/buttons/green.png" position="140,0" size="140,40" alphatest="on"/> + <ePixmap pixmap="skin_default/buttons/yellow.png" position="280,0" size="140,40" alphatest="on"/> + <ePixmap pixmap="skin_default/buttons/blue.png" position="420,0" size="140,40" alphatest="on"/> + <widget source="key_red" render="Label" position="0,0" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#9f1313" transparent="1"/> + <widget source="key_green" render="Label" position="140,0" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#1f771f" transparent="1"/> + <widget source="key_yellow" render="Label" position="280,0" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#a08500" transparent="1"/> + <widget source="key_blue" render="Label" position="420,0" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#18188b" transparent="1"/> + <widget source="list" render="Listbox" position="5,50" size="550,280" zPosition="10" scrollbarMode="showOnDemand"> + <convert type="TemplatedMultiContent"> + <!-- device, description, devicepng, divpng --> + {"template": [ + MultiContentEntryPixmapAlphaTest(pos = (2, 8), size = (54, 54), png = 2), # index 3 is the interface pixmap + MultiContentEntryText(pos = (65, 6), size = (450, 54), font=0, flags = RT_HALIGN_LEFT|RT_VALIGN_CENTER|RT_WRAP, text = 1), # index 1 is the interfacename + ], + "fonts": [gFont("Regular", 28),gFont("Regular", 20)], + "itemHeight": 70 + } + + </convert> + </widget> + <ePixmap pixmap="skin_default/div-h.png" position="0,340" zPosition="1" size="560,2"/> + <widget source="introduction" render="Label" position="0,350" size="560,50" zPosition="10" font="Regular;21" halign="center" valign="center" backgroundColor="#25062748" transparent="1"/> + </screen>""" + + + def __init__(self, session): + Screen.__init__(self, session) + HelpableScreen.__init__(self) + + self.edittext = _("Press OK to edit the settings.") + + self["key_red"] = StaticText(_("Close")) + self["key_green"] = StaticText(_("Select")) + self["key_yellow"] = StaticText("") + self["key_blue"] = StaticText("") + self["introduction"] = StaticText(self.edittext) + + self.devices = [(iInputDevices.getDeviceName(x),x) for x in iInputDevices.getDeviceList()] + print "[InputDeviceSelection] found devices :->", len(self.devices),self.devices + + self["OkCancelActions"] = HelpableActionMap(self, "OkCancelActions", + { + "cancel": (self.close, _("Exit input device selection.")), + "ok": (self.okbuttonClick, _("Select input device.")), + }, -2) + + self["ColorActions"] = HelpableActionMap(self, "ColorActions", + { + "red": (self.close, _("Exit input device selection.")), + "green": (self.okbuttonClick, _("Select input device.")), + }, -2) + + self.currentIndex = 0 + self.list = [] + self["list"] = List(self.list) + self.updateList() + self.onLayoutFinish.append(self.layoutFinished) + self.onClose.append(self.cleanup) + + def layoutFinished(self): + self.setTitle(_("Select input device")) + + def cleanup(self): + self.currentIndex = 0 + + def buildInterfaceList(self,device,description,type ): + divpng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_CURRENT_SKIN, "skin_default/div-h.png")) + activepng = None + devicepng = None + enabled = iInputDevices.getDeviceAttribute(device, 'enabled') + + if type == 'remote': + if config.misc.rcused.value == 0: + if enabled: + devicepng = LoadPixmap(resolveFilename(SCOPE_CURRENT_SKIN, "skin_default/icons/input_rcnew-configured.png")) + else: + devicepng = LoadPixmap(resolveFilename(SCOPE_CURRENT_SKIN, "skin_default/icons/input_rcnew.png")) + else: + if enabled: + devicepng = LoadPixmap(resolveFilename(SCOPE_CURRENT_SKIN, "skin_default/icons/input_rcold-configured.png")) + else: + devicepng = LoadPixmap(resolveFilename(SCOPE_CURRENT_SKIN, "skin_default/icons/input_rcold.png")) + elif type == 'keyboard': + if enabled: + devicepng = LoadPixmap(resolveFilename(SCOPE_CURRENT_SKIN, "skin_default/icons/input_keyboard-configured.png")) + else: + devicepng = LoadPixmap(resolveFilename(SCOPE_CURRENT_SKIN, "skin_default/icons/input_keyboard.png")) + elif type == 'mouse': + if enabled: + devicepng = LoadPixmap(resolveFilename(SCOPE_CURRENT_SKIN, "skin_default/icons/input_mouse-configured.png")) + else: + devicepng = LoadPixmap(resolveFilename(SCOPE_CURRENT_SKIN, "skin_default/icons/input_mouse.png")) + else: + devicepng = LoadPixmap(resolveFilename(SCOPE_CURRENT_SKIN, "skin_default/icons/input_rcnew.png")) + return((device, description, devicepng, divpng)) + + def updateList(self): + self.list = [] + for x in self.devices: + dev_type = iInputDevices.getDeviceAttribute(x[1], 'type') + self.list.append(self.buildInterfaceList(x[1],_(x[0]), dev_type )) + self["list"].setList(self.list) + self["list"].setIndex(self.currentIndex) + + def okbuttonClick(self): + selection = self["list"].getCurrent() + self.currentIndex = self["list"].getIndex() + if selection is not None: + self.session.openWithCallback(self.DeviceSetupClosed, InputDeviceSetup, selection[0]) + + def DeviceSetupClosed(self, *ret): + self.updateList() + + +class InputDeviceSetup(Screen, ConfigListScreen): + + skin = """ + <screen name="InputDeviceSetup" position="center,center" size="560,440" title="Input device setup"> + <ePixmap pixmap="skin_default/buttons/red.png" position="0,0" size="140,40" alphatest="on" /> + <ePixmap pixmap="skin_default/buttons/green.png" position="140,0" size="140,40" alphatest="on" /> + <ePixmap pixmap="skin_default/buttons/yellow.png" position="280,0" size="140,40" alphatest="on" /> + <ePixmap pixmap="skin_default/buttons/blue.png" position="420,0" size="140,40" alphatest="on" /> + <widget source="key_red" render="Label" position="0,0" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#9f1313" transparent="1" /> + <widget source="key_green" render="Label" position="140,0" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#1f771f" transparent="1" /> + <widget source="key_yellow" render="Label" position="280,0" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#a08500" transparent="1" /> + <widget source="key_blue" render="Label" position="420,0" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#18188b" transparent="1" /> + <widget name="config" position="5,50" size="550,350" scrollbarMode="showOnDemand" /> + <ePixmap pixmap="skin_default/div-h.png" position="0,400" zPosition="1" size="560,2" /> + <widget source="introduction" render="Label" position="5,410" size="550,30" zPosition="10" font="Regular;21" halign="center" valign="center" backgroundColor="#25062748" transparent="1" /> + </screen>""" + + def __init__(self, session, device): + Screen.__init__(self, session) + self.inputDevice = device + iInputDevices.currentDevice = self.inputDevice + self.onChangedEntry = [ ] + self.setup_title = _("Input device setup") + self.isStepSlider = None + self.enableEntry = None + self.repeatEntry = None + self.delayEntry = None + self.nameEntry = None + self.enableConfigEntry = None + + self.list = [ ] + ConfigListScreen.__init__(self, self.list, session = session, on_change = self.changedEntry) + + self["actions"] = ActionMap(["SetupActions"], + { + "cancel": self.keyCancel, + "save": self.apply, + }, -2) + + self["key_red"] = StaticText(_("Cancel")) + self["key_green"] = StaticText(_("OK")) + self["key_yellow"] = StaticText() + self["key_blue"] = StaticText() + self["introduction"] = StaticText() + + self.createSetup() + self.onLayoutFinish.append(self.layoutFinished) + self.onClose.append(self.cleanup) + + def layoutFinished(self): + self.setTitle(self.setup_title) + + def cleanup(self): + iInputDevices.currentDevice = "" + + def createSetup(self): + self.list = [ ] + cmd = "self.enableEntry = getConfigListEntry(_('"'Change repeat and delay settings?'"'), config.inputDevices." + self.inputDevice + ".enabled)" + exec (cmd) + cmd = "self.repeatEntry = getConfigListEntry(_('"'Interval between keys when repeating:'"'), config.inputDevices." + self.inputDevice + ".repeat)" + exec (cmd) + cmd = "self.delayEntry = getConfigListEntry(_('"'Delay before key repeat starts:'"'), config.inputDevices." + self.inputDevice + ".delay)" + exec (cmd) + cmd = "self.nameEntry = getConfigListEntry(_('"'Devicename:'"'), config.inputDevices." + self.inputDevice + ".name)" + exec (cmd) + if self.enableEntry: + if isinstance(self.enableEntry[1], ConfigYesNo): + self.enableConfigEntry = self.enableEntry[1] + + self.list.append(self.enableEntry) + if self.enableConfigEntry: + if self.enableConfigEntry.value is True: + self.list.append(self.repeatEntry) + self.list.append(self.delayEntry) + else: + self.repeatEntry[1].setValue(self.repeatEntry[1].default) + self["config"].invalidate(self.repeatEntry) + self.delayEntry[1].setValue(self.delayEntry[1].default) + self["config"].invalidate(self.delayEntry) + self.nameEntry[1].setValue(self.nameEntry[1].default) + self["config"].invalidate(self.nameEntry) + + self["config"].list = self.list + self["config"].l.setSeperation(400) + self["config"].l.setList(self.list) + if not self.selectionChanged in self["config"].onSelectionChanged: + self["config"].onSelectionChanged.append(self.selectionChanged) + self.selectionChanged() + + def selectionChanged(self): + if self["config"].getCurrent() == self.enableEntry: + self["introduction"].setText(_("Current device: ") + str(iInputDevices.getDeviceAttribute(self.inputDevice, 'name')) ) + else: + self["introduction"].setText(_("Current value: ") + self.getCurrentValue() + _(" ms")) + + def newConfig(self): + current = self["config"].getCurrent() + if current: + if current == self.enableEntry: + self.createSetup() + + def keyLeft(self): + ConfigListScreen.keyLeft(self) + self.newConfig() + + def keyRight(self): + ConfigListScreen.keyRight(self) + self.newConfig() + + def confirm(self, confirmed): + if not confirmed: + print "not confirmed" + return + else: + self.nameEntry[1].setValue(iInputDevices.getDeviceAttribute(self.inputDevice, 'name')) + cmd = "config.inputDevices." + self.inputDevice + ".name.save()" + exec (cmd) + self.keySave() + + def apply(self): + self.session.openWithCallback(self.confirm, MessageBox, _("Use this input device settings?"), MessageBox.TYPE_YESNO, timeout = 20, default = True) + + def cancelConfirm(self, result): + if not result: + return + for x in self["config"].list: + x[1].cancel() + self.close() + + def keyCancel(self): + if self["config"].isChanged(): + self.session.openWithCallback(self.cancelConfirm, MessageBox, _("Really close without saving settings?"), MessageBox.TYPE_YESNO, timeout = 20, default = True) + else: + self.close() + # for summary: + def changedEntry(self): + for x in self.onChangedEntry: + x() + self.selectionChanged() + + def getCurrentEntry(self): + return self["config"].getCurrent()[0] + + def getCurrentValue(self): + return str(self["config"].getCurrent()[1].value) + + def createSummary(self): + from Screens.Setup import SetupSummary + return SetupSummary diff --git a/lib/python/Screens/Ipkg.py b/lib/python/Screens/Ipkg.py index 96876336..7f98f3df 100755 --- a/lib/python/Screens/Ipkg.py +++ b/lib/python/Screens/Ipkg.py @@ -25,6 +25,7 @@ class Ipkg(Screen): self.packages = 0 self.error = 0 + self.processed_packages = [] self.activity = 0 self.activityTimer = eTimer() @@ -85,15 +86,21 @@ class Ipkg(Screen): self.slider.setValue(self.sliderPackages[param]) self.package.setText(param) self.status.setText(_("Upgrading")) - self.packages += 1 + if not param in self.processed_packages: + self.processed_packages.append(param) + self.packages += 1 elif event == IpkgComponent.EVENT_INSTALL: self.package.setText(param) self.status.setText(_("Installing")) - self.packages += 1 + if not param in self.processed_packages: + self.processed_packages.append(param) + self.packages += 1 elif event == IpkgComponent.EVENT_REMOVE: self.package.setText(param) self.status.setText(_("Removing")) - self.packages += 1 + if not param in self.processed_packages: + self.processed_packages.append(param) + self.packages += 1 elif event == IpkgComponent.EVENT_CONFIGURING: self.package.setText(param) self.status.setText(_("Configuring")) @@ -103,10 +110,10 @@ class Ipkg(Screen): self.runNextCmd() elif event == IpkgComponent.EVENT_MODIFIED: self.session.openWithCallback( - self.modificationCallback, - MessageBox, - _("A configuration file (%s) was modified since Installation.\nDo you want to keep your version?") % (param) - ) + self.modificationCallback, + MessageBox, + _("A configuration file (%s) was modified since Installation.\nDo you want to keep your version?") % (param) + ) def modificationCallback(self, res): self.ipkg.write(res and "N" or "Y") diff --git a/lib/python/Screens/Makefile.am b/lib/python/Screens/Makefile.am index d96b491e..69600f01 100755 --- a/lib/python/Screens/Makefile.am +++ b/lib/python/Screens/Makefile.am @@ -1,18 +1,19 @@ installdir = $(pkglibdir)/python/Screens install_PYTHON = \ - ChannelSelection.py ClockDisplay.py ConfigMenu.py InfoBar.py Menu.py \ - MessageBox.py Screen.py ServiceScan.py TimerEdit.py \ + AudioSelection.py ChannelSelection.py ClockDisplay.py ConfigMenu.py InfoBar.py \ + Menu.py MessageBox.py Screen.py ServiceScan.py TimerEdit.py \ MovieSelection.py Setup.py About.py HarddiskSetup.py FixedMenu.py \ Satconfig.py ScanSetup.py NetworkSetup.py Ci.py TimerEntry.py Volume.py \ EpgSelection.py EventView.py Mute.py Standby.py ServiceInfo.py \ InfoBarGenerics.py HelpMenu.py Wizard.py __init__.py \ - Dish.py LanguageSelection.py StartWizard.py PiPSetup.py Subtitles.py \ + Dish.py LanguageSelection.py StartWizard.py PiPSetup.py \ TutorialWizard.py PluginBrowser.py MinuteInput.py Scart.py PVRState.py \ Console.py InputBox.py ChoiceBox.py SimpleSummary.py \ TimerSelection.py PictureInPicture.py TimeDateInput.py \ SubtitleDisplay.py SubservicesQuickzap.py ParentalControlSetup.py NumericalTextInputHelpDialog.py \ SleepTimerEdit.py Ipkg.py RdsDisplay.py Globals.py DefaultWizard.py \ SessionGlobals.py LocationBox.py WizardLanguage.py TaskView.py Rc.py VirtualKeyBoard.py \ - TextBox.py FactoryReset.py RecordPaths.py UnhandledKey.py ServiceStopScreen.py + TextBox.py FactoryReset.py RecordPaths.py UnhandledKey.py ServiceStopScreen.py \ + InputDeviceSetup.py diff --git a/lib/python/Screens/NetworkSetup.py b/lib/python/Screens/NetworkSetup.py index c0037f81..4b15ee00 100755 --- a/lib/python/Screens/NetworkSetup.py +++ b/lib/python/Screens/NetworkSetup.py @@ -7,10 +7,11 @@ from Screens.HelpMenu import HelpableScreen from Components.Network import iNetwork from Components.Sources.StaticText import StaticText from Components.Sources.Boolean import Boolean +from Components.Sources.List import List from Components.Label import Label,MultiColorLabel from Components.Pixmap import Pixmap,MultiPixmap from Components.MenuList import MenuList -from Components.config import config, ConfigYesNo, ConfigIP, NoSave, ConfigText, ConfigPassword, ConfigSelection, getConfigListEntry, ConfigNothing +from Components.config import config, ConfigYesNo, ConfigIP, NoSave, ConfigText, ConfigPassword, ConfigSelection, getConfigListEntry, ConfigNothing, ConfigBoolean from Components.ConfigList import ConfigListScreen from Components.PluginComponent import plugins from Components.MultiContent import MultiContentEntryText, MultiContentEntryPixmapAlphaTest @@ -23,32 +24,6 @@ from os import path as os_path, system as os_system, unlink from re import compile as re_compile, search as re_search -class InterfaceList(MenuList): - def __init__(self, list, enableWrapAround=False): - MenuList.__init__(self, list, enableWrapAround, eListboxPythonMultiContent) - self.l.setFont(0, gFont("Regular", 20)) - self.l.setItemHeight(30) - -def InterfaceEntryComponent(index,name,default,active ): - res = [ - (index), - MultiContentEntryText(pos=(80, 5), size=(430, 25), font=0, text=name) - ] - num_configured_if = len(iNetwork.getConfiguredAdapters()) - if num_configured_if >= 2: - if default is True: - png = LoadPixmap(cached=True, path=resolveFilename(SCOPE_CURRENT_SKIN, "skin_default/buttons/button_blue.png")) - if default is False: - png = LoadPixmap(cached=True, path=resolveFilename(SCOPE_CURRENT_SKIN, "skin_default/buttons/button_blue_off.png")) - res.append(MultiContentEntryPixmapAlphaTest(pos=(10, 5), size=(25, 25), png = png)) - if active is True: - png2 = LoadPixmap(cached=True, path=resolveFilename(SCOPE_CURRENT_SKIN, "skin_default/icons/lock_on.png")) - if active is False: - png2 = LoadPixmap(cached=True, path=resolveFilename(SCOPE_CURRENT_SKIN, "skin_default/icons/lock_error.png")) - res.append(MultiContentEntryPixmapAlphaTest(pos=(40, 1), size=(25, 25), png = png2)) - return res - - class NetworkAdapterSelection(Screen,HelpableScreen): def __init__(self, session): Screen.__init__(self, session) @@ -67,11 +42,6 @@ class NetworkAdapterSelection(Screen,HelpableScreen): self["key_blue"] = StaticText("") self["introduction"] = StaticText(self.edittext) - self.adapters = [(iNetwork.getFriendlyAdapterName(x),x) for x in iNetwork.getAdapterList()] - - if not self.adapters: - self.onFirstExecBegin.append(self.NetworkFallback) - self["OkCancelActions"] = HelpableActionMap(self, "OkCancelActions", { "cancel": (self.close, _("exit network interface list")), @@ -90,14 +60,58 @@ class NetworkAdapterSelection(Screen,HelpableScreen): "yellow": (self.setDefaultInterface, [_("Set interface as default Interface"),_("* Only available if more than one interface is active.")] ), }) + self.adapters = [(iNetwork.getFriendlyAdapterName(x),x) for x in iNetwork.getAdapterList()] + + if not self.adapters: + self.adapters = [(iNetwork.getFriendlyAdapterName(x),x) for x in iNetwork.getConfiguredAdapters()] + + if len(self.adapters) == 0: + self.adapters = [(iNetwork.getFriendlyAdapterName(x),x) for x in iNetwork.getInstalledAdapters()] + self.list = [] - self["list"] = InterfaceList(self.list) + self["list"] = List(self.list) self.updateList() if len(self.adapters) == 1: self.onFirstExecBegin.append(self.okbuttonClick) self.onClose.append(self.cleanup) + def buildInterfaceList(self,iface,name,default,active ): + divpng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_CURRENT_SKIN, "skin_default/div-h.png")) + defaultpng = None + activepng = None + description = None + interfacepng = None + + if not iNetwork.isWirelessInterface(iface): + if active is True: + interfacepng = LoadPixmap(resolveFilename(SCOPE_CURRENT_SKIN, "skin_default/icons/network_wired-active.png")) + elif active is False: + interfacepng = LoadPixmap(resolveFilename(SCOPE_CURRENT_SKIN, "skin_default/icons/network_wired-inactive.png")) + else: + interfacepng = LoadPixmap(resolveFilename(SCOPE_CURRENT_SKIN, "skin_default/icons/network_wired.png")) + elif iNetwork.isWirelessInterface(iface): + if active is True: + interfacepng = LoadPixmap(resolveFilename(SCOPE_CURRENT_SKIN, "skin_default/icons/network_wireless-active.png")) + elif active is False: + interfacepng = LoadPixmap(resolveFilename(SCOPE_CURRENT_SKIN, "skin_default/icons/network_wireless-inactive.png")) + else: + interfacepng = LoadPixmap(resolveFilename(SCOPE_CURRENT_SKIN, "skin_default/icons/network_wireless.png")) + + num_configured_if = len(iNetwork.getConfiguredAdapters()) + if num_configured_if >= 2: + if default is True: + defaultpng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_CURRENT_SKIN, "skin_default/buttons/button_blue.png")) + elif default is False: + defaultpng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_CURRENT_SKIN, "skin_default/buttons/button_blue_off.png")) + if active is True: + activepng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_CURRENT_SKIN, "skin_default/icons/lock_on.png")) + elif active is False: + activepng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_CURRENT_SKIN, "skin_default/icons/lock_error.png")) + + description = iNetwork.getFriendlyAdapterDescription(iface) + + return((iface, name, description, interfacepng, defaultpng, activepng, divpng)) def updateList(self): self.list = [] @@ -121,23 +135,20 @@ class NetworkAdapterSelection(Screen,HelpableScreen): fp.close() default_gw = result - if len(self.adapters) == 0: # no interface available => display only eth0 - self.list.append(InterfaceEntryComponent("eth0",iNetwork.getFriendlyAdapterName('eth0'),True,True )) - else: - for x in self.adapters: - if x[1] == default_gw: - default_int = True - else: - default_int = False - if iNetwork.getAdapterAttribute(x[1], 'up') is True: - active_int = True - else: - active_int = False - self.list.append(InterfaceEntryComponent(index = x[1],name = _(x[0]),default=default_int,active=active_int )) + for x in self.adapters: + if x[1] == default_gw: + default_int = True + else: + default_int = False + if iNetwork.getAdapterAttribute(x[1], 'up') is True: + active_int = True + else: + active_int = False + self.list.append(self.buildInterfaceList(x[1],_(x[0]),default_int,active_int )) if os_path.exists(resolveFilename(SCOPE_PLUGINS, "SystemPlugins/NetworkWizard/networkwizard.xml")): self["key_blue"].setText(_("NetworkWizard")) - self["list"].l.setList(self.list) + self["list"].setList(self.list) def setDefaultInterface(self): selection = self["list"].getCurrent() @@ -168,22 +179,6 @@ class NetworkAdapterSelection(Screen,HelpableScreen): else: self.updateList() - def NetworkFallback(self): - if iNetwork.configuredNetworkAdapters.has_key('wlan0') is True: - self.session.openWithCallback(self.ErrorMessageClosed, MessageBox, self.wlan_errortext, type = MessageBox.TYPE_INFO,timeout = 10) - if iNetwork.configuredNetworkAdapters.has_key('ath0') is True: - self.session.openWithCallback(self.ErrorMessageClosed, MessageBox, self.wlan_errortext, type = MessageBox.TYPE_INFO,timeout = 10) - else: - self.session.openWithCallback(self.ErrorMessageClosed, MessageBox, self.lan_errortext, type = MessageBox.TYPE_INFO,timeout = 10) - - def ErrorMessageClosed(self, *ret): - if iNetwork.configuredNetworkAdapters.has_key('wlan0') is True: - self.session.openWithCallback(self.AdapterSetupClosed, AdapterSetupConfiguration, 'wlan0') - elif iNetwork.configuredNetworkAdapters.has_key('ath0') is True: - self.session.openWithCallback(self.AdapterSetupClosed, AdapterSetupConfiguration, 'ath0') - else: - self.session.openWithCallback(self.AdapterSetupClosed, AdapterSetupConfiguration, 'eth0') - def cleanup(self): iNetwork.stopLinkStateConsole() iNetwork.stopRestartConsole() @@ -253,7 +248,7 @@ class NameserverSetup(Screen, ConfigListScreen, HelpableScreen): self.list = [] ConfigListScreen.__init__(self, self.list) self.createSetup() - + def createConfig(self): self.nameservers = iNetwork.getNameserverList() self.nameserverEntries = [ NoSave(ConfigIP(default=nameserver)) for nameserver in self.nameservers] @@ -301,18 +296,17 @@ class NameserverSetup(Screen, ConfigListScreen, HelpableScreen): class AdapterSetup(Screen, ConfigListScreen, HelpableScreen): - def __init__(self, session, networkinfo, essid=None, aplist=None): + def __init__(self, session, networkinfo, essid=None): Screen.__init__(self, session) HelpableScreen.__init__(self) self.session = session if isinstance(networkinfo, (list, tuple)): self.iface = networkinfo[0] self.essid = networkinfo[1] - self.aplist = networkinfo[2] else: self.iface = networkinfo self.essid = essid - self.aplist = aplist + self.extended = None self.applyConfigRef = None self.finished_cb = None @@ -403,62 +397,36 @@ class AdapterSetup(Screen, ConfigListScreen, HelpableScreen): self.gatewayEntry = None self.hiddenSSID = None self.wlanSSID = None - self.encryptionEnabled = None - self.encryptionKey = None + self.encryption = None self.encryptionType = None - self.nwlist = None + self.encryptionKey = None self.encryptionlist = None self.weplist = None self.wsconfig = None self.default = None - if self.iface == "wlan0" or self.iface == "ath0" : - from Plugins.SystemPlugins.WirelessLan.Wlan import wpaSupplicant,Wlan - self.w = Wlan(self.iface) + if iNetwork.isWirelessInterface(self.iface): + from Plugins.SystemPlugins.WirelessLan.Wlan import wpaSupplicant self.ws = wpaSupplicant() self.encryptionlist = [] + self.encryptionlist.append(("Unencrypted", _("Unencrypted"))) self.encryptionlist.append(("WEP", _("WEP"))) self.encryptionlist.append(("WPA", _("WPA"))) - self.encryptionlist.append(("WPA2", _("WPA2"))) self.encryptionlist.append(("WPA/WPA2", _("WPA or WPA2"))) + self.encryptionlist.append(("WPA2", _("WPA2"))) self.weplist = [] self.weplist.append("ASCII") self.weplist.append("HEX") - if self.aplist is not None: - self.nwlist = self.aplist - self.nwlist.sort(key = lambda x: x[0]) - else: - self.nwlist = [] - self.aps = None - try: - self.aps = self.w.getNetworkList() - if self.aps is not None: - for ap in self.aps: - a = self.aps[ap] - if a['active']: - if a['essid'] != '': - self.nwlist.append((a['essid'],a['essid'])) - self.nwlist.sort(key = lambda x: x[0]) - except: - self.nwlist.append(("No Networks found",_("No Networks found"))) - - self.wsconfig = self.ws.loadConfig() - if self.essid is not None: # ssid from wlan scan - self.default = self.essid - else: - self.default = self.wsconfig['ssid'] - if "hidden..." not in self.nwlist: - self.nwlist.append(("hidden...",_("enter hidden network SSID"))) - if self.default not in self.nwlist: - self.nwlist.append((self.default,self.default)) - config.plugins.wlan.essid = NoSave(ConfigSelection(self.nwlist, default = self.default )) - config.plugins.wlan.hiddenessid = NoSave(ConfigText(default = self.wsconfig['hiddenessid'], visible_width = 50, fixed_size = False)) + self.wsconfig = self.ws.loadConfig(self.iface) + if self.essid is None: + self.essid = self.wsconfig['ssid'] - config.plugins.wlan.encryption.enabled = NoSave(ConfigYesNo(default = self.wsconfig['encryption'] )) - config.plugins.wlan.encryption.type = NoSave(ConfigSelection(self.encryptionlist, default = self.wsconfig['encryption_type'] )) - config.plugins.wlan.encryption.wepkeytype = NoSave(ConfigSelection(self.weplist, default = self.wsconfig['encryption_wepkeytype'] )) - config.plugins.wlan.encryption.psk = NoSave(ConfigPassword(default = self.wsconfig['key'], visible_width = 50, fixed_size = False)) + config.plugins.wlan.hiddenessid = NoSave(ConfigYesNo(default = self.wsconfig['hiddenessid'])) + config.plugins.wlan.essid = NoSave(ConfigText(default = self.essid, visible_width = 50, fixed_size = False)) + config.plugins.wlan.encryption = NoSave(ConfigSelection(self.encryptionlist, default = self.wsconfig['encryption'] )) + config.plugins.wlan.wepkeytype = NoSave(ConfigSelection(self.weplist, default = self.wsconfig['wepkeytype'] )) + config.plugins.wlan.psk = NoSave(ConfigPassword(default = self.wsconfig['key'], visible_width = 50, fixed_size = False)) self.activateInterfaceEntry = NoSave(ConfigYesNo(default=iNetwork.getAdapterAttribute(self.iface, "up") or False)) self.dhcpConfigEntry = NoSave(ConfigYesNo(default=iNetwork.getAdapterAttribute(self.iface, "dhcp") or False)) @@ -491,6 +459,7 @@ class AdapterSetup(Screen, ConfigListScreen, HelpableScreen): self.list.append(getConfigListEntry(_('Gateway'), self.gatewayConfigEntry)) self.extended = None + self.configStrings = None for p in plugins.getPlugins(PluginDescriptor.WHERE_NETWORKSETUP): callFnc = p.__call__["ifaceSupported"](self.iface) if callFnc is not None: @@ -498,30 +467,21 @@ class AdapterSetup(Screen, ConfigListScreen, HelpableScreen): self.extended = callFnc if p.__call__.has_key("configStrings"): self.configStrings = p.__call__["configStrings"] - else: - self.configStrings = None - if config.plugins.wlan.essid.value == 'hidden...': - self.wlanSSID = getConfigListEntry(_("Network SSID"), config.plugins.wlan.essid) - self.list.append(self.wlanSSID) - self.hiddenSSID = getConfigListEntry(_("Hidden network SSID"), config.plugins.wlan.hiddenessid) - self.list.append(self.hiddenSSID) - else: - self.wlanSSID = getConfigListEntry(_("Network SSID"), config.plugins.wlan.essid) - self.list.append(self.wlanSSID) - self.encryptionEnabled = getConfigListEntry(_("Encryption"), config.plugins.wlan.encryption.enabled) - self.list.append(self.encryptionEnabled) - - if config.plugins.wlan.encryption.enabled.value: - self.encryptionType = getConfigListEntry(_("Encryption Type"), config.plugins.wlan.encryption.type) - self.list.append(self.encryptionType) - if config.plugins.wlan.encryption.type.value == 'WEP': - self.list.append(getConfigListEntry(_("Encryption Keytype"), config.plugins.wlan.encryption.wepkeytype)) - self.encryptionKey = getConfigListEntry(_("Encryption Key"), config.plugins.wlan.encryption.psk) - self.list.append(self.encryptionKey) - else: - self.encryptionKey = getConfigListEntry(_("Encryption Key"), config.plugins.wlan.encryption.psk) - self.list.append(self.encryptionKey) + self.hiddenSSID = getConfigListEntry(_("Hidden network"), config.plugins.wlan.hiddenessid) + self.list.append(self.hiddenSSID) + self.wlanSSID = getConfigListEntry(_("Networkname (SSID)"), config.plugins.wlan.essid) + self.list.append(self.wlanSSID) + self.encryption = getConfigListEntry(_("Encryption"), config.plugins.wlan.encryption) + self.list.append(self.encryption) + + self.encryptionType = getConfigListEntry(_("Encryption Keytype"), config.plugins.wlan.wepkeytype) + self.encryptionKey = getConfigListEntry(_("Encryption Key"), config.plugins.wlan.psk) + + if config.plugins.wlan.encryption.value != "Unencrypted": + if config.plugins.wlan.encryption.value == 'WEP': + self.list.append(self.encryptionType) + self.list.append(self.encryptionKey) self["config"].list = self.list self["config"].l.setList(self.list) @@ -535,12 +495,8 @@ class AdapterSetup(Screen, ConfigListScreen, HelpableScreen): self.createSetup() if self["config"].getCurrent() == self.gatewayEntry: self.createSetup() - if self.iface == "wlan0" or self.iface == "ath0" : - if self["config"].getCurrent() == self.wlanSSID: - self.createSetup() - if self["config"].getCurrent() == self.encryptionEnabled: - self.createSetup() - if self["config"].getCurrent() == self.encryptionType: + if iNetwork.isWirelessInterface(self.iface): + if self["config"].getCurrent() == self.encryption: self.createSetup() def keyLeft(self): @@ -565,7 +521,7 @@ class AdapterSetup(Screen, ConfigListScreen, HelpableScreen): if (ret == True): num_configured_if = len(iNetwork.getConfiguredAdapters()) if num_configured_if >= 1: - if num_configured_if == 1 and self.iface in iNetwork.getConfiguredAdapters(): + if self.iface in iNetwork.getConfiguredAdapters(): self.applyConfig(True) else: self.session.openWithCallback(self.secondIfaceFoundCB, MessageBox, _("A second configured interface has been found.\n\nDo you want to disable the second network interface?"), default = True) @@ -583,11 +539,15 @@ class AdapterSetup(Screen, ConfigListScreen, HelpableScreen): if interface == self.iface: continue iNetwork.setAdapterAttribute(interface, "up", False) - iNetwork.deactivateInterface(interface) - self.applyConfig(True) + iNetwork.deactivateInterface(configuredInterfaces,self.deactivateSecondInterfaceCB) + + def deactivateSecondInterfaceCB(self, data): + if data is True: + self.applyConfig(True) def applyConfig(self, ret = False): if (ret == True): + self.applyConfigRef = None iNetwork.setAdapterAttribute(self.iface, "up", self.activateInterfaceEntry.value) iNetwork.setAdapterAttribute(self.iface, "dhcp", self.dhcpConfigEntry.value) iNetwork.setAdapterAttribute(self.iface, "ip", self.ipConfigEntry.value) @@ -596,17 +556,33 @@ class AdapterSetup(Screen, ConfigListScreen, HelpableScreen): iNetwork.setAdapterAttribute(self.iface, "gateway", self.gatewayConfigEntry.value) else: iNetwork.removeAdapterAttribute(self.iface, "gateway") - if self.extended is not None and self.configStrings is not None: + + if (self.extended is not None and self.configStrings is not None): iNetwork.setAdapterAttribute(self.iface, "configStrings", self.configStrings(self.iface)) - self.ws.writeConfig() + self.ws.writeConfig(self.iface) + if self.activateInterfaceEntry.value is False: - iNetwork.deactivateInterface(self.iface) - iNetwork.writeNetworkConfig() - iNetwork.restartNetwork(self.applyConfigDataAvail) - self.applyConfigRef = self.session.openWithCallback(self.applyConfigfinishedCB, MessageBox, _("Please wait for activation of your network configuration..."), type = MessageBox.TYPE_INFO, enable_input = False) + iNetwork.deactivateInterface(self.iface,self.deactivateInterfaceCB) + iNetwork.writeNetworkConfig() + self.applyConfigRef = self.session.openWithCallback(self.applyConfigfinishedCB, MessageBox, _("Please wait for activation of your network configuration..."), type = MessageBox.TYPE_INFO, enable_input = False) + else: + if self.oldInterfaceState is False: + iNetwork.activateInterface(self.iface,self.deactivateInterfaceCB) + else: + iNetwork.deactivateInterface(self.iface,self.activateInterfaceCB) + iNetwork.writeNetworkConfig() + self.applyConfigRef = self.session.openWithCallback(self.applyConfigfinishedCB, MessageBox, _("Please wait for activation of your network configuration..."), type = MessageBox.TYPE_INFO, enable_input = False) else: self.keyCancel() + def deactivateInterfaceCB(self, data): + if data is True: + self.applyConfigDataAvail(True) + + def activateInterfaceCB(self, data): + if data is True: + iNetwork.activateInterface(self.iface,self.applyConfigDataAvail) + def applyConfigDataAvail(self, data): if data is True: iNetwork.getInterfaces(self.getInterfacesDataAvail) @@ -664,10 +640,10 @@ class AdapterSetup(Screen, ConfigListScreen, HelpableScreen): def hideInputHelp(self): current = self["config"].getCurrent() - if current == self.hiddenSSID and config.plugins.wlan.essid.value == 'hidden...': + if current == self.wlanSSID: if current[1].help_window.instance is not None: current[1].help_window.instance.hide() - elif current == self.encryptionKey and config.plugins.wlan.encryption.enabled.value: + elif current == self.encryptionKey and config.plugins.wlan.encryption.value is not "Unencrypted": if current[1].help_window.instance is not None: current[1].help_window.instance.hide() @@ -692,7 +668,8 @@ class AdapterSetupConfiguration(Screen, HelpableScreen): self.oktext = _("Press OK on your remote control to continue.") self.reboottext = _("Your Dreambox will restart after pressing OK on your remote control.") - self.errortext = _("No working wireless network interface found.\n Please verify that you have attached a compatible WLAN device or enable your local network interface.") + self.errortext = _("No working wireless network interface found.\n Please verify that you have attached a compatible WLAN device or enable your local network interface.") + self.missingwlanplugintxt = _("The wireless LAN plugin is not installed!\nPlease install it.") self["WizardActions"] = HelpableActionMap(self, "WizardActions", { @@ -728,23 +705,39 @@ class AdapterSetupConfiguration(Screen, HelpableScreen): self.onLayoutFinish.append(self.layoutFinished) self.onClose.append(self.cleanup) + + def queryWirelessDevice(self,iface): + try: + from pythonwifi.iwlibs import Wireless + import errno + except ImportError: + return False + else: + try: + ifobj = Wireless(iface) # a Wireless NIC Object + wlanresponse = ifobj.getAPaddr() + except IOError, (error_no, error_str): + if error_no in (errno.EOPNOTSUPP, errno.ENODEV, errno.EPERM): + return False + else: + print "error: ",error_no,error_str + return True + else: + return True + def ok(self): self.cleanup() if self["menulist"].getCurrent()[1] == 'edit': - if self.iface == 'wlan0' or self.iface == 'ath0': + if iNetwork.isWirelessInterface(self.iface): try: from Plugins.SystemPlugins.WirelessLan.plugin import WlanScan - from Plugins.SystemPlugins.WirelessLan.iwlibs import Wireless except ImportError: - self.session.open(MessageBox, _("The wireless LAN plugin is not installed!\nPlease install it."), type = MessageBox.TYPE_INFO,timeout = 10 ) + self.session.open(MessageBox, self.missingwlanplugintxt, type = MessageBox.TYPE_INFO,timeout = 10 ) else: - ifobj = Wireless(self.iface) # a Wireless NIC Object - self.wlanresponse = ifobj.getStatistics() - if self.wlanresponse[0] != 19: # Wlan Interface found. + if self.queryWirelessDevice(self.iface): self.session.openWithCallback(self.AdapterSetupClosed, AdapterSetup,self.iface) else: - # Display Wlan not available Message - self.showErrorMessage() + self.showErrorMessage() # Display Wlan not available Message else: self.session.openWithCallback(self.AdapterSetupClosed, AdapterSetup,self.iface) if self["menulist"].getCurrent()[1] == 'test': @@ -754,31 +747,23 @@ class AdapterSetupConfiguration(Screen, HelpableScreen): if self["menulist"].getCurrent()[1] == 'scanwlan': try: from Plugins.SystemPlugins.WirelessLan.plugin import WlanScan - from Plugins.SystemPlugins.WirelessLan.iwlibs import Wireless except ImportError: - self.session.open(MessageBox, _("The wireless LAN plugin is not installed!\nPlease install it."), type = MessageBox.TYPE_INFO,timeout = 10 ) + self.session.open(MessageBox, self.missingwlanplugintxt, type = MessageBox.TYPE_INFO,timeout = 10 ) else: - ifobj = Wireless(self.iface) # a Wireless NIC Object - self.wlanresponse = ifobj.getStatistics() - if self.wlanresponse[0] != 19: + if self.queryWirelessDevice(self.iface): self.session.openWithCallback(self.WlanScanClosed, WlanScan, self.iface) else: - # Display Wlan not available Message - self.showErrorMessage() + self.showErrorMessage() # Display Wlan not available Message if self["menulist"].getCurrent()[1] == 'wlanstatus': try: from Plugins.SystemPlugins.WirelessLan.plugin import WlanStatus - from Plugins.SystemPlugins.WirelessLan.iwlibs import Wireless except ImportError: - self.session.open(MessageBox, _("The wireless LAN plugin is not installed!\nPlease install it."), type = MessageBox.TYPE_INFO,timeout = 10 ) - else: - ifobj = Wireless(self.iface) # a Wireless NIC Object - self.wlanresponse = ifobj.getStatistics() - if self.wlanresponse[0] != 19: + self.session.open(MessageBox, self.missingwlanplugintxt, type = MessageBox.TYPE_INFO,timeout = 10 ) + else: + if self.queryWirelessDevice(self.iface): self.session.openWithCallback(self.WlanStatusClosed, WlanStatus,self.iface) else: - # Display Wlan not available Message - self.showErrorMessage() + self.showErrorMessage() # Display Wlan not available Message if self["menulist"].getCurrent()[1] == 'lanrestart': self.session.openWithCallback(self.restartLan, MessageBox, (_("Are you sure you want to restart your network interfaces?\n\n") + self.oktext ) ) if self["menulist"].getCurrent()[1] == 'openwizard': @@ -817,7 +802,7 @@ class AdapterSetupConfiguration(Screen, HelpableScreen): if self["menulist"].getCurrent()[1] == 'dns': self["description"].setText(_("Edit the Nameserver configuration of your Dreambox.\n" ) + self.oktext ) if self["menulist"].getCurrent()[1] == 'scanwlan': - self["description"].setText(_("Scan your network for wireless Access Points and connect to them using your selected wireless device.\n" ) + self.oktext ) + self["description"].setText(_("Scan your network for wireless access points and connect to them using your selected wireless device.\n" ) + self.oktext ) if self["menulist"].getCurrent()[1] == 'wlanstatus': self["description"].setText(_("Shows the state of your wireless LAN connection.\n" ) + self.oktext ) if self["menulist"].getCurrent()[1] == 'lanrestart': @@ -834,7 +819,7 @@ class AdapterSetupConfiguration(Screen, HelpableScreen): self["IF"].setText(iNetwork.getFriendlyAdapterName(self.iface)) self["Statustext"].setText(_("Link:")) - if self.iface == 'wlan0' or self.iface == 'ath0': + if iNetwork.isWirelessInterface(self.iface): try: from Plugins.SystemPlugins.WirelessLan.Wlan import iStatus except: @@ -884,20 +869,16 @@ class AdapterSetupConfiguration(Screen, HelpableScreen): def AdapterSetupClosed(self, *ret): if ret is not None and len(ret): - if ret[0] == 'ok' and (self.iface == 'wlan0' or self.iface == 'ath0') and iNetwork.getAdapterAttribute(self.iface, "up") is True: + if ret[0] == 'ok' and (iNetwork.isWirelessInterface(self.iface) and iNetwork.getAdapterAttribute(self.iface, "up") is True): try: from Plugins.SystemPlugins.WirelessLan.plugin import WlanStatus - from Plugins.SystemPlugins.WirelessLan.iwlibs import Wireless except ImportError: - self.session.open(MessageBox, _("The wireless LAN plugin is not installed!\nPlease install it."), type = MessageBox.TYPE_INFO,timeout = 10 ) + self.session.open(MessageBox, self.missingwlanplugintxt, type = MessageBox.TYPE_INFO,timeout = 10 ) else: - ifobj = Wireless(self.iface) # a Wireless NIC Object - self.wlanresponse = ifobj.getStatistics() - if self.wlanresponse[0] != 19: + if self.queryWirelessDevice(self.iface): self.session.openWithCallback(self.WlanStatusClosed, WlanStatus,self.iface) else: - # Display Wlan not available Message - self.showErrorMessage() + self.showErrorMessage() # Display Wlan not available Message else: self.updateStatusbar() else: @@ -905,15 +886,15 @@ class AdapterSetupConfiguration(Screen, HelpableScreen): def WlanStatusClosed(self, *ret): if ret is not None and len(ret): - from Plugins.SystemPlugins.WirelessLan.Wlan import iStatus,Status + from Plugins.SystemPlugins.WirelessLan.Wlan import iStatus iStatus.stopWlanConsole() self.updateStatusbar() def WlanScanClosed(self,*ret): if ret[0] is not None: - self.session.openWithCallback(self.AdapterSetupClosed, AdapterSetup, self.iface,ret[0],ret[1]) + self.session.openWithCallback(self.AdapterSetupClosed, AdapterSetup, self.iface,ret[0]) else: - from Plugins.SystemPlugins.WirelessLan.Wlan import iStatus,Status + from Plugins.SystemPlugins.WirelessLan.Wlan import iStatus iStatus.stopWlanConsole() self.updateStatusbar() @@ -956,6 +937,7 @@ class AdapterSetupConfiguration(Screen, HelpableScreen): def cleanup(self): iNetwork.stopLinkStateConsole() iNetwork.stopDeactivateInterfaceConsole() + iNetwork.stopActivateInterfaceConsole() iNetwork.stopPingConsole() try: from Plugins.SystemPlugins.WirelessLan.Wlan import iStatus @@ -969,7 +951,7 @@ class AdapterSetupConfiguration(Screen, HelpableScreen): if data is not None: if data is True: if status is not None: - if status[self.iface]["acesspoint"] == "No Connection" or status[self.iface]["acesspoint"] == "Not-Associated" or status[self.iface]["acesspoint"] == False: + if status[self.iface]["essid"] == "off" or status[self.iface]["accesspoint"] == "Not-Associated" or status[self.iface]["accesspoint"] == False: self.LinkState = False self["statuspic"].setPixmapNum(1) self["statuspic"].show() @@ -1263,6 +1245,7 @@ class NetworkAdapterTest(Screen): self.nextStepTimer.stop() def layoutFinished(self): + self.setTitle(_("Network test: ") + iNetwork.getFriendlyAdapterName(self.iface) ) self["shortcutsyellow"].setEnabled(False) self["AdapterInfo_OK"].hide() self["NetworkInfo_Check"].hide() @@ -1282,7 +1265,7 @@ class NetworkAdapterTest(Screen): self["AdapterInfo_Text"] = MultiColorLabel(_("Show Info")) self["AdapterInfo_OK"] = Pixmap() - if self.iface == 'wlan0' or self.iface == 'ath0': + if self.iface in iNetwork.wlan_interfaces: self["Networktext"] = MultiColorLabel(_("Wireless Network")) else: self["Networktext"] = MultiColorLabel(_("Local Network")) @@ -1321,9 +1304,9 @@ class NetworkAdapterTest(Screen): self["InfoText"] = Label() def getLinkState(self,iface): - if iface == 'wlan0' or iface == 'ath0': + if iface in iNetwork.wlan_interfaces: try: - from Plugins.SystemPlugins.WirelessLan.Wlan import iStatus,Status + from Plugins.SystemPlugins.WirelessLan.Wlan import iStatus except: self["Network"].setForegroundColorNum(1) self["Network"].setText(_("disconnected")) @@ -1336,7 +1319,7 @@ class NetworkAdapterTest(Screen): def LinkStatedataAvail(self,data): self.output = data.strip() - result = self.output.split('\n') + result = self.output.splitlines() pattern = re_compile("Link detected: yes") for item in result: if re_search(pattern, item): @@ -1390,7 +1373,7 @@ class NetworkAdapterTest(Screen): if data is not None: if data is True: if status is not None: - if status[self.iface]["acesspoint"] == "No Connection" or status[self.iface]["acesspoint"] == "Not-Associated" or status[self.iface]["acesspoint"] == False: + if status[self.iface]["essid"] == "off" or status[self.iface]["accesspoint"] == "Not-Associated" or status[self.iface]["accesspoint"] == False: self["Network"].setForegroundColorNum(1) self["Network"].setText(_("disconnected")) self["NetworkInfo_Check"].setPixmapNum(1) @@ -1405,7 +1388,7 @@ class NetworkAdapterTest(Screen): iNetwork.stopLinkStateConsole() iNetwork.stopDNSConsole() try: - from Plugins.SystemPlugins.WirelessLan.Wlan import iStatus,Status + from Plugins.SystemPlugins.WirelessLan.Wlan import iStatus except ImportError: pass else: diff --git a/lib/python/Screens/ParentalControlSetup.py b/lib/python/Screens/ParentalControlSetup.py index a123d2d3..284294fc 100755..100644 --- a/lib/python/Screens/ParentalControlSetup.py +++ b/lib/python/Screens/ParentalControlSetup.py @@ -3,7 +3,7 @@ from Components.ConfigList import ConfigListScreen from Components.ActionMap import NumberActionMap from Components.config import config, getConfigListEntry, ConfigNothing, NoSave, ConfigPIN from Components.ParentalControlList import ParentalControlEntryComponent, ParentalControlList -from Components.ParentalControl import parentalControl + from Components.Sources.StaticText import StaticText from Screens.ChoiceBox import ChoiceBox from Screens.MessageBox import MessageBox @@ -16,7 +16,7 @@ from operator import itemgetter class ProtectedScreen: def __init__(self): if self.isProtected(): - self.onFirstExecBegin.append(boundFunction(self.session.openWithCallback, self.pinEntered, PinInput, pinList = [self.protectedWithPin()], triesEntry = self.getTriesEntry(), title = self.getPinText(), windowTitle = _("Change pin code"))) + self.onFirstExecBegin.append(boundFunction(self.session.openWithCallback, self.pinEntered, PinInput, pinList = [self.protectedWithPin()], triesEntry = self.getTriesEntry(), title = self.getPinText(), windowTitle = _("Enter pin code"))) def getTriesEntry(self): return config.ParentalControl.retries.setuppin @@ -48,11 +48,11 @@ class ParentalControlSetup(Screen, ConfigListScreen, ProtectedScreen): self.list = [] ConfigListScreen.__init__(self, self.list, session = self.session, on_change = self.changedEntry) self.createSetup() - + self["actions"] = NumberActionMap(["SetupActions"], { - "cancel": self.keyCancel, - "save": self.keyCancel + "cancel": self.keyCancel, + "save": self.keyCancel }, -2) self["key_red"] = StaticText(_("Cancel")) self["key_green"] = StaticText(_("OK")) @@ -63,34 +63,45 @@ class ParentalControlSetup(Screen, ConfigListScreen, ProtectedScreen): def isProtected(self): return config.ParentalControl.setuppinactive.value and config.ParentalControl.configured.value - + def createSetup(self): self.editListEntry = None self.changePin = None self.changeSetupPin = None - + self.list = [] self.list.append(getConfigListEntry(_("Enable parental control"), config.ParentalControl.configured)) print "config.ParentalControl.configured.value", config.ParentalControl.configured.value + self.editBouquetListEntry = -1 + self.reloadLists = -1 if config.ParentalControl.configured.value: #self.list.append(getConfigListEntry(_("Configuration mode"), config.ParentalControl.mode)) self.list.append(getConfigListEntry(_("Protect setup"), config.ParentalControl.setuppinactive)) if config.ParentalControl.setuppinactive.value: - self.changeSetupPin = getConfigListEntry(_("Change setup pin"), NoSave(ConfigNothing())) + self.changeSetupPin = getConfigListEntry(_("Change setup PIN"), NoSave(ConfigNothing())) self.list.append(self.changeSetupPin) self.list.append(getConfigListEntry(_("Protect services"), config.ParentalControl.servicepinactive)) if config.ParentalControl.servicepinactive.value: self.list.append(getConfigListEntry(_("Parental control type"), config.ParentalControl.type)) if config.ParentalControl.mode.value == "complex": - self.changePin = getConfigListEntry(_("Change service pins"), NoSave(ConfigNothing())) + self.changePin = getConfigListEntry(_("Change service PINs"), NoSave(ConfigNothing())) self.list.append(self.changePin) elif config.ParentalControl.mode.value == "simple": - self.changePin = getConfigListEntry(_("Change service pin"), NoSave(ConfigNothing())) + self.changePin = getConfigListEntry(_("Change service PIN"), NoSave(ConfigNothing())) self.list.append(self.changePin) - #self.list.append(getConfigListEntry(_("Remember service pin"), config.ParentalControl.storeservicepin)) + #Added Option to remember the service pin + self.list.append(getConfigListEntry(_("Remember service PIN"), config.ParentalControl.storeservicepin)) + #Added Option to remember the cancellation of service pin entry + self.list.append(getConfigListEntry(_("Remember service PIN cancel"), config.ParentalControl.storeservicepincancel)) self.editListEntry = getConfigListEntry(_("Edit services list"), NoSave(ConfigNothing())) self.list.append(self.editListEntry) - + #New funtion: Possibility to add Bouquets to whitelist / blacklist + self.editBouquetListEntry = getConfigListEntry(_("Edit bouquets list"), NoSave(ConfigNothing())) + self.list.append(self.editBouquetListEntry) + #New option to reload service lists (for example if bouquets have changed) + self.reloadLists = getConfigListEntry(_("Reload Black-/Whitelists"), NoSave(ConfigNothing())) + self.list.append(self.reloadLists) + self["config"].list = self.list self["config"].setList(self.list) @@ -98,13 +109,18 @@ class ParentalControlSetup(Screen, ConfigListScreen, ProtectedScreen): print "self[\"config\"].l.getCurrentSelection()", self["config"].l.getCurrentSelection() if self["config"].l.getCurrentSelection() == self.editListEntry: self.session.open(ParentalControlEditor) + elif self["config"].l.getCurrentSelection() == self.editBouquetListEntry: + self.session.open(ParentalControlBouquetEditor) elif self["config"].l.getCurrentSelection() == self.changePin: if config.ParentalControl.mode.value == "complex": pass else: - self.session.open(ParentalControlChangePin, config.ParentalControl.servicepin[0], _("service pin")) + self.session.open(ParentalControlChangePin, config.ParentalControl.servicepin[0], _("service PIN")) elif self["config"].l.getCurrentSelection() == self.changeSetupPin: - self.session.open(ParentalControlChangePin, config.ParentalControl.setuppin, _("setup pin")) + self.session.open(ParentalControlChangePin, config.ParentalControl.setuppin, _("setup PIN")) + elif self["config"].l.getCurrentSelection() == self.reloadLists: + from Components.ParentalControl import parentalControl + parentalControl.open() else: ConfigListScreen.keyRight(self) print "current selection:", self["config"].l.getCurrentSelection() @@ -122,14 +138,14 @@ class ParentalControlSetup(Screen, ConfigListScreen, ProtectedScreen): def SetupPinMessageCallback(self, value): if value: - self.session.openWithCallback(self.cancelCB, ParentalControlChangePin, config.ParentalControl.setuppin, _("setup pin")) + self.session.openWithCallback(self.cancelCB, ParentalControlChangePin, config.ParentalControl.setuppin, _("setup PIN")) else: config.ParentalControl.setuppinactive.value = False self.keyCancel() def ServicePinMessageCallback(self, value): if value: - self.session.openWithCallback(self.cancelCB, ParentalControlChangePin, config.ParentalControl.servicepin[0], _("service pin")) + self.session.openWithCallback(self.cancelCB, ParentalControlChangePin, config.ParentalControl.servicepin[0], _("service PIN")) else: config.ParentalControl.servicepinactive.value = False self.keyCancel() @@ -149,6 +165,7 @@ class ParentalControlSetup(Screen, ConfigListScreen, ProtectedScreen): def keyNumberGlobal(self, number): pass + # for summary: def changedEntry(self): for x in self.onChangedEntry: @@ -224,13 +241,13 @@ class ParentalControlEditor(Screen): if not self.servicesList.has_key(key): self.servicesList[key] = [] self.servicesList[key].append(s) - + def chooseLetter(self): print "choose letter" mylist = [] for x in self.servicesList.keys(): if x == chr(SPECIAL_CHAR): - x = ("special characters", x) + x = (_("special characters"), x) else: x = (x, x) mylist.append(x) @@ -239,15 +256,57 @@ class ParentalControlEditor(Screen): self.session.openWithCallback(self.letterChosen, ChoiceBox, title=_("Show services beginning with"), list=mylist, keys = [], selection = sel) def letterChosen(self, result): + from Components.ParentalControl import parentalControl if result is not None: print "result:", result self.currentLetter = result[1] - self.list = [ParentalControlEntryComponent(x[0], x[1], parentalControl.getProtectionLevel(x[0]) != -1) for x in self.servicesList[result[1]]] + #Replace getProtectionLevel by new getProtectionType + self.list = [ParentalControlEntryComponent(x[0], x[1], parentalControl.getProtectionType(x[0])) for x in self.servicesList[result[1]]] self.servicelist.setList(self.list) else: parentalControl.save() self.close() +class ParentalControlBouquetEditor(Screen): + #This new class allows adding complete bouquets to black- and whitelists + #The servicereference that is stored for bouquets is their refstr as listed in bouquets.tv + def __init__(self, session): + Screen.__init__(self, session) + self.skinName = "ParentalControlEditor" + self.list = [] + self.bouquetslist = ParentalControlList(self.list) + self["servicelist"] = self.bouquetslist; + self.readBouquetList() + self.onLayoutFinish.append(self.selectBouquet) + + self["actions"] = NumberActionMap(["DirectionActions", "ColorActions", "OkCancelActions"], + { + "ok": self.select, + "cancel": self.cancel + }, -1) + + def cancel(self): + from Components.ParentalControl import parentalControl + parentalControl.save() + self.close() + + def select(self): + self.bouquetslist.toggleSelectedLock() + + def readBouquetList(self): + serviceHandler = eServiceCenter.getInstance() + refstr = '1:134:1:0:0:0:0:0:0:0:FROM BOUQUET \"bouquets.tv\" ORDER BY bouquet' + bouquetroot = eServiceReference(refstr) + self.bouquetlist = {} + list = serviceHandler.list(bouquetroot) + if list is not None: + self.bouquetlist = list.getContent("CN", True) + + def selectBouquet(self): + from Components.ParentalControl import parentalControl + self.list = [ParentalControlEntryComponent(x[0], x[1], parentalControl.getProtectionType(x[0])) for x in self.bouquetlist] + self.bouquetslist.setList(self.list) + class ParentalControlChangePin(Screen, ConfigListScreen, ProtectedScreen): def __init__(self, session, pin, pinname): Screen.__init__(self, session) @@ -262,14 +321,14 @@ class ParentalControlChangePin(Screen, ConfigListScreen, ProtectedScreen): self.pin2 = ConfigPIN(default = 1112, censor = "*") self.pin1.addEndNotifier(boundFunction(self.valueChanged, 1)) self.pin2.addEndNotifier(boundFunction(self.valueChanged, 2)) - self.list.append(getConfigListEntry(_("New pin"), NoSave(self.pin1))) - self.list.append(getConfigListEntry(_("Reenter new pin"), NoSave(self.pin2))) - ConfigListScreen.__init__(self, self.list, session = self.session, on_change = self.changedEntry) + self.list.append(getConfigListEntry(_("New PIN"), NoSave(self.pin1))) + self.list.append(getConfigListEntry(_("Reenter new PIN"), NoSave(self.pin2))) + ConfigListScreen.__init__(self, self.list) # print "old pin:", pin #if pin.value != "aaaa": #self.onFirstExecBegin.append(boundFunction(self.session.openWithCallback, self.pinEntered, PinInput, pinList = [self.pin.value], title = _("please enter the old pin"), windowTitle = _("Change pin code"))) ProtectedScreen.__init__(self) - + self["actions"] = NumberActionMap(["DirectionActions", "ColorActions", "OkCancelActions"], { "cancel": self.cancel, @@ -290,7 +349,7 @@ class ParentalControlChangePin(Screen, ConfigListScreen, ProtectedScreen): self.keyOK() def getPinText(self): - return _("Please enter the old pin code") + return _("Please enter the old PIN code") def isProtected(self): return (self.pin.value != "aaaa") @@ -309,9 +368,9 @@ class ParentalControlChangePin(Screen, ConfigListScreen, ProtectedScreen): if self.pin1.value == self.pin2.value: self.pin.value = self.pin1.value self.pin.save() - self.session.openWithCallback(self.close, MessageBox, _("The pin code has been changed successfully."), MessageBox.TYPE_INFO) + self.session.openWithCallback(self.close, MessageBox, _("The PIN code has been changed successfully."), MessageBox.TYPE_INFO) else: - self.session.open(MessageBox, _("The pin codes you entered are different."), MessageBox.TYPE_ERROR) + self.session.open(MessageBox, _("The PIN codes you entered are different."), MessageBox.TYPE_ERROR) def cancel(self): self.close(None) diff --git a/lib/python/Screens/PluginBrowser.py b/lib/python/Screens/PluginBrowser.py index 69bf80f7..359552eb 100755 --- a/lib/python/Screens/PluginBrowser.py +++ b/lib/python/Screens/PluginBrowser.py @@ -155,9 +155,9 @@ class PluginDownloadBrowser(Screen): def runInstall(self, val): if val: if self.type == self.DOWNLOAD: - self.session.openWithCallback(self.installFinished, Console, cmdlist = ["ipkg install " + "enigma2-plugin-" + self["list"].l.getCurrentSelection()[0].name]) + self.session.openWithCallback(self.installFinished, Console, cmdlist = ["opkg install " + "enigma2-plugin-" + self["list"].l.getCurrentSelection()[0].name]) elif self.type == self.REMOVE: - self.session.openWithCallback(self.installFinished, Console, cmdlist = ["ipkg remove " + "enigma2-plugin-" + self["list"].l.getCurrentSelection()[0].name]) + self.session.openWithCallback(self.installFinished, Console, cmdlist = ["opkg remove " + "enigma2-plugin-" + self["list"].l.getCurrentSelection()[0].name]) def setWindowTitle(self): if self.type == self.DOWNLOAD: @@ -166,17 +166,17 @@ class PluginDownloadBrowser(Screen): self.setTitle(_("Remove plugins")) def startIpkgListInstalled(self): - self.container.execute("ipkg list_installed enigma2-plugin-*") + self.container.execute("opkg list_installed enigma2-plugin-*") def startIpkgListAvailable(self): - self.container.execute("ipkg list enigma2-plugin-*") + self.container.execute("opkg list enigma2-plugin-*") def startRun(self): self["list"].instance.hide() if self.type == self.DOWNLOAD: if not PluginDownloadBrowser.lastDownloadDate or (time() - PluginDownloadBrowser.lastDownloadDate) > 3600: # Only update from internet once per hour - self.container.execute("ipkg update") + self.container.execute("opkg update") PluginDownloadBrowser.lastDownloadDate = time() else: self.startIpkgListAvailable() @@ -256,4 +256,4 @@ class PluginDownloadBrowser(Screen): self.list = list self["list"].l.setList(list) -language.addCallback(languageChanged)
\ No newline at end of file +language.addCallback(languageChanged) diff --git a/lib/python/Screens/RecordPaths.py b/lib/python/Screens/RecordPaths.py index c833266f..22ca9fcf 100644 --- a/lib/python/Screens/RecordPaths.py +++ b/lib/python/Screens/RecordPaths.py @@ -6,7 +6,7 @@ from Components.config import config, ConfigSelection, getConfigListEntry, confi from Components.ConfigList import ConfigListScreen from Components.ActionMap import ActionMap from Tools.Directories import fileExists - +from Components.UsageConfig import preferredPath class RecordPathsSettings(Screen,ConfigListScreen): skin = """ @@ -115,7 +115,7 @@ class RecordPathsSettings(Screen,ConfigListScreen): self.dirnameSelected, MovieLocationBox, txt, - self.default_dirname.value + preferredPath(self.default_dirname.value) ) elif currentry == self.timer_entry: self.entrydirname = self.timer_dirname @@ -123,7 +123,7 @@ class RecordPathsSettings(Screen,ConfigListScreen): self.dirnameSelected, MovieLocationBox, _("Initial location in new timers"), - self.timer_dirname.value + preferredPath(self.timer_dirname.value) ) elif currentry == self.instantrec_entry: self.entrydirname = self.instantrec_dirname @@ -131,7 +131,7 @@ class RecordPathsSettings(Screen,ConfigListScreen): self.dirnameSelected, MovieLocationBox, _("Location for instant recordings"), - self.instantrec_dirname.value + preferredPath(self.instantrec_dirname.value) ) elif currentry == self.timeshift_entry: self.entrydirname = self.timeshift_dirname diff --git a/lib/python/Screens/Satconfig.py b/lib/python/Screens/Satconfig.py index 156f7780..a5712dcd 100644 --- a/lib/python/Screens/Satconfig.py +++ b/lib/python/Screens/Satconfig.py @@ -58,7 +58,7 @@ class NimSetup(Screen, ConfigListScreen, ServiceStopScreen): def createConfigMode(self): if self.nim.isCompatible("DVB-S"): - choices = { "nothing": _("nothing connected"), + choices = { "nothing": _("not configured"), "simple": _("simple"), "advanced": _("advanced")} #if len(nimmanager.getNimListOfType(nimmanager.getNimType(self.slotid), exception = x)) > 0: @@ -76,6 +76,7 @@ class NimSetup(Screen, ConfigListScreen, ServiceStopScreen): print "Creating setup" self.list = [ ] + self.multiType = None self.configMode = None self.diseqcModeEntry = None self.advancedSatsEntry = None @@ -94,6 +95,12 @@ class NimSetup(Screen, ConfigListScreen, ServiceStopScreen): self.advancedType = None self.advancedManufacturer = None self.advancedSCR = None + self.advancedConnected = None + + if self.nim.isMultiType(): + multiType = self.nimConfig.multiType + self.multiType = getConfigListEntry(_("Tuner type"), multiType) + self.list.append(self.multiType) if self.nim.isCompatible("DVB-S"): self.configMode = getConfigListEntry(_("Configuration Mode"), self.nimConfig.configMode) @@ -199,11 +206,18 @@ class NimSetup(Screen, ConfigListScreen, ServiceStopScreen): checkList = (self.configMode, self.diseqcModeEntry, self.advancedSatsEntry, \ self.advancedLnbsEntry, self.advancedDiseqcMode, self.advancedUsalsEntry, \ self.advancedLof, self.advancedPowerMeasurement, self.turningSpeed, \ - self.advancedType, self.advancedSCR, self.advancedManufacturer, self.advancedUnicable, \ - self.uncommittedDiseqcCommand, self.cableScanType) + self.advancedType, self.advancedSCR, self.advancedManufacturer, self.advancedUnicable, self.advancedConnected, \ + self.uncommittedDiseqcCommand, self.cableScanType, self.multiType) + if self["config"].getCurrent() == self.multiType: + from Components.NimManager import InitNimManager + InitNimManager(nimmanager) + self.nim = nimmanager.nim_slots[self.slotid] + self.nimConfig = self.nim.config + for x in checkList: if self["config"].getCurrent() == x: self.createSetup() + break def run(self): if self.have_advanced and self.nim.config_mode == "advanced": @@ -272,6 +286,18 @@ class NimSetup(Screen, ConfigListScreen, ServiceStopScreen): self.list.append(self.advancedType) self.list.append(self.advancedSCR) self.list.append(getConfigListEntry(_("Frequency"), manufacturer.vco[product_name][manufacturer.scr[product_name].index])) + + choices = [] + connectable = nimmanager.canConnectTo(self.slotid) + for id in connectable: + choices.append((str(id), nimmanager.getNimDescription(id))) + if len(choices): + self.advancedConnected = getConfigListEntry(_("connected"), self.nimConfig.advanced.unicableconnected) + self.list.append(self.advancedConnected) + if self.nimConfig.advanced.unicableconnected.value == True: + self.nimConfig.advanced.unicableconnectedTo.setChoices(choices) + self.list.append(getConfigListEntry(_("Connected to"),self.nimConfig.advanced.unicableconnectedTo)) + else: #kein Unicable self.list.append(getConfigListEntry(_("Voltage mode"), Sat.voltage)) self.list.append(getConfigListEntry(_("Increased voltage"), currLnb.increased_voltage)) @@ -347,7 +373,6 @@ class NimSetup(Screen, ConfigListScreen, ServiceStopScreen): new_configured_sats = nimmanager.getConfiguredSats() self.unconfed_sats = old_configured_sats - new_configured_sats self.satpos_to_remove = None - self.restoreService(_("Zap back to service before tuner setup?")) self.deleteConfirmed((None, "no")) def deleteConfirmed(self, confirmed): @@ -377,7 +402,9 @@ class NimSetup(Screen, ConfigListScreen, ServiceStopScreen): if confirmed[1] == "yestoall" or confirmed[1] == "notoall": self.deleteConfirmed(confirmed) break - + else: + self.restoreService(_("Zap back to service before tuner setup?")) + def __init__(self, session, slotid): Screen.__init__(self, session) self.list = [ ] @@ -462,7 +489,7 @@ class NimSelection(Screen): def okbuttonClick(self): nim = self["nimlist"].getCurrent() nim = nim and nim[3] - if nim is not None and not nim.empty: + if nim is not None and not nim.empty and nim.isSupported(): self.session.openWithCallback(self.updateList, self.resultclass, nim.slot) def showNim(self, nim): @@ -482,20 +509,28 @@ class NimSelection(Screen): "satposdepends": _("second cable of motorized LNB") } [nimConfig.configMode.value] text += " " + _("Tuner") + " " + ["A", "B", "C", "D"][int(nimConfig.connectedTo.value)] elif nimConfig.configMode.value == "nothing": - text = _("nothing connected") + text = _("not configured") elif nimConfig.configMode.value == "simple": if nimConfig.diseqcMode.value in ("single", "toneburst_a_b", "diseqc_a_b", "diseqc_a_b_c_d"): - text = _("Sats") + ": " + text = {"single": _("Single"), "toneburst_a_b": _("Toneburst A/B"), "diseqc_a_b": _("DiSEqC A/B"), "diseqc_a_b_c_d": _("DiSEqC A/B/C/D")}[nimConfig.diseqcMode.value] + "\n" + text += _("Sats") + ": " + satnames = [] if nimConfig.diseqcA.orbital_position != 3601: - text += nimmanager.getSatName(int(nimConfig.diseqcA.value)) + satnames.append(nimmanager.getSatName(int(nimConfig.diseqcA.value))) if nimConfig.diseqcMode.value in ("toneburst_a_b", "diseqc_a_b", "diseqc_a_b_c_d"): if nimConfig.diseqcB.orbital_position != 3601: - text += "," + nimmanager.getSatName(int(nimConfig.diseqcB.value)) + satnames.append(nimmanager.getSatName(int(nimConfig.diseqcB.value))) if nimConfig.diseqcMode.value == "diseqc_a_b_c_d": if nimConfig.diseqcC.orbital_position != 3601: - text += "," + nimmanager.getSatName(int(nimConfig.diseqcC.value)) + satnames.append(nimmanager.getSatName(int(nimConfig.diseqcC.value))) if nimConfig.diseqcD.orbital_position != 3601: - text += "," + nimmanager.getSatName(int(nimConfig.diseqcD.value)) + satnames.append(nimmanager.getSatName(int(nimConfig.diseqcD.value))) + if len(satnames) <= 2: + text += ", ".join(satnames) + elif len(satnames) > 2: + # we need a newline here, since multi content lists don't support automtic line wrapping + text += ", ".join(satnames[:2]) + ",\n" + text += " " + ", ".join(satnames[2:]) elif nimConfig.diseqcMode.value == "positioner": text = _("Positioner") + ":" if nimConfig.positionerMode.value == "usals": @@ -511,6 +546,10 @@ class NimSelection(Screen): text = _("nothing connected") elif nimConfig.configMode.value == "enabled": text = _("enabled") + if x.isMultiType(): + text = _("Switchable tuner types:") + "(" + ','.join(x.getMultiTypeList().values()) + ")" + "\n" + text + if not x.isSupported(): + text = _("tuner is not supported") self.list.append((slotid, x.friendly_full_description, text, x)) self["nimlist"].setList(self.list) diff --git a/lib/python/Screens/ScanSetup.py b/lib/python/Screens/ScanSetup.py index fa787a70..f8bfeb72 100644 --- a/lib/python/Screens/ScanSetup.py +++ b/lib/python/Screens/ScanSetup.py @@ -8,6 +8,7 @@ from Components.ConfigList import ConfigListScreen from Components.NimManager import nimmanager, getConfigSatlist from Components.Label import Label from Tools.Directories import resolveFilename, SCOPE_DEFAULTPARTITIONMOUNTDIR, SCOPE_DEFAULTDIR, SCOPE_DEFAULTPARTITION +from Tools.HardwareInfo import HardwareInfo from Screens.MessageBox import MessageBox from enigma import eTimer, eDVBFrontendParametersSatellite, eComponentScan, \ eDVBSatelliteEquipmentControl, eDVBFrontendParametersTerrestrial, \ @@ -106,6 +107,7 @@ class CableTransponderSearchSupport: if raw_channel: frontend = raw_channel.getFrontend() if frontend: + frontend.closeFrontend() # immediate close... del frontend del raw_channel return True @@ -115,10 +117,14 @@ class CableTransponderSearchSupport: print "cableTransponderSearchSessionClosed, val", val self.cable_search_container.appClosed.remove(self.cableTransponderSearchClosed) self.cable_search_container.dataAvail.remove(self.getCableTransponderData) + if val and len(val): + if val[0]: + self.setCableTransponderSearchResult(self.__tlist) + else: + self.cable_search_container.sendCtrlC() + self.setCableTransponderSearchResult(None) self.cable_search_container = None self.cable_search_session = None - if val and len(val) and val[0]: - self.setCableTransponderSearchResult(self.__tlist) self.__tlist = None self.cableTransponderSearchFinished() @@ -138,7 +144,8 @@ class CableTransponderSearchSupport: "QAM128" : parm.Modulation_QAM128, "QAM256" : parm.Modulation_QAM256 } inv = { "INVERSION_OFF" : parm.Inversion_Off, - "INVERSION_ON" : parm.Inversion_On } + "INVERSION_ON" : parm.Inversion_On, + "INVERSION_AUTO" : parm.Inversion_Unknown } fec = { "FEC_AUTO" : parm.FEC_Auto, "FEC_1_2" : parm.FEC_1_2, "FEC_2_3" : parm.FEC_2_3, @@ -153,12 +160,12 @@ class CableTransponderSearchSupport: parm.modulation = qam[data[4]] parm.inversion = inv[data[5]] self.__tlist.append(parm) - tmpstr = _("Try to find used Transponders in cable network.. please wait...") - tmpstr += "\n\n" - tmpstr += data[1] - tmpstr += " kHz " - tmpstr += data[0] - self.cable_search_session["text"].setText(tmpstr) + tmpstr = _("Try to find used Transponders in cable network.. please wait...") + tmpstr += "\n\n" + tmpstr += data[1] + tmpstr += " kHz " + tmpstr += data[0] + self.cable_search_session["text"].setText(tmpstr) def startCableTransponderSearch(self, nim_idx): if not self.tryGetRawFrontend(nim_idx): @@ -175,15 +182,17 @@ class CableTransponderSearchSupport: self.cable_search_container.appClosed.append(self.cableTransponderSearchClosed) self.cable_search_container.dataAvail.append(self.getCableTransponderData) cableConfig = config.Nims[nim_idx].cable - cmd = "tda1002x --init --scan --verbose --wakeup --inv 2 --bus " - #FIXMEEEEEE hardcoded i2c devices for dm7025 and dm8000 - if nim_idx < 2: - cmd += str(nim_idx) + tunername = nimmanager.getNimName(nim_idx) + bus = nimmanager.getI2CDevice(nim_idx) + if bus is None: + print "ERROR: could not get I2C device for nim", nim_idx, "for cable transponder search" + bus = 2 + + if tunername == "CXD1981": + cmd = "cxd1978 --init --scan --verbose --wakeup --inv 2 --bus %d" % bus else: - if nim_idx == 2: - cmd += "2" # first nim socket on DM8000 use /dev/i2c/2 - else: - cmd += "4" # second nim socket on DM8000 use /dev/i2c/4 + cmd = "tda1002x --init --scan --verbose --wakeup --inv 2 --bus %d" % bus + if cableConfig.scan_type.value == "bands": cmd += " --scan-bands " bands = 0 @@ -352,15 +361,15 @@ class ScanSetup(ConfigListScreen, Screen, CableTransponderSearchSupport): self.list.append(getConfigListEntry(_('Satellite'), self.scan_satselection[index_to_scan])) self.list.append(getConfigListEntry(_('Frequency'), self.scan_sat.frequency)) self.list.append(getConfigListEntry(_('Inversion'), self.scan_sat.inversion)) - self.list.append(getConfigListEntry(_('Symbol Rate'), self.scan_sat.symbolrate)) - self.list.append(getConfigListEntry(_("Polarity"), self.scan_sat.polarization)) + self.list.append(getConfigListEntry(_('Symbol rate'), self.scan_sat.symbolrate)) + self.list.append(getConfigListEntry(_('Polarization'), self.scan_sat.polarization)) if self.scan_sat.system.value == eDVBFrontendParametersSatellite.System_DVB_S: self.list.append(getConfigListEntry(_("FEC"), self.scan_sat.fec)) elif self.scan_sat.system.value == eDVBFrontendParametersSatellite.System_DVB_S2: self.list.append(getConfigListEntry(_("FEC"), self.scan_sat.fec_s2)) self.modulationEntry = getConfigListEntry(_('Modulation'), self.scan_sat.modulation) self.list.append(self.modulationEntry) - self.list.append(getConfigListEntry(_('Rolloff'), self.scan_sat.rolloff)) + self.list.append(getConfigListEntry(_('Roll-off'), self.scan_sat.rolloff)) self.list.append(getConfigListEntry(_('Pilot'), self.scan_sat.pilot)) elif self.scan_type.value == "single_satellite": self.updateSatList() @@ -382,7 +391,7 @@ class ScanSetup(ConfigListScreen, Screen, CableTransponderSearchSupport): if self.scan_typecable.value == "single_transponder": self.list.append(getConfigListEntry(_("Frequency"), self.scan_cab.frequency)) self.list.append(getConfigListEntry(_("Inversion"), self.scan_cab.inversion)) - self.list.append(getConfigListEntry(_("Symbol Rate"), self.scan_cab.symbolrate)) + self.list.append(getConfigListEntry(_("Symbol rate"), self.scan_cab.symbolrate)) self.list.append(getConfigListEntry(_("Modulation"), self.scan_cab.modulation)) self.list.append(getConfigListEntry(_("FEC"), self.scan_cab.fec)) elif nim.isCompatible("DVB-T"): @@ -390,12 +399,12 @@ class ScanSetup(ConfigListScreen, Screen, CableTransponderSearchSupport): self.list.append(getConfigListEntry(_("Frequency"), self.scan_ter.frequency)) self.list.append(getConfigListEntry(_("Inversion"), self.scan_ter.inversion)) self.list.append(getConfigListEntry(_("Bandwidth"), self.scan_ter.bandwidth)) - self.list.append(getConfigListEntry(_("Code rate high"), self.scan_ter.fechigh)) - self.list.append(getConfigListEntry(_("Code rate low"), self.scan_ter.feclow)) + self.list.append(getConfigListEntry(_("Code rate HP"), self.scan_ter.fechigh)) + self.list.append(getConfigListEntry(_("Code rate LP"), self.scan_ter.feclow)) self.list.append(getConfigListEntry(_("Modulation"), self.scan_ter.modulation)) self.list.append(getConfigListEntry(_("Transmission mode"), self.scan_ter.transmission)) - self.list.append(getConfigListEntry(_("Guard interval mode"), self.scan_ter.guard)) - self.list.append(getConfigListEntry(_("Hierarchy mode"), self.scan_ter.hierarchy)) + self.list.append(getConfigListEntry(_("Guard interval"), self.scan_ter.guard)) + self.list.append(getConfigListEntry(_("Hierarchy info"), self.scan_ter.hierarchy)) self.list.append(getConfigListEntry(_("Network scan"), self.scan_networkScan)) self.list.append(getConfigListEntry(_("Clear before scan"), self.scan_clearallservices)) self.list.append(getConfigListEntry(_("Only Free scan"), self.scan_onlyfree)) @@ -548,8 +557,8 @@ class ScanSetup(ConfigListScreen, Screen, CableTransponderSearchSupport): (eDVBFrontendParametersSatellite.System_DVB_S2, _("DVB-S2"))]) self.scan_sat.frequency = ConfigInteger(default = defaultSat["frequency"], limits = (1, 99999)) self.scan_sat.inversion = ConfigSelection(default = defaultSat["inversion"], choices = [ - (eDVBFrontendParametersSatellite.Inversion_Off, _("off")), - (eDVBFrontendParametersSatellite.Inversion_On, _("on")), + (eDVBFrontendParametersSatellite.Inversion_Off, _("Off")), + (eDVBFrontendParametersSatellite.Inversion_On, _("On")), (eDVBFrontendParametersSatellite.Inversion_Unknown, _("Auto"))]) self.scan_sat.symbolrate = ConfigInteger(default = defaultSat["symbolrate"], limits = (1, 99999)) self.scan_sat.polarization = ConfigSelection(default = defaultSat["polarization"], choices = [ @@ -583,15 +592,15 @@ class ScanSetup(ConfigListScreen, Screen, CableTransponderSearchSupport): (eDVBFrontendParametersSatellite.RollOff_alpha_0_25, "0.25"), (eDVBFrontendParametersSatellite.RollOff_alpha_0_20, "0.20")]) self.scan_sat.pilot = ConfigSelection(default = defaultSat.get("pilot", eDVBFrontendParametersSatellite.Pilot_Unknown), choices = [ - (eDVBFrontendParametersSatellite.Pilot_Off, _("off")), - (eDVBFrontendParametersSatellite.Pilot_On, _("on")), + (eDVBFrontendParametersSatellite.Pilot_Off, _("Off")), + (eDVBFrontendParametersSatellite.Pilot_On, _("On")), (eDVBFrontendParametersSatellite.Pilot_Unknown, _("Auto"))]) # cable self.scan_cab.frequency = ConfigInteger(default = defaultCab["frequency"], limits = (50, 999)) self.scan_cab.inversion = ConfigSelection(default = defaultCab["inversion"], choices = [ - (eDVBFrontendParametersCable.Inversion_Off, _("off")), - (eDVBFrontendParametersCable.Inversion_On, _("on")), + (eDVBFrontendParametersCable.Inversion_Off, _("Off")), + (eDVBFrontendParametersCable.Inversion_On, _("On")), (eDVBFrontendParametersCable.Inversion_Unknown, _("Auto"))]) self.scan_cab.modulation = ConfigSelection(default = defaultCab["modulation"], choices = [ (eDVBFrontendParametersCable.Modulation_QAM16, "16-QAM"), @@ -613,8 +622,8 @@ class ScanSetup(ConfigListScreen, Screen, CableTransponderSearchSupport): # terrestial self.scan_ter.frequency = ConfigInteger(default = 466000, limits = (50000, 999000)) self.scan_ter.inversion = ConfigSelection(default = defaultTer["inversion"], choices = [ - (eDVBFrontendParametersTerrestrial.Inversion_Off, _("off")), - (eDVBFrontendParametersTerrestrial.Inversion_On, _("on")), + (eDVBFrontendParametersTerrestrial.Inversion_Off, _("Off")), + (eDVBFrontendParametersTerrestrial.Inversion_On, _("On")), (eDVBFrontendParametersTerrestrial.Inversion_Unknown, _("Auto"))]) # WORKAROUND: we can't use BW-auto self.scan_ter.bandwidth = ConfigSelection(default = defaultTer["bandwidth"], choices = [ @@ -826,7 +835,10 @@ class ScanSetup(ConfigListScreen, Screen, CableTransponderSearchSupport): self.tlist = tlist def cableTransponderSearchFinished(self): - self.startScan(self.tlist, self.flags, self.feid) + if self.tlist is None: + self.tlist = [] + else: + self.startScan(self.tlist, self.flags, self.feid) def startScan(self, tlist, flags, feid): if len(tlist): @@ -992,7 +1004,8 @@ class ScanSimple(ConfigListScreen, Screen, CableTransponderSearchSupport): self.session.open(MessageBox, _("Nothing to scan!\nPlease setup your tuner settings before you start a service scan."), MessageBox.TYPE_ERROR) def setCableTransponderSearchResult(self, tlist): - self.scanList.append({"transponders": tlist, "feid": self.feid, "flags": self.flags}) + if tlist is not None: + self.scanList.append({"transponders": tlist, "feid": self.feid, "flags": self.flags}) def cableTransponderSearchFinished(self): self.buildTransponderList() diff --git a/lib/python/Screens/Screen.py b/lib/python/Screens/Screen.py index f0bf773d..4a0accda 100644 --- a/lib/python/Screens/Screen.py +++ b/lib/python/Screens/Screen.py @@ -6,6 +6,8 @@ profile("LOAD:Source") from Components.Sources.Source import Source profile("LOAD:GUIComponent") from Components.GUIComponent import GUIComponent +profile("LOAD:eRCInput") +from enigma import eRCInput class Screen(dict, GUISkin): @@ -46,6 +48,24 @@ class Screen(dict, GUISkin): # stand alone screens (for example web screens) # don't care about having or not having focus. self.stand_alone = False + self.keyboardMode = None + + def saveKeyboardMode(self): + rcinput = eRCInput.getInstance() + self.keyboardMode = rcinput.getKeyboardMode() + + def setKeyboardModeAscii(self): + rcinput = eRCInput.getInstance() + rcinput.setKeyboardMode(rcinput.kmAscii) + + def setKeyboardModeNone(self): + rcinput = eRCInput.getInstance() + rcinput.setKeyboardMode(rcinput.kmNone) + + def restoreKeyboardMode(self): + rcinput = eRCInput.getInstance() + if self.keyboardMode is not None: + rcinput.setKeyboardMode(self.keyboardMode) def execBegin(self): self.active_components = [ ] diff --git a/lib/python/Screens/ServiceInfo.py b/lib/python/Screens/ServiceInfo.py index fa2f4474..61885dfa 100644 --- a/lib/python/Screens/ServiceInfo.py +++ b/lib/python/Screens/ServiceInfo.py @@ -81,9 +81,9 @@ class ServiceInfo(Screen): self.feinfo = None else: self.type = TYPE_SERVICE_INFO - self["red"] = Label(_("Serviceinfo")) + self["red"] = Label(_("Service")) self["green"] = Label(_("PIDs")) - self["yellow"] = Label(_("Transponder")) + self["yellow"] = Label(_("Multiplex")) self["blue"] = Label(_("Tuner status")) service = session.nav.getCurrentService() if service is not None: @@ -105,63 +105,63 @@ class ServiceInfo(Screen): name = ServiceReference(self.session.nav.getCurrentlyPlayingServiceReference()).getServiceName() refstr = self.session.nav.getCurrentlyPlayingServiceReference().toString() else: - name = "N/A" - refstr = "N/A" + name = _("N/A") + refstr = _("N/A") aspect = self.getServiceInfoValue(iServiceInformation.sAspect) if aspect in ( 1, 2, 5, 6, 9, 0xA, 0xD, 0xE ): - aspect = "4:3" + aspect = _("4:3") else: - aspect = "16:9" + aspect = _("16:9") width = self.info and self.info.getInfo(iServiceInformation.sVideoWidth) or -1 height = self.info and self.info.getInfo(iServiceInformation.sVideoHeight) or -1 if width != -1 and height != -1: - Labels = ( ("Name", name, TYPE_TEXT), - ("Provider", self.getServiceInfoValue(iServiceInformation.sProvider), TYPE_TEXT), - ("Videoformat", aspect, TYPE_TEXT), - ("Videosize", "%dx%d" %(width, height), TYPE_TEXT), - ("Namespace", self.getServiceInfoValue(iServiceInformation.sNamespace), TYPE_VALUE_HEX, 8), - ("Service Reference", refstr, TYPE_TEXT)) + Labels = ( (_("Name"), name, TYPE_TEXT), + (_("Provider"), self.getServiceInfoValue(iServiceInformation.sProvider), TYPE_TEXT), + (_("Aspect ratio"), aspect, TYPE_TEXT), + (_("Resolution"), "%dx%d" %(width, height), TYPE_TEXT), + (_("Namespace"), self.getServiceInfoValue(iServiceInformation.sNamespace), TYPE_VALUE_HEX, 8), + (_("Service reference"), refstr, TYPE_TEXT)) else: - Labels = ( ("Name", name, TYPE_TEXT), - ("Provider", self.getServiceInfoValue(iServiceInformation.sProvider), TYPE_TEXT), - ("Videoformat", aspect, TYPE_TEXT), - ("Namespace", self.getServiceInfoValue(iServiceInformation.sNamespace), TYPE_VALUE_HEX, 8), - ("Service Reference", refstr, TYPE_TEXT)) + Labels = ( (_("Name"), name, TYPE_TEXT), + (_("Provider"), self.getServiceInfoValue(iServiceInformation.sProvider), TYPE_TEXT), + (_("Aspect ratio"), aspect, TYPE_TEXT), + (_("Namespace"), self.getServiceInfoValue(iServiceInformation.sNamespace), TYPE_VALUE_HEX, 8), + (_("Service reference"), refstr, TYPE_TEXT)) self.fillList(Labels) else: if self.transponder_info: tp_info = ConvertToHumanReadable(self.transponder_info) - conv = { "tuner_type" : _("Transponder Type"), - "system" : _("System"), - "modulation" : _("Modulation"), - "orbital_position" : _("Orbital Position"), - "frequency" : _("Frequency"), - "symbol_rate" : _("Symbolrate"), - "bandwidth" : _("Bandwidth"), - "polarization" : _("Polarization"), - "inversion" : _("Inversion"), - "pilot" : _("Pilot"), - "rolloff" : _("Rolloff"), - "fec_inner" : _("FEC"), - "code_rate_lp" : _("Coderate LP"), - "code_rate_hp" : _("Coderate HP"), - "constellation" : _("Constellation"), - "transmission_mode": _("Transmission Mode"), - "guard_interval" : _("Guard Interval"), - "hierarchy_information": _("Hierarchy Information") } + conv = { "tuner_type" : _("Type"), + "system" : _("System"), + "modulation" : _("Modulation"), + "orbital_position" : _("Orbital position"), + "frequency" : _("Frequency"), + "symbol_rate" : _("Symbol rate"), + "bandwidth" : _("Bandwidth"), + "polarization" : _("Polarization"), + "inversion" : _("Inversion"), + "pilot" : _("Pilot"), + "rolloff" : _("Roll-off"), + "fec_inner" : _("FEC"), + "code_rate_lp" : _("Code rate LP"), + "code_rate_hp" : _("Code rate HP"), + "constellation" : _("Constellation"), + "transmission_mode" : _("Transmission mode"), + "guard_interval" : _("Guard interval"), + "hierarchy_information" : _("Hierarchy info") } Labels = [(conv[i], tp_info[i], TYPE_VALUE_DEC) for i in tp_info.keys()] self.fillList(Labels) def pids(self): if self.type == TYPE_SERVICE_INFO: - Labels = ( ("VideoPID", self.getServiceInfoValue(iServiceInformation.sVideoPID), TYPE_VALUE_HEX_DEC, 4), - ("AudioPID", self.getServiceInfoValue(iServiceInformation.sAudioPID), TYPE_VALUE_HEX_DEC, 4), - ("PCRPID", self.getServiceInfoValue(iServiceInformation.sPCRPID), TYPE_VALUE_HEX_DEC, 4), - ("PMTPID", self.getServiceInfoValue(iServiceInformation.sPMTPID), TYPE_VALUE_HEX_DEC, 4), - ("TXTPID", self.getServiceInfoValue(iServiceInformation.sTXTPID), TYPE_VALUE_HEX_DEC, 4), - ("TSID", self.getServiceInfoValue(iServiceInformation.sTSID), TYPE_VALUE_HEX_DEC, 4), - ("ONID", self.getServiceInfoValue(iServiceInformation.sONID), TYPE_VALUE_HEX_DEC, 4), - ("SID", self.getServiceInfoValue(iServiceInformation.sSID), TYPE_VALUE_HEX_DEC, 4)) + Labels = ( (_("Video PID"), self.getServiceInfoValue(iServiceInformation.sVideoPID), TYPE_VALUE_HEX_DEC, 4), + (_("Audio PID"), self.getServiceInfoValue(iServiceInformation.sAudioPID), TYPE_VALUE_HEX_DEC, 4), + (_("PCR PID"), self.getServiceInfoValue(iServiceInformation.sPCRPID), TYPE_VALUE_HEX_DEC, 4), + (_("PMT PID"), self.getServiceInfoValue(iServiceInformation.sPMTPID), TYPE_VALUE_HEX_DEC, 4), + (_("TXT PID"), self.getServiceInfoValue(iServiceInformation.sTXTPID), TYPE_VALUE_HEX_DEC, 4), + (_("TSID"), self.getServiceInfoValue(iServiceInformation.sTSID), TYPE_VALUE_HEX_DEC, 4), + (_("ONID"), self.getServiceInfoValue(iServiceInformation.sONID), TYPE_VALUE_HEX_DEC, 4), + (_("SID"), self.getServiceInfoValue(iServiceInformation.sSID), TYPE_VALUE_HEX_DEC, 4)) self.fillList(Labels) def showFrontendData(self, real): @@ -182,37 +182,38 @@ class ServiceInfo(Screen): if frontendDataOrg and len(frontendDataOrg): frontendData = ConvertToHumanReadable(frontendDataOrg) if frontendDataOrg["tuner_type"] == "DVB-S": - return (("NIM", ('A', 'B', 'C', 'D')[frontendData["tuner_number"]], TYPE_TEXT), - ("Type", frontendData["system"], TYPE_TEXT), - ("Modulation", frontendData["modulation"], TYPE_TEXT), - ("Orbital position", frontendData["orbital_position"], TYPE_VALUE_DEC), - ("Frequency", frontendData["frequency"], TYPE_VALUE_DEC), - ("Symbolrate", frontendData["symbol_rate"], TYPE_VALUE_DEC), - ("Polarization", frontendData["polarization"], TYPE_TEXT), - ("Inversion", frontendData["inversion"], TYPE_TEXT), - ("FEC inner", frontendData["fec_inner"], TYPE_TEXT), - ("Pilot", frontendData.get("pilot", None), TYPE_TEXT), - ("Rolloff", frontendData.get("rolloff", None), TYPE_TEXT)) + return ((_("NIM"), ('A', 'B', 'C', 'D')[frontendData["tuner_number"]], TYPE_TEXT), + (_("Type"), frontendData["tuner_type"], TYPE_TEXT), + (_("System"), frontendData["system"], TYPE_TEXT), + (_("Modulation"), frontendData["modulation"], TYPE_TEXT), + (_("Orbital position"), frontendData["orbital_position"], TYPE_VALUE_DEC), + (_("Frequency"), frontendData["frequency"], TYPE_VALUE_DEC), + (_("Symbol rate"), frontendData["symbol_rate"], TYPE_VALUE_DEC), + (_("Polarization"), frontendData["polarization"], TYPE_TEXT), + (_("Inversion"), frontendData["inversion"], TYPE_TEXT), + (_("FEC"), frontendData["fec_inner"], TYPE_TEXT), + (_("Pilot"), frontendData.get("pilot", None), TYPE_TEXT), + (_("Roll-off"), frontendData.get("rolloff", None), TYPE_TEXT)) elif frontendDataOrg["tuner_type"] == "DVB-C": - return (("NIM", ('A', 'B', 'C', 'D')[frontendData["tuner_number"]], TYPE_TEXT), - ("Type", frontendData["tuner_type"], TYPE_TEXT), - ("Frequency", frontendData["frequency"], TYPE_VALUE_DEC), - ("Symbolrate", frontendData["symbol_rate"], TYPE_VALUE_DEC), - ("Modulation", frontendData["modulation"], TYPE_TEXT), - ("Inversion", frontendData["inversion"], TYPE_TEXT), - ("FEC inner", frontendData["fec_inner"], TYPE_TEXT)) + return ((_("NIM"), ('A', 'B', 'C', 'D')[frontendData["tuner_number"]], TYPE_TEXT), + (_("Type"), frontendData["tuner_type"], TYPE_TEXT), + (_("Modulation"), frontendData["modulation"], TYPE_TEXT), + (_("Frequency"), frontendData["frequency"], TYPE_VALUE_DEC), + (_("Symbol rate"), frontendData["symbol_rate"], TYPE_VALUE_DEC), + (_("Inversion"), frontendData["inversion"], TYPE_TEXT), + (_("FEC"), frontendData["fec_inner"], TYPE_TEXT)) elif frontendDataOrg["tuner_type"] == "DVB-T": - return (("NIM", ('A', 'B', 'C', 'D')[frontendData["tuner_number"]], TYPE_TEXT), - ("Type", frontendData["tuner_type"], TYPE_TEXT), - ("Frequency", frontendData["frequency"], TYPE_VALUE_DEC), - ("Inversion", frontendData["inversion"], TYPE_TEXT), - ("Bandwidth", frontendData["bandwidth"], TYPE_VALUE_DEC), - ("CodeRateLP", frontendData["code_rate_lp"], TYPE_TEXT), - ("CodeRateHP", frontendData["code_rate_hp"], TYPE_TEXT), - ("Constellation", frontendData["constellation"], TYPE_TEXT), - ("Transmission Mode", frontendData["transmission_mode"], TYPE_TEXT), - ("Guard Interval", frontendData["guard_interval"], TYPE_TEXT), - ("Hierarchy Inform.", frontendData["hierarchy_information"], TYPE_TEXT)) + return ((_("NIM"), ('A', 'B', 'C', 'D')[frontendData["tuner_number"]], TYPE_TEXT), + (_("Type"), frontendData["tuner_type"], TYPE_TEXT), + (_("Frequency"), frontendData["frequency"], TYPE_VALUE_DEC), + (_("Inversion"), frontendData["inversion"], TYPE_TEXT), + (_("Bandwidth"), frontendData["bandwidth"], TYPE_VALUE_DEC), + (_("Code rate LP"), frontendData["code_rate_lp"], TYPE_TEXT), + (_("Code rate HP"), frontendData["code_rate_hp"], TYPE_TEXT), + (_("Constellation"), frontendData["constellation"], TYPE_TEXT), + (_("Transmission mode"), frontendData["transmission_mode"], TYPE_TEXT), + (_("Guard interval"), frontendData["guard_interval"], TYPE_TEXT), + (_("Hierarchy info"), frontendData["hierarchy_information"], TYPE_TEXT)) return [ ] def fillList(self, Labels): @@ -237,6 +238,6 @@ class ServiceInfo(Screen): if v == -2: v = self.info.getInfoString(what) elif v == -1: - v = "N/A" + v = _("N/A") return v diff --git a/lib/python/Screens/ServiceScan.py b/lib/python/Screens/ServiceScan.py index 6ee35b84..df427f99 100644 --- a/lib/python/Screens/ServiceScan.py +++ b/lib/python/Screens/ServiceScan.py @@ -36,7 +36,7 @@ class ServiceScan(Screen): def cancel(self): self.close() - + def __init__(self, session, scanList): Screen.__init__(self, session) @@ -58,7 +58,7 @@ class ServiceScan(Screen): "ok": self.ok, "cancel": self.cancel }) - + self.onFirstExecBegin.append(self.doServiceScan) def doServiceScan(self): diff --git a/lib/python/Screens/Setup.py b/lib/python/Screens/Setup.py index e9c3c680..61e6fdd7 100755 --- a/lib/python/Screens/Setup.py +++ b/lib/python/Screens/Setup.py @@ -4,6 +4,7 @@ from Components.config import config, ConfigNothing from Components.SystemInfo import SystemInfo from Components.ConfigList import ConfigListScreen from Components.Sources.StaticText import StaticText +from enigma import eEnv import xml.etree.cElementTree @@ -14,7 +15,7 @@ try: setupfile = file('data/setup.xml', 'r') except: # if not found in the current path, we use the global datadir-path - setupfile = file('/usr/share/enigma2/setup.xml', 'r') + setupfile = file(eEnv.resolve('${datadir}/enigma2/setup.xml'), 'r') setupdom = xml.etree.cElementTree.parse(setupfile) setupfile.close() diff --git a/lib/python/Screens/SleepTimerEdit.py b/lib/python/Screens/SleepTimerEdit.py index e5e7af4e..61440d41 100644 --- a/lib/python/Screens/SleepTimerEdit.py +++ b/lib/python/Screens/SleepTimerEdit.py @@ -103,6 +103,7 @@ class SleepTimerEdit(Screen): config.SleepTimer.defaulttime.setValue(time) config.SleepTimer.defaulttime.save() config.SleepTimer.action.save() + config.SleepTimer.ask.save() self.session.nav.SleepTimer.setSleepTime(time) self.session.openWithCallback(self.close, MessageBox, _("The sleep timer has been activated."), MessageBox.TYPE_INFO) else: diff --git a/lib/python/Screens/Standby.py b/lib/python/Screens/Standby.py index 406b87cb..94a56ee4 100644 --- a/lib/python/Screens/Standby.py +++ b/lib/python/Screens/Standby.py @@ -3,6 +3,7 @@ from Components.ActionMap import ActionMap from Components.config import config from Components.AVSwitch import AVSwitch from Components.SystemInfo import SystemInfo +from GlobalActions import globalActionMap from enigma import eDVBVolumecontrol inStandby = None @@ -41,6 +42,8 @@ class Standby(Screen): "power": self.Power }, -1) + globalActionMap.setEnabled(False) + #mute adc self.setMute() @@ -72,6 +75,7 @@ class Standby(Screen): elif self.paused_service: self.paused_service.unPauseService() self.session.screen["Standby"].boolean = False + globalActionMap.setEnabled(True) def __onFirstExecBegin(self): global inStandby @@ -156,14 +160,9 @@ class TryQuitMainloop(MessageBox): self.conntected=False self.session.nav.record_event.remove(self.getRecordEvent) if value: - # hack .. we dont like to show any other screens when this screen has closed - self.onClose = [self.__closed] - self.session.dialog_stack = [] - self.session.summary_stack = [None] - MessageBox.close(self, True) - - def __closed(self): - quitMainloop(self.retval) + quitMainloop(self.retval) + else: + MessageBox.close(self, True) def __onShow(self): global inTryQuitMainloop diff --git a/lib/python/Screens/Subtitles.py b/lib/python/Screens/Subtitles.py deleted file mode 100644 index 9ba7e0a5..00000000 --- a/lib/python/Screens/Subtitles.py +++ /dev/null @@ -1,103 +0,0 @@ -from Screen import Screen -from Components.ServiceEventTracker import ServiceEventTracker -from Components.ActionMap import ActionMap -from Components.ConfigList import ConfigListScreen -from Components.config import config, getConfigListEntry, ConfigNothing -from enigma import iPlayableService - -from Tools.ISO639 import LanguageCodes - -class Subtitles(Screen, ConfigListScreen): - def __init__(self, session, infobar=None): - Screen.__init__(self, session) - - self["actions"] = ActionMap(["SetupActions"], - { - "ok": self.ok, - "cancel": self.cancel, - }, -2) - - self.list = [] - ConfigListScreen.__init__(self, self.list) - self.infobar = infobar or self.session.infobar - self.fillList() - - self.__event_tracker = ServiceEventTracker(screen=self, eventmap= - { - iPlayableService.evUpdatedInfo: self.__updatedInfo - }) - self.cached_subtitle_checked = False - self.__selected_subtitle = None - - def fillList(self): - list = self.list - del list[:] - print "self.list", list - if self.subtitlesEnabled(): - list.append(getConfigListEntry(_("Disable Subtitles"), ConfigNothing(), None)) - sel = self.infobar.selected_subtitle - else: - sel = None - for x in self.getSubtitleList(): - if sel and sel[:4] == x[:4]: #ignore Language code in compare - text = _("Running") - else: - text = _("Enable") - if x[0] == 0: - if LanguageCodes.has_key(x[4]): - list.append(getConfigListEntry(text+" DVB "+LanguageCodes[x[4]][0], ConfigNothing(), x)) - else: - list.append(getConfigListEntry(text+" DVB "+x[4], ConfigNothing(), x)) - elif x[0] == 1: - if x[4] == 'und': #undefined - list.append(getConfigListEntry(text+" TTX "+_("Page")+" %x%02x"%(x[3],x[2]), ConfigNothing(), x)) - else: - if LanguageCodes.has_key(x[4]): - list.append(getConfigListEntry(text+" TTX "+_("Page")+" %x%02x"%(x[3],x[2])+" "+LanguageCodes[x[4]][0], ConfigNothing(), x)) - else: - list.append(getConfigListEntry(text+" TTX "+_("Page")+" %x%02x"%(x[3],x[2])+" "+x[4], ConfigNothing(), x)) - elif x[0] == 2: - types = (" UTF-8 text "," SSA / AAS "," .SRT file ") - if x[4] == 'und': #undefined - list.append(getConfigListEntry(text+types[x[2]]+_("Subtitles")+" %d" % x[1], ConfigNothing(), x)) - else: - if LanguageCodes.has_key(x[4]): - list.append(getConfigListEntry(text+types[x[2]]+_("Subtitles") + ' ' + LanguageCodes[x[4]][0], ConfigNothing(), x)) - else: - list.append(getConfigListEntry(text+types[x[2]]+_("Subtitles")+" %d " % x[1] +x[4], ConfigNothing(), x)) -# return _("Disable subtitles") - self["config"].list = list - self["config"].l.setList(list) - - def __updatedInfo(self): - self.fillList() - - def getSubtitleList(self): - s = self.infobar and self.infobar.getCurrentServiceSubtitle() - l = s and s.getSubtitleList() or [ ] - return l - - def subtitlesEnabled(self): - return self.infobar.subtitles_enabled - - def enableSubtitle(self, subtitles): - if self.infobar.selected_subtitle != subtitles: - self.infobar.subtitles_enabled = False - self.infobar.selected_subtitle = subtitles - if subtitles: - self.infobar.subtitles_enabled = True - - def keyLeft(self): - ConfigListScreen.keyLeft(self) - - def keyRight(self): - ConfigListScreen.keyRight(self) - - def ok(self): - if self.list: - cur = self["config"].getCurrent() - self.enableSubtitle(cur[2]) - self.close(1) - - def cancel(self): - self.close() diff --git a/lib/python/Screens/TaskView.py b/lib/python/Screens/TaskView.py index eb926ca3..6e1b752d 100644 --- a/lib/python/Screens/TaskView.py +++ b/lib/python/Screens/TaskView.py @@ -7,7 +7,7 @@ import Screens.Standby from Tools import Notifications class JobView(InfoBarNotifications, Screen, ConfigListScreen): - def __init__(self, session, job, parent=None, cancelable = True, backgroundable = True, afterEvent = 0): + def __init__(self, session, job, parent=None, cancelable = True, backgroundable = True, afterEventChangeable = True): from Components.Sources.StaticText import StaticText from Components.Sources.Progress import Progress from Components.Sources.Boolean import Boolean @@ -43,19 +43,24 @@ class JobView(InfoBarNotifications, Screen, ConfigListScreen): "ok": self.ok, }, -2) - self.afterevents = [ "nothing", "standby", "deepstandby", "close" ] self.settings = ConfigSubsection() if SystemInfo["DeepstandbySupport"]: shutdownString = _("go to deep standby") else: shutdownString = _("shut down") - self.settings.afterEvent = ConfigSelection(choices = [("nothing", _("do nothing")), ("close", _("Close")), ("standby", _("go to standby")), ("deepstandby", shutdownString)], default = self.afterevents[afterEvent]) + self.settings.afterEvent = ConfigSelection(choices = [("nothing", _("do nothing")), ("close", _("Close")), ("standby", _("go to standby")), ("deepstandby", shutdownString)], default = self.job.afterEvent or "nothing") + self.job.afterEvent = self.settings.afterEvent.getValue() + self.afterEventChangeable = afterEventChangeable self.setupList() self.state_changed() def setupList(self): - self["config"].setList( [ getConfigListEntry(_("After event"), self.settings.afterEvent) ]) - + if self.afterEventChangeable: + self["config"].setList( [ getConfigListEntry(_("After event"), self.settings.afterEvent) ]) + else: + self["config"].hide() + self.job.afterEvent = self.settings.afterEvent.getValue() + def keyLeft(self): ConfigListScreen.keyLeft(self) self.setupList() @@ -103,17 +108,17 @@ class JobView(InfoBarNotifications, Screen, ConfigListScreen): self.close(False) def abort(self): - if self.job.status in (self.job.FINISHED, self.job.FAILED): - self.close(False) - if self["cancelable"].boolean == True: + if self.job.status == self.job.IN_PROGRESS and self["cancelable"].boolean == True: self.job.cancel() + else: + self.close(False) def performAfterEvent(self): self["config"].hide() if self.settings.afterEvent.getValue() == "nothing": return - elif self.settings.afterEvent.getValue() == "close": - self.abort() + elif self.settings.afterEvent.getValue() == "close" and self.job.status == self.job.FINISHED: + self.close(False) from Screens.MessageBox import MessageBox if self.settings.afterEvent.getValue() == "deepstandby": if not Screens.Standby.inTryQuitMainloop: @@ -122,6 +127,12 @@ class JobView(InfoBarNotifications, Screen, ConfigListScreen): if not Screens.Standby.inStandby: Notifications.AddNotificationWithCallback(self.sendStandbyNotification, MessageBox, _("A sleep timer wants to set your\nDreambox to standby. Do that now?"), timeout = 20) + def checkNotifications(self): + InfoBarNotifications.checkNotifications(self) + if Notifications.notifications == []: + if self.settings.afterEvent.getValue() == "close" and self.job.status == self.job.FAILED: + self.close(False) + def sendStandbyNotification(self, answer): if answer: Notifications.AddNotification(Screens.Standby.Standby) diff --git a/lib/python/Screens/TimerEdit.py b/lib/python/Screens/TimerEdit.py index 54354987..0abb42a8 100644 --- a/lib/python/Screens/TimerEdit.py +++ b/lib/python/Screens/TimerEdit.py @@ -13,6 +13,7 @@ from ServiceReference import ServiceReference from TimerEntry import TimerEntry, TimerLog from Tools.BoundFunction import boundFunction from time import time +from timer import TimerEntry as RealTimerEntry class TimerEditList(Screen): EMPTY = 0 @@ -88,7 +89,9 @@ class TimerEditList(Screen): if not timersanitycheck.check(): t.disable() print "Sanity check failed" - self.session.openWithCallback(self.finishedEdit, TimerSanityConflict, timersanitycheck.getSimulTimerList()) + simulTimerList = timersanitycheck.getSimulTimerList() + if simulTimerList is not None: + self.session.openWithCallback(self.finishedEdit, TimerSanityConflict, simulTimerList) else: print "Sanity check passed" if timersanitycheck.doubleCheck(): @@ -172,11 +175,20 @@ class TimerEditList(Screen): self.key_blue_choice = self.EMPTY def fillTimerList(self): + #helper function to move finished timers to end of list + def eol_compare(x, y): + if x[0].state != y[0].state and x[0].state == RealTimerEntry.StateEnded or y[0].state == RealTimerEntry.StateEnded: + return cmp(x[0].state, y[0].state) + return cmp(x[0].begin, y[0].begin) + list = self.list del list[:] list.extend([(timer, False) for timer in self.session.nav.RecordTimer.timer_list]) list.extend([(timer, True) for timer in self.session.nav.RecordTimer.processed_timers]) - list.sort(cmp = lambda x, y: x[0].begin < y[0].begin) + if config.usage.timerlist_finished_timer_position.index: #end of list + list.sort(cmp = eol_compare) + else: + list.sort(key = lambda x: x[0].begin) def showLog(self): cur=self["timerlist"].getCurrent() diff --git a/lib/python/Screens/TimerEntry.py b/lib/python/Screens/TimerEntry.py index b231b568..9885e700 100644 --- a/lib/python/Screens/TimerEntry.py +++ b/lib/python/Screens/TimerEntry.py @@ -13,8 +13,9 @@ from Components.UsageConfig import defaultMoviePath from Screens.MovieSelection import getPreferredTagEditor from Screens.LocationBox import MovieLocationBox from Screens.ChoiceBox import ChoiceBox +from Screens.MessageBox import MessageBox from RecordTimer import AFTEREVENT -from enigma import eEPGCache +from enigma import eEPGCache, eServiceReference from time import localtime, mktime, time, strftime from datetime import datetime @@ -106,10 +107,11 @@ class TimerEntry(Screen, ConfigListScreen): self.timerentry_tagsset = ConfigSelection(choices = [not self.timerentry_tags and "None" or " ".join(self.timerentry_tags)]) self.timerentry_repeated = ConfigSelection(default = repeated, choices = [("daily", _("daily")), ("weekly", _("weekly")), ("weekdays", _("Mon-Fri")), ("user", _("user defined"))]) - + self.timerentry_date = ConfigDateTime(default = self.timer.begin, formatstring = _("%d.%B %Y"), increment = 86400) self.timerentry_starttime = ConfigClock(default = self.timer.begin) self.timerentry_endtime = ConfigClock(default = self.timer.end) + self.timerentry_showendtime = ConfigSelection(default = ((self.timer.end - self.timer.begin) > 4), choices = [(True, _("yes")), (False, _("no"))]) default = self.timer.dirname or defaultMoviePath() tmp = config.movielist.videodirs.value @@ -172,11 +174,14 @@ class TimerEntry(Screen, ConfigListScreen): self.entryStartTime = getConfigListEntry(_("StartTime"), self.timerentry_starttime) self.list.append(self.entryStartTime) - if self.timerentry_justplay.value != "zap": - self.entryEndTime = getConfigListEntry(_("EndTime"), self.timerentry_endtime) + + self.entryShowEndTime = getConfigListEntry(_("Set End Time"), self.timerentry_showendtime) + if self.timerentry_justplay.value == "zap": + self.list.append(self.entryShowEndTime) + self.entryEndTime = getConfigListEntry(_("EndTime"), self.timerentry_endtime) + if self.timerentry_justplay.value != "zap" or self.timerentry_showendtime.value: self.list.append(self.entryEndTime) - else: - self.entryEndTime = None + self.channelEntry = getConfigListEntry(_("Channel"), self.timerentry_service) self.list.append(self.channelEntry) @@ -194,11 +199,7 @@ class TimerEntry(Screen, ConfigListScreen): def newConfig(self): print "newConfig", self["config"].getCurrent() - if self["config"].getCurrent() == self.timerTypeEntry: - self.createSetup("config") - if self["config"].getCurrent() == self.timerJustplayEntry: - self.createSetup("config") - if self["config"].getCurrent() == self.frequencyEntry: + if self["config"].getCurrent() in (self.timerTypeEntry, self.timerJustplayEntry, self.frequencyEntry, self.entryShowEndTime): self.createSetup("config") def keyLeft(self): @@ -245,7 +246,7 @@ class TimerEntry(Screen, ConfigListScreen): self.timerentry_service_ref = ServiceReference(args[0]) self.timerentry_service.setCurrentText(self.timerentry_service_ref.getServiceName()) self["config"].invalidate(self.channelEntry) - + def getTimestamp(self, date, mytime): d = localtime(date) dt = datetime(d.tm_year, d.tm_mon, d.tm_mday, mytime[0], mytime[1]) @@ -264,10 +265,28 @@ class TimerEntry(Screen, ConfigListScreen): end += 86400 return begin, end - def keyGo(self): + def selectChannelSelector(self, *args): + self.session.openWithCallback( + self.finishedChannelSelectionCorrection, + ChannelSelection.SimpleChannelSelection, + _("Select channel to record from") + ) + + def finishedChannelSelectionCorrection(self, *args): + if args: + self.finishedChannelSelection(*args) + self.keyGo() + + def keyGo(self, result = None): + if not self.timerentry_service_ref.isRecordable(): + self.session.openWithCallback(self.selectChannelSelector, MessageBox, _("You didn't select a channel to record from."), MessageBox.TYPE_ERROR) + return self.timer.name = self.timerentry_name.value self.timer.description = self.timerentry_description.value self.timer.justplay = self.timerentry_justplay.value == "zap" + if self.timerentry_justplay.value == "zap": + if not self.timerentry_showendtime.value: + self.timerentry_endtime.value = self.timerentry_starttime.value self.timer.resetRepeated() self.timer.afterEvent = { "nothing": AFTEREVENT.NONE, diff --git a/lib/python/Screens/VirtualKeyBoard.py b/lib/python/Screens/VirtualKeyBoard.py index 7846e4b8..a8695439 100755 --- a/lib/python/Screens/VirtualKeyBoard.py +++ b/lib/python/Screens/VirtualKeyBoard.py @@ -1,12 +1,13 @@ -# -*- coding: iso-8859-1 -*- +# -*- coding: UTF-8 -*- +from enigma import eListboxPythonMultiContent, gFont, RT_HALIGN_CENTER, RT_VALIGN_CENTER, getPrevAsciiCode +from Screen import Screen from Components.Language import language from Components.ActionMap import ActionMap +from Components.Sources.StaticText import StaticText from Components.Label import Label from Components.Pixmap import Pixmap from Components.MenuList import MenuList from Components.MultiContent import MultiContentEntryText, MultiContentEntryPixmapAlphaTest -from enigma import eListboxPythonMultiContent, gFont, RT_HALIGN_CENTER, RT_VALIGN_CENTER -from Screen import Screen from Tools.Directories import resolveFilename, SCOPE_CURRENT_SKIN from Tools.LoadPixmap import LoadPixmap @@ -86,52 +87,127 @@ class VirtualKeyBoard(Screen): self.keys_list = [] self.shiftkeys_list = [] self.lang = language.getLanguage() + self.nextLang = None + self.shiftMode = False + self.text = text + self.selectedKey = 0 + + self["country"] = StaticText("") + self["header"] = Label(title) + self["text"] = Label(self.text) + self["list"] = VirtualKeyBoardList([]) + + self["actions"] = ActionMap(["OkCancelActions", "WizardActions", "ColorActions", "KeyboardInputActions", "InputBoxActions", "InputAsciiActions"], + { + "gotAsciiCode": self.keyGotAscii, + "ok": self.okClicked, + "cancel": self.exit, + "left": self.left, + "right": self.right, + "up": self.up, + "down": self.down, + "red": self.backClicked, + "green": self.ok, + "yellow": self.switchLang, + "deleteBackward": self.backClicked, + "back": self.exit + }, -2) + self.setLang() + self.onExecBegin.append(self.setKeyboardModeAscii) + self.onLayoutFinish.append(self.buildVirtualKeyBoard) + + def switchLang(self): + self.lang = self.nextLang + self.setLang() + self.buildVirtualKeyBoard() + + def setLang(self): if self.lang == 'de_DE': self.keys_list = [ [u"EXIT", u"1", u"2", u"3", u"4", u"5", u"6", u"7", u"8", u"9", u"0", u"BACKSPACE"], - [u"q", u"w", u"e", u"r", u"t", u"z", u"u", u"i", u"o", u"p", u"ü", u"+"], - [u"a", u"s", u"d", u"f", u"g", u"h", u"j", u"k", u"l", u"ö", u"ä", u"#"], + [u"q", u"w", u"e", u"r", u"t", u"z", u"u", u"i", u"o", u"p", u"ü", u"+"], + [u"a", u"s", u"d", u"f", u"g", u"h", u"j", u"k", u"l", u"ö", u"ä", u"#"], [u"<", u"y", u"x", u"c", u"v", u"b", u"n", u"m", u",", ".", u"-", u"CLEAR"], - [u"SHIFT", u"SPACE", u"@", u"ß", u"OK"]] - + [u"SHIFT", u"SPACE", u"@", u"ß", u"OK"]] self.shiftkeys_list = [ - [u"EXIT", u"!", u'"', u"§", u"$", u"%", u"&", u"/", u"(", u")", u"=", u"BACKSPACE"], - [u"Q", u"W", u"E", u"R", u"T", u"Z", u"U", u"I", u"O", u"P", u"Ü", u"*"], - [u"A", u"S", u"D", u"F", u"G", u"H", u"J", u"K", u"L", u"Ö", u"Ä", u"'"], + [u"EXIT", u"!", u'"', u"§", u"$", u"%", u"&", u"/", u"(", u")", u"=", u"BACKSPACE"], + [u"Q", u"W", u"E", u"R", u"T", u"Z", u"U", u"I", u"O", u"P", u"Ü", u"*"], + [u"A", u"S", u"D", u"F", u"G", u"H", u"J", u"K", u"L", u"Ö", u"Ä", u"'"], [u">", u"Y", u"X", u"C", u"V", u"B", u"N", u"M", u";", u":", u"_", u"CLEAR"], [u"SHIFT", u"SPACE", u"?", u"\\", u"OK"]] - + self.nextLang = 'es_ES' elif self.lang == 'es_ES': - #still missing keys (u"ùÙ") + #still missing keys (u"ùÙ") self.keys_list = [ [u"EXIT", u"1", u"2", u"3", u"4", u"5", u"6", u"7", u"8", u"9", u"0", u"BACKSPACE"], - [u"q", u"w", u"e", u"r", u"t", u"z", u"u", u"i", u"o", u"p", u"ú", u"+"], - [u"a", u"s", u"d", u"f", u"g", u"h", u"j", u"k", u"l", u"ó", u"á", u"#"], + [u"q", u"w", u"e", u"r", u"t", u"z", u"u", u"i", u"o", u"p", u"ú", u"+"], + [u"a", u"s", u"d", u"f", u"g", u"h", u"j", u"k", u"l", u"ó", u"á", u"#"], [u"<", u"y", u"x", u"c", u"v", u"b", u"n", u"m", u",", ".", u"-", u"CLEAR"], - [u"SHIFT", u"SPACE", u"@", u"£", u"à", u"é", u"è", u"í", u"ì", u"ñ", u"ò", u"OK"]] - + [u"SHIFT", u"SPACE", u"@", u"Å", u"Å•", u"é", u"Ä", u"Ã", u"Ä›", u"Å„", u"ň", u"OK"]] self.shiftkeys_list = [ - [u"EXIT", u"!", u'"', u"§", u"$", u"%", u"&", u"/", u"(", u")", u"=", u"BACKSPACE"], - [u"Q", u"W", u"E", u"R", u"T", u"Z", u"U", u"I", u"O", u"P", u"Ú", u"*"], - [u"A", u"S", u"D", u"F", u"G", u"H", u"J", u"K", u"L", u"Ó", u"Á", u"'"], + [u"EXIT", u"!", u'"', u"§", u"$", u"%", u"&", u"/", u"(", u")", u"=", u"BACKSPACE"], + [u"Q", u"W", u"E", u"R", u"T", u"Z", u"U", u"I", u"O", u"P", u"Ú", u"*"], + [u"A", u"S", u"D", u"F", u"G", u"H", u"J", u"K", u"L", u"Ó", u"Ã", u"'"], [u">", u"Y", u"X", u"C", u"V", u"B", u"N", u"M", u";", u":", u"_", u"CLEAR"], - [u"SHIFT", u"SPACE", u"?", u"\\", u"À", u"É", u"È", u"Í", u"Ì", u"Ñ", u"Ò", u"OK"]] - - elif self.lang in ('sv_SE', 'fi_FI'): + [u"SHIFT", u"SPACE", u"?", u"\\", u"Å”", u"É", u"ÄŒ", u"Ã", u"Äš", u"Ń", u"Ň", u"OK"]] + self.nextLang = 'fi_FI' + elif self.lang == 'fi_FI': self.keys_list = [ [u"EXIT", u"1", u"2", u"3", u"4", u"5", u"6", u"7", u"8", u"9", u"0", u"BACKSPACE"], - [u"q", u"w", u"e", u"r", u"t", u"z", u"u", u"i", u"o", u"p", u"é", u"+"], - [u"a", u"s", u"d", u"f", u"g", u"h", u"j", u"k", u"l", u"ö", u"ä", u"#"], + [u"q", u"w", u"e", u"r", u"t", u"z", u"u", u"i", u"o", u"p", u"é", u"+"], + [u"a", u"s", u"d", u"f", u"g", u"h", u"j", u"k", u"l", u"ö", u"ä", u"#"], [u"<", u"y", u"x", u"c", u"v", u"b", u"n", u"m", u",", ".", u"-", u"CLEAR"], - [u"SHIFT", u"SPACE", u"@", u"ß", u"å", u"OK"]] - + [u"SHIFT", u"SPACE", u"@", u"ß", u"ĺ", u"OK"]] self.shiftkeys_list = [ - [u"EXIT", u"!", u'"', u"§", u"$", u"%", u"&", u"/", u"(", u")", u"=", u"BACKSPACE"], - [u"Q", u"W", u"E", u"R", u"T", u"Z", u"U", u"I", u"O", u"P", u"É", u"*"], - [u"A", u"S", u"D", u"F", u"G", u"H", u"J", u"K", u"L", u"Ö", u"Ä", u"'"], + [u"EXIT", u"!", u'"', u"§", u"$", u"%", u"&", u"/", u"(", u")", u"=", u"BACKSPACE"], + [u"Q", u"W", u"E", u"R", u"T", u"Z", u"U", u"I", u"O", u"P", u"É", u"*"], + [u"A", u"S", u"D", u"F", u"G", u"H", u"J", u"K", u"L", u"Ö", u"Ä", u"'"], [u">", u"Y", u"X", u"C", u"V", u"B", u"N", u"M", u";", u":", u"_", u"CLEAR"], - - [u"SHIFT", u"SPACE", u"?", u"\\", u"Å", u"OK"]] + [u"SHIFT", u"SPACE", u"?", u"\\", u"Ĺ", u"OK"]] + self.nextLang = 'sv_SE' + elif self.lang == 'sv_SE': + self.keys_list = [ + [u"EXIT", u"1", u"2", u"3", u"4", u"5", u"6", u"7", u"8", u"9", u"0", u"BACKSPACE"], + [u"q", u"w", u"e", u"r", u"t", u"z", u"u", u"i", u"o", u"p", u"é", u"+"], + [u"a", u"s", u"d", u"f", u"g", u"h", u"j", u"k", u"l", u"ö", u"ä", u"#"], + [u"<", u"y", u"x", u"c", u"v", u"b", u"n", u"m", u",", ".", u"-", u"CLEAR"], + [u"SHIFT", u"SPACE", u"@", u"ß", u"ĺ", u"OK"]] + self.shiftkeys_list = [ + [u"EXIT", u"!", u'"', u"§", u"$", u"%", u"&", u"/", u"(", u")", u"=", u"BACKSPACE"], + [u"Q", u"W", u"E", u"R", u"T", u"Z", u"U", u"I", u"O", u"P", u"É", u"*"], + [u"A", u"S", u"D", u"F", u"G", u"H", u"J", u"K", u"L", u"Ö", u"Ä", u"'"], + [u">", u"Y", u"X", u"C", u"V", u"B", u"N", u"M", u";", u":", u"_", u"CLEAR"], + [u"SHIFT", u"SPACE", u"?", u"\\", u"Ĺ", u"OK"]] + self.nextLang = 'sk_SK' + elif self.lang =='sk_SK': + self.keys_list = [ + [u"EXIT", u"1", u"2", u"3", u"4", u"5", u"6", u"7", u"8", u"9", u"0", u"BACKSPACE"], + [u"q", u"w", u"e", u"r", u"t", u"z", u"u", u"i", u"o", u"p", u"ú", u"+"], + [u"a", u"s", u"d", u"f", u"g", u"h", u"j", u"k", u"l", u"ľ", u"@", u"#"], + [u"<", u"y", u"x", u"c", u"v", u"b", u"n", u"m", u",", ".", u"-", u"CLEAR"], + [u"SHIFT", u"SPACE", u"Å¡", u"Ä", u"ž", u"ý", u"á", u"Ã", u"é", u"OK"]] + self.shiftkeys_list = [ + [u"EXIT", u"!", u'"', u"§", u"$", u"%", u"&", u"/", u"(", u")", u"=", u"BACKSPACE"], + [u"Q", u"W", u"E", u"R", u"T", u"Z", u"U", u"I", u"O", u"P", u"Å¥", u"*"], + [u"A", u"S", u"D", u"F", u"G", u"H", u"J", u"K", u"L", u"ň", u"Ä", u"'"], + [u"Ã", u"É", u"ÄŽ", u"Ã", u"Ã", u"Ó", u"Ú", u"Ž", u"Å ", u"ÄŒ", u"Ť", u"Ň"], + [u">", u"Y", u"X", u"C", u"V", u"B", u"N", u"M", u";", u":", u"_", u"CLEAR"], + [u"SHIFT", u"SPACE", u"?", u"\\", u"ä", u"ö", u"ü", u"ô", u"Å•", u"ĺ", u"OK"]] + self.nextLang = 'cs_CZ' + elif self.lang == 'cs_CZ': + self.keys_list = [ + [u"EXIT", u"1", u"2", u"3", u"4", u"5", u"6", u"7", u"8", u"9", u"0", u"BACKSPACE"], + [u"q", u"w", u"e", u"r", u"t", u"z", u"u", u"i", u"o", u"p", u"ú", u"+"], + [u"a", u"s", u"d", u"f", u"g", u"h", u"j", u"k", u"l", u"ů", u"@", u"#"], + [u"<", u"y", u"x", u"c", u"v", u"b", u"n", u"m", u",", ".", u"-", u"CLEAR"], + [u"SHIFT", u"SPACE", u"Ä›", u"Å¡", u"Ä", u"Å™", u"ž", u"ý", u"á", u"Ã", u"é", u"OK"]] + self.shiftkeys_list = [ + [u"EXIT", u"!", u'"', u"§", u"$", u"%", u"&", u"/", u"(", u")", u"=", u"BACKSPACE"], + [u"Q", u"W", u"E", u"R", u"T", u"Z", u"U", u"I", u"O", u"P", u"Å¥", u"*"], + [u"A", u"S", u"D", u"F", u"G", u"H", u"J", u"K", u"L", u"ň", u"Ä", u"'"], + [u">", u"Y", u"X", u"C", u"V", u"B", u"N", u"M", u";", u":", u"_", u"CLEAR"], + [u"SHIFT", u"SPACE", u"?", u"\\", u"ÄŒ", u"Ř", u"Å ", u"Ž", u"Ú", u"Ã", u"É", u"OK"]] + self.nextLang = 'en_EN' else: self.keys_list = [ [u"EXIT", u"1", u"2", u"3", u"4", u"5", u"6", u"7", u"8", u"9", u"0", u"BACKSPACE"], @@ -139,36 +215,15 @@ class VirtualKeyBoard(Screen): [u"a", u"s", u"d", u"f", u"g", u"h", u"j", u"k", u"l", u"#", u"\\"], [u"<", u"y", u"x", u"c", u"v", u"b", u"n", u"m", u",", ".", u"-", u"CLEAR"], [u"SHIFT", u"SPACE", u"OK"]] - self.shiftkeys_list = [ - [u"EXIT", u"!", u'"', u"§", u"$", u"%", u"&", u"/", u"(", u")", u"=", u"BACKSPACE"], + [u"EXIT", u"!", u'"', u"§", u"$", u"%", u"&", u"/", u"(", u")", u"=", u"BACKSPACE"], [u"Q", u"W", u"E", u"R", u"T", u"Z", u"U", u"I", u"O", u"P", u"*"], [u"A", u"S", u"D", u"F", u"G", u"H", u"J", u"K", u"L", u"'", u"?"], [u">", u"Y", u"X", u"C", u"V", u"B", u"N", u"M", u";", u":", u"_", u"CLEAR"], [u"SHIFT", u"SPACE", u"OK"]] - - self.shiftMode = False - self.text = text - self.selectedKey = 0 - - self["header"] = Label(title) - self["text"] = Label(self.text) - self["list"] = VirtualKeyBoardList([]) - - self["actions"] = ActionMap(["OkCancelActions", "WizardActions", "ColorActions"], - { - "ok": self.okClicked, - "cancel": self.exit, - "left": self.left, - "right": self.right, - "up": self.up, - "down": self.down, - "red": self.backClicked, - "green": self.ok - }, -2) - - self.onLayoutFinish.append(self.buildVirtualKeyBoard) - + self.lang = 'en_EN' + self.nextLang = 'de_DE' + self["country"].setText(self.lang) self.max_key=47+len(self.keys_list[4]) def buildVirtualKeyBoard(self, selectedKey=0): @@ -192,7 +247,6 @@ class VirtualKeyBoard(Screen): selectedKey -= 12 self["list"].setList(list) - def backClicked(self): self.text = self["text"].getText()[:-1] @@ -219,7 +273,7 @@ class VirtualKeyBoard(Screen): if text is None: return - text = text.encode("utf-8") + text = text.encode("UTF-8") if text == "EXIT": self.close(None) @@ -312,3 +366,33 @@ class VirtualKeyBoard(Screen): def showActiveKey(self): self.buildVirtualKeyBoard(self.selectedKey) + + def inShiftKeyList(self,key): + for KeyList in self.shiftkeys_list: + for char in KeyList: + if char == key: + return True + return False + + def keyGotAscii(self): + char = str(unichr(getPrevAsciiCode()).encode('utf-8')) + if self.inShiftKeyList(char): + self.shiftMode = True + list = self.shiftkeys_list + else: + self.shiftMode = False + list = self.keys_list + + if char == " ": + char = "SPACE" + + selkey = 0 + for keylist in list: + for key in keylist: + if key == char: + self.selectedKey = selkey + self.okClicked() + self.showActiveKey() + return + else: + selkey += 1 diff --git a/lib/python/Screens/Wizard.py b/lib/python/Screens/Wizard.py index 1bff0284..61e1d5ef 100755 --- a/lib/python/Screens/Wizard.py +++ b/lib/python/Screens/Wizard.py @@ -10,7 +10,7 @@ from Components.ActionMap import NumberActionMap from Components.MenuList import MenuList from Components.ConfigList import ConfigList from Components.Sources.List import List -from enigma import eTimer +from enigma import eTimer, eEnv from xml.sax import make_parser from xml.sax.handler import ContentHandler @@ -178,7 +178,7 @@ class Wizard(Screen): parser.setContentHandler(wizardHandler) for xmlfile in self.xmlfile: if xmlfile[0] != '/': - parser.parse('/usr/share/enigma2/' + xmlfile) + parser.parse(eEnv.resolve('${datadir}/enigma2/') + xmlfile) else: parser.parse(xmlfile) @@ -387,10 +387,14 @@ class Wizard(Screen): def keyNumberGlobal(self, number): if (self.wizard[self.currStep]["config"]["screen"] != None): self.configInstance.keyNumberGlobal(number) + elif (self.wizard[self.currStep]["config"]["type"] == "dynamic"): + self["config"].handleKey(KEY_0 + number) def keyGotAscii(self): if (self.wizard[self.currStep]["config"]["screen"] != None): self["config"].handleKey(KEY_ASCII) + elif (self.wizard[self.currStep]["config"]["type"] == "dynamic"): + self["config"].handleKey(KEY_ASCII) def left(self): self.resetCounter() diff --git a/lib/python/Tools/Directories.py b/lib/python/Tools/Directories.py index 8ed2c8a4..f0ef0de2 100755 --- a/lib/python/Tools/Directories.py +++ b/lib/python/Tools/Directories.py @@ -3,6 +3,7 @@ from os import path as os_path, mkdir, rmdir, system, walk, stat as os_stat, listdir, readlink, makedirs, error as os_error, symlink, access, F_OK, R_OK, W_OK from stat import S_IMODE from re import compile +from enigma import eEnv try: from os import chmod @@ -39,26 +40,26 @@ PATH_CREATE = 0 PATH_DONTCREATE = 1 PATH_FALLBACK = 2 defaultPaths = { - SCOPE_TRANSPONDERDATA: ("/etc/", PATH_DONTCREATE), - SCOPE_SYSETC: ("/etc/", PATH_DONTCREATE), - SCOPE_FONTS: ("/usr/share/fonts/", PATH_DONTCREATE), - SCOPE_CONFIG: ("/etc/enigma2/", PATH_CREATE), - SCOPE_PLUGINS: ("/usr/lib/enigma2/python/Plugins/", PATH_CREATE), + SCOPE_TRANSPONDERDATA: (eEnv.resolve("${sysconfdir}/"), PATH_DONTCREATE), + SCOPE_SYSETC: (eEnv.resolve("${sysconfdir}/"), PATH_DONTCREATE), + SCOPE_FONTS: (eEnv.resolve("${datadir}/fonts/"), PATH_DONTCREATE), + SCOPE_CONFIG: (eEnv.resolve("${sysconfdir}/enigma2/"), PATH_CREATE), + SCOPE_PLUGINS: (eEnv.resolve("${libdir}/enigma2/python/Plugins/"), PATH_CREATE), - SCOPE_LANGUAGE: ("/usr/share/enigma2/po/", PATH_DONTCREATE), + SCOPE_LANGUAGE: (eEnv.resolve("${datadir}/enigma2/po/"), PATH_DONTCREATE), - SCOPE_SKIN: ("/usr/share/enigma2/", PATH_DONTCREATE), - SCOPE_SKIN_IMAGE: ("/usr/share/enigma2/", PATH_DONTCREATE), + SCOPE_SKIN: (eEnv.resolve("${datadir}/enigma2/"), PATH_DONTCREATE), + SCOPE_SKIN_IMAGE: (eEnv.resolve("${datadir}/enigma2/"), PATH_DONTCREATE), SCOPE_HDD: ("/hdd/movie/", PATH_DONTCREATE), SCOPE_MEDIA: ("/media/", PATH_DONTCREATE), - SCOPE_PLAYLIST: ("/etc/enigma2/playlist/", PATH_CREATE), + SCOPE_PLAYLIST: (eEnv.resolve("${sysconfdir}/enigma2/playlist/"), PATH_CREATE), SCOPE_USERETC: ("", PATH_DONTCREATE), # user home directory - SCOPE_DEFAULTDIR: ("/usr/share/enigma2/defaults/", PATH_CREATE), - SCOPE_DEFAULTPARTITION: ("/dev/mtdblock/6", PATH_DONTCREATE), - SCOPE_DEFAULTPARTITIONMOUNTDIR: ("/usr/share/enigma2/dealer", PATH_CREATE), - SCOPE_METADIR: ("/usr/share/meta", PATH_CREATE), + SCOPE_DEFAULTDIR: (eEnv.resolve("${datadir}/enigma2/defaults/"), PATH_CREATE), + SCOPE_DEFAULTPARTITION: ("/dev/mtdblock6", PATH_DONTCREATE), + SCOPE_DEFAULTPARTITIONMOUNTDIR: (eEnv.resolve("${datadir}/enigma2/dealer"), PATH_CREATE), + SCOPE_METADIR: (eEnv.resolve("${datadir}/meta"), PATH_CREATE), } FILE_COPY = 0 # copy files from fallback dir to the basedir @@ -67,7 +68,7 @@ PATH_COPY = 2 # copy the complete fallback dir to the basedir PATH_MOVE = 3 # move the fallback dir to the basedir (can be used for changes in paths) fallbackPaths = { SCOPE_CONFIG: [("/home/root/", FILE_MOVE), - ("/usr/share/enigma2/defaults/", FILE_COPY)], + (eEnv.resolve("${datadir}/enigma2/defaults/"), FILE_COPY)], SCOPE_HDD: [("/hdd/movies", PATH_MOVE)] } @@ -117,7 +118,11 @@ def resolveFilename(scope, base = "", path_prefix = None): if flags == PATH_CREATE: if not pathExists(path): - mkdir(path) + try: + mkdir(path) + except OSError: + print "resolveFilename: Couldn't create %s" % path + return None fallbackPath = fallbackPaths.get(scope) @@ -223,12 +228,13 @@ def InitFallbackFiles(): # returns a list of tuples containing pathname and filename matching the given pattern # example-pattern: match all txt-files: ".*\.txt$" def crawlDirectory(directory, pattern): - expression = compile(pattern) list = [] - for root, dirs, files in walk(directory): - for file in files: - if expression.match(file) is not None: - list.append((root, file)) + if directory: + expression = compile(pattern) + for root, dirs, files in walk(directory): + for file in files: + if expression.match(file) is not None: + list.append((root, file)) return list def copyfile(src, dst): diff --git a/lib/python/Tools/NumericalTextInput.py b/lib/python/Tools/NumericalTextInput.py index df6a5ea8..252e174e 100644 --- a/lib/python/Tools/NumericalTextInput.py +++ b/lib/python/Tools/NumericalTextInput.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# -*- coding: UTF-8 -*- from enigma import eTimer from Components.Language import language @@ -31,19 +31,19 @@ class NumericalTextInput: return if self.lang == 'de_DE': - self.mapping.append (u".,?'+\"0-()@/:_$!") # 0 - self.mapping.append (u" 1") # 1 - self.mapping.append (u"aäbc2AÄBC") # 2 + self.mapping.append (u"0,?!&@=*'+\"()$~") # 0 + self.mapping.append (u" 1.:/-_") # 1 + self.mapping.append (u"abcä2ABCÄ") # 2 self.mapping.append (u"def3DEF") # 3 self.mapping.append (u"ghi4GHI") # 4 self.mapping.append (u"jkl5JKL") # 5 self.mapping.append (u"mnoö6MNOÖ") # 6 self.mapping.append (u"pqrsß7PQRSß") # 7 - self.mapping.append (u"tuüv8TUÜV") # 8 + self.mapping.append (u"tuvü8TUVÜ") # 8 self.mapping.append (u"wxyz9WXYZ") # 9 elif self.lang == 'es_ES': - self.mapping.append (u".,?'+\"0-()@/:_$!") # 0 - self.mapping.append (u" 1") # 1 + self.mapping.append (u"0,?!&@=*'+\"()$~") # 0 + self.mapping.append (u" 1.:/-_") # 1 self.mapping.append (u"abcáà 2ABCÃÀ") # 2 self.mapping.append (u"deéèf3DEFÉÈ") # 3 self.mapping.append (u"ghiÃì4GHIÃÃŒ") # 4 @@ -53,8 +53,8 @@ class NumericalTextInput: self.mapping.append (u"tuvúù8TUVÚÙ") # 8 self.mapping.append (u"wxyz9WXYZ") # 9 if self.lang in ('sv_SE', 'fi_FI'): - self.mapping.append (u".,?'+\"0-()@/:_$!") # 0 - self.mapping.append (u" 1") # 1 + self.mapping.append (u"0,?!&@=*'+\"()$~") # 0 + self.mapping.append (u" 1.:/-_") # 1 self.mapping.append (u"abcåä2ABCÅÄ") # 2 self.mapping.append (u"defé3DEFÉ") # 3 self.mapping.append (u"ghi4GHI") # 4 @@ -63,9 +63,20 @@ class NumericalTextInput: self.mapping.append (u"pqrs7PQRS") # 7 self.mapping.append (u"tuv8TUV") # 8 self.mapping.append (u"wxyz9WXYZ") # 9 + elif self.lang in ('cs_CZ', 'sk_SK'): + self.mapping.append (u"0,?'+\"()@$!=&*") # 0 + self.mapping.append (u" 1.:/-_") # 1 + self.mapping.append (u"abc2áäÄABCÃÄČ") # 2 + self.mapping.append (u"def3ÄéěDEFĎÉĚ") # 3 + self.mapping.append (u"ghi4ÃGHIÃ") # 4 + self.mapping.append (u"jkl5ľĺJKLĽĹ") # 5 + self.mapping.append (u"mno6ňóöôMNOŇÓÖÔ") # 6 + self.mapping.append (u"pqrs7řŕšPQRSŘŔŠ") # 7 + self.mapping.append (u"tuv8ťúůüTUVŤÚŮÜ") # 8 + self.mapping.append (u"wxyz9ýžWXYZÃŽ") # 9 else: - self.mapping.append (u".,?'+\"0-()@/:_$!") # 0 - self.mapping.append (u" 1") # 1 + self.mapping.append (u"0,?!&@=*'+\"()$~") # 0 + self.mapping.append (u" 1.:/-_") # 1 self.mapping.append (u"abc2ABC") # 2 self.mapping.append (u"def3DEF") # 3 self.mapping.append (u"ghi4GHI") # 4 @@ -76,7 +87,8 @@ class NumericalTextInput: self.mapping.append (u"wxyz9WXYZ") # 9 def setUseableChars(self, useable): - self.useableChars = useable + self.useableChars = unicode(useable) + def getKey(self, num): cnt=0 diff --git a/lib/python/Tools/Transponder.py b/lib/python/Tools/Transponder.py index 6868929c..0b8b96ad 100644 --- a/lib/python/Tools/Transponder.py +++ b/lib/python/Tools/Transponder.py @@ -1,4 +1,5 @@ from enigma import eDVBFrontendParametersSatellite, eDVBFrontendParametersCable, eDVBFrontendParametersTerrestrial +from Components.NimManager import nimmanager def ConvertToHumanReadable(tp, type = None): ret = { } @@ -27,6 +28,7 @@ def ConvertToHumanReadable(tp, type = None): eDVBFrontendParametersSatellite.Modulation_QPSK : "QPSK", eDVBFrontendParametersSatellite.Modulation_QAM16 : "QAM16", eDVBFrontendParametersSatellite.Modulation_8PSK : "8PSK"}[tp["modulation"]] + ret["orbital_position"] = nimmanager.getSatName(int(tp["orbital_position"])) ret["polarization"] = { eDVBFrontendParametersSatellite.Polarisation_Horizontal : _("Horizontal"), eDVBFrontendParametersSatellite.Polarisation_Vertical : _("Vertical"), diff --git a/lib/python/connections.h b/lib/python/connections.h index ad76198a..374749ab 100644 --- a/lib/python/connections.h +++ b/lib/python/connections.h @@ -3,11 +3,6 @@ #include <libsig_comp.h> - /* avoid warnigs :) */ -#include <features.h> -#undef _POSIX_C_SOURCE -#define _POSIX_C_SOURCE 200112L - #include <lib/python/python.h> class PSignal @@ -85,4 +80,23 @@ public: } }; +template <class R, class V0, class V1, class V2> +class PSignal3: public PSignal, public Signal3<R,V0,V1,V2> +{ +public: + R operator()(V0 a0, V1 a1, V2 a2) + { + if (m_list) + { + PyObject *pArgs = PyTuple_New(3); + PyTuple_SET_ITEM(pArgs, 0, PyFrom(a0)); + PyTuple_SET_ITEM(pArgs, 1, PyFrom(a1)); + PyTuple_SET_ITEM(pArgs, 2, PyFrom(a2)); + callPython(pArgs); + Org_Py_DECREF(pArgs); + } + return Signal3<R,V0,V1,V2>::operator()(a0, a1, a2); + } +}; + #endif diff --git a/lib/python/enigma_python.i b/lib/python/enigma_python.i index 2fec2ff1..7c999277 100644 --- a/lib/python/enigma_python.i +++ b/lib/python/enigma_python.i @@ -38,7 +38,9 @@ is usually caused by not marking PSignals as immutable. #define SWIG_COMPILE #include <lib/base/ebase.h> #include <lib/base/smartptr.h> +#include <lib/base/eenv.h> #include <lib/base/eerror.h> +#include <lib/base/etpm.h> #include <lib/base/nconfig.h> #include <lib/base/message.h> #include <lib/driver/rc.h> @@ -49,7 +51,7 @@ is usually caused by not marking PSignals as immutable. #include <lib/gdi/fb.h> #include <lib/gdi/font.h> #include <lib/gdi/gpixmap.h> -#include <lib/gdi/gfbdc.h> +#include <lib/gdi/gmaindc.h> #include <lib/gui/ewidget.h> #include <lib/gui/elabel.h> #include <lib/gui/einput.h> @@ -132,6 +134,7 @@ typedef long time_t; %include "std_string.i" %include <lib/python/swig.h> %include <lib/base/object.h> +%include <lib/base/eenv.h> %include <lib/base/eerror.h> %immutable eSocketNotifier::activated; @@ -157,12 +160,13 @@ typedef long time_t; %immutable ePythonMessagePump::recv_msg; %immutable eDVBLocalTimeHandler::m_timeUpdated; %include <lib/base/message.h> +%include <lib/base/etpm.h> %include <lib/base/nconfig.h> %include <lib/driver/rc.h> %include <lib/gdi/fb.h> %include <lib/gdi/font.h> %include <lib/gdi/gpixmap.h> -%include <lib/gdi/gfbdc.h> +%include <lib/gdi/gmaindc.h> %include <lib/gdi/epoint.h> %include <lib/gdi/erect.h> %include <lib/gdi/esize.h> diff --git a/lib/python/python.h b/lib/python/python.h index 18fdac78..f5b6e4d8 100644 --- a/lib/python/python.h +++ b/lib/python/python.h @@ -4,7 +4,6 @@ #define __lib_python_python_class_h #endif -#include <Python.h> #include <string> #include <lib/base/object.h> diff --git a/lib/service/Makefile.am b/lib/service/Makefile.am index ed09d73d..9f956b66 100644 --- a/lib/service/Makefile.am +++ b/lib/service/Makefile.am @@ -1,9 +1,39 @@ -INCLUDES = \ - -I$(top_srcdir)/include +AM_CPPFLAGS = \ + -I$(top_srcdir) \ + -I$(top_srcdir)/include \ + -include Python.h \ + -include $(top_builddir)/enigma2_config.h + +AM_CXXFLAGS = \ + $(LIBXINE_CFLAGS) noinst_LIBRARIES = libenigma_service.a libenigma_service_a_SOURCES = \ - listboxservice.cpp service.cpp servicemp3.cpp servicedvb.cpp servicefs.cpp \ - event.cpp servicedvbrecord.cpp - + event.cpp \ + listboxservice.cpp \ + service.cpp \ + servicedvb.cpp \ + servicedvbrecord.cpp \ + servicefs.cpp \ + servicemp3.cpp \ + servicem2ts.cpp + +serviceincludedir = $(pkgincludedir)/lib/service +serviceinclude_HEADERS = \ + event.h \ + iservice.h \ + listboxservice.h \ + service.h \ + servicedvb.h \ + servicedvbrecord.h \ + servicefs.h \ + servicemp3.h \ + servicem2ts.h + +if HAVE_LIBXINE +libenigma_service_a_SOURCES += \ + servicexine.cpp +serviceinclude_HEADERS += \ + servicexine.h +endif diff --git a/lib/service/iservice.h b/lib/service/iservice.h index c35313f0..7f582495 100644 --- a/lib/service/iservice.h +++ b/lib/service/iservice.h @@ -289,6 +289,7 @@ public: sFileSize, sCAIDs, + sCAIDPIDs, sVideoType, /* MPEG2 MPEG4 */ sTags, /* space seperated list of tags */ @@ -356,6 +357,8 @@ public: sTagCRC, sTagChannelMode, + sTransferBPS, + sUser = 0x100 }; enum { @@ -594,6 +597,7 @@ class iTimeshiftService: public iObject public: virtual RESULT startTimeshift()=0; virtual RESULT stopTimeshift(bool swToLive=true)=0; + virtual RESULT setNextPlaybackFile(const char *fn)=0; // not needed by our internal timeshift.. but external plugin... virtual int isTimeshiftActive()=0; /* this essentially seeks to the relative end of the timeshift buffer */ diff --git a/lib/service/listboxservice.cpp b/lib/service/listboxservice.cpp index 05aaf731..cc8c32b5 100644 --- a/lib/service/listboxservice.cpp +++ b/lib/service/listboxservice.cpp @@ -526,7 +526,12 @@ void eListboxServiceContent::paint(gPainter &painter, eWindowStyle &style, const ePtr<iStaticServiceInformation> service_info; m_service_center->info(*m_cursor, service_info); eServiceReference ref = *m_cursor; - bool isPlayable = !(ref.flags & eServiceReference::isDirectory || ref.flags & eServiceReference::isMarker); + bool isMarker = ref.flags & eServiceReference::isMarker; + bool isPlayable = !(ref.flags & eServiceReference::isDirectory || isMarker); + bool paintProgress = false; + ePtr<eServiceEvent> evt; + + bool serviceAvail = true; if (!marked && isPlayable && service_info && m_is_playable_ignore.valid() && !service_info->isPlayable(*m_cursor, m_is_playable_ignore)) { @@ -534,12 +539,13 @@ void eListboxServiceContent::paint(gPainter &painter, eWindowStyle &style, const painter.setForegroundColor(m_color[serviceNotAvail]); else painter.setForegroundColor(gRGB(0xbbbbbb)); + serviceAvail = false; } if (selected && local_style && local_style->m_selection) painter.blit(local_style->m_selection, offset, eRect(), gPainter::BT_ALPHATEST); - int xoffset=0; // used as offset when painting the folder/marker symbol + int xoffset=0; // used as offset when painting the folder/marker symbol or the serviceevent progress for (int e = 0; e < celElements; ++e) { @@ -583,13 +589,19 @@ void eListboxServiceContent::paint(gPainter &painter, eWindowStyle &style, const } case celServiceInfo: { - ePtr<eServiceEvent> evt; - if ( isPlayable && service_info && !service_info->getEvent(*m_cursor, evt) ) + if ( isPlayable && !service_info->getEvent(*m_cursor, evt) ) { std::string name = evt->getEventName(); if (!name.length()) continue; text = '(' + evt->getEventName() + ')'; + if (serviceAvail) + { + if (!selected && m_color_set[serviceDescriptionColor]) + painter.setForegroundColor(m_color[serviceDescriptionColor]); + else if (selected && m_color_set[serviceDescriptionColorSelected]) + painter.setForegroundColor(m_color[serviceDescriptionColorSelected]); + } } else continue; @@ -608,9 +620,9 @@ void eListboxServiceContent::paint(gPainter &painter, eWindowStyle &style, const { eRect bbox = para->getBoundBox(); int name_width = bbox.width()+8; - m_element_position[celServiceInfo].setLeft(area.left()+name_width); + m_element_position[celServiceInfo].setLeft(area.left()+name_width+xoffs); m_element_position[celServiceInfo].setTop(area.top()); - m_element_position[celServiceInfo].setWidth(area.width()-name_width); + m_element_position[celServiceInfo].setWidth(area.width()-(name_width+xoffs)); m_element_position[celServiceInfo].setHeight(area.height()); } @@ -678,12 +690,62 @@ void eListboxServiceContent::paint(gPainter &painter, eWindowStyle &style, const painter.clippop(); } } + else if (e == celServiceEventProgressbar) + { + eRect area = m_element_position[celServiceEventProgressbar]; + if (area.width() > 0 && (isPlayable || isMarker)) + { + // we schedule it to paint it as last element.. so we dont need to reset fore/background color + paintProgress = isPlayable; + xoffset = area.width() + 10; + } + } } - if (selected && (!local_style || !local_style->m_selection)) style.drawFrame(painter, eRect(offset, m_itemsize), eWindowStyle::frameListboxEntry); + if (paintProgress && evt) + { + eRect area = m_element_position[celServiceEventProgressbar]; + if (!selected && m_color_set[serviceEventProgressbarBorderColor]) + painter.setForegroundColor(m_color[serviceEventProgressbarBorderColor]); + else if (selected && m_color_set[serviceEventProgressbarBorderColorSelected]) + painter.setForegroundColor(m_color[serviceEventProgressbarBorderColorSelected]); + + int border = 1; + int progressH = 6; + int progressX = area.left() + offset.x(); + int progressW = area.width() - 2 * border; + int progressT = offset.y() + (m_itemsize.height() - progressH - 2*border) / 2; + + // paint progressbar frame + painter.fill(eRect(progressX, progressT, area.width(), border)); + painter.fill(eRect(progressX, progressT + border, border, progressH)); + painter.fill(eRect(progressX, progressT + progressH + border, area.width(), border)); + painter.fill(eRect(progressX + area.width() - border, progressT + border, border, progressH)); + + // calculate value + time_t now = time(0); + int value = progressW * (now - evt->getBeginTime()) / evt->getDuration(); + + eRect tmp = eRect(progressX + border, progressT + border, value, progressH); + ePtr<gPixmap> &pixmap = m_pixmaps[picServiceEventProgressbar]; + if (pixmap) + { + area.moveBy(offset); + painter.clip(area); + painter.blit(pixmap, ePoint(progressX + border, progressT + border), tmp, gPainter::BT_ALPHATEST); + painter.clippop(); + } + else + { + if (!selected && m_color_set[serviceEventProgressbarColor]) + painter.setForegroundColor(m_color[serviceEventProgressbarColor]); + else if (selected && m_color_set[serviceEventProgressbarColorSelected]) + painter.setForegroundColor(m_color[serviceEventProgressbarColorSelected]); + painter.fill(tmp); + } + } } - painter.clippop(); } diff --git a/lib/service/listboxservice.h b/lib/service/listboxservice.h index 5228a2f2..589afba6 100644 --- a/lib/service/listboxservice.h +++ b/lib/service/listboxservice.h @@ -49,6 +49,7 @@ public: celServiceNumber, celMarkerPixmap, celFolderPixmap, + celServiceEventProgressbar, celServiceName, celServiceTypePixmap, celServiceInfo, // "now" event @@ -62,6 +63,7 @@ public: picServiceGroup, picFolder, picMarker, + picServiceEventProgressbar, picElements }; @@ -84,6 +86,12 @@ public: markedBackground, markedBackgroundSelected, serviceNotAvail, + serviceEventProgressbarColor, + serviceEventProgressbarColorSelected, + serviceEventProgressbarBorderColor, + serviceEventProgressbarBorderColorSelected, + serviceDescriptionColor, + serviceDescriptionColorSelected, colorElements }; diff --git a/lib/service/service.cpp b/lib/service/service.cpp index eb2757ab..8c674c5e 100644 --- a/lib/service/service.cpp +++ b/lib/service/service.cpp @@ -201,6 +201,25 @@ RESULT eServiceCenter::removeServiceFactory(int id) return 0; } +RESULT eServiceCenter::addFactoryExtension(int id, const char *extension) +{ + std::map<int, std::list<std::string> >::iterator it = extensions.find(id); + if (it == extensions.end()) + return -1; + it->second.push_back(extension); + return 0; +} + +RESULT eServiceCenter::removeFactoryExtension(int id, const char *extension) +{ + std::map<int, std::list<std::string> >::iterator it = extensions.find(id); + if (it == extensions.end()) + return -1; + it->second.remove(extension); + return 0; +} + + int eServiceCenter::getServiceTypeForExtension(const char *str) { for (std::map<int, std::list<std::string> >::iterator sit(extensions.begin()); sit != extensions.end(); ++sit) diff --git a/lib/service/service.h b/lib/service/service.h index 6f6ab980..ffc7d275 100644 --- a/lib/service/service.h +++ b/lib/service/service.h @@ -40,6 +40,8 @@ public: static RESULT getPrivInstance(ePtr<eServiceCenter> &ptr) { ptr = instance; return 0; } RESULT addServiceFactory(int id, iServiceHandler *hnd, std::list<std::string> &extensions); RESULT removeServiceFactory(int id); + RESULT addFactoryExtension(int id, const char *extension); + RESULT removeFactoryExtension(int id, const char *extension); #endif static SWIG_VOID(RESULT) getInstance(ePtr<iServiceHandler> &SWIG_NAMED_OUTPUT(ptr)) { ptr = instance; return 0; } }; diff --git a/lib/service/servicedvb.cpp b/lib/service/servicedvb.cpp index 558bf0c2..8650989a 100644 --- a/lib/service/servicedvb.cpp +++ b/lib/service/servicedvb.cpp @@ -309,7 +309,9 @@ eStaticServiceDVBPVRInformation::eStaticServiceDVBPVRInformation(const eServiceR RESULT eStaticServiceDVBPVRInformation::getName(const eServiceReference &ref, std::string &name) { ASSERT(ref == m_ref); - if (m_parser.m_name.size()) + if (!ref.name.empty()) + name = ref.name; + else if (!m_parser.m_name.empty()) name = m_parser.m_name; else { @@ -503,18 +505,19 @@ RESULT eDVBPVRServiceOfflineOperations::reindex() int err = f.open(m_ref.path.c_str(), 0); if (err < 0) return -1; - + + off_t offset = 0; off_t length = f.length(); unsigned char buffer[188*256*4]; while (1) { - off_t offset = f.lseek(0, SEEK_CUR); eDebug("at %08llx / %08llx (%d %%)", offset, length, (int)(offset * 100 / length)); - int r = f.read(buffer, sizeof(buffer)); + int r = f.read(offset, buffer, sizeof(buffer)); if (!r) break; if (r < 0) return r; + offset += r; parser.parseData(offset, buffer, r); } @@ -902,7 +905,7 @@ RESULT eServiceFactoryDVB::lookupService(ePtr<eDVBService> &service, const eServ /* we are sure to have a ..DVB reference as the info() call was forwarded here according to it's ID. */ if ((err = db->getService((eServiceReferenceDVB&)ref, service)) != 0) { - eDebug("getService failed!"); +// eDebug("getService failed!"); return err; } } @@ -910,7 +913,7 @@ RESULT eServiceFactoryDVB::lookupService(ePtr<eDVBService> &service, const eServ return 0; } -eDVBServicePlay::eDVBServicePlay(const eServiceReference &ref, eDVBService *service): +eDVBServicePlay::eDVBServicePlay(const eServiceReference &ref, eDVBService *service): m_reference(ref), m_dvb_service(service), m_have_video_pid(0), m_is_paused(0) { m_is_primary = 1; @@ -1021,12 +1024,13 @@ void eDVBServicePlay::serviceEvent(int event) updateTimeshiftPids(); if (!m_timeshift_active) updateDecoder(); - if (m_first_program_info && m_is_pvr) + if (m_first_program_info & 1 && m_is_pvr) { - m_first_program_info = 0; + m_first_program_info &= ~1; seekTo(0); } - m_event((iPlayableService*)this, evUpdatedInfo); + if (!m_timeshift_active) + m_event((iPlayableService*)this, evUpdatedInfo); break; } case eDVBServicePMTHandler::eventPreStart: @@ -1046,17 +1050,87 @@ void eDVBServicePlay::serviceEventTimeshift(int event) switch (event) { case eDVBServicePMTHandler::eventNewProgramInfo: + eDebug("eventNewProgramInfo TS"); if (m_timeshift_active) + { updateDecoder(); + if (m_first_program_info & 2) + { + if (m_slowmotion) + { + eDebug("re-apply slowmotion after timeshift file change"); + m_decoder->setSlowMotion(m_slowmotion); + } + if (m_fastforward) + { + eDebug("re-apply skip %d, ratio %d after timeshift file change", m_skipmode, m_fastforward); + if (m_skipmode) + m_cue->setSkipmode(m_skipmode * 90000); /* convert to 90000 per second */ + if (m_fastforward != 1) + m_decoder->setFastForward(m_fastforward); + else + m_decoder->setTrickmode(); + } + else + seekTo(0); + m_first_program_info &= ~2; + } + m_event((iPlayableService*)this, evUpdatedInfo); + } break; case eDVBServicePMTHandler::eventSOF: - m_event((iPlayableService*)this, evSOF); +#if 0 + if (!m_timeshift_file_next.empty()) + { + eDebug("timeshift SOF, switch to next file"); + m_decoder->pause(); + + m_first_program_info |= 2; + + eServiceReferenceDVB r = (eServiceReferenceDVB&)m_reference; + r.path = m_timeshift_file_next; + + /* free the timeshift service handler, we need the resources */ + m_service_handler_timeshift.free(); + resetTimeshift(1); + + if (m_skipmode < 0) + m_cue->seekTo(0, -1000); + ePtr<iTsSource> source = createTsSource(r); + m_service_handler_timeshift.tuneExt(r, 1, source, r.path.c_str(), m_cue, 0, m_dvb_service); /* use the decoder demux for everything */ + + m_event((iPlayableService*)this, evUser+1); + } + else +#endif + m_event((iPlayableService*)this, evSOF); break; case eDVBServicePMTHandler::eventEOF: if ((!m_is_paused) && (m_skipmode >= 0)) { - eDebug("timeshift EOF, so let's go live"); - switchToLive(); + if (m_timeshift_file_next.empty()) + { + eDebug("timeshift EOF, so let's go live"); + switchToLive(); + } + else + { + eDebug("timeshift EOF, switch to next file"); + + m_first_program_info |= 2; + + eServiceReferenceDVB r = (eServiceReferenceDVB&)m_reference; + r.path = m_timeshift_file_next; + + /* free the timeshift service handler, we need the resources */ + m_service_handler_timeshift.free(); + resetTimeshift(1); + + ePtr<iTsSource> source = createTsSource(r); + m_service_handler_timeshift.tuneExt(r, 1, source, m_timeshift_file_next.c_str(), m_cue, 0, m_dvb_service); /* use the decoder demux for everything */ + + m_event((iPlayableService*)this, evUser+1); + } } break; } @@ -1083,7 +1157,8 @@ RESULT eDVBServicePlay::start() m_event(this, evStart); m_first_program_info = 1; - m_service_handler.tune(service, m_is_pvr, m_cue, false, m_dvb_service); + ePtr<iTsSource> source = createTsSource(service); + m_service_handler.tuneExt(service, m_is_pvr, source, service.path.c_str(), m_cue, false, m_dvb_service); if (m_is_pvr) { @@ -1516,7 +1591,7 @@ int eDVBServicePlay::getInfo(int w) { eDVBServicePMTHandler::program program; - if (w == sCAIDs) + if (w == sCAIDs || w == sCAIDPIDs) return resIsPyObject; eDVBServicePMTHandler &h = m_timeshift_active ? m_service_handler_timeshift : m_service_handler; @@ -1657,6 +1732,8 @@ PyObject *eDVBServicePlay::getInfoObject(int w) { case sCAIDs: return m_service_handler.getCaIds(); + case sCAIDPIDs: + return m_service_handler.getCaIds(true); case sTransponderData: return eStaticServiceDVBInformation().getInfoObject(m_reference, w); default: @@ -1724,6 +1801,8 @@ RESULT eDVBServicePlay::getTrackInfo(struct iAudioTrackInfo &info, unsigned int info.m_description = "AAC-HE"; else if (program.audioStreams[i].type == eDVBServicePMTHandler::audioStream::atDTS) info.m_description = "DTS"; + else if (program.audioStreams[i].type == eDVBServicePMTHandler::audioStream::atDTSHD) + info.m_description = "DTS-HD"; else info.m_description = "???"; @@ -2240,38 +2319,32 @@ void eDVBServicePlay::updateTimeshiftPids() } } +RESULT eDVBServicePlay::setNextPlaybackFile(const char *f) +{ + m_timeshift_file_next = f; + return 0; +} + void eDVBServicePlay::switchToLive() { if (!m_timeshift_active) return; - + eDebug("SwitchToLive"); - - m_cue = 0; - m_decoder = 0; - m_decode_demux = 0; - m_teletext_parser = 0; - m_rds_decoder = 0; - m_subtitle_parser = 0; - m_new_dvb_subtitle_page_connection = 0; - m_new_subtitle_page_connection = 0; - m_rds_decoder_event_connection = 0; - m_video_event_connection = 0; + + resetTimeshift(0); + m_is_paused = m_skipmode = m_fastforward = m_slowmotion = 0; /* not supported in live mode */ - /* free the timeshift service handler, we need the resources */ + /* free the timeshift service handler, we need the resources */ m_service_handler_timeshift.free(); - m_timeshift_active = 0; - m_timeshift_changed = 1; updateDecoder(true); } -void eDVBServicePlay::switchToTimeshift() +void eDVBServicePlay::resetTimeshift(int start) { - if (m_timeshift_active) - return; - + m_cue = 0; m_decode_demux = 0; m_decoder = 0; m_teletext_parser = 0; @@ -2281,16 +2354,39 @@ void eDVBServicePlay::switchToTimeshift() m_new_dvb_subtitle_page_connection = 0; m_rds_decoder_event_connection = 0; m_video_event_connection = 0; - - m_timeshift_active = 1; m_timeshift_changed = 1; + m_timeshift_file_next.clear(); + + if (start) + { + m_cue = new eCueSheet(); + m_timeshift_active = 1; + } + else + m_timeshift_active = 0; +} + +ePtr<iTsSource> eDVBServicePlay::createTsSource(eServiceReferenceDVB &ref) +{ + eRawFile *f = new eRawFile(); + f->open(ref.path.c_str()); + return ePtr<iTsSource>(f); +} + +void eDVBServicePlay::switchToTimeshift() +{ + if (m_timeshift_active) + return; + + resetTimeshift(1); eServiceReferenceDVB r = (eServiceReferenceDVB&)m_reference; r.path = m_timeshift_file; - m_cue = new eCueSheet(); m_cue->seekTo(0, -1000); - m_service_handler_timeshift.tune(r, 1, m_cue, 0, m_dvb_service); /* use the decoder demux for everything */ + + ePtr<iTsSource> source = createTsSource(r); + m_service_handler_timeshift.tuneExt(r, 1, source, m_timeshift_file.c_str(), m_cue, 0, m_dvb_service); /* use the decoder demux for everything */ eDebug("eDVBServicePlay::switchToTimeshift, in pause mode now."); pause(); @@ -2309,7 +2405,7 @@ void eDVBServicePlay::updateDecoder(bool sendSeekableStateChanged) eDebug("getting program info failed."); else { - eDebugNoNewLine("have %d video stream(s)", program.videoStreams.size()); + eDebugNoNewLine("have %zd video stream(s)", program.videoStreams.size()); if (!program.videoStreams.empty()) { eDebugNoNewLine(" ("); @@ -2328,7 +2424,7 @@ void eDVBServicePlay::updateDecoder(bool sendSeekableStateChanged) } eDebugNoNewLine(")"); } - eDebugNoNewLine(", and %d audio stream(s)", program.audioStreams.size()); + eDebugNoNewLine(", and %zd audio stream(s)", program.audioStreams.size()); if (!program.audioStreams.empty()) { eDebugNoNewLine(" ("); @@ -2351,8 +2447,6 @@ void eDVBServicePlay::updateDecoder(bool sendSeekableStateChanged) if (!m_decoder) { h.getDecodeDemux(m_decode_demux); - if (m_timeshift_changed) - m_decoder = 0; if (m_decode_demux) { m_decode_demux->getMPEGDecoder(m_decoder, m_is_primary); @@ -2507,7 +2601,7 @@ void eDVBServicePlay::loadCuesheet() m_cue_entries.insert(cueEntry(where, what)); } fclose(f); - eDebug("%d entries", m_cue_entries.size()); + eDebug("%zd entries", m_cue_entries.size()); } else eDebug("cutfile not found!"); diff --git a/lib/service/servicedvb.h b/lib/service/servicedvb.h index 1262836e..3efc259d 100644 --- a/lib/service/servicedvb.h +++ b/lib/service/servicedvb.h @@ -162,6 +162,7 @@ public: RESULT stopTimeshift(bool swToLive=true); int isTimeshiftActive(); RESULT activateTimeshift(); + RESULT setNextPlaybackFile(const char *fn); // iCueSheet PyObject *getCutList(); @@ -184,7 +185,7 @@ public: RESULT stream(ePtr<iStreamableService> &ptr); PyObject *getStreamingData(); -private: +protected: friend class eServiceFactoryDVB; eServiceReference m_reference; @@ -213,9 +214,8 @@ private: int m_is_pvr, m_is_paused, m_timeshift_enabled, m_timeshift_active, m_timeshift_changed; int m_first_program_info; - std::string m_timeshift_file; + std::string m_timeshift_file, m_timeshift_file_next; int m_timeshift_fd; - ePtr<iDVBDemux> m_decode_demux; int m_current_audio_stream; @@ -228,8 +228,10 @@ private: void updateTimeshiftPids(); void switchToLive(); + + void resetTimeshift(int start); void switchToTimeshift(); - + void updateDecoder(bool sendSeekableStateChanged=false); int m_skipmode; @@ -287,6 +289,8 @@ private: ePtr<eConnection> m_video_event_connection; void video_event(struct iTSMPEGDecoder::videoEvent); + + virtual ePtr<iTsSource> createTsSource(eServiceReferenceDVB &ref); }; class eStaticServiceDVBBouquetInformation: public iStaticServiceInformation diff --git a/lib/service/servicedvbrecord.cpp b/lib/service/servicedvbrecord.cpp index 419c26ba..08cd2471 100644 --- a/lib/service/servicedvbrecord.cpp +++ b/lib/service/servicedvbrecord.cpp @@ -313,7 +313,7 @@ int eDVBServiceRecord::doRecord() int timing_pid = -1, timing_pid_type = -1; - eDebugNoNewLine("RECORD: have %d video stream(s)", program.videoStreams.size()); + eDebugNoNewLine("RECORD: have %zd video stream(s)", program.videoStreams.size()); if (!program.videoStreams.empty()) { eDebugNoNewLine(" ("); @@ -335,7 +335,7 @@ int eDVBServiceRecord::doRecord() } eDebugNoNewLine(")"); } - eDebugNoNewLine(", and %d audio stream(s)", program.audioStreams.size()); + eDebugNoNewLine(", and %zd audio stream(s)", program.audioStreams.size()); if (!program.audioStreams.empty()) { eDebugNoNewLine(" ("); diff --git a/lib/service/servicem2ts.cpp b/lib/service/servicem2ts.cpp new file mode 100644 index 00000000..e79907dd --- /dev/null +++ b/lib/service/servicem2ts.cpp @@ -0,0 +1,380 @@ +#include <lib/base/init_num.h> +#include <lib/base/init.h> +#include <lib/dvb/metaparser.h> +#include <lib/service/servicem2ts.h> + +DEFINE_REF(eServiceFactoryM2TS) + +class eM2TSFile: public iTsSource +{ + DECLARE_REF(eM2TSFile); + eSingleLock m_lock; +public: + eM2TSFile(const char *filename, bool cached=false); + ~eM2TSFile(); + + // iTsSource + off_t lseek(off_t offset, int whence); + ssize_t read(off_t offset, void *buf, size_t count); + off_t length(); + int valid(); +private: + int m_sync_offset; + int m_fd; /* for uncached */ + FILE *m_file; /* for cached */ + off_t m_current_offset, m_length; + bool m_cached; + off_t lseek_internal(off_t offset, int whence); +}; + +class eStaticServiceM2TSInformation: public iStaticServiceInformation +{ + DECLARE_REF(eStaticServiceM2TSInformation); + eServiceReference m_ref; + eDVBMetaParser m_parser; +public: + eStaticServiceM2TSInformation(const eServiceReference &ref); + RESULT getName(const eServiceReference &ref, std::string &name); + int getLength(const eServiceReference &ref); + RESULT getEvent(const eServiceReference &ref, ePtr<eServiceEvent> &SWIG_OUTPUT, time_t start_time); + int isPlayable(const eServiceReference &ref, const eServiceReference &ignore) { return 1; } + int getInfo(const eServiceReference &ref, int w); + std::string getInfoString(const eServiceReference &ref,int w); + PyObject *getInfoObject(const eServiceReference &r, int what); +}; + +DEFINE_REF(eStaticServiceM2TSInformation); + +eStaticServiceM2TSInformation::eStaticServiceM2TSInformation(const eServiceReference &ref) +{ + m_ref = ref; + m_parser.parseFile(ref.path); +} + +RESULT eStaticServiceM2TSInformation::getName(const eServiceReference &ref, std::string &name) +{ + ASSERT(ref == m_ref); + if (m_parser.m_name.size()) + name = m_parser.m_name; + else + { + name = ref.path; + size_t n = name.rfind('/'); + if (n != std::string::npos) + name = name.substr(n + 1); + } + return 0; +} + +int eStaticServiceM2TSInformation::getLength(const eServiceReference &ref) +{ + ASSERT(ref == m_ref); + + eDVBTSTools tstools; + + struct stat s; + stat(ref.path.c_str(), &s); + + eM2TSFile *file = new eM2TSFile(ref.path.c_str()); + ePtr<iTsSource> source = file; + + if (!source->valid()) + return 0; + + tstools.setSource(source); + + /* check if cached data is still valid */ + if (m_parser.m_data_ok && (s.st_size == m_parser.m_filesize) && (m_parser.m_length)) + return m_parser.m_length / 90000; + + /* open again, this time with stream info */ + tstools.setSource(source, ref.path.c_str()); + + /* otherwise, re-calc length and update meta file */ + pts_t len; + if (tstools.calcLen(len)) + return 0; + + m_parser.m_length = len; + m_parser.m_filesize = s.st_size; + m_parser.updateMeta(ref.path); + return m_parser.m_length / 90000; +} + +int eStaticServiceM2TSInformation::getInfo(const eServiceReference &ref, int w) +{ + switch (w) + { + case iServiceInformation::sDescription: + return iServiceInformation::resIsString; + case iServiceInformation::sServiceref: + return iServiceInformation::resIsString; + case iServiceInformation::sFileSize: + return m_parser.m_filesize; + case iServiceInformation::sTimeCreate: + if (m_parser.m_time_create) + return m_parser.m_time_create; + else + return iServiceInformation::resNA; + default: + return iServiceInformation::resNA; + } +} + +std::string eStaticServiceM2TSInformation::getInfoString(const eServiceReference &ref,int w) +{ + switch (w) + { + case iServiceInformation::sDescription: + return m_parser.m_description; + case iServiceInformation::sServiceref: + return m_parser.m_ref.toString(); + case iServiceInformation::sTags: + return m_parser.m_tags; + default: + return ""; + } +} + +PyObject *eStaticServiceM2TSInformation::getInfoObject(const eServiceReference &r, int what) +{ + switch (what) + { + case iServiceInformation::sFileSize: + return PyLong_FromLongLong(m_parser.m_filesize); + default: + Py_RETURN_NONE; + } +} + +RESULT eStaticServiceM2TSInformation::getEvent(const eServiceReference &ref, ePtr<eServiceEvent> &evt, time_t start_time) +{ + if (!ref.path.empty()) + { + ePtr<eServiceEvent> event = new eServiceEvent; + std::string filename = ref.path; + filename.erase(filename.length()-4, 2); + filename+="eit"; + if (!event->parseFrom(filename, (m_parser.m_ref.getTransportStreamID().get()<<16)|m_parser.m_ref.getOriginalNetworkID().get())) + { + evt = event; + return 0; + } + } + evt = 0; + return -1; +} + +DEFINE_REF(eM2TSFile); + +eM2TSFile::eM2TSFile(const char *filename, bool cached) + :m_lock(false), m_sync_offset(0), m_fd(-1), m_file(NULL), m_current_offset(0), m_length(0), m_cached(cached) +{ + if (!m_cached) + m_fd = ::open(filename, O_RDONLY | O_LARGEFILE); + else + m_file = ::fopen64(filename, "rb"); + if (valid()) + m_current_offset = m_length = lseek_internal(0, SEEK_END); +} + +eM2TSFile::~eM2TSFile() +{ + if (m_cached) + { + if (m_file) + { + ::fclose(m_file); + m_file = 0; + } + } + else + { + if (m_fd >= 0) + ::close(m_fd); + m_fd = -1; + } +} + +off_t eM2TSFile::lseek(off_t offset, int whence) +{ + eSingleLocker l(m_lock); + + offset = (offset % 188) + (offset * 192) / 188; + + if (offset != m_current_offset) + m_current_offset = lseek_internal(offset, whence); + + return m_current_offset; +} + +off_t eM2TSFile::lseek_internal(off_t offset, int whence) +{ + off_t ret; + + if (!m_cached) + ret = ::lseek(m_fd, offset, whence); + else + { + if (::fseeko(m_file, offset, whence) < 0) + perror("fseeko"); + ret = ::ftello(m_file); + } + return ret <= 0 ? ret : (ret % 192) + (ret*188) / 192; +} + +ssize_t eM2TSFile::read(off_t offset, void *b, size_t count) +{ + eSingleLocker l(m_lock); + unsigned char tmp[192*3]; + unsigned char *buf = (unsigned char*)b; + + size_t rd=0; + offset = (offset % 188) + (offset * 192) / 188; + +sync: + if ((offset+m_sync_offset) != m_current_offset) + { +// eDebug("seekTo %lld", offset+m_sync_offset); + m_current_offset = lseek_internal(offset+m_sync_offset, SEEK_SET); + if (m_current_offset < 0) + return m_current_offset; + } + + while (rd < count) { + size_t ret; + if (!m_cached) + ret = ::read(m_fd, tmp, 192); + else + ret = ::fread(tmp, 1, 192, m_file); + if (ret < 0 || ret < 192) + return rd ? rd : ret; + + if (tmp[4] != 0x47) + { + if (rd > 0) { + eDebug("short read at pos %lld async!!", m_current_offset); + return rd; + } + else { + int x=0; + if (!m_cached) + ret = ::read(m_fd, tmp+192, 384); + else + ret = ::fread(tmp+192, 1, 384, m_file); + +#if 0 + eDebugNoNewLine("m2ts out of sync at pos %lld, real %lld:", offset + m_sync_offset, m_current_offset); + for (; x < 192; ++x) + eDebugNoNewLine(" %02x", tmp[x]); + eDebug(""); + x=0; +#else + eDebug("m2ts out of sync at pos %lld, real %lld", offset + m_sync_offset, m_current_offset); +#endif + for (; x < 192; ++x) + { + if (tmp[x] == 0x47 && tmp[x+192] == 0x47) + { + int add_offs = (x - 4); + eDebug("sync found at pos %d, sync_offset is now %d, old was %d", x, add_offs + m_sync_offset, m_sync_offset); + m_sync_offset += add_offs; + goto sync; + } + } + } + } + + memcpy(buf+rd, tmp+4, 188); + + rd += 188; + m_current_offset += 188; + } + + m_sync_offset %= 188; + + return rd; +} + +int eM2TSFile::valid() +{ + if (!m_cached) + return m_fd != -1; + else + return !!m_file; +} + +off_t eM2TSFile::length() +{ + return m_length; +} + +eServiceFactoryM2TS::eServiceFactoryM2TS() +{ + ePtr<eServiceCenter> sc; + eServiceCenter::getPrivInstance(sc); + if (sc) + { + std::list<std::string> extensions; + extensions.push_back("m2ts"); + extensions.push_back("mts"); + sc->addServiceFactory(eServiceFactoryM2TS::id, this, extensions); + } +} + +eServiceFactoryM2TS::~eServiceFactoryM2TS() +{ + ePtr<eServiceCenter> sc; + + eServiceCenter::getPrivInstance(sc); + if (sc) + sc->removeServiceFactory(eServiceFactoryM2TS::id); +} + +RESULT eServiceFactoryM2TS::play(const eServiceReference &ref, ePtr<iPlayableService> &ptr) +{ + ptr = new eServiceM2TS(ref); + return 0; +} + +RESULT eServiceFactoryM2TS::record(const eServiceReference &ref, ePtr<iRecordableService> &ptr) +{ + ptr=0; + return -1; +} + +RESULT eServiceFactoryM2TS::list(const eServiceReference &ref, ePtr<iListableService> &ptr) +{ + ptr=0; + return -1; +} + +RESULT eServiceFactoryM2TS::info(const eServiceReference &ref, ePtr<iStaticServiceInformation> &ptr) +{ + ptr=new eStaticServiceM2TSInformation(ref); + return 0; +} + +RESULT eServiceFactoryM2TS::offlineOperations(const eServiceReference &ref, ePtr<iServiceOfflineOperations> &ptr) +{ + ptr = 0; + return -1; +} + +eServiceM2TS::eServiceM2TS(const eServiceReference &ref) + :eDVBServicePlay(ref, NULL) +{ +} + +ePtr<iTsSource> eServiceM2TS::createTsSource(eServiceReferenceDVB &ref) +{ + ePtr<iTsSource> source = new eM2TSFile(ref.path.c_str()); + return source; +} + +RESULT eServiceM2TS::isCurrentlySeekable() +{ + return 1; // for fast winding we need index files... so only skip forward/backward yet +} + +eAutoInitPtr<eServiceFactoryM2TS> init_eServiceFactoryM2TS(eAutoInitNumbers::service+1, "eServiceFactoryM2TS"); diff --git a/lib/service/servicem2ts.h b/lib/service/servicem2ts.h new file mode 100644 index 00000000..bfa4f7d9 --- /dev/null +++ b/lib/service/servicem2ts.h @@ -0,0 +1,33 @@ +#ifndef __servicem2ts_h +#define __servicem2ts_h + +#include <lib/service/servicedvb.h> + +class eServiceFactoryM2TS: public iServiceHandler +{ + DECLARE_REF(eServiceFactoryM2TS); +public: + eServiceFactoryM2TS(); + virtual ~eServiceFactoryM2TS(); + enum { id = 0x3 }; + + // iServiceHandler + RESULT play(const eServiceReference &, ePtr<iPlayableService> &ptr); + RESULT record(const eServiceReference &, ePtr<iRecordableService> &ptr); + RESULT list(const eServiceReference &, ePtr<iListableService> &ptr); + RESULT info(const eServiceReference &, ePtr<iStaticServiceInformation> &ptr); + RESULT offlineOperations(const eServiceReference &, ePtr<iServiceOfflineOperations> &ptr); +}; + +class eServiceM2TS: public eDVBServicePlay +{ + friend class eServiceFactoryM2TS; +protected: + eServiceM2TS(const eServiceReference &ref); + ePtr<iTsSource> createTsSource(eServiceReferenceDVB &ref); + + // iSeekableService + RESULT isCurrentlySeekable(); +}; + +#endif diff --git a/lib/service/servicemp3.cpp b/lib/service/servicemp3.cpp index 2c84f7be..be55d0cd 100644 --- a/lib/service/servicemp3.cpp +++ b/lib/service/servicemp3.cpp @@ -1,5 +1,3 @@ -#ifdef HAVE_GSTREAMER - /* note: this requires gstreamer 0.10.x and a big list of plugins. */ /* it's currently hardcoded to use a big-endian alsasink as sink. */ #include <lib/base/ebase.h> @@ -13,6 +11,7 @@ #include <lib/gui/esubtitle.h> #include <lib/service/servicemp3.h> #include <lib/service/service.h> +#include <lib/gdi/gpixmap.h> #include <string> @@ -20,6 +19,8 @@ #include <gst/pbutils/missing-plugins.h> #include <sys/stat.h> +#define HTTP_TIMEOUT 10 + // eServiceFactoryMP3 eServiceFactoryMP3::eServiceFactoryMP3() @@ -217,6 +218,7 @@ eServiceMP3::eServiceMP3(eServiceReference ref) { m_seekTimeout = eTimer::create(eApp); m_subtitle_sync_timer = eTimer::create(eApp); + m_streamingsrc_timeout = 0; m_stream_tags = 0; m_currentAudioStream = -1; m_currentSubtitleStream = 0; @@ -224,6 +226,10 @@ eServiceMP3::eServiceMP3(eServiceReference ref) m_currentTrickRatio = 0; m_subs_to_pull = 0; m_buffer_size = 1*1024*1024; + m_prev_decoder_time = -1; + m_decoder_time_valid_state = 0; + m_errorInfo.missing_codec = ""; + CONNECT(m_seekTimeout->timeout, eServiceMP3::seekTimeoutCB); CONNECT(m_subtitle_sync_timer->timeout, eServiceMP3::pushSubtitles); CONNECT(m_pump.recv_msg, eServiceMP3::gstPoll); @@ -237,63 +243,73 @@ eServiceMP3::eServiceMP3(eServiceReference ref) if (!ext) ext = filename; - sourceStream sourceinfo; - sourceinfo.is_video = FALSE; - sourceinfo.audiotype = atUnknown; + m_sourceinfo.is_video = FALSE; + m_sourceinfo.audiotype = atUnknown; if ( (strcasecmp(ext, ".mpeg") && strcasecmp(ext, ".mpg") && strcasecmp(ext, ".vob") && strcasecmp(ext, ".bin") && strcasecmp(ext, ".dat") ) == 0 ) { - sourceinfo.containertype = ctMPEGPS; - sourceinfo.is_video = TRUE; + m_sourceinfo.containertype = ctMPEGPS; + m_sourceinfo.is_video = TRUE; } else if ( strcasecmp(ext, ".ts") == 0 ) { - sourceinfo.containertype = ctMPEGTS; - sourceinfo.is_video = TRUE; + m_sourceinfo.containertype = ctMPEGTS; + m_sourceinfo.is_video = TRUE; } else if ( strcasecmp(ext, ".mkv") == 0 ) { - sourceinfo.containertype = ctMKV; - sourceinfo.is_video = TRUE; + m_sourceinfo.containertype = ctMKV; + m_sourceinfo.is_video = TRUE; } else if ( strcasecmp(ext, ".avi") == 0 || strcasecmp(ext, ".divx") == 0) { - sourceinfo.containertype = ctAVI; - sourceinfo.is_video = TRUE; + m_sourceinfo.containertype = ctAVI; + m_sourceinfo.is_video = TRUE; } else if ( strcasecmp(ext, ".mp4") == 0 || strcasecmp(ext, ".mov") == 0 || strcasecmp(ext, ".m4v") == 0) { - sourceinfo.containertype = ctMP4; - sourceinfo.is_video = TRUE; + m_sourceinfo.containertype = ctMP4; + m_sourceinfo.is_video = TRUE; } else if ( strcasecmp(ext, ".m4a") == 0 ) { - sourceinfo.containertype = ctMP4; - sourceinfo.audiotype = atAAC; + m_sourceinfo.containertype = ctMP4; + m_sourceinfo.audiotype = atAAC; } else if ( strcasecmp(ext, ".mp3") == 0 ) - sourceinfo.audiotype = atMP3; + m_sourceinfo.audiotype = atMP3; else if ( (strncmp(filename, "/autofs/", 8) || strncmp(filename+strlen(filename)-13, "/track-", 7) || strcasecmp(ext, ".wav")) == 0 ) - sourceinfo.containertype = ctCDA; + m_sourceinfo.containertype = ctCDA; if ( strcasecmp(ext, ".dat") == 0 ) { - sourceinfo.containertype = ctVCD; - sourceinfo.is_video = TRUE; + m_sourceinfo.containertype = ctVCD; + m_sourceinfo.is_video = TRUE; } - if ( (strncmp(filename, "http://", 7)) == 0 || (strncmp(filename, "udp://", 6)) == 0 || (strncmp(filename, "rtp://", 6)) == 0 || (strncmp(filename, "https://", 8)) == 0 || (strncmp(filename, "mms://", 6)) == 0 || (strncmp(filename, "rtsp://", 7)) == 0 ) - sourceinfo.is_streaming = TRUE; + if ( (strncmp(filename, "http://", 7)) == 0 || (strncmp(filename, "udp://", 6)) == 0 || (strncmp(filename, "rtp://", 6)) == 0 || (strncmp(filename, "https://", 8)) == 0 || (strncmp(filename, "mms://", 6)) == 0 || (strncmp(filename, "rtsp://", 7)) == 0 || (strncmp(filename, "rtspt://", 7)) == 0 ) + m_sourceinfo.is_streaming = TRUE; gchar *uri; - if ( sourceinfo.is_streaming ) + if ( m_sourceinfo.is_streaming ) { uri = g_strdup_printf ("%s", filename); + m_streamingsrc_timeout = eTimer::create(eApp);; + CONNECT(m_streamingsrc_timeout->timeout, eServiceMP3::sourceTimeout); + + std::string config_str; + if( ePythonConfigQuery::getConfigValue("config.mediaplayer.useAlternateUserAgent", config_str) == 0 ) + { + if ( config_str == "True" ) + ePythonConfigQuery::getConfigValue("config.mediaplayer.alternateUserAgent", m_useragent); + } + if ( m_useragent.length() == 0 ) + m_useragent = "Dream Multimedia Dreambox Enigma2 Mediaplayer"; } - else if ( sourceinfo.containertype == ctCDA ) + else if ( m_sourceinfo.containertype == ctCDA ) { int i_track = atoi(filename+18); uri = g_strdup_printf ("cdda://%i", i_track); } - else if ( sourceinfo.containertype == ctVCD ) + else if ( m_sourceinfo.containertype == ctVCD ) { int fd = open(filename,O_RDONLY); char tmp[128*1024]; @@ -302,21 +318,21 @@ eServiceMP3::eServiceMP3(eServiceReference ref) if ( ret == -1 ) // this is a "REAL" VCD uri = g_strdup_printf ("vcd://"); else - uri = g_strdup_printf ("file://%s", filename); + uri = g_filename_to_uri(filename, NULL, NULL); } else - uri = g_strdup_printf ("file://%s", filename); + uri = g_filename_to_uri(filename, NULL, NULL); eDebug("eServiceMP3::playbin2 uri=%s", uri); m_gst_playbin = gst_element_factory_make("playbin2", "playbin"); if (!m_gst_playbin) - m_error_message = "failed to create GStreamer pipeline!\n"; + m_errorInfo.error_message = "failed to create GStreamer pipeline!\n"; g_object_set (G_OBJECT (m_gst_playbin), "uri", uri, NULL); - int flags = 0x47; // ( == GST_PLAY_FLAG_VIDEO | GST_PLAY_FLAG_AUDIO | GST_PLAY_FLAG_NATIVE_VIDEO | GST_PLAY_FLAG_TEXT ) + int flags = 0x47; // ( GST_PLAY_FLAG_VIDEO | GST_PLAY_FLAG_AUDIO | GST_PLAY_FLAG_NATIVE_VIDEO | GST_PLAY_FLAG_TEXT ); g_object_set (G_OBJECT (m_gst_playbin), "flags", flags, NULL); g_free(uri); @@ -327,7 +343,9 @@ eServiceMP3::eServiceMP3(eServiceReference ref) else { m_subs_to_pull_handler_id = g_signal_connect (subsink, "new-buffer", G_CALLBACK (gstCBsubtitleAvail), this); + g_object_set (G_OBJECT (subsink), "caps", gst_caps_from_string("text/plain; text/x-plain; text/x-pango-markup; video/x-dvd-subpicture; subpicture/x-pgs"), NULL); g_object_set (G_OBJECT (m_gst_playbin), "text-sink", subsink, NULL); + } if ( m_gst_playbin ) @@ -340,13 +358,12 @@ eServiceMP3::eServiceMP3(eServiceReference ref) struct stat buffer; if (stat(srt_filename, &buffer) == 0) { - std::string suburi = "file://" + (std::string)srt_filename; - eDebug("eServiceMP3::subtitle uri: %s",suburi.c_str()); - g_object_set (G_OBJECT (m_gst_playbin), "suburi", suburi.c_str(), NULL); - subtitleStream subs; - subs.type = stSRT; - subs.language_code = std::string("und"); - m_subtitleStreams.push_back(subs); + eDebug("eServiceMP3::subtitle uri: %s", g_filename_to_uri(srt_filename, NULL, NULL)); + g_object_set (G_OBJECT (m_gst_playbin), "suburi", g_filename_to_uri(srt_filename, NULL, NULL), NULL); + } + if ( m_sourceinfo.is_streaming ) + { + g_signal_connect (G_OBJECT (m_gst_playbin), "notify::source", G_CALLBACK (gstHTTPSourceSetAgent), this); } } else { @@ -355,7 +372,7 @@ eServiceMP3::eServiceMP3(eServiceReference ref) if (m_gst_playbin) gst_object_unref(GST_OBJECT(m_gst_playbin)); - eDebug("eServiceMP3::sorry, can't play: %s",m_error_message.c_str()); + eDebug("eServiceMP3::sorry, can't play: %s",m_errorInfo.error_message.c_str()); m_gst_playbin = 0; } @@ -365,12 +382,12 @@ eServiceMP3::eServiceMP3(eServiceReference ref) eServiceMP3::~eServiceMP3() { // disconnect subtitle callback - GstElement *sink; - g_object_get (G_OBJECT (m_gst_playbin), "text-sink", &sink, NULL); - if (sink) + GstElement *appsink = gst_bin_get_by_name(GST_BIN(m_gst_playbin), "subtitle_sink"); + + if (appsink) { - g_signal_handler_disconnect (sink, m_subs_to_pull_handler_id); - gst_object_unref(sink); + g_signal_handler_disconnect (appsink, m_subs_to_pull_handler_id); + gst_object_unref(appsink); } delete m_subtitle_widget; @@ -415,12 +432,20 @@ RESULT eServiceMP3::start() return 0; } +void eServiceMP3::sourceTimeout() +{ + eDebug("eServiceMP3::http source timeout! issuing eof..."); + m_event((iPlayableService*)this, evEOF); +} + RESULT eServiceMP3::stop() { ASSERT(m_state != stIdle); if (m_state == stStopped) return -1; + + //GST_DEBUG_BIN_TO_DOT_FILE(GST_BIN(m_gst_playbin),GST_DEBUG_GRAPH_SHOW_ALL,"e2-playbin"); eDebug("eServiceMP3::stop %s", m_ref.path.c_str()); gst_element_set_state(m_gst_playbin, GST_STATE_NULL); @@ -547,6 +572,8 @@ RESULT eServiceMP3::seekTo(pts_t to) if (!(ret = seekToImpl(to))) { m_subtitle_pages.clear(); + m_prev_decoder_time = -1; + m_decoder_time_valid_state = 0; m_subs_to_pull = 0; } } @@ -648,6 +675,7 @@ RESULT eServiceMP3::getPlayPosition(pts_t &pts) /* pos is in nanoseconds. we have 90 000 pts per second. */ pts = pos / 11111; +// eDebug("gst_element_query_position %lld pts (%lld ms)", pts, pos/1000000); return 0; } @@ -908,7 +936,7 @@ std::string eServiceMP3::getInfoString(int w) tag = "channel-mode"; break; case sUser+12: - return m_error_message; + return m_errorInfo.error_message; default: return ""; } @@ -1090,24 +1118,66 @@ RESULT eServiceMP3::getTrackInfo(struct iAudioTrackInfo &info, unsigned int i) return 0; } +subtype_t getSubtitleType(GstPad* pad, gchar *g_codec=NULL) +{ + subtype_t type = stUnknown; + GstCaps* caps = gst_pad_get_negotiated_caps(pad); + + if ( caps ) + { + GstStructure* str = gst_caps_get_structure(caps, 0); + const gchar *g_type = gst_structure_get_name(str); + eDebug("getSubtitleType::subtitle probe caps type=%s", g_type); + + if ( !strcmp(g_type, "video/x-dvd-subpicture") ) + type = stVOB; + else if ( !strcmp(g_type, "text/x-pango-markup") ) + type = stSSA; + else if ( !strcmp(g_type, "text/plain") ) + type = stPlainText; + else if ( !strcmp(g_type, "subpicture/x-pgs") ) + type = stPGS; + else + eDebug("getSubtitleType::unsupported subtitle caps %s (%s)", g_type, g_codec); + } + else if ( g_codec ) + { + eDebug("getSubtitleType::subtitle probe codec tag=%s", g_codec); + if ( !strcmp(g_codec, "VOB") ) + type = stVOB; + else if ( !strcmp(g_codec, "SubStation Alpha") || !strcmp(g_codec, "SSA") ) + type = stSSA; + else if ( !strcmp(g_codec, "ASS") ) + type = stASS; + else if ( !strcmp(g_codec, "UTF-8 plain text") ) + type = stPlainText; + else + eDebug("getSubtitleType::unsupported subtitle codec %s", g_codec); + } + else + eDebug("getSubtitleType::unidentifiable subtitle stream!"); + + return type; +} + void eServiceMP3::gstBusCall(GstBus *bus, GstMessage *msg) { if (!msg) return; gchar *sourceName; GstObject *source; - source = GST_MESSAGE_SRC(msg); + if (!GST_IS_OBJECT(source)) + return; sourceName = gst_object_get_name(source); #if 0 + gchar *string; if (gst_message_get_structure(msg)) - { - gchar *string = gst_structure_to_string(gst_message_get_structure(msg)); - eDebug("eServiceMP3::gst_message from %s: %s", sourceName, string); - g_free(string); - } + string = gst_structure_to_string(gst_message_get_structure(msg)); else - eDebug("eServiceMP3::gst_message from %s: %s (without structure)", sourceName, GST_MESSAGE_TYPE_NAME(msg)); + string = g_strdup(GST_MESSAGE_TYPE_NAME(msg)); + eDebug("eTsRemoteSource::gst_message from %s: %s", sourceName, string); + g_free(string); #endif switch (GST_MESSAGE_TYPE (msg)) { @@ -1136,21 +1206,22 @@ void eServiceMP3::gstBusCall(GstBus *bus, GstMessage *msg) } break; case GST_STATE_CHANGE_READY_TO_PAUSED: { - GstElement *sink; - g_object_get (G_OBJECT (m_gst_playbin), "text-sink", &sink, NULL); - if (sink) - { - g_object_set (G_OBJECT (sink), "max-buffers", 2, NULL); - g_object_set (G_OBJECT (sink), "sync", FALSE, NULL); - g_object_set (G_OBJECT (sink), "async", FALSE, NULL); - g_object_set (G_OBJECT (sink), "emit-signals", TRUE, NULL); - gst_object_unref(sink); - } + GstElement *appsink = gst_bin_get_by_name(GST_BIN(m_gst_playbin), "subtitle_sink"); + if (appsink) + { + g_object_set (G_OBJECT (appsink), "max-buffers", 2, NULL); + g_object_set (G_OBJECT (appsink), "sync", FALSE, NULL); + g_object_set (G_OBJECT (appsink), "emit-signals", TRUE, NULL); + eDebug("eServiceMP3::appsink properties set!"); + gst_object_unref(appsink); + } setAC3Delay(ac3_delay); setPCMDelay(pcm_delay); } break; case GST_STATE_CHANGE_PAUSED_TO_PLAYING: { + if ( m_sourceinfo.is_streaming && m_streamingsrc_timeout ) + m_streamingsrc_timeout->stop(); } break; case GST_STATE_CHANGE_PLAYING_TO_PAUSED: { @@ -1260,7 +1331,6 @@ void eServiceMP3::gstBusCall(GstBus *bus, GstMessage *msg) continue; GstStructure* str = gst_caps_get_structure(caps, 0); const gchar *g_type = gst_structure_get_name(str); - eDebug("AUDIO STRUCT=%s", g_type); audio.type = gstCheckAudioPad(str); g_codec = g_strdup(g_type); g_lang = g_strdup_printf ("und"); @@ -1281,64 +1351,85 @@ void eServiceMP3::gstBusCall(GstBus *bus, GstMessage *msg) } for (i = 0; i < n_text; i++) - { - gchar *g_lang; -// gchar *g_type; -// GstPad* pad = 0; -// g_signal_emit_by_name (m_gst_playbin, "get-text-pad", i, &pad); -// GstCaps* caps = gst_pad_get_negotiated_caps(pad); -// GstStructure* str = gst_caps_get_structure(caps, 0); -// g_type = gst_structure_get_name(str); -// g_signal_emit_by_name (m_gst_playbin, "get-text-tags", i, &tags); + { + gchar *g_codec = NULL, *g_lang = NULL; + g_signal_emit_by_name (m_gst_playbin, "get-text-tags", i, &tags); subtitleStream subs; - subs.type = stPlainText; +// int ret; + g_lang = g_strdup_printf ("und"); if ( tags && gst_is_tag_list(tags) ) + { gst_tag_list_get_string(tags, GST_TAG_LANGUAGE_CODE, &g_lang); + gst_tag_list_get_string(tags, GST_TAG_SUBTITLE_CODEC, &g_codec); + gst_tag_list_free(tags); + } + subs.language_code = std::string(g_lang); - eDebug("eServiceMP3::subtitle stream=%i language=%s"/* type=%s*/, i, g_lang/*, g_type*/); + eDebug("eServiceMP3::subtitle stream=%i language=%s codec=%s", i, g_lang, g_codec); + + GstPad* pad = 0; + g_signal_emit_by_name (m_gst_playbin, "get-text-pad", i, &pad); + if ( pad ) + g_signal_connect (G_OBJECT (pad), "notify::caps", G_CALLBACK (gstTextpadHasCAPS), this); + subs.type = getSubtitleType(pad, g_codec); + m_subtitleStreams.push_back(subs); g_free (g_lang); -// g_free (g_type); } m_event((iPlayableService*)this, evUpdatedEventInfo); - } - case GST_MESSAGE_ELEMENT: - { - if ( gst_is_missing_plugin_message(msg) ) + + if ( m_errorInfo.missing_codec != "" ) { - gchar *description = gst_missing_plugin_message_get_description(msg); - if ( description ) - { - m_error_message = "GStreamer plugin " + (std::string)description + " not available!\n"; - g_free(description); + if ( m_errorInfo.missing_codec.find("video/") == 0 || ( m_errorInfo.missing_codec.find("audio/") == 0 && getNumberOfTracks() == 0 ) ) m_event((iPlayableService*)this, evUser+12); - } } - else if (const GstStructure *msgstruct = gst_message_get_structure(msg)) + break; + } + case GST_MESSAGE_ELEMENT: + { + if (const GstStructure *msgstruct = gst_message_get_structure(msg)) { - const gchar *eventname = gst_structure_get_name(msgstruct); - if ( eventname ) + if ( gst_is_missing_plugin_message(msg) ) { - if (!strcmp(eventname, "eventSizeChanged") || !strcmp(eventname, "eventSizeAvail")) + GstCaps *caps; + gst_structure_get (msgstruct, "detail", GST_TYPE_CAPS, &caps, NULL); + std::string codec = (const char*) gst_caps_to_string(caps); + gchar *description = gst_missing_plugin_message_get_description(msg); + if ( description ) { - gst_structure_get_int (msgstruct, "aspect_ratio", &m_aspect); - gst_structure_get_int (msgstruct, "width", &m_width); - gst_structure_get_int (msgstruct, "height", &m_height); - if (strstr(eventname, "Changed")) - m_event((iPlayableService*)this, evVideoSizeChanged); + eDebug("eServiceMP3::m_errorInfo.missing_codec = %s", codec.c_str()); + m_errorInfo.error_message = "GStreamer plugin " + (std::string)description + " not available!\n"; + m_errorInfo.missing_codec = codec.substr(0,(codec.find_first_of(','))); + g_free(description); } - else if (!strcmp(eventname, "eventFrameRateChanged") || !strcmp(eventname, "eventFrameRateAvail")) - { - gst_structure_get_int (msgstruct, "frame_rate", &m_framerate); - if (strstr(eventname, "Changed")) - m_event((iPlayableService*)this, evVideoFramerateChanged); - } - else if (!strcmp(eventname, "eventProgressiveChanged") || !strcmp(eventname, "eventProgressiveAvail")) + gst_caps_unref(caps); + } + else + { + const gchar *eventname = gst_structure_get_name(msgstruct); + if ( eventname ) { - gst_structure_get_int (msgstruct, "progressive", &m_progressive); - if (strstr(eventname, "Changed")) - m_event((iPlayableService*)this, evVideoProgressiveChanged); + if (!strcmp(eventname, "eventSizeChanged") || !strcmp(eventname, "eventSizeAvail")) + { + gst_structure_get_int (msgstruct, "aspect_ratio", &m_aspect); + gst_structure_get_int (msgstruct, "width", &m_width); + gst_structure_get_int (msgstruct, "height", &m_height); + if (strstr(eventname, "Changed")) + m_event((iPlayableService*)this, evVideoSizeChanged); + } + else if (!strcmp(eventname, "eventFrameRateChanged") || !strcmp(eventname, "eventFrameRateAvail")) + { + gst_structure_get_int (msgstruct, "frame_rate", &m_framerate); + if (strstr(eventname, "Changed")) + m_event((iPlayableService*)this, evVideoFramerateChanged); + } + else if (!strcmp(eventname, "eventProgressiveChanged") || !strcmp(eventname, "eventProgressiveAvail")) + { + gst_structure_get_int (msgstruct, "progressive", &m_progressive); + if (strstr(eventname, "Changed")) + m_event((iPlayableService*)this, evVideoProgressiveChanged); + } } } } @@ -1350,6 +1441,37 @@ void eServiceMP3::gstBusCall(GstBus *bus, GstMessage *msg) gst_message_parse_buffering(msg, &(m_bufferInfo.bufferPercent)); gst_message_parse_buffering_stats(msg, &mode, &(m_bufferInfo.avgInRate), &(m_bufferInfo.avgOutRate), &(m_bufferInfo.bufferingLeft)); m_event((iPlayableService*)this, evBuffering); + break; + } + case GST_MESSAGE_STREAM_STATUS: + { + GstStreamStatusType type; + GstElement *owner; + gst_message_parse_stream_status (msg, &type, &owner); + if ( type == GST_STREAM_STATUS_TYPE_CREATE && m_sourceinfo.is_streaming ) + { + if ( GST_IS_PAD(source) ) + owner = gst_pad_get_parent_element(GST_PAD(source)); + else if ( GST_IS_ELEMENT(source) ) + owner = GST_ELEMENT(source); + else + owner = 0; + if ( owner ) + { + GstElementFactory *factory = gst_element_get_factory(GST_ELEMENT(owner)); + const gchar *name = gst_plugin_feature_get_name(GST_PLUGIN_FEATURE(factory)); + if (!strcmp(name, "souphttpsrc")) + { + m_streamingsrc_timeout->start(HTTP_TIMEOUT*1000, true); + g_object_set (G_OBJECT (owner), "timeout", HTTP_TIMEOUT, NULL); + eDebug("eServiceMP3::GST_STREAM_STATUS_TYPE_CREATE -> setting timeout on %s to %is", name, HTTP_TIMEOUT); + } + + } + if ( GST_IS_PAD(source) ) + gst_object_unref(owner); + } + break; } default: break; @@ -1360,11 +1482,20 @@ void eServiceMP3::gstBusCall(GstBus *bus, GstMessage *msg) GstBusSyncReply eServiceMP3::gstBusSyncHandler(GstBus *bus, GstMessage *message, gpointer user_data) { eServiceMP3 *_this = (eServiceMP3*)user_data; - _this->m_pump.send(1); + _this->m_pump.send(Message(1)); /* wake */ return GST_BUS_PASS; } +void eServiceMP3::gstHTTPSourceSetAgent(GObject *object, GParamSpec *unused, gpointer user_data) +{ + eServiceMP3 *_this = (eServiceMP3*)user_data; + GstElement *source; + g_object_get(_this->m_gst_playbin, "source", &source, NULL); + g_object_set (G_OBJECT (source), "user-agent", _this->m_useragent.c_str(), NULL); + gst_object_unref(source); +} + audiotype_t eServiceMP3::gstCheckAudioPad(GstStructure* structure) { if (!structure) @@ -1405,43 +1536,103 @@ audiotype_t eServiceMP3::gstCheckAudioPad(GstStructure* structure) return atUnknown; } -void eServiceMP3::gstPoll(const int &msg) +void eServiceMP3::gstPoll(const Message &msg) { - /* ok, we have a serious problem here. gstBusSyncHandler sends - us the wakup signal, but likely before it was posted. - the usleep, an EVIL HACK (DON'T DO THAT!!!) works around this. - - I need to understand the API a bit more to make this work - proplerly. */ - if (msg == 1) + if (msg.type == 1) { GstBus *bus = gst_pipeline_get_bus (GST_PIPELINE (m_gst_playbin)); GstMessage *message; - usleep(1); - while ((message = gst_bus_pop (bus))) + while ((message = gst_bus_pop(bus))) { gstBusCall(bus, message); gst_message_unref (message); } } - else + else if (msg.type == 2) pullSubtitle(); + else if (msg.type == 3) + gstTextpadHasCAPS_synced(msg.d.pad); + else + eDebug("gstPoll unhandled Message %d\n", msg.type); } eAutoInitPtr<eServiceFactoryMP3> init_eServiceFactoryMP3(eAutoInitNumbers::service+1, "eServiceFactoryMP3"); void eServiceMP3::gstCBsubtitleAvail(GstElement *appsink, gpointer user_data) { - eServiceMP3 *_this = (eServiceMP3*)user_data; + eServiceMP3 *_this = (eServiceMP3*)user_data; eSingleLocker l(_this->m_subs_to_pull_lock); ++_this->m_subs_to_pull; - _this->m_pump.send(2); + _this->m_pump.send(Message(2)); +} + +void eServiceMP3::gstTextpadHasCAPS(GstPad *pad, GParamSpec * unused, gpointer user_data) +{ + eServiceMP3 *_this = (eServiceMP3*)user_data; + + gst_object_ref (pad); + + _this->m_pump.send(Message(3, pad)); +} + +// after messagepump +void eServiceMP3::gstTextpadHasCAPS_synced(GstPad *pad) +{ + GstCaps *caps; + + g_object_get (G_OBJECT (pad), "caps", &caps, NULL); + + eDebug("gstTextpadHasCAPS:: signal::caps = %s", gst_caps_to_string(caps)); + + if (caps) + { + subtitleStream subs; + +// eDebug("gstGhostpadHasCAPS_synced %p %d", pad, m_subtitleStreams.size()); + + if (!m_subtitleStreams.empty()) + subs = m_subtitleStreams[m_currentSubtitleStream]; + else { + subs.type = stUnknown; + subs.pad = pad; + } + + if ( subs.type == stUnknown ) + { + GstTagList *tags; +// eDebug("gstGhostpadHasCAPS::m_subtitleStreams[%i].type == stUnknown...", m_currentSubtitleStream); + + gchar *g_lang; + g_signal_emit_by_name (m_gst_playbin, "get-text-tags", m_currentSubtitleStream, &tags); + + g_lang = g_strdup_printf ("und"); + if ( tags && gst_is_tag_list(tags) ) + gst_tag_list_get_string(tags, GST_TAG_LANGUAGE_CODE, &g_lang); + + subs.language_code = std::string(g_lang); + subs.type = getSubtitleType(pad); + + if (!m_subtitleStreams.empty()) + m_subtitleStreams[m_currentSubtitleStream] = subs; + else + m_subtitleStreams.push_back(subs); + + g_free (g_lang); + } + +// eDebug("gstGhostpadHasCAPS:: m_gst_prev_subtitle_caps=%s equal=%i",gst_caps_to_string(m_gst_prev_subtitle_caps),gst_caps_is_equal(m_gst_prev_subtitle_caps, caps)); + + gst_caps_unref (caps); + } + + gst_object_unref (pad); } void eServiceMP3::pullSubtitle() { GstElement *sink; g_object_get (G_OBJECT (m_gst_playbin), "text-sink", &sink, NULL); + if (sink) { while (m_subs_to_pull && m_subtitle_pages.size() < 2) @@ -1457,17 +1648,31 @@ void eServiceMP3::pullSubtitle() gint64 buf_pos = GST_BUFFER_TIMESTAMP(buffer); gint64 duration_ns = GST_BUFFER_DURATION(buffer); size_t len = GST_BUFFER_SIZE(buffer); - unsigned char line[len+1]; - memcpy(line, GST_BUFFER_DATA(buffer), len); - line[len] = 0; - eDebug("got new subtitle @ buf_pos = %lld ns (in pts=%lld): '%s' ", buf_pos, buf_pos/11111, line); - ePangoSubtitlePage page; - gRGB rgbcol(0xD0,0xD0,0xD0); - page.m_elements.push_back(ePangoSubtitlePageElement(rgbcol, (const char*)line)); - page.show_pts = buf_pos / 11111L; - page.m_timeout = duration_ns / 1000000; - m_subtitle_pages.push_back(page); - pushSubtitles(); + eDebug("pullSubtitle m_subtitleStreams[m_currentSubtitleStream].type=%i",m_subtitleStreams[m_currentSubtitleStream].type); + + if ( m_subtitleStreams[m_currentSubtitleStream].type ) + { + if ( m_subtitleStreams[m_currentSubtitleStream].type < stVOB ) + { + unsigned char line[len+1]; + SubtitlePage page; + memcpy(line, GST_BUFFER_DATA(buffer), len); + line[len] = 0; + eDebug("got new text subtitle @ buf_pos = %lld ns (in pts=%lld): '%s' ", buf_pos, buf_pos/11111, line); + gRGB rgbcol(0xD0,0xD0,0xD0); + page.type = SubtitlePage::Pango; + page.pango_page.m_elements.push_back(ePangoSubtitlePageElement(rgbcol, (const char*)line)); + page.pango_page.m_show_pts = buf_pos / 11111L; + page.pango_page.m_timeout = duration_ns / 1000000; + m_subtitle_pages.push_back(page); + if (m_subtitle_pages.size()==1) + pushSubtitles(); + } + else + { + eDebug("unsupported subpicture... ignoring"); + } + } gst_buffer_unref(buffer); } } @@ -1479,45 +1684,56 @@ void eServiceMP3::pullSubtitle() void eServiceMP3::pushSubtitles() { - ePangoSubtitlePage page; - pts_t running_pts; while ( !m_subtitle_pages.empty() ) { + SubtitlePage &frontpage = m_subtitle_pages.front(); + pts_t running_pts; + gint64 diff_ms = 0; + gint64 show_pts = 0; + getPlayPosition(running_pts); - page = m_subtitle_pages.front(); - gint64 diff_ms = ( page.show_pts - running_pts ) / 90; - eDebug("eServiceMP3::pushSubtitles show_pts = %lld running_pts = %lld diff = %lld", page.show_pts, running_pts, diff_ms); - if (diff_ms < -100) - { - GstFormat fmt = GST_FORMAT_TIME; - gint64 now; - if (gst_element_query_position(m_gst_playbin, &fmt, &now) != -1) - { - now /= 11111; - diff_ms = abs((now - running_pts) / 90); - eDebug("diff < -100ms check decoder/pipeline diff: decoder: %lld, pipeline: %lld, diff: %lld", running_pts, now, diff_ms); - if (diff_ms > 100000) - { - eDebug("high decoder/pipeline difference.. assume decoder has now started yet.. check again in 1sec"); - m_subtitle_sync_timer->start(1000, true); - break; - } + + if (m_decoder_time_valid_state < 4) { + ++m_decoder_time_valid_state; + if (m_prev_decoder_time == running_pts) + m_decoder_time_valid_state = 0; + if (m_decoder_time_valid_state < 4) { +// if (m_decoder_time_valid_state) +// eDebug("%d: decoder time not valid! prev %lld, now %lld\n", m_decoder_time_valid_state, m_prev_decoder_time/90, running_pts/90); +// else +// eDebug("%d: decoder time not valid! now %lld\n", m_decoder_time_valid_state, running_pts/90); + m_subtitle_sync_timer->start(25, true); + m_prev_decoder_time = running_pts; + break; } - else - eDebug("query position for decoder/pipeline check failed!"); - eDebug("subtitle to late... drop"); + } + + if (frontpage.type == SubtitlePage::Pango) + show_pts = frontpage.pango_page.m_show_pts; + + diff_ms = ( show_pts - running_pts ) / 90; + eDebug("check subtitle: decoder: %lld, show_pts: %lld, diff: %lld ms", running_pts/90, show_pts/90, diff_ms); + + if ( diff_ms < -100 ) + { + eDebug("subtitle too late... drop"); m_subtitle_pages.pop_front(); } else if ( diff_ms > 20 ) { -// eDebug("start recheck timer"); - m_subtitle_sync_timer->start(diff_ms > 1000 ? 1000 : diff_ms, true); + eDebug("start timer"); + m_subtitle_sync_timer->start(diff_ms, true); break; } else // immediate show { - if (m_subtitle_widget) - m_subtitle_widget->setPage(page); + if ( m_subtitle_widget ) + { + eDebug("show!\n"); + if ( frontpage.type == SubtitlePage::Pango) + m_subtitle_widget->setPage(frontpage.pango_page); + m_subtitle_widget->show(); + } m_subtitle_pages.pop_front(); } } @@ -1525,12 +1741,20 @@ void eServiceMP3::pushSubtitles() pullSubtitle(); } + RESULT eServiceMP3::enableSubtitles(eWidget *parent, ePyObject tuple) { + eDebug ("eServiceMP3::enableSubtitles m_currentSubtitleStream=%i this=%p",m_currentSubtitleStream, this); ePyObject entry; int tuplesize = PyTuple_Size(tuple); int pid, type; gint text_pid = 0; + eSingleLocker l(m_subs_to_pull_lock); + +// GstPad *pad = 0; +// g_signal_emit_by_name (m_gst_playbin, "get-text-pad", m_currentSubtitleStream, &pad); +// gst_element_get_static_pad(m_gst_subtitlebin, "sink"); +// gulong subprobe_handler_id = gst_pad_add_buffer_probe (pad, G_CALLBACK (gstCBsubtitleDrop), NULL); if (!PyTuple_Check(tuple)) goto error_out; @@ -1547,10 +1771,11 @@ RESULT eServiceMP3::enableSubtitles(eWidget *parent, ePyObject tuple) if (m_currentSubtitleStream != pid) { - eSingleLocker l(m_subs_to_pull_lock); g_object_set (G_OBJECT (m_gst_playbin), "current-text", pid, NULL); + eDebug ("eServiceMP3::enableSubtitles g_object_set current-text = %i", pid); m_currentSubtitleStream = pid; m_subs_to_pull = 0; + m_prev_decoder_time = -1; m_subtitle_pages.clear(); } @@ -1561,6 +1786,9 @@ RESULT eServiceMP3::enableSubtitles(eWidget *parent, ePyObject tuple) g_object_get (G_OBJECT (m_gst_playbin), "current-text", &text_pid, NULL); eDebug ("eServiceMP3::switched to subtitle stream %i", text_pid); +// gst_pad_remove_buffer_probe (pad, subprobe_handler_id); + + m_event((iPlayableService*)this, evUpdatedInfo); return 0; @@ -1587,26 +1815,35 @@ PyObject *eServiceMP3::getCachedSubtitle() PyObject *eServiceMP3::getSubtitleList() { - eDebug("eServiceMP3::getSubtitleList"); - +// eDebug("eServiceMP3::getSubtitleList"); ePyObject l = PyList_New(0); - int stream_count[sizeof(subtype_t)]; - for ( unsigned int i = 0; i < sizeof(subtype_t); i++ ) - stream_count[i] = 0; - + int stream_idx = 0; + for (std::vector<subtitleStream>::iterator IterSubtitleStream(m_subtitleStreams.begin()); IterSubtitleStream != m_subtitleStreams.end(); ++IterSubtitleStream) { subtype_t type = IterSubtitleStream->type; - ePyObject tuple = PyTuple_New(5); - PyTuple_SET_ITEM(tuple, 0, PyInt_FromLong(2)); - PyTuple_SET_ITEM(tuple, 1, PyInt_FromLong(stream_count[type])); - PyTuple_SET_ITEM(tuple, 2, PyInt_FromLong(int(type))); - PyTuple_SET_ITEM(tuple, 3, PyInt_FromLong(0)); - PyTuple_SET_ITEM(tuple, 4, PyString_FromString((IterSubtitleStream->language_code).c_str())); - PyList_Append(l, tuple); - Py_DECREF(tuple); - stream_count[type]++; + switch(type) + { + case stUnknown: + case stVOB: + case stPGS: + break; + default: + { + ePyObject tuple = PyTuple_New(5); +// eDebug("eServiceMP3::getSubtitleList idx=%i type=%i, code=%s", stream_idx, int(type), (IterSubtitleStream->language_code).c_str()); + PyTuple_SET_ITEM(tuple, 0, PyInt_FromLong(2)); + PyTuple_SET_ITEM(tuple, 1, PyInt_FromLong(stream_idx)); + PyTuple_SET_ITEM(tuple, 2, PyInt_FromLong(int(type))); + PyTuple_SET_ITEM(tuple, 3, PyInt_FromLong(0)); + PyTuple_SET_ITEM(tuple, 4, PyString_FromString((IterSubtitleStream->language_code).c_str())); + PyList_Append(l, tuple); + Py_DECREF(tuple); + } + } + stream_idx++; } + eDebug("eServiceMP3::getSubtitleList finished"); return l; } @@ -1725,6 +1962,3 @@ void eServiceMP3::setPCMDelay(int delay) } } -#else -#warning gstreamer not available, not building media player -#endif diff --git a/lib/service/servicemp3.h b/lib/service/servicemp3.h index 56a068b9..f3cc6ddf 100644 --- a/lib/service/servicemp3.h +++ b/lib/service/servicemp3.h @@ -1,7 +1,6 @@ #ifndef __servicemp3_h #define __servicemp3_h -#ifdef HAVE_GSTREAMER #include <lib/base/message.h> #include <lib/service/iservice.h> #include <lib/dvb/pmt.h> @@ -47,7 +46,7 @@ public: typedef struct _GstElement GstElement; typedef enum { atUnknown, atMPEG, atMP3, atAC3, atDTS, atAAC, atPCM, atOGG, atFLAC } audiotype_t; -typedef enum { stPlainText, stSSA, stSRT } subtype_t; +typedef enum { stUnknown, stPlainText, stSSA, stASS, stSRT, stVOB, stPGS } subtype_t; typedef enum { ctNone, ctMPEGTS, ctMPEGPS, ctMKV, ctAVI, ctMP4, ctVCD, ctCDA } containertype_t; class eServiceMP3: public iPlayableService, public iPauseableService, @@ -168,12 +167,18 @@ public: int bufferPercent; int avgInRate; int avgOutRate; - long long bufferingLeft; + int64_t bufferingLeft; bufferInfo() :bufferPercent(0), avgInRate(0), avgOutRate(0), bufferingLeft(-1) { } }; + struct errorInfo + { + std::string error_message; + std::string missing_codec; + }; + private: static int pcm_delay; static int ac3_delay; @@ -190,38 +195,76 @@ private: eServiceReference m_ref; int m_buffer_size; bufferInfo m_bufferInfo; + errorInfo m_errorInfo; eServiceMP3(eServiceReference ref); Signal2<void,iPlayableService*,int> m_event; enum { stIdle, stRunning, stStopped, + }; + int m_state; + GstElement *m_gst_playbin; + GstTagList *m_stream_tags; + + struct Message + { + Message() + :type(-1) + {} + Message(int type) + :type(type) + {} + Message(int type, GstPad *pad) + :type(type) + { + d.pad=pad; + } + + int type; + union { + GstPad *pad; // for msg type 3 + } d; + }; + + eFixedMessagePump<Message> m_pump; + + audiotype_t gstCheckAudioPad(GstStructure* structure); + void gstBusCall(GstBus *bus, GstMessage *msg); + static GstBusSyncReply gstBusSyncHandler(GstBus *bus, GstMessage *message, gpointer user_data); + static void gstTextpadHasCAPS(GstPad *pad, GParamSpec * unused, gpointer user_data); + void gstTextpadHasCAPS_synced(GstPad *pad); + static void gstCBsubtitleAvail(GstElement *element, gpointer user_data); + GstPad* gstCreateSubtitleSink(eServiceMP3* _this, subtype_t type); + void gstPoll(const Message&); + static void gstHTTPSourceSetAgent(GObject *source, GParamSpec *unused, gpointer user_data); + + struct SubtitlePage + { + enum { Unknown, Pango, Vob } type; + ePangoSubtitlePage pango_page; + eVobSubtitlePage vob_page; }; - int m_state; - GstElement *m_gst_playbin; - GstTagList *m_stream_tags; - eFixedMessagePump<int> m_pump; - std::string m_error_message; - - audiotype_t gstCheckAudioPad(GstStructure* structure); - void gstBusCall(GstBus *bus, GstMessage *msg); - static GstBusSyncReply gstBusSyncHandler(GstBus *bus, GstMessage *message, gpointer user_data); - static void gstCBsubtitleAvail(GstElement *element, gpointer user_data); - GstPad* gstCreateSubtitleSink(eServiceMP3* _this, subtype_t type); - void gstPoll(const int&); - - std::list<ePangoSubtitlePage> m_subtitle_pages; - ePtr<eTimer> m_subtitle_sync_timer; - void pushSubtitles(); - void pullSubtitle(); - int m_subs_to_pull; + + std::list<SubtitlePage> m_subtitle_pages; + ePtr<eTimer> m_subtitle_sync_timer; + + ePtr<eTimer> m_streamingsrc_timeout; + pts_t m_prev_decoder_time; + int m_decoder_time_valid_state; + + void pushSubtitles(); + void pullSubtitle(); + void sourceTimeout(); + int m_subs_to_pull; + sourceStream m_sourceinfo; eSingleLock m_subs_to_pull_lock; gulong m_subs_to_pull_handler_id; RESULT seekToImpl(pts_t to); gint m_aspect, m_width, m_height, m_framerate, m_progressive; + std::string m_useragent; RESULT trickSeek(gdouble ratio); }; -#endif #endif |
