Skip to content

Commit 07948b4

Browse files
committed
This commit creates an event-driven notification system to trigger user-specified commands when certain events occur in MythTV. A new MythSystemEventHandler class is instantiated when mythbackend, mythfrontend, and mythjobqueue are started. This allows any frontend, backend, or JobQueue server to process system event messages and run the corresponding user-specified command. The current list of events which are handled are: Recording pending Recording started Recording finished Recording deleted Recording expired Playback started Playback stopped Playback paused Playback unpaused Playback program changed Master backend started Master backend shutdown Client connected to master backend Client disconnected from master backend Slave backend connected to master Slave backend disconnected from master Network Control client connected Network Control client disconnected mythfilldatabase ran Scheduler ran Settings cache cleared Along with the above predefined system events, the user can also configure up to 10 user-generated keystroke events and associate them with a keypress as normal keys are configured. MythTV System Event handler commands are configured on a per server basis. A given System Event may have a command to be run on one backend but not on another, or only on a particular frontend. System Event handler commands may be configured via either mythtv-setup or mythfrontend's setup area. When a System Event fires, a handler command is run on any system that has the handler command configured for that System Event. This applies even for the user-generated keystroke events. It is up to the user's script to determine whether to act on the event or not. For instance, when the "Playback started" event is fired, any frontend that has a handler command configured for this event will run the handler command, it is up to the handler script to determine whether to act on the event depending on whether it is concerned that playback started on the particular given host. This functionality allows the user to control where to run events and also allows the user to generate events on one or more frontends via keystrokes and have those keystrokes trigger a command or script to run on a particular backend. This could be useful for maniupulating hardware such as a multi-DVD changer connected to a remote backend. System Event commands can be used for numerous things, a couple examples include the above-mentioned remote DVD changer, turning room lights on/off when playback stops/starts, and emailing the latest schedule whenever the scheduler completes a run. System Events may be sent via the command line using a new command line option called --systemevent to mythbackend. System Event handler commands are stored in the normal MythTV settings table. This means that the user can create and fire their own System Event commands via a combination of the -O settings override option and the --systemevent option. To setup a handler for a user-created event, run mythbackend similar to this: mythbackend -O EventCmdUser1=/usr/local/bin/myth_event_user_01.sh to send the User1 System Event on the same host or a remote host: mythbackend --systemevent USER_1 Using this functionality, users can for instance send their own event when a User Job runs or completes and have the event trigger a command to be run on a remote backend or frontend. System Event handler commands can utilize the same %MATCH% type variable substitution as MythJobQueue handler commands. Additional variable substitutions are: CARDID - cardid a recording is being made on SECS - seconds until a recording starts SENDER - hostname of system sending the event All System Event handler commands are run in the background in the global QThreadPool. The event handler thread releases the thread so that the thread does not count against the max thread count in the pool. This is done so that long-running handler commands do not use all threads and prevent other shorter-running handler commands from executing. System Event handler commands are run only once for all processes running on a given host. If a host has a handler command configured for a specific event and both mythfrontend and mythbackend are running on that host, the handler command will be run only once. Which process actually runs the handler command is unspecified in the case of a slave backend and a frontend process. The first process connection to be found by the master backend will handle the command. In the case of a master backend also running a frontend process, the master backend will always handle the event and run the command, the frontend process will never see the event. git-svn-id: http://svn.mythtv.org/svn/trunk@23012 7dbf422c-18fa-0310-86e9-fd20926502f2
1 parent 83ddac5 commit 07948b4

33 files changed

+1364
-37
lines changed

mythtv/libs/libmyth/libmyth.pro

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ HEADERS += mythhttppool.h mythhttphandler.h
3030
HEADERS += audiopulseutil.h
3131
HEADERS += programinfo.h programlist.h programinfoupdater.h
3232
HEADERS += recordingtypes.h remoteutil.h
33+
HEADERS += rawsettingseditor.h
3334

3435
# remove when everything is switched to mythui
3536
HEADERS += virtualkeyboard_qt.h
@@ -53,6 +54,7 @@ SOURCES += mythhttppool.cpp mythhttphandler.cpp
5354
SOURCES += audiopulseutil.cpp
5455
SOURCES += programinfo.cpp programlist.cpp programinfoupdater.cpp
5556
SOURCES += recordingtypes.cpp remoteutil.cpp
57+
SOURCES += rawsettingseditor.cpp
5658

5759
# remove when everything is switched to mythui
5860
SOURCES += virtualkeyboard_qt.cpp

mythtv/libs/libmyth/programinfo.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3798,6 +3798,42 @@ QString ProgramInfo::i18n(const QString &msg)
37983798
return (msg_arr == msg_i18n_arr) ? msg : msg_i18n;
37993799
}
38003800

3801+
/** \fn ProgramInfo::SubstituteMatches(QString &str)
3802+
* \brief Subsitute %MATCH% type variable names in the given string
3803+
* \param str QString to substitute matches in
3804+
*/
3805+
void ProgramInfo::SubstituteMatches(QString &str)
3806+
{
3807+
QString pburl = GetPlaybackURL(false, true);
3808+
if (pburl.left(7) == "myth://")
3809+
{
3810+
str.replace(QString("%DIR%"), pburl);
3811+
}
3812+
else
3813+
{
3814+
QFileInfo dirInfo(pburl);
3815+
str.replace(QString("%DIR%"), dirInfo.path());
3816+
}
3817+
3818+
str.replace(QString("%FILE%"), GetRecordBasename(true));
3819+
str.replace(QString("%TITLE%"), title);
3820+
str.replace(QString("%SUBTITLE%"), subtitle);
3821+
str.replace(QString("%DESCRIPTION%"), description);
3822+
str.replace(QString("%HOSTNAME%"), hostname);
3823+
str.replace(QString("%CATEGORY%"), category);
3824+
str.replace(QString("%RECGROUP%"), recgroup);
3825+
str.replace(QString("%PLAYGROUP%"), playgroup);
3826+
str.replace(QString("%CHANID%"), chanid);
3827+
str.replace(QString("%STARTTIME%"), recstartts.toString("yyyyMMddhhmmss"));
3828+
str.replace(QString("%ENDTIME%"), recendts.toString("yyyyMMddhhmmss"));
3829+
str.replace(QString("%STARTTIMEISO%"), recstartts.toString(Qt::ISODate));
3830+
str.replace(QString("%ENDTIMEISO%"), recendts.toString(Qt::ISODate));
3831+
str.replace(QString("%PROGSTART%"), startts.toString("yyyyMMddhhmmss"));
3832+
str.replace(QString("%PROGEND%"), endts.toString("yyyyMMddhhmmss"));
3833+
str.replace(QString("%PROGSTARTISO%"), startts.toString(Qt::ISODate));
3834+
str.replace(QString("%PROGENDISO%"), endts.toString(Qt::ISODate));
3835+
}
3836+
38013837
QString SkipTypeToString(int flags)
38023838
{
38033839
if (COMM_DETECT_COMMFREE == flags)

mythtv/libs/libmyth/programinfo.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,7 @@ class MPUBLIC ProgramInfo
277277
void ToStringList(QStringList &list) const;
278278
virtual void ToMap(QHash<QString, QString> &progMap,
279279
bool showrerecord = false) const;
280+
virtual void SubstituteMatches(QString &str);
280281

281282
// Used for scheduling recordings
282283
//int IsProgramRecurring(void) const; //not used...
Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
2+
#include "mythcontext.h"
3+
#include "mythverbose.h"
4+
#include "mythuibutton.h"
5+
#include "mythuibuttonlist.h"
6+
#include "mythuitext.h"
7+
#include "mythuitextedit.h"
8+
#include "rawsettingseditor.h"
9+
#include "remoteutil.h"
10+
11+
/** \fn RawSettingsEditor::RawSettingsEditor(MythScreenStack *parent,
12+
const char name)
13+
* \brief Raw Settings Editor constructor
14+
*
15+
* Initializes necessary variables.
16+
*
17+
* \param parent Parent screen stack for this window
18+
* \param name Name of this window
19+
*/
20+
RawSettingsEditor::RawSettingsEditor(MythScreenStack *parent, const char *name)
21+
: MythScreenType(parent, name),
22+
m_title(tr("Settings Editor")),
23+
// Settings widgets
24+
m_settingsList(NULL), m_settingValue(NULL),
25+
// Action buttons
26+
m_saveButton(NULL), m_cancelButton(NULL),
27+
// Labels
28+
m_textLabel(NULL)
29+
{
30+
}
31+
32+
/** \fn RawSettingsEditor::~RawSettingsEditor()
33+
* \brief Raw Settings Editor destructor
34+
*/
35+
RawSettingsEditor::~RawSettingsEditor()
36+
{
37+
}
38+
39+
/** \fn RawSettingsEditor::Create(void)
40+
* \brief Creates the UI screen.
41+
*/
42+
bool RawSettingsEditor::Create(void)
43+
{
44+
if (!LoadWindowFromXML("settings-ui.xml", "rawsettingseditor", this))
45+
return false;
46+
47+
m_settingsList = dynamic_cast<MythUIButtonList *> (GetChild("settings"));
48+
49+
m_saveButton = dynamic_cast<MythUIButton *> (GetChild("save"));
50+
m_cancelButton = dynamic_cast<MythUIButton *> (GetChild("cancel"));
51+
52+
if (!m_saveButton || !m_cancelButton)
53+
{
54+
VERBOSE(VB_IMPORTANT, "Theme is missing critical theme elements.");
55+
return false;
56+
}
57+
58+
if (!BuildFocusList())
59+
VERBOSE(VB_IMPORTANT, "Failed to build a focuslist. Something is wrong");
60+
61+
MythUIText *text = dynamic_cast<MythUIText *> (GetChild("heading"));
62+
text->SetText(m_title);
63+
64+
m_textLabel = dynamic_cast<MythUIText *> (GetChild("label-text"));
65+
66+
MythUIShape *shape = NULL;
67+
68+
for (int i = -8; i <= 8; i++)
69+
{
70+
text = dynamic_cast<MythUIText *>
71+
(GetChild(QString("value%1%2").arg(i >= 0? "+" : "").arg(i)));
72+
if (text)
73+
m_prevNextTexts[i] = text;
74+
75+
shape = dynamic_cast<MythUIShape *>
76+
(GetChild(QString("shape%1%2").arg(i >= 0? "+" : "").arg(i)));
77+
if (shape)
78+
m_prevNextShapes[i] = shape;
79+
}
80+
81+
m_settingValue = dynamic_cast<MythUITextEdit *> (GetChild("settingvalue"));
82+
83+
connect(m_settingsList, SIGNAL(itemSelected(MythUIButtonListItem*)),
84+
SLOT(selectionChanged(MythUIButtonListItem*)));
85+
connect(m_settingValue, SIGNAL(LosingFocus()), SLOT(valueChanged()));
86+
87+
connect(m_saveButton, SIGNAL(Clicked()), this, SLOT(Save()));
88+
connect(m_cancelButton, SIGNAL(Clicked()), this, SLOT(Close()));
89+
90+
LoadInBackground();
91+
92+
return true;
93+
}
94+
95+
/** \fn RawSettingsEditor::Load(void)
96+
* \brief Loads the current values for the specified settings list
97+
*/
98+
void RawSettingsEditor::Load(void)
99+
{
100+
QList<QString>settingsList = m_settings.keys();
101+
QList<QString>::iterator it = settingsList.begin();
102+
103+
// FIXME, optimize this using gContext->GetSettings()
104+
// QMap<QString,QString> kv;
105+
106+
while (it != settingsList.end())
107+
{
108+
QString value = gContext->GetSetting(*it);
109+
m_settingValues[*it] = value;
110+
m_origValues[*it] = value;
111+
112+
++it;
113+
}
114+
m_settingValues.detach();
115+
m_origValues.detach();
116+
}
117+
118+
/** \fn RawSettingsEditor::Init(void)
119+
* \brief Initialize the settings screen with the loaded data
120+
*/
121+
void RawSettingsEditor::Init(void)
122+
{
123+
QList<QString>settingsList = m_settings.keys();
124+
QList<QString>::iterator it = settingsList.begin();
125+
126+
while (it != settingsList.end())
127+
{
128+
MythUIButtonListItem *item = new MythUIButtonListItem(m_settingsList,
129+
"", qVariantFromValue(*it));
130+
131+
if (m_settings[*it].isEmpty())
132+
item->SetText(*it);
133+
else
134+
item->SetText(m_settings[*it]);
135+
136+
++it;
137+
}
138+
139+
m_settingsList->SetItemCurrent(0);
140+
m_textLabel->SetText(m_settingsList->GetItemFirst()->GetText());
141+
updatePrevNextTexts();
142+
}
143+
144+
/** \fn RawSettingsEditor::Save(void)
145+
* \brief Save editted values and clear settings cache if necessary
146+
*/
147+
void RawSettingsEditor::Save(void)
148+
{
149+
bool changed = false;
150+
151+
QHash <QString, QString>::const_iterator it = m_settingValues.constBegin();
152+
while (it != m_settingValues.constEnd())
153+
{
154+
if ((!it.value().isEmpty()) ||
155+
((m_origValues.contains(it.key())) &&
156+
(!m_origValues.value(it.key()).isEmpty())))
157+
{
158+
gContext->SaveSetting(it.key(), it.value());
159+
changed = true;
160+
}
161+
162+
++it;
163+
}
164+
165+
if (changed && (!gContext->IsMasterHost() || gContext->BackendIsRunning()))
166+
RemoteSendMessage("CLEAR_SETTINGS_CACHE");
167+
168+
Close();
169+
}
170+
171+
/** \fn RawSettingsEditor::selectionChanged(MythUIButtonListItem *item)
172+
* \brief Slot handler for buttonlist current item changes
173+
*
174+
* Updates the text edit area with the current value of the setting that
175+
* is currently selected in the button list.
176+
*
177+
* \param item The currently selected item in the buttonlist
178+
*/
179+
void RawSettingsEditor::selectionChanged(MythUIButtonListItem *item)
180+
{
181+
if (!item)
182+
return;
183+
184+
m_settingValue->SetText(m_settingValues[item->GetData().toString()]);
185+
m_textLabel->SetText(item->GetText());
186+
187+
updatePrevNextTexts();
188+
}
189+
190+
/** \fn RawSettingsEditor::updatePrevNextTexts(void)
191+
* \brief Updates previous and next text areas
192+
*
193+
* Updates previous and next text areas to show values of other non-current
194+
* settings in the button list.
195+
*/
196+
void RawSettingsEditor::updatePrevNextTexts(void)
197+
{
198+
MythUIButtonListItem *tmpitem;
199+
int curPos = m_settingsList->GetCurrentPos();
200+
int recs = m_settingsList->GetCount();
201+
202+
if (!recs)
203+
return;
204+
205+
for (int i = -8; i <= 8; i++)
206+
{
207+
if (m_prevNextTexts.contains(i))
208+
{
209+
if (((i < 0) && ((curPos + i) >= 0)) ||
210+
((i > 0) && (((recs-1) - i) >= curPos)))
211+
{
212+
if (m_prevNextShapes.contains(i))
213+
m_prevNextShapes[i]->Show();
214+
215+
tmpitem = m_settingsList->GetItemAt(curPos + i);
216+
m_prevNextTexts[i]->SetText(
217+
m_settingValues[tmpitem->GetData().toString()]);
218+
}
219+
else
220+
{
221+
if (m_prevNextShapes.contains(i))
222+
m_prevNextShapes[i]->Hide();
223+
224+
m_prevNextTexts[i]->SetText(QString());
225+
}
226+
}
227+
}
228+
}
229+
230+
/** \fn RawSettingsEditor::valueChanged(void)
231+
* \brief Tracks current value for a setting when the value is editted
232+
*
233+
* Updates the local in-memory settings value cache when the value for a
234+
* setting in the list is updated.
235+
*/
236+
void RawSettingsEditor::valueChanged(void)
237+
{
238+
m_settingValues[m_settingsList->GetItemCurrent()->GetData().toString()] =
239+
m_settingValue->GetText();
240+
}
241+
242+
/* vim: set expandtab tabstop=4 shiftwidth=4: */
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
#ifndef RAW_SETTINGS_EDITOR_H_
2+
#define RAW_SETTINGS_EDITOR_H_
3+
4+
#include <QEvent>
5+
#include <QHash>
6+
#include <QObject>
7+
8+
#include "mythscreentype.h"
9+
10+
#include "mythuibutton.h"
11+
#include "mythuishape.h"
12+
#include "mythuitextedit.h"
13+
14+
class MythUIButtonList;
15+
class MythUIButtonListItem;
16+
class MythUIText;
17+
class MythUITextEdit;
18+
class MythUIButton;
19+
20+
/** \class RawSettingsEditor
21+
* \brief An editor screen that allows manipulation of raw settings values
22+
*
23+
* This class is a base to build on. It is not meant to be used directly.
24+
* Any inheritting class must set the title and list of settings names in
25+
* its constructor.
26+
*/
27+
class RawSettingsEditor : public MythScreenType
28+
{
29+
Q_OBJECT
30+
31+
public:
32+
// Constructor
33+
RawSettingsEditor(MythScreenStack *parent, const char *name = 0);
34+
35+
// Destructor
36+
~RawSettingsEditor();
37+
38+
// MythScreenType overrides
39+
bool Create(void);
40+
void Load(void);
41+
void Init(void);
42+
43+
private slots:
44+
// Saves changes
45+
void Save(void);
46+
47+
// Helpers for keeping track of current value and updating the screen
48+
void selectionChanged(MythUIButtonListItem *item);
49+
void valueChanged(void);
50+
51+
protected:
52+
// Protected data, directly accessed by inheritting classes
53+
QString m_title;
54+
QMap <QString, QString> m_settings;
55+
56+
private:
57+
// Helper for updating prev/next values on screen
58+
void updatePrevNextTexts(void);
59+
60+
// UI Widgets on screen
61+
MythUIButtonList *m_settingsList;
62+
MythUITextEdit *m_settingValue;
63+
64+
MythUIButton *m_saveButton;
65+
MythUIButton *m_cancelButton;
66+
67+
MythUIText *m_textLabel;
68+
69+
// Prev/Next text areas on screen
70+
QHash <int, MythUIText*> m_prevNextTexts;
71+
QHash <int, MythUIShape*> m_prevNextShapes;
72+
73+
// Original and current settings values used for selective saving
74+
QHash <QString, QString> m_origValues;
75+
QHash <QString, QString> m_settingValues;
76+
};
77+
78+
#endif
79+
80+
/* vim: set expandtab tabstop=4 shiftwidth=4: */

0 commit comments

Comments
 (0)