summaryrefslogtreecommitdiffstats
path: root/zbar/processor
diff options
context:
space:
mode:
Diffstat (limited to 'zbar/processor')
-rw-r--r--zbar/processor/lock.c228
-rw-r--r--zbar/processor/null.c57
-rw-r--r--zbar/processor/posix.c325
-rw-r--r--zbar/processor/posix.h132
-rw-r--r--zbar/processor/win.c332
-rw-r--r--zbar/processor/x.c266
6 files changed, 1340 insertions, 0 deletions
diff --git a/zbar/processor/lock.c b/zbar/processor/lock.c
new file mode 100644
index 0000000..2668b4e
--- /dev/null
+++ b/zbar/processor/lock.c
@@ -0,0 +1,228 @@
+/*------------------------------------------------------------------------
+ * Copyright 2007-2009 (c) Jeff Brown <spadix@users.sourceforge.net>
+ *
+ * This file is part of the ZBar Bar Code Reader.
+ *
+ * The ZBar Bar Code Reader is free software; you can redistribute it
+ * and/or modify it under the terms of the GNU Lesser Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * The ZBar Bar Code Reader 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 Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser Public License
+ * along with the ZBar Bar Code Reader; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ *
+ * http://sourceforge.net/projects/zbar
+ *------------------------------------------------------------------------*/
+
+#include <assert.h>
+#include "processor.h"
+
+/* the processor api lock is a recursive mutex with added capabilities
+ * to completely drop all lock levels before blocking and atomically
+ * unblock a waiting set. the lock is implemented using a variation
+ * of the "specific notification pattern" [cargill], which makes it
+ * easy to provide these features across platforms with consistent,
+ * predictable semantics. probably overkill, but additional overhead
+ * associated with this mechanism should fall in the noise, as locks
+ * are only exchanged O(frame/image)
+ *
+ * [cargill]
+ * http://www.profcon.com/profcon/cargill/jgf/9809/SpecificNotification.html
+ */
+
+static inline proc_waiter_t *proc_waiter_queue(zbar_processor_t *proc)
+{
+ proc_waiter_t *waiter = proc->free_waiter;
+ if (waiter) {
+ proc->free_waiter = waiter->next;
+ waiter->events = 0;
+ } else {
+ waiter = calloc(1, sizeof(proc_waiter_t));
+ _zbar_event_init(&waiter->notify);
+ }
+
+ waiter->next = NULL;
+ waiter->requester = _zbar_thread_self();
+
+ if (proc->wait_head)
+ proc->wait_tail->next = waiter;
+ else
+ proc->wait_head = waiter;
+ proc->wait_tail = waiter;
+ return (waiter);
+}
+
+static inline proc_waiter_t *proc_waiter_dequeue(zbar_processor_t *proc)
+{
+ proc_waiter_t *prev = proc->wait_next, *waiter;
+ if (prev)
+ waiter = prev->next;
+ else
+ waiter = proc->wait_head;
+ while (waiter && (waiter->events & EVENTS_PENDING)) {
+ prev = waiter;
+ proc->wait_next = waiter;
+ waiter = waiter->next;
+ }
+
+ if (waiter) {
+ if (prev)
+ prev->next = waiter->next;
+ else
+ proc->wait_head = waiter->next;
+ if (!waiter->next)
+ proc->wait_tail = prev;
+ waiter->next = NULL;
+
+ proc->lock_level = 1;
+ proc->lock_owner = waiter->requester;
+ }
+ return (waiter);
+}
+
+static inline void proc_waiter_release(zbar_processor_t *proc,
+ proc_waiter_t *waiter)
+{
+ if (waiter) {
+ waiter->next = proc->free_waiter;
+ proc->free_waiter = waiter;
+ }
+}
+
+int _zbar_processor_lock(zbar_processor_t *proc)
+{
+ proc_waiter_t *waiter;
+ if (!proc->lock_level) {
+ proc->lock_owner = _zbar_thread_self();
+ proc->lock_level = 1;
+ return (0);
+ }
+
+ if (_zbar_thread_is_self(proc->lock_owner)) {
+ proc->lock_level++;
+ return (0);
+ }
+
+ waiter = proc_waiter_queue(proc);
+ _zbar_event_wait(&waiter->notify, &proc->mutex, NULL);
+
+ assert(proc->lock_level == 1);
+ assert(_zbar_thread_is_self(proc->lock_owner));
+
+ proc_waiter_release(proc, waiter);
+ return (0);
+}
+
+int _zbar_processor_unlock(zbar_processor_t *proc, int all)
+{
+ assert(proc->lock_level > 0);
+ assert(_zbar_thread_is_self(proc->lock_owner));
+
+ if (all)
+ proc->lock_level = 0;
+ else
+ proc->lock_level--;
+
+ if (!proc->lock_level) {
+ proc_waiter_t *waiter = proc_waiter_dequeue(proc);
+ if (waiter)
+ _zbar_event_trigger(&waiter->notify);
+ }
+ return (0);
+}
+
+void _zbar_processor_notify(zbar_processor_t *proc, unsigned events)
+{
+ proc_waiter_t *waiter;
+ proc->wait_next = NULL;
+ for (waiter = proc->wait_head; waiter; waiter = waiter->next)
+ waiter->events =
+ ((waiter->events & ~events) | (events & EVENT_CANCELED));
+
+ if (!proc->lock_level) {
+ waiter = proc_waiter_dequeue(proc);
+ if (waiter)
+ _zbar_event_trigger(&waiter->notify);
+ }
+}
+
+static inline int proc_wait_unthreaded(zbar_processor_t *proc,
+ proc_waiter_t *waiter,
+ zbar_timer_t *timeout)
+{
+ int rc;
+ int blocking = proc->streaming && zbar_video_get_fd(proc->video) < 0;
+ _zbar_mutex_unlock(&proc->mutex);
+
+ rc = 1;
+ while (rc > 0 && (waiter->events & EVENTS_PENDING)) {
+ int reltime;
+ /* FIXME lax w/the locking (though shouldn't matter...) */
+ if (blocking) {
+ zbar_image_t *img = zbar_video_next_image(proc->video);
+ if (!img) {
+ rc = -1;
+ break;
+ }
+
+ /* FIXME reacquire API lock! (refactor w/video thread?) */
+ _zbar_mutex_lock(&proc->mutex);
+ _zbar_process_image(proc, img);
+ zbar_image_destroy(img);
+ _zbar_mutex_unlock(&proc->mutex);
+ }
+ reltime = _zbar_timer_check(timeout);
+ if (blocking && (reltime < 0 || reltime > MAX_INPUT_BLOCK))
+ reltime = MAX_INPUT_BLOCK;
+ rc = _zbar_processor_input_wait(proc, NULL, reltime);
+ }
+ _zbar_mutex_lock(&proc->mutex);
+ return (rc);
+}
+
+int _zbar_processor_wait(zbar_processor_t *proc, unsigned events,
+ zbar_timer_t *timeout)
+{
+ int save_level;
+ proc_waiter_t *waiter;
+ int rc;
+
+ _zbar_mutex_lock(&proc->mutex);
+ save_level = proc->lock_level;
+ waiter = proc_waiter_queue(proc);
+ waiter->events = events & EVENTS_PENDING;
+
+ _zbar_processor_unlock(proc, 1);
+ if (proc->threaded)
+ rc = _zbar_event_wait(&waiter->notify, &proc->mutex, timeout);
+ else
+ rc = proc_wait_unthreaded(proc, waiter, timeout);
+
+ if (rc <= 0 || !proc->threaded) {
+ /* reacquire api lock */
+ waiter->events &= EVENT_CANCELED;
+ proc->wait_next = NULL;
+ if (!proc->lock_level) {
+ proc_waiter_t *w = proc_waiter_dequeue(proc);
+ assert(w == waiter);
+ } else
+ _zbar_event_wait(&waiter->notify, &proc->mutex, NULL);
+ }
+ if (rc > 0 && (waiter->events & EVENT_CANCELED))
+ rc = -1;
+
+ assert(proc->lock_level == 1);
+ assert(_zbar_thread_is_self(proc->lock_owner));
+
+ proc->lock_level = save_level;
+ proc_waiter_release(proc, waiter);
+ _zbar_mutex_unlock(&proc->mutex);
+ return (rc);
+}
diff --git a/zbar/processor/null.c b/zbar/processor/null.c
new file mode 100644
index 0000000..5ca8d42
--- /dev/null
+++ b/zbar/processor/null.c
@@ -0,0 +1,57 @@
+/*------------------------------------------------------------------------
+ * Copyright 2008-2009 (c) Jeff Brown <spadix@users.sourceforge.net>
+ *
+ * This file is part of the ZBar Bar Code Reader.
+ *
+ * The ZBar Bar Code Reader is free software; you can redistribute it
+ * and/or modify it under the terms of the GNU Lesser Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * The ZBar Bar Code Reader 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 Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser Public License
+ * along with the ZBar Bar Code Reader; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ *
+ * http://sourceforge.net/projects/zbar
+ *------------------------------------------------------------------------*/
+
+#include "processor.h"
+
+static inline int null_error(void *m, const char *func)
+{
+ return (err_capture(m, SEV_ERROR, ZBAR_ERR_UNSUPPORTED, func,
+ "not compiled with output window support"));
+}
+
+int _zbar_processor_open(zbar_processor_t *proc, char *name, unsigned w,
+ unsigned h)
+{
+ return (null_error(proc, __func__));
+}
+
+int _zbar_processor_close(zbar_processor_t *proc)
+{
+ return (null_error(proc, __func__));
+}
+
+int _zbar_processor_set_visible(zbar_processor_t *proc, int vis)
+{
+ return (null_error(proc, __func__));
+}
+
+int _zbar_processor_set_size(zbar_processor_t *proc, unsigned width,
+ unsigned height)
+{
+ return (null_error(proc, __func__));
+}
+
+int _zbar_processor_invalidate(zbar_processor_t *proc)
+{
+ return (null_error(proc, __func__));
+}
diff --git a/zbar/processor/posix.c b/zbar/processor/posix.c
new file mode 100644
index 0000000..06b8d9a
--- /dev/null
+++ b/zbar/processor/posix.c
@@ -0,0 +1,325 @@
+/*------------------------------------------------------------------------
+ * Copyright 2007-2009 (c) Jeff Brown <spadix@users.sourceforge.net>
+ *
+ * This file is part of the ZBar Bar Code Reader.
+ *
+ * The ZBar Bar Code Reader is free software; you can redistribute it
+ * and/or modify it under the terms of the GNU Lesser Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * The ZBar Bar Code Reader 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 Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser Public License
+ * along with the ZBar Bar Code Reader; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ *
+ * http://sourceforge.net/projects/zbar
+ *------------------------------------------------------------------------*/
+
+#include "posix.h"
+#include <assert.h>
+#include <errno.h>
+#include <unistd.h>
+#include "processor.h"
+
+static inline int proc_sleep(int timeout)
+{
+ assert(timeout > 0);
+ struct timespec sleepns, remns;
+ sleepns.tv_sec = timeout / 1000;
+ sleepns.tv_nsec = (timeout % 1000) * 1000000;
+ while (nanosleep(&sleepns, &remns) && errno == EINTR)
+ sleepns = remns;
+ return (1);
+}
+
+int _zbar_event_init(zbar_event_t *event)
+{
+ event->state = 0;
+ event->pollfd = -1;
+#ifdef HAVE_LIBPTHREAD
+ pthread_cond_init(&event->cond, NULL);
+#endif
+ return (0);
+}
+
+void _zbar_event_destroy(zbar_event_t *event)
+{
+ event->state = -1;
+ event->pollfd = -1;
+#ifdef HAVE_LIBPTHREAD
+ pthread_cond_destroy(&event->cond);
+#endif
+}
+
+/* lock must be held */
+void _zbar_event_trigger(zbar_event_t *event)
+{
+ event->state = 1;
+#ifdef HAVE_LIBPTHREAD
+ pthread_cond_broadcast(&event->cond);
+#endif
+ if (event->pollfd >= 0) {
+ unsigned i = 0; /* unused */
+ if (write(event->pollfd, &i, sizeof(unsigned)) < 0)
+ perror("");
+ event->pollfd = -1;
+ }
+}
+
+#ifdef HAVE_LIBPTHREAD
+
+/* lock must be held */
+int _zbar_event_wait(zbar_event_t *event, zbar_mutex_t *lock,
+ zbar_timer_t *timeout)
+{
+ int rc = 0;
+ while (!rc && !event->state) {
+ if (!timeout)
+ rc = pthread_cond_wait(&event->cond, lock);
+ else {
+ struct timespec *timer;
+#if _POSIX_TIMERS > 0
+ timer = timeout;
+#else
+ struct timespec tmp;
+ tmp.tv_sec = timeout->tv_sec;
+ tmp.tv_nsec = timeout->tv_usec * 1000;
+ timer = &tmp;
+#endif
+ rc = pthread_cond_timedwait(&event->cond, lock, timer);
+ }
+ }
+
+ /* consume/reset event */
+ event->state = 0;
+
+ if (!rc)
+ return (1); /* got event */
+ if (rc == ETIMEDOUT)
+ return (0); /* timed out */
+ return (-1); /* error (FIXME save info) */
+}
+
+int _zbar_thread_start(zbar_thread_t *thr, zbar_thread_proc_t *proc, void *arg,
+ zbar_mutex_t *lock)
+{
+ if (thr->started || thr->running)
+ return (-1 /*FIXME*/);
+ thr->started = 1;
+ _zbar_event_init(&thr->notify);
+ _zbar_event_init(&thr->activity);
+
+ int rc = 0;
+ _zbar_mutex_lock(lock);
+ if (pthread_create(&thr->tid, NULL, proc, arg) ||
+ _zbar_event_wait(&thr->activity, lock, NULL) < 0 || !thr->running) {
+ thr->started = 0;
+ _zbar_event_destroy(&thr->notify);
+ _zbar_event_destroy(&thr->activity);
+ /*rc = err_capture_num(proc, SEV_ERROR, ZBAR_ERR_SYSTEM,
+ __func__, "spawning thread", rc);*/
+ rc = -1 /*FIXME*/;
+ }
+ _zbar_mutex_unlock(lock);
+ return (rc);
+}
+
+int _zbar_thread_stop(zbar_thread_t *thr, zbar_mutex_t *lock)
+{
+ if (thr->started) {
+ thr->started = 0;
+ _zbar_event_trigger(&thr->notify);
+ while (thr->running)
+ /* FIXME time out and abandon? */
+ _zbar_event_wait(&thr->activity, lock, NULL);
+ pthread_join(thr->tid, NULL);
+ _zbar_event_destroy(&thr->notify);
+ _zbar_event_destroy(&thr->activity);
+ }
+ return (0);
+}
+
+#else
+
+int _zbar_event_wait(zbar_event_t *event, zbar_mutex_t *lock,
+ zbar_timer_t *timeout)
+{
+ int rc = !event->state;
+ if (rc) {
+ if (!timeout)
+ /* FIXME was that error or hang? */
+ return (-1);
+
+ int sleep = _zbar_timer_check(timeout);
+ if (sleep)
+ proc_sleep(sleep);
+ }
+
+ rc = !event->state;
+
+ /* consume/reset event */
+ event->state = 0;
+
+ return (rc);
+}
+
+#endif
+
+/* used by poll interface. lock is already held */
+static int proc_video_handler(zbar_processor_t *proc, int i)
+{
+ _zbar_mutex_lock(&proc->mutex);
+ _zbar_processor_lock(proc);
+ _zbar_mutex_unlock(&proc->mutex);
+
+ zbar_image_t *img = NULL;
+ if (proc->streaming) {
+ /* not expected to block */
+ img = zbar_video_next_image(proc->video);
+ if (img)
+ _zbar_process_image(proc, img);
+ }
+
+ _zbar_mutex_lock(&proc->mutex);
+ _zbar_processor_unlock(proc, 0);
+ _zbar_mutex_unlock(&proc->mutex);
+ if (img)
+ zbar_image_destroy(img);
+ return (0);
+}
+
+static inline void proc_cache_polling(processor_state_t *state)
+{
+ /* make a thread-local copy of polling data */
+ int n = state->thr_polling.num = state->polling.num;
+ alloc_polls(&state->thr_polling);
+ memcpy(state->thr_polling.fds, state->polling.fds,
+ n * sizeof(struct pollfd));
+ memcpy(state->thr_polling.handlers, state->polling.handlers,
+ n * sizeof(poll_handler_t *));
+}
+
+static int proc_kick_handler(zbar_processor_t *proc, int i)
+{
+ processor_state_t *state = proc->state;
+ zprintf(5, "kicking %d fds\n", state->polling.num);
+
+ unsigned junk[2];
+ int rc = read(state->kick_fds[0], junk, 2 * sizeof(unsigned));
+
+ assert(proc->threaded);
+ _zbar_mutex_lock(&proc->mutex);
+ proc_cache_polling(proc->state);
+ _zbar_mutex_unlock(&proc->mutex);
+ return (rc);
+}
+
+static inline int proc_poll_inputs(zbar_processor_t *proc, int timeout)
+{
+ processor_state_t *state = proc->state;
+ if (state->pre_poll_handler)
+ state->pre_poll_handler(proc, -1);
+
+ poll_desc_t *p = &state->thr_polling;
+ assert(p->num);
+ int rc = poll(p->fds, p->num, timeout);
+ if (rc <= 0)
+ /* FIXME detect and handle fatal errors (somehow) */
+ return (rc);
+ int i;
+ for (i = p->num - 1; i >= 0; i--)
+ if (p->fds[i].revents) {
+ if (p->handlers[i])
+ p->handlers[i](proc, i);
+ p->fds[i].revents = 0; /* debug */
+ rc--;
+ }
+ assert(!rc);
+ return (1);
+}
+
+int _zbar_processor_input_wait(zbar_processor_t *proc, zbar_event_t *event,
+ int timeout)
+{
+ processor_state_t *state = proc->state;
+ if (state->thr_polling.num) {
+ if (event) {
+ _zbar_mutex_lock(&proc->mutex);
+ event->pollfd = state->kick_fds[1];
+ _zbar_mutex_unlock(&proc->mutex);
+ }
+ return (proc_poll_inputs(proc, timeout));
+ } else if (timeout)
+ return (proc_sleep(timeout));
+ return (-1);
+}
+
+int _zbar_processor_init(zbar_processor_t *proc)
+{
+ processor_state_t *state = proc->state =
+ calloc(1, sizeof(processor_state_t));
+ state->kick_fds[0] = state->kick_fds[1] = -1;
+
+ if (proc->threaded) {
+ /* FIXME check errors */
+ if (pipe(state->kick_fds))
+ return (err_capture(proc, SEV_FATAL, ZBAR_ERR_SYSTEM, __func__,
+ "failed to open pipe"));
+ add_poll(proc, state->kick_fds[0], proc_kick_handler);
+ proc_cache_polling(proc->state);
+ }
+ return (0);
+}
+
+int _zbar_processor_cleanup(zbar_processor_t *proc)
+{
+ processor_state_t *state = proc->state;
+ if (proc->threaded) {
+ close(state->kick_fds[0]);
+ close(state->kick_fds[1]);
+ state->kick_fds[0] = state->kick_fds[1] = -1;
+ }
+ if (state->polling.fds) {
+ free(state->polling.fds);
+ state->polling.fds = NULL;
+ if (!proc->threaded)
+ state->thr_polling.fds = NULL;
+ }
+ if (state->polling.handlers) {
+ free(state->polling.handlers);
+ state->polling.handlers = NULL;
+ if (!proc->threaded)
+ state->thr_polling.handlers = NULL;
+ }
+ if (state->thr_polling.fds) {
+ free(state->thr_polling.fds);
+ state->thr_polling.fds = NULL;
+ }
+ if (state->thr_polling.handlers) {
+ free(state->thr_polling.handlers);
+ state->thr_polling.handlers = NULL;
+ }
+ free(proc->state);
+ proc->state = NULL;
+ return (0);
+}
+
+int _zbar_processor_enable(zbar_processor_t *proc)
+{
+ int vid_fd = zbar_video_get_fd(proc->video);
+ if (vid_fd < 0)
+ return (0);
+
+ if (proc->streaming)
+ add_poll(proc, vid_fd, proc_video_handler);
+ else
+ remove_poll(proc, vid_fd);
+ /* FIXME failure recovery? */
+ return (0);
+}
diff --git a/zbar/processor/posix.h b/zbar/processor/posix.h
new file mode 100644
index 0000000..7ed05cc
--- /dev/null
+++ b/zbar/processor/posix.h
@@ -0,0 +1,132 @@
+/*------------------------------------------------------------------------
+ * Copyright 2007-2009 (c) Jeff Brown <spadix@users.sourceforge.net>
+ *
+ * This file is part of the ZBar Bar Code Reader.
+ *
+ * The ZBar Bar Code Reader is free software; you can redistribute it
+ * and/or modify it under the terms of the GNU Lesser Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * The ZBar Bar Code Reader 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 Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser Public License
+ * along with the ZBar Bar Code Reader; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ *
+ * http://sourceforge.net/projects/zbar
+ *------------------------------------------------------------------------*/
+#ifndef _PROCESSOR_POSIX_H_
+#define _PROCESSOR_POSIX_H_
+
+#include "processor.h"
+
+#ifdef HAVE_POLL_H
+#include <poll.h>
+#endif
+
+#ifdef HAVE_POLL_H
+typedef int(poll_handler_t)(zbar_processor_t *, int);
+
+/* poll information */
+typedef struct poll_desc_s {
+ int num; /* number of descriptors */
+ struct pollfd *fds; /* poll descriptors */
+ poll_handler_t **handlers; /* poll handlers */
+} poll_desc_t;
+#endif
+
+struct processor_state_s {
+#ifdef HAVE_POLL_H
+ poll_desc_t polling; /* polling registration */
+ poll_desc_t thr_polling; /* thread copy */
+#endif
+ int kick_fds[2]; /* poll kicker */
+ poll_handler_t *pre_poll_handler; /* special case */
+};
+
+#ifdef HAVE_POLL_H
+static inline int alloc_polls(volatile poll_desc_t *p)
+{
+ p->fds = realloc(p->fds, p->num * sizeof(struct pollfd));
+ p->handlers = realloc(p->handlers, p->num * sizeof(poll_handler_t *));
+ /* FIXME should check for ENOMEM */
+ return (0);
+}
+
+static inline int add_poll(zbar_processor_t *proc, int fd,
+ poll_handler_t *handler)
+{
+ processor_state_t *state = proc->state;
+
+ _zbar_mutex_lock(&proc->mutex);
+
+ poll_desc_t *polling = &state->polling;
+ unsigned i = polling->num++;
+ zprintf(5, "[%d] fd=%d handler=%p\n", i, fd, handler);
+ if (!alloc_polls(polling)) {
+ memset(&polling->fds[i], 0, sizeof(struct pollfd));
+ polling->fds[i].fd = fd;
+ polling->fds[i].events = POLLIN;
+ polling->handlers[i] = handler;
+ } else
+ i = -1;
+
+ _zbar_mutex_unlock(&proc->mutex);
+
+ if (proc->input_thread.started) {
+ assert(state->kick_fds[1] >= 0);
+ if (write(state->kick_fds[1], &i /* unused */, sizeof(unsigned)) < 0)
+ return (-1);
+ } else if (!proc->threaded) {
+ state->thr_polling.num = polling->num;
+ state->thr_polling.fds = polling->fds;
+ state->thr_polling.handlers = polling->handlers;
+ }
+ return (i);
+}
+
+static inline int remove_poll(zbar_processor_t *proc, int fd)
+{
+ processor_state_t *state = proc->state;
+
+ _zbar_mutex_lock(&proc->mutex);
+
+ poll_desc_t *polling = &state->polling;
+ int i;
+ for (i = polling->num - 1; i >= 0; i--)
+ if (polling->fds[i].fd == fd)
+ break;
+ zprintf(5, "[%d] fd=%d n=%d\n", i, fd, polling->num);
+
+ if (i >= 0) {
+ if (i + 1 < polling->num) {
+ int n = polling->num - i - 1;
+ memmove(&polling->fds[i], &polling->fds[i + 1],
+ n * sizeof(struct pollfd));
+ memmove(&polling->handlers[i], &polling->handlers[i + 1],
+ n * sizeof(poll_handler_t));
+ }
+ polling->num--;
+ i = alloc_polls(polling);
+ }
+
+ _zbar_mutex_unlock(&proc->mutex);
+
+ if (proc->input_thread.started) {
+ if (write(state->kick_fds[1], &i /* unused */, sizeof(unsigned)) < 0)
+ return (-1);
+ } else if (!proc->threaded) {
+ state->thr_polling.num = polling->num;
+ state->thr_polling.fds = polling->fds;
+ state->thr_polling.handlers = polling->handlers;
+ }
+ return (i);
+}
+#endif
+
+#endif
diff --git a/zbar/processor/win.c b/zbar/processor/win.c
new file mode 100644
index 0000000..48fa9ac
--- /dev/null
+++ b/zbar/processor/win.c
@@ -0,0 +1,332 @@
+/*------------------------------------------------------------------------
+ * Copyright 2007-2009 (c) Jeff Brown <spadix@users.sourceforge.net>
+ *
+ * This file is part of the ZBar Bar Code Reader.
+ *
+ * The ZBar Bar Code Reader is free software; you can redistribute it
+ * and/or modify it under the terms of the GNU Lesser Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * The ZBar Bar Code Reader 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 Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser Public License
+ * along with the ZBar Bar Code Reader; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ *
+ * http://sourceforge.net/projects/zbar
+ *------------------------------------------------------------------------*/
+
+#include <assert.h>
+#if defined(_MSC_VER)
+#include <WinSock2.h>
+#endif
+#include <windows.h>
+#include "processor.h"
+
+#define WIN_STYLE \
+ (WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX)
+#define EXT_STYLE (WS_EX_APPWINDOW | WS_EX_OVERLAPPEDWINDOW)
+
+struct processor_state_s {
+ ATOM registeredClass;
+};
+
+int _zbar_event_init(zbar_event_t *event)
+{
+ *event = CreateEvent(NULL, 0, 0, NULL);
+ return ((*event) ? 0 : -1);
+}
+
+void _zbar_event_destroy(zbar_event_t *event)
+{
+ if (*event) {
+ CloseHandle(*event);
+ *event = NULL;
+ }
+}
+
+void _zbar_event_trigger(zbar_event_t *event)
+{
+ SetEvent(*event);
+}
+
+/* lock must be held */
+int _zbar_event_wait(zbar_event_t *event, zbar_mutex_t *lock,
+ zbar_timer_t *timeout)
+{
+ int rc;
+ if (lock)
+ _zbar_mutex_unlock(lock);
+ rc = WaitForSingleObject(*event, _zbar_timer_check(timeout));
+ if (lock)
+ _zbar_mutex_lock(lock);
+
+ if (!rc)
+ return (1); /* got event */
+ if (rc == WAIT_TIMEOUT)
+ return (0); /* timed out */
+ return (-1); /* error (FIXME save info) */
+}
+
+int _zbar_thread_start(zbar_thread_t *thr, zbar_thread_proc_t *proc, void *arg,
+ zbar_mutex_t *lock)
+{
+ HANDLE hthr;
+ int rc;
+ if (thr->started || thr->running)
+ return (-1 /*FIXME*/);
+ thr->started = 1;
+ _zbar_event_init(&thr->notify);
+ _zbar_event_init(&thr->activity);
+
+ hthr = CreateThread(NULL, 0, proc, arg, 0, NULL);
+ rc = (!hthr || _zbar_event_wait(&thr->activity, NULL, NULL) < 0 ||
+ !thr->running);
+ CloseHandle(hthr);
+ if (rc) {
+ thr->started = 0;
+ _zbar_event_destroy(&thr->notify);
+ _zbar_event_destroy(&thr->activity);
+ return (-1 /*FIXME*/);
+ }
+ return (0);
+}
+
+int _zbar_thread_stop(zbar_thread_t *thr, zbar_mutex_t *lock)
+{
+ if (thr->started) {
+ thr->started = 0;
+ _zbar_event_trigger(&thr->notify);
+ while (thr->running)
+ /* FIXME time out and abandon? */
+ _zbar_event_wait(&thr->activity, lock, NULL);
+ _zbar_event_destroy(&thr->notify);
+ _zbar_event_destroy(&thr->activity);
+ }
+ return (0);
+}
+
+static LRESULT CALLBACK win_handle_event(HWND hwnd, UINT message, WPARAM wparam,
+ LPARAM lparam)
+{
+ zbar_processor_t *proc =
+ (zbar_processor_t *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
+ /* initialized during window creation */
+ if (message == WM_NCCREATE) {
+ proc = ((LPCREATESTRUCT)lparam)->lpCreateParams;
+ assert(proc);
+ SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)proc);
+ proc->display = hwnd;
+
+ zbar_window_attach(proc->window, proc->display, proc->xwin);
+ } else if (!proc)
+ return (DefWindowProc(hwnd, message, wparam, lparam));
+
+ switch (message) {
+ case WM_SIZE: {
+ RECT r;
+ GetClientRect(hwnd, &r);
+ zprintf(3, "WM_SIZE %ldx%ld\n", r.right, r.bottom);
+ assert(proc);
+ zbar_window_resize(proc->window, r.right, r.bottom);
+ InvalidateRect(hwnd, NULL, 0);
+ return (0);
+ }
+
+ case WM_PAINT: {
+ PAINTSTRUCT ps;
+ BeginPaint(hwnd, &ps);
+ if (zbar_window_redraw(proc->window)) {
+ HDC hdc = GetDC(hwnd);
+ RECT r;
+ GetClientRect(hwnd, &r);
+ FillRect(hdc, &r, GetStockObject(BLACK_BRUSH));
+ ReleaseDC(hwnd, hdc);
+ }
+ EndPaint(hwnd, &ps);
+ return (0);
+ }
+
+ case WM_CHAR: {
+ _zbar_processor_handle_input(proc, wparam);
+ return (0);
+ }
+
+ case WM_LBUTTONDOWN: {
+ _zbar_processor_handle_input(proc, 1);
+ return (0);
+ }
+
+ case WM_MBUTTONDOWN: {
+ _zbar_processor_handle_input(proc, 2);
+ return (0);
+ }
+
+ case WM_RBUTTONDOWN: {
+ _zbar_processor_handle_input(proc, 3);
+ return (0);
+ }
+
+ case WM_CLOSE: {
+ zprintf(3, "WM_CLOSE\n");
+ _zbar_processor_handle_input(proc, -1);
+ return (1);
+ }
+
+ case WM_DESTROY: {
+ zprintf(3, "WM_DESTROY\n");
+ proc->display = NULL;
+ zbar_window_attach(proc->window, NULL, 0);
+ return (0);
+ }
+ }
+ return (DefWindowProc(hwnd, message, wparam, lparam));
+}
+
+static inline int win_handle_events(zbar_processor_t *proc)
+{
+ int rc = 0;
+ while (1) {
+ MSG msg;
+ rc = PeekMessage(&msg, proc->display, 0, 0, PM_NOYIELD | PM_REMOVE);
+ if (!rc)
+ return (0);
+ if (rc < 0)
+ return (err_capture(proc, SEV_ERROR, ZBAR_ERR_WINAPI, __func__,
+ "failed to obtain event"));
+
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+}
+
+int _zbar_processor_init(zbar_processor_t *proc)
+{
+ proc->state = calloc(1, sizeof(processor_state_t));
+ return (0);
+}
+
+int _zbar_processor_cleanup(zbar_processor_t *proc)
+{
+ free(proc->state);
+ proc->state = 0;
+ return (0);
+}
+
+int _zbar_processor_input_wait(zbar_processor_t *proc, zbar_event_t *event,
+ int timeout)
+{
+ int n = (event) ? 1 : 0;
+ int rc = MsgWaitForMultipleObjects(n, event, 0, timeout, QS_ALLINPUT);
+
+ if (rc == n) {
+ if (win_handle_events(proc) < 0)
+ return (-1);
+ return (1);
+ }
+ if (!rc)
+ return (1);
+ if (rc == WAIT_TIMEOUT)
+ return (0);
+ return (-1);
+}
+
+int _zbar_processor_enable(zbar_processor_t *proc)
+{
+ return (0);
+}
+
+static inline ATOM win_register_class(HINSTANCE hmod)
+{
+ BYTE and_mask[1] = { 0xff };
+ BYTE xor_mask[1] = { 0x00 };
+
+ WNDCLASSEX wc = {
+ sizeof(WNDCLASSEX),
+ 0,
+ };
+ wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
+ wc.hInstance = hmod;
+ wc.lpfnWndProc = win_handle_event;
+ wc.lpszClassName = "_ZBar Class";
+ wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
+ wc.hCursor = CreateCursor(hmod, 0, 0, 1, 1, and_mask, xor_mask);
+
+ return (RegisterClassEx(&wc));
+}
+
+int _zbar_processor_open(zbar_processor_t *proc, char *title, unsigned width,
+ unsigned height)
+{
+ ATOM wca;
+ RECT r = { 0, 0, width, height };
+ HMODULE hmod = NULL;
+ if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
+ GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
+ (void *)_zbar_processor_open, (HINSTANCE *)&hmod))
+ return (err_capture(proc, SEV_ERROR, ZBAR_ERR_WINAPI, __func__,
+ "failed to obtain module handle"));
+
+ wca = win_register_class(hmod);
+ if (!wca)
+ return (err_capture(proc, SEV_ERROR, ZBAR_ERR_WINAPI, __func__,
+ "failed to register window class"));
+
+ proc->state->registeredClass = wca;
+ AdjustWindowRectEx(&r, WIN_STYLE, 0, EXT_STYLE);
+ proc->display = CreateWindowEx(EXT_STYLE, (LPCTSTR)(long)wca, "ZBar",
+ WIN_STYLE, CW_USEDEFAULT, CW_USEDEFAULT,
+ r.right - r.left, r.bottom - r.top, NULL,
+ NULL, hmod, proc);
+
+ if (!proc->display)
+ return (err_capture(proc, SEV_ERROR, ZBAR_ERR_WINAPI, __func__,
+ "failed to open window"));
+ return (0);
+}
+
+int _zbar_processor_close(zbar_processor_t *proc)
+{
+ if (proc->display) {
+ DestroyWindow(proc->display);
+ UnregisterClass((LPCTSTR)(long)proc->state->registeredClass, 0);
+ proc->display = NULL;
+ }
+ return (0);
+}
+
+int _zbar_processor_set_visible(zbar_processor_t *proc, int visible)
+{
+ ShowWindow(proc->display, (visible) ? SW_SHOWNORMAL : SW_HIDE);
+ if (visible)
+ InvalidateRect(proc->display, NULL, 0);
+ /* no error conditions */
+ return (0);
+}
+
+int _zbar_processor_set_size(zbar_processor_t *proc, unsigned width,
+ unsigned height)
+{
+ RECT r = { 0, 0, width, height };
+ AdjustWindowRectEx(&r, GetWindowLong(proc->display, GWL_STYLE), 0,
+ GetWindowLong(proc->display, GWL_EXSTYLE));
+ if (!SetWindowPos(
+ proc->display, NULL, 0, 0, r.right - r.left, r.bottom - r.top,
+ SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOREPOSITION))
+ return (-1 /*FIXME*/);
+
+ return (0);
+}
+
+int _zbar_processor_invalidate(zbar_processor_t *proc)
+{
+ if (!InvalidateRect(proc->display, NULL, 0))
+ return (-1 /*FIXME*/);
+
+ return (0);
+}
diff --git a/zbar/processor/x.c b/zbar/processor/x.c
new file mode 100644
index 0000000..7cb1a6e
--- /dev/null
+++ b/zbar/processor/x.c
@@ -0,0 +1,266 @@
+/*------------------------------------------------------------------------
+ * Copyright 2007-2009 (c) Jeff Brown <spadix@users.sourceforge.net>
+ *
+ * This file is part of the ZBar Bar Code Reader.
+ *
+ * The ZBar Bar Code Reader is free software; you can redistribute it
+ * and/or modify it under the terms of the GNU Lesser Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * The ZBar Bar Code Reader 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 Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser Public License
+ * along with the ZBar Bar Code Reader; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ *
+ * http://sourceforge.net/projects/zbar
+ *------------------------------------------------------------------------*/
+
+#include "posix.h"
+#include "processor.h"
+#include "window.h"
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/keysym.h>
+
+static inline int x_handle_event(zbar_processor_t *proc)
+{
+ XEvent ev;
+ XNextEvent(proc->display, &ev);
+
+ switch (ev.type) {
+ case Expose: {
+ /* FIXME ignore when running(?) */
+ XExposeEvent *exp = (XExposeEvent *)&ev;
+ while (1) {
+ assert(ev.type == Expose);
+ _zbar_window_expose(proc->window, exp->x, exp->y, exp->width,
+ exp->height);
+ if (!exp->count)
+ break;
+ XNextEvent(proc->display, &ev);
+ }
+ zbar_window_redraw(proc->window);
+ break;
+ }
+
+ case ConfigureNotify:
+ zprintf(3, "resized to %d x %d\n", ev.xconfigure.width,
+ ev.xconfigure.height);
+ zbar_window_resize(proc->window, ev.xconfigure.width,
+ ev.xconfigure.height);
+ _zbar_processor_invalidate(proc);
+ break;
+
+ case ClientMessage:
+ if ((ev.xclient.message_type ==
+ XInternAtom(proc->display, "WM_PROTOCOLS", 0)) &&
+ ev.xclient.format == 32 &&
+ (ev.xclient.data.l[0] ==
+ XInternAtom(proc->display, "WM_DELETE_WINDOW", 0))) {
+ zprintf(3, "WM_DELETE_WINDOW\n");
+ return (_zbar_processor_handle_input(proc, -1));
+ }
+ break;
+
+ case KeyPress: {
+ KeySym key = XLookupKeysym(&ev.xkey, 0);
+ if (IsModifierKey(key))
+ break;
+ if ((key & 0xff00) == 0xff00)
+ key &= 0x00ff;
+ zprintf(16, "KeyPress(%04lx)\n", key);
+ /* FIXME this doesn't really work... */
+ return (_zbar_processor_handle_input(proc, key & 0xffff));
+ }
+ case ButtonPress: {
+ zprintf(16, "ButtonPress(%d)\n", ev.xbutton.button);
+ int idx = 1;
+ switch (ev.xbutton.button) {
+ case Button2:
+ idx = 2;
+ break;
+ case Button3:
+ idx = 3;
+ break;
+ case Button4:
+ idx = 4;
+ break;
+ case Button5:
+ idx = 5;
+ break;
+ }
+ return (_zbar_processor_handle_input(proc, idx));
+ }
+
+ case DestroyNotify:
+ zprintf(16, "DestroyNotify\n");
+ zbar_window_attach(proc->window, NULL, 0);
+ proc->xwin = 0;
+ return (0);
+
+ default:
+ /* ignored */;
+ }
+ return (0);
+}
+
+static int x_handle_events(zbar_processor_t *proc)
+{
+ int rc = 0;
+ while (rc >= 0 && XPending(proc->display))
+ rc = x_handle_event(proc);
+ return (rc);
+}
+
+static int x_connection_handler(zbar_processor_t *proc, int i)
+{
+ _zbar_mutex_lock(&proc->mutex);
+ _zbar_processor_lock(proc);
+ _zbar_mutex_unlock(&proc->mutex);
+
+ x_handle_events(proc);
+
+ _zbar_mutex_lock(&proc->mutex);
+ _zbar_processor_unlock(proc, 0);
+ _zbar_mutex_unlock(&proc->mutex);
+ return (0);
+}
+
+static int x_internal_handler(zbar_processor_t *proc, int i)
+{
+ XProcessInternalConnection(proc->display, proc->state->polling.fds[i].fd);
+ x_connection_handler(proc, i);
+ return (0);
+}
+
+static void x_internal_watcher(Display *display, XPointer client_arg, int fd,
+ Bool opening, XPointer *watch_arg)
+{
+ zbar_processor_t *proc = (void *)client_arg;
+ if (opening)
+ add_poll(proc, fd, x_internal_handler);
+ else
+ remove_poll(proc, fd);
+}
+
+int _zbar_processor_open(zbar_processor_t *proc, char *title, unsigned width,
+ unsigned height)
+{
+ proc->display = XOpenDisplay(NULL);
+ if (!proc->display)
+ return (err_capture_str(proc, SEV_ERROR, ZBAR_ERR_XDISPLAY, __func__,
+ "unable to open X display",
+ XDisplayName(NULL)));
+
+ add_poll(proc, ConnectionNumber(proc->display), x_connection_handler);
+ XAddConnectionWatch(proc->display, x_internal_watcher, (void *)proc);
+ /* must also flush X event queue before polling */
+ proc->state->pre_poll_handler = x_connection_handler;
+
+ int screen = DefaultScreen(proc->display);
+ XSetWindowAttributes attr;
+ attr.event_mask =
+ (ExposureMask | StructureNotifyMask | KeyPressMask | ButtonPressMask);
+
+ proc->xwin = XCreateWindow(proc->display, RootWindow(proc->display, screen),
+ 0, 0, width, height, 0, CopyFromParent,
+ InputOutput, CopyFromParent, CWEventMask, &attr);
+ if (!proc->xwin) {
+ XCloseDisplay(proc->display);
+ return (err_capture(proc, SEV_ERROR, ZBAR_ERR_XPROTO, __func__,
+ "creating window"));
+ }
+
+ XStoreName(proc->display, proc->xwin, title);
+
+ XClassHint *class_hint = XAllocClassHint();
+ class_hint->res_name = "zbar";
+ class_hint->res_class = "zbar";
+ XSetClassHint(proc->display, proc->xwin, class_hint);
+ XFree(class_hint);
+ class_hint = NULL;
+
+ Atom wm_delete_window = XInternAtom(proc->display, "WM_DELETE_WINDOW", 0);
+ if (wm_delete_window)
+ XSetWMProtocols(proc->display, proc->xwin, &wm_delete_window, 1);
+
+ if (zbar_window_attach(proc->window, proc->display, proc->xwin))
+ return (err_copy(proc, proc->window));
+ return (0);
+}
+
+int _zbar_processor_close(zbar_processor_t *proc)
+{
+ if (proc->window)
+ zbar_window_attach(proc->window, NULL, 0);
+
+ if (proc->display) {
+ if (proc->xwin) {
+ XDestroyWindow(proc->display, proc->xwin);
+ proc->xwin = 0;
+ }
+ proc->state->pre_poll_handler = NULL;
+ remove_poll(proc, ConnectionNumber(proc->display));
+ XCloseDisplay(proc->display);
+ proc->display = NULL;
+ }
+ return (0);
+}
+
+int _zbar_processor_invalidate(zbar_processor_t *proc)
+{
+ if (!proc->display || !proc->xwin)
+ return (0);
+ XClearArea(proc->display, proc->xwin, 0, 0, 0, 0, 1);
+ XFlush(proc->display);
+ return (0);
+}
+
+int _zbar_processor_set_size(zbar_processor_t *proc, unsigned width,
+ unsigned height)
+{
+ if (!proc->display || !proc->xwin)
+ return (0);
+
+ /* refuse to resize greater than (default) screen size */
+ XWindowAttributes attr;
+ XGetWindowAttributes(proc->display, proc->xwin, &attr);
+
+ int maxw = WidthOfScreen(attr.screen);
+ int maxh = HeightOfScreen(attr.screen);
+ int w, h;
+ if (width > maxw) {
+ h = (maxw * height + width - 1) / width;
+ w = maxw;
+ } else {
+ w = width;
+ h = height;
+ }
+ if (h > maxh) {
+ w = (maxh * width + height - 1) / height;
+ h = maxh;
+ }
+ assert(w <= maxw);
+ assert(h <= maxh);
+
+ XResizeWindow(proc->display, proc->xwin, w, h);
+ XFlush(proc->display);
+ return (0);
+}
+
+int _zbar_processor_set_visible(zbar_processor_t *proc, int visible)
+{
+ if (visible)
+ XMapRaised(proc->display, proc->xwin);
+ else
+ XUnmapWindow(proc->display, proc->xwin);
+ XFlush(proc->display);
+ return (0);
+}