/* Haiku window system support
   Copyright (C) 2021-2025 Free Software Foundation, Inc.

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 3 of the License, 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.  If not, see <https://www.gnu.org/licenses/>.  */

#include <config.h>

#include "dispextern.h"
#include "frame.h"
#include "lisp.h"
#include "haikugui.h"
#include "keyboard.h"
#include "haikuterm.h"
#include "blockinput.h"
#include "termchar.h"
#include "termhooks.h"
#include "menu.h"
#include "buffer.h"
#include "haiku_support.h"
#include "thread.h"
#include "window.h"
#include "haikuselect.h"

#include <math.h>
#include <stdlib.h>

#ifdef USE_BE_CAIRO
#include <cairo.h>
#endif

/* Minimum and maximum values used for Haiku scroll bars.  */
#define BE_SB_MAX 12000000

/* The single Haiku display (if any).  */
struct haiku_display_info *x_display_list;

/* This is used to determine when to evict the font lookup cache,
   which we do every 50 updates.  */
static int up_to_date_count;

/* List of defined fringe bitmaps.  */
static void **fringe_bmps;

/* The amount of fringe bitmaps in that list.  */
static int max_fringe_bmp;

/* Alist of resources to their values.  */
static Lisp_Object rdb;

/* Non-zero means that a HELP_EVENT has been generated since Emacs
   start.  */
static bool any_help_event_p;

char *
get_keysym_name (int keysym)
{
  static char value[16];
  sprintf (value, "%d", keysym);
  return value;
}

static struct frame *
haiku_window_to_frame (void *window)
{
  Lisp_Object tail, tem;
  struct frame *f;

  FOR_EACH_FRAME (tail, tem)
    {
      f = XFRAME (tem);
      if (!FRAME_HAIKU_P (f))
	continue;

      eassert (FRAME_DISPLAY_INFO (f) == x_display_list);

      if (FRAME_HAIKU_WINDOW (f) == window)
	return f;
    }

  return 0;
}

static void
haiku_coords_from_parent (struct frame *f, int *x, int *y)
{
  struct frame *p = FRAME_PARENT_FRAME (f);

  *x -= FRAME_OUTPUT_DATA (p)->frame_x;
  *y -= FRAME_OUTPUT_DATA (p)->frame_y;
}

static void
haiku_toolkit_position (struct frame *f, int x, int y,
			bool *menu_bar_p, bool *tool_bar_p)
{
  if (FRAME_OUTPUT_DATA (f)->menubar)
    *menu_bar_p = (x >= 0 && x < FRAME_PIXEL_WIDTH (f)
		   && y >= 0 && y < FRAME_MENU_BAR_HEIGHT (f));
}

static void
haiku_delete_terminal (struct terminal *terminal)
{
  error ("The Haiku terminal cannot be deleted");
}

static const char *
haiku_get_string_resource (void *ignored, const char *name,
			   const char *class)
{
  const char *native;

  if (!name)
    return NULL;

  Lisp_Object lval = assoc_no_quit (build_string (name), rdb);

  if (!NILP (lval))
    return SSDATA (XCDR (lval));

  if ((native = be_find_setting (name)))
    return native;

  return NULL;
}

static void
haiku_update_size_hints (struct frame *f)
{
  if (f->tooltip)
    return;

  block_input ();
  BWindow_set_size_alignment (FRAME_HAIKU_WINDOW (f),
			      (frame_resize_pixelwise
			       ? 1 : FRAME_COLUMN_WIDTH (f)),
			      (frame_resize_pixelwise
			       ? 1 : FRAME_LINE_HEIGHT (f)));
  unblock_input ();
}

static void
haiku_clip_to_string (struct glyph_string *s)
{
  struct haiku_rect r[2];
  int n = get_glyph_string_clip_rects (s, (struct haiku_rect *) &r, 2);

  if (n)
    {
      /* If n[FOO].width is 0, it means to not draw at all, so set the
	 clipping to some impossible value.  */
      if (r[0].width <= 0)
	BView_ClipToRect (FRAME_HAIKU_DRAWABLE (s->f),
			  FRAME_PIXEL_WIDTH (s->f),
			  FRAME_PIXEL_HEIGHT (s->f),
			  10, 10);
      else
	{
	  BView_ClipToRect (FRAME_HAIKU_DRAWABLE (s->f), r[0].x,
			    r[0].y, r[0].width, r[0].height);
	  BView_invalidate_region (FRAME_HAIKU_DRAWABLE (s->f), r[0].x,
				   r[0].y, r[0].width, r[0].height);
	}
    }

  if (n > 1)
    {
      /* If n[FOO].width is 0, it means to not draw at all, so set the
	 clipping to some impossible value.  */
      if (r[1].width <= 0)
	BView_ClipToRect (FRAME_HAIKU_DRAWABLE (s->f),
			  FRAME_PIXEL_WIDTH (s->f),
			  FRAME_PIXEL_HEIGHT (s->f),
			  10, 10);
      else
	{
	  BView_ClipToRect (FRAME_HAIKU_DRAWABLE (s->f), r[1].x, r[1].y,
			    r[1].width, r[1].height);
	  BView_invalidate_region (FRAME_HAIKU_DRAWABLE (s->f), r[1].x,
				   r[1].y, r[1].width, r[1].height);
	}
    }
}

static void
haiku_clip_to_string_exactly (struct glyph_string *s, struct glyph_string *dst)
{
  BView_ClipToRect (FRAME_HAIKU_DRAWABLE (s->f), s->x, s->y,
		    s->width, s->height);
  BView_invalidate_region (FRAME_HAIKU_DRAWABLE (s->f), s->x,
			   s->y, s->width, s->height);
}

static void
haiku_flip_buffers (struct frame *f)
{
  void *view = FRAME_OUTPUT_DATA (f)->view;
  block_input ();

  BView_draw_lock (view, false, 0, 0, 0, 0);
  FRAME_DIRTY_P (f) = 0;
  EmacsView_flip_and_blit (view);
  BView_draw_unlock (view);

  unblock_input ();
}

static void
haiku_frame_up_to_date (struct frame *f)
{
  block_input ();
  FRAME_MOUSE_UPDATE (f);
  if (FRAME_DIRTY_P (f) && !buffer_flipping_blocked_p ())
    haiku_flip_buffers (f);

  up_to_date_count++;
  if (up_to_date_count == 50)
    {
      be_evict_font_cache ();
      up_to_date_count = 0;
    }

  /* Mark the frame as complete.  */
  FRAME_COMPLETE_P (f) = true;
  unblock_input ();
}

static void
haiku_buffer_flipping_unblocked_hook (struct frame *f)
{
  if (FRAME_DIRTY_P (f))
    haiku_flip_buffers (f);
}

static void
haiku_clear_frame_area (struct frame *f, int x, int y,
			int width, int height)
{
  void *vw = FRAME_HAIKU_DRAWABLE (f);
  block_input ();
  BView_draw_lock (vw, true, x, y, width, height);
  BView_StartClip (vw);
  BView_ClipToRect (vw, x, y, width, height);
  BView_SetHighColor (vw, FRAME_BACKGROUND_PIXEL (f));
  BView_FillRectangle (vw, x, y, width, height);
  BView_EndClip (vw);
  BView_draw_unlock (vw);
  unblock_input ();
}

static void
haiku_clear_frame (struct frame *f)
{
  void *view = FRAME_HAIKU_DRAWABLE (f);

  mark_window_cursors_off (XWINDOW (FRAME_ROOT_WINDOW (f)));

  FRAME_COMPLETE_P (f) = false;

  block_input ();
  BView_draw_lock (view, true, 0, 0, FRAME_PIXEL_WIDTH (f),
		   FRAME_PIXEL_HEIGHT (f));
  BView_StartClip (view);
  BView_ClipToRect (view, 0, 0, FRAME_PIXEL_WIDTH (f),
		    FRAME_PIXEL_HEIGHT (f));
  BView_SetHighColor (view, FRAME_BACKGROUND_PIXEL (f));
  BView_FillRectangle (view, 0, 0, FRAME_PIXEL_WIDTH (f) ,
		       FRAME_PIXEL_HEIGHT (f));
  BView_EndClip (view);
  BView_draw_unlock (view);
  unblock_input ();
}

/* Give frame F the font FONT-OBJECT as its default font.  The return
   value is FONT-OBJECT.  FONTSET is an ID of the fontset for the
   frame.  If it is negative, generate a new fontset from
   FONT-OBJECT.  */

static Lisp_Object
haiku_new_font (struct frame *f, Lisp_Object font_object, int fontset)
{
  struct font *font;
  int ascent, descent, unit;

  font = XFONT_OBJECT (font_object);

  if (fontset < 0)
    fontset = fontset_from_font (font_object);

  FRAME_FONTSET (f) = fontset;

  if (FRAME_FONT (f) == font)
    return font_object;

  FRAME_FONT (f) = font;
  FRAME_BASELINE_OFFSET (f) = font->baseline_offset;
  FRAME_COLUMN_WIDTH (f) = font->average_width;

  get_font_ascent_descent (font, &ascent, &descent);
  FRAME_LINE_HEIGHT (f) = ascent + descent;
  FRAME_TAB_BAR_HEIGHT (f) = FRAME_TAB_BAR_LINES (f) * FRAME_LINE_HEIGHT (f);

  unit = FRAME_COLUMN_WIDTH (f);
  if (FRAME_CONFIG_SCROLL_BAR_WIDTH (f) > 0)
    FRAME_CONFIG_SCROLL_BAR_COLS (f)
      = (FRAME_CONFIG_SCROLL_BAR_WIDTH (f) + unit - 1) / unit;
  else
    FRAME_CONFIG_SCROLL_BAR_COLS (f) = (14 + unit - 1) / unit;

  if (FRAME_HAIKU_WINDOW (f) && !FRAME_TOOLTIP_P (f))
    adjust_frame_size (f, FRAME_COLS (f) * FRAME_COLUMN_WIDTH (f),
		       FRAME_LINES (f) * FRAME_LINE_HEIGHT (f),
		       3, false, Qfont);

  return font_object;
}

static int
haiku_valid_modifier_p (Lisp_Object sym)
{
  return EQ (sym, Qcommand) || EQ (sym, Qshift)
    || EQ (sym, Qcontrol) || EQ (sym, Qoption);
}

#define MODIFIER_OR(obj, def) (haiku_valid_modifier_p (obj) ? obj : def)

static void
haiku_add_modifier (int modifier, int toput, Lisp_Object qtem, int *modifiers)
{
  if ((modifier & HAIKU_MODIFIER_ALT && EQ (qtem, Qcommand))
      || (modifier & HAIKU_MODIFIER_SHIFT && EQ (qtem, Qshift))
      || (modifier & HAIKU_MODIFIER_CTRL && EQ (qtem, Qcontrol))
      || (modifier & HAIKU_MODIFIER_SUPER && EQ (qtem, Qoption)))
    *modifiers |= toput;
}

static int
haiku_modifiers_to_emacs (int haiku_key)
{
  int modifiers = 0;
  haiku_add_modifier (haiku_key, shift_modifier,
		      MODIFIER_OR (Vhaiku_shift_keysym, Qshift), &modifiers);
  haiku_add_modifier (haiku_key, super_modifier,
		      MODIFIER_OR (Vhaiku_super_keysym, Qoption), &modifiers);
  haiku_add_modifier (haiku_key, meta_modifier,
		      MODIFIER_OR (Vhaiku_meta_keysym, Qcommand), &modifiers);
  haiku_add_modifier (haiku_key, ctrl_modifier,
		      MODIFIER_OR (Vhaiku_control_keysym, Qcontrol), &modifiers);
  return modifiers;
}

#undef MODIFIER_OR

static void
haiku_rehighlight (void)
{
  eassert (x_display_list && !x_display_list->next);

  block_input ();

  struct frame *old_hl = x_display_list->highlight_frame;

  if (x_display_list->focused_frame)
    {
      x_display_list->highlight_frame
	= ((FRAMEP (FRAME_FOCUS_FRAME (x_display_list->focused_frame)))
	   ? XFRAME (FRAME_FOCUS_FRAME (x_display_list->focused_frame))
	   : x_display_list->focused_frame);
      if (!FRAME_LIVE_P (x_display_list->highlight_frame))
	{
	  fset_focus_frame (x_display_list->focused_frame, Qnil);
	  x_display_list->highlight_frame = x_display_list->focused_frame;
	}
    }
  else
    x_display_list->highlight_frame = 0;

  if (old_hl)
    gui_update_cursor (old_hl, true);

  if (x_display_list->highlight_frame)
    gui_update_cursor (x_display_list->highlight_frame, true);
  unblock_input ();
}

static void
haiku_frame_raise_lower (struct frame *f, bool raise_p)
{
  if (raise_p)
    {
      block_input ();
      BWindow_activate (FRAME_HAIKU_WINDOW (f));
      BWindow_sync (FRAME_HAIKU_WINDOW (f));
      unblock_input ();
    }
  else
    {
      block_input ();
      BWindow_send_behind (FRAME_HAIKU_WINDOW (f), NULL);
      BWindow_sync (FRAME_HAIKU_WINDOW (f));
      unblock_input ();
    }
}

static struct frame *
haiku_mouse_or_wdesc_frame (void *window, bool accept_tooltip)
{
  struct frame *lm_f = (gui_mouse_grabbed (x_display_list)
			? x_display_list->last_mouse_frame
			: NULL);

  if (lm_f && !EQ (track_mouse, Qdropping)
      && !EQ (track_mouse, Qdrag_source))
    return lm_f;
  else
    {
      struct frame *w_f = haiku_window_to_frame (window);

      /* Do not return a tooltip frame.  */
      if (!w_f || (FRAME_TOOLTIP_P (w_f) && !accept_tooltip))
	return EQ (track_mouse, Qdropping) ? lm_f : NULL;
      else
	/* When dropping it would be probably nice to raise w_f
	   here.  */
	return w_f;
    }
}

/* Set the thumb size and position of scroll bar BAR.  We are
   currently displaying PORTION out of a whole WHOLE, and our position
   POSITION.  */

static void
haiku_set_scroll_bar_thumb (struct scroll_bar *bar, int portion,
			    int position, int whole)
{
  void *scroll_bar = bar->scroll_bar;
  double top, shown, size, value;

  if (scroll_bar_adjust_thumb_portion_p)
    {
      /* We use an estimate of 30 chars per line rather than the real
         `portion' value.  This has the disadvantage that the thumb
         size is not very representative, but it makes our life a lot
         easier.  Otherwise, we have to constantly adjust the thumb
         size, which we can't always do quickly enough: while
         dragging, the size of the thumb might prevent the user from
         dragging the thumb all the way to the end.  */
      portion = WINDOW_TOTAL_LINES (XWINDOW (bar->window)) * 30;
      /* When the thumb is at the bottom, position == whole.  So we
         need to increase `whole' to make space for the thumb.  */
      whole += portion;
    }
  else
    bar->page_size = 0;

  if (whole <= 0)
    top = 0, shown = 1;
  else
    {
      top = (double) position / whole;
      shown = (double) portion / whole;
    }

  /* Slider size.  Must be in the range [1 .. MAX - MIN] where MAX
     is the scroll bar's maximum and MIN is the scroll bar's minimum
     value.  */
  size = clip_to_bounds (1, shown * BE_SB_MAX, BE_SB_MAX);

  /* Position.  Must be in the range [MIN .. MAX - SLIDER_SIZE].  */
  value = top * BE_SB_MAX;
  value = min (value, BE_SB_MAX - size);

  if (!bar->dragging && scroll_bar_adjust_thumb_portion_p)
    bar->page_size = size;

  BView_scroll_bar_update (scroll_bar, lrint (size),
			   BE_SB_MAX, ceil (value),
			   (scroll_bar_adjust_thumb_portion_p
			    ? bar->dragging : bar->dragging ? -1 : 0),
			   !scroll_bar_adjust_thumb_portion_p);
}

static void
haiku_set_horizontal_scroll_bar_thumb (struct scroll_bar *bar, int portion,
				       int position, int whole)
{
  void *scroll_bar = bar->scroll_bar;
  double size, value, shown, top;

  shown = (double) portion / whole;
  top = (double) position / whole;

  size = shown * BE_SB_MAX;
  value = top * BE_SB_MAX;

  if (!bar->dragging)
    bar->page_size = size;

  BView_scroll_bar_update (scroll_bar, lrint (size), BE_SB_MAX,
			   ceil (value), bar->dragging ? -1 : 0, true);
}

static struct scroll_bar *
haiku_scroll_bar_from_widget (void *scroll_bar, void *window)
{
  Lisp_Object tem;
  struct frame *frame = haiku_window_to_frame (window);

  if (!frame)
    return NULL;

  if (!scroll_bar)
    return NULL;

  if (!NILP (FRAME_SCROLL_BARS (frame)))
    {
      for (tem = FRAME_SCROLL_BARS (frame); !NILP (tem);
	   tem = XSCROLL_BAR (tem)->next)
	{
	  if (XSCROLL_BAR (tem)->scroll_bar == scroll_bar)
	    return XSCROLL_BAR (tem);
	}
    }

  return NULL;
}

/* Unfortunately, NOACTIVATE is not implementable on Haiku.  */
static void
haiku_focus_frame (struct frame *frame, bool noactivate)
{
  if (x_display_list->focused_frame != frame)
    haiku_frame_raise_lower (frame, 1);
}

static void
haiku_new_focus_frame (struct frame *frame)
{
  eassert (x_display_list && !x_display_list->next);

  block_input ();
  if (frame != x_display_list->focused_frame)
    {
      if (x_display_list->focused_frame &&
	  x_display_list->focused_frame->auto_lower)
	haiku_frame_raise_lower (x_display_list->focused_frame, 0);

      x_display_list->focused_frame = frame;

      if (frame && frame->auto_raise && !popup_activated_p)
	haiku_frame_raise_lower (frame, 1);
    }
  unblock_input ();

  haiku_rehighlight ();
}

static void
haiku_implicitly_set_name (struct frame *f, Lisp_Object arg, Lisp_Object oldval)
{
  haiku_set_name (f, arg, 0);
}

static void
haiku_query_frame_background_color (struct frame *f, Emacs_Color *bgcolor)
{
  haiku_query_color (FRAME_BACKGROUND_PIXEL (f), bgcolor);
}

static bool
haiku_defined_color (struct frame *f, const char *name,
		     Emacs_Color *color, bool alloc, bool make_index)
{
  int rc;

  rc = !haiku_get_color (name, color);

  if (rc && f->gamma && alloc)
    gamma_correct (f, color);

  return rc;
}

/* Adapted from xterm `x_draw_box_rect'.  */
static void
haiku_draw_box_rect (struct glyph_string *s, int left_x, int top_y,
		     int right_x, int bottom_y, int hwidth, int vwidth,
		     bool left_p, bool right_p, struct haiku_rect *clip_rect)
{
  void *view = FRAME_HAIKU_DRAWABLE (s->f);
  struct face *face = s->face;

  BView_SetHighColor (view, face->box_color);
  if (clip_rect)
    BView_ClipToRect (view, clip_rect->x, clip_rect->y, clip_rect->width,
		      clip_rect->height);
  BView_FillRectangle (view, left_x, top_y, right_x - left_x + 1, hwidth);
  if (left_p)
    BView_FillRectangle (view, left_x, top_y, vwidth, bottom_y - top_y + 1);

  BView_FillRectangle (view, left_x, bottom_y - hwidth + 1,
		       right_x - left_x + 1, hwidth);
  if (right_p)
    BView_FillRectangle (view, right_x - vwidth + 1,
			 top_y, vwidth, bottom_y - top_y + 1);
}

static void
haiku_calculate_relief_colors (struct glyph_string *s, uint32_t *rgbout_w,
			       uint32_t *rgbout_b)
{
  double h, cs, l;
  uint32_t rgbin;
  struct haiku_output *di;

  if (s->face->use_box_color_for_shadows_p)
    rgbin = s->face->box_color;
  else if (s->first_glyph->type == IMAGE_GLYPH
	   && s->img->pixmap
	   && !IMAGE_BACKGROUND_TRANSPARENT (s->img, s->f, 0))
    rgbin = IMAGE_BACKGROUND (s->img, s->f, 0);
  else
    rgbin = s->face->background;

  di = FRAME_OUTPUT_DATA (s->f);

  if (s->hl == DRAW_CURSOR)
    rgbin = FRAME_CURSOR_COLOR (s->f).pixel;

  if (di->relief_background != rgbin)
    {
      di->relief_background = rgbin & 0xffffffff;

      rgb_color_hsl (rgbin, &h, &cs, &l);
      hsl_color_rgb (h, cs, fmin (1.0, fmax (0.2, l) * 0.6),
		     &di->black_relief_pixel);
      hsl_color_rgb (h, cs, fmin (1.0, fmax (0.2, l) * 1.2),
		     &di->white_relief_pixel);
    }

  *rgbout_w = di->white_relief_pixel;
  *rgbout_b = di->black_relief_pixel;
}

static void
haiku_draw_relief_rect (struct glyph_string *s, int left_x, int top_y,
			int right_x, int bottom_y, int hwidth, int vwidth,
			bool raised_p, bool top_p, bool bot_p, bool left_p,
			bool right_p, struct haiku_rect *clip_rect)
{
  uint32_t color_white, color_black;
  void *view;

  view = FRAME_HAIKU_DRAWABLE (s->f);
  haiku_calculate_relief_colors (s, &color_white, &color_black);

  BView_SetHighColor (view, raised_p ? color_white : color_black);

  if (clip_rect)
    {
      BView_StartClip (view);
      haiku_clip_to_string (s);
      BView_ClipToRect (view, clip_rect->x, clip_rect->y,
			clip_rect->width, clip_rect->height);
    }

  if (top_p)
    BView_FillRectangle (view, left_x, top_y,
			 right_x - left_x + 1, hwidth);

  if (left_p)
    BView_FillRectangle (view, left_x, top_y,
			 vwidth, bottom_y - top_y + 1);

  BView_SetHighColor (view, !raised_p ? color_white : color_black);

  if (bot_p)
    BView_FillRectangle (view, left_x, bottom_y - hwidth + 1,
			 right_x - left_x + 1, hwidth);
  if (right_p)
    BView_FillRectangle (view, right_x - vwidth + 1, top_y,
			 vwidth, bottom_y - top_y + 1);

  /* Draw the triangle for the bottom-left corner.  */
  if (bot_p && left_p)
    {
      BView_SetHighColor (view, raised_p ? color_white : color_black);
      BView_FillTriangle (view, left_x, bottom_y - hwidth, left_x + vwidth,
			  bottom_y - hwidth, left_x, bottom_y);
    }

  /* Now draw the triangle for the top-right corner.  */
  if (top_p && right_p)
    {
      BView_SetHighColor (view, raised_p ? color_white : color_black);
      BView_FillTriangle (view, right_x - vwidth, top_y,
			  right_x, top_y,
			  right_x - vwidth, top_y + hwidth);
    }

  /* If (h/v)width is > 1, we draw the outer-most line on each side in the
     black relief color.  */

  BView_SetHighColor (view, color_black);

  if (hwidth > 1 && top_p)
    BView_StrokeLine (view, left_x, top_y, right_x, top_y);
  if (hwidth > 1 && bot_p)
    BView_StrokeLine (view, left_x, bottom_y, right_x, bottom_y);
  if (vwidth > 1 && left_p)
    BView_StrokeLine (view, left_x, top_y, left_x, bottom_y);
  if (vwidth > 1 && right_p)
    BView_StrokeLine (view, right_x, top_y, right_x, bottom_y);

  BView_SetHighColor (view, FRAME_BACKGROUND_PIXEL (s->f));

  /* Omit corner pixels.  */
  if (hwidth > 1 && vwidth > 1)
    {
      if (left_p && top_p)
	BView_FillRectangle (view, left_x, top_y, 1, 1);
      if (left_p && bot_p)
	BView_FillRectangle (view, left_x, bottom_y, 1, 1);
      if (right_p && top_p)
	BView_FillRectangle (view, right_x, top_y, 1, 1);
      if (right_p && bot_p)
	BView_FillRectangle (view, right_x, bottom_y, 1, 1);
    }

  if (clip_rect)
    BView_EndClip (view);
}

static void
haiku_get_scale_factor (int *scale_x, int *scale_y)
{
  struct haiku_display_info *dpyinfo = x_display_list;

  if (dpyinfo->resx > 96)
    *scale_x = floor (dpyinfo->resx / 96);
  if (dpyinfo->resy > 96)
    *scale_y = floor (dpyinfo->resy / 96);
}

static void
haiku_draw_underwave (struct glyph_string *s, int width, int x)
{
  int wave_height, wave_length;
  int y, dx, dy, odd, xmax, scale_x, scale_y;
  float ax, ay, bx, by;
  void *view;

  scale_x = 1;
  scale_y = 1;
  haiku_get_scale_factor (&scale_x, &scale_y);
  wave_height = 3 * scale_y;
  wave_length = 2 * scale_x;

  dx = wave_length;
  dy = wave_height - 1;
  y = s->ybase - wave_height + 3;
  xmax = x + width;
  view = FRAME_HAIKU_DRAWABLE (s->f);

  BView_StartClip (view);
  haiku_clip_to_string (s);
  BView_ClipToRect (view, x, y, width, wave_height);

  ax = x - ((int) (x) % dx) + (float) 0.5;
  bx = ax + dx;
  odd = (int) (ax / dx) % 2;
  ay = by = y + 0.5;

  if (odd)
    ay += dy;
  else
    by += dy;

  BView_SetPenSize (view, scale_y);

  while (ax <= xmax)
    {
      BView_StrokeLine (view, ax, ay, bx, by);
      ax = bx, ay = by;
      bx += dx, by = y + 0.5 + odd * dy;
      odd = !odd;
    }

  BView_SetPenSize (view, 1);
  BView_EndClip (view);
}

/* Draw a dashed underline of thickness THICKNESS and width WIDTH onto F
   at a vertical offset of OFFSET from the position of the glyph string
   S, with each segment SEGMENT pixels in length.  */

static void
haiku_draw_dash (struct frame *f, struct glyph_string *s, int width,
		 int segment, int offset, int thickness)
{
  int y_center, which, length, x, doffset;
  void *view;

  /* Configure the thickness of the view's strokes.  */
  view = FRAME_HAIKU_VIEW (s->f);
  BView_SetPenSize (view, thickness);

  /* Offset the origin of the line by half the line width. */
  y_center = s->ybase + offset + thickness / 2;

  /* Remove redundant portions of OFFSET.  */
  doffset = s->x % (segment * 2);

  /* Set which to the phase of the first dash that ought to be drawn and
     length to its length.  */
  which = doffset < segment;
  length = segment - (s->x % segment);

  /* Begin drawing this dash.  */
  for (x = s->x; x < s->x + width; x += length, length = segment)
    {
      if (which)
	BView_StrokeLine (view, x, y_center,
			  min (x + length - 1,
			       s->x + width - 1),
			  y_center);

      which = !which;
    }
}

/* Draw an underline of STYLE onto F at an offset of POSITION from the
   baseline of the glyph string S, S->WIDTH in length, and THICKNESS in
   height.  */

static void
haiku_fill_underline (struct frame *f, struct glyph_string *s,
		      enum face_underline_type style, int position,
		      int thickness)
{
  int segment;
  void *view;

  segment = thickness * 3;
  view = FRAME_HAIKU_VIEW (f);

  switch (style)
    {
      /* FACE_UNDERLINE_DOUBLE_LINE is treated identically to SINGLE, as
	 the second line will be filled by another invocation of this
	 function.  */
    case FACE_UNDERLINE_SINGLE:
    case FACE_UNDERLINE_DOUBLE_LINE:
      BView_FillRectangle (view, s->x, s->ybase + position,
			   s->width, thickness);
      break;

    case FACE_UNDERLINE_DOTS:
      segment = thickness;
      FALLTHROUGH;

    case FACE_UNDERLINE_DASHES:
      haiku_draw_dash (f, s, s->width, segment, position, thickness);
      break;

    case FACE_NO_UNDERLINE:
    case FACE_UNDERLINE_WAVE:
    default:
      emacs_abort ();
    }
}

static void
haiku_draw_text_decoration (struct glyph_string *s, struct face *face,
			    int width, int x)
{
  unsigned long cursor_color;

  if (s->for_overlaps)
    return;

  if (s->hl == DRAW_CURSOR)
    haiku_merge_cursor_foreground (s, &cursor_color, NULL);

  void *view = FRAME_HAIKU_DRAWABLE (s->f);

  if (face->underline)
    {
      if (s->hl == DRAW_CURSOR)
	BView_SetHighColor (view, cursor_color);
      else if (!face->underline_defaulted_p)
	BView_SetHighColor (view, face->underline_color);
      else
	BView_SetHighColor (view, face->foreground);

      if (face->underline == FACE_UNDERLINE_WAVE)
	haiku_draw_underwave (s, width, x);
      else if (face->underline >= FACE_UNDERLINE_SINGLE)
	{
	  unsigned long thickness, position;

	  if (s->prev
	      && (s->prev->face->underline != FACE_UNDERLINE_WAVE
		  && s->prev->face->underline >= FACE_UNDERLINE_SINGLE)
	      && (s->prev->face->underline_at_descent_line_p
		  == s->face->underline_at_descent_line_p)
	      && (s->prev->face->underline_pixels_above_descent_line
		  == s->face->underline_pixels_above_descent_line))
	    {
	      /* We use the same underline style as the previous one.  */
	      thickness = s->prev->underline_thickness;
	      position = s->prev->underline_position;
	    }
	  else
	    {
	      struct font *font = font_for_underline_metrics (s);
	      unsigned long minimum_offset;
	      bool underline_at_descent_line;
	      bool use_underline_position_properties;
	      Lisp_Object val = (WINDOW_BUFFER_LOCAL_VALUE
				 (Qunderline_minimum_offset, s->w));

	      if (FIXNUMP (val))
		minimum_offset = max (0, XFIXNUM (val));
	      else
		minimum_offset = 1;

	      val = (WINDOW_BUFFER_LOCAL_VALUE
		     (Qx_underline_at_descent_line, s->w));
	      underline_at_descent_line
		= (!(NILP (val) || BASE_EQ (val, Qunbound))
		   || s->face->underline_at_descent_line_p);

	      val = (WINDOW_BUFFER_LOCAL_VALUE
		     (Qx_use_underline_position_properties, s->w));
	      use_underline_position_properties
		= !(NILP (val) || BASE_EQ (val, Qunbound));

	      /* Get the underline thickness.  Default is 1 pixel.  */
	      if (font && font->underline_thickness > 0)
		thickness = font->underline_thickness;
	      else
		thickness = 1;
	      if (underline_at_descent_line)
		position = ((s->height - thickness)
			    - (s->ybase - s->y)
			    - s->face->underline_pixels_above_descent_line);
	      else
		{
		  /* Get the underline position.  This is the
		     recommended vertical offset in pixels from
		     the baseline to the top of the underline.
		     This is a signed value according to the
		     specs, and its default is

		     ROUND ((maximum descent) / 2), with
		     ROUND(x) = floor (x + 0.5)  */

		  if (use_underline_position_properties
		      && font && font->underline_position >= 0)
		    position = font->underline_position;
		  else if (font)
		    position = (font->descent + 1) / 2;
		  else
		    position = minimum_offset;
		}
	      position = max (position, minimum_offset);
	    }
	  /* Check the sanity of thickness and position.  We should
	     avoid drawing underline out of the current line area.  */
	  if (s->y + s->height <= s->ybase + position)
	    position = (s->height - 1) - (s->ybase - s->y);
	  if (s->y + s->height < s->ybase + position + thickness)
	    thickness = (s->y + s->height) - (s->ybase + position);
	  s->underline_thickness = thickness;
	  s->underline_position = position;

	  haiku_fill_underline (s->f, s, s->face->underline,
				position, thickness);

	  /* Place a second underline above the first if this was
	     requested in the face specification.  */

	  if (s->face->underline == FACE_UNDERLINE_DOUBLE_LINE)
	    {
	      /* Compute the position of the second underline.  */
	      position = position - thickness - 1;
	      haiku_fill_underline (s->f, s, s->face->underline,
				    position, thickness);
	    }
	}
    }

  if (face->overline_p)
    {
      unsigned long dy = 0, h = 1;
      if (s->hl == DRAW_CURSOR)
	BView_SetHighColor (view, cursor_color);
      else if (!face->overline_color_defaulted_p)
	BView_SetHighColor (view, face->overline_color);
      else
	BView_SetHighColor (view, face->foreground);

      BView_FillRectangle (view, s->x, s->y + dy, s->width, h);
    }

  if (face->strike_through_p)
    {
      /* Y-coordinate and height of the glyph string's first
	 glyph.  We cannot use s->y and s->height because those
	 could be larger if there are taller display elements
	 (e.g., characters displayed with a larger font) in the
	 same glyph row.  */
      int glyph_y = s->ybase - s->first_glyph->ascent;
      int glyph_height = s->first_glyph->ascent + s->first_glyph->descent;
      /* Strike-through width and offset from the glyph string's
	 top edge.  */
      unsigned long h = 1;
      unsigned long dy = (glyph_height - h) / 2;

      if (s->hl == DRAW_CURSOR)
	BView_SetHighColor (view, cursor_color);
      else if (!face->strike_through_color_defaulted_p)
	BView_SetHighColor (view, face->strike_through_color);
      else
	BView_SetHighColor (view, face->foreground);

      BView_FillRectangle (view, s->x, glyph_y + dy, s->width, h);
    }
}

static void
haiku_draw_string_box (struct glyph_string *s)
{
  int hwidth, vwidth, left_x, right_x, top_y, bottom_y, last_x;
  bool raised_p, left_p, right_p;
  struct glyph *last_glyph;
  struct face *face = s->face;

  last_x = ((s->row->full_width_p && !s->w->pseudo_window_p)
	    ? WINDOW_RIGHT_EDGE_X (s->w)
	    : window_box_right (s->w, s->area));

  /* The glyph that may have a right box line.  For static
     compositions and images, the right-box flag is on the first glyph
     of the glyph string; for other types it's on the last glyph.  */
  if (s->cmp || s->img)
    last_glyph = s->first_glyph;
  else if (s->first_glyph->type == COMPOSITE_GLYPH
	   && s->first_glyph->u.cmp.automatic)
    {
      /* For automatic compositions, we need to look up the last glyph
	 in the composition.  */
        struct glyph *end = s->row->glyphs[s->area] + s->row->used[s->area];
	struct glyph *g = s->first_glyph;
	for (last_glyph = g++;
	     g < end && g->u.cmp.automatic && g->u.cmp.id == s->cmp_id
	       && g->slice.cmp.to < s->cmp_to;
	     last_glyph = g++)
	  ;
    }
  else
    last_glyph = s->first_glyph + s->nchars - 1;

  vwidth = eabs (face->box_vertical_line_width);
  hwidth = eabs (face->box_horizontal_line_width);
  raised_p = face->box == FACE_RAISED_BOX;
  left_x = s->x;
  right_x = (s->row->full_width_p && s->extends_to_end_of_line_p
	     ? last_x - 1
	     : min (last_x, s->x + s->background_width) - 1);

  top_y = s->y;
  bottom_y = top_y + s->height - 1;

  left_p = (s->first_glyph->left_box_line_p
	    || (s->hl == DRAW_MOUSE_FACE
		&& (s->prev == NULL
		    || s->prev->hl != s->hl)));
  right_p = (last_glyph->right_box_line_p
	     || (s->hl == DRAW_MOUSE_FACE
		 && (s->next == NULL
		     || s->next->hl != s->hl)));

  if (face->box == FACE_SIMPLE_BOX)
    haiku_draw_box_rect (s, left_x, top_y, right_x, bottom_y, hwidth,
			 vwidth, left_p, right_p, NULL);
  else
    haiku_draw_relief_rect (s, left_x, top_y, right_x, bottom_y, hwidth,
			    vwidth, raised_p, true, true, left_p, right_p,
			    NULL);
}

static void
haiku_draw_plain_background (struct glyph_string *s, struct face *face,
			     int x, int y, int width, int height)
{
  void *view = FRAME_HAIKU_DRAWABLE (s->f);
  unsigned long cursor_color;

  if (s->hl == DRAW_CURSOR)
    {
      haiku_merge_cursor_foreground (s, NULL, &cursor_color);
      BView_SetHighColor (view, cursor_color);
    }
  else
    BView_SetHighColor (view, face->background_defaulted_p ?
			FRAME_BACKGROUND_PIXEL (s->f) :
			face->background);

  BView_FillRectangle (view, x, y, width, height);
}

static struct haiku_bitmap_record *
haiku_get_bitmap_rec (struct frame *f, ptrdiff_t id)
{
  return &FRAME_DISPLAY_INFO (f)->bitmaps[id - 1];
}

static void
haiku_update_bitmap_rec (struct haiku_bitmap_record *rec,
			 uint32_t new_foreground,
			 uint32_t new_background)
{
  char *bits;
  int x, y, bytes_per_line;

  if (new_foreground == rec->stipple_foreground
      && new_background == rec->stipple_background)
    return;

  bits = rec->stipple_bits;
  bytes_per_line = (rec->width + 7) / 8;

  for (y = 0; y < rec->height; y++)
    {
      for (x = 0; x < rec->width; x++)
	haiku_put_pixel (rec->img, x, y,
			 ((bits[x / 8] >> (x % 8)) & 1
			  ? new_foreground : new_background));

      bits += bytes_per_line;
    }

  rec->stipple_foreground = new_foreground;
  rec->stipple_background = new_background;
}

static void
haiku_draw_stipple_background (struct glyph_string *s, struct face *face,
			       int x, int y, int width, int height,
			       bool explicit_colors_p,
			       uint32 explicit_background,
			       uint32 explicit_foreground)
{
  struct haiku_bitmap_record *rec;
  unsigned long foreground, background;
  void *view;

  view = FRAME_HAIKU_DRAWABLE (s->f);
  rec = haiku_get_bitmap_rec (s->f, s->face->stipple);

  if (explicit_colors_p)
    {
      background = explicit_background;
      foreground = explicit_foreground;
    }
  else if (s->hl == DRAW_CURSOR)
    haiku_merge_cursor_foreground (s, &foreground, &background);
  else
    {
      foreground = s->face->foreground;
      background = s->face->background;
    }

  haiku_update_bitmap_rec (rec, foreground, background);

  BView_StartClip (view);
  haiku_clip_to_string (s);
  BView_ClipToRect (view, x, y, width, height);
  BView_DrawBitmapTiled (view, rec->img, 0, 0, -1, -1,
			 0, 0, x + width, y + height);
  BView_EndClip (view);
}

void
haiku_draw_background_rect (struct glyph_string *s, struct face *face,
			    int x, int y, int width, int height)
{
  if (!s->stippled_p)
    haiku_draw_plain_background (s, face, x, y, width, height);
  else
    haiku_draw_stipple_background (s, face, x, y, width, height,
				   false, 0, 0);
}

static void
haiku_maybe_draw_background (struct glyph_string *s, int force_p)
{
  if ((s->first_glyph->type != IMAGE_GLYPH) && !s->background_filled_p)
    {
      struct face *face = s->face;
      int box_line_width = max (face->box_horizontal_line_width, 0);
      int box_vline_width = max (face->box_vertical_line_width, 0);

      if (FONT_HEIGHT (s->font) < s->height - 2 * box_vline_width
	  || FONT_TOO_HIGH (s->font)
          || s->font_not_found_p || s->extends_to_end_of_line_p || force_p)
	{
	  haiku_draw_background_rect (s, s->face, s->x, s->y + box_line_width,
				      s->background_width,
				      s->height - 2 * box_line_width);

	  s->background_filled_p = 1;
	}
    }
}

static void
haiku_mouse_face_colors (struct glyph_string *s, uint32_t *fg,
			 uint32_t *bg)
{
  int face_id;
  struct face *face;

  /* What face has to be used last for the mouse face?  */
  face_id = MOUSE_HL_INFO (s->f)->mouse_face_face_id;
  face = FACE_FROM_ID_OR_NULL (s->f, face_id);
  if (face == NULL)
    face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);

  if (s->first_glyph->type == CHAR_GLYPH)
    face_id = FACE_FOR_CHAR (s->f, face, s->first_glyph->u.ch, -1, Qnil);
  else
    face_id = FACE_FOR_CHAR (s->f, face, 0, -1, Qnil);

  face = FACE_FROM_ID (s->f, face_id);
  prepare_face_for_display (s->f, s->face);

  if (fg)
    *fg = face->foreground;
  if (bg)
    *bg = face->background;
}

static void
haiku_draw_glyph_string_foreground (struct glyph_string *s)
{
  struct face *face = s->face;

  int i, x;
  if (face->box != FACE_NO_BOX
      && s->first_glyph->left_box_line_p)
    x = s->x + max (face->box_vertical_line_width, 0);
  else
    x = s->x;

  void *view = FRAME_HAIKU_DRAWABLE (s->f);

  if (s->font_not_found_p)
    {
      if (s->hl == DRAW_CURSOR)
	BView_SetHighColor (view, FRAME_OUTPUT_DATA (s->f)->cursor_fg);
      else
	BView_SetHighColor (view, face->foreground);
      for (i = 0; i < s->nchars; ++i)
	{
	  struct glyph *g = s->first_glyph + i;

	  BView_SetPenSize (view, 1);
	  BView_StrokeRectangle (view, x, s->y, g->pixel_width,
				 s->height);
	  x += g->pixel_width;
	}
    }
  else
    {
      struct font *ft = s->font;
      int off = ft->baseline_offset;
      int y;

      if (ft->vertical_centering)
	off = VCENTER_BASELINE_OFFSET (ft, s->f) - off;
      y = s->ybase - off;
      if (s->for_overlaps || (s->background_filled_p && s->hl != DRAW_CURSOR))
	ft->driver->draw (s, 0, s->nchars, x, y, false);
      else
	ft->driver->draw (s, 0, s->nchars, x, y, true);

      if (face->overstrike)
	ft->driver->draw (s, 0, s->nchars, x + 1, y, false);
    }
}

static void
haiku_draw_glyphless_glyph_string_foreground (struct glyph_string *s)
{
  struct glyph *glyph = s->first_glyph;
  static unsigned char2b[8];
  int x, i, j;
  struct face *face = s->face;
  unsigned long color;

  /* If first glyph of S has a left box line, start drawing the text
     of S to the right of that box line.  */
  if (face && face->box != FACE_NO_BOX
      && s->first_glyph->left_box_line_p)
    x = s->x + max (face->box_vertical_line_width, 0);
  else
    x = s->x;

  s->char2b = char2b;

  for (i = 0; i < s->nchars; i++, glyph++)
    {
#ifdef GCC_LINT
      enum { PACIFY_GCC_BUG_81401 = 1 };
#else
      enum { PACIFY_GCC_BUG_81401 = 0 };
#endif
      char buf[7 + PACIFY_GCC_BUG_81401];
      char *str = NULL;
      int len = glyph->u.glyphless.len;

      if (glyph->u.glyphless.method == GLYPHLESS_DISPLAY_ACRONYM)
	{
	  if (len > 0
	      && CHAR_TABLE_P (Vglyphless_char_display)
	      && (CHAR_TABLE_EXTRA_SLOTS (XCHAR_TABLE (Vglyphless_char_display))
		  >= 1))
	    {
	      Lisp_Object acronym
		= (! glyph->u.glyphless.for_no_font
		   ? CHAR_TABLE_REF (Vglyphless_char_display,
				     glyph->u.glyphless.ch)
		   : XCHAR_TABLE (Vglyphless_char_display)->extras[0]);
	      if (CONSP (acronym))
		acronym = XCAR (acronym);
	      if (STRINGP (acronym))
		str = SSDATA (acronym);
	    }
	}
      else if (glyph->u.glyphless.method == GLYPHLESS_DISPLAY_HEX_CODE)
	{
	  unsigned int ch = glyph->u.glyphless.ch;
	  eassume (ch <= MAX_CHAR);
	  sprintf (buf, "%0*X", ch < 0x10000 ? 4 : 6, ch);
	  str = buf;
	}

      if (str)
	{
	  int upper_len = (len + 1) / 2;

	  /* It is assured that all LEN characters in STR is ASCII.  */
	  for (j = 0; j < len; j++)
            char2b[j] = s->font->driver->encode_char (s->font, str[j]) & 0xFFFF;

	  s->font->driver->draw (s, 0, upper_len,
				 x + glyph->slice.glyphless.upper_xoff,
				 s->ybase + glyph->slice.glyphless.upper_yoff,
				 false);
	  s->font->driver->draw (s, upper_len, len,
				 x + glyph->slice.glyphless.lower_xoff,
				 s->ybase + glyph->slice.glyphless.lower_yoff,
				 false);
	}

      if (glyph->u.glyphless.method != GLYPHLESS_DISPLAY_THIN_SPACE)
	{
	  if (s->hl == DRAW_CURSOR)
	    haiku_merge_cursor_foreground (s, NULL, &color);
	  else
	    color = s->face->foreground;

	  BView_SetHighColor (FRAME_HAIKU_DRAWABLE (s->f), color);
	  BView_SetPenSize (FRAME_HAIKU_DRAWABLE (s->f), 1);
	  BView_StrokeRectangle (FRAME_HAIKU_DRAWABLE (s->f),
				 x, s->ybase - glyph->ascent,
				 glyph->pixel_width,
				 glyph->ascent + glyph->descent);
	}
      x += glyph->pixel_width;
   }
}

static void
haiku_draw_stretch_glyph_string (struct glyph_string *s)
{
  struct face *face = s->face;
  uint32_t bkg;

  if (s->hl == DRAW_CURSOR && !x_stretch_cursor_p)
    {
      int width, background_width = s->background_width;
      int x = s->x;

      if (!s->row->reversed_p)
	{
	  int left_x = window_box_left_offset (s->w, TEXT_AREA);

	  if (x < left_x)
	    {
	      background_width -= left_x - x;
	      x = left_x;
	    }
	}
      else
	{
	  /* In R2L rows, draw the cursor on the right edge of the
	     stretch glyph.  */
	  int right_x = window_box_right (s->w, TEXT_AREA);
	  if (x + background_width > right_x)
	    background_width -= x - right_x;
	  x += background_width;
	}

      width = min (FRAME_COLUMN_WIDTH (s->f), background_width);
      if (s->row->reversed_p)
	x -= width;

      void *view = FRAME_HAIKU_DRAWABLE (s->f);
      unsigned long cursor_color;

      haiku_merge_cursor_foreground (s, NULL, &cursor_color);
      BView_SetHighColor (view, cursor_color);
      BView_FillRectangle (view, x, s->y, width, s->height);

      if (width < background_width)
	{
	  if (!s->row->reversed_p)
	    x += width;
	  else
	    x = s->x;

	  int y = s->y;
	  int w = background_width - width, h = s->height;

	  /* Draw stipples manually because we want the background
	     part of a stretch glyph to have a stipple even if the
	     cursor is visible on top.  */
	  if (!face->stipple)
	    {
	      if (s->row->mouse_face_p && cursor_in_mouse_face_p (s->w))
		haiku_mouse_face_colors (s, NULL, &bkg);
	      else
		bkg = face->background;

	      BView_SetHighColor (view, bkg);
	      BView_FillRectangle (view, x, y, w, h);
	    }
	  else
	    {
	      if (s->row->mouse_face_p && cursor_in_mouse_face_p (s->w))
		haiku_mouse_face_colors (s, NULL, &bkg);
	      else
		bkg = face->background;

	      haiku_draw_stipple_background (s, s->face, x, y, w, h,
					     true, bkg, face->foreground);
	    }
	}
    }
  else if (!s->background_filled_p)
    {
      int background_width = s->background_width;
      int x = s->x, text_left_x = window_box_left (s->w, TEXT_AREA);

      /* Don't draw into left fringe or scrollbar area except for
         header line and mode line.  */
      if (s->area == TEXT_AREA
	  && x < text_left_x && !s->row->mode_line_p)
	{
	  background_width -= text_left_x - x;
	  x = text_left_x;
	}

      if (background_width > 0)
	haiku_draw_background_rect (s, s->face, x, s->y,
				    background_width, s->height);
    }
  s->background_filled_p = 1;
}

static void
haiku_start_clip (struct glyph_string *s)
{
  void *view = FRAME_HAIKU_DRAWABLE (s->f);
  BView_StartClip (view);
}

static void
haiku_end_clip (struct glyph_string *s)
{
  void *view = FRAME_HAIKU_DRAWABLE (s->f);
  BView_EndClip (view);
}

static void
haiku_clip_to_row (struct window *w, struct glyph_row *row,
		   enum glyph_row_area area)
{
  struct frame *f = WINDOW_XFRAME (w);
  int window_x, window_y, window_width;
  int x, y, width, height;

  window_box (w, area, &window_x, &window_y, &window_width, 0);

  x = window_x;
  y = WINDOW_TO_FRAME_PIXEL_Y (w, max (0, row->y));
  y = max (y, window_y);
  width = window_width;
  height = row->visible_height;

  BView_ClipToRect (FRAME_HAIKU_DRAWABLE (f), x, y, width, height);
}

static void
haiku_update_begin (struct frame *f)
{
  /* Mark the frame as incomplete so it is not flushed upon handling
     input.  */
  FRAME_COMPLETE_P (f) = false;
}

static void
haiku_update_end (struct frame *f)
{
  MOUSE_HL_INFO (f)->mouse_face_defer = false;
  BWindow_Flush (FRAME_HAIKU_WINDOW (f));
}

static void
haiku_draw_composite_glyph_string_foreground (struct glyph_string *s)
{
  int i, j, x;
  struct font *font = s->font;
  void *view = FRAME_HAIKU_DRAWABLE (s->f);
  struct face *face = s->face;

  /* If first glyph of S has a left box line, start drawing the text
     of S to the right of that box line.  */
  if (face && face->box != FACE_NO_BOX
      && s->first_glyph->left_box_line_p)
    x = s->x + max (face->box_vertical_line_width, 0);
  else
    x = s->x;

  /* S is a glyph string for a composition.  S->cmp_from is the index
     of the first character drawn for glyphs of this composition.
     S->cmp_from == 0 means we are drawing the very first character of
     this composition.  */

  /* Draw a rectangle for the composition if the font for the very
     first character of the composition could not be loaded.  */
  if (s->font_not_found_p && !s->cmp_from)
    {
      if (s->hl == DRAW_CURSOR)
	BView_SetHighColor (view, FRAME_OUTPUT_DATA (s->f)->cursor_fg);
      else
	BView_SetHighColor (view, s->face->foreground);

      BView_SetPenSize (view, 1);
      BView_StrokeRectangle (view, s->x, s->y,
			     s->width, s->height);
    }
  else if (!s->first_glyph->u.cmp.automatic)
    {
      int y = s->ybase;

      for (i = 0, j = s->cmp_from; i < s->nchars; i++, j++)
	/* TAB in a composition means display glyphs with padding
	   space on the left or right.  */
	if (COMPOSITION_GLYPH (s->cmp, j) != '\t')
	  {
	    int xx = x + s->cmp->offsets[j * 2];
	    int yy = y - s->cmp->offsets[j * 2 + 1];

	    font->driver->draw (s, j, j + 1, xx, yy, false);
	    if (face->overstrike)
	      font->driver->draw (s, j, j + 1, xx + 1, yy, false);
	  }
    }
  else
    {
      Lisp_Object gstring = composition_gstring_from_id (s->cmp_id);
      Lisp_Object glyph;
      int y = s->ybase;
      int width = 0;

      for (i = j = s->cmp_from; i < s->cmp_to; i++)
	{
	  glyph = LGSTRING_GLYPH (gstring, i);
	  if (NILP (LGLYPH_ADJUSTMENT (glyph)))
	    width += LGLYPH_WIDTH (glyph);
	  else
	    {
	      int xoff, yoff, wadjust;

	      if (j < i)
		{
		  font->driver->draw (s, j, i, x, y, false);
		  if (s->face->overstrike)
		    font->driver->draw (s, j, i, x + 1, y, false);
		  x += width;
		}
	      xoff = LGLYPH_XOFF (glyph);
	      yoff = LGLYPH_YOFF (glyph);
	      wadjust = LGLYPH_WADJUST (glyph);
	      font->driver->draw (s, i, i + 1, x + xoff, y + yoff, false);
	      if (face->overstrike)
		font->driver->draw (s, i, i + 1, x + xoff + 1, y + yoff,
				    false);
	      x += wadjust;
	      j = i + 1;
	      width = 0;
	    }
	}
      if (j < i)
	{
	  font->driver->draw (s, j, i, x, y, false);
	  if (face->overstrike)
	    font->driver->draw (s, j, i, x + 1, y, false);
	}
    }
}

static void
haiku_draw_image_relief (struct glyph_string *s)
{
  int x1, y1, thick;
  bool raised_p, top_p, bot_p, left_p, right_p;
  int extra_x, extra_y;
  struct haiku_rect r;
  int x = s->x;
  int y = s->ybase - image_ascent (s->img, s->face, &s->slice);

  struct face *face = s->face;

  /* If first glyph of S has a left box line, start drawing it to the
     right of that line.  */
  if (face->box != FACE_NO_BOX
      && s->first_glyph->left_box_line_p
      && s->slice.x == 0)
    x += max (face->box_vertical_line_width, 0);

  /* If there is a margin around the image, adjust x- and y-position
     by that margin.  */
  if (s->slice.x == 0)
    x += s->img->hmargin;
  if (s->slice.y == 0)
    y += s->img->vmargin;

  if (s->hl == DRAW_IMAGE_SUNKEN
      || s->hl == DRAW_IMAGE_RAISED)
    {
      if (s->face->id == TAB_BAR_FACE_ID)
	thick = (tab_bar_button_relief < 0
		 ? DEFAULT_TAB_BAR_BUTTON_RELIEF
		 : min (tab_bar_button_relief, 1000000));
      else
	thick = (tool_bar_button_relief < 0
		 ? DEFAULT_TOOL_BAR_BUTTON_RELIEF
		 : min (tool_bar_button_relief, 1000000));
      raised_p = s->hl == DRAW_IMAGE_RAISED;
    }
  else
    {
      thick = eabs (s->img->relief);
      raised_p = s->img->relief > 0;
    }

  x1 = x + s->slice.width - 1;
  y1 = y + s->slice.height - 1;

  extra_x = extra_y = 0;

  if (s->face->id == TAB_BAR_FACE_ID)
    {
      if (CONSP (Vtab_bar_button_margin)
	  && FIXNUMP (XCAR (Vtab_bar_button_margin))
	  && FIXNUMP (XCDR (Vtab_bar_button_margin)))
	{
	  extra_x = XFIXNUM (XCAR (Vtab_bar_button_margin)) - thick;
	  extra_y = XFIXNUM (XCDR (Vtab_bar_button_margin)) - thick;
	}
      else if (FIXNUMP (Vtab_bar_button_margin))
	extra_x = extra_y = XFIXNUM (Vtab_bar_button_margin) - thick;
    }

  if (s->face->id == TOOL_BAR_FACE_ID)
    {
      if (CONSP (Vtool_bar_button_margin)
	  && FIXNUMP (XCAR (Vtool_bar_button_margin))
	  && FIXNUMP (XCDR (Vtool_bar_button_margin)))
	{
	  extra_x = XFIXNUM (XCAR (Vtool_bar_button_margin));
	  extra_y = XFIXNUM (XCDR (Vtool_bar_button_margin));
	}
      else if (FIXNUMP (Vtool_bar_button_margin))
	extra_x = extra_y = XFIXNUM (Vtool_bar_button_margin);
    }

  top_p = bot_p = left_p = right_p = 0;

  if (s->slice.x == 0)
    x -= thick + extra_x, left_p = 1;
  if (s->slice.y == 0)
    y -= thick + extra_y, top_p = 1;
  if (s->slice.x + s->slice.width == s->img->width)
    x1 += thick + extra_x, right_p = 1;
  if (s->slice.y + s->slice.height == s->img->height)
    y1 += thick + extra_y, bot_p = 1;

  get_glyph_string_clip_rect (s, &r);
  haiku_draw_relief_rect (s, x, y, x1, y1, thick, thick, raised_p,
			  top_p, bot_p, left_p, right_p, &r);
}

static void
haiku_translate_transform (double (*transform)[3], double dx,
			   double dy)
{
  transform[0][2] += dx;
  transform[1][2] += dy;
}

static void
haiku_draw_image_glyph_string (struct glyph_string *s)
{
  struct face *face = s->face;
  void *view, *bitmap, *mask;
  int box_line_hwidth = max (face->box_vertical_line_width, 0);
  int box_line_vwidth = max (face->box_horizontal_line_width, 0);
  int x, y, height, width, relief;
  struct haiku_rect nr;
  Emacs_Rectangle cr, ir, r;
  unsigned long background;
  double image_transform[3][3];

  height = s->height;
  if (s->slice.y == 0)
    height -= box_line_vwidth;
  if (s->slice.y + s->slice.height >= s->img->height)
    height -= box_line_vwidth;

  width = s->background_width;
  x = s->x;
  if (s->first_glyph->left_box_line_p
      && s->slice.x == 0)
    {
      x += box_line_hwidth;
      width -= box_line_hwidth;
    }

  y = s->y;
  if (s->slice.y == 0)
    y += box_line_vwidth;

  view = FRAME_HAIKU_DRAWABLE (s->f);
  bitmap = s->img->pixmap;

  s->stippled_p = face->stipple != 0;

  if (s->hl == DRAW_CURSOR)
    haiku_merge_cursor_foreground (s, NULL, &background);
  else
    background = face->background;

  haiku_draw_background_rect (s, face, x, y,
			      width, height);

  if (bitmap)
    {
      get_glyph_string_clip_rect (s, &nr);
      CONVERT_TO_EMACS_RECT (cr, nr);
      x = s->x;
      y = s->ybase - image_ascent (s->img, face, &s->slice);

      if (s->slice.x == 0)
	x += s->img->hmargin;
      if (s->slice.y == 0)
	y += s->img->vmargin;

      if (face->box != FACE_NO_BOX
	  && s->first_glyph->left_box_line_p
	  && s->slice.x == 0)
	x += max (face->box_vertical_line_width, 0);

      ir.x = x;
      ir.y = y;
      ir.width = s->slice.width;
      ir.height = s->slice.height;
      r = ir;

      mask = s->img->mask;

      if (gui_intersect_rectangles (&cr, &ir, &r))
	{
	  memcpy (&image_transform, &s->img->transform,
		  sizeof image_transform);

	  if (s->slice.x != x || s->slice.y != y
	      || s->slice.width != s->img->width
	      || s->slice.height != s->img->height)
	    {
	      BView_StartClip (view);
	      BView_ClipToRect (view, r.x, r.y, r.width, r.height);
	    }

	  haiku_translate_transform (image_transform,
				     x - s->slice.x,
				     y - s->slice.y);

	  be_apply_affine_transform (view,
				     image_transform[0][0],
				     image_transform[0][1],
				     image_transform[0][2],
				     image_transform[1][0],
				     image_transform[1][1],
				     image_transform[1][2]);

	  if (!s->stippled_p || !mask)
	    {
	      BView_DrawBitmap (view, bitmap, 0, 0,
				s->img->original_width,
				s->img->original_height,
				0, 0,
				s->img->original_width,
				s->img->original_height,
				s->img->use_bilinear_filtering);

	      if (mask)
		be_draw_image_mask (mask, view, 0, 0,
				    s->img->original_width,
				    s->img->original_height,
				    0, 0,
				    s->img->original_width,
				    s->img->original_height,
				    background);
	    }
	  else
	    /* In order to make sure the stipple background remains
	       visible, use the mask for the alpha channel of BITMAP
	       and composite it onto the view instead.  */
	    be_draw_bitmap_with_mask (view, bitmap, mask, 0, 0,
				      s->img->original_width,
				      s->img->original_height,
				      0, 0,
				      s->img->original_width,
				      s->img->original_height,
				      s->img->use_bilinear_filtering);

	  if (s->slice.x != x || s->slice.y != y
	      || s->slice.width != s->img->width
	      || s->slice.height != s->img->height)
	    BView_EndClip (view);

	  be_apply_affine_transform (view, 1, 0, 0, 0, 1, 0);
	}

      if (!s->img->mask)
	{
	  /* When the image has a mask, we can expect that at
	     least part of a mouse highlight or a block cursor will
	     be visible.  If the image doesn't have a mask, make
	     a block cursor visible by drawing a rectangle around
	     the image.  I believe it's looking better if we do
	     nothing here for mouse-face.  */

	  if (s->hl == DRAW_CURSOR)
	    {
	      relief = eabs (s->img->relief);

	      BView_SetPenSize (view, 1);
	      BView_SetHighColor (view, FRAME_CURSOR_COLOR (s->f).pixel);
	      BView_StrokeRectangle (view, x - relief, y - relief,
				     s->slice.width + relief * 2,
				     s->slice.height + relief * 2);
	    }
	}
    }

  if (s->img->relief
      || s->hl == DRAW_IMAGE_RAISED
      || s->hl == DRAW_IMAGE_SUNKEN)
    haiku_draw_image_relief (s);
}

static void
haiku_draw_glyph_string (struct glyph_string *s)
{
  void *view = FRAME_HAIKU_DRAWABLE (s->f);;
  struct face *face = s->face;

  block_input ();
  BView_draw_lock (view, false, 0, 0, 0, 0);
  prepare_face_for_display (s->f, s->face);

  s->stippled_p = s->hl != DRAW_CURSOR && face->stipple;

  if (s->next && s->right_overhang && !s->for_overlaps)
    {
      int width;
      struct glyph_string *next;

      for (width = 0, next = s->next;
	   next && width < s->right_overhang;
	   width += next->width, next = next->next)
	if (next->first_glyph->type != IMAGE_GLYPH)
          {
	    prepare_face_for_display (s->f, next->face);
	    next->stippled_p
	      = next->hl != DRAW_CURSOR && next->face->stipple;

	    haiku_start_clip (next);
	    haiku_clip_to_string (next);
            if (next->first_glyph->type != STRETCH_GLYPH)
	      haiku_maybe_draw_background (next, true);
            else
	      haiku_draw_stretch_glyph_string (next);
	    haiku_end_clip (s);
          }
    }

  haiku_start_clip (s);

  int box_filled_p = 0;

  if (!s->for_overlaps && face->box != FACE_NO_BOX
      && (s->first_glyph->type == CHAR_GLYPH
	  || s->first_glyph->type == COMPOSITE_GLYPH))
    {
      haiku_clip_to_string (s);
      haiku_maybe_draw_background (s, 1);
      box_filled_p = 1;
      haiku_draw_string_box (s);
    }
  else if (!s->clip_head /* draw_glyphs didn't specify a clip mask. */
	   && !s->clip_tail
	   && ((s->prev && s->prev->hl != s->hl && s->left_overhang)
	       || (s->next && s->next->hl != s->hl && s->right_overhang)))
    /* We must clip just this glyph.  left_overhang part has already
       drawn when s->prev was drawn, and right_overhang part will be
       drawn later when s->next is drawn. */
    haiku_clip_to_string_exactly (s, s);
  else
    haiku_clip_to_string (s);

  if (s->for_overlaps)
    s->background_filled_p = 1;

  switch (s->first_glyph->type)
    {
    case COMPOSITE_GLYPH:
      if (s->for_overlaps || (s->cmp_from > 0
			      && ! s->first_glyph->u.cmp.automatic))
	s->background_filled_p = 1;
      else
	haiku_maybe_draw_background (s, 1);
      haiku_draw_composite_glyph_string_foreground (s);
      break;
    case CHAR_GLYPH:
      if (s->for_overlaps)
	s->background_filled_p = 1;
      else
	haiku_maybe_draw_background (s, 0);
      haiku_draw_glyph_string_foreground (s);
      break;
    case STRETCH_GLYPH:
      haiku_draw_stretch_glyph_string (s);
      break;
    case IMAGE_GLYPH:
      haiku_draw_image_glyph_string (s);
      break;
    case GLYPHLESS_GLYPH:
      if (s->for_overlaps)
	s->background_filled_p = 1;
      else
	haiku_maybe_draw_background (s, 1);
      haiku_draw_glyphless_glyph_string_foreground (s);
      break;
    default:
      emacs_abort ();
    }

  if (!s->for_overlaps)
    {
      if (!box_filled_p && face->box != FACE_NO_BOX)
	haiku_draw_string_box (s);
      else
	haiku_draw_text_decoration (s, face, s->width, s->x);

      if (s->prev)
	{
	  struct glyph_string *prev;

	  for (prev = s->prev; prev; prev = prev->prev)
	    if (prev->hl != s->hl
		&& prev->x + prev->width + prev->right_overhang > s->x)
	      {
		/* As prev was drawn while clipped to its own area, we
		   must draw the right_overhang part using s->hl now.  */
		enum draw_glyphs_face save = prev->hl;

		prev->hl = s->hl;
		haiku_start_clip (s);
		haiku_clip_to_string (s);
		haiku_clip_to_string_exactly (s, prev);
		if (prev->first_glyph->type == CHAR_GLYPH)
		  haiku_draw_glyph_string_foreground (prev);
		else
		  haiku_draw_composite_glyph_string_foreground (prev);
		haiku_end_clip (s);
		prev->hl = save;
	      }
	}

      if (s->next)
	{
	  struct glyph_string *next;

	  for (next = s->next; next; next = next->next)
	    if (next->hl != s->hl
		&& next->x - next->left_overhang < s->x + s->width)
	      {
		/* As next will be drawn while clipped to its own area,
		   we must draw the left_overhang part using s->hl now.  */
		enum draw_glyphs_face save = next->hl;

		next->hl = s->hl;
		haiku_start_clip (s);
		haiku_clip_to_string (s);
		haiku_clip_to_string_exactly (s, next);
		if (next->first_glyph->type == CHAR_GLYPH)
		  haiku_draw_glyph_string_foreground (next);
		else
		  haiku_draw_composite_glyph_string_foreground (next);
		haiku_end_clip (s);

		next->hl = save;
		next->clip_head = s->next;
	      }
	}
    }

  haiku_end_clip (s);
  BView_draw_unlock (view);

  /* Set the stipple_p flag indicating whether or not a stipple was
     drawn in s->row.  That is the case either when s is a stretch
     glyph string and s->face->stipple is not NULL, or when
     s->face->stipple exists and s->hl is not DRAW_CURSOR.  */
  if (s->face->stipple
      && (s->first_glyph->type == STRETCH_GLYPH
	  || s->hl != DRAW_CURSOR))
    s->row->stipple_p = true;

  unblock_input ();
}

static void
haiku_after_update_window_line (struct window *w,
				struct glyph_row *desired_row)
{
  eassert (w);
  struct frame *f;
  int width, height;

  if (!desired_row->mode_line_p && !w->pseudo_window_p)
    desired_row->redraw_fringe_bitmaps_p = true;

  if (windows_or_buffers_changed
      && desired_row->full_width_p
      && (f = XFRAME (w->frame),
	  width = FRAME_INTERNAL_BORDER_WIDTH (f),
	  width != 0)
      && (height = desired_row->visible_height,
	  height > 0))
    {
      int y = WINDOW_TO_FRAME_PIXEL_Y (w, max (0, desired_row->y));
      int face_id =
        !NILP (Vface_remapping_alist)
        ? lookup_basic_face (NULL, f, INTERNAL_BORDER_FACE_ID)
        : INTERNAL_BORDER_FACE_ID;
      struct face *face = FACE_FROM_ID_OR_NULL (f, face_id);

      block_input ();
      if (face)
	{
	  void *view = FRAME_HAIKU_DRAWABLE (f);
	  BView_draw_lock (view, false, 0, 0, 0, 0);
	  BView_StartClip (view);
	  BView_SetHighColor (view, (face->background_defaulted_p
				     ? FRAME_BACKGROUND_PIXEL (f)
				     : face->background));
	  BView_FillRectangle (view, 0, y, width, height);
	  BView_FillRectangle (view, FRAME_PIXEL_WIDTH (f) - width,
			       y, width, height);
	  BView_invalidate_region (FRAME_HAIKU_DRAWABLE (f),
				   0, y, width, height);
	  BView_invalidate_region (view, FRAME_PIXEL_WIDTH (f) - width,
				   y, width, height);
	  BView_EndClip (view);
	  BView_draw_unlock (view);
	}
      else
	{
	  haiku_clear_frame_area (f, 0, y, width, height);
	  haiku_clear_frame_area (f, FRAME_PIXEL_WIDTH (f) - width,
				  y, width, height);
	}
      unblock_input ();
    }
}

static void
haiku_set_window_size (struct frame *f, bool change_gravity,
		       int width, int height)
{
  Lisp_Object frame;

  /* On X Windows, window managers typically disallow resizing a
     window when it is fullscreen.  Do the same here.  */

  XSETFRAME (frame, f);
  if (!NILP (Fframe_parameter (frame, Qfullscreen))
      /* Only do this if the fullscreen status has actually been
	 applied.  */
      && f->want_fullscreen == FULLSCREEN_NONE
      /* And if the configury during frame creation has been
	 completed.  Otherwise, there will be no valid "old size" to
	 go back to.  */
      && FRAME_OUTPUT_DATA (f)->configury_done)
    return;

  haiku_update_size_hints (f);

  if (FRAME_HAIKU_WINDOW (f))
    {
      block_input ();
      BWindow_resize (FRAME_HAIKU_WINDOW (f),
		      width, height);

      if (FRAME_VISIBLE_P (f)
	  && (width != FRAME_PIXEL_WIDTH (f)
	      || height != FRAME_PIXEL_HEIGHT (f)))
	haiku_wait_for_event (f, FRAME_RESIZED);
      unblock_input ();
    }

  do_pending_window_change (false);
}

static void
haiku_draw_hollow_cursor (struct window *w, struct glyph_row *row)
{
  struct frame *f;
  int x, y, wd, h;
  struct glyph *cursor_glyph;
  uint32_t foreground;
  void *view;

  f = XFRAME (WINDOW_FRAME (w));
  view = FRAME_HAIKU_DRAWABLE (f);

  /* Get the glyph the cursor is on.  If we can't tell because
     the current matrix is invalid or such, give up.  */
  cursor_glyph = get_phys_cursor_glyph (w);
  if (cursor_glyph == NULL)
    return;

  /* Compute frame-relative coordinates for phys cursor.  */
  get_phys_cursor_geometry (w, row, cursor_glyph, &x, &y, &h);
  wd = w->phys_cursor_width;

  /* The foreground of cursor_gc is typically the same as the normal
     background color, which can cause the cursor box to be invisible.  */
  foreground = FRAME_CURSOR_COLOR (f).pixel;

  /* When on R2L character, show cursor at the right edge of the
     glyph, unless the cursor box is as wide as the glyph or wider
     (the latter happens when x-stretch-cursor is non-nil).  */
  if ((cursor_glyph->resolved_level & 1) != 0
      && cursor_glyph->pixel_width > wd)
    x += cursor_glyph->pixel_width - wd;

  /* Set clipping, draw the rectangle, and reset clipping again.
     This also marks the region as invalidated.  */

  BView_draw_lock (view, true, x, y, wd, h);
  BView_StartClip (view);
  haiku_clip_to_row (w, row, TEXT_AREA);

  /* Now set the foreground color and pen size.  */
  BView_SetHighColor (view, foreground);
  BView_SetPenSize (view, 1);

  /* Actually draw the rectangle.  */
  BView_StrokeRectangle (view, x, y, wd, h);

  /* Reset clipping.  */
  BView_EndClip (view);
  BView_draw_unlock (view);
}

static void
haiku_draw_bar_cursor (struct window *w, struct glyph_row *row,
		       int width, enum text_cursor_kinds kind)
{
  struct frame *f;
  struct glyph *cursor_glyph;
  struct glyph_row *r;
  struct face *face;
  uint32_t foreground;
  void *view;
  int x, y, dummy_x, dummy_y, dummy_h;

  f = XFRAME (w->frame);

  /* If cursor is out of bounds, don't draw garbage.  This can happen
     in mini-buffer windows when switching between echo area glyphs
     and mini-buffer.  */
  cursor_glyph = get_phys_cursor_glyph (w);
  if (cursor_glyph == NULL)
    return;

  /* If on an image, draw like a normal cursor.  That's usually better
     visible than drawing a bar, esp. if the image is large so that
     the bar might not be in the window.  */
  if (cursor_glyph->type == IMAGE_GLYPH)
    {
      r = MATRIX_ROW (w->current_matrix, w->phys_cursor.vpos);
      draw_phys_cursor_glyph (w, r, DRAW_CURSOR);
    }
  else
    {
      view = FRAME_HAIKU_DRAWABLE (f);
      face = FACE_FROM_ID (f, cursor_glyph->face_id);

      /* If the glyph's background equals the color we normally draw
	 the bars cursor in, the bar cursor in its normal color is
	 invisible.  Use the glyph's foreground color instead in this
	 case, on the assumption that the glyph's colors are chosen so
	 that the glyph is legible.  */
      if (face->background == FRAME_CURSOR_COLOR (f).pixel)
	foreground = face->foreground;
      else
	foreground = FRAME_CURSOR_COLOR (f).pixel;

      BView_draw_lock (view, false, 0, 0, 0, 0);
      BView_StartClip (view);
      BView_SetHighColor (view, foreground);
      haiku_clip_to_row (w, row, TEXT_AREA);

      if (kind == BAR_CURSOR)
	{
	  x = WINDOW_TEXT_TO_FRAME_PIXEL_X (w, w->phys_cursor.x);
	  y = WINDOW_TO_FRAME_PIXEL_Y (w, w->phys_cursor.y);

	  if (width < 0)
	    width = FRAME_CURSOR_WIDTH (f);
	  width = min (cursor_glyph->pixel_width, width);

	  w->phys_cursor_width = width;

	  /* If the character under cursor is R2L, draw the bar cursor
	     on the right of its glyph, rather than on the left.  */
	  if ((cursor_glyph->resolved_level & 1) != 0)
	    x += cursor_glyph->pixel_width - width;

	  BView_FillRectangle (view, x, y, width, row->height);
	  BView_invalidate_region (view, x, y, width, row->height);
	}
      else /* HBAR_CURSOR */
	{
	  x = WINDOW_TEXT_TO_FRAME_PIXEL_X (w, w->phys_cursor.x);
	  y = WINDOW_TO_FRAME_PIXEL_Y (w, w->phys_cursor.y +
					   row->height - width);

	  if (width < 0)
	    width = row->height;

	  width = min (row->height, width);

	  get_phys_cursor_geometry (w, row, cursor_glyph, &dummy_x,
				    &dummy_y, &dummy_h);

	  if ((cursor_glyph->resolved_level & 1) != 0
	      && cursor_glyph->pixel_width > w->phys_cursor_width - 1)
	    x += cursor_glyph->pixel_width - w->phys_cursor_width + 1;

	  BView_FillRectangle (view, x, y, w->phys_cursor_width - 1,
			       width);
	  BView_invalidate_region (view, x, y, w->phys_cursor_width - 1,
				   width);
	}

      BView_EndClip (view);
      BView_draw_unlock (view);
    }
}

static void
haiku_draw_window_cursor (struct window *w, struct glyph_row *glyph_row,
			  int x, int y, enum text_cursor_kinds cursor_type,
			  int cursor_width, bool on_p, bool active_p)
{
  if (on_p)
    {
      w->phys_cursor_type = cursor_type;
      w->phys_cursor_on_p = true;

      if (glyph_row->exact_window_width_line_p
	  && (glyph_row->reversed_p
	      ? (w->phys_cursor.hpos < 0)
	      : (w->phys_cursor.hpos >= glyph_row->used[TEXT_AREA])))
	{
	  glyph_row->cursor_in_fringe_p = true;
	  draw_fringe_bitmap (w, glyph_row, glyph_row->reversed_p);
	}
      else
	{
	  switch (cursor_type)
	    {
	    case HOLLOW_BOX_CURSOR:
	      haiku_draw_hollow_cursor (w, glyph_row);
	      break;

	    case FILLED_BOX_CURSOR:
	      draw_phys_cursor_glyph (w, glyph_row, DRAW_CURSOR);
	      break;

	    case BAR_CURSOR:
	      haiku_draw_bar_cursor (w, glyph_row, cursor_width, BAR_CURSOR);
	      break;

	    case HBAR_CURSOR:
	      haiku_draw_bar_cursor (w, glyph_row, cursor_width, HBAR_CURSOR);
	      break;

	    case NO_CURSOR:
	      w->phys_cursor_width = 0;
	      break;

	    default:
	      emacs_abort ();
	    }
	}
    }
}

static void
haiku_show_hourglass (struct frame *f)
{
  if (FRAME_TOOLTIP_P (f)
      || FRAME_OUTPUT_DATA (f)->hourglass_p)
    return;

  block_input ();
  FRAME_OUTPUT_DATA (f)->hourglass_p = 1;

  if (FRAME_HAIKU_VIEW (f))
    BView_set_view_cursor (FRAME_HAIKU_VIEW (f),
			   FRAME_OUTPUT_DATA (f)->hourglass_cursor);
  unblock_input ();
}

static void
haiku_hide_hourglass (struct frame *f)
{
  if (FRAME_TOOLTIP_P (f)
      || !FRAME_OUTPUT_DATA (f)->hourglass_p)
    return;

  block_input ();
  FRAME_OUTPUT_DATA (f)->hourglass_p = 0;

  if (FRAME_HAIKU_VIEW (f))
    BView_set_view_cursor (FRAME_HAIKU_VIEW (f),
			   FRAME_OUTPUT_DATA (f)->current_cursor);
  unblock_input ();
}

static void
haiku_compute_glyph_string_overhangs (struct glyph_string *s)
{
  if (s->cmp == NULL
      && (s->first_glyph->type == CHAR_GLYPH
	  || s->first_glyph->type == COMPOSITE_GLYPH))
    {
      struct font_metrics metrics;

      if (s->first_glyph->type == CHAR_GLYPH)
	{
	  struct font *font = s->font;
	  font->driver->text_extents (font, s->char2b, s->nchars, &metrics);
	}
      else
	{
	  Lisp_Object gstring = composition_gstring_from_id (s->cmp_id);

	  composition_gstring_width (gstring, s->cmp_from, s->cmp_to, &metrics);
	}
      s->right_overhang = (metrics.rbearing > metrics.width
			   ? metrics.rbearing - metrics.width : 0);
      s->left_overhang = metrics.lbearing < 0 ? - metrics.lbearing : 0;
    }
  else if (s->cmp)
    {
      s->right_overhang = s->cmp->rbearing - s->cmp->pixel_width;
      s->left_overhang = - s->cmp->lbearing;
    }
}

static void
haiku_draw_vertical_window_border (struct window *w,
				   int x, int y_0, int y_1)
{
  struct frame *f = XFRAME (WINDOW_FRAME (w));
  struct face *face;

  face = FACE_FROM_ID_OR_NULL (f, VERTICAL_BORDER_FACE_ID);
  void *view = FRAME_HAIKU_DRAWABLE (f);
  BView_draw_lock (view, true, x, y_0, 1, y_1);
  BView_StartClip (view);
  if (face)
    BView_SetHighColor (view, face->foreground);
  BView_StrokeLine (view, x, y_0, x, y_1);
  BView_EndClip (view);
  BView_draw_unlock (view);
}

static void
haiku_set_scroll_bar_default_width (struct frame *f)
{
  int unit, size;

  unit = FRAME_COLUMN_WIDTH (f);
  size = BScrollBar_default_size (0) + 1;

  FRAME_CONFIG_SCROLL_BAR_WIDTH (f) = size;
  FRAME_CONFIG_SCROLL_BAR_COLS (f) = (size + unit - 1) / unit;
}

static void
haiku_set_scroll_bar_default_height (struct frame *f)
{
  int height, size;

  height = FRAME_LINE_HEIGHT (f);
  size = BScrollBar_default_size (true) + 1;

  FRAME_CONFIG_SCROLL_BAR_HEIGHT (f) = size;
  FRAME_CONFIG_SCROLL_BAR_LINES (f) = (size + height - 1) / height;
}

static void
haiku_draw_window_divider (struct window *w, int x0, int x1, int y0, int y1)
{
  struct frame *f = XFRAME (WINDOW_FRAME (w));
  struct face *face = FACE_FROM_ID_OR_NULL (f, WINDOW_DIVIDER_FACE_ID);
  struct face *face_first
    = FACE_FROM_ID_OR_NULL (f, WINDOW_DIVIDER_FIRST_PIXEL_FACE_ID);
  struct face *face_last
    = FACE_FROM_ID_OR_NULL (f, WINDOW_DIVIDER_LAST_PIXEL_FACE_ID);
  unsigned long color = face ? face->foreground : FRAME_FOREGROUND_PIXEL (f);
  unsigned long color_first = (face_first
			       ? face_first->foreground
			       : FRAME_FOREGROUND_PIXEL (f));
  unsigned long color_last = (face_last
			      ? face_last->foreground
			      : FRAME_FOREGROUND_PIXEL (f));
  void *view = FRAME_HAIKU_DRAWABLE (f);

  BView_draw_lock (view, true, x0, y0, x1 - x0 + 1, y1 - y0 + 1);
  BView_StartClip (view);

  if ((y1 - y0 > x1 - x0) && (x1 - x0 >= 3))
    /* A vertical divider, at least three pixels wide: Draw first and
       last pixels differently.  */
    {
      BView_SetHighColor (view, color_first);
      BView_StrokeLine (view, x0, y0, x0, y1 - 1);
      BView_SetHighColor (view, color);
      BView_FillRectangle (view, x0 + 1, y0, x1 - x0 - 2, y1 - y0);
      BView_SetHighColor (view, color_last);
      BView_StrokeLine (view, x1 - 1, y0, x1 - 1, y1 - 1);
    }
  else if ((x1 - x0 > y1 - y0) && (y1 - y0 >= 3))
    /* A horizontal divider, at least three pixels high: Draw first and
       last pixels differently.  */
    {
      BView_SetHighColor (view, color_first);
      BView_StrokeLine (view, x0, y0, x1 - 1, y0);
      BView_SetHighColor (view, color);
      BView_FillRectangle (view, x0, y0 + 1, x1 - x0, y1 - y0 - 2);
      BView_SetHighColor (view, color_last);
      BView_FillRectangle (view, x0, y1 - 1, x1 - x0, 1);
    }
  else
    {
      BView_SetHighColor (view, color);
      BView_FillRectangleAbs (view, x0, y0, x1, y1);
    }
  BView_EndClip (view);
  BView_draw_unlock (view);
}

static void
haiku_condemn_scroll_bars (struct frame *frame)
{
  if (!NILP (FRAME_SCROLL_BARS (frame)))
    {
      if (!NILP (FRAME_CONDEMNED_SCROLL_BARS (frame)))
	{
	  /* Prepend scrollbars to already condemned ones.  */
	  Lisp_Object last = FRAME_SCROLL_BARS (frame);

	  while (!NILP (XSCROLL_BAR (last)->next))
	    last = XSCROLL_BAR (last)->next;

	  XSCROLL_BAR (last)->next = FRAME_CONDEMNED_SCROLL_BARS (frame);
	  XSCROLL_BAR (FRAME_CONDEMNED_SCROLL_BARS (frame))->prev = last;
	}

      fset_condemned_scroll_bars (frame, FRAME_SCROLL_BARS (frame));
      fset_scroll_bars (frame, Qnil);
    }
}

static void
haiku_redeem_scroll_bar (struct window *w)
{
  struct scroll_bar *bar;
  Lisp_Object barobj;
  struct frame *f;

  if (!NILP (w->vertical_scroll_bar) && WINDOW_HAS_VERTICAL_SCROLL_BAR (w))
    {
      bar = XSCROLL_BAR (w->vertical_scroll_bar);
      /* Unlink it from the condemned list.  */
      f = XFRAME (WINDOW_FRAME (w));
      if (NILP (bar->prev))
	{
	  /* If the prev pointer is nil, it must be the first in one of
	     the lists.  */
	  if (EQ (FRAME_SCROLL_BARS (f), w->vertical_scroll_bar))
	    /* It's not condemned.  Everything's fine.  */
	    goto horizontal;
	  else if (EQ (FRAME_CONDEMNED_SCROLL_BARS (f),
		       w->vertical_scroll_bar))
	    fset_condemned_scroll_bars (f, bar->next);
	  else
	    /* If its prev pointer is nil, it must be at the front of
	       one or the other!  */
	    emacs_abort ();
	}
      else
	XSCROLL_BAR (bar->prev)->next = bar->next;

      if (! NILP (bar->next))
	XSCROLL_BAR (bar->next)->prev = bar->prev;

      bar->next = FRAME_SCROLL_BARS (f);
      bar->prev = Qnil;
      XSETVECTOR (barobj, bar);
      fset_scroll_bars (f, barobj);
      if (! NILP (bar->next))
	XSETVECTOR (XSCROLL_BAR (bar->next)->prev, bar);
    }
 horizontal:
  if (!NILP (w->horizontal_scroll_bar) && WINDOW_HAS_HORIZONTAL_SCROLL_BAR (w))
    {
      bar = XSCROLL_BAR (w->horizontal_scroll_bar);
      /* Unlink it from the condemned list.  */
      f = XFRAME (WINDOW_FRAME (w));
      if (NILP (bar->prev))
	{
	  /* If the prev pointer is nil, it must be the first in one of
	     the lists.  */
	  if (EQ (FRAME_SCROLL_BARS (f), w->horizontal_scroll_bar))
	    /* It's not condemned.  Everything's fine.  */
	    return;
	  else if (EQ (FRAME_CONDEMNED_SCROLL_BARS (f),
		       w->horizontal_scroll_bar))
	    fset_condemned_scroll_bars (f, bar->next);
	  else
	    /* If its prev pointer is nil, it must be at the front of
	       one or the other!  */
	    emacs_abort ();
	}
      else
	XSCROLL_BAR (bar->prev)->next = bar->next;

      if (! NILP (bar->next))
	XSCROLL_BAR (bar->next)->prev = bar->prev;

      bar->next = FRAME_SCROLL_BARS (f);
      bar->prev = Qnil;
      XSETVECTOR (barobj, bar);
      fset_scroll_bars (f, barobj);
      if (! NILP (bar->next))
	XSETVECTOR (XSCROLL_BAR (bar->next)->prev, bar);
    }
}

static void
haiku_judge_scroll_bars (struct frame *f)
{
  Lisp_Object bar, next;

  bar = FRAME_CONDEMNED_SCROLL_BARS (f);

  /* Clear out the condemned list now so we won't try to process any
     more events on the hapless scroll bars.  */
  fset_condemned_scroll_bars (f, Qnil);

  for (; ! NILP (bar); bar = next)
    {
      struct scroll_bar *b = XSCROLL_BAR (bar);

      haiku_scroll_bar_remove (b);

      next = b->next;
      b->next = b->prev = Qnil;
    }

  /* Now there should be no references to the condemned scroll bars,
     and they should get garbage-collected.  */
}

static struct scroll_bar *
haiku_scroll_bar_create (struct window *w, int left, int top,
			 int width, int height, bool horizontal_p)
{
  struct frame *f;
  Lisp_Object barobj;
  struct scroll_bar *bar;
  void *scroll_bar;
  void *view;

  f = XFRAME (WINDOW_FRAME (w));
  view = FRAME_HAIKU_DRAWABLE (f);

  block_input ();
  bar = ALLOCATE_PSEUDOVECTOR (struct scroll_bar, prev, PVEC_OTHER);

  XSETWINDOW (bar->window, w);
  bar->top = top;
  bar->left = left;
  bar->width = width;
  bar->height = height;
  bar->position = 0;
  bar->total = 0;
  bar->dragging = 0;
  bar->update = -1;
  bar->horizontal = horizontal_p;

  scroll_bar = be_make_scroll_bar_for_view (view, horizontal_p,
					    left, top, left + width - 1,
					    top + height - 1);
  BView_publish_scroll_bar (view, left, top, width, height);

  bar->next = FRAME_SCROLL_BARS (f);
  bar->prev = Qnil;
  bar->scroll_bar = scroll_bar;
  XSETVECTOR (barobj, bar);
  fset_scroll_bars (f, barobj);

  if (!NILP (bar->next))
    XSETVECTOR (XSCROLL_BAR (bar->next)->prev, bar);

  unblock_input ();
  return bar;
}

static void
haiku_set_horizontal_scroll_bar (struct window *w, int portion, int whole, int position)
{
  Lisp_Object barobj;
  struct scroll_bar *bar;
  int top, height, left, width;
  int window_x, window_width;
  void *view;

  eassert (WINDOW_HAS_HORIZONTAL_SCROLL_BAR (w));
  /* Get window dimensions.  */
  window_box (w, ANY_AREA, &window_x, 0, &window_width, 0);
  left = window_x;
  width = window_width;
  top = WINDOW_SCROLL_BAR_AREA_Y (w);
  height = WINDOW_CONFIG_SCROLL_BAR_HEIGHT (w);
  view = FRAME_HAIKU_DRAWABLE (WINDOW_XFRAME (w));

  block_input ();

  if (NILP (w->horizontal_scroll_bar))
    {
      bar = haiku_scroll_bar_create (w, left, top, width, height, true);
      bar->update = position;
      bar->position = position;
      bar->total = whole;
    }
  else
    {
      bar = XSCROLL_BAR (w->horizontal_scroll_bar);

      if (bar->left != left || bar->top != top
	  || bar->width != width || bar->height != height)
	{
	  BView_forget_scroll_bar (view, bar->left, bar->top,
				   bar->width, bar->height);
	  BView_move_frame (bar->scroll_bar, left, top,
			    left + width - 1, top + height - 1);
	  BView_publish_scroll_bar (view, left, top, width, height);

	  bar->left = left;
	  bar->top = top;
	  bar->width = width;
	  bar->height = height;
	}
    }

  haiku_set_horizontal_scroll_bar_thumb (bar, portion, position, whole);
  bar->position = position;
  bar->total = whole;
  XSETVECTOR (barobj, bar);
  wset_horizontal_scroll_bar (w, barobj);
  unblock_input ();
}

static void
haiku_set_vertical_scroll_bar (struct window *w, int portion, int whole, int position)
{
  Lisp_Object barobj;
  struct scroll_bar *bar;
  int top, height, left, width;
  int window_y, window_height;
  void *view;

  eassert (WINDOW_HAS_VERTICAL_SCROLL_BAR (w));

  /* Get window dimensions.  */
  window_box (w, ANY_AREA, 0, &window_y, 0, &window_height);
  top = window_y;
  height = window_height;

  /* Compute the left edge and the width of the scroll bar area.  */
  left = WINDOW_SCROLL_BAR_AREA_X (w);
  width = WINDOW_SCROLL_BAR_AREA_WIDTH (w);

  view = FRAME_HAIKU_DRAWABLE (WINDOW_XFRAME (w));

  block_input ();
  if (NILP (w->vertical_scroll_bar))
    {
      bar = haiku_scroll_bar_create (w, left, top, width, height, false);
      bar->position = position;
      bar->total = whole;
    }
  else
    {
      bar = XSCROLL_BAR (w->vertical_scroll_bar);

      if (bar->left != left || bar->top != top
	  || bar->width != width || bar->height != height)
	{
	  BView_forget_scroll_bar (view, bar->left, bar->top,
				   bar->width, bar->height);
	  BView_move_frame (bar->scroll_bar, left, top,
			    left + width - 1, top + height - 1);
	  BView_publish_scroll_bar (view, left, top, width, height);

	  bar->left = left;
	  bar->top = top;
	  bar->width = width;
	  bar->height = height;
	}
    }

  haiku_set_scroll_bar_thumb (bar, portion, position, whole);
  bar->position = position;
  bar->total = whole;

  XSETVECTOR (barobj, bar);
  wset_vertical_scroll_bar (w, barobj);
  unblock_input ();
}

static void
haiku_draw_fringe_bitmap (struct window *w, struct glyph_row *row,
			  struct draw_fringe_bitmap_params *p)
{
  struct face *face;
  struct frame *f;
  struct haiku_bitmap_record *rec;
  void *view, *bitmap;
  uint32 col;

  f = XFRAME (WINDOW_FRAME (w));
  view = FRAME_HAIKU_DRAWABLE (f);
  face = p->face;

  block_input ();
  BView_draw_lock (view, true, 0, 0, 0, 0);
  BView_StartClip (view);

  if (p->wd && p->h)
    BView_invalidate_region (view, p->x, p->y, p->wd, p->h);

  haiku_clip_to_row (w, row, ANY_AREA);

  if (p->bx >= 0 && !p->overlay_p)
    {
      BView_invalidate_region (view, p->bx, p->by, p->nx, p->ny);

      if (!face->stipple)
	{
	  BView_SetHighColor (view, face->background);
	  BView_FillRectangle (view, p->bx, p->by, p->nx, p->ny);
	}
      else
	{
	  rec = haiku_get_bitmap_rec (f, face->stipple);
	  haiku_update_bitmap_rec (rec, face->foreground,
				   face->background);

	  BView_StartClip (view);
	  haiku_clip_to_row (w, row, ANY_AREA);
	  BView_ClipToRect (view, p->bx, p->by, p->nx, p->ny);
	  BView_DrawBitmapTiled (view, rec->img, 0, 0, -1, -1,
				 0, 0, FRAME_PIXEL_WIDTH (f),
				 FRAME_PIXEL_HEIGHT (f));
	  BView_EndClip (view);

	  row->stipple_p = true;
	}
    }

  if (p->which
      && p->which < max_fringe_bmp
      && p->which < max_used_fringe_bitmap)
    {
      bitmap = fringe_bmps[p->which];

      if (!bitmap)
	{
	  /* This fringe bitmap is known to fringe.c, but lacks the
	     BBitmap which shadows that bitmap.  This is typical to
	     define-fringe-bitmap being called when the selected frame
	     was not a GUI frame, for example, when packages that
	     define fringe bitmaps are loaded by a daemon Emacs.
	     Create the missing pattern now.  */
	  gui_define_fringe_bitmap (WINDOW_XFRAME (w), p->which);
	  bitmap = fringe_bmps[p->which];
	}

      if (!p->cursor_p)
	col = face->foreground;
      else if (p->overlay_p)
	col = face->background;
      else
	col = FRAME_CURSOR_COLOR (XFRAME (WINDOW_FRAME (w))).pixel;

      if (!p->overlay_p)
        {
	  BView_SetHighColor (view, face->background);
	  BView_FillRectangle (view, p->x, p->y, p->wd, p->h);
	}

      BView_SetLowColor (view, col);
      BView_DrawBitmapWithEraseOp (view, bitmap, p->x, p->y, p->wd, p->h);
    }
  BView_EndClip (view);
  BView_draw_unlock (view);
  unblock_input ();
}

static void
haiku_define_fringe_bitmap (int which, unsigned short *bits,
			    int h, int wd)
{
  if (which >= max_fringe_bmp)
    {
      int i = max_fringe_bmp;
      max_fringe_bmp = which + 20;
      fringe_bmps = !i ? xmalloc (max_fringe_bmp * sizeof (void *)) :
	xrealloc (fringe_bmps, max_fringe_bmp * sizeof (void *));

      while (i < max_fringe_bmp)
	fringe_bmps[i++] = NULL;
    }

  block_input ();
  fringe_bmps[which] = BBitmap_new (wd, h, 1);
  if (!fringe_bmps[which])
    memory_full (SIZE_MAX);
  BBitmap_import_fringe_bitmap (fringe_bmps[which], bits, wd, h);
  unblock_input ();
}

static void
haiku_destroy_fringe_bitmap (int which)
{
  if (which >= max_fringe_bmp)
    return;

  if (fringe_bmps[which])
    BBitmap_free (fringe_bmps[which]);
  fringe_bmps[which] = NULL;
}

static void
haiku_scroll_run (struct window *w, struct run *run)
{
  struct frame *f = XFRAME (w->frame);
  void *view = FRAME_HAIKU_DRAWABLE (f);
  int x, y, width, height, from_y, to_y, bottom_y;
  window_box (w, ANY_AREA, &x, &y, &width, &height);

  from_y = WINDOW_TO_FRAME_PIXEL_Y (w, run->current_y);
  to_y = WINDOW_TO_FRAME_PIXEL_Y (w, run->desired_y);
  bottom_y = y + height;

  if (to_y < from_y)
    {
      /* Scrolling up.  Make sure we don't copy part of the mode
	 line at the bottom.  */
      if (from_y + run->height > bottom_y)
	height = bottom_y - from_y;
      else
	height = run->height;
    }
  else
    {
      /* Scrolling down.  Make sure we don't copy over the mode line.
	 at the bottom.  */
      if (to_y + run->height > bottom_y)
	height = bottom_y - to_y;
      else
	height = run->height;
    }

  block_input ();
  gui_clear_cursor (w);

  BView_draw_lock (view, true, x, to_y, width, height);
  BView_StartClip (view);
  BView_CopyBits (view, x, from_y, width, height,
		  x, to_y, width, height);
  BView_EndClip (view);
  BView_draw_unlock (view);

  unblock_input ();
}

/* Haiku doesn't provide any way to get the frame actually underneath
   the pointer, so we typically return dpyinfo->last_mouse_frame if
   the display is grabbed and `track-mouse' is not `dropping' or
   `drag-source'; failing that, we return the selected frame, and
   finally a random window system frame (as long as `track-mouse' is
   not `drag-source') if that didn't work either.  */
static void
haiku_mouse_position (struct frame **fp, int insist, Lisp_Object *bar_window,
		      enum scroll_bar_part *part, Lisp_Object *x, Lisp_Object *y,
		      Time *timestamp)
{
  Lisp_Object frame, tail;
  struct frame *f1;
  int screen_x, screen_y;
  void *view;

  if (!fp)
    return;

  f1 = NULL;
  block_input ();

  FOR_EACH_FRAME (tail, frame)
    {
      if (FRAME_HAIKU_P (XFRAME (frame)))
	XFRAME (frame)->mouse_moved = false;
    }

  if (gui_mouse_grabbed (x_display_list)
      && !EQ (track_mouse, Qdropping)
      && !EQ (track_mouse, Qdrag_source))
    f1 = x_display_list->last_mouse_frame;
  else
    f1 = x_display_list->last_mouse_motion_frame;

  if (!f1 && FRAME_HAIKU_P (SELECTED_FRAME ()))
    f1 = SELECTED_FRAME ();

  if (!f1 || (!FRAME_HAIKU_P (f1) && (insist > 0)))
    FOR_EACH_FRAME (tail, frame)
      if (FRAME_HAIKU_P (XFRAME (frame)) &&
	  !FRAME_TOOLTIP_P (XFRAME (frame)))
	f1 = XFRAME (frame);

  if (f1 && FRAME_TOOLTIP_P (f1))
    f1 = NULL;

  if (f1 && FRAME_HAIKU_P (f1))
    {
      view = FRAME_HAIKU_VIEW (f1);

      if (view)
	{
	  BView_get_mouse (view, &screen_x, &screen_y);
	  remember_mouse_glyph (f1, screen_x, screen_y,
				&x_display_list->last_mouse_glyph);
	  x_display_list->last_mouse_glyph_frame = f1;

	  *bar_window = Qnil;
	  *part = scroll_bar_nowhere;

	  /* If track-mouse is `drag-source' and the mouse pointer is
	     certain to not be actually under the chosen frame, return
	     NULL in FP to at least try being consistent with X.  */
	  if (EQ (track_mouse, Qdrag_source)
	      && (screen_x < 0 || screen_y < 0
		  || screen_x >= FRAME_PIXEL_WIDTH (f1)
		  || screen_y >= FRAME_PIXEL_HEIGHT (f1)))
	    *fp = NULL;
	  else
	    *fp = f1;

	  *timestamp = x_display_list->last_mouse_movement_time;
	  XSETINT (*x, screen_x);
	  XSETINT (*y, screen_y);
	}
    }

  unblock_input ();
}

static void
haiku_flush (struct frame *f)
{
  /* This is needed for tooltip frames to work properly with double
     buffering.  */
  if (FRAME_DIRTY_P (f) && !buffer_flipping_blocked_p ())
    haiku_flip_buffers (f);

  /* The frame is complete again as its contents were just
     flushed.  */
  FRAME_COMPLETE_P (f) = true;

  if (FRAME_VISIBLE_P (f) && !FRAME_TOOLTIP_P (f))
    BWindow_Flush (FRAME_HAIKU_WINDOW (f));
}

static void
haiku_define_frame_cursor (struct frame *f, Emacs_Cursor cursor)
{
  if (FRAME_TOOLTIP_P (f))
    return;

  block_input ();
  if (!f->pointer_invisible && FRAME_HAIKU_VIEW (f)
      && !FRAME_OUTPUT_DATA (f)->hourglass_p)
    BView_set_view_cursor (FRAME_HAIKU_VIEW (f), cursor);
  unblock_input ();
  FRAME_OUTPUT_DATA (f)->current_cursor = cursor;
}

static void
haiku_default_font_parameter (struct frame *f, Lisp_Object parms)
{
  struct haiku_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
  Lisp_Object font_param = gui_display_get_arg (dpyinfo, parms, Qfont, NULL, NULL,
                                                RES_TYPE_STRING);
  Lisp_Object font = Qnil;
  if (BASE_EQ (font_param, Qunbound))
    font_param = Qnil;

  if (NILP (font_param))
    /* System font should take precedence over X resources.  We
       suggest this regardless of font-use-system-font because .emacs
       may not have been read yet.  Returning a font-spec is Haiku
       specific behavior.  */
    font = font_open_by_spec (f, Ffont_get_system_font ());

  if (NILP (font))
    font = (!NILP (font_param)
	    ? font_param
	    : gui_display_get_arg (dpyinfo, parms, Qfont,
				   "font", "Font",
				   RES_TYPE_STRING));

  if (! FONTP (font) && ! STRINGP (font))
    {
      const char **names = (const char *[]) { "monospace-12",
					      "Noto Sans Mono-12",
					      "Source Code Pro-12",
					      NULL };
      int i;

      for (i = 0; names[i]; i++)
        {
          font
            = font_open_by_name (f, build_unibyte_string (names[i]));
          if (!NILP (font))
            break;
        }
      if (NILP (font))
        error ("No suitable font was found");
    }

  gui_default_parameter (f, parms, Qfont, font, "font", "Font",
                         RES_TYPE_STRING);
}

static struct redisplay_interface haiku_redisplay_interface =
  {
    haiku_frame_parm_handlers,
    gui_produce_glyphs,
    gui_write_glyphs,
    gui_insert_glyphs,
    gui_clear_end_of_line,
    haiku_scroll_run,
    haiku_after_update_window_line,
    NULL, /* update_window_begin */
    NULL, /* update_window_end */
    haiku_flush,
    gui_clear_window_mouse_face,
    gui_get_glyph_overhangs,
    gui_fix_overlapping_area,
    haiku_draw_fringe_bitmap,
    haiku_define_fringe_bitmap,
    haiku_destroy_fringe_bitmap,
    haiku_compute_glyph_string_overhangs,
    haiku_draw_glyph_string,
    haiku_define_frame_cursor,
    haiku_clear_frame_area,
    haiku_clear_under_internal_border,
    haiku_draw_window_cursor,
    haiku_draw_vertical_window_border,
    haiku_draw_window_divider,
    NULL, /* shift glyphs for insert */
    haiku_show_hourglass,
    haiku_hide_hourglass,
    haiku_default_font_parameter,
  };

static void
haiku_make_fullscreen_consistent (struct frame *f)
{
  Lisp_Object lval;
  struct haiku_output *output;

  output = FRAME_OUTPUT_DATA (f);

  if (output->fullscreen_mode == FULLSCREEN_MODE_BOTH)
    lval = Qfullboth;
  else if (output->fullscreen_mode == FULLSCREEN_MODE_WIDTH)
    lval = Qfullwidth;
  else if (output->fullscreen_mode == FULLSCREEN_MODE_HEIGHT)
    lval = Qfullheight;
  else if (output->fullscreen_mode == FULLSCREEN_MODE_MAXIMIZED)
    lval = Qmaximized;
  else
    lval = Qnil;

  store_frame_param (f, Qfullscreen, lval);
}

static void
haiku_flush_dirty_back_buffer_on (struct frame *f)
{
  if (FRAME_GARBAGED_P (f)
      || buffer_flipping_blocked_p ()
      /* If the frame is not already up to date, do not flush buffers
	 on input, as that will result in flicker.  */
      || !FRAME_COMPLETE_P (f)
      || !FRAME_DIRTY_P (f))
    return;

  haiku_flip_buffers (f);
}

/* N.B. that support for TYPE must be explicitly added to
   haiku_read_socket.  */
void
haiku_wait_for_event (struct frame *f, int type)
{
  int input_blocked_to;
  object_wait_info info;
  specpdl_ref depth;

  input_blocked_to = interrupt_input_blocked;
  info.object = port_application_to_emacs;
  info.type = B_OBJECT_TYPE_PORT;
  info.events = B_EVENT_READ;

  depth = SPECPDL_INDEX ();
  specbind (Qinhibit_quit, Qt);

  FRAME_OUTPUT_DATA (f)->wait_for_event_type = type;

  while (FRAME_OUTPUT_DATA (f)->wait_for_event_type == type)
    {
      if (wait_for_objects (&info, 1) < B_OK)
	continue;

      pending_signals = true;
      /* This will call the read_socket_hook.  */
      totally_unblock_input ();
      interrupt_input_blocked = input_blocked_to;
      info.events = B_EVENT_READ;
    }

  unbind_to (depth, Qnil);
}

static int
haiku_read_socket (struct terminal *terminal, struct input_event *hold_quit)
{
  int message_count;
  void *buf;
  ssize_t b_size;
  int button_or_motion_p, do_help;
  enum haiku_event_type type;
  struct input_event inev, inev2;
  struct frame *mouse_frame;

  message_count = 0;
  button_or_motion_p = 0;
  do_help = 0;

  buf = alloca (200);

  block_input ();
  haiku_read_size (&b_size, false);
  while (b_size >= 0)
    {
      if (b_size > 200)
	emacs_abort ();

      EVENT_INIT (inev);
      EVENT_INIT (inev2);
      inev.kind = NO_EVENT;
      inev2.kind = NO_EVENT;
      inev.arg = Qnil;
      inev2.arg = Qnil;

      button_or_motion_p = 0;
      haiku_read (&type, buf, b_size);

      switch (type)
	{
	case QUIT_REQUESTED:
	  {
	    struct haiku_quit_requested_event *b = buf;
	    struct frame *f = haiku_window_to_frame (b->window);

	    if (!f)
	      continue;

	    inev.kind = DELETE_WINDOW_EVENT;
	    XSETFRAME (inev.frame_or_window, f);
	    break;
	  }
	case FRAME_RESIZED:
	  {
	    struct haiku_resize_event *b = buf;
	    struct frame *f = haiku_window_to_frame (b->window);

	    if (!f)
	      continue;

	    int width = lrint (b->width);
	    int height = lrint (b->height);

	    if (FRAME_OUTPUT_DATA (f)->wait_for_event_type
		== FRAME_RESIZED)
	      FRAME_OUTPUT_DATA (f)->wait_for_event_type = -1;

	    if (FRAME_TOOLTIP_P (f))
	      {
		if (FRAME_PIXEL_WIDTH (f) != width
		    || FRAME_PIXEL_HEIGHT (f) != height)
		  SET_FRAME_GARBAGED (f);

		FRAME_PIXEL_WIDTH (f) = width;
		FRAME_PIXEL_HEIGHT (f) = height;

		haiku_clear_under_internal_border (f);

		/* Flush the frame and flip buffers here.  It is
		   necessary for tooltips displayed inside menus, as
		   redisplay cannot happen.  */
		haiku_flush (f);
		continue;
	      }

	    BView_draw_lock (FRAME_HAIKU_DRAWABLE (f), false, 0, 0, 0, 0);
	    BView_resize_to (FRAME_HAIKU_DRAWABLE (f), width, height);
	    BView_draw_unlock (FRAME_HAIKU_DRAWABLE (f));

	    if (width != FRAME_PIXEL_WIDTH (f)
		|| height != FRAME_PIXEL_HEIGHT (f)
		|| (f->new_size_p
		    && ((f->new_width >= 0 && width != f->new_width)
			|| (f->new_height >= 0 && height != f->new_height))))
	      {
		change_frame_size (f, width, height, false, true, false);
		SET_FRAME_GARBAGED (f);
		cancel_mouse_face (f);
		haiku_clear_under_internal_border (f);
	      }

	    break;
	  }
	case FRAME_EXPOSED:
	  {
	    struct haiku_expose_event *b = buf;
	    struct frame *f = haiku_window_to_frame (b->window);

	    if (!f)
	      continue;

	    expose_frame (f, b->x, b->y, b->width, b->height);
	    haiku_clear_under_internal_border (f);
	    break;
	  }
	case KEY_DOWN:
	  {
	    struct haiku_key_event *b = buf;
	    Mouse_HLInfo *hlinfo = &x_display_list->mouse_highlight;
	    struct frame *f = haiku_window_to_frame (b->window);

	    if (!f)
	      continue;

	    /* If mouse-highlight is an integer, input clears out
	       mouse highlighting.  */
	    if (!hlinfo->mouse_face_hidden && FIXNUMP (Vmouse_highlight)
		&& (f == 0
		    || !EQ (f->tool_bar_window, hlinfo->mouse_face_window)
		    || !EQ (f->tab_bar_window, hlinfo->mouse_face_window)))
	      {
		mouse_frame = hlinfo->mouse_face_mouse_frame;

		clear_mouse_face (hlinfo);
		hlinfo->mouse_face_hidden = true;

		if (mouse_frame)
		  haiku_flush_dirty_back_buffer_on (mouse_frame);
	      }

	    inev.code = b->keysym ? b->keysym : b->multibyte_char;

	    if (b->keysym)
	      inev.kind = NON_ASCII_KEYSTROKE_EVENT;
	    else
	      inev.kind = inev.code > 127 ? MULTIBYTE_CHAR_KEYSTROKE_EVENT :
		ASCII_KEYSTROKE_EVENT;

	    inev.timestamp = b->time / 1000;
	    inev.modifiers = (haiku_modifiers_to_emacs (b->modifiers)
			      | (extra_keyboard_modifiers
				 & (meta_modifier
				    | hyper_modifier
				    | ctrl_modifier
				    | alt_modifier
				    | shift_modifier)));

	    XSETFRAME (inev.frame_or_window, f);
	    break;
	  }
	case ACTIVATION:
	  {
	    struct haiku_activation_event *b = buf;
	    struct frame *f = haiku_window_to_frame (b->window);

	    if (!f)
	      continue;

	    if ((x_display_list->focus_event_frame != f && b->activated_p)
		|| (x_display_list->focus_event_frame == f && !b->activated_p))
	      {
		haiku_new_focus_frame (b->activated_p ? f : NULL);
		if (b->activated_p)
		  x_display_list->focus_event_frame = f;
		else
		  x_display_list->focus_event_frame = NULL;
		inev.kind = b->activated_p ? FOCUS_IN_EVENT : FOCUS_OUT_EVENT;
		XSETFRAME (inev.frame_or_window, f);
	      }

	    break;
	  }
	case MENU_BAR_LEFT:
	  {
	    struct haiku_menu_bar_left_event *b = buf;
	    struct frame *f = haiku_window_to_frame (b->window);

	    if (!f)
	      continue;

	    if (b->y > 0 && b->y <= FRAME_PIXEL_HEIGHT (f)
		&& b->x > 0 && b->x <= FRAME_PIXEL_WIDTH (f))
	      break;

	    if (f->auto_lower && !popup_activated_p)
	      haiku_frame_raise_lower (f, 0);

	    break;
	  }
	case MOUSE_MOTION:
	  {
	    struct haiku_mouse_motion_event *b = buf;
	    struct frame *f = haiku_mouse_or_wdesc_frame (b->window, true);
	    Mouse_HLInfo *hlinfo = &x_display_list->mouse_highlight;
	    Lisp_Object frame;

	    if (!f)
	      continue;

	    if (FRAME_TOOLTIP_P (f))
	      {
		/* Dismiss the tooltip if the mouse moves onto a
		   tooltip frame (except when drag-and-drop is in
		   progress and we are trying to move the tooltip
		   along with the mouse pointer).  FIXME: for some
		   reason we don't get leave notification events for
		   this.  */

		if (any_help_event_p
		    && !(be_drag_and_drop_in_progress ()
			 && haiku_dnd_follow_tooltip)
		    && !((EQ (track_mouse, Qdrag_source)
			  || EQ (track_mouse, Qdropping))
			 && gui_mouse_grabbed (x_display_list)))
		  do_help = -1;
		break;
	      }

	    XSETFRAME (frame, f);

	    x_display_list->last_mouse_movement_time = b->time / 1000;
	    button_or_motion_p = 1;

	    if (hlinfo->mouse_face_hidden)
	      {
		hlinfo->mouse_face_hidden = false;
		clear_mouse_face (hlinfo);
		haiku_flush_dirty_back_buffer_on (f);
	      }

	    if (b->just_exited_p)
	      {
		Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (f);

		if (f == hlinfo->mouse_face_mouse_frame)
		  {
		    /* If we move outside the frame, then we're
		       certainly no longer on any text in the frame.  */
		    clear_mouse_face (hlinfo);
		    hlinfo->mouse_face_mouse_frame = 0;

		    haiku_flush_dirty_back_buffer_on (f);
		  }

		if (f == x_display_list->last_mouse_glyph_frame)
		  x_display_list->last_mouse_glyph_frame = NULL;

		if (f->auto_lower && !popup_activated_p
		    /* Don't do this if the mouse entered a scroll bar.  */
		    && !BView_inside_scroll_bar (FRAME_HAIKU_VIEW (f),
						 b->x, b->y))
		  {
		    /* If we're leaving towards the menu bar, don't
		       auto-lower here, and wait for a exit
		       notification from the menu bar instead.  */
		    if (b->x > FRAME_PIXEL_WIDTH (f)
			|| b->y >= FRAME_MENU_BAR_HEIGHT (f)
			|| b->x < 0
			|| b->y < 0)
		      haiku_frame_raise_lower (f, 0);
		  }

		haiku_new_focus_frame (x_display_list->focused_frame);

		if (any_help_event_p
		    && !((EQ (track_mouse, Qdrag_source)
			  || EQ (track_mouse, Qdropping))
			 && gui_mouse_grabbed (x_display_list)))
		  do_help = -1;
	      }
	    else
	      {
		struct haiku_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
		struct haiku_rect r = dpyinfo->last_mouse_glyph;

		/* For an unknown reason Haiku sends phantom motion events when a
		   tooltip frame is visible.  FIXME */
		if (FRAMEP (tip_frame)
		    && FRAME_LIVE_P (XFRAME (tip_frame))
		    && FRAME_VISIBLE_P (XFRAME (tip_frame))
		    && f == dpyinfo->last_mouse_motion_frame
		    && b->x == dpyinfo->last_mouse_motion_x
		    && b->y == dpyinfo->last_mouse_motion_y)
		  continue;

		dpyinfo->last_mouse_motion_x = b->x;
		dpyinfo->last_mouse_motion_y = b->y;
		dpyinfo->last_mouse_motion_frame = f;

		previous_help_echo_string = help_echo_string;
		help_echo_string = Qnil;

		/* A crossing event might be sent out-of-order with
		   regard to motion events from other windows, such as
		   when the mouse pointer rapidly moves from an
		   undecorated child frame to its parent.  This can
		   cause a failure to clear the mouse face on the
		   former if an event for the latter is read by Emacs
		   first and ends up showing the mouse face there.

		   Work around the problem by clearing the mouse face
		   now if it is currently shown on a different
		   frame.  */

		if (hlinfo->mouse_face_hidden
		    || (f != hlinfo->mouse_face_mouse_frame
			&& !NILP (hlinfo->mouse_face_window)))
		  {
		    hlinfo->mouse_face_hidden = 0;
		    clear_mouse_face (hlinfo);
		  }

		if (f != dpyinfo->last_mouse_glyph_frame
		    || b->x < r.x || b->x >= r.x + r.width
		    || b->y < r.y || b->y >= r.y + r.height)
		  {
		    f->mouse_moved = true;
		    note_mouse_highlight (f, b->x, b->y);
		    remember_mouse_glyph (f, b->x, b->y,
					  &FRAME_DISPLAY_INFO (f)->last_mouse_glyph);
		    dpyinfo->last_mouse_glyph_frame = f;
		  }
		else
		  help_echo_string = previous_help_echo_string;

		if (!NILP (Vmouse_autoselect_window))
		  {
		    static Lisp_Object last_mouse_window;
		    Lisp_Object window = window_from_coordinates (f, b->x, b->y, 0, 0, 0, 0);

		    if (WINDOWP (window)
			&& !EQ (window, last_mouse_window)
			&& !EQ (window, selected_window)
			&& !popup_activated_p
			&& !MINI_WINDOW_P (XWINDOW (selected_window))
			&& (!NILP (focus_follows_mouse)
			    || f == SELECTED_FRAME ()))
		      {
			inev2.kind = SELECT_WINDOW_EVENT;
			inev2.frame_or_window = window;
		      }

		    last_mouse_window = window;
		  }

		if (f->auto_raise)
		  {
		    if (!BWindow_is_active (FRAME_HAIKU_WINDOW (f)))
		      haiku_frame_raise_lower (f, 1);
		  }

		if (!NILP (help_echo_string)
		    || !NILP (previous_help_echo_string))
		  do_help = 1;

		if (b->dnd_message)
		  {
		    /* It doesn't make sense to show tooltips when
		       another program is dragging stuff over us.  */

		    if (any_help_event_p || do_help)
		      do_help = -1;

		    if (!be_drag_and_drop_in_progress ())
		      {
			inev.kind = DRAG_N_DROP_EVENT;
			inev.arg = Qlambda;

			XSETINT (inev.x, b->x);
			XSETINT (inev.y, b->y);
			XSETFRAME (inev.frame_or_window, f);
		      }
		    else
		      haiku_note_drag_motion ();

		    break;
		  }
	      }

	    if (FRAME_DIRTY_P (f))
	      haiku_flush_dirty_back_buffer_on (f);
	    break;
	  }
	case BUTTON_UP:
	case BUTTON_DOWN:
	  {
	    struct haiku_button_event *b = buf;
	    struct frame *f = haiku_mouse_or_wdesc_frame (b->window, false);
	    Lisp_Object tab_bar_arg = Qnil;
	    int tab_bar_p = 0, tool_bar_p = 0;
	    bool up_okay_p = false;
	    struct scroll_bar *bar;

	    if (popup_activated_p || !f)
	      continue;

	    inev.modifiers = haiku_modifiers_to_emacs (b->modifiers);
	    bar = haiku_scroll_bar_from_widget (b->scroll_bar, b->window);

	    x_display_list->last_mouse_glyph_frame = 0;
	    x_display_list->last_mouse_movement_time = b->time / 1000;
	    button_or_motion_p = 1;

	    /* Is this in the tab-bar?  */
	    if (WINDOWP (f->tab_bar_window)
		&& WINDOW_TOTAL_LINES (XWINDOW (f->tab_bar_window)))
	      {
		Lisp_Object window;
		int x = b->x;
		int y = b->y;

		window = window_from_coordinates (f, x, y, 0, true, true, true);
		tab_bar_p = EQ (window, f->tab_bar_window);

		if (tab_bar_p)
		  {
		    tab_bar_arg = handle_tab_bar_click
		      (f, x, y, type == BUTTON_DOWN, inev.modifiers);
		    haiku_flush_dirty_back_buffer_on (f);
		  }
	      }

	    if (WINDOWP (f->tool_bar_window)
		&& WINDOW_TOTAL_LINES (XWINDOW (f->tool_bar_window)))
	      {
		Lisp_Object window;
		int x = b->x;
		int y = b->y;

		window = window_from_coordinates (f, x, y, 0, true, true, true);
		tool_bar_p = (EQ (window, f->tool_bar_window)
			      && (type != BUTTON_UP
				  || f->last_tool_bar_item != -1));

		if (tool_bar_p)
		  {
		    handle_tool_bar_click
		      (f, x, y, type == BUTTON_DOWN, inev.modifiers);
		    haiku_flush_dirty_back_buffer_on (f);
		  }
	      }

	    if (type == BUTTON_UP)
	      {
		inev.modifiers |= up_modifier;
		up_okay_p = (x_display_list->grabbed & (1 << b->btn_no));
		x_display_list->grabbed &= ~(1 << b->btn_no);
	      }
	    else
	      {
		up_okay_p = true;
		inev.modifiers |= down_modifier;
		x_display_list->last_mouse_frame = f;
		x_display_list->grabbed |= (1 << b->btn_no);
		if (f && !tab_bar_p)
		  f->last_tab_bar_item = -1;
		if (f && !tool_bar_p)
		  f->last_tool_bar_item = -1;
	      }

	    if (bar)
	      {
		inev.kind = (bar->horizontal
			     ? HORIZONTAL_SCROLL_BAR_CLICK_EVENT
			     : SCROLL_BAR_CLICK_EVENT);
		inev.part = (bar->horizontal
			     ? scroll_bar_horizontal_handle
			     : scroll_bar_handle);
	      }
	    else if (up_okay_p
		     && !(tab_bar_p && NILP (tab_bar_arg))
		     && !tool_bar_p)
	      inev.kind = MOUSE_CLICK_EVENT;

	    inev.arg = tab_bar_arg;
	    inev.code = b->btn_no;

	    f->mouse_moved = false;

	    if (bar)
	      {
		if (bar->horizontal)
		  {
		    XSETINT (inev.x, min (max (0, b->x - bar->left),
					  bar->width));
		    XSETINT (inev.y, bar->width);
		  }
		else
		  {
		    XSETINT (inev.x, min (max (0, b->y - bar->top),
					  bar->height));
		    XSETINT (inev.y, bar->height);
		  }

		inev.frame_or_window = bar->window;
	      }
	    else
	      {
		XSETINT (inev.x, b->x);
		XSETINT (inev.y, b->y);
		XSETFRAME (inev.frame_or_window, f);
	      }

	    break;
	  }
	case ICONIFICATION:
	  {
	    struct haiku_iconification_event *b = buf;
	    struct frame *f = haiku_window_to_frame (b->window);

	    if (!f)
	      continue;

	    if (!b->iconified_p)
	      {
		SET_FRAME_VISIBLE (f, 1);
		SET_FRAME_ICONIFIED (f, 0);
		inev.kind = DEICONIFY_EVENT;

		/* Haiku doesn't expose frames on deiconification, but
		   if we are double-buffered, the previous screen
		   contents should have been preserved. */
		if (!EmacsView_double_buffered_p (FRAME_HAIKU_VIEW (f)))
		  {
		    SET_FRAME_GARBAGED (f);
		    expose_frame (f, 0, 0, 0, 0);
		  }
	      }
	    else
	      {
		SET_FRAME_VISIBLE (f, 0);
		SET_FRAME_ICONIFIED (f, 1);
		inev.kind = ICONIFY_EVENT;
	      }

	    XSETFRAME (inev.frame_or_window, f);
	    break;
	  }
	case MOVE_EVENT:
	  {
	    struct haiku_move_event *b = buf;
	    struct frame *f = haiku_window_to_frame (b->window);
	    int top, left;
	    struct frame *p;

	    if (!f)
	      continue;

	    FRAME_OUTPUT_DATA (f)->frame_x = b->x;
	    FRAME_OUTPUT_DATA (f)->frame_y = b->y;

	    if (FRAME_PARENT_FRAME (f))
	      haiku_coords_from_parent (f, &b->x, &b->y);

	    left = b->x - b->decorator_width;
	    top = b->y - b->decorator_height;

	    if (left != f->left_pos || top != f->top_pos)
	      {
		inev.kind = MOVE_FRAME_EVENT;

		XSETINT (inev.x, left);
		XSETINT (inev.y, top);

		f->left_pos = left;
		f->top_pos = top;

		p = FRAME_PARENT_FRAME (f);

		if (p)
		  EmacsWindow_move_weak_child (FRAME_HAIKU_WINDOW (p),
					       b->window, left, top);

		XSETFRAME (inev.frame_or_window, f);
	      }

	    haiku_make_fullscreen_consistent (f);
	    break;
	  }
	case SCROLL_BAR_VALUE_EVENT:
	  {
	    struct haiku_scroll_bar_value_event *b = buf;
	    struct scroll_bar *bar
	      = haiku_scroll_bar_from_widget (b->scroll_bar, b->window);
	    int portion, whole;

	    if (!bar)
	      continue;

	    struct window *w = XWINDOW (bar->window);

	    if (bar->update != -1)
	      {
		bar->update = -1;
		break;
	      }

	    if (bar->position != b->position)
	      {
		inev.kind = (bar->horizontal
			     ? HORIZONTAL_SCROLL_BAR_CLICK_EVENT :
			     SCROLL_BAR_CLICK_EVENT);
		inev.part = (bar->horizontal
			     ? scroll_bar_horizontal_handle
			     : scroll_bar_handle);

		if (bar->horizontal)
		  {
		    portion = bar->total * ((float) b->position
					    / BE_SB_MAX);
		    whole = (bar->total
			     * ((float) (BE_SB_MAX - bar->page_size)
				/ BE_SB_MAX));
		    portion = min (portion, whole);
		  }
		else
		  {
		    whole = BE_SB_MAX - bar->page_size;
		    portion = min (b->position, whole);
		  }

		XSETINT (inev.x, portion);
		XSETINT (inev.y, whole);
		XSETWINDOW (inev.frame_or_window, w);
	      }
	    break;
	  }
	case SCROLL_BAR_PART_EVENT:
	  {
	    struct haiku_scroll_bar_part_event *b = buf;
	    struct scroll_bar *bar
	      = haiku_scroll_bar_from_widget (b->scroll_bar, b->window);

	    if (!bar)
	      continue;

	    inev.kind = (bar->horizontal ? HORIZONTAL_SCROLL_BAR_CLICK_EVENT
			 : SCROLL_BAR_CLICK_EVENT);

	    bar->dragging = 0;

	    switch (b->part)
	      {
	      case HAIKU_SCROLL_BAR_UP_BUTTON:
		inev.part = (bar->horizontal
			     ? scroll_bar_left_arrow
			     : scroll_bar_up_arrow);
		break;
	      case HAIKU_SCROLL_BAR_DOWN_BUTTON:
		inev.part = (bar->horizontal
			     ? scroll_bar_right_arrow
			     : scroll_bar_down_arrow);
		break;
	      }

	    XSETINT (inev.x, 0);
	    XSETINT (inev.y, 0);
	    inev.frame_or_window = bar->window;

	    break;
	  }
	case SCROLL_BAR_DRAG_EVENT:
	  {
	    struct haiku_scroll_bar_drag_event *b = buf;
	    struct scroll_bar *bar
	      = haiku_scroll_bar_from_widget (b->scroll_bar, b->window);

	    if (!bar)
	      continue;

	    bar->dragging = b->dragging_p;
	    if (!b->dragging_p && bar->horizontal)
	      set_horizontal_scroll_bar (XWINDOW (bar->window));
	    else if (!b->dragging_p)
	      set_vertical_scroll_bar (XWINDOW (bar->window));
	    break;
	  }
	case WHEEL_MOVE_EVENT:
	  {
	    struct haiku_wheel_move_event *b = buf;
	    struct frame *f = haiku_window_to_frame (b->window);
	    int x, y, scroll_width, scroll_height;
	    static float px = 0.0f, py = 0.0f;
	    Lisp_Object wheel_window;

	    if (!f)
	      continue;

	    BView_get_mouse (FRAME_HAIKU_VIEW (f), &x, &y);

	    wheel_window = window_from_coordinates (f, x, y, 0, false, false, false);

	    if (NILP (wheel_window))
	      {
		scroll_width = FRAME_PIXEL_WIDTH (f);
		scroll_height = FRAME_PIXEL_HEIGHT (f);
	      }
	    else
	      {
		scroll_width = XWINDOW (wheel_window)->pixel_width;
		scroll_height = XWINDOW (wheel_window)->pixel_height;
	      }

	    inev.modifiers = haiku_modifiers_to_emacs (b->modifiers);

	    inev2.modifiers = inev.modifiers;

	    if (signbit (px) != signbit (b->delta_x))
	      px = 0;

	    if (signbit (py) != signbit (b->delta_y))
	      py = 0;

	    px += (b->delta_x
		   * powf (scroll_width, 2.0f / 3.0f));
	    py += (b->delta_y
		   * powf (scroll_height, 2.0f / 3.0f));

	    if (fabsf (py) >= FRAME_LINE_HEIGHT (f)
		|| fabsf (px) >= FRAME_COLUMN_WIDTH (f)
		|| !mwheel_coalesce_scroll_events)
	      {
		inev.kind = (fabsf (px) > fabsf (py)
			     ? HORIZ_WHEEL_EVENT
			     : WHEEL_EVENT);
		inev.code = 0;

		XSETINT (inev.x, x);
		XSETINT (inev.y, y);
		inev.arg = list3 (Qnil, make_float (-px),
				  make_float (-py));
		XSETFRAME (inev.frame_or_window, f);

		inev.modifiers |= (signbit (inev.kind == HORIZ_WHEEL_EVENT
					    ? px : py)
				   ? up_modifier
				   : down_modifier);
		py = 0.0f;
		px = 0.0f;

		if (be_drag_and_drop_in_progress ())
		  haiku_note_drag_wheel (&inev);
	      }

	    break;
	  }
	case MENU_BAR_RESIZE:
	  {
	    struct haiku_menu_bar_resize_event *b = buf;
	    struct frame *f = haiku_window_to_frame (b->window);

	    if (!f || !FRAME_EXTERNAL_MENU_BAR (f))
	      continue;

	    if (FRAME_OUTPUT_DATA (f)->wait_for_event_type
		== MENU_BAR_RESIZE)
	      FRAME_OUTPUT_DATA (f)->wait_for_event_type = -1;

	    int old_height = FRAME_MENU_BAR_HEIGHT (f);

	    FRAME_MENU_BAR_HEIGHT (f) = b->height;
	    FRAME_MENU_BAR_LINES (f)
	      = (b->height + FRAME_LINE_HEIGHT (f)) / FRAME_LINE_HEIGHT (f);

	    if (old_height != b->height)
	      {
		adjust_frame_size (f, -1, -1, 3, true, Qmenu_bar_lines);
		haiku_clear_under_internal_border (f);
	      }
	    break;
	  }
	case MENU_BAR_CLICK:
	  {
	    struct haiku_menu_bar_click_event *b = buf;
	    struct frame *f = haiku_window_to_frame (b->window);

	    if (!f || !FRAME_EXTERNAL_MENU_BAR (f))
	      continue;

	    if (!FRAME_OUTPUT_DATA (f)->saved_menu_event)
	      FRAME_OUTPUT_DATA (f)->saved_menu_event = xmalloc (sizeof *b);
	    *FRAME_OUTPUT_DATA (f)->saved_menu_event = *b;
	    inev.kind = MENU_BAR_ACTIVATE_EVENT;
	    XSETFRAME (inev.frame_or_window, f);
	    break;
	  }
	case MENU_BAR_OPEN:
	case MENU_BAR_CLOSE:
	  {
	    struct haiku_menu_bar_state_event *b = buf;
	    struct frame *f = haiku_window_to_frame (b->window);

	    if (!f || !FRAME_EXTERNAL_MENU_BAR (f))
	      continue;

	    if (type == MENU_BAR_OPEN)
	      {
		FRAME_OUTPUT_DATA (f)->menu_bar_open_p = 1;
		popup_activated_p += 1;
	      }
	    else
	      {
		if (!popup_activated_p)
		  emacs_abort ();

		if (FRAME_OUTPUT_DATA (f)->menu_bar_open_p)
		  {
		    FRAME_OUTPUT_DATA (f)->menu_bar_open_p = 0;
		    popup_activated_p -= 1;
		  }
	      }
	    break;
	  }
	case MENU_BAR_SELECT_EVENT:
	  {
	    struct haiku_menu_bar_select_event *b = buf;
	    struct frame *f = haiku_window_to_frame (b->window);

	    if (!f || !FRAME_EXTERNAL_MENU_BAR (f))
	      continue;

	    find_and_call_menu_selection (f, f->menu_bar_items_used,
					  f->menu_bar_vector, b->ptr);
	    break;
	  }
	case MENU_BAR_HELP_EVENT:
	  {
	    struct haiku_menu_bar_help_event *b = buf;

	    if (!popup_activated_p)
	      continue;

	    struct frame *f = haiku_window_to_frame (b->window);
	    if (!f || !FRAME_EXTERNAL_MENU_BAR (f)
		|| !FRAME_OUTPUT_DATA (f)->menu_bar_open_p)
	      continue;

	    run_menu_bar_help_event (f, b->mb_idx);
	    break;
	  }
	case ZOOM_EVENT:
	  {
	    struct haiku_zoom_event *b = buf;
	    struct frame *f = haiku_window_to_frame (b->window);

	    if (!f)
	      continue;

	    if (b->fullscreen_mode == FULLSCREEN_MODE_MAXIMIZED)
	      f->want_fullscreen = FULLSCREEN_NONE;
	    else
	      f->want_fullscreen = FULLSCREEN_MAXIMIZED;

	    FRAME_TERMINAL (f)->fullscreen_hook (f);
	    break;
	  }
	case DRAG_AND_DROP_EVENT:
	  {
	    struct haiku_drag_and_drop_event *b = buf;
	    struct frame *f = haiku_window_to_frame (b->window);

	    if (!f)
	      {
		BMessage_delete (b->message);
		continue;
	      }

	    inev.kind = DRAG_N_DROP_EVENT;
	    inev.arg = haiku_message_to_lisp (b->message);

	    XSETINT (inev.x, b->x);
	    XSETINT (inev.y, b->y);
	    XSETFRAME (inev.frame_or_window, f);

	    BMessage_delete (b->message);
	    break;
	  }
	case SCREEN_CHANGED_EVENT:
	  {
	    struct haiku_screen_changed_event *b = buf;

	    inev.kind = MONITORS_CHANGED_EVENT;
	    XSETTERMINAL (inev.arg, x_display_list->terminal);
	    inev.timestamp = b->when / 1000;
	    break;
	  }
	case CLIPBOARD_CHANGED_EVENT:
	  be_handle_clipboard_changed_message ();
	  break;
	case APP_QUIT_REQUESTED_EVENT:
	  inev.kind = SAVE_SESSION_EVENT;
	  inev.arg = Qt;
	  break;
	case FONT_CHANGE_EVENT:
	  /* This generates CONFIG_CHANGED_EVENTs, which are then
	     handled in Lisp.  */
	  haiku_handle_font_change_event (buf, &inev);
	  break;

	case NOTIFICATION_CLICK_EVENT:
	  /* This code doesn't function, but the why is unknown.  */
#if 0
	  {
	    struct haiku_notification_click_event *b = buf;

	    inev.kind = NOTIFICATION_CLICKED_EVENT;
	    inev.arg  = make_int (b->id);
	    break;
	  }
#endif /* 0 */

	case KEY_UP:
	case DUMMY_EVENT:
	default:
	  break;
	}

      haiku_read_size (&b_size, false);

      if (inev.kind != NO_EVENT)
	{
	  if (inev.kind != HELP_EVENT && !inev.timestamp)
	    inev.timestamp = (button_or_motion_p
			      ? x_display_list->last_mouse_movement_time
			      : system_time () / 1000);
	  kbd_buffer_store_event_hold (&inev, hold_quit);
	  ++message_count;
	}

      if (inev2.kind != NO_EVENT)
	{
	  if (inev2.kind != HELP_EVENT && !inev.timestamp)
	    inev2.timestamp = (button_or_motion_p
			       ? x_display_list->last_mouse_movement_time
			       : system_time () / 1000);
	  kbd_buffer_store_event_hold (&inev2, hold_quit);
	  ++message_count;
	}
    }

  if (do_help && !(hold_quit && hold_quit->kind != NO_EVENT))
    {
      Lisp_Object help_frame = Qnil;

      if (x_display_list->last_mouse_frame)
	XSETFRAME (help_frame,
		   x_display_list->last_mouse_frame);

      if (do_help > 0)
	{
	  any_help_event_p = true;
	  gen_help_event (help_echo_string, help_frame,
			  help_echo_window, help_echo_object,
			  help_echo_pos);
	}
      else
	{
	  help_echo_string = Qnil;
	  gen_help_event (Qnil, help_frame, Qnil, Qnil, 0);
	}
    }

  unblock_input ();

  return message_count;
}

static Lisp_Object
haiku_get_focus_frame (struct frame *f)
{
  Lisp_Object lisp_focus;
  struct frame *focus;

  focus = FRAME_DISPLAY_INFO (f)->focused_frame;

  if (!focus)
    return Qnil;

  XSETFRAME (lisp_focus, focus);
  return lisp_focus;
}

static void
haiku_frame_rehighlight (struct frame *frame)
{
  haiku_rehighlight ();
}

static void
haiku_delete_window (struct frame *f)
{
  check_window_system (f);
  haiku_free_frame_resources (f);
}

static void
haiku_free_pixmap (struct frame *f, Emacs_Pixmap pixmap)
{
  BBitmap_free (pixmap);
}

static void
haiku_flash (struct frame *f)
{
  /* Get the height not including a menu bar widget.  */
  int height = FRAME_PIXEL_HEIGHT (f);
  /* Height of each line to flash.  */
  int flash_height = FRAME_LINE_HEIGHT (f);
  /* These will be the left and right margins of the rectangles.  */
  int flash_left = FRAME_INTERNAL_BORDER_WIDTH (f);
  int flash_right = FRAME_PIXEL_WIDTH (f) - FRAME_INTERNAL_BORDER_WIDTH (f);
  int width = flash_right - flash_left;
  void *view = FRAME_HAIKU_DRAWABLE (f);
  object_wait_info info;
  bigtime_t wakeup;

  info.object = port_application_to_emacs;
  info.type = B_OBJECT_TYPE_PORT;
  info.events = B_EVENT_READ;
  wakeup = system_time () + 150000;

  BView_draw_lock (view, true, 0, 0, FRAME_PIXEL_WIDTH (f),
		   FRAME_PIXEL_HEIGHT (f));
  BView_StartClip (view);
  /* If window is tall, flash top and bottom line.  */
  if (height > 3 * FRAME_LINE_HEIGHT (f))
    {
      BView_InvertRect (view, flash_left,
			(FRAME_INTERNAL_BORDER_WIDTH (f)
			 + FRAME_TOP_MARGIN_HEIGHT (f)),
			width, flash_height);

      BView_InvertRect (view, flash_left,
			(height - flash_height
			 - FRAME_INTERNAL_BORDER_WIDTH (f)
			 - FRAME_BOTTOM_MARGIN_HEIGHT (f)),
			width, flash_height);
    }
  else
    /* If it is short, flash it all.  */
    BView_InvertRect (view, flash_left, FRAME_INTERNAL_BORDER_WIDTH (f),
		      width, height - 2 * FRAME_INTERNAL_BORDER_WIDTH (f));
  BView_EndClip (view);
  BView_draw_unlock (view);

  flush_frame (f);

  if (EmacsView_double_buffered_p (view))
    haiku_flip_buffers (f);

  /* Keep waiting until past the time wakeup or any input gets
     available.  */
  while (!detect_input_pending ())
    {
      /* Break if result would not be positive.  */
      if (wakeup < system_time ())
	break;

      /* Try to wait that long--but we might wake up sooner.  */
      wait_for_objects_etc (&info, 1, B_ABSOLUTE_TIMEOUT, wakeup);

      if (info.events & B_EVENT_READ)
	break;

      info.events = B_EVENT_READ;
    }

  BView_draw_lock (view, true, 0, 0, FRAME_PIXEL_WIDTH (f),
		   FRAME_PIXEL_HEIGHT (f));
  BView_StartClip (view);
  /* If window is tall, flash top and bottom line.  */
  if (height > 3 * FRAME_LINE_HEIGHT (f))
    {
      BView_InvertRect (view, flash_left,
			(FRAME_INTERNAL_BORDER_WIDTH (f)
			 + FRAME_TOP_MARGIN_HEIGHT (f)),
			width, flash_height);

      BView_InvertRect (view, flash_left,
			(height - flash_height
			 - FRAME_INTERNAL_BORDER_WIDTH (f)
			 - FRAME_BOTTOM_MARGIN_HEIGHT (f)),
			width, flash_height);
    }
  else
    /* If it is short, flash it all.  */
    BView_InvertRect (view, flash_left, FRAME_INTERNAL_BORDER_WIDTH (f),
		      width, height - 2 * FRAME_INTERNAL_BORDER_WIDTH (f));
  BView_EndClip (view);
  BView_draw_unlock (view);

  flush_frame (f);
  if (EmacsView_double_buffered_p (view))
    haiku_flip_buffers (f);
}

static void
haiku_beep (struct frame *f)
{
  if (visible_bell)
    {
      void *view = FRAME_HAIKU_VIEW (f);
      if (view)
	{
	  block_input ();
	  haiku_flash (f);
	  unblock_input ();
	}
    }
  else
    haiku_ring_bell ();
}

static void
haiku_toggle_invisible_pointer (struct frame *f, bool invisible_p)
{
  void *view = FRAME_HAIKU_VIEW (f);

  if (view && !FRAME_TOOLTIP_P (f))
    {
      block_input ();
      BView_set_view_cursor (view, (invisible_p
				    ? FRAME_OUTPUT_DATA (f)->no_cursor
				    : FRAME_OUTPUT_DATA (f)->current_cursor));
      f->pointer_invisible = invisible_p;
      unblock_input ();
    }
}

static void
haiku_fullscreen (struct frame *f)
{
  enum haiku_fullscreen_mode mode;

  /* When FRAME_OUTPUT_DATA (f)->configury_done is false, the frame is
     being created, and its regular width and height have not yet been
     set.  This function will be called again by haiku_create_frame,
     so do nothing.  */
  if (!FRAME_OUTPUT_DATA (f)->configury_done)
    return;

  if (f->want_fullscreen == FULLSCREEN_MAXIMIZED)
    mode = FULLSCREEN_MODE_MAXIMIZED;
  else if (f->want_fullscreen == FULLSCREEN_BOTH)
    mode = FULLSCREEN_MODE_BOTH;
  else if (f->want_fullscreen == FULLSCREEN_WIDTH)
    mode = FULLSCREEN_MODE_WIDTH;
  else if (f->want_fullscreen == FULLSCREEN_HEIGHT)
    mode = FULLSCREEN_MODE_HEIGHT;
  else
    mode = FULLSCREEN_MODE_NONE;

  f->want_fullscreen = FULLSCREEN_NONE;
  be_set_window_fullscreen_mode (FRAME_HAIKU_WINDOW (f), mode);
  FRAME_OUTPUT_DATA (f)->fullscreen_mode = mode;

  haiku_update_size_hints (f);
  haiku_make_fullscreen_consistent (f);
}

static struct terminal *
haiku_create_terminal (struct haiku_display_info *dpyinfo)
{
  struct terminal *terminal;

  terminal = create_terminal (output_haiku, &haiku_redisplay_interface);

  terminal->display_info.haiku = dpyinfo;
  dpyinfo->terminal = terminal;
  terminal->kboard = allocate_kboard (Qhaiku);

  terminal->iconify_frame_hook = haiku_iconify_frame;
  terminal->focus_frame_hook = haiku_focus_frame;
  terminal->ring_bell_hook = haiku_beep;
  terminal->popup_dialog_hook = haiku_popup_dialog;
  terminal->frame_visible_invisible_hook = haiku_set_frame_visible_invisible;
  terminal->set_frame_offset_hook = haiku_set_offset;
  terminal->delete_terminal_hook = haiku_delete_terminal;
  terminal->get_string_resource_hook = haiku_get_string_resource;
  terminal->set_new_font_hook = haiku_new_font;
  terminal->defined_color_hook = haiku_defined_color;
  terminal->set_window_size_hook = haiku_set_window_size;
  terminal->read_socket_hook = haiku_read_socket;
  terminal->implicit_set_name_hook = haiku_implicitly_set_name;
  terminal->mouse_position_hook = haiku_mouse_position;
  terminal->delete_frame_hook = haiku_delete_window;
  terminal->frame_up_to_date_hook = haiku_frame_up_to_date;
  terminal->buffer_flipping_unblocked_hook = haiku_buffer_flipping_unblocked_hook;
  terminal->clear_frame_hook = haiku_clear_frame;
  terminal->change_tab_bar_height_hook = haiku_change_tab_bar_height;
  terminal->change_tool_bar_height_hook = haiku_change_tool_bar_height;
  terminal->set_vertical_scroll_bar_hook = haiku_set_vertical_scroll_bar;
  terminal->set_horizontal_scroll_bar_hook = haiku_set_horizontal_scroll_bar;
  terminal->set_scroll_bar_default_height_hook = haiku_set_scroll_bar_default_height;
  terminal->set_scroll_bar_default_width_hook = haiku_set_scroll_bar_default_width;
  terminal->judge_scroll_bars_hook = haiku_judge_scroll_bars;
  terminal->condemn_scroll_bars_hook = haiku_condemn_scroll_bars;
  terminal->redeem_scroll_bar_hook = haiku_redeem_scroll_bar;
  terminal->update_begin_hook = haiku_update_begin;
  terminal->update_end_hook = haiku_update_end;
  terminal->frame_rehighlight_hook = haiku_frame_rehighlight;
  terminal->query_frame_background_color = haiku_query_frame_background_color;
  terminal->free_pixmap = haiku_free_pixmap;
  terminal->frame_raise_lower_hook = haiku_frame_raise_lower;
  terminal->menu_show_hook = haiku_menu_show;
  terminal->toggle_invisible_pointer_hook = haiku_toggle_invisible_pointer;
  terminal->fullscreen_hook = haiku_fullscreen;
  terminal->toolkit_position_hook = haiku_toolkit_position;
  terminal->activate_menubar_hook = haiku_activate_menubar;
  terminal->get_focus_frame = haiku_get_focus_frame;

  return terminal;
}

struct haiku_display_info *
haiku_term_init (void)
{
  struct haiku_display_info *dpyinfo;
  struct terminal *terminal;
  Lisp_Object color_file, color_map, system_name;
  ptrdiff_t nbytes;
  void *name_buffer;

  block_input ();

  Fset_input_interrupt_mode (Qt);
  baud_rate = 19200;
  dpyinfo = xzalloc (sizeof *dpyinfo);
  haiku_io_init ();

  if (port_application_to_emacs < B_OK
      || port_emacs_to_session_manager < B_OK)
    emacs_abort ();

  color_file = Fexpand_file_name (build_string ("rgb.txt"),
				  Fsymbol_value (Qdata_directory));
  color_map = Fx_load_color_file (color_file);

  if (NILP (color_map))
    fatal ("Could not read %s.\n", SDATA (color_file));

  dpyinfo->color_map = color_map;
  dpyinfo->display = BApplication_setup ();
  dpyinfo->next = x_display_list;
  dpyinfo->n_planes = be_get_display_planes ();
  be_get_display_resolution (&dpyinfo->resx, &dpyinfo->resy);

  x_display_list = dpyinfo;

  terminal = haiku_create_terminal (dpyinfo);
  if (current_kboard == initial_kboard)
    current_kboard = terminal->kboard;

  terminal->kboard->reference_count++;
  /* Never delete haiku displays -- there can only ever be one,
     anyhow.  */
  terminal->reference_count++;
  terminal->name = xstrdup ("be");

  dpyinfo->name_list_element = Fcons (build_string ("be"), Qnil);
  dpyinfo->smallest_font_height = 1;
  dpyinfo->smallest_char_width = 1;

  gui_init_fringe (terminal->rif);

#define ASSIGN_CURSOR(cursor, cursor_id)			\
  (dpyinfo->cursor = be_create_cursor_from_id (cursor_id))
  ASSIGN_CURSOR (text_cursor,			CURSOR_ID_I_BEAM);
  ASSIGN_CURSOR (nontext_cursor,		CURSOR_ID_SYSTEM_DEFAULT);
  ASSIGN_CURSOR (modeline_cursor,		CURSOR_ID_CONTEXT_MENU);
  ASSIGN_CURSOR (hand_cursor,			CURSOR_ID_GRAB);
  ASSIGN_CURSOR (hourglass_cursor,		CURSOR_ID_PROGRESS);
  ASSIGN_CURSOR (horizontal_drag_cursor,	CURSOR_ID_RESIZE_EAST_WEST);
  ASSIGN_CURSOR (vertical_drag_cursor,		CURSOR_ID_RESIZE_NORTH_SOUTH);
  ASSIGN_CURSOR (left_edge_cursor,		CURSOR_ID_RESIZE_WEST);
  ASSIGN_CURSOR (top_left_corner_cursor,	CURSOR_ID_RESIZE_NORTH_WEST);
  ASSIGN_CURSOR (top_edge_cursor,		CURSOR_ID_RESIZE_NORTH);
  ASSIGN_CURSOR (top_right_corner_cursor,	CURSOR_ID_RESIZE_NORTH_EAST);
  ASSIGN_CURSOR (right_edge_cursor,		CURSOR_ID_RESIZE_EAST);
  ASSIGN_CURSOR (bottom_right_corner_cursor,	CURSOR_ID_RESIZE_SOUTH_EAST);
  ASSIGN_CURSOR (bottom_edge_cursor,		CURSOR_ID_RESIZE_SOUTH);
  ASSIGN_CURSOR (bottom_left_corner_cursor,	CURSOR_ID_RESIZE_SOUTH_WEST);
  ASSIGN_CURSOR (no_cursor,			CURSOR_ID_NO_CURSOR);
#undef ASSIGN_CURSOR

  system_name = Fsystem_name ();

  if (STRINGP (system_name))
    {
      nbytes = sizeof "GNU Emacs" + sizeof " at ";

      if (ckd_add (&nbytes, nbytes, SBYTES (system_name)))
	memory_full (SIZE_MAX);

      name_buffer = alloca (nbytes);
      sprintf (name_buffer, "%s%s%s", "GNU Emacs",
	       " at ", SDATA (system_name));
      dpyinfo->default_name = build_string (name_buffer);
    }
  else
    dpyinfo->default_name = build_string ("GNU Emacs");

  haiku_start_watching_selections ();

  /* Start listening for font configuration changes.  */
  be_listen_font_settings ();
  unblock_input ();

  return dpyinfo;
}

void
put_xrm_resource (Lisp_Object name, Lisp_Object val)
{
  eassert (STRINGP (name));
  eassert (STRINGP (val) || NILP (val));

  Lisp_Object lval = assoc_no_quit (name, rdb);
  if (!NILP (lval))
    Fsetcdr (lval, val);
  else
    rdb = Fcons (Fcons (name, val), rdb);
}

void
haiku_clear_under_internal_border (struct frame *f)
{
  if (FRAME_INTERNAL_BORDER_WIDTH (f) > 0
      /* This is needed because tooltip frames set up the internal
	 border before init_frame_faces.  */
      && FRAME_FACE_CACHE (f))
    {
      int border = FRAME_INTERNAL_BORDER_WIDTH (f);
      int width = FRAME_PIXEL_WIDTH (f);
      int height = FRAME_PIXEL_HEIGHT (f);
      int margin = FRAME_TOP_MARGIN_HEIGHT (f);
      int bottom_margin = FRAME_BOTTOM_MARGIN_HEIGHT (f);
      int face_id = (FRAME_PARENT_FRAME (f)
		     ? (!NILP (Vface_remapping_alist)
			? lookup_basic_face (NULL, f,
					     CHILD_FRAME_BORDER_FACE_ID)
			: CHILD_FRAME_BORDER_FACE_ID)
		     : (!NILP (Vface_remapping_alist)
			? lookup_basic_face (NULL, f,
					     INTERNAL_BORDER_FACE_ID)
			: INTERNAL_BORDER_FACE_ID));
      struct face *face = FACE_FROM_ID_OR_NULL (f, face_id);
      void *view = FRAME_HAIKU_DRAWABLE (f);

      block_input ();
      BView_draw_lock (view, true, 0, 0, FRAME_PIXEL_WIDTH (f),
		       FRAME_PIXEL_HEIGHT (f));
      BView_StartClip (view);
      BView_ClipToRect (view, 0, 0, FRAME_PIXEL_WIDTH (f),
			FRAME_PIXEL_HEIGHT (f));

      if (face)
	BView_SetHighColor (view, face->background);
      else
	BView_SetHighColor (view, FRAME_BACKGROUND_PIXEL (f));

      BView_FillRectangle (view, 0, margin, width, border);
      BView_FillRectangle (view, 0, 0, border, height);
      BView_FillRectangle (view, 0, margin, width, border);
      BView_FillRectangle (view, width - border, 0, border, height);
      BView_FillRectangle (view, 0, height - bottom_margin - border,
			   width, border);
      BView_EndClip (view);
      BView_draw_unlock (view);
      unblock_input ();
    }
}

void
mark_haiku_display (void)
{
  if (x_display_list)
    {
      mark_object (x_display_list->color_map);
      mark_object (x_display_list->default_name);
    }
}

void
haiku_scroll_bar_remove (struct scroll_bar *bar)
{
  void *view;
  struct frame *f;

  f = WINDOW_XFRAME (XWINDOW (bar->window));
  view = FRAME_HAIKU_DRAWABLE (f);

  block_input ();
  BView_forget_scroll_bar (view, bar->left, bar->top,
			   bar->width, bar->height);
  BScrollBar_delete (bar->scroll_bar);
  expose_frame (WINDOW_XFRAME (XWINDOW (bar->window)),
		bar->left, bar->top, bar->width, bar->height);

  if (bar->horizontal)
    wset_horizontal_scroll_bar (XWINDOW (bar->window), Qnil);
  else
    wset_vertical_scroll_bar (XWINDOW (bar->window), Qnil);
  unblock_input ();
};

void
haiku_set_offset (struct frame *frame, int x, int y,
		  int change_gravity)
{
  Lisp_Object lframe;

  /* Don't allow moving a fullscreen frame: the semantics of that are
     unclear.  */

  XSETFRAME (lframe, frame);
  if (EQ (Fframe_parameter (lframe, Qfullscreen), Qfullboth)
      /* Only do this if the fullscreen status has actually been
	 applied.  */
      && frame->want_fullscreen == FULLSCREEN_NONE
      /* And if the configury during frame creation has been
	 completed.  Otherwise, there will be no valid "old position"
	 to go back to.  */
      && FRAME_OUTPUT_DATA (frame)->configury_done)
    return;

  if (change_gravity > 0)
    {
      frame->top_pos = y;
      frame->left_pos = x;
      frame->size_hint_flags &= ~ (XNegative | YNegative);
      if (x < 0)
	frame->size_hint_flags |= XNegative;
      if (y < 0)
	frame->size_hint_flags |= YNegative;
      frame->win_gravity = NorthWestGravity;
    }

  haiku_update_size_hints (frame);

  block_input ();
  if (change_gravity)
    BWindow_set_offset (FRAME_HAIKU_WINDOW (frame), x, y);
  unblock_input ();
}

#ifdef USE_BE_CAIRO
cairo_t *
haiku_begin_cr_clip (struct frame *f, struct glyph_string *s)
{
  cairo_t *cr = FRAME_CR_CONTEXT (f);

  if (!cr)
    return NULL;

  cairo_save (cr);
  return cr;
}

void
haiku_end_cr_clip (cairo_t *cr)
{
  if (!cr)
    return;

  cairo_restore (cr);
}
#endif

void
haiku_merge_cursor_foreground (struct glyph_string *s,
			       unsigned long *foreground_out,
			       unsigned long *background_out)
{
  unsigned long background = FRAME_CURSOR_COLOR (s->f).pixel;
  unsigned long foreground = s->face->background;

  if (background == foreground)
    foreground = s->face->background;
  if (background == foreground)
    foreground = FRAME_OUTPUT_DATA (s->f)->cursor_fg;
  if (background == foreground)
    foreground = s->face->foreground;

  if (background == s->face->background
      && foreground == s->face->foreground)
    {
      background = s->face->foreground;
      foreground = s->face->background;
    }

  if (foreground_out)
    *foreground_out = foreground;
  if (background_out)
    *background_out = background;
}

void
syms_of_haikuterm (void)
{
  DEFVAR_BOOL ("haiku-initialized", haiku_initialized,
     doc: /* Non-nil if the Haiku terminal backend has been initialized.  */);

  DEFVAR_BOOL ("x-use-underline-position-properties",
	       x_use_underline_position_properties,
     doc: /* SKIP: real doc in xterm.c.  */);
  x_use_underline_position_properties = 1;

  DEFVAR_BOOL ("x-underline-at-descent-line",
	       x_underline_at_descent_line,
     doc: /* SKIP: real doc in xterm.c.  */);
  x_underline_at_descent_line = 0;

  DEFVAR_LISP ("x-toolkit-scroll-bars", Vx_toolkit_scroll_bars,
     doc: /* SKIP: real doc in xterm.c.  */);
  Vx_toolkit_scroll_bars = Qt;

  DEFVAR_BOOL ("haiku-debug-on-fatal-error", haiku_debug_on_fatal_error,
     doc: /* If non-nil, Emacs will launch the system debugger upon a fatal error.  */);
  haiku_debug_on_fatal_error = 1;

  DEFSYM (Qshift, "shift");
  DEFSYM (Qcontrol, "control");
  DEFSYM (Qoption, "option");
  DEFSYM (Qcommand, "command");

  DEFSYM (Qdata_directory, "data-directory");

  DEFVAR_LISP ("haiku-meta-keysym", Vhaiku_meta_keysym,
     doc: /* Which key Emacs uses as the meta modifier.
This is either one of the symbols `shift', `control', `command', and
`option', or nil, in which case it is treated as `command'.

Setting it to any other value is equivalent to `command'.  */);
  Vhaiku_meta_keysym = Qnil;

  DEFVAR_LISP ("haiku-control-keysym", Vhaiku_control_keysym,
     doc: /* Which key Emacs uses as the control modifier.
This is either one of the symbols `shift', `control', `command', and
`option', or nil, in which case it is treated as `control'.

Setting it to any other value is equivalent to `control'.  */);
  Vhaiku_control_keysym = Qnil;

  DEFVAR_LISP ("haiku-super-keysym", Vhaiku_super_keysym,
     doc: /* Which key Emacs uses as the super modifier.
This is either one of the symbols `shift', `control', `command', and
`option', or nil, in which case it is treated as `option'.

Setting it to any other value is equivalent to `option'.  */);
  Vhaiku_super_keysym = Qnil;

  DEFVAR_LISP ("haiku-shift-keysym", Vhaiku_shift_keysym,
     doc: /* Which key Emacs uses as the shift modifier.
This is either one of the symbols `shift', `control', `command', and
`option', or nil, in which case it is treated as `shift'.

Setting it to any other value is equivalent to `shift'.  */);
  Vhaiku_shift_keysym = Qnil;

  DEFSYM (Qx_use_underline_position_properties,
	  "x-use-underline-position-properties");

  DEFSYM (Qx_underline_at_descent_line, "x-underline-at-descent-line");

  rdb = Qnil;
  staticpro (&rdb);

  Fprovide (Qhaiku, Qnil);
#ifdef USE_BE_CAIRO
  Fprovide (intern_c_string ("cairo"), Qnil);
#endif
}
