From 107fcd22bcfec4d629a8c321aef8b888ac2e78a2 Mon Sep 17 00:00:00 2001 From: Mark Kendall Date: Fri, 4 Feb 2011 08:51:46 +0800 Subject: [PATCH] UPnP: Extend the MythFEXML interface to send actions directly to the frontend. In summary:- Send a message to the frontend (N.B. syntax change). - http://mythfrontend-ip:6547/MythFE/SendMessage?text='Hello world' Send an action (e.g. UP) to the frontend. - http://mythfrontend-ip:6547/MythFE/SendAction?action=UP A list of valid actions is cached internally and used to validate the submitted action. To retrieve a simple XML formatted list of those actions, their descriptions and the contexts in which they are valid. - http://mythfrontend-ip:6547/MythFE/GetActionList To display an html test page of all valid actions. - http://mythfrontend-ip:6547/MythFE/GetActionTest GetServeDesc returns the service description for the first time (the description needs updating). N.B. There is every possibility that this interface will be tweaked before 0.25 is released. There are some issues around languages/translations with the action descriptions and I make no apologies for the quality of my html/xml (patches willingly accepted) --- mythtv/programs/mythfrontend/mythfexml.cpp | 203 +++++++++++++++++---- mythtv/programs/mythfrontend/mythfexml.h | 18 +- 2 files changed, 186 insertions(+), 35 deletions(-) diff --git a/mythtv/programs/mythfrontend/mythfexml.cpp b/mythtv/programs/mythfrontend/mythfexml.cpp index 5fdbf29a4ff..5953db94cad 100644 --- a/mythtv/programs/mythfrontend/mythfexml.cpp +++ b/mythtv/programs/mythfrontend/mythfexml.cpp @@ -19,9 +19,12 @@ #include #include #include +#include #include "../../config.h" +#include "keybindings.h" + ///////////////////////////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////////////////////////// @@ -52,10 +55,14 @@ MythFEXML::~MythFEXML() // ///////////////////////////////////////////////////////////////////////////// -MythFEXMLMethod MythFEXML::GetMethod( const QString &sURI ) +MythFEXMLMethod MythFEXML::GetMethod(const QString &sURI) { + if (sURI == "GetServDesc") return MFEXML_GetServiceDescription; if (sURI == "GetScreenShot") return MFEXML_GetScreenShot; - if (sURI == "Message") return MFEXML_Message; + if (sURI == "SendMessage") return MFEXML_Message; + if (sURI == "SendAction") return MFEXML_Action; + if (sURI == "GetActionList") return MFEXML_ActionList; + if (sURI == "GetActionTest") return MFEXML_ActionListTest; return( MFEXML_Unknown ); } @@ -66,38 +73,39 @@ MythFEXMLMethod MythFEXML::GetMethod( const QString &sURI ) bool MythFEXML::ProcessRequest( HttpWorkerThread *pThread, HTTPRequest *pRequest ) { - try - { - if (pRequest) - { - if (pRequest->m_sBaseUrl != m_sControlUrl) - return( false ); + if (!pRequest) + return false; - VERBOSE(VB_UPNP, QString("MythFEXML::ProcessRequest: %1 : %2") - .arg(pRequest->m_sMethod) - .arg(pRequest->m_sRawRequest)); + if (pRequest->m_sBaseUrl != m_sControlUrl) + return false; - switch( GetMethod( pRequest->m_sMethod )) - { - case MFEXML_GetScreenShot : GetScreenShot ( pRequest ); return true; - case MFEXML_Message : SendMessage ( pRequest ); return true; + VERBOSE(VB_UPNP, QString("MythFEXML::ProcessRequest: %1 : %2") + .arg(pRequest->m_sMethod).arg(pRequest->m_sRawRequest)); - - default: - { - UPnp::FormatErrorResponse( pRequest, UPnPResult_InvalidAction ); - - return true; - } - } - } - } - catch( ... ) + switch(GetMethod(pRequest->m_sMethod)) { - VERBOSE( VB_IMPORTANT, "MythFEXML::ProcessRequest() - Unexpected Exception" ); + case MFEXML_GetServiceDescription: + pRequest->FormatFileResponse(m_sServiceDescFileName); + break; + case MFEXML_GetScreenShot: + GetScreenShot(pRequest); + break; + case MFEXML_Message: + SendMessage(pRequest); + break; + case MFEXML_Action: + SendAction(pRequest); + break; + case MFEXML_ActionList: + GetActionList(pRequest); + break; + case MFEXML_ActionListTest: + GetActionListTest(pRequest); + break; + default: + UPnp::FormatErrorResponse(pRequest, UPnPResult_InvalidAction); } - - return( false ); + return true; } // ========================================================================== @@ -108,7 +116,7 @@ bool MythFEXML::ProcessRequest( HttpWorkerThread *pThread, HTTPRequest *pRequest // ///////////////////////////////////////////////////////////////////////////// -void MythFEXML::GetScreenShot( HTTPRequest *pRequest ) +void MythFEXML::GetScreenShot(HTTPRequest *pRequest) { pRequest->m_eResponseType = ResponseTypeFile; @@ -141,7 +149,7 @@ void MythFEXML::GetScreenShot( HTTPRequest *pRequest ) pRequest->m_sFileName = sFileName; } -void MythFEXML::SendMessage( HTTPRequest *pRequest ) +void MythFEXML::SendMessage(HTTPRequest *pRequest) { pRequest->m_eResponseType = ResponseTypeNone; QString sText = pRequest->m_mapParams[ "text" ]; @@ -151,3 +159,136 @@ void MythFEXML::SendMessage( HTTPRequest *pRequest ) MythEvent* me = new MythEvent(MythEvent::MythUserMessage, sText); qApp->postEvent(window, me); } + +void MythFEXML::SendAction(HTTPRequest *pRequest) +{ + pRequest->m_eResponseType = ResponseTypeNone; + QString sText = pRequest->m_mapParams["action"]; + VERBOSE(VB_UPNP, QString("UPNP Action: ") + sText); + + InitActions(); + if (!m_actionList.contains(sText)) + { + VERBOSE(VB_GENERAL, QString("UPNP Action: %1 is invalid").arg(sText)); + return; + } + + MythMainWindow *window = GetMythMainWindow(); + QKeyEvent* ke = new QKeyEvent(QEvent::KeyPress, 0, Qt::NoModifier, sText); + qApp->postEvent(window, (QEvent*)ke); +} + +void MythFEXML::GetActionListTest(HTTPRequest *pRequest) +{ + InitActions(); + + pRequest->m_eResponseType = ResponseTypeHTML; + pRequest->m_mapRespHeaders[ "Cache-Control" ] = "no-cache=\"Ext\", max-age = 5000"; + + pRequest->m_response << + "\n" + " \n" + " \n" + " \n"; + + QHashIterator contexts(m_actionDescriptions); + while (contexts.hasNext()) + { + contexts.next(); + QStringList actions = contexts.value(); + foreach (QString action, actions) + { + QStringList split = action.split(","); + if (split.size() == 2) + { + pRequest->m_response << + QString("
%1  %3
\n") + .arg(contexts.key()).arg(split[0]).arg(split[1]); + } + } + } + + pRequest->m_response << + " \n" + "\n"; + +} + +void MythFEXML::GetActionList(HTTPRequest *pRequest) +{ + InitActions(); + + pRequest->m_eResponseType = ResponseTypeXML; + pRequest->m_mapRespHeaders[ "Cache-Control" ] = "no-cache=\"Ext\", max-age = 5000"; + + pRequest->m_response << ""; + + QHashIterator contexts(m_actionDescriptions); + while (contexts.hasNext()) + { + contexts.next(); + pRequest->m_response << QString("") + .arg(contexts.key()); + QStringList actions = contexts.value(); + foreach (QString action, actions) + { + QStringList split = action.split(","); + if (split.size() == 2) + { + pRequest->m_response << + QString("%2") + .arg(split[0]).arg(split[1]); + } + } + pRequest->m_response << ""; + } + pRequest->m_response << ""; +} + +void MythFEXML::InitActions(void) +{ + static bool initialised = false; + if (initialised) + return; + + initialised = true; + KeyBindings *bindings = new KeyBindings(gCoreContext->GetHostName()); + if (bindings) + { + QStringList contexts = bindings->GetContexts(); + contexts.sort(); + foreach (QString context, contexts) + { + m_actionDescriptions[context] = QStringList(); + QStringList ctx_actions = bindings->GetActions(context); + ctx_actions.sort(); + m_actionList += ctx_actions; + foreach (QString actions, ctx_actions) + { + QString desc = actions + "," + + bindings->GetActionDescription(context, actions); + m_actionDescriptions[context].append(desc); + } + } + } + m_actionList.removeDuplicates(); + m_actionList.sort(); + + foreach (QString actions, m_actionList) + VERBOSE(VB_UPNP, QString("MythFEXML Action: %1").arg(actions)); +} diff --git a/mythtv/programs/mythfrontend/mythfexml.h b/mythtv/programs/mythfrontend/mythfexml.h index de2782030c7..8c28a5075ab 100644 --- a/mythtv/programs/mythfrontend/mythfexml.h +++ b/mythtv/programs/mythfrontend/mythfexml.h @@ -16,10 +16,13 @@ typedef enum { - MFEXML_Unknown = 0, - MFEXML_GetScreenShot = 1, - MFEXML_Message = 2, - + MFEXML_Unknown = 0, + MFEXML_GetServiceDescription, + MFEXML_GetScreenShot, + MFEXML_Message, + MFEXML_Action, + MFEXML_ActionList, + MFEXML_ActionListTest, } MythFEXMLMethod; class MythFEXML : public Eventing @@ -29,6 +32,9 @@ class MythFEXML : public Eventing QString m_sControlUrl; QString m_sServiceDescFileName; + QStringList m_actionList; + QHash m_actionDescriptions; + protected: // Implement UPnpServiceImpl methods that we can @@ -44,6 +50,10 @@ class MythFEXML : public Eventing void GetScreenShot ( HTTPRequest *pRequest ); void SendMessage ( HTTPRequest *pRequest ); + void SendAction ( HTTPRequest *pRequest ); + void GetActionList ( HTTPRequest *pRequest ); + void GetActionListTest( HTTPRequest *pRequest ); + void InitActions ( void ); public: MythFEXML( UPnpDevice *pDevice , const QString sSharePath);