all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
* Haiku port try 2
       [not found] <87ee9tjjmt.fsf.ref@yahoo.com>
@ 2021-09-13  2:26 ` Po Lu via Emacs development discussions.
  2021-09-13 18:35   ` Alan Third
  0 siblings, 1 reply; 7+ messages in thread
From: Po Lu via Emacs development discussions. @ 2021-09-13  2:26 UTC (permalink / raw)
  To: emacs-devel

[-- Attachment #1: Type: text/plain, Size: 1677 bytes --]


Hi.  I'm attaching a new version of the port to the Haiku operating
system, which should be complete enough for general usage.  The patch
should apply cleanly to a current repository check-out.

I would appreciate it for someone using Haiku as his daily driver to
take his time to try this, and for someone to take the time to glance
over the patch and tell me if I missed something.  If it doesn't build
for you or crashes, please let me know.

I'm also not sure Emacs with this patch will still build on a few
platforms I can't test, namely macOS and MS-Windows.  It would be nice
for someone with a Macintosh to verify that Emacs still builds with this
patch in place, and inform me of any issues so I can rectify them.

Changes and known problems have been documented in NEWS, MACHINES, and
PROBLEMS where appropriate, and I think the updates to the Lisp
reference manual are complete, and that no updates to the other manuals
are necessitated by the scope of the changes currently present.  If
nobody finds anything missing, I hope that it will be appropriate to
mark the changes as documented in NEWS.

It would also be nice to expedite the paperwork; I haven't heard
anything from Craig Topham in a while.

This is probably too late to make it into Emacs 28, but perhaps I'm
being overly pessimistic?

The patch also includes a copy of the X Pixmap Library (which is free
software) with modifications made by me, as the libXpm shipped by Haiku
has a hard dependency on a running X server.  Because the port is built
as part of Emacs, there are probably some copyright related issues to
work out.  If there are any, how should I proceed with sorting them out?

Thanks.


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: haiku-emacs.patch --]
[-- Type: text/x-patch, Size: 747880 bytes --]

diff --git a/.gitignore b/.gitignore
index fcbc9cd7f4..0e7eda4f89 100644
--- a/.gitignore
+++ b/.gitignore
@@ -180,6 +180,7 @@ ID
 # Executables.
 *.exe
 a.out
+lib-src/be-resources
 lib-src/blessmail
 lib-src/ctags
 lib-src/ebrowse
@@ -201,6 +202,7 @@ nextstep/GNUstep/Emacs.base/Resources/Info-gnustep.plist
 src/bootstrap-emacs
 src/emacs
 src/emacs-[0-9]*
+src/Emacs
 src/temacs
 src/dmpstruct.h
 src/*.pdmp
diff --git a/Makefile.in b/Makefile.in
index 235b707673..8993b54518 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -102,6 +102,8 @@ HAVE_NATIVE_COMP =
 
 USE_STARTUP_NOTIFICATION = @USE_STARTUP_NOTIFICATION@
 
+HAVE_BE_APP = @HAVE_BE_APP@
+
 # ==================== Where To Install Things ====================
 
 # Location to install Emacs.app under GNUstep / macOS.
@@ -514,8 +516,14 @@ install-arch-dep:
 	$(MAKE) -C lib-src install
 ifeq (${ns_self_contained},no)
 	${INSTALL_PROGRAM} $(INSTALL_STRIP) src/emacs${EXEEXT} "$(DESTDIR)${bindir}/$(EMACSFULL)"
+ifeq (${HAVE_BE_APP},yes)
+	${INSTALL_PROGRAM} $(INSTALL_STRIP) src/Emacs "$(DESTDIR)${bindir}/Emacs"
+endif
 ifeq (${DUMPING},pdumper)
 	${INSTALL_DATA} src/emacs.pdmp "$(DESTDIR)${libexecdir}/emacs/${version}/${configuration}"/emacs.pdmp
+ifeq (${HAVE_BE_APP},yes)
+	${INSTALL_DATA} src/Emacs.pdmp "$(DESTDIR)${libexecdir}/emacs/${version}/${configuration}"/Emacs.pdmp
+endif
 endif
 	-chmod 755 "$(DESTDIR)${bindir}/$(EMACSFULL)"
 ifndef NO_BIN_LINK
@@ -880,7 +888,7 @@ $(1)_$(2):
 ###      target for GCC does not delete 'libgcc.a', because recompiling it
 ###      is rarely necessary and takes a lot of time.
 mostlyclean_dirs = src oldXMenu lwlib lib lib-src nt doc/emacs doc/misc \
-  doc/lispref doc/lispintro test
+  doc/lispref doc/lispintro test be_xpm
 
 $(foreach dir,$(mostlyclean_dirs),$(eval $(call submake_template,$(dir),mostlyclean)))
 
diff --git a/be_xpm/Attrib.c b/be_xpm/Attrib.c
new file mode 100644
index 0000000000..1b380470c9
--- /dev/null
+++ b/be_xpm/Attrib.c
@@ -0,0 +1,308 @@
+/*
+ * Copyright (C) 1989-95 GROUPE BULL
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * GROUPE BULL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Except as contained in this notice, the name of GROUPE BULL shall not be
+ * used in advertising or otherwise to promote the sale, use or other dealings
+ * in this Software without prior written authorization from GROUPE BULL.
+ */
+
+/*****************************************************************************\
+* Attrib.c:                                                                   *
+*                                                                             *
+*  XPM library                                                                *
+*  Functions related to the XpmAttributes structure                           *
+*                                                                             *
+*  Developed by Arnaud Le Hors                                                *
+\*****************************************************************************/
+
+/* October 2004, source code review by Thomas Biege <thomas@suse.de> */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include "XpmI.h"
+
+/* 3.2 backward compatibility code */
+LFUNC(CreateOldColorTable, int, (XpmColor *ct, unsigned int ncolors,
+				 XpmColor ***oldct));
+
+LFUNC(FreeOldColorTable, void, (XpmColor **colorTable, unsigned int ncolors));
+
+/*
+ * Create a colortable compatible with the old style colortable
+ */
+static int
+CreateOldColorTable(
+    XpmColor	  *ct,
+    unsigned int   ncolors,
+    XpmColor	***oldct)
+{
+    XpmColor **colorTable, **color;
+    unsigned int a;
+
+    if (ncolors >= UINT_MAX / sizeof(XpmColor *))
+	return XpmNoMemory;
+
+    colorTable = (XpmColor **) XpmMalloc(ncolors * sizeof(XpmColor *));
+    if (!colorTable) {
+	*oldct = NULL;
+	return (XpmNoMemory);
+    }
+    for (a = 0, color = colorTable; a < ncolors; a++, color++, ct++)
+	*color = ct;
+    *oldct = colorTable;
+    return (XpmSuccess);
+}
+
+static void
+FreeOldColorTable(
+    XpmColor	**colorTable,
+    unsigned int  ncolors)
+{
+    unsigned int a, b;
+    XpmColor **color;
+    char **sptr;
+
+    if (colorTable) {
+	for (a = 0, color = colorTable; a < ncolors; a++, color++) {
+	    for (b = 0, sptr = (char **) *color; b <= NKEYS; b++, sptr++)
+		if (*sptr)
+		    XpmFree(*sptr);
+	}
+	XpmFree(*colorTable);
+	XpmFree(colorTable);
+    }
+}
+
+/* end 3.2 bc */
+
+/*
+ * Free the computed color table
+ */
+void
+xpmFreeColorTable(
+    XpmColor	*colorTable,
+    int		 ncolors)
+{
+    int a, b;
+    XpmColor *color;
+    char **sptr;
+
+    if (colorTable) {
+	for (a = 0, color = colorTable; a < ncolors; a++, color++) {
+	    for (b = 0, sptr = (char **) color; b <= NKEYS; b++, sptr++)
+		if (*sptr)
+		    XpmFree(*sptr);
+	}
+	XpmFree(colorTable);
+    }
+}
+
+/*
+ * Free array of extensions
+ */
+void
+XpmFreeExtensions(
+    XpmExtension	*extensions,
+    int			 nextensions)
+{
+    unsigned int i, j, nlines;
+    XpmExtension *ext;
+    char **sptr;
+
+    if (extensions  && nextensions > 0) {
+	for (i = 0, ext = extensions; i < nextensions; i++, ext++) {
+	    if (ext->name)
+		XpmFree(ext->name);
+	    nlines = ext->nlines;
+	    for (j = 0, sptr = ext->lines; j < nlines; j++, sptr++)
+		if (sptr && *sptr)
+		    XpmFree(*sptr);
+	    if (ext->lines)
+		XpmFree(ext->lines);
+	}
+	XpmFree(extensions);
+    }
+}
+
+/*
+ * Return the XpmAttributes structure size
+ */
+
+int
+XpmAttributesSize(void)
+{
+    return sizeof(XpmAttributes);
+}
+
+/*
+ * Init returned data to free safely later on
+ */
+void
+xpmInitAttributes(XpmAttributes *attributes)
+{
+    if (attributes) {
+	attributes->pixels = NULL;
+	attributes->npixels = 0;
+	attributes->colorTable = NULL;
+	attributes->ncolors = 0;
+/* 3.2 backward compatibility code */
+	attributes->hints_cmt = NULL;
+	attributes->colors_cmt = NULL;
+	attributes->pixels_cmt = NULL;
+/* end 3.2 bc */
+	if (attributes->valuemask & XpmReturnExtensions) {
+	    attributes->extensions = NULL;
+	    attributes->nextensions = 0;
+	}
+	if (attributes->valuemask & XpmReturnAllocPixels) {
+	    attributes->alloc_pixels = NULL;
+	    attributes->nalloc_pixels = 0;
+	}
+    }
+}
+
+/*
+ * Fill in the XpmAttributes with the XpmImage and the XpmInfo
+ */
+void
+xpmSetAttributes(
+    XpmAttributes	*attributes,
+    XpmImage		*image,
+    XpmInfo		*info)
+{
+    if (attributes->valuemask & XpmReturnColorTable) {
+	attributes->colorTable = image->colorTable;
+	attributes->ncolors = image->ncolors;
+
+	/* avoid deletion of copied data */
+	image->ncolors = 0;
+	image->colorTable = NULL;
+    }
+/* 3.2 backward compatibility code */
+    else if (attributes->valuemask & XpmReturnInfos) {
+	int ErrorStatus;
+
+	ErrorStatus = CreateOldColorTable(image->colorTable, image->ncolors,
+					  (XpmColor ***)
+					  &attributes->colorTable);
+
+	/* if error just say we can't return requested data */
+	if (ErrorStatus != XpmSuccess) {
+	    attributes->valuemask &= ~XpmReturnInfos;
+	    if (!(attributes->valuemask & XpmReturnPixels)) {
+		XpmFree(attributes->pixels);
+		attributes->pixels = NULL;
+		attributes->npixels = 0;
+	    }
+	    attributes->ncolors = 0;
+	} else {
+	    attributes->ncolors = image->ncolors;
+	    attributes->hints_cmt = info->hints_cmt;
+	    attributes->colors_cmt = info->colors_cmt;
+	    attributes->pixels_cmt = info->pixels_cmt;
+
+	    /* avoid deletion of copied data */
+	    image->ncolors = 0;
+	    image->colorTable = NULL;
+	    info->hints_cmt = NULL;
+	    info->colors_cmt = NULL;
+	    info->pixels_cmt = NULL;
+	}
+    }
+/* end 3.2 bc */
+    if (attributes->valuemask & XpmReturnExtensions) {
+	attributes->extensions = info->extensions;
+	attributes->nextensions = info->nextensions;
+
+	/* avoid deletion of copied data */
+	info->extensions = NULL;
+	info->nextensions = 0;
+    }
+    if (info->valuemask & XpmHotspot) {
+	attributes->valuemask |= XpmHotspot;
+	attributes->x_hotspot = info->x_hotspot;
+	attributes->y_hotspot = info->y_hotspot;
+    }
+    attributes->valuemask |= XpmCharsPerPixel;
+    attributes->cpp = image->cpp;
+    attributes->valuemask |= XpmSize;
+    attributes->width = image->width;
+    attributes->height = image->height;
+}
+
+/*
+ * Free the XpmAttributes structure members
+ * but the structure itself
+ */
+void
+XpmFreeAttributes(XpmAttributes *attributes)
+{
+    if (attributes->valuemask & XpmReturnPixels && attributes->npixels) {
+	XpmFree(attributes->pixels);
+	attributes->pixels = NULL;
+	attributes->npixels = 0;
+    }
+    if (attributes->valuemask & XpmReturnColorTable) {
+	xpmFreeColorTable(attributes->colorTable, attributes->ncolors);
+	attributes->colorTable = NULL;
+	attributes->ncolors = 0;
+    }
+/* 3.2 backward compatibility code */
+    else if (attributes->valuemask & XpmInfos) {
+	if (attributes->colorTable) {
+	    FreeOldColorTable((XpmColor **) attributes->colorTable,
+			      attributes->ncolors);
+	    attributes->colorTable = NULL;
+	    attributes->ncolors = 0;
+	}
+	if (attributes->hints_cmt) {
+	    XpmFree(attributes->hints_cmt);
+	    attributes->hints_cmt = NULL;
+	}
+	if (attributes->colors_cmt) {
+	    XpmFree(attributes->colors_cmt);
+	    attributes->colors_cmt = NULL;
+	}
+	if (attributes->pixels_cmt) {
+	    XpmFree(attributes->pixels_cmt);
+	    attributes->pixels_cmt = NULL;
+	}
+	if (attributes->pixels) {
+	    XpmFree(attributes->pixels);
+	    attributes->pixels = NULL;
+	    attributes->npixels = 0;
+	}
+    }
+/* end 3.2 bc */
+    if (attributes->valuemask & XpmReturnExtensions
+	&& attributes->nextensions) {
+	XpmFreeExtensions(attributes->extensions, attributes->nextensions);
+	attributes->extensions = NULL;
+	attributes->nextensions = 0;
+    }
+    if (attributes->valuemask & XpmReturnAllocPixels
+	&& attributes->nalloc_pixels) {
+	XpmFree(attributes->alloc_pixels);
+	attributes->alloc_pixels = NULL;
+	attributes->nalloc_pixels = 0;
+    }
+    attributes->valuemask = 0;
+}
diff --git a/be_xpm/COPYING.libXpm b/be_xpm/COPYING.libXpm
new file mode 100644
index 0000000000..dac401302b
--- /dev/null
+++ b/be_xpm/COPYING.libXpm
@@ -0,0 +1,68 @@
+Copyright (C) 1989-95 GROUPE BULL
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+GROUPE BULL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of GROUPE BULL shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from GROUPE BULL.
+
+Copyright (C) 1998 Arnaud LE HORS
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+Arnaud LE HORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of Arnaud LE HORS shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from Arnaud LE HORS.
+
+Copyright (C) 1996 Lorens Younes
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+Lorens Younes BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of Lorens Younes shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from Lorens Younes.
\ No newline at end of file
diff --git a/be_xpm/CrBufFrI.c b/be_xpm/CrBufFrI.c
new file mode 100644
index 0000000000..dd242510a7
--- /dev/null
+++ b/be_xpm/CrBufFrI.c
@@ -0,0 +1,465 @@
+/*
+ * Copyright (C) 1989-95 GROUPE BULL
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL GROUPE BULL BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Except as contained in this notice, the name of GROUPE BULL shall
+ * not be used in advertising or otherwise to promote the sale, use or
+ * other dealings in this Software without prior written authorization
+ * from GROUPE BULL.
+ */
+
+/*****************************************************************************\
+*  CrBufFrI.c: *
+* *
+*  XPM library *
+*  Scan an image and possibly its mask and create an XPM buffer *
+* *
+*  Developed by Arnaud Le Hors *
+\*****************************************************************************/
+
+/* October 2004, source code review by Thomas Biege <thomas@suse.de>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include "XpmI.h"
+
+LFUNC (WriteColors, int,
+       (char **dataptr, unsigned int *data_size,
+        unsigned int *used_size, XpmColor *colors,
+        unsigned int ncolors, unsigned int cpp));
+
+LFUNC (WritePixels, void,
+       (char *dataptr, unsigned int data_size,
+        unsigned int *used_size, unsigned int width,
+        unsigned int height, unsigned int cpp, unsigned int *pixels,
+        XpmColor *colors));
+
+LFUNC (WriteExtensions, void,
+       (char *dataptr, unsigned int data_size,
+        unsigned int *used_size, XpmExtension *ext,
+        unsigned int num));
+
+LFUNC (ExtensionsSize, unsigned int,
+       (XpmExtension * ext, unsigned int num));
+LFUNC (CommentsSize, int, (XpmInfo * info));
+
+int
+XpmCreateBufferFromImage (Display *display, char **buffer_return,
+                          XImage *image, XImage *shapeimage,
+                          XpmAttributes *attributes)
+{
+  XpmImage xpmimage;
+  XpmInfo info;
+  int ErrorStatus;
+
+  /* initialize return value */
+  if (buffer_return)
+    *buffer_return = NULL;
+
+  /* create an XpmImage from the image */
+  ErrorStatus
+    = XpmCreateXpmImageFromImage (display, image, shapeimage,
+                                  &xpmimage, attributes);
+  if (ErrorStatus != XpmSuccess)
+    return (ErrorStatus);
+
+  /* create the buffer from the XpmImage */
+  if (attributes)
+    {
+      xpmSetInfo (&info, attributes);
+      ErrorStatus = XpmCreateBufferFromXpmImage (buffer_return,
+                                                 &xpmimage, &info);
+    }
+  else
+    ErrorStatus
+      = XpmCreateBufferFromXpmImage (buffer_return, &xpmimage, NULL);
+
+  /* free the XpmImage */
+  XpmFreeXpmImage (&xpmimage);
+
+  return (ErrorStatus);
+}
+
+#undef RETURN
+#define RETURN(status)      \
+  do                        \
+    {                       \
+      ErrorStatus = status; \
+      goto error;           \
+    }                       \
+  while (0)
+
+int
+XpmCreateBufferFromXpmImage (char **buffer_return, XpmImage *image,
+                             XpmInfo *info)
+{
+  /* calculation variables */
+  int ErrorStatus;
+  char buf[BUFSIZ];
+  unsigned int cmts, extensions, ext_size = 0;
+  unsigned int l, cmt_size = 0;
+  char *ptr = NULL, *p;
+  unsigned int ptr_size, used_size, tmp;
+
+  *buffer_return = NULL;
+
+  cmts = info && (info->valuemask & XpmComments);
+  extensions
+    = info && (info->valuemask & XpmExtensions) && info->nextensions;
+
+  /* compute the extensions and comments size */
+  if (extensions)
+    ext_size = ExtensionsSize (info->extensions, info->nextensions);
+  if (cmts)
+    cmt_size = CommentsSize (info);
+
+    /* write the header line */
+#ifndef VOID_SPRINTF
+  used_size =
+#endif
+    sprintf (buf, "/* XPM */\nstatic char * image_name[] = {\n");
+#ifdef VOID_SPRINTF
+  used_size = strlen (buf);
+#endif
+  ptr_size
+    = used_size + ext_size + cmt_size + 1; /* ptr_size can't be 0 */
+  if (ptr_size <= used_size || ptr_size <= ext_size
+      || ptr_size <= cmt_size)
+    {
+      return XpmNoMemory;
+    }
+  ptr = (char *) XpmMalloc (ptr_size);
+  if (!ptr)
+    return XpmNoMemory;
+  strcpy (ptr, buf);
+
+  /* write the values line */
+  if (cmts && info->hints_cmt)
+    {
+#ifndef VOID_SPRINTF
+      used_size +=
+#endif
+        snprintf (ptr + used_size, ptr_size - used_size, "/*%s*/\n",
+                  info->hints_cmt);
+#ifdef VOID_SPRINTF
+      used_size += strlen (info->hints_cmt) + 5;
+#endif
+    }
+#ifndef VOID_SPRINTF
+  l =
+#endif
+    sprintf (buf, "\"%d %d %d %d", image->width, image->height,
+             image->ncolors, image->cpp);
+#ifdef VOID_SPRINTF
+  l = strlen (buf);
+#endif
+
+  if (info && (info->valuemask & XpmHotspot))
+    {
+#ifndef VOID_SPRINTF
+      l +=
+#endif
+        snprintf (buf + l, sizeof (buf) - l, " %d %d",
+                  info->x_hotspot, info->y_hotspot);
+#ifdef VOID_SPRINTF
+      l = strlen (buf);
+#endif
+    }
+  if (extensions)
+    {
+#ifndef VOID_SPRINTF
+      l +=
+#endif
+        sprintf (buf + l, " XPMEXT");
+#ifdef VOID_SPRINTF
+      l = strlen (buf);
+#endif
+    }
+#ifndef VOID_SPRINTF
+  l +=
+#endif
+    sprintf (buf + l, "\",\n");
+#ifdef VOID_SPRINTF
+  l = strlen (buf);
+#endif
+  ptr_size += l;
+  if (ptr_size <= l)
+    RETURN (XpmNoMemory);
+  p = (char *) XpmRealloc (ptr, ptr_size);
+  if (!p)
+    RETURN (XpmNoMemory);
+  ptr = p;
+  strcpy (ptr + used_size, buf);
+  used_size += l;
+
+  /* write colors */
+  if (cmts && info->colors_cmt)
+    {
+#ifndef VOID_SPRINTF
+      used_size +=
+#endif
+        snprintf (ptr + used_size, ptr_size - used_size, "/*%s*/\n",
+                  info->colors_cmt);
+#ifdef VOID_SPRINTF
+      used_size += strlen (info->colors_cmt) + 5;
+#endif
+    }
+  ErrorStatus
+    = WriteColors (&ptr, &ptr_size, &used_size, image->colorTable,
+                   image->ncolors, image->cpp);
+
+  if (ErrorStatus != XpmSuccess)
+    RETURN (ErrorStatus);
+
+  /*
+   * now we know the exact size we need, realloc the data
+   * 4 = 1 (for '"') + 3 (for '",\n')
+   * 1 = - 2 (because the last line does not end with ',\n') + 3 (for
+   * '};\n')
+   */
+  if (image->width > UINT_MAX / image->cpp
+      || (tmp = image->width * image->cpp + 4) <= 4
+      || image->height > UINT_MAX / tmp
+      || (tmp = image->height * tmp + 1) <= 1
+      || (ptr_size += tmp) <= tmp)
+    RETURN (XpmNoMemory);
+
+  p = (char *) XpmRealloc (ptr, ptr_size);
+  if (!p)
+    RETURN (XpmNoMemory);
+  ptr = p;
+
+  /* print pixels */
+  if (cmts && info->pixels_cmt)
+    {
+#ifndef VOID_SPRINTF
+      used_size +=
+#endif
+        snprintf (ptr + used_size, ptr_size - used_size, "/*%s*/\n",
+                  info->pixels_cmt);
+#ifdef VOID_SPRINTF
+      used_size += strlen (info->pixels_cmt) + 5;
+#endif
+    }
+  WritePixels (ptr + used_size, ptr_size - used_size, &used_size,
+               image->width, image->height, image->cpp, image->data,
+               image->colorTable);
+
+  /* print extensions */
+  if (extensions)
+    WriteExtensions (ptr + used_size, ptr_size - used_size,
+                     &used_size, info->extensions, info->nextensions);
+
+  /* close the array */
+  strcpy (ptr + used_size, "};\n");
+
+  *buffer_return = ptr;
+
+  return (XpmSuccess);
+
+/* exit point in case of error, free only locally allocated variables
+ */
+error:
+  if (ptr)
+    XpmFree (ptr);
+  return (ErrorStatus);
+}
+
+static int
+WriteColors (char **dataptr, unsigned int *data_size,
+             unsigned int *used_size, XpmColor *colors,
+             unsigned int ncolors, unsigned int cpp)
+{
+  char buf[BUFSIZ] = { 0 };
+  unsigned int a, key, l;
+  char *s, *s2;
+  char **defaults;
+
+  *buf = '"';
+  for (a = 0; a < ncolors; a++, colors++)
+    {
+      defaults = (char **) colors;
+      s = buf + 1;
+      if (cpp > (sizeof (buf) - (s - buf)))
+        return (XpmNoMemory);
+      strncpy (s, *defaults++, cpp);
+      s += cpp;
+
+      for (key = 1; key <= NKEYS; key++, defaults++)
+        {
+          if ((s2 = *defaults))
+            {
+#ifndef VOID_SPRINTF
+              s +=
+#endif
+                /* assume C99 compliance */
+                snprintf (s, sizeof (buf) - (s - buf), "\t%s %s",
+                          xpmColorKeys[key - 1], s2);
+#ifdef VOID_SPRINTF
+              s += strlen (s);
+#endif
+              /* now let's check if s points out-of-bounds */
+              if ((s - buf) > sizeof (buf))
+                return (XpmNoMemory);
+            }
+        }
+      if (sizeof (buf) - (s - buf) < 4)
+        return (XpmNoMemory);
+      strcpy (s, "\",\n");
+      l = s + 3 - buf;
+      if (*data_size >= UINT_MAX - l || *data_size + l <= *used_size
+          || (*data_size + l - *used_size) <= sizeof (buf))
+        return (XpmNoMemory);
+      s = (char *) XpmRealloc (*dataptr, *data_size + l);
+      if (!s)
+        return (XpmNoMemory);
+      *data_size += l;
+      strcpy (s + *used_size, buf);
+      *used_size += l;
+      *dataptr = s;
+    }
+  return (XpmSuccess);
+}
+
+static void
+WritePixels (char *dataptr, unsigned int data_size,
+             unsigned int *used_size, unsigned int width,
+             unsigned int height, unsigned int cpp,
+             unsigned int *pixels, XpmColor *colors)
+{
+  char *s = dataptr;
+  unsigned int x, y, h;
+
+  if (height <= 1)
+    return;
+
+  h = height - 1;
+  for (y = 0; y < h; y++)
+    {
+      *s++ = '"';
+      for (x = 0; x < width; x++, pixels++)
+        {
+          if (cpp >= (data_size - (s - dataptr)))
+            return;
+          strncpy (s, colors[*pixels].string,
+                   cpp); /* how can we trust *pixels? :-\ */
+          s += cpp;
+        }
+      if ((data_size - (s - dataptr)) < 4)
+        return;
+      strcpy (s, "\",\n");
+      s += 3;
+    }
+  /* duplicate some code to avoid a test in the loop */
+  *s++ = '"';
+  for (x = 0; x < width; x++, pixels++)
+    {
+      if (cpp >= (data_size - (s - dataptr)))
+        return;
+      strncpy (s, colors[*pixels].string,
+               cpp); /* how can we trust *pixels? */
+      s += cpp;
+    }
+  *s++ = '"';
+  *used_size += s - dataptr;
+}
+
+static unsigned int
+ExtensionsSize (XpmExtension *ext, unsigned int num)
+{
+  unsigned int x, y, a, size;
+  char **line;
+
+  size = 0;
+  if (num == 0)
+    return (0); /* ok? */
+  for (x = 0; x < num; x++, ext++)
+    {
+      /* 11 = 10 (for ',\n"XPMEXT ') + 1 (for '"') */
+      size += strlen (ext->name) + 11;
+      a = ext->nlines; /* how can we trust ext->nlines to be not
+                          out-of-bounds? */
+      for (y = 0, line = ext->lines; y < a; y++, line++)
+        /* 4 = 3 (for ',\n"') + 1 (for '"') */
+        size += strlen (*line) + 4;
+    }
+  /* 13 is for ',\n"XPMENDEXT"' */
+  if (size > UINT_MAX - 13) /* unlikely */
+    return (0);
+  return size + 13;
+}
+
+static void
+WriteExtensions (char *dataptr, unsigned int data_size,
+                 unsigned int *used_size, XpmExtension *ext,
+                 unsigned int num)
+{
+  unsigned int x, y, a;
+  char **line;
+  char *s = dataptr;
+
+  for (x = 0; x < num; x++, ext++)
+    {
+#ifndef VOID_SPRINTF
+      s +=
+#endif
+        snprintf (s, data_size - (s - dataptr), ",\n\"XPMEXT %s\"",
+                  ext->name);
+#ifdef VOID_SPRINTF
+      s += strlen (ext->name) + 11;
+#endif
+      a = ext->nlines;
+      for (y = 0, line = ext->lines; y < a; y++, line++)
+        {
+#ifndef VOID_SPRINTF
+          s +=
+#endif
+            snprintf (s, data_size - (s - dataptr), ",\n\"%s\"",
+                      *line);
+#ifdef VOID_SPRINTF
+          s += strlen (*line) + 4;
+#endif
+        }
+    }
+  strncpy (s, ",\n\"XPMENDEXT\"", data_size - (s - dataptr) - 1);
+  *used_size += s - dataptr + 13;
+}
+
+static int
+CommentsSize (XpmInfo *info)
+{
+  int size = 0;
+
+  /* 5 = 2 (for "/_*") + 3 (for "*_/\n") */
+  /* wrap possible but *very* unlikely */
+  if (info->hints_cmt)
+    size += 5 + strlen (info->hints_cmt);
+
+  if (info->colors_cmt)
+    size += 5 + strlen (info->colors_cmt);
+
+  if (info->pixels_cmt)
+    size += 5 + strlen (info->pixels_cmt);
+
+  return size;
+}
diff --git a/be_xpm/CrDatFrI.c b/be_xpm/CrDatFrI.c
new file mode 100644
index 0000000000..07c50b9848
--- /dev/null
+++ b/be_xpm/CrDatFrI.c
@@ -0,0 +1,436 @@
+/*
+ * Copyright (C) 1989-95 GROUPE BULL
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL GROUPE BULL BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Except as contained in this notice, the name of GROUPE BULL shall
+ * not be used in advertising or otherwise to promote the sale, use or
+ * other dealings in this Software without prior written authorization
+ * from GROUPE BULL.
+ */
+
+/*****************************************************************************\
+*  CrDataFI.c: *
+* *
+*  XPM library *
+*  Scan an image and possibly its mask and create an XPM array *
+* *
+*  Developed by Arnaud Le Hors *
+\*****************************************************************************/
+
+/* October 2004, source code review by Thomas Biege <thomas@suse.de>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include "XpmI.h"
+
+LFUNC (CreateColors, int,
+       (char **dataptr, unsigned int *data_size, XpmColor *colors,
+        unsigned int ncolors, unsigned int cpp));
+
+LFUNC (CreatePixels, void,
+       (char **dataptr, unsigned int data_size, unsigned int width,
+        unsigned int height, unsigned int cpp, unsigned int *pixels,
+        XpmColor *colors));
+
+LFUNC (CountExtensions, int,
+       (XpmExtension * ext, unsigned int num, unsigned int *ext_size,
+        unsigned int *ext_nlines));
+
+LFUNC (CreateExtensions, void,
+       (char **dataptr, unsigned int data_size, unsigned int offset,
+        XpmExtension *ext, unsigned int num,
+        unsigned int ext_nlines));
+
+int
+XpmCreateDataFromImage (Display *display, char ***data_return,
+                        XImage *image, XImage *shapeimage,
+                        XpmAttributes *attributes)
+{
+  XpmImage xpmimage;
+  XpmInfo info;
+  int ErrorStatus;
+
+  /* initialize return value */
+  if (data_return)
+    *data_return = NULL;
+
+  /* create an XpmImage from the image */
+  ErrorStatus
+    = XpmCreateXpmImageFromImage (display, image, shapeimage,
+                                  &xpmimage, attributes);
+  if (ErrorStatus != XpmSuccess)
+    return (ErrorStatus);
+
+  /* create the data from the XpmImage */
+  if (attributes)
+    {
+      xpmSetInfo (&info, attributes);
+      ErrorStatus
+        = XpmCreateDataFromXpmImage (data_return, &xpmimage, &info);
+    }
+  else
+    ErrorStatus
+      = XpmCreateDataFromXpmImage (data_return, &xpmimage, NULL);
+
+  /* free the XpmImage */
+  XpmFreeXpmImage (&xpmimage);
+
+  return (ErrorStatus);
+}
+
+#undef RETURN
+#define RETURN(status)      \
+  do                        \
+    {                       \
+      ErrorStatus = status; \
+      goto exit;            \
+    }                       \
+  while (0)
+
+int
+XpmCreateDataFromXpmImage (char ***data_return, XpmImage *image,
+                           XpmInfo *info)
+{
+  /* calculation variables */
+  int ErrorStatus;
+  char buf[BUFSIZ];
+  char **header = NULL, **data, **sptr, **sptr2, *s;
+  unsigned int header_size, header_nlines;
+  unsigned int data_size, data_nlines;
+  unsigned int extensions = 0, ext_size = 0, ext_nlines = 0;
+  unsigned int offset, l, n;
+
+  *data_return = NULL;
+
+  extensions
+    = info && (info->valuemask & XpmExtensions) && info->nextensions;
+
+  /* compute the number of extensions lines and size */
+  if (extensions)
+    if (CountExtensions (info->extensions, info->nextensions,
+                         &ext_size, &ext_nlines))
+      return (XpmNoMemory);
+
+  /*
+   * alloc a temporary array of char pointer for the header section
+   * which is the hints line + the color table lines
+   */
+  header_nlines
+    = 1 + image->ncolors; /* this may wrap and/or become 0 */
+
+  /* 2nd check superfluous if we do not need header_nlines any further
+   */
+  if (header_nlines <= image->ncolors
+      || header_nlines >= UINT_MAX / sizeof (char *))
+    return (XpmNoMemory);
+
+  header_size = sizeof (char *) * header_nlines;
+  if (header_size >= UINT_MAX / sizeof (char *))
+    return (XpmNoMemory);
+  header = (char **)
+    XpmCalloc (header_size,
+               sizeof (char *)); /* can we trust image->ncolors */
+  if (!header)
+    return (XpmNoMemory);
+
+  /* print the hints line */
+  s = buf;
+#ifndef VOID_SPRINTF
+  s +=
+#endif
+    sprintf (s, "%d %d %d %d", image->width, image->height,
+             image->ncolors, image->cpp);
+#ifdef VOID_SPRINTF
+  s += strlen (s);
+#endif
+
+  if (info && (info->valuemask & XpmHotspot))
+    {
+#ifndef VOID_SPRINTF
+      s +=
+#endif
+        sprintf (s, " %d %d", info->x_hotspot, info->y_hotspot);
+#ifdef VOID_SPRINTF
+      s += strlen (s);
+#endif
+    }
+  if (extensions)
+    {
+      strcpy (s, " XPMEXT");
+      s += 7;
+    }
+  l = s - buf + 1;
+  *header = (char *) XpmMalloc (l);
+  if (!*header)
+    RETURN (XpmNoMemory);
+  header_size += l;
+  strcpy (*header, buf);
+
+  /* print colors */
+  ErrorStatus
+    = CreateColors (header + 1, &header_size, image->colorTable,
+                    image->ncolors, image->cpp);
+
+  if (ErrorStatus != XpmSuccess)
+    RETURN (ErrorStatus);
+
+  /* now we know the size needed, alloc the data and copy the header
+   * lines */
+  offset = image->width * image->cpp + 1;
+
+  if (offset <= image->width || offset <= image->cpp)
+    RETURN (XpmNoMemory);
+
+  if (image->height > UINT_MAX - ext_nlines
+      || image->height + ext_nlines >= UINT_MAX / sizeof (char *))
+    RETURN (XpmNoMemory);
+  data_size = (image->height + ext_nlines) * sizeof (char *);
+
+  if (image->height > UINT_MAX / offset
+      || image->height * offset > UINT_MAX - data_size)
+    RETURN (XpmNoMemory);
+  data_size += image->height * offset;
+
+  if (header_size > UINT_MAX - ext_size
+      || header_size + ext_size >= (UINT_MAX - data_size))
+    RETURN (XpmNoMemory);
+  data_size += header_size + ext_size;
+
+  data = (char **) XpmMalloc (data_size);
+  if (!data)
+    RETURN (XpmNoMemory);
+
+  data_nlines = header_nlines + image->height + ext_nlines;
+  *data = (char *) (data + data_nlines);
+
+  /* can header have less elements then n suggests? */
+  n = image->ncolors;
+  for (l = 0, sptr = data, sptr2 = header; l <= n && sptr && sptr2;
+       l++, sptr++, sptr2++)
+    {
+      strcpy (*sptr, *sptr2);
+      *(sptr + 1) = *sptr + strlen (*sptr2) + 1;
+    }
+
+  /* print pixels */
+  data[header_nlines]
+    = (char *) data + header_size
+      + (image->height + ext_nlines) * sizeof (char *);
+
+  CreatePixels (data + header_nlines, data_size - header_nlines,
+                image->width, image->height, image->cpp, image->data,
+                image->colorTable);
+
+  /* print extensions */
+  if (extensions)
+    CreateExtensions (data + header_nlines + image->height - 1,
+                      data_size - header_nlines - image->height + 1,
+                      offset, info->extensions, info->nextensions,
+                      ext_nlines);
+
+  *data_return = data;
+  ErrorStatus = XpmSuccess;
+
+/* exit point, free only locally allocated variables */
+exit:
+  if (header)
+    {
+      for (l = 0; l < header_nlines; l++)
+        if (header[l])
+          XpmFree (header[l]);
+      XpmFree (header);
+    }
+  return (ErrorStatus);
+}
+
+static int
+CreateColors (char **dataptr, unsigned int *data_size,
+              XpmColor *colors, unsigned int ncolors,
+              unsigned int cpp)
+{
+  char buf[BUFSIZ];
+  unsigned int a, key, l;
+  char *s, *s2;
+  char **defaults;
+
+  /* can ncolors be trusted here? */
+  for (a = 0; a < ncolors; a++, colors++, dataptr++)
+    {
+      defaults = (char **) colors;
+      if (sizeof (buf) <= cpp)
+        return (XpmNoMemory);
+      strncpy (buf, *defaults++, cpp);
+      s = buf + cpp;
+
+      if (sizeof (buf) <= (s - buf))
+        return XpmNoMemory;
+
+      for (key = 1; key <= NKEYS; key++, defaults++)
+        {
+          if ((s2 = *defaults))
+            {
+#ifndef VOID_SPRINTF
+              s +=
+#endif
+                /* assume C99 compliance */
+                snprintf (s, sizeof (buf) - (s - buf), "\t%s %s",
+                          xpmColorKeys[key - 1], s2);
+#ifdef VOID_SPRINTF
+              s += strlen (s);
+#endif
+              /* does s point out-of-bounds? */
+              if (sizeof (buf) < (s - buf))
+                return XpmNoMemory;
+            }
+        }
+      /* what about using strdup()? */
+      l = s - buf + 1;
+      s = (char *) XpmMalloc (l);
+      if (!s)
+        return (XpmNoMemory);
+      *data_size += l;
+      *dataptr = strcpy (s, buf);
+    }
+  return (XpmSuccess);
+}
+
+static void
+CreatePixels (char **dataptr, unsigned int data_size,
+              unsigned int width, unsigned int height,
+              unsigned int cpp, unsigned int *pixels,
+              XpmColor *colors)
+{
+  char *s;
+  unsigned int x, y, h, offset;
+
+  if (height <= 1)
+    return;
+
+  h = height - 1;
+
+  offset = width * cpp + 1;
+
+  if (offset <= width || offset <= cpp)
+    return;
+
+  /* why trust h? */
+  for (y = 0; y < h; y++, dataptr++)
+    {
+      s = *dataptr;
+      /* why trust width? */
+      for (x = 0; x < width; x++, pixels++)
+        {
+          if (cpp > (data_size - (s - *dataptr)))
+            return;
+          strncpy (s, colors[*pixels].string,
+                   cpp); /* why trust pixel? */
+          s += cpp;
+        }
+      *s = '\0';
+      if (offset > data_size)
+        return;
+      *(dataptr + 1) = *dataptr + offset;
+    }
+  /* duplicate some code to avoid a test in the loop */
+  s = *dataptr;
+  /* why trust width? */
+  for (x = 0; x < width; x++, pixels++)
+    {
+      if (cpp > data_size - (s - *dataptr))
+        return;
+      strncpy (s, colors[*pixels].string,
+               cpp); /* why should we trust *pixel? */
+      s += cpp;
+    }
+  *s = '\0';
+}
+
+static int
+CountExtensions (XpmExtension *ext, unsigned int num,
+                 unsigned int *ext_size, unsigned int *ext_nlines)
+{
+  size_t len;
+  unsigned int x, y, a, size, nlines;
+  char **line;
+
+  size = 0;
+  nlines = 0;
+  for (x = 0; x < num; x++, ext++)
+    {
+      /* 1 for the name */
+      if (ext->nlines == UINT_MAX
+          || nlines > UINT_MAX - ext->nlines - 1)
+        return (1);
+      nlines += ext->nlines + 1;
+      /* 8 = 7 (for "XPMEXT ") + 1 (for 0) */
+      len = strlen (ext->name) + 8;
+      if (len > UINT_MAX - size)
+        return (1);
+      size += len;
+      a = ext->nlines;
+      for (y = 0, line = ext->lines; y < a; y++, line++)
+        {
+          len = strlen (*line) + 1;
+          if (len > UINT_MAX - size)
+            return (1);
+          size += len;
+        }
+    }
+  if (size > UINT_MAX - 10 || nlines > UINT_MAX - 1)
+    return (1);
+  /* 10 and 1 are for the ending "XPMENDEXT" */
+  *ext_size = size + 10;
+  *ext_nlines = nlines + 1;
+  return (0);
+}
+
+static void
+CreateExtensions (char **dataptr, unsigned int data_size,
+                  unsigned int offset, XpmExtension *ext,
+                  unsigned int num, unsigned int ext_nlines)
+{
+  unsigned int x, y, a, b;
+  char **line;
+
+  *(dataptr + 1) = *dataptr + offset;
+  dataptr++;
+  a = 0;
+  for (x = 0; x < num; x++, ext++)
+    {
+      snprintf (*dataptr, data_size, "XPMEXT %s", ext->name);
+      a++;
+      if (a < ext_nlines)
+        *(dataptr + 1) = *dataptr + strlen (ext->name) + 8;
+      dataptr++;
+      b = ext->nlines; /* can we trust these values? */
+      for (y = 0, line = ext->lines; y < b; y++, line++)
+        {
+          strcpy (*dataptr, *line);
+          a++;
+          if (a < ext_nlines)
+            *(dataptr + 1) = *dataptr + strlen (*line) + 1;
+          dataptr++;
+        }
+    }
+  strcpy (*dataptr, "XPMENDEXT");
+}
diff --git a/be_xpm/CrDatFrP.c b/be_xpm/CrDatFrP.c
new file mode 100644
index 0000000000..0e17fee140
--- /dev/null
+++ b/be_xpm/CrDatFrP.c
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 1989-95 GROUPE BULL
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * GROUPE BULL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Except as contained in this notice, the name of GROUPE BULL shall not be
+ * used in advertising or otherwise to promote the sale, use or other dealings
+ * in this Software without prior written authorization from GROUPE BULL.
+ */
+
+/*****************************************************************************\
+*  CrDataFP.c:                                                                *
+*                                                                             *
+*  XPM library                                                                *
+*  Scan a pixmap and possibly its mask and create an XPM array                *
+*                                                                             *
+*  Developed by Arnaud Le Hors                                                *
+\*****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include "XpmI.h"
+
+int
+XpmCreateDataFromPixmap(
+    Display		  *display,
+    char		***data_return,
+    Pixmap		   pixmap,
+    Pixmap		   shapemask,
+    XpmAttributes	  *attributes)
+{
+    XImage *ximage = NULL;
+    XImage *shapeimage = NULL;
+    unsigned int width = 0;
+    unsigned int height = 0;
+    int ErrorStatus;
+
+    /* get geometry */
+    if (attributes && attributes->valuemask & XpmSize) {
+	width = attributes->width;
+	height = attributes->height;
+    }
+    /* get the ximages */
+    if (pixmap)
+	xpmCreateImageFromPixmap(display, pixmap, &ximage, &width, &height);
+    if (shapemask)
+	xpmCreateImageFromPixmap(display, shapemask, &shapeimage,
+				 &width, &height);
+
+    /* create the data */
+    ErrorStatus = XpmCreateDataFromImage(display, data_return, ximage,
+					 shapeimage, attributes);
+
+    /* destroy the ximages */
+    if (ximage)
+	XDestroyImage(ximage);
+    if (shapeimage)
+	XDestroyImage(shapeimage);
+
+    return (ErrorStatus);
+}
diff --git a/be_xpm/CrIFrBuf.c b/be_xpm/CrIFrBuf.c
new file mode 100644
index 0000000000..b6b12cad1c
--- /dev/null
+++ b/be_xpm/CrIFrBuf.c
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 1989-95 GROUPE BULL
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * GROUPE BULL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Except as contained in this notice, the name of GROUPE BULL shall not be
+ * used in advertising or otherwise to promote the sale, use or other dealings
+ * in this Software without prior written authorization from GROUPE BULL.
+ */
+
+/*****************************************************************************\
+*  CrIFrBuf.c:                                                                *
+*                                                                             *
+*  XPM library                                                                *
+*  Parse an Xpm buffer (file in memory) and create the image and possibly its *
+*  mask                                                                       *
+*  Developed by Arnaud Le Hors                                                *
+\*****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include "XpmI.h"
+
+LFUNC(OpenBuffer, void, (char *buffer, xpmData *mdata));
+
+int
+XpmCreateImageFromBuffer(
+    Display		 *display,
+    char		 *buffer,
+    XImage		**image_return,
+    XImage		**shapeimage_return,
+    XpmAttributes	 *attributes)
+{
+    XpmImage image;
+    XpmInfo info;
+    int ErrorStatus;
+    xpmData mdata;
+
+    xpmInitXpmImage(&image);
+    xpmInitXpmInfo(&info);
+
+    /* open buffer to read */
+    OpenBuffer(buffer, &mdata);
+
+    /* create the XImage from the XpmData */
+    if (attributes) {
+	xpmInitAttributes(attributes);
+	xpmSetInfoMask(&info, attributes);
+	ErrorStatus = xpmParseDataAndCreate(display, &mdata,
+					    image_return, shapeimage_return,
+					    &image, &info, attributes);
+    } else
+	ErrorStatus = xpmParseDataAndCreate(display, &mdata,
+					    image_return, shapeimage_return,
+					    &image, NULL, attributes);
+    if (attributes) {
+	if (ErrorStatus >= 0)		/* no fatal error */
+	    xpmSetAttributes(attributes, &image, &info);
+	XpmFreeXpmInfo(&info);
+    }
+
+    /* free the XpmImage */
+    XpmFreeXpmImage(&image);
+
+    return (ErrorStatus);
+}
+
+int
+XpmCreateXpmImageFromBuffer(
+    char	*buffer,
+    XpmImage	*image,
+    XpmInfo	*info)
+{
+    xpmData mdata;
+    int ErrorStatus;
+
+    /* init returned values */
+    xpmInitXpmImage(image);
+    xpmInitXpmInfo(info);
+
+    /* open buffer to read */
+    OpenBuffer(buffer, &mdata);
+
+    /* create the XpmImage from the XpmData */
+    ErrorStatus = xpmParseData(&mdata, image, info);
+
+    return (ErrorStatus);
+}
+
+/*
+ * open the given buffer to be read or written as an xpmData which is returned
+ */
+static void
+OpenBuffer(
+    char	*buffer,
+    xpmData	*mdata)
+{
+    mdata->type = XPMBUFFER;
+    mdata->cptr = buffer;
+    mdata->CommentLength = 0;
+}
diff --git a/be_xpm/CrIFrDat.c b/be_xpm/CrIFrDat.c
new file mode 100644
index 0000000000..9ef1557218
--- /dev/null
+++ b/be_xpm/CrIFrDat.c
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 1989-95 GROUPE BULL
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * GROUPE BULL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Except as contained in this notice, the name of GROUPE BULL shall not be
+ * used in advertising or otherwise to promote the sale, use or other dealings
+ * in this Software without prior written authorization from GROUPE BULL.
+ */
+
+/*****************************************************************************\
+*  CrIFrData.c:                                                               *
+*                                                                             *
+*  XPM library                                                                *
+*  Parse an Xpm array and create the image and possibly its mask              *
+*                                                                             *
+*  Developed by Arnaud Le Hors                                                *
+\*****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include "XpmI.h"
+
+LFUNC(OpenArray, void, (char **data, xpmData *mdata));
+
+int
+XpmCreateImageFromData(
+    Display		 *display,
+    char		**data,
+    XImage		**image_return,
+    XImage		**shapeimage_return,
+    XpmAttributes	 *attributes)
+{
+    XpmImage image;
+    XpmInfo info;
+    int ErrorStatus;
+    xpmData mdata;
+
+    xpmInitXpmImage(&image);
+    xpmInitXpmInfo(&info);
+
+    /* open data */
+    OpenArray(data, &mdata);
+
+    /* create an XpmImage from the file */
+    if (attributes) {
+	xpmInitAttributes(attributes);
+	xpmSetInfoMask(&info, attributes);
+	ErrorStatus = xpmParseDataAndCreate(display, &mdata,
+					    image_return, shapeimage_return,
+					    &image, &info, attributes);
+    } else
+	ErrorStatus = xpmParseDataAndCreate(display, &mdata,
+					    image_return, shapeimage_return,
+					    &image, NULL, attributes);
+    if (attributes) {
+	if (ErrorStatus >= 0)		/* no fatal error */
+	    xpmSetAttributes(attributes, &image, &info);
+	XpmFreeXpmInfo(&info);
+    }
+
+    /* free the XpmImage */
+    XpmFreeXpmImage(&image);
+
+    return (ErrorStatus);
+}
+
+int
+XpmCreateXpmImageFromData(
+    char	**data,
+    XpmImage	 *image,
+    XpmInfo	 *info)
+{
+    xpmData mdata;
+    int ErrorStatus;
+
+    /* init returned values */
+    xpmInitXpmImage(image);
+    xpmInitXpmInfo(info);
+
+    /* open data */
+    OpenArray(data, &mdata);
+
+    /* create the XpmImage from the XpmData */
+    ErrorStatus = xpmParseData(&mdata, image, info);
+
+    return (ErrorStatus);
+}
+
+/*
+ * open the given array to be read or written as an xpmData which is returned
+ */
+static void
+OpenArray(
+    char	**data,
+    xpmData	 *mdata)
+{
+    mdata->type = XPMARRAY;
+    mdata->stream.data = data;
+    mdata->cptr = *data;
+    mdata->line = 0;
+    mdata->CommentLength = 0;
+    mdata->Bcmt = mdata->Ecmt = NULL;
+    mdata->Bos = mdata->Eos = '\0';
+    mdata->format = 0;			/* this can only be Xpm 2 or 3 */
+}
diff --git a/be_xpm/Image.c b/be_xpm/Image.c
new file mode 100644
index 0000000000..e0223cc2a9
--- /dev/null
+++ b/be_xpm/Image.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 1989-95 GROUPE BULL
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * GROUPE BULL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Except as contained in this notice, the name of GROUPE BULL shall not be
+ * used in advertising or otherwise to promote the sale, use or other dealings
+ * in this Software without prior written authorization from GROUPE BULL.
+ */
+
+/*****************************************************************************\
+*  Image.c:                                                                   *
+*                                                                             *
+*  XPM library                                                                *
+*  Functions to init and free the XpmImage structure.                         *
+*                                                                             *
+*  Developed by Arnaud Le Hors                                                *
+\*****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include "XpmI.h"
+
+/*
+ * Init returned data to free safely later on
+ */
+void
+xpmInitXpmImage(XpmImage *image)
+{
+    image->ncolors = 0;
+    image->colorTable = NULL;
+    image->data = NULL;
+}
+
+/*
+ * Free the XpmImage data which have been allocated
+ */
+void
+XpmFreeXpmImage(XpmImage *image)
+{
+    if (image->colorTable)
+	xpmFreeColorTable(image->colorTable, image->ncolors);
+    if (image->data)
+	XpmFree(image->data);
+    image->data = NULL;
+}
diff --git a/be_xpm/Info.c b/be_xpm/Info.c
new file mode 100644
index 0000000000..e975a6dce6
--- /dev/null
+++ b/be_xpm/Info.c
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 1989-95 GROUPE BULL
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * GROUPE BULL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Except as contained in this notice, the name of GROUPE BULL shall not be
+ * used in advertising or otherwise to promote the sale, use or other dealings
+ * in this Software without prior written authorization from GROUPE BULL.
+ */
+
+/*****************************************************************************\
+*  Info.c:                                                                    *
+*                                                                             *
+*  XPM library                                                                *
+*  Functions related to the XpmInfo structure.                                *
+*                                                                             *
+*  Developed by Arnaud Le Hors                                                *
+\*****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include "XpmI.h"
+
+/*
+ * Init returned data to free safely later on
+ */
+void
+xpmInitXpmInfo(XpmInfo *info)
+{
+    if (info) {
+	info->hints_cmt = NULL;
+	info->colors_cmt = NULL;
+	info->pixels_cmt = NULL;
+	info->extensions = NULL;
+	info->nextensions = 0;
+    }
+}
+
+/*
+ * Free the XpmInfo data which have been allocated
+ */
+void
+XpmFreeXpmInfo(XpmInfo *info)
+{
+    if (info) {
+	if (info->valuemask & XpmComments) {
+	    if (info->hints_cmt) {
+		XpmFree(info->hints_cmt);
+		info->hints_cmt = NULL;
+	    }
+	    if (info->colors_cmt) {
+		XpmFree(info->colors_cmt);
+		info->colors_cmt = NULL;
+	    }
+	    if (info->pixels_cmt) {
+		XpmFree(info->pixels_cmt);
+		info->pixels_cmt = NULL;
+	    }
+	}
+	if (info->valuemask & XpmReturnExtensions && info->nextensions) {
+	    XpmFreeExtensions(info->extensions, info->nextensions);
+	    info->extensions = NULL;
+	    info->nextensions = 0;
+	}
+	info->valuemask = 0;
+    }
+}
+
+/*
+ * Set the XpmInfo valuemask to retrieve required info
+ */
+void
+xpmSetInfoMask(
+    XpmInfo		*info,
+    XpmAttributes	*attributes)
+{
+    info->valuemask = 0;
+    if (attributes->valuemask & XpmReturnInfos)
+	info->valuemask |= XpmReturnComments;
+    if (attributes->valuemask & XpmReturnExtensions)
+	info->valuemask |= XpmReturnExtensions;
+}
+
+/*
+ * Fill in the XpmInfo with the XpmAttributes
+ */
+void
+xpmSetInfo(
+    XpmInfo		*info,
+    XpmAttributes	*attributes)
+{
+    info->valuemask = 0;
+    if (attributes->valuemask & XpmInfos) {
+	info->valuemask |= XpmComments | XpmColorTable;
+	info->hints_cmt = attributes->hints_cmt;
+	info->colors_cmt = attributes->colors_cmt;
+	info->pixels_cmt = attributes->pixels_cmt;
+    }
+    if (attributes->valuemask & XpmExtensions) {
+	info->valuemask |= XpmExtensions;
+	info->extensions = attributes->extensions;
+	info->nextensions = attributes->nextensions;
+    }
+    if (attributes->valuemask & XpmHotspot) {
+	info->valuemask |= XpmHotspot;
+	info->x_hotspot = attributes->x_hotspot;
+	info->y_hotspot = attributes->y_hotspot;
+    }
+}
diff --git a/be_xpm/Makefile.in b/be_xpm/Makefile.in
new file mode 100644
index 0000000000..9c368f222e
--- /dev/null
+++ b/be_xpm/Makefile.in
@@ -0,0 +1,66 @@
+### @configure_input@
+
+# Copyright (C) 2021 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/>.
+
+CC=@CC@
+CFLAGS=@CFLAGS@
+CPPFLAGS = @CPPFLAGS@
+AR = @AR@
+
+top_builddir = @top_builddir@
+
+HAVE_BE_APP = @HAVE_BE_APP@
+BE_XPM_OBJECTS = Attrib.o Info.o CrBufFrI.o CrDatFrI.o \
+	create.o CrIFrBuf.o CrIFrDat.o data.o hashtab.o \
+	Image.o misc.o parse.o RdFToBuf.o RdFToDat.o RdFToI.o \
+	rgb.o scan.o
+RM = rm -f
+RANLIB = @RANLIB@
+AR = @AR@
+ARFLAGS = @ARFLAGS@
+
+-include ${top_builddir}/src/verbose.mk
+
+AUTO_DEPEND = @AUTO_DEPEND@
+DEPDIR = deps
+ifeq ($(AUTO_DEPEND),yes)
+  DEPFLAGS = -MMD -MF $(DEPDIR)/$*.d -MP
+  -include $(BE_XPM_OBJECTS:%.o=$(DEPDIR)/%.d)
+else
+  DEPFLAGS =
+  include $(srcdir)/deps.mk
+endif
+
+all: libXpmBe.a
+.PHONY: all
+
+.c.o:
+	$(AM_V_CC)$(CC) -c $(CPPFLAGS) -DFOR_MSW $(ALL_CFLAGS) -o $@ $<
+
+libXpmBe.a: $(BE_XPM_OBJECTS)
+	$(AM_V_GEN)$(RM) $@
+	$(AM_V_at)$(AR) $(ARFLAGS) $@ $(BE_XPM_OBJECTS) $(EXTRA)
+	$(AM_V_at)$(RANLIB) $@
+
+clean:
+	rm -f ./*.[ao] ./*/*.o ./*-t \#* $(DEPDIR)/*.d $(DEPDIR)/*/*.d
+mostlyclean: clean
+	rm -f $(filter-out %-t,$(MOSTLYCLEANFILES))
+distclean bootstrap-clean: mostlyclean
+	rm -f Makefile
+	rm -fr $(DEPDIR)
diff --git a/be_xpm/README b/be_xpm/README
new file mode 100644
index 0000000000..0fd7dcd1c2
--- /dev/null
+++ b/be_xpm/README
@@ -0,0 +1,10 @@
+libXpm - X Pixmap (XPM) image file format library
+-------------------------------------------------
+
+This directory contains a port of the X Pixmap library to Haiku; on
+other platforms, Emacs uses the system libXpm.
+
+Almost everything neccessary is included in this directory, with the
+notable exception of an implementation of everything in simx.h, which
+is actually in the main Emacs source folder under the name
+haiku_simx.c.
\ No newline at end of file
diff --git a/be_xpm/RdFToBuf.c b/be_xpm/RdFToBuf.c
new file mode 100644
index 0000000000..eda52c3ce7
--- /dev/null
+++ b/be_xpm/RdFToBuf.c
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 1989-95 GROUPE BULL
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * GROUPE BULL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Except as contained in this notice, the name of GROUPE BULL shall not be
+ * used in advertising or otherwise to promote the sale, use or other dealings
+ * in this Software without prior written authorization from GROUPE BULL.
+ */
+
+/*****************************************************************************\
+* RdFToBuf.c:                                                                 *
+*                                                                             *
+*  XPM library                                                                *
+*  Copy a file to a malloc'ed buffer, provided as a convenience.              *
+*                                                                             *
+*  Developed by Arnaud Le Hors                                                *
+\*****************************************************************************/
+
+/*
+ * The code related to FOR_MSW has been added by
+ * HeDu (hedu@cul-ipn.uni-kiel.de) 4/94
+ */
+
+/* October 2004, source code review by Thomas Biege <thomas@suse.de> */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include "XpmI.h"
+#include <sys/stat.h>
+#if !defined(FOR_MSW) && !defined(WIN32)
+#include <unistd.h>
+#endif
+#ifndef VAX11C
+#include <fcntl.h>
+#endif
+
+int
+XpmReadFileToBuffer(
+    const char	 *filename,
+    char	**buffer_return)
+{
+    int fd, fcheck;
+    off_t len;
+    char *ptr;
+    struct stat stats;
+    FILE *fp;
+
+    *buffer_return = NULL;
+
+#ifndef VAX11C
+    fd = open(filename, O_RDONLY);
+#else
+    fd = open(filename, O_RDONLY, NULL);
+#endif
+    if (fd < 0)
+	return XpmOpenFailed;
+
+    if (fstat(fd, &stats)) {
+	close(fd);
+	return XpmOpenFailed;
+    }
+    fp = fdopen(fd, "r");
+    if (!fp) {
+	close(fd);
+	return XpmOpenFailed;
+    }
+    len = stats.st_size;
+    if (len < 0 || len >= SIZE_MAX) {
+	fclose(fp);
+	return XpmOpenFailed;
+    }
+    ptr = (char *) XpmMalloc(len + 1);
+    if (!ptr) {
+	fclose(fp);
+	return XpmNoMemory;
+    }
+    fcheck = fread(ptr, 1, len, fp);
+    fclose(fp);
+#ifdef VMS
+    /* VMS often stores text files in a variable-length record format,
+       where there are two bytes of size followed by the record.  fread
+       converts this so it looks like a record followed by a newline.
+       Unfortunately, the size reported by fstat() (and fseek/ftell)
+       counts the two bytes for the record terminator, while fread()
+       counts only one.  So, fread() sees fewer bytes in the file (size
+       minus # of records) and thus when asked to read the amount
+       returned by stat(), it fails.
+       The best solution, suggested by DEC, seems to consider the length
+       returned from fstat() as an upper bound and call fread() with
+       a record length of 1. Then don't check the return value.
+       We'll check for 0 for gross error that's all.
+    */
+    len = fcheck;
+    if (fcheck == 0) {
+#else
+    if (fcheck != len) {
+#endif
+	XpmFree(ptr);
+	return XpmOpenFailed;
+    }
+    ptr[len] = '\0';
+    *buffer_return = ptr;
+    return XpmSuccess;
+}
diff --git a/be_xpm/RdFToDat.c b/be_xpm/RdFToDat.c
new file mode 100644
index 0000000000..fa92bd40a7
--- /dev/null
+++ b/be_xpm/RdFToDat.c
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 1989-95 GROUPE BULL
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * GROUPE BULL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Except as contained in this notice, the name of GROUPE BULL shall not be
+ * used in advertising or otherwise to promote the sale, use or other dealings
+ * in this Software without prior written authorization from GROUPE BULL.
+ */
+
+/*****************************************************************************\
+*  RdFToDat.c:                                                                *
+*                                                                             *
+*  XPM library                                                                *
+*  Parse an XPM file and create an array of strings corresponding to it.      *
+*                                                                             *
+*  Developed by Dan Greening dgreen@cs.ucla.edu / dgreen@sti.com              *
+\*****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include "XpmI.h"
+
+int
+XpmReadFileToData(
+    const char	  *filename,
+    char	***data_return)
+{
+    XpmImage image;
+    XpmInfo info;
+    int ErrorStatus;
+
+    info.valuemask = XpmReturnComments | XpmReturnExtensions;
+
+    /*
+     * initialize return value
+     */
+    if (data_return)
+	*data_return = NULL;
+
+    ErrorStatus = XpmReadFileToXpmImage(filename, &image, &info);
+    if (ErrorStatus != XpmSuccess)
+	return (ErrorStatus);
+
+    ErrorStatus =
+	XpmCreateDataFromXpmImage(data_return, &image, &info);
+
+    XpmFreeXpmImage(&image);
+    XpmFreeXpmInfo(&info);
+
+    return (ErrorStatus);
+}
diff --git a/be_xpm/RdFToI.c b/be_xpm/RdFToI.c
new file mode 100644
index 0000000000..bd09611b19
--- /dev/null
+++ b/be_xpm/RdFToI.c
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 1989-95 GROUPE BULL
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * GROUPE BULL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Except as contained in this notice, the name of GROUPE BULL shall not be
+ * used in advertising or otherwise to promote the sale, use or other dealings
+ * in this Software without prior written authorization from GROUPE BULL.
+ */
+
+/*****************************************************************************\
+*  RdFToI.c:                                                                  *
+*                                                                             *
+*  XPM library                                                                *
+*  Parse an XPM file and create the image and possibly its mask               *
+*                                                                             *
+*  Developed by Arnaud Le Hors                                                *
+\*****************************************************************************/
+
+/* October 2004, source code review by Thomas Biege <thomas@suse.de> */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include "XpmI.h"
+#ifndef NO_ZPIPE
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#else
+#ifdef FOR_MSW
+#include <fcntl.h>
+#endif
+#endif
+
+LFUNC(OpenReadFile, int, (const char *filename, xpmData *mdata));
+LFUNC(xpmDataClose, void, (xpmData *mdata));
+
+FUNC(xpmPipeThrough, FILE*, (int fd,
+			     const char *cmd,
+			     const char *arg1,
+			     const char *mode));
+
+#ifndef CXPMPROG
+int
+XpmReadFileToImage(
+    Display		 *display,
+    const char		 *filename,
+    XImage		**image_return,
+    XImage		**shapeimage_return,
+    XpmAttributes	 *attributes)
+{
+    XpmImage image;
+    XpmInfo info;
+    int ErrorStatus;
+    xpmData mdata;
+
+    xpmInitXpmImage(&image);
+    xpmInitXpmInfo(&info);
+
+    /* open file to read */
+    if ((ErrorStatus = OpenReadFile(filename, &mdata)) != XpmSuccess)
+	return (ErrorStatus);
+
+    /* create the XImage from the XpmData */
+    if (attributes) {
+	xpmInitAttributes(attributes);
+	xpmSetInfoMask(&info, attributes);
+	ErrorStatus = xpmParseDataAndCreate(display, &mdata,
+					    image_return, shapeimage_return,
+					    &image, &info, attributes);
+    } else
+	ErrorStatus = xpmParseDataAndCreate(display, &mdata,
+					    image_return, shapeimage_return,
+					    &image, NULL, attributes);
+    if (attributes) {
+	if (ErrorStatus >= 0)		/* no fatal error */
+	    xpmSetAttributes(attributes, &image, &info);
+	XpmFreeXpmInfo(&info);
+    }
+
+    xpmDataClose(&mdata);
+    /* free the XpmImage */
+    XpmFreeXpmImage(&image);
+
+    return (ErrorStatus);
+}
+
+int
+XpmReadFileToXpmImage(
+    const char	*filename,
+    XpmImage	*image,
+    XpmInfo	*info)
+{
+    xpmData mdata;
+    int ErrorStatus;
+
+    /* init returned values */
+    xpmInitXpmImage(image);
+    xpmInitXpmInfo(info);
+
+    /* open file to read */
+    if ((ErrorStatus = OpenReadFile(filename, &mdata)) != XpmSuccess)
+	return (ErrorStatus);
+
+    /* create the XpmImage from the XpmData */
+    ErrorStatus = xpmParseData(&mdata, image, info);
+
+    xpmDataClose(&mdata);
+
+    return (ErrorStatus);
+}
+#endif /* CXPMPROG */
+
+#ifndef NO_ZPIPE
+/* Do not depend on errno after read_through */
+FILE*
+xpmPipeThrough(
+    int		 fd,
+    const char	*cmd,
+    const char	*arg1,
+    const char	*mode)
+{
+    FILE* fp;
+    int status, fds[2], in = 0, out = 1;
+    pid_t pid;
+    if ( 'w' == *mode )
+	out = 0, in = 1;
+    if ( pipe(fds) < 0 )
+	return NULL;
+    pid = fork();
+    if ( pid < 0 )
+	goto fail1;
+    if ( 0 == pid )
+    {
+	close(fds[in]);
+	if ( dup2(fds[out], out) < 0 )
+	    goto err;
+	close(fds[out]);
+	if ( dup2(fd, in) < 0 )
+	    goto err;
+	close(fd);
+	pid = fork();
+	if ( pid < 0 )
+	    goto err;
+	if ( 0 == pid )
+	{
+	    execlp(cmd, cmd, arg1, (char *)NULL);
+	    perror(cmd);
+	    goto err;
+	}
+	_exit(0);
+    err:
+	_exit(1);
+    }
+    close(fds[out]);
+    /* calling process: wait for first child */
+    while ( waitpid(pid, &status, 0) < 0 && EINTR == errno )
+	;
+    if ( WIFSIGNALED(status) ||
+	 (WIFEXITED(status) && WEXITSTATUS(status) != 0) )
+	goto fail2;
+    fp = fdopen(fds[in], mode);
+    if ( !fp )
+	goto fail2;
+    close(fd); /* still open in 2nd child */
+    return fp;
+fail1:
+    close(fds[out]);
+fail2:
+    close(fds[in]);
+    return NULL;
+}
+#endif
+
+/*
+ * open the given file to be read as an xpmData which is returned.
+ */
+static int
+OpenReadFile(
+    const char	*filename,
+    xpmData	*mdata)
+{
+    if (!filename) {
+	mdata->stream.file = (stdin);
+	mdata->type = XPMFILE;
+    } else {
+	int fd = open(filename, O_RDONLY);
+#if defined(NO_ZPIPE)
+	if ( fd < 0 )
+	    return XpmOpenFailed;
+#else
+	const char* ext = NULL;
+	if ( fd >= 0 )
+	    ext = strrchr(filename, '.');
+#ifdef STAT_ZFILE /* searching for z-files if the given name not found */
+	else
+	{
+	    size_t len = strlen(filename);
+	    char *compressfile = (char *) XpmMalloc(len + 4);
+	    if ( !compressfile )
+		return (XpmNoMemory);
+	    strcpy(compressfile, filename);
+	    strcpy(compressfile + len, ext = ".Z");
+	    fd = open(compressfile, O_RDONLY);
+	    if ( fd < 0 )
+	    {
+		strcpy(compressfile + len, ext = ".gz");
+		fd = open(compressfile, O_RDONLY);
+		if ( fd < 0 )
+		{
+		    XpmFree(compressfile);
+		    return XpmOpenFailed;
+		}
+	    }
+	    XpmFree(compressfile);
+	}
+#endif
+	if ( ext && !strcmp(ext, ".Z") )
+	{
+	    mdata->type = XPMPIPE;
+	    mdata->stream.file = xpmPipeThrough(fd, "uncompress", "-c", "r");
+	}
+	else if ( ext && !strcmp(ext, ".gz") )
+	{
+	    mdata->type = XPMPIPE;
+	    mdata->stream.file = xpmPipeThrough(fd, "gunzip", "-qc", "r");
+	}
+	else
+#endif /* z-files */
+	{
+	    mdata->type = XPMFILE;
+	    mdata->stream.file = fdopen(fd, "r");
+	}
+	if (!mdata->stream.file)
+	{
+	    close(fd);
+	    return (XpmOpenFailed);
+	}
+    }
+    mdata->CommentLength = 0;
+#ifdef CXPMPROG
+    mdata->lineNum = 0;
+    mdata->charNum = 0;
+#endif
+    return (XpmSuccess);
+}
+
+/*
+ * close the file related to the xpmData if any
+ */
+static void
+xpmDataClose(xpmData *mdata)
+{
+    if (mdata->stream.file != (stdin))
+	fclose(mdata->stream.file);
+}
diff --git a/be_xpm/XpmI.h b/be_xpm/XpmI.h
new file mode 100644
index 0000000000..4a251594f9
--- /dev/null
+++ b/be_xpm/XpmI.h
@@ -0,0 +1,334 @@
+/*
+ * Copyright (C) 1989-95 GROUPE BULL
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * GROUPE BULL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Except as contained in this notice, the name of GROUPE BULL shall not be
+ * used in advertising or otherwise to promote the sale, use or other dealings
+ * in this Software without prior written authorization from GROUPE BULL.
+ */
+
+/*****************************************************************************\
+* XpmI.h:                                                                     *
+*                                                                             *
+*  XPM library                                                                *
+*  Internal Include file                                                      *
+*                                                                             *
+*  ** Everything defined here is subject to changes any time. **              *
+*                                                                             *
+*  Developed by Arnaud Le Hors                                                *
+\*****************************************************************************/
+
+/*
+ * The code related to FOR_MSW has been added by
+ * HeDu (hedu@cul-ipn.uni-kiel.de) 4/94
+ */
+
+#ifndef XPMI_h
+#define XPMI_h
+
+#include "xpm_be.h"
+
+/*
+ * lets try to solve include files
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+/* stdio.h doesn't declare popen on a Sequent DYNIX OS */
+#ifdef sequent
+extern FILE *popen();
+#endif
+
+#ifdef FOR_MSW
+#include "simx.h"
+#else
+#include <X11/Xos.h>
+#include <X11/Xfuncs.h>
+#include <X11/Xmd.h>
+#endif
+
+#ifdef VMS
+#include <unixio.h>
+#include <file.h>
+#endif
+
+/* The following should help people wanting to use their own memory allocation
+ * functions. To avoid the overhead of a function call when the standard
+ * functions are used these are all macros, even the XpmFree function which
+ * needs to be a real function for the outside world though.
+ * So if change these be sure to change the XpmFree function in misc.c
+ * accordingly.
+ */
+#define XpmFree(ptr) free(ptr)
+
+#ifndef FOR_MSW
+#define XpmMalloc(size) malloc((size))
+#define XpmRealloc(ptr, size) realloc((ptr), (size))
+#define XpmCalloc(nelem, elsize) calloc((nelem), (elsize))
+#else
+/* checks for mallocs bigger than 64K */
+#define XpmMalloc(size) boundCheckingMalloc((long)(size))/* in simx.[ch] */
+#define XpmRealloc(ptr, size) boundCheckingRealloc((ptr),(long)(size))
+#define XpmCalloc(nelem, elsize) \
+		boundCheckingCalloc((long)(nelem),(long) (elsize))
+#endif
+
+#if defined(SCO) || defined(__USLC__)
+#include <stdint.h>	/* For SIZE_MAX */
+#endif
+#include <limits.h>
+#ifndef SIZE_MAX
+# ifdef ULONG_MAX
+#  define SIZE_MAX ULONG_MAX
+# else
+#  define SIZE_MAX UINT_MAX
+# endif
+#endif
+
+#define XPMMAXCMTLEN BUFSIZ
+typedef struct {
+    unsigned int type;
+    union {
+	FILE *file;
+	char **data;
+    }     stream;
+    char *cptr;
+    unsigned int line;
+    int CommentLength;
+    char Comment[XPMMAXCMTLEN];
+    const char *Bcmt, *Ecmt;
+    char Bos, Eos;
+    int format;			/* 1 if XPM1, 0 otherwise */
+#ifdef CXPMPROG
+    int lineNum;
+    int charNum;
+#endif
+}      xpmData;
+
+#define XPMARRAY 0
+#define XPMFILE  1
+#define XPMPIPE  2
+#define XPMBUFFER 3
+
+#define EOL '\n'
+#define TAB '\t'
+#define SPC ' '
+
+typedef struct {
+    const char *type;		/* key word */
+    const char *Bcmt;		/* string beginning comments */
+    const char *Ecmt;		/* string ending comments */
+    char Bos;			/* character beginning strings */
+    char Eos;			/* character ending strings */
+    const char *Strs;		/* strings separator */
+    const char *Dec;		/* data declaration string */
+    const char *Boa;		/* string beginning assignment */
+    const char *Eoa;		/* string ending assignment */
+}      xpmDataType;
+
+extern xpmDataType xpmDataTypes[];
+
+/*
+ * rgb values and ascii names (from rgb text file) rgb values,
+ * range of 0 -> 65535 color mnemonic of rgb value
+ */
+typedef struct {
+    int r, g, b;
+    char *name;
+}      xpmRgbName;
+
+/* Maximum number of rgb mnemonics allowed in rgb text file. */
+#define MAX_RGBNAMES 1024
+
+extern const char *xpmColorKeys[];
+
+#define TRANSPARENT_COLOR "None"	/* this must be a string! */
+
+/* number of xpmColorKeys */
+#define NKEYS 5
+
+/* XPM internal routines */
+
+FUNC(xpmParseData, int, (xpmData *data, XpmImage *image, XpmInfo *info));
+FUNC(xpmParseDataAndCreate, int, (Display *display, xpmData *data,
+				  XImage **image_return,
+				  XImage **shapeimage_return,
+				  XpmImage *image, XpmInfo *info,
+				  XpmAttributes *attributes));
+
+FUNC(xpmFreeColorTable, void, (XpmColor *colorTable, int ncolors));
+
+FUNC(xpmInitAttributes, void, (XpmAttributes *attributes));
+
+FUNC(xpmInitXpmImage, void, (XpmImage *image));
+
+FUNC(xpmInitXpmInfo, void, (XpmInfo *info));
+
+FUNC(xpmSetInfoMask, void, (XpmInfo *info, XpmAttributes *attributes));
+FUNC(xpmSetInfo, void, (XpmInfo *info, XpmAttributes *attributes));
+FUNC(xpmSetAttributes, void, (XpmAttributes *attributes, XpmImage *image,
+			      XpmInfo *info));
+
+#if !defined(FOR_MSW) && !defined(AMIGA)
+FUNC(xpmCreatePixmapFromImage, void, (Display *display, Drawable d,
+				      XImage *ximage, Pixmap *pixmap_return));
+
+FUNC(xpmCreateImageFromPixmap, void, (Display *display, Pixmap pixmap,
+				      XImage **ximage_return,
+				      unsigned int *width,
+				      unsigned int *height));
+#endif
+
+/* structures and functions related to hastable code */
+
+typedef struct _xpmHashAtom {
+    char *name;
+    void *data;
+}      *xpmHashAtom;
+
+typedef struct {
+    unsigned int size;
+    unsigned int limit;
+    unsigned int used;
+    xpmHashAtom *atomTable;
+}      xpmHashTable;
+
+FUNC(xpmHashTableInit, int, (xpmHashTable *table));
+FUNC(xpmHashTableFree, void, (xpmHashTable *table));
+FUNC(xpmHashSlot, xpmHashAtom *, (xpmHashTable *table, char *s));
+FUNC(xpmHashIntern, int, (xpmHashTable *table, char *tag, void *data));
+
+#if defined(_MSC_VER) && defined(_M_X64)
+#define HashAtomData(i) ((void *)(long long)i)
+#define HashColorIndex(slot) ((unsigned long long)((*slot)->data))
+#else
+#define HashAtomData(i) ((void *)(long)i)
+#define HashColorIndex(slot) ((unsigned long)((*slot)->data))
+#endif
+#define USE_HASHTABLE (cpp > 2 && ncolors > 4)
+
+/* I/O utility */
+
+FUNC(xpmNextString, int, (xpmData *mdata));
+FUNC(xpmNextUI, int, (xpmData *mdata, unsigned int *ui_return));
+FUNC(xpmGetString, int, (xpmData *mdata, char **sptr, unsigned int *l));
+
+#define xpmGetC(mdata) \
+	((!mdata->type || mdata->type == XPMBUFFER) ? \
+	 (*mdata->cptr++) : (getc(mdata->stream.file)))
+
+FUNC(xpmNextWord, unsigned int,
+     (xpmData *mdata, char *buf, unsigned int buflen));
+FUNC(xpmGetCmt, int, (xpmData *mdata, char **cmt));
+FUNC(xpmParseHeader, int, (xpmData *mdata));
+FUNC(xpmParseValues, int, (xpmData *data, unsigned int *width,
+			   unsigned int *height, unsigned int *ncolors,
+			   unsigned int *cpp, unsigned int *x_hotspot,
+			   unsigned int *y_hotspot, unsigned int *hotspot,
+			   unsigned int *extensions));
+
+FUNC(xpmParseColors, int, (xpmData *data, unsigned int ncolors,
+			   unsigned int cpp, XpmColor **colorTablePtr,
+			   xpmHashTable *hashtable));
+
+FUNC(xpmParseExtensions, int, (xpmData *data, XpmExtension **extensions,
+			       unsigned int *nextensions));
+
+/* RGB utility */
+
+FUNC(xpmReadRgbNames, int, (char *rgb_fname, xpmRgbName *rgbn));
+FUNC(xpmGetRgbName, char *, (xpmRgbName *rgbn, int rgbn_max,
+			     int red, int green, int blue));
+FUNC(xpmFreeRgbNames, void, (xpmRgbName *rgbn, int rgbn_max));
+#ifdef FOR_MSW
+FUNC(xpmGetRGBfromName,int, (char *name, int *r, int *g, int *b));
+#endif
+
+#ifndef AMIGA
+FUNC(xpm_xynormalizeimagebits, void, (register unsigned char *bp,
+				      register XImage *img));
+FUNC(xpm_znormalizeimagebits, void, (register unsigned char *bp,
+				     register XImage *img));
+
+/*
+ * Macros
+ *
+ * The XYNORMALIZE macro determines whether XY format data requires
+ * normalization and calls a routine to do so if needed. The logic in
+ * this module is designed for LSBFirst byte and bit order, so
+ * normalization is done as required to present the data in this order.
+ *
+ * The ZNORMALIZE macro performs byte and nibble order normalization if
+ * required for Z format data.
+ *
+ * The XYINDEX macro computes the index to the starting byte (char) boundary
+ * for a bitmap_unit containing a pixel with coordinates x and y for image
+ * data in XY format.
+ *
+ * The ZINDEX* macros compute the index to the starting byte (char) boundary
+ * for a pixel with coordinates x and y for image data in ZPixmap format.
+ *
+ */
+
+#define XYNORMALIZE(bp, img) \
+    if ((img->byte_order == MSBFirst) || (img->bitmap_bit_order == MSBFirst)) \
+	xpm_xynormalizeimagebits((unsigned char *)(bp), img)
+
+#define ZNORMALIZE(bp, img) \
+    if (img->byte_order == MSBFirst) \
+	xpm_znormalizeimagebits((unsigned char *)(bp), img)
+
+#define XYINDEX(x, y, img) \
+    ((y) * img->bytes_per_line) + \
+    (((x) + img->xoffset) / img->bitmap_unit) * (img->bitmap_unit >> 3)
+
+#define ZINDEX(x, y, img) ((y) * img->bytes_per_line) + \
+    (((x) * img->bits_per_pixel) >> 3)
+
+#define ZINDEX32(x, y, img) ((y) * img->bytes_per_line) + ((x) << 2)
+
+#define ZINDEX16(x, y, img) ((y) * img->bytes_per_line) + ((x) << 1)
+
+#define ZINDEX8(x, y, img) ((y) * img->bytes_per_line) + (x)
+
+#define ZINDEX1(x, y, img) ((y) * img->bytes_per_line) + ((x) >> 3)
+#endif /* not AMIGA */
+
+#ifdef NEED_STRDUP
+FUNC(xpmstrdup, char *, (char *s1));
+#else
+#undef xpmstrdup
+#define xpmstrdup strdup
+#include <string.h>
+#endif
+
+#ifdef NEED_STRCASECMP
+FUNC(xpmstrcasecmp, int, (char *s1, char *s2));
+#else
+#undef xpmstrcasecmp
+#define xpmstrcasecmp strcasecmp
+#include <strings.h>
+#endif
+
+FUNC(xpmatoui, unsigned int,
+     (char *p, unsigned int l, unsigned int *ui_return));
+
+#endif
diff --git a/be_xpm/create.c b/be_xpm/create.c
new file mode 100644
index 0000000000..99bfabdbf4
--- /dev/null
+++ b/be_xpm/create.c
@@ -0,0 +1,2517 @@
+/*
+ * Copyright (C) 1989-95 GROUPE BULL
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL GROUPE BULL BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Except as contained in this notice, the name of GROUPE BULL shall
+ * not be used in advertising or otherwise to promote the sale, use or
+ * other dealings in this Software without prior written authorization
+ * from GROUPE BULL.
+ */
+
+/*****************************************************************************\
+* create.c: *
+* *
+*  XPM library *
+*  Create an X image and possibly its related shape mask *
+*  from the given XpmImage. *
+* *
+*  Developed by Arnaud Le Hors *
+\*****************************************************************************/
+
+/*
+ * The code related to FOR_MSW has been added by
+ * HeDu (hedu@cul-ipn.uni-kiel.de) 4/94
+ */
+
+/*
+ * The code related to AMIGA has been added by
+ * Lorens Younes (d93-hyo@nada.kth.se) 4/96
+ */
+
+/* October 2004, source code review by Thomas Biege <thomas@suse.de>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <ctype.h>
+#include "XpmI.h"
+
+LFUNC (xpmVisualType, int, (Visual * visual));
+
+LFUNC (AllocColor, int,
+       (Display * display, Colormap colormap, char *colorname,
+        XColor *xcolor, void *closure));
+LFUNC (FreeColors, int,
+       (Display * display, Colormap colormap, Pixel *pixels, int n,
+        void *closure));
+
+#ifndef FOR_MSW
+LFUNC (SetCloseColor, int,
+       (Display * display, Colormap colormap, Visual *visual,
+        XColor *col, Pixel *image_pixel, Pixel *mask_pixel,
+        Pixel *alloc_pixels, unsigned int *nalloc_pixels,
+        XpmAttributes *attributes, XColor *cols, int ncols,
+        XpmAllocColorFunc allocColor, void *closure));
+#else
+/* let the window system take care of close colors */
+#endif
+
+LFUNC (SetColor, int,
+       (Display * display, Colormap colormap, Visual *visual,
+        char *colorname, unsigned int color_index, Pixel *image_pixel,
+        Pixel *mask_pixel, unsigned int *mask_pixel_index,
+        Pixel *alloc_pixels, unsigned int *nalloc_pixels,
+        Pixel *used_pixels, unsigned int *nused_pixels,
+        XpmAttributes *attributes, XColor *cols, int ncols,
+        XpmAllocColorFunc allocColor, void *closure));
+
+LFUNC (CreateXImage, int,
+       (Display * display, Visual *visual, unsigned int depth,
+        int format, unsigned int width, unsigned int height,
+        XImage **image_return));
+
+LFUNC (CreateColors, int,
+       (Display * display, XpmAttributes *attributes,
+        XpmColor *colors, unsigned int ncolors, Pixel *image_pixels,
+        Pixel *mask_pixels, unsigned int *mask_pixel_index,
+        Pixel *alloc_pixels, unsigned int *nalloc_pixels,
+        Pixel *used_pixels, unsigned int *nused_pixels));
+
+#ifndef FOR_MSW
+LFUNC (ParseAndPutPixels, int,
+       (xpmData * data, unsigned int width, unsigned int height,
+        unsigned int ncolors, unsigned int cpp, XpmColor *colorTable,
+        xpmHashTable *hashtable, XImage *image, Pixel *image_pixels,
+        XImage *mask, Pixel *mask_pixels));
+#else /* FOR_MSW */
+LFUNC (ParseAndPutPixels, int,
+       (Display * dc, xpmData *data, unsigned int width,
+        unsigned int height, unsigned int ncolors, unsigned int cpp,
+        XpmColor *colorTable, xpmHashTable *hashtable, XImage *image,
+        Pixel *image_pixels, XImage *mask, Pixel *mask_pixels));
+#endif
+
+#ifndef FOR_MSW
+#ifndef AMIGA
+/* XImage pixel routines */
+LFUNC (PutImagePixels, void,
+       (XImage * image, unsigned int width, unsigned int height,
+        unsigned int *pixelindex, Pixel *pixels));
+
+LFUNC (PutImagePixels32, void,
+       (XImage * image, unsigned int width, unsigned int height,
+        unsigned int *pixelindex, Pixel *pixels));
+
+LFUNC (PutImagePixels16, void,
+       (XImage * image, unsigned int width, unsigned int height,
+        unsigned int *pixelindex, Pixel *pixels));
+
+LFUNC (PutImagePixels8, void,
+       (XImage * image, unsigned int width, unsigned int height,
+        unsigned int *pixelindex, Pixel *pixels));
+
+LFUNC (PutImagePixels1, void,
+       (XImage * image, unsigned int width, unsigned int height,
+        unsigned int *pixelindex, Pixel *pixels));
+
+LFUNC (PutPixel1, int,
+       (XImage * ximage, int x, int y, unsigned long pixel));
+LFUNC (PutPixel, int,
+       (XImage * ximage, int x, int y, unsigned long pixel));
+#if !defined(WORD64) && !defined(LONG64)
+LFUNC (PutPixel32, int,
+       (XImage * ximage, int x, int y, unsigned long pixel));
+#endif
+LFUNC (PutPixel32MSB, int,
+       (XImage * ximage, int x, int y, unsigned long pixel));
+LFUNC (PutPixel32LSB, int,
+       (XImage * ximage, int x, int y, unsigned long pixel));
+LFUNC (PutPixel16MSB, int,
+       (XImage * ximage, int x, int y, unsigned long pixel));
+LFUNC (PutPixel16LSB, int,
+       (XImage * ximage, int x, int y, unsigned long pixel));
+LFUNC (PutPixel8, int,
+       (XImage * ximage, int x, int y, unsigned long pixel));
+LFUNC (PutPixel1MSB, int,
+       (XImage * ximage, int x, int y, unsigned long pixel));
+LFUNC (PutPixel1LSB, int,
+       (XImage * ximage, int x, int y, unsigned long pixel));
+
+#else  /* AMIGA */
+LFUNC (APutImagePixels, void,
+       (XImage * ximage, unsigned int width, unsigned int height,
+        unsigned int *pixelindex, Pixel *pixels));
+#endif /* AMIGA */
+#else  /* FOR_MSW */
+/* FOR_MSW pixel routine */
+LFUNC (MSWPutImagePixels, void,
+       (Display * dc, XImage *image, unsigned int width,
+        unsigned int height, unsigned int *pixelindex,
+        Pixel *pixels));
+#endif /* FOR_MSW */
+
+#ifdef NEED_STRCASECMP
+FUNC (xpmstrcasecmp, int, (char *s1, char *s2));
+
+/*
+ * in case strcasecmp is not provided by the system here is one
+ * which does the trick
+ */
+int
+xpmstrcasecmp (register char *s1, register char *s2)
+{
+  register int c1, c2;
+
+  while (*s1 && *s2)
+    {
+      c1 = tolower (*s1);
+      c2 = tolower (*s2);
+      if (c1 != c2)
+        return (c1 - c2);
+      s1++;
+      s2++;
+    }
+  return (int) (*s1 - *s2);
+}
+
+#endif
+
+/*
+ * return the default color key related to the given visual
+ */
+static int
+xpmVisualType (Visual *visual)
+{
+#ifndef FOR_MSW
+#ifndef AMIGA
+  switch (visual->class)
+    {
+    case StaticGray:
+    case GrayScale:
+      switch (visual->map_entries)
+        {
+        case 2:
+          return (XPM_MONO);
+        case 4:
+          return (XPM_GRAY4);
+        default:
+          return (XPM_GRAY);
+        }
+    default:
+      return (XPM_COLOR);
+    }
+#else
+  /* set the key explicitly in the XpmAttributes to override this */
+  return (XPM_COLOR);
+#endif
+#else
+  /* there should be a similar switch for MSW */
+  return (XPM_COLOR);
+#endif
+}
+
+typedef struct
+{
+  int cols_index;
+  long closeness;
+} CloseColor;
+
+static int
+closeness_cmp (const void *a, const void *b)
+{
+  const CloseColor *x = (const CloseColor *) a,
+                   *y = (const CloseColor *) b;
+
+  /* cast to int as qsort requires */
+  return (int) (x->closeness - y->closeness);
+}
+
+/* default AllocColor function:
+ *   call XParseColor if colorname is given, return negative value if
+ * failure call XAllocColor and return 0 if failure, positive
+ * otherwise
+ */
+static int
+AllocColor (Display *display, Colormap colormap, char *colorname,
+            XColor *xcolor, void *closure) /* not used */
+{
+  int status;
+  if (colorname)
+    if (!XParseColor (display, colormap, colorname, xcolor))
+      return -1;
+  status = XAllocColor (display, colormap, xcolor);
+  return status != 0 ? 1 : 0;
+}
+
+#ifndef FOR_MSW
+/*
+ * set a close color in case the exact one can't be set
+ * return 0 if success, 1 otherwise.
+ */
+
+static int
+SetCloseColor (Display *display, Colormap colormap, Visual *visual,
+               XColor *col, Pixel *image_pixel, Pixel *mask_pixel,
+               Pixel *alloc_pixels, unsigned int *nalloc_pixels,
+               XpmAttributes *attributes, XColor *cols, int ncols,
+               XpmAllocColorFunc allocColor, void *closure)
+{
+  /*
+   * Allocation failed, so try close colors. To get here the visual
+   * must be GreyScale, PseudoColor or DirectColor (or perhaps
+   * StaticColor? What about sharing systems like QDSS?). Beware: we
+   * have to treat DirectColor differently.
+   */
+
+  long int red_closeness, green_closeness, blue_closeness;
+  int n;
+  Bool alloc_color;
+
+  if (attributes && (attributes->valuemask & XpmCloseness))
+    red_closeness = green_closeness = blue_closeness
+      = attributes->closeness;
+  else
+    {
+      red_closeness = attributes->red_closeness;
+      green_closeness = attributes->green_closeness;
+      blue_closeness = attributes->blue_closeness;
+    }
+  if (attributes && (attributes->valuemask & XpmAllocCloseColors))
+    alloc_color = attributes->alloc_close_colors;
+  else
+    alloc_color = True;
+
+    /*
+     * We sort the colormap by closeness and try to allocate the color
+     * closest to the target. If the allocation of this close color
+     * fails, which almost never happens, then one of two scenarios is
+     * possible. Either the colormap must have changed (since the last
+     * close color allocation or possibly while we were sorting the
+     * colormap), or the color is allocated as Read/Write by some
+     * other client. (Note: X _should_ allow clients to check if a
+     * particular color is Read/Write, but it doesn't! :-( ). We
+     * cannot determine which of these scenarios occurred, so we try
+     * the next closest color, and so on, until no more colors are
+     * within closeness of the target. If we knew that the colormap
+     * had changed, we could skip this sequence.
+     *
+     * If _none_ of the colors within closeness of the target can be
+     * allocated, then we can finally be pretty sure that the colormap
+     * has actually changed. In this case we try to allocate the
+     * original color (again), then try the closecolor stuff
+     * (again)...
+     *
+     * In theory it would be possible for an infinite loop to occur if
+     * another process kept changing the colormap every time we sorted
+     * it, so we set a maximum on the number of iterations. After this
+     * many tries, we use XGrabServer() to ensure that the colormap
+     * remains unchanged.
+     *
+     * This approach gives particularly bad worst case performance -
+     * as many as <MaximumIterations> colormap reads and sorts may be
+     * needed, and as many as <MaximumIterations> * <ColormapSize>
+     * attempted allocations may fail. On an 8-bit system, this means
+     * as many as 3 colormap reads, 3 sorts and 768 failed allocations
+     * per execution of this code! Luckily, my experiments show that
+     * in general use in a typical 8-bit color environment only about
+     * 1 in every 10000 allocations fails to succeed in the fastest
+     * possible time. So virtually every time what actually happens is
+     * a single sort followed by a successful allocate. The very first
+     * allocation also costs a colormap read, but no further reads are
+     * usually necessary.
+     */
+
+#define ITERATIONS                   \
+  2 /* more than one is almost never \
+     * necessary */
+
+  for (n = 0; n <= ITERATIONS; ++n)
+    {
+      CloseColor *closenesses
+        = (CloseColor *) XpmCalloc (ncols, sizeof (CloseColor));
+      int i, c;
+
+      for (i = 0; i < ncols; ++i)
+        { /* build & sort closenesses table */
+#define COLOR_FACTOR 3
+#define BRIGHTNESS_FACTOR 1
+
+          closenesses[i].cols_index = i;
+          closenesses[i].closeness
+            = COLOR_FACTOR
+                * (labs ((long) col->red - (long) cols[i].red)
+                   + labs ((long) col->green - (long) cols[i].green)
+                   + labs ((long) col->blue - (long) cols[i].blue))
+              + BRIGHTNESS_FACTOR
+                  * labs (((long) col->red + (long) col->green
+                           + (long) col->blue)
+                          - ((long) cols[i].red + (long) cols[i].green
+                             + (long) cols[i].blue));
+        }
+      qsort (closenesses, ncols, sizeof (CloseColor), closeness_cmp);
+
+      i = 0;
+      c = closenesses[i].cols_index;
+      while (
+        (long) cols[c].red >= (long) col->red - red_closeness
+        && (long) cols[c].red <= (long) col->red + red_closeness
+        && (long) cols[c].green >= (long) col->green - green_closeness
+        && (long) cols[c].green <= (long) col->green + green_closeness
+        && (long) cols[c].blue >= (long) col->blue - blue_closeness
+        && (long) cols[c].blue <= (long) col->blue + blue_closeness)
+        {
+          if (alloc_color)
+            {
+              if ((*allocColor) (display, colormap, NULL, &cols[c],
+                                 closure))
+                {
+                  if (n == ITERATIONS)
+                    XUngrabServer (display);
+                  XpmFree (closenesses);
+                  *image_pixel = cols[c].pixel;
+                  *mask_pixel = 1;
+                  alloc_pixels[(*nalloc_pixels)++] = cols[c].pixel;
+                  return (0);
+                }
+              else
+                {
+                  ++i;
+                  if (i == ncols)
+                    break;
+                  c = closenesses[i].cols_index;
+                }
+            }
+          else
+            {
+              if (n == ITERATIONS)
+                XUngrabServer (display);
+              XpmFree (closenesses);
+              *image_pixel = cols[c].pixel;
+              *mask_pixel = 1;
+              return (0);
+            }
+        }
+
+      /* Couldn't allocate _any_ of the close colors! */
+
+      if (n == ITERATIONS)
+        XUngrabServer (display);
+      XpmFree (closenesses);
+
+      if (i == 0 || i == ncols) /* no color close enough or cannot */
+        return (1);             /* alloc any color (full of r/w's) */
+
+      if ((*allocColor) (display, colormap, NULL, col, closure))
+        {
+          *image_pixel = col->pixel;
+          *mask_pixel = 1;
+          alloc_pixels[(*nalloc_pixels)++] = col->pixel;
+          return (0);
+        }
+      else
+        { /* colormap has probably changed, so
+           * re-read... */
+          if (n == ITERATIONS - 1)
+            XGrabServer (display);
+
+#if 0
+	    if (visual->class == DirectColor) {
+		/* TODO */
+	    } else
+#endif
+          XQueryColors (display, colormap, cols, ncols);
+        }
+    }
+  return (1);
+}
+
+#define USE_CLOSECOLOR                                \
+  attributes                                          \
+    && (((attributes->valuemask & XpmCloseness)       \
+         && attributes->closeness != 0)               \
+        || ((attributes->valuemask & XpmRGBCloseness) \
+            && (attributes->red_closeness != 0        \
+                || attributes->green_closeness != 0   \
+                || attributes->blue_closeness != 0)))
+
+#else
+/* FOR_MSW part */
+/* nothing to do here, the window system does it */
+#endif
+
+/*
+ * set the color pixel related to the given colorname,
+ * return 0 if success, 1 otherwise.
+ */
+
+static int
+SetColor (Display *display, Colormap colormap, Visual *visual,
+          char *colorname, unsigned int color_index,
+          Pixel *image_pixel, Pixel *mask_pixel,
+          unsigned int *mask_pixel_index, Pixel *alloc_pixels,
+          unsigned int *nalloc_pixels, Pixel *used_pixels,
+          unsigned int *nused_pixels, XpmAttributes *attributes,
+          XColor *cols, int ncols, XpmAllocColorFunc allocColor,
+          void *closure)
+{
+  XColor xcolor;
+  int status;
+
+  if (xpmstrcasecmp (colorname, TRANSPARENT_COLOR))
+    {
+      status = (*allocColor) (display, colormap, colorname, &xcolor,
+                              closure);
+      if (status < 0) /* parse color failed */
+        return (1);
+
+      if (status == 0)
+        {
+#ifndef FOR_MSW
+          if (USE_CLOSECOLOR)
+            return (SetCloseColor (display, colormap, visual, &xcolor,
+                                   image_pixel, mask_pixel,
+                                   alloc_pixels, nalloc_pixels,
+                                   attributes, cols, ncols,
+                                   allocColor, closure));
+          else
+#endif /* ndef FOR_MSW */
+            return (1);
+        }
+      else
+        alloc_pixels[(*nalloc_pixels)++] = xcolor.pixel;
+      *image_pixel = xcolor.pixel;
+      *mask_pixel = 1;
+      used_pixels[(*nused_pixels)++] = xcolor.pixel;
+    }
+  else
+    {
+      *image_pixel = 0;
+      *mask_pixel = 0;
+      /* store the color table index */
+      *mask_pixel_index = color_index;
+    }
+  return (0);
+}
+
+static int
+CreateColors (Display *display, XpmAttributes *attributes,
+              XpmColor *colors, unsigned int ncolors,
+              Pixel *image_pixels, Pixel *mask_pixels,
+              unsigned int *mask_pixel_index, Pixel *alloc_pixels,
+              unsigned int *nalloc_pixels, Pixel *used_pixels,
+              unsigned int *nused_pixels)
+{
+  /* variables stored in the XpmAttributes structure */
+  Visual *visual;
+  Colormap colormap;
+  XpmColorSymbol *colorsymbols = NULL;
+  unsigned int numsymbols;
+  XpmAllocColorFunc allocColor;
+  void *closure;
+
+  char *colorname;
+  unsigned int color, key;
+  Bool pixel_defined;
+  XpmColorSymbol *symbol = NULL;
+  char **defaults;
+  int ErrorStatus = XpmSuccess;
+  char *s;
+  int default_index;
+
+  XColor *cols = NULL;
+  unsigned int ncols = 0;
+
+  /*
+   * retrieve information from the XpmAttributes
+   */
+  if (attributes && attributes->valuemask & XpmColorSymbols)
+    {
+      colorsymbols = attributes->colorsymbols;
+      numsymbols = attributes->numsymbols;
+    }
+  else
+    numsymbols = 0;
+
+  if (attributes && attributes->valuemask & XpmVisual)
+    visual = attributes->visual;
+  else
+    visual = XDefaultVisual (display, XDefaultScreen (display));
+
+  if (attributes && (attributes->valuemask & XpmColormap))
+    colormap = attributes->colormap;
+  else
+    colormap = XDefaultColormap (display, XDefaultScreen (display));
+
+  if (attributes && (attributes->valuemask & XpmColorKey))
+    key = attributes->color_key;
+  else
+    key = xpmVisualType (visual);
+
+  if (attributes && (attributes->valuemask & XpmAllocColor))
+    allocColor = attributes->alloc_color;
+  else
+    allocColor = AllocColor;
+  if (attributes && (attributes->valuemask & XpmColorClosure))
+    closure = attributes->color_closure;
+  else
+    closure = NULL;
+
+#ifndef FOR_MSW
+  if (USE_CLOSECOLOR)
+    {
+      /* originally from SetCloseColor */
+#if 0
+	if (visual->class == DirectColor) {
+
+	    /*
+	     * TODO: Implement close colors for DirectColor visuals. This is
+	     * difficult situation. Chances are that we will never get here,
+	     * because any machine that supports DirectColor will probably
+	     * also support TrueColor (and probably PseudoColor). Also,
+	     * DirectColor colormaps can be very large, so looking for close
+	     * colors may be too slow.
+	     */
+	} else {
+#endif
+      unsigned int i;
+
+#ifndef AMIGA
+      ncols = visual->map_entries;
+#else
+      ncols = colormap->Count;
+#endif
+      cols = (XColor *) XpmCalloc (ncols, sizeof (XColor));
+      for (i = 0; i < ncols; ++i)
+        cols[i].pixel = i;
+      XQueryColors (display, colormap, cols, ncols);
+#if 0
+	}
+#endif
+    }
+#endif /* ndef FOR_MSW */
+
+  switch (key)
+    {
+    case XPM_MONO:
+      default_index = 2;
+      break;
+    case XPM_GRAY4:
+      default_index = 3;
+      break;
+    case XPM_GRAY:
+      default_index = 4;
+      break;
+    case XPM_COLOR:
+    default:
+      default_index = 5;
+      break;
+    }
+
+  for (color = 0; color < ncolors;
+       color++, colors++, image_pixels++, mask_pixels++)
+    {
+      colorname = NULL;
+      pixel_defined = False;
+      defaults = (char **) colors;
+
+      /*
+       * look for a defined symbol
+       */
+      if (numsymbols)
+        {
+          unsigned int n;
+
+          s = defaults[1];
+          for (n = 0, symbol = colorsymbols; n < numsymbols;
+               n++, symbol++)
+            {
+              if (symbol->name && s && !strcmp (symbol->name, s))
+                /* override name */
+                break;
+              if (!symbol->name && symbol->value)
+                { /* override value */
+                  int def_index = default_index;
+
+                  while (defaults[def_index] == NULL) /* find defined
+                                                       * colorname */
+                    --def_index;
+                  if (def_index < 2)
+                    { /* nothing towards mono, so try
+                       * towards color */
+                      def_index = default_index + 1;
+                      while (def_index <= 5
+                             && defaults[def_index] == NULL)
+                        ++def_index;
+                    }
+                  if (def_index >= 2 && def_index <= 5
+                      && defaults[def_index] != NULL
+                      && !xpmstrcasecmp (symbol->value,
+                                         defaults[def_index]))
+                    break;
+                }
+            }
+          if (n != numsymbols)
+            {
+              if (symbol->name && symbol->value)
+                colorname = symbol->value;
+              else
+                pixel_defined = True;
+            }
+        }
+      if (!pixel_defined)
+        { /* pixel not given as symbol value */
+
+          unsigned int k;
+
+          if (colorname)
+            { /* colorname given as symbol value */
+              if (!SetColor (display, colormap, visual, colorname,
+                             color, image_pixels, mask_pixels,
+                             mask_pixel_index, alloc_pixels,
+                             nalloc_pixels, used_pixels, nused_pixels,
+                             attributes, cols, ncols, allocColor,
+                             closure))
+                pixel_defined = True;
+              else
+                ErrorStatus = XpmColorError;
+            }
+          k = key;
+          while (!pixel_defined && k > 1)
+            {
+              if (defaults[k])
+                {
+                  if (!SetColor (display, colormap, visual,
+                                 defaults[k], color, image_pixels,
+                                 mask_pixels, mask_pixel_index,
+                                 alloc_pixels, nalloc_pixels,
+                                 used_pixels, nused_pixels,
+                                 attributes, cols, ncols, allocColor,
+                                 closure))
+                    {
+                      pixel_defined = True;
+                      break;
+                    }
+                  else
+                    ErrorStatus = XpmColorError;
+                }
+              k--;
+            }
+          k = key + 1;
+          while (!pixel_defined && k < NKEYS + 1)
+            {
+              if (defaults[k])
+                {
+                  if (!SetColor (display, colormap, visual,
+                                 defaults[k], color, image_pixels,
+                                 mask_pixels, mask_pixel_index,
+                                 alloc_pixels, nalloc_pixels,
+                                 used_pixels, nused_pixels,
+                                 attributes, cols, ncols, allocColor,
+                                 closure))
+                    {
+                      pixel_defined = True;
+                      break;
+                    }
+                  else
+                    ErrorStatus = XpmColorError;
+                }
+              k++;
+            }
+          if (!pixel_defined)
+            {
+              if (cols)
+                XpmFree (cols);
+              return (XpmColorFailed);
+            }
+        }
+      else
+        {
+          /* simply use the given pixel */
+          *image_pixels = symbol->pixel;
+          /* the following makes the mask to be built even if none
+             is given a particular pixel */
+          if (symbol->value
+              && !xpmstrcasecmp (symbol->value, TRANSPARENT_COLOR))
+            {
+              *mask_pixels = 0;
+              *mask_pixel_index = color;
+            }
+          else
+            *mask_pixels = 1;
+          used_pixels[(*nused_pixels)++] = *image_pixels;
+        }
+    }
+  if (cols)
+    XpmFree (cols);
+  return (ErrorStatus);
+}
+
+/* default FreeColors function, simply call XFreeColors */
+static int
+FreeColors (Display *display, Colormap colormap, Pixel *pixels, int n,
+            void *closure) /* not used */
+{
+  return XFreeColors (display, colormap, pixels, n, 0);
+}
+
+/* function call in case of error */
+
+#undef RETURN
+#define RETURN(status)      \
+  do                        \
+    {                       \
+      ErrorStatus = status; \
+      goto error;           \
+    }                       \
+  while (0)
+
+int
+XpmCreateImageFromXpmImage (Display *display, XpmImage *image,
+                            XImage **image_return,
+                            XImage **shapeimage_return,
+                            XpmAttributes *attributes)
+{
+  /* variables stored in the XpmAttributes structure */
+  Visual *visual;
+  Colormap colormap;
+  unsigned int depth;
+  int bitmap_format;
+  XpmFreeColorsFunc freeColors;
+
+  /* variables to return */
+  XImage *ximage = NULL;
+  XImage *shapeimage = NULL;
+  unsigned int mask_pixel_index = XpmUndefPixel;
+  int ErrorStatus;
+
+  /* calculation variables */
+  Pixel *image_pixels = NULL;
+  Pixel *mask_pixels = NULL;
+  Pixel *alloc_pixels = NULL;
+  Pixel *used_pixels = NULL;
+  unsigned int nalloc_pixels = 0;
+  unsigned int nused_pixels = 0;
+
+  /* initialize return values */
+  if (image_return)
+    *image_return = NULL;
+  if (shapeimage_return)
+    *shapeimage_return = NULL;
+
+  /* retrieve information from the XpmAttributes */
+  if (attributes && (attributes->valuemask & XpmVisual))
+    visual = attributes->visual;
+  else
+    visual = XDefaultVisual (display, XDefaultScreen (display));
+
+  if (attributes && (attributes->valuemask & XpmColormap))
+    colormap = attributes->colormap;
+  else
+    colormap = XDefaultColormap (display, XDefaultScreen (display));
+
+  if (attributes && (attributes->valuemask & XpmDepth))
+    depth = attributes->depth;
+  else
+    depth = XDefaultDepth (display, XDefaultScreen (display));
+
+  if (attributes && (attributes->valuemask & XpmBitmapFormat))
+    bitmap_format = attributes->bitmap_format;
+  else
+    bitmap_format = ZPixmap;
+
+  if (attributes && (attributes->valuemask & XpmFreeColors))
+    freeColors = attributes->free_colors;
+  else
+    freeColors = FreeColors;
+
+  ErrorStatus = XpmSuccess;
+
+  if (image->ncolors >= UINT_MAX / sizeof (Pixel))
+    return (XpmNoMemory);
+
+  /* malloc pixels index tables */
+  image_pixels
+    = (Pixel *) XpmMalloc (sizeof (Pixel) * image->ncolors);
+  if (!image_pixels)
+    return (XpmNoMemory);
+
+  mask_pixels = (Pixel *) XpmMalloc (sizeof (Pixel) * image->ncolors);
+  if (!mask_pixels)
+    RETURN (XpmNoMemory);
+
+  /* maximum of allocated pixels will be the number of colors */
+  alloc_pixels
+    = (Pixel *) XpmMalloc (sizeof (Pixel) * image->ncolors);
+  if (!alloc_pixels)
+    RETURN (XpmNoMemory);
+
+  /* maximum of allocated pixels will be the number of colors */
+  used_pixels = (Pixel *) XpmMalloc (sizeof (Pixel) * image->ncolors);
+  if (!used_pixels)
+    RETURN (XpmNoMemory);
+
+  /* get pixel colors, store them in index tables */
+  ErrorStatus
+    = CreateColors (display, attributes, image->colorTable,
+                    image->ncolors, image_pixels, mask_pixels,
+                    &mask_pixel_index, alloc_pixels, &nalloc_pixels,
+                    used_pixels, &nused_pixels);
+
+  if (ErrorStatus != XpmSuccess
+      && (ErrorStatus < 0
+          || (attributes && (attributes->valuemask & XpmExactColors)
+              && attributes->exactColors)))
+    RETURN (ErrorStatus);
+
+  /* create the ximage */
+  if (image_return)
+    {
+      ErrorStatus
+        = CreateXImage (display, visual, depth,
+                        (depth == 1 ? bitmap_format : ZPixmap),
+                        image->width, image->height, &ximage);
+      if (ErrorStatus != XpmSuccess)
+        RETURN (ErrorStatus);
+
+#ifndef FOR_MSW
+#ifndef AMIGA
+
+      /*
+       * set the ximage data using optimized functions for ZPixmap
+       */
+
+      if (ximage->bits_per_pixel == 8)
+        PutImagePixels8 (ximage, image->width, image->height,
+                         image->data, image_pixels);
+      else if (((ximage->bits_per_pixel | ximage->depth) == 1)
+               && (ximage->byte_order == ximage->bitmap_bit_order))
+        PutImagePixels1 (ximage, image->width, image->height,
+                         image->data, image_pixels);
+      else if (ximage->bits_per_pixel == 16)
+        PutImagePixels16 (ximage, image->width, image->height,
+                          image->data, image_pixels);
+      else if (ximage->bits_per_pixel == 32)
+        PutImagePixels32 (ximage, image->width, image->height,
+                          image->data, image_pixels);
+      else
+        PutImagePixels (ximage, image->width, image->height,
+                        image->data, image_pixels);
+#else /* AMIGA */
+      APutImagePixels (ximage, image->width, image->height,
+                       image->data, image_pixels);
+#endif
+#else /* FOR_MSW */
+      MSWPutImagePixels (display, ximage, image->width, image->height,
+                         image->data, image_pixels);
+#endif
+    }
+  /* create the shape mask image */
+  if (mask_pixel_index != XpmUndefPixel && shapeimage_return)
+    {
+      ErrorStatus
+        = CreateXImage (display, visual, 1, bitmap_format,
+                        image->width, image->height, &shapeimage);
+      if (ErrorStatus != XpmSuccess)
+        RETURN (ErrorStatus);
+
+#ifndef FOR_MSW
+#ifndef AMIGA
+      PutImagePixels1 (shapeimage, image->width, image->height,
+                       image->data, mask_pixels);
+#else /* AMIGA */
+      APutImagePixels (shapeimage, image->width, image->height,
+                       image->data, mask_pixels);
+#endif
+#else /* FOR_MSW */
+      MSWPutImagePixels (display, shapeimage, image->width,
+                         image->height, image->data, mask_pixels);
+#endif
+    }
+  XpmFree (image_pixels);
+  XpmFree (mask_pixels);
+
+  /* if requested return used pixels in the XpmAttributes structure */
+  if (attributes
+      && (attributes->valuemask & XpmReturnPixels ||
+          /* 3.2 backward compatibility code */
+          attributes->valuemask & XpmReturnInfos))
+    {
+      /* end 3.2 bc */
+      attributes->pixels = used_pixels;
+      attributes->npixels = nused_pixels;
+      attributes->mask_pixel = mask_pixel_index;
+    }
+  else
+    XpmFree (used_pixels);
+
+  /* if requested return alloc'ed pixels in the XpmAttributes
+   * structure */
+  if (attributes && (attributes->valuemask & XpmReturnAllocPixels))
+    {
+      attributes->alloc_pixels = alloc_pixels;
+      attributes->nalloc_pixels = nalloc_pixels;
+    }
+  else
+    XpmFree (alloc_pixels);
+
+  /* return created images */
+  if (image_return)
+    *image_return = ximage;
+  if (shapeimage_return)
+    *shapeimage_return = shapeimage;
+
+  return (ErrorStatus);
+
+/* exit point in case of error, free only locally allocated variables
+ */
+error:
+  if (ximage)
+    XDestroyImage (ximage);
+  if (shapeimage)
+    XDestroyImage (shapeimage);
+  if (image_pixels)
+    XpmFree (image_pixels);
+  if (mask_pixels)
+    XpmFree (mask_pixels);
+  if (nalloc_pixels)
+    (*freeColors) (display, colormap, alloc_pixels, nalloc_pixels,
+                   NULL);
+  if (alloc_pixels)
+    XpmFree (alloc_pixels);
+  if (used_pixels)
+    XpmFree (used_pixels);
+
+  return (ErrorStatus);
+}
+
+/*
+ * Create an XImage with its data
+ */
+static int
+CreateXImage (Display *display, Visual *visual, unsigned int depth,
+              int format, unsigned int width, unsigned int height,
+              XImage **image_return)
+{
+  int bitmap_pad;
+
+  /* first get bitmap_pad */
+  if (depth > 16)
+    bitmap_pad = 32;
+  else if (depth > 8)
+    bitmap_pad = 16;
+  else
+    bitmap_pad = 8;
+
+  /* then create the XImage with data = NULL and bytes_per_line = 0 */
+  *image_return = XCreateImage (display, visual, depth, format, 0, 0,
+                                width, height, bitmap_pad, 0);
+  if (!*image_return)
+    return (XpmNoMemory);
+
+#if !defined(FOR_MSW) && !defined(AMIGA)
+  if (height != 0
+      && (*image_return)->bytes_per_line >= INT_MAX / height)
+    {
+      XDestroyImage (*image_return);
+      return XpmNoMemory;
+    }
+  /* now that bytes_per_line must have been set properly alloc data */
+  if ((*image_return)->bytes_per_line == 0 || height == 0)
+    return XpmNoMemory;
+  (*image_return)->data
+    = (char *) XpmMalloc ((*image_return)->bytes_per_line * height);
+
+  if (!(*image_return)->data)
+    {
+      XDestroyImage (*image_return);
+      *image_return = NULL;
+      return (XpmNoMemory);
+    }
+#else
+    /* under FOR_MSW and AMIGA XCreateImage has done it all */
+#endif
+  return (XpmSuccess);
+}
+
+#ifndef FOR_MSW
+#ifndef AMIGA
+/*
+ * The functions below are written from X11R5 MIT's code (XImUtil.c)
+ *
+ * The idea is to have faster functions than the standard XPutPixel
+ * function to build the image data. Indeed we can speed up things by
+ * suppressing tests performed for each pixel. We do the same tests
+ * but at the image level. We also assume that we use only ZPixmap
+ * images with null offsets.
+ */
+
+LFUNC (_putbits, void,
+       (register char *src, int dstoffset, register int numbits,
+        register char *dst));
+
+LFUNC (_XReverse_Bytes, int,
+       (register unsigned char *bpt, register unsigned int nb));
+
+static unsigned char const _reverse_byte[0x100] = {
+  0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50,
+  0xd0, 0x30, 0xb0, 0x70, 0xf0, 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8,
+  0x68, 0xe8, 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, 0x04,
+  0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4,
+  0x34, 0xb4, 0x74, 0xf4, 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c,
+  0xec, 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, 0x02, 0x82,
+  0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, 0x12, 0x92, 0x52, 0xd2, 0x32,
+  0xb2, 0x72, 0xf2, 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
+  0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, 0x06, 0x86, 0x46,
+  0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6,
+  0x76, 0xf6, 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, 0x1e,
+  0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, 0x01, 0x81, 0x41, 0xc1,
+  0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71,
+  0xf1, 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99,
+  0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, 0x05, 0x85, 0x45, 0xc5, 0x25,
+  0xa5, 0x65, 0xe5, 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
+  0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d,
+  0xdd, 0x3d, 0xbd, 0x7d, 0xfd, 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3,
+  0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, 0x0b,
+  0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, 0x1b, 0x9b, 0x5b, 0xdb,
+  0x3b, 0xbb, 0x7b, 0xfb, 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67,
+  0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, 0x0f, 0x8f,
+  0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf, 0x3f,
+  0xbf, 0x7f, 0xff
+};
+
+static int
+_XReverse_Bytes (register unsigned char *bpt,
+                 register unsigned int nb)
+{
+  do
+    {
+      *bpt = _reverse_byte[*bpt];
+      bpt++;
+    }
+  while (--nb > 0); /* is nb user-controled? */
+  return 0;
+}
+
+void
+xpm_xynormalizeimagebits (register unsigned char *bp,
+                          register XImage *img)
+{
+  register unsigned char c;
+
+  if (img->byte_order != img->bitmap_bit_order)
+    {
+      switch (img->bitmap_unit)
+        {
+        case 16:
+          c = *bp;
+          *bp = *(bp + 1);
+          *(bp + 1) = c;
+          break;
+
+        case 32:
+          c = *(bp + 3);
+          *(bp + 3) = *bp;
+          *bp = c;
+          c = *(bp + 2);
+          *(bp + 2) = *(bp + 1);
+          *(bp + 1) = c;
+          break;
+        }
+    }
+  if (img->bitmap_bit_order == MSBFirst)
+    _XReverse_Bytes (bp, img->bitmap_unit >> 3);
+}
+
+void
+xpm_znormalizeimagebits (register unsigned char *bp,
+                         register XImage *img)
+{
+  register unsigned char c;
+
+  switch (img->bits_per_pixel)
+    {
+    case 2:
+      _XReverse_Bytes (bp, 1);
+      break;
+
+    case 4:
+      *bp = ((*bp >> 4) & 0xF) | ((*bp << 4) & ~0xF);
+      break;
+
+    case 16:
+      c = *bp;
+      *bp = *(bp + 1);
+      *(bp + 1) = c;
+      break;
+
+    case 24:
+      c = *(bp + 2);
+      *(bp + 2) = *bp;
+      *bp = c;
+      break;
+
+    case 32:
+      c = *(bp + 3);
+      *(bp + 3) = *bp;
+      *bp = c;
+      c = *(bp + 2);
+      *(bp + 2) = *(bp + 1);
+      *(bp + 1) = c;
+      break;
+    }
+}
+
+static unsigned char const _lomask[0x09]
+  = { 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff };
+static unsigned char const _himask[0x09]
+  = { 0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00 };
+
+static void
+_putbits (register char *src,   /* address of source bit string */
+          int dstoffset,        /* bit offset into destination;
+                                 * range is 0-31 */
+          register int numbits, /* number of bits to copy to
+                                 * destination */
+          register char *dst) /* address of destination bit string */
+{
+  register unsigned char chlo, chhi;
+  int hibits;
+
+  dst = dst + (dstoffset >> 3);
+  dstoffset = dstoffset & 7;
+  hibits = 8 - dstoffset;
+  chlo = *dst & _lomask[dstoffset];
+  for (;;)
+    {
+      chhi = (*src << dstoffset) & _himask[dstoffset];
+      if (numbits <= hibits)
+        {
+          chhi = chhi & _lomask[dstoffset + numbits];
+          *dst = (*dst & _himask[dstoffset + numbits]) | chlo | chhi;
+          break;
+        }
+      *dst = chhi | chlo;
+      dst++;
+      numbits = numbits - hibits;
+      chlo = (unsigned char) (*src & _himask[hibits]) >> hibits;
+      src++;
+      if (numbits <= dstoffset)
+        {
+          chlo = chlo & _lomask[numbits];
+          *dst = (*dst & _himask[numbits]) | chlo;
+          break;
+        }
+      numbits = numbits - dstoffset;
+    }
+}
+
+/*
+ * Default method to write pixels into a Z image data structure.
+ * The algorithm used is:
+ *
+ *	copy the destination bitmap_unit or Zpixel to temp
+ *	normalize temp if needed
+ *	copy the pixel bits into the temp
+ *	renormalize temp if needed
+ *	copy the temp back into the destination image data
+ */
+
+static void
+PutImagePixels (XImage *image, unsigned int width,
+                unsigned int height, unsigned int *pixelindex,
+                Pixel *pixels)
+{
+  register char *src;
+  register char *dst;
+  register unsigned int *iptr;
+  register unsigned int x, y;
+  register char *data;
+  Pixel pixel, px;
+  int nbytes, depth, ibu, ibpp, i;
+
+  data = image->data;
+  iptr = pixelindex;
+  depth = image->depth;
+  if (depth == 1)
+    {
+      ibu = image->bitmap_unit;
+      for (y = 0; y < height; y++) /* how can we trust height */
+        for (x = 0; x < width; x++, iptr++)
+          { /* how can we trust width */
+            pixel = pixels[*iptr];
+            for (i = 0, px = pixel; i < sizeof (unsigned long);
+                 i++, px >>= 8)
+              ((unsigned char *) &pixel)[i] = px;
+            src = &data[XYINDEX (x, y, image)];
+            dst = (char *) &px;
+            px = 0;
+            nbytes = ibu >> 3;
+            for (i = nbytes; --i >= 0;)
+              *dst++ = *src++;
+            XYNORMALIZE (&px, image);
+            _putbits ((char *) &pixel, (x % ibu), 1, (char *) &px);
+            XYNORMALIZE (&px, image);
+            src = (char *) &px;
+            dst = &data[XYINDEX (x, y, image)];
+            for (i = nbytes; --i >= 0;)
+              *dst++ = *src++;
+          }
+    }
+  else
+    {
+      ibpp = image->bits_per_pixel;
+      for (y = 0; y < height; y++)
+        for (x = 0; x < width; x++, iptr++)
+          {
+            pixel = pixels[*iptr];
+            if (depth == 4)
+              pixel &= 0xf;
+            for (i = 0, px = pixel; i < sizeof (unsigned long);
+                 i++, px >>= 8)
+              ((unsigned char *) &pixel)[i] = px;
+            src = &data[ZINDEX (x, y, image)];
+            dst = (char *) &px;
+            px = 0;
+            nbytes = (ibpp + 7) >> 3;
+            for (i = nbytes; --i >= 0;)
+              *dst++ = *src++;
+            ZNORMALIZE (&px, image);
+            _putbits ((char *) &pixel, (x * ibpp) & 7, ibpp,
+                      (char *) &px);
+            ZNORMALIZE (&px, image);
+            src = (char *) &px;
+            dst = &data[ZINDEX (x, y, image)];
+            for (i = nbytes; --i >= 0;)
+              *dst++ = *src++;
+          }
+    }
+}
+
+/*
+ * write pixels into a 32-bits Z image data structure
+ */
+
+#if !defined(WORD64) && !defined(LONG64)
+/* this item is static but deterministic so let it slide; doesn't
+ * hurt re-entrancy of this library. Note if it is actually const then
+ * would be OK under rules of ANSI-C but probably not C++ which may
+ * not want to allocate space for it.
+ */
+static unsigned long byteorderpixel = MSBFirst << 24;
+
+#endif
+
+/*
+   WITHOUT_SPEEDUPS is a flag to be turned on if you wish to use the
+   original 3.2e code - by default you get the speeded-up version.
+*/
+
+static void
+PutImagePixels32 (XImage *image, unsigned int width,
+                  unsigned int height, unsigned int *pixelindex,
+                  Pixel *pixels)
+{
+  unsigned char *data;
+  unsigned int *iptr;
+  unsigned int y;
+  Pixel pixel;
+
+#ifdef WITHOUT_SPEEDUPS
+
+  unsigned int x;
+  unsigned char *addr;
+
+  data = (unsigned char *) image->data;
+  iptr = pixelindex;
+#if !defined(WORD64) && !defined(LONG64)
+  if (*((char *) &byteorderpixel) == image->byte_order)
+    {
+      for (y = 0; y < height; y++)
+        for (x = 0; x < width; x++, iptr++)
+          {
+            addr = &data[ZINDEX32 (x, y, image)];
+            *((unsigned long *) addr) = pixels[*iptr];
+          }
+    }
+  else
+#endif
+    if (image->byte_order == MSBFirst)
+    for (y = 0; y < height; y++)
+      for (x = 0; x < width; x++, iptr++)
+        {
+          addr = &data[ZINDEX32 (x, y, image)];
+          pixel = pixels[*iptr];
+          addr[0] = pixel >> 24;
+          addr[1] = pixel >> 16;
+          addr[2] = pixel >> 8;
+          addr[3] = pixel;
+        }
+  else
+    for (y = 0; y < height; y++)
+      for (x = 0; x < width; x++, iptr++)
+        {
+          addr = &data[ZINDEX32 (x, y, image)];
+          pixel = pixels[*iptr];
+          addr[0] = pixel;
+          addr[1] = pixel >> 8;
+          addr[2] = pixel >> 16;
+          addr[3] = pixel >> 24;
+        }
+
+#else /* WITHOUT_SPEEDUPS */
+
+  unsigned int bpl = image->bytes_per_line;
+  unsigned char *data_ptr, *max_data;
+
+  data = (unsigned char *) image->data;
+  iptr = pixelindex;
+#if !defined(WORD64) && !defined(LONG64)
+  if (*((char *) &byteorderpixel) == image->byte_order)
+    {
+      for (y = 0; y < height; y++)
+        {
+          data_ptr = data;
+          max_data = data_ptr + (width << 2);
+
+          while (data_ptr < max_data)
+            {
+              *((unsigned long *) data_ptr) = pixels[*(iptr++)];
+              data_ptr += (1 << 2);
+            }
+          data += bpl;
+        }
+    }
+  else
+#endif
+    if (image->byte_order == MSBFirst)
+    for (y = 0; y < height; y++)
+      {
+        data_ptr = data;
+        max_data = data_ptr + (width << 2);
+
+        while (data_ptr < max_data)
+          {
+            pixel = pixels[*(iptr++)];
+
+            *data_ptr++ = pixel >> 24;
+            *data_ptr++ = pixel >> 16;
+            *data_ptr++ = pixel >> 8;
+            *data_ptr++ = pixel;
+          }
+        data += bpl;
+      }
+  else
+    for (y = 0; y < height; y++)
+      {
+        data_ptr = data;
+        max_data = data_ptr + (width << 2);
+
+        while (data_ptr < max_data)
+          {
+            pixel = pixels[*(iptr++)];
+
+            *data_ptr++ = pixel;
+            *data_ptr++ = pixel >> 8;
+            *data_ptr++ = pixel >> 16;
+            *data_ptr++ = pixel >> 24;
+          }
+        data += bpl;
+      }
+
+#endif /* WITHOUT_SPEEDUPS */
+}
+
+/*
+ * write pixels into a 16-bits Z image data structure
+ */
+
+static void
+PutImagePixels16 (XImage *image, unsigned int width,
+                  unsigned int height, unsigned int *pixelindex,
+                  Pixel *pixels)
+{
+  unsigned char *data;
+  unsigned int *iptr;
+  unsigned int y;
+
+#ifdef WITHOUT_SPEEDUPS
+
+  unsigned int x;
+  unsigned char *addr;
+
+  data = (unsigned char *) image->data;
+  iptr = pixelindex;
+  if (image->byte_order == MSBFirst)
+    for (y = 0; y < height; y++)
+      for (x = 0; x < width; x++, iptr++)
+        {
+          addr = &data[ZINDEX16 (x, y, image)];
+          addr[0] = pixels[*iptr] >> 8;
+          addr[1] = pixels[*iptr];
+        }
+  else
+    for (y = 0; y < height; y++)
+      for (x = 0; x < width; x++, iptr++)
+        {
+          addr = &data[ZINDEX16 (x, y, image)];
+          addr[0] = pixels[*iptr];
+          addr[1] = pixels[*iptr] >> 8;
+        }
+
+#else /* WITHOUT_SPEEDUPS */
+
+  Pixel pixel;
+
+  unsigned int bpl = image->bytes_per_line;
+  unsigned char *data_ptr, *max_data;
+
+  data = (unsigned char *) image->data;
+  iptr = pixelindex;
+  if (image->byte_order == MSBFirst)
+    for (y = 0; y < height; y++)
+      {
+        data_ptr = data;
+        max_data = data_ptr + (width << 1);
+
+        while (data_ptr < max_data)
+          {
+            pixel = pixels[*(iptr++)];
+
+            data_ptr[0] = pixel >> 8;
+            data_ptr[1] = pixel;
+
+            data_ptr += (1 << 1);
+          }
+        data += bpl;
+      }
+  else
+    for (y = 0; y < height; y++)
+      {
+        data_ptr = data;
+        max_data = data_ptr + (width << 1);
+
+        while (data_ptr < max_data)
+          {
+            pixel = pixels[*(iptr++)];
+
+            data_ptr[0] = pixel;
+            data_ptr[1] = pixel >> 8;
+
+            data_ptr += (1 << 1);
+          }
+        data += bpl;
+      }
+
+#endif /* WITHOUT_SPEEDUPS */
+}
+
+/*
+ * write pixels into a 8-bits Z image data structure
+ */
+
+static void
+PutImagePixels8 (XImage *image, unsigned int width,
+                 unsigned int height, unsigned int *pixelindex,
+                 Pixel *pixels)
+{
+  char *data;
+  unsigned int *iptr;
+  unsigned int y;
+
+#ifdef WITHOUT_SPEEDUPS
+
+  unsigned int x;
+
+  data = image->data;
+  iptr = pixelindex;
+  for (y = 0; y < height; y++)
+    for (x = 0; x < width; x++, iptr++)
+      data[ZINDEX8 (x, y, image)] = pixels[*iptr];
+
+#else /* WITHOUT_SPEEDUPS */
+
+  unsigned int bpl = image->bytes_per_line;
+  char *data_ptr, *max_data;
+
+  data = image->data;
+  iptr = pixelindex;
+
+  for (y = 0; y < height; y++)
+    {
+      data_ptr = data;
+      max_data = data_ptr + width;
+
+      while (data_ptr < max_data)
+        *(data_ptr++) = pixels[*(iptr++)];
+
+      data += bpl;
+    }
+
+#endif /* WITHOUT_SPEEDUPS */
+}
+
+/*
+ * write pixels into a 1-bit depth image data structure and **offset
+ * null**
+ */
+
+static void
+PutImagePixels1 (XImage *image, unsigned int width,
+                 unsigned int height, unsigned int *pixelindex,
+                 Pixel *pixels)
+{
+  if (image->byte_order != image->bitmap_bit_order)
+    PutImagePixels (image, width, height, pixelindex, pixels);
+  else
+    {
+      unsigned int *iptr;
+      unsigned int y;
+      char *data;
+
+#ifdef WITHOUT_SPEEDUPS
+
+      unsigned int x;
+
+      data = image->data;
+      iptr = pixelindex;
+      if (image->bitmap_bit_order == MSBFirst)
+        for (y = 0; y < height; y++)
+          for (x = 0; x < width; x++, iptr++)
+            {
+              if (pixels[*iptr] & 1)
+                data[ZINDEX1 (x, y, image)] |= 0x80 >> (x & 7);
+              else
+                data[ZINDEX1 (x, y, image)] &= ~(0x80 >> (x & 7));
+            }
+      else
+        for (y = 0; y < height; y++)
+          for (x = 0; x < width; x++, iptr++)
+            {
+              if (pixels[*iptr] & 1)
+                data[ZINDEX1 (x, y, image)] |= 1 << (x & 7);
+              else
+                data[ZINDEX1 (x, y, image)] &= ~(1 << (x & 7));
+            }
+
+#else /* WITHOUT_SPEEDUPS */
+
+      char value;
+      char *data_ptr, *max_data;
+      int bpl = image->bytes_per_line;
+      int diff, count;
+
+      data = image->data;
+      iptr = pixelindex;
+
+      diff = width & 7;
+      width >>= 3;
+
+      if (image->bitmap_bit_order == MSBFirst)
+        for (y = 0; y < height; y++)
+          {
+            data_ptr = data;
+            max_data = data_ptr + width;
+            while (data_ptr < max_data)
+              {
+                value = 0;
+
+                value = (value << 1) | (pixels[*(iptr++)] & 1);
+                value = (value << 1) | (pixels[*(iptr++)] & 1);
+                value = (value << 1) | (pixels[*(iptr++)] & 1);
+                value = (value << 1) | (pixels[*(iptr++)] & 1);
+                value = (value << 1) | (pixels[*(iptr++)] & 1);
+                value = (value << 1) | (pixels[*(iptr++)] & 1);
+                value = (value << 1) | (pixels[*(iptr++)] & 1);
+                value = (value << 1) | (pixels[*(iptr++)] & 1);
+
+                *(data_ptr++) = value;
+              }
+            if (diff)
+              {
+                value = 0;
+                for (count = 0; count < diff; count++)
+                  {
+                    if (pixels[*(iptr++)] & 1)
+                      value |= (0x80 >> count);
+                  }
+                *(data_ptr) = value;
+              }
+            data += bpl;
+          }
+      else
+        for (y = 0; y < height; y++)
+          {
+            data_ptr = data;
+            max_data = data_ptr + width;
+            while (data_ptr < max_data)
+              {
+                value = 0;
+                iptr += 8;
+
+                value = (value << 1) | (pixels[*(--iptr)] & 1);
+                value = (value << 1) | (pixels[*(--iptr)] & 1);
+                value = (value << 1) | (pixels[*(--iptr)] & 1);
+                value = (value << 1) | (pixels[*(--iptr)] & 1);
+                value = (value << 1) | (pixels[*(--iptr)] & 1);
+                value = (value << 1) | (pixels[*(--iptr)] & 1);
+                value = (value << 1) | (pixels[*(--iptr)] & 1);
+                value = (value << 1) | (pixels[*(--iptr)] & 1);
+
+                iptr += 8;
+                *(data_ptr++) = value;
+              }
+            if (diff)
+              {
+                value = 0;
+                for (count = 0; count < diff; count++)
+                  {
+                    if (pixels[*(iptr++)] & 1)
+                      value |= (1 << count);
+                  }
+                *(data_ptr) = value;
+              }
+            data += bpl;
+          }
+
+#endif /* WITHOUT_SPEEDUPS */
+    }
+}
+
+int
+XpmCreatePixmapFromXpmImage (Display *display, Drawable d,
+                             XpmImage *image, Pixmap *pixmap_return,
+                             Pixmap *shapemask_return,
+                             XpmAttributes *attributes)
+{
+  XImage *ximage, *shapeimage;
+  int ErrorStatus;
+
+  /* initialize return values */
+  if (pixmap_return)
+    *pixmap_return = 0;
+  if (shapemask_return)
+    *shapemask_return = 0;
+
+  /* create the ximages */
+  ErrorStatus
+    = XpmCreateImageFromXpmImage (display, image,
+                                  (pixmap_return ? &ximage : NULL),
+                                  (shapemask_return ? &shapeimage
+                                                    : NULL),
+                                  attributes);
+  if (ErrorStatus < 0)
+    return (ErrorStatus);
+
+  /* create the pixmaps and destroy images */
+  if (pixmap_return && ximage)
+    {
+      xpmCreatePixmapFromImage (display, d, ximage, pixmap_return);
+      XDestroyImage (ximage);
+    }
+  if (shapemask_return && shapeimage)
+    {
+      xpmCreatePixmapFromImage (display, d, shapeimage,
+                                shapemask_return);
+      XDestroyImage (shapeimage);
+    }
+  return (ErrorStatus);
+}
+
+#else /* AMIGA */
+
+static void
+APutImagePixels (XImage *image, unsigned int width,
+                 unsigned int height, unsigned int *pixelindex,
+                 Pixel *pixels)
+{
+  unsigned int *data = pixelindex;
+  unsigned int x, y;
+  unsigned char *array;
+  XImage *tmp_img;
+  BOOL success = FALSE;
+
+  array = XpmMalloc ((((width + 15) >> 4) << 4) * sizeof (*array));
+  if (array != NULL)
+    {
+      tmp_img = AllocXImage ((((width + 15) >> 4) << 4), 1,
+                             image->rp->BitMap->Depth);
+      if (tmp_img != NULL)
+        {
+          for (y = 0; y < height; ++y)
+            {
+              for (x = 0; x < width; ++x)
+                array[x] = pixels[*(data++)];
+              WritePixelLine8 (image->rp, 0, y, width, array,
+                               tmp_img->rp);
+            }
+          FreeXImage (tmp_img);
+          success = TRUE;
+        }
+      XpmFree (array);
+    }
+
+  if (!success)
+    {
+      for (y = 0; y < height; ++y)
+        for (x = 0; x < width; ++x)
+          XPutPixel (image, x, y, pixels[*(data++)]);
+    }
+}
+
+#endif /* AMIGA */
+#else  /* FOR_MSW part follows */
+static void
+MSWPutImagePixels (Display *dc, XImage *image, unsigned int width,
+                   unsigned int height, unsigned int *pixelindex,
+                   Pixel *pixels)
+{
+  unsigned int *data = pixelindex;
+  unsigned int x, y;
+
+  for (y = 0; y < height; y++)
+    {
+      for (x = 0; x < width; x++)
+        {
+          XPutPixel (image, x, y,
+                     pixels[*(data++)]); /* data is [x+y*width] */
+        }
+    }
+}
+
+#endif /* FOR_MSW */
+
+#if !defined(FOR_MSW) && !defined(AMIGA)
+
+static int
+PutPixel1 (register XImage *ximage, int x, int y, unsigned long pixel)
+{
+  register char *src;
+  register char *dst;
+  register int i;
+  Pixel px;
+  int nbytes;
+
+  if (x < 0 || y < 0)
+    return 0;
+
+  for (i = 0, px = pixel; i < sizeof (unsigned long); i++, px >>= 8)
+    ((unsigned char *) &pixel)[i] = px;
+  src = &ximage->data[XYINDEX (x, y, ximage)];
+  dst = (char *) &px;
+  px = 0;
+  nbytes = ximage->bitmap_unit >> 3;
+  for (i = nbytes; --i >= 0;)
+    *dst++ = *src++;
+  XYNORMALIZE (&px, ximage);
+  i = ((x + ximage->xoffset) % ximage->bitmap_unit);
+  _putbits ((char *) &pixel, i, 1, (char *) &px);
+  XYNORMALIZE (&px, ximage);
+  src = (char *) &px;
+  dst = &ximage->data[XYINDEX (x, y, ximage)];
+  for (i = nbytes; --i >= 0;)
+    *dst++ = *src++;
+
+  return 1;
+}
+
+static int
+PutPixel (register XImage *ximage, int x, int y, unsigned long pixel)
+{
+  register char *src;
+  register char *dst;
+  register int i;
+  Pixel px;
+  unsigned int nbytes, ibpp;
+
+  if (x < 0 || y < 0)
+    return 0;
+
+  ibpp = ximage->bits_per_pixel;
+  if (ximage->depth == 4)
+    pixel &= 0xf;
+  for (i = 0, px = pixel; i < sizeof (unsigned long); i++, px >>= 8)
+    ((unsigned char *) &pixel)[i] = px;
+  src = &ximage->data[ZINDEX (x, y, ximage)];
+  dst = (char *) &px;
+  px = 0;
+  nbytes = (ibpp + 7) >> 3;
+  for (i = nbytes; --i >= 0;)
+    *dst++ = *src++;
+  ZNORMALIZE (&px, ximage);
+  _putbits ((char *) &pixel, (x * ibpp) & 7, ibpp, (char *) &px);
+  ZNORMALIZE (&px, ximage);
+  src = (char *) &px;
+  dst = &ximage->data[ZINDEX (x, y, ximage)];
+  for (i = nbytes; --i >= 0;)
+    *dst++ = *src++;
+
+  return 1;
+}
+
+#if !defined(WORD64) && !defined(LONG64)
+static int
+PutPixel32 (register XImage *ximage, int x, int y,
+            unsigned long pixel)
+{
+  unsigned char *addr;
+
+  if (x < 0 || y < 0)
+    return 0;
+
+  addr = &((unsigned char *) ximage->data)[ZINDEX32 (x, y, ximage)];
+  *((unsigned long *) addr) = pixel;
+  return 1;
+}
+#endif
+
+static int
+PutPixel32MSB (register XImage *ximage, int x, int y,
+               unsigned long pixel)
+{
+  unsigned char *addr;
+
+  if (x < 0 || y < 0)
+    return 0;
+
+  addr = &((unsigned char *) ximage->data)[ZINDEX32 (x, y, ximage)];
+  addr[0] = pixel >> 24;
+  addr[1] = pixel >> 16;
+  addr[2] = pixel >> 8;
+  addr[3] = pixel;
+  return 1;
+}
+
+static int
+PutPixel32LSB (register XImage *ximage, int x, int y,
+               unsigned long pixel)
+{
+  unsigned char *addr;
+
+  if (x < 0 || y < 0)
+    return 0;
+
+  addr = &((unsigned char *) ximage->data)[ZINDEX32 (x, y, ximage)];
+  addr[3] = pixel >> 24;
+  addr[2] = pixel >> 16;
+  addr[1] = pixel >> 8;
+  addr[0] = pixel;
+  return 1;
+}
+
+static int
+PutPixel16MSB (register XImage *ximage, int x, int y,
+               unsigned long pixel)
+{
+  unsigned char *addr;
+
+  if (x < 0 || y < 0)
+    return 0;
+
+  addr = &((unsigned char *) ximage->data)[ZINDEX16 (x, y, ximage)];
+  addr[0] = pixel >> 8;
+  addr[1] = pixel;
+  return 1;
+}
+
+static int
+PutPixel16LSB (register XImage *ximage, int x, int y,
+               unsigned long pixel)
+{
+  unsigned char *addr;
+
+  if (x < 0 || y < 0)
+    return 0;
+
+  addr = &((unsigned char *) ximage->data)[ZINDEX16 (x, y, ximage)];
+  addr[1] = pixel >> 8;
+  addr[0] = pixel;
+  return 1;
+}
+
+static int
+PutPixel8 (register XImage *ximage, int x, int y, unsigned long pixel)
+{
+  if (x < 0 || y < 0)
+    return 0;
+
+  ximage->data[ZINDEX8 (x, y, ximage)] = pixel;
+  return 1;
+}
+
+static int
+PutPixel1MSB (register XImage *ximage, int x, int y,
+              unsigned long pixel)
+{
+  if (x < 0 || y < 0)
+    return 0;
+
+  if (pixel & 1)
+    ximage->data[ZINDEX1 (x, y, ximage)] |= 0x80 >> (x & 7);
+  else
+    ximage->data[ZINDEX1 (x, y, ximage)] &= ~(0x80 >> (x & 7));
+  return 1;
+}
+
+static int
+PutPixel1LSB (register XImage *ximage, int x, int y,
+              unsigned long pixel)
+{
+  if (x < 0 || y < 0)
+    return 0;
+
+  if (pixel & 1)
+    ximage->data[ZINDEX1 (x, y, ximage)] |= 1 << (x & 7);
+  else
+    ximage->data[ZINDEX1 (x, y, ximage)] &= ~(1 << (x & 7));
+  return 1;
+}
+
+#endif /* not FOR_MSW && not AMIGA */
+
+/*
+ * This function parses an Xpm file or data and directly create an
+ * XImage
+ */
+int
+xpmParseDataAndCreate (Display *display, xpmData *data,
+                       XImage **image_return,
+                       XImage **shapeimage_return, XpmImage *image,
+                       XpmInfo *info, XpmAttributes *attributes)
+{
+  /* variables stored in the XpmAttributes structure */
+  Visual *visual;
+  Colormap colormap;
+  unsigned int depth;
+  int bitmap_format;
+  XpmFreeColorsFunc freeColors;
+
+  /* variables to return */
+  XImage *ximage = NULL;
+  XImage *shapeimage = NULL;
+  unsigned int mask_pixel_index = XpmUndefPixel;
+
+  /* calculation variables */
+  Pixel *image_pixels = NULL;
+  Pixel *mask_pixels = NULL;
+  Pixel *alloc_pixels = NULL;
+  Pixel *used_pixels = NULL;
+  unsigned int nalloc_pixels = 0;
+  unsigned int nused_pixels = 0;
+  unsigned int width, height, ncolors, cpp;
+  unsigned int x_hotspot, y_hotspot, hotspot = 0, extensions = 0;
+  XpmColor *colorTable = NULL;
+  char *hints_cmt = NULL;
+  char *colors_cmt = NULL;
+  char *pixels_cmt = NULL;
+
+  unsigned int cmts;
+  int ErrorStatus;
+  xpmHashTable hashtable;
+
+  /* initialize return values */
+  if (image_return)
+    *image_return = NULL;
+  if (shapeimage_return)
+    *shapeimage_return = NULL;
+
+  /* retrieve information from the XpmAttributes */
+  if (attributes && (attributes->valuemask & XpmVisual))
+    visual = attributes->visual;
+  else
+    visual = XDefaultVisual (display, XDefaultScreen (display));
+
+  if (attributes && (attributes->valuemask & XpmColormap))
+    colormap = attributes->colormap;
+  else
+    colormap = XDefaultColormap (display, XDefaultScreen (display));
+
+  if (attributes && (attributes->valuemask & XpmDepth))
+    depth = attributes->depth;
+  else
+    depth = XDefaultDepth (display, XDefaultScreen (display));
+
+  if (attributes && (attributes->valuemask & XpmBitmapFormat))
+    bitmap_format = attributes->bitmap_format;
+  else
+    bitmap_format = ZPixmap;
+
+  if (attributes && (attributes->valuemask & XpmFreeColors))
+    freeColors = attributes->free_colors;
+  else
+    freeColors = FreeColors;
+
+  cmts = info && (info->valuemask & XpmReturnComments);
+
+  /*
+   * parse the header
+   */
+  ErrorStatus = xpmParseHeader (data);
+  if (ErrorStatus != XpmSuccess)
+    return (ErrorStatus);
+
+  /*
+   * read values
+   */
+  ErrorStatus
+    = xpmParseValues (data, &width, &height, &ncolors, &cpp,
+                      &x_hotspot, &y_hotspot, &hotspot, &extensions);
+  if (ErrorStatus != XpmSuccess)
+    return (ErrorStatus);
+
+  /*
+   * store the hints comment line
+   */
+  if (cmts)
+    xpmGetCmt (data, &hints_cmt);
+
+  /*
+   * init the hashtable
+   */
+  if (USE_HASHTABLE)
+    {
+      ErrorStatus = xpmHashTableInit (&hashtable);
+      if (ErrorStatus != XpmSuccess)
+        RETURN (ErrorStatus);
+    }
+
+  /*
+   * read colors
+   */
+  ErrorStatus
+    = xpmParseColors (data, ncolors, cpp, &colorTable, &hashtable);
+  if (ErrorStatus != XpmSuccess)
+    RETURN (ErrorStatus);
+
+  /*
+   * store the colors comment line
+   */
+  if (cmts)
+    xpmGetCmt (data, &colors_cmt);
+
+  /* malloc pixels index tables */
+  if (ncolors >= UINT_MAX / sizeof (Pixel))
+    RETURN (XpmNoMemory);
+
+  image_pixels = (Pixel *) XpmMalloc (sizeof (Pixel) * ncolors);
+  if (!image_pixels)
+    RETURN (XpmNoMemory);
+
+  mask_pixels = (Pixel *) XpmMalloc (sizeof (Pixel) * ncolors);
+  if (!mask_pixels)
+    RETURN (XpmNoMemory);
+
+  /* maximum of allocated pixels will be the number of colors */
+  alloc_pixels = (Pixel *) XpmMalloc (sizeof (Pixel) * ncolors);
+  if (!alloc_pixels)
+    RETURN (XpmNoMemory);
+
+  /* maximum of allocated pixels will be the number of colors */
+  used_pixels = (Pixel *) XpmMalloc (sizeof (Pixel) * ncolors);
+  if (!used_pixels)
+    RETURN (XpmNoMemory);
+
+  /* get pixel colors, store them in index tables */
+  ErrorStatus
+    = CreateColors (display, attributes, colorTable, ncolors,
+                    image_pixels, mask_pixels, &mask_pixel_index,
+                    alloc_pixels, &nalloc_pixels, used_pixels,
+                    &nused_pixels);
+
+  if (ErrorStatus != XpmSuccess
+      && (ErrorStatus < 0
+          || (attributes && (attributes->valuemask & XpmExactColors)
+              && attributes->exactColors)))
+    RETURN (ErrorStatus);
+
+  /* now create the ximage */
+  if (image_return)
+    {
+      ErrorStatus
+        = CreateXImage (display, visual, depth,
+                        (depth == 1 ? bitmap_format : ZPixmap), width,
+                        height, &ximage);
+      if (ErrorStatus != XpmSuccess)
+        RETURN (ErrorStatus);
+
+#if !defined(FOR_MSW) && !defined(AMIGA)
+
+      /*
+       * set the XImage pointer function, to be used with XPutPixel,
+       * to an internal optimized function
+       */
+
+      if (ximage->bits_per_pixel == 8)
+        ximage->f.put_pixel = PutPixel8;
+      else if (((ximage->bits_per_pixel | ximage->depth) == 1)
+               && (ximage->byte_order == ximage->bitmap_bit_order))
+        if (ximage->bitmap_bit_order == MSBFirst)
+          ximage->f.put_pixel = PutPixel1MSB;
+        else
+          ximage->f.put_pixel = PutPixel1LSB;
+      else if (ximage->bits_per_pixel == 16)
+        if (ximage->bitmap_bit_order == MSBFirst)
+          ximage->f.put_pixel = PutPixel16MSB;
+        else
+          ximage->f.put_pixel = PutPixel16LSB;
+      else if (ximage->bits_per_pixel == 32)
+#if !defined(WORD64) && !defined(LONG64)
+        if (*((char *) &byteorderpixel) == ximage->byte_order)
+          ximage->f.put_pixel = PutPixel32;
+        else
+#endif
+          if (ximage->bitmap_bit_order == MSBFirst)
+          ximage->f.put_pixel = PutPixel32MSB;
+        else
+          ximage->f.put_pixel = PutPixel32LSB;
+      else if ((ximage->bits_per_pixel | ximage->depth) == 1)
+        ximage->f.put_pixel = PutPixel1;
+      else
+        ximage->f.put_pixel = PutPixel;
+#endif /* not FOR_MSW && not AMIGA */
+    }
+
+  /* create the shape mask image */
+  if (mask_pixel_index != XpmUndefPixel && shapeimage_return)
+    {
+      ErrorStatus = CreateXImage (display, visual, 1, bitmap_format,
+                                  width, height, &shapeimage);
+      if (ErrorStatus != XpmSuccess)
+        RETURN (ErrorStatus);
+
+#if !defined(FOR_MSW) && !defined(AMIGA)
+      if (shapeimage->bitmap_bit_order == MSBFirst)
+        shapeimage->f.put_pixel = PutPixel1MSB;
+      else
+        shapeimage->f.put_pixel = PutPixel1LSB;
+#endif
+    }
+
+  /*
+   * read pixels and put them in the XImage
+   */
+  ErrorStatus = ParseAndPutPixels (
+#ifdef FOR_MSW
+    display,
+#endif
+    data, width, height, ncolors, cpp, colorTable, &hashtable, ximage,
+    image_pixels, shapeimage, mask_pixels);
+  XpmFree (image_pixels);
+  image_pixels = NULL;
+  XpmFree (mask_pixels);
+  mask_pixels = NULL;
+
+  /*
+   * free the hastable
+   */
+  if (ErrorStatus != XpmSuccess)
+    RETURN (ErrorStatus);
+  else if (USE_HASHTABLE)
+    xpmHashTableFree (&hashtable);
+
+  /*
+   * store the pixels comment line
+   */
+  if (cmts)
+    xpmGetCmt (data, &pixels_cmt);
+
+  /*
+   * parse extensions
+   */
+  if (info && (info->valuemask & XpmReturnExtensions))
+    {
+      if (extensions)
+        {
+          ErrorStatus = xpmParseExtensions (data, &info->extensions,
+                                            &info->nextensions);
+          if (ErrorStatus != XpmSuccess)
+            RETURN (ErrorStatus);
+        }
+      else
+        {
+          info->extensions = NULL;
+          info->nextensions = 0;
+        }
+    }
+  /*
+   * store found informations in the XpmImage structure
+   */
+  image->width = width;
+  image->height = height;
+  image->cpp = cpp;
+  image->ncolors = ncolors;
+  image->colorTable = colorTable;
+  image->data = NULL;
+
+  if (info)
+    {
+      if (cmts)
+        {
+          info->hints_cmt = hints_cmt;
+          info->colors_cmt = colors_cmt;
+          info->pixels_cmt = pixels_cmt;
+        }
+      if (hotspot)
+        {
+          info->x_hotspot = x_hotspot;
+          info->y_hotspot = y_hotspot;
+          info->valuemask |= XpmHotspot;
+        }
+    }
+  /* if requested return used pixels in the XpmAttributes structure */
+  if (attributes
+      && (attributes->valuemask & XpmReturnPixels ||
+          /* 3.2 backward compatibility code */
+          attributes->valuemask & XpmReturnInfos))
+    {
+      /* end 3.2 bc */
+      attributes->pixels = used_pixels;
+      attributes->npixels = nused_pixels;
+      attributes->mask_pixel = mask_pixel_index;
+    }
+  else
+    XpmFree (used_pixels);
+
+  /* if requested return alloc'ed pixels in the XpmAttributes
+   * structure */
+  if (attributes && (attributes->valuemask & XpmReturnAllocPixels))
+    {
+      attributes->alloc_pixels = alloc_pixels;
+      attributes->nalloc_pixels = nalloc_pixels;
+    }
+  else
+    XpmFree (alloc_pixels);
+
+  /* return created images */
+  if (image_return)
+    *image_return = ximage;
+  if (shapeimage_return)
+    *shapeimage_return = shapeimage;
+
+  return (XpmSuccess);
+
+/* exit point in case of error, free only locally allocated variables
+ */
+error:
+  if (USE_HASHTABLE)
+    xpmHashTableFree (&hashtable);
+  if (colorTable)
+    xpmFreeColorTable (colorTable, ncolors);
+  if (hints_cmt)
+    XpmFree (hints_cmt);
+  if (colors_cmt)
+    XpmFree (colors_cmt);
+  if (pixels_cmt)
+    XpmFree (pixels_cmt);
+  if (ximage)
+    XDestroyImage (ximage);
+  if (shapeimage)
+    XDestroyImage (shapeimage);
+  if (image_pixels)
+    XpmFree (image_pixels);
+  if (mask_pixels)
+    XpmFree (mask_pixels);
+  if (nalloc_pixels)
+    (*freeColors) (display, colormap, alloc_pixels, nalloc_pixels,
+                   NULL);
+  if (alloc_pixels)
+    XpmFree (alloc_pixels);
+  if (used_pixels)
+    XpmFree (used_pixels);
+
+  return (ErrorStatus);
+}
+
+static int
+ParseAndPutPixels (
+#ifdef FOR_MSW
+  Display *dc,
+#endif
+  xpmData *data, unsigned int width, unsigned int height,
+  unsigned int ncolors, unsigned int cpp, XpmColor *colorTable,
+  xpmHashTable *hashtable, XImage *image, Pixel *image_pixels,
+  XImage *shapeimage, Pixel *shape_pixels)
+{
+  unsigned int a, x, y;
+
+  switch (cpp)
+    {
+    case (1): /* Optimize for single character
+               * colors */
+      {
+        unsigned short colidx[256];
+        if (ncolors > 256)
+          return (XpmFileInvalid);
+
+        bzero ((char *) colidx, 256 * sizeof (short));
+        for (a = 0; a < ncolors; a++)
+          colidx[(unsigned char) colorTable[a].string[0]] = a + 1;
+
+        for (y = 0; y < height; y++)
+          {
+            xpmNextString (data);
+            for (x = 0; x < width; x++)
+              {
+                int c = xpmGetC (data);
+
+                if (c > 0 && c < 256 && colidx[c] != 0)
+                  {
+                    XPutPixel (image, x, y,
+                               image_pixels[colidx[c] - 1]);
+                    if (shapeimage)
+                      XPutPixel (shapeimage, x, y,
+                                 shape_pixels[colidx[c] - 1]);
+                  }
+                else
+                  return (XpmFileInvalid);
+              }
+          }
+      }
+      break;
+
+    case (2): /* Optimize for double character
+               * colors */
+      {
+/* free all allocated pointers at all exits */
+#define FREE_CIDX             \
+  {                           \
+    int f;                    \
+    for (f = 0; f < 256; f++) \
+      if (cidx[f])            \
+        XpmFree (cidx[f]);    \
+  }
+
+        /* array of pointers malloced by need */
+        unsigned short *cidx[256];
+        unsigned int char1;
+
+        bzero ((char *) cidx,
+               256 * sizeof (unsigned short *)); /* init */
+        for (a = 0; a < ncolors; a++)
+          {
+            char1 = (unsigned char) colorTable[a].string[0];
+            if (cidx[char1] == NULL)
+              { /* get new memory */
+                cidx[char1] = (unsigned short *)
+                  XpmCalloc (256, sizeof (unsigned short));
+                if (cidx[char1] == NULL)
+                  { /* new block failed */
+                    FREE_CIDX;
+                    return (XpmNoMemory);
+                  }
+              }
+            cidx[char1][(unsigned char) colorTable[a].string[1]]
+              = a + 1;
+          }
+
+        for (y = 0; y < height; y++)
+          {
+            xpmNextString (data);
+            for (x = 0; x < width; x++)
+              {
+                int cc1 = xpmGetC (data);
+                if (cc1 > 0 && cc1 < 256)
+                  {
+                    int cc2 = xpmGetC (data);
+                    if (cc2 > 0 && cc2 < 256 && cidx[cc1]
+                        && cidx[cc1][cc2] != 0)
+                      {
+                        XPutPixel (image, x, y,
+                                   image_pixels[cidx[cc1][cc2] - 1]);
+                        if (shapeimage)
+                          XPutPixel (shapeimage, x, y,
+                                     shape_pixels[cidx[cc1][cc2]
+                                                  - 1]);
+                      }
+                    else
+                      {
+                        FREE_CIDX;
+                        return (XpmFileInvalid);
+                      }
+                  }
+                else
+                  {
+                    FREE_CIDX;
+                    return (XpmFileInvalid);
+                  }
+              }
+          }
+        FREE_CIDX;
+      }
+      break;
+
+    default: /* Non-optimized case of long color
+              * names */
+      {
+        char *s;
+        char buf[BUFSIZ];
+
+        if (cpp >= sizeof (buf))
+          return (XpmFileInvalid);
+
+        buf[cpp] = '\0';
+        if (USE_HASHTABLE)
+          {
+            xpmHashAtom *slot;
+
+            for (y = 0; y < height; y++)
+              {
+                xpmNextString (data);
+                for (x = 0; x < width; x++)
+                  {
+                    for (a = 0, s = buf; a < cpp; a++, s++)
+                      *s = xpmGetC (data);
+                    slot = xpmHashSlot (hashtable, buf);
+                    if (!*slot) /* no color matches */
+                      return (XpmFileInvalid);
+                    XPutPixel (image, x, y,
+                               image_pixels[HashColorIndex (slot)]);
+                    if (shapeimage)
+                      XPutPixel (shapeimage, x, y,
+                                 shape_pixels[HashColorIndex (slot)]);
+                  }
+              }
+          }
+        else
+          {
+            for (y = 0; y < height; y++)
+              {
+                xpmNextString (data);
+                for (x = 0; x < width; x++)
+                  {
+                    for (a = 0, s = buf; a < cpp; a++, s++)
+                      *s = xpmGetC (data);
+                    for (a = 0; a < ncolors; a++)
+                      if (!strcmp (colorTable[a].string, buf))
+                        break;
+                    if (a == ncolors) /* no color matches */
+                      return (XpmFileInvalid);
+                    XPutPixel (image, x, y, image_pixels[a]);
+                    if (shapeimage)
+                      XPutPixel (shapeimage, x, y, shape_pixels[a]);
+                  }
+              }
+          }
+      }
+      break;
+    }
+  return (XpmSuccess);
+}
diff --git a/be_xpm/data.c b/be_xpm/data.c
new file mode 100644
index 0000000000..898889c37a
--- /dev/null
+++ b/be_xpm/data.c
@@ -0,0 +1,481 @@
+/*
+ * Copyright (C) 1989-95 GROUPE BULL
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * GROUPE BULL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Except as contained in this notice, the name of GROUPE BULL shall not be
+ * used in advertising or otherwise to promote the sale, use or other dealings
+ * in this Software without prior written authorization from GROUPE BULL.
+ */
+
+/*****************************************************************************\
+* data.c:                                                                     *
+*                                                                             *
+*  XPM library                                                                *
+*  IO utilities                                                               *
+*                                                                             *
+*  Developed by Arnaud Le Hors                                                *
+\*****************************************************************************/
+
+/* October 2004, source code review by Thomas Biege <thomas@suse.de> */
+
+#ifndef CXPMPROG
+#if 0
+/* Official version number */
+static char *RCS_Version = "$XpmVersion: 3.4k $";
+
+/* Internal version number */
+static char *RCS_Id = "Id: xpm.shar,v 3.71 1998/03/19 19:47:14 lehors Exp $";
+#endif
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include "XpmI.h"
+#endif
+#include <ctype.h>
+
+#ifndef CXPMPROG
+#define Getc(data, file) getc(file)
+#define Ungetc(data, c, file) ungetc(c, file)
+#endif
+
+static int
+ParseComment(xpmData *data)
+{
+    if (data->type == XPMBUFFER) {
+	register char c;
+	register unsigned int n = 0;
+	unsigned int notend;
+	char *s;
+	const char *s2;
+
+	s = data->Comment;
+	*s = data->Bcmt[0];
+
+	/* skip the string beginning comment */
+	s2 = data->Bcmt;
+	do {
+	    c = *data->cptr++;
+	    *++s = c;
+	    n++;
+	    s2++;
+	} while (c == *s2 && *s2 != '\0' && c);
+
+	if (*s2 != '\0') {
+	    /* this wasn't the beginning of a comment */
+	    data->cptr -= n;
+	    return 0;
+	}
+	/* store comment */
+	data->Comment[0] = *s;
+	s = data->Comment;
+	notend = 1;
+	n = 0;
+	while (notend) {
+	    s2 = data->Ecmt;
+	    while (*s != *s2 && c) {
+		c = *data->cptr++;
+		if (n == XPMMAXCMTLEN - 1)  { /* forget it */
+		    s = data->Comment;
+		    n = 0;
+		}
+		*++s = c;
+		n++;
+	    }
+	    data->CommentLength = n;
+	    do {
+		c = *data->cptr++;
+		if (n == XPMMAXCMTLEN - 1)  { /* forget it */
+		    s = data->Comment;
+		    n = 0;
+		}
+		*++s = c;
+		n++;
+		s2++;
+	    } while (c == *s2 && *s2 != '\0' && c);
+	    if (*s2 == '\0') {
+		/* this is the end of the comment */
+		notend = 0;
+		data->cptr--;
+	    }
+	}
+	return 0;
+    } else {
+	FILE *file = data->stream.file;
+	register int c;
+	register unsigned int n = 0, a;
+	unsigned int notend;
+	char *s;
+	const char *s2;
+
+	s = data->Comment;
+	*s = data->Bcmt[0];
+
+	/* skip the string beginning comment */
+	s2 = data->Bcmt;
+	do {
+	    c = Getc(data, file);
+	    *++s = c;
+	    n++;
+	    s2++;
+	} while (c == *s2 && *s2 != '\0' && c != EOF);
+
+	if (*s2 != '\0') {
+	    /* this wasn't the beginning of a comment */
+	    /* put characters back in the order that we got them */
+	    for (a = n; a > 0; a--, s--)
+		Ungetc(data, *s, file);
+	    return 0;
+	}
+	/* store comment */
+	data->Comment[0] = *s;
+	s = data->Comment;
+	notend = 1;
+	n = 0;
+	while (notend) {
+	    s2 = data->Ecmt;
+	    while (*s != *s2 && c != EOF) {
+		c = Getc(data, file);
+		if (n == XPMMAXCMTLEN - 1)  { /* forget it */
+		    s = data->Comment;
+		    n = 0;
+		}
+		*++s = c;
+		n++;
+	    }
+	    data->CommentLength = n;
+	    do {
+		c = Getc(data, file);
+		if (n == XPMMAXCMTLEN - 1)  { /* forget it */
+		    s = data->Comment;
+		    n = 0;
+		}
+		*++s = c;
+		n++;
+		s2++;
+	    } while (c == *s2 && *s2 != '\0' && c != EOF);
+	    if (*s2 == '\0') {
+		/* this is the end of the comment */
+		notend = 0;
+		Ungetc(data, *s, file);
+	    }
+	}
+	return 0;
+    }
+}
+
+/*
+ * skip to the end of the current string and the beginning of the next one
+ */
+int
+xpmNextString(xpmData *data)
+{
+    if (!data->type)
+	data->cptr = (data->stream.data)[++data->line];
+    else if (data->type == XPMBUFFER) {
+	register char c;
+
+	/* get to the end of the current string */
+	if (data->Eos)
+	    while ((c = *data->cptr++) && c != data->Eos);
+
+	/*
+	 * then get to the beginning of the next string looking for possible
+	 * comment
+	 */
+	if (data->Bos) {
+	    while ((c = *data->cptr++) && c != data->Bos)
+		if (data->Bcmt && c == data->Bcmt[0])
+		    ParseComment(data);
+	} else if (data->Bcmt) {	/* XPM2 natural */
+	    while ((c = *data->cptr++) == data->Bcmt[0])
+		ParseComment(data);
+	    data->cptr--;
+	}
+    } else {
+	register int c;
+	FILE *file = data->stream.file;
+
+	/* get to the end of the current string */
+	if (data->Eos)
+	    while ((c = Getc(data, file)) != data->Eos && c != EOF);
+
+	/*
+	 * then get to the beginning of the next string looking for possible
+	 * comment
+	 */
+	if (data->Bos) {
+	    while ((c = Getc(data, file)) != data->Bos && c != EOF)
+		if (data->Bcmt && c == data->Bcmt[0])
+		    ParseComment(data);
+
+	} else if (data->Bcmt) {	/* XPM2 natural */
+	    while ((c = Getc(data, file)) == data->Bcmt[0])
+		ParseComment(data);
+	    Ungetc(data, c, file);
+	}
+    }
+    return 0;
+}
+
+
+/*
+ * skip whitespace and return the following word
+ */
+unsigned int
+xpmNextWord(
+    xpmData		*data,
+    char		*buf,
+    unsigned int	 buflen)
+{
+    register unsigned int n = 0;
+    int c;
+
+    if (!data->type || data->type == XPMBUFFER) {
+	while (isspace(c = *data->cptr) && c != data->Eos)
+	    data->cptr++;
+	do {
+	    c = *data->cptr++;
+	    *buf++ = c;
+	    n++;
+	} while (!isspace(c) && c != data->Eos && n < buflen);
+	n--;
+	data->cptr--;
+    } else {
+	FILE *file = data->stream.file;
+
+	while ((c = Getc(data, file)) != EOF && isspace(c) && c != data->Eos);
+	while (!isspace(c) && c != data->Eos && c != EOF && n < buflen) {
+	    *buf++ = c;
+	    n++;
+	    c = Getc(data, file);
+	}
+	Ungetc(data, c, file);
+    }
+    return (n); /* this returns bytes read + 1 */
+}
+
+/*
+ * skip whitespace and compute the following unsigned int,
+ * returns 1 if one is found and 0 if not
+ */
+int
+xpmNextUI(
+    xpmData		*data,
+    unsigned int	*ui_return)
+{
+    char buf[BUFSIZ];
+    int l;
+
+    l = xpmNextWord(data, buf, BUFSIZ);
+    return xpmatoui(buf, l, ui_return);
+}
+
+/*
+ * return end of string - WARNING: malloc!
+ */
+int
+xpmGetString(
+    xpmData		 *data,
+    char		**sptr,
+    unsigned int	 *l)
+{
+    unsigned int i, n = 0;
+    int c;
+    char *p = NULL, *q, buf[BUFSIZ];
+
+    if (!data->type || data->type == XPMBUFFER) {
+	if (data->cptr) {
+	    char *start = data->cptr;
+	    while ((c = *data->cptr) && c != data->Eos)
+		data->cptr++;
+	    n = data->cptr - start + 1;
+	    p = (char *) XpmMalloc(n);
+	    if (!p)
+		return (XpmNoMemory);
+	    strncpy(p, start, n);
+	    if (data->type)		/* XPMBUFFER */
+		p[n - 1] = '\0';
+	}
+    } else {
+	FILE *file = data->stream.file;
+
+	if ((c = Getc(data, file)) == EOF)
+	    return (XpmFileInvalid);
+
+	i = 0;
+	q = buf;
+	p = (char *) XpmMalloc(1);
+	while (c != data->Eos && c != EOF) {
+	    if (i == BUFSIZ) {
+		/* get to the end of the buffer */
+		/* malloc needed memory */
+		q = (char *) XpmRealloc(p, n + i);
+		if (!q) {
+		    XpmFree(p);
+		    return (XpmNoMemory);
+		}
+		p = q;
+		q += n;
+		/* and copy what we already have */
+		strncpy(q, buf, i);
+		n += i;
+		i = 0;
+		q = buf;
+	    }
+	    *q++ = c;
+	    i++;
+	    c = Getc(data, file);
+	}
+	if (c == EOF) {
+	    XpmFree(p);
+	    return (XpmFileInvalid);
+	}
+	if (n + i != 0) {
+	    /* malloc needed memory */
+	    q = (char *) XpmRealloc(p, n + i + 1);
+	    if (!q) {
+		XpmFree(p);
+		return (XpmNoMemory);
+	    }
+	    p = q;
+	    q += n;
+	    /* and copy the buffer */
+	    strncpy(q, buf, i);
+	    n += i;
+	    p[n++] = '\0';
+	} else {
+	    *p = '\0';
+	    n = 1;
+	}
+	Ungetc(data, c, file);
+    }
+    *sptr = p;
+    *l = n;
+    return (XpmSuccess);
+}
+
+/*
+ * get the current comment line
+ */
+int
+xpmGetCmt(
+    xpmData	 *data,
+    char	**cmt)
+{
+    if (!data->type)
+	*cmt = NULL;
+    else if (data->CommentLength != 0 && data->CommentLength < UINT_MAX - 1) {
+	if( (*cmt = (char *) XpmMalloc(data->CommentLength + 1)) == NULL)
+		return XpmNoMemory;
+	strncpy(*cmt, data->Comment, data->CommentLength);
+	(*cmt)[data->CommentLength] = '\0';
+	data->CommentLength = 0;
+    } else
+	*cmt = NULL;
+    return 0;
+}
+
+xpmDataType xpmDataTypes[] =
+{
+    {"", "!", "\n", '\0', '\n', "", "", "", ""},	/* Natural type */
+    {"C", "/*", "*/", '"', '"', ",\n", "static char *", "[] = {\n", "};\n"},
+    {"Lisp", ";", "\n", '"', '"', "\n", "(setq ", " '(\n", "))\n"},
+    {NULL, NULL, NULL, 0, 0, NULL, NULL, NULL, NULL}
+};
+
+/*
+ * parse xpm header
+ */
+int
+xpmParseHeader(xpmData *data)
+{
+    char buf[BUFSIZ+1] = {0};
+    int l, n = 0;
+
+    if (data->type) {
+	data->Bos = '\0';
+	data->Eos = '\n';
+	data->Bcmt = data->Ecmt = NULL;
+	l = xpmNextWord(data, buf, BUFSIZ);
+	if (l == 7 && !strncmp("#define", buf, 7)) {
+	    /* this maybe an XPM 1 file */
+	    char *ptr;
+
+	    l = xpmNextWord(data, buf, BUFSIZ);
+	    if (!l)
+		return (XpmFileInvalid);
+	    buf[l] = '\0';
+	    ptr = strrchr(buf, '_');
+	    if (!ptr || strncmp("_format", ptr, l - (ptr - buf)))
+		return XpmFileInvalid;
+	    /* this is definitely an XPM 1 file */
+	    data->format = 1;
+	    n = 1;			/* handle XPM1 as mainly XPM2 C */
+	} else {
+
+	    /*
+	     * skip the first word, get the second one, and see if this is
+	     * XPM 2 or 3
+	     */
+	    l = xpmNextWord(data, buf, BUFSIZ);
+	    if ((l == 3 && !strncmp("XPM", buf, 3)) ||
+		(l == 4 && !strncmp("XPM2", buf, 4))) {
+		if (l == 3)
+		    n = 1;		/* handle XPM as XPM2 C */
+		else {
+		    /* get the type key word */
+		    l = xpmNextWord(data, buf, BUFSIZ);
+
+		    /*
+		     * get infos about this type
+		     */
+		    while (xpmDataTypes[n].type
+			   && strncmp(xpmDataTypes[n].type, buf, l))
+			n++;
+		}
+		data->format = 0;
+	    } else
+		/* nope this is not an XPM file */
+		return XpmFileInvalid;
+	}
+	if (xpmDataTypes[n].type) {
+	    if (n == 0) {		/* natural type */
+		data->Bcmt = xpmDataTypes[n].Bcmt;
+		data->Ecmt = xpmDataTypes[n].Ecmt;
+		xpmNextString(data);	/* skip the end of the headerline */
+		data->Bos = xpmDataTypes[n].Bos;
+		data->Eos = xpmDataTypes[n].Eos;
+	    } else {
+		data->Bcmt = xpmDataTypes[n].Bcmt;
+		data->Ecmt = xpmDataTypes[n].Ecmt;
+		if (!data->format) {	/* XPM 2 or 3 */
+		    data->Bos = xpmDataTypes[n].Bos;
+		    data->Eos = '\0';
+		    /* get to the beginning of the first string */
+		    xpmNextString(data);
+		    data->Eos = xpmDataTypes[n].Eos;
+		} else			/* XPM 1 skip end of line */
+		    xpmNextString(data);
+	    }
+	} else
+	    /* we don't know about that type of XPM file... */
+	    return XpmFileInvalid;
+    }
+    return XpmSuccess;
+}
diff --git a/be_xpm/hashtab.c b/be_xpm/hashtab.c
new file mode 100644
index 0000000000..1bd4109a41
--- /dev/null
+++ b/be_xpm/hashtab.c
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 1989-95 GROUPE BULL
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * GROUPE BULL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Except as contained in this notice, the name of GROUPE BULL shall not be
+ * used in advertising or otherwise to promote the sale, use or other dealings
+ * in this Software without prior written authorization from GROUPE BULL.
+ */
+
+/*****************************************************************************\
+* hashtab.c:                                                                  *
+*                                                                             *
+*  XPM library                                                                *
+*                                                                             *
+*  Developed by Arnaud Le Hors                                                *
+*  this originaly comes from Colas Nahaboo as a part of Wool                  *
+*                                                                             *
+\*****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include "XpmI.h"
+
+LFUNC(AtomMake, xpmHashAtom, (char *name, void *data));
+LFUNC(HashTableGrows, int, (xpmHashTable * table));
+
+static xpmHashAtom
+AtomMake(				/* makes an atom */
+    char	*name,			/* WARNING: is just pointed to */
+    void	*data)
+{
+    xpmHashAtom object = (xpmHashAtom) XpmMalloc(sizeof(struct _xpmHashAtom));
+
+    if (object) {
+	object->name = name;
+	object->data = data;
+    }
+    return object;
+}
+
+/************************\
+* 			 *
+*  hash table routines 	 *
+* 			 *
+\************************/
+
+/*
+ * Hash function definition:
+ * HASH_FUNCTION: hash function, hash = hashcode, hp = pointer on char,
+ *				 hash2 = temporary for hashcode.
+ * INITIAL_TABLE_SIZE in slots
+ * HASH_TABLE_GROWS how hash table grows.
+ */
+
+/* Mock lisp function */
+#define HASH_FUNCTION 	  hash = (hash << 5) - hash + *hp++;
+/* #define INITIAL_HASH_SIZE 2017 */
+#define INITIAL_HASH_SIZE 256		/* should be enough for colors */
+#define HASH_TABLE_GROWS  size = size * 2;
+
+/* aho-sethi-ullman's HPJ (sizes should be primes)*/
+#ifdef notdef
+#define HASH_FUNCTION	hash <<= 4; hash += *hp++; \
+    if(hash2 = hash & 0xf0000000) hash ^= (hash2 >> 24) ^ hash2;
+#define INITIAL_HASH_SIZE 4095		/* should be 2^n - 1 */
+#define HASH_TABLE_GROWS  size = size << 1 + 1;
+#endif
+
+/* GNU emacs function */
+/*
+#define HASH_FUNCTION 	  hash = (hash << 3) + (hash >> 28) + *hp++;
+#define INITIAL_HASH_SIZE 2017
+#define HASH_TABLE_GROWS  size = size * 2;
+*/
+
+/* end of hash functions */
+
+/*
+ * The hash table is used to store atoms via their NAME:
+ *
+ * NAME --hash--> ATOM |--name--> "foo"
+ *		       |--data--> any value which has to be stored
+ *
+ */
+
+/*
+ * xpmHashSlot gives the slot (pointer to xpmHashAtom) of a name
+ * (slot points to NULL if it is not defined)
+ *
+ */
+
+xpmHashAtom *
+xpmHashSlot(
+    xpmHashTable	*table,
+    char		*s)
+{
+    xpmHashAtom *atomTable = table->atomTable;
+    unsigned int hash;
+    xpmHashAtom *p;
+    char *hp = s;
+    char *ns;
+
+    hash = 0;
+    while (*hp) {			/* computes hash function */
+	HASH_FUNCTION
+    }
+    p = atomTable + hash % table->size;
+    while (*p) {
+	ns = (*p)->name;
+	if (ns[0] == s[0] && strcmp(ns, s) == 0)
+	    break;
+	p--;
+	if (p < atomTable)
+	    p = atomTable + table->size - 1;
+    }
+    return p;
+}
+
+static int
+HashTableGrows(xpmHashTable *table)
+{
+    xpmHashAtom *atomTable = table->atomTable;
+    unsigned int size = table->size;
+    xpmHashAtom *t, *p;
+    int i;
+    unsigned int oldSize = size;
+
+    t = atomTable;
+    HASH_TABLE_GROWS
+	table->size = size;
+    table->limit = size / 3;
+    if (size >= UINT_MAX / sizeof(*atomTable))
+	return (XpmNoMemory);
+    atomTable = (xpmHashAtom *) XpmMalloc(size * sizeof(*atomTable));
+    if (!atomTable)
+	return (XpmNoMemory);
+    table->atomTable = atomTable;
+    for (p = atomTable + size; p > atomTable;)
+	*--p = NULL;
+    for (i = 0, p = t; i < oldSize; i++, p++)
+	if (*p) {
+	    xpmHashAtom *ps = xpmHashSlot(table, (*p)->name);
+
+	    *ps = *p;
+	}
+    XpmFree(t);
+    return (XpmSuccess);
+}
+
+/*
+ * xpmHashIntern(table, name, data)
+ * an xpmHashAtom is created if name doesn't exist, with the given data.
+ */
+
+int
+xpmHashIntern(
+    xpmHashTable	*table,
+    char		*tag,
+    void		*data)
+{
+    xpmHashAtom *slot;
+
+    if (!*(slot = xpmHashSlot(table, tag))) {
+	/* undefined, make a new atom with the given data */
+	if (!(*slot = AtomMake(tag, data)))
+	    return (XpmNoMemory);
+	if (table->used >= table->limit) {
+	    int ErrorStatus;
+
+	    if ((ErrorStatus = HashTableGrows(table)) != XpmSuccess)
+		return (ErrorStatus);
+	    table->used++;
+	    return (XpmSuccess);
+	}
+	table->used++;
+    }
+    return (XpmSuccess);
+}
+
+/*
+ *  must be called before allocating any atom
+ */
+
+int
+xpmHashTableInit(xpmHashTable *table)
+{
+    xpmHashAtom *p;
+    xpmHashAtom *atomTable;
+
+    table->size = INITIAL_HASH_SIZE;
+    table->limit = table->size / 3;
+    table->used = 0;
+    table->atomTable = NULL;
+    if (table->size >= UINT_MAX / sizeof(*atomTable))
+	return (XpmNoMemory);
+    atomTable = (xpmHashAtom *) XpmMalloc(table->size * sizeof(*atomTable));
+    if (!atomTable)
+	return (XpmNoMemory);
+    for (p = atomTable + table->size; p > atomTable;)
+	*--p = NULL;
+    table->atomTable = atomTable;
+    return (XpmSuccess);
+}
+
+/*
+ *   frees a hashtable and all the stored atoms
+ */
+
+void
+xpmHashTableFree(xpmHashTable *table)
+{
+    xpmHashAtom *p;
+    xpmHashAtom *atomTable = table->atomTable;
+
+    if (!atomTable)
+	return;
+    for (p = atomTable + table->size; p > atomTable;)
+	if (*--p)
+	    XpmFree(*p);
+    XpmFree(atomTable);
+    table->atomTable = NULL;
+}
diff --git a/be_xpm/misc.c b/be_xpm/misc.c
new file mode 100644
index 0000000000..e5bc0f6b6c
--- /dev/null
+++ b/be_xpm/misc.c
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 1989-95 GROUPE BULL
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * GROUPE BULL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Except as contained in this notice, the name of GROUPE BULL shall not be
+ * used in advertising or otherwise to promote the sale, use or other dealings
+ * in this Software without prior written authorization from GROUPE BULL.
+ */
+
+/*****************************************************************************\
+* misc.c:                                                                     *
+*                                                                             *
+*  XPM library                                                                *
+*  Miscellaneous utilities                                                    *
+*                                                                             *
+*  Developed by Arnaud Le Hors                                                *
+\*****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include "XpmI.h"
+
+#ifdef NEED_STRDUP
+/*
+ * in case strdup is not provided by the system here is one
+ * which does the trick
+ */
+char *
+xpmstrdup(char *s1)
+{
+    char *s2;
+    size_t l = strlen(s1) + 1;
+
+    if (s2 = (char *) XpmMalloc(l))
+	strcpy(s2, s1);
+    return s2;
+}
+
+#endif
+
+unsigned int
+xpmatoui(
+    register char	*p,
+    unsigned int	 l,
+    unsigned int	*ui_return)
+{
+    register unsigned int n, i;
+
+    n = 0;
+    for (i = 0; i < l; i++)
+	if (*p >= '0' && *p <= '9')
+	    n = n * 10 + *p++ - '0';
+	else
+	    break;
+
+    if (i != 0 && i == l) {
+	*ui_return = n;
+	return 1;
+    } else
+	return 0;
+}
+
+/*
+ * Function returning a character string related to an error code.
+ */
+char *
+XpmGetErrorString(int errcode)
+{
+    switch (errcode) {
+    case XpmColorError:
+	return ("XpmColorError");
+    case XpmSuccess:
+	return ("XpmSuccess");
+    case XpmOpenFailed:
+	return ("XpmOpenFailed");
+    case XpmFileInvalid:
+	return ("XpmFileInvalid");
+    case XpmNoMemory:
+	return ("XpmNoMemory");
+    case XpmColorFailed:
+	return ("XpmColorFailed");
+    default:
+	return ("Invalid XpmError");
+    }
+}
+
+/*
+ * The following function provides a way to figure out if the linked library is
+ * newer or older than the one with which a program has been first compiled.
+ */
+int
+XpmLibraryVersion(void)
+{
+    return XpmIncludeVersion;
+}
+
+
+/* The following should help people wanting to use their own functions */
+#ifdef XpmFree
+#undef XpmFree
+#endif
+
+void
+XpmFree(void *ptr)
+{
+    free(ptr);
+}
diff --git a/be_xpm/parse.c b/be_xpm/parse.c
new file mode 100644
index 0000000000..613529e627
--- /dev/null
+++ b/be_xpm/parse.c
@@ -0,0 +1,865 @@
+/*
+ * Copyright (C) 1989-95 GROUPE BULL
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * GROUPE BULL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Except as contained in this notice, the name of GROUPE BULL shall not be
+ * used in advertising or otherwise to promote the sale, use or other dealings
+ * in this Software without prior written authorization from GROUPE BULL.
+ */
+
+/*****************************************************************************\
+* parse.c:                                                                    *
+*                                                                             *
+*  XPM library                                                                *
+*  Parse an XPM file or array and store the found informations                *
+*  in the given XpmImage structure.                                           *
+*                                                                             *
+*  Developed by Arnaud Le Hors                                                *
+\*****************************************************************************/
+
+/*
+ * The code related to FOR_MSW has been added by
+ * HeDu (hedu@cul-ipn.uni-kiel.de) 4/94
+ */
+
+/* October 2004, source code review by Thomas Biege <thomas@suse.de> */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include "XpmI.h"
+#include <ctype.h>
+#include <string.h>
+
+/**
+ * like strlcat() but returns true on success and false if the string got
+ * truncated.
+ */
+static inline Bool
+xstrlcat(char *dst, const char *src, size_t dstsize)
+{
+#if defined(HAS_STRLCAT) || defined(HAVE_STRLCAT)
+    return strlcat(dst, src, dstsize) < dstsize;
+#else
+    if ((strlen(dst) + strlen(src)) < dstsize) {
+        strcat(dst, src);
+        return True;
+    } else {
+        return False;
+    }
+#endif
+}
+
+/**
+ * like strlcpy() but returns true on success and false if the string got
+ * truncated.
+ */
+static inline Bool
+xstrlcpy(char *dst, const char *src, size_t dstsize)
+{
+#if defined(HAS_STRLCAT) || defined(HAVE_STRLCAT)
+    return strlcpy(dst, src, dstsize) < dstsize;
+#else
+    if (strlen(src) < dstsize) {
+        strcpy(dst, src);
+        return True;
+    } else {
+        return False;
+    }
+#endif
+}
+
+LFUNC(ParsePixels, int, (xpmData *data, unsigned int width,
+			 unsigned int height, unsigned int ncolors,
+			 unsigned int cpp, XpmColor *colorTable,
+			 xpmHashTable *hashtable, unsigned int **pixels));
+
+const char *xpmColorKeys[] = {
+    "s",				/* key #1: symbol */
+    "m",				/* key #2: mono visual */
+    "g4",				/* key #3: 4 grays visual */
+    "g",				/* key #4: gray visual */
+    "c",				/* key #5: color visual */
+};
+
+int
+xpmParseValues(
+    xpmData		*data,
+    unsigned int	*width,
+    unsigned int	*height,
+    unsigned int	*ncolors,
+    unsigned int	*cpp,
+    unsigned int	*x_hotspot,
+    unsigned int	*y_hotspot,
+    unsigned int	*hotspot,
+    unsigned int	*extensions)
+{
+    unsigned int l;
+    char buf[BUFSIZ + 1];
+
+    if (!data->format) {		/* XPM 2 or 3 */
+
+	/*
+	 * read values: width, height, ncolors, chars_per_pixel
+	 */
+	if (!(xpmNextUI(data, width) && xpmNextUI(data, height)
+	      && xpmNextUI(data, ncolors) && xpmNextUI(data, cpp)))
+	    return (XpmFileInvalid);
+
+	/*
+	 * read optional information (hotspot and/or XPMEXT) if any
+	 */
+	l = xpmNextWord(data, buf, BUFSIZ);
+	if (l) {
+	    *extensions = (l == 6 && !strncmp("XPMEXT", buf, 6));
+	    if (*extensions)
+		*hotspot = (xpmNextUI(data, x_hotspot)
+			    && xpmNextUI(data, y_hotspot));
+	    else {
+		*hotspot = (xpmatoui(buf, l, x_hotspot)
+			    && xpmNextUI(data, y_hotspot));
+		l = xpmNextWord(data, buf, BUFSIZ);
+		*extensions = (l == 6 && !strncmp("XPMEXT", buf, 6));
+	    }
+	}
+    } else {
+
+	/*
+	 * XPM 1 file read values: width, height, ncolors, chars_per_pixel
+	 */
+	int i;
+	char *ptr;
+	Bool got_one, saw_width = False, saw_height = False;
+	Bool saw_ncolors = False, saw_chars_per_pixel = False;
+
+	for (i = 0; i < 4; i++) {
+	    l = xpmNextWord(data, buf, BUFSIZ);
+	    if (l != 7 || strncmp("#define", buf, 7))
+		return (XpmFileInvalid);
+	    l = xpmNextWord(data, buf, BUFSIZ);
+	    if (!l)
+		return (XpmFileInvalid);
+	    buf[l] = '\0';
+	    ptr = buf;
+	    got_one = False;
+	    while (!got_one) {
+		ptr = strchr(ptr, '_');
+		if (!ptr)
+		    return (XpmFileInvalid);
+		switch (l - (ptr - buf)) {
+		case 6:
+		    if (saw_width || strncmp("_width", ptr, 6)
+			|| !xpmNextUI(data, width))
+			return (XpmFileInvalid);
+		    else
+			saw_width = True;
+		    got_one = True;
+		    break;
+		case 7:
+		    if (saw_height || strncmp("_height", ptr, 7)
+			|| !xpmNextUI(data, height))
+			return (XpmFileInvalid);
+		    else
+			saw_height = True;
+		    got_one = True;
+		    break;
+		case 8:
+		    if (saw_ncolors || strncmp("_ncolors", ptr, 8)
+			|| !xpmNextUI(data, ncolors))
+			return (XpmFileInvalid);
+		    else
+			saw_ncolors = True;
+		    got_one = True;
+		    break;
+		case 16:
+		    if (saw_chars_per_pixel
+			|| strncmp("_chars_per_pixel", ptr, 16)
+			|| !xpmNextUI(data, cpp))
+			return (XpmFileInvalid);
+		    else
+			saw_chars_per_pixel = True;
+		    got_one = True;
+		    break;
+		default:
+		    ptr++;
+		}
+	    }
+	    /* skip the end of line */
+	    xpmNextString(data);
+	}
+	if (!saw_width || !saw_height || !saw_ncolors || !saw_chars_per_pixel)
+	  return (XpmFileInvalid);
+
+	*hotspot = 0;
+	*extensions = 0;
+    }
+    return (XpmSuccess);
+}
+
+int
+xpmParseColors(
+    xpmData		 *data,
+    unsigned int	  ncolors,
+    unsigned int	  cpp,
+    XpmColor		**colorTablePtr,
+    xpmHashTable	 *hashtable)
+{
+    unsigned int key = 0, l, a, b, len;
+    unsigned int curkey;		/* current color key */
+    unsigned int lastwaskey;		/* key read */
+    char buf[BUFSIZ+1];
+    char curbuf[BUFSIZ];		/* current buffer */
+    const char **sptr;
+    char *s;
+    XpmColor *color;
+    XpmColor *colorTable;
+    char **defaults;
+    int ErrorStatus;
+
+    if (ncolors >= UINT_MAX / sizeof(XpmColor))
+	return (XpmNoMemory);
+    colorTable = (XpmColor *) XpmCalloc(ncolors, sizeof(XpmColor));
+    if (!colorTable)
+	return (XpmNoMemory);
+
+    if (!data->format) {		/* XPM 2 or 3 */
+	for (a = 0, color = colorTable; a < ncolors; a++, color++) {
+	    xpmNextString(data);	/* skip the line */
+
+	    /*
+	     * read pixel value
+	     */
+	    if (cpp >= UINT_MAX - 1) {
+		ErrorStatus = XpmNoMemory;
+		goto error;
+	    }
+	    color->string = (char *) XpmMalloc(cpp + 1);
+	    if (!color->string) {
+		ErrorStatus = XpmNoMemory;
+		goto error;
+	    }
+	    for (b = 0, s = color->string; b < cpp; b++, s++) {
+		int c = xpmGetC(data);
+		if (c < 0) {
+		    ErrorStatus = XpmFileInvalid;
+		    goto error;
+		}
+		*s = (char) c;
+	    }
+	    *s = '\0';
+
+	    /*
+	     * store the string in the hashtable with its color index number
+	     */
+	    if (USE_HASHTABLE) {
+		ErrorStatus =
+		    xpmHashIntern(hashtable, color->string, HashAtomData(a));
+		if (ErrorStatus != XpmSuccess) {
+		    goto error;
+		}
+	    }
+
+	    /*
+	     * read color keys and values
+	     */
+	    defaults = (char **) color;
+	    curkey = 0;
+	    lastwaskey = 0;
+	    *curbuf = '\0';		/* init curbuf */
+	    while ((l = xpmNextWord(data, buf, BUFSIZ))) {
+		if (!lastwaskey) {
+		    for (key = 0, sptr = xpmColorKeys; key < NKEYS; key++,
+			 sptr++)
+			if ((strlen(*sptr) == l) && (!strncmp(*sptr, buf, l)))
+			    break;
+		}
+		if (!lastwaskey && key < NKEYS) {	/* open new key */
+		    if (curkey) {	/* flush string */
+			len = strlen(curbuf) + 1;
+			s = (char *) XpmMalloc(len);
+			if (!s) {
+			    ErrorStatus = XpmNoMemory;
+			    goto error;
+			}
+			defaults[curkey] = s;
+			memcpy(s, curbuf, len);
+		    }
+		    curkey = key + 1;	/* set new key  */
+		    *curbuf = '\0';	/* reset curbuf */
+		    lastwaskey = 1;
+		} else {
+		    if (!curkey) {	/* key without value */
+			ErrorStatus = XpmFileInvalid;
+			goto error;
+		    }
+		    if (!lastwaskey) {
+                        if (!xstrlcat(curbuf, " ", sizeof(curbuf))) { /* append space */
+                            ErrorStatus = XpmFileInvalid;
+                            goto error;
+                        }
+                    }
+		    buf[l] = '\0';
+		    if (!xstrlcat(curbuf, buf, sizeof(curbuf))) { /* append buf */
+                        ErrorStatus = XpmFileInvalid;
+                        goto error;
+                    }
+		    lastwaskey = 0;
+		}
+	    }
+	    if (!curkey) {		/* key without value */
+		ErrorStatus = XpmFileInvalid;
+		goto error;
+	    }
+	    len = strlen(curbuf) + 1; /* integer overflow just theoretically possible */
+	    s = defaults[curkey] = (char *) XpmMalloc(len);
+	    if (!s) {
+		ErrorStatus = XpmNoMemory;
+		goto error;
+	    }
+	    memcpy(s, curbuf, len);
+	}
+    } else {				/* XPM 1 */
+	/* get to the beginning of the first string */
+	data->Bos = '"';
+	data->Eos = '\0';
+	xpmNextString(data);
+	data->Eos = '"';
+	for (a = 0, color = colorTable; a < ncolors; a++, color++) {
+
+	    /*
+	     * read pixel value
+	     */
+	    if (cpp >= UINT_MAX - 1) {
+		ErrorStatus = XpmNoMemory;
+		goto error;
+	    }
+	    color->string = (char *) XpmMalloc(cpp + 1);
+	    if (!color->string) {
+		ErrorStatus = XpmNoMemory;
+		goto error;
+	    }
+	    for (b = 0, s = color->string; b < cpp; b++, s++) {
+		int c = xpmGetC(data);
+		if (c < 0) {
+		    ErrorStatus = XpmFileInvalid;
+		    goto error;
+		}
+		*s = (char) c;
+	    }
+	    *s = '\0';
+
+	    /*
+	     * store the string in the hashtable with its color index number
+	     */
+	    if (USE_HASHTABLE) {
+		ErrorStatus =
+		    xpmHashIntern(hashtable, color->string, HashAtomData(a));
+		if (ErrorStatus != XpmSuccess) {
+		    goto error;
+		}
+	    }
+
+	    /*
+	     * read color values
+	     */
+	    xpmNextString(data);	/* get to the next string */
+	    *curbuf = '\0';		/* init curbuf */
+	    while ((l = xpmNextWord(data, buf, BUFSIZ))) {
+		if (*curbuf != '\0') {
+		    if (!xstrlcat(curbuf, " ", sizeof(curbuf))) { /* append space */
+                        ErrorStatus = XpmFileInvalid;
+                        goto error;
+                    }
+                }
+		buf[l] = '\0';
+		if (!xstrlcat(curbuf, buf, sizeof(curbuf))) {	/* append buf */
+                    ErrorStatus = XpmFileInvalid;
+                    goto error;
+                }
+	    }
+	    len = strlen(curbuf) + 1;
+	    s = (char *) XpmMalloc(len);
+	    if (!s) {
+		ErrorStatus = XpmNoMemory;
+		goto error;
+	    }
+	    memcpy(s, curbuf, len);
+	    color->c_color = s;
+	    *curbuf = '\0';		/* reset curbuf */
+	    if (a < ncolors - 1)	/* can we trust ncolors -> leave data's bounds */
+		xpmNextString(data);	/* get to the next string */
+	}
+    }
+    *colorTablePtr = colorTable;
+    return (XpmSuccess);
+
+error:
+    xpmFreeColorTable(colorTable, ncolors);
+    return ErrorStatus;
+}
+
+static int
+ParsePixels(
+    xpmData		 *data,
+    unsigned int	  width,
+    unsigned int	  height,
+    unsigned int	  ncolors,
+    unsigned int	  cpp,
+    XpmColor		 *colorTable,
+    xpmHashTable	 *hashtable,
+    unsigned int	**pixels)
+{
+    unsigned int *iptr, *iptr2 = NULL; /* found by Egbert Eich */
+    unsigned int a, x, y;
+
+    if ((height > 0 && width >= UINT_MAX / height) ||
+	width * height >= UINT_MAX / sizeof(unsigned int))
+	return XpmNoMemory;
+#ifndef FOR_MSW
+    iptr2 = (unsigned int *) XpmMalloc(sizeof(unsigned int) * width * height);
+#else
+
+    /*
+     * special treatment to trick DOS malloc(size_t) where size_t is 16 bit!!
+     * XpmMalloc is defined to longMalloc(long) and checks the 16 bit boundary
+     */
+    iptr2 = (unsigned int *)
+	XpmMalloc((long) sizeof(unsigned int) * (long) width * (long) height);
+#endif
+    if (!iptr2)
+	return (XpmNoMemory);
+
+    iptr = iptr2;
+
+    switch (cpp) {
+
+    case (1):				/* Optimize for single character
+					 * colors */
+	{
+	    unsigned short colidx[256];
+
+	    if (ncolors > 256) {
+		XpmFree(iptr2); /* found by Egbert Eich */
+		return (XpmFileInvalid);
+	    }
+
+	    bzero((char *)colidx, 256 * sizeof(short));
+	    for (a = 0; a < ncolors; a++)
+		colidx[(unsigned char)colorTable[a].string[0]] = a + 1;
+
+	    for (y = 0; y < height; y++) {
+		xpmNextString(data);
+		for (x = 0; x < width; x++, iptr++) {
+		    int c = xpmGetC(data);
+
+		    if (c > 0 && c < 256 && colidx[c] != 0)
+			*iptr = colidx[c] - 1;
+		    else {
+			XpmFree(iptr2);
+			return (XpmFileInvalid);
+		    }
+		}
+	    }
+	}
+	break;
+
+    case (2):				/* Optimize for double character
+					 * colors */
+	{
+
+/* free all allocated pointers at all exits */
+#define FREE_CIDX \
+do \
+{ \
+	int f; for (f = 0; f < 256; f++) \
+	if (cidx[f]) XpmFree(cidx[f]); \
+} while(0)
+
+	    /* array of pointers malloced by need */
+	    unsigned short *cidx[256];
+	    unsigned int char1;
+
+	    bzero((char *)cidx, 256 * sizeof(unsigned short *)); /* init */
+	    for (a = 0; a < ncolors; a++) {
+		char1 = (unsigned char) colorTable[a].string[0];
+		if (cidx[char1] == NULL) { /* get new memory */
+		    cidx[char1] = (unsigned short *)
+			XpmCalloc(256, sizeof(unsigned short));
+		    if (cidx[char1] == NULL) { /* new block failed */
+			FREE_CIDX;
+			XpmFree(iptr2);
+			return (XpmNoMemory);
+		    }
+		}
+		cidx[char1][(unsigned char)colorTable[a].string[1]] = a + 1;
+	    }
+
+	    for (y = 0; y < height; y++) {
+		xpmNextString(data);
+		for (x = 0; x < width; x++, iptr++) {
+		    int cc1 = xpmGetC(data);
+		    if (cc1 > 0 && cc1 < 256) {
+			int cc2 = xpmGetC(data);
+			if (cc2 > 0 && cc2 < 256 &&
+			    cidx[cc1] && cidx[cc1][cc2] != 0)
+			    *iptr = cidx[cc1][cc2] - 1;
+			else {
+			    FREE_CIDX;
+			    XpmFree(iptr2);
+			    return (XpmFileInvalid);
+			}
+		    } else {
+			FREE_CIDX;
+			XpmFree(iptr2);
+			return (XpmFileInvalid);
+		    }
+		}
+	    }
+	    FREE_CIDX;
+	}
+	break;
+
+    default:				/* Non-optimized case of long color
+					 * names */
+	{
+	    char *s;
+	    char buf[BUFSIZ];
+
+	    if (cpp >= sizeof(buf)) {
+		XpmFree(iptr2); /* found by Egbert Eich */
+		return (XpmFileInvalid);
+	    }
+
+	    buf[cpp] = '\0';
+	    if (USE_HASHTABLE) {
+		xpmHashAtom *slot;
+
+		for (y = 0; y < height; y++) {
+		    xpmNextString(data);
+		    for (x = 0; x < width; x++, iptr++) {
+			for (a = 0, s = buf; a < cpp; a++, s++) {
+			    int c = xpmGetC(data);
+			    if (c < 0) {
+				XpmFree(iptr2);
+				return (XpmFileInvalid);
+			    }
+			    *s = (char) c;
+			}
+			slot = xpmHashSlot(hashtable, buf);
+			if (!*slot) {	/* no color matches */
+			    XpmFree(iptr2);
+			    return (XpmFileInvalid);
+			}
+			*iptr = HashColorIndex(slot);
+		    }
+		}
+	    } else {
+		for (y = 0; y < height; y++) {
+		    xpmNextString(data);
+		    for (x = 0; x < width; x++, iptr++) {
+			for (a = 0, s = buf; a < cpp; a++, s++) {
+			    int c = xpmGetC(data);
+			    if (c < 0) {
+				XpmFree(iptr2);
+				return (XpmFileInvalid);
+			    }
+			    *s = (char) c;
+			}
+			for (a = 0; a < ncolors; a++)
+			    if (!strcmp(colorTable[a].string, buf))
+				break;
+			if (a == ncolors) {	/* no color matches */
+			    XpmFree(iptr2);
+			    return (XpmFileInvalid);
+			}
+			*iptr = a;
+		    }
+		}
+	    }
+	}
+	break;
+    }
+    *pixels = iptr2;
+    return (XpmSuccess);
+}
+
+int
+xpmParseExtensions(
+    xpmData		 *data,
+    XpmExtension	**extensions,
+    unsigned int	 *nextensions)
+{
+    XpmExtension *exts = NULL, *ext;
+    unsigned int num = 0;
+    unsigned int nlines, a, l, notstart, notend = 0;
+    int status;
+    char *string, *s, *s2, **sp;
+
+    xpmNextString(data);
+    exts = (XpmExtension *) XpmMalloc(sizeof(XpmExtension));
+    /* get the whole string */
+    status = xpmGetString(data, &string, &l);
+    if (status != XpmSuccess) {
+	XpmFree(exts);
+	return (status);
+    }
+    /* look for the key word XPMEXT, skip lines before this */
+    while ((notstart = strncmp("XPMEXT", string, 6))
+	   && (notend = strncmp("XPMENDEXT", string, 9))) {
+	XpmFree(string);
+	xpmNextString(data);
+	status = xpmGetString(data, &string, &l);
+	if (status != XpmSuccess) {
+	    XpmFree(exts);
+	    return (status);
+	}
+    }
+    if (!notstart)
+	notend = strncmp("XPMENDEXT", string, 9);
+    while (!notstart && notend) {
+	/* there starts an extension */
+	ext = (XpmExtension *)
+	    XpmRealloc(exts, (num + 1) * sizeof(XpmExtension)); /* can the loop be forced to iterate often enough to make "(num + 1) * sizeof(XpmExtension)" wrapping? */
+	if (!ext) {
+	    XpmFree(string);
+	    XpmFreeExtensions(exts, num);
+	    return (XpmNoMemory);
+	}
+	exts = ext;
+	ext += num;
+	/* skip whitespace and store its name */
+	s2 = s = string + 6;
+	while (isspace(*s2))
+	    s2++;
+	a = s2 - s;
+	ext->name = (char *) XpmMalloc(l - a - 6);
+	if (!ext->name) {
+	    XpmFree(string);
+	    ext->lines = NULL;
+	    ext->nlines = 0;
+	    XpmFreeExtensions(exts, num + 1);
+	    return (XpmNoMemory);
+	}
+	strncpy(ext->name, s + a, l - a - 6);
+	XpmFree(string);
+	/* now store the related lines */
+	xpmNextString(data);
+	status = xpmGetString(data, &string, &l);
+	if (status != XpmSuccess) {
+	    ext->lines = NULL;
+	    ext->nlines = 0;
+	    XpmFreeExtensions(exts, num + 1);
+	    return (status);
+	}
+	ext->lines = (char **) XpmMalloc(sizeof(char *));
+	nlines = 0;
+	while ((notstart = strncmp("XPMEXT", string, 6))
+	       && (notend = strncmp("XPMENDEXT", string, 9))) {
+	    sp = (char **)
+		XpmRealloc(ext->lines, (nlines + 1) * sizeof(char *)); /* can we iterate enough for a wrapping? */
+	    if (!sp) {
+		XpmFree(string);
+		ext->nlines = nlines;
+		XpmFreeExtensions(exts, num + 1);
+		return (XpmNoMemory);
+	    }
+	    ext->lines = sp;
+	    ext->lines[nlines] = string;
+	    nlines++;
+	    xpmNextString(data);
+	    status = xpmGetString(data, &string, &l);
+	    if (status != XpmSuccess) {
+		ext->nlines = nlines;
+		XpmFreeExtensions(exts, num + 1);
+		return (status);
+	    }
+	}
+	if (!nlines) {
+	    XpmFree(ext->lines);
+	    ext->lines = NULL;
+	}
+	ext->nlines = nlines;
+	num++;
+    }
+    if (!num) {
+	XpmFree(string);
+	XpmFree(exts);
+	exts = NULL;
+    } else if (!notend)
+	XpmFree(string);
+    *nextensions = num;
+    *extensions = exts;
+    return (XpmSuccess);
+}
+
+
+/* function call in case of error */
+#undef RETURN
+#define RETURN(status) \
+do { \
+      goto error; \
+} while(0)
+
+/*
+ * This function parses an Xpm file or data and store the found informations
+ * in an an XpmImage structure which is returned.
+ */
+int
+xpmParseData(
+    xpmData	*data,
+    XpmImage	*image,
+    XpmInfo	*info)
+{
+    /* variables to return */
+    unsigned int width, height, ncolors, cpp;
+    unsigned int x_hotspot, y_hotspot, hotspot = 0, extensions = 0;
+    XpmColor *colorTable = NULL;
+    unsigned int *pixelindex = NULL;
+    char *hints_cmt = NULL;
+    char *colors_cmt = NULL;
+    char *pixels_cmt = NULL;
+
+    unsigned int cmts;
+    int ErrorStatus;
+    xpmHashTable hashtable;
+
+    cmts = info && (info->valuemask & XpmReturnComments);
+
+    /*
+     * parse the header
+     */
+    ErrorStatus = xpmParseHeader(data);
+    if (ErrorStatus != XpmSuccess)
+	return (ErrorStatus);
+
+    /*
+     * read values
+     */
+    ErrorStatus = xpmParseValues(data, &width, &height, &ncolors, &cpp,
+				 &x_hotspot, &y_hotspot, &hotspot,
+				 &extensions);
+    if (ErrorStatus != XpmSuccess)
+	return (ErrorStatus);
+
+    /*
+     * store the hints comment line
+     */
+    if (cmts)
+	xpmGetCmt(data, &hints_cmt);
+
+    /*
+     * init the hashtable
+     */
+    if (USE_HASHTABLE) {
+	ErrorStatus = xpmHashTableInit(&hashtable);
+	if (ErrorStatus != XpmSuccess)
+	    RETURN(ErrorStatus);
+    }
+
+    /*
+     * read colors
+     */
+    ErrorStatus = xpmParseColors(data, ncolors, cpp, &colorTable, &hashtable);
+    if (ErrorStatus != XpmSuccess) {
+	if (USE_HASHTABLE)
+	    xpmHashTableFree(&hashtable);
+	RETURN(ErrorStatus);
+    }
+
+    /*
+     * store the colors comment line
+     */
+    if (cmts)
+	xpmGetCmt(data, &colors_cmt);
+
+    /*
+     * read pixels and index them on color number
+     */
+    ErrorStatus = ParsePixels(data, width, height, ncolors, cpp, colorTable,
+			      &hashtable, &pixelindex);
+
+    /*
+     * free the hastable
+     */
+    if (USE_HASHTABLE)
+	xpmHashTableFree(&hashtable);
+
+    if (ErrorStatus != XpmSuccess)
+	RETURN(ErrorStatus);
+
+    /*
+     * store the pixels comment line
+     */
+    if (cmts)
+	xpmGetCmt(data, &pixels_cmt);
+
+    /*
+     * parse extensions
+     */
+    if (info && (info->valuemask & XpmReturnExtensions)) {
+	if (extensions) {
+	    ErrorStatus = xpmParseExtensions(data, &info->extensions,
+					     &info->nextensions);
+	    if (ErrorStatus != XpmSuccess)
+		RETURN(ErrorStatus);
+	} else {
+	    info->extensions = NULL;
+	    info->nextensions = 0;
+	}
+    }
+
+    /*
+     * store found informations in the XpmImage structure
+     */
+    image->width = width;
+    image->height = height;
+    image->cpp = cpp;
+    image->ncolors = ncolors;
+    image->colorTable = colorTable;
+    image->data = pixelindex;
+
+    if (info) {
+	if (cmts) {
+	    info->hints_cmt = hints_cmt;
+	    info->colors_cmt = colors_cmt;
+	    info->pixels_cmt = pixels_cmt;
+	}
+	if (hotspot) {
+	    info->x_hotspot = x_hotspot;
+	    info->y_hotspot = y_hotspot;
+	    info->valuemask |= XpmHotspot;
+	}
+    }
+    return (XpmSuccess);
+
+/* exit point in case of error, free only locally allocated variables */
+error:
+    if (colorTable)
+	xpmFreeColorTable(colorTable, ncolors);
+    if (pixelindex)
+	XpmFree(pixelindex);
+    if (hints_cmt)
+	XpmFree(hints_cmt);
+    if (colors_cmt)
+	XpmFree(colors_cmt);
+    if (pixels_cmt)
+	XpmFree(pixels_cmt);
+
+    return(ErrorStatus);
+}
diff --git a/be_xpm/rgb.c b/be_xpm/rgb.c
new file mode 100644
index 0000000000..83309df6ef
--- /dev/null
+++ b/be_xpm/rgb.c
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 1989-95 GROUPE BULL
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * GROUPE BULL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Except as contained in this notice, the name of GROUPE BULL shall not be
+ * used in advertising or otherwise to promote the sale, use or other dealings
+ * in this Software without prior written authorization from GROUPE BULL.
+ */
+
+/*****************************************************************************\
+* rgb.c:                                                                      *
+*                                                                             *
+*  XPM library                                                                *
+*  Rgb file utilities                                                         *
+*                                                                             *
+*  Developed by Arnaud Le Hors                                                *
+\*****************************************************************************/
+
+/*
+ * The code related to FOR_MSW has been added by
+ * HeDu (hedu@cul-ipn.uni-kiel.de) 4/94
+ */
+
+/*
+ * Part of this code has been taken from the ppmtoxpm.c file written by Mark
+ * W. Snitily but has been modified for my special need
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include "XpmI.h"
+#include <ctype.h>
+
+#ifndef FOR_MSW				/* normal part first, MSW part at
+					 * the end, (huge ifdef!) */
+/*
+ * Read a rgb text file.  It stores the rgb values (0->65535)
+ * and the rgb mnemonics (malloc'ed) into the "rgbn" array.  Returns the
+ * number of entries stored.
+ */
+int
+xpmReadRgbNames(
+    char	*rgb_fname,
+    xpmRgbName	 rgbn[])
+{
+    FILE *rgbf;
+    int n, items, red, green, blue;
+    char line[512], name[512], *rgbname, *s1, *s2;
+    xpmRgbName *rgb;
+
+    /* Open the rgb text file.  Abort if error. */
+    if ((rgbf = fopen(rgb_fname, "r")) == NULL)
+	return 0;
+
+    /* Loop reading each line in the file. */
+    n = 0;
+    rgb = rgbn;
+    /* Quit if rgb text file has too many entries. */
+    while (fgets(line, sizeof(line), rgbf) && n < MAX_RGBNAMES) {
+
+	/* Skip silently if line is bad. */
+	items = sscanf(line, "%d %d %d %[^\n]\n", &red, &green, &blue, name);
+	if (items != 4)
+	    continue;
+
+	/*
+	 * Make sure rgb values are within 0->255 range. Skip silently if
+	 * bad.
+	 */
+	if (red < 0 || red > 0xFF ||
+	    green < 0 || green > 0xFF ||
+	    blue < 0 || blue > 0xFF)
+	    continue;
+
+	/* Allocate memory for ascii name. If error give up here. */
+	if (!(rgbname = (char *) XpmMalloc(strlen(name) + 1)))
+	    break;
+
+	/* Copy string to ascii name and lowercase it. */
+	for (s1 = name, s2 = rgbname; *s1; s1++)
+	    *s2++ = tolower(*s1);
+	*s2 = '\0';
+
+	/* Save the rgb values and ascii name in the array. */
+	rgb->r = red * 257;		/* 65535/255 = 257 */
+	rgb->g = green * 257;
+	rgb->b = blue * 257;
+	rgb->name = rgbname;
+	rgb++;
+	n++;
+    }
+
+    fclose(rgbf);
+
+    /* Return the number of read rgb names. */
+    return n < 0 ? 0 : n;
+}
+
+/*
+ * Return the color name corresponding to the given rgb values
+ */
+char *
+xpmGetRgbName(
+    xpmRgbName	rgbn[],		/* rgb mnemonics from rgb text file */
+    int		rgbn_max,	/* number of rgb mnemonics in table */
+    int		red,		/* rgb values */
+    int		green,
+    int		blue)
+{
+    int i;
+    xpmRgbName *rgb;
+
+    /*
+     * Just perform a dumb linear search over the rgb values of the color
+     * mnemonics.  One could speed things up by sorting the rgb values and
+     * using a binary search, or building a hash table, etc...
+     */
+    for (i = 0, rgb = rgbn; i < rgbn_max; i++, rgb++)
+	if (red == rgb->r && green == rgb->g && blue == rgb->b)
+	    return rgb->name;
+
+    /* if not found return NULL */
+    return NULL;
+}
+
+/*
+ * Free the strings which have been malloc'ed in xpmReadRgbNames
+ */
+void
+xpmFreeRgbNames(
+    xpmRgbName	rgbn[],
+    int		rgbn_max)
+{
+    int i;
+    xpmRgbName *rgb;
+
+    for (i = 0, rgb = rgbn; i < rgbn_max; i++, rgb++)
+	XpmFree(rgb->name);
+}
+
+#else					/* here comes the MSW part, the
+					 * second part of the  huge ifdef */
+
+#include "rgbtab.h"			/* hard coded rgb.txt table */
+
+int
+xpmReadRgbNames(
+    char	*rgb_fname,
+    xpmRgbName	 rgbn[])
+{
+    /*
+     * check for consistency???
+     * table has to be sorted for calls on strcasecmp
+     */
+    return (numTheRGBRecords);
+}
+
+char *
+xpmGetRgbName(
+    xpmRgbName rgbn[],		/* rgb mnemonics from rgb text file
+				 * not used */
+    int		rgbn_max,	/* not used */
+    int		red, 		/* rgb values */
+    int		green,
+    int		blue)
+
+{
+    int i;
+    unsigned long rgbVal;
+
+    i = 0;
+    while (i < numTheRGBRecords) {
+	rgbVal = theRGBRecords[i].rgb;
+	if ((rgbVal >> 16) & 0xff == red &&
+	    (rgbVal >> 8) & 0xff == green &&
+	    rgbVal & 0xff == blue)
+	    return (theRGBRecords[i].name);
+	i++;
+    }
+    return (NULL);
+}
+
+/* used in XParseColor in simx.c */
+int
+xpmGetRGBfromName(
+    char	*inname,
+    int		*r,
+    int		*g,
+    int		*b)
+{
+    int left, right, middle;
+    int cmp;
+    unsigned long rgbVal;
+    char *name;
+    char *grey, *p;
+
+    name = xpmstrdup(inname);
+
+    /*
+     * the table in rgbtab.c has no names with spaces, and no grey, but a
+     * lot of gray
+     */
+    /* so first extract ' ' */
+    while (p = strchr(name, ' ')) {
+	while (*(p)) {			/* till eof of string */
+	    *p = *(p + 1);		/* copy to the left */
+	    p++;
+	}
+    }
+    /* fold to lower case */
+    p = name;
+    while (*p) {
+	*p = tolower(*p);
+	p++;
+    }
+
+    /*
+     * substitute Grey with Gray, else rgbtab.h would have more than 100
+     * 'duplicate' entries
+     */
+    if (grey = strstr(name, "grey"))
+	grey[2] = 'a';
+
+    /* binary search */
+    left = 0;
+    right = numTheRGBRecords - 1;
+    do {
+	middle = (left + right) / 2;
+	cmp = xpmstrcasecmp(name, theRGBRecords[middle].name);
+	if (cmp == 0) {
+	    rgbVal = theRGBRecords[middle].rgb;
+	    *r = (rgbVal >> 16) & 0xff;
+	    *g = (rgbVal >> 8) & 0xff;
+	    *b = rgbVal & 0xff;
+	    free(name);
+	    return (1);
+	} else if (cmp < 0) {
+	    right = middle - 1;
+	} else {			/* > 0 */
+	    left = middle + 1;
+	}
+    } while (left <= right);
+
+    /*
+     * I don't like to run in a ColorInvalid error and to see no pixmap at
+     * all, so simply return a red pixel. Should be wrapped in an #ifdef
+     * HeDu
+     */
+
+    *r = 255;
+    *g = 0;
+    *b = 0;				/* red error pixel */
+
+    free(name);
+    return (1);
+}
+
+void
+xpmFreeRgbNames(
+    xpmRgbName	rgbn[],
+    int		rgbn_max)
+{
+    /* nothing to do */
+}
+
+#endif					/* MSW part */
diff --git a/be_xpm/rgbtab.h b/be_xpm/rgbtab.h
new file mode 100644
index 0000000000..dcfb7b10be
--- /dev/null
+++ b/be_xpm/rgbtab.h
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 1989-95 GROUPE BULL
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * GROUPE BULL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Except as contained in this notice, the name of GROUPE BULL shall not be
+ * used in advertising or otherwise to promote the sale, use or other dealings
+ * in this Software without prior written authorization from GROUPE BULL.
+ */
+
+/*****************************************************************************\
+* rgbtab.h                                                                    *
+*                                                                             *
+* A hard coded rgb.txt. To keep it short I removed all colornames with        *
+* trailing numbers, Blue3 etc, except the GrayXX. Sorry Grey-lovers I prefer  *
+* Gray ;-). But Grey is recognized on lookups, only on save Gray will be      *
+* used, maybe you want to do some substitue there too.                        *
+*                                                                             *
+* To save memory the RGBs are coded in one long value, as done by the RGB     *
+* macro.                                                                      *
+*                                                                             *
+* Developed by HeDu 3/94 (hedu@cul-ipn.uni-kiel.de)                           *
+\*****************************************************************************/
+
+
+typedef struct {
+  char *name;
+  unsigned long rgb;			/* it's unsigned long */
+}      rgbRecord;
+
+#define myRGB(r,g,b) \
+	((unsigned long)r<<16|(unsigned long)g<<8|(unsigned long)b)
+
+
+static rgbRecord theRGBRecords[] =
+{
+    {"AliceBlue", myRGB(240, 248, 255)},
+    {"AntiqueWhite", myRGB(250, 235, 215)},
+    {"Aquamarine", myRGB(50, 191, 193)},
+    {"Azure", myRGB(240, 255, 255)},
+    {"Beige", myRGB(245, 245, 220)},
+    {"Bisque", myRGB(255, 228, 196)},
+    {"Black", myRGB(0, 0, 0)},
+    {"BlanchedAlmond", myRGB(255, 235, 205)},
+    {"Blue", myRGB(0, 0, 255)},
+    {"BlueViolet", myRGB(138, 43, 226)},
+    {"Brown", myRGB(165, 42, 42)},
+    {"burlywood", myRGB(222, 184, 135)},
+    {"CadetBlue", myRGB(95, 146, 158)},
+    {"chartreuse", myRGB(127, 255, 0)},
+    {"chocolate", myRGB(210, 105, 30)},
+    {"Coral", myRGB(255, 114, 86)},
+    {"CornflowerBlue", myRGB(34, 34, 152)},
+    {"cornsilk", myRGB(255, 248, 220)},
+    {"Cyan", myRGB(0, 255, 255)},
+    {"DarkGoldenrod", myRGB(184, 134, 11)},
+    {"DarkGreen", myRGB(0, 86, 45)},
+    {"DarkKhaki", myRGB(189, 183, 107)},
+    {"DarkOliveGreen", myRGB(85, 86, 47)},
+    {"DarkOrange", myRGB(255, 140, 0)},
+    {"DarkOrchid", myRGB(139, 32, 139)},
+    {"DarkSalmon", myRGB(233, 150, 122)},
+    {"DarkSeaGreen", myRGB(143, 188, 143)},
+    {"DarkSlateBlue", myRGB(56, 75, 102)},
+    {"DarkSlateGray", myRGB(47, 79, 79)},
+    {"DarkTurquoise", myRGB(0, 166, 166)},
+    {"DarkViolet", myRGB(148, 0, 211)},
+    {"DeepPink", myRGB(255, 20, 147)},
+    {"DeepSkyBlue", myRGB(0, 191, 255)},
+    {"DimGray", myRGB(84, 84, 84)},
+    {"DodgerBlue", myRGB(30, 144, 255)},
+    {"Firebrick", myRGB(142, 35, 35)},
+    {"FloralWhite", myRGB(255, 250, 240)},
+    {"ForestGreen", myRGB(80, 159, 105)},
+    {"gainsboro", myRGB(220, 220, 220)},
+    {"GhostWhite", myRGB(248, 248, 255)},
+    {"Gold", myRGB(218, 170, 0)},
+    {"Goldenrod", myRGB(239, 223, 132)},
+    {"Gray", myRGB(126, 126, 126)},
+    {"Gray0", myRGB(0, 0, 0)},
+    {"Gray1", myRGB(3, 3, 3)},
+    {"Gray10", myRGB(26, 26, 26)},
+    {"Gray100", myRGB(255, 255, 255)},
+    {"Gray11", myRGB(28, 28, 28)},
+    {"Gray12", myRGB(31, 31, 31)},
+    {"Gray13", myRGB(33, 33, 33)},
+    {"Gray14", myRGB(36, 36, 36)},
+    {"Gray15", myRGB(38, 38, 38)},
+    {"Gray16", myRGB(41, 41, 41)},
+    {"Gray17", myRGB(43, 43, 43)},
+    {"Gray18", myRGB(46, 46, 46)},
+    {"Gray19", myRGB(48, 48, 48)},
+    {"Gray2", myRGB(5, 5, 5)},
+    {"Gray20", myRGB(51, 51, 51)},
+    {"Gray21", myRGB(54, 54, 54)},
+    {"Gray22", myRGB(56, 56, 56)},
+    {"Gray23", myRGB(59, 59, 59)},
+    {"Gray24", myRGB(61, 61, 61)},
+    {"Gray25", myRGB(64, 64, 64)},
+    {"Gray26", myRGB(66, 66, 66)},
+    {"Gray27", myRGB(69, 69, 69)},
+    {"Gray28", myRGB(71, 71, 71)},
+    {"Gray29", myRGB(74, 74, 74)},
+    {"Gray3", myRGB(8, 8, 8)},
+    {"Gray30", myRGB(77, 77, 77)},
+    {"Gray31", myRGB(79, 79, 79)},
+    {"Gray32", myRGB(82, 82, 82)},
+    {"Gray33", myRGB(84, 84, 84)},
+    {"Gray34", myRGB(87, 87, 87)},
+    {"Gray35", myRGB(89, 89, 89)},
+    {"Gray36", myRGB(92, 92, 92)},
+    {"Gray37", myRGB(94, 94, 94)},
+    {"Gray38", myRGB(97, 97, 97)},
+    {"Gray39", myRGB(99, 99, 99)},
+    {"Gray4", myRGB(10, 10, 10)},
+    {"Gray40", myRGB(102, 102, 102)},
+    {"Gray41", myRGB(105, 105, 105)},
+    {"Gray42", myRGB(107, 107, 107)},
+    {"Gray43", myRGB(110, 110, 110)},
+    {"Gray44", myRGB(112, 112, 112)},
+    {"Gray45", myRGB(115, 115, 115)},
+    {"Gray46", myRGB(117, 117, 117)},
+    {"Gray47", myRGB(120, 120, 120)},
+    {"Gray48", myRGB(122, 122, 122)},
+    {"Gray49", myRGB(125, 125, 125)},
+    {"Gray5", myRGB(13, 13, 13)},
+    {"Gray50", myRGB(127, 127, 127)},
+    {"Gray51", myRGB(130, 130, 130)},
+    {"Gray52", myRGB(133, 133, 133)},
+    {"Gray53", myRGB(135, 135, 135)},
+    {"Gray54", myRGB(138, 138, 138)},
+    {"Gray55", myRGB(140, 140, 140)},
+    {"Gray56", myRGB(143, 143, 143)},
+    {"Gray57", myRGB(145, 145, 145)},
+    {"Gray58", myRGB(148, 148, 148)},
+    {"Gray59", myRGB(150, 150, 150)},
+    {"Gray6", myRGB(15, 15, 15)},
+    {"Gray60", myRGB(153, 153, 153)},
+    {"Gray61", myRGB(156, 156, 156)},
+    {"Gray62", myRGB(158, 158, 158)},
+    {"Gray63", myRGB(161, 161, 161)},
+    {"Gray64", myRGB(163, 163, 163)},
+    {"Gray65", myRGB(166, 166, 166)},
+    {"Gray66", myRGB(168, 168, 168)},
+    {"Gray67", myRGB(171, 171, 171)},
+    {"Gray68", myRGB(173, 173, 173)},
+    {"Gray69", myRGB(176, 176, 176)},
+    {"Gray7", myRGB(18, 18, 18)},
+    {"Gray70", myRGB(179, 179, 179)},
+    {"Gray71", myRGB(181, 181, 181)},
+    {"Gray72", myRGB(184, 184, 184)},
+    {"Gray73", myRGB(186, 186, 186)},
+    {"Gray74", myRGB(189, 189, 189)},
+    {"Gray75", myRGB(191, 191, 191)},
+    {"Gray76", myRGB(194, 194, 194)},
+    {"Gray77", myRGB(196, 196, 196)},
+    {"Gray78", myRGB(199, 199, 199)},
+    {"Gray79", myRGB(201, 201, 201)},
+    {"Gray8", myRGB(20, 20, 20)},
+    {"Gray80", myRGB(204, 204, 204)},
+    {"Gray81", myRGB(207, 207, 207)},
+    {"Gray82", myRGB(209, 209, 209)},
+    {"Gray83", myRGB(212, 212, 212)},
+    {"Gray84", myRGB(214, 214, 214)},
+    {"Gray85", myRGB(217, 217, 217)},
+    {"Gray86", myRGB(219, 219, 219)},
+    {"Gray87", myRGB(222, 222, 222)},
+    {"Gray88", myRGB(224, 224, 224)},
+    {"Gray89", myRGB(227, 227, 227)},
+    {"Gray9", myRGB(23, 23, 23)},
+    {"Gray90", myRGB(229, 229, 229)},
+    {"Gray91", myRGB(232, 232, 232)},
+    {"Gray92", myRGB(235, 235, 235)},
+    {"Gray93", myRGB(237, 237, 237)},
+    {"Gray94", myRGB(240, 240, 240)},
+    {"Gray95", myRGB(242, 242, 242)},
+    {"Gray96", myRGB(245, 245, 245)},
+    {"Gray97", myRGB(247, 247, 247)},
+    {"Gray98", myRGB(250, 250, 250)},
+    {"Gray99", myRGB(252, 252, 252)},
+    {"Green", myRGB(0, 255, 0)},
+    {"GreenYellow", myRGB(173, 255, 47)},
+    {"honeydew", myRGB(240, 255, 240)},
+    {"HotPink", myRGB(255, 105, 180)},
+    {"IndianRed", myRGB(107, 57, 57)},
+    {"ivory", myRGB(255, 255, 240)},
+    {"Khaki", myRGB(179, 179, 126)},
+    {"lavender", myRGB(230, 230, 250)},
+    {"LavenderBlush", myRGB(255, 240, 245)},
+    {"LawnGreen", myRGB(124, 252, 0)},
+    {"LemonChiffon", myRGB(255, 250, 205)},
+    {"LightBlue", myRGB(176, 226, 255)},
+    {"LightCoral", myRGB(240, 128, 128)},
+    {"LightCyan", myRGB(224, 255, 255)},
+    {"LightGoldenrod", myRGB(238, 221, 130)},
+    {"LightGoldenrodYellow", myRGB(250, 250, 210)},
+    {"LightGray", myRGB(168, 168, 168)},
+    {"LightPink", myRGB(255, 182, 193)},
+    {"LightSalmon", myRGB(255, 160, 122)},
+    {"LightSeaGreen", myRGB(32, 178, 170)},
+    {"LightSkyBlue", myRGB(135, 206, 250)},
+    {"LightSlateBlue", myRGB(132, 112, 255)},
+    {"LightSlateGray", myRGB(119, 136, 153)},
+    {"LightSteelBlue", myRGB(124, 152, 211)},
+    {"LightYellow", myRGB(255, 255, 224)},
+    {"LimeGreen", myRGB(0, 175, 20)},
+    {"linen", myRGB(250, 240, 230)},
+    {"Magenta", myRGB(255, 0, 255)},
+    {"Maroon", myRGB(143, 0, 82)},
+    {"MediumAquamarine", myRGB(0, 147, 143)},
+    {"MediumBlue", myRGB(50, 50, 204)},
+    {"MediumForestGreen", myRGB(50, 129, 75)},
+    {"MediumGoldenrod", myRGB(209, 193, 102)},
+    {"MediumOrchid", myRGB(189, 82, 189)},
+    {"MediumPurple", myRGB(147, 112, 219)},
+    {"MediumSeaGreen", myRGB(52, 119, 102)},
+    {"MediumSlateBlue", myRGB(106, 106, 141)},
+    {"MediumSpringGreen", myRGB(35, 142, 35)},
+    {"MediumTurquoise", myRGB(0, 210, 210)},
+    {"MediumVioletRed", myRGB(213, 32, 121)},
+    {"MidnightBlue", myRGB(47, 47, 100)},
+    {"MintCream", myRGB(245, 255, 250)},
+    {"MistyRose", myRGB(255, 228, 225)},
+    {"moccasin", myRGB(255, 228, 181)},
+    {"NavajoWhite", myRGB(255, 222, 173)},
+    {"Navy", myRGB(35, 35, 117)},
+    {"NavyBlue", myRGB(35, 35, 117)},
+    {"OldLace", myRGB(253, 245, 230)},
+    {"OliveDrab", myRGB(107, 142, 35)},
+    {"Orange", myRGB(255, 135, 0)},
+    {"OrangeRed", myRGB(255, 69, 0)},
+    {"Orchid", myRGB(239, 132, 239)},
+    {"PaleGoldenrod", myRGB(238, 232, 170)},
+    {"PaleGreen", myRGB(115, 222, 120)},
+    {"PaleTurquoise", myRGB(175, 238, 238)},
+    {"PaleVioletRed", myRGB(219, 112, 147)},
+    {"PapayaWhip", myRGB(255, 239, 213)},
+    {"PeachPuff", myRGB(255, 218, 185)},
+    {"peru", myRGB(205, 133, 63)},
+    {"Pink", myRGB(255, 181, 197)},
+    {"Plum", myRGB(197, 72, 155)},
+    {"PowderBlue", myRGB(176, 224, 230)},
+    {"purple", myRGB(160, 32, 240)},
+    {"Red", myRGB(255, 0, 0)},
+    {"RosyBrown", myRGB(188, 143, 143)},
+    {"RoyalBlue", myRGB(65, 105, 225)},
+    {"SaddleBrown", myRGB(139, 69, 19)},
+    {"Salmon", myRGB(233, 150, 122)},
+    {"SandyBrown", myRGB(244, 164, 96)},
+    {"SeaGreen", myRGB(82, 149, 132)},
+    {"seashell", myRGB(255, 245, 238)},
+    {"Sienna", myRGB(150, 82, 45)},
+    {"SkyBlue", myRGB(114, 159, 255)},
+    {"SlateBlue", myRGB(126, 136, 171)},
+    {"SlateGray", myRGB(112, 128, 144)},
+    {"snow", myRGB(255, 250, 250)},
+    {"SpringGreen", myRGB(65, 172, 65)},
+    {"SteelBlue", myRGB(84, 112, 170)},
+    {"Tan", myRGB(222, 184, 135)},
+    {"Thistle", myRGB(216, 191, 216)},
+    {"tomato", myRGB(255, 99, 71)},
+    {"Transparent", myRGB(0, 0, 1)},
+    {"Turquoise", myRGB(25, 204, 223)},
+    {"Violet", myRGB(156, 62, 206)},
+    {"VioletRed", myRGB(243, 62, 150)},
+    {"Wheat", myRGB(245, 222, 179)},
+    {"White", myRGB(255, 255, 255)},
+    {"WhiteSmoke", myRGB(245, 245, 245)},
+    {"Yellow", myRGB(255, 255, 0)},
+    {"YellowGreen", myRGB(50, 216, 56)},
+    NULL
+};
+
+static int numTheRGBRecords = 234;
diff --git a/be_xpm/scan.c b/be_xpm/scan.c
new file mode 100644
index 0000000000..fcf10f15f9
--- /dev/null
+++ b/be_xpm/scan.c
@@ -0,0 +1,1023 @@
+/*
+ * Copyright (C) 1989-95 GROUPE BULL
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * GROUPE BULL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Except as contained in this notice, the name of GROUPE BULL shall not be
+ * used in advertising or otherwise to promote the sale, use or other dealings
+ * in this Software without prior written authorization from GROUPE BULL.
+ */
+
+/*****************************************************************************\
+* scan.c:                                                                     *
+*                                                                             *
+*  XPM library                                                                *
+*  Scanning utility for XPM file format                                       *
+*                                                                             *
+*  Developed by Arnaud Le Hors                                                *
+\*****************************************************************************/
+
+/*
+ * The code related to FOR_MSW has been added by
+ * HeDu (hedu@cul-ipn.uni-kiel.de) 4/94
+ */
+
+/*
+ * The code related to AMIGA has been added by
+ * Lorens Younes (d93-hyo@nada.kth.se) 4/96
+ */
+
+/* October 2004, source code review by Thomas Biege <thomas@suse.de> */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include "XpmI.h"
+
+#define MAXPRINTABLE 92			/* number of printable ascii chars
+					 * minus \ and " for string compat
+					 * and ? to avoid ANSI trigraphs. */
+
+static const char *printable =
+" .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjklzxcvbnmMNBVCZ\
+ASDFGHJKLPIUYTREWQ!~^/()_`'][{}|";
+
+/*
+ * printable begin with a space, so in most case, due to my algorithm, when
+ * the number of different colors is less than MAXPRINTABLE, it will give a
+ * char follow by "nothing" (a space) in the readable xpm file
+ */
+
+
+typedef struct {
+    Pixel *pixels;
+    unsigned int *pixelindex;
+    unsigned int size;
+    unsigned int ncolors;
+    unsigned int mask_pixel;		/* whether there is or not */
+}      PixelsMap;
+
+LFUNC(storePixel, int, (Pixel pixel, PixelsMap *pmap,
+			unsigned int *index_return));
+
+LFUNC(storeMaskPixel, int, (Pixel pixel, PixelsMap *pmap,
+			    unsigned int *index_return));
+
+typedef int (*storeFuncPtr)(Pixel pixel, PixelsMap *pmap,
+			    unsigned int *index_return);
+
+#ifndef FOR_MSW
+# ifndef AMIGA
+LFUNC(GetImagePixels, int, (XImage *image, unsigned int width,
+			    unsigned int height, PixelsMap *pmap));
+
+LFUNC(GetImagePixels32, int, (XImage *image, unsigned int width,
+			      unsigned int height, PixelsMap *pmap));
+
+LFUNC(GetImagePixels16, int, (XImage *image, unsigned int width,
+			      unsigned int height, PixelsMap *pmap));
+
+LFUNC(GetImagePixels8, int, (XImage *image, unsigned int width,
+			     unsigned int height, PixelsMap *pmap));
+
+LFUNC(GetImagePixels1, int, (XImage *image, unsigned int width,
+			     unsigned int height, PixelsMap *pmap,
+			     storeFuncPtr storeFunc));
+# else /* AMIGA */
+LFUNC(AGetImagePixels, int, (XImage *image, unsigned int width,
+			     unsigned int height, PixelsMap *pmap,
+			     storeFuncPtr storeFunc));
+# endif/* AMIGA */
+#else  /* ndef FOR_MSW */
+LFUNC(MSWGetImagePixels, int, (Display *d, XImage *image, unsigned int width,
+			       unsigned int height, PixelsMap *pmap,
+			       storeFuncPtr storeFunc));
+#endif
+LFUNC(ScanTransparentColor, int, (XpmColor *color, unsigned int cpp,
+				  XpmAttributes *attributes));
+
+LFUNC(ScanOtherColors, int, (Display *display, XpmColor *colors,
+			     unsigned int ncolors,
+			     Pixel *pixels, unsigned int mask,
+			     unsigned int cpp, XpmAttributes *attributes));
+
+/*
+ * This function stores the given pixel in the given arrays which are grown
+ * if not large enough.
+ */
+static int
+storePixel(
+    Pixel		 pixel,
+    PixelsMap		*pmap,
+    unsigned int	*index_return)
+{
+    unsigned int i;
+    Pixel *p;
+    unsigned int ncolors;
+
+    if (*index_return) {		/* this is a transparent pixel! */
+	*index_return = 0;
+	return 0;
+    }
+    ncolors = pmap->ncolors;
+    p = pmap->pixels + pmap->mask_pixel;
+    for (i = pmap->mask_pixel; i < ncolors; i++, p++)
+	if (*p == pixel)
+	    break;
+    if (i == ncolors) {
+	if (ncolors >= pmap->size) {
+	    pmap->size *= 2;
+	    p = (Pixel *) XpmRealloc(pmap->pixels, sizeof(Pixel) * pmap->size);
+	    if (!p)
+		return (1);
+	    pmap->pixels = p;
+
+	}
+	(pmap->pixels)[ncolors] = pixel;
+	pmap->ncolors++;
+    }
+    *index_return = i;
+    return 0;
+}
+
+static int
+storeMaskPixel(
+    Pixel		 pixel,
+    PixelsMap		*pmap,
+    unsigned int	*index_return)
+{
+    if (!pixel) {
+	if (!pmap->ncolors) {
+	    pmap->ncolors = 1;
+	    (pmap->pixels)[0] = 0;
+	    pmap->mask_pixel = 1;
+	}
+	*index_return = 1;
+    } else
+	*index_return = 0;
+    return 0;
+}
+
+/* function call in case of error */
+#undef RETURN
+#define RETURN(status) \
+do { \
+      ErrorStatus = status; \
+      goto error; \
+} while(0)
+
+/*
+ * This function scans the given image and stores the found informations in
+ * the given XpmImage structure.
+ */
+int
+XpmCreateXpmImageFromImage(
+    Display		*display,
+    XImage		*image,
+    XImage		*shapeimage,
+    XpmImage		*xpmimage,
+    XpmAttributes	*attributes)
+{
+    /* variables stored in the XpmAttributes structure */
+    unsigned int cpp;
+
+    /* variables to return */
+    PixelsMap pmap;
+    XpmColor *colorTable = NULL;
+    int ErrorStatus = 0;
+
+    /* calculation variables */
+    unsigned int width = 0;
+    unsigned int height = 0;
+    unsigned int cppm;			/* minimum chars per pixel */
+    unsigned int c;
+
+    /* initialize pmap */
+    pmap.pixels = NULL;
+    pmap.pixelindex = NULL;
+    pmap.size = 256;			/* should be enough most of the time */
+    pmap.ncolors = 0;
+    pmap.mask_pixel = 0;
+
+    /*
+     * get geometry
+     */
+    if (image) {
+	width = image->width;
+	height = image->height;
+    } else if (shapeimage) {
+	width = shapeimage->width;
+	height = shapeimage->height;
+    }
+
+    /*
+     * retrieve information from the XpmAttributes
+     */
+    if (attributes && (attributes->valuemask & XpmCharsPerPixel
+/* 3.2 backward compatibility code */
+		       || attributes->valuemask & XpmInfos))
+/* end 3.2 bc */
+	cpp = attributes->cpp;
+    else
+	cpp = 0;
+
+    if ((height > 0 && width >= UINT_MAX / height) ||
+	width * height >= UINT_MAX / sizeof(unsigned int))
+	RETURN(XpmNoMemory);
+    pmap.pixelindex =
+	(unsigned int *) XpmCalloc(width * height, sizeof(unsigned int));
+    if (!pmap.pixelindex)
+	RETURN(XpmNoMemory);
+
+    if (pmap.size >= UINT_MAX / sizeof(Pixel))
+	RETURN(XpmNoMemory);
+
+    pmap.pixels = (Pixel *) XpmMalloc(sizeof(Pixel) * pmap.size);
+    if (!pmap.pixels)
+	RETURN(XpmNoMemory);
+
+    /*
+     * scan shape mask if any
+     */
+    if (shapeimage) {
+#ifndef FOR_MSW
+# ifndef AMIGA
+	ErrorStatus = GetImagePixels1(shapeimage, width, height, &pmap,
+				      storeMaskPixel);
+# else
+	ErrorStatus = AGetImagePixels(shapeimage, width, height, &pmap,
+				      storeMaskPixel);
+# endif
+#else
+	ErrorStatus = MSWGetImagePixels(display, shapeimage, width, height,
+					&pmap, storeMaskPixel);
+#endif
+	if (ErrorStatus != XpmSuccess)
+	    RETURN(ErrorStatus);
+    }
+
+    /*
+     * scan the image data
+     *
+     * In case depth is 1 or bits_per_pixel is 4, 6, 8, 24 or 32 use optimized
+     * functions, otherwise use slower but sure general one.
+     *
+     */
+
+    if (image) {
+#ifndef FOR_MSW
+# ifndef AMIGA
+	if (((image->bits_per_pixel | image->depth) == 1)  &&
+	    (image->byte_order == image->bitmap_bit_order))
+	    ErrorStatus = GetImagePixels1(image, width, height, &pmap,
+					  storePixel);
+	else if (image->format == ZPixmap) {
+	    if (image->bits_per_pixel == 8)
+		ErrorStatus = GetImagePixels8(image, width, height, &pmap);
+	    else if (image->bits_per_pixel == 16)
+		ErrorStatus = GetImagePixels16(image, width, height, &pmap);
+	    else if (image->bits_per_pixel == 32)
+		ErrorStatus = GetImagePixels32(image, width, height, &pmap);
+	} else
+	    ErrorStatus = GetImagePixels(image, width, height, &pmap);
+# else
+	ErrorStatus = AGetImagePixels(image, width, height, &pmap,
+				      storePixel);
+# endif
+#else
+	ErrorStatus = MSWGetImagePixels(display, image, width, height, &pmap,
+					storePixel);
+#endif
+	if (ErrorStatus != XpmSuccess)
+	    RETURN(ErrorStatus);
+    }
+
+    /*
+     * get rgb values and a string of char, and possibly a name for each
+     * color
+     */
+    if (pmap.ncolors >= UINT_MAX / sizeof(XpmColor))
+	RETURN(XpmNoMemory);
+    colorTable = (XpmColor *) XpmCalloc(pmap.ncolors, sizeof(XpmColor));
+    if (!colorTable)
+	RETURN(XpmNoMemory);
+
+    /* compute the minimal cpp */
+    for (cppm = 1, c = MAXPRINTABLE; pmap.ncolors > c; cppm++)
+	c *= MAXPRINTABLE;
+    if (cpp < cppm)
+	cpp = cppm;
+
+    if (pmap.mask_pixel) {
+	ErrorStatus = ScanTransparentColor(colorTable, cpp, attributes);
+	if (ErrorStatus != XpmSuccess)
+	    RETURN(ErrorStatus);
+    }
+
+    ErrorStatus = ScanOtherColors(display, colorTable, pmap.ncolors,
+				  pmap.pixels, pmap.mask_pixel, cpp,
+				  attributes);
+    if (ErrorStatus != XpmSuccess)
+	RETURN(ErrorStatus);
+
+    /*
+     * store found informations in the XpmImage structure
+     */
+    xpmimage->width = width;
+    xpmimage->height = height;
+    xpmimage->cpp = cpp;
+    xpmimage->ncolors = pmap.ncolors;
+    xpmimage->colorTable = colorTable;
+    xpmimage->data = pmap.pixelindex;
+
+    XpmFree(pmap.pixels);
+    return (XpmSuccess);
+
+/* exit point in case of error, free only locally allocated variables */
+error:
+    if (pmap.pixelindex)
+	XpmFree(pmap.pixelindex);
+    if (pmap.pixels)
+	XpmFree(pmap.pixels);
+    if (colorTable)
+	xpmFreeColorTable(colorTable, pmap.ncolors);
+
+    return (ErrorStatus);
+}
+
+static int
+ScanTransparentColor(
+    XpmColor		*color,
+    unsigned int	 cpp,
+    XpmAttributes	*attributes)
+{
+    char *s;
+    unsigned int a, b, c;
+
+    /* first get a character string */
+    a = 0;
+    if (cpp >= UINT_MAX - 1)
+	return (XpmNoMemory);
+    if (!(s = color->string = (char *) XpmMalloc(cpp + 1)))
+	return (XpmNoMemory);
+    *s++ = printable[c = a % MAXPRINTABLE];
+    for (b = 1; b < cpp; b++, s++)
+	*s = printable[c = ((a - c) / MAXPRINTABLE) % MAXPRINTABLE];
+    *s = '\0';
+
+    /* then retreive related info from the attributes if any */
+    if (attributes && (attributes->valuemask & XpmColorTable
+/* 3.2 backward compatibility code */
+		       || attributes->valuemask & XpmInfos)
+/* end 3.2 bc */
+	&& attributes->mask_pixel != XpmUndefPixel) {
+
+	unsigned int key;
+	char **defaults = (char **) color;
+	char **mask_defaults;
+
+/* 3.2 backward compatibility code */
+	if (attributes->valuemask & XpmColorTable)
+/* end 3.2 bc */
+	    mask_defaults = (char **) (
+		attributes->colorTable + attributes->mask_pixel);
+/* 3.2 backward compatibility code */
+	else
+	    mask_defaults = (char **)
+		((XpmColor **) attributes->colorTable)[attributes->mask_pixel];
+/* end 3.2 bc */
+	for (key = 1; key <= NKEYS; key++) {
+	    if ((s = mask_defaults[key])) {
+		defaults[key] = (char *) xpmstrdup(s);
+		if (!defaults[key])
+		    return (XpmNoMemory);
+	    }
+	}
+    } else {
+	color->c_color = (char *) xpmstrdup(TRANSPARENT_COLOR);
+	if (!color->c_color)
+	    return (XpmNoMemory);
+    }
+    return (XpmSuccess);
+}
+
+static int
+ScanOtherColors(
+    Display		*display,
+    XpmColor		*colors,
+    unsigned int	 ncolors,
+    Pixel		*pixels,
+    unsigned int	 mask,
+    unsigned int	 cpp,
+    XpmAttributes	*attributes)
+{
+    /* variables stored in the XpmAttributes structure */
+    Colormap colormap;
+    char *rgb_fname;
+
+#ifndef FOR_MSW
+    xpmRgbName rgbn[MAX_RGBNAMES];
+#else
+    xpmRgbName *rgbn = NULL;
+#endif
+    int rgbn_max = 0;
+    unsigned int i, j, c, i2;
+    XpmColor *color;
+    XColor *xcolors = NULL, *xcolor;
+    char *colorname, *s;
+    XpmColor *colorTable = NULL, **oldColorTable = NULL;
+    unsigned int ancolors = 0;
+    Pixel *apixels = NULL;
+    unsigned int mask_pixel = 0;
+    Bool found;
+
+    /* retrieve information from the XpmAttributes */
+    if (attributes && (attributes->valuemask & XpmColormap))
+	colormap = attributes->colormap;
+    else
+	colormap = XDefaultColormap(display, XDefaultScreen(display));
+    if (attributes && (attributes->valuemask & XpmRgbFilename))
+	rgb_fname = attributes->rgb_fname;
+    else
+	rgb_fname = NULL;
+
+    /* start from the right element */
+    if (mask) {
+	colors++;
+	ncolors--;
+	pixels++;
+    }
+
+    /* first get character strings and rgb values */
+    if (ncolors >= UINT_MAX / sizeof(XColor) || cpp >= UINT_MAX - 1)
+	return (XpmNoMemory);
+    xcolors = (XColor *) XpmMalloc(sizeof(XColor) * ncolors);
+    if (!xcolors)
+	return (XpmNoMemory);
+
+    for (i = 0, i2 = mask, color = colors, xcolor = xcolors;
+	 i < ncolors; i++, i2++, color++, xcolor++, pixels++) {
+
+	if (!(s = color->string = (char *) XpmMalloc(cpp + 1))) {
+	    XpmFree(xcolors);
+	    return (XpmNoMemory);
+	}
+	*s++ = printable[c = i2 % MAXPRINTABLE];
+	for (j = 1; j < cpp; j++, s++)
+	    *s = printable[c = ((i2 - c) / MAXPRINTABLE) % MAXPRINTABLE];
+	*s = '\0';
+
+	xcolor->pixel = *pixels;
+    }
+    XQueryColors(display, colormap, xcolors, ncolors);
+
+#ifndef FOR_MSW
+    /* read the rgb file if any was specified */
+    if (rgb_fname)
+	rgbn_max = xpmReadRgbNames(attributes->rgb_fname, rgbn);
+#else
+    /* FOR_MSW: rgb names and values are hardcoded in rgbtab.h */
+    rgbn_max = xpmReadRgbNames(NULL, NULL);
+#endif
+
+    if (attributes && attributes->valuemask & XpmColorTable) {
+	colorTable = attributes->colorTable;
+	ancolors = attributes->ncolors;
+	apixels = attributes->pixels;
+	mask_pixel = attributes->mask_pixel;
+    }
+/* 3.2 backward compatibility code */
+    else if (attributes && attributes->valuemask & XpmInfos) {
+	oldColorTable = (XpmColor **) attributes->colorTable;
+	ancolors = attributes->ncolors;
+	apixels = attributes->pixels;
+	mask_pixel = attributes->mask_pixel;
+    }
+/* end 3.2 bc */
+
+    for (i = 0, color = colors, xcolor = xcolors; i < ncolors;
+						  i++, color++, xcolor++) {
+
+	/* look for related info from the attributes if any */
+	found = False;
+	if (ancolors) {
+	    unsigned int offset = 0;
+
+	    for (j = 0; j < ancolors; j++) {
+		if (j == mask_pixel) {
+		    offset = 1;
+		    continue;
+		}
+		if (apixels[j - offset] == xcolor->pixel)
+		    break;
+	    }
+	    if (j != ancolors) {
+		unsigned int key;
+		char **defaults = (char **) color;
+		char **adefaults;
+
+/* 3.2 backward compatibility code */
+		if (oldColorTable)
+		    adefaults = (char **) oldColorTable[j];
+		else
+/* end 3.2 bc */
+		    adefaults = (char **) (colorTable + j);
+
+		found = True;
+		for (key = 1; key <= NKEYS; key++) {
+		    if ((s = adefaults[key]))
+			defaults[key] = (char *) xpmstrdup(s);
+		}
+	    }
+	}
+	if (!found) {
+	    /* if nothing found look for a color name */
+	    colorname = NULL;
+	    if (rgbn_max)
+		colorname = xpmGetRgbName(rgbn, rgbn_max, xcolor->red,
+					  xcolor->green, xcolor->blue);
+	    if (colorname)
+		color->c_color = (char *) xpmstrdup(colorname);
+	    else {
+		/* at last store the rgb value */
+		char buf[BUFSIZ];
+#ifndef FOR_MSW
+		sprintf(buf, "#%04X%04X%04X",
+			xcolor->red, xcolor->green, xcolor->blue);
+#else
+		sprintf(buf, "#%02x%02x%02x",
+			xcolor->red, xcolor->green, xcolor->blue);
+#endif
+		color->c_color = (char *) xpmstrdup(buf);
+	    }
+	    if (!color->c_color) {
+		XpmFree(xcolors);
+		xpmFreeRgbNames(rgbn, rgbn_max);
+		return (XpmNoMemory);
+	    }
+	}
+    }
+
+    XpmFree(xcolors);
+    xpmFreeRgbNames(rgbn, rgbn_max);
+    return (XpmSuccess);
+}
+
+#ifndef FOR_MSW
+# ifndef AMIGA
+/*
+ * The functions below are written from X11R5 MIT's code (XImUtil.c)
+ *
+ * The idea is to have faster functions than the standard XGetPixel function
+ * to scan the image data. Indeed we can speed up things by suppressing tests
+ * performed for each pixel. We do exactly the same tests but at the image
+ * level.
+ */
+
+static unsigned long const low_bits_table[] = {
+    0x00000000, 0x00000001, 0x00000003, 0x00000007,
+    0x0000000f, 0x0000001f, 0x0000003f, 0x0000007f,
+    0x000000ff, 0x000001ff, 0x000003ff, 0x000007ff,
+    0x00000fff, 0x00001fff, 0x00003fff, 0x00007fff,
+    0x0000ffff, 0x0001ffff, 0x0003ffff, 0x0007ffff,
+    0x000fffff, 0x001fffff, 0x003fffff, 0x007fffff,
+    0x00ffffff, 0x01ffffff, 0x03ffffff, 0x07ffffff,
+    0x0fffffff, 0x1fffffff, 0x3fffffff, 0x7fffffff,
+    0xffffffff
+};
+
+/*
+ * Default method to scan pixels of an image data structure.
+ * The algorithm used is:
+ *
+ *	copy the source bitmap_unit or Zpixel into temp
+ *	normalize temp if needed
+ *	extract the pixel bits into return value
+ *
+ */
+
+static int
+GetImagePixels(
+    XImage		*image,
+    unsigned int	 width,
+    unsigned int	 height,
+    PixelsMap		*pmap)
+{
+    char *src;
+    char *dst;
+    unsigned int *iptr;
+    char *data;
+    unsigned int x, y;
+    int bits, depth, ibu, ibpp, offset, i;
+    unsigned long lbt;
+    Pixel pixel, px;
+
+    data = image->data;
+    iptr = pmap->pixelindex;
+    depth = image->depth;
+    lbt = low_bits_table[depth];
+    ibpp = image->bits_per_pixel;
+    offset = image->xoffset;
+
+    if (image->bitmap_unit < 0)
+	    return (XpmNoMemory);
+
+    if ((image->bits_per_pixel | image->depth) == 1) {
+	ibu = image->bitmap_unit;
+	for (y = 0; y < height; y++)
+	    for (x = 0; x < width; x++, iptr++) {
+		src = &data[XYINDEX(x, y, image)];
+		dst = (char *) &pixel;
+		pixel = 0;
+		for (i = ibu >> 3; --i >= 0;)
+		    *dst++ = *src++;
+		XYNORMALIZE(&pixel, image);
+		bits = (x + offset) % ibu;
+		pixel = ((((char *) &pixel)[bits >> 3]) >> (bits & 7)) & 1;
+		if (ibpp != depth)
+		    pixel &= lbt;
+		if (storePixel(pixel, pmap, iptr))
+		    return (XpmNoMemory);
+	    }
+    } else if (image->format == XYPixmap) {
+	int nbytes, bpl, j;
+	long plane = 0;
+	ibu = image->bitmap_unit;
+	nbytes = ibu >> 3;
+	bpl = image->bytes_per_line;
+	for (y = 0; y < height; y++)
+	    for (x = 0; x < width; x++, iptr++) {
+		pixel = 0;
+		plane = 0;
+		for (i = depth; --i >= 0;) {
+		    src = &data[XYINDEX(x, y, image) + plane];
+		    dst = (char *) &px;
+		    px = 0;
+		    for (j = nbytes; --j >= 0;)
+			*dst++ = *src++;
+		    XYNORMALIZE(&px, image);
+		    bits = (x + offset) % ibu;
+		    pixel = (pixel << 1) |
+			    (((((char *) &px)[bits >> 3]) >> (bits & 7)) & 1);
+		    plane = plane + (bpl * height);
+		}
+		if (ibpp != depth)
+		    pixel &= lbt;
+		if (storePixel(pixel, pmap, iptr))
+		    return (XpmNoMemory);
+	    }
+    } else if (image->format == ZPixmap) {
+	for (y = 0; y < height; y++)
+	    for (x = 0; x < width; x++, iptr++) {
+		src = &data[ZINDEX(x, y, image)];
+		dst = (char *) &px;
+		px = 0;
+		for (i = (ibpp + 7) >> 3; --i >= 0;)
+		    *dst++ = *src++;
+		ZNORMALIZE(&px, image);
+		pixel = 0;
+		for (i = sizeof(unsigned long); --i >= 0;)
+		    pixel = (pixel << 8) | ((unsigned char *) &px)[i];
+		if (ibpp == 4) {
+		    if (x & 1)
+			pixel >>= 4;
+		    else
+			pixel &= 0xf;
+		}
+		if (ibpp != depth)
+		    pixel &= lbt;
+		if (storePixel(pixel, pmap, iptr))
+		    return (XpmNoMemory);
+	    }
+    } else
+	return (XpmColorError); /* actually a bad image */
+    return (XpmSuccess);
+}
+
+/*
+ * scan pixels of a 32-bits Z image data structure
+ */
+
+#if !defined(WORD64) && !defined(LONG64)
+static unsigned long byteorderpixel = MSBFirst << 24;
+#endif
+
+static int
+GetImagePixels32(
+    XImage		 *image,
+    unsigned int	 width,
+    unsigned int	 height,
+    PixelsMap		*pmap)
+{
+    unsigned char *addr;
+    unsigned char *data;
+    unsigned int *iptr;
+    unsigned int x, y;
+    unsigned long lbt;
+    Pixel pixel;
+    int depth;
+
+    data = (unsigned char *) image->data;
+    iptr = pmap->pixelindex;
+    depth = image->depth;
+    lbt = low_bits_table[depth];
+#if !defined(WORD64) && !defined(LONG64)
+    if (*((char *) &byteorderpixel) == image->byte_order) {
+	for (y = 0; y < height; y++)
+	    for (x = 0; x < width; x++, iptr++) {
+		addr = &data[ZINDEX32(x, y, image)];
+		pixel = *((unsigned long *) addr);
+		if (depth != 32)
+		    pixel &= lbt;
+		if (storePixel(pixel, pmap, iptr))
+		    return (XpmNoMemory);
+	    }
+    } else
+#endif
+    if (image->byte_order == MSBFirst)
+	for (y = 0; y < height; y++)
+	    for (x = 0; x < width; x++, iptr++) {
+		addr = &data[ZINDEX32(x, y, image)];
+		pixel = ((unsigned long) addr[0] << 24 |
+			 (unsigned long) addr[1] << 16 |
+			 (unsigned long) addr[2] << 8 |
+			 addr[3]);
+		if (depth != 32)
+		    pixel &= lbt;
+		if (storePixel(pixel, pmap, iptr))
+		    return (XpmNoMemory);
+	    }
+    else
+	for (y = 0; y < height; y++)
+	    for (x = 0; x < width; x++, iptr++) {
+		addr = &data[ZINDEX32(x, y, image)];
+		pixel = (addr[0] |
+			 (unsigned long) addr[1] << 8 |
+			 (unsigned long) addr[2] << 16 |
+			 (unsigned long) addr[3] << 24);
+		if (depth != 32)
+		    pixel &= lbt;
+		if (storePixel(pixel, pmap, iptr))
+		    return (XpmNoMemory);
+	    }
+    return (XpmSuccess);
+}
+
+/*
+ * scan pixels of a 16-bits Z image data structure
+ */
+
+static int
+GetImagePixels16(
+    XImage		*image,
+    unsigned int	 width,
+    unsigned int	 height,
+    PixelsMap		*pmap)
+{
+    unsigned char *addr;
+    unsigned char *data;
+    unsigned int *iptr;
+    unsigned int x, y;
+    unsigned long lbt;
+    Pixel pixel;
+    int depth;
+
+    data = (unsigned char *) image->data;
+    iptr = pmap->pixelindex;
+    depth = image->depth;
+    lbt = low_bits_table[depth];
+    if (image->byte_order == MSBFirst)
+	for (y = 0; y < height; y++)
+	    for (x = 0; x < width; x++, iptr++) {
+		addr = &data[ZINDEX16(x, y, image)];
+		pixel = addr[0] << 8 | addr[1];
+		if (depth != 16)
+		    pixel &= lbt;
+		if (storePixel(pixel, pmap, iptr))
+		    return (XpmNoMemory);
+	    }
+    else
+	for (y = 0; y < height; y++)
+	    for (x = 0; x < width; x++, iptr++) {
+		addr = &data[ZINDEX16(x, y, image)];
+		pixel = addr[0] | addr[1] << 8;
+		if (depth != 16)
+		    pixel &= lbt;
+		if (storePixel(pixel, pmap, iptr))
+		    return (XpmNoMemory);
+	    }
+    return (XpmSuccess);
+}
+
+/*
+ * scan pixels of a 8-bits Z image data structure
+ */
+
+static int
+GetImagePixels8(
+    XImage		*image,
+    unsigned int	 width,
+    unsigned int	 height,
+    PixelsMap		*pmap)
+{
+    unsigned int *iptr;
+    unsigned char *data;
+    unsigned int x, y;
+    unsigned long lbt;
+    Pixel pixel;
+    int depth;
+
+    data = (unsigned char *) image->data;
+    iptr = pmap->pixelindex;
+    depth = image->depth;
+    lbt = low_bits_table[depth];
+    for (y = 0; y < height; y++)
+	for (x = 0; x < width; x++, iptr++) {
+	    pixel = data[ZINDEX8(x, y, image)];
+	    if (depth != 8)
+		pixel &= lbt;
+	    if (storePixel(pixel, pmap, iptr))
+		return (XpmNoMemory);
+	}
+    return (XpmSuccess);
+}
+
+/*
+ * scan pixels of a 1-bit depth Z image data structure
+ */
+
+static int
+GetImagePixels1(
+    XImage		*image,
+    unsigned int	 width,
+    unsigned int	 height,
+    PixelsMap		 *pmap,
+    storeFuncPtr	 storeFunc)
+{
+    unsigned int *iptr;
+    unsigned int x, y;
+    char *data;
+    Pixel pixel;
+    int xoff, yoff, offset, bpl;
+
+    data = image->data;
+    iptr = pmap->pixelindex;
+    offset = image->xoffset;
+    bpl = image->bytes_per_line;
+
+    if (image->bitmap_bit_order == MSBFirst)
+	for (y = 0; y < height; y++)
+	    for (x = 0; x < width; x++, iptr++) {
+		xoff = x + offset;
+		yoff = y * bpl + (xoff >> 3);
+		xoff &= 7;
+		pixel = (data[yoff] & (0x80 >> xoff)) ? 1 : 0;
+		if ((*storeFunc) (pixel, pmap, iptr))
+		    return (XpmNoMemory);
+	    }
+    else
+	for (y = 0; y < height; y++)
+	    for (x = 0; x < width; x++, iptr++) {
+		xoff = x + offset;
+		yoff = y * bpl + (xoff >> 3);
+		xoff &= 7;
+		pixel = (data[yoff] & (1 << xoff)) ? 1 : 0;
+		if ((*storeFunc) (pixel, pmap, iptr))
+		    return (XpmNoMemory);
+	    }
+    return (XpmSuccess);
+}
+
+# else /* AMIGA */
+
+#define CLEAN_UP(status) \
+do {\
+    if (pixels) XpmFree (pixels);\
+    if (tmp_img) FreeXImage (tmp_img);\
+    return (status);\
+} while(0)
+
+static int
+AGetImagePixels (
+    XImage        *image,
+    unsigned int   width,
+    unsigned int   height,
+    PixelsMap     *pmap,
+    int          (*storeFunc) (Pixel, PixelsMap *, unsigned int *))
+{
+    unsigned int   *iptr;
+    unsigned int    x, y;
+    unsigned char  *pixels;
+    XImage         *tmp_img;
+
+    pixels = XpmMalloc ((((width+15)>>4)<<4)*sizeof (*pixels));
+    if (pixels == NULL)
+	return XpmNoMemory;
+
+    tmp_img = AllocXImage ((((width+15)>>4)<<4), 1, image->rp->BitMap->Depth);
+    if (tmp_img == NULL)
+	CLEAN_UP (XpmNoMemory);
+
+    iptr = pmap->pixelindex;
+    for (y = 0; y < height; ++y)
+    {
+	ReadPixelLine8 (image->rp, 0, y, width, pixels, tmp_img->rp);
+	for (x = 0; x < width; ++x, ++iptr)
+	{
+	    if ((*storeFunc) (pixels[x], pmap, iptr))
+		CLEAN_UP (XpmNoMemory);
+	}
+    }
+
+    CLEAN_UP (XpmSuccess);
+}
+
+#undef CLEAN_UP
+
+# endif/* AMIGA */
+#else  /* ndef FOR_MSW */
+static int
+MSWGetImagePixels(
+    Display	 *display,
+    XImage	 *image,
+    unsigned int  width,
+    unsigned int  height,
+    PixelsMap	 *pmap,
+    int		(*storeFunc) (Pixel, PixelsMap*, unsigned int *))
+{
+    unsigned int *iptr;
+    unsigned int x, y;
+    Pixel pixel;
+
+    iptr = pmap->pixelindex;
+
+    for (y = 0; y < height; y++) {
+	for (x = 0; x < width; x++, iptr++) {
+	    pixel = XGetPixel(image, x, y);
+	    if ((*storeFunc) (pixel, pmap, iptr))
+		return (XpmNoMemory);
+	}
+    }
+    return (XpmSuccess);
+}
+
+#endif
+
+#ifndef FOR_MSW
+# ifndef AMIGA
+int
+XpmCreateXpmImageFromPixmap(
+    Display		*display,
+    Pixmap		 pixmap,
+    Pixmap		 shapemask,
+    XpmImage		*xpmimage,
+    XpmAttributes	*attributes)
+{
+    XImage *ximage = NULL;
+    XImage *shapeimage = NULL;
+    unsigned int width = 0;
+    unsigned int height = 0;
+    int ErrorStatus;
+
+    /* get geometry */
+    if (attributes && attributes->valuemask & XpmSize) {
+	width = attributes->width;
+	height = attributes->height;
+    }
+    /* get the ximages */
+    if (pixmap)
+	xpmCreateImageFromPixmap(display, pixmap, &ximage, &width, &height);
+    if (shapemask)
+	xpmCreateImageFromPixmap(display, shapemask, &shapeimage,
+				 &width, &height);
+
+    /* create the related XpmImage */
+    ErrorStatus = XpmCreateXpmImageFromImage(display, ximage, shapeimage,
+					     xpmimage, attributes);
+
+    /* destroy the ximages */
+    if (ximage)
+	XDestroyImage(ximage);
+    if (shapeimage)
+	XDestroyImage(shapeimage);
+
+    return (ErrorStatus);
+}
+
+# endif/* not AMIGA */
+#endif /* ndef FOR_MSW */
diff --git a/be_xpm/simx.h b/be_xpm/simx.h
new file mode 100644
index 0000000000..cf42fd26aa
--- /dev/null
+++ b/be_xpm/simx.h
@@ -0,0 +1,116 @@
+/* Utilities and defines for using XPM without X on Haiku
+
+Copyright (C) 2021 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/>.  */
+
+/* Parts taken from libXpm simx.h, whose copyright notice is
+   reproduced below: */
+
+/*
+ * Copyright (C) 1989-95 GROUPE BULL
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * GROUPE BULL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Except as contained in this notice, the name of GROUPE BULL shall not be
+ * used in advertising or otherwise to promote the sale, use or other dealings
+ * in this Software without prior written authorization from GROUPE BULL.
+ */
+
+#ifndef _SIMX_H
+#define _SIMX_H
+
+#include <stdint.h>
+
+#define _XFUNCPROTOBEGIN
+#define _XFUNCPROTOEND
+
+typedef void *Window;
+typedef void *Drawable;
+typedef void *Pixmap;
+typedef int Display;
+typedef void *Screen;
+typedef void *Visual;
+typedef void *Colormap;
+typedef uint32_t Pixel;
+
+#define PIXEL_ALREADY_TYPEDEFED
+
+typedef struct
+{
+  Pixel pixel;
+  uint8_t red, green, blue;
+} XColor;
+
+typedef struct
+{
+  void *bitmap;
+  unsigned int width;
+  unsigned int height;
+  unsigned int depth;
+} XImage;
+
+extern void *boundCheckingMalloc (long);
+extern void *boundCheckingCalloc (long, long);
+extern void *boundCheckingRealloc (void *, long);
+
+extern int XAllocColor (Display *, Colormap, XColor *);
+extern void XPutPixel (XImage *, int, int, unsigned long);
+extern unsigned long XGetPixel (XImage *, int, int);
+extern Visual *XDefaultVisual (Display *, Screen *);
+extern Screen *XDefaultScreen (Display *);
+extern Colormap *XDefaultColormap (Display *, Screen *);
+extern int XDefaultDepth (Display *, Screen *);
+extern int XParseColor (Display *, Colormap *, char *, XColor *);
+extern void XQueryColors (Display *, Colormap *, XColor *, int);
+extern int XFreeColors (Display *, Colormap, Pixel *, int, unsigned long);
+extern XImage *XCreateImage (Display *, Visual *, int, int,
+			     int, int, int, int, int, int);
+extern void XDestroyImage (XImage *);
+extern void XImageFree (XImage *);
+
+#define ZPixmap 1			/* not really used */
+#define XYBitmap 1			/* not really used */
+
+#define AllPlanes 		((unsigned long)~0L)
+#define GCForeground            0x01
+#define GCBackground            0x02
+
+
+#ifndef True
+#define True 1
+#define False 0
+#endif
+#ifndef Bool
+typedef int Bool;
+#endif
+
+#endif /* _SIMX_H */
diff --git a/be_xpm/xpm_be.h b/be_xpm/xpm_be.h
new file mode 100644
index 0000000000..f108f1f6ea
--- /dev/null
+++ b/be_xpm/xpm_be.h
@@ -0,0 +1,477 @@
+/*
+ * Copyright (C) 1989-95 GROUPE BULL
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * GROUPE BULL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Except as contained in this notice, the name of GROUPE BULL shall not be
+ * used in advertising or otherwise to promote the sale, use or other dealings
+ * in this Software without prior written authorization from GROUPE BULL.
+ */
+
+/*****************************************************************************\
+* xpm.h:                                                                      *
+*                                                                             *
+*  XPM library                                                                *
+*  Include file                                                               *
+*                                                                             *
+*  Developed by Arnaud Le Hors                                                *
+\*****************************************************************************/
+
+/*
+ * The code related to FOR_MSW has been added by
+ * HeDu (hedu@cul-ipn.uni-kiel.de) 4/94
+ */
+
+/*
+ * The code related to AMIGA has been added by
+ * Lorens Younes (d93-hyo@nada.kth.se) 4/96
+ */
+
+#ifndef XPM_h
+#define XPM_h
+
+/*
+ * first some identification numbers:
+ * the version and revision numbers are determined with the following rule:
+ * SO Major number = LIB minor version number.
+ * SO Minor number = LIB sub-minor version number.
+ * e.g: Xpm version 3.2f
+ *      we forget the 3 which is the format number, 2 gives 2, and f gives 6.
+ *      thus we have XpmVersion = 2 and XpmRevision = 6
+ *      which gives  SOXPMLIBREV = 2.6
+ *
+ * Then the XpmIncludeVersion number is built from these numbers.
+ */
+#define XpmFormat 3
+#define XpmVersion 4
+#define XpmRevision 11
+#define XpmIncludeVersion ((XpmFormat * 100 + XpmVersion) * 100 + XpmRevision)
+
+#ifndef XPM_NUMBERS
+
+#ifdef FOR_MSW
+# define SYSV			/* uses memcpy string.h etc. */
+# include <malloc.h>
+# include "simx.h"		/* defines some X stuff using MSW types */
+#define NEED_STRCASECMP		/* at least for MSVC++ */
+#else /* FOR_MSW */
+# ifdef AMIGA
+#  include "amigax.h"
+# else /* not AMIGA */
+#  include <X11/Xlib.h>
+#  include <X11/Xutil.h>
+# endif /* not AMIGA */
+#endif /* FOR_MSW */
+
+/* let's define Pixel if it is not done yet */
+#if ! defined(_XtIntrinsic_h) && ! defined(PIXEL_ALREADY_TYPEDEFED)
+typedef unsigned long Pixel;	/* Index into colormap */
+# define PIXEL_ALREADY_TYPEDEFED
+#endif
+
+/* Return ErrorStatus codes:
+ * null     if full success
+ * positive if partial success
+ * negative if failure
+ */
+
+#define XpmColorError    1
+#define XpmSuccess       0
+#define XpmOpenFailed   -1
+#define XpmFileInvalid  -2
+#define XpmNoMemory     -3
+#define XpmColorFailed  -4
+
+typedef struct {
+    char *name;			/* Symbolic color name */
+    char *value;		/* Color value */
+    Pixel pixel;		/* Color pixel */
+}      XpmColorSymbol;
+
+typedef struct {
+    char *name;			/* name of the extension */
+    unsigned int nlines;	/* number of lines in this extension */
+    char **lines;		/* pointer to the extension array of strings */
+}      XpmExtension;
+
+typedef struct {
+    char *string;		/* characters string */
+    char *symbolic;		/* symbolic name */
+    char *m_color;		/* monochrom default */
+    char *g4_color;		/* 4 level grayscale default */
+    char *g_color;		/* other level grayscale default */
+    char *c_color;		/* color default */
+}      XpmColor;
+
+typedef struct {
+    unsigned int width;		/* image width */
+    unsigned int height;	/* image height */
+    unsigned int cpp;		/* number of characters per pixel */
+    unsigned int ncolors;	/* number of colors */
+    XpmColor *colorTable;	/* list of related colors */
+    unsigned int *data;		/* image data */
+}      XpmImage;
+
+typedef struct {
+    unsigned long valuemask;	/* Specifies which attributes are defined */
+    char *hints_cmt;		/* Comment of the hints section */
+    char *colors_cmt;		/* Comment of the colors section */
+    char *pixels_cmt;		/* Comment of the pixels section */
+    unsigned int x_hotspot;	/* Returns the x hotspot's coordinate */
+    unsigned int y_hotspot;	/* Returns the y hotspot's coordinate */
+    unsigned int nextensions;	/* number of extensions */
+    XpmExtension *extensions;	/* pointer to array of extensions */
+}      XpmInfo;
+
+typedef int (*XpmAllocColorFunc)(
+    Display*			/* display */,
+    Colormap			/* colormap */,
+    char*			/* colorname */,
+    XColor*			/* xcolor */,
+    void*			/* closure */
+);
+
+typedef int (*XpmFreeColorsFunc)(
+    Display*			/* display */,
+    Colormap			/* colormap */,
+    Pixel*			/* pixels */,
+    int				/* npixels */,
+    void*			/* closure */
+);
+
+typedef struct {
+    unsigned long valuemask;		/* Specifies which attributes are
+					   defined */
+
+    Visual *visual;			/* Specifies the visual to use */
+    Colormap colormap;			/* Specifies the colormap to use */
+    unsigned int depth;			/* Specifies the depth */
+    unsigned int width;			/* Returns the width of the created
+					   pixmap */
+    unsigned int height;		/* Returns the height of the created
+					   pixmap */
+    unsigned int x_hotspot;		/* Returns the x hotspot's
+					   coordinate */
+    unsigned int y_hotspot;		/* Returns the y hotspot's
+					   coordinate */
+    unsigned int cpp;			/* Specifies the number of char per
+					   pixel */
+    Pixel *pixels;			/* List of used color pixels */
+    unsigned int npixels;		/* Number of used pixels */
+    XpmColorSymbol *colorsymbols;	/* List of color symbols to override */
+    unsigned int numsymbols;		/* Number of symbols */
+    char *rgb_fname;			/* RGB text file name */
+    unsigned int nextensions;		/* Number of extensions */
+    XpmExtension *extensions;		/* List of extensions */
+
+    unsigned int ncolors;               /* Number of colors */
+    XpmColor *colorTable;               /* List of colors */
+/* 3.2 backward compatibility code */
+    char *hints_cmt;                    /* Comment of the hints section */
+    char *colors_cmt;                   /* Comment of the colors section */
+    char *pixels_cmt;                   /* Comment of the pixels section */
+/* end 3.2 bc */
+    unsigned int mask_pixel;            /* Color table index of transparent
+                                           color */
+
+    /* Color Allocation Directives */
+    Bool exactColors;			/* Only use exact colors for visual */
+    unsigned int closeness;		/* Allowable RGB deviation */
+    unsigned int red_closeness;		/* Allowable red deviation */
+    unsigned int green_closeness;	/* Allowable green deviation */
+    unsigned int blue_closeness;	/* Allowable blue deviation */
+    int color_key;			/* Use colors from this color set */
+
+    Pixel *alloc_pixels;		/* Returns the list of alloc'ed color
+					   pixels */
+    int nalloc_pixels;			/* Returns the number of alloc'ed
+					   color pixels */
+
+    Bool alloc_close_colors;    	/* Specify whether close colors should
+					   be allocated using XAllocColor
+					   or not */
+    int bitmap_format;			/* Specify the format of 1bit depth
+					   images: ZPixmap or XYBitmap */
+
+    /* Color functions */
+    XpmAllocColorFunc alloc_color;	/* Application color allocator */
+    XpmFreeColorsFunc free_colors;	/* Application color de-allocator */
+    void *color_closure;		/* Application private data to pass to
+					   alloc_color and free_colors */
+
+}      XpmAttributes;
+
+/* XpmAttributes value masks bits */
+#define XpmVisual	   (1L<<0)
+#define XpmColormap	   (1L<<1)
+#define XpmDepth	   (1L<<2)
+#define XpmSize		   (1L<<3)	/* width & height */
+#define XpmHotspot	   (1L<<4)	/* x_hotspot & y_hotspot */
+#define XpmCharsPerPixel   (1L<<5)
+#define XpmColorSymbols	   (1L<<6)
+#define XpmRgbFilename	   (1L<<7)
+/* 3.2 backward compatibility code */
+#define XpmInfos	   (1L<<8)
+#define XpmReturnInfos	   XpmInfos
+/* end 3.2 bc */
+#define XpmReturnPixels	   (1L<<9)
+#define XpmExtensions      (1L<<10)
+#define XpmReturnExtensions XpmExtensions
+
+#define XpmExactColors     (1L<<11)
+#define XpmCloseness	   (1L<<12)
+#define XpmRGBCloseness	   (1L<<13)
+#define XpmColorKey	   (1L<<14)
+
+#define XpmColorTable      (1L<<15)
+#define XpmReturnColorTable XpmColorTable
+
+#define XpmReturnAllocPixels (1L<<16)
+#define XpmAllocCloseColors (1L<<17)
+#define XpmBitmapFormat    (1L<<18)
+
+#define XpmAllocColor      (1L<<19)
+#define XpmFreeColors      (1L<<20)
+#define XpmColorClosure    (1L<<21)
+
+
+/* XpmInfo value masks bits */
+#define XpmComments        XpmInfos
+#define XpmReturnComments  XpmComments
+
+/* XpmAttributes mask_pixel value when there is no mask */
+#ifndef FOR_MSW
+#define XpmUndefPixel 0x80000000
+#else
+/* int is only 16 bit for MSW */
+#define XpmUndefPixel 0x8000
+#endif
+
+/*
+ * color keys for visual type, they must fit along with the number key of
+ * each related element in xpmColorKeys[] defined in XpmI.h
+ */
+#define XPM_MONO	2
+#define XPM_GREY4	3
+#define XPM_GRAY4	3
+#define XPM_GREY 	4
+#define XPM_GRAY 	4
+#define XPM_COLOR	5
+
+
+/* macros for forward declarations of functions with prototypes */
+#define FUNC(f, t, p) extern t f p
+#define LFUNC(f, t, p) static t f p
+
+
+/*
+ * functions declarations
+ */
+
+_XFUNCPROTOBEGIN
+
+/* FOR_MSW, all ..Pixmap.. are excluded, only the ..XImage.. are used */
+/* Same for Amiga! */
+
+#if !defined(FOR_MSW) && !defined(AMIGA)
+    FUNC(XpmCreatePixmapFromData, int, (Display *display,
+					Drawable d,
+					char **data,
+					Pixmap *pixmap_return,
+					Pixmap *shapemask_return,
+					XpmAttributes *attributes));
+
+    FUNC(XpmCreateDataFromPixmap, int, (Display *display,
+					char ***data_return,
+					Pixmap pixmap,
+					Pixmap shapemask,
+					XpmAttributes *attributes));
+
+    FUNC(XpmReadFileToPixmap, int, (Display *display,
+				    Drawable d,
+				    const char *filename,
+				    Pixmap *pixmap_return,
+				    Pixmap *shapemask_return,
+				    XpmAttributes *attributes));
+
+    FUNC(XpmWriteFileFromPixmap, int, (Display *display,
+				       const char *filename,
+				       Pixmap pixmap,
+				       Pixmap shapemask,
+				       XpmAttributes *attributes));
+#endif
+
+    FUNC(XpmCreateImageFromData, int, (Display *display,
+				       char **data,
+				       XImage **image_return,
+				       XImage **shapemask_return,
+				       XpmAttributes *attributes));
+
+    FUNC(XpmCreateDataFromImage, int, (Display *display,
+				       char ***data_return,
+				       XImage *image,
+				       XImage *shapeimage,
+				       XpmAttributes *attributes));
+
+    FUNC(XpmReadFileToImage, int, (Display *display,
+				   const char *filename,
+				   XImage **image_return,
+				   XImage **shapeimage_return,
+				   XpmAttributes *attributes));
+
+    FUNC(XpmWriteFileFromImage, int, (Display *display,
+				      const char *filename,
+				      XImage *image,
+				      XImage *shapeimage,
+				      XpmAttributes *attributes));
+
+    FUNC(XpmCreateImageFromBuffer, int, (Display *display,
+					 char *buffer,
+					 XImage **image_return,
+					 XImage **shapemask_return,
+					 XpmAttributes *attributes));
+#if !defined(FOR_MSW) && !defined(AMIGA)
+    FUNC(XpmCreatePixmapFromBuffer, int, (Display *display,
+					  Drawable d,
+					  char *buffer,
+					  Pixmap *pixmap_return,
+					  Pixmap *shapemask_return,
+					  XpmAttributes *attributes));
+
+    FUNC(XpmCreateBufferFromImage, int, (Display *display,
+					 char **buffer_return,
+					 XImage *image,
+					 XImage *shapeimage,
+					 XpmAttributes *attributes));
+
+    FUNC(XpmCreateBufferFromPixmap, int, (Display *display,
+					  char **buffer_return,
+					  Pixmap pixmap,
+					  Pixmap shapemask,
+					  XpmAttributes *attributes));
+#endif
+    FUNC(XpmReadFileToBuffer, int, (const char *filename, char **buffer_return));
+    FUNC(XpmWriteFileFromBuffer, int, (const char *filename, char *buffer));
+
+    FUNC(XpmReadFileToData, int, (const char *filename, char ***data_return));
+    FUNC(XpmWriteFileFromData, int, (const char *filename, char **data));
+
+    FUNC(XpmAttributesSize, int, (void));
+    FUNC(XpmFreeAttributes, void, (XpmAttributes *attributes));
+    FUNC(XpmFreeExtensions, void, (XpmExtension *extensions,
+				   int nextensions));
+
+    FUNC(XpmFreeXpmImage, void, (XpmImage *image));
+    FUNC(XpmFreeXpmInfo, void, (XpmInfo *info));
+    FUNC(XpmGetErrorString, char *, (int errcode));
+    FUNC(XpmLibraryVersion, int, (void));
+
+    /* XpmImage functions */
+    FUNC(XpmReadFileToXpmImage, int, (const char *filename,
+				      XpmImage *image,
+				      XpmInfo *info));
+
+    FUNC(XpmWriteFileFromXpmImage, int, (const char *filename,
+					 XpmImage *image,
+					 XpmInfo *info));
+#if !defined(FOR_MSW) && !defined(AMIGA)
+    FUNC(XpmCreatePixmapFromXpmImage, int, (Display *display,
+					    Drawable d,
+					    XpmImage *image,
+					    Pixmap *pixmap_return,
+					    Pixmap *shapemask_return,
+					    XpmAttributes *attributes));
+#endif
+    FUNC(XpmCreateImageFromXpmImage, int, (Display *display,
+					   XpmImage *image,
+					   XImage **image_return,
+					   XImage **shapeimage_return,
+					   XpmAttributes *attributes));
+
+    FUNC(XpmCreateXpmImageFromImage, int, (Display *display,
+					   XImage *image,
+					   XImage *shapeimage,
+					   XpmImage *xpmimage,
+					   XpmAttributes *attributes));
+#if !defined(FOR_MSW) && !defined(AMIGA)
+    FUNC(XpmCreateXpmImageFromPixmap, int, (Display *display,
+					    Pixmap pixmap,
+					    Pixmap shapemask,
+					    XpmImage *xpmimage,
+					    XpmAttributes *attributes));
+#endif
+    FUNC(XpmCreateDataFromXpmImage, int, (char ***data_return,
+					  XpmImage *image,
+					  XpmInfo *info));
+
+    FUNC(XpmCreateXpmImageFromData, int, (char **data,
+					  XpmImage *image,
+					  XpmInfo *info));
+
+    FUNC(XpmCreateXpmImageFromBuffer, int, (char *buffer,
+					    XpmImage *image,
+					    XpmInfo *info));
+
+    FUNC(XpmCreateBufferFromXpmImage, int, (char **buffer_return,
+					    XpmImage *image,
+					    XpmInfo *info));
+
+    FUNC(XpmGetParseError, int, (char *filename,
+				 int *linenum_return,
+				 int *charnum_return));
+
+    FUNC(XpmFree, void, (void *ptr));
+
+_XFUNCPROTOEND
+
+/* backward compatibility */
+
+/* for version 3.0c */
+#define XpmPixmapColorError  XpmColorError
+#define XpmPixmapSuccess     XpmSuccess
+#define XpmPixmapOpenFailed  XpmOpenFailed
+#define XpmPixmapFileInvalid XpmFileInvalid
+#define XpmPixmapNoMemory    XpmNoMemory
+#define XpmPixmapColorFailed XpmColorFailed
+
+#define XpmReadPixmapFile(dpy, d, file, pix, mask, att) \
+    XpmReadFileToPixmap(dpy, d, file, pix, mask, att)
+#define XpmWritePixmapFile(dpy, file, pix, mask, att) \
+    XpmWriteFileFromPixmap(dpy, file, pix, mask, att)
+
+/* for version 3.0b */
+#define PixmapColorError  XpmColorError
+#define PixmapSuccess     XpmSuccess
+#define PixmapOpenFailed  XpmOpenFailed
+#define PixmapFileInvalid XpmFileInvalid
+#define PixmapNoMemory    XpmNoMemory
+#define PixmapColorFailed XpmColorFailed
+
+#define ColorSymbol XpmColorSymbol
+
+#define XReadPixmapFile(dpy, d, file, pix, mask, att) \
+    XpmReadFileToPixmap(dpy, d, file, pix, mask, att)
+#define XWritePixmapFile(dpy, file, pix, mask, att) \
+    XpmWriteFileFromPixmap(dpy, file, pix, mask, att)
+#define XCreatePixmapFromData(dpy, d, data, pix, mask, att) \
+    XpmCreatePixmapFromData(dpy, d, data, pix, mask, att)
+#define XCreateDataFromPixmap(dpy, data, pix, mask, att) \
+    XpmCreateDataFromPixmap(dpy, data, pix, mask, att)
+
+#endif /* XPM_NUMBERS */
+#endif
diff --git a/configure.ac b/configure.ac
index 418a62fd5e..d7fae69f45 100644
--- a/configure.ac
+++ b/configure.ac
@@ -508,6 +508,12 @@ AC_DEFUN
 OPTION_DEFAULT_OFF([xwidgets],
   [enable use of xwidgets in Emacs buffers (requires gtk3 or macOS Cocoa)])
 
+OPTION_DEFAULT_OFF([be-app],
+  [enable use of Haiku's Application Kit as a window system])
+
+OPTION_DEFAULT_OFF([be-freetype],
+  [enable use of FreeType under Haiku's Application Kit])
+
 ## Makefile.in needs the cache file name.
 AC_SUBST(cache_file)
 
@@ -784,6 +790,10 @@ AC_DEFUN
     LDFLAGS="-N2M $LDFLAGS"
   ;;
 
+  *-haiku )
+    opsys=haiku
+  ;;
+
   ## Intel 386 machines where we don't care about the manufacturer.
   i[3456]86-*-* )
     case "${canonical}" in
@@ -905,7 +915,9 @@ AC_DEFUN
     if test $emacs_cv_prog_cc_g3 != yes; then
       CFLAGS=$emacs_save_CFLAGS
     fi
-    if test $opsys = mingw32; then
+    # Haiku also needs -gdwarf-2 because its GDB is too old
+    # to understand newer formats.
+    if test $opsys = mingw32 || test $opsys = haiku; then
       CFLAGS="$CFLAGS -gdwarf-2"
     fi
   fi
@@ -1572,6 +1584,8 @@ AC_DEFUN
 
   ## Motif needs -lgen.
   unixware) LIBS_SYSTEM="-lsocket -lnsl -lelf -lgen" ;;
+
+  haiku) LIBS_SYSTEM="-lnetwork" ;;
 esac
 
 AC_SUBST(LIBS_SYSTEM)
@@ -2077,6 +2091,26 @@ AC_DEFUN
    fi
 fi
 
+HAVE_BE_APP=no
+if test "${opsys}" = "haiku" && test "${with_be_app}" = "yes"; then
+   dnl Only GCC and Clang work for now
+   AC_PROG_CXX([gcc cl CC cxx cc++ c++ g++ clang clang++])
+   CXXFLAGS="$CXXFLAGS $emacs_g3_CFLAGS"
+   AC_LANG_PUSH([C++])
+   AC_CHECK_HEADER([app/Application.h], [HAVE_BE_APP=yes],
+                   [AC_MSG_ERROR([The Application Kit headers required for building
+with the Application Kit were not found or cannot be compiled. Either fix this, or
+re-configure with the option '--without-be-app'.])])
+   AC_LANG_POP([C++])
+fi
+
+AC_SUBST(HAVE_BE_APP)
+
+HAVE_BE_FREETYPE=no
+if test "${HAVE_BE_APP}" = "yes" && test "${with_be_freetype}" = "yes"; then
+   HAVE_BE_FREETYPE=yes
+fi
+
 HAVE_W32=no
 W32_OBJ=
 W32_LIBS=
@@ -2198,6 +2232,36 @@ AC_DEFUN
   with_xft=no
 fi
 
+HAIKU_OBJ=
+HAIKU_CXX_OBJ=
+HAIKU_LIBS=
+HAIKU_CFLAGS=
+
+if test "${HAVE_BE_APP}" = "yes"; then
+  AC_DEFINE([HAVE_HAIKU], 1,
+      [Define if Emacs will be built with Haiku windowing support])
+fi
+
+if test "${HAVE_BE_APP}" = "yes"; then
+  window_system=haiku
+  with_xft=no
+  HAIKU_OBJ="$HAIKU_OBJ haikufns.o haikuterm.o haikumenu.o haikufont.o haikuselect.o haiku_io.o"
+  HAIKU_CXX_OBJ="haiku_support.o haiku_font_support.o haiku_draw_support.o haiku_select.o"
+  HAIKU_LIBS="-lbe -lgame -ltranslation -ltracker" # -lgame is needed for set_mouse_position.
+  HAIKU_CFLAGS="-I$srcdir/../be_xpm"
+
+  if test "${with_native_image_api}" = yes; then
+     AC_DEFINE(HAVE_NATIVE_IMAGE_API, 1, [Define to use native OS APIs for images.])
+     NATIVE_IMAGE_API="yes (haiku)"
+     HAIKU_OBJ="$HAIKU_OBJ haikuimage.o"
+  fi
+fi
+
+AC_SUBST(HAIKU_LIBS)
+AC_SUBST(HAIKU_OBJ)
+AC_SUBST(HAIKU_CXX_OBJ)
+AC_SUBST(HAIKU_CFLAGS)
+
 ## $window_system is now set to the window system we will
 ## ultimately use.
 
@@ -2237,6 +2301,9 @@ AC_DEFUN
   w32 )
     term_header=w32term.h
   ;;
+  haiku )
+    term_header=haikuterm.h
+  ;;
 esac
 
 if test "$window_system" = none && test "X$with_x" != "Xno"; then
@@ -2568,7 +2635,8 @@ AC_DEFUN
 
 ### Use -lrsvg-2 if available, unless '--with-rsvg=no' is specified.
 HAVE_RSVG=no
-if test "${HAVE_X11}" = "yes" || test "${HAVE_NS}" = "yes" || test "${opsys}" = "mingw32"; then
+if test "${HAVE_X11}" = "yes" || test "${HAVE_NS}" = "yes" \
+   || test "${opsys}" = "mingw32" || test "${HAVE_BE_APP}" = "yes"; then
   if test "${with_rsvg}" != "no"; then
     RSVG_REQUIRED=2.14.0
     RSVG_MODULE="librsvg-2.0 >= $RSVG_REQUIRED"
@@ -2589,7 +2657,8 @@ AC_DEFUN
 fi
 
 HAVE_IMAGEMAGICK=no
-if test "${HAVE_X11}" = "yes" || test "${HAVE_NS}" = "yes" || test "${HAVE_W32}" = "yes"; then
+if test "${HAVE_X11}" = "yes" || test "${HAVE_NS}" = "yes" || test "${HAVE_W32}" = "yes" || \
+   test "${HAVE_BE_APP}" = "yes"; then
   if test "${with_imagemagick}" != "no"; then
     if test -n "$BREW"; then
       # Homebrew doesn't link ImageMagick 6 by default, so make sure
@@ -3239,6 +3308,9 @@ AC_DEFUN
   elif test "${HAVE_W32}" = "yes"; then
     AC_DEFINE(USE_TOOLKIT_SCROLL_BARS)
     USE_TOOLKIT_SCROLL_BARS=yes
+  elif test "${HAVE_BE_APP}" = "yes"; then
+    AC_DEFINE(USE_TOOLKIT_SCROLL_BARS)
+    USE_TOOLKIT_SCROLL_BARS=yes
   fi
 fi
 
@@ -3477,6 +3549,67 @@ AC_DEFUN
   fi
 fi
 
+### Start of font-backend (under Haiku) selectionn.
+if test "${HAVE_BE_APP}" = "yes"; then
+  if test $HAVE_BE_FREETYPE = "yes"; then
+    EMACS_CHECK_MODULES([FREETYPE], [freetype2])
+    test "$HAVE_FREETYPE" = "no" && AC_MSG_ERROR(freetype on Haiku requires libfreetype)
+    EMACS_CHECK_MODULES([FONTCONFIG], [fontconfig >= 2.2.0])
+    test "$HAVE_FONTCONFIG" = "no" && AC_MSG_ERROR(freetype on Haiku requires libfontconfig)
+    HAVE_XFT=no
+  fi
+  HAVE_LIBOTF=no
+  if test "${HAVE_FREETYPE}" = "yes"; then
+    AC_DEFINE(HAVE_FREETYPE, 1,
+	      [Define to 1 if using the freetype and fontconfig libraries.])
+    OLD_CFLAGS=$CFLAGS
+    OLD_LIBS=$LIBS
+    CFLAGS="$CFLAGS $FREETYPE_CFLAGS"
+    LIBS="$FREETYPE_LIBS $LIBS"
+    AC_CHECK_FUNCS(FT_Face_GetCharVariantIndex)
+    CFLAGS=$OLD_CFLAGS
+    LIBS=$OLD_LIBS
+    if test "${with_libotf}" != "no"; then
+      EMACS_CHECK_MODULES([LIBOTF], [libotf])
+      if test "$HAVE_LIBOTF" = "yes"; then
+	AC_DEFINE(HAVE_LIBOTF, 1, [Define to 1 if using libotf.])
+	AC_CHECK_LIB(otf, OTF_get_variation_glyphs,
+		     HAVE_OTF_GET_VARIATION_GLYPHS=yes,
+		     HAVE_OTF_GET_VARIATION_GLYPHS=no)
+	if test "${HAVE_OTF_GET_VARIATION_GLYPHS}" = "yes"; then
+	  AC_DEFINE(HAVE_OTF_GET_VARIATION_GLYPHS, 1,
+		    [Define to 1 if libotf has OTF_get_variation_glyphs.])
+	fi
+	if ! $PKG_CONFIG --atleast-version=0.9.16 libotf; then
+	  AC_DEFINE(HAVE_OTF_KANNADA_BUG, 1,
+[Define to 1 if libotf is affected by https://debbugs.gnu.org/28110.])
+	fi
+      fi
+    fi
+  dnl FIXME should there be an error if HAVE_FREETYPE != yes?
+  dnl Does the new font backend require it, or can it work without it?
+  fi
+
+  HAVE_M17N_FLT=no
+  if test "${HAVE_LIBOTF}" = yes; then
+    if test "${with_m17n_flt}" != "no"; then
+      EMACS_CHECK_MODULES([M17N_FLT], [m17n-flt])
+      if test "$HAVE_M17N_FLT" = "yes"; then
+	AC_DEFINE(HAVE_M17N_FLT, 1, [Define to 1 if using libm17n-flt.])
+      fi
+    fi
+  fi
+fi
+
+if test "${HAVE_BE_APP}" = "yes" && test "${HAVE_FREETYPE}" = "yes"; then
+  if test "${with_harfbuzz}" != "no"; then
+    EMACS_CHECK_MODULES([HARFBUZZ], [harfbuzz >= $harfbuzz_required_ver])
+    if test "$HAVE_HARFBUZZ" = "yes"; then
+      AC_DEFINE(HAVE_HARFBUZZ, 1, [Define to 1 if using HarfBuzz.])
+    fi
+  fi
+fi
+
 ### End of font-backend section.
 
 AC_SUBST(FREETYPE_CFLAGS)
@@ -3592,13 +3725,26 @@ AC_DEFUN
   fi
 fi
 
+if test "${HAVE_BE_APP}" = "yes"; then
+  if test "${with_xpm}" != "no"; then
+     HAVE_XPM=yes
+  fi
+
+  if test "${HAVE_XPM}" = "yes"; then
+    AC_DEFINE(HAVE_XPM, 1, [Define to 1 if you have the Xpm library (-lXpm).])
+    HAIKU_OBJ="$HAIKU_OBJ haiku_simx.o"
+    LIBXPM='$(be_xpmdir)/libXpmBe.a'
+    AUTODEPEND_PARENTS="$AUTODEPEND_PARENTS be_xpm"
+  fi
+fi
+
 AC_SUBST(LIBXPM)
 
 ### Use -ljpeg if available, unless '--with-jpeg=no'.
 HAVE_JPEG=no
 LIBJPEG=
 if test "${HAVE_X11}" = "yes" || test "${HAVE_W32}" = "yes" \
-   || test "${HAVE_NS}" = "yes"; then
+   || test "${HAVE_NS}" = "yes" || test "${HAVE_BE_APP}" = "yes"; then
   if test "${with_jpeg}" != "no"; then
     AC_CACHE_CHECK([for jpeglib 6b or later],
       [emacs_cv_jpeglib],
@@ -3896,7 +4042,7 @@ AC_DEFUN
   if test "$opsys" = mingw32; then
     AC_CHECK_HEADER([png.h], [HAVE_PNG=yes])
   elif test "${HAVE_X11}" = "yes" || test "${HAVE_W32}" = "yes" \
-       || test "${HAVE_NS}" = "yes"; then
+       || test "${HAVE_NS}" = "yes" || test "${HAVE_BE_APP}" = "yes"; then
     EMACS_CHECK_MODULES([PNG], [libpng >= 1.0.0])
     if test $HAVE_PNG = yes; then
       LIBPNG=$PNG_LIBS
@@ -3971,7 +4117,7 @@ AC_DEFUN
     AC_DEFINE(HAVE_TIFF, 1, [Define to 1 if you have the tiff library (-ltiff).])
   fi
 elif test "${HAVE_X11}" = "yes" || test "${HAVE_W32}" = "yes" \
-     || test "${HAVE_NS}" = "yes"; then
+     || test "${HAVE_NS}" = "yes" || test "${HAVE_BE_APP}" = "yes"; then
   if test "${with_tiff}" != "no"; then
     AC_CHECK_HEADER(tiffio.h,
       [tifflibs="-lz -lm"
@@ -4000,7 +4146,8 @@ AC_DEFUN
     AC_DEFINE(HAVE_GIF, 1, [Define to 1 if you have a gif (or ungif) library.])
   fi
 elif test "${HAVE_X11}" = "yes" && test "${with_gif}" != "no" \
-        || test "${HAVE_W32}" = "yes" || test "${HAVE_NS}" = "yes"; then
+        || test "${HAVE_W32}" = "yes" || test "${HAVE_NS}" = "yes" \
+	|| test "${HAVE_BE_APP}" = "yes"; then
   AC_CHECK_HEADER(gif_lib.h,
 # EGifPutExtensionLast only exists from version libungif-4.1.0b1.
 # Earlier versions can crash Emacs, but version 5.0 removes EGifPutExtensionLast.
@@ -4417,6 +4564,13 @@ AC_DEFUN
       [AC_MSG_ERROR([Non-ELF systems are not supported on this platform.])]);;
 esac
 
+if test "$with_unexec" = yes && test "$opsys" = "haiku"; then
+  dnl A serious attempt was actually made to port unexec to Haiku.
+  dnl Something in libstdc++ seems to prevent it from working.
+  AC_MSG_ERROR([Haiku is not supported by the legacy unexec dumper.
+Please use the portable dumper instead.])
+fi
+
 # Dump loading
 AC_CHECK_FUNCS([posix_madvise])
 
@@ -4753,7 +4907,7 @@ AC_DEFUN
 LIBS="$OLDLIBS"])
 if test "${emacs_cv_links_glib}" = "yes"; then
   AC_DEFINE(HAVE_GLIB, 1, [Define to 1 if GLib is linked in.])
-  if test "$HAVE_NS" = no;then
+  if test "$HAVE_NS" = no ; then
     XGSELOBJ=xgselect.o
   fi
 fi
@@ -5008,7 +5162,7 @@ AC_DEFUN
 dnl to read the input and send it to the true Emacs process
 dnl through a pipe.
 case $opsys in
-  darwin | gnu-linux | gnu-kfreebsd )
+  darwin | gnu-linux | gnu-kfreebsd)
     AC_DEFINE(INTERRUPT_INPUT, 1, [Define to read input using SIGIO.])
   ;;
 esac
@@ -5104,6 +5258,14 @@ AC_DEFUN
     AC_DEFINE(PTY_OPEN, [fd = open (pty_name, O_RDWR | O_NONBLOCK)])
     AC_DEFINE(PTY_TTY_NAME_SPRINTF, [{ char *ptsname (int), *ptyname; int grantpt_result; sigset_t blocked; sigemptyset (&blocked); sigaddset (&blocked, SIGCHLD); pthread_sigmask (SIG_BLOCK, &blocked, 0); grantpt_result = grantpt (fd); pthread_sigmask (SIG_UNBLOCK, &blocked, 0); if (grantpt_result == -1) fatal("could not grant slave pty"); if (unlockpt(fd) == -1) fatal("could not unlock slave pty"); if (!(ptyname = ptsname(fd))) fatal ("could not enable slave pty"); snprintf (pty_name, PTY_NAME_SIZE, "%s", ptyname); }])
     ;;
+
+  haiku*)
+    AC_DEFINE(FIRST_PTY_LETTER, ['s'])
+    AC_DEFINE(PTY_NAME_SPRINTF, [])
+    dnl on Haiku pty names aren't distinctive, thus the use of posix_openpt
+    AC_DEFINE(PTY_OPEN, [fd = posix_openpt (O_RDWR | O_NONBLOCK)])
+    AC_DEFINE(PTY_TTY_NAME_SPRINTF, [{ char *ptyname; int grantpt_result; sigset_t blocked; sigemptyset (&blocked); sigaddset (&blocked, SIGCHLD); pthread_sigmask (SIG_BLOCK, &blocked, 0); grantpt_result = grantpt (fd); pthread_sigmask (SIG_UNBLOCK, &blocked, 0); if (grantpt_result == -1) fatal("could not grant slave pty"); if (unlockpt(fd) == -1) fatal("could not unlock slave pty"); if (!(ptyname = ptsname(fd))) fatal ("could not enable slave pty"); snprintf (pty_name, PTY_NAME_SIZE, "%s", ptyname); }])
+    ;;
 esac
 
 
@@ -5325,8 +5487,25 @@ AC_DEFUN
     AC_DEFINE(USG, [])
     AC_DEFINE(USG5_4, [])
     ;;
+
+  haiku)
+    AC_DEFINE(HAIKU, [], [Define if the system is Haiku.])
+    ;;
 esac
 
+AC_SYS_POSIX_TERMIOS
+if test $ac_cv_sys_posix_termios = yes; then
+  AC_CHECK_SIZEOF([speed_t], [], [#include <termios.h>])
+  dnl on Haiku, and possibly other platforms, speed_t is defined to
+  dnl unsigned char, even when speeds greater than 200 baud are
+  dnl defined.
+
+  if test ${ac_cv_sizeof_speed_t} -lt 2; then
+    AC_DEFINE([HAVE_TINY_SPEED_T], [1],
+        [Define to 1 if speed_t has some sort of nonsensically tiny size.])
+  fi
+fi
+
 AC_CACHE_CHECK([for usable FIONREAD], [emacs_cv_usable_FIONREAD],
   [case $opsys in
      aix4-2 | nacl)
@@ -5369,6 +5548,22 @@ AC_DEFUN
       AC_DEFINE([USABLE_SIGIO], [1], [Define to 1 if SIGIO is usable.])
     fi
   fi
+
+  if test $emacs_broken_SIGIO = no && test $emacs_cv_usable_SIGIO = no; then
+    AC_CACHE_CHECK([for usable SIGPOLL], [emacs_cv_usable_SIGPOLL],
+      [AC_COMPILE_IFELSE(
+	 [AC_LANG_PROGRAM([[#include <fcntl.h>
+			    #include <signal.h>
+			  ]],
+			  [[int foo = SIGPOLL | F_SETFL;]])],
+	 [emacs_cv_usable_SIGPOLL=yes],
+	 [emacs_cv_usable_SIGPOLL=no])],
+      [emacs_cv_usable_SIGPOLL=yes],
+      [emacs_cv_usable_SIGPOLL=no])
+    if test $emacs_cv_usable_SIGPOLL = yes; then
+      AC_DEFINE([USABLE_SIGPOLL], [1], [Define to 1 if SIGPOLL is usable but SIGIO is not.])
+    fi
+  fi
 fi
 
 case $opsys in
@@ -5481,6 +5676,14 @@ AC_DEFUN
     FONT_OBJ="$FONT_OBJ ftfont.o"
   fi
 fi
+
+if test "${HAVE_BE_APP}" = "yes" ; then
+  if test "${HAVE_FREETYPE}" = "yes"; then
+    FONT_OBJ="$FONT_OBJ ftfont.o ftbefont.o"
+    HAIKU_CXX_OBJ="$HAIKU_CXX_OBJ haiku_freetype.o"
+  fi
+fi
+
 if test "${HAVE_HARFBUZZ}" = "yes" ; then
   FONT_OBJ="$FONT_OBJ hbfont.o"
 fi
@@ -5868,7 +6071,7 @@ AC_DEFUN
 #### Please respect alphabetical ordering when making additions.
 optsep=
 emacs_config_features=
-for opt in ACL CAIRO DBUS FREETYPE GCONF GIF GLIB GMP GNUTLS GPM GSETTINGS \
+for opt in ACL BE_APP CAIRO DBUS FREETYPE GCONF GIF GLIB GMP GNUTLS GPM GSETTINGS \
  HARFBUZZ IMAGEMAGICK JPEG JSON LCMS2 LIBOTF LIBSELINUX LIBSYSTEMD LIBXML2 \
  M17N_FLT MODULES NATIVE_COMP NOTIFY NS OLDXMENU PDUMPER PNG RSVG SECCOMP \
  SOUND THREADS TIFF \
@@ -6012,7 +6215,7 @@ AC_DEFUN
 AC_CONFIG_FILES([$srcdir/doc/man/emacs.1])
 
 m4_define([subdir_makefiles],
-  [lib/Makefile lib-src/Makefile oldXMenu/Makefile doc/emacs/Makefile doc/misc/Makefile doc/lispintro/Makefile doc/lispref/Makefile src/Makefile lwlib/Makefile lisp/Makefile leim/Makefile nextstep/Makefile nt/Makefile])
+  [lib/Makefile lib-src/Makefile oldXMenu/Makefile doc/emacs/Makefile doc/misc/Makefile doc/lispintro/Makefile doc/lispref/Makefile src/Makefile lwlib/Makefile lisp/Makefile leim/Makefile nextstep/Makefile nt/Makefile be_xpm/Makefile])
 SUBDIR_MAKEFILES="subdir_makefiles"
 AC_CONFIG_FILES(subdir_makefiles)
 
diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi
index 510efaf271..024a35cf3d 100644
--- a/doc/lispref/display.texi
+++ b/doc/lispref/display.texi
@@ -2699,8 +2699,9 @@ Defining Faces
 @item type
 The kind of window system the terminal uses---either @code{graphic}
 (any graphics-capable display), @code{x}, @code{pc} (for the MS-DOS
-console), @code{w32} (for MS Windows 9X/NT/2K/XP), or @code{tty} (a
-non-graphics-capable display).  @xref{Window Systems, window-system}.
+console), @code{w32} (for MS Windows 9X/NT/2K/XP), @code{haiku} (for
+Haiku), or @code{tty} (a non-graphics-capable display).
+@xref{Window Systems, window-system}.
 
 @item class
 What kinds of colors the terminal supports---either @code{color},
@@ -8024,6 +8025,8 @@ Window Systems
 GNUstep and macOS).
 @item pc
 Emacs is displaying the frame using MS-DOS direct screen writes.
+@item haiku
+Emacs is displaying the frame using the Application Kit on Haiku.
 @item nil
 Emacs is displaying the frame on a character-based terminal.
 @end table
@@ -8070,6 +8073,7 @@ Tooltips
 displayed in the echo area.
 @end defun
 
+@cindex system tooltips
 @vindex x-gtk-use-system-tooltips
 When Emacs is built with GTK+ support, it by default displays tooltips
 using GTK+ functions, and the appearance of the tooltips is then
@@ -8078,6 +8082,12 @@ Tooltips
 @code{nil}.  The rest of this subsection describes how to control
 non-GTK+ tooltips, which are presented by Emacs itself.
 
+@vindex haiku-use-system-tooltips
+When Emacs is built with the Haiku Application Kit, it usually displays
+tooltips using Application Kit functions.  When using these tooltips, text
+properties inside tooltip text will be unavailable. To disable these
+tooltips, change the value of @code{haiku-use-system-tooltips} to @code{nil}.
+
 @cindex tooltip frames
 Tooltips are displayed in special frames called tooltip frames, which
 have their own frame parameters (@pxref{Frame Parameters}).  Unlike
diff --git a/doc/lispref/frames.texi b/doc/lispref/frames.texi
index 477c105a95..59c0116ef3 100644
--- a/doc/lispref/frames.texi
+++ b/doc/lispref/frames.texi
@@ -213,7 +213,8 @@ Multiple Terminals
 @item
 The kind of display associated with the terminal.  This is the symbol
 returned by the function @code{terminal-live-p} (i.e., @code{x},
-@code{t}, @code{w32}, @code{ns}, or @code{pc}).  @xref{Frames}.
+@code{t}, @code{w32}, @code{ns}, @code{pc}, or @code{haiku}).
+@xref{Frames}.
 
 @item
 A list of terminal parameters.  @xref{Terminal Parameters}.
@@ -669,7 +670,7 @@ Frame Layout
 @itemize @w{}
 @item (1) non-toolkit and terminal frames
 
-@item (2) Lucid, Motif and MS-Windows frames
+@item (2) Lucid, Motif, MS-Windows, and Haiku frames
 
 @item (3) GTK+ and NS frames
 @end itemize
@@ -1745,6 +1746,9 @@ Size Parameters
 both will be displayed if the mouse pointer is moved to the top of the
 screen.
 
+On Haiku, setting @code{fullscreen} to @code{fullwidth} or
+@code{fullheight} has no effect.
+
 @vindex fullscreen-restore@r{, a frame parameter}
 @item fullscreen-restore
 This parameter specifies the desired fullscreen state of the frame
@@ -2173,7 +2177,8 @@ Management Parameters
 @code{mouse-autoselect-window} (@pxref{Mouse Window Auto-selection}).
 This may have the unwanted side-effect that a user cannot scroll a
 non-selected frame with the mouse.  Some window managers may not honor
-this parameter.
+this parameter.  On Haiku, it also has the side-effect that the window
+will not be able to receive any keyboard input from the user.
 
 @vindex undecorated@r{, a frame parameter}
 @item undecorated
@@ -3125,6 +3130,9 @@ Raising and Lowering
 below all other frames belonging to the same or a higher z-group as
 @var{frame}.  If @var{frame} is a child frame (@pxref{Child Frames}),
 this lowers @var{frame} below all other child frames of its parent.
+
+Lowering frames is not supported on Haiku, due to limitations imposed
+by the system.
 @end deffn
 
 @defun frame-restack frame1 frame2 &optional above
@@ -3145,6 +3153,9 @@ Raising and Lowering
 @var{frame1} remains unaltered.
 
 Some window managers may refuse to restack windows.
+
+Restacking frames is not supported on Haiku, due to limitations
+imposed by the system.
 @end defun
 
 Note that the effect of restacking will only hold as long as neither of
@@ -3254,6 +3265,12 @@ Child Frames
 allowing them to be positioned so they do not obscure the parent frame
 while still being visible themselves.
 
+  On Haiku, child frames are only visible when a parent frame is
+visible, owing to a limitation of the Haiku windowing system.  Owing
+to the same limitation, child frames are only guaranteed to appear
+above their top-level parent; that is to say, the top-most frame in
+the hierarchy, which does not have a parent frame.
+
   Usually, moving a parent frame moves along all its child frames and
 their descendants as well, keeping their relative positions unaltered.
 Note that the hook @code{move-frame-functions} (@pxref{Frame Position})
diff --git a/doc/lispref/os.texi b/doc/lispref/os.texi
index 12ddaf04b6..3adb5c3e06 100644
--- a/doc/lispref/os.texi
+++ b/doc/lispref/os.texi
@@ -947,6 +947,9 @@ System Environment
 @item gnu/kfreebsd
 A GNU (glibc-based) system with a FreeBSD kernel.
 
+@item haiku
+The Haiku operating system, a derivative of the Be Operating System.
+
 @item hpux
 Hewlett-Packard HPUX operating system.
 
diff --git a/etc/MACHINES b/etc/MACHINES
index d8d0b86fb4..d883f1abd6 100644
--- a/etc/MACHINES
+++ b/etc/MACHINES
@@ -103,6 +103,34 @@ the list at the end of this file.
     ./configure CC='gcc -m64'  # GCC
     ./configure CC='cc -m64'   # Oracle Developer Studio
 
+** Haiku
+
+  On 32-bit Haiku it is required that the newer GCC 8 be used, instead
+  of the legacy GCC 2 used by default.  This can be achieved by
+  invoking configure inside a shell launched by the 'setarch' program
+  invoked as 'setarch x86'.
+
+  When building with packages discovered through pkg-config, such as
+  libpng, on a GCC 2/GCC 8 hybrid system, simply evaluating 'setarch
+  x86' is insufficient to ensure that all required libraries are found
+  at their correct locations.  To avoid this problem, set the
+  environment variable 'PKG_CONFIG_PATH' to the GCC 8 pkg-config
+  directory at '/system/develop/lib/x86/pkgconfig/' before configuring
+  Emacs.
+
+  If GCC complains about not being able to resolve symbols such as
+  "BHandler::LockLooper", you are almost certainly experiencing this
+  problem.
+
+  Haiku running on non-x86 systems has not been tested.  It is
+  anticipated that Haiku running on big-endian systems will experience
+  problems when Emacs is built with Haiku windowing support, but there
+  doesn't seem to be any reliable way to get Haiku running on a
+  big-endian system at present.
+
+  The earliest release of Haiku that will successfully compile Emacs
+  is R1/Beta2.  For windowing support, R1/Beta3 or later is required.
+
 \f
 * Obsolete platforms
 
diff --git a/etc/NEWS b/etc/NEWS
index 8385128f69..b3ad0444fc 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -31,6 +31,27 @@ and also requires GCC and Binutils to be available when Lisp code is
 natively compiled.  See the Info node "(elisp) Native Compilation" for
 more details.
 
+** Emacs has been ported to the Haiku operating system.
+The configuration process should automatically detect and build for Haiku.
+There is also an optional window-system port to Haiku, which can be enabled
+by configuring Emacs with the option '--with-be-app', which will require
+the Haiku Application Kit development headers and a C++ compiler to be present
+on your system.  If Emacs is not built with the option '--with-be-app', the
+resulting Emacs will only run in text-mode terminals.
+
+** FreeType and HarfBuzz support has been ported to the Haiku operating system.
+To enable FreeType support, ensure that the FreeType development
+headers are present on your system, and configure Emacs with
+'--with-be-freetype'.  If Emacs is not configured for FreeType, the
+default font driver that will be enabled which will not have HarfBuzz
+support.
+
+---
+** Double buffering is now enabled on the Haiku operating system.
+Unlike X, there is no compile-time option to enable or disable double-buffering.
+If you wish to disable double-buffering, change the frame parameter
+`inhibit-double-buffering' instead.
+
 ** The Cairo graphics library is now used by default if present.
 '--with-cairo' is now the default, if the appropriate development files
 are found by 'configure'.  Note that building with Cairo means using
@@ -1497,6 +1518,10 @@ symbol property to the browsing commands.  With a new command
 'browse-url-with-browser-kind', an URL can explicitly be browsed with
 either an internal or external browser.
 
++++
+*** Added support for the WebPositive web browser.
+WebPosisitve is the default browser on Haiku, and is based on WebKit.
+
 ---
 *** Support for browsing of remote files.
 If a remote file is specified, a local temporary copy of that file is
diff --git a/etc/PROBLEMS b/etc/PROBLEMS
index f90418798d..6d33e88c22 100644
--- a/etc/PROBLEMS
+++ b/etc/PROBLEMS
@@ -989,6 +989,15 @@ modern fonts are used, such as Noto Emoji or Ebrima.
 The solution is to switch to a configuration that uses HarfBuzz as its
 shaping engine, where these problems don't exist.
 
+** On Haiku, some proportionally-spaced fonts display with artifacting.
+
+This is a Haiku bug: https://dev.haiku-os.org/ticket/17229, which can
+be remedied by using a different font that does not exhibit this
+problem, or by compiling Emacs with the FreeType font driver.
+
+So far, Bitstream Charter and Noto Sans have been known to exhibit
+this problem, while Noto Sans Display is known to not do so.
+
 * Internationalization problems
 
 ** M-{ does not work on a Spanish PC keyboard.
diff --git a/lib-src/Makefile.in b/lib-src/Makefile.in
index e6cda73367..d062e78366 100644
--- a/lib-src/Makefile.in
+++ b/lib-src/Makefile.in
@@ -27,7 +27,9 @@ EMACSOPT =
 # ==================== Things 'configure' will edit ====================
 
 CC=@CC@
+CXX=@CXX@
 CFLAGS=@CFLAGS@
+CXXFLAGS=@CXXFLAGS@
 CPPFLAGS = @CPPFLAGS@
 LDFLAGS = @LDFLAGS@
 
@@ -130,6 +132,11 @@ MKDIR_P =
 
 # ========================== Lists of Files ===========================
 
+## Haiku build-time support
+HAVE_BE_APP=@HAVE_BE_APP@
+HAIKU_LIBS=@HAIKU_LIBS@
+HAIKU_CFLAGS=@HAIKU_CFLAGS@
+
 # emacsclientw.exe for MinGW, empty otherwise
 CLIENTW = @CLIENTW@
 
@@ -143,7 +150,11 @@ UTILITIES =
 	    $(if $(with_mailutils), , movemail${EXEEXT}) \
             $(and $(use_gamedir), update-game-score${EXEEXT})
 
+ifeq ($(HAVE_BE_APP),yes)
+DONT_INSTALL= make-docfile${EXEEXT} make-fingerprint${EXEEXT} be-resources
+else
 DONT_INSTALL= make-docfile${EXEEXT} make-fingerprint${EXEEXT}
+endif
 
 # Like UTILITIES, but they're not system-dependent, and should not be
 #  deleted by the distclean target.
@@ -230,6 +241,10 @@ WINDRES =
 ## Some systems define this to request special libraries.
 LIBS_SYSTEM = @LIBS_SYSTEM@
 
+# Flags that could be in WARN_CFLAGS, but are invalid for C++.
+NON_CXX_CFLAGS = -Wmissing-prototypes -Wnested-externs -Wold-style-definition \
+  -Wstrict-prototypes -Wno-override-init
+
 BASE_CFLAGS = $(C_SWITCH_SYSTEM) $(C_SWITCH_MACHINE) \
 	      $(WARN_CFLAGS) $(WERROR_CFLAGS) \
 	      -I. -I../src -I../lib \
@@ -238,6 +253,9 @@ BASE_CFLAGS =
 ALL_CFLAGS = ${BASE_CFLAGS} ${PROFILING_CFLAGS} ${LDFLAGS} ${CPPFLAGS} ${CFLAGS}
 CPP_CFLAGS = ${BASE_CFLAGS} ${PROFILING_CFLAGS} ${CPPFLAGS} ${CFLAGS}
 
+ALL_CXXFLAGS = $(filter-out ${NON_CXX_CFLAGS},${BASE_CFLAGS}) \
+  ${PROFILING_CFLAGS} ${LDFLAGS} ${CPPFLAGS} ${CFLAGS} ${CXXFLAGS} ${HAIKU_CFLAGS}
+
 # Configuration files for .o files to depend on.
 config_h = ../src/config.h $(srcdir)/../src/conf_post.h
 
@@ -407,6 +425,9 @@ emacsclientw${EXEEXT}:
 	   $(LOADLIBES) \
 	   $(LIB_WSOCK32) $(LIB_EACCESS) $(LIBS_ECLIENT) -o $@
 
+be-resources: ${srcdir}/be_resources.cc ${config_h}
+	$(AM_V_CXXLD)$(CXX) ${ALL_CXXFLAGS} ${HAIKU_LIBS} $< -o $@
+
 NTINC = ${srcdir}/../nt/inc
 NTDEPS = $(NTINC)/ms-w32.h $(NTINC)/sys/stat.h $(NTINC)/inttypes.h \
  $(NTINC)/stdint.h $(NTINC)/pwd.h $(NTINC)/sys/time.h $(NTINC)/stdbool.h \
diff --git a/lib-src/be_resources.cc b/lib-src/be_resources.cc
new file mode 100644
index 0000000000..d8a1c6a886
--- /dev/null
+++ b/lib-src/be_resources.cc
@@ -0,0 +1,137 @@
+/* Haiku window system support
+   Copyright (C) 2021 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 <cstdio>
+#include <cstring>
+#include <cstdlib>
+
+#include <SupportDefs.h>
+#include <Path.h>
+#include <AppFileInfo.h>
+#include <TranslationUtils.h>
+#include <Application.h>
+#include <Catalog.h>
+
+using namespace std;
+
+static void
+be_perror (status_t code, char *arg)
+{
+  if (code != B_OK)
+    {
+      switch (code)
+	{
+	case B_BAD_VALUE:
+	  fprintf (stderr, "%s: Bad value\n", arg);
+	  break;
+	case B_ENTRY_NOT_FOUND:
+	  fprintf (stderr, "%s: Not found\n", arg);
+	  break;
+	case B_PERMISSION_DENIED:
+	  fprintf (stderr, "%s: Permission denied\n", arg);
+	  break;
+	case B_NO_MEMORY:
+	  fprintf (stderr, "%s: No memory\n", arg);
+	  break;
+	case B_LINK_LIMIT:
+	  fprintf (stderr, "%s: Link limit reached\n", arg);
+	  break;
+	case B_BUSY:
+	  fprintf (stderr, "%s: Busy\n", arg);
+	  break;
+	case B_NO_MORE_FDS:
+	  fprintf (stderr, "%s: No more file descriptors\n", arg);
+	  break;
+	case B_FILE_ERROR:
+	  fprintf (stderr, "%s: File error\n", arg);
+	  break;
+	default:
+	  fprintf (stderr, "%s: Unknown error\n", arg);
+	}
+    }
+  else
+    {
+      abort ();
+    }
+}
+
+int
+main (int argc, char **argv)
+{
+  BApplication app ("application/x-vnd.GNU-emacs-resource-helper");
+  BFile file;
+  BBitmap *icon;
+  BAppFileInfo info;
+  status_t code;
+  struct version_info vinfo;
+  char *v = strdup (PACKAGE_VERSION);
+
+  if (argc != 3)
+    {
+      printf ("be-resources ICON FILE: make FILE appropriate for Emacs.\n");
+      return EXIT_FAILURE;
+    }
+
+  code = file.SetTo (argv[2], B_READ_WRITE);
+  if (code != B_OK)
+    {
+      be_perror (code, argv[2]);
+      return EXIT_FAILURE;
+    }
+  code = info.SetTo (&file);
+  if (code != B_OK)
+    {
+      be_perror (code, argv[2]);
+      return EXIT_FAILURE;
+    }
+
+  icon = BTranslationUtils::GetBitmapFile (argv[1], NULL);
+
+  if (!icon)
+    {
+      be_perror (B_ERROR, argv[1]);
+      return EXIT_FAILURE;
+    }
+
+  info.SetIcon (icon, B_MINI_ICON);
+  info.SetIcon (icon, B_LARGE_ICON);
+  info.SetSignature ("application/x-vnd.GNU-emacs");
+
+  v = strtok (v, ".");
+  vinfo.major = atoi (v);
+
+  v = strtok (NULL, ".");
+  vinfo.middle = atoi (v);
+
+  v = strtok (NULL, ".");
+  vinfo.minor = v ? atoi (v) : 0;
+
+  vinfo.variety = 0;
+  vinfo.internal = 0;
+
+  strncpy ((char *) &vinfo.short_info, PACKAGE_VERSION,
+	   sizeof vinfo.short_info - 1);
+  strncpy ((char *) &vinfo.long_info, PACKAGE_STRING,
+	   sizeof vinfo.long_info - 1);
+
+  info.SetVersionInfo (&vinfo, B_APP_VERSION_KIND);
+
+  return EXIT_SUCCESS;
+}
diff --git a/lib-src/emacsclient.c b/lib-src/emacsclient.c
index 018e81e422..ca81ac2b08 100644
--- a/lib-src/emacsclient.c
+++ b/lib-src/emacsclient.c
@@ -601,6 +601,8 @@ decode_options (int argc, char **argv)
       alt_display = "ns";
 #elif defined (HAVE_NTGUI)
       alt_display = "w32";
+#elif defined (HAVE_HAIKU)
+      alt_display = "be";
 #endif
 
       display = egetenv ("DISPLAY");
diff --git a/lisp/cus-edit.el b/lisp/cus-edit.el
index 7eae2e416b..e2c5ee35cf 100644
--- a/lisp/cus-edit.el
+++ b/lisp/cus-edit.el
@@ -2176,7 +2176,7 @@ custom-magic-reset
 ;;; The `custom' Widget.
 
 (defface custom-button
-  '((((type x w32 ns) (class color))	; Like default mode line
+  '((((type x w32 ns haiku) (class color))	; Like default mode line
      :box (:line-width 2 :style released-button)
      :background "lightgrey" :foreground "black"))
   "Face for custom buffer buttons if `custom-raised-buttons' is non-nil."
@@ -2184,7 +2184,7 @@ custom-button
   :group 'custom-faces)
 
 (defface custom-button-mouse
-  '((((type x w32 ns) (class color))
+  '((((type x w32 ns haiku) (class color))
      :box (:line-width 2 :style released-button)
      :background "grey90" :foreground "black")
     (t
@@ -2209,7 +2209,7 @@ custom-button-unraised
       (if custom-raised-buttons 'custom-button-mouse 'highlight))
 
 (defface custom-button-pressed
-  '((((type x w32 ns) (class color))
+  '((((type x w32 ns haiku) (class color))
      :box (:line-width 2 :style pressed-button)
      :background "lightgrey" :foreground "black")
     (t :inverse-video t))
diff --git a/lisp/faces.el b/lisp/faces.el
index a5aef757b1..ac7c544c04 100644
--- a/lisp/faces.el
+++ b/lisp/faces.el
@@ -1171,7 +1171,7 @@ face-valid-attribute-values
            (:height
             'integerp)
            (:stipple
-            (and (memq (window-system frame) '(x ns)) ; No stipple on w32
+            (and (memq (window-system frame) '(x ns)) ; No stipple on w32 or haiku
                  (mapcar #'list
                          (apply #'nconc
                                 (mapcar (lambda (dir)
@@ -2819,7 +2819,7 @@ tool-bar
   '((default
      :box (:line-width 1 :style released-button)
      :foreground "black")
-    (((type x w32 ns) (class color))
+    (((type x w32 ns haiku) (class color))
      :background "grey75")
     (((type x) (class mono))
      :background "grey"))
diff --git a/lisp/frame.el b/lisp/frame.el
index 60234fc2ae..febcb19840 100644
--- a/lisp/frame.el
+++ b/lisp/frame.el
@@ -1633,6 +1633,7 @@ frame-current-scroll-bars
 (declare-function x-frame-geometry "xfns.c" (&optional frame))
 (declare-function w32-frame-geometry "w32fns.c" (&optional frame))
 (declare-function ns-frame-geometry "nsfns.m" (&optional frame))
+(declare-function haiku-frame-geometry "haikufns.c" (&optional frame))
 
 (defun frame-geometry (&optional frame)
   "Return geometric attributes of FRAME.
@@ -1682,6 +1683,8 @@ frame-geometry
       (w32-frame-geometry frame))
      ((eq frame-type 'ns)
       (ns-frame-geometry frame))
+     ((eq frame-type 'haiku)
+      (haiku-frame-geometry frame))
      (t
       (list
        '(outer-position 0 . 0)
@@ -1806,6 +1809,7 @@ frame--size-history
 (declare-function x-frame-edges "xfns.c" (&optional frame type))
 (declare-function w32-frame-edges "w32fns.c" (&optional frame type))
 (declare-function ns-frame-edges "nsfns.m" (&optional frame type))
+(declare-function haiku-frame-edges "haikufns.c" (&optional frame type))
 
 (defun frame-edges (&optional frame type)
   "Return coordinates of FRAME's edges.
@@ -1829,12 +1833,15 @@ frame-edges
       (w32-frame-edges frame type))
      ((eq frame-type 'ns)
       (ns-frame-edges frame type))
+     ((eq frame-type 'haiku)
+      (haiku-frame-edges frame type))
      (t
       (list 0 0 (frame-width frame) (frame-height frame))))))
 
 (declare-function w32-mouse-absolute-pixel-position "w32fns.c")
 (declare-function x-mouse-absolute-pixel-position "xfns.c")
 (declare-function ns-mouse-absolute-pixel-position "nsfns.m")
+(declare-function haiku-mouse-absolute-pixel-position "haikufns.c")
 
 (defun mouse-absolute-pixel-position ()
   "Return absolute position of mouse cursor in pixels.
@@ -1849,12 +1856,15 @@ mouse-absolute-pixel-position
       (w32-mouse-absolute-pixel-position))
      ((eq frame-type 'ns)
       (ns-mouse-absolute-pixel-position))
+     ((eq frame-type 'haiku)
+      (haiku-mouse-absolute-pixel-position))
      (t
       (cons 0 0)))))
 
 (declare-function ns-set-mouse-absolute-pixel-position "nsfns.m" (x y))
 (declare-function w32-set-mouse-absolute-pixel-position "w32fns.c" (x y))
 (declare-function x-set-mouse-absolute-pixel-position "xfns.c" (x y))
+(declare-function haiku-set-mouse-absolute-pixel-position "haikufns.c" (x y))
 
 (defun set-mouse-absolute-pixel-position (x y)
   "Move mouse pointer to absolute pixel position (X, Y).
@@ -1867,7 +1877,9 @@ set-mouse-absolute-pixel-position
      ((eq frame-type 'x)
       (x-set-mouse-absolute-pixel-position x y))
      ((eq frame-type 'w32)
-      (w32-set-mouse-absolute-pixel-position x y)))))
+      (w32-set-mouse-absolute-pixel-position x y))
+     ((eq frame-type 'haiku)
+      (haiku-set-mouse-absolute-pixel-position x y)))))
 
 (defun frame-monitor-attributes (&optional frame)
   "Return the attributes of the physical monitor dominating FRAME.
@@ -1960,6 +1972,7 @@ frame-monitor-workarea
 (declare-function x-frame-list-z-order "xfns.c" (&optional display))
 (declare-function w32-frame-list-z-order "w32fns.c" (&optional display))
 (declare-function ns-frame-list-z-order "nsfns.m" (&optional display))
+(declare-function ns-frame-list-z-order "haikufns.c" (&optional display))
 
 (defun frame-list-z-order (&optional display)
   "Return list of Emacs' frames, in Z (stacking) order.
@@ -1979,7 +1992,9 @@ frame-list-z-order
      ((eq frame-type 'w32)
       (w32-frame-list-z-order display))
      ((eq frame-type 'ns)
-      (ns-frame-list-z-order display)))))
+      (ns-frame-list-z-order display))
+     ((eq frame-type 'haiku)
+      (haiku-frame-list-z-order display)))))
 
 (declare-function x-frame-restack "xfns.c" (frame1 frame2 &optional above))
 (declare-function w32-frame-restack "w32fns.c" (frame1 frame2 &optional above))
@@ -2060,8 +2075,8 @@ display-mouse-p
      ((eq frame-type 'w32)
       (with-no-warnings
        (> w32-num-mouse-buttons 0)))
-     ((memq frame-type '(x ns))
-      t)    ;; We assume X and NeXTstep *always* have a pointing device
+     ((memq frame-type '(x ns haiku))
+      t)    ;; We assume X, NeXTstep and Haiku *always* have a pointing device
      (t
       (or (and (featurep 'xt-mouse)
 	       xterm-mouse-mode)
@@ -2086,7 +2101,7 @@ display-graphic-p
 that use a window system such as X, and false for text-only terminals.
 DISPLAY can be a display name, a frame, or nil (meaning the selected
 frame's display)."
-  (not (null (memq (framep-on-display display) '(x w32 ns)))))
+  (not (null (memq (framep-on-display display) '(x w32 ns haiku)))))
 
 (defun display-images-p (&optional display)
   "Return non-nil if DISPLAY can display images.
@@ -2157,7 +2172,7 @@ display-pixel-height
 `display-monitor-attributes-list'."
   (let ((frame-type (framep-on-display display)))
     (cond
-     ((memq frame-type '(x w32 ns))
+     ((memq frame-type '(x w32 ns haiku))
       (x-display-pixel-height display))
      (t
       (frame-height (if (framep display) display (selected-frame)))))))
@@ -2177,7 +2192,7 @@ display-pixel-width
 `display-monitor-attributes-list'."
   (let ((frame-type (framep-on-display display)))
     (cond
-     ((memq frame-type '(x w32 ns))
+     ((memq frame-type '(x w32 ns haiku))
       (x-display-pixel-width display))
      (t
       (frame-width (if (framep display) display (selected-frame)))))))
@@ -2215,7 +2230,7 @@ display-mm-height
 refers to the height in millimeters for all physical monitors
 associated with DISPLAY.  To get information for each physical
 monitor, use `display-monitor-attributes-list'."
-  (and (memq (framep-on-display display) '(x w32 ns))
+  (and (memq (framep-on-display display) '(x w32 ns haiku))
        (or (cddr (assoc (or display (frame-parameter nil 'display))
 			display-mm-dimensions-alist))
 	   (cddr (assoc t display-mm-dimensions-alist))
@@ -2236,7 +2251,7 @@ display-mm-width
 refers to the width in millimeters for all physical monitors
 associated with DISPLAY.  To get information for each physical
 monitor, use `display-monitor-attributes-list'."
-  (and (memq (framep-on-display display) '(x w32 ns))
+  (and (memq (framep-on-display display) '(x w32 ns haiku))
        (or (cadr (assoc (or display (frame-parameter nil 'display))
 			display-mm-dimensions-alist))
 	   (cadr (assoc t display-mm-dimensions-alist))
@@ -2254,7 +2269,7 @@ display-backing-store
 If DISPLAY is omitted or nil, it defaults to the selected frame's display."
   (let ((frame-type (framep-on-display display)))
     (cond
-     ((memq frame-type '(x w32 ns))
+     ((memq frame-type '(x w32 ns haiku))
       (x-display-backing-store display))
      (t
       'not-useful))))
@@ -2267,7 +2282,7 @@ display-save-under
 If DISPLAY is omitted or nil, it defaults to the selected frame's display."
   (let ((frame-type (framep-on-display display)))
     (cond
-     ((memq frame-type '(x w32 ns))
+     ((memq frame-type '(x w32 ns haiku))
       (x-display-save-under display))
      (t
       'not-useful))))
@@ -2280,7 +2295,7 @@ display-planes
 If DISPLAY is omitted or nil, it defaults to the selected frame's display."
   (let ((frame-type (framep-on-display display)))
     (cond
-     ((memq frame-type '(x w32 ns))
+     ((memq frame-type '(x w32 ns haiku))
       (x-display-planes display))
      ((eq frame-type 'pc)
       4)
@@ -2295,7 +2310,7 @@ display-color-cells
 If DISPLAY is omitted or nil, it defaults to the selected frame's display."
   (let ((frame-type (framep-on-display display)))
     (cond
-     ((memq frame-type '(x w32 ns))
+     ((memq frame-type '(x w32 ns haiku))
       (x-display-color-cells display))
      ((eq frame-type 'pc)
       16)
@@ -2314,6 +2329,8 @@ display-visual-class
     (cond
      ((memq frame-type '(x w32 ns))
       (x-display-visual-class display))
+     ((eq frame-type 'haiku)
+      'direct-color)
      ((and (memq frame-type '(pc t))
 	   (tty-display-color-p display))
       'static-color)
diff --git a/lisp/loadup.el b/lisp/loadup.el
index 158c02ecea..deec90662a 100644
--- a/lisp/loadup.el
+++ b/lisp/loadup.el
@@ -303,6 +303,13 @@
       (load "term/common-win")
       (load "term/x-win")))
 
+(if (featurep 'haiku)
+    (progn
+      (load "term/common-win")
+      (load "term/haiku-win")
+      (load "international/mule-util")
+      (load "international/ucs-normalize")))
+
 (if (or (eq system-type 'windows-nt)
         (featurep 'w32))
     (progn
@@ -546,6 +553,7 @@
               (delete-file output)))))
       ;; Recompute NAME now, so that it isn't set when we dump.
       (if (not (or (eq system-type 'ms-dos)
+                   (eq system-type 'haiku) ;; BFS doesn't support hard links
                    ;; Don't bother adding another name if we're just
                    ;; building bootstrap-emacs.
                    (member dump-mode '("pbootstrap" "bootstrap"))))
diff --git a/lisp/menu-bar.el b/lisp/menu-bar.el
index 8def1575b2..a2705853b2 100644
--- a/lisp/menu-bar.el
+++ b/lisp/menu-bar.el
@@ -2632,9 +2632,10 @@ menu-bar-open
 this is the numeric argument to the command.
 This function decides which method to use to access the menu
 depending on FRAME's terminal device.  On X displays, it calls
-`x-menu-bar-open'; on Windows, `w32-menu-bar-open'; otherwise it
-calls either `popup-menu' or `tmm-menubar' depending on whether
-`tty-menu-open-use-tmm' is nil or not.
+`x-menu-bar-open'; on Windows, `w32-menu-bar-open'; on Haiku,
+`haiku-menu-bar-open'; otherwise it calls either `popup-menu'
+or `tmm-menubar' depending on whether `tty-menu-open-use-tmm'
+is nil or not.
 
 If FRAME is nil or not given, use the selected frame."
   (interactive
@@ -2643,6 +2644,7 @@ menu-bar-open
     (cond
      ((eq type 'x) (x-menu-bar-open frame))
      ((eq type 'w32) (w32-menu-bar-open frame))
+     ((eq type 'haiku) (haiku-menu-bar-open frame))
      ((and (null tty-menu-open-use-tmm)
 	   (not (zerop (or (frame-parameter nil 'menu-bar-lines) 0))))
       ;; Make sure the menu bar is up to date.  One situation where
diff --git a/lisp/mwheel.el b/lisp/mwheel.el
index def7758774..7fab0ca776 100644
--- a/lisp/mwheel.el
+++ b/lisp/mwheel.el
@@ -55,7 +55,8 @@ mouse-wheel-change-button
     (mouse-wheel-mode 1)))
 
 (defcustom mouse-wheel-down-event
-  (if (or (featurep 'w32-win) (featurep 'ns-win))
+  (if (or (featurep 'w32-win) (featurep 'ns-win)
+          (featurep 'haiku-win))
       'wheel-up
     'mouse-4)
   "Event used for scrolling down."
@@ -64,7 +65,8 @@ mouse-wheel-down-event
   :set 'mouse-wheel-change-button)
 
 (defcustom mouse-wheel-up-event
-  (if (or (featurep 'w32-win) (featurep 'ns-win))
+  (if (or (featurep 'w32-win) (featurep 'ns-win)
+          (featurep 'haiku-win))
       'wheel-down
     'mouse-5)
   "Event used for scrolling up."
@@ -221,13 +223,15 @@ mwheel-scroll-right-function
   "Function that does the job of scrolling right.")
 
 (defvar mouse-wheel-left-event
-  (if (or (featurep 'w32-win) (featurep 'ns-win))
+  (if (or (featurep 'w32-win) (featurep 'ns-win)
+          (featurep 'haiku-win))
       'wheel-left
     'mouse-6)
   "Event used for scrolling left.")
 
 (defvar mouse-wheel-right-event
-  (if (or (featurep 'w32-win) (featurep 'ns-win))
+  (if (or (featurep 'w32-win) (featurep 'ns-win)
+          (featurep 'haiku-win))
       'wheel-right
     'mouse-7)
   "Event used for scrolling right.")
diff --git a/lisp/net/browse-url.el b/lisp/net/browse-url.el
index bf77ecc7af..ee98c40580 100644
--- a/lisp/net/browse-url.el
+++ b/lisp/net/browse-url.el
@@ -39,6 +39,7 @@
 ;; browse-url-chrome                  Chrome      47.0.2526.111
 ;; browse-url-chromium                Chromium    3.0
 ;; browse-url-epiphany                Epiphany    Don't know
+;; browse-url-webpositive             WebPositive 1.2-alpha (Haiku R1/beta3)
 ;; browse-url-w3                      w3          0
 ;; browse-url-text-*	              Any text browser     0
 ;; browse-url-generic                 arbitrary
@@ -156,6 +157,7 @@ browse-url--browser-defcustom-type
     (function-item :tag "Google Chrome" :value browse-url-chrome)
     (function-item :tag "Chromium" :value browse-url-chromium)
     (function-item :tag "Epiphany" :value  browse-url-epiphany)
+    (function-item :tag "WebPositive" :value browse-url-webpositive)
     (function-item :tag "Text browser in an xterm window"
 		   :value browse-url-text-xterm)
     (function-item :tag "Text browser in an Emacs window"
@@ -366,6 +368,11 @@ browse-url-epiphany-startup-arguments
 `browse-url' is loaded."
   :type '(repeat (string :tag "Argument")))
 
+(defcustom browse-url-webpositive-program "WebPositive"
+  "The name by which to invoke WebPositive."
+  :type 'string
+  :version "28.1")
+
 ;; GNOME means of invoking either Mozilla or Netscape.
 (defvar browse-url-gnome-moz-program "gnome-moz-remote")
 
@@ -1050,6 +1057,7 @@ browse-url-default-browser
     ((executable-find browse-url-kde-program) 'browse-url-kde)
 ;;;    ((executable-find browse-url-netscape-program) 'browse-url-netscape)
     ((executable-find browse-url-chrome-program) 'browse-url-chrome)
+    ((executable-find browse-url-webpositive-program) 'browse-url-webpositive)
     ((executable-find browse-url-xterm-program) 'browse-url-text-xterm)
     ((locate-library "w3") 'browse-url-w3)
     (t
@@ -1376,6 +1384,18 @@ browse-url-epiphany-sentinel
 
 (defvar url-handler-regexp)
 
+;;;###autoload
+(defun browse-url-webpositive (url &optional _new-window)
+  "Ask the WebPositive WWW browser to load URL.
+Default to the URL around or before point.
+The optional argument NEW-WINDOW is not used."
+  (interactive (browse-url-interactive-arg "URL: "))
+  (setq url (browse-url-encode-url url))
+  (let* ((process-environment (browse-url-process-environment)))
+    (start-process (concat "WebPositive " url) nil "WebPositive" url)))
+
+(function-put 'browse-url-webpositive 'browse-url-browser-kind 'external)
+
 ;;;###autoload
 (defun browse-url-emacs (url &optional same-window)
   "Ask Emacs to load URL into a buffer and show it in another window.
diff --git a/lisp/term/haiku-win.el b/lisp/term/haiku-win.el
new file mode 100644
index 0000000000..f54c4ec175
--- /dev/null
+++ b/lisp/term/haiku-win.el
@@ -0,0 +1,121 @@
+;;; haiku-win.el --- set up windowing on Haiku -*- lexical-binding: t -*-
+
+;; Copyright (C) 2021 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/>.
+
+;;; Commentary:
+
+;; Support for using Haiku's BeOS derived windowing system.
+
+;;; Code:
+
+(eval-when-compile (require 'cl-lib))
+(unless (featurep 'haiku)
+  (error "%s: Loading haiku-win without having Haiku"
+         invocation-name))
+
+;; Documentation-purposes only: actually loaded in loadup.el.
+(require 'frame)
+(require 'mouse)
+(require 'scroll-bar)
+(require 'menu-bar)
+(require 'fontset)
+(require 'dnd)
+
+(add-to-list 'display-format-alist '(".*" . haiku-win))
+
+;;;; Command line argument handling.
+
+(defvar x-invocation-args)
+(defvar x-command-line-resources)
+
+(defvar haiku-initialized)
+
+(declare-function x-open-connection "haikufns.c")
+(declare-function x-handle-args "common-win")
+(declare-function haiku-selection-data "haikuselect.c")
+(declare-function haiku-selection-put "haikuselect.c")
+(declare-function haiku-put-resource "haikufns.c")
+
+(defun haiku--handle-x-command-line-resources (command-line-resources)
+  "Handle command line X resources specified with the option `-xrm'.
+The resources should be a list of strings in COMMAND-LINE-RESOURCES."
+  (dolist (s command-line-resources)
+    (let ((components (split-string s ":")))
+      (when (car components)
+        (haiku-put-resource (car components)
+                            (string-trim-left (cadr components)))))))
+
+(cl-defmethod window-system-initialization (&context (window-system haiku)
+                                                     &optional display)
+  "Set up the window system.  WINDOW-SYSTEM must be HAIKU.
+DISPLAY may be set to the name of a display that will be initialized."
+  (cl-assert (not haiku-initialized))
+
+  (create-default-fontset)
+  (when x-command-line-resources
+    (haiku--handle-x-command-line-resources
+     (split-string x-command-line-resources "\n")))
+  (x-open-connection (or display "be") x-command-line-resources t)
+  (setq haiku-initialized t))
+
+(cl-defmethod frame-creation-function (params &context (window-system haiku))
+  (x-create-frame-with-faces params))
+
+(cl-defmethod handle-args-function (args &context (window-system haiku))
+  (x-handle-args args))
+
+(defun haiku--selection-type-to-mime (type)
+  "Convert symbolic selection type TYPE to its MIME equivalent.
+If TYPE is nil, return \"text/plain\"."
+  (cond
+   ((memq type '(TEXT COMPOUND_TEXT STRING UTF8_STRING)) "text/plain")
+   ((stringp type) type)
+   (t "text/plain")))
+
+(cl-defmethod gui-backend-get-selection (type data-type
+                                              &context (window-system haiku))
+  (haiku-selection-data type (haiku--selection-type-to-mime data-type)))
+
+(cl-defmethod gui-backend-set-selection (type value
+                                              &context (window-system haiku))
+  (haiku-selection-put type "text/plain" value))
+
+(cl-defmethod gui-backend-selection-exists-p (selection
+                                              &context (window-system haiku))
+  (haiku-selection-data selection "text/plain"))
+
+(cl-defmethod gui-backend-selection-owner-p (_
+                                             &context (window-system haiku))
+  t)
+
+(declare-function haiku-read-file-name "haikufns.c")
+
+(defun x-file-dialog (prompt dir default_filename mustmatch only_dir_p)
+  "SKIP: real doc in xfns.c."
+  (if (eq (framep-on-display (selected-frame)) 'haiku)
+      (haiku-read-file-name prompt (selected-frame)
+                            (or dir (and default_filename
+                                         (file-name-directory default_filename)))
+                            mustmatch only_dir_p
+                            (file-name-nondirectory default_filename))
+    (error "x-file-dialog on a tty frame")))
+
+(provide 'haiku-win)
+(provide 'term/haiku-win)
+
+;;; haiku-win.el ends here
diff --git a/lisp/tooltip.el b/lisp/tooltip.el
index 23b67ee2ca..6cc482d012 100644
--- a/lisp/tooltip.el
+++ b/lisp/tooltip.el
@@ -368,10 +368,15 @@ tooltip-show-help-non-mode
      ((equal-including-properties tooltip-help-message (current-message))
       (message nil)))))
 
+(declare-function menu-or-popup-active-p "xmenu.c" ())
+
 (defun tooltip-show-help (msg)
   "Function installed as `show-help-function'.
 MSG is either a help string to display, or nil to cancel the display."
-  (if (display-graphic-p)
+  (if (and (display-graphic-p)
+           (or (not (eq window-system 'haiku)) ;; On Haiku, there isn't a reliable way to show tooltips
+                                               ;; above menus.
+               (not (menu-or-popup-active-p))))
       (let ((previous-help tooltip-help-message))
 	(setq tooltip-help-message msg)
 	(cond ((null msg)
diff --git a/lisp/version.el b/lisp/version.el
index 3a3093fdd4..5d0a1ae37d 100644
--- a/lisp/version.el
+++ b/lisp/version.el
@@ -53,6 +53,8 @@ gtk-version-string
 (defvar ns-version-string)
 (defvar cairo-version-string)
 
+(declare-function haiku-get-version-string "haikufns.c")
+
 (defun emacs-version (&optional here)
   "Return string describing the version of Emacs that is running.
 If optional argument HERE is non-nil, insert string at point.
@@ -71,6 +73,8 @@ emacs-version
 		       ((featurep 'x-toolkit) ", X toolkit")
 		       ((featurep 'ns)
 			(format ", NS %s" ns-version-string))
+                       ((featurep 'haiku)
+                        (format ", Haiku %s" (haiku-get-version-string)))
 		       (t ""))
 		 (if (featurep 'cairo)
 		     (format ", cairo version %s" cairo-version-string)
diff --git a/src/Makefile.in b/src/Makefile.in
index 732cd8f099..1f32e21e47 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -34,6 +34,7 @@ top_builddir =
 abs_top_srcdir=@abs_top_srcdir@
 VPATH = $(srcdir)
 CC = @CC@
+CXX = @CXX@
 CFLAGS = @CFLAGS@
 CPPFLAGS = @CPPFLAGS@
 LDFLAGS = @LDFLAGS@
@@ -50,6 +51,7 @@ lib =
 libsrc = ../lib-src
 etc = ../etc
 oldXMenudir = ../oldXMenu
+be_xpmdir = ../be_xpm
 lwlibdir = ../lwlib
 
 # Configuration files for .o files to depend on.
@@ -339,10 +341,23 @@ BUILD_DETAILS =
 
 UNEXEC_OBJ = @UNEXEC_OBJ@
 
+HAIKU_OBJ = @HAIKU_OBJ@
+HAIKU_CXX_OBJ = @HAIKU_CXX_OBJ@
+HAIKU_LIBS = @HAIKU_LIBS@
+HAIKU_CFLAGS = @HAIKU_CFLAGS@
+
 DUMPING=@DUMPING@
 CHECK_STRUCTS = @CHECK_STRUCTS@
 HAVE_PDUMPER = @HAVE_PDUMPER@
 
+HAVE_BE_APP = @HAVE_BE_APP@
+
+ifeq ($(HAVE_BE_APP),yes)
+HAIKU_LIBXPM = @LIBXPM@
+else
+HAIKU_LIBXPM =
+endif
+
 ## ARM Macs require that all code have a valid signature.  Since pdump
 ## invalidates the signature, we must re-sign to fix it.
 DO_CODESIGN=$(patsubst aarch64-apple-darwin%,yes,@configuration@)
@@ -360,6 +375,9 @@ pdmp :=
 
 # Flags that might be in WARN_CFLAGS but are not valid for Objective C.
 NON_OBJC_CFLAGS = -Wignored-attributes -Wignored-qualifiers -Wopenmp-simd
+# Ditto, but for C++.
+NON_CXX_CFLAGS = -Wmissing-prototypes -Wnested-externs -Wold-style-definition \
+  -Wstrict-prototypes -Wno-override-init
 
 # -Demacs makes some files produce the correct version for use in Emacs.
 # MYCPPFLAGS is for by-hand Emacs-specific overrides, e.g.,
@@ -375,17 +393,21 @@ EMACS_CFLAGS=
   $(HARFBUZZ_CFLAGS) $(LIBOTF_CFLAGS) $(M17N_FLT_CFLAGS) $(DEPFLAGS) \
   $(LIBSYSTEMD_CFLAGS) $(JSON_CFLAGS) \
   $(LIBGNUTLS_CFLAGS) $(NOTIFY_CFLAGS) $(CAIRO_CFLAGS) \
-  $(WERROR_CFLAGS)
+  $(WERROR_CFLAGS) $(HAIKU_CFLAGS)
 ALL_CFLAGS = $(EMACS_CFLAGS) $(WARN_CFLAGS) $(CFLAGS)
 ALL_OBJC_CFLAGS = $(EMACS_CFLAGS) \
   $(filter-out $(NON_OBJC_CFLAGS),$(WARN_CFLAGS)) $(CFLAGS) \
   $(GNU_OBJC_CFLAGS)
+ALL_CXX_CFLAGS = $(EMACS_CFLAGS) \
+  $(filter-out $(NON_CXX_CFLAGS),$(WARN_CFLAGS)) $(CXXFLAGS)
 
-.SUFFIXES: .m
+.SUFFIXES: .m .cc
 .c.o:
 	$(AM_V_CC)$(CC) -c $(CPPFLAGS) $(ALL_CFLAGS) $(PROFILING_CFLAGS) $<
 .m.o:
 	$(AM_V_CC)$(CC) -c $(CPPFLAGS) $(ALL_OBJC_CFLAGS) $(PROFILING_CFLAGS) $<
+.cc.o:
+	$(AM_V_CXX)$(CXX) -c $(CPPFLAGS) $(ALL_CXX_CFLAGS) $(PROFILING_CFLAGS) $<
 
 ## lastfile must follow all files whose initialized data areas should
 ## be dumped as pure by dump-emacs.
@@ -407,8 +429,10 @@ base_obj =
 	thread.o systhread.o \
 	$(if $(HYBRID_MALLOC),sheap.o) \
 	$(MSDOS_OBJ) $(MSDOS_X_OBJ) $(NS_OBJ) $(CYGWIN_OBJ) $(FONT_OBJ) \
-	$(W32_OBJ) $(WINDOW_SYSTEM_OBJ) $(XGSELOBJ) $(JSON_OBJ)
-obj = $(base_obj) $(NS_OBJC_OBJ)
+	$(W32_OBJ) $(WINDOW_SYSTEM_OBJ) $(XGSELOBJ) $(JSON_OBJ) \
+	$(HAIKU_OBJ)
+doc_obj = $(base_obj) $(NS_OBJC_OBJ)
+obj = $(doc_obj) $(HAIKU_CXX_OBJ)
 
 ## Object files used on some machine or other.
 ## These go in the DOC file on all machines in case they are needed.
@@ -422,7 +446,8 @@ SOME_MACHINE_OBJECTS =
   w32.o w32console.o w32cygwinx.o w32fns.o w32heap.o w32inevt.o w32notify.o \
   w32menu.o w32proc.o w32reg.o w32select.o w32term.o w32xfns.o \
   w16select.o widget.o xfont.o ftfont.o xftfont.o gtkutil.o \
-  xsettings.o xgselect.o termcap.o hbfont.o
+  xsettings.o xgselect.o termcap.o hbfont.o \
+  haikuterm.o haikufns.o haikumenu.o haikufont.o
 
 ## gmalloc.o if !SYSTEM_MALLOC && !DOUG_LEA_MALLOC, else empty.
 GMALLOC_OBJ=@GMALLOC_OBJ@
@@ -448,7 +473,12 @@ FIRSTFILE_OBJ=
 ALLOBJS = $(FIRSTFILE_OBJ) $(VMLIMIT_OBJ) $(obj) $(otherobj)
 
 # Must be first, before dep inclusion!
+ifneq ($(HAVE_BE_APP),yes)
 all: emacs$(EXEEXT) $(pdmp) $(OTHER_FILES)
+else
+all: Emacs Emacs.pdmp $(OTHER_FILES)
+endif
+
 .PHONY: all
 
 dmpstruct_headers=$(srcdir)/lisp.h $(srcdir)/buffer.h \
@@ -517,7 +547,7 @@ LIBES =
    $(FREETYPE_LIBS) $(FONTCONFIG_LIBS) $(HARFBUZZ_LIBS) $(LIBOTF_LIBS) $(M17N_FLT_LIBS) \
    $(LIBGNUTLS_LIBS) $(LIB_PTHREAD) $(GETADDRINFO_A_LIBS) $(LCMS2_LIBS) \
    $(NOTIFY_LIBS) $(LIB_MATH) $(LIBZ) $(LIBMODULES) $(LIBSYSTEMD_LIBS) \
-   $(JSON_LIBS) $(LIBGMP) $(LIBGCCJIT_LIBS)
+   $(JSON_LIBS) $(LIBGMP) $(LIBGCCJIT_LIBS) $(HAIKU_LIBS)
 
 ## FORCE it so that admin/unidata can decide whether this file is
 ## up-to-date.  Although since charprop depends on bootstrap-emacs,
@@ -565,6 +595,18 @@ emacs$(EXEEXT):
 	rm -f $@ && cp -f temacs$(EXEEXT) $@
 endif
 
+## On Haiku, also produce a binary named Emacs with the appropriate
+## icon set.
+
+ifeq ($(HAVE_BE_APP),yes)
+Emacs: emacs$(EXEEXT)
+	cp -f emacs$(EXEEXT) $@
+	$(AM_V_GEN) $(libsrc)/be-resources \
+	  $(etc)/images/icons/hicolor/32x32/apps/emacs.png $@
+Emacs.pdmp: $(pdmp)
+	$(AM_V_GEN) cp -f $(pdmp) $@
+endif
+
 ifeq ($(DUMPING),pdumper)
 $(pdmp): emacs$(EXEEXT)
 	LC_ALL=C $(RUN_TEMACS) -batch $(BUILD_DETAILS) -l loadup --temacs=pdump \
@@ -583,11 +625,11 @@ $(pdmp):
 ## for the first time, this prevents any variation between configurations
 ## in the contents of the DOC file.
 ##
-$(etc)/DOC: lisp.mk $(libsrc)/make-docfile$(EXEEXT) $(obj) $(lisp)
+$(etc)/DOC: lisp.mk $(libsrc)/make-docfile$(EXEEXT) $(doc_obj) $(lisp)
 	$(AM_V_GEN)$(MKDIR_P) $(etc)
 	$(AM_V_at)rm -f $(etc)/DOC
 	$(AM_V_at)$(libsrc)/make-docfile -d $(srcdir) \
-	  $(SOME_MACHINE_OBJECTS) $(obj) > $(etc)/DOC
+	  $(SOME_MACHINE_OBJECTS) $(doc_obj) > $(etc)/DOC
 	$(AM_V_at)$(libsrc)/make-docfile -a $(etc)/DOC -d $(lispsource) \
 	  $(shortlisp)
 
@@ -605,7 +647,7 @@ buildobj.h:
 GLOBAL_SOURCES = $(base_obj:.o=.c) $(NS_OBJC_OBJ:.o=.m)
 
 gl-stamp: $(libsrc)/make-docfile$(EXEEXT) $(GLOBAL_SOURCES)
-	$(AM_V_GLOBALS)$(libsrc)/make-docfile -d $(srcdir) -g $(obj) > globals.tmp
+	$(AM_V_GLOBALS)$(libsrc)/make-docfile -d $(srcdir) -g $(doc_obj) > globals.tmp
 	$(AM_V_at)$(top_srcdir)/build-aux/move-if-change globals.tmp globals.h
 	$(AM_V_at)echo timestamp > $@
 
@@ -629,10 +671,16 @@ $(LIBEGNU_ARCHIVE):
 ## This goes on to affect various things, and the emacs binary fails
 ## to start if Vinstallation_directory has the wrong value.
 temacs$(EXEEXT): $(LIBXMENU) $(ALLOBJS) $(LIBEGNU_ARCHIVE) $(EMACSRES) \
-  $(charsets) $(charscript) $(MAKE_PDUMPER_FINGERPRINT)
-	$(AM_V_CCLD)$(CC) -o $@.tmp \
+  $(charsets) $(charscript) $(MAKE_PDUMPER_FINGERPRINT) $(HAIKU_LIBXPM)
+ifeq ($(HAVE_BE_APP),yes)
+	$(AM_V_CXXLD)$(CXX) -o $@.tmp \
 	  $(ALL_CFLAGS) $(TEMACS_LDFLAGS) $(LDFLAGS) \
+	  $(ALLOBJS) $(LIBEGNU_ARCHIVE) $(W32_RES_LINK) $(LIBES) -lstdc++
+else
+	$(AM_V_CCLD)$(CC) -o $@.tmp \
+	  $(ALL_CFLAGS) $(CXXFLAGS) $(TEMACS_LDFLAGS) $(LDFLAGS) \
 	  $(ALLOBJS) $(LIBEGNU_ARCHIVE) $(W32_RES_LINK) $(LIBES)
+endif
 ifeq ($(HAVE_PDUMPER),yes)
 	$(AM_V_at)$(MAKE_PDUMPER_FINGERPRINT) $@.tmp
 ifeq ($(DO_CODESIGN),yes)
@@ -653,6 +701,12 @@ $(lwlibdir)/liblw.a:
 	$(MAKE) -C $(dir $@) $(notdir $@)
 $(oldXMenudir)/libXMenu11.a: FORCE
 	$(MAKE) -C $(dir $@) $(notdir $@)
+
+# Similarly, the following rule is only used if Xpm is to be used on Haiku,
+# but there's also no harm in keeping it defined
+$(be_xpmdir)/libXpmBe.a: FORCE
+	$(MAKE) -C $(dir $@) $(notdir $@)
+
 FORCE:
 .PHONY: FORCE
 
diff --git a/src/alloc.c b/src/alloc.c
index 4ea337ddba..2c2434feaa 100644
--- a/src/alloc.c
+++ b/src/alloc.c
@@ -6141,6 +6141,10 @@ garbage_collect (void)
   xg_mark_data ();
 #endif
 
+#ifdef HAVE_HAIKU
+  mark_haiku_display ();
+#endif
+
 #ifdef HAVE_WINDOW_SYSTEM
   mark_fringe_data ();
 #endif
diff --git a/src/dispextern.h b/src/dispextern.h
index f4c7575b35..37801ae887 100644
--- a/src/dispextern.h
+++ b/src/dispextern.h
@@ -134,6 +134,13 @@ #define FACE_COLOR_TO_PIXEL(face_color, frame) (FRAME_NS_P (frame) \
 #define FACE_COLOR_TO_PIXEL(face_color, frame) face_color
 #endif
 
+#ifdef HAVE_HAIKU
+#include "haikugui.h"
+typedef struct haiku_display_info Display_Info;
+typedef Emacs_Pixmap Emacs_Pix_Container;
+typedef Emacs_Pixmap Emacs_Pix_Context;
+#endif
+
 #ifdef HAVE_WINDOW_SYSTEM
 # include <time.h>
 # include "fontset.h"
@@ -3009,7 +3016,7 @@ reset_mouse_highlight (Mouse_HLInfo *hlinfo)
 #ifdef HAVE_WINDOW_SYSTEM
 
 # if (defined USE_CAIRO || defined HAVE_XRENDER \
-      || defined HAVE_NS || defined HAVE_NTGUI)
+      || defined HAVE_NS || defined HAVE_NTGUI || defined HAVE_HAIKU)
 #  define HAVE_NATIVE_TRANSFORMS
 # endif
 
@@ -3048,6 +3055,14 @@ reset_mouse_highlight (Mouse_HLInfo *hlinfo)
 #ifdef HAVE_NTGUI
   XFORM xform;
 #endif
+#ifdef HAVE_HAIKU
+  /* Non-zero if the image has not yet been transformed for display. */
+  int have_be_transforms_p;
+
+  double be_rotate;
+  double be_scale_x;
+  double be_scale_y;
+#endif
 
   /* Colors allocated for this image, if any.  Allocated via xmalloc.  */
   unsigned long *colors;
@@ -3487,7 +3502,8 @@ #define TRY_WINDOW_IGNORE_FONTS_CHANGE	(1 << 1)
 void prepare_image_for_display (struct frame *, struct image *);
 ptrdiff_t lookup_image (struct frame *, Lisp_Object, int);
 
-#if defined HAVE_X_WINDOWS || defined USE_CAIRO || defined HAVE_NS
+#if defined HAVE_X_WINDOWS || defined USE_CAIRO || defined HAVE_NS \
+  || defined HAVE_HAIKU
 #define RGB_PIXEL_COLOR unsigned long
 #endif
 
diff --git a/src/dispnew.c b/src/dispnew.c
index 0c31319917..113174af04 100644
--- a/src/dispnew.c
+++ b/src/dispnew.c
@@ -6151,7 +6151,7 @@ sit_for (Lisp_Object timeout, bool reading, int display_option)
     wrong_type_argument (Qnumberp, timeout);
 
 
-#ifdef USABLE_SIGIO
+#if defined (USABLE_SIGIO) || defined (USABLE_SIGPOLL)
   gobble_input ();
 #endif
 
@@ -6458,6 +6458,15 @@ init_display_interactive (void)
     }
 #endif
 
+#ifdef HAVE_HAIKU
+  if (!inhibit_window_system && !will_dump_p ())
+    {
+      Vinitial_window_system = Qhaiku;
+      Vwindow_system_version = make_fixnum (1);
+      return;
+    }
+#endif
+
   /* If no window system has been specified, try to use the terminal.  */
   if (! isatty (STDIN_FILENO))
     fatal ("standard input is not a tty");
diff --git a/src/emacs.c b/src/emacs.c
index 866e43fda9..22f9f163a7 100644
--- a/src/emacs.c
+++ b/src/emacs.c
@@ -2177,6 +2177,18 @@ main (int argc, char **argv)
       syms_of_fontset ();
 #endif /* HAVE_NS */
 
+#ifdef HAVE_HAIKU
+      syms_of_haikuterm ();
+      syms_of_haikufns ();
+      syms_of_haikumenu ();
+      syms_of_haikufont ();
+      syms_of_haikuselect ();
+#ifdef HAVE_NATIVE_IMAGE_API
+      syms_of_haikuimage ();
+#endif
+      syms_of_fontset ();
+#endif /* HAVE_HAIKU */
+
       syms_of_gnutls ();
 
 #ifdef HAVE_INOTIFY
@@ -2231,6 +2243,10 @@ main (int argc, char **argv)
 #if defined WINDOWSNT || defined HAVE_NTGUI
       globals_of_w32select ();
 #endif
+
+#ifdef HAVE_HAIKU
+      init_haiku_select ();
+#endif
     }
 
   init_charset ();
@@ -2690,6 +2706,9 @@ shut_down_emacs (int sig, Lisp_Object stuff)
   /* Don't update display from now on.  */
   Vinhibit_redisplay = Qt;
 
+#ifdef HAVE_HAIKU
+  be_app_quit ();
+#endif
   /* If we are controlling the terminal, reset terminal modes.  */
 #ifndef DOS_NT
   pid_t tpgrp = tcgetpgrp (STDIN_FILENO);
diff --git a/src/fileio.c b/src/fileio.c
index 0db8ed793b..40b320ef96 100644
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -6186,7 +6186,7 @@ DEFUN ("next-read-file-uses-dialog-p", Fnext_read_file_uses_dialog_p,
   (void)
 {
 #if (defined USE_GTK || defined USE_MOTIF \
-     || defined HAVE_NS || defined HAVE_NTGUI)
+     || defined HAVE_NS || defined HAVE_NTGUI || defined HAVE_HAIKU)
   if ((NILP (last_nonmenu_event) || CONSP (last_nonmenu_event))
       && use_dialog_box
       && use_file_dialog
diff --git a/src/filelock.c b/src/filelock.c
index cc185d96cd..c12776246b 100644
--- a/src/filelock.c
+++ b/src/filelock.c
@@ -65,7 +65,7 @@ Copyright (C) 1985-1987, 1993-1994, 1996, 1998-2021 Free Software
 #define BOOT_TIME_FILE "/var/run/random-seed"
 #endif
 
-#if !defined WTMP_FILE && !defined WINDOWSNT
+#if !defined WTMP_FILE && !defined WINDOWSNT && defined BOOT_TIME
 #define WTMP_FILE "/var/log/wtmp"
 #endif
 
diff --git a/src/floatfns.c b/src/floatfns.c
index aadae4fd9d..ab3fe19acf 100644
--- a/src/floatfns.c
+++ b/src/floatfns.c
@@ -347,6 +347,19 @@ DEFUN ("logb", Flogb, Slogb, 1, 1, 0,
 double_integer_scale (double d)
 {
   int exponent = ilogb (d);
+#ifdef HAIKU
+  /* On Haiku, the values of FP_ILOGB0 and FP_ILOGBNAN are identical.
+     To top it all, both are also identical to INT_MAX. */
+  if (exponent == FP_ILOGB0)
+    {
+      if (isnan (d))
+	return (DBL_MANT_DIG - DBL_MIN_EXP) + 2;
+      if (isinf (d))
+	return (DBL_MANT_DIG - DBL_MIN_EXP) + 1;
+
+      return (DBL_MANT_DIG - DBL_MIN_EXP);
+    }
+#endif
   return (DBL_MIN_EXP - 1 <= exponent && exponent < INT_MAX
 	  ? DBL_MANT_DIG - 1 - exponent
 	  : (DBL_MANT_DIG - DBL_MIN_EXP
diff --git a/src/font.c b/src/font.c
index e043ef8d01..ffab700c57 100644
--- a/src/font.c
+++ b/src/font.c
@@ -5607,6 +5607,9 @@ syms_of_font (void)
 #ifdef HAVE_WINDOW_SYSTEM
 #ifdef HAVE_FREETYPE
   syms_of_ftfont ();
+#ifdef HAVE_HAIKU
+  syms_of_ftbefont ();
+#endif
 #ifdef HAVE_X_WINDOWS
   syms_of_xfont ();
 #ifdef USE_CAIRO
diff --git a/src/font.h b/src/font.h
index d3e1530642..7c80d4688d 100644
--- a/src/font.h
+++ b/src/font.h
@@ -998,7 +998,8 @@ #define FONT_DEFERRED_LOG(ACTION, ARG, RESULT)		\
 INLINE bool
 font_data_structures_may_be_ill_formed (void)
 {
-#ifdef USE_CAIRO
+  /* ftbefont experiences this too... odd. */
+#if defined USE_CAIRO || defined HAVE_HAIKU
   /* Although this works around Bug#20890, it is probably not the
      right thing to do.  */
   return gc_in_progress;
diff --git a/src/frame.c b/src/frame.c
index f95566818a..ee12f638b9 100644
--- a/src/frame.c
+++ b/src/frame.c
@@ -226,6 +226,7 @@ DEFUN ("framep", Fframep, Sframep, 1, 1, 0,
  `w32' for an Emacs frame that is a window on MS-Windows display,
  `ns' for an Emacs frame on a GNUstep or Macintosh Cocoa display,
  `pc' for a direct-write MS-DOS frame.
+ `haiku` for an Emacs frame running in Haiku.
 See also `frame-live-p'.  */)
   (Lisp_Object object)
 {
@@ -244,6 +245,8 @@ DEFUN ("framep", Fframep, Sframep, 1, 1, 0,
       return Qpc;
     case output_ns:
       return Qns;
+    case output_haiku:
+      return Qhaiku;
     default:
       emacs_abort ();
     }
@@ -6024,6 +6027,7 @@ syms_of_frame (void)
   DEFSYM (Qw32, "w32");
   DEFSYM (Qpc, "pc");
   DEFSYM (Qns, "ns");
+  DEFSYM (Qhaiku, "haiku");
   DEFSYM (Qvisible, "visible");
   DEFSYM (Qbuffer_predicate, "buffer-predicate");
   DEFSYM (Qbuffer_list, "buffer-list");
diff --git a/src/frame.h b/src/frame.h
index a8ad011889..a9b3042dcc 100644
--- a/src/frame.h
+++ b/src/frame.h
@@ -585,6 +585,7 @@ #define EMACS_FRAME_H
     struct x_output *x;         /* From xterm.h.  */
     struct w32_output *w32;     /* From w32term.h.  */
     struct ns_output *ns;       /* From nsterm.h.  */
+    struct haiku_output *haiku; /* From haikuterm.h. */
   }
   output_data;
 
@@ -852,6 +853,11 @@ #define FRAME_NS_P(f) false
 #else
 #define FRAME_NS_P(f) ((f)->output_method == output_ns)
 #endif
+#ifndef HAVE_HAIKU
+#define FRAME_HAIKU_P(f) false
+#else
+#define FRAME_HAIKU_P(f) ((f)->output_method == output_haiku)
+#endif
 
 /* FRAME_WINDOW_P tests whether the frame is a graphical window system
    frame.  */
@@ -864,6 +870,9 @@ #define FRAME_WINDOW_P(f) FRAME_W32_P (f)
 #ifdef HAVE_NS
 #define FRAME_WINDOW_P(f) FRAME_NS_P(f)
 #endif
+#ifdef HAVE_HAIKU
+#define FRAME_WINDOW_P(f) FRAME_HAIKU_P (f)
+#endif
 #ifndef FRAME_WINDOW_P
 #define FRAME_WINDOW_P(f) ((void) (f), false)
 #endif
diff --git a/src/ftbefont.c b/src/ftbefont.c
new file mode 100644
index 0000000000..41f6b36017
--- /dev/null
+++ b/src/ftbefont.c
@@ -0,0 +1,278 @@
+/* FreeType font driver for Haiku
+   Copyright (C) 2021 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 "blockinput.h"
+#include "haiku_support.h"
+#include "lisp.h"
+#include "dispextern.h"
+#include "font.h"
+#include "pdumper.h"
+#include "frame.h"
+#include "haikuterm.h"
+#include "termchar.h"
+#include "math.h"
+#include "ftfont.h"
+
+#ifdef HAVE_HARFBUZZ
+#include <hb.h>
+#include <hb-ft.h>
+#endif
+
+static Lisp_Object
+ftbefont_list (struct frame *f, Lisp_Object spec)
+{
+  return ftfont_list2 (f, spec, Qftbe);
+}
+
+static Lisp_Object
+ftbefont_match (struct frame *f, Lisp_Object spec)
+{
+  return ftfont_match2 (f, spec, Qftbe);
+}
+
+#ifdef HAVE_HARFBUZZ
+
+static Lisp_Object
+ftbehbfont_list (struct frame *f, Lisp_Object spec)
+{
+  return ftfont_list2 (f, spec, Qftbehb);
+}
+
+static Lisp_Object
+ftbehbfont_match (struct frame *f, Lisp_Object spec)
+{
+  return ftfont_match2 (f, spec, Qftbehb);
+}
+
+#endif
+
+static int
+ftbefont_draw (struct glyph_string *s,
+               int from, int to, int x, int y, bool with_background)
+{
+  struct frame *f = s->f;
+  struct face *face = s->face;
+  struct font_info *info = (struct font_info *) s->font;
+  void *view = FRAME_HAIKU_VIEW (f);
+
+  if (s->hl == DRAW_MOUSE_FACE)
+    face = FACE_FROM_ID_OR_NULL (s->f,
+				 MOUSE_HL_INFO (s->f)->mouse_face_face_id);
+  if (!face)
+    face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
+
+  block_input ();
+  prepare_face_for_display (s->f, face);
+
+  if (info->ft_size != info->face->size)
+    FT_Activate_Size (info->ft_size);
+
+  BView_draw_lock (view);
+  BView_StartClip (view);
+
+  if (with_background && !s->background_filled_p)
+    {
+      int fibw = FRAME_INTERNAL_BORDER_WIDTH (s->f);
+      int vx, vy, width, height;
+      vx = s->x;
+      vy = s->y;
+      width = s->width;
+      height = FONT_HEIGHT (face->font);
+      if (s->face->box != FACE_NO_BOX && s->first_glyph->left_box_line_p)
+	vx += max (s->face->box_vertical_line_width, 0);
+
+
+      int mbox_line_width = max (s->face->box_vertical_line_width, 0);
+
+      if (s->row->full_width_p)
+        {
+          if (vx <= fibw + 1 + mbox_line_width)
+            {
+              width += vx - mbox_line_width;
+              vx = mbox_line_width;
+            }
+          if (FRAME_PIXEL_WIDTH (s->f) - (vx + width)
+                <= fibw + 1)
+            width += fibw;
+        }
+      if (s->face->box == FACE_NO_BOX)
+        {
+          /* Expand unboxed top row over internal border.  */
+          if (vy <= fibw + 1 + mbox_line_width)
+            {
+              height += vy;
+              vy = 0;
+            }
+        }
+      else
+        {
+          int correction = abs (s->face->box_horizontal_line_width) + 1;
+          vy += correction;
+          height -= 2 * correction;
+          correction = abs (s->face->box_vertical_line_width) + 1;
+          vx += correction;
+          width -= 2 * correction;
+        }
+      BView_SetHighColor (view, s->hl == DRAW_CURSOR ?
+			  FRAME_CURSOR_COLOR (s->f).pixel : face->background);
+
+      BView_FillRectangle (view, vx, vy, width, height);
+      s->background_filled_p = 1;
+    }
+
+  if (s->left_overhang && s->clip_head && !s->for_overlaps)
+    {
+      /* XXX: Why is this neccessary? */
+      BView_ClipToRect (view, s->clip_head->x, 0,
+			FRAME_PIXEL_WIDTH (f), FRAME_PIXEL_HEIGHT (f));
+    }
+
+  if (s->hl == DRAW_CURSOR)
+    BView_FT_render_glyphs (info->face, &s->char2b[from], to - from,
+			    FRAME_OUTPUT_DATA (s->f)->cursor_fg, x, y, view,
+			    info->sc);
+  else
+    BView_FT_render_glyphs (info->face, &s->char2b[from], to - from,
+			    face->foreground, x, y, view, info->sc);
+
+  BView_EndClip (view);
+  BView_draw_unlock (view);
+  unblock_input ();
+  return 0;
+}
+
+static Lisp_Object
+ftbefont_open (struct frame *f, Lisp_Object entity, int pixel_size)
+{
+  Lisp_Object ft = ftfont_open (f, entity, pixel_size);
+  if (FONT_OBJECT_P (ft))
+    {
+      struct font_info *info = (struct font_info *) XFONT_OBJECT (ft);
+      ASET (ft, FONT_TYPE_INDEX, Qftbe);
+      info->font.driver = &ftbefont_driver;
+      info->sc = be_make_ft_shape_cache ();
+    }
+  return ft;
+}
+
+#ifdef HAVE_HARFBUZZ
+static hb_font_t *
+ftbehbfont_begin_hb_font (struct font *font, double *position_unit)
+{
+  struct font_info *ftfont_info = (struct font_info *) font;
+  FT_Face ft_face = ftfont_info->face;
+
+  ftfont_info->ft_size = ft_face->size;
+  return fthbfont_begin_hb_font (font, position_unit);
+}
+
+static void
+ftbehbfont_end_hb_font (struct font *font, hb_font_t *hb_font)
+{
+  struct font_info *ftfont_info = (struct font_info *) font;
+  ftfont_info->ft_size = NULL;
+}
+
+static Lisp_Object
+ftbehbfont_open (struct frame *f, Lisp_Object entity, int pixel_size)
+{
+  Lisp_Object ft = ftfont_open (f, entity, pixel_size);
+  if (FONT_OBJECT_P (ft))
+    {
+      struct font_info *info = (struct font_info *) XFONT_OBJECT (ft);
+      ASET (ft, FONT_TYPE_INDEX, Qftbehb);
+      info->font.driver = &ftbehbfont_driver;
+      info->sc = be_make_ft_shape_cache ();
+    }
+  return ft;
+}
+#endif
+
+static void
+ftbefont_close (struct font *font)
+{
+  if (font_data_structures_may_be_ill_formed ())
+    return;
+  be_free_ft_shape_cache (((struct font_info *) font)->sc);
+  ftfont_close (font);
+}
+
+struct font_driver const ftbefont_driver =
+  {
+    .type = LISPSYM_INITIALLY (Qftbe),
+    .get_cache = ftfont_get_cache,
+    .list = ftbefont_list,
+    .match = ftbefont_match,
+    .draw = ftbefont_draw,
+    .list_family = ftfont_list_family,
+    .open_font = ftbefont_open,
+    .close_font = ftbefont_close,
+    .has_char = ftfont_has_char,
+    .encode_char = ftfont_encode_char,
+    .text_extents = ftfont_text_extents,
+    .get_bitmap = ftfont_get_bitmap,
+    .anchor_point = ftfont_anchor_point,
+#ifdef HAVE_LIBOTF
+    .otf_capability = ftfont_otf_capability,
+#endif
+#if defined HAVE_M17N_FLT && defined HAVE_LIBOTF
+    .shape = ftfont_shape,
+#endif
+#if defined HAVE_OTF_GET_VARIATION_GLYPHS || defined HAVE_FT_FACE_GETCHARVARIANTINDEX
+    .get_variation_glyphs = ftfont_variation_glyphs,
+#endif
+    .filter_properties = ftfont_filter_properties,
+    .combining_capability = ftfont_combining_capability
+  };
+
+#ifdef HAVE_HARFBUZZ
+struct font_driver ftbehbfont_driver;
+#endif
+
+static void
+syms_of_ftbefont_for_pdumper (void)
+{
+  register_font_driver (&ftbefont_driver, NULL);
+#ifdef HAVE_HARFBUZZ
+  ftbehbfont_driver = ftbefont_driver;
+  ftbehbfont_driver.type = Qftbehb;
+  ftbehbfont_driver.list = ftbehbfont_list;
+  ftbehbfont_driver.match = ftbehbfont_match;
+  ftbehbfont_driver.otf_capability = hbfont_otf_capability;
+  ftbehbfont_driver.shape = hbfont_shape;
+  ftbehbfont_driver.open_font = ftbehbfont_open;
+  ftbehbfont_driver.combining_capability = hbfont_combining_capability;
+  ftbehbfont_driver.begin_hb_font = ftbehbfont_begin_hb_font;
+  ftbehbfont_driver.end_hb_font = ftbehbfont_end_hb_font;
+
+  register_font_driver (&ftbehbfont_driver, NULL);
+#endif
+}
+
+void
+syms_of_ftbefont (void)
+{
+  DEFSYM (Qftbe, "ftbe");
+#ifdef HAVE_HARFBUZZ
+  DEFSYM (Qftbehb, "ftbehb");
+  Fput (Qftbe, Qfont_driver_superseded_by, Qftbehb);
+#endif
+  pdumper_do_now_and_after_load (syms_of_ftbefont_for_pdumper);
+}
diff --git a/src/ftfont.c b/src/ftfont.c
index 12d0d72d27..97117cf706 100644
--- a/src/ftfont.c
+++ b/src/ftfont.c
@@ -1336,6 +1336,9 @@ ftfont_open (struct frame *f, Lisp_Object entity, int pixel_size)
   ftfont_info = (struct font_info *) font;
   ftfont_info->ft_size = ft_face->size;
   ftfont_info->index = XFIXNUM (idx);
+#ifdef HAVE_HAIKU
+  ftfont_info->face = ft_face;
+#endif
 #ifdef HAVE_LIBOTF
   ftfont_info->maybe_otf = (ft_face->face_flags & FT_FACE_FLAG_SFNT) != 0;
   ftfont_info->otf = NULL;
@@ -1541,6 +1544,10 @@ ftfont_text_extents (struct font *font, const unsigned int *code,
 		     int nglyphs, struct font_metrics *metrics)
 {
   struct font_info *ftfont_info = (struct font_info *) font;
+#if defined (HAVE_HAIKU)
+  if (!ftfont_info->ft_size)
+    ftfont_info->ft_size = ftfont_info->face->size;
+#endif
   FT_Face ft_face = ftfont_info->ft_size->face;
   int i, width = 0;
   bool first;
@@ -3110,6 +3117,10 @@ syms_of_ftfont (void)
   Fput (Qfreetype, Qfont_driver_superseded_by, Qfreetypehb);
 #endif	/* HAVE_HARFBUZZ */
 
+#ifdef HAVE_HAIKU
+  DEFSYM (Qmono, "mono");
+#endif
+
   /* Fontconfig's generic families and their aliases.  */
   DEFSYM (Qmonospace, "monospace");
   DEFSYM (Qsans_serif, "sans-serif");
diff --git a/src/ftfont.h b/src/ftfont.h
index f771dc159b..5527247ba9 100644
--- a/src/ftfont.h
+++ b/src/ftfont.h
@@ -71,6 +71,9 @@ #define EMACS_FTFONT_H
   /* Font metrics cache.  */
   struct font_metrics **metrics;
   short metrics_nrows;
+#elif defined (HAVE_HAIKU)
+  FT_Face face;
+  void *sc;
 #else
   /* These are used by the XFT backend.  */
   Display *display;
diff --git a/src/haiku_draw_support.cc b/src/haiku_draw_support.cc
new file mode 100644
index 0000000000..b8141888db
--- /dev/null
+++ b/src/haiku_draw_support.cc
@@ -0,0 +1,553 @@
+/* Haiku window system support.  Hey, Emacs, this is -*- C++ -*-
+   Copyright (C) 2021 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 <View.h>
+#include <Region.h>
+#include <Font.h>
+#include <Window.h>
+#include <Bitmap.h>
+
+#include <cmath>
+
+#include "haiku_support.h"
+
+#define RGB_TO_UINT32(r, g, b) ((255 << 24) | ((r) << 16) | ((g) << 8) | (b))
+#define RED_FROM_ULONG(color)	(((color) >> 16) & 0xff)
+#define GREEN_FROM_ULONG(color)	(((color) >> 8) & 0xff)
+#define BLUE_FROM_ULONG(color)	((color) & 0xff)
+
+extern "C" void emacs_abort ();
+
+static void
+rgb32_to_rgb_color (uint32_t rgb, rgb_color *color)
+{
+  color->red = RED_FROM_ULONG (rgb);
+  color->green = GREEN_FROM_ULONG (rgb);
+  color->blue = BLUE_FROM_ULONG (rgb);
+  color->alpha = 255;
+}
+
+static BView *
+get_view (void *vw)
+{
+  BView *view = (BView *) find_appropriate_view_for_draw (vw);
+  return view;
+}
+
+void
+BView_StartClip (void *view)
+{
+  BView *vw = get_view (view);
+  BView_draw_lock (vw);
+  vw->PushState ();
+}
+
+void
+BView_EndClip (void *view)
+{
+  BView *vw = get_view (view);
+  vw->PopState ();
+  BView_draw_unlock (vw);
+}
+
+void
+BView_SetHighColor (void *view, uint32_t color)
+{
+  BView *vw = get_view (view);
+  rgb_color col;
+  rgb32_to_rgb_color (color, &col);
+
+  vw->SetHighColor (col);
+}
+
+void
+BView_SetLowColor (void *view, uint32_t color)
+{
+  BView *vw = get_view (view);
+  rgb_color col;
+  rgb32_to_rgb_color (color, &col);
+
+  vw->SetLowColor (col);
+}
+
+void
+BView_SetPenSize (void *view, int u)
+{
+  BView *vw = get_view (view);
+  vw->SetPenSize (u);
+}
+
+void
+BView_FillRectangle (void *view, int x, int y, int width, int height)
+{
+  BView *vw = get_view (view);
+  BRect rect = BRect (x, y, x + width - 1, y + height - 1);
+
+  vw->FillRect (rect);
+}
+
+void
+BView_FillRectangleAbs (void *view, int x, int y, int x1, int y1)
+{
+  BView *vw = get_view (view);
+  BRect rect = BRect (x, y, x1, y1);
+
+  vw->FillRect (rect);
+}
+
+void
+BView_StrokeRectangle (void *view, int x, int y, int width, int height)
+{
+  BView *vw = get_view (view);
+  BRect rect = BRect (x, y, x + width - 1, y + height - 1);
+
+  vw->StrokeRect (rect);
+}
+
+void
+BView_SetViewColor (void *view, uint32_t color)
+{
+  BView *vw = get_view (view);
+  rgb_color col;
+  rgb32_to_rgb_color (color, &col);
+
+  vw->SetViewColor (col);
+}
+
+void
+BView_ClipToRect (void *view, int x, int y, int width, int height)
+{
+  BView *vw = get_view (view);
+  BRect rect = BRect (x, y, x + width - 1, y + height - 1);
+
+  vw->ClipToRect (rect);
+}
+
+void
+BView_ClipToInverseRect (void *view, int x, int y, int width, int height)
+{
+  BView *vw = get_view (view);
+  BRect rect = BRect (x, y, x + width - 1, y + height - 1);
+
+  vw->ClipToInverseRect (rect);
+}
+
+void
+BView_StrokeLine (void *view, int sx, int sy, int tx, int ty)
+{
+  BView *vw = get_view (view);
+  BPoint from = BPoint (sx, sy);
+  BPoint to = BPoint (tx, ty);
+
+  vw->StrokeLine (from, to);
+}
+
+void
+BView_SetFont (void *view, void *font)
+{
+  BView *vw = get_view (view);
+
+  vw->SetFont ((BFont *) font);
+}
+
+void
+BView_MovePenTo (void *view, int x, int y)
+{
+  BView *vw = get_view (view);
+  BPoint pt = BPoint (x, y);
+
+  vw->MovePenTo (pt);
+}
+
+void
+BView_DrawString (void *view, const char *chr, ptrdiff_t len)
+{
+  BView *vw = get_view (view);
+
+  vw->DrawString (chr, len);
+}
+
+void
+BView_DrawChar (void *view, char chr)
+{
+  BView *vw = get_view (view);
+
+  vw->DrawChar (chr);
+}
+
+void
+BView_CopyBits (void *view, int x, int y, int width, int height,
+		int tox, int toy, int towidth, int toheight)
+{
+  BView *vw = get_view (view);
+
+  vw->CopyBits (BRect (x, y, x + width - 1, y + height - 1),
+		BRect (tox, toy, tox + towidth - 1, toy + toheight - 1));
+  vw->Sync ();
+}
+
+void
+rgb_color_hsl (uint32_t rgb, double *h, double *s, double *l)
+{
+  rgb_color col;
+  rgb32_to_rgb_color (rgb, &col);
+
+  double red = col.red / 255.0;
+  double green = col.green / 255.0;
+  double blue = col.blue / 255.0;
+
+  double max = std::fmax (std::fmax (red, blue), green);
+  double min = std::fmin (std::fmin (red, blue), green);
+  double delta = max - min;
+  *l = (max + min) / 2.0;
+
+  if (!delta)
+    {
+      *h = 0;
+      *s = 0;
+      return;
+    }
+
+  *s = (*l < 0.5) ? delta / (max + min) :
+    delta / (20 - max - min);
+  double rc = (max - red) / delta;
+  double gc = (max - green) / delta;
+  double bc = (max - blue) / delta;
+
+  if (red == max)
+    *h = bc - gc;
+  else if (green == max)
+    *h = 2.0 + rc + -bc;
+  else
+    *h = 4.0 + gc + -rc;
+  *h = std::fmod (*h / 6, 1.0);
+}
+
+static double
+hue_to_rgb (double v1, double v2, double h)
+{
+  if (h < 1 / 6)
+    return v1 + (v2 - v1) * h * 6.0;
+  else if (h < 0.5)
+    return v2;
+  else if (h < 2.0 / 3)
+    return v1 + (v2 - v1) * (2.0 / 3 - h) * 6.0;
+  return v1;
+}
+
+void
+hsl_color_rgb (double h, double s, double l, uint32_t *rgb)
+{
+  if (!s)
+    *rgb = RGB_TO_UINT32 (std::lrint (l * 255),
+			  std::lrint (l * 255),
+			  std::lrint (l * 255));
+  else
+    {
+      double m2 = l <= 0.5 ? l * (1 + s) : l + s - l * s;
+      double m1 = 2.0 * l - m2;
+
+      *rgb = RGB_TO_UINT32
+	(std::lrint (hue_to_rgb (m1, m2,
+				 std::fmod (h + 1 / 3.0, 1)) * 255),
+	 std::lrint (hue_to_rgb (m1, m2, h) * 255),
+	 std::lrint (hue_to_rgb (m1, m2,
+				 std::fmod (h - 1 / 3.0, 1)) * 255));
+    }
+}
+
+void
+BView_Draw (void *view, int x, int y, int width, int height)
+{
+  BView *vw = get_view (view);
+  if (vw->LockLooper ())
+    {
+      vw->Draw (BRect (x, y, x + width, y + height));
+      vw->UnlockLooper ();
+    }
+}
+
+void
+BView_DrawBitmap (void *view, void *bitmap, int x, int y,
+		  int width, int height, int vx, int vy, int vwidth,
+		  int vheight)
+{
+  BView *vw = get_view (view);
+  BBitmap *bm = (BBitmap *) bitmap;
+
+  vw->PushState ();
+  vw->SetDrawingMode (B_OP_OVER);
+  vw->DrawBitmap (bm, BRect (x, y, x + width - 1, y + height - 1),
+		  BRect (vx, vy, vx + vwidth - 1, vy + vheight - 1));
+  vw->PopState ();
+}
+
+void
+BView_DrawBitmapWithEraseOpAbs (void *view, void *bitmap, int x, int y,
+				int width, int height, int vx, int vy, int vwidth,
+				int vheight)
+{
+  BView *vw = get_view (view);
+  BBitmap *bm = (BBitmap *) bitmap;
+
+  drawing_mode dm = vw->DrawingMode ();
+  vw->SetDrawingMode (B_OP_ERASE);
+  vw->DrawBitmap (bm, BRect (x, y, x + width - 1, y + height - 1),
+		  BRect (vx, vy, vx + vwidth - 1, vy + vheight - 1));
+  vw->SetDrawingMode (dm);
+}
+
+void
+BView_BeginLayer (void *view, float opacity)
+{
+  BView *vw = get_view (view);
+  vw->BeginLayer (opacity * 255);
+}
+
+void
+BView_EndLayer (void *view)
+{
+  BView *vw = get_view (view);
+  vw->EndLayer ();
+}
+
+void
+BView_DrawBitmapWithEraseOp (void *view, void *bitmap, int x,
+			     int y, int width, int height)
+{
+  BView *vw = get_view (view);
+  BBitmap *bm = (BBitmap *) bitmap;
+
+  drawing_mode dm = vw->DrawingMode ();
+  vw->SetDrawingMode (B_OP_ERASE);
+  vw->DrawBitmap (bm, BRect (x, y, x + width - 1, y + height - 1));
+  vw->SetDrawingMode (dm);
+}
+
+void
+BView_BeginTransaction (void *view)
+{
+  BView *vw = get_view (view);
+  if (!vw->Window ()->LockLooper ())
+    emacs_abort ();
+  vw->Window ()->BeginViewTransaction ();
+  vw->Window ()->Sync ();
+  vw->Window ()->UnlockLooper ();
+}
+
+void
+BView_EndTransaction (void *view)
+{
+  BView *vw = get_view (view);
+  if (!vw->Window ()->LockLooper ())
+    emacs_abort ();
+  vw->Window ()->EndViewTransaction ();
+  vw->Window ()->Sync ();
+  vw->Window ()->UnlockLooper ();
+}
+
+/* Potential Haiku bug: the Be Book says B_OP_ERASE ought to treat
+   monochrome BBitmaps as patterns, but that isn't the case here, so
+   we have to do everything manually. */
+void
+BView_DrawFringeBitmap (void *src, void *view,
+			int x, int y, int width, int height,
+			uint32_t color)
+{
+  BBitmap *source = (BBitmap *) src;
+  BBitmap bm = BBitmap (source->Bounds (), B_RGBA32);
+  memset (bm.Bits (), 0, bm.BitsLength ());
+  for (int y = 0; y < source->Bounds ().Height (); ++y)
+    {
+      for (int x = 0; x < source->Bounds ().Width (); ++x)
+	{
+	  int byte = y * source->BytesPerRow () + x / 8;
+	  uint8_t *bits = (uint8_t *) source->Bits ();
+	  int bit = bits[byte] & (1 << (x % 8));
+
+	  uint32_t *row = (uint32_t *)
+	    &((uint8_t *) bm.Bits ())[y * bm.BytesPerRow ()];
+
+	  if (!bit)
+	    row[x] = color;
+	  else
+	    row[x] = 0;
+	}
+    }
+
+  BView *vw = get_view (view);
+  vw->SetDrawingMode (B_OP_OVER);
+  vw->DrawBitmap (&bm, BRect (x, y, x + width - 1, y + height - 1));
+}
+
+void
+BView_DrawMask (void *src, void *view,
+		int x, int y, int width, int height,
+		int vx, int vy, int vwidth, int vheight,
+		uint32_t color)
+{
+  BBitmap *source = (BBitmap *) src;
+  BBitmap bm = BBitmap (source->Bounds (), B_RGBA32);
+  for (int y = 0; y < bm.Bounds ().Height (); ++y)
+    {
+      for (int x = 0; x < bm.Bounds ().Width (); ++x)
+	{
+	  int bit = haiku_get_pixel ((void *) source, x, y);
+
+	  if (!bit)
+	    haiku_put_pixel ((void *) &bm, x, y, ((uint32_t) 255 << 24) | color);
+	  else
+	    haiku_put_pixel ((void *) &bm, x, y, 0);
+	}
+    }
+  BView *vw = get_view (view);
+  vw->SetDrawingMode (B_OP_OVER);
+  vw->DrawBitmap (&bm, BRect (x, y, x + width - 1, y + height - 1),
+		  BRect (vx, vy, vx + vwidth - 1, vy + vheight - 1));
+}
+
+void
+BView_DisableWindowUpdates (void *view)
+{
+  BView *vw = get_view (view);
+  vw->Window ()->DisableUpdates ();
+}
+
+void
+BView_EnableWindowUpdates (void *view)
+{
+  BView *vw = get_view (view);
+  vw->Window ()->EnableUpdates ();
+}
+
+static BBitmap *
+rotate_bitmap_270 (BBitmap *bmp)
+{
+  BRect r = bmp->Bounds ();
+  BBitmap *bm = new BBitmap (BRect (r.top, r.left, r.bottom, r.right),
+			     bmp->ColorSpace (), true);
+  int w = bmp->Bounds ().Width ();
+  int h = bmp->Bounds ().Height ();
+
+  for (int y = 0; y < h; ++y)
+    for (int x = 0; x < w; ++x)
+      haiku_put_pixel ((void *) bm, y, w - x - 1,
+		       haiku_get_pixel ((void *) bmp, x, y));
+
+  return bm;
+}
+
+static BBitmap *
+rotate_bitmap_90 (BBitmap *bmp)
+{
+  BRect r = bmp->Bounds ();
+  BBitmap *bm = new BBitmap (BRect (r.top, r.left, r.bottom, r.right),
+			     bmp->ColorSpace (), true);
+  int w = bmp->Bounds ().Width ();
+  int h = bmp->Bounds ().Height ();
+
+  for (int y = 0; y < h; ++y)
+    for (int x = 0; x < w; ++x)
+      haiku_put_pixel ((void *) bm, h - y - 1, x,
+		       haiku_get_pixel ((void *) bmp, x, y));
+
+  return bm;
+}
+
+void *
+BBitmap_transform_bitmap (void *bitmap, void *mask, uint32_t m_color,
+			  double rot, int desw, int desh)
+{
+  BBitmap *bm = (BBitmap *) bitmap;
+  BBitmap *mk = (BBitmap *) mask;
+  int copied_p = 0;
+
+  if (rot == 90)
+    {
+      copied_p = 1;
+      bm = rotate_bitmap_90 (bm);
+      if (mk)
+	mk = rotate_bitmap_90 (mk);
+    }
+
+  if (rot == 270)
+    {
+      copied_p = 1;
+      bm = rotate_bitmap_270 (bm);
+      if (mk)
+	mk = rotate_bitmap_270 (mk);
+    }
+
+  BRect r = bm->Bounds ();
+  if (r.Width () != desw || r.Height () != desh)
+    {
+      BRect n = BRect (0, 0, desw - 1, desh - 1);
+      BView vw (n, NULL, B_FOLLOW_NONE, 0);
+      BBitmap *dst = new BBitmap (n, bm->ColorSpace (), true);
+      dst->AddChild (&vw);
+
+      if (!vw.LockLooper ())
+	emacs_abort ();
+
+      if (rot != 90 && rot != 270)
+	{
+	  BAffineTransform tr;
+	  tr.RotateBy (BPoint (desw / 2, desh / 2), rot * M_PI / 180.0);
+	  vw.SetTransform (tr);
+	}
+
+      vw.MovePenTo (0, 0);
+      vw.DrawBitmap (bm, n);
+      if (mk)
+	BView_DrawMask ((void *) mk, (void *) &vw,
+			0, 0, mk->Bounds ().Width (),
+			mk->Bounds ().Height (),
+			0, 0, desw, desh, m_color);
+      vw.Sync ();
+      vw.RemoveSelf ();
+
+      if (copied_p)
+	delete bm;
+      if (copied_p && mk)
+	delete mk;
+      return dst;
+    }
+
+  return bm;
+}
+
+void
+BView_SetHighColorForVisibleBell (void *view, uint32_t color)
+{
+  BView *vw = (BView *) view;
+  rgb_color col;
+  rgb32_to_rgb_color (color, &col);
+
+  vw->SetHighColor (col);
+}
+
+void
+BView_FillRectangleForVisibleBell (void *view, int x, int y, int width, int height)
+{
+  BView *vw = (BView *) view;
+  BRect rect = BRect (x, y, x + width - 1, y + height - 1);
+
+  vw->FillRect (rect);
+}
diff --git a/src/haiku_font_support.cc b/src/haiku_font_support.cc
new file mode 100644
index 0000000000..c1a0c24229
--- /dev/null
+++ b/src/haiku_font_support.cc
@@ -0,0 +1,633 @@
+/* Haiku window system support.  Hey, Emacs, this is -*- C++ -*-
+   Copyright (C) 2021 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 <Font.h>
+#include <Rect.h>
+#include <AffineTransform.h>
+
+#include <cstring>
+#include <cmath>
+
+#include "haiku_support.h"
+
+/* Haiku doesn't expose font language data in BFont objects.  Thus, we
+   select a few representative characters for each supported `:lang'
+   (currently Chinese, Korean and Japanese,) and test for those
+   instead. */
+
+static uint32_t language_code_points[MAX_LANGUAGE][4] =
+  {{20154, 20754, 22996, 0}, /* Chinese. */
+   {51312, 49440, 44544, 0}, /* Korean. */
+   {26085, 26412, 12371, 0}, /* Japanese. */};
+
+static void
+estimate_font_ascii (BFont *font, int *max_width,
+		     int *min_width, int *avg_width)
+{
+  char ch[2];
+  bool tems[1];
+  int total = 0;
+  int count = 0;
+  int min = 0;
+  int max = 0;
+
+  std::memset (ch, 0, sizeof ch);
+  for (ch[0] = 1; ch[0] < 127; ++ch[0])
+    {
+      tems[0] = false;
+      font->GetHasGlyphs (ch, 1, tems);
+      if (tems[0])
+	{
+	  int w = font->StringWidth (ch);
+	  ++count;
+	  total += w;
+
+	  if (!min || min > w)
+	    min = w;
+	  if (max < w)
+	    max = w;
+	}
+    }
+
+  *min_width = min;
+  *max_width = max;
+  *avg_width = total / count;
+}
+
+void *
+BFont_open_default (double size, int plain_or_bold_or_fixed)
+{
+  BFont *ft;
+  if (!plain_or_bold_or_fixed)
+    ft = new BFont (be_fixed_font);
+  else if (plain_or_bold_or_fixed > 0)
+    ft = new BFont (be_plain_font);
+  else
+    ft = new BFont (be_bold_font);
+  ft->SetSize (size);
+  ft->SetEncoding (B_UNICODE_UTF8);
+  return ft;
+}
+
+void
+BFont_close (void *font)
+{
+  if (font != (void *) be_fixed_font &&
+      font != (void *) be_plain_font &&
+      font != (void *) be_bold_font)
+    delete (BFont *) font;
+}
+
+void
+BFont_dat (void *font, int *px_size, int *min_width, int *max_width,
+	   int *avg_width, int *height, int *space_width, int *ascent,
+	   int *descent, int *underline_position, int *underline_thickness)
+{
+  BFont *ft = (BFont *) font;
+  struct font_height fheight;
+  bool have_space_p;
+
+  char atem[1];
+  bool otem[1];
+
+  ft->GetHeight (&fheight);
+  atem[0] = ' ';
+  otem[0] = false;
+  ft->GetHasGlyphs (atem, 1, otem);
+  have_space_p = otem[0];
+
+  estimate_font_ascii (ft, max_width, min_width, avg_width);
+  *ascent = std::lrint (fheight.ascent);
+  *descent = std::lrint (fheight.descent);
+  *height = *ascent + *descent;
+
+  *space_width = have_space_p ? ft->StringWidth (" ") : 0;
+
+  *px_size = std::lrint (ft->Size ());
+  *underline_position = 0;
+  *underline_thickness = 0;
+}
+
+int
+BFont_have_char_p (void *font, int32_t chr)
+{
+  BFont *ft = (BFont *) font;
+  return ft->IncludesBlock (chr, chr);
+}
+
+void
+BFont_char_bounds (void *font, const char *mb_str, int *advance,
+		   int *lb, int *rb)
+{
+  BFont *ft = (BFont *) font;
+  edge_info edge_info;
+  font_height height;
+  float size, escapement;
+  size = ft->Size ();
+
+  ft->GetEdges (mb_str, 1, &edge_info);
+  ft->GetEscapements (mb_str, 1, &escapement);
+  ft->GetHeight (&height);
+  *advance = std::lrint (escapement * size);
+  *lb = std::lrint (edge_info.left * size);
+  *rb = *advance + std::lrint (edge_info.right * size);
+}
+
+void
+BFont_nchar_bounds (void *font, const char *mb_str, int *advance,
+		    int *lb, int *rb, int32_t n)
+{
+  BFont *ft = (BFont *) font;
+  edge_info edge_info[n];
+  float size;
+  float escapement[n];
+
+  size = ft->Size ();
+
+  ft->GetEdges (mb_str, n, edge_info);
+  ft->GetEscapements (mb_str, n, (float *) escapement);
+
+  for (int32_t i = 0; i < n; ++i)
+    {
+      advance[i] = std::lrint (escapement[i] * size);
+      lb[i] = advance[i] - std::lrint (edge_info[i].left * size);
+      rb[i] = advance[i] + std::lrint (edge_info[i].right * size);
+    }
+}
+
+void *
+BFont_new (void)
+{
+  return new BFont ();
+}
+
+int
+BFont_family_has_style_p (haiku_font_family_or_style family,
+			  haiku_font_family_or_style style)
+{
+  int sc = count_font_styles (family);
+  for (int idx = 0; idx < sc; ++idx)
+    {
+      font_style s;
+      if (get_font_style (family, idx, &s) == B_OK)
+	{
+	  if (!strcmp (s, style))
+	    return 1;
+	}
+    }
+  return 0;
+}
+
+int
+BFont_family (int idx, char *f, int *fixed_p)
+{
+  uint32 flags;
+  if (get_font_family (idx, (char (*)[64]) f, &flags) != B_OK)
+    return 1;
+  *fixed_p = flags & B_IS_FIXED;
+  return 0;
+}
+
+int
+BFont_set_family_and_style (void *font, haiku_font_family_or_style family,
+			    haiku_font_family_or_style style)
+{
+  BFont *ft = (BFont *) font;
+  return ft->SetFamilyAndStyle (family, style) != B_OK;
+}
+
+int
+BFont_set_family_face (void *font, haiku_font_family_or_style family,
+		       int italic_p, int bold_p)
+{
+  BFont *ft = (BFont *) font;
+  return ft->SetFamilyAndFace (family, (italic_p ? B_ITALIC_FACE : 0) |
+			       (bold_p == HAIKU_BOLD ? B_BOLD_FACE : 0));
+
+}
+
+static void
+font_style_to_flags (char *st, struct haiku_font_pattern *pattern)
+{
+  char *style = strdup (st);
+  char *token;
+  pattern->weight = -1;
+  pattern->width = NO_WIDTH;
+  pattern->slant = NO_SLANT;
+  int tok = 0;
+
+  while ((token = std::strtok (!tok ? style : NULL, " ")) && tok < 3)
+    {
+      if (token && !strcmp (token, "Thin"))
+	pattern->weight = HAIKU_THIN;
+      else if (token && !strcmp (token, "UltraLight"))
+	pattern->weight = HAIKU_ULTRALIGHT;
+      else if (token && !strcmp (token, "ExtraLight"))
+	pattern->weight = HAIKU_EXTRALIGHT;
+      else if (token && !strcmp (token, "Light"))
+	pattern->weight = HAIKU_LIGHT;
+      else if (token && !strcmp (token, "SemiLight"))
+	pattern->weight = HAIKU_SEMI_LIGHT;
+      else if (token && !strcmp (token, "Regular"))
+	{
+	  if (pattern->slant == NO_SLANT)
+	    pattern->slant = SLANT_REGULAR;
+
+	  if (pattern->width == NO_WIDTH)
+	    pattern->width = NORMAL_WIDTH;
+
+	  if (pattern->weight == -1)
+	    pattern->weight = HAIKU_REGULAR;
+	}
+      else if (token && !strcmp (token, "SemiBold"))
+	pattern->weight = HAIKU_SEMI_BOLD;
+      else if (token && !strcmp (token, "Bold"))
+	pattern->weight = HAIKU_BOLD;
+      else if (token && (!strcmp (token, "ExtraBold") ||
+			 /* This has actually been seen in the wild. */
+			 !strcmp (token, "Extrabold")))
+	pattern->weight = HAIKU_EXTRA_BOLD;
+      else if (token && !strcmp (token, "UltraBold"))
+	pattern->weight = HAIKU_ULTRA_BOLD;
+      else if (token && !strcmp (token, "Oblique"))
+	pattern->slant = SLANT_OBLIQUE;
+      else if (token && !strcmp (token, "Italic"))
+	pattern->slant = SLANT_ITALIC;
+      else if (token && !strcmp (token, "UltraCondensed"))
+	pattern->width = ULTRA_CONDENSED;
+      else if (token && !strcmp (token, "ExtraCondensed"))
+	pattern->width = EXTRA_CONDENSED;
+      else if (token && !strcmp (token, "Condensed"))
+	pattern->width = CONDENSED;
+      else if (token && !strcmp (token, "SemiCondensed"))
+	pattern->width = SEMI_CONDENSED;
+      else if (token && !strcmp (token, "SemiExpanded"))
+	pattern->width = SEMI_EXPANDED;
+      else if (token && !strcmp (token, "Expanded"))
+	pattern->width = EXPANDED;
+      else if (token && !strcmp (token, "ExtraExpanded"))
+	pattern->width = EXTRA_EXPANDED;
+      else if (token && !strcmp (token, "UltraExpanded"))
+	pattern->width = ULTRA_EXPANDED;
+      else
+	{
+	  tok = 1000;
+	  break;
+	}
+      tok++;
+    }
+
+  if (pattern->weight != -1)
+    pattern->specified |= FSPEC_WEIGHT;
+  if (pattern->slant != NO_SLANT)
+    pattern->specified |= FSPEC_SLANT;
+  if (pattern->width != NO_WIDTH)
+    pattern->specified |= FSPEC_WIDTH;
+
+  if (tok > 3)
+    {
+      pattern->specified &= ~FSPEC_SLANT;
+      pattern->specified &= ~FSPEC_WEIGHT;
+      pattern->specified &= ~FSPEC_WIDTH;
+      pattern->specified |= FSPEC_STYLE;
+      std::strncpy ((char *) &pattern->style, st,
+		    sizeof pattern->style - 1);
+    }
+
+  free (style);
+}
+
+static bool
+font_check_wanted_chars (struct haiku_font_pattern *pattern, font_family family,
+			 char *style)
+{
+  BFont ft;
+
+  if (ft.SetFamilyAndStyle (family, style) != B_OK)
+    return false;
+
+  for (int i = 0; i < pattern->want_chars_len; ++i)
+    if (!ft.IncludesBlock (pattern->wanted_chars[i],
+			   pattern->wanted_chars[i]))
+      return false;
+
+  return true;
+}
+
+static bool
+font_check_one_of (struct haiku_font_pattern *pattern, font_family family,
+		   char *style)
+{
+  BFont ft;
+
+  if (ft.SetFamilyAndStyle (family, style) != B_OK)
+    return false;
+
+  for (int i = 0; i < pattern->need_one_of_len; ++i)
+    if (ft.IncludesBlock (pattern->need_one_of[i],
+			  pattern->need_one_of[i]))
+      return true;
+
+  return false;
+}
+
+static bool
+font_check_language (struct haiku_font_pattern *pattern, font_family family,
+		     char *style)
+{
+  BFont ft;
+
+  if (ft.SetFamilyAndStyle (family, style) != B_OK)
+    return false;
+
+  if (pattern->language == MAX_LANGUAGE)
+    return false;
+
+  for (uint32_t *ch = (uint32_t *)
+	 &language_code_points[pattern->language]; *ch; ch++)
+    if (!ft.IncludesBlock (*ch, *ch))
+      return false;
+
+  return true;
+}
+
+static bool
+font_family_style_matches_p (font_family family, char *style, uint32_t flags,
+			     struct haiku_font_pattern *pattern,
+			     int ignore_flags_p = 0)
+{
+  struct haiku_font_pattern m;
+  m.specified = 0;
+
+  if (style)
+    font_style_to_flags (style, &m);
+
+  if ((pattern->specified & FSPEC_FAMILY) &&
+      strcmp ((char *) &pattern->family, family))
+    return false;
+
+  if (!ignore_flags_p && (pattern->specified & FSPEC_SPACING) &&
+      !(pattern->mono_spacing_p) != !(flags & B_IS_FIXED))
+    return false;
+
+  if (pattern->specified & FSPEC_STYLE)
+    return style && !strcmp (style, pattern->style);
+
+  if ((pattern->specified & FSPEC_WEIGHT) &&
+      pattern->weight != ((m.specified & FSPEC_WEIGHT) ? m.weight : HAIKU_REGULAR))
+    return false;
+
+  if ((pattern->specified & FSPEC_SLANT) &&
+      pattern->slant != ((m.specified & FSPEC_SLANT) ? m.slant : SLANT_REGULAR))
+    return false;
+
+  if ((pattern->specified & FSPEC_WANTED) &&
+      !font_check_wanted_chars (pattern, family, style))
+    return false;
+
+  if ((pattern->specified & FSPEC_WIDTH) &&
+      pattern->width != ((m.specified & FSPEC_WIDTH) ? m.width : NORMAL_WIDTH))
+    return false;
+
+  if ((pattern->specified & FSPEC_NEED_ONE_OF) &&
+      !font_check_one_of (pattern, family, style))
+    return false;
+
+  if ((pattern->specified & FSPEC_LANGUAGE) &&
+      !font_check_language (pattern, family, style))
+    return false;
+
+  return true;
+}
+
+static void
+haiku_font_fill_pattern (struct haiku_font_pattern *pattern,
+			 font_family family, char *style,
+			 uint32_t flags)
+{
+  if (style)
+    font_style_to_flags (style, pattern);
+
+  pattern->specified |= FSPEC_FAMILY;
+  std::strncpy (pattern->family, family,
+		sizeof pattern->family - 1);
+  pattern->specified |= FSPEC_SPACING;
+  pattern->mono_spacing_p = flags & B_IS_FIXED;
+}
+
+void
+haiku_font_pattern_free (struct haiku_font_pattern *pt)
+{
+  struct haiku_font_pattern *tem = pt;
+  while (tem)
+    {
+      struct haiku_font_pattern *t = tem;
+      tem = t->next;
+      delete t;
+    }
+}
+
+struct haiku_font_pattern *
+BFont_find (struct haiku_font_pattern *pt)
+{
+  struct haiku_font_pattern *r = NULL;
+  font_family name;
+  font_style sname;
+  uint32 flags;
+  int sty_count;
+  int fam_count = count_font_families ();
+
+  for (int fi = 0; fi < fam_count; ++fi)
+    {
+      if (get_font_family (fi, &name, &flags) == B_OK)
+	{
+	  sty_count = count_font_styles (name);
+	  if (!sty_count &&
+	      font_family_style_matches_p (name, NULL, flags, pt))
+	    {
+	      struct haiku_font_pattern *p = new struct haiku_font_pattern;
+	      p->specified = 0;
+	      p->oblique_seen_p = 1;
+	      haiku_font_fill_pattern (p, name, NULL, flags);
+	      p->next = r;
+	      if (p->next)
+		p->next->last = p;
+	      p->last = NULL;
+	      p->next_family = r;
+	      r = p;
+	    }
+	  else if (sty_count)
+	    {
+	      for (int si = 0; si < sty_count; ++si)
+		{
+		  int oblique_seen_p = 0;
+		  struct haiku_font_pattern *head = r;
+		  struct haiku_font_pattern *p = NULL;
+
+		  if (get_font_style (name, si, &sname, &flags) == B_OK)
+		    {
+		      if (font_family_style_matches_p (name, (char *) &sname, flags, pt))
+			{
+			  p = new struct haiku_font_pattern;
+			  p->specified = 0;
+			  haiku_font_fill_pattern (p, name, (char *) &sname, flags);
+			  if (p->specified & FSPEC_SLANT &&
+			      ((p->slant == SLANT_OBLIQUE) || (p->slant == SLANT_ITALIC)))
+			    oblique_seen_p = 1;
+
+			  p->next = r;
+			  if (p->next)
+			    p->next->last = p;
+			  r = p;
+			  p->next_family = head;
+			}
+		    }
+
+		  if (p)
+		    p->last = NULL;
+
+		  for (; head; head = head->last)
+		    {
+		      head->oblique_seen_p = oblique_seen_p;
+		    }
+		}
+	    }
+	}
+    }
+
+  /* There's a very good chance that this result will get cached if no
+     slant is specified.  Thus, we look through each font that hasn't
+     seen an oblique style, and add one. */
+
+  if (!(pt->specified & FSPEC_SLANT))
+    {
+      /* r->last is invalid from here onwards. */
+      for (struct haiku_font_pattern *p = r; p;)
+	{
+	  if (!p->oblique_seen_p)
+	    {
+	      struct haiku_font_pattern *n = new haiku_font_pattern;
+	      *n = *p;
+	      n->slant = SLANT_OBLIQUE;
+	      p->next = n;
+	      p = p->next_family;
+	    }
+	  else
+	    p = p->next_family;
+	}
+    }
+
+  return r;
+}
+
+int
+BFont_open_pattern (struct haiku_font_pattern *pat, void **font, float size)
+{
+  int sty_count;
+  font_family name;
+  font_style sname;
+  uint32 flags = 0;
+  if (!(pat->specified & FSPEC_FAMILY))
+    return 1;
+  strncpy (name, pat->family, sizeof name - 1);
+  sty_count = count_font_styles (name);
+
+  if (!sty_count &&
+      font_family_style_matches_p (name, NULL, flags, pat, 1))
+    {
+      BFont *ft = new BFont;
+      if (ft->SetFamilyAndStyle (name, NULL) != B_OK)
+	{
+	  delete ft;
+	  return 1;
+	}
+      ft->SetSize (size);
+      ft->SetEncoding (B_UNICODE_UTF8);
+      ft->SetSpacing (B_BITMAP_SPACING);
+      *font = (void *) ft;
+      return 0;
+    }
+  else if (sty_count)
+    {
+      for (int si = 0; si < sty_count; ++si)
+	{
+	  if (get_font_style (name, si, &sname, &flags) == B_OK &&
+	      font_family_style_matches_p (name, (char *) &sname, flags, pat))
+	    {
+	      BFont *ft = new BFont;
+	      if (ft->SetFamilyAndStyle (name, sname) != B_OK)
+		{
+		  delete ft;
+		  return 1;
+		}
+	      ft->SetSize (size);
+	      ft->SetEncoding (B_UNICODE_UTF8);
+	      ft->SetSpacing (B_BITMAP_SPACING);
+	      *font = (void *) ft;
+	      return 0;
+	    }
+	}
+    }
+
+  if (pat->specified & FSPEC_SLANT && pat->slant == SLANT_OBLIQUE)
+    {
+      struct haiku_font_pattern copy = *pat;
+      copy.slant = SLANT_REGULAR;
+      int code = BFont_open_pattern (&copy, font, size);
+      if (code)
+	return code;
+      BFont *ft = (BFont *) *font;
+      /* XXX Font measurements don't respect shear.  Haiku bug?
+	 This apparently worked in BeOS.
+	 ft->SetShear (100.0); */
+      ft->SetFace (B_ITALIC_FACE);
+      return 0;
+    }
+
+  return 1;
+}
+
+void
+BFont_populate_fixed_family (struct haiku_font_pattern *ptn)
+{
+  font_family f;
+  font_style s;
+  be_fixed_font->GetFamilyAndStyle (&f, &s);
+
+  ptn->specified |= FSPEC_FAMILY;
+  strncpy (ptn->family, f, sizeof ptn->family - 1);
+}
+
+void
+BFont_populate_plain_family (struct haiku_font_pattern *ptn)
+{
+  font_family f;
+  font_style s;
+  be_plain_font->GetFamilyAndStyle (&f, &s);
+
+  ptn->specified |= FSPEC_FAMILY;
+  strncpy (ptn->family, f, sizeof ptn->family - 1);
+}
+
+int
+BFont_string_width (void *font, const char *utf8)
+{
+  return ((BFont *) font)->StringWidth (utf8);
+}
diff --git a/src/haiku_freetype.cc b/src/haiku_freetype.cc
new file mode 100644
index 0000000000..c6382574f2
--- /dev/null
+++ b/src/haiku_freetype.cc
@@ -0,0 +1,201 @@
+/* FreeType font driver for Haiku
+   Copyright (C) 2021 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 "haiku_support.h"
+
+#include <interface/View.h>
+#include <interface/Bitmap.h>
+#include <interface/Point.h>
+#include <interface/Region.h>
+#include <interface/Shape.h>
+
+#include <alloca.h>
+#include <cstddef>
+#include <cmath>
+
+#include <algorithm>
+#include <unordered_map>
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_SIZES_H
+#include FT_BITMAP_H
+#include FT_IMAGE_H
+#include FT_OUTLINE_H
+
+#define FLOAT_FROM_26_6(t) ((float) (t) / 64.0f)
+#define DOUBLE_TO_16_16(d) ((FT_Fixed) ((d) * 65536.0))
+
+#define VECTOR_POINT(v) (BPoint (FLOAT_FROM_26_6 ((v)->x),	\
+				 FLOAT_FROM_26_6 ((v)->y)))
+
+static int tx;
+static int mty;
+
+struct ftbe_shape_cache
+{
+  std::unordered_map <unsigned int, BShape *> shapes;
+};
+
+static int
+move_to (FT_Vector *to, void *closure)
+{
+  BShape *shape = (BShape *) closure;
+  return shape->MoveTo (VECTOR_POINT (to)) != B_OK;
+}
+
+static int
+line_to (FT_Vector *to, void *closure)
+{
+  BShape *shape = (BShape *) closure;
+  return shape->LineTo (VECTOR_POINT (to)) != B_OK;
+}
+
+static int
+conic_to (FT_Vector *control, FT_Vector *to, void *closure)
+{
+  BShape *shape = (BShape *) closure;
+  BPoint pt_0 = shape->CurrentPosition ();
+
+  return shape->BezierTo
+    (BPoint (pt_0.x + 2.0f / 3.0f * (FLOAT_FROM_26_6 (control->x) - pt_0.x),
+	     pt_0.y + 2.0f / 3.0f * (FLOAT_FROM_26_6 (control->y) - pt_0.y)),
+     BPoint (FLOAT_FROM_26_6 (to->x) + 2.0f / 3.0f * (FLOAT_FROM_26_6 (control->x) -
+						      FLOAT_FROM_26_6 (to->x)),
+	     FLOAT_FROM_26_6 (to->y) + 2.0f / 3.0f * (FLOAT_FROM_26_6 (control->y) -
+						      FLOAT_FROM_26_6 (to->y))),
+     VECTOR_POINT (to)) != B_OK;
+}
+
+static int
+cubic_to (FT_Vector *control1, FT_Vector *control2,
+	  FT_Vector *to, void *closure)
+{
+  BShape *shape = (BShape *) closure;
+  return shape->BezierTo (VECTOR_POINT (control1),
+			  VECTOR_POINT (control2),
+			  VECTOR_POINT (to)) != B_OK;
+}
+
+static int
+digest_outline (FT_Outline *outline, FT_Face face, BShape *shape)
+{
+  static const FT_Matrix invert_y =
+    {
+      DOUBLE_TO_16_16 (1.0), 0,
+      0, DOUBLE_TO_16_16 (-1.0),
+    };
+  static const FT_Outline_Funcs outline_funcs =
+    {
+      (FT_Outline_MoveToFunc) move_to,
+      (FT_Outline_LineToFunc) line_to,
+      (FT_Outline_ConicToFunc) conic_to,
+      (FT_Outline_CubicToFunc) cubic_to,
+      0, 0
+    };
+
+  FT_Outline_Translate (outline, tx * 64.0, -mty * 64.0);
+  FT_Outline_Transform (outline, &invert_y);
+
+  if (FT_Outline_Decompose (outline, &outline_funcs, shape) != FT_Err_Ok)
+    return 1;
+
+  if (shape->Close () != B_OK)
+    return 1;
+
+  return 0;
+}
+
+extern "C"
+{
+  extern void emacs_abort (void);
+}
+
+extern void
+be_free_ft_shape_cache (void *c)
+{
+  for (const std::pair <unsigned int, BShape *> &pair :
+	 ((struct ftbe_shape_cache *) c)->shapes)
+    delete pair.second;
+  delete (struct ftbe_shape_cache *) c;
+}
+
+extern void *
+be_make_ft_shape_cache (void)
+{
+  return new struct ftbe_shape_cache;
+}
+
+extern void
+BView_FT_render_glyphs (FT_Face face, unsigned int *codes,
+			int len, uint32_t color, int x, int y,
+			void *view, void *ft_shape_cache)
+{
+  FT_Error error = FT_Err_Ok;
+  BView *vw = (BView *) find_appropriate_view_for_draw (view);
+  struct ftbe_shape_cache *c = (struct ftbe_shape_cache *) ft_shape_cache;
+  BShape gshape;
+  gshape.Clear ();
+
+  tx = 0, mty = 0;
+
+  if (len >= 5)
+    {
+      vw->MovePenTo (x, y);
+      x = 0, y = 0;
+    }
+
+  BView_SetHighColor (view, color);
+
+  for (int i = 0; i < len; ++i)
+    {
+      error = FT_Load_Glyph (face, codes[i], FT_LOAD_DEFAULT);
+
+      if (error != FT_Err_Ok)
+	continue;
+      BShape *shape;
+      if (len < 5)
+	shape = c->shapes[codes[i]];
+      if (len >= 5)
+	tx = x, mty = y;
+      if (face->glyph->format == FT_GLYPH_FORMAT_OUTLINE && (!shape || len >= 5))
+	{
+	  if (len < 5)
+	    {
+	      shape = new BShape ();
+	      shape->Clear ();
+	    }
+	  digest_outline (&face->glyph->outline, face,
+			  len >= 5 ? &gshape : shape);
+	  if (len < 5)
+	    c->shapes[codes[i]] = shape;
+	}
+      if (len < 5)
+	{
+	  vw->MovePenTo (x, y);
+	  vw->FillShape (shape);
+	}
+      x += face->glyph->advance.x >> 6;
+      y += face->glyph->advance.y >> 6;
+    }
+
+  if (len >= 5)
+    vw->FillShape (&gshape);
+}
diff --git a/src/haiku_io.c b/src/haiku_io.c
new file mode 100644
index 0000000000..e523999b5f
--- /dev/null
+++ b/src/haiku_io.c
@@ -0,0 +1,192 @@
+/* Haiku window system support.
+   Copyright (C) 2021 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 <signal.h>
+#include <stdio.h>
+#include <pthread.h>
+#include <unistd.h>
+
+#include <OS.h>
+
+#include "haiku_support.h"
+#include "lisp.h"
+#include "haikuterm.h"
+#include "blockinput.h"
+
+#define PORT_CAP 1200
+
+port_id port_application_to_emacs;
+
+void
+haiku_io_init (void)
+{
+  port_application_to_emacs = create_port (PORT_CAP, "application emacs port");
+}
+
+static ssize_t
+haiku_len (enum haiku_event_type type)
+{
+  switch (type)
+    {
+    case QUIT_REQUESTED:
+      return sizeof (struct haiku_quit_requested_event);
+    case FRAME_RESIZED:
+      return sizeof (struct haiku_resize_event);
+    case FRAME_EXPOSED:
+      return sizeof (struct haiku_expose_event);
+    case KEY_DOWN:
+    case KEY_UP:
+      return sizeof (struct haiku_key_event);
+    case ACTIVATION:
+      return sizeof (struct haiku_activation_event);
+    case MOUSE_MOTION:
+      return sizeof (struct haiku_mouse_motion_event);
+    case BUTTON_DOWN:
+    case BUTTON_UP:
+      return sizeof (struct haiku_button_event);
+    case ICONIFICATION:
+      return sizeof (struct haiku_iconification_event);
+    case MOVE_EVENT:
+      return sizeof (struct haiku_move_event);
+    case SCROLL_BAR_VALUE_EVENT:
+      return sizeof (struct haiku_scroll_bar_value_event);
+    case SCROLL_BAR_DRAG_EVENT:
+      return sizeof (struct haiku_scroll_bar_drag_event);
+    case WHEEL_MOVE_EVENT:
+      return sizeof (struct haiku_wheel_move_event);
+    case MENU_BAR_RESIZE:
+      return sizeof (struct haiku_menu_bar_resize_event);
+    case MENU_BAR_OPEN:
+    case MENU_BAR_CLOSE:
+      return sizeof (struct haiku_menu_bar_state_event);
+    case MENU_BAR_SELECT_EVENT:
+      return sizeof (struct haiku_menu_bar_select_event);
+    case FILE_PANEL_EVENT:
+      return sizeof (struct haiku_file_panel_event);
+    case MENU_BAR_HELP_EVENT:
+      return sizeof (struct haiku_menu_bar_help_event);
+    case ZOOM_EVENT:
+      return sizeof (struct haiku_zoom_event);
+    }
+
+  emacs_abort ();
+}
+
+void
+haiku_read_size (ssize_t *len)
+{
+  port_id from = port_application_to_emacs;
+  ssize_t size;
+
+  size = port_buffer_size_etc (from, B_TIMEOUT, 0);
+
+  if (size < B_OK)
+    *len = -1;
+  else
+    *len = size;
+}
+
+int
+haiku_read (enum haiku_event_type *type, void *buf, ssize_t len)
+{
+  int32 typ;
+  port_id from = port_application_to_emacs;
+
+  if (read_port (from, &typ, buf, len) < B_OK)
+    return -1;
+
+  *type = (enum haiku_event_type) typ;
+  eassert (len >= haiku_len (typ));
+  return 0;
+}
+
+int
+haiku_read_with_timeout (enum haiku_event_type *type, void *buf, ssize_t len,
+			 time_t timeout)
+{
+  int32 typ;
+  port_id from = port_application_to_emacs;
+
+  block_input ();
+  if (read_port_etc (from, &typ, buf, len,
+		     B_TIMEOUT, (bigtime_t) timeout) < B_OK)
+    {
+      unblock_input ();
+      return -1;
+    }
+  unblock_input ();
+  *type = (enum haiku_event_type) typ;
+  eassert (len >= haiku_len (typ));
+  return 0;
+}
+
+int
+haiku_write (enum haiku_event_type type, void *buf)
+{
+  port_id to = port_application_to_emacs;
+
+  if (write_port (to, (int32_t) type, buf, haiku_len (type)) < B_OK)
+    return -1;
+
+  kill (getpid (), SIGPOLL);
+
+  return 0;
+}
+
+int
+haiku_write_without_signal (enum haiku_event_type type, void *buf)
+{
+  port_id to = port_application_to_emacs;
+
+  if (write_port (to, (int32_t) type, buf, haiku_len (type)) < B_OK)
+    return -1;
+
+  return 0;
+}
+
+void
+haiku_io_init_in_app_thread (void)
+{
+  sigset_t set;
+  sigemptyset (&set);
+  sigaddset (&set, SIGUSR2);
+  sigaddset (&set, SIGUSR1);
+
+  if (pthread_sigmask (SIG_BLOCK, &set, NULL))
+    perror ("pthread_sigmask");
+}
+
+void
+record_c_unwind_protect_from_cxx (void (*fn) (void *), void *r)
+{
+  record_unwind_protect_ptr (fn, r);
+}
+
+ptrdiff_t
+c_specpdl_idx_from_cxx (void)
+{
+  return SPECPDL_INDEX ();
+}
+
+void
+c_unbind_to_nil_from_cxx (ptrdiff_t idx)
+{
+  unbind_to (idx, Qnil);
+}
diff --git a/src/haiku_select.cc b/src/haiku_select.cc
new file mode 100644
index 0000000000..8d345ca661
--- /dev/null
+++ b/src/haiku_select.cc
@@ -0,0 +1,155 @@
+/* Haiku window system selection support. Hey Emacs, this is -*- C++ -*-
+   Copyright (C) 2021 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 <Clipboard.h>
+
+#include <cstdlib>
+#include <cstring>
+
+#include "haikuselect.h"
+
+
+static BClipboard *primary = NULL;
+static BClipboard *secondary = NULL;
+static BClipboard *system_clipboard = NULL;
+
+int selection_state_flag;
+
+static char *
+BClipboard_find_data (BClipboard *cb, const char *type, ssize_t *len)
+{
+  if (!cb->Lock ())
+    return 0;
+
+  BMessage *dat = cb->Data ();
+  if (!dat)
+    {
+      cb->Unlock ();
+      return 0;
+    }
+
+  const char *ptr;
+  ssize_t bt;
+  dat->FindData (type, B_MIME_TYPE, (const void **) &ptr, &bt);
+
+  if (!ptr)
+    {
+      cb->Unlock ();
+      return NULL;
+    }
+
+  if (len)
+    *len = bt;
+
+  cb->Unlock ();
+
+  return strndup (ptr, bt);
+}
+
+static void
+BClipboard_set_data (BClipboard *cb, const char *type, const char *dat,
+		     ssize_t len)
+{
+  if (!cb->Lock ())
+    return;
+  cb->Clear ();
+  BMessage *mdat = cb->Data ();
+  if (!mdat)
+    {
+      cb->Unlock ();
+      return;
+    }
+
+  if (dat)
+    mdat->AddData (type, B_MIME_TYPE, dat, len);
+  cb->Commit ();
+  cb->Unlock ();
+}
+
+char *
+BClipboard_find_system_data (const char *type, ssize_t *len)
+{
+  if (!system_clipboard)
+    return 0;
+
+  return BClipboard_find_data (system_clipboard, type, len);
+}
+
+char *
+BClipboard_find_primary_selection_data (const char *type, ssize_t *len)
+{
+  if (!primary)
+    return 0;
+
+  return BClipboard_find_data (primary, type, len);
+}
+
+char *
+BClipboard_find_secondary_selection_data (const char *type, ssize_t *len)
+{
+  if (!secondary)
+    return 0;
+
+  return BClipboard_find_data (secondary, type, len);
+}
+
+void
+BClipboard_set_system_data (const char *type, const char *data,
+			    ssize_t len)
+{
+  if (!system_clipboard)
+    return;
+
+  BClipboard_set_data (system_clipboard, type, data, len);
+}
+
+void
+BClipboard_set_primary_selection_data (const char *type, const char *data,
+				       ssize_t len)
+{
+  if (!primary)
+    return;
+
+  BClipboard_set_data (primary, type, data, len);
+}
+
+void
+BClipboard_set_secondary_selection_data (const char *type, const char *data,
+					 ssize_t len)
+{
+  if (!secondary)
+    return;
+
+  BClipboard_set_data (secondary, type, data, len);
+}
+
+void
+BClipboard_free_data (void *ptr)
+{
+  std::free (ptr);
+}
+
+void
+init_haiku_select (void)
+{
+  system_clipboard = new BClipboard ("system");
+  primary = new BClipboard ("primary");
+  secondary = new BClipboard ("secondary");
+}
diff --git a/src/haiku_simx.c b/src/haiku_simx.c
new file mode 100644
index 0000000000..bd1d6c88f0
--- /dev/null
+++ b/src/haiku_simx.c
@@ -0,0 +1,185 @@
+/* Utilities and defines for using XPM without X on Haiku
+
+Copyright (C) 2021 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 <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "dispextern.h"
+#include "haiku_support.h"
+#define FOR_MSW
+#include "xpm_be.h"
+#undef FOR_MSW
+
+/* Defined in haikufns.c. */
+extern int haiku_get_color (const char *name, Emacs_Color *color);
+extern unsigned long haiku_get_pixel (haiku bitmap, int x, int y);
+extern void haiku_put_pixel (haiku bitmap, int x, int y, unsigned long pixel);
+
+Visual *
+XDefaultVisual (Display *dpy, Screen *screen)
+{
+  return NULL;
+}
+
+Screen *
+XDefaultScreen (Display *d)
+{
+  return NULL;
+}
+
+int
+XDefaultDepth (Display *display, Screen *screen)
+{
+  return 24;
+}
+
+Colormap *
+XDefaultColormap (Display *display, Screen *screen)
+{
+  return NULL;
+}
+
+int
+XParseColor (Display *d, Colormap *cmap, char *name, XColor *color)
+{
+  int r, g, b;
+  int okay;
+
+  if (name == NULL)
+    return (0);
+
+  Emacs_Color ecol;
+
+  okay = !haiku_get_color (name, &ecol);
+  if (okay)
+    {
+      r = ecol.red / 256;
+      g = ecol.green / 256;
+      b = ecol.blue / 256;
+    }
+
+  if (okay)
+    {
+      color->red = (uint8_t) r;
+      color->green = (uint8_t) g;
+      color->blue = (uint8_t) b;
+      color->pixel = ((color->red << 16) |
+		      (color->green << 8) |
+		      color->blue);
+      return 1;
+    }
+  else
+    return 0;
+}
+
+int
+XAllocColor (Display *d, Colormap cmap, XColor *color)
+{
+  return 1;
+}
+
+void
+XQueryColors (Display *display, Colormap *colormap,
+	      XColor *xcolors, int ncolors)
+{
+  for (int i = 0; i < ncolors; ++i)
+    {
+      xcolors[i].red = xcolors[i].pixel >> 16 & 0xff;
+      xcolors[i].green = xcolors[i].pixel >> 8 & 0xff;
+      xcolors[i].blue = xcolors[i].pixel & 0xff;
+    }
+}
+
+XImage *
+XCreateImage (Display *d, Visual *v,
+	      int depth, int format,
+	      int x, int y, int width, int height,
+	      int pad, int foo)
+{
+  XImage *img = (XImage *) malloc (sizeof (XImage));
+
+  if (img)
+    {
+      img->bitmap = BBitmap_new (width + 1, height + 1, depth == 1);
+      img->width = width + 1;
+      img->height = height + 1;
+      img->depth = depth == 1 ? 1 : 32;
+    }
+
+  return img;
+}
+
+int
+XFreeColors (Display *d, Colormap cmap,
+	     Pixel *pixels, int npixels, unsigned long planes)
+{
+  return 0;
+}
+
+void
+XImageFree (XImage *img)
+{
+  if (img)
+    free (img);
+}
+
+void
+XDestroyImage (XImage *img)
+{
+  if (img)
+    {
+      BBitmap_free (img->bitmap);
+      XImageFree (img);
+    }
+}
+
+void *
+boundCheckingMalloc (long size)
+{
+  return malloc (size);
+}
+
+void *
+boundCheckingCalloc (long n, long s)
+{
+  return calloc (n, s);
+}
+
+void *
+boundCheckingRealloc (void *p, long s)
+{
+  return realloc (p, s);
+}
+
+void
+XPutPixel (XImage *img, int x, int y, unsigned long p)
+{
+  if (img->depth == 1)
+    haiku_put_pixel (img->bitmap, x, y, p);
+  else
+    haiku_put_pixel (img->bitmap, x, y, p | ((unsigned long) 255) << 24);
+}
+
+unsigned long
+XGetPixel (XImage *img, int x, int y)
+{
+  return haiku_get_pixel (img->bitmap, x, y);
+}
diff --git a/src/haiku_support.cc b/src/haiku_support.cc
new file mode 100644
index 0000000000..3304c37548
--- /dev/null
+++ b/src/haiku_support.cc
@@ -0,0 +1,2344 @@
+/* Haiku window system support.  Hey, Emacs, this is -*- C++ -*-
+   Copyright (C) 2021 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 <app/Application.h>
+#include <app/Cursor.h>
+#include <app/Messenger.h>
+
+#include <interface/GraphicsDefs.h>
+#include <interface/InterfaceDefs.h>
+#include <interface/Bitmap.h>
+#include <interface/Window.h>
+#include <interface/View.h>
+#include <interface/Screen.h>
+#include <interface/ScrollBar.h>
+#include <interface/Region.h>
+#include <interface/Menu.h>
+#include <interface/MenuItem.h>
+#include <interface/PopUpMenu.h>
+#include <interface/MenuBar.h>
+#include <interface/Alert.h>
+#include <interface/Button.h>
+
+#include <locale/UnicodeChar.h>
+
+#include <game/WindowScreen.h>
+#include <game/DirectWindow.h>
+
+#include <storage/Entry.h>
+#include <storage/Path.h>
+#include <storage/FilePanel.h>
+#include <storage/AppFileInfo.h>
+#include <storage/Path.h>
+#include <storage/PathFinder.h>
+
+#include <support/Beep.h>
+#include <support/DataIO.h>
+
+#include <translation/TranslatorRoster.h>
+#include <translation/TranslationDefs.h>
+#include <translation/TranslationUtils.h>
+
+#include <kernel/OS.h>
+#include <kernel/fs_attr.h>
+#include <kernel/scheduler.h>
+
+#include <cmath>
+#include <cstring>
+#include <pthread.h>
+#include <cstdint>
+
+#include "haiku_support.h"
+
+#define SCROLL_BAR_UPDATE 3000
+
+static color_space dpy_color_space = B_NO_COLOR_SPACE;
+static key_map *key_map = NULL;
+static char *key_chars = NULL;
+
+extern "C"
+{
+  extern void emacs_abort (void);
+
+  /* Also defined in haikuterm.h. */
+  extern void be_app_quit (void);
+}
+
+static void
+map_key (char *chars, int32 offset, uint32_t *c)
+{
+  int size = chars[offset++];
+  switch (size)
+    {
+    case 0:
+      break;
+
+    case 1:
+      *c = chars[offset];
+      break;
+
+    default:
+      {
+        char str[5];
+        int i = (size <= 4) ? size : 4;
+        strncpy (str, &(chars[offset]), i);
+        str[i] = '0';
+	*c = BUnicodeChar::FromUTF8 ((char *) &str);
+	break;
+      }
+    }
+}
+
+static void
+map_shift (uint32_t kc, uint32_t *ch)
+{
+  if (!key_map)
+    get_key_map (&key_map, &key_chars);
+  if (!key_map)
+    return;
+  if (kc >= 128)
+    return;
+
+  int32_t m = key_map->shift_map[kc];
+  map_key (key_chars, m, ch);
+}
+
+class Emacs : public BApplication
+{
+public:
+  Emacs () : BApplication ("application/x-vnd.GNU-emacs")
+  {
+  }
+
+  bool
+  QuitRequested (void)
+  {
+    BApplication::QuitRequested ();
+    return 0;
+  }
+};
+
+class EmacsWindow : public BDirectWindow
+{
+public:
+  struct child_frame
+  {
+    struct child_frame *next;
+    int xoff, yoff;
+    EmacsWindow *window;
+  } *subset_windows = NULL;
+
+  EmacsWindow *parent = NULL;
+  BRect pre_fullscreen_rect;
+  BRect pre_zoom_rect;
+  int x_before_zoom = INT_MIN;
+  int y_before_zoom = INT_MIN;
+  int fullscreen_p = 0;
+  int zoomed_p = 0;
+
+  EmacsWindow () : BDirectWindow (BRect (0, 0, 0, 0), "", B_TITLED_WINDOW_LOOK,
+				  B_NORMAL_WINDOW_FEEL, B_NO_SERVER_SIDE_WINDOW_MODIFIERS)
+  {
+
+  }
+
+  ~EmacsWindow ()
+  {
+    struct child_frame *next;
+    for (struct child_frame *f = subset_windows; f; f = next)
+      {
+	f->window->Unparent ();
+	next = f->next;
+	delete f;
+      }
+
+    if (this->parent)
+      UnparentAndUnlink ();
+  }
+
+  void
+  UpwardsSubset (EmacsWindow *w)
+  {
+    for (; w; w = w->parent)
+      AddToSubset (w);
+  }
+
+  void
+  UpwardsSubsetChildren (EmacsWindow *w)
+  {
+    UpwardsSubset (w);
+    for (struct child_frame *f = subset_windows; f;
+	 f = f->next)
+      f->window->UpwardsSubsetChildren (w);
+  }
+
+  void
+  UpwardsUnSubset (EmacsWindow *w)
+  {
+    for (; w; w = w->parent)
+      RemoveFromSubset (w);
+  }
+
+  void
+  UpwardsUnSubsetChildren (EmacsWindow *w)
+  {
+    UpwardsUnSubset (w);
+    for (struct child_frame *f = subset_windows; f;
+	 f = f->next)
+      f->window->UpwardsUnSubsetChildren (w);
+  }
+
+  void
+  Unparent (void)
+  {
+    this->SetFeel (B_NORMAL_WINDOW_FEEL);
+    UpwardsUnSubsetChildren (parent);
+    this->RemoveFromSubset (this);
+    this->parent = NULL;
+    if (fullscreen_p)
+      {
+	fullscreen_p = 0;
+	MakeFullscreen (1);
+      }
+  }
+
+  void
+  UnparentAndUnlink (void)
+  {
+    this->parent->UnlinkChild (this);
+    this->Unparent ();
+  }
+
+  void
+  UnlinkChild (EmacsWindow *window)
+  {
+    struct child_frame *last = NULL;
+    struct child_frame *tem = subset_windows;
+
+    for (; tem; last = tem, tem = tem->next)
+      {
+	if (tem->window == window)
+	  {
+	    if (last)
+	      last->next = tem->next;
+	    if (tem == subset_windows)
+	      subset_windows = NULL;
+	    delete tem;
+	    return;
+	  }
+      }
+
+    emacs_abort ();
+  }
+
+  void
+  ParentTo (EmacsWindow *window)
+  {
+    if (this->parent)
+      UnparentAndUnlink ();
+
+    this->parent = window;
+    this->SetFeel (B_FLOATING_SUBSET_WINDOW_FEEL);
+    this->AddToSubset (this);
+    if (!IsHidden () && this->parent)
+      UpwardsSubsetChildren (parent);
+    if (fullscreen_p)
+      {
+	fullscreen_p = 0;
+	MakeFullscreen (1);
+      }
+    this->Sync ();
+    window->LinkChild (this);
+  }
+
+  void
+  LinkChild (EmacsWindow *window)
+  {
+    struct child_frame *f = new struct child_frame;
+
+    for (struct child_frame *f = subset_windows; f;
+	 f = f->next)
+      {
+	if (window == f->window)
+	  emacs_abort ();
+      }
+
+    f->window = window;
+    f->next = subset_windows;
+    f->xoff = -1;
+    f->yoff = -1;
+
+    subset_windows = f;
+  }
+
+  void
+  DoMove (struct child_frame *f)
+  {
+    BRect frame = this->Frame ();
+    f->window->MoveTo (frame.left + f->xoff,
+		       frame.top + f->yoff);
+    this->Sync ();
+  }
+
+  void
+  DoUpdateWorkspace (struct child_frame *f)
+  {
+    f->window->SetWorkspaces (this->Workspaces ());
+  }
+
+  void
+  MoveChild (EmacsWindow *window, int xoff, int yoff,
+	     int weak_p)
+  {
+    for (struct child_frame *f = subset_windows; f;
+	 f = f->next)
+      {
+	if (window == f->window)
+	  {
+	    f->xoff = xoff;
+	    f->yoff = yoff;
+	    if (!weak_p)
+	      DoMove (f);
+	    return;
+	  }
+      }
+
+    emacs_abort ();
+  }
+
+  void
+  WindowActivated (bool activated)
+  {
+    struct haiku_activation_event rq;
+    rq.window = this;
+    rq.activated_p = activated;
+
+    haiku_write (ACTIVATION, &rq);
+  }
+
+  void
+  MessageReceived (BMessage *msg)
+  {
+    int32 old_what = 0;
+
+    if (msg->GetPointer ("menuptr"))
+      {
+	struct haiku_menu_bar_select_event rq;
+	rq.window = this;
+	rq.ptr = (void *) msg->GetPointer ("menuptr");
+	haiku_write (MENU_BAR_SELECT_EVENT, &rq);
+      }
+    else if (msg->what == 'FPSE' ||
+	     ((msg->FindInt32 ("old_what", &old_what) == B_OK &&
+	       old_what == 'FPSE')))
+      {
+	struct haiku_file_panel_event rq;
+	BEntry entry;
+	BPath path;
+	entry_ref ref;
+
+	rq.ptr = NULL;
+
+	if (msg->FindRef ("refs", &ref) == B_OK &&
+	    entry.SetTo (&ref, 0) == B_OK &&
+	    entry.GetPath (&path) == B_OK)
+	  {
+	    const char *str_path = path.Path ();
+	    if (str_path)
+	      rq.ptr = strdup (str_path);
+	  }
+
+	if (msg->FindRef ("directory", &ref),
+	    entry.SetTo (&ref, 0) == B_OK &&
+	    entry.GetPath (&path) == B_OK)
+	  {
+	    const char *name = msg->GetString ("name");
+	    const char *str_path = path.Path ();
+
+	    if (name)
+	      {
+		char str_buf[std::strlen (str_path) + std::strlen (name) + 2];
+		snprintf ((char *) &str_buf,
+			  std::strlen (str_path) + std::strlen (name) + 2, "%s/%s",
+			  str_path, name);
+		rq.ptr = strdup (str_buf);
+	      }
+	  }
+
+	haiku_write (FILE_PANEL_EVENT, &rq);
+      }
+    BDirectWindow::MessageReceived (msg);
+  }
+
+  void
+  DispatchMessage (BMessage *msg, BHandler *handler)
+  {
+    if (msg->what == B_KEY_DOWN || msg->what == B_KEY_UP)
+      {
+	struct haiku_key_event rq;
+	rq.window = this;
+
+	int32_t code = msg->GetInt32 ("raw_char", 0);
+
+	rq.modifiers = 0;
+	uint32_t mods = modifiers ();
+
+	if (mods & B_SHIFT_KEY)
+	  rq.modifiers |= HAIKU_MODIFIER_SHIFT;
+
+	if (mods & B_CONTROL_KEY)
+	  rq.modifiers |= HAIKU_MODIFIER_CTRL;
+
+	if (mods & B_COMMAND_KEY)
+	  rq.modifiers |= HAIKU_MODIFIER_ALT;
+
+	rq.mb_char = code;
+	rq.kc = msg->GetInt32 ("key", -1);
+	rq.unraw_mb_char =
+	  BUnicodeChar::FromUTF8 (msg->GetString ("bytes"));
+
+	if ((mods & B_SHIFT_KEY) && rq.kc >= 0)
+	  map_shift (rq.kc, &rq.unraw_mb_char);
+
+	haiku_write (msg->what == B_KEY_DOWN ? KEY_DOWN : KEY_UP, &rq);
+      }
+    else if (msg->what == B_MOUSE_WHEEL_CHANGED)
+      {
+	struct haiku_wheel_move_event rq;
+	rq.window = this;
+	rq.modifiers = 0;
+
+	uint32_t mods = modifiers ();
+
+	if (mods & B_SHIFT_KEY)
+	  rq.modifiers |= HAIKU_MODIFIER_SHIFT;
+
+	if (mods & B_CONTROL_KEY)
+	  rq.modifiers |= HAIKU_MODIFIER_CTRL;
+
+	if (mods & B_COMMAND_KEY)
+	  rq.modifiers |= HAIKU_MODIFIER_ALT;
+
+	float dx, dy;
+	if (msg->FindFloat ("be:wheel_delta_x", &dx) == B_OK &&
+	    msg->FindFloat ("be:wheel_delta_y", &dy) == B_OK)
+	  {
+	    rq.delta_x = dx;
+	    rq.delta_y = dy;
+
+	    haiku_write (WHEEL_MOVE_EVENT, &rq);
+	  };
+      }
+    else
+      BDirectWindow::DispatchMessage (msg, handler);
+  }
+
+  void
+  MenusBeginning ()
+  {
+    struct haiku_menu_bar_state_event rq;
+    rq.window = this;
+
+    haiku_write (MENU_BAR_OPEN, &rq);
+  }
+
+  void
+  MenusEnded ()
+  {
+    struct haiku_menu_bar_state_event rq;
+    rq.window = this;
+
+    haiku_write (MENU_BAR_CLOSE, &rq);
+  }
+
+  void
+  FrameResized (float newWidth, float newHeight)
+  {
+    struct haiku_resize_event rq;
+    rq.window = this;
+    rq.px_heightf = newHeight;
+    rq.px_widthf = newWidth;
+
+    haiku_write (FRAME_RESIZED, &rq);
+    BDirectWindow::FrameResized (newWidth, newHeight);
+  }
+
+  void
+  FrameMoved (BPoint newPosition)
+  {
+    struct haiku_move_event rq;
+    rq.window = this;
+    rq.x = std::lrint (newPosition.x);
+    rq.y = std::lrint (newPosition.y);
+
+    haiku_write (MOVE_EVENT, &rq);
+
+    for (struct child_frame *f = subset_windows;
+	 f; f = f->next)
+      DoMove (f);
+    BDirectWindow::FrameMoved (newPosition);
+  }
+
+  void
+  WorkspacesChanged (uint32_t old, uint32_t n)
+  {
+    for (struct child_frame *f = subset_windows;
+	 f; f = f->next)
+      DoUpdateWorkspace (f);
+  }
+
+  void
+  EmacsMoveTo (int x, int y)
+  {
+    if (!this->parent)
+      this->MoveTo (x, y);
+    else
+      this->parent->MoveChild (this, x, y, 0);
+  }
+
+  bool
+  QuitRequested ()
+  {
+    struct haiku_quit_requested_event rq;
+    rq.window = this;
+    haiku_write (QUIT_REQUESTED, &rq);
+    return false;
+  }
+
+  void
+  Minimize (bool minimized_p)
+  {
+    BDirectWindow::Minimize (minimized_p);
+    struct haiku_iconification_event rq;
+    rq.window = this;
+    rq.iconified_p = !parent && minimized_p;
+
+    haiku_write (ICONIFICATION, &rq);
+  }
+
+  void
+  EmacsHide (void)
+  {
+    if (this->IsHidden ())
+      return;
+    Hide ();
+    if (this->parent)
+      UpwardsUnSubsetChildren (this->parent);
+  }
+
+  void
+  EmacsShow (void)
+  {
+    if (!this->IsHidden ())
+      return;
+    Show ();
+    if (this->parent)
+      UpwardsSubsetChildren (this->parent);
+  }
+
+  void
+  Zoom (BPoint o, float w, float h)
+  {
+    struct haiku_zoom_event rq;
+    rq.window = this;
+
+    rq.x = o.x;
+    rq.y = o.y;
+
+    rq.width = w;
+    rq.height = h;
+
+    if (fullscreen_p)
+      MakeFullscreen (0);
+
+    if (o.x != x_before_zoom ||
+	o.y != y_before_zoom)
+      {
+	x_before_zoom = Frame ().left;
+	y_before_zoom = Frame ().top;
+	pre_zoom_rect = Frame ();
+	zoomed_p = 1;
+	haiku_write (ZOOM_EVENT, &rq);
+      }
+    else
+      {
+	zoomed_p = 0;
+	x_before_zoom = y_before_zoom = INT_MIN;
+      }
+
+    BDirectWindow::Zoom (o, w, h);
+  }
+
+  void
+  UnZoom (void)
+  {
+    if (!zoomed_p)
+      return;
+    zoomed_p = 0;
+
+    EmacsMoveTo (pre_zoom_rect.left, pre_zoom_rect.top);
+    ResizeTo (pre_zoom_rect.Width (),
+	      pre_zoom_rect.Height ());
+  }
+
+  void
+  GetParentWidthHeight (int *width, int *height)
+  {
+    if (parent)
+      {
+	*width = parent->Frame ().Width ();
+	*height = parent->Frame ().Height ();
+      }
+    else
+      {
+	BScreen s (this);
+	*width = s.Frame ().Width ();
+	*height = s.Frame ().Height ();
+      }
+  }
+
+  void
+  OffsetChildRect (BRect *r, EmacsWindow *c)
+  {
+    for (struct child_frame *f; f; f = f->next)
+      if (f->window == c)
+	{
+	  r->top -= f->yoff;
+	  r->bottom -= f->yoff;
+	  r->left -= f->xoff;
+	  r->right -= f->xoff;
+	  return;
+	}
+
+    emacs_abort ();
+  }
+
+  void
+  MakeFullscreen (int make_fullscreen_p)
+  {
+    BScreen screen (this);
+
+    if (make_fullscreen_p == fullscreen_p)
+      return;
+
+    fullscreen_p = make_fullscreen_p;
+    uint32 flags = Flags ();
+    if (fullscreen_p)
+      {
+	if (zoomed_p)
+	  UnZoom ();
+
+	flags |= B_NOT_MOVABLE | B_NOT_ZOOMABLE;
+	pre_fullscreen_rect = Frame ();
+	if (parent)
+	  parent->OffsetChildRect (&pre_fullscreen_rect, this);
+
+	int w, h;
+	EmacsMoveTo (0, 0);
+	GetParentWidthHeight (&w, &h);
+	ResizeTo (w, h);
+      }
+    else
+      {
+	flags &= ~(B_NOT_MOVABLE | B_NOT_ZOOMABLE);
+	EmacsMoveTo (pre_fullscreen_rect.left,
+		     pre_fullscreen_rect.top);
+	ResizeTo (pre_fullscreen_rect.Width (),
+		  pre_fullscreen_rect.Height ());
+      }
+    SetFlags (flags);
+  }
+};
+
+class EmacsMenuBar : public BMenuBar
+{
+public:
+  EmacsMenuBar () : BMenuBar (BRect (0, 0, 0, 0), NULL)
+  {
+  }
+
+  void
+  FrameResized (float newWidth, float newHeight)
+  {
+    struct haiku_menu_bar_resize_event rq;
+    rq.window = this->Window ();
+    rq.height = std::lrint (newHeight);
+    rq.width = std::lrint (newWidth);
+
+    haiku_write (MENU_BAR_RESIZE, &rq);
+    BMenuBar::FrameResized (newWidth, newHeight);
+  }
+};
+
+class EmacsView : public BView
+{
+public:
+  uint32_t visible_bell_color = 0;
+  uint32_t previous_buttons = 0;
+  int looper_locked_count = 0;
+  BRegion sb_region;
+
+  BView *offscreen_draw_view = NULL;
+  BBitmap *offscreen_draw_bitmap_1 = NULL;
+  BBitmap *offscreen_blit_bitmap_1 = NULL;
+
+  BScreen *screen = NULL;
+  color_space cspace;
+
+  EmacsView () : BView (BRect (0, 0, 0, 0), NULL, B_FOLLOW_NONE, B_WILL_DRAW)
+  {
+
+  }
+
+  ~EmacsView ()
+  {
+    TearDownDoubleBuffering ();
+    delete screen;
+  }
+
+  void
+  AttachedToWindow (void)
+  {
+    screen = new BScreen (Window ());
+    cspace = screen->ColorSpace ();
+  }
+
+  void
+  TearDownDoubleBuffering (void)
+  {
+    if (offscreen_draw_view)
+      {
+	if (!looper_locked_count)
+	  if (!offscreen_draw_view->LockLooper ())
+	    emacs_abort ();
+	offscreen_draw_view->RemoveSelf ();
+	delete offscreen_draw_view;
+	offscreen_draw_view = NULL;
+	delete offscreen_draw_bitmap_1;
+	offscreen_draw_bitmap_1 = NULL;
+	delete offscreen_blit_bitmap_1;
+	offscreen_blit_bitmap_1 = NULL;
+      }
+   }
+
+  void
+  AfterResize (float newWidth, float newHeight)
+  {
+    if (offscreen_draw_view)
+      {
+	if (!offscreen_draw_view->LockLooper ())
+	  emacs_abort ();
+	offscreen_draw_view->RemoveSelf ();
+	delete offscreen_blit_bitmap_1;
+	offscreen_blit_bitmap_1 = new (std::nothrow) BBitmap (Frame (), cspace, 1);
+	if (!offscreen_blit_bitmap_1)
+	  emacs_abort ();
+	delete offscreen_draw_bitmap_1;
+	offscreen_draw_bitmap_1 = new (std::nothrow) BBitmap (Frame (), cspace, 1);
+	if (!offscreen_draw_bitmap_1)
+	  emacs_abort ();
+	offscreen_draw_view->MoveTo (Frame ().left, Frame ().top);
+	offscreen_draw_view->ResizeTo (Frame ().Width (), Frame ().Height ());
+	offscreen_draw_bitmap_1->AddChild (offscreen_draw_view);
+
+	if (looper_locked_count)
+	  {
+	    offscreen_blit_bitmap_1->Lock ();
+	    offscreen_draw_bitmap_1->Lock ();
+	  }
+      }
+  }
+
+  void
+  Draw (BRect expose_bounds)
+  {
+    struct haiku_expose_event rq;
+
+    if (visible_bell_color > 0)
+      {
+	PushState ();
+	BView_SetHighColorForVisibleBell (this, visible_bell_color);
+	visible_bell_color = 0;
+	FillRect (Frame ());
+	PopState ();
+	return;
+      }
+
+    if (!offscreen_draw_view)
+      {
+	if (sb_region.Contains (std::lrint (expose_bounds.left),
+				std::lrint (expose_bounds.top)) &&
+	    sb_region.Contains (std::lrint (expose_bounds.right),
+				std::lrint (expose_bounds.top)) &&
+	    sb_region.Contains (std::lrint (expose_bounds.left),
+				std::lrint (expose_bounds.bottom)) &&
+	    sb_region.Contains (std::lrint (expose_bounds.right),
+				std::lrint (expose_bounds.bottom)))
+	  return;
+
+	rq.x = std::floor (expose_bounds.left);
+	rq.y = std::floor (expose_bounds.top);
+	rq.width = std::ceil (expose_bounds.right - expose_bounds.left + 1);
+	rq.height = std::ceil (expose_bounds.bottom - expose_bounds.top + 1);
+	if (!rq.width)
+	  rq.width = 1;
+	if (!rq.height)
+	  rq.height = 1;
+	rq.window = this->Window ();
+
+	haiku_write (FRAME_EXPOSED, &rq);
+      }
+    else
+      {
+	DrawBitmap (offscreen_blit_bitmap_1, expose_bounds, expose_bounds);
+      }
+  }
+
+  void
+  DoVisibleBell (uint32_t color)
+  {
+    if (LockLooper ())
+      emacs_abort ();
+    visible_bell_color = color | (255 << 24);
+    PushState ();
+    BView_SetHighColorForVisibleBell (this, visible_bell_color);
+    FillRect (Frame ());
+    PopState ();
+    Invalidate ();
+    UnlockLooper ();
+  }
+
+  void
+  FlipBuffers (void)
+  {
+    if (!LockLooper ())
+      emacs_abort ();
+    if (!offscreen_draw_view)
+      emacs_abort ();
+
+    offscreen_draw_view->RemoveSelf ();
+    BBitmap *b = offscreen_blit_bitmap_1;
+    offscreen_blit_bitmap_1 = offscreen_draw_bitmap_1;
+    offscreen_draw_bitmap_1 = b;
+    b->ImportBits (offscreen_blit_bitmap_1);
+    offscreen_draw_bitmap_1->AddChild (offscreen_draw_view);
+
+    Invalidate ();
+    UnlockLooper ();
+    return;
+  }
+
+  void
+  SetUpDoubleBuffering (void)
+  {
+    if (!LockLooper ())
+      emacs_abort ();
+    if (offscreen_draw_view)
+      emacs_abort ();
+
+    offscreen_draw_view = new BView (Frame (), NULL, B_FOLLOW_NONE, B_WILL_DRAW);
+    offscreen_blit_bitmap_1 = new BBitmap (Frame (), cspace, 1);
+    offscreen_draw_bitmap_1 = new BBitmap (Frame (), cspace, 1);
+    offscreen_draw_bitmap_1->AddChild (offscreen_draw_view);
+
+    if (looper_locked_count)
+      {
+	offscreen_blit_bitmap_1->Lock ();
+	offscreen_draw_bitmap_1->Lock ();
+      }
+
+    UnlockLooper ();
+    Invalidate ();
+  }
+
+  void
+  MouseMoved (BPoint point, uint32 transit, const BMessage *msg)
+  {
+    struct haiku_mouse_motion_event rq;
+
+    rq.just_exited_p = transit == B_EXITED_VIEW;
+    rq.x = point.x;
+    rq.y = point.y;
+    rq.be_code = transit;
+    rq.window = this->Window ();
+
+    haiku_write (MOUSE_MOTION, &rq);
+  }
+
+  void
+  MouseDown (BPoint point)
+  {
+    struct haiku_button_event rq;
+    uint32 buttons;
+
+    this->GetMouse (&point, &buttons, false);
+
+    rq.window = this->Window ();
+    rq.btn_no = 0;
+
+    if (!(previous_buttons & B_PRIMARY_MOUSE_BUTTON) &&
+	(buttons & B_PRIMARY_MOUSE_BUTTON))
+      rq.btn_no = 0;
+    else if (!(previous_buttons & B_SECONDARY_MOUSE_BUTTON) &&
+	     (buttons & B_SECONDARY_MOUSE_BUTTON))
+      rq.btn_no = 2;
+    else if (!(previous_buttons & B_TERTIARY_MOUSE_BUTTON) &&
+	     (buttons & B_TERTIARY_MOUSE_BUTTON))
+      rq.btn_no = 1;
+    previous_buttons = buttons;
+
+    rq.x = point.x;
+    rq.y = point.y;
+
+    uint32_t mods = modifiers ();
+
+    rq.modifiers = 0;
+    if (mods & B_SHIFT_KEY)
+      rq.modifiers |= HAIKU_MODIFIER_SHIFT;
+
+    if (mods & B_CONTROL_KEY)
+      rq.modifiers |= HAIKU_MODIFIER_CTRL;
+
+    if (mods & B_COMMAND_KEY)
+      rq.modifiers |= HAIKU_MODIFIER_ALT;
+
+    haiku_write (BUTTON_DOWN, &rq);
+  }
+
+  void
+  MouseUp (BPoint point)
+  {
+    struct haiku_button_event rq;
+    uint32 buttons;
+
+    this->GetMouse (&point, &buttons, false);
+
+    rq.window = this->Window ();
+    rq.btn_no = 0;
+
+    if ((previous_buttons & B_PRIMARY_MOUSE_BUTTON) &&
+	!(buttons & B_PRIMARY_MOUSE_BUTTON))
+      rq.btn_no = 0;
+    else if ((previous_buttons & B_SECONDARY_MOUSE_BUTTON) &&
+	     !(buttons & B_SECONDARY_MOUSE_BUTTON))
+      rq.btn_no = 2;
+    else if ((previous_buttons & B_TERTIARY_MOUSE_BUTTON) &&
+	     !(buttons & B_TERTIARY_MOUSE_BUTTON))
+      rq.btn_no = 1;
+    previous_buttons = buttons;
+
+    rq.x = point.x;
+    rq.y = point.y;
+
+    uint32_t mods = modifiers ();
+
+    rq.modifiers = 0;
+    if (mods & B_SHIFT_KEY)
+      rq.modifiers |= HAIKU_MODIFIER_SHIFT;
+
+    if (mods & B_CONTROL_KEY)
+      rq.modifiers |= HAIKU_MODIFIER_CTRL;
+
+    if (mods & B_COMMAND_KEY)
+      rq.modifiers |= HAIKU_MODIFIER_ALT;
+
+    haiku_write (BUTTON_UP, &rq);
+  }
+};
+
+class EmacsScrollBar : public BScrollBar
+{
+public:
+  void *scroll_bar;
+
+  EmacsScrollBar (int x, int y, int x1, int y1, bool horizontal_p) :
+    BScrollBar (BRect (x, y, x1, y1), NULL, NULL, 0, 0, horizontal_p ?
+		B_HORIZONTAL : B_VERTICAL)
+  {
+    BView *vw = (BView *) this;
+    vw->SetResizingMode (B_FOLLOW_NONE);
+  }
+
+  void
+  MessageReceived (BMessage *msg)
+  {
+    if (msg->what == SCROLL_BAR_UPDATE)
+      {
+	this->SetRange (0, msg->GetInt32 ("emacs:range", 0));
+	this->SetValue (msg->GetInt32 ("emacs:units", 0));
+      }
+
+    BScrollBar::MessageReceived (msg);
+  }
+
+  void
+  ValueChanged (float new_value)
+  {
+    struct haiku_scroll_bar_value_event rq;
+    rq.scroll_bar = scroll_bar;
+    rq.position = new_value;
+
+    haiku_write (SCROLL_BAR_VALUE_EVENT, &rq);
+  }
+
+  void
+  MouseDown (BPoint pt)
+  {
+    struct haiku_scroll_bar_drag_event rq;
+    rq.dragging_p = 1;
+    rq.scroll_bar = scroll_bar;
+
+    haiku_write (SCROLL_BAR_DRAG_EVENT, &rq);
+    BScrollBar::MouseDown (pt);
+  }
+
+  void
+  MouseUp (BPoint pt)
+  {
+    struct haiku_scroll_bar_drag_event rq;
+    rq.dragging_p = 0;
+    rq.scroll_bar = scroll_bar;
+
+    haiku_write (SCROLL_BAR_DRAG_EVENT, &rq);
+    BScrollBar::MouseUp (pt);
+  }
+};
+
+class EmacsMenuItem : public BMenuItem
+{
+public:
+  int menu_bar_id = -1;
+  void *wind_ptr = NULL;
+  char *key = NULL;
+
+  EmacsMenuItem (const char *ky,
+		 const char *str,
+		 BMessage *message = NULL) : BMenuItem (str, message)
+  {
+    if (ky)
+      {
+	key = strdup (ky);
+	if (!key)
+	  {
+	    perror ("strdup");
+	    emacs_abort ();
+	  }
+      }
+  }
+
+  ~EmacsMenuItem ()
+  {
+    if (key)
+      free (key);
+  }
+
+  void
+  DrawContent (void)
+  {
+    BMenu *menu = Menu ();
+
+    BMenuItem::DrawContent ();
+
+    if (key)
+      {
+	BRect r = Frame ();
+	int w = menu->StringWidth (key);
+	menu->MovePenTo (r.Width () - w - 4,
+			 menu->PenLocation ().y);
+	menu->DrawString (key);
+      }
+  }
+
+  void
+  GetContentSize (float *w, float *h)
+  {
+    BMenuItem::GetContentSize (w, h);
+    if (Menu () && key)
+      *w += 4 + Menu ()->StringWidth (key);
+  }
+
+  void
+  Highlight (bool highlight_p)
+  {
+    struct haiku_menu_bar_help_event rq;
+
+    if (menu_bar_id >= 0)
+      {
+	rq.window = wind_ptr;
+	rq.mb_idx = highlight_p ? menu_bar_id : -1;
+
+	haiku_write (MENU_BAR_HELP_EVENT, &rq);
+      }
+
+    BMenuItem::Highlight (highlight_p);
+  }
+};
+
+static int32
+start_running_application (void *data)
+{
+  haiku_io_init_in_app_thread ();
+
+  ((Emacs *) data)->Lock ();
+  ((Emacs *) data)->Run ();
+  ((Emacs *) data)->Unlock ();
+  return 0;
+}
+
+static uint8_t
+reverse_fringe_byte (uint8_t b)
+{
+  b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
+  b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
+  b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
+  return b;
+}
+
+void *
+BBitmap_data (void *bitmap)
+{
+  return ((BBitmap *) bitmap)->Bits ();
+}
+
+int
+BBitmap_convert (void *_bitmap, void **new_bitmap)
+{
+  BBitmap *bitmap = (BBitmap *) _bitmap;
+  if (bitmap->ColorSpace () == B_RGBA32)
+    return -1;
+  BRect bounds = bitmap->Bounds ();
+  BBitmap *bmp = new (std::nothrow) BBitmap (bounds, B_RGBA32);
+  if (!bmp)
+    return 0;
+  if (bmp->ImportBits (bitmap) != B_OK)
+    {
+      delete bmp;
+      return 0;
+    }
+  *(BBitmap **) new_bitmap = bmp;
+  return 1;
+}
+
+void
+BBitmap_free (void *bitmap)
+{
+  delete (BBitmap *) bitmap;
+}
+
+void *
+BBitmap_new (int width, int height, int mono_p)
+{
+  BBitmap *bn = new (std::nothrow) BBitmap (BRect (0, 0, width - 1, height - 1),
+					    mono_p ? B_GRAY1 : B_RGB32);
+
+  return bn;
+}
+
+void
+BBitmap_dimensions (void *bitmap, int *left, int *top,
+		    int *right, int *bottom,
+		    int32_t *bytes_per_row, int *mono_p)
+{
+  BRect rect = ((BBitmap *) bitmap)->Bounds ();
+  *left = rect.left;
+  *top = rect.top;
+  *right = rect.right;
+  *bottom = rect.bottom;
+
+  *bytes_per_row = ((BBitmap *) bitmap)->BytesPerRow ();
+  *mono_p = (((BBitmap *) bitmap)->ColorSpace () == B_GRAY1);
+}
+
+void *
+BApplication_setup (void)
+{
+  if (be_app)
+    return be_app;
+  Emacs *app;
+
+  app = new Emacs;
+  if (spawn_thread (start_running_application, "Emacs app thread",
+		    B_DEFAULT_MEDIA_PRIORITY, app) < 0)
+    {
+      fprintf (stderr, "spawn_thread failed\n");
+      emacs_abort ();
+    }
+
+  return app;
+}
+
+void *
+BWindow_new (void *_view)
+{
+  BWindow *window = new (std::nothrow) EmacsWindow;
+  BView **v = (BView **) _view;
+  if (!window)
+    {
+      *v = NULL;
+      return window;
+    }
+
+  BView *vw = new (std::nothrow) EmacsView;
+  if (!vw)
+    {
+      *v = NULL;
+      window->Lock ();
+      window->Quit ();
+      return NULL;
+    }
+  window->AddChild (vw);
+  *v = vw;
+  return window;
+}
+
+void
+BWindow_quit (void *window)
+{
+  ((BWindow *) window)->Lock ();
+  ((BWindow *) window)->Quit ();
+}
+
+void
+BWindow_set_offset (void *window, int x, int y)
+{
+  BWindow *wn = (BWindow *) window;
+  EmacsWindow *w = dynamic_cast<EmacsWindow *> (wn);
+  if (w)
+    {
+      if (!w->LockLooper ())
+	emacs_abort ();
+      w->EmacsMoveTo (x, y);
+      w->UnlockLooper ();
+    }
+  else
+    wn->MoveTo (x, y);
+}
+
+void
+BWindow_iconify (void *window)
+{
+  if (((BWindow *) window)->IsHidden ())
+    BWindow_set_visible (window, true);
+  ((BWindow *) window)->Minimize (true);
+}
+
+void
+BWindow_set_visible (void *window, int visible_p)
+{
+  EmacsWindow *win = (EmacsWindow *) window;
+  if (visible_p)
+    {
+      if (win->IsMinimized ())
+	win->Minimize (false);
+      win->EmacsShow ();
+    }
+  else if (!win->IsHidden ())
+    {
+      if (win->IsMinimized ())
+	win->Minimize (false);
+      win->EmacsHide ();
+    }
+  win->Sync ();
+}
+
+void
+BWindow_retitle (void *window, const char *title)
+{
+  ((BWindow *) window)->SetTitle (title);
+}
+
+void
+BWindow_resize (void *window, int width, int height)
+{
+  ((BWindow *) window)->ResizeTo (width, height);
+}
+
+void
+BWindow_activate (void *window)
+{
+  ((BWindow *) window)->Activate ();
+}
+
+void
+BScreen_px_dim (int *width, int *height)
+{
+  BScreen screen;
+  BRect frame = screen.Frame ();
+
+  *width = frame.right - frame.left;
+  *height = frame.bottom - frame.top;
+}
+
+void
+BView_resize_to (void *view, int width, int height)
+{
+  EmacsView *vw = (EmacsView *) view;
+  if (!vw->LockLooper ())
+    emacs_abort ();
+  vw->ResizeTo (width, height);
+  vw->AfterResize (width, height);
+  vw->UnlockLooper ();
+}
+
+void *
+BCursor_create_default (void)
+{
+  return new BCursor (B_CURSOR_ID_SYSTEM_DEFAULT);
+}
+
+void *
+BCursor_from_id (enum haiku_cursor cursor)
+{
+  return new BCursor ((enum BCursorID) cursor);
+}
+
+void *
+BCursor_create_i_beam (void)
+{
+  return new BCursor (B_CURSOR_ID_I_BEAM);
+}
+
+void *
+BCursor_create_progress_cursor (void)
+{
+  return new BCursor (B_CURSOR_ID_PROGRESS);
+}
+
+void *
+BCursor_create_grab (void)
+{
+  return new BCursor (B_CURSOR_ID_GRAB);
+}
+
+void
+BCursor_delete (void *cursor)
+{
+  delete (BCursor *) cursor;
+}
+
+void
+BView_set_view_cursor (void *view, void *cursor)
+{
+  if (!((BView *) view)->LockLooper ())
+    emacs_abort ();
+  ((BView *) view)->SetViewCursor ((BCursor *) cursor);
+  ((BView *) view)->UnlockLooper ();
+}
+
+void
+BWindow_Flush (void *window)
+{
+  ((BWindow *) window)->Flush ();
+}
+
+void
+BMapKey (uint32_t kc, int *non_ascii_p, unsigned *code)
+{
+  *code = 0;
+
+  switch (kc)
+    {
+    default:
+      *non_ascii_p = 0;
+      if (kc < 0xe && kc > 0x1)
+	{
+	  *code = XK_F1 + kc - 2;
+	  *non_ascii_p = 1;
+	}
+      return;
+    case 0x1e:
+      *code = XK_BackSpace;
+      break;
+    case 0x32:
+    case 0x5b:
+    case 0x47:
+      *code = XK_Return;
+      break;
+    case 0x61:
+      *code = XK_Left;
+      break;
+    case 0x63:
+      *code = XK_Right;
+      break;
+    case 0x57:
+      *code = XK_Up;
+      break;
+    case 0x62:
+      *code = XK_Down;
+      break;
+    case 0x64:
+      *code = XK_Insert;
+      break;
+    case 0x65:
+      *code = XK_Delete;
+      break;
+    case 0x37:
+      *code = XK_Home;
+      break;
+    case 0x58:
+      *code = XK_End;
+      break;
+    case 0x39:
+      *code = XK_Page_Up;
+      break;
+    case 0x5a:
+      *code = XK_Page_Down;
+      break;
+    case 0x1:
+      *code = XK_Escape;
+      break;
+    case 0x68:
+      *code = XK_Menu;
+      break;
+    }
+  *non_ascii_p = 1;
+}
+
+void *
+BScrollBar_make_for_view (void *view, int horizontal_p,
+			  int x, int y, int x1, int y1,
+			  void *scroll_bar_ptr)
+{
+  EmacsScrollBar *sb = new EmacsScrollBar (x, y, x1, y1, horizontal_p);
+  sb->scroll_bar = scroll_bar_ptr;
+
+  BView *vw = (BView *) view;
+  BView *sv = (BView *) sb;
+  if (!vw->LockLooper ())
+    emacs_abort ();
+  vw->AddChild ((BView *) sb);
+  sv->WindowActivated (vw->Window ()->IsActive ());
+  vw->UnlockLooper ();
+  return sb;
+}
+
+void
+BScrollBar_delete (void *sb)
+{
+  BView *view = (BView *) sb;
+  BView *pr = view->Parent ();
+
+  if (!pr->LockLooper ())
+    emacs_abort ();
+  pr->RemoveChild (view);
+  pr->UnlockLooper ();
+
+  delete (EmacsScrollBar *) sb;
+}
+
+void
+BView_move_frame (void *view, int x, int y, int x1, int y1)
+{
+  BView *vw = (BView *) view;
+
+  if (!vw->LockLooper ())
+    emacs_abort ();
+  vw->MoveTo (x, y);
+  vw->ResizeTo (x1 - x, y1 - y);
+  vw->UnlockLooper ();
+}
+
+void
+BView_scroll_bar_update (void *sb, int portion, int whole, int position)
+{
+  BScrollBar *bar = (BScrollBar *) sb;
+  BMessage msg = BMessage (SCROLL_BAR_UPDATE);
+  BMessenger mr = BMessenger (bar);
+  msg.AddInt32 ("emacs:range", whole);
+  msg.AddInt32 ("emacs:units", position);
+
+  mr.SendMessage (&msg);
+}
+
+int
+BScrollBar_default_size (int horizontal_p)
+{
+  return horizontal_p ? B_H_SCROLL_BAR_HEIGHT : B_V_SCROLL_BAR_WIDTH;
+}
+
+void
+BView_hide (void *view)
+{
+  BView *vw = (BView *) view;
+  if (!vw->LockLooper ())
+    emacs_abort ();
+  vw->Hide ();
+  vw->UnlockLooper ();
+}
+
+void
+BView_show (void *view)
+{
+  BView *vw = (BView *) view;
+  if (!vw->LockLooper ())
+    emacs_abort ();
+  vw->Show ();
+  vw->UnlockLooper ();
+}
+
+void
+BView_invalidate (void *view)
+{
+  BView *vw = (BView *) view;
+  if (!vw->LockLooper ())
+    emacs_abort ();
+  vw->Invalidate ();
+  vw->UnlockLooper ();
+}
+
+void
+BView_draw_lock (void *view)
+{
+  EmacsView *vw = (EmacsView *) view;
+  if (vw->looper_locked_count)
+    {
+      vw->looper_locked_count++;
+      return;
+    }
+  BView *v = (BView *) find_appropriate_view_for_draw (vw);
+  if (v != vw)
+    {
+      vw->offscreen_blit_bitmap_1->Lock ();
+      vw->offscreen_draw_bitmap_1->Lock ();
+    }
+  else
+    v->LockLooper ();
+
+  if (v != vw && !vw->LockLooper ())
+    emacs_abort ();
+  vw->looper_locked_count++;
+}
+
+void
+BView_draw_unlock (void *view)
+{
+  EmacsView *vw = (EmacsView *) view;
+  if (--vw->looper_locked_count)
+    return;
+
+  BView *v = (BView *) find_appropriate_view_for_draw (view);
+  if (v == vw)
+    vw->UnlockLooper ();
+  else
+    {
+      vw->offscreen_blit_bitmap_1->Unlock ();
+      vw->offscreen_draw_bitmap_1->Unlock ();
+      vw->UnlockLooper ();
+    }
+}
+
+void
+BWindow_center_on_screen (void *window)
+{
+  BWindow *w = (BWindow *) window;
+  w->CenterOnScreen ();
+}
+
+void
+BView_mouse_down (void *view, int x, int y)
+{
+  BView *vw = (BView *) view;
+  if (vw->LockLooper ())
+    {
+      vw->MouseDown (BPoint (x, y));
+      vw->UnlockLooper ();
+    }
+}
+
+void
+BView_mouse_up (void *view, int x, int y)
+{
+  BView *vw = (BView *) view;
+  if (vw->LockLooper ())
+    {
+      vw->MouseUp (BPoint (x, y));
+      vw->UnlockLooper ();
+    }
+}
+
+void
+BView_mouse_moved (void *view, int x, int y, uint32_t transit)
+{
+  BView *vw = (BView *) view;
+  if (vw->LockLooper ())
+    {
+      vw->MouseMoved (BPoint (x, y), transit, NULL);
+      vw->UnlockLooper ();
+    }
+}
+
+void
+BBitmap_import_mono_bits (void *bitmap, void *bits, int wd, int h)
+{
+  BBitmap *bmp = (BBitmap *) bitmap;
+  unsigned char *data = (unsigned char *) bmp->Bits ();
+  unsigned short *bts = (unsigned short *) bits;
+
+  for (int i = 0; i < (h * (wd / 8)); i++)
+    {
+      *((unsigned short *) data) = reverse_fringe_byte (~bts[i]);
+      data += bmp->BytesPerRow ();
+    }
+}
+
+void
+BView_publish_scroll_bar (void *view, int x, int y, int width, int height)
+{
+  EmacsView *vw = (EmacsView *) view;
+  if (vw->LockLooper ())
+    {
+      vw->sb_region.Include (BRect (x, y, x - 1 + width,
+				    y - 1 + height));
+      vw->UnlockLooper ();
+    }
+}
+
+void
+BView_forget_scroll_bar (void *view, int x, int y, int width, int height)
+{
+  EmacsView *vw = (EmacsView *) view;
+  if (vw->LockLooper ())
+    {
+      vw->sb_region.Exclude (BRect (x, y, x - 1 + width,
+				    y - 1 + height));
+      vw->UnlockLooper ();
+    }
+}
+
+int
+BFont_family_count (void)
+{
+  return count_font_families ();
+}
+
+void
+BView_get_mouse (void *view, int *x, int *y)
+{
+  BPoint l;
+  BView *vw = (BView *) view;
+  if (!vw->LockLooper ())
+    emacs_abort ();
+  vw->GetMouse (&l, NULL, 1);
+  vw->UnlockLooper ();
+
+  *x = std::lrint (l.x);
+  *y = std::lrint (l.y);
+}
+
+void
+BView_convert_to_screen (void *view, int *x, int *y)
+{
+  BPoint l = BPoint (*x, *y);
+  BView *vw = (BView *) view;
+  if (!vw->LockLooper ())
+    emacs_abort ();
+  vw->ConvertToScreen (&l);
+  vw->UnlockLooper ();
+
+  *x = std::lrint (l.x);
+  *y = std::lrint (l.y);
+}
+
+void
+BView_convert_from_screen (void *view, int *x, int *y)
+{
+  BPoint l = BPoint (*x, *y);
+  BView *vw = (BView *) view;
+  if (!vw->LockLooper ())
+    emacs_abort ();
+  vw->ConvertFromScreen (&l);
+  vw->UnlockLooper ();
+
+  *x = std::lrint (l.x);
+  *y = std::lrint (l.y);
+}
+
+void
+BWindow_change_decoration (void *window, int decorate_p)
+{
+  BWindow *w = (BWindow *) window;
+  if (!w->LockLooper ())
+    emacs_abort ();
+  if (decorate_p)
+    w->SetLook (B_TITLED_WINDOW_LOOK);
+  else
+    w->SetLook (B_NO_BORDER_WINDOW_LOOK);
+  w->UnlockLooper ();
+}
+
+void
+BWindow_set_tooltip_decoration (void *window)
+{
+  BWindow *w = (BWindow *) window;
+  if (!w->LockLooper ())
+    emacs_abort ();
+  w->SetLook (B_BORDERED_WINDOW_LOOK);
+  w->SetFeel (B_FLOATING_APP_WINDOW_FEEL);
+  w->UnlockLooper ();
+}
+
+void
+BWindow_set_avoid_focus (void *window, int avoid_focus_p)
+{
+  BWindow *w = (BWindow *) window;
+  if (!w->LockLooper ())
+    emacs_abort ();
+
+  if (!avoid_focus_p)
+    w->SetFlags (w->Flags () & ~B_AVOID_FOCUS);
+  else
+    w->SetFlags (w->Flags () | B_AVOID_FOCUS);
+  w->Sync ();
+  w->UnlockLooper ();
+}
+
+void
+BView_emacs_delete (void *view)
+{
+  EmacsView *vw = (EmacsView *) view;
+  if (!vw->LockLooper ())
+    emacs_abort ();
+  vw->RemoveSelf ();
+  delete vw;
+}
+
+uint32_t
+haiku_current_workspace (void)
+{
+  return current_workspace ();
+}
+
+uint32_t
+BWindow_workspaces (void *window)
+{
+  return ((BWindow *) window)->Workspaces ();
+}
+
+void *
+BPopUpMenu_new (const char *name)
+{
+  BPopUpMenu *menu = new BPopUpMenu (name, false);
+  return menu;
+}
+
+void
+BMenu_add_item (void *menu, const char *label, void *ptr, bool enabled_p,
+		bool marked_p, bool mbar_p, void *mbw_ptr, const char *key)
+{
+  BMenu *m = (BMenu *) menu;
+  BMessage *msg;
+  if (ptr)
+    msg = new BMessage ();
+  EmacsMenuItem *it = new EmacsMenuItem (key, label, ptr ? msg : NULL);
+  it->SetTarget (m->Window ());
+  it->SetEnabled (enabled_p);
+  it->SetMarked (marked_p);
+  if (mbar_p)
+    {
+      it->menu_bar_id = (intptr_t) ptr;
+      it->wind_ptr = mbw_ptr;
+    }
+  if (ptr)
+    msg->AddPointer ("menuptr", ptr);
+  m->AddItem (it);
+}
+
+void
+BMenu_add_separator (void *menu)
+{
+  BMenu *m = (BMenu *) menu;
+
+  m->AddSeparatorItem ();
+}
+
+void *
+BMenu_new_submenu (void *menu, const char *label, bool enabled_p)
+{
+  BMenu *m = (BMenu *) menu;
+  BMenu *mn = new BMenu (label, B_ITEMS_IN_COLUMN);
+  mn->SetRadioMode (0);
+  BMenuItem *i = new BMenuItem (mn);
+  i->SetEnabled (enabled_p);
+  m->AddItem (i);
+  return mn;
+}
+
+void *
+BMenu_new_menu_bar_submenu (void *menu, const char *label)
+{
+  BMenu *m = (BMenu *) menu;
+  BMenu *mn = new BMenu (label, B_ITEMS_IN_COLUMN);
+  mn->SetRadioMode (0);
+  BMenuItem *i = new BMenuItem (mn);
+  i->SetEnabled (1);
+  m->AddItem (i);
+  return mn;
+}
+
+void *
+BMenu_run (void *menu, int x, int y)
+{
+  BPopUpMenu *mn = (BPopUpMenu *) menu;
+  BMenuItem *it = mn->Go (BPoint (x, y));
+  if (it)
+    {
+      BMessage *mg = it->Message ();
+      if (mg)
+	return (void *) mg->GetPointer ("menuptr");
+      else
+	return NULL;
+    }
+  return NULL;
+}
+
+void
+BPopUpMenu_delete (void *menu)
+{
+  delete (BPopUpMenu *) menu;
+}
+
+void *
+BMenuBar_new (void *view)
+{
+  BView *vw = (BView *) view;
+  EmacsMenuBar *bar = new EmacsMenuBar ();
+
+  if (!vw->LockLooper ())
+    emacs_abort ();
+  vw->AddChild ((BView *) bar);
+  vw->UnlockLooper ();
+
+  return bar;
+}
+
+void
+BMenuBar_delete (void *menubar)
+{
+  BView *vw = (BView *) menubar;
+  BView *p = vw->Parent ();
+  if (!p->LockLooper ())
+    emacs_abort ();
+  vw->RemoveSelf ();
+  p->UnlockLooper ();
+  delete vw;
+}
+
+void
+BMenu_delete_all (void *menu)
+{
+  BMenu *mn = (BMenu *) menu;
+  mn->RemoveItems (0, mn->CountItems (), true);
+}
+
+void
+BMenu_delete_from (void *menu, int start, int count)
+{
+  BMenu *mn = (BMenu *) menu;
+  mn->RemoveItems (start, count, true);
+}
+
+int
+BMenu_count_items (void *menu)
+{
+  return ((BMenu *) menu)->CountItems ();
+}
+
+void *
+BMenu_item_at (void *menu, int idx)
+{
+  return ((BMenu *) menu)->ItemAt (idx);
+}
+
+void
+BMenu_item_set_label (void *item, const char *label)
+{
+  ((BMenuItem *) item)->SetLabel (label);
+}
+
+void *
+BMenu_item_get_menu (void *item)
+{
+  return ((BMenuItem *) item)->Submenu ();
+}
+
+void
+haiku_ring_bell (void)
+{
+  beep ();
+}
+
+void *
+BAlert_new (const char *text, enum haiku_alert_type type)
+{
+  return new BAlert (NULL, text, NULL, NULL, NULL, B_WIDTH_AS_USUAL,
+		     (enum alert_type) type);
+}
+
+void *
+BAlert_add_button (void *alert, const char *text)
+{
+  BAlert *al = (BAlert *) alert;
+  al->AddButton (text);
+  return al->ButtonAt (al->CountButtons () - 1);
+}
+
+int32_t
+BAlert_go (void *alert)
+{
+  return ((BAlert *) alert)->Go ();
+}
+
+void
+BButton_set_enabled (void *button, int enabled_p)
+{
+  ((BButton *) button)->SetEnabled (enabled_p);
+}
+
+void
+BView_set_tooltip (void *view, const char *tooltip)
+{
+  ((BView *) view)->SetToolTip (tooltip);
+}
+
+void
+BAlert_delete (void *alert)
+{
+  delete (BAlert *) alert;
+}
+
+void
+BScreen_res (double *rrsx, double *rrsy)
+{
+  BScreen s (B_MAIN_SCREEN_ID);
+  if (!s.IsValid ())
+    emacs_abort ();
+  monitor_info i;
+
+  if (s.GetMonitorInfo (&i) == B_OK)
+    {
+      *rrsx = (double) i.width / (double) 2.54;
+      *rrsy = (double) i.height / (double) 2.54;
+    }
+  else
+    {
+      *rrsx = 72.27;
+      *rrsy = 72.27;
+    }
+}
+
+void
+EmacsWindow_parent_to (void *window, void *other_window)
+{
+  EmacsWindow *w = (EmacsWindow *) window;
+  if (!w->LockLooper ())
+    emacs_abort ();
+  w->ParentTo ((EmacsWindow *) other_window);
+  w->UnlockLooper ();
+}
+
+void
+EmacsWindow_unparent (void *window)
+{
+  EmacsWindow *w = (EmacsWindow *) window;
+  if (!w->LockLooper ())
+    emacs_abort ();
+  w->UnparentAndUnlink ();
+  w->UnlockLooper ();
+}
+
+void
+be_get_version_string (char *version, int len)
+{
+  std::strncpy (version, "Unknown Haiku release", len - 1);
+  BPath path;
+  if (find_directory (B_BEOS_LIB_DIRECTORY, &path) == B_OK)
+    {
+      path.Append ("libbe.so");
+
+      BAppFileInfo appFileInfo;
+      version_info versionInfo;
+      BFile file;
+      if (file.SetTo (path.Path (), B_READ_ONLY) == B_OK
+          && appFileInfo.SetTo (&file) == B_OK
+          && appFileInfo.GetVersionInfo (&versionInfo,
+                                         B_APP_VERSION_KIND) == B_OK
+          && versionInfo.short_info[0] != '\0')
+	std::strncpy (version, versionInfo.short_info, len - 1);
+    }
+}
+
+int
+be_get_display_planes (void)
+{
+  color_space space = dpy_color_space;
+  if (space == B_NO_COLOR_SPACE)
+    {
+      BScreen screen; /* This is actually a very slow operation. */
+      space = dpy_color_space = screen.ColorSpace ();
+    }
+
+  if (space == B_RGB32 || space == B_RGB24)
+    return 24;
+  if (space == B_RGB16)
+    return 16;
+  if (space == B_RGB15)
+    return 15;
+  if (space == B_CMAP8)
+    return 8;
+
+  emacs_abort (); /* https://www.haiku-os.org/docs/api/classBScreen.html
+		     says a valid screen can't be anything else. */
+  return -1;
+}
+
+int
+be_get_display_color_cells (void)
+{
+  color_space space = dpy_color_space;
+  if (space == B_NO_COLOR_SPACE)
+    {
+      BScreen screen;
+      space = dpy_color_space = screen.ColorSpace ();
+    }
+
+  if (space == B_RGB32 || space == B_RGB24)
+    return 1677216;
+  if (space == B_RGB16)
+    return 65536;
+  if (space == B_RGB15)
+    return 32768;
+  if (space == B_CMAP8)
+    return 256;
+
+  emacs_abort ();
+  return -1;
+}
+
+void
+be_warp_pointer (int x, int y)
+{
+  /* We're not supposed to use the following function without a
+     BWindowScreen object, but in Haiku nothing actually prevents us
+     from doing so. */
+
+  set_mouse_position (x, y);
+}
+
+void
+EmacsWindow_move_weak_child (void *window, void *child, int xoff, int yoff)
+{
+  EmacsWindow *w = (EmacsWindow *) window;
+  EmacsWindow *c = (EmacsWindow *) child;
+
+  if (!w->LockLooper ())
+    emacs_abort ();
+  w->MoveChild (c, xoff, yoff, 1);
+  w->UnlockLooper ();
+}
+
+void *
+find_appropriate_view_for_draw (void *vw)
+{
+  BView *v = (BView *) vw;
+  EmacsView *ev = dynamic_cast<EmacsView *>(v);
+  if (!ev)
+    return v;
+
+  return ev->offscreen_draw_view ? ev->offscreen_draw_view : vw;
+}
+
+void
+EmacsView_set_up_double_buffering (void *vw)
+{
+  EmacsView *view = (EmacsView *) vw;
+  if (!view->LockLooper ())
+    emacs_abort ();
+  if (view->offscreen_draw_view)
+    {
+      view->UnlockLooper ();
+      return;
+    }
+  view->SetUpDoubleBuffering ();
+  view->UnlockLooper ();
+}
+
+void
+EmacsView_flip_and_blit (void *vw)
+{
+  EmacsView *view = (EmacsView *) vw;
+  if (!view->offscreen_draw_view)
+    return;
+  if (!view->LockLooper ())
+    emacs_abort ();
+  view->FlipBuffers ();
+  view->UnlockLooper ();
+}
+
+void
+EmacsView_disable_double_buffering (void *vw)
+{
+  EmacsView *view = (EmacsView *) vw;
+  if (!view->LockLooper ())
+    emacs_abort ();
+  view->TearDownDoubleBuffering ();
+  view->UnlockLooper ();
+}
+
+int
+EmacsView_double_buffered_p (void *vw)
+{
+  EmacsView *view = (EmacsView *) vw;
+  if (!view->LockLooper ())
+    emacs_abort ();
+  int db_p = !!view->offscreen_draw_view;
+  view->UnlockLooper ();
+  return db_p;
+}
+
+struct popup_file_dialog_data
+{
+  BMessage *msg;
+  BFilePanel *panel;
+  BEntry *entry;
+};
+
+static void
+unwind_popup_file_dialog (void *ptr)
+{
+  struct popup_file_dialog_data *data =
+    (struct popup_file_dialog_data *) ptr;
+  BFilePanel *panel = data->panel;
+  delete panel;
+  delete data->entry;
+  delete data->msg;
+}
+
+static void
+be_popup_file_dialog_safe_set_target (BFilePanel *dialog, BWindow *window)
+{
+  dialog->SetTarget (BMessenger (window));
+}
+
+char *
+be_popup_file_dialog (int open_p, const char *default_dir, int must_match_p, int dir_only_p,
+		      void *window, const char *save_text, const char *prompt,
+		      void (*block_input_function) (void),
+		      void (*unblock_input_function) (void))
+{
+  ptrdiff_t idx = c_specpdl_idx_from_cxx ();
+  /* setjmp/longjmp is UB with automatic objects. */
+  block_input_function ();
+  BWindow *w = (BWindow *) window;
+  uint32_t mode = dir_only_p ? B_DIRECTORY_NODE : B_FILE_NODE | B_DIRECTORY_NODE;
+  BEntry *path = new BEntry;
+  BMessage *msg = new BMessage ('FPSE');
+  BFilePanel *panel = new BFilePanel (open_p ? B_OPEN_PANEL : B_SAVE_PANEL,
+				      NULL, NULL, mode);
+  unblock_input_function ();
+
+  struct popup_file_dialog_data dat;
+  dat.entry = path;
+  dat.msg = msg;
+  dat.panel = panel;
+
+  record_c_unwind_protect_from_cxx (unwind_popup_file_dialog, &dat);
+  if (default_dir)
+    {
+      if (path->SetTo (default_dir, 0) != B_OK)
+	default_dir = NULL;
+    }
+
+  panel->SetMessage (msg);
+  if (default_dir)
+    panel->SetPanelDirectory (path);
+  if (save_text)
+    panel->SetSaveText (save_text);
+  panel->SetHideWhenDone (0);
+  panel->Window ()->SetTitle (prompt);
+  be_popup_file_dialog_safe_set_target (panel, w);
+
+  panel->Show ();
+
+  void *buf = alloca (200);
+  while (1)
+    {
+      enum haiku_event_type type;
+      char *ptr = NULL;
+
+      if (!haiku_read_with_timeout (&type, buf, 200, 100000))
+	{
+	  if (type != FILE_PANEL_EVENT)
+	    haiku_write (type, buf);
+	  else if (type != ACTIVATION)
+	    {
+	      if (!ptr)
+		ptr = (char *) ((struct haiku_file_panel_event *) buf)->ptr;
+	    }
+	}
+
+      ssize_t b_s;
+      haiku_read_size (&b_s);
+      if (!b_s || b_s == -1 || ptr)
+	{
+	  c_unbind_to_nil_from_cxx (idx);
+	  return ptr;
+	}
+    }
+
+  emacs_abort ();
+  return NULL;
+}
+
+void
+be_app_quit (void)
+{
+  if (be_app)
+    be_app->PostMessage (B_QUIT_REQUESTED);
+}
+
+void
+EmacsView_do_visible_bell (void *view, uint32_t color)
+{
+  EmacsView *vw = (EmacsView *) view;
+  vw->DoVisibleBell (color);
+}
+
+void
+BWindow_zoom (void *window)
+{
+  BWindow *w = (BWindow *) window;
+  w->Zoom ();
+}
+
+void
+EmacsWindow_make_fullscreen (void *window, int fullscreen_p)
+{
+  EmacsWindow *w = (EmacsWindow *) window;
+  w->MakeFullscreen (fullscreen_p);
+}
+
+void
+EmacsWindow_unzoom (void *window)
+{
+  EmacsWindow *w = (EmacsWindow *) window;
+  w->UnZoom ();
+}
+
+void
+BMenuBar_start_tracking (void *mbar)
+{
+  EmacsMenuBar *mb = (EmacsMenuBar *) mbar;
+  if (!mb->LockLooper ())
+    emacs_abort ();
+  BRect frame = mb->Frame ();
+  BPoint pt = frame.LeftTop ();
+  BPoint l = pt;
+  mb->Parent ()->ConvertToScreen (&pt);
+  set_mouse_position (pt.x, pt.y);
+  mb->MouseDown (l);
+  mb->UnlockLooper ();
+}
+
+#ifdef HAVE_NATIVE_IMAGE_API
+int
+be_can_translate_type_to_bitmap_p (const char *mime)
+{
+  BTranslatorRoster *r = BTranslatorRoster::Default ();
+  translator_id *ids;
+  int32 id_len;
+
+  if (r->GetAllTranslators (&ids, &id_len) != B_OK)
+    return 0;
+
+  int found_in = 0;
+  int found_out = 0;
+
+  for (int i = 0; i < id_len; ++i)
+    {
+      found_in = 0;
+      found_out = 0;
+      const translation_format *i_fmts;
+      const translation_format *o_fmts;
+
+      int32 i_count, o_count;
+
+      if (r->GetInputFormats (ids[i], &i_fmts, &i_count) != B_OK)
+	continue;
+
+      if (r->GetOutputFormats (ids[i], &o_fmts, &o_count) != B_OK)
+	continue;
+
+      for (int x = 0; x < i_count; ++x)
+	{
+	  if (!strcmp (i_fmts[x].MIME, mime))
+	    {
+	      found_in = 1;
+	      break;
+	    }
+	}
+
+      for (int x = 0; x < i_count; ++x)
+	{
+	  if (!strcmp (o_fmts[x].MIME, "image/x-be-bitmap") ||
+	      !strcmp (o_fmts[x].MIME, "image/x-vnd.Be-bitmap"))
+	    {
+	      found_out = 1;
+	      break;
+	    }
+	}
+
+      if (found_in && found_out)
+	break;
+    }
+
+  delete [] ids;
+
+  return found_in && found_out;
+}
+
+void *
+be_translate_bitmap_from_file_name (const char *filename)
+{
+  BBitmap *bm = BTranslationUtils::GetBitmap (filename);
+  return bm;
+}
+
+void *
+be_translate_bitmap_from_memory (const void *buf, size_t bytes)
+{
+  BMemoryIO io (buf, bytes);
+  BBitmap *bm = BTranslationUtils::GetBitmap (&io);
+  return bm;
+}
+#endif
+
+size_t
+BBitmap_bytes_length (void *bitmap)
+{
+  BBitmap *bm = (BBitmap *) bitmap;
+  return bm->BitsLength ();
+}
+
+void
+BView_show_tooltip (void *view)
+{
+  BView *vw = (BView *) view;
+  vw->ShowToolTip ();
+}
diff --git a/src/haiku_support.h b/src/haiku_support.h
new file mode 100644
index 0000000000..3074f829d7
--- /dev/null
+++ b/src/haiku_support.h
@@ -0,0 +1,1014 @@
+/* Haiku window system support.  Hey Emacs, this is -*- C++ -*-
+   Copyright (C) 2021 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/>.  */
+
+#ifndef _HAIKU_SUPPORT_H
+#define _HAIKU_SUPPORT_H
+
+#include <stdint.h>
+
+#ifdef HAVE_FREETYPE
+#include <ft2build.h>
+#include <fontconfig/fontconfig.h>
+#include FT_FREETYPE_H
+#include FT_SIZES_H
+#endif
+
+enum haiku_cursor
+  {
+    CURSOR_ID_NO_CURSOR = 12,
+    CURSOR_ID_RESIZE_NORTH = 15,
+    CURSOR_ID_RESIZE_EAST = 16,
+    CURSOR_ID_RESIZE_SOUTH = 17,
+    CURSOR_ID_RESIZE_WEST = 18,
+    CURSOR_ID_RESIZE_NORTH_EAST = 19,
+    CURSOR_ID_RESIZE_NORTH_WEST = 20,
+    CURSOR_ID_RESIZE_SOUTH_EAST = 21,
+    CURSOR_ID_RESIZE_SOUTH_WEST = 22,
+    CURSOR_ID_RESIZE_NORTH_SOUTH = 23,
+    CURSOR_ID_RESIZE_EAST_WEST = 24,
+    CURSOR_ID_RESIZE_NORTH_EAST_SOUTH_WEST = 25,
+    CURSOR_ID_RESIZE_NORTH_WEST_SOUTH_EAST = 26
+  };
+
+enum haiku_alert_type
+  {
+    HAIKU_EMPTY_ALERT = 0,
+    HAIKU_INFO_ALERT,
+    HAIKU_IDEA_ALERT,
+    HAIKU_WARNING_ALERT,
+    HAIKU_STOP_ALERT
+  };
+
+enum haiku_event_type
+  {
+    QUIT_REQUESTED,
+    FRAME_RESIZED,
+    FRAME_EXPOSED,
+    KEY_DOWN,
+    KEY_UP,
+    ACTIVATION,
+    MOUSE_MOTION,
+    BUTTON_DOWN,
+    BUTTON_UP,
+    ICONIFICATION,
+    MOVE_EVENT,
+    SCROLL_BAR_VALUE_EVENT,
+    SCROLL_BAR_DRAG_EVENT,
+    WHEEL_MOVE_EVENT,
+    MENU_BAR_RESIZE,
+    MENU_BAR_OPEN,
+    MENU_BAR_SELECT_EVENT,
+    MENU_BAR_CLOSE,
+    FILE_PANEL_EVENT,
+    MENU_BAR_HELP_EVENT,
+    ZOOM_EVENT
+  };
+
+struct haiku_quit_requested_event
+{
+  void *window;
+};
+
+struct haiku_resize_event
+{
+  void *window;
+  float px_heightf;
+  float px_widthf;
+};
+
+struct haiku_expose_event
+{
+  void *window;
+  int x;
+  int y;
+  int width;
+  int height;
+};
+
+#define HAIKU_MODIFIER_ALT (1)
+#define HAIKU_MODIFIER_CTRL (1 << 1)
+#define HAIKU_MODIFIER_SHIFT (1 << 2)
+
+struct haiku_key_event
+{
+  void *window;
+  int modifiers;
+  uint32_t mb_char;
+  uint32_t unraw_mb_char;
+  short kc;
+};
+
+struct haiku_activation_event
+{
+  void *window;
+  int activated_p;
+};
+
+struct haiku_mouse_motion_event
+{
+  void *window;
+  bool just_exited_p;
+  int x;
+  int y;
+  uint32_t be_code;
+};
+
+struct haiku_button_event
+{
+  void *window;
+  int btn_no;
+  int modifiers;
+  int x;
+  int y;
+};
+
+struct haiku_iconification_event
+{
+  void *window;
+  int iconified_p;
+};
+
+struct haiku_move_event
+{
+  void *window;
+  int x;
+  int y;
+};
+
+struct haiku_wheel_move_event
+{
+  void *window;
+  int modifiers;
+  float delta_x;
+  float delta_y;
+};
+
+struct haiku_menu_bar_select_event
+{
+  void *window;
+  void *ptr;
+};
+
+struct haiku_file_panel_event
+{
+  void *ptr;
+};
+
+struct haiku_menu_bar_help_event
+{
+  void *window;
+  int mb_idx;
+};
+
+struct haiku_zoom_event
+{
+  void *window;
+  int x;
+  int y;
+  int width;
+  int height;
+};
+
+#define FSPEC_FAMILY 1
+#define FSPEC_STYLE (1 << 1)
+#define FSPEC_SLANT (1 << 2)
+#define FSPEC_WEIGHT (1 << 3)
+#define FSPEC_SPACING (1 << 4)
+#define FSPEC_WANTED (1 << 5)
+#define FSPEC_NEED_ONE_OF (1 << 6)
+#define FSPEC_WIDTH (1 << 7)
+#define FSPEC_LANGUAGE (1 << 8)
+
+typedef char haiku_font_family_or_style[64];
+
+enum haiku_font_slant
+  {
+    NO_SLANT = -1,
+    SLANT_OBLIQUE,
+    SLANT_REGULAR,
+    SLANT_ITALIC
+  };
+
+enum haiku_font_width
+  {
+    NO_WIDTH = -1,
+    ULTRA_CONDENSED,
+    EXTRA_CONDENSED,
+    CONDENSED,
+    SEMI_CONDENSED,
+    NORMAL_WIDTH,
+    SEMI_EXPANDED,
+    EXPANDED,
+    EXTRA_EXPANDED,
+    ULTRA_EXPANDED
+  };
+
+enum haiku_font_language
+  {
+    LANGUAGE_CN,
+    LANGUAGE_KO,
+    LANGUAGE_JP,
+    MAX_LANGUAGE /* This isn't a language. */
+  };
+
+struct haiku_font_pattern
+{
+  int specified;
+  struct haiku_font_pattern *next;
+  /* The next two fields are only temporarily used during the font
+     discovery process! Do not rely on it being correct outside
+     BFont_find. */
+  struct haiku_font_pattern *last;
+  struct haiku_font_pattern *next_family;
+  haiku_font_family_or_style family;
+  haiku_font_family_or_style style;
+  int weight;
+  int mono_spacing_p;
+  int want_chars_len;
+  int need_one_of_len;
+  enum haiku_font_slant slant;
+  enum haiku_font_width width;
+  enum haiku_font_language language;
+  uint32_t *wanted_chars;
+  uint32_t *need_one_of;
+
+  int oblique_seen_p;
+};
+
+struct haiku_scroll_bar_value_event
+{
+  void *scroll_bar;
+  int position;
+};
+
+struct haiku_scroll_bar_drag_event
+{
+  void *scroll_bar;
+  int dragging_p;
+};
+
+struct haiku_menu_bar_resize_event
+{
+  void *window;
+  int width;
+  int height;
+};
+
+struct haiku_menu_bar_state_event
+{
+  void *window;
+};
+
+#define HAIKU_THIN 0
+#define HAIKU_ULTRALIGHT 20
+#define HAIKU_EXTRALIGHT 40
+#define HAIKU_LIGHT 50
+#define HAIKU_SEMI_LIGHT 75
+#define HAIKU_REGULAR 100
+#define HAIKU_SEMI_BOLD 180
+#define HAIKU_BOLD 200
+#define HAIKU_EXTRA_BOLD 205
+#define HAIKU_ULTRA_BOLD 210
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+#include <pthread.h>
+#include <OS.h>
+
+#ifdef __cplusplus
+  typedef void *haiku;
+
+  extern void
+  haiku_put_pixel (haiku bitmap, int x, int y, unsigned long pixel);
+
+  extern unsigned long
+  haiku_get_pixel (haiku bitmap, int x, int y);
+#endif
+
+  /* The port used to send messages from the application thread to
+     Emacs. */
+  extern port_id port_application_to_emacs;
+
+  extern void haiku_io_init (void);
+  extern void haiku_io_init_in_app_thread (void);
+
+  /* Read the size of the next message into len, returning -1 if the
+     query fails or there is no next message */
+  extern void
+  haiku_read_size (ssize_t *len);
+
+  /* Read the next message into buf, putting its type into type,
+     assuming the message is len long.  Return 0 if successful and
+     -1 if the read fails. */
+  extern int
+  haiku_read (enum haiku_event_type *type, void *buf, ssize_t len);
+
+  /* The same as haiku_read, but time out after TIMEOUT microseconds.
+     Input is blocked when an attempt to read is in progress. */
+  extern int
+  haiku_read_with_timeout (enum haiku_event_type *type, void *buf, ssize_t len,
+			   time_t timeout);
+
+  /* Write a message with type type into buf. */
+  extern int
+  haiku_write (enum haiku_event_type type, void *buf);
+
+  /* Ditto, but without signalling the Emacs thread. */
+  extern int
+  haiku_write_without_signal (enum haiku_event_type type, void *buf);
+
+  /* Convert RGB32 color color from RGB color space to its
+     HSL components pointed to by h, s and l. */
+  extern void
+  rgb_color_hsl (uint32_t rgb, double *h, double *s, double *l);
+
+  /* Ditto, but the other way round */
+  extern void
+  hsl_color_rgb (double h, double s, double l, uint32_t *rgb);
+
+  /* Create new bitmap in RGB32 format */
+  extern void *
+  BBitmap_new (int width, int height, int mono_p);
+
+  /* Take bitmap, a reference to a BBitmap, and return a pointer to
+     its data, in RGB32 format */
+  extern void *
+  BBitmap_data (void *bitmap);
+
+  /* Convert bitmap if required, placing the new bitmap in new_bitmap,
+     and return non-null if bitmap was successfully converted.
+     Bitmaps should be freed with `BBitmap_free' */
+  extern int
+  BBitmap_convert (void *bitmap, void **new_bitmap);
+
+  /* Delete bitmap */
+  extern void
+  BBitmap_free (void *bitmap);
+
+  /* Retrieve the dimensions of bitmap */
+  extern void
+  BBitmap_dimensions (void *bitmap, int *left, int *top,
+		      int *right, int *bottom, int32_t *bytes_per_row,
+		      int *mono_p);
+
+  /* Set up an application and return it.  If starting the application
+     thread fails, abort Emacs */
+  extern void *
+  BApplication_setup (void);
+
+  /* Set up and return a window with its view put in VIEW */
+  extern void *
+  BWindow_new (void *view);
+
+  /* Delete a window */
+  extern void
+  BWindow_quit (void *window);
+
+  /* Set window offset to X, Y */
+  extern void
+  BWindow_set_offset (void *window, int x, int y);
+
+  /* Iconify window */
+  extern void
+  BWindow_iconify (void *window);
+
+  /* Show or hide window */
+  extern void
+  BWindow_set_visible (void *window, int visible_p);
+
+  /* Open the default monospace font */
+  extern void *
+  BFont_open_default (double size, int plain_or_bold_or_fixed);
+
+  /* Close a font */
+  extern void
+  BFont_close (void *font);
+
+  /* Compute font data */
+  extern void
+  BFont_dat (void *font, int *px_size, int *min_width, int *max_width,
+	     int *avg_width, int *height, int *space_width, int *ascent,
+	     int *descent, int *underline_position, int *underline_thickness);
+
+  /* Return non-null if font contains chr, a Unicode code-point */
+  extern int
+  BFont_have_char_p (void *font, int32_t chr);
+
+  /* Compute bounds for mb_str, a character in multibyte encoding,
+     used with font.  The width (in pixels) is returned in advance,
+     the left bearing in lb, and the right bearing in rb */
+  extern void
+  BFont_char_bounds (void *font, const char *mb_str, int *advance,
+		     int *lb, int *rb);
+
+  /* The same, but for a variable amount of chars. */
+  extern void
+  BFont_nchar_bounds (void *font, const char *mb_str, int *advance,
+		      int *lb, int *rb, int32_t n);
+
+  /* Change the title of window to the multibyte string title */
+  extern void
+  BWindow_retitle (void *window, const char *title);
+
+  /* Resize window to width by height */
+  extern void
+  BWindow_resize (void *window, int width, int height);
+
+  /* Activate window */
+  extern void
+  BWindow_activate (void *window);
+
+  /* Drawing functions */
+  extern void
+  BView_StartClip (void *view);
+
+  extern void
+  BView_EndClip (void *view);
+
+  extern void
+  BView_SetHighColor (void *view, uint32_t color);
+
+  extern void
+  BView_SetHighColorForVisibleBell (void *view, uint32_t color);
+
+  extern void
+  BView_FillRectangleForVisibleBell (void *view, int x, int y, int width, int height);
+
+  extern void
+  BView_SetLowColor (void *view, uint32_t color);
+
+  extern void
+  BView_SetPenSize (void *view, int u);
+
+  extern void
+  BView_SetFont (void *view, void *font);
+
+  extern void
+  BView_MovePenTo (void *view, int x, int y);
+
+  extern void
+  BView_DrawString (void *view, const char *chr, ptrdiff_t len);
+
+  extern void
+  BView_DrawChar (void *view, char chr);
+
+  extern void
+  BView_Draw (void *view, int x, int y, int width, int height);
+
+  extern void
+  BView_FillRectangle (void *view, int x, int y, int width, int height);
+
+  extern void
+  BView_FillRectangleAbs (void *view, int x, int y, int x1, int y1);
+
+  extern void
+  BView_StrokeRectangle (void *view, int x, int y, int width, int height);
+
+  extern void
+  BView_SetViewColor (void *view, uint32_t color);
+
+  extern void
+  BView_ClipToRect (void *view, int x, int y, int width, int height);
+
+  extern void
+  BView_ClipToInverseRect (void *view, int x, int y, int width, int height);
+
+  extern void
+  BView_StrokeLine (void *view, int sx, int sy, int tx, int ty);
+
+  extern void
+  BView_CopyBits (void *view, int x, int y, int width, int height,
+		  int tox, int toy, int towidth, int toheight);
+
+  extern void
+  BView_DrawBitmap (void *view, void *bitmap, int x, int y,
+		    int width, int height, int vx, int vy, int vwidth,
+		    int vheight);
+
+  extern void
+  BView_DrawBitmapWithEraseOpAbs (void *view, void *bitmap, int x, int y,
+				  int width, int height, int vx, int vy, int vwidth,
+				  int vheight);
+
+  extern void
+  BView_DrawBitmapWithEraseOp (void *view, void *bitmap, int x,
+			       int y, int width, int height);
+
+  /* Potential Haiku bug: the Be Book says B_OP_ERASE ought to treat
+     monochrome BBitmaps as patterns, but that isn't the case here, so
+     we have to do everything manually. */
+  extern void
+  BView_DrawFringeBitmap (void *src, void *view,
+			  int x, int y, int width, int height,
+			  uint32_t color);
+
+  extern void
+  BView_DrawMask (void *src, void *view,
+		  int x, int y, int width, int height,
+		  int vx, int vy, int vwidth, int vheight,
+		  uint32_t color);
+
+  extern void *
+  BBitmap_transform_bitmap (void *bitmap, void *mask, uint32_t m_color,
+			    double rot, int desw, int desh);
+
+  extern void
+  BView_EnableWindowUpdates (void *view);
+
+  extern void
+  BView_DisableWindowUpdates (void *view);
+
+  extern void
+  BView_BeginLayer (void *view, float opacity);
+
+  extern void
+  BView_EndLayer (void *view);
+
+  /* Return the pixel dimensions of the main screen in width and height */
+  extern void
+  BScreen_px_dim (int *width, int *height);
+
+  /* Resize view to width, height */
+  extern void
+  BView_resize_to (void *view, int width, int height);
+
+  /* Functions for creating and freeing cursors */
+  extern void *
+  BCursor_create_default (void);
+
+  extern void *
+  BCursor_from_id (enum haiku_cursor cursor);
+
+  extern void *
+  BCursor_create_i_beam (void);
+
+  extern void *
+  BCursor_create_progress_cursor (void);
+
+  extern void *
+  BCursor_create_grab (void);
+
+  /* Delete cursor */
+  extern void
+  BCursor_delete (void *cursor);
+
+  /* Set view's cursor to cursor */
+  extern void
+  BView_set_view_cursor (void *view, void *cursor);
+
+  /* Flush window's connection to the App Server */
+  extern void
+  BWindow_Flush (void *window);
+
+  /* Map the keycode kc, storing the result in code and 1 in
+     non_ascii_p if it should be used */
+  extern void
+  BMapKey (uint32_t kc, int *non_ascii_p, unsigned *code);
+
+  /* Make a scrollbar, attach it to view's window, and return it */
+  extern void *
+  BScrollBar_make_for_view (void *view, int horizontal_p,
+			    int x, int y, int x1, int y1,
+			    void *scroll_bar_ptr);
+
+  /* Delete sb, a scrollbar */
+  extern void
+  BScrollBar_delete (void *sb);
+
+  /* Move view's frame to x, y, x1, y1 */
+  extern void
+  BView_move_frame (void *view, int x, int y, int x1, int y1);
+
+  /* Update sb with portion, whole and position */
+  extern void
+  BView_scroll_bar_update (void *sb, int portion, int whole, int position);
+
+  /* Return the default scrollbar size  */
+  extern int
+  BScrollBar_default_size (int horizontal_p);
+
+  /* Make view invisible */
+  extern void
+  BView_hide (void *view);
+
+  /* Make view visible */
+  extern void
+  BView_show (void *view);
+
+  /* Invalidate view */
+  extern void
+  BView_invalidate (void *view);
+
+  /* Lock view.  This is usually faster than calling LockLooper
+     directly. */
+  extern void
+  BView_draw_lock (void *view);
+
+  /* Ditto, but unlock instead. */
+  extern void
+  BView_draw_unlock (void *view);
+
+  /* Center window on its screen */
+  extern void
+  BWindow_center_on_screen (void *window);
+
+  /* Tell view that the mouse has moved to x by y */
+  extern void
+  BView_mouse_moved (void *view, int x, int y, uint32_t transit);
+
+  /* Tell view it has been clicked at x by y */
+  extern void
+  BView_mouse_down (void *view, int x, int y);
+
+  /* Tell view it has been released at x by y */
+  extern void
+  BView_mouse_up (void *view, int x, int y);
+
+  /* Import bits into bitmap using the B_GRAY1 colorspace */
+  extern void
+  BBitmap_import_mono_bits (void *bitmap, void *bits, int wd, int h);
+
+  /* Return the amount of font families */
+  extern int
+  BFont_family_count (void);
+
+  /* Query the font family at IDX, returning non-0 on failure */
+  extern int
+  BFont_family (int idx, char *f, int *fixed_p);
+
+  /* Return non-0 if the family contains style */
+  extern int
+  BFont_family_has_style_p (haiku_font_family_or_style family,
+			    haiku_font_family_or_style style);
+
+  /* Create a font */
+  extern void *
+  BFont_new (void);
+
+  /* Set font's family and style to FAMILY and STYLE respectively,
+     returning non-0 on failure.  */
+  extern int
+  BFont_set_family_and_style (void *font, haiku_font_family_or_style family,
+			      haiku_font_family_or_style style);
+
+  /* Set FONT's family along with its ITALIC and BOLD faces */
+  extern int
+  BFont_set_family_face (void *font, haiku_font_family_or_style family,
+			 int italic_p, int bold_p);
+
+  /* Delete every element of the font pattern PT */
+  extern void
+  haiku_font_pattern_free (struct haiku_font_pattern *pt);
+
+  /* Find all fonts matching the font pattern PT */
+  extern struct haiku_font_pattern *
+  BFont_find (struct haiku_font_pattern *pt);
+
+  /* Find and open a font matching the pattern PAT, which must have
+     its family set */
+  extern int
+  BFont_open_pattern (struct haiku_font_pattern *pat, void **font, float size);
+
+  /* Query the family of the default fixed font */
+  extern void
+  BFont_populate_fixed_family (struct haiku_font_pattern *ptn);
+
+  /* Ditto, but with the plain family */
+  extern void
+  BFont_populate_plain_family (struct haiku_font_pattern *ptn);
+
+  /* Make a scrollbar at x, y known to the view */
+  extern void
+  BView_publish_scroll_bar (void *view, int x, int y, int width, int height);
+
+  /* Forget the scrollbar at x, y by width, height */
+  extern void
+  BView_forget_scroll_bar (void *view, int x, int y, int width, int height);
+
+  /* Place the current coordinates of the mouse, relative to view, at x and y */
+  extern void
+  BView_get_mouse (void *view, int *x, int *y);
+
+  /* Perform an in-place conversion of x and y from view's coordinate
+     system to its screen's coordinate system */
+  extern void
+  BView_convert_to_screen (void *view, int *x, int *y);
+
+  /* Do the same, but from the screen coordinate system to the view's. */
+  extern void
+  BView_convert_from_screen (void *view, int *x, int *y);
+
+  /* Decorate or undecorate window depending on decorate_p */
+  extern void
+  BWindow_change_decoration (void *window, int decorate_p);
+
+  /* Decorate window appropriately for use as a tooltip */
+  extern void
+  BWindow_set_tooltip_decoration (void *window);
+
+  /* Set B_AVOID_FOCUS on window if avoid_focus_p is non-nil, or clear
+     it otherwise */
+  extern void
+  BWindow_set_avoid_focus (void *window, int avoid_focus_p);
+
+  /* Delete the frame view view */
+  extern void
+  BView_emacs_delete (void *view);
+
+  /* Begin a view transaction, during which drawing operations will
+     not be displayed */
+  extern void
+  BView_BeginTransaction (void *view);
+
+  /* End a view transaction and flush the view */
+  extern void
+  BView_EndTransaction (void *view);
+
+  /* Return the current workspace */
+  extern uint32_t
+  haiku_current_workspace (void);
+
+  /* Return a bitmask consisting of workspaces window is on */
+  extern uint32_t
+  BWindow_workspaces (void *window);
+
+  /* Create a popup menu */
+  extern void *
+  BPopUpMenu_new (const char *name);
+
+  /* Add an item to the menu */
+  extern void
+  BMenu_add_item (void *menu, const char *label, void *ptr, bool enabled_p,
+		  bool marked_p, bool mbar_p, void *mbw_ptr, const char *key);
+
+  /* Add a separator to the menu */
+  extern void
+  BMenu_add_separator (void *menu);
+
+  /* Create a submenu and attach it to menu */
+  extern void *
+  BMenu_new_submenu (void *menu, const char *label, bool enabled_p);
+
+  /* Create a submenu that notifies Emacs upon opening */
+  extern void *
+  BMenu_new_menu_bar_submenu (void *menu, const char *label);
+
+  /* Count items in menu */
+  extern int
+  BMenu_count_items (void *menu);
+
+  /* Find the menu item at idx */
+  extern void *
+  BMenu_item_at (void *menu, int idx);
+
+  /* Run menu, waiting for it to close, and returning a pointer to the
+     data of the selected item (if one exists), or nil.  X, Y should
+     be in the screen coordinate system */
+  extern void *
+  BMenu_run (void *menu, int x, int y);
+
+  /* Delete the entire menu hierarchy of menu, and then delete menu
+     itself */
+  extern void
+  BPopUpMenu_delete (void *menu);
+
+  /* Create a menubar, attach it to view, and return it */
+  extern void *
+  BMenuBar_new (void *view);
+
+  /* Delete all items from menu */
+  extern void
+  BMenu_delete_all (void *menu);
+
+  /* Delete menubar along with all subitems */
+  extern void
+  BMenuBar_delete (void *menubar);
+
+  /* Set item's label to label */
+  extern void
+  BMenu_item_set_label (void *item, const char *label);
+
+  /* Get item's menu */
+  extern void *
+  BMenu_item_get_menu (void *item);
+
+  /* Delete count items from menu starting from start */
+  extern void
+  BMenu_delete_from (void *menu, int start, int count);
+
+  /* Emit a beep noise */
+  extern void
+  haiku_ring_bell (void);
+
+  /* Create a BAlert with text */
+  extern void *
+  BAlert_new (const char *text, enum haiku_alert_type type);
+
+  /* Add a button to alert and return the button */
+  extern void *
+  BAlert_add_button (void *alert, const char *text);
+
+  /* Run the alert, returning the number of the button that was
+     selected, or -1 if no button was selected before the alert was
+     closed */
+  extern int32_t
+  BAlert_go (void *alert);
+
+  /* Enable or disable button depending on enabled_p */
+  extern void
+  BButton_set_enabled (void *button, int enabled_p);
+
+  /* Set view's tooltip to tooltip */
+  extern void
+  BView_set_tooltip (void *view, const char *tooltip);
+
+  /* Delete alert */
+  extern void
+  BAlert_delete (void *alert);
+
+#ifdef HAVE_FREETYPE
+  /* Render GLYPHS in FACE */
+  extern void
+  BView_FT_render_glyphs (FT_Face face, unsigned int *codes,
+			  int len, uint32_t color, int x, int y,
+			  void *view, void *ft_shape_cache);
+
+  /* Create and free shape caches. */
+  extern void
+  be_free_ft_shape_cache (void *c);
+
+  extern void *
+  be_make_ft_shape_cache (void);
+#endif
+
+  /* Place the resolution of the monitor in DPI in RSSX and RSSY. */
+  extern void
+  BScreen_res (double *rrsx, double *rrsy);
+
+  /* Add WINDOW to OTHER_WINDOW's subset and parent it to OTHER_WINDOW. */
+  extern void
+  EmacsWindow_parent_to (void *window, void *other_window);
+
+  /* Unparent WINDOW. */
+  extern void
+  EmacsWindow_unparent (void *window);
+
+  extern int
+  BFont_string_width (void *font, const char *utf8);
+
+  /* Place text describing the current version of Haiku in VERSION,
+     which should be a buffer LEN bytes wide. */
+  extern void
+  be_get_version_string (char *version, int len);
+
+  /* Return the amount of color planes in the current display. */
+  extern int
+  be_get_display_planes (void);
+
+  /* Return the amount of colors the display can handle. */
+  extern int
+  be_get_display_color_cells (void);
+
+  /* Warp the pointer to X by Y. */
+  extern void
+  be_warp_pointer (int x, int y);
+
+  /* Update the position of CHILD in WINDOW without actually moving it. */
+  extern void
+  EmacsWindow_move_weak_child (void *window, void *child, int xoff, int yoff);
+
+  /* Set up double buffering for VW. */
+  extern void
+  EmacsView_set_up_double_buffering (void *vw);
+
+  /* Disable double buffering for VW. */
+  extern void
+  EmacsView_disable_double_buffering (void *vw);
+
+  /* Flip and invalidate the view VW. */
+  extern void
+  EmacsView_flip_and_blit (void *vw);
+
+  /* Return non-0 if VW is double-buffered. */
+  extern int
+  EmacsView_double_buffered_p (void *vw);
+
+  /* Popup a file dialog. */
+  extern char *
+  be_popup_file_dialog (int open_p, const char *default_dir, int must_match_p, int dir_only_p,
+			void *window, const char *save_text, const char *prompt,
+			void (*block_input_function) (void),
+			void (*unblock_input_function) (void));
+
+  /* Record an unwind protect from C++ code. */
+  extern void
+  record_c_unwind_protect_from_cxx (void (*) (void *), void *);
+
+  /* SPECPDL_IDX that is safe from C++ code. */
+  extern ptrdiff_t
+  c_specpdl_idx_from_cxx (void);
+
+  /* unbind_to (idx, Qnil), but safe from C++ code. */
+  extern void
+  c_unbind_to_nil_from_cxx (ptrdiff_t idx);
+
+  /* Temporarily fill VIEW with COLOR. */
+  extern void
+  EmacsView_do_visible_bell (void *view, uint32_t color);
+
+  /* Zoom WINDOW. */
+  extern void
+  BWindow_zoom (void *window);
+
+  /* Make WINDOW fullscreen if FULLSCREEN_P. */
+  extern void
+  EmacsWindow_make_fullscreen (void *window, int fullscreen_p);
+
+  /* Unzoom (maximize) WINDOW. */
+  extern void
+  EmacsWindow_unzoom (void *window);
+
+#ifdef HAVE_NATIVE_IMAGE_API
+  extern int
+  be_can_translate_type_to_bitmap_p (const char *mime);
+
+  extern void *
+  be_translate_bitmap_from_file_name (const char *filename);
+
+  extern void *
+  be_translate_bitmap_from_memory (const void *buf, size_t bytes);
+#endif
+  /* Move the pointer into MBAR and start tracking. */
+  extern void
+  BMenuBar_start_tracking (void *mbar);
+
+  /* Return the size of BITMAP's data, in bytes. */
+  extern size_t
+  BBitmap_bytes_length (void *bitmap);
+
+  /* Show VIEW's tooltip at the current mouse position. */
+  extern void
+  BView_show_tooltip (void *view);
+#ifdef __cplusplus
+  /* Find an appropriate view to draw onto. */
+  extern void *
+  find_appropriate_view_for_draw (void *vw);
+}
+#endif /* _cplusplus */
+
+/* Borrowed from X.Org keysymdef.h */
+#define XK_BackSpace                     0xff08  /* Back space, back char */
+#define XK_Tab                           0xff09
+#define XK_Linefeed                      0xff0a  /* Linefeed, LF */
+#define XK_Clear                         0xff0b
+#define XK_Return                        0xff0d  /* Return, enter */
+#define XK_Pause                         0xff13  /* Pause, hold */
+#define XK_Scroll_Lock                   0xff14
+#define XK_Sys_Req                       0xff15
+#define XK_Escape                        0xff1b
+#define XK_Delete                        0xffff  /* Delete, rubout */
+#define XK_Home                          0xff50
+#define XK_Left                          0xff51  /* Move left, left arrow */
+#define XK_Up                            0xff52  /* Move up, up arrow */
+#define XK_Right                         0xff53  /* Move right, right arrow */
+#define XK_Down                          0xff54  /* Move down, down arrow */
+#define XK_Prior                         0xff55  /* Prior, previous */
+#define XK_Page_Up                       0xff55
+#define XK_Next                          0xff56  /* Next */
+#define XK_Page_Down                     0xff56
+#define XK_End                           0xff57  /* EOL */
+#define XK_Begin                         0xff58  /* BOL */
+#define XK_Select                        0xff60  /* Select, mark */
+#define XK_Print                         0xff61
+#define XK_Execute                       0xff62  /* Execute, run, do */
+#define XK_Insert                        0xff63  /* Insert, insert here */
+#define XK_Undo                          0xff65
+#define XK_Redo                          0xff66  /* Redo, again */
+#define XK_Menu                          0xff67
+#define XK_Find                          0xff68  /* Find, search */
+#define XK_Cancel                        0xff69  /* Cancel, stop, abort, exit */
+#define XK_Help                          0xff6a  /* Help */
+#define XK_Break                         0xff6b
+#define XK_Mode_switch                   0xff7e  /* Character set switch */
+#define XK_script_switch                 0xff7e  /* Alias for mode_switch */
+#define XK_Num_Lock                      0xff7f
+#define XK_F1                            0xffbe
+
+#endif /* _HAIKU_SUPPORT_H_ */
diff --git a/src/haikufns.c b/src/haikufns.c
new file mode 100644
index 0000000000..438c815dd5
--- /dev/null
+++ b/src/haikufns.c
@@ -0,0 +1,2388 @@
+/* Haiku window system support
+   Copyright (C) 2021 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 <math.h>
+
+#include "lisp.h"
+#include "frame.h"
+#include "blockinput.h"
+#include "termchar.h"
+#include "font.h"
+#include "keyboard.h"
+#include "buffer.h"
+#include "dispextern.h"
+
+#include "haikugui.h"
+#include "haikuterm.h"
+#include "haiku_support.h"
+#include "termhooks.h"
+
+#define RGB_TO_ULONG(r, g, b) \
+  (((r) << 16) | ((g) << 8) | (b));
+#define RED_FROM_ULONG(color)	(((color) >> 16) & 0xff)
+#define GREEN_FROM_ULONG(color)	(((color) >> 8) & 0xff)
+#define BLUE_FROM_ULONG(color)	((color) & 0xff)
+
+/* The frame of the currently visible tooltip.  */
+static Lisp_Object tip_frame;
+
+/* The window-system window corresponding to the frame of the
+   currently visible tooltip.  */
+static Window tip_window;
+
+/* A timer that hides or deletes the currently visible tooltip when it
+   fires.  */
+static Lisp_Object tip_timer;
+
+/* STRING argument of last `x-show-tip' call.  */
+static Lisp_Object tip_last_string;
+
+/* Normalized FRAME argument of last `x-show-tip' call.  */
+static Lisp_Object tip_last_frame;
+
+/* PARMS argument of last `x-show-tip' call.  */
+static Lisp_Object tip_last_parms;
+
+static void
+haiku_explicitly_set_name (struct frame *f, Lisp_Object arg, Lisp_Object oldval);
+static void
+haiku_set_title (struct frame *f, Lisp_Object name, Lisp_Object old_name);
+
+static ptrdiff_t image_cache_refcount;
+
+static Lisp_Object
+get_geometry_from_preferences (struct haiku_display_info *dpyinfo,
+                               Lisp_Object parms)
+{
+  struct {
+    const char *val;
+    const char *cls;
+    Lisp_Object tem;
+  } r[] = {
+    { "width",  "Width", Qwidth },
+    { "height", "Height", Qheight },
+    { "left", "Left", Qleft },
+    { "top", "Top", Qtop },
+  };
+
+  int i;
+  for (i = 0; i < ARRAYELTS (r); ++i)
+    {
+      if (NILP (Fassq (r[i].tem, parms)))
+        {
+          Lisp_Object value
+            = gui_display_get_arg (dpyinfo, parms, r[i].tem, r[i].val, r[i].cls,
+                                   RES_TYPE_NUMBER);
+          if (! EQ (value, Qunbound))
+            parms = Fcons (Fcons (r[i].tem, value), parms);
+        }
+    }
+
+  return parms;
+}
+
+void
+haiku_change_tool_bar_height (struct frame *f, int height)
+{
+  int unit = FRAME_LINE_HEIGHT (f);
+  int old_height = FRAME_TOOL_BAR_HEIGHT (f);
+  int lines = (height + unit - 1) / unit;
+  Lisp_Object fullscreen = get_frame_param (f, Qfullscreen);
+
+  /* Make sure we redisplay all windows in this frame.  */
+  fset_redisplay (f);
+
+  FRAME_TOOL_BAR_HEIGHT (f) = height;
+  FRAME_TOOL_BAR_LINES (f) = lines;
+  store_frame_param (f, Qtool_bar_lines, make_fixnum (lines));
+
+  if (FRAME_HAIKU_WINDOW (f) && FRAME_TOOL_BAR_HEIGHT (f) == 0)
+    {
+      clear_frame (f);
+      clear_current_matrices (f);
+    }
+
+  if ((height < old_height) && WINDOWP (f->tool_bar_window))
+    clear_glyph_matrix (XWINDOW (f->tool_bar_window)->current_matrix);
+
+  if (!f->tool_bar_resized)
+    {
+      /* As long as tool_bar_resized is false, effectively try to change
+	 F's native height.  */
+      if (NILP (fullscreen) || EQ (fullscreen, Qfullwidth))
+	adjust_frame_size (f, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f),
+			   1, false, Qtool_bar_lines);
+      else
+	adjust_frame_size (f, -1, -1, 4, false, Qtool_bar_lines);
+
+      f->tool_bar_resized =  f->tool_bar_redisplayed;
+    }
+  else
+    /* Any other change may leave the native size of F alone.  */
+    adjust_frame_size (f, -1, -1, 3, false, Qtool_bar_lines);
+
+  /* adjust_frame_size might not have done anything, garbage frame
+     here.  */
+  adjust_frame_glyphs (f);
+  SET_FRAME_GARBAGED (f);
+
+  if (FRAME_HAIKU_WINDOW (f))
+    haiku_clear_under_internal_border (f);
+}
+
+void
+haiku_change_tab_bar_height (struct frame *f, int height)
+{
+  int unit = FRAME_LINE_HEIGHT (f);
+  int old_height = FRAME_TAB_BAR_HEIGHT (f);
+  int lines = (height + unit - 1) / unit;
+  Lisp_Object fullscreen = get_frame_param (f, Qfullscreen);
+
+  /* Make sure we redisplay all windows in this frame.  */
+  fset_redisplay (f);
+
+  /* Recalculate tab bar and frame text sizes.  */
+  FRAME_TAB_BAR_HEIGHT (f) = height;
+  FRAME_TAB_BAR_LINES (f) = lines;
+  store_frame_param (f, Qtab_bar_lines, make_fixnum (lines));
+
+  if (FRAME_HAIKU_WINDOW (f) && FRAME_TAB_BAR_HEIGHT (f) == 0)
+    {
+      clear_frame (f);
+      clear_current_matrices (f);
+    }
+
+  if ((height < old_height) && WINDOWP (f->tab_bar_window))
+    clear_glyph_matrix (XWINDOW (f->tab_bar_window)->current_matrix);
+
+  if (!f->tab_bar_resized)
+    {
+      /* As long as tab_bar_resized is false, effectively try to change
+	 F's native height.  */
+      if (NILP (fullscreen) || EQ (fullscreen, Qfullwidth))
+	adjust_frame_size (f, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f),
+			   1, false, Qtab_bar_lines);
+      else
+	adjust_frame_size (f, -1, -1, 4, false, Qtab_bar_lines);
+
+      f->tab_bar_resized = f->tab_bar_redisplayed;
+    }
+  else
+    /* Any other change may leave the native size of F alone.  */
+    adjust_frame_size (f, -1, -1, 3, false, Qtab_bar_lines);
+
+  /* adjust_frame_size might not have done anything, garbage frame
+     here.  */
+  adjust_frame_glyphs (f);
+  SET_FRAME_GARBAGED (f);
+  if (FRAME_HAIKU_WINDOW (f))
+    haiku_clear_under_internal_border (f);
+}
+
+static void
+haiku_set_no_focus_on_map (struct frame *f, Lisp_Object value,
+			   Lisp_Object oldval)
+{
+  if (!EQ (value, oldval))
+    FRAME_NO_FOCUS_ON_MAP (f) = !NILP (value);
+}
+
+static void
+haiku_set_tool_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval)
+{
+  if (FRAME_TOOLTIP_P (f))
+    return;
+  int nlines;
+
+  /* Treat tool bars like menu bars.  */
+  if (FRAME_MINIBUF_ONLY_P (f))
+    return;
+
+  /* Use VALUE only if an int >= 0.  */
+  if (RANGED_FIXNUMP (0, value, INT_MAX))
+    nlines = XFIXNAT (value);
+  else
+    nlines = 0;
+
+  haiku_change_tool_bar_height (f, nlines * FRAME_LINE_HEIGHT (f));
+}
+
+static void
+haiku_set_tab_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval)
+{
+  if (FRAME_TOOLTIP_P (f))
+    return;
+  int olines = FRAME_TAB_BAR_LINES (f);
+  int nlines;
+
+  /* Treat tab bars like menu bars.  */
+  if (FRAME_MINIBUF_ONLY_P (f))
+    return;
+
+  /* Use VALUE only if an int >= 0.  */
+  if (RANGED_FIXNUMP (0, value, INT_MAX))
+    nlines = XFIXNAT (value);
+  else
+    nlines = 0;
+
+  if (nlines != olines && (olines == 0 || nlines == 0))
+    haiku_change_tab_bar_height (f, nlines * FRAME_LINE_HEIGHT (f));
+}
+
+
+int
+haiku_get_color (const char *name, Emacs_Color *color)
+{
+  unsigned short r16, g16, b16;
+  Lisp_Object tem;
+
+  if (parse_color_spec (name, &r16, &g16, &b16))
+    {
+      color->pixel = RGB_TO_ULONG (r16 / 256, g16 / 256, b16 / 256);
+      color->red = r16;
+      color->green = g16;
+      color->blue = b16;
+      return 0;
+    }
+  else
+    {
+      block_input ();
+      eassert (x_display_list && !NILP (x_display_list->color_map));
+      tem = x_display_list->color_map;
+      for (; CONSP (tem); tem = XCDR (tem))
+	{
+	  Lisp_Object col = XCAR (tem);
+	  if (CONSP (col) && !xstrcasecmp (SSDATA (XCAR (col)), name))
+	    {
+	      int32_t clr = XFIXNUM (XCDR (col));
+	      color->pixel = clr;
+	      color->red = RED_FROM_ULONG (clr) * 257;
+	      color->green = GREEN_FROM_ULONG (clr) * 257;
+	      color->blue = BLUE_FROM_ULONG (clr) * 257;
+	      unblock_input ();
+	      return 0;
+	  }
+	}
+
+      unblock_input ();
+    }
+
+  return 1;
+}
+
+static struct haiku_display_info *
+haiku_display_info_for_name (Lisp_Object name)
+{
+  CHECK_STRING (name);
+
+  if (!NILP (Fstring_equal (name, build_string ("be"))))
+    {
+      if (!x_display_list)
+	return x_display_list;
+
+      error ("Be windowing not initialized");
+    }
+
+  error ("Be displays can only be named \"be\"");
+}
+
+static struct haiku_display_info *
+check_haiku_display_info (Lisp_Object object)
+{
+  struct haiku_display_info *dpyinfo = NULL;
+
+  if (NILP (object))
+    {
+      struct frame *sf = XFRAME (selected_frame);
+
+      if (FRAME_HAIKU_P (sf) && FRAME_LIVE_P (sf))
+	dpyinfo = FRAME_DISPLAY_INFO (sf);
+      else if (x_display_list)
+	dpyinfo = x_display_list;
+      else
+	error ("Be windowing not present");
+    }
+  else if (TERMINALP (object))
+    {
+      struct terminal *t = decode_live_terminal (object);
+
+      if (t->type != output_haiku)
+	error ("Terminal %d is not a Be display", t->id);
+
+      dpyinfo = t->display_info.haiku;
+    }
+  else if (STRINGP (object))
+    dpyinfo = haiku_display_info_for_name (object);
+  else
+    {
+      struct frame *f = decode_window_system_frame (object);
+      dpyinfo = FRAME_DISPLAY_INFO (f);
+    }
+
+  return dpyinfo;
+}
+
+static void
+haiku_set_title_bar_text (struct frame *f, Lisp_Object text)
+{
+  if (FRAME_HAIKU_WINDOW (f))
+    {
+      block_input ();
+      BWindow_retitle (FRAME_HAIKU_WINDOW (f), SSDATA (ENCODE_UTF_8 (text)));
+      unblock_input ();
+    }
+}
+
+static void
+haiku_set_title (struct frame *f, Lisp_Object name, Lisp_Object old_name)
+{
+  /* Don't change the title if it's already NAME.  */
+  if (EQ (name, f->title))
+    return;
+
+  update_mode_lines = 26;
+
+  fset_title (f, name);
+
+  if (NILP (name))
+    name = f->name;
+
+  haiku_set_title_bar_text (f, name);
+}
+
+static void
+haiku_set_child_frame_border_width (struct frame *f,
+				    Lisp_Object arg, Lisp_Object oldval)
+{
+  int border;
+
+  if (NILP (arg))
+    border = -1;
+  else if (RANGED_FIXNUMP (0, arg, INT_MAX))
+    border = XFIXNAT (arg);
+  else
+    signal_error ("Invalid child frame border width", arg);
+
+  if (border != FRAME_CHILD_FRAME_BORDER_WIDTH (f))
+    {
+      f->child_frame_border_width = border;
+
+      if (FRAME_HAIKU_WINDOW (f))
+	adjust_frame_size (f, -1, -1, 3, 0, Qchild_frame_border_width);
+
+      SET_FRAME_GARBAGED (f);
+    }
+}
+
+static void
+haiku_set_parent_frame (struct frame *f,
+			Lisp_Object new_value, Lisp_Object old_value)
+{
+  struct frame *p = NULL;
+  block_input ();
+  if (!NILP (new_value)
+      && (!FRAMEP (new_value)
+	  || !FRAME_LIVE_P (p = XFRAME (new_value))
+	  || !FRAME_HAIKU_P (p)))
+    {
+      store_frame_param (f, Qparent_frame, old_value);
+      unblock_input ();
+      error ("Invalid specification of `parent-frame'");
+    }
+
+  if (EQ (new_value, old_value))
+    {
+      unblock_input ();
+      return;
+    }
+
+  if (!NILP (old_value))
+    EmacsWindow_unparent (FRAME_HAIKU_WINDOW (f));
+  if (!NILP (new_value))
+    {
+      EmacsWindow_parent_to (FRAME_HAIKU_WINDOW (f),
+			     FRAME_HAIKU_WINDOW (p));
+      BWindow_set_offset (FRAME_HAIKU_WINDOW (f),
+			  f->left_pos, f->top_pos);
+    }
+  fset_parent_frame (f, new_value);
+  unblock_input ();
+}
+
+static void
+haiku_explicitly_set_name (struct frame *f, Lisp_Object arg, Lisp_Object oldval)
+{
+  haiku_set_name (f, arg, 1);
+}
+
+static void
+haiku_set_no_accept_focus (struct frame *f, Lisp_Object new_value, Lisp_Object old_value)
+{
+  block_input ();
+  if (!EQ (new_value, old_value))
+    FRAME_NO_ACCEPT_FOCUS (f) = !NILP (new_value);
+
+  if (FRAME_HAIKU_WINDOW (f))
+    {
+      BWindow_set_avoid_focus (FRAME_HAIKU_WINDOW (f),
+			       FRAME_NO_ACCEPT_FOCUS (f));
+    }
+  unblock_input ();
+}
+
+static void
+unwind_create_frame (Lisp_Object frame)
+{
+  struct frame *f = XFRAME (frame);
+
+  /* If frame is already dead, nothing to do.  This can happen if the
+     display is disconnected after the frame has become official, but
+     before x_create_frame removes the unwind protect.  */
+  if (!FRAME_LIVE_P (f))
+    return;
+
+  /* If frame is ``official'', nothing to do.  */
+  if (NILP (Fmemq (frame, Vframe_list)))
+    {
+#if defined GLYPH_DEBUG && defined ENABLE_CHECKING
+      struct haiku_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
+#endif
+
+      /* If the frame's image cache refcount is still the same as our
+	 private shadow variable, it means we are unwinding a frame
+	 for which we didn't yet call init_frame_faces, where the
+	 refcount is incremented.  Therefore, we increment it here, so
+	 that free_frame_faces, called in free_frame_resources later,
+	 will not mistakenly decrement the counter that was not
+	 incremented yet to account for this new frame.  */
+      if (FRAME_IMAGE_CACHE (f) != NULL
+	  && FRAME_IMAGE_CACHE (f)->refcount == image_cache_refcount)
+	FRAME_IMAGE_CACHE (f)->refcount++;
+
+      haiku_free_frame_resources (f);
+      free_glyphs (f);
+
+#if defined GLYPH_DEBUG && defined ENABLE_CHECKING
+      /* Check that reference counts are indeed correct.  */
+      if (dpyinfo->terminal->image_cache)
+	eassert (dpyinfo->terminal->image_cache->refcount == image_cache_refcount);
+#endif
+    }
+}
+
+static void
+unwind_create_tip_frame (Lisp_Object frame)
+{
+  unwind_create_frame (frame);
+  tip_window = NULL;
+  tip_frame = Qnil;
+}
+
+static void
+haiku_set_foreground_color (struct frame *f, Lisp_Object arg, Lisp_Object oldval)
+{
+  struct haiku_output *output = FRAME_OUTPUT_DATA (f);
+  unsigned long old_fg;
+
+  Emacs_Color color;
+
+  if (haiku_get_color (SSDATA (arg), &color))
+    {
+      store_frame_param (f, Qforeground_color, oldval);
+      unblock_input ();
+      error ("Bad color");
+    }
+
+  old_fg = FRAME_FOREGROUND_PIXEL (f);
+  FRAME_FOREGROUND_PIXEL (f) = color.pixel;
+
+  if (FRAME_HAIKU_WINDOW (f))
+    {
+
+      block_input ();
+      if (output->cursor_color.pixel == old_fg)
+	{
+	  output->cursor_color.pixel = old_fg;
+	  output->cursor_color.red = RED_FROM_ULONG (old_fg);
+	  output->cursor_color.green = GREEN_FROM_ULONG (old_fg);
+	  output->cursor_color.blue = BLUE_FROM_ULONG (old_fg);
+	}
+
+      unblock_input ();
+
+      update_face_from_frame_parameter (f, Qforeground_color, arg);
+
+      if (FRAME_VISIBLE_P (f))
+        redraw_frame (f);
+    }
+}
+
+static void
+unwind_popup (void)
+{
+  if (!popup_activated_p)
+    emacs_abort ();
+  --popup_activated_p;
+}
+
+static Lisp_Object
+haiku_create_frame (Lisp_Object parms, int ttip_p)
+{
+  struct frame *f;
+  Lisp_Object frame, tem;
+  Lisp_Object name;
+  bool minibuffer_only = false;
+  long window_prompting = 0;
+  ptrdiff_t count = SPECPDL_INDEX ();
+  Lisp_Object display;
+  struct haiku_display_info *dpyinfo = NULL;
+  struct kboard *kb;
+
+  parms = Fcopy_alist (parms);
+
+  Vx_resource_name = Vinvocation_name;
+
+  display = gui_display_get_arg (dpyinfo, parms, Qterminal, 0, 0,
+                                 RES_TYPE_STRING);
+  if (EQ (display, Qunbound))
+    display = Qnil;
+  dpyinfo = check_haiku_display_info (display);
+  kb = dpyinfo->terminal->kboard;
+
+  if (!dpyinfo->terminal->name)
+    error ("Terminal is not live, can't create new frames on it");
+
+  name = gui_display_get_arg (dpyinfo, parms, Qname, 0, 0,
+                              RES_TYPE_STRING);
+  if (!STRINGP (name)
+      && ! EQ (name, Qunbound)
+      && ! NILP (name))
+    error ("Invalid frame name--not a string or nil");
+
+  if (STRINGP (name))
+    Vx_resource_name = name;
+
+  block_input ();
+
+  /* make_frame_without_minibuffer can run Lisp code and garbage collect.  */
+  /* No need to protect DISPLAY because that's not used after passing
+     it to make_frame_without_minibuffer.  */
+  frame = Qnil;
+  tem = gui_display_get_arg (dpyinfo, parms, Qminibuffer,
+                             "minibuffer", "Minibuffer",
+                             RES_TYPE_SYMBOL);
+  if (EQ (tem, Qnone) || NILP (tem) || ttip_p)
+      f = make_frame_without_minibuffer (Qnil, kb, display);
+  else if (EQ (tem, Qonly))
+    {
+      f = make_minibuffer_frame ();
+      minibuffer_only = 1;
+    }
+  else if (WINDOWP (tem))
+      f = make_frame_without_minibuffer (tem, kb, display);
+  else
+      f = make_frame (1);
+  XSETFRAME (frame, f);
+
+  f->terminal = dpyinfo->terminal;
+
+  f->output_method = output_haiku;
+  f->output_data.haiku = xzalloc (sizeof *f->output_data.haiku);
+
+  f->output_data.haiku->pending_zoom_x = INT_MIN;
+  f->output_data.haiku->pending_zoom_y = INT_MIN;
+  f->output_data.haiku->pending_zoom_width = INT_MIN;
+  f->output_data.haiku->pending_zoom_height = INT_MIN;
+
+  fset_icon_name (f, gui_display_get_arg (dpyinfo, parms, Qicon_name,
+                                          "iconName", "Title",
+                                          RES_TYPE_STRING));
+  if (! STRINGP (f->icon_name))
+    fset_icon_name (f, Qnil);
+
+  FRAME_DISPLAY_INFO (f) = dpyinfo;
+
+  /* With FRAME_DISPLAY_INFO set up, this unwind-protect is safe.  */
+  if (!ttip_p)
+    record_unwind_protect (unwind_create_frame, frame);
+  else
+    record_unwind_protect (unwind_create_tip_frame, frame);
+
+  FRAME_OUTPUT_DATA (f)->parent_desc = NULL;
+  FRAME_OUTPUT_DATA (f)->explicit_parent = 0;
+
+  /* Set the name; the functions to which we pass f expect the name to
+     be set.  */
+  if (EQ (name, Qunbound) || NILP (name) || ! STRINGP (name))
+    {
+      fset_name (f, Vinvocation_name);
+      f->explicit_name = 0;
+    }
+  else
+    {
+      fset_name (f, name);
+      f->explicit_name = 1;
+      specbind (Qx_resource_name, name);
+    }
+
+#ifdef HAVE_FREETYPE
+  register_font_driver (&ftbefont_driver, f);
+#ifdef HAVE_HARFBUZZ
+  register_font_driver (&ftbehbfont_driver, f);
+#endif
+#endif
+  register_font_driver (&haikufont_driver, f);
+
+  f->tooltip = ttip_p;
+
+  image_cache_refcount =
+    FRAME_IMAGE_CACHE (f) ? FRAME_IMAGE_CACHE (f)->refcount : 0;
+
+  gui_default_parameter (f, parms, Qfont_backend, Qnil,
+                         "fontBackend", "FontBackend", RES_TYPE_STRING);
+
+  FRAME_RIF (f)->default_font_parameter (f, parms);
+
+  unblock_input ();
+
+  gui_default_parameter (f, parms, Qborder_width, make_fixnum (0),
+                         "borderwidth", "BorderWidth", RES_TYPE_NUMBER);
+  gui_default_parameter (f, parms, Qinternal_border_width, make_fixnum (ttip_p ? 1 : 2),
+                         "internalBorderWidth", "InternalBorderWidth",
+                         RES_TYPE_NUMBER);
+  gui_default_parameter (f, parms, Qchild_frame_border_width, Qnil,
+			 "childFrameBorderWidth", "childFrameBorderWidth",
+			 RES_TYPE_NUMBER);
+  gui_default_parameter (f, parms, Qright_divider_width, make_fixnum (0),
+		       NULL, NULL, RES_TYPE_NUMBER);
+  gui_default_parameter (f, parms, Qbottom_divider_width, make_fixnum (0),
+		       NULL, NULL, RES_TYPE_NUMBER);
+  gui_default_parameter (f, parms, Qvertical_scroll_bars, !ttip_p ? Qt : Qnil,
+			 "verticalScrollBars", "VerticalScrollBars",
+			 RES_TYPE_SYMBOL);
+  gui_default_parameter (f, parms, Qhorizontal_scroll_bars, Qnil,
+                         "horizontalScrollBars", "HorizontalScrollBars",
+                         RES_TYPE_SYMBOL);
+  gui_default_parameter (f, parms, Qforeground_color, build_string ("black"),
+                         "foreground", "Foreground", RES_TYPE_STRING);
+  gui_default_parameter (f, parms, Qbackground_color, build_string ("white"),
+                         "background", "Background", RES_TYPE_STRING);
+  gui_default_parameter (f, parms, Qline_spacing, Qnil,
+                         "lineSpacing", "LineSpacing", RES_TYPE_NUMBER);
+  gui_default_parameter (f, parms, Qleft_fringe, Qnil,
+                         "leftFringe", "LeftFringe", RES_TYPE_NUMBER);
+  gui_default_parameter (f, parms, Qright_fringe, Qnil,
+                         "rightFringe", "RightFringe", RES_TYPE_NUMBER);
+  gui_default_parameter (f, parms, Qno_special_glyphs, ttip_p ? Qnil : Qt,
+                         NULL, NULL, RES_TYPE_BOOLEAN);
+
+  init_frame_faces (f);
+
+  /* Read comment about this code in corresponding place in xfns.c.  */
+  tem = gui_display_get_arg (dpyinfo, parms, Qmin_width, NULL, NULL,
+                             RES_TYPE_NUMBER);
+  if (FIXNUMP (tem))
+    store_frame_param (f, Qmin_width, tem);
+  tem = gui_display_get_arg (dpyinfo, parms, Qmin_height, NULL, NULL,
+                             RES_TYPE_NUMBER);
+  if (FIXNUMP (tem))
+    store_frame_param (f, Qmin_height, tem);
+  adjust_frame_size (f, FRAME_COLS (f) * FRAME_COLUMN_WIDTH (f),
+		     FRAME_LINES (f) * FRAME_LINE_HEIGHT (f), 5, 1,
+		     Qx_create_frame_1);
+
+  if (!ttip_p)
+    {
+      gui_default_parameter (f, parms, Qz_group, Qnil, NULL, NULL, RES_TYPE_SYMBOL);
+      gui_default_parameter (f, parms, Qno_focus_on_map, Qnil,
+			     NULL, NULL, RES_TYPE_BOOLEAN);
+      gui_default_parameter (f, parms, Qno_accept_focus, Qnil,
+			     NULL, NULL, RES_TYPE_BOOLEAN);
+
+      /* The resources controlling the menu-bar, tool-bar, and tab-bar are
+	 processed specially at startup, and reflected in the mode
+	 variables; ignore them here.  */
+      gui_default_parameter (f, parms, Qmenu_bar_lines,
+			     NILP (Vmenu_bar_mode)
+			     ? make_fixnum (0) : make_fixnum (1),
+			     NULL, NULL, RES_TYPE_NUMBER);
+      gui_default_parameter (f, parms, Qtab_bar_lines,
+			     NILP (Vtab_bar_mode)
+			     ? make_fixnum (0) : make_fixnum (1),
+			     NULL, NULL, RES_TYPE_NUMBER);
+      gui_default_parameter (f, parms, Qtool_bar_lines,
+			     NILP (Vtool_bar_mode)
+			     ? make_fixnum (0) : make_fixnum (1),
+			     NULL, NULL, RES_TYPE_NUMBER);
+    }
+
+  gui_default_parameter (f, parms, Qbuffer_predicate, Qnil, "bufferPredicate",
+                         "BufferPredicate", RES_TYPE_SYMBOL);
+  gui_default_parameter (f, parms, Qtitle, Qnil, "title", "Title",
+                         RES_TYPE_STRING);
+
+  parms = get_geometry_from_preferences (dpyinfo, parms);
+  window_prompting = gui_figure_window_size (f, parms, false, true);
+
+  if (ttip_p)
+    {
+       /* No fringes on tip frame.  */
+      f->fringe_cols = 0;
+      f->left_fringe_width = 0;
+      f->right_fringe_width = 0;
+      /* No dividers on tip frame.  */
+      f->right_divider_width = 0;
+      f->bottom_divider_width = 0;
+    }
+
+  tem = gui_display_get_arg (dpyinfo, parms, Qunsplittable, 0, 0,
+                             RES_TYPE_BOOLEAN);
+  f->no_split = minibuffer_only || (!EQ (tem, Qunbound) && !NILP (tem));
+
+  /* Add `tooltip' frame parameter's default value. */
+  if (NILP (Fframe_parameter (frame, Qtooltip)) && ttip_p)
+    Fmodify_frame_parameters (frame, Fcons (Fcons (Qtooltip, Qt), Qnil));
+
+#define ASSIGN_CURSOR(cursor, be_cursor) \
+  (FRAME_OUTPUT_DATA (f)->cursor = be_cursor)
+
+  ASSIGN_CURSOR (text_cursor, BCursor_create_i_beam ());
+  ASSIGN_CURSOR (nontext_cursor, BCursor_create_default ());
+  ASSIGN_CURSOR (modeline_cursor, BCursor_create_default ());
+  ASSIGN_CURSOR (hand_cursor, BCursor_create_grab ());
+  ASSIGN_CURSOR (hourglass_cursor, BCursor_create_progress_cursor ());
+  ASSIGN_CURSOR (horizontal_drag_cursor,
+		 BCursor_from_id (CURSOR_ID_RESIZE_EAST_WEST));
+  ASSIGN_CURSOR (vertical_drag_cursor,
+		 BCursor_from_id (CURSOR_ID_RESIZE_NORTH_SOUTH));
+  ASSIGN_CURSOR (left_edge_cursor,
+		 BCursor_from_id (CURSOR_ID_RESIZE_WEST));
+  ASSIGN_CURSOR (top_left_corner_cursor,
+		 BCursor_from_id (CURSOR_ID_RESIZE_NORTH_WEST));
+  ASSIGN_CURSOR (top_edge_cursor,
+		 BCursor_from_id (CURSOR_ID_RESIZE_NORTH));
+  ASSIGN_CURSOR (top_right_corner_cursor,
+		 BCursor_from_id (CURSOR_ID_RESIZE_NORTH_EAST));
+  ASSIGN_CURSOR (right_edge_cursor,
+		 BCursor_from_id (CURSOR_ID_RESIZE_EAST));
+  ASSIGN_CURSOR (bottom_right_corner_cursor,
+		 BCursor_from_id (CURSOR_ID_RESIZE_SOUTH_EAST));
+  ASSIGN_CURSOR (bottom_edge_cursor,
+		 BCursor_from_id (CURSOR_ID_RESIZE_SOUTH));
+  ASSIGN_CURSOR (bottom_left_corner_cursor,
+		 BCursor_from_id (CURSOR_ID_RESIZE_SOUTH_WEST));
+  ASSIGN_CURSOR (no_cursor,
+		 BCursor_from_id (CURSOR_ID_NO_CURSOR));
+
+  ASSIGN_CURSOR (current_cursor, FRAME_OUTPUT_DATA (f)->text_cursor);
+#undef ASSIGN_CURSOR
+
+
+  if (ttip_p)
+    f->no_split = true;
+  f->terminal->reference_count++;
+
+  FRAME_OUTPUT_DATA (f)->window = BWindow_new (&FRAME_OUTPUT_DATA (f)->view);
+  if (!FRAME_OUTPUT_DATA (f)->window)
+    xsignal1 (Qerror, build_unibyte_string ("Could not create window"));
+
+  if (!minibuffer_only && !ttip_p && FRAME_EXTERNAL_MENU_BAR (f))
+    initialize_frame_menubar (f);
+
+  FRAME_OUTPUT_DATA (f)->window_desc = FRAME_OUTPUT_DATA (f)->window;
+
+  Vframe_list = Fcons (frame, Vframe_list);
+
+  Lisp_Object parent_frame = gui_display_get_arg (dpyinfo, parms, Qparent_frame, NULL, NULL,
+						  RES_TYPE_SYMBOL);
+
+  if (EQ (parent_frame, Qunbound)
+      || NILP (parent_frame)
+      || !FRAMEP (parent_frame)
+      || !FRAME_LIVE_P (XFRAME (parent_frame)))
+    parent_frame = Qnil;
+
+  fset_parent_frame (f, parent_frame);
+  store_frame_param (f, Qparent_frame, parent_frame);
+
+  if (!NILP (parent_frame))
+    haiku_set_parent_frame (f, parent_frame, Qnil);
+
+  gui_default_parameter (f, parms, Qundecorated, Qnil, NULL, NULL, RES_TYPE_BOOLEAN);
+
+  gui_default_parameter (f, parms, Qicon_type, Qnil,
+                         "bitmapIcon", "BitmapIcon", RES_TYPE_SYMBOL);
+  if (ttip_p)
+    {
+      gui_default_parameter (f, parms, Qundecorated, Qt, NULL, NULL, RES_TYPE_BOOLEAN);
+      gui_default_parameter (f, parms, Qno_accept_focus, Qt, NULL, NULL,
+			     RES_TYPE_BOOLEAN);
+    }
+
+  gui_default_parameter (f, parms, Qauto_raise, Qnil,
+                         "autoRaise", "AutoRaiseLower", RES_TYPE_BOOLEAN);
+  gui_default_parameter (f, parms, Qauto_lower, Qnil,
+                         "autoLower", "AutoLower", RES_TYPE_BOOLEAN);
+  gui_default_parameter (f, parms, Qcursor_type, Qbox,
+                         "cursorType", "CursorType", RES_TYPE_SYMBOL);
+  gui_default_parameter (f, parms, Qscroll_bar_width, Qnil,
+                         "scrollBarWidth", "ScrollBarWidth",
+                         RES_TYPE_NUMBER);
+  gui_default_parameter (f, parms, Qscroll_bar_height, Qnil,
+                         "scrollBarHeight", "ScrollBarHeight",
+                         RES_TYPE_NUMBER);
+  gui_default_parameter (f, parms, Qalpha, Qnil,
+                         "alpha", "Alpha", RES_TYPE_NUMBER);
+  gui_default_parameter (f, parms, Qfullscreen, Qnil,
+                         "fullscreen", "Fullscreen", RES_TYPE_SYMBOL);
+  gui_default_parameter (f, parms, Qinhibit_double_buffering, Qnil,
+                         "inhibitDoubleBuffering", "InhibitDoubleBuffering",
+                         RES_TYPE_BOOLEAN);
+
+  f->can_set_window_size = true;
+
+  adjust_frame_size (f, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f),
+		     0, true, ttip_p ? Qtip_frame : Qx_create_frame_2);
+
+  if (!FRAME_OUTPUT_DATA (f)->explicit_parent && !ttip_p)
+    {
+      Lisp_Object visibility;
+
+      visibility = gui_display_get_arg (dpyinfo, parms, Qvisibility, 0, 0,
+                                        RES_TYPE_SYMBOL);
+      if (EQ (visibility, Qunbound))
+	visibility = Qt;
+      if (EQ (visibility, Qicon))
+	haiku_iconify_frame (f);
+      else if (!NILP (visibility))
+	haiku_visualize_frame (f);
+      else /* Qnil */
+	{
+	  f->was_invisible = true;
+	}
+    }
+
+  if (FRAME_HAS_MINIBUF_P (f)
+      && (!FRAMEP (KVAR (kb, Vdefault_minibuffer_frame))
+          || !FRAME_LIVE_P (XFRAME (KVAR (kb, Vdefault_minibuffer_frame)))))
+    kset_default_minibuffer_frame (kb, frame);
+
+  for (tem = parms; CONSP (tem); tem = XCDR (tem))
+    if (CONSP (XCAR (tem)) && !NILP (XCAR (XCAR (tem))))
+      fset_param_alist (f, Fcons (XCAR (tem), f->param_alist));
+
+  if (window_prompting & (USPosition | PPosition))
+    haiku_set_offset (f, f->left_pos, f->top_pos, 1);
+  else
+    BWindow_center_on_screen (FRAME_HAIKU_WINDOW (f));
+
+  /* Make sure windows on this frame appear in calls to next-window
+     and similar functions.  */
+  Vwindow_list = Qnil;
+
+  if (ttip_p)
+    adjust_frame_size (f, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f),
+		       0, true, Qtip_frame);
+
+  return unbind_to (count, frame);
+}
+
+static void
+compute_tip_xy (struct frame *f,
+		Lisp_Object parms, Lisp_Object dx, Lisp_Object dy,
+		int width, int height, int *root_x, int *root_y)
+{
+  Lisp_Object left, top, right, bottom;
+  int min_x = 0, min_y = 0, max_x = 0, max_y = 0;
+
+  /* User-specified position?  */
+  left = Fcdr (Fassq (Qleft, parms));
+  top  = Fcdr (Fassq (Qtop, parms));
+  right = Fcdr (Fassq (Qright, parms));
+  bottom = Fcdr (Fassq (Qbottom, parms));
+
+  /* Move the tooltip window where the mouse pointer is.  Resize and
+     show it.  */
+  if ((!FIXNUMP (left) && !FIXNUMP (right))
+      || (!FIXNUMP (top) && !FIXNUMP (bottom)))
+    {
+      int x, y;
+
+      /* Default min and max values.  */
+      min_x = 0;
+      min_y = 0;
+      BScreen_px_dim (&max_x, &max_y);
+
+      block_input ();
+      BView_get_mouse (FRAME_HAIKU_VIEW (f), &x, &y);
+      BView_convert_to_screen (FRAME_HAIKU_VIEW (f), &x, &y);
+      *root_x = x;
+      *root_y = y;
+      unblock_input ();
+    }
+
+  if (FIXNUMP (top))
+    *root_y = XFIXNUM (top);
+  else if (FIXNUMP (bottom))
+    *root_y = XFIXNUM (bottom) - height;
+  else if (*root_y + XFIXNUM (dy) <= min_y)
+    *root_y = min_y; /* Can happen for negative dy */
+  else if (*root_y + XFIXNUM (dy) + height <= max_y)
+    /* It fits below the pointer */
+      *root_y += XFIXNUM (dy);
+  else if (height + XFIXNUM (dy) + min_y <= *root_y)
+    /* It fits above the pointer.  */
+    *root_y -= height + XFIXNUM (dy);
+  else
+    /* Put it on the top.  */
+    *root_y = min_y;
+
+  if (FIXNUMP (left))
+    *root_x = XFIXNUM (left);
+  else if (FIXNUMP (right))
+    *root_x = XFIXNUM (right) - width;
+  else if (*root_x + XFIXNUM (dx) <= min_x)
+    *root_x = 0; /* Can happen for negative dx */
+  else if (*root_x + XFIXNUM (dx) + width <= max_x)
+    /* It fits to the right of the pointer.  */
+    *root_x += XFIXNUM (dx);
+  else if (width + XFIXNUM (dx) + min_x <= *root_x)
+    /* It fits to the left of the pointer.  */
+    *root_x -= width + XFIXNUM (dx);
+  else
+    /* Put it left justified on the screen -- it ought to fit that way.  */
+    *root_x = min_x;
+}
+
+static Lisp_Object
+haiku_hide_tip (bool delete)
+{
+  if (!NILP (tip_timer))
+    {
+      call1 (Qcancel_timer, tip_timer);
+      tip_timer = Qnil;
+    }
+
+  Lisp_Object it, frame;
+  FOR_EACH_FRAME (it, frame)
+    if (FRAME_WINDOW_P (XFRAME (frame)) &&
+	FRAME_HAIKU_VIEW (XFRAME (frame)))
+      BView_set_tooltip (FRAME_HAIKU_VIEW (XFRAME (frame)), NULL);
+
+  if (NILP (tip_frame)
+      || (!delete && FRAMEP (tip_frame)
+	  && !FRAME_VISIBLE_P (XFRAME (tip_frame))))
+    return Qnil;
+  else
+    {
+      ptrdiff_t count;
+      Lisp_Object was_open = Qnil;
+
+      count = SPECPDL_INDEX ();
+      specbind (Qinhibit_redisplay, Qt);
+      specbind (Qinhibit_quit, Qt);
+
+      if (FRAMEP (tip_frame))
+	{
+	  if (FRAME_LIVE_P (XFRAME (tip_frame)))
+	    {
+	      if (delete)
+		{
+		  delete_frame (tip_frame, Qnil);
+		  tip_frame = Qnil;
+		}
+	      else
+		haiku_unvisualize_frame (XFRAME (tip_frame));
+
+	      was_open = Qt;
+	    }
+	  else
+	    tip_frame = Qnil;
+	}
+      else
+	tip_frame = Qnil;
+
+      return unbind_to (count, was_open);
+    }
+}
+
+static void
+haiku_set_undecorated (struct frame *f, Lisp_Object new_value,
+		       Lisp_Object old_value)
+{
+  if (EQ (new_value, old_value))
+    return;
+
+  block_input ();
+  FRAME_UNDECORATED (f) = !NILP (new_value);
+  BWindow_change_decoration (FRAME_HAIKU_WINDOW (f), NILP (new_value));
+  unblock_input ();
+}
+
+static void
+haiku_set_menu_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval)
+{
+  if (FRAME_TOOLTIP_P (f))
+    return;
+  int nlines;
+  if (TYPE_RANGED_FIXNUMP (int, value))
+    nlines = XFIXNUM (value);
+  else
+    nlines = 0;
+
+  fset_redisplay (f);
+
+  FRAME_MENU_BAR_LINES (f) = 0;
+  FRAME_MENU_BAR_HEIGHT (f) = 0;
+
+  if (nlines)
+    {
+      FRAME_EXTERNAL_MENU_BAR (f) = 1;
+      if (FRAME_HAIKU_P (f) && !FRAME_HAIKU_MENU_BAR (f))
+	XWINDOW (FRAME_SELECTED_WINDOW (f))->update_mode_line = 1;
+    }
+  else
+    {
+      if (FRAME_EXTERNAL_MENU_BAR (f))
+	free_frame_menubar (f);
+      FRAME_EXTERNAL_MENU_BAR (f) = 0;
+      if (FRAME_HAIKU_P (f))
+	FRAME_HAIKU_MENU_BAR (f) = 0;
+    }
+
+  adjust_frame_glyphs (f);
+}
+
+/* Return geometric attributes of FRAME.  According to the value of
+   ATTRIBUTES return the outer edges of FRAME (Qouter_edges), the inner
+   edges of FRAME, the root window edges of frame (Qroot_edges).  Any
+   other value means to return the geometry as returned by
+   Fx_frame_geometry.  */
+static Lisp_Object
+frame_geometry (Lisp_Object frame, Lisp_Object attribute)
+{
+  struct frame *f = decode_live_frame (frame);
+  check_window_system (f);
+
+  if (EQ (attribute, Qouter_edges))
+    return list4i (f->left_pos, f->top_pos,
+		   f->left_pos, f->top_pos);
+  else if (EQ (attribute, Qnative_edges))
+    return list4i (f->left_pos, f->top_pos,
+		   f->left_pos + FRAME_PIXEL_WIDTH (f),
+		   f->top_pos + FRAME_PIXEL_HEIGHT (f));
+  else if (EQ (attribute, Qinner_edges))
+    return list4i (f->left_pos + FRAME_INTERNAL_BORDER_WIDTH (f),
+		   f->top_pos + FRAME_INTERNAL_BORDER_WIDTH (f) +
+		   FRAME_MENU_BAR_HEIGHT (f) + FRAME_TOOL_BAR_HEIGHT (f),
+		   f->left_pos - FRAME_INTERNAL_BORDER_WIDTH (f) +
+		   FRAME_PIXEL_WIDTH (f),
+		   f->top_pos + FRAME_PIXEL_HEIGHT (f) -
+		   FRAME_INTERNAL_BORDER_WIDTH (f));
+
+  else
+    return
+      list (Fcons (Qouter_position,
+		   Fcons (make_fixnum (f->left_pos),
+			  make_fixnum (f->top_pos))),
+	    Fcons (Qouter_size,
+		   Fcons (make_fixnum (FRAME_PIXEL_WIDTH (f)),
+			  make_fixnum (FRAME_PIXEL_HEIGHT (f)))),
+	    Fcons (Qexternal_border_size,
+		   Fcons (make_fixnum (0), make_fixnum (0))),
+	    Fcons (Qtitle_bar_size,
+		   Fcons (make_fixnum (0), make_fixnum (0))),
+	    Fcons (Qmenu_bar_external, Qnil),
+	    Fcons (Qmenu_bar_size, Fcons (make_fixnum (FRAME_PIXEL_WIDTH (f) -
+						       (FRAME_INTERNAL_BORDER_WIDTH (f) * 2)),
+					  make_fixnum (FRAME_MENU_BAR_HEIGHT (f)))),
+	    Fcons (Qtool_bar_external, Qnil),
+	    Fcons (Qtool_bar_position, Qtop),
+	    Fcons (Qtool_bar_size, Fcons (make_fixnum (FRAME_PIXEL_WIDTH (f) -
+						       (FRAME_INTERNAL_BORDER_WIDTH (f) * 2)),
+					  make_fixnum (FRAME_TOOL_BAR_HEIGHT (f)))),
+	    Fcons (Qinternal_border_width, make_fixnum (FRAME_INTERNAL_BORDER_WIDTH (f))));
+}
+
+void
+haiku_set_background_color (struct frame *f, Lisp_Object arg, Lisp_Object oldval)
+{
+  CHECK_STRING (arg);
+
+  block_input ();
+  Emacs_Color color;
+
+  if (haiku_get_color (SSDATA (arg), &color))
+    {
+      store_frame_param (f, Qbackground_color, oldval);
+      unblock_input ();
+      error ("Bad color");
+    }
+
+  FRAME_OUTPUT_DATA (f)->cursor_fg = color.pixel;
+  FRAME_BACKGROUND_PIXEL (f) = color.pixel;
+
+  if (FRAME_HAIKU_VIEW (f))
+    {
+      struct face *defface;
+
+      BView_draw_lock (FRAME_HAIKU_VIEW (f));
+      BView_SetViewColor (FRAME_HAIKU_VIEW (f), color.pixel);
+      BView_draw_unlock (FRAME_HAIKU_VIEW (f));
+
+      defface = FACE_FROM_ID_OR_NULL (f, DEFAULT_FACE_ID);
+      if (defface)
+	{
+	  defface->background = color.pixel;
+	  update_face_from_frame_parameter (f, Qbackground_color, arg);
+	  clear_frame (f);
+	}
+    }
+
+  if (FRAME_VISIBLE_P (f))
+    SET_FRAME_GARBAGED (f);
+  unblock_input ();
+}
+
+void
+haiku_set_cursor_color (struct frame *f, Lisp_Object arg, Lisp_Object oldval)
+{
+  CHECK_STRING (arg);
+
+  block_input ();
+  Emacs_Color color;
+
+  if (haiku_get_color (SSDATA (arg), &color))
+    {
+      store_frame_param (f, Qcursor_color, oldval);
+      unblock_input ();
+      error ("Bad color");
+    }
+
+  FRAME_CURSOR_COLOR (f) = color;
+  if (FRAME_VISIBLE_P (f))
+    {
+      gui_update_cursor (f, 0);
+      gui_update_cursor (f, 1);
+    }
+  update_face_from_frame_parameter (f, Qcursor_color, arg);
+  unblock_input ();
+}
+
+void
+haiku_set_cursor_type (struct frame *f, Lisp_Object arg, Lisp_Object oldval)
+{
+  set_frame_cursor_types (f, arg);
+}
+
+unsigned long
+haiku_get_pixel (haiku bitmap, int x, int y)
+{
+  unsigned char *data;
+  int32_t bytes_per_row;
+  int mono_p;
+  int left;
+  int right;
+  int top;
+  int bottom;
+
+  data = BBitmap_data (bitmap);
+  BBitmap_dimensions (bitmap, &left, &top, &right, &bottom,
+		      &bytes_per_row, &mono_p);
+
+  if (x < left || x > right || y < top || y > bottom)
+    emacs_abort ();
+
+  if (!mono_p)
+    return ((uint32_t *) (data + (bytes_per_row * y)))[x];
+
+  int byte = y * bytes_per_row + x / 8;
+  return data[byte] & (1 << (x % 8));
+}
+
+void
+haiku_put_pixel (haiku bitmap, int x, int y, unsigned long pixel)
+{
+  unsigned char *data;
+  int32_t bytes_per_row;
+  int mono_p;
+  int left;
+  int right;
+  int top;
+  int bottom;
+
+  data = BBitmap_data (bitmap);
+  BBitmap_dimensions (bitmap, &left, &top, &right, &bottom,
+		      &bytes_per_row, &mono_p);
+
+  if (x < left || x > right || y < top || y > bottom)
+    emacs_abort ();
+
+  if (mono_p)
+    {
+      ptrdiff_t off = y * bytes_per_row;
+      ptrdiff_t bit = x % 8;
+      ptrdiff_t xoff = x / 8;
+
+      unsigned char *byte = data + off + xoff;
+      if (!pixel)
+	*byte &= ~(1 << bit);
+      else
+	*byte |= 1 << bit;
+    }
+  else
+    ((uint32_t *) (data + (bytes_per_row * y)))[x] = pixel;
+}
+
+void
+haiku_free_frame_resources (struct frame *f)
+{
+  haiku window, drawable, mbar;
+  Mouse_HLInfo *hlinfo;
+  struct haiku_display_info *dpyinfo;
+  Lisp_Object bar;
+  struct scroll_bar *b;
+
+  block_input ();
+  check_window_system (f);
+
+  hlinfo = MOUSE_HL_INFO (f);
+  window = FRAME_HAIKU_WINDOW (f);
+  drawable = FRAME_HAIKU_VIEW (f);
+  mbar = FRAME_HAIKU_MENU_BAR (f);
+  dpyinfo = FRAME_DISPLAY_INFO (f);
+
+  free_frame_faces (f);
+
+  /* Free scroll bars */
+  for (bar = FRAME_SCROLL_BARS (f); !NILP (bar); bar = b->next)
+    {
+      b = XSCROLL_BAR (bar);
+      haiku_scroll_bar_remove (b);
+    }
+
+  if (f == dpyinfo->highlight_frame)
+    dpyinfo->highlight_frame = 0;
+  if (f == dpyinfo->focused_frame)
+    dpyinfo->focused_frame = 0;
+  if (f == dpyinfo->last_mouse_motion_frame)
+    dpyinfo->last_mouse_motion_frame = NULL;
+  if (f == dpyinfo->last_mouse_frame)
+    dpyinfo->last_mouse_frame = NULL;
+  if (f == dpyinfo->focus_event_frame)
+    dpyinfo->focus_event_frame = NULL;
+
+  if (f == hlinfo->mouse_face_mouse_frame)
+    reset_mouse_highlight (hlinfo);
+
+  if (mbar)
+    {
+      BMenuBar_delete (mbar);
+      if (f->output_data.haiku->menu_bar_open_p)
+	{
+	  --popup_activated_p;
+	  f->output_data.haiku->menu_bar_open_p = 0;
+	}
+    }
+
+  if (drawable)
+    BView_emacs_delete (drawable);
+
+  if (window)
+    BWindow_quit (window);
+
+  /* Free cursors */
+
+  BCursor_delete (f->output_data.haiku->text_cursor);
+  BCursor_delete (f->output_data.haiku->nontext_cursor);
+  BCursor_delete (f->output_data.haiku->modeline_cursor);
+  BCursor_delete (f->output_data.haiku->hand_cursor);
+  BCursor_delete (f->output_data.haiku->hourglass_cursor);
+  BCursor_delete (f->output_data.haiku->horizontal_drag_cursor);
+  BCursor_delete (f->output_data.haiku->vertical_drag_cursor);
+  BCursor_delete (f->output_data.haiku->left_edge_cursor);
+  BCursor_delete (f->output_data.haiku->top_left_corner_cursor);
+  BCursor_delete (f->output_data.haiku->top_edge_cursor);
+  BCursor_delete (f->output_data.haiku->top_right_corner_cursor);
+  BCursor_delete (f->output_data.haiku->right_edge_cursor);
+  BCursor_delete (f->output_data.haiku->bottom_right_corner_cursor);
+  BCursor_delete (f->output_data.haiku->bottom_edge_cursor);
+  BCursor_delete (f->output_data.haiku->bottom_left_corner_cursor);
+  BCursor_delete (f->output_data.haiku->no_cursor);
+
+  xfree (FRAME_OUTPUT_DATA (f));
+  FRAME_OUTPUT_DATA (f) = NULL;
+
+  unblock_input ();
+}
+
+void
+haiku_iconify_frame (struct frame *frame)
+{
+  if (FRAME_ICONIFIED_P (frame))
+    return;
+
+  block_input ();
+
+  SET_FRAME_VISIBLE (frame, false);
+  SET_FRAME_ICONIFIED (frame, true);
+
+  BWindow_iconify (FRAME_HAIKU_WINDOW (frame));
+
+  unblock_input ();
+}
+
+void
+haiku_set_offset (struct frame *frame, int x, int y,
+		  int change_gravity)
+{
+  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;
+    }
+
+  block_input ();
+  if (change_gravity)
+    BWindow_set_offset (FRAME_HAIKU_WINDOW (frame), x, y);
+  unblock_input ();
+}
+
+void
+haiku_visualize_frame (struct frame *f)
+{
+  block_input ();
+
+  if (!FRAME_VISIBLE_P (f))
+    {
+      if (FRAME_NO_FOCUS_ON_MAP (f))
+	BWindow_set_avoid_focus (FRAME_HAIKU_WINDOW (f), 1);
+      BWindow_set_visible (FRAME_HAIKU_WINDOW (f), 1);
+      if (FRAME_NO_FOCUS_ON_MAP (f) &&
+	  !FRAME_NO_ACCEPT_FOCUS (f))
+	BWindow_set_avoid_focus (FRAME_HAIKU_WINDOW (f), 0);
+
+      haiku_set_offset (f, f->left_pos, f->top_pos, 0);
+
+      SET_FRAME_VISIBLE (f, 1);
+      SET_FRAME_ICONIFIED (f, 0);
+    }
+
+  unblock_input ();
+}
+
+void
+haiku_unvisualize_frame (struct frame *f)
+{
+  block_input ();
+
+  BWindow_set_visible (FRAME_HAIKU_WINDOW (f), 0);
+  SET_FRAME_VISIBLE (f, 0);
+  SET_FRAME_ICONIFIED (f, 0);
+
+  unblock_input ();
+}
+
+void
+haiku_set_internal_border_width (struct frame *f, Lisp_Object arg, Lisp_Object oldval)
+{
+  int old_width = FRAME_INTERNAL_BORDER_WIDTH (f);
+  int new_width = check_int_nonnegative (arg);
+
+  if (new_width == old_width)
+    return;
+  f->internal_border_width = new_width;
+
+  if (FRAME_HAIKU_WINDOW (f))
+    {
+      adjust_frame_size (f, -1, -1, 3, 0, Qinternal_border_width);
+      haiku_clear_under_internal_border (f);
+    }
+
+  SET_FRAME_GARBAGED (f);
+}
+
+void
+haiku_set_frame_visible_invisible (struct frame *f, bool visible_p)
+{
+  if (visible_p)
+    haiku_visualize_frame (f);
+  else
+    haiku_unvisualize_frame (f);
+}
+
+void
+frame_set_mouse_pixel_position (struct frame *f, int pix_x, int pix_y)
+{
+  block_input ();
+
+  BView_convert_to_screen (FRAME_HAIKU_VIEW (f), &pix_x, &pix_y);
+  be_warp_pointer (pix_x, pix_y);
+
+  unblock_input ();
+}
+
+void
+haiku_query_color (uint32_t col, Emacs_Color *color_def)
+{
+  color_def->red = RED_FROM_ULONG (col) * 257;
+  color_def->green = GREEN_FROM_ULONG (col) * 257;
+  color_def->blue = BLUE_FROM_ULONG (col) * 257;
+
+  color_def->pixel = col;
+}
+
+Display_Info *
+check_x_display_info (Lisp_Object object)
+{
+  return check_haiku_display_info (object);
+}
+
+/* Rename frame F to NAME.  If NAME is nil, set F's name to "GNU
+   Emacs".  If EXPLICIT_P is non-zero, that indicates Lisp code is
+   setting the name, not redisplay; in that case, set F's name to NAME
+   and set F->explicit_name; if NAME is nil, clear F->explicit_name.
+
+   If EXPLICIT_P is zero, it means redisplay is setting the name; the
+   name provided will be ignored if explicit_name is set. */
+void
+haiku_set_name (struct frame *f, Lisp_Object name, bool explicit_p)
+{
+  if (explicit_p)
+    {
+      if (f->explicit_name && NILP (name))
+	update_mode_lines = 24;
+
+      f->explicit_name = !NILP (name);
+    }
+  else if (f->explicit_name)
+    return;
+
+  if (NILP (name))
+    name = build_unibyte_string ("GNU Emacs");
+
+  if (!NILP (Fstring_equal (name, f->name)))
+    return;
+
+  fset_name (f, name);
+
+  if (!NILP (f->title))
+    name = f->title;
+
+  haiku_set_title_bar_text (f, name);
+}
+
+static void
+haiku_set_inhibit_double_buffering (struct frame *f,
+				    Lisp_Object new_value,
+				    Lisp_Object old_value)
+{
+  block_input ();
+  if (FRAME_HAIKU_WINDOW (f))
+    {
+      if (NILP (new_value))
+	{
+	  EmacsView_set_up_double_buffering (FRAME_HAIKU_VIEW (f));
+	  if (!NILP (old_value))
+	    {
+	      SET_FRAME_GARBAGED (f);
+	      expose_frame (f, 0, 0, 0, 0);
+	    }
+	}
+      else
+	EmacsView_disable_double_buffering (FRAME_HAIKU_VIEW (f));
+    }
+  unblock_input ();
+}
+
+\f
+
+DEFUN ("haiku-set-mouse-absolute-pixel-position",
+       Fhaiku_set_mouse_absolute_pixel_position,
+       Shaiku_set_mouse_absolute_pixel_position, 2, 2, 0,
+       doc: /* Move mouse pointer to a pixel position at (X, Y).  The
+coordinates X and Y are interpreted to start from the top-left
+corner of the screen. */)
+  (Lisp_Object x, Lisp_Object y)
+{
+  int xval = check_integer_range (x, INT_MIN, INT_MAX);
+  int yval = check_integer_range (y, INT_MIN, INT_MAX);
+
+  if (!x_display_list)
+    error ("Window system not initialized");
+
+  block_input ();
+  be_warp_pointer (xval, yval);
+  unblock_input ();
+  return Qnil;
+}
+
+DEFUN ("haiku-mouse-absolute-pixel-position", Fhaiku_mouse_absolute_pixel_position,
+       Shaiku_mouse_absolute_pixel_position, 0, 0, 0,
+       doc: /* Return absolute position of mouse cursor in pixels.
+The position is returned as a cons cell (X . Y) of the coordinates of
+the mouse cursor position in pixels relative to a position (0, 0) of the
+selected frame's display.  */)
+  (void)
+{
+  if (!x_display_list)
+    return Qnil;
+
+  struct frame *f = SELECTED_FRAME ();
+
+  if (FRAME_INITIAL_P (f) || !FRAME_HAIKU_P (f)
+      || !FRAME_HAIKU_VIEW (f))
+    return Qnil;
+
+  block_input ();
+  void *view = FRAME_HAIKU_VIEW (f);
+
+  int x, y;
+  BView_get_mouse (view, &x, &y);
+  BView_convert_to_screen (view, &x, &y);
+  unblock_input ();
+
+  return Fcons (make_fixnum (x), make_fixnum (y));
+}
+
+DEFUN ("xw-display-color-p", Fxw_display_color_p, Sxw_display_color_p, 0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+     (Lisp_Object terminal)
+{
+  return Qt;
+}
+
+DEFUN ("xw-color-defined-p", Fxw_color_defined_p, Sxw_color_defined_p, 1, 2, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object color, Lisp_Object frame)
+{
+  Emacs_Color col;
+  CHECK_STRING (color);
+  decode_window_system_frame (frame);
+
+  return haiku_get_color (SSDATA (color), &col) ? Qnil : Qt;
+}
+
+DEFUN ("xw-color-values", Fxw_color_values, Sxw_color_values, 1, 2, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+     (Lisp_Object color, Lisp_Object frame)
+{
+  Emacs_Color col;
+  CHECK_STRING (color);
+  decode_window_system_frame (frame);
+
+  block_input ();
+  if (haiku_get_color (SSDATA (color), &col))
+    {
+      unblock_input ();
+      return Qnil;
+    }
+  unblock_input ();
+  return list3i (lrint (col.red), lrint (col.green), lrint (col.blue));
+}
+
+DEFUN ("x-display-grayscale-p", Fx_display_grayscale_p, Sx_display_grayscale_p,
+       0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object terminal)
+{
+  return Qnil;
+}
+
+DEFUN ("x-open-connection", Fx_open_connection, Sx_open_connection,
+       1, 3, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+     (Lisp_Object display, Lisp_Object resource_string, Lisp_Object must_succeed)
+{
+  struct haiku_display_info *dpy_info;
+  CHECK_STRING (display);
+
+  if (NILP (Fstring_equal (display, build_string ("be"))))
+    !NILP (must_succeed) ? fatal ("Bad display") : error ("Bad display");
+  dpy_info = haiku_term_init ();
+
+  if (!dpy_info)
+    !NILP (must_succeed) ? fatal ("Display not responding") :
+      error ("Display not responding");
+
+  return Qnil;
+}
+
+DEFUN ("x-display-pixel-width", Fx_display_pixel_width, Sx_display_pixel_width,
+       0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object terminal)
+
+{
+  check_haiku_display_info (terminal);
+
+  int width, height;
+  BScreen_px_dim (&width, &height);
+  return make_fixnum (width);
+}
+
+DEFUN ("x-display-pixel-height", Fx_display_pixel_height, Sx_display_pixel_height,
+       0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object terminal)
+
+{
+  check_haiku_display_info (terminal);
+
+  int width, height;
+  BScreen_px_dim (&width, &height);
+  return make_fixnum (width);
+}
+
+DEFUN ("x-display-mm-height", Fx_display_mm_height, Sx_display_mm_height, 0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object terminal)
+{
+  struct haiku_display_info *dpyinfo = check_haiku_display_info (terminal);
+
+  int width, height;
+  BScreen_px_dim (&width, &height);
+
+  return make_fixnum (height / (dpyinfo->resy / 25.4));
+}
+
+
+DEFUN ("x-display-mm-width", Fx_display_mm_width, Sx_display_mm_width, 0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object terminal)
+{
+  struct haiku_display_info *dpyinfo = check_haiku_display_info (terminal);
+
+  int width, height;
+  BScreen_px_dim (&width, &height);
+
+  return make_fixnum (height / (dpyinfo->resy / 25.4));
+}
+
+DEFUN ("x-create-frame", Fx_create_frame, Sx_create_frame,
+       1, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+     (Lisp_Object parms)
+{
+  return haiku_create_frame (parms, 0);
+}
+
+DEFUN ("x-display-visual-class", Fx_display_visual_class,
+       Sx_display_visual_class, 0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object terminal)
+{
+  check_haiku_display_info (terminal);
+
+  return intern ("direct-color");
+}
+
+DEFUN ("x-show-tip", Fx_show_tip, Sx_show_tip, 1, 6, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object string, Lisp_Object frame, Lisp_Object parms,
+   Lisp_Object timeout, Lisp_Object dx, Lisp_Object dy)
+{
+  struct frame *tip_f;
+  struct window *w;
+  int root_x, root_y;
+  struct buffer *old_buffer;
+  struct text_pos pos;
+  int width, height;
+  int old_windows_or_buffers_changed = windows_or_buffers_changed;
+  ptrdiff_t count = SPECPDL_INDEX ();
+  ptrdiff_t count_1;
+  Lisp_Object window, size, tip_buf;
+
+  if (haiku_use_system_tooltips)
+    {
+      CHECK_STRING (string);
+      if (STRING_MULTIBYTE (string))
+	string = ENCODE_UTF_8 (string);
+
+      if (NILP (frame))
+	frame = selected_frame;
+
+      struct frame *f = decode_window_system_frame (frame);
+      block_input ();
+      BView_set_tooltip (FRAME_HAIKU_VIEW (f), SSDATA (string));
+      BView_show_tooltip (FRAME_HAIKU_VIEW (f));
+      unblock_input ();
+      goto start_timer;
+    }
+  AUTO_STRING (tip, " *tip*");
+
+  specbind (Qinhibit_redisplay, Qt);
+
+  CHECK_STRING (string);
+
+  if (NILP (frame))
+    frame = selected_frame;
+  decode_window_system_frame (frame);
+
+  if (NILP (timeout))
+    timeout = make_fixnum (5);
+  else
+    CHECK_FIXNAT (timeout);
+
+  if (NILP (dx))
+    dx = make_fixnum (5);
+  else
+    CHECK_FIXNUM (dx);
+
+  if (NILP (dy))
+    dy = make_fixnum (-10);
+  else
+    CHECK_FIXNUM (dy);
+
+  if (FRAMEP (tip_frame) && FRAME_LIVE_P (XFRAME (tip_frame)))
+    {
+      if (FRAME_VISIBLE_P (XFRAME (tip_frame))
+	  && EQ (frame, tip_last_frame)
+	  && !NILP (Fequal_including_properties (string, tip_last_string))
+	  && !NILP (Fequal (parms, tip_last_parms)))
+	{
+	  /* Only DX and DY have changed.  */
+	  tip_f = XFRAME (tip_frame);
+	  if (!NILP (tip_timer))
+	    {
+	      Lisp_Object timer = tip_timer;
+
+	      tip_timer = Qnil;
+	      call1 (Qcancel_timer, timer);
+	    }
+
+	  block_input ();
+	  compute_tip_xy (tip_f, parms, dx, dy, FRAME_PIXEL_WIDTH (tip_f),
+			  FRAME_PIXEL_HEIGHT (tip_f), &root_x, &root_y);
+	  haiku_set_offset (tip_f, root_x, root_y, 1);
+	  haiku_visualize_frame (tip_f);
+	  unblock_input ();
+
+	  goto start_timer;
+	}
+      else if (tooltip_reuse_hidden_frame && EQ (frame, tip_last_frame))
+	{
+	  bool delete = false;
+	  Lisp_Object tail, elt, parm, last;
+
+	  /* Check if every parameter in PARMS has the same value in
+	     tip_last_parms.  This may destruct tip_last_parms
+	     which, however, will be recreated below.  */
+	  for (tail = parms; CONSP (tail); tail = XCDR (tail))
+	    {
+	      elt = XCAR (tail);
+	      parm = Fcar (elt);
+	      /* The left, top, right and bottom parameters are handled
+		 by compute_tip_xy so they can be ignored here.  */
+	      if (!EQ (parm, Qleft) && !EQ (parm, Qtop)
+		  && !EQ (parm, Qright) && !EQ (parm, Qbottom))
+		{
+		  last = Fassq (parm, tip_last_parms);
+		  if (NILP (Fequal (Fcdr (elt), Fcdr (last))))
+		    {
+		      /* We lost, delete the old tooltip.  */
+		      delete = true;
+		      break;
+		    }
+		  else
+		    tip_last_parms =
+		      call2 (Qassq_delete_all, parm, tip_last_parms);
+		}
+	      else
+		tip_last_parms =
+		  call2 (Qassq_delete_all, parm, tip_last_parms);
+	    }
+
+	  /* Now check if there's a parameter left in tip_last_parms with a
+	     non-nil value.  */
+	  for (tail = tip_last_parms; CONSP (tail); tail = XCDR (tail))
+	    {
+	      elt = XCAR (tail);
+	      parm = Fcar (elt);
+	      if (!EQ (parm, Qleft) && !EQ (parm, Qtop) && !EQ (parm, Qright)
+		  && !EQ (parm, Qbottom) && !NILP (Fcdr (elt)))
+		{
+		  /* We lost, delete the old tooltip.  */
+		  delete = true;
+		  break;
+		}
+	    }
+
+	  haiku_hide_tip (delete);
+	}
+      else
+	haiku_hide_tip (true);
+    }
+  else
+    haiku_hide_tip (true);
+
+  tip_last_frame = frame;
+  tip_last_string = string;
+  tip_last_parms = parms;
+
+  /* Block input until the tip has been fully drawn, to avoid crashes
+     when drawing tips in menus.  */
+  block_input ();
+
+  if (!FRAMEP (tip_frame) || !FRAME_LIVE_P (XFRAME (tip_frame)))
+    {
+      /* Add default values to frame parameters.  */
+      if (NILP (Fassq (Qname, parms)))
+	parms = Fcons (Fcons (Qname, build_string ("tooltip")), parms);
+      if (NILP (Fassq (Qinternal_border_width, parms)))
+	parms = Fcons (Fcons (Qinternal_border_width, make_fixnum (3)), parms);
+      if (NILP (Fassq (Qborder_width, parms)))
+	parms = Fcons (Fcons (Qborder_width, make_fixnum (1)), parms);
+      if (NILP (Fassq (Qborder_color, parms)))
+	parms = Fcons (Fcons (Qborder_color, build_string ("lightyellow")),
+		       parms);
+      if (NILP (Fassq (Qbackground_color, parms)))
+	parms = Fcons (Fcons (Qbackground_color, build_string ("lightyellow")),
+		       parms);
+
+      /* Create a frame for the tooltip and record it in the global
+	 variable tip_frame.  */
+
+      if (NILP (tip_frame = haiku_create_frame (parms, 1)))
+	{
+	  /* Creating the tip frame failed.  */
+	  unblock_input ();
+	  return unbind_to (count, Qnil);
+	}
+    }
+
+  tip_f = XFRAME (tip_frame);
+  window = FRAME_ROOT_WINDOW (tip_f);
+  tip_buf = Fget_buffer_create (tip, Qnil);
+  /* We will mark the tip window a "pseudo-window" below, and such
+     windows cannot have display margins.  */
+  bset_left_margin_cols (XBUFFER (tip_buf), make_fixnum (0));
+  bset_right_margin_cols (XBUFFER (tip_buf), make_fixnum (0));
+  set_window_buffer (window, tip_buf, false, false);
+  w = XWINDOW (window);
+  w->pseudo_window_p = true;
+  /* Try to avoid that `other-window' select us (Bug#47207).  */
+  Fset_window_parameter (window, Qno_other_window, Qt);
+
+  /* Set up the frame's root window.  Note: The following code does not
+     try to size the window or its frame correctly.  Its only purpose is
+     to make the subsequent text size calculations work.  The right
+     sizes should get installed when the toolkit gets back to us.  */
+  w->left_col = 0;
+  w->top_line = 0;
+  w->pixel_left = 0;
+  w->pixel_top = 0;
+
+  if (CONSP (Vx_max_tooltip_size)
+      && RANGED_FIXNUMP (1, XCAR (Vx_max_tooltip_size), INT_MAX)
+      && RANGED_FIXNUMP (1, XCDR (Vx_max_tooltip_size), INT_MAX))
+    {
+      w->total_cols = XFIXNAT (XCAR (Vx_max_tooltip_size));
+      w->total_lines = XFIXNAT (XCDR (Vx_max_tooltip_size));
+    }
+  else
+    {
+      w->total_cols = 80;
+      w->total_lines = 40;
+    }
+
+  w->pixel_width = w->total_cols * FRAME_COLUMN_WIDTH (tip_f);
+  w->pixel_height = w->total_lines * FRAME_LINE_HEIGHT (tip_f);
+  FRAME_TOTAL_COLS (tip_f) = WINDOW_TOTAL_COLS (w);
+  adjust_frame_glyphs (tip_f);
+
+  /* Insert STRING into the root window's buffer and fit the frame to
+     the buffer.  */
+  count_1 = SPECPDL_INDEX ();
+  old_buffer = current_buffer;
+  set_buffer_internal_1 (XBUFFER (w->contents));
+  bset_truncate_lines (current_buffer, Qnil);
+  specbind (Qinhibit_read_only, Qt);
+  specbind (Qinhibit_modification_hooks, Qt);
+  specbind (Qinhibit_point_motion_hooks, Qt);
+  Ferase_buffer ();
+  Finsert (1, &string);
+  clear_glyph_matrix (w->desired_matrix);
+  clear_glyph_matrix (w->current_matrix);
+  SET_TEXT_POS (pos, BEGV, BEGV_BYTE);
+  try_window (window, pos, TRY_WINDOW_IGNORE_FONTS_CHANGE);
+  /* Calculate size of tooltip window.  */
+  size = Fwindow_text_pixel_size (window, Qnil, Qnil, Qnil,
+				  make_fixnum (w->pixel_height), Qnil);
+  /* Add the frame's internal border to calculated size.  */
+  width = XFIXNUM (Fcar (size)) + 2 * FRAME_INTERNAL_BORDER_WIDTH (tip_f);
+  height = XFIXNUM (Fcdr (size)) + 2 * FRAME_INTERNAL_BORDER_WIDTH (tip_f);
+  /* Calculate position of tooltip frame.  */
+  compute_tip_xy (tip_f, parms, dx, dy, width, height, &root_x, &root_y);
+  BWindow_resize (FRAME_HAIKU_WINDOW (tip_f), width, height);
+  haiku_set_offset (tip_f, root_x, root_y, 1);
+  BWindow_set_tooltip_decoration (FRAME_HAIKU_WINDOW (tip_f));
+  BView_set_view_cursor (FRAME_HAIKU_VIEW (tip_f),
+			 FRAME_OUTPUT_DATA (XFRAME (frame))->current_cursor);
+  SET_FRAME_VISIBLE (tip_f, 1);
+  BWindow_set_visible (FRAME_HAIKU_WINDOW (tip_f), 1);
+
+  w->must_be_updated_p = true;
+  update_single_window (w);
+  set_buffer_internal_1 (old_buffer);
+  unbind_to (count_1, Qnil);
+  unblock_input ();
+  windows_or_buffers_changed = old_windows_or_buffers_changed;
+
+ start_timer:
+  /* Let the tip disappear after timeout seconds.  */
+  tip_timer = call3 (intern ("run-at-time"), timeout, Qnil,
+		     intern ("x-hide-tip"));
+
+  return unbind_to (count, Qnil);
+}
+
+DEFUN ("x-hide-tip", Fx_hide_tip, Sx_hide_tip, 0, 0, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (void)
+{
+  return haiku_hide_tip (!tooltip_reuse_hidden_frame);
+}
+
+DEFUN ("x-close-connection", Fx_close_connection, Sx_close_connection, 1, 1, 0,
+       doc: /* SKIP: real doc in xfns.c. */
+       attributes: noreturn)
+  (Lisp_Object terminal)
+{
+  check_haiku_display_info (terminal);
+
+  error ("Cannot close Haiku displays");
+}
+
+DEFUN ("x-display-list", Fx_display_list, Sx_display_list, 0, 0, 0,
+       doc: /* SKIP: real doc in xfns.c. */)
+  (void)
+{
+  if (!x_display_list)
+    return Qnil;
+
+  return list1 (x_display_list->name_list_element);
+}
+
+DEFUN ("x-server-vendor", Fx_server_vendor, Sx_server_vendor, 0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object terminal)
+{
+  check_haiku_display_info (terminal);
+  return build_string ("Haiku, Inc.");
+}
+
+DEFUN ("x-server-version", Fx_server_version, Sx_server_version, 0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c. */)
+  (Lisp_Object terminal)
+{
+  check_haiku_display_info (terminal);
+  return list3i (5, 1, 1);
+}
+
+DEFUN ("x-display-screens", Fx_display_screens, Sx_display_screens, 0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object terminal)
+{
+  check_haiku_display_info (terminal);
+  return make_fixnum (1);
+}
+
+DEFUN ("haiku-get-version-string", Fhaiku_get_version_string,
+       Shaiku_get_version_string, 0, 0, 0,
+       doc: /* Return a string describing the current Haiku version. */)
+  (void)
+{
+  char buf[1024];
+
+  be_get_version_string ((char *) &buf, sizeof buf);
+  return build_string (buf);
+}
+
+DEFUN ("x-display-color-cells", Fx_display_color_cells, Sx_display_color_cells,
+       0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object terminal)
+{
+  check_haiku_display_info (terminal);
+
+  return make_fixnum (be_get_display_color_cells ());
+}
+
+DEFUN ("x-display-planes", Fx_display_planes, Sx_display_planes,
+       0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object terminal)
+{
+  check_haiku_display_info (terminal);
+
+  return make_fixnum (be_get_display_planes ());
+}
+
+DEFUN ("x-double-buffered-p", Fx_double_buffered_p, Sx_double_buffered_p,
+       0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object frame)
+{
+  struct frame *f = decode_live_frame (frame);
+  check_window_system (f);
+
+  return EmacsView_double_buffered_p (FRAME_HAIKU_VIEW (f)) ? Qt : Qnil;
+}
+
+DEFUN ("x-display-backing-store", Fx_display_backing_store, Sx_display_backing_store,
+       0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c. */)
+  (Lisp_Object terminal)
+{
+  if (FRAMEP (terminal))
+    {
+      CHECK_LIVE_FRAME (terminal);
+      struct frame *f = decode_window_system_frame (terminal);
+
+      if (FRAME_HAIKU_VIEW (f) &&
+	  EmacsView_double_buffered_p (FRAME_HAIKU_VIEW (f)))
+	return Qalways;
+      else
+	return Qnot_useful;
+    }
+  else
+    {
+      check_haiku_display_info (terminal);
+      return Qnot_useful;
+    }
+}
+
+DEFUN ("haiku-frame-geometry", Fhaiku_frame_geometry, Shaiku_frame_geometry, 0, 1, 0,
+       doc: /* Return geometric attributes of FRAME.
+FRAME must be a live frame and defaults to the selected one.  The return
+value is an association list of the attributes listed below.  All height
+and width values are in pixels.
+
+`outer-position' is a cons of the outer left and top edges of FRAME
+  relative to the origin - the position (0, 0) - of FRAME's display.
+
+`outer-size' is a cons of the outer width and height of FRAME.  The
+  outer size includes the title bar and the external borders as well as
+  any menu and/or tool bar of frame.
+
+`external-border-size' is a cons of the horizontal and vertical width of
+  FRAME's external borders as supplied by the window manager.
+
+`title-bar-size' is a cons of the width and height of the title bar of
+  FRAME as supplied by the window manager.  If both of them are zero,
+  FRAME has no title bar.  If only the width is zero, Emacs was not
+  able to retrieve the width information.
+
+`menu-bar-external', if non-nil, means the menu bar is external (never
+  included in the inner edges of FRAME).
+
+`menu-bar-size' is a cons of the width and height of the menu bar of
+  FRAME.
+
+`tool-bar-external', if non-nil, means the tool bar is external (never
+  included in the inner edges of FRAME).
+
+`tool-bar-position' tells on which side the tool bar on FRAME is and can
+  be one of `left', `top', `right' or `bottom'.  If this is nil, FRAME
+  has no tool bar.
+
+`tool-bar-size' is a cons of the width and height of the tool bar of
+  FRAME.
+
+`internal-border-width' is the width of the internal border of
+  FRAME.  */)
+  (Lisp_Object frame)
+{
+  return frame_geometry (frame, Qnil);
+}
+
+DEFUN ("haiku-frame-edges", Fhaiku_frame_edges, Shaiku_frame_edges, 0, 2, 0,
+       doc: /* Return edge coordinates of FRAME.
+FRAME must be a live frame and defaults to the selected one.  The return
+value is a list of the form (LEFT, TOP, RIGHT, BOTTOM).  All values are
+in pixels relative to the origin - the position (0, 0) - of FRAME's
+display.
+
+If optional argument TYPE is the symbol `outer-edges', return the outer
+edges of FRAME.  The outer edges comprise the decorations of the window
+manager (like the title bar or external borders) as well as any external
+menu or tool bar of FRAME.  If optional argument TYPE is the symbol
+`native-edges' or nil, return the native edges of FRAME.  The native
+edges exclude the decorations of the window manager and any external
+menu or tool bar of FRAME.  If TYPE is the symbol `inner-edges', return
+the inner edges of FRAME.  These edges exclude title bar, any borders,
+menu bar or tool bar of FRAME.  */)
+  (Lisp_Object frame, Lisp_Object type)
+{
+  return frame_geometry (frame, ((EQ (type, Qouter_edges)
+				  || EQ (type, Qinner_edges))
+				 ? type
+				 : Qnative_edges));
+}
+
+DEFUN ("haiku-read-file-name", Fhaiku_read_file_name, Shaiku_read_file_name, 1, 6, 0,
+       doc: /* Use a graphical panel to read a file name, using prompt PROMPT.
+Optional arg FRAME specifies a frame on which to display the file panel.
+If it is nil, the current frame is used instead.
+Optional arg DIR, if non-nil, supplies a default directory.
+Optional arg MUSTMATCH, if non-nil, means the returned file or
+directory must exist.
+Optional arg DIR_ONLY_P, if non-nil, means choose only directories.
+Optional arg SAVE_TEXT, if non-nil, specifies some text to show in the entry field.  */)
+  (Lisp_Object prompt, Lisp_Object frame,
+   Lisp_Object dir, Lisp_Object mustmatch,
+   Lisp_Object dir_only_p, Lisp_Object save_text)
+{
+  ptrdiff_t idx;
+  if (!x_display_list)
+    error ("Be windowing not initialized");
+
+  if (!NILP (dir))
+    CHECK_STRING (dir);
+
+  if (!NILP (save_text))
+    CHECK_STRING (save_text);
+
+  if (NILP (frame))
+    frame = selected_frame;
+
+  CHECK_STRING (prompt);
+
+  CHECK_LIVE_FRAME (frame);
+  check_window_system (XFRAME (frame));
+
+  idx = SPECPDL_INDEX ();
+  record_unwind_protect_void (unwind_popup);
+
+  struct frame *f = XFRAME (frame);
+
+  FRAME_DISPLAY_INFO (f)->focus_event_frame = f;
+
+  ++popup_activated_p;
+  char *fn = be_popup_file_dialog (!NILP (mustmatch) || !NILP (dir_only_p),
+				   !NILP (dir) ? SSDATA (ENCODE_UTF_8 (dir)) : NULL,
+				   !NILP (mustmatch), !NILP (dir_only_p),
+				   FRAME_HAIKU_WINDOW (f),
+				   !NILP (save_text) ? SSDATA (ENCODE_UTF_8 (save_text)) : NULL,
+				   SSDATA (ENCODE_UTF_8 (prompt)),
+				   block_input, unblock_input);
+
+  unbind_to (idx, Qnil);
+  if (!fn)
+    return Qnil;
+
+  Lisp_Object p = build_string_from_utf8 (fn);
+  free (fn);
+  return p;
+}
+
+DEFUN ("haiku-put-resource", Fhaiku_put_resource, Shaiku_put_resource,
+       2, 2, 0, doc: /* Place STRING by the key RESOURCE in the resource database.
+It can later be retrieved with `x-get-resource'. */)
+  (Lisp_Object resource, Lisp_Object string)
+{
+  CHECK_STRING (resource);
+  if (!NILP (string))
+    CHECK_STRING (string);
+
+  put_xrm_resource (resource, string);
+  return Qnil;
+}
+
+DEFUN ("haiku-frame-list-z-order", Fhaiku_frame_list_z_order,
+       Shaiku_frame_list_z_order, 0, 1, 0,
+       doc: /* Return list of Emacs' frames, in Z (stacking) order.
+If TERMINAL is non-nil and specifies a live frame, return the child
+frames of that frame in Z (stacking) order.
+
+As it is impossible to reliably determine the frame stacking order on
+Haiku, the selected frame is always the first element of the returned
+list, while the rest are not guaranteed to be in any particular order.
+
+Frames are listed from topmost (first) to bottommost (last).  */)
+  (Lisp_Object terminal)
+{
+  Lisp_Object frames = Qnil;
+  Lisp_Object head, tail;
+  Lisp_Object sel = Qnil;
+
+  FOR_EACH_FRAME (head, tail)
+    {
+      struct frame *f = XFRAME (tail);
+      if (!FRAME_HAIKU_P (f) ||
+	  (FRAMEP (terminal) &&
+	   FRAME_LIVE_P (XFRAME (terminal)) &&
+	   !EQ (terminal, get_frame_param (f, Qparent_frame))))
+	continue;
+
+      if (EQ (tail, selected_frame))
+	sel = tail;
+      else
+	frames = Fcons (tail, frames);
+    }
+
+  if (NILP (sel))
+    return frames;
+  return Fcons (sel, frames);
+}
+
+DEFUN ("x-display-save-under", Fx_display_save_under,
+       Sx_display_save_under, 0, 1, 0,
+       doc: /* SKIP: real doc in xfns.c.  */)
+  (Lisp_Object terminal)
+{
+  check_haiku_display_info (terminal);
+
+  if (FRAMEP (terminal))
+    {
+      struct frame *f = decode_window_system_frame (terminal);
+      return FRAME_HAIKU_VIEW (f) && EmacsView_double_buffered_p (FRAME_HAIKU_VIEW (f)) ?
+	Qt : Qnil;
+    }
+
+  return Qnil;
+}
+
+frame_parm_handler haiku_frame_parm_handlers[] =
+  {
+    gui_set_autoraise,
+    gui_set_autolower,
+    haiku_set_background_color,
+    NULL, /* x_set_border_color */
+    gui_set_border_width,
+    haiku_set_cursor_color,
+    haiku_set_cursor_type,
+    gui_set_font,
+    haiku_set_foreground_color,
+    NULL, /* set icon name */
+    NULL, /* set icon type */
+    haiku_set_child_frame_border_width,
+    haiku_set_internal_border_width,
+    gui_set_right_divider_width,
+    gui_set_bottom_divider_width,
+    haiku_set_menu_bar_lines,
+    NULL, /* set mouse color */
+    haiku_explicitly_set_name,
+    gui_set_scroll_bar_width,
+    gui_set_scroll_bar_height,
+    haiku_set_title,
+    gui_set_unsplittable,
+    gui_set_vertical_scroll_bars,
+    gui_set_horizontal_scroll_bars,
+    gui_set_visibility,
+    haiku_set_tab_bar_lines,
+    haiku_set_tool_bar_lines,
+    NULL, /* set scroll bar fg */
+    NULL, /* set scroll bar bkg */
+    gui_set_screen_gamma,
+    gui_set_line_spacing,
+    gui_set_left_fringe,
+    gui_set_right_fringe,
+    NULL, /* x wait for wm */
+    gui_set_fullscreen,
+    gui_set_font_backend,
+    gui_set_alpha,
+    NULL, /* set sticky */
+    NULL, /* set tool bar pos */
+    haiku_set_inhibit_double_buffering,
+    haiku_set_undecorated,
+    haiku_set_parent_frame,
+    NULL, /* set skip taskbar */
+    haiku_set_no_focus_on_map,
+    haiku_set_no_accept_focus,
+    NULL, /* set z group */
+    NULL, /* set override redir */
+    gui_set_no_special_glyphs
+  };
+
+void
+syms_of_haikufns (void)
+{
+  DEFSYM (Qfont_parameter, "font-parameter");
+  DEFSYM (Qcancel_timer, "cancel-timer");
+  DEFSYM (Qassq_delete_all, "assq-delete-all");
+
+  DEFSYM (Qalways, "always");
+  DEFSYM (Qnot_useful, "not-useful");
+
+  defsubr (&Sx_hide_tip);
+  defsubr (&Sxw_display_color_p);
+  defsubr (&Sx_display_grayscale_p);
+  defsubr (&Sx_open_connection);
+  defsubr (&Sx_create_frame);
+  defsubr (&Sx_display_pixel_width);
+  defsubr (&Sx_display_pixel_height);
+  defsubr (&Sxw_color_values);
+  defsubr (&Sxw_color_defined_p);
+  defsubr (&Sx_display_visual_class);
+  defsubr (&Sx_show_tip);
+  defsubr (&Sx_display_mm_height);
+  defsubr (&Sx_display_mm_width);
+  defsubr (&Sx_close_connection);
+  defsubr (&Sx_display_list);
+  defsubr (&Sx_server_vendor);
+  defsubr (&Sx_server_version);
+  defsubr (&Sx_display_screens);
+  defsubr (&Shaiku_get_version_string);
+  defsubr (&Sx_display_color_cells);
+  defsubr (&Sx_display_planes);
+  defsubr (&Shaiku_set_mouse_absolute_pixel_position);
+  defsubr (&Shaiku_mouse_absolute_pixel_position);
+  defsubr (&Shaiku_frame_geometry);
+  defsubr (&Shaiku_frame_edges);
+  defsubr (&Sx_double_buffered_p);
+  defsubr (&Sx_display_backing_store);
+  defsubr (&Shaiku_read_file_name);
+  defsubr (&Shaiku_put_resource);
+  defsubr (&Shaiku_frame_list_z_order);
+  defsubr (&Sx_display_save_under);
+
+  tip_timer = Qnil;
+  staticpro (&tip_timer);
+  tip_frame = Qnil;
+  staticpro (&tip_frame);
+  tip_last_frame = Qnil;
+  staticpro (&tip_last_frame);
+  tip_last_string = Qnil;
+  staticpro (&tip_last_string);
+  tip_last_parms = Qnil;
+  staticpro (&tip_last_parms);
+
+  DEFVAR_LISP ("x-max-tooltip-size", Vx_max_tooltip_size,
+	       doc: /* SKIP: real doc in xfns.c.  */);
+  Vx_max_tooltip_size = Fcons (make_fixnum (80), make_fixnum (40));
+
+  DEFVAR_BOOL ("haiku-use-system-tooltips", haiku_use_system_tooltips,
+	       doc: /* When non-nil, Emacs will display tooltips using the App Kit.
+This can avoid a great deal of consing that does not play
+well with the Haiku memory allocator, but comes with the
+disadvantage of not being able to use special display properties
+within tooltips. */);
+  haiku_use_system_tooltips = 1;
+
+  return;
+}
diff --git a/src/haikufont.c b/src/haikufont.c
new file mode 100644
index 0000000000..7b7c3e6e96
--- /dev/null
+++ b/src/haikufont.c
@@ -0,0 +1,942 @@
+/* Font support for Haiku windowing
+
+Copyright (C) 2021 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 "lisp.h"
+#include "dispextern.h"
+#include "composite.h"
+#include "blockinput.h"
+#include "charset.h"
+#include "frame.h"
+#include "window.h"
+#include "fontset.h"
+#include "haikuterm.h"
+#include "character.h"
+#include "font.h"
+#include "termchar.h"
+#include "pdumper.h"
+#include "haiku_support.h"
+
+#include <math.h>
+#include <stdlib.h>
+
+static Lisp_Object
+haikufont_get_fallback_entity (void)
+{
+  Lisp_Object ent = font_make_entity ();
+  ASET (ent, FONT_TYPE_INDEX, Qhaiku);
+  ASET (ent, FONT_FOUNDRY_INDEX, Qhaiku);
+  ASET (ent, FONT_FAMILY_INDEX, Qnil);
+  ASET (ent, FONT_ADSTYLE_INDEX, Qnil);
+  ASET (ent, FONT_REGISTRY_INDEX, Qiso10646_1);
+  ASET (ent, FONT_SIZE_INDEX, make_fixnum (0));
+  ASET (ent, FONT_AVGWIDTH_INDEX, make_fixnum (0));
+  ASET (ent, FONT_SPACING_INDEX, make_fixnum (FONT_SPACING_MONO));
+  FONT_SET_STYLE (ent, FONT_WIDTH_INDEX, Qnil);
+  FONT_SET_STYLE (ent, FONT_WEIGHT_INDEX, Qnil);
+  FONT_SET_STYLE (ent, FONT_SLANT_INDEX, Qnil);
+
+  return ent;
+}
+
+static Lisp_Object
+haikufont_get_cache (struct frame *frame)
+{
+  return x_display_list->name_list_element;
+}
+
+static Lisp_Object
+haikufont_weight_to_lisp (int weight)
+{
+  switch (weight)
+    {
+    case HAIKU_THIN:
+      return Qthin;
+    case HAIKU_ULTRALIGHT:
+      return Qultra_light;
+    case HAIKU_EXTRALIGHT:
+      return Qextra_light;
+    case HAIKU_LIGHT:
+      return Qlight;
+    case HAIKU_SEMI_LIGHT:
+      return Qsemi_light;
+    case HAIKU_REGULAR:
+      return Qnormal;
+    case HAIKU_SEMI_BOLD:
+      return Qsemi_bold;
+    case HAIKU_BOLD:
+      return Qbold;
+    case HAIKU_EXTRA_BOLD:
+      return Qextra_bold;
+    case HAIKU_ULTRA_BOLD:
+      return Qultra_bold;
+    }
+  emacs_abort ();
+}
+
+static int
+haikufont_lisp_to_weight (Lisp_Object weight)
+{
+  if (EQ (weight, Qthin))
+    return HAIKU_THIN;
+  if (EQ (weight, Qultra_light))
+    return HAIKU_ULTRALIGHT;
+  if (EQ (weight, Qextra_light))
+    return HAIKU_EXTRALIGHT;
+  if (EQ (weight, Qlight))
+    return HAIKU_LIGHT;
+  if (EQ (weight, Qsemi_light))
+    return HAIKU_SEMI_LIGHT;
+  if (EQ (weight, Qnormal))
+    return HAIKU_REGULAR;
+  if (EQ (weight, Qsemi_bold))
+    return HAIKU_SEMI_BOLD;
+  if (EQ (weight, Qbold))
+    return HAIKU_BOLD;
+  if (EQ (weight, Qextra_bold))
+    return HAIKU_EXTRA_BOLD;
+  if (EQ (weight, Qultra_bold))
+    return HAIKU_ULTRA_BOLD;
+
+  emacs_abort ();
+}
+
+static Lisp_Object
+haikufont_slant_to_lisp (enum haiku_font_slant slant)
+{
+  switch (slant)
+    {
+    case NO_SLANT:
+      emacs_abort ();
+    case SLANT_ITALIC:
+      return Qitalic;
+    case SLANT_REGULAR:
+      return Qnormal;
+    case SLANT_OBLIQUE:
+      return Qoblique;
+    }
+  emacs_abort ();
+}
+
+static enum haiku_font_slant
+haikufont_lisp_to_slant (Lisp_Object slant)
+{
+  if (EQ (slant, Qitalic) ||
+      EQ (slant, Qreverse_italic))
+    return SLANT_ITALIC;
+  if (EQ (slant, Qoblique) ||
+      EQ (slant, Qreverse_oblique))
+    return SLANT_OBLIQUE;
+  if (EQ (slant, Qnormal))
+    return SLANT_REGULAR;
+  emacs_abort ();
+}
+
+static Lisp_Object
+haikufont_width_to_lisp (enum haiku_font_width width)
+{
+  switch (width)
+    {
+    case NO_WIDTH:
+      emacs_abort ();
+    case ULTRA_CONDENSED:
+      return Qultra_condensed;
+    case EXTRA_CONDENSED:
+      return Qextra_condensed;
+    case CONDENSED:
+      return Qcondensed;
+    case SEMI_CONDENSED:
+      return Qsemi_condensed;
+    case NORMAL_WIDTH:
+      return Qnormal;
+    case SEMI_EXPANDED:
+      return Qsemi_expanded;
+    case EXPANDED:
+      return Qexpanded;
+    case EXTRA_EXPANDED:
+      return Qextra_expanded;
+    case ULTRA_EXPANDED:
+      return Qultra_expanded;
+    }
+
+  emacs_abort ();
+}
+
+static enum haiku_font_width
+haikufont_lisp_to_width (Lisp_Object lisp)
+{
+  if (EQ (lisp, Qultra_condensed))
+    return ULTRA_CONDENSED;
+  if (EQ (lisp, Qextra_condensed))
+    return EXTRA_CONDENSED;
+  if (EQ (lisp, Qcondensed))
+    return CONDENSED;
+  if (EQ (lisp, Qsemi_condensed))
+    return SEMI_CONDENSED;
+  if (EQ (lisp, Qnormal))
+    return NORMAL_WIDTH;
+  if (EQ (lisp, Qexpanded))
+    return EXPANDED;
+  if (EQ (lisp, Qextra_expanded))
+    return EXTRA_EXPANDED;
+  if (EQ (lisp, Qultra_expanded))
+    return ULTRA_EXPANDED;
+  emacs_abort ();
+}
+
+static int
+haikufont_maybe_handle_special_family (Lisp_Object family,
+				       struct haiku_font_pattern *ptn)
+{
+  CHECK_SYMBOL (family);
+
+  if (EQ (family, Qmonospace) || EQ (family, Qfixed) ||
+      EQ (family, Qdefault))
+    {
+      BFont_populate_fixed_family (ptn);
+      return 1;
+    }
+  else if (EQ (family, intern ("Sans Serif")))
+    {
+      BFont_populate_plain_family (ptn);
+      return 1;
+    }
+  return 0;
+}
+
+static Lisp_Object
+haikufont_pattern_to_entity (struct haiku_font_pattern *ptn)
+{
+  Lisp_Object ent = font_make_entity ();
+  ASET (ent, FONT_TYPE_INDEX, Qhaiku);
+  ASET (ent, FONT_FOUNDRY_INDEX, Qhaiku);
+  ASET (ent, FONT_FAMILY_INDEX, Qdefault);
+  ASET (ent, FONT_ADSTYLE_INDEX, Qnil);
+  ASET (ent, FONT_REGISTRY_INDEX, Qiso10646_1);
+  ASET (ent, FONT_SIZE_INDEX, make_fixnum (0));
+  ASET (ent, FONT_AVGWIDTH_INDEX, make_fixnum (0));
+  ASET (ent, FONT_SPACING_INDEX, make_fixnum (FONT_SPACING_MONO));
+  FONT_SET_STYLE (ent, FONT_WIDTH_INDEX, Qnormal);
+  FONT_SET_STYLE (ent, FONT_WEIGHT_INDEX, Qnormal);
+  FONT_SET_STYLE (ent, FONT_SLANT_INDEX, Qnormal);
+
+  if (ptn->specified & FSPEC_FAMILY)
+    ASET (ent, FONT_FAMILY_INDEX, intern (ptn->family));
+  else
+    ASET (ent, FONT_FAMILY_INDEX, Qdefault);
+
+  if (ptn->specified & FSPEC_STYLE)
+    ASET (ent, FONT_ADSTYLE_INDEX, intern (ptn->style));
+  else
+    {
+      if (ptn->specified & FSPEC_WEIGHT)
+	FONT_SET_STYLE (ent, FONT_WEIGHT_INDEX,
+			haikufont_weight_to_lisp (ptn->weight));
+      if (ptn->specified & FSPEC_SLANT)
+	FONT_SET_STYLE (ent, FONT_SLANT_INDEX,
+			haikufont_slant_to_lisp (ptn->slant));
+      if (ptn->specified & FSPEC_WIDTH)
+	FONT_SET_STYLE (ent, FONT_WIDTH_INDEX,
+			haikufont_width_to_lisp (ptn->width));
+    }
+
+  if (ptn->specified & FSPEC_SPACING)
+    ASET (ent, FONT_SPACING_INDEX,
+	  make_fixnum (ptn->mono_spacing_p ?
+		       FONT_SPACING_MONO : FONT_SPACING_PROPORTIONAL));
+  return ent;
+}
+
+static void
+haikufont_spec_or_entity_to_pattern (Lisp_Object ent,
+				     int list_p,
+				     struct haiku_font_pattern *ptn)
+{
+  Lisp_Object tem;
+  ptn->specified = 0;
+
+  tem = AREF (ent, FONT_ADSTYLE_INDEX);
+  if (!NILP (tem))
+    {
+      ptn->specified |= FSPEC_STYLE;
+      strncpy ((char *) &ptn->style,
+	       SSDATA (SYMBOL_NAME (tem)),
+	       sizeof ptn->style - 1);
+    }
+
+  tem = FONT_SLANT_SYMBOLIC (ent);
+  if (!NILP (tem))
+    {
+      ptn->specified |= FSPEC_SLANT;
+      ptn->slant = haikufont_lisp_to_slant (tem);
+    }
+
+  tem = FONT_WEIGHT_SYMBOLIC (ent);
+  if (!NILP (tem))
+    {
+      ptn->specified |= FSPEC_WEIGHT;
+      ptn->weight = haikufont_lisp_to_weight (tem);
+    }
+
+  tem = FONT_WIDTH_SYMBOLIC (ent);
+  if (!NILP (tem))
+    {
+      ptn->specified |= FSPEC_WIDTH;
+      ptn->width = haikufont_lisp_to_width (tem);
+    }
+
+  tem = AREF (ent, FONT_SPACING_INDEX);
+  if (FIXNUMP (tem))
+    {
+      ptn->specified |= FSPEC_SPACING;
+      ptn->mono_spacing_p = XFIXNUM (tem) != FONT_SPACING_PROPORTIONAL;
+    }
+
+  tem = AREF (ent, FONT_FAMILY_INDEX);
+  if (!NILP (tem) &&
+      (list_p && !haikufont_maybe_handle_special_family (tem, ptn)))
+    {
+      ptn->specified |= FSPEC_FAMILY;
+      strncpy ((char *) &ptn->family,
+	       SSDATA (SYMBOL_NAME (tem)),
+	       sizeof ptn->family - 1);
+    }
+
+  tem = assq_no_quit (QCscript, AREF (ent, FONT_EXTRA_INDEX));
+  if (!NILP (tem))
+    {
+      tem = assq_no_quit (XCDR (tem), Vscript_representative_chars);
+
+      if (CONSP (tem) && VECTORP (XCDR (tem)))
+	{
+	  tem = XCDR (tem);
+
+	  int count = 0;
+
+	  for (int j = 0; j < ASIZE (tem); ++j)
+	    if (TYPE_RANGED_FIXNUMP (uint32_t, AREF (tem, j)))
+	      ++count;
+
+	  if (count)
+	    {
+	      ptn->specified |= FSPEC_NEED_ONE_OF;
+	      ptn->need_one_of_len = count;
+	      ptn->need_one_of = xmalloc (count * sizeof *ptn->need_one_of);
+	      count = 0;
+	      for (int j = 0; j < ASIZE (tem); ++j)
+		if (TYPE_RANGED_FIXNUMP (uint32_t, AREF (tem, j)))
+		  {
+		    ptn->need_one_of[j] = XFIXNAT (AREF (tem, j));
+		    ++count;
+		  }
+	    }
+	}
+      else if (CONSP (tem) && CONSP (XCDR (tem)))
+	{
+	  int count = 0;
+
+	  for (Lisp_Object it = XCDR (tem); CONSP (it); it = XCDR (it))
+	    if (TYPE_RANGED_FIXNUMP (uint32_t, XCAR (it)))
+	      ++count;
+
+	  if (count)
+	    {
+	      ptn->specified |= FSPEC_WANTED;
+	      ptn->want_chars_len = count;
+	      ptn->wanted_chars = xmalloc (count * sizeof *ptn->wanted_chars);
+	      count = 0;
+
+	      for (tem = XCDR (tem); CONSP (tem); tem = XCDR (tem))
+		if (TYPE_RANGED_FIXNUMP (uint32_t, XCAR (tem)))
+		  {
+		    ptn->wanted_chars[count] = XFIXNAT (XCAR (tem));
+		    ++count;
+		  }
+	    }
+	}
+    }
+
+  tem = assq_no_quit (QClang, AREF (ent, FONT_EXTRA_INDEX));
+  if (CONSP (tem))
+    {
+      tem = XCDR (tem);
+      if (EQ (tem, Qzh))
+	{
+	  ptn->specified |= FSPEC_LANGUAGE;
+	  ptn->language = LANGUAGE_CN;
+	}
+      else if (EQ (tem, Qko))
+	{
+	  ptn->specified |= FSPEC_LANGUAGE;
+	  ptn->language = LANGUAGE_KO;
+	}
+      else if (EQ (tem, Qjp))
+	{
+	  ptn->specified |= FSPEC_LANGUAGE;
+	  ptn->language = LANGUAGE_JP;
+	}
+    }
+}
+
+static void
+haikufont_done_with_query_pattern (struct haiku_font_pattern *ptn)
+{
+  if (ptn->specified & FSPEC_WANTED)
+    xfree (ptn->wanted_chars);
+
+  if (ptn->specified & FSPEC_NEED_ONE_OF)
+    xfree (ptn->need_one_of);
+}
+
+static Lisp_Object
+haikufont_match (struct frame *f, Lisp_Object font_spec)
+{
+  block_input ();
+  Lisp_Object tem = Qnil;
+  struct haiku_font_pattern ptn;
+  haikufont_spec_or_entity_to_pattern (font_spec, 0, &ptn);
+  ptn.specified &= ~FSPEC_FAMILY;
+  struct haiku_font_pattern *found = BFont_find (&ptn);
+  haikufont_done_with_query_pattern (&ptn);
+  if (found)
+    {
+      tem = haikufont_pattern_to_entity (found);
+      haiku_font_pattern_free (found);
+    }
+  unblock_input ();
+  return !NILP (tem) ? tem : haikufont_get_fallback_entity ();
+}
+
+static Lisp_Object
+haikufont_list (struct frame *f, Lisp_Object font_spec)
+{
+  block_input ();
+  Lisp_Object lst = Qnil;
+  struct haiku_font_pattern ptn;
+  haikufont_spec_or_entity_to_pattern (font_spec, 1, &ptn);
+  struct haiku_font_pattern *found = BFont_find (&ptn);
+  haikufont_done_with_query_pattern (&ptn);
+  if (found)
+    {
+      for (struct haiku_font_pattern *pt = found;
+	   pt; pt = pt->next)
+	lst = Fcons (haikufont_pattern_to_entity (pt), lst);
+      haiku_font_pattern_free (found);
+    }
+  unblock_input ();
+  return lst;
+}
+
+static unsigned int
+haikufont_encode_char (struct font *font, int c)
+{
+  struct haikufont_info *info = (struct haikufont_info *) font;
+
+  if (c < HAVE_CHAR_CACHE_MAX && c < info->charc_size && info->have_char_cache[c])
+    return info->have_char_cache[c] < 0 ? FONT_INVALID_CODE : c;
+
+  if (c < HAVE_CHAR_CACHE_MAX)
+    {
+      int min_size = c + 1;
+      if (info->charc_size < min_size)
+	{
+	  ptrdiff_t current = info->charc_size;
+	  info->charc_size = min_size + min_size % 256;
+	  info->met_cache = xrealloc (info->met_cache, info->charc_size *
+				      sizeof *info->met_cache);
+	  info->have_char_cache = xrealloc (info->have_char_cache, info->charc_size *
+					    sizeof *info->have_char_cache);
+	  memset (&info->have_char_cache[current], 0,
+		  sizeof *info->have_char_cache * (info->charc_size - current));
+	  memset (&info->met_cache[current], 0,
+		  sizeof *info->met_cache * (info->charc_size - current));
+	}
+      info->have_char_cache[c] = -1;
+    }
+  if (!BFont_have_char_p (((struct haikufont_info *) font)->be_font, c))
+    {
+      if (c < HAVE_CHAR_CACHE_MAX)
+	{
+	  info->have_char_cache[c] = -1;
+	}
+      return FONT_INVALID_CODE;
+    }
+  if (c < HAVE_CHAR_CACHE_MAX)
+    info->have_char_cache[c] = 1;
+
+  return c;
+}
+
+static int
+haikufont_have_char (Lisp_Object font, int c)
+{
+  if (FONT_ENTITY_P (font))
+    return -1;
+
+  return haikufont_encode_char (XFONT_OBJECT (font), c)
+    != FONT_INVALID_CODE;
+}
+
+static Lisp_Object
+haikufont_open (struct frame *f, Lisp_Object font_entity, int x)
+{
+  struct haikufont_info *font_info;
+  struct haiku_font_pattern ptn;
+  struct font *font;
+  void *be_font;
+  Lisp_Object font_object;
+  Lisp_Object tem;
+
+  block_input ();
+  if (x <= 0)
+    {
+      /* Get pixel size from frame instead */
+      tem = get_frame_param (f, Qfontsize);
+      x = NILP (tem) ? 0 : XFIXNAT (tem);
+    }
+
+  haikufont_spec_or_entity_to_pattern (font_entity, 1, &ptn);
+
+  if (BFont_open_pattern (&ptn, &be_font, x))
+    {
+      haikufont_done_with_query_pattern (&ptn);
+      unblock_input ();
+      return Qnil;
+    }
+
+  haikufont_done_with_query_pattern (&ptn);
+
+  font_object = font_make_object (VECSIZE (struct haikufont_info),
+				  font_entity, x);
+
+  ASET (font_object, FONT_TYPE_INDEX, Qhaiku);
+  font_info = (struct haikufont_info *) XFONT_OBJECT (font_object);
+  font = (struct font *) font_info;
+
+  font_info->charc_size = 256;
+  font_info->met_cache = xmalloc (font_info->charc_size *
+				  sizeof *font_info->met_cache);
+  font_info->have_char_cache = xmalloc (font_info->charc_size *
+					sizeof *font_info->met_cache);
+
+  memset (font_info->met_cache, 0, font_info->charc_size *
+	  sizeof *font_info->met_cache);
+
+  memset (font_info->have_char_cache, 0, font_info->charc_size *
+	  sizeof *font_info->met_cache);
+
+  if (!font)
+    {
+      unblock_input ();
+      return Qnil;
+    }
+
+  font_info->be_font = be_font;
+
+  font->pixel_size = 0;
+  font->driver = &haikufont_driver;
+  font->encoding_charset = -1;
+  font->repertory_charset = -1;
+  font->default_ascent = 0;
+  font->vertical_centering = 0;
+  font->baseline_offset = 0;
+  font->relative_compose = 0;
+
+  font->props[FONT_FULLNAME_INDEX] =
+    build_unibyte_string ("fixed");
+
+  int px_size, min_width, max_width,
+    avg_width, height, space_width, ascent,
+    descent, underline_pos, underline_thickness;
+
+  BFont_dat (be_font, &px_size, &min_width,
+	     &max_width, &avg_width, &height,
+	     &space_width, &ascent, &descent,
+	     &underline_pos, &underline_thickness);
+
+  font->pixel_size = px_size;
+  font->min_width = min_width;
+  font->max_width = max_width;
+  font->average_width = avg_width;
+  font->height = height;
+  font->space_width = space_width;
+  font->ascent = ascent;
+  font->descent = descent;
+  font->default_ascent = ascent;
+  font->underline_position = underline_pos;
+  font->underline_thickness = underline_thickness;
+
+  font->vertical_centering = 0;
+  font->baseline_offset = 0;
+  font->relative_compose = 0;
+
+  font->props[FONT_NAME_INDEX] = Ffont_xlfd_name (font_object, Qnil);
+
+  unblock_input ();
+  return font_object;
+}
+
+static void
+haikufont_close (struct font *font)
+{
+  if (font_data_structures_may_be_ill_formed ())
+    return;
+  struct haikufont_info *info = (struct haikufont_info *) font;
+
+  block_input ();
+  if (info && info->be_font)
+    BFont_close (info->be_font);
+  unblock_input ();
+}
+
+static void
+haikufont_prepare_face (struct frame *f, struct face *face)
+{
+
+}
+
+static void
+haikufont_glyph_extents (struct font *font, unsigned code,
+			 struct font_metrics *metrics)
+{
+  struct haikufont_info *info = (struct haikufont_info *) font;
+
+  if (code < HAVE_CHAR_CACHE_MAX &&
+      info->met_cache[code].ascent != 0)
+    {
+      int min_size = code + 1;
+      if (info->charc_size < min_size)
+	{
+	  info->charc_size = min_size + min_size % 256;
+	  ptrdiff_t current = info->charc_size;
+
+	  info->charc_size = min_size + min_size % 256;
+	  info->met_cache = xrealloc (info->met_cache, info->charc_size *
+				      sizeof *info->met_cache);
+	  info->have_char_cache = xrealloc (info->have_char_cache, info->charc_size *
+					    sizeof *info->have_char_cache);
+	  memset (&info->have_char_cache[current], 0,
+		  sizeof *info->have_char_cache * (info->charc_size - current));
+	  memset (&info->met_cache[current], 0,
+		  sizeof *info->met_cache * (info->charc_size - current));
+	}
+
+      *metrics = info->met_cache[code];
+      return;
+    }
+
+  unsigned char utf8[MAX_MULTIBYTE_LENGTH];
+  memset (utf8, 0, MAX_MULTIBYTE_LENGTH);
+  CHAR_STRING (code, utf8);
+  int advance, lb, rb;
+  BFont_char_bounds (info->be_font, (const char *) utf8, &advance, &lb, &rb);
+
+  metrics->lbearing = lb;
+  metrics->rbearing = rb;
+  metrics->width = advance;
+  metrics->ascent = font->ascent;
+  metrics->descent = font->descent;
+
+  if (code < HAVE_CHAR_CACHE_MAX)
+    {
+      int min_size = code + 1;
+      if (info->charc_size < min_size)
+	{
+	  ptrdiff_t current = info->charc_size;
+
+	  info->charc_size = min_size + min_size % 256;
+	  info->met_cache = xrealloc (info->met_cache, info->charc_size *
+				      sizeof *info->met_cache);
+	  info->have_char_cache = xrealloc (info->have_char_cache, info->charc_size *
+					    sizeof *info->have_char_cache);
+	  memset (&info->have_char_cache[current], 0,
+		  sizeof *info->have_char_cache * (info->charc_size - current));
+	  memset (&info->met_cache[current], 0,
+		  sizeof *info->met_cache * (info->charc_size - current));
+	}
+      info->met_cache[code] = *metrics;
+    }
+}
+
+static void
+haikufont_text_extents (struct font *font, const unsigned int *code,
+			int nglyphs, struct font_metrics *metrics)
+{
+  int totalwidth = 0;
+  memset (metrics, 0, sizeof (struct font_metrics));
+
+  block_input ();
+  for (int i = 0; i < nglyphs; i++)
+    {
+      struct font_metrics m;
+      haikufont_glyph_extents (font, code[i], &m);
+      if (metrics)
+	{
+	  if (totalwidth + m.lbearing < metrics->lbearing)
+	    metrics->lbearing = totalwidth + m.lbearing;
+	  if (totalwidth + m.rbearing > metrics->rbearing)
+	    metrics->rbearing = totalwidth + m.rbearing;
+	  if (m.ascent > metrics->ascent)
+	    metrics->ascent = m.ascent;
+	  if (m.descent > metrics->descent)
+	    metrics->descent = m.descent;
+	}
+      totalwidth += m.width;
+    }
+
+  unblock_input ();
+
+  if (metrics)
+    metrics->width = totalwidth;
+}
+
+static Lisp_Object
+haikufont_shape (Lisp_Object lgstring, Lisp_Object direction)
+{
+  struct haikufont_info *font =
+    (struct haikufont_info *) CHECK_FONT_GET_OBJECT (LGSTRING_FONT (lgstring));
+  int *advance, *lb, *rb;
+  ptrdiff_t glyph_len, len, i, b_len;
+  Lisp_Object tem;
+  char *b;
+  uint32_t *mb_buf;
+
+  glyph_len = LGSTRING_GLYPH_LEN (lgstring);
+  for (i = 0; i < glyph_len; ++i)
+    {
+      tem = LGSTRING_GLYPH (lgstring, i);
+
+      if (NILP (tem))
+	break;
+    }
+
+  len = i;
+
+  if (INT_MAX / 2 < len)
+    memory_full (SIZE_MAX);
+
+  block_input ();
+
+  b_len = 0;
+  b = xmalloc (b_len);
+  mb_buf = alloca (len * sizeof *mb_buf);
+
+  for (i = b_len; i < len; ++i)
+    {
+      uint32_t c = LGLYPH_CHAR (LGSTRING_GLYPH (lgstring, i));
+      mb_buf[i] = c;
+      unsigned char mb[MAX_MULTIBYTE_LENGTH];
+      int slen = CHAR_STRING (c, mb);
+
+      b = xrealloc (b, b_len = (b_len + slen));
+      if (len == 1)
+	b[b_len - slen] = mb[0];
+      else
+	memcpy (b + b_len - slen, mb, slen);
+    }
+
+  advance = alloca (len * sizeof *advance);
+  lb = alloca (len * sizeof *lb);
+  rb = alloca (len * sizeof *rb);
+
+  eassert (font->be_font);
+  BFont_nchar_bounds (font->be_font, b, advance, lb, rb, len);
+  xfree (b);
+
+  for (i = 0; i < len; ++i)
+    {
+      tem = LGSTRING_GLYPH (lgstring, i);
+      if (NILP (tem))
+	{
+	  tem = LGLYPH_NEW ();
+	  LGSTRING_SET_GLYPH (lgstring, i, tem);
+	}
+
+      LGLYPH_SET_FROM (tem, i);
+      LGLYPH_SET_TO (tem, i);
+      LGLYPH_SET_CHAR (tem, mb_buf[i]);
+      LGLYPH_SET_CODE (tem, mb_buf[i]);
+
+      LGLYPH_SET_WIDTH (tem, advance[i]);
+      LGLYPH_SET_LBEARING (tem, lb[i]);
+      LGLYPH_SET_RBEARING (tem, rb[i]);
+      LGLYPH_SET_ASCENT (tem, font->font.ascent);
+      LGLYPH_SET_DESCENT (tem, font->font.descent);
+    }
+
+  unblock_input ();
+
+  return make_fixnum (len);
+}
+
+static int
+haikufont_draw (struct glyph_string *s, int from, int to,
+		int x, int y, bool with_background)
+{
+  struct frame *f = s->f;
+  struct face *face = s->face;
+  struct font_info *info = (struct font_info *) s->font;
+  unsigned char mb[MAX_MULTIBYTE_LENGTH];
+  void *view = FRAME_HAIKU_VIEW (f);
+
+  if (s->hl == DRAW_MOUSE_FACE)
+    face = FACE_FROM_ID_OR_NULL (s->f,
+				 MOUSE_HL_INFO (s->f)->mouse_face_face_id);
+  if (!face)
+    face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
+
+  block_input ();
+  prepare_face_for_display (s->f, face);
+
+  BView_draw_lock (view);
+  BView_StartClip (view);
+  if (with_background && !s->background_filled_p)
+    {
+      int fibw = FRAME_INTERNAL_BORDER_WIDTH (s->f);
+      int vx, vy, width, height;
+      vx = s->x;
+      vy = s->y;
+      width = s->width;
+      height = FONT_HEIGHT (face->font);
+      if (s->face->box != FACE_NO_BOX && s->first_glyph->left_box_line_p)
+	vx += max (s->face->box_vertical_line_width, 0);
+
+      int mbox_line_width = max (s->face->box_vertical_line_width, 0);
+
+      if (s->row->full_width_p)
+        {
+          if (vx <= fibw + 1 + mbox_line_width)
+            {
+              width += vx - mbox_line_width;
+              vx = mbox_line_width;
+            }
+          if (FRAME_PIXEL_WIDTH (s->f) - (vx + width)
+                <= fibw+1)
+            width += fibw;
+        }
+      if (s->face->box == FACE_NO_BOX)
+        {
+          /* Expand unboxed top row over internal border.  */
+          if (vy <= fibw + 1 + mbox_line_width)
+            {
+              height += vy;
+              vy = 0;
+            }
+        }
+      else
+        {
+          int correction = abs (s->face->box_horizontal_line_width) + 1;
+          vy += correction;
+          height -= 2 * correction;
+          correction = abs (s->face->box_vertical_line_width) + 1;
+          vx += correction;
+          width -= 2 * correction;
+        }
+      BView_SetHighColor (view, s->hl == DRAW_CURSOR ?
+			  FRAME_CURSOR_COLOR (s->f).pixel : face->background);
+
+      BView_FillRectangle (view, vx, vy, width, height);
+      s->background_filled_p = 1;
+    }
+
+  if (s->left_overhang && s->clip_head && !s->for_overlaps)
+    {
+      /* XXX: Why is this neccessary? */
+      BView_ClipToRect (view, s->clip_head->x, 0,
+			FRAME_PIXEL_WIDTH (f), FRAME_PIXEL_HEIGHT (f));
+    }
+
+  if (s->hl == DRAW_CURSOR)
+    BView_SetHighColor (view, FRAME_OUTPUT_DATA (s->f)->cursor_fg);
+  else
+    BView_SetHighColor (view, face->foreground);
+
+  BView_MovePenTo (view, x, y);
+  BView_SetFont (view, ((struct haikufont_info *) info)->be_font);
+
+  if (from == to)
+    {
+      int len = CHAR_STRING (s->char2b[from], mb);
+      BView_DrawString (view, (char *) mb, len);
+    }
+  else
+    {
+      ptrdiff_t b_len = 0;
+      char *b = xmalloc (b_len);
+
+      for (int idx = from; idx < to; ++idx)
+	{
+	  int len = CHAR_STRING (s->char2b[idx], mb);
+	  b = xrealloc (b, b_len = (b_len + len));
+	  if (len == 1)
+	    b[b_len - len] = mb[0];
+	  else
+	    memcpy (b + b_len - len, mb, len);
+	}
+
+      BView_DrawString (view, b, b_len);
+      xfree (b);
+    }
+  BView_EndClip (view);
+  BView_draw_unlock (view);
+  unblock_input ();
+  return 1;
+}
+
+struct font_driver const haikufont_driver =
+  {
+    .type = LISPSYM_INITIALLY (Qhaiku),
+    .case_sensitive = true,
+    .get_cache = haikufont_get_cache,
+    .has_char = haikufont_have_char,
+    .list = haikufont_list,
+    .match = haikufont_match,
+    .draw = haikufont_draw,
+    .open_font = haikufont_open,
+    .close_font = haikufont_close,
+    .prepare_face = haikufont_prepare_face,
+    .encode_char = haikufont_encode_char,
+    .text_extents = haikufont_text_extents,
+    .shape = haikufont_shape
+  };
+
+void
+syms_of_haikufont (void)
+{
+  DEFSYM (Qfontsize, "fontsize");
+  DEFSYM (Qfixed, "fixed");
+  DEFSYM (Qplain, "plain");
+  DEFSYM (Qultra_light, "ultra-light");
+  DEFSYM (Qthin, "thin");
+  DEFSYM (Qreverse_italic, "reverse-italic");
+  DEFSYM (Qreverse_oblique, "reverse-oblique");
+  DEFSYM (Qmonospace, "monospace");
+  DEFSYM (Qultra_condensed, "ultra-condensed");
+  DEFSYM (Qextra_condensed, "extra-condensed");
+  DEFSYM (Qcondensed, "condensed");
+  DEFSYM (Qsemi_condensed, "semi-condensed");
+  DEFSYM (Qsemi_expanded, "semi-expanded");
+  DEFSYM (Qexpanded, "expanded");
+  DEFSYM (Qextra_expanded, "extra-expanded");
+  DEFSYM (Qultra_expanded, "ultra-expanded");
+  DEFSYM (Qzh, "zh");
+  DEFSYM (Qko, "ko");
+  DEFSYM (Qjp, "jp");
+}
diff --git a/src/haikugui.h b/src/haikugui.h
new file mode 100644
index 0000000000..cfc693fb55
--- /dev/null
+++ b/src/haikugui.h
@@ -0,0 +1,106 @@
+/* Haiku window system support
+   Copyright (C) 2021 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/>.  */
+
+#ifndef _HAIKU_GUI_H_
+#define _HAIKU_GUI_H_
+
+#ifdef _cplusplus
+extern "C"
+{
+#endif
+
+typedef struct haiku_char_struct
+{
+  int rbearing;
+  int lbearing;
+  int width;
+  int ascent;
+  int descent;
+} XCharStruct;
+
+struct haiku_rect
+{
+  int x, y;
+  int width, height;
+};
+
+typedef void *haiku;
+
+typedef haiku Emacs_Pixmap;
+typedef haiku Emacs_Window;
+typedef haiku Emacs_Cursor;
+typedef haiku Drawable;
+
+#define NativeRectangle struct haiku_rect
+#define CONVERT_TO_EMACS_RECT(xr, nr)	\
+  ((xr).x     = (nr).x,			\
+   (xr).y     = (nr).y,			\
+   (xr).width = (nr).width,		\
+   (xr).height = (nr).height)
+
+#define CONVERT_FROM_EMACS_RECT(xr, nr)	\
+  ((nr).x    = (xr).x,			\
+   (nr).y    = (xr).y,			\
+   (nr).width  = (xr).width,		\
+   (nr).height = (xr).height)
+
+#define STORE_NATIVE_RECT(nr, px, py, pwidth, pheight)	\
+  ((nr).x    = (px),					\
+   (nr).y    = (py),					\
+   (nr).width  = (pwidth),				\
+   (nr).height = (pheight))
+
+#define ForgetGravity		0
+#define NorthWestGravity	1
+#define NorthGravity		2
+#define NorthEastGravity	3
+#define WestGravity		4
+#define CenterGravity		5
+#define EastGravity		6
+#define SouthWestGravity	7
+#define SouthGravity		8
+#define SouthEastGravity	9
+#define StaticGravity		10
+
+#define NoValue		0x0000
+#define XValue  	0x0001
+#define YValue		0x0002
+#define WidthValue  	0x0004
+#define HeightValue  	0x0008
+#define AllValues 	0x000F
+#define XNegative 	0x0010
+#define YNegative 	0x0020
+
+#define USPosition	(1L << 0) /* user specified x, y */
+#define USSize		(1L << 1) /* user specified width, height */
+#define PPosition	(1L << 2) /* program specified position */
+#define PSize		(1L << 3) /* program specified size */
+#define PMinSize	(1L << 4) /* program specified minimum size */
+#define PMaxSize	(1L << 5) /* program specified maximum size */
+#define PResizeInc	(1L << 6) /* program specified resize increments */
+#define PAspect		(1L << 7) /* program specified min, max aspect ratios */
+#define PBaseSize	(1L << 8) /* program specified base for incrementing */
+#define PWinGravity	(1L << 9) /* program specified window gravity */
+
+typedef haiku Window;
+typedef int Display;
+
+#ifdef _cplusplus
+};
+#endif
+#endif /* _HAIKU_GUI_H_ */
diff --git a/src/haikuimage.c b/src/haikuimage.c
new file mode 100644
index 0000000000..a863dc4994
--- /dev/null
+++ b/src/haikuimage.c
@@ -0,0 +1,107 @@
+/* Haiku window system support.  Hey, Emacs, this is -*- C++ -*-
+   Copyright (C) 2021 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 "lisp.h"
+#include "dispextern.h"
+#include "haikuterm.h"
+#include "coding.h"
+
+#include "haiku_support.h"
+
+bool
+haiku_can_use_native_image_api (Lisp_Object type)
+{
+  const char *mime_type = NULL;
+
+  if (EQ (type, Qnative_image))
+    return 1;
+
+#ifdef HAVE_RSVG
+  if (EQ (type, Qsvg))
+    return 0;
+#endif
+
+  if (EQ (type, Qjpeg))
+    mime_type = "image/jpeg";
+  else if (EQ (type, Qpng))
+    mime_type = "image/png";
+  else if (EQ (type, Qgif))
+    mime_type = "image/gif";
+  else if (EQ (type, Qtiff))
+    mime_type = "image/tiff";
+  else if (EQ (type, Qbmp))
+    mime_type = "image/bmp";
+  else if (EQ (type, Qsvg))
+    mime_type = "image/svg";
+
+  if (!mime_type)
+    return 0;
+
+  return be_can_translate_type_to_bitmap_p (mime_type);
+}
+
+extern int
+haiku_load_image (struct frame *f, struct image *img,
+		  Lisp_Object spec_file, Lisp_Object spec_data)
+{
+  eassert (valid_image_p (img->spec));
+
+  void *pixmap = NULL;
+
+  if (STRINGP (spec_file))
+    {
+      pixmap = be_translate_bitmap_from_file_name
+	(SSDATA (ENCODE_UTF_8 (spec_file)));
+    }
+  else if (STRINGP (spec_data))
+    {
+      pixmap = be_translate_bitmap_from_memory
+	(SSDATA (spec_data), SBYTES (spec_data));
+    }
+
+  void *conv = NULL;
+
+  if (!pixmap || !BBitmap_convert (pixmap, &conv))
+    {
+      add_to_log ("Unable to load image %s", img->spec);
+      return 0;
+    }
+
+  if (conv)
+    {
+      BBitmap_free (pixmap);
+      pixmap = conv;
+    }
+
+  int left, top, right, bottom, stride, mono_p;
+  BBitmap_dimensions (pixmap, &left, &top, &right, &bottom, &stride, &mono_p);
+
+  img->width = (1 + right - left);
+  img->height = (1 + bottom - top);
+  img->pixmap = pixmap;
+
+  return 1;
+}
+
+void
+syms_of_haikuimage (void)
+{
+  DEFSYM (Qbmp, "bmp");
+}
diff --git a/src/haikumenu.c b/src/haikumenu.c
new file mode 100644
index 0000000000..9e7ad701e3
--- /dev/null
+++ b/src/haikumenu.c
@@ -0,0 +1,639 @@
+/* Haiku window system support
+   Copyright (C) 2021 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 "lisp.h"
+#include "frame.h"
+#include "keyboard.h"
+#include "menu.h"
+#include "buffer.h"
+#include "blockinput.h"
+
+#include "haikuterm.h"
+#include "haiku_support.h"
+
+static Lisp_Object *volatile menu_item_selection;
+
+int popup_activated_p = 0;
+
+struct submenu_stack_cell
+{
+  void *parent_menu;
+  void *pane;
+};
+
+static void
+digest_menu_items (void *first_menu, int start, int menu_items_used,
+		   int mbar_p)
+{
+  void **menus, **panes;
+  ssize_t menu_len = (menu_items_used + 1 - start) * sizeof *menus;
+  ssize_t pane_len = (menu_items_used + 1 - start) * sizeof *panes;
+
+  menus = alloca (menu_len);
+  panes = alloca (pane_len);
+
+  int i = start, menu_depth = 0;
+
+  memset (menus, 0, menu_len);
+  memset (panes, 0, pane_len);
+
+  void *menu = first_menu;
+
+  menus[0] = first_menu;
+
+  void *window = NULL;
+  if (FRAMEP (Vmenu_updating_frame) &&
+      FRAME_LIVE_P (XFRAME (Vmenu_updating_frame)) &&
+      FRAME_HAIKU_P (XFRAME (Vmenu_updating_frame)))
+    window = FRAME_HAIKU_WINDOW (XFRAME (Vmenu_updating_frame));
+
+  while (i < menu_items_used)
+    {
+      if (NILP (AREF (menu_items, i)))
+	{
+	  menus[++menu_depth] = menu;
+	  i++;
+	}
+      else if (EQ (AREF (menu_items, i), Qlambda))
+	{
+	  panes[menu_depth] = NULL;
+	  menu = panes[--menu_depth] ? panes[menu_depth] : menus[menu_depth];
+	  i++;
+	}
+      else if (EQ (AREF (menu_items, i), Qquote))
+	i += 1;
+      else if (EQ (AREF (menu_items, i), Qt))
+	{
+	  Lisp_Object pane_name, prefix;
+	  const char *pane_string;
+
+	  if (menu_items_n_panes == 1)
+	    {
+	      i += MENU_ITEMS_PANE_LENGTH;
+	      continue;
+	    }
+
+	  pane_name = AREF (menu_items, i + MENU_ITEMS_PANE_NAME);
+	  prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
+
+	  if (STRINGP (pane_name) && STRING_MULTIBYTE (pane_name))
+	    {
+	      pane_name = ENCODE_UTF_8 (pane_name);
+	      ASET (menu_items, i + MENU_ITEMS_PANE_NAME, pane_name);
+	    }
+
+	  pane_string = (NILP (pane_name)
+			 ? "" : SSDATA (pane_name));
+	  if (!NILP (prefix))
+	    pane_string++;
+
+	  if (strcmp (pane_string, ""))
+	    {
+	      panes[menu_depth] =
+		menu = BMenu_new_submenu (menus[menu_depth], pane_string, 1);
+	    }
+
+	  i += MENU_ITEMS_PANE_LENGTH;
+	}
+      else
+	{
+	  Lisp_Object item_name, enable, descrip, def, selected;
+	  item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
+	  enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
+	  descrip = AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY);
+	  def = AREF (menu_items, i + MENU_ITEMS_ITEM_DEFINITION);
+	  selected = AREF (menu_items, i + MENU_ITEMS_ITEM_SELECTED);
+
+	  if (STRINGP (item_name) && STRING_MULTIBYTE (item_name))
+	    {
+	      item_name = ENCODE_UTF_8 (item_name);
+	      ASET (menu_items, i + MENU_ITEMS_ITEM_NAME, item_name);
+	    }
+
+	  if (STRINGP (descrip) && STRING_MULTIBYTE (descrip))
+	    {
+	      descrip = ENCODE_UTF_8 (descrip);
+	      ASET (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY, descrip);
+	    }
+
+	  if (i + MENU_ITEMS_ITEM_LENGTH < menu_items_used &&
+	      NILP (AREF (menu_items, i + MENU_ITEMS_ITEM_LENGTH)))
+	    menu = BMenu_new_submenu (menu, SSDATA (item_name), !NILP (enable));
+	  else if (NILP (def) && menu_separator_name_p (SSDATA (item_name)))
+	    BMenu_add_separator (menu);
+	  else if (!mbar_p)
+	    BMenu_add_item (menu, SSDATA (item_name),
+			    !NILP (def) ? aref_addr (menu_items, i) : NULL,
+			    !NILP (enable), !NILP (selected), 0, window,
+			    !NILP (descrip) ? SSDATA (descrip) : NULL);
+	  else
+	    BMenu_add_item (menu, SSDATA (item_name),
+			    !NILP (def) ? (void *) (intptr_t) i : NULL,
+			    !NILP (enable), !NILP (selected), 1, window,
+			    !NILP (descrip) ? SSDATA (descrip) : NULL);
+
+	  i += MENU_ITEMS_ITEM_LENGTH;
+	}
+    }
+}
+
+static Lisp_Object
+haiku_dialog_show (struct frame *f, Lisp_Object title,
+		   Lisp_Object header, const char **error_name)
+{
+  int i, nb_buttons = 0;
+
+  *error_name = NULL;
+
+  if (menu_items_n_panes > 1)
+    {
+      *error_name = "Multiple panes in dialog box";
+      return Qnil;
+    }
+
+  Lisp_Object pane_name = AREF (menu_items, MENU_ITEMS_PANE_NAME);
+  i = MENU_ITEMS_PANE_LENGTH;
+
+  if (STRING_MULTIBYTE (pane_name))
+    pane_name = ENCODE_UTF_8 (pane_name);
+
+  block_input ();
+  void *alert = BAlert_new (SSDATA (pane_name), NILP (header) ? HAIKU_INFO_ALERT :
+			    HAIKU_IDEA_ALERT);
+
+  Lisp_Object vals[10];
+
+  while (i < menu_items_used)
+    {
+      Lisp_Object item_name, enable, descrip, value;
+      item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
+      enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
+      descrip = AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY);
+      value = AREF (menu_items, i + MENU_ITEMS_ITEM_VALUE);
+
+      if (NILP (item_name))
+	{
+	  BAlert_delete (alert);
+	  *error_name = "Submenu in dialog items";
+	  unblock_input ();
+	  return Qnil;
+	}
+
+      if (EQ (item_name, Qquote))
+	{
+	  i++;
+	}
+
+      if (nb_buttons >= 9)
+	{
+	  BAlert_delete (alert);
+	  *error_name = "Too many dialog items";
+	  unblock_input ();
+	  return Qnil;
+	}
+
+      if (STRING_MULTIBYTE (item_name))
+	item_name = ENCODE_UTF_8 (item_name);
+      if (!NILP (descrip) && STRING_MULTIBYTE (descrip))
+	descrip = ENCODE_UTF_8 (descrip);
+
+      void *button = BAlert_add_button (alert, SSDATA (item_name));
+
+      BButton_set_enabled (button, !NILP (enable));
+      if (!NILP (descrip))
+	BView_set_tooltip (button, SSDATA (descrip));
+
+      vals[nb_buttons] = value;
+      ++nb_buttons;
+      i += MENU_ITEMS_ITEM_LENGTH;
+    }
+
+  int32_t val = BAlert_go (alert);
+  unblock_input ();
+
+  if (val < 0)
+    quit ();
+  else
+    return vals[val];
+
+  return Qnil;
+}
+
+Lisp_Object
+haiku_popup_dialog (struct frame *f, Lisp_Object header, Lisp_Object contents)
+{
+  Lisp_Object title;
+  const char *error_name = NULL;
+  Lisp_Object selection;
+  ptrdiff_t specpdl_count = SPECPDL_INDEX ();
+
+  check_window_system (f);
+
+  /* Decode the dialog items from what was specified.  */
+  title = Fcar (contents);
+  CHECK_STRING (title);
+  record_unwind_protect_void (unuse_menu_items);
+
+  if (NILP (Fcar (Fcdr (contents))))
+    /* No buttons specified, add an "Ok" button so users can pop down
+       the dialog.  Also, the lesstif/motif version crashes if there are
+       no buttons.  */
+    contents = list2 (title, Fcons (build_string ("Ok"), Qt));
+
+  list_of_panes (list1 (contents));
+
+  /* Display them in a dialog box.  */
+  block_input ();
+  selection = haiku_dialog_show (f, title, header, &error_name);
+  unblock_input ();
+
+  unbind_to (specpdl_count, Qnil);
+  discard_menu_items ();
+
+  if (error_name)
+    error ("%s", error_name);
+  return selection;
+}
+
+Lisp_Object
+haiku_menu_show (struct frame *f, int x, int y, int menuflags,
+		 Lisp_Object title, const char **error_name)
+{
+  int i = 0, submenu_depth = 0;
+  void *view = FRAME_HAIKU_VIEW (f);
+  void *menu;
+
+  Lisp_Object *subprefix_stack =
+    alloca (menu_items_used * sizeof (Lisp_Object));
+
+  eassert (FRAME_HAIKU_P (f));
+
+  *error_name = NULL;
+
+  if (menu_items_used <= MENU_ITEMS_PANE_LENGTH)
+    {
+      *error_name = "Empty menu";
+      return Qnil;
+    }
+
+  block_input ();
+  if (STRINGP (title) && STRING_MULTIBYTE (title))
+    title = ENCODE_UTF_8 (title);
+
+  menu = BPopUpMenu_new (STRINGP (title) ? SSDATA (title) : NULL);
+  digest_menu_items (menu, 0, menu_items_used, 0);
+  BView_convert_to_screen (view, &x, &y);
+  unblock_input ();
+
+  menu_item_selection = BMenu_run (menu, x, y);
+
+  FRAME_DISPLAY_INFO (f)->grabbed = 0;
+
+  if (menu_item_selection)
+    {
+      Lisp_Object prefix, entry;
+
+      prefix = entry = Qnil;
+      i = 0;
+      while (i < menu_items_used)
+	{
+	  if (NILP (AREF (menu_items, i)))
+	    {
+	      subprefix_stack[submenu_depth++] = prefix;
+	      prefix = entry;
+	      i++;
+	    }
+	  else if (EQ (AREF (menu_items, i), Qlambda))
+	    {
+	      prefix = subprefix_stack[--submenu_depth];
+	      i++;
+	    }
+	  else if (EQ (AREF (menu_items, i), Qt))
+	    {
+	      prefix
+		= AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
+	      i += MENU_ITEMS_PANE_LENGTH;
+	    }
+	  /* Ignore a nil in the item list.
+	     It's meaningful only for dialog boxes.  */
+	  else if (EQ (AREF (menu_items, i), Qquote))
+	    i += 1;
+	  else
+	    {
+	      entry
+		= AREF (menu_items, i + MENU_ITEMS_ITEM_VALUE);
+	      if (menu_item_selection == aref_addr (menu_items, i))
+		{
+		  if (menuflags & MENU_KEYMAPS)
+		    {
+		      int j;
+
+		      entry = list1 (entry);
+		      if (!NILP (prefix))
+			entry = Fcons (prefix, entry);
+		      for (j = submenu_depth - 1; j >= 0; j--)
+			if (!NILP (subprefix_stack[j]))
+			  entry = Fcons (subprefix_stack[j], entry);
+		    }
+		  BPopUpMenu_delete (menu);
+		  return entry;
+		}
+	      i += MENU_ITEMS_ITEM_LENGTH;
+	    }
+	}
+    }
+  else if (!(menuflags & MENU_FOR_CLICK))
+    {
+      BPopUpMenu_delete (menu);
+      quit ();
+    }
+  BPopUpMenu_delete (menu);
+  return Qnil;
+}
+
+void
+free_frame_menubar (struct frame *f)
+{
+  FRAME_MENU_BAR_LINES (f) = 0;
+  FRAME_MENU_BAR_HEIGHT (f) = 0;
+  FRAME_EXTERNAL_MENU_BAR (f) = 0;
+
+  block_input ();
+  void *mbar = FRAME_HAIKU_MENU_BAR (f);
+  if (mbar)
+    BMenuBar_delete (mbar);
+  if (FRAME_OUTPUT_DATA (f)->menu_bar_open_p)
+    --popup_activated_p;
+  FRAME_OUTPUT_DATA (f)->menu_bar_open_p = 0;
+  unblock_input ();
+
+  adjust_frame_size (f, -1, -1, 2, false, Qmenu_bar_lines);
+}
+
+void
+initialize_frame_menubar (struct frame *f)
+{
+  /* This function is called before the first chance to redisplay
+     the frame.  It has to be, so the frame will have the right size.  */
+  fset_menu_bar_items (f, menu_bar_items (FRAME_MENU_BAR_ITEMS (f)));
+  set_frame_menubar (f, true);
+}
+
+void
+set_frame_menubar (struct frame *f, bool deep_p)
+{
+  void *mbar = FRAME_HAIKU_MENU_BAR (f);
+  void *view = FRAME_HAIKU_VIEW (f);
+
+  int first_time_p = 0;
+
+  if (!mbar)
+    {
+      mbar = FRAME_HAIKU_MENU_BAR (f) = BMenuBar_new (view);
+      first_time_p = 1;
+    }
+
+  Lisp_Object items;
+  struct buffer *prev = current_buffer;
+  Lisp_Object buffer;
+  ptrdiff_t specpdl_count = SPECPDL_INDEX ();
+  int previous_menu_items_used = f->menu_bar_items_used;
+  Lisp_Object *previous_items
+    = alloca (previous_menu_items_used * sizeof *previous_items);
+
+  XSETFRAME (Vmenu_updating_frame, f);
+
+  if (!deep_p)
+    {
+      FRAME_OUTPUT_DATA (f)->menu_up_to_date_p = 0;
+      items = FRAME_MENU_BAR_ITEMS (f);
+      Lisp_Object string;
+
+      block_input ();
+      int count = BMenu_count_items (mbar);
+
+      int i;
+      for (i = 0; i < ASIZE (items); i += 4)
+	{
+	  string = AREF (items, i + 1);
+
+	  if (!STRINGP (string))
+	    break;
+
+	  if (STRING_MULTIBYTE (string))
+	    string = ENCODE_UTF_8 (string);
+
+	  if (i / 4 < count)
+	    {
+	      void *it = BMenu_item_at (mbar, i / 4);
+	      BMenu_item_set_label (it, SSDATA (string));
+	    }
+	  else
+	    BMenu_new_menu_bar_submenu (mbar, SSDATA (string));
+	}
+
+      if (i / 4 < count)
+	BMenu_delete_from (mbar, i / 4, count - i / 4 + 1);
+      unblock_input ();
+
+      f->menu_bar_items_used = 0;
+    }
+  else
+    {
+      /* If we are making a new widget, its contents are empty,
+	 do always reinitialize them.  */
+      if (first_time_p)
+	previous_menu_items_used = 0;
+      buffer = XWINDOW (FRAME_SELECTED_WINDOW (f))->contents;
+      specbind (Qinhibit_quit, Qt);
+      /* Don't let the debugger step into this code
+	 because it is not reentrant.  */
+      specbind (Qdebug_on_next_call, Qnil);
+
+      record_unwind_save_match_data ();
+      if (NILP (Voverriding_local_map_menu_flag))
+	{
+	  specbind (Qoverriding_terminal_local_map, Qnil);
+	  specbind (Qoverriding_local_map, Qnil);
+	}
+
+      set_buffer_internal_1 (XBUFFER (buffer));
+
+      /* Run the Lucid hook.  */
+      safe_run_hooks (Qactivate_menubar_hook);
+
+      /* If it has changed current-menubar from previous value,
+	 really recompute the menubar from the value.  */
+      if (! NILP (Vlucid_menu_bar_dirty_flag))
+	call0 (Qrecompute_lucid_menubar);
+      safe_run_hooks (Qmenu_bar_update_hook);
+      fset_menu_bar_items (f, menu_bar_items (FRAME_MENU_BAR_ITEMS (f)));
+
+      items = FRAME_MENU_BAR_ITEMS (f);
+
+      /* Save the frame's previous menu bar contents data.  */
+      if (previous_menu_items_used)
+	memcpy (previous_items, xvector_contents (f->menu_bar_vector),
+		previous_menu_items_used * word_size);
+
+      /* Fill in menu_items with the current menu bar contents.
+	 This can evaluate Lisp code.  */
+      save_menu_items ();
+      menu_items = f->menu_bar_vector;
+      menu_items_allocated = VECTORP (menu_items) ? ASIZE (menu_items) : 0;
+      init_menu_items ();
+      int i;
+      int count = BMenu_count_items (mbar);
+      int subitems = ASIZE (items) / 4;
+
+      int *submenu_start, *submenu_end,	*submenu_n_panes;
+      Lisp_Object *submenu_names;
+
+      submenu_start = alloca ((subitems + 1) * sizeof *submenu_start);
+      submenu_end = alloca (subitems * sizeof *submenu_end);
+      submenu_n_panes = alloca (subitems * sizeof *submenu_n_panes);
+      submenu_names = alloca (subitems * sizeof (Lisp_Object));
+
+      for (i = 0; i < subitems; ++i)
+	{
+	  Lisp_Object key, string, maps;
+
+	  key = AREF (items, i * 4);
+	  string = AREF (items, i * 4 + 1);
+	  maps = AREF (items, i * 4 + 2);
+
+	  if (NILP (string))
+	    break;
+
+	  if (STRINGP (string) && STRING_MULTIBYTE (string))
+	    string = ENCODE_UTF_8 (string);
+
+	  submenu_start[i] = menu_items_used;
+	  menu_items_n_panes = 0;
+	  parse_single_submenu (key, string, maps);
+	  submenu_n_panes[i] = menu_items_n_panes;
+	  submenu_end[i] = menu_items_used;
+	  submenu_names[i] = string;
+	}
+      finish_menu_items ();
+      submenu_start[i] = -1;
+
+      block_input ();
+      for (i = 0; submenu_start[i] >= 0; ++i)
+	{
+	  void *mn = NULL;
+	  if (i < count)
+	    mn = BMenu_item_get_menu (BMenu_item_at (mbar, i));
+	  if (mn)
+	    BMenu_delete_all (mn);
+	  else
+	    mn = BMenu_new_menu_bar_submenu (mbar, SSDATA (submenu_names[i]));
+
+	  menu_items_n_panes = submenu_n_panes[i];
+	  digest_menu_items (mn, submenu_start[i], submenu_end[i], 1);
+	}
+      unblock_input ();
+
+      set_buffer_internal_1 (prev);
+
+      FRAME_OUTPUT_DATA (f)->menu_up_to_date_p = 1;
+      fset_menu_bar_vector (f, menu_items);
+      f->menu_bar_items_used = menu_items_used;
+    }
+  unbind_to (specpdl_count, Qnil);
+}
+
+void
+run_menu_bar_help_event (struct frame *f, int mb_idx)
+{
+  Lisp_Object frame;
+  Lisp_Object vec;
+  Lisp_Object help;
+
+  if (!FRAME_OUTPUT_DATA (f)->menu_up_to_date_p)
+    return;
+
+  block_input ();
+  XSETFRAME (frame, f);
+
+  if (mb_idx < 0)
+    {
+      kbd_buffer_store_help_event (frame, Qnil);
+      unblock_input ();
+      return;
+    }
+
+  vec = f->menu_bar_vector;
+  if (mb_idx >= ASIZE (vec))
+    emacs_abort ();
+
+  help = AREF (vec, mb_idx + MENU_ITEMS_ITEM_HELP);
+  if (STRINGP (help) || NILP (help))
+    kbd_buffer_store_help_event (frame, help);
+  unblock_input ();
+}
+
+DEFUN ("menu-or-popup-active-p", Fmenu_or_popup_active_p, Smenu_or_popup_active_p,
+       0, 0, 0, doc: /* SKIP: real doc in xmenu.c. */)
+  (void)
+{
+  return popup_activated_p ? Qt : Qnil;
+}
+
+DEFUN ("haiku-menu-bar-open", Fhaiku_menu_bar_open, Shaiku_menu_bar_open, 0, 1, "i",
+       doc: /* Show the menu bar in FRAME.
+
+Move the mouse pointer onto the first element of FRAME's menu bar, and
+cause it to be opened.  If FRAME is nil or not given, use the selected
+frame.  If FRAME has no menu bar, a pop-up is displayed at the position
+of the last non-menu event instead. */)
+  (Lisp_Object frame)
+{
+  struct frame *f = decode_window_system_frame (frame);
+
+  if (FRAME_EXTERNAL_MENU_BAR (f))
+    {
+      if (!FRAME_OUTPUT_DATA (f)->menu_up_to_date_p)
+	set_frame_menubar (f, 1);
+    }
+  else
+    {
+      return call2 (Qpopup_menu, call0 (Qmouse_menu_bar_map),
+		    last_nonmenu_event);
+    }
+
+  block_input ();
+  BMenuBar_start_tracking (FRAME_HAIKU_MENU_BAR (f));
+  unblock_input ();
+
+  return Qnil;
+}
+
+void
+syms_of_haikumenu (void)
+{
+  DEFSYM (Qdebug_on_next_call, "debug-on-next-call");
+  DEFSYM (Qpopup_menu, "popup-menu");
+  DEFSYM (Qmouse_menu_bar_map, "mouse-menu-bar-map");
+
+  defsubr (&Smenu_or_popup_active_p);
+  defsubr (&Shaiku_menu_bar_open);
+  return;
+}
diff --git a/src/haikuselect.c b/src/haikuselect.c
new file mode 100644
index 0000000000..c17c23583b
--- /dev/null
+++ b/src/haikuselect.c
@@ -0,0 +1,134 @@
+/* Haiku window system selection support. Hey Emacs, this is -*- C++ -*-
+   Copyright (C) 2021 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 "lisp.h"
+#include "blockinput.h"
+#include "coding.h"
+#include "haikuselect.h"
+#include "haikuterm.h"
+
+DEFUN ("haiku-selection-data", Fhaiku_selection_data, Shaiku_selection_data,
+       2, 2, 0,
+       doc: /* Retrieve content typed as NAME from the clipboard
+CLIPBOARD.  CLIPBOARD is the symbol `PRIMARY', `SECONDARY' or
+`CLIPBOARD'.  NAME is a MIME type denoting the type of the data to
+fetch. */)
+  (Lisp_Object clipboard, Lisp_Object name)
+{
+  CHECK_SYMBOL (clipboard);
+  CHECK_STRING (name);
+  char *dat;
+  ssize_t len;
+
+  block_input ();
+  if (EQ (clipboard, QPRIMARY))
+    dat = BClipboard_find_primary_selection_data (SSDATA (name), &len);
+  else if (EQ (clipboard, QSECONDARY))
+    dat = BClipboard_find_secondary_selection_data (SSDATA (name), &len);
+  else if (EQ (clipboard, QCLIPBOARD))
+    dat = BClipboard_find_system_data (SSDATA (name), &len);
+  else
+    {
+      unblock_input ();
+      signal_error ("Bad clipboard", clipboard);
+    }
+  unblock_input ();
+
+  if (!dat)
+    return Qnil;
+
+  Lisp_Object str = make_unibyte_string (dat, len);
+  Lisp_Object lispy_type = Qnil;
+
+  if (!strcmp (SSDATA (name), "text/utf-8") ||
+      !strcmp (SSDATA (name), "text/plain"))
+    {
+      if (string_ascii_p (str))
+	lispy_type = QSTRING;
+      else
+	lispy_type = QUTF8_STRING;
+    }
+
+  if (!NILP (lispy_type))
+    Fput_text_property (make_fixnum (0), make_fixnum (len),
+			Qforeign_selection, lispy_type, str);
+
+  block_input ();
+  BClipboard_free_data (dat);
+  unblock_input ();
+
+  return str;
+}
+
+DEFUN ("haiku-selection-put", Fhaiku_selection_put, Shaiku_selection_put,
+       3, 3, 0,
+       doc: /* Add or remove content from the clipboard CLIPBOARD.
+CLIPBOARD is the symbol `PRIMARY', `SECONDARY' or `CLIPBOARD'.  NAME
+is a MIME type denoting the type of the data to add.  DATA is the
+string that will be placed in the clipboard, or nil if the content is
+to be removed.  If NAME is the string `text/utf-8' or the string
+`text/plain', encode it as UTF-8 before storing it into the
+clipboard. */)
+  (Lisp_Object clipboard, Lisp_Object name, Lisp_Object data)
+{
+  CHECK_SYMBOL (clipboard);
+  CHECK_STRING (name);
+  if (!NILP (data))
+    CHECK_STRING (data);
+
+  block_input ();
+  /* It seems that Haiku applications counter-intuitively expect
+     UTF-8 data in both text/utf-8 and text/plain. */
+  if (!NILP (data) && STRING_MULTIBYTE (data) &&
+      (!strcmp (SSDATA (name), "text/utf-8") ||
+       !strcmp (SSDATA (name), "text/plain")))
+    data = ENCODE_UTF_8 (data);
+
+  char *dat = !NILP (data) ? SSDATA (data) : NULL;
+  ptrdiff_t len = !NILP (data) ? SBYTES (data) : 0;
+
+  if (EQ (clipboard, QPRIMARY))
+    BClipboard_set_primary_selection_data (SSDATA (name), dat, len);
+  else if (EQ (clipboard, QSECONDARY))
+    BClipboard_set_secondary_selection_data (SSDATA (name), dat, len);
+  else if (EQ (clipboard, QCLIPBOARD))
+    BClipboard_set_system_data (SSDATA (name), dat, len);
+  else
+    {
+      unblock_input ();
+      signal_error ("Bad clipboard", clipboard);
+    }
+  unblock_input ();
+
+  return Qnil;
+}
+
+void
+syms_of_haikuselect (void)
+{
+  DEFSYM (QSECONDARY, "SECONDARY");
+  DEFSYM (QCLIPBOARD, "CLIPBOARD");
+  DEFSYM (QSTRING, "STRING");
+  DEFSYM (QUTF8_STRING, "UTF8_STRING");
+  DEFSYM (Qforeign_selection, "foreign-selection");
+
+  defsubr (&Shaiku_selection_data);
+  defsubr (&Shaiku_selection_put);
+}
diff --git a/src/haikuselect.h b/src/haikuselect.h
new file mode 100644
index 0000000000..000c83b4dd
--- /dev/null
+++ b/src/haikuselect.h
@@ -0,0 +1,64 @@
+/* Haiku window system selection support. Hey Emacs, this is -*- C++ -*-
+   Copyright (C) 2021 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/>.  */
+
+#ifndef _HAIKU_SELECT_H_
+#define _HAIKU_SELECT_H_
+
+#ifdef __cplusplus
+#include <cstdio>
+#endif
+
+#ifdef __cplusplus
+#include <stdio.h>
+extern "C"
+{
+  extern void init_haiku_select (void);
+#endif
+
+  /* Whether or not the selection was recently changed. */
+  extern int selection_state_flag;
+
+  /* Find a string with the MIME type TYPE in the system clipboard. */
+  extern char *
+  BClipboard_find_system_data (const char *type, ssize_t *len);
+
+  /* Ditto, but for the primary selection and not clipboard. */
+  extern char *
+  BClipboard_find_primary_selection_data (const char *type, ssize_t *len);
+
+  /* Ditto, this time for the secondary selection. */
+  extern char *
+  BClipboard_find_secondary_selection_data (const char *type, ssize_t *len);
+
+  extern void
+  BClipboard_set_system_data (const char *type, const char *data, ssize_t len);
+
+  extern void
+  BClipboard_set_primary_selection_data (const char *type, const char *data,
+					 ssize_t len);
+
+  extern void
+  BClipboard_set_secondary_selection_data (const char *type, const char *data,
+					   ssize_t len);
+
+  /* Free the returned data. */
+  extern void BClipboard_free_data (void *ptr);
+#ifdef __cplusplus
+};
+#endif
+#endif /* _HAIKU_SELECT_H_ */
diff --git a/src/haikuterm.c b/src/haikuterm.c
new file mode 100644
index 0000000000..a13c7e1032
--- /dev/null
+++ b/src/haikuterm.c
@@ -0,0 +1,3365 @@
+/* Haiku window system support
+   Copyright (C) 2021 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 <math.h>
+
+struct haiku_display_info *x_display_list = NULL;
+extern frame_parm_handler haiku_frame_parm_handlers[];
+
+static void **fringe_bmps;
+static int fringe_bitmap_fillptr = 0;
+
+static Lisp_Object rdb;
+
+struct unhandled_event
+{
+  struct unhandled_event *next;
+  enum haiku_event_type type;
+  uint8_t buffer[200];
+};
+
+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);
+  eassert (p);
+
+  for (struct frame *parent = p; parent;
+       parent = FRAME_PARENT_FRAME (parent))
+    {
+      *x -= parent->left_pos;
+      *y -= parent->top_pos;
+    }
+}
+
+static void
+haiku_delete_terminal (struct terminal *terminal)
+{
+  emacs_abort ();
+}
+
+static const char *
+get_string_resource (void *ignored, const char *name, const char *class)
+{
+  if (!name)
+    return NULL;
+
+  Lisp_Object lval = assoc_no_quit (build_string (name), rdb);
+
+  if (!NILP (lval))
+    return SSDATA (XCDR (lval));
+
+  return NULL;
+}
+
+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)
+    BView_ClipToRect (FRAME_HAIKU_VIEW (s->f), r[0].x, r[0].y,
+		      r[0].width, r[0].height);
+  if (n > 1)
+    {
+      BView_ClipToRect (FRAME_HAIKU_VIEW (s->f), r[1].x, r[1].y,
+			r[1].width, r[1].height);
+    }
+
+  s->num_clips = n;
+}
+
+static void
+haiku_clip_to_string_exactly (struct glyph_string *s, struct glyph_string *dst)
+{
+  BView_ClipToRect (FRAME_HAIKU_VIEW (s->f), s->x, s->y,
+		    s->width, s->height);
+  dst->num_clips = 1;
+}
+
+static void
+haiku_flip_buffers (struct frame *f)
+{
+  void *view = FRAME_OUTPUT_DATA (f)->view;
+  block_input ();
+
+  BView_draw_lock (view);
+  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);
+  unblock_input ();
+}
+
+static void
+haiku_buffer_flipping_unblocked_hook (struct frame *f)
+{
+  if (FRAME_DIRTY_P (f))
+    haiku_flip_buffers (f);
+}
+
+/* Set the clipping region to the inverse of all scroll bars in the
+   frame F.  This ensures we never clear a scrollbar by accident. */
+static void
+haiku_inverse_clip_to_scroll_bars (struct frame *f)
+{
+  Lisp_Object tem;
+  void *view = FRAME_HAIKU_VIEW (f);
+
+  for (tem = FRAME_SCROLL_BARS (f); VECTORLIKEP (tem);
+       tem = XSCROLL_BAR (tem)->next)
+    {
+      struct scroll_bar *sb = XSCROLL_BAR (tem);
+      if (sb->scroll_bar)
+	{
+	  BView_ClipToInverseRect (view, sb->left, sb->top,
+				   sb->width, sb->height);
+	}
+    }
+}
+
+static void
+haiku_clear_frame_area (struct frame *f, int x, int y,
+			int width, int height)
+{
+  void *vw = FRAME_HAIKU_VIEW (f);
+  block_input ();
+  BView_draw_lock (vw);
+  BView_StartClip (vw);
+  haiku_inverse_clip_to_scroll_bars (f);
+  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_VIEW (f);
+  block_input ();
+  BView_draw_lock (view);
+  BView_StartClip (view);
+  haiku_inverse_clip_to_scroll_bars (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 = 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;
+
+  int ascent, descent;
+  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);
+
+  int 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))
+    {
+      adjust_frame_size (f, FRAME_COLS (f) * FRAME_COLUMN_WIDTH (f),
+			 FRAME_LINES (f) * FRAME_LINE_HEIGHT (f),
+			 3, false, Qfont);
+
+      haiku_clear_under_internal_border (f);
+    }
+  return font_object;
+}
+
+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)
+    {
+      BWindow_activate (FRAME_HAIKU_WINDOW (f));
+      flush_frame (f);
+    }
+}
+
+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)
+	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)
+{
+  return !haiku_get_color (name, color);
+}
+
+static void
+haiku_draw_plain_background (struct glyph_string *s, struct face *face,
+			     int box_line_width)
+{
+  void *view = FRAME_HAIKU_VIEW (s->f);
+  BView_StartClip (view);
+  if (s->hl == DRAW_CURSOR)
+    BView_SetHighColor (view, FRAME_CURSOR_COLOR (s->f).pixel);
+  else
+    BView_SetHighColor (view, face->background_defaulted_p ?
+			FRAME_BACKGROUND_PIXEL (s->f) :
+		      face->background);
+  BView_FillRectangle (view, s->x, s->y + box_line_width,
+		       s->background_width,
+		       s->height - 2 * box_line_width);
+  BView_EndClip (view);
+}
+
+static void
+haiku_draw_stipple_background (struct glyph_string *s, struct face *face,
+			       int box_line_width)
+{
+}
+
+static void
+haiku_maybe_draw_background (struct glyph_string *s, int force_p)
+{
+  if ((s->first_glyph->type != IMAGE_GLYPH) &&
+      (!s->background_filled_p || s->hl == DRAW_MOUSE_FACE))
+    {
+      int box_line_width = max (s->face->box_horizontal_line_width, 0);
+      if (FONT_HEIGHT (s->font) < s->height - 2 * box_line_width
+	  || FONT_TOO_HIGH (s->font)
+          || s->font_not_found_p || s->extends_to_end_of_line_p || force_p)
+	{
+	  struct face *face;
+          if (s->hl == DRAW_MOUSE_FACE)
+            {
+              face = FACE_FROM_ID_OR_NULL (s->f,
+					   MOUSE_HL_INFO (s->f)->mouse_face_face_id);
+              if (!face)
+                face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
+            }
+          else
+            face = FACE_FROM_ID (s->f, s->first_glyph->face_id);
+	  if (!face->stipple)
+	    haiku_draw_plain_background (s, face, box_line_width);
+	  else
+	    haiku_draw_stipple_background (s, face, box_line_width);
+	  s->background_filled_p = 1;
+	}
+    }
+}
+
+/* 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_VIEW (s->f);
+
+  BView_StartClip (view);
+  BView_SetHighColor (view, s->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);
+  BView_EndClip (view);
+}
+
+static void
+haiku_calculate_relief_colors (struct glyph_string *s,
+			       uint32_t *rgbout_w, uint32_t *rgbout_b)
+{
+  struct face *face = s->face;
+
+  if (s->hl == DRAW_MOUSE_FACE)
+    face = FACE_FROM_ID_OR_NULL (s->f,
+				 MOUSE_HL_INFO (s->f)->mouse_face_face_id);
+  if (!face && s->hl == DRAW_MOUSE_FACE)
+    face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
+
+  if (!face)
+    face = s->face;
+
+  prepare_face_for_display (s->f, s->face);
+
+  uint32_t rgbin = face->use_box_color_for_shadows_p ?
+    face->box_color : face->background;
+
+  if (s->hl == DRAW_CURSOR)
+    rgbin = FRAME_CURSOR_COLOR (s->f).pixel;
+
+  double h, cs, l;
+  rgb_color_hsl (rgbin, &h, &cs, &l);
+
+  hsl_color_rgb (h, cs, fmin (1.0, fmax (0.2, l) * 0.6), rgbout_b);
+  hsl_color_rgb (h, cs, fmin (1.0, fmax (0.2, l) * 1.1), rgbout_w);
+}
+
+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;
+  uint32_t color_black;
+
+  haiku_calculate_relief_colors (s, &color_white, &color_black);
+
+  void *view = FRAME_HAIKU_VIEW (s->f);
+  BView_StartClip (view);
+  BView_SetHighColor (view, raised_p ? color_white : color_black);
+
+  if (clip_rect)
+    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);
+
+  BView_EndClip (view);
+}
+
+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);
+  s->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_underwave (struct glyph_string *s, int width, int x)
+{
+  int wave_height = 3, wave_length = 2;
+  int y, dx, dy, odd, xmax;
+  dx = wave_length;
+  dy = wave_height - 1;
+  y = s->ybase - wave_height + 3;
+
+  float ax, ay, bx, by;
+  xmax = x + width;
+
+  void *view = FRAME_HAIKU_VIEW (s->f);
+
+  BView_StartClip (view);
+  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;
+
+  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_EndClip (view);
+}
+
+static void
+haiku_draw_text_decoration (struct glyph_string *s, struct face *face,
+			    uint8_t dcol, int width, int x)
+{
+  if (s->for_overlaps)
+    return;
+
+  void *view = FRAME_HAIKU_VIEW (s->f);
+  BView_draw_lock (view);
+  BView_StartClip (view);
+
+  if (s->face->underline)
+    {
+      if (s->hl == DRAW_CURSOR)
+	BView_SetHighColor (view, FRAME_OUTPUT_DATA (s->f)->cursor_fg);
+      else if (!face->underline_defaulted_p)
+	BView_SetHighColor (view, face->underline_color);
+      else
+	BView_SetHighColor (view, dcol);
+
+      if (s->face->underline == FACE_UNDER_WAVE)
+	haiku_draw_underwave (s, width, x);
+      else if (s->face->underline == FACE_UNDER_LINE)
+	{
+	  unsigned long thickness, position;
+	  int y;
+
+	  if (s->prev &&
+	      s->prev->face->underline == FACE_UNDER_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) || EQ (val, Qunbound));
+
+	      val = (WINDOW_BUFFER_LOCAL_VALUE
+		     (Qx_use_underline_position_properties, s->w));
+	      use_underline_position_properties
+		= !(NILP (val) || 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);
+	      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;
+	  y = s->ybase + position;
+
+	  BView_FillRectangle (view, s->x, y, s->width, thickness);
+	}
+    }
+
+  if (s->face->overline_p)
+    {
+      unsigned long dy = 0, h = 1;
+      if (s->hl == DRAW_CURSOR)
+	BView_SetHighColor (view, FRAME_OUTPUT_DATA (s->f)->cursor_fg);
+      else if (!face->overline_color_defaulted_p)
+	BView_SetHighColor (view, face->overline_color);
+      else
+	BView_SetHighColor (view, dcol);
+
+      BView_FillRectangle (view, s->x, s->y + dy, s->width, h);
+    }
+
+  if (s->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, FRAME_OUTPUT_DATA (s->f)->cursor_fg);
+      else if (!face->overline_color_defaulted_p)
+	BView_SetHighColor (view, face->overline_color);
+      else
+	BView_SetHighColor (view, dcol);
+
+      BView_FillRectangle (view, s->x, glyph_y + dy, s->width, h);
+    }
+
+  BView_EndClip (view);
+  BView_draw_unlock (view);
+}
+
+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 haiku_rect clip_rect;
+
+  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 (s->face->box_vertical_line_width);
+  hwidth = eabs (s->face->box_horizontal_line_width);
+  raised_p = s->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)));
+
+  get_glyph_string_clip_rect (s, &clip_rect);
+
+  if (s->face->box == FACE_SIMPLE_BOX)
+    haiku_draw_box_rect (s, left_x, top_y, right_x, bottom_y, hwidth,
+			 vwidth, left_p, right_p, &clip_rect);
+  else
+    haiku_draw_relief_rect (s, left_x, top_y, right_x, bottom_y, hwidth,
+			    vwidth, raised_p, true, true, left_p, right_p,
+			    &clip_rect);
+}
+
+static void
+haiku_draw_glyph_string_foreground (struct glyph_string *s)
+{
+  int i, x;
+  if (s->face->box != FACE_NO_BOX
+      && s->first_glyph->left_box_line_p)
+    x = s->x + max (s->face->box_vertical_line_width, 0);
+  else
+    x = s->x;
+
+  void *view = FRAME_HAIKU_VIEW (s->f);
+
+  if (s->font_not_found_p)
+    {
+      BView_StartClip (view);
+      if (s->hl == DRAW_CURSOR)
+	BView_SetHighColor (view, FRAME_OUTPUT_DATA (s->f)->cursor_fg);
+      else
+	BView_SetHighColor (view, s->face->foreground);
+      for (i = 0; i < s->nchars; ++i)
+	{
+	  struct glyph *g = s->first_glyph + i;
+	  BView_StrokeRectangle (view, x, s->y, g->pixel_width,
+				 s->height);
+	  x += g->pixel_width;
+	}
+      BView_EndClip (view);
+    }
+  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 (s->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;
+  unsigned char2b[8];
+  int x, i, j;
+
+  /* If first glyph of S has a left box line, start drawing the text
+     of S to the right of that box line.  */
+  if (s->face && s->face->box != FACE_NO_BOX
+      && s->first_glyph->left_box_line_p)
+    x = s->x + max (s->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 (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);
+	}
+      BView_StartClip (FRAME_HAIKU_VIEW (s->f));
+      if (glyph->u.glyphless.method != GLYPHLESS_DISPLAY_THIN_SPACE)
+	BView_FillRectangle (FRAME_HAIKU_VIEW (s->f),
+			     x, s->ybase - glyph->ascent,
+			     glyph->pixel_width - 1,
+			     glyph->ascent + glyph->descent - 1);
+      BView_EndClip (FRAME_HAIKU_VIEW (s->f));
+      x += glyph->pixel_width;
+   }
+}
+
+static void
+haiku_draw_stretch_glyph_string (struct glyph_string *s)
+{
+  eassert (s->first_glyph->type == STRETCH_GLYPH);
+
+  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;
+      else
+	x += width;
+
+      haiku_maybe_draw_background (s, 1);
+
+      if (width < background_width)
+	{
+	  int y = s->y;
+	  int w = background_width - width, h = s->height;
+
+	  if (!s->face->stipple)
+	    {
+	      uint32_t bkg;
+	      if (s->hl == DRAW_MOUSE_FACE)
+		haiku_mouse_face_colors (s, NULL, &bkg);
+	      else
+		bkg = s->face->background;
+
+	      void *view = FRAME_HAIKU_VIEW (s->f);
+
+	      BView_StartClip (view);
+	      BView_SetHighColor (view, bkg);
+	      BView_FillRectangle (view, x, y, w, h);
+	      BView_EndClip (view);
+	    }
+	}
+    }
+  else if (!s->background_filled_p)
+    {
+      int background_width = s->background_width;
+      int x = s->x, text_left_x = window_box_left_offset (s->w, TEXT_AREA);
+      if (x < text_left_x && !s->row->mode_line_p)
+	{
+	  int left_x = WINDOW_LEFT_SCROLL_BAR_AREA_WIDTH (s->w);
+	  int right_x = text_left_x;
+
+	  if (WINDOW_HAS_FRINGES_OUTSIDE_MARGINS (s->w))
+	    left_x += WINDOW_LEFT_FRINGE_WIDTH (s->w);
+	  else
+	    right_x -= WINDOW_LEFT_FRINGE_WIDTH (s->w);
+
+	  /* Adjust X and BACKGROUND_WIDTH to fit inside the space
+	     between LEFT_X and RIGHT_X.  */
+	  if (x < left_x)
+	    {
+	      background_width -= left_x - x;
+	      x = left_x;
+	    }
+	  if (x + background_width > right_x)
+	    background_width = right_x - x;
+	}
+
+      if (background_width > 0)
+	{
+	  void *view = FRAME_HAIKU_VIEW (s->f);
+	  BView_StartClip (view);
+	  uint32_t bkg;
+	  if (s->hl == DRAW_MOUSE_FACE)
+	    haiku_mouse_face_colors (s, NULL, &bkg);
+	  else if (s->hl == DRAW_CURSOR)
+	    bkg = FRAME_CURSOR_COLOR (s->f).pixel;
+	  else
+	    bkg = s->face->background;
+
+	  BView_SetHighColor (view, bkg);
+	  BView_FillRectangle (view, x, s->y, background_width, s->height);
+	  BView_EndClip (view);
+	}
+    }
+  s->background_filled_p = 1;
+}
+
+static void
+haiku_start_clip (struct glyph_string *s)
+{
+  void *view = FRAME_HAIKU_VIEW (s->f);
+  BView_draw_lock (view);
+  BView_StartClip (view);
+}
+
+static void
+haiku_end_clip (struct glyph_string *s)
+{
+  void *view = FRAME_HAIKU_VIEW (s->f);
+  BView_EndClip (view);
+  BView_draw_unlock (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_VIEW (f), x, y, width, height);
+}
+
+static void
+haiku_update_begin (struct frame *f)
+{
+}
+
+static void
+haiku_update_end (struct frame *f)
+{
+  MOUSE_HL_INFO (f)->mouse_face_defer = false;
+  flush_frame (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_VIEW (s->f);
+
+  /* If first glyph of S has a left box line, start drawing the text
+     of S to the right of that box line.  */
+  if (s->face && s->face->box != FACE_NO_BOX
+      && s->first_glyph->left_box_line_p)
+    x = s->x + max (s->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)
+    {
+      BView_StartClip (view);
+      if (s->hl == DRAW_CURSOR)
+	BView_SetHighColor (view, FRAME_OUTPUT_DATA (s->f)->cursor_fg);
+      else
+	BView_SetHighColor (view, s->face->foreground);
+      BView_StrokeRectangle (view, s->x, s->y, s->width - 1, s->height - 1);
+      BView_EndClip (view);
+    }
+  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 (s->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 (s->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 (s->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);
+
+  /* If first glyph of S has a left box line, start drawing it to the
+     right of that line.  */
+  if (s->face->box != FACE_NO_BOX
+      && s->first_glyph->left_box_line_p
+      && s->slice.x == 0)
+    x += max (s->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_draw_image_glyph_string (struct glyph_string *s)
+{
+  struct face *face;
+
+  if (s->hl == DRAW_MOUSE_FACE)
+    {
+      face = FACE_FROM_ID_OR_NULL (s->f,
+				   MOUSE_HL_INFO (s->f)->mouse_face_face_id);
+      if (!face)
+       face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
+    }
+  else
+    face = s->face;
+
+  int box_line_hwidth = max (s->face->box_vertical_line_width, 0);
+  int box_line_vwidth = max (s->face->box_horizontal_line_width, 0);
+
+  int x, y;
+  int height, width;
+
+  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;
+
+  void *view = FRAME_HAIKU_VIEW (s->f);
+  void *bitmap = s->img->pixmap;
+
+  s->stippled_p = s->face->stipple != 0;
+
+  BView_draw_lock (view);
+  BView_StartClip (view);
+  BView_SetHighColor (view, face->background);
+  BView_FillRectangle (view, x, y, width, height);
+  BView_EndClip (view);
+  BView_draw_unlock (view);
+
+  if (bitmap)
+    {
+      struct haiku_rect nr;
+      Emacs_Rectangle cr, ir, r;
+
+      get_glyph_string_clip_rect (s, &nr);
+      CONVERT_TO_EMACS_RECT (cr, nr);
+      x = s->x;
+      y = s->ybase - image_ascent (s->img, s->face, &s->slice);
+
+      if (s->slice.x == 0)
+	x += s->img->hmargin;
+      if (s->slice.y == 0)
+	y += s->img->vmargin;
+
+      if (s->face->box != FACE_NO_BOX
+	  && s->first_glyph->left_box_line_p
+	  && s->slice.x == 0)
+	x += max (s->face->box_vertical_line_width, 0);
+
+      ir.x = x;
+      ir.y = y;
+      ir.width = s->slice.width;
+      ir.height = s->slice.height;
+      r = ir;
+
+      void *mask = s->img->mask;
+
+      if (gui_intersect_rectangles (&cr, &ir, &r))
+	{
+	  BView_draw_lock (view);
+	  BView_StartClip (view);
+
+	  haiku_clip_to_string (s);
+	  if (s->img->have_be_transforms_p)
+	    {
+	      bitmap = BBitmap_transform_bitmap (bitmap,
+						 s->img->mask,
+						 face->background,
+						 s->img->be_rotate,
+						 s->img->width,
+						 s->img->height);
+	      mask = NULL;
+	    }
+
+	  BView_DrawBitmap (view, bitmap,
+			    s->slice.x + r.x - x,
+			    s->slice.y + r.y - y,
+			    r.width, r.height,
+			    r.x, r.y, r.width, r.height);
+	  if (mask)
+	    {
+	      BView_DrawMask (mask, view,
+			      s->slice.x + r.x - x,
+			      s->slice.y + r.y - y,
+			      r.width, r.height,
+			      r.x, r.y, r.width, r.height,
+			      face->background);
+	    }
+
+	  if (s->img->have_be_transforms_p)
+	    BBitmap_free (bitmap);
+	  BView_EndClip (view);
+	  BView_draw_unlock (view);
+	}
+
+      if (s->hl == DRAW_CURSOR)
+	{
+	  BView_draw_lock (view);
+	  BView_StartClip (view);
+	  BView_SetPenSize (view, 1);
+	  BView_SetHighColor (view, FRAME_CURSOR_COLOR (s->f).pixel);
+	  BView_StrokeRectangle (view, r.x, r.y, r.width, r.height);
+	  BView_EndClip (view);
+	  BView_draw_unlock (view);
+	}
+    }
+
+  if (s->img->relief
+      || s->hl == DRAW_IMAGE_RAISED
+      || s->hl == DRAW_IMAGE_SUNKEN)
+    haiku_draw_image_relief (s);
+
+  if (!s->for_overlaps && s->face->box != FACE_NO_BOX)
+    haiku_draw_string_box (s);
+}
+
+static void
+haiku_draw_glyph_string (struct glyph_string *s)
+{
+  block_input ();
+  prepare_face_for_display (s->f, s->face);
+
+  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, s->next->face);
+	    haiku_start_clip (s->next);
+	    haiku_clip_to_string (s->next);
+            if (next->first_glyph->type != STRETCH_GLYPH)
+	      haiku_maybe_draw_background (s->next, 1);
+            else
+	      haiku_draw_stretch_glyph_string (s->next);
+            next->num_clips = 0;
+	    haiku_end_clip (s);
+          }
+    }
+
+  haiku_start_clip (s);
+  haiku_inverse_clip_to_scroll_bars (s->f);
+
+  if (!s->for_overlaps && s->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);
+      haiku_draw_string_box (s);
+    }
+  else if (!s->clip_head && !s->clip_tail &&
+	   ((s->prev && s->left_overhang && s->prev->hl != s->hl) ||
+	    (s->next && s->right_overhang && s->next->hl != s->hl)))
+    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;
+    }
+
+  if (!s->for_overlaps)
+    {
+      uint32_t dcol;
+      struct face *face;
+      if (s->hl == DRAW_MOUSE_FACE)
+	{
+	  face = FACE_FROM_ID_OR_NULL (s->f,
+				       MOUSE_HL_INFO (s->f)->mouse_face_face_id);
+	  if (!face)
+	    face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
+	}
+      else
+	face = s->face;
+      dcol = face->foreground;
+
+      haiku_draw_text_decoration (s, face, dcol, 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;
+		struct face *save_face = prev->face;
+
+		prev->hl = s->hl;
+		prev->face = s->face;
+		haiku_start_clip (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;
+		prev->face = save_face;
+		prev->num_clips = 0;
+	      }
+	}
+
+      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;
+		struct face *save_face = next->face;
+
+		next->hl = s->hl;
+		next->face = s->face;
+		haiku_start_clip (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->background_filled_p = 0;
+		next->hl = save;
+		next->face = save_face;
+		next->clip_head = next;
+		next->num_clips = 0;
+	      }
+	}
+    }
+  s->num_clips = 0;
+  haiku_end_clip (s);
+  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_VIEW (f);
+	  BView_draw_lock (view);
+	  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_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)
+{
+  if (FRAME_HAIKU_WINDOW (f))
+    {
+      block_input ();
+      BWindow_resize (FRAME_HAIKU_WINDOW (f), width, height);
+      unblock_input ();
+    }
+}
+
+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)
+{
+  struct frame *f = XFRAME (WINDOW_FRAME (w));
+
+  struct glyph *phys_cursor_glyph;
+  struct glyph *cursor_glyph;
+
+  void *view = FRAME_HAIKU_VIEW (f);
+
+  int fx, fy, h, cursor_height;
+
+  if (!on_p)
+    return;
+
+  if (cursor_type == NO_CURSOR)
+    {
+      w->phys_cursor_width = 0;
+      return;
+    }
+
+  w->phys_cursor_on_p = true;
+  w->phys_cursor_type = cursor_type;
+
+  phys_cursor_glyph = get_phys_cursor_glyph (w);
+
+  if (!phys_cursor_glyph)
+    {
+      if (glyph_row->exact_window_width_line_p
+          && w->phys_cursor.hpos >= glyph_row->used[TEXT_AREA])
+        {
+          glyph_row->cursor_in_fringe_p = 1;
+          draw_fringe_bitmap (w, glyph_row, 0);
+        }
+      return;
+    }
+
+  get_phys_cursor_geometry (w, glyph_row, phys_cursor_glyph, &fx, &fy, &h);
+
+  if (cursor_type == BAR_CURSOR)
+    {
+      if (cursor_width < 1)
+	cursor_width = max (FRAME_CURSOR_WIDTH (f), 1);
+      if (cursor_width < w->phys_cursor_width)
+        w->phys_cursor_width = cursor_width;
+    }
+  else if (cursor_type == HBAR_CURSOR)
+    {
+      cursor_height = (cursor_width < 1) ? lrint (0.25 * h) : cursor_width;
+      if (cursor_height > glyph_row->height)
+        cursor_height = glyph_row->height;
+      if (h > cursor_height)
+        fy += h - cursor_height;
+      h = cursor_height;
+    }
+
+  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);
+      return;
+    }
+
+  if (cursor_type == HOLLOW_BOX_CURSOR &&
+      phys_cursor_glyph->type != IMAGE_GLYPH)
+    draw_phys_cursor_glyph (w, glyph_row, DRAW_NORMAL_TEXT);
+
+  BView_draw_lock (view);
+  BView_StartClip (view);
+  BView_SetHighColor (view, FRAME_CURSOR_COLOR (f).pixel);
+  haiku_clip_to_row (w, glyph_row, TEXT_AREA);
+
+  switch (cursor_type)
+    {
+    default:
+    case DEFAULT_CURSOR:
+    case NO_CURSOR:
+      break;
+    case HBAR_CURSOR:
+      BView_FillRectangle (view, fx, fy, w->phys_cursor_width, h);
+      break;
+    case BAR_CURSOR:
+      cursor_glyph = get_phys_cursor_glyph (w);
+      if (cursor_glyph->resolved_level & 1)
+	BView_FillRectangle (view, fx + cursor_glyph->pixel_width - w->phys_cursor_width,
+			     fy, w->phys_cursor_width, h);
+      else
+	BView_FillRectangle (view, fx, fy, w->phys_cursor_width, h);
+      break;
+    case HOLLOW_BOX_CURSOR:
+      if (phys_cursor_glyph->type != IMAGE_GLYPH)
+	{
+	  BView_SetPenSize (view, 1);
+	  BView_StrokeRectangle (view, fx, fy, w->phys_cursor_width, h);
+	}
+      else
+	draw_phys_cursor_glyph (w, glyph_row, DRAW_CURSOR);
+      break;
+    case FILLED_BOX_CURSOR:
+      draw_phys_cursor_glyph (w, glyph_row, DRAW_CURSOR);
+    }
+  BView_EndClip (view);
+  BView_draw_unlock (view);
+}
+
+static void
+haiku_show_hourglass (struct frame *f)
+{
+  if (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_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_VIEW (f);
+  BView_draw_lock (view);
+  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 = FRAME_COLUMN_WIDTH (f);
+  FRAME_CONFIG_SCROLL_BAR_WIDTH (f) = BScrollBar_default_size (0) + 1;
+  FRAME_CONFIG_SCROLL_BAR_COLS (f) =
+    (FRAME_CONFIG_SCROLL_BAR_WIDTH (f) + unit - 1) / unit;
+}
+
+static void
+haiku_set_scroll_bar_default_height (struct frame *f)
+{
+  int height = FRAME_LINE_HEIGHT (f);
+  FRAME_CONFIG_SCROLL_BAR_HEIGHT (f) = BScrollBar_default_size (1) + 1;
+  FRAME_CONFIG_SCROLL_BAR_LINES (f) =
+    (FRAME_CONFIG_SCROLL_BAR_HEIGHT (f) + 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_VIEW (f);
+
+  BView_draw_lock (view);
+  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 (f, 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_StrokeLine (view, x0, y1, x1 - 1, y1);
+    }
+  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 = XFRAME (WINDOW_FRAME (w));
+  Lisp_Object barobj;
+
+  void *sb = NULL;
+  void *vw = FRAME_HAIKU_VIEW (f);
+
+  block_input ();
+  struct scroll_bar *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;
+
+  sb = BScrollBar_make_for_view (vw, horizontal_p,
+				 left, top, left + width - 1,
+				 top + height - 1, bar);
+
+  BView_publish_scroll_bar (vw, left, top, width, height);
+
+  bar->next = FRAME_SCROLL_BARS (f);
+  bar->prev = Qnil;
+  bar->scroll_bar = sb;
+  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)
+{
+  eassert (WINDOW_HAS_HORIZONTAL_SCROLL_BAR (w));
+  Lisp_Object barobj;
+  struct scroll_bar *bar;
+  int top, height, left, width;
+  int window_x, window_width;
+
+  /* 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);
+
+  block_input ();
+
+  if (NILP (w->horizontal_scroll_bar))
+    {
+      bar = haiku_scroll_bar_create (w, left, top, width, height, true);
+      BView_scroll_bar_update (bar->scroll_bar, portion, whole, position);
+      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)
+	{
+	  void *view = FRAME_HAIKU_VIEW (WINDOW_XFRAME (w));
+	  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;
+	}
+
+      if (!bar->dragging)
+	{
+	  BView_scroll_bar_update (bar->scroll_bar, portion, whole, position);
+	  BView_invalidate (bar->scroll_bar);
+	}
+    }
+  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)
+{
+  eassert (WINDOW_HAS_VERTICAL_SCROLL_BAR (w));
+  Lisp_Object barobj;
+  struct scroll_bar *bar;
+  int top, height, left, width;
+  int window_y, window_height;
+
+  /* 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);
+  block_input ();
+
+  if (NILP (w->vertical_scroll_bar))
+    {
+      bar = haiku_scroll_bar_create (w, left, top, width, height, false);
+      BView_scroll_bar_update (bar->scroll_bar, portion, whole, position);
+      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)
+	{
+	  void *view = FRAME_HAIKU_VIEW (WINDOW_XFRAME (w));
+	  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);
+	  flush_frame (WINDOW_XFRAME (w));
+	  BView_publish_scroll_bar (view, left, top, width, height);
+	  bar->left = left;
+	  bar->top = top;
+	  bar->width = width;
+	  bar->height = height;
+	}
+
+      if (!bar->dragging)
+	{
+	  BView_scroll_bar_update (bar->scroll_bar, portion, whole, position);
+	  bar->update = position;
+	  BView_invalidate (bar->scroll_bar);
+	}
+    }
+
+  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)
+{
+  void *view = FRAME_HAIKU_VIEW (XFRAME (WINDOW_FRAME (w)));
+  struct face *face = p->face;
+
+  BView_draw_lock (view);
+  BView_StartClip (view);
+
+  haiku_clip_to_row (w, row, ANY_AREA);
+  if (p->bx >= 0)
+    {
+      BView_SetHighColor (view, face->background);
+      BView_FillRectangle (view, p->bx, p->by, p->nx, p->ny);
+    }
+
+  if (p->which && p->which < fringe_bitmap_fillptr)
+    {
+      void *bitmap = fringe_bmps[p->which];
+
+      uint32_t col;
+
+      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 0 /* See comment in BView_DrawFringeBitmap. */
+      BView_SetLowColor (view, col);
+      BView_DrawBitmapWithEraseOp (view, bitmap, p->x, p->y, p->wd, p->h);
+#else
+      BView_DrawFringeBitmap (bitmap, view, p->x, p->y,
+			      p->wd, p->h, col | ((unsigned) 255 << 24));
+#endif
+    }
+  BView_EndClip (view);
+  BView_draw_unlock (view);
+}
+
+static void
+haiku_define_fringe_bitmap (int which, unsigned short *bits,
+			    int h, int wd)
+{
+  if (which >= fringe_bitmap_fillptr)
+    {
+      int i = fringe_bitmap_fillptr;
+      fringe_bitmap_fillptr = which + 20;
+      fringe_bmps = !i ? xmalloc (fringe_bitmap_fillptr * sizeof (void *)) :
+	xrealloc (fringe_bmps, fringe_bitmap_fillptr * sizeof (void *));
+
+      while (i < fringe_bitmap_fillptr)
+	fringe_bmps[i++] = NULL;
+    }
+
+  fringe_bmps[which] = BBitmap_new (wd, h, 1);
+  BBitmap_import_mono_bits (fringe_bmps[which], bits, wd, h);
+}
+
+static void
+haiku_destroy_fringe_bitmap (int which)
+{
+  if (which >= fringe_bitmap_fillptr)
+    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_VIEW (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;
+    }
+
+  if (!height)
+    return;
+
+  block_input ();
+  gui_clear_cursor (w);
+  BView_draw_lock (view);
+  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 ();
+}
+
+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)
+{
+  block_input ();
+  if (!fp)
+    return;
+  Lisp_Object frame, tail;
+  struct frame *f1 = NULL;
+  FOR_EACH_FRAME (tail, frame)
+    XFRAME (frame)->mouse_moved = false;
+
+  if (gui_mouse_grabbed (x_display_list) && !EQ (track_mouse, Qdropping))
+    f1 = x_display_list->last_mouse_frame;
+
+  if (!f1 || FRAME_TOOLTIP_P (f1))
+    f1 = ((EQ (track_mouse, Qdropping) && gui_mouse_grabbed (x_display_list))
+	  ? x_display_list->last_mouse_frame
+	  : NULL);
+
+  if (!f1 && insist > 0)
+    f1 = SELECTED_FRAME ();
+
+  if (!f1 || (!FRAME_HAIKU_P (f1) && (insist > 0)))
+    FOR_EACH_FRAME (tail, frame)
+      if (FRAME_HAIKU_P (XFRAME (frame)))
+	f1 = XFRAME (frame);
+
+  if (f1 && FRAME_HAIKU_P (f1))
+    {
+      int sx, sy;
+      void *view = FRAME_HAIKU_VIEW (f1);
+      if (view)
+	{
+	  BView_get_mouse (view, &sx, &sy);
+
+	  remember_mouse_glyph (f1, sx, sy, &x_display_list->last_mouse_glyph);
+	  x_display_list->last_mouse_glyph_frame = f1;
+
+	  *bar_window = Qnil;
+	  *part = scroll_bar_above_handle;
+	  *fp = f1;
+	  XSETINT (*x, sx);
+	  XSETINT (*y, sy);
+	}
+    }
+
+  unblock_input ();
+}
+
+static void
+haiku_flush (struct frame *f)
+{
+  if (FRAME_VISIBLE_P (f))
+    BWindow_Flush (FRAME_HAIKU_WINDOW (f));
+}
+
+static void
+haiku_define_frame_cursor (struct frame *f, Emacs_Cursor cursor)
+{
+  if (f->tooltip)
+    return;
+  block_input ();
+  if (!f->pointer_invisible && FRAME_HAIKU_VIEW (f))
+    {
+      BView_set_view_cursor (FRAME_HAIKU_VIEW (f), cursor);
+    }
+  unblock_input ();
+  FRAME_OUTPUT_DATA (f)->current_cursor = cursor;
+}
+
+static void
+haiku_update_window_end (struct window *w, bool cursor_on_p,
+			 bool mouse_face_overwritten_p)
+{
+
+}
+
+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 (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.  */
+      struct haiku_font_pattern ptn;
+      ptn.specified = 0;
+
+      if (f->tooltip)
+	BFont_populate_plain_family (&ptn);
+      else
+	BFont_populate_fixed_family (&ptn);
+
+      if (ptn.specified & FSPEC_FAMILY)
+	font = font_open_by_name (f, build_unibyte_string (ptn.family));
+    }
+
+  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;
+      if (f->tooltip)
+	names = (const char *[]) { "Sans Serif-12", NULL };
+      else
+	names = (const char *[]) { "monospace-12",
+				   "-*-*-medium-r-normal-*-*-140-*-*-c-*-iso8859-1",
+				   "-*-*-medium-r-*-*-*-*-*-*-c-*-iso8859-1",
+				   "-*-fixed-*-*-*-*-*-140-*-*-c-*-iso8859-1",
+				   "fixed",
+				   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");
+    }
+  else if (!NILP (font_param))
+    {
+      /* Remember the explicit font parameter, so we can re-apply it
+         after we've applied the `default' face settings.  */
+      AUTO_FRAME_ARG (arg, Qfont_parameter, font_param);
+      gui_set_frame_parameters (f, arg);
+    }
+
+  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,
+    haiku_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,
+    0, /* 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 = get_frame_param (f, Qfullscreen);
+
+  if (!EQ (lval, Qmaximized) && FRAME_OUTPUT_DATA (f)->zoomed_p)
+    lval = Qmaximized;
+  else if (EQ (lval, Qmaximized) && !FRAME_OUTPUT_DATA (f)->zoomed_p)
+    lval = Qnil;
+
+  store_frame_param (f, Qfullscreen, lval);
+}
+
+static int
+haiku_read_socket (struct terminal *terminal, struct input_event *hold_quit)
+{
+  block_input ();
+  int message_count = 0;
+  void *buf;
+  ssize_t b_size;
+  struct unhandled_event *unhandled_events = NULL;
+
+  buf = alloca (200);
+  haiku_read_size (&b_size);
+  while (b_size >= 0)
+    {
+      enum haiku_event_type type;
+      struct input_event inev, inev2;
+
+      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;
+
+      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 = (int) b->px_widthf;
+	    int height = (int) b->px_heightf;
+
+	    BView_resize_to (FRAME_HAIKU_VIEW (f), width, height);
+	    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);
+	      }
+
+	    if (FRAME_OUTPUT_DATA (f)->pending_zoom_width != width ||
+		FRAME_OUTPUT_DATA (f)->pending_zoom_height != height)
+	      {
+		FRAME_OUTPUT_DATA (f)->zoomed_p = 0;
+		haiku_make_fullscreen_consistent (f);
+	      }
+	    else
+	      {
+		FRAME_OUTPUT_DATA (f)->zoomed_p = 1;
+		FRAME_OUTPUT_DATA (f)->pending_zoom_width = INT_MIN;
+		FRAME_OUTPUT_DATA (f)->pending_zoom_height = INT_MIN;
+	      }
+	    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;
+	    struct frame *f = haiku_window_to_frame (b->window);
+	    int non_ascii_p;
+	    if (!f)
+	      continue;
+
+	    BMapKey (b->kc, &non_ascii_p, &inev.code);
+
+	    if (!non_ascii_p)
+	      inev.code = b->unraw_mb_char;
+
+	    if (non_ascii_p)
+	      inev.kind = NON_ASCII_KEYSTROKE_EVENT;
+	    else
+	      inev.kind = inev.code > 127 ? MULTIBYTE_CHAR_KEYSTROKE_EVENT :
+		ASCII_KEYSTROKE_EVENT;
+
+	    if (b->modifiers & HAIKU_MODIFIER_SHIFT)
+	      inev.modifiers |= shift_modifier;
+	    if (b->modifiers & HAIKU_MODIFIER_CTRL)
+	      inev.modifiers |= ctrl_modifier;
+	    if (b->modifiers & HAIKU_MODIFIER_ALT)
+	      inev.modifiers |= meta_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 MOUSE_MOTION:
+	  {
+	    struct haiku_mouse_motion_event *b = buf;
+	    struct frame *f = haiku_window_to_frame (b->window);
+
+	    if (!f)
+	      continue;
+
+	    Lisp_Object frame;
+	    XSETFRAME (frame, 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_new_focus_frame (x_display_list->focused_frame);
+		help_echo_string = Qnil;
+		gen_help_event (Qnil, frame, Qnil, Qnil, 0);
+	      }
+	    else
+	      {
+		struct haiku_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
+		struct haiku_rect r = dpyinfo->last_mouse_glyph;
+
+		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;
+
+		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;
+		    dpyinfo->last_mouse_scroll_bar = NULL;
+		    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;
+		    gen_help_event (help_echo_string, frame, help_echo_window,
+				    help_echo_object, help_echo_pos);
+		  }
+
+		if (MOUSE_HL_INFO (f)->mouse_face_hidden)
+		  {
+		    MOUSE_HL_INFO (f)->mouse_face_hidden = 0;
+		    clear_mouse_face (MOUSE_HL_INFO (f));
+		  }
+
+		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);
+
+		    if (WINDOWP (window)
+			&& !EQ (window, last_mouse_window)
+			&& !EQ (window, selected_window)
+			&& (!NILP (focus_follows_mouse)
+				|| (EQ (XWINDOW (window)->frame,
+					XWINDOW (selected_window)->frame))))
+		      {
+			inev.kind = SELECT_WINDOW_EVENT;
+			inev.frame_or_window = window;
+		      }
+
+		    last_mouse_window = window;
+		  }
+	      }
+	    break;
+	  }
+	case BUTTON_UP:
+	case BUTTON_DOWN:
+	  {
+	    struct haiku_button_event *b = buf;
+	    struct frame *f = haiku_window_to_frame (b->window);
+	    Lisp_Object tab_bar_arg = Qnil;
+	    int tab_bar_p = 0, tool_bar_p = 0;
+
+	    if (!f)
+	      continue;
+
+	    struct haiku_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
+
+	    if (b->modifiers & HAIKU_MODIFIER_SHIFT)
+	      inev.modifiers |= shift_modifier;
+	    if (b->modifiers & HAIKU_MODIFIER_CTRL)
+	      inev.modifiers |= ctrl_modifier;
+	    if (b->modifiers & HAIKU_MODIFIER_ALT)
+	      inev.modifiers |= meta_modifier;
+
+	    x_display_list->last_mouse_glyph_frame = 0;
+
+	    /* 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);
+		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);
+	      }
+
+	    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);
+		tool_bar_p = EQ (window, f->tool_bar_window);
+
+		if (tool_bar_p)
+		  handle_tool_bar_click
+		    (f, x, y, type == BUTTON_DOWN, inev.modifiers);
+	      }
+
+	    if (type == BUTTON_UP)
+	      {
+		inev.modifiers |= up_modifier;
+		dpyinfo->grabbed &= ~(1 << b->btn_no);
+	      }
+	    else
+	      {
+		inev.modifiers |= down_modifier;
+		dpyinfo->last_mouse_frame = f;
+		dpyinfo->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 (!(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;
+
+	    inev.modifiers |= type == BUTTON_UP ?
+	      up_modifier : down_modifier;
+
+	    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);
+
+	    if (!f)
+	      continue;
+
+	    if (FRAME_OUTPUT_DATA (f)->pending_zoom_x != b->x ||
+		FRAME_OUTPUT_DATA (f)->pending_zoom_y != b->y)
+	      FRAME_OUTPUT_DATA (f)->zoomed_p = 0;
+	    else
+	      {
+		FRAME_OUTPUT_DATA (f)->zoomed_p = 1;
+		FRAME_OUTPUT_DATA (f)->pending_zoom_x = INT_MIN;
+		FRAME_OUTPUT_DATA (f)->pending_zoom_y = INT_MIN;
+	      }
+
+	    if (FRAME_PARENT_FRAME (f))
+	      haiku_coords_from_parent (f, &b->x, &b->y);
+
+	    if (b->x != f->left_pos || b->y != f->top_pos)
+	      {
+		inev.kind = MOVE_FRAME_EVENT;
+
+		XSETINT (inev.x, b->x);
+		XSETINT (inev.y, b->y);
+
+		f->left_pos = b->x;
+		f->top_pos = b->y;
+
+		if (FRAME_PARENT_FRAME (f))
+		  {
+		    void *window = FRAME_HAIKU_WINDOW (FRAME_PARENT_FRAME (f));
+		    EmacsWindow_move_weak_child (window, b->window, b->x, b->y);
+		  }
+
+		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 = b->scroll_bar;
+
+	    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;
+
+		XSETINT (inev.x, b->position);
+		XSETINT (inev.y, bar->total);
+		XSETWINDOW (inev.frame_or_window, w);
+	      }
+	    break;
+	  }
+	case SCROLL_BAR_DRAG_EVENT:
+	  {
+	    struct haiku_scroll_bar_drag_event *b = buf;
+	    struct scroll_bar *bar = b->scroll_bar;
+
+	    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;
+
+	    if (!f)
+	      continue;
+	    BView_get_mouse (FRAME_HAIKU_VIEW (f), &x, &y);
+
+	    if (b->modifiers & HAIKU_MODIFIER_SHIFT)
+	      inev.modifiers |= shift_modifier;
+	    if (b->modifiers & HAIKU_MODIFIER_CTRL)
+	      inev.modifiers |= ctrl_modifier;
+	    if (b->modifiers & HAIKU_MODIFIER_ALT)
+	      inev.modifiers |= meta_modifier;
+	    inev2.modifiers = inev.modifiers;
+
+	    if (b->delta_y)
+	      {
+		inev.kind = WHEEL_EVENT;
+		inev.code = 0;
+
+		XSETINT (inev.x, x);
+		XSETINT (inev.y, y);
+		XSETFRAME (inev.frame_or_window, f);
+
+		inev.modifiers |= signbit (b->delta_y) ?
+		  up_modifier : down_modifier;
+	      }
+
+	    if (b->delta_x)
+	      {
+		inev2.kind = HORIZ_WHEEL_EVENT;
+		inev2.code = 0;
+
+		XSETINT (inev2.x, x);
+		XSETINT (inev2.y, y);
+		XSETFRAME (inev2.frame_or_window, f);
+
+		inev2.modifiers |= signbit (b->delta_x) ?
+		  up_modifier : down_modifier;
+	      }
+
+	    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;
+
+	    int old_height = FRAME_MENU_BAR_HEIGHT (f);
+
+	    FRAME_MENU_BAR_HEIGHT (f) = b->height + 1;
+	    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_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)
+	      {
+		if (!FRAME_OUTPUT_DATA (f)->menu_up_to_date_p)
+		  {
+		    BView_draw_lock (FRAME_HAIKU_VIEW (f));
+		    /* This shouldn't be here, but nsmenu does it, so
+		       it should probably be safe. */
+		    int was_waiting_for_input_p = waiting_for_input;
+		    if (waiting_for_input)
+		      waiting_for_input = 0;
+		    set_frame_menubar (f, 1);
+		    waiting_for_input = was_waiting_for_input_p;
+		    BView_draw_unlock (FRAME_HAIKU_VIEW (f));
+		  }
+		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;
+
+	    if (FRAME_OUTPUT_DATA (f)->menu_up_to_date_p)
+	      find_and_call_menu_selection (f, f->menu_bar_items_used,
+					    f->menu_bar_vector, b->ptr);
+	    break;
+	  }
+	case FILE_PANEL_EVENT:
+	  {
+	    if (!popup_activated_p)
+	      continue;
+
+	    struct unhandled_event *ev = xmalloc (sizeof *ev);
+	    ev->next = unhandled_events;
+	    ev->type = type;
+	    memcpy (&ev->buffer, buf, 200);
+
+	    unhandled_events = ev;
+	    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;
+
+	    FRAME_OUTPUT_DATA (f)->pending_zoom_height = b->height;
+	    FRAME_OUTPUT_DATA (f)->pending_zoom_width = b->width;
+	    FRAME_OUTPUT_DATA (f)->pending_zoom_x = b->x;
+	    FRAME_OUTPUT_DATA (f)->pending_zoom_y = b->y;
+
+	    FRAME_OUTPUT_DATA (f)->zoomed_p = 1;
+	    haiku_make_fullscreen_consistent (f);
+	    break;
+	  }
+	case KEY_UP:
+	default:
+	  break;
+	}
+
+      haiku_read_size (&b_size);
+
+      if (inev.kind != NO_EVENT)
+	{
+	  kbd_buffer_store_event_hold (&inev, hold_quit);
+	  ++message_count;
+	}
+
+      if (inev2.kind != NO_EVENT)
+	{
+	  kbd_buffer_store_event_hold (&inev2, hold_quit);
+	  ++message_count;
+	}
+    }
+
+  for (struct unhandled_event *ev = unhandled_events; ev;)
+    {
+      haiku_write_without_signal (ev->type, &ev->buffer);
+      struct unhandled_event *old = ev;
+      ev = old->next;
+      xfree (old);
+    }
+
+  unblock_input ();
+  return message_count;
+}
+
+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_beep (struct frame *f)
+{
+  if (visible_bell)
+    {
+      void *view = FRAME_HAIKU_VIEW (f);
+      if (view)
+	{
+	  block_input ();
+	  BView_draw_lock (view);
+	  if (!EmacsView_double_buffered_p (view))
+	    {
+	      BView_SetHighColorForVisibleBell (view, FRAME_FOREGROUND_PIXEL (f));
+	      BView_FillRectangleForVisibleBell (view, 0, 0, FRAME_PIXEL_WIDTH (f),
+						 FRAME_PIXEL_HEIGHT (f));
+	      SET_FRAME_GARBAGED (f);
+	      expose_frame (f, 0, 0, 0, 0);
+	    }
+	  else
+	    {
+	      EmacsView_do_visible_bell (view, FRAME_FOREGROUND_PIXEL (f));
+	      haiku_flip_buffers (f);
+	    }
+	  BView_draw_unlock (view);
+	  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)
+    {
+      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)
+{
+  if (f->want_fullscreen == FULLSCREEN_MAXIMIZED)
+    {
+      EmacsWindow_make_fullscreen (FRAME_HAIKU_WINDOW (f), 0);
+      BWindow_zoom (FRAME_HAIKU_WINDOW (f));
+    }
+  else if (f->want_fullscreen == FULLSCREEN_BOTH)
+    EmacsWindow_make_fullscreen (FRAME_HAIKU_WINDOW (f), 1);
+  else if (f->want_fullscreen == FULLSCREEN_NONE)
+    {
+      EmacsWindow_make_fullscreen (FRAME_HAIKU_WINDOW (f), 0);
+      EmacsWindow_unzoom (FRAME_HAIKU_WINDOW (f));
+    }
+
+  f->want_fullscreen = FULLSCREEN_NONE;
+}
+
+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->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 = 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;
+
+  return terminal;
+}
+
+struct haiku_display_info *
+haiku_term_init (void)
+{
+  struct haiku_display_info *dpyinfo;
+  struct terminal *terminal;
+
+  Lisp_Object color_file, color_map;
+
+  block_input ();
+  Fset_input_interrupt_mode (Qnil);
+
+  baud_rate = 19200;
+
+  dpyinfo = xzalloc (sizeof *dpyinfo);
+  bzero (dpyinfo, sizeof *dpyinfo);
+
+  haiku_io_init ();
+
+  if (port_application_to_emacs < B_OK)
+    emacs_abort ();
+
+  color_file = Fexpand_file_name (build_string ("rgb.txt"),
+				  Fsymbol_value (intern ("data-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 ();
+
+  BScreen_res (&dpyinfo->resx, &dpyinfo->resy);
+
+  dpyinfo->next = x_display_list;
+  dpyinfo->n_planes = be_get_display_planes ();
+  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);
+  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)
+    {
+      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 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_VIEW (f);
+      block_input ();
+      BView_draw_lock (view);
+      BView_StartClip (view);
+
+      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 - 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);
+}
+
+void
+haiku_scroll_bar_remove (struct scroll_bar *bar)
+{
+  block_input ();
+  void *view = FRAME_HAIKU_VIEW (WINDOW_XFRAME (XWINDOW (bar->window)));
+  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
+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 = 0;
+
+  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;
+
+  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);
+}
diff --git a/src/haikuterm.h b/src/haikuterm.h
new file mode 100644
index 0000000000..d648cd6f9c
--- /dev/null
+++ b/src/haikuterm.h
@@ -0,0 +1,285 @@
+/* Haiku window system support
+   Copyright (C) 2021 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/>.  */
+
+#ifndef _HAIKU_TERM_H_
+#define _HAIKU_TERM_H_
+
+#include <pthread.h>
+
+#include "haikugui.h"
+#include "frame.h"
+#include "character.h"
+#include "dispextern.h"
+#include "font.h"
+
+#define C_FRAME struct frame *
+#define C_FONT struct font *
+#define C_TERMINAL struct terminal *
+
+#define HAVE_CHAR_CACHE_MAX 65535
+
+extern int popup_activated_p;
+
+extern void be_app_quit (void);
+
+struct haikufont_info
+{
+  struct font font;
+  haiku be_font;
+  ptrdiff_t charc_size;
+  int *have_char_cache;
+  struct font_metrics *met_cache;
+};
+
+struct haiku_bitmap_record
+{
+  haiku img;
+  char *file;
+  int refcount;
+  int height, width, depth;
+};
+
+struct haiku_display_info
+{
+  /* Chain of all haiku_display_info structures. */
+  struct haiku_display_info *next;
+  C_TERMINAL terminal;
+
+  Lisp_Object name_list_element;
+  Lisp_Object color_map;
+
+  int n_fonts;
+
+  int smallest_char_width;
+  int smallest_font_height;
+
+  struct frame *focused_frame;
+  struct frame *focus_event_frame;
+  struct frame *last_mouse_glyph_frame;
+
+  struct haiku_bitmap_record *bitmaps;
+  ptrdiff_t bitmaps_size;
+  ptrdiff_t bitmaps_last;
+
+  int grabbed;
+  int n_planes;
+  int color_p;
+
+  Window root_window;
+  Lisp_Object rdb;
+
+  Emacs_Cursor vertical_scroll_bar_cursor;
+  Emacs_Cursor horizontal_scroll_bar_cursor;
+
+  Mouse_HLInfo mouse_highlight;
+
+  C_FRAME highlight_frame;
+  C_FRAME last_mouse_frame;
+  C_FRAME last_mouse_motion_frame;
+
+  int last_mouse_motion_x;
+  int last_mouse_motion_y;
+
+  struct haiku_rect last_mouse_glyph;
+
+  void *last_mouse_scroll_bar;
+
+  haiku display;
+
+  double resx, resy;
+};
+
+struct haiku_output
+{
+  Emacs_Cursor text_cursor;
+  Emacs_Cursor nontext_cursor;
+  Emacs_Cursor modeline_cursor;
+  Emacs_Cursor hand_cursor;
+  Emacs_Cursor hourglass_cursor;
+  Emacs_Cursor horizontal_drag_cursor;
+  Emacs_Cursor vertical_drag_cursor;
+  Emacs_Cursor left_edge_cursor;
+  Emacs_Cursor top_left_corner_cursor;
+  Emacs_Cursor top_edge_cursor;
+  Emacs_Cursor top_right_corner_cursor;
+  Emacs_Cursor right_edge_cursor;
+  Emacs_Cursor bottom_right_corner_cursor;
+  Emacs_Cursor bottom_edge_cursor;
+  Emacs_Cursor bottom_left_corner_cursor;
+  Emacs_Cursor no_cursor;
+
+  Emacs_Cursor current_cursor;
+
+  struct haiku_display_info *display_info;
+
+  int baseline_offset;
+  int fontset;
+
+  Emacs_Color cursor_color;
+
+  Window window_desc, parent_desc;
+  char explicit_parent;
+
+  int titlebar_height;
+  int toolbar_height;
+
+  haiku window;
+  haiku view;
+  haiku menubar;
+
+  int menu_up_to_date_p;
+  int zoomed_p;
+
+  int pending_zoom_x;
+  int pending_zoom_y;
+  int pending_zoom_width;
+  int pending_zoom_height;
+
+  int menu_bar_open_p;
+
+  C_FONT font;
+
+  int hourglass_p;
+  uint32_t cursor_fg;
+  bool dirty_p;
+
+  /* The pending position we're waiting for. */
+  int pending_top, pending_left;
+};
+
+struct x_output
+{
+  /* Unused, makes term.c happy. */
+};
+
+extern struct haiku_display_info *x_display_list;
+extern struct font_driver const haikufont_driver;
+
+#ifdef HAVE_FREETYPE
+extern struct font_driver const ftbefont_driver;
+#ifdef HAVE_HARFBUZZ
+extern struct font_driver ftbehbfont_driver;
+#endif
+#endif
+
+struct scroll_bar
+{
+  /* These fields are shared by all vectors.  */
+  union vectorlike_header header;
+
+  /* The window we're a scroll bar for.  */
+  Lisp_Object window;
+
+  /* The next and previous in the chain of scroll bars in this frame.  */
+  Lisp_Object next, prev;
+
+  /* Fields after 'prev' are not traced by the GC.  */
+
+  /* The position and size of the scroll bar in pixels, relative to the
+     frame.  */
+  int top, left, width, height;
+
+  /* The actual scrollbar. */
+  void *scroll_bar;
+
+  /* Non-nil if the scroll bar handle is currently being dragged by
+     the user.  */
+  int dragging;
+
+  /* The update position if we are waiting for a scrollbar update, or
+     -1. */
+  int update;
+
+  /* The last known position of this scrollbar. */
+  int position;
+
+  /* The total number of units inside this scrollbar. */
+  int total;
+
+  /* True if the scroll bar is horizontal.  */
+  bool horizontal;
+};
+
+#define XSCROLL_BAR(vec) ((struct scroll_bar *) XVECTOR (vec))
+
+#define FRAME_DIRTY_P(f) (FRAME_OUTPUT_DATA (f)->dirty_p)
+#define MAKE_FRAME_DIRTY(f) (FRAME_DIRTY_P (f) = 1)
+#define FRAME_OUTPUT_DATA(f) ((f)->output_data.haiku)
+#define FRAME_HAIKU_WINDOW(f) (FRAME_OUTPUT_DATA (f)->window)
+#define FRAME_HAIKU_VIEW(f) ((MAKE_FRAME_DIRTY (f)), FRAME_OUTPUT_DATA (f)->view)
+#define FRAME_HAIKU_MENU_BAR(f) (FRAME_OUTPUT_DATA (f)->menubar)
+#define FRAME_DISPLAY_INFO(f) (FRAME_OUTPUT_DATA (f)->display_info)
+#define FRAME_FONT(f) (FRAME_OUTPUT_DATA (f)->font)
+#define FRAME_FONTSET(f) (FRAME_OUTPUT_DATA (f)->fontset)
+#define FRAME_NATIVE_WINDOW(f) (FRAME_OUTPUT_DATA (f)->window)
+#define FRAME_BASELINE_OFFSET(f) (FRAME_OUTPUT_DATA (f)->baseline_offset)
+#define FRAME_CURSOR_COLOR(f) (FRAME_OUTPUT_DATA (f)->cursor_color)
+
+extern void syms_of_haikuterm (void);
+extern void syms_of_haikufns (void);
+extern void syms_of_haikumenu (void);
+extern void syms_of_haikufont (void);
+extern void syms_of_haikuselect (void);
+extern void init_haiku_select (void);
+
+extern void haiku_iconify_frame (struct frame *);
+extern void haiku_visualize_frame (struct frame *);
+extern void haiku_unvisualize_frame (struct frame *);
+extern void haiku_set_offset (struct frame *, int, int, int);
+extern void haiku_set_frame_visible_invisible (struct frame *, bool);
+extern void haiku_free_frame_resources (struct frame *f);
+extern void haiku_scroll_bar_remove (struct scroll_bar *bar);
+extern void haiku_clear_under_internal_border (struct frame *f);
+extern void haiku_set_name (struct frame *f, Lisp_Object name, bool explicit_p);
+
+extern struct haiku_display_info *haiku_term_init (void);
+
+extern void mark_haiku_display (void);
+
+extern int haiku_get_color (const char *name, Emacs_Color *color);
+extern void haiku_set_background_color (struct frame *f, Lisp_Object arg, Lisp_Object oldval);
+extern void haiku_set_cursor_color (struct frame *f, Lisp_Object arg, Lisp_Object oldval);
+extern void haiku_set_cursor_type (struct frame *f, Lisp_Object arg, Lisp_Object oldval);
+extern void haiku_set_internal_border_width (struct frame *f, Lisp_Object arg, Lisp_Object oldval);
+extern void haiku_change_tab_bar_height (struct frame *f, int height);
+extern void haiku_change_tool_bar_height (struct frame *f, int height);
+
+extern void haiku_query_color (uint32_t col, Emacs_Color *color);
+
+extern unsigned long haiku_get_pixel (haiku bitmap, int x, int y);
+extern void haiku_put_pixel (haiku bitmap, int x, int y, unsigned long pixel);
+
+extern Lisp_Object haiku_menu_show (struct frame *f, int x, int y, int menu_flags,
+				    Lisp_Object title, const char **error_name);
+extern Lisp_Object haiku_popup_dialog (struct frame *f, Lisp_Object header, Lisp_Object contents);
+extern void initialize_frame_menubar (struct frame *f);
+
+extern void run_menu_bar_help_event (struct frame *f, int mb_idx);
+extern void put_xrm_resource (Lisp_Object name, Lisp_Object val);
+
+#ifdef HAVE_FREETYPE
+extern void syms_of_ftbefont (void);
+#endif
+
+#ifdef HAVE_NATIVE_IMAGE_API
+extern bool haiku_can_use_native_image_api (Lisp_Object type);
+extern int haiku_load_image (struct frame *f, struct image *img,
+			     Lisp_Object spec_file, Lisp_Object spec_data);
+extern void syms_of_haikuimage (void);
+#endif
+#endif /* _HAIKU_TERM_H_ */
diff --git a/src/image.c b/src/image.c
index 206c7baa2f..f34a307983 100644
--- a/src/image.c
+++ b/src/image.c
@@ -20,6 +20,7 @@ Copyright (C) 1989, 1992-2021 Free Software Foundation, Inc.
 #include <config.h>
 
 #include <fcntl.h>
+#include <math.h>
 #include <unistd.h>
 
 /* Include this before including <setjmp.h> to work around bugs with
@@ -135,6 +136,27 @@ #define PIX_MASK_DRAW	1
 # define COLOR_TABLE_SUPPORT 1
 #endif
 
+#ifdef HAVE_HAIKU
+#include "haiku_support.h"
+typedef struct haiku_bitmap_record Bitmap_Record;
+
+#define GET_PIXEL(ximg, x, y) haiku_get_pixel (ximg, x, y)
+#define PUT_PIXEL haiku_put_pixel
+#define NO_PIXMAP 0
+
+#define PIX_MASK_RETAIN	0
+#define PIX_MASK_DRAW	1
+
+#define RGB_TO_ULONG(r, g, b) (((r) << 16) | ((g) << 8) | (b))
+#define RED_FROM_ULONG(color)	(((color) >> 16) & 0xff)
+#define GREEN_FROM_ULONG(color)	(((color) >> 8) & 0xff)
+#define BLUE_FROM_ULONG(color)	((color) & 0xff)
+#define RED16_FROM_ULONG(color)		(RED_FROM_ULONG (color) * 0x101)
+#define GREEN16_FROM_ULONG(color)	(GREEN_FROM_ULONG (color) * 0x101)
+#define BLUE16_FROM_ULONG(color)	(BLUE_FROM_ULONG (color) * 0x101)
+
+#endif
+
 static void image_disable_image (struct frame *, struct image *);
 static void image_edge_detection (struct frame *, struct image *, Lisp_Object,
                                   Lisp_Object);
@@ -430,6 +452,11 @@ image_create_bitmap_from_data (struct frame *f, char *bits,
       return -1;
 #endif
 
+#ifdef HAVE_HAIKU
+  void *bitmap = BBitmap_new (width, height, 1);
+  BBitmap_import_mono_bits (bitmap, bits, width, height);
+#endif
+
   id = image_allocate_bitmap_record (f);
 
 #ifdef HAVE_NS
@@ -437,6 +464,11 @@ image_create_bitmap_from_data (struct frame *f, char *bits,
   dpyinfo->bitmaps[id - 1].depth = 1;
 #endif
 
+#ifdef HAVE_HAIKU
+  dpyinfo->bitmaps[id - 1].img = bitmap;
+  dpyinfo->bitmaps[id - 1].depth = 1;
+#endif
+
   dpyinfo->bitmaps[id - 1].file = NULL;
   dpyinfo->bitmaps[id - 1].height = height;
   dpyinfo->bitmaps[id - 1].width = width;
@@ -465,7 +497,7 @@ image_create_bitmap_from_data (struct frame *f, char *bits,
 ptrdiff_t
 image_create_bitmap_from_file (struct frame *f, Lisp_Object file)
 {
-#ifdef HAVE_NTGUI
+#if defined (HAVE_NTGUI) || defined (HAVE_HAIKU)
   return -1;  /* W32_TODO : bitmap support */
 #else
   Display_Info *dpyinfo = FRAME_DISPLAY_INFO (f);
@@ -561,6 +593,10 @@ free_bitmap_record (Display_Info *dpyinfo, Bitmap_Record *bm)
   ns_release_object (bm->img);
 #endif
 
+#ifdef HAVE_HAIKU
+  BBitmap_free (bm->img);
+#endif
+
   if (bm->file)
     {
       xfree (bm->file);
@@ -1834,6 +1870,11 @@ image_size_in_bytes (struct image *img)
   if (img->mask)
     size += w32_image_size (img->mask);
 
+#elif defined HAVE_HAIKU
+  if (img->pixmap)
+    size += BBitmap_bytes_length (img->pixmap);
+  if (img->mask)
+    size += BBitmap_bytes_length (img->mask);
 #endif
 
   return size;
@@ -2173,6 +2214,7 @@ compute_image_size (size_t width, size_t height,
    single step, but the maths for each element is much more complex
    and performing the steps separately makes for more readable code.  */
 
+#ifndef HAVE_HAIKU
 typedef double matrix3x3[3][3];
 
 static void
@@ -2187,6 +2229,7 @@ matrix3x3_mult (matrix3x3 a, matrix3x3 b, matrix3x3 result)
 	result[i][j] = sum;
       }
 }
+#endif /* not HAVE_HAIKU */
 
 static void
 compute_image_rotation (struct image *img, double *rotation)
@@ -2244,6 +2287,7 @@ image_set_transform (struct frame *f, struct image *img)
   double rotation = 0.0;
   compute_image_rotation (img, &rotation);
 
+#ifndef HAVE_HAIKU
 # if defined USE_CAIRO || defined HAVE_XRENDER || defined HAVE_NS
   /* We want scale up operations to use a nearest neighbor filter to
      show real pixels instead of munging them, but scale down
@@ -2414,6 +2458,34 @@ image_set_transform (struct frame *f, struct image *img)
   img->xform.eDx  = matrix[2][0];
   img->xform.eDy  = matrix[2][1];
 # endif
+#else
+  if (rotation != 0 &&
+      rotation != 90 &&
+      rotation != 180 &&
+      rotation != 270 &&
+      rotation != 360)
+    {
+      image_error ("No native support for rotation by %g degrees",
+		   make_float (rotation));
+      return;
+    }
+
+  rotation = fmod (rotation, 360.0);
+
+  if (rotation == 90 || rotation == 270)
+    {
+      int w = width;
+      width = height;
+      height = w;
+    }
+
+  img->have_be_transforms_p = rotation != 0 || (img->width != width) || (img->height != height);
+  img->be_rotate = rotation;
+  img->be_scale_x = 1.0 / (img->width / (double) width);
+  img->be_scale_y = 1.0 / (img->height / (double) height);
+  img->width = width;
+  img->height = height;
+#endif /* not HAVE_HAIKU */
 }
 
 #endif /* HAVE_IMAGEMAGICK || HAVE_NATIVE_TRANSFORMS */
@@ -2820,6 +2892,29 @@ image_create_x_image_and_pixmap_1 (struct frame *f, int width, int height, int d
   return 1;
 #endif /* HAVE_X_WINDOWS */
 
+#ifdef HAVE_HAIKU
+  if (depth == 0)
+    depth = 24;
+
+  if (depth != 24)
+    {
+      image_error ("Invalid image bit depth specified");
+      return 0;
+    }
+
+  *pixmap = BBitmap_new (width, height, depth == 1);
+
+  if (*pixmap == NO_PIXMAP)
+    {
+      *pimg = NULL;
+      image_error ("Unable to create pixmap", Qnil, Qnil);
+      return 0;
+    }
+
+  *pimg = *pixmap;
+  return 1;
+#endif
+
 #ifdef HAVE_NTGUI
 
   BITMAPINFOHEADER *header;
@@ -2960,7 +3055,7 @@ image_destroy_x_image (Emacs_Pix_Container pimg)
 gui_put_x_image (struct frame *f, Emacs_Pix_Container pimg,
                  Emacs_Pixmap pixmap, int width, int height)
 {
-#ifdef USE_CAIRO
+#if defined USE_CAIRO || defined HAVE_HAIKU
   eassert (pimg == pixmap);
 #elif defined HAVE_X_WINDOWS
   GC gc;
@@ -3087,7 +3182,7 @@ image_unget_x_image_or_dc (struct image *img, bool mask_p,
 static Emacs_Pix_Container
 image_get_x_image (struct frame *f, struct image *img, bool mask_p)
 {
-#ifdef USE_CAIRO
+#if defined USE_CAIRO || defined (HAVE_HAIKU)
   return !mask_p ? img->pixmap : img->mask;
 #elif defined HAVE_X_WINDOWS
   XImage *ximg_in_img = !mask_p ? img->ximg : img->mask_img;
@@ -4016,19 +4111,26 @@ xbm_load (struct frame *f, struct image *img)
  ***********************************************************************/
 
 #ifdef HAVE_XPM
-#ifdef HAVE_NTGUI
+/* XpmBe needs this too, despite the name of the define implying
+   Windows. */
+#if defined (HAVE_NTGUI) || defined (HAVE_HAIKU)
 /* Indicate to xpm.h that we don't have Xlib.  */
 #define FOR_MSW
 /* simx.h in xpm defines XColor and XImage differently than Emacs.  */
 /* It also defines Display the same way as Emacs, but gcc 3.3 still barfs.  */
+#ifndef HAVE_HAIKU
 #define XColor xpm_XColor
 #define XImage xpm_XImage
 #define Display xpm_Display
+#endif
+
 #ifdef CYGWIN
 #include "noX/xpm.h"
-#else  /* not CYGWIN */
+#elif defined (HAVE_NTGUI)
 #include "X11/xpm.h"
-#endif	/* not CYGWIN */
+#else
+#include "xpm_be.h" /* In this case we use xpm_be.h for libXpmBe */
+#endif	/* not CYGWIN and not HAVE_NTGUI*/
 #undef FOR_MSW
 #undef XColor
 #undef XImage
@@ -4352,8 +4454,10 @@ x_create_bitmap_from_xpm_data (struct frame *f, const char **bits)
 
   memset (&attrs, 0, sizeof attrs);
 
+#ifndef HAVE_HAIKU
   attrs.visual = FRAME_X_VISUAL (f);
   attrs.colormap = FRAME_X_COLORMAP (f);
+#endif
   attrs.valuemask |= XpmVisual;
   attrs.valuemask |= XpmColormap;
 
@@ -4365,8 +4469,13 @@ x_create_bitmap_from_xpm_data (struct frame *f, const char **bits)
   xpm_init_color_cache (f, &attrs);
 #endif
 
+#ifndef HAVE_HAIKU
   rc = XpmCreatePixmapFromData (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f),
 				(char **) bits, &bitmap, &mask, &attrs);
+#else
+  rc = XpmCreateImageFromData (NULL, (char **) bits, &bitmap, &mask, &attrs);
+#endif
+
   if (rc != XpmSuccess)
     {
       XpmFreeAttributes (&attrs);
@@ -4412,13 +4521,19 @@ xpm_load (struct frame *f, struct image *img)
   xpm_XImage * xpm_image = NULL, * xpm_mask = NULL;
 #endif /* HAVE_NTGUI */
 
+#ifdef HAVE_HAIKU
+  XImage *xpm_image = NULL, *xpm_mask = NULL;
+#endif /* HAVE_HAIKU */
+
   /* Configure the XPM lib.  Use the visual of frame F.  Allocate
      close colors.  Return colors allocated.  */
   memset (&attrs, 0, sizeof attrs);
 
 #ifndef HAVE_NTGUI
+#ifndef HAVE_HAIKU
   attrs.visual = FRAME_X_VISUAL (f);
   attrs.colormap = FRAME_X_COLORMAP (f);
+#endif
   attrs.valuemask |= XpmVisual;
   attrs.valuemask |= XpmColormap;
 #endif /* HAVE_NTGUI */
@@ -4535,9 +4650,14 @@ xpm_load (struct frame *f, struct image *img)
 			       &xpm_image, &xpm_mask,
 			       &attrs);
 #else
+#ifndef HAVE_HAIKU
       rc = XpmReadFileToImage (FRAME_X_DISPLAY (f), SSDATA (file),
 			       &img->ximg, &img->mask_img,
 			       &attrs);
+#else
+      rc = XpmReadFileToImage (NULL, SSDATA (file),
+			       &xpm_image, &xpm_mask, &attrs);
+#endif /* HAVE_HAIKU */
 #endif /* HAVE_NTGUI */
     }
   else
@@ -4559,9 +4679,14 @@ xpm_load (struct frame *f, struct image *img)
 				     &xpm_image, &xpm_mask,
 				     &attrs);
 #else
+#ifndef HAVE_HAIKU
       rc = XpmCreateImageFromBuffer (FRAME_X_DISPLAY (f), SSDATA (buffer),
 				     &img->ximg, &img->mask_img,
 				     &attrs);
+#else
+      rc = XpmCreateImageFromBuffer (NULL, SSDATA (buffer),
+				     &xpm_image, &xpm_mask, &attrs);
+#endif /* HAVE_HAIKU */
 #endif /* HAVE_NTGUI */
     }
 
@@ -4610,7 +4735,7 @@ xpm_load (struct frame *f, struct image *img)
 #else /* not ALLOC_XPM_COLORS */
       int i;
 
-#ifdef HAVE_NTGUI
+#if defined (HAVE_NTGUI) || defined (HAVE_HAIKU)
       /* W32 XPM uses XImage to wrap what W32 Emacs calls a Pixmap,
 	 plus some duplicate attributes.  */
       if (xpm_image && xpm_image->bitmap)
@@ -4622,6 +4747,7 @@ xpm_load (struct frame *f, struct image *img)
 	}
       if (xpm_mask && xpm_mask->bitmap)
 	{
+#ifndef HAVE_HAIKU
 	  /* The mask appears to be inverted compared with what we expect.
 	     TODO: invert our expectations.  See other places where we
 	     have to invert bits because our idea of masks is backwards.  */
@@ -4630,14 +4756,17 @@ xpm_load (struct frame *f, struct image *img)
 
 	  PatBlt (hdc, 0, 0, xpm_mask->width, xpm_mask->height, DSTINVERT);
 	  SelectObject (hdc, old_obj);
-
+#endif /* not HAVE_HAIKU */
 	  img->mask = xpm_mask->bitmap;
 	  XImageFree (xpm_mask);
+#ifndef HAVE_HAIKU
 	  DeleteDC (hdc);
+#endif /* not HAVE_HAIKU */
 	}
-
+#ifndef HAVE_HAIKU
       DeleteDC (hdc);
-#endif /* HAVE_NTGUI */
+#endif /* not HAVE_HAIKU */
+#endif /* HAVE_NTGUI or HAVE_HAIKU */
 
       /* Remember allocated colors.  */
       img->colors = xnmalloc (attrs.nalloc_pixels, sizeof *img->colors);
@@ -5446,7 +5575,7 @@ lookup_rgb_color (struct frame *f, int r, int g, int b)
 {
 #ifdef HAVE_NTGUI
   return PALETTERGB (r >> 8, g >> 8, b >> 8);
-#elif defined USE_CAIRO || defined HAVE_NS
+#elif defined USE_CAIRO || defined HAVE_NS || defined HAVE_HAIKU
   return RGB_TO_ULONG (r >> 8, g >> 8, b >> 8);
 #else
   xsignal1 (Qfile_error,
@@ -5519,7 +5648,7 @@ image_to_emacs_colors (struct frame *f, struct image *img, bool rgb_p)
   p = colors;
   for (y = 0; y < img->height; ++y)
     {
-#if !defined USE_CAIRO && !defined HAVE_NS
+#if !defined USE_CAIRO && !defined HAVE_NS && !defined HAVE_HAIKU
       Emacs_Color *row = p;
       for (x = 0; x < img->width; ++x, ++p)
 	p->pixel = GET_PIXEL (ximg, x, y);
@@ -5841,6 +5970,7 @@ image_disable_image (struct frame *f, struct image *img)
     {
 #ifndef HAVE_NTGUI
 #ifndef HAVE_NS  /* TODO: NS support, however this not needed for toolbars */
+#ifndef HAVE_HAIKU
 
 #ifndef USE_CAIRO
 #define CrossForeground(f) BLACK_PIX_DEFAULT (f)
@@ -5858,6 +5988,7 @@ #define MaskForeground(f)  PIX_MASK_DRAW
       if (img->mask)
 	image_pixmap_draw_cross (f, img->mask, 0, 0, img->width, img->height,
 				 MaskForeground (f));
+#endif /* !HAVE_HAIKU */
 #endif /* !HAVE_NS */
 #else
       HDC hdc, bmpdc;
@@ -6415,6 +6546,8 @@ image_can_use_native_api (Lisp_Object type)
   return w32_can_use_native_image_api (type);
 # elif defined HAVE_NS
   return ns_can_use_native_image_api (type);
+# elif defined HAVE_HAIKU
+  return haiku_can_use_native_image_api (type);
 # else
   return false;
 # endif
@@ -6489,6 +6622,9 @@ native_image_load (struct frame *f, struct image *img)
 # elif defined HAVE_NS
   return ns_load_image (f, img, image_file,
                         image_spec_value (img->spec, QCdata, NULL));
+# elif defined HAVE_HAIKU
+  return haiku_load_image (f, img, image_file,
+			   image_spec_value (img->spec, QCdata, NULL));
 # else
   return 0;
 # endif
@@ -9339,7 +9475,8 @@ imagemagick_load_image (struct frame *f, struct image *img,
 
   init_color_table ();
 
-#if defined (HAVE_MAGICKEXPORTIMAGEPIXELS) && ! defined (HAVE_NS)
+#if defined (HAVE_MAGICKEXPORTIMAGEPIXELS) && \
+  ! defined (HAVE_NS) && ! defined (HAVE_HAIKU)
   if (imagemagick_render_type != 0)
     {
       /* Magicexportimage is normally faster than pixelpushing.  This
@@ -10628,7 +10765,8 @@ DEFUN ("image-transforms-p", Fimage_transforms_p, Simage_transforms_p, 0, 1, 0,
   if (FRAME_WINDOW_P (f))
     {
 #ifdef HAVE_NATIVE_TRANSFORMS
-# if defined HAVE_IMAGEMAGICK || defined (USE_CAIRO) || defined (HAVE_NS)
+# if defined HAVE_IMAGEMAGICK || defined (USE_CAIRO) || defined (HAVE_NS) \
+  || defined (HAVE_HAIKU)
       return list2 (Qscale, Qrotate90);
 # elif defined (HAVE_X_WINDOWS) && defined (HAVE_XRENDER)
       int event_basep, error_basep;
diff --git a/src/keyboard.c b/src/keyboard.c
index 63bf29a948..4ef9ef13d1 100644
--- a/src/keyboard.c
+++ b/src/keyboard.c
@@ -3874,7 +3874,7 @@ kbd_buffer_get_event (KBOARD **kbp,
       /* One way or another, wait until input is available; then, if
 	 interrupt handlers have not read it, read it now.  */
 
-#ifdef USABLE_SIGIO
+#if defined (USABLE_SIGIO) || defined (USABLE_SIGPOLL)
       gobble_input ();
 #endif
       if (kbd_fetch_ptr != kbd_store_ptr)
@@ -7177,7 +7177,7 @@ tty_read_avail_input (struct terminal *terminal,
 static void
 handle_async_input (void)
 {
-#ifdef USABLE_SIGIO
+#if defined (USABLE_SIGIO) || defined (USABLE_SIGPOLL)
   while (1)
     {
       int nread = gobble_input ();
@@ -7240,7 +7240,7 @@ totally_unblock_input (void)
   unblock_input_to (0);
 }
 
-#ifdef USABLE_SIGIO
+#if defined (USABLE_SIGIO) || defined (USABLE_SIGPOLL)
 
 void
 handle_input_available_signal (int sig)
@@ -7256,7 +7256,7 @@ deliver_input_available_signal (int sig)
 {
   deliver_process_signal (sig, handle_input_available_signal);
 }
-#endif /* USABLE_SIGIO */
+#endif /* defined (USABLE_SIGIO) || defined (USABLE_SIGPOLL)  */
 
 \f
 /* User signal events.  */
@@ -7326,7 +7326,7 @@ handle_user_signal (int sig)
           }
 
 	p->npending++;
-#ifdef USABLE_SIGIO
+#if defined (USABLE_SIGIO) || defined (USABLE_SIGPOLL)
 	if (interrupt_input)
 	  handle_input_available_signal (sig);
 	else
@@ -11091,7 +11091,7 @@ DEFUN ("set-input-interrupt-mode", Fset_input_interrupt_mode,
   (Lisp_Object interrupt)
 {
   bool new_interrupt_input;
-#ifdef USABLE_SIGIO
+#if defined (USABLE_SIGIO) || defined (USABLE_SIGPOLL)
 #ifdef HAVE_X_WINDOWS
   if (x_display_list != NULL)
     {
@@ -11102,9 +11102,9 @@ DEFUN ("set-input-interrupt-mode", Fset_input_interrupt_mode,
   else
 #endif /* HAVE_X_WINDOWS */
     new_interrupt_input = !NILP (interrupt);
-#else /* not USABLE_SIGIO */
+#else /* not USABLE_SIGIO || USABLE_SIGPOLL */
   new_interrupt_input = false;
-#endif /* not USABLE_SIGIO */
+#endif /* not USABLE_SIGIO || USABLE_SIGPOLL */
 
   if (new_interrupt_input != interrupt_input)
     {
@@ -11531,12 +11531,16 @@ init_keyboard (void)
       sigaction (SIGQUIT, &action, 0);
 #endif /* not DOS_NT */
     }
-#ifdef USABLE_SIGIO
+#if defined (USABLE_SIGIO) || defined (USABLE_SIGPOLL)
   if (!noninteractive)
     {
       struct sigaction action;
       emacs_sigaction_init (&action, deliver_input_available_signal);
+#ifdef USABLE_SIGIO
       sigaction (SIGIO, &action, 0);
+#else
+      sigaction (SIGPOLL, &action, 0);
+#endif
     }
 #endif
 
diff --git a/src/lisp.h b/src/lisp.h
index 7bfc69b647..d93bafad89 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -138,7 +138,12 @@ verify (BITS_WORD_MAX >> (BITS_PER_BITS_WORD - 1) == 1);
    buffers and strings.  Emacs never allocates objects larger than
    PTRDIFF_MAX bytes, as they cause problems with pointer subtraction.
    In C99, pD can always be "t"; configure it here for the sake of
-   pre-C99 libraries such as glibc 2.0 and Solaris 8.  */
+   pre-C99 libraries such as glibc 2.0 and Solaris 8.
+
+   On Haiku, the size of ptrdiff_t is inconsistent with the value of
+   PTRDIFF_MAX.  In that case, "t" should be sufficient. */
+
+#ifndef HAIKU
 #if PTRDIFF_MAX == INT_MAX
 # define pD ""
 #elif PTRDIFF_MAX == LONG_MAX
@@ -148,6 +153,9 @@ verify (BITS_WORD_MAX >> (BITS_PER_BITS_WORD - 1) == 1);
 #else
 # define pD "t"
 #endif
+#else
+# define pD "t"
+#endif
 
 /* Convenience macro for rarely-used functions that do not return.  */
 #define AVOID _Noreturn ATTRIBUTE_COLD void
@@ -3316,7 +3324,7 @@ rarely_quit (unsigned short int count)
 
 /* Define if the windowing system provides a menu bar.  */
 #if defined (USE_X_TOOLKIT) || defined (HAVE_NTGUI) \
-  || defined (HAVE_NS) || defined (USE_GTK)
+  || defined (HAVE_NS) || defined (USE_GTK) || defined (HAVE_HAIKU)
 #define HAVE_EXT_MENU_BAR true
 #endif
 
@@ -4411,7 +4419,7 @@ fast_string_match_ignore_case (Lisp_Object regexp, Lisp_Object string)
 extern Lisp_Object tab_bar_items (Lisp_Object, int *);
 extern Lisp_Object tool_bar_items (Lisp_Object, int *);
 extern void discard_mouse_events (void);
-#ifdef USABLE_SIGIO
+#if defined (USABLE_SIGIO) || defined (USABLE_SIGPOLL)
 void handle_input_available_signal (int);
 #endif
 extern Lisp_Object pending_funcalls;
diff --git a/src/menu.c b/src/menu.c
index 1aafa78c3c..a829e697bf 100644
--- a/src/menu.c
+++ b/src/menu.c
@@ -50,7 +50,8 @@ Copyright (C) 1986, 1988, 1993-1994, 1996, 1999-2021 Free Software
 static bool
 have_boxes (void)
 {
-#if defined (USE_X_TOOLKIT) || defined (USE_GTK) || defined (HAVE_NTGUI) || defined(HAVE_NS)
+#if defined (USE_X_TOOLKIT) || defined (USE_GTK) || defined (HAVE_NTGUI) || defined (HAVE_NS) \
+  || defined (HAVE_HAIKU)
   if (FRAME_WINDOW_P (XFRAME (Vmenu_updating_frame)))
     return 1;
 #endif
@@ -169,7 +170,7 @@ ensure_menu_items (int items)
     }
 }
 
-#ifdef HAVE_EXT_MENU_BAR
+#if defined (HAVE_EXT_MENU_BAR) || defined (HAVE_HAIKU)
 
 /* Begin a submenu.  */
 
@@ -422,7 +423,8 @@ single_menu_item (Lisp_Object key, Lisp_Object item, Lisp_Object dummy, void *sk
 		  AREF (item_properties, ITEM_PROPERTY_SELECTED),
 		  AREF (item_properties, ITEM_PROPERTY_HELP));
 
-#if defined (USE_X_TOOLKIT) || defined (USE_GTK) || defined (HAVE_NS) || defined (HAVE_NTGUI)
+#if defined (USE_X_TOOLKIT) || defined (USE_GTK) || defined (HAVE_NS) \
+  || defined (HAVE_NTGUI) || defined (HAVE_HAIKU)
   /* Display a submenu using the toolkit.  */
   if (FRAME_WINDOW_P (XFRAME (Vmenu_updating_frame))
       && ! (NILP (map) || NILP (enabled)))
@@ -872,6 +874,9 @@ update_submenu_strings (widget_value *first_wv)
     }
 }
 
+#endif /* USE_X_TOOLKIT || USE_GTK || HAVE_NS || HAVE_NTGUI */
+#ifdef HAVE_WINDOW_SYSTEM
+
 /* Find the menu selection and store it in the keyboard buffer.
    F is the frame the menu is on.
    MENU_BAR_ITEMS_USED is the length of VECTOR.
@@ -959,8 +964,7 @@ find_and_call_menu_selection (struct frame *f, int menu_bar_items_used,
   SAFE_FREE ();
 }
 
-#endif /* USE_X_TOOLKIT || USE_GTK || HAVE_NS || HAVE_NTGUI */
-
+#endif /* HAVE_WINDOW_SYSTEM */
 #ifdef HAVE_NS
 /* As above, but return the menu selection instead of storing in kb buffer.
    If KEYMAPS, return full prefixes to selection. */
diff --git a/src/process.c b/src/process.c
index bfca165fca..901fd5e32e 100644
--- a/src/process.c
+++ b/src/process.c
@@ -258,7 +258,7 @@ #define READ_OUTPUT_DELAY_MAX_MAX   (READ_OUTPUT_DELAY_INCREMENT * 7)
 
 static void start_process_unwind (Lisp_Object);
 static void create_process (Lisp_Object, char **, Lisp_Object);
-#ifdef USABLE_SIGIO
+#if defined (USABLE_SIGIO) || defined (USABLE_SIGPOLL)
 static bool keyboard_bit_set (fd_set *);
 #endif
 static void deactivate_process (Lisp_Object);
@@ -5700,7 +5700,7 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd,
       if (! NILP (wait_for_cell) && ! NILP (XCAR (wait_for_cell)))
 	break;
 
-#ifdef USABLE_SIGIO
+#if defined (USABLE_SIGIO) || defined (USABLE_SIGPOLL)
       /* If we think we have keyboard input waiting, but didn't get SIGIO,
 	 go read it.  This can happen with X on BSD after logging out.
 	 In that case, there really is no input and no SIGIO,
@@ -5708,7 +5708,11 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd,
 
       if (read_kbd && interrupt_input
 	  && keyboard_bit_set (&Available) && ! noninteractive)
+#ifdef USABLE_SIGIO
 	handle_input_available_signal (SIGIO);
+#else
+	handle_input_available_signal (SIGPOLL);
+#endif
 #endif
 
       /* If checking input just got us a size-change event from X,
@@ -7702,7 +7706,7 @@ delete_gpm_wait_descriptor (int desc)
 
 # endif
 
-# ifdef USABLE_SIGIO
+#if defined (USABLE_SIGIO) || defined (USABLE_SIGPOLL)
 
 /* Return true if *MASK has a bit set
    that corresponds to one of the keyboard input descriptors.  */
diff --git a/src/sound.c b/src/sound.c
index 9041076bdc..d42bc8550d 100644
--- a/src/sound.c
+++ b/src/sound.c
@@ -299,11 +299,15 @@ sound_perror (const char *msg)
   int saved_errno = errno;
 
   turn_on_atimers (1);
-#ifdef USABLE_SIGIO
+#if defined (USABLE_SIGIO) || defined (USABLE_SIGPOLL)
   {
     sigset_t unblocked;
     sigemptyset (&unblocked);
+#ifdef USABLE_SIGIO
     sigaddset (&unblocked, SIGIO);
+#else
+    sigaddset (&unblocked, SIGPOLL);
+#endif
     pthread_sigmask (SIG_UNBLOCK, &unblocked, 0);
   }
 #endif
@@ -698,7 +702,7 @@ vox_open (struct sound_device *sd)
 vox_configure (struct sound_device *sd)
 {
   int val;
-#ifdef USABLE_SIGIO
+#if defined (USABLE_SIGIO) || defined (USABLE_SIGPOLL)
   sigset_t oldset, blocked;
 #endif
 
@@ -708,9 +712,13 @@ vox_configure (struct sound_device *sd)
      interrupted by a signal.  Block the ones we know to cause
      troubles.  */
   turn_on_atimers (0);
-#ifdef USABLE_SIGIO
+#if defined (USABLE_SIGIO) || defined (USABLE_SIGPOLL)
   sigemptyset (&blocked);
+#ifdef USABLE_SIGIO
   sigaddset (&blocked, SIGIO);
+#else
+  sigaddset (&blocked, SIGPOLL);
+#endif
   pthread_sigmask (SIG_BLOCK, &blocked, &oldset);
 #endif
 
@@ -744,7 +752,7 @@ vox_configure (struct sound_device *sd)
     }
 
   turn_on_atimers (1);
-#ifdef USABLE_SIGIO
+#if defined (USABLE_SIGIO) || defined (USABLE_SIGPOLL)
   pthread_sigmask (SIG_SETMASK, &oldset, 0);
 #endif
 }
@@ -760,10 +768,14 @@ vox_close (struct sound_device *sd)
       /* On GNU/Linux, it seems that the device driver doesn't like to
 	 be interrupted by a signal.  Block the ones we know to cause
 	 troubles.  */
-#ifdef USABLE_SIGIO
+#if defined (USABLE_SIGIO) || defined (USABLE_SIGPOLL)
       sigset_t blocked, oldset;
       sigemptyset (&blocked);
+#ifdef USABLE_SIGIO
       sigaddset (&blocked, SIGIO);
+#else
+      sigaddset (&blocked, SIGPOLL);
+#endif
       pthread_sigmask (SIG_BLOCK, &blocked, &oldset);
 #endif
       turn_on_atimers (0);
@@ -772,7 +784,7 @@ vox_close (struct sound_device *sd)
       ioctl (sd->fd, SNDCTL_DSP_SYNC, NULL);
 
       turn_on_atimers (1);
-#ifdef USABLE_SIGIO
+#if defined (USABLE_SIGIO) || defined (USABLE_SIGPOLL)
       pthread_sigmask (SIG_SETMASK, &oldset, 0);
 #endif
 
diff --git a/src/sysdep.c b/src/sysdep.c
index 8eaee22498..df077f1698 100644
--- a/src/sysdep.c
+++ b/src/sysdep.c
@@ -71,6 +71,10 @@
 # include <math.h>
 #endif
 
+#ifdef HAIKU
+#include <kernel/OS.h>
+#endif
+
 #ifdef HAVE_SOCKETS
 #include <sys/socket.h>
 #include <netdb.h>
@@ -678,6 +682,9 @@ sys_subshell (void)
 #ifdef USABLE_SIGIO
   saved_handlers[3].code = SIGIO;
   saved_handlers[4].code = 0;
+#elif defined (USABLE_SIGPOLL)
+  saved_handlers[3].code = SIGPOLL;
+  saved_handlers[4].code = 0;
 #else
   saved_handlers[3].code = 0;
 #endif
@@ -788,6 +795,7 @@ init_sigio (int fd)
 }
 
 #ifndef DOS_NT
+#ifdef F_SETOWN
 static void
 reset_sigio (int fd)
 {
@@ -795,12 +803,13 @@ reset_sigio (int fd)
   fcntl (fd, F_SETFL, old_fcntl_flags[fd]);
 #endif
 }
+#endif /* F_SETOWN */
 #endif
 
 void
 request_sigio (void)
 {
-#ifdef USABLE_SIGIO
+#if defined (USABLE_SIGIO) || defined (USABLE_SIGPOLL)
   sigset_t unblocked;
 
   if (noninteractive)
@@ -810,7 +819,11 @@ request_sigio (void)
 # ifdef SIGWINCH
   sigaddset (&unblocked, SIGWINCH);
 # endif
+# ifdef USABLE_SIGIO
   sigaddset (&unblocked, SIGIO);
+# else
+  sigaddset (&unblocked, SIGPOLL);
+# endif
   pthread_sigmask (SIG_UNBLOCK, &unblocked, 0);
 
   interrupts_deferred = 0;
@@ -820,7 +833,7 @@ request_sigio (void)
 void
 unrequest_sigio (void)
 {
-#ifdef USABLE_SIGIO
+#if defined (USABLE_SIGIO) || defined (USABLE_SIGPOLL)
   sigset_t blocked;
 
   if (noninteractive)
@@ -830,7 +843,11 @@ unrequest_sigio (void)
 # ifdef SIGWINCH
   sigaddset (&blocked, SIGWINCH);
 # endif
+# ifdef USABLE_SIGIO
   sigaddset (&blocked, SIGIO);
+# else
+  sigaddset (&blocked, SIGPOLL);
+# endif
   pthread_sigmask (SIG_BLOCK, &blocked, 0);
   interrupts_deferred = 1;
 #endif
@@ -1256,9 +1273,12 @@ init_sys_modes (struct tty_display_info *tty_out)
   /* This code added to insure that, if flow-control is not to be used,
      we have an unlocked terminal at the start. */
 
+#ifndef HAIKU /* On Haiku, TCXONC is a no-op and causes spurious
+		 compiler warnings. */
 #ifdef TCXONC
   if (!tty_out->flow_control) ioctl (fileno (tty_out->input), TCXONC, 1);
 #endif
+#endif /* HAIKU */
 #ifdef TIOCSTART
   if (!tty_out->flow_control) ioctl (fileno (tty_out->input), TIOCSTART, 0);
 #endif
@@ -1674,6 +1694,8 @@ emacs_sigaction_init (struct sigaction *action, signal_handler_t handler)
       sigaddset (&action->sa_mask, SIGQUIT);
 #ifdef USABLE_SIGIO
       sigaddset (&action->sa_mask, SIGIO);
+#elif defined (USABLE_SIGPOLL)
+      sigaddset (&action->sa_mask, SIGPOLL);
 #endif
     }
 
@@ -2772,6 +2794,7 @@ cfsetspeed (struct termios *termios_p, speed_t vitesse)
 #ifdef B150
     { 150, B150 },
 #endif
+#ifndef HAVE_TINY_SPEED_T
 #ifdef B200
     { 200, B200 },
 #endif
@@ -2859,6 +2882,7 @@ cfsetspeed (struct termios *termios_p, speed_t vitesse)
 #ifdef B4000000
     { 4000000, B4000000 },
 #endif
+#endif /* HAVE_TINY_SPEED_T */
   };
 
 /* Convert a numerical speed (e.g., 9600) to a Bnnn constant (e.g.,
@@ -3119,6 +3143,23 @@ list_system_processes (void)
   return  proclist;
 }
 
+#elif defined HAIKU
+
+Lisp_Object
+list_system_processes (void)
+{
+  team_info info;
+  int32 cookie = 0;
+  Lisp_Object lval = Qnil;
+
+  while (get_next_team_info (&cookie, &info) == B_OK)
+    {
+      lval = Fcons (make_fixnum (info.team), lval);
+    }
+
+  return lval;
+}
+
 /* The WINDOWSNT implementation is in w32.c.
    The MSDOS implementation is in dosfns.c.  */
 #elif !defined (WINDOWSNT) && !defined (MSDOS)
@@ -4199,6 +4240,87 @@ system_process_attributes (Lisp_Object pid)
   return attrs;
 }
 
+#elif defined (HAIKU)
+
+Lisp_Object
+system_process_attributes (Lisp_Object pid)
+{
+  CHECK_FIXNUM (pid);
+
+  team_info info;
+  Lisp_Object lval = Qnil;
+  thread_info inf;
+  area_info area;
+  team_id id = (team_id) XFIXNUM (pid);
+  struct passwd *g;
+  size_t mem = 0;
+
+  if (get_team_info (id, &info) != B_OK)
+    return Qnil;
+
+  bigtime_t everything = 0, vsample = 0;
+  bigtime_t cpu_eaten = 0, esample = 0;
+
+  lval = Fcons (Fcons (Qeuid, make_fixnum (info.uid)), lval);
+  lval = Fcons (Fcons (Qegid, make_fixnum (info.gid)), lval);
+  lval = Fcons (Fcons (Qthcount, make_fixnum (info.thread_count)), lval);
+  lval = Fcons (Fcons (Qcomm, build_string_from_utf8 (info.args)), lval);
+
+  g = getpwuid (info.uid);
+
+  if (g && g->pw_name)
+    lval = Fcons (Fcons (Quser, build_string (g->pw_name)), lval);
+
+  /* FIXME: Calculating this makes Emacs show up as using 100% CPU! */
+
+  for (int32 team_cookie = 0;
+       get_next_team_info (&team_cookie, &info) == B_OK;)
+    for (int32 thread_cookie = 0;
+	 get_next_thread_info (info.team, &thread_cookie, &inf) == B_OK;)
+      {
+	if (inf.team == id && strncmp (inf.name, "idle thread ", 12))
+	  cpu_eaten += inf.user_time + inf.user_time;
+	everything += inf.user_time + inf.user_time;
+      }
+
+  sleep (0.05);
+
+  for (int32 team_cookie = 0;
+       get_next_team_info (&team_cookie, &info) == B_OK;)
+    for (int32 thread_cookie = 0;
+	 get_next_thread_info (info.team, &thread_cookie, &inf) == B_OK;)
+      {
+	if (inf.team == id && strncmp (inf.name, "idle thread ", 12))
+	  esample += inf.user_time + inf.user_time;
+	vsample += inf.user_time + inf.user_time;
+      }
+
+  cpu_eaten = esample - cpu_eaten;
+  everything = vsample - everything;
+
+  if (everything)
+    lval = Fcons (Fcons (Qpcpu, make_float (((double) (cpu_eaten) /
+					     (double) (everything)) * 100)),
+		  lval);
+  else
+    lval = Fcons (Fcons (Qpcpu, make_float (0.0)), lval);
+
+  for (ssize_t area_cookie = 0;
+       get_next_area_info (id, &area_cookie, &area) == B_OK;)
+    mem += area.ram_size;
+
+  system_info sinfo;
+  get_system_info (&sinfo);
+  int64 max = (int64) sinfo.max_pages * B_PAGE_SIZE;
+
+  lval = Fcons (Fcons (Qpmem, make_float (((double) mem /
+					   (double) max) * 100)),
+		lval);
+  lval = Fcons (Fcons (Qrss, make_fixnum (mem / 1024)), lval);
+
+  return lval;
+}
+
 /* The WINDOWSNT implementation is in w32.c.
    The MSDOS implementation is in dosfns.c.  */
 #elif !defined (WINDOWSNT) && !defined (MSDOS)
diff --git a/src/termhooks.h b/src/termhooks.h
index 1d3cdc8fe8..3d6bfd89d6 100644
--- a/src/termhooks.h
+++ b/src/termhooks.h
@@ -60,7 +60,8 @@ #define EMACS_TERMHOOKS_H
   output_x_window,
   output_msdos_raw,
   output_w32,
-  output_ns
+  output_ns,
+  output_haiku
 };
 
 /* Input queue declarations and hooks.  */
@@ -442,6 +443,7 @@ #define EVENT_INIT(event) memset (&(event), 0, sizeof (struct input_event))
     struct x_display_info *x;         /* xterm.h */
     struct w32_display_info *w32;     /* w32term.h */
     struct ns_display_info *ns;       /* nsterm.h */
+    struct haiku_display_info *haiku; /* haikuterm.h */
   } display_info;
 
 \f
@@ -830,6 +832,9 @@ #define TERMINAL_FONT_CACHE(t)						\
 #elif defined (HAVE_NS)
 #define TERMINAL_FONT_CACHE(t)						\
   (t->type == output_ns ? t->display_info.ns->name_list_element : Qnil)
+#elif defined (HAVE_HAIKU)
+#define TERMINAL_FONT_CACHE(t)						\
+  (t->type == output_haiku ? t->display_info.haiku->name_list_element : Qnil)
 #endif
 
 extern struct terminal *decode_live_terminal (Lisp_Object);
diff --git a/src/terminal.c b/src/terminal.c
index b83adc596b..b5f244ee31 100644
--- a/src/terminal.c
+++ b/src/terminal.c
@@ -445,6 +445,8 @@ DEFUN ("terminal-live-p", Fterminal_live_p, Sterminal_live_p, 1, 1, 0,
       return Qpc;
     case output_ns:
       return Qns;
+    case output_haiku:
+      return Qhaiku;
     default:
       emacs_abort ();
     }
diff --git a/src/verbose.mk.in b/src/verbose.mk.in
index 50d6ea3200..b03596e9b8 100644
--- a/src/verbose.mk.in
+++ b/src/verbose.mk.in
@@ -23,7 +23,9 @@ ifeq (${V},1)
 AM_V_AR =
 AM_V_at =
 AM_V_CC =
+AM_V_CXX =
 AM_V_CCLD =
+AM_V_CXXLD =
 AM_V_ELC =
 AM_V_GEN =
 AM_V_GLOBALS =
@@ -33,7 +35,9 @@ else
 AM_V_AR = @echo "  AR      " $@;
 AM_V_at = @
 AM_V_CC = @echo "  CC      " $@;
+AM_V_CXX = @echo "  CXX     " $@;
 AM_V_CCLD = @echo "  CCLD    " $@;
+AM_V_CXXLD = @echo "  CXXLD   " $@;
 ifeq ($(HAVE_NATIVE_COMP),yes)
 ifeq ($(NATIVE_DISABLED),1)
 AM_V_ELC = @echo "  ELC     " $@;
diff --git a/src/xdisp.c b/src/xdisp.c
index 45c7090fc0..ba7dbd9c0f 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -15613,6 +15613,11 @@ redisplay_internal (void)
     }
 #endif
 
+#if defined (HAVE_HAIKU)
+  if (popup_activated_p)
+    return;
+#endif
+
   /* I don't think this happens but let's be paranoid.  */
   if (redisplaying_p)
     return;
@@ -25197,6 +25202,11 @@ display_menu_bar (struct window *w)
     return;
 #endif /* HAVE_NS */
 
+#ifdef HAVE_HAIKU
+  if (FRAME_HAIKU_P (f))
+    return;
+#endif /* HAVE_HAIKU */
+
 #if defined (USE_X_TOOLKIT) || defined (USE_GTK)
   eassert (!FRAME_WINDOW_P (f));
   init_iterator (&it, w, -1, -1, f->desired_matrix->rows, MENU_FACE_ID);
@@ -33519,6 +33529,11 @@ note_mouse_highlight (struct frame *f, int x, int y)
     return;
 #endif
 
+#if defined (HAVE_HAIKU)
+  if (popup_activated_p)
+    return;
+#endif
+
   if (!f->glyphs_initialized_p
       || f->pointer_invisible)
     return;
diff --git a/src/xfaces.c b/src/xfaces.c
index 2273fb4fe4..d788fdb11a 100644
--- a/src/xfaces.c
+++ b/src/xfaces.c
@@ -246,6 +246,10 @@ #define GCGraphicsExposures 0
 #ifdef HAVE_NS
 #define GCGraphicsExposures 0
 #endif /* HAVE_NS */
+
+#ifdef HAVE_HAIKU
+#define GCGraphicsExposures 0
+#endif /* HAVE_HAIKU */
 #endif /* HAVE_WINDOW_SYSTEM */
 
 #include "buffer.h"
@@ -555,8 +559,8 @@ x_free_gc (struct frame *f, Emacs_GC *gc)
 
 #endif  /* HAVE_NTGUI */
 
-#ifdef HAVE_NS
-/* NS emulation of GCs */
+#if defined (HAVE_NS) || defined (HAVE_HAIKU)
+/* NS and Haiku emulation of GCs */
 
 static Emacs_GC *
 x_create_gc (struct frame *f,
diff --git a/src/xfns.c b/src/xfns.c
index 0d0335c299..74a1918cf7 100644
--- a/src/xfns.c
+++ b/src/xfns.c
@@ -4416,7 +4416,8 @@ DEFUN ("x-server-version", Fx_server_version, Sx_server_version, 0, 1, 0,
 Protocol used on TERMINAL and the 3rd number is the distributor-specific
 release number.  For MS Windows, the 3 numbers report the OS major and
 minor version and build number.  For Nextstep, the first 2 numbers are
-hard-coded and the 3rd represents the OS version.
+hard-coded and the 3rd represents the OS version.  For Haiku, all 3
+numbers are hard-coded.
 
 See also the function `x-server-vendor'.
 
@@ -7374,7 +7375,7 @@ DEFUN ("x-file-dialog", Fx_file_dialog, Sx_file_dialog, 2, 5, 0,
 selection box, if specified.  If MUSTMATCH is non-nil, the returned file
 or directory must exist.
 
-This function is defined only on NS, MS Windows, and X Windows with the
+This function is defined only on NS, Haiku, MS Windows, and X Windows with the
 Motif or Gtk toolkits.  With the Motif toolkit, ONLY-DIR-P is ignored.
 Otherwise, if ONLY-DIR-P is non-nil, the user can select only directories.
 On MS Windows 7 and later, the file selection dialog "remembers" the last
@@ -8014,7 +8015,7 @@ syms_of_xfns (void)
   x_gtk_use_system_tooltips = true;
 
   DEFVAR_LISP ("x-gtk-resize-child-frames", x_gtk_resize_child_frames,
-    doc: /* If non-nil, resize child frames specially with GTK builds.
+    doc: /* If non-nil,1 resize child frames specially with GTK builds.
 If this is nil, resize child frames like any other frames.  This is the
 default and usually works with most desktops.  Some desktop environments
 (GNOME shell in particular when using the mutter window manager),
diff --git a/src/xterm.c b/src/xterm.c
index 2c56c73068..f2b9f1f9ce 100644
--- a/src/xterm.c
+++ b/src/xterm.c
@@ -13692,7 +13692,7 @@ syms_of_xterm (void)
 A value of nil means Emacs doesn't use toolkit scroll bars.
 With the X Window system, the value is a symbol describing the
 X toolkit.  Possible values are: gtk, motif, xaw, or xaw3d.
-With MS Windows or Nextstep, the value is t.  */);
+With MS Windows, Haiku windowing or Nextstep, the value is t.  */);
 #ifdef USE_TOOLKIT_SCROLL_BARS
 #ifdef USE_MOTIF
   Vx_toolkit_scroll_bars = intern_c_string ("motif");

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: Haiku port try 2
  2021-09-13  2:26 ` Haiku port try 2 Po Lu via Emacs development discussions.
@ 2021-09-13 18:35   ` Alan Third
  2021-09-14  0:46     ` Po Lu
  2021-09-14 15:05     ` Alan Third
  0 siblings, 2 replies; 7+ messages in thread
From: Alan Third @ 2021-09-13 18:35 UTC (permalink / raw)
  To: Po Lu; +Cc: emacs-devel

On Mon, Sep 13, 2021 at 10:26:34AM +0800, Po Lu via Emacs development discussions. wrote:
> 
> I'm also not sure Emacs with this patch will still build on a few
> platforms I can't test, namely macOS and MS-Windows.  It would be nice
> for someone with a Macintosh to verify that Emacs still builds with this
> patch in place, and inform me of any issues so I can rectify them.

I'll try giving it a go tomorrow, if I remember.

> The patch also includes a copy of the X Pixmap Library (which is free
> software) with modifications made by me, as the libXpm shipped by Haiku
> has a hard dependency on a running X server.

You might want to have a look at how the NS port deals with XPMs, see
image.c:4714.
-- 
Alan Third



^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Haiku port try 2
  2021-09-13 18:35   ` Alan Third
@ 2021-09-14  0:46     ` Po Lu
  2021-09-14  8:19       ` Alan Third
  2021-09-14 15:05     ` Alan Third
  1 sibling, 1 reply; 7+ messages in thread
From: Po Lu @ 2021-09-14  0:46 UTC (permalink / raw)
  To: Alan Third; +Cc: emacs-devel

Alan Third <alan@idiocy.org> writes:

> You might want to have a look at how the NS port deals with XPMs, see
> image.c:4714.

Thanks.  I don't see anything notable at that location, or around it, so
could you please explain in more detail?  It would be nice to use Haiku
libXpm instead of shipping a modified copy.




^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Haiku port try 2
  2021-09-14  0:46     ` Po Lu
@ 2021-09-14  8:19       ` Alan Third
  2021-09-14  8:55         ` Po Lu
  0 siblings, 1 reply; 7+ messages in thread
From: Alan Third @ 2021-09-14  8:19 UTC (permalink / raw)
  To: Po Lu; +Cc: emacs-devel

On Tue, Sep 14, 2021 at 08:46:51AM +0800, Po Lu wrote:
> Alan Third <alan@idiocy.org> writes:
> 
> > You might want to have a look at how the NS port deals with XPMs, see
> > image.c:4714.
> 
> Thanks.  I don't see anything notable at that location, or around it, so
> could you please explain in more detail?  It would be nice to use Haiku
> libXpm instead of shipping a modified copy.

Basically Macs don't come with libXpm, so there is code in image.c to
support xpm's directly. If libXpm is available then I believe it will
use it, but it means it's not a requirement.

Search for this comment:

/* XPM support functions for NS where libxpm is not available, and for
   Cairo.  Only XPM version 3 (without any extensions) is supported.  */

-- 
Alan Third



^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Haiku port try 2
  2021-09-14  8:19       ` Alan Third
@ 2021-09-14  8:55         ` Po Lu
  0 siblings, 0 replies; 7+ messages in thread
From: Po Lu @ 2021-09-14  8:55 UTC (permalink / raw)
  To: Alan Third; +Cc: emacs-devel

Alan Third <alan@idiocy.org> writes:

> Search for this comment:
>
> /* XPM support functions for NS where libxpm is not available, and for
>    Cairo.  Only XPM version 3 (without any extensions) is supported.  */

Thanks!  That does make it quite a lot easier.



^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Haiku port try 2
  2021-09-13 18:35   ` Alan Third
  2021-09-14  0:46     ` Po Lu
@ 2021-09-14 15:05     ` Alan Third
  2021-09-15  1:17       ` Po Lu
  1 sibling, 1 reply; 7+ messages in thread
From: Alan Third @ 2021-09-14 15:05 UTC (permalink / raw)
  To: Po Lu, emacs-devel

On Mon, Sep 13, 2021 at 07:35:31PM +0100, Alan Third wrote:
> On Mon, Sep 13, 2021 at 10:26:34AM +0800, Po Lu via Emacs development discussions. wrote:
> > 
> > I'm also not sure Emacs with this patch will still build on a few
> > platforms I can't test, namely macOS and MS-Windows.  It would be nice
> > for someone with a Macintosh to verify that Emacs still builds with this
> > patch in place, and inform me of any issues so I can rectify them.
> 
> I'll try giving it a go tomorrow, if I remember.

Builds fine on the Mac.
-- 
Alan Third



^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Haiku port try 2
  2021-09-14 15:05     ` Alan Third
@ 2021-09-15  1:17       ` Po Lu
  0 siblings, 0 replies; 7+ messages in thread
From: Po Lu @ 2021-09-15  1:17 UTC (permalink / raw)
  To: Alan Third; +Cc: emacs-devel

Alan Third <alan@idiocy.org> writes:

> Builds fine on the Mac.

Thanks!  Can anyone try on MSW?



^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2021-09-15  1:17 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <87ee9tjjmt.fsf.ref@yahoo.com>
2021-09-13  2:26 ` Haiku port try 2 Po Lu via Emacs development discussions.
2021-09-13 18:35   ` Alan Third
2021-09-14  0:46     ` Po Lu
2021-09-14  8:19       ` Alan Third
2021-09-14  8:55         ` Po Lu
2021-09-14 15:05     ` Alan Third
2021-09-15  1:17       ` Po Lu

Code repositories for project(s) associated with this external index

	https://git.savannah.gnu.org/cgit/emacs.git
	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.