/* fonts.c -- font manipulation
   $Id$

   Copyright (C) 1999 John Harper <john@dcs.warwick.ac.uk>

   This file is part of sawfish.

   sawfish 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.

   sawfish 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 sawfish; see the file COPYING.   If not, write to
   the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */

/* AIX requires this to be the first thing in the file.  */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifndef __GNUC__
# if HAVE_ALLOCA_H
#  include <alloca.h>
# else
#  ifdef _AIX
 #pragma alloca
#  else
#   ifndef alloca /* predefined by HP cc +Olibcalls */
   char *alloca ();
#   endif
#  endif
# endif
#endif

#include "sawfish.h"
#include <string.h>
#include <X11/Xlocale.h>
#include <ctype.h>

#ifdef HAVE_X11_XFT_XFT_H
# include <X11/Xft/Xft.h>
#else
# undef HAVE_PANGO
#endif

#ifdef HAVE_PANGO
# include <glib.h>
# define PANGO_ENABLE_BACKEND
# include <pango/pango.h>
# undef PANGO_ENABLE_BACKEND
# ifdef HAVE_PANGO_XFT
#  define PANGO_ENABLE_ENGINE
#  include <pango/pangoxft.h>
#  undef PANGO_ENABLE_ENGINE
# endif
#endif

static Lisp_Font *font_list;
int font_type;

DEFSYM(default_font, "default-font");

struct Lisp_Font_Class_struct {
    const char *type;
    bool (*load) (Lisp_Font *f);
    void (*finalize) (Lisp_Font *f);
    int (*measure) (Lisp_Font *f, char *string, size_t length);
    void (*draw) (Lisp_Font *f, char *string, size_t length,
		  Window id, GC gc, Lisp_Color *fg, int x, int y);
};

/* Xlib font structs */

static bool
fontstruct_load (Lisp_Font *f)
{
    XFontStruct *font_struct;

    font_struct = XLoadQueryFont (dpy, rep_STR (f->name));

    if (font_struct == 0)
	return FALSE;

    f->font = font_struct;
    f->ascent = font_struct->ascent;
    f->descent = font_struct->descent;

    return TRUE;
}

static void
fontstruct_finalize (Lisp_Font *f)
{
    XFreeFont (dpy, f->font);
}

static int
fontstruct_measure (Lisp_Font *f, char *string, size_t length)
{
    return XTextWidth (f->font, string, length);
}

static void
fontstruct_draw (Lisp_Font *f, char *string, size_t length,
		 Window id, GC gc, Lisp_Color *fg, int x, int y)
{
    XFontStruct *fs;
    XGCValues gcv;

    fs = f->font;

    gcv.foreground = fg->pixel;
    gcv.font = fs->fid;
    XChangeGC (dpy, gc, GCForeground | GCFont, &gcv);

    XDrawString (dpy, id, gc, x, y, string, length);
}

static const Lisp_Font_Class fontstruct_class = {
    "xlfd",
    fontstruct_load, fontstruct_finalize,
    fontstruct_measure, fontstruct_draw,
};

/* Xlib font sets */

static char *
xlfd_get_element (const char *xlfd, int idx)
{
    const char *p = xlfd;
    while (*p != 0)
    {
	if (*p == '-' && --idx == 0)
	{
	    const char *end = strchr (p + 1, '-');
	    char *buf;
	    size_t len;
	    if (end == 0)
		end = p + strlen (p);
	    len = end - (p + 1);
	    buf = malloc (len + 1);
	    memcpy (buf, p + 1, len);
	    buf[len] = 0;
	    return buf;
	}
	p++;
    }
    return 0;
}

static char *
generalize_xlfd (const char *xlfd)
{
    char *weight = xlfd_get_element (xlfd, 3);
    char *slant = xlfd_get_element (xlfd, 4);
    char *pxlsz = xlfd_get_element (xlfd, 7);

    char *buf;
    int len;

    if (weight == 0)
	weight = strdup ("*");
    if (slant == 0)
	slant = strdup ("*");
    if (pxlsz == 0)
	pxlsz = strdup ("*");

#define XLFD_FORMAT "%s,-*-*-%s-%s-*-*-%s-*-*-*-*-*-*-*," \
		    "-*-*-*-*-*-*-%s-*-*-*-*-*-*-*,*"

    len = strlen (xlfd) + strlen (weight) + strlen (slant) +
          strlen (pxlsz) + strlen (pxlsz) + strlen (XLFD_FORMAT);
    buf = malloc (len + 1);
#ifdef HAVE_SNPRINTF
    snprintf (buf, len + 1, XLFD_FORMAT, xlfd, weight, slant, pxlsz, pxlsz);
#else
    sprintf (buf, XLFD_FORMAT, xlfd, weight, slant, pxlsz, pxlsz);
#endif

    free (pxlsz);
    free (slant);
    free (weight);

    return buf;
}

static XFontSet
x_create_fontset (char *xlfd, char ***missing,
		   int *nmissing, char **def_string)
{
    XFontSet fs = XCreateFontSet (dpy, xlfd, missing, nmissing, def_string);

    if (fs != 0 && *nmissing == 0)
	return fs;

    /* for non-iso8859-1 language and iso8859-1 specification
       (this fontset is only for pattern analysis) */

    if (fs == 0)
    {
	char *old_locale, *tem;

	if (*nmissing != 0)
	    XFreeStringList (*missing);

	/* Save the old LC_CTYPE locale.. */
	tem = setlocale (LC_CTYPE, 0);
	if (tem != 0)
	{
	    old_locale = alloca (strlen (tem) + 1);
	    strcpy (old_locale, tem);

	    /* ..then create the fontset in the default locale.. */
	    setlocale (LC_CTYPE, "C");
	}
	else
	    old_locale = 0;

	fs = XCreateFontSet (dpy, xlfd, missing, nmissing, def_string);

	/* ..then restore the original locale */
	if (old_locale != 0)
	    setlocale (LC_CTYPE, old_locale);
    }

    /* make XLFD font name for pattern analysis */
    if (fs != 0)
    {
	XFontStruct **fontstructs;
	char **fontnames;
	if (XFontsOfFontSet (fs, &fontstructs, &fontnames) > 0)
	    xlfd = fontnames[0];
    }

    xlfd = generalize_xlfd (xlfd);

    if (*nmissing != 0)
	XFreeStringList (*missing);
    if (fs != 0)
	XFreeFontSet (dpy, fs);

    fs = XCreateFontSet (dpy, xlfd, missing, nmissing, def_string);

    free (xlfd);
    return fs;
}

static bool
fontset_load (Lisp_Font *f)
{
    XFontSet font_set;
    int ascent, descent;

    char **missing_charset_list, *def_string;
    int num_missing_charset_list;

    font_set = x_create_fontset (rep_STR (f->name),
				 &missing_charset_list,
				 &num_missing_charset_list,
				 &def_string);

    if (font_set != 0)
    {
	XFontStruct **fstrs;
	char **font_names;
	int i, j, num_fonts;

	f->font = font_set;

	num_fonts = XFontsOfFontSet (font_set, &fstrs, &font_names);
	ascent = descent = 0;

	for (i = 0; i < num_fonts; i++)
	{
	    if (fstrs[i]->ascent > ascent)
		f->ascent = fstrs[i]->ascent;
	    if (fstrs[i]->descent > descent)
		f->descent = fstrs[i]->descent;
	}

	if (num_missing_charset_list > 0)
	{
	    fprintf (stderr, "Missing charsets in FontSet creation\n");
	    for (j = 0; j < num_missing_charset_list; j++)
		fprintf (stderr, "\t%s\n", missing_charset_list[j]);
	    XFreeStringList (missing_charset_list);
	}

	return TRUE;
    }

    return FALSE;
}

static void
fontset_finalize (Lisp_Font *f)
{
    XFreeFontSet (dpy, f->font);
}

static int
fontset_measure (Lisp_Font *f, char *string, size_t length)
{
#ifdef X_HAVE_UTF8_STRING
    return Xutf8TextEscapement (f->font, string, length);
#else
    return XmbTextEscapement (f->font, string, length);
#endif
}

static void
fontset_draw (Lisp_Font *f, char *string, size_t length,
	      Window id, GC gc, Lisp_Color *fg, int x, int y)
{
    XGCValues gcv;

    gcv.foreground = fg->pixel;
    XChangeGC (dpy, gc, GCForeground, &gcv);
#ifdef X_HAVE_UTF8_STRING
    Xutf8DrawString (dpy, id, f->font, gc, x, y, string, length);
#else
    XmbDrawString (dpy, id, f->font, gc, x, y, string, length);
#endif
}

static const Lisp_Font_Class fontset_class = {
    "xlfd",
    fontset_load, fontset_finalize,
    fontset_measure, fontset_draw,
};

/* Xft fonts */

#ifdef HAVE_X11_XFT_XFT_H

static bool
xft_load (Lisp_Font *f)
{
    XftFont *xft_font;

    xft_font = XftFontOpenName (dpy, screen_num, rep_STR (f->name));

    if (xft_font == 0)
	return FALSE;

    f->font = xft_font;
    f->ascent = xft_font->ascent;
    f->descent = xft_font->descent;

    return TRUE;
}

static void
xft_finalize (Lisp_Font *f)
{
    XftFontClose (dpy, f->font);
}

static int
xft_measure (Lisp_Font *f, char *string, size_t length)
{
    XGlyphInfo info;

    XftTextExtentsUtf8 (dpy, f->font, (FcChar8 *)string, length, &info);

    return info.xOff; 
}

static void
xft_draw (Lisp_Font *f, char *string, size_t length,
	  Window id, GC gc, Lisp_Color *fg, int x, int y)
{
    static XftDraw *draw;

    XftColor xft_color;

    if (draw == 0)
	draw = XftDrawCreate (dpy, id, image_visual, image_cmap);
    else
	XftDrawChange (draw, id);

    xft_color.pixel = fg->pixel;
    xft_color.color.red = fg->red;
    xft_color.color.green = fg->green;
    xft_color.color.blue = fg->blue;
    xft_color.color.alpha = fg->alpha;

    XftDrawStringUtf8 (draw, &xft_color, f->font,
                       x, y, (FcChar8 *)string, length);
}

static const Lisp_Font_Class xft_class = {
    "Xft",
    xft_load, xft_finalize,
    xft_measure, xft_draw,
};

#endif /* HAVE_X11_XFT_XFT_H */

/* Pango fonts */

#ifdef HAVE_PANGO

static PangoContext *pango_context;

static bool
pango_load (Lisp_Font *f)
{
    PangoLanguage *language;
    PangoFontDescription *fontdesc;
    PangoFont *font;
    PangoFontMetrics *metrics;

    if (pango_context)
    {
	language = pango_context_get_language (pango_context);
    }
    else
    {
	char *langname, *p;

#ifdef HAVE_PANGO_XFT
	pango_context = pango_xft_get_context (dpy, screen_num);
#endif

	langname = g_strdup (setlocale (LC_CTYPE, NULL));
	p = strchr (langname, '.');
	if (p)
	    *p = 0;
	p = strchr (langname, '@');
	if (p)
	    *p = 0;
	language = pango_language_from_string (langname);
	pango_context_set_language (pango_context, language);
	g_free (langname);
    }

    fontdesc = pango_font_description_from_string (rep_STR (f->name));

    if (!pango_font_description_get_family (fontdesc))
	pango_font_description_set_family (fontdesc, "Sans");
    if (pango_font_description_get_size (fontdesc) <= 0)
	pango_font_description_set_size (fontdesc, 12 * PANGO_SCALE);

    pango_context_set_font_description (pango_context, fontdesc);
    font = pango_context_load_font (pango_context, fontdesc);

    if (!font) {
        pango_font_description_free(fontdesc);
	return FALSE;
    }

    metrics = pango_font_get_metrics (font, language);

    f->ascent = metrics->ascent / PANGO_SCALE;
    f->descent = metrics->descent / PANGO_SCALE;

    pango_font_metrics_unref (metrics);

    f->font = fontdesc; /* We save the font description, not the font itself!
                        That's because it seems we can't recover it perfectly
                        later, and the layout routines want a description */

    return TRUE;
}

static void
pango_finalize (Lisp_Font *f)
{
    pango_font_description_free (f->font);
}

static int
pango_measure (Lisp_Font *f, char *string, size_t length)
{
    PangoLayout *layout;
    PangoRectangle rect;

    layout = pango_layout_new (pango_context);
    pango_layout_set_font_description(layout, f->font);
    pango_layout_set_text (layout, string, length);

    pango_layout_get_extents (layout, NULL, &rect);

    g_object_unref (layout);
 
    return rect.width / PANGO_SCALE;
}

static void
pango_draw_line (XftDraw *draw, Window id, GC gc, XftColor *xft_color,
		 PangoLayoutLine *line, int x, int y)
{
    GSList *p;

    for (p = line->runs; p != NULL; p = p->next)
    {
	PangoLayoutRun *run = p->data;
	PangoFont *font = run->item->analysis.font;
	PangoGlyphString *glyphs = run->glyphs;
	PangoRectangle rect;

	pango_glyph_string_extents (glyphs, font, NULL, &rect);
#ifdef HAVE_PANGO_XFT
	if (PANGO_XFT_IS_FONT (font))
	    pango_xft_render (draw, xft_color, font, glyphs, x, y);
#endif

	x += rect.width / PANGO_SCALE;
    }
}

static void
pango_draw (Lisp_Font *f, char *string, size_t length,
	    Window id, GC gc, Lisp_Color *fg, int x, int y)
{
    static XftDraw *draw;
    XftColor xft_color;
    PangoLayout *layout;
    PangoLayoutIter *iter;

    if (draw == 0)
	draw = XftDrawCreate (dpy, id, image_visual, image_cmap);
    else
	XftDrawChange (draw, id);

    xft_color.pixel = fg->pixel;
    xft_color.color.red = fg->red;
    xft_color.color.green = fg->green;
    xft_color.color.blue = fg->blue;
    xft_color.color.alpha = fg->alpha;

    layout = pango_layout_new (pango_context);
    pango_layout_set_font_description(layout, f->font);
    pango_layout_set_text (layout, string, length);
    iter = pango_layout_get_iter (layout);

    do {
	PangoLayoutLine *line = pango_layout_iter_get_line (iter);
	PangoRectangle rect;

	pango_layout_iter_get_line_extents (iter, NULL, &rect);
	pango_draw_line (draw, id, gc, &xft_color,
			 line, x + rect.x / PANGO_SCALE, y);
    } while (pango_layout_iter_next_line (iter));

    g_object_unref (layout);
    pango_layout_iter_free (iter);
}

static const Lisp_Font_Class pango_class = {
    "pango",
    pango_load, pango_finalize,
    pango_measure, pango_draw,
};

#endif /* HAVE_PANGO */

/* All classes */

static const Lisp_Font_Class *classes[] = {
    &fontstruct_class,
    &fontset_class,
#ifdef HAVE_X11_XFT_XFT_H
    &xft_class,
#endif
#ifdef HAVE_PANGO
    &pango_class,
#endif
    0,
};

/* Entry points */

DEFUN ("font-type-exists-p", Ffont_type_exists_p,
       Sfont_type_exists_p, (repv type), rep_Subr1) /*
::doc:sawfish.wm.fonts#font-type-exists-p::
font-type-exists-p TYPE

Returns true if fonts with the type described by the string TYPE can be
loaded.
::end:: */
{
    int i;

    rep_DECLARE1 (type, rep_STRINGP);

    for (i = 0; classes[i] != 0; i++)
    {
	if (strcasecmp (rep_STR (type), classes[i]->type) == 0)
	    return Qt;
    }

    return Qnil;
}

DEFUN("get-font-typed", Fget_font_typed, Sget_font_typed,
      (repv type, repv name), rep_Subr2) /*
::doc:sawfish.wm.fonts#get-font-typed::
get-font-typed TYPE NAME

Return the font object representing the font named NAME. NAME is
interpreted based on the value of the string TYPE.
::end:: */
{
    Lisp_Font *f;
    const Lisp_Font_Class *class;
    repv tem;
    int i;

    rep_DECLARE1(name, rep_STRINGP);
    rep_DECLARE2(type, rep_STRINGP);

    if (dpy == 0)
	return Qnil;

    for (f = font_list; f != NULL; f = f->next)
    {
	if (strcmp (rep_STR(name), rep_STR(f->name)) == 0
	    && strcmp (rep_STR (type), rep_STR (f->type)) == 0)
	{
	    return rep_VAL (f);
	}
    }

    class = 0;

    if (strcasecmp (rep_STR (type), "xlfd") == 0)
    {
	/* Boring old X core fonts */

	tem = global_symbol_value (Qfonts_are_fontsets);
	if (tem != Qnil)
	    class = &fontset_class;
	else
	    class = &fontstruct_class;
    }
    else
    {
	for (i = 0; classes[i] != 0; i++)
	{
	    if (strcasecmp (rep_STR (type), classes[i]->type) == 0)
	    {
		class = classes[i];
		break;
	    }
	}
    }

    if (class == 0)
    {
	DEFSTRING (err, "unknown font type");
	return Fsignal (Qerror, rep_list_2 (rep_VAL (&err), type));
    }
	
    f = rep_ALLOC_CELL(sizeof(Lisp_Font));

    f->car = font_type;
    f->class = class;
    f->type = type;
    f->name = name;
    f->plist = Qnil;

    if (!(*class->load) (f))
    {
	DEFSTRING (err, "unknown font");

	rep_FREE_CELL (f);
	return Fsignal (Qerror, rep_list_2 (rep_VAL (&err), name));
    }

    rep_data_after_gc += sizeof (Lisp_Font);

    f->next = font_list;
    font_list = f;

    return rep_VAL (f);
}

DEFUN("get-font", Fget_font, Sget_font, (repv name), rep_Subr1) /*
::doc:sawfish.wm.fonts#get-font::
get-font NAME

Return the font object representing the font named NAME (a standard X
font specifier string).
::end:: */
{
    DEFSTRING (type, "xlfd");

    return Fget_font_typed (rep_VAL (&type), name);
}

DEFUN("font-get", Ffont_get, Sfont_get, (repv win, repv prop), rep_Subr2) /*
::doc:sawfish.wm.fonts#font-get::
font-get FONT PROPERTY

Return the property PROPERTY (a symbol) associated with FONT.
::end:: */
{
    repv plist;
    rep_DECLARE1(win, FONTP);
    plist = VFONT(win)->plist;
    while (rep_CONSP(plist) && rep_CONSP(rep_CDR(plist)))
    {
	if (rep_CAR(plist) == prop
	    || (!rep_SYMBOLP(prop)
		&& rep_value_cmp (rep_CAR(plist), prop) == 0))
	{
	    return rep_CAR(rep_CDR(plist));
	}
	plist = rep_CDR(rep_CDR(plist));
    }
    return Qnil;
}

DEFUN("font-put", Ffont_put, Sfont_put, (repv win, repv prop, repv val), rep_Subr3) /*
::doc:sawfish.wm.fonts#font-put::
font-put FONT PROPERTY VALUE

Set the property PROPERTY (a symbol) associated with FONT to VALUE.
::end:: */
{
    repv plist;
    rep_DECLARE1(win, FONTP);
    plist = VFONT(win)->plist;
    while (rep_CONSP(plist) && rep_CONSP(rep_CDR(plist)))
    {
	if (rep_CAR(plist) == prop
	    || (!rep_SYMBOLP(prop)
		&& rep_value_cmp (rep_CAR(plist), prop) == 0))
	{
	    rep_CAR(rep_CDR(plist)) = val;
	    return val;
	}
	plist = rep_CDR(rep_CDR(plist));
    }
    plist = Fcons(prop, Fcons(val, VFONT(win)->plist));
    if (plist != rep_NULL)
	VFONT(win)->plist = plist;
    return val;
}

DEFUN("font-type", Ffont_type, Sfont_type, (repv font), rep_Subr1) /*
::doc:sawfish.wm.fonts#font-type::
font-type FONT

Return the type of the font represented by the font object FONT.
::end:: */
{
    rep_DECLARE1(font, FONTP);
    return VFONT(font)->type;
}

DEFUN("font-name", Ffont_name, Sfont_name, (repv font), rep_Subr1) /*
::doc:sawfish.wm.fonts#font-name::
font-name FONT

Return the name of the font represented by the font object FONT.
::end:: */
{
    rep_DECLARE1(font, FONTP);
    return VFONT(font)->name;
}

DEFUN("fontp", Ffontp, Sfontp, (repv win), rep_Subr1) /*
::doc:sawfish.wm.fonts#fontp::
fontp ARG

Return t if ARG is a font object.
::end:: */
{
    return FONTP(win) ? Qt : Qnil;
}

int
x_text_width (repv font, char *string, size_t len)
{
    return (*VFONT (font)->class->measure) (VFONT (font), string, len);
}

/* The foreground pixel of GC is undefined after this function returns. */
void
x_draw_string (Window id, repv font, GC gc, Lisp_Color *fg_color,
	       int x, int y, char *string, size_t len)
{
    (*VFONT (font)->class->draw) (VFONT (font), string, len,
				  id, gc, fg_color, x, y);
}

DEFUN("text-width", Ftext_width, Stext_width, (repv string, repv font), rep_Subr2) /*
::doc:sawfish.wm.fonts#text-width::
text-width STRING [FONT]

Return the number of horizontal pixels that would be required to display
the text STRING using font object FONT (or the default-font).
::end:: */
{
    rep_DECLARE1(string, rep_STRINGP);
    if (font == Qnil)
	font = global_symbol_value (Qdefault_font);
    rep_DECLARE2(font, FONTP);
    return rep_MAKE_INT (x_text_width (font, rep_STR(string),
				       rep_STRING_LEN(string)));
}

DEFUN("font-height", Ffont_height, Sfont_height, (repv font), rep_Subr1) /*
::doc:sawfish.wm.fonts#font-height::
font-height [FONT]

Return the bounding height of glyphs rendered using FONT (or the
default-font).
::end:: */
{
    if (font == Qnil)
	font = global_symbol_value (Qdefault_font);
    rep_DECLARE1(font, FONTP);
    return rep_MAKE_INT(VFONT(font)->ascent + VFONT(font)->descent);
}

DEFUN("font-ascent", Ffont_ascent, Sfont_ascent, (repv font), rep_Subr1) /*
::doc:sawfish.wm.fonts#font-ascent::
font-ascent [FONT]

Return the ascent of glyphs rendered using FONT (or the
default-font).
::end:: */
{
    if (font == Qnil)
	font = global_symbol_value (Qdefault_font);
    rep_DECLARE1(font, FONTP);
    return rep_MAKE_INT(VFONT(font)->ascent);
}

DEFUN("font-descent", Ffont_descent, Sfont_descent, (repv font), rep_Subr1) /*
::doc:sawfish.wm.fonts#font-descent::
font-descent [FONT]

Return the descent of glyphs rendered using FONT (or the
default-font).
::end:: */
{
    if (font == Qnil)
	font = global_symbol_value (Qdefault_font);
    rep_DECLARE1(font, FONTP);
    return rep_MAKE_INT(VFONT(font)->descent);
}

/* type hooks */

static int
font_cmp (repv w1, repv w2)
{
    return w1 != w2;
}

static void
font_prin (repv stream, repv obj)
{
    char buf[256];
    sprintf (buf, "#<font %s:%s>",
	     rep_STR(VFONT(obj)->type), rep_STR(VFONT(obj)->name));
    rep_stream_puts (stream, buf, -1, FALSE);
}

static void
font_mark (repv obj)
{
    rep_MARKVAL(VFONT(obj)->type);
    rep_MARKVAL(VFONT(obj)->name);
    rep_MARKVAL(VFONT(obj)->plist);
}

static void
font_sweep (void)
{
    Lisp_Font *w = font_list;
    font_list = 0;
    while (w != 0)
    {
	Lisp_Font *next = w->next;

	if (!rep_GC_CELL_MARKEDP(rep_VAL(w)))
	{
	    (*w->class->finalize) (w);
	    rep_FREE_CELL(w);
	}
	else
	{
	    rep_GC_CLR_CELL(rep_VAL(w));
	    w->next = font_list;
	    font_list = w;
	}
	w = next;
    }
}

static int
use_xft (void)
{
    char *val = getenv ("GDK_USE_XFT");

    return val == NULL || strcmp (val, "0") != 0;
}

/* initialisation */

void
fonts_init (void)
{
    repv tem = rep_push_structure ("sawfish.wm.fonts");
    font_type = rep_register_new_type ("font", font_cmp, font_prin, font_prin,
				       font_sweep, font_mark,
				       0, 0, 0, 0, 0, 0, 0);

    rep_ADD_SUBR(Sfont_type_exists_p);
    rep_ADD_SUBR(Sget_font_typed);
    rep_ADD_SUBR(Sget_font);
    rep_ADD_SUBR(Sfont_get);
    rep_ADD_SUBR(Sfont_put);
    rep_ADD_SUBR(Sfont_type);
    rep_ADD_SUBR(Sfont_name);
    rep_ADD_SUBR(Sfontp);
    rep_ADD_SUBR(Stext_width);
    rep_ADD_SUBR(Sfont_height);
    rep_ADD_SUBR(Sfont_ascent);
    rep_ADD_SUBR(Sfont_descent);

    rep_INTERN_SPECIAL(default_font);
    if (!batch_mode_p ())
    {
	DEFSTRING (xlfd_type, "xlfd");
	DEFSTRING (xlfd_name, "fixed");
	DEFSTRING (xft_type, "Xft");
	DEFSTRING (xft_name, "Sans");
	DEFSTRING (pango_type, "Pango");
	DEFSTRING (pango_name, "Sans");

	repv font = Qnil;

	if (use_xft ())
	{
#ifdef HAVE_PANGO
	    font = Fget_font_typed (rep_VAL (&pango_type),
				    rep_VAL (&pango_name));
	    if (font == rep_NULL || !FONTP (font))
#endif
	    {
		font = Fget_font_typed (rep_VAL (&xft_type),
					rep_VAL (&xft_name));
	    }
	}

	if (font == rep_NULL || !FONTP (font))
	{
	    font = Fget_font_typed (rep_VAL (&xlfd_type),
				    rep_VAL (&xlfd_name));
	}

	if (font == rep_NULL || !FONTP(font))
	{
	    fputs ("can't load default font during initialisation", stderr);
	    rep_throw_value = rep_NULL;
	    font = Qnil;
	}

	Fset (Qdefault_font, font);
    }
    rep_pop_structure (tem);
}

void
fonts_kill (void)
{
}
