Subject: bug#51658: [PATCH] Haiku port (again)

From: Po Lu <[email-masked]>
Date: Thu, 11 Nov 2021 15:40:01 +0800

Eli Zaretskii <[email-masked]> writes:

>> From: Po Lu <[email-masked]>
>> Cc: [email-masked]
>> Date: Thu, 11 Nov 2021 08:27:18 +0800
>> 
>> The haikufont backend will use whatever shaping engine is used by the
>> App Server (the window server on Haiku).  Right now, it can't do any
>> fancy shaping, but the Haiku developers plan to add HarfBuzz in the
>> future.
>> 
>> Once that is done, then the haikufont backend will support HarfBuzz
>> without any further work.
>> 
>> > I hope you will reconsider.  Having a niche platform with 3 font
>> > backends is really too much.  Especially since Emacs 29 learned to
>> > display Emoji now, and we are talking about adding decent support for
>> > ligatures, both of which require a shaping engine.
>> 
>> OK, so WDYT about keeping haikufont and Cairo support?  Please let me
>> know, and I'll update the patch.  Thanks in advance!
>
> SGTM, thanks.

Thanks.

Here is the updated commit message; the patch is attached.

Add support for the Haiku operating system and its window system 

* .gitignore: Add binaries specific to Haiku.

* Makefie.in (HAVE_BE_APP): New variable.
(install-arch-dep): Install Emacs and Emacs.pdmp when using Haiku.

* configure.ac: Detect and configure for Haiku and various related
 configurations.
(be-app, be-freetype, be-cairo): New options.
(HAVE_BE_APP, HAIKU_OBJ, HAIKU_CXX_OBJ)
(HAIKU_LIBS, HAIKU_CFLAGS): New variables.
(HAIKU, HAVE_TINY_SPEED_T): New define.
(emacs_config_features): Add BE_APP.

* doc/emacs/Makefile.in (EMACSSOURCES): Add Haiku appendix.

* doc/emacs/emacs.texi: Add Haiku appendix to menus and
include it.

* doc/emacs/haiku.texi: New Haiku appendix.

* doc/lispref/display.texi (Defining Faces, Window Systems): Explain
haiku as a window system identifier.
(haiku-use-system-tooltips): Explain meaning of system tooltips on
Haiku.

* doc/lispref/frames.texi (Multiple Terminals): Explain meaning of
haiku as a display type.
(Frame Layout): Clarify section for Haiku frames.
(Size Parameters): Explain limitations of fullwidth and fullheight
on Haiku.
(Management Parameters): Explain limitations of inhibiting double
buffering on builds with Cairo, and the inability of frames with
no-accept-focus to receive keyboard input on Haiku.
(Font and Color Parameters): Explain the different font backends
available on Haiku.
(Raising and Lowering): Explain that lowering and restacking frames
doesn't work on Haiku.
(Child Frames): Explain oddities of child frame visibility on Haiku.

* doc/lispref/os.texi (System Environment): Explain meaning of haiku
.
* etc/MACHINES: Add appropriate notices for Haiku.
* etc/NEWS: Document changes.
* etc/PROBLEMS: Document font spacing bug on Haiku.

* lib-src/Makefile.in: Build be-resources binary on Haiku.
(CXX, CXXFLAGS, NON_CXX_FLAGS, ALL_CXXFLAGS)
(HAVE_BE_APP, HAIKU_LIBS, HAIKU_CFLAGS): New variables.
(DONT_INSTALL): Add be-resources binary if on Haiku.
(be-resources): New target.

* lib-src/be_resources: Add helper binary for setting resources on
the Emacs application.

* lib-src/emacsclient.c (decode_options): Set alt_display to "be"
on Haiku.

* lisp/cus-edit.el (custom-button, custom-button-mouse)
(custom-button-unraised, custom-button-pressed): Update face
definitions for Haiku.

* lisp/cus-start.el: Add haiku-debug-on-fatal-error and
haiku-use-system-tooltips.

* lisp/faces.el (face-valid-attribute-values): Clarify attribute
comment for Haiku.
(tool-bar): Add appropriate toolbar color for Haiku.

* lisp/frame.el (haiku-frame-geometry)
(haiku-mouse-absolute-pixel-position)
(haiku-set-mouse-absolute-pixel-position)
(haiku-frame-edges)
(haiku-frame-list-z-order): New function declarations.

(frame-geometry, frame-edges)
(mouse-absolute-pixel-position)
(set-mouse-absolute-pixel-position)
(frame-list-z-order): Call appropriate window system functions
on Haiku.

(display-mouse-p, display-graphic-p)
(display-images-p, display-pixel-height)
(display-pixel-width, display-mm-height)
(display-mm-width, display-backing-store)
(display-save-under, display-planes)
(display-color-cells, display-visual-class): Update type tests
for Haiku.

* lisp/international/mule-cmds.el (set-coding-system-map): Also
prevent set-terminal-coding-system from appearing in the menu bar
on Haiku.

* lisp/loadup.el: Load Haiku-specific files when built with Haiku,
and don't rename newly built Emacs on Haiku as BFS doesn't support
hard links.

* lisp/menu-bar.el (menu-bar-open): Add for Haiku.

* lisp/mwheel.el (mouse-wheel-down-event): Expect wheel-up on Haiku.
(mouse-wheel-up-event): Expect wheel-down on Haiku.
(mouse-wheel-left-event): Expect wheel-left on Haiku.
(mouse-wheel-right-event): Expect wheel-right on Haiku.

* lisp/net/browse-url.el (browse-url--browser-defcustom-type):
Add option for WebPositive.
(browse-url-webpositive-program): New variable.
(browse-url-default-program): Search for WebPositive.
(browse-url-webpositive): New function.

* lisp/net/eww.el (eww-form-submit, eww-form-file)
(eww-form-checkbox, eww-form-select): Define faces appropriately
for Haiku.

* lisp/term/haiku-win.el: New file.

* lisp/tooltip.el (menu-or-popup-active-p): New function declaration.
(tooltip-show-help): Don't use tooltips on Haiku when a menu is active.

* lisp/version.el (haiku-get-version-string): New function declaration.
(emacs-version): Add Haiku version string if appropriate.

* src/Makefile.in: Also produce binary named "Emacs" with Haiku resources
set.

(CXX, HAIKU_OBJ, HAIKU_CXX_OBJ, HAIKU_LIBS)
(HAIKU_CFLAGS, HAVE_BE_APP, NON_CXX_FLAGS, ALL_CXX_FLAGS): New
variables.

(.SUFFIXES): Add .cc.
(.cc.o): New target.
(base_obj): Add Haiku C objects.
(doc_obj, obj): Split objects that should scanned for documentation
into doc_obj.
(SOME_MACHINE_OBJECTS): Add appropriate Haiku C objects.
(all): Depend on Emacs and Emacs.pdmp on Haiku.
(LIBES): Add Haiku libraries.
(gl-stamp)
($(etc)/DOC): Scan doc_obj instead of obj
(temacs$(EXEEXT): Use C++ linker on Haiku.
(ctagsfiles3): New variable.
(TAGS): Scan C++ files.

* src/alloc.c (garbage_collect): Mark Haiku display.

* src/dispextern.h (HAVE_NATIVE_TRANSFORMS): Also enable on Haiku.
(struct image): Add fields for Haiku transforms.
(RGB_PIXEL_COLOR): Define to unsigned long on Haiku as well.
(sit_for): Also check USABLE_SIGPOLL.
(init_display_interactive): Set initial window system to Haiku on
Haiku builds.

* src/emacs.c (main): Define Haiku syms and init haiku clipboard.
(shut_down_emacs): Quit BApplication on Haiku and trigger debug
on aborts if haiku_debug_on_fatal_error.
(Vsystem_type): Update docstring.

* src/fileio.c (next-read-file-uses-dialog-p): Enable on Haiku.

* src/filelock.c (WTMP_FILE): Only define if BOOT_TIME is also
defined.

* src/floatfns.c (double_integer_scale): Work around Haiku libroot
brain damage.

* src/font.c (syms_of_font): Define appropriate font driver symbols
for Haiku builds with various options.

* src/font.h: Also enable ftcrfont on Haiku builds with Cairo.
(font_data_structures_may_be_ill_formed): Also enable on Haiku
builds that have FreeType or Cairo.

* src/frame.c (Fframep): Update doc-string for Haiku builds and
return haiku if appropriate.
(syms_of_frame): New symbol `haiku'.

* src/frame.h (struct frame): Add output data for Haiku.
(FRAME_HAIKU_P): New macro.
(FRAME_WINDOW_P): Test for Haiku frames as well.

* src/ftcrfont.c (RED_FROM_ULONG, GREEN_FROM_ULONG)
(BLUE_FROM_ULONG): New macros.
(ftcrfont_draw): Add haiku specific code for Haiku builds with Cairo.

* src/ftfont.c (ftfont_open): Set face.
(ftfont_has_char, ftfont_text_extents): Work around crash.
(syms_of_ftfont): New symbol `mono'.

* src/ftfont.h (struct font_info): Enable Cairo-specific fields for Cairo
builds on Haiku.

* src/haiku_draw_support.cc:
* src/haiku_font_support.cc:
* src/haiku_io.c:
* src/haiku_select.cc:
* src/haiku_support.cc:
* src/haiku_support.h:
* src/haikufns.c:
* src/haikufont.c:
* src/haikugui.h:
* src/haikuimage.c:
* src/haikumenu.c:
* src/haikuselect.c:
* src/haikuselect.h:
* src/haikuterm.c:
* src/haikuterm.h: Add new files for Haiku windowing support.

* src/image.c: Implement image transforms and native XPM support
on Haiku.

(GET_PIXEL, PUT_PIXEL, NO_PIXMAP)
(PIX_MASK_RETAIN, PIX_MASK_DRAW)
(RGB_TO_ULONG, RED_FROM_ULONG, GREEN_FROM_ULONG)
(BLUE_FROM_ULONG, RED16_FROM_ULONG, GREEN16_FROM_ULONG)
(BLUE16_FROM_ULONG): Define to appropriate values on Haiku.

(image_create_bitmap_from_data): Add Haiku support.
(image_create_bitmap_from_file): Add TODO on Haiku.
(free_bitmap_record): Free bitmap on Haiku.
(image_size_in_bytes): Implement for Haiku bitmaps.
(image_set_transform): Implement on Haiku.
(image_create_x_image_and_pixmap_1): Implement on Haiku, 24-bit or
1-bit only.
(image_destroy_x_image, image_get_x_image): Use correct img and
pixmap values on Haiku.
(lookup_rgb_color): Use correct macro on Haiku.
(image_to_emacs_colors): Implement on Haiku.
(image_disable_image): Disable on Haiku.
(image_can_use_native_api): Test for translator presence on Haiku.
(native_image_load): Use translator on Haiku.
(imagemagick_load_image): Add Haiku-specific quirks.
(Fimage_transforms_p): Allow rotate90 on Haiku.
(image_types): Enable native XPM support on Haiku.
(syms_of_image): Enable XPM images on Haiku.

* src/keyboard.c (kbd_buffer_get_event)
(handle_async_input, handle_input_available_signal)
(handle_user_signal, Fset_input_interrupt_mode)
(init_keyboard): Check for USABLE_SIGPOLL along with USABLE_SIGIO.

(make_lispy_event, kbd_buffer_get_event)
(syms_of_keyboard): Handle special Haiku events.

* src/lisp.h (pD): Work around broken Haiku headers.
(HAVE_EXT_MENU_BAR): Define on Haiku.
(handle_input_available_signal): Enable if we just have SIGPOLL
as well.

* src/menu.c (have_boxes): Return true on Haiku.
(single_menu_item): Enable toolkit menus on Haiku.
(find_and_call_menu_selection): Also enable on Haiku.

* src/process.c (keyboard_bit_set): Enable with only usable SIGPOLL.
(wait_reading_process_output): Test for SIGPOLL as well as SIGIO
availability.

* src/sound.c (sound_perror, vox_open)
(vox_configure, vox_close): Enable for usable SIGPOLL as well.

* src/sysdep.c (sys_subshell): Enable for usable SIGPOLL.
(reset_sigio): Make conditional on F_SETOWN.
(request_sigio, unrequest_sigio)
(emacs_sigaction_init): Also handle SIGPOLLs.
(init_sys_modes): Disable TCXONC usage on Haiku, as it doesn't
have any ttys other than pseudo ttys, which don't support C-s/C-q
flow control.
(speeds): Disable high speeds if HAVE_TINY_SPEED_T.
(list_system_processes, system_process_attributes): Implement for
Haiku.

* src/termhooks.h (enum output_method): Add output_haiku.
(enum event_kind): Add HAIKU_REFS_FOUND_EVENT and
HAIKU_QUIT_REQUESTED_EVENT.
(struct terminal): Add Haiku display info.
(TERMINAL_FONT_CACHE): Enable for Haiku.

* src/terminal.c (Fterminal_live_p): Return `haiku' if appropriate.
* src/verbose.mk.in (AM_V_CXX, AM_V_CXXLD): New logging variables.

* src/xdisp.c (redisplay_internal, note_mouse_highlight): Return on
Haiku if a popup is activated.
(display_menu_bar): Return on Haiku if frame is a Haiku frame.

* src/xfaces.c (GCGraphicsExposures): Enable correctly on Haiku.
(x_create_gc): Enable dummy GC code on Haiku.

* src/xfns.c (x-server-version, x-file-dialog): Add Haiku specifics
to doc strings.

* src/xterm.c (syms_of_xterm): Add Haiku information to doc string.

diff --git a/.gitignore b/.gitignore
index ea1662c9b8..f1abb2ab68 100644
--- a/.gitignore
+++ b/.gitignore
@@ -182,6 +182,7 @@ ID
 # Executables.
 *.exe
 a.out
+lib-src/be-resources
 lib-src/blessmail
 lib-src/ctags
 lib-src/ebrowse
@@ -203,6 +204,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 ccb5d93f2f..3c092fa63d 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.
@@ -521,7 +523,13 @@ 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)${prefix}/apps/Emacs"
+endif
 ifeq (${DUMPING},pdumper)
+ifeq (${HAVE_BE_APP},yes)
+	${INSTALL_DATA} src/Emacs.pdmp "$(DESTDIR)${libexecdir}/emacs/${version}/${configuration}"/Emacs.pdmp
+endif
 	${INSTALL_DATA} src/emacs.pdmp "$(DESTDIR)${libexecdir}/emacs/${version}/${configuration}"/emacs-${EMACS_PDMP}
 endif
 	-chmod 755 "$(DESTDIR)${bindir}/$(EMACSFULL)"
diff --git a/configure.ac b/configure.ac
index 33e7037afe..8a424e59a0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -510,6 +510,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-cairo],
+  [enable use of cairo under Haiku's Application Kit])
+
 ## Makefile.in needs the cache file name.
 AC_SUBST(cache_file)
 
@@ -786,6 +792,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
@@ -907,7 +917,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
@@ -1574,6 +1586,8 @@ AC_DEFUN
 
   ## Motif needs -lgen.
   unixware) LIBS_SYSTEM="-lsocket -lnsl -lelf -lgen" ;;
+
+  haiku) LIBS_SYSTEM="-lnetwork" ;;
 esac
 
 AC_SUBST(LIBS_SYSTEM)
@@ -2079,6 +2093,22 @@ AC_DEFUN
    fi
 fi
 
+HAVE_BE_APP=no
+if test "${opsys}" = "haiku" && test "${with_be_app}" = "yes"; then
+   dnl Only GCC is supported.  Clang might work, but it's
+   dnl not reliable, so don't check for it here.
+   AC_PROG_CXX([gcc g++])
+   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_W32=no
 W32_OBJ=
 W32_LIBS=
@@ -2200,6 +2230,35 @@ 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.
+
+  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.
 
@@ -2239,6 +2298,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
@@ -2570,7 +2632,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"
@@ -2594,7 +2657,8 @@ AC_DEFUN
 HAVE_WEBP=no
 if test "${with_webp}" != "no"; then
    if test "${HAVE_X11}" = "yes" || test "${opsys}" = "mingw32" \
-   || test "${HAVE_W32}" = "yes" || test "${HAVE_NS}" = "yes"; then
+   || test "${HAVE_W32}" = "yes" || test "${HAVE_NS}" = "yes" \
+   || test "${HAVE_BE_APP}" = "yes"; then
       WEBP_REQUIRED=0.6.0
       WEBP_MODULE="libwebp >= $WEBP_REQUIRED"
 
@@ -2613,7 +2677,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
@@ -3263,6 +3328,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
 
@@ -3352,6 +3420,22 @@ AC_DEFUN
     fi
   fi
 fi
+if test "${HAVE_BE_APP}" = "yes"; then
+  if test "${with_be_cairo}" != "no"; then
+    CAIRO_REQUIRED=1.8.0
+    CAIRO_MODULE="cairo >= $CAIRO_REQUIRED"
+    EMACS_CHECK_MODULES(CAIRO, $CAIRO_MODULE)
+    if test $HAVE_CAIRO = yes; then
+      AC_DEFINE(USE_BE_CAIRO, 1, [Define to 1 if using cairo on Haiku.])
+      CFLAGS="$CFLAGS $CAIRO_CFLAGS"
+      LIBS="$LIBS $CAIRO_LIBS"
+      AC_SUBST(CAIRO_CFLAGS)
+      AC_SUBST(CAIRO_LIBS)
+    else
+      AC_MSG_WARN([cairo requested but not found.])
+    fi
+  fi
+fi
 
 ### Start of font-backend (under any platform) section.
 # (nothing here yet -- this is a placeholder)
@@ -3501,6 +3585,68 @@ AC_DEFUN
   fi
 fi
 
+### Start of font-backend (under Haiku) selectionn.
+if test "${HAVE_BE_APP}" = "yes"; then
+  if test $HAVE_CAIRO = "yes"; then
+      EMACS_CHECK_MODULES([FREETYPE], [freetype2 >= 2.5.0])
+      test "$HAVE_FREETYPE" = "no" && AC_MSG_ERROR(cairo on Haiku requires libfreetype)
+      EMACS_CHECK_MODULES([FONTCONFIG], [fontconfig >= 2.2.0])
+      test "$HAVE_FONTCONFIG" = "no" && AC_MSG_ERROR(cairo on Haiku requires libfontconfig)
+  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)
@@ -3622,7 +3768,7 @@ AC_DEFUN
 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],
@@ -3940,7 +4086,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
@@ -4015,7 +4161,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"
@@ -4044,7 +4190,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.
@@ -4461,6 +4608,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])
 
@@ -4797,7 +4951,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
@@ -5052,7 +5206,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
@@ -5148,6 +5302,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

@@ -5369,8 +5531,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)
@@ -5413,6 +5592,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
@@ -5525,6 +5720,17 @@ AC_DEFUN
     FONT_OBJ="$FONT_OBJ ftfont.o"
   fi
 fi
+
+if test "${HAVE_BE_APP}" = "yes" ; then
+  if test "${HAVE_FREETYPE}" = "yes" || \
+     test "${HAVE_CAIRO}" = "yes"; then
+     FONT_OBJ="$FONT_OBJ ftfont.o"
+  fi
+  if test "${HAVE_CAIRO}" = "yes"; then
+    FONT_OBJ="$FONT_OBJ ftcrfont.o"
+  fi
+fi
+
 if test "${HAVE_HARFBUZZ}" = "yes" ; then
   FONT_OBJ="$FONT_OBJ hbfont.o"
 fi
@@ -5913,7 +6119,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 TOOLKIT_SCROLL_BARS \
diff --git a/doc/emacs/Makefile.in b/doc/emacs/Makefile.in
index 69d39efa8b..dde3ae83c1 100644
--- a/doc/emacs/Makefile.in
+++ b/doc/emacs/Makefile.in
@@ -140,6 +140,7 @@ EMACSSOURCES=
 	${srcdir}/xresources.texi \
 	${srcdir}/anti.texi \
 	${srcdir}/macos.texi \
+	$(srcdir)/haiku.texi \
 	${srcdir}/msdos.texi \
 	${srcdir}/gnu.texi \
 	${srcdir}/glossary.texi \
diff --git a/doc/emacs/emacs.texi b/doc/emacs/emacs.texi
index 83847fb8f1..dcc09892b3 100644
--- a/doc/emacs/emacs.texi
+++ b/doc/emacs/emacs.texi
@@ -221,6 +221,7 @@ Top
 * X Resources::         X resources for customizing Emacs.
 * Antinews::            Information about Emacs version 27.
 * Mac OS / GNUstep::     Using Emacs under macOS and GNUstep.
+* Haiku::               Using Emacs on Haiku.
 * Microsoft Windows::   Using Emacs on Microsoft Windows and MS-DOS.
 * Manifesto::           What's GNU?  Gnu's Not Unix!
 
@@ -1249,6 +1250,14 @@ Top
 * Mac / GNUstep Events::        How window system events are handled.
 * GNUstep Support::             Details on status of GNUstep support.
 
+Emacs and Haiku
+
+* Haiku Basics::        Basic Emacs usage and installation under Haiku.
+* Haiku Customization:: Customizations specific to Haiku.
+* Haiku Events::        Specifics of window system event handling under Haiku.
+* Haiku Resources::     The facsimile of an X resource database used on Haiku.
+* Haiku Fonts::         The various options for displaying fonts on Haiku.
+
 Emacs and Microsoft Windows/MS-DOS
 
 * Windows Startup::     How to start Emacs on Windows.
@@ -1618,6 +1627,7 @@ GNU Free Documentation License
 
 @include anti.texi
 @include macos.texi
+@include haiku.texi
 @c Includes msdos-xtra.
 @include msdos.texi
 @include gnu.texi
diff --git a/doc/emacs/haiku.texi b/doc/emacs/haiku.texi
new file mode 100644
index 0000000000..a70c3faeb1
--- /dev/null
+++ b/doc/emacs/haiku.texi
@@ -0,0 +1,216 @@
+@c This is part of the Emacs manual.
+@c Copyright (C) 2021 Free Software Foundation, Inc.
+@c See file emacs.texi for copying conditions.
+@node Haiku
+@appendix Emacs and Haiku
+@cindex Haiku
+
+  Haiku is a Unix-like operating system that originated as a
+re-implementation of the operating system BeOS.  As it is free
+software, this port of GNU Emacs is provided for the convenience of
+its users.
+
+  This section describes the peculiarities of using Emacs built with
+the Application Kit, the windowing system native to Haiku.  The
+oddities described here do not apply to using Emacs on Haiku built
+without windowing support, or built with X11.
+
+@menu
+* Haiku Basics::        Basic Emacs usage and installation under Haiku.
+* Haiku Customization:: Customizations specific to Haiku.
+* Haiku Events::        Specifics of window system event handling under Haiku.
+* Haiku Resources::     The facsimile of an X resource database used on Haiku.
+* Haiku Fonts::         The various options for displaying fonts on Haiku.
+@end menu
+
+@node Haiku Basics
+@section Installation and usage peculiarities under Haiku
+@cindex haiku application
+@cindex haiku installation
+
+  Emacs installs two separate executables under Haiku; it is up to the
+user to decide which one suits him best: A regular executable, with
+the lowercase name @code{emacs}, and a binary containing
+Haiku-specific application metadata, with the name @code{Emacs}.
+
+@cindex launching Emacs from the tracker
+@cindex TTY Emacs in haiku
+  If you are launching Emacs from the Tracker, or want to make the
+Tracker open files using Emacs, you should use the binary named
+@code{Emacs}; If you are going to use Emacs in the terminal, or wish
+to launch separate instances of Emacs, or do not care for the
+aforementioned system integration features, the binary named
+@code{emacs} should be used instead.
+
+@cindex modifier keys and system keymap (Haiku)
+@cindex haiku keymap
+  On Haiku, unusual modifier keys such as the Hyper key are
+unsupported.  By default, the super key corresponds with the option
+key defined by the operating system, the meta key with the command
+key, the control key with the system control key, and the shift key
+with the system shift key.  On a standard PC keyboard, Haiku should
+map these keys to positions familiar to those using a GNU system, but
+this may require some adjustment to your system's configuration to
+work.
+
+  If the system shift key is held down, Emacs will always use the
+system shift keymap regardless of what other modifier keys are
+currently held down.  Otherwise, if any other modifier key is held
+down, the default system keymap will be used to map keys.  This makes
+it impossible to type accented characters using the system super key
+map.
+
+  You can customize the correspondence between modifier keys known to
+the system, and those known to Emacs.  See @ref{Haiku Customization}.
+
+@cindex haiku input methods
+@cindex BeCJK and Emacs
+  Some popular Haiku input methods such BeCJK are known to behave
+badly when interacting with Emacs.  If you are affected, you can use
+an Emacs input method instead.  See @ref{Input Methods}.
+
+@cindex tooltips (haiku)
+@cindex haiku tooltips
+  On Haiku, Emacs defaults to using the system tooltip mechanism.
+This usually leads to more responsive tooltips, but the tooltips will
+not be able to use advanced display features.  If that is what you
+need, you can disable the use of system tooltips by setting the
+variable @code{haiku-use-system-tooltips} to nil. (@pxref{Tooltips,,,
+elisp, The Emacs Lisp Reference Manual}).
+
+@subsection What to do when Emacs crashes
+@cindex haiku debugger
+@vindex haiku-debug-on-fatal-error
+  If the variable @code{haiku-debug-on-fatal-error} is non-nil, Emacs
+will launch the system debugger when a fatal signal is received.  It
+defaults to @code{t}.  If GDB cannot be used on your system, please
+attach the report generated by the system debugger when reporting a
+bug.
+
+@node Haiku Customization
+@section Customizations specific to Haiku
+  This section details several customizations that are specific to
+Haiku windowing.
+
+@table @code
+@vindex haiku-use-system-tooltips
+@item haiku-use-system-tooltips
+This variable controls whether or not system tooltips will be used.
+
+When non-nil, tooltips are displayed by the Application Kit and not
+Emacs, and accordingly do not have a tooltip frame.  As such, they are
+also unable to utilize any display properties.
+
+@vindex haiku-use-system-debugger
+@item haiku-use-system-debugger
+When non-nil, Emacs will ask the system to launch the system debugger
+whenever it experiences a fatal error.  This behaviour is standard
+among Haiku applications.
+@end table
+
+@subsection Modifier keys
+@cindex modifier key customization (Haiku)
+You can customize which Emacs modifiers the various system modifier
+keys correspond to through the following variables:
+
+@table @code
+@vindex haiku-meta-keysym
+@item haiku-meta-keysym
+The system modifier key that will be treated as the Meta key by Emacs.
+It defaults to @code{command}.
+
+@vindex haiku-control-keysym
+@item haiku-control-keysym
+The system modifier key that will be treated as the Control key by
+Emacs.  It defaults to @code{control}.
+
+@vindex haiku-super-keysym
+@item haiku-super-keysym
+The system modifier key that will be treated as the Super key by
+Emacs.  It defaults to @code{option}.
+
+@vindex haiku-shift-keysym
+@item haiku-super-keysym
+The system modifier key that will be treated as the Shift key by
+Emacs.  It defaults to @code{shift}.
+@end table
+
+The value of each variable can one of the symbols @code{command},
+@code{control}, @code{option}, @code{shift}, or @code{nil}.
+@code{nil} or any other value will cause the default value to be used
+instead.
+
+@node Haiku Events
+@section Window system events under Haiku
+@cindex events on Haiku
+
+  On Haiku, Emacs can receive events that have no equivalent under the
+X window system.  These are sent as specially defined key events,
+which do not correspond to any sequence of keystrokes.  Under Emacs,
+these key events can be bound to functions just like ordinary
+keystrokes.  Here is a list of these events.
+
+@table @key
+@item haiku-refs-found
+This event is received whenever the operating system tells Emacs to
+open a file, such as when a file's icon is dropped onto the
+@code{Emacs} binary in the Tracker, or when a file is dropped onto a
+frame.  The event itself is a list, the second item of which is a
+string containing the path of the file to be opened.
+
+For an explanation of the purpose of the @code{Emacs} binary, see
+@ref{Haiku Basics}.
+
+@item haiku-quit-requested
+This event is received whenever the operating system tells Emacs that
+the user has asked Emacs to quit, optionally seeking confirmation if
+the user has any unsaved documents. By default, it is bound to
+@code{save-buffers-kill-emacs}, see @ref{Exiting}.
+@end table
+
+@node Haiku Resources
+@section Using the X defaults database on Haiku
+@cindex X resources, haiku
+@cindex x-get-resource, haiku
+
+  As there is no equivalent to an X resource database on Haiku, a
+facsimile is provided in its place.  This facsimile does not attempt
+to process X resource files, but is responsive to the @kbd{--xrm}
+command-line argument.  For details about using this argument, see
+@ref{Resources}.
+
+@node Haiku Fonts
+@section Font and font backend selection on Haiku
+@cindex font backend selection (Haiku)
+
+  Emacs, when built with Haiku windowing support, can be built with
+several different font backends.  You can specify font backends by
+specifying @kbd{-xrm Emacs.fontBackend:BACKEND} on the command line
+used to invoke Emacs, where @kbd{BACKEND} is one of the backends
+specified below, or on a per-frame basis by changing the
+@code{font-backend} frame parameter. (@pxref{Parameter Access,,,
+elisp, The Emacs Lisp Reference Manual}).
+
+@cindex ftcr font backend
+
+  The @code{ftcr} font backend is available whenever Emacs is built
+with Cairo support.  It is the most fully featured, but will cause
+display issues when used on a frame with double-buffering disabled.
+
+@cindex haiku font backend
+@cindex fonts (Haiku)
+
+  The @code{haiku} font backend is always available.  At present, it
+is incapable of displaying color emoji and advanced compositions
+(though this may change if Haiku gains support for both at some later
+time, independent of Emacs).
+
+  This font backend has a known problem with displaying certain bold
+or italic fonts on older Haiku systems.  It is documented in
+@file{etc/PROBLEMS}, see @ref{Known Problems}.
+
+@cindex HarfBuzz support (Haiku)
+
+  The @code{ftcr} backend features HarfBuzz variants when Emacs is
+built with HarfBuzz support.  The HarfBuzz variant is named
+@code{ftcrhb}.
diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi
index ad1077e0c4..15cbd222dc 100644
--- a/doc/lispref/display.texi
+++ b/doc/lispref/display.texi
@@ -2767,8 +2767,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},
@@ -8196,6 +8197,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
@@ -8242,6 +8245,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
@@ -8250,6 +8254,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 31ad82b7ad..045e919f50 100644
--- a/doc/lispref/frames.texi
+++ b/doc/lispref/frames.texi
@@ -214,7 +214,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}.
@@ -680,7 +681,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
@@ -1756,6 +1757,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
@@ -2168,6 +2172,10 @@ Management Parameters
 available, to reduce flicker.  Set this property if you experience
 display bugs or pine for that retro, flicker-y feeling.
 
+If Emacs is built with Haiku and Cairo, inhibiting double buffering on
+a frame may lead to severe artifacting when display elements such as
+the mouse cursor pass over a frame.
+
 @vindex skip-taskbar@r{, a frame parameter}
 @item skip-taskbar
 If non-@code{nil}, this tells the window manager to remove the frame's
@@ -2191,7 +2199,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
@@ -2352,7 +2361,13 @@ Font and Color Parameters
 engine), and @code{harfbuzz} (font driver for OTF and TTF fonts with
 HarfBuzz text shaping) (@pxref{Windows Fonts,,, emacs, The GNU Emacs
 Manual}).  The @code{harfbuzz} driver is similarly recommended.  On
-other systems, there is only one available font backend, so it does
+Haiku, there are up to two font drivers: @code{haiku} (the server-side
+font driver, always available), and @code{ftcr} (a font-driver using
+Cairo, only available if Emacs was built with Cairo).  For
+@code{ftcr}, there also exists a variant that shapes text using
+HarfBuzz, named @code{ftcrhb}.
+
+On other systems, there is only one available font backend, so it does
 not make sense to modify this frame parameter.
 
 @vindex background-mode@r{, a frame parameter}
@@ -3143,6 +3158,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
@@ -3163,6 +3181,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
@@ -3272,6 +3293,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
+active, 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 1fbd66458a..fb0f25fa3d 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.
+
 
 * Obsolete platforms
 
diff --git a/etc/NEWS b/etc/NEWS
index 78ce3c067f..0ed2fa21f5 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -24,6 +24,25 @@ applies, and please also update docstrings as needed.
 
 * Installation Changes in Emacs 29.1
 
+** 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.
+
++++
+** Cairo drawing support has been enabled for Haiku builds.
+To enable Cairo support, ensure that the Cairo and FreeType development files
+are present on your system, and configure Emacs with '--with-be-cairo'.
+
+---
+** 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.
+
 ** Emacs now installs the ".pdmp" file using a unique fingerprint in the name.
 The file is typically installed using a file name akin to
 "...dir/libexec/emacs/29.1/x86_64-pc-linux-gnu/emacs-<fingerprint>.pdmp".
diff --git a/etc/PROBLEMS b/etc/PROBLEMS
index d8d4cf7a17..2779915b77 100644
--- a/etc/PROBLEMS
+++ b/etc/PROBLEMS
@@ -1022,6 +1022,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..e6a14f037b
--- /dev/null
+++ b/lib-src/be_resources.cc
@@ -0,0 +1,144 @@
+/* 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>
+#include <Roster.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;
+    }
+  code = info.SetAppFlags (B_EXCLUSIVE_LAUNCH | B_ARGV_ONLY);
+  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 0e800dd7e8..c55b29830d 100644
--- a/lib-src/emacsclient.c
+++ b/lib-src/emacsclient.c
@@ -603,6 +603,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 34a6db508d..219b5d68ac 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/cus-start.el b/lisp/cus-start.el
index a46107a678..68019c038e 100644
--- a/lisp/cus-start.el
+++ b/lisp/cus-start.el
@@ -829,7 +829,11 @@ minibuffer-prompt-properties--setter
 	     ;; xselect.c
 	     (x-select-enable-clipboard-manager killing boolean "24.1")
 	     ;; xsettings.c
-	     (font-use-system-font font-selection boolean "23.2")))
+	     (font-use-system-font font-selection boolean "23.2")
+             ;; haikuterm.c
+             (haiku-debug-on-fatal-error debug boolean "29.1")
+             ;; haikufns.c
+             (haiku-use-system-tooltips tooltip boolean "29.1")))
     (setq ;; If we did not specify any standard value expression above,
 	  ;; use the current value as the standard value.
 	  standard (if (setq prop (memq :standard rest))
@@ -846,6 +850,8 @@ minibuffer-prompt-properties--setter
 		       (eq system-type 'windows-nt))
 		      ((string-match "\\`ns-" (symbol-name symbol))
 		       (featurep 'ns))
+                      ((string-match "\\`haiku-" (symbol-name symbol))
+                       (featurep 'haiku))
 		      ((string-match "\\`x-.*gtk" (symbol-name symbol))
 		       (featurep 'gtk))
 		      ((string-match "clipboard-manager" (symbol-name symbol))
diff --git a/lisp/faces.el b/lisp/faces.el
index 9ec20c4298..b2498cda88 100644
--- a/lisp/faces.el
+++ b/lisp/faces.el
@@ -1172,7 +1172,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)
@@ -2822,7 +2822,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 2c73737a54..1319759e74 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 haiku-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.
@@ -2137,7 +2152,7 @@ display-screens
 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-screens display))
      (t
       1))))
@@ -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)
@@ -2312,7 +2327,7 @@ display-visual-class
 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-visual-class display))
      ((and (memq frame-type '(pc t))
 	   (tty-display-color-p display))
diff --git a/lisp/international/mule-cmds.el b/lisp/international/mule-cmds.el
index 089decb83c..b922f192a9 100644
--- a/lisp/international/mule-cmds.el
+++ b/lisp/international/mule-cmds.el
@@ -88,7 +88,7 @@ set-coding-system-map
     (bindings--define-key map [separator-3] menu-bar-separator)
     (bindings--define-key map [set-terminal-coding-system]
       '(menu-item "For Terminal" set-terminal-coding-system
-        :enable (null (memq initial-window-system '(x w32 ns)))
+        :enable (null (memq initial-window-system '(x w32 ns haiku)))
         :help "How to encode terminal output"))
     (bindings--define-key map [set-keyboard-coding-system]
       '(menu-item "For Keyboard" set-keyboard-coding-system
diff --git a/lisp/loadup.el b/lisp/loadup.el
index e8ecb67d56..99723cfac9 100644
--- a/lisp/loadup.el
+++ b/lisp/loadup.el
@@ -302,6 +302,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
@@ -557,6 +564,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 1a81f1a3d0..2dfc946bb6 100644
--- a/lisp/menu-bar.el
+++ b/lisp/menu-bar.el
@@ -2665,9 +2665,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
@@ -2676,6 +2677,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 51410e3ef4..2787d1e452 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 3af37e412d..687bf6c884 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/net/eww.el b/lisp/net/eww.el
index 70ebc1d2ec..1dcb81252a 100644
--- a/lisp/net/eww.el
+++ b/lisp/net/eww.el
@@ -239,7 +239,7 @@ eww-url-transformers
   :version "29.1")
 
 (defface eww-form-submit
-  '((((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 "#808080" :foreground "black"))
   "Face for eww buffer buttons."
@@ -247,7 +247,7 @@ eww-form-submit
   :group 'eww)
 
 (defface eww-form-file
-  '((((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 "#808080" :foreground "black"))
   "Face for eww buffer buttons."
@@ -255,7 +255,7 @@ eww-form-file
   :group 'eww)
 
 (defface eww-form-checkbox
-  '((((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 eww buffer buttons."
@@ -263,7 +263,7 @@ eww-form-checkbox
   :group 'eww)
 
 (defface eww-form-select
-  '((((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 eww buffer buttons."
diff --git a/lisp/term/haiku-win.el b/lisp/term/haiku-win.el
new file mode 100644
index 0000000000..34734d4e2b
--- /dev/null
+++ b/lisp/term/haiku-win.el
@@ -0,0 +1,136 @@
+;;; 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
+                             (mapconcat #'identity (cdr 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")))
+
+(defun haiku-refs-found (event)
+  "Open the path found in EVENT.
+This should be bound to the event `refs-found', which is sent
+when the window-system tells us that the user has told Emacs to
+open a file."
+  (interactive "e")
+  (when (and last-event-frame (eq (framep last-event-frame) 'haiku))
+    (select-frame last-event-frame)
+    (raise-frame last-event-frame))
+  (find-file (cadr event)))
+
+(global-set-key [haiku-refs-found] 'haiku-refs-found)
+(global-set-key [haiku-quit-requested] 'save-buffers-kill-emacs)
+
+(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 4c5535f8ad..db6b603a7f 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@
@@ -343,10 +344,17 @@ 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@
+
 ## 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@)
@@ -364,6 +372,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.,
@@ -379,17 +390,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.
@@ -411,8 +426,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.
@@ -426,7 +443,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@
@@ -452,7 +470,11 @@ 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
 ifeq ($(HAVE_NATIVE_COMP):$(NATIVE_DISABLED),yes:)
 all: ../native-lisp
 endif
@@ -524,7 +546,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,
@@ -581,6 +603,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 \
@@ -599,11 +633,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)
 
@@ -621,7 +655,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 > $@
 
@@ -646,9 +680,15 @@ $(LIBEGNU_ARCHIVE):
 ## to start if Vinstallation_directory has the wrong value.
 temacs$(EXEEXT): $(LIBXMENU) $(ALLOBJS) $(LIBEGNU_ARCHIVE) $(EMACSRES) \
   $(charsets) $(charscript) ${emoji-zwj} $(MAKE_PDUMPER_FINGERPRINT)
-	$(AM_V_CCLD)$(CC) -o $@.tmp \
+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)
@@ -733,6 +773,7 @@ ${ETAGS}:
 # to be built before we can get TAGS.
 ctagsfiles1 = $(filter-out ${srcdir}/macuvs.h, $(wildcard ${srcdir}/*.[hc]))
 ctagsfiles2 = $(wildcard ${srcdir}/*.m)
+ctagsfiles3 = $(wildcard ${srcdir}/*.cc)
 
 ## In out-of-tree builds, TAGS are generated in the build dir, like
 ## other non-bootstrap build products (see Bug#31744).
@@ -747,7 +788,8 @@ TAGS:
 	  $(ctagsfiles1) \
 	  --regex='{objc}/[ 	]*DEFVAR_[A-Z_ 	(]+"\([^"]+\)"/\1/' \
 	  --regex='{objc}/[ 	]*DEFVAR_[A-Z_ 	(]+"[^"]+",[ 	]\([A-Za-z0-9_]+\)/\1/' \
-	  $(ctagsfiles2)
+	  $(ctagsfiles2) \
+	  $(ctagsfiles3)
 
 ## Arrange to make tags tables for ../lisp and ../lwlib,
 ## which the above TAGS file for the C files includes by reference.
diff --git a/src/alloc.c b/src/alloc.c
index aa790d3afa..f8908c91db 100644
--- a/src/alloc.c
+++ b/src/alloc.c
@@ -6149,6 +6149,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 f17f095e0d..a698f6546b 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"
@@ -3011,7 +3018,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
 
@@ -3050,6 +3057,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;
@@ -3489,7 +3504,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 632eec2f03..f3f110a8f2 100644
--- a/src/dispnew.c
+++ b/src/dispnew.c
@@ -6146,7 +6146,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
 
@@ -6453,6 +6453,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 032b27fcf3..63f2a39308 100644
--- a/src/emacs.c
+++ b/src/emacs.c
@@ -109,6 +109,10 @@ #define MAIN_PROGRAM
 #include "getpagesize.h"
 #include "gnutls.h"
 
+#ifdef HAVE_HAIKU
+#include <kernel/OS.h>
+#endif
+
 #ifdef PROFILING
 # include <sys/gmon.h>
 extern void moncontrol (int mode);
@@ -2207,6 +2211,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
@@ -2261,6 +2277,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 ();
@@ -2728,6 +2748,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);
@@ -2737,6 +2760,10 @@ shut_down_emacs (int sig, Lisp_Object stuff)
       if (sig && sig != SIGTERM)
 	{
 	  static char const fmt[] = "Fatal error %d: %n%s\n";
+#ifdef HAVE_HAIKU
+	  if (haiku_debug_on_fatal_error)
+	    debugger ("Fatal error in Emacs");
+#endif
 	  char buf[max ((sizeof fmt - sizeof "%d%n%s\n"
 			 + INT_STRLEN_BOUND (int) + 1),
 			min (PIPE_BUF, MAX_ALLOCA))];
@@ -3229,6 +3256,7 @@ syms_of_emacs (void)
   `ms-dos'       compiled as an MS-DOS application.
   `windows-nt'   compiled as a native W32 application.
   `cygwin'       compiled using the Cygwin library.
+  `haiku'        compiled for a Haiku system.
 Anything else (in Emacs 26, the possibilities are: aix, berkeley-unix,
 hpux, usg-unix-v) indicates some sort of Unix system.  */);
   Vsystem_type = intern_c_string (SYSTEM_TYPE);
diff --git a/src/fileio.c b/src/fileio.c
index 3c13d3fe41..f7a0b31bf0 100644
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -6190,7 +6190,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..2857cf24bb 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 f70054ea40..1200f562f5 100644
--- a/src/font.c
+++ b/src/font.c
@@ -5711,6 +5711,9 @@ syms_of_font (void)
 #ifdef HAVE_NTGUI
   syms_of_w32font ();
 #endif	/* HAVE_NTGUI */
+#ifdef USE_BE_CAIRO
+  syms_of_ftcrfont ();
+#endif
 #endif	/* HAVE_WINDOW_SYSTEM */
 }
 
diff --git a/src/font.h b/src/font.h
index 1da72cca07..1a8f3a8309 100644
--- a/src/font.h
+++ b/src/font.h
@@ -964,7 +964,7 @@ valid_font_driver (struct font_driver const *d)
 extern void syms_of_nsfont (void);
 extern void syms_of_macfont (void);
 #endif	/* HAVE_NS */
-#ifdef USE_CAIRO
+#if defined (USE_CAIRO) || defined (USE_BE_CAIRO)
 extern struct font_driver const ftcrfont_driver;
 #ifdef HAVE_HARFBUZZ
 extern struct font_driver ftcrhbfont_driver;
@@ -998,7 +998,7 @@ #define FONT_DEFERRED_LOG(ACTION, ARG, RESULT)		\
 INLINE bool
 font_data_structures_may_be_ill_formed (void)
 {
-#ifdef USE_CAIRO
+#if defined USE_CAIRO || defined USE_BE_CAIRO
   /* 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 79a7c89e0d..a21dd0d927 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 ();
     }
@@ -6020,6 +6023,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 3dd76805dd..cb2bad71c5 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/ftcrfont.c b/src/ftcrfont.c
index db417b3e77..5d75f18357 100644
--- a/src/ftcrfont.c
+++ b/src/ftcrfont.c
@@ -22,7 +22,13 @@
 #include <cairo-ft.h>
 
 #include "lisp.h"
+#ifdef HAVE_X_WINDOWS
 #include "xterm.h"
+#else /* Otherwise, Haiku */
+#include "haikuterm.h"
+#include "haiku_support.h"
+#include "termchar.h"
+#endif
 #include "blockinput.h"
 #include "charset.h"
 #include "composite.h"
@@ -30,6 +36,12 @@
 #include "ftfont.h"
 #include "pdumper.h"
 
+#ifdef USE_BE_CAIRO
+#define RED_FROM_ULONG(color)	(((color) >> 16) & 0xff)
+#define GREEN_FROM_ULONG(color)	(((color) >> 8) & 0xff)
+#define BLUE_FROM_ULONG(color)	((color) & 0xff)
+#endif
+
 #define METRICS_NCOLS_PER_ROW	(128)
 
 enum metrics_status
@@ -513,11 +525,37 @@ ftcrfont_draw (struct glyph_string *s,
 
   block_input ();
 
+#ifndef USE_BE_CAIRO
   cr = x_begin_cr_clip (f, s->gc);
+#else
+  BView_draw_lock (FRAME_HAIKU_VIEW (f));
+  EmacsWindow_begin_cr_critical_section (FRAME_HAIKU_WINDOW (f));
+  cr = haiku_begin_cr_clip (f, s);
+  if (!cr)
+    {
+      BView_draw_unlock (FRAME_HAIKU_VIEW (f));
+      EmacsWindow_end_cr_critical_section (FRAME_HAIKU_WINDOW (f));
+      unblock_input ();
+      return 0;
+    }
+  BView_cr_dump_clipping (FRAME_HAIKU_VIEW (f), cr);
+#endif
 
   if (with_background)
     {
+#ifndef USE_BE_CAIRO
       x_set_cr_source_with_gc_background (f, s->gc);
+      s->background_filled_p = 1;
+#else
+      struct face *face = s->face;
+
+      uint32_t col = s->hl == DRAW_CURSOR ?
+	FRAME_CURSOR_COLOR (s->f).pixel : face->background;
+
+      cairo_set_source_rgb (cr, RED_FROM_ULONG (col) / 255.0,
+			    GREEN_FROM_ULONG (col) / 255.0,
+			    BLUE_FROM_ULONG (col) / 255.0);
+#endif
       cairo_rectangle (cr, x, y - FONT_BASE (face->font),
 		       s->width, FONT_HEIGHT (face->font));
       cairo_fill (cr);
@@ -533,13 +571,25 @@ ftcrfont_draw (struct glyph_string *s,
                                                        glyphs[i].index,
                                                        NULL));
     }
-
+#ifndef USE_BE_CAIRO
   x_set_cr_source_with_gc_foreground (f, s->gc);
+#else
+  uint32_t col = s->hl == DRAW_CURSOR ?
+    FRAME_OUTPUT_DATA (s->f)->cursor_fg : face->foreground;
+
+  cairo_set_source_rgb (cr, RED_FROM_ULONG (col) / 255.0,
+			GREEN_FROM_ULONG (col) / 255.0,
+			BLUE_FROM_ULONG (col) / 255.0);
+#endif
   cairo_set_scaled_font (cr, ftcrfont_info->cr_scaled_font);
   cairo_show_glyphs (cr, glyphs, len);
-
+#ifndef USE_BE_CAIRO
   x_end_cr_clip (f);
-
+#else
+  haiku_end_cr_clip (cr);
+  EmacsWindow_end_cr_critical_section (FRAME_HAIKU_WINDOW (f));
+  BView_draw_unlock (FRAME_HAIKU_VIEW (f));
+#endif
   unblock_input ();
 
   return len;
diff --git a/src/ftfont.c b/src/ftfont.c
index 12d0d72d27..2717f2a44c 100644
--- a/src/ftfont.c
+++ b/src/ftfont.c
@@ -3110,6 +3110,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..a233b698eb 100644
--- a/src/ftfont.h
+++ b/src/ftfont.h
@@ -29,6 +29,10 @@ #define EMACS_FTFONT_H
 # include FT_BDF_H
 #endif
 
+#ifdef USE_BE_CAIRO
+#include <cairo.h>
+#endif
+
 #ifdef HAVE_HARFBUZZ
 #include <hb.h>
 #include <hb-ft.h>
@@ -62,7 +66,7 @@ #define EMACS_FTFONT_H
   hb_font_t *hb_font;
 #endif  /* HAVE_HARFBUZZ */
 
-#ifdef USE_CAIRO
+#if defined (USE_CAIRO) || defined (USE_BE_CAIRO)
   cairo_scaled_font_t *cr_scaled_font;
   /* Scale factor from the bitmap strike metrics in 1/64 pixels, used
      as the hb_position_t value in HarfBuzz, to those in (scaled)
@@ -71,7 +75,8 @@ #define EMACS_FTFONT_H
   /* Font metrics cache.  */
   struct font_metrics **metrics;
   short metrics_nrows;
-#else
+#endif
+#ifndef HAVE_HAIKU
   /* These are used by the XFT backend.  */
   Display *display;
   XftFont *xftfont;
diff --git a/src/haiku_draw_support.cc b/src/haiku_draw_support.cc
new file mode 100644
index 0000000000..c04bed81c2
--- /dev/null
+++ b/src/haiku_draw_support.cc
@@ -0,0 +1,486 @@
+/* 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)
+
+#define RGB_COLOR_UINT32(r) RGB_TO_UINT32 ((r).red, (r).green, (r).blue)
+
+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);
+  vw->PushState ();
+}
+
+void
+BView_EndClip (void *view)
+{
+  BView *vw = get_view (view);
+  vw->PopState ();
+}
+
+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);
+
+#ifndef USE_BE_CAIRO
+  vw->SetViewColor (col);
+#else
+  vw->SetViewColor (B_TRANSPARENT_32_BIT);
+#endif
+}
+
+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_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_DrawBitmapWithEraseOp (void *view, void *bitmap, int x,
+			     int y, int width, int height)
+{
+  BView *vw = get_view (view);
+  BBitmap *bm = (BBitmap *) bitmap;
+  BBitmap bc (bm->Bounds (), B_RGBA32);
+  BRect rect (x, y, x + width - 1, y + height - 1);
+
+  if (bc.InitCheck () != B_OK || bc.ImportBits (bm) != B_OK)
+    return;
+
+  uint32_t *bits = (uint32_t *) bc.Bits ();
+  size_t stride = bc.BytesPerRow ();
+
+  if (bm->ColorSpace () == B_GRAY1)
+    {
+      rgb_color low_color = vw->LowColor ();
+      for (int y = 0; y <= bc.Bounds ().Height (); ++y)
+	{
+	  for (int x = 0; x <= bc.Bounds ().Width (); ++x)
+	    {
+	      if (bits[y * (stride / 4) + x] == 0xFF000000)
+		bits[y * (stride / 4) + x] = RGB_COLOR_UINT32 (low_color);
+	      else
+		bits[y * (stride / 4) + x] = 0;
+	    }
+	}
+    }
+
+  vw->PushState ();
+  vw->SetDrawingMode (bm->ColorSpace () == B_GRAY1 ? B_OP_OVER : B_OP_ERASE);
+  vw->DrawBitmap (&bc, rect);
+  vw->PopState ();
+}
+
+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 (source->Bounds (), B_RGBA32);
+  if (bm.InitCheck () != B_OK)
+    return;
+  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));
+}
+
+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);
+  if (bm->InitCheck () != B_OK)
+    gui_abort ("Failed to init bitmap for rotate");
+  int w = bmp->Bounds ().Width () + 1;
+  int h = bmp->Bounds ().Height () + 1;
+
+  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);
+  if (bm->InitCheck () != B_OK)
+    gui_abort ("Failed to init bitmap for rotate");
+  int w = bmp->Bounds ().Width () + 1;
+  int h = bmp->Bounds ().Height () + 1;
+
+  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);
+      if (dst->InitCheck () != B_OK)
+	if (bm->InitCheck () != B_OK)
+	  gui_abort ("Failed to init bitmap for scale");
+      dst->AddChild (&vw);
+
+      if (!vw.LockLooper ())
+	gui_abort ("Failed to lock offscreen view for scale");
+
+      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_FillTriangle (void *view, int x1, int y1,
+		    int x2, int y2, int x3, int y3)
+{
+  BView *vw = get_view (view);
+  vw->FillTriangle (BPoint (x1, y1), BPoint (x2, y2),
+		    BPoint (x3, y3));
+}
+
+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..f113a88726
--- /dev/null
+++ b/src/haiku_font_support.cc
@@ -0,0 +1,636 @@
+/* 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] = 32; 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_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);
+}
+
+int
+BFont_have_char_block (void *font, int32_t beg, int32_t end)
+{
+  BFont *ft = (BFont *) font;
+  return ft->IncludesBlock (beg, end);
+}
+
+void
+BFont_char_bounds (void *font, const char *mb_str, int *advance,
+		   int *lb, int *rb)
+{
+  BFont *ft = (BFont *) font;
+  edge_info edge_info;
+  float size, escapement;
+  size = ft->Size ();
+
+  ft->GetEdges (mb_str, 1, &edge_info);
+  ft->GetEscapements (mb_str, 1, &escapement);
+  *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, "Book"))
+	pattern->weight = HAIKU_BOOK;
+      else if (token && !strcmp (token, "Heavy"))
+	pattern->weight = HAIKU_HEAVY;
+      else if (token && !strcmp (token, "UltraHeavy"))
+	pattern->weight = HAIKU_ULTRA_HEAVY;
+      else if (token && !strcmp (token, "Black"))
+	pattern->weight = HAIKU_BLACK;
+      else if (token && !strcmp (token, "Medium"))
+	pattern->weight = HAIKU_MEDIUM;
+      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_io.c b/src/haiku_io.c
new file mode 100644
index 0000000000..5f3eaaee1b
--- /dev/null
+++ b/src/haiku_io.c
@@ -0,0 +1,194 @@
+/* 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);
+    case REFS_EVENT:
+      return sizeof (struct haiku_refs_event);
+    case APP_QUIT_REQUESTED_EVENT:
+      return sizeof (struct haiku_app_quit_requested_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;
+  sigfillset (&set);
+
+  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_support.cc b/src/haiku_support.cc
new file mode 100644
index 0000000000..1634fa61e2
--- /dev/null
+++ b/src/haiku_support.cc
@@ -0,0 +1,2814 @@
+/* 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 <support/Locker.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 <private/interface/ToolTip.h>
+
+#include <cmath>
+#include <cstring>
+#include <cstdint>
+#include <cstdio>
+#include <csignal>
+#include <cfloat>
+
+#include <pthread.h>
+
+#ifdef USE_BE_CAIRO
+#include <cairo.h>
+#endif
+
+#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;
+static BLocker key_map_lock;
+
+extern "C"
+{
+  extern _Noreturn void emacs_abort (void);
+  /* Also defined in haikuterm.h.  */
+  extern void be_app_quit (void);
+}
+
+static thread_id app_thread;
+
+_Noreturn void
+gui_abort (const char *msg)
+{
+  fprintf (stderr, "Abort in GUI code: %s\n", msg);
+  fprintf (stderr, "Under Haiku, Emacs cannot recover from errors in GUI code\n");
+  fprintf (stderr, "App Server disconnects usually manifest as bitmap "
+	   "initialization failures or lock failures.");
+  emacs_abort ();
+}
+
+#ifdef USE_BE_CAIRO
+static cairo_format_t
+cairo_format_from_color_space (color_space space)
+{
+  switch (space)
+    {
+    case B_RGBA32:
+      return CAIRO_FORMAT_ARGB32;
+    case B_RGB32:
+      return CAIRO_FORMAT_RGB24;
+    case B_RGB16:
+      return CAIRO_FORMAT_RGB16_565;
+    case B_GRAY8:
+      return CAIRO_FORMAT_A8;
+    case B_GRAY1:
+      return CAIRO_FORMAT_A1;
+    default:
+      gui_abort ("Unsupported color space");
+    }
+}
+#endif
+
+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_lock.Lock ())
+    gui_abort ("Failed to lock keymap");
+  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);
+  key_map_lock.Unlock ();
+}
+
+static void
+map_normal (uint32_t kc, uint32_t *ch)
+{
+  if (!key_map_lock.Lock ())
+    gui_abort ("Failed to lock keymap");
+  if (!key_map)
+    get_key_map (&key_map, &key_chars);
+  if (!key_map)
+    return;
+  if (kc >= 128)
+    return;
+
+  int32_t m = key_map->normal_map[kc];
+  map_key (key_chars, m, ch);
+  key_map_lock.Unlock ();
+}
+
+class Emacs : public BApplication
+{
+public:
+  Emacs () : BApplication ("application/x-vnd.GNU-emacs")
+  {
+  }
+
+  void
+  AboutRequested (void)
+  {
+    BAlert *about = new BAlert (PACKAGE_NAME,
+				PACKAGE_STRING
+				"\nThe extensible, self-documenting, real-time display editor.",
+				"Close");
+    about->Go ();
+  }
+
+  bool
+  QuitRequested (void)
+  {
+    struct haiku_app_quit_requested_event rq;
+    haiku_write (APP_QUIT_REQUESTED_EVENT, &rq);
+    return 0;
+  }
+
+  void
+  RefsReceived (BMessage *msg)
+  {
+    struct haiku_refs_event rq;
+    entry_ref ref;
+    BEntry entry;
+    BPath path;
+    int32 cookie = 0;
+
+    while (msg->FindRef ("refs", cookie++, &ref) == B_OK)
+      {
+        if (entry.SetTo (&ref, 0) == B_OK
+            && entry.GetPath (&path) == B_OK)
+          {
+            rq.ref = strdup (path.Path ());
+            haiku_write (REFS_EVENT, &rq);
+          }
+      }
+  }
+};
+
+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;
+  int shown_flag = 0;
+
+#ifdef USE_BE_CAIRO
+  BLocker surface_lock;
+  cairo_surface_t *cr_surface = NULL;
+#endif
+
+  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 ();
+
+#ifdef USE_BE_CAIRO
+    if (!surface_lock.Lock ())
+      gui_abort ("Failed to lock cairo surface");
+    if (cr_surface)
+      {
+	cairo_surface_destroy (cr_surface);
+	cr_surface = NULL;
+      }
+    surface_lock.Unlock ();
+#endif
+  }
+
+  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;
+	  }
+      }
+
+    gui_abort ("Failed to unlink child frame");
+  }
+
+  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)
+	  gui_abort ("Trying to link a child frame that is already present");
+      }
+
+    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;
+	  }
+      }
+
+    gui_abort ("Trying to move a child frame that doesn't exist");
+  }
+
+  void
+  WindowActivated (bool activated)
+  {
+    struct haiku_activation_event rq;
+    rq.window = this;
+    rq.activated_p = activated;
+
+    haiku_write (ACTIVATION, &rq);
+  }
+
+  void
+  DirectConnected (direct_buffer_info *info)
+  {
+#ifdef USE_BE_CAIRO
+    if (!surface_lock.Lock ())
+      gui_abort ("Failed to lock window direct cr surface");
+    if (cr_surface)
+      {
+	cairo_surface_destroy (cr_surface);
+	cr_surface = NULL;
+      }
+
+    if (info->buffer_state != B_DIRECT_STOP)
+      {
+	int left, top, right, bottom;
+	left = info->clip_bounds.left;
+	top = info->clip_bounds.top;
+	right = info->clip_bounds.right;
+	bottom = info->clip_bounds.bottom;
+
+	unsigned char *bits = (unsigned char *) info->bits;
+	if ((info->bits_per_pixel % 8) == 0)
+	  {
+	    bits += info->bytes_per_row * top;
+	    bits += (left * info->bits_per_pixel / 8);
+	    cr_surface = cairo_image_surface_create_for_data
+	      (bits,
+	       cairo_format_from_color_space (info->pixel_format),
+	       right - left + 1,
+	       bottom - top + 1,
+	       info->bytes_per_row);
+	  }
+      }
+    surface_lock.Unlock ();
+#endif
+  }
+
+  void
+  MessageReceived (BMessage *msg)
+  {
+    int32 old_what = 0;
+
+    if (msg->WasDropped ())
+      {
+	entry_ref ref;
+        if (msg->FindRef ("refs", &ref) == B_OK)
+	  {
+	    msg->what = B_REFS_RECEIVED;
+	    be_app->PostMessage (msg);
+	    msg->SendReply (B_OK);
+	  }
+      }
+    else 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);
+      }
+    else
+      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;
+
+	if (mods & B_OPTION_KEY)
+	  rq.modifiers |= HAIKU_MODIFIER_SUPER;
+
+	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);
+	else if (rq.kc >= 0)
+	  map_normal (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;
+
+	if (mods & B_OPTION_KEY)
+	  rq.modifiers |= HAIKU_MODIFIER_SUPER;
+
+	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 * 10;
+	    rq.delta_y = dy * 10;
+
+	    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;
+    if (this->parent)
+      shown_flag = 1;
+    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;
+	}
+
+    gui_abort ("Trying to calculate offsets for a child frame that doesn't exist");
+  }
+
+  void
+  MakeFullscreen (int make_fullscreen_p)
+  {
+    BScreen screen (this);
+
+      if (!screen.IsValid ())
+	gui_abort ("Trying to make a window fullscreen without a screen");
+
+    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 *copy_bitmap = NULL;
+
+#ifdef USE_BE_CAIRO
+  cairo_surface_t *cr_surface = NULL;
+  BLocker cr_surface_lock;
+#endif
+
+  BPoint tt_absl_pos;
+
+  color_space cspace;
+
+  EmacsView () : BView (BRect (0, 0, 0, 0), "Emacs", B_FOLLOW_NONE, B_WILL_DRAW)
+  {
+
+  }
+
+  ~EmacsView ()
+  {
+    TearDownDoubleBuffering ();
+  }
+
+  void
+  AttachedToWindow (void)
+  {
+    cspace = B_RGBA32;
+  }
+
+#ifdef USE_BE_CAIRO
+  void
+  DetachCairoSurface (void)
+  {
+    if (!cr_surface_lock.Lock ())
+      gui_abort ("Could not lock cr surface during detachment");
+    if (!cr_surface)
+      gui_abort ("Trying to detach window cr surface when none exists");
+    cairo_surface_destroy (cr_surface);
+    cr_surface = NULL;
+    cr_surface_lock.Unlock ();
+  }
+
+  void
+  AttachCairoSurface (void)
+  {
+    if (!cr_surface_lock.Lock ())
+      gui_abort ("Could not lock cr surface during attachment");
+    if (cr_surface)
+      gui_abort ("Trying to attach cr surface when one already exists");
+    cr_surface = cairo_image_surface_create_for_data
+      ((unsigned char *) offscreen_draw_bitmap_1->Bits (),
+       CAIRO_FORMAT_ARGB32, offscreen_draw_bitmap_1->Bounds ().Width (),
+       offscreen_draw_bitmap_1->Bounds ().Height (),
+       offscreen_draw_bitmap_1->BytesPerRow ());
+    if (!cr_surface)
+      gui_abort ("Cr surface allocation failed for double-buffered view");
+    cr_surface_lock.Unlock ();
+  }
+#endif
+
+  void
+  TearDownDoubleBuffering (void)
+  {
+    if (offscreen_draw_view)
+      {
+	if (Window ())
+	  ClearViewBitmap ();
+	if (copy_bitmap)
+	  {
+	    delete copy_bitmap;
+	    copy_bitmap = NULL;
+	  }
+	if (!looper_locked_count)
+	  if (!offscreen_draw_view->LockLooper ())
+	    gui_abort ("Failed to lock offscreen draw view");
+#ifdef USE_BE_CAIRO
+	if (cr_surface)
+	  DetachCairoSurface ();
+#endif
+	offscreen_draw_view->RemoveSelf ();
+	delete offscreen_draw_view;
+	offscreen_draw_view = NULL;
+	delete offscreen_draw_bitmap_1;
+	offscreen_draw_bitmap_1 = NULL;
+      }
+   }
+
+  void
+  AfterResize (float newWidth, float newHeight)
+  {
+    if (offscreen_draw_view)
+      {
+	if (!LockLooper ())
+	  gui_abort ("Failed to lock looper after resize");
+
+	if (!offscreen_draw_view->LockLooper ())
+	  gui_abort ("Failed to lock offscreen draw view after resize");
+#ifdef USE_BE_CAIRO
+	DetachCairoSurface ();
+#endif
+	offscreen_draw_view->RemoveSelf ();
+	delete offscreen_draw_bitmap_1;
+	offscreen_draw_bitmap_1 = new BBitmap (Frame (), cspace, 1);
+	if (offscreen_draw_bitmap_1->InitCheck () != B_OK)
+	  gui_abort ("Offscreen draw bitmap initialization failed");
+
+	offscreen_draw_view->MoveTo (Frame ().left, Frame ().top);
+	offscreen_draw_view->ResizeTo (Frame ().Width (), Frame ().Height ());
+	offscreen_draw_bitmap_1->AddChild (offscreen_draw_view);
+#ifdef USE_BE_CAIRO
+	AttachCairoSurface ();
+#endif
+
+	if (looper_locked_count)
+	  {
+	    offscreen_draw_bitmap_1->Lock ();
+	  }
+
+	UnlockLooper ();
+      }
+  }
+
+  void
+  Pulse (void)
+  {
+    visible_bell_color = 0;
+    SetFlags (Flags () & ~B_PULSE_NEEDED);
+    Window ()->SetPulseRate (0);
+    Invalidate ();
+  }
+
+  void
+  Draw (BRect expose_bounds)
+  {
+    struct haiku_expose_event rq;
+    EmacsWindow *w = (EmacsWindow *) Window ();
+
+    if (visible_bell_color > 0)
+      {
+	PushState ();
+	BView_SetHighColorForVisibleBell (this, visible_bell_color);
+	FillRect (Frame ());
+	PopState ();
+	return;
+      }
+
+    if (w->shown_flag)
+      {
+	PushState ();
+	SetDrawingMode (B_OP_ERASE);
+	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);
+      }
+  }
+
+  void
+  DoVisibleBell (uint32_t color)
+  {
+    if (!LockLooper ())
+      gui_abort ("Failed to lock looper during visible bell");
+    visible_bell_color = color | (255 << 24);
+    SetFlags (Flags () | B_PULSE_NEEDED);
+    Window ()->SetPulseRate (100 * 1000);
+    Invalidate ();
+    UnlockLooper ();
+  }
+
+  void
+  FlipBuffers (void)
+  {
+    if (!LockLooper ())
+      gui_abort ("Failed to lock looper during buffer flip");
+    if (!offscreen_draw_view)
+      gui_abort ("Failed to lock offscreen view during buffer flip");
+
+    offscreen_draw_view->Flush ();
+    offscreen_draw_view->Sync ();
+
+    EmacsWindow *w = (EmacsWindow *) Window ();
+    w->shown_flag = 0;
+
+    if (copy_bitmap &&
+	copy_bitmap->Bounds () != offscreen_draw_bitmap_1->Bounds ())
+      {
+	delete copy_bitmap;
+	copy_bitmap = NULL;
+      }
+    if (!copy_bitmap)
+      copy_bitmap = new BBitmap (offscreen_draw_bitmap_1);
+    else
+      copy_bitmap->ImportBits (offscreen_draw_bitmap_1);
+
+    if (copy_bitmap->InitCheck () != B_OK)
+      gui_abort ("Failed to init copy bitmap during buffer flip");
+
+    SetViewBitmap (copy_bitmap,
+		   Frame (), Frame (), B_FOLLOW_NONE, 0);
+
+    Invalidate ();
+    UnlockLooper ();
+    return;
+  }
+
+  void
+  SetUpDoubleBuffering (void)
+  {
+    if (!LockLooper ())
+      gui_abort ("Failed to lock self setting up double buffering");
+    if (offscreen_draw_view)
+      gui_abort ("Failed to lock offscreen view setting up double buffering");
+
+    offscreen_draw_bitmap_1 = new BBitmap (Frame (), cspace, 1);
+    if (offscreen_draw_bitmap_1->InitCheck () != B_OK)
+      gui_abort ("Failed to init offscreen bitmap");
+#ifdef USE_BE_CAIRO
+    AttachCairoSurface ();
+#endif
+    offscreen_draw_view = new BView (Frame (), NULL, B_FOLLOW_NONE, B_WILL_DRAW);
+    offscreen_draw_bitmap_1->AddChild (offscreen_draw_view);
+
+    if (looper_locked_count)
+      {
+	if (!offscreen_draw_bitmap_1->Lock ())
+	  gui_abort ("Failed to lock bitmap after double buffering was set up.");
+      }
+
+    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 ();
+
+    if (ToolTip ())
+      ToolTip ()->SetMouseRelativeLocation (BPoint (-(point.x - tt_absl_pos.x),
+						    -(point.y - tt_absl_pos.y)));
+
+    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;
+
+    if (mods & B_OPTION_KEY)
+      rq.modifiers |= HAIKU_MODIFIER_SUPER;
+
+    SetMouseEventMask (B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS);
+
+    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;
+
+    if (mods & B_OPTION_KEY)
+      rq.modifiers |= HAIKU_MODIFIER_SUPER;
+
+    if (!buttons)
+      SetMouseEventMask (0, 0);
+
+    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 EmacsTitleMenuItem : public BMenuItem
+{
+public:
+  EmacsTitleMenuItem (const char *str) : BMenuItem (str, NULL)
+  {
+    SetEnabled (0);
+  }
+
+  void
+  DrawContent (void)
+  {
+    BMenu *menu = Menu ();
+
+    menu->PushState ();
+    menu->SetFont (be_bold_font);
+    BView_SetHighColorForVisibleBell (menu, 0);
+    BMenuItem::DrawContent ();
+    menu->PopState ();
+  }
+};
+
+class EmacsMenuItem : public BMenuItem
+{
+public:
+  int menu_bar_id = -1;
+  void *wind_ptr = NULL;
+  char *key = NULL;
+  char *help = NULL;
+
+  EmacsMenuItem (const char *ky,
+		 const char *str,
+		 const char *help,
+		 BMessage *message = NULL) : BMenuItem (str, message)
+  {
+    if (ky)
+      {
+	key = strdup (ky);
+	if (!key)
+	  {
+	    perror ("strdup");
+	    gui_abort ("strdup failed");
+	  }
+      }
+
+    if (help)
+      {
+	this->help = strdup (help);
+	if (!this->help)
+	  {
+	    perror ("strdup");
+	    gui_abort ("strdup failed");
+	  }
+      }
+  }
+
+  ~EmacsMenuItem ()
+  {
+    if (key)
+      free (key);
+    if (help)
+      free (help);
+  }
+
+  void
+  DrawContent (void)
+  {
+    BMenu *menu = Menu ();
+
+    BMenuItem::DrawContent ();
+
+    if (key)
+      {
+	BRect r = menu->Frame ();
+	int w = menu->StringWidth (key);
+	menu->MovePenTo (BPoint (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);
+      }
+    else if (help)
+      {
+	Menu ()->SetToolTip (highlight_p ? help : NULL);
+      }
+
+    BMenuItem::Highlight (highlight_p);
+  }
+};
+
+class EmacsPopUpMenu : public BPopUpMenu
+{
+public:
+  EmacsPopUpMenu (const char *name) : BPopUpMenu (name, 0)
+  {
+
+  }
+
+  void
+  FrameResized (float w, float h)
+  {
+    Invalidate ();
+    BPopUpMenu::FrameResized (w, h);
+  }
+};
+
+static int32
+start_running_application (void *data)
+{
+  haiku_io_init_in_app_thread ();
+
+  if (!((Emacs *) data)->Lock ())
+    gui_abort ("Failed to lock application");
+
+  ((Emacs *) data)->Run ();
+  ((Emacs *) data)->Unlock ();
+  return 0;
+}
+
+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 || bmp->InitCheck () != B_OK)
+    {
+      if (bmp)
+	delete 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->InitCheck () == B_OK ? (void *) bn : (void *) (delete bn, NULL);
+}
+
+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;
+  thread_id id;
+  Emacs *app;
+
+  app = new Emacs;
+  app->Unlock ();
+  if ((id = spawn_thread (start_running_application, "Emacs app thread",
+			  B_DEFAULT_MEDIA_PRIORITY, app)) < 0)
+    gui_abort ("spawn_thread failed");
+
+  resume_thread (id);
+
+  app_thread = id;
+  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 ())
+	gui_abort ("Failed to lock window looper setting offset");
+      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;
+  if (!screen.IsValid ())
+    gui_abort ("Invalid 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 ())
+    gui_abort ("Failed to lock view for resize");
+  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_create_modeline (void)
+{
+  return new BCursor (B_CURSOR_ID_CONTEXT_MENU);
+}
+
+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 ())
+    gui_abort ("Failed to lock view setting cursor");
+  ((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)
+{
+  if (*code == 10 && kc != 0x42)
+    {
+      *code = XK_Return;
+      *non_ascii_p = 1;
+      return;
+    }
+
+  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 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 ())
+    gui_abort ("Failed to lock scrollbar owner");
+  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 ())
+    gui_abort ("Failed to lock scrollbar parent");
+  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 ())
+    gui_abort ("Failed to lock view moving frame");
+  vw->MoveTo (x, y);
+  vw->ResizeTo (x1 - x, y1 - y);
+  vw->Flush ();
+  vw->Sync ();
+  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_invalidate (void *view)
+{
+  BView *vw = (BView *) view;
+  if (!vw->LockLooper ())
+    gui_abort ("Couldn't lock view while invalidating it");
+  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)
+    {
+      if (!vw->offscreen_draw_bitmap_1->Lock ())
+	gui_abort ("Failed to lock offscreen bitmap while acquiring draw lock");
+    }
+  else if (!v->LockLooper ())
+    gui_abort ("Failed to lock draw view while acquiring draw lock");
+
+  if (v != vw && !vw->LockLooper ())
+    gui_abort ("Failed to lock view while acquiring draw lock");
+  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_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) = 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 ())
+    gui_abort ("Failed to lock view in BView_get_mouse");
+  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 ())
+    gui_abort ("Failed to lock view in convert_to_screen");
+  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 ())
+    gui_abort ("Failed to lock view in convert_from_screen");
+  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 ())
+    gui_abort ("Failed to lock window while changing its decorations");
+  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 ())
+    gui_abort ("Failed to lock window while setting ttip decoration");
+  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 ())
+    gui_abort ("Failed to lock window while setting avoid focus");
+
+  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 ())
+    gui_abort ("Failed to lock view while deleting it");
+  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 EmacsPopUpMenu (name);
+  menu->SetRadioMode (0);
+  return menu;
+}
+
+void
+BMenu_add_title (void *menu, const char *text)
+{
+  EmacsTitleMenuItem *it = new EmacsTitleMenuItem (text);
+  BMenu *mn = (BMenu *) menu;
+  mn->AddItem (it);
+}
+
+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,
+		const char *help)
+{
+  BMenu *m = (BMenu *) menu;
+  BMessage *msg;
+  if (ptr)
+    msg = new BMessage ();
+  EmacsMenuItem *it = new EmacsMenuItem (key, label, help, 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;
+  mn->SetRadioMode (0);
+  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 ())
+    gui_abort ("Failed to lock menu bar parent");
+  vw->AddChild ((BView *) bar);
+  vw->UnlockLooper ();
+
+  return bar;
+}
+
+void
+BMenuBar_delete (void *menubar)
+{
+  BView *vw = (BView *) menubar;
+  BView *p = vw->Parent ();
+  if (!p->LockLooper ())
+    gui_abort ("Failed to lock menu bar parent while removing menubar");
+  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
+BView_set_and_show_sticky_tooltip (void *view, const char *tooltip,
+				   int x, int y)
+{
+  BToolTip *tip;
+  BView *vw = (BView *) view;
+  if (!vw->LockLooper ())
+    gui_abort ("Failed to lock view while showing sticky tooltip");
+  vw->SetToolTip (tooltip);
+  tip = vw->ToolTip ();
+  BPoint pt;
+  EmacsView *ev = dynamic_cast<EmacsView *> (vw);
+  if (ev)
+    ev->tt_absl_pos = BPoint (x, y);
+
+  vw->GetMouse (&pt, NULL, 1);
+  pt.x -= x;
+  pt.y -= y;
+
+  pt.x = -pt.x;
+  pt.y = -pt.y;
+
+  tip->SetMouseRelativeLocation (pt);
+  tip->SetSticky (1);
+  vw->ShowToolTip (tip);
+  vw->UnlockLooper ();
+}
+
+void
+BAlert_delete (void *alert)
+{
+  delete (BAlert *) alert;
+}
+
+void
+BScreen_res (double *rrsx, double *rrsy)
+{
+  BScreen s (B_MAIN_SCREEN_ID);
+  if (!s.IsValid ())
+    gui_abort ("Invalid screen for resolution checks");
+  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 ())
+    gui_abort ("Failed to lock window while parenting");
+  w->ParentTo ((EmacsWindow *) other_window);
+  w->UnlockLooper ();
+}
+
+void
+EmacsWindow_unparent (void *window)
+{
+  EmacsWindow *w = (EmacsWindow *) window;
+  if (!w->LockLooper ())
+    gui_abort ("Failed to lock window while unparenting");
+  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.  */
+      if (!screen.IsValid ())
+	gui_abort ("Invalid screen");
+      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;
+
+  gui_abort ("Bad colorspace for screen");
+  /* 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;
+      if (!screen.IsValid ())
+	gui_abort ("Invalid 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;
+
+  gui_abort ("Bad colorspace for screen");
+  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 ())
+    gui_abort ("Couldn't lock window for weak move");
+  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 ())
+    gui_abort ("Couldn't lock view while setting up double buffering");
+  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 ())
+    gui_abort ("Couldn't lock view in flip_and_blit");
+  view->FlipBuffers ();
+  view->UnlockLooper ();
+}
+
+void
+EmacsView_disable_double_buffering (void *vw)
+{
+  EmacsView *view = (EmacsView *) vw;
+  if (!view->LockLooper ())
+    gui_abort ("Couldn't lock view tearing down double buffering");
+  view->TearDownDoubleBuffering ();
+  view->UnlockLooper ();
+}
+
+int
+EmacsView_double_buffered_p (void *vw)
+{
+  EmacsView *view = (EmacsView *) vw;
+  if (!view->LockLooper ())
+    gui_abort ("Couldn't lock view testing double buffering status");
+  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 ();
+  panel->Window ()->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 (!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 || panel->Window ()->IsHidden ())
+	{
+	  c_unbind_to_nil_from_cxx (idx);
+	  return ptr;
+	}
+    }
+}
+
+void
+be_app_quit (void)
+{
+  if (be_app)
+    {
+      status_t e;
+      while (!be_app->Lock ());
+      be_app->Quit ();
+      wait_for_thread (app_thread, &e);
+    }
+}
+
+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 ())
+    gui_abort ("Couldn't lock menubar");
+  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;
+  if (vw->LockLooper ())
+    {
+      vw->ShowToolTip (vw->ToolTip ());
+      vw->UnlockLooper ();
+    }
+}
+
+#ifdef USE_BE_CAIRO
+cairo_surface_t *
+EmacsView_cairo_surface (void *view)
+{
+  EmacsView *vw = (EmacsView *) view;
+  EmacsWindow *wn = (EmacsWindow *) vw->Window ();
+  return vw->cr_surface ? vw->cr_surface : wn->cr_surface;
+}
+
+void
+BView_cr_dump_clipping (void *view, cairo_t *ctx)
+{
+  BView *vw = (BView *) find_appropriate_view_for_draw (view);
+  BRegion cr;
+  vw->GetClippingRegion (&cr);
+
+  for (int i = 0; i < cr.CountRects (); ++i)
+    {
+      BRect r = cr.RectAt (i);
+      cairo_rectangle (ctx, r.left, r.top, r.Width () + 1,
+		       r.Height () + 1);
+    }
+
+  cairo_clip (ctx);
+}
+
+void
+EmacsWindow_begin_cr_critical_section (void *window)
+{
+  EmacsWindow *w = (EmacsWindow *) window;
+  if (!w->surface_lock.Lock ())
+    gui_abort ("Couldn't lock cairo surface");
+
+  BView *vw = (BView *) w->FindView ("Emacs");
+  EmacsView *ev = dynamic_cast <EmacsView *> (vw);
+  if (ev && !ev->cr_surface_lock.Lock ())
+    gui_abort ("Couldn't lock view cairo surface");
+}
+
+void
+EmacsWindow_end_cr_critical_section (void *window)
+{
+  EmacsWindow *w = (EmacsWindow *) window;
+  w->surface_lock.Unlock ();
+  BView *vw = (BView *) w->FindView ("Emacs");
+  EmacsView *ev = dynamic_cast <EmacsView *> (vw);
+  if (ev)
+    ev->cr_surface_lock.Unlock ();
+}
+#endif
+
+int
+be_string_width_with_plain_font (const char *str)
+{
+  return be_plain_font->StringWidth (str);
+}
+
+int
+be_plain_font_height (void)
+{
+  struct font_height fheight;
+  be_plain_font->GetHeight (&fheight);
+
+  return fheight.ascent + fheight.descent;
+}
+
+int
+be_get_display_screens (void)
+{
+  int count = 1;
+  BScreen scr;
+
+  if (!scr.IsValid ())
+    gui_abort ("Main screen vanished!");
+  while (scr.SetToNext () == B_OK && scr.IsValid ())
+    ++count;
+
+  return count;
+}
+
+void
+BWindow_set_min_size (void *window, int width, int height)
+{
+  BWindow *w = (BWindow *) window;
+
+  if (!w->LockLooper ())
+    gui_abort ("Failed to lock window looper setting min size");
+  w->SetSizeLimits (width, -1, height, -1);
+  w->UnlockLooper ();
+}
+
+void
+BWindow_set_size_alignment (void *window, int align_width, int align_height)
+{
+  BWindow *w = (BWindow *) window;
+
+  if (!w->LockLooper ())
+    gui_abort ("Failed to lock window looper setting alignment");
+#if 0 /* Haiku does not currently implement SetWindowAlignment.  */
+  if (w->SetWindowAlignment (B_PIXEL_ALIGNMENT, -1, -1, align_width,
+			     align_width, -1, -1, align_height,
+			     align_height) != B_NO_ERROR)
+    gui_abort ("Invalid pixel alignment");
+#endif
+  w->UnlockLooper ();
+}
diff --git a/src/haiku_support.h b/src/haiku_support.h
new file mode 100644
index 0000000000..160feac337
--- /dev/null
+++ b/src/haiku_support.h
@@ -0,0 +1,1053 @@
+/* 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
+
+#ifdef USE_BE_CAIRO
+#include <cairo.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,
+    REFS_EVENT,
+    APP_QUIT_REQUESTED_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;
+};
+
+struct haiku_refs_event
+{
+  /* Free this with free! */
+  char *ref;
+};
+
+struct haiku_app_quit_requested_event
+{
+  char dummy;
+};
+
+#define HAIKU_MODIFIER_ALT (1)
+#define HAIKU_MODIFIER_CTRL (1 << 1)
+#define HAIKU_MODIFIER_SHIFT (1 << 2)
+#define HAIKU_MODIFIER_SUPER (1 << 3)
+
+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 them 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
+#define HAIKU_BOOK 400
+#define HAIKU_HEAVY 800
+#define HAIKU_ULTRA_HEAVY 900
+#define HAIKU_BLACK 1000
+#define HAIKU_MEDIUM 2000
+
+#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, or in MONO1 if MONO_P
+     is non-zero.  */
+  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.  */
+  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);
+
+  /* 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);
+
+  /* Return non-null if font contains a block from BEG to END.  */
+  extern int
+  BFont_have_char_block (void *font, int32_t beg, int32_t end);
+
+  /* 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_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_FillTriangle (void *view, int x1, int y1,
+		      int x2, int y2, int x3, int y3);
+
+  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_DrawBitmapWithEraseOp (void *view, void *bitmap, int x,
+			       int y, int width, int height);
+
+  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);
+
+  /* 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_modeline (void);
+
+  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);
+
+  /* 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 Y 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 U.  */
+  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 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);
+
+  /* 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,
+		  const char *help);
+
+  /* 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 return a pointer to the
+     data of the selected item (if one exists), or NULL.  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 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, int flags);
+
+  /* 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.  */
+  extern void
+  BView_show_tooltip (void *view);
+
+#ifdef USE_BE_CAIRO
+  extern cairo_surface_t *
+  EmacsView_cairo_surface (void *view);
+
+  extern void
+  BView_cr_dump_clipping (void *view, cairo_t *ctx);
+
+  extern void
+  EmacsWindow_begin_cr_critical_section (void *window);
+
+  extern void
+  EmacsWindow_end_cr_critical_section (void *window);
+#endif
+  /* Set VIEW's tooltip to a sticky tooltip at X by Y.  */
+  extern void
+  BView_set_and_show_sticky_tooltip (void *view, const char *tooltip,
+				     int x, int y);
+
+  /* Add a title item to MENU.  These items cannot be highlighted or
+     triggered, and their labels will display as bold text.  */
+  extern void
+  BMenu_add_title (void *menu, const char *text);
+
+  /* Get the ascent + descent of the plain font.  */
+  extern int
+  be_plain_font_height (void);
+
+  /* Get the width of STR in the plain font.  */
+  extern int
+  be_string_width_with_plain_font (const char *str);
+
+  /* Return the number of physical displays connected.  */
+  extern int
+  be_get_display_screens (void);
+
+  /* Set the minimum width the user can resize WINDOW to.  */
+  extern void
+  BWindow_set_min_size (void *window, int width, int height);
+
+  /* Set the alignment of WINDOW's dimensions.  */
+  extern void
+  BWindow_set_size_alignment (void *window, int align_width, int align_height);
+
+#ifdef __cplusplus
+  /* Find an appropriate view to draw onto.  */
+  extern void *
+  find_appropriate_view_for_draw (void *vw);
+}
+
+extern _Noreturn void
+gui_abort (const char *msg);
+#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..5ea5a3cca4
--- /dev/null
+++ b/src/haikufns.c
@@ -0,0 +1,2613 @@
+/* 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"
+
+#include <stdlib.h>
+
+#include <kernel/OS.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);
+}
+
+/* Borrowed from w32 implementation.  */
+
+struct load_sample
+{
+  time_t sample_time;
+  bigtime_t idle;
+  bigtime_t kernel;
+  bigtime_t user;
+};
+
+/* We maintain 1-sec samples for the last 16 minutes in a circular buffer.  */
+static struct load_sample samples[16*60];
+static int first_idx = -1, last_idx = -1;
+static int max_idx = ARRAYELTS (samples);
+static unsigned num_of_processors = 0;
+
+static int
+buf_next (int from)
+{
+  int next_idx = from + 1;
+
+  if (next_idx >= max_idx)
+    next_idx = 0;
+
+  return next_idx;
+}
+
+static int
+buf_prev (int from)
+{
+  int prev_idx = from - 1;
+
+  if (prev_idx < 0)
+    prev_idx = max_idx - 1;
+
+  return prev_idx;
+}
+
+static double
+getavg (int which)
+{
+  double retval = -1.0;
+  double tdiff;
+  int idx;
+  double span = (which == 0 ? 1.0 : (which == 1 ? 5.0 : 15.0)) * 60;
+  time_t now = samples[last_idx].sample_time;
+
+  if (first_idx != last_idx)
+    {
+      for (idx = buf_prev (last_idx); ; idx = buf_prev (idx))
+	{
+	  tdiff = difftime (now, samples[idx].sample_time);
+	  if (tdiff >= span - 2 * DBL_EPSILON * now)
+	    {
+	      long double sys =
+		(samples[last_idx].kernel + samples[last_idx].user) -
+		(samples[idx].kernel + samples[idx].user);
+	      long double idl = samples[last_idx].idle - samples[idx].idle;
+
+	      retval = (idl / (sys + idl)) * num_of_processors;
+	      break;
+	    }
+	  if (idx == first_idx)
+	    break;
+	}
+    }
+
+  return retval;
+}
+
+static void
+sample_sys_load (bigtime_t *idle, bigtime_t *system, bigtime_t *user)
+{
+  bigtime_t i = 0, s = 0, u = 0;
+  team_info info;
+  thread_info inf;
+
+  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 (!strncmp (inf.name, "idle thread ", 12))
+	  i += inf.user_time + inf.kernel_time;
+	else
+	  s += inf.kernel_time, u += inf.user_time;
+      }
+
+  *idle = i;
+  *system = s;
+  *user = u;
+}
+
+int
+getloadavg (double loadavg[], int nelem)
+{
+  int elem;
+  bigtime_t idle, kernel, user;
+  time_t now = time (NULL);
+
+  if (num_of_processors <= 0)
+    {
+      system_info i;
+      if (get_system_info (&i) == B_OK)
+	num_of_processors = i.cpu_count;
+    }
+
+  /* If system time jumped back for some reason, delete all samples
+     whose time is later than the current wall-clock time.  This
+     prevents load average figures from becoming frozen for prolonged
+     periods of time, when system time is reset backwards.  */
+  if (last_idx >= 0)
+    {
+      while (difftime (now, samples[last_idx].sample_time) < -1.0)
+	{
+	  if (last_idx == first_idx)
+	    {
+	      first_idx = last_idx = -1;
+	      break;
+	    }
+	  last_idx = buf_prev (last_idx);
+	}
+    }
+
+  /* Store another sample.  We ignore samples that are less than 1 sec
+     apart.  */
+  if (last_idx < 0
+      || (difftime (now, samples[last_idx].sample_time)
+	  >= 1.0 - 2 * DBL_EPSILON * now))
+    {
+      sample_sys_load (&idle, &kernel, &user);
+      last_idx = buf_next (last_idx);
+      samples[last_idx].sample_time = now;
+      samples[last_idx].idle = idle;
+      samples[last_idx].kernel = kernel;
+      samples[last_idx].user = user;
+      /* If the buffer has more that 15 min worth of samples, discard
+	 the old ones.  */
+      if (first_idx == -1)
+	first_idx = last_idx;
+      while (first_idx != last_idx
+	     && (difftime (now, samples[first_idx].sample_time)
+	         >= 15.0 * 60 + 2 * DBL_EPSILON * now))
+	first_idx = buf_next (first_idx);
+    }
+
+  for (elem = 0; elem < nelem; elem++)
+    {
+      double avg = getavg (elem);
+
+      if (avg < 0)
+	break;
+      loadavg[elem] = avg;
+    }
+
+  /* Always return at least one element, otherwise load-average
+     returns nil, and Lisp programs might decide we cannot measure
+     system load.  For example, jit-lock-stealth-load's defcustom
+     might decide that feature is "unsupported".  */
+  if (elem == 0)
+    loadavg[elem++] = 0.09;	/* < display-time-load-average-threshold */
+
+  return elem;
+}
+
+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;
+  bool face_change_before = face_change;
+  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 (ttip_p)
+    f = make_frame (0);
+  else if (EQ (tem, Qnone) || NILP (tem))
+      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;
+
+  if (ttip_p)
+    f->wants_modeline = false;
+
+  fset_icon_name (f, gui_display_get_arg (dpyinfo, parms, Qicon_name,
+                                          "iconName", "Title",
+                                          RES_TYPE_STRING));
+  if (! STRINGP (f->icon_name) || ttip_p)
+    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 USE_BE_CAIRO
+  register_font_driver (&ftcrfont_driver, f);
+#ifdef HAVE_HARFBUZZ
+  register_font_driver (&ftcrhbfont_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_modeline ());
+  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);
+    }
+  else
+    {
+      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);
+
+  if (ttip_p)
+    {
+      Lisp_Object bg = Fframe_parameter (frame, Qbackground_color);
+
+      call2 (Qface_set_after_frame_default, frame, Qnil);
+
+      if (!EQ (bg, Fframe_parameter (frame, Qbackground_color)))
+	{
+	  AUTO_FRAME_ARG (arg, Qbackground_color, bg);
+	  Fmodify_frame_parameters (frame, arg);
+	}
+    }
+
+  if (ttip_p)
+    face_change = face_change_before;
+
+  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 (!ttip_p)
+    {
+      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 && !NILP (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 (!NILP (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_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 ();
+}
+
+
+
+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);
+
+  int planes = be_get_display_planes ();
+
+  if (planes == 8)
+    return intern ("static-color");
+  else if (planes == 16 || planes == 15)
+    return intern ("pseudo-color");
+
+  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;
+
+  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 (haiku_use_system_tooltips)
+    {
+      int root_x, root_y;
+      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 ();
+
+      char *str = xstrdup (SSDATA (string));
+      int height = be_plain_font_height ();
+      int width;
+      char *tok = strtok (str, "\n");
+      width = be_string_width_with_plain_font (tok);
+
+      while ((tok = strtok (NULL, "\n")))
+	{
+	  height = be_plain_font_height ();
+	  int w = be_string_width_with_plain_font (tok);
+	  if (w > width)
+	    w = width;
+	}
+      free (str);
+
+      height += 16; /* Default margin.  */
+      width += 16; /* Ditto.  Unfortunately there isn't a more
+		      reliable way to get it.  */
+      compute_tip_xy (f, parms, dx, dy, width, height, &root_x, &root_y);
+      BView_convert_from_screen (FRAME_HAIKU_VIEW (f), &root_x, &root_y);
+      BView_set_and_show_sticky_tooltip (FRAME_HAIKU_VIEW (f), SSDATA (string),
+					 root_x, root_y);
+      unblock_input ();
+      goto start_timer;
+    }
+
+  if (!NILP (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 (NILP (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;
+  flush_frame (tip_f);
+  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 (XCAR (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 (be_get_display_screens ());
+}
+
+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 FRAME_PARENT_FRAME (f) ? Qwhen_mapped : 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.
+The frame being used will be brought to the front of
+the display after the file panel is closed.
+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);
+
+  block_input ();
+  BWindow_activate (FRAME_HAIKU_WINDOW (f));
+  unblock_input ();
+
+  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");
+  DEFSYM (Qwhen_mapped, "when-mapped");
+
+  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;
+
+#ifdef USE_BE_CAIRO
+  DEFVAR_LISP ("cairo-version-string", Vcairo_version_string,
+               doc: /* Version info for cairo.  */);
+  {
+    char cairo_version[sizeof ".." + 3 * INT_STRLEN_BOUND (int)];
+    int len = sprintf (cairo_version, "%d.%d.%d",
+		       CAIRO_VERSION_MAJOR, CAIRO_VERSION_MINOR,
+                       CAIRO_VERSION_MICRO);
+    Vcairo_version_string = make_pure_string (cairo_version, len, len, false);
+  }
+#endif
+
+  return;
+}
diff --git a/src/haikufont.c b/src/haikufont.c
new file mode 100644
index 0000000000..811fa62a84
--- /dev/null
+++ b/src/haikufont.c
@@ -0,0 +1,1072 @@
+/* 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 font_cache;
+
+#define METRICS_NCOLS_PER_ROW	(128)
+
+enum metrics_status
+  {
+    METRICS_INVALID = -1,    /* metrics entry is invalid */
+  };
+
+#define METRICS_STATUS(metrics)	((metrics)->ascent + (metrics)->descent)
+#define METRICS_SET_STATUS(metrics, status) \
+  ((metrics)->ascent = 0, (metrics)->descent = (status))
+
+static struct
+{
+  /* registry name */
+  const char *name;
+  /* characters to distinguish the charset from the others */
+  int uniquifier[6];
+  /* additional constraint by language */
+  const char *lang;
+} em_charset_table[] =
+  { { "iso8859-1", { 0x00A0, 0x00A1, 0x00B4, 0x00BC, 0x00D0 } },
+    { "iso8859-2", { 0x00A0, 0x010E }},
+    { "iso8859-3", { 0x00A0, 0x0108 }},
+    { "iso8859-4", { 0x00A0, 0x00AF, 0x0128, 0x0156, 0x02C7 }},
+    { "iso8859-5", { 0x00A0, 0x0401 }},
+    { "iso8859-6", { 0x00A0, 0x060C }},
+    { "iso8859-7", { 0x00A0, 0x0384 }},
+    { "iso8859-8", { 0x00A0, 0x05D0 }},
+    { "iso8859-9", { 0x00A0, 0x00A1, 0x00BC, 0x011E }},
+    { "iso8859-10", { 0x00A0, 0x00D0, 0x0128, 0x2015 }},
+    { "iso8859-11", { 0x00A0, 0x0E01 }},
+    { "iso8859-13", { 0x00A0, 0x201C }},
+    { "iso8859-14", { 0x00A0, 0x0174 }},
+    { "iso8859-15", { 0x00A0, 0x00A1, 0x00D0, 0x0152 }},
+    { "iso8859-16", { 0x00A0, 0x0218}},
+    { "gb2312.1980-0", { 0x4E13 }, "zh-cn"},
+    { "big5-0", { 0x9C21 }, "zh-tw" },
+    { "jisx0208.1983-0", { 0x4E55 }, "ja"},
+    { "ksc5601.1985-0", { 0xAC00 }, "ko"},
+    { "cns11643.1992-1", { 0xFE32 }, "zh-tw"},
+    { "cns11643.1992-2", { 0x4E33, 0x7934 }},
+    { "cns11643.1992-3", { 0x201A9 }},
+    { "cns11643.1992-4", { 0x20057 }},
+    { "cns11643.1992-5", { 0x20000 }},
+    { "cns11643.1992-6", { 0x20003 }},
+    { "cns11643.1992-7", { 0x20055 }},
+    { "gbk-0", { 0x4E06 }, "zh-cn"},
+    { "jisx0212.1990-0", { 0x4E44 }},
+    { "jisx0213.2000-1", { 0xFA10 }, "ja"},
+    { "jisx0213.2000-2", { 0xFA49 }},
+    { "jisx0213.2004-1", { 0x20B9F }},
+    { "viscii1.1-1", { 0x1EA0, 0x1EAE, 0x1ED2 }, "vi"},
+    { "tis620.2529-1", { 0x0E01 }, "th"},
+    { "microsoft-cp1251", { 0x0401, 0x0490 }, "ru"},
+    { "koi8-r", { 0x0401, 0x2219 }, "ru"},
+    { "mulelao-1", { 0x0E81 }, "lo"},
+    { "unicode-sip", { 0x20000 }},
+    { "mulearabic-0", { 0x628 }},
+    { "mulearabic-1", { 0x628 }},
+    { "mulearabic-2", { 0x628 }},
+    { NULL }
+  };
+
+static void
+haikufont_apply_registry (struct haiku_font_pattern *pattern,
+			  Lisp_Object registry)
+{
+  char *str = SSDATA (SYMBOL_NAME (registry));
+  USE_SAFE_ALLOCA;
+  char *re = SAFE_ALLOCA (SBYTES (SYMBOL_NAME (registry)) * 2 + 1);
+  int i, j;
+
+  for (i = j = 0; i < SBYTES (SYMBOL_NAME (registry)); i++, j++)
+    {
+      if (str[i] == '.')
+	re[j++] = '\\';
+      else if (str[i] == '*')
+	re[j++] = '.';
+      re[j] = str[i];
+      if (re[j] == '?')
+	re[j] = '.';
+    }
+  re[j] = '\0';
+  AUTO_STRING_WITH_LEN (regexp, re, j);
+  for (i = 0; em_charset_table[i].name; i++)
+    if (fast_c_string_match_ignore_case
+	(regexp, em_charset_table[i].name,
+	 strlen (em_charset_table[i].name)) >= 0)
+      break;
+  SAFE_FREE ();
+  if (!em_charset_table[i].name)
+    return;
+  int *uniquifier = em_charset_table[i].uniquifier;
+  int l;
+
+  for (l = 0; uniquifier[l]; ++l);
+
+  uint32_t *a = xmalloc (l * sizeof *a);
+  for (l = 0; uniquifier[l]; ++l)
+    a[l] = uniquifier[l];
+
+  if (pattern->specified & FSPEC_WANTED)
+    {
+      int old_l = l;
+      l += pattern->want_chars_len;
+      a = xrealloc (a, l * sizeof *a);
+      memcpy (&a[old_l], pattern->wanted_chars, (l - old_l) * sizeof *a);
+      xfree (pattern->wanted_chars);
+    }
+  pattern->specified |= FSPEC_WANTED;
+  pattern->want_chars_len = l;
+  pattern->wanted_chars = a;
+
+  if (em_charset_table[i].lang)
+    {
+      if (!strncmp (em_charset_table[i].lang, "zh", 2))
+	{
+	  pattern->specified |= FSPEC_LANGUAGE;
+	  pattern->language = LANGUAGE_CN;
+	}
+      else if (!strncmp (em_charset_table[i].lang, "ko", 2))
+	{
+	  pattern->specified |= FSPEC_LANGUAGE;
+	  pattern->language = LANGUAGE_KO;
+	}
+      else if (!strncmp (em_charset_table[i].lang, "ja", 2))
+	{
+	  pattern->specified |= FSPEC_LANGUAGE;
+	  pattern->language = LANGUAGE_JP;
+	}
+    }
+
+  return;
+}
+
+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, Qutf_8);
+  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 font_cache;
+}
+
+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;
+    case HAIKU_BOOK:
+      return Qbook;
+    case HAIKU_HEAVY:
+      return Qheavy;
+    case HAIKU_ULTRA_HEAVY:
+      return Qultra_heavy;
+    case HAIKU_BLACK:
+      return Qblack;
+    case HAIKU_MEDIUM:
+      return Qmedium;
+    }
+  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;
+  if (EQ (weight, Qbook))
+    return HAIKU_BOOK;
+  if (EQ (weight, Qheavy))
+    return HAIKU_HEAVY;
+  if (EQ (weight, Qultra_heavy))
+    return HAIKU_ULTRA_HEAVY;
+  if (EQ (weight, Qblack))
+    return HAIKU_BLACK;
+  if (EQ (weight, Qmedium))
+    return HAIKU_MEDIUM;
+
+  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, Qutf_8);
+  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;
+	}
+    }
+
+  tem = AREF (ent, FONT_REGISTRY_INDEX);
+  if (SYMBOLP (tem))
+    haikufont_apply_registry (ptn, tem);
+}
+
+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;
+
+  /* Returning irrelevant results on receiving an OTF form will cause
+     fontset.c to loop over and over, making displaying some
+     characters very slow.  */
+  Lisp_Object tem = assq_no_quit (QCotf, AREF (font_spec, FONT_EXTRA_INDEX));
+  if (CONSP (tem) && !NILP (XCDR (tem)))
+    {
+      unblock_input ();
+      return 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 void
+haiku_bulk_encode (struct haikufont_info *font_info, int block)
+{
+  unsigned short *unichars = xmalloc (0x101 * sizeof (*unichars));
+  unsigned int i, idx;
+
+  block_input ();
+
+  font_info->glyphs[block] = unichars;
+  if (!unichars)
+    emacs_abort ();
+
+  for (idx = block << 8, i = 0; i < 0x100; idx++, i++)
+    unichars[i] = idx;
+  unichars[0x100] = 0;
+
+
+  /* If the font contains the entire block, just store it.  */
+  if (!BFont_have_char_block (font_info->be_font,
+			      unichars[0], unichars[0xff]))
+    {
+      for (int i = 0; i < 0x100; ++i)
+	if (!BFont_have_char_p (font_info->be_font, unichars[i]))
+	  unichars[i] = 0xFFFF;
+    }
+
+  unblock_input ();
+}
+
+static unsigned int
+haikufont_encode_char (struct font *font, int c)
+{
+  struct haikufont_info *font_info = (struct haikufont_info *) font;
+  unsigned char high = (c & 0xff00) >> 8, low = c & 0x00ff;
+  unsigned short g;
+
+  if (c > 0xFFFF)
+    return FONT_INVALID_CODE;
+
+  if (!font_info->glyphs[high])
+    haiku_bulk_encode (font_info, high);
+  g = font_info->glyphs[high][low];
+  return g == 0xFFFF ? FONT_INVALID_CODE : g;
+}
+
+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;
+
+  if (!font)
+    {
+      unblock_input ();
+      return Qnil;
+    }
+
+  font_info->be_font = be_font;
+  font_info->glyphs = xzalloc (0x100 * sizeof *font_info->glyphs);
+
+  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_info->metrics = NULL;
+  font_info->metrics_nrows = 0;
+
+  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);
+
+  for (int i = 0; i < info->metrics_nrows; i++)
+    if (info->metrics[i])
+      xfree (info->metrics[i]);
+  if (info->metrics)
+    xfree (info->metrics);
+  for (int i = 0; i < 0x100; ++i)
+    if (info->glyphs[i])
+      xfree (info->glyphs[i]);
+  xfree (info->glyphs);
+  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;
+
+  struct font_metrics *cache;
+  int row, col;
+
+  row = code / METRICS_NCOLS_PER_ROW;
+  col = code % METRICS_NCOLS_PER_ROW;
+  if (row >= info->metrics_nrows)
+    {
+      info->metrics =
+	xrealloc (info->metrics,
+		  sizeof (struct font_metrics *) * (row + 1));
+      memset (info->metrics + info->metrics_nrows, 0,
+	      (sizeof (struct font_metrics *)
+	       * (row + 1 - info->metrics_nrows)));
+      info->metrics_nrows = row + 1;
+    }
+
+  if (info->metrics[row] == NULL)
+    {
+      struct font_metrics *new;
+      int i;
+
+      new = xmalloc (sizeof (struct font_metrics) * METRICS_NCOLS_PER_ROW);
+      for (i = 0; i < METRICS_NCOLS_PER_ROW; i++)
+	METRICS_SET_STATUS (new + i, METRICS_INVALID);
+      info->metrics[row] = new;
+    }
+  cache = info->metrics[row] + col;
+
+  if (METRICS_STATUS (cache) == METRICS_INVALID)
+    {
+      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);
+
+      cache->lbearing = lb;
+      cache->rbearing = rb;
+      cache->width = advance;
+      cache->ascent = font->ascent;
+      cache->descent = font->descent;
+    }
+
+  if (metrics)
+    *metrics = *cache;
+}
+
+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);
+
+  block_input ();
+  prepare_face_for_display (s->f, face);
+
+  BView_draw_lock (view);
+  BView_StartClip (view);
+  if (with_background)
+    {
+      int height = FONT_HEIGHT (s->font), ascent = FONT_BASE (s->font);
+
+      /* Font's global height and ascent values might be
+	 preposterously large for some fonts.  We fix here the case
+	 when those fonts are used for display of glyphless
+	 characters, because drawing background with font dimensions
+	 in those cases makes the display illegible.  There's only one
+	 more call to the draw method with with_background set to
+	 true, and that's in x_draw_glyph_string_foreground, when
+	 drawing the cursor, where we have no such heuristics
+	 available.  FIXME.  */
+      if (s->first_glyph->type == GLYPHLESS_GLYPH
+	  && (s->first_glyph->u.glyphless.method == GLYPHLESS_DISPLAY_HEX_CODE
+	      || s->first_glyph->u.glyphless.method == GLYPHLESS_DISPLAY_ACRONYM))
+	height = ascent =
+	  s->first_glyph->slice.glyphless.lower_yoff
+	  - s->first_glyph->slice.glyphless.upper_yoff;
+
+      BView_SetHighColor (view, s->hl == DRAW_CURSOR ?
+			  FRAME_CURSOR_COLOR (s->f).pixel : face->background);
+
+      BView_FillRectangle (view, x, y - ascent, s->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,
+    .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");
+
+  font_cache = list (Qnil);
+  staticpro (&font_cache);
+}
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..138e5b84e6
--- /dev/null
+++ b/src/haikuimage.c
@@ -0,0 +1,109 @@
+/* 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 "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";
+  else if (EQ (type, Qpbm))
+    mime_type = "image/pbm";
+
+  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..698da9d639
--- /dev/null
+++ b/src/haikumenu.c
@@ -0,0 +1,656 @@
+/* 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, help;
+	  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);
+	  help = AREF (menu_items, i + MENU_ITEMS_ITEM_HELP);
+
+	  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 (STRINGP (help) && STRING_MULTIBYTE (help))
+	    {
+	      help = ENCODE_UTF_8 (help);
+	      ASET (menu_items, i + MENU_ITEMS_ITEM_HELP, help);
+	    }
+
+	  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,
+			    STRINGP (help) ? SSDATA (help) : 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,
+			    STRINGP (help) ? SSDATA (help) : 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);
+  if (STRINGP (title))
+    {
+      BMenu_add_title (menu, SSDATA (title));
+      BMenu_add_separator (menu);
+    }
+  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;
+
+  block_input ();
+  if (!FRAME_OUTPUT_DATA (f)->menu_up_to_date_p)
+    {
+      unblock_input ();
+      return;
+    }
+
+  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..3f0441e077
--- /dev/null
+++ b/src/haikuselect.c
@@ -0,0 +1,134 @@
+/* Haiku window system selection 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 "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..542d550d64
--- /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..5764d8aa1c
--- /dev/null
+++ b/src/haikuterm.c
@@ -0,0 +1,3605 @@
+/* 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>
+#include <stdlib.h>
+
+#ifdef USE_BE_CAIRO
+#include <cairo.h>
+#endif
+
+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_update_size_hints (struct frame *f)
+{
+  int base_width, base_height;
+  eassert (FRAME_HAIKU_P (f) && FRAME_HAIKU_WINDOW (f));
+
+  base_width = FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, 0);
+  base_height = FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, 0);
+
+  block_input ();
+  BWindow_set_size_alignment (FRAME_HAIKU_WINDOW (f),
+			      frame_resize_pixelwise ? 1 : FRAME_COLUMN_WIDTH (f),
+			      frame_resize_pixelwise ? 1 : FRAME_LINE_HEIGHT (f));
+  BWindow_set_min_size (FRAME_HAIKU_WINDOW (f), base_width,
+			base_height
+			+ FRAME_TOOL_BAR_HEIGHT (f)
+			+ FRAME_MENU_BAR_HEIGHT (f));
+  unblock_input ();
+}
+
+static void
+haiku_clip_to_string (struct glyph_string *s)
+{
+  struct haiku_rect r[2];
+  int n = get_glyph_string_clip_rects (s, (struct haiku_rect *) &r, 2);
+
+  if (n)
+    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);
+}
+
+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);
+  BView_ClipToRect (vw, x, y, width, height);
+  BView_SetHighColor (vw, FRAME_BACKGROUND_PIXEL (f));
+  BView_FillRectangle (vw, x, y, width, height);
+  BView_EndClip (vw);
+  BView_draw_unlock (vw);
+  unblock_input ();
+}
+
+static void
+haiku_clear_frame (struct frame *f)
+{
+  void *view = FRAME_HAIKU_VIEW (f);
+  block_input ();
+  BView_draw_lock (view);
+  BView_StartClip (view);
+  BView_ClipToRect (view, 0, 0, FRAME_PIXEL_WIDTH (f),
+		    FRAME_PIXEL_HEIGHT (f));
+  BView_SetHighColor (view, FRAME_BACKGROUND_PIXEL (f));
+  BView_FillRectangle (view, 0, 0, FRAME_PIXEL_WIDTH (f),
+		       FRAME_PIXEL_HEIGHT (f));
+  BView_EndClip (view);
+  BView_draw_unlock (view);
+  unblock_input ();
+}
+
+/* Give frame F the font FONT-OBJECT as its default font.  The return
+   value is FONT-OBJECT.  FONTSET is an ID of the fontset for the
+   frame.  If it is negative, generate a new fontset from
+   FONT-OBJECT.  */
+
+static Lisp_Object
+haiku_new_font (struct frame *f, Lisp_Object font_object, int fontset)
+{
+  struct font *font = 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 int
+haiku_valid_modifier_p (Lisp_Object sym)
+{
+  return EQ (sym, Qcommand) || EQ (sym, Qshift)
+    || EQ (sym, Qcontrol) || EQ (sym, Qoption);
+}
+
+#define MODIFIER_OR(obj, def) (haiku_valid_modifier_p (obj) ? obj : def)
+
+static void
+haiku_add_modifier (int modifier, int toput, Lisp_Object qtem, int *modifiers)
+{
+  if ((modifier & HAIKU_MODIFIER_ALT && EQ (qtem, Qcommand)) ||
+      (modifier & HAIKU_MODIFIER_SHIFT && EQ (qtem, Qshift)) ||
+      (modifier & HAIKU_MODIFIER_CTRL && EQ (qtem, Qcontrol)) ||
+      (modifier & HAIKU_MODIFIER_SUPER && EQ (qtem, Qoption)))
+    *modifiers |= toput;
+}
+
+static int
+haiku_modifiers_to_emacs (int haiku_key)
+{
+  int modifiers = 0;
+  haiku_add_modifier (haiku_key, shift_modifier,
+		      MODIFIER_OR (Vhaiku_shift_keysym, Qshift), &modifiers);
+  haiku_add_modifier (haiku_key, super_modifier,
+		      MODIFIER_OR (Vhaiku_super_keysym, Qoption), &modifiers);
+  haiku_add_modifier (haiku_key, meta_modifier,
+		      MODIFIER_OR (Vhaiku_meta_keysym, Qcommand), &modifiers);
+  haiku_add_modifier (haiku_key, ctrl_modifier,
+		      MODIFIER_OR (Vhaiku_control_keysym, Qcontrol), &modifiers);
+  return modifiers;
+}
+
+#undef MODIFIER_OR
+
+static void
+haiku_rehighlight (void)
+{
+  eassert (x_display_list && !x_display_list->next);
+
+  block_input ();
+
+  struct frame *old_hl = x_display_list->highlight_frame;
+
+  if (x_display_list->focused_frame)
+    {
+      x_display_list->highlight_frame
+	= ((FRAMEP (FRAME_FOCUS_FRAME (x_display_list->focused_frame)))
+	   ? XFRAME (FRAME_FOCUS_FRAME (x_display_list->focused_frame))
+	   : x_display_list->focused_frame);
+      if (!FRAME_LIVE_P (x_display_list->highlight_frame))
+	{
+	  fset_focus_frame (x_display_list->focused_frame, Qnil);
+	  x_display_list->highlight_frame = x_display_list->focused_frame;
+	}
+    }
+  else
+    x_display_list->highlight_frame = 0;
+
+  if (old_hl)
+    gui_update_cursor (old_hl, true);
+
+  if (x_display_list->highlight_frame)
+    gui_update_cursor (x_display_list->highlight_frame, true);
+  unblock_input ();
+}
+
+static void
+haiku_frame_raise_lower (struct frame *f, bool raise_p)
+{
+  if (raise_p)
+    {
+      block_input ();
+      BWindow_activate (FRAME_HAIKU_WINDOW (f));
+      flush_frame (f);
+      unblock_input ();
+    }
+}
+
+/* Unfortunately, NOACTIVATE is not implementable on Haiku.  */
+static void
+haiku_focus_frame (struct frame *frame, bool noactivate)
+{
+  if (x_display_list->focused_frame != frame)
+    haiku_frame_raise_lower (frame, 1);
+}
+
+static void
+haiku_new_focus_frame (struct frame *frame)
+{
+  eassert (x_display_list && !x_display_list->next);
+
+  block_input ();
+  if (frame != x_display_list->focused_frame)
+    {
+      if (x_display_list->focused_frame &&
+	  x_display_list->focused_frame->auto_lower)
+	haiku_frame_raise_lower (x_display_list->focused_frame, 0);
+
+      x_display_list->focused_frame = frame;
+
+      if (frame && frame->auto_raise)
+	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);
+}
+
+/* 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);
+  struct face *face = s->face;
+
+  BView_StartClip (view);
+  BView_SetHighColor (view, face->box_color);
+  if (clip_rect)
+    BView_ClipToRect (view, clip_rect->x, clip_rect->y, clip_rect->width,
+		      clip_rect->height);
+  BView_FillRectangle (view, left_x, top_y, right_x - left_x + 1, hwidth);
+  if (left_p)
+    BView_FillRectangle (view, left_x, top_y, vwidth, bottom_y - top_y + 1);
+
+  BView_FillRectangle (view, left_x, bottom_y - hwidth + 1,
+		       right_x - left_x + 1, hwidth);
+  if (right_p)
+    BView_FillRectangle (view, right_x - vwidth + 1,
+			 top_y, vwidth, bottom_y - top_y + 1);
+  BView_EndClip (view);
+}
+
+static void
+haiku_calculate_relief_colors (struct glyph_string *s,
+			       uint32_t *rgbout_w, uint32_t *rgbout_b,
+			       uint32_t *rgbout_c)
+{
+  struct 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.2), rgbout_w);
+  hsl_color_rgb (h, cs, fmin (1.0, fmax (0.2, l) * 1.8), rgbout_c);
+}
+
+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, bool fancy_p)
+{
+  uint32_t color_white;
+  uint32_t color_black;
+  uint32_t color_corner;
+
+  haiku_calculate_relief_colors (s, &color_white, &color_black,
+				 &color_corner);
+
+  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);
+
+  /* Draw the triangle for the bottom-left corner.  */
+  if (bot_p && left_p)
+    {
+      BView_SetHighColor (view, raised_p ? color_white : color_black);
+      BView_FillTriangle (view, left_x, bottom_y - hwidth, left_x + vwidth,
+			  bottom_y - hwidth, left_x, bottom_y);
+    }
+
+  /* Now draw the triangle for the top-right corner.  */
+  if (top_p && right_p)
+    {
+      BView_SetHighColor (view, raised_p ? color_white : color_black);
+      BView_FillTriangle (view, right_x - vwidth, top_y,
+			  right_x, top_y,
+			  right_x - vwidth, top_y + hwidth);
+    }
+
+  /* If (h/v)width is > 1, we draw the outer-most line on each side in the
+     black relief color.  */
+
+  BView_SetHighColor (view, color_black);
+
+  if (hwidth > 1 && top_p)
+    BView_StrokeLine (view, left_x, top_y, right_x, top_y);
+  if (hwidth > 1 && bot_p)
+    BView_StrokeLine (view, left_x, bottom_y, right_x, bottom_y);
+  if (vwidth > 1 && left_p)
+    BView_StrokeLine (view, left_x, top_y, left_x, bottom_y);
+  if (vwidth > 1 && right_p)
+    BView_StrokeLine (view, right_x, top_y, right_x, bottom_y);
+
+  BView_SetHighColor (view, color_corner);
+
+  /* Omit corner pixels.  */
+  if (hwidth > 1 || vwidth > 1)
+    {
+      if (left_p && top_p)
+	BView_FillRectangle (view, left_x, top_y, 1, 1);
+      if (left_p && bot_p)
+	BView_FillRectangle (view, left_x, bottom_y, 1, 1);
+      if (right_p && top_p)
+	BView_FillRectangle (view, right_x, top_y, 1, 1);
+      if (right_p && bot_p)
+	BView_FillRectangle (view, right_x, bottom_y, 1, 1);
+    }
+
+  BView_EndClip (view);
+}
+
+static void
+haiku_draw_string_box (struct glyph_string *s, int clip_p)
+{
+  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;
+
+  struct face *face = s->face;
+
+  last_x = ((s->row->full_width_p && !s->w->pseudo_window_p)
+	    ? WINDOW_RIGHT_EDGE_X (s->w)
+	    : window_box_right (s->w, s->area));
+
+  /* The glyph that may have a right box line.  For static
+     compositions and images, the right-box flag is on the first glyph
+     of the glyph string; for other types it's on the last glyph.  */
+  if (s->cmp || s->img)
+    last_glyph = s->first_glyph;
+  else if (s->first_glyph->type == COMPOSITE_GLYPH
+	   && s->first_glyph->u.cmp.automatic)
+    {
+      /* For automatic compositions, we need to look up the last glyph
+	 in the composition.  */
+        struct glyph *end = s->row->glyphs[s->area] + s->row->used[s->area];
+	struct glyph *g = s->first_glyph;
+	for (last_glyph = g++;
+	     g < end && g->u.cmp.automatic && g->u.cmp.id == s->cmp_id
+	       && g->slice.cmp.to < s->cmp_to;
+	     last_glyph = g++)
+	  ;
+    }
+  else
+    last_glyph = s->first_glyph + s->nchars - 1;
+
+  vwidth = eabs (face->box_vertical_line_width);
+  hwidth = eabs (face->box_horizontal_line_width);
+  raised_p = face->box == FACE_RAISED_BOX;
+  left_x = s->x;
+  right_x = (s->row->full_width_p && s->extends_to_end_of_line_p
+	     ? last_x - 1
+	     : min (last_x, s->x + s->background_width) - 1);
+
+  top_y = s->y;
+  bottom_y = top_y + s->height - 1;
+
+  left_p = (s->first_glyph->left_box_line_p
+	    || (s->hl == DRAW_MOUSE_FACE
+		&& (s->prev == NULL
+		    || s->prev->hl != s->hl)));
+  right_p = (last_glyph->right_box_line_p
+	     || (s->hl == DRAW_MOUSE_FACE
+		 && (s->next == NULL
+		     || s->next->hl != s->hl)));
+
+  get_glyph_string_clip_rect (s, &clip_rect);
+
+  if (face->box == FACE_SIMPLE_BOX)
+    haiku_draw_box_rect (s, left_x, top_y, right_x, bottom_y, hwidth,
+			 vwidth, left_p, right_p, &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, 1);
+
+  if (clip_p)
+    {
+      void *view = FRAME_HAIKU_VIEW (s->f);
+      BView_ClipToInverseRect (view, left_x, top_y, right_x - left_x + 1, hwidth);
+      if (left_p)
+	BView_ClipToInverseRect (view, left_x, top_y, vwidth, bottom_y - top_y + 1);
+      BView_ClipToInverseRect (view, left_x, bottom_y - hwidth + 1,
+			       right_x - left_x + 1, hwidth);
+      if (right_p)
+	BView_ClipToInverseRect (view, right_x - vwidth + 1,
+				 top_y, vwidth, bottom_y - top_y + 1);
+    }
+}
+
+static void
+haiku_draw_plain_background (struct glyph_string *s, struct face *face,
+			     int box_line_hwidth, int box_line_vwidth)
+{
+  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_hwidth,
+		       s->background_width,
+		       s->height - 2 * box_line_hwidth);
+  BView_EndClip (view);
+}
+
+static void
+haiku_draw_stipple_background (struct glyph_string *s, struct face *face,
+			       int box_line_hwidth, int box_line_vwidth)
+{
+}
+
+static void
+haiku_maybe_draw_background (struct glyph_string *s, int force_p)
+{
+  if ((s->first_glyph->type != IMAGE_GLYPH) && !s->background_filled_p)
+    {
+      struct face *face = s->face;
+      int box_line_width = max (face->box_horizontal_line_width, 0);
+      int box_vline_width = max (face->box_vertical_line_width, 0);
+
+      if (FONT_HEIGHT (s->font) < s->height - 2 * box_vline_width
+	  || FONT_TOO_HIGH (s->font)
+          || s->font_not_found_p || s->extends_to_end_of_line_p || force_p)
+	{
+	  if (!face->stipple)
+	    haiku_draw_plain_background (s, face, box_line_width,
+					 box_vline_width);
+	  else
+	    haiku_draw_stipple_background (s, face, box_line_width,
+					   box_vline_width);
+	  s->background_filled_p = 1;
+	}
+    }
+}
+
+static void
+haiku_mouse_face_colors (struct glyph_string *s, uint32_t *fg,
+			 uint32_t *bg)
+{
+  int face_id;
+  struct face *face;
+
+  /* What face has to be used last for the mouse face?  */
+  face_id = MOUSE_HL_INFO (s->f)->mouse_face_face_id;
+  face = FACE_FROM_ID_OR_NULL (s->f, face_id);
+  if (face == NULL)
+    face = FACE_FROM_ID (s->f, MOUSE_FACE_ID);
+
+  if (s->first_glyph->type == CHAR_GLYPH)
+    face_id = FACE_FOR_CHAR (s->f, face, s->first_glyph->u.ch, -1, Qnil);
+  else
+    face_id = FACE_FOR_CHAR (s->f, face, 0, -1, Qnil);
+
+  face = FACE_FROM_ID (s->f, face_id);
+  prepare_face_for_display (s->f, s->face);
+
+  if (fg)
+    *fg = face->foreground;
+  if (bg)
+    *bg = face->background;
+}
+
+static void
+haiku_draw_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 (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 (face->underline == FACE_UNDER_WAVE)
+	haiku_draw_underwave (s, width, x);
+      else if (face->underline == FACE_UNDER_LINE)
+	{
+	  unsigned long thickness, position;
+	  int y;
+
+	  if (s->prev && s->prev && s->prev->hl == DRAW_MOUSE_FACE)
+	    {
+	      struct face *prev_face = s->prev->face;
+
+	      if (prev_face && 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
+		goto calculate_underline_metrics;
+	    }
+	  else
+	    {
+	    calculate_underline_metrics:;
+	      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 (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 (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->strike_through_color_defaulted_p)
+	BView_SetHighColor (view, face->strike_through_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_glyph_string_foreground (struct glyph_string *s)
+{
+  struct face *face = s->face;
+
+  int i, x;
+  if (face->box != FACE_NO_BOX
+      && s->first_glyph->left_box_line_p)
+    x = s->x + max (face->box_vertical_line_width, 0);
+  else
+    x = s->x;
+
+  void *view = FRAME_HAIKU_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, 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 (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;
+  struct face *face = s->face;
+
+  /* If first glyph of S has a left box line, start drawing the text
+     of S to the right of that box line.  */
+  if (face && face->box != FACE_NO_BOX
+      && s->first_glyph->left_box_line_p)
+    x = s->x + max (face->box_vertical_line_width, 0);
+  else
+    x = s->x;
+
+  s->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);
+
+  struct face *face = s->face;
+
+  if (s->hl == DRAW_CURSOR && !x_stretch_cursor_p)
+    {
+      int width, background_width = s->background_width;
+      int x = s->x;
+
+      if (!s->row->reversed_p)
+	{
+	  int left_x = window_box_left_offset (s->w, TEXT_AREA);
+
+	  if (x < left_x)
+	    {
+	      background_width -= left_x - x;
+	      x = left_x;
+	    }
+	}
+      else
+	{
+	  /* In R2L rows, draw the cursor on the right edge of the
+	     stretch glyph.  */
+	  int right_x = window_box_right (s->w, TEXT_AREA);
+	  if (x + background_width > right_x)
+	    background_width -= x - right_x;
+	  x += background_width;
+	}
+
+      width = min (FRAME_COLUMN_WIDTH (s->f), background_width);
+      if (s->row->reversed_p)
+	x -= width;
+
+      void *view = FRAME_HAIKU_VIEW (s->f);
+      BView_StartClip (view);
+      BView_SetHighColor (view, FRAME_CURSOR_COLOR (s->f).pixel);
+      BView_FillRectangle (view, x, s->y, width, s->height);
+      BView_EndClip (view);
+
+      if (width < background_width)
+	{
+	  if (!s->row->reversed_p)
+	    x += width;
+	  else
+	    x = s->x;
+
+	  int y = s->y;
+	  int w = background_width - width, h = s->height;
+
+	  if (!face->stipple)
+	    {
+	      uint32_t bkg;
+	      if (s->hl == DRAW_MOUSE_FACE || (s->hl == DRAW_CURSOR
+					       && s->row->mouse_face_p
+					       && cursor_in_mouse_face_p (s->w)))
+		  haiku_mouse_face_colors (s, NULL, &bkg);
+	      else
+		bkg = face->background;
+
+	      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 (s->w, TEXT_AREA);
+
+      /* Don't draw into left fringe or scrollbar area except for
+         header line and mode line.  */
+      if (s->area == TEXT_AREA
+	  && x < text_left_x && !s->row->mode_line_p)
+	{
+	  background_width -= text_left_x - x;
+	  x = text_left_x;
+	}
+
+      if (background_width > 0)
+	{
+	  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);
+  struct face *face = s->face;
+
+  /* If first glyph of S has a left box line, start drawing the text
+     of S to the right of that box line.  */
+  if (face && face->box != FACE_NO_BOX
+      && s->first_glyph->left_box_line_p)
+    x = s->x + max (face->box_vertical_line_width, 0);
+  else
+    x = s->x;
+
+  /* S is a glyph string for a composition.  S->cmp_from is the index
+     of the first character drawn for glyphs of this composition.
+     S->cmp_from == 0 means we are drawing the very first character of
+     this composition.  */
+
+  /* Draw a rectangle for the composition if the font for the very
+     first character of the composition could not be loaded.  */
+
+  if (s->font_not_found_p && !s->cmp_from)
+    {
+      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 (face->overstrike)
+	      font->driver->draw (s, j, j + 1, xx + 1, yy, false);
+	  }
+    }
+  else
+    {
+      Lisp_Object gstring = composition_gstring_from_id (s->cmp_id);
+      Lisp_Object glyph;
+      int y = s->ybase;
+      int width = 0;
+
+      for (i = j = s->cmp_from; i < s->cmp_to; i++)
+	{
+	  glyph = LGSTRING_GLYPH (gstring, i);
+	  if (NILP (LGLYPH_ADJUSTMENT (glyph)))
+	    width += LGLYPH_WIDTH (glyph);
+	  else
+	    {
+	      int xoff, yoff, wadjust;
+
+	      if (j < i)
+		{
+		  font->driver->draw (s, j, i, x, y, false);
+		  if (s->face->overstrike)
+		    font->driver->draw (s, j, i, x + 1, y, false);
+		  x += width;
+		}
+	      xoff = LGLYPH_XOFF (glyph);
+	      yoff = LGLYPH_YOFF (glyph);
+	      wadjust = LGLYPH_WADJUST (glyph);
+	      font->driver->draw (s, i, i + 1, x + xoff, y + yoff, false);
+	      if (face->overstrike)
+		font->driver->draw (s, i, i + 1, x + xoff + 1, y + yoff,
+				    false);
+	      x += wadjust;
+	      j = i + 1;
+	      width = 0;
+	    }
+	}
+      if (j < i)
+	{
+	  font->driver->draw (s, j, i, x, y, false);
+	  if (face->overstrike)
+	    font->driver->draw (s, j, i, x + 1, y, false);
+	}
+    }
+}
+
+static void
+haiku_draw_image_relief (struct glyph_string *s)
+{
+  int x1, y1, thick;
+  bool raised_p, top_p, bot_p, left_p, right_p;
+  int extra_x, extra_y;
+  struct haiku_rect r;
+  int x = s->x;
+  int y = s->ybase - image_ascent (s->img, s->face, &s->slice);
+
+  struct face *face = s->face;
+
+  /* If first glyph of S has a left box line, start drawing it to the
+     right of that line.  */
+  if (face->box != FACE_NO_BOX
+      && s->first_glyph->left_box_line_p
+      && s->slice.x == 0)
+    x += max (face->box_vertical_line_width, 0);
+
+  /* If there is a margin around the image, adjust x- and y-position
+     by that margin.  */
+  if (s->slice.x == 0)
+    x += s->img->hmargin;
+  if (s->slice.y == 0)
+    y += s->img->vmargin;
+
+  if (s->hl == DRAW_IMAGE_SUNKEN
+      || s->hl == DRAW_IMAGE_RAISED)
+    {
+      if (s->face->id == TAB_BAR_FACE_ID)
+	thick = (tab_bar_button_relief < 0
+		 ? DEFAULT_TAB_BAR_BUTTON_RELIEF
+		 : min (tab_bar_button_relief, 1000000));
+      else
+	thick = (tool_bar_button_relief < 0
+		 ? DEFAULT_TOOL_BAR_BUTTON_RELIEF
+		 : min (tool_bar_button_relief, 1000000));
+      raised_p = s->hl == DRAW_IMAGE_RAISED;
+    }
+  else
+    {
+      thick = eabs (s->img->relief);
+      raised_p = s->img->relief > 0;
+    }
+
+  x1 = x + s->slice.width - 1;
+  y1 = y + s->slice.height - 1;
+
+  extra_x = extra_y = 0;
+
+  if (s->face->id == TAB_BAR_FACE_ID)
+    {
+      if (CONSP (Vtab_bar_button_margin)
+	  && FIXNUMP (XCAR (Vtab_bar_button_margin))
+	  && FIXNUMP (XCDR (Vtab_bar_button_margin)))
+	{
+	  extra_x = XFIXNUM (XCAR (Vtab_bar_button_margin)) - thick;
+	  extra_y = XFIXNUM (XCDR (Vtab_bar_button_margin)) - thick;
+	}
+      else if (FIXNUMP (Vtab_bar_button_margin))
+	extra_x = extra_y = XFIXNUM (Vtab_bar_button_margin) - thick;
+    }
+
+  if (s->face->id == TOOL_BAR_FACE_ID)
+    {
+      if (CONSP (Vtool_bar_button_margin)
+	  && FIXNUMP (XCAR (Vtool_bar_button_margin))
+	  && FIXNUMP (XCDR (Vtool_bar_button_margin)))
+	{
+	  extra_x = XFIXNUM (XCAR (Vtool_bar_button_margin));
+	  extra_y = XFIXNUM (XCDR (Vtool_bar_button_margin));
+	}
+      else if (FIXNUMP (Vtool_bar_button_margin))
+	extra_x = extra_y = XFIXNUM (Vtool_bar_button_margin);
+    }
+
+  top_p = bot_p = left_p = right_p = 0;
+
+  if (s->slice.x == 0)
+    x -= thick + extra_x, left_p = 1;
+  if (s->slice.y == 0)
+    y -= thick + extra_y, top_p = 1;
+  if (s->slice.x + s->slice.width == s->img->width)
+    x1 += thick + extra_x, right_p = 1;
+  if (s->slice.y + s->slice.height == s->img->height)
+    y1 += thick + extra_y, bot_p = 1;
+
+  get_glyph_string_clip_rect (s, &r);
+  haiku_draw_relief_rect (s, x, y, x1, y1, thick, thick, raised_p,
+			  top_p, bot_p, left_p, right_p, &r, 0);
+}
+
+static void
+haiku_draw_image_glyph_string (struct glyph_string *s)
+{
+  struct face *face = s->face;
+
+  int box_line_hwidth = max (face->box_vertical_line_width, 0);
+  int box_line_vwidth = max (face->box_horizontal_line_width, 0);
+
+  int x, y;
+  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 = 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, face, &s->slice);
+
+      if (s->slice.x == 0)
+	x += s->img->hmargin;
+      if (s->slice.y == 0)
+	y += s->img->vmargin;
+
+      if (face->box != FACE_NO_BOX
+	  && s->first_glyph->left_box_line_p
+	  && s->slice.x == 0)
+	x += max (face->box_vertical_line_width, 0);
+
+      ir.x = x;
+      ir.y = y;
+      ir.width = s->slice.width;
+      ir.height = s->slice.height;
+      r = ir;
+
+      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);
+}
+
+static void
+haiku_draw_glyph_string (struct glyph_string *s)
+{
+  block_input ();
+  prepare_face_for_display (s->f, s->face);
+
+  struct face *face = s->face;
+  if (face != s->face)
+    prepare_face_for_display (s->f, 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);
+
+  int box_filled_p = 0;
+
+  if (!s->for_overlaps && face->box != FACE_NO_BOX
+      && (s->first_glyph->type == CHAR_GLYPH
+	  || s->first_glyph->type == COMPOSITE_GLYPH))
+    {
+      haiku_clip_to_string (s);
+      haiku_maybe_draw_background (s, 1);
+      box_filled_p = 1;
+      haiku_draw_string_box (s, 0);
+    }
+  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 (!box_filled_p && face->box != FACE_NO_BOX)
+    haiku_draw_string_box (s, 1);
+
+  if (!s->for_overlaps)
+    {
+      uint32_t dcol;
+      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)
+{
+  haiku_update_size_hints (f);
+
+  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;
+    }
+
+  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 && !p->overlay_p)
+    {
+      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 (!p->overlay_p)
+        {
+	  BView_SetHighColor (view, face->background);
+	  BView_FillRectangle (view, p->x, p->y, p->wd, p->h);
+	}
+
+      BView_SetLowColor (view, col);
+      BView_DrawBitmapWithEraseOp (view, bitmap, p->x, p->y, p->wd, p->h);
+    }
+  BView_EndClip (view);
+  BView_draw_unlock (view);
+}
+
+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);
+#ifdef USE_BE_CAIRO
+  if (EmacsView_double_buffered_p (view))
+    {
+#endif
+      BView_StartClip (view);
+      BView_CopyBits (view, x, from_y, width, height,
+		      x, to_y, width, height);
+      BView_EndClip (view);
+#ifdef USE_BE_CAIRO
+    }
+  else
+    {
+      EmacsWindow_begin_cr_critical_section (FRAME_HAIKU_WINDOW (f));
+      cairo_surface_t *surface = FRAME_CR_SURFACE (f);
+      cairo_surface_t *s
+	= cairo_surface_create_similar (surface,
+					cairo_surface_get_content (surface),
+					width, height);
+      cairo_t *cr = cairo_create (s);
+      if (surface)
+	{
+	  cairo_set_source_surface (cr, surface, -x, -from_y);
+	  cairo_paint (cr);
+	  cairo_destroy (cr);
+
+	  cr = haiku_begin_cr_clip (f, NULL);
+	  cairo_save (cr);
+	  cairo_set_source_surface (cr, s, x, to_y);
+	  cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+	  cairo_rectangle (cr, x, to_y, width, height);
+	  cairo_fill (cr);
+	  cairo_restore (cr);
+	  cairo_surface_destroy (s);
+	  haiku_end_cr_clip (cr);
+	}
+      EmacsWindow_end_cr_critical_section (FRAME_HAIKU_WINDOW (f));
+    }
+#endif
+  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)) &&
+	  !FRAME_TOOLTIP_P (XFRAME (frame)))
+	f1 = XFRAME (frame);
+
+  if (FRAME_TOOLTIP_P (f1))
+    f1 = NULL;
+
+  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)
+      && !FRAME_OUTPUT_DATA (f)->hourglass_p)
+    BView_set_view_cursor (FRAME_HAIKU_VIEW (f), cursor);
+  unblock_input ();
+  FRAME_OUTPUT_DATA (f)->current_cursor = cursor;
+}
+
+static void
+haiku_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 = (const char *[]) { "monospace-12",
+					      "Noto Sans Mono-12",
+					      "Source Code Pro-12",
+					      NULL };
+      int i;
+
+      for (i = 0; names[i]; i++)
+        {
+          font
+            = font_open_by_name (f, build_unibyte_string (names[i]));
+          if (!NILP (font))
+            break;
+        }
+      if (NILP (font))
+        error ("No suitable font was found");
+    }
+  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;
+  static void *buf = NULL;
+  ssize_t b_size;
+  struct unhandled_event *unhandled_events = NULL;
+
+  if (!buf)
+    buf = xmalloc (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_draw_lock (FRAME_HAIKU_VIEW (f));
+	    BView_resize_to (FRAME_HAIKU_VIEW (f), width, height);
+	    BView_draw_unlock (FRAME_HAIKU_VIEW (f));
+	    if (width != FRAME_PIXEL_WIDTH (f)
+		|| height != FRAME_PIXEL_HEIGHT (f)
+		|| (f->new_size_p
+		    && ((f->new_width >= 0 && width != f->new_width)
+			|| (f->new_height >= 0 && height != f->new_height))))
+	      {
+		change_frame_size (f, width, height, false, true, false);
+		SET_FRAME_GARBAGED (f);
+		cancel_mouse_face (f);
+		haiku_clear_under_internal_border (f);
+	      }
+
+	    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;
+
+	    inev.code = b->unraw_mb_char;
+
+	    BMapKey (b->kc, &non_ascii_p, &inev.code);
+
+	    if (non_ascii_p)
+	      inev.kind = NON_ASCII_KEYSTROKE_EVENT;
+	    else
+	      inev.kind = inev.code > 127 ? MULTIBYTE_CHAR_KEYSTROKE_EVENT :
+		ASCII_KEYSTROKE_EVENT;
+
+	    inev.modifiers = haiku_modifiers_to_emacs (b->modifiers);
+	    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 - 1 || b->y < r.y ||
+		    b->y >= r.y + r.height - 1)
+		  {
+		    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);
+
+	    inev.modifiers = haiku_modifiers_to_emacs (b->modifiers);
+
+	    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;
+
+		struct frame *p;
+
+		if ((p = FRAME_PARENT_FRAME (f)))
+		  {
+		    void *window = FRAME_HAIKU_WINDOW (p);
+		    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;
+	    static float px = 0.0f, py = 0.0f;
+
+	    if (!f)
+	      continue;
+	    BView_get_mouse (FRAME_HAIKU_VIEW (f), &x, &y);
+
+	    inev.modifiers = haiku_modifiers_to_emacs (b->modifiers);
+
+	    inev2.modifiers = inev.modifiers;
+
+	    if (signbit (px) != signbit (b->delta_x))
+	      px = 0;
+
+	    if (signbit (py) != signbit (b->delta_y))
+	      py = 0;
+
+	    px += b->delta_x;
+	    py += b->delta_y;
+
+	    if (fabsf (py) >= FRAME_LINE_HEIGHT (f))
+	      {
+		inev.kind = WHEEL_EVENT;
+		inev.code = 0;
+
+		XSETINT (inev.x, x);
+		XSETINT (inev.y, y);
+		XSETINT (inev.arg, lrint (fabsf (py) / FRAME_LINE_HEIGHT (f)));
+		XSETFRAME (inev.frame_or_window, f);
+
+		inev.modifiers |= signbit (py) ? up_modifier : down_modifier;
+		py = 0.0f;
+	      }
+
+	    if (fabsf (px) >= FRAME_COLUMN_WIDTH (f))
+	      {
+		inev2.kind = HORIZ_WHEEL_EVENT;
+		inev2.code = 0;
+
+		XSETINT (inev2.x, x);
+		XSETINT (inev2.y, y);
+		XSETINT (inev2.arg, lrint (fabsf (px) / FRAME_COLUMN_WIDTH (f)));
+		XSETFRAME (inev2.frame_or_window, f);
+
+		inev2.modifiers |= signbit (px) ? up_modifier : down_modifier;
+		px = 0.0f;
+	      }
+
+	    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 REFS_EVENT:
+	  {
+	    struct haiku_refs_event *b = buf;
+
+	    inev.kind = HAIKU_REFS_FOUND_EVENT;
+	    inev.arg = build_string_from_utf8 (b->ref);
+
+	    /* There should be no problem with calling free here.
+	       I have had verbal assurance that free is thread-safe. */
+	    free (b->ref);
+	    break;
+	  }
+	case APP_QUIT_REQUESTED_EVENT:
+	  {
+	    inev.kind = HAIKU_QUIT_REQUESTED_EVENT;
+	    inev.arg = Qnil;
+	    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;
+
+  haiku_update_size_hints (f);
+}
+
+static struct terminal *
+haiku_create_terminal (struct haiku_display_info *dpyinfo)
+{
+  struct terminal *terminal;
+
+  terminal = create_terminal (output_haiku, &haiku_redisplay_interface);
+
+  terminal->display_info.haiku = dpyinfo;
+  dpyinfo->terminal = terminal;
+  terminal->kboard = allocate_kboard (Qhaiku);
+
+  terminal->iconify_frame_hook = haiku_iconify_frame;
+  terminal->focus_frame_hook = haiku_focus_frame;
+  terminal->ring_bell_hook = haiku_beep;
+  terminal->popup_dialog_hook = haiku_popup_dialog;
+  terminal->frame_visible_invisible_hook = haiku_set_frame_visible_invisible;
+  terminal->set_frame_offset_hook = haiku_set_offset;
+  terminal->delete_terminal_hook = haiku_delete_terminal;
+  terminal->get_string_resource_hook = 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);
+
+  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);
+      BView_ClipToRect (view, 0, 0, FRAME_PIXEL_WIDTH (f),
+			FRAME_PIXEL_HEIGHT (f));
+
+      if (face)
+	BView_SetHighColor (view, face->background);
+      else
+	BView_SetHighColor (view, FRAME_BACKGROUND_PIXEL (f));
+
+      BView_FillRectangle (view, 0, margin, width, border);
+      BView_FillRectangle (view, 0, 0, border, height);
+      BView_FillRectangle (view, 0, margin, width, border);
+      BView_FillRectangle (view, width - border, 0, border, height);
+      BView_FillRectangle (view, 0, height - 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
+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;
+    }
+
+  haiku_update_size_hints (frame);
+
+  block_input ();
+  if (change_gravity)
+    BWindow_set_offset (FRAME_HAIKU_WINDOW (frame), x, y);
+  unblock_input ();
+}
+
+#ifdef USE_BE_CAIRO
+cairo_t *
+haiku_begin_cr_clip (struct frame *f, struct glyph_string *s)
+{
+  cairo_surface_t *surface = FRAME_CR_SURFACE (f);
+  if (!surface)
+    return NULL;
+
+  cairo_t *context = cairo_create (surface);
+  return context;
+}
+
+void
+haiku_end_cr_clip (cairo_t *cr)
+{
+  cairo_destroy (cr);
+}
+#endif
+
+void
+syms_of_haikuterm (void)
+{
+  DEFVAR_BOOL ("haiku-initialized", haiku_initialized,
+     doc: /* Non-nil if the Haiku terminal backend has been initialized.  */);
+
+  DEFVAR_BOOL ("x-use-underline-position-properties",
+	       x_use_underline_position_properties,
+     doc: /* SKIP: real doc in xterm.c.  */);
+  x_use_underline_position_properties = 1;
+
+  DEFVAR_BOOL ("x-underline-at-descent-line",
+	       x_underline_at_descent_line,
+     doc: /* SKIP: real doc in xterm.c.  */);
+  x_underline_at_descent_line = 0;
+
+  DEFVAR_LISP ("x-toolkit-scroll-bars", Vx_toolkit_scroll_bars,
+     doc: /* SKIP: real doc in xterm.c.  */);
+  Vx_toolkit_scroll_bars = Qt;
+
+  DEFVAR_BOOL ("haiku-debug-on-fatal-error", haiku_debug_on_fatal_error,
+     doc: /* If non-nil, Emacs will launch the system debugger upon a fatal error.  */);
+  haiku_debug_on_fatal_error = 1;
+
+  DEFSYM (Qshift, "shift");
+  DEFSYM (Qcontrol, "control");
+  DEFSYM (Qoption, "option");
+  DEFSYM (Qcommand, "command");
+
+  DEFVAR_LISP ("haiku-meta-keysym", Vhaiku_meta_keysym,
+     doc: /* Which key Emacs uses as the meta modifier.
+This is either one of the symbols `shift', `control', `command', and
+`option', or nil, in which case it is treated as `command'.
+
+Setting it to any other value is equivalent to `command'.  */);
+  Vhaiku_meta_keysym = Qnil;
+
+  DEFVAR_LISP ("haiku-control-keysym", Vhaiku_control_keysym,
+     doc: /* Which key Emacs uses as the control modifier.
+This is either one of the symbols `shift', `control', `command', and
+`option', or nil, in which case it is treated as `control'.
+
+Setting it to any other value is equivalent to `control'.  */);
+  Vhaiku_control_keysym = Qnil;
+
+  DEFVAR_LISP ("haiku-super-keysym", Vhaiku_super_keysym,
+     doc: /* Which key Emacs uses as the super modifier.
+This is either one of the symbols `shift', `control', `command', and
+`option', or nil, in which case it is treated as `option'.
+
+Setting it to any other value is equivalent to `option'.  */);
+  Vhaiku_super_keysym = Qnil;
+
+  DEFVAR_LISP ("haiku-shift-keysym", Vhaiku_shift_keysym,
+     doc: /* Which key Emacs uses as the shift modifier.
+This is either one of the symbols `shift', `control', `command', and
+`option', or nil, in which case it is treated as `shift'.
+
+Setting it to any other value is equivalent to `shift'.  */);
+  Vhaiku_shift_keysym = Qnil;
+
+
+  DEFSYM (Qx_use_underline_position_properties,
+	  "x-use-underline-position-properties");
+
+  DEFSYM (Qx_underline_at_descent_line, "x-underline-at-descent-line");
+
+  rdb = Qnil;
+  staticpro (&rdb);
+
+  Fprovide (Qhaiku, Qnil);
+#ifdef HAVE_BE_FREETYPE
+  Fprovide (Qfreetype, Qnil);
+#endif
+#ifdef USE_BE_CAIRO
+  Fprovide (intern_c_string ("cairo"), Qnil);
+#endif
+}
diff --git a/src/haikuterm.h b/src/haikuterm.h
new file mode 100644
index 0000000000..af55f68c67
--- /dev/null
+++ b/src/haikuterm.h
@@ -0,0 +1,293 @@
+/* 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>
+
+#ifdef USE_BE_CAIRO
+#include <cairo.h>
+#endif
+
+#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;
+  struct font_metrics **metrics;
+  short metrics_nrows;
+
+  unsigned short **glyphs;
+};
+
+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;
+
+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)
+
+#ifdef USE_BE_CAIRO
+#define FRAME_CR_SURFACE(f) \
+  (FRAME_HAIKU_VIEW (f) ? EmacsView_cairo_surface (FRAME_HAIKU_VIEW (f)) : 0);
+#endif
+
+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_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
+
+#ifdef USE_BE_CAIRO
+extern cairo_t *
+haiku_begin_cr_clip (struct frame *f, struct glyph_string *s);
+
+extern void
+haiku_end_cr_clip (cairo_t *cr);
+#endif
+#endif /* _HAIKU_TERM_H_ */
diff --git a/src/image.c b/src/image.c
index 6769e49120..d2c2b55b29 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 && depth != 1)
+    {
+      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;
@@ -4036,7 +4131,7 @@ #define Display xpm_Display
 #endif /* not HAVE_NTGUI */
 #endif /* HAVE_XPM */
 
-#if defined HAVE_XPM || defined USE_CAIRO || defined HAVE_NS
+#if defined HAVE_XPM || defined USE_CAIRO || defined HAVE_NS || defined HAVE_HAIKU
 
 /* Indices of image specification fields in xpm_format, below.  */
 
@@ -4056,7 +4151,7 @@ #define Display xpm_Display
   XPM_LAST
 };
 
-#if defined HAVE_XPM || defined HAVE_NS
+#if defined HAVE_XPM || defined HAVE_NS || defined HAVE_HAIKU
 /* Vector of image_keyword structures describing the format
    of valid XPM image specifications.  */
 
@@ -4074,7 +4169,7 @@ #define Display xpm_Display
   {":color-symbols",	IMAGE_DONT_CHECK_VALUE_TYPE,		0},
   {":background",	IMAGE_STRING_OR_NIL_VALUE,		0}
 };
-#endif	/* HAVE_XPM || HAVE_NS */
+#endif	/* HAVE_XPM || HAVE_NS || HAVE_HAIKU */
 
 #if defined HAVE_X_WINDOWS && !defined USE_CAIRO
 
@@ -4298,7 +4393,7 @@ init_xpm_functions (void)
 
 #endif /* WINDOWSNT */
 
-#if defined HAVE_XPM || defined HAVE_NS
+#if defined HAVE_XPM || defined HAVE_NS || defined HAVE_HAIKU
 /* Value is true if COLOR_SYMBOLS is a valid color symbols list
    for XPM images.  Such a list must consist of conses whose car and
    cdr are strings.  */
@@ -4334,9 +4429,9 @@ xpm_image_p (Lisp_Object object)
 	  && (! fmt[XPM_COLOR_SYMBOLS].count
 	      || xpm_valid_color_symbols_p (fmt[XPM_COLOR_SYMBOLS].value)));
 }
-#endif	/* HAVE_XPM || HAVE_NS */
+#endif	/* HAVE_XPM || HAVE_NS || HAVE_HAIKU */
 
-#endif /* HAVE_XPM || USE_CAIRO || HAVE_NS */
+#endif /* HAVE_XPM || USE_CAIRO || HAVE_NS || HAVE_HAIKU */
 
 #if defined HAVE_XPM && defined HAVE_X_WINDOWS && !defined USE_GTK
 ptrdiff_t
@@ -4705,9 +4800,10 @@ xpm_load (struct frame *f, struct image *img)
 #endif /* HAVE_XPM && !USE_CAIRO */
 
 #if (defined USE_CAIRO && defined HAVE_XPM)	\
-  || (defined HAVE_NS && !defined HAVE_XPM)
+  || (defined HAVE_NS && !defined HAVE_XPM)	\
+  || (defined HAVE_HAIKU && !defined HAVE_XPM)
 
-/* XPM support functions for NS where libxpm is not available, and for
+/* XPM support functions for NS and Haiku where libxpm is not available, and for
    Cairo.  Only XPM version 3 (without any extensions) is supported.  */
 
 static void xpm_put_color_table_v (Lisp_Object, const char *,
@@ -5444,7 +5540,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,
@@ -5517,7 +5613,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);
@@ -5525,7 +5621,7 @@ image_to_emacs_colors (struct frame *f, struct image *img, bool rgb_p)
         {
           FRAME_TERMINAL (f)->query_colors (f, row, img->width);
         }
-#else  /* USE_CAIRO || HAVE_NS */
+#else  /* USE_CAIRO || HAVE_NS || HAVE_HAIKU */
       for (x = 0; x < img->width; ++x, ++p)
 	{
 	  p->pixel = GET_PIXEL (ximg, x, y);
@@ -5839,6 +5935,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)
@@ -5856,6 +5953,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;
@@ -6413,6 +6511,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
@@ -6486,6 +6586,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
@@ -9635,7 +9738,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
@@ -10925,7 +11029,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;
@@ -11015,7 +11120,7 @@ initialize_image_type (struct image_type const *type)
  { SYMBOL_INDEX (Qjpeg), jpeg_image_p, jpeg_load, image_clear_image,
    IMAGE_TYPE_INIT (init_jpeg_functions) },
 #endif
-#if defined HAVE_XPM || defined HAVE_NS
+#if defined HAVE_XPM || defined HAVE_NS || defined HAVE_HAIKU
  { SYMBOL_INDEX (Qxpm), xpm_image_p, xpm_load, image_clear_image,
    IMAGE_TYPE_INIT (init_xpm_functions) },
 #endif
@@ -11163,7 +11268,7 @@ syms_of_image (void)
   DEFSYM (Qxbm, "xbm");
   add_image_type (Qxbm);
 
-#if defined (HAVE_XPM) || defined (HAVE_NS)
+#if defined (HAVE_XPM) || defined (HAVE_NS) || defined (HAVE_HAIKU)
   DEFSYM (Qxpm, "xpm");
   add_image_type (Qxpm);
 #endif
diff --git a/src/keyboard.c b/src/keyboard.c
index de9805df32..083cc52f9a 100644
--- a/src/keyboard.c
+++ b/src/keyboard.c
@@ -3865,7 +3865,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)
@@ -3994,6 +3994,10 @@ kbd_buffer_get_event (KBOARD **kbp,
 #ifdef HAVE_XWIDGETS
       case XWIDGET_EVENT:
       case XWIDGET_DISPLAY_EVENT:
+#endif
+#ifdef HAVE_HAIKU
+      case HAIKU_REFS_FOUND_EVENT:
+      case HAIKU_QUIT_REQUESTED_EVENT:
 #endif
       case SAVE_SESSION_EVENT:
       case NO_EVENT:
@@ -6152,7 +6156,12 @@ make_lispy_event (struct input_event *event)
     case CONFIG_CHANGED_EVENT:
 	return list3 (Qconfig_changed_event,
 		      event->arg, event->frame_or_window);
-
+#ifdef HAVE_HAIKU
+    case HAIKU_REFS_FOUND_EVENT:
+      return list2 (Qhaiku_refs_found, event->arg);
+    case HAIKU_QUIT_REQUESTED_EVENT:
+      return list1 (Qhaiku_quit_requested);
+#endif
       /* The 'kind' field of the event is something we don't recognize.  */
     default:
       emacs_abort ();
@@ -7180,7 +7189,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 ();
@@ -7243,7 +7252,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)
@@ -7259,7 +7268,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)  */
 
 
 /* User signal events.  */
@@ -7329,7 +7338,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
@@ -11099,7 +11108,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)
     {
@@ -11110,9 +11119,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)
     {
@@ -11541,12 +11550,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
 
@@ -11737,6 +11750,11 @@ syms_of_keyboard (void)
   DEFSYM (Qfile_notify, "file-notify");
 #endif /* USE_FILE_NOTIFY */
 
+#ifdef HAVE_HAIKU
+  DEFSYM (Qhaiku_refs_found, "haiku-refs-found");
+  DEFSYM (Qhaiku_quit_requested, "haiku-quit-requested");
+#endif
+
   /* Menu and tool bar item parts.  */
   DEFSYM (QCenable, ":enable");
   DEFSYM (QCvisible, ":visible");
diff --git a/src/lisp.h b/src/lisp.h
index 31656bb3b1..19caba4001 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
@@ -3330,7 +3338,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
 
@@ -4429,7 +4437,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..aee29f86af 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,10 @@ update_submenu_strings (widget_value *first_wv)
     }
 }
 
+#endif /* USE_X_TOOLKIT || USE_GTK || HAVE_NS || HAVE_NTGUI */
+#if defined (USE_X_TOOLKIT) || defined (USE_GTK) || defined (HAVE_NS) \
+  || defined (HAVE_NTGUI) || defined (HAVE_HAIKU)
+
 /* 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,7 +965,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 /* USE_X_TOOLKIT || USE_GTK || HAVE_NS || HAVE_NTGUI || HAVE_HAIKU */
 
 #ifdef HAVE_NS
 /* As above, but return the menu selection instead of storing in kb buffer.
diff --git a/src/process.c b/src/process.c
index f923aff1cb..d0d604b05a 100644
--- a/src/process.c
+++ b/src/process.c
@@ -259,7 +259,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);
@@ -5721,7 +5721,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,
@@ -5729,7 +5729,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,
@@ -7723,7 +7727,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..bb238cb6d8 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.kernel_time;
+	everything += inf.user_time + inf.kernel_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.kernel_time;
+	vsample += inf.user_time + inf.kernel_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 e7539bbce2..87d57931a1 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.  */
@@ -264,6 +265,15 @@ #define EMACS_TERMHOOKS_H
   , FILE_NOTIFY_EVENT
 #endif
 
+#ifdef HAVE_HAIKU
+  /* The system has asked us to open a file.  ARG should be a string
+     denoting the path of the file to open.  */
+  , HAIKU_REFS_FOUND_EVENT
+
+  /* The system has requested that Emacs close, prompting the user for
+     confirmation if there is unsaved data.  */
+  , HAIKU_QUIT_REQUESTED_EVENT
+#endif
 };
 
 /* Bit width of an enum event_kind tag at the start of structs and unions.  */
@@ -444,6 +454,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;
 
 
@@ -832,6 +843,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 a5ff931ed0..9252971acc 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_ELN =
 AM_V_GEN =
@@ -34,7 +36,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 d7ad548917..b62332cc48 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -15657,6 +15657,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;
@@ -25247,6 +25252,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);
@@ -33698,6 +33708,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 a8cbf34790..04934ae552 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 785ae3baca..68b6104757 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
diff --git a/src/xterm.c b/src/xterm.c
index 172abe919d..bd7502ada0 100644
--- a/src/xterm.c
+++ b/src/xterm.c
@@ -13842,7 +13842,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");