refactor of XLCDproc, fixed bugs and leaks
[xbmc:xbmc-antiquated.git] / xbmc / linux / XLCDproc.cpp
1 /*
2  *      Copyright (C) 2005-2008 Team XBMC
3  *      http://www.xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, write to
17  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18  *  http://www.gnu.org/copyleft/gpl.html
19  *
20  */
21
22 #include "PlatformInclude.h"
23 #include "XLCDproc.h"
24 #include "../utils/log.h"
25 #include "../utils/TimeUtils.h"
26 #include "AdvancedSettings.h"
27 #include "GUISettings.h"
28
29 #define SCROLL_SPEED_IN_MSEC 250
30
31
32 XLCDproc::XLCDproc()
33 {
34   m_iActualpos   = 0;
35   m_iBackLight   = 32;
36   m_iLCDContrast = 50;
37   m_bStop        = true;
38   m_sockfd       = -1;
39   m_lastInitAttempt = 0;
40   m_initRetryInterval = INIT_RETRY_INTERVAL;
41   m_used = true;
42 }
43
44 XLCDproc::~XLCDproc()
45 {
46 }
47
48 void XLCDproc::Initialize()
49 {
50   if (!m_used || !g_guiSettings.GetBool("videoscreen.haslcd"))
51     return ;//nothing to do
52
53   // don't try to initialize too often
54   int now = CTimeUtils::GetTimeMS();
55   if (now < m_lastInitAttempt + m_initRetryInterval)
56     return;
57   m_lastInitAttempt = now;
58
59   ILCD::Initialize();
60
61   if (Connect())
62   {
63     // reset the retry interval after a successful connect
64     m_initRetryInterval = INIT_RETRY_INTERVAL;
65
66     m_bStop = false;
67   }
68   else
69   {
70     CloseSocket();
71
72     // give up after 60 seconds
73     if (m_initRetryInterval > INIT_RETRY_INTERVAL_MAX)
74     {
75       m_used = false;
76       CLog::Log(LOGERROR, "XLCDproc::%s - Connect failed. Giving up.", __FUNCTION__);
77     }
78     else
79     {
80       m_initRetryInterval *= 2;
81       CLog::Log(LOGERROR, "XLCDproc::%s - Connect failed. Retry in %d seconds.", __FUNCTION__,
82                 m_initRetryInterval/1000);
83     }
84   }
85 }
86
87 bool XLCDproc::Connect()
88 {
89   CloseSocket();
90
91   struct hostent *server;
92   server = gethostbyname(g_advancedSettings.m_lcdHostName);
93   if (server == NULL)
94   {
95      CLog::Log(LOGERROR, "XLCDproc::%s - Unable to resolve LCDd host.", __FUNCTION__);
96      return false;
97   }
98
99   m_sockfd = socket(AF_INET, SOCK_STREAM, 0);
100   if (m_sockfd == -1)
101   {
102     CLog::Log(LOGERROR, "XLCDproc::%s - Unable to create socket.", __FUNCTION__);
103     return false;
104   }
105
106   struct sockaddr_in serv_addr = {};
107   serv_addr.sin_family = AF_INET;
108   memmove(&serv_addr.sin_addr, server->h_addr_list[0], server->h_length);
109   //Connect to default LCDd port, hard coded for now.
110   serv_addr.sin_port = htons(13666);
111
112   if (connect(m_sockfd,(struct sockaddr*)&serv_addr,sizeof(serv_addr)) == -1)
113   {
114     CLog::Log(LOGERROR, "XLCDproc::%s - Unable to connect to host.", __FUNCTION__);
115     return false;
116   }
117
118   // Start a new session
119   CStdString hello;
120   hello = "hello\n";
121
122   if (write(m_sockfd,hello.c_str(),hello.size()) == -1)
123   {
124     CLog::Log(LOGERROR, "XLCDproc::%s - Unable to write to socket", __FUNCTION__);
125     return false;
126   }
127
128   // Receive LCDproc data to determine row and column information
129   char reply[1024];
130   if (read(m_sockfd,reply,1024) == -1)
131   {
132     CLog::Log(LOGERROR, "XLCDproc::%s - Unable to read from socket", __FUNCTION__);
133     return false;
134   }
135
136   unsigned int i=0;
137   while ((strncmp("lcd",reply + i,3) != 0 ) && (i < (strlen(reply) - 5))) i++;
138   if(sscanf(reply+i,"lcd wid %u hgt %u", &m_iColumns, &m_iRows))
139     CLog::Log(LOGDEBUG, "XLCDproc::%s - LCDproc data: Columns %i - Rows %i.", __FUNCTION__, m_iColumns, m_iRows);
140
141   //Build command to setup screen
142   CStdString cmd;
143   cmd = "screen_add xbmc\n";
144   if (!g_advancedSettings.m_lcdHeartbeat)
145     cmd.append("screen_set xbmc -heartbeat off\n");
146   if (g_advancedSettings.m_lcdScrolldelay != 0)
147   {
148     cmd.append("widget_add xbmc line1 scroller\n");
149     cmd.append("widget_add xbmc line2 scroller\n");
150     cmd.append("widget_add xbmc line3 scroller\n");
151     cmd.append("widget_add xbmc line4 scroller\n");
152   }
153   else
154   {
155     cmd.append("widget_add xbmc line1 string\n");
156     cmd.append("widget_add xbmc line2 string\n");
157     cmd.append("widget_add xbmc line3 string\n");
158     cmd.append("widget_add xbmc line4 string\n");
159   }
160
161   //Send to server
162   if (write(m_sockfd,cmd.c_str(),cmd.size()) == -1)
163   {
164     CLog::Log(LOGERROR, "XLCDproc::%s - Unable to write to socket", __FUNCTION__);
165     return false;
166   }
167
168   return true;
169 }
170
171 void XLCDproc::CloseSocket()
172 {
173   if (m_sockfd != -1)
174   {
175     shutdown(m_sockfd, SHUT_RDWR);
176     close(m_sockfd);
177     m_sockfd = -1;
178   }
179 }
180
181 bool XLCDproc::IsConnected()
182 {
183   if (m_sockfd == -1)
184     return false;
185
186   CStdString cmd;
187   cmd = "noop\n";
188
189   if (write(m_sockfd,cmd.c_str(),cmd.size()) == -1)
190   {
191     CLog::Log(LOGERROR, "XLCDproc::%s - Unable to write to socket", __FUNCTION__);
192     CloseSocket();
193     return false;
194   }
195
196   return true;
197 }
198
199 void XLCDproc::SetBackLight(int iLight)
200 {
201   if (m_sockfd == -1)
202     return;
203
204   //Build command
205   CStdString cmd;
206
207   if (iLight == 0)
208   {
209     m_bStop = true;
210     cmd = "screen_set xbmc -backlight off\n";
211     cmd.append("widget_del xbmc line1\n");
212     cmd.append("widget_del xbmc line2\n");
213     cmd.append("widget_del xbmc line3\n");
214     cmd.append("widget_del xbmc line4\n");
215   }
216   else if (iLight > 0)
217   {
218     m_bStop = false;
219     cmd = "screen_set xbmc -backlight on\n";
220     if (g_advancedSettings.m_lcdScrolldelay != 0)
221     {
222       cmd.append("widget_add xbmc line1 scroller\n");
223       cmd.append("widget_add xbmc line2 scroller\n");
224       cmd.append("widget_add xbmc line3 scroller\n");
225       cmd.append("widget_add xbmc line4 scroller\n");
226     }
227     else
228     {
229       cmd.append("widget_add xbmc line1 string\n");
230       cmd.append("widget_add xbmc line2 string\n");
231       cmd.append("widget_add xbmc line3 string\n");
232       cmd.append("widget_add xbmc line4 string\n");
233     }
234   }
235
236   //Send to server
237   if (write(m_sockfd,cmd.c_str(),cmd.size()) == -1)
238   {
239     CLog::Log(LOGERROR, "XLCDproc::%s - Unable to write to socket", __FUNCTION__);
240     CloseSocket();
241   }
242 }
243 void XLCDproc::SetContrast(int iContrast)
244 {
245   //TODO: Not sure if you can control contrast from client
246 }
247
248 void XLCDproc::Stop()
249 {
250   CloseSocket();
251   m_bStop = true;
252 }
253
254 void XLCDproc::Suspend()
255 {
256   if (m_bStop || m_sockfd == -1)
257     return;
258
259   //Build command to suspend screen
260   CStdString cmd;
261   cmd = "screen_set xbmc -priority hidden\n";
262
263   //Send to server
264   if (write(m_sockfd,cmd.c_str(),cmd.size()) == -1)
265   {
266     CLog::Log(LOGERROR, "XLCDproc::%s - Unable to write to socket", __FUNCTION__);
267     CloseSocket();
268   }
269 }
270
271 void XLCDproc::Resume()
272 {
273   if (m_bStop || m_sockfd == -1)
274     return;
275
276   //Build command to resume screen
277   CStdString cmd;
278   cmd = "screen_set xbmc -priority info\n";
279
280   //Send to server
281   if (write(m_sockfd,cmd.c_str(),cmd.size()) == -1)
282   {
283     CLog::Log(LOGERROR, "XLCDproc::%s - Unable to write to socket", __FUNCTION__);
284     CloseSocket();
285   }
286 }
287
288 void XLCDproc::SetLine(int iLine, const CStdString& strLine)
289 {
290   if (m_bStop || m_sockfd == -1)
291     return;
292
293   if (iLine < 0 || iLine >= (int)m_iRows)
294     return;
295
296   CStdString strLineLong = strLine;
297   strLineLong.Trim();
298   StringToLCDCharSet(strLineLong);
299
300   //make string fit the display if it's smaller than the width
301   if (strLineLong.size() < m_iColumns)
302     strLineLong.append(m_iColumns - strLineLong.size(), ' ');
303   //else if the string doesn't fit the display, lcdproc will scroll it, so we need a space
304   else if (strLineLong.size() > m_iColumns)
305     strLineLong += " ";
306
307   if (strLineLong != m_strLine[iLine])
308   {
309     CStdString cmd;
310     int ln = iLine + 1;
311
312     if (g_advancedSettings.m_lcdScrolldelay != 0)
313       cmd.Format("widget_set xbmc line%i 1 %i %i %i m %i \"%s\"\n", ln, ln, m_iColumns, ln, g_advancedSettings.m_lcdScrolldelay, strLineLong.c_str());
314     else
315       cmd.Format("widget_set xbmc line%i 1 %i \"%s\"\n", ln, ln, strLineLong.c_str());
316
317     if (write(m_sockfd, cmd.c_str(), cmd.size()) == -1)
318     {
319       CLog::Log(LOGERROR, "XLCDproc::%s - Unable to write to socket, LCDd not running?", __FUNCTION__);
320       CloseSocket();
321       return;
322     }
323     m_bUpdate[iLine] = true;
324     m_strLine[iLine] = strLineLong;
325     m_event.Set();
326   }
327 }
328
329 void XLCDproc::Process()
330 {
331 }
332