summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordiscomfitor <michael.blumenkrantz@gmail.com>2013-09-08 11:08:09 +0100
committerdiscomfitor <michael.blumenkrantz@gmail.com>2013-09-08 11:09:10 +0100
commit7d1ccef234bd99719a977d08928fed61e20b4375 (patch)
tree8f5b7c32f975cc0adaaf9456351bc27c985fbec1
first commit
-rw-r--r--AUTHORS1
-rw-r--r--COPYING23
-rw-r--r--Makefile.am30
-rw-r--r--README0
-rw-r--r--configure.ac106
-rw-r--r--m4/efl_compiler_flag.m457
-rw-r--r--src/bin/Makefile.mk15
-rw-r--r--src/bin/demo.c44
-rw-r--r--src/lib/Burrito.h12
-rw-r--r--src/lib/Makefile.mk11
-rw-r--r--src/lib/burrito.c1071
11 files changed, 1370 insertions, 0 deletions
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..94c81a0
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1 @@
Mike Blumenkrantz <michael.blumenkrantz@gmail.com>
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..2593752
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,23 @@
1Copyright (C) 2013 Mike Blumenkrantz and various contributors (see AUTHORS)
2
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, are permitted provided that the following conditions are met:
7
8 1. Redistributions of source code must retain the above copyright
9 notice, this list of conditions and the following disclaimer.
10 2. Redistributions in binary form must reproduce the above copyright
11 notice, this list of conditions and the following disclaimer in the
12 documentation and/or other materials provided with the distribution.
13
14THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
15INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
16FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
17COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
18INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
19LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
20OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
21LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
22NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
23EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..c1d3650
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,30 @@
1ACLOCAL_AMFLAGS = -I m4
2AUTOMAKE_OPTIONS = subdir-objects
3
4EXTRA_DIST = \
5AUTHORS \
6COPYING
7
8bin_PROGRAMS =
9DISTCLEANFILES =
10BUILT_SOURCES =
11MAINTAINERCLEANFILES = \
12Makefile.in \
13aclocal.m4 \
14config.guess \
15config.h* \
16config.sub \
17configure \
18depcomp \
19install-sh \
20ltmain.sh \
21missing \
22compile \
23m4/l* \
24ylwrap
25
26include src/bin/Makefile.mk
27include src/lib/Makefile.mk
28
29maintainer-clean-local:
30 rm -rf autom4te.cache
diff --git a/README b/README
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/README
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..25a973f
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,106 @@
1##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##
2##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##
3m4_define([v_maj], [0])
4m4_define([v_min], [0])
5m4_define([v_mic], [1])
6m4_define([v_rev], m4_esyscmd([(git rev-list --count HEAD 2>/dev/null || echo 0) | tr -d '\n']))dnl
7##-- When released, remove the dnl on the below line
8dnl m4_undefine([v_rev])
9##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##
10m4_ifdef([v_rev], [m4_define([v_ver], [v_maj.v_min.v_mic.v_rev])],
11[m4_define([v_ver], [v_maj.v_min.v_mic])])
12m4_define([lt_rev], m4_eval(v_maj + v_min))
13m4_define([lt_cur], v_mic)
14m4_define([lt_age], v_min)
15##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##
16##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##
17
18AC_INIT([burrito], [v_ver], [discomfitor@efl.so], [burrito])
19AC_CONFIG_SRCDIR([Makefile.am])
20AC_CONFIG_MACRO_DIR([m4])
21AC_CONFIG_HEADER([config.h])
22
23AC_GNU_SOURCE
24AC_ISC_POSIX
25
26##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##
27##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##
28m4_ifdef([v_rev], , [m4_define([v_rev], [0])])
29m4_ifdef([v_rel], , [m4_define([v_rel], [])])
30AC_DEFINE_UNQUOTED(VMAJ, [v_maj], [Major version])
31AC_DEFINE_UNQUOTED(VMIN, [v_min], [Minor version])
32AC_DEFINE_UNQUOTED(VMIC, [v_mic], [Micro version])
33AC_DEFINE_UNQUOTED(VREV, [v_rev], [Revison])
34version_info="lt_rev:lt_cur:lt_age"
35release_info="v_rel"
36AC_SUBST(version_info)
37AC_SUBST(release_info)
38##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##
39##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##--##
40VMAJ=v_maj
41AC_SUBST(VMAJ)
42
43AM_INIT_AUTOMAKE([foreign])
44m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
45
46AC_PROG_LIBTOOL
47AC_PROG_INSTALL
48AC_PROG_CC
49AM_PROG_CC_C_O
50AC_PROG_MAKE_SET
51AC_HEADER_STDC
52
53AC_C_CONST
54AC_C_INLINE
55AC_TYPE_SIZE_T
56
57AC_FUNC_ALLOCA
58AC_CHECK_FUNCS([strdup strndup])
59
60PKG_PROG_PKG_CONFIG
61
62PKG_CHECK_MODULES([EFL], [ecore-evas])
63PKG_CHECK_MODULES([SPICE], [spice-client-gtk-2.0 ecore])
64
65if test -z "$($PKG_CONFIG --variable=evas spice-client-gtk-2.0)" ; then
66 AC_MSG_ERROR([need spice-client-gtk-2.0 compiled with evas support])
67fi
68
69##############################
70
71m4_ifdef([v_rev],
72 [
73 EFL_COMPILER_FLAG([-Wshadow])
74 EFL_COMPILER_FLAG([-Wall])
75 EFL_COMPILER_FLAG([-Wextra])
76 ])
77
78AC_CONFIG_FILES([
79 Makefile
80])
81AC_OUTPUT
82
83# report
84txt_strip() {
85 echo "[$]@" | sed -e 's/^[[ \t]]*\([[^ \t]]*\)[[ \t]]*$/\1/g'
86}
87
88echo
89cat << SUMMARY_EOF
90Summary:
91 * project.........: $PACKAGE $VERSION
92 * prefix..........: $(txt_strip $prefix)
93 * CFLAGS..........: $(txt_strip $CFLAGS)
94 * LDFLAGS.........: $(txt_strip $LDFLAGS)
95SUMMARY_EOF
96echo
97
98cat << COMPILE_EOF
99Compilation........: make (or gmake)
100COMPILE_EOF
101echo
102
103cat << INSTALL_EOF
104Installation.......: make all install (as root if needed, with 'su' or 'sudo')
105INSTALL_EOF
106echo
diff --git a/m4/efl_compiler_flag.m4 b/m4/efl_compiler_flag.m4
new file mode 100644
index 0000000..25c285d
--- /dev/null
+++ b/m4/efl_compiler_flag.m4
@@ -0,0 +1,57 @@
1dnl Copyright (C) 2010 Vincent Torri <vtorri at univ-evry dot fr>
2dnl and Albin Tonnerre <albin dot tonnerre at gmail dot com>
3dnl That code is public domain and can be freely used or copied.
4
5dnl Macro that checks if a compiler flag is supported by the compiler.
6
7dnl Usage: EFL_COMPILER_FLAG(flag)
8dnl flag is added to CFLAGS if supported.
9
10AC_DEFUN([EFL_COMPILER_FLAG],
11[
12
13CFLAGS_save="${CFLAGS}"
14CFLAGS="${CFLAGS} $1"
15
16AC_LANG_PUSH([C])
17AC_MSG_CHECKING([whether the compiler supports $1])
18
19AC_COMPILE_IFELSE(
20 [AC_LANG_PROGRAM([[]])],
21 [have_flag="yes"],
22 [have_flag="no"])
23AC_MSG_RESULT([${have_flag}])
24
25if test "x${have_flag}" = "xno" ; then
26 CFLAGS="${CFLAGS_save}"
27fi
28AC_LANG_POP([C])
29
30])
31
32dnl Macro that checks if a linker flag is supported by the compiler.
33
34dnl Usage: EFL_LINKER_FLAG(flag)
35dnl flag is added to LDFLAGS if supported (will be passed to ld anyway).
36
37AC_DEFUN([EFL_LINKER_FLAG],
38[
39
40LDFLAGS_save="${LDFLAGS}"
41LDFLAGS="${LDFLAGS} $1"
42
43AC_LANG_PUSH([C])
44AC_MSG_CHECKING([whether the compiler supports $1])
45
46AC_LINK_IFELSE(
47 [AC_LANG_PROGRAM([[]])],
48 [have_flag="yes"],
49 [have_flag="no"])
50AC_MSG_RESULT([${have_flag}])
51
52if test "x${have_flag}" = "xno" ; then
53 LDFLAGS="${LDFLAGS_save}"
54fi
55AC_LANG_POP([C])
56
57])
diff --git a/src/bin/Makefile.mk b/src/bin/Makefile.mk
new file mode 100644
index 0000000..d4650a8
--- /dev/null
+++ b/src/bin/Makefile.mk
@@ -0,0 +1,15 @@
1bin_PROGRAMS += src/bin/demo
2
3src_bin_demo_SOURCES =\
4src/bin/demo.c
5
6src_bin_demo_CPPFLAGS =\
7-I$(top_builddir) \
8-I$(top_srcdir)/src/lib \
9@SPICE_CFLAGS@ \
10@EFL_CFLAGS@
11
12src_bin_demo_LDADD =\
13@SPICE_LIBS@ \
14@EFL_LIBS@ \
15$(top_builddir)/src/lib/libburrito.la
diff --git a/src/bin/demo.c b/src/bin/demo.c
new file mode 100644
index 0000000..a15d80d
--- /dev/null
+++ b/src/bin/demo.c
@@ -0,0 +1,44 @@
1#include "Burrito.h"
2#include <Ecore.h>
3#include <Ecore_Evas.h>
4
5static Evas_Object *burrito = NULL;
6
7static void
8delete_cb(Ecore_Evas *ee)
9{
10 ecore_evas_free(ee);
11 ecore_main_loop_quit();
12}
13
14static void
15resize_cb(Ecore_Evas *ee)
16{
17 int w, h;
18
19 ecore_evas_geometry_get(ee, NULL, NULL, &w, &h);
20 evas_object_resize(burrito, w, h);
21}
22
23int
24main(int argc, const char *argv[])
25{
26 Ecore_Evas *ee;
27
28 ecore_init();
29 ecore_evas_init();
30
31 ecore_app_args_set(argc, argv);
32 ee = ecore_evas_software_x11_new(NULL, 0, 0, 0, 800, 600);
33 ecore_evas_name_class_set(ee, "burrito", "display");
34 ecore_evas_callback_delete_request_set(ee, delete_cb);
35 ecore_evas_callback_resize_set(ee, resize_cb);
36
37 burrito = burrito_add(ecore_evas_get(ee), NULL, argv[1], strtoul(argv[2], NULL, 10), 0, NULL);
38 evas_object_resize(burrito, 800, 600);
39 evas_object_show(burrito);
40
41 ecore_evas_show(ee);
42 ecore_main_loop_begin();
43 return 0;
44}
diff --git a/src/lib/Burrito.h b/src/lib/Burrito.h
new file mode 100644
index 0000000..6604850
--- /dev/null
+++ b/src/lib/Burrito.h
@@ -0,0 +1,12 @@
1#ifndef BURRITO_H
2# define BURRITO_H
3
4#ifdef HAVE_CONFIG_H
5# include "config.h"
6#endif
7
8#include <Evas.h>
9
10Evas_Object *burrito_add(Evas *e, const char *uri, const char *host, unsigned int port, unsigned int tls_port, const char *password);
11
12#endif
diff --git a/src/lib/Makefile.mk b/src/lib/Makefile.mk
new file mode 100644
index 0000000..ef68f73
--- /dev/null
+++ b/src/lib/Makefile.mk
@@ -0,0 +1,11 @@
1lib_LTLIBRARIES = src/lib/libburrito.la
2
3src_lib_libburrito_la_CPPFLAGS =\
4-I$(top_builddir) \
5@SPICE_CFLAGS@
6
7src_lib_libburrito_la_LIBADD =\
8@SPICE_LIBS@
9
10src_lib_libburrito_la_SOURCES =\
11src/lib/burrito.c
diff --git a/src/lib/burrito.c b/src/lib/burrito.c
new file mode 100644
index 0000000..6c2fb29
--- /dev/null
+++ b/src/lib/burrito.c
@@ -0,0 +1,1071 @@
1#include "Burrito.h"
2#include <Ecore.h>
3
4#include <gtk/gtk.h>
5
6/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
7/*
8 Copyright (C) 2010-2011 Red Hat, Inc.
9
10 This library is free software; you can redistribute it and/or
11 modify it under the terms of the GNU Lesser General Public
12 License as published by the Free Software Foundation; either
13 version 2.1 of the License, or (at your option) any later version.
14
15 This library is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 Lesser General Public License for more details.
19
20 You should have received a copy of the GNU Lesser General Public
21 License along with this library; if not, see <http://www.gnu.org/licenses/>.
22*/
23
24#define _(X) X
25#define N_(X) X
26
27#include <stdbool.h>
28#include <sys/stat.h>
29#ifdef HAVE_TERMIOS_H
30#include <termios.h>
31#endif
32
33#ifdef USE_SMARTCARD
34#include <vreader.h>
35#include <smartcard-manager.h>
36#endif
37
38#include <spice-widget.h>
39#include <spice-gtk-session.h>
40#include <spice-audio.h>
41#include <spice/macros.h>
42#include <spice-option.h>
43#include <usb-device-widget.h>
44
45typedef struct spice_connection spice_connection;
46
47enum {
48 STATE_SCROLL_LOCK,
49 STATE_CAPS_LOCK,
50 STATE_NUM_LOCK,
51 STATE_MAX,
52};
53
54#define SPICE_TYPE_WINDOW (spice_window_get_type ())
55#define SPICE_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SPICE_TYPE_WINDOW, SpiceWindow))
56#define SPICE_IS_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SPICE_TYPE_WINDOW))
57#define SPICE_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SPICE_TYPE_WINDOW, SpiceWindowClass))
58#define SPICE_IS_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SPICE_TYPE_WINDOW))
59#define SPICE_WINDOW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SPICE_TYPE_WINDOW, SpiceWindowClass))
60
61typedef struct _SpiceWindow SpiceWindow;
62typedef struct _SpiceWindowClass SpiceWindowClass;
63
64struct _SpiceWindow {
65 GtkWindow parent_instance;
66 spice_connection *conn;
67 Evas_Object *image;
68 gint id;
69 gint monitor_id;
70 GtkWidget *spice;
71 GtkWidget *ritem, *rmenu;
72 bool mouse_grabbed;
73 SpiceChannel *display_channel;
74#ifdef WIN32
75 gint win_x;
76 gint win_y;
77#endif
78 bool enable_accels_save;
79 bool enable_mnemonics_save;
80};
81
82struct _SpiceWindowClass
83{
84 GtkWindowClass parent_class;
85 GtkWindow *window;
86};
87
88G_DEFINE_TYPE (SpiceWindow, spice_window, GTK_TYPE_WINDOW);
89
90#define CHANNELID_MAX 4
91#define MONITORID_MAX 4
92
93// FIXME: turn this into an object, get rid of fixed wins array, use
94// signals to replace the various callback that iterate over wins array
95struct spice_connection {
96 SpiceSession *session;
97 SpiceGtkSession *gtk_session;
98 SpiceMainChannel *main;
99 Evas_Object *smart_obj;
100 SpiceWindow *wins[CHANNELID_MAX * MONITORID_MAX];
101 SpiceAudio *audio;
102 const char *mouse_state;
103 const char *agent_state;
104 gboolean agent_connected;
105 int channels;
106 int disconnecting;
107};
108
109#define INTERNAL_ENTRY Burrito* sd; sd = evas_object_smart_data_get(obj); if (!sd) return;
110
111typedef struct Burrito
112{
113 Evas_Object *obj;
114 Evas_Object *clip;
115 Evas_Object *image;
116 int w, h;
117
118 SpiceWindow *win;
119 spice_connection *conn;
120} Burrito;
121
122static spice_connection *connection_new(void);
123static void connection_connect(spice_connection *conn);
124static void connection_disconnect(spice_connection *conn);
125static void connection_destroy(spice_connection *conn);
126static void usb_connect_failed(GObject *object,
127 SpiceUsbDevice *device,
128 GError *error,
129 gpointer data);
130static gboolean is_gtk_session_property(const gchar *property);
131static void del_window(spice_connection *conn, SpiceWindow *win);
132
133static SpicePortChannel*stdin_port = NULL;
134
135static Evas_Smart *_burrito_smart = NULL;
136/* ------------------------------------------------------------------ */
137
138static int ask_user(GtkWidget *parent, char *title, char *message,
139 char *dest, int dlen, int hide)
140{
141 GtkWidget *dialog, *area, *label, *entry;
142 const char *txt;
143 int retval;
144
145 /* Create the widgets */
146 dialog = gtk_dialog_new_with_buttons(title,
147 parent ? GTK_WINDOW(parent) : NULL,
148 GTK_DIALOG_DESTROY_WITH_PARENT,
149 GTK_STOCK_OK,
150 GTK_RESPONSE_ACCEPT,
151 GTK_STOCK_CANCEL,
152 GTK_RESPONSE_REJECT,
153 NULL);
154 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
155 area = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
156
157 label = gtk_label_new(message);
158 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
159 gtk_box_pack_start(GTK_BOX(area), label, FALSE, FALSE, 5);
160
161 entry = gtk_entry_new();
162 gtk_entry_set_text(GTK_ENTRY(entry), dest);
163 gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
164 if (hide)
165 gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE);
166 gtk_box_pack_start(GTK_BOX(area), entry, FALSE, FALSE, 5);
167
168 /* show and wait for response */
169 gtk_widget_show_all(dialog);
170 switch (gtk_dialog_run(GTK_DIALOG(dialog))) {
171 case GTK_RESPONSE_ACCEPT:
172 txt = gtk_entry_get_text(GTK_ENTRY(entry));
173 snprintf(dest, dlen, "%s", txt);
174 retval = 0;
175 break;
176 default:
177 retval = -1;
178 break;
179 }
180 gtk_widget_destroy(dialog);
181 return retval;
182}
183
184/* ------------------------------------------------------------------ */
185
186static void update_status_window(SpiceWindow *win)
187{
188 gchar *status;
189
190 if (win == NULL)
191 return;
192
193 if (win->mouse_grabbed) {
194 SpiceGrabSequence *sequence = spice_display_get_grab_keys(SPICE_DISPLAY(win->spice));
195 gchar *seq = spice_grab_sequence_as_string(sequence);
196 status = g_strdup_printf(_("Use %s to ungrab mouse."), seq);
197 g_free(seq);
198 } else {
199 status = g_strdup_printf(_("mouse: %s, agent: %s"),
200 win->conn->mouse_state, win->conn->agent_state);
201 }
202
203 g_free(status);
204}
205
206static void update_status(struct spice_connection *conn)
207{
208 int i;
209
210 for (i = 0; i < SPICE_N_ELEMENTS(conn->wins); i++) {
211 if (conn->wins[i] == NULL)
212 continue;
213 update_status_window(conn->wins[i]);
214 }
215}
216
217static const char *spice_edit_properties[] = {
218 "CopyToGuest",
219 "PasteFromGuest",
220};
221
222static void menu_cb_connect(GtkAction *action, void *data)
223{
224 struct spice_connection *conn;
225
226 conn = connection_new();
227 connection_connect(conn);
228}
229
230static void menu_cb_close(GtkAction *action, void *data)
231{
232 SpiceWindow *win = data;
233
234 connection_disconnect(win->conn);
235}
236
237static void menu_cb_copy(GtkAction *action, void *data)
238{
239 SpiceWindow *win = data;
240
241 spice_gtk_session_copy_to_guest(win->conn->gtk_session);
242}
243
244static void menu_cb_paste(GtkAction *action, void *data)
245{
246 SpiceWindow *win = data;
247
248 spice_gtk_session_paste_from_guest(win->conn->gtk_session);
249}
250
251static void mouse_grab_cb(GtkWidget *widget, gint grabbed, gpointer data)
252{
253 SpiceWindow *win = data;
254
255 win->mouse_grabbed = grabbed;
256 update_status(win->conn);
257}
258
259static void keyboard_grab_cb(GtkWidget *widget, gint grabbed, gpointer data)
260{
261 SpiceWindow *win = data;
262 GtkSettings *settings = gtk_widget_get_settings (widget);
263
264 if (grabbed) {
265 /* disable mnemonics & accels */
266 g_object_get(settings,
267 "gtk-enable-accels", &win->enable_accels_save,
268 "gtk-enable-mnemonics", &win->enable_mnemonics_save,
269 NULL);
270 g_object_set(settings,
271 "gtk-enable-accels", FALSE,
272 "gtk-enable-mnemonics", FALSE,
273 NULL);
274 } else {
275 g_object_set(settings,
276 "gtk-enable-accels", win->enable_accels_save,
277 "gtk-enable-mnemonics", win->enable_mnemonics_save,
278 NULL);
279 }
280}
281
282static const char *spice_display_properties[] = {
283 "grab-keyboard",
284 "grab-mouse",
285 "resize-guest",
286 "scaling",
287 "disable-inputs",
288};
289
290static const char *spice_gtk_session_properties[] = {
291 "auto-clipboard",
292 "auto-usbredir",
293};
294
295static gboolean is_gtk_session_property(const gchar *property)
296{
297 int i;
298
299 for (i = 0; i < G_N_ELEMENTS(spice_gtk_session_properties); i++) {
300 if (!strcmp(spice_gtk_session_properties[i], property)) {
301 return TRUE;
302 }
303 }
304 return FALSE;
305}
306
307#ifndef WIN32
308static void recent_item_activated_cb(GtkRecentChooser *chooser, gpointer data)
309{
310 GtkRecentInfo *info;
311 struct spice_connection *conn;
312 const char *uri;
313
314 info = gtk_recent_chooser_get_current_item(chooser);
315
316 uri = gtk_recent_info_get_uri(info);
317 g_return_if_fail(uri != NULL);
318
319 conn = connection_new();
320 g_object_set(conn->session, "uri", uri, NULL);
321 gtk_recent_info_unref(info);
322 connection_connect(conn);
323}
324#endif
325
326static GObject *
327spice_window_constructor(GType gtype,
328 guint n_properties,
329 GObjectConstructParam *properties)
330{
331 /* Always chain up to the parent constructor */
332 GObjectClass *parent_class;
333 parent_class = G_OBJECT_CLASS(spice_window_parent_class);
334 return parent_class->constructor(gtype, n_properties, properties);
335}
336
337
338static void spice_window_dispose(GObject *obj)
339{
340 G_OBJECT_CLASS(spice_window_parent_class)->dispose(obj);
341}
342
343static void
344spice_window_realize(GtkWidget *widget)
345{
346 gtk_widget_set_realized(widget, TRUE);
347}
348
349static void
350spice_window_unrealize(GtkWidget *widget)
351{
352 gtk_widget_set_realized(widget, FALSE);
353}
354
355static void
356spice_window_class_init (SpiceWindowClass *klass)
357{
358 GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
359 GtkWidgetClass *gtkwidget_class = GTK_WIDGET_CLASS(klass);
360
361 gtkwidget_class->realize = spice_window_realize;
362 gtkwidget_class->unrealize = spice_window_unrealize;
363 gtkwidget_class->map = NULL;
364 gtkwidget_class->unmap = NULL;
365
366 gobject_class->constructor = spice_window_constructor;
367 gobject_class->dispose = spice_window_dispose;
368}
369
370static void
371spice_window_init(SpiceWindow *self)
372{
373
374}
375
376
377static SpiceWindow *create_spice_window(spice_connection *conn, SpiceChannel *channel, int id, gint monitor_id)
378{
379 char title[32];
380 SpiceWindow *win;
381 GtkAction *toggle;
382 gboolean state;
383 GtkWidget *vbox;
384 GtkWindow *window;
385 GError *err = NULL;
386 int i;
387 SpiceGrabSequence *seq;
388 Evas *e;
389 Burrito *sd;
390
391 win = g_object_new(SPICE_TYPE_WINDOW, NULL);
392 win->id = id;
393 win->monitor_id = monitor_id;
394 win->conn = conn;
395 win->display_channel = channel;
396 window = GTK_WINDOW(win);
397 window->need_default_size = window->need_default_position = FALSE;
398 sd = evas_object_smart_data_get(conn->smart_obj);
399
400 sd->win = win;
401 g_object_set_data(G_OBJECT(win), "evas_image", sd->image);
402
403
404 /* spice display */
405 win->spice = GTK_WIDGET(spice_display_new_with_monitor(conn->session, id, monitor_id));
406 seq = spice_grab_sequence_new_from_string("Shift_L+F12");
407 spice_display_set_grab_keys(SPICE_DISPLAY(win->spice), seq);
408 spice_grab_sequence_free(seq);
409
410 g_signal_connect(G_OBJECT(win->spice), "mouse-grab",
411 G_CALLBACK(mouse_grab_cb), win);
412 g_signal_connect(G_OBJECT(win->spice), "keyboard-grab",
413 G_CALLBACK(keyboard_grab_cb), win);
414
415#ifdef USE_SMARTCARD
416 gboolean smartcard;
417
418 enable_smartcard_actions(win, NULL, FALSE, FALSE);
419 g_object_get(G_OBJECT(conn->session),
420 "enable-smartcard", &smartcard,
421 NULL);
422 if (smartcard) {
423 g_signal_connect(G_OBJECT(spice_smartcard_manager_get()), "reader-added",
424 (GCallback)reader_added_cb, win);
425 g_signal_connect(G_OBJECT(spice_smartcard_manager_get()), "reader-removed",
426 (GCallback)reader_removed_cb, win);
427 g_signal_connect(G_OBJECT(spice_smartcard_manager_get()), "card-inserted",
428 (GCallback)card_inserted_cb, win);
429 g_signal_connect(G_OBJECT(spice_smartcard_manager_get()), "card-removed",
430 (GCallback)card_removed_cb, win);
431 }
432#endif
433#if GTK_CHECK_VERSION(3,0,0)
434 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 1);
435#else
436 vbox = gtk_vbox_new(FALSE, 1);
437#endif
438 gtk_container_set_border_width(GTK_CONTAINER(vbox), 0);
439 gtk_container_add(GTK_CONTAINER(win), vbox);
440 gtk_box_pack_start(GTK_BOX(vbox), win->spice, TRUE, TRUE, 0);
441 gtk_widget_show_all(vbox);
442
443 gtk_widget_grab_focus(win->spice);
444
445 return win;
446}
447
448static void destroy_spice_window(SpiceWindow *win)
449{
450 Burrito *sd;
451 if (win == NULL)
452 return;
453
454 sd = evas_object_smart_data_get(win->conn->smart_obj);
455 SPICE_DEBUG("destroy window (#%d:%d)", win->id, win->monitor_id);
456 g_object_unref(win);
457 sd->win = NULL;
458}
459
460/* ------------------------------------------------------------------ */
461
462static void recent_add(SpiceSession *session)
463{
464 GtkRecentManager *recent;
465 GtkRecentData meta = {
466 .mime_type = (char*)"application/x-spice",
467 .app_name = (char*)"spicy",
468 .app_exec = (char*)"spicy --uri=%u",
469 };
470 char *uri;
471
472 g_object_get(session, "uri", &uri, NULL);
473 SPICE_DEBUG("%s: %s", __FUNCTION__, uri);
474
475 g_return_if_fail(g_str_has_prefix(uri, "spice://"));
476
477 recent = gtk_recent_manager_get_default();
478 meta.display_name = uri + 8;
479 if (!gtk_recent_manager_add_full(recent, uri, &meta))
480 g_warning("Recent item couldn't be added successfully");
481
482 g_free(uri);
483}
484
485static void main_channel_event(SpiceChannel *channel, SpiceChannelEvent event,
486 gpointer data)
487{
488 spice_connection *conn = data;
489 char password[64];
490 int rc;
491
492 switch (event) {
493 case SPICE_CHANNEL_OPENED:
494 g_message("main channel: opened");
495 recent_add(conn->session);
496 break;
497 case SPICE_CHANNEL_SWITCHING:
498 g_message("main channel: switching host");
499 break;
500 case SPICE_CHANNEL_CLOSED:
501 /* this event is only sent if the channel was succesfully opened before */
502 g_message("main channel: closed");
503 connection_disconnect(conn);
504 break;
505 case SPICE_CHANNEL_ERROR_IO:
506 connection_disconnect(conn);
507 break;
508 case SPICE_CHANNEL_ERROR_TLS:
509 case SPICE_CHANNEL_ERROR_LINK:
510 case SPICE_CHANNEL_ERROR_CONNECT:
511 g_message("main channel: failed to connect");
512 break;
513 case SPICE_CHANNEL_ERROR_AUTH:
514 g_warning("main channel: auth failure (wrong password?)");
515 strcpy(password, "");
516 /* FIXME i18 */
517 rc = ask_user(NULL, _("Authentication"),
518 _("Please enter the spice server password"),
519 password, sizeof(password), true);
520 if (rc == 0) {
521 g_object_set(conn->session, "password", password, NULL);
522 connection_connect(conn);
523 } else {
524 connection_disconnect(conn);
525 }
526 break;
527 default:
528 /* TODO: more sophisticated error handling */
529 g_warning("unknown main channel event: %d", event);
530 /* connection_disconnect(conn); */
531 break;
532 }
533}
534
535static void main_mouse_update(SpiceChannel *channel, gpointer data)
536{
537 spice_connection *conn = data;
538 gint mode;
539
540 g_object_get(channel, "mouse-mode", &mode, NULL);
541 switch (mode) {
542 case SPICE_MOUSE_MODE_SERVER:
543 conn->mouse_state = "server";
544 break;
545 case SPICE_MOUSE_MODE_CLIENT:
546 conn->mouse_state = "client";
547 break;
548 default:
549 conn->mouse_state = "?";
550 break;
551 }
552 update_status(conn);
553}
554
555static void main_agent_update(SpiceChannel *channel, gpointer data)
556{
557 spice_connection *conn = data;
558
559 g_object_get(channel, "agent-connected", &conn->agent_connected, NULL);
560 conn->agent_state = conn->agent_connected ? _("yes") : _("no");
561 update_status(conn);
562}
563
564static void inputs_modifiers(SpiceChannel *channel, gpointer data)
565{
566 spice_connection *conn = data;
567 int m, i;
568
569 g_object_get(channel, "key-modifiers", &m, NULL);
570 for (i = 0; i < SPICE_N_ELEMENTS(conn->wins); i++) {
571 if (conn->wins[i] == NULL)
572 continue;
573
574 //gtk_label_set_text(GTK_LABEL(conn->wins[i]->st[STATE_SCROLL_LOCK]),
575 //m & SPICE_KEYBOARD_MODIFIER_FLAGS_SCROLL_LOCK ? _("SCROLL") : "");
576 //gtk_label_set_text(GTK_LABEL(conn->wins[i]->st[STATE_CAPS_LOCK]),
577 //m & SPICE_KEYBOARD_MODIFIER_FLAGS_CAPS_LOCK ? _("CAPS") : "");
578 //gtk_label_set_text(GTK_LABEL(conn->wins[i]->st[STATE_NUM_LOCK]),
579 //m & SPICE_KEYBOARD_MODIFIER_FLAGS_NUM_LOCK ? _("NUM") : "");
580 }
581}
582
583static SpiceWindow* get_window(spice_connection *conn, int channel_id, int monitor_id)
584{
585 g_return_val_if_fail(channel_id < CHANNELID_MAX, NULL);
586 g_return_val_if_fail(monitor_id < MONITORID_MAX, NULL);
587
588 return conn->wins[channel_id * CHANNELID_MAX + monitor_id];
589}
590
591static void add_window(spice_connection *conn, SpiceWindow *win)
592{
593 g_return_if_fail(win != NULL);
594 g_return_if_fail(win->id < CHANNELID_MAX);
595 g_return_if_fail(win->monitor_id < MONITORID_MAX);
596 g_return_if_fail(conn->wins[win->id * CHANNELID_MAX + win->monitor_id] == NULL);
597
598 SPICE_DEBUG("add display monitor %d:%d", win->id, win->monitor_id);
599 conn->wins[win->id * CHANNELID_MAX + win->monitor_id] = win;
600}
601
602static void del_window(spice_connection *conn, SpiceWindow *win)
603{
604 if (win == NULL)
605 return;
606
607 g_return_if_fail(win->id < CHANNELID_MAX);
608 g_return_if_fail(win->monitor_id < MONITORID_MAX);
609
610 g_debug("del display monitor %d:%d", win->id, win->monitor_id);
611 conn->wins[win->id * CHANNELID_MAX + win->monitor_id] = NULL;
612 if (win->id > 0)
613 spice_main_set_display_enabled(conn->main, win->id, FALSE);
614 else
615 spice_main_set_display_enabled(conn->main, win->monitor_id, FALSE);
616 spice_main_send_monitor_config(conn->main);
617
618 destroy_spice_window(win);
619}
620
621static void display_mark(SpiceChannel *channel, gint mark, SpiceWindow *win)
622{
623 g_return_if_fail(win != NULL);
624
625 if (mark == TRUE) {
626 gtk_widget_realize(GTK_WIDGET(win->spice));
627 } else {
628 gtk_widget_unrealize(GTK_WIDGET(win->spice));
629 }
630}
631
632static void display_monitors(SpiceChannel *display, GParamSpec *pspec,
633 spice_connection *conn)
634{
635 GArray *monitors = NULL;
636 int id;
637 guint i;
638
639 g_object_get(display,
640 "channel-id", &id,
641 "monitors", &monitors,
642 NULL);
643 g_return_if_fail(monitors != NULL);
644
645 for (i = 0; i < monitors->len; i++) {
646 SpiceWindow *w;
647
648 if (!get_window(conn, id, i)) {
649 w = create_spice_window(conn, display, id, i);
650 add_window(conn, w);
651 spice_g_signal_connect_object(display, "display-mark",
652 G_CALLBACK(display_mark), w, 0);
653 //update_auto_usbredir_sensitive(conn);
654 }
655 }
656
657 for (; i < MONITORID_MAX; i++)
658 del_window(conn, get_window(conn, id, i));
659
660 g_clear_pointer(&monitors, g_array_unref);
661}
662
663static void port_write_cb(GObject *source_object,
664 GAsyncResult *res,
665 gpointer user_data)
666{
667 SpicePortChannel *port = SPICE_PORT_CHANNEL(source_object);
668 GError *error = NULL;
669
670 spice_port_write_finish(port, res, &error);
671 if (error != NULL)
672 g_warning("%s", error->message);
673 g_clear_error(&error);
674}
675
676static void port_flushed_cb(GObject *source_object,
677 GAsyncResult *res,
678 gpointer user_data)
679{
680 SpiceChannel *channel = SPICE_CHANNEL(source_object);
681 GError *error = NULL;
682
683 spice_channel_flush_finish(channel, res, &error);
684 if (error != NULL)
685 g_warning("%s", error->message);
686 g_clear_error(&error);
687
688 spice_channel_disconnect(channel, SPICE_CHANNEL_CLOSED);
689}
690
691static gboolean input_cb(GIOChannel *gin, GIOCondition condition, gpointer data)
692{
693 char buf[4096];
694 gsize bytes_read;
695 GIOStatus status;
696
697 if (!(condition & G_IO_IN))
698 return FALSE;
699
700 status = g_io_channel_read_chars(gin, buf, sizeof(buf), &bytes_read, NULL);
701 if (status != G_IO_STATUS_NORMAL)
702 return FALSE;
703
704 if (stdin_port != NULL)
705 spice_port_write_async(stdin_port, buf, bytes_read, NULL, port_write_cb, NULL);
706
707 return TRUE;
708}
709
710static void port_opened(SpiceChannel *channel, GParamSpec *pspec,
711 spice_connection *conn)
712{
713 SpicePortChannel *port = SPICE_PORT_CHANNEL(channel);
714 gchar *name = NULL;
715 gboolean opened = FALSE;
716
717 g_object_get(channel,
718 "port-name", &name,
719 "port-opened", &opened,
720 NULL);
721
722 g_printerr("port %p %s: %s\n", channel, name, opened ? "opened" : "closed");
723
724 if (opened) {
725 /* only send a break event and disconnect */
726 if (g_strcmp0(name, "org.spice.spicy.break") == 0) {
727 spice_port_event(port, SPICE_PORT_EVENT_BREAK);
728 }
729
730 /* handle the first spicy port and connect it to stdin/out */
731 if (g_strcmp0(name, "org.spice.spicy") == 0 && stdin_port == NULL) {
732 stdin_port = port;
733 goto end;
734 }
735
736 if (port == stdin_port)
737 goto end;
738
739 spice_channel_flush_async(channel, NULL, port_flushed_cb, conn);
740 } else {
741 if (port == stdin_port)
742 stdin_port = NULL;
743 }
744
745end:
746 g_free(name);
747}
748
749static void port_data(SpicePortChannel *port,
750 gpointer data, int size, spice_connection *conn)
751{
752 int r;
753
754 if (port != stdin_port)
755 return;
756
757 r = write(fileno(stdout), data, size);
758 if (r != size) {
759 g_warning("port write failed result %d/%d errno %d", r, size, errno);
760 }
761}
762
763static void channel_new(SpiceSession *s, SpiceChannel *channel, gpointer data)
764{
765 spice_connection *conn = data;
766 int id;
767
768 g_object_get(channel, "channel-id", &id, NULL);
769 conn->channels++;
770 SPICE_DEBUG("new channel (#%d)", id);
771
772 if (SPICE_IS_MAIN_CHANNEL(channel)) {
773 SPICE_DEBUG("new main channel");
774 conn->main = SPICE_MAIN_CHANNEL(channel);
775 g_signal_connect(channel, "channel-event",
776 G_CALLBACK(main_channel_event), conn);
777 g_signal_connect(channel, "main-mouse-update",
778 G_CALLBACK(main_mouse_update), conn);
779 g_signal_connect(channel, "main-agent-update",
780 G_CALLBACK(main_agent_update), conn);
781 main_mouse_update(channel, conn);
782 main_agent_update(channel, conn);
783 }
784
785 if (SPICE_IS_DISPLAY_CHANNEL(channel)) {
786 if (id >= SPICE_N_ELEMENTS(conn->wins))
787 return;
788 if (conn->wins[id] != NULL)
789 return;
790 SPICE_DEBUG("new display channel (#%d)", id);
791 g_signal_connect(channel, "notify::monitors",
792 G_CALLBACK(display_monitors), conn);
793 spice_channel_connect(channel);
794 }
795
796 if (SPICE_IS_INPUTS_CHANNEL(channel)) {
797 SPICE_DEBUG("new inputs channel");
798 g_signal_connect(channel, "inputs-modifiers",
799 G_CALLBACK(inputs_modifiers), conn);
800 }
801
802 if (SPICE_IS_PLAYBACK_CHANNEL(channel)) {
803 SPICE_DEBUG("new audio channel");
804 conn->audio = spice_audio_get(s, NULL);
805 }
806
807 if (SPICE_IS_USBREDIR_CHANNEL(channel)) {
808 //update_auto_usbredir_sensitive(conn);
809 }
810
811 if (SPICE_IS_PORT_CHANNEL(channel)) {
812 g_signal_connect(channel, "notify::port-opened",
813 G_CALLBACK(port_opened), conn);
814 g_signal_connect(channel, "port-data",
815 G_CALLBACK(port_data), conn);
816 spice_channel_connect(channel);
817 }
818}
819
820static void channel_destroy(SpiceSession *s, SpiceChannel *channel, gpointer data)
821{
822 spice_connection *conn = data;
823 int id;
824
825 g_object_get(channel, "channel-id", &id, NULL);
826 if (SPICE_IS_MAIN_CHANNEL(channel)) {
827 SPICE_DEBUG("zap main channel");
828 conn->main = NULL;
829 }
830
831 if (SPICE_IS_DISPLAY_CHANNEL(channel)) {
832 if (id >= SPICE_N_ELEMENTS(conn->wins))
833 return;
834 SPICE_DEBUG("zap display channel (#%d)", id);
835 /* FIXME destroy widget only */
836 }
837
838 if (SPICE_IS_PLAYBACK_CHANNEL(channel)) {
839 SPICE_DEBUG("zap audio channel");
840 }
841
842 if (SPICE_IS_USBREDIR_CHANNEL(channel)) {
843 //update_auto_usbredir_sensitive(conn);
844 }
845
846 if (SPICE_IS_PORT_CHANNEL(channel)) {
847 if (SPICE_PORT_CHANNEL(channel) == stdin_port)
848 stdin_port = NULL;
849 }
850
851 conn->channels--;
852 if (conn->channels > 0) {
853 return;
854 }
855
856 connection_destroy(conn);
857}
858
859static void migration_state(GObject *session,
860 GParamSpec *pspec, gpointer data)
861{
862 SpiceSessionMigration mig;
863
864 g_object_get(session, "migration-state", &mig, NULL);
865 if (mig == SPICE_SESSION_MIGRATION_SWITCHING)
866 g_message("migrating session");
867}
868
869static spice_connection *connection_new(void)
870{
871 spice_connection *conn;
872 SpiceUsbDeviceManager *manager;
873
874 conn = g_new0(spice_connection, 1);
875 conn->session = spice_session_new();
876 conn->gtk_session = spice_gtk_session_get(conn->session);
877 g_signal_connect(conn->session, "channel-new",
878 G_CALLBACK(channel_new), conn);
879 g_signal_connect(conn->session, "channel-destroy",
880 G_CALLBACK(channel_destroy), conn);
881 g_signal_connect(conn->session, "notify::migration-state",
882 G_CALLBACK(migration_state), conn);
883
884 manager = spice_usb_device_manager_get(conn->session, NULL);
885 if (manager) {
886 g_signal_connect(manager, "auto-connect-failed",
887 G_CALLBACK(usb_connect_failed), NULL);
888 g_signal_connect(manager, "device-error",
889 G_CALLBACK(usb_connect_failed), NULL);
890 }
891
892 return conn;
893}
894
895static void connection_connect(spice_connection *conn)
896{
897 conn->disconnecting = false;
898 spice_session_connect(conn->session);
899}
900
901static void connection_disconnect(spice_connection *conn)
902{
903 if (conn->disconnecting)
904 return;
905 conn->disconnecting = true;
906 spice_session_disconnect(conn->session);
907}
908
909static void connection_destroy(spice_connection *conn)
910{
911 g_object_unref(conn->session);
912 free(conn);
913}
914
915/* ------------------------------------------------------------------ */
916
917static void usb_connect_failed(GObject *object,
918 SpiceUsbDevice *device,
919 GError *error,
920 gpointer data)
921{
922 GtkWidget *dialog;
923
924 if (error->domain == G_IO_ERROR && error->code == G_IO_ERROR_CANCELLED)
925 return;
926
927 dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR,
928 GTK_BUTTONS_CLOSE,
929 "USB redirection error");
930 gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
931 "%s", error->message);
932 gtk_dialog_run(GTK_DIALOG(dialog));
933 gtk_widget_destroy(dialog);
934}
935
936static void
937_burrito_smart_add(Evas_Object *obj)
938{
939 Burrito *sd;
940 Evas *e = evas_object_evas_get(obj);
941
942 sd = calloc(1, sizeof(Burrito));
943 if (!sd) return;
944 sd->obj = obj;
945 sd->clip = evas_object_rectangle_add(e);
946 evas_object_smart_member_add(sd->clip, sd->obj);
947 sd->image = evas_object_image_filled_add(e);
948 evas_object_smart_member_add(sd->image, sd->obj);
949 evas_object_smart_data_set(obj, sd);
950}
951
952static void
953_burrito_smart_del(Evas_Object *obj)
954{
955 INTERNAL_ENTRY;
956 del_window(sd->conn, sd->win);
957 evas_object_del(sd->clip);
958 evas_object_del(sd->image);
959 free(sd);
960}
961
962static void
963_burrito_smart_move(Evas_Object *obj, Evas_Coord x, Evas_Coord y)
964{
965 INTERNAL_ENTRY;
966 evas_object_move(sd->clip, x, y);
967 evas_object_move(sd->image, x, y);
968}
969
970static void
971_burrito_smart_resize(Evas_Object *obj, Evas_Coord w, Evas_Coord h)
972{
973 INTERNAL_ENTRY;
974 evas_object_resize(sd->clip, w, h);
975 evas_object_resize(sd->image, w, h);
976}
977
978static void
979_burrito_smart_show(Evas_Object *obj)
980{
981 INTERNAL_ENTRY;
982 evas_object_show(sd->clip);
983 evas_object_show(sd->image);
984}
985
986static void
987_burrito_smart_hide(Evas_Object *obj)
988{
989 INTERNAL_ENTRY;
990 evas_object_hide(sd->clip);
991 evas_object_hide(sd->image);
992}
993
994static void
995_burrito_smart_color_set(Evas_Object *obj, int r, int g, int b, int a)
996{
997 INTERNAL_ENTRY;
998 evas_object_color_set(sd->clip, r, g, b, a);
999}
1000
1001static void
1002_burrito_smart_clip_set(Evas_Object *obj, Evas_Object *clip)
1003{
1004 INTERNAL_ENTRY;
1005 evas_object_clip_set(sd->clip, clip);
1006}
1007
1008static void
1009_burrito_smart_clip_unset(Evas_Object *obj)
1010{
1011 INTERNAL_ENTRY;
1012 evas_object_clip_unset(sd->clip);
1013}
1014
1015static void
1016_burrito_init(void)
1017{
1018 char **argv;
1019 int argc;
1020 static const Evas_Smart_Class sc =
1021 {
1022 "burrito", EVAS_SMART_CLASS_VERSION,
1023 _burrito_smart_add, _burrito_smart_del, _burrito_smart_move, _burrito_smart_resize,
1024 _burrito_smart_show, _burrito_smart_hide, _burrito_smart_color_set, _burrito_smart_clip_set,
1025 _burrito_smart_clip_unset, NULL, NULL, NULL, NULL, NULL, NULL, NULL
1026 };
1027 if (_burrito_smart) return;
1028 evas_init();
1029 ecore_init();
1030 ecore_app_args_get(&argc, &argv);
1031 ecore_main_loop_glib_integrate();
1032 gtk_init(&argc, &argv);
1033 _burrito_smart = evas_smart_class_new(&sc);
1034}
1035
1036Evas_Object *
1037burrito_add(Evas *e, const char *uri, const char *host, unsigned int port, unsigned int tls_port, const char *password)
1038{
1039 Evas_Object *o;
1040 Burrito *sd;
1041 char buf[16];
1042
1043 EINA_SAFETY_ON_TRUE_RETURN_VAL(port > 65535, NULL);
1044 EINA_SAFETY_ON_TRUE_RETURN_VAL(tls_port > 65535, NULL);
1045 _burrito_init();
1046 o = evas_object_smart_add(e, _burrito_smart);
1047 sd = evas_object_smart_data_get(o);
1048
1049 sd->conn = connection_new();
1050 if (uri)
1051 g_object_set(sd->conn->session, "uri", uri, NULL);
1052 if (host)
1053 g_object_set(sd->conn->session, "host", host, NULL);
1054 if (port)
1055 {
1056 snprintf(buf, sizeof(buf), "%u", port);
1057 g_object_set(sd->conn->session, "port", buf, NULL);
1058 }
1059 if (tls_port)
1060 {
1061 snprintf(buf, sizeof(buf), "%u", tls_port);
1062 g_object_set(sd->conn->session, "tls-port", buf, NULL);
1063 }
1064 if (password)
1065 g_object_set(sd->conn->session, "password", password, NULL);
1066
1067 connection_connect(sd->conn);
1068 sd->conn->smart_obj = o;
1069
1070 return o;
1071}