mirror of
https://github.com/go-vgo/robotgo.git
synced 2025-05-31 06:13:55 +00:00
robotgo
This commit is contained in:
parent
cd6bc9e323
commit
af08ef934b
87
base/MMBitmap.h
Normal file
87
base/MMBitmap.h
Normal file
@ -0,0 +1,87 @@
|
||||
#pragma once
|
||||
#ifndef MMBITMAP_H
|
||||
#define MMBITMAP_H
|
||||
|
||||
#include "types.h"
|
||||
#include "rgb.h"
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
struct _MMBitmap {
|
||||
uint8_t *imageBuffer; /* Pixels stored in Quad I format; i.e., origin is in
|
||||
* top left. Length should be height * bytewidth. */
|
||||
size_t width; /* Never 0, unless image is NULL. */
|
||||
size_t height; /* Never 0, unless image is NULL. */
|
||||
size_t bytewidth; /* The aligned width (width + padding). */
|
||||
uint8_t bitsPerPixel; /* Should be either 24 or 32. */
|
||||
uint8_t bytesPerPixel; /* For convenience; should be bitsPerPixel / 8. */
|
||||
};
|
||||
|
||||
typedef struct _MMBitmap MMBitmap;
|
||||
typedef MMBitmap *MMBitmapRef;
|
||||
|
||||
/* Creates new MMBitmap with the given values.
|
||||
* Follows the Create Rule (caller is responsible for destroy()'ing object). */
|
||||
MMBitmapRef createMMBitmap(uint8_t *buffer, size_t width, size_t height,
|
||||
size_t bytewidth, uint8_t bitsPerPixel,
|
||||
uint8_t bytesPerPixel);
|
||||
|
||||
/* Releases memory occupied by MMBitmap. */
|
||||
void destroyMMBitmap(MMBitmapRef bitmap);
|
||||
|
||||
/* Releases memory occupied by MMBitmap. Acts via CallBack method*/
|
||||
void destroyMMBitmapBuffer(char * bitmapBuffer, void * hint);
|
||||
|
||||
/* Returns copy of MMBitmap, to be destroy()'d by caller. */
|
||||
MMBitmapRef copyMMBitmap(MMBitmapRef bitmap);
|
||||
|
||||
/* Returns copy of one MMBitmap juxtaposed in another (to be destroy()'d
|
||||
* by the caller.), or NULL on error. */
|
||||
MMBitmapRef copyMMBitmapFromPortion(MMBitmapRef source, MMRect rect);
|
||||
|
||||
#define MMBitmapPointInBounds(image, p) ((p).x < (image)->width && \
|
||||
(p).y < (image)->height)
|
||||
#define MMBitmapRectInBounds(image, r) \
|
||||
(((r).origin.x + (r).size.width <= (image)->width) && \
|
||||
((r).origin.y + (r).size.height <= (image)->height))
|
||||
|
||||
#define MMBitmapGetBounds(image) MMRectMake(0, 0, image->width, image->height)
|
||||
|
||||
/* Get pointer to pixel of MMBitmapRef. No bounds checking is performed (check
|
||||
* yourself before calling this with MMBitmapPointInBounds(). */
|
||||
#define MMRGBColorRefAtPoint(image, x, y) \
|
||||
(MMRGBColor *)(assert(MMBitmapPointInBounds(bitmap, MMPointMake(x, y))), \
|
||||
((image)->imageBuffer) + (((image)->bytewidth * (y)) \
|
||||
+ ((x) * (image)->bytesPerPixel)))
|
||||
|
||||
/* Dereference pixel of MMBitmapRef. Again, no bounds checking is performed. */
|
||||
#define MMRGBColorAtPoint(image, x, y) *MMRGBColorRefAtPoint(image, x, y)
|
||||
|
||||
/* Hex/integer value of color at point. */
|
||||
#define MMRGBHexAtPoint(image, x, y) \
|
||||
hexFromMMRGB(MMRGBColorAtPoint(image, x, y))
|
||||
|
||||
/* Increment either point.x or point.y depending on the position of point.x.
|
||||
* That is, if x + 1 is >= width, increment y and start x at the beginning.
|
||||
* Otherwise, increment x.
|
||||
*
|
||||
* This is used as a convenience macro to scan rows when calling functions such
|
||||
* as findColorInRectAt() and findBitmapInBitmapAt(). */
|
||||
#define ITER_NEXT_POINT(pixel, width, start_x) \
|
||||
do { \
|
||||
if (++(pixel).x >= (width)) { \
|
||||
(pixel).x = start_x; \
|
||||
++(point).y; \
|
||||
} \
|
||||
} while (0);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* MMBITMAP_H */
|
96
base/MMBitmap_init.h
Normal file
96
base/MMBitmap_init.h
Normal file
@ -0,0 +1,96 @@
|
||||
#include "MMBitmap.h"
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
//MMBitmapRef createMMBitmap()
|
||||
MMBitmapRef createMMBitmap(
|
||||
uint8_t *buffer,
|
||||
size_t width,
|
||||
size_t height,
|
||||
size_t bytewidth,
|
||||
uint8_t bitsPerPixel,
|
||||
uint8_t bytesPerPixel
|
||||
){
|
||||
MMBitmapRef bitmap = malloc(sizeof(MMBitmap));
|
||||
if (bitmap == NULL) return NULL;
|
||||
|
||||
bitmap->imageBuffer = buffer;
|
||||
bitmap->width = width;
|
||||
bitmap->height = height;
|
||||
bitmap->bytewidth = bytewidth;
|
||||
bitmap->bitsPerPixel = bitsPerPixel;
|
||||
bitmap->bytesPerPixel = bytesPerPixel;
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
void destroyMMBitmap(MMBitmapRef bitmap)
|
||||
{
|
||||
assert(bitmap != NULL);
|
||||
|
||||
if (bitmap->imageBuffer != NULL) {
|
||||
free(bitmap->imageBuffer);
|
||||
bitmap->imageBuffer = NULL;
|
||||
}
|
||||
|
||||
free(bitmap);
|
||||
}
|
||||
|
||||
void destroyMMBitmapBuffer(char * bitmapBuffer, void * hint)
|
||||
{
|
||||
if (bitmapBuffer != NULL)
|
||||
{
|
||||
free(bitmapBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
MMBitmapRef copyMMBitmap(MMBitmapRef bitmap)
|
||||
{
|
||||
uint8_t *copiedBuf = NULL;
|
||||
|
||||
assert(bitmap != NULL);
|
||||
if (bitmap->imageBuffer != NULL) {
|
||||
const size_t bufsize = bitmap->height * bitmap->bytewidth;
|
||||
copiedBuf = malloc(bufsize);
|
||||
if (copiedBuf == NULL) return NULL;
|
||||
|
||||
memcpy(copiedBuf, bitmap->imageBuffer, bufsize);
|
||||
}
|
||||
|
||||
return createMMBitmap(copiedBuf,
|
||||
bitmap->width,
|
||||
bitmap->height,
|
||||
bitmap->bytewidth,
|
||||
bitmap->bitsPerPixel,
|
||||
bitmap->bytesPerPixel);
|
||||
}
|
||||
|
||||
MMBitmapRef copyMMBitmapFromPortion(MMBitmapRef source, MMRect rect)
|
||||
{
|
||||
assert(source != NULL);
|
||||
|
||||
if (source->imageBuffer == NULL || !MMBitmapRectInBounds(source, rect)) {
|
||||
return NULL;
|
||||
} else {
|
||||
uint8_t *copiedBuf = NULL;
|
||||
const size_t bufsize = rect.size.height * source->bytewidth;
|
||||
const size_t offset = (source->bytewidth * rect.origin.y) +
|
||||
(rect.origin.x * source->bytesPerPixel);
|
||||
|
||||
/* Don't go over the bounds, programmer! */
|
||||
assert((bufsize + offset) <= (source->bytewidth * source->height));
|
||||
|
||||
copiedBuf = malloc(bufsize);
|
||||
if (copiedBuf == NULL) return NULL;
|
||||
|
||||
memcpy(copiedBuf, source->imageBuffer + offset, bufsize);
|
||||
|
||||
return createMMBitmap(copiedBuf,
|
||||
rect.size.width,
|
||||
rect.size.height,
|
||||
source->bytewidth,
|
||||
source->bitsPerPixel,
|
||||
source->bytesPerPixel);
|
||||
}
|
||||
}
|
54
base/bmp_io.h
Normal file
54
base/bmp_io.h
Normal file
@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
#ifndef BMP_IO_H
|
||||
#define BMP_IO_H
|
||||
|
||||
#include "MMBitmap.h"
|
||||
#include "io.h"
|
||||
|
||||
enum _BMPReadError {
|
||||
kBMPGenericError = 0,
|
||||
kBMPAccessError,
|
||||
kBMPInvalidKeyError,
|
||||
kBMPUnsupportedHeaderError,
|
||||
kBMPInvalidColorPanesError,
|
||||
kBMPUnsupportedColorDepthError,
|
||||
kBMPUnsupportedCompressionError,
|
||||
kBMPInvalidPixelDataError
|
||||
};
|
||||
|
||||
typedef MMIOError MMBMPReadError;
|
||||
|
||||
/* Returns description of given MMBMPReadError.
|
||||
* Returned string is constant and hence should not be freed. */
|
||||
const char *MMBMPReadErrorString(MMIOError error);
|
||||
|
||||
/* Attempts to read bitmap file at path; returns new MMBitmap on success, or
|
||||
* NULL on error. If |error| is non-NULL, it will be set to the error code
|
||||
* on return.
|
||||
*
|
||||
* Currently supports:
|
||||
* - Uncompressed Windows v3/v4/v5 24-bit or 32-bit BMP.
|
||||
* - OS/2 v1 or v2 24-bit BMP.
|
||||
* - Does NOT yet support: 1-bit, 4-bit, 8-bit, 16-bit, compressed bitmaps,
|
||||
* or PNGs/JPEGs disguised as BMPs (and returns NULL if those are given).
|
||||
*
|
||||
* Responsibility for destroy()'ing returned MMBitmap is left up to caller. */
|
||||
MMBitmapRef newMMBitmapFromBMP(const char *path, MMBMPReadError *error);
|
||||
|
||||
/* Returns a buffer containing the raw BMP file data in Windows v3 BMP format,
|
||||
* ready to be saved to a file. If |len| is not NULL, it will be set to the
|
||||
* number of bytes allocated in the returned buffer.
|
||||
*
|
||||
* Responsibility for free()'ing data is left up to the caller. */
|
||||
uint8_t *createBitmapData(MMBitmapRef bitmap, size_t *len);
|
||||
|
||||
/* Saves bitmap to file in Windows v3 BMP format.
|
||||
* Returns 0 on success, -1 on error. */
|
||||
int saveMMBitmapAsBMP(MMBitmapRef bitmap, const char *path);
|
||||
|
||||
/* Swaps bitmap from Quadrant 1 to Quadran III format, or vice versa
|
||||
* (upside-down Cartesian/PostScript/GL <-> right side up QD/CG raster format).
|
||||
*/
|
||||
void flipBitmapData(void *data, size_t width, size_t height, size_t bytewidth);
|
||||
|
||||
#endif /* BMP_IO_H */
|
431
base/bmp_io_init.h
Normal file
431
base/bmp_io_init.h
Normal file
@ -0,0 +1,431 @@
|
||||
#include "bmp_io.h"
|
||||
#include "os.h"
|
||||
#include "endian.h"
|
||||
#include <stdio.h> /* fopen() */
|
||||
#include <string.h> /* memcpy() */
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#include "ms_stdbool.h"
|
||||
#include "ms_stdint.h"
|
||||
#else
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
#pragma pack(push, 1) /* The following structs should be continguous, so we can
|
||||
* copy them in one read. */
|
||||
/*
|
||||
* Standard, initial BMP Header
|
||||
*/
|
||||
struct BITMAP_FILE_HEADER {
|
||||
uint16_t magic; /* First two byes of the file; should be 0x4D42. */
|
||||
uint32_t fileSize; /* Size of the BMP file in bytes (unreliable). */
|
||||
uint32_t reserved; /* Application-specific. */
|
||||
uint32_t imageOffset; /* Offset to bitmap data. */
|
||||
};
|
||||
|
||||
#define BMP_MAGIC 0x4D42 /* The starting key that marks the file as a BMP. */
|
||||
|
||||
enum _BMP_COMPRESSION {
|
||||
kBMP_RGB = 0, /* No compression. */
|
||||
kBMP_RLE8 = 1, /* Can only be used with 8-bit bitmaps. */
|
||||
kBMP_RLE4 = 2, /* Can only be used with 4-bit bitmaps. */
|
||||
kBMP_BITFIELDS = 3, /* Can only be used with 16/32-bit bitmaps. */
|
||||
kBMP_JPEG = 4, /* Bitmap contains a JPEG image. */
|
||||
kBMP_PNG = 5 /* Bitmap contains a PNG image. */
|
||||
};
|
||||
|
||||
typedef uint32_t BMP_COMPRESSION;
|
||||
|
||||
/*
|
||||
* Windows 3 Header
|
||||
*/
|
||||
struct BITMAP_INFO_HEADER {
|
||||
uint32_t headerSize; /* The size of this header (40 bytes). */
|
||||
int32_t width; /* The bitmap width in pixels. */
|
||||
int32_t height; /* The bitmap height in pixels. */
|
||||
/* (A negative value denotes that the image
|
||||
* is flipped.) */
|
||||
uint16_t colorPlanes; /* The number of color planes; must be 1. */
|
||||
uint16_t bitsPerPixel; /* The color depth of the image (1, 4, 8, 16,
|
||||
* 24, or 32). */
|
||||
BMP_COMPRESSION compression; /* The compression method being used. */
|
||||
uint32_t imageSize; /* Size of the bitmap in bytes (unreliable).*/
|
||||
int32_t xRes; /* The horizontal resolution (unreliable). */
|
||||
int32_t yRes; /* The vertical resolution (unreliable). */
|
||||
uint32_t colorsUsed; /* The number of colors in the color table,
|
||||
* or 0 to default to 2^n. */
|
||||
uint32_t colorsImportant; /* Colors important for displaying bitmap,
|
||||
* or 0 when every color is equally important;
|
||||
* ignored. */
|
||||
};
|
||||
|
||||
/*
|
||||
* OS/2 v1 Header
|
||||
*/
|
||||
struct BITMAP_CORE_HEADER {
|
||||
uint32_t headerSize; /* The size of this header (12 bytes). */
|
||||
uint16_t width; /* The bitmap width in pixels. */
|
||||
uint16_t height; /* The bitmap height in pixels. */
|
||||
uint16_t colorPlanes; /* The number of color planes; must be 1. */
|
||||
uint16_t bitsPerPixel; /* Color depth of the image (1, 4, 8, or 24). */
|
||||
};
|
||||
|
||||
#pragma pack(pop) /* Let the compiler do what it wants now. */
|
||||
|
||||
/* BMP files are always saved in little endian format (x86), so we need to
|
||||
* convert them if we're not on a little endian machine (e.g., ARM & ppc). */
|
||||
|
||||
#if __BYTE_ORDER == __BIG_ENDIAN
|
||||
|
||||
/* Converts bitmap file header from to and from little endian, if and only if
|
||||
* host is big endian. */
|
||||
static void convertBitmapFileHeader(struct BITMAP_FILE_HEADER *header)
|
||||
{
|
||||
header->magic = swapLittleAndHost16(header->magic);
|
||||
swapLittleAndHost32(header->fileSize);
|
||||
swapLittleAndHost32(header->reserved);
|
||||
swapLittleAndHost32(header->imageOffset);
|
||||
}
|
||||
|
||||
/* Converts bitmap info header from to and from little endian, if and only if
|
||||
* host is big endian. */
|
||||
static void convertBitmapInfoHeader(struct BITMAP_INFO_HEADER *header)
|
||||
{
|
||||
header->headerSize = swapLittleAndHost32(header->headerSize);
|
||||
header->width = swapLittleAndHost32(header->width);
|
||||
header->height = swapLittleAndHost32(header->height);
|
||||
header->colorPlanes = swapLittleAndHost16(header->colorPlanes);
|
||||
header->bitsPerPixel = swapLittleAndHost16(header->bitsPerPixel);
|
||||
header->compression = swapLittleAndHost32(header->compression);
|
||||
header->imageSize = swapLittleAndHost32(header->imageSize);
|
||||
header->xRes = swapLittleAndHost32(header->xRes);
|
||||
header->yRes = swapLittleAndHost32(header->yRes);
|
||||
header->colorsUsed = swapLittleAndHost32(header->colorsUsed);
|
||||
header->colorsImportant = swapLittleAndHost32(header->colorsImportant);
|
||||
}
|
||||
|
||||
#elif __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
/* No conversion necessary if we are already little endian. */
|
||||
#define convertBitmapFileHeader(header)
|
||||
#define convertBitmapInfoHeader(header)
|
||||
#endif
|
||||
|
||||
/* Returns newly alloc'd image data from bitmap file. The current position of
|
||||
* the file must be at the start of the image before calling this. */
|
||||
static uint8_t *readImageData(FILE *fp, size_t width, size_t height,
|
||||
uint8_t bytesPerPixel, size_t bytewidth);
|
||||
|
||||
/* Copys image buffer from |bitmap| to |dest| in BGR format. */
|
||||
static void copyBGRDataFromMMBitmap(MMBitmapRef bitmap, uint8_t *dest);
|
||||
|
||||
const char *MMBMPReadErrorString(MMIOError error)
|
||||
{
|
||||
switch (error) {
|
||||
case kBMPAccessError:
|
||||
return "Could not open file";
|
||||
case kBMPInvalidKeyError:
|
||||
return "Not a BMP file";
|
||||
case kBMPUnsupportedHeaderError:
|
||||
return "Unsupported BMP header";
|
||||
case kBMPInvalidColorPanesError:
|
||||
return "Invalid number of color panes in BMP file";
|
||||
case kBMPUnsupportedColorDepthError:
|
||||
return "Unsupported color depth in BMP file";
|
||||
case kBMPUnsupportedCompressionError:
|
||||
return "Unsupported file compression in BMP file";
|
||||
case kBMPInvalidPixelDataError:
|
||||
return "Could not read BMP pixel data";
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
MMBitmapRef newMMBitmapFromBMP(const char *path, MMBMPReadError *err)
|
||||
{
|
||||
FILE *fp;
|
||||
struct BITMAP_FILE_HEADER fileHeader = {0}; /* Initialize elements to 0. */
|
||||
struct BITMAP_INFO_HEADER dibHeader = {0};
|
||||
uint32_t headerSize = 0;
|
||||
uint8_t bytesPerPixel;
|
||||
size_t bytewidth;
|
||||
uint8_t *imageBuf;
|
||||
|
||||
if ((fp = fopen(path, "rb")) == NULL) {
|
||||
if (err != NULL) *err = kBMPAccessError;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Initialize error code to generic value. */
|
||||
if (err != NULL) *err = kBMPGenericError;
|
||||
|
||||
if (fread(&fileHeader, sizeof(fileHeader), 1, fp) == 0) goto bail;
|
||||
|
||||
/* Convert from little-endian if it's not already. */
|
||||
convertBitmapFileHeader(&fileHeader);
|
||||
|
||||
/* First two bytes should always be 0x4D42. */
|
||||
if (fileHeader.magic != BMP_MAGIC) {
|
||||
if (err != NULL) *err = kBMPInvalidKeyError;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
/* Get header size. */
|
||||
if (fread(&headerSize, sizeof(headerSize), 1, fp) == 0) goto bail;
|
||||
headerSize = swapLittleAndHost32(headerSize);
|
||||
|
||||
/* Back up before reading header. */
|
||||
if (fseek(fp, -(long)sizeof(headerSize), SEEK_CUR) < 0) goto bail;
|
||||
|
||||
if (headerSize == 12) { /* OS/2 v1 header */
|
||||
struct BITMAP_CORE_HEADER coreHeader = {0};
|
||||
if (fread(&coreHeader, sizeof(coreHeader), 1, fp) == 0) goto bail;
|
||||
|
||||
dibHeader.width = coreHeader.width;
|
||||
dibHeader.height = coreHeader.height;
|
||||
dibHeader.colorPlanes = coreHeader.colorPlanes;
|
||||
dibHeader.bitsPerPixel = coreHeader.bitsPerPixel;
|
||||
} else if (headerSize == 40 || headerSize == 108 || headerSize == 124) {
|
||||
/* Windows v3/v4/v5 header */
|
||||
/* Read only the common part (v3) and skip over the rest. */
|
||||
if (fread(&dibHeader, sizeof(dibHeader), 1, fp) == 0) goto bail;
|
||||
} else {
|
||||
if (err != NULL) *err = kBMPUnsupportedHeaderError;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
convertBitmapInfoHeader(&dibHeader);
|
||||
|
||||
if (dibHeader.colorPlanes != 1) {
|
||||
if (err != NULL) *err = kBMPInvalidColorPanesError;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
/* Currently only 24-bit and 32-bit are supported. */
|
||||
if (dibHeader.bitsPerPixel != 24 && dibHeader.bitsPerPixel != 32) {
|
||||
if (err != NULL) *err = kBMPUnsupportedColorDepthError;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
if (dibHeader.compression != kBMP_RGB) {
|
||||
if (err != NULL) *err = kBMPUnsupportedCompressionError;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
/* This can happen because we don't fully parse Windows v4/v5 headers. */
|
||||
if (ftell(fp) != (long)fileHeader.imageOffset) {
|
||||
fseek(fp, fileHeader.imageOffset, SEEK_SET);
|
||||
}
|
||||
|
||||
/* Get bytes per row, including padding. */
|
||||
bytesPerPixel = dibHeader.bitsPerPixel / 8;
|
||||
bytewidth = ADD_PADDING(dibHeader.width * bytesPerPixel);
|
||||
|
||||
imageBuf = readImageData(fp, dibHeader.width, abs(dibHeader.height),
|
||||
bytesPerPixel, bytewidth);
|
||||
fclose(fp);
|
||||
|
||||
if (imageBuf == NULL) {
|
||||
if (err != NULL) *err = kBMPInvalidPixelDataError;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* A negative height indicates that the image is flipped.
|
||||
*
|
||||
* We store our bitmaps as "flipped" according to the BMP format; i.e., (0, 0)
|
||||
* is the top left, not bottom left. So we only need to flip the bitmap if
|
||||
* the height is NOT negative. */
|
||||
if (dibHeader.height < 0) {
|
||||
dibHeader.height = -dibHeader.height;
|
||||
} else {
|
||||
flipBitmapData(imageBuf, dibHeader.width, dibHeader.height, bytewidth);
|
||||
}
|
||||
|
||||
return createMMBitmap(imageBuf, dibHeader.width, dibHeader.height,
|
||||
bytewidth, (uint8_t)dibHeader.bitsPerPixel,
|
||||
bytesPerPixel);
|
||||
|
||||
bail:
|
||||
fclose(fp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint8_t *createBitmapData(MMBitmapRef bitmap, size_t *len)
|
||||
{
|
||||
/* BMP files are always aligned to 4 bytes. */
|
||||
const size_t bytewidth = ((bitmap->width * bitmap->bytesPerPixel) + 3) & ~3;
|
||||
|
||||
const size_t imageSize = bytewidth * bitmap->height;
|
||||
struct BITMAP_FILE_HEADER *fileHeader;
|
||||
struct BITMAP_INFO_HEADER *dibHeader;
|
||||
|
||||
/* Should always be 54. */
|
||||
const size_t imageOffset = sizeof(*fileHeader) + sizeof(*dibHeader);
|
||||
uint8_t *data;
|
||||
const size_t dataLen = imageOffset + imageSize;
|
||||
|
||||
data = calloc(1, dataLen);
|
||||
if (data == NULL) return NULL;
|
||||
|
||||
/* Save top header. */
|
||||
fileHeader = (struct BITMAP_FILE_HEADER *)data;
|
||||
fileHeader->magic = BMP_MAGIC;
|
||||
fileHeader->fileSize = (uint32_t)(sizeof(*dibHeader) + imageSize);
|
||||
fileHeader->imageOffset = (uint32_t)imageOffset;
|
||||
|
||||
/* BMP files are always stored as little-endian, so we need to convert back
|
||||
* if necessary. */
|
||||
convertBitmapFileHeader(fileHeader);
|
||||
|
||||
/* Copy Windows v3 header. */
|
||||
dibHeader = (struct BITMAP_INFO_HEADER *)(data + sizeof(*fileHeader));
|
||||
dibHeader->headerSize = sizeof(*dibHeader); /* Should always be 40. */
|
||||
dibHeader->width = (int32_t)bitmap->width;
|
||||
dibHeader->height = -(int32_t)bitmap->height; /* Our bitmaps are "flipped". */
|
||||
dibHeader->colorPlanes = 1;
|
||||
dibHeader->bitsPerPixel = bitmap->bitsPerPixel;
|
||||
dibHeader->compression = kBMP_RGB; /* Don't save with compression. */
|
||||
dibHeader->imageSize = (uint32_t)imageSize;
|
||||
|
||||
convertBitmapInfoHeader(dibHeader);
|
||||
|
||||
/* Lastly, copy the pixel data. */
|
||||
copyBGRDataFromMMBitmap(bitmap, data + imageOffset);
|
||||
|
||||
if (len != NULL) *len = dataLen;
|
||||
return data;
|
||||
}
|
||||
|
||||
int saveMMBitmapAsBMP(MMBitmapRef bitmap, const char *path)
|
||||
{
|
||||
FILE *fp;
|
||||
size_t dataLen;
|
||||
uint8_t *data;
|
||||
|
||||
if ((fp = fopen(path, "wb")) == NULL) return -1;
|
||||
|
||||
if ((data = createBitmapData(bitmap, &dataLen)) == NULL) {
|
||||
fclose(fp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fwrite(data, dataLen, 1, fp) == 0) {
|
||||
free(data);
|
||||
fclose(fp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
free(data);
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint8_t *readImageData(FILE *fp, size_t width, size_t height,
|
||||
uint8_t bytesPerPixel, size_t bytewidth)
|
||||
{
|
||||
size_t imageSize = bytewidth * height;
|
||||
uint8_t *imageBuf = calloc(1, imageSize);
|
||||
|
||||
if (MMRGB_IS_BGR && (bytewidth % 4) == 0) { /* No conversion needed. */
|
||||
if (fread(imageBuf, imageSize, 1, fp) == 0) {
|
||||
free(imageBuf);
|
||||
return NULL;
|
||||
}
|
||||
} else { /* Convert from BGR with 4-byte alignment. */
|
||||
uint8_t *row = malloc(bytewidth);
|
||||
size_t y;
|
||||
const size_t bmp_bytewidth = (width * bytesPerPixel + 3) & ~3;
|
||||
|
||||
if (row == NULL) return NULL;
|
||||
assert(bmp_bytewidth <= bytewidth);
|
||||
|
||||
/* Read image data row by row. */
|
||||
for (y = 0; y < height; ++y) {
|
||||
const size_t rowOffset = y * bytewidth;
|
||||
size_t x;
|
||||
uint8_t *rowptr = row;
|
||||
if (fread(row, bmp_bytewidth, 1, fp) == 0) {
|
||||
free(imageBuf);
|
||||
free(row);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (x = 0; x < width; ++x) {
|
||||
const size_t colOffset = x * bytesPerPixel;
|
||||
MMRGBColor *color = (MMRGBColor *)(imageBuf +
|
||||
rowOffset + colOffset);
|
||||
|
||||
/* BMP files are stored in BGR format. */
|
||||
color->blue = rowptr[0];
|
||||
color->green = rowptr[1];
|
||||
color->red = rowptr[2];
|
||||
rowptr += bytesPerPixel;
|
||||
}
|
||||
}
|
||||
|
||||
free(row);
|
||||
}
|
||||
|
||||
return imageBuf;
|
||||
}
|
||||
|
||||
static void copyBGRDataFromMMBitmap(MMBitmapRef bitmap, uint8_t *dest)
|
||||
{
|
||||
if (MMRGB_IS_BGR && (bitmap->bytewidth % 4) == 0) { /* No conversion needed. */
|
||||
memcpy(dest, bitmap->imageBuffer, bitmap->bytewidth * bitmap->height);
|
||||
} else { /* Convert to RGB with other-than-4-byte alignment. */
|
||||
const size_t bytewidth = (bitmap->width * bitmap->bytesPerPixel + 3) & ~3;
|
||||
size_t y;
|
||||
|
||||
/* Copy image data row by row. */
|
||||
for (y = 0; y < bitmap->height; ++y) {
|
||||
uint8_t *rowptr = dest + (y * bytewidth);
|
||||
size_t x;
|
||||
for (x = 0; x < bitmap->width; ++x) {
|
||||
MMRGBColor *color = MMRGBColorRefAtPoint(bitmap, x, y);
|
||||
|
||||
/* BMP files are stored in BGR format. */
|
||||
rowptr[0] = color->blue;
|
||||
rowptr[1] = color->green;
|
||||
rowptr[2] = color->red;
|
||||
|
||||
rowptr += bitmap->bytesPerPixel;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Perform an in-place swap from Quadrant 1 to Quadrant III format (upside-down
|
||||
* PostScript/GL to right side up QD/CG raster format) We do this in-place,
|
||||
* which requires more copying, but will touch only half the pages.
|
||||
*
|
||||
* This is blatantly copied from Apple's glGrab example code. */
|
||||
void flipBitmapData(void *data, size_t width, size_t height, size_t bytewidth)
|
||||
{
|
||||
size_t top, bottom;
|
||||
void *topP;
|
||||
void *bottomP;
|
||||
void *tempbuf;
|
||||
|
||||
if (height <= 1) return; /* No flipping necessary if height is <= 1. */
|
||||
|
||||
top = 0;
|
||||
bottom = height - 1;
|
||||
tempbuf = malloc(bytewidth);
|
||||
if (tempbuf == NULL) return;
|
||||
|
||||
while (top < bottom) {
|
||||
topP = (void *)((top * bytewidth) + (intptr_t)data);
|
||||
bottomP = (void *)((bottom * bytewidth) + (intptr_t)data);
|
||||
|
||||
/* Save and swap scanlines.
|
||||
* Does a simple in-place exchange with a temp buffer. */
|
||||
memcpy(tempbuf, topP, bytewidth);
|
||||
memcpy(topP, bottomP, bytewidth);
|
||||
memcpy(bottomP, tempbuf, bytewidth);
|
||||
|
||||
++top;
|
||||
--bottom;
|
||||
}
|
||||
free(tempbuf);
|
||||
}
|
35
base/deadbeef_rand.h
Normal file
35
base/deadbeef_rand.h
Normal file
@ -0,0 +1,35 @@
|
||||
#ifndef DEADBEEF_RAND_H
|
||||
#define DEADBEEF_RAND_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define DEADBEEF_MAX UINT32_MAX
|
||||
|
||||
/* Dead Beef Random Number Generator
|
||||
* From: http://inglorion.net/software/deadbeef_rand
|
||||
* A fast, portable psuedo-random number generator by BJ Amsterdam Zuidoost.
|
||||
* Stated in license terms: "Feel free to use the code in your own software." */
|
||||
|
||||
/* Generates a random number between 0 and DEADBEEF_MAX. */
|
||||
uint32_t deadbeef_rand(void);
|
||||
|
||||
/* Seeds with the given integer. */
|
||||
void deadbeef_srand(uint32_t x);
|
||||
|
||||
/* Generates seed from the current time. */
|
||||
uint32_t deadbeef_generate_seed(void);
|
||||
|
||||
/* Seeds with the above function. */
|
||||
#define deadbeef_srand_time() deadbeef_srand(deadbeef_generate_seed())
|
||||
|
||||
/* Returns random double in the range [a, b).
|
||||
* Taken directly from the rand() man page. */
|
||||
#define DEADBEEF_UNIFORM(a, b) \
|
||||
((a) + (deadbeef_rand() / (((double)DEADBEEF_MAX / (b - a) + 1))))
|
||||
|
||||
/* Returns random integer in the range [a, b).
|
||||
* Also taken from the rand() man page. */
|
||||
#define DEADBEEF_RANDRANGE(a, b) \
|
||||
(uint32_t)DEADBEEF_UNIFORM(a, b)
|
||||
|
||||
#endif /* DEADBEEF_RAND_H */
|
27
base/deadbeef_rand_init.h
Normal file
27
base/deadbeef_rand_init.h
Normal file
@ -0,0 +1,27 @@
|
||||
#include "deadbeef_rand.h"
|
||||
#include <time.h>
|
||||
|
||||
static uint32_t deadbeef_seed;
|
||||
static uint32_t deadbeef_beef = 0xdeadbeef;
|
||||
|
||||
uint32_t deadbeef_rand(void)
|
||||
{
|
||||
deadbeef_seed = (deadbeef_seed << 7) ^ ((deadbeef_seed >> 25) + deadbeef_beef);
|
||||
deadbeef_beef = (deadbeef_beef << 7) ^ ((deadbeef_beef >> 25) + 0xdeadbeef);
|
||||
return deadbeef_seed;
|
||||
}
|
||||
|
||||
void deadbeef_srand(uint32_t x)
|
||||
{
|
||||
deadbeef_seed = x;
|
||||
deadbeef_beef = 0xdeadbeef;
|
||||
}
|
||||
|
||||
/* Taken directly from the documentation:
|
||||
* http://inglorion.net/software/cstuff/deadbeef_rand/ */
|
||||
uint32_t deadbeef_generate_seed(void)
|
||||
{
|
||||
uint32_t t = (uint32_t)time(NULL);
|
||||
uint32_t c = (uint32_t)clock();
|
||||
return (t << 24) ^ (c << 11) ^ t ^ (size_t) &c;
|
||||
}
|
123
base/endian.h
Normal file
123
base/endian.h
Normal file
@ -0,0 +1,123 @@
|
||||
#pragma once
|
||||
#ifndef ENDIAN_H
|
||||
#define ENDIAN_H
|
||||
|
||||
#include "os.h"
|
||||
|
||||
/*
|
||||
* (Mostly) cross-platform endian definitions and bit swapping macros.
|
||||
* Unfortunately, there is no standard C header for this, so we just
|
||||
* include the most common ones and fallback to our own custom macros.
|
||||
*/
|
||||
|
||||
#if defined(__linux__) /* Linux */
|
||||
#include <endian.h>
|
||||
#include <byteswap.h>
|
||||
#elif (defined(__FreeBSD__) && __FreeBSD_version >= 470000) || \
|
||||
defined(__OpenBSD__) || defined(__NetBSD__) /* (Free|Open|Net)BSD */
|
||||
#include <sys/endian.h>
|
||||
#define __BIG_ENDIAN BIG_ENDIAN
|
||||
#define __LITTLE_ENDIAN LITTLE_ENDIAN
|
||||
#define __BYTE_ORDER BYTE_ORDER
|
||||
#elif defined(IS_MACOSX) || (defined(BSD) && (BSD >= 199103)) /* Other BSD */
|
||||
#include <machine/endian.h>
|
||||
#define __BIG_ENDIAN BIG_ENDIAN
|
||||
#define __LITTLE_ENDIAN LITTLE_ENDIAN
|
||||
#define __BYTE_ORDER BYTE_ORDER
|
||||
#elif defined(IS_WINDOWS) /* Windows is assumed to be little endian only. */
|
||||
#define __BIG_ENDIAN 4321
|
||||
#define __LITTLE_ENDIAN 1234
|
||||
#define __BYTE_ORDER __LITTLE_ENDIAN
|
||||
#endif
|
||||
|
||||
/* Fallback to custom constants. */
|
||||
#if !defined(__BIG_ENDIAN)
|
||||
#define __BIG_ENDIAN 4321
|
||||
#endif
|
||||
|
||||
#if !defined(__LITTLE_ENDIAN)
|
||||
#define __LITTLE_ENDIAN 1234
|
||||
#endif
|
||||
|
||||
/* Prefer compiler flag settings if given. */
|
||||
#if defined(MM_BIG_ENDIAN)
|
||||
#undef __BYTE_ORDER /* Avoid redefined macro compiler warning. */
|
||||
#define __BYTE_ORDER __BIG_ENDIAN
|
||||
#elif defined(MM_LITTLE_ENDIAN)
|
||||
#undef __BYTE_ORDER /* Avoid redefined macro compiler warning. */
|
||||
#define __BYTE_ORDER __LITTLE_ENDIAN
|
||||
#endif
|
||||
|
||||
/* Define default endian-ness. */
|
||||
#ifndef __LITTLE_ENDIAN
|
||||
#define __LITTLE_ENDIAN 1234
|
||||
#endif /* __LITTLE_ENDIAN */
|
||||
|
||||
#ifndef __BIG_ENDIAN
|
||||
#define __BIG_ENDIAN 4321
|
||||
#endif /* __BIG_ENDIAN */
|
||||
|
||||
#ifndef __BYTE_ORDER
|
||||
#warning "Byte order not defined on your system; assuming little endian"
|
||||
#define __BYTE_ORDER __LITTLE_ENDIAN
|
||||
#endif /* __BYTE_ORDER */
|
||||
|
||||
#if __BYTE_ORDER != __BIG_ENDIAN && __BYTE_ORDER != __LITTLE_ENDIAN
|
||||
#error "__BYTE_ORDER set to unknown byte order"
|
||||
#endif
|
||||
|
||||
#if defined(IS_MACOSX)
|
||||
#include <libkern/OSByteOrder.h>
|
||||
|
||||
/* OS X system functions. */
|
||||
#define bitswap16(i) OSSwapInt16(i)
|
||||
#define bitswap32(i) OSSwapInt32(i)
|
||||
#define swapLittleAndHost32(i) OSSwapLittleToHostInt32(i)
|
||||
#define swapLittleAndHost16(i) OSSwapLittleToHostInt16(i)
|
||||
#else
|
||||
#ifndef bitswap16
|
||||
#if defined(bswap16)
|
||||
#define bitswap16(i) bswap16(i) /* FreeBSD system function */
|
||||
#elif defined(bswap_16)
|
||||
#define bitswap16(i) bswap_16(i) /* Linux system function */
|
||||
#else /* Default macro */
|
||||
#define bitswap16(i) (((uint16_t)(i) & 0xFF00) >> 8) | \
|
||||
(((uint16_t)(i) & 0x00FF) << 8)
|
||||
#endif
|
||||
#endif /* bitswap16 */
|
||||
|
||||
#ifndef bitswap32
|
||||
#if defined(bswap32)
|
||||
#define bitswap32(i) bswap32(i) /* FreeBSD system function. */
|
||||
#elif defined(bswap_32)
|
||||
#define bitswap32(i) bswap_32(i) /* Linux system function. */
|
||||
#else /* Default macro */
|
||||
#define bitswap32(i) (((uint32_t)(i) & 0xFF000000) >> 24) | \
|
||||
((uint32_t)((i) & 0x00FF0000) >> 8) | \
|
||||
((uint32_t)((i) & 0x0000FF00) << 8) | \
|
||||
((uint32_t)((i) & 0x000000FF) << 24)
|
||||
#endif
|
||||
#endif /* bitswap32 */
|
||||
#endif
|
||||
|
||||
#if __BYTE_ORDER == __BIG_ENDIAN
|
||||
/* Little endian to/from host byte order (big endian). */
|
||||
#ifndef swapLittleAndHost16
|
||||
#define swapLittleAndHost16(i) bitswap16(i)
|
||||
#endif /* swapLittleAndHost16 */
|
||||
|
||||
#ifndef swapLittleAndHost32
|
||||
#define swapLittleAndHost32(i) bitswap32(i)
|
||||
#endif /* swapLittleAndHost32 */
|
||||
#elif __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
/* We are already little endian, so no conversion is needed. */
|
||||
#ifndef swapLittleAndHost16
|
||||
#define swapLittleAndHost16(i) i
|
||||
#endif /* swapLittleAndHost16 */
|
||||
|
||||
#ifndef swapLittleAndHost32
|
||||
#define swapLittleAndHost32(i) i
|
||||
#endif /* swapLittleAndHost32 */
|
||||
#endif
|
||||
|
||||
#endif /* ENDIAN_H */
|
15
base/inline_keywords.h
Normal file
15
base/inline_keywords.h
Normal file
@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
/* A complicated, portable model for declaring inline functions in
|
||||
* header files. */
|
||||
#if !defined(H_INLINE)
|
||||
#if defined(__GNUC__)
|
||||
#define H_INLINE static __inline__ __attribute__((always_inline))
|
||||
#elif defined(__MWERKS__) || defined(__cplusplus)
|
||||
#define H_INLINE static inline
|
||||
#elif defined(_MSC_VER)
|
||||
#define H_INLINE static __inline
|
||||
#elif TARGET_OS_WIN32
|
||||
#define H_INLINE static __inline__
|
||||
#endif
|
||||
#endif /* H_INLINE */
|
79
base/io.c
Normal file
79
base/io.c
Normal file
@ -0,0 +1,79 @@
|
||||
#include "io.h"
|
||||
#include "os.h"
|
||||
#include "bmp_io.h"
|
||||
#include "png_io.h"
|
||||
#include <stdio.h> /* For fputs() */
|
||||
#include <string.h> /* For strcmp() */
|
||||
#include <ctype.h> /* For tolower() */
|
||||
|
||||
const char *getExtension(const char *fname, size_t len)
|
||||
{
|
||||
if (fname == NULL || len <= 0) return NULL;
|
||||
|
||||
while (--len > 0 && fname[len] != '.' && fname[len] != '\0')
|
||||
;
|
||||
|
||||
return fname + len + 1;
|
||||
}
|
||||
|
||||
MMImageType imageTypeFromExtension(const char *extension)
|
||||
{
|
||||
char ext[4];
|
||||
const size_t maxlen = sizeof(ext) / sizeof(ext[0]);
|
||||
size_t i;
|
||||
|
||||
for (i = 0; extension[i] != '\0'; ++i) {
|
||||
if (i >= maxlen) return kInvalidImageType;
|
||||
ext[i] = tolower(extension[i]);
|
||||
}
|
||||
ext[i] = '\0';
|
||||
|
||||
if (strcmp(ext, "png") == 0) {
|
||||
return kPNGImageType;
|
||||
} else if (strcmp(ext, "bmp") == 0) {
|
||||
return kBMPImageType;
|
||||
} else {
|
||||
return kInvalidImageType;
|
||||
}
|
||||
}
|
||||
|
||||
MMBitmapRef newMMBitmapFromFile(const char *path,
|
||||
MMImageType type,
|
||||
MMIOError *err)
|
||||
{
|
||||
switch (type) {
|
||||
case kBMPImageType:
|
||||
return newMMBitmapFromBMP(path, err);
|
||||
case kPNGImageType:
|
||||
return newMMBitmapFromPNG(path, err);
|
||||
default:
|
||||
if (err != NULL) *err = kMMIOUnsupportedTypeError;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int saveMMBitmapToFile(MMBitmapRef bitmap,
|
||||
const char *path,
|
||||
MMImageType type)
|
||||
{
|
||||
switch (type) {
|
||||
case kBMPImageType:
|
||||
return saveMMBitmapAsBMP(bitmap, path);
|
||||
case kPNGImageType:
|
||||
return saveMMBitmapAsPNG(bitmap, path);
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
const char *MMIOErrorString(MMImageType type, MMIOError error)
|
||||
{
|
||||
switch (type) {
|
||||
case kBMPImageType:
|
||||
return MMBMPReadErrorString(error);
|
||||
case kPNGImageType:
|
||||
return MMPNGReadErrorString(error);
|
||||
default:
|
||||
return "Unsupported image type";
|
||||
}
|
||||
}
|
46
base/io.h
Normal file
46
base/io.h
Normal file
@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
#ifndef IO_H
|
||||
#define IO_H
|
||||
|
||||
#include "MMBitmap.h"
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
enum _MMImageType {
|
||||
kInvalidImageType = 0,
|
||||
kPNGImageType,
|
||||
kBMPImageType /* Currently only PNG and BMP are supported. */
|
||||
};
|
||||
|
||||
typedef uint16_t MMImageType;
|
||||
|
||||
enum _MMIOError {
|
||||
kMMIOUnsupportedTypeError = 0
|
||||
};
|
||||
|
||||
typedef uint16_t MMIOError;
|
||||
|
||||
const char *getExtension(const char *fname, size_t len);
|
||||
|
||||
/* Returns best guess at the MMImageType based on a file extension, or
|
||||
* |kInvalidImageType| if no matching type was found. */
|
||||
MMImageType imageTypeFromExtension(const char *ext);
|
||||
|
||||
/* Attempts to parse the file of the given type at the given path.
|
||||
* |filepath| is an ASCII string describing the absolute POSIX path.
|
||||
* Returns new bitmap (to be destroy()'d by caller) on success, NULL on error.
|
||||
* If |error| is non-NULL, it will be set to the error code on return.
|
||||
*/
|
||||
MMBitmapRef newMMBitmapFromFile(const char *path, MMImageType type, MMIOError *err);
|
||||
|
||||
/* Saves |bitmap| to a file of the given type at the given path.
|
||||
* |filepath| is an ASCII string describing the absolute POSIX path.
|
||||
* Returns 0 on success, -1 on error. */
|
||||
int saveMMBitmapToFile(MMBitmapRef bitmap, const char *path, MMImageType type);
|
||||
|
||||
/* Returns description of given error code.
|
||||
* Returned string is constant and hence should not be freed. */
|
||||
const char *MMIOErrorString(MMImageType type, MMIOError error);
|
||||
|
||||
#endif /* IO_H */
|
40
base/microsleep.h
Normal file
40
base/microsleep.h
Normal file
@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
#ifndef MICROSLEEP_H
|
||||
#define MICROSLEEP_H
|
||||
|
||||
#include "os.h"
|
||||
#include "inline_keywords.h"
|
||||
|
||||
#if !defined(IS_WINDOWS)
|
||||
/* Make sure nanosleep gets defined even when using C89. */
|
||||
#if !defined(__USE_POSIX199309) || !__USE_POSIX199309
|
||||
#define __USE_POSIX199309 1
|
||||
#endif
|
||||
|
||||
#include <time.h> /* For nanosleep() */
|
||||
#endif
|
||||
|
||||
/*
|
||||
* A more widely supported alternative to usleep(), based on Sleep() in Windows
|
||||
* and nanosleep() everywhere else.
|
||||
*
|
||||
* Pauses execution for the given amount of milliseconds.
|
||||
*/
|
||||
H_INLINE void microsleep(double milliseconds)
|
||||
{
|
||||
#if defined(IS_WINDOWS)
|
||||
Sleep((DWORD)milliseconds); /* (Unfortunately truncated to a 32-bit integer.) */
|
||||
#else
|
||||
/* Technically, nanosleep() is not an ANSI function, but it is the most
|
||||
* supported precise sleeping function I can find.
|
||||
*
|
||||
* If it is really necessary, it may be possible to emulate this with some
|
||||
* hack using select() in the future if we really have to. */
|
||||
struct timespec sleepytime;
|
||||
sleepytime.tv_sec = milliseconds / 1000;
|
||||
sleepytime.tv_nsec = (milliseconds - (sleepytime.tv_sec * 1000)) * 1000000;
|
||||
nanosleep(&sleepytime, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* MICROSLEEP_H */
|
27
base/ms_stdbool.h
Normal file
27
base/ms_stdbool.h
Normal file
@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
#if !defined(MS_STDBOOL_H) && \
|
||||
(!defined(__bool_true_false_are_defined) || __bool_true_false_are_defined)
|
||||
#define MS_STDBOOL_H
|
||||
|
||||
#ifndef _MSC_VER
|
||||
#error "Use this header only with Microsoft Visual C++ compilers!"
|
||||
#endif /* _MSC_VER */
|
||||
|
||||
#define __bool_true_false_are_defined 1
|
||||
|
||||
#ifndef __cplusplus
|
||||
|
||||
#if defined(true) || defined(false) || defined(bool)
|
||||
#error "Boolean type already defined"
|
||||
#endif
|
||||
|
||||
enum {
|
||||
false = 0,
|
||||
true = 1
|
||||
};
|
||||
|
||||
typedef unsigned char bool;
|
||||
|
||||
#endif /* !__cplusplus */
|
||||
|
||||
#endif /* MS_STDBOOL_H */
|
224
base/ms_stdint.h
Normal file
224
base/ms_stdint.h
Normal file
@ -0,0 +1,224 @@
|
||||
/* ISO C9x compliant stdint.h for Microsoft Visual Studio
|
||||
* Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124
|
||||
*
|
||||
* Copyright (c) 2006-2008 Alexander Chemeris
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. The name of the author may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _MSC_VER
|
||||
#error "Use this header only with Microsoft Visual C++ compilers!"
|
||||
#endif /* _MSC_VER */
|
||||
|
||||
#ifndef MSC_STDINT_H
|
||||
#define MSC_STDINT_H
|
||||
|
||||
#if _MSC_VER > 1000
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
/* For Visual Studio 6 in C++ mode and for many Visual Studio versions when
|
||||
* compiling for ARM we should wrap <wchar.h> include with 'extern "C++" {}'
|
||||
* or compiler give many errors like this: */
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
#include <wchar.h>
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/* Define _W64 macros to mark types changing their size, like intptr_t. */
|
||||
#ifndef _W64
|
||||
#if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300
|
||||
#define _W64 __w64
|
||||
#else
|
||||
#define _W64
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
/* 7.18.1 Integer types */
|
||||
|
||||
/* 7.18.1.1 Exact-width integer types */
|
||||
|
||||
/* Visual Studio 6 and Embedded Visual C++ 4 doesn't
|
||||
* realize that, e.g. char has the same size as __int8
|
||||
* so we give up on __intX for them. */
|
||||
#if _MSC_VER < 1300
|
||||
typedef signed char int8_t;
|
||||
typedef signed short int16_t;
|
||||
typedef signed int int32_t;
|
||||
typedef unsigned char uint8_t;
|
||||
typedef unsigned short uint16_t;
|
||||
typedef unsigned int uint32_t;
|
||||
#else
|
||||
typedef signed __int8 int8_t;
|
||||
typedef signed __int16 int16_t;
|
||||
typedef signed __int32 int32_t;
|
||||
typedef unsigned __int8 uint8_t;
|
||||
typedef unsigned __int16 uint16_t;
|
||||
typedef unsigned __int32 uint32_t;
|
||||
#endif
|
||||
typedef signed __int64 int64_t;
|
||||
typedef unsigned __int64 uint64_t;
|
||||
|
||||
/* 7.18.1.2 Minimum-width integer types */
|
||||
typedef int8_t int_least8_t;
|
||||
typedef int16_t int_least16_t;
|
||||
typedef int32_t int_least32_t;
|
||||
typedef int64_t int_least64_t;
|
||||
typedef uint8_t uint_least8_t;
|
||||
typedef uint16_t uint_least16_t;
|
||||
typedef uint32_t uint_least32_t;
|
||||
typedef uint64_t uint_least64_t;
|
||||
|
||||
/* 7.18.1.3 Fastest minimum-width integer types */
|
||||
typedef int8_t int_fast8_t;
|
||||
typedef int16_t int_fast16_t;
|
||||
typedef int32_t int_fast32_t;
|
||||
typedef int64_t int_fast64_t;
|
||||
typedef uint8_t uint_fast8_t;
|
||||
typedef uint16_t uint_fast16_t;
|
||||
typedef uint32_t uint_fast32_t;
|
||||
typedef uint64_t uint_fast64_t;
|
||||
|
||||
/* 7.18.1.4 Integer types capable of holding object pointers */
|
||||
#if defined(_WIN64)
|
||||
typedef signed __int64 intptr_t;
|
||||
typedef unsigned __int64 uintptr_t;
|
||||
#else
|
||||
typedef _W64 signed int intptr_t;
|
||||
typedef _W64 unsigned int uintptr_t;
|
||||
#endif /* _WIN64 ] */
|
||||
|
||||
/* 7.18.1.5 Greatest-width integer types */
|
||||
typedef int64_t intmax_t;
|
||||
typedef uint64_t uintmax_t;
|
||||
|
||||
/* 7.18.2 Limits of specified-width integer types */
|
||||
|
||||
/* See footnote 220 at page 257 and footnote 221 at page 259 */
|
||||
#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS)
|
||||
|
||||
/* 7.18.2.1 Limits of exact-width integer types */
|
||||
#define INT8_MIN ((int8_t)_I8_MIN)
|
||||
#define INT8_MAX _I8_MAX
|
||||
#define INT16_MIN ((int16_t)_I16_MIN)
|
||||
#define INT16_MAX _I16_MAX
|
||||
#define INT32_MIN ((int32_t)_I32_MIN)
|
||||
#define INT32_MAX _I32_MAX
|
||||
#define INT64_MIN ((int64_t)_I64_MIN)
|
||||
#define INT64_MAX _I64_MAX
|
||||
#define UINT8_MAX _UI8_MAX
|
||||
#define UINT16_MAX _UI16_MAX
|
||||
#define UINT32_MAX _UI32_MAX
|
||||
#define UINT64_MAX _UI64_MAX
|
||||
|
||||
/* 7.18.2.2 Limits of minimum-width integer types */
|
||||
#define INT_LEAST8_MIN INT8_MIN
|
||||
#define INT_LEAST8_MAX INT8_MAX
|
||||
#define INT_LEAST16_MIN INT16_MIN
|
||||
#define INT_LEAST16_MAX INT16_MAX
|
||||
#define INT_LEAST32_MIN INT32_MIN
|
||||
#define INT_LEAST32_MAX INT32_MAX
|
||||
#define INT_LEAST64_MIN INT64_MIN
|
||||
#define INT_LEAST64_MAX INT64_MAX
|
||||
#define UINT_LEAST8_MAX UINT8_MAX
|
||||
#define UINT_LEAST16_MAX UINT16_MAX
|
||||
#define UINT_LEAST32_MAX UINT32_MAX
|
||||
#define UINT_LEAST64_MAX UINT64_MAX
|
||||
|
||||
/* 7.18.2.4 Limits of integer types capable of holding object pointers */
|
||||
#if defined(_WIN64)
|
||||
#define INTPTR_MIN INT64_MIN
|
||||
#define INTPTR_MAX INT64_MAX
|
||||
#define UINTPTR_MAX UINT64_MAX
|
||||
#else
|
||||
#define INTPTR_MIN INT32_MIN
|
||||
#define INTPTR_MAX INT32_MAX
|
||||
#define UINTPTR_MAX UINT32_MAX
|
||||
#endif
|
||||
|
||||
/* 7.18.3 Limits of other integer types */
|
||||
|
||||
#if defined(_WIN64)
|
||||
#define PTRDIFF_MIN _I64_MIN
|
||||
#define PTRDIFF_MAX _I64_MAX
|
||||
#else
|
||||
#define PTRDIFF_MIN _I32_MIN
|
||||
#define PTRDIFF_MAX _I32_MAX
|
||||
#endif /* _WIN64 */
|
||||
|
||||
#define SIG_ATOMIC_MIN INT_MIN
|
||||
#define SIG_ATOMIC_MAX INT_MAX
|
||||
|
||||
#ifndef SIZE_MAX
|
||||
#if defined(_WIN64)
|
||||
#define SIZE_MAX _UI64_MAX
|
||||
#else
|
||||
#define SIZE_MAX _UI32_MAX
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* WCHAR_MIN and WCHAR_MAX are also defined in <wchar.h> */
|
||||
#ifndef WCHAR_MIN
|
||||
#define WCHAR_MIN 0
|
||||
#endif /* WCHAR_MIN */
|
||||
|
||||
#ifndef WCHAR_MAX
|
||||
#define WCHAR_MAX _UI16_MAX
|
||||
#endif /* WCHAR_MAX */
|
||||
|
||||
#define WINT_MIN 0
|
||||
#define WINT_MAX _UI16_MAX
|
||||
#endif /* __STDC_LIMIT_MACROS */
|
||||
|
||||
|
||||
/* 7.18.4 Limits of other integer types */
|
||||
|
||||
#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) /* See footnote 224 at page 260 */
|
||||
|
||||
/* 7.18.4.1 Macros for minimum-width integer constants */
|
||||
|
||||
#define INT8_C(val) val##i8
|
||||
#define INT16_C(val) val##i16
|
||||
#define INT32_C(val) val##i32
|
||||
#define INT64_C(val) val##i64
|
||||
|
||||
#define UINT8_C(val) val##ui8
|
||||
#define UINT16_C(val) val##ui16
|
||||
#define UINT32_C(val) val##ui32
|
||||
#define UINT64_C(val) val##ui64
|
||||
|
||||
/* 7.18.4.2 Macros for greatest-width integer constants */
|
||||
#define INTMAX_C INT64_C
|
||||
#define UINTMAX_C UINT64_C
|
||||
|
||||
#endif /* __STDC_CONSTANT_MACROS */
|
||||
|
||||
#endif /* MSC_STDINT_H */
|
49
base/os.h
Normal file
49
base/os.h
Normal file
@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
#ifndef OS_H
|
||||
#define OS_H
|
||||
|
||||
/* Python versions under 2.5 don't support this macro, but it's not
|
||||
* terribly difficult to replicate: */
|
||||
#ifndef PyModule_AddIntMacro
|
||||
#define PyModule_AddIntMacro(module, macro) \
|
||||
PyModule_AddIntConstant(module, #macro, macro)
|
||||
#endif /* PyModule_AddIntMacro */
|
||||
|
||||
#if !defined(IS_MACOSX) && defined(__APPLE__) && defined(__MACH__)
|
||||
#define IS_MACOSX
|
||||
#endif /* IS_MACOSX */
|
||||
|
||||
#if !defined(IS_WINDOWS) && (defined(WIN32) || defined(_WIN32) || \
|
||||
defined(__WIN32__) || defined(__WINDOWS__))
|
||||
#define IS_WINDOWS
|
||||
#endif /* IS_WINDOWS */
|
||||
|
||||
#if !defined(USE_X11) && !defined(NUSE_X11) && !defined(IS_MACOSX) && !defined(IS_WINDOWS)
|
||||
#define USE_X11
|
||||
#endif /* USE_X11 */
|
||||
|
||||
#if defined(IS_WINDOWS)
|
||||
#define STRICT /* Require use of exact types. */
|
||||
#define WIN32_LEAN_AND_MEAN 1 /* Speed up compilation. */
|
||||
#include <windows.h>
|
||||
#elif !defined(IS_MACOSX) && !defined(USE_X11)
|
||||
#error "Sorry, this platform isn't supported yet!"
|
||||
#endif
|
||||
|
||||
/* Interval to align by for large buffers (e.g. bitmaps). */
|
||||
/* Must be a power of 2. */
|
||||
#ifndef BYTE_ALIGN
|
||||
#define BYTE_ALIGN 4 /* Bytes to align pixel buffers to. */
|
||||
/* #include <stddef.h> */
|
||||
/* #define BYTE_ALIGN (sizeof(size_t)) */
|
||||
#endif /* BYTE_ALIGN */
|
||||
|
||||
#if BYTE_ALIGN == 0
|
||||
/* No alignment needed. */
|
||||
#define ADD_PADDING(width) (width)
|
||||
#else
|
||||
/* Aligns given width to padding. */
|
||||
#define ADD_PADDING(width) (BYTE_ALIGN + (((width) - 1) & ~(BYTE_ALIGN - 1)))
|
||||
#endif
|
||||
|
||||
#endif /* OS_H */
|
103
base/rgb.h
Normal file
103
base/rgb.h
Normal file
@ -0,0 +1,103 @@
|
||||
#pragma once
|
||||
#ifndef RGB_H
|
||||
#define RGB_H
|
||||
|
||||
#include <stdlib.h> /* For abs() */
|
||||
#include <math.h>
|
||||
#include "inline_keywords.h" /* For H_INLINE */
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
/* RGB colors in MMBitmaps are stored as BGR for convenience in converting
|
||||
* to/from certain formats (mainly OpenGL).
|
||||
*
|
||||
* It is best not to rely on the order (simply use rgb.{blue,green,red} to
|
||||
* access values), but some situations (e.g., glReadPixels) require one to
|
||||
* do so. In that case, check to make sure to use MMRGB_IS_BGR for future
|
||||
* compatibility. */
|
||||
|
||||
/* #define MMRGB_IS_BGR (offsetof(MMRGBColor, red) > offsetof(MMRGBColor, blue)) */
|
||||
#define MMRGB_IS_BGR 1
|
||||
|
||||
struct _MMRGBColor {
|
||||
uint8_t blue;
|
||||
uint8_t green;
|
||||
uint8_t red;
|
||||
};
|
||||
|
||||
typedef struct _MMRGBColor MMRGBColor;
|
||||
|
||||
/* MMRGBHex is a hexadecimal color value, akin to HTML's, in the form 0xRRGGBB
|
||||
* where RR is the red value expressed as hexadecimal, GG is the green value,
|
||||
* and BB is the blue value. */
|
||||
typedef uint32_t MMRGBHex;
|
||||
|
||||
#define MMRGBHEX_MIN 0x000000
|
||||
#define MMRGBHEX_MAX 0xFFFFFF
|
||||
|
||||
/* Converts rgb color to hexadecimal value.
|
||||
* |red|, |green|, and |blue| should each be of the type |uint8_t|, where the
|
||||
* range is 0 - 255. */
|
||||
#define RGB_TO_HEX(red, green, blue) (((red) << 16) | ((green) << 8) | (blue))
|
||||
|
||||
/* Convenience wrapper for MMRGBColors. */
|
||||
H_INLINE MMRGBHex hexFromMMRGB(MMRGBColor rgb)
|
||||
{
|
||||
return RGB_TO_HEX(rgb.red, rgb.green, rgb.blue);
|
||||
}
|
||||
|
||||
#define RED_FROM_HEX(hex) ((hex >> 16) & 0xFF)
|
||||
#define GREEN_FROM_HEX(hex) ((hex >> 8) & 0xFF)
|
||||
#define BLUE_FROM_HEX(hex) (hex & 0xFF)
|
||||
|
||||
/* Converts hexadecimal color to MMRGBColor. */
|
||||
H_INLINE MMRGBColor MMRGBFromHex(MMRGBHex hex)
|
||||
{
|
||||
MMRGBColor color;
|
||||
color.red = RED_FROM_HEX(hex);
|
||||
color.green = GREEN_FROM_HEX(hex);
|
||||
color.blue = BLUE_FROM_HEX(hex);
|
||||
return color;
|
||||
}
|
||||
|
||||
/* Check absolute equality of two RGB colors. */
|
||||
#define MMRGBColorEqualToColor(c1, c2) ((c1).red == (c2).red && \
|
||||
(c1).blue == (c2).blue && \
|
||||
(c1).green == (c2).green)
|
||||
|
||||
/* Returns whether two colors are similar within the given range, |tolerance|.
|
||||
* Tolerance can be in the range 0.0f - 1.0f, where 0 denotes the exact
|
||||
* color and 1 denotes any color. */
|
||||
H_INLINE int MMRGBColorSimilarToColor(MMRGBColor c1, MMRGBColor c2,
|
||||
float tolerance)
|
||||
{
|
||||
/* Speedy case */
|
||||
if (tolerance <= 0.0f) {
|
||||
return MMRGBColorEqualToColor(c1, c2);
|
||||
} else { /* Otherwise, use a Euclidean space to determine similarity */
|
||||
uint8_t d1 = c1.red - c2.red;
|
||||
uint8_t d2 = c1.green - c2.green;
|
||||
uint8_t d3 = c1.blue - c2.blue;
|
||||
return sqrt((double)(d1 * d1) +
|
||||
(d2 * d2) +
|
||||
(d3 * d3)) <= (tolerance * 442.0f);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Identical to MMRGBColorSimilarToColor, only for hex values. */
|
||||
H_INLINE int MMRGBHexSimilarToColor(MMRGBHex h1, MMRGBHex h2, float tolerance)
|
||||
{
|
||||
if (tolerance <= 0.0f) {
|
||||
return h1 == h2;
|
||||
} else {
|
||||
uint8_t d1 = RED_FROM_HEX(h1) - RED_FROM_HEX(h2);
|
||||
uint8_t d2 = GREEN_FROM_HEX(h1) - GREEN_FROM_HEX(h2);
|
||||
uint8_t d3 = BLUE_FROM_HEX(h1) - BLUE_FROM_HEX(h2);
|
||||
return sqrt((double)(d1 * d1) +
|
||||
(d2 * d2) +
|
||||
(d3 * d3)) <= (tolerance * 442.0f);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* RGB_H */
|
69
base/types.h
Normal file
69
base/types.h
Normal file
@ -0,0 +1,69 @@
|
||||
#pragma once
|
||||
#ifndef TYPES_H
|
||||
#define TYPES_H
|
||||
|
||||
#include "os.h"
|
||||
#include "inline_keywords.h" /* For H_INLINE */
|
||||
#include <stddef.h>
|
||||
|
||||
/* Some generic, cross-platform types. */
|
||||
|
||||
struct _MMPoint {
|
||||
size_t x;
|
||||
size_t y;
|
||||
};
|
||||
|
||||
typedef struct _MMPoint MMPoint;
|
||||
|
||||
struct _MMSize {
|
||||
size_t width;
|
||||
size_t height;
|
||||
};
|
||||
|
||||
typedef struct _MMSize MMSize;
|
||||
|
||||
struct _MMRect {
|
||||
MMPoint origin;
|
||||
MMSize size;
|
||||
};
|
||||
|
||||
typedef struct _MMRect MMRect;
|
||||
|
||||
H_INLINE MMPoint MMPointMake(size_t x, size_t y)
|
||||
{
|
||||
MMPoint point;
|
||||
point.x = x;
|
||||
point.y = y;
|
||||
return point;
|
||||
}
|
||||
|
||||
H_INLINE MMSize MMSizeMake(size_t width, size_t height)
|
||||
{
|
||||
MMSize size;
|
||||
size.width = width;
|
||||
size.height = height;
|
||||
return size;
|
||||
}
|
||||
|
||||
H_INLINE MMRect MMRectMake(size_t x, size_t y, size_t width, size_t height)
|
||||
{
|
||||
MMRect rect;
|
||||
rect.origin = MMPointMake(x, y);
|
||||
rect.size = MMSizeMake(width, height);
|
||||
return rect;
|
||||
}
|
||||
|
||||
#define MMPointZero MMPointMake(0, 0)
|
||||
|
||||
#if defined(IS_MACOSX)
|
||||
|
||||
#define CGPointFromMMPoint(p) CGPointMake((CGFloat)(p).x, (CGFloat)(p).y)
|
||||
#define MMPointFromCGPoint(p) MMPointMake((size_t)(p).x, (size_t)(p).y)
|
||||
|
||||
#elif defined(IS_WINDOWS)
|
||||
|
||||
#define MMPointFromPOINT(p) MMPointMake((size_t)p.x, (size_t)p.y)
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* TYPES_H */
|
929
base/uthash.h
Normal file
929
base/uthash.h
Normal file
@ -0,0 +1,929 @@
|
||||
/*
|
||||
* Copyright (c) 2003-2009, Troy D. Hanson http://uthash.sourceforge.net
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#ifndef UTHASH_H
|
||||
#define UTHASH_H
|
||||
|
||||
#include <string.h> /* memcmp, strlen */
|
||||
#include <stddef.h> /* ptrdiff_t */
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
#define UTHASH_VERSION 1.8
|
||||
|
||||
/* C++ requires extra stringent casting */
|
||||
#if defined __cplusplus
|
||||
#define TYPEOF(x) (typeof(x))
|
||||
#else
|
||||
#define TYPEOF(x)
|
||||
#endif
|
||||
|
||||
|
||||
#define uthash_fatal(msg) exit(-1) /* fatal error (out of memory,etc) */
|
||||
#define uthash_malloc(sz) malloc(sz) /* malloc fcn */
|
||||
#define uthash_free(ptr) free(ptr) /* free fcn */
|
||||
|
||||
#define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */
|
||||
#define uthash_expand_fyi(tbl) /* can be defined to log expands */
|
||||
|
||||
/* initial number of buckets */
|
||||
#define HASH_INITIAL_NUM_BUCKETS 32 /* initial number of buckets */
|
||||
#define HASH_INITIAL_NUM_BUCKETS_LOG2 5 /* lg2 of initial number of buckets */
|
||||
#define HASH_BKT_CAPACITY_THRESH 10 /* expand when bucket count reaches */
|
||||
|
||||
/* calculate the element whose hash handle address is hhe */
|
||||
#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)hhp) - (tbl)->hho))
|
||||
|
||||
#define HASH_FIND(hh,head,keyptr,keylen,out) \
|
||||
do { \
|
||||
unsigned _hf_bkt,_hf_hashv; \
|
||||
out=TYPEOF(out)NULL; \
|
||||
if (head) { \
|
||||
HASH_FCN(keyptr,keylen, (head)->hh.tbl->num_buckets, _hf_hashv, _hf_bkt); \
|
||||
if (HASH_BLOOM_TEST((head)->hh.tbl, _hf_hashv)) { \
|
||||
HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], \
|
||||
keyptr,keylen,out); \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#if defined(HASH_BLOOM)
|
||||
#define HASH_BLOOM_BITLEN (1ULL << HASH_BLOOM)
|
||||
#define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8) + ((HASH_BLOOM_BITLEN%8) ? 1:0)
|
||||
#define HASH_BLOOM_MAKE(tbl) \
|
||||
do { \
|
||||
(tbl)->bloom_nbits = HASH_BLOOM; \
|
||||
(tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN); \
|
||||
if (!((tbl)->bloom_bv)) { uthash_fatal( "out of memory"); } \
|
||||
memset((tbl)->bloom_bv, 0, HASH_BLOOM_BYTELEN); \
|
||||
(tbl)->bloom_sig = HASH_BLOOM_SIGNATURE; \
|
||||
} while (0);
|
||||
|
||||
#define HASH_BLOOM_FREE(tbl) \
|
||||
do { \
|
||||
uthash_free((tbl)->bloom_bv); \
|
||||
} while (0);
|
||||
|
||||
#define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8] |= (1U << ((idx)%8)))
|
||||
#define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8] & (1U << ((idx)%8)))
|
||||
|
||||
#define HASH_BLOOM_ADD(tbl,hashv) \
|
||||
HASH_BLOOM_BITSET((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1)))
|
||||
|
||||
#define HASH_BLOOM_TEST(tbl,hashv) \
|
||||
HASH_BLOOM_BITTEST((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1)))
|
||||
|
||||
#else
|
||||
#define HASH_BLOOM_MAKE(tbl)
|
||||
#define HASH_BLOOM_FREE(tbl)
|
||||
#define HASH_BLOOM_ADD(tbl,hashv)
|
||||
#define HASH_BLOOM_TEST(tbl,hashv) (1)
|
||||
#endif
|
||||
|
||||
#define HASH_MAKE_TABLE(hh,head) \
|
||||
do { \
|
||||
(head)->hh.tbl = (UT_hash_table*)uthash_malloc( \
|
||||
sizeof(UT_hash_table)); \
|
||||
if (!((head)->hh.tbl)) { uthash_fatal( "out of memory"); } \
|
||||
memset((head)->hh.tbl, 0, sizeof(UT_hash_table)); \
|
||||
(head)->hh.tbl->tail = &((head)->hh); \
|
||||
(head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \
|
||||
(head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \
|
||||
(head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \
|
||||
(head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc( \
|
||||
HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \
|
||||
if (! (head)->hh.tbl->buckets) { uthash_fatal( "out of memory"); } \
|
||||
memset((head)->hh.tbl->buckets, 0, \
|
||||
HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \
|
||||
HASH_BLOOM_MAKE((head)->hh.tbl); \
|
||||
(head)->hh.tbl->signature = HASH_SIGNATURE; \
|
||||
} while(0)
|
||||
|
||||
#define HASH_ADD(hh,head,fieldname,keylen_in,add) \
|
||||
HASH_ADD_KEYPTR(hh,head,&add->fieldname,keylen_in,add)
|
||||
|
||||
#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \
|
||||
do { \
|
||||
unsigned _ha_bkt; \
|
||||
(add)->hh.next = NULL; \
|
||||
(add)->hh.key = (char*)keyptr; \
|
||||
(add)->hh.keylen = keylen_in; \
|
||||
if (!(head)) { \
|
||||
head = (add); \
|
||||
(head)->hh.prev = NULL; \
|
||||
HASH_MAKE_TABLE(hh,head); \
|
||||
} else { \
|
||||
(head)->hh.tbl->tail->next = (add); \
|
||||
(add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \
|
||||
(head)->hh.tbl->tail = &((add)->hh); \
|
||||
} \
|
||||
(head)->hh.tbl->num_items++; \
|
||||
(add)->hh.tbl = (head)->hh.tbl; \
|
||||
HASH_FCN(keyptr,keylen_in, (head)->hh.tbl->num_buckets, \
|
||||
(add)->hh.hashv, _ha_bkt); \
|
||||
HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt],&(add)->hh); \
|
||||
HASH_BLOOM_ADD((head)->hh.tbl,(add)->hh.hashv); \
|
||||
HASH_EMIT_KEY(hh,head,keyptr,keylen_in); \
|
||||
HASH_FSCK(hh,head); \
|
||||
} while(0)
|
||||
|
||||
#define HASH_TO_BKT( hashv, num_bkts, bkt ) \
|
||||
do { \
|
||||
bkt = ((hashv) & ((num_bkts) - 1)); \
|
||||
} while(0)
|
||||
|
||||
/* delete "delptr" from the hash table.
|
||||
* "the usual" patch-up process for the app-order doubly-linked-list.
|
||||
* The use of _hd_hh_del below deserves special explanation.
|
||||
* These used to be expressed using (delptr) but that led to a bug
|
||||
* if someone used the same symbol for the head and deletee, like
|
||||
* HASH_DELETE(hh,users,users);
|
||||
* We want that to work, but by changing the head (users) below
|
||||
* we were forfeiting our ability to further refer to the deletee (users)
|
||||
* in the patch-up process. Solution: use scratch space in the table to
|
||||
* copy the deletee pointer, then the latter references are via that
|
||||
* scratch pointer rather than through the repointed (users) symbol.
|
||||
*/
|
||||
#define HASH_DELETE(hh,head,delptr) \
|
||||
do { \
|
||||
unsigned _hd_bkt; \
|
||||
struct UT_hash_handle *_hd_hh_del; \
|
||||
if ( ((delptr)->hh.prev == NULL) && ((delptr)->hh.next == NULL) ) { \
|
||||
uthash_free((head)->hh.tbl->buckets ); \
|
||||
HASH_BLOOM_FREE((head)->hh.tbl); \
|
||||
uthash_free((head)->hh.tbl); \
|
||||
head = NULL; \
|
||||
} else { \
|
||||
_hd_hh_del = &((delptr)->hh); \
|
||||
if ((delptr) == ELMT_FROM_HH((head)->hh.tbl,(head)->hh.tbl->tail)) { \
|
||||
(head)->hh.tbl->tail = \
|
||||
(UT_hash_handle*)((char*)((delptr)->hh.prev) + \
|
||||
(head)->hh.tbl->hho); \
|
||||
} \
|
||||
if ((delptr)->hh.prev) { \
|
||||
((UT_hash_handle*)((char*)((delptr)->hh.prev) + \
|
||||
(head)->hh.tbl->hho))->next = (delptr)->hh.next; \
|
||||
} else { \
|
||||
head = TYPEOF(head)((delptr)->hh.next); \
|
||||
} \
|
||||
if (_hd_hh_del->next) { \
|
||||
((UT_hash_handle*)((char*)_hd_hh_del->next + \
|
||||
(head)->hh.tbl->hho))->prev = \
|
||||
_hd_hh_del->prev; \
|
||||
} \
|
||||
HASH_TO_BKT( _hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \
|
||||
HASH_DEL_IN_BKT(hh,(head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \
|
||||
(head)->hh.tbl->num_items--; \
|
||||
} \
|
||||
HASH_FSCK(hh,head); \
|
||||
} while (0)
|
||||
|
||||
|
||||
/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */
|
||||
#define HASH_FIND_STR(head,findstr,out) \
|
||||
HASH_FIND(hh,head,findstr,strlen(findstr),out)
|
||||
#define HASH_ADD_STR(head,strfield,add) \
|
||||
HASH_ADD(hh,head,strfield,strlen(add->strfield),add)
|
||||
#define HASH_FIND_INT(head,findint,out) \
|
||||
HASH_FIND(hh,head,findint,sizeof(int),out)
|
||||
#define HASH_ADD_INT(head,intfield,add) \
|
||||
HASH_ADD(hh,head,intfield,sizeof(int),add)
|
||||
#define HASH_DEL(head,delptr) \
|
||||
HASH_DELETE(hh,head,delptr)
|
||||
|
||||
/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined.
|
||||
* This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined.
|
||||
*/
|
||||
#if defined(HASH_DEBUG)
|
||||
#define HASH_OOPS(...) do { fprintf(stderr,__VA_ARGS__); exit(-1); } while (0)
|
||||
#define HASH_FSCK(hh,head) \
|
||||
do { \
|
||||
unsigned _bkt_i; \
|
||||
unsigned _count, _bkt_count; \
|
||||
char *_prev; \
|
||||
struct UT_hash_handle *_thh; \
|
||||
if (head) { \
|
||||
_count = 0; \
|
||||
for( _bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; _bkt_i++) { \
|
||||
_bkt_count = 0; \
|
||||
_thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \
|
||||
_prev = NULL; \
|
||||
while (_thh) { \
|
||||
if (_prev != (char*)(_thh->hh_prev)) { \
|
||||
HASH_OOPS("invalid hh_prev %p, actual %p\n", \
|
||||
_thh->hh_prev, _prev ); \
|
||||
} \
|
||||
_bkt_count++; \
|
||||
_prev = (char*)(_thh); \
|
||||
_thh = _thh->hh_next; \
|
||||
} \
|
||||
_count += _bkt_count; \
|
||||
if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \
|
||||
HASH_OOPS("invalid bucket count %d, actual %d\n", \
|
||||
(head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \
|
||||
} \
|
||||
} \
|
||||
if (_count != (head)->hh.tbl->num_items) { \
|
||||
HASH_OOPS("invalid hh item count %d, actual %d\n", \
|
||||
(head)->hh.tbl->num_items, _count ); \
|
||||
} \
|
||||
/* traverse hh in app order; check next/prev integrity, count */ \
|
||||
_count = 0; \
|
||||
_prev = NULL; \
|
||||
_thh = &(head)->hh; \
|
||||
while (_thh) { \
|
||||
_count++; \
|
||||
if (_prev !=(char*)(_thh->prev)) { \
|
||||
HASH_OOPS("invalid prev %p, actual %p\n", \
|
||||
_thh->prev, _prev ); \
|
||||
} \
|
||||
_prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \
|
||||
_thh = ( _thh->next ? (UT_hash_handle*)((char*)(_thh->next) + \
|
||||
(head)->hh.tbl->hho) : NULL ); \
|
||||
} \
|
||||
if (_count != (head)->hh.tbl->num_items) { \
|
||||
HASH_OOPS("invalid app item count %d, actual %d\n", \
|
||||
(head)->hh.tbl->num_items, _count ); \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
#else
|
||||
#define HASH_FSCK(hh,head)
|
||||
#endif
|
||||
|
||||
/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to
|
||||
* the descriptor to which this macro is defined for tuning the hash function.
|
||||
* The app can #include <unistd.h> to get the prototype for write(2). */
|
||||
#if defined(HASH_EMIT_KEYS)
|
||||
#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) \
|
||||
do { \
|
||||
unsigned _klen = fieldlen; \
|
||||
write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \
|
||||
write(HASH_EMIT_KEYS, keyptr, fieldlen); \
|
||||
} while (0)
|
||||
#else
|
||||
#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen)
|
||||
#endif
|
||||
|
||||
/* default to Jenkin's hash unless overridden e.g. DHASH_FUNCTION=HASH_SAX */
|
||||
#if defined(HASH_FUNCTION)
|
||||
#define HASH_FCN HASH_FUNCTION
|
||||
#else
|
||||
#define HASH_FCN HASH_JEN
|
||||
#endif
|
||||
|
||||
/* The Bernstein hash function, used in Perl prior to v5.6 */
|
||||
#define HASH_BER(key,keylen,num_bkts,hashv,bkt) \
|
||||
do { \
|
||||
unsigned _hb_keylen=keylen; \
|
||||
char *_hb_key=(char*)key; \
|
||||
(hashv) = 0; \
|
||||
while (_hb_keylen--) { (hashv) = ((hashv) * 33) + *_hb_key++; } \
|
||||
bkt = (hashv) & (num_bkts-1); \
|
||||
} while (0)
|
||||
|
||||
|
||||
/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at
|
||||
* http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx */
|
||||
#define HASH_SAX(key,keylen,num_bkts,hashv,bkt) \
|
||||
do { \
|
||||
unsigned _sx_i; \
|
||||
char *_hs_key=(char*)key; \
|
||||
hashv = 0; \
|
||||
for(_sx_i=0; _sx_i < keylen; _sx_i++) \
|
||||
hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \
|
||||
bkt = hashv & (num_bkts-1); \
|
||||
} while (0)
|
||||
|
||||
#define HASH_FNV(key,keylen,num_bkts,hashv,bkt) \
|
||||
do { \
|
||||
unsigned _fn_i; \
|
||||
char *_hf_key=(char*)key; \
|
||||
hashv = 2166136261UL; \
|
||||
for(_fn_i=0; _fn_i < keylen; _fn_i++) \
|
||||
hashv = (hashv * 16777619) ^ _hf_key[_fn_i]; \
|
||||
bkt = hashv & (num_bkts-1); \
|
||||
} while(0);
|
||||
|
||||
#define HASH_OAT(key,keylen,num_bkts,hashv,bkt) \
|
||||
do { \
|
||||
unsigned _ho_i; \
|
||||
char *_ho_key=(char*)key; \
|
||||
hashv = 0; \
|
||||
for(_ho_i=0; _ho_i < keylen; _ho_i++) { \
|
||||
hashv += _ho_key[_ho_i]; \
|
||||
hashv += (hashv << 10); \
|
||||
hashv ^= (hashv >> 6); \
|
||||
} \
|
||||
hashv += (hashv << 3); \
|
||||
hashv ^= (hashv >> 11); \
|
||||
hashv += (hashv << 15); \
|
||||
bkt = hashv & (num_bkts-1); \
|
||||
} while(0)
|
||||
|
||||
#define HASH_JEN_MIX(a,b,c) \
|
||||
do { \
|
||||
a -= b; a -= c; a ^= ( c >> 13 ); \
|
||||
b -= c; b -= a; b ^= ( a << 8 ); \
|
||||
c -= a; c -= b; c ^= ( b >> 13 ); \
|
||||
a -= b; a -= c; a ^= ( c >> 12 ); \
|
||||
b -= c; b -= a; b ^= ( a << 16 ); \
|
||||
c -= a; c -= b; c ^= ( b >> 5 ); \
|
||||
a -= b; a -= c; a ^= ( c >> 3 ); \
|
||||
b -= c; b -= a; b ^= ( a << 10 ); \
|
||||
c -= a; c -= b; c ^= ( b >> 15 ); \
|
||||
} while (0)
|
||||
|
||||
#define HASH_JEN(key,keylen,num_bkts,hashv,bkt) \
|
||||
do { \
|
||||
unsigned _hj_i,_hj_j,_hj_k; \
|
||||
char *_hj_key=(char*)key; \
|
||||
hashv = 0xfeedbeef; \
|
||||
_hj_i = _hj_j = 0x9e3779b9; \
|
||||
_hj_k = keylen; \
|
||||
while (_hj_k >= 12) { \
|
||||
_hj_i += (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 ) \
|
||||
+ ( (unsigned)_hj_key[2] << 16 ) \
|
||||
+ ( (unsigned)_hj_key[3] << 24 ) ); \
|
||||
_hj_j += (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 ) \
|
||||
+ ( (unsigned)_hj_key[6] << 16 ) \
|
||||
+ ( (unsigned)_hj_key[7] << 24 ) ); \
|
||||
hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 ) \
|
||||
+ ( (unsigned)_hj_key[10] << 16 ) \
|
||||
+ ( (unsigned)_hj_key[11] << 24 ) ); \
|
||||
\
|
||||
HASH_JEN_MIX(_hj_i, _hj_j, hashv); \
|
||||
\
|
||||
_hj_key += 12; \
|
||||
_hj_k -= 12; \
|
||||
} \
|
||||
hashv += keylen; \
|
||||
switch ( _hj_k ) { \
|
||||
case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); \
|
||||
case 10: hashv += ( (unsigned)_hj_key[9] << 16 ); \
|
||||
case 9: hashv += ( (unsigned)_hj_key[8] << 8 ); \
|
||||
case 8: _hj_j += ( (unsigned)_hj_key[7] << 24 ); \
|
||||
case 7: _hj_j += ( (unsigned)_hj_key[6] << 16 ); \
|
||||
case 6: _hj_j += ( (unsigned)_hj_key[5] << 8 ); \
|
||||
case 5: _hj_j += _hj_key[4]; \
|
||||
case 4: _hj_i += ( (unsigned)_hj_key[3] << 24 ); \
|
||||
case 3: _hj_i += ( (unsigned)_hj_key[2] << 16 ); \
|
||||
case 2: _hj_i += ( (unsigned)_hj_key[1] << 8 ); \
|
||||
case 1: _hj_i += _hj_key[0]; \
|
||||
} \
|
||||
HASH_JEN_MIX(_hj_i, _hj_j, hashv); \
|
||||
bkt = hashv & (num_bkts-1); \
|
||||
} while(0)
|
||||
|
||||
/* The Paul Hsieh hash function */
|
||||
#undef get16bits
|
||||
#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \
|
||||
|| defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__)
|
||||
#define get16bits(d) (*((const uint16_t *) (d)))
|
||||
#endif
|
||||
|
||||
#if !defined (get16bits)
|
||||
#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \
|
||||
+(uint32_t)(((const uint8_t *)(d))[0]) )
|
||||
#endif
|
||||
#define HASH_SFH(key,keylen,num_bkts,hashv,bkt) \
|
||||
do { \
|
||||
char *_sfh_key=(char*)key; \
|
||||
uint32_t _sfh_tmp, _sfh_len = keylen; \
|
||||
\
|
||||
int _sfh_rem = _sfh_len & 3; \
|
||||
_sfh_len >>= 2; \
|
||||
hashv = 0xcafebabe; \
|
||||
\
|
||||
/* Main loop */ \
|
||||
for (;_sfh_len > 0; _sfh_len--) { \
|
||||
hashv += get16bits (_sfh_key); \
|
||||
_sfh_tmp = (get16bits (_sfh_key+2) << 11) ^ hashv; \
|
||||
hashv = (hashv << 16) ^ _sfh_tmp; \
|
||||
_sfh_key += 2*sizeof (uint16_t); \
|
||||
hashv += hashv >> 11; \
|
||||
} \
|
||||
\
|
||||
/* Handle end cases */ \
|
||||
switch (_sfh_rem) { \
|
||||
case 3: hashv += get16bits (_sfh_key); \
|
||||
hashv ^= hashv << 16; \
|
||||
hashv ^= _sfh_key[sizeof (uint16_t)] << 18; \
|
||||
hashv += hashv >> 11; \
|
||||
break; \
|
||||
case 2: hashv += get16bits (_sfh_key); \
|
||||
hashv ^= hashv << 11; \
|
||||
hashv += hashv >> 17; \
|
||||
break; \
|
||||
case 1: hashv += *_sfh_key; \
|
||||
hashv ^= hashv << 10; \
|
||||
hashv += hashv >> 1; \
|
||||
} \
|
||||
\
|
||||
/* Force "avalanching" of final 127 bits */ \
|
||||
hashv ^= hashv << 3; \
|
||||
hashv += hashv >> 5; \
|
||||
hashv ^= hashv << 4; \
|
||||
hashv += hashv >> 17; \
|
||||
hashv ^= hashv << 25; \
|
||||
hashv += hashv >> 6; \
|
||||
bkt = hashv & (num_bkts-1); \
|
||||
} while(0);
|
||||
|
||||
#if defined(HASH_USING_NO_STRICT_ALIASING)
|
||||
/* The MurmurHash exploits some CPU's (e.g. x86) tolerance for unaligned reads.
|
||||
* For other types of CPU's (e.g. Sparc) an unaligned read causes a bus error.
|
||||
* So MurmurHash comes in two versions, the faster unaligned one and the slower
|
||||
* aligned one. We only use the faster one on CPU's where we know it's safe.
|
||||
*
|
||||
* Note the preprocessor built-in defines can be emitted using:
|
||||
*
|
||||
* gcc -m64 -dM -E - < /dev/null (on gcc)
|
||||
* cc -## a.c (where a.c is a simple test file) (Sun Studio)
|
||||
*/
|
||||
#if (defined(__i386__) || defined(__x86_64__))
|
||||
#define HASH_MUR HASH_MUR_UNALIGNED
|
||||
#else
|
||||
#define HASH_MUR HASH_MUR_ALIGNED
|
||||
#endif
|
||||
|
||||
/* Appleby's MurmurHash fast version for unaligned-tolerant archs like i386 */
|
||||
#define HASH_MUR_UNALIGNED(key,keylen,num_bkts,hashv,bkt) \
|
||||
do { \
|
||||
const unsigned int _mur_m = 0x5bd1e995; \
|
||||
const int _mur_r = 24; \
|
||||
hashv = 0xcafebabe ^ keylen; \
|
||||
char *_mur_key = (char *)key; \
|
||||
uint32_t _mur_tmp, _mur_len = keylen; \
|
||||
\
|
||||
for (;_mur_len >= 4; _mur_len-=4) { \
|
||||
_mur_tmp = *(uint32_t *)_mur_key; \
|
||||
_mur_tmp *= _mur_m; \
|
||||
_mur_tmp ^= _mur_tmp >> _mur_r; \
|
||||
_mur_tmp *= _mur_m; \
|
||||
hashv *= _mur_m; \
|
||||
hashv ^= _mur_tmp; \
|
||||
_mur_key += 4; \
|
||||
} \
|
||||
\
|
||||
switch(_mur_len) \
|
||||
{ \
|
||||
case 3: hashv ^= _mur_key[2] << 16; \
|
||||
case 2: hashv ^= _mur_key[1] << 8; \
|
||||
case 1: hashv ^= _mur_key[0]; \
|
||||
hashv *= _mur_m; \
|
||||
}; \
|
||||
\
|
||||
hashv ^= hashv >> 13; \
|
||||
hashv *= _mur_m; \
|
||||
hashv ^= hashv >> 15; \
|
||||
\
|
||||
bkt = hashv & (num_bkts-1); \
|
||||
} while(0)
|
||||
|
||||
/* Appleby's MurmurHash version for alignment-sensitive archs like Sparc */
|
||||
#define HASH_MUR_ALIGNED(key,keylen,num_bkts,hashv,bkt) \
|
||||
do { \
|
||||
const unsigned int _mur_m = 0x5bd1e995; \
|
||||
const int _mur_r = 24; \
|
||||
hashv = 0xcafebabe ^ keylen; \
|
||||
char *_mur_key = (char *)key; \
|
||||
uint32_t _mur_len = keylen; \
|
||||
int _mur_align = (int)_mur_key & 3; \
|
||||
\
|
||||
if (_mur_align && (_mur_len >= 4)) { \
|
||||
unsigned _mur_t = 0, _mur_d = 0; \
|
||||
switch(_mur_align) { \
|
||||
case 1: _mur_t |= _mur_key[2] << 16; \
|
||||
case 2: _mur_t |= _mur_key[1] << 8; \
|
||||
case 3: _mur_t |= _mur_key[0]; \
|
||||
} \
|
||||
_mur_t <<= (8 * _mur_align); \
|
||||
_mur_key += 4-_mur_align; \
|
||||
_mur_len -= 4-_mur_align; \
|
||||
int _mur_sl = 8 * (4-_mur_align); \
|
||||
int _mur_sr = 8 * _mur_align; \
|
||||
\
|
||||
for (;_mur_len >= 4; _mur_len-=4) { \
|
||||
_mur_d = *(unsigned *)_mur_key; \
|
||||
_mur_t = (_mur_t >> _mur_sr) | (_mur_d << _mur_sl); \
|
||||
unsigned _mur_k = _mur_t; \
|
||||
_mur_k *= _mur_m; \
|
||||
_mur_k ^= _mur_k >> _mur_r; \
|
||||
_mur_k *= _mur_m; \
|
||||
hashv *= _mur_m; \
|
||||
hashv ^= _mur_k; \
|
||||
_mur_t = _mur_d; \
|
||||
_mur_key += 4; \
|
||||
} \
|
||||
_mur_d = 0; \
|
||||
if(_mur_len >= _mur_align) { \
|
||||
switch(_mur_align) { \
|
||||
case 3: _mur_d |= _mur_key[2] << 16; \
|
||||
case 2: _mur_d |= _mur_key[1] << 8; \
|
||||
case 1: _mur_d |= _mur_key[0]; \
|
||||
} \
|
||||
unsigned _mur_k = (_mur_t >> _mur_sr) | (_mur_d << _mur_sl); \
|
||||
_mur_k *= _mur_m; \
|
||||
_mur_k ^= _mur_k >> _mur_r; \
|
||||
_mur_k *= _mur_m; \
|
||||
hashv *= _mur_m; \
|
||||
hashv ^= _mur_k; \
|
||||
_mur_k += _mur_align; \
|
||||
_mur_len -= _mur_align; \
|
||||
\
|
||||
switch(_mur_len) \
|
||||
{ \
|
||||
case 3: hashv ^= _mur_key[2] << 16; \
|
||||
case 2: hashv ^= _mur_key[1] << 8; \
|
||||
case 1: hashv ^= _mur_key[0]; \
|
||||
hashv *= _mur_m; \
|
||||
} \
|
||||
} else { \
|
||||
switch(_mur_len) \
|
||||
{ \
|
||||
case 3: _mur_d ^= _mur_key[2] << 16; \
|
||||
case 2: _mur_d ^= _mur_key[1] << 8; \
|
||||
case 1: _mur_d ^= _mur_key[0]; \
|
||||
case 0: hashv ^= (_mur_t >> _mur_sr) | (_mur_d << _mur_sl); \
|
||||
hashv *= _mur_m; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
hashv ^= hashv >> 13; \
|
||||
hashv *= _mur_m; \
|
||||
hashv ^= hashv >> 15; \
|
||||
} else { \
|
||||
for (;_mur_len >= 4; _mur_len-=4) { \
|
||||
unsigned _mur_k = *(unsigned*)_mur_key; \
|
||||
_mur_k *= _mur_m; \
|
||||
_mur_k ^= _mur_k >> _mur_r; \
|
||||
_mur_k *= _mur_m; \
|
||||
hashv *= _mur_m; \
|
||||
hashv ^= _mur_k; \
|
||||
_mur_key += 4; \
|
||||
} \
|
||||
switch(_mur_len) \
|
||||
{ \
|
||||
case 3: hashv ^= _mur_key[2] << 16; \
|
||||
case 2: hashv ^= _mur_key[1] << 8; \
|
||||
case 1: hashv ^= _mur_key[0]; \
|
||||
hashv *= _mur_m; \
|
||||
} \
|
||||
\
|
||||
hashv ^= hashv >> 13; \
|
||||
hashv *= _mur_m; \
|
||||
hashv ^= hashv >> 15; \
|
||||
} \
|
||||
bkt = hashv & (num_bkts-1); \
|
||||
} while(0)
|
||||
#endif /* HASH_USING_NO_STRICT_ALIASING */
|
||||
|
||||
/* key comparison function; return 0 if keys equal */
|
||||
#define HASH_KEYCMP(a,b,len) memcmp(a,b,len)
|
||||
|
||||
/* iterate over items in a known bucket to find desired item */
|
||||
#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,out) \
|
||||
out = TYPEOF(out)((head.hh_head) ? ELMT_FROM_HH(tbl,head.hh_head) : NULL); \
|
||||
while (out) { \
|
||||
if (out->hh.keylen == keylen_in) { \
|
||||
if ((HASH_KEYCMP(out->hh.key,keyptr,keylen_in)) == 0) break; \
|
||||
} \
|
||||
out= TYPEOF(out)((out->hh.hh_next) ? \
|
||||
ELMT_FROM_HH(tbl,out->hh.hh_next) : NULL); \
|
||||
}
|
||||
|
||||
/* add an item to a bucket */
|
||||
#define HASH_ADD_TO_BKT(head,addhh) \
|
||||
do { \
|
||||
head.count++; \
|
||||
(addhh)->hh_next = head.hh_head; \
|
||||
(addhh)->hh_prev = NULL; \
|
||||
if (head.hh_head) { (head).hh_head->hh_prev = (addhh); } \
|
||||
(head).hh_head=addhh; \
|
||||
if (head.count >= ((head.expand_mult+1) * HASH_BKT_CAPACITY_THRESH) \
|
||||
&& (addhh)->tbl->noexpand != 1) { \
|
||||
HASH_EXPAND_BUCKETS((addhh)->tbl); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
/* remove an item from a given bucket */
|
||||
#define HASH_DEL_IN_BKT(hh,head,hh_del) \
|
||||
(head).count--; \
|
||||
if ((head).hh_head == hh_del) { \
|
||||
(head).hh_head = hh_del->hh_next; \
|
||||
} \
|
||||
if (hh_del->hh_prev) { \
|
||||
hh_del->hh_prev->hh_next = hh_del->hh_next; \
|
||||
} \
|
||||
if (hh_del->hh_next) { \
|
||||
hh_del->hh_next->hh_prev = hh_del->hh_prev; \
|
||||
}
|
||||
|
||||
/* Bucket expansion has the effect of doubling the number of buckets
|
||||
* and redistributing the items into the new buckets. Ideally the
|
||||
* items will distribute more or less evenly into the new buckets
|
||||
* (the extent to which this is true is a measure of the quality of
|
||||
* the hash function as it applies to the key domain).
|
||||
*
|
||||
* With the items distributed into more buckets, the chain length
|
||||
* (item count) in each bucket is reduced. Thus by expanding buckets
|
||||
* the hash keeps a bound on the chain length. This bounded chain
|
||||
* length is the essence of how a hash provides constant time lookup.
|
||||
*
|
||||
* The calculation of tbl->ideal_chain_maxlen below deserves some
|
||||
* explanation. First, keep in mind that we're calculating the ideal
|
||||
* maximum chain length based on the *new* (doubled) bucket count.
|
||||
* In fractions this is just n/b (n=number of items,b=new num buckets).
|
||||
* Since the ideal chain length is an integer, we want to calculate
|
||||
* ceil(n/b). We don't depend on floating point arithmetic in this
|
||||
* hash, so to calculate ceil(n/b) with integers we could write
|
||||
*
|
||||
* ceil(n/b) = (n/b) + ((n%b)?1:0)
|
||||
*
|
||||
* and in fact a previous version of this hash did just that.
|
||||
* But now we have improved things a bit by recognizing that b is
|
||||
* always a power of two. We keep its base 2 log handy (call it lb),
|
||||
* so now we can write this with a bit shift and logical AND:
|
||||
*
|
||||
* ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0)
|
||||
*
|
||||
*/
|
||||
#define HASH_EXPAND_BUCKETS(tbl) \
|
||||
do { \
|
||||
unsigned _he_bkt; \
|
||||
unsigned _he_bkt_i; \
|
||||
struct UT_hash_handle *_he_thh, *_he_hh_nxt; \
|
||||
UT_hash_bucket *_he_new_buckets, *_he_newbkt; \
|
||||
_he_new_buckets = (UT_hash_bucket*)uthash_malloc( \
|
||||
2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \
|
||||
if (!_he_new_buckets) { uthash_fatal( "out of memory"); } \
|
||||
memset(_he_new_buckets, 0, \
|
||||
2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \
|
||||
tbl->ideal_chain_maxlen = \
|
||||
(tbl->num_items >> (tbl->log2_num_buckets+1)) + \
|
||||
((tbl->num_items & ((tbl->num_buckets*2)-1)) ? 1 : 0); \
|
||||
tbl->nonideal_items = 0; \
|
||||
for(_he_bkt_i = 0; _he_bkt_i < tbl->num_buckets; _he_bkt_i++) \
|
||||
{ \
|
||||
_he_thh = tbl->buckets[ _he_bkt_i ].hh_head; \
|
||||
while (_he_thh) { \
|
||||
_he_hh_nxt = _he_thh->hh_next; \
|
||||
HASH_TO_BKT( _he_thh->hashv, tbl->num_buckets*2, _he_bkt); \
|
||||
_he_newbkt = &(_he_new_buckets[ _he_bkt ]); \
|
||||
if (++(_he_newbkt->count) > tbl->ideal_chain_maxlen) { \
|
||||
tbl->nonideal_items++; \
|
||||
_he_newbkt->expand_mult = _he_newbkt->count / \
|
||||
tbl->ideal_chain_maxlen; \
|
||||
} \
|
||||
_he_thh->hh_prev = NULL; \
|
||||
_he_thh->hh_next = _he_newbkt->hh_head; \
|
||||
if (_he_newbkt->hh_head) _he_newbkt->hh_head->hh_prev = \
|
||||
_he_thh; \
|
||||
_he_newbkt->hh_head = _he_thh; \
|
||||
_he_thh = _he_hh_nxt; \
|
||||
} \
|
||||
} \
|
||||
tbl->num_buckets *= 2; \
|
||||
tbl->log2_num_buckets++; \
|
||||
uthash_free( tbl->buckets ); \
|
||||
tbl->buckets = _he_new_buckets; \
|
||||
tbl->ineff_expands = (tbl->nonideal_items > (tbl->num_items >> 1)) ? \
|
||||
(tbl->ineff_expands+1) : 0; \
|
||||
if (tbl->ineff_expands > 1) { \
|
||||
tbl->noexpand=1; \
|
||||
uthash_noexpand_fyi(tbl); \
|
||||
} \
|
||||
uthash_expand_fyi(tbl); \
|
||||
} while(0)
|
||||
|
||||
|
||||
/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */
|
||||
/* Note that HASH_SORT assumes the hash handle name to be hh.
|
||||
* HASH_SRT was added to allow the hash handle name to be passed in. */
|
||||
#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn)
|
||||
#define HASH_SRT(hh,head,cmpfcn) \
|
||||
do { \
|
||||
unsigned _hs_i; \
|
||||
unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize; \
|
||||
struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \
|
||||
if (head) { \
|
||||
_hs_insize = 1; \
|
||||
_hs_looping = 1; \
|
||||
_hs_list = &((head)->hh); \
|
||||
while (_hs_looping) { \
|
||||
_hs_p = _hs_list; \
|
||||
_hs_list = NULL; \
|
||||
_hs_tail = NULL; \
|
||||
_hs_nmerges = 0; \
|
||||
while (_hs_p) { \
|
||||
_hs_nmerges++; \
|
||||
_hs_q = _hs_p; \
|
||||
_hs_psize = 0; \
|
||||
for ( _hs_i = 0; _hs_i < _hs_insize; _hs_i++ ) { \
|
||||
_hs_psize++; \
|
||||
_hs_q = (UT_hash_handle*)((_hs_q->next) ? \
|
||||
((void*)((char*)(_hs_q->next) + \
|
||||
(head)->hh.tbl->hho)) : NULL); \
|
||||
if (! (_hs_q) ) break; \
|
||||
} \
|
||||
_hs_qsize = _hs_insize; \
|
||||
while ((_hs_psize > 0) || ((_hs_qsize > 0) && _hs_q )) { \
|
||||
if (_hs_psize == 0) { \
|
||||
_hs_e = _hs_q; \
|
||||
_hs_q = (UT_hash_handle*)((_hs_q->next) ? \
|
||||
((void*)((char*)(_hs_q->next) + \
|
||||
(head)->hh.tbl->hho)) : NULL); \
|
||||
_hs_qsize--; \
|
||||
} else if ( (_hs_qsize == 0) || !(_hs_q) ) { \
|
||||
_hs_e = _hs_p; \
|
||||
_hs_p = (UT_hash_handle*)((_hs_p->next) ? \
|
||||
((void*)((char*)(_hs_p->next) + \
|
||||
(head)->hh.tbl->hho)) : NULL); \
|
||||
_hs_psize--; \
|
||||
} else if (( \
|
||||
cmpfcn(TYPEOF(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_p)), \
|
||||
TYPEOF(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_q))) \
|
||||
) <= 0) { \
|
||||
_hs_e = _hs_p; \
|
||||
_hs_p = (UT_hash_handle*)((_hs_p->next) ? \
|
||||
((void*)((char*)(_hs_p->next) + \
|
||||
(head)->hh.tbl->hho)) : NULL); \
|
||||
_hs_psize--; \
|
||||
} else { \
|
||||
_hs_e = _hs_q; \
|
||||
_hs_q = (UT_hash_handle*)((_hs_q->next) ? \
|
||||
((void*)((char*)(_hs_q->next) + \
|
||||
(head)->hh.tbl->hho)) : NULL); \
|
||||
_hs_qsize--; \
|
||||
} \
|
||||
if ( _hs_tail ) { \
|
||||
_hs_tail->next = ((_hs_e) ? \
|
||||
ELMT_FROM_HH((head)->hh.tbl,_hs_e) : NULL); \
|
||||
} else { \
|
||||
_hs_list = _hs_e; \
|
||||
} \
|
||||
_hs_e->prev = ((_hs_tail) ? \
|
||||
ELMT_FROM_HH((head)->hh.tbl,_hs_tail) : NULL); \
|
||||
_hs_tail = _hs_e; \
|
||||
} \
|
||||
_hs_p = _hs_q; \
|
||||
} \
|
||||
_hs_tail->next = NULL; \
|
||||
if ( _hs_nmerges <= 1 ) { \
|
||||
_hs_looping=0; \
|
||||
(head)->hh.tbl->tail = _hs_tail; \
|
||||
(head) = TYPEOF(head)ELMT_FROM_HH((head)->hh.tbl, _hs_list); \
|
||||
} \
|
||||
_hs_insize *= 2; \
|
||||
} \
|
||||
HASH_FSCK(hh,head); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/* This function selects items from one hash into another hash.
|
||||
* The end result is that the selected items have dual presence
|
||||
* in both hashes. There is no copy of the items made; rather
|
||||
* they are added into the new hash through a secondary hash
|
||||
* hash handle that must be present in the structure. */
|
||||
#define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \
|
||||
do { \
|
||||
unsigned _src_bkt, _dst_bkt; \
|
||||
void *_last_elt=NULL, *_elt; \
|
||||
UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL; \
|
||||
ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst)); \
|
||||
if (src) { \
|
||||
for(_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \
|
||||
for(_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \
|
||||
_src_hh; \
|
||||
_src_hh = _src_hh->hh_next) { \
|
||||
_elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \
|
||||
if (cond(_elt)) { \
|
||||
_dst_hh = (UT_hash_handle*)(((char*)_elt) + _dst_hho); \
|
||||
_dst_hh->key = _src_hh->key; \
|
||||
_dst_hh->keylen = _src_hh->keylen; \
|
||||
_dst_hh->hashv = _src_hh->hashv; \
|
||||
_dst_hh->prev = _last_elt; \
|
||||
_dst_hh->next = NULL; \
|
||||
if (_last_elt_hh) { _last_elt_hh->next = _elt; } \
|
||||
if (!dst) { \
|
||||
dst = TYPEOF(dst)_elt; \
|
||||
HASH_MAKE_TABLE(hh_dst,dst); \
|
||||
} else { \
|
||||
_dst_hh->tbl = (dst)->hh_dst.tbl; \
|
||||
} \
|
||||
HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \
|
||||
HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt],_dst_hh); \
|
||||
(dst)->hh_dst.tbl->num_items++; \
|
||||
_last_elt = _elt; \
|
||||
_last_elt_hh = _dst_hh; \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
HASH_FSCK(hh_dst,dst); \
|
||||
} while (0)
|
||||
|
||||
#define HASH_CLEAR(hh,head) \
|
||||
do { \
|
||||
if (head) { \
|
||||
uthash_free((head)->hh.tbl->buckets ); \
|
||||
uthash_free((head)->hh.tbl); \
|
||||
(head)=NULL; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
/* obtain a count of items in the hash */
|
||||
#define HASH_COUNT(head) HASH_CNT(hh,head)
|
||||
#define HASH_CNT(hh,head) (head?(head->hh.tbl->num_items):0)
|
||||
|
||||
typedef struct UT_hash_bucket {
|
||||
struct UT_hash_handle *hh_head;
|
||||
unsigned count;
|
||||
|
||||
/* expand_mult is normally set to 0. In this situation, the max chain length
|
||||
* threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If
|
||||
* the bucket's chain exceeds this length, bucket expansion is triggered).
|
||||
* However, setting expand_mult to a non-zero value delays bucket expansion
|
||||
* (that would be triggered by additions to this particular bucket)
|
||||
* until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH.
|
||||
* (The multiplier is simply expand_mult+1). The whole idea of this
|
||||
* multiplier is to reduce bucket expansions, since they are expensive, in
|
||||
* situations where we know that a particular bucket tends to be overused.
|
||||
* It is better to let its chain length grow to a longer yet-still-bounded
|
||||
* value, than to do an O(n) bucket expansion too often.
|
||||
*/
|
||||
unsigned expand_mult;
|
||||
|
||||
} UT_hash_bucket;
|
||||
|
||||
/* random signature used only to find hash tables in external analysis */
|
||||
#define HASH_SIGNATURE 0xa0111fe1
|
||||
#define HASH_BLOOM_SIGNATURE 0xb12220f2
|
||||
|
||||
typedef struct UT_hash_table {
|
||||
UT_hash_bucket *buckets;
|
||||
unsigned num_buckets, log2_num_buckets;
|
||||
unsigned num_items;
|
||||
struct UT_hash_handle *tail; /* tail hh in app order, for fast append */
|
||||
ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */
|
||||
|
||||
/* in an ideal situation (all buckets used equally), no bucket would have
|
||||
* more than ceil(#items/#buckets) items. that's the ideal chain length. */
|
||||
unsigned ideal_chain_maxlen;
|
||||
|
||||
/* nonideal_items is the number of items in the hash whose chain position
|
||||
* exceeds the ideal chain maxlen. these items pay the penalty for an uneven
|
||||
* hash distribution; reaching them in a chain traversal takes >ideal steps */
|
||||
unsigned nonideal_items;
|
||||
|
||||
/* ineffective expands occur when a bucket doubling was performed, but
|
||||
* afterward, more than half the items in the hash had nonideal chain
|
||||
* positions. If this happens on two consecutive expansions we inhibit any
|
||||
* further expansion, as it's not helping; this happens when the hash
|
||||
* function isn't a good fit for the key domain. When expansion is inhibited
|
||||
* the hash will still work, albeit no longer in constant time. */
|
||||
unsigned ineff_expands, noexpand;
|
||||
|
||||
uint32_t signature; /* used only to find hash tables in external analysis */
|
||||
#if defined(HASH_BLOOM)
|
||||
uint32_t bloom_sig; /* used only to test bloom exists in external analysis */
|
||||
uint8_t *bloom_bv;
|
||||
char bloom_nbits;
|
||||
#endif
|
||||
|
||||
} UT_hash_table;
|
||||
|
||||
typedef struct UT_hash_handle {
|
||||
struct UT_hash_table *tbl;
|
||||
void *prev; /* prev element in app order */
|
||||
void *next; /* next element in app order */
|
||||
struct UT_hash_handle *hh_prev; /* previous hh in bucket order */
|
||||
struct UT_hash_handle *hh_next; /* next hh in bucket order */
|
||||
void *key; /* ptr to enclosing struct's key */
|
||||
unsigned keylen; /* enclosing struct's key len */
|
||||
unsigned hashv; /* result of hash-fcn(key) */
|
||||
} UT_hash_handle;
|
||||
|
||||
#endif /* UTHASH_H */
|
29
base/xdisplay.h
Normal file
29
base/xdisplay.h
Normal file
@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
#ifndef XDISPLAY_H
|
||||
#define XDISPLAY_H
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
|
||||
/* Returns the main display, closed either on exit or when closeMainDisplay()
|
||||
* is invoked. This removes a bit of the overhead of calling XOpenDisplay() &
|
||||
* XCloseDisplay() everytime the main display needs to be used.
|
||||
*
|
||||
* Note that this is almost certainly not thread safe. */
|
||||
Display *XGetMainDisplay(void);
|
||||
|
||||
/* Closes the main display if it is open, or does nothing if not. */
|
||||
void XCloseMainDisplay(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
char *getXDisplay(void);
|
||||
void setXDisplay(char *name);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* XDISPLAY_H */
|
55
base/xdisplay_init.h
Normal file
55
base/xdisplay_init.h
Normal file
@ -0,0 +1,55 @@
|
||||
#include "xdisplay.h"
|
||||
#include <stdio.h> /* For fputs() */
|
||||
#include <stdlib.h> /* For atexit() */
|
||||
|
||||
static Display *mainDisplay = NULL;
|
||||
static int registered = 0;
|
||||
static char *displayName = ":0.0";
|
||||
static int hasDisplayNameChanged = 0;
|
||||
|
||||
Display *XGetMainDisplay(void)
|
||||
{
|
||||
/* Close the display if displayName has changed */
|
||||
if (hasDisplayNameChanged) {
|
||||
XCloseMainDisplay();
|
||||
hasDisplayNameChanged = 0;
|
||||
}
|
||||
|
||||
if (mainDisplay == NULL) {
|
||||
/* First try the user set displayName */
|
||||
mainDisplay = XOpenDisplay(displayName);
|
||||
|
||||
/* Then try using environment variable DISPLAY */
|
||||
if (mainDisplay == NULL) {
|
||||
mainDisplay = XOpenDisplay(NULL);
|
||||
}
|
||||
|
||||
if (mainDisplay == NULL) {
|
||||
fputs("Could not open main display\n", stderr);
|
||||
} else if (!registered) {
|
||||
atexit(&XCloseMainDisplay);
|
||||
registered = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return mainDisplay;
|
||||
}
|
||||
|
||||
void XCloseMainDisplay(void)
|
||||
{
|
||||
if (mainDisplay != NULL) {
|
||||
XCloseDisplay(mainDisplay);
|
||||
mainDisplay = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
char *getXDisplay(void)
|
||||
{
|
||||
return displayName;
|
||||
}
|
||||
|
||||
void setXDisplay(char *name)
|
||||
{
|
||||
displayName = strdup(name);
|
||||
hasDisplayNameChanged = 1;
|
||||
}
|
10
bitmap/goBitmap.h
Normal file
10
bitmap/goBitmap.h
Normal file
@ -0,0 +1,10 @@
|
||||
class BMP
|
||||
{
|
||||
public:
|
||||
size_t width;
|
||||
size_t height;
|
||||
size_t byteWidth;
|
||||
uint8_t bitsPerPixel;
|
||||
uint8_t bytesPerPixel;
|
||||
uint8_t *image;
|
||||
};
|
235
key/goKey.h
Normal file
235
key/goKey.h
Normal file
@ -0,0 +1,235 @@
|
||||
#include "../base/types.h"
|
||||
// #include "keycode.h"
|
||||
// #include "keypress.h"
|
||||
#include "keypress_init.h"
|
||||
#include "keycode_init.h"
|
||||
|
||||
|
||||
int keyboardDelay = 10;
|
||||
|
||||
// struct KeyNames{
|
||||
// const char* name;
|
||||
// MMKeyCode key;
|
||||
// };
|
||||
|
||||
// static KeyNames key_names[] ={
|
||||
struct KeyNames{
|
||||
const char* name;
|
||||
MMKeyCode key;
|
||||
}key_names[] = {
|
||||
{ "backspace", K_BACKSPACE },
|
||||
{ "delete", K_DELETE },
|
||||
{ "enter", K_RETURN },
|
||||
{ "tab", K_TAB },
|
||||
{ "escape", K_ESCAPE },
|
||||
{ "up", K_UP },
|
||||
{ "down", K_DOWN },
|
||||
{ "right", K_RIGHT },
|
||||
{ "left", K_LEFT },
|
||||
{ "home", K_HOME },
|
||||
{ "end", K_END },
|
||||
{ "pageup", K_PAGEUP },
|
||||
{ "pagedown", K_PAGEDOWN },
|
||||
{ "f1", K_F1 },
|
||||
{ "f2", K_F2 },
|
||||
{ "f3", K_F3 },
|
||||
{ "f4", K_F4 },
|
||||
{ "f5", K_F5 },
|
||||
{ "f6", K_F6 },
|
||||
{ "f7", K_F7 },
|
||||
{ "f8", K_F8 },
|
||||
{ "f9", K_F9 },
|
||||
{ "f10", K_F10 },
|
||||
{ "f11", K_F11 },
|
||||
{ "f12", K_F12 },
|
||||
{ "f13", K_F13 },
|
||||
{ "f14", K_F14 },
|
||||
{ "f15", K_F15 },
|
||||
{ "f16", K_F16 },
|
||||
{ "f17", K_F17 },
|
||||
{ "f18", K_F18 },
|
||||
{ "f19", K_F19 },
|
||||
{ "f20", K_F20 },
|
||||
{ "f21", K_F21 },
|
||||
{ "f22", K_F22 },
|
||||
{ "f23", K_F23 },
|
||||
{ "f24", K_F24 },
|
||||
{ "command", K_META },
|
||||
{ "alt", K_ALT },
|
||||
{ "control", K_CONTROL },
|
||||
{ "shift", K_SHIFT },
|
||||
{ "right_shift", K_RIGHTSHIFT },
|
||||
{ "space", K_SPACE },
|
||||
{ "printscreen", K_PRINTSCREEN },
|
||||
{ "insert", K_INSERT },
|
||||
|
||||
{ "audio_mute", K_AUDIO_VOLUME_MUTE },
|
||||
{ "audio_vol_down", K_AUDIO_VOLUME_DOWN },
|
||||
{ "audio_vol_up", K_AUDIO_VOLUME_UP },
|
||||
{ "audio_play", K_AUDIO_PLAY },
|
||||
{ "audio_stop", K_AUDIO_STOP },
|
||||
{ "audio_pause", K_AUDIO_PAUSE },
|
||||
{ "audio_prev", K_AUDIO_PREV },
|
||||
{ "audio_next", K_AUDIO_NEXT },
|
||||
{ "audio_rewind", K_AUDIO_REWIND },
|
||||
{ "audio_forward", K_AUDIO_FORWARD },
|
||||
{ "audio_repeat", K_AUDIO_REPEAT },
|
||||
{ "audio_random", K_AUDIO_RANDOM },
|
||||
|
||||
{ "numpad_0", K_NUMPAD_0 },
|
||||
{ "numpad_1", K_NUMPAD_1 },
|
||||
{ "numpad_2", K_NUMPAD_2 },
|
||||
{ "numpad_3", K_NUMPAD_3 },
|
||||
{ "numpad_4", K_NUMPAD_4 },
|
||||
{ "numpad_5", K_NUMPAD_5 },
|
||||
{ "numpad_6", K_NUMPAD_6 },
|
||||
{ "numpad_7", K_NUMPAD_7 },
|
||||
{ "numpad_8", K_NUMPAD_8 },
|
||||
{ "numpad_9", K_NUMPAD_9 },
|
||||
|
||||
{ "lights_mon_up", K_LIGHTS_MON_UP },
|
||||
{ "lights_mon_down", K_LIGHTS_MON_DOWN },
|
||||
{ "lights_kbd_toggle",K_LIGHTS_KBD_TOGGLE },
|
||||
{ "lights_kbd_up", K_LIGHTS_KBD_UP },
|
||||
{ "lights_kbd_down", K_LIGHTS_KBD_DOWN },
|
||||
|
||||
{ NULL, K_NOT_A_KEY } /* end marker */
|
||||
};
|
||||
|
||||
int CheckKeyCodes(char* k, MMKeyCode *key)
|
||||
{
|
||||
if (!key) return -1;
|
||||
|
||||
if (strlen(k) == 1)
|
||||
{
|
||||
*key = keyCodeForChar(*k);
|
||||
return 0;
|
||||
}
|
||||
|
||||
*key = K_NOT_A_KEY;
|
||||
|
||||
struct KeyNames* kn = key_names;
|
||||
while (kn->name)
|
||||
{
|
||||
if (strcmp(k, kn->name) == 0)
|
||||
{
|
||||
*key = kn->key;
|
||||
break;
|
||||
}
|
||||
kn++;
|
||||
}
|
||||
|
||||
if (*key == K_NOT_A_KEY)
|
||||
{
|
||||
return -2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CheckKeyFlags(char* f, MMKeyFlags* flags)
|
||||
{
|
||||
if (!flags) return -1;
|
||||
|
||||
if (strcmp(f, "alt") == 0)
|
||||
{
|
||||
*flags = MOD_ALT;
|
||||
}
|
||||
else if(strcmp(f, "command") == 0)
|
||||
{
|
||||
*flags = MOD_META;
|
||||
}
|
||||
else if(strcmp(f, "control") == 0)
|
||||
{
|
||||
*flags = MOD_CONTROL;
|
||||
}
|
||||
else if(strcmp(f, "shift") == 0)
|
||||
{
|
||||
*flags = MOD_SHIFT;
|
||||
}
|
||||
else if(strcmp(f, "none") == 0)
|
||||
{
|
||||
*flags = (MMKeyFlags) MOD_NONE;
|
||||
}
|
||||
else
|
||||
{
|
||||
return -2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// //If it's not an array, it should be a single string value.
|
||||
// return GetFlagsFromString(value, flags);
|
||||
// }
|
||||
|
||||
int akeyTap(char *k){
|
||||
MMKeyFlags flags = (MMKeyFlags) MOD_NONE;
|
||||
// MMKeyFlags flags = 0;
|
||||
MMKeyCode key;
|
||||
|
||||
// char *k;
|
||||
// k = *kstr;
|
||||
|
||||
switch(CheckKeyCodes(k, &key)){
|
||||
case -1:
|
||||
return 1;
|
||||
break;
|
||||
case -2:
|
||||
return 1;
|
||||
break;
|
||||
default:
|
||||
tapKeyCode(key, flags);
|
||||
microsleep(keyboardDelay);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
char* akeyToggle(char *k,char *d){
|
||||
MMKeyFlags flags = (MMKeyFlags) MOD_NONE;
|
||||
MMKeyCode key;
|
||||
|
||||
bool down;
|
||||
// char *k;
|
||||
// k = *kstr;
|
||||
|
||||
if (d != 0){
|
||||
// char *d;
|
||||
// d = *dstr;
|
||||
if (strcmp(d, "down") == 0){
|
||||
down = true;
|
||||
}else if (strcmp(d, "up") == 0){
|
||||
down = false;
|
||||
}else{
|
||||
return "Invalid key state specified.";
|
||||
}
|
||||
}
|
||||
|
||||
switch(CheckKeyCodes(k, &key)){
|
||||
case -1:
|
||||
return "Null pointer in key code.";
|
||||
break;
|
||||
case -2:
|
||||
return "Invalid key code specified.";
|
||||
break;
|
||||
default:
|
||||
toggleKeyCode(key, down, flags);
|
||||
microsleep(keyboardDelay);
|
||||
}
|
||||
|
||||
return "success";
|
||||
}
|
||||
|
||||
void atypeString(char *str){
|
||||
typeString(str);
|
||||
}
|
||||
|
||||
void atypeStringDelayed(char *str,size_t cpm){
|
||||
|
||||
typeStringDelayed(str, cpm);
|
||||
}
|
||||
|
||||
void asetKeyboardDelay(size_t val){
|
||||
keyboardDelay =val;
|
||||
}
|
281
key/keycode.h
Normal file
281
key/keycode.h
Normal file
@ -0,0 +1,281 @@
|
||||
#pragma once
|
||||
#ifndef KEYCODE_H
|
||||
#define KEYCODE_H
|
||||
|
||||
#include "../base/os.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#if defined(IS_MACOSX)
|
||||
|
||||
#include <Carbon/Carbon.h> /* Really only need <HIToolbox/Events.h> */
|
||||
#include <ApplicationServices/ApplicationServices.h>
|
||||
#import <IOKit/hidsystem/ev_keymap.h>
|
||||
|
||||
enum _MMKeyCode {
|
||||
K_NOT_A_KEY = 9999,
|
||||
K_BACKSPACE = kVK_Delete,
|
||||
K_DELETE = kVK_ForwardDelete,
|
||||
K_RETURN = kVK_Return,
|
||||
K_TAB = kVK_Tab,
|
||||
K_ESCAPE = kVK_Escape,
|
||||
K_UP = kVK_UpArrow,
|
||||
K_DOWN = kVK_DownArrow,
|
||||
K_RIGHT = kVK_RightArrow,
|
||||
K_LEFT = kVK_LeftArrow,
|
||||
K_HOME = kVK_Home,
|
||||
K_END = kVK_End,
|
||||
K_PAGEUP = kVK_PageUp,
|
||||
K_PAGEDOWN = kVK_PageDown,
|
||||
K_F1 = kVK_F1,
|
||||
K_F2 = kVK_F2,
|
||||
K_F3 = kVK_F3,
|
||||
K_F4 = kVK_F4,
|
||||
K_F5 = kVK_F5,
|
||||
K_F6 = kVK_F6,
|
||||
K_F7 = kVK_F7,
|
||||
K_F8 = kVK_F8,
|
||||
K_F9 = kVK_F9,
|
||||
K_F10 = kVK_F10,
|
||||
K_F11 = kVK_F11,
|
||||
K_F12 = kVK_F12,
|
||||
K_F13 = kVK_F13,
|
||||
K_F14 = kVK_F14,
|
||||
K_F15 = kVK_F15,
|
||||
K_F16 = kVK_F16,
|
||||
K_F17 = kVK_F17,
|
||||
K_F18 = kVK_F18,
|
||||
K_F19 = kVK_F19,
|
||||
K_F20 = kVK_F20,
|
||||
K_F21 = K_NOT_A_KEY,
|
||||
K_F22 = K_NOT_A_KEY,
|
||||
K_F23 = K_NOT_A_KEY,
|
||||
K_F24 = K_NOT_A_KEY,
|
||||
K_META = kVK_Command,
|
||||
K_ALT = kVK_Option,
|
||||
K_CONTROL = kVK_Control,
|
||||
K_SHIFT = kVK_Shift,
|
||||
K_RIGHTSHIFT = kVK_RightShift,
|
||||
K_CAPSLOCK = kVK_CapsLock,
|
||||
K_SPACE = kVK_Space,
|
||||
K_INSERT = K_NOT_A_KEY,
|
||||
K_PRINTSCREEN = K_NOT_A_KEY,
|
||||
|
||||
K_NUMPAD_0 = kVK_ANSI_Keypad0,
|
||||
K_NUMPAD_1 = kVK_ANSI_Keypad1,
|
||||
K_NUMPAD_2 = kVK_ANSI_Keypad2,
|
||||
K_NUMPAD_3 = kVK_ANSI_Keypad3,
|
||||
K_NUMPAD_4 = kVK_ANSI_Keypad4,
|
||||
K_NUMPAD_5 = kVK_ANSI_Keypad5,
|
||||
K_NUMPAD_6 = kVK_ANSI_Keypad6,
|
||||
K_NUMPAD_7 = kVK_ANSI_Keypad7,
|
||||
K_NUMPAD_8 = kVK_ANSI_Keypad8,
|
||||
K_NUMPAD_9 = kVK_ANSI_Keypad9,
|
||||
|
||||
K_AUDIO_VOLUME_MUTE = 1007,
|
||||
K_AUDIO_VOLUME_DOWN = 1001,
|
||||
K_AUDIO_VOLUME_UP = 1000,
|
||||
K_AUDIO_PLAY = 1016,
|
||||
K_AUDIO_STOP = K_NOT_A_KEY,
|
||||
K_AUDIO_PAUSE = 1016,
|
||||
K_AUDIO_PREV = 1018,
|
||||
K_AUDIO_NEXT = 1017,
|
||||
K_AUDIO_REWIND = K_NOT_A_KEY,
|
||||
K_AUDIO_FORWARD = K_NOT_A_KEY,
|
||||
K_AUDIO_REPEAT = K_NOT_A_KEY,
|
||||
K_AUDIO_RANDOM = K_NOT_A_KEY,
|
||||
|
||||
K_LIGHTS_MON_UP = 1002,
|
||||
K_LIGHTS_MON_DOWN = 1003,
|
||||
K_LIGHTS_KBD_TOGGLE = 1023,
|
||||
K_LIGHTS_KBD_UP = 1021,
|
||||
K_LIGHTS_KBD_DOWN = 1022
|
||||
};
|
||||
|
||||
typedef CGKeyCode MMKeyCode;
|
||||
|
||||
#elif defined(USE_X11)
|
||||
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/XF86keysym.h>
|
||||
|
||||
enum _MMKeyCode {
|
||||
K_NOT_A_KEY = 9999,
|
||||
K_BACKSPACE = XK_BackSpace,
|
||||
K_DELETE = XK_Delete,
|
||||
K_RETURN = XK_Return,
|
||||
K_TAB = XK_Tab,
|
||||
K_ESCAPE = XK_Escape,
|
||||
K_UP = XK_Up,
|
||||
K_DOWN = XK_Down,
|
||||
K_RIGHT = XK_Right,
|
||||
K_LEFT = XK_Left,
|
||||
K_HOME = XK_Home,
|
||||
K_END = XK_End,
|
||||
K_PAGEUP = XK_Page_Up,
|
||||
K_PAGEDOWN = XK_Page_Down,
|
||||
K_F1 = XK_F1,
|
||||
K_F2 = XK_F2,
|
||||
K_F3 = XK_F3,
|
||||
K_F4 = XK_F4,
|
||||
K_F5 = XK_F5,
|
||||
K_F6 = XK_F6,
|
||||
K_F7 = XK_F7,
|
||||
K_F8 = XK_F8,
|
||||
K_F9 = XK_F9,
|
||||
K_F10 = XK_F10,
|
||||
K_F11 = XK_F11,
|
||||
K_F12 = XK_F12,
|
||||
K_F13 = XK_F13,
|
||||
K_F14 = XK_F14,
|
||||
K_F15 = XK_F15,
|
||||
K_F16 = XK_F16,
|
||||
K_F17 = XK_F17,
|
||||
K_F18 = XK_F18,
|
||||
K_F19 = XK_F19,
|
||||
K_F20 = XK_F20,
|
||||
K_F21 = XK_F21,
|
||||
K_F22 = XK_F22,
|
||||
K_F23 = XK_F23,
|
||||
K_F24 = XK_F24,
|
||||
K_META = XK_Super_L,
|
||||
K_ALT = XK_Alt_L,
|
||||
K_CONTROL = XK_Control_L,
|
||||
K_SHIFT = XK_Shift_L,
|
||||
K_RIGHTSHIFT = XK_Shift_R,
|
||||
K_CAPSLOCK = XK_Shift_Lock,
|
||||
K_SPACE = XK_space,
|
||||
K_INSERT = XK_Insert,
|
||||
K_PRINTSCREEN = XK_Print,
|
||||
|
||||
K_NUMPAD_0 = K_NOT_A_KEY,
|
||||
K_NUMPAD_1 = K_NOT_A_KEY,
|
||||
K_NUMPAD_2 = K_NOT_A_KEY,
|
||||
K_NUMPAD_3 = K_NOT_A_KEY,
|
||||
K_NUMPAD_4 = K_NOT_A_KEY,
|
||||
K_NUMPAD_5 = K_NOT_A_KEY,
|
||||
K_NUMPAD_6 = K_NOT_A_KEY,
|
||||
K_NUMPAD_7 = K_NOT_A_KEY,
|
||||
K_NUMPAD_8 = K_NOT_A_KEY,
|
||||
K_NUMPAD_9 = K_NOT_A_KEY,
|
||||
|
||||
K_AUDIO_VOLUME_MUTE = XF86XK_AudioMute,
|
||||
K_AUDIO_VOLUME_DOWN = XF86XK_AudioLowerVolume,
|
||||
K_AUDIO_VOLUME_UP = XF86XK_AudioRaiseVolume,
|
||||
K_AUDIO_PLAY = XF86XK_AudioPlay,
|
||||
K_AUDIO_STOP = XF86XK_AudioStop,
|
||||
K_AUDIO_PAUSE = XF86XK_AudioPause,
|
||||
K_AUDIO_PREV = XF86XK_AudioPrev,
|
||||
K_AUDIO_NEXT = XF86XK_AudioNext,
|
||||
K_AUDIO_REWIND = XF86XK_AudioRewind,
|
||||
K_AUDIO_FORWARD = XF86XK_AudioForward,
|
||||
K_AUDIO_REPEAT = XF86XK_AudioRepeat,
|
||||
K_AUDIO_RANDOM = XF86XK_AudioRandomPlay,
|
||||
|
||||
K_LIGHTS_MON_UP = XF86XK_MonBrightnessUp,
|
||||
K_LIGHTS_MON_DOWN = XF86XK_MonBrightnessDown,
|
||||
K_LIGHTS_KBD_TOGGLE = XF86XK_KbdLightOnOff,
|
||||
K_LIGHTS_KBD_UP = XF86XK_KbdBrightnessUp,
|
||||
K_LIGHTS_KBD_DOWN = XF86XK_KbdBrightnessDown
|
||||
};
|
||||
|
||||
typedef KeySym MMKeyCode;
|
||||
|
||||
#elif defined(IS_WINDOWS)
|
||||
|
||||
enum _MMKeyCode {
|
||||
K_NOT_A_KEY = 9999,
|
||||
K_BACKSPACE = VK_BACK,
|
||||
K_DELETE = VK_DELETE,
|
||||
K_RETURN = VK_RETURN,
|
||||
K_TAB = VK_TAB,
|
||||
K_ESCAPE = VK_ESCAPE,
|
||||
K_UP = VK_UP,
|
||||
K_DOWN = VK_DOWN,
|
||||
K_RIGHT = VK_RIGHT,
|
||||
K_LEFT = VK_LEFT,
|
||||
K_HOME = VK_HOME,
|
||||
K_END = VK_END,
|
||||
K_PAGEUP = VK_PRIOR,
|
||||
K_PAGEDOWN = VK_NEXT,
|
||||
K_F1 = VK_F1,
|
||||
K_F2 = VK_F2,
|
||||
K_F3 = VK_F3,
|
||||
K_F4 = VK_F4,
|
||||
K_F5 = VK_F5,
|
||||
K_F6 = VK_F6,
|
||||
K_F7 = VK_F7,
|
||||
K_F8 = VK_F8,
|
||||
K_F9 = VK_F9,
|
||||
K_F10 = VK_F10,
|
||||
K_F11 = VK_F11,
|
||||
K_F12 = VK_F12,
|
||||
K_F13 = VK_F13,
|
||||
K_F14 = VK_F14,
|
||||
K_F15 = VK_F15,
|
||||
K_F16 = VK_F16,
|
||||
K_F17 = VK_F17,
|
||||
K_F18 = VK_F18,
|
||||
K_F19 = VK_F19,
|
||||
K_F20 = VK_F20,
|
||||
K_F21 = VK_F21,
|
||||
K_F22 = VK_F22,
|
||||
K_F23 = VK_F23,
|
||||
K_F24 = VK_F24,
|
||||
K_META = VK_LWIN,
|
||||
K_CONTROL = VK_CONTROL,
|
||||
K_SHIFT = VK_SHIFT,
|
||||
K_RIGHTSHIFT = VK_RSHIFT,
|
||||
K_ALT = VK_MENU,
|
||||
K_CAPSLOCK = VK_CAPITAL,
|
||||
K_SPACE = VK_SPACE,
|
||||
K_PRINTSCREEN = VK_SNAPSHOT,
|
||||
K_INSERT = VK_INSERT,
|
||||
|
||||
K_NUMPAD_0 = VK_NUMPAD0,
|
||||
K_NUMPAD_1 = VK_NUMPAD1,
|
||||
K_NUMPAD_2 = VK_NUMPAD2,
|
||||
K_NUMPAD_3 = VK_NUMPAD3,
|
||||
K_NUMPAD_4 = VK_NUMPAD4,
|
||||
K_NUMPAD_5 = VK_NUMPAD5,
|
||||
K_NUMPAD_6 = VK_NUMPAD6,
|
||||
K_NUMPAD_7 = VK_NUMPAD7,
|
||||
K_NUMPAD_8 = VK_NUMPAD8,
|
||||
K_NUMPAD_9 = VK_NUMPAD9,
|
||||
|
||||
K_AUDIO_VOLUME_MUTE = VK_VOLUME_MUTE,
|
||||
K_AUDIO_VOLUME_DOWN = VK_VOLUME_DOWN,
|
||||
K_AUDIO_VOLUME_UP = VK_VOLUME_UP,
|
||||
K_AUDIO_PLAY = VK_MEDIA_PLAY_PAUSE,
|
||||
K_AUDIO_STOP = VK_MEDIA_STOP,
|
||||
K_AUDIO_PAUSE = VK_MEDIA_PLAY_PAUSE,
|
||||
K_AUDIO_PREV = VK_MEDIA_PREV_TRACK,
|
||||
K_AUDIO_NEXT = VK_MEDIA_NEXT_TRACK,
|
||||
K_AUDIO_REWIND = K_NOT_A_KEY,
|
||||
K_AUDIO_FORWARD = K_NOT_A_KEY,
|
||||
K_AUDIO_REPEAT = K_NOT_A_KEY,
|
||||
K_AUDIO_RANDOM = K_NOT_A_KEY,
|
||||
|
||||
K_LIGHTS_MON_UP = K_NOT_A_KEY,
|
||||
K_LIGHTS_MON_DOWN = K_NOT_A_KEY,
|
||||
K_LIGHTS_KBD_TOGGLE = K_NOT_A_KEY,
|
||||
K_LIGHTS_KBD_UP = K_NOT_A_KEY,
|
||||
K_LIGHTS_KBD_DOWN = K_NOT_A_KEY
|
||||
};
|
||||
|
||||
typedef int MMKeyCode;
|
||||
|
||||
#endif
|
||||
|
||||
/* Returns the keyCode corresponding to the current keyboard layout for the
|
||||
* given ASCII character. */
|
||||
MMKeyCode keyCodeForChar(const char c);
|
||||
|
||||
#endif /* KEYCODE_H */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
163
key/keycode_init.h
Normal file
163
key/keycode_init.h
Normal file
@ -0,0 +1,163 @@
|
||||
#include "keycode.h"
|
||||
|
||||
#if defined(IS_MACOSX)
|
||||
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <Carbon/Carbon.h> /* For kVK_ constants, and TIS functions. */
|
||||
|
||||
/* Returns string representation of key, if it is printable.
|
||||
* Ownership follows the Create Rule; that is, it is the caller's
|
||||
* responsibility to release the returned object. */
|
||||
CFStringRef createStringForKey(CGKeyCode keyCode);
|
||||
|
||||
#elif defined(USE_X11)
|
||||
|
||||
/*
|
||||
* Structs to store key mappings not handled by XStringToKeysym() on some
|
||||
* Linux systems.
|
||||
*/
|
||||
|
||||
struct XSpecialCharacterMapping {
|
||||
char name;
|
||||
MMKeyCode code;
|
||||
};
|
||||
|
||||
struct XSpecialCharacterMapping XSpecialCharacterTable[] = {
|
||||
{'~', XK_asciitilde},
|
||||
{'_', XK_underscore},
|
||||
{'[', XK_bracketleft},
|
||||
{']', XK_bracketright},
|
||||
{'!', XK_exclam},
|
||||
{'\'', XK_quotedbl},
|
||||
{'#', XK_numbersign},
|
||||
{'$', XK_dollar},
|
||||
{'%', XK_percent},
|
||||
{'&', XK_ampersand},
|
||||
{'\'', XK_quoteright},
|
||||
{'*', XK_asterisk},
|
||||
{'+', XK_plus},
|
||||
{',', XK_comma},
|
||||
{'-', XK_minus},
|
||||
{'.', XK_period},
|
||||
{'?', XK_question},
|
||||
{'<', XK_less},
|
||||
{'>', XK_greater},
|
||||
{'=', XK_equal},
|
||||
{'@', XK_at},
|
||||
{':', XK_colon},
|
||||
{';', XK_semicolon},
|
||||
{'\\', XK_backslash},
|
||||
{'`', XK_grave},
|
||||
{'{', XK_braceleft},
|
||||
{'}', XK_braceright},
|
||||
{'|', XK_bar},
|
||||
{'^', XK_asciicircum},
|
||||
{'(', XK_parenleft},
|
||||
{')', XK_parenright},
|
||||
{' ', XK_space},
|
||||
{'/', XK_slash},
|
||||
{'\t', XK_Tab},
|
||||
{'\n', XK_Return}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
MMKeyCode keyCodeForChar(const char c)
|
||||
{
|
||||
#if defined(IS_MACOSX)
|
||||
/* OS X does not appear to have a built-in function for this, so instead we
|
||||
* have to write our own. */
|
||||
static CFMutableDictionaryRef charToCodeDict = NULL;
|
||||
CGKeyCode code;
|
||||
UniChar character = c;
|
||||
CFStringRef charStr = NULL;
|
||||
|
||||
/* Generate table of keycodes and characters. */
|
||||
if (charToCodeDict == NULL) {
|
||||
size_t i;
|
||||
charToCodeDict = CFDictionaryCreateMutable(kCFAllocatorDefault,
|
||||
128,
|
||||
&kCFCopyStringDictionaryKeyCallBacks,
|
||||
NULL);
|
||||
if (charToCodeDict == NULL) return UINT16_MAX;
|
||||
|
||||
/* Loop through every keycode (0 - 127) to find its current mapping. */
|
||||
for (i = 0; i < 128; ++i) {
|
||||
CFStringRef string = createStringForKey((CGKeyCode)i);
|
||||
if (string != NULL) {
|
||||
CFDictionaryAddValue(charToCodeDict, string, (const void *)i);
|
||||
CFRelease(string);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
charStr = CFStringCreateWithCharacters(kCFAllocatorDefault, &character, 1);
|
||||
|
||||
/* Our values may be NULL (0), so we need to use this function. */
|
||||
if (!CFDictionaryGetValueIfPresent(charToCodeDict, charStr,
|
||||
(const void **)&code)) {
|
||||
code = UINT16_MAX; /* Error */
|
||||
}
|
||||
|
||||
CFRelease(charStr);
|
||||
return (MMKeyCode)code;
|
||||
#elif defined(IS_WINDOWS)
|
||||
return VkKeyScan(c);
|
||||
#elif defined(USE_X11)
|
||||
MMKeyCode code;
|
||||
|
||||
char buf[2];
|
||||
buf[0] = c;
|
||||
buf[1] = '\0';
|
||||
|
||||
code = XStringToKeysym(buf);
|
||||
if (code == NoSymbol) {
|
||||
/* Some special keys are apparently not handled properly by
|
||||
* XStringToKeysym() on some systems, so search for them instead in our
|
||||
* mapping table. */
|
||||
size_t i;
|
||||
const size_t specialCharacterCount =
|
||||
sizeof(XSpecialCharacterTable) / sizeof(XSpecialCharacterTable[0]);
|
||||
for (i = 0; i < specialCharacterCount; ++i) {
|
||||
if (c == XSpecialCharacterTable[i].name) {
|
||||
code = XSpecialCharacterTable[i].code;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return code;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(IS_MACOSX)
|
||||
|
||||
CFStringRef createStringForKey(CGKeyCode keyCode)
|
||||
{
|
||||
TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource();
|
||||
CFDataRef layoutData =
|
||||
TISGetInputSourceProperty(currentKeyboard,
|
||||
kTISPropertyUnicodeKeyLayoutData);
|
||||
const UCKeyboardLayout *keyboardLayout =
|
||||
(const UCKeyboardLayout *)CFDataGetBytePtr(layoutData);
|
||||
|
||||
UInt32 keysDown = 0;
|
||||
UniChar chars[4];
|
||||
UniCharCount realLength;
|
||||
|
||||
UCKeyTranslate(keyboardLayout,
|
||||
keyCode,
|
||||
kUCKeyActionDisplay,
|
||||
0,
|
||||
LMGetKbdType(),
|
||||
kUCKeyTranslateNoDeadKeysBit,
|
||||
&keysDown,
|
||||
sizeof(chars) / sizeof(chars[0]),
|
||||
&realLength,
|
||||
chars);
|
||||
CFRelease(currentKeyboard);
|
||||
|
||||
return CFStringCreateWithCharacters(kCFAllocatorDefault, chars, 1);
|
||||
}
|
||||
|
||||
#endif
|
85
key/keypress.h
Normal file
85
key/keypress.h
Normal file
@ -0,0 +1,85 @@
|
||||
#pragma once
|
||||
#ifndef KEYPRESS_H
|
||||
#define KEYPRESS_H
|
||||
|
||||
#include "../base/os.h"
|
||||
#include "keycode.h"
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#include "../base/ms_stdbool.h"
|
||||
#else
|
||||
#include <stdbool.h>
|
||||
#endif
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
#if defined(IS_MACOSX)
|
||||
|
||||
typedef enum {
|
||||
MOD_NONE = 0,
|
||||
MOD_META = kCGEventFlagMaskCommand,
|
||||
MOD_ALT = kCGEventFlagMaskAlternate,
|
||||
MOD_CONTROL = kCGEventFlagMaskControl,
|
||||
MOD_SHIFT = kCGEventFlagMaskShift
|
||||
} MMKeyFlags;
|
||||
|
||||
#elif defined(USE_X11)
|
||||
|
||||
enum _MMKeyFlags {
|
||||
MOD_NONE = 0,
|
||||
MOD_META = Mod4Mask,
|
||||
MOD_ALT = Mod1Mask,
|
||||
MOD_CONTROL = ControlMask,
|
||||
MOD_SHIFT = ShiftMask
|
||||
};
|
||||
|
||||
typedef unsigned int MMKeyFlags;
|
||||
|
||||
#elif defined(IS_WINDOWS)
|
||||
|
||||
enum _MMKeyFlags {
|
||||
MOD_NONE = 0,
|
||||
/* These are already defined by the Win32 API */
|
||||
/* MOD_ALT = 0,
|
||||
MOD_CONTROL = 0,
|
||||
MOD_SHIFT = 0, */
|
||||
MOD_META = MOD_WIN
|
||||
};
|
||||
|
||||
typedef unsigned int MMKeyFlags;
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(IS_WINDOWS)
|
||||
/* Send win32 key event for given key. */
|
||||
void win32KeyEvent(int key, MMKeyFlags flags);
|
||||
#endif
|
||||
|
||||
/* Toggles the given key down or up. */
|
||||
void toggleKeyCode(MMKeyCode code, const bool down, MMKeyFlags flags);
|
||||
|
||||
/* Toggles the key down and then up. */
|
||||
void tapKeyCode(MMKeyCode code, MMKeyFlags flags);
|
||||
|
||||
/* Toggles the key corresponding to the given UTF character up or down. */
|
||||
void toggleKey(char c, const bool down, MMKeyFlags flags);
|
||||
void tapKey(char c, MMKeyFlags flags);
|
||||
|
||||
/* Sends a UTF-8 string without modifiers. */
|
||||
void typeString(const char *str);
|
||||
|
||||
/* Macro to convert WPM to CPM integers.
|
||||
* (the average English word length is 5.1 characters.) */
|
||||
#define WPM_TO_CPM(WPM) (unsigned)(5.1 * WPM)
|
||||
|
||||
/* Sends a string with partially random delays between each letter. Note that
|
||||
* deadbeef_srand() must be called before this function if you actually want
|
||||
* randomness. */
|
||||
void typeStringDelayed(const char *str, const unsigned cpm);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* KEYPRESS_H */
|
245
key/keypress_init.h
Normal file
245
key/keypress_init.h
Normal file
@ -0,0 +1,245 @@
|
||||
#include "keypress.h"
|
||||
// #include "../base/deadbeef_rand_init.h"
|
||||
#include "../base/microsleep.h"
|
||||
|
||||
#include <ctype.h> /* For isupper() */
|
||||
|
||||
#if defined(IS_MACOSX)
|
||||
#include <ApplicationServices/ApplicationServices.h>
|
||||
#import <IOKit/hidsystem/IOHIDLib.h>
|
||||
#import <IOKit/hidsystem/ev_keymap.h>
|
||||
#elif defined(USE_X11)
|
||||
#include <X11/extensions/XTest.h>
|
||||
// #include "../base/xdisplay_init.h"
|
||||
#endif
|
||||
|
||||
/* Convenience wrappers around ugly APIs. */
|
||||
#if defined(IS_WINDOWS)
|
||||
#define WIN32_KEY_EVENT_WAIT(key, flags) \
|
||||
(win32KeyEvent(key, flags), Sleep(DEADBEEF_RANDRANGE(0, 63)))
|
||||
#elif defined(USE_X11)
|
||||
#define X_KEY_EVENT(display, key, is_press) \
|
||||
(XTestFakeKeyEvent(display, \
|
||||
XKeysymToKeycode(display, key), \
|
||||
is_press, CurrentTime), \
|
||||
XFlush(display))
|
||||
#define X_KEY_EVENT_WAIT(display, key, is_press) \
|
||||
(X_KEY_EVENT(display, key, is_press), \
|
||||
microsleep(DEADBEEF_UNIFORM(0.0, 62.5)))
|
||||
#endif
|
||||
|
||||
#if defined(IS_MACOSX)
|
||||
static io_connect_t _getAuxiliaryKeyDriver(void)
|
||||
{
|
||||
static mach_port_t sEventDrvrRef = 0;
|
||||
mach_port_t masterPort, service, iter;
|
||||
kern_return_t kr;
|
||||
|
||||
if (!sEventDrvrRef) {
|
||||
kr = IOMasterPort( bootstrap_port, &masterPort );
|
||||
assert(KERN_SUCCESS == kr);
|
||||
kr = IOServiceGetMatchingServices(masterPort, IOServiceMatching( kIOHIDSystemClass), &iter );
|
||||
assert(KERN_SUCCESS == kr);
|
||||
service = IOIteratorNext( iter );
|
||||
assert(service);
|
||||
kr = IOServiceOpen(service, mach_task_self(), kIOHIDParamConnectType, &sEventDrvrRef );
|
||||
assert(KERN_SUCCESS == kr);
|
||||
IOObjectRelease(service);
|
||||
IOObjectRelease(iter);
|
||||
}
|
||||
return sEventDrvrRef;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(IS_WINDOWS)
|
||||
void win32KeyEvent(int key, MMKeyFlags flags)
|
||||
{
|
||||
int scan = MapVirtualKey(key & 0xff, MAPVK_VK_TO_VSC);
|
||||
|
||||
/* Set the scan code for extended keys */
|
||||
switch (key)
|
||||
{
|
||||
case VK_RCONTROL:
|
||||
case VK_SNAPSHOT: /* Print Screen */
|
||||
case VK_RMENU: /* Right Alt / Alt Gr */
|
||||
case VK_PAUSE: /* Pause / Break */
|
||||
case VK_HOME:
|
||||
case VK_UP:
|
||||
case VK_PRIOR: /* Page up */
|
||||
case VK_LEFT:
|
||||
case VK_RIGHT:
|
||||
case VK_END:
|
||||
case VK_DOWN:
|
||||
case VK_NEXT: /* 'Page Down' */
|
||||
case VK_INSERT:
|
||||
case VK_DELETE:
|
||||
case VK_LWIN:
|
||||
case VK_RWIN:
|
||||
case VK_APPS: /* Application */
|
||||
case VK_VOLUME_MUTE:
|
||||
case VK_VOLUME_DOWN:
|
||||
case VK_VOLUME_UP:
|
||||
case VK_MEDIA_NEXT_TRACK:
|
||||
case VK_MEDIA_PREV_TRACK:
|
||||
case VK_MEDIA_STOP:
|
||||
case VK_MEDIA_PLAY_PAUSE:
|
||||
case VK_BROWSER_BACK:
|
||||
case VK_BROWSER_FORWARD:
|
||||
case VK_BROWSER_REFRESH:
|
||||
case VK_BROWSER_STOP:
|
||||
case VK_BROWSER_SEARCH:
|
||||
case VK_BROWSER_FAVORITES:
|
||||
case VK_BROWSER_HOME:
|
||||
case VK_LAUNCH_MAIL:
|
||||
{
|
||||
flags |= KEYEVENTF_EXTENDEDKEY;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set the scan code for keyup */
|
||||
if ( flags & KEYEVENTF_KEYUP ) {
|
||||
scan |= 0x80;
|
||||
}
|
||||
|
||||
keybd_event(key, scan, flags, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
void toggleKeyCode(MMKeyCode code, const bool down, MMKeyFlags flags)
|
||||
{
|
||||
#if defined(IS_MACOSX)
|
||||
/* The media keys all have 1000 added to them to help us detect them. */
|
||||
if (code >= 1000) {
|
||||
code = code - 1000; /* Get the real keycode. */
|
||||
NXEventData event;
|
||||
kern_return_t kr;
|
||||
IOGPoint loc = { 0, 0 };
|
||||
UInt32 evtInfo = code << 16 | (down?NX_KEYDOWN:NX_KEYUP) << 8;
|
||||
bzero(&event, sizeof(NXEventData));
|
||||
event.compound.subType = NX_SUBTYPE_AUX_CONTROL_BUTTONS;
|
||||
event.compound.misc.L[0] = evtInfo;
|
||||
kr = IOHIDPostEvent( _getAuxiliaryKeyDriver(), NX_SYSDEFINED, loc, &event, kNXEventDataVersion, 0, FALSE );
|
||||
assert( KERN_SUCCESS == kr );
|
||||
} else {
|
||||
CGEventRef keyEvent = CGEventCreateKeyboardEvent(NULL,
|
||||
(CGKeyCode)code, down);
|
||||
assert(keyEvent != NULL);
|
||||
|
||||
CGEventSetType(keyEvent, down ? kCGEventKeyDown : kCGEventKeyUp);
|
||||
CGEventSetFlags(keyEvent, (int) flags);
|
||||
// CGEventSetFlags(keyEvent, 0);
|
||||
// CGEventSetFlags(keyEvent, kCGEventFlagMaskShift | kCGEventFlagMaskCommand);
|
||||
CGEventPost(kCGSessionEventTap, keyEvent);
|
||||
CFRelease(keyEvent);
|
||||
}
|
||||
#elif defined(IS_WINDOWS)
|
||||
const DWORD dwFlags = down ? 0 : KEYEVENTF_KEYUP;
|
||||
|
||||
/* Parse modifier keys. */
|
||||
if (flags & MOD_META) WIN32_KEY_EVENT_WAIT(K_META, dwFlags);
|
||||
if (flags & MOD_ALT) WIN32_KEY_EVENT_WAIT(K_ALT, dwFlags);
|
||||
if (flags & MOD_CONTROL) WIN32_KEY_EVENT_WAIT(K_CONTROL, dwFlags);
|
||||
if (flags & MOD_SHIFT) WIN32_KEY_EVENT_WAIT(K_SHIFT, dwFlags);
|
||||
|
||||
win32KeyEvent(code, dwFlags);
|
||||
#elif defined(USE_X11)
|
||||
Display *display = XGetMainDisplay();
|
||||
const Bool is_press = down ? True : False; /* Just to be safe. */
|
||||
|
||||
/* Parse modifier keys. */
|
||||
if (flags & MOD_META) X_KEY_EVENT_WAIT(display, K_META, is_press);
|
||||
if (flags & MOD_ALT) X_KEY_EVENT_WAIT(display, K_ALT, is_press);
|
||||
if (flags & MOD_CONTROL) X_KEY_EVENT_WAIT(display, K_CONTROL, is_press);
|
||||
if (flags & MOD_SHIFT) X_KEY_EVENT_WAIT(display, K_SHIFT, is_press);
|
||||
|
||||
X_KEY_EVENT(display, code, is_press);
|
||||
#endif
|
||||
}
|
||||
|
||||
void tapKeyCode(MMKeyCode code, MMKeyFlags flags)
|
||||
{
|
||||
toggleKeyCode(code, true, flags);
|
||||
toggleKeyCode(code, false, flags);
|
||||
}
|
||||
|
||||
void toggleKey(char c, const bool down, MMKeyFlags flags)
|
||||
{
|
||||
MMKeyCode keyCode = keyCodeForChar(c);
|
||||
|
||||
//Prevent unused variable warning for Mac and Linux.
|
||||
#if defined(IS_WINDOWS)
|
||||
int modifiers;
|
||||
#endif
|
||||
|
||||
if (isupper(c) && !(flags & MOD_SHIFT)) {
|
||||
flags |= MOD_SHIFT; /* Not sure if this is safe for all layouts. */
|
||||
}
|
||||
|
||||
#if defined(IS_WINDOWS)
|
||||
modifiers = keyCode >> 8; // Pull out modifers.
|
||||
if ((modifiers & 1) != 0) flags |= MOD_SHIFT; // Uptdate flags from keycode modifiers.
|
||||
if ((modifiers & 2) != 0) flags |= MOD_CONTROL;
|
||||
if ((modifiers & 4) != 0) flags |= MOD_ALT;
|
||||
keyCode = keyCode & 0xff; // Mask out modifiers.
|
||||
#endif
|
||||
toggleKeyCode(keyCode, down, flags);
|
||||
}
|
||||
|
||||
void tapKey(char c, MMKeyFlags flags)
|
||||
{
|
||||
toggleKey(c, true, flags);
|
||||
toggleKey(c, false, flags);
|
||||
}
|
||||
|
||||
#if defined(IS_MACOSX)
|
||||
void toggleUniKey(char c, const bool down)
|
||||
{
|
||||
/* This function relies on the convenient
|
||||
* CGEventKeyboardSetUnicodeString(), which allows us to not have to
|
||||
* convert characters to a keycode, but does not support adding modifier
|
||||
* flags. It is therefore only used in typeString() and typeStringDelayed()
|
||||
* -- if you need modifier keys, use the above functions instead. */
|
||||
UniChar ch = (UniChar)c; /* Convert to unsigned char */
|
||||
|
||||
CGEventRef keyEvent = CGEventCreateKeyboardEvent(NULL, 0, down);
|
||||
if (keyEvent == NULL) {
|
||||
fputs("Could not create keyboard event.\n", stderr);
|
||||
return;
|
||||
}
|
||||
|
||||
CGEventKeyboardSetUnicodeString(keyEvent, 1, &ch);
|
||||
|
||||
CGEventPost(kCGSessionEventTap, keyEvent);
|
||||
CFRelease(keyEvent);
|
||||
}
|
||||
#else
|
||||
#define toggleUniKey(c, down) toggleKey(c, down, MOD_NONE)
|
||||
#endif
|
||||
|
||||
static void tapUniKey(char c)
|
||||
{
|
||||
toggleUniKey(c, true);
|
||||
toggleUniKey(c, false);
|
||||
}
|
||||
|
||||
void typeString(const char *str)
|
||||
{
|
||||
while (*str != '\0') {
|
||||
tapUniKey(*str++);
|
||||
}
|
||||
}
|
||||
|
||||
void typeStringDelayed(const char *str, const unsigned cpm)
|
||||
{
|
||||
/* Characters per second */
|
||||
const double cps = (double)cpm / 60.0;
|
||||
|
||||
/* Average milli-seconds per character */
|
||||
const double mspc = (cps == 0.0) ? 0.0 : 1000.0 / cps;
|
||||
|
||||
while (*str != '\0') {
|
||||
tapUniKey(*str++);
|
||||
microsleep(mspc + (DEADBEEF_UNIFORM(0.0, 62.5)));
|
||||
}
|
||||
}
|
121
mouse/goMouse.h
Normal file
121
mouse/goMouse.h
Normal file
@ -0,0 +1,121 @@
|
||||
#include "../base/types.h"
|
||||
#include "mouse_init.h"
|
||||
|
||||
//Global delays.
|
||||
int mouseDelay = 10;
|
||||
// int keyboardDelay = 10;
|
||||
|
||||
|
||||
// int CheckMouseButton(const char * const b, MMMouseButton * const button){
|
||||
// if (!button) return -1;
|
||||
|
||||
// if (strcmp(b, "left") == 0)
|
||||
// {
|
||||
// *button = LEFT_BUTTON;
|
||||
// }
|
||||
// else if (strcmp(b, "right") == 0)
|
||||
// {
|
||||
// *button = RIGHT_BUTTON;
|
||||
// }
|
||||
// else if (strcmp(b, "middle") == 0)
|
||||
// {
|
||||
// *button = CENTER_BUTTON;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// return -2;
|
||||
// }
|
||||
|
||||
// return 0;
|
||||
// }
|
||||
|
||||
int amoveMouse(int x, int y){
|
||||
MMPoint point;
|
||||
//int x =103;
|
||||
//int y =104;
|
||||
point = MMPointMake(x, y);
|
||||
moveMouse(point);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int adragMouse(int x, int y){
|
||||
// const size_t x=10;
|
||||
// const size_t y=20;
|
||||
MMMouseButton button = LEFT_BUTTON;
|
||||
|
||||
MMPoint point;
|
||||
point = MMPointMake(x, y);
|
||||
dragMouse(point, button);
|
||||
microsleep(mouseDelay);
|
||||
|
||||
// printf("%s\n","gyp-----");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int amoveMouseSmooth(int x, int y){
|
||||
MMPoint point;
|
||||
point = MMPointMake(x, y);
|
||||
smoothlyMoveMouse(point);
|
||||
microsleep(mouseDelay);
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
MMPoint agetMousePos(){
|
||||
MMPoint pos = getMousePos();
|
||||
|
||||
//Return object with .x and .y.
|
||||
// printf("%zu\n%zu\n", pos.x, pos.y );
|
||||
return pos;
|
||||
}
|
||||
|
||||
int amouseClick(){
|
||||
MMMouseButton button = LEFT_BUTTON;
|
||||
bool doubleC = false;
|
||||
|
||||
if (!doubleC){
|
||||
clickMouse(button);
|
||||
}else{
|
||||
doubleClick(button);
|
||||
}
|
||||
|
||||
microsleep(mouseDelay);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int amouseToggle(){
|
||||
MMMouseButton button = LEFT_BUTTON;
|
||||
bool down = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int asetMouseDelay(int val){
|
||||
// int val=10;
|
||||
mouseDelay = val;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ascrollMouse(int scrollMagnitude,char *s){
|
||||
// int scrollMagnitude = 20;
|
||||
|
||||
MMMouseWheelDirection scrollDirection;
|
||||
|
||||
if (strcmp(s, "up") == 0){
|
||||
scrollDirection = DIRECTION_UP;
|
||||
}else if (strcmp(s, "down") == 0){
|
||||
scrollDirection = DIRECTION_DOWN;
|
||||
}else{
|
||||
// return "Invalid scroll direction specified.";
|
||||
return 1;
|
||||
}
|
||||
|
||||
scrollMouse(scrollMagnitude, scrollDirection);
|
||||
microsleep(mouseDelay);
|
||||
|
||||
return 0;
|
||||
}
|
103
mouse/mouse.h
Normal file
103
mouse/mouse.h
Normal file
@ -0,0 +1,103 @@
|
||||
#pragma once
|
||||
#ifndef MOUSE_H
|
||||
#define MOUSE_H
|
||||
|
||||
#include "../base/os.h"
|
||||
#include "../base/types.h"
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#include "../base/ms_stdbool.h"
|
||||
#else
|
||||
#include <stdbool.h>
|
||||
#endif
|
||||
#ifdef __cplusplus
|
||||
// #ifdefined(__cplusplus)||defined(c_plusplus)
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
#if defined(IS_MACOSX)
|
||||
|
||||
// #include </System/Library/Frameworks/ApplicationServices.framework/Headers/ApplicationServices.h>
|
||||
#include <ApplicationServices/ApplicationServices.h>
|
||||
// #include </System/Library/Frameworks/ApplicationServices.framework/Versions/A/Headers/ApplicationServices.h>
|
||||
|
||||
typedef enum {
|
||||
LEFT_BUTTON = kCGMouseButtonLeft,
|
||||
RIGHT_BUTTON = kCGMouseButtonRight,
|
||||
CENTER_BUTTON = kCGMouseButtonCenter
|
||||
} MMMouseButton;
|
||||
|
||||
#elif defined(USE_X11)
|
||||
|
||||
enum _MMMouseButton {
|
||||
LEFT_BUTTON = 1,
|
||||
CENTER_BUTTON = 2,
|
||||
RIGHT_BUTTON = 3
|
||||
};
|
||||
typedef unsigned int MMMouseButton;
|
||||
|
||||
#elif defined(IS_WINDOWS)
|
||||
|
||||
enum _MMMouseButton {
|
||||
LEFT_BUTTON = 1,
|
||||
CENTER_BUTTON = 2,
|
||||
RIGHT_BUTTON = 3
|
||||
};
|
||||
typedef unsigned int MMMouseButton;
|
||||
|
||||
#else
|
||||
#error "No mouse button constants set for platform"
|
||||
#endif
|
||||
|
||||
#define MMMouseButtonIsValid(button) \
|
||||
(button == LEFT_BUTTON || button == RIGHT_BUTTON || \
|
||||
button == CENTER_BUTTON)
|
||||
|
||||
enum __MMMouseWheelDirection
|
||||
{
|
||||
DIRECTION_DOWN = -1,
|
||||
DIRECTION_UP = 1
|
||||
};
|
||||
typedef int MMMouseWheelDirection;
|
||||
|
||||
/* Immediately moves the mouse to the given point on-screen.
|
||||
* It is up to the caller to ensure that this point is within the
|
||||
* screen boundaries. */
|
||||
void moveMouse(MMPoint point);
|
||||
|
||||
/* Like moveMouse, moves the mouse to the given point on-screen, but marks
|
||||
* the event as the mouse being dragged on platforms where it is supported.
|
||||
* It is up to the caller to ensure that this point is within the screen
|
||||
* boundaries. */
|
||||
void dragMouse(MMPoint point, const MMMouseButton button);
|
||||
|
||||
/* Smoothly moves the mouse from the current position to the given point.
|
||||
* deadbeef_srand() should be called before using this function.
|
||||
*
|
||||
* Returns false if unsuccessful (i.e. a point was hit that is outside of the
|
||||
* screen boundaries), or true if successful. */
|
||||
bool smoothlyMoveMouse(MMPoint point);
|
||||
|
||||
/* Returns the coordinates of the mouse on the current screen. */
|
||||
MMPoint getMousePos(void);
|
||||
|
||||
/* Holds down or releases the mouse with the given button in the current
|
||||
* position. */
|
||||
void toggleMouse(bool down, MMMouseButton button);
|
||||
|
||||
/* Clicks the mouse with the given button in the current position. */
|
||||
void clickMouse(MMMouseButton button);
|
||||
|
||||
/* Double clicks the mouse with the given button. */
|
||||
void doubleClick(MMMouseButton button);
|
||||
|
||||
/* Scrolls the mouse in the stated direction.
|
||||
* TODO: Add a smoothly scroll mouse next. */
|
||||
void scrollMouse(int scrollMagnitude, MMMouseWheelDirection scrollDirection);
|
||||
|
||||
#endif /* MOUSE_H */
|
||||
|
||||
//#ifdefined(__cplusplus)||defined(c_plusplus)
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
365
mouse/mouse_init.h
Normal file
365
mouse/mouse_init.h
Normal file
@ -0,0 +1,365 @@
|
||||
#include "mouse.h"
|
||||
// #include "../screen/screen.h"
|
||||
// #include "../screen/screen_init.h"
|
||||
#include "../base/deadbeef_rand_init.h"
|
||||
// #include "../deadbeef_rand.h"
|
||||
#include "../base/microsleep.h"
|
||||
|
||||
#include <math.h> /* For floor() */
|
||||
|
||||
#if defined(IS_MACOSX)
|
||||
// #include </System/Library/Frameworks/ApplicationServices.framework/Headers/ApplicationServices.h>
|
||||
#include <ApplicationServices/ApplicationServices.h>
|
||||
// #include </System/Library/Frameworks/ApplicationServices.framework/Versions/A/Headers/ApplicationServices.h>
|
||||
#elif defined(USE_X11)
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/extensions/XTest.h>
|
||||
#include <stdlib.h>
|
||||
// #include "../base/xdisplay_init.h"
|
||||
#endif
|
||||
|
||||
#if !defined(M_SQRT2)
|
||||
#define M_SQRT2 1.4142135623730950488016887 /* Fix for MSVC. */
|
||||
#endif
|
||||
|
||||
/* Some convenience macros for converting our enums to the system API types. */
|
||||
#if defined(IS_MACOSX)
|
||||
|
||||
#define MMMouseToCGEventType(down, button) \
|
||||
(down ? MMMouseDownToCGEventType(button) : MMMouseUpToCGEventType(button))
|
||||
|
||||
#define MMMouseDownToCGEventType(button) \
|
||||
((button) == (LEFT_BUTTON) ? kCGEventLeftMouseDown \
|
||||
: ((button) == RIGHT_BUTTON ? kCGEventRightMouseDown \
|
||||
: kCGEventOtherMouseDown))
|
||||
|
||||
#define MMMouseUpToCGEventType(button) \
|
||||
((button) == LEFT_BUTTON ? kCGEventLeftMouseUp \
|
||||
: ((button) == RIGHT_BUTTON ? kCGEventRightMouseUp \
|
||||
: kCGEventOtherMouseUp))
|
||||
|
||||
#define MMMouseDragToCGEventType(button) \
|
||||
((button) == LEFT_BUTTON ? kCGEventLeftMouseDragged \
|
||||
: ((button) == RIGHT_BUTTON ? kCGEventRightMouseDragged \
|
||||
: kCGEventOtherMouseDragged))
|
||||
|
||||
#elif defined(IS_WINDOWS)
|
||||
|
||||
#define MMMouseToMEventF(down, button) \
|
||||
(down ? MMMouseDownToMEventF(button) : MMMouseUpToMEventF(button))
|
||||
|
||||
#define MMMouseUpToMEventF(button) \
|
||||
((button) == LEFT_BUTTON ? MOUSEEVENTF_LEFTUP \
|
||||
: ((button) == RIGHT_BUTTON ? MOUSEEVENTF_RIGHTUP \
|
||||
: MOUSEEVENTF_MIDDLEUP))
|
||||
|
||||
#define MMMouseDownToMEventF(button) \
|
||||
((button) == LEFT_BUTTON ? MOUSEEVENTF_LEFTDOWN \
|
||||
: ((button) == RIGHT_BUTTON ? MOUSEEVENTF_RIGHTDOWN \
|
||||
: MOUSEEVENTF_MIDDLEDOWN))
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(IS_MACOSX)
|
||||
/**
|
||||
* Calculate the delta for a mouse move and add them to the event.
|
||||
* @param event The mouse move event (by ref).
|
||||
* @param point The new mouse x and y.
|
||||
*/
|
||||
void calculateDeltas(CGEventRef *event, MMPoint point)
|
||||
{
|
||||
/**
|
||||
* The next few lines are a workaround for games not detecting mouse moves.
|
||||
* See this issue for more information:
|
||||
* https://github.com/octalmage/robotjs/issues/159
|
||||
*/
|
||||
CGEventRef get = CGEventCreate(NULL);
|
||||
CGPoint mouse = CGEventGetLocation(get);
|
||||
|
||||
// Calculate the deltas.
|
||||
int64_t deltaX = point.x - mouse.x;
|
||||
int64_t deltaY = point.y - mouse.y;
|
||||
|
||||
CGEventSetIntegerValueField(*event, kCGMouseEventDeltaX, deltaX);
|
||||
CGEventSetIntegerValueField(*event, kCGMouseEventDeltaY, deltaY);
|
||||
|
||||
CFRelease(get);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* Move the mouse to a specific point.
|
||||
* @param point The coordinates to move the mouse to (x, y).
|
||||
*/
|
||||
void moveMouse(MMPoint point)
|
||||
{
|
||||
#if defined(IS_MACOSX)
|
||||
CGEventRef move = CGEventCreateMouseEvent(NULL, kCGEventMouseMoved,
|
||||
CGPointFromMMPoint(point),
|
||||
kCGMouseButtonLeft);
|
||||
|
||||
calculateDeltas(&move, point);
|
||||
|
||||
CGEventPost(kCGSessionEventTap, move);
|
||||
CFRelease(move);
|
||||
#elif defined(USE_X11)
|
||||
Display *display = XGetMainDisplay();
|
||||
XWarpPointer(display, None, DefaultRootWindow(display),
|
||||
0, 0, 0, 0, point.x, point.y);
|
||||
XFlush(display);
|
||||
#elif defined(IS_WINDOWS)
|
||||
//Mouse motion is now done using SendInput with MOUSEINPUT. We use Absolute mouse positioning
|
||||
#define MOUSE_COORD_TO_ABS(coord, width_or_height) (((65536 * coord) / width_or_height) + (coord < 0 ? -1 : 1))
|
||||
point.x = MOUSE_COORD_TO_ABS(point.x, GetSystemMetrics(SM_CXSCREEN));
|
||||
point.y = MOUSE_COORD_TO_ABS(point.y, GetSystemMetrics(SM_CYSCREEN));
|
||||
INPUT mouseInput;
|
||||
mouseInput.type = INPUT_MOUSE;
|
||||
mouseInput.mi.dx = point.x;
|
||||
mouseInput.mi.dy = point.y;
|
||||
mouseInput.mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE;
|
||||
mouseInput.mi.time = 0; //System will provide the timestamp
|
||||
mouseInput.mi.dwExtraInfo = 0;
|
||||
mouseInput.mi.mouseData = 0;
|
||||
SendInput(1, &mouseInput, sizeof(mouseInput));
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void dragMouse(MMPoint point, const MMMouseButton button)
|
||||
{
|
||||
#if defined(IS_MACOSX)
|
||||
const CGEventType dragType = MMMouseDragToCGEventType(button);
|
||||
CGEventRef drag = CGEventCreateMouseEvent(NULL, dragType,
|
||||
CGPointFromMMPoint(point),
|
||||
(CGMouseButton)button);
|
||||
calculateDeltas(&drag, point);
|
||||
|
||||
CGEventPost(kCGSessionEventTap, drag);
|
||||
CFRelease(drag);
|
||||
#else
|
||||
moveMouse(point);
|
||||
#endif
|
||||
}
|
||||
|
||||
MMPoint getMousePos()
|
||||
{
|
||||
#if defined(IS_MACOSX)
|
||||
CGEventRef event = CGEventCreate(NULL);
|
||||
CGPoint point = CGEventGetLocation(event);
|
||||
CFRelease(event);
|
||||
|
||||
return MMPointFromCGPoint(point);
|
||||
#elif defined(USE_X11)
|
||||
int x, y; /* This is all we care about. Seriously. */
|
||||
Window garb1, garb2; /* Why you can't specify NULL as a parameter */
|
||||
int garb_x, garb_y; /* is beyond me. */
|
||||
unsigned int more_garbage;
|
||||
|
||||
Display *display = XGetMainDisplay();
|
||||
XQueryPointer(display, XDefaultRootWindow(display), &garb1, &garb2,
|
||||
&x, &y, &garb_x, &garb_y, &more_garbage);
|
||||
|
||||
return MMPointMake(x, y);
|
||||
#elif defined(IS_WINDOWS)
|
||||
POINT point;
|
||||
GetCursorPos(&point);
|
||||
|
||||
return MMPointFromPOINT(point);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Press down a button, or release it.
|
||||
* @param down True for down, false for up.
|
||||
* @param button The button to press down or release.
|
||||
*/
|
||||
void toggleMouse(bool down, MMMouseButton button)
|
||||
{
|
||||
#if defined(IS_MACOSX)
|
||||
const CGPoint currentPos = CGPointFromMMPoint(getMousePos());
|
||||
const CGEventType mouseType = MMMouseToCGEventType(down, button);
|
||||
CGEventRef event = CGEventCreateMouseEvent(NULL,
|
||||
mouseType,
|
||||
currentPos,
|
||||
(CGMouseButton)button);
|
||||
CGEventPost(kCGSessionEventTap, event);
|
||||
CFRelease(event);
|
||||
#elif defined(USE_X11)
|
||||
Display *display = XGetMainDisplay();
|
||||
XTestFakeButtonEvent(display, button, down ? True : False, CurrentTime);
|
||||
XFlush(display);
|
||||
#elif defined(IS_WINDOWS)
|
||||
mouse_event(MMMouseToMEventF(down, button), 0, 0, 0, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
void clickMouse(MMMouseButton button)
|
||||
{
|
||||
toggleMouse(true, button);
|
||||
toggleMouse(false, button);
|
||||
}
|
||||
|
||||
/**
|
||||
* Special function for sending double clicks, needed for Mac OS X.
|
||||
* @param button Button to click.
|
||||
*/
|
||||
void doubleClick(MMMouseButton button)
|
||||
{
|
||||
|
||||
#if defined(IS_MACOSX)
|
||||
|
||||
/* Double click for Mac. */
|
||||
const CGPoint currentPos = CGPointFromMMPoint(getMousePos());
|
||||
const CGEventType mouseTypeDown = MMMouseToCGEventType(true, button);
|
||||
const CGEventType mouseTypeUP = MMMouseToCGEventType(false, button);
|
||||
|
||||
CGEventRef event = CGEventCreateMouseEvent(NULL, mouseTypeDown, currentPos, kCGMouseButtonLeft);
|
||||
|
||||
/* Set event to double click. */
|
||||
CGEventSetIntegerValueField(event, kCGMouseEventClickState, 2);
|
||||
|
||||
CGEventPost(kCGHIDEventTap, event);
|
||||
|
||||
CGEventSetType(event, mouseTypeUP);
|
||||
CGEventPost(kCGHIDEventTap, event);
|
||||
|
||||
CFRelease(event);
|
||||
|
||||
#else
|
||||
|
||||
/* Double click for everything else. */
|
||||
clickMouse(button);
|
||||
microsleep(200);
|
||||
clickMouse(button);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Function used to scroll the screen in the required direction.
|
||||
* This uses the magnitude to scroll the required amount in the direction.
|
||||
* TODO Requires further fine tuning based on the requirements.
|
||||
*/
|
||||
void scrollMouse(int scrollMagnitude, MMMouseWheelDirection scrollDirection)
|
||||
{
|
||||
#if defined(IS_WINDOWS)
|
||||
// Fix for #97 https://github.com/octalmage/robotjs/issues/97,
|
||||
// C89 needs variables declared on top of functions (mouseScrollInput)
|
||||
INPUT mouseScrollInput;
|
||||
#endif
|
||||
|
||||
/* Direction should only be considered based on the scrollDirection. This
|
||||
* Should not interfere. */
|
||||
int cleanScrollMagnitude = abs(scrollMagnitude);
|
||||
if (!(scrollDirection == DIRECTION_UP || scrollDirection == DIRECTION_DOWN))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* Set up the OS specific solution */
|
||||
#if defined(__APPLE__)
|
||||
|
||||
CGWheelCount wheel = 1;
|
||||
CGEventRef event;
|
||||
|
||||
/* Make scroll magnitude negative if we're scrolling down. */
|
||||
cleanScrollMagnitude = cleanScrollMagnitude * scrollDirection;
|
||||
|
||||
event = CGEventCreateScrollWheelEvent(NULL, kCGScrollEventUnitLine, wheel, cleanScrollMagnitude, 0);
|
||||
CGEventPost(kCGHIDEventTap, event);
|
||||
|
||||
#elif defined(USE_X11)
|
||||
|
||||
int x;
|
||||
int dir = 4; /* Button 4 is up, 5 is down. */
|
||||
Display *display = XGetMainDisplay();
|
||||
|
||||
if (scrollDirection == DIRECTION_DOWN)
|
||||
{
|
||||
dir = 5;
|
||||
}
|
||||
|
||||
for (x = 0; x < cleanScrollMagnitude; x++)
|
||||
{
|
||||
XTestFakeButtonEvent(display, dir, 1, CurrentTime);
|
||||
XTestFakeButtonEvent(display, dir, 0, CurrentTime);
|
||||
}
|
||||
|
||||
XFlush(display);
|
||||
|
||||
#elif defined(IS_WINDOWS)
|
||||
|
||||
mouseScrollInput.type = INPUT_MOUSE;
|
||||
mouseScrollInput.mi.dx = 0;
|
||||
mouseScrollInput.mi.dy = 0;
|
||||
mouseScrollInput.mi.dwFlags = MOUSEEVENTF_WHEEL;
|
||||
mouseScrollInput.mi.time = 0;
|
||||
mouseScrollInput.mi.dwExtraInfo = 0;
|
||||
mouseScrollInput.mi.mouseData = WHEEL_DELTA * scrollDirection * cleanScrollMagnitude;
|
||||
|
||||
SendInput(1, &mouseScrollInput, sizeof(mouseScrollInput));
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* A crude, fast hypot() approximation to get around the fact that hypot() is
|
||||
* not a standard ANSI C function.
|
||||
*
|
||||
* It is not particularly accurate but that does not matter for our use case.
|
||||
*
|
||||
* Taken from this StackOverflow answer:
|
||||
* http://stackoverflow.com/questions/3506404/fast-hypotenuse-algorithm-for-embedded-processor#3507882
|
||||
*
|
||||
*/
|
||||
static double crude_hypot(double x, double y)
|
||||
{
|
||||
double big = fabs(x); /* max(|x|, |y|) */
|
||||
double small = fabs(y); /* min(|x|, |y|) */
|
||||
|
||||
if (big > small) {
|
||||
double temp = big;
|
||||
big = small;
|
||||
small = temp;
|
||||
}
|
||||
|
||||
return ((M_SQRT2 - 1.0) * small) + big;
|
||||
}
|
||||
|
||||
bool smoothlyMoveMouse(MMPoint endPoint)
|
||||
{
|
||||
MMPoint pos = getMousePos();
|
||||
MMSize screenSize = getMainDisplaySize();
|
||||
double velo_x = 0.0, velo_y = 0.0;
|
||||
double distance;
|
||||
|
||||
while ((distance = crude_hypot((double)pos.x - endPoint.x,
|
||||
(double)pos.y - endPoint.y)) > 1.0) {
|
||||
double gravity = DEADBEEF_UNIFORM(5.0, 500.0);
|
||||
double veloDistance;
|
||||
velo_x += (gravity * ((double)endPoint.x - pos.x)) / distance;
|
||||
velo_y += (gravity * ((double)endPoint.y - pos.y)) / distance;
|
||||
|
||||
/* Normalize velocity to get a unit vector of length 1. */
|
||||
veloDistance = crude_hypot(velo_x, velo_y);
|
||||
velo_x /= veloDistance;
|
||||
velo_y /= veloDistance;
|
||||
|
||||
pos.x += floor(velo_x + 0.5);
|
||||
pos.y += floor(velo_y + 0.5);
|
||||
|
||||
/* Make sure we are in the screen boundaries!
|
||||
* (Strange things will happen if we are not.) */
|
||||
if (pos.x >= screenSize.width || pos.y >= screenSize.height) {
|
||||
return false;
|
||||
}
|
||||
|
||||
moveMouse(pos);
|
||||
|
||||
/* Wait 1 - 3 milliseconds. */
|
||||
microsleep(DEADBEEF_UNIFORM(1.0, 3.0));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
148
robotgo.go
Normal file
148
robotgo.go
Normal file
@ -0,0 +1,148 @@
|
||||
package robotgo
|
||||
|
||||
/*
|
||||
//#if defined(IS_MACOSX)
|
||||
#cgo darwin CFLAGS: -x objective-c -Wno-deprecated-declarations
|
||||
#cgo darwin LDFLAGS: -framework Cocoa -framework OpenGL -framework IOKit -framework Carbon -framework CoreFoundation
|
||||
//#elif defined(USE_X11)
|
||||
#cgo linux CFLAGS:-I/usr/src
|
||||
#cgo linux LDFLAGS:-L/usr/src -lpng -lz -lX11 -lXtst -lm
|
||||
//#endif
|
||||
#cgo windows LDFLAGS: -lgdi32 -luser32
|
||||
//#include <AppKit/NSEvent.h>
|
||||
#include "screen/goScreen.h"
|
||||
#include "mouse/goMouse.h"
|
||||
#include "key/goKey.h"
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
. "fmt"
|
||||
// "runtime"
|
||||
// "syscall"
|
||||
)
|
||||
|
||||
/*
|
||||
__ __
|
||||
| \/ | ___ _ _ ___ ___
|
||||
| |\/| |/ _ \| | | / __|/ _ \
|
||||
| | | | (_) | |_| \__ \ __/
|
||||
|_| |_|\___/ \__,_|___/\___|
|
||||
|
||||
*/
|
||||
|
||||
type MPoint struct {
|
||||
x int
|
||||
y int
|
||||
}
|
||||
|
||||
//C.size_t int
|
||||
func MoveMouse(x, y C.int) {
|
||||
C.amoveMouse(x, y)
|
||||
}
|
||||
|
||||
func DragMouse(x, y C.int) {
|
||||
C.adragMouse(x, y)
|
||||
}
|
||||
|
||||
func MoveMouseSmooth(x, y C.int) {
|
||||
C.amoveMouseSmooth(x, y)
|
||||
}
|
||||
|
||||
func GetMousePos() (C.size_t, C.size_t) {
|
||||
pos := C.agetMousePos()
|
||||
// Println("pos:###", pos, pos.x, pos.y)
|
||||
return pos.x, pos.y
|
||||
}
|
||||
|
||||
func MouseClick() {
|
||||
C.amouseClick()
|
||||
}
|
||||
|
||||
func MouseToggle() {
|
||||
C.amouseToggle()
|
||||
}
|
||||
|
||||
func SetMouseDelay(x C.int) {
|
||||
C.asetMouseDelay(x)
|
||||
}
|
||||
|
||||
func ScrollMouse(x C.int, y string) {
|
||||
z := C.CString(y)
|
||||
C.ascrollMouse(x, z)
|
||||
}
|
||||
|
||||
/*
|
||||
_ __ _ _
|
||||
| |/ /___ _ _| |__ ___ __ _ _ __ __| |
|
||||
| ' // _ \ | | | '_ \ / _ \ / _` | '__/ _` |
|
||||
| . \ __/ |_| | |_) | (_) | (_| | | | (_| |
|
||||
|_|\_\___|\__, |_.__/ \___/ \__,_|_| \__,_|
|
||||
|___/
|
||||
*/
|
||||
|
||||
func KeyTap(x string) {
|
||||
z := C.CString(x)
|
||||
// Println("----")
|
||||
C.akeyTap(z)
|
||||
}
|
||||
|
||||
func KeyToggle(x string, y string) {
|
||||
cx := C.CString(x)
|
||||
cy := C.CString(y)
|
||||
str := C.akeyToggle(cx, cy)
|
||||
Println(str)
|
||||
}
|
||||
|
||||
func TypeString(x string) {
|
||||
cx := C.CString(x)
|
||||
C.atypeString(cx)
|
||||
}
|
||||
|
||||
func TypeStringDelayed(x string, y C.size_t) {
|
||||
cx := C.CString(x)
|
||||
C.atypeStringDelayed(cx, y)
|
||||
}
|
||||
|
||||
func SetKeyboardDelay(x C.size_t) {
|
||||
C.asetKeyboardDelay(x)
|
||||
}
|
||||
|
||||
/*
|
||||
____
|
||||
/ ___| ___ _ __ ___ ___ _ __
|
||||
\___ \ / __| '__/ _ \/ _ \ '_ \
|
||||
___) | (__| | | __/ __/ | | |
|
||||
|____/ \___|_| \___|\___|_| |_|
|
||||
|
||||
*/
|
||||
|
||||
func GetPixelColor(x, y C.size_t) string {
|
||||
color := C.agetPixelColor(x, y)
|
||||
gcolor := C.GoString(color)
|
||||
return gcolor
|
||||
}
|
||||
|
||||
func GetScreenSize() (C.size_t, C.size_t) {
|
||||
size := C.agetScreenSize()
|
||||
// Println("...", size, size.width)
|
||||
return size.width, size.height
|
||||
}
|
||||
|
||||
func GetXDisplayName() string {
|
||||
name := C.agetXDisplayName()
|
||||
gname := C.GoString(name)
|
||||
return gname
|
||||
}
|
||||
|
||||
func SetXDisplayName(name string) string {
|
||||
cname := C.CString(name)
|
||||
str := C.asetXDisplayName(cname)
|
||||
gstr := C.GoString(str)
|
||||
return gstr
|
||||
}
|
||||
|
||||
func CaptureScreen(x, y, w, h C.int) {
|
||||
bit := C.acaptureScreen(x, y, w, h)
|
||||
Println("...", bit)
|
||||
}
|
86
screen/goScreen.h
Normal file
86
screen/goScreen.h
Normal file
@ -0,0 +1,86 @@
|
||||
#include "../base/types.h"
|
||||
#include "screengrab_init.h"
|
||||
#include "screen_init.h"
|
||||
// #include "../MMBitmap_init.h"
|
||||
|
||||
void padHex(MMRGBHex color, char* hex){
|
||||
//Length needs to be 7 because snprintf includes a terminating null.
|
||||
//Use %06x to pad hex value with leading 0s.
|
||||
snprintf(hex, 7, "%06x", color);
|
||||
}
|
||||
|
||||
|
||||
char* agetPixelColor(size_t x, size_t y){
|
||||
MMBitmapRef bitmap;
|
||||
MMRGBHex color;
|
||||
|
||||
if (!pointVisibleOnMainDisplay(MMPointMake(x, y))){
|
||||
// return 1;
|
||||
return "screen's dimensions.";
|
||||
}
|
||||
|
||||
bitmap = copyMMBitmapFromDisplayInRect(MMRectMake(x, y, 1, 1));
|
||||
// bitmap = MMRectMake(x, y, 1, 1);
|
||||
|
||||
color = MMRGBHexAtPoint(bitmap, 0, 0);
|
||||
|
||||
char hex[7];
|
||||
|
||||
padHex(color, hex);
|
||||
|
||||
destroyMMBitmap(bitmap);
|
||||
|
||||
// printf("%s\n", hex);
|
||||
|
||||
// return 0;
|
||||
|
||||
char* s=(char*)calloc(100,sizeof(char*));
|
||||
if(s)strcpy(s,hex);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
MMSize agetScreenSize(){
|
||||
//Get display size.
|
||||
MMSize displaySize = getMainDisplaySize();
|
||||
return displaySize;
|
||||
}
|
||||
|
||||
char* agetXDisplayName(){
|
||||
#if defined(USE_X11)
|
||||
const char* display = getXDisplay();
|
||||
char* sd=(char*)calloc(100,sizeof(char*));
|
||||
if(sd)strcpy(sd,display);
|
||||
|
||||
return sd;
|
||||
#else
|
||||
return "getXDisplayName is only supported on Linux";
|
||||
#endif
|
||||
}
|
||||
|
||||
char* asetXDisplayName(char* name){
|
||||
#if defined(USE_X11)
|
||||
setXDisplay(name);
|
||||
return "success";
|
||||
#else
|
||||
return "setXDisplayName is only supported on Linux";
|
||||
#endif
|
||||
}
|
||||
|
||||
MMBitmapRef acaptureScreen(int x,int y,int w,int h){
|
||||
// if (){
|
||||
// x = 0;
|
||||
// y = 0;
|
||||
|
||||
// //Get screen size.
|
||||
// MMSize displaySize = getMainDisplaySize();
|
||||
// w = displaySize.width;
|
||||
// h = displaySize.height;
|
||||
// }
|
||||
|
||||
MMBitmapRef bitmap = copyMMBitmapFromDisplayInRect(MMRectMake(x, y, w, h));
|
||||
// printf("%s\n", bitmap);
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
|
29
screen/screen.h
Normal file
29
screen/screen.h
Normal file
@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
#ifndef SCREEN_H
|
||||
#define SCREEN_H
|
||||
|
||||
#include "../base/types.h"
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#include "../base/ms_stdbool.h"
|
||||
#else
|
||||
#include <stdbool.h>
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/* Returns the size of the main display. */
|
||||
MMSize getMainDisplaySize(void);
|
||||
|
||||
/* Convenience function that returns whether the given point is in the bounds
|
||||
* of the main screen. */
|
||||
bool pointVisibleOnMainDisplay(MMPoint point);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* SCREEN_H */
|
33
screen/screen_init.h
Normal file
33
screen/screen_init.h
Normal file
@ -0,0 +1,33 @@
|
||||
#include "screen.h"
|
||||
#include "../base/os.h"
|
||||
|
||||
#if defined(IS_MACOSX)
|
||||
#include <ApplicationServices/ApplicationServices.h>
|
||||
#elif defined(USE_X11)
|
||||
#include <X11/Xlib.h>
|
||||
// #include "../base/xdisplay_init.h"
|
||||
#endif
|
||||
|
||||
MMSize getMainDisplaySize(void)
|
||||
{
|
||||
#if defined(IS_MACOSX)
|
||||
CGDirectDisplayID displayID = CGMainDisplayID();
|
||||
return MMSizeMake(CGDisplayPixelsWide(displayID),
|
||||
CGDisplayPixelsHigh(displayID));
|
||||
#elif defined(USE_X11)
|
||||
Display *display = XGetMainDisplay();
|
||||
const int screen = DefaultScreen(display);
|
||||
|
||||
return MMSizeMake((size_t)DisplayWidth(display, screen),
|
||||
(size_t)DisplayHeight(display, screen));
|
||||
#elif defined(IS_WINDOWS)
|
||||
return MMSizeMake((size_t)GetSystemMetrics(SM_CXSCREEN),
|
||||
(size_t)GetSystemMetrics(SM_CYSCREEN));
|
||||
#endif
|
||||
}
|
||||
|
||||
bool pointVisibleOnMainDisplay(MMPoint point)
|
||||
{
|
||||
MMSize displaySize = getMainDisplaySize();
|
||||
return point.x < displaySize.width && point.y < displaySize.height;
|
||||
}
|
21
screen/screengrab.h
Normal file
21
screen/screengrab.h
Normal file
@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
#ifndef SCREENGRAB_H
|
||||
#define SCREENGRAB_H
|
||||
|
||||
#include "../base/types.h"
|
||||
#include "../base/MMBitmap_init.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/* Returns a raw bitmap of screengrab of the display (to be destroyed()'d by
|
||||
* caller), or NULL on error. */
|
||||
MMBitmapRef copyMMBitmapFromDisplayInRect(MMRect rect);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* SCREENGRAB_H */
|
163
screen/screengrab_init.h
Normal file
163
screen/screengrab_init.h
Normal file
@ -0,0 +1,163 @@
|
||||
#include "screengrab.h"
|
||||
#include "../base/bmp_io_init.h"
|
||||
#include "../base/endian.h"
|
||||
#include <stdlib.h> /* malloc() */
|
||||
|
||||
#if defined(IS_MACOSX)
|
||||
#include <OpenGL/OpenGL.h>
|
||||
#include <OpenGL/gl.h>
|
||||
#include <ApplicationServices/ApplicationServices.h>
|
||||
#elif defined(USE_X11)
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include "../base/xdisplay_init.h"
|
||||
#elif defined(IS_WINDOWS)
|
||||
// #include "windows.h"
|
||||
// #include <wingdi.h>
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
MMBitmapRef copyMMBitmapFromDisplayInRect(MMRect rect)
|
||||
{
|
||||
#if defined(IS_MACOSX)
|
||||
|
||||
size_t bytewidth;
|
||||
uint8_t bitsPerPixel, bytesPerPixel;
|
||||
//uint8_t *buffer;
|
||||
|
||||
CGDirectDisplayID displayID = CGMainDisplayID();
|
||||
|
||||
//Replacement for CGDisplayBitsPerPixel.
|
||||
CGDisplayModeRef mode = CGDisplayCopyDisplayMode(displayID);
|
||||
size_t depth = 0;
|
||||
|
||||
CFStringRef pixEnc = CGDisplayModeCopyPixelEncoding(mode);
|
||||
if(CFStringCompare(pixEnc, CFSTR(IO32BitDirectPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo)
|
||||
depth = 32;
|
||||
else if(CFStringCompare(pixEnc, CFSTR(IO16BitDirectPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo)
|
||||
depth = 16;
|
||||
else if(CFStringCompare(pixEnc, CFSTR(IO8BitIndexedPixels), kCFCompareCaseInsensitive) == kCFCompareEqualTo)
|
||||
depth = 8;
|
||||
|
||||
CGDisplayModeRelease(mode);
|
||||
CFRelease(pixEnc);
|
||||
|
||||
bitsPerPixel = (uint8_t) depth;
|
||||
bytesPerPixel = bitsPerPixel / 8;
|
||||
/* Align width to padding. */
|
||||
//bytewidth = ADD_PADDING(rect.size.width * bytesPerPixel);
|
||||
bytewidth = rect.size.width * bytesPerPixel;
|
||||
|
||||
/* Convert Quartz point to postscript point. */
|
||||
//rect.origin.y = CGDisplayPixelsHigh(displayID) - rect.origin.y - rect.size.height;
|
||||
|
||||
CGImageRef image = CGDisplayCreateImageForRect(displayID, CGRectMake(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height));
|
||||
|
||||
// Request access to the raw pixel data via the image's DataProvider.
|
||||
CGDataProviderRef provider = CGImageGetDataProvider(image);
|
||||
CFDataRef data = CGDataProviderCopyData(provider);
|
||||
|
||||
size_t width, height;
|
||||
width = CGImageGetWidth(image);
|
||||
height = CGImageGetHeight(image);
|
||||
size_t bpp = CGImageGetBitsPerPixel(image) / 8;
|
||||
|
||||
uint8 *pixels = malloc(width * height * bpp);
|
||||
memcpy(pixels, CFDataGetBytePtr(data), width * height * bpp);
|
||||
CFRelease(data);
|
||||
CGImageRelease(image);
|
||||
|
||||
return createMMBitmap(pixels, rect.size.width, rect.size.height, bytewidth,
|
||||
bitsPerPixel, bytesPerPixel);
|
||||
#elif defined(USE_X11)
|
||||
MMBitmapRef bitmap;
|
||||
|
||||
Display *display = XOpenDisplay(NULL);
|
||||
XImage *image = XGetImage(display,
|
||||
XDefaultRootWindow(display),
|
||||
(int)rect.origin.x,
|
||||
(int)rect.origin.y,
|
||||
(unsigned int)rect.size.width,
|
||||
(unsigned int)rect.size.height,
|
||||
AllPlanes, ZPixmap);
|
||||
XCloseDisplay(display);
|
||||
if (image == NULL) return NULL;
|
||||
|
||||
bitmap = createMMBitmap((uint8_t *)image->data,
|
||||
rect.size.width,
|
||||
rect.size.height,
|
||||
(size_t)image->bytes_per_line,
|
||||
(uint8_t)image->bits_per_pixel,
|
||||
(uint8_t)image->bits_per_pixel / 8);
|
||||
image->data = NULL; /* Steal ownership of bitmap data so we don't have to
|
||||
* copy it. */
|
||||
XDestroyImage(image);
|
||||
|
||||
return bitmap;
|
||||
#elif defined(IS_WINDOWS)
|
||||
MMBitmapRef bitmap;
|
||||
void *data;
|
||||
HDC screen = NULL, screenMem = NULL;
|
||||
HBITMAP dib;
|
||||
BITMAPINFO bi;
|
||||
|
||||
/* Initialize bitmap info. */
|
||||
bi.bmiHeader.biSize = sizeof(bi.bmiHeader);
|
||||
bi.bmiHeader.biWidth = (long)rect.size.width;
|
||||
bi.bmiHeader.biHeight = -(long)rect.size.height; /* Non-cartesian, please */
|
||||
bi.bmiHeader.biPlanes = 1;
|
||||
bi.bmiHeader.biBitCount = 32;
|
||||
bi.bmiHeader.biCompression = BI_RGB;
|
||||
bi.bmiHeader.biSizeImage = (DWORD)(4 * rect.size.width * rect.size.height);
|
||||
bi.bmiHeader.biXPelsPerMeter = 0;
|
||||
bi.bmiHeader.biYPelsPerMeter = 0;
|
||||
bi.bmiHeader.biClrUsed = 0;
|
||||
bi.bmiHeader.biClrImportant = 0;
|
||||
|
||||
screen = GetDC(NULL); /* Get entire screen */
|
||||
if (screen == NULL) return NULL;
|
||||
|
||||
/* Get screen data in display device context. */
|
||||
dib = CreateDIBSection(screen, &bi, DIB_RGB_COLORS, &data, NULL, 0);
|
||||
|
||||
/* Copy the data into a bitmap struct. */
|
||||
if ((screenMem = CreateCompatibleDC(screen)) == NULL ||
|
||||
SelectObject(screenMem, dib) == NULL ||
|
||||
!BitBlt(screenMem,
|
||||
(int)0,
|
||||
(int)0,
|
||||
(int)rect.size.width,
|
||||
(int)rect.size.height,
|
||||
screen,
|
||||
rect.origin.x,
|
||||
rect.origin.y,
|
||||
SRCCOPY)) {
|
||||
|
||||
/* Error copying data. */
|
||||
ReleaseDC(NULL, screen);
|
||||
DeleteObject(dib);
|
||||
if (screenMem != NULL) DeleteDC(screenMem);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bitmap = createMMBitmap(NULL,
|
||||
rect.size.width,
|
||||
rect.size.height,
|
||||
4 * rect.size.width,
|
||||
(uint8_t)bi.bmiHeader.biBitCount,
|
||||
4);
|
||||
|
||||
/* Copy the data to our pixel buffer. */
|
||||
if (bitmap != NULL) {
|
||||
bitmap->imageBuffer = malloc(bitmap->bytewidth * bitmap->height);
|
||||
memcpy(bitmap->imageBuffer, data, bitmap->bytewidth * bitmap->height);
|
||||
}
|
||||
|
||||
ReleaseDC(NULL, screen);
|
||||
DeleteObject(dib);
|
||||
DeleteDC(screenMem);
|
||||
|
||||
return bitmap;
|
||||
#endif
|
||||
}
|
29
test/main.go
Normal file
29
test/main.go
Normal file
@ -0,0 +1,29 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
. "fmt"
|
||||
|
||||
"github.com/go-vgo/robotgo"
|
||||
)
|
||||
|
||||
func arobotgo() {
|
||||
x, y := robotgo.GetMousePos()
|
||||
Println("pos:", x, y)
|
||||
|
||||
Println(robotgo.GetPixelColor(x, y))
|
||||
|
||||
color := robotgo.GetPixelColor(100, 200)
|
||||
Println("color@@@", color)
|
||||
|
||||
robotgo.TypeString("Hello World")
|
||||
robotgo.KeyTap("enter")
|
||||
// robotgo.KeyToggle("enter", "down")
|
||||
robotgo.TypeString("en")
|
||||
|
||||
// robotgo.MouseClick()
|
||||
robotgo.ScrollMouse(10, "up")
|
||||
}
|
||||
|
||||
func main() {
|
||||
arobotgo()
|
||||
}
|
Loading…
Reference in New Issue
Block a user