diff options
Diffstat (limited to 'zbar/processor')
-rw-r--r-- | zbar/processor/lock.c | 228 | ||||
-rw-r--r-- | zbar/processor/null.c | 57 | ||||
-rw-r--r-- | zbar/processor/posix.c | 325 | ||||
-rw-r--r-- | zbar/processor/posix.h | 132 | ||||
-rw-r--r-- | zbar/processor/win.c | 332 | ||||
-rw-r--r-- | zbar/processor/x.c | 266 |
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); +} |