diff --git a/go.mod b/go.mod index f9704ae..3fc0e58 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/go-ole/go-ole v1.2.2 // indirect github.com/lxn/win v0.0.0-20181015143721-a7f87360b10e github.com/otiai10/gosseract v2.2.0+incompatible - github.com/robotn/gohook v0.0.0-20181215173318-e36d1aac6c1a + github.com/robotn/gohook v0.0.0-20190221131031-8d5c93253274 github.com/shirou/gopsutil v2.18.12+incompatible github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4 // indirect github.com/vcaesar/imgo v0.0.0-20181209162409-13af122cf2fa diff --git a/go.sum b/go.sum index 7f7b724..b6f9e11 100644 --- a/go.sum +++ b/go.sum @@ -14,8 +14,8 @@ github.com/lxn/win v0.0.0-20181015143721-a7f87360b10e h1:dz4TzIsrPe4XtUyhLkOLdCS github.com/lxn/win v0.0.0-20181015143721-a7f87360b10e/go.mod h1:jACzEp9RV7NhfPJQkiCNTteU4nkZZVlvkNpYtVOZPfE= github.com/otiai10/gosseract v2.2.0+incompatible h1:r2Icyba/doznLy0MgsDMWJADETgJNjlX78x/BBbhCUY= github.com/otiai10/gosseract v2.2.0+incompatible/go.mod h1:XrzWItCzCpFRZ35n3YtVTgq5bLAhFIkascoRo8G32QE= -github.com/robotn/gohook v0.0.0-20181215173318-e36d1aac6c1a h1:ywJG+bNPAxgEjZA6lqfbzcBlAw2F91dbMGYtP9xmrHw= -github.com/robotn/gohook v0.0.0-20181215173318-e36d1aac6c1a/go.mod h1:YD5RyCnUEY2xqtkkgeQVZ31UAfAnVPwUxpTE5cwSXg4= +github.com/robotn/gohook v0.0.0-20190221131031-8d5c93253274 h1:kSg34ruV/HqMFgzSkmsUPaDwm8pCpVZXf9j8VY0gNY0= +github.com/robotn/gohook v0.0.0-20190221131031-8d5c93253274/go.mod h1:YD5RyCnUEY2xqtkkgeQVZ31UAfAnVPwUxpTE5cwSXg4= github.com/shirou/gopsutil v2.18.12+incompatible h1:1eaJvGomDnH74/5cF4CTmTbLHAriGFsTZppLXDX93OM= github.com/shirou/gopsutil v2.18.12+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4 h1:udFKJ0aHUL60LboW/A+DfgoHVedieIzIXE8uylPue0U= diff --git a/vendor/github.com/robotn/gohook/.gitignore b/vendor/github.com/robotn/gohook/.gitignore index daf913b..e22c771 100644 --- a/vendor/github.com/robotn/gohook/.gitignore +++ b/vendor/github.com/robotn/gohook/.gitignore @@ -22,3 +22,4 @@ _testmain.go *.exe *.test *.prof +*.idea \ No newline at end of file diff --git a/vendor/github.com/robotn/gohook/README.md b/vendor/github.com/robotn/gohook/README.md index bd46bc6..251729c 100644 --- a/vendor/github.com/robotn/gohook/README.md +++ b/vendor/github.com/robotn/gohook/README.md @@ -16,10 +16,13 @@ import ( ) func main() { - // hook.AsyncHook() - veve := hook.AddEvent("v") - if veve == 0 { - fmt.Println("v...") + EvChan := hook.Start() + defer hook.End() + + for ev := range EvChan { + fmt.Println(ev) } } -``` \ No newline at end of file +``` + +Based on [libuiohook](https://github.com/kwhat/libuiohook). \ No newline at end of file diff --git a/vendor/github.com/robotn/gohook/appveyor.yml b/vendor/github.com/robotn/gohook/appveyor.yml index abe8c2a..7d8bfb8 100644 --- a/vendor/github.com/robotn/gohook/appveyor.yml +++ b/vendor/github.com/robotn/gohook/appveyor.yml @@ -34,7 +34,7 @@ environment: PATH: C:\msys64\mingw32\bin\;C:\Program Files (x86)\NSIS\;%PATH% # - COMPILER: MINGW_W64 # ARCHITECTURE: x64 - GOVERSION: 1.11.4 + GOVERSION: 1.11.5 # GOPATH: c:\gopath # scripts that run after cloning repository diff --git a/vendor/github.com/robotn/gohook/chan/eb_chan.h b/vendor/github.com/robotn/gohook/chan/eb_chan.h new file mode 100644 index 0000000..e4855b4 --- /dev/null +++ b/vendor/github.com/robotn/gohook/chan/eb_chan.h @@ -0,0 +1,1439 @@ +// ####################################################### +// ## Generated by merge_src from the following files: +// ## eb_assert.c +// ## eb_assert.h +// ## eb_atomic.h +// ## eb_chan.c +// ## eb_chan.h +// ## eb_nsec.h +// ## eb_port.c +// ## eb_port.h +// ## eb_spinlock.h +// ## eb_sys.c +// ## eb_sys.h +// ## eb_time.c +// ## eb_time.h +// ####################################################### + +// ####################################################### +// ## eb_chan.h +// ####################################################### + +#ifndef EB_CHAN_H +#define EB_CHAN_H + +#include +#include +// ####################################################### +// ## eb_nsec.h +// ####################################################### + +#include + +typedef uint64_t eb_nsec; /* Units of nanoseconds */ +#define eb_nsec_zero UINT64_C(0) +#define eb_nsec_forever UINT64_MAX +#define eb_nsec_per_sec UINT64_C(1000000000) + +/* ## Types */ +typedef enum { + eb_chan_res_ok, /* Success */ + eb_chan_res_closed, /* Failed because the channel is closed */ + eb_chan_res_stalled, /* Failed because the send/recv couldn't proceed without blocking (applies to _try_send()/_try_recv()) */ +} eb_chan_res; + +typedef struct eb_chan *eb_chan; +typedef struct { + eb_chan chan; /* The applicable channel, where NULL channels block forever */ + bool send; /* True if sending, false if receiving */ + eb_chan_res res; /* _ok if the op completed due to a successful send/recv operation, _closed if the op completed because the channel is closed. */ + const void *val; /* The value to be sent/the value that was received */ +} eb_chan_op; + +/* ## Channel creation/lifecycle */ +eb_chan eb_chan_create(size_t buf_cap); +eb_chan eb_chan_retain(eb_chan c); +void eb_chan_release(eb_chan c); + +/* ## Channel closing */ +/* Returns _ok on success, or _closed if the channel was already closed. */ +eb_chan_res eb_chan_close(eb_chan c); + +/* ## Getters */ +size_t eb_chan_buf_cap(eb_chan c); +size_t eb_chan_buf_len(eb_chan c); + +/* ## Sending/receiving */ +/* Send/receive a value on a channel (where _send()/_recv() are blocking and _try_send()/_try_recv() are non-blocking) */ +eb_chan_res eb_chan_send(eb_chan c, const void *val); +eb_chan_res eb_chan_try_send(eb_chan c, const void *val); +eb_chan_res eb_chan_recv(eb_chan c, const void **val); +eb_chan_res eb_chan_try_recv(eb_chan c, const void **val); + +/* ## Multiplexing */ +/* _select_list() performs at most one of the operations in the supplied list, and returns the one that was performed. + It returns NULL if no operation was performed before the timeout. */ +eb_chan_op *eb_chan_select_list(eb_nsec timeout, eb_chan_op *const ops[], size_t nops); + +/* _select() is a convenience macro that wraps _select_list() to avoid having to manually create an array of ops on the stack. + For example: + eb_chan_op op1 = eb_chan_op_send(c1, NULL); + eb_chan_op op2 = eb_chan_op_recv(c2); + eb_chan_op *result = eb_chan_select(timeout, &op1, &op2); + ... +*/ +#define eb_chan_select(timeout, ...) ({ \ + eb_chan_op *const eb_chan_select_ops[] = {__VA_ARGS__}; \ + eb_chan_select_list(timeout, eb_chan_select_ops, (sizeof(eb_chan_select_ops) / sizeof(*eb_chan_select_ops))); \ +}) + +/* Return initialized send/recv ops for use with _select() */ +static inline eb_chan_op eb_chan_op_send(eb_chan c, const void *val) { + return (eb_chan_op){.chan = c, .send = true, .res = eb_chan_res_closed, .val = val}; +} + +static inline eb_chan_op eb_chan_op_recv(eb_chan c) { + return (eb_chan_op){.chan = c, .send = false, .res = eb_chan_res_closed, .val = NULL}; +} + +// ####################################################### +// ## eb_chan.c +// ####################################################### +#include +#include +#include +#include +#include +// ####################################################### +// ## eb_assert.h +// ####################################################### + +#include +#include + +#define eb_no_op + +#define eb_assert_or_recover(cond, action) ({ \ + if (!(cond)) { \ + eb_assert_print("Assertion failed", #cond, __FILE__, (uintmax_t)__LINE__, __PRETTY_FUNCTION__); \ + action; \ + } \ +}) + +#define eb_assert_or_bail(cond, msg) ({ \ + if (!(cond)) { \ + eb_assert_print(msg, #cond, __FILE__, (uintmax_t)__LINE__, __PRETTY_FUNCTION__); \ + abort(); \ + } \ +}) + +void eb_assert_print(const char *msg, const char *cond, const char *file, uintmax_t line, const char *func); +// ####################################################### +// ## eb_assert.c +// ####################################################### + +#include + +void eb_assert_print(const char *msg, const char *cond, const char *file, uintmax_t line, const char *func) { + fprintf(stderr, "=== %s ===\n" + " Assertion: %s\n" + " File: %s:%ju\n" + " Function: %s\n", msg, cond, file, line, func); +} +// ####################################################### +// ## eb_port.h +// ####################################################### + +#include +#include + +typedef struct eb_port *eb_port; + +eb_port eb_port_create(); +eb_port eb_port_retain(eb_port p); +void eb_port_release(eb_port p); + +void eb_port_signal(eb_port p); +bool eb_port_wait(eb_port p, eb_nsec timeout); +// ####################################################### +// ## eb_port.c +// ####################################################### + +#include +#include +#include +#include +// ####################################################### +// ## eb_sys.h +// ####################################################### + +#include + +#if __MACH__ + #define EB_SYS_DARWIN 1 +#elif __linux__ + #define EB_SYS_LINUX 1 +#else +// #error Unsupported system +#endif + +/* ## Variables */ +/* Returns the number of logical cores on the machine. _init must be called for this to be valid! */ +size_t eb_sys_ncores; + +/* ## Functions */ +void eb_sys_init(); +// ####################################################### +// ## eb_sys.c +// ####################################################### + +// ####################################################### +// ## eb_atomic.h +// ####################################################### + + +#define eb_atomic_add(ptr, delta) __sync_add_and_fetch(ptr, delta) /* Returns the new value */ +#define eb_atomic_compare_and_swap(ptr, old, new) __sync_bool_compare_and_swap(ptr, old, new) +#define eb_atomic_barrier() __sync_synchronize() + +#if EB_SYS_DARWIN + #include +#elif EB_SYS_LINUX + #include +#endif + +size_t ncores() { + #if EB_SYS_DARWIN + host_basic_info_data_t info; + mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT; + kern_return_t r = host_info(mach_host_self(), HOST_BASIC_INFO, (host_info_t)&info, &count); + eb_assert_or_recover(r == KERN_SUCCESS, return 0); + eb_assert_or_recover(count == HOST_BASIC_INFO_COUNT, return 0); + eb_assert_or_recover(info.logical_cpu > 0 && info.logical_cpu <= SIZE_MAX, return 0); + + return (size_t)info.logical_cpu; + #elif EB_SYS_LINUX + long ncores = sysconf(_SC_NPROCESSORS_ONLN); + eb_assert_or_recover(ncores > 0 && ncores <= SIZE_MAX, return 0); + + return (size_t)ncores; + #endif +} + +void eb_sys_init() { + if (!eb_sys_ncores) { + eb_atomic_compare_and_swap(&eb_sys_ncores, 0, ncores()); + } +} +#if EB_SYS_DARWIN + #include +#elif EB_SYS_LINUX + #include + #include +#endif +// ####################################################### +// ## eb_spinlock.h +// ####################################################### + +#include +#include + +/* ## Types */ +typedef int eb_spinlock; +#define EB_SPINLOCK_INIT 0 + +/* ## Functions */ +#define eb_spinlock_try(l) eb_atomic_compare_and_swap(l, 0, 1) + +#define eb_spinlock_lock(l) ({ \ + if (eb_sys_ncores > 1) { \ + while (!eb_spinlock_try(l)); \ + } else { \ + while (!eb_spinlock_try(l)) { \ + sched_yield(); \ + } \ + } \ +}) + +#define eb_spinlock_unlock(l) eb_atomic_compare_and_swap(l, 1, 0) + +//#define eb_spinlock_try(l) __sync_lock_test_and_set(l, 1) == 0 +//#define eb_spinlock_lock(l) while (!eb_spinlock_try(l)) +//#define eb_spinlock_unlock(l) __sync_lock_release(l) +// +//typedef OSSpinLock eb_spinlock; +//#define eb_spinlock_try(l) OSSpinLockTry(l) +//#define eb_spinlock_lock(l) OSSpinLockLock(l) +//#define eb_spinlock_unlock(l) OSSpinLockUnlock(l) +// ####################################################### +// ## eb_time.h +// ####################################################### + + +/* Returns the number of nanoseconds since an arbitrary point in time (usually the machine's boot time) */ +eb_nsec eb_time_now(); +// ####################################################### +// ## eb_time.c +// ####################################################### + +#include +#include +#if EB_SYS_DARWIN + #include +#elif EB_SYS_LINUX + #include +#endif + +eb_nsec eb_time_now() { +#if EB_SYS_DARWIN + /* Initialize k_timebase_info, thread-safely */ + static mach_timebase_info_t k_timebase_info = NULL; + if (!k_timebase_info) { + mach_timebase_info_t timebase_info = malloc(sizeof(*timebase_info)); + kern_return_t r = mach_timebase_info(timebase_info); + eb_assert_or_recover(r == KERN_SUCCESS, return 0); + + /* Make sure the writes to 'timebase_info' are complete before we assign k_timebase_info */ + eb_atomic_barrier(); + + if (!eb_atomic_compare_and_swap(&k_timebase_info, NULL, timebase_info)) { + free(timebase_info); + timebase_info = NULL; + } + } + + return ((mach_absolute_time() * k_timebase_info->numer) / k_timebase_info->denom); +#elif EB_SYS_LINUX + struct timespec ts; + int r = clock_gettime(CLOCK_MONOTONIC, &ts); + eb_assert_or_recover(!r, return 0); + + return ((uint64_t)ts.tv_sec * eb_nsec_per_sec) + ts.tv_nsec; +#endif +} + +#define PORT_POOL_CAP 0x10 +static eb_spinlock g_port_pool_lock = EB_SPINLOCK_INIT; +static eb_port g_port_pool[PORT_POOL_CAP]; +static size_t g_port_pool_len = 0; + +struct eb_port { + unsigned int retain_count; + bool sem_valid; + bool signaled; + #if EB_SYS_DARWIN + semaphore_t sem; + #elif EB_SYS_LINUX + sem_t sem; + #endif +}; + +static void eb_port_free(eb_port p) { + /* Allowing p==NULL so that this function can be called unconditionally on failure from eb_port_create() */ + if (!p) { + return; + } + + bool added_to_pool = false; + if (p->sem_valid) { + /* Determine whether we should clear the reset the port because we're going to try adding the port to our pool. */ + bool reset = false; + eb_spinlock_lock(&g_port_pool_lock); + reset = (g_port_pool_len < PORT_POOL_CAP); + eb_spinlock_unlock(&g_port_pool_lock); + + if (reset) { + eb_port_wait(p, eb_nsec_zero); + } + + /* Now that the port's reset, add it to the pool as long as it'll still fit. */ + eb_spinlock_lock(&g_port_pool_lock); + if (g_port_pool_len < PORT_POOL_CAP) { + g_port_pool[g_port_pool_len] = p; + g_port_pool_len++; + added_to_pool = true; + } + eb_spinlock_unlock(&g_port_pool_lock); + + /* If we couldn't add the port to the pool, destroy the underlying semaphore. */ + if (!added_to_pool) { + #if EB_SYS_DARWIN + kern_return_t r = semaphore_destroy(mach_task_self(), p->sem); + eb_assert_or_recover(r == KERN_SUCCESS, eb_no_op); + #elif EB_SYS_LINUX + int r = sem_destroy(&p->sem); + eb_assert_or_recover(!r, eb_no_op); + #endif + + p->sem_valid = false; + } + } + + if (!added_to_pool) { + free(p); + p = NULL; + } +} + +eb_port eb_port_create() { + eb_port p = NULL; + + /* First try to pop a port out of the pool */ + eb_spinlock_lock(&g_port_pool_lock); + if (g_port_pool_len) { + g_port_pool_len--; + p = g_port_pool[g_port_pool_len]; + } + eb_spinlock_unlock(&g_port_pool_lock); + + if (p) { + /* We successfully popped a port out of the pool */ + eb_assert_or_bail(!p->retain_count, "Sanity-check failed"); + } else { + /* We couldn't get a port out of the pool */ + /* Using calloc so that bytes are zeroed */ + p = calloc(1, sizeof(*p)); + eb_assert_or_recover(p, goto failed); + + /* Create the semaphore */ + #if EB_SYS_DARWIN + kern_return_t r = semaphore_create(mach_task_self(), &p->sem, SYNC_POLICY_FIFO, 0); + eb_assert_or_recover(r == KERN_SUCCESS, goto failed); + #elif EB_SYS_LINUX + int r = sem_init(&p->sem, 0, 0); + eb_assert_or_recover(!r, goto failed); + #endif + } + + p->sem_valid = true; + p->retain_count = 1; + return p; + failed: { + eb_port_free(p); + return NULL; + } +} + +eb_port eb_port_retain(eb_port p) { + assert(p); + eb_atomic_add(&p->retain_count, 1); + return p; +} + +void eb_port_release(eb_port p) { + assert(p); + if (eb_atomic_add(&p->retain_count, -1) == 0) { + eb_port_free(p); + } +} + +void eb_port_signal(eb_port p) { + assert(p); + + if (eb_atomic_compare_and_swap(&p->signaled, false, true)) { + #if EB_SYS_DARWIN + kern_return_t r = semaphore_signal(p->sem); + eb_assert_or_recover(r == KERN_SUCCESS, eb_no_op); + #elif EB_SYS_LINUX + int r = sem_post(&p->sem); + eb_assert_or_recover(!r, eb_no_op); + #endif + } +} + +bool eb_port_wait(eb_port p, eb_nsec timeout) { + assert(p); + + bool result = false; + if (timeout == eb_nsec_zero) { + /* ## Non-blocking */ + #if EB_SYS_DARWIN + kern_return_t r = semaphore_timedwait(p->sem, (mach_timespec_t){0, 0}); + eb_assert_or_recover(r == KERN_SUCCESS || r == KERN_OPERATION_TIMED_OUT, eb_no_op); + + result = (r == KERN_SUCCESS); + #elif EB_SYS_LINUX + int r = 0; + while ((r = sem_trywait(&p->sem)) == -1 && errno == EINTR); + eb_assert_or_recover(!r || (r == -1 && errno == EAGAIN), eb_no_op); + + result = !r; + #endif + } else if (timeout == eb_nsec_forever) { + /* ## Blocking */ + #if EB_SYS_DARWIN + kern_return_t r; + while ((r = semaphore_wait(p->sem)) == KERN_ABORTED); + eb_assert_or_recover(r == KERN_SUCCESS, eb_no_op); + + result = (r == KERN_SUCCESS); + #elif EB_SYS_LINUX + int r; + while ((r = sem_wait(&p->sem)) == -1 && errno == EINTR); + eb_assert_or_recover(!r, eb_no_op); + + result = !r; + #endif + } else { + /* ## Actual timeout */ + eb_nsec start_time = eb_time_now(); + eb_nsec remaining_timeout = timeout; + for (;;) { + #if EB_SYS_DARWIN + /* This needs to be in a loop because semaphore_timedwait() can return KERN_ABORTED, e.g. if the process receives a signal. */ + mach_timespec_t ts = {.tv_sec = (unsigned int)(remaining_timeout / eb_nsec_per_sec), .tv_nsec = (clock_res_t)(remaining_timeout % eb_nsec_per_sec)}; + kern_return_t r = semaphore_timedwait(p->sem, ts); + eb_assert_or_recover(r == KERN_SUCCESS || r == KERN_OPERATION_TIMED_OUT || r == KERN_ABORTED, eb_no_op); + + if (r == KERN_SUCCESS) { + result = true; + break; + } + #elif EB_SYS_LINUX + /* Because sem_timedwait() uses the system's _REALTIME clock instead of the _MONOTONIC clock, we'll time out when + the system's time changes. For that reason, we check for the timeout case ourself (instead of relying on errno + after calling sem_timedwait()) condition ourself, using our own monotonic clock APIs (eb_time_now()), and + restart sem_timedwait() if we determine independently that we haven't timed-out. */ + struct timespec ts; + int r = clock_gettime(CLOCK_REALTIME, &ts); + eb_assert_or_recover(!r, break); + + ts.tv_sec += (remaining_timeout / eb_nsec_per_sec); + ts.tv_nsec += (remaining_timeout % eb_nsec_per_sec); + r = sem_timedwait(&p->sem, &ts); + /* The allowed return cases are: success (r==0), timed-out (r==-1, errno==ETIMEDOUT), (r==-1, errno==EINTR) */ + eb_assert_or_recover(!r || (r == -1 && (errno == ETIMEDOUT || errno == EINTR)), break); + + /* If we acquired the semaphore, set our flag and break! */ + if (!r) { + result = true; + break; + } + #endif + + /* Determine whether we timed-out, and if not, update 'remaining_timeout' with the amount of time to go. */ + eb_nsec elapsed = eb_time_now() - start_time; + if (elapsed < timeout) { + remaining_timeout = timeout - elapsed; + } else { + break; + } + } + } + + if (result) { + assert(eb_atomic_compare_and_swap(&p->signaled, true, false)); + } + + return result; +} + +#pragma mark - Types - +typedef struct { + eb_spinlock lock; + size_t cap; + size_t len; + eb_port *ports; +} *port_list; + +static inline void port_list_free(port_list l); + +/* Creates a new empty list */ +static inline port_list port_list_alloc(size_t cap) { + assert(cap > 0); + + port_list result = malloc(sizeof(*result)); + eb_assert_or_recover(result, goto failed); + + result->lock = EB_SPINLOCK_INIT; + result->cap = cap; + result->len = 0; + result->ports = malloc(cap * sizeof(*(result->ports))); + eb_assert_or_recover(result->ports, goto failed); + + return result; + failed: { + port_list_free(result); + return NULL; + } +} + +/* Releases every port in the list, and frees the list itself */ +static inline void port_list_free(port_list l) { + /* Intentionally allowing l==NULL */ + if (!l) { + return; + } + + /* Release each port in our list */ + size_t i; + for (i = 0; i < l->len; i++) { + eb_port_release(l->ports[i]); + } + + free(l->ports); + l->ports = NULL; + + free(l); + l = NULL; +} + +/* Add a port to the end of the list, expanding the buffer as necessary */ +static inline void port_list_add(port_list l, eb_port p) { + assert(l); + assert(p); + + /* First retain the port! */ + eb_port_retain(p); + + eb_spinlock_lock(&l->lock); + /* Sanity-check that the list's length is less than its capacity */ + eb_assert_or_bail(l->len <= l->cap, "Sanity check failed"); + + /* Expand the list's buffer if it's full */ + if (l->len == l->cap) { + l->cap *= 2; + // TODO: reimplement as a linked list, where the port nodes are just on the stacks of the _select_list() calls. that way the number of ports is unbounded, and we don't have to allocate anything on the heap! + l->ports = realloc(l->ports, l->cap * sizeof(*(l->ports))); + eb_assert_or_bail(l->ports, "Allocation failed"); + } + + l->ports[l->len] = p; + l->len++; + eb_spinlock_unlock(&l->lock); +} + +/* Remove the first occurence of 'p' in the list. Returns whether a port was actually removed. */ +static inline bool port_list_rm(port_list l, eb_port p) { + assert(l); + assert(p); + + bool result = false; + eb_spinlock_lock(&l->lock); + /* Sanity-check that the list's length is less than its capacity */ + eb_assert_or_bail(l->len <= l->cap, "Sanity-check failed"); + + /* Search for first occurence of the given port. If we find it, release it and move the last port in the list into the hole. */ + size_t i; + for (i = 0; i < l->len; i++) { + if (l->ports[i] == p) { + /* Move the last element in the port list into the now-vacant spot */ + l->ports[i] = l->ports[l->len-1]; + /* Decrement the buffer length */ + l->len--; + result = true; + break; + } + } + eb_spinlock_unlock(&l->lock); + + if (result) { + /* Release the port, but do so outside of the spinlock because releasing does some stuff that might not be quick. */ + eb_port_release(p); + } + + return result; +} + +/* Signal the first port in the list that isn't 'ignore' */ +static inline void port_list_signal_first(const port_list l, eb_port ignore) { + assert(l); + + eb_port p = NULL; + eb_spinlock_lock(&l->lock); + size_t i; + for (i = 0; i < l->len; i++) { + if (l->ports[i] != ignore) { + p = eb_port_retain(l->ports[i]); + break; + } + } + eb_spinlock_unlock(&l->lock); + + if (p) { + eb_port_signal(p); + eb_port_release(p); + p = NULL; + } +} + +enum { + /* Buffered/unbuffered channel states */ + chanstate_open, + chanstate_closed, + /* Unbuffered channel states */ + chanstate_send, + chanstate_recv, + chanstate_ack, + chanstate_done, + chanstate_cancelled +}; typedef int32_t chanstate; + +typedef struct { + eb_chan_op *const *ops; + size_t nops; + bool *cleanup_ops; + + eb_nsec timeout; + eb_port port; +} do_state; + +struct eb_chan { + unsigned int retain_count; + eb_spinlock lock; + chanstate state; + + port_list sends; + port_list recvs; + + /* Buffered ivars */ + size_t buf_cap; + size_t buf_len; + size_t buf_idx; + const void **buf; + + /* Unbuffered ivars */ + const do_state *unbuf_state; + eb_chan_op *unbuf_op; + eb_port unbuf_port; +}; + +#pragma mark - Channel creation/lifecycle - +static inline void eb_chan_free(eb_chan c) { + /* Intentionally allowing c==NULL so that this function can be called from eb_chan_create() */ + if (!c) { + return; + } + + if (c->buf_cap) { + /* ## Buffered */ + free(c->buf); + c->buf = NULL; + } + + port_list_free(c->recvs); + c->recvs = NULL; + + port_list_free(c->sends); + c->sends = NULL; + + free(c); + c = NULL; +} + +eb_chan eb_chan_create(size_t buf_cap) { + static const size_t k_init_buf_cap = 16; + + /* Initialize eb_sys so that eb_sys_ncores is valid. */ + eb_sys_init(); + + /* Using calloc so that the bytes are zeroed. */ + eb_chan c = calloc(1, sizeof(*c)); + eb_assert_or_recover(c, goto failed); + + c->retain_count = 1; + c->lock = EB_SPINLOCK_INIT; + c->state = chanstate_open; + + c->sends = port_list_alloc(k_init_buf_cap); + eb_assert_or_recover(c->sends, goto failed); + c->recvs = port_list_alloc(k_init_buf_cap); + eb_assert_or_recover(c->recvs, goto failed); + + if (buf_cap) { + /* ## Buffered */ + c->buf_cap = buf_cap; + c->buf_len = 0; + c->buf_idx = 0; + c->buf = malloc(c->buf_cap * sizeof(*(c->buf))); + eb_assert_or_recover(c->buf, goto failed); + } else { + /* ## Unbuffered */ + c->unbuf_state = NULL; + c->unbuf_op = NULL; + c->unbuf_port = NULL; + } + + /* Issue a memory barrier since we didn't have the lock acquired for our set up (and this channel could theoretically + be passed to another thread without a barrier, and that'd be bad news...) */ + eb_atomic_barrier(); + + return c; + failed: { + eb_chan_free(c); + return NULL; + } +} + +eb_chan eb_chan_retain(eb_chan c) { + assert(c); + eb_atomic_add(&c->retain_count, 1); + return c; +} + +void eb_chan_release(eb_chan c) { + assert(c); + if (eb_atomic_add(&c->retain_count, -1) == 0) { + eb_chan_free(c); + } +} + +#pragma mark - Channel closing - +eb_chan_res eb_chan_close(eb_chan c) { + assert(c); + + eb_chan_res result = eb_chan_res_stalled; + while (result == eb_chan_res_stalled) { + eb_port signal_port = NULL; + eb_spinlock_lock(&c->lock); + if (c->state == chanstate_open) { + c->state = chanstate_closed; + result = eb_chan_res_ok; + } else if (c->state == chanstate_closed) { + result = eb_chan_res_closed; + } else if (c->state == chanstate_send || c->state == chanstate_recv) { + if (c->unbuf_port) { + signal_port = eb_port_retain(c->unbuf_port); + } + c->state = chanstate_closed; + result = eb_chan_res_ok; + } + eb_spinlock_unlock(&c->lock); + + /* Wake up the send/recv */ + if (signal_port) { + eb_port_signal(signal_port); + eb_port_release(signal_port); + signal_port = NULL; + } + } + + if (result == eb_chan_res_ok) { + /* Wake up the sends/recvs so that they see the channel's now closed */ + port_list_signal_first(c->sends, NULL); + port_list_signal_first(c->recvs, NULL); + } + + return result; +} + +#pragma mark - Getters - +size_t eb_chan_buf_cap(eb_chan c) { + assert(c); + return c->buf_cap; +} + +size_t eb_chan_buf_len(eb_chan c) { + assert(c); + + /* buf_len is only valid if the channel's buffered */ + if (!c->buf_cap) { + return 0; + } + + size_t r = 0; + eb_spinlock_lock(&c->lock); + r = c->buf_len; + eb_spinlock_unlock(&c->lock); + return r; +} + +#pragma mark - Performing operations - +enum { + op_result_complete, /* The op completed and the caller should return */ + op_result_next, /* The op couldn't make any progress and the caller should move on to the next op */ + op_result_retry, /* The channel's busy and we should try the op again */ +}; typedef unsigned int op_result; + +static inline void cleanup_ops(const do_state *state) { + assert(state); + + size_t i; + for (i = 0; i < state->nops; i++) { + if (state->cleanup_ops[i]) { + eb_chan_op *op = state->ops[i]; + eb_chan c = op->chan; + bool signal_send = false; + bool signal_recv = false; + eb_spinlock_lock(&c->lock); + if (c->state == chanstate_send && c->unbuf_op == op) { + /* 'op' was in the process of an unbuffered send on the channel, but no recv had arrived + yet, so reset state to _open. */ + c->state = chanstate_open; + signal_send = true; + } else if (c->state == chanstate_recv && c->unbuf_op == op) { + /* 'op' was in the process of an unbuffered recv on the channel, but no send had arrived + yet, so reset state to _open. */ + c->state = chanstate_open; + signal_recv = true; + } else if (c->state == chanstate_ack && c->unbuf_op == op) { + /* A counterpart acknowledged 'op' but, but 'op' isn't the one that completed in our select() call, so we're cancelling. */ + c->state = chanstate_cancelled; + } + eb_spinlock_unlock(&c->lock); + + if (signal_send) { + port_list_signal_first(c->sends, state->port); + } + + if (signal_recv) { + port_list_signal_first(c->recvs, state->port); + } + + state->cleanup_ops[i] = false; + } + } +} + +static inline op_result send_buf(const do_state *state, eb_chan_op *op, size_t op_idx) { + assert(state); + assert(op); + assert(op->chan); + + eb_chan c = op->chan; + op_result result = op_result_next; + + if (c->buf_len < c->buf_cap || c->state == chanstate_closed) { + /* It looks like our channel's in an acceptable state, so try to acquire the lock */ + if (eb_spinlock_try(&c->lock)) { + /* Sanity-check the channel's state */ + eb_assert_or_bail(c->state == chanstate_open || c->state == chanstate_closed, "Invalid channel state"); + + bool signal_recv = false; + if (c->state == chanstate_closed) { + /* ## Sending, buffered, channel closed */ + /* Set our op's state and our return value */ + op->res = eb_chan_res_closed; + result = op_result_complete; + } else if (c->buf_len < c->buf_cap) { + /* ## Sending, buffered, channel open, buffer has space */ + /* Notify the channel's recvs if our buffer is going from empty to non-empty */ + signal_recv = (!c->buf_len); + /* Add the value to the buffer */ + size_t idx = (c->buf_idx + c->buf_len) % c->buf_cap; + c->buf[idx] = op->val; + c->buf_len++; + /* Set our op's state and our return value */ + op->res = eb_chan_res_ok; + result = op_result_complete; + } + + eb_spinlock_unlock(&c->lock); + + if (signal_recv) { + port_list_signal_first(c->recvs, state->port); + } + } else { + result = op_result_retry; + } + } + + return result; +} + +static inline op_result recv_buf(const do_state *state, eb_chan_op *op, size_t op_idx) { + assert(state); + assert(op); + assert(op->chan); + + eb_chan c = op->chan; + op_result result = op_result_next; + + if (c->buf_len || c->state == chanstate_closed) { + if (eb_spinlock_try(&c->lock)) { + /* Sanity-check the channel's state */ + eb_assert_or_bail(c->state == chanstate_open || c->state == chanstate_closed, "Invalid channel state"); + + bool signal_send = false; + if (c->buf_len) { + /* ## Receiving, buffered, buffer non-empty */ + /* Notify the channel's sends if our buffer is going from full to not-full */ + signal_send = (c->buf_len == c->buf_cap); + /* Set our op's state and our return value */ + op->res = eb_chan_res_ok; + op->val = c->buf[c->buf_idx]; + result = op_result_complete; + /* Update chan's buffer. (Updating buf_idx needs to come after we use it!) */ + c->buf_len--; + c->buf_idx = (c->buf_idx + 1) % c->buf_cap; + } else if (c->state == chanstate_closed) { + /* ## Receiving, buffered, buffer empty, channel closed */ + /* Set our op's state and our return value */ + op->res = eb_chan_res_closed; + op->val = NULL; + result = op_result_complete; + } + + eb_spinlock_unlock(&c->lock); + + if (signal_send) { + port_list_signal_first(c->sends, state->port); + } + } else { + result = op_result_retry; + } + } + + return result; +} + +static inline op_result send_unbuf(const do_state *state, eb_chan_op *op, size_t op_idx) { + assert(state); + assert(op); + assert(op->chan); + + eb_chan c = op->chan; + op_result result = op_result_next; + + if ((c->state == chanstate_open && state->timeout != eb_nsec_zero) || + c->state == chanstate_closed || + (c->state == chanstate_send && c->unbuf_op == op) || + (c->state == chanstate_recv && c->unbuf_state != state) || + (c->state == chanstate_ack && c->unbuf_op == op)) { + + /* It looks like our channel's in an acceptable state, so try to acquire the lock */ + if (eb_spinlock_try(&c->lock)) { + /* Reset the cleanup state since we acquired the lock and are actually getting a look at the channel's state */ + state->cleanup_ops[op_idx] = false; + + bool signal_recv = false; + if (c->state == chanstate_open && state->timeout != eb_nsec_zero) { + c->state = chanstate_send; + c->unbuf_state = state; + c->unbuf_op = op; + c->unbuf_port = state->port; + /* We need to cleanup after this since we put it in the _send state! */ + state->cleanup_ops[op_idx] = true; + /* Signal a recv since one of them can continue now */ + signal_recv = true; + } else if (c->state == chanstate_closed) { + /* Set our op's state and our return value */ + op->res = eb_chan_res_closed; + result = op_result_complete; + } else if (c->state == chanstate_send && c->unbuf_op == op) { + /* We own the send op that's in progress, so assign chan's unbuf_port */ + /* Verify that the unbuf_state matches our 'id' parameter. If this assertion fails, it means there's likely + one eb_chan_op being shared by multiple threads, which isn't allowed. */ + eb_assert_or_bail(c->unbuf_state == state, "unbuf_state invalid"); + /* Assign the port */ + c->unbuf_port = state->port; + /* We need to cleanup after this since we put it in the _send state! */ + state->cleanup_ops[op_idx] = true; + } else if (c->state == chanstate_recv && c->unbuf_state != state) { + /* We verified (immediately above) that the recv isn't part of the same op pool (we can't do unbuffered + sends/recvs from the same _do() call) */ + + /* Sanity check -- make sure the op is a recv */ + eb_assert_or_bail(!c->unbuf_op->send, "Op isn't a recv as expected"); + + /* Set the recv op's value. This needs to happen before we transition out of the _recv state, otherwise the unbuf_op may no longer be valid! */ + c->unbuf_op->val = op->val; + /* Acknowledge the receive */ + c->state = chanstate_ack; + /* Get a reference to the unbuf_port that needs to be signaled */ + eb_port signal_port = (c->unbuf_port ? eb_port_retain(c->unbuf_port) : NULL); + eb_spinlock_unlock(&c->lock); + + /* Wake up the recv */ + if (signal_port) { + eb_port_signal(signal_port); + eb_port_release(signal_port); + signal_port = NULL; + } + + /* We have to cleanup all our ops here to cancel any outstanding unbuffered send/recvs, to avoid a deadlock + situation that arises when another _do() is waiting on our _do() to complete, but it never does because + we're about to wait for the other _do() to complete. */ + cleanup_ops(state); + + for (;;) { + if (*((volatile chanstate *)&c->state) != chanstate_ack) { + eb_spinlock_lock(&c->lock); + if (c->state == chanstate_done) { + /* Reset the channel state back to _open */ + c->state = chanstate_open; + /* We reset our state to _open, so signal a send since it can proceed now. */ + signal_recv = true; + /* Set our op's state and our return value */ + op->res = eb_chan_res_ok; + result = op_result_complete; + /* Breaking here so that we skip the _unlock() call, because we unlock the spinlock outside + of our large if-statement. */ + break; + } else if (c->state == chanstate_cancelled) { + /* Reset the channel state back to _open */ + c->state = chanstate_open; + /* As long as we're not polling, we should try the op again */ + if (state->timeout != eb_nsec_zero) { + result = op_result_retry; + } else { + /* We're not telling the caller to retry, so signal a send since it can proceed now. */ + signal_recv = true; + } + /* Breaking here so that we skip the _unlock() call, because we unlock the spinlock outside + of our large if-statement. */ + break; + } + eb_spinlock_unlock(&c->lock); + } else if (eb_sys_ncores == 1) { + /* On uniprocessor machines, yield to the scheduler because we can't continue until another + thread updates the channel's state. */ + sched_yield(); + } + } + } else if (c->state == chanstate_ack && c->unbuf_op == op) { + /* A recv acknowledged our send! */ + /* Verify that the unbuf_state matches our 'id' parameter. If this assertion fails, it means there's likely + one eb_chan_op being shared by multiple threads, which isn't allowed. */ + eb_assert_or_bail(c->unbuf_state == state, "unbuf_state invalid"); + /* A recv is polling for chan's state to change, so update it to signal that we're done sending! */ + c->state = chanstate_done; + /* Set our op's state and our return value */ + op->res = eb_chan_res_ok; + result = op_result_complete; + } + + eb_spinlock_unlock(&c->lock); + + if (signal_recv) { + port_list_signal_first(c->recvs, state->port); + } + } else { + result = op_result_retry; + } + } + + return result; +} + +static inline op_result recv_unbuf(const do_state *state, eb_chan_op *op, size_t op_idx) { + assert(state); + assert(op); + assert(op->chan); + + eb_chan c = op->chan; + op_result result = op_result_next; + + if ((c->state == chanstate_open && state->timeout != eb_nsec_zero) || + c->state == chanstate_closed || + (c->state == chanstate_send && c->unbuf_state != state) || + (c->state == chanstate_recv && c->unbuf_op == op) || + (c->state == chanstate_ack && c->unbuf_op == op)) { + + /* It looks like our channel's in an acceptable state, so try to acquire the lock */ + if (eb_spinlock_try(&c->lock)) { + /* Reset the cleanup state since we acquired the lock and are actually getting a look at the channel's state */ + state->cleanup_ops[op_idx] = false; + + bool signal_send = false; + if (c->state == chanstate_open && state->timeout != eb_nsec_zero) { + c->state = chanstate_recv; + c->unbuf_state = state; + c->unbuf_op = op; + c->unbuf_port = state->port; + /* We need to cleanup after this since we put it in the _send state! */ + state->cleanup_ops[op_idx] = true; + /* Signal a send since one of them can continue now */ + signal_send = true; + } else if (c->state == chanstate_closed) { + /* Set our op's state and our return value */ + op->res = eb_chan_res_closed; + op->val = NULL; + result = op_result_complete; + } else if (c->state == chanstate_send && c->unbuf_state != state) { + /* We verified (immediately above) that the send isn't part of the same op pool (we can't do unbuffered + sends/recvs from the same _do() call) */ + + /* Sanity check -- make sure the op is a send */ + eb_assert_or_bail(c->unbuf_op->send, "Op isn't a send as expected"); + + /* Get the op's value. This needs to happen before we transition out of the _send state, otherwise the unbuf_op may no longer be valid! */ + op->val = c->unbuf_op->val; + /* Acknowledge the send */ + c->state = chanstate_ack; + /* Get a reference to the unbuf_port that needs to be signaled */ + eb_port signal_port = (c->unbuf_port ? eb_port_retain(c->unbuf_port) : NULL); + eb_spinlock_unlock(&c->lock); + + /* Wake up the send */ + if (signal_port) { + eb_port_signal(signal_port); + eb_port_release(signal_port); + signal_port = NULL; + } + + /* We have to cleanup all our ops here to cancel any outstanding unbuffered send/recvs, to avoid a deadlock + situation that arises when another _do() is waiting on our _do() to complete, but it never does because + we're about to wait for the other _do() to complete. */ + cleanup_ops(state); + + for (;;) { + if (*((volatile chanstate *)&c->state) != chanstate_ack) { + eb_spinlock_lock(&c->lock); + if (c->state == chanstate_done) { + /* Reset the channel state back to _open */ + c->state = chanstate_open; + /* We reset our state to _open, so signal a recv since it can proceed now. */ + signal_send = true; + /* Set our op's state and our return value */ + op->res = eb_chan_res_ok; + result = op_result_complete; + /* Breaking here so that we skip the _unlock() call, because we unlock the spinlock outside + of our large if-statement. */ + break; + } else if (c->state == chanstate_cancelled) { + /* Reset the channel state back to _open */ + c->state = chanstate_open; + /* As long as we're not polling, we should try the op again */ + if (state->timeout != eb_nsec_zero) { + result = op_result_retry; + } else { + /* We're not telling the caller to retry, so signal a recv since it can proceed now. */ + signal_send = true; + } + /* Breaking here so that we skip the _unlock() call, because we unlock the spinlock outside + of our large if-statement. */ + break; + } + eb_spinlock_unlock(&c->lock); + } else if (eb_sys_ncores == 1) { + /* On uniprocessor machines, yield to the scheduler because we can't continue until another + thread updates the channel's state. */ + sched_yield(); + } + } + } else if (c->state == chanstate_recv && c->unbuf_op == op) { + /* We own the recv op that's in progress, so assign chan's unbuf_port */ + /* Verify that the _recv_id matches our 'id' parameter. If this assertion fails, it means there's likely + one eb_chan_op being shared by multiple threads, which isn't allowed. */ + eb_assert_or_bail(c->unbuf_state == state, "unbuf_state invalid"); + /* Assign the port */ + c->unbuf_port = state->port; + /* We need to cleanup after this since we put it in the _send state! */ + state->cleanup_ops[op_idx] = true; + } else if (c->state == chanstate_ack && c->unbuf_op == op) { + /* A send acknowledged our recv! */ + /* Verify that the unbuf_state matches our 'id' parameter. If this assertion fails, it means there's likely + one eb_chan_op being shared by multiple threads, which isn't allowed. */ + eb_assert_or_bail(c->unbuf_state == state, "unbuf_state invalid"); + /* A send is polling for chan's state to change, so update it to signal that we're done sending! */ + c->state = chanstate_done; + /* Set our op's state and our return value */ + op->res = eb_chan_res_ok; + result = op_result_complete; + } + + eb_spinlock_unlock(&c->lock); + + if (signal_send) { + port_list_signal_first(c->sends, state->port); + } + } else { + result = op_result_retry; + } + } + + return result; +} + +static inline op_result try_op(const do_state *state, eb_chan_op *op, size_t op_idx) { + assert(state); + assert(op); + + eb_chan c = op->chan; + if (c) { + if (op->send) { + /* ## Send */ + return (c->buf_cap ? send_buf(state, op, op_idx) : send_unbuf(state, op, op_idx)); + } else { + /* ## Receive */ + return (c->buf_cap ? recv_buf(state, op, op_idx) : recv_unbuf(state, op, op_idx)); + } + } + return op_result_next; +} + +eb_chan_res eb_chan_send(eb_chan c, const void *val) { + eb_chan_op op = eb_chan_op_send(c, val); + eb_assert_or_bail(eb_chan_select(eb_nsec_forever, &op) == &op, "Invalid select() return value"); + return op.res; +} + +eb_chan_res eb_chan_try_send(eb_chan c, const void *val) { + eb_chan_op op = eb_chan_op_send(c, val); + eb_chan_op *r = eb_chan_select(eb_nsec_zero, &op); + eb_assert_or_bail(r == NULL || r == &op, "Invalid select() return value"); + return (r ? op.res : eb_chan_res_stalled); +} + +eb_chan_res eb_chan_recv(eb_chan c, const void **val) { + eb_chan_op op = eb_chan_op_recv(c); + eb_assert_or_bail(eb_chan_select(eb_nsec_forever, &op) == &op, "Invalid select() return value"); + if (op.res == eb_chan_res_ok && val) { + *val = op.val; + } + return op.res; +} + +eb_chan_res eb_chan_try_recv(eb_chan c, const void **val) { + eb_chan_op op = eb_chan_op_recv(c); + eb_chan_op *r = eb_chan_select(eb_nsec_zero, &op); + eb_assert_or_bail(r == NULL || r == &op, "Invalid select() return value"); + if (r && op.res == eb_chan_res_ok && val) { + *val = op.val; + } + return (r ? op.res : eb_chan_res_stalled); +} + +#pragma mark - Multiplexing - +#define next_idx(nops, delta, idx) (delta == 1 && idx == nops-1 ? 0 : ((delta == -1 && idx == 0) ? nops-1 : idx+delta)) +eb_chan_op *eb_chan_select_list(eb_nsec timeout, eb_chan_op *const ops[], size_t nops) { + assert(!nops || ops); + + const size_t k_attempt_multiplier = (eb_sys_ncores == 1 ? 1 : 500); + eb_nsec start_time = 0; + size_t idx_start = 0; + int8_t idx_delta = 0; + if (nops > 1) { + /* Assign idx_start/idx_delta, which control the op pseudo-randomization */ + start_time = eb_time_now(); + idx_start = (start_time/1000)%nops; + idx_delta = (!((start_time/10000)%2) ? 1 : -1); + } + + bool co[nops]; + memset(co, 0, sizeof(co)); + + eb_chan_op *result = NULL; + do_state state = { + .ops = ops, + .nops = nops, + .cleanup_ops = co, + .timeout = timeout, + .port = NULL}; + + if (timeout == eb_nsec_zero) { + /* ## timeout == 0: try every op exactly once; if none of them can proceed, return NULL. */ + size_t i, idx; + for (i = 0, idx = idx_start; i < nops; i++, idx = next_idx(nops, idx_delta, idx)) { + eb_chan_op *op = ops[idx]; + op_result r; + while ((r = try_op(&state, op, idx)) == op_result_retry) { + if (eb_sys_ncores == 1) { + /* On uniprocessor machines, yield to the scheduler because we can't continue until another + thread updates the channel's state. */ + sched_yield(); + } + } + + /* If the op completed, we need to exit! */ + if (r == op_result_complete) { + result = op; + goto cleanup; + } + } + } else { + /* ## timeout != 0 */ + if (timeout != eb_nsec_forever && !start_time) { + start_time = eb_time_now(); + } + + for (;;) { + /* ## Fast path: loop over our operations to see if one of them was able to send/receive. (If not, + we'll enter the slow path where we put our thread to sleep until we're signaled.) */ + size_t i, idx; + for (i = 0, idx = idx_start; i < k_attempt_multiplier*nops; i++, idx = next_idx(nops, idx_delta, idx)) { + eb_chan_op *op = ops[idx]; + op_result r = try_op(&state, op, idx); + /* If the op completed, we need to exit! */ + if (r == op_result_complete) { + result = op; + goto cleanup; + } + } + + /* ## Slow path: we weren't able to find an operation that could send/receive, so we'll create a + port to receive notifications on and put this thread to sleep until someone wakes us up. */ + if (!state.port) { + /* Create our port that we'll attach to channels so that we can be notified when events occur. */ + state.port = eb_port_create(); + eb_assert_or_recover(state.port, goto cleanup); + + /* Register our port for the appropriate notifications on every channel. */ + /* This adds 'port' to the channel's sends/recvs (depending on the op), which we clean up at the + end of this function. */ + size_t i; + for (i = 0; i < nops; i++) { + eb_chan_op *op = ops[i]; + eb_chan c = op->chan; + if (c) { + port_list_add((op->send ? c->sends : c->recvs), state.port); + } + } + } + + /* Before we go to sleep, call try_op() for every op until we get a non-busy return value. This way we'll ensure + that no op is actually able to be performed, and we'll also ensure that 'port' is registered as the 'unbuf_port' + for the necessary channels. */ + // size_t i, idx; + for (i = 0, idx = idx_start; i < nops; i++, idx = next_idx(nops, idx_delta, idx)) { + eb_chan_op *op = ops[idx]; + op_result r; + while ((r = try_op(&state, op, idx)) == op_result_retry) { + if (eb_sys_ncores == 1) { + /* On uniprocessor machines, yield to the scheduler because we can't continue until another + thread updates the channel's state. */ + sched_yield(); + } + } + + /* If the op completed, we need to exit! */ + if (r == op_result_complete) { + result = op; + goto cleanup; + } + } + + eb_nsec wait_timeout = eb_nsec_forever; + if (timeout != eb_nsec_forever) { + /* If we have a timeout, determine how much time has elapsed, because we may have timed-out. */ + eb_nsec elapsed = eb_time_now() - start_time; + /* Check if we timed-out */ + if (elapsed < timeout) { + wait_timeout = timeout - elapsed; + } else { + goto cleanup; + } + } + + /* Put our thread to sleep until someone alerts us of an event */ + eb_port_wait(state.port, wait_timeout); + } + } + + /* Cleanup! */ + cleanup: { + if (state.port) { + size_t i; + for (i = 0; i < nops; i++) { + eb_chan_op *op = ops[i]; + eb_chan c = op->chan; + if (c) { + port_list ports = (op->send ? c->sends : c->recvs); + port_list_rm(ports, state.port); + port_list_signal_first(ports, state.port); + } + } + } + + cleanup_ops(&state); + + if (state.port) { + eb_port_release(state.port); + state.port = NULL; + } + } + + return result; +} +#endif /* EB_CHAN_H */ \ No newline at end of file diff --git a/vendor/github.com/robotn/gohook/event/dispatch_proc.h b/vendor/github.com/robotn/gohook/event/dispatch_proc.h new file mode 100644 index 0000000..fddf9c0 --- /dev/null +++ b/vendor/github.com/robotn/gohook/event/dispatch_proc.h @@ -0,0 +1,217 @@ +// Copyright 2016 The go-vgo Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// https://github.com/go-vgo/robotgo/blob/master/LICENSE +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#ifndef dispatch_proc_h +#define dispatch_proc_h + +// #include "pub.h" +// #include "../chan/eb_chan.h" + +void dispatch_proc(iohook_event * const event) { + if (!sending) { return; } + + // leaking memory? hope not + char* buffer = calloc(200, sizeof(char)); + + switch (event->type) { + case EVENT_HOOK_ENABLED: + case EVENT_HOOK_DISABLED: + sprintf(buffer, + "{\"id\":%i,\"time\":%" PRIu64 ",\"mask\":%hu,\"reserved\":%hu}", + event->type, event->time, event->mask,event->reserved); + break; // send it? + case EVENT_KEY_PRESSED: + case EVENT_KEY_RELEASED: + case EVENT_KEY_TYPED: + sprintf(buffer, + "{\"id\":%i,\"time\":%" PRIu64 ",\"mask\":%hu,\"reserved\":%hu,\"keycode\":%hu,\"rawcode\":%hu,\"keychar\":%hu}", + event->type, event->time, event->mask,event->reserved, + event->data.keyboard.keycode, + event->data.keyboard.rawcode, + event->data.keyboard.keychar); + break; + case EVENT_MOUSE_PRESSED: + case EVENT_MOUSE_RELEASED: + case EVENT_MOUSE_CLICKED: + case EVENT_MOUSE_MOVED: + case EVENT_MOUSE_DRAGGED: + sprintf(buffer, + "{\"id\":%i,\"time\":%" PRIu64 ",\"mask\":%hu,\"reserved\":%hu,\"x\":%hd,\"y\":%hd,\"button\":%u,\"clicks\":%u}", + event->type, event->time, event->mask,event->reserved, + event->data.mouse.x, + event->data.mouse.y, + event->data.mouse.button, + event->data.mouse.clicks); + break; + case EVENT_MOUSE_WHEEL: + sprintf(buffer, + "{\"id\":%i,\"time\":%" PRIu64 ",\"mask\":%hu,\"reserved\":%hu,\"clicks\":%hu,\"x\":%hd,\"y\":%hd,\"type\":%hu,\"ammount\":%hu,\"rotation\":%hd,\"direction\":%hu}", + event->type, event->time, event->mask, event->reserved, + event->data.wheel.clicks, + event->data.wheel.x, + event->data.wheel.y, + event->data.wheel.type, + event->data.wheel.amount, + event->data.wheel.rotation, + event->data.wheel.direction); + break; + default: + fprintf(stderr,"\nError on file: %s, unusual event->type: %i\n",__FILE__,event->type); + return; + } + + // to-do remove this for + int i; + for (i = 0; i < 5; i++) { + switch (eb_chan_try_send(events, buffer)) { // never block the hook callback + case eb_chan_res_ok: + i=5; + break; + default: + if (i == 4) { // let's not leak memory + free(buffer); + } + continue; + } + } + + // fprintf(stdout, "----%s\n", buffer); +} + +void dispatch_proc_end(iohook_event * const event) { + char buffer[256] = { 0 }; + size_t length = snprintf(buffer, sizeof(buffer), + "id=%i,when=%" PRIu64 ",mask=0x%X", + event->type, event->time, event->mask); + + switch (event->type) { + case EVENT_KEY_PRESSED: + // If the escape key is pressed, naturally terminate the program. + if (event->data.keyboard.keycode == VC_ESCAPE) { + // int status = hook_stop(); + // switch (status) { + // // System level errors. + // case IOHOOK_ERROR_OUT_OF_MEMORY: + // loggerProc(LOG_LEVEL_ERROR, "Failed to allocate memory. (%#X)", status); + // break; + + // case IOHOOK_ERROR_X_RECORD_GET_CONTEXT: + // // NOTE This is the only platform specific error that occurs on hook_stop(). + // loggerProc(LOG_LEVEL_ERROR, "Failed to get XRecord context. (%#X)", status); + // break; + + // // Default error. + // case IOHOOK_FAILURE: + // default: + // loggerProc(LOG_LEVEL_ERROR, "An unknown hook error occurred. (%#X)", status); + // break; + // } + } + case EVENT_KEY_RELEASED: + snprintf(buffer + length, sizeof(buffer) - length, + ",keycode=%u,rawcode=0x%X", + event->data.keyboard.keycode, event->data.keyboard.rawcode); + int key_code = (uint16_t) event->data.keyboard.keycode; + + if (event->data.keyboard.keycode == VC_ESCAPE + && atoi(cevent) == 11) { + int stopEvent = stop_event(); + // printf("stop_event%d\n", stopEvent); + cstatus = 0; + } + + // printf("atoi(str)---%d\n", atoi(cevent)); + if (key_code == atoi(cevent)) { + int stopEvent = stop_event(); + // printf("%d\n", stopEvent); + cstatus = 0; + } + break; + + case EVENT_KEY_TYPED: + snprintf(buffer + length, sizeof(buffer) - length, + ",keychar=%lc,rawcode=%u", + (uint16_t) event->data.keyboard.keychar, + event->data.keyboard.rawcode); + + #ifdef WE_REALLY_WANT_A_POINTER + char *buf = malloc (6); + #else + char buf[6]; + #endif + + sprintf(buf, "%lc", (uint16_t) event->data.keyboard.keychar); + + #ifdef WE_REALLY_WANT_A_POINTER + free (buf); + #endif + + if (strcmp(buf, cevent) == 0) { + int stopEvent = stop_event(); + // printf("%d\n", stopEvent); + cstatus = 0; + } + // return (char*) event->data.keyboard.keychar; + break; + + case EVENT_MOUSE_PRESSED: + case EVENT_MOUSE_RELEASED: + case EVENT_MOUSE_CLICKED: + case EVENT_MOUSE_MOVED: + case EVENT_MOUSE_DRAGGED: + snprintf(buffer + length, sizeof(buffer) - length, + ",x=%i,y=%i,button=%i,clicks=%i", + event->data.mouse.x, event->data.mouse.y, + event->data.mouse.button, event->data.mouse.clicks); + + int abutton = event->data.mouse.button; + int aclicks = event->data.mouse.clicks; + int amouse = -1; + + if (strcmp(cevent, "mleft") == 0) { + amouse = 1; + } + if (strcmp(cevent, "mright") == 0) { + amouse = 2; + } + if (strcmp(cevent, "wheelDown") == 0) { + amouse = 4; + } + if (strcmp(cevent, "wheelUp") == 0) { + amouse = 5; + } + if (strcmp(cevent, "wheelLeft") == 0) { + amouse = 6; + } + if (strcmp(cevent, "wheelRight") == 0) { + amouse = 7; + } + if (abutton == amouse && aclicks == 1) { + int stopEvent = stop_event(); + cstatus = 0; + } + + break; + + case EVENT_MOUSE_WHEEL: + snprintf(buffer + length, sizeof(buffer) - length, + ",type=%i,amount=%i,rotation=%i", + event->data.wheel.type, event->data.wheel.amount, + event->data.wheel.rotation); + break; + + default: + break; + } + + // fprintf(stdout, "----%s\n", buffer); +} + +#endif \ No newline at end of file diff --git a/vendor/github.com/robotn/gohook/event/goEvent.h b/vendor/github.com/robotn/gohook/event/goEvent.h index acd41cc..e0db196 100644 --- a/vendor/github.com/robotn/gohook/event/goEvent.h +++ b/vendor/github.com/robotn/gohook/event/goEvent.h @@ -8,151 +8,69 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#ifndef goevent_h +#define goevent_h #ifdef HAVE_CONFIG_H #include #endif #include #include "pub.h" +// #include "../chan/eb_chan.h" +#include "dispatch_proc.h" +void go_send(char*); +void go_sleep(void); -void dispatch_proc(iohook_event * const event) { - char buffer[256] = { 0 }; - size_t length = snprintf(buffer, sizeof(buffer), - "id=%i,when=%" PRIu64 ",mask=0x%X", - event->type, event->time, event->mask); +void start_ev(){ + events = eb_chan_create(1024); + eb_chan_retain(events); + sending = true; + // add_event("q"); + add_event_async(); +} - switch (event->type) { - case EVENT_KEY_PRESSED: - // If the escape key is pressed, naturally terminate the program. - if (event->data.keyboard.keycode == VC_ESCAPE) { - // int status = hook_stop(); - // switch (status) { - // // System level errors. - // case IOHOOK_ERROR_OUT_OF_MEMORY: - // loggerProc(LOG_LEVEL_ERROR, "Failed to allocate memory. (%#X)", status); - // break; +void pollEv(){ + if (events == NULL) { return; } + + for (;eb_chan_buf_len(events)!=0;) { + char* tmp; + if (eb_chan_try_recv(events, (const void**) &tmp) + == eb_chan_res_ok) { + // send a char + go_send(tmp); + free(tmp); + } else { + // + } + } +} - // case IOHOOK_ERROR_X_RECORD_GET_CONTEXT: - // // NOTE This is the only platform specific error that occurs on hook_stop(). - // loggerProc(LOG_LEVEL_ERROR, "Failed to get XRecord context. (%#X)", status); - // break; - - // // Default error. - // case IOHOOK_FAILURE: - // default: - // loggerProc(LOG_LEVEL_ERROR, "An unknown hook error occurred. (%#X)", status); - // break; - // } - } - case EVENT_KEY_RELEASED: - snprintf(buffer + length, sizeof(buffer) - length, - ",keycode=%u,rawcode=0x%X", - event->data.keyboard.keycode, event->data.keyboard.rawcode); - int key_code = (uint16_t) event->data.keyboard.keycode; - - if (event->data.keyboard.keycode == VC_ESCAPE - && atoi(cevent) == 11) { - int stopEvent = stop_event(); - // printf("stop_event%d\n", stopEvent); - cstatus = 0; - } - - // printf("atoi(str)---%d\n", atoi(cevent)); - if (key_code == atoi(cevent)) { - int stopEvent = stop_event(); - // printf("%d\n", stopEvent); - cstatus = 0; - } - break; - - case EVENT_KEY_TYPED: - snprintf(buffer + length, sizeof(buffer) - length, - ",keychar=%lc,rawcode=%u", - (uint16_t) event->data.keyboard.keychar, - event->data.keyboard.rawcode); - - #ifdef WE_REALLY_WANT_A_POINTER - char *buf = malloc (6); - #else - char buf[6]; - #endif - - sprintf(buf, "%lc", (uint16_t) event->data.keyboard.keychar); - - #ifdef WE_REALLY_WANT_A_POINTER - free (buf); - #endif - - if (strcmp(buf, cevent) == 0) { - int stopEvent = stop_event(); - // printf("%d\n", stopEvent); - cstatus = 0; - } - // return (char*) event->data.keyboard.keychar; - break; - - case EVENT_MOUSE_PRESSED: - case EVENT_MOUSE_RELEASED: - case EVENT_MOUSE_CLICKED: - case EVENT_MOUSE_MOVED: - case EVENT_MOUSE_DRAGGED: - snprintf(buffer + length, sizeof(buffer) - length, - ",x=%i,y=%i,button=%i,clicks=%i", - event->data.mouse.x, event->data.mouse.y, - event->data.mouse.button, event->data.mouse.clicks); - - int abutton = event->data.mouse.button; - int aclicks = event->data.mouse.clicks; - int amouse = -1; - - if (strcmp(cevent, "mleft") == 0) { - amouse = 1; - } - if (strcmp(cevent, "mright") == 0) { - amouse = 2; - } - if (strcmp(cevent, "wheelDown") == 0) { - amouse = 4; - } - if (strcmp(cevent, "wheelUp") == 0) { - amouse = 5; - } - if (strcmp(cevent, "wheelLeft") == 0) { - amouse = 6; - } - if (strcmp(cevent, "wheelRight") == 0) { - amouse = 7; - } - if (abutton == amouse && aclicks == 1) { - int stopEvent = stop_event(); - cstatus = 0; - } - - break; - - case EVENT_MOUSE_WHEEL: - snprintf(buffer + length, sizeof(buffer) - length, - ",type=%i,amount=%i,rotation=%i", - event->data.wheel.type, event->data.wheel.amount, - event->data.wheel.rotation); - break; - - default: - break; - } - - // fprintf(stdout, "----%s\n", buffer); +void endPoll(){ + sending = false; + pollEv(); // remove last things from channel + eb_chan_release(events); } int add_event(char *key_event) { // (uint16_t *) cevent = key_event; + add_hook(&dispatch_proc_end); + + return cstatus; +} + +void add_event_async(){ + add_hook(&dispatch_proc); +} + +int add_hook(dispatcher_t dispatch) { // Set the logger callback for library output. hook_set_logger(&loggerProc); // Set the event callback for IOhook events. - hook_set_dispatch_proc(&dispatch_proc); + hook_set_dispatch_proc(dispatch); + // Start the hook and block. // NOTE If EVENT_HOOK_ENABLED was delivered, the status will always succeed. int status = hook_run(); @@ -224,9 +142,9 @@ int add_event(char *key_event) { break; } - // return status; + return status; // printf("%d\n", status); - return cstatus; + // return cstatus; } int stop_event(){ @@ -242,7 +160,7 @@ int stop_event(){ loggerProc(LOG_LEVEL_ERROR, "Failed to get XRecord context. (%#X)", status); break; - // Default error. + // Default error. case IOHOOK_FAILURE: default: // loggerProc(LOG_LEVEL_ERROR, "An unknown hook error occurred. (%#X)", status); @@ -250,4 +168,6 @@ int stop_event(){ } return status; -} \ No newline at end of file +} + +#endif \ No newline at end of file diff --git a/vendor/github.com/robotn/gohook/event/os.h b/vendor/github.com/robotn/gohook/event/os.h index ab46989..60a4d86 100644 --- a/vendor/github.com/robotn/gohook/event/os.h +++ b/vendor/github.com/robotn/gohook/event/os.h @@ -23,7 +23,7 @@ #endif /* USE_X11 */ #if defined(IS_WINDOWS) - #define STRICT /* Require use of exact types. */ +// #define STRICT /* Require use of exact types. */ #define WIN32_LEAN_AND_MEAN 1 /* Speed up compilation. */ #include #elif !defined(IS_MACOSX) && !defined(USE_X11) diff --git a/vendor/github.com/robotn/gohook/event/pub.h b/vendor/github.com/robotn/gohook/event/pub.h index 92f59a1..80a76d9 100644 --- a/vendor/github.com/robotn/gohook/event/pub.h +++ b/vendor/github.com/robotn/gohook/event/pub.h @@ -33,21 +33,28 @@ #include #include #include -#include "../hook/iohook.h" +#include "../hook/iohook.h" +#include "../chan/eb_chan.h" + +eb_chan events; +bool sending = false; int vccode[100]; -int codesz; +int codesz; char *cevent; -int rrevent; // uint16_t *cevent; int cstatus = 1; int event_status; +int rrevent; - -int stop_event(); +int add_hook(dispatcher_t dispatch); +void add_event_async(); int add_event(char *key_event); +int stop_event(); + +void dispatch_proc_end(iohook_event * const event); // int allEvent(char *key_event); int allEvent(char *key_event, int vcode[], int size); diff --git a/vendor/github.com/robotn/gohook/example/main.go b/vendor/github.com/robotn/gohook/example/main.go new file mode 100644 index 0000000..9b1a7e8 --- /dev/null +++ b/vendor/github.com/robotn/gohook/example/main.go @@ -0,0 +1,40 @@ +package main + +import ( + "fmt" + + "github.com/robotn/gohook" +) + +func add() { + s := hook.Start() + defer hook.End() + + ct := false + for { + i := <-s + + if i.Kind == hook.KeyHold && i.Rawcode == 59 { + ct = true + } + + if ct && i.Rawcode == 12 { + break + } + } +} + +func base() { + EvChan := hook.Start() + defer hook.End() + + for ev := range EvChan { + fmt.Println(ev) + } +} + +func main() { + base() + + add() +} diff --git a/vendor/github.com/robotn/gohook/extern.go b/vendor/github.com/robotn/gohook/extern.go new file mode 100644 index 0000000..6cacb4c --- /dev/null +++ b/vendor/github.com/robotn/gohook/extern.go @@ -0,0 +1,37 @@ +package hook + +/* + +// #include "event/hook_async.h" +*/ +import "C" +import ( + "log" + "time" + + "encoding/json" +) + +//export go_send +func go_send(s *C.char) { + str := []byte(C.GoString(s)) + out := Event{} + + err := json.Unmarshal(str, &out) + if err != nil { + log.Fatal(err) + } + + if out.Keychar != CharUndefined { + raw2key[out.Rawcode] = string([]rune{out.Keychar}) + } + + // todo bury this deep into the C lib so that the time is correct + out.When = time.Now() // at least it's consistent + if err != nil { + log.Fatal(err) + } + + // todo: maybe make non-bloking + ev <- out +} diff --git a/vendor/github.com/robotn/gohook/hook.go b/vendor/github.com/robotn/gohook/hook.go index 0613cb0..65d59dd 100644 --- a/vendor/github.com/robotn/gohook/hook.go +++ b/vendor/github.com/robotn/gohook/hook.go @@ -20,15 +20,146 @@ package hook //#cgo windows LDFLAGS: -lgdi32 -luser32 #include "event/goEvent.h" -// #include "event/hook_async.h" + */ import "C" import ( - // "fmt" + "fmt" + "time" "unsafe" ) +const ( + // HookEnabled honk enable status + HookEnabled = 1 // iota + HookDisabled = 2 + + KeyDown = 3 + KeyHold = 4 + KeyUp = 5 + + MouseUp = 6 + MouseHold = 7 + MouseDown = 8 + MouseMove = 9 + MouseDrag = 10 + MouseWheel = 11 + + FakeEvent = 12 + // Keychar could be v + CharUndefined = 0xFFFF + WheelUp = -1 + WheelDown = 1 +) + +// Event Holds a system event +// If it's a Keyboard event the relevant fields are: +// Mask, Keycode, Rawcode, and Keychar, +// Keychar is probably what you want. +// If it's a Mouse event the relevant fields are: +// Button, Clicks, X, Y, Amount, Rotation and Direction +type Event struct { + Kind uint8 `json:"id"` + When time.Time + Mask uint16 `json:"mask"` + Reserved uint16 `json:"reserved"` + + Keycode uint16 `json:"keycode"` + Rawcode uint16 `json:"rawcode"` + Keychar rune `json:"keychar"` + + Button uint16 `json:"button"` + Clicks uint16 `json:"clicks"` + + X int16 `json:"x"` + Y int16 `json:"y"` + + Amount uint16 `json:"amount"` + Rotation int16 `json:"rotation"` + Direction uint8 `json:"direction"` +} + +var ( + ev = make(chan Event, 1024) + asyncon = false +) + +// String return hook kind string +func (e Event) String() string { + switch e.Kind { + case HookEnabled: + return fmt.Sprintf("%v - Event: {Kind: HookEnabled}", e.When) + case HookDisabled: + return fmt.Sprintf("%v - Event: {Kind: HookDisabled}", e.When) + case KeyUp: + return fmt.Sprintf("%v - Event: {Kind: KeyUp, Rawcode: %v, Keychar: %v}", e.When, e.Rawcode, e.Keychar) + case KeyHold: + return fmt.Sprintf("%v - Event: {Kind: KeyHold, Rawcode: %v, Keychar: %v}", e.When, e.Rawcode, e.Keychar) + case KeyDown: + return fmt.Sprintf("%v - Event: {Kind: KeyDown, Rawcode: %v, Keychar: %v}", e.When, e.Rawcode, e.Keychar) + case MouseUp: + return fmt.Sprintf("%v - Event: {Kind: MouseUp, Button: %v, X: %v, Y: %v, Clicks: %v}", e.When, e.Button, e.X, e.Y, e.Clicks) + case MouseHold: + return fmt.Sprintf("%v - Event: {Kind: MouseHold, Button: %v, X: %v, Y: %v, Clicks: %v}", e.When, e.Button, e.X, e.Y, e.Clicks) + case MouseDown: + return fmt.Sprintf("%v - Event: {Kind: MouseDown, Button: %v, X: %v, Y: %v, Clicks: %v}", e.When, e.Button, e.X, e.Y, e.Clicks) + case MouseMove: + return fmt.Sprintf("%v - Event: {Kind: MouseMove, Button: %v, X: %v, Y: %v, Clicks: %v}", e.When, e.Button, e.X, e.Y, e.Clicks) + case MouseDrag: + return fmt.Sprintf("%v - Event: {Kind: MouseDrag, Button: %v, X: %v, Y: %v, Clicks: %v}", e.When, e.Button, e.X, e.Y, e.Clicks) + case MouseWheel: + return fmt.Sprintf("%v - Event: {Kind: MouseWheel, Amount: %v, Rotation: %v, Direction: %v}", e.When, e.Amount, e.Rotation, e.Direction) + case FakeEvent: + return fmt.Sprintf("%v - Event: {Kind: FakeEvent}", e.When) + } + + return "Unknown event, contact the mantainers" +} + +// RawcodetoKeychar rawcode to keychar +func RawcodetoKeychar(r uint16) string { + return raw2key[r] +} + +// KeychartoRawcode key char to rawcode +func KeychartoRawcode(kc string) uint16 { + return keytoraw[kc] +} + +// Start Adds global event hook to OS +// returns event channel +func Start() chan Event { + asyncon = true + go C.start_ev() + + go func() { + for { + C.pollEv() + time.Sleep(time.Millisecond * 50) + + // todo: find smallest time that does not destroy the cpu utilization + if !asyncon { + return + } + } + }() + + return ev +} + +// End removes global event hook +func End() { + C.endPoll() + C.stop_event() + + for len(ev) != 0 { + <-ev + } + + asyncon = false +} + // AddEvent add event listener func AddEvent(key string) int { cs := C.CString(key) diff --git a/vendor/github.com/robotn/gohook/hook/darwin/hook_c.h b/vendor/github.com/robotn/gohook/hook/darwin/hook_c.h index 474bef0..b717bcf 100644 --- a/vendor/github.com/robotn/gohook/hook/darwin/hook_c.h +++ b/vendor/github.com/robotn/gohook/hook/darwin/hook_c.h @@ -1110,235 +1110,234 @@ CGEventRef hook_event_proc(CGEventTapProxy tap_proxy, CGEventType type, CGEventR IOHOOK_API int hook_run() { int status = IOHOOK_SUCCESS; - do { - // Reset the restart flag... - restart_tap = false; + // Check for accessibility each time we start the loop. + if (is_accessibility_enabled()) { + logger(LOG_LEVEL_DEBUG, "%s [%u]: Accessibility API is enabled.\n", + __FUNCTION__, __LINE__); - // Check for accessibility each time we start the loop. - if (is_accessibility_enabled()) { - logger(LOG_LEVEL_DEBUG, "%s [%u]: Accessibility API is enabled.\n", - __FUNCTION__, __LINE__); + do { + // Reset the restart flag... + restart_tap = false; - // Initialize starting modifiers. - initialize_modifiers(); + // Initialize starting modifiers. + initialize_modifiers(); - // Try and allocate memory for hook_info. - hook_info *hook = malloc(sizeof(hook_info)); - if (hook != NULL) { - // Setup the event mask to listen for. - #ifdef USE_DEBUG - CGEventMask event_mask = kCGEventMaskForAllEvents; - #else - CGEventMask event_mask = CGEventMaskBit(kCGEventKeyDown) | - CGEventMaskBit(kCGEventKeyUp) | - CGEventMaskBit(kCGEventFlagsChanged) | + // Try and allocate memory for hook_info. + hook_info *hook = malloc(sizeof(hook_info)); + if (hook != NULL) { + // Setup the event mask to listen for. + #ifdef USE_DEBUG + CGEventMask event_mask = kCGEventMaskForAllEvents; + #else + CGEventMask event_mask = CGEventMaskBit(kCGEventKeyDown) | + CGEventMaskBit(kCGEventKeyUp) | + CGEventMaskBit(kCGEventFlagsChanged) | - CGEventMaskBit(kCGEventLeftMouseDown) | - CGEventMaskBit(kCGEventLeftMouseUp) | - CGEventMaskBit(kCGEventLeftMouseDragged) | + CGEventMaskBit(kCGEventLeftMouseDown) | + CGEventMaskBit(kCGEventLeftMouseUp) | + CGEventMaskBit(kCGEventLeftMouseDragged) | - CGEventMaskBit(kCGEventRightMouseDown) | - CGEventMaskBit(kCGEventRightMouseUp) | - CGEventMaskBit(kCGEventRightMouseDragged) | + CGEventMaskBit(kCGEventRightMouseDown) | + CGEventMaskBit(kCGEventRightMouseUp) | + CGEventMaskBit(kCGEventRightMouseDragged) | - CGEventMaskBit(kCGEventOtherMouseDown) | - CGEventMaskBit(kCGEventOtherMouseUp) | - CGEventMaskBit(kCGEventOtherMouseDragged) | + CGEventMaskBit(kCGEventOtherMouseDown) | + CGEventMaskBit(kCGEventOtherMouseUp) | + CGEventMaskBit(kCGEventOtherMouseDragged) | - CGEventMaskBit(kCGEventMouseMoved) | - CGEventMaskBit(kCGEventScrollWheel) | + CGEventMaskBit(kCGEventMouseMoved) | + CGEventMaskBit(kCGEventScrollWheel) | - // NOTE This event is undocumented and used - // for caps-lock release and multi-media keys. - CGEventMaskBit(NX_SYSDEFINED); - #endif + // NOTE This event is undocumented and used + // for caps-lock release and multi-media keys. + CGEventMaskBit(NX_SYSDEFINED); + #endif - // Create the event tap. - hook->port = CGEventTapCreate( - kCGSessionEventTap, // kCGHIDEventTap - kCGHeadInsertEventTap, // kCGTailAppendEventTap - kCGEventTapOptionDefault, // kCGEventTapOptionListenOnly See Bug #22 - event_mask, - hook_event_proc, - NULL); + // Create the event tap. + hook->port = CGEventTapCreate( + kCGSessionEventTap, // kCGHIDEventTap + kCGHeadInsertEventTap, // kCGTailAppendEventTap + kCGEventTapOptionDefault, // kCGEventTapOptionListenOnly See Bug #22 + event_mask, + hook_event_proc, + NULL); - if (hook->port != NULL) { - logger(LOG_LEVEL_DEBUG, "%s [%u]: CGEventTapCreate Successful.\n", - __FUNCTION__, __LINE__); - - // Create the runloop event source from the event tap. - hook->source = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, hook->port, 0); - if (hook->source != NULL) { - logger(LOG_LEVEL_DEBUG, "%s [%u]: CFMachPortCreateRunLoopSource successful.\n", + if (hook->port != NULL) { + logger(LOG_LEVEL_DEBUG, "%s [%u]: CGEventTapCreate Successful.\n", __FUNCTION__, __LINE__); - event_loop = CFRunLoopGetCurrent(); - if (event_loop != NULL) { - logger(LOG_LEVEL_DEBUG, "%s [%u]: CFRunLoopGetCurrent successful.\n", + // Create the runloop event source from the event tap. + hook->source = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, hook->port, 0); + if (hook->source != NULL) { + logger(LOG_LEVEL_DEBUG, "%s [%u]: CFMachPortCreateRunLoopSource successful.\n", __FUNCTION__, __LINE__); - // Create run loop observers. - hook->observer = CFRunLoopObserverCreate( - kCFAllocatorDefault, - kCFRunLoopEntry | kCFRunLoopExit, //kCFRunLoopAllActivities, - true, - 0, - hook_status_proc, - NULL); - - if (hook->observer != NULL) { - logger(LOG_LEVEL_DEBUG, "%s [%u]: CFRunLoopObserverCreate successful.\n", + event_loop = CFRunLoopGetCurrent(); + if (event_loop != NULL) { + logger(LOG_LEVEL_DEBUG, "%s [%u]: CFRunLoopGetCurrent successful.\n", __FUNCTION__, __LINE__); - tis_message = (TISMessage *) calloc(1, sizeof(TISMessage)); - if (tis_message != NULL) { - if (! CFEqual(event_loop, CFRunLoopGetMain())) { - #ifdef USE_WEAK_IMPORT - if (dispatch_sync_f == NULL || dispatch_get_main_queue == NULL) { - #else - *(void **) (&dispatch_sync_f_f) = dlsym(RTLD_DEFAULT, "dispatch_sync_f"); - const char *dlError = dlerror(); - if (dlError != NULL) { - logger(LOG_LEVEL_DEBUG, "%s [%u]: %s.\n", - __FUNCTION__, __LINE__, dlError); - } + // Create run loop observers. + hook->observer = CFRunLoopObserverCreate( + kCFAllocatorDefault, + kCFRunLoopEntry | kCFRunLoopExit, //kCFRunLoopAllActivities, + true, + 0, + hook_status_proc, + NULL); - *(void **) (&dispatch_get_main_queue_f) = dlsym(RTLD_DEFAULT, "dispatch_get_main_queue"); - dlError = dlerror(); - if (dlError != NULL) { - logger(LOG_LEVEL_DEBUG, "%s [%u]: %s.\n", - __FUNCTION__, __LINE__, dlError); - } + if (hook->observer != NULL) { + logger(LOG_LEVEL_DEBUG, "%s [%u]: CFRunLoopObserverCreate successful.\n", + __FUNCTION__, __LINE__); - if (dispatch_sync_f_f == NULL || dispatch_get_main_queue_f == NULL) { - #endif - logger(LOG_LEVEL_DEBUG, "%s [%u]: Failed to locate dispatch_sync_f() or dispatch_get_main_queue()!\n", - __FUNCTION__, __LINE__); - - #if ! defined(USE_CARBON_LEGACY) && defined(USE_COREFOUNDATION) - logger(LOG_LEVEL_DEBUG, "%s [%u]: Falling back to runloop signaling.\n", - __FUNCTION__, __LINE__); - - int runloop_status = start_message_port_runloop(); - if (runloop_status != IOHOOK_SUCCESS) { - return runloop_status; + tis_message = (TISMessage *) calloc(1, sizeof(TISMessage)); + if (tis_message != NULL) { + if (! CFEqual(event_loop, CFRunLoopGetMain())) { + #ifdef USE_WEAK_IMPORT + if (dispatch_sync_f == NULL || dispatch_get_main_queue == NULL) { + #else + *(void **) (&dispatch_sync_f_f) = dlsym(RTLD_DEFAULT, "dispatch_sync_f"); + const char *dlError = dlerror(); + if (dlError != NULL) { + logger(LOG_LEVEL_DEBUG, "%s [%u]: %s.\n", + __FUNCTION__, __LINE__, dlError); } + + *(void **) (&dispatch_get_main_queue_f) = dlsym(RTLD_DEFAULT, "dispatch_get_main_queue"); + dlError = dlerror(); + if (dlError != NULL) { + logger(LOG_LEVEL_DEBUG, "%s [%u]: %s.\n", + __FUNCTION__, __LINE__, dlError); + } + + if (dispatch_sync_f_f == NULL || dispatch_get_main_queue_f == NULL) { #endif + logger(LOG_LEVEL_DEBUG, "%s [%u]: Failed to locate dispatch_sync_f() or dispatch_get_main_queue()!\n", + __FUNCTION__, __LINE__); + + #if ! defined(USE_CARBON_LEGACY) && defined(USE_COREFOUNDATION) + logger(LOG_LEVEL_DEBUG, "%s [%u]: Falling back to runloop signaling.\n", + __FUNCTION__, __LINE__); + + int runloop_status = start_message_port_runloop(); + if (runloop_status != IOHOOK_SUCCESS) { + return runloop_status; + } + #endif + } } - } - // Add the event source and observer to the runloop mode. - CFRunLoopAddSource(event_loop, hook->source, kCFRunLoopDefaultMode); - CFRunLoopAddObserver(event_loop, hook->observer, kCFRunLoopDefaultMode); + // Add the event source and observer to the runloop mode. + CFRunLoopAddSource(event_loop, hook->source, kCFRunLoopDefaultMode); + CFRunLoopAddObserver(event_loop, hook->observer, kCFRunLoopDefaultMode); - #ifdef USE_OBJC - // Create a garbage collector to handle Cocoa events correctly. - Class NSAutoreleasePool_class = (Class) objc_getClass("NSAutoreleasePool"); - id pool = class_createInstance(NSAutoreleasePool_class, 0); - auto_release_pool = objc_msgSend(pool, sel_registerName("init")); - #endif - - // Start the hook thread runloop. - CFRunLoopRun(); - - - #ifdef USE_OBJC - //objc_msgSend(auto_release_pool, sel_registerName("drain")); - objc_msgSend(auto_release_pool, sel_registerName("release")); - #endif - - // Lock back up until we are done processing the exit. - if (CFRunLoopContainsObserver(event_loop, hook->observer, kCFRunLoopDefaultMode)) { - CFRunLoopRemoveObserver(event_loop, hook->observer, kCFRunLoopDefaultMode); - } - - if (CFRunLoopContainsSource(event_loop, hook->source, kCFRunLoopDefaultMode)) { - CFRunLoopRemoveSource(event_loop, hook->source, kCFRunLoopDefaultMode); - } - - #if ! defined(USE_CARBON_LEGACY) && defined(USE_COREFOUNDATION) - if (! CFEqual(event_loop, CFRunLoopGetMain())) { - #ifdef USE_WEAK_IMPORT - if (dispatch_sync_f == NULL || dispatch_get_main_queue == NULL) { - #else - if (dispatch_sync_f_f == NULL || dispatch_get_main_queue_f == NULL) { + #ifdef USE_OBJC + // Create a garbage collector to handle Cocoa events correctly. + Class NSAutoreleasePool_class = (Class) objc_getClass("NSAutoreleasePool"); + id pool = class_createInstance(NSAutoreleasePool_class, 0); + auto_release_pool = objc_msgSend(pool, sel_registerName("init")); #endif - stop_message_port_runloop(); - } - } - #endif - // Free the TIS Message. - free(tis_message); + // Start the hook thread runloop. + CFRunLoopRun(); + + + #ifdef USE_OBJC + //objc_msgSend(auto_release_pool, sel_registerName("drain")); + objc_msgSend(auto_release_pool, sel_registerName("release")); + #endif + + // Lock back up until we are done processing the exit. + if (CFRunLoopContainsObserver(event_loop, hook->observer, kCFRunLoopDefaultMode)) { + CFRunLoopRemoveObserver(event_loop, hook->observer, kCFRunLoopDefaultMode); + } + + if (CFRunLoopContainsSource(event_loop, hook->source, kCFRunLoopDefaultMode)) { + CFRunLoopRemoveSource(event_loop, hook->source, kCFRunLoopDefaultMode); + } + + #if ! defined(USE_CARBON_LEGACY) && defined(USE_COREFOUNDATION) + if (! CFEqual(event_loop, CFRunLoopGetMain())) { + #ifdef USE_WEAK_IMPORT + if (dispatch_sync_f == NULL || dispatch_get_main_queue == NULL) { + #else + if (dispatch_sync_f_f == NULL || dispatch_get_main_queue_f == NULL) { + #endif + stop_message_port_runloop(); + } + } + #endif + + // Free the TIS Message. + free(tis_message); + } + else { + logger(LOG_LEVEL_ERROR, "%s [%u]: Failed to allocate memory for TIS message structure!\n", + __FUNCTION__, __LINE__); + + // Set the exit status. + status = IOHOOK_ERROR_OUT_OF_MEMORY; + } + + // Invalidate and free hook observer. + CFRunLoopObserverInvalidate(hook->observer); + CFRelease(hook->observer); } else { - logger(LOG_LEVEL_ERROR, "%s [%u]: Failed to allocate memory for TIS message structure!\n", + // We cant do a whole lot of anything if we cant + // create run loop observer. + logger(LOG_LEVEL_ERROR, "%s [%u]: CFRunLoopObserverCreate failure!\n", __FUNCTION__, __LINE__); // Set the exit status. - status = IOHOOK_ERROR_OUT_OF_MEMORY; + status = IOHOOK_ERROR_CREATE_OBSERVER; } - - // Invalidate and free hook observer. - CFRunLoopObserverInvalidate(hook->observer); - CFRelease(hook->observer); } else { - // We cant do a whole lot of anything if we cant - // create run loop observer. - logger(LOG_LEVEL_ERROR, "%s [%u]: CFRunLoopObserverCreate failure!\n", + logger(LOG_LEVEL_ERROR, "%s [%u]: CFRunLoopGetCurrent failure!\n", __FUNCTION__, __LINE__); // Set the exit status. - status = IOHOOK_ERROR_CREATE_OBSERVER; + status = IOHOOK_ERROR_GET_RUNLOOP; } + + // Clean up the event source. + CFRelease(hook->source); } else { - logger(LOG_LEVEL_ERROR, "%s [%u]: CFRunLoopGetCurrent failure!\n", + logger(LOG_LEVEL_ERROR, "%s [%u]: CFMachPortCreateRunLoopSource failure!\n", __FUNCTION__, __LINE__); // Set the exit status. - status = IOHOOK_ERROR_GET_RUNLOOP; + status = IOHOOK_ERROR_CREATE_RUN_LOOP_SOURCE; } - // Clean up the event source. - CFRelease(hook->source); + // Stop the CFMachPort from receiving any more messages. + CFMachPortInvalidate(hook->port); + CFRelease(hook->port); } else { - logger(LOG_LEVEL_ERROR, "%s [%u]: CFMachPortCreateRunLoopSource failure!\n", + logger(LOG_LEVEL_ERROR, "%s [%u]: Failed to create event port!\n", __FUNCTION__, __LINE__); // Set the exit status. - status = IOHOOK_ERROR_CREATE_RUN_LOOP_SOURCE; + status = IOHOOK_ERROR_CREATE_EVENT_PORT; } - // Stop the CFMachPort from receiving any more messages. - CFMachPortInvalidate(hook->port); - CFRelease(hook->port); + // Free the hook structure. + free(hook); } else { - logger(LOG_LEVEL_ERROR, "%s [%u]: Failed to create event port!\n", - __FUNCTION__, __LINE__); - - // Set the exit status. - status = IOHOOK_ERROR_CREATE_EVENT_PORT; + status = IOHOOK_ERROR_OUT_OF_MEMORY; } + } while (restart_tap); + } else { + logger(LOG_LEVEL_ERROR, "%s [%u]: Accessibility API is disabled!\n", + __FUNCTION__, __LINE__); - // Free the hook structure. - free(hook); - } - else { - status = IOHOOK_ERROR_OUT_OF_MEMORY; - } - } - else { - logger(LOG_LEVEL_ERROR, "%s [%u]: Accessibility API is disabled!\n", - __FUNCTION__, __LINE__); - - // Set the exit status. - status = IOHOOK_ERROR_AXAPI_DISABLED; - } - } while (restart_tap); + // Set the exit status. + status = IOHOOK_ERROR_AXAPI_DISABLED; + } logger(LOG_LEVEL_DEBUG, "%s [%u]: Something, something, something, complete.\n", __FUNCTION__, __LINE__); diff --git a/vendor/github.com/robotn/gohook/hook/iohook.h b/vendor/github.com/robotn/gohook/hook/iohook.h index 022c2a1..e135ba6 100644 --- a/vendor/github.com/robotn/gohook/hook/iohook.h +++ b/vendor/github.com/robotn/gohook/hook/iohook.h @@ -79,7 +79,8 @@ typedef struct _screen_data { typedef struct _keyboard_event_data { uint16_t keycode; uint16_t rawcode; - uint16_t keychar; + // uint16_t keychar; + uint32_t keychar; // char *keychar; } keyboard_event_data, key_pressed_event_data, diff --git a/vendor/github.com/robotn/gohook/hook/windows/input_c.h b/vendor/github.com/robotn/gohook/hook/windows/input_c.h index c261c87..995101a 100644 --- a/vendor/github.com/robotn/gohook/hook/windows/input_c.h +++ b/vendor/github.com/robotn/gohook/hook/windows/input_c.h @@ -293,8 +293,8 @@ unsigned short keycode_to_scancode(DWORD vk_code, DWORD flags) { scancode = keycode_scancode_table[vk_code][0]; if (flags & LLKHF_EXTENDED) { - logger(LOG_LEVEL_WARN, "%s [%u]: EXTD2, vk_code %li\n", - __FUNCTION__, __LINE__, vk_code); +// logger(LOG_LEVEL_WARN, "%s [%u]: EXTD2, vk_code %li\n", +// __FUNCTION__, __LINE__, vk_code); switch (vk_code) { case VK_PRIOR: diff --git a/vendor/github.com/robotn/gohook/tables.go b/vendor/github.com/robotn/gohook/tables.go new file mode 100644 index 0000000..9a4ec86 --- /dev/null +++ b/vendor/github.com/robotn/gohook/tables.go @@ -0,0 +1,352 @@ +package hook + +var ( + raw2key = map[uint16]string{ // https://github.com/wesbos/keycodes + 0: "error", + 3: "break", + 8: "backspace", + 9: "tab", + 12: "clear", + 13: "enter", + 16: "shift", + 17: "ctrl", + 18: "alt", + 19: "pause/break", + 20: "caps lock", + 21: "hangul", + 25: "hanja", + 27: "escape", + 28: "conversion", + 29: "non-conversion", + 32: "spacebar", + 33: "page up", + 34: "page down", + 35: "end", + 36: "home", + 37: "left arrow", + 38: "up arrow", + 39: "right arrow", + 40: "down arrow", + 41: "select", + 42: "print", + 43: "execute", + 44: "Print Screen", + 45: "insert", + 46: "delete", + 47: "help", + 48: "0", + 49: "1", + 50: "2", + 51: "3", + 52: "4", + 53: "5", + 54: "6", + 55: "7", + 56: "8", + 57: "9", + 58: ":", + 59: ";", + 60: "<", + 61: "=", + 63: "ß", + 64: "@", + 65: "a", + 66: "b", + 67: "c", + 68: "d", + 69: "e", + 70: "f", + 71: "g", + 72: "h", + 73: "i", + 74: "j", + 75: "k", + 76: "l", + 77: "m", + 78: "n", + 79: "o", + 80: "p", + 81: "q", + 82: "r", + 83: "s", + 84: "t", + 85: "u", + 86: "v", + 87: "w", + 88: "x", + 89: "y", + 90: "z", + 91: "l-super", + 92: "r-super", + 93: "apps", + 95: "sleep", + 96: "numpad 0", + 97: "numpad 1", + 98: "numpad 2", + 99: "numpad 3", + 100: "numpad 4", + 101: "numpad 5", + 102: "numpad 6", + 103: "numpad 7", + 104: "numpad 8", + 105: "numpad 9", + 106: "multiply", + 107: "add", + 108: "numpad period", + 109: "subtract", + 110: "decimal point", + 111: "divide", + 112: "f1", + 113: "f2", + 114: "f3", + 115: "f4", + 116: "f5", + 117: "f6", + 118: "f7", + 119: "f8", + 120: "f9", + 121: "f10", + 122: "f11", + 123: "f12", + 124: "f13", + 125: "f14", + 126: "f15", + 127: "f16", + 128: "f17", + 129: "f18", + 130: "f19", + 131: "f20", + 132: "f21", + 133: "f22", + 134: "f23", + 135: "f24", + 144: "num lock", + 145: "scroll lock", + 160: "^", + 161: "!", + 162: "؛", + 163: "#", + 164: "$", + 165: "ù", + 166: "page backward", + 167: "page forward", + 168: "refresh", + 169: "closing paren (AZERTY)", + 170: "*", + 171: "~ + * key", + 172: "home key", + 173: "minus (firefox), mute/unmute", + 174: "decrease volume level", + 175: "increase volume level", + 176: "next", + 177: "previous", + 178: "stop", + 179: "play/pause", + 180: "e-mail", + 181: "mute/unmute (firefox)", + 182: "decrease volume level (firefox)", + 183: "increase volume level (firefox)", + 186: "semi-colon / ñ", + 187: "equal sign", + 188: "comma", + 189: "dash", + 190: "period", + 191: "forward slash / ç", + 192: "grave accent / ñ / æ / ö", + 193: "?, / or °", + 194: "numpad period (chrome)", + 219: "open bracket", + 220: "back slash", + 221: "close bracket / å", + 222: "single quote / ø / ä", + 223: "`", + 224: "left or right ⌘ key (firefox)", + 225: "altgr", + 226: "< /git >, left back slash", + 230: "GNOME Compose Key", + 231: "ç", + 233: "XF86Forward", + 234: "XF86Back", + 235: "non-conversion", + 240: "alphanumeric", + 242: "hiragana/katakana", + 243: "half-width/full-width", + 244: "kanji", + 251: "unlock trackpad (Chrome/Edge)", + 255: "toggle touchpad", + } + + keytoraw = map[string]uint16{ + "error": 0, + "break": 3, + "backspace": 8, + "tab": 9, + "clear": 12, + "enter": 13, + "shift": 16, + "ctrl": 17, + "alt": 18, + "pause/break": 19, + "caps lock": 20, + "hangul": 21, + "hanja": 25, + "escape": 27, + "conversion": 28, + "non-conversion": 29, + "spacebar": 32, + "page up": 33, + "page down": 34, + "end": 35, + "home": 36, + "left arrow": 37, + "up arrow": 38, + "right arrow": 39, + "down arrow": 40, + "select": 41, + "print": 42, + "execute": 43, + "Print Screen": 44, + "insert": 45, + "delete": 46, + "help": 47, + "0": 48, + "1": 49, + "2": 50, + "3": 51, + "4": 52, + "5": 53, + "6": 54, + "7": 55, + "8": 56, + "9": 57, + ":": 58, + ";": 59, + "<": 60, + "=": 61, + "ß": 63, + "@": 64, + "a": 65, + "b": 66, + "c": 67, + "d": 68, + "e": 69, + "f": 70, + "g": 71, + "h": 72, + "i": 73, + "j": 74, + "k": 75, + "l": 76, + "m": 77, + "n": 78, + "o": 79, + "p": 80, + "q": 81, + "r": 82, + "s": 83, + "t": 84, + "u": 85, + "v": 86, + "w": 87, + "x": 88, + "y": 89, + "z": 90, + "l-super": 91, + "r-super": 92, + "apps": 93, + "sleep": 95, + "numpad 0": 96, + "numpad 1": 97, + "numpad 2": 98, + "numpad 3": 99, + "numpad 4": 100, + "numpad 5": 101, + "numpad 6": 102, + "numpad 7": 103, + "numpad 8": 104, + "numpad 9": 105, + "multiply": 106, + "add": 107, + "numpad period": 108, + "subtract": 109, + "decimal point": 110, + "divide": 111, + "f1": 112, + "f2": 113, + "f3": 114, + "f4": 115, + "f5": 116, + "f6": 117, + "f7": 118, + "f8": 119, + "f9": 120, + "f10": 121, + "f11": 122, + "f12": 123, + "f13": 124, + "f14": 125, + "f15": 126, + "f16": 127, + "f17": 128, + "f18": 129, + "f19": 130, + "f20": 131, + "f21": 132, + "f22": 133, + "f23": 134, + "f24": 135, + "num lock": 144, + "scroll lock": 145, + "^": 160, + "!": 161, + "؛": 162, + "#": 163, + "$": 164, + "ù": 165, + "page backward": 166, + "page forward": 167, + "refresh": 168, + "closing paren (AZERTY)": 169, + "*": 170, + "~ + * key": 171, + "home key": 172, + "minus (firefox), mute/unmute": 173, + "decrease volume level": 174, + "increase volume level": 175, + "next": 176, + "previous": 177, + "stop": 178, + "play/pause": 179, + "e-mail": 180, + "mute/unmute (firefox)": 181, + "decrease volume level (firefox)": 182, + "increase volume level (firefox)": 183, + "semi-colon / ñ": 186, + "equal sign": 187, + "comma": 188, + "dash": 189, + "period": 190, + "forward slash / ç": 191, + "grave accent / ñ / æ / ö": 192, + "?, / or °": 193, + "numpad period (chrome)": 194, + "open bracket": 219, + "back slash": 220, + "close bracket / å": 221, + "single quote / ø / ä": 222, + "`": 223, + "left or right ⌘ key (firefox)": 224, + "altgr": 225, + "< /git >, left back slash": 226, + "GNOME Compose Key": 230, + "ç": 231, + "XF86Forward": 233, + "XF86Back": 234, + "alphanumeric": 240, + "hiragana/katakana": 242, + "half-width/full-width": 243, + "kanji": 244, + "unlock trackpad (Chrome/Edge)": 251, + "toggle touchpad": 255, + } +) diff --git a/vendor/modules.txt b/vendor/modules.txt index 33a7a76..f5fb9a4 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -17,7 +17,7 @@ github.com/go-ole/go-ole/oleutil github.com/lxn/win # github.com/otiai10/gosseract v2.2.0+incompatible github.com/otiai10/gosseract -# github.com/robotn/gohook v0.0.0-20181215173318-e36d1aac6c1a +# github.com/robotn/gohook v0.0.0-20190220150725-b5ca357c14ff github.com/robotn/gohook # github.com/shirou/gopsutil v2.18.12+incompatible github.com/shirou/gopsutil/process @@ -30,7 +30,7 @@ github.com/shirou/gopsutil/net github.com/shirou/w32 # github.com/vcaesar/imgo v0.0.0-20181209162409-13af122cf2fa github.com/vcaesar/imgo -# golang.org/x/image v0.0.0-20181116024801-cd38e8056d9b +# golang.org/x/image v0.0.0-20181116024801-cd38e8056d9b => github.com/golang/image v0.0.0-20181116024801-cd38e8056d9b golang.org/x/image/bmp # golang.org/x/sys v0.0.0-20190109145017-48ac38b7c8cb => github.com/golang/sys v0.0.0-20190109145017-48ac38b7c8cb golang.org/x/sys/unix