Add support for SPICE.
[virt-click.git] / spice.c
1 /*
2    Copyright (C) 2010 Red Hat, Inc.
3
4    This library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public
6    License as published by the Free Software Foundation; either
7    version 2.1 of the License, or (at your option) any later version.
8
9    This library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Lesser General Public License for more details.
13
14    You should have received a copy of the GNU Lesser General Public
15    License along with this library; if not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #ifdef HAVE_CONFIG_H
19 # include "config.h"
20 #endif
21
22 #include <stdio.h>
23 #include <stdlib.h>
24
25 #include <glib/gi18n.h>
26
27 #include <spice-client.h>
28 #include <spice-util.h>
29 #include <channel-display.h>
30
31 #include "click.h"
32
33 static void shutdown (self_t *self);
34 static int click (struct self_t *self, int x, int y, int b);
35
36 static char *host;
37 static char *port;
38 static char *tls_port;
39 static char *password;
40 static char *uri;
41 static char *ca_file;
42 static char *host_subject;
43 static char *smartcard_db;
44 static char *smartcard_certificates;
45 static gboolean smartcard = FALSE;
46
47 static GOptionEntry spice_entries[] = {
48     {
49         .long_name        = "spice-uri",
50         .arg              = G_OPTION_ARG_STRING,
51         .arg_data         = &uri,
52         .description      = N_("Spice server uri"),
53         .arg_description  = N_("<uri>"),
54     },{
55         .long_name        = "spice-host",
56         .arg              = G_OPTION_ARG_STRING,
57         .arg_data         = &host,
58         .description      = N_("Spice server address"),
59         .arg_description  = N_("<host>"),
60     },{
61         .long_name        = "spice-port",
62         .arg              = G_OPTION_ARG_STRING,
63         .arg_data         = &port,
64         .description      = N_("Spice server port"),
65         .arg_description  = N_("<port>"),
66     },{
67         .long_name        = "spice-secure-port",
68         .arg              = G_OPTION_ARG_STRING,
69         .arg_data         = &tls_port,
70         .description      = N_("Spice server secure port"),
71         .arg_description  = N_("<port>"),
72     },{
73         .long_name        = "spice-ca-file",
74         .arg              = G_OPTION_ARG_FILENAME,
75         .arg_data         = &ca_file,
76         .description      = N_("Truststore file for secure connections"),
77         .arg_description  = N_("<file>"),
78     },{
79         .long_name        = "spice-password",
80         .arg              = G_OPTION_ARG_STRING,
81         .arg_data         = &password,
82         .description      = N_("Server password"),
83         .arg_description  = N_("<password>"),
84     },{
85         .long_name        = "spice-host-subject",
86         .arg              = G_OPTION_ARG_STRING,
87         .arg_data         = &host_subject,
88         .description      = N_("Subject of the host certificate (field=value pairs separated by commas)"),
89         .arg_description  = N_("<host-subject>"),
90     },{
91         /* end of list */
92     }
93 };
94
95 static GOptionGroup *spice_group;
96
97 GOptionGroup *
98 vc_spice_cmdline_get_option_group(void)
99 {
100     if (spice_group == NULL) {
101         spice_group = g_option_group_new("spice",
102                                          _("Spice Options:"),
103                                          _("Show spice Options"),
104                                          NULL, NULL);
105         g_option_group_add_entries(spice_group, spice_entries);
106     }
107     return spice_group;
108 }
109
110 static void
111 primary_create (SpiceChannel *channel, gint format,
112                 gint width, gint height, gint stride,
113                 gint shmid, gpointer imgdata, gpointer opaque)
114 {
115   self_t *self = opaque;
116
117   if (verbose)
118     fprintf (stderr, "Connected to spice server (primary display channel)\n");
119
120   /* Remember that we managed to connect. */
121   self->connected = TRUE;
122
123   vc_issue_command (self);
124 }
125
126 static void
127 channel_new (SpiceSession *s, SpiceChannel *channel, gpointer opaque)
128 {
129   self_t *self = opaque;
130
131   if (SPICE_IS_INPUTS_CHANNEL (channel))
132     self->inputs_channel = channel;
133   if (SPICE_IS_DISPLAY_CHANNEL (channel))
134     self->display_channel = channel;
135
136   if (SPICE_IS_DISPLAY_CHANNEL (channel))
137     g_signal_connect (channel, "display-primary-create",
138                       G_CALLBACK (primary_create), self);
139   spice_channel_connect (channel);
140 }
141
142 static callbacks_t callbacks = {
143   .click = click,
144   .shutdown = shutdown,
145 };
146
147 void
148 vc_spice_setup (self_t *self)
149 {
150   if (verbose)
151     spice_util_set_debug (TRUE);
152
153   self->callbacks = &callbacks;
154
155   self->session = spice_session_new ();
156   g_signal_connect (self->session, "channel-new",
157                     G_CALLBACK(channel_new), self);
158
159   if (ca_file == NULL) {
160     const char *homedir = g_getenv("HOME");
161     if (!homedir)
162       homedir = g_get_home_dir();
163     ca_file = g_strdup_printf("%s/.spicec/spice_truststore.pem", homedir);
164   }
165
166   if (uri)
167     g_object_set(self->session, "uri", uri, NULL);
168   if (host)
169     g_object_set(self->session, "host", host, NULL);
170   if (port)
171     g_object_set(self->session, "port", port, NULL);
172   if (tls_port)
173     g_object_set(self->session, "tls-port", tls_port, NULL);
174   if (password)
175     g_object_set(self->session, "password", password, NULL);
176   if (ca_file)
177     g_object_set(self->session, "ca-file", ca_file, NULL);
178   if (host_subject)
179     g_object_set(self->session, "cert-subject", host_subject, NULL);
180
181   if (!spice_session_connect (self->session)) {
182     fprintf (stderr, _("spice: session connection failed\n"));
183     exit (EXIT_FAILURE);
184   }
185 }
186
187 gboolean
188 vc_spice_is_selected (void)
189 {
190   return uri != NULL || host != NULL;
191 }
192
193 static int
194 get_channel_id (SpiceChannel *channel)
195 {
196   int id;
197   g_object_get (channel, "channel-id", &id, NULL);
198   return id;
199 }
200
201
202 static void
203 shutdown (self_t *self)
204 {
205   spice_session_disconnect (self->session);
206   g_object_unref (self->session);
207 }
208
209 static int
210 click (struct self_t *self, int x, int y, int b)
211 {
212   int id = get_channel_id (self->display_channel);
213
214   spice_inputs_position (SPICE_INPUTS_CHANNEL (self->inputs_channel),
215                          x, y, id, 0);
216
217   switch (b) {
218   case 0: b = 0; break;
219   case 1: b = SPICE_MOUSE_BUTTON_LEFT; break;
220   case 2: b = SPICE_MOUSE_BUTTON_MIDDLE; break;
221   case 3: b = SPICE_MOUSE_BUTTON_RIGHT; break;
222   case 4: b = SPICE_MOUSE_BUTTON_UP; break;
223   case 5: b = SPICE_MOUSE_BUTTON_DOWN; break;
224   default:
225     fprintf (stderr, "spice: button %d is not supported\n", b);
226     return -1;
227   }
228
229   if (b)
230     spice_inputs_button_press (SPICE_INPUTS_CHANNEL (self->inputs_channel),
231                                b, 0);
232
233   return 0;
234 }