2 * Irreco MythTV backend
3 * Copyright (C) 2007 Jami Pekkanen (jami.pekkanen at tkk.fi)
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
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.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 #define IRRECO_DEBUG_PREFIX "MYTH"
21 #include <irreco_util.h>
22 #include <irreco_backend_api.h>
29 #include <sys/types.h>
30 #include <sys/socket.h>
31 #include <netinet/in.h>
33 #include <arpa/inet.h>
37 /* This is used elsewhere too */
38 #define _(String) (String)
49 MYTH_BACKEND_ERROR_CONFIG_READ
= 1,
50 MYTH_BACKEND_ERROR_CONFIG_WRITE
,
51 MYTH_BACKEND_ERROR_CONNECT
,
52 MYTH_BACKEND_ERROR_CON_WRITE
57 MYTH_BACKEND_DEVICE_GLOBAL
,
58 MYTH_BACKEND_DEVICE_VIEWING
61 const char *myth_backend_get_error_msg(MythBackend
*self
,
62 IrrecoBackendStatus code
)
66 case MYTH_BACKEND_ERROR_CONFIG_READ
:
67 return _("Couldn't read configuration");
68 case MYTH_BACKEND_ERROR_CONFIG_WRITE
:
69 return _("Couldn't write configuration");
70 case MYTH_BACKEND_ERROR_CONNECT
:
71 return _("Couldn't connect to remote system");
72 case MYTH_BACKEND_ERROR_CON_WRITE
:
73 return _("Error while sending data to remote system");
77 return _("Unknown error");
80 void *myth_backend_create()
84 self
= g_slice_new0(MythBackend
);
85 self
->host
= g_string_new("localhost");
92 void myth_backend_disconnect(MythBackend
*self
)
96 g_io_channel_shutdown(self
->con
, TRUE
, NULL
);
97 g_io_channel_unref(self
->con
);
102 void myth_backend_destroy(MythBackend
*self
)
104 myth_backend_disconnect(self
);
105 g_string_free(self
->host
, TRUE
);
106 g_slice_free(MythBackend
, self
);
109 #define MYTH_BACKEND_CONFIG_GROUP "myth"
112 myth_backend_read_from_conf(MythBackend
*self
,
113 const char *config_file
)
118 IrrecoKeyFile
*keyfile
= NULL
;
120 keyfile
= irreco_keyfile_create(
121 g_path_get_dirname(config_file
),
123 MYTH_BACKEND_CONFIG_GROUP
);
125 retval
= MYTH_BACKEND_ERROR_CONFIG_READ
;
127 if(keyfile
== NULL
) goto cleanup
;
128 if(!irreco_keyfile_get_int(keyfile
, "port", &port
)) goto cleanup
;
129 if(!irreco_keyfile_get_str(keyfile
, "host", &host
)) goto cleanup
;
133 g_string_set_size(self
->host
, 0);
134 g_string_append(self
->host
, host
);
135 retval
= IRRECO_BACKEND_OK
;
138 irreco_keyfile_destroy(keyfile
);
145 myth_backend_save_to_conf(MythBackend
*self
,
146 const char *config_file
)
148 GKeyFile
*keyfile
= NULL
;
149 gchar
*grp
= MYTH_BACKEND_CONFIG_GROUP
;
151 keyfile
= g_key_file_new();
152 g_key_file_set_string(keyfile
, grp
, "host", self
->host
->str
);
153 g_key_file_set_integer(keyfile
, grp
, "port", self
->port
);
155 if(!irreco_write_keyfile(keyfile
, config_file
))
156 return MYTH_BACKEND_ERROR_CONFIG_WRITE
;
158 return IRRECO_BACKEND_OK
;
162 myth_backend_get_devices(MythBackend
*self
,
163 IrrecoGetDeviceCallback callback
)
165 callback("Global", (void *)MYTH_BACKEND_DEVICE_GLOBAL
);
166 callback("Viewing", (void *)MYTH_BACKEND_DEVICE_VIEWING
);
167 return IRRECO_BACKEND_OK
;
172 myth_backend_get_commands(MythBackend
*self
,
173 const char *device_name
,
174 MythBackendDevice device_context
,
175 IrrecoGetCommandCallback callback
)
177 switch(device_context
)
179 case MYTH_BACKEND_DEVICE_GLOBAL
:
180 callback("Enter", "key enter");
181 callback("Escape", "key escape");
182 callback("Up", "key up");
183 callback("Right", "key right");
184 callback("Down", "key down");
185 callback("Left", "key left");
186 callback("TV", "jump livetv");
187 callback("Main Menu","jump mainmenu");
188 callback("Video", "jump mythvideo");
189 callback("DVD", "jump playdvd");
190 callback("Channel Up", "play channel up");
191 callback("Channel Down", "play channel down");
193 case MYTH_BACKEND_DEVICE_VIEWING
:
194 callback("Aspect ratio", "key w");
195 callback("OSD Menu", "key m");
196 callback("Volume down", "key f10");
197 callback("Volume up", "key f11");
198 callback("Change channel Up", "play channel up");
199 callback("Change channel Down", "play channel down");
200 callback("Seek to beginning", "play seek beginning");
201 callback("Seek forward", "play seek forward");
202 callback("Seek backwards", "play seek backward");
203 callback("Pause", "play speed pause");
204 callback("Play", "play speed normal");
205 callback("Playback at normal speed", "play speed 1x");
206 callback("Playback at reverse", "play speed -1x");
207 callback("Playback at 1/16x speed", "play speed 1/16x");
208 callback("Playback at 1/8x speed", "play speed 1/8x");
209 callback("Playback at 1/4x speed", "play speed 1/4x");
210 callback("Playback at 1/2x speed", "play speed 1/2x");
211 callback("Playback at 2x speed", "play speed 2x");
212 callback("Playback at 4x speed", "play speed 4x");
213 callback("Playback at 8x speed", "play speed 8x");
214 callback("Playback at 16x speed", "play speed 16x");
215 callback("Stop", "play stop");
219 return IRRECO_BACKEND_OK
;
223 myth_backend_connection_error(MythBackend
*self
, GError
*error
)
225 /* TODO: See if connection has died? */
226 IRRECO_PRINTF("Connection error occured, Killing connection");
227 myth_backend_disconnect(self
);
230 void myth_backend_connection_error_callback(GIOChannel
*source
,
231 GIOCondition cond
, MythBackend
*self
)
233 IRRECO_PRINTF("Connection error by callback");
234 /* TODO: Can GError be digged from the object? */
235 myth_backend_connection_error(self
, NULL
);
240 myth_backend_connect(MythBackend
*self
)
243 struct sockaddr_in addr
;
244 struct hostent
*host
;
246 IRRECO_PRINTF("Connecting to %s:%d", self
->host
->str
, self
->port
);
248 memset(&addr
, '\0', sizeof(addr
));
250 addr
.sin_family
= AF_INET
;
251 addr
.sin_port
= htons(self
->port
);
253 if(inet_aton(self
->host
->str
, &addr
.sin_addr
))
255 IRRECO_PRINTF("Address is IP");
257 else if((host
= gethostbyname(self
->host
->str
)))
259 IRRECO_PRINTF("Address is valid hostname");
260 memcpy((void *) &addr
.sin_addr
, (void *) host
->h_addr_list
[0],
261 (size_t) host
->h_length
);
266 IRRECO_PRINTF("Couldn't resolve address: %s", strerror(errno
));
267 return MYTH_BACKEND_ERROR_CONNECT
;
271 sock
= socket(PF_INET
, SOCK_STREAM
, 0);
275 IRRECO_PRINTF("Creating socket failed: %s", strerror(errno
));
276 return MYTH_BACKEND_ERROR_CONNECT
;
279 if(connect(sock
, (struct sockaddr
*) &addr
, sizeof(addr
)) < 0)
281 IRRECO_PRINTF("Connecting socket failed: %s", strerror(errno
));
282 return MYTH_BACKEND_ERROR_CONNECT
;
285 self
->con
= g_io_channel_unix_new(sock
);
287 g_io_add_watch(self
->con
, G_IO_ERR
,
288 (GIOFunc
) myth_backend_connection_error_callback
,
291 /* TODO: Should wait until the server responds */
293 return 0; /* IRRECO_BACKEND_OK */
298 myth_backend_ensure_connection(MythBackend
*self
)
302 /* TODO: Better checking? */
303 return 0; /* IRRECO_BACKEND_OK */
307 return myth_backend_connect(self
);
312 myth_backend_send_command(MythBackend
*self
,
313 const char *device_name
,
314 void *device_context
,
315 const char *command_name
,
316 void *command_context
)
322 GError
*error
= NULL
;
325 command
= g_string_new((gchar
*)command_context
);
326 g_string_append(command
, "\r\n");
328 IRRECO_PRINTF("In myth_backend_send_command");
330 if((constatus
= myth_backend_ensure_connection(self
)))
335 IRRECO_PRINTF("Connection ensured, starting write");
337 /* TODO: Should read incoming stuff */
339 for(total_written
= 0; total_written
< command
->len
; total_written
+= written
)
341 /* TODO: Better error reporting */
342 status
= g_io_channel_write_chars(self
->con
, &(command
->str
[total_written
]), -1, &written
, &error
);
343 if(status
== G_IO_STATUS_ERROR
)
345 IRRECO_PRINTF("Failed writing to socket: %s", error
->message
);
346 myth_backend_connection_error(self
, error
);
347 return MYTH_BACKEND_ERROR_CON_WRITE
;
351 IRRECO_PRINTF("Command written. Flushing");
353 status
= g_io_channel_flush(self
->con
, NULL
);
355 /* TODO: Better handling for G_IO_STATUS_AGAIN ? */
358 case G_IO_STATUS_ERROR
:
359 case G_IO_STATUS_AGAIN
:
360 myth_backend_connection_error(self
, error
);
361 return MYTH_BACKEND_ERROR_CON_WRITE
;
365 IRRECO_PRINTF("Command sent successfully");
367 g_string_free(command
, TRUE
);
369 return IRRECO_BACKEND_OK
;
373 myth_backend_configure(MythBackend
*self
,
378 GtkEntry
*host_widget
;
379 GtkSpinButton
*port_widget
;
380 const gchar
*new_host
;
384 dialog
= GTK_DIALOG(gtk_dialog_new_with_buttons(
385 "MythTV configuration",
387 GTK_DIALOG_MODAL
| GTK_DIALOG_DESTROY_WITH_PARENT
,
388 GTK_STOCK_CANCEL
, GTK_RESPONSE_REJECT
,
389 GTK_STOCK_OK
, GTK_RESPONSE_ACCEPT
, NULL
));
391 table
= GTK_TABLE(gtk_table_new(2, 2, FALSE
));
393 host_widget
= GTK_ENTRY(gtk_entry_new());
394 gtk_entry_set_text(host_widget
, self
->host
->str
);
396 port_widget
= GTK_SPIN_BUTTON(
397 gtk_spin_button_new_with_range(0, 2<<15, 1));
398 gtk_spin_button_set_digits(port_widget
, 0);
399 gtk_spin_button_set_value(port_widget
, self
->port
);
401 gtk_table_attach_defaults(table
,
402 gtk_label_new(_("Host")), 0, 1, 0, 1);
403 gtk_table_attach_defaults(table
,
404 GTK_WIDGET(host_widget
), 1, 2, 0, 1);
406 gtk_table_attach_defaults(table
,
407 gtk_label_new(_("Port")), 0, 1, 1, 2);
408 gtk_table_attach_defaults(table
,
409 GTK_WIDGET(port_widget
), 1, 2, 1, 2);
411 gtk_container_add(GTK_CONTAINER(dialog
->vbox
), GTK_WIDGET(table
));
413 gtk_widget_show_all(GTK_WIDGET(dialog
));
415 if(gtk_dialog_run(dialog
) == GTK_RESPONSE_ACCEPT
)
417 new_host
= gtk_entry_get_text(host_widget
);
418 new_port
= gtk_spin_button_get_value_as_int(port_widget
);
420 if(strcmp(new_host
, self
->host
->str
) || new_port
!= self
->port
)
422 g_string_assign(self
->host
, new_host
);
423 self
->port
= new_port
;
424 IRRECO_PRINTF("New configuration, resetting connection");
425 myth_backend_disconnect(self
);
429 gtk_widget_destroy(GTK_WIDGET(dialog
));
431 return IRRECO_BACKEND_OK
;
434 IrrecoBackendFunctionTable myth_backend_function_table
=
436 IRRECO_BACKEND_API_VERSION
,
439 (IrrecoBackendGetErrorMsg
) myth_backend_get_error_msg
,
440 (IrrecoBackendCreate
) myth_backend_create
,
441 (IrrecoBackendDestroy
) myth_backend_destroy
,
442 (IrrecoBackendReadFromConf
) myth_backend_read_from_conf
,
443 (IrrecoBackendSaveToConf
) myth_backend_save_to_conf
,
444 (IrrecoBackendGetDevices
) myth_backend_get_devices
,
445 (IrrecoBackendGetCommands
) myth_backend_get_commands
,
446 (IrrecoBackendSendCommand
) myth_backend_send_command
,
447 (IrrecoBackendConfigure
) myth_backend_configure
,
451 IrrecoBackendFunctionTable
*get_irreco_backend_function_table()
453 return &myth_backend_function_table
;