Add QuadPiP plugin.
authorhschang <chang@dev3>
Mon, 21 Aug 2017 07:18:36 +0000 (16:18 +0900)
committerhschang <chang@dev3>
Mon, 21 Aug 2017 07:18:36 +0000 (16:18 +0900)
22 files changed:
configure.ac
lib/dvb/decoder.cpp
lib/gui/evideo.cpp
lib/gui/evideo.h
lib/python/Components/ServiceList.py
lib/python/Components/SystemInfo.py
lib/python/Plugins/Extensions/DVDPlayer/src/servicedvd.h
lib/python/Plugins/Extensions/Makefile.am
lib/python/Plugins/Extensions/QuadPiP/Makefile.am [new file with mode: 0644]
lib/python/Plugins/Extensions/QuadPiP/__init__.py [new file with mode: 0644]
lib/python/Plugins/Extensions/QuadPiP/keymap.xml [new file with mode: 0644]
lib/python/Plugins/Extensions/QuadPiP/meta/Makefile.am [new file with mode: 0644]
lib/python/Plugins/Extensions/QuadPiP/meta/plugin_quadpip.xml [new file with mode: 0644]
lib/python/Plugins/Extensions/QuadPiP/plugin.py [new file with mode: 0644]
lib/python/Plugins/Extensions/QuadPiP/qpip.py [new file with mode: 0644]
lib/python/Screens/Makefile.am
lib/python/Screens/QuadPiP.py [new file with mode: 0644]
lib/service/iservice.h
lib/service/servicedvb.cpp
lib/service/servicedvb.h
lib/service/servicehdmi.h
lib/service/servicemp3.h

index 36488af..286e71e 100644 (file)
@@ -312,6 +312,8 @@ lib/python/Plugins/SystemPlugins/Ultimo4kMiscControl/Makefile
 lib/python/Plugins/SystemPlugins/Ultimo4kMiscControl/meta/Makefile
 lib/python/Plugins/SystemPlugins/PvrDescrambleConvert/Makefile
 lib/python/Plugins/SystemPlugins/PvrDescrambleConvert/meta/Makefile
 lib/python/Plugins/SystemPlugins/Ultimo4kMiscControl/meta/Makefile
 lib/python/Plugins/SystemPlugins/PvrDescrambleConvert/Makefile
 lib/python/Plugins/SystemPlugins/PvrDescrambleConvert/meta/Makefile
+lib/python/Plugins/Extensions/QuadPiP/Makefile
+lib/python/Plugins/Extensions/QuadPiP/meta/Makefile
 lib/python/Tools/Makefile
 lib/service/Makefile
 lib/components/Makefile
 lib/python/Tools/Makefile
 lib/service/Makefile
 lib/components/Makefile
index 2c8044b..2091114 100644 (file)
@@ -186,7 +186,21 @@ int eDVBAudio::startPid(int pid, int type)
                pes.pid      = pid;
                pes.input    = DMX_IN_FRONTEND;
                pes.output   = DMX_OUT_DECODER;
                pes.pid      = pid;
                pes.input    = DMX_IN_FRONTEND;
                pes.output   = DMX_OUT_DECODER;
-               pes.pes_type = m_dev ? DMX_PES_AUDIO1 : DMX_PES_AUDIO0; /* FIXME */
+               switch (m_dev)
+               {
+               case 0:
+                       pes.pes_type = DMX_PES_AUDIO0;
+                       break;
+               case 1:
+                       pes.pes_type = DMX_PES_AUDIO1;
+                       break;
+               case 2:
+                       pes.pes_type = DMX_PES_AUDIO2;
+                       break;
+               case 3:
+                       pes.pes_type = DMX_PES_AUDIO3;
+                       break;
+               }
                pes.flags    = 0;
                eDebugNoNewLine("DMX_SET_PES_FILTER(0x%02x) - audio - ", pid);
                if (::ioctl(m_fd_demux, DMX_SET_PES_FILTER, &pes) < 0)
                pes.flags    = 0;
                eDebugNoNewLine("DMX_SET_PES_FILTER(0x%02x) - audio - ", pid);
                if (::ioctl(m_fd_demux, DMX_SET_PES_FILTER, &pes) < 0)
@@ -500,7 +514,21 @@ int eDVBVideo::startPid(int pid, int type)
                pes.pid      = pid;
                pes.input    = DMX_IN_FRONTEND;
                pes.output   = DMX_OUT_DECODER;
                pes.pid      = pid;
                pes.input    = DMX_IN_FRONTEND;
                pes.output   = DMX_OUT_DECODER;
-               pes.pes_type = m_dev ? DMX_PES_VIDEO1 : DMX_PES_VIDEO0; /* FIXME */
+               switch (m_dev)
+               {
+               case 0:
+                       pes.pes_type = DMX_PES_VIDEO0;
+                       break;
+               case 1:
+                       pes.pes_type = DMX_PES_VIDEO1;
+                       break;
+               case 2:
+                       pes.pes_type = DMX_PES_VIDEO2;
+                       break;
+               case 3:
+                       pes.pes_type = DMX_PES_VIDEO3;
+                       break;
+               }
                pes.flags    = 0;
                eDebugNoNewLine("DMX_SET_PES_FILTER(0x%02x) - video - ", pid);
                if (::ioctl(m_fd_demux, DMX_SET_PES_FILTER, &pes) < 0)
                pes.flags    = 0;
                eDebugNoNewLine("DMX_SET_PES_FILTER(0x%02x) - video - ", pid);
                if (::ioctl(m_fd_demux, DMX_SET_PES_FILTER, &pes) < 0)
@@ -829,7 +857,21 @@ int eDVBPCR::startPid(int pid)
        pes.pid      = pid;
        pes.input    = DMX_IN_FRONTEND;
        pes.output   = DMX_OUT_DECODER;
        pes.pid      = pid;
        pes.input    = DMX_IN_FRONTEND;
        pes.output   = DMX_OUT_DECODER;
-       pes.pes_type = m_dev ? DMX_PES_PCR1 : DMX_PES_PCR0; /* FIXME */
+       switch (m_dev)
+       {
+       case 0:
+               pes.pes_type = DMX_PES_PCR0;
+               break;
+       case 1:
+               pes.pes_type = DMX_PES_PCR1;
+               break;
+       case 2:
+               pes.pes_type = DMX_PES_PCR2;
+               break;
+       case 3:
+               pes.pes_type = DMX_PES_PCR3;
+               break;
+       }
        pes.flags    = 0;
        eDebugNoNewLine("DMX_SET_PES_FILTER(0x%02x) - pcr - ", pid);
        if (::ioctl(m_fd_demux, DMX_SET_PES_FILTER, &pes) < 0)
        pes.flags    = 0;
        eDebugNoNewLine("DMX_SET_PES_FILTER(0x%02x) - pcr - ", pid);
        if (::ioctl(m_fd_demux, DMX_SET_PES_FILTER, &pes) < 0)
index 745571d..f5876e9 100644 (file)
@@ -30,7 +30,7 @@ void setFullsize()
 }
 
 eVideoWidget::eVideoWidget(eWidget *parent)
 }
 
 eVideoWidget::eVideoWidget(eWidget *parent)
-       :eLabel(parent), m_fb_size(720, 576), m_state(0), m_decoder(1)
+       :eLabel(parent), m_fb_size(720, 576), m_state(0), m_decoder(1), m_adjust_position(true)
 {
        if (!fullsizeTimer)
        {
 {
        if (!fullsizeTimer)
        {
@@ -118,14 +118,17 @@ void eVideoWidget::updatePosition(int disable)
        int width = pos.width() * 720 / m_fb_size.width();
        int height = pos.height() * 576 / m_fb_size.height();
 
        int width = pos.width() * 720 / m_fb_size.width();
        int height = pos.height() * 576 / m_fb_size.height();
 
-       int tmp = left - (width * 4) / 100;
-       left = tmp < 0 ? 0 : tmp;
-       tmp = top - (height * 4) / 100;
-       top = tmp < 0 ? 0 : tmp;
-       tmp = (width * 108) / 100;
-       width = left + tmp > 720 ? 720 - left : tmp;
-       tmp = (height * 108) / 100;
-       height = top + tmp > 576 ? 576 - top : tmp;
+       if (m_adjust_position)
+       {
+               int tmp = left - (width * 4) / 100;
+               left = tmp < 0 ? 0 : tmp;
+               tmp = top - (height * 4) / 100;
+               top = tmp < 0 ? 0 : tmp;
+               tmp = (width * 108) / 100;
+               width = left + tmp > 720 ? 720 - left : tmp;
+               tmp = (height * 108) / 100;
+               height = top + tmp > 576 ? 576 - top : tmp;
+       }
 
 //     eDebug("picture recalced %d %d -> %d %d", left, top, width, height);
 
 
 //     eDebug("picture recalced %d %d -> %d %d", left, top, width, height);
 
@@ -172,3 +175,9 @@ void eVideoWidget::setDecoder(int decoder)
 {
        m_decoder = decoder;
 }
 {
        m_decoder = decoder;
 }
+
+void eVideoWidget::setAdjustPosition(bool value)
+{
+       m_adjust_position = value;
+}
+
index 2f1b91b..e6ff21d 100644 (file)
@@ -9,11 +9,13 @@ class eVideoWidget: public eLabel
        int m_state;
        eRect m_user_rect;
        int m_decoder;
        int m_state;
        eRect m_user_rect;
        int m_decoder;
+       bool m_adjust_position;
 public:
        eVideoWidget(eWidget *parent);
        ~eVideoWidget();
        void setDecoder(int target);
        void setFBSize(eSize size);
 public:
        eVideoWidget(eWidget *parent);
        ~eVideoWidget();
        void setDecoder(int target);
        void setFBSize(eSize size);
+       void setAdjustPosition(bool value);
 protected:
        int event(int event, void *data=0, void *data2=0);
        void updatePosition(int disable = 0);
 protected:
        int event(int event, void *data=0, void *data2=0);
        void updatePosition(int disable = 0);
index 6972d91..f99c7f0 100644 (file)
@@ -265,3 +265,7 @@ class ServiceList(HTMLComponent, GUIComponent):
                        self.l.setElementPosition(self.l.celServiceName, eRect(60, 0, self.instance.size().width()-60, self.ItemHeight))
                        self.l.setElementFont(self.l.celServiceInfo, self.ServiceInfoFont)
 
                        self.l.setElementPosition(self.l.celServiceName, eRect(60, 0, self.instance.size().width()-60, self.ItemHeight))
                        self.l.setElementFont(self.l.celServiceInfo, self.ServiceInfoFont)
 
+       def selectionEnabled(self, enabled):
+               if self.instance is not None:
+                       self.instance.setSelectionEnable(enabled)
+
index 6fcab1d..a1f0d6f 100644 (file)
@@ -32,4 +32,6 @@ SystemInfo["DeepstandbySupport"] = HardwareInfo().get_device_name() != "dm800"
 SystemInfo["HdmiInSupport"] = HardwareInfo().get_vu_device_name() == "ultimo4k"
 SystemInfo["WOWLSupport"] = HardwareInfo().get_vu_device_name() == "ultimo4k"
 SystemInfo["ScrambledPlayback"] = HardwareInfo().get_vu_device_name() in ("solo4k", "ultimo4k")
 SystemInfo["HdmiInSupport"] = HardwareInfo().get_vu_device_name() == "ultimo4k"
 SystemInfo["WOWLSupport"] = HardwareInfo().get_vu_device_name() == "ultimo4k"
 SystemInfo["ScrambledPlayback"] = HardwareInfo().get_vu_device_name() in ("solo4k", "ultimo4k")
+SystemInfo["FastChannelChange"] =  fileExists("/proc/stb/frontend/fbc/fcc")
+SystemInfo["MiniTV"] = fileExists("/proc/stb/lcd/live_enable")
 
 
index 80cfcf0..47e02d1 100644 (file)
@@ -45,6 +45,8 @@ public:
        RESULT streamed(ePtr<iStreamedService> &ptr) { ptr = 0; return -1; }
        RESULT cueSheet(ePtr<iCueSheet> &ptr);
 
        RESULT streamed(ePtr<iStreamedService> &ptr) { ptr = 0; return -1; }
        RESULT cueSheet(ePtr<iCueSheet> &ptr);
 
+       void setQpipMode(bool value, bool audio) { }
+
                // iPlayableService
        RESULT connectEvent(const Slot2<void,iPlayableService*,int> &event, ePtr<eConnection> &connection);
        RESULT start();
                // iPlayableService
        RESULT connectEvent(const Slot2<void,iPlayableService*,int> &event, ePtr<eConnection> &connection);
        RESULT start();
index d94b791..32b7e14 100755 (executable)
@@ -1,7 +1,7 @@
 installdir = $(pkglibdir)/python/Plugins/Extensions
 
 SUBDIRS = TuxboxPlugins CutListEditor PicturePlayer MediaScanner MediaPlayer GraphMultiEPG SocketMMI DVDBurn Modem WebBrowser \
 installdir = $(pkglibdir)/python/Plugins/Extensions
 
 SUBDIRS = TuxboxPlugins CutListEditor PicturePlayer MediaScanner MediaPlayer GraphMultiEPG SocketMMI DVDBurn Modem WebBrowser \
-       StreamTV DLNABrowser DLNAServer HbbTV BackupSuiteUSB BackupSuiteHDD SatipClient MiniTV
+       StreamTV DLNABrowser DLNAServer HbbTV BackupSuiteUSB BackupSuiteHDD SatipClient MiniTV QuadPiP
 
 if HAVE_LIBDDVD
 SUBDIRS += DVDPlayer
 
 if HAVE_LIBDDVD
 SUBDIRS += DVDPlayer
diff --git a/lib/python/Plugins/Extensions/QuadPiP/Makefile.am b/lib/python/Plugins/Extensions/QuadPiP/Makefile.am
new file mode 100644 (file)
index 0000000..9bd3320
--- /dev/null
@@ -0,0 +1,9 @@
+installdir = $(pkglibdir)/python/Plugins/Extensions/QuadPiP
+
+SUBDIRS = meta
+
+install_PYTHON =       \
+       __init__.py \
+       plugin.py \
+       qpip.py \
+       keymap.xml
diff --git a/lib/python/Plugins/Extensions/QuadPiP/__init__.py b/lib/python/Plugins/Extensions/QuadPiP/__init__.py
new file mode 100644 (file)
index 0000000..139597f
--- /dev/null
@@ -0,0 +1,2 @@
+
+
diff --git a/lib/python/Plugins/Extensions/QuadPiP/keymap.xml b/lib/python/Plugins/Extensions/QuadPiP/keymap.xml
new file mode 100644 (file)
index 0000000..4361718
--- /dev/null
@@ -0,0 +1,23 @@
+<keymap>
+       <map context="QuadPipSetupActions">
+               <key id="KEY_OK" mapto="ok" flags="m" />
+               <key id="KEY_EXIT" mapto="cancel" flags="m" />
+               <key id="KEY_UP" mapto="up" flags="m" />
+               <key id="KEY_DOWN" mapto="down" flags="m" />
+               <key id="KEY_LEFT" mapto="left" flags="m" />
+               <key id="KEY_RIGHT" mapto="right" flags="m" />
+               <key id="KEY_RED" mapto="red" flags="m" />
+               <key id="KEY_GREEN" mapto="green" flags="m" />
+               <key id="KEY_YELLOW" mapto="yellow" flags="m" />
+               <key id="KEY_BLUE" mapto="blue" flags="m" />
+               <key id="KEY_MENU" mapto="menu" flags="m" />
+               <key id="KEY_CHANNELUP" mapto="channelup" flags="m" />
+               <key id="KEY_CHANNELDOWN" mapto="channeldown" flags="m" />
+               <key id="KEY_PREVIOUS" mapto="channelPrev" flags="m" />
+               <key id="KEY_NEXT" mapto="channelNext" flags="m" />
+       </map>
+       <map context="QuadPipChannelEditActions">
+               <key id="KEY_INFO" mapto="toggleList" flags="m" />
+               <key id="KEY_VIDEO" mapto="editName" flags="m" />
+       </map>
+</keymap>
diff --git a/lib/python/Plugins/Extensions/QuadPiP/meta/Makefile.am b/lib/python/Plugins/Extensions/QuadPiP/meta/Makefile.am
new file mode 100644 (file)
index 0000000..b29c526
--- /dev/null
@@ -0,0 +1,3 @@
+installdir = $(datadir)/meta
+
+dist_install_DATA = plugin_quadpip.xml
diff --git a/lib/python/Plugins/Extensions/QuadPiP/meta/plugin_quadpip.xml b/lib/python/Plugins/Extensions/QuadPiP/meta/plugin_quadpip.xml
new file mode 100644 (file)
index 0000000..cec9642
--- /dev/null
@@ -0,0 +1,17 @@
+<default>
+         <prerequisites>
+                    <tag type="Display" />
+                    <tag type="System" />
+         </prerequisites>
+          <info>
+                    <author>hschang</author>
+                    <name>Quad PiP</name>
+                    <packagename>enigma2-plugin-extensions-quadpip</packagename>
+                    <shortdescription>Support Quad PiP of your VU+</shortdescription>
+                    <description>Support Quad PiP of your VU+</description>
+          </info>
+
+         <files type="package"> <!-- without version, without .ipk -->
+               <file type="package" name="enigma2-plugin-extensions-quadpip" />
+       </files>
+</default>
diff --git a/lib/python/Plugins/Extensions/QuadPiP/plugin.py b/lib/python/Plugins/Extensions/QuadPiP/plugin.py
new file mode 100644 (file)
index 0000000..928de86
--- /dev/null
@@ -0,0 +1,31 @@
+from Plugins.Plugin import PluginDescriptor
+from Components.PluginComponent import plugins
+from Components.config import config, ConfigSubsection, ConfigSelection
+from enigma import eDBoxLCD
+
+from qpip import QuadPipScreen, setDecoderMode
+
+def main(session, **kwargs):
+       session.open(QuadPipScreen)
+
+def autoStart(reason, **kwargs):
+       if reason == 0:
+               setDecoderMode("normal")
+       elif reason == 1:
+               pass
+
+def Plugins(**kwargs):
+       list = []
+       list.append(
+               PluginDescriptor(name=_("Enable Quad PIP"),
+               description="Quad Picture in Picture",
+               where = [PluginDescriptor.WHERE_EXTENSIONSMENU],
+               fnc = main))
+
+       list.append(
+               PluginDescriptor(
+               where = [PluginDescriptor.WHERE_AUTOSTART],
+               fnc = autoStart))
+
+       return list
+
diff --git a/lib/python/Plugins/Extensions/QuadPiP/qpip.py b/lib/python/Plugins/Extensions/QuadPiP/qpip.py
new file mode 100644 (file)
index 0000000..72a4819
--- /dev/null
@@ -0,0 +1,1008 @@
+from Screens.Screen import Screen
+from Components.ConfigList import ConfigListScreen
+from Components.config import config, ConfigSubsection, ConfigSelection, getConfigListEntry
+from Components.ActionMap import ActionMap
+from Components.Pixmap import Pixmap, MovingPixmap
+from Screens.MessageBox import MessageBox
+from Components.Sources.StaticText import StaticText
+from Plugins.Plugin import PluginDescriptor
+from Tools.Directories import fileExists
+from Tools.Directories import resolveFilename, SCOPE_PLUGINS
+
+from Components.Sources.List import List
+from Components.Label import Label
+from Components.ActionMap import HelpableActionMap
+from Components.MenuList import MenuList
+
+from Screens.ChannelSelection import ChannelSelectionBase
+from enigma import eServiceReference
+from enigma import eListboxPythonMultiContent
+from enigma import eTimer
+from ServiceReference import ServiceReference
+from Components.FileList import FileList
+from Components.Button import Button
+from Screens.ChoiceBox import ChoiceBox
+from Screens.QuadPiP import QuadPiP
+
+from Screens.VirtualKeyBoard import VirtualKeyBoard
+from Screens.HelpMenu import HelpableScreen
+
+import pickle
+import os
+
+from Components.config import config, ConfigSubsection, ConfigNumber
+from Components.Slider import Slider
+
+from Components.SystemInfo import SystemInfo
+
+config.plugins.quadpip = ConfigSubsection()
+config.plugins.quadpip.lastchannel = ConfigNumber(default = 1)
+
+ENABLE_QPIP_PROCPATH = "/proc/stb/video/decodermode"
+
+def setDecoderMode(value):
+       if os.access(ENABLE_QPIP_PROCPATH, os.F_OK):
+               fd = open(ENABLE_QPIP_PROCPATH,"w")
+               fd.write(value)
+               fd.close()
+
+               # read to check
+               fd = open(ENABLE_QPIP_PROCPATH,"r")
+               data = fd.read()
+               fd.close()
+
+               return data.strip() == value
+
+class QuadPipChannelEntry:
+       def __init__(self, name, idx, ch1, ch2, ch3, ch4):
+               self.name = name
+               self.idx = idx
+               self.channel = {"1" : ch1, "2" : ch2, "3" : ch1, "4" : ch1,}
+
+       def __str__(self):
+               return "idx : %d, name : %s, ch0 : %s, ch1 : %s, ch2 : %s, ch3 : %s"\
+                                       % (self.idx, self.name, self.channel.get("1"), self.channel.get("2"), self.channel.get("3"), self.channel.get("4"))
+
+       def __cmp__(self, other):
+               return self.idx - other.idx
+
+       def getName(self):
+               return self.name
+
+       def getIndex(self):
+               return self.idx
+
+       def setChannel(self, idx, chName, sref):
+               if self.channel.has_key(idx):
+                       self.channel[idx] = (chName, sref)
+                       return True
+
+               return False
+
+       def deleteChannel(self, idx):
+               if self.channel.has_key(idx):
+                       self.channel[idx] = None
+                       return True
+
+               return False
+
+       def getChannel(self, idx):
+               return self.channel.get(idx, None)
+
+       def getChannelName(self, idx):
+               chName = None
+               ch = self.getChannel(idx)
+               if ch:
+                       chName = ch[0]
+
+               if chName is None:
+                       chName = " <No Channel>"
+
+               return chName
+
+       def getChannelSref(self, idx):
+               chSref = None
+               ch = self.getChannel(idx)
+               if ch:
+                       chSref = ch[1]
+               return chSref
+
+       def setIndex(self, idx):
+               self.idx = idx
+
+       def setName(self, name):
+               self.name = name
+
+class QuadPipChannelData:
+       def __init__(self):
+               self.PipChannelList = []
+               self.pipChannelDataPath = "/etc/enigma2/quadPipChannels.dat"
+               self.dataLoad()
+
+       def dataSave(self):
+               fd = open(self.pipChannelDataPath, "w")
+               pickle.dump(self.PipChannelList, fd)
+               fd.close()
+               #print "[*] dataSave"
+
+       def dataLoad(self):
+               if not os.access(self.pipChannelDataPath, os.R_OK):
+                       return
+
+               fd = open(self.pipChannelDataPath, "r")
+               self.PipChannelList = pickle.load(fd)
+               fd.close()
+               #print "[*] dataLoad"
+
+       def getPipChannels(self):
+               return self.PipChannelList
+
+       def length(self):
+               return len(self.PipChannelList)
+
+class QuadPipChannelList(QuadPipChannelData):
+       def __init__(self):
+               QuadPipChannelData.__init__(self)
+               self._curIdx = config.plugins.quadpip.lastchannel.value # starting from 1
+               self.defaultEntryPreName = "Quad PiP channel "
+
+       def saveAll(self):
+               self.dataSave()
+               config.plugins.quadpip.lastchannel.value = self._curIdx
+               config.plugins.quadpip.lastchannel.save()
+
+       def setIdx(self, value):
+               if self._curIdx != value:
+                       self._curIdx = value
+                       config.plugins.quadpip.lastchannel.value = self._curIdx
+                       config.plugins.quadpip.lastchannel.save()
+
+       def getIdx(self):
+               return self._curIdx
+
+       def getCurrentChannel(self):
+               return self.getChannel(self._curIdx)
+
+       def getChannel(self, idx):
+               for ch in self.PipChannelList:
+                       if idx == ch.getIndex():
+                               return ch
+
+               return None
+
+       def addNewChannel(self, newChannel):
+               self.PipChannelList.append(newChannel)
+
+       def removeChannel(self, _channel):
+               if self.getIdx() == _channel.getIndex():
+                       self.setIdx(0) # set invalid index
+
+               self.PipChannelList.remove(_channel)
+
+       def sortPipChannelList(self):
+               self.PipChannelList.sort()
+               newIdx = 1
+               for ch in self.PipChannelList:
+                       ch.setIndex(newIdx)
+                       chName = ch.getName()
+                       if chName.startswith(self.defaultEntryPreName):
+                               ch.setName("%s%d" % (self.defaultEntryPreName, ch.getIndex()))
+                       newIdx += 1
+
+       def getDefaultPreName(self):
+               return self.defaultEntryPreName
+
+quad_pip_channel_list_instance = QuadPipChannelList()
+
+class CreateQuadPipChannelEntry(ChannelSelectionBase):
+       skin_default_1080p = """
+               <screen name="CreateQuadPipChannelEntry" position="center,center" size="1500,850" flags="wfNoBorder">
+                       <widget source="Title" render="Label" position="100,60" size="1300,60" zPosition="3" font="Semiboldit;52" halign="left" valign="center" backgroundColor="#25062748" transparent="1" />
+                       <ePixmap pixmap="skin_default/buttons/red.png" position="137,140" size="140,40" alphatest="blend" />
+                       <ePixmap pixmap="skin_default/buttons/green.png" position="492,140" size="140,40" alphatest="blend" />
+                       <ePixmap pixmap="skin_default/buttons/yellow.png" position="837,140" size="140,40" alphatest="blend" />
+                       <ePixmap pixmap="skin_default/buttons/blue.png" position="1192,140" size="140,40" alphatest="blend" />
+                       <widget name="key_red" position="137,140" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#9f1313" foregroundColor="#ffffff" transparent="1" />
+                       <widget name="key_green" position="492,140" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#1f771f" foregroundColor="#ffffff" transparent="1" />
+                       <widget name="key_yellow" position="837,140" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#a08500" foregroundColor="#ffffff" transparent="1" />
+                       <widget name="key_blue" position="1192,140" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#18188b" foregroundColor="#ffffff" transparent="1" />
+                       <widget name="list" position="100,200" size="1250,365" serviceItemHeight="40" serviceNumberFont="Regular;28" serviceNameFont="Regular;28" serviceInfoFont="Semibold;24" foregroundColorServiceNotAvail="#58595b" transparent="1" scrollbarMode="showOnDemand" />
+                       <widget name="textChannels" position="100,580" size="1250,30" font="Regular;33" transparent="1" />
+                       <widget name="selectedList" position="110,620" size="700,160" font="Regular;28" itemHeight="40" transparent="1" />
+                       <widget name="description" position="860,630" size="650,160" font="Regular;28" halign="left" transparent="1" />
+               </screen>
+               """
+       skin_default_720p = """
+               <screen name="CreateQuadPipChannelEntry" position="center,center" size="1000,610" flags="wfNoBorder">
+                       <widget source="Title" render="Label" position="40,40" size="910,40" zPosition="3" font="Semiboldit;32" backgroundColor="#25062748" transparent="1" />
+                       <ePixmap pixmap="skin_default/buttons/red.png" position="75,80" size="140,40" alphatest="blend" />
+                       <ePixmap pixmap="skin_default/buttons/green.png" position="325,80" size="140,40" alphatest="blend" />
+                       <ePixmap pixmap="skin_default/buttons/yellow.png" position="575,80" size="140,40" alphatest="blend" />
+                       <ePixmap pixmap="skin_default/buttons/blue.png" position="825,80" size="140,40" alphatest="blend" />
+                       <widget name="key_red" position="75,80" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#9f1313" foregroundColor="#ffffff" transparent="1" />
+                       <widget name="key_green" position="325,80" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#1f771f" foregroundColor="#ffffff" transparent="1" />
+                       <widget name="key_yellow" position="575,80" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#a08500" foregroundColor="#ffffff" transparent="1" />
+                       <widget name="key_blue" position="825,80" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#18188b" foregroundColor="#ffffff" transparent="1" />
+                       <widget name="list" position="60,130" size="700,255" transparent="1" scrollbarMode="showOnDemand" foregroundColorServiceNotAvail="#58595b" />
+                       <widget name="textChannels" position="60,400" size="850,20" font="Regular;24" transparent="1" />
+                       <widget name="selectedList" position="70,430" size="480,150" font="Regular;22" itemHeight="25" transparent="1" />
+                       <widget name="description" position="590,460" size="350,150" font="Regular;22" halign="left" transparent="1" />
+               </screen>
+               """
+       skin_default_576p = """
+               <screen name="CreateQuadPipChannelEntry" position="center,center" size="680,520" flags="wfNoBorder">
+                       <widget source="Title" render="Label" position="30,20" size="600,30" zPosition="3" font="Regular;22" backgroundColor="#25062748" transparent="1" />
+                       <ePixmap pixmap="skin_default/buttons/red.png" position="15,60" size="140,40" alphatest="blend" />
+                       <ePixmap pixmap="skin_default/buttons/green.png" position="185,60" size="140,40" alphatest="blend" />
+                       <ePixmap pixmap="skin_default/buttons/yellow.png" position="355,60" size="140,40" alphatest="blend" />
+                       <ePixmap pixmap="skin_default/buttons/blue.png" position="525,60" size="140,40" alphatest="blend" />
+                       <widget name="key_red" position="15,60" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#9f1313" foregroundColor="#ffffff" transparent="1" />
+                       <widget name="key_green" position="185,60" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#1f771f" foregroundColor="#ffffff" transparent="1" />
+                       <widget name="key_yellow" position="355,60" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#a08500" foregroundColor="#ffffff" transparent="1" />
+                       <widget name="key_blue" position="525,60" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#18188b" foregroundColor="#ffffff" transparent="1" />
+                       <widget name="list" position="50,115" size="580,230" transparent="1" scrollbarMode="showOnDemand" foregroundColorServiceNotAvail="#58595b" />
+                       <widget name="textChannels" position="45,360" size="295,20" font="Regular;22" transparent="1" />
+                       <widget name="selectedList" position="50,385" size="290,100" font="Regular;20" itemHeight="22" transparent="1" />
+                       <widget name="description" position="360,390" size="310,140" font="Regular;20" halign="left" transparent="1" />
+               </screen>
+               """
+       def __init__(self, session, defaultEntryName, channel = None):
+               ChannelSelectionBase.__init__(self, session)
+
+               self["actions"] = ActionMap(["OkCancelActions", "DirectionActions", "QuadPipChannelEditActions"],
+               {
+                       "cancel": self.Exit,
+                       "ok": self.channelSelected,
+                       "toggleList" : self.toggleCurrList,
+                       "editName" : self.editEntryName,
+                       "up": self.goUp,
+                       "down": self.goDown,
+               }, -1)
+
+               self.session = session
+               dh = self.session.desktop.size().height()
+               self.skin = {1080:CreateQuadPipChannelEntry.skin_default_1080p, \
+                                               720:CreateQuadPipChannelEntry.skin_default_720p, \
+                                               576:CreateQuadPipChannelEntry.skin_default_576p}.get(dh, CreateQuadPipChannelEntry.skin_default_1080p)
+
+               self.defaultEntryName = defaultEntryName
+               self["textChannels"] = Label(_(" "))
+               self["description"] = Label(_(" "))
+
+               self.currList = None
+
+               self.newChannel = channel
+               self.descChannels = []
+               self.prepareChannels()
+               self["selectedList"] = MenuList(self.descChannels, True)
+               self.selectedList = self["selectedList"]
+
+               self.onLayoutFinish.append(self.layoutFinished)
+
+       def layoutFinished(self):
+               self.setTvMode()
+               self.showFavourites()
+               self.switchToServices()
+               self.updateEntryName()
+
+       def updateEntryName(self):
+               self["textChannels"].setText("%s :" % self.newChannel.getName())
+
+       def editEntryName(self):
+               self.session.openWithCallback(self.editEntryNameCB, VirtualKeyBoard, title = (_("Input channel name.")), text = self.newChannel.getName())
+
+       def editEntryNameCB(self, newName):
+               if newName:
+                       self.newChannel.setName(newName)
+                       self.updateEntryName()          
+
+       def updateDescription(self):
+               if self.currList == "channelList":
+                       desc = _("EPG key : Switch to quad PiP entry\nOk key : Add to new entry\nPVR key : Input channel name\nExit key : Finish channel edit")
+               else:
+                       desc = _("EPG key : Switch to channel list\nOk key : Remove selected channel\nPVR key : Input channel name\nExit key : Finish channel edit")
+
+               self["description"].setText(desc)
+
+       def prepareChannels(self):
+               if self.newChannel is None:
+                       self.newChannel = QuadPipChannelEntry(self.defaultEntryName, 99999, None, None, None, None)
+
+               self.updateDescChannels()
+
+       def updateDescChannels(self):
+               self.descChannels = []
+               for idx in range(1,5):
+                       sIdx = str(idx)
+                       _isEmpty = False
+                       chName = self.newChannel.getChannelName(sIdx)
+                       if chName is None:
+                               chName = " <empty>"
+                               _isEmpty = True
+                       self.descChannels.append(("%d)  %s" % (idx, chName), sIdx, _isEmpty))   
+
+       def updateDescChannelList(self):
+               self["selectedList"].setList(self.descChannels)
+
+       def goUp(self):
+               if self.currList == "channelList":
+                       self.servicelist.moveUp()
+               else:
+                       self.selectedList.up()
+
+       def goDown(self):
+               if self.currList == "channelList":
+                       self.servicelist.moveDown()
+               else:
+                       self.selectedList.down()
+
+       def toggleCurrList(self):
+               if self.currList == "channelList":
+                       self.switchToSelected()
+               else:
+                       self.switchToServices()
+
+       def switchToServices(self):
+               self.servicelist.selectionEnabled(1)
+               self.selectedList.selectionEnabled(0)
+               self.currList = "channelList"
+               self.updateDescription()
+
+       def switchToSelected(self):
+               self.servicelist.selectionEnabled(0)
+               self.selectedList.selectionEnabled(1)
+               self.currList = "selectedList"
+               self.updateDescription()
+
+       def channelSelected(self): # just return selected service
+               if self.currList == "channelList":
+                       ref = self.getCurrentSelection()
+                       if (ref.flags & 7) == 7:
+                               self.enterPath(ref)
+                       elif not (ref.flags & eServiceReference.isMarker):
+                               ref = self.getCurrentSelection()
+                               serviceName = ServiceReference(ref).getServiceName()
+                               sref = ref.toString()
+                               #self.addChannel(serviceName, sref)
+                               _title = _('Choice where to put "%s"') % serviceName
+                               _list = []
+                               for idx in range(1,5):
+                                       sIdx = str(idx)
+                                       _isEmpty = False
+                                       chName = self.newChannel.getChannelName(sIdx)
+                                       _list.append((chName, sIdx, serviceName, sref, _isEmpty))
+
+                               self.session.openWithCallback(self.choiceIdxCallback, ChoiceBox, title=_title, list=tuple(_list))
+               else:
+                       self.removeChannel()
+
+       def choiceIdxCallback(self, answer):
+               if answer is not None:
+                       (desc, sIdx, serviceName, sref, _isEmpty) = answer
+                       self.addChannel(sIdx, serviceName, sref)
+
+       def addChannel(self, sIdx, serviceName, sref):
+               if self.newChannel.setChannel(sIdx, serviceName, sref):
+                       self.updateDescChannels()
+                       self.updateDescChannelList()
+
+       def removeChannel(self):
+               cur = self.selectedList.getCurrent()
+               if cur:
+                       sIdx = cur[1]
+                       if self.newChannel.deleteChannel(sIdx):
+                               self.updateDescChannels()
+                               self.updateDescChannelList()
+
+       def getNewChannel(self):
+               for idx in range(1,5):
+                       sIdx = str(idx)
+                       ch = self.newChannel.getChannel(sIdx)
+                       if ch is not None:
+                               return self.newChannel
+
+               return None
+
+       def Exit(self):
+               self.close(self.getNewChannel())
+
+class QuadPiPChannelSelection(Screen, HelpableScreen):
+       skin = """
+               <screen position="%s,%s" size="%d,%d">
+                       <ePixmap pixmap="skin_default/buttons/red.png" position="%d,%d" size="140,40" alphatest="on" />
+                       <ePixmap pixmap="skin_default/buttons/green.png" position="%d,%d" size="140,40" alphatest="on" />
+                       <ePixmap pixmap="skin_default/buttons/yellow.png" position="%d,%d" size="140,40" alphatest="on" />
+                       <ePixmap pixmap="skin_default/buttons/blue.png" position="%d,%d" size="140,40" alphatest="on" />
+                       <widget name="key_red" position="%d,%d" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" foregroundColor="#ffffff" backgroundColor="#9f1313" transparent="1" />
+                       <widget name="key_green" position="%d,%d" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" foregroundColor="#ffffff" backgroundColor="#1f771f" transparent="1" />
+                       <widget name="key_yellow" position="%d,%d" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" foregroundColor="#ffffff" backgroundColor="#a08500" transparent="1" />
+                       <widget name="key_blue" position="%d,%d" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" foregroundColor="#ffffff" backgroundColor="#18188b" transparent="1" />
+                       <widget source="ChannelList" render="Listbox" position="%d,%d" size="%d,%d" scrollbarMode="showOnDemand">
+                               <convert type="TemplatedMultiContent">
+                               {"template":
+                                       [
+                                               MultiContentEntryText(pos = (%d, %d), size = (%d, %d), font=0, flags = RT_HALIGN_LEFT|RT_VALIGN_CENTER, text = 0),
+                                               MultiContentEntryText(pos = (%d, %d), size = (%d, %d), font=1, flags = RT_HALIGN_LEFT|RT_VALIGN_CENTER, text = 1),
+                                               MultiContentEntryText(pos = (%d, %d), size = (%d, %d), font=1, flags = RT_HALIGN_LEFT|RT_VALIGN_CENTER, text = 2),
+                                               MultiContentEntryText(pos = (%d, %d), size = (%d, %d), font=1, flags = RT_HALIGN_LEFT|RT_VALIGN_CENTER, text = 3),
+                                               MultiContentEntryText(pos = (%d, %d), size = (%d, %d), font=1, flags = RT_HALIGN_LEFT|RT_VALIGN_CENTER, text = 4),
+                                       ],
+                                       "fonts": [gFont("Regular", %d), gFont("Regular", %d)],
+                                       "itemHeight": %d
+                               }
+                               </convert>
+                       </widget>
+               </screen>
+               """
+       def __init__(self, session):
+               self.session = session
+               Screen.__init__(self, session)
+               HelpableScreen.__init__(self)
+               self.setTitle(_("Quad PiP Channel Selection"))
+
+               dw = self.session.desktop.size().width()
+               dh = self.session.desktop.size().height()
+               pw, ph = {1080:("center", "center"), 720:("center", "center"), 576:("center", "20%")}.get(dh, ("center", "center"))
+               (sw, sh) = {1080:(dw/3, dh/2), 720:(int(dw/2), int(dh/1.5)), 576:(int(dw/1.3), int(dh/1.5))}.get(dh, (28, 24))
+               button_margin = 5
+               button_h = 40
+               list_y = 40+button_margin*3
+               self.fontSize = {1080:(28, 24), 720:(24,20), 576:(20,18)}.get(dh, (28, 24))
+               self.skin = QuadPiPChannelSelection.skin % (pw, ph, \
+                                                                                                               sw, sh+list_y, \
+                                                                                                               sw/8-70, button_margin, \
+                                                                                                               sw/8-70+sw/4, button_margin, \
+                                                                                                               sw/8-70+sw/4*2, button_margin, \
+                                                                                                               sw/8-70+sw/4*3, button_margin, \
+                                                                                                               sw/8-70, button_margin, \
+                                                                                                               sw/8-70+sw/4, button_margin, \
+                                                                                                               sw/8-70+sw/4*2, button_margin, \
+                                                                                                               sw/8-70+sw/4*3, button_margin, \
+                                                                                                               0, list_y, sw, sh, \
+                                                                                                               sw/16, 1, sw-sw/16*2, sh/13, \
+                                                                                                               sw/11, 1+sh/13,                         sw-sw/16*2-sw/8, sh/18, \
+                                                                                                               sw/11, 1+sh/13+sh/18,   sw-sw/16*2-sw/8, sh/18, \
+                                                                                                               sw/11, 1+sh/13+sh/18*2,         sw-sw/16*2-sw/8, sh/18, \
+                                                                                                               sw/11, 1+sh/13+sh/18*3,         sw-sw/16*2-sw/8, sh/18, \
+                                                                                                               self.fontSize[0], self.fontSize[1], \
+                                                                                                               sh/3)
+               self["key_red"] = Label(_("Select"))
+               self["key_green"] = Label(_("Add"))
+               self["key_yellow"] = Label(_("Remove"))
+               self["key_blue"] = Label(_("Edit"))
+
+               self.PipChannelListApply = []
+               self["ChannelList"] = List(self.PipChannelListApply)
+
+               self["qpipActions"] = HelpableActionMap(self, "QuadPipSetupActions",
+               {
+                       "red": (self.keyRed, _("Select Quad Channels")),
+                       "green": (self.keyGreen, _("Add New Quad Channel Entry")),
+                       "yellow": (self.keyYellow, _("Remove Quad Channel Entry")),
+                       "blue": (self.keyBlue, _("Edit Quad Channel Entry")),
+               }, -2)
+
+               self["OkCancelActions"] = HelpableActionMap(self, "OkCancelActions",
+               {
+                       "ok": (self.keyOk, _("Select Quad Channels")),
+                       "cancel": (self.keyCancel, _("Exit Quad Channel Selection")),
+               }, -2)
+
+               self.oldPosition = None
+
+               global quad_pip_channel_list_instance
+               self.qpipChannelList = quad_pip_channel_list_instance
+
+               self.oldPosition = self.qpipChannelList.getIdx()-1
+
+               self.onLayoutFinish.append(self.layoutFinishedCB)
+
+       def layoutFinishedCB(self):
+               self.updateDisplay()
+               self.updatePosition()
+
+       def keyOk(self):
+               idx = self.getCurrentIndex()
+               if idx != -1:
+                       self.qpipChannelList.setIdx(idx)
+
+               self.close()
+
+       def keyCancel(self):
+               self.close()
+
+       def keyRed(self):
+               self.keyOk()
+
+       def keyGreen(self):
+               self.session.openWithCallback(self.CreateQuadPipChannelEntryCB, CreateQuadPipChannelEntry, self.getDefaultEntryName(), None)
+
+       def keyYellow(self):
+               curChannel = self.getSelectedChannel()
+               if curChannel:
+                       self.oldPosition = self["ChannelList"].getIndex()
+                       self.qpipChannelList.removeChannel(curChannel)
+                       self.updateChannelList()
+
+       def keyBlue(self):
+               curChannel = self.getSelectedChannel()
+               if curChannel:
+                       self.oldPosition = self["ChannelList"].getIndex()
+                       self.qpipChannelList.removeChannel(curChannel)
+                       self.session.openWithCallback(self.CreateQuadPipChannelEntryCB, CreateQuadPipChannelEntry, None, curChannel)
+
+       def getCurrentIndex(self):
+               idx = -1
+               cur = self["ChannelList"].getCurrent()
+               if cur:
+                       idx = cur[5]
+
+               return idx
+
+       def getSelectedChannel(self):
+               selectedChannel = None
+               idx = self.getCurrentIndex()
+               if idx != -1:
+                       selectedChannel = self.qpipChannelList.getChannel(idx)
+
+               return selectedChannel
+
+       def getDefaultEntryName(self):
+               return "%s%d" % (self.qpipChannelList.getDefaultPreName(), self.qpipChannelList.length() + 1)
+
+       def CreateQuadPipChannelEntryCB(self, newChannel):
+               if newChannel:
+                       self.qpipChannelList.addNewChannel(newChannel)
+                       self.qpipChannelList.sortPipChannelList()
+                       self.oldPosition = newChannel.getIndex()-1
+                       self.updateDisplay()
+                       self.updatePosition()
+
+       def updateDisplay(self):
+               self.PipChannelListApply = []
+               for ch in self.getChannelList():
+                       entry = []
+
+                       entryName = ch.getName()
+                       if not entryName:
+                               entryName = "%s%d" % (self.qpipChannelList.getDefaultPreName(), ch.getIndex())
+
+                       if self.qpipChannelList.getIdx() == ch.getIndex():
+                               entryName += " (current channel)"
+
+                       entry.append(entryName)
+                       entry.append("1) " + ch.getChannelName("1"))
+                       entry.append("2) " + ch.getChannelName("2"))
+                       entry.append("3) " + ch.getChannelName("3"))
+                       entry.append("4) " + ch.getChannelName("4"))
+                       entry.append(ch.getIndex())
+                       self.PipChannelListApply.append(tuple(entry))
+
+               self["ChannelList"].setList(self.PipChannelListApply)
+
+       def updatePosition(self):
+               if self.oldPosition:
+                       if self["ChannelList"].count() > self.oldPosition:
+                               self["ChannelList"].setIndex(self.oldPosition)
+                       self.oldPosition = None
+
+       def updateChannelList(self):
+               self.qpipChannelList.sortPipChannelList()
+               self.updateDisplay()
+               self.updatePosition()
+
+       def getChannelList(self):
+               return self.qpipChannelList.getPipChannels()
+
+class FocusShowHide:
+       STATE_HIDDEN = 0
+       STATE_SHOWN = 1
+
+       def __init__(self):
+               self.__state = self.STATE_SHOWN
+               self.hideTimer = eTimer()
+               self.hideTimer.callback.append(self.hideTimerCB)
+               self.onLayoutFinish.append(self.startHideTimer)
+
+       def startHideTimer(self):
+               self.hideTimer.stop()
+               self.hideTimer.start(5000, True)
+
+       def hideTimerCB(self):
+               self.hideFocus()
+
+       def isShown(self):
+               return self.__state == self.STATE_SHOWN
+
+       def showFocus(self):
+               self.show()
+               self.__state = self.STATE_SHOWN
+               self.startHideTimer()
+
+       def hideFocus(self):
+               self.hideTimer.stop()
+               self.hide()
+               self.__state = self.STATE_HIDDEN
+
+       def toggleShow(self):
+               if self.__state == self.STATE_SHOWN:
+                       self.hideFocus()
+               elif self.__state == self.STATE_HIDDEN:
+                       self.showFocus()
+
+class QuadPipScreen(Screen, FocusShowHide, HelpableScreen):
+       skin = """
+               <screen position="0,0" size="%d,%d" backgroundColor="transparent" flags="wfNoBorder">
+                   <widget name="ch1" position="240,240" zPosition="1" size="480,60" font="Regular; %d" halign="center" valign="center" foregroundColor="white" backgroundColor="#ffffffff" alphatest="on" />
+                   <widget name="ch2" position="1200,240" zPosition="1" size="480,60" font="Regular; %d" halign="center" valign="center" foregroundColor="white" backgroundColor="#ffffffff" alphatest="on" />
+                   <widget name="ch3" position="240,780" zPosition="1" size="480,60" font="Regular; %d" halign="center" valign="center" foregroundColor="white" backgroundColor="#ffffffff" alphatest="on" />
+                   <widget name="ch4" position="1200,780" zPosition="1" size="480,60" font="Regular; %d" halign="center" valign="center" foregroundColor="white" backgroundColor="#ffffffff" alphatest="on" />
+                       <widget name="text1" position="%d,%d" zPosition="2" size="%d,%d" font="Regular; %d" halign="left" valign="center" alphatest="on" />
+                       <widget name="text2" position="%d,%d" zPosition="2" size="%d,%d" font="Regular; %d" halign="left" valign="center" alphatest="on" />
+                       <widget name="focus" position="0,0" zPosition="-1" size="960,540" backgroundColor="#ffffffff" borderWidth="5" borderColor="#e61616" alphatest="on" />
+                 </screen>
+               """
+       def __init__(self, session):
+               self.session = session
+               self.session.qPips = None
+               Screen.__init__(self, session)
+               FocusShowHide.__init__(self)
+               HelpableScreen.__init__(self)
+               self.setTitle(_("Quad PiP Screen"))
+
+               self["actions"] = HelpableActionMap(self, "QuadPipSetupActions",
+               {
+                       "cancel": (self.keyExit, _("Exit quad PiP")),
+                       "ok": (self.keyOk, _("Zap focused channel on full screen")),
+                       "left": (self.keyLeft, _("Select channel audio")),
+                       "right": (self.keyRight, _("Select channel audio")),
+                       "up": (self.keyUp, _("Select channel audio")),
+                       "down": (self.keyDown, _("Select channel audio")),
+                       "channelup" : (self.KeyChannel, _("Show channel selection")),
+                       "channeldown" : (self.KeyChannel, _("Show channel selection")),
+                       "menu" : (self.KeyChannel, _("Show channel selection")),
+                       "channelPrev" : (self.KeyPrev, _("Prev quad PiP channel")),
+                       "channelNext" : (self.KeyNext, _("Next quad PiP channel")),
+                       "red" : (self.KeyRed, _("Show/Hide focus bar")),
+               }, -1)
+
+               self["ch1"] = Label(_(" "))
+               self["ch2"] = Label(_(" "))
+               self["ch3"] = Label(_(" "))
+               self["ch4"] = Label(_(" "))
+               self["text1"] = Label(_("  Red key : Show/Hide channel name"))
+               self["text2"] = Label(_("  Menu key : Select quad channel"))
+               self["focus"] = Slider(-1, -1)
+
+               self.currentPosition = 1 # 1~4
+               self.updatePositionList()
+
+               self.skin = QuadPipScreen.skin % (self.session.desktop.size().width(), self.session.desktop.size().height(), \
+                                                                                               self.fontSize, self.fontSize, self.fontSize, self.fontSize, \
+                                                                                               self.text1Pos[0], self.text1Pos[1], self.text1Pos[2], self.text1Pos[3], self.fontSize, \
+                                                                                               self.text2Pos[0], self.text2Pos[1], self.text2Pos[2], self.text2Pos[3], self.fontSize)
+               self.oldService = None
+               self.curChannel = None
+               self.curPlayAudio = -1
+
+               global quad_pip_channel_list_instance
+               self.qpipChannelList = quad_pip_channel_list_instance
+
+               self.oldFccEnable = False
+               self.oldMinitvEanble = False
+
+               self.onLayoutFinish.append(self.layoutFinishedCB)
+
+               self.notSupportTimer = eTimer()
+               self.notSupportTimer.callback.append(self.showNotSupport)
+
+               self.noChannelTimer = eTimer()
+               self.noChannelTimer.callback.append(self.noChannelTimerCB)
+
+               self.forceToExitTimer = eTimer()
+               self.forceToExitTimer.callback.append(self.forceToExitTimerCB)
+
+       def forceToExitTimerCB(self):
+               self.session.openWithCallback(self.close, MessageBox, _("Quad PiP is not available."), MessageBox.TYPE_ERROR)
+
+       def showNotSupport(self):
+               self.session.openWithCallback(self.close, MessageBox, _("Box or driver is not support Quad PiP."), MessageBox.TYPE_ERROR)
+
+       def noChannelTimerCB(self):
+               self.session.openWithCallback(self.ChannelSelectCB, QuadPiPChannelSelection)
+
+       def layoutFinishedCB(self):
+               if not os.access(ENABLE_QPIP_PROCPATH, os.F_OK):
+                       self.notSupportTimer.start(100, True)
+                       return
+
+               self.onClose.append(self.__onClose)
+
+               if self.session.pipshown: # try to disable pip
+                       self.session.pipshown = False
+                       del self.session.pip
+
+               self.oldService = self.session.nav.getCurrentlyPlayingServiceReference()
+               self.session.nav.stopService()
+
+               if SystemInfo.get("FastChannelChange", False):
+                       self.disableFCC()
+
+               if SystemInfo.get("MiniTV", False):
+                       self.disableMiniTV()
+
+               ret = setDecoderMode("mosaic")
+               if ret is not True:
+                       self.forceToExitTimer.start(0, True)
+                       return
+
+               self.moveLabel()
+
+               if self.qpipChannelList.length() == 0:
+                       self.noChannelTimer.start(10, True)
+               else:
+                       self.playLastChannel()
+
+       def __onClose(self):
+               self.disableQuadPip()
+               setDecoderMode("normal")
+
+               if SystemInfo.get("FastChannelChange", False):
+                       self.enableFCC()
+
+               if SystemInfo.get("MiniTV", False):
+                       self.enableMiniTV()
+
+               self.qpipChannelList.saveAll()
+               self.session.nav.playService(self.oldService)
+
+       def getChannelPosMap(self, w, h):
+               rectMap = {}
+               ch1 = (0, 0, int(w*0.5), int(h*0.5))
+               ch2 = (int(w*0.5), 0, int(w*0.5), int(h*0.5))
+               ch3 = (0, int(h*0.5), int(w*0.5), int(h*0.5))
+               ch4 = (int(w*0.5), int(h*0.5), int(w*0.5), int(h*0.5))
+               rectMap = (None, ch1, ch2, ch3, ch4)
+
+               return rectMap
+
+       def updatePositionList(self):
+               w = self.session.desktop.size().width()
+               h = self.session.desktop.size().height()
+               self.framePosMap = self.getChannelPosMap(w, h)
+               self.eVideoPosMap = self.getChannelPosMap(720, 576)
+
+               self.movePositionMap = {}
+               self.movePositionMap["left"] = [-1, 2, 1, 4, 3]
+               self.movePositionMap["right"] = [-1, 2, 1, 4, 3]
+               self.movePositionMap["up"] = [-1, 3, 4, 1, 2]
+               self.movePositionMap["down"] = [-1, 3, 4, 1, 2]
+
+               self.labelPositionMap = {}
+               self.labelPositionMap["ch1"] = (w/8,            h/4-h/36,               w/4,    h/18)
+               self.labelPositionMap["ch2"] = (w/8+w/2,        h/4-h/36,               w/4,    h/18)
+               self.labelPositionMap["ch3"] = (w/8,            h/4-h/36+h/2,   w/4,    h/18)
+               self.labelPositionMap["ch4"] = (w/8+w/2,        h/4-h/36+h/2,   w/4,    h/18)
+
+               self.decoderIdxMap = [None, 0, 1, 2, 3]
+
+               self.fontSize = {1080:40, 720:28, 576:18}.get(h, 40)
+               self.text1Pos = (w-w/3, h-h/18-h/18, w/3, h/18)
+               self.text2Pos = (w-w/3, h-h/18, w/3, h/18)
+
+       def moveFrame(self):
+               self.showFocus()
+               pos = self.framePosMap[self.currentPosition]
+               self["focus"].resize(int(pos[2]), int(pos[3]))
+               self["focus"].move(int(pos[0]), int(pos[1]))
+
+       def moveLabel(self):
+               posMap = self.labelPositionMap
+
+               ch1_posMap = posMap["ch1"]
+               self["ch1"].move(int(ch1_posMap[0]), int(ch1_posMap[1]))
+               self["ch1"].resize(int(ch1_posMap[2]), int(ch1_posMap[3]))
+
+               ch2_posMap = posMap["ch2"]
+               self["ch2"].move(int(ch2_posMap[0]), int(ch2_posMap[1]))
+               self["ch2"].resize(int(ch2_posMap[2]), int(ch2_posMap[3]))
+
+               ch3_posMap = posMap["ch3"]
+               self["ch3"].move(int(ch3_posMap[0]), int(ch3_posMap[1]))
+               self["ch3"].resize(int(ch3_posMap[2]), int(ch3_posMap[3]))
+
+               ch4_posMap = posMap["ch4"]
+               self["ch4"].move(int(ch4_posMap[0]), int(ch4_posMap[1]))
+               self["ch4"].resize(int(ch4_posMap[2]), int(ch4_posMap[3]))
+
+       def keyExit(self):
+               self.close()
+
+       def keyOk(self):
+               if self.isShown():
+                       channel = self.qpipChannelList.getCurrentChannel()
+                       if channel:
+                               chInfo = channel.getChannel(str(self.currentPosition))
+                               if chInfo:
+                                       (sname, sref) = chInfo
+                                       self.oldService = eServiceReference(sref)
+                                       self.close()
+               else:
+                       self.showFocus()
+
+       def keyLeft(self):
+               newPosition = self.movePositionMap["left"][self.currentPosition]
+               self.selectPosition(newPosition)
+
+       def keyRight(self):
+               newPosition = self.movePositionMap["right"][self.currentPosition]
+               self.selectPosition(newPosition)
+
+       def keyUp(self):
+               newPosition = self.movePositionMap["up"][self.currentPosition]
+               self.selectPosition(newPosition)
+
+       def keyDown(self):
+               newPosition = self.movePositionMap["down"][self.currentPosition]
+               self.selectPosition(newPosition)
+
+       def KeyChannel(self):
+               self.session.openWithCallback(self.ChannelSelectCB, QuadPiPChannelSelection)
+
+       def KeyPrev(self):
+               curIdx = self.qpipChannelList.getIdx()
+               curIdx -= 1
+               if curIdx == 0:
+                       curIdx = self.qpipChannelList.length()
+               self.qpipChannelList.setIdx(curIdx)
+               self.playLastChannel()
+
+       def KeyNext(self):
+               curIdx = self.qpipChannelList.getIdx()
+               curIdx += 1
+               if curIdx > self.qpipChannelList.length():
+                       curIdx = 1
+               self.qpipChannelList.setIdx(curIdx)
+               self.playLastChannel()
+
+       def KeyRed(self):
+               self.toggleShow()
+
+       def selectPosition(self, pos):
+               self.currentPosition = pos
+               self.moveFrame()
+               self.selectAudio()
+
+       def selectAudio(self):
+               if self.curPlayAudio == -1:
+                       return
+
+               if self.curPlayAudio != self.currentPosition:
+                       if self.session.qPips and len(self.session.qPips) >= self.currentPosition:
+                               self.playAudio(self.curPlayAudio, False)
+                               self.playAudio(self.currentPosition, True)
+
+       def disableQuadPip(self):
+               if self.session.qPips is not None:
+                       for qPip in self.session.qPips:
+                               del qPip
+
+                       self.session.qPips = None
+                       self.curPlayAudio = -1
+
+               self.updateChannelName(None)
+
+       def ChannelSelectCB(self):
+               if self.qpipChannelList.length() == 0:
+                       self.disableQuadPip()
+               else:
+                       self.playLastChannel()
+
+       def playLastChannel(self, first=True):
+               if self.qpipChannelList.length() == 0:
+                       return
+
+               channel = self.qpipChannelList.getCurrentChannel()
+               if channel:
+                       self.playChannel(channel)
+               elif first:
+                       self.qpipChannelList.setIdx(1)
+                       self.playLastChannel(False)
+
+               return channel
+
+       def playChannel(self, channel):
+               print "[playChannel] channel : ", channel
+
+               if self.curChannel and self.curChannel == channel.channel:
+                       return
+
+               self.disableQuadPip()
+               self.selectPosition(1)
+
+               self.curChannel = channel.channel.copy()
+
+               self.session.qPips = []
+               for idx in range(1,5):
+                       chInfo = channel.getChannel(str(idx))
+                       if chInfo is None:
+                               continue
+
+                       (sname, sref) = chInfo
+
+                       qPipShown = False
+
+                       decoderIdx = self.decoderIdxMap[idx]
+                       pos = self.eVideoPosMap[idx]
+                       #print "===================================================================="
+                       #print "sname : ", sname
+                       #print "sref : ", sref
+                       #print "decoderIdx : " , decoderIdx
+                       #print "pos : ", pos
+                       #print "===================================================================="
+
+                       qPipInstance =  self.session.instantiateDialog(QuadPiP, decoderIdx, pos)
+                       qPipInstance.setAnimationMode(0)
+                       qPipInstance.show()
+
+                       isPlayAudio = False
+                       if self.currentPosition == idx:
+                               isPlayAudio = True
+                               self.curPlayAudio = idx
+
+                       if qPipInstance.playService(eServiceReference(sref), isPlayAudio):
+                               self.session.qPips.append(qPipInstance)
+                       else:
+                               print "play failed, ", sref
+                               del qPipInstance
+
+               self.updateChannelName(channel)
+               self.showFocus()
+
+       def playAudio(self, idx, value):
+               qPipInstance = self.session.qPips[idx-1]
+               qPipInstance.setQpipMode(True, value)
+
+               if value:
+                       self.curPlayAudio = idx
+               else:
+                       self.curPlayAudio = -1
+
+       def updateChannelName(self, channel):
+               for idx in range(1,5):
+                       self["ch%d" % idx].setText((channel and channel.getChannelName(str(idx))) or "No channel")
+
+       def disableFCC(self):
+               try:
+                       self.oldFccEnable = config.plugins.fccsetup.activate.value
+                       if self.oldFccEnable:
+                               config.plugins.fccsetup.activate.value = False
+                               from Plugins.SystemPlugins.FastChannelChange.plugin import FCCChanged
+                               FCCChanged()
+               except:
+                       self.oldFccEnable = False
+
+       def enableFCC(self):
+               if self.oldFccEnable:
+                       try:
+                               config.plugins.fccsetup.activate.value = self.oldFccEnable
+                               from Plugins.SystemPlugins.FastChannelChange.plugin import FCCChanged
+                               FCCChanged()
+                       except:
+                               pass
+
+       def disableMiniTV(self):
+               try:
+                       self.oldMinitvEanble = config.plugins.minitv.enable.value
+                       if self.oldMinitvEanble:
+                               config.plugins.minitv.enable.value = False
+               except:
+                       self.oldFccEnable = False
+
+       def enableMiniTV(self):
+               if self.oldMinitvEanble:
+                       try:
+                               config.plugins.minitv.enable.value = self.oldMinitvEanble
+                       except:
+                               pass
+
index 4c4fa44..35e8510 100755 (executable)
@@ -15,5 +15,5 @@ install_PYTHON = \
        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 \
        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 \
-       InputDeviceSetup.py
+       InputDeviceSetup.py QuadPiP.py
 
 
diff --git a/lib/python/Screens/QuadPiP.py b/lib/python/Screens/QuadPiP.py
new file mode 100644 (file)
index 0000000..57dac06
--- /dev/null
@@ -0,0 +1,63 @@
+from Screens.Screen import Screen
+from enigma import ePoint, eSize, eServiceCenter, getBestPlayableServiceReference, eServiceReference
+from Components.VideoWindow import VideoWindow
+from Components.config import config, ConfigPosition
+
+class QuadPiP(Screen):
+       def __init__(self, session, decoderIdx = 1, pos = None):
+               Screen.__init__(self, session)
+               self["video"] = VideoWindow(decoderIdx, 720, 576)
+               self.currentService = None
+               self.onLayoutFinish.append(self.LayoutFinished)
+               self.decoderIdx = decoderIdx
+               self.pos = pos
+               self.skinName = "PictureInPicture"
+
+       def LayoutFinished(self):
+               self["video"].instance.setAdjustPosition(False)
+               self.onLayoutFinish.remove(self.LayoutFinished)
+               x = self.pos[0]
+               y = self.pos[1]
+               w = self.pos[2]
+               h = self.pos[3]
+
+               if x != -1 and y != -1 and w != -1 and h != -1:
+                       self.move(x, y)
+                       self.resize(w, h)
+
+       def move(self, x, y):
+               self.instance.move(ePoint(x, y))
+
+       def resize(self, w, h):
+               self.instance.resize(eSize(*(w, h)))
+               self["video"].instance.resize(eSize(*(w, h)))
+
+       def getPosition(self):
+               return ((self.instance.position().x(), self.instance.position().y()))
+
+       def getSize(self):
+               return (self.instance.size().width(), self.instance.size().height())
+
+       def playService(self, service, playAudio):
+               if service and (service.flags & eServiceReference.isGroup):
+                       ref = getBestPlayableServiceReference(service, eServiceReference())
+               else:
+                       ref = service
+               if ref:
+                       self.pipservice = eServiceCenter.getInstance().play(ref)
+                       if self.pipservice and not self.pipservice.setTarget(self.decoderIdx):
+                               self.setQpipMode(True, playAudio)
+                               self.pipservice.start()
+                               self.currentService = service
+                               return True
+                       else:
+                               self.pipservice = None
+               return False
+
+       def setQpipMode(self, pipMode, playAudio):
+               if self.pipservice:
+                       self.pipservice.setQpipMode(pipMode, playAudio)
+
+       def getCurrentService(self):
+               return self.currentService
+
index 33ff66b..bc0f872 100644 (file)
@@ -879,6 +879,7 @@ public:
        virtual SWIG_VOID(RESULT) stream(ePtr<iStreamableService> &SWIG_OUTPUT)=0;
        virtual SWIG_VOID(RESULT) streamed(ePtr<iStreamedService> &SWIG_OUTPUT)=0;
        virtual SWIG_VOID(RESULT) keys(ePtr<iServiceKeys> &SWIG_OUTPUT)=0;
        virtual SWIG_VOID(RESULT) stream(ePtr<iStreamableService> &SWIG_OUTPUT)=0;
        virtual SWIG_VOID(RESULT) streamed(ePtr<iStreamedService> &SWIG_OUTPUT)=0;
        virtual SWIG_VOID(RESULT) keys(ePtr<iServiceKeys> &SWIG_OUTPUT)=0;
+       virtual void setQpipMode(bool value, bool audio)=0;
 };
 SWIG_TEMPLATE_TYPEDEF(ePtr<iPlayableService>, iPlayableServicePtr);
 
 };
 SWIG_TEMPLATE_TYPEDEF(ePtr<iPlayableService>, iPlayableServicePtr);
 
index 1494cc5..f740b47 100755 (executable)
@@ -959,6 +959,10 @@ eDVBServicePlay::eDVBServicePlay(const eServiceReference &ref, eDVBService *serv
 
        m_current_video_pid_type = 0;
 
 
        m_current_video_pid_type = 0;
 
+       m_qpip_mode = false;
+
+       m_play_audio = true;
+
        CONNECT(m_subtitle_sync_timer->timeout, eDVBServicePlay::checkSubtitleTiming);
 }
 
        CONNECT(m_subtitle_sync_timer->timeout, eDVBServicePlay::checkSubtitleTiming);
 }
 
@@ -1963,7 +1967,7 @@ int eDVBServicePlay::selectAudioStream(int i)
 
        m_current_audio_pid = apid;
 
 
        m_current_audio_pid = apid;
 
-       if (m_is_primary && m_decoder->setAudioPID(apid, apidtype))
+       if ((m_is_primary || (m_qpip_mode && m_play_audio)) && m_decoder->setAudioPID(apid, apidtype))
        {
                eDebug("set audio pid failed");
                return -4;
        {
                eDebug("set audio pid failed");
                return -4;
@@ -2665,7 +2669,9 @@ void eDVBServicePlay::updateDecoder(bool sendSeekableStateChanged)
 
                m_decoder->setVideoPID(vpid, vpidtype);
                m_current_video_pid_type = vpidtype;
 
                m_decoder->setVideoPID(vpid, vpidtype);
                m_current_video_pid_type = vpidtype;
-               selectAudioStream();
+
+               if (!m_qpip_mode || m_play_audio) // 1) no qpip mode, 2) qpip mode & play audio
+                       selectAudioStream();
 
                //if (!(m_is_pvr || m_is_stream || m_timeshift_active || !m_is_primary))
                if (!(m_is_pvr || m_is_stream || m_timeshift_active))
 
                //if (!(m_is_pvr || m_is_stream || m_timeshift_active || !m_is_primary))
                if (!(m_is_pvr || m_is_stream || m_timeshift_active))
@@ -3305,6 +3311,24 @@ PyObject *eDVBServicePlay::getStreamingData()
        return r;
 }
 
        return r;
 }
 
+void eDVBServicePlay::setQpipMode(bool value, bool audio)
+{
+       m_qpip_mode = value;
+       m_play_audio = audio;
+
+       if(m_decoder)
+       {
+               if (m_play_audio)
+               {
+                       selectAudioStream();
+               }
+               else
+               {
+                       m_decoder->setAudioPID(-1, -1);
+               }
+               m_decoder->set();
+       }
+}
 
 DEFINE_REF(eDVBServicePlay)
 
 
 DEFINE_REF(eDVBServicePlay)
 
index eafc96d..6d2eaf9 100644 (file)
@@ -184,6 +184,7 @@ public:
                // iStreamableService
        RESULT stream(ePtr<iStreamableService> &ptr);
        PyObject *getStreamingData();
                // iStreamableService
        RESULT stream(ePtr<iStreamableService> &ptr);
        PyObject *getStreamingData();
+       void setQpipMode(bool value, bool audio);
 
 protected:
        friend class eServiceFactoryDVB;
 
 protected:
        friend class eServiceFactoryDVB;
@@ -202,6 +203,9 @@ protected:
        eDVBServiceEITHandler m_event_handler;
        int m_current_audio_pid;
        int m_current_video_pid_type;
        eDVBServiceEITHandler m_event_handler;
        int m_current_audio_pid;
        int m_current_video_pid_type;
+
+       bool m_qpip_mode;
+       bool m_play_audio;
        
        eDVBServicePlay(const eServiceReference &ref, eDVBService *service, bool connect_event=true);
        
        
        eDVBServicePlay(const eServiceReference &ref, eDVBService *service, bool connect_event=true);
        
index 1f8e664..0248f3d 100644 (file)
@@ -75,6 +75,8 @@ public:
        std::string getInfoString(int w);
        PyObject *getInfoObject(int w);
 
        std::string getInfoString(int w);
        PyObject *getInfoObject(int w);
 
+       void setQpipMode(bool value, bool audio) { }
+
 private:
        friend class eServiceFactoryHDMI;
        eServiceHDMI(eServiceReference ref);
 private:
        friend class eServiceFactoryHDMI;
        eServiceHDMI(eServiceReference ref);
index 50957c4..8ada1b5 100644 (file)
@@ -83,6 +83,8 @@ public:
        RESULT keys(ePtr<iServiceKeys> &ptr) { ptr = 0; return -1; }
        RESULT stream(ePtr<iStreamableService> &ptr) { ptr = 0; return -1; }
 
        RESULT keys(ePtr<iServiceKeys> &ptr) { ptr = 0; return -1; }
        RESULT stream(ePtr<iStreamableService> &ptr) { ptr = 0; return -1; }
 
+       void setQpipMode(bool value, bool audio) { }
+
                // iPausableService
        RESULT pause();
        RESULT unpause();
                // iPausableService
        RESULT pause();
        RESULT unpause();