/* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2005-2008 Marcel Holtmann * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include "pin.h" #define AGENT_PATH "/org/bluez/agent/wizard" /* We'll try to connect to the device repeatedly for that * amount of time before we bail out */ #define CONNECT_TIMEOUT 3.0 #define W(x) GTK_WIDGET(gtk_builder_get_object(builder, x)) enum { PAGE_SEARCH, PAGE_CONNECTING, PAGE_SETUP, PAGE_SSP_SETUP, PAGE_FINISHING, PAGE_SUMMARY }; typedef enum { PAIRING_UI_NORMAL, PAIRING_UI_KEYBOARD, PAIRING_UI_ICADE } PairingUIBehaviour; static gboolean set_page_search_complete(void); static BluetoothClient *client; static BluetoothAgent *agent; static gchar *target_address = NULL; static gchar *target_name = NULL; static BluetoothType target_type = BLUETOOTH_TYPE_ANY; static guint target_max_digits = 0; static PairingUIBehaviour target_ui_behaviour = PAIRING_UI_NORMAL; static gboolean legacypairing = FALSE; static gboolean create_started = FALSE; static gboolean display_called = FALSE; static guint pincode_cb_id = 0; /* NULL means automatic, anything else is a pincode specified by the user */ static gchar *user_pincode = NULL; static GtkBuilder *builder = NULL; static GtkAssistant *window_assistant = NULL; static GtkWidget *button_quit = NULL; static GtkWidget *button_cancel = NULL; static GtkWidget *page_search = NULL; static GtkWidget *page_connecting = NULL; static GtkWidget *page_setup = NULL; static GtkWidget *page_ssp_setup = NULL; static GtkWidget *page_finishing = NULL; static GtkWidget *page_summary = NULL; static GtkWidget *label_connecting = NULL; static GtkWidget *spinner_connecting = NULL; static GtkWidget *label_pin = NULL; static GtkWidget *label_pin_help = NULL; static GtkWidget *label_ssp_pin_help = NULL; static GtkWidget *label_ssp_pin = NULL; static GtkWidget *does_not_match_button = NULL; static GtkWidget *matches_button = NULL; static GtkWidget *label_finishing = NULL; static GtkWidget *spinner_finishing = NULL; static gboolean summary_failure = FALSE; static GtkWidget *label_summary = NULL; static GtkWidget *extra_config_vbox = NULL; static BluetoothChooser *selector = NULL; static GtkWidget *pin_option_button = NULL; static GtkWidget *pin_dialog = NULL; static GtkWidget *radio_auto = NULL; static GtkWidget *radio_0000 = NULL; static GtkWidget *radio_1111 = NULL; static GtkWidget *radio_1234 = NULL; static GtkWidget *radio_none = NULL; static GtkWidget *radio_custom = NULL; static GtkWidget *entry_custom = NULL; /* Signals */ void quit_callback(GtkWidget *assistant, gpointer data); void prepare_callback(GtkWidget *assistant, GtkWidget *page, gpointer data); void select_device_changed(BluetoothChooser *selector, const char *address, gpointer user_data); gboolean entry_custom_event(GtkWidget *entry, GdkEventKey *event); void set_user_pincode(GtkWidget *button); void toggle_set_sensitive(GtkWidget *button, gpointer data); void pin_option_button_clicked (GtkButton *button, gpointer data); void entry_custom_changed(GtkWidget *entry); void restart_button_clicked (GtkButton *button, gpointer user_data); void does_not_match_cb (GtkButton *button, gpointer user_data); void matches_cb (GtkButton *button, gpointer user_data); static void set_large_label (GtkLabel *label, const char *text) { char *str; str = g_strdup_printf(" %s ", text); gtk_label_set_markup(GTK_LABEL(label), str); g_free(str); } static char * get_random_pincode (guint num_digits) { if (num_digits == 0) num_digits = PIN_NUM_DIGITS; return g_strdup_printf ("%d", g_random_int_range (pow (10, num_digits - 1), pow (10, num_digits))); } static char * get_icade_pincode (char **pin_display_str) { GString *pin, *pin_display; guint i; static char *arrows[] = { NULL, "⬆", /* up = 1 */ "⬇", /* down = 2 */ "⬅", /* left = 3 */ "➡" /* right = 4 */ }; pin = g_string_new (NULL); pin_display = g_string_new (NULL); for (i = 0; i < PIN_NUM_DIGITS; i++) { int r; char *c; r = g_random_int_range (1, 5); c = g_strdup_printf ("%d", r); g_string_append (pin, c); g_free (c); g_string_append (pin_display, arrows[r]); } g_string_append (pin_display, "❍"); *pin_display_str = g_string_free (pin_display, FALSE); return g_string_free (pin, FALSE); } static void replace_target_properties_for_device (GDBusProxy *device) { GVariant *value; g_free (target_name); value = g_dbus_proxy_get_cached_property (device, "Name"); target_name = g_variant_dup_string (value, NULL); g_variant_unref (value); g_free (target_address); value = g_dbus_proxy_get_cached_property (device, "Address"); target_address = g_variant_dup_string (value, NULL); g_variant_unref (value); value = g_dbus_proxy_get_cached_property (device, "Class"); if (value != NULL) { target_type = value ? bluetooth_class_to_type (g_variant_get_uint32 (value)) : BLUETOOTH_TYPE_ANY; g_variant_unref (value); } else { target_type = BLUETOOTH_TYPE_ANY; } } static void update_user_pincode (void) { const char *pincode; pincode = get_pincode_for_device (target_type, target_address, target_name, &target_max_digits, NULL); if (pincode != NULL) { if (g_str_equal (pincode, "KEYBOARD")) target_ui_behaviour = PAIRING_UI_KEYBOARD; else if (g_str_equal (pincode, "ICADE")) target_ui_behaviour = PAIRING_UI_ICADE; gtk_entry_set_max_length (GTK_ENTRY (entry_custom), target_max_digits); g_free (user_pincode); user_pincode = g_strdup (pincode); } else { target_ui_behaviour = PAIRING_UI_NORMAL; } } static gboolean pincode_callback_timeout_cb (gpointer user_data) { GDBusMethodInvocation *invocation = user_data; GDBusProxy *device = g_object_get_data (G_OBJECT (invocation), "device"); create_started = TRUE; replace_target_properties_for_device (device); update_user_pincode (); if (user_pincode == NULL) { char *help, *pincode_display; gtk_assistant_set_current_page (window_assistant, PAGE_SETUP); pincode_display = NULL; switch (target_ui_behaviour) { case PAIRING_UI_NORMAL: help = g_strdup_printf (_("Please enter the following PIN on '%s':"), target_name); user_pincode = get_random_pincode (target_max_digits); break; case PAIRING_UI_KEYBOARD: help = g_strdup_printf (_("Please enter the following PIN on '%s' and press “Enter” on the keyboard:"), target_name); user_pincode = get_random_pincode (target_max_digits); pincode_display = g_strdup_printf ("%s⏎", user_pincode); break; case PAIRING_UI_ICADE: help = g_strdup (_("Please move the joystick of your iCade in the following directions:")); user_pincode = get_icade_pincode (&pincode_display); break; default: g_assert_not_reached (); } gtk_label_set_markup(GTK_LABEL(label_pin_help), help); g_free (help); set_large_label (GTK_LABEL (label_pin), pincode_display ? pincode_display : user_pincode); g_free (pincode_display); } else { char *text; gtk_assistant_set_current_page (window_assistant, PAGE_CONNECTING); gtk_spinner_start (GTK_SPINNER (spinner_connecting)); /* translators: * The '%s' is the device name, for example: * Connecting to 'Sony Bluetooth Headset'... */ text = g_strdup_printf (_("Connecting to '%s'..."), target_name); gtk_label_set_text (GTK_LABEL (label_connecting), text); g_free (text); /* Set the filter on the selector, so we can use it to get more * info later, in page_summary */ g_object_set (selector, "device-category-filter", BLUETOOTH_CATEGORY_ALL, NULL); } gtk_widget_show (button_cancel); bluetooth_client_set_trusted (client, g_dbus_proxy_get_object_path (device), TRUE); g_debug ("Using pincode \"%s\" for '%s' (%s)", user_pincode, target_name, target_address); g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", user_pincode)); pincode_cb_id = 0; return G_SOURCE_REMOVE; } static void pincode_callback (GDBusMethodInvocation *invocation, GDBusProxy *device, gpointer user_data) { g_assert (pincode_cb_id == 0); g_object_set_data_full (G_OBJECT (invocation), "device", g_object_ref (device), g_object_unref); pincode_cb_id = g_timeout_add (500, pincode_callback_timeout_cb, invocation); } void restart_button_clicked (GtkButton *button, gpointer user_data) { /* Clean up old state */ display_called = FALSE; g_free (target_address); target_address = NULL; g_free (target_name); target_name = NULL; summary_failure = FALSE; target_ui_behaviour = PAIRING_UI_NORMAL; g_object_set (selector, "device-category-filter", BLUETOOTH_CATEGORY_NOT_PAIRED_OR_TRUSTED, NULL); gtk_assistant_set_current_page (window_assistant, PAGE_SEARCH); } void does_not_match_cb (GtkButton *button, gpointer user_data) { GDBusMethodInvocation *invocation; GError *error = NULL; char *text; summary_failure = TRUE; gtk_assistant_set_current_page (window_assistant, PAGE_SUMMARY); /* translators: * The '%s' is the device name, for example: * Pairing with 'Sony Bluetooth Headset' cancelled */ text = g_strdup_printf(_("Pairing with '%s' cancelled"), target_name); gtk_label_set_text(GTK_LABEL(label_summary), text); g_free(text); invocation = g_object_get_data (G_OBJECT (button), "invocation"); error = g_error_new (AGENT_ERROR, AGENT_ERROR_REJECT, "Agent callback cancelled"); g_dbus_method_invocation_take_error (invocation, error); g_object_set_data (G_OBJECT(does_not_match_button), "invocation", NULL); g_object_set_data (G_OBJECT(matches_button), "invocation", NULL); } void matches_cb (GtkButton *button, gpointer user_data) { GDBusMethodInvocation *invocation; invocation = g_object_get_data (G_OBJECT (button), "invocation"); gtk_widget_set_sensitive (does_not_match_button, FALSE); gtk_widget_set_sensitive (matches_button, FALSE); g_dbus_method_invocation_return_value (invocation, NULL); g_object_set_data (G_OBJECT(does_not_match_button), "invocation", NULL); g_object_set_data (G_OBJECT(matches_button), "invocation", NULL); } static void authorize_callback (GDBusMethodInvocation *invocation, GDBusProxy *device, gpointer user_data) { g_dbus_method_invocation_return_value (invocation, NULL); replace_target_properties_for_device (device); } static void authorize_service_callback (GDBusMethodInvocation *invocation, GDBusProxy *device, const char *uuid, gpointer user_data) { g_dbus_method_invocation_return_value (invocation, NULL); replace_target_properties_for_device (device); } static void confirm_callback (GDBusMethodInvocation *invocation, GDBusProxy *device, guint pin, gpointer user_data) { char *str, *label; gtk_assistant_set_current_page (window_assistant, PAGE_SSP_SETUP); replace_target_properties_for_device (device); gtk_widget_show (label_ssp_pin_help); label = g_strdup_printf (_("Please confirm that the PIN displayed on '%s' matches this one."), target_name); gtk_label_set_markup(GTK_LABEL(label_ssp_pin_help), label); g_free (label); gtk_widget_show (label_ssp_pin); str = g_strdup_printf ("%06d", pin); set_large_label (GTK_LABEL (label_ssp_pin), str); g_free (str); g_object_set_data (G_OBJECT(does_not_match_button), "invocation", invocation); g_object_set_data (G_OBJECT(matches_button), "invocation", invocation); } static void display_callback (GDBusMethodInvocation *invocation, GDBusProxy *device, guint pin, guint entered, gpointer user_data) { gchar *text, *done, *code, *label; display_called = TRUE; gtk_assistant_set_current_page (window_assistant, PAGE_SSP_SETUP); replace_target_properties_for_device (device); code = g_strdup_printf("%06d", pin); if (entered > 0) { GtkEntry *entry; gunichar invisible; GString *str; guint i; entry = GTK_ENTRY (gtk_entry_new ()); invisible = gtk_entry_get_invisible_char (entry); g_object_unref (entry); str = g_string_new (NULL); for (i = 0; i < entered; i++) g_string_append_unichar (str, invisible); if (entered < strlen (code)) g_string_append (str, code + entered); done = g_string_free (str, FALSE); } else { done = g_strdup_printf("%s", code); } gtk_widget_show (label_ssp_pin); if (target_ui_behaviour == PAIRING_UI_KEYBOARD) { label = g_strdup_printf (_("Please enter the following PIN on '%s' and press “Enter” on the keyboard:"), target_name); text = g_strdup_printf("%s⏎", done); } else { label = g_strdup_printf (_("Please enter the following PIN on '%s':"), target_name); text = g_strdup_printf("%s", done); } gtk_label_set_markup(GTK_LABEL(label_ssp_pin_help), label); set_large_label (GTK_LABEL (label_ssp_pin), text); g_free(text); g_free(done); g_free(code); g_free(label); g_dbus_method_invocation_return_value (invocation, NULL); } static void display_pincode_callback (GDBusMethodInvocation *invocation, GDBusProxy *device, const char *pincode, gpointer user_data) { g_debug ("Reject bluetoothd PIN %s", pincode); /* Reject all the calls here, so that we'll get asked about the * pincode instead of being told the pincode */ g_dbus_method_invocation_return_dbus_error (invocation, "org.bluez.Error.Rejected", "Rejected bluetoothd generated PIN code"); } static gboolean cancel_callback (GDBusMethodInvocation *invocation, gpointer user_data) { gchar *text; create_started = FALSE; summary_failure = TRUE; gtk_assistant_set_current_page (window_assistant, PAGE_SUMMARY); /* translators: * The '%s' is the device name, for example: * Pairing with 'Sony Bluetooth Headset' cancelled */ text = g_strdup_printf(_("Pairing with '%s' cancelled"), target_name); gtk_label_set_text(GTK_LABEL(label_summary), text); g_free(text); g_dbus_method_invocation_return_value (invocation, NULL); return TRUE; } typedef struct { const char *device; GTimer *timer; guint timeout_id; } ConnectData; static void connect_callback (GObject *source_object, GAsyncResult *res, gpointer user_data); static gboolean connect_timeout_cb (gpointer user_data) { ConnectData *data = (ConnectData *) user_data; bluetooth_client_connect_service (client, data->device, TRUE, NULL, connect_callback, data); data->timeout_id = 0; return G_SOURCE_REMOVE; } static void connect_callback (GObject *source_object, GAsyncResult *res, gpointer user_data) { ConnectData *data = (ConnectData *) user_data; gboolean success; success = bluetooth_client_connect_service_finish (client, res, NULL); if (success == FALSE && g_timer_elapsed (data->timer, NULL) < CONNECT_TIMEOUT) { g_assert (data->timeout_id == 0); data->timeout_id = g_timeout_add (500, connect_timeout_cb, data); return; } if (data->timeout_id > 0) g_source_remove (data->timeout_id); if (success == FALSE) g_debug ("Failed to connect to device %s", data->device); g_timer_destroy (data->timer); g_free (data); gtk_assistant_set_current_page (window_assistant, PAGE_SUMMARY); } static void create_callback (GObject *source_object, GAsyncResult *res, gpointer user_data) { ConnectData *data; GError *error = NULL; gboolean ret; char *path; create_started = FALSE; ret = bluetooth_client_setup_device_finish (BLUETOOTH_CLIENT (source_object), res, &path, &error); /* Create failed */ if (ret == FALSE) { char *text; summary_failure = TRUE; gtk_assistant_set_current_page (window_assistant, PAGE_SUMMARY); /* translators: * The '%s' is the device name, for example: * Setting up 'Sony Bluetooth Headset' failed */ text = g_strdup_printf(_("Setting up '%s' failed"), target_name); g_warning ("Setting up '%s' (at %s) failed: %s", target_name, path, error->message); gtk_label_set_markup(GTK_LABEL(label_summary), text); g_free (text); g_error_free (error); g_free (path); return; } bluetooth_client_set_trusted (client, path, TRUE); data = g_new0 (ConnectData, 1); data->device = path; data->timer = g_timer_new (); bluetooth_client_connect_service (client, path, TRUE, NULL, connect_callback, data); gtk_assistant_set_current_page (window_assistant, PAGE_FINISHING); } void quit_callback (GtkWidget *widget, gpointer data) { gtk_widget_destroy(GTK_WIDGET (window_assistant)); } void prepare_callback (GtkWidget *assistant, GtkWidget *page, gpointer data) { gboolean complete = FALSE; gtk_widget_hide (button_quit); gtk_widget_hide (button_cancel); if (page == page_search) { complete = set_page_search_complete (); bluetooth_chooser_start_discovery(selector); } else { bluetooth_chooser_stop_discovery(selector); } if (page == page_connecting && create_started == FALSE) { const char *device; char *text; GValue value = { 0, }; GDBusProxy *proxy; gboolean pair = TRUE; gtk_spinner_start (GTK_SPINNER (spinner_connecting)); g_free(target_name); target_name = bluetooth_chooser_get_selected_device_name (selector); /* translators: * The '%s' is the device name, for example: * Connecting to 'Sony Bluetooth Headset'... */ text = g_strdup_printf (_("Connecting to '%s'..."), target_name); gtk_label_set_text (GTK_LABEL (label_connecting), text); g_free (text); gtk_widget_show (button_cancel); bluetooth_chooser_get_selected_device_info (selector, "proxy", &value); proxy = g_value_get_object(&value); device = g_dbus_proxy_get_object_path(proxy); /* Set the filter on the selector, so we can use it to get more * info later, in page_summary */ g_object_set (selector, "device-category-filter", BLUETOOTH_CATEGORY_ALL, NULL); g_free(target_address); target_address = bluetooth_chooser_get_selected_device (selector); target_type = bluetooth_chooser_get_selected_device_type (selector); target_ui_behaviour = PAIRING_UI_NORMAL; if (legacypairing == TRUE) update_user_pincode (); g_object_ref(agent); if (user_pincode != NULL && g_str_equal (user_pincode, "NULL")) { g_debug ("Got NULL pincode, will not pair with %s", target_name); pair = FALSE; } bluetooth_client_setup_device (client, device, pair, NULL, (GAsyncReadyCallback) create_callback, NULL); create_started = TRUE; } else { gtk_spinner_stop (GTK_SPINNER (spinner_connecting)); } if (page == page_finishing) { char *text; gtk_spinner_start (GTK_SPINNER (spinner_finishing)); /* translators: * The '%s' is the device name, for example: * Please wait while finishing setup on 'Sony Bluetooth Headset'... */ text = g_strdup_printf (_("Please wait while finishing setup on device '%s'..."), target_name); gtk_label_set_text (GTK_LABEL (label_finishing), text); g_free (text); gtk_widget_show (button_quit); } else { gtk_spinner_stop (GTK_SPINNER (spinner_finishing)); } if (page == page_summary && summary_failure == FALSE) { char *text, *name; /* FIXME remove this code when bluetoothd has pair/unpair code */ g_object_set (G_OBJECT (selector), "device-selected", target_address, NULL); name = bluetooth_chooser_get_selected_device_name (selector); text = g_strdup_printf (_("Successfully set up new device '%s'"), name); g_free (name); gtk_label_set_text (GTK_LABEL (label_summary), text); g_free (text); gtk_widget_show (button_quit); } /* Setup the buttons some */ if (page == page_summary && summary_failure) { gtk_assistant_add_action_widget (GTK_ASSISTANT (assistant), W("restart_button")); gtk_widget_show (button_quit); } else { if (gtk_widget_get_parent (W("restart_button")) != NULL) gtk_assistant_remove_action_widget (GTK_ASSISTANT (assistant), W("restart_button")); } if (page == page_ssp_setup) { if (display_called == FALSE) { gtk_assistant_add_action_widget (GTK_ASSISTANT (assistant), W("matches_button")); gtk_assistant_add_action_widget (GTK_ASSISTANT (assistant), W("does_not_match_button")); } else { gtk_widget_show (button_cancel); } } else { if (gtk_widget_get_parent (W("does_not_match_button")) != NULL) gtk_assistant_remove_action_widget (GTK_ASSISTANT (assistant), W("does_not_match_button")); if (gtk_widget_get_parent (W("matches_button")) != NULL) gtk_assistant_remove_action_widget (GTK_ASSISTANT (assistant), W("matches_button")); } gtk_assistant_set_page_complete (GTK_ASSISTANT(assistant), page, complete); } static gboolean set_page_search_complete (void) { char *name, *address; gboolean complete = FALSE; address = bluetooth_chooser_get_selected_device (selector); name = bluetooth_chooser_get_selected_device_name (selector); if (address == NULL) complete = FALSE; else if (name == NULL) complete = (user_pincode != NULL && strlen(user_pincode) >= 4); else complete = (user_pincode == NULL || strlen(user_pincode) >= 4); g_free (address); g_free (name); gtk_assistant_set_page_complete (GTK_ASSISTANT (window_assistant), page_search, complete); return complete; } gboolean entry_custom_event (GtkWidget *entry, GdkEventKey *event) { gunichar c; if (event->length == 0) return FALSE; /* Not a printable character? */ c = gdk_keyval_to_unicode (event->keyval); if (c == 0 || g_unichar_iscntrl (c) || g_unichar_isdigit (c)) return FALSE; return TRUE; } void entry_custom_changed (GtkWidget *entry) { g_free (user_pincode); user_pincode = g_strdup (gtk_entry_get_text(GTK_ENTRY(entry))); gtk_dialog_set_response_sensitive (GTK_DIALOG (pin_dialog), GTK_RESPONSE_ACCEPT, gtk_entry_get_text_length (GTK_ENTRY (entry)) >= 1); } void toggle_set_sensitive (GtkWidget *button, gpointer data) { gboolean active; active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)); gtk_widget_set_sensitive(entry_custom, active); /* When selecting another PIN, make sure the "Close" button is sensitive */ if (!active) gtk_dialog_set_response_sensitive (GTK_DIALOG (pin_dialog), GTK_RESPONSE_ACCEPT, TRUE); else entry_custom_changed (entry_custom); } void set_user_pincode (GtkWidget *button) { if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) return; g_free (user_pincode); user_pincode = g_strdup (g_object_get_data (G_OBJECT (button), "pin")); } void select_device_changed (BluetoothChooser *selector, const char *address, gpointer user_data) { GValue value = { 0, }; int lp; if (gtk_assistant_get_current_page (GTK_ASSISTANT (window_assistant)) != PAGE_SEARCH) return; set_page_search_complete (); gtk_widget_hide (pin_dialog); /* Device was deselected */ if (address == NULL) { gtk_widget_set_sensitive (pin_option_button, FALSE); return; } gtk_widget_set_sensitive (pin_option_button, TRUE); if (bluetooth_chooser_get_selected_device_info (selector, "legacypairing", &value) != FALSE) { lp = g_value_get_int (&value); if (lp == -1) lp = TRUE; } else { lp = TRUE; } legacypairing = lp; if (legacypairing == TRUE) { gtk_widget_show(radio_0000); gtk_widget_show(radio_1111); gtk_widget_show(radio_1234); gtk_widget_show(radio_custom); gtk_widget_show(entry_custom); } else { gtk_widget_hide(radio_0000); gtk_widget_hide(radio_1111); gtk_widget_hide(radio_1234); gtk_widget_hide(radio_custom); gtk_widget_hide(entry_custom); } return; } void pin_option_button_clicked (GtkButton *button, gpointer data) { GtkWidget *radio; gtk_window_set_transient_for (GTK_WINDOW (pin_dialog), GTK_WINDOW (window_assistant)); gtk_window_present (GTK_WINDOW (pin_dialog)); /* When reopening, try to guess where the pincode was set */ if (user_pincode == NULL) radio = radio_auto; else if (g_str_equal (user_pincode, "0000")) radio = radio_0000; else if (g_str_equal (user_pincode, "1111")) radio = radio_1111; else if (g_str_equal (user_pincode, "1234")) radio = radio_1234; else if (g_str_equal (user_pincode, "NULL")) radio = radio_none; else { radio = radio_custom; gtk_entry_set_text (GTK_ENTRY (entry_custom), user_pincode); } gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio), TRUE); gtk_dialog_run (GTK_DIALOG (pin_dialog)); gtk_widget_hide (pin_dialog); } static int page_func (gint current_page, gpointer data) { switch (current_page) { case PAGE_SETUP: return PAGE_SUMMARY; default: return current_page + 1; } } static gboolean create_wizard (void) { GtkAssistant *assistant; GError *err = NULL; builder = gtk_builder_new (); if (gtk_builder_add_from_file (builder, "wizard.ui", NULL) == 0) { if (gtk_builder_add_from_file (builder, PKGDATADIR "/wizard.ui", &err) == 0) { g_warning ("Could not load UI from %s: %s", PKGDATADIR "/wizard.ui", err->message); g_error_free(err); return FALSE; } } window_assistant = GTK_ASSISTANT(gtk_builder_get_object(builder, "assistant")); assistant = window_assistant; gtk_assistant_set_forward_page_func (assistant, page_func, NULL, NULL); /* The 2 custom buttons */ button_quit = W("quit_button"); button_cancel = W("cancel_button"); gtk_assistant_add_action_widget (assistant, button_quit); gtk_assistant_add_action_widget (assistant, button_cancel); gtk_widget_hide (button_quit); gtk_widget_hide (button_cancel); /* Intro page, nothing to do */ /* Search page */ page_search = W("page_search"); selector = BLUETOOTH_CHOOSER (gtk_builder_get_object (builder, "selector")); /* Connecting page */ page_connecting = W("page_connecting"); label_connecting = W("label_connecting"); spinner_connecting = W("spinner_connecting"); /* Setup page */ page_setup = W("page_setup"); label_pin_help = W("label_pin_help"); label_pin = W("label_pin"); /* SSP Setup page */ page_ssp_setup = W("page_ssp_setup"); gtk_assistant_set_page_complete(assistant, page_ssp_setup, FALSE); label_ssp_pin_help = W("label_ssp_pin_help"); label_ssp_pin = W("label_ssp_pin"); does_not_match_button = W("does_not_match_button"); matches_button = W("matches_button"); /* Finishing page */ page_finishing = W("page_finishing"); label_finishing = W("label_finishing"); spinner_finishing = W("spinner_finishing"); /* Summary page */ page_summary = W("page_summary"); label_summary = W("label_summary"); extra_config_vbox = W("extra_config_vbox"); /* PIN dialog */ pin_option_button = W("pin_option_button"); pin_dialog = W("pin_dialog"); radio_auto = W("radio_auto"); radio_0000 = W("radio_0000"); radio_1111 = W("radio_1111"); radio_1234 = W("radio_1234"); radio_none = W("radio_none"); radio_custom = W("radio_custom"); entry_custom = W("entry_custom"); g_object_set_data (G_OBJECT (radio_auto), "pin", NULL); g_object_set_data (G_OBJECT (radio_0000), "pin", "0000"); g_object_set_data (G_OBJECT (radio_1111), "pin", "1111"); g_object_set_data (G_OBJECT (radio_1234), "pin", "1234"); g_object_set_data (G_OBJECT (radio_none), "pin", "NULL"); g_object_set_data (G_OBJECT (radio_custom), "pin", ""); g_object_set_data (G_OBJECT (radio_custom), "entry", entry_custom); gtk_builder_connect_signals(builder, NULL); gtk_widget_show (GTK_WIDGET(assistant)); gtk_assistant_update_buttons_state(GTK_ASSISTANT(assistant)); return TRUE; } static void activate_cb (GApplication *app, gpointer user_data) { gtk_window_present_with_time (GTK_WINDOW (user_data), GDK_CURRENT_TIME); } static GOptionEntry options[] = { { NULL }, }; int main (int argc, char **argv) { GtkApplication *app; GError *error = NULL; bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR); bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); textdomain(GETTEXT_PACKAGE); if (gtk_init_with_args(&argc, &argv, NULL, options, GETTEXT_PACKAGE, &error) == FALSE) { if (error) { g_printerr("%s\n", error->message); g_error_free(error); } else g_printerr("An unknown error occurred\n"); return 1; } app = gtk_application_new ("org.gnome.Bluetooth.wizard", G_APPLICATION_FLAGS_NONE); if (g_application_register (G_APPLICATION (app), NULL, &error) == FALSE) { g_warning ("Could not register application: %s", error->message); g_error_free (error); return 1; } if (g_application_get_is_remote (G_APPLICATION (app))) { g_application_activate (G_APPLICATION (app)); gdk_notify_startup_complete (); return 0; } gtk_window_set_default_icon_name("bluetooth"); client = bluetooth_client_new(); agent = bluetooth_agent_new(); if (bluetooth_agent_register(agent) == FALSE) return 1; g_object_add_weak_pointer (G_OBJECT (agent), (gpointer *) (&agent)); bluetooth_agent_set_pincode_func(agent, pincode_callback, NULL); bluetooth_agent_set_display_func(agent, display_callback, NULL); bluetooth_agent_set_display_pincode_func(agent, display_pincode_callback, NULL); bluetooth_agent_set_cancel_func(agent, cancel_callback, NULL); bluetooth_agent_set_confirm_func(agent, confirm_callback, NULL); bluetooth_agent_set_authorize_func(agent, authorize_callback, NULL); bluetooth_agent_set_authorize_service_func(agent, authorize_service_callback, NULL); bluetooth_agent_setup(agent, AGENT_PATH); if (create_wizard() == FALSE) return 1; gtk_application_add_window (app, GTK_WINDOW (window_assistant)); g_signal_connect (app, "activate", G_CALLBACK (activate_cb), window_assistant); g_application_run (G_APPLICATION (app), argc, argv); if (agent != NULL) g_object_unref (agent); g_object_unref(client); g_object_unref(app); return 0; }