//
// "$Id: fl_font_xft.cxx 4288 2005-04-16 00:13:17Z mike $"
//
// Xft font code for the Fast Light Tool Kit (FLTK).
//
// Copyright 2001-2005 Bill Spitzak and others.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library 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
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
// USA.
//
// Please report all bugs and problems on the following page:
//
//     http://www.fltk.org/str.php
//

//
// Draw fonts using Keith Packard's Xft library to provide anti-
// aliased text. Yow!
//
// Many thanks to Carl for making the original version of this.
//
// This font code only requires libXft to work.  Contrary to popular
// belief there is no need to have FreeType, or the Xrender extension
// available to use this code.  You will just get normal Xlib fonts
// (Xft calls them "core" fonts) The Xft algorithms for choosing
// these is about as good as the FLTK ones (I hope to fix it so it is
// exactly as good...), plus it can cache it's results and share them
// between programs, so using this should be a win in all cases. Also
// it should be obvious by comparing this file and fl_font_x.cxx that
// it is a lot easier to program with Xft than with Xlib.
//
// Also, Xft supports UTF-8 text rendering directly, which will allow
// us to support UTF-8 on all platforms more easily.
//
// To actually get antialiasing you need the following:
//
//     1. You have XFree86 4
//     2. You have the XRender extension
//     3. Your X device driver supports the render extension
//     4. You have libXft
//     5. Your libXft has FreeType2 support compiled in
//     6. You have the FreeType2 library
//
// Distributions that have XFree86 4.0.3 or later should have all of this...
//
// Unlike some other Xft packages, I tried to keep this simple and not
// to work around the current problems in Xft by making the "patterns"
// complicated. I belive doing this defeats our ability to improve Xft
// itself. You should edit the ~/.xftconfig file to "fix" things, there
// are several web pages of information on how to do this.
//

#include <X11/Xft/Xft.h>

// The predefined fonts that FLTK has:
static Fl_Fontdesc built_in_table[] = {
{" sans"},
{"Bsans"},
{"Isans"},
{"Psans"},
{" mono"},
{"Bmono"},
{"Imono"},
{"Pmono"},
{" serif"},
{"Bserif"},
{"Iserif"},
{"Pserif"},
{" symbol"},
{" screen"},
{"Bscreen"},
{" dingbats"},
};

Fl_Fontdesc* fl_fonts = built_in_table;

#define current_font (fl_fontsize->font)

int fl_font_ = 0;
int fl_size_ = 0;
XFontStruct* fl_xfont = 0;
const char* fl_encoding_ = "iso8859-1";
Fl_FontSize* fl_fontsize = 0;

void fl_font(int fnum, int size) {
  if (fnum == fl_font_ && size == fl_size_ &&
      !strcasecmp(fl_fontsize->encoding, fl_encoding_))
    return;
  fl_font_ = fnum; fl_size_ = size;
  Fl_Fontdesc *font = fl_fonts + fnum;
  Fl_FontSize* f;
  // search the fontsizes we have generated already
  for (f = font->first; f; f = f->next) {
    if (f->size == size && !strcasecmp(f->encoding, fl_encoding_))
      break;
  }
  if (!f) {
    f = new Fl_FontSize(font->name);
    f->next = font->first;
    font->first = f;
  }
  fl_fontsize = f;
#if XFT_MAJOR < 2
  fl_xfont    = f->font->u.core.font;
#endif // XFT_MAJOR < 2
}

static XftFont* fontopen(const char* name, bool core) {
  fl_open_display();
  int slant = XFT_SLANT_ROMAN;
  int weight = XFT_WEIGHT_MEDIUM;
  // may be efficient, but this is non-obvious
  switch (*name++) {
  case 'I': slant = XFT_SLANT_ITALIC; break;
  case 'P': slant = XFT_SLANT_ITALIC;
  case 'B': weight = XFT_WEIGHT_BOLD; break;
  case ' ': break;
  default: name--;
  }
  // this call is extremely slow...
  return XftFontOpen(fl_display, fl_screen,
		     XFT_FAMILY, XftTypeString, name,
		     XFT_WEIGHT, XftTypeInteger, weight,
		     XFT_SLANT, XftTypeInteger, slant,
		     XFT_ENCODING, XftTypeString, fl_encoding_,
		     XFT_PIXEL_SIZE, XftTypeDouble, (double)fl_size_,
		     core ? XFT_CORE : 0, XftTypeBool, true,
		     XFT_RENDER, XftTypeBool, false,
		     0);
}

Fl_FontSize::Fl_FontSize(const char* name) {
  encoding = fl_encoding_;
  size = fl_size_;
#if HAVE_GL
  listbase = 0;
#endif // HAVE_GL
  font = fontopen(name, false);
}

Fl_FontSize::~Fl_FontSize() {
  if (this == fl_fontsize) fl_fontsize = 0;
//  XftFontClose(fl_display, font);
}

int fl_height() {
  if (current_font) return current_font->ascent + current_font->descent;
  else return -1;
}

int fl_descent() {
  if (current_font) return current_font->descent;
  else return -1;
}

double fl_width(const char *str, int n) {
  if (!current_font) return -1.0;
  XGlyphInfo i;
  XftTextExtents8(fl_display, current_font, (XftChar8 *)str, n, &i);
  return i.xOff;
}

double fl_width(uchar c) {
  return fl_width((const char *)(&c), 1);
}

#if HAVE_GL
// This call is used by opengl to get a bitmapped font.
XFontStruct* fl_xxfont() {
#  if XFT_MAJOR > 1
  // kludge!
  static XFontStruct* fixed = 0;
  if (!fixed) fixed = XLoadQueryFont(fl_display, "fixed");
  return fixed;
#  else
  if (current_font->core) return current_font->u.core.font;
  static XftFont* xftfont;
  if (xftfont) XftFontClose (fl_display, xftfont);
  xftfont = fontopen(fl_fonts[fl_font_].name, true);
  return xftfont->u.core.font;
#  endif // XFT_MAJOR > 1
}
#endif // HAVE_GL

#if USE_OVERLAY
// Currently Xft does not work with colormapped visuals, so this probably
// does not work unless you have a true-color overlay.
extern bool fl_overlay;
extern Colormap fl_overlay_colormap;
extern XVisualInfo* fl_overlay_visual;
#endif

// For some reason Xft produces errors if you destroy a window whose id
// still exists in an XftDraw structure. It would be nice if this is not
// true, a lot of junk is needed to try to stop this:

static XftDraw* draw;
static Window draw_window;
#if USE_OVERLAY
static XftDraw* draw_overlay;
static Window draw_overlay_window;
#endif

void fl_destroy_xft_draw(Window id) {
  if (id == draw_window)
    XftDrawChange(draw, draw_window = fl_message_window);
#if USE_OVERLAY
  if (id == draw_overlay_window)
    XftDrawChange(draw_overlay, draw_overlay_window = fl_message_window);
#endif
}

void fl_draw(const char *str, int n, int x, int y) {
#if USE_OVERLAY
  XftDraw*& draw = fl_overlay ? draw_overlay : ::draw;
  if (fl_overlay) {
    if (!draw) 
      draw = XftDrawCreate(fl_display, draw_overlay_window = fl_window,
			   fl_overlay_visual->visual, fl_overlay_colormap);
    else //if (draw_overlay_window != fl_window)
      XftDrawChange(draw, draw_overlay_window = fl_window);
  } else
#endif
  if (!draw)
    draw = XftDrawCreate(fl_display, draw_window = fl_window,
			 fl_visual->visual, fl_colormap);
  else //if (draw_window != fl_window)
    XftDrawChange(draw, draw_window = fl_window);

  Region region = fl_clip_region();
  if (region && XEmptyRegion(region)) return;
  XftDrawSetClip(draw, region);

  // Use fltk's color allocator, copy the results to match what
  // XftCollorAllocValue returns:
  XftColor color;
  color.pixel = fl_xpixel(fl_color_);
  uchar r,g,b; Fl::get_color(fl_color_, r,g,b);
  color.color.red   = ((int)r)*0x101;
  color.color.green = ((int)g)*0x101;
  color.color.blue  = ((int)b)*0x101;
  color.color.alpha = 0xffff;

  XftDrawString8(draw, &color, current_font, x, y, (XftChar8 *)str, n);
}

void fl_draw(const char* str, int n, float x, float y) {
  fl_draw(str, n, (int)x, (int)y);
}

//
// End of "$Id: fl_font_xft.cxx 4288 2005-04-16 00:13:17Z mike $"
//
