在spice-gtk-0.30內(nèi)部有一個(gè)spicy命令用于鏈接遠(yuǎn)程的虛擬機(jī), 例如spicy -h 172.16.3.4 -p 6700 這樣就可以鏈接到虛擬桌面 其客戶端有個(gè)USB 設(shè)備重定向管理功能,在spicy內(nèi)有個(gè)Input選項(xiàng)卡,其子項(xiàng)有個(gè)“Select USB Devices for redirection”。選擇重定向USB 設(shè)備,點(diǎn)開(kāi)后就會(huì)出現(xiàn)列有USB設(shè)備的對(duì)話礦對(duì)話框,就可以選擇USB 設(shè)備進(jìn)行重定向。其如下圖所示:
彈出的對(duì)話況如下所示:
可以看到所有的USB設(shè)備都一GTK CheckButton的形式列出來(lái)了,那么當(dāng)選中里面的USB設(shè)備時(shí),本地的USB設(shè)備就會(huì)被重定向到虛擬桌面Win7下面: 其實(shí)現(xiàn)的流程如下: 
相關(guān)源文件:spicy.c、usb-device-manager.c、channel-usbredir.c、usb-device-widget.c 在usb-device-widget.c中的代碼如下:
static GObject *spice_usb_device_widget_constructor( GType gtype, guint n_PRoperties, GObjectConstructParam *properties){ GObject *obj; SpiceUsbDeviceWidget *self; SpiceUsbDeviceWidgetPrivate *priv; GPtrArray *devices = NULL; GError *err = NULL; GtkWidget *label; gchar *str; int i;printf("spice_usb_device_widget_constructor-------------usb-device-widget.c/n"); { /* Always chain up to the parent constructor */ GObjectClass *parent_class; parent_class = G_OBJECT_CLASS(spice_usb_device_widget_parent_class); obj = parent_class->constructor(gtype, n_properties, properties); } self = SPICE_USB_DEVICE_WIDGET(obj); priv = self->priv; if (!priv->session) g_error("SpiceUsbDeviceWidget constructed without a session"); label = gtk_label_new(NULL);//創(chuàng)建lable控件 str = g_strdup_printf("<b>%s</b>", _("Select USB devices to redirect"));//控件的內(nèi)容 gtk_label_set_markup(GTK_LABEL (label), str);//設(shè)置lable字體屬性 g_free(str); gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);//設(shè)置對(duì)齊 gtk_box_pack_start(GTK_BOX(self), label, FALSE, FALSE, 0);//啟動(dòng)容器 priv->manager = spice_usb_device_manager_get(priv->session, &err);//從session中獲取manager if (err) { spice_usb_device_widget_show_info_bar(self, err->message, GTK_MESSAGE_WARNING, GTK_STOCK_DIALOG_WARNING); g_clear_error(&err); return obj; } g_signal_connect(priv->manager, "device-added", G_CALLBACK(device_added_cb), self);//設(shè)置回調(diào)函數(shù)用于處理USB熱插拔 g_signal_connect(priv->manager, "device-removed", G_CALLBACK(device_removed_cb), self); g_signal_connect(priv->manager, "device-error", G_CALLBACK(device_error_cb), self);//從manager內(nèi)獲取device列表是根據(jù)filter去獲取過(guò)濾USB設(shè)備規(guī)則,但是此處是將所有的USB設(shè)備都呈現(xiàn),因此filter為空 devices = spice_usb_device_manager_get_devices(priv->manager); if (!devices) goto end; for (i = 0; i < devices->len; i++)//以gtk內(nèi)checkbutton控件的模式列出所有設(shè)備 { device_added_cb(NULL, g_ptr_array_index(devices, i), self); printf("xxxxxxxxxxxxxxx--usb-device-widget.c/n"); } g_ptr_array_unref(devices);//清空devicesend: spice_usb_device_widget_update_status(self);//更新控件狀態(tài) return obj;}在對(duì)話框構(gòu)建constructor內(nèi)會(huì)為每個(gè)USB設(shè)備建立CheckButton控件,如下:
static void device_added_cb(SpiceUsbDeviceManager *manager, SpiceUsbDevice *device, gpointer user_data){ SpiceUsbDeviceWidget *self = SPICE_USB_DEVICE_WIDGET(user_data); SpiceUsbDeviceWidgetPrivate *priv = self->priv; GtkWidget *align, *check; gchar *desc;printf("device_added_cb-----usb-device-widget.c/n"); desc = spice_usb_device_get_description(device,//獲取usb設(shè)備描述符 priv->device_format_string); check = gtk_check_button_new_with_label(desc);//創(chuàng)建checkbutton g_free(desc);//創(chuàng)建完畢后就釋放掉 if (spice_usb_device_manager_is_device_connected(priv->manager,//檢測(cè)usb設(shè)備是否鏈接狀態(tài)如果是連接的就設(shè)置checkbutton為真 device)) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), TRUE);//設(shè)置checkbutton為T(mén)ure g_object_set_data_full(//設(shè)置數(shù)據(jù)和回調(diào)函數(shù) G_OBJECT(check), "usb-device", g_boxed_copy(spice_usb_device_get_type(), device), checkbox_usb_device_destroy_notify); g_signal_connect(G_OBJECT(check), "clicked",//注冊(cè)點(diǎn)擊checkbutton事件 G_CALLBACK(checkbox_clicked_cb), self);//當(dāng)點(diǎn)擊事件觸發(fā)時(shí)調(diào)用回調(diào)函數(shù)checkbox_clicked_cb align = gtk_alignment_new(0, 0, 0, 0);//創(chuàng)建對(duì)齊控件 gtk_alignment_set_padding(GTK_ALIGNMENT(align), 0, 0, 12, 0); gtk_container_add(GTK_CONTAINER(align), check);//在checkbutton中添加對(duì)齊方式 gtk_box_pack_end(GTK_BOX(self), align, FALSE, FALSE, 0); spice_usb_device_widget_update_status(self);//更新usb控件狀態(tài) gtk_widget_show_all(align);}在建立成功后注冊(cè)CheckButton的選中點(diǎn)擊事件:
static void checkbox_clicked_cb(GtkWidget *check, gpointer user_data){ SpiceUsbDeviceWidget *self = SPICE_USB_DEVICE_WIDGET(user_data); SpiceUsbDeviceWidgetPrivate *priv = self->priv; SpiceUsbDevice *device; device = g_object_get_data(G_OBJECT(check), "usb-device");//獲取數(shù)據(jù) if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check))) {//獲取checkbutton的狀態(tài),為T(mén)ure connect_cb_data *data = g_new(connect_cb_data, 1); data->check = g_object_ref(check); data->self = g_object_ref(self); printf("checkbox_clicked_cb---true-------usb-device-widget.c/n"); spice_usb_device_manager_connect_device_async(priv->manager,//重定向設(shè)備 device, NULL, connect_cb, data); } else {//如果狀態(tài)為False則斷開(kāi)鏈接 printf("checkbox_clicked_cb---false-------usb-device-widget.c/n"); spice_usb_device_manager_disconnect_device(priv->manager,//斷開(kāi)channel與device device); } spice_usb_device_widget_update_status(self);}到此兩個(gè)事件,一個(gè)選中事件則調(diào)用spice_usb_device_manager_connect_device_async對(duì)該USB 設(shè)備俄進(jìn)行重定向 另一個(gè)取消選中,就會(huì)掉用spice_usb_device_manager_disconnect_device,斷開(kāi)channel和device的鏈接,取消重定向,
一先看看簡(jiǎn)單的取消的過(guò)程:這個(gè)函數(shù)在usb-device-manager.c文件內(nèi)定義如下:
void spice_usb_device_manager_disconnect_device(SpiceUsbDeviceManager *self, SpiceUsbDevice *device){ g_return_if_fail(SPICE_IS_USB_DEVICE_MANAGER(self)); g_return_if_fail(device != NULL);//device為空退出 SPICE_DEBUG("disconnecting device %p", device);printf("spice_usb_device_manager_disconnect_device,disconnecting device %p ------usb-device-manager.c/n",device);#ifdef USE_USBREDIR SpiceUsbredirChannel *channel; channel = spice_usb_device_manager_get_channel_for_dev(self, device);//根據(jù)device獲取channel if (channel) spice_usbredir_channel_disconnect_device(channel);//清空channel內(nèi)的相關(guān)device并設(shè)置狀態(tài) #ifdef G_OS_WIN32 //沒(méi)有在windows下使用,因此代碼省略注銷 #endif #endif}在這個(gè)函數(shù)內(nèi)部首先根據(jù)device獲取相匹配的Channel,在對(duì)Channel內(nèi)的device進(jìn)行清空:
static SpiceUsbredirChannel *spice_usb_device_manager_get_channel_for_dev( SpiceUsbDeviceManager *manager, SpiceUsbDevice *device){#ifdef USE_USBREDIR SpiceUsbDeviceManagerPrivate *priv = manager->priv; guint i; for (i = 0; i < priv->channels->len; i++) {//獲取device相對(duì)應(yīng)的channel SpiceUsbredirChannel *channel = g_ptr_array_index(priv->channels, i);//獲取channel libusb_device *libdev = spice_usbredir_channel_get_device(channel); if (spice_usb_device_equal_libdev(device, libdev))//如果兩者匹配就返回channel return channel;//返回device對(duì)應(yīng)的channel }#endif return NULL;}清空函數(shù)spice_usbredir_channel_disconnect_device在channel-usbredir.c內(nèi)部
void spice_usbredir_channel_disconnect_device(SpiceUsbredirChannel *channel){ SpiceUsbredirChannelPrivate *priv = channel->priv; CHANNEL_DEBUG(channel, "disconnecting device from usb channel %p", channel); printf("spice_usbredir_channel_disconnect_device, priv->state=%d---------channel-usbredir.c/n",priv->state); switch (priv->state) { case STATE_DISCONNECTED: case STATE_DISCONNECTING: break;#if USE_POLKIT case STATE_WAITING_FOR_ACL_HELPER: priv->state = STATE_DISCONNECTING; /* We're still waiting for the acl helper -> cancel it */ spice_usb_acl_helper_close_acl(priv->acl_helper); break;#endif case STATE_CONNECTED://設(shè)備處于鏈接狀態(tài) /* * This sets the usb event thread run condition to FALSE, therefor * it must be done before usbredirhost_set_device NULL, as * usbredirhost_set_device NULL will interrupt the * libusb_handle_events call in the thread. */ printf("STATE_CONNECTED --------channel-usbredir.c/n"); { SpiceSession *session = spice_channel_get_session(SPICE_CHANNEL(channel));//從SpiceChannel中獲取session if (session != NULL) spice_usb_device_manager_stop_event_listening(//停止監(jiān)聽(tīng) spice_usb_device_manager_get(session, NULL));//獲取USBmanager即從SpiceSession獲取SpiceUsbDeviceManager } /* This also closes the libusb handle we passed from open_device */ usbredirhost_set_device(priv->host, NULL);//清空usbhost libusb_unref_device(priv->device);//去掉引用計(jì)數(shù) priv->device = NULL;//清空設(shè)備 g_boxed_free(spice_usb_device_get_type(), priv->spice_device);//清空 priv->spice_device = NULL; priv->state = STATE_DISCONNECTED;//設(shè)置狀態(tài)為斷開(kāi)狀態(tài) break; }}可以看到在該channel內(nèi)的device被清空了,首先就是要告知manager停止對(duì)該device的事件監(jiān)聽(tīng),退出這個(gè)device的事件線程,
spice_usb_device_manager_stop_event_listening如下:void spice_usb_device_manager_stop_event_listening( SpiceUsbDeviceManager *self){ SpiceUsbDeviceManagerPrivate *priv = self->priv; g_return_if_fail(priv->event_listeners > 0); priv->event_listeners--;//運(yùn)行監(jiān)聽(tīng)計(jì)數(shù)減1 printf("priv->event_listeners--= %d-----usb-device-manager.c/n",priv->event_listeners); if (priv->event_listeners == 0) priv->event_thread_run = FALSE;//設(shè)置線程運(yùn)行開(kāi)關(guān)為FALSE用來(lái)關(guān)閉該線程}新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注