/* $Id: ggi.c,v 1.21 2004/10/01 03:55:25 agraef Exp $ */
/* This file is part of the Q programming system.
The Q programming system is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
The Q programming system is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#if defined (HAVE_CONFIG_H)
# include "config.h"
#endif
#ifdef _WIN32
#define STDC_HEADERS 1
#define HAVE_STRDUP 1
#define HAVE_MEMCPY 1
#define HAVE_LIMITS_H 1
#include <windows.h>
#endif
/* system headers */
#include <stdio.h>
#include <ctype.h>
#include <math.h>
/* check for standard C headers */
#if STDC_HEADERS
# include <stdlib.h>
# include <string.h>
#else
# ifndef HAVE_STRCHR
# define strchr index
# define strrchr rindex
# endif
char *strchr (), *strrchr ();
#endif
#ifdef HAVE_MALLOC_H
#include <stdlib.h>
#endif
#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef DMALLOC
#include <dmalloc.h>
#endif
#if defined(HAVE_FT_INIT_FREETYPE) && !defined(HAVE_FT2)
#define HAVE_FT2 1
#endif
#include <libq.h>
#include <ggi/ggi.h>
#include <ggi/gii.h>
#ifdef HAVE_FT2
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_GLYPH_H
#if FREETYPE_MAJOR != 2
#error "This module needs FreeType2, sorry."
#endif
#if FREETYPE_MINOR < 1
#define FT_KERNING_DEFAULT ft_kerning_default
#define FT_RENDER_MODE_NORMAL ft_render_mode_normal
#define FT_RENDER_MODE_MONO ft_render_mode_mono
#endif
#endif
#include "ggilib.h"
MODULE(ggi)
static int init = 0, ft2init = 0;
#ifdef HAVE_FT2
static FT_Library library;
#endif
INIT(ggi)
{
init = ggiInit() == 0;
if (init) giiMTInit();
#ifdef HAVE_FT2
if (init && !FT_Init_FreeType(&library))
ft2init = 1;
#endif
}
FINI(ggi)
{
if (init) {
ggiExit();
#ifdef HAVE_FT2
if (ft2init)
FT_Done_FreeType(library);
#endif
}
}
/* ByteStr data structure, see clib.c */
typedef struct bstr {
long size;
unsigned char *v;
} bstr_t;
#ifndef HAVE_FT2
#define FT_FACE_FLAG_SCALABLE 0
#define FT_FACE_FLAG_FIXED_SIZES 0
#define FT_FACE_FLAG_FIXED_WIDTH 0
#define FT_FACE_FLAG_SFNT 0
#define FT_FACE_FLAG_HORIZONTAL 0
#define FT_FACE_FLAG_VERTICAL 0
#define FT_FACE_FLAG_KERNING 0
#define FT_FACE_FLAG_FAST_GLYPHS 0
#define FT_FACE_FLAG_MULTIPLE_MASTERS 0
#define FT_FACE_FLAG_GLYPH_NAMES 0
#define FT_FACE_FLAG_EXTERNAL_STREAM 0
#define FT_STYLE_FLAG_ITALIC 0
#define FT_STYLE_FLAG_BOLD 0
#endif
FUNCTION(ggi,ggi_vars,argc,argv)
{
if (argc != 0) return __FAIL;
return mktuplel
(94,
mkuint(GGIFLAG_ASYNC),
/* events */
mkuint(evNothing), mkuint(evCommand), mkuint(evInformation),
mkuint(evExpose), mkuint(evKeyPress), mkuint(evKeyRelease),
mkuint(evKeyRepeat),
mkuint(evPtrRelative), mkuint(evPtrAbsolute), mkuint(evPtrButtonPress),
mkuint(evPtrButtonRelease), mkuint(evValRelative), mkuint(evValAbsolute),
/* event masks */
mkuint(emCommand), mkuint(emInformation), mkuint(emExpose),
mkuint(emKeyPress), mkuint(emKeyRelease), mkuint(emKeyRepeat),
mkuint(emKey),
mkuint(emPtrRelative), mkuint(emPtrAbsolute), mkuint(emPtrButtonPress),
mkuint(emPtrButtonRelease), mkuint(emPtrMove), mkuint(emPtrButton),
mkuint(emPointer),
mkuint(emValRelative), mkuint(emValAbsolute), mkuint(emValuator),
mkuint(emAll), mkuint(emNothing),
/* special event origins and targets */
mkuint(GII_EV_ORIGIN_NONE), mkuint(GII_EV_ORIGIN_SENDEVENT),
mkuint(GII_EV_TARGET_QUEUE), mkuint(GII_EV_TARGET_ALL),
/* modifiers */
mkuint(GII_MOD_SHIFT), mkuint(GII_MOD_CTRL), mkuint(GII_MOD_ALT),
mkuint(GII_MOD_META),
mkuint(GII_MOD_SUPER), mkuint(GII_MOD_HYPER), mkuint(GII_MOD_ALTGR),
mkuint(GII_MOD_CAPS), mkuint(GII_MOD_NUM),
mkuint(GII_MOD_SCROLL),
/* mouse buttons */
mkuint(GII_PBUTTON_LEFT), mkuint(GII_PBUTTON_MIDDLE),
mkuint(GII_PBUTTON_RIGHT),
/* command codes and flags */
mkuint(GII_CMDCODE_EVENTLOST), mkuint(GII_CMDCODE_PREFER_ABSPTR),
mkuint(GII_CMDCODE_PREFER_RELPTR), mkuint(GII_CMDCODE_GETDEVINFO),
mkuint(GII_CMDCODE_GETVALINFO),
mkuint(GII_CMDFLAG_NODATA), mkuint(GII_CMDFLAG_PRIVATE),
mkuint(GII_CMDFLAG_EXTERNAL),
/* valuator types */
mkuint(GII_PT_UNKNOWN), mkuint(GII_PT_TIME), mkuint(GII_PT_FREQUENCY),
mkuint(GII_PT_LENGTH),
mkuint(GII_PT_VELOCITY), mkuint(GII_PT_ACCELERATION),
mkuint(GII_PT_ANGLE), mkuint(GII_PT_ANGVELOCITY),
mkuint(GII_PT_ANGACCELERATION), mkuint(GII_PT_AREA),
mkuint(GII_PT_VOLUME), mkuint(GII_PT_MASS),
mkuint(GII_PT_FORCE), mkuint(GII_PT_PRESSURE), mkuint(GII_PT_TORQUE),
mkuint(GII_PT_ENERGY),
mkuint(GII_PT_POWER), mkuint(GII_PT_TEMPERATURE), mkuint(GII_PT_CURRENT),
mkuint(GII_PT_VOLTAGE),
mkuint(GII_PT_RESISTANCE), mkuint(GII_PT_CAPACITY),
mkuint(GII_PT_INDUCTIVITY),
/* font properties */
mkuint(FT_FACE_FLAG_SCALABLE),
mkuint(FT_FACE_FLAG_FIXED_SIZES),
mkuint(FT_FACE_FLAG_FIXED_WIDTH),
mkuint(FT_FACE_FLAG_SFNT),
mkuint(FT_FACE_FLAG_HORIZONTAL),
mkuint(FT_FACE_FLAG_VERTICAL),
mkuint(FT_FACE_FLAG_KERNING),
mkuint(FT_FACE_FLAG_FAST_GLYPHS),
mkuint(FT_FACE_FLAG_MULTIPLE_MASTERS),
mkuint(FT_FACE_FLAG_GLYPH_NAMES),
mkuint(FT_FACE_FLAG_EXTERNAL_STREAM),
mkuint(FT_STYLE_FLAG_ITALIC),
mkuint(FT_STYLE_FLAG_BOLD));
}
/* visual type */
typedef struct {
/* public fields; make sure these come first, some external modules depend
on it! */
ggi_visual_t vis;
void *abuf;
unsigned char asz;
unsigned short afg, abg;
int stride, lines;
/* private stuff */
#ifdef HAVE_FT2
FT_Face face;
FT_Matrix *matrix;
FT_Vector *vect;
unsigned char aa;
#endif
} visual_t;
/* check whether graphic mode has been set on a visual */
static inline int vis_check(ggi_visual_t vis)
{
int w, h;
return !ggiGetCharSize(vis, &w, &h);
}
/* FIXME: Here we assume that ggi_color already has the proper storage
layout. */
/* parse pixel value to color */
static inline int iscolor(expr x, ggi_color **c)
{
bstr_t *m;
if (isobj(x, type(ByteStr), (void**)&m) && m->size == sizeof(ggi_color)) {
*c = (ggi_color*)m->v;
return 1;
} else
return 0;
}
/* parse pixel byte string to color sequence */
static inline int iscolors(expr x, ggi_color **c, int *n)
{
bstr_t *m;
if (isobj(x, type(ByteStr), (void**)&m) && m->size%8 == 0) {
*c = (ggi_color*)m->v;
*n = m->size/sizeof(ggi_color);
return 1;
} else
return 0;
}
/* create pixel value from color */
static inline expr mkcolor(ggi_color *c)
{
bstr_t *m;
ggi_color *v;
if (!(m = malloc(sizeof(bstr_t)))) return __ERROR;
if (!(m->v = malloc(sizeof(ggi_color)))) { free(m); return __ERROR; }
v = (ggi_color*)m->v;
*v = *c;
m->size = sizeof(ggi_color);
return mkobj(type(ByteStr), m);
}
/* create a pixel byte string from a sequence of color values (reuse the color
buffer for efficiency) */
static inline expr *mkcolors(ggi_color *c, int n)
{
bstr_t *m;
if (!(m = malloc(sizeof(bstr_t)))) return __ERROR;
m->size = n*sizeof(ggi_color);
m->v = (char*)c;
return mkobj(type(ByteStr), m);
}
/* parse pixel value to pixel and alpha */
static inline int ispixel(expr x, ggi_visual_t vis, ggi_pixel *pixel,
unsigned short *alpha)
{
ggi_color *c;
if (vis_check(vis) && iscolor(x, &c)) {
*pixel = ggiMapColor(vis, c);
*alpha = c->a;
return 1;
} else
return 0;
}
/* create pixel value from pixel and alpha */
static inline expr mkpixel(ggi_visual_t vis, ggi_pixel pixel,
unsigned short alpha)
{
ggi_color c;
if (ggiUnmapPixel(vis, pixel, &c)) return __FAIL;
c.a = alpha;
return mkcolor(&c);
}
/* calculate the size of a pixel pack */
static inline int pack_size(ggi_visual_t vis, int n)
{
int bpp, r;
ggi_mode m;
if (ggiGetMode(vis, &m))
return -1;
bpp = GT_SIZE(m.graphtype);
if (n > INT_MAX / bpp)
return -1;
r = ((bpp*n)%8>0)?1:0;
return (bpp*n)/8+r;
}
DESTRUCTOR(ggi,GGIVisual,ptr)
{
visual_t *v = (visual_t*)ptr;
if (v->vis) {
if (v->abuf) free(v->abuf);
#ifdef HAVE_FT2
if (v->face) FT_Done_Face(v->face);
if (v->matrix) free(v->matrix);
if (v->vect) free(v->vect);
#endif
ggiClose(v->vis);
}
free(v);
}
FUNCTION(ggi,ggi_open,argc,argv)
{
char *dpy = NULL;
if (init && argc == 1 && (isstr(argv[0], &dpy) || isvoid(argv[0]))) {
visual_t *v = malloc(sizeof(visual_t));
if (!v)
return __ERROR;
else if ((v->vis = ggiOpen(dpy, NULL))) {
v->abuf = NULL; v->asz = 0; v->afg = v->abg = 0;
v->stride = v->lines = 0;
#ifdef HAVE_FT2
v->face = NULL;
v->matrix = NULL;
v->vect = NULL;
v->aa = 1;
#endif
return mkobj(type(GGIVisual), v);
} else {
free(v);
return __FAIL;
}
} else
return __FAIL;
}
FUNCTION(ggi,ggi_close,argc,argv)
{
visual_t *v;
if (init && argc == 1 && isobj(argv[0], type(GGIVisual), (void**)&v) &&
v->vis) {
if (v->abuf) free(v->abuf);
#ifdef HAVE_FT2
if (v->face) FT_Done_Face(v->face);
if (v->matrix) free(v->matrix);
if (v->vect) free(v->vect);
v->face = NULL;
v->matrix = NULL;
v->vect = NULL;
v->aa = 1;
#endif
ggiClose(v->vis);
v->abuf = NULL; v->asz = 0; v->afg = v->abg = 0;
v->stride = v->lines = 0;
v->vis = NULL;
return mkvoid;
} else
return __FAIL;
}
static int parse_mode(char *mode, ggi_mode *m,
unsigned *adepth)
{
char buf[1000], *p, *q, *r = NULL;
*adepth = 0;
strcpy(buf, mode);
p = strtok(buf, ".");
while (p) {
if (r > buf) r[-1] = '.';
if (*p != 'A') {
r = p;
p = strtok(NULL, ".");
} else {
if (*p == 'A') {
long val = strtol(p+1, &q, 10);
if (!*q && val > 0 && val <= 32)
*adepth = (unsigned)val;
else
return -1;
p = strtok(NULL, ".");
}
if (p) return -1;
}
}
if (!r)
*buf = 0;
else if (r > buf)
r[-1] = '.';
return ggiParseMode(buf, m);
}
static int print_mode(char *buf, ggi_mode *m,
unsigned adepth)
{
int ret = ggiSPrintMode(buf, m);
if (ret >= 0) {
int l = strlen(buf);
if (adepth)
sprintf(buf+l, "%sA%u", l?".":"", adepth);
}
return ret;
}
FUNCTION(ggi,ggi_check_mode,argc,argv)
{
visual_t *v;
char *mode = "";
ggi_mode m;
unsigned adepth;
if (init && argc == 2 && isobj(argv[0], type(GGIVisual), (void**)&v) &&
v->vis &&
(isstr(argv[1], &mode) || isvoid(argv[1])) &&
!parse_mode(mode, &m, &adepth)) {
char buf[1000];
ggiCheckMode(v->vis, &m);
if (adepth != 0 && adepth != 8 && adepth != 16) {
adepth = (adepth+7)/8*8;
if (adepth > 16) adepth = 16;
}
if (print_mode(buf, &m, adepth) >= 0)
return mkstr(strdup(buf));
else
return __FAIL;
} else
return __FAIL;
}
FUNCTION(ggi,ggi_set_mode,argc,argv)
{
visual_t *v;
char *mode = "";
ggi_mode m;
unsigned adepth;
int ret;
if (init && argc == 2 && isobj(argv[0], type(GGIVisual), (void**)&v) &&
v->vis &&
(isstr(argv[1], &mode) || isvoid(argv[1])) &&
!parse_mode(mode, &m, &adepth) &&
(adepth == 0 || adepth == 8 || adepth == 16)) {
if (ggiSetMode(v->vis, &m))
return __FAIL;
else {
int size;
void *abuf = NULL;
ggiGetMode(v->vis, &m);
size = m.virt.x*m.virt.y*(adepth/8);
if (size > 0)
abuf = malloc(size);
if (size < 0 || size > 0 && !abuf)
return __ERROR;
if (v->abuf) free(v->abuf);
v->asz = adepth/8;
v->abuf = abuf;
v->afg = v->abg = (adepth==0)?0:(adepth==8)?0xff:0xffff;
if (v->abuf) memset(v->abuf, 0xff, size);
v->stride = m.virt.x;
v->lines = m.virt.y;
return mkvoid;
}
} else
return __FAIL;
}
FUNCTION(ggi,ggi_get_mode,argc,argv)
{
visual_t *v;
if (init && argc == 1 && isobj(argv[0], type(GGIVisual), (void**)&v) &&
v->vis) {
ggi_mode m;
char buf[1000];
if (ggiGetMode(v->vis, &m))
return __FAIL;
else if (print_mode(buf, &m, v->asz*8) >= 0)
return mkstr(strdup(buf));
else
return __FAIL;
} else
return __FAIL;
}
FUNCTION(ggi,ggi_set_flags,argc,argv)
{
visual_t *v;
unsigned long flags;
if (init && argc == 2 && isobj(argv[0], type(GGIVisual), (void**)&v) &&
v->vis && isuint(argv[1], &flags)) {
if (ggiSetFlags(v->vis, (ggi_flags)flags))
return __FAIL;
else
return mkvoid;
} else
return __FAIL;
}
FUNCTION(ggi,ggi_get_flags,argc,argv)
{
visual_t *v;
if (init && argc == 1 && isobj(argv[0], type(GGIVisual), (void**)&v) &&
v->vis)
return mkuint((unsigned long)ggiGetFlags(v->vis));
else
return __FAIL;
}
FUNCTION(ggi,ggi_set_display_frame,argc,argv)
{
visual_t *v;
long frame;
if (init && argc == 2 && isobj(argv[0], type(GGIVisual), (void**)&v) &&
v->vis && isint(argv[1], &frame)) {
if (ggiSetDisplayFrame(v->vis, (int)frame))
return __FAIL;
else
return mkvoid;
} else
return __FAIL;
}
FUNCTION(ggi,ggi_get_display_frame,argc,argv)
{
visual_t *v;
if (init && argc == 1 && isobj(argv[0], type(GGIVisual), (void**)&v) &&
v->vis)
return mkint((long)ggiGetDisplayFrame(v->vis));
else
return __FAIL;
}
FUNCTION(ggi,ggi_set_read_frame,argc,argv)
{
visual_t *v;
long frame;
if (init && argc == 2 && isobj(argv[0], type(GGIVisual), (void**)&v) &&
v->vis && isint(argv[1], &frame)) {
if (ggiSetReadFrame(v->vis, (int)frame))
return __FAIL;
else
return mkvoid;
} else
return __FAIL;
}
FUNCTION(ggi,ggi_get_read_frame,argc,argv)
{
visual_t *v;
if (init && argc == 1 && isobj(argv[0], type(GGIVisual), (void**)&v) &&
v->vis)
return mkint((long)ggiGetReadFrame(v->vis));
else
return __FAIL;
}
FUNCTION(ggi,ggi_set_write_frame,argc,argv)
{
visual_t *v;
long frame;
if (init && argc == 2 && isobj(argv[0], type(GGIVisual), (void**)&v) &&
v->vis && isint(argv[1], &frame)) {
if (ggiSetWriteFrame(v->vis, (int)frame))
return __FAIL;
else
return mkvoid;
} else
return __FAIL;
}
FUNCTION(ggi,ggi_get_write_frame,argc,argv)
{
visual_t *v;
if (init && argc == 1 && isobj(argv[0], type(GGIVisual), (void**)&v) &&
v->vis)
return mkint((long)ggiGetWriteFrame(v->vis));
else
return __FAIL;
}
FUNCTION(ggi,ggi_set_clip,argc,argv)
{
visual_t *v;
expr *xv;
int n;
long x1, y1, x2, y2;
if (init && argc == 2 && isobj(argv[0], type(GGIVisual), (void**)&v) &&
v->vis && istuple(argv[1], &n, &xv) && n == 4 &&
isint(xv[0], &x1) && isint(xv[1], &y1) &&
isint(xv[2], &x2) && isint(xv[3], &y2)) {
if (ggiSetGCClipping(v->vis, (int)x1, (int)y1, (int)x2, (int)y2))
return __FAIL;
else
return mkvoid;
} else
return __FAIL;
}
FUNCTION(ggi,ggi_get_clip,argc,argv)
{
visual_t *v;
int x1, y1, x2, y2;
if (init && argc == 1 && isobj(argv[0], type(GGIVisual), (void**)&v) &&
v->vis && !ggiGetGCClipping(v->vis, &x1, &y1, &x2, &y2))
return mktuplel(4, mkint((long)x1), mkint((long)y1),
mkint((long)x2), mkint((long)y2));
else
return __FAIL;
}
FUNCTION(ggi,ggi_set_orig,argc,argv)
{
visual_t *v;
expr *xv;
int n;
long x, y;
if (init && argc == 2 && isobj(argv[0], type(GGIVisual), (void**)&v) &&
v->vis && istuple(argv[1], &n, &xv) && n == 2 &&
isint(xv[0], &x) && isint(xv[1], &y)) {
if (ggiSetOrigin(v->vis, (int)x, (int)y))
return __FAIL;
else
return mkvoid;
} else
return __FAIL;
}
FUNCTION(ggi,ggi_get_orig,argc,argv)
{
visual_t *v;
int x, y;
if (init && argc == 1 && isobj(argv[0], type(GGIVisual), (void**)&v) &&
v->vis && !ggiGetOrigin(v->vis, &x, &y))
return mktuplel(2, mkint((long)x), mkint((long)y));
else
return __FAIL;
}
FUNCTION(ggi,ggi_set_foreground,argc,argv)
{
visual_t *v;
ggi_pixel pixel;
unsigned short alpha;
if (init && argc == 2 && isobj(argv[0], type(GGIVisual), (void**)&v) &&
v->vis && ispixel(argv[1], v->vis, &pixel, &alpha)) {
if (ggiSetGCForeground(v->vis, pixel))
return __FAIL;
if (v->abuf)
v->afg = (v->asz == 1)?(alpha/0x101):alpha;
return mkvoid;
} else
return __FAIL;
}
FUNCTION(ggi,ggi_get_foreground,argc,argv)
{
visual_t *v;
ggi_pixel pixel;
unsigned short alpha = 0xffff;
if (init && argc == 1 && isobj(argv[0], type(GGIVisual), (void**)&v) &&
v->vis && !ggiGetGCForeground(v->vis, &pixel)) {
if (v->abuf)
alpha = (v->asz == 1)?(v->afg*0x101):v->afg;
return mkpixel(v->vis, pixel, alpha);
} else
return __FAIL;
}
FUNCTION(ggi,ggi_set_background,argc,argv)
{
visual_t *v;
ggi_pixel pixel;
unsigned short alpha;
if (init && argc == 2 && isobj(argv[0], type(GGIVisual), (void**)&v) &&
v->vis && ispixel(argv[1], v->vis, &pixel, &alpha)) {
if (ggiSetGCBackground(v->vis, pixel))
return __FAIL;
if (v->abuf)
v->abg = (v->asz == 1)?(alpha/0x101):alpha;
return mkvoid;
} else
return __FAIL;
}
FUNCTION(ggi,ggi_get_background,argc,argv)
{
visual_t *v;
ggi_pixel pixel;
unsigned short alpha = 0xffff;
if (init && argc == 1 && isobj(argv[0], type(GGIVisual), (void**)&v) &&
v->vis && !ggiGetGCBackground(v->vis, &pixel)) {
if (v->abuf)
alpha = (v->asz == 1)?(v->abg*0x101):v->abg;
return mkpixel(v->vis, pixel, alpha);
} else
return __FAIL;
}
FUNCTION(ggi,ggi_pixel,argc,argv)
{
unsigned long r, g, b, a = 0xffff;
expr *xv;
int n;
if (init && argc == 1)
if (istuple(argv[0], &n, &xv) && n >= 3 && n <= 4 &&
isuint(xv[0], &r) && isuint(xv[1], &g) && isuint(xv[2], &b) &&
(n == 3 || isuint(xv[3], &a)) &&
r <= 0xffff && g <= 0xffff && b <= 0xffff && a <= 0xffff) {
ggi_color c;
c.r = (unsigned)r; c.g = (unsigned)g; c.b = (unsigned)b;
c.a = (unsigned)a;
return mkcolor(&c);
} else {
/* if we come here then the argument is in list form */
ggi_color *c;
expr x, hd, tl;
int i;
for (i = 0, x = argv[0]; iscons(x, &hd, &tl); x = tl) {
a = 0xffff;
if (i >= INT_MAX)
return __ERROR;
else if (istuple(hd, &n, &xv) && n >= 3 && n <= 4 &&
isuint(xv[0], &r) && isuint(xv[1], &g) && isuint(xv[2], &b)
&& (n == 3 || isuint(xv[3], &a)) &&
r <= 0xffff && g <= 0xffff && b <= 0xffff && a <= 0xffff)
i++;
else
return __FAIL;
}
if (!isnil(x)) return __FAIL;
if (i == 0)
c = NULL;
else {
if (!(c = malloc(i*sizeof(ggi_color)))) return __ERROR;
for (i = 0, x = argv[0]; iscons(x, &hd, &tl); x = tl) {
a = 0xffff;
istuple(hd, &n, &xv) &&
isuint(xv[0], &r) && isuint(xv[1], &g) && isuint(xv[2], &b) &&
(n == 3 || isuint(xv[3], &a));
c[i].r = r; c[i].g = g; c[i].b = b; c[i].a = a;
i++;
}
}
return mkcolors(c, i);
}
else
return __FAIL;
}
FUNCTION(ggi,ggi_color,argc,argv)
{
bstr_t m;
ggi_color *c;
int i, n;
if (init && argc == 1)
if (iscolor(argv[0], &c))
return mktuplel(4, mkuint((unsigned long)c->r),
mkuint((unsigned long)c->g),
mkuint((unsigned long)c->b),
mkuint((unsigned long)c->a));
else if (iscolors(argv[0], &c, &n)) {
expr x = mknil;
for (i = n-1; x && i >= 0; i--)
x = mkcons(mktuplel(4, mkuint((unsigned long)c[i].r),
mkuint((unsigned long)c[i].g),
mkuint((unsigned long)c[i].b),
mkuint((unsigned long)c[i].a)),
x);
return x;
}
else
return __FAIL;
}
FUNCTION(ggi,ggi_set_default_font,argc,argv)
{
visual_t *v;
if (init && argc == 1 && isobj(argv[0], type(GGIVisual), (void**)&v) &&
v->vis) {
#ifdef HAVE_FT2
if (v->face) FT_Done_Face(v->face);
v->face = NULL;
#endif
return mkvoid;
} else
return __FAIL;
}
FUNCTION(ggi,ggi_set_font,argc,argv)
{
#ifdef HAVE_FT2
visual_t *v;
char *name;
long index;
if (init && ft2init && argc == 3 &&
isobj(argv[0], type(GGIVisual), (void**)&v) &&
v->vis && isstr(argv[1], &name) && isint(argv[2], &index)) {
FT_Face face;
if (FT_New_Face(library, name, index, &face))
return __FAIL;
if (v->face) FT_Done_Face(v->face);
if (v->matrix) free(v->matrix);
if (v->vect) free(v->vect);
v->face = face;
v->matrix = NULL;
v->vect = NULL;
v->aa = 1;
return mkvoid;
} else
#endif
return __FAIL;
}
FUNCTION(ggi,ggi_set_font_metrics,argc,argv)
{
#ifdef HAVE_FT2
visual_t *v;
char *name;
if (init && argc == 2 && isobj(argv[0], type(GGIVisual), (void**)&v) &&
v->vis && v->face && isstr(argv[1], &name) &&
!FT_Attach_File(v->face, name))
return mkvoid;
else
#endif
return __FAIL;
}
#ifdef HAVE_FT2
static int iscoord(expr x, FT_F26Dot6 *size)
{
long ival;
double fval;
/* parse a 26.6 pixel coordinate */
if (isint(x, &ival)) {
*size = ival*0x40;
return 1;
} else if (isfloat(x, &fval)) {
ival = (int)(fval*64.0);
*size = ival;
return 1;
} else
return 0;
}
#endif
FUNCTION(ggi,ggi_set_char_size,argc,argv)
{
#ifdef HAVE_FT2
visual_t *v;
FT_F26Dot6 width, height;
unsigned long horiz, vert;
expr *xv;
int n;
if (init && argc == 3 && isobj(argv[0], type(GGIVisual), (void**)&v) &&
v->vis && v->face &&
(istuple(argv[1], &n, &xv) && n == 2 &&
iscoord(xv[0], &width) && iscoord(xv[0], &height) ||
iscoord(argv[1], &width) && (height = width, 1)) &&
(istuple(argv[2], &n, &xv) && n == 2 &&
isuint(xv[0], &horiz) && isuint(xv[0], &vert) ||
isuint(argv[2], &horiz) && (vert = horiz, 1)) &&
!FT_Set_Char_Size(v->face, width, height, horiz, vert))
return mkvoid;
else
#endif
return __FAIL;
}
FUNCTION(ggi,ggi_set_antialias,argc,argv)
{
#ifdef HAVE_FT2
visual_t *v;
if (init && argc == 2 && isobj(argv[0], type(GGIVisual), (void**)&v) &&
v->vis && v->face) {
if (istrue(argv[1]))
v->aa = 1;
else if (isfalse(argv[1]))
v->aa = 0;
else
return __FAIL;
return mkvoid;
} else
#endif
return __FAIL;
}
FUNCTION(ggi,ggi_get_antialias,argc,argv)
{
#ifdef HAVE_FT2
visual_t *v;
if (init && argc == 1 && isobj(argv[0], type(GGIVisual), (void**)&v) &&
v->vis && v->face) {
if (v->aa)
return mktrue;
else
return mkfalse;
} else
#endif
return __FAIL;
}
FUNCTION(ggi,ggi_set_transform,argc,argv)
{
#ifdef HAVE_FT2
visual_t *v;
double x, y, xx, xy, yx, yy;
expr *xv;
int n;
if (init && argc == 3 && isobj(argv[0], type(GGIVisual), (void**)&v) &&
v->vis && v->face &&
istuple(argv[1], &n, &xv) && n == 2 &&
(isfloat(xv[0], &x) || ismpz_float(xv[0], &x)) &&
(isfloat(xv[1], &y) || ismpz_float(xv[1], &y)) &&
istuple(argv[2], &n, &xv) && n == 4 &&
(isfloat(xv[0], &xx) || ismpz_float(xv[0], &xx)) &&
(isfloat(xv[1], &xy) || ismpz_float(xv[1], &xy)) &&
(isfloat(xv[2], &yx) || ismpz_float(xv[2], &yx)) &&
(isfloat(xv[3], &yy) || ismpz_float(xv[3], &yy))) {
if (x == 0.0 && y == 0.0 &&
xx == 1.0 && xy == 0.0 && yx == 0.0 && yy == 1.0) {
/* identity transform: remove any existing transform from the visual */
if (v->vect) free(v->vect);
if (v->matrix) free(v->matrix);
v->vect = NULL;
v->matrix = NULL;
} else {
FT_Matrix *matrix = malloc(sizeof(FT_Matrix));
FT_Vector *vect = malloc(sizeof(FT_Vector));
if (!matrix || !vect) {
if (matrix) free(matrix);
if (vect) free(vect);
return __ERROR;
}
vect->x = (FT_Pos)(x*0x40);
vect->y = (FT_Pos)(y*0x40);
matrix->xx = (FT_Fixed)(xx*0x10000L);
matrix->xy = (FT_Fixed)(xy*0x10000L);
matrix->yx = (FT_Fixed)(yx*0x10000L);
matrix->yy = (FT_Fixed)(yy*0x10000L);
if (v->vect) free(v->vect);
if (v->matrix) free(v->matrix);
v->vect = vect;
v->matrix = matrix;
}
return mkvoid;
} else
#endif
return __FAIL;
}
FUNCTION(ggi,ggi_get_transform_vector,argc,argv)
{
#ifdef HAVE_FT2
visual_t *v;
if (init && argc == 1 && isobj(argv[0], type(GGIVisual), (void**)&v) &&
v->vis && v->face) {
if (v->vect)
return mktuplel(2, mkfloat(((double)v->vect->x)/0x40),
mkfloat(((double)v->vect->y)/0x40));
else
return mktuplel(2, mkfloat(0.0), mkfloat(0.0));
} else
#endif
return __FAIL;
}
FUNCTION(ggi,ggi_get_transform_matrix,argc,argv)
{
#ifdef HAVE_FT2
visual_t *v;
if (init && argc == 1 && isobj(argv[0], type(GGIVisual), (void**)&v) &&
v->vis && v->face) {
if (v->matrix)
return mktuplel(4, mkfloat(((double)v->matrix->xx)/0x10000L),
mkfloat(((double)v->matrix->xy)/0x10000L),
mkfloat(((double)v->matrix->yx)/0x10000L),
mkfloat(((double)v->matrix->yy)/0x10000L));
else
return mktuplel(4, mkfloat(1.0), mkfloat(0.0), mkfloat(0.0),
mkfloat(1.0));
} else
#endif
return __FAIL;
}
FUNCTION(ggi,ggi_get_font_info,argc,argv)
{
#ifdef HAVE_FT2
visual_t *v;
if (init && argc == 1 && isobj(argv[0], type(GGIVisual), (void**)&v) &&
v->vis && v->face) {
expr *sizes = NULL;
int i, n = v->face->num_fixed_sizes;
if (n > 0 && !(sizes = malloc(n*sizeof(expr)))) return __ERROR;
for (i = 0; i < n; i++)
sizes[i] = mktuplel(2, mkint(v->face->available_sizes[i].width),
mkint(v->face->available_sizes[i].height));
if (FT_IS_SCALABLE(v->face))
return mktuplel(18, mkint(v->face->num_faces),
mkint(v->face->face_index),
mkuint(v->face->face_flags),
mkuint(v->face->style_flags),
mkstr(strdup(v->face->family_name)),
mkstr(strdup(v->face->style_name)),
mklistv(n, sizes),
mkuint(v->face->units_per_EM),
mkuint(v->face->size->metrics.x_ppem),
mkuint(v->face->size->metrics.y_ppem),
mktuplel(4, mkint(v->face->bbox.xMin),
mkint(v->face->bbox.xMax),
mkint(v->face->bbox.yMin),
mkint(v->face->bbox.yMax)),
mkint(v->face->ascender),
mkint(v->face->descender),
mkint(v->face->height),
mkint(v->face->max_advance_width),
mkint(v->face->max_advance_height),
mkint(v->face->underline_position),
mkint(v->face->underline_thickness));
else
return mktuplel(7, mkint(v->face->num_faces),
mkint(v->face->face_index),
mkuint(v->face->face_flags),
mkuint(v->face->style_flags),
mkstr(strdup(v->face->family_name)),
mkstr(strdup(v->face->style_name)),
mklistv(n, sizes));
} else
#endif
return __FAIL;
}
FUNCTION(ggi,ggi_clear,argc,argv)
{
visual_t *v;
ggi_pixel back, fore;
if (init && argc == 1 && isobj(argv[0], type(GGIVisual), (void**)&v) &&
v->vis && !ggiGetGCBackground(v->vis, &back) &&
!ggiGetGCForeground(v->vis, &fore)) {
int ret;
ggiSetGCForeground(v->vis, back);
ret = !ggiFillscreen(v->vis) && (!v->abuf || !clear_alpha_buffer(v));
ggiSetGCForeground(v->vis, fore);
if (ret)
return mkvoid;
else
return __FAIL;
} else
return __FAIL;
}
FUNCTION(ggi,ggi_clear_alpha,argc,argv)
{
visual_t *v;
if (init && argc == 1 && isobj(argv[0], type(GGIVisual), (void**)&v) &&
v->vis && v->abuf && !clear_alpha_buffer(v))
return mkvoid;
else
return __FAIL;
}
FUNCTION(ggi,ggi_draw_pixel,argc,argv)
{
visual_t *v;
expr *xv;
int n;
long x, y;
if (init && argc == 2 && isobj(argv[0], type(GGIVisual), (void**)&v) &&
v->vis && istuple(argv[1], &n, &xv) && n == 2 &&
isint(xv[0], &x) && isint(xv[1], &y)) {
if ((n = draw_box(v, x, y, 1, 1)) < 0)
return __ERROR;
else if (n == 0)
return __FAIL;
else
return mkvoid;
} else
return __FAIL;
}
FUNCTION(ggi,ggi_draw_hline,argc,argv)
{
visual_t *v;
expr *xv;
int n;
long x, y, w;
if (init && argc == 3 && isobj(argv[0], type(GGIVisual), (void**)&v) &&
v->vis && istuple(argv[1], &n, &xv) && n == 2 &&
isint(xv[0], &x) && isint(xv[1], &y) &&
isint(argv[2], &w)) {
if ((n = draw_box(v, x, y, w, 1)) < 0)
return __ERROR;
else if (n == 0)
return __FAIL;
else
return mkvoid;
} else
return __FAIL;
}
FUNCTION(ggi,ggi_draw_vline,argc,argv)
{
visual_t *v;
expr *xv;
int n;
long x, y, h;
if (init && argc == 3 && isobj(argv[0], type(GGIVisual), (void**)&v) &&
v->vis && istuple(argv[1], &n, &xv) && n == 2 &&
isint(xv[0], &x) && isint(xv[1], &y) &&
isint(argv[2], &h)) {
if ((n = draw_box(v, x, y, 1, h)) < 0)
return __ERROR;
else if (n == 0)
return __FAIL;
else
return mkvoid;
} else
return __FAIL;
}
FUNCTION(ggi,ggi_draw_line,argc,argv)
{
visual_t *v;
expr *xv;
int n;
long x1, y1, x2, y2;
if (init && argc == 3 && isobj(argv[0], type(GGIVisual), (void**)&v) &&
v->vis && istuple(argv[1], &n, &xv) && n == 2 &&
isint(xv[0], &x1) && isint(xv[1], &y1) &&
istuple(argv[2], &n, &xv) && n == 2 &&
isint(xv[0], &x2) && isint(xv[1], &y2) &&
!draw_line(v, (int)x1, (int)y1, (int)x2, (int)y2))
return mkvoid;
else
return __FAIL;
}
FUNCTION(ggi,ggi_draw_box,argc,argv)
{
visual_t *v;
expr *xv;
int n;
long x, y, w, h;
if (init && argc == 3 && isobj(argv[0], type(GGIVisual), (void**)&v) &&
v->vis && istuple(argv[1], &n, &xv) && n == 2 &&
isint(xv[0], &x) && isint(xv[1], &y) &&
istuple(argv[2], &n, &xv) && n == 2 &&
isint(xv[0], &w) && isint(xv[1], &h)) {
if ((n = draw_box(v, x, y, w, h)) < 0)
return __ERROR;
else if (n == 0)
return __FAIL;
else
return mkvoid;
} else
return __FAIL;
}
#ifdef HAVE_FT2
/* render characters using FreeType; this stuff is mostly pilfered straight
from the FT2 Turtorial, Part II */
/* struct used to keep glyph info */
typedef struct TGlyph_ {
FT_UInt index; /* glyph index */
FT_Vector pos; /* glyph origin on the baseline */
FT_Glyph image; /* glyph image */
} TGlyph, *PGlyph;
/* the renderer */
static int render(visual_t *v, char *s, int x, int y)
{
ggi_visual_t vis = v->vis;
FT_Face face = v->face;
FT_Bool use_kerning;
FT_UInt previous;
int pen_x, pen_y, wd, ht, dim, n, m, l = strlen(s), error;
PGlyph glyphs = l?malloc(l*sizeof(TGlyph)):NULL; /* glyphs table */
PGlyph glyph; /* current glyph in table */
FT_BBox bbox; /* bounding box */
FT_Vector pen;
ggi_color fgcol, bgcol, *col = NULL;
ggi_pixel pix;
unsigned fgalpha = 0xffff, bgalpha = 0xffff;
if (!glyphs) return (l==0);
/* load the glyph sequence */
/* NOTE: we assume a left-to-right script like Latin here */
pen_x = 0;
pen_y = 0;
use_kerning = FT_HAS_KERNING(face);
previous = 0;
glyph = glyphs;
for (n = 0; n < l; n++) {
glyph->index = FT_Get_Char_Index(face, s[n]);
/* apply kerning if available */
if (use_kerning && previous && glyph->index) {
FT_Vector delta;
FT_Get_Kerning(face, previous, glyph->index, FT_KERNING_DEFAULT,
&delta);
pen_x += delta.x;
}
/* store current pen position */
glyph->pos.x = pen_x;
glyph->pos.y = pen_y;
error = FT_Load_Glyph(face, glyph->index, FT_LOAD_DEFAULT);
if (error) continue;
error = FT_Get_Glyph(face->glyph, &glyph->image);
if (error) continue;
/* translate the glyph image now */
FT_Glyph_Transform(glyph->image, 0, &glyph->pos);
pen_x += face->glyph->advance.x;
previous = glyph->index;
/* increment number of glyphs */
glyph++;
}
/* count number of glyphs loaded */
m = glyph - glyphs;
/* transform glyphs */
if (v->vect || v->matrix)
for (n = 0; n < m; n++)
FT_Glyph_Transform(glyphs[n].image, v->matrix, v->vect);
/* compute the bounding box */
bbox.xMin = bbox.yMin = 32000;
bbox.xMax = bbox.yMax = -32000;
for (n = 0; n < m; n++) {
FT_BBox glyph_bbox;
FT_Glyph_Get_CBox(glyphs[n].image, ft_glyph_bbox_pixels, &glyph_bbox);
if (glyph_bbox.xMin < bbox.xMin) bbox.xMin = glyph_bbox.xMin;
if (glyph_bbox.yMin < bbox.yMin) bbox.yMin = glyph_bbox.yMin;
if (glyph_bbox.xMax > bbox.xMax) bbox.xMax = glyph_bbox.xMax;
if (glyph_bbox.yMax > bbox.yMax) bbox.yMax = glyph_bbox.yMax;
}
if ( bbox.xMin > bbox.xMax ) {
bbox.xMin = 0; bbox.yMin = 0; bbox.xMax = 0; bbox.yMax = 0;
}
/* now actually render the glyphs */
/* compute string dimensions in integer pixels */
wd = bbox.xMax - bbox.xMin;
ht = bbox.yMax - bbox.yMin;
dim = wd*ht;
if (dim == 0) {
free(glyphs);
for (n = 0; n < m; n++)
FT_Done_Glyph(glyphs[n].image);
return 1;
}
if (v->abuf)
if (v->asz == 1) {
fgalpha = v->afg*0x101;
bgalpha = v->abg*0x101;
} else {
fgalpha = v->afg;
bgalpha = v->abg;
}
if (ggiGetGCForeground(vis, &pix) || ggiUnmapPixel(vis, pix, &fgcol))
goto errout;
fgcol.a = fgalpha;
if (ggiGetGCBackground(vis, &pix) || ggiUnmapPixel(vis, pix, &bgcol))
goto errout;
bgcol.a = bgalpha;
if (wd > INT_MAX/ht ||
!(col = malloc(dim*sizeof(ggi_color))))
goto errout;
for (n = 0; n < dim; n++)
col[n] = bgcol;
pen.x = 0;
pen.y = 0;
for (n = 0; n < m; n++) {
FT_Glyph image;
image = glyphs[n].image;
if (v->aa) {
error = FT_Glyph_To_Bitmap(&image, FT_RENDER_MODE_NORMAL, &pen, 1);
if (!error) {
FT_BitmapGlyph bit = (FT_BitmapGlyph)image;
FT_Bitmap *bitmap = &bit->bitmap;
unsigned char *buf = bitmap->buffer, *bufp;
int p, q, r, w = bitmap->width, h = bitmap->rows,
pitch = bitmap->pitch,
startx = bit->left - bbox.xMin, starty = bbox.yMax - bit->top;
for (bufp = buf, r = starty*wd+startx, q = 0; q < h;
bufp += pitch, r += wd, q++)
for (p = 0; p < w; p++) {
int i = r+p;
if (bufp[p]) {
/* blend fg with bg color -- this looks odd with some fg/bg
combinations, is there a better way to do this? */
col[i].r = (((long)fgcol.r)*bufp[p]+
((long)bgcol.r)*(0xff-bufp[p]))/0xff;
col[i].g = (((long)fgcol.g)*bufp[p]+
((long)bgcol.g)*(0xff-bufp[p]))/0xff;
col[i].b = (((long)fgcol.b)*bufp[p]+
((long)bgcol.b)*(0xff-bufp[p]))/0xff;
col[i].a = (((long)fgcol.a)*bufp[p]+
((long)bgcol.a)*(0xff-bufp[p]))/0xff;
}
}
FT_Done_Glyph(image);
}
} else {
error = FT_Glyph_To_Bitmap(&image, FT_RENDER_MODE_MONO, &pen, 1);
if (!error) {
FT_BitmapGlyph bit = (FT_BitmapGlyph)image;
FT_Bitmap *bitmap = &bit->bitmap;
unsigned char *buf = bitmap->buffer, *bufp;
int p, q, r, w = bitmap->width, h = bitmap->rows,
pitch = bitmap->pitch,
startx = bit->left - bbox.xMin, starty = bbox.yMax - bit->top;
for (bufp = buf, r = starty*wd+startx, q = 0; q < h;
bufp += pitch, r += wd, q++)
for (p = 0; p < w; p++) {
int i = r+p;
int j = p>>3, k = 7-(p&7);
if (bufp[j] & (1<<k))
col[i] = fgcol;
}
FT_Done_Glyph(image);
}
}
}
free(glyphs);
/* blit the rendered string to the visual */
if (!error)
error = put_box(v, x, y, wd, ht, col) < 0;
free(col);
return !error;
errout:
if (col) free(col);
for (n = 0; n < m; n++)
FT_Done_Glyph(glyphs[n].image);
free(glyphs);
return 0;
}
/* compute the bounding box of a rendered string */
static int bbox(visual_t *v, char *s,
int *xmin, int *xmax,
int *ymin, int *ymax)
{
ggi_visual_t vis = v->vis;
FT_Face face = v->face;
FT_Bool use_kerning;
FT_UInt previous;
int pen_x, pen_y, n, m, l = strlen(s), error;
PGlyph glyphs = l?malloc(l*sizeof(TGlyph)):NULL; /* glyphs table */
PGlyph glyph; /* current glyph in table */
FT_BBox bbox; /* bounding box */
*xmin = *xmax = *ymin = *ymax = 0;
if (!glyphs) return (l==0);
pen_x = 0;
pen_y = 0;
use_kerning = FT_HAS_KERNING(face);
previous = 0;
glyph = glyphs;
for (n = 0; n < l; n++) {
glyph->index = FT_Get_Char_Index(face, s[n]);
if (use_kerning && previous && glyph->index) {
FT_Vector delta;
FT_Get_Kerning(face, previous, glyph->index, FT_KERNING_DEFAULT,
&delta);
pen_x += delta.x;
}
glyph->pos.x = pen_x;
glyph->pos.y = pen_y;
error = FT_Load_Glyph(face, glyph->index, FT_LOAD_DEFAULT);
if (error) continue;
error = FT_Get_Glyph(face->glyph, &glyph->image);
if (error) continue;
FT_Glyph_Transform(glyph->image, 0, &glyph->pos);
pen_x += face->glyph->advance.x;
previous = glyph->index;
glyph++;
}
m = glyph - glyphs;
if (v->vect || v->matrix)
for (n = 0; n < m; n++)
FT_Glyph_Transform(glyphs[n].image, v->matrix, v->vect);
bbox.xMin = bbox.yMin = 32000;
bbox.xMax = bbox.yMax = -32000;
for (n = 0; n < m; n++) {
FT_BBox glyph_bbox;
FT_Glyph_Get_CBox(glyphs[n].image, ft_glyph_bbox_pixels, &glyph_bbox);
if (glyph_bbox.xMin < bbox.xMin) bbox.xMin = glyph_bbox.xMin;
if (glyph_bbox.yMin < bbox.yMin) bbox.yMin = glyph_bbox.yMin;
if (glyph_bbox.xMax > bbox.xMax) bbox.xMax = glyph_bbox.xMax;
if (glyph_bbox.yMax > bbox.yMax) bbox.yMax = glyph_bbox.yMax;
}
if ( bbox.xMin > bbox.xMax ) {
bbox.xMin = 0; bbox.yMin = 0; bbox.xMax = 0; bbox.yMax = 0;
}
for (n = 0; n < m; n++)
FT_Done_Glyph(glyphs[n].image);
free(glyphs);
*xmin = bbox.xMin;
*xmax = bbox.xMax;
*ymin = bbox.yMin;
*ymax = bbox.yMax;
return 1;
}
#endif
FUNCTION(ggi,ggi_get_string_bbox,argc,argv)
{
visual_t *v;
int xmin, xmax, ymin, ymax;
char *s;
if (init && argc == 2 && isobj(argv[0], type(GGIVisual), (void**)&v) &&
v->vis && isstr(argv[1], &s))
#ifdef HAVE_FT2
if (v->face)
if (bbox(v, s, &xmin, &xmax, &ymin, &ymax))
return mktuplel(4, mkint(xmin), mkint(xmax), mkint(ymin), mkint(ymax));
else
return __FAIL;
else
#endif
if (ggiGetCharSize(v->vis, &xmax, &ymax))
return __FAIL;
else
return mktuplel(4, mkint(0), mkint(xmax*strlen(s)), mkint(0),
mkint(ymax));
else
return __FAIL;
}
FUNCTION(ggi,ggi_puts,argc,argv)
{
visual_t *v;
expr *xv;
int n;
long x, y;
char *s;
if (init && argc == 3 && isobj(argv[0], type(GGIVisual), (void**)&v) &&
v->vis && istuple(argv[1], &n, &xv) && n == 2 &&
isint(xv[0], &x) && isint(xv[1], &y) &&
isstr(argv[2], &s)) {
#ifdef HAVE_FT2
if (v->face)
if (render(v, s, (int)x, (int)y))
return mkvoid;
else
return __FAIL;
else
#endif
/* FIXME: alpha blending */
if (ggiPuts(v->vis, (int)x, (int)y, s) >= 0)
return mkvoid;
else
return __FAIL;
} else
return __FAIL;
}
FUNCTION(ggi,ggi_putc,argc,argv)
{
visual_t *v;
expr *xv;
int n;
long x, y;
char *s;
if (init && argc == 3 && isobj(argv[0], type(GGIVisual), (void**)&v) &&
v->vis && istuple(argv[1], &n, &xv) && n == 2 &&
isint(xv[0], &x) && isint(xv[1], &y) &&
isstr(argv[2], &s) && s[0] && !s[1]) {
#ifdef HAVE_FT2
if (v->face)
if (render(v, s, (int)x, (int)y))
return mkvoid;
else
return __FAIL;
else
#endif
/* FIXME: alpha blending */
if (!ggiPutc(v->vis, (int)x, (int)y, s[0]))
return mkvoid;
else
return __FAIL;
} else
return __FAIL;
}
FUNCTION(ggi,ggi_get_pixel,argc,argv)
{
visual_t *v;
expr *xv;
int n;
long x, y;
ggi_pixel pixel;
unsigned alpha = 0xffff;
if (init && argc == 2 && isobj(argv[0], type(GGIVisual), (void**)&v) &&
v->vis && istuple(argv[1], &n, &xv) && n == 2 &&
isint(xv[0], &x) && isint(xv[1], &y)) {
ggi_color *c;
if ((n = get_box(v, x, y, 1, 1, &c)) < 0)
return __ERROR;
else if (n == 0)
return __FAIL;
else
return mkcolors(c, n);
} else
return __FAIL;
}
FUNCTION(ggi,ggi_put_pixel,argc,argv)
{
visual_t *v;
expr *xv;
int n;
long x, y;
ggi_color *c;
if (init && argc == 3 && isobj(argv[0], type(GGIVisual), (void**)&v) &&
v->vis && istuple(argv[1], &n, &xv) && n == 2 &&
isint(xv[0], &x) && isint(xv[1], &y) &&
iscolor(argv[2], &c)) {
if ((n = put_box(v, x, y, 1, 1, c)) < 0)
return __ERROR;
else if (n == 0)
return __FAIL;
else
return mkvoid;
} else
return __FAIL;
}
FUNCTION(ggi,ggi_get_hline,argc,argv)
{
visual_t *v;
expr *xv;
int n;
long x, y, w;
if (init && argc == 3 && isobj(argv[0], type(GGIVisual), (void**)&v) &&
v->vis && vis_check(v->vis) && istuple(argv[1], &n, &xv) && n == 2 &&
isint(xv[0], &x) && isint(xv[1], &y) &&
isint(argv[2], &w)) {
ggi_color *c;
if (w <= 0)
return mkcolors(NULL, 0);
else if ((n = get_box(v, x, y, w, 1, &c)) < 0)
return __ERROR;
else if (n == 0)
return __FAIL;
else
return mkcolors(c, n);
} else
return __FAIL;
}
FUNCTION(ggi,ggi_put_hline,argc,argv)
{
visual_t *v;
expr *xv;
int n;
long x, y, w;
ggi_color *c;
if (init && argc == 4 && isobj(argv[0], type(GGIVisual), (void**)&v) &&
v->vis && vis_check(v->vis) && istuple(argv[1], &n, &xv) && n == 2 &&
isint(xv[0], &x) && isint(xv[1], &y) &&
isint(argv[2], &w) &&
iscolors(argv[3], &c, &n)) {
if (w != n) return __FAIL;
if (n <= 0) return mkvoid;
if ((n = put_box(v, x, y, w, 1, c)) < 0)
return __ERROR;
else if (n == 0)
return __FAIL;
else
return mkvoid;
} else
return __FAIL;
}
FUNCTION(ggi,ggi_get_vline,argc,argv)
{
visual_t *v;
expr *xv;
int n;
long x, y, h;
if (init && argc == 3 && isobj(argv[0], type(GGIVisual), (void**)&v) &&
v->vis && vis_check(v->vis) && istuple(argv[1], &n, &xv) && n == 2 &&
isint(xv[0], &x) && isint(xv[1], &y) &&
isint(argv[2], &h)) {
ggi_color *c;
if (h <= 0)
return mkcolors(NULL, 0);
else if ((n = get_box(v, x, y, 1, h, &c)) < 0)
return __ERROR;
else if (n == 0)
return __FAIL;
else
return mkcolors(c, n);
} else
return __FAIL;
}
FUNCTION(ggi,ggi_put_vline,argc,argv)
{
visual_t *v;
expr *xv;
int n;
long x, y, h;
ggi_color *c;
if (init && argc == 4 && isobj(argv[0], type(GGIVisual), (void**)&v) &&
v->vis && vis_check(v->vis) && istuple(argv[1], &n, &xv) && n == 2 &&
isint(xv[0], &x) && isint(xv[1], &y) &&
isint(argv[2], &h) &&
iscolors(argv[3], &c, &n)) {
if (h != n) return __FAIL;
if (n <= 0) return mkvoid;
if ((n = put_box(v, x, y, 1, h, c)) < 0)
return __ERROR;
else if (n == 0)
return __FAIL;
else
return mkvoid;
} else
return __FAIL;
}
FUNCTION(ggi,ggi_get_box,argc,argv)
{
visual_t *v;
expr *xv;
int n;
long x, y, w, h;
if (init && argc == 3 && isobj(argv[0], type(GGIVisual), (void**)&v) &&
v->vis && vis_check(v->vis) && istuple(argv[1], &n, &xv) && n == 2 &&
isint(xv[0], &x) && isint(xv[1], &y) &&
istuple(argv[2], &n, &xv) && n == 2 &&
isint(xv[0], &w) && isint(xv[1], &h)) {
ggi_color *c;
if (w <= 0 || h <= 0)
return mkcolors(NULL, 0);
else if ((n = get_box(v, x, y, w, h, &c)) < 0)
return __ERROR;
else if (n == 0)
return __FAIL;
else
return mkcolors(c, n);
} else
return __FAIL;
}
FUNCTION(ggi,ggi_put_box,argc,argv)
{
visual_t *v;
expr *xv;
int n;
long x, y, w, h;
ggi_color *c;
if (init && argc == 4 && isobj(argv[0], type(GGIVisual), (void**)&v) &&
v->vis && vis_check(v->vis) && istuple(argv[1], &n, &xv) && n == 2 &&
isint(xv[0], &x) && isint(xv[1], &y) &&
istuple(argv[2], &n, &xv) && n == 2 &&
isint(xv[0], &w) && isint(xv[1], &h) &&
iscolors(argv[3], &c, &n)) {
if (w*h != n) return __FAIL;
if (n <= 0) return mkvoid;
if ((n = put_box(v, x, y, w, h, c)) < 0)
return __ERROR;
else if (n == 0)
return __FAIL;
else
return mkvoid;
} else
return __FAIL;
}
FUNCTION(ggi,ggi_flush,argc,argv)
{
visual_t *v;
if (init && argc == 1 && isobj(argv[0], type(GGIVisual), (void**)&v) &&
v->vis) {
ggiFlush(v->vis);
return mkvoid;
} else
return __FAIL;
}
FUNCTION(ggi,ggi_flush_region,argc,argv)
{
visual_t *v;
expr *xv;
int n;
long x, y, w, h;
if (init && argc == 3 && isobj(argv[0], type(GGIVisual), (void**)&v) &&
v->vis && istuple(argv[1], &n, &xv) && n == 2 &&
isint(xv[0], &x) && isint(xv[1], &y) &&
istuple(argv[2], &n, &xv) && n == 2 &&
isint(xv[0], &w) && isint(xv[1], &h)) {
ggiFlushRegion(v->vis, (int)x, (int)y, (int)w, (int)h);
return mkvoid;
} else
return __FAIL;
}
FUNCTION(ggi,ggi_devices,argc,argv)
{
visual_t *v;
if (init && argc == 1 && isobj(argv[0], type(GGIVisual), (void**)&v) &&
v->vis) {
gii_input_t inp = ggiGetInput(v->vis);
unsigned n, origin;
gii_cmddata_getdevinfo info;
expr *xv;
for (n = 0; !giiQueryDeviceInfoByNumber(inp, n, &origin, &info); n++)
;
if (!(xv = malloc(n*sizeof(expr)))) return __ERROR;
for (n = 0; !giiQueryDeviceInfoByNumber(inp, n, &origin, &info); n++)
xv[n] = mkuint(origin);
return mklistv(n, xv);
} else
return __FAIL;
}
FUNCTION(ggi,ggi_device_info,argc,argv)
{
visual_t *v;
unsigned long origin;
if (init && argc == 2 && isobj(argv[0], type(GGIVisual), (void**)&v) &&
v->vis && isuint(argv[1], &origin)) {
gii_input_t inp = ggiGetInput(v->vis);
gii_cmddata_getdevinfo info;
if (giiQueryDeviceInfo(inp, origin, &info))
return __FAIL;
else
return mktuplel(5, mkstr(strdup(info.longname)),
mkstr(strdup(info.shortname)),
mkuint((unsigned long)info.can_generate),
mkint((unsigned long)info.num_buttons),
mkint((unsigned long)info.num_axes));
} else
return __FAIL;
}
FUNCTION(ggi,ggi_val_info,argc,argv)
{
visual_t *v;
unsigned long origin, n;
if (init && argc == 3 && isobj(argv[0], type(GGIVisual), (void**)&v) &&
v->vis && isuint(argv[1], &origin) && isuint(argv[2], &n)) {
gii_input_t inp = ggiGetInput(v->vis);
gii_cmddata_getvalinfo info;
if (giiQueryValInfo(inp, origin, n, &info))
return __FAIL;
else
return mktuplel(10, mkstr(strdup(info.longname)),
mkstr(strdup(info.shortname)),
mkint((long)info.range.min),
mkint((long)info.range.center),
mkint((long)info.range.max),
mkint((long)info.phystype),
mkint((long)info.SI_add),
mkint((long)info.SI_mul),
mkint((long)info.SI_div),
mkint((long)info.SI_shift));
} else
return __FAIL;
}
FUNCTION(ggi,ggi_set_mask,argc,argv)
{
visual_t *v;
unsigned long mask;
if (init && argc == 2 && isobj(argv[0], type(GGIVisual), (void**)&v) &&
v->vis && isuint(argv[1], &mask)) {
if (ggiSetEventMask(v->vis, (gii_event_mask)mask))
return __FAIL;
else
return mkvoid;
} else
return __FAIL;
}
FUNCTION(ggi,ggi_get_mask,argc,argv)
{
visual_t *v;
if (init && argc == 1 && isobj(argv[0], type(GGIVisual), (void**)&v) &&
v->vis)
return mkuint((unsigned long)ggiGetEventMask(v->vis));
else
return __FAIL;
}
FUNCTION(ggi,ggi_poll,argc,argv)
{
visual_t *v;
unsigned long mask;
if (init && argc == 3 && isobj(argv[0], type(GGIVisual), (void**)&v) &&
v->vis && isuint(argv[1], &mask)) {
ggi_event_mask ret;
struct timeval tv, *timeout = NULL;
long ti;
double tf;
if (isint(argv[2], &ti))
if (ti >= 0) {
tv.tv_sec = ti;
tv.tv_usec = 0;
timeout = &tv;
} else
return __FAIL;
else if (isfloat(argv[2], &tf))
if (tf >= 0.0) {
double ip, fp;
if (tf > LONG_MAX) tf = LONG_MAX;
fp = modf(tf, &ip);
tv.tv_sec = (unsigned long)ip;
tv.tv_usec = (unsigned long)(fp*1e6);
timeout = &tv;
} else
return __FAIL;
else if (!isvoid(argv[2]))
return __FAIL;
if ((ret = ggiEventPoll(v->vis, (gii_event_mask)mask, timeout)) >= 0)
return mkuint((unsigned long)ret);
else
return __FAIL;
} else
return __FAIL;
}
FUNCTION(ggi,ggi_queued,argc,argv)
{
visual_t *v;
unsigned long mask;
if (init && argc == 2 && isobj(argv[0], type(GGIVisual), (void**)&v) &&
v->vis && isuint(argv[1], &mask)) {
if (ggiEventsQueued(v->vis, (gii_event_mask)mask))
return mktrue;
else
return mkfalse;
} else
return __FAIL;
}
static expr decode_event(gii_event *e)
{
gii_any_event *any = &e->any;
expr x = mksym(sym(ggi_event));
/* common parameters */
x = mkapp(x, mkuint((unsigned long)any->type));
x = mkapp(x, mkint((long)any->error));
x = mkapp(x, mkuint((unsigned long)any->origin));
x = mkapp(x, mkuint((unsigned long)any->target));
x = mkapp(x, mkfloat(((double)any->time.tv_sec)+
((double)any->time.tv_usec)*1e-6));
/* specific parameters */
switch (any->type) {
case evKeyPress:
case evKeyRelease:
case evKeyRepeat:
{
gii_key_event *key = &e->key;
x = mkapp(x, mktuplel(4, mkuint((unsigned long)key->modifiers),
mkuint((unsigned long)key->sym),
mkuint((unsigned long)key->label),
mkuint((unsigned long)key->button)));
}
break;
case evPtrRelative:
case evPtrAbsolute:
{
gii_pmove_event *pmove = &e->pmove;
x = mkapp(x, mktuplel(4, mkint((long)pmove->x),
mkint((long)pmove->y),
mkint((long)pmove->z),
mkint((long)pmove->wheel)));
}
break;
case evPtrButtonPress:
case evPtrButtonRelease:
{
gii_pbutton_event *pbutton = &e->pbutton;
x = mkapp(x, mkuint((unsigned long)pbutton->button));
}
break;
case evCommand:
case evInformation:
{
gii_cmd_event *cmd = &e->cmd;
if (cmd->code & GII_CMDFLAG_NODATA)
x = mkapp(x, mkuint((unsigned long)cmd->code));
else {
bstr_t *m = malloc(sizeof(bstr_t));
if (!m || !(m->v = malloc(GII_CMD_DATA_MAX))) {
if (m) free(m);
dispose(x);
return __ERROR;
}
memcpy(m->v, cmd->data, GII_CMD_DATA_MAX);
m->size = GII_CMD_DATA_MAX;
x = mkapp(x, mktuplel(2, mkuint((unsigned long)cmd->code),
mkobj(type(ByteStr), (void*)m)));
}
}
break;
case evValRelative:
case evValAbsolute:
{
gii_val_event *val = &e->val;
expr *xv = malloc((val->count+1)*sizeof(expr));
int i;
if (!xv) {
dispose(x);
return __ERROR;
}
xv[0] = mkuint((unsigned long)val->first);
for (i = 0; i < val->count; i++)
xv[i+1] = mkint((long)val->value[i]);
x = mkapp(x, mktuplev(val->count+1, xv));
}
break;
case evExpose:
{
gii_expose_event *expose = &e->expose;
x = mkapp(x, mktuplel(4, mkuint((unsigned long)expose->x),
mkuint((unsigned long)expose->y),
mkuint((unsigned long)expose->h),
mkuint((unsigned long)expose->w)));
}
break;
default:
x = mkapp(x, mkvoid);
break;
}
return x;
}
FUNCTION(ggi,ggi_read,argc,argv)
{
visual_t *v;
unsigned long mask;
if (init && argc == 2 && isobj(argv[0], type(GGIVisual), (void**)&v) &&
v->vis && isuint(argv[1], &mask)) {
ggi_event e;
if (ggiEventRead(v->vis, &e, (gii_event_mask)mask))
return decode_event(&e);
else
return __FAIL;
} else
return __FAIL;
}
static int encode_event(expr x, gii_event *e)
{
expr y, params, arg;
unsigned long type, origin, target;
long error;
struct timeval tv;
if (isapp(x, &y, ¶ms))
x = y;
else
return 0;
if (isapp(x, &y, &arg)) {
long ti;
double tf;
if (isint(arg, &ti))
if (ti >= 0) {
tv.tv_sec = ti;
tv.tv_usec = 0;
} else
return 0;
else if (isfloat(arg, &tf))
if (tf >= 0.0) {
double ip, fp;
if (tf > LONG_MAX) tf = LONG_MAX;
fp = modf(tf, &ip);
tv.tv_sec = (unsigned long)ip;
tv.tv_usec = (unsigned long)(fp*1e6);
} else
return 0;
else
return 0;
x = y;
} else
return 0;
if (isapp(x, &y, &arg) && isuint(arg, &target))
x = y;
else
return 0;
if (isapp(x, &y, &arg) && isuint(arg, &origin))
x = y;
else
return 0;
if (isapp(x, &y, &arg) && isint(arg, &error))
x = y;
else
return 0;
if (isapp(x, &y, &arg) && issym(y, sym(ggi_event)) &&
isuint(arg, &type)) {
gii_any_event *any = &e->any;
any->type = type;
any->error = error;
any->origin = origin;
any->target = target;
any->time = tv;
switch (type) {
case evKeyPress:
case evKeyRelease:
case evKeyRepeat:
{
gii_key_event *key = &e->key;
int n;
expr *xv;
unsigned long modifiers, sym, label, button;
if (istuple(params, &n, &xv) && n == 4 &&
isuint(xv[0], &modifiers) &&
isuint(xv[1], &sym) &&
isuint(xv[2], &label) &&
isuint(xv[3], &button)) {
key->modifiers = modifiers;
key->sym = sym;
key->label = label;
key->button = button;
key->size = sizeof(gii_key_event);
return 1;
} else
return 0;
}
case evPtrRelative:
case evPtrAbsolute:
{
gii_pmove_event *pmove = &e->pmove;
int n;
expr *xv;
long x, y, z, wheel;
if (istuple(params, &n, &xv) && n == 4 &&
isint(xv[0], &x) &&
isint(xv[1], &y) &&
isint(xv[2], &z) &&
isint(xv[3], &wheel)) {
pmove->x = x;
pmove->y = y;
pmove->z = z;
pmove->wheel = wheel;
pmove->size = sizeof(gii_pmove_event);
return 1;
} else
return 0;
}
case evPtrButtonPress:
case evPtrButtonRelease:
{
gii_pbutton_event *pbutton = &e->pbutton;
unsigned long button;
if (isuint(params, &button)) {
pbutton->button = button;
pbutton->size = sizeof(gii_pbutton_event);
return 1;
} else
return 0;
}
case evCommand:
case evInformation:
{
gii_cmd_event *cmd = &e->cmd;
unsigned long code;
bstr_t *m;
int n;
expr *xv;
if (isuint(params, &code)) {
int i;
cmd->code = code;
for (i = 0; i < GII_CMD_DATA_MAX; i++) cmd->data[i] = 0;
cmd->size = sizeof(gii_cmd_nodata_event);
return 1;
} else if (istuple(params, &n, &xv) && n == 2 &&
isuint(xv[0], &code) &&
isobj(xv[1], type(ByteStr), (void**)&m) &&
m->size <= GII_CMD_DATA_MAX) {
int i;
cmd->code = code;
for (i = 0; i < GII_CMD_DATA_MAX; i++) cmd->data[i] = m->v[i];
for (i = m->size; i < GII_CMD_DATA_MAX; i++) cmd->data[i] = 0;
cmd->size = sizeof(gii_cmd_event);
return 1;
} else
return 0;
}
case evValRelative:
case evValAbsolute:
{
gii_val_event *val = &e->val;
unsigned long first;
int n;
expr *xv;
if (istuple(params, &n, &xv) && n > 0 && n-1 <= 32 &&
isuint(xv[0], &first)) {
int i;
long value;
val->first = first;
val->count = n-1;
for (i = 0; i < n-1; i++)
if (isint(xv[i+1], &value))
val->value[i] = value;
else
return 0;
for (i = n-1; i < 32; i++) val->value[i] = 0;
val->size = sizeof(gii_val_event);
return 1;
} else
return 0;
}
case evExpose:
{
gii_expose_event *expose = &e->expose;
int n;
expr *xv;
unsigned long x, y, h, w;
if (istuple(params, &n, &xv) && n == 4 &&
isuint(xv[0], &x) &&
isuint(xv[1], &y) &&
isuint(xv[2], &h) &&
isuint(xv[3], &w)) {
expose->x = x;
expose->y = y;
expose->h = h;
expose->w = w;
expose->size = sizeof(gii_expose_event);
return 1;
} else
return 0;
}
default:
if (isvoid(params)) {
any->size = sizeof(gii_any_event);
return 1;
} else
return 0;
}
} else
return 0;
}
FUNCTION(ggi,ggi_send,argc,argv)
{
visual_t *v;
ggi_event e;
if (init && argc == 2 && isobj(argv[0], type(GGIVisual), (void**)&v) &&
v->vis && encode_event(argv[1], &e) && !ggiEventSend(v->vis, &e))
return mkvoid;
else
return __FAIL;
}
FUNCTION(ggi,ggi_kbhit,argc,argv)
{
visual_t *v;
if (init && argc == 1 && isobj(argv[0], type(GGIVisual), (void**)&v) &&
v->vis) {
return ggiKbhit(v->vis)?mktrue:mkfalse;
} else
return __FAIL;
}
FUNCTION(ggi,ggi_getc,argc,argv)
{
visual_t *v;
if (init && argc == 1 && isobj(argv[0], type(GGIVisual), (void**)&v) &&
v->vis)
return mkint(ggiGetc(v->vis));
else
return __FAIL;
}
syntax highlighted by Code2HTML, v. 0.9.1