/* Copyright 1989 GROUPE BULL -- See license conditions in file COPYRIGHT
 * Copyright 1989 Massachusetts Institute of Technology
 */
/***********************\
* 		        *
*  WOOL_OBJECT: Pixmap  *
*  BODY		        *
* 		        *
\***********************/

#define INCLUDE_PIXMAP_H
#include <string.h>
#include "EXTERN.h"
#include <stdio.h>
#include <X11/xpm.h>
#include "wool.h"
#include "wl_string.h"
#include "wl_atom.h"
#include "wl_pointer.h"
#include "wl_active.h"
#include "wl_number.h"
#include "wl_label.h"
#include "wl_list.h"
#include "gwm.h"
#include <X11/Xatom.h>
#include <X11/Xutil.h>
#include "INTERN.h"
#undef INCLUDE_PIXMAP_H
#include "wl_pixmap.h"
#include "def_bitmap.h"

/* local prototyping */

WOOL_OBJECT 	wool_default_bitmap_make();
WOOL_OBJECT 	wool_default_pixmap_make();

/* when debugging, maintain the screen to which belongs the pixmap */

#ifdef DEBUG
#define StorePixmapScreen(pixmap, root) \
	pixmap -> screen = ScreenOfRoot(root)
#else
#define StorePixmapScreen(pixmap, root)
#endif /* DEBUG */

/*
 * makes a WOOL pixmap out of a X11 bitmap (or appropriate depth pixmap)
 */

WOOL_Pixmap
WLPixmap_make(pixmap)
Pixmap	pixmap;
{
    WOOL_Pixmap     object = (WOOL_Pixmap)
	Malloc(sizeof(struct _WOOL_Pixmap));
    unsigned int    bw, depth;
    Window          root;

    zrt_put(object);
    object -> type = WLPixmap;
    object->mask = 0;
    XGetGeometry(dpy, pixmap, &root, &object -> x_hot, &object -> y_hot,
		 &object -> width, &object -> height, &bw, &depth);

    if (!object -> width || !object -> height || !depth) {	/* ERROR */
	wool_default_pixmap_make(object, "invalid pixmap %s", "size");
    } else if (depth == 1) {	/* BITMAP */
	Trace('p', ("WLPixmap_make: create pixmap %d %d %d\n",
		     object -> width,
		     object -> height, Context->depth));
	object -> pixmap = XCreatePixmap(dpy, Context->root, object -> width,
					 object -> height, Context->depth);
	XSetForeground(dpy, Context->gc.Work, Context -> pixel.Back);
	XFillRectangle(dpy, object -> pixmap, Context->gc.Work, 0, 0,
		       object -> width, object -> height);
	XSetForeground(dpy, Context->gc.Set, Context -> pixel.Fore);
	XSetStipple(dpy, Context->gc.Set, pixmap);
	XFillRectangle(dpy, object -> pixmap, Context->gc.Set, 0, 0,
		       object -> width, object -> height);
    } else if (depth == Context->depth) {	/* PIXMAP */
	Trace('p', ("WLPixmap_make: create pixmap %d %d %d\n",
		     object -> width,
		     object -> height, Context->depth));
 	object -> pixmap = XCreatePixmap(dpy, Context->root, object -> width,
					 object -> height, Context->depth);
 	XCopyArea(dpy, pixmap, object->pixmap, Context->gc.Work, 0, 0,
 		  object->width, object->height, 0, 0);
    } else {			/* ERROR */
	wool_default_pixmap_make(object, "Pixmap is of depth %d", depth);
    }
    StorePixmapScreen(object, root);
    return object;
}

/*
 * the physical reading of a bitmap. Used for bitmaps & cursors
 * if parameter is a label returns it!
 */

WOOL_OBJECT
wool_raw_bitmap_make(filename)
WOOL_String	filename;
{
    char           *name;
    WOOL_Pixmap     object;
    char            temp_filename[MAX_TEMP_STRING_SIZE];

    must_be_string(filename, 0);
    object = (WOOL_Pixmap) Malloc(sizeof(struct _WOOL_Pixmap));
    zrt_put(object);
    object -> type = WLPixmap;
    object->mask = 0;
    name = (char *) file_in_path(filename -> string,
				 BITMAP_EXTENSION, wool_path, temp_filename);
    if (!name) {
	return wool_default_bitmap_make(object,
			  "GWM: Cannot find Bitmap %s", filename -> string);
    }
    switch (XReadBitmapFile(dpy, Context->root, name,
		     &object -> width, &object -> height, &object -> pixmap,
			    &object -> x_hot, &object -> y_hot)) {
    case BitmapOpenFailed:
	return wool_default_bitmap_make(object,
					"Cannot open file %s", name);
    case BitmapFileInvalid:
	return wool_default_bitmap_make(object,
					"File %s is not a bitmap", name);
    case BitmapNoMemory:
	wool_error("No memory to load file %s", name);
	break;
    }
    return (WOOL_OBJECT) object;
}

/* Given a pre-allocated WOOL_Pixmap, fill it with  the default
 * bitmap or pixmap, rexpectively
 */

WOOL_OBJECT
wool_default_bitmap_make(object, reason_format, reason)
WOOL_Pixmap object;			/* empty wool structure */
char *reason_format;			/* text of the error message or 0 */
char *reason;				/* data for %s in reason format */
{
    if (reason_format) {
	wool_printf(reason_format, reason);
	wool_puts(", using default instead\n");
    }
    object -> width = def_bitmap_width;
    object -> height = def_bitmap_height;
    object -> x_hot = def_bitmap_x_hot;
    object -> y_hot = def_bitmap_y_hot;
    object -> pixmap = Context -> DefaultBitmap;
    return (WOOL_OBJECT) object;
}

WOOL_OBJECT
wool_default_pixmap_make(object, reason_format, reason)
WOOL_Pixmap object;                     /* empty wool structure */
char *reason_format;                    /* text of the error message or 0 */
char *reason;                           /* data for %s in reason format */
{
    if (reason_format) {
        wool_printf(reason_format, reason);
        wool_puts(", using default instead\n");
    }
    object -> width = def_bitmap_width;
    object -> height = def_bitmap_height;

    XSetForeground(dpy, Context->gc.Work, Context -> pixel.Back);
    Trace('p', ("wool_default_pixmap_make: create pixmap %d %d %d\n",
		 object -> width, object -> height,
		 Context->depth));
    object -> pixmap = XCreatePixmap(dpy, Context->root,
				     object -> width, object -> height,
				     Context->depth);
    XFillRectangle(dpy, object -> pixmap, Context->gc.Work, 0, 0,
		   object -> width, object -> height);
    XSetForeground(dpy, Context->gc.Set, Context -> pixel.Fore);
    XSetStipple(dpy, Context->gc.Set, Context -> DefaultBitmap);
    XFillRectangle(dpy, object -> pixmap, Context->gc.Set, 0, 0,
		   object -> width, object -> height);
    StorePixmapScreen(object, Context -> root);
    return (WOOL_OBJECT) object;
}

/*
 * to overlay bitmaps:
 * (layered-bitmap-make back "file1" color1 "file2" color2 ...)
 * filenames can be active-labels
 */

WOOL_Pixmap
wool_layered_bitmap_make(argc, argv)
int argc;
WOOL_Pixmap argv[];
{
    Pixmap          pix;
    int             i;
    unsigned int    width = 0, height = 0;
    WOOL_Pixmap     object;
    struct _Box     box;
    WOOL_Label      label;
    Graphic_desc   *graphics;

    if ((argc == 0) || (argc > 2 && (argc % 2 == 0)))
	wool_error(BAD_NUMBER_OF_ARGS, argc);
    if (argc == 1) {		/* one args re-calls with default colors */
	WOOL_Number     new_argv[3];

	new_argv[0] = WLNumber_make(Context -> pixel.Back);
	new_argv[2] = WLNumber_make(Context -> pixel.Fore);
	new_argv[1] = (WOOL_Number) argv[0];
	return (WOOL_Pixmap) wool_layered_bitmap_make(3, new_argv);
    }
    if (argc == 2) {		/* two args make a blank bitmaps */
	return wool_make_blank_bitmap(argv[0], argv[1]);
    }
    /* multi-args form */
    must_be_number(argv[0], 0);
    must_be_number(argv[2], 2);
    graphics = (Graphic_desc *) Malloc(sizeof(Graphic_desc) * argc);
    
    /* first determine the size of the final pixmap and load the bitmaps */
    for (i = 1; i < argc; i += 2) {
	if (is_a_string(argv[i])) {
	    graphics[i].is_bitmap = 1;
	    argv[i] = (WOOL_Pixmap) wool_raw_bitmap_make(argv[i]);
	} else {
	    graphics[i].is_bitmap = 0;
	}
	WOOL_send(WOOL_get_dimensions, argv[i], (argv[i], &box));
	graphics[i].width = box.width;
	graphics[i].height = box.height;
	width = Max(width, box.width);
	height = Max(height, box.height);
    }

    /* then lay the graphics one on top of another */
    XSetForeground(dpy, Context->gc.Work, ((WOOL_Number) argv[0]) -> number);
    Trace('p', ("wool_layered_bitmap_make: create pixmap %d %d %d\n",
		 width, height, Context->depth));
    pix = XCreatePixmap(dpy, Context->root, width, height, Context->depth);
    XFillRectangle(dpy, pix, Context->gc.Work, 0, 0, width, height);
    for (i = 1; i < argc; i += 2) {
	must_be_number(argv[i + 1], i + 1);
	if (graphics[i].is_bitmap) {
	    XSetForeground(dpy, Context->gc.Set,
	    	((WOOL_Number) argv[i + 1]) -> number);
	    XSetStipple(dpy, Context->gc.Set, argv[i] -> pixmap);
	    XSetTSOrigin(dpy, Context->gc.Set, (width - argv[i] -> width) / 2, (height - argv[i] -> height) / 2);
	    XFillRectangle(dpy, pix, Context->gc.Set,
	    (width - argv[i] -> width) / 2, (height - argv[i] -> height) / 2,
			   argv[i] -> width, argv[i] -> height);
	    XSetTSOrigin(dpy, Context->gc.Set, 0, 0);
	} else if (argv[i] -> type == WLPixmap) {
	    XCopyArea(dpy, argv[i] -> pixmap, pix, Context->gc.Work, 0, 0,
		      argv[i] -> width, argv[i] -> height,
		      (width - argv[i] -> width) / 2,
		      (height - argv[i] -> height) / 2);
	} else if (argv[i] -> type == WLLabel) {	/* label */
	    label = (WOOL_Label) argv[i];
	    XSetFont(dpy, Context->gc.Work, label -> font);
	    XSetForeground(dpy, Context->gc.Work,
			   ((WOOL_Number) argv[i + 1]) -> number);
	    XDrawString(dpy, pix, Context->gc.Work,
			label -> x, label -> y, label -> label -> string,
			strlen(label -> label -> string));
	}
    }

    /* then creates the WOOL_Pixmap */
    object = (WOOL_Pixmap) Malloc(sizeof(struct _WOOL_Pixmap));
    zrt_put(object);
    object -> type = WLPixmap;
    object->mask = 0;
    object -> pixmap = pix;
    object -> x_hot = object -> y_hot = 0;
    object -> width = width;
    object -> height = height;
    Free(graphics);
    StorePixmapScreen(object, Context -> root);
    return object;
}

/*
 * makes a pixmap of "foreground" color of a given size
 */

WOOL_Pixmap
wool_make_blank_bitmap(width, height)
WOOL_Number width;
WOOL_Number height;
{
    WOOL_Pixmap     object;
    Pixmap          pix;
    WOOL_Pixmap     tile;

    get_val_from_context(tile, WA_tile);

    Trace('p', ("wool_make_blank_bitmap: create pixmap %d %d %d\n",
		 width -> number, height -> number, Context->depth));
    pix = XCreatePixmap(dpy, Context->root,
			width -> number, height -> number, Context->depth);
    if (tile->type != WLPixmap) {
	/* no tile = use solid color */
	XSetForeground(dpy, Context->gc.Work, Context -> pixel.Fore);
	XFillRectangle(dpy, pix, Context->gc.Work, 0, 0,
		       width -> number, height -> number);
    } else {
	/* tile overrides color */
	XSetTile(dpy, Context->gc.Tile, tile->pixmap);
	XFillRectangle(dpy, pix, Context->gc.Tile, 0, 0,
		       width -> number, height -> number);
    }
    object = (WOOL_Pixmap) Malloc(sizeof(struct _WOOL_Pixmap));
    zrt_put(object);
    object -> type = WLPixmap;
    object->mask = 0;
    object -> pixmap = pix;
    object -> x_hot = object -> y_hot = 0;
    object -> width = width -> number;
    object -> height = height -> number;
    StorePixmapScreen(object, Context -> root);
    return object;
}

/*
 * A stamp is a string "stamped" on the background to yield a bitmap
 */

WOOL_OBJECT
wool_stamp_make(argc, argv)
int             argc;
WOOL_String     argv[];
{
    int             up, down, dir, x, y, width, height, font;
    XCharStruct     extent;
    WOOL_Pixmap     object;

    if (argc == 0 || argc > 2)
        return wool_error(BAD_NUMBER_OF_ARGS, argc);
    must_be_string(argv[0], 0);
    if (argc == 2) {
        must_be_number(argv[1], 1);
        font = ((WOOL_Number) argv[1]) -> number;
    } else
        font = DefaultFont;
    object  = (WOOL_Pixmap) Malloc(sizeof(struct _WOOL_Pixmap));
    XQueryTextExtents(dpy, font,
		      argv[0] -> string, strlen(argv[0] -> string),
		      &dir, &up, &down, &extent);
    x = DefaultLabelHorizontalMargin - extent.lbearing;
    y = DefaultLabelVerticalMargin + up;
    width = Max(1, extent.width + 2 * DefaultLabelHorizontalMargin);
    height = Max(1, up + down + 2 * DefaultLabelVerticalMargin);

    zrt_put(object);
    object -> type = WLPixmap;
    object->mask = 0;
    Trace('p', ("wool_make_blank_bitmap: create pixmap %d %d %d\n",
		 width, height, Context->depth));
    object -> pixmap = XCreatePixmap(dpy, Context->root, width, height, 
	Context->depth);
    object -> x_hot = object -> y_hot = 0;
    object -> width = width;
    object -> height = height;

    XSetForeground(dpy, Context->gc.Work, Context -> pixel.Back);
    XFillRectangle(dpy, object -> pixmap, Context->gc.Work,
		   0, 0, width, height);
    XSetFont(dpy, Context->gc.Work, font);
    XSetForeground(dpy, Context->gc.Work, Context -> pixel.Fore);
    XDrawString(dpy, object -> pixmap, Context->gc.Work,
		x, y, argv[0] -> string,
		strlen(argv[0] -> string));
    StorePixmapScreen(object, Context -> root);
    return (WOOL_OBJECT) object;
}

/*
 * WLPixmap_print:
 * a /  bitmap prints as [PIXMAP widthxheight id]
 */

WOOL_OBJECT 
WLPixmap_print(obj)
WOOL_Pixmap     obj;
{
    wool_printf("{PIXMAP %dx", obj -> width);
    wool_printf("%d", obj -> height);
    wool_printf(" 0x%x", obj -> pixmap);
#ifdef DEBUG
    wool_printf(", on screen %d", obj -> screen);
#endif /* DEBUG */
    wool_puts("}");
    return (WOOL_OBJECT) obj;
}

/*
 * WLPixmap_free:
 * The structure is just freed, and string released.
 */

WOOL_OBJECT 
WLPixmap_free(obj)
WOOL_Pixmap     obj;
{
	if (obj->pixmap != Context -> DefaultBitmap)
		XFreePixmap(dpy, obj->pixmap);
	if (obj->mask)
	    XFreePixmap(dpy, obj->mask);
	Free(obj);
	return NULL;
}

/*
 * returns the dimensions of the strings in the font + margins
 * x,y is the start of the string (baseline) in the box.
 */

WOOL_OBJECT
WLPixmap_get_dimensions(bitmap, box)
WOOL_Pixmap bitmap;
Box	box;	     		/* RETURN the dimensions */
{
    box -> x = 0;
    box -> y = 0;
    box -> width = bitmap -> width;
    box -> height = bitmap -> height;
    box -> shape = bitmap -> mask;
    return NULL;
}

/*
 * opening a bitmap is putting it in backgroud
 */

WOOL_OBJECT
WLPixmap_open(bitmap, wob)
WOOL_Pixmap	bitmap;
Wob wob;
{
    XSetWindowBackgroundPixmap(dpy, wob -> hook, bitmap -> pixmap);
    XClearWindow(dpy,  wob -> hook);
    return NULL;		/* 0 means no exposure necessary */
}

/*
 * drawing a bitmap  (not needed ?)
 */

WOOL_OBJECT
WLPixmap_draw(bitmap, wob)
WOOL_Pixmap	bitmap;
Wob		wob;
{
    XClearWindow(dpy, wob -> hook);
    return NULL;
}

/*
 * Reading of a Pixmap file (xpm v3 format)
 */

WOOL_OBJECT
wool_xpm_pixmap_make(argc, argv)
int 	argc;
WOOL_String argv[];
{
    char           *name;
    WOOL_Pixmap     object;
    char            temp_filename[MAX_TEMP_STRING_SIZE];
    unsigned int    npixels_return;
    unsigned long   *pixels_return;
    WOOL_String	filename;
    XpmAttributes xpmatt;
    XpmColorSymbol     *colorsymbols = 0;
    int             ncolorsymbols = 0;
    Pixmap pixmapshape;
    int i;
    int res;

    if ((argc == 0) || (argc > 2 && (argc % 2 == 0)))
	wool_error(BAD_NUMBER_OF_ARGS, argc);
    filename = argv[0];
    must_be_string(filename, 0);
    
    if (argc > 1) {
	ncolorsymbols = (argc-1)/2;
	colorsymbols = (XpmColorSymbol *) Malloc(sizeof(XpmColorSymbol)
						 * ncolorsymbols);
	for (i=0; i<ncolorsymbols; i++) {
	    must_be_string(argv[i*2+1], i*2+1);
	    colorsymbols[i].name = argv[i*2+1] -> string;
	    if (argv[i*2+2]->type == WLNumber) {
		colorsymbols[i].value = 0;
		colorsymbols[i].pixel = ((WOOL_Number) argv[i*2+2])->number;
	    } else {
		must_be_string(argv[i*2+2], i*2+2);
		colorsymbols[i].value = argv[i*2+2] -> string;
	    }
	}
    }
    object = (WOOL_Pixmap) Malloc(sizeof(struct _WOOL_Pixmap));
    zrt_put(object);
    object -> type = WLPixmap;
    object -> x_hot = 0;
    object -> y_hot = 0;
    object -> mask = 0;
    name = (char *) file_in_path(filename -> string,
				 PIXMAP_EXTENSION, wool_path, temp_filename);
    if (!name) {
	return wool_default_pixmap_make(object,
					"GWM: Cannot find Pixmap %s", filename -> string);
    }

    xpmatt.valuemask = XpmVisual|XpmColormap|XpmDepth|XpmColorSymbols;
    xpmatt.visual = DefaultVisual(dpy, Context->screen);
    xpmatt.colormap = DefaultColormap(dpy, Context->screen);
    xpmatt.depth = Context->depth;
    xpmatt.colorsymbols = colorsymbols;
    xpmatt.numsymbols = ncolorsymbols;
    if (GWM_xpm_closeness) {
	xpmatt.closeness = GWM_xpm_closeness;
	xpmatt.valuemask |= XpmCloseness;
    }

    Trace('p', ("Reading pixmap file: %s\n", name));
    switch (
	res = XpmReadFileToPixmap(dpy, Context->root, name,
				      &object -> pixmap, &pixmapshape, &xpmatt),
	(colorsymbols ? (Free(colorsymbols), 0) : 0),
	res
	) {
	case PixmapSuccess:
	    break;
	case PixmapOpenFailed:
	    return wool_default_pixmap_make(object,
					    "Cannot open file %s", name);
	case PixmapFileInvalid:
	    return wool_default_pixmap_make(object,
					    "File %s is not a XPM pixmap", name);
	case PixmapNoMemory:
	    wool_error("No memory to load file %s", name);
	    break;
	default:
	    wool_error("Error while reading pixmap %s", name);
	    break;
    }
    if (pixmapshape)
	object->mask = pixmapshape;
    object->width = xpmatt.width;
    object->height = xpmatt.height;
    return (WOOL_OBJECT) object;
}

MakeDefaultBitmap()
{
    if (!(Context -> DefaultBitmap = XCreateBitmapFromData(dpy, Context->root,
		     def_bitmap_bits, def_bitmap_width, def_bitmap_height)))
	wool_error(NO_MEMORY, "");
}

/* Graphical primitives
 */

/* draws a line in a pixmap:
 * syntax (draw-line pixmap x1 y1 x2 y2)
 * uses "foreground" color
 */

WOOL_OBJECT
wool_draw_line(argc, argv)
int 	argc;
WOOL_Number argv[];
{
    int             i;

    if (argc != 5)
	wool_error(BAD_NUMBER_OF_ARGS, argc);
    must_be(WLPixmap, argv[0], 0);
    for (i = 1; i < 5; i++)
	must_be(WLNumber, argv[i], i);

    XSetForeground(dpy, Context->gc.Work, Context -> pixel.Fore);
    XDrawLine(dpy, ((WOOL_Pixmap) argv[0]) -> pixmap, Context->gc.Work,
	      argv[1] -> number, argv[2] -> number,
	      argv[3] -> number, argv[4] -> number);
    return (WOOL_OBJECT) argv[0];
}

/* draws a filled rectangle in a pixmap:
 * syntax (draw-rectangle pixmap x1 y1 width height border style)
 * uses "foreground" color for border
 * uses "background" color for rectangle
 * style: 0 = nothing, 1 = outline, 2 = filled, 3 = outline+filled 
 */

WOOL_OBJECT
wool_draw_rectangle(argc, argv)
int 	argc;
WOOL_Number argv[];
{
    int             i;

    if (argc != 7)
	wool_error(BAD_NUMBER_OF_ARGS, argc);
    must_be(WLPixmap, argv[0], 0);
    for (i = 1; i < 7; i++)
	must_be(WLNumber, argv[i], i);

    if (argv[6] -> number & 2) {	/* inside rectangle */
	XSetForeground(dpy, Context->gc.Work, Context -> pixel.Back);
	XFillRectangle(dpy, ((WOOL_Pixmap) argv[0]) -> pixmap,
		       Context->gc.Work,
		       argv[1] -> number, argv[2] -> number,
		       argv[3] -> number, argv[4] -> number);
    }
    if (argv[6] -> number & 1) {	/* ouline rectangle */
	int linewidth = argv[5] -> number ? argv[5] -> number : 1;
	XPoint points[5];

	points[0].x = argv[1] -> number - 1;
	points[0].y = argv[2] -> number - 1;
	points[1].x = points[0].x + argv[3] -> number + 1;
	points[1].y = points[0].y;
	points[2].x = points[1].x;
	points[2].y = points[0].y + argv[4] -> number + 1;
	points[3].x = points[0].x;
	points[3].y = points[2].y;
	points[4].x = points[0].x;
	points[4].y = points[0].y;

	XSetForeground(dpy, Context->gc.Work, Context -> pixel.Fore);
	XDrawLines(dpy, ((WOOL_Pixmap) argv[0]) -> pixmap,
		   Context->gc.Work, points, 5, CoordModeOrigin);
	for (i = 1; i < linewidth; i++) {
	    points[0].x -= 1; points[0].y -= 1;
	    points[1].x += 1; points[1].y -= 1;
	    points[2].x += 1; points[2].y += 1;
	    points[3].x -= 1; points[3].y += 1;
	    points[4].x -= 1; points[4].y -= 1;
	    XDrawLines(dpy, ((WOOL_Pixmap) argv[0]) -> pixmap,
		       Context->gc.Work, points, 5, CoordModeOrigin);
	}
    }
    return (WOOL_OBJECT) argv[0];
}

/* prints a text in a pixmap:
 * syntax (draw-text pixmap x1 y1 font text)
 * uses "foreground" color for text color
 */

WOOL_OBJECT
wool_draw_text(argc, argv)
int 	argc;
WOOL_Number argv[];
{
    int             i;

    if (argc != 5)
	wool_error(BAD_NUMBER_OF_ARGS, argc);
    must_be(WLPixmap, argv[0], 0);
    for (i = 1; i < 4; i++)
	must_be(WLNumber, argv[i], i);
    must_be_string(argv[4], 4);

    XSetForeground(dpy, Context->gc.Work, Context -> pixel.Fore);
    XSetFont(dpy, Context->gc.Work, argv[3]->number);
    XDrawString(dpy,  ((WOOL_Pixmap) argv[0]) -> pixmap, Context->gc.Work,
		argv[1] -> number, argv[2] -> number,
		((WOOL_String) argv[4]) -> string,
		strlen(((WOOL_String) argv[4]) -> string));
    return (WOOL_OBJECT) argv[0];
}
