win32: removed unneeded afxres.h include in the resource files, which is not present...
[deb_libcec.git] / src / testclient / main.cpp
1 /*
2 * This file is part of the libCEC(R) library.
3 *
4 * libCEC(R) is Copyright (C) 2011 Pulse-Eight Limited. All rights reserved.
5 * libCEC(R) is an original work, containing original code.
6 *
7 * libCEC(R) is a trademark of Pulse-Eight Limited.
8 *
9 * This program is dual-licensed; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 *
23 *
24 * Alternatively, you can license this library under a commercial license,
25 * please contact Pulse-Eight Licensing for more information.
26 *
27 * For more information contact:
28 * Pulse-Eight Licensing <license@pulse-eight.com>
29 * http://www.pulse-eight.com/
30 * http://www.pulse-eight.net/
31 */
32
33 #include <cec.h>
34
35 #include <cstdio>
36 #include <fcntl.h>
37 #include <iostream>
38 #include <fstream>
39 #include <string>
40 #include <sstream>
41 #include "../lib/platform/threads.h"
42 #include "../lib/util/StdString.h"
43
44 using namespace CEC;
45 using namespace std;
46
47 #define CEC_TEST_CLIENT_VERSION 1
48
49 #include <cecloader.h>
50
51 int g_cecLogLevel = CEC_LOG_ALL;
52 ofstream g_logOutput;
53 bool g_bShortLog = false;
54 CStdString g_strPort;
55
56 inline bool HexStrToInt(const std::string& data, uint8_t& value)
57 {
58 int iTmp(0);
59 if (sscanf(data.c_str(), "%x", &iTmp) == 1)
60 {
61 if (iTmp > 256)
62 value = 255;
63 else if (iTmp < 0)
64 value = 0;
65 else
66 value = (uint8_t) iTmp;
67
68 return true;
69 }
70
71 return false;
72 }
73
74 //get the first word (separated by whitespace) from string data and place that in word
75 //then remove that word from string data
76 bool GetWord(string& data, string& word)
77 {
78 stringstream datastream(data);
79 string end;
80
81 datastream >> word;
82 if (datastream.fail())
83 {
84 data.clear();
85 return false;
86 }
87
88 size_t pos = data.find(word) + word.length();
89
90 if (pos >= data.length())
91 {
92 data.clear();
93 return true;
94 }
95
96 data = data.substr(pos);
97
98 datastream.clear();
99 datastream.str(data);
100
101 datastream >> end;
102 if (datastream.fail())
103 data.clear();
104
105 return true;
106 }
107
108 void flush_log(ICECAdapter *cecParser)
109 {
110 cec_log_message message;
111 while (cecParser && cecParser->GetNextLogMessage(&message))
112 {
113 if ((message.level & g_cecLogLevel) == message.level)
114 {
115 CStdString strLevel;
116 switch (message.level)
117 {
118 case CEC_LOG_ERROR:
119 strLevel = "ERROR: ";
120 break;
121 case CEC_LOG_WARNING:
122 strLevel = "WARNING: ";
123 break;
124 case CEC_LOG_NOTICE:
125 strLevel = "NOTICE: ";
126 break;
127 case CEC_LOG_TRAFFIC:
128 strLevel = "TRAFFIC: ";
129 break;
130 case CEC_LOG_DEBUG:
131 strLevel = "DEBUG: ";
132 break;
133 default:
134 break;
135 }
136
137 CStdString strFullLog;
138 strFullLog.Format("%s[%16lld]\t%s", strLevel.c_str(), message.time, message.message);
139 cout << strFullLog.c_str() << endl;
140
141 if (g_logOutput.is_open())
142 {
143 if (g_bShortLog)
144 g_logOutput << message.message << endl;
145 else
146 g_logOutput << strFullLog.c_str() << endl;
147 }
148 }
149 }
150 }
151
152 void list_devices(ICECAdapter *parser)
153 {
154 cec_adapter *devices = new cec_adapter[10];
155 uint8_t iDevicesFound = parser->FindAdapters(devices, 10, NULL);
156 if (iDevicesFound <= 0)
157 {
158 cout << "Found devices: NONE" << endl;
159 }
160 else
161 {
162 CStdString strLog;
163 strLog.Format("Found devices: %d", iDevicesFound);
164 cout << strLog.c_str() << endl;
165 for (unsigned int iDevicePtr = 0; iDevicePtr < iDevicesFound; iDevicePtr++)
166 {
167 CStdString strDevice;
168 strDevice.Format("device: %d\npath: %s\ncom port: %s", iDevicePtr + 1, devices[iDevicePtr].path, devices[iDevicePtr].comm);
169 cout << endl << strDevice.c_str() << endl;
170 }
171 }
172 }
173
174 void show_help(const char* strExec)
175 {
176 cout << endl <<
177 strExec << " {-h|--help|-l|--list-devices|[COM PORT]}" << endl <<
178 endl <<
179 "parameters:" << endl <<
180 " -h --help Shows this help text" << endl <<
181 " -l --list-devices List all devices on this system" << endl <<
182 " -t --type {p|r|t|a} The device type to use. More than one is possible." << endl <<
183 " -f --log-file {file} Writes all libCEC log message to a file" << endl <<
184 " -sf --short-log-file {file} Writes all libCEC log message without timestamps" << endl <<
185 " and log levels to a file." << endl <<
186 " -d --log-level {level} Sets the log level. See cectypes.h for values." << endl <<
187 " [COM PORT] The com port to connect to. If no COM" << endl <<
188 " port is given, the client tries to connect to the" << endl <<
189 " first device that is detected." << endl <<
190 endl <<
191 "Type 'h' or 'help' and press enter after starting the client to display all " << endl <<
192 "available commands" << endl;
193 }
194
195 ICECAdapter *create_parser(cec_device_type_list typeList)
196 {
197 ICECAdapter *parser = LibCecInit("CECTester", typeList);
198 if (!parser || parser->GetMinLibVersion() > CEC_TEST_CLIENT_VERSION)
199 {
200 #ifdef __WINDOWS__
201 cout << "Cannot load libcec.dll" << endl;
202 #else
203 cout << "Cannot load libcec.so" << endl;
204 #endif
205 return NULL;
206 }
207
208 CStdString strLog;
209 strLog.Format("CEC Parser created - libcec version %d.%d", parser->GetLibVersionMajor(), parser->GetLibVersionMinor());
210 cout << strLog.c_str() << endl;
211
212 return parser;
213 }
214
215 void show_console_help(void)
216 {
217 cout << endl <<
218 "================================================================================" << endl <<
219 "Available commands:" << endl <<
220 endl <<
221 "tx {bytes} transfer bytes over the CEC line." << endl <<
222 "txn {bytes} transfer bytes but don't wait for transmission ACK." << endl <<
223 "[tx 40 00 FF 11 22 33] sends bytes 0x40 0x00 0xFF 0x11 0x22 0x33" << endl <<
224 endl <<
225 "on {address} power on the device with the given logical address." << endl <<
226 "[on 5] power on a connected audio system" << endl <<
227 endl <<
228 "standby {address} put the device with the given address in standby mode." << endl <<
229 "[standby 0] powers off the TV" << endl <<
230 endl <<
231 "la {logical_address} change the logical address of the CEC adapter." << endl <<
232 "[la 4] logical address 4" << endl <<
233 endl <<
234 "pa {physical_address} change the physical address of the CEC adapter." << endl <<
235 "[pa 10 00] physical address 1.0.0.0" << endl <<
236 endl <<
237 "osd {addr} {string} set OSD message on the specified device." << endl <<
238 "[osd 0 Test Message] displays 'Test Message' on the TV" << endl <<
239 endl <<
240 "ver {addr} get the CEC version of the specified device." << endl <<
241 "[ver 0] get the CEC version of the TV" << endl <<
242 endl <<
243 "ven {addr} get the vendor ID of the specified device." << endl <<
244 "[ven 0] get the vendor ID of the TV" << endl <<
245 endl <<
246 "lang {addr} get the menu language of the specified device." << endl <<
247 "[lang 0] get the menu language of the TV" << endl <<
248 endl <<
249 "pow {addr} get the power status of the specified device." << endl <<
250 "[pow 0] get the power status of the TV" << endl <<
251 endl <<
252 "poll {addr} poll the specified device." << endl <<
253 "[poll 0] sends a poll message to the TV" << endl <<
254 endl <<
255 "[mon] {1|0} enable or disable CEC bus monitoring." << endl <<
256 "[log] {1 - 31} change the log level. see cectypes.h for values." << endl <<
257 "[ping] send a ping command to the CEC adapter." << endl <<
258 "[bl] to let the adapter enter the bootloader, to upgrade" << endl <<
259 " the flash rom." << endl <<
260 "[r] reconnect to the CEC adapter." << endl <<
261 "[h] or [help] show this help." << endl <<
262 "[q] or [quit] to quit the CEC test client and switch off all" << endl <<
263 " connected CEC devices." << endl <<
264 "================================================================================" << endl;
265 }
266
267 int main (int argc, char *argv[])
268 {
269 cec_device_type_list typeList;
270 typeList.clear();
271
272 int iArgPtr = 1;
273 while (iArgPtr < argc)
274 {
275 if (argc >= iArgPtr + 1)
276 {
277 if (!strcmp(argv[iArgPtr], "-f") ||
278 !strcmp(argv[iArgPtr], "--log-file") ||
279 !strcmp(argv[iArgPtr], "-sf") ||
280 !strcmp(argv[iArgPtr], "--short-log-file"))
281 {
282 if (argc >= iArgPtr + 2)
283 {
284 g_logOutput.open(argv[iArgPtr + 1]);
285 g_bShortLog = (!strcmp(argv[iArgPtr], "-sf") || !strcmp(argv[iArgPtr], "--short-log-file"));
286 iArgPtr += 2;
287 }
288 else
289 {
290 cout << "== skipped log-file parameter: no file given ==" << endl;
291 ++iArgPtr;
292 }
293 }
294 else if (!strcmp(argv[iArgPtr], "-d") ||
295 !strcmp(argv[iArgPtr], "--log-level"))
296 {
297 if (argc >= iArgPtr + 2)
298 {
299 int iNewLevel = atoi(argv[iArgPtr + 1]);
300 if (iNewLevel >= CEC_LOG_ERROR && iNewLevel <= CEC_LOG_ALL)
301 {
302 g_cecLogLevel = iNewLevel;
303 cout << "log level set to " << argv[iArgPtr + 1] << endl;
304 }
305 else
306 {
307 cout << "== skipped log-level parameter: invalid level '" << argv[iArgPtr + 1] << "' ==" << endl;
308 }
309 iArgPtr += 2;
310 }
311 else
312 {
313 cout << "== skipped log-level parameter: no level given ==" << endl;
314 ++iArgPtr;
315 }
316 }
317 else if (!strcmp(argv[iArgPtr], "-t") ||
318 !strcmp(argv[iArgPtr], "--type"))
319 {
320 if (argc >= iArgPtr + 2)
321 {
322 if (!strcmp(argv[iArgPtr + 1], "p"))
323 {
324 cout << "== using device type 'playback device'" << endl;
325 typeList.add(CEC_DEVICE_TYPE_PLAYBACK_DEVICE);
326 }
327 else if (!strcmp(argv[iArgPtr + 1], "r"))
328 {
329 cout << "== using device type 'recording device'" << endl;
330 typeList.add(CEC_DEVICE_TYPE_RECORDING_DEVICE);
331 }
332 else if (!strcmp(argv[iArgPtr + 1], "t"))
333 {
334 cout << "== using device type 'tuner'" << endl;
335 typeList.add(CEC_DEVICE_TYPE_TUNER);
336 }
337 else if (!strcmp(argv[iArgPtr + 1], "a"))
338 {
339 cout << "== using device type 'audio system'" << endl;
340 typeList.add(CEC_DEVICE_TYPE_AUDIO_SYSTEM);
341 }
342 else
343 {
344 cout << "== skipped invalid device type '" << argv[iArgPtr + 1] << "'" << endl;
345 }
346 ++iArgPtr;
347 }
348 ++iArgPtr;
349 }
350 else if (!strcmp(argv[iArgPtr], "--list-devices") ||
351 !strcmp(argv[iArgPtr], "-l"))
352 {
353 ICECAdapter *parser = create_parser(typeList);
354 if (parser)
355 {
356 list_devices(parser);
357 UnloadLibCec(parser);
358 }
359 return 0;
360 }
361 else if (!strcmp(argv[iArgPtr], "--help") ||
362 !strcmp(argv[iArgPtr], "-h"))
363 {
364 show_help(argv[0]);
365 return 0;
366 }
367 else
368 {
369 g_strPort = argv[iArgPtr++];
370 }
371 }
372 }
373
374 if (typeList.empty())
375 {
376 cout << "No device type given. Using 'playback device'" << endl;
377 typeList.add(CEC_DEVICE_TYPE_PLAYBACK_DEVICE);
378 }
379
380 ICECAdapter *parser = LibCecInit("CECTester", typeList);
381 if (!parser || parser->GetMinLibVersion() > CEC_TEST_CLIENT_VERSION)
382 {
383 #ifdef __WINDOWS__
384 cout << "Cannot load libcec.dll" << endl;
385 #else
386 cout << "Cannot load libcec.so" << endl;
387 #endif
388 return 1;
389 }
390 CStdString strLog;
391 strLog.Format("CEC Parser created - libcec version %d.%d", parser->GetLibVersionMajor(), parser->GetLibVersionMinor());
392 cout << strLog.c_str() << endl;
393
394 //make stdin non-blocking
395 #ifndef __WINDOWS__
396 int flags = fcntl(0, F_GETFL, 0);
397 flags |= O_NONBLOCK;
398 fcntl(0, F_SETFL, flags);
399 #endif
400
401 if (g_strPort.IsEmpty())
402 {
403 cout << "no serial port given. trying autodetect: ";
404 cec_adapter devices[10];
405 uint8_t iDevicesFound = parser->FindAdapters(devices, 10, NULL);
406 if (iDevicesFound <= 0)
407 {
408 cout << "FAILED" << endl;
409 UnloadLibCec(parser);
410 return 1;
411 }
412 else
413 {
414 cout << endl << " path: " << devices[0].path << endl <<
415 " com port: " << devices[0].comm << endl << endl;
416 g_strPort = devices[0].comm;
417 }
418 }
419
420 if (!parser->Open(g_strPort.c_str()))
421 {
422 cout << "unable to open the device on port " << g_strPort << endl;
423 flush_log(parser);
424 UnloadLibCec(parser);
425 return 1;
426 }
427
428 cout << "cec device opened" << endl;
429
430 parser->PowerOnDevices(CECDEVICE_TV);
431 flush_log(parser);
432
433 parser->SetActiveSource();
434 flush_log(parser);
435
436 bool bContinue(true);
437 cout << "waiting for input" << endl;
438 while (bContinue)
439 {
440 flush_log(parser);
441
442 /* just ignore the command buffer and clear it */
443 cec_command dummy;
444 while (parser && parser->GetNextCommand(&dummy)) {}
445
446 string input;
447 getline(cin, input);
448 cin.clear();
449
450 if (!input.empty())
451 {
452 string command;
453 if (GetWord(input, command))
454 {
455 if (command == "tx" || command == "txn")
456 {
457 string strvalue;
458 uint8_t ivalue;
459 cec_command bytes;
460 bytes.clear();
461
462 while (GetWord(input, strvalue) && HexStrToInt(strvalue, ivalue))
463 bytes.push_back(ivalue);
464
465 if (command == "txn")
466 bytes.transmit_timeout = 0;
467
468 parser->Transmit(bytes);
469 }
470 else if (command == "on")
471 {
472 string strValue;
473 uint8_t iValue = 0;
474 if (GetWord(input, strValue) && HexStrToInt(strValue, iValue) && iValue <= 0xF)
475 {
476 parser->PowerOnDevices((cec_logical_address) iValue);
477 }
478 else
479 {
480 cout << "invalid destination" << endl;
481 }
482 }
483 else if (command == "standby")
484 {
485 string strValue;
486 uint8_t iValue = 0;
487 if (GetWord(input, strValue) && HexStrToInt(strValue, iValue) && iValue <= 0xF)
488 {
489 parser->StandbyDevices((cec_logical_address) iValue);
490 }
491 else
492 {
493 cout << "invalid destination" << endl;
494 }
495 }
496 else if (command == "poll")
497 {
498 string strValue;
499 uint8_t iValue = 0;
500 if (GetWord(input, strValue) && HexStrToInt(strValue, iValue) && iValue <= 0xF)
501 {
502 if (parser->PollDevice((cec_logical_address) iValue))
503 cout << "POLL message sent" << endl;
504 else
505 cout << "POLL message not sent" << endl;
506 }
507 else
508 {
509 cout << "invalid destination" << endl;
510 }
511 }
512 else if (command == "la")
513 {
514 string strvalue;
515 if (GetWord(input, strvalue))
516 {
517 parser->SetLogicalAddress((cec_logical_address) atoi(strvalue.c_str()));
518 }
519 }
520 else if (command == "pa")
521 {
522 string strB1, strB2;
523 uint8_t iB1, iB2;
524 if (GetWord(input, strB1) && HexStrToInt(strB1, iB1) &&
525 GetWord(input, strB2) && HexStrToInt(strB2, iB2))
526 {
527 uint16_t iPhysicalAddress = ((uint16_t)iB1 << 8) + iB2;
528 parser->SetPhysicalAddress(iPhysicalAddress);
529 }
530 }
531 else if (command == "osd")
532 {
533 bool bFirstWord(false);
534 string strAddr, strMessage, strWord;
535 uint8_t iAddr;
536 if (GetWord(input, strAddr) && HexStrToInt(strAddr, iAddr) && iAddr < 0xF)
537 {
538 while (GetWord(input, strWord))
539 {
540 if (bFirstWord)
541 {
542 bFirstWord = false;
543 strMessage.append(" ");
544 }
545 strMessage.append(strWord);
546 }
547 parser->SetOSDString((cec_logical_address) iAddr, CEC_DISPLAY_CONTROL_DISPLAY_FOR_DEFAULT_TIME, strMessage.c_str());
548 }
549 }
550 else if (command == "ping")
551 {
552 parser->PingAdapter();
553 }
554 else if (command == "mon")
555 {
556 CStdString strEnable;
557 if (GetWord(input, strEnable) && (strEnable.Equals("0") || strEnable.Equals("1")))
558 {
559 parser->SwitchMonitoring(strEnable.Equals("1"));
560 }
561 }
562 else if (command == "bl")
563 {
564 parser->StartBootloader();
565 }
566 else if (command == "lang")
567 {
568 CStdString strDev;
569 if (GetWord(input, strDev))
570 {
571 int iDev = atoi(strDev);
572 if (iDev >= 0 && iDev < 15)
573 {
574 CStdString strLog;
575 cec_menu_language language;
576 if (parser->GetDeviceMenuLanguage((cec_logical_address) iDev, &language))
577 strLog.Format("menu language '%s'", language.language);
578 else
579 strLog = "failed!";
580 cout << strLog.c_str() << endl;
581 }
582 }
583 }
584 else if (command == "ven")
585 {
586 CStdString strDev;
587 if (GetWord(input, strDev))
588 {
589 int iDev = atoi(strDev);
590 if (iDev >= 0 && iDev < 15)
591 {
592 uint64_t iVendor = parser->GetDeviceVendorId((cec_logical_address) iDev);
593 CStdString strLog;
594 strLog.Format("vendor id: %06x", iVendor);
595 cout << strLog.c_str() << endl;
596 }
597 }
598 }
599 else if (command == "ver")
600 {
601 CStdString strDev;
602 if (GetWord(input, strDev))
603 {
604 int iDev = atoi(strDev);
605 if (iDev >= 0 && iDev < 15)
606 {
607 cec_version iVersion = parser->GetDeviceCecVersion((cec_logical_address) iDev);
608 switch (iVersion)
609 {
610 case CEC_VERSION_1_2:
611 cout << "CEC version 1.2" << endl;
612 break;
613 case CEC_VERSION_1_2A:
614 cout << "CEC version 1.2a" << endl;
615 break;
616 case CEC_VERSION_1_3:
617 cout << "CEC version 1.3" << endl;
618 break;
619 case CEC_VERSION_1_3A:
620 cout << "CEC version 1.3a" << endl;
621 break;
622 default:
623 cout << "unknown CEC version" << endl;
624 break;
625 }
626 }
627 }
628 }
629 else if (command == "pow")
630 {
631 CStdString strDev;
632 if (GetWord(input, strDev))
633 {
634 int iDev = atoi(strDev);
635 if (iDev >= 0 && iDev < 15)
636 {
637 cec_power_status iPower = parser->GetDevicePowerStatus((cec_logical_address) iDev);
638 switch (iPower)
639 {
640 case CEC_POWER_STATUS_ON:
641 cout << "powered on" << endl;
642 break;
643 case CEC_POWER_STATUS_IN_TRANSITION_ON_TO_STANDBY:
644 cout << "on -> standby" << endl;
645 break;
646 case CEC_POWER_STATUS_IN_TRANSITION_STANDBY_TO_ON:
647 cout << "standby -> on" << endl;
648 break;
649 case CEC_POWER_STATUS_STANDBY:
650 cout << "standby" << endl;
651 break;
652 default:
653 cout << "unknown power status" << endl;
654 break;
655 }
656 }
657 }
658 }
659 else if (command == "r")
660 {
661 cout << "closing the connection" << endl;
662 parser->Close();
663 flush_log(parser);
664
665 cout << "opening a new connection" << endl;
666 parser->Open(g_strPort.c_str());
667 flush_log(parser);
668
669 cout << "setting active source" << endl;
670 parser->SetActiveSource();
671 }
672 else if (command == "h" || command == "help")
673 {
674 show_console_help();
675 }
676 else if (command == "q" || command == "quit")
677 {
678 bContinue = false;
679 }
680 else if (command == "log")
681 {
682 CStdString strLevel;
683 if (GetWord(input, strLevel))
684 {
685 int iNewLevel = atoi(strLevel);
686 if (iNewLevel >= CEC_LOG_ERROR && iNewLevel <= CEC_LOG_ALL)
687 {
688 g_cecLogLevel = iNewLevel;
689 cout << "log level changed to " << strLevel.c_str() << endl;
690 }
691 }
692 }
693 }
694 if (bContinue)
695 cout << "waiting for input" << endl;
696 }
697 CCondition::Sleep(50);
698 }
699
700 parser->StandbyDevices(CECDEVICE_BROADCAST);
701 parser->Close();
702 flush_log(parser);
703 UnloadLibCec(parser);
704
705 if (g_logOutput.is_open())
706 g_logOutput.close();
707
708 return 0;
709 }