tclzbar.c at tip
Not logged in

File jni/ZBar/tcl/tclzbar.c from the latest check-in


/*
 * tclzbar.c --
 *
 *      This file contains the implementation of the "zbar" Tcl
 *      command which uses the ZBar library to scan barcodes.
 *
 * Copyright (c) 2015-2023 Christian Werner <chw@ch-werner.de>
 *
 * See the file "LICENSE" for information on usage and redistribution of
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 */

#ifdef ZBAR_NO_TK

/* Partial photo image block */

typedef struct {
    int width;
    int height;
    unsigned char *pixelPtr;
} Tk_PhotoImageBlock;

#else

#include <tk.h>

#endif

#include <tcl.h>
#include <zbar.h>
#include <string.h>

/*
 * Symbology mapping.
 */

static const struct {
    const char *string;
    zbar_symbol_type_t type;
} ZbarSyms[] = {
    { "NONE",		ZBAR_NONE	},
    { "PARTIAL",	ZBAR_PARTIAL	},
    { "EAN8",		ZBAR_EAN8	},
    { "UPCE",		ZBAR_UPCE	},
    { "ISBN10",		ZBAR_ISBN10	},
    { "UPCA",		ZBAR_UPCA	},
    { "EAN13",		ZBAR_EAN13	},
    { "ISBN13",		ZBAR_ISBN13	},
    { "DATABAR",	ZBAR_DATABAR	},
    { "DATABAR_EXP",	ZBAR_DATABAR_EXP},
    { "I25",		ZBAR_I25	},
    { "CODABAR",	ZBAR_CODABAR	},
    { "CODE39",		ZBAR_CODE39	},
#ifdef ZBAR_PDF417
    { "PDF417",		ZBAR_PDF417	},
#endif
    { "QRCODE",		ZBAR_QRCODE	},
    { "CODE93",		ZBAR_CODE93	},
    { "CODE128",	ZBAR_CODE128	}
};

#if !defined(__arm__)

/*
 * Color weight tables
 * R=0.299, G=0.587, B=0.114
 */

static const int weightR[256] = {
          0,   19518,   39036,   58554,   78072,   97590,  117108,  136626,
     156144,  175662,  195180,  214698,  234216,  253734,  273252,  292770,
     312288,  331806,  351324,  370842,  390360,  409878,  429396,  448914,
     468432,  487950,  507468,  526986,  546504,  566022,  585540,  605058,
     624576,  644094,  663612,  683130,  702648,  722166,  741684,  761202,
     780720,  800238,  819756,  839274,  858792,  878310,  897828,  917346,
     936864,  956382,  975900,  995418, 1014936, 1034454, 1053972, 1073490,
    1093008, 1112526, 1132044, 1151562, 1171080, 1190598, 1210116, 1229634,
    1249152, 1268670, 1288188, 1307706, 1327224, 1346742, 1366260, 1385778,
    1405296, 1424814, 1444332, 1463850, 1483368, 1502886, 1522404, 1541922,
    1561440, 1580958, 1600476, 1619994, 1639512, 1659030, 1678548, 1698066,
    1717584, 1737102, 1756620, 1776138, 1795656, 1815174, 1834692, 1854210,
    1873728, 1893246, 1912764, 1932282, 1951800, 1971318, 1990836, 2010354,
    2029872, 2049390, 2068908, 2088426, 2107944, 2127462, 2146980, 2166498,
    2186016, 2205534, 2225052, 2244570, 2264088, 2283606, 2303124, 2322642,
    2342160, 2361678, 2381196, 2400714, 2420232, 2439750, 2459268, 2478786,
    2498304, 2517822, 2537340, 2556858, 2576376, 2595894, 2615412, 2634930,
    2654448, 2673966, 2693484, 2713002, 2732520, 2752038, 2771556, 2791074,
    2810592, 2830110, 2849628, 2869146, 2888664, 2908182, 2927700, 2947218,
    2966736, 2986254, 3005772, 3025290, 3044808, 3064326, 3083844, 3103362,
    3122880, 3142398, 3161916, 3181434, 3200952, 3220470, 3239988, 3259506,
    3279024, 3298542, 3318060, 3337578, 3357096, 3376614, 3396132, 3415650,
    3435168, 3454686, 3474204, 3493722, 3513240, 3532758, 3552276, 3571794,
    3591312, 3610830, 3630348, 3649866, 3669384, 3688902, 3708420, 3727938,
    3747456, 3766974, 3786492, 3806010, 3825528, 3845046, 3864564, 3884082,
    3903600, 3923118, 3942636, 3962154, 3981672, 4001190, 4020708, 4040226,
    4059744, 4079262, 4098780, 4118298, 4137816, 4157334, 4176852, 4196370,
    4215888, 4235406, 4254924, 4274442, 4293960, 4313478, 4332996, 4352514,
    4372032, 4391550, 4411068, 4430586, 4450104, 4469622, 4489140, 4508658,
    4528176, 4547694, 4567212, 4586730, 4606248, 4625766, 4645284, 4664802,
    4684320, 4703838, 4723356, 4742874, 4762392, 4781910, 4801428, 4820946,
    4840464, 4859982, 4879500, 4899018, 4918536, 4938054, 4957572, 4977090
};

static const int weightG[256] = {
          0,   38319,   76638,  114957,  153276,  191595,  229914,  268233,
     306552,  344871,  383190,  421509,  459828,  498147,  536466,  574785,
     613104,  651423,  689742,  728061,  766380,  804699,  843018,  881337,
     919656,  957975,  996294, 1034613, 1072932, 1111251, 1149570, 1187889,
    1226208, 1264527, 1302846, 1341165, 1379484, 1417803, 1456122, 1494441,
    1532760, 1571079, 1609398, 1647717, 1686036, 1724355, 1762674, 1800993,
    1839312, 1877631, 1915950, 1954269, 1992588, 2030907, 2069226, 2107545,
    2145864, 2184183, 2222502, 2260821, 2299140, 2337459, 2375778, 2414097,
    2452416, 2490735, 2529054, 2567373, 2605692, 2644011, 2682330, 2720649,
    2758968, 2797287, 2835606, 2873925, 2912244, 2950563, 2988882, 3027201,
    3065520, 3103839, 3142158, 3180477, 3218796, 3257115, 3295434, 3333753,
    3372072, 3410391, 3448710, 3487029, 3525348, 3563667, 3601986, 3640305,
    3678624, 3716943, 3755262, 3793581, 3831900, 3870219, 3908538, 3946857,
    3985176, 4023495, 4061814, 4100133, 4138452, 4176771, 4215090, 4253409,
    4291728, 4330047, 4368366, 4406685, 4445004, 4483323, 4521642, 4559961,
    4598280, 4636599, 4674918, 4713237, 4751556, 4789875, 4828194, 4866513,
    4904832, 4943151, 4981470, 5019789, 5058108, 5096427, 5134746, 5173065,
    5211384, 5249703, 5288022, 5326341, 5364660, 5402979, 5441298, 5479617,
    5517936, 5556255, 5594574, 5632893, 5671212, 5709531, 5747850, 5786169,
    5824488, 5862807, 5901126, 5939445, 5977764, 6016083, 6054402, 6092721,
    6131040, 6169359, 6207678, 6245997, 6284316, 6322635, 6360954, 6399273,
    6437592, 6475911, 6514230, 6552549, 6590868, 6629187, 6667506, 6705825,
    6744144, 6782463, 6820782, 6859101, 6897420, 6935739, 6974058, 7012377,
    7050696, 7089015, 7127334, 7165653, 7203972, 7242291, 7280610, 7318929,
    7357248, 7395567, 7433886, 7472205, 7510524, 7548843, 7587162, 7625481,
    7663800, 7702119, 7740438, 7778757, 7817076, 7855395, 7893714, 7932033,
    7970352, 8008671, 8046990, 8085309, 8123628, 8161947, 8200266, 8238585,
    8276904, 8315223, 8353542, 8391861, 8430180, 8468499, 8506818, 8545137,
    8583456, 8621775, 8660094, 8698413, 8736732, 8775051, 8813370, 8851689,
    8890008, 8928327, 8966646, 9004965, 9043284, 9081603, 9119922, 9158241,
    9196560, 9234879, 9273198, 9311517, 9349836, 9388155, 9426474, 9464793,
    9503112, 9541431, 9579750, 9618069, 9656388, 9694707, 9733026, 9771345
};

static const int weightB[256] = {
          0,    7442,   14884,   22326,   29768,   37210,   44652,   52094,
      59536,   66978,   74420,   81862,   89304,   96746,  104188,  111630,
     119072,  126514,  133956,  141398,  148840,  156282,  163724,  171166,
     178608,  186050,  193492,  200934,  208376,  215818,  223260,  230702,
     238144,  245586,  253028,  260470,  267912,  275354,  282796,  290238,
     297680,  305122,  312564,  320006,  327448,  334890,  342332,  349774,
     357216,  364658,  372100,  379542,  386984,  394426,  401868,  409310,
     416752,  424194,  431636,  439078,  446520,  453962,  461404,  468846,
     476288,  483730,  491172,  498614,  506056,  513498,  520940,  528382,
     535824,  543266,  550708,  558150,  565592,  573034,  580476,  587918,
     595360,  602802,  610244,  617686,  625128,  632570,  640012,  647454,
     654896,  662338,  669780,  677222,  684664,  692106,  699548,  706990,
     714432,  721874,  729316,  736758,  744200,  751642,  759084,  766526,
     773968,  781410,  788852,  796294,  803736,  811178,  818620,  826062,
     833504,  840946,  848388,  855830,  863272,  870714,  878156,  885598,
     893040,  900482,  907924,  915366,  922808,  930250,  937692,  945134,
     952576,  960018,  967460,  974902,  982344,  989786,  997228, 1004670,
    1012112, 1019554, 1026996, 1034438, 1041880, 1049322, 1056764, 1064206,
    1071648, 1079090, 1086532, 1093974, 1101416, 1108858, 1116300, 1123742,
    1131184, 1138626, 1146068, 1153510, 1160952, 1168394, 1175836, 1183278,
    1190720, 1198162, 1205604, 1213046, 1220488, 1227930, 1235372, 1242814,
    1250256, 1257698, 1265140, 1272582, 1280024, 1287466, 1294908, 1302350,
    1309792, 1317234, 1324676, 1332118, 1339560, 1347002, 1354444, 1361886,
    1369328, 1376770, 1384212, 1391654, 1399096, 1406538, 1413980, 1421422,
    1428864, 1436306, 1443748, 1451190, 1458632, 1466074, 1473516, 1480958,
    1488400, 1495842, 1503284, 1510726, 1518168, 1525610, 1533052, 1540494,
    1547936, 1555378, 1562820, 1570262, 1577704, 1585146, 1592588, 1600030,
    1607472, 1614914, 1622356, 1629798, 1637240, 1644682, 1652124, 1659566,
    1667008, 1674450, 1681892, 1689334, 1696776, 1704218, 1711660, 1719102,
    1726544, 1733986, 1741428, 1748870, 1756312, 1763754, 1771196, 1778638,
    1786080, 1793522, 1800964, 1808406, 1815848, 1823290, 1830732, 1838174,
    1845616, 1853058, 1860500, 1867942, 1875384, 1882826, 1890268, 1897710
};

#endif /* !defined(__arm__) */


/*
 *-------------------------------------------------------------------------
 *
 * ZbarGetSymbolObj --
 *
 *	Return string object given symbol type or list of
 *	all known symbol types.
 *
 *-------------------------------------------------------------------------
 */

static Tcl_Obj *
ZbarGetSymbolObj(zbar_symbol_type_t t, int all)
{
    Tcl_Obj *result;
    int i;

    if (all) {
	result = Tcl_NewListObj(0, NULL);
	for (i = 0; i < sizeof(ZbarSyms) / sizeof(ZbarSyms[0]); i++) {
	    Tcl_ListObjAppendElement(NULL, result,
				     Tcl_NewStringObj(ZbarSyms[i].string, -1));
	}
    } else {
	result = NULL;
	for (i = 0; i < sizeof(ZbarSyms) / sizeof(ZbarSyms[0]); i++) {
	    if (t == ZbarSyms[i].type) {
		result = Tcl_NewStringObj(ZbarSyms[i].string, -1);
		break;
	    }
	}
	if (result == NULL) {
	    result = Tcl_NewObj();
	}
    }
    return result;
}

/*
 *-------------------------------------------------------------------------
 *
 * ZbarGetSymbolType --
 *
 *	Return zbar_symbol_type_t given string object.
 *
 *-------------------------------------------------------------------------
 */

static zbar_symbol_type_t
ZbarGetSymbolType(Tcl_Obj *obj, int *idxPtr)
{
    char *string = Tcl_GetString(obj);
    int i;

    for (i = 0; i < sizeof(ZbarSyms) / sizeof(ZbarSyms[0]); i++) {
	if (strcmp(string, ZbarSyms[i].string) == 0) {
	    if (idxPtr != NULL) {
		*idxPtr = i;
	    }
	    return ZbarSyms[i].type;
	}
    }
    return ZBAR_SYMBOL;	/* no match */
}

#ifdef TCL_THREADS

/*
 * NB: as of Tcl 8.6.5 asynchronous event handlers (Tcl_AsyncCreate())
 * are unreliable since they can break the Tcl catch command.
 * If USE_ASYNC_HANDLER is undefined, Tcl_ThreadQueueEvent() is used
 * instead and runtime behaviour should be almost identical.
 */

#undef USE_ASYNC_HANDLER

/*
 * Structure used for asynchronous decoding carried out by
 * a dedicated thread. At most, one decoding job can be
 * outstanding at any one time.
 */

typedef struct {
    int tip609;			/* When true, TIP#609 is available. */
    int run;			/* Controls thread loop */
    Tcl_Mutex mutex;		/* Lock for this struct */
    Tcl_Condition cond;		/* For waking up thread */
    Tcl_ThreadId tid;		/* Thread identifier */
    Tcl_Interp *interp;		/* Interpreter using Zbar decoder */
#ifdef USE_ASYNC_HANDLER
    Tcl_AsyncHandler async;	/* For signalling result */
#else
    Tcl_ThreadId interpTid;	/* Thread identifier of interp */
#endif
    unsigned char *pixPtr;	/* Thread input: pixel array or NULL */
    zbar_image_t *imgPtr;	/* Thread input: ZBar image struct */
    int nCmdObjs;		/* Callback, number Tcl_Objs */
    Tcl_Obj **cmdObjs;		/* Callback, nCmdObjs plus 3 for result */

    /* Thread input: enabled symbologies */
    unsigned char enabled[sizeof(ZbarSyms) / sizeof(ZbarSyms[0])];

    /* Thread output: ms, symbol type, data */
    int ms;
    zbar_symbol_type_t sym;
    unsigned char *dataPtr;
    int dataLength;
} AsyncDecode;

#ifndef USE_ASYNC_HANDLER

/*
 * Event type for the event queue.
 */

typedef struct {
    Tcl_Event header;
    AsyncDecode *aPtr;
} AsyncEvent;

static int ZbarDecodeHandleEvent(Tcl_Event *evPtr, int flags);

#endif

/*
 *-------------------------------------------------------------------------
 *
 * ZBarThread --
 *
 *	Decoder thread, waits per condition for a decode request.
 *	Reports the result back by an asynchronous event which
 *	triggers a do-when-idle handler in the requesting thread.
 *
 *-------------------------------------------------------------------------
 */

static Tcl_ThreadCreateType
ZbarThread(ClientData clientData)
{
    AsyncDecode *aPtr = (AsyncDecode *) clientData;
    zbar_image_scanner_t *scanPtr;
    unsigned char enabled[sizeof(ZbarSyms) / sizeof(ZbarSyms[0])];
    Tcl_Time now;
    int i, ms;
    Tcl_WideInt tw[2];
#ifndef USE_ASYNC_HANDLER
    AsyncEvent *event;
#endif

    Tcl_MutexLock(&aPtr->mutex);
    for (;;) {
	while (aPtr->run && (aPtr->imgPtr == NULL)) {
	    Tcl_ConditionWait(&aPtr->cond, &aPtr->mutex, NULL);
	}
	if (!aPtr->run) {
	    break;
	}
	if (aPtr->imgPtr == NULL) {
	    continue;
	}
	memcpy(enabled, aPtr->enabled, sizeof(enabled));
	Tcl_MutexUnlock(&aPtr->mutex);
	Tcl_GetTime(&now);
	tw[0] = (Tcl_WideInt) now.sec * 1000 + now.usec / 1000;
	scanPtr = zbar_image_scanner_create();
	if (scanPtr == NULL) {
	    Tcl_GetTime(&now);
	    tw[1] = (Tcl_WideInt) now.sec * 1000 + now.usec / 1000;
	    ms = tw[1] - tw[0];
	    if (ms < 0) {
		ms = -1;
	    }
	    Tcl_MutexLock(&aPtr->mutex);
	    zbar_image_destroy(aPtr->imgPtr);
	    aPtr->imgPtr = NULL;
	    ckfree((char *) aPtr->pixPtr);
	    aPtr->pixPtr = NULL;
	    if (aPtr->cmdObjs != NULL) {
		aPtr->ms = ms;
		aPtr->dataPtr = NULL;
		aPtr->dataLength = 0;
#ifdef USE_ASYNC_HANDLER
		Tcl_AsyncMark(aPtr->async);
#else
		event = (AsyncEvent *) ckalloc(sizeof(AsyncEvent));
		event->header.proc = ZbarDecodeHandleEvent;
		event->header.nextPtr = NULL;
		event->aPtr = aPtr;
		if (aPtr->tip609) {
		    /* TCL_QUEUE_TAIL_ALERT_IF_EMPTY */
		    Tcl_ThreadQueueEvent(aPtr->interpTid, &event->header,
					 TCL_QUEUE_TAIL | 4);
		} else {
		    Tcl_ThreadQueueEvent(aPtr->interpTid, &event->header,
					 TCL_QUEUE_TAIL);
		    Tcl_ThreadAlert(aPtr->interpTid);
		}
#endif
	    }
	    continue;
	}
	zbar_image_scanner_set_config(scanPtr, ZBAR_NONE, ZBAR_CFG_ENABLE, 0);
	for (i = 0; i < sizeof(enabled); i++) {
	    if (enabled[i]) {
		zbar_symbol_type_t t = ZbarSyms[i].type;

		zbar_image_scanner_set_config(scanPtr, t, ZBAR_CFG_ENABLE, 1);
	    }
	}
	zbar_scan_image(scanPtr, aPtr->imgPtr);
	Tcl_GetTime(&now);
	tw[1] = (Tcl_WideInt) now.sec * 1000 + now.usec / 1000;
	ms = tw[1] - tw[0];
	if (ms < 0) {
	    ms = -1;
	}
	Tcl_MutexLock(&aPtr->mutex);
	if (aPtr->cmdObjs != NULL) {
	    const zbar_symbol_t *symPtr;

	    aPtr->ms = ms;
	    aPtr->dataPtr = NULL;
	    aPtr->dataLength = 0;
	    symPtr = zbar_image_first_symbol(aPtr->imgPtr);
	    if (symPtr != NULL) {
		zbar_symbol_type_t t = zbar_symbol_get_type(symPtr);
		const unsigned char *dp = (const unsigned char *)
		    zbar_symbol_get_data(symPtr);

		aPtr->sym = t;
		aPtr->dataLength = zbar_symbol_get_data_length(symPtr);
		aPtr->dataPtr =
			(unsigned char *) attemptckalloc(aPtr->dataLength);
		if (aPtr->dataPtr != NULL) {
		    memcpy(aPtr->dataPtr, dp, aPtr->dataLength);
		}
	    }
#ifdef USE_ASYNC_HANDLER
	    Tcl_AsyncMark(aPtr->async);
#else
	    event = (AsyncEvent *) ckalloc(sizeof(AsyncEvent));
	    event->header.proc = ZbarDecodeHandleEvent;
	    event->header.nextPtr = NULL;
	    event->aPtr = aPtr;
	    if (aPtr->tip609) {
		/* TCL_QUEUE_TAIL_ALERT_IF_EMPTY */
		Tcl_ThreadQueueEvent(aPtr->interpTid, &event->header,
				     TCL_QUEUE_TAIL | 4);
	    } else {
		Tcl_ThreadQueueEvent(aPtr->interpTid, &event->header,
				     TCL_QUEUE_TAIL);
		Tcl_ThreadAlert(aPtr->interpTid);
	    }
#endif
	}
	zbar_image_scanner_destroy(scanPtr);
	zbar_image_destroy(aPtr->imgPtr);
	aPtr->imgPtr = NULL;
	ckfree((char *) aPtr->pixPtr);
	aPtr->pixPtr = NULL;
    }
    if (aPtr->imgPtr) {
	zbar_image_destroy(aPtr->imgPtr);
	aPtr->imgPtr = NULL;
    }
    if (aPtr->pixPtr != NULL) {
	ckfree((char *) aPtr->pixPtr);
	aPtr->pixPtr = NULL;
    }
    Tcl_MutexUnlock(&aPtr->mutex);
    Tcl_ExitThread(0);
    TCL_THREAD_CREATE_RETURN;
}

/*
 *-------------------------------------------------------------------------
 *
 * ZbarDecodeDone --
 *
 *	Process asynchronous result callback.
 *	Function executes as a do-when-idle handler.
 *
 *-------------------------------------------------------------------------
 */

#ifdef USE_ASYNC_HANDLER
static void
ZbarDecodeDone(ClientData clientData)
#else
static int
ZbarDecodeHandleEvent(Tcl_Event *evPtr, int flags)
#endif
{
#ifdef USE_ASYNC_HANDLER
    AsyncDecode *aPtr = (AsyncDecode *) clientData;
#else
    AsyncEvent *aevPtr = (AsyncEvent *) evPtr;
    AsyncDecode *aPtr = aevPtr->aPtr;
#endif
    int ret, i, ms, dataLength, nCmdObjs = 0;
    unsigned char *dataPtr;
    zbar_symbol_type_t sym;
    Tcl_Obj **cmdObjs;

#ifndef USE_ASYNC_HANDLER
    if (aPtr->interpTid == NULL) {
	return 1;
    }
#endif
    Tcl_Preserve(aPtr);
    Tcl_Preserve(aPtr->interp);
    Tcl_MutexLock(&aPtr->mutex);
    cmdObjs = aPtr->cmdObjs;
    if (cmdObjs != NULL) {
	nCmdObjs = aPtr->nCmdObjs;
    }
    aPtr->nCmdObjs = 0;
    aPtr->cmdObjs = NULL;
    ms = aPtr->ms;
    sym = aPtr->sym;
    dataPtr = aPtr->dataPtr;
    dataLength = aPtr->dataLength;
    aPtr->dataPtr = NULL;
    aPtr->dataLength = 0;
    Tcl_MutexUnlock(&aPtr->mutex);
    if (cmdObjs != NULL) {
	cmdObjs[nCmdObjs] = Tcl_NewIntObj(ms);
	if (dataPtr != NULL) {
	    cmdObjs[nCmdObjs + 1] = ZbarGetSymbolObj(sym, 0);
	    cmdObjs[nCmdObjs + 2] = Tcl_NewByteArrayObj(dataPtr, dataLength);
	    ckfree((char *) dataPtr);
	} else {
	    cmdObjs[nCmdObjs + 1] = Tcl_NewObj();
	    cmdObjs[nCmdObjs + 2] = Tcl_NewObj();
	}
	Tcl_IncrRefCount(cmdObjs[nCmdObjs]);
	Tcl_IncrRefCount(cmdObjs[nCmdObjs + 1]);
	Tcl_IncrRefCount(cmdObjs[nCmdObjs + 2]);
	ret = Tcl_EvalObjv(aPtr->interp, nCmdObjs + 3, cmdObjs,
			   TCL_GLOBAL_ONLY);
	for (i = 0; i < nCmdObjs + 3; i++) {
	    if (cmdObjs[i] != NULL) {
		Tcl_DecrRefCount(cmdObjs[i]);
		cmdObjs[i] = NULL;
	    }
	}
	ckfree((char *) cmdObjs);
	if (ret == TCL_ERROR) {
	    Tcl_AddErrorInfo(aPtr->interp, "\n    (zbar event handler)");
	    Tcl_BackgroundException(aPtr->interp, ret);
	}
    } else if (dataPtr != NULL) {
	ckfree((char *) dataPtr);
    }
    Tcl_Release(aPtr->interp);
    Tcl_Release(aPtr);
#ifndef USE_ASYNC_HANDLER
    return 1;	/* event handled */
#endif
}

#ifdef USE_ASYNC_HANDLER

/*
 *-------------------------------------------------------------------------
 *
 * ZbarDecodeHandler --
 *
 *	Function triggered by asynchronous event from decoder thread
 *	dispatching do-when-idle handler to invoke Tcl callback.
 *
 *-------------------------------------------------------------------------
 */

static int
ZbarDecodeHandler(ClientData clientData, Tcl_Interp *interp, int code)
{
    Tcl_DoWhenIdle(ZbarDecodeDone, clientData);
    return code;
}

#endif

/*
 *-------------------------------------------------------------------------
 *
 * ZbarAsyncStop --
 *
 *	Stop the decoder thread, if any.
 *
 *-------------------------------------------------------------------------
 */

static int
ZbarAsyncStop(Tcl_Interp *interp, AsyncDecode *aPtr)
{
    int i;

#ifdef USE_ASYNC_HANDLER
    Tcl_CancelIdleCall(ZbarDecodeDone, (ClientData) aPtr);
#endif
    Tcl_MutexLock(&aPtr->mutex);
    if (aPtr->run) {
	int dummy;

	aPtr->run = 0;
	Tcl_ConditionNotify(&aPtr->cond);
	Tcl_MutexUnlock(&aPtr->mutex);
	Tcl_JoinThread(aPtr->tid, &dummy);
	aPtr->tid = NULL;
	Tcl_MutexLock(&aPtr->mutex);
    }
#ifdef USE_ASYNC_HANDLER
    if (aPtr->async != NULL) {
	Tcl_AsyncDelete(aPtr->async);
	aPtr->async = NULL;
    }
#else
    aPtr->interpTid = NULL;
#endif
    Tcl_MutexUnlock(&aPtr->mutex);
    if (aPtr->cmdObjs != NULL) {
	for (i = 0; i < aPtr->nCmdObjs + 3; i++) {
	    if (aPtr->cmdObjs[i] != NULL) {
		Tcl_DecrRefCount(aPtr->cmdObjs[i]);
		aPtr->cmdObjs[i] = NULL;
	    }
	}
	ckfree((char *) aPtr->cmdObjs);
    }
    aPtr->nCmdObjs = 0;
    aPtr->cmdObjs = NULL;
    if (aPtr->dataPtr != NULL) {
	ckfree((char *) aPtr->dataPtr);
	aPtr->dataPtr = NULL;
	aPtr->dataLength = 0;
    }
    return TCL_OK;
}

/*
 *-------------------------------------------------------------------------
 *
 * ZbarAsyncState --
 *
 *	Return status of decoder thread, if any.
 *
 *-------------------------------------------------------------------------
 */

static int
ZbarAsyncStatus(Tcl_Interp *interp, AsyncDecode *aPtr)
{
    int state;
    char *stateString;

    Tcl_MutexLock(&aPtr->mutex);
    if (!aPtr->run) {
	state = 0;
    } else if ((aPtr->imgPtr != NULL) ||
	       (aPtr->pixPtr != NULL) ||
	       (aPtr->cmdObjs != NULL)) {
	state = 2;
    } else {
	state = 1;
    }
    Tcl_MutexUnlock(&aPtr->mutex);
    switch (state) {
    case 1:
	stateString = "ready";
	break;
    case 2:
	stateString = "running";
	break;
    default:
	stateString = "stopped";
	break;
    }
    Tcl_SetResult(interp, stateString, TCL_STATIC);
    return TCL_OK;
}

/*
 *-------------------------------------------------------------------------
 *
 * ZbarAsyncStart --
 *
 *	Check/start the decoder thread. Error cases:
 *	- thread creation failed, could not be started
 *	- thread is already started but still processing a request
 *
 *-------------------------------------------------------------------------
 */

static int
ZbarAsyncStart(Tcl_Interp *interp, AsyncDecode *aPtr)
{
    int success = 0;

    Tcl_MutexLock(&aPtr->mutex);
    if (!aPtr->run) {
	if (Tcl_CreateThread(&aPtr->tid, ZbarThread, (ClientData) aPtr,
			     TCL_THREAD_STACK_DEFAULT, TCL_THREAD_JOINABLE)
	    == TCL_OK) {
	    aPtr->interp = interp;
#ifdef USE_ASYNC_HANDLER
	    aPtr->async = Tcl_AsyncCreate(ZbarDecodeHandler, (ClientData) aPtr);
#else
	    aPtr->interpTid = Tcl_GetCurrentThread();
#endif
	    aPtr->run = success = 1;
	}
    } else if ((aPtr->imgPtr != NULL) ||
	       (aPtr->pixPtr != NULL) ||
	       (aPtr->cmdObjs != NULL)) {
	success = -1;
    } else {
	success = 1;
    }
    Tcl_MutexUnlock(&aPtr->mutex);
    if (success < 0) {
	Tcl_SetResult(interp, "decode process still running", TCL_STATIC);
	return TCL_ERROR;
    }
    if (success == 0) {
	Tcl_SetResult(interp, "decode process not started", TCL_STATIC);
	return TCL_ERROR;
    }
    return TCL_OK;
}

/*
 *-------------------------------------------------------------------------
 *
 * ZbarAsyncFree --
 *
 *	Free zbar::async_decode command client data structure.
 *
 *-------------------------------------------------------------------------
 */

static void
ZbarAsyncFree(char *clientData)
{
    AsyncDecode *aPtr = (AsyncDecode *) clientData;

    ZbarAsyncStop(aPtr->interp, aPtr);
    Tcl_ConditionFinalize(&aPtr->cond);
    Tcl_MutexFinalize(&aPtr->mutex);
    ckfree((char *) aPtr);
}

/*
 *-------------------------------------------------------------------------
 *
 * ZbarAsyncCmdDeleted --
 *
 *	Callback for deletion of zbar::async_decode command.
 *
 *-------------------------------------------------------------------------
 */

static void
ZbarAsyncCmdDeleted(ClientData clientData)
{
    Tcl_EventuallyFree(clientData, ZbarAsyncFree);
}

/*
 *-------------------------------------------------------------------------
 *
 * ZbarAsyncDeocdeObjCmd --
 *
 *	zbar::async_decode Tcl command, asynchronous decoding.
 *	Command formats/arguments are
 *
 *	Stop (finish) decoder thread, releasing resources
 *
 *		zbar::async_decode stop
 *
 *	Return status of asynchronous decoding process
 *
 *		zbar::async_decode status
 *
 *	Start decoding an image
 *
 *		zbar::async_decode photoEtc callback ?syms?
 *
 *		photoEtc	photo image name or list of
 *				{width height bpp bytes}
 *		callback	procedure to invoke at end of
 *				decoding process
 *		syms		list of enabled symbologies
 *
 *	Arguments appended to callback
 *
 *		time	decode/processing time in milliseconds
 *		type	decoded symbol type for first found symbol or empty
 *		data	decoded symbol data for first found symbol or empty
 *
 *-------------------------------------------------------------------------
 */

static int
ZbarAsyncDecodeObjCmd(ClientData clientData, Tcl_Interp *interp,
		      int objc,  Tcl_Obj *CONST objv[])
{
    AsyncDecode *aPtr = (AsyncDecode *) clientData;
    Tk_PhotoImageBlock block;
    int bpp, x, y, nElems, nCmdObjs;
    unsigned char *pixPtr;
    zbar_image_t *imgPtr;
    Tcl_Obj **elems, **cmdObjs;
    unsigned char enabled[sizeof(ZbarSyms) / sizeof(ZbarSyms[0])];

    if ((objc < 2) || (objc > 4)) {
	Tcl_WrongNumArgs(interp, 1, objv, "stop|photoEtc ?callback? ?syms?");
	return TCL_ERROR;
    }
    if (objc == 2) {
	const char *cmd = Tcl_GetString(objv[1]);

	if (strcmp(cmd, "status") == 0) {
	    return ZbarAsyncStatus(interp, aPtr);
	}
	if (strcmp(cmd, "stop") == 0) {
	    return ZbarAsyncStop(interp, aPtr);
	}
	Tcl_WrongNumArgs(interp, 1, objv, "status|stop");
	return TCL_ERROR;
    }
    if (Tcl_ListObjGetElements(interp, objv[1], &nElems, &elems) != TCL_OK) {
	return TCL_ERROR;
    }
    if (nElems < 1) {
	Tcl_SetResult(interp, "need photo image or list", TCL_STATIC);
	return TCL_ERROR;
    } else if (nElems >= 4) {
	int size, length;

	if ((Tcl_GetIntFromObj(interp, elems[0], &block.width) != TCL_OK) ||
	    (Tcl_GetIntFromObj(interp, elems[1], &block.height) != TCL_OK) ||
	    (Tcl_GetIntFromObj(interp, elems[2], &bpp) != TCL_OK)) {
	    return TCL_ERROR;
	}
	if ((bpp != 1) || (bpp != 3)) {
	    Tcl_SetResult(interp, "unsupported image depth", TCL_STATIC);
	    return TCL_ERROR;
	}
	size = block.width * block.height;
	if (size <= 0) {
	    Tcl_SetResult(interp, "invalid image size", TCL_STATIC);
	    return TCL_ERROR;
	}
	block.pixelPtr = Tcl_GetByteArrayFromObj(elems[3], &length);
	if ((block.pixelPtr == NULL) || (length < size)) {
	    Tcl_SetResult(interp, "malformed image", TCL_STATIC);
	    return TCL_ERROR;
	}
	pixPtr = (unsigned char *) attemptckalloc(block.width * block.height);
	if (pixPtr == NULL) {
	    Tcl_SetResult(interp, "out of memory", TCL_STATIC);
	    return TCL_ERROR;
	}
	if (bpp == 3) {
	    for (y = 0; y < block.height; y++) {
		unsigned char *srcPtr, *dstPtr;
		int tmp;

		srcPtr = block.pixelPtr + y * block.width * 3;
		dstPtr = pixPtr + y * block.width;
		for (x = 0; x < block.width; x++) {
#if !defined(__arm__)
		    tmp  = weightR[*srcPtr++];
		    tmp += weightG[*srcPtr++];
		    tmp += weightB[*srcPtr++];
#else
		    tmp  = 19518 * *srcPtr++;
		    tmp += 38319 * *srcPtr++;
		    tmp +=  7442 * *srcPtr++;
#endif
		    *dstPtr++ = tmp >> 16;
		}
	    }
	} else {
	    memcpy(pixPtr, block.pixelPtr, block.width * block.height);
	}
    } else {
#ifdef ZBAR_NO_TK
	Tcl_SetResult(interp, "need list of width, height, bpp, bytes",
		      TCL_STATIC);
	return TCL_ERROR;
#else
	Tk_PhotoHandle handle;

	handle = Tk_FindPhoto(interp, Tcl_GetString(objv[1]));
	if (handle == NULL) {
	    Tcl_SetObjResult(interp, Tcl_ObjPrintf("photo \"%s\" not found",
						   Tcl_GetString(objv[1])));
	    return TCL_ERROR;
	}
	if (Tk_PhotoGetImage(handle, &block) != 1) {
	    Tcl_SetResult(interp, "error retrieving photo image", TCL_STATIC);
	    return TCL_ERROR;
	}
	if ((block.offset[0] == block.offset[1]) &&
	    (block.offset[0] == block.offset[2])) {
	    bpp = 1;
	} else {
	    bpp = 3;
	}
	pixPtr = (unsigned char *) attemptckalloc(block.width * block.height);
	if (pixPtr == NULL) {
	    Tcl_SetResult(interp, "out of memory", TCL_STATIC);
	    return TCL_ERROR;
	}
	for (y = 0; y < block.height; y++) {
	    unsigned char *srcPtr, *dstPtr;
	    int tmp;

	    srcPtr = block.pixelPtr + y * block.pitch;
	    dstPtr = pixPtr + y * block.width;
	    for (x = 0; x < block.width; x++) {
		if (bpp == 1) {
		    dstPtr[0] = srcPtr[block.offset[0]];
		} else {
#if !defined(__arm__)
		    tmp  = weightR[srcPtr[block.offset[0]]];
		    tmp += weightG[srcPtr[block.offset[1]]];
		    tmp += weightB[srcPtr[block.offset[2]]];
#else
		    tmp  = 19518 * srcPtr[block.offset[0]];
		    tmp += 38319 * srcPtr[block.offset[1]];
		    tmp +=  7442 * srcPtr[block.offset[2]];
#endif
		    dstPtr[0] = tmp >> 16;
		}
		dstPtr += 1;
		srcPtr += block.pixelSize;
	    }
	}
#endif
    }
    if (ZbarAsyncStart(interp, aPtr) != TCL_OK) {
	ckfree((char *) pixPtr);
	return TCL_ERROR;
    }
    imgPtr = zbar_image_create();
    if (imgPtr == NULL) {
	ckfree((char *) pixPtr);
	Tcl_SetResult(interp, "error creating image", TCL_STATIC);
	return TCL_ERROR;
    }
    zbar_image_set_format(imgPtr, zbar_fourcc('Y', '8', '0', '0'));
    zbar_image_set_size(imgPtr, block.width, block.height);
    zbar_image_set_data(imgPtr, pixPtr, block.width * block.height, NULL);
    if (Tcl_ListObjGetElements(interp, objv[2], &nCmdObjs, &cmdObjs)
	!= TCL_OK) {
	zbar_image_destroy(imgPtr);
	ckfree((char *) pixPtr);
	return TCL_ERROR;
    }
    if (nCmdObjs <= 0) {
	Tcl_SetResult(interp, "empty callback", TCL_STATIC);
	zbar_image_destroy(imgPtr);
	ckfree((char *) pixPtr);
	return TCL_ERROR;
    }
    memset(enabled, 1, sizeof(enabled));
    if (objc > 3) {
	if (Tcl_ListObjGetElements(interp, objv[3], &nElems, &elems)
	    != TCL_OK) {
	    zbar_image_destroy(imgPtr);
	    ckfree((char *) pixPtr);
	    return TCL_ERROR;
	}
	if (nElems > 0) {
	    memset(enabled, 0, sizeof(enabled));
	    for (x = 0; x < nElems; x++) {
		zbar_symbol_type_t t = ZbarGetSymbolType(elems[x], &y);

		if (t != ZBAR_SYMBOL) {
		    enabled[y] = 1;
		}
	    }
	}
    }
    Tcl_MutexLock(&aPtr->mutex);
    aPtr->pixPtr = pixPtr;
    aPtr->imgPtr = imgPtr;
    aPtr->nCmdObjs = nCmdObjs;
    aPtr->cmdObjs =
	(Tcl_Obj **) ckalloc((nCmdObjs + 3) * sizeof(Tcl_Obj *));
    for (x = 0; x < nCmdObjs; x++) {
	aPtr->cmdObjs[x] = cmdObjs[x];
	Tcl_IncrRefCount(aPtr->cmdObjs[x]);
    }
    aPtr->cmdObjs[x++] = NULL;
    aPtr->cmdObjs[x++] = NULL;
    aPtr->cmdObjs[x++] = NULL;
    memcpy(aPtr->enabled, enabled, sizeof(aPtr->enabled));
    Tcl_ConditionNotify(&aPtr->cond);
    Tcl_MutexUnlock(&aPtr->mutex);
    return TCL_OK;
}

#endif /* TCL_THREADS */

/*
 *-------------------------------------------------------------------------
 *
 * ZbarDecodeObjCmd --
 *
 *	zbar::decode Tcl command, synchronous decoding.
 *	Command arguments are
 *
 *		zbar::decode photoEtc ?syms?
 *
 *		photoEtc	photo image name or list of
 *				{width height bpp bytes}
 *		syms	list of enabled symbologies
 *
 *	Result is a list with three element
 *
 *		time	decode/processing time in milliseconds
 *		type	decoded symbol type for first found symbol or empty
 *		data	decoded symbol data for first found symbol or empty
 *
 *-------------------------------------------------------------------------
 */

static int
ZbarDecodeObjCmd(ClientData unused, Tcl_Interp *interp,
		 int objc,  Tcl_Obj *CONST objv[])
{
    Tk_PhotoImageBlock block;
    int bpp, x, y, nElems;
    unsigned char *pixPtr = NULL;
    zbar_image_scanner_t *scanPtr;
    zbar_image_t *imgPtr;
    const zbar_symbol_t *symPtr;
    Tcl_Time now;
    Tcl_WideInt tw[2];
    Tcl_Obj **elems, *list[3];

    if ((objc < 2) || (objc > 3)) {
	Tcl_WrongNumArgs(interp, 1, objv, "photoEtc ?syms?");
	return TCL_ERROR;
    }
    Tcl_GetTime(&now);
    tw[0] = (Tcl_WideInt) now.sec * 1000 + now.usec / 1000;
    if (Tcl_ListObjGetElements(interp, objv[1], &nElems, &elems) != TCL_OK) {
	return TCL_ERROR;
    }
    if (nElems < 1) {
	Tcl_SetResult(interp, "need photo image or list", TCL_STATIC);
	return TCL_ERROR;
    } else if (nElems >= 4) {
	int size, length;

	if ((Tcl_GetIntFromObj(interp, elems[0], &block.width) != TCL_OK) ||
	    (Tcl_GetIntFromObj(interp, elems[1], &block.height) != TCL_OK) ||
	    (Tcl_GetIntFromObj(interp, elems[2], &bpp) != TCL_OK)) {
	    return TCL_ERROR;
	}
	if ((bpp != 1) || (bpp != 3)) {
	    Tcl_SetResult(interp, "unsupported image depth", TCL_STATIC);
	    return TCL_ERROR;
	}
	size = block.width * block.height;
	if (size <= 0) {
	    Tcl_SetResult(interp, "invalid image size", TCL_STATIC);
	    return TCL_ERROR;
	}
	block.pixelPtr = Tcl_GetByteArrayFromObj(elems[3], &length);
	if ((block.pixelPtr == NULL) || (length < size)) {
	    Tcl_SetResult(interp, "malformed image", TCL_STATIC);
	    return TCL_ERROR;
	}
	if (bpp == 3) {
	    pixPtr = (unsigned char *)
		attemptckalloc(block.width * block.height);
	    if (pixPtr == NULL) {
		Tcl_SetResult(interp, "out of memory", TCL_STATIC);
		return TCL_ERROR;
	    }
	    for (y = 0; y < block.height; y++) {
		unsigned char *srcPtr, *dstPtr;
		int tmp;

		srcPtr = block.pixelPtr + y * block.width * 3;
		dstPtr = pixPtr + y * block.width;
		for (x = 0; x < block.width; x++) {
#if !defined(__arm__)
		    tmp  = weightR[*srcPtr++];
		    tmp += weightG[*srcPtr++];
		    tmp += weightB[*srcPtr++];
#else
		    tmp  = 19518 * *srcPtr++;
		    tmp += 38319 * *srcPtr++;
		    tmp +=  7442 * *srcPtr++;
#endif
		    *dstPtr++ = tmp >> 16;
		}
	    }
	}
    } else {
#ifdef ZBAR_NO_TK
	Tcl_SetResult(interp, "need list of width, height, bpp, bytes",
		      TCL_STATIC);
	return TCL_ERROR;
#else
	Tk_PhotoHandle handle;

	handle = Tk_FindPhoto(interp, Tcl_GetString(objv[1]));
	if (handle == NULL) {
	    Tcl_SetObjResult(interp, Tcl_ObjPrintf("photo \"%s\" not found",
						   Tcl_GetString(objv[1])));
	    return TCL_ERROR;
	}
	if (Tk_PhotoGetImage(handle, &block) != 1) {
	    Tcl_SetResult(interp, "error retrieving photo image", TCL_STATIC);
	    return TCL_ERROR;
	}
	if ((block.offset[0] == block.offset[1]) &&
	    (block.offset[0] == block.offset[2])) {
	    bpp = 1;
	} else {
	    bpp = 3;
	}
	pixPtr = (unsigned char *) attemptckalloc(block.width * block.height);
	if (pixPtr == NULL) {
	    Tcl_SetResult(interp, "out of memory", TCL_STATIC);
	    return TCL_ERROR;
	}
	for (y = 0; y < block.height; y++) {
	    unsigned char *srcPtr, *dstPtr;
	    int tmp;

	    srcPtr = block.pixelPtr + y * block.pitch;
	    dstPtr = pixPtr + y * block.width;
	    for (x = 0; x < block.width; x++) {
		if (bpp == 1) {
		    dstPtr[0] = srcPtr[block.offset[0]];
		} else {
#if !defined(__arm__)
		    tmp  = weightR[srcPtr[block.offset[0]]];
		    tmp += weightG[srcPtr[block.offset[1]]];
		    tmp += weightB[srcPtr[block.offset[2]]];
#else
		    tmp  = 19518 * srcPtr[block.offset[0]];
		    tmp += 38319 * srcPtr[block.offset[1]];
		    tmp +=  7442 * srcPtr[block.offset[2]];
#endif
		    dstPtr[0] = tmp >> 16;
		}
		dstPtr += 1;
		srcPtr += block.pixelSize;
	    }
	}
#endif
    }
    scanPtr = zbar_image_scanner_create();
    if (scanPtr == NULL) {
	if (pixPtr != NULL) {
	    ckfree((char *) pixPtr);
	}
	Tcl_SetResult(interp, "error creating image scanner", TCL_STATIC);
	return TCL_ERROR;
    }
    if (objc > 2) {
	if (Tcl_ListObjGetElements(interp, objv[2], &nElems, &elems)
	    != TCL_OK) {
	    zbar_image_scanner_destroy(scanPtr);
	    if (pixPtr != NULL) {
		ckfree((char *) pixPtr);
	    }
	    return TCL_ERROR;
	}
	zbar_image_scanner_set_config(scanPtr, ZBAR_NONE, ZBAR_CFG_ENABLE, 0);
	for (x = 0; x < nElems; x++) {
	    zbar_symbol_type_t t = ZbarGetSymbolType(elems[x], NULL);

	    if (t != ZBAR_SYMBOL) {
		zbar_image_scanner_set_config(scanPtr, t, ZBAR_CFG_ENABLE, 1);
	    }
	}
    }
    imgPtr = zbar_image_create();
    if (imgPtr == NULL) {
	zbar_image_scanner_destroy(scanPtr);
	if (pixPtr != NULL) {
	    ckfree((char *) pixPtr);
	}
	Tcl_SetResult(interp, "error creating image", TCL_STATIC);
	return TCL_ERROR;
    }
    zbar_image_set_format(imgPtr, zbar_fourcc('Y', '8', '0', '0'));
    zbar_image_set_size(imgPtr, block.width, block.height);
    zbar_image_set_data(imgPtr, (pixPtr == NULL) ? block.pixelPtr : pixPtr,
			block.width * block.height, NULL);
    zbar_scan_image(scanPtr, imgPtr);
    Tcl_GetTime(&now);
    tw[1] = (Tcl_WideInt) now.sec * 1000 + now.usec / 1000;
    x = tw[1] - tw[0];
    if (x < 0) {
	x = -1;
    }
    list[0] = Tcl_NewIntObj(x);
    /* report only first symbol as result */
    symPtr = zbar_image_first_symbol(imgPtr);
    if (symPtr != NULL) {
	zbar_symbol_type_t t = zbar_symbol_get_type(symPtr);
	const unsigned char *dp = (const unsigned char *)
	    zbar_symbol_get_data(symPtr);

	list[1] = ZbarGetSymbolObj(t, 0);
	list[2] = Tcl_NewByteArrayObj(dp,
				      zbar_symbol_get_data_length(symPtr));
    } else {
	list[1] = Tcl_NewObj();
	list[2] = Tcl_NewObj();
    }
    zbar_image_destroy(imgPtr);
    zbar_image_scanner_destroy(scanPtr);
    if (pixPtr != NULL) {
	ckfree((char *) pixPtr);
    }
    Tcl_SetObjResult(interp, Tcl_NewListObj(3, list));
    return TCL_OK;
}

/*
 *-------------------------------------------------------------------------
 *
 * ZbarSymbolTypesObjCmd --
 *
 *	zbar::symbol_types Tcl command, returns list of symbol types.
 *	Command format
 *
 *		zbar::symbol_types
 *
 *	Result is a list with supported symbologies
 *
 *-------------------------------------------------------------------------
 */

static int
ZbarSymbolTypesObjCmd(ClientData unused, Tcl_Interp *interp,
		      int objc,  Tcl_Obj *CONST objv[])
{
    if (objc != 1) {
	Tcl_WrongNumArgs(interp, 1, objv, "");
	return TCL_ERROR;
    }
    Tcl_SetObjResult(interp, ZbarGetSymbolObj(ZBAR_NONE, 1));
    return TCL_OK;
}

#ifndef TCL_THREADS

/*
 *-------------------------------------------------------------------------
 *
 * ZbarAsyncDecodeObjCmd --
 *
 *	zbar::async_decode Tcl command, asynchronous decoding.
 *	Dummy for non-threaded builds
 *
 *-------------------------------------------------------------------------
 */

static int
ZbarAsyncDecodeObjCmd_NoThreads(ClientData clientData, Tcl_Interp *interp,
				int objc,  Tcl_Obj *CONST objv[])
{
    Tcl_SetResult(interp, "unsupported in non-threaded builds",
		  TCL_STATIC);
    return TCL_OK;
}

#endif

/*
 *-------------------------------------------------------------------------
 *
 * ZbarInit --
 *
 *	Module initializer
 *
 *-------------------------------------------------------------------------
 */

int
Zbar_Init(Tcl_Interp *interp)
{
#ifdef TCL_THREADS
    AsyncDecode *aPtr;
    int major = 0, minor = 0;
    const char *val;
#endif

#ifdef USE_TCL_STUBS
    if (Tcl_InitStubs(interp, "8.4", 0) == NULL)
#else
    if (Tcl_PkgRequire(interp, "Tcl", "8.4", 0) == NULL)
#endif
    {
	return TCL_ERROR;
    }
#ifndef ZBAR_NO_TK
#ifdef USE_TK_STUBS
    if (Tk_InitStubs(interp, "8.4", 0) == NULL)
#else
    if (Tcl_PkgRequire(interp, "Tk", "8.4", 0) == NULL)
#endif
    {
	return TCL_ERROR;
    }
#endif
#ifdef TCL_THREADS
    aPtr = (AsyncDecode *) ckalloc(sizeof(AsyncDecode));
    memset(aPtr, 0, sizeof(AsyncDecode));
    Tcl_CreateObjCommand(interp, "zbar::async_decode",
			 ZbarAsyncDecodeObjCmd, (ClientData) aPtr,
			 ZbarAsyncCmdDeleted);
    Tcl_GetVersion(&major, &minor, NULL, NULL);
    if ((major > 8) || ((major == 8) && (minor > 6))) {
	aPtr->tip609 = 1;
    } else {
	val = Tcl_GetVar2(interp, "tcl_platform", "tip609", TCL_GLOBAL_ONLY);
	if ((val != NULL) && *val && (*val != '0')) {
	    aPtr->tip609 = 1;
	}
    }
#else
    Tcl_CreateObjCommand(interp, "zbar::async_decode",
			 ZbarAsyncDecodeObjCmd_NoThreads, NULL, NULL);
#endif
    Tcl_CreateObjCommand(interp, "zbar::decode", ZbarDecodeObjCmd,
			 (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
    Tcl_CreateObjCommand(interp, "zbar::symbol_types", ZbarSymbolTypesObjCmd,
			 (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
    Tcl_PkgProvide(interp, PACKAGE_NAME, PACKAGE_VERSION);
    return TCL_OK;
}

/*
 * Local Variables:
 * mode: c
 * c-basic-offset: 4
 * fill-column: 78
 * tab-width: 8
 * End:
 */