Modified theme editing.
[irreco.git] / backend / mythtv / myth_backend.c
blobb254d444b5b2d9ab7fa2f9dfefe3ec95edfa2d79
1 /*
2 * Irreco MythTV backend
3 * Copyright (C) 2007 Jami Pekkanen (jami.pekkanen at tkk.fi)
4 *
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.
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.
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>
24 #include <unistd.h>
25 #include <malloc.h>
26 #include <errno.h>
27 #include <string.h>
29 #include <sys/types.h>
30 #include <sys/socket.h>
31 #include <netinet/in.h>
32 #include <netdb.h>
33 #include <arpa/inet.h>
35 #include <gtk/gtk.h>
37 /* This is used elsewhere too */
38 #define _(String) (String)
40 typedef struct
42 GString *host;
43 gint port;
44 GIOChannel *con;
45 } MythBackend;
47 typedef enum
49 MYTH_BACKEND_ERROR_CONFIG_READ = 1,
50 MYTH_BACKEND_ERROR_CONFIG_WRITE,
51 MYTH_BACKEND_ERROR_CONNECT,
52 MYTH_BACKEND_ERROR_CON_WRITE
53 } MythBackendError;
55 typedef enum
57 MYTH_BACKEND_DEVICE_GLOBAL,
58 MYTH_BACKEND_DEVICE_VIEWING
59 } MythBackendDevice;
61 const char *myth_backend_get_error_msg(MythBackend *self,
62 IrrecoBackendStatus code)
64 switch(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");
74 default: break;
77 return _("Unknown error");
80 void *myth_backend_create()
82 MythBackend *self;
84 self = g_slice_new0(MythBackend);
85 self->host = g_string_new("localhost");
86 self->port = 6546;
87 self->con = NULL;
89 return self;
92 void myth_backend_disconnect(MythBackend *self)
94 if(self->con)
96 g_io_channel_shutdown(self->con, TRUE, NULL);
97 g_io_channel_unref(self->con);
98 self->con = NULL;
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"
111 IrrecoBackendStatus
112 myth_backend_read_from_conf(MythBackend *self,
113 const char *config_file)
115 gint port;
116 gint retval;
117 gchar *host = NULL;
118 IrrecoKeyFile *keyfile = NULL;
120 keyfile = irreco_keyfile_create(
121 g_path_get_dirname(config_file),
122 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;
132 self->port = port;
133 g_string_set_size(self->host, 0);
134 g_string_append(self->host, host);
135 retval = IRRECO_BACKEND_OK;
137 cleanup:
138 irreco_keyfile_destroy(keyfile);
139 g_free(host);
141 return retval;
144 IrrecoBackendStatus
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;
161 IrrecoBackendStatus
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;
171 IrrecoBackendStatus
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");
192 break;
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");
216 break;
219 return IRRECO_BACKEND_OK;
222 void
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);
239 IrrecoBackendStatus
240 myth_backend_connect(MythBackend *self)
242 int sock;
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);
264 else
266 IRRECO_PRINTF("Couldn't resolve address: %s", strerror(errno));
267 return MYTH_BACKEND_ERROR_CONNECT;
271 sock = socket(PF_INET, SOCK_STREAM, 0);
273 if(sock < 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,
289 self);
291 /* TODO: Should wait until the server responds */
293 return 0; /* IRRECO_BACKEND_OK */
297 IrrecoBackendStatus
298 myth_backend_ensure_connection(MythBackend *self)
300 if(self->con)
302 /* TODO: Better checking? */
303 return 0; /* IRRECO_BACKEND_OK */
305 else
307 return myth_backend_connect(self);
311 IrrecoBackendStatus
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)
318 gsize total_written;
319 gsize written = 0;
320 GString *command;
321 GIOStatus status;
322 GError *error = NULL;
323 int constatus;
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)))
332 return constatus;
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 ? */
356 switch(status)
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;
362 default: break;
365 IRRECO_PRINTF("Command sent successfully");
367 g_string_free(command, TRUE);
369 return IRRECO_BACKEND_OK;
372 IrrecoBackendStatus
373 myth_backend_configure(MythBackend *self,
374 GtkWindow *parent)
376 GtkDialog *dialog;
377 GtkTable *table;
378 GtkEntry *host_widget;
379 GtkSpinButton *port_widget;
380 const gchar *new_host;
381 gint new_port;
384 dialog = GTK_DIALOG(gtk_dialog_new_with_buttons(
385 "MythTV configuration",
386 parent,
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,
438 "MythTV Frontend",
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,
448 NULL, NULL, NULL
451 IrrecoBackendFunctionTable *get_irreco_backend_function_table()
453 return &myth_backend_function_table;