view src/xfaces.c @ 2336:7aaafd275bec

Initial revision
author Jim Blandy <jimb@redhat.com>
date Tue, 23 Mar 1993 08:06:31 +0000
parents
children f881f2936eec
line wrap: on
line source

/* "Face" primitives
This file is part of GNU Emacs.

GNU Emacs 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 1, or (at your option)
any later version.

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

#include <sys/types.h>
#include <sys/stat.h>

#include "config.h"
#include "lisp.h"

#include "xterm.h"
#include "buffer.h"
#include "extents.h"
#include "screen.h"
#include "window.h"
#include "indent.h"

/* Display Context for the icons */ 
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/Xmu/Drawing.h>
#include <X11/Xos.h>

/* A table of display faces.  */
struct face **face_vector;
/* The length in use of the table.  */
int nfaces;
/* The allocated length of the table.   */
int nfaces_allocated;

/* The number of face-id's in use (same for all frames).  */
int next_face_id;

static struct face *allocate_face ();
static void build_face ();

/* Make a new face that's a copy of an existing one.  */

static struct face *
copy_face (face)
     struct face *face;
{
  struct face *result = allocate_face ();

  result->font = face->font;
  result->foreground = face->foreground;
  result->background = face->background;
  result->back_pixmap = face->back_pixmap;
  result->underline = face->underline;

  return result;
}

static int
face_eql (face1, face2)
     struct face *face1, *face2;
{
  return (face1->font == face2->font
	  && face1->foreground == face2->foreground
	  && face1->background == face2->background
	  && face1->back_pixmap == face2->back_pixmap
	  && face1->underline == face2->underline);
}

/* Return the unique display face corresponding to the user-level face FACE.

   If there isn't one, make one, and find a slot in the face_vector to
   put it in.  */

static struct face *
get_vector_face (f, face)
     struct frame *f;
     struct face *face;
{
  int i, empty = -1;

  /* Look for an existing display face that does the job.
     Also find an empty slot if any.   */
  for (i = 0; i < nfaces; i++)
    {
      if (face_eql (face_vector[i], face))
	return face_vector[i];
      if (face_vector[i] == 0)
	empty = i;
    }

  /* If no empty slots, make one.  */
  if (empty < 0 && nfaces == nfaces_allocated)
    {
      int newsize = nfaces + 20;
      face_vector
	= (struct face **) xrealloc (face_vector,
				     newsize * sizeof (struct face *));
      nfaces_allocated = newsize;
    }

  if (empty < 0)
    empty = nfaces++;

  /* Put a new display face in the empty slot.  */
  result = copy_face (face);
  face_vector[empty] = result;
  
  /* Make a graphics context for it.  */
  build_face (f, result);

  return result;
}

/* Clear out face_vector and start anew.
   This should be done from time to time just to avoid
   keeping too many graphics contexts in face_vector
   that are no longer needed.  */

void
clear_face_vector ()
{
  Lisp_Object rest;
  Display *dpy = x_current_display;

  BLOCK_INPUT;
  /* Free the display faces in the face_vector.  */
  for (i = 0; i < nfaces; i++)
    {
      struct face *face = face_vector[i];
      if (face->facegc)
	XFreeGC (dpy, face->facegc);
      xfree (face);
    }
  nfaces = 0;

  UNBLOCK_INPUT;
}

/* Make a graphics context for face FACE, which is on frame F.  */

static void
build_face (f, face)
     struct frame* f;
     struct face* face;
{
  GC gc;
  XGCValues xgcv;
  unsigned long mask;

  xgcv.foreground = face->foreground;
  xgcv.background = face->background;
  xgcv.font = face->font->fid;
  xgcv.graphics_exposures = 0;
  mask = GCForeground | GCBackground | GCFont | GCGraphicsExposures;
  gc = XCreateGC (x_current_display, FRAME_X_WINDOW (f),
		  mask, &xgcv);
#if 0
  if (face->back_pixmap && face->back_pixmap != FACE_DEFAULT)
    XSetStipple (XtDisplay (f->display.x->widget), gc, face->back_pixmap);
#endif
  face->facegc = gc;
}

/* Given a frame face, return an equivalent display face
   (one which has a graphics context).  */

static struct face *
get_display_face (f, face)
     struct frame *f;
     struct face *face;
{
  struct face *result;

  /* Does the face have a GC already?  */
  if (face->facegc)
    return face;
  
  /* If it's equivalent to the normal face, use that.  */
  if (face->font == FRAME_NORMAL_FACE (f)->font
      && face->foreground == FRAME_NORMAL_FACE (f)->foreground
      && face->background == FRAME_NORMAL_FACE (f)->background
      && face->back_pixmap == FRAME_NORMAL_FACE (f)->back_pixmap
      && face->underline == FRAME_NORMAL_FACE (f)->underline)
    {
      if (!FRAME_NORMAL_FACE (f)->framegc)
	build_frame (f, FRAME_NORMAL_FACE (f));
      return FRAME_NORMAL_FACE (f);
    }

  /* If it's equivalent to the mode line face, use that.  */
  if (face->font == FRAME_MODELINE_FACE (f)->font
      && face->foreground == FRAME_MODELINE_FACE (f)->foreground
      && face->background == FRAME_MODELINE_FACE (f)->background
      && face->back_pixmap == FRAME_MODELINE_FACE (f)->back_pixmap
      && face->underline == FRAME_MODELINE_FACE (f)->underline)
    {
      if (!FRAME_MODELINE_FACE (f)->framegc)
	build_frame (f, FRAME_MODELINE_FACE (f));
      return FRAME_MODELINE_FACE (f);
    }

  /* Get a specialized display face.  */
  return get_cached_face (f, face);
}


/* Allocate a new face */
static struct face *
allocate_face ()
{
  struct face *result = (struct face *) xmalloc (sizeof (struct face));
  bzero (result, sizeof (struct face));
  return result;
}

/* Make face id ID valid on frame F.  */

void
ensure_face_ready (f, id)
     struct frame *f;
     int id;
{
  if (f->n_faces <= id)
    {
      int n = id + 10;
      int i;
      if (!f->n_faces)
	f->faces = (struct face **) xmalloc (sizeof (struct face *) * n);
      else
	f->faces
	  = (struct face **) xrealloc (f->faces, sizeof (struct face *) * n);

      f->n_faces = n;
    }

  f->faces[id] = allocate_face ();
}

/* Allocating, freeing, and duplicating fonts, colors, and pixmaps.  */

#ifdef HAVE_X_WINDOWS

static XFontStruct *
load_font (f, name)
     struct frame *f;
     Lisp_Object name;
{
  XFontStruct *font;

  CHECK_STRING (name, 0);
  BLOCK_INPUT;
  font = XLoadQueryFont (x_current_display, (char *) XSTRING (name)->data);
  UNBLOCK_INPUT;

  if (! font)
    Fsignal (Qerror, Fcons (build_string ("undefined font"),
			    Fcons (name, Qnil)));
  return font;
}

static void
unload_font (f, font)
     struct frame *f;
     XFontStruct *font;
{
  if (!font || font == ((XFontStruct *) FACE_DEFAULT))
    return;
  XFreeFont (x_current_display, font);
}

static unsigned long
load_color (f, name)
     struct frame *f;
     Lisp_Object name;
{
  Display *dpy = x_current_display;
  Colormap cmap;
  XColor color;
  int result;

  cmap = DefaultColormapOfScreen (x_screen);

  CHECK_STRING (name, 0);
  BLOCK_INPUT;
  result = XParseColor (dpy, cmap, (char *) XSTRING (name)->data, &color);
  UNBLOCK_INPUT;
  if (! result)
    Fsignal (Qerror, Fcons (build_string ("undefined color"),
			    Fcons (name, Qnil)));
  BLOCK_INPUT;
  result = XAllocColor (dpy, cmap, &color);
  UNBLOCK_INPUT;
  if (! result)
    Fsignal (Qerror, Fcons (build_string ("X server cannot allocate color"),
			    Fcons (name, Qnil)));
  return (unsigned long) color.pixel;
}

static void
unload_color (f, pixel)
     struct frame *f;
     Pixel pixel;
{
  Colormap cmap;
  Display *dpy = x_current_display;
  if (pixel == FACE_DEFAULT)
    return;
  cmap = DefaultColormapOfScreen (x_screen);
  BLOCK_INPUT;
  XFreeColors (dpy, cmap, &pixel, 1, 0);
  UNBLOCK_INPUT;
}

#endif /* HAVE_X_WINDOWS */


/* frames */

void
init_frame_faces (f)
     struct frame f;
{
  struct frame *other_frame = 0;
  Lisp_Object rest;

  for (rest = Vframe_list; !NILP (rest); rest = Fcdr (rest))
    {
      struct frame *f2 = XFRAME (Fcar (rest));
      if (f2 != f && FRAME_IS_X (f2))
	{
	  other_frame = f2;
	  break;
	}
    }

  if (other_frame)
    {
      /* Make sure this frame's face vector is as big as the others.  */
      f->n_faces = other_frame->n_faces;
      f->faces = (struct face **) xmalloc (f->n_faces * sizeof (struct face *));

      /* Make sure the frame has the two basic faces.  */
      FRAME_NORMAL_FACE (f)
	= copy_face (FRAME_NORMAL_FACE (other_frame));
      FRAME_MODELINE_FACE (f)
	= copy_face (FRAME_MODELINE_FACE (other_frame));
    }
}


/* Called from Fdelete_frame?  */

void
free_screen_faces (f)
     struct frame *f;
{
  Display *dpy = x_current_display;
  int i;

  for (i = 0; i < f->n_faces; i++)
    {
      struct face *face = f->faces [i];
      if (! face)
        continue;
      if (face->facegc)
	XFreeGC (dpy, face->facegc);
      unload_font (f, face->font);
      unload_color (f, face->foreground);
      unload_color (f, face->background);
      unload_pixmap (f, face->back_pixmap);
      xfree (face);
    }
  xfree (f->faces);
  f->faces = 0;
  f->n_faces = 0;
}


/* Lisp interface */

DEFUN ("frame-face-alist", Fframe_face_alist, Sframe_face_alist, 1, 1, 0,
       "")
     (frame)
     Lisp_Object frame;
{
  CHECK_FRAME (frame, 0);
  return XFRAME (frame)->face_alist;
}

DEFUN ("set-frame-face-alist", Fset_frame_face_alist, Sset_frame_face_alist,
       2, 2, 0, "")
     (frame, value)
     Lisp_Object frame, value;
{
  CHECK_FRAME (frame, 0);
  XFRAME (frame)->face_alist = value;
  return value;
}


DEFUN ("make-face-internal", Fmake_face_internal, Smake_face_internal, 1, 1, 0,
  "Create face number FACE-ID on all frames.")
  (face_id)
     Lisp_Object face_id;
{
  Lisp_Object rest;
  int id = XINT (face_id);

  CHECK_FIXNUM (face_id, 0);
  if (id < 0)
    error ("Face id must be nonnegative");

  for (rest = Vframe_list; !NILP (rest); rest = XCONS (rest)->cdr)
    {
      struct frame *f = XFRAME (XCONS (rest)->car);
      ensure_face_ready (f, id);
    }
  return Qnil;
}


DEFUN ("set-face-attribute-internal", Fset_face_attribute_internal,
       Sset_face_attribute_internal, 4, 4, 0, "")
     (face_id, attr_name, attr_value, frame)
     Lisp_Object face_id, attr_name, attr_value, frame;
{
  struct face *face;
  struct frame *f;
  int magic_p;
  int id;

  CHECK_FRAME (frame, 0);
  CHECK_FIXNUM (face_id, 0);
  CHECK_SYMBOL (attr_name, 0);

  f = XFRAME (frame);
  id = XINT (face_id);
  if (id < 0)
    Fsignal (Qerror, Fcons (build_string ("invalid face id"),
			    Fcons (face_id, Fcons (frame, Qnil))));

  ensure_face_ready (f, id);
  face = f->faces [XFASTINT (face_id)];
  if (! face) abort ();

  magic_p = (NILP (attr_value) && XFASTINT (face_id) <= 1);

  if (EQ (attr_name, intern ("font")))
    {
#ifdef HAVE_X_WINDOWS
      XFontStruct *font;
      if (magic_p)
	error ("font of the `normal' or `modeline' face may not be nil");
      font = load_font (f, attr_value);
      unload_font (f, face->font);
      face->font = font;
#endif /* HAVE_X_WINDOWS */
    }
  else if (EQ (attr_name, intern ("foreground")))
    {
#ifdef HAVE_X_WINDOWS
      unsigned long new_color;
      if (magic_p)
	error ("forground color of the `normal' or `modeline' face may not be nil");
      new_color = load_color (f, attr_value);
      unload_color (f, face->foreground);
      face->foreground = new_color;
#endif /* HAVE_X_WINDOWS */
    }
  else if (EQ (attr_name, intern ("background")))
    {
#ifdef HAVE_X_WINDOWS
      unsigned long new_color;
      if (magic_p)
	error ("background color of the `normal' or `modeline' face may not be nil");
      new_color = load_color (f, attr_value);
      unload_color (f, face->background);
      face->background = new_color;
#endif /* HAVE_X_WINDOWS */
    }
#if 0
  else if (EQ (attr_name, intern ("background-pixmap")))
    {
#ifdef HAVE_X_WINDOWS
      unsigned int w, h, d;
      unsigned long new_pixmap = load_pixmap (f, attr_value, &w, &h, &d, 0);
      unload_pixmap (f, face->back_pixmap);
      if (magic_p) new_pixmap = 0;
      face->back_pixmap = new_pixmap;
      face->pixmap_w = w;
      face->pixmap_h = h;
/*      face->pixmap_depth = d; */
#endif /* HAVE_X_WINDOWS */
    }
#endif /* 0 */
  else if (EQ (attr_name, intern ("underline")))
    {
      int new = !NILP (attr_value);
      face->underline = new;
    }
  else
    error ("unknown face attribute");

  if (id == 0)
    {
      BLOCK_INPUT;
      XFreeGC (dpy, FRAME_NORMAL_FACE (f)->facegc);
      build_face (f, FRAME_NORMAL_FACE (f));
      UNBLOCK_INPUT;
    }

  if (id == 1)
    {
      BLOCK_INPUT;
      XFreeGC (dpy, FRAME_NORMAL_FACE (f)->facegc);
      build_face (f, FRAME_NORMAL_FACE (f));
      UNBLOCK_INPUT;
    }

  return Qnil;
}

DEFUN ("internal-next-face-id", Finternal_next_face_id, Sinternal_next_face_id,
  0, 0, 0, "")
  ()
{
  return make_number (next_face_id++);
}

void
syms_of_faces ()
{
  defsubr (&Sframe_face_alist);
  defsubr (&Sset_frame_face_alist);
  defsubr (&Smake_face_internal);
  defsubr (&Sset_face_attribute_internal);
  defsubr (&Sinternal_next_face_id);
}