author | paule <paule> | 2007-02-07 10:25:00 (UTC) |
---|---|---|
committer | paule <paule> | 2007-02-07 10:25:00 (UTC) |
commit | 6629be2c0147283023abcf021227bf74ca18849f (patch) (unidiff) | |
tree | ed90c015aa96f199ec92fd930c0208e69098b941 | |
parent | d7a838e0f4352522fb2c12feea58129b16e76b89 (diff) | |
download | opie-6629be2c0147283023abcf021227bf74ca18849f.zip opie-6629be2c0147283023abcf021227bf74ca18849f.tar.gz opie-6629be2c0147283023abcf021227bf74ca18849f.tar.bz2 |
Implement learning process (requires patched irrecord with stdout buffering disabled)
-rw-r--r-- | noncore/tools/remote/recorddialog.cpp | 230 | ||||
-rw-r--r-- | noncore/tools/remote/recorddialog.h | 29 |
2 files changed, 229 insertions, 30 deletions
diff --git a/noncore/tools/remote/recorddialog.cpp b/noncore/tools/remote/recorddialog.cpp index a9c4c61..3d21fad 100644 --- a/noncore/tools/remote/recorddialog.cpp +++ b/noncore/tools/remote/recorddialog.cpp | |||
@@ -1,6 +1,6 @@ | |||
1 | /* | 1 | /* |
2 | Opie-Remote. emulates remote controlls on an iPaq (and maybe a Zaurus) in Opie. | 2 | Opie-Remote. emulates remote controlls on an iPaq (and maybe a Zaurus) in Opie. |
3 | Copyright (C) 2002 Thomas Stephens | 3 | Copyright (C) 2007 Paul Eggleton & Thomas Stephens |
4 | 4 | ||
5 | This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public | 5 | This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public |
6 | License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later | 6 | License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later |
@@ -14,18 +14,30 @@ You should have received a copy of the GNU General Public License along with thi | |||
14 | Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 14 | Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
15 | */ | 15 | */ |
16 | 16 | ||
17 | #include <stdlib.h> | ||
18 | #include <sys/stat.h> | ||
19 | |||
20 | #include <qfile.h> | ||
21 | |||
17 | #include "recorddialog.h" | 22 | #include "recorddialog.h" |
18 | 23 | ||
24 | #define RECORD_TMPDIR "/tmp/opie-remote" | ||
25 | |||
19 | using namespace Opie::Core; | 26 | using namespace Opie::Core; |
20 | using namespace Opie::Core; | 27 | using namespace Opie::Core; |
21 | RecordDialog::RecordDialog(QWidget *parent, const char *name) | 28 | RecordDialog::RecordDialog(QWidget *parent, const char *name) |
22 | :QDialog(parent, name) | 29 | :QDialog(parent, name, TRUE) |
23 | { | 30 | { |
24 | QVBoxLayout *layout = new QVBoxLayout(this); | 31 | QVBoxLayout *layout = new QVBoxLayout(this); |
25 | QHBoxLayout *hlayout = new QHBoxLayout(this); | 32 | QHBoxLayout *hlayout = new QHBoxLayout(this); |
26 | 33 | ||
27 | layout->insertSpacing(0,5); | 34 | layout->insertSpacing(0,5); |
28 | output = new QTextView("Please enter the name of the new remote, and press Return\n", 0, this, "output"); | 35 | |
36 | output = new QMultiLineEdit(this, "output"); | ||
37 | output->setReadOnly(true); | ||
38 | output->setWordWrap(QMultiLineEdit::WidgetWidth); | ||
39 | output->setText(tr("Please enter the name of the new remote, and press Next\n")); | ||
40 | |||
29 | layout->insertWidget(-1, output); | 41 | layout->insertWidget(-1, output); |
30 | layout->insertSpacing(-1, 5); | 42 | layout->insertSpacing(-1, 5); |
31 | layout->insertLayout(-1, hlayout); | 43 | layout->insertLayout(-1, hlayout); |
@@ -36,49 +48,215 @@ RecordDialog::RecordDialog(QWidget *parent, const char *name) | |||
36 | hlayout->insertWidget(-1, input, 1); | 48 | hlayout->insertWidget(-1, input, 1); |
37 | hlayout->insertSpacing(-1, 5); | 49 | hlayout->insertSpacing(-1, 5); |
38 | 50 | ||
39 | QPushButton *ret = new QPushButton("Return", this, "return"); | 51 | nextbtn = new QPushButton(tr("Next"), this, "next"); |
40 | hlayout->insertWidget(-1, ret); | 52 | nextbtn->setDefault(true); |
53 | hlayout->insertWidget(-1, nextbtn); | ||
41 | hlayout->insertSpacing(-1, 5); | 54 | hlayout->insertSpacing(-1, 5); |
42 | connect(ret, SIGNAL(clicked()), this, SLOT(retPressed()) ); | 55 | connect(nextbtn, SIGNAL(clicked()), this, SLOT(nextPressed()) ); |
43 | where = 0; | 56 | |
57 | setCaption(tr("Learn Remote")); | ||
58 | input->setFocus(); | ||
59 | |||
60 | stepnum = 0; | ||
61 | outputenabled = false; | ||
62 | writeok = true; | ||
63 | ignorekill = false; | ||
44 | 64 | ||
45 | record = new OProcess; | 65 | record = new OProcess; |
66 | |||
67 | // We need to stop lircd because irrecord accesses the IR device directly | ||
68 | lh = new LircHandler(); | ||
69 | lh->stopLircd(); | ||
46 | } | 70 | } |
47 | 71 | ||
48 | void RecordDialog::retPressed() | 72 | RecordDialog::~RecordDialog(void) |
49 | { | 73 | { |
50 | printf("RecordDialog::retPressed: ret pressed\n"); | 74 | delete lh; |
75 | delete record; | ||
76 | } | ||
51 | 77 | ||
52 | if(where == 0) | 78 | void RecordDialog::nextPressed() |
53 | { | 79 | { |
80 | if(stepnum == 0) { | ||
81 | QString remotename = input->text().stripWhiteSpace(); | ||
82 | |||
83 | if(remotename == "") { | ||
84 | QMessageBox::warning(this, tr("Error"), | ||
85 | tr("Please enter a name"), | ||
86 | QMessageBox::Ok, QMessageBox::NoButton); | ||
87 | return; | ||
88 | } | ||
89 | |||
90 | if(remotename.left(1) == ".") { | ||
91 | QMessageBox::warning(this, tr("Error"), | ||
92 | tr("Name cannot start with a dot"), | ||
93 | QMessageBox::Ok, QMessageBox::NoButton); | ||
94 | return; | ||
95 | } | ||
96 | |||
97 | // Check for invalid chars | ||
98 | QRegExp invalidchars = QRegExp("[~\\[\\]/'\"|\\\\]", TRUE, FALSE); | ||
99 | if(remotename.find(invalidchars) > -1) { | ||
100 | QMessageBox::warning(this, tr("Error"), | ||
101 | tr("The name %1 contains\ninvalid characters").arg(remotename), | ||
102 | QMessageBox::Ok, QMessageBox::NoButton); | ||
103 | return; | ||
104 | } | ||
105 | |||
106 | // Check if name already exists in config | ||
107 | if(lh->checkRemoteExists(remotename)) { | ||
108 | QMessageBox::warning(this, tr("Error"), | ||
109 | tr("A remote named %1\nalready exists.").arg(remotename), | ||
110 | QMessageBox::Ok, QMessageBox::NoButton); | ||
111 | return; | ||
112 | } | ||
113 | |||
114 | printf("RecordDialog::nextPressed: starting irrecord\n"); | ||
115 | input->setEnabled(false); | ||
116 | |||
54 | connect(record, SIGNAL(receivedStdout(Opie::Core::OProcess*,char*,int)), this, SLOT(incoming(Opie::Core::OProcess*,char*,int)) ); | 117 | connect(record, SIGNAL(receivedStdout(Opie::Core::OProcess*,char*,int)), this, SLOT(incoming(Opie::Core::OProcess*,char*,int)) ); |
55 | connect(record, SIGNAL(receivedStderr(Opie::Core::OProcess*,char*,int)), this, SLOT(incoming(Opie::Core::OProcess*,char*,int)) ); | 118 | connect(record, SIGNAL(receivedStderr(Opie::Core::OProcess*,char*,int)), this, SLOT(incomingErr(Opie::Core::OProcess*,char*,int)) ); |
56 | connect(record, SIGNAL(processExited(Opie::Core::OProcess*)), this, SLOT(done(Opie::Core::OProcess*)) ); | 119 | connect(record, SIGNAL(processExited(Opie::Core::OProcess*)), this, SLOT(done(Opie::Core::OProcess*)) ); |
57 | printf("RecordDialog::retPressed: starting irrecord\n"); | 120 | connect(record, SIGNAL(wroteStdin(Opie::Core::OProcess*)), this, SLOT(writeFinished(Opie::Core::OProcess*)) ); |
58 | QString file = "/tmp/" + input->text(); | 121 | |
59 | *record<<"irrecord"<<file.latin1(); | 122 | // Prepare temp path |
60 | if(!record->start(OProcess::NotifyOnExit, OProcess::AllOutput)) | 123 | QString tmpdir = RECORD_TMPDIR; |
124 | mkdir(tmpdir, 0700); | ||
125 | remotefile = tmpdir + "/" + remotename; | ||
126 | if(QFile::exists(remotefile)) | ||
127 | QFile::remove(remotefile); | ||
128 | |||
129 | // Set up and start irrecord | ||
130 | *record<<"irrecord"<<remotename.latin1(); | ||
131 | record->setWorkingDirectory(tmpdir); | ||
132 | if(!record->start(OProcess::NotifyOnExit, OProcess::All)) | ||
61 | { | 133 | { |
62 | QMessageBox *mb = new QMessageBox("Error!", | 134 | QMessageBox::critical(this, tr("Error"), |
63 | "Could not start irrecord. You must<br>use an lirc ipkg that includes<br>irrecord", | 135 | tr("Failed to start irrecord."), |
64 | QMessageBox::NoIcon, | 136 | QMessageBox::Ok, QMessageBox::NoButton); |
65 | QMessageBox::Ok, | ||
66 | QMessageBox::NoButton, | ||
67 | QMessageBox::NoButton); | ||
68 | mb->exec(); | ||
69 | return; | 137 | return; |
70 | } | 138 | } |
71 | // record->resume(); | 139 | stepnum = 1; |
72 | where = 1; | 140 | writeToProcess("\n", 1); |
141 | output->setText("Please get ready to press buttons on your remote.\n\nPress many different buttons, one at a time, holding each one down for approximately one second. Each button should generate at least one dot but in no case more than ten dots of output. Don't stop pressing buttons until at least two lines of dots appear.\n\nClick Next when you are ready to begin."); | ||
142 | } | ||
143 | else if(stepnum == 1) { | ||
144 | output->setText("Press buttons now:\n"); | ||
145 | output->setCursorPosition(output->numLines(), 32767); | ||
146 | outputenabled = true; | ||
147 | decoding = true; | ||
148 | writeToProcess("\n", 1); | ||
149 | nextbtn->setEnabled(false); | ||
150 | stepnum = 2; | ||
151 | } | ||
152 | else if(stepnum == 2) { | ||
153 | QString inp = input->text() + "\n"; | ||
154 | writeToProcess(inp, inp.length()); | ||
155 | input->clear(); | ||
73 | } | 156 | } |
74 | } | 157 | } |
75 | 158 | ||
76 | void RecordDialog::incoming(OProcess *proc, char *buffer, int len) | 159 | void RecordDialog::incoming(OProcess *proc, char *buffer, int len) |
77 | { | 160 | { |
78 | //output->setText(output->text() + QString(buffer).truncate(len-1)); | 161 | int line, col; |
79 | printf("RecordDialog::incoming: got text from irrecord\n"); | 162 | |
163 | if(outputenabled) { | ||
164 | char *bufstr = strndup(buffer, len); | ||
165 | output->getCursorPosition( &line, &col ); | ||
166 | output->insertAt( bufstr, line, col ); | ||
167 | |||
168 | if(decoding) { | ||
169 | int pos = output->text().find("Now enter the names for the buttons"); | ||
170 | if(pos > -1) { | ||
171 | output->setText(output->text().mid(pos)); | ||
172 | output->setCursorPosition(output->numLines(), 32767); | ||
173 | decoding = false; | ||
174 | input->clear(); | ||
175 | input->setEnabled(true); | ||
176 | nextbtn->setEnabled(true); | ||
177 | input->setFocus(); | ||
178 | } | ||
179 | } | ||
180 | |||
181 | free(bufstr); | ||
182 | } | ||
183 | } | ||
184 | |||
185 | void RecordDialog::incomingErr(OProcess *proc, char *buffer, int len) | ||
186 | { | ||
187 | char *bufstr = strndup(buffer, len); | ||
188 | errors += bufstr; | ||
189 | free(bufstr); | ||
190 | } | ||
191 | |||
192 | void RecordDialog::writeToProcess(const char *buffer, int len) | ||
193 | { | ||
194 | while(!writeok) { } | ||
195 | // FIXME leakage | ||
196 | record->writeStdin(strndup(buffer, len), len); | ||
197 | } | ||
198 | |||
199 | void RecordDialog::writeFinished(Opie::Core::OProcess *proc) | ||
200 | { | ||
201 | writeok = true; | ||
80 | } | 202 | } |
81 | 203 | ||
82 | void RecordDialog::done(OProcess *proc) | 204 | void RecordDialog::done(OProcess *proc) |
83 | { | 205 | { |
206 | if(proc->normalExit() && proc->exitStatus() == 0) { | ||
207 | // Success | ||
208 | |||
209 | // Merge in new remote to lircd.conf | ||
210 | lh->mergeRemoteConfig(remotefile); | ||
211 | |||
212 | nextbtn->hide(); | ||
213 | input->hide(); | ||
214 | output->setText("Learning completed successfully. Click OK to close."); | ||
215 | stepnum = 4; | ||
216 | } | ||
217 | else if(!ignorekill) { | ||
218 | QMessageBox::critical(this, tr("Error"), tr("Learning failed.\n") + errors, QMessageBox::Ok, QMessageBox::NoButton); | ||
219 | } | ||
220 | } | ||
221 | |||
222 | void RecordDialog::accept() | ||
223 | { | ||
224 | if(stepnum < 4) | ||
225 | QMessageBox::warning(this, tr("Error"), tr("Please complete the learning\n process or tap X to cancel."), QMessageBox::Ok, QMessageBox::NoButton); | ||
226 | else { | ||
227 | if(checkClose()) | ||
228 | QDialog::accept(); | ||
229 | } | ||
230 | } | ||
231 | |||
232 | void RecordDialog::reject() | ||
233 | { | ||
234 | if(checkClose()) | ||
235 | QDialog::reject(); | ||
236 | } | ||
237 | |||
238 | bool RecordDialog::checkClose() | ||
239 | { | ||
240 | if(!record->isRunning() || confirmClose()) { | ||
241 | if(record->isRunning()) { | ||
242 | ignorekill = true; | ||
243 | record->kill(); | ||
244 | } | ||
245 | // Restart lircd | ||
246 | lh->startLircd(); | ||
247 | // Delete temp file if it exists | ||
248 | if(QFile::exists(remotefile)) | ||
249 | unlink(remotefile); | ||
250 | rmdir(RECORD_TMPDIR); | ||
251 | |||
252 | return true; | ||
253 | } | ||
254 | return false; | ||
255 | } | ||
256 | |||
257 | bool RecordDialog::confirmClose() | ||
258 | { | ||
259 | return (QMessageBox::warning(this, tr("Cancel"), | ||
260 | tr("Learning is not complete.\nAre you sure you want to close?"), | ||
261 | QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes); | ||
84 | } | 262 | } |
diff --git a/noncore/tools/remote/recorddialog.h b/noncore/tools/remote/recorddialog.h index 46a82ce..e569fb6 100644 --- a/noncore/tools/remote/recorddialog.h +++ b/noncore/tools/remote/recorddialog.h | |||
@@ -18,31 +18,52 @@ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |||
18 | #define RecordDialog_H | 18 | #define RecordDialog_H |
19 | 19 | ||
20 | #include <qdialog.h> | 20 | #include <qdialog.h> |
21 | #include <qtextview.h> | 21 | #include <qmultilineedit.h> |
22 | #include <qlineedit.h> | 22 | #include <qlineedit.h> |
23 | #include <qpushbutton.h> | 23 | #include <qpushbutton.h> |
24 | #include <qwidget.h> | 24 | #include <qwidget.h> |
25 | #include <qlayout.h> | 25 | #include <qlayout.h> |
26 | #include <qlabel.h> | ||
26 | #include <qmessagebox.h> | 27 | #include <qmessagebox.h> |
27 | 28 | ||
28 | #include <stdio.h> | 29 | #include <stdio.h> |
29 | 30 | ||
30 | #include <opie2/oprocess.h> | 31 | #include <opie2/oprocess.h> |
31 | 32 | ||
33 | #include "lirchandler.h" | ||
34 | |||
32 | class RecordDialog : public QDialog | 35 | class RecordDialog : public QDialog |
33 | { | 36 | { |
34 | Q_OBJECT | 37 | Q_OBJECT |
35 | public: | 38 | public: |
36 | RecordDialog(QWidget *parent=0, const char *name=0); | 39 | RecordDialog(QWidget *parent=0, const char *name=0); |
40 | ~RecordDialog(void); | ||
37 | public slots: | 41 | public slots: |
38 | void retPressed(); | 42 | void nextPressed(); |
39 | void incoming(Opie::Core::OProcess *proc, char *buffer, int len); | 43 | void incoming(Opie::Core::OProcess *proc, char *buffer, int len); |
44 | void incomingErr(Opie::Core::OProcess *proc, char *buffer, int len); | ||
45 | void writeFinished(Opie::Core::OProcess *proc); | ||
40 | void done(Opie::Core::OProcess *proc); | 46 | void done(Opie::Core::OProcess *proc); |
47 | void accept(); | ||
48 | void reject(); | ||
49 | protected: | ||
50 | bool confirmClose(); | ||
51 | bool checkClose(); | ||
41 | private: | 52 | private: |
42 | QTextView *output; | 53 | QMultiLineEdit *output; |
43 | QLineEdit *input; | 54 | QLineEdit *input; |
55 | QPushButton *nextbtn; | ||
44 | Opie::Core::OProcess *record; | 56 | Opie::Core::OProcess *record; |
45 | int where; | 57 | int stepnum; |
58 | bool outputenabled; | ||
59 | bool decoding; | ||
60 | bool writeok; | ||
61 | bool ignorekill; | ||
62 | QString errors; | ||
63 | QString remotefile; | ||
64 | LircHandler *lh; | ||
65 | |||
66 | void writeToProcess(const char *buffer, int len); | ||
46 | }; | 67 | }; |
47 | 68 | ||
48 | #endif | 69 | #endif |