Hi On Tue, Sep 18, 2018 at 8:05 PM Yuri Benditovich <yuri.benditov...@daynix.com> wrote: > > > > On Tue, Sep 18, 2018 at 12:15 PM, Marc-André Lureau > <marcandre.lur...@gmail.com> wrote: >> >> Hi >> >> On Tue, Sep 18, 2018 at 12:04 PM Yuri Benditovich >> <yuri.benditov...@daynix.com> wrote: >> > >> > >> > >> > On Tue, Sep 18, 2018 at 9:10 AM, Victor Toso <victort...@redhat.com> wrote: >> >> >> >> On Mon, Sep 17, 2018 at 04:23:02PM +0300, Yuri Benditovich wrote: >> >> > New USB widget supports all the functionality related to >> >> > redirection of local USB device (as previous usb widget did). >> >> > Additionally it allows creation, management and redirection of >> >> > emulated USB CD devices for CD sharing. >> >> >> >> Wouldn't be possible to extend the current widget instead of >> >> creating a new one? >> > >> > >> > Of course it was the first idea before creating new one. >> > After several discussions we understood that there is almost nothing >> > common between what we'd like to have for CD and what we have in existing >> > widget. >> > This also was decided on special meeting of Alexander with spice team >> > (remove >> > some controls and split it to two lists, locals and CD). Earlier the >> > screenshot of >> > the latest widget (current code) was sent to internal list for review and >> > no objections >> > were received. >> >> Ultimately the discussion and decision should take place on public mailing >> list. >> >> And many of us (including me), may not be directly involved in the RH >> spice team. >> >> Could you detail the goals you had and why you had to write a new widget? > > > Regarding new USB widget: > > User experience when USB CD drive is presented to user is wider than just > plug/unplug the USB device. > > The CD may include several units, each of them can be managed separately. > If more than one unit supported on CD, single CD with number of units requires > just one redirection channel. [At the moment due to time restrictions, we > define > CD as single unit, but I think in the future we will want to enable multiple > units on CD] > So in common case the GUI for CD drives tends to be something like tree view. > This easily serves also local USB devices as in GTK tree view == list view. > Initially the widget was developed with single list containing all the devices > when shared CD devices have leaves and local USB devices do not. > IMHO, this kind of presentation is better than current one which presents > local devices > and CD devices separately.
Ok, let say I am biased, and I have opinions because I would use it and this is stuff I am also maintaining: - I have never used a pluggable CD drive. - I have never used mulitple CD drive simultaneously. > When CD drive is presented to the user on guest machine, it can be ejected by > user action (from Gnome or Explorer). When the media is ejected, it can be > changed > on client side. So the status (ejected/loaded) should be visible and the user > should be > able to control this also on client side exactly as you can open/close the > drive door manually. > > Drive lock. When CD is reqired for SW operation, the SW often locks > the drive preventing user-initiated eject. In such case, if needed, the CD > still can be > manually ejected using clip. Another option related to 'lock' is that user > may want > to prevent removal of CD by guest software and lock it on client side. - I have never used locking either. > > The user shall select the properties for logical CD unit and it is good to > show them > in the GUI. They include (at least): > * file name (or device name) used as media That's useful indeed > * device attributes (vendor/product) which are used to report properties of > SCSI device > (these properties are visible to user in seabios boot screen, so in case of 2 > CDs the user > can recognize the needed one). Note that 2 CDs is typical case for Windows > installation > on VM with virtio. You can switch the mounted CD for this case, no need for multiple simultaneously mounted CD. I don't know a case where this is necessary, except perhaps for direct CD copy, that used to be very risky anyway.. > * Whether the CD media is currently loaded > > Visible status of the device is (IMHO) good for self support. When something > does not > work as expected and you can't see anything in the GUI that can explain this, > the next step > is opening issues, sending logs etc., i.e. spend own time and time of other > people. > I disagree with the statement 'less GUI is better'. Definitely, less GUI is > simpler, > but this simplicity often increases cost of ownership. The UI in Boxes is doing the job quite fine for average user. See attached screenshot. > > Our solid opinion is that even current CD additions are too heavy for > existing widget. In my opinion, we can make it much more user friendly. But we need to understand what we want to support first. Why spice-gtk cd redirection would need to be more complex than what Boxes or virt-manager provide? > > Thanks, > Yuri > >> >> >> For UI, it's best to focus on the supported use case & the user >> story/experience, and avoid feature creep (usually the less UI, the >> better). >> >> > >> >> >> >> >> >> > Signed-off-by: Alexander Nezhinsky <alexan...@daynix.com> >> >> > Signed-off-by: Yuri Benditovich <yuri.benditov...@daynix.com> >> >> > --- >> >> > src/usb-device-redir-widget.c | 2065 >> >> > +++++++++++++++++++++++++++++++++++++++++ >> >> > 1 file changed, 2065 insertions(+) >> >> > create mode 100644 src/usb-device-redir-widget.c >> >> > >> >> > diff --git a/src/usb-device-redir-widget.c >> >> > b/src/usb-device-redir-widget.c >> >> > new file mode 100644 >> >> > index 0000000..59a6043 >> >> > --- /dev/null >> >> > +++ b/src/usb-device-redir-widget.c >> >> > @@ -0,0 +1,2065 @@ >> >> > +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */ >> >> > +/* >> >> > + Copyright (C) 2012 Red Hat, Inc. >> >> > + >> >> > + Red Hat Authors: >> >> > + Alexander Nezhinsky<anezh...@redhat.com> >> >> > + >> >> > + This library is free software; you can redistribute it and/or >> >> > + modify it under the terms of the GNU Lesser General Public >> >> > + License as published by the Free Software Foundation; either >> >> > + version 2.1 of the License, or (at your option) any later version. >> >> > + >> >> > + This library 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 >> >> > + Lesser General Public License for more details. >> >> > + >> >> > + You should have received a copy of the GNU Lesser General Public >> >> > + License along with this library; if not, see >> >> > <http://www.gnu.org/licenses/>. >> >> > +*/ >> >> > + >> >> > +#include "config.h" >> >> > +#ifndef USB_WIDGET_TEST >> >> > + #include <glib/gi18n-lib.h> >> >> > + #include "spice-client.h" >> >> > + #include "spice-marshal.h" >> >> > +#else >> >> > + #include "spice-client.h" >> >> > +#endif >> >> > +#include "usb-device-widget.h" >> >> > + >> >> > +/* >> >> > + Debugging note: >> >> > + Logging from this module is not affected by --spice-debug >> >> > + command line parameter >> >> > + Use SPICE_DEBUG=1 environment varible to enable logs >> >> > +*/ >> >> > + >> >> > +#ifdef USE_NEW_USB_WIDGET >> >> > + >> >> > +/** >> >> > + * SECTION:usb-device-widget >> >> > + * @short_description: USB device selection widget >> >> > + * @title: Spice USB device selection widget >> >> > + * @section_id: >> >> > + * @see_also: >> >> > + * @stability: Under development >> >> > + * @include: spice-client-gtk.h >> >> > + * >> >> > + * #SpiceUsbDeviceWidget is a gtk widget which apps can use to easily >> >> > + * add an UI to select USB devices to redirect (or unredirect). >> >> > + */ >> >> > + >> >> > +struct _SpiceUsbDeviceWidget >> >> > +{ >> >> > + GtkBox parent; >> >> > + >> >> > + SpiceUsbDeviceWidgetPrivate *priv; >> >> > +}; >> >> > + >> >> > +struct _SpiceUsbDeviceWidgetClass >> >> > +{ >> >> > + GtkBoxClass parent_class; >> >> > + >> >> > + /* signals */ >> >> > + void (*connect_failed) (SpiceUsbDeviceWidget *widget, >> >> > + SpiceUsbDevice *device, GError *error); >> >> > +}; >> >> > + >> >> > +/* ------------------------------------------------------------------ >> >> > */ >> >> > +/* Prototypes for callbacks */ >> >> > +static void device_added_cb(SpiceUsbDeviceManager *manager, >> >> > + SpiceUsbDevice *device, gpointer user_data); >> >> > +static void device_removed_cb(SpiceUsbDeviceManager *manager, >> >> > + SpiceUsbDevice *device, gpointer user_data); >> >> > +static void device_changed_cb(SpiceUsbDeviceManager *manager, >> >> > + SpiceUsbDevice *device, gpointer user_data); >> >> > +static void device_error_cb(SpiceUsbDeviceManager *manager, >> >> > + SpiceUsbDevice *device, GError *err, gpointer user_data); >> >> > +static gboolean spice_usb_device_widget_update_status(gpointer >> >> > user_data); >> >> > + >> >> > +/* ------------------------------------------------------------------ >> >> > */ >> >> > +/* gobject glue >> >> > */ >> >> > + >> >> > +#define SPICE_USB_DEVICE_WIDGET_GET_PRIVATE(obj) \ >> >> > + (G_TYPE_INSTANCE_GET_PRIVATE((obj), SPICE_TYPE_USB_DEVICE_WIDGET, \ >> >> > + SpiceUsbDeviceWidgetPrivate)) >> >> > + >> >> > +enum { >> >> > + PROP_0, >> >> > + PROP_SESSION, >> >> > + PROP_DEVICE_FORMAT_STRING, >> >> > +}; >> >> > + >> >> > +enum { >> >> > + CONNECT_FAILED, >> >> > + LAST_SIGNAL, >> >> > +}; >> >> > + >> >> > +typedef struct { >> >> > + GtkTreeView *tree_view; >> >> > + GtkTreeStore *tree_store; >> >> > +} SpiceUsbDeviceWidgetTree; >> >> > + >> >> > +struct _SpiceUsbDeviceWidgetPrivate { >> >> > + SpiceSession *session; >> >> > + gchar *device_format_string; >> >> > + SpiceUsbDeviceManager *manager; >> >> > + GtkWidget *info_bar; >> >> > + GtkWidget *label; >> >> > + SpiceUsbDeviceWidgetTree cd_tree; >> >> > + SpiceUsbDeviceWidgetTree usb_tree; >> >> > + GdkPixbuf *icon_cd; >> >> > + GdkPixbuf *icon_connected; >> >> > + GdkPixbuf *icon_disconn; >> >> > + GdkPixbuf *icon_warning; >> >> > + GdkPixbuf *icon_info; >> >> > + gchar *err_msg; >> >> > + gsize device_count; >> >> > +}; >> >> > + >> >> > +static guint signals[LAST_SIGNAL] = { 0, }; >> >> > + >> >> > +G_DEFINE_TYPE(SpiceUsbDeviceWidget, spice_usb_device_widget, >> >> > GTK_TYPE_BOX); >> >> > + >> >> > +/* TREE */ >> >> > + >> >> > +enum column_id >> >> > +{ >> >> > + COL_REDIRECT = 0, >> >> > + COL_ADDRESS, >> >> > + COL_CONNECT_ICON, >> >> > + COL_CD_ICON, >> >> > + COL_VENDOR, >> >> > + COL_PRODUCT, >> >> > + COL_FILE, >> >> > + COL_LOADED, >> >> > + COL_LOCKED, >> >> > + COL_IDLE, >> >> > + /* internal columns */ >> >> > + COL_REVISION, >> >> > + COL_CD_DEV, >> >> > + COL_LUN_ITEM, >> >> > + COL_DEV_ITEM, >> >> > + COL_ITEM_DATA, >> >> > + COL_CONNECTED, >> >> > + COL_CAN_REDIRECT, >> >> > + COL_ROW_COLOR, >> >> > + COL_ROW_COLOR_SET, >> >> > + NUM_COLS, >> >> > + >> >> > + INVALID_COL >> >> > +}; >> >> > + >> >> > +// there is a possibility to use different names >> >> > +// for columns in USB device list and CD device list, if needed >> >> > +// currently they are identical >> >> > +static const char *column_names_cd[NUM_COLS]; >> >> > +static const char *column_names_usb[NUM_COLS]; >> >> > + >> >> > +#define SET_COLUMN(n, s) column_names_cd[n] = column_names_usb[n] = s >> >> > + >> >> > +static void initialize_columns(void) >> >> > +{ >> >> > + SET_COLUMN(COL_REDIRECT, _("Redirect")); >> >> > + SET_COLUMN(COL_ADDRESS, _("Address")); >> >> > + SET_COLUMN(COL_CONNECT_ICON, _("Conn")); >> >> > + SET_COLUMN(COL_CD_ICON, _("CD")); >> >> > + SET_COLUMN(COL_VENDOR, _("Vendor")); >> >> > + SET_COLUMN(COL_PRODUCT, _("Product")); >> >> > + SET_COLUMN(COL_FILE, _("File/Device Path")); >> >> > + SET_COLUMN(COL_LOADED, _("Loaded")); >> >> > + SET_COLUMN(COL_LOCKED, _("Locked")); >> >> > + SET_COLUMN(COL_IDLE, _("Idle")); >> >> > + SET_COLUMN(COL_REVISION, "?Revision"); >> >> > + SET_COLUMN(COL_CD_DEV, "?CD_DEV"); >> >> > + SET_COLUMN(COL_LUN_ITEM, "?LUN_ITEM"); >> >> > + SET_COLUMN(COL_DEV_ITEM, "?DEV_ITEM"); >> >> > + SET_COLUMN(COL_ITEM_DATA, "?ITEM_DATA"); >> >> > + SET_COLUMN(COL_CONNECTED, "?CONNECTED"); >> >> > + SET_COLUMN(COL_CAN_REDIRECT, "?CAN_REDIRECT"); >> >> > + SET_COLUMN(COL_ROW_COLOR, "?ROW_COLOR"); >> >> > + SET_COLUMN(COL_ROW_COLOR_SET, "?ROW_COLOR_SET"); >> >> > +}; >> >> > + >> >> > +static const char *column_name(enum column_id id, gboolean is_cd) >> >> > +{ >> >> > + const char **col_name = is_cd ? column_names_cd : column_names_usb; >> >> > + return col_name[id]; >> >> > +} >> >> > + >> >> > +typedef struct _UsbWidgetLunItem { >> >> > + SpiceUsbDeviceManager *manager; >> >> > + SpiceUsbDevice *device; >> >> > + guint lun; >> >> > + SpiceUsbDeviceLunInfo info; >> >> > +} UsbWidgetLunItem; >> >> > + >> >> > +typedef struct _TreeFindUsbDev { >> >> > + SpiceUsbDevice *usb_dev; >> >> > + GtkTreeIter dev_iter; >> >> > +} TreeFindUsbDev; >> >> > + >> >> > +typedef void (*tree_item_toggled_cb)(GtkCellRendererToggle *, gchar *, >> >> > gpointer); >> >> > + >> >> > +static void usb_widget_add_device(SpiceUsbDeviceWidget *self, >> >> > + SpiceUsbDevice *usb_device, >> >> > + GtkTreeIter *old_dev_iter); >> >> > + >> >> > +static gchar *usb_device_description(SpiceUsbDeviceManager *manager, >> >> > + SpiceUsbDevice *device, >> >> > + const gchar *format) >> >> > +{ >> >> > + SpiceUsbDeviceDescription desc; >> >> > + gchar *descriptor; >> >> > + gchar *res; >> >> > + spice_usb_device_get_info(manager, device, &desc); >> >> > + descriptor = g_strdup_printf("[%04x:%04x]", desc.vendor_id, >> >> > desc.product_id); >> >> > + if (!format) { >> >> > + format = _("%s %s %s at %d-%d"); >> >> > + } >> >> > + res = g_strdup_printf(format, desc.vendor, desc.product, >> >> > descriptor, desc.bus, desc.address); >> >> > + g_free(desc.vendor); >> >> > + g_free(desc.product); >> >> > + g_free(descriptor); >> >> > + return res; >> >> > +} >> >> > + >> >> > +static GtkTreeStore* usb_widget_create_tree_store(void) >> >> > +{ >> >> > + GtkTreeStore *tree_store; >> >> > + >> >> > + tree_store = gtk_tree_store_new(NUM_COLS, >> >> > + G_TYPE_BOOLEAN, /* COL_REDIRECT */ >> >> > + G_TYPE_STRING, /* COL_ADDRESS */ >> >> > + GDK_TYPE_PIXBUF, /* COL_CONNECT_ICON */ >> >> > + GDK_TYPE_PIXBUF, /* COL_CD_ICON */ >> >> > + G_TYPE_STRING, /* COL_VENDOR */ >> >> > + G_TYPE_STRING, /* COL_PRODUCT */ >> >> > + G_TYPE_STRING, /* COL_FILE */ >> >> > + G_TYPE_BOOLEAN, /* COL_LOADED */ >> >> > + G_TYPE_BOOLEAN, /* COL_LOCKED */ >> >> > + G_TYPE_BOOLEAN, /* COL_IDLE */ >> >> > + /* internal columns */ >> >> > + G_TYPE_STRING, /* COL_REVISION */ >> >> > + G_TYPE_BOOLEAN, /* COL_CD_DEV */ >> >> > + G_TYPE_BOOLEAN, /* COL_LUN_ITEM */ >> >> > + G_TYPE_BOOLEAN, /* COL_DEV_ITEM */ >> >> > + G_TYPE_POINTER, /* COL_ITEM_DATA */ >> >> > + G_TYPE_BOOLEAN, /* COL_CONNECTED */ >> >> > + G_TYPE_BOOLEAN, /* COL_CAN_REDIRECT */ >> >> > + G_TYPE_STRING, /* COL_ROW_COLOR */ >> >> > + G_TYPE_BOOLEAN /* COL_ROW_COLOR_SET */ ); >> >> > + SPICE_DEBUG("tree store created"); >> >> > + >> >> > + return tree_store; >> >> > +} >> >> > + >> >> > +static GdkPixbuf *get_named_icon(const gchar *name, gint size) >> >> > +{ >> >> > + GtkIconInfo *info = >> >> > gtk_icon_theme_lookup_icon(gtk_icon_theme_get_default(), name, size, 0); >> >> > + GdkPixbuf *pixbuf = gtk_icon_info_load_icon(info, NULL); >> >> > + g_object_unref (info); >> >> > + return pixbuf; >> >> > +} >> >> > + >> >> > +static void select_widget_size(GtkWidget *wg) >> >> > +{ >> >> > + GdkDisplay *d = gtk_widget_get_display(wg); >> >> > + int i, w = 2000, h = 1024; >> >> > + int n = gdk_display_get_n_monitors(d); >> >> > + for (i = 0; i < n; ++i) >> >> > + { >> >> > + GdkMonitor *m = gdk_display_get_monitor(d, i); >> >> > + if (m) { >> >> > + GdkRectangle area; >> >> > + gdk_monitor_get_workarea(m, &area); >> >> > + SPICE_DEBUG("monitor %d: %d x %d @( %d, %d)", >> >> > + i, area.width, area.height, area.x, area.y ); >> >> > + w = MIN(w, area.width); >> >> > + h = MIN(h, area.height); >> >> > + } >> >> > + } >> >> > + >> >> > + w = (w * 3) / 4; >> >> > + h = h / 2; >> >> > + >> >> > + SPICE_DEBUG("sizing widget as %d x %d", w, h); >> >> > + gtk_widget_set_size_request(wg, w, h); >> >> > +} >> >> > + >> >> > +static void usb_widget_add_device(SpiceUsbDeviceWidget *self, >> >> > + SpiceUsbDevice *usb_device, >> >> > + GtkTreeIter *old_dev_iter) >> >> > +{ >> >> > + SpiceUsbDeviceWidgetPrivate *priv = self->priv; >> >> > + SpiceUsbDeviceManager *usb_dev_mgr = priv->manager; >> >> > + SpiceUsbDeviceWidgetTree *tree; >> >> > + GtkTreeView *tree_view; >> >> > + GtkTreeStore *tree_store; >> >> > + GtkTreeIter new_dev_iter; >> >> > + SpiceUsbDeviceDescription dev_info; >> >> > + GtkTreePath *new_dev_path; >> >> > + gboolean is_dev_redirected, is_dev_connected, is_dev_cd; >> >> > + gchar *addr_str; >> >> > + GArray *lun_array; >> >> > + guint lun_index; >> >> > + GError *error = NULL; >> >> > + >> >> > + is_dev_cd = spice_usb_device_manager_is_device_cd(usb_dev_mgr, >> >> > usb_device); >> >> > + tree = is_dev_cd ? &priv->cd_tree : &priv->usb_tree; >> >> > + tree_view = tree->tree_view; >> >> > + tree_store = tree->tree_store; >> >> > + >> >> > + if (old_dev_iter == NULL) { >> >> > + gtk_tree_store_append(tree_store, &new_dev_iter, NULL); >> >> > + } else { >> >> > + gtk_tree_store_insert_after(tree_store, &new_dev_iter, NULL, >> >> > old_dev_iter); >> >> > + gtk_tree_store_remove(tree_store, old_dev_iter); >> >> > + } >> >> > + >> >> > + spice_usb_device_get_info(usb_dev_mgr, usb_device, &dev_info); >> >> > + addr_str = g_strdup_printf("%d:%d", (gint)dev_info.bus, >> >> > (gint)dev_info.address); >> >> > + is_dev_connected = >> >> > spice_usb_device_manager_is_device_connected(usb_dev_mgr, usb_device); >> >> > + is_dev_redirected = is_dev_connected; >> >> > + SPICE_DEBUG("usb device a:[%s] p:[%s] m:[%s] conn:%d cd:%d", >> >> > + addr_str, dev_info.vendor, dev_info.product, is_dev_connected, >> >> > is_dev_cd); >> >> > + >> >> > + gtk_tree_store_set(tree_store, &new_dev_iter, >> >> > + COL_REDIRECT, is_dev_redirected, >> >> > + COL_ADDRESS, addr_str, >> >> > + COL_CONNECT_ICON, is_dev_connected ? priv->icon_connected : >> >> > priv->icon_disconn, >> >> > + COL_CD_ICON, priv->icon_cd, >> >> > + COL_VENDOR, dev_info.vendor, >> >> > + COL_PRODUCT, dev_info.product, >> >> > + COL_CD_DEV, is_dev_cd, >> >> > + COL_LUN_ITEM, FALSE, /* USB device item */ >> >> > + COL_DEV_ITEM, TRUE, /* USB device item */ >> >> > + COL_ITEM_DATA, (gpointer)usb_device, >> >> > + COL_CONNECTED, is_dev_connected, >> >> > + COL_CAN_REDIRECT, >> >> > spice_usb_device_manager_can_redirect_device(usb_dev_mgr, usb_device, >> >> > &error), >> >> > + COL_ROW_COLOR, "beige", >> >> > + COL_ROW_COLOR_SET, TRUE, >> >> > + -1); >> >> > + g_clear_error(&error); >> >> > + >> >> > + priv->device_count++; >> >> > + >> >> > + /* get all the luns */ >> >> > + lun_array = spice_usb_device_manager_get_device_luns(usb_dev_mgr, >> >> > usb_device); >> >> > + for (lun_index = 0; lun_index < lun_array->len; lun_index++) { >> >> > + UsbWidgetLunItem *lun_item; >> >> > + GtkTreeIter lun_iter; >> >> > + gchar lun_str[8]; >> >> > + >> >> > + lun_item = g_malloc(sizeof(*lun_item)); >> >> > + lun_item->manager = usb_dev_mgr; >> >> > + lun_item->device = usb_device; >> >> > + lun_item->lun = g_array_index(lun_array, guint, lun_index); >> >> > + spice_usb_device_manager_device_lun_get_info(usb_dev_mgr, >> >> > usb_device, lun_item->lun, &lun_item->info); >> >> > + SPICE_DEBUG("lun %u v:[%s] p:[%s] r:[%s] file:[%s] >> >> > lun_item:%p", >> >> > + lun_index, lun_item->info.vendor, >> >> > lun_item->info.product, >> >> > + lun_item->info.revision, lun_item->info.file_path, >> >> > lun_item); >> >> > + g_snprintf(lun_str, 8, "↳%u", lun_item->lun); >> >> > + >> >> > + /* Append LUN as a child of USB device */ >> >> > + gtk_tree_store_append(tree_store, &lun_iter, &new_dev_iter); >> >> > + gtk_tree_store_set(tree_store, &lun_iter, >> >> > + COL_ADDRESS, lun_str, >> >> > + COL_VENDOR, lun_item->info.vendor, >> >> > + COL_PRODUCT, lun_item->info.product, >> >> > + COL_REVISION, lun_item->info.revision, >> >> > + COL_FILE, lun_item->info.file_path, >> >> > + COL_LOADED, lun_item->info.loaded, >> >> > + COL_LOCKED, lun_item->info.locked, >> >> > + COL_IDLE, !lun_item->info.started, >> >> > + COL_CD_DEV, FALSE, >> >> > + COL_LUN_ITEM, TRUE, /* LUN item */ >> >> > + COL_DEV_ITEM, FALSE, /* LUN item */ >> >> > + COL_ITEM_DATA, (gpointer)lun_item, >> >> > + COL_CONNECTED, is_dev_connected, >> >> > + COL_ROW_COLOR, "azure", >> >> > + COL_ROW_COLOR_SET, TRUE, >> >> > + -1); >> >> > + } >> >> > + g_array_unref(lun_array); >> >> > + >> >> > + new_dev_path = gtk_tree_model_get_path(GTK_TREE_MODEL(tree_store), >> >> > &new_dev_iter); >> >> > + gtk_tree_view_expand_row(tree_view, new_dev_path, FALSE); >> >> > + gtk_tree_path_free(new_dev_path); >> >> > + >> >> > + g_free(dev_info.vendor); >> >> > + g_free(dev_info.product); >> >> > + g_free(addr_str); >> >> > +} >> >> > + >> >> > +static gboolean tree_item_is_lun(GtkTreeStore *tree_store, GtkTreeIter >> >> > *iter) >> >> > +{ >> >> > + gboolean is_lun; >> >> > + gtk_tree_model_get(GTK_TREE_MODEL(tree_store), iter, COL_LUN_ITEM, >> >> > &is_lun, -1); >> >> > + return is_lun; >> >> > +} >> >> > + >> >> > +static gboolean >> >> > usb_widget_tree_store_find_usb_dev_foreach_cb(GtkTreeModel *tree_model, >> >> > + >> >> > GtkTreePath *path, GtkTreeIter *iter, >> >> > + gpointer >> >> > user_data) >> >> > +{ >> >> > + TreeFindUsbDev *find_info = (TreeFindUsbDev *)user_data; >> >> > + SpiceUsbDevice *find_usb_device = find_info->usb_dev; >> >> > + SpiceUsbDevice *usb_device; >> >> > + gboolean is_lun_item; >> >> > + >> >> > + gtk_tree_model_get(tree_model, iter, >> >> > + COL_LUN_ITEM, &is_lun_item, >> >> > + COL_ITEM_DATA, (gpointer *)&usb_device, >> >> > + -1); >> >> > + if (!is_lun_item && usb_device == find_usb_device) { >> >> > + find_info->dev_iter = *iter; >> >> > + find_info->usb_dev = NULL; >> >> > + SPICE_DEBUG("Usb dev found %p iter %p", usb_device, iter); >> >> > + return TRUE; /* stop iterating */ >> >> > + } else { >> >> > + return FALSE; /* continue iterating */ >> >> > + } >> >> > +} >> >> > + >> >> > +static GtkTreeIter *usb_widget_tree_store_find_usb_device(GtkTreeStore >> >> > *tree_store, >> >> > + >> >> > SpiceUsbDevice *usb_device) >> >> > +{ >> >> > + TreeFindUsbDev find_info = { .usb_dev = usb_device,.dev_iter = {} >> >> > }; >> >> > + GtkTreeIter *iter = NULL; >> >> > + >> >> > + gtk_tree_model_foreach(GTK_TREE_MODEL(tree_store), >> >> > + >> >> > usb_widget_tree_store_find_usb_dev_foreach_cb, (gpointer)&find_info); >> >> > + // the callback sets 'usb_dev' field to zero if it finds the device >> >> > + if (!find_info.usb_dev) { >> >> > + iter = g_malloc(sizeof(*iter)); >> >> > + *iter = find_info.dev_iter; >> >> > + } >> >> > + return iter; >> >> > +} >> >> > + >> >> > +static gboolean usb_widget_remove_device(SpiceUsbDeviceWidget *self, >> >> > + SpiceUsbDevice *usb_device) >> >> > +{ >> >> > + SpiceUsbDeviceWidgetPrivate *priv = self->priv; >> >> > + GtkTreeIter *old_dev_iter; >> >> > + GtkTreeStore *tree_store = priv->usb_tree.tree_store; >> >> > + // on WIN32 it is possible the backend device is already removed >> >> > + // from the USB device manager list and we can't know it was >> >> > + // USB or CD, do we will try both lists >> >> > + old_dev_iter = usb_widget_tree_store_find_usb_device(tree_store, >> >> > usb_device); >> >> > + if (old_dev_iter != NULL) { >> >> > + SPICE_DEBUG("USB Device removed"); >> >> > + gtk_tree_store_remove(tree_store, old_dev_iter); >> >> > + priv->device_count--; >> >> > + g_free(old_dev_iter); >> >> > + return TRUE; >> >> > + } >> >> > + tree_store = priv->cd_tree.tree_store; >> >> > + if (tree_store) { >> >> > + old_dev_iter = >> >> > usb_widget_tree_store_find_usb_device(tree_store, usb_device); >> >> > + } >> >> > + if (old_dev_iter != NULL) { >> >> > + SPICE_DEBUG("CD Device removed"); >> >> > + gtk_tree_store_remove(tree_store, old_dev_iter); >> >> > + priv->device_count--; >> >> > + g_free(old_dev_iter); >> >> > + return TRUE; >> >> > + } >> >> > + SPICE_DEBUG("Device %p not found!", usb_device); >> >> > + return FALSE; >> >> > +} >> >> > + >> >> > +static GtkTreeViewColumn* view_add_toggle_column(SpiceUsbDeviceWidget >> >> > *self, >> >> > + enum column_id >> >> > toggle_col_id, >> >> > + enum column_id >> >> > visible_col_id, >> >> > + enum column_id >> >> > sensitive_col_id, >> >> > + tree_item_toggled_cb >> >> > toggled_cb, >> >> > + gboolean is_cd) >> >> > +{ >> >> > + SpiceUsbDeviceWidgetPrivate *priv = self->priv; >> >> > + GtkCellRenderer *renderer; >> >> > + GtkTreeViewColumn *view_col; >> >> > + const char *col_name = column_name(toggle_col_id, is_cd); >> >> > + >> >> > + renderer = gtk_cell_renderer_toggle_new(); >> >> > + >> >> > + if (sensitive_col_id != INVALID_COL) { >> >> > + view_col = gtk_tree_view_column_new_with_attributes( >> >> > + col_name, >> >> > + renderer, >> >> > + "active", toggle_col_id, >> >> > + "visible", visible_col_id, >> >> > + "activatable", sensitive_col_id, >> >> > + NULL); >> >> > + } else { >> >> > + view_col = gtk_tree_view_column_new_with_attributes( >> >> > + col_name, >> >> > + renderer, >> >> > + "active", toggle_col_id, >> >> > + "visible", visible_col_id, >> >> > + NULL); >> >> > + } >> >> > + >> >> > + gtk_tree_view_column_set_sizing(view_col, >> >> > GTK_TREE_VIEW_COLUMN_FIXED); >> >> > + gtk_tree_view_column_set_expand(view_col, FALSE); >> >> > + gtk_tree_view_column_set_resizable(view_col, FALSE); >> >> > + gtk_tree_view_append_column(is_cd ? priv->cd_tree.tree_view : >> >> > priv->usb_tree.tree_view, view_col); >> >> > + >> >> > + g_signal_connect(renderer, "toggled", G_CALLBACK(toggled_cb), >> >> > self); >> >> > + >> >> > + SPICE_DEBUG("view added toggle column [%u : %s] visible when [%u : >> >> > %s]", >> >> > + toggle_col_id, col_name, >> >> > + visible_col_id, column_name(visible_col_id, is_cd)); >> >> > + return view_col; >> >> > +} >> >> > + >> >> > +static GtkTreeViewColumn* >> >> > view_add_read_only_toggle_column(SpiceUsbDeviceWidget *self, >> >> > + enum >> >> > column_id toggle_col_id, >> >> > + enum >> >> > column_id visible_col_id, >> >> > + gboolean >> >> > is_cd) >> >> > +{ >> >> > + SpiceUsbDeviceWidgetPrivate *priv = self->priv; >> >> > + GtkCellRenderer *renderer; >> >> > + GtkTreeViewColumn *view_col; >> >> > + const char *col_name = column_name(toggle_col_id, is_cd); >> >> > + >> >> > + renderer = gtk_cell_renderer_toggle_new(); >> >> > + >> >> > + view_col = gtk_tree_view_column_new_with_attributes( >> >> > + col_name, >> >> > + renderer, >> >> > + "active", toggle_col_id, >> >> > + "visible", visible_col_id, >> >> > + NULL); >> >> > + >> >> > + >> >> > gtk_cell_renderer_toggle_set_activatable(GTK_CELL_RENDERER_TOGGLE(renderer), >> >> > FALSE); >> >> > + >> >> > + gtk_tree_view_column_set_sizing(view_col, >> >> > GTK_TREE_VIEW_COLUMN_FIXED); >> >> > + gtk_tree_view_column_set_expand(view_col, FALSE); >> >> > + gtk_tree_view_column_set_resizable(view_col, FALSE); >> >> > + gtk_tree_view_append_column(is_cd ? priv->cd_tree.tree_view : >> >> > priv->usb_tree.tree_view, view_col); >> >> > + >> >> > + SPICE_DEBUG("view added read-only toggle column [%u : %s] visible >> >> > when [%u : %s]", >> >> > + toggle_col_id, col_name, >> >> > + visible_col_id, column_name(visible_col_id, is_cd)); >> >> > + return view_col; >> >> > +} >> >> > + >> >> > +static GtkTreeViewColumn* view_add_text_column(SpiceUsbDeviceWidget >> >> > *self, >> >> > + enum column_id col_id, >> >> > + gboolean expandable, >> >> > + gboolean is_cd) >> >> > +{ >> >> > + SpiceUsbDeviceWidgetPrivate *priv = self->priv; >> >> > + GtkCellRenderer *renderer; >> >> > + GtkTreeViewColumn *view_col; >> >> > + >> >> > + renderer = gtk_cell_renderer_text_new(); >> >> > + >> >> > + view_col = gtk_tree_view_column_new_with_attributes( >> >> > + column_name(col_id, is_cd), >> >> > + renderer, >> >> > + "text", col_id, >> >> > + //"cell-background", COL_ROW_COLOR, >> >> > + //"cell-background-set", COL_ROW_COLOR_SET, >> >> > + NULL); >> >> > + >> >> > + gtk_tree_view_column_set_sizing(view_col, >> >> > GTK_TREE_VIEW_COLUMN_GROW_ONLY); >> >> > + gtk_tree_view_column_set_resizable(view_col, TRUE); >> >> > + gtk_tree_view_column_set_expand(view_col, expandable); >> >> > + >> >> > + gtk_tree_view_append_column(is_cd ? priv->cd_tree.tree_view : >> >> > priv->usb_tree.tree_view, view_col); >> >> > + >> >> > + SPICE_DEBUG("view added text column [%u : %s]", col_id, >> >> > column_name(col_id, is_cd)); >> >> > + return view_col; >> >> > +} >> >> > + >> >> > +static GtkTreeViewColumn* view_add_pixbuf_column(SpiceUsbDeviceWidget >> >> > *self, >> >> > + enum column_id col_id, >> >> > + enum column_id >> >> > visible_col_id, >> >> > + gboolean is_cd) >> >> > +{ >> >> > + SpiceUsbDeviceWidgetPrivate *priv = self->priv; >> >> > + GtkCellRenderer *renderer; >> >> > + GtkTreeViewColumn *view_col; >> >> > + const char *col_name = column_name(col_id, is_cd); >> >> > + >> >> > + renderer = gtk_cell_renderer_pixbuf_new(); >> >> > + >> >> > + if (visible_col_id == INVALID_COL) { >> >> > + view_col = gtk_tree_view_column_new_with_attributes( >> >> > + col_name, >> >> > + renderer, >> >> > + "pixbuf", col_id, >> >> > + NULL); >> >> > + SPICE_DEBUG("view added pixbuf col[%u : %s] visible always", >> >> > col_id, col_name); >> >> > + } else { >> >> > + view_col = gtk_tree_view_column_new_with_attributes( >> >> > + col_name, >> >> > + renderer, >> >> > + "pixbuf", col_id, >> >> > + "visible", visible_col_id, >> >> > + NULL); >> >> > + SPICE_DEBUG("view added pixbuf col[%u : %s] visible when[%u : >> >> > %s]", >> >> > + col_id, col_name, visible_col_id, >> >> > column_name(visible_col_id, is_cd)); >> >> > + } >> >> > + gtk_tree_view_append_column(is_cd ? priv->cd_tree.tree_view : >> >> > priv->usb_tree.tree_view, view_col); >> >> > + return view_col; >> >> > +} >> >> > + >> >> > +/* Toggle handlers */ >> >> > + >> >> > +static gboolean tree_item_toggle_get_val(GtkTreeStore *tree_store, >> >> > gchar *path_str, GtkTreeIter *iter, enum column_id col_id) >> >> > +{ >> >> > + gboolean toggle_val; >> >> > + >> >> > + gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(tree_store), >> >> > iter, path_str); >> >> > + gtk_tree_model_get(GTK_TREE_MODEL(tree_store), iter, col_id, >> >> > &toggle_val, -1); >> >> > + >> >> > + return toggle_val; >> >> > +} >> >> > + >> >> > +static void tree_item_toggle_set(GtkTreeStore *tree_store, GtkTreeIter >> >> > *iter, enum column_id col_id, gboolean new_val) >> >> > +{ >> >> > + gtk_tree_store_set(tree_store, iter, col_id, new_val, -1); >> >> > +} >> >> > + >> >> > +typedef struct _connect_cb_data { >> >> > + SpiceUsbDeviceWidget *self; >> >> > + SpiceUsbDevice *usb_dev; >> >> > +} connect_cb_data; >> >> > + >> >> > +static void connect_cb_data_free(connect_cb_data *user_data) >> >> > +{ >> >> > + spice_usb_device_widget_update_status(user_data->self); >> >> > + g_object_unref(user_data->self); >> >> > + g_boxed_free(spice_usb_device_get_type(), user_data->usb_dev); >> >> > + g_free(user_data); >> >> > +} >> >> > + >> >> > +static void usb_widget_connect_cb(GObject *source_object, GAsyncResult >> >> > *res, gpointer user_data) >> >> > +{ >> >> > + connect_cb_data *cb_data = user_data; >> >> > + SpiceUsbDeviceWidget *self = cb_data->self; >> >> > + SpiceUsbDeviceWidgetPrivate *priv = self->priv; >> >> > + SpiceUsbDevice *usb_dev = cb_data->usb_dev; >> >> > + GError *err = NULL; >> >> > + GtkTreeIter *dev_iter; >> >> > + gchar *desc; >> >> > + gboolean finished; >> >> > + gboolean is_cd = >> >> > spice_usb_device_manager_is_device_cd(priv->manager, usb_dev); >> >> > + GtkTreeStore *tree_store = is_cd ? priv->cd_tree.tree_store : >> >> > priv->usb_tree.tree_store; >> >> > + >> >> > + dev_iter = usb_widget_tree_store_find_usb_device(tree_store, >> >> > usb_dev); >> >> > + if (!dev_iter) { >> >> > + return; >> >> > + } >> >> > + >> >> > + desc = usb_device_description(priv->manager, usb_dev, >> >> > priv->device_format_string); >> >> > + SPICE_DEBUG("Connect cb: %p %s", usb_dev, desc); >> >> > + >> >> > + finished = >> >> > spice_usb_device_manager_connect_device_finish(priv->manager, res, >> >> > &err); >> >> > + if (finished) { >> >> > + gtk_tree_store_set(tree_store, dev_iter, >> >> > + COL_CONNECT_ICON, priv->icon_connected, >> >> > + COL_CONNECTED, TRUE, >> >> > + -1); >> >> > + } else { >> >> > + gtk_tree_store_set(tree_store, dev_iter, >> >> > + COL_REDIRECT, FALSE, >> >> > + -1); >> >> > + g_prefix_error(&err, "Device connect failed %s: ", desc); >> >> > + if (err) { >> >> > + SPICE_DEBUG("%s", err->message); >> >> > + g_signal_emit(self, signals[CONNECT_FAILED], 0, usb_dev, >> >> > err); >> >> > + g_error_free(err); >> >> > + } else { >> >> > + g_signal_emit(self, signals[CONNECT_FAILED], 0, usb_dev, >> >> > NULL); >> >> > + } >> >> > + >> >> > + /* don't trigger a disconnect if connect failed */ >> >> > + /* >> >> > + >> >> > g_signal_handlers_block_by_func(GTK_TOGGLE_BUTTON(user_data->check), >> >> > + checkbox_clicked_cb, self); >> >> > + >> >> > gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(user_data->check), >> >> > FALSE); >> >> > + >> >> > g_signal_handlers_unblock_by_func(GTK_TOGGLE_BUTTON(user_data->check), >> >> > + checkbox_clicked_cb, self); >> >> > + */ >> >> > + } >> >> > + g_free(desc); >> >> > + g_free(dev_iter); >> >> > + connect_cb_data_free(user_data); >> >> > +} >> >> > + >> >> > +static void usb_widget_disconnect_cb(GObject *source_object, >> >> > GAsyncResult *res, gpointer user_data) >> >> > +{ >> >> > + connect_cb_data *cb_data = user_data; >> >> > + SpiceUsbDeviceWidget *self = cb_data->self; >> >> > + SpiceUsbDeviceWidgetPrivate *priv = self->priv; >> >> > + SpiceUsbDevice *usb_dev = cb_data->usb_dev; >> >> > + GError *err = NULL; >> >> > + GtkTreeIter *dev_iter; >> >> > + gchar *desc; >> >> > + gboolean finished; >> >> > + gboolean is_cd = >> >> > spice_usb_device_manager_is_device_cd(priv->manager, usb_dev); >> >> > + GtkTreeStore *tree_store = is_cd ? priv->cd_tree.tree_store : >> >> > priv->usb_tree.tree_store; >> >> > + >> >> > + dev_iter = usb_widget_tree_store_find_usb_device(tree_store, >> >> > usb_dev); >> >> > + if (!dev_iter) { >> >> > + return; >> >> > + } >> >> > + >> >> > + desc = usb_device_description(priv->manager, usb_dev, >> >> > priv->device_format_string); >> >> > + SPICE_DEBUG("Disconnect cb: %p %s", usb_dev, desc); >> >> > + >> >> > + finished = >> >> > spice_usb_device_manager_disconnect_device_finish(priv->manager, res, >> >> > &err); >> >> > + if (finished) { >> >> > + gtk_tree_store_set(tree_store, dev_iter, >> >> > + COL_CONNECT_ICON, priv->icon_disconn, >> >> > + COL_CONNECTED, FALSE, >> >> > + -1); >> >> > + } else { >> >> > + gtk_tree_store_set(tree_store, dev_iter, >> >> > + COL_REDIRECT, TRUE, >> >> > + -1); >> >> > + g_prefix_error(&err, "Device disconnect failed %s: ", desc); >> >> > + if (err) { >> >> > + SPICE_DEBUG("%s", err->message); >> >> > + g_error_free(err); >> >> > + } >> >> > + } >> >> > + g_free(desc); >> >> > + g_free(dev_iter); >> >> > + connect_cb_data_free(user_data); >> >> > +} >> >> > + >> >> > +static void tree_item_toggled_cb_redirect(GtkCellRendererToggle *cell, >> >> > + gchar *path_str, >> >> > + SpiceUsbDeviceWidget *self, >> >> > + GtkTreeStore *tree_store) >> >> > +{ >> >> > + SpiceUsbDeviceWidgetPrivate *priv = self->priv; >> >> > + connect_cb_data *cb_data = g_new(connect_cb_data, 1); >> >> > + SpiceUsbDevice *usb_dev; >> >> > + GtkTreeIter iter; >> >> > + gboolean new_redirect_val; >> >> > + >> >> > + new_redirect_val = !tree_item_toggle_get_val(tree_store, path_str, >> >> > &iter, COL_REDIRECT); >> >> > + SPICE_DEBUG("Redirect: %s", new_redirect_val ? "ON" : "OFF"); >> >> > + tree_item_toggle_set(tree_store, &iter, COL_REDIRECT, >> >> > new_redirect_val); >> >> > + >> >> > + gtk_tree_model_get(GTK_TREE_MODEL(tree_store), &iter, >> >> > COL_ITEM_DATA, (gpointer *)&usb_dev, -1); >> >> > + cb_data->self = g_object_ref(self); >> >> > + cb_data->usb_dev = g_boxed_copy(spice_usb_device_get_type(), >> >> > usb_dev); >> >> > + >> >> > + if (new_redirect_val) { >> >> > + spice_usb_device_manager_connect_device_async(priv->manager, >> >> > usb_dev, >> >> > + NULL, /* >> >> > cancellable */ >> >> > + >> >> > usb_widget_connect_cb, cb_data); >> >> > + } else { >> >> > + >> >> > spice_usb_device_manager_disconnect_device_async(priv->manager, usb_dev, >> >> > + NULL, /* >> >> > cancellable */ >> >> > + >> >> > usb_widget_disconnect_cb, cb_data); >> >> > + >> >> > + } >> >> > + spice_usb_device_widget_update_status(self); >> >> > +} >> >> > + >> >> > +static void tree_item_toggled_cb_redirect_cd(GtkCellRendererToggle >> >> > *cell, >> >> > + gchar *path_str, >> >> > + gpointer user_data) >> >> > +{ >> >> > + SpiceUsbDeviceWidget *self = SPICE_USB_DEVICE_WIDGET(user_data); >> >> > + SpiceUsbDeviceWidgetPrivate *priv = self->priv; >> >> > + tree_item_toggled_cb_redirect(cell, path_str, self, >> >> > priv->cd_tree.tree_store); >> >> > +} >> >> > + >> >> > +static void tree_item_toggled_cb_redirect_usb(GtkCellRendererToggle >> >> > *cell, >> >> > + gchar *path_str, >> >> > + gpointer user_data) >> >> > +{ >> >> > + SpiceUsbDeviceWidget *self = SPICE_USB_DEVICE_WIDGET(user_data); >> >> > + SpiceUsbDeviceWidgetPrivate *priv = self->priv; >> >> > + tree_item_toggled_cb_redirect(cell, path_str, self, >> >> > priv->usb_tree.tree_store); >> >> > +} >> >> > + >> >> > +/* Signal handlers */ >> >> > + >> >> > +static void device_added_cb(SpiceUsbDeviceManager *usb_dev_mgr, >> >> > + SpiceUsbDevice *usb_device, gpointer user_data) >> >> > +{ >> >> > + SpiceUsbDeviceWidget *self = SPICE_USB_DEVICE_WIDGET(user_data); >> >> > + >> >> > + SPICE_DEBUG("Signal: Device Added"); >> >> > + >> >> > + usb_widget_add_device(self, usb_device, NULL); >> >> > + >> >> > + spice_usb_device_widget_update_status(self); >> >> > +} >> >> > + >> >> > +static void device_removed_cb(SpiceUsbDeviceManager *usb_dev_mgr, >> >> > + SpiceUsbDevice *usb_device, gpointer user_data) >> >> > +{ >> >> > + SpiceUsbDeviceWidget *self = SPICE_USB_DEVICE_WIDGET(user_data); >> >> > + gboolean dev_removed; >> >> > + >> >> > + SPICE_DEBUG("Signal: Device Removed"); >> >> > + >> >> > + dev_removed = usb_widget_remove_device(self, usb_device); >> >> > + if (dev_removed) { >> >> > + spice_usb_device_widget_update_status(self); >> >> > + } >> >> > +} >> >> > + >> >> > +static void device_changed_cb(SpiceUsbDeviceManager *usb_dev_mgr, >> >> > + SpiceUsbDevice *usb_device, gpointer user_data) >> >> > +{ >> >> > + SpiceUsbDeviceWidget *self = SPICE_USB_DEVICE_WIDGET(user_data); >> >> > + SpiceUsbDeviceWidgetPrivate *priv = self->priv; >> >> > + GtkTreeIter *old_dev_iter; >> >> > + gboolean is_cd = >> >> > spice_usb_device_manager_is_device_cd(priv->manager, usb_device); >> >> > + GtkTreeStore *tree_store = is_cd ? priv->cd_tree.tree_store : >> >> > priv->usb_tree.tree_store; >> >> > + >> >> > + SPICE_DEBUG("Signal: Device Changed"); >> >> > + >> >> > + old_dev_iter = usb_widget_tree_store_find_usb_device(tree_store, >> >> > usb_device); >> >> > + if (old_dev_iter != NULL) { >> >> > + >> >> > + usb_widget_add_device(self, usb_device, old_dev_iter); >> >> > + >> >> > + spice_usb_device_widget_update_status(self); >> >> > + g_free(old_dev_iter); >> >> > + } else { >> >> > + SPICE_DEBUG("Device not found!"); >> >> > + } >> >> > +} >> >> > + >> >> > +static void device_error_cb(SpiceUsbDeviceManager *manager, >> >> > + SpiceUsbDevice *usb_device, GError *err, gpointer user_data) >> >> > +{ >> >> > + SpiceUsbDeviceWidget *self = SPICE_USB_DEVICE_WIDGET(user_data); >> >> > + SpiceUsbDeviceWidgetPrivate *priv = self->priv; >> >> > + GtkTreeIter *dev_iter; >> >> > + gboolean is_cd = >> >> > spice_usb_device_manager_is_device_cd(priv->manager, usb_device); >> >> > + GtkTreeStore *tree_store = is_cd ? priv->cd_tree.tree_store : >> >> > priv->usb_tree.tree_store; >> >> > + >> >> > + SPICE_DEBUG("Signal: Device Error"); >> >> > + >> >> > + dev_iter = usb_widget_tree_store_find_usb_device(tree_store, >> >> > usb_device); >> >> > + if (dev_iter != NULL) { >> >> > + tree_item_toggle_set(tree_store, dev_iter, COL_REDIRECT, >> >> > FALSE); >> >> > + spice_usb_device_widget_update_status(self); >> >> > + g_free(dev_iter); >> >> > + } else { >> >> > + SPICE_DEBUG("Device not found!"); >> >> > + } >> >> > +} >> >> > + >> >> > +/* Selection handler */ >> >> > + >> >> > +static void tree_selection_changed_cb(GtkTreeSelection *select, >> >> > gpointer user_data) >> >> > +{ >> >> > + GtkTreeModel *tree_model; >> >> > + GtkTreeIter iter; >> >> > + GtkTreePath *path; >> >> > + gboolean is_lun; >> >> > + UsbWidgetLunItem *lun_item; >> >> > + gchar *txt[3]; >> >> > + >> >> > + if (gtk_tree_selection_get_selected(select, &tree_model, &iter)) { >> >> > + gtk_tree_model_get(tree_model, &iter, >> >> > + COL_VENDOR, &txt[0], >> >> > + COL_PRODUCT, &txt[1], >> >> > + COL_REVISION, &txt[2], >> >> > + COL_LUN_ITEM, &is_lun, >> >> > + COL_ITEM_DATA, (gpointer *)&lun_item, >> >> > + -1); >> >> > + path = gtk_tree_model_get_path(tree_model, &iter); >> >> > + >> >> > + SPICE_DEBUG("selected: %s,%s,%s [%s %s] [%s]", >> >> > + txt[0], txt[1], >> >> > + is_lun ? txt[2] : "--", >> >> > + is_lun ? "LUN" : "USB-DEV", >> >> > + is_lun ? lun_item->info.file_path : "--", >> >> > + gtk_tree_path_to_string(path)); >> >> > + >> >> > + if (txt[0]) { >> >> > + g_free(txt[0]); >> >> > + } >> >> > + if (txt[1]) { >> >> > + g_free(txt[1]); >> >> > + } >> >> > + if (txt[2]) { >> >> > + g_free(txt[2]); >> >> > + } >> >> > + gtk_tree_path_free(path); >> >> > + } >> >> > +} >> >> > + >> >> > +static GtkTreeSelection* set_selection_handler(GtkTreeView *tree_view) >> >> > +{ >> >> > + GtkTreeSelection *select; >> >> > + >> >> > + select = gtk_tree_view_get_selection(tree_view); >> >> > + gtk_tree_selection_set_mode(select, GTK_SELECTION_SINGLE); >> >> > + >> >> > + g_signal_connect(G_OBJECT(select), "changed", >> >> > + G_CALLBACK(tree_selection_changed_cb), >> >> > + NULL); >> >> > + >> >> > + SPICE_DEBUG("selection handler set"); >> >> > + return select; >> >> > +} >> >> > + >> >> > +static GtkWidget *create_image_button_box(const gchar *label_str, >> >> > const gchar *icon_name, GtkWidget *parent) >> >> > +{ >> >> > + GtkWidget *box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); >> >> > + GtkWidget *icon = gtk_image_new_from_icon_name(icon_name, >> >> > GTK_ICON_SIZE_MENU); >> >> > + GtkWidget *label = gtk_accel_label_new(label_str); >> >> > + GtkAccelGroup *accel_group = gtk_accel_group_new(); >> >> > + guint accel_key; >> >> > + >> >> > + /* add icon */ >> >> > + gtk_container_add(GTK_CONTAINER(box), icon); >> >> > + >> >> > + /* add label */ >> >> > + gtk_label_set_xalign(GTK_LABEL(label), 0.0); >> >> > + gtk_label_set_use_underline(GTK_LABEL(label), TRUE); >> >> > + g_object_get(G_OBJECT(label), "mnemonic-keyval", &accel_key, NULL); >> >> > + gtk_widget_add_accelerator(parent, "activate", accel_group, >> >> > accel_key, >> >> > + GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE); >> >> > + gtk_accel_label_set_accel_widget(GTK_ACCEL_LABEL(label), parent); >> >> > + gtk_box_pack_end(GTK_BOX(box), label, TRUE, TRUE, 0); >> >> > + >> >> > + /* add the new box to the parent widget */ >> >> > + gtk_container_add(GTK_CONTAINER(parent), box); >> >> > + >> >> > + return box; >> >> > +} >> >> > + >> >> > +/* LUN properties dialog */ >> >> > + >> >> > +typedef struct _lun_properties_dialog { >> >> > + GtkWidget *dialog; >> >> > + GtkWidget *advanced_grid; >> >> > + gboolean advanced_shown; >> >> > + >> >> > + GtkWidget *file_entry; >> >> > + GtkWidget *vendor_entry; >> >> > + GtkWidget *product_entry; >> >> > + GtkWidget *revision_entry; >> >> > + GtkWidget *loaded_switch; >> >> > + GtkWidget *locked_switch; >> >> > +} lun_properties_dialog; >> >> > + >> >> > +#if 1 >> >> > +static void usb_cd_choose_file(GtkWidget *button, gpointer user_data) >> >> > +{ >> >> > + GtkWidget *file_entry = (GtkWidget *)user_data; >> >> > + GtkFileChooserNative *native; >> >> > + GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_OPEN; >> >> > + gint res; >> >> > + >> >> > + native = gtk_file_chooser_native_new("Choose File for USB CD", >> >> > + GTK_WINDOW(gtk_widget_get_toplevel(file_entry)), >> >> > + action, >> >> > + "_Open", >> >> > + "_Cancel"); >> >> > + >> >> > + res = gtk_native_dialog_run(GTK_NATIVE_DIALOG(native)); >> >> > + if (res == GTK_RESPONSE_ACCEPT) { >> >> > + char *filename = >> >> > gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(native)); >> >> > + gtk_entry_set_alignment(GTK_ENTRY(file_entry), 1); >> >> > + gtk_entry_set_text(GTK_ENTRY(file_entry), filename); >> >> > + g_free(filename); >> >> > + } >> >> > + else { >> >> > + gtk_widget_grab_focus(button); >> >> > + } >> >> > + >> >> > + g_object_unref(native); >> >> > +} >> >> > +#else >> >> > +// to be removed >> >> > +static void usb_cd_choose_file(GtkWidget *button, gpointer user_data) >> >> > +{ >> >> > + GtkWidget *file_entry = (GtkWidget *)user_data; >> >> > + GtkWidget *dialog; >> >> > + gint res; >> >> > + >> >> > + dialog = gtk_file_chooser_dialog_new ("Choose File for USB CD", >> >> > + >> >> > GTK_WINDOW(gtk_widget_get_toplevel(file_entry)), >> >> > + GTK_FILE_CHOOSER_ACTION_OPEN, >> >> > + "_Cancel", >> >> > + GTK_RESPONSE_CANCEL, >> >> > + "_Ok", >> >> > + GTK_RESPONSE_ACCEPT, >> >> > + NULL); >> >> > + gtk_dialog_set_default_response(GTK_DIALOG(dialog), >> >> > GTK_RESPONSE_ACCEPT); >> >> > + >> >> > + res = gtk_dialog_run(GTK_DIALOG(dialog)); >> >> > + if (res == GTK_RESPONSE_ACCEPT) { >> >> > + char *filename = >> >> > gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); >> >> > + gtk_entry_set_alignment(GTK_ENTRY(file_entry), 1); >> >> > + gtk_entry_set_text(GTK_ENTRY(file_entry), filename); >> >> > + g_free(filename); >> >> > + } >> >> > + gtk_widget_destroy(dialog); >> >> > +} >> >> > +#endif >> >> > + >> >> > +static gboolean lun_properties_dialog_loaded_switch_cb(GtkWidget >> >> > *widget, >> >> > + gboolean state, >> >> > gpointer user_data) >> >> > +{ >> >> > + lun_properties_dialog *lun_dialog = user_data; >> >> > + >> >> > + gtk_widget_set_sensitive(lun_dialog->locked_switch, state); >> >> > + gtk_widget_set_can_focus(lun_dialog->locked_switch, state); >> >> > + >> >> > + return FALSE; /* call default signal handler */ >> >> > +} >> >> > + >> >> > +static void lun_properties_dialog_toggle_advanced(GtkWidget *widget, >> >> > gpointer user_data) >> >> > +{ >> >> > + lun_properties_dialog *lun_dialog = user_data; >> >> > + >> >> > + if (lun_dialog->advanced_shown) { >> >> > + gtk_widget_hide(lun_dialog->advanced_grid); >> >> > + lun_dialog->advanced_shown = FALSE; >> >> > + } else { >> >> > + gtk_widget_show_all(lun_dialog->advanced_grid); >> >> > + lun_dialog->advanced_shown = TRUE; >> >> > + } >> >> > +} >> >> > + >> >> > +static void create_lun_properties_dialog(SpiceUsbDeviceWidget *self, >> >> > + GtkWidget *parent_window, >> >> > + SpiceUsbDeviceLunInfo >> >> > *lun_info, >> >> > + lun_properties_dialog >> >> > *lun_dialog) >> >> > +{ >> >> > + // SpiceUsbDeviceWidgetPrivate *priv = self->priv; >> >> > + GtkWidget *dialog, *content_area; >> >> > + GtkWidget *grid, *advanced_grid; >> >> > + GtkWidget *file_entry, *choose_button; >> >> > + GtkWidget *advanced_button, *advanced_icon; >> >> > + GtkWidget *vendor_entry, *product_entry, *revision_entry; >> >> > + GtkWidget *loaded_switch, *loaded_label; >> >> > + GtkWidget *locked_switch, *locked_label; >> >> > + gint nrow = 0; >> >> > + >> >> > + dialog = gtk_dialog_new_with_buttons (!lun_info ? "Add CD LUN" : >> >> > "CD LUN Settings", >> >> > + GTK_WINDOW(parent_window), >> >> > + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, >> >> > /* flags */ >> >> > + !lun_info ? "Add" : "OK", GTK_RESPONSE_ACCEPT, >> >> > + "Cancel", GTK_RESPONSE_REJECT, >> >> > + NULL); >> >> > + >> >> > + gtk_dialog_set_default_response(GTK_DIALOG(dialog), >> >> > GTK_RESPONSE_ACCEPT); >> >> > + gtk_container_set_border_width(GTK_CONTAINER(dialog), 12); >> >> > + gtk_box_set_spacing(GTK_BOX(gtk_bin_get_child(GTK_BIN(dialog))), >> >> > 12); >> >> > + >> >> > + content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); >> >> > + >> >> > + /* main grid - always visible */ >> >> > + grid = gtk_grid_new(); >> >> > + gtk_grid_set_row_spacing(GTK_GRID(grid), 12); >> >> > + gtk_grid_set_column_homogeneous(GTK_GRID(grid), FALSE); >> >> > + gtk_container_add(GTK_CONTAINER(content_area), grid); >> >> > + >> >> > + /* File path label */ >> >> > + gtk_grid_attach(GTK_GRID(grid), >> >> > + gtk_label_new("Select file or device"), >> >> > + 0, nrow++, // left top >> >> > + 7, 1); // width height >> >> > + >> >> > + /* file/device path entry */ >> >> > + file_entry = gtk_entry_new(); >> >> > + gtk_widget_set_hexpand(file_entry, TRUE); >> >> > + if (!lun_info) { >> >> > + gtk_entry_set_placeholder_text(GTK_ENTRY(file_entry), >> >> > "file-path"); >> >> > + } else { >> >> > + gtk_entry_set_text(GTK_ENTRY(file_entry), lun_info->file_path); >> >> > + if (lun_info->loaded) { >> >> > + gtk_editable_set_editable(GTK_EDITABLE(file_entry), FALSE); >> >> > + gtk_widget_set_can_focus(file_entry, FALSE); >> >> > + } >> >> > + } >> >> > + gtk_grid_attach(GTK_GRID(grid), >> >> > + file_entry, >> >> > + 0, nrow, // left top >> >> > + 6, 1); // width height >> >> > + >> >> > + /* choose button */ >> >> > + choose_button = gtk_button_new_with_mnemonic("_Choose File"); >> >> > + gtk_widget_set_hexpand(choose_button, FALSE); >> >> > + g_signal_connect(GTK_BUTTON(choose_button), >> >> > + "clicked", G_CALLBACK(usb_cd_choose_file), >> >> > file_entry); >> >> > + if (lun_info && lun_info->loaded) { >> >> > + gtk_widget_set_sensitive(choose_button, FALSE); >> >> > + gtk_widget_set_can_focus(choose_button, FALSE); >> >> > + } >> >> > + >> >> > + gtk_grid_attach(GTK_GRID(grid), >> >> > + choose_button, >> >> > + 6, nrow++, // left top >> >> > + 1, 1); // width height >> >> > + >> >> > + /* advanced button */ >> >> > + advanced_button = gtk_button_new_with_label("Advanced"); >> >> > + gtk_button_set_relief(GTK_BUTTON(advanced_button), >> >> > GTK_RELIEF_NONE); >> >> > + advanced_icon = gtk_image_new_from_icon_name("preferences-system", >> >> > GTK_ICON_SIZE_BUTTON); >> >> > + gtk_button_set_image(GTK_BUTTON(advanced_button), advanced_icon); >> >> > + gtk_button_set_always_show_image(GTK_BUTTON(advanced_button), >> >> > TRUE); >> >> > + g_signal_connect(advanced_button, "clicked", >> >> > G_CALLBACK(lun_properties_dialog_toggle_advanced), lun_dialog); >> >> > + >> >> > + gtk_grid_attach(GTK_GRID(grid), >> >> > + advanced_button, >> >> > + 0, nrow++, // left top >> >> > + 1, 1); // width height >> >> > + >> >> > + /* advanced grid */ >> >> > + advanced_grid = gtk_grid_new(); >> >> > + gtk_grid_set_row_spacing(GTK_GRID(advanced_grid), 12); >> >> > + gtk_grid_set_column_homogeneous(GTK_GRID(advanced_grid), FALSE); >> >> > + gtk_container_add(GTK_CONTAINER(content_area), advanced_grid); >> >> > + >> >> > + /* horizontal separator */ >> >> > + gtk_container_add(GTK_CONTAINER(content_area), >> >> > gtk_separator_new(GTK_ORIENTATION_HORIZONTAL)); >> >> > + >> >> > + /* pack advanced grid */ >> >> > + nrow = 0; >> >> > + >> >> > + /* horizontal separator */ >> >> > + gtk_grid_attach(GTK_GRID(advanced_grid), >> >> > + gtk_separator_new(GTK_ORIENTATION_HORIZONTAL), >> >> > + 0, nrow++, // left top >> >> > + 7, 1); // width height >> >> > + >> >> > + /* product id labels */ >> >> > + gtk_grid_attach(GTK_GRID(advanced_grid), >> >> > + gtk_label_new("Vendor"), >> >> > + 0, nrow, // left top >> >> > + 2, 1); // width height >> >> > + >> >> > + gtk_grid_attach(GTK_GRID(advanced_grid), >> >> > + gtk_label_new("Product"), >> >> > + 2, nrow, // left top >> >> > + 4, 1); // width height >> >> > + >> >> > + gtk_grid_attach(GTK_GRID(advanced_grid), >> >> > + gtk_label_new("Revision"), >> >> > + 6, nrow++, // left top >> >> > + 1, 1); // width height >> >> > + >> >> > + /* vendor entry */ >> >> > + vendor_entry = gtk_entry_new(); >> >> > + gtk_entry_set_max_length(GTK_ENTRY(vendor_entry), 8); >> >> > + if (lun_info) { >> >> > + gtk_widget_set_sensitive(vendor_entry, FALSE); >> >> > + gtk_widget_set_can_focus(vendor_entry, FALSE); >> >> > + gtk_entry_set_text(GTK_ENTRY(vendor_entry), lun_info->vendor); >> >> > + } else { >> >> > + gtk_entry_set_placeholder_text(GTK_ENTRY(vendor_entry), >> >> > "auto"); >> >> > + } >> >> > + gtk_grid_attach(GTK_GRID(advanced_grid), >> >> > + vendor_entry, >> >> > + 0, nrow, // left top >> >> > + 2, 1); // width height >> >> > + >> >> > + /* product entry */ >> >> > + product_entry = gtk_entry_new(); >> >> > + gtk_entry_set_max_length(GTK_ENTRY(product_entry), 16); >> >> > + if (lun_info) { >> >> > + gtk_widget_set_sensitive(product_entry, FALSE); >> >> > + gtk_widget_set_can_focus(product_entry, FALSE); >> >> > + gtk_entry_set_text(GTK_ENTRY(product_entry), >> >> > lun_info->product); >> >> > + } else { >> >> > + gtk_entry_set_placeholder_text(GTK_ENTRY(product_entry), >> >> > "auto"); >> >> > + } >> >> > + gtk_grid_attach(GTK_GRID(advanced_grid), >> >> > + product_entry, >> >> > + 2, nrow, // left top >> >> > + 4, 1); // width height >> >> > + >> >> > + /* revision entry */ >> >> > + revision_entry = gtk_entry_new(); >> >> > + gtk_entry_set_max_length(GTK_ENTRY(revision_entry), 4); >> >> > + if (lun_info) { >> >> > + gtk_widget_set_sensitive(revision_entry, FALSE); >> >> > + gtk_widget_set_can_focus(revision_entry, FALSE); >> >> > + gtk_entry_set_text(GTK_ENTRY(revision_entry), >> >> > lun_info->revision); >> >> > + } else { >> >> > + gtk_entry_set_placeholder_text(GTK_ENTRY(revision_entry), >> >> > "auto"); >> >> > + } >> >> > + gtk_grid_attach(GTK_GRID(advanced_grid), >> >> > + revision_entry, >> >> > + 6, nrow++, // left top >> >> > + 1, 1); // width height >> >> > + >> >> > + /* horizontal separator */ >> >> > + if (!lun_info) { >> >> > + gtk_grid_attach(GTK_GRID(advanced_grid), >> >> > + gtk_separator_new(GTK_ORIENTATION_HORIZONTAL), >> >> > + 0, nrow++, // left top >> >> > + 7, 1); // width height >> >> > + } >> >> > + >> >> > + /* initially loaded switch */ >> >> > + loaded_label = gtk_label_new("Initially loaded:"); >> >> > + gtk_grid_attach(GTK_GRID(advanced_grid), >> >> > + loaded_label, >> >> > + 0, nrow, // left top >> >> > + 2, 1); // width height >> >> > + >> >> > + loaded_switch = gtk_switch_new(); >> >> > + gtk_switch_set_state(GTK_SWITCH(loaded_switch), TRUE); >> >> > + if (lun_info) { >> >> > + gtk_widget_set_child_visible(loaded_switch, FALSE); >> >> > + gtk_widget_set_child_visible(loaded_label, FALSE); >> >> > + } else { >> >> > + g_signal_connect(loaded_switch, "state-set", >> >> > + >> >> > G_CALLBACK(lun_properties_dialog_loaded_switch_cb), lun_dialog); >> >> > + } >> >> > + gtk_widget_set_halign(loaded_switch, GTK_ALIGN_START); >> >> > + gtk_grid_attach(GTK_GRID(advanced_grid), >> >> > + loaded_switch, >> >> > + 2, nrow++, // left top >> >> > + 1, 1); // width height >> >> > + >> >> > + /* initially locked switch */ >> >> > + locked_label = gtk_label_new("Initially locked:"); >> >> > + gtk_grid_attach(GTK_GRID(advanced_grid), >> >> > + locked_label, >> >> > + 0, nrow, // left top >> >> > + 2, 1); // width height >> >> > + >> >> > + locked_switch = gtk_switch_new(); >> >> > + gtk_switch_set_state(GTK_SWITCH(locked_switch), FALSE); >> >> > + gtk_widget_set_hexpand(locked_switch, FALSE); >> >> > + if (lun_info) { >> >> > + gtk_widget_set_child_visible(locked_switch, FALSE); >> >> > + gtk_widget_set_child_visible(locked_label, FALSE); >> >> > + } >> >> > + gtk_widget_set_halign(locked_switch, GTK_ALIGN_START); >> >> > + gtk_grid_attach(GTK_GRID(advanced_grid), >> >> > + locked_switch, >> >> > + 2, nrow++, // left top >> >> > + 1, 1); // width height >> >> > + >> >> > + lun_dialog->dialog = dialog; >> >> > + lun_dialog->advanced_grid = advanced_grid; >> >> > + lun_dialog->advanced_shown = FALSE; >> >> > + lun_dialog->file_entry = file_entry; >> >> > + lun_dialog->vendor_entry = vendor_entry; >> >> > + lun_dialog->product_entry = product_entry; >> >> > + lun_dialog->revision_entry = revision_entry; >> >> > + lun_dialog->loaded_switch = loaded_switch; >> >> > + lun_dialog->locked_switch = locked_switch; >> >> > + >> >> > + gtk_widget_show_all(dialog); >> >> > + gtk_widget_hide(advanced_grid); >> >> > +} >> >> > + >> >> > +static void lun_properties_dialog_get_info(lun_properties_dialog >> >> > *lun_dialog, >> >> > + SpiceUsbDeviceLunInfo >> >> > *lun_info) >> >> > +{ >> >> > + lun_info->file_path = >> >> > gtk_entry_get_text(GTK_ENTRY(lun_dialog->file_entry)); >> >> > + lun_info->vendor = >> >> > gtk_entry_get_text(GTK_ENTRY(lun_dialog->vendor_entry)); >> >> > + lun_info->product = >> >> > gtk_entry_get_text(GTK_ENTRY(lun_dialog->product_entry)); >> >> > + lun_info->revision = >> >> > gtk_entry_get_text(GTK_ENTRY(lun_dialog->revision_entry)); >> >> > + lun_info->loaded = >> >> > gtk_switch_get_active(GTK_SWITCH(lun_dialog->loaded_switch)); >> >> > + lun_info->locked = >> >> > gtk_switch_get_active(GTK_SWITCH(lun_dialog->locked_switch)); >> >> > +} >> >> > + >> >> > +/* Popup menu */ >> >> > +static void view_popup_menu_on_eject(GtkWidget *menuitem, gpointer >> >> > user_data) >> >> > +{ >> >> > + SpiceUsbDeviceWidget *self = SPICE_USB_DEVICE_WIDGET(user_data); >> >> > + SpiceUsbDeviceWidgetPrivate *priv = self->priv; >> >> > + GtkTreeSelection *select = >> >> > gtk_tree_view_get_selection(priv->cd_tree.tree_view); >> >> > + GtkTreeModel *tree_model; >> >> > + GtkTreeIter iter; >> >> > + >> >> > + if (gtk_tree_selection_get_selected(select, &tree_model, &iter)) { >> >> > + if (!tree_item_is_lun(priv->cd_tree.tree_store, &iter)) { >> >> > + SpiceUsbDevice *usb_device; >> >> > + gtk_tree_model_get(tree_model, &iter, COL_ITEM_DATA, >> >> > (gpointer *)&usb_device, -1); >> >> > + SPICE_DEBUG("%s - not applicable for USB device", >> >> > __FUNCTION__); >> >> > + } >> >> > + else { >> >> > + UsbWidgetLunItem *lun_item; >> >> > + gtk_tree_model_get(tree_model, &iter, COL_ITEM_DATA, >> >> > (gpointer *)&lun_item, -1); >> >> > + spice_usb_device_manager_device_lun_load( >> >> > + lun_item->manager, lun_item->device, lun_item->lun, >> >> > !lun_item->info.loaded); >> >> > + } >> >> > + } >> >> > + else { >> >> > + SPICE_DEBUG("%s - failed to get selection", __FUNCTION__); >> >> > + } >> >> > +} >> >> > + >> >> > +static void view_popup_menu_on_lock(GtkWidget *menuitem, gpointer >> >> > user_data) >> >> > +{ >> >> > + SpiceUsbDeviceWidget *self = SPICE_USB_DEVICE_WIDGET(user_data); >> >> > + SpiceUsbDeviceWidgetPrivate *priv = self->priv; >> >> > + GtkTreeSelection *select = >> >> > gtk_tree_view_get_selection(priv->cd_tree.tree_view); >> >> > + GtkTreeModel *tree_model; >> >> > + GtkTreeIter iter; >> >> > + >> >> > + if (gtk_tree_selection_get_selected(select, &tree_model, &iter)) { >> >> > + if (!tree_item_is_lun(priv->cd_tree.tree_store, &iter)) { >> >> > + SpiceUsbDevice *usb_device; >> >> > + gtk_tree_model_get(tree_model, &iter, COL_ITEM_DATA, >> >> > (gpointer *)&usb_device, -1); >> >> > + SPICE_DEBUG("%s - not applicable for USB device", >> >> > __FUNCTION__); >> >> > + } >> >> > + else { >> >> > + UsbWidgetLunItem *lun_item; >> >> > + gtk_tree_model_get(tree_model, &iter, COL_ITEM_DATA, >> >> > (gpointer *)&lun_item, -1); >> >> > + spice_usb_device_manager_device_lun_lock( >> >> > + lun_item->manager, lun_item->device, lun_item->lun, >> >> > !lun_item->info.locked); >> >> > + } >> >> > + } >> >> > + else { >> >> > + SPICE_DEBUG("%s - failed to get selection", __FUNCTION__); >> >> > + } >> >> > +} >> >> > + >> >> > +static void view_popup_menu_on_remove(GtkWidget *menuitem, gpointer >> >> > user_data) >> >> > +{ >> >> > + SpiceUsbDeviceWidget *self = SPICE_USB_DEVICE_WIDGET(user_data); >> >> > + SpiceUsbDeviceWidgetPrivate *priv = self->priv; >> >> > + GtkTreeSelection *select = >> >> > gtk_tree_view_get_selection(priv->cd_tree.tree_view); >> >> > + GtkTreeModel *tree_model; >> >> > + GtkTreeIter iter; >> >> > + >> >> > + if (gtk_tree_selection_get_selected(select, &tree_model, &iter)) { >> >> > + if (!tree_item_is_lun(priv->cd_tree.tree_store, &iter)) { >> >> > + SpiceUsbDevice *usb_device; >> >> > + gtk_tree_model_get(tree_model, &iter, COL_ITEM_DATA, >> >> > (gpointer *)&usb_device, -1); >> >> > + SPICE_DEBUG("Remove USB device"); >> >> > + } else { >> >> > + UsbWidgetLunItem *lun_item; >> >> > + gtk_tree_model_get(tree_model, &iter, COL_ITEM_DATA, >> >> > (gpointer *)&lun_item, -1); >> >> > + gtk_tree_selection_unselect_all(select); >> >> > + >> >> > spice_usb_device_manager_device_lun_remove(lun_item->manager, >> >> > lun_item->device, lun_item->lun); >> >> > + } >> >> > + } else { >> >> > + SPICE_DEBUG("Remove - failed to get selection"); >> >> > + } >> >> > +} >> >> > + >> >> > +static void view_popup_menu_on_settings(GtkWidget *menuitem, gpointer >> >> > user_data) >> >> > +{ >> >> > + SpiceUsbDeviceWidget *self = SPICE_USB_DEVICE_WIDGET(user_data); >> >> > + SpiceUsbDeviceWidgetPrivate *priv = self->priv; >> >> > + GtkTreeSelection *select = >> >> > gtk_tree_view_get_selection(priv->cd_tree.tree_view); >> >> > + GtkTreeModel *tree_model; >> >> > + GtkTreeIter iter; >> >> > + >> >> > + if (gtk_tree_selection_get_selected(select, &tree_model, &iter)) { >> >> > + if (!tree_item_is_lun(priv->cd_tree.tree_store, &iter)) { >> >> > + SPICE_DEBUG("No settings for USB device yet"); >> >> > + } else { >> >> > + lun_properties_dialog lun_dialog; >> >> > + UsbWidgetLunItem *lun_item; >> >> > + gint resp; >> >> > + >> >> > + gtk_tree_model_get(tree_model, &iter, COL_ITEM_DATA, >> >> > (gpointer *)&lun_item, -1); >> >> > + gtk_tree_selection_unselect_all(select); >> >> > + create_lun_properties_dialog(self, NULL, &lun_item->info, >> >> > &lun_dialog); >> >> > + >> >> > + resp = gtk_dialog_run(GTK_DIALOG(lun_dialog.dialog)); >> >> > + if (resp == GTK_RESPONSE_ACCEPT) { >> >> > + SpiceUsbDeviceLunInfo lun_info; >> >> > + SPICE_DEBUG("response is ACCEPT"); >> >> > + lun_properties_dialog_get_info(&lun_dialog, &lun_info); >> >> > + spice_usb_device_manager_device_lun_change_media( >> >> > + priv->manager, lun_item->device, lun_item->lun, >> >> > &lun_info); >> >> > + } else { >> >> > + SPICE_DEBUG("response is REJECT"); >> >> > + } >> >> > + gtk_widget_destroy(lun_dialog.dialog); >> >> > + } >> >> > + } else { >> >> > + SPICE_DEBUG("Remove - failed to get selection"); >> >> > + } >> >> > +} >> >> > + >> >> > +static GtkWidget *view_popup_add_menu_item(GtkWidget *menu, >> >> > + const gchar *label_str, >> >> > + const gchar *icon_name, >> >> > + GCallback cb_func, gpointer user_data) >> >> > +{ >> >> > + GtkWidget *menu_item = gtk_menu_item_new(); >> >> > + create_image_button_box(label_str, icon_name, menu_item); >> >> > + g_signal_connect(menu_item, "activate", cb_func, user_data); >> >> > + >> >> > + gtk_widget_show_all(menu_item); >> >> > + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); >> >> > + >> >> > + return menu_item; >> >> > +} >> >> > + >> >> > +static gboolean has_single_lun(SpiceUsbDeviceWidgetPrivate *priv, >> >> > SpiceUsbDevice *device) >> >> > +{ >> >> > + gboolean result; >> >> > + SpiceUsbDeviceManager *usb_dev_mgr = priv->manager; >> >> > + GArray *lun_array; >> >> > + lun_array = spice_usb_device_manager_get_device_luns(usb_dev_mgr, >> >> > device); >> >> > + result = lun_array && lun_array->len <= 1; >> >> > + if (lun_array) { >> >> > + g_array_unref(lun_array); >> >> > + } >> >> > + return result; >> >> > +} >> >> > + >> >> > +static void view_popup_menu(GtkTreeView *tree_view, GdkEventButton >> >> > *event, gpointer user_data) >> >> > +{ >> >> > + SpiceUsbDeviceWidget *self = SPICE_USB_DEVICE_WIDGET(user_data); >> >> > + SpiceUsbDeviceWidgetPrivate *priv = self->priv; >> >> > + GtkTreeSelection *select; >> >> > + GtkTreeModel *tree_model; >> >> > + GtkTreeIter iter; >> >> > + UsbWidgetLunItem *lun_item; >> >> > + gboolean is_loaded, is_locked; >> >> > + GtkTreeIter *usb_dev_iter; >> >> > + gboolean is_dev_connected; >> >> > + GtkWidget *menu; >> >> > + >> >> > + if (tree_view != priv->cd_tree.tree_view) { >> >> > + SPICE_DEBUG("Not applicable for USB device"); >> >> > + return; >> >> > + } >> >> > + >> >> > + select = gtk_tree_view_get_selection(tree_view); >> >> > + >> >> > + if (!gtk_tree_selection_get_selected(select, &tree_model, &iter)) { >> >> > + SPICE_DEBUG("No tree view row is selected"); >> >> > + return; >> >> > + } >> >> > + if (!tree_item_is_lun(priv->cd_tree.tree_store, &iter)) { >> >> > + SPICE_DEBUG("No settings for USB device yet"); >> >> > + return; >> >> > + } >> >> > + >> >> > + gtk_tree_model_get(tree_model, &iter, >> >> > + COL_ITEM_DATA, (gpointer *)&lun_item, >> >> > + COL_LOADED, &is_loaded, >> >> > + COL_LOCKED, &is_locked, >> >> > + -1); >> >> > + >> >> > + usb_dev_iter = >> >> > usb_widget_tree_store_find_usb_device(priv->cd_tree.tree_store, >> >> > lun_item->device); >> >> > + if (usb_dev_iter != NULL) { >> >> > + gtk_tree_model_get(tree_model, usb_dev_iter, >> >> > + COL_CONNECTED, &is_dev_connected, >> >> > + -1); >> >> > + g_free(usb_dev_iter); >> >> > + } else { >> >> > + is_dev_connected = FALSE; >> >> > + SPICE_DEBUG("Failed to find USB device for LUN: %s|%s", >> >> > + lun_item->info.vendor, lun_item->info.product); >> >> > + } >> >> > + SPICE_DEBUG("Right-click on LUN: %s|%s, dev connected:%d, lun >> >> > loaded:%d locked:%d", >> >> > + lun_item->info.vendor, lun_item->info.product, >> >> > + is_dev_connected, is_loaded, is_locked); >> >> > + >> >> > + /* Set up the menu */ >> >> > + menu = gtk_menu_new(); >> >> > + >> >> > + view_popup_add_menu_item(menu, "_Settings", "preferences-system", >> >> > + G_CALLBACK(view_popup_menu_on_settings), >> >> > user_data); >> >> > + if (is_loaded) { >> >> > + if (!is_locked) { >> >> > + view_popup_add_menu_item(menu, "_Lock", >> >> > "system-lock-screen", >> >> > + >> >> > G_CALLBACK(view_popup_menu_on_lock), user_data); >> >> > + view_popup_add_menu_item(menu, "_Eject", "media-eject", >> >> > + >> >> > G_CALLBACK(view_popup_menu_on_eject), user_data); >> >> > + } else { >> >> > + view_popup_add_menu_item(menu, "_Unlock", >> >> > "system-lock-screen", >> >> > + >> >> > G_CALLBACK(view_popup_menu_on_lock), user_data); >> >> > + } >> >> > + } else { >> >> > + view_popup_add_menu_item(menu, "_Load", "media-eject", >> >> > + G_CALLBACK(view_popup_menu_on_eject), >> >> > user_data); >> >> > + } >> >> > + >> >> > + if (!is_dev_connected || has_single_lun(priv, lun_item->device)) { >> >> > + view_popup_add_menu_item(menu, "_Remove", "edit-delete", >> >> > + >> >> > G_CALLBACK(view_popup_menu_on_remove), user_data); >> >> > + } >> >> > + >> >> > + gtk_widget_show_all(menu); >> >> > + gtk_menu_popup_at_pointer(GTK_MENU(menu), NULL); >> >> > +} >> >> > + >> >> > +static void treeview_select_current_row_by_pos(GtkTreeView *tree_view, >> >> > gint x, gint y) >> >> > +{ >> >> > + GtkTreeSelection *selection = >> >> > gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view)); >> >> > + if (gtk_tree_selection_count_selected_rows(selection) <= 1) { >> >> > + GtkTreePath *path; >> >> > + /* Get tree path for row that was clicked */ >> >> > + if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tree_view), x, >> >> > y, &path, NULL, NULL, NULL)) >> >> > + { >> >> > + gtk_tree_selection_unselect_all(selection); >> >> > + gtk_tree_selection_select_path(selection, path); >> >> > + gtk_tree_path_free(path); >> >> > + } >> >> > + } >> >> > +} >> >> > + >> >> > +static gboolean treeview_on_right_button_pressed_cb(GtkWidget *view, >> >> > GdkEventButton *event, gpointer user_data) >> >> > +{ >> >> > + GtkTreeView *tree_view = GTK_TREE_VIEW(view); >> >> > + /* single click with the right mouse button */ >> >> > + if (event->type == GDK_BUTTON_PRESS && event->button == 3) { >> >> > + /* select the row that was clicked, it will also provide the >> >> > context */ >> >> > + treeview_select_current_row_by_pos(tree_view, (gint)event->x, >> >> > (gint)event->y); >> >> > + view_popup_menu(tree_view, event, user_data); >> >> > + return TRUE; /* we handled this */ >> >> > + } else { >> >> > + return FALSE; /* we did not handle this */ >> >> > + } >> >> > +} >> >> > + >> >> > +static gboolean treeview_on_popup_key_pressed_cb(GtkWidget *view, >> >> > gpointer user_data) >> >> > +{ >> >> > + view_popup_menu(GTK_TREE_VIEW(view), NULL, user_data); >> >> > + return TRUE; /* we handled this */ >> >> > +} >> >> > + >> >> > +/* Add LUN dialog */ >> >> > + >> >> > +static void add_cd_lun_button_clicked_cb(GtkWidget *add_cd_button, >> >> > gpointer user_data) >> >> > +{ >> >> > + SpiceUsbDeviceWidget *self = SPICE_USB_DEVICE_WIDGET(user_data); >> >> > + SpiceUsbDeviceWidgetPrivate *priv = self->priv; >> >> > + GtkWidget *parent_window = gtk_widget_get_toplevel(add_cd_button); >> >> > + lun_properties_dialog lun_dialog; >> >> > + gint resp; >> >&g -- Marc-André Lureau
_______________________________________________ Spice-devel mailing list Spice-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/spice-devel