/* Camicro - Microscope camera viewer. * * Copyright (C) 2019 Nicolas Schodet * * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * Contact : * Web: http://ni.fr.eu.org/ * Email: */ #include #include "usb_source.h" struct fd_tag { int fd; gpointer tag; }; struct usb_source { GSource parent; libusb_context *usb; GArray *fd_tag; }; static gboolean usb_source_prepare(GSource *source, int *timeout) { return FALSE; } static gboolean usb_source_check(GSource *source) { GIOCondition revents = 0; struct usb_source *usb_source = (struct usb_source *)source; for (guint i = 0; i < usb_source->fd_tag->len; i++) { struct fd_tag e = g_array_index(usb_source->fd_tag, struct fd_tag, i); revents |= g_source_query_unix_fd(source, e.tag); } return revents != 0; } static gboolean usb_source_dispatch(GSource *source, GSourceFunc callback, gpointer user_data) { if (!callback) return G_SOURCE_CONTINUE; else return callback(user_data); } static void usb_source_finalize(GSource *source) { struct usb_source *usb_source = (struct usb_source *)source; libusb_set_pollfd_notifiers(usb_source->usb, NULL, NULL, NULL); for (guint i = 0; i < usb_source->fd_tag->len; i++) { struct fd_tag e = g_array_index(usb_source->fd_tag, struct fd_tag, i); g_source_remove_unix_fd(source, e.tag); } g_array_unref(usb_source->fd_tag); } static void usb_source_pollfd_added(int fd, short events, void *user_data) { struct usb_source *usb_source = user_data; if (!g_source_is_destroyed(&usb_source->parent)) { gpointer tag = g_source_add_unix_fd(&usb_source->parent, fd, events); struct fd_tag fd_tag = { fd, tag }; g_array_append_val(usb_source->fd_tag, fd_tag); } } static void usb_source_pollfd_removed(int fd, void *user_data) { struct usb_source *usb_source = user_data; if (!g_source_is_destroyed(&usb_source->parent)) { bool found = false; for (guint i = 0; !found && i < usb_source->fd_tag->len; i++) { struct fd_tag e = g_array_index(usb_source->fd_tag, struct fd_tag, i); if (e.fd == fd) { g_source_remove_unix_fd(&usb_source->parent, e.tag); g_array_remove_index_fast(usb_source->fd_tag, i); found = true; } } g_assert(found); } } GSource * usb_source_new(libusb_context *usb) { /* Get file descriptors. */ const struct libusb_pollfd **pollfds = libusb_get_pollfds(usb); if (!pollfds) g_error("unable to get libusb file descriptors"); /* Create source. */ static GSourceFuncs funcs = { .prepare = usb_source_prepare, .check = usb_source_check, .dispatch = usb_source_dispatch, .finalize = usb_source_finalize, }; GSource *source = g_source_new(&funcs, sizeof(struct usb_source)); struct usb_source *usb_source = (struct usb_source *)source; g_source_set_name(source, "usb"); usb_source->usb = usb; usb_source->fd_tag = g_array_new(FALSE, FALSE, sizeof(struct fd_tag)); g_assert(libusb_pollfds_handle_timeouts(usb)); /* Add existing file descriptors. */ for (const struct libusb_pollfd **pollfd = pollfds; *pollfd; pollfd++) usb_source_pollfd_added((*pollfd)->fd, (*pollfd)->events, usb_source); libusb_free_pollfds(pollfds); /* Connect notifiers. */ libusb_set_pollfd_notifiers(usb, usb_source_pollfd_added, usb_source_pollfd_removed, usb_source); /* Done. */ return &usb_source->parent; }