Merge lp:~timchen119/ubuntu/utopic/gnome-bluetooth/lp1035431 into lp:ubuntu/utopic/gnome-bluetooth

Proposed by Tim Chen
Status: Merged
Approved by: Mathieu Trudel-Lapierre
Approved revision: 94
Merge reported by: Mathieu Trudel-Lapierre
Merged at revision: not available
Proposed branch: lp:~timchen119/ubuntu/utopic/gnome-bluetooth/lp1035431
Merge into: lp:ubuntu/utopic/gnome-bluetooth
Diff against target: 1854 lines (+1753/-12)
8 files modified
.pc/applied-patches (+1/-0)
.pc/ssp-parameter-fix.patch/lib/bluetooth-agent.c (+633/-0)
.pc/ssp-parameter-fix.patch/wizard/main.c (+1001/-0)
debian/changelog (+10/-0)
debian/patches/series (+1/-0)
debian/patches/ssp-parameter-fix.patch (+86/-0)
lib/bluetooth-agent.c (+4/-4)
wizard/main.c (+17/-8)
To merge this branch: bzr merge lp:~timchen119/ubuntu/utopic/gnome-bluetooth/lp1035431
Reviewer Review Type Date Requested Status
Mathieu Trudel-Lapierre Approve
Review via email: mp+219628@code.launchpad.net

Description of the change

This fixed the issue for the paring issue on Logitech K810 and HP bluetooth keyboard K4000 and should also fixed any ssp paired bluetooth keyboard.

gnome-bluetooth is being removed in recent gnome upstream, so we need to maintain a ubuntu distro patch here.

Another related merge is at
https://code.launchpad.net/~timchen119/ubuntu/utopic/bluez/lp1035431/+merge/219626

The root problem here is that in bluez4, the doc (agent-api.txt), specific that the
function

void DisplayPasskey(object device, uint32 passkey, uint8 entered)

however in bluez4's src/agent.c agent_display_passkey()
didn't pass the entered correct "enter" parameter,
and most of client implementation in linux (at least gnome-bluetooth, bluedevil, blueman) follows the specification in the api document so they never receive correct parameter.

This is already fixed in bluez5,
however due to the compatibility issue between bluez4 and bluez5
this never being backported in bluez4.

This request is from our OEM so please help to review these patch and see if there's things I can improve this patch. If the patch is accepted I'll send SRUs to trusty (OEM shipping image is LTS based, so it will be trusty right now).

To post a comment you must log in.
Revision history for this message
Mathieu Trudel-Lapierre (cyphermox) wrote :

Looks fine to me. If it fixes the issues let's land it.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.pc/applied-patches'
2--- .pc/applied-patches 2014-02-25 16:32:43 +0000
3+++ .pc/applied-patches 2014-05-15 03:31:21 +0000
4@@ -7,3 +7,4 @@
5 git_leak_fixes.patch
6 git_disconnect_callbacks.patch
7 git_reference_handling.patch
8+ssp-parameter-fix.patch
9
10=== added directory '.pc/ssp-parameter-fix.patch'
11=== added directory '.pc/ssp-parameter-fix.patch/lib'
12=== added file '.pc/ssp-parameter-fix.patch/lib/bluetooth-agent.c'
13--- .pc/ssp-parameter-fix.patch/lib/bluetooth-agent.c 1970-01-01 00:00:00 +0000
14+++ .pc/ssp-parameter-fix.patch/lib/bluetooth-agent.c 2014-05-15 03:31:21 +0000
15@@ -0,0 +1,633 @@
16+/*
17+ *
18+ * BlueZ - Bluetooth protocol stack for Linux
19+ *
20+ * Copyright (C) 2005-2008 Marcel Holtmann <marcel@holtmann.org>
21+ *
22+ *
23+ * This library is free software; you can redistribute it and/or
24+ * modify it under the terms of the GNU Lesser General Public
25+ * License as published by the Free Software Foundation; either
26+ * version 2.1 of the License, or (at your option) any later version.
27+ *
28+ * This library is distributed in the hope that it will be useful,
29+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
30+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
31+ * Lesser General Public License for more details.
32+ *
33+ * You should have received a copy of the GNU Lesser General Public
34+ * License along with this library; if not, write to the Free Software
35+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
36+ *
37+ */
38+
39+#ifdef HAVE_CONFIG_H
40+#include <config.h>
41+#endif
42+
43+#include <stdio.h>
44+#include <gio/gio.h>
45+
46+#include "bluetooth-client-glue.h"
47+#include "bluetooth-agent.h"
48+
49+#define BLUEZ_SERVICE "org.bluez"
50+
51+#define BLUEZ_MANAGER_PATH "/"
52+#define BLUEZ_MANAGER_INTERFACE "org.bluez.Manager"
53+#define BLUEZ_DEVICE_INTERFACE "org.bluez.Device"
54+
55+static const gchar introspection_xml[] =
56+"<node name='/'>"
57+" <interface name='org.bluez.Agent'>"
58+" <method name='Release'/>"
59+" <method name='RequestPinCode'>"
60+" <arg type='o' name='device' direction='in'/>"
61+" <arg type='s' name='pincode' direction='out'/>"
62+" </method>"
63+" <method name='RequestPasskey'>"
64+" <arg type='o' name='device' direction='in'/>"
65+" <arg type='u' name='passkey' direction='out'/>"
66+" </method>"
67+" <method name='DisplayPasskey'>"
68+" <arg type='o' name='device' direction='in'/>"
69+" <arg type='u' name='passkey' direction='in'/>"
70+" <arg type='y' name='entered' direction='in'/>"
71+" </method>"
72+" <method name='RequestConfirmation'>"
73+" <arg type='o' name='device' direction='in'/>"
74+" <arg type='u' name='passkey' direction='in'/>"
75+" </method>"
76+" <method name='Authorize'>"
77+" <arg type='o' name='device' direction='in'/>"
78+" <arg type='s' name='uuid' direction='in'/>"
79+" </method>"
80+" <method name='ConfirmMode'>"
81+" <arg type='s' name='mode'/>"
82+" </method>"
83+" <method name='Cancel'/>"
84+" </interface>"
85+"</node>";
86+
87+#define BLUETOOTH_AGENT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), \
88+ BLUETOOTH_TYPE_AGENT, BluetoothAgentPrivate))
89+
90+typedef struct _BluetoothAgentPrivate BluetoothAgentPrivate;
91+
92+struct _BluetoothAgentPrivate {
93+ GDBusConnection *conn;
94+ gchar *busname;
95+ gchar *path;
96+ GDBusProxy *adapter;
97+ GDBusNodeInfo *introspection_data;
98+ guint reg_id;
99+ guint watch_id;
100+
101+ BluetoothAgentPasskeyFunc pincode_func;
102+ gpointer pincode_data;
103+
104+ BluetoothAgentDisplayFunc display_func;
105+ gpointer display_data;
106+
107+ BluetoothAgentPasskeyFunc passkey_func;
108+ gpointer passkey_data;
109+
110+ BluetoothAgentConfirmFunc confirm_func;
111+ gpointer confirm_data;
112+
113+ BluetoothAgentAuthorizeFunc authorize_func;
114+ gpointer authorize_data;
115+
116+ BluetoothAgentCancelFunc cancel_func;
117+ gpointer cancel_data;
118+};
119+
120+G_DEFINE_TYPE(BluetoothAgent, bluetooth_agent, G_TYPE_OBJECT)
121+
122+static GDBusProxy *
123+get_device_from_adapter (BluetoothAgent *agent,
124+ const char *path)
125+{
126+ BluetoothAgentPrivate *priv = BLUETOOTH_AGENT_GET_PRIVATE(agent);
127+
128+ if (priv->adapter == NULL)
129+ return NULL;
130+
131+ return g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
132+ G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS | G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
133+ NULL,
134+ g_dbus_proxy_get_name (priv->adapter),
135+ path,
136+ BLUEZ_DEVICE_INTERFACE,
137+ NULL,
138+ NULL);
139+}
140+
141+static gboolean bluetooth_agent_request_pin_code(BluetoothAgent *agent,
142+ const char *path, GDBusMethodInvocation *invocation)
143+{
144+ BluetoothAgentPrivate *priv = BLUETOOTH_AGENT_GET_PRIVATE(agent);
145+ GDBusProxy *device;
146+ gboolean result = FALSE;
147+
148+ if (priv->pincode_func) {
149+ device = get_device_from_adapter (agent, path);
150+
151+ result = priv->pincode_func(invocation, device,
152+ priv->pincode_data);
153+
154+ if (device != NULL)
155+ g_object_unref(device);
156+ }
157+
158+ return result;
159+}
160+
161+static gboolean bluetooth_agent_request_passkey(BluetoothAgent *agent,
162+ const char *path, GDBusMethodInvocation *invocation)
163+{
164+ BluetoothAgentPrivate *priv = BLUETOOTH_AGENT_GET_PRIVATE(agent);
165+ GDBusProxy *device;
166+ gboolean result = FALSE;
167+
168+ if (priv->passkey_func) {
169+ device = get_device_from_adapter (agent, path);
170+
171+ result = priv->passkey_func(invocation, device,
172+ priv->passkey_data);
173+
174+ if (device != NULL)
175+ g_object_unref(device);
176+ }
177+
178+ return result;
179+}
180+
181+static gboolean bluetooth_agent_display_passkey(BluetoothAgent *agent,
182+ const char *path, guint passkey, guint8 entered,
183+ GDBusMethodInvocation *invocation)
184+{
185+ BluetoothAgentPrivate *priv = BLUETOOTH_AGENT_GET_PRIVATE(agent);
186+ GDBusProxy *device;
187+ gboolean result = FALSE;
188+
189+ if (priv->display_func) {
190+ device = get_device_from_adapter (agent, path);
191+
192+ result = priv->display_func(invocation, device, passkey, entered,
193+ priv->display_data);
194+
195+ if (device != NULL)
196+ g_object_unref(device);
197+ }
198+
199+ return result;
200+}
201+
202+static gboolean bluetooth_agent_request_confirmation(BluetoothAgent *agent,
203+ const char *path, guint passkey,
204+ GDBusMethodInvocation *invocation)
205+{
206+ BluetoothAgentPrivate *priv = BLUETOOTH_AGENT_GET_PRIVATE(agent);
207+ GDBusProxy *device;
208+ gboolean result = FALSE;
209+
210+ if (priv->confirm_func) {
211+ device = get_device_from_adapter (agent, path);
212+
213+ result = priv->confirm_func(invocation, device, passkey,
214+ priv->confirm_data);
215+
216+ if (device != NULL)
217+ g_object_unref(device);
218+ }
219+
220+ return result;
221+}
222+
223+static gboolean bluetooth_agent_authorize(BluetoothAgent *agent,
224+ const char *path, const char *uuid,
225+ GDBusMethodInvocation *invocation)
226+{
227+ BluetoothAgentPrivate *priv = BLUETOOTH_AGENT_GET_PRIVATE(agent);
228+ GDBusProxy *device;
229+ gboolean result = FALSE;
230+
231+ if (priv->authorize_func) {
232+ device = get_device_from_adapter (agent, path);
233+
234+ result = priv->authorize_func(invocation, device, uuid,
235+ priv->authorize_data);
236+
237+ if (device != NULL)
238+ g_object_unref(device);
239+ }
240+
241+ return result;
242+}
243+
244+static gboolean bluetooth_agent_cancel(BluetoothAgent *agent,
245+ GDBusMethodInvocation *invocation)
246+{
247+ BluetoothAgentPrivate *priv = BLUETOOTH_AGENT_GET_PRIVATE(agent);
248+ gboolean result = FALSE;
249+
250+ if (priv->cancel_func)
251+ result = priv->cancel_func(invocation, priv->cancel_data);
252+
253+ return result;
254+}
255+
256+static void
257+name_appeared_cb (GDBusConnection *connection,
258+ const gchar *name,
259+ const gchar *name_owner,
260+ BluetoothAgent *agent)
261+{
262+ BluetoothAgentPrivate *priv = BLUETOOTH_AGENT_GET_PRIVATE(agent);
263+
264+ g_free (priv->busname);
265+ priv->busname = g_strdup (name_owner);
266+}
267+
268+static void
269+name_vanished_cb (GDBusConnection *connection,
270+ const gchar *name,
271+ BluetoothAgent *agent)
272+{
273+ BluetoothAgentPrivate *priv = BLUETOOTH_AGENT_GET_PRIVATE(agent);
274+
275+ g_free (priv->busname);
276+ priv->busname = NULL;
277+}
278+
279+static void bluetooth_agent_init(BluetoothAgent *agent)
280+{
281+ BluetoothAgentPrivate *priv = BLUETOOTH_AGENT_GET_PRIVATE(agent);
282+
283+ priv->introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL);
284+ g_assert (priv->introspection_data);
285+ priv->conn = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL);
286+ priv->watch_id = g_bus_watch_name_on_connection (priv->conn,
287+ BLUEZ_SERVICE,
288+ G_BUS_NAME_WATCHER_FLAGS_NONE,
289+ (GBusNameAppearedCallback) name_appeared_cb,
290+ (GBusNameVanishedCallback) name_vanished_cb,
291+ agent,
292+ NULL);
293+}
294+
295+static void bluetooth_agent_finalize(GObject *agent)
296+{
297+ BluetoothAgentPrivate *priv = BLUETOOTH_AGENT_GET_PRIVATE(agent);
298+
299+ bluetooth_agent_unregister (BLUETOOTH_AGENT (agent));
300+
301+ g_bus_unwatch_name (priv->watch_id);
302+ g_free (priv->busname);
303+ g_dbus_node_info_unref (priv->introspection_data);
304+ g_object_unref (priv->conn);
305+
306+ G_OBJECT_CLASS(bluetooth_agent_parent_class)->finalize(agent);
307+}
308+
309+static void bluetooth_agent_class_init(BluetoothAgentClass *klass)
310+{
311+ GObjectClass *object_class = (GObjectClass *) klass;
312+
313+ g_type_class_add_private(klass, sizeof(BluetoothAgentPrivate));
314+
315+ object_class->finalize = bluetooth_agent_finalize;
316+}
317+
318+BluetoothAgent *
319+bluetooth_agent_new (void)
320+{
321+ return BLUETOOTH_AGENT (g_object_new (BLUETOOTH_TYPE_AGENT, NULL));
322+}
323+
324+static void
325+handle_method_call (GDBusConnection *connection,
326+ const gchar *sender,
327+ const gchar *object_path,
328+ const gchar *interface_name,
329+ const gchar *method_name,
330+ GVariant *parameters,
331+ GDBusMethodInvocation *invocation,
332+ gpointer user_data)
333+{
334+ BluetoothAgent *agent = (BluetoothAgent *) user_data;
335+ BluetoothAgentPrivate *priv = BLUETOOTH_AGENT_GET_PRIVATE(agent);
336+
337+ if (g_str_equal (sender, priv->busname) == FALSE) {
338+ g_assert_not_reached ();
339+ /* FIXME, should this just be a D-Bus Error instead? */
340+ }
341+
342+ if (g_strcmp0 (method_name, "Release") == 0) {
343+ g_dbus_method_invocation_return_value (invocation, NULL);
344+ } else if (g_strcmp0 (method_name, "RequestPinCode") == 0) {
345+ char *path;
346+ g_variant_get (parameters, "(o)", &path);
347+ bluetooth_agent_request_pin_code (agent, path, invocation);
348+ g_free (path);
349+ } else if (g_strcmp0 (method_name, "RequestPasskey") == 0) {
350+ char *path;
351+ g_variant_get (parameters, "(o)", &path);
352+ bluetooth_agent_request_passkey (agent, path, invocation);
353+ g_free (path);
354+ } else if (g_strcmp0 (method_name, "DisplayPasskey") == 0) {
355+ char *path;
356+ guint32 passkey;
357+ guint8 entered;
358+
359+ g_variant_get (parameters, "(ouy)", &path, &passkey, &entered);
360+ bluetooth_agent_display_passkey (agent, path, passkey, entered, invocation);
361+ g_free (path);
362+ } else if (g_strcmp0 (method_name, "RequestConfirmation") == 0) {
363+ char *path;
364+ guint32 passkey;
365+
366+ g_variant_get (parameters, "(ou)", &path, &passkey);
367+ bluetooth_agent_request_confirmation (agent, path, passkey, invocation);
368+ g_free (path);
369+ } else if (g_strcmp0 (method_name, "Authorize") == 0) {
370+ char *path, *uuid;
371+ g_variant_get (parameters, "(os)", &path, &uuid);
372+ bluetooth_agent_authorize (agent, path, uuid, invocation);
373+ g_free (path);
374+ g_free (uuid);
375+ } else if (g_strcmp0 (method_name, "Cancel") == 0) {
376+ bluetooth_agent_cancel (agent, invocation);
377+ } else if (g_strcmp0 (method_name, "ConfirmMode") == 0) {
378+ g_dbus_method_invocation_return_value (invocation, NULL);
379+ }
380+}
381+
382+static const GDBusInterfaceVTable interface_vtable =
383+{
384+ handle_method_call,
385+ NULL, /* GetProperty */
386+ NULL, /* SetProperty */
387+};
388+
389+gboolean bluetooth_agent_setup(BluetoothAgent *agent, const char *path)
390+{
391+ BluetoothAgentPrivate *priv = BLUETOOTH_AGENT_GET_PRIVATE(agent);
392+ GError *error = NULL;
393+
394+ if (priv->path != NULL) {
395+ g_warning ("Agent already setup on '%s'", priv->path);
396+ return FALSE;
397+ }
398+
399+ priv->path = g_strdup(path);
400+
401+ priv->reg_id = g_dbus_connection_register_object (priv->conn,
402+ priv->path,
403+ priv->introspection_data->interfaces[0],
404+ &interface_vtable,
405+ agent,
406+ NULL,
407+ &error);
408+ if (priv->reg_id == 0) {
409+ g_warning ("Failed to register object: %s", error->message);
410+ g_error_free (error);
411+ }
412+
413+ return TRUE;
414+}
415+
416+#define BLUEZ_SERVICE "org.bluez"
417+#define BLUEZ_MANAGER_INTERFACE "org.bluez.Manager"
418+
419+static GDBusProxy *
420+get_default_adapter (void)
421+{
422+ Manager *manager;
423+ char *adapter_path;
424+ Adapter *adapter;
425+
426+ manager = manager_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
427+ G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
428+ BLUEZ_SERVICE,
429+ BLUEZ_MANAGER_PATH,
430+ NULL,
431+ NULL);
432+ if (manager == NULL)
433+ return NULL;
434+ if (manager_call_default_adapter_sync (manager, &adapter_path, NULL, NULL) == FALSE) {
435+ g_object_unref (manager);
436+ return NULL;
437+ }
438+ adapter = adapter_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
439+ G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
440+ BLUEZ_SERVICE,
441+ adapter_path,
442+ NULL,
443+ NULL);
444+ g_object_unref (manager);
445+ g_free (adapter_path);
446+
447+ return G_DBUS_PROXY (adapter);
448+}
449+
450+gboolean bluetooth_agent_register(BluetoothAgent *agent)
451+{
452+ BluetoothAgentPrivate *priv;
453+ GError *error = NULL;
454+ char *path;
455+ GVariant *r;
456+
457+ g_return_val_if_fail (BLUETOOTH_IS_AGENT (agent), FALSE);
458+
459+ priv = BLUETOOTH_AGENT_GET_PRIVATE (agent);
460+
461+ priv->adapter = get_default_adapter ();
462+
463+ if (priv->adapter == NULL)
464+ return FALSE;
465+
466+ if (priv->path != NULL) {
467+ g_warning ("Agent already setup on '%s'", priv->path);
468+ return FALSE;
469+ }
470+
471+ path = g_path_get_basename(g_dbus_proxy_get_object_path(priv->adapter));
472+ priv->path = g_strdup_printf("/org/bluez/agent/%s", path);
473+ g_free(path);
474+
475+ priv->reg_id = g_dbus_connection_register_object (priv->conn,
476+ priv->path,
477+ priv->introspection_data->interfaces[0],
478+ &interface_vtable,
479+ agent,
480+ NULL,
481+ &error);
482+ if (priv->reg_id == 0) {
483+ g_warning ("Failed to register object: %s", error->message);
484+ g_error_free (error);
485+ error = NULL;
486+ return FALSE;
487+ }
488+
489+ r = g_dbus_proxy_call_sync (priv->adapter, "RegisterAgent",
490+ g_variant_new ("(os)", priv->path, "DisplayYesNo"),
491+ G_DBUS_CALL_FLAGS_NONE,
492+ -1, NULL, &error);
493+ if (r == NULL) {
494+ g_printerr ("Agent registration failed: %s\n", error->message);
495+ g_error_free (error);
496+ return FALSE;
497+ }
498+ g_variant_unref (r);
499+
500+ return TRUE;
501+}
502+
503+gboolean bluetooth_agent_unregister(BluetoothAgent *agent)
504+{
505+ BluetoothAgentPrivate *priv;
506+ GError *error = NULL;
507+
508+ g_return_val_if_fail (BLUETOOTH_IS_AGENT (agent), FALSE);
509+
510+ priv = BLUETOOTH_AGENT_GET_PRIVATE(agent);
511+
512+ if (priv->adapter == NULL)
513+ return FALSE;
514+
515+ if (g_dbus_proxy_call_sync (priv->adapter, "UnregisterAgent",
516+ g_variant_new ("(o)", priv->path),
517+ G_DBUS_CALL_FLAGS_NONE,
518+ -1, NULL, &error) == FALSE) {
519+ /* Ignore errors if the adapter is gone */
520+ if (g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD) == FALSE) {
521+ g_printerr ("Agent unregistration failed: %s '%s'\n",
522+ error->message,
523+ g_quark_to_string (error->domain));
524+ }
525+ g_error_free(error);
526+ }
527+
528+ g_object_unref(priv->adapter);
529+ priv->adapter = NULL;
530+
531+ g_free(priv->path);
532+ priv->path = NULL;
533+
534+ g_free(priv->busname);
535+ priv->busname = NULL;
536+
537+ if (priv->reg_id > 0) {
538+ g_dbus_connection_unregister_object (priv->conn, priv->reg_id);
539+ priv->reg_id = 0;
540+ }
541+
542+ return TRUE;
543+}
544+
545+void bluetooth_agent_set_pincode_func(BluetoothAgent *agent,
546+ BluetoothAgentPasskeyFunc func, gpointer data)
547+{
548+ BluetoothAgentPrivate *priv;
549+
550+ g_return_if_fail (BLUETOOTH_IS_AGENT (agent));
551+
552+ priv = BLUETOOTH_AGENT_GET_PRIVATE(agent);
553+
554+ priv->pincode_func = func;
555+ priv->pincode_data = data;
556+}
557+
558+void bluetooth_agent_set_passkey_func(BluetoothAgent *agent,
559+ BluetoothAgentPasskeyFunc func, gpointer data)
560+{
561+ BluetoothAgentPrivate *priv;
562+
563+ g_return_if_fail (BLUETOOTH_IS_AGENT (agent));
564+
565+ priv = BLUETOOTH_AGENT_GET_PRIVATE(agent);
566+
567+ priv->passkey_func = func;
568+ priv->passkey_data = data;
569+}
570+
571+void bluetooth_agent_set_display_func(BluetoothAgent *agent,
572+ BluetoothAgentDisplayFunc func, gpointer data)
573+{
574+ BluetoothAgentPrivate *priv;
575+
576+ g_return_if_fail (BLUETOOTH_IS_AGENT (agent));
577+
578+ priv = BLUETOOTH_AGENT_GET_PRIVATE(agent);
579+
580+ priv->display_func = func;
581+ priv->display_data = data;
582+}
583+
584+void bluetooth_agent_set_confirm_func(BluetoothAgent *agent,
585+ BluetoothAgentConfirmFunc func, gpointer data)
586+{
587+ BluetoothAgentPrivate *priv;
588+
589+ g_return_if_fail (BLUETOOTH_IS_AGENT (agent));
590+
591+ priv = BLUETOOTH_AGENT_GET_PRIVATE(agent);
592+
593+ priv->confirm_func = func;
594+ priv->confirm_data = data;
595+}
596+
597+void bluetooth_agent_set_authorize_func(BluetoothAgent *agent,
598+ BluetoothAgentAuthorizeFunc func, gpointer data)
599+{
600+ BluetoothAgentPrivate *priv;
601+
602+ g_return_if_fail (BLUETOOTH_IS_AGENT (agent));
603+
604+ priv = BLUETOOTH_AGENT_GET_PRIVATE(agent);
605+
606+ priv->authorize_func = func;
607+ priv->authorize_data = data;
608+}
609+
610+void bluetooth_agent_set_cancel_func(BluetoothAgent *agent,
611+ BluetoothAgentCancelFunc func, gpointer data)
612+{
613+ BluetoothAgentPrivate *priv;
614+
615+ g_return_if_fail (BLUETOOTH_IS_AGENT (agent));
616+
617+ priv = BLUETOOTH_AGENT_GET_PRIVATE(agent);
618+
619+ priv->cancel_func = func;
620+ priv->cancel_data = data;
621+}
622+
623+GQuark bluetooth_agent_error_quark(void)
624+{
625+ static GQuark quark = 0;
626+ if (!quark)
627+ quark = g_quark_from_static_string("agent");
628+
629+ return quark;
630+}
631+
632+#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
633+
634+GType bluetooth_agent_error_get_type(void)
635+{
636+ static GType etype = 0;
637+ if (etype == 0) {
638+ static const GEnumValue values[] = {
639+ ENUM_ENTRY(AGENT_ERROR_REJECT, "Rejected"),
640+ { 0, 0, 0 }
641+ };
642+
643+ etype = g_enum_register_static("agent", values);
644+ }
645+
646+ return etype;
647+}
648+
649
650=== added directory '.pc/ssp-parameter-fix.patch/wizard'
651=== added file '.pc/ssp-parameter-fix.patch/wizard/main.c'
652--- .pc/ssp-parameter-fix.patch/wizard/main.c 1970-01-01 00:00:00 +0000
653+++ .pc/ssp-parameter-fix.patch/wizard/main.c 2014-05-15 03:31:21 +0000
654@@ -0,0 +1,1001 @@
655+/*
656+ *
657+ * BlueZ - Bluetooth protocol stack for Linux
658+ *
659+ * Copyright (C) 2005-2008 Marcel Holtmann <marcel@holtmann.org>
660+ *
661+ *
662+ * This program is free software; you can redistribute it and/or modify
663+ * it under the terms of the GNU General Public License as published by
664+ * the Free Software Foundation; either version 2 of the License, or
665+ * (at your option) any later version.
666+ *
667+ * This program is distributed in the hope that it will be useful,
668+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
669+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
670+ * GNU General Public License for more details.
671+ *
672+ * You should have received a copy of the GNU General Public License
673+ * along with this program; if not, write to the Free Software
674+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
675+ *
676+ */
677+
678+#ifdef HAVE_CONFIG_H
679+#include <config.h>
680+#endif
681+
682+#include <math.h>
683+#include <glib/gi18n.h>
684+#include <gtk/gtk.h>
685+#include <gdk/gdkkeysyms.h>
686+
687+#include <bluetooth-client.h>
688+#include <bluetooth-client-private.h>
689+#include <bluetooth-chooser.h>
690+#include <bluetooth-agent.h>
691+#include <bluetooth-plugin-manager.h>
692+
693+#include "pin.h"
694+
695+#define AGENT_PATH "/org/bluez/agent/wizard"
696+
697+/* We'll try to connect to the device repeatedly for that
698+ * amount of time before we bail out */
699+#define CONNECT_TIMEOUT 3.0
700+
701+#define W(x) GTK_WIDGET(gtk_builder_get_object(builder, x))
702+
703+enum {
704+ PAGE_SEARCH,
705+ PAGE_CONNECTING,
706+ PAGE_SETUP,
707+ PAGE_SSP_SETUP,
708+ PAGE_FINISHING,
709+ PAGE_SUMMARY
710+};
711+
712+typedef enum {
713+ PAIRING_UI_NORMAL,
714+ PAIRING_UI_KEYBOARD,
715+ PAIRING_UI_ICADE
716+} PairingUIBehaviour;
717+
718+static gboolean set_page_search_complete(void);
719+
720+static BluetoothClient *client;
721+static BluetoothAgent *agent;
722+
723+static gchar *target_address = NULL;
724+static gchar *target_name = NULL;
725+static guint target_max_digits = 0;
726+static PairingUIBehaviour target_ui_behaviour = PAIRING_UI_NORMAL;
727+static gboolean target_ssp = FALSE;
728+static gboolean create_started = FALSE;
729+static gboolean display_called = FALSE;
730+
731+/* NULL means automatic, anything else is a pincode specified by the user */
732+static gchar *user_pincode = NULL;
733+/* If TRUE, then we won't display the PIN code to the user when pairing */
734+static gboolean automatic_pincode = FALSE;
735+static char *pincode = NULL;
736+
737+static GtkBuilder *builder = NULL;
738+
739+static GtkAssistant *window_assistant = NULL;
740+static GtkWidget *button_quit = NULL;
741+static GtkWidget *button_cancel = NULL;
742+static GtkWidget *page_search = NULL;
743+static GtkWidget *page_connecting = NULL;
744+static GtkWidget *page_setup = NULL;
745+static GtkWidget *page_ssp_setup = NULL;
746+static GtkWidget *page_finishing = NULL;
747+static GtkWidget *page_summary = NULL;
748+
749+static GtkWidget *label_connecting = NULL;
750+static GtkWidget *spinner_connecting = NULL;
751+
752+static GtkWidget *label_pin = NULL;
753+static GtkWidget *label_pin_help = NULL;
754+
755+static GtkWidget *label_ssp_pin_help = NULL;
756+static GtkWidget *label_ssp_pin = NULL;
757+static GtkWidget *does_not_match_button = NULL;
758+static GtkWidget *matches_button = NULL;
759+
760+static GtkWidget *label_finishing = NULL;
761+static GtkWidget *spinner_finishing = NULL;
762+
763+static gboolean summary_failure = FALSE;
764+static GtkWidget *label_summary = NULL;
765+static GtkWidget *extra_config_vbox = NULL;
766+
767+static BluetoothChooser *selector = NULL;
768+
769+static GtkWidget *pin_dialog = NULL;
770+static GtkWidget *radio_auto = NULL;
771+static GtkWidget *radio_0000 = NULL;
772+static GtkWidget *radio_1111 = NULL;
773+static GtkWidget *radio_1234 = NULL;
774+static GtkWidget *radio_none = NULL;
775+static GtkWidget *radio_custom = NULL;
776+static GtkWidget *entry_custom = NULL;
777+
778+/* Signals */
779+void quit_callback(GtkWidget *assistant, gpointer data);
780+void prepare_callback(GtkWidget *assistant, GtkWidget *page, gpointer data);
781+void select_device_changed(BluetoothChooser *selector, const char *address, gpointer user_data);
782+gboolean entry_custom_event(GtkWidget *entry, GdkEventKey *event);
783+void set_user_pincode(GtkWidget *button);
784+void toggle_set_sensitive(GtkWidget *button, gpointer data);
785+void pin_option_button_clicked (GtkButton *button, gpointer data);
786+void entry_custom_changed(GtkWidget *entry);
787+void restart_button_clicked (GtkButton *button, gpointer user_data);
788+void does_not_match_cb (GtkButton *button, gpointer user_data);
789+void matches_cb (GtkButton *button, gpointer user_data);
790+
791+static void
792+set_large_label (GtkLabel *label, const char *text)
793+{
794+ char *str;
795+
796+ str = g_strdup_printf("<span font_desc=\"50\"> %s </span>", text);
797+ gtk_label_set_markup(GTK_LABEL(label), str);
798+ g_free(str);
799+}
800+
801+static char *
802+get_random_pincode (guint num_digits)
803+{
804+ if (num_digits == 0)
805+ num_digits = PIN_NUM_DIGITS;
806+ return g_strdup_printf ("%d", g_random_int_range (pow (10, num_digits - 1),
807+ pow (10, num_digits)));
808+}
809+
810+static char *
811+get_icade_pincode (char **pin_display_str)
812+{
813+ GString *pin, *pin_display;
814+ guint i;
815+ static char *arrows[] = {
816+ NULL,
817+ "⬆", /* up = 1 */
818+ "⬇", /* down = 2 */
819+ "⬅", /* left = 3 */
820+ "➡" /* right = 4 */
821+ };
822+
823+ pin = g_string_new (NULL);
824+ pin_display = g_string_new (NULL);
825+
826+ for (i = 0; i < PIN_NUM_DIGITS; i++) {
827+ int r;
828+ char *c;
829+
830+ r = g_random_int_range (1, 4);
831+
832+ c = g_strdup_printf ("%d", r);
833+ g_string_append (pin, c);
834+ g_free (c);
835+
836+ g_string_append (pin_display, arrows[r]);
837+ }
838+ g_string_append (pin_display, "❍");
839+
840+ *pin_display_str = g_string_free (pin_display, FALSE);
841+ return g_string_free (pin, FALSE);
842+}
843+
844+static gboolean
845+pincode_callback (GDBusMethodInvocation *invocation,
846+ GDBusProxy *device,
847+ gpointer user_data)
848+{
849+ target_ssp = FALSE;
850+
851+ /* Only show the pincode page if the pincode isn't automatic */
852+ if (automatic_pincode == FALSE)
853+ gtk_assistant_set_current_page (window_assistant, PAGE_SETUP);
854+ g_debug ("Using pincode \"%s\"", pincode);
855+ g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", pincode));
856+
857+ return TRUE;
858+}
859+
860+void
861+restart_button_clicked (GtkButton *button,
862+ gpointer user_data)
863+{
864+ /* Clean up old state */
865+ target_ssp = FALSE;
866+ display_called = FALSE;
867+ g_free (target_address);
868+ target_address = NULL;
869+ g_free (target_name);
870+ target_name = NULL;
871+ summary_failure = FALSE;
872+ target_ui_behaviour = PAIRING_UI_NORMAL;
873+
874+ g_object_set (selector,
875+ "device-category-filter", BLUETOOTH_CATEGORY_NOT_PAIRED_OR_TRUSTED,
876+ NULL);
877+
878+ gtk_assistant_set_current_page (window_assistant, PAGE_SEARCH);
879+}
880+
881+void
882+does_not_match_cb (GtkButton *button,
883+ gpointer user_data)
884+{
885+ GDBusMethodInvocation *invocation;
886+ GError *error = NULL;
887+ char *text;
888+
889+ summary_failure = TRUE;
890+ gtk_assistant_set_current_page (window_assistant, PAGE_SUMMARY);
891+
892+ /* translators:
893+ * The '%s' is the device name, for example:
894+ * Pairing with 'Sony Bluetooth Headset' cancelled
895+ */
896+ text = g_strdup_printf(_("Pairing with '%s' cancelled"), target_name);
897+ gtk_label_set_text(GTK_LABEL(label_summary), text);
898+ g_free(text);
899+
900+ invocation = g_object_get_data (G_OBJECT (button), "invocation");
901+ error = g_error_new (AGENT_ERROR, AGENT_ERROR_REJECT,
902+ "Agent callback cancelled");
903+ g_dbus_method_invocation_return_gerror (invocation, error);
904+ g_error_free (error);
905+
906+ g_object_set_data (G_OBJECT(does_not_match_button), "invocation", NULL);
907+ g_object_set_data (G_OBJECT(matches_button), "invocation", NULL);
908+}
909+
910+void
911+matches_cb (GtkButton *button,
912+ gpointer user_data)
913+{
914+ GDBusMethodInvocation *invocation;
915+
916+ invocation = g_object_get_data (G_OBJECT (button), "invocation");
917+ gtk_widget_set_sensitive (does_not_match_button, FALSE);
918+ gtk_widget_set_sensitive (matches_button, FALSE);
919+ g_dbus_method_invocation_return_value (invocation, NULL);
920+
921+ g_object_set_data (G_OBJECT(does_not_match_button), "invocation", NULL);
922+ g_object_set_data (G_OBJECT(matches_button), "invocation", NULL);
923+}
924+
925+static gboolean
926+confirm_callback (GDBusMethodInvocation *invocation,
927+ GDBusProxy *device,
928+ guint pin,
929+ gpointer user_data)
930+{
931+ char *str, *label;
932+
933+ target_ssp = TRUE;
934+ gtk_assistant_set_current_page (window_assistant, PAGE_SSP_SETUP);
935+
936+ gtk_widget_show (label_ssp_pin_help);
937+ label = g_strdup_printf (_("Please confirm that the PIN displayed on '%s' matches this one."),
938+ target_name);
939+ gtk_label_set_markup(GTK_LABEL(label_ssp_pin_help), label);
940+ g_free (label);
941+
942+ gtk_widget_show (label_ssp_pin);
943+ str = g_strdup_printf ("%06d", pin);
944+ set_large_label (GTK_LABEL (label_ssp_pin), str);
945+ g_free (str);
946+
947+ g_object_set_data (G_OBJECT(does_not_match_button), "invocation", invocation);
948+ g_object_set_data (G_OBJECT(matches_button), "invocation", invocation);
949+
950+ return TRUE;
951+}
952+
953+static gboolean
954+display_callback (GDBusMethodInvocation *invocation,
955+ GDBusProxy *device,
956+ guint pin,
957+ guint entered,
958+ gpointer user_data)
959+{
960+ gchar *text, *done, *code;
961+
962+ display_called = TRUE;
963+ target_ssp = TRUE;
964+ gtk_assistant_set_current_page (window_assistant, PAGE_SSP_SETUP);
965+
966+ code = g_strdup_printf("%06d", pin);
967+
968+ if (entered > 0) {
969+ GtkEntry *entry;
970+ gunichar invisible;
971+ GString *str;
972+ guint i;
973+
974+ entry = GTK_ENTRY (gtk_entry_new ());
975+ invisible = gtk_entry_get_invisible_char (entry);
976+ g_object_unref (entry);
977+
978+ str = g_string_new (NULL);
979+ for (i = 0; i < entered; i++)
980+ g_string_append_unichar (str, invisible);
981+ if (entered < strlen (code))
982+ g_string_append (str, code + entered);
983+
984+ done = g_string_free (str, FALSE);
985+ } else {
986+ done = g_strdup ("");
987+ }
988+
989+ gtk_widget_show (label_pin_help);
990+
991+ gtk_label_set_markup(GTK_LABEL(label_ssp_pin_help), _("Please enter the following PIN:"));
992+ text = g_strdup_printf("%s%s", done, code + entered);
993+ set_large_label (GTK_LABEL (label_ssp_pin), text);
994+ g_free(text);
995+
996+ g_free(done);
997+ g_free(code);
998+
999+ g_dbus_method_invocation_return_value (invocation, NULL);
1000+
1001+ return TRUE;
1002+}
1003+
1004+static gboolean
1005+cancel_callback (GDBusMethodInvocation *invocation,
1006+ gpointer user_data)
1007+{
1008+ gchar *text;
1009+
1010+ create_started = FALSE;
1011+
1012+ summary_failure = TRUE;
1013+ gtk_assistant_set_current_page (window_assistant, PAGE_SUMMARY);
1014+
1015+ /* translators:
1016+ * The '%s' is the device name, for example:
1017+ * Pairing with 'Sony Bluetooth Headset' cancelled
1018+ */
1019+ text = g_strdup_printf(_("Pairing with '%s' cancelled"), target_name);
1020+ gtk_label_set_text(GTK_LABEL(label_summary), text);
1021+ g_free(text);
1022+
1023+ g_dbus_method_invocation_return_value (invocation, NULL);
1024+
1025+ return TRUE;
1026+}
1027+
1028+typedef struct {
1029+ char *path;
1030+ GTimer *timer;
1031+} ConnectData;
1032+
1033+static void
1034+connect_callback (GObject *source_object,
1035+ GAsyncResult *res,
1036+ gpointer user_data)
1037+{
1038+ ConnectData *data = (ConnectData *) user_data;
1039+ gboolean success;
1040+
1041+ success = bluetooth_client_connect_service_finish (client, res, NULL);
1042+
1043+ if (success == FALSE && g_timer_elapsed (data->timer, NULL) < CONNECT_TIMEOUT) {
1044+ bluetooth_client_connect_service (client, data->path, TRUE, NULL, connect_callback, data);
1045+ return;
1046+ }
1047+
1048+ if (success == FALSE)
1049+ g_debug ("Failed to connect to device %s", data->path);
1050+
1051+ g_timer_destroy (data->timer);
1052+ g_free (data->path);
1053+ g_free (data);
1054+
1055+ gtk_assistant_set_current_page (window_assistant, PAGE_SUMMARY);
1056+}
1057+
1058+static void
1059+create_callback (BluetoothClient *_client,
1060+ const char *path,
1061+ const GError *error,
1062+ gpointer user_data)
1063+{
1064+ ConnectData *data;
1065+
1066+ create_started = FALSE;
1067+
1068+ /* Create failed */
1069+ if (path == NULL) {
1070+ char *text;
1071+
1072+ summary_failure = TRUE;
1073+ gtk_assistant_set_current_page (window_assistant, PAGE_SUMMARY);
1074+
1075+ /* translators:
1076+ * The '%s' is the device name, for example:
1077+ * Setting up 'Sony Bluetooth Headset' failed
1078+ */
1079+ text = g_strdup_printf(_("Setting up '%s' failed"), target_name);
1080+
1081+ g_warning ("Setting up '%s' failed: %s", target_name, error->message);
1082+
1083+ gtk_label_set_markup(GTK_LABEL(label_summary), text);
1084+ g_free (text);
1085+
1086+ return;
1087+ }
1088+
1089+ bluetooth_client_set_trusted(client, path, TRUE);
1090+
1091+ data = g_new0 (ConnectData, 1);
1092+ data->path = g_strdup (path);
1093+ data->timer = g_timer_new ();
1094+
1095+ bluetooth_client_connect_service (client, path, TRUE, NULL, connect_callback, data);
1096+ gtk_assistant_set_current_page (window_assistant, PAGE_FINISHING);
1097+}
1098+
1099+void
1100+quit_callback (GtkWidget *widget,
1101+ gpointer data)
1102+{
1103+ gtk_widget_destroy(GTK_WIDGET (window_assistant));
1104+}
1105+
1106+void prepare_callback (GtkWidget *assistant,
1107+ GtkWidget *page,
1108+ gpointer data)
1109+{
1110+ gboolean complete = TRUE;
1111+
1112+ gtk_widget_hide (button_quit);
1113+ gtk_widget_hide (button_cancel);
1114+
1115+ if (page == page_search) {
1116+ complete = set_page_search_complete ();
1117+ bluetooth_chooser_start_discovery(selector);
1118+ } else {
1119+ bluetooth_chooser_stop_discovery(selector);
1120+ }
1121+
1122+ if (page == page_connecting) {
1123+ char *text;
1124+
1125+ complete = FALSE;
1126+
1127+ gtk_spinner_start (GTK_SPINNER (spinner_connecting));
1128+
1129+ /* translators:
1130+ * The '%s' is the device name, for example:
1131+ * Connecting to 'Sony Bluetooth Headset'...
1132+ */
1133+ text = g_strdup_printf (_("Connecting to '%s'..."), target_name);
1134+ gtk_label_set_text (GTK_LABEL (label_connecting), text);
1135+ g_free (text);
1136+
1137+ gtk_widget_show (button_cancel);
1138+ } else {
1139+ gtk_spinner_stop (GTK_SPINNER (spinner_connecting));
1140+ }
1141+
1142+ if ((page == page_setup || page == page_connecting) && (create_started == FALSE)) {
1143+ const char *path = AGENT_PATH;
1144+
1145+ /* Set the filter on the selector, so we can use it to get more
1146+ * info later, in page_summary */
1147+ g_object_set (selector,
1148+ "device-category-filter", BLUETOOTH_CATEGORY_ALL,
1149+ NULL);
1150+
1151+ /* Do we pair, or don't we? */
1152+ if (automatic_pincode && pincode == NULL) {
1153+ g_debug ("Not pairing as %s", automatic_pincode ? "pincode is NULL" : "automatic_pincode is FALSE");
1154+ path = NULL;
1155+ }
1156+
1157+ g_object_ref(agent);
1158+ bluetooth_client_create_device (client, target_address,
1159+ path, create_callback, assistant);
1160+ create_started = TRUE;
1161+ }
1162+
1163+ if (page == page_setup) {
1164+ complete = FALSE;
1165+
1166+ if (automatic_pincode == FALSE && target_ssp == FALSE) {
1167+ char *help, *pincode_display;
1168+
1169+ g_free (pincode);
1170+ pincode = NULL;
1171+ pincode_display = NULL;
1172+
1173+ switch (target_ui_behaviour) {
1174+ case PAIRING_UI_NORMAL:
1175+ help = g_strdup_printf (_("Please enter the following PIN on '%s':"), target_name);
1176+ break;
1177+ case PAIRING_UI_KEYBOARD:
1178+ help = g_strdup_printf (_("Please enter the following PIN on '%s' and press “Enter” on the keyboard:"), target_name);
1179+ pincode = get_random_pincode (target_max_digits);
1180+ pincode_display = g_strdup_printf ("%s⏎", pincode);
1181+ break;
1182+ case PAIRING_UI_ICADE:
1183+ help = g_strdup (_("Please move the joystick of your iCade in the following directions:"));
1184+ pincode = get_icade_pincode (&pincode_display);
1185+ break;
1186+ default:
1187+ g_assert_not_reached ();
1188+ }
1189+
1190+ if (pincode == NULL)
1191+ pincode = get_random_pincode (target_max_digits);
1192+
1193+ gtk_label_set_markup(GTK_LABEL(label_pin_help), help);
1194+ g_free (help);
1195+ set_large_label (GTK_LABEL (label_pin), pincode_display ? pincode_display : pincode);
1196+ g_free (pincode_display);
1197+ } else {
1198+ g_assert_not_reached ();
1199+ }
1200+
1201+ gtk_widget_show (button_cancel);
1202+ }
1203+
1204+ if (page == page_finishing) {
1205+ char *text;
1206+
1207+ complete = FALSE;
1208+
1209+ gtk_spinner_start (GTK_SPINNER (spinner_finishing));
1210+
1211+ /* translators:
1212+ * The '%s' is the device name, for example:
1213+ * Please wait while finishing setup on 'Sony Bluetooth Headset'...
1214+ */
1215+ text = g_strdup_printf (_("Please wait while finishing setup on device '%s'..."), target_name);
1216+ gtk_label_set_text (GTK_LABEL (label_finishing), text);
1217+ g_free (text);
1218+
1219+ gtk_widget_show (button_quit);
1220+ } else {
1221+ gtk_spinner_stop (GTK_SPINNER (spinner_finishing));
1222+ }
1223+
1224+ if (page == page_summary && summary_failure == FALSE) {
1225+ GList *widgets = NULL;
1226+ GValue value = { 0, };
1227+ char **uuids, *text;
1228+
1229+ /* FIXME remove this code when bluetoothd has pair/unpair code */
1230+ g_object_set (G_OBJECT (selector), "device-selected", target_address, NULL);
1231+
1232+ bluetooth_chooser_get_selected_device_info (selector, "name", &value);
1233+ text = g_strdup_printf (_("Successfully set up new device '%s'"), g_value_get_string (&value));
1234+ g_value_unset (&value);
1235+ gtk_label_set_text (GTK_LABEL (label_summary), text);
1236+ g_free (text);
1237+
1238+ if (bluetooth_chooser_get_selected_device_info (selector, "uuids", &value) != FALSE) {
1239+ uuids = g_value_get_boxed (&value);
1240+ widgets = bluetooth_plugin_manager_get_widgets (target_address,
1241+ (const char **) uuids);
1242+ g_value_unset (&value);
1243+ }
1244+ if (widgets != NULL) {
1245+ GList *l;
1246+
1247+ for (l = widgets; l != NULL; l = l->next) {
1248+ GtkWidget *widget = l->data;
1249+ gtk_box_pack_start (GTK_BOX (extra_config_vbox),
1250+ widget,
1251+ FALSE,
1252+ TRUE,
1253+ 0);
1254+ }
1255+ g_list_free (widgets);
1256+ gtk_widget_show_all (extra_config_vbox);
1257+ }
1258+ gtk_widget_show (button_quit);
1259+ }
1260+
1261+ /* Setup the buttons some */
1262+ if (page == page_summary && summary_failure) {
1263+ complete = FALSE;
1264+ gtk_assistant_add_action_widget (GTK_ASSISTANT (assistant), W("restart_button"));
1265+ gtk_widget_show (button_quit);
1266+ } else {
1267+ if (gtk_widget_get_parent (W("restart_button")) != NULL)
1268+ gtk_assistant_remove_action_widget (GTK_ASSISTANT (assistant), W("restart_button"));
1269+ }
1270+
1271+ if (page == page_ssp_setup) {
1272+ if (display_called == FALSE) {
1273+ complete = FALSE;
1274+ gtk_assistant_add_action_widget (GTK_ASSISTANT (assistant), W("matches_button"));
1275+ gtk_assistant_add_action_widget (GTK_ASSISTANT (assistant), W("does_not_match_button"));
1276+ } else {
1277+ gtk_widget_show (button_cancel);
1278+ }
1279+ } else {
1280+ if (gtk_widget_get_parent (W("does_not_match_button")) != NULL)
1281+ gtk_assistant_remove_action_widget (GTK_ASSISTANT (assistant), W("does_not_match_button"));
1282+ if (gtk_widget_get_parent (W("matches_button")) != NULL)
1283+ gtk_assistant_remove_action_widget (GTK_ASSISTANT (assistant), W("matches_button"));
1284+ }
1285+
1286+ gtk_assistant_set_page_complete (GTK_ASSISTANT(assistant),
1287+ page, complete);
1288+}
1289+
1290+static gboolean
1291+set_page_search_complete (void)
1292+{
1293+ char *name, *address;
1294+ gboolean complete = FALSE;
1295+
1296+ address = bluetooth_chooser_get_selected_device (selector);
1297+ name = bluetooth_chooser_get_selected_device_name (selector);
1298+
1299+ if (address == NULL)
1300+ complete = FALSE;
1301+ else if (name == NULL)
1302+ complete = (user_pincode != NULL && strlen(user_pincode) >= 4);
1303+ else
1304+ complete = (user_pincode == NULL || strlen(user_pincode) >= 4);
1305+
1306+ g_free (address);
1307+ g_free (name);
1308+
1309+ gtk_assistant_set_page_complete (GTK_ASSISTANT (window_assistant),
1310+ page_search, complete);
1311+
1312+ return complete;
1313+}
1314+
1315+gboolean
1316+entry_custom_event (GtkWidget *entry, GdkEventKey *event)
1317+{
1318+ gunichar c;
1319+
1320+ if (event->length == 0)
1321+ return FALSE;
1322+
1323+ /* Not a printable character? */
1324+ c = gdk_keyval_to_unicode (event->keyval);
1325+ if (c == 0 ||
1326+ g_unichar_iscntrl (c) ||
1327+ g_unichar_isdigit (c))
1328+ return FALSE;
1329+
1330+ return TRUE;
1331+}
1332+
1333+void
1334+entry_custom_changed (GtkWidget *entry)
1335+{
1336+ g_free (user_pincode);
1337+ user_pincode = g_strdup (gtk_entry_get_text(GTK_ENTRY(entry)));
1338+ gtk_dialog_set_response_sensitive (GTK_DIALOG (pin_dialog),
1339+ GTK_RESPONSE_ACCEPT,
1340+ gtk_entry_get_text_length (GTK_ENTRY (entry)) >= 1);
1341+}
1342+
1343+void
1344+toggle_set_sensitive (GtkWidget *button,
1345+ gpointer data)
1346+{
1347+ gboolean active;
1348+
1349+ active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
1350+ gtk_widget_set_sensitive(entry_custom, active);
1351+ /* When selecting another PIN, make sure the "Close" button is sensitive */
1352+ if (!active)
1353+ gtk_dialog_set_response_sensitive (GTK_DIALOG (pin_dialog),
1354+ GTK_RESPONSE_ACCEPT, TRUE);
1355+ else
1356+ entry_custom_changed (entry_custom);
1357+}
1358+
1359+void
1360+set_user_pincode (GtkWidget *button)
1361+{
1362+ if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
1363+ return;
1364+
1365+ g_free (user_pincode);
1366+ user_pincode = g_strdup (g_object_get_data (G_OBJECT (button), "pin"));
1367+}
1368+
1369+void
1370+select_device_changed (BluetoothChooser *selector,
1371+ const char *address,
1372+ gpointer user_data)
1373+{
1374+ GValue value = { 0, };
1375+ guint target_type = BLUETOOTH_TYPE_ANY;
1376+ gboolean is_custom_pin = FALSE;
1377+ int legacypairing;
1378+
1379+ if (gtk_assistant_get_current_page (GTK_ASSISTANT (window_assistant)) != PAGE_SEARCH)
1380+ return;
1381+
1382+ set_page_search_complete ();
1383+
1384+ /* Device was deselected */
1385+ if (address == NULL)
1386+ return;
1387+
1388+ if (bluetooth_chooser_get_selected_device_info (selector, "legacypairing", &value) != FALSE) {
1389+ legacypairing = g_value_get_int (&value);
1390+ if (legacypairing == -1)
1391+ legacypairing = TRUE;
1392+ } else {
1393+ legacypairing = TRUE;
1394+ }
1395+
1396+ g_free(target_address);
1397+ target_address = g_strdup (address);
1398+
1399+ g_free(target_name);
1400+ target_name = bluetooth_chooser_get_selected_device_name (selector);
1401+
1402+ target_type = bluetooth_chooser_get_selected_device_type (selector);
1403+ target_ssp = !legacypairing;
1404+ automatic_pincode = FALSE;
1405+ target_ui_behaviour = PAIRING_UI_NORMAL;
1406+
1407+ g_free (pincode);
1408+ pincode = NULL;
1409+
1410+ g_free (user_pincode);
1411+ user_pincode = get_pincode_for_device (target_type, target_address, target_name, &target_max_digits);
1412+ if (user_pincode != NULL &&
1413+ g_str_equal (user_pincode, "NULL") == FALSE) {
1414+ if (g_str_equal (user_pincode, "KEYBOARD")) {
1415+ target_ui_behaviour = PAIRING_UI_KEYBOARD;
1416+ is_custom_pin = TRUE;
1417+ } else if (g_str_equal (user_pincode, "ICADE")) {
1418+ target_ui_behaviour = PAIRING_UI_ICADE;
1419+ is_custom_pin = TRUE;
1420+ } else {
1421+ pincode = g_strdup (user_pincode);
1422+ }
1423+ }
1424+
1425+ if (is_custom_pin)
1426+ automatic_pincode = FALSE;
1427+ else
1428+ automatic_pincode = user_pincode != NULL;
1429+
1430+ g_free (user_pincode);
1431+ user_pincode = NULL;
1432+
1433+ gtk_entry_set_max_length (GTK_ENTRY (entry_custom), target_max_digits);
1434+}
1435+
1436+void
1437+pin_option_button_clicked (GtkButton *button,
1438+ gpointer data)
1439+{
1440+ GtkWidget *radio;
1441+
1442+ gtk_window_set_transient_for (GTK_WINDOW (pin_dialog),
1443+ GTK_WINDOW (window_assistant));
1444+ gtk_window_present (GTK_WINDOW (pin_dialog));
1445+
1446+ /* When reopening, try to guess where the pincode was set */
1447+ if (user_pincode == NULL)
1448+ radio = radio_auto;
1449+ else if (g_str_equal (user_pincode, "0000"))
1450+ radio = radio_0000;
1451+ else if (g_str_equal (user_pincode, "1111"))
1452+ radio = radio_1111;
1453+ else if (g_str_equal (user_pincode, "1234"))
1454+ radio = radio_1234;
1455+ else if (g_str_equal (user_pincode, "NULL"))
1456+ radio = radio_none;
1457+ else {
1458+ radio = radio_custom;
1459+ gtk_entry_set_text (GTK_ENTRY (entry_custom), user_pincode);
1460+ }
1461+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio), TRUE);
1462+
1463+ gtk_dialog_run (GTK_DIALOG (pin_dialog));
1464+ gtk_widget_hide (pin_dialog);
1465+ g_free (pincode);
1466+ pincode = g_strdup (user_pincode);
1467+ automatic_pincode = user_pincode != NULL;
1468+}
1469+
1470+static int
1471+page_func (gint current_page,
1472+ gpointer data)
1473+{
1474+ if (current_page == PAGE_SEARCH) {
1475+ if (target_ssp != FALSE || automatic_pincode != FALSE)
1476+ return PAGE_CONNECTING;
1477+ else
1478+ return PAGE_SETUP;
1479+ }
1480+ if (current_page == PAGE_SETUP)
1481+ return PAGE_SUMMARY;
1482+ return current_page + 1;
1483+}
1484+
1485+static gboolean
1486+create_wizard (void)
1487+{
1488+ GtkAssistant *assistant;
1489+ GError *err = NULL;
1490+
1491+ builder = gtk_builder_new ();
1492+ if (gtk_builder_add_from_file (builder, "wizard.ui", NULL) == 0) {
1493+ if (gtk_builder_add_from_file (builder, PKGDATADIR "/wizard.ui", &err) == 0) {
1494+ g_warning ("Could not load UI from %s: %s", PKGDATADIR "/wizard.ui", err->message);
1495+ g_error_free(err);
1496+ return FALSE;
1497+ }
1498+ }
1499+
1500+ window_assistant = GTK_ASSISTANT(gtk_builder_get_object(builder, "assistant"));
1501+ assistant = window_assistant;
1502+
1503+ gtk_assistant_set_forward_page_func (assistant, page_func, NULL, NULL);
1504+
1505+ /* The 2 custom buttons */
1506+ button_quit = W("quit_button");
1507+ button_cancel = W("cancel_button");
1508+ gtk_assistant_add_action_widget (assistant, button_quit);
1509+ gtk_assistant_add_action_widget (assistant, button_cancel);
1510+ gtk_widget_hide (button_quit);
1511+ gtk_widget_hide (button_cancel);
1512+
1513+ /* Intro page, nothing to do */
1514+
1515+ /* Search page */
1516+ page_search = W("page_search");
1517+ selector = BLUETOOTH_CHOOSER (gtk_builder_get_object (builder, "selector"));
1518+
1519+ /* Connecting page */
1520+ page_connecting = W("page_connecting");
1521+ label_connecting = W("label_connecting");
1522+ spinner_connecting = W("spinner_connecting");
1523+
1524+ /* Setup page */
1525+ page_setup = W("page_setup");
1526+ label_pin_help = W("label_pin_help");
1527+ label_pin = W("label_pin");
1528+
1529+ /* SSP Setup page */
1530+ page_ssp_setup = W("page_ssp_setup");
1531+ gtk_assistant_set_page_complete(assistant, page_ssp_setup, FALSE);
1532+ label_ssp_pin_help = W("label_ssp_pin_help");
1533+ label_ssp_pin = W("label_ssp_pin");
1534+ does_not_match_button = W("does_not_match_button");
1535+ matches_button = W("matches_button");
1536+
1537+ /* Finishing page */
1538+ page_finishing = W("page_finishing");
1539+ label_finishing = W("label_finishing");
1540+ spinner_finishing = W("spinner_finishing");
1541+
1542+ /* Summary page */
1543+ page_summary = W("page_summary");
1544+ label_summary = W("label_summary");
1545+ extra_config_vbox = W("extra_config_vbox");
1546+
1547+ /* PIN dialog */
1548+ pin_dialog = W("pin_dialog");
1549+ radio_auto = W("radio_auto");
1550+ radio_0000 = W("radio_0000");
1551+ radio_1111 = W("radio_1111");
1552+ radio_1234 = W("radio_1234");
1553+ radio_none = W("radio_none");
1554+ radio_custom = W("radio_custom");
1555+ entry_custom = W("entry_custom");
1556+
1557+ g_object_set_data (G_OBJECT (radio_auto), "pin", NULL);
1558+ g_object_set_data (G_OBJECT (radio_0000), "pin", "0000");
1559+ g_object_set_data (G_OBJECT (radio_1111), "pin", "1111");
1560+ g_object_set_data (G_OBJECT (radio_1234), "pin", "1234");
1561+ g_object_set_data (G_OBJECT (radio_none), "pin", "NULL");
1562+ g_object_set_data (G_OBJECT (radio_custom), "pin", "");
1563+ g_object_set_data (G_OBJECT (radio_custom), "entry", entry_custom);
1564+
1565+ gtk_builder_connect_signals(builder, NULL);
1566+
1567+ gtk_widget_show (GTK_WIDGET(assistant));
1568+
1569+ gtk_assistant_update_buttons_state(GTK_ASSISTANT(assistant));
1570+
1571+ return TRUE;
1572+}
1573+
1574+static void
1575+activate_cb (GApplication *app,
1576+ gpointer user_data)
1577+{
1578+ gtk_window_present_with_time (GTK_WINDOW (user_data), GDK_CURRENT_TIME);
1579+}
1580+
1581+static GOptionEntry options[] = {
1582+ { NULL },
1583+};
1584+
1585+int main (int argc, char **argv)
1586+{
1587+ GtkApplication *app;
1588+ GError *error = NULL;
1589+
1590+ bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
1591+ bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
1592+ textdomain(GETTEXT_PACKAGE);
1593+
1594+ if (gtk_init_with_args(&argc, &argv, NULL,
1595+ options, GETTEXT_PACKAGE, &error) == FALSE) {
1596+ if (error) {
1597+ g_printerr("%s\n", error->message);
1598+ g_error_free(error);
1599+ } else
1600+ g_printerr("An unknown error occurred\n");
1601+
1602+ return 1;
1603+ }
1604+
1605+ app = gtk_application_new ("org.gnome.Bluetooth.wizard", G_APPLICATION_FLAGS_NONE);
1606+ if (g_application_register (G_APPLICATION (app), NULL, &error) == FALSE) {
1607+ g_warning ("Could not register application: %s", error->message);
1608+ g_error_free (error);
1609+ return 1;
1610+ }
1611+
1612+ if (g_application_get_is_remote (G_APPLICATION (app))) {
1613+ g_application_activate (G_APPLICATION (app));
1614+ gdk_notify_startup_complete ();
1615+ return 0;
1616+ }
1617+
1618+ gtk_window_set_default_icon_name("bluetooth");
1619+
1620+ client = bluetooth_client_new();
1621+
1622+ agent = bluetooth_agent_new();
1623+ g_object_add_weak_pointer (G_OBJECT (agent), (gpointer *) (&agent));
1624+
1625+ bluetooth_agent_set_pincode_func(agent, pincode_callback, NULL);
1626+ bluetooth_agent_set_display_func(agent, display_callback, NULL);
1627+ bluetooth_agent_set_cancel_func(agent, cancel_callback, NULL);
1628+ bluetooth_agent_set_confirm_func(agent, confirm_callback, NULL);
1629+
1630+ bluetooth_agent_setup(agent, AGENT_PATH);
1631+
1632+ bluetooth_plugin_manager_init ();
1633+
1634+ if (create_wizard() == FALSE)
1635+ return 1;
1636+ gtk_application_add_window (app,
1637+ GTK_WINDOW (window_assistant));
1638+
1639+ g_signal_connect (app, "activate",
1640+ G_CALLBACK (activate_cb), window_assistant);
1641+
1642+ g_application_run (G_APPLICATION (app), argc, argv);
1643+
1644+ bluetooth_plugin_manager_cleanup ();
1645+
1646+ if (agent != NULL)
1647+ g_object_unref (agent);
1648+
1649+ g_object_unref(client);
1650+
1651+ g_object_unref(app);
1652+
1653+ return 0;
1654+}
1655+
1656
1657=== modified file 'debian/changelog'
1658--- debian/changelog 2014-04-25 02:00:56 +0000
1659+++ debian/changelog 2014-05-15 03:31:21 +0000
1660@@ -1,3 +1,13 @@
1661+gnome-bluetooth (3.8.2.1-0ubuntu6~test1) utopic; urgency=medium
1662+
1663+ * Add ssp parameter fix, fix the parameter entered in agent dbus API,
1664+ also fix the wizard's UI to show the pin code correctly.
1665+ Fixed Logitech Bluetooth Keyboard K760, Logitech Bluetooth Keyboard K810,
1666+ HP Bluetooth Keyboard K4000 secure simple pairing failed issue.
1667+ (LP: #1035431, #1291756)
1668+
1669+ -- Jian-Ding Chen (timchen119) <tim.chen119@canonical.com> Fri, 02 May 2014 16:50:57 +0800
1670+
1671 gnome-bluetooth (3.8.2.1-0ubuntu5) utopic; urgency=low
1672
1673 * debian/patches/99_add_microsoft_mice.patch: fix the problem that
1674
1675=== modified file 'debian/patches/series'
1676--- debian/patches/series 2014-02-25 16:32:43 +0000
1677+++ debian/patches/series 2014-05-15 03:31:21 +0000
1678@@ -7,3 +7,4 @@
1679 git_leak_fixes.patch
1680 git_disconnect_callbacks.patch
1681 git_reference_handling.patch
1682+ssp-parameter-fix.patch
1683
1684=== added file 'debian/patches/ssp-parameter-fix.patch'
1685--- debian/patches/ssp-parameter-fix.patch 1970-01-01 00:00:00 +0000
1686+++ debian/patches/ssp-parameter-fix.patch 2014-05-15 03:31:21 +0000
1687@@ -0,0 +1,86 @@
1688+Description: ssp parameter fix
1689+ * Add ssp parameter fix, fix the parameter entered in agent dbus API,
1690+ also fix the wizard's UI to show the pin code correctly.
1691+ Fixed Logitech Bluetooth Keyboard K760, Logitech Bluetooth Keyboard K810,
1692+ HP Bluetooth Keyboard K4000 secure simple pairing failed issue.
1693+ (LP: #1035431, #1291756)
1694+Author: Jian-Ding Chen (timchen119) <tim.chen119@canonical.com>
1695+Bug-Ubuntu: https://bugs.launchpad.net/bugs/1035431
1696+Bug-Ubuntu: https://bugs.launchpad.net/bugs/1291756
1697+Last-Update: 2014-04-29
1698+
1699+--- gnome-bluetooth-3.8.2.1.orig/lib/bluetooth-agent.c
1700++++ gnome-bluetooth-3.8.2.1/lib/bluetooth-agent.c
1701+@@ -52,7 +52,7 @@ static const gchar introspection_xml[] =
1702+ " <method name='DisplayPasskey'>"
1703+ " <arg type='o' name='device' direction='in'/>"
1704+ " <arg type='u' name='passkey' direction='in'/>"
1705+-" <arg type='y' name='entered' direction='in'/>"
1706++" <arg type='q' name='entered' direction='in'/>"
1707+ " </method>"
1708+ " <method name='RequestConfirmation'>"
1709+ " <arg type='o' name='device' direction='in'/>"
1710+@@ -164,7 +164,7 @@ static gboolean bluetooth_agent_request_
1711+ }
1712+
1713+ static gboolean bluetooth_agent_display_passkey(BluetoothAgent *agent,
1714+- const char *path, guint passkey, guint8 entered,
1715++ const char *path, guint passkey, guint16 entered,
1716+ GDBusMethodInvocation *invocation)
1717+ {
1718+ BluetoothAgentPrivate *priv = BLUETOOTH_AGENT_GET_PRIVATE(agent);
1719+@@ -339,9 +339,9 @@ handle_method_call (GDBusConnection
1720+ } else if (g_strcmp0 (method_name, "DisplayPasskey") == 0) {
1721+ char *path;
1722+ guint32 passkey;
1723+- guint8 entered;
1724++ guint16 entered;
1725+
1726+- g_variant_get (parameters, "(ouy)", &path, &passkey, &entered);
1727++ g_variant_get (parameters, "(ouq)", &path, &passkey, &entered);
1728+ bluetooth_agent_display_passkey (agent, path, passkey, entered, invocation);
1729+ g_free (path);
1730+ } else if (g_strcmp0 (method_name, "RequestConfirmation") == 0) {
1731+--- gnome-bluetooth-3.8.2.1.orig/wizard/main.c
1732++++ gnome-bluetooth-3.8.2.1/wizard/main.c
1733+@@ -303,7 +303,7 @@ display_callback (GDBusMethodInvocation
1734+ guint entered,
1735+ gpointer user_data)
1736+ {
1737+- gchar *text, *done, *code;
1738++ gchar *text, *done, *code, *label;
1739+
1740+ display_called = TRUE;
1741+ target_ssp = TRUE;
1742+@@ -329,18 +329,27 @@ display_callback (GDBusMethodInvocation
1743+
1744+ done = g_string_free (str, FALSE);
1745+ } else {
1746+- done = g_strdup ("");
1747++ done = g_strdup_printf("%s", code);
1748+ }
1749+
1750+- gtk_widget_show (label_pin_help);
1751++ gtk_widget_show (label_ssp_pin);
1752+
1753+- gtk_label_set_markup(GTK_LABEL(label_ssp_pin_help), _("Please enter the following PIN:"));
1754+- text = g_strdup_printf("%s%s", done, code + entered);
1755++ if (target_ui_behaviour == PAIRING_UI_KEYBOARD) {
1756++ label = g_strdup_printf (_("Please enter the following PIN on '%s' and press “Enter” on the keyboard:"), target_name);
1757++ text = g_strdup_printf("%s⏎", done);
1758++ }
1759++ else {
1760++ label = g_strdup_printf (_("Please enter the following PIN on '%s':"), target_name);
1761++ text = g_strdup_printf("%s", done);
1762++ }
1763++
1764++ gtk_label_set_markup(GTK_LABEL(label_ssp_pin_help), label);
1765+ set_large_label (GTK_LABEL (label_ssp_pin), text);
1766+ g_free(text);
1767+
1768+ g_free(done);
1769+ g_free(code);
1770++ g_free(label);
1771+
1772+ g_dbus_method_invocation_return_value (invocation, NULL);
1773+
1774
1775=== modified file 'lib/bluetooth-agent.c'
1776--- lib/bluetooth-agent.c 2013-07-03 16:07:57 +0000
1777+++ lib/bluetooth-agent.c 2014-05-15 03:31:21 +0000
1778@@ -52,7 +52,7 @@
1779 " <method name='DisplayPasskey'>"
1780 " <arg type='o' name='device' direction='in'/>"
1781 " <arg type='u' name='passkey' direction='in'/>"
1782-" <arg type='y' name='entered' direction='in'/>"
1783+" <arg type='q' name='entered' direction='in'/>"
1784 " </method>"
1785 " <method name='RequestConfirmation'>"
1786 " <arg type='o' name='device' direction='in'/>"
1787@@ -164,7 +164,7 @@
1788 }
1789
1790 static gboolean bluetooth_agent_display_passkey(BluetoothAgent *agent,
1791- const char *path, guint passkey, guint8 entered,
1792+ const char *path, guint passkey, guint16 entered,
1793 GDBusMethodInvocation *invocation)
1794 {
1795 BluetoothAgentPrivate *priv = BLUETOOTH_AGENT_GET_PRIVATE(agent);
1796@@ -339,9 +339,9 @@
1797 } else if (g_strcmp0 (method_name, "DisplayPasskey") == 0) {
1798 char *path;
1799 guint32 passkey;
1800- guint8 entered;
1801+ guint16 entered;
1802
1803- g_variant_get (parameters, "(ouy)", &path, &passkey, &entered);
1804+ g_variant_get (parameters, "(ouq)", &path, &passkey, &entered);
1805 bluetooth_agent_display_passkey (agent, path, passkey, entered, invocation);
1806 g_free (path);
1807 } else if (g_strcmp0 (method_name, "RequestConfirmation") == 0) {
1808
1809=== modified file 'wizard/main.c'
1810--- wizard/main.c 2013-07-03 16:07:57 +0000
1811+++ wizard/main.c 2014-05-15 03:31:21 +0000
1812@@ -303,7 +303,7 @@
1813 guint entered,
1814 gpointer user_data)
1815 {
1816- gchar *text, *done, *code;
1817+ gchar *text, *done, *code, *label;
1818
1819 display_called = TRUE;
1820 target_ssp = TRUE;
1821@@ -329,18 +329,27 @@
1822
1823 done = g_string_free (str, FALSE);
1824 } else {
1825- done = g_strdup ("");
1826- }
1827-
1828- gtk_widget_show (label_pin_help);
1829-
1830- gtk_label_set_markup(GTK_LABEL(label_ssp_pin_help), _("Please enter the following PIN:"));
1831- text = g_strdup_printf("%s%s", done, code + entered);
1832+ done = g_strdup_printf("%s", code);
1833+ }
1834+
1835+ gtk_widget_show (label_ssp_pin);
1836+
1837+ if (target_ui_behaviour == PAIRING_UI_KEYBOARD) {
1838+ label = g_strdup_printf (_("Please enter the following PIN on '%s' and press “Enter” on the keyboard:"), target_name);
1839+ text = g_strdup_printf("%s⏎", done);
1840+ }
1841+ else {
1842+ label = g_strdup_printf (_("Please enter the following PIN on '%s':"), target_name);
1843+ text = g_strdup_printf("%s", done);
1844+ }
1845+
1846+ gtk_label_set_markup(GTK_LABEL(label_ssp_pin_help), label);
1847 set_large_label (GTK_LABEL (label_ssp_pin), text);
1848 g_free(text);
1849
1850 g_free(done);
1851 g_free(code);
1852+ g_free(label);
1853
1854 g_dbus_method_invocation_return_value (invocation, NULL);
1855

Subscribers

People subscribed via source and target branches

to all changes: