+#define NUMVAL(X) \
+ ((INTEGERP (X) || FLOATP (X)) \
+ ? XFLOATINT (X) \
+ : - 1)
+
+int
+calc_pixel_width_or_height (res, it, prop, font, width_p, align_to)
+ double *res;
+ struct it *it;
+ Lisp_Object prop;
+ void *font;
+ int width_p, *align_to;
+{
+ double pixels;
+
+#define OK_PIXELS(val) ((*res = (double)(val)), 1)
+#define OK_ALIGN_TO(val) ((*align_to = (int)(val)), 1)
+
+ if (NILP (prop))
+ return OK_PIXELS (0);
+
+ if (SYMBOLP (prop))
+ {
+ if (SCHARS (SYMBOL_NAME (prop)) == 2)
+ {
+ char *unit = SDATA (SYMBOL_NAME (prop));
+
+ if (unit[0] == 'i' && unit[1] == 'n')
+ pixels = 1.0;
+ else if (unit[0] == 'm' && unit[1] == 'm')
+ pixels = 25.4;
+ else if (unit[0] == 'c' && unit[1] == 'm')
+ pixels = 2.54;
+ else
+ pixels = 0;
+ if (pixels > 0)
+ {
+ double ppi;
+ if ((ppi = NUMVAL (Vdisplay_pixels_per_inch), ppi > 0)
+ || (CONSP (Vdisplay_pixels_per_inch)
+ && (ppi = (width_p
+ ? NUMVAL (XCAR (Vdisplay_pixels_per_inch))
+ : NUMVAL (XCDR (Vdisplay_pixels_per_inch))),
+ ppi > 0)))
+ return OK_PIXELS (ppi / pixels);
+
+ return 0;
+ }
+ }
+
+#ifdef HAVE_WINDOW_SYSTEM
+ if (EQ (prop, Qheight))
+ return OK_PIXELS (font ? FONT_HEIGHT ((XFontStruct *)font) : FRAME_LINE_HEIGHT (it->f));
+ if (EQ (prop, Qwidth))
+ return OK_PIXELS (font ? FONT_WIDTH ((XFontStruct *)font) : FRAME_COLUMN_WIDTH (it->f));
+#else
+ if (EQ (prop, Qheight) || EQ (prop, Qwidth))
+ return OK_PIXELS (1);
+#endif
+
+ if (EQ (prop, Qtext))
+ return OK_PIXELS (width_p
+ ? window_box_width (it->w, TEXT_AREA)
+ : WINDOW_BOX_HEIGHT_NO_MODE_LINE (it->w));
+
+ if (align_to && *align_to < 0)
+ {
+ *res = 0;
+ if (EQ (prop, Qleft))
+ return OK_ALIGN_TO (window_box_left_offset (it->w, TEXT_AREA));
+ if (EQ (prop, Qright))
+ return OK_ALIGN_TO (window_box_right_offset (it->w, TEXT_AREA));
+ if (EQ (prop, Qcenter))
+ return OK_ALIGN_TO (window_box_left_offset (it->w, TEXT_AREA)
+ + window_box_width (it->w, TEXT_AREA) / 2);
+ if (EQ (prop, Qleft_fringe))
+ return OK_ALIGN_TO (WINDOW_HAS_FRINGES_OUTSIDE_MARGINS (it->w)
+ ? WINDOW_LEFT_SCROLL_BAR_AREA_WIDTH (it->w)
+ : window_box_right_offset (it->w, LEFT_MARGIN_AREA));
+ if (EQ (prop, Qright_fringe))
+ return OK_ALIGN_TO (WINDOW_HAS_FRINGES_OUTSIDE_MARGINS (it->w)
+ ? window_box_right_offset (it->w, RIGHT_MARGIN_AREA)
+ : window_box_right_offset (it->w, TEXT_AREA));
+ if (EQ (prop, Qleft_margin))
+ return OK_ALIGN_TO (window_box_left_offset (it->w, LEFT_MARGIN_AREA));
+ if (EQ (prop, Qright_margin))
+ return OK_ALIGN_TO (window_box_left_offset (it->w, RIGHT_MARGIN_AREA));
+ if (EQ (prop, Qscroll_bar))
+ return OK_ALIGN_TO (WINDOW_HAS_VERTICAL_SCROLL_BAR_ON_LEFT (it->w)
+ ? 0
+ : (window_box_right_offset (it->w, RIGHT_MARGIN_AREA)
+ + (WINDOW_HAS_FRINGES_OUTSIDE_MARGINS (it->w)
+ ? WINDOW_RIGHT_FRINGE_WIDTH (it->w)
+ : 0)));
+ }
+ else
+ {
+ if (EQ (prop, Qleft_fringe))
+ return OK_PIXELS (WINDOW_LEFT_FRINGE_WIDTH (it->w));
+ if (EQ (prop, Qright_fringe))
+ return OK_PIXELS (WINDOW_RIGHT_FRINGE_WIDTH (it->w));
+ if (EQ (prop, Qleft_margin))
+ return OK_PIXELS (WINDOW_LEFT_MARGIN_WIDTH (it->w));
+ if (EQ (prop, Qright_margin))
+ return OK_PIXELS (WINDOW_RIGHT_MARGIN_WIDTH (it->w));
+ if (EQ (prop, Qscroll_bar))
+ return OK_PIXELS (WINDOW_SCROLL_BAR_AREA_WIDTH (it->w));
+ }
+
+ prop = Fbuffer_local_value (prop, it->w->buffer);
+ }
+
+ if (INTEGERP (prop) || FLOATP (prop))
+ {
+ int base_unit = (width_p
+ ? FRAME_COLUMN_WIDTH (it->f)
+ : FRAME_LINE_HEIGHT (it->f));
+ return OK_PIXELS (XFLOATINT (prop) * base_unit);
+ }
+
+ if (CONSP (prop))
+ {
+ Lisp_Object car = XCAR (prop);
+ Lisp_Object cdr = XCDR (prop);
+
+ if (SYMBOLP (car))
+ {
+#ifdef HAVE_WINDOW_SYSTEM
+ if (valid_image_p (prop))
+ {
+ int id = lookup_image (it->f, prop);
+ struct image *img = IMAGE_FROM_ID (it->f, id);
+
+ return OK_PIXELS (width_p ? img->width : img->height);
+ }
+#endif
+ if (EQ (car, Qplus) || EQ (car, Qminus))
+ {
+ int first = 1;
+ double px;
+
+ pixels = 0;
+ while (CONSP (cdr))
+ {
+ if (!calc_pixel_width_or_height (&px, it, XCAR (cdr),
+ font, width_p, align_to))
+ return 0;
+ if (first)
+ pixels = (EQ (car, Qplus) ? px : -px), first = 0;
+ else
+ pixels += px;
+ cdr = XCDR (cdr);
+ }
+ if (EQ (car, Qminus))
+ pixels = -pixels;
+ return OK_PIXELS (pixels);
+ }
+
+ car = Fbuffer_local_value (car, it->w->buffer);
+ }
+
+ if (INTEGERP (car) || FLOATP (car))
+ {
+ double fact;
+ pixels = XFLOATINT (car);
+ if (NILP (cdr))
+ return OK_PIXELS (pixels);
+ if (calc_pixel_width_or_height (&fact, it, cdr,
+ font, width_p, align_to))
+ return OK_PIXELS (pixels * fact);
+ return 0;
+ }
+
+ return 0;
+ }
+
+ return 0;
+}
+
+\f
+/***********************************************************************
+ Glyph Display
+ ***********************************************************************/
+
+#ifdef HAVE_WINDOW_SYSTEM
+
+#if GLYPH_DEBUG
+
+void
+dump_glyph_string (s)
+ struct glyph_string *s;
+{
+ fprintf (stderr, "glyph string\n");
+ fprintf (stderr, " x, y, w, h = %d, %d, %d, %d\n",
+ s->x, s->y, s->width, s->height);
+ fprintf (stderr, " ybase = %d\n", s->ybase);
+ fprintf (stderr, " hl = %d\n", s->hl);
+ fprintf (stderr, " left overhang = %d, right = %d\n",
+ s->left_overhang, s->right_overhang);
+ fprintf (stderr, " nchars = %d\n", s->nchars);
+ fprintf (stderr, " extends to end of line = %d\n",
+ s->extends_to_end_of_line_p);
+ fprintf (stderr, " font height = %d\n", FONT_HEIGHT (s->font));
+ fprintf (stderr, " bg width = %d\n", s->background_width);
+}
+
+#endif /* GLYPH_DEBUG */
+
+/* Initialize glyph string S. CHAR2B is a suitably allocated vector
+ of XChar2b structures for S; it can't be allocated in
+ init_glyph_string because it must be allocated via `alloca'. W
+ is the window on which S is drawn. ROW and AREA are the glyph row
+ and area within the row from which S is constructed. START is the
+ index of the first glyph structure covered by S. HL is a
+ face-override for drawing S. */
+
+#ifdef HAVE_NTGUI
+#define OPTIONAL_HDC(hdc) hdc,
+#define DECLARE_HDC(hdc) HDC hdc;
+#define ALLOCATE_HDC(hdc, f) hdc = get_frame_dc ((f))
+#define RELEASE_HDC(hdc, f) release_frame_dc ((f), (hdc))
+#endif
+
+#ifndef OPTIONAL_HDC
+#define OPTIONAL_HDC(hdc)
+#define DECLARE_HDC(hdc)
+#define ALLOCATE_HDC(hdc, f)
+#define RELEASE_HDC(hdc, f)
+#endif
+
+static void
+init_glyph_string (s, OPTIONAL_HDC (hdc) char2b, w, row, area, start, hl)
+ struct glyph_string *s;
+ DECLARE_HDC (hdc)
+ XChar2b *char2b;
+ struct window *w;
+ struct glyph_row *row;
+ enum glyph_row_area area;
+ int start;
+ enum draw_glyphs_face hl;
+{
+ bzero (s, sizeof *s);
+ s->w = w;
+ s->f = XFRAME (w->frame);
+#ifdef HAVE_NTGUI
+ s->hdc = hdc;
+#endif
+ s->display = FRAME_X_DISPLAY (s->f);
+ s->window = FRAME_X_WINDOW (s->f);
+ s->char2b = char2b;
+ s->hl = hl;
+ s->row = row;
+ s->area = area;
+ s->first_glyph = row->glyphs[area] + start;
+ s->height = row->height;
+ s->y = WINDOW_TO_FRAME_PIXEL_Y (w, row->y);
+
+ /* Display the internal border below the tool-bar window. */
+ if (WINDOWP (s->f->tool_bar_window)
+ && s->w == XWINDOW (s->f->tool_bar_window))
+ s->y -= FRAME_INTERNAL_BORDER_WIDTH (s->f);
+
+ s->ybase = s->y + row->ascent;
+}
+
+
+/* Append the list of glyph strings with head H and tail T to the list
+ with head *HEAD and tail *TAIL. Set *HEAD and *TAIL to the result. */
+
+static INLINE void
+append_glyph_string_lists (head, tail, h, t)
+ struct glyph_string **head, **tail;
+ struct glyph_string *h, *t;
+{
+ if (h)
+ {
+ if (*head)
+ (*tail)->next = h;
+ else
+ *head = h;
+ h->prev = *tail;
+ *tail = t;
+ }
+}
+
+
+/* Prepend the list of glyph strings with head H and tail T to the
+ list with head *HEAD and tail *TAIL. Set *HEAD and *TAIL to the
+ result. */
+
+static INLINE void
+prepend_glyph_string_lists (head, tail, h, t)
+ struct glyph_string **head, **tail;
+ struct glyph_string *h, *t;
+{
+ if (h)
+ {
+ if (*head)
+ (*head)->prev = t;
+ else
+ *tail = t;
+ t->next = *head;
+ *head = h;
+ }
+}
+
+
+/* Append glyph string S to the list with head *HEAD and tail *TAIL.
+ Set *HEAD and *TAIL to the resulting list. */
+
+static INLINE void
+append_glyph_string (head, tail, s)
+ struct glyph_string **head, **tail;
+ struct glyph_string *s;
+{
+ s->next = s->prev = NULL;
+ append_glyph_string_lists (head, tail, s, s);
+}
+
+
+/* Get face and two-byte form of character glyph GLYPH on frame F.